diff options
author | theuni <theuni-nospam-@xbmc.org> | 2011-01-24 16:05:21 -0500 |
---|---|---|
committer | theuni <theuni-nospam-@xbmc.org> | 2011-01-24 16:05:21 -0500 |
commit | c51b1189e3d5353e842991f5859ddcea0f73e426 (patch) | |
tree | ef2cb8a6184699aa614f3655dca4ce661cdc108e /lib/snesapu | |
parent | be61ebdc9e897fe40c6f371111724de79ddee8d5 (diff) |
Merged cptspiff's code-reshuffle branch.
Squashed commit due to build breakage during code-reshuffle history.
Conflicts:
xbmc/Util.cpp
xbmc/cdrip/CDDARipper.cpp
xbmc/filesystem/Directory.cpp
xbmc/filesystem/File.cpp
Diffstat (limited to 'lib/snesapu')
22 files changed, 18759 insertions, 0 deletions
diff --git a/lib/snesapu/A2Misc.h b/lib/snesapu/A2Misc.h new file mode 100644 index 0000000000..35086ee201 --- /dev/null +++ b/lib/snesapu/A2Misc.h @@ -0,0 +1,173 @@ +/*************************************************************************************************** +* Program: Miscellaneous Classes * +* Programmer: Anti Resonance * +* * +* This library is free software; you can redistribute it and/or modify it under the terms of the * +* GNU Lesser General Public License as published by the Free Software Foundation; either version * +* 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * +* See the GNU Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License along with this * +* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * +* Boston, MA 02111-1307 USA * +* * +* Copyright (C)2006 Alpha-II Productions * +***************************************************************************************************/ + +#include "Types.h" + +namespace A2 +{ + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Set Class Template + // + // Used to maintain a set of integral values from 0 to 31. For a larger range or for sets + // containing negative values, use SetL. Behaviour is undefined for Sets of a non-integral + // type. No range checking is performed. + + template<typename T> + class Set + { + u32 bits; + + public: + inline Set<T>() {bits = 0;} + inline Set<T>(const Set<T>& s) {bits = s.bits;} + inline Set<T>(const T i) {bits = 1 << i;} + Set<T>(u32 num, const T* items) + { bits = 0; while (num) bits |= 1 << items[--num]; } + + inline void Clear() {bits = 0;} + + inline bool HasItems() const {return bits != 0;} + u32 NumItems() const + { u32 i=0,j=bits; while (j) {i += (j & 1); j >>= 1;} return i; } + + inline Set<T>& operator = (const Set<T> s) {bits = s; return *this;} + inline Set<T>& operator << (const Set<T> s) {bits |= s.bits; return *this;} + inline Set<T>& operator >> (const Set<T> s) {bits &= ~s.bits; return *this;} + inline Set<T> operator ~ () const {Set<T> t; t.bits = ~bits; return t;} + + inline Set<T>& operator = (const T i) {bits = 1 << i; return *this;} + inline Set<T>& operator << (const T i) {bits |= 1 << i; return *this;} + inline Set<T>& operator >> (const T i) {bits &= ~(1 << i); return *this;} + inline bool operator [] (const T i) const {return (bits & (1 << i)) != 0;} + + inline bool operator && (const Set<T> s) const {return (bits & s.bits) == s.bits;} + inline bool operator || (const Set<T> s) const {return (bits & s.bits) != 0;} + }; + + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Large Set Class Template + // + // Like a Set, but can contain much larger ranges (-2^31 to 2^31-1). Range checking _is_ + // performed on the input. + +#ifndef _MSC_VER //MS VC++ 6 doesn't like this class + template<typename T, T start, T end> + class SetL + { + private: + u8 bits[((end - start) + 8) >> 3]; + + public: + inline SetL<T,start,end>() {Clear();} + + inline SetL<T,start,end>(const SetL<T,start,end>& s) + { operator = (s); } + + SetL<T,start,end>(u32 num, const s32* items) + { Clear(); while (num) operator << (items[--num]); } + + + void Clear() + { for (u32 i=((end - start) + 7) >> 3; i;) bits[--i] = 0; } + + + inline SetL<T,start,end>& operator << (const s32 i) + { if (i >= start && i <= end) + { const s32 b = i - start; bits[b >> 3] |= 1 << (b & 7); } + return *this; } + + inline SetL<T,start,end>& operator >> (const s32 i) + { if (i >= start && i <= end) + { const s32 b = i - start; bits[b >> 3] &= ~(1 << (b & 7)); } + return *this; } + + inline bool operator [] (const s32 i) const + { if (i >= start && i <= end) + { const s32 b = i - start; + return (bits[b >> 3] & (1 << (b & 7))) != 0; } + return false; } + }; +#endif + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Color Class + + class Color + { + union + { + struct {u8 r,g,b,a;}; + u32 rgba; + }; + + public: + inline Color& operator = (const Color& c) + {this->rgba = c.rgba; return *this;} + + inline Color& operator = (const u32 rgba) + {this->rgba = rgba; return *this;} + + inline void operator () (const u8 r, const u8 g, const u8 b, const u8 a = 255) + {this->r = r; this->g = g; this->b = b; this->a = a;} + +// inline void operator () (const f32 r, const f32 g, const f32 b, const f32 a = 1.0f) +// { this->r = (u8)(r * 255.0f); this->g = (u8)(g * 255.0f); +// this->b = (u8)(b * 255.0f); this->a = (u8)(a * 255.0f); } + + inline Color() {} + inline Color(const Color& c) {operator = (c.rgba);} + inline Color(const u32 rgba) {operator = (rgba);} + inline Color(const u8 r, const u8 g, const u8 b, const u8 a = 255) + {operator () (r, g, b, a);} +// inline Color(const f32 r, const f32 g, const f32 b, const f32 a = 1.0f) +// {operator () (r, g, b, a);} + + inline void Red(const u8 val) {r = val;} + inline void Green(const u8 val) {g = val;} + inline void Blue(const u8 val) {b = val;} + inline void Alpha(const u8 val) {a = val;} + + inline u8 Red() const {return r;} + inline u8 Green() const {return g;} + inline u8 Blue() const {return b;} + inline u8 Alpha() const {return a;} + + inline operator u32 () const {return rgba;} + +#if defined __BORLANDC__ + inline operator TColor () const + {return static_cast<TColor>(rgba & 0xFFFFFF);} +#endif + + inline Color operator >> (const u8 sa) const + {return Color((u8)(r >> sa), (u8)(g >> sa), (u8)(b >> sa), (u8)(a >> sa));} + + inline void Avg(const Color c1, const Color c2) + { r = (u8)(((u32)c1.r + (u32)c2.r) >> 1); + g = (u8)(((u32)c1.g + (u32)c2.g) >> 1); + b = (u8)(((u32)c1.b + (u32)c2.b) >> 1); + a = (u8)(((u32)c1.a + (u32)c2.a) >> 1);} + + }; + +} //namespace A2 diff --git a/lib/snesapu/Macro.Inc b/lib/snesapu/Macro.Inc new file mode 100644 index 0000000000..ffebf43ecc --- /dev/null +++ b/lib/snesapu/Macro.Inc @@ -0,0 +1,373 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Description: TASM Like Macros +;Platform: NASM +;Programmer: Anti Resonance +; +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Revision History: +; +; 2004.09.14 +; - Changed parameter %defines to context local (makes the code look uglier, but keeps parameter +; names from getting a global scope) +; +; 2004.08.07 +; + Replaced RetZ and RetNZ with Retc +; + Updated PUBLIC, PROC, and LOCALS so the number of parameters is unlimited +; + USES now pushes EBP when procedure has no stack frame +; + Simplified ENDP by using a loop to pop used registers +; +; Copyright (C)2003-06 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +;The following defines affect the way some macros work: +; INTERNAL effects the way PUBLIC works (see PUBLIC header for more info) +; STDCALL mangles labels and cleans up the stack on returns +; CDECL mangles labels + +%define ST ST0 + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Overloaded Instructions + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Push +; +;Params: +; List of values to be pushed onto the stack. Values are pushed left to right. + +%macro Push 2-*.nolist + %rep %0 + Push %1 + %rotate 1 + %endrep +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Pop +; +;Params: +; List of values to be popped off the stack. Values are popped left to right. + +%macro Pop 2-*.nolist + %rep %0 + Pop %1 + %rotate 1 + %endrep +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Call Procedure +; +;If STDCALL is defined, the procedure is assumed to clean up the stack. +; +;Params: +; Function name followed by stack parameters. All arguments are assumed to be dwords. + +%macro Call 2-*.nolist + %rep %0-1 + %rotate -1 + Push dword %1 + %endrep + %rotate -1 + + Call %1 + + %ifndef STDCALL + Add ESP,(%0*4)-4 + %endif +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Return from Procedure +; +;Ensures the stack frame will be cleaned up and any used registers restored +; +;Params (optional): +; Value to return in EAX + +%macro RetS 0-1.nolist ;Short return + %if %0 + Mov EAX,%1 + %endif + + %ifdef %$_params + Jmp short %$Done + %elifdef %$_uses + Jmp short %$Done + %else + Ret + %endif +%endmacro + +%macro RetN 0-1.nolist ;Near return + %if %0 + Mov EAX,%1 + %endif + + %ifdef %$_params + Jmp %$Done + %elifdef %$_uses + Jmp %$Done + %else + Ret + %endif +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Conditional Return from Procedure +; +;Ensures the stack frame will be cleaned up and any used registers restored +; +;Params: +; Condition code +; Value to return in EAX (optional) + +%macro Retc 1-2.nolist + %if %0==2 + Mov EAX,%2 + %endif + J%+1 short %$Done +%endmacro + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Procedure Macros + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Publicly Declare a Label +; +;If INTERNAL is defined, the label is declared as GLOBAL, otherwise EXTERN. +;If STDCALL is defined, the label has an underscore prepended and an 'at' with the size of the +; argument stack appended. +;If CDECL is defined, the label has an underscore prepended. +; +;Params: +; The variable or procedure name. Procedures must be followed by a list of arguments. If a +; procedure has no arguments, specify 'NULL'. + +%macro PUBLIC 1-*.nolist + %ifdef STDCALL + %push PUBLIC + + %if %0==1 + %xdefine ARGSIZE + %else + %ifidni %2,NULL + %xdefine ARGSIZE @0 + %else + %assign %$size (%0*4)-4 + %xdefine ARGSIZE @%$size + %endif + %endif + + %ifdef INTERNAL + GLOBAL _%1ARGSIZE + %else + EXTERN _%1ARGSIZE + %endif + + %xdefine %1 _%1ARGSIZE + + %pop + %elifdef CDECL + %ifdef INTERNAL + GLOBAL _%1 + %else + EXTERN _%1 + %endif + + %xdefine %1 _%1 + %else + %ifdef INTERNAL + GLOBAL %1 + %else + EXTERN %1 + %endif + %endif +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Define Procedure +; +;Used to begin a procedure definition (body). Defines a label, creates the initial stack frame, and +;generates offsets for any arguments. Arguments are assumed to be 32-bit and passed right to left. +; +;Params: +; Procedure name followed by a list of arguments passed on the stack + +%macro PROC 1-*.nolist + ALIGN 16 ;Align entry point on a cache line boundary + %1: ;Define procedure label + + %push PROC ;Save context + + %if %0>1 ;Does this procedure have any arguments? + Push EBP ;Create a frame pointer + Mov EBP,ESP + + %assign %$_params (%0*4)-4 ;Save the size of the frame + + %assign %$_offset 8 ;Create pointers to each argument + %rep %0-1 + %rotate 1 + %xdefine %$%1 EBP+%$_offset + %assign %$_offset %$_offset+4 + %endrep + %endif +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Define Local Variables +; +;Defines a list of local variables to be temporarily allocated on the stack +; +;By default, each variable name is allocated four bytes on the stack. Specifying a number in the +;list will allocate that much memory for the next variable. For example: +; +; LOCALS float,8,double - Allocate four bytes for %$float and eight bytes for %$double +; +;Params: +; Variable names + +%macro LOCALS 1-*.nolist + %ifndef %$_params ;If a stack frame hasn't been created, create one + %assign %$_params 0 ;No arguments + Push EBP + Mov EBP,ESP + %endif + + %assign %$_offset 0 ;Create pointers to each of the locals + %rep %0 + %ifnum %1 + %assign %$_offset %$_offset+%1-4 + %else + %assign %$_offset %$_offset+4 + %xdefine %$%1 EBP-%$_offset + %endif + %rotate 1 + %endrep + + %assign %$_locals %$_offset ;Save the size of the local variables + Add ESP,-%$_locals ;Allocate space on the stack +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Registers Used by Procedure +; +;Pushes registers onto the stack, then restores them at the end of the procedure. EAX is assumed +;to contain the return value. +; +;ALL saves all registers, except ESP and, if there's no stack frame, EBP. +; +;Params: +; Registers used by the procedure (EBX, ECX, EDX, ESI, EDI, EBP, or ALL to save all.) + +%macro USES 1-5.nolist + %ifidni %1,ALL + Push ECX,EDX,EBX,ESI,EDI + %assign %$_uses 5 + %assign %$_ur0 1 + %assign %$_ur1 2 + %assign %$_ur2 3 + %assign %$_ur3 6 + %assign %$_ur4 7 + %ifndef %$paramsize + Push EBP + %assign %$_uses 6 + %assign %$_ur5 5 + %endif + %else + %assign %$_uses 0 + %rep %0 + %ifidni %1,ECX ;Does parameter == "ECX"? + %assign %$_ur%$_uses 1 ;Define "$_ur?" as the register number + Push ECX ;Push ECX + %elifidni %1,EDX + %assign %$_ur%$_uses 2 + Push EDX + %elifidni %1,EBX + %assign %$_ur%$_uses 3 + Push EBX + %elifidni %1,EBP + %assign %$_ur%$_uses 5 + Push EBP + %elifidni %1,ESI + %assign %$_ur%$_uses 6 + Push ESI + %elifidni %1,EDI + %assign %$_ur%$_uses 7 + Push EDI + %else + %error "USES only accepts EBX, ECX, EDX, ESI, EDI, EBP, or ALL" + %endif + + %rotate 1 + %assign %$_uses %$_uses+1 + %endrep + %endif + +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;End Procedure Definition +; +;Ends a procedure definition. Restores any used registers, cleans up the stack frame, and pops the +;context stack. Also pops any arguments off the stack if STDCALL is defined. + +%macro ENDP 0-*.nolist + %$Done: + + %ifdef %$_uses ;Pop used registers off the stack + %rep %$_uses + %assign %$_uses %$_uses-1 + DB 58h+%$_ur%$_uses ;Pop register off the stack + %endrep + %endif + + %ifdef %$_params ;Clean up stack frame + Leave + + %if %$_params > 0 + %ifdef STDCALL + Ret %$_params + %else + Ret + %endif + %else + Ret + %endif + %else + Ret + %endif + + %pop +%endmacro + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Data Macros + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Creating Floating Point Powers of 2 + +%macro Scale32 2.nolist ;Single + %1 DD (127+(%2)) << 23 +%endmacro + +%macro Scale64 2.nolist ;Double + %1 DD 0,(1023+(%2)) << 20 +%endmacro + +%macro Scale80 2.nolist ;Extended + %1 DD 0,80000000h + DW 16383+(%2) +%endmacro diff --git a/lib/snesapu/SNES/SNESAPU/APU.Asm b/lib/snesapu/SNES/SNESAPU/APU.Asm new file mode 100644 index 0000000000..e7fad1f16f --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/APU.Asm @@ -0,0 +1,423 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Super Nintendo Entertainment System(tm) Audio Processing Unit Emulator +; Copyright (C)2003-06 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +CPU 386 +BITS 32 + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Header files + +%include "../../Macro.Inc" +%include "SPC700.Inc" +%include "DSP.Inc" +%define INTERNAL +%include "APU.Inc" + +%if DSPINTEG +EXTERN SetEmuDSP ;DSP.Asm - Used to set EmuDSP parameters +%endif + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Data and Variables + +SECTION .bss ALIGN=4 + + cycLeft resd 1 ;Clock cycles left to emulate in EmuAPU loop + smpDec resd 1 ;Unused clocks from cycle to sample conversion + smpRate resd 1 ;Output sample rate (used by sound card and DSP) + smpRAdj resd 1 ;Sample rate adjustment (16.16) + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Code + +SECTION .text ALIGN=16 + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Initialize Audio Processing Unit + +PROC InitAPU + + Call InitSPC + Call InitDSP + + Mov dword [smpRate],32000 + Mov dword [smpRAdj],10000h + + Call SetAPUSmpClk,[smpRAdj] + + Mov EAX, \ + CPU_CYC | \ + (DEBUG << 8) | (DSPINTEG << 9) | \ + (HALFC << 16) | (CNTBK << 17) | (SPEED << 18) | (IPLW << 19) | (DSPBK << 20) | (PROFILE << 21) | \ + (MMETER << 24) | (VMETER << 25) | (STEREO << 26) + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Reset Audio Processor + +PROC ResetAPU + + Call ResetSPC + Call ResetDSP + + And dword [cycLeft],0 + And dword [smpDec],0 + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Load SPC File + +PROC LoadSPCFile, pFile +USES ALL + + Call ResetAPU + + Mov ESI,[%$pFile] + + Add ESI,100h ;memcpy(pAPURAM, &spc[0x100], 0x10000) + Mov EDI,[pAPURAM] + Mov ECX,10000h / 4 + Rep MovSD + + Mov EDI,dsp ;memcpy(&dsp, &spc[0x10100], 128) + Or ECX,128 / 4 + Rep MovSD + + Add ESI,40h ;memcpy(xram, &spc[0x101C0], 64) + Mov EDI,[pAPURAM] + Add EDI,-80h + Or ECX,40h / 4 + Rep MovSD + + Mov ESI,[%$pFile] + MovZX EAX,byte [2Bh+ESI] ;SP + MovZX ECX,byte [2Ah+ESI] ;PSW + MovZX EDX,byte [28h+ESI] ;X + MovZX EBX,byte [29h+ESI] ;Y + MovZX EDI,byte [27h+ESI] ;A + MovZX ESI,word [25h+ESI] ;PC + Call FixSPCLoad,ESI,EDI,EBX,EDX,ECX,EAX + Call FixDSPLoad + + Call SetDSPAAR,-1,-1,-1,-1 + +ENDP + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Save Audio Processor State + +PROC SaveAPU, pSPC, pDSP + + Mov EAX,[%$pSPC] + Test EAX,EAX + JZ short .NoSPC + Call SaveSPC,EAX + .NoSPC: + + Mov EAX,[%$pDSP] + Test EAX,EAX + JZ short .NoDSP + Call SaveDSP,EAX + .NoDSP: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Restore Audio Processor State + +PROC RestoreAPU, pSPC, pDSP + + Mov EAX,[%$pSPC] + Test EAX,EAX + JZ short .NoSPC + Call RestoreSPC,EAX + .NoSPC: + + Mov EAX,[%$pDSP] + Test EAX,EAX + JZ short .NoDSP + Call RestoreDSP,EAX + .NoDSP: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Audio Processor Options + +PROC SetAPUOpt, mixtype, chn, bits, rate, inter, opts + + Mov EAX,[%$rate] ;Is a rate specified? + Cmp EAX,-1 ;if (rate!=-1) + JE short .KeepRate + + Push ECX,EDX + Mov EAX,[%$rate] + + Mov ECX,8000 ;if (rate < 8000) rate = 8000; + Sub EAX,ECX + CDQ + Not EDX + And EAX,EDX + Add EAX,ECX + + Mov ECX,192000 ;if (rate > 192000) rate = 192000; + Sub EAX,ECX + CDQ + And EAX,EDX + Add EAX,ECX + + Mov [smpRate],EAX + Pop EDX,ECX + + .KeepRate: + + Pop EBP + Jmp SetDSPOpt ;Set options in DSP emulator + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Audio Processor Sample Clock + +PROC SetAPUSmpClk, speed +USES EDX,ECX + + Mov EAX,[%$speed] + + Mov ECX,1000h ;if (speed < 4096) speed = 4096; + Sub EAX,ECX + CDQ + Not EDX + And EAX,EDX + Add EAX,ECX + + Mov ECX,100000h ;if (speed > 1048576) speed = 1048576; + Sub EAX,ECX + CDQ + And EAX,EDX + Add EAX,ECX + + Mov [smpRAdj],EAX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate Audio Processing Unit + +PROC EmuAPU, pBuf, cyc, smp +LOCALS rate,fcw +USES ECX,EDX,EBX,EDI + +%if PROFILE + Call StartAPUProfile,Profile.apuTSC +%endif + + ;Switch the FPU into single precision mode + ;According to the Intel docs, this is faster since the floating-point mixing routine only + ; requires single precision accuracy. However, on my Pentium 4 the greatest performance + ; increase I've seen is 4 percent. + + FStCW [%$fcw] + FStCW [2+%$fcw] + And word [%$fcw],~0300h ;Switch to single precision operation + FLdCW [%$fcw] + + Mov EAX,[smpRate] ;rate = (smpRate << 16) / smpRAdj + Mov EDX,EAX + ShL EAX,16 + ShR EDX,16 + Div dword [smpRAdj] + Mov EBX,EAX + + Mov ECX,APU_CLK + +%if DSPINTEG + ;Fixup samples/cycles -------------------- + Cmp dword [%$smp],0 + JZ short .NoSamples + + Call SetEmuDSP,[%$pBuf],[%$smp],EBX + + Mov EAX,[%$cyc] + Test EAX,EAX + JNZ short .Cycles + + Mov EAX,[%$smp] ;Calculate number of cycles based on samples + Mul ECX ;cycles = (APU_CLK * len) / smpREmu + Div EBX + + .Cycles: + Add [cycLeft],EAX + JLE short .NoCycles + Jmp short .Emulate + + .NoSamples: ;Calculate number of samples based on cycles + Mov EAX,[%$cyc] + Add [cycLeft],EAX + Retc LE + + Mov EAX,[cycLeft] + Mul EBX ;samples = (smpREmu * len) / APU_CLK + Div ECX + Call SetEmuDSP,[%$pBuf],EAX,EBX + + .Emulate: + + ;Emulate APU ----------------------------- + Call EmuSPC,[cycLeft] + Mov [cycLeft],EAX + + .NoCycles: + Call SetEmuDSP,0,0,0 ;Create any remaining samples + +%else + + ;If samples were passed, convert to clock cycles + Mov EAX,[%$cyc] + Test EAX,EAX + JNZ short .HaveCycles + Mov EAX,[%$smp] + Mul ECX ;cycles = (APU_CLK * len) / smpREmu + Div EBX + .HaveCycles: + Add [cycLeft],EAX + JLE short .HaveSamples + + Cmp dword [%$smp],0 + JNZ short .HaveSamples + Mov EAX,[cycLeft] + Mul EBX + Div ECX + Mov [%$smp],EAX + .HaveSamples: + + ;Emulate APU ----------------------------- + Mov [%$rate],EBX + XOr EBX,EBX ;Number of samples generated + Mov EDI,[%$pBuf] + .Next: + Mov EAX,[cycLeft] + Test EAX,EAX + JLE short .Done + + ;SPC700 ------------------------------- + Mov EDX,EAX + Call EmuSPC,EAX + Mov [cycLeft],EAX + + ;DSP ---------------------------------- + Sub EDX,EAX ;Calculate number of samples to create + Mov EAX,EDX ;EAX = number of cycles emulated + Mul dword [%$rate] + Add EAX,[smpDec] + AdC EDX,0 + Div ECX ;samples = (((cycles - cycLeft) * smpREmu) + smpDec) / APU_CLK + Mov [smpDec],EDX + + Add EBX,EAX ;size += samples + Cmp EBX,[%$smp] ;Sometimes sample count will go over by one + JBE short .LenOK + Add EAX,[%$smp] + Sub EAX,EBX + Mov EBX,[%$smp] + .LenOK: + + Call EmuDSP,EDI,EAX ;pBuf = EmuDSP(pBuf,samples) + Mov EDI,EAX + + Jmp short .Next + + .Done: + + ;Make sure enough samples were created to fill buffer + Sub EBX,[%$smp] + Retc Z,EDI + + Neg EBX + Call EmuDSP,EDI,EBX +%endif + + FLdCW [2+%$fcw] ;Restore FPU precision to previous degree + +%if PROFILE + Call EndAPUProfile,Profile.apuTSC +%endif + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Seek to Position + +PROC SeekAPU, time, fast +USES ECX,EDX + + XOr EDX,EDX + Mov EAX,[%$time] ;numSeconds = time / 64000 + Test EAX,EAX + Retc Z + Mov ECX,64000 + Div ECX + Mov ECX,EAX + IMul EDX,APU_CLK/64000 ;EDX = (time % 64000) * (APU_CLK / 64000) + + Test byte [%$fast],-1 ;Fast mode completely bypasses the DSP emulation + JZ short .Slow + + Add EDX,[cycLeft] + Mov EAX,EDX + .EmuSPC: + + Add EAX,APU_CLK ;Emulate SPC for 1 second + .Next: + Call EmuSPC,EAX + Test EAX,EAX + JG short .Next + + Dec ECX + JG short .EmuSPC + Mov [cycLeft],EAX + + Jmp short .Done + + .Slow: + Test EDX,EDX + JZ short .EmuAPU + + Call EmuAPU,0,EDX,0 + Test ECX,ECX + JZ short .Done + + .EmuAPU: + Call EmuAPU,0,APU_CLK,0 + Dec ECX + JNZ short .EmuAPU + + .Done: + + Call FixDSPSeek,[%$fast] ;Fixup DSP after seeking + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Shutdown Audio Processing Unit + +PROC ShutAPU + +ENDP
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/APU.Inc b/lib/snesapu/SNES/SNESAPU/APU.Inc new file mode 100644 index 0000000000..f33588721d --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/APU.Inc @@ -0,0 +1,249 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Program: Super Nintendo Entertainment System(tm) Audio Processing Unit Emulator +;Platform: Intel 80386 & MMX +;Programmer: Anti Resonance +; +;"SNES" and "Super Nintendo Entertainment System" are trademarks of Nintendo Co., Limited and its +;subsidiary companies. +; +;This library is free software; you can redistribute it and/or modify it under the terms of the +;GNU Lesser General Public License as published by the Free Software Foundation; either version +;2.1 of the License, or (at your option) any later version. +; +;This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +;without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +;See the GNU Lesser General Public License for more details. +; +;You should have received a copy of the GNU Lesser General Public License along with this +;library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +;Boston, MA 02111-1307 USA +; +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +; Revision History: +; +; 2005.10.28 SNESAPU 3.0 +; + Added NULL pointer support to SaveAPU and RestoreAPU +; + Moved variables into the .bss +; + The build options are now returned by InitAPU instead of being stored in a global variable +; - Calculate the emulated sample rate at the top of EmuAPU instead ofin SetAPUSmpClk +; +; 2003.11.04 SNESAPU 2.0 +; + Updated EmuAPU to use SetEmuDSP when DSPINTEG is enabled +; + Move fade out code into DSP.Asm +; + Removed amp setting from ResetAPU +; +; 2003.07.12 SNESAPU 1.0a +; - Fixed the call to SetDSPVol in SetFade that wasn't restoring the stack +; +; 2003.06.20 +; + Added LoadSPCFile +; + Added ability to pass -1 to ResetAPU to keep current amp level +; +; 2003.02.10 +; + Rewrote in assembly +; + Removed GetAPUData and memory allocation (they were specific to SNESAPU.DLL) +; - Sometimes EmuAPU would generate an extra sample +; +; 2002.06.20 +; - Call SetFade after seeking +; +; 2001.04.12 +; + Updated SeekAPU to incorporate new features in SPC700 and DSP emus +; +; 2000.12.30 SNESAmp v2.0 +; + Added SetAPULength, SeekAPU, SaveAPU, RestoreAPU, HaltAPU +; + General optimizations +; - Simplified interface a bit +; +; 2000.04.04 SNESAmp v1.0 +; +; 2000.03.17 SNESAmp v0.9 +; Copyright (C)2003-06 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +%define APU_INC + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Compile Options + +CPU_CYC EQU 24 ;CPU clock divisor + ;10 = 2.457MHz + ;12 = 2.048MHz + ;24 = 1.024MHz + +;Debugging ability: +; This option enables code that allows hooks to be installed into the SPC700 opcode fetcher and +; DSP register handler. See SetSPCDbg and SetDSPDbg for more information. + +DEBUG EQU 1 ;Enable debugging ability + +;Integrate DSP emulation with SPC700: +; The DSP will automatically be emulated, if necessary, each time a DSP register is written to. +; With DSP integration enabled, the CNTBK and DSPBK options change from breaking SPC700 emulation +; to updating the DSP. + +DSPINTEG EQU 1 ;Enable integration of the DSP with the SPC700 + +PROFILE EQU 0 ;Enable instruction profiling + +;SPC700 specific ---------------------------- +HALFC EQU 0 ;Enable half-carry flag emulation for decimal adjusts +IPLW EQU 1 ;Enables the IPL Writable flag in register F1 + +CNTBK EQU 0 ;Break SPC700/Update DSP if a counter increases +DSPBK EQU 1 ;Break SPC700/Update DSP if register F3 is read from + ; (only if the reg is x8, x9, or 7C) + +SPEED EQU 0 ;Enable a hack to skip cycles when a counter is read + +;DSP specific ------------------------------- +MMETER EQU 1 ;Main output PPM (for AAR and visualization) +VMETER EQU 1 ;Voice output PPM (for visualization) +STEREO EQU 1 ;Enable user stereo controls. + ; (see SetDSPStereo and SetDSPEFBCT) + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Exported Functions + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Initialize Audio Processing Unit +; +;Initializes internal tables, mixer settings, memory, and registers. +;This only needs to be called once. +; +;see InitSPC and InitDSP +; +;Out: +; EAX = APU build options (see APU.Inc and SNESAPU.h GetAPUData() for more info) + +PUBLIC InitAPU, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Reset Audio Processor +; +;Clears all memory and sets registers to their default values +; +;see ResetSPC and ResetDSP + +PUBLIC ResetAPU, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Load SPC File +; +;Restores the APU state from an .SPC file. You do not need to call ResetAPU before loading a file. +; +;In: +; pFile -> 66048 byte SPC file + +PUBLIC LoadSPCFile, pFile:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Save Audio Processor State +; +;Creates a saved state of the audio processor +; +;see SaveSPC and SaveDSP +; +;In: +; pSPC -> SPCState structure +; pDSP -> DSPState structure + +PUBLIC SaveAPU, pSPC:ptr, pDSP:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Restore Audio Processor State +; +;Restores the audio processor from a saved state +; +;see RestoreSPC and RestoreDSP +; +;In: +; pSPC -> SPCState structure +; pDSP -> DSPState structure + +PUBLIC RestoreAPU, pSPC:ptr, pDSP:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Audio Processor Options +; +;Configures the sound processor emulator. Range checking is performed on all parameters. +; +;Notes: -1 can be passed for any parameter you want to remain unchanged +; see SetDSPOpt in DSP.Inc for a more detailed description +; +;In: +; mix = Mixing routine (default MIX_INT) +; chn = Number of channels (1 or 2, default 2) +; bits = Sample size (8, 16, 24, 32, or -32 [IEEE 754], default 16) +; rate = Sample rate (8000-192000, default 32000) +; inter = Interpolation type (default INT_GAUSS) +; opts = See 'DSP options' in the Equates section of DSP.Inc + +PUBLIC SetAPUOpt, mix:dword, chn:dword, bits:dword, rate:dword, inter:dword, opts:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Audio Processor Sample Clock +; +;Calculates the ratio of emulated clock cycles to sample output. Used to speed up or slow down a +;song without affecting the pitch. Range checking is performed. +; +;In: +; speed = Multiplier [16.16] (1/16x to 16x) + +PUBLIC SetAPUSmpClk, speed:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate Audio Processing Unit +; +;Emulates the APU for a specified amount of time. DSP output is placed in a buffer to be handled +;by the main program. +; +;Calling EmuAPU instead of EmuSPC and EmuDSP will ensure the two processors are kept in sync with +;each other. +; +;If the cycle count is 0, the SPC700 will be emulated until the DSP has filled the output buffer +;with the specified number of samples. +; +;If the sample count is 0, the DSP will be produce output for the same amount of time as the SPC700 +;is emulated. +; +;If the cycle count and sample count aren't equal in terms of time, then one processor will be +;halted at the end of its count while the other processor is continued. +; +;In: +; pBuf-> Buffer to store output samples (can be NULL) +; cyc = Number of clock cycles to emulate SPC700 for (APU_CLK = 1 second) +; smp = Number of samples for the DSP to generate +; +;Out: +; EAX -> End of buffer + +PUBLIC EmuAPU, pBuf:ptr, cyc:dword, smp:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Seek to Position +; +;Seeks forward in the song +; +;In: +; time = 1/64000ths second to seek forward +; fast = Use faster seeking method (may break some songs) + +PUBLIC SeekAPU, time:dword, fast:bool + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Shutdown Audio Processing Unit +; +;Currently does nothing + +PUBLIC ShutAPU, NULL
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/APU.h b/lib/snesapu/SNES/SNESAPU/APU.h new file mode 100644 index 0000000000..18a0a1922c --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/APU.h @@ -0,0 +1,337 @@ +/*************************************************************************************************** +* Program: Super Nintendo Entertainment System(tm) Audio Processing Unit Emulator * +* Platform: Intel 80386 & MMX * +* Programmer: Anti Resonance * +* * +* "SNES" and "Super Nintendo Entertainment System" are trademarks of Nintendo Co., Limited and its * +* subsidiary companies. * +* * +* This library is free software; you can redistribute it and/or modify it under the terms of the * +* GNU Lesser General Public License as published by the Free Software Foundation; either version * +* 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * +* See the GNU Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License along with this * +* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * +* Boston, MA 02111-1307 USA * +* * +* ------------------------------------------------------------------------------------------------ * +* Revision History: * +* * +* 2005.10.28 SNESAPU 3.0 +* + Added NULL pointer support to SaveAPU and RestoreAPU * +* + Moved variables into the .bss * +* + The build options are now returned by InitAPU instead of being stored in a global variable * +* - Calculate the emulated sample rate at the top of EmuAPU so SetAPUSmpClk will be thread safe * +* * +* 2003.11.04 SNESAPU 2.0 * +* + Updated EmuAPU to use SetEmuDSP when DSPINTEG is enabled * +* + Move fade out code into DSP.Asm * +* + Removed amp setting from ResetAPU * +* * +* 2003.07.12 SNESAPU 1.0a * +* - Fixed the call to SetDSPVol in SetFade that wasn't restoring the stack * +* * +* 2003.06.20 * +* + Added LoadSPCFile * +* + Added ability to pass -1 to ResetAPU to keep current amp level * +* * +* 2003.02.10 * +* + Rewrote in assembly * +* + Removed GetAPUData and memory allocation (they were specific to SNESAPU.DLL) * +* - Sometimes EmuAPU would generate an extra sample * +* * +* 2002.06.20 * +* - Call SetFade after seeking * +* * +* 2001.04.12 * +* + Updated SeekAPU to incorporate new features in SPC700 and DSP emus * +* * +* 2000.12.30 SNESAmp v2.0 * +* + Added SetAPULength, SeekAPU, SaveAPU, RestoreAPU, HaltAPU * +* + General optimizations * +* - Simplified interface a bit * +* * +* 2000.04.04 SNESAmp v1.0 * +* * +* 2000.03.17 SNESAmp v0.9 * +* Copyright (C)2003-06 Alpha-II Productions * +***************************************************************************************************/ + +namespace A2 +{ +namespace SNES +{ + + //********************************************************************************************** + // Compile Options + + //The build options correspond to the equates at the top of APU.Inc + + enum APUOpts + { + //SPC700 clock speed divisor (bits 7-0) + SA_CLKDIV = 0x00000FF, //Mask for retieving the clock divisor + + //APU build options (bits 15-8) -------- + SA_DEBUG = 0x0000100, //Debugging ability enabled + SA_DSPINTEG = 0x0000200, //DSP emulation is integrated with the SPC700 + + //SPC700 build options (bits 23-16) ---- + SA_HALFC = 0x0010000, //Half-carry flag emulation enabled + SA_IPLW = 0x0080000, //IPL ROM region writeable + SA_CNTBK = 0x0020000, //Break SPC700/Update DSP if a counter is increased + SA_DSPBK = 0x0100000, //Break SPC700/Update DSP if 0F3h is read from + SA_SPEED = 0x0040000, //Hacks are enabled to skip cycles when a counter is + // read or the processor is sleeping + SA_PROFILE = 0x0200000, //Profiling counters enabled + + //DSP build options (bits 31-24) ------- + SA_MAINPPM = 0x1000000, //Peak program metering on main output (for AAR) + SA_VOICEPPM = 0x2000000, //Peak program metering on voices (for visualization) + SA_STEREO = 0x4000000 //Stereo controls enabled (seperation and EFBCT) + }; + + + //********************************************************************************************** + // Structures + + //Profiling counters ----------------------- + struct Profile + { + struct Counter + { + u64 acc; //Accumulated time spent executing + u64 base; //Temp value (holds the TSC upon entrance) + }; + + u32 exec[256]; //Number of times instruction has been executed + + struct Branch //For branch instructions executed, the number of + { // times the branch was taken: + u32 bxx[8]; //BPL, BMI, BVC, BVS, BCC, BCS, BNE, BEQ + u32 bbc[8]; //BBC.bit + u32 bbs[8]; //BBS.bit + u32 cbne[2]; //CBNE dp, CBNE dp+X + u32 dbnz[2]; //DBNZ Y, DBNZ dp + } branch; + + struct Func //Number of times a function register was accessed + { + u32 read[16]; + u32 write[16]; + u32 write16[16]; //16-bit write via MOVW, INCW, or DECW + } func; + + struct DSP + { + u32 read[128]; //DSP register was read from + u32 write[256]; //DSP register was written to + + struct Update //Used when DSP integration is enabled + { + u32 reg[128]; //Register write that caused an update + u32 num; //Number of times DSP got updated during SPC700 emu. + u32 read; //Number of those times because of a register read + u32 gen; //Number of times the update generated output + u32 _r; + } update; + } dsp; + + struct TSC //Time-stamp counters + { + Counter host; //Place holder for user to store total execution time + Counter apu; //Time spent in EmuAPU() + Counter spc700; //Time spent in EmuSPC() + Counter dsp; //Time spent in EmuDSP() + } tsc; + }; + + +#if !defined(SNESAPU_DLL) || defined(DONT_IMPORT_SNESAPU) + + //////////////////////////////////////////////////////////////////////////////////////////////////// + // Private Declarations + + extern "C" Profile profile; //Profiling counters + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // External Functions + +extern "C" { + + //********************************************************************************************** + // Initialize Audio Processing Unit + // + // Initializes internal tables, mixer settings, memory, and registers. + // This only needs to be called once. + // + // see InitSPC() and InitDSP() + // + // Out: + // APU build options (see the SA_??? above, APU.Inc, and SNESAPU.h GetAPUData()) + + u32 InitAPU(); + + + //********************************************************************************************** + // Reset Audio Processor + // + // Clears all memory and sets registers to their default values + // + // Thread safe: + // No + // + // see ResetSPC() and ResetDSP() + + void ResetAPU(); + + + //********************************************************************************************** + // Load SPC File + // + // Restores the APU state from an .SPC file. You do not need to call ResetAPU() before loading + // a file. + // + // Thread safe: + // No + // + // In: + // pFile -> 66048 byte SPC file + + void LoadSPCFile(const void* pFile); + + + //********************************************************************************************** + // Save Audio Processor State + // + // Creates a saved state of the audio processor + // + // see SaveSPC() and SaveDSP() + // + // Thread safe: + // No + // + // In: + // pSPC -> SPCState structure (can be NULL) + // pDSP -> DSPState structure (can be NULL) + + void SaveAPU(SPCState* pSPC, DSPState* pDSP); + + + //********************************************************************************************** + // Restore Audio Processor State + // + // Restores the audio processor from a saved state + // + // see RestoreSPC() and RestoreDSP() + // + // Thread safe: + // No + // + // In: + // spc -> SPCState structure (can be NULL) + // dsp -> DSPState structure (can be NULL) + + void RestoreAPU(const SPCState& spc, const DSPState& dsp); + + + //********************************************************************************************** + // Set Audio Processor Options + // + // Configures the sound processor emulator. Range checking is performed on all parameters. + // + // Notes: -1 can be passed for any parameter you want to remain unchanged + // see SetDSPOpt() in DSP.h for a more detailed explantion of the options + // + // Thread safe: + // No, except when only changing the interpolation type + // + // In: + // mix = Mixing routine (default MIX_INT) + // chn = Number of channels (1 or 2, default 2) + // bits = Sample size (8, 16, 24, 32, or -32 [IEEE 754], default 16) + // rate = Sample rate (8000-192000, default 32000) + // inter = Interpolation type (default INT_GAUSS) + // opts = See enum DSPOpts + + void SetAPUOpt(Mixing mix, u32 chn, u32 bits, u32 rate, DSPInter inter = INT_INVALID, + Set<DSPOpts> opts = ~Set<DSPOpts>()); + + + //********************************************************************************************** + // Set Audio Processor Sample Clock + // + // Calculates the ratio of emulated clock cycles to sample output. Used to speed up or slow + // down a song without affecting the pitch. Range checking is performed. + // + // This function is safe to call while the APU is emulated in another thread + // + // Thread safe: + // Yes + // + // In: + // speed = Multiplier [16.16] (1/16x to 16x) + + void SetAPUSmpClk(u32 speed); + + + //********************************************************************************************** + // Emulate Audio Processing Unit + // + // Emulates the APU for a specified amount of time. DSP output is placed in a buffer to be + // handled by the main program. + // + // Calling EmuAPU() instead of EmuSPC() and EmuDSP() will ensure the two processors are kept in + // sync with each other. + // + // If the cycle count is 0, the SPC700 will be emulated until the DSP has filled the output + // buffer with the specified number of samples. + // + // If the sample count is 0, the DSP will be produce output for the same amount of time as the + // SPC700 is emulated. + // + // If the cycle count and sample count aren't equal in terms of time, then one processor will be + // halted at the end of its count while the other processor is continued. + // + // In: + // pBuf -> Buffer to store output samples (can be NULL) + // cycles = Number of clock cycles to emulate SPC700 for (APU_CLK = 1 second) + // samples = Number of samples for the DSP to generate + // + // Out: + // -> End of buffer + + void* EmuAPU(void* pBuf, u32 cycles, u32 samples); + + + //********************************************************************************************** + // Seek to Position + // + // Seeks forward in the song from the current position + // + // In: + // time = 1/64000ths of a second to seek forward (must be >= 0) + // fast = Use faster seeking method (may break some songs) + + void SeekAPU(u32 time, bool fast); + + + //********************************************************************************************** + // Shutdown Audio Processing Unit + // + // Currently does nothing + + void ShutAPU(); + +} // extern "C" + +#endif //!SNESAPU_DLL + +} // namespace SNES +} // namespace A2 + diff --git a/lib/snesapu/SNES/SNESAPU/DSP.Asm b/lib/snesapu/SNES/SNESAPU/DSP.Asm new file mode 100644 index 0000000000..05254b39e3 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/DSP.Asm @@ -0,0 +1,8191 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;SNES Digital Signal Processor Emulator +; Copyright (C)1999-2006 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +CPU PPRO +BITS 32 + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Header files + +%include "../../Macro.Inc" +%include "APU.Inc" +%include "SPC700.Inc" +%define INTERNAL +%include "DSP.Inc" + +GLOBAL SetDSPReg_A ;Needed by SPC700.Asm + +%if DSPINTEG +GLOBAL UpdateDSP ;Needed by SPC700.Asm +GLOBAL SetEmuDSP ;Needed by APU.Asm +%endif + +%if PROFILE +EXTERN profile ;SPC700.Asm - Profiling information +%endif + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Equates + +;Envelope precision ------------------------- +E_SHIFT EQU 4 ;Amount to shift envelope to get 8-bit signed value + +;Envelope adjustment rates ------------------ +A_GAIN EQU (1 << E_SHIFT) ;Amount to adjust envelope values +A_LINEAR EQU (128*A_GAIN)/64 ;Linear rate to increase/decrease envelope +A_REL EQU (128*A_GAIN)/256 ;Rate to decrease envelope during release +A_BENT EQU (128*A_GAIN)/256 ;Rate to increase envelope after bend +A_NOATT EQU (64*A_GAIN) ;Rate to increase if attack rate is set to 0ms +A_EXP EQU 0 ;Rate to decrease envelope exponentially (Not used) + +;Envelope destination values ---------------- +D_MAX EQU (128*A_GAIN)-1 ;Maximum envelope value +D_ATTACK EQU (128*A_GAIN*63)/64 ;Destination of attack rate +D_BENT EQU (128*A_GAIN*3)/4 ;First destination of bent line +D_DECAY EQU (128*A_GAIN)/8 ;Minimum decay destination value +D_MIN EQU 0 ;Minimum envelope value + +;Array sizes -------------------------------- +MIX_SIZE EQU 1024 ;Size of mixing buffer in samples +FIRBUF EQU 2*2*64 ;stereo * ring loop * 256kHz / 32kHz +ECHOBUF EQU 2*((192000*240)/1000) ;Size of echo buffer (stereo * 192kHz * 240ms) + +LOWTAPS EQU 32 ;Number of taps in low-pass FIR filter +LOWBUF EQU 2*2*LOWTAPS + +VAATAPS EQU 16 + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Data + +SECTION .rodata ALIGN=32 + + ;12-bit Gaussian curve generated by SNES DSP + gaussTab DW 5920,20880, 5984, 0, 5856,20880, 6048, 0, 5792,20864, 6096, 0, 5728,20864, 6160, 0 + DW 5664,20864, 6224, 0, 5616,20864, 6288, 0, 5552,20864, 6352, 0, 5488,20848, 6416, 0 + DW 5424,20848, 6480, 0, 5376,20848, 6560, 0, 5312,20832, 6624, 0, 5248,20832, 6688, 0 + DW 5200,20816, 6752, 0, 5136,20800, 6816, 0, 5088,20800, 6880, 0, 5024,20784, 6944, 0 + DW 4976,20768, 7024, 16, 4912,20752, 7088, 16, 4864,20752, 7152, 16, 4800,20736, 7216, 16 + DW 4752,20720, 7296, 16, 4688,20704, 7360, 16, 4640,20688, 7424, 16, 4576,20672, 7504, 16 + DW 4528,20656, 7568, 16, 4480,20640, 7632, 16, 4416,20608, 7712, 16, 4368,20592, 7776, 32 + DW 4320,20576, 7856, 32, 4272,20544, 7920, 32, 4208,20528, 7984, 32, 4160,20512, 8064, 32 + DW 4112,20480, 8128, 32, 4064,20464, 8208, 32, 4016,20432, 8272, 48, 3968,20400, 8352, 48 + DW 3920,20384, 8432, 48, 3872,20352, 8496, 48, 3824,20320, 8576, 48, 3776,20304, 8640, 64 + DW 3728,20272, 8720, 64, 3680,20240, 8800, 64, 3632,20208, 8864, 64, 3584,20176, 8944, 64 + DW 3536,20144, 9008, 80, 3488,20112, 9088, 80, 3440,20080, 9168, 80, 3392,20048, 9232, 80 + DW 3360,20016, 9312, 96, 3312,19968, 9392, 96, 3264,19936, 9472, 96, 3216,19904, 9536, 96 + DW 3184,19856, 9616, 112, 3136,19824, 9696, 112, 3088,19792, 9776, 112, 3056,19744, 9840, 128 + DW 3008,19712, 9920, 128, 2976,19664,10000, 128, 2928,19632,10080, 144, 2880,19584,10160, 144 + DW 2848,19536,10240, 144, 2800,19504,10304, 160, 2768,19456,10384, 160, 2736,19408,10464, 160 + DW 2688,19360,10544, 176, 2656,19312,10624, 176, 2608,19280,10704, 176, 2576,19232,10784, 192 + DW 2544,19184,10848, 192, 2496,19136,10928, 208, 2464,19088,11008, 208, 2432,19040,11088, 224 + DW 2400,18976,11168, 224, 2352,18928,11248, 240, 2320,18880,11328, 240, 2288,18832,11408, 240 + DW 2256,18784,11488, 256, 2224,18720,11568, 256, 2192,18672,11648, 272, 2144,18624,11712, 272 + DW 2112,18560,11792, 288, 2080,18512,11872, 304, 2048,18448,11952, 304, 2016,18400,12032, 320 + DW 1984,18336,12112, 320, 1952,18288,12192, 336, 1920,18224,12272, 336, 1888,18176,12352, 352 + DW 1872,18112,12432, 368, 1840,18048,12512, 368, 1808,18000,12592, 384, 1776,17936,12672, 384 + DW 1744,17872,12752, 400, 1712,17808,12832, 416, 1696,17744,12896, 432, 1664,17696,12976, 432 + DW 1632,17632,13056, 448, 1600,17568,13136, 464, 1584,17504,13216, 464, 1552,17440,13296, 480 + DW 1520,17376,13376, 496, 1504,17312,13456, 512, 1472,17248,13536, 512, 1440,17184,13616, 528 + DW 1424,17120,13680, 544, 1392,17056,13760, 560, 1376,16976,13840, 576, 1344,16912,13920, 576 + DW 1328,16848,14000, 592, 1296,16784,14080, 608, 1280,16720,14144, 624, 1248,16640,14224, 640 + DW 1232,16576,14304, 656, 1216,16512,14384, 672, 1184,16432,14464, 688, 1168,16368,14528, 704 + DW 1136,16304,14608, 720, 1120,16224,14688, 736, 1104,16160,14768, 752, 1072,16080,14832, 768 + DW 1056,16016,14912, 784, 1040,15952,14992, 800, 1024,15872,15056, 816, 992,15808,15136, 832 + DW 976,15728,15216, 848, 960,15648,15280, 864, 944,15584,15360, 880, 928,15504,15440, 896 + DW 896,15440,15504, 928, 880,15360,15584, 944, 864,15280,15648, 960, 848,15216,15728, 976 + DW 832,15136,15808, 992, 816,15056,15872, 1024, 800,14992,15952, 1040, 784,14912,16016, 1056 + DW 768,14832,16080, 1072, 752,14768,16160, 1104, 736,14688,16224, 1120, 720,14608,16304, 1136 + DW 704,14528,16368, 1168, 688,14464,16432, 1184, 672,14384,16512, 1216, 656,14304,16576, 1232 + DW 640,14224,16640, 1248, 624,14144,16720, 1280, 608,14080,16784, 1296, 592,14000,16848, 1328 + DW 576,13920,16912, 1344, 576,13840,16976, 1376, 560,13760,17056, 1392, 544,13680,17120, 1424 + DW 528,13616,17184, 1440, 512,13536,17248, 1472, 512,13456,17312, 1504, 496,13376,17376, 1520 + DW 480,13296,17440, 1552, 464,13216,17504, 1584, 464,13136,17568, 1600, 448,13056,17632, 1632 + DW 432,12976,17696, 1664, 432,12896,17744, 1696, 416,12832,17808, 1712, 400,12752,17872, 1744 + DW 384,12672,17936, 1776, 384,12592,18000, 1808, 368,12512,18048, 1840, 368,12432,18112, 1872 + DW 352,12352,18176, 1888, 336,12272,18224, 1920, 336,12192,18288, 1952, 320,12112,18336, 1984 + DW 320,12032,18400, 2016, 304,11952,18448, 2048, 304,11872,18512, 2080, 288,11792,18560, 2112 + DW 272,11712,18624, 2144, 272,11648,18672, 2192, 256,11568,18720, 2224, 256,11488,18784, 2256 + DW 240,11408,18832, 2288, 240,11328,18880, 2320, 240,11248,18928, 2352, 224,11168,18976, 2400 + DW 224,11088,19040, 2432, 208,11008,19088, 2464, 208,10928,19136, 2496, 192,10848,19184, 2544 + DW 192,10784,19232, 2576, 176,10704,19280, 2608, 176,10624,19312, 2656, 176,10544,19360, 2688 + DW 160,10464,19408, 2736, 160,10384,19456, 2768, 160,10304,19504, 2800, 144,10240,19536, 2848 + DW 144,10160,19584, 2880, 144,10080,19632, 2928, 128,10000,19664, 2976, 128, 9920,19712, 3008 + DW 128, 9840,19744, 3056, 112, 9776,19792, 3088, 112, 9696,19824, 3136, 112, 9616,19856, 3184 + DW 96, 9536,19904, 3216, 96, 9472,19936, 3264, 96, 9392,19968, 3312, 96, 9312,20016, 3360 + DW 80, 9232,20048, 3392, 80, 9168,20080, 3440, 80, 9088,20112, 3488, 80, 9008,20144, 3536 + DW 64, 8944,20176, 3584, 64, 8864,20208, 3632, 64, 8800,20240, 3680, 64, 8720,20272, 3728 + DW 64, 8640,20304, 3776, 48, 8576,20320, 3824, 48, 8496,20352, 3872, 48, 8432,20384, 3920 + DW 48, 8352,20400, 3968, 48, 8272,20432, 4016, 32, 8208,20464, 4064, 32, 8128,20480, 4112 + DW 32, 8064,20512, 4160, 32, 7984,20528, 4208, 32, 7920,20544, 4272, 32, 7856,20576, 4320 + DW 32, 7776,20592, 4368, 16, 7712,20608, 4416, 16, 7632,20640, 4480, 16, 7568,20656, 4528 + DW 16, 7504,20672, 4576, 16, 7424,20688, 4640, 16, 7360,20704, 4688, 16, 7296,20720, 4752 + DW 16, 7216,20736, 4800, 16, 7152,20752, 4864, 16, 7088,20752, 4912, 16, 7024,20768, 4976 + DW 0, 6944,20784, 5024, 0, 6880,20800, 5088, 0, 6816,20800, 5136, 0, 6752,20816, 5200 + DW 0, 6688,20832, 5248, 0, 6624,20832, 5312, 0, 6560,20848, 5376, 0, 6480,20848, 5424 + DW 0, 6416,20848, 5488, 0, 6352,20864, 5552, 0, 6288,20864, 5616, 0, 6224,20864, 5664 + DW 0, 6160,20864, 5728, 0, 6096,20864, 5792, 0, 6048,20880, 5856, 0, 5984,20880, 5920 + + ;Registers that require special handling depending on output type + dspRegsI DD RVolL, RVolR, RMVolL, RMVolR, REVolL, REVolR, REFB, RFCI ;Integer + dspRegsM DD RVolLM, RVolRM, RMVolLM, RMVolRM, REVolLM, REVolRM, REFBM, RFCI ;Monaural + dspRegsF DD RVolLF, RVolRF, RMVolLF, RMVolRF, REVolLF, REVolRF, REFBF, RFCF ;Floating-point + + ;Pointers to interpolation functions for each mixing routine + intRout DD NoneI, LinearI, CubicI, GaussI, SincI + DD NoneX, LinearX, CubicX, GaussX, SincX + DD NoneF, LinearF, CubicF, GaussF, SincF + DD 0 + + ;Pointers to each mixing routine: none, integer (386), mmx, and float + mixRout DD EmuDSPN, EmuDSPI, EmuDSPX, EmuDSPF + + ;Masks for MMX gaussian interpolation ---- + loSmp DD 00000FFFFh,00000FFFFh + hiSmp DD 0FFFF0000h,0FFFF0000h + + ;Frequency table ------------------------- + freqTab DW 0 + DW 2048, 1536, 1280 ;Number of samples between updates. Used to determine + DW 1024, 768, 640 ;envelope rates and noise frequencies + DW 512, 384, 320 + DW 256, 192, 160 + DW 128, 96, 80 + DW 64, 48, 40 + DW 32, 24, 20 + DW 16, 12, 10 + DW 8, 6, 5 + DW 4, 3 + DW 2 + DW 1 + + ;Floating point constants ---------------- + fn2_5 DD -2.5 + fp0_5 DD 0.5 + + Scale32 fp64k,16 + Scale32 fpShR7,-7 ;Voice volume + Scale32 fpShR15,-15 ;Interpolation + Scale32 fpShR16,-16 + Scale32 fpShR23,-23 ;Echo feedback + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Variables + +%ifndef WIN32 +SECTION .bss ALIGN=256 +%else +SECTION .bss ALIGN=64 + +;The BSS must be aligned on at least a 256-byte boundary. This is tricky in Windows because the +;win32 object files can only specify 64-byte alignment. To enforce that the BSS is properly +;aligned, a debug breakpoint has been inserted into InitDSP that will be triggered if alignment is +;not strict enough. In such an event, try padding with multiples of 64 until it works. + + resb 192 ;Force page alignment +%endif + +;Don't touch! All arrays are carefully aligned on large boundaries to facillitate easier indexing +;and better cache utilization. Add new variables at the end or in empty slots. + + ;DSP Core ---------------------------- [0] + mix resb 8*128 ;<VoiceMix> Mixing settings for each voice + dsp resb 128 ;<DSPRAM> DSP registers + + ;Look-up Tables -------------------- [480] + rateTab resd 32 ;Update Rate Table + cubicTab resq 256 ;Cubic interpolation + sincTab resq 256*2 ;Sinc interpolation + regTab resd 128 ;Jump table for DSP register writes + + ;Integration with SPC700 ---------- [1F00] + %define _Out(v) ESI + (out %+ v - output) + output: + outPBuf resd 1 ;-> output buffer + outLeft resd 1 ;Number of samples left to fill output buffer + outCnt resd 1 ;t64 count at last call to EmuDSP + outDec resd 1 ;Fractional number of samples to be generated + outRate resd 1 ;Output sample rate set by APU.Asm + + ;Globals -------------------------- [1F14] + songLen resd 1 ;Length of song (in ticks) + fadeLen resd 1 ;Length of fade (in ticks) + + pTrace resd 1 ;-> Debugging vector + dbgOpt resb 1 ;Debugging options + + konLate resb 1 + resb 2 + resd 1 + + ;DSP Options ---------------------- [1F28] + procType resb 1 ;Type of host CPU (returned by GetProcType) + dspMix resb 1 ;Mixing routine + + dspChn resb 1 ;Number of channels being output + dspSize resb 1 ;Size of samples in bytes + + dspRate resd 1 ;Output sample rate + pitchBas resd 1 ;Base sample rate + pitchAdj resd 1 ;Amount to adjust pitch rates [16.16] + + pInter resd 1 ;-> interpolation function + dspInter resb 1 ;Interpolation method + voiceMix resb 1 ;Voices that are currently being mixed + + dspOpts resb 1 ;Option flags passed to SetDSPOpt + surround resb 1 ;Turn on surround sound + pDecomp resd 1 ;-> sample decompression routine + + ;Volume --------------------------- [1F44] + volAmp resd 1 ;Amplification [16.16] + volAtt resd 1 ;Global volume attenuation [1.16] + volAdj resd 1 ;Amount to adjust main volumes [-15.16] + + volMainL resd 1 ;Main volume + volMainR resd 1 + volEchoL resd 1 ;Echo volume + volEchoR resd 1 + + mMaxL resd 1 ;Maximum absolute sample output + mMaxR resd 1 + + volRamp resd 2 ;Amount to ramp volume per sample (+/-) + volSepar resd 1 ;Stereo separation + + ;Noise ---------------------------- [1F74] + nRate resd 1 ;Noise sample rate reciprocal [.32] + nAcc resd 1 ;Noise accumulator [.32] (>= 1 generate a new sample) + nSmp resd 1 ;Current Noise sample + + ;Echo filtering ------------------- [1F80] + firTaps resd 2*8 ;Filter coefficents (doubled for stereo MMX) + firCur resd 1 ;Index of the first sample to feed into the filter + firRate resd 1 ;Rate to feed samples into filter + firDec resd 1 ;Temp + firEnabl resb 1 ;0 if filtering is disabled (each bit C0-C7 != 0) + disEcho resb 1 ;0 if echo is enabled + resb 2 ; [0] - FIR coefficients are all 0 + ; [4] - User has disabled echo + ; [5] - Echo is disabled in FLG register + + ;Echo ----------------------------- [1FD0] + echoDel resd 1 ;Size of delay (in bytes) + echoCur resd 1 ;Current sample in echo area + resd 1 + efbct resd 1 ;User specified echo feedback crosstalk + echoFB resd 2 ;Echo feedback + echoFBCT resd 2 ;Echo feedback crosstalk + + ;Single source playback ----------- [1FF0] + %define _Src(v) EBX + (src %+ v - src) + src: + resw 8 ;Temporary buffer for single sound playback + srcBuf resw 16 ; tBuf must start on a 64-byte boundary + srcIdx resd 1 + srcRate resd 1 + srcDec resd 1 + srcBlk resd 1 + srcLoop resd 1 + srcP1 resw 1 + srcP2 resw 1 + srcBRR resb 8 ;Temporary buffer for storing BRR block + + ;Low-pass filter ------------------ [2040] + realRate resd 1 ;Real output sample rate, not emulated rate + realOutS resd 1 ;Real size of output buffer + + lowAmp resd 1 ;Amplification to apply during filtering + lowCur resd 1 + lowRate resd 1 ;Rate to feed samples into filter [16.16] + lowDec resd 1 ;Current sample fraction [0.16] + lowRFI resd 2 ;Current RFI simulation sample + + vaaCut resd 1 ;Cutoff rate for voice AA filtering + + ;Auto Amplification Reduction ----- [2064] + %define _AAR(v) ESI + (aar %+ v - aar) + aar: + aarCnt resd 1 ;t64Cnt at last amp increase + aarMin resd 1 ;Minimum value to decrease to + aarMax resd 1 ;Maximum value to increase to + aarMMaxL resd 1 ;Maximum absolute main output + aarMMaxR resd 1 + aarThrsh resd 1 ;Attenuation threshold + aarType resb 1 ;Type of AAR + resb 3 + + ;Space for new variables ---------- [2080] + resd 32 ;<-- room for new variables here + + ;Storage buffers ------------------ [2100] + mixBuf resd 2*2*MIX_SIZE ;Temporary mixing buffer (2 main, 2 echo) + + ;Ring buffers are reversed, meaning that buffer[0] contains the most recent sample, buffer[1] + ;contains the previous sample, and so forth. + startBuf: + echoBuf resd ECHOBUF ;External echo memory (240ms @ 192kHz) + firBuf resd FIRBUF ;Unaltered echo samples fed into FIR filter + lowBuf resd LOWBUF ;Interpolated samples fed into low-pass FIR filter + vaaBuf resw 8*VAATAPS + endBuf: + + lowTaps resd LOWTAPS ;Filter coefficients used for low-pass filter + vaaTaps resd 8*VAATAPS ;Filter coefficients for voice anti-aliasing filters + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Code + +SECTION .text ALIGN=16 + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get Processor Information + +PROC GetProcInfo +USES ECX,EDX,EBX + + ;Test for CPUID instruction -------------- + PushFD + Mov EDX,[ESP] ;EDX = EFLAGS + XOr dword [ESP],200000h ;Flip CPUID support flag + PopFD ;Restore EFLAGS + PushFD + Pop EAX ;EAX = New EFLAGS + XOr EAX,EDX ;Does processor support the CPUID instruction? + JZ .Done ; No, Return 0 + + XOr EAX,EAX ;Get the number of extended functions + CPUID + Test EAX,EAX ;Does CPU have any? + JZ .Done ; No, Return 0 + + ;Test for MMX support -------------------- + Mov EAX,1 ;EAX = Get extended information + CPUID + XOr EAX,EAX + BT EDX,23 ;Does CPU have MMX support? + JNC .Done ; No, Return 0 + + ;Check processor manufacturer ------------ + XOr EAX,EAX + CPUID + + Cmp EBX,"Genu" ;Is processor an Intel? + SetE AL + Cmp EDX,"ineI" + SetE AH + Or AL,AH + Cmp ECX,"ntel" + SetE AH + Or AL,AH + JZ .NotIntel + Mov EAX,1 + CPUID + BT EDX,25 ;Does CPU have SIMD support? + SetC AL ; Yes + MovZX EAX,AL + ShL EAX,2 + Or AL,1 ;Return MMX support as well + RetS + .NotIntel: + + Cmp EBX,"Auth" ;Is processor an AMD? + SetE AL + Cmp EDX,"enti" + SetE AH + Or AL,AH + Cmp ECX,"cAMD" + SetE AH + Or AL,AH + JZ .NotAMD + Mov EAX,80000001h + CPUID + BT EDX,31 ;Does CPU have 3DNow! support? + SetC AL ; Yes + MovZX EAX,AL + Add EAX,EAX + Or AL,1 + RetS + .NotAMD: + + Mov EAX,1 + + .Done: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Calculate Power of e +; +;Desc: +; Calculates e to the power of x by using the formula: +; +; 2^(x*log2(e)) +; +; Where 2^x is calculated by: +; +; 2^int(x) * 2^frac(x) +; +;In: +; ST = x +; +;Out: +; ST = e^x + +;PROC Exp +; +; FStCW [ESP-6] ;Save control state +; FStCW [ESP-4] +; FStCW [ESP-2] +; +; And word [ESP-4],0F0FFh +; And word [ESP-6],0F0FFh +; Or word [ESP-4],0300h ;Enable extended double precision +; Or word [ESP-6],0C00h ; " " w/ rounding towards 0 +; FLdCW [ESP-4] +; +; FLdL2e ; |x Log2(e) +; FMulP ST1,ST ; |x*Log2(e) +; +; FLd ST1 ; |ex ex +; FLdCW [ESP-6] +; FRndInt ;Get the integer portion |ex int(ex) +; FLdCW [ESP-4] +; FSub ST1,ST ;Get the fractional portion|ex-iex iex +; +; FLd1 ; |fex iex 1 +; FScale ; |fex iex 1<<iex +; FStP ST1 ; |fex i +; +; FXch ST1 ; |i fex +; F2XM1 ;Compute 2^frac |i pow(2,fex)-1 +; +; FMul ST,ST1 ;Compute 2^int |i p*i +; FAddP ST1,ST ; |i+p +; +; FLdCW [ESP-2] ;Restore control state +; +;ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Initialize DSP + +PROC InitDSP +LOCALS ipD,fn1_5,fn0_5,fp1_5,fp256,fp32k,fp32km1,fpShR8,fpShR10 +USES ECX,EDX,EBX,ESI,EDI + + ;Initialize local variables -------------- + Mov dword [%$fn1_5],0BFC00000h ;-1.5 + Mov dword [%$fn0_5],0BF000000h ;-0.5 + Mov dword [%$fp1_5],3FC00000h ;1.5 + Mov dword [%$fp32k],47000000h ;32768.0 + Mov dword [%$fp32km1],46FFFE00h ;32767.0 + Mov dword [%$fp256],43800000h ;256.0 + Mov dword [%$fpShR8],3B800000h ;1/256.0 + Mov dword [%$fpShR10],3A800000h ;1/1024.0 + + Mov byte [dspMix],-1 ;Reset values so SetDSPOpt will create new ones + Mov byte [dspChn],-1 + Mov byte [dspSize],-1 + Mov dword [dspRate],-1 + Mov dword [realRate],32000 + Mov dword [pitchBas],32000 + Mov byte [dspInter],-1 + Mov byte [dspOpts],0 + Mov byte [disEcho],0 + Mov dword [volSepar],0 + Mov dword [volAmp],10000h ;Amplification set to 0dB + Mov dword [efbct],10000h ;No echo crosstalk + + Mov EDI,mix ;Erase all mixer settings + XOr EAX,EAX + Mov ECX,(8*128)/4 + Rep StoSD + +%ifdef WIN32 + Mov EAX,mix ;Halt execution if BSS is not properly aligned + Test AL,AL + JZ short .BSSOkay + Int 3 + .BSSOkay: + + Mov EAX,srcBuf + Test AL,20h + JZ short .TBufOK + Int 3 + .TBufOK: +%endif + + ;Create jump table for DSP register writes + Mov EAX,RNull ;Initialize all registers to NULL + Mov EDI,regTab + Mov ECX,128 + Rep StoSD + + Mov EDI,regTab ;Set voice specific registers + Mov EDX,7*64 + Mov EAX,RPitch + Mov EBX,RADSR + Mov ECX,RGain + .NextReg: + Mov [ 8+EDX+EDI],EAX + Mov [12+EDX+EDI],EAX + Mov [20+EDX+EDI],EBX + Mov [24+EDX+EDI],EBX + Mov [28+EDX+EDI],ECX + Sub EDX,64 + JNC short .NextReg + + Mov dword [4Ch*4+regTab],RKOn ;Set global registers + Mov dword [5Ch*4+regTab],RKOf ;The remaining registers will get set by SetDSPOpt + Mov dword [6Ch*4+regTab],RFlg + + Mov dword [2Dh*4+regTab],RPMOn + Mov dword [7Dh*4+regTab],REDl + + + ;Build a look-up table based on the Hermite cubic spline + ; + ; y(x) = ax^3 + bx^2 + cx + d | Cubic spline equation + ; + ; a = y(0) | end point 1 (delta x = 0) + ; b = y(1) | end point 2 (delta x = 1) + ; c = y'(0) | tangent vector at end point 1 + ; d = y'(1) | tangent vector at end point 2 + ; + ;--- + ; + ; 3 (s[0] - s[1]) - s[-1] + s[2] + ; a = ------------------------------ + ; 2 + ; + ; 5 s[0] + s[2] + ; b = 2 s[1] + s[-1] - ------------- + ; 2 + ; + ; s[1] - s[-1] + ; c = ------------ + ; 2 + ; + ; d = s[0] + ; + ;s is a four sample array with [0] being the current sample + + FInit ;Reset FPU, otherwise there'll be problems + Mov dword [%$ipD],0 ;Start with a delta of 0 (calculate 256 points) + + Mov EDI,cubicTab ;EDI->Cubic array |FPU Stack after execution + .NextC: + ;x1=(n/256) x2=(n/256)^2 x3=(n/256)^3 + FILd dword [%$ipD] ;Load (int) delta |D + FDiv dword [%$fp256] ;Divide delta by 256 |D/256=X1 + FLd ST ;Copy top of stack |X1 X1 + FMul ST,ST1 ;Square point |X1 X1*X1=X2 + FLd ST ; |X1 X2 X2 + FMul ST,ST2 ;Cube point |X1 X2 X2*X1=X3 + + ;s[-1] *= -.5(x^3) + (x^2) - .5x ------ + FLd dword [%$fn0_5] ; |X1 X2 X3 -0.5 + FMul ST,ST3 ; |X1 X2 X3 -0.5*X1=T1 + FAdd ST,ST2 ; |X1 X2 X3 T1+X2 + FLd dword [%$fn0_5] ; |X1 X2 X3 T1 -0.5 + FMul ST,ST2 ; |X1 X2 X3 T1 -0.5*X3=T2 + FAddP ST1,ST ; |X1 X2 X3 T1+T2 + FMul dword [%$fp32km1] ;Convert to fixed point (-.15) + FIStP word [EDI] ;Store value in cubicTab |X1 X2 X3 + + ;s[0] *= 1.5(x^3) - 2.5(x^2) + 1 ------ + FLd dword [fn2_5] ; |X1 X2 X3 -2.5 + FMul ST,ST2 ; |X1 X2 X3 -2.5*X2=T1 + FLd dword [%$fp1_5] ; |X1 X2 X3 T1 1.5 + FMul ST,ST2 ; |X1 X2 X3 T1 1.5*X3=T2 + FLd1 ; |X1 X2 X3 T1 T2 1.0 + FAddP ST1,ST ; |X1 X2 X3 T1 T2+1 + FAddP ST1,ST ; |X1 X2 X3 T1+T2 + FMul dword [%$fp32km1] + FIStP word [2+EDI] ; |X1 X2 X3 + + ;s[1] *= -1.5(x^3) + 2(x^2) + .5x ----- + FLd dword [fp0_5] ; |X1 X2 X3 0.5 + FMul ST,ST3 ; |X1 X2 X3 0.5*X1=T1 + FLd ST2 ; |X1 X2 X3 T1 X2 + FAdd ST,ST3 ; |X1 X2 X3 T1 X2+X2=T2 + FLd dword [%$fn1_5] ; |X1 X2 X3 T1 T2 -1.5 + FMul ST,ST3 ; |X1 X2 X3 T1 T2 -1.5*X3=T3 + FAddP ST1,ST ; |X1 X2 X3 T1 T2+T3 + FAddP ST1,ST ; |X1 X2 X3 T1+T2 + FMul dword [%$fp32km1] + FIStP word [4+EDI] ; |X1 X2 X3 + + ;s[2] *= .5(x^3) - .5(x^2) ------------ + FLd dword [%$fn0_5] ; |X1 X2 X3 -0.5 + FMul ST,ST2 ; |X1 X2 X3 -0.5*X2=T1 + FLd dword [fp0_5] ; |X1 X2 X3 T1 0.5 + FMul ST,ST2 ; |X1 X2 X3 T1 0.5*X3=T2 + FAddP ST1,ST ; |X1 X2 X3 T1+T2 + FMul dword [%$fp32km1] + FIStP word [6+EDI] ; |X1 X2 X3 + Add EDI,8 + + FStP ST ;Pop X's off stack |X1 X3 + FStP ST ; |X3 + FStP ST ; |(empty) + + Inc byte [%$ipD] + JNZ .NextC + + ;Build a look-up table for 8-point sinc interpolation with a Hann window + ; + ; sin((n/2)pi x) + ; -------------- * (0.5 + 0.5cos(pi x)) + ; (n/2)pi x + ; + ;n is the number of points of interpolation (8) + + Mov EDI,sincTab + + XOr EAX,EAX ;If ipD were initialized to -768 (-3.0), a divide by + Mov [EDI],EAX ; zero error would occur when building the table. + Mov [4+EDI],EAX ; So we manually initialize the first row, which is + Mov [8+EDI],EAX ; easy to do. + Mov [12+EDI],EAX + Mov word [6+EDI],32767 ;Set first row to 0 0 0 1 0 0 0 0 + Add EDI,16 + + Mov dword [%$ipD],-769 ;Fill remaining rows -769 to -1023 (-3.004 to -3.996) + Mov CH,255 + .NextS: + + Mov CL,8 + .NextSS: + FILd dword [%$ipD] ;(x >> 10) * 4pi |x + FMul dword [%$fpShR8] ; |x>>8 + FLdPi ; |x pi + FMulP ST1,ST ; |x*pi + FLd ST ; |x x + + FSin ;Sinc function |x sin(x) + FDivRP ST1,ST ;sin(x) / x |x/sin(x) + + FILd dword [%$ipD] ;Hann window |sinc x + FMul dword [%$fpShR10] ;cos((x >> 10) * pi) |sinc x>>10 + FLdPi ; |sinc x pi + FMulP ST1,ST ; |sinc x*pi + FCos ; |sinc cos(x*pi) + FLd1 ;(1.0 + cos) * 0.5 |sinc cos 1.0 + FAddP ST1,ST ; |sinc cos+1 + FMul dword [fp0_5] ; |sinc cos*0.5 + + FMulP ST1,ST ;Multiply by window |sinc*window + FMul dword [%$fp32k] ;Convert to integer |sinc<<15 + FIStP word [EDI] ;Store |(empty) + Add EDI,2 + + Add dword [%$ipD],256 ;Move to next point of interpolation (x += 256) + Dec CL + JNZ .NextSS + + Sub dword [%$ipD],801h + Dec CH + JNZ .NextS + + + Call GetProcInfo + Mov [procType],AL + + Call SetDSPOpt,1,2,16,32000,INT_GAUSS,0 + Call SetDSPDbg,0,0 + Call SetDSPAAR,AAR_TOFF,32768,16384,32768 ;No AAR, threshold = 0dB, min = -6dB, max = 0dB + Call SetDSPDbg,0,0 ;Disable debugging + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Reset DSP Settings + +PROC ResetDSP +USES ECX,EBX,EDI + + XOr EAX,EAX + + ;Erase DSP Registers --------------------- + Mov EDI,dsp + Mov ECX,128/4 + Rep StoSD + Mov byte [dsp+flg],0E0h ;Place DSP in power up mode + + ;Erase internal mixing settings ---------- + Mov AH,8 + Mov EBX,dsp + Mov EDI,mix + .ClrMix: + Mov CL,mFlg + Rep StoSB + And byte [EDI],MFLG_MUTE ;Leave voice muted + Or byte [EDI],MFLG_OFF ;Voice is inactive + Inc EDI + Mov CL,7Fh-mFlg + Rep StoSB + + Mov [EDI+pDSPV-80h],EBX + Add EBX,10h + + Dec AH + JNZ short .ClrMix + + ;Erase global volume settings ------------ + Mov [volMainL],EAX + Mov [volMainR],EAX + Mov [volEchoL],EAX + Mov [volEchoR],EAX + + ;Erase noise settings -------------------- + Mov [nRate],EAX + Mov [nAcc],EAX + Mov [nSmp],EAX + + ;Reset buffers --------------------------- + Mov EDI,startBuf + Mov ECX,(endBuf-startBuf) / 4 + Rep StoSD + + ;Echo region ----------------------------- + Mov [echoCur],EAX ;Reset echo variables + Mov dword [echoDel],8 ;Delay 1 sample + Mov [echoFB],EAX + Mov [4+echoFB],EAX + Mov [echoFBCT],EAX + Mov [4+echoFBCT],EAX + + ;Echo filter ----------------------------- + Mov EDI,firTaps ;Reset filter coefficients + Mov CL,2*8 + Rep StoSD + + Mov [firCur],EAX ;Reset filter variables + Mov [firDec],EAX + Mov [firEnabl],AL + + ;Reset low-pass filter ------------------- + Mov dword [lowCur],lowBuf + Mov [lowDec],EAX + Mov [lowRFI],EAX + Mov [4+lowRFI],EAX + + ;Disable voices -------------------------- + Mov [voiceMix],AL + + ;Reset times ----------------------------- + Mov [outLeft],EAX + Mov [outCnt],EAX + Mov [outDec],EAX + Mov [konLate],AL + Mov dword [songLen],-1 + Mov dword [fadeLen],1 + + ;Reset AAR ------------------------------- + Mov [mMaxL],EAX + Mov [mMaxR],EAX + + Mov [aarCnt],EAX + Mov [aarMMaxL],EAX + Mov [aarMMaxR],EAX + + ;Reset fade volume ----------------------- + Call SetDSPVol,10000h + + And byte [dbgOpt],~DSP_HALT + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Calculate Anti-Aliasing Filter Coefficients +; +;Adapted from code in the SoundTouch sound processing library. +;If aaCutoff is greater than the Nyquist frequency, the center tap is 1 with the remaining taps at 0 +;Taps are written out as integers. Use aaScale to adjust the taps for fixed point. +; +;In: +; aaBuf -> Buffer to store taps in +; aaNum = Number of taps to generate +; aaRate = Sample rate +; aaCutoff = Cutoff frequency +; aaScale = Number to scale taps by (float) +; +;Destroys: +; EAX + +PROC AACoeffs, pBuf, num, rate, cutoff, scale +LOCALS cnt,fp0_46,fp0_54 +USES ECX,EDI + + Mov EAX,[%$cutoff] + Add EAX,EAX + Cmp EAX,[%$rate] + JAE .NoFilter + + Mov dword [%$fp0_46],3EEB851Fh + Mov dword [%$fp0_54],3F0A3D71h + + FLdZ ; |sum + FILd dword [%$cutoff] ;fc2 = 2.0 * Cutoff |sum cutoff + FIDiv dword [%$rate] ; |sum cutoff/rate + FAdd ST,ST ; |sum ratio*2.0 + + FLdPi ;wc = Pi * fc2 |sum fc2 Pi + FMul ST,ST1 ; |sum fc2 Pi*fc2 + + FLdPi ;co = 2*PI / NumTaps |sum fc2 wc Pi + FAdd ST,ST ; |sum fc2 wc 2*Pi + FIDiv dword [%$num] ; |sum fc2 wc 2Pi/num + + Mov EAX,[%$num] + Mov ECX,EAX ;ECX = Tap index + ShR EAX,1 + Inc EAX + Mov [%$cnt],EAX + + Mov EDI,[%$pBuf] + .Next: + Dec dword [%$cnt] + FILd dword [%$cnt] ;cnt = i - NumTaps / 2 |sum fc2 wc co cnt + FLd1 ;h = 1.0 |sum fc2 wc co cnt 1.0 + JZ short .Zero ;if (cnt != 0) |sum fc2 wc co cnt 1.0 + FStP ST ; |sum fc2 wc co cnt + FLd ST ;temp = cnt * wc |sum fc2 wc co cnt cnt + FMul ST,ST3 ; |sum fc2 wc co cnt cnt*wc + + FLd ST ;sin(temp) * fc2 / temp |sum fc2 wc co cnt temp temp + FSin ; |sum fc2 wc co cnt temp sin(temp) + FMul ST,ST5 ; |sum fc2 wc co cnt temp sin*fc2 + FDivRP ST1,ST ; |sum fc2 wc co cnt sin/temp + .Zero: + + Dec ECX + + FXch ST1 ;Hamming window |sum fc2 wc co h cnt + FMul ST,ST2 ;0.54+0.46*cos(co*cnt) |sum fc2 wc co h cnt*co + FCos ; |sum fc2 wc co h cos(cnt) + FMul dword [%$fp0_46] ; |sum fc2 wc co h w*0.46 + FAdd dword [%$fp0_54] ; |sum fc2 wc co h w+0.54 + + FMulP ST1,ST ;tap = w * h; |sum fc2 wc co h*w + + FSt dword [ECX*4+EDI] + + FAddP ST4,ST ;sum += temp |sum+tap fc2 wc co + + JNZ short .Next + + FStP ST ; |sum fc2 wc + FStP ST ; |sum fc2 + FStP ST ; |sum + + ;Normalize coefficients ------------------ + FLd1 + FDivRP ST1,ST + FMul dword [%$scale] + Mov ECX,[%$num] + .Norm: + Dec ECX + FLd dword [ECX*4+EDI] + FMul ST1 + FIStP dword [ECX*4+EDI] + JNZ short .Norm + + FStP ST ; |(empty) + RetS + + .NoFilter: + XOr EAX,EAX + Mov EDI,[%$pBuf] + Mov ECX,[%$num] + Rep StoSD + + Mov EDI,[%$pBuf] + Mov ECX,[%$num] + ShR ECX,1 + FLd dword [%$scale] + FIStP dword [ECX*4+EDI] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set DSP Options + +PROC SetDSPOpt, mixType, chn, bits, rate, inter, opts +LOCALS fixVol,eraseBuf,ftemp +USES ALL + + Mov byte [%$fixVol],0 + Mov byte [%$eraseBuf],0 + + ;========================================= + ;Verify parameters + + ;mixType --------------------------------- + MovZX EAX,byte [dspMix] + Mov EDX,[%$mixType] + Cmp EDX,-1 + JE short .DefMix + + Mov AL,3 + Cmp EDX,MIX_FLOAT + JAE short .DefMix + + Test byte [procType],CPU_MMX + SetNZ AL + Inc AL + Cmp EDX,MIX_INT + JAE short .DefMix + + XOr EAX,EAX + + .DefMix: + Mov [%$mixType],EAX + + ;numChn ---------------------------------- + MovZX EAX,byte [dspChn] + Mov EDX,[%$chn] + Cmp EDX,-1 + JE short .DefChn + + Mov EAX,EDX + + Cmp EDX,1 + JE short .DefChn + Cmp EDX,2 + JE short .DefChn + Cmp EDX,4 + JE short .DefChn + + Mov EAX,2 + + .DefChn: + Mov [%$chn],EAX + + ;bits ------------------------------------ + MovZX EAX,byte [dspSize] + Mov EDX,[%$bits] + Cmp EDX,-1 + JE short .DefBits + + Mov EAX,EDX + SAR EAX,3 + + Cmp EDX,8 + JE short .DefBits + Cmp EDX,16 + JE short .DefBits + Cmp EDX,24 + JE short .DefBits + Cmp EDX,32 + JE short .DefBits + Cmp EDX,-32 + JE short .DefBits + + Mov EAX,2 + + .DefBits: + Mov [%$bits],EAX + + ;rate ------------------------------------ + Mov EAX,[dspRate] + Test byte [dspOpts],OPT_ANALOG + JZ short .NoLow1 + Mov EAX,[realRate] + .NoLow1: + + Mov EDX,[%$rate] + Cmp EDX,-1 + JE short .DefRate + + Mov EAX,EDX + XOr ECX,ECX + + Cmp EDX,8000 + SetB CL + Cmp EDX,192000 + SetA CH + Test ECX,ECX + JZ short .DefRate + + Mov EAX,32000 + + .DefRate: + Mov [%$rate],EAX + + ;inter ----------------------------------- + MovZX EAX,byte [dspInter] + Mov EDX,[%$inter] + Cmp EDX,-1 + JE short .DefInter + + Mov EAX,EDX + Cmp EDX,4 + JBE .DefInter + + Mov EAX,3 + + .DefInter: + Mov [%$inter],EAX + + ;opts ------------------------------------ + MovZX EAX,byte [dspOpts] + Mov EDX,[%$opts] + Cmp EDX,-1 + JE short .DefOpts + + Mov EAX,EDX + + .DefOpts: + Mov [%$opts],EAX + + ;Verify mix type is capable of producing requested output + Cmp byte [%$mixType],0 ;MIX_NONE is unaffected by output settings + JZ .MixOK + + Cmp byte [%$bits],2 ;ifn (bits >= 16-bit && numChn >= stereo && + SetAE AL ; mixType >= MMX) + Cmp byte [%$chn],2 + SetAE AH + And AL,AH + Cmp byte [%$mixType],2 + SetAE AH + And AL,AH + JNZ short .MixOK + + Mov byte [%$mixType],1 ;Force mix type to 386 + + Cmp byte [%$bits],1 ;if (bits == 8-bit) mixType = 386 + JE short .MixOK + + Cmp byte [%$chn],1 ;if (numChn == mono) mixType = 386; bits = 16-bit + JA short .Stereo + Mov byte [%$bits],2 + Jmp short .MixOK + .Stereo: + + Cmp byte [%$bits],2 ;if (bits != 16-bit) mixType = float + JE short .MixOK + Mov byte [%$mixType],3 + + .MixOK: + + + ;========================================= + ;Options + + Mov DL,[%$opts] + + ;Select ADPCM routine -------------------- + Mov dword [pDecomp],UnpackBRROld + Test DL,OPT_OLDSMP + JNZ short .OldSmp + Mov dword [pDecomp],UnpackBRR + .OldSmp: + + ;Pseudo surround sound ------------------- + Mov AL,[surround] + XOr AL,DL + And AL,OPT_SURND + Or [%$fixVol],AL ;Did surround sound flag change? + + Test DL,OPT_SURND + SetZ AL + Dec AL + Mov [surround],AL + + ;Reverse stereo -------------------------- + Mov AL,[dspOpts] + XOr AL,DL + And AL,OPT_REVERSE + Or [%$fixVol],AL ;Did reverse flag change? + + ;Disable echo ---------------------------- + Test DL,OPT_NOECHO + SetZ AL + Dec AL + And AL,OPT_NOECHO + And byte [disEcho],~OPT_NOECHO + Or [disEcho],AL + + Mov [dspOpts],DL ;Save option flags + + + ;========================================= + ;Interpolation method + + MovZX EAX,byte [%$inter] ;Save interpolation type + Mov [dspInter],AL + + MovZX EDX,byte [%$mixType] ;if (mixType != MIX_NONE) + Test EDX,EDX + JZ short .NoMix + + Dec EDX + LEA EDX,[EDX*5] + Add EDX,EAX + Mov EAX,[EDX*4+intRout] + Mov [pInter],EAX + + .NoMix: + + + ;========================================= + ;Calculate sample rate change + + Mov EAX,[%$rate] + Mov [realRate],EAX + + Test byte [dspOpts],OPT_ANALOG + JZ short .NoLow + And byte [dspOpts],~OPT_ANALOG ;Clear flag, incase filter can't be implemented + + Cmp byte [%$mixType],2 ;if (mixType == MMX && rate >= 32000 && + JNE short .NoLow ; (opts & OPT_LOW)) + Cmp dword [%$rate],32000 + JB short .NoLow + XOr EDX,EDX + Mov EAX,32000 << 16 + Mov [lowDec],EDX + Div dword [%$rate] + Mov [lowRate],EAX + + Mov dword [%$rate],32000 + Or byte [dspOpts],OPT_ANALOG + + Call AACoeffs,lowTaps,LOWTAPS,[realRate],13000,4F000000h ;2^31 + .NoLow: + + Mov EAX,[%$rate] + Cmp EAX,[dspRate] ;Has sample rate changed? + JE .SameRate ; No + Mov [dspRate],EAX ; Yes, Adjust a lot of items + + ;Calculate amount to adjust DSP pitch values + XOr EDX,EDX ;EDX:EAX = Base pitch << 20 + Mov EAX,[pitchBas] + ShLD EDX,EAX,20 + ShL EAX,20 + + Div dword [dspRate] + Mov [pitchAdj],EAX + + ;Calculate update rate for envelopes and noise + Mov ESI,freqTab + Mov EDI,rateTab + Mov EBX,32000 + + Mov ECX,31 + .CalcRT: + Mov AX,[ECX*2+ESI] + ShL EAX,16 + Mul dword [dspRate] + Div EBX + + Cmp EAX,10000h + JAE short .RTOK + Mov EAX,10000h + .RTOK: + + Mov [ECX*4+EDI],EAX + + Dec ECX + JNZ short .CalcRT + Mov [EDI],ECX + + ;Volume ramping rate ------------------ + Mov dword [%$ftemp],32000 + FILd dword [%$ftemp] + FIDiv dword [dspRate] + Mov dword [%$ftemp],3A000000h ;>> 11 + FMul dword [%$ftemp] + FSt dword [volRamp] + FChS + FStP dword [4+volRamp] + + ;Reset FIR info ----------------------- + XOr EAX,EAX + Mov [firDec],EAX + Mov [firCur],EAX + Mov [lowDec],EAX + + Mov EAX,[dspRate] + MovZX EDX,word [2+dspRate] + ShL EAX,16 + Mov ECX,32000 + Div ECX + Mov [firRate],EAX ;firRate = (dspRate<<16) / 32kHz + + ;Adjust voice rates ------------------- + Mov EDX,1 + XOr EAX,EAX + Div dword [%$rate] + Mov ECX,EAX + + Mov EAX,[%$rate] ;Set voice cutoff to 3/4ths the Nyquist frequency or + LEA EAX,[EAX*3] ; 18kHz, whichever is lower + ShR EAX,3 + Cmp EAX,18000 + JB short .COffOK + Mov EAX,18000 + .COffOK: + Mul ECX + ShRD EAX,EDX,16 + Mov [vaaCut],EAX + + Mov ESI,7*16 ;Adjust the current rates in each voice incase the + Mov EBX,7*80h ; sample rate is being changed during emulation + .Voice: + MovZX EAX,word [ESI+dsp+pitch] ;Set pitch + And AH,3Fh + Mul dword [pitchAdj] + ShRD EAX,EDX,16 + Mov [EBX+mix+mRate],EAX + + MovZX EDI,byte [EBX+mix+eRIdx] ;Set envelope adjustment + Mov EAX,[EDI*4+rateTab] + Mov [EBX+mix+eRate],EAX + Mov [EBX+mix+eCnt],EAX + + Sub ESI,16 + Add EBX,-80h + JNS short .Voice + + ;Adjust echo delay -------------------- + MovZX EAX,byte [dsp+edl] + ShL AL,4 + Mul dword [dspRate] + Mov ECX,1000 + Div ECX + Test EAX,EAX + SetZ CL + Or AL,CL + ShL EAX,3 + Mov [echoDel],EAX + + Mov byte [%$eraseBuf],1 + .SameRate: + + + ;========================================= + ;Set sample size + + Mov AL,[%$bits] + Cmp AL,[dspSize] ;If the sample size has changed, CL = 1 + JE short .SameBits + Mov [dspSize],AL + .SameBits: + + + ;========================================= + ;Set number of channels + + Mov AL,[%$chn] + Cmp AL,[dspChn] ;If the number of channels has changed, CL = 1 + SetNE CL + Or [%$fixVol],CL + Mov [dspChn],AL + + + ;========================================= + ;Update areas affected by the mix type + + Mov AL,[%$mixType] + Cmp AL,[dspMix] + JE .SameMix + + Mov [dspMix],AL + + Mov byte [%$fixVol],1 ;Force volumes to be recalculated + Mov byte [%$eraseBuf],1 + .SameMix: + + + ;========================================= + ;Erase sample buffers + + Test byte [%$eraseBuf],-1 + JZ short .NoErase + + XOr EAX,EAX + + Mov EDI,echoBuf + Mov ECX,ECHOBUF + Rep StoSD + + Mov EDI,firBuf + Mov ECX,FIRBUF + Rep StoSD + + Mov EDI,lowBuf + Mov ECX,LOWBUF + Rep StoSD + + Mov [aarMMaxL],EAX + Mov [aarMMaxR],EAX + Mov [mMaxL],EAX + Mov [mMaxR],EAX + .NoErase: + + + ;========================================= + ;Fixup volume handlers + + Test byte [%$fixVol],-1 + JZ .Done + + ;Setup DSP register jump table -------- + Mov ESI,dspRegsF ;ESI -> floating point register handlers + Cmp byte [dspMix],3 + JE .SetRegs + Mov ESI,dspRegsM ;ESI -> mono integer register handlers + Cmp byte [dspChn],1 + JE .SetRegs + Mov ESI,dspRegsI ;ESI -> stereo integer register handlers + + .SetRegs: + Mov EDI,regTab + + Mov EAX,[ESI] ;EAX -> VOLL handler + Mov EDX,[4+ESI] ;EDX -> VOLR handler + Mov ECX,[28+ESI] ;ECX -> FC handler + + Test byte [dspOpts],OPT_REVERSE ;If reversed, swap pointers to left and right volume + JZ short .NoRev1 + XChg EAX,EDX + .NoRev1: + + Mov EBX,70h + .CopyVol: + Mov [volL*4+EBX*4+EDI],EAX + Mov [volR*4+EBX*4+EDI],EDX + Mov [fc*4+EBX*4+EDI],ECX + Sub BL,10h + JNS short .CopyVol + + Mov EAX,[8+ESI] ;EAX -> MVOLL handler + Mov EDX,[12+ESI] ;EDX -> MVOLR handler + Mov EBX,[16+ESI] ;EBX -> EVOLL handler + Mov ECX,[20+ESI] ;ECX -> EVOLR handler + + Test byte [dspOpts],OPT_REVERSE + JZ short .NoRev2 + XChg EAX,EDX + XChg EBX,ECX + .NoRev2: + + Mov [mvolL*4+EDI],EAX + Mov [mvolR*4+EDI],EDX + Mov [evolL*4+EDI],EBX + Mov [evolR*4+EDI],ECX + + Mov EAX,[24+ESI] ;EAX -> EFB handler + Mov [efb*4+EDI],EAX + + ;Reinitialize registers --------------- + Mov ECX,70h + .NextVoice: + LEA EBX,[ECX+volL] + Call InitReg + + LEA EBX,[ECX+volR] + Call InitReg + + Mov EDX,mix + Mov EAX,[ECX*8+EDX+mTgtL] + Mov EBX,[ECX*8+EDX+mTgtR] + Mov [ECX*8+EDX+mChnL],EAX + Mov [ECX*8+EDX+mChnR],EBX + + LEA EBX,[ECX+fc] + Call InitReg + + Sub CL,10h + JNC short .NextVoice + + Mov BL,efb + Call InitReg + + .Done: + + Mov EAX,[volAmp] ;Fixup after any changes that could affect main volumes + Call SetDSPAmpB + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Debug DSP + +PROC SetDSPDbg, pTraceFunc, opts +USES EDX + + Mov AL,[%$opts] + Cmp AL,-1 + JE short .NoOpts + And AL,DSP_HALT + Mov [dbgOpt],AL + .NoOpts: + + Mov EAX,[pTrace] + Mov EDX,[%$pTraceFunc] + Cmp EDX,-1 + JE short .NoFunc + Mov [pTrace],EDX + .NoFunc: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Fix DSP After Loading Saved State + +PROC FixDSPLoad +USES ALL + + ;Enable voices currently keyed on -------- + Mov byte [voiceMix],0 + + Mov BL,kon + Call InitReg + + ;Setup global paramaters ----------------- + Mov BL,mvolL + Call InitReg + + Mov BL,mvolR + Call InitReg + + Mov BL,evolL + Call InitReg + + Mov BL,evolR + Call InitReg + + Mov BL,flg + Call InitReg + + Mov BL,efb + Call InitReg + + Mov BL,edl + Call InitReg + + Mov ECX,70h + .NextTap: + LEA EBX,[ECX+fc] + Call InitReg + Sub CL,10h + JNC short .NextTap + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Fix DSP After Seeking + +PROC FixDSPSeek, reset +USES ECX,EDI + + Mov AL,[%$reset] + Test AL,AL + JZ .NoReset + ;Turn off all voices ------------------ + Mov byte [dsp+endx],-1 ;Mark all playing voices as ended + XOr EAX,EAX + Mov [dsp+kon],AL ;Reset key registers + Mov [dsp+kof],AL + Mov [voiceMix],AL + + Mov CL,8 + Mov EDI,mix + .ResetMix: + Mov [EDI+eVal],EAX + Mov [EDI+mOut],EAX + And byte [EDI+mFlg],MFLG_MUTE + Or byte [EDI+mFlg],MFLG_OFF + Sub EDI,-80h + Dec CL + JNZ short .ResetMix + + Mov CL,8 + Mov EDI,dsp + .ResetDSP: + Mov [EDI+envx],AL + Mov [EDI+outx],AL + Add EDI,10h + Dec CL + JNZ short .ResetDSP + .NoReset: + + ;Erase buffers --------------------------- + XOr EAX,EAX + Mov EDI,startBuf + Mov ECX,(endBuf-startBuf) / 4 + Rep StoSD + + Call SetFade + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Save DSP's Current State of Operation + +PROC SaveDSP, pState +USES ALL + + Mov EBX,[%$pState] + + ;DSP Registers --------------------------- + Mov EDI,[EBX+DSPState.pReg] + Test EDI,EDI + JZ .NoRegs + Mov ESI,dsp + Mov ECX,128/4 + Rep MovSD + .NoRegs: + + ;Internal Voice Structures --------------- + Mov EDI,[EBX+DSPState.pVoice] + Test EDI,EDI + JZ .NoMix + Mov ESI,mix ;Copy structure array + Mov ECX,8*128/4 + Rep MovSD + + Mov ESI,[EBX+DSPState.pReg] + Sub ESI,dsp + Mov EDI,[EBX+DSPState.pVoice] + Mov ECX,80h*7 + .NextVoice: + Add [ECX+EDI+pDSPV],ESI ;Point pDSPV to the correct place in memory + + Mov EAX,[pAPURAM] + Sub [ECX+EDI+bCur],EAX + + MovZX EAX,byte [ECX+EDI+eRIdx] ;Adjust rate to 32kHz + Mov AX,[EAX*2+freqTab] + ShL EAX,16 + Mov [ECX+EDI+eRate],EAX + + Mov EAX,32000 ;Adjust sample counter to 32kHz + Mul dword [ECX+EDI+eCnt] + Div dword [dspRate] + Mov [ECX+EDI+eCnt],EAX + + Mov EAX,EDI ;Point sIdx to the correct place + Sub EAX,mix + Add [ECX+EDI+sIdx],EAX + + Add ECX,-80h + JNS .NextVoice + .NoMix: + + ;Echo region ----------------------------- + Mov EDI,[EBX+DSPState.pEcho] + Test EDI,EDI + JZ .NoEcho + LEA ESI,[EAX+echoBuf] ;Copy from the current echo position to the end of the + Mov ECX,[echoDel] ; buffer + Sub ECX,[echoCur] + ShR ECX,2 + Rep MovSD + + Mov ESI,echoBuf ;Copy remaing buffer + Mov ECX,[echoCur] + ShR ECX,2 + Rep MovSD + .NoEcho: + + ;Volume ---------------------------------- + Mov AL,[aarType] + ShL EAX,28 + Or EAX,[volAmp] + Mov [EBX+DSPState.amp],EAX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Restore DSP's State of Operation + +PROC RestoreDSP, pState +USES ALL + + Mov EBX,[%$pState] + + ;DSP Registers --------------------------- + Mov ESI,[EBX+DSPState.pReg] + Test ESI,ESI + JZ .NoRegs + Mov EDI,dsp + Mov ECX,128/4 + Rep MovSD + .NoRegs: + + ;Internal Voice Structures --------------- + Mov ESI,[EBX+DSPState.pVoice] + Test ESI,ESI + JZ .NoMix + Mov EDI,mix ;Copy structure array + Mov ECX,8*128/4 + Rep MovSD + + Mov EDI,mix + Mov EAX,dsp + Mov CL,8 + .FixDSPV: + Mov [EDI+pDSPV],EAX + Sub EDI,-80h + Add EAX,10h + Dec CL + JNZ short .FixDSPV + + Mov byte [voiceMix],0 + Mov EDI,mix + XOr ECX,ECX + .ChkVoice: + Test byte [EDI+mFlg],MFLG_OFF + JNZ .Inactive + + ShR ECX,7 + BTS [voiceMix],ECX + ShL ECX,7 + + Mov EAX,[pAPURAM] + Add [EDI+bCur],EAX + + Mov EAX,[EDI+bCur] + Mov AL,[EAX] + Mov [EDI+bHdr],AL + + ;Envelope -------------------------- + Mov ESI,32000 + Mov EAX,[EDI+eCnt] + Mul dword [dspRate] + Div ESI + Mov [EDI+eCnt],EAX + + Cmp dword [EDI+eRate],-1 + JNE short .KeepEnv + Push EBX + Mov EBX,ECX + Call ChgEnv + Pop EBX + .KeepEnv: + + MovZX EAX,byte [EDI+eRIdx] + Mov EAX,[EAX*4+rateTab] + Mov [EDI+eRate],EAX + + ;Sound source ---------------------- + Mov EAX,EDI + Sub EAX,ECX + Sub EAX,[EBX+DSPState.pVoice] + Add [EDI+sIdx],EAX + + ;Mixing ---------------------------- + Cmp dword [EDI+mRate],-1 + JNE short .KeepMix + Push EBX + + LEA EBX,[EDI+pitch] + Call InitReg + Mov dword [EDI+mDec],0 + + LEA EBX,[EDI+volL] + Call InitReg + + LEA EBX,[EDI+volR] + Call InitReg + + Pop EBX + .KeepMix: + + .Inactive: + Add EDI,80h + + Sub ECX,-80h + Cmp CH,4 + JB .ChkVoice + + .NoMix: + + ;========================================= + ;Restore Global Settings + + ;Volume ---------------------------------- + Mov EAX,[EBX+DSPState.amp] + And EAX,0FFFFFFFh + Call SetDSPAmp, EAX ;SetDSPAmp will reset main and echo volumes + + Mov AL,[3+EBX+DSPState.amp] + ShR AL,4 + Mov [aarType],AL + + Call GetSPCTime + XOr EDX,EDX + Mov ECX,8000 + Div ECX + IMul EAX,ECX + Mov [aarCnt],EAX + + ;DSP registers --------------------------- + Mov BL,flg + Call InitReg + + Mov BL,efb + Call InitReg + + Mov BL,edl + Call InitReg + + Mov ECX,70h + .NextTap: + LEA EBX,[ECX+fc] + Call InitReg + Sub ECX,10h + JNC short .NextTap + + ;Variables ------------------------------- + XOr EAX,EAX + Mov [konLate],AL + Mov [mMaxL],EAX + Mov [mMaxR],EAX + Mov [aarMMaxL],EAX + Mov [aarMMaxR],EAX + + Mov dword [lowCur],lowBuf + Mov [lowDec],EAX + + Mov dword [songLen],-1 + Mov dword [fadeLen],1 + + ;========================================= + ;Reset Buffers + + ;Echo region ----------------------------- + Mov [echoCur],EAX + + Mov EBX,[%$pState] + Mov ESI,[EBX+DSPState.pEcho] + Mov EDI,echoBuf + Test ESI,ESI + JZ .NoEcho + Mov ECX,[echoDel] + ShR ECX,4 + Rep MovSD + Jmp .HasEcho + .NoEcho: + XOr EAX,EAX + Mov ECX,ECHOBUF + Rep StoSD + .HasEcho: + + ;Restore FIR ring buffer ----------------- + Mov EDI,firBuf + Mov ESI,echoBuf + Mov ECX,FIRBUF + Rep MovSD + Mov ESI,echoBuf + Mov ECX,FIRBUF + Rep MovSD + Mov dword [firCur],0 + + ;Erase low-pass filter buffer ------------ + XOr EAX,EAX + Mov EDI,lowBuf + Mov ECX,LOWBUF + Rep StoSD + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Amplification Level + +PROC SetDSPAmpB +USES ECX,EDX,EBX + + ;Multiply by volume ---------------------- + Test byte [dspOpts],OPT_ANALOG + JZ short .NoAmp + ShR EAX,8 + Neg EAX + Mov [lowAmp],EAX + + Mov EAX,10000h + .NoAmp: + + Mul dword [volAtt] + ShRD EAX,EDX,16 + Mov [volAdj],EAX + + ;Update global volumes ------------------- + Mov BL,mvolL + Call InitReg + + Mov BL,mvolR + Call InitReg + + Mov BL,evolL + Call InitReg + + Mov BL,evolR + Call InitReg + +ENDP + +PROC SetDSPAmp, amp + + And byte [aarType],~AAR_ON ;Disable AAR + + Mov EAX,[%$amp] + CDQ ;if amp<0 amp=0 + Not EDX + And EAX,EDX + Mov [volAmp],EAX + + Call SetDSPAmpB + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get Amplification Level + +PROC GetDSPAmp + + Mov EAX,[volAmp] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Automatic Amplification Reduction + +PROC SetDSPAAR, type, thresh, min, max +USES ESI + + Mov ESI,aar + + Cmp dword [%$max],-1 + JE short .NoMax + Mov EAX,[%$max] + Mov [_AAR(Max)],EAX + .NoMax: + + Cmp dword [%$min],-1 + JE short .NoMin + Mov EAX,[%$min] + Mov [_AAR(Min)],EAX + .NoMin: + + Cmp dword [%$thresh],-1 + JE short .NoThresh + Mov EAX,[%$thresh] + Mov [_AAR(Thrsh)],EAX + .NoThresh: + + Cmp byte [%$type],-1 + JE short .NoType + Mov AL,[%$type] + And AL,AAR_TYPE + Mov [_AAR(Type)],AL + .NoType: + + XOr EAX,EAX + Mov [_AAR(MMaxL)],EAX + Mov [_AAR(MMaxR)],EAX + Mov [mMaxL],EAX + Mov [mMaxR],EAX + + Mov AL,[_AAR(Type)] + And AL,AAR_TYPE + Retc Z + + Or byte [_AAR(Type)],AAR_ON + Cmp AL,AAR_TINC + Retc B + + Or byte [_AAR(Type)],AAR_INC + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Process Auto Amplification Reduction + +PROC ProcessAAR +USES ECX,EDX,ESI + + Mov ESI,aar + + ;Update max output for visualization ----- + Mov EAX,[mMaxL] + Sub EAX,[_AAR(MMaxL)] + CDQ + And EAX,EDX + Sub [mMaxL],EAX + + Mov EAX,[mMaxR] + Sub EAX,[_AAR(MMaxR)] + CDQ + And EAX,EDX + Sub [mMaxR],EAX + + Mov EAX,[_AAR(MMaxL)] ;ECX = Max(maxL, maxR) + Mov ECX,EAX + Sub EAX,[_AAR(MMaxR)] + CDQ + And EAX,EDX + Sub ECX,EAX + + XOr EAX,EAX + Mov [_AAR(MMaxL)],EAX + Mov [_AAR(MMaxR)],EAX + + ;Process AAR ----------------------------- + Test byte [_AAR(Type)],AAR_ON + JZ .Done + + Mov EAX,[_AAR(Thrsh)] + Cmp ECX,EAX ;Has volume breached threshold? + JBE short .Increase ; Nope, Check for increase + + Mul dword [volAmp] ;EAX = Amp * (MMax / Threshold) + Div ECX + + Cmp EAX,[_AAR(Min)] ;if (EAX < Min) disable AAR + JA short .NotMin + Mov EAX,[_AAR(Min)] ;Clamp amp level + And byte [_AAR(Type)],~AAR_ON ;Disable AAR + .NotMin: + + And byte [_AAR(Type)],~AAR_INC ;Disable auto increase + Mov [volAmp],EAX + Call SetDSPAmpB ;Set new amplification + RetS + + .Increase: + Test byte [_AAR(Type)],AAR_INC ;Is auto increase enabled? + Retc Z + + Call GetSPCTime ;Has 1/8th of a second gone by? + Sub EAX,[_AAR(Cnt)] + Cmp EAX,8000 + Retc B + + Mov dword [ESP-4],3F817CBFh ;2^(1/60) (or 60th root of 2) + FILd dword [volAmp] ;Increase amp level by 0.1dB + FMul dword [ESP-4] + FIStP dword [volAmp] + Add [_AAR(Cnt)],EAX + + Mov EAX,[volAmp] ;Has amp reached max level? + Cmp EAX,[_AAR(Max)] + JB short .NotMax + Mov EAX,[_AAR(Max)] + And byte [_AAR(Type)],~AAR_INC + Mov [volAmp],EAX + .NotMax: + + Call SetDSPAmpB + + .Done: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set DSP Volume +; +;This value attenuates the output and was implemented to allow songs to be faded out. ResetDSP sets +;this value to 65536 (no attenuation). +; +;In: +; vol = Volume [-1.16] (0.0 to 1.0, negative values act as 0) +; +;Destroys: +; EAX + +PROC SetDSPVol, vol +USES ECX,EDX,EBX + + Mov EAX,[%$vol] ;if EAX<0 EAX=0 + CDQ + Not EDX + And EAX,EDX + Mov [volAtt],EAX + + Test byte [dspOpts],OPT_ANALOG + JNZ short .NoAmp + Mul dword [volAmp] + ShRD EAX,EDX,16 + .NoAmp: + + Mov [volAdj],EAX + + ;Update global volumes ------------------- + Mov BL,mvolL + Call InitReg + + Mov BL,mvolR + Call InitReg + + Mov BL,evolL + Call InitReg + + Mov BL,evolR + Call InitReg + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Fade Volume +; +;Calls SetDSPVol to fade the song out based on t64Cnt, songLen, and fadeLen. + +PROC SetFade +USES EDX + + Call GetSPCTime ;EDX = T64Cnt - songLen; + Mov EDX,EAX + Sub EDX,[songLen] + JBE .Done + + XOr EAX,EAX ;if (EAX > fadeLen) EAX = fadeLen; + Cmp EDX,[fadeLen] + SetA AL + Dec EAX + And EDX,EAX + Not EAX + And EAX,[fadeLen] + Or EDX,EAX + + XOr EAX,EAX ;EDX = 65536 - ((EDX << 16) / fadeLen); + ShRD EAX,EDX,16 + ShR EDX,16 + Div dword [fadeLen] + Mov EDX,65536 + Sub EDX,EAX + + Call SetDSPVol,EDX ;SetDSPVol(EDX); + + .Done: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Song Length + +PROC SetDSPLength, song, fade +USES EDX + + Mov EDX,[%$fade] + XOr EAX,EAX ;if (fadeLen == 0) fadeLen = 1; + Test EDX,EDX ;0 will cause a division error + SetZ AL + Or EDX,EAX + Mov [fadeLen],EDX + + Mov EAX,[%$song] + Add EDX,EAX + Mov [songLen],EAX + + Call GetSPCTime ;if (t64Cnt < songLen) + Cmp EAX,[%$song] + JAE short .SetFade + Call SetDSPVol,10000h + RetS EDX + + .SetFade: + Call SetFade ;If song is in fade mode, set fade volume + Mov EAX,EDX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Pitch Adjustment + +PROC SetDSPPitch, base +USES EDX,EBX,ESI + + ;Calculate amount to adjust DSP pitch values + XOr EDX,EDX + Mov EAX,[%$base] + Mov [pitchBas],EAX + ShLD EDX,EAX,20 + ShL EAX,20 + + Div dword [dspRate] + Mov [pitchAdj],EAX + + ;Adjust voice rates to new pitch --------- + Mov ESI,7*16 ;Adjust the current rates in each voice incase the + Mov EBX,7*80h ; sample rate is being changed during emulation + .Voice: + Mov EAX,[EBX+mix+pDSPV] + MovZX EAX,word [EAX+pitch] + And AH,3Fh + Mul dword [pitchAdj] + ShRD EAX,EDX,16 + Mov [EBX+mix+mRate],EAX + + Sub ESI,16 + Add EBX,-80h + JNS short .Voice + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Adjust Voice Volume for Stereo Separation +; +;A big nasty function to adjust the left and right channel volumes for stereo separation control +; +;In: +; EBX = Indexes current voice + +%if STEREO +PROC ChnSep +RVolL: +RVolR: +LOCALS fp128,fpShR7,fp0_5,ftemp +USES ECX,EBX + + Mov dword [%$fp128],43000000h + Mov dword [%$fpShR7],3C000000h + Mov dword [%$fp0_5],3F000000h + + ShR EBX,3 + Test byte [dspOpts],OPT_REVERSE + JNZ short .Reverse + MovSX EAX,byte [EBX+dsp+volL] + MovSX EDX,byte [EBX+dsp+volR] + Jmp short .Normal + .Reverse: + MovSX EAX,byte [EBX+dsp+volR] + MovSX EDX,byte [EBX+dsp+volL] + .Normal: + + LEA EBX,[EBX*8+mix] + Mov [EBX+mChnL],EAX ;Save the sign of each channel + Mov [EBX+mTgtL],EAX + Mov [EBX+mChnR],EDX + Mov [EBX+mTgtR],EDX + + Cmp EAX,EDX ;If both volumes are 0, an exception will be generated + JE .Done + + Mov ECX,[volSepar] ;Don't waste time if no separation is applied + Test ECX,ECX + JZ .Done + + SAR EAX,31 + SAR EDX,31 + + ;Convert left/right into vol/pan --------- + ; _____________ + ; | 2 2 + ; | L R + ; V = _ | --- + --- + ; \| 128 128 + ; + ; 2 + ; R + ; P = --- - 0+5 + ; V + ; + ; V is 0.0 to 1.414 (-inf to +3 dB) + ; P is -0.5 to 0.5 (left to right) + + FILd dword [EBX+mChnR] ;V = sqrt((L/128)^2+(R/128)^2) |chnR + FMul dword [%$fpShR7] ;Change vol to float |chnR/128=R + FLd ST ; |R R + FMul ST,ST ; |R R^2 + FILd dword [EBX+mChnL] ; |R R chnL + FMul dword [%$fpShR7] ; |R R chnL/128=L + FMul ST,ST ; |R R L^2 + FAddP ST1,ST ; |R R+L=V + FSqrt ; |R sqrt(V) + FXch ;P = (R/V)^2 - 0+5 |V R + FAbs ; |V |R| + FDiv ST,ST1 ; |V R/V + FMul ST,ST ; |V R^2 + FSub dword [%$fp0_5] ; |V R-0+5=P + + ;Adjust panning -------------------------- + ; S is -1.0 to 0 (pan toward center): + ; + ; P = P + (P * S) + ; + ; + ; S is 0 to 1.0 (pan toward outside): + ; + ; Left + ; P = P + ((0.5 + P) * S) + ; + ; Right + ; P = P + ((0.5 - P) * S) + + FLd ST ; |V P D + Test byte [3+volSepar],80h ;Is panning adjusted toward center? + JNZ short .ToCenter + FSt dword [%$ftemp] + FLd dword [%$fp0_5] ; |V P D 0.5 + Test byte [3+%$ftemp],80h ;Is pan on left side? + JZ short .Right + FChS ;Negate 0.5 |V P D -0.5 + .Right: + FSubRP ST1,ST ;Get distance from side |V P 0.5-D + .ToCenter: + FMul dword [volSepar] ;Get fraction of distance |V P D*volSepar + FAddP ST1,ST ;Add fraction to pan |V P+D + FLd ST ;Copy new panning value |V P P + + ;Convert vol/pan back into left/right ---- + ; _________ + ; L = V * \| 0.5 - P * 128 + ; _________ + ; R = V * \| 0.5 + P * 128 + + FAdd dword [%$fp0_5] ;R = V*sqrt(+5+P)*128 |V P P+0.5 + FSqrt ; |V P sqrt(P) + FMul ST,ST2 ; |V P P*V=R + FMul dword [%$fp128] ; |V P R*128 + FIStP dword [EBX+mChnR] ; |V P + XOr [EBX+mChnR],EDX ;Set the correct sign + Sub [EBX+mChnR],EDX + + FSubR dword [%$fp0_5] ;L = V*sqrt(+5-P)*128 |V 0.5-P + FSqrt ; |V sqrt(P) + FMulP ST1,ST ; |V*P=L + FMul dword [%$fp128] ; |L*128 + FIStP dword [EBX+mChnL] ; |(empty) + XOr [EBX+mChnL],EAX + Sub [EBX+mChnL],EAX + + Mov EDX,[EBX+mChnR] + Mov EAX,[EBX+mChnL] + Mov [EBX+mTgtR],EDX + Mov [EBX+mTgtL],EAX + +.Done: + XOr EAX,EAX + +ENDP + +;Channel separator for floating-point routines +PROC ChnSepF +RVolLF: +RVolRF: +LOCALS fpShR7,fp0_5,ftemp +USES ECX,EBX + + Mov dword [%$fpShR7],3C000000h + Mov dword [%$fp0_5],3F000000h + + ShR EBX,3 + Test byte [dspOpts],OPT_REVERSE + JNZ short .Reverse + MovSX EAX,byte [EBX+dsp+volL] + MovSX EDX,byte [EBX+dsp+volR] + Jmp short .Normal + .Reverse: + MovSX EAX,byte [EBX+dsp+volR] + MovSX EDX,byte [EBX+dsp+volL] + .Normal: + + LEA EBX,[EBX*8+mix] + Mov [EBX+mTgtL],EAX + Mov [EBX+mTgtR],EDX + + Cmp EAX,EDX + JE .NoSep + + Mov ECX,[volSepar] + Test ECX,ECX + JZ .NoSep + + And AL,80h ;Save sign bit of each volume + And DL,80h + ShL EAX,24 + ShL EDX,24 + + ;Convert left/right into vol/pan --------- + FILd dword [EBX+mTgtR] + FMul dword [%$fpShR7] + FLd ST + FMul ST,ST + FILd dword [EBX+mTgtL] + FMul dword [%$fpShR7] + FMul ST,ST + FAddP ST1,ST + FSqrt + FXch + FAbs + FDiv ST,ST1 + FMul ST,ST + FSub dword [%$fp0_5] + + ;Adjust panning -------------------------- + FLd ST + Test byte [3+volSepar],80h + JNZ short .ToCenterF + FSt dword [%$ftemp] + FLd dword [%$fp0_5] + Test byte [3+%$ftemp],80h + JZ short .RightF + FChS + .RightF: + FSubRP ST1,ST + .ToCenterF: + FMul dword [volSepar] + FAddP ST1,ST + FLd ST + + ;Convert vol/pan back into left/right ---- + FAdd dword [%$fp0_5] + FSqrt + FMul ST,ST2 + FStP dword [EBX+mTgtR] + Or [EBX+mTgtR],EDX + + FSubR dword [%$fp0_5] + FSqrt + FMulP ST1,ST + FStP dword [EBX+mTgtL] + Or [EBX+mTgtL],EAX + + XOr EAX,EAX + RetS + +.NoSep: + FILd dword [EBX+mTgtL] + FMul dword [%$fpShR7] + FStP dword [EBX+mTgtL] + + FILd dword [EBX+mTgtR] + FMul dword [%$fpShR7] + FStP dword [EBX+mTgtR] + + XOr EAX,EAX + +ENDP +%endif + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Convert Stereo Volume to Monaural +; +;Attenuates the volume so hard panned values will sound the same as they would be percieved in a +;stereo system. + +PROC GetMonoVol, left, right +USES ECX + + Mov EAX,[%$right] ;if (abs(mvLeft) == abs(mvRight)) return mvLeft; + CDQ + XOr EAX,EDX + Sub EAX,EDX + Mov ECX,EAX + + Mov EAX,[%$left] + CDQ + XOr EAX,EDX + Sub EAX,EDX + + Cmp EAX,ECX + Retc Z + + Mov dword [ESP-4],3F3504F3h ;2^-0.5 (or 1/sqrt(2)) + FILd dword [%$left] ; |left + FAbs ; ||left| + FLd dword [ESP-4] ; |left 2^-0.5 + FLd1 ; |left .707 1.0 + FIld dword [%$right] ; |left .707 1.0 right + FAbs ; |left .707 1.0 |right| + + ;if (abs(left) < abs(right)) swap values + JA short .NoSwap + FXCh ST,ST3 ; |right .707 1.0 left + .NoSwap: + + ;Get the percentage of difference of the volumes + ;diff = (vol - otherVol) / vol + FSubR ST,ST3 ; |vol .707 1.0 vol-other + FDiv ST,ST3 ; |vol .707 1.0 diff/vol + + ;Convert the linear percentage into a logarithmic value + ;Hard panned volumes get attenuated -3dB + ;vol = vol * (0.707 + (sqrt(1.0 - diff) * (1.0 - 0.707))) + FSubR ST,ST1 ; |vol .707 1.0 1.0-diff + FSqrt ; |vol .707 1.0 sqrt(diff) + FXCh ST1 ; |vol .707 sqrt 1.0 + FSub ST2 ; |vol .707 sqrt 1.0-0.707 + FMulP ST1,ST ; |vol .707 sqrt*.293 + FAddP ST1,ST ; |vol .707+att + FMulP ST1,ST ; |vol*att + + FIStP dword [%$left] ; |(empty) + Mov EAX,[%$left] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Stereo Separation + +PROC SetDSPStereo, sep +USES EDX,EBX + +%if STEREO + Sub dword [%$sep],32768 ;Convert fixed point unsigned value to signed float + FILd dword [%$sep] + FMul dword [fpShR15] + FStP dword [volSepar] + + ;Update each voice with new separation --- + Mov EBX,7*80h + Cmp byte [dspMix],3 + JE .Float + + .Voice: + Call ChnSep + Add EBX,-80h + JNS short .Voice + RetS + + .Float: + Call ChnSepF + Mov EAX,[EBX+mix+mTgtL] + Mov EDX,[EBX+mix+mTgtR] + Mov [EBX+mix+mChnL],EAX + Mov [EBX+mix+mChnR],EDX + Add EBX,-80h + JNS short .Float +%endif + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Echo Stereo Separation + +PROC SetDSPEFBCT, leak +USES EDX,EBX + + Mov EAX,[%$leak] + Add EAX,32768 ;Unsign crosstalk + Mov [efbct],EAX + + ;Update echo feedback -------------------- + Mov BL,efb + Call InitReg + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Voice Mute + +PROC SetDSPVoiceMute, voice, state +USES EDX + + Mov EDX,[%$voice] + MovZX EAX,byte [%$state] + + And EDX,7 + ShL EDX,7 + Add EDX,mix+mFlg + + Cmp AL,MUTE_OFF + JE short .Off + Cmp AL,MUTE_ON + JE short .On + Cmp AL,MUTE_SOLO + JE short .Solo + Cmp AL,MUTE_TOGGLE + JE short .Toggle + + Mov AL,MFLG_MUTE + And AL,[EDX] + RetN + + .Off: + And byte [EDX],~MFLG_MUTE + RetN + + .On: + Or byte [EDX],MFLG_MUTE + RetN + + .Solo: + Mov AL,MFLG_MUTE + Or [000h+mix+mFlg],AL + Or [080h+mix+mFlg],AL + Or [100h+mix+mFlg],AL + Or [180h+mix+mFlg],AL + Or [200h+mix+mFlg],AL + Or [280h+mix+mFlg],AL + Or [300h+mix+mFlg],AL + Or [380h+mix+mFlg],AL + Not AL + And [EDX],AL + Mov AL,0 + RetN + + .Toggle: + Mov AL,MFLG_MUTE + XOr [EDX],AL + And AL,[EDX] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Start Sound Source Decompression +; +;Called when a voice is keyed on to set up the internal data for waveform mixing and decompress the +;first block. +; +;In: +; EBX-> mix[voice] +; ESI-> dsp.voice[voice] +; +;Out: +; nothing +; +;Destroys: +; EAX,EDX + +PROC StartSrc +USES ESI,EDI,EBP + + MovZX EAX,byte [ESI+srcn] + Mov ESI,[pAPURAM] + ShL EAX,2 + Add AH,[dsp+dir] + Mov SI,[EAX+ESI] ;ESI -> First block of waveform + + LEA EDI,[EBX+sBuf] ;EDI -> Uncompressed sample buffer + Mov [EBX+bCur],ESI ;Save physical pointers to wave data + Mov [EBX+sIdx],EDI + + ;Decompress first block ------------------ + Mov AL,[ESI] + Push EBX + Mov [EBX+bHdr],AL ;Save block header + MovSX EDX,word [EBX+sP1] + MovSX EBX,word [EBX+sP2] + Call [pDecomp] + Mov EAX,EBX + Pop EBX + Mov [EBX+sP1],DX + Mov [EBX+sP2],AX + + ;Initialize interpolation ---------------- + XOr EAX,EAX + Mov [EBX+sBuf-16],EAX + Mov [EBX+sBuf-12],EAX + Mov [EBX+sBuf-8],EAX + Mov [EBX+sBuf-4],EAX + + Test byte [dspOpts],OPT_FILTER + JZ short .NoFilter + Mov EDI,EBX + Sub EDI,mix + ShR EDI,2 + Add EDI,vaaBuf + Mov [EDI],EAX + Mov [4+EDI],EAX + Mov [8+EDI],EAX + Mov [12+EDI],EAX + Mov [16+EDI],EAX + Mov [20+EDI],EAX + Mov [24+EDI],EAX + Mov [28+EDI],EAX + + Push ECX + Mov CL,12 + Sub byte [EBX+sIdx],2 + Call FilterVoice + Pop ECX + + Jmp short .NoInter + .NoFilter: + + Cmp byte [dspInter],2 ;Is interpolation enabled? + JB short .NoInter + Add byte [EBX+sIdx],6 ;Update sample index + .NoInter: + +ENDP + + +;In: +; EBX -> mix[voice] +; CL = Number of samples to increase +; +;Out: +; mix.sIdx = New index +; CL = Number of samples left to increase +; +;Destroys: +; EAX,EDX,ESI + +PROC FilterVoice +USES EDI,EBP + + Mov DL,[EBX+sIdx] ;AL indexes current sample + And DL,3Fh + Cmp DL,1Eh ;If we're at the end of the buffer, quit + Retc E + + ShL DL,2 + SAR DL,2 + Mov AL,30 + MovSX ESI,DL + Sub AL,DL + ShR AL,1 + + Sub CL,AL + JA short .ok + Add AL,CL + Mov CL,0 + .ok: + + Push ECX + Mov CL,AL + + Mov EDI,EBX + Sub EDI,mix + ShR EDI,2 + LEA EDX,[EDI*2+vaaTaps] ;EDX -> filter taps for voice + Add EDI,vaaBuf ;EDI -> buffer containing unfiltered samples + + .Sample: + Add ESI,2 ;Move to next sample + Mov AX,[ESI+EBX+sBuf] ;Get unfiltered sample and store it in the filter buffer + Mov [ESI+EDI],AX + + XOr EBP,EBP ;Push 16 samples through the filter. ESI will wrap around. + Mov CH,VAATAPS + .Next: + MovSX EAX,word [ESI+EDI] + Sub ESI,2 + IMul EAX,dword [EDX] + And ESI,1Fh + Add EDX,4 + Add EBP,EAX + Dec CH + JNZ .Next + + ShR EBP,16 + Add EBP,EBP + Sub EDX,4*VAATAPS + + Mov [ESI+EBX+sBuf],BP ;Store filtered sample + + Dec CL + JNZ .Sample + + LEA ESI,[ESI+EBX+sBuf] + Mov [EBX+sIdx],ESI + + Pop ECX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Start Envelope +; +;Called when a voice is keyed on to set up the internal data to begin envelope modification based on +;the values in ADSR/Gain. +; +;In: +; EBX-> mix[voice] +; ESI-> dsp.voice[voice] +; +;Out: +; mix.e??? = correct values for envelope routine in mixer +; dsp.voice.envx = 0 +; +;Destroys: +; EAX,EDX + +PROC StartEnv + + XOr EAX,EAX + Mov [EBX+eVal],EAX ;Envelope starts at 0 + Mov [ESI+envx],AL ;Reset envelope height + Mov byte [EBX+eMode],E_ATT << 4 ;If envelope gets switched out of gain mode, start ADSR + + Test byte [ESI+adsr1],80h ;Is the envelope in ADSR mode? + JZ ChgGain ; No, It's in gain mode + +ChgAtt: + Mov AL,byte [ESI+adsr1] + And EAX,0Fh + Add EAX,EAX ;Adjust EAX to index rateTab + Inc EAX + Mov [EBX+eRIdx],AL + Mov EDX,[EAX*4+rateTab] ;EDX = Rate of adjustment + Mov [EBX+eRate],EDX + Mov [EBX+eCnt],EDX + Mov byte [EBX+eMode],E_ATT ;Set envelope mode to attack + Cmp AL,1Fh ;Is there an attack? + JE .NoAtt + + Mov dword [EBX+eAdj],A_LINEAR ;Set adjustment rate to linear + Mov dword [EBX+eDest],D_ATTACK ;Set destination to 63/64ths + RetN ;Exit + + .NoAtt: + Mov dword [EBX+eAdj],A_NOATT ;Set adjustment rate to 1/2 + Mov dword [EBX+eDest],D_MAX ;Set destination to 1.0 + RetN + +ALIGN 16 +ChgDec: + Mov dword [EBX+eAdj],A_EXP ;Set adjustment rate to exponential + + MovZX EAX,byte [ESI+adsr2] + ShR EAX,5 + Inc EAX + IMul EAX,D_DECAY + Mov [EBX+eDest],EAX ;Set destination to AL/8 + + Mov AL,[ESI+adsr1] + And EAX,70h + ShR EAX,3 + Add EAX,10h ;Adjust AL to index rateTab + Mov [EBX+eRIdx],AL + Mov EDX,[EAX*4+rateTab] + + Mov [EBX+eRate],EDX ;Set rate of adjustment + Mov [EBX+eCnt],EDX + Mov byte [EBX+eMode],E_DECAY ;Set envelope mode to decay + RetN ;Exit + +ALIGN 16 +ChgSus: + Mov AL,[ESI+adsr2] + And EAX,1Fh + Mov [EBX+eRIdx],AL + Mov EDX,[EAX*4+rateTab] + SetZ AL + ShL AL,7 ;E_IDLE + Mov [EBX+eRate],EDX + Mov [EBX+eCnt],EDX + Mov dword [EBX+eDest],D_MIN ;Set destination to 0 + Or AL,E_SUST + Mov [EBX+eMode],AL ;Set envelope mode to sustain + RetN ;Exit + +ALIGN 16 +ChgGain: + .SetGain: + Mov AL,[ESI+gain] + Test AL,80h ;Is gain direct? + JNZ short .GainMode ; No, Program envelope + And EAX,7Fh ;Isolate direct value + ShL EAX,E_SHIFT ;Adjust value for internal precision + Mov [EBX+eVal],EAX + + Mov AL,[EBX+eMode] + And AL,70h + Or AL,E_DIRECT|E_IDLE + Mov [EBX+eMode],AL ;Set envelope mode to direct + RetN + +ALIGN 16 + .GainMode: + Mov DL,AL + And EAX,1Fh ;Is index zero? + Mov [EBX+eRIdx],AL + Mov EAX,[EAX*4+rateTab] + Mov [EBX+eRate],EAX ;Set rate of change + Mov [EBX+eCnt],EAX + SetZ AL + RoR AL,1 ;E_IDLE + + Mov AH,[EBX+eMode] ;Preserve ADSR mode + And AH,70h + Or AL,AH + + Test DL,60h ;Jump to the right mode + JZ .GainDec + Test DL,40h + JZ short .GainExp + Test DL,20h + JZ short .GainInc + + .GainBent: + Mov dword [EBX+eAdj],A_LINEAR + Mov dword [EBX+eDest],D_BENT + Or AL,E_BENT ;Set mode to bent line increase + Mov [EBX+eMode],AL + RetS + +ALIGN 16 + .GainInc: + Mov dword [EBX+eAdj],A_LINEAR + Mov dword [EBX+eDest],D_MAX + Or AL,E_INC ;Set mode to linear increase + Mov [EBX+eMode],AL + RetS + +ALIGN 16 + .GainExp: + Mov dword [EBX+eAdj],A_EXP + Mov dword [EBX+eDest],D_MIN + Or AL,E_EXP ;Set mode to exponential decrease + Mov [EBX+eMode],AL + RetS + +ALIGN 16 + .GainDec: + Mov dword [EBX+eAdj],A_LINEAR + Mov dword [EBX+eDest],D_MIN + Or AL,E_DEC ;Set mode to linear decrease + Mov [EBX+eMode],AL + + EnvDone: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Change Envelope +; +;Called when the ADSR or GAIN registers are written to while an envelope is in progress. Control is +;transfered to StartEnv where the envelope is updated. +; +;In: +; EBX = Voice << 4 +; +;Destroys: +; EAX,EDX,EBX + +PROC ChgEnv + + Push ESI ;ESI will get popped on return from StartEnv + LEA ESI,[EBX+dsp] + LEA EBX,[EBX*8+mix] + + Mov DL,[EBX+eMode] + And DL,0Fh + Push .Return + + Cmp DL,E_ATT ;If the envelope isn't in attack, decay, or sustain + JE ChgAtt ; mode, changes to the ADSR registers have no effect + Cmp DL,E_DECAY + JE ChgDec + Cmp DL,E_SUST + JE ChgSus + Test DL,E_ADSR + JZ ChgGain + Pop EAX + + .Return: + Pop ESI ;No changes were made, pop ESI and return + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Data Port + +;-------------------------------------------- +;External procedure for users of SNESAPU.DLL + +PROC SetDSPReg, reg, val +USES ECX,EDX,EBX + + MovZX EBX,byte [%$reg] + MovZX EAX,byte [%$val] + Mov CL,0 ;CL = Don't emulate DSP + Call SetDSPReg_B ;Process register write without calling debug function + + Mov EBX,[pAPURAM] ;Update DSP Data function register in APU RAM + Mov DL,[0F2h+EBX] + And EDX,7Fh + Mov DL,[EDX+dsp] + Mov [0F3h+EBX],DL + +ENDP + + +;-------------------------------------------- +;Internal procedure for initialzing DSP registers +; +;In: +; BL = Register +; +;Destroys: +; EAX,EDX,EBX + +PROC InitReg +USES ECX + + MovZX EBX,BL + Mov AL,[EBX+dsp] + Mov CL,0 ;CL = Don't emulate DSP + Call SetDSPReg_C ;Process register regardless of current register value + +ENDP + + +;-------------------------------------------- +;Procedure for writing to the DSP from the SPC700 +; +;Destroys: +; EAX,CL,EDX,EBX + +PROC SetDSPReg_A + +%if DEBUG + Mov EDX,[pTrace] + Test EDX,EDX + JZ short .NoDbg + + MovZX EAX,AL + Add EBX,dsp + + Push EAX ;Pass these as parameters + Push EBX + Call EDX + Pop EBX + Pop EAX + + MovZX EBX,BL + + .NoDbg: +%endif + +%if DSPINTEG + Mov CL,1 ;CL = Emulate DSP to catch up to current state +%endif + +SetDSPReg_B: + + Mov DL,BL ;Do nothing if DSP is suspended or if writes are to + Or DL,[dbgOpt] ; 80-FFh + JS short DSPQuit + + Cmp BL,kon + JE RKOn + Cmp BL,kof ;Check for registers that can have duplicate data + JE short RKOf ; written + Cmp BL,endx + JE short REndX + + Cmp AL,[EBX+dsp] ;Is the new data the same as the current data? + JZ short DSPQuit ; Yes, Don't bother updating + +SetDSPReg_C: + Mov EDX,EBX + + Mov AH,BL + And EBX,70h + Not AH + ShL EBX,3 ;EBX indexes mix (needed by some handlers) + And AH,MFLG_OFF ;AH = 08h if the register is in dsp.voice + + Test [EBX+mix+mFlg],AH ;Is the voice inactive? + JNZ short DSPDone ; Yes, Don't bother updating + +%if DSPINTEG + Test CL,CL ;If write was from SPC700, emulate DSP before + JZ short .NoOutput ; processing new register data + Call UpdateDSP + .NoOutput: +%endif + + Mov [EDX+dsp],AL ;Update DSP RAM + Jmp [EDX*4+regTab] + +DSPDone: + Mov [EDX+dsp],AL + +DSPQuit: + XOr EAX,EAX ;DSP state didn't change + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Register Handlers + +;============================================ +;End block decoded + +ALIGN 16 +REndX: +%if DSPINTEG + Test CL,CL ;If write was from SPC700, emulate DSP before + JZ short .NoOutput ; processing new register data + Call UpdateDSP + .NoOutput: +%endif + + XOr EAX,EAX + Or AL,[dsp+endx] + Mov [dsp+endx],AH ;Reset the ENDX register + SetNZ AL + Ret + +;============================================ +;Key On/Off + +ALIGN 16 +RKOf: +%if DSPINTEG + Test CL,CL ;If write was from SPC700, emulate DSP before + JZ short .NoOutput ; processing new register data + Call UpdateDSP + .NoOutput: +%endif + + XOr EDX,EDX + Mov [dsp+kof],AL + And AL,[voiceMix] ;Only check voices that are currently playing + JZ .Done + + Mov EDX,[31*4+rateTab] + Mov EBX,mix + Jmp short .Start + + .KeyOff: + Mov byte [EBX+eRIdx],31 ;Place envelope in release mode + Mov dword [EBX+eRate],EDX + Mov dword [EBX+eCnt],EDX + Mov dword [EBX+eAdj],A_REL + Mov dword [EBX+eDest],D_MIN + Mov byte [EBX+eMode],E_REL + Or byte [EBX+mFlg],MFLG_KOFF ;Flag voice as keying off + + .NoKOff: + Sub EBX,-80h + + .Start: + ShR AL,1 + JC short .KeyOff + JNZ short .NoKOff + + XOr EDX,EDX ;Mark DSP state as having changed + Inc EDX + +.Done: + ;Problem: + ;The DSP processes the KON register before the KOF register. Because of this, voices that have + ;their key off flag set can't be keyed on. However, some games (like Super Turrican 2) write to + ;the KON register then immediately write to the KOF register. This works on the SNES because the + ;DSP processes the key registers every other sample frame, I think, and the two writes happen + ;within that time frame. + ; + ;Here in the emulation code I process the registers as soon as they're written to, and so games + ;that write to the KOF register after the KON register will be missing notes. + ; + ;Solution: + ;If any voices weren't keyed on with the last KON write, check them against the new value of KOF + ;and see if they can be keyed on. + + Mov CL,[dsp+kof] + Not CL + And CL,[konLate] + JNZ .KOn + + Mov EAX,EDX + Ret + + .KOn: + Mov [konLate],AL ;Reset konLate + Push EDX + Call RKOn2 + Pop EDX + + Or EAX,EDX ;If KON or KOF changed DSP, return true + Ret + + +ALIGN 16 +RKOn: +%if DSPINTEG + Test CL,CL + JZ short .NoOutput + Call UpdateDSP + .NoOutput: +%endif + + Mov CL,AL + And AL,[dsp+kof] + Mov [konLate],AL ;Save the voices that won't be keyed on + XOr CL,AL ;Remove those voices from the kon register + JZ KDone + +RKOn2: + Push ECX,ESI,EDI + Mov CH,1 + Mov EBX,mix + Mov ESI,dsp + Mov EDI,vaaTaps + Jmp .Start + + .KeyOn: + And byte [EBX+mFlg],MFLG_MUTE ;Reset flags + + ;Set voice volume --------------------- + Sub EBX,mix + Mov AL,[ESI+volL] + Call [regTab] ;If stereo controls are enabled, setting the left +%if STEREO=0 ; volume will also set the right and vise versa + Mov AL,[ESI+volR] + Call [4+regTab] +%endif + Add EBX,mix ;We don't want the channel volume to be ramped in on + Mov EAX,[EBX+mTgtL] ; a key on. Set current volume to target level. + Mov [EBX+mChnL],EAX + + Mov EAX,[EBX+mTgtR] + Mov [EBX+mChnR],EAX + + ;Set pitch ---------------------------- + MovZX EAX,word [ESI+pitch] + And AH,3Fh + Mul dword [pitchAdj] + ShRD EAX,EDX,16 + AdC EAX,0 + Mov [EBX+mRate],EAX + Mov word [EBX+mDec],0 + + Test byte [dspOpts],OPT_FILTER + JZ short .NoFilter + Call AACoeffs,EDI,VAATAPS,EAX,[vaaCut],47000000h ;32768.0 + .NoFilter: + + Call StartSrc ;Start waveform decompression + Call StartEnv ;Start envelope + Or [voiceMix],CH ;Mark voice as being on internally + + .NoKey: + Add CH,CH + Add ESI,10h + Sub EBX,-80h + Add EDI,VAATAPS*4 + + .Start: + ShR CL,1 + JC .KeyOn + JNZ short .NoKey + + Pop EDI,ESI,ECX + XOr EAX,EAX + Inc EAX + Ret + + KDone: + XOr EAX,EAX + Ret + +;============================================ +;Voice volume + +%if STEREO=0 +ALIGN 16 +RVolL: + MovSX EAX,AL ;Sign extend volume to 32-bits + Mov [EBX+mix+mChnL],EAX + Mov [EBX+mix+mTgtL],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RVolLF: + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FMul dword [fpShR7] ;Convert volume from fixed to floating-point + FStP dword [EBX+mix+mTgtL] + + XOr EAX,EAX + Inc EAX + Ret +%endif + +ALIGN 16 +RVolLM: + ShR EBX,3 + MovSX EAX,AL + MovSX EDX,byte [EBX+dsp+volR] + ShL EBX,3 + Call GetMonoVol,EAX,EDX + Add EBX,mix + Mov [EBX+mChnL],EAX + Mov [EBX+mTgtL],EAX + Mov [EBX+mChnR],EAX + Mov [EBX+mTgtR],EAX + + XOr EAX,EAX + Inc EAX + Ret + +%if STEREO=0 +ALIGN 16 +RVolR: + MovSX EAX,AL + Mov [EBX+mix+mChnR],EAX + Mov [EBX+mix+mTgtR],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RVolRF: + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FMul dword [fpShR7] + FStP dword [EBX+mix+mTgtR] + + XOr EAX,EAX + Inc EAX + Ret +%endif + +ALIGN 16 +RVolRM: + ShR EBX,3 + MovSX EAX,AL + MovSX EDX,byte [EBX+dsp+volL] + ShL EBX,3 + Call GetMonoVol,EDX,EAX + Add EBX,mix + Mov [EBX+mChnL],EAX + Mov [EBX+mTgtL],EAX + Mov [EBX+mChnR],EAX + Mov [EBX+mTgtR],EAX + + XOr EAX,EAX + Inc EAX + Ret + +;============================================ +;Pitch + +ALIGN 16 +RPitch: + Mov EAX,[EBX+mix+pDSPV] + MovZX EAX,word [EAX+pitch] + And AH,3Fh + + Mul dword [pitchAdj] ;Convert the pitch into a more meaningful value + ShRD EAX,EDX,16 ;Remove 16-bit fraction from pitchAdj + AdC EAX,0 + Mov [EBX+mix+mRate],EAX + + Test byte [dspOpts],OPT_FILTER + JZ short .NoFilter + ShR EBX,1 + Add EBX,vaaTaps + Call AACoeffs,EBX,VAATAPS,EAX,[vaaCut],47000000h ;32768.0 + .NoFilter: + + XOr EAX,EAX + Inc EAX + Ret + +;============================================ +;Envelope + +ALIGN 16 +RADSR: + XOr EAX,EAX + Test byte [EBX+mix+mFlg],MFLG_KOFF ;Is voice in key off mode? + JNZ short .NoChg ; Yes, Envelope setting can't be changed now + + Mov AL,[EBX+mix+eMode] ;AL = ADSR or Gain mode + ShR EBX,3 + And AL,E_ADSR + Mov AH,[EBX+dsp+adsr1] + And AH,80h + Or AL,AH + + Test AL,80h + E_ADSR + JZ short .NoChg ;Envelope is already in gain mode, do nothing + Test AL,80h + JZ short .SetGain ;Switched from ADSR to Gain + Test AL,E_ADSR + JNZ .Change ;Envelope is in ADSR mode, update settings + + Mov AL,[EBX*8+mix+eMode] ;Switched from Gain to ADSR, restore previous ADSR + ShR AL,4 ; state then update settings + Or AL,E_ADSR + Mov [EBX*8+mix+eMode],AL + + .Change: + Call ChgEnv + XOr EAX,EAX + Inc EAX + + .NoChg: + Ret + + .SetGain: + ShL EBX,3 + ShL byte [EBX+mix+eMode],4 ;Save ADSR state, ChgGain will set bits 7 and 3-0 + +; Mov AL,[EBX+mix+eMode] ;If envelope switches to gain while in attack mode and +; Cmp AL,E_ATT << 4 ; and attack time is 0ms, then force envelope to max. +; JNE short RGain +; Cmp dword [ebx+mix+eAdj],A_NOATT +; JNE short RGain +; Mov dword [EBX+mix+eVal],D_MAX + Jmp short RGain + +ALIGN 16 +RGain: + XOr EAX,EAX + Test byte [EBX+mix+mFlg],MFLG_KOFF ;Is voice in key off mode? + JNZ short .NoChg ; Yes, Envelope setting can't be changed now + + ShR EBX,3 + Test byte [EBX+dsp+adsr1],80h ;Is envelope in gain mode? + JNZ short .NoChg ; No, Setting gain register has no effect + + Call ChgEnv + XOr EAX,EAX + Inc EAX + + .NoChg: + Ret + +;============================================ +;Main volumes + +ALIGN 16 +RMVolL: + MovSX EAX,AL + IMul EAX,[volAdj] + SAR EAX,16 ;Remove fraction from multiplication + Mov [volMainL],EAX + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RMVolLM: + MovSX EAX,AL + MovSX EDX,byte [dsp+mvolR] + Call GetMonoVol,EAX,EDX + IMul dword [volAdj] + SAR EAX,16 + Mov [volMainL],EAX + Mov [volMainR],EAX + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RMVolLF: + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FIMul dword [volAdj] + FMul dword [fpShR7] ;>> 7 to turn MVOL into a float + FStP dword [volMainL] ;Leave the 16-bits added by volAdj so the final + XOr EAX,EAX ; output will be 32-bit instead of 16-bit + Inc EAX + Ret + +ALIGN 16 +RMVolR: + XOr AL,[surround] ;Invert right output, if surround is enabled + Sub AL,[surround] + MovSX EAX,AL + IMul EAX,[volAdj] + SAR EAX,16 + Mov [volMainR],EAX + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RMVolRM: + MovSX EAX,AL + MovSX EDX,byte [dsp+mvolL] + Call GetMonoVol,EDX,EAX + IMul dword [volAdj] + SAR EAX,16 + Mov [volMainL],EAX + Mov [volMainR],EAX + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RMVolRF: + XOr AL,[surround] + Sub AL,[surround] + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FIMul dword [volAdj] + FMul dword [fpShR7] + FStP dword [volMainR] + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REVolL: + MovSX EAX,AL + IMul EAX,[volAdj] + SAR EAX,16 ;Remove fraction + Mov [volEchoL],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REVolLM: + MovSX EDX,byte [dsp+evolR] + MovSX EAX,AL + Call GetMonoVol,EAX,EDX + IMul dword [volAdj] + SAR EAX,16 + Mov [volEchoL],EAX + Mov [volEchoR],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REVolLF: + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FIMul dword [volAdj] + FMul dword [fpShR7] + FStP dword [volEchoL] + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REVolR: + XOr AL,[surround] + Sub AL,[surround] + MovSX EAX,AL + IMul EAX,[volAdj] + SAR EAX,16 + Mov [volEchoR],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REVolRM: + MovSX EAX,AL + MovSX EDX,byte [dsp+evolL] + Call GetMonoVol,EDX,EAX + IMul dword [volAdj] + SAR EAX,16 + Mov [volEchoL],EAX + Mov [volEchoR],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REVolRF: + XOr AL,[surround] + Sub AL,[surround] + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FIMul dword [volAdj] + FMul dword [fpShR7] + FStP dword [volEchoR] + + XOr EAX,EAX + Inc EAX + Ret + +;============================================ +;Echo settings + +ALIGN 16 +REFB: +%if STEREO + MovSX EAX,AL + Mov EDX,EAX + IMul EAX,[efbct] + SAR EAX,16 + Mov [echoFB],EAX + Mov [4+echoFB],EAX + + Mov EAX,10000h ;Calculate crosstalk + Sub EAX,[efbct] + IMul EAX,EDX + SAR EAX,16 + Mov [echoFBCT],EAX + Mov [4+echoFBCT],EAX +%else + MovSX EAX,AL + Mov [echoFB],EAX + Mov [4+echoFB],EAX +%endif + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REFBM: + MovSX EAX,AL + Mov [echoFB],EAX + Mov [4+echoFB],EAX +%if STEREO + XOr EAX,EAX + Mov [echoFBCT],EAX + Mov [4+echoFBCT],EAX +%endif + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REFBF: + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] +%if STEREO + FLd ST + + FIMul dword [efbct] + FMul dword [fpShR23] ;Convert from fixed to floating-point + FSt dword [echoFB] ;7-bits (efb) + 16-bits (efbct) = 23-bits + FStP dword [4+echoFB] + + FLd dword [fp64k] + FISub dword [efbct] + FMulP ST1,ST + FMul dword [fpShR23] + FSt dword [echoFBCT] + FStP dword [4+echoFBCT] +%else + FMul dword [fpShR7] + FSt dword [echoFB] + FStP dword [4+echoFB] +%endif + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +REDl: + And EAX,0Fh + ShL EAX,9 ;EAX = Number of samples to delay + SetZ CL ;if (EDL == 0) EAX = 1 + Or AL,CL + Mul dword [dspRate] ;EAX *= Rate / 32kHz + Mov EBX,32000 + Div EBX + Test EAX,EAX ;if EAX = 0, EAX = 1 + SetZ CL + Or AL,CL + ShL EAX,3 ;Multiply by 8, since echo is stored in 32-bit stereo + Mov [echoDel],EAX + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RFCI: + ShR EBX,4 + MovSX EAX,AL ;Sign extend filter coefficient + Mov [EBX+firTaps],EAX ;Store for left and right (for parallel instructions) + Mov [4+EBX+firTaps],EAX + Test EBX,EBX + JZ short CheckFC1 + Jmp short CheckFC ;Check if filter coefficients do anything + +ALIGN 16 +RFCF: + ShR EBX,4 + MovSX EAX,AL + Mov [ESP-4],EAX + FILd dword [ESP-4] + FMul dword [fpShR7] + FSt dword [EBX+firTaps] + FStP dword [4+EBX+firTaps] + Test EBX,EBX + JNZ short CheckFC + +CheckFC1: + Cmp AL,7Fh ;If first tap is 127, pretend it's 0 + SetE AH + Dec AH + And AL,AH + +CheckFC: + Test AL,AL + SetNZ AL + ShR EBX,3 + Mov AH,1 + Mov CL,BL + ShL EAX,CL + Not AH + And [firEnabl],AH ;Set corresponding bit if tap is non-zero + Or [firEnabl],AL + + ;Disable echo if all taps are 0 ---------- + Mov AL,[firEnabl] ;Mask for all taps + Or AL,[dsp+fc] ;Value of first tap (here, 127 doesn't count as 0) + SetZ AL ;If all taps are 0, including first one, set AL + And byte [disEcho],~1 + Or [disEcho],AL ;Set first bit to disable echo + + XOr EAX,EAX ;DSP state changed if echo was enabled + Inc EAX + Ret + +;============================================ +;Other + +ALIGN 16 +RPMOn: + And AL,0FEh ;Disable LSB + Mov [dsp+pmon],AL + + ;Reset all pitch on all voices ----------- + Mov EBX,mix + Mov CL,8 + .Next: + Mov EAX,[EBX+pDSPV] + MovZX EAX,word [EAX+pitch] + And AH,3Fh + Mul dword [pitchAdj] + ShRD EAX,EDX,16 + AdC EAX,0 + Mov [EBX+mRate],EAX + Sub EBX,-80h + Dec CL + JNZ short .Next + + XOr EAX,EAX + Inc EAX + Ret + +ALIGN 16 +RFlg: + Test AL,80h ;Has a soft reset been initialized? + JZ short .NoSRst ; No + Mov EBX,dsp + And AL,~80h + Or AL,60h ;Turn on mute and disable echo + Mov [EBX+flg],AL + Mov byte [EBX+endx],BL ;Clear end block flags + Mov byte [EBX+kon],BL + Mov byte [EBX+kof],BL + Mov byte [voiceMix],BL + + ;Reset internal voice settings -------- + Mov EBX,mix+mFlg + Mov CL,8 + .MFlg: + And byte [EBX],MFLG_MUTE ;Preserve mute flag + Or byte [EBX],MFLG_OFF ;Set voice to inactive + Sub EBX,-80h + Dec CL + JNZ .MFlg + .NoSRst: + + ;Disable echo ---------------------------- + Mov DL,AL + And DL,20h ;Isolate "disable echo" flag + And byte [disEcho],~20h + Or [disEcho],DL ;Store it + + ;Update noise clock ---------------------- + Mov dword [nRate],0 + And EAX,1Fh + JZ short .NoNoise + Mov EBX,EAX + Or EAX,-1 + Mov EDX,65535 + Div dword [EBX*4+rateTab] + Mov [nRate],EAX + .NoNoise: + + XOr EAX,EAX + Inc EAX + Ret + +;============================================ +;Null register + +ALIGN 16 +RNull: + XOr EAX,EAX + Ret + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Interpolation Functions +; +;All interpolation functions, except Gaussian, return the original sample if a 0 delta is used. +; +;In: +; ESI-> Samples to interpolate between +; EAX = Delta between samples (0 - 65535) +; +;Out: +; EAX or ST = Interpolated sample +; +;Destroys: +; LinearI and LinearX destroys EDX + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;No Interpolation + +PROC NoneI + MovSX EAX,word [ESI] ;EAX = Current sample +ENDP + +PROC NoneX + MovSX EAX,word [ESI] +ENDP + +PROC NoneF + FILd word [ESI] +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Linear Interpolation + +PROC LinearI + Push ESI + MovSX EDX,word [ESI] + MovSX ESI,word [ESI-2] + Sub EDX,ESI + SAR EDX,1 + IMul EDX,EAX + SAR EDX,15 + LEA EAX,[EDX+ESI] + Pop ESI +ENDP + +PROC LinearX + Push ESI + MovSX EDX,word [ESI] + MovSX ESI,word [ESI-2] + Sub EDX,ESI + SAR EDX,1 + IMul EDX,EAX + SAR EDX,15 + LEA EAX,[EDX+ESI] + And EAX,-2 + Pop ESI +ENDP + +PROC LinearF + FILd word [ESI-2] + FILd word [ESI] + Mov [ESP-4],EAX + FSub ST,ST1 ;Difference between samples + FIMul dword [ESP-4] ;Multiply by delta x from last sample + FMul dword [fpShR16] + FAddP ST1,ST +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Cubic Interpolation + +PROC CubicI + Push ECX,EBX + ShR EAX,8 + LEA EBX,[EAX*8+cubicTab] ;EBX -> Cubic table value + Mov AX,[ESI-6] ;Get first sample + IMul word [EBX+0] ;Multiply by cubic + Mov AX,[ESI-4] + Mov CX,DX ;DX = Partial sample >> 1 + IMul word [EBX+2] + Mov AX,[ESI-2] + Add CX,DX + IMul word [EBX+4] + Mov AX,[ESI-0] + Add CX,DX + IMul word [EBX+6] + Add CX,DX + Pop EBX + MovSX EAX,CX ;EAX = New sample + Pop ECX + Add EAX,EAX ;15-bit to 16-bit +ENDP + +PROC CubicX + MovQ MM7,[ESI-6] ;Load all four samples (isn't MMX nice?) + ShR EAX,8 ;EAX indexes interpolation table value + PMAddWD MM7,[EAX*8+cubicTab] ;Multiply samples by table + PSRAD MM7,15 + MovQ MM6,MM7 + PSLLQ MM7,32 + PAddD MM7,MM6 ;Add two dwords together and store in upper dword of MM7 + PackSSDW MM7,MM0 ;Pack dwords into words (EmuDSPX requires 16-bit samples) + MovD EAX,MM7 + SAR EAX,16 ;Sign extend 16-bit result to 32-bits + And EAX,-2 +ENDP + +PROC CubicF + Mov [ESP-4],EAX + FILd dword [ESP-4] ;delta x = mDec>>16 + FMul dword [fpShR16] ; |x + + FLd ST ; |x x + FMul ST,ST1 ; |x x^2 + FLd ST ; |x x2 x2 + FMul ST,ST2 ; |x x2 x^3 + FLd ST ; |x x2 x3 x3 + FMul dword [fp0_5] ; |x x2 x3 x3*0.5 + FAdd ST1,ST ; |x x2 1.5*x3 .5x3 + FXch ST3 ; |.5x3 x2 1.5x3 x + FMul dword [fp0_5] ; |.5x3 x2 1.5x3 x*0.5 + + ;s[-1] = -.5(x^3) + (x^2) - .5x + 0 + FLd ST2 ; |.5x3 x2 1.5x3 .5x x2 + FSub ST,ST4 ; |.5x3 x2 1.5x3 .5x x2-.5x3 + FSub ST,ST1 ; |.5x3 x2 1.5x3 .5x D0-.5x + + ;s[1] = -1.5(x^3) + 2(x^2) + .5x + 0 + FXch ST1 ; |.5x3 x2 1.5x3 D0 .5x + FAdd ST,ST3 ; |.5x3 x2 1.5x3 D0 .5x+x2 + FAdd ST,ST3 ; |.5x3 x2 1.5x3 D0 D2+x2 + FSub ST,ST2 ; |.5x3 x2 1.5x3 D0 D2-1.5x3 + + ;s[0] = 1.5(x^3) - 2.5(x^2) + 0x + 1 + FXch ST2 ; |.5x3 x2 D2 D0 1.5x3 + FLd1 + FAddP ST1,ST + FLd ST3 + FMul dword [fn2_5] + FAddP ST1,ST ; |.5x3 x2 D2 D0 D1 + + ;s[2] = .5(x^3) - .5(x^2) + 0x + 0 + FXch ST3 ; |.5x3 D1 D2 D0 x2 + FMul dword [fp0_5] ; |.5x3 D1 D2 D0 x2*0.5 + FSubP ST4,ST ; |.5x3-.5x2 D1 D2 D0 + + FIMul word [ESI-6] ; |D3 D1 D2 D0*S + FXch ST3 ; |SO D1 D2 D3 + FIMul word [ESI-0] ; |SO D1 D2 D3*S + FAddP ST3,ST ; |S0+S2 D1 D2 + FIMul word [ESI-2] ; |S D1 D2*S + FAddP ST2,ST ; |S+S2 D1 + FIMul word [ESI-4] ; |S D1*S + FAddP ST1,ST ; |S+S1 + +; ;Use lookup table ------------------------ +; ShR EAX,8 +; LEA EDI,[EAX*8+cubicTab] +; FILd word [ESI-6] +; FIMul word [EDX+0] +; FILd word [ESI-4] +; FIMul word [EDX+2] +; FILd word [ESI-2] +; FIMul word [EDX+4] +; FILd word [ESI] +; FIMul word [EDX+6] +; FAddP ST1,ST +; FAddP ST1,ST +; FAddP ST1,ST +; FMul dword [fpShR15] +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;4-point Gaussian Interpolation + +PROC GaussI + Push ECX,EBX + ShR EAX,8 + LEA EBX,[EAX*8+gaussTab] ;EBX -> Gaussian table value + Mov AX,[ESI-6] ;Get first sample + IMul word [EBX+0] ;Multiply by cubic + Mov AX,[ESI-4] + Mov CX,DX ;DX = Partial sample >> 1 + IMul word [EBX+2] + Mov AX,[ESI-2] + Add CX,DX + IMul word [EBX+4] + Mov AX,[ESI-0] + Add CX,DX + IMul word [EBX+6] + Add CX,DX + Pop EBX + MovSX EAX,CX ;EAX = New sample + Pop ECX + Add EAX,EAX ;15-bit to 16-bit +ENDP + +PROC GaussX + MovQ MM6,[ESI-6] + ShR EAX,8 ;EAX indexes interpolation table value + MovQ MM7,MM6 + PAnd MM6,[loSmp] ;MM6 = 00 s2 00 s0 + PAnd MM7,[hiSmp] ;MM7 = s3 00 s1 00 + PMAddWD MM6,[EAX*8+gaussTab] ;Multiply samples by table + PMAddWD MM7,[EAX*8+gaussTab] + PSRAD MM6,15 + PSRAD MM7,15 + PAddD MM7,MM6 + MovQ MM6,MM7 + PSRLQ MM6,32 + PAddD MM7,MM6 + PackSSDW MM7,MM0 + MovD EAX,MM7 + MovSX EAX,AX + And EAX,-2 +ENDP + +PROC GaussF + ShR EAX,8 ;EAX indexes interpolation table value + LEA EAX,[EAX*8+gaussTab] + FILd word [ESI-6] ;Get first sample + FIMul word [EAX+0] + FILd word [ESI-4] + FIMul word [EAX+2] + FILd word [ESI-2] + FIMul word [EAX+4] + FILd word [ESI] + FIMul word [EAX+6] + FAddP ST2,ST + FAddP ST2,ST + FAddP ST1,ST + FMul dword [fpShR15] +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;8-point Sinc Interpolation + +PROC SincI + Push ECX,EBX + ShR EAX,4 + And EAX,-16 + LEA EBX,[EAX+sincTab] + Mov AX,[ESI-14] + IMul word [EBX+0] + Mov AX,[ESI-12] + Mov CX,DX + IMul word [EBX+2] + Mov AX,[ESI-10] + Add CX,DX + IMul word [EBX+4] + Mov AX,[ESI-8] + Add CX,DX + IMul word [EBX+6] + Mov AX,[ESI-6] + Add CX,DX + IMul word [EBX+8] + Mov AX,[ESI-4] + Add CX,DX + IMul word [EBX+10] + Mov AX,[ESI-2] + Add CX,DX + IMul word [EBX+12] + Mov AX,[ESI-0] + Add CX,DX + IMul word [EBX+14] + Add CX,DX + Pop EBX + MovSX EAX,CX ;EAX = New sample + Pop ECX + Add EAX,EAX ;15-bit to 16-bit +ENDP + +PROC SincX + MovQ MM6,[ESI-14] + MovQ MM7,[ESI-6] + ShR EAX,4 + And EAX,-16 + PMAddWD MM6,[EAX+sincTab] + PMAddWD MM7,[8+EAX+sincTab] + PSRAD MM6,15 + PSRAD MM7,15 + PAddD MM6,MM7 + MovQ MM7,MM6 + PSLLQ MM6,32 + PAddD MM7,MM6 + PackSSDW MM7,MM0 + MovD EAX,MM7 + SAR EAX,16 + And EAX,-2 +ENDP + +PROC SincF + ShR EAX,4 ;EAX indexes interpolation table value + And EAX,-16 + Add EAX,sincTab + FILd word [ESI-14] + FIMul word [EAX+0] + FILd word [ESI-12] + FIMul word [EAX+2] + FILd word [ESI-10] + FIMul word [EAX+4] + FILd word [ESI-8] + FIMul word [EAX+6] + FILd word [ESI-6] + FIMul word [EAX+8] + FILd word [ESI-4] + FIMul word [EAX+10] + FILd word [ESI-2] + FIMul word [EAX+12] + FILd word [ESI-0] + FIMul word [EAX+14] + FAddP ST4,ST + FAddP ST4,ST + FAddP ST4,ST + FAddP ST4,ST + FAddP ST2,ST + FAddP ST2,ST + FAddP ST1,ST + FMul dword [fpShR15] +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Noise Generator +; +;Generates white noise samples +; +;Out: +; nSmp = Random 16-bit sample +; +;Destroys: +; EAX,EDX + +%macro NoiseGen 0 + Mov EAX,[nRate] + Add [nAcc],EAX + JNC short %%NoNInc + IMul EAX,[nSmp],27865 ;X=(AX+C)%M Where: X<M and 2<=A<M and 0<C<M + Add EAX,7263 ;Add C + CWDE ;Modulus M (32768) + Mov [nSmp],EAX + %%NoNInc: +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Pitch Modulation +; +;Changes the pitch based on the output of the previous voice: +; +; P' = (P * (OUTX + 32768)) >> 15 +; +;Pitch modulation in the SNES uses the full 16-bit sample value, not the 8-bit value in OUTX as +;previously believed. +; +;In: +; CH = Bitmask for current voice +; EBX-> Current voice in 'mix' +; +;Destroys: +; EAX,EDX + +%macro PitchMod 0 + Test [dsp+pmon],CH ;Is pitch modulation enabled? + JZ short %%NoPMod ; No, Pitch doesn't need to be adjusted + ;Adjust pitch by sample value --------- + Mov EDX,[EBX+pDSPV] + Mov EAX,[EBX+mOut-80h] ;EAX = Wave height of last voice (-16.15) + MovZX EDX,word [EDX+pitch] + Add EAX,32768 ;Unsign sample + And DH,3Fh + IMul EAX,EDX ;Apply sample height to pitch + SAR EAX,15 + + ;Clamp pitch to 14-bits --------------- + Mov EDX,EAX + SAR EDX,14 + JZ short %%PitchOK + SetS AL + And EAX,1 + Dec EAX + And EAX,3FFFh + %%PitchOK: + + ;Convert pitch to sample rate --------- + Mul dword [pitchAdj] + ShRD EAX,EDX,16 + Mov [EBX+mRate],EAX + %%NoPMod: +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Process Sound Source +; +;Updates the current sample position and decompresses the next block if necessary +; +;In: +; CH = Bitmask for current voice +; EBX-> Current voice in 'mix' +; +;Destroys: +; EAX,EDX,CL,ESI + +PROC ProcessSrc + + And byte [EBX+sIdx],~20h ;Adjust sample index for wrap around + Mov EAX,[EBX+sBuf+16] ;Copy last four samples of buffer + Mov EDX,[EBX+sBuf+20] ; (needed for interpolation) + Mov [EBX+sBuf-16],EAX + Mov [EBX+sBuf-12],EDX + Mov EAX,[EBX+sBuf+24] + Mov EDX,[EBX+sBuf+28] + Mov [EBX+sBuf-8],EAX + Mov [EBX+sBuf-4],EDX + + Add word [EBX+bCur],9 ;Move to next sample block + + Test byte [EBX+bHdr],BRR_END ;Was this the end block? + JZ short .NotEndB ; No, Decompress next block + Or [dsp+endx],CH ;Set flag in ENDX + Test byte [EBX+bHdr],BRR_LOOP ;Is this source looped? + JNZ short .LoopB ; Yes, Start over at loop point + + ;End voice playback ------------------- + .EndPlay: + Not CH + And [voiceMix],CH ;Don't include voice in mixing process + Not CH + Mov dword [EBX+eVal],0 ;Reset envelope and wave height + Mov dword [EBX+mOut],0 + Or byte [EBX+mFlg],MFLG_OFF ;Mark voice as inactive + And byte [EBX+mFlg],~MFLG_KOFF + RetS + + ;Restart loop ------------------------- + .LoopB: + Mov EAX,[EBX+pDSPV] + MovZX EDX,byte [EAX+srcn] ;EDX = Source number + Mov EAX,[pAPURAM] + ShL EDX,2 + Add DH,[dsp+dir] ;EDX indexes source directory + Mov AX,[2+EDX+EAX] + Mov [EBX+bCur],EAX ;Store loop point in current block pointer + + ;Decompress next block ---------------- + .NotEndB: + Mov ESI,[EBX+bCur] ;ESI -> Current sample block + Push EDI,EBX + Mov AL,[ESI] ;Get block header + LEA EDI,[EBX+sBuf] ;EDI -> location to store samples + Mov [EBX+bHdr],AL ;Save header byte + MovSX EDX,word [EBX+sP1] ;Load previous two samples + MovSX EBX,word [EBX+sP2] + Call [pDecomp] ;Call user selected decompression routine + Mov EAX,EBX + Pop EBX,EDI + Mov [EBX+sP1],DX ;Save last two samples in 32-bit form + Mov [EBX+sP2],AX + + Test byte [dspOpts],OPT_FILTER + JZ short .NoFilter2 + Sub byte [EBX+sIdx],CL ;Undo addition of CL from the top of the macro + ShR CL,1 ;Playback will end when the filter still has eight + Call FilterVoice ; samples in queue. So it's unnecessary to zero out + RetS ; the last eight samples. + .NoFilter2: + + Mov AL,[EBX+bHdr] + And AL,3 + Cmp AL,1 + Retc NE + + XOr EAX,EAX ;The SNES ends playback of one-shot sounds about + Mov [16+EBX+sBuf],EAX ; half-way through the last block. To simulate this, + Mov [20+EBX+sBuf],EAX ; zero out last eight samples. Fixes EWJ popping. + Mov [24+EBX+sBuf],EAX + Mov [28+EBX+sBuf],EAX + +ENDP + +%macro UpdateSrc 0 + ;Update sample index --------------------- + Mov CL,0 ;CL = Number of whole samples to increase index by + Mov EAX,[EBX+mRate] ;AX = Fraction of sample to increase index by + Add [EBX+mDec],AX ;Add AX to the decimal counter + AdC CL,[EBX+mRate+2] ;Add carry, if any, to increase amount + JZ short %%NoSInc ;If the amount is zero, index didn't increase + + ;Check for end of block ------------------ + Test byte [dspOpts],OPT_FILTER + JZ short %%NoFilter + Call FilterVoice + %%NoFilter: + + Add CL,CL ;CL <<= 1 (for 16-bit samples) + Add [EBX+sIdx],CL ;Increase sample index + Test byte [EBX+sIdx],20h ;Have we reached the end of the block? + JZ short %%NoSInc ; Nope + + Call ProcessSrc + + %%NoSInc: +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Calculate Envelope Modification +; +;Changes the current height of the volume envelope based on its programming +; +;In: +; EBX-> Current voice in mix +; CH = Current voice bit mask +; +;Destroys: +; EAX,CL,EDX,ESI + +PROC ProcessEnv + + Mov EAX,[EBX+eRate] ;Restore sample counter + Add [EBX+eCnt],EAX + + ;Adjust Envelope ------------------------- + Test CL,E_ADJ ;Is the adjustment an exponential decrease? + JZ short .AdjLin ; No, Go to linear + Mov EAX,[EBX+eVal] + Mov EDX,[EBX+eDest] ;Get destination (SL or 0) + + Neg EAX + SAR EAX,8 + Add [EBX+eVal],EAX ;Subtract 1/256th of envelope height + Cmp EDX,[EBX+eVal] ;Has height reached destination? + JB .Done ; No, Quit + + Test EDX,EDX ;If destination isn't 0, change to sustain mode + JNZ short .AdjDone + + Or byte [EBX+eMode],E_IDLE ;Otherwise mark envelope as no longer changing + RetN + + .AdjLin: + Test CL,E_DIR ;Is the adjustment up or down? + JZ short .AdjDec + Mov EAX,[EBX+eVal] + Add EAX,[EBX+eAdj] ;Add adjustment to height + Mov [EBX+eVal],EAX + Cmp EAX,[EBX+eDest] ;Has height reached destination? + JB .Done ; No, Quit + + Cmp EAX,D_MAX ;Clamp height to D_MAX + JBE short .AdjDone + Mov dword [EBX+eVal],D_MAX + Jmp short .AdjDone + + .AdjDec: ;Linear decrease + Mov EAX,[EBX+eAdj] + Sub [EBX+eVal],EAX ;Decrease envelope height + JA .Done ; Jump if envelope is > 0 (destination is always 0) + + Mov dword [EBX+eVal],D_MIN + + Mov AL,[EBX+eMode] ;If the envelope started out in ADSR mode, but was + And AL,~70h ; switched to Gain w/ linear decrease, the ADSR state + Or AL,E_SUST << 4 ; will become sustain if ADSR is re-enabled. + Mov [EBX+eMode],AL + + Mov AL,[EBX+mFlg] ;If the voice was getting keyed off, set MFLG_OFF to + And AL,MFLG_KOFF ; mark the voice as now being inactive + Add AL,AL + SetZ AH + Or [EBX+mFlg],AL + And byte [EBX+mFlg],~MFLG_KOFF + + Dec AH + And AH,CH + Not AH + And [voiceMix],AH ;Disable voice mixing if keyed off + + Or byte [EBX+eMode],E_IDLE ;Envelope is no longer changing + RetN + + .AdjDone: + + ;Change adjustment mode ------------------ + ;(see StartEnv) + Test CL,E_ADSR ;Is envelope in ADSR mode? + JZ short .EnvGain ; Nope, Jump to Gain + + Mov ESI,EBX + And CL,0Fh + Sub ESI,mix + XOr EAX,EAX + ShR ESI,3 ;ESI indexes current voice in dsp + Add ESI,dsp + + Cmp CL,E_DECAY ;Switch to next mode + JE short .EnvSust + + .EnvDecay: + Mov dword [EBX+eAdj],A_EXP ;see StartEnv + + Mov AL,[ESI+adsr2] + ShR AL,5 + Inc AL + IMul EAX,D_DECAY + Mov [EBX+eDest],EAX + + MovZX EAX,byte [ESI+adsr1] + And AL,70h + ShR AL,3 + Add AL,10h + Mov [EBX+eRIdx],AL + Mov ESI,[EAX*4+rateTab] + + Mov [EBX+eRate],ESI + Mov [EBX+eCnt],ESI + Mov byte [EBX+eMode],E_DECAY + RetS + + .EnvSust: + Mov AL,[ESI+adsr2] ;see StartEnv + And EAX,1Fh + Mov [EBX+eRIdx],AL + Mov ESI,[EAX*4+rateTab] + SetZ AL + Mov [EBX+eRate],ESI + Mov [EBX+eCnt],ESI + + RoR AL,1 + Mov dword [EBX+eDest],D_MIN + Or AL,E_SUST + Mov [EBX+eMode],AL + RetS + + .EnvGain: + Or byte [EBX+eMode],E_IDLE ;Envelope is now constant + + Test CL,E_DEST ;If gain is in "bent line" mode and line has reached + Retc Z ; bend point, adjust envelope settings, otherwise + ; envelope is done. + Cmp dword [EBX+eDest],D_MAX + Retc E + + And byte [EBX+eMode],~E_IDLE ;Undo idle flag + Mov dword [EBX+eAdj],A_BENT ;Slow down increase rate + Mov dword [EBX+eDest],D_MAX ;Set destination to max + + .Done: + +ENDP + +%macro UpdateEnv 0 + Mov CL,[EBX+eMode] + + Test CL,E_IDLE ;Is the envelope constant? + JNZ short %%Quit ; Yes, Quit + + Dec word [2+EBX+eCnt] ;Decrease sample counter, is it zero? + JNZ short %%Quit ; No, Quit + + Call ProcessEnv + + %%Quit: +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Finite Impulse Response Echo Filter +; +;Filters the echo using an eight tap FIR filter: +; +; 7 +; --- +; x = \ c * s +; / n n +; --- +; n=0 +; +; x = output sample +; c = filter coefficient (-.7) +; s = unfiltered sample +; n = 0 is the oldest sample and 7 is the most recent (opposite most FIR filter implementations) +; +;FIR filters are based on the sample rate. This was fine in the SNES, because the sample rate was +;always 32kHz, but in the case of an emulator the sample rate can change. So measures have to be +;taken to ensure the filter will have the same effect, regardless of the output sample rate. +; +;To overcome this problem, I figured each tap of the filter is applied every 31250ns. So the +;solution is to calculate when 31250ns have gone by, and use the sample at that point. Of course +;this method really only works if the output rate is a multiple of 32k. In order to get accurate +;results, some sort of interpolation method needs to be introduced. I went the cheap route and used +;linear interpolation. + +;In: +; EAX,EDX = Input samples +; +;Out: +; EAX,EDX = Filtered samples +; EDI -> Echo buffer +; +;Destroys: +; ECX,EBX + +%macro FIRFilterI 0 + Sub byte [firCur],4 ;Move index back one sample. (Index will wrap around + Mov EDI,[firCur] ; after 64 samples, enough for up to 256kHz output.) + LEA EDI,[EDI*2+firBuf] ;EDI -> Current sample in filter buffer + + Mov [EDI],EAX ;Store new samples in buffer + Mov [4+EDI],EDX + Mov [FIRBUF*2+EDI],EAX + Mov [FIRBUF*2+4+EDI],EDX + + Push ESI,EBP + XOr EBX,EBX + XOr ECX,ECX + Mov ESI,firTaps+56 ;ESI -> Filter taps + Mov [firDec],EBX ;Reset decimal overflow, so filtering is consistant + Mov EBP,8 ;8-tap FIR filter + %%Tap: + Mov EAX,[8+EDI] ;Interpolate left sample + Sub EAX,[EDI] + IMul dword [firDec] + ShRD EAX,EDX,16 + Add EAX,[EDI] + IMul EAX,[ESI] ;Multiply by filter tap + SAR EAX,7 + Add EBX,EAX ;Accumulate result + + Mov EAX,[12+EDI] ;Right sample + Sub EAX,[4+EDI] + IMul dword [firDec] + ShRD EAX,EDX,16 + Add EAX,[4+EDI] + IMul EAX,[ESI] + SAR EAX,7 + Add ECX,EAX + + Mov EDX,[firDec] ;Determine next sample to use in filter + Add EDX,[firRate] + Mov [firDec],DX + ShR EDX,16 + + LEA EDI,[EDX*8+EDI] ;EDI -> Sample to use in filter + Sub ESI,8 ;ESI -> Next filter tap + + Dec EBP + JNZ short %%Tap + Pop EBP,ESI + + MovSX EAX,BX + MovSX EDX,CX + And EAX,~1 + And EDX,~1 + + Mov EDI,[echoCur] ;Restore EDI + Add EDI,echoBuf +%endmacro + + +;In: +; MM2 = Input samples +; +;Out: +; MM2 = Filtered samples +; EDI-> Echo buffer +; +;Destroys: +; EAX,EDX,CL,MM5-7 + +%macro FIRFilterX 0 + Sub byte [firCur],4 + Mov EDI,[firCur] + LEA EDI,[EDI*2+firBuf] + + MovQ [EDI],MM2 + MovQ [FIRBUF*2+EDI],MM2 + + Push ESI + PXOr MM2,MM2 + XOr EAX,EAX + XOr EDX,EDX + Mov ESI,firTaps+56 + Mov CL,8 + %%Tap: + MovD MM7,EDX ;MM7 = Delta between samples + MovQ MM5,[EDI] ;MM5 = Current echo samples + MovQ MM6,[8+EDI] ;MM6 = Next echo samples + PUnpckLDQ MM7,MM7 + PSRAW MM5,1 ;Clear LSB in samples + PSRAW MM6,1 + PSRLD MM7,1 + PSubW MM6,MM5 ;Get difference between samples + PMAddWD MM6,MM7 ;Multiply by delta + PAddD MM6,MM6 ;Restore bit lost in multiplication + PSRLD MM6,16 ;Shift result down into lower word and clear upper + PAddW MM6,MM5 ;Add current samples to interpolated value + PAddW MM6,MM6 ;Restore LSB + + PMAddWD MM6,[ESI] ;Apply filter tap + PSRAD MM6,7 + PAddD MM2,MM6 + + Mov EAX,EDX + Add EAX,[firRate] + Mov DX,AX + ShR EAX,16 + + Sub ESI,8 + LEA EDI,[EAX*8+EDI] + + Dec CL + JNZ short %%Tap + Pop ESI + +;Enabling this seems to break some songs +; PSRAD MM2,1 ;Clamp to 17 bits +; PackSSDW MM2,MM0 +; PUnpckLWD MM2,MM0 +; PAddW MM2,MM2 ;Clear LSB + + PackSSDW MM2,MM0 ;Clamp to 16 bits + PUnpckLWD MM2,MM0 ;(The SNES clamps to 17 bits, but this breaks DQK. + PSRAW MM2,1 ; More investigation is needed.) + PAddW MM2,MM2 ;Clear LSB + + Mov EDI,[echoCur] + Add EDI,echoBuf +%endmacro + + +;In: +; ST0,1 = Input samples +; +;Out: +; ST0,1 = Filtered samples +; +;Destroys: +; EAX,EDX,EBX,CL + +%macro FIRFilterF 0 + Sub byte [firCur],4 + Mov EBX,[firCur] + LEA EBX,[EBX*2+firBuf] + + FSt dword [EBX] + FStP dword [FIRBUF*2+EBX] + FSt dword [4+EBX] + FStP dword [FIRBUF*2+4+EBX] + + Mov dword [firDec],0 + FLdZ + FLdZ + Mov ECX,firTaps+56 + %%Tap: + FILd dword [firDec] + FMul dword [fpShR16] + + FLd dword [8+EBX] + FSub dword [EBX] + FMul ST1 + FAdd dword [EBX] + FMul dword [ECX] + FAddP ST2,ST + + FLd dword [12+EBX] + FSub dword [4+EBX] + FMulP ST1,ST + FAdd dword [4+EBX] + FMul dword [ECX] + FAddP ST2,ST + + Sub ECX,8 + + Mov EAX,[firDec] + Add EAX,[firRate] + Mov [firDec],AX + ShR EAX,16 + LEA EBX,[EAX*8+EBX] + + Cmp ECX,firTaps + JAE short %%Tap +%endmacro + + +PROC AAFilter +USES ECX,ESI,EDI + + Mov EAX,[ESI+8] + Sub EAX,[ESI] + IMul EBP + ShRD EAX,EDX,16 + Add EAX,[ESI] + Mov [EBX],EAX + Mov [LOWBUF*2+EBX],EAX + + Mov EAX,[ESI+12] + Sub EAX,[ESI+4] + IMul EBP + ShRD EAX,EDX,16 + Add EAX,[ESI+4] + Mov [EBX+4],EAX + Mov [LOWBUF*2+EBX+4],EAX + + Add EBX,8 + And EBX,~(LOWBUF*2) + + Mov ECX,LOWTAPS-1 + XOr ESI,ESI + XOr EDI,EDI + .Filter: + Mov EAX,[ECX*8+EBX] + IMul dword [ECX*4+lowTaps] + Add ESI,EDX + + Mov EAX,[ECX*8+EBX+4] + IMul dword [ECX*4+lowTaps] + Add EDI,EDX + + Dec ECX + JNS short .Filter + + Mov EAX,ESI + Mov EDX,EDI + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate DSP + +%if DSPINTEG +;-------------------------------------------- +;Emulate DSP up to Current Point in Time + +PROC UpdateDSP +USES ESI,EDX + +%if PROFILE + Inc dword [profile+Profile.update] +%endif + Push EAX + Mov ESI,output + + Call GetSPCTime ;EAX = number of ticks that have passed + Sub EAX,[_Out(Cnt)] + Add [_Out(Cnt)],EAX + + Mul dword [_Out(Rate)] ;EAX = number of samples to generate + Add EAX,[_Out(Dec)] + AdC EDX,0 + Mov [_Out(Dec)],AX + ShRD EAX,EDX,16 + JZ short .Done + + Sub [_Out(Left)],EAX ;Subtract sample count from samples left + JNC short .Okay + Add EAX,[_Out(Left)] + Mov dword [_Out(Left)],0 + .Okay: + +%if PROFILE + Inc dword [8+profile+Profile.update] +%endif + Call EmuDSP,[_Out(PBuf)],EAX + Mov [_Out(PBuf)],EAX + + .Done: + Pop EAX + +ENDP + +;-------------------------------------------- +;Set Automatic EmuDSP Parameters +; +;pBufD -> Buffer to store samples +;numD = Number of samples to generate +;rateD = Sample rate +; rateD may be less or more than dspRate if the APU +; is trying to speed up or slow down the music + +PROC SetEmuDSP, pBuf, num, rate +USES ESI + + Mov EAX,[%$rate] + Mov ESI,output + Test EAX,EAX + JZ short .Final + + Push ECX,EDX + XOr EDX,EDX + ShLD EDX,EAX,16 + ShL EAX,16 + Mov ECX,64000 + Div ECX + Mov [_Out(Rate)],EAX + Pop EDX,ECX + + Mov EAX,[%$num] + Mov [_Out(Left)],EAX + Mov EAX,[%$pBuf] + Mov [_Out(PBuf)],EAX + Call GetSPCTime + Mov [_Out(Cnt)],EAX + Mov dword [_Out(Dec)],0 + RetS + + .Final: + Call EmuDSP,[_Out(PBuf)],[_Out(Left)] + Mov [_Out(PBuf)],EAX + Mov dword [_Out(Left)],0 + +ENDP +%endif + +PROC EmuDSP, pBuf, num +USES ALL + +%if PROFILE + Call StartAPUProfile,Profile.dspTSC +%endif + + Mov EAX,[%$pBuf] + XOr EBX,EBX ;EBX = 0 if output pointer is null, otherwise it indexes + Test EAX,EAX ; the emulation routine + SetZ BL + Dec BL + And BL,[dspMix] + + .Next: + + ;Verify output buffer length ---------- + Mov EDX,[%$num] + + Test EDX,EDX ;Is num > 0? + JLE .Quit ; No, Quit + + Cmp EDX,MIX_SIZE ;Is num <= size of internal buffer? + JBE short .NSmpOK + Mov EDX,MIX_SIZE + .NSmpOK: + + Sub [%$num],EDX + + Test byte [dbgOpt],DSP_HALT ;Do nothing if DSP is suspended + JNZ short .Mute + + ;Call emulation routine --------------- + Test byte [dspOpts],OPT_ANALOG + JZ short .NoLow + Mov [realOutS],EDX + IMul EDX,[lowRate] + Add EDX,[lowDec] + ShR EDX,16 + .NoLow: + + Call [EBX*4+mixRout],EAX,EDX + JC short .Next ;Quit, if emulation produced output + + .Mute: + ;Output silence ----------------------- + Mov EDI,EAX + + XOr EAX,EAX ;EDX = Size of output buffer in bytes + MovSX AX,[dspSize] + XOr AL,AH + Sub AL,AH + Mul byte [dspChn] + IMul EDX,EAX + + Cmp byte [dspSize],1 ;EAX = 80h if samples are unsigned, 0 otherwise + SetNE AL + Dec EAX + And EAX,80808080h + + Mov ECX,EDX ;Fill output buffer with baseline samples + And EDX,3 + ShR ECX,2 + Rep StoSD + Mov ECX,EDX + Rep StoSB + Mov EAX,EDI + + Jmp .Next + + .Quit: + + Push EAX + +%if MMETER + Call ProcessAAR +%endif + Call SetFade + + ;Update ENVX and OUTX registers ---------- + Mov EBX,7*10h + Mov ESI,mix + Mov EDI,dsp + .XRegs: +; XOr EAX,EAX +; Mov DL,[EBX+EDI+adsr1] +; Or DL,[EBX+EDI+gain] +; JNS short .NoEnvX + Mov EAX,[EBX*8+ESI+eVal] + ShR EAX,E_SHIFT +; .NoEnvX: + Mov [EBX+EDI+envx],AL + + Mov AL,[EBX*8+ESI+mOut+1] + Mov [EBX+EDI+outx],AL + + Sub EBX,10h + JAE short .XRegs + + ;Update DSP data register in APU RAM ----- + Mov EDI,[pAPURAM] + Mov DL,[0F2h+EDI] + And EDX,7Fh + Mov DL,[EDX+dsp] + Mov [0F3h+EDI],DL + +%if PROFILE + Call EndAPUProfile,Profile.dspTSC +%endif + + Pop EAX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate DSP (Integer) +; +;Emulates the DSP of the SNES using standard 80386 instructions. +;This routine is provided for historical purposes. It's the only routine that produces 8-bit or +;monaural output. +; +;In: +; pBuf-> Buffer to store output +; num = Number of samples to create (1 - MIX_SIZE) +; +;Out: +; CF = Set, samples were created +; EAX-> End of buffer +; +; CF = Clear, DSP is muted +; EDI = pBuf +; EDX = num +; +;Destroys: +; ECX,EDX,ESI,EDI + +PROC EmuDSPI, pBuf, num +LOCALS count +USES EBX + + Mov EAX,[%$num] ;Save output size as counter + Mov [%$count],EAX + Mov EDI,mixBuf ;EDI -> Temporary mixing buffer + +ALIGN 16 + .NextEmu: + + ;Generate Noise ======================= + NoiseGen + + ;Voice Loop =========================== + XOr ECX,ECX + Mov EBX,mix ;EBX -> Internal mixing structure + Mov [EDI],ECX ;Erase current samples in buffer + Mov [4+EDI],ECX + Mov [8+EDI],ECX + Mov [12+EDI],ECX + + Mov CH,1 ;CH = Voice bitmask (start with first voice) +ALIGN 16 + .VoiceMix: + XOr EAX,EAX + XOr EDX,EDX + + Test [voiceMix],CH ;Is the current voice active? + JZ .VoiceDone ; No, Goto next voice + + PitchMod ;Apply pitch modulation + UpdateEnv ;Update envelope + + ;Get sample ======================== + Mov EAX,[nSmp] ;Load noise + Test [dsp+non],CH ;Is noise enabled? + JNZ short .Noise ; Yes, Use noise sample + Mov ESI,[EBX+sIdx] ;ESI -> Samples + Mov EAX,[EBX+mDec] ;EAX = Sample delta decimal + Call [pInter] + .Noise: + + ;Mixing ============================ + IMul EAX,[EBX+eVal] ;Multiply envelope height + SAR EAX,E_SHIFT+7 ;Remove the effects of envelope multiplication + Mov [EBX+mOut],EAX ;Store sample for pitch modulation + + Test byte [EBX+mFlg],MFLG_MUTE ;Is voice muted by user? + JNZ .VoiceOff ; Yes, Don't add to master + Mov EDX,EAX ;Duplicate sample for right side + IMul EAX,[EBX+mChnL] ;Apply left and right volumes + IMul EDX,[EBX+mChnR] + SAR EAX,7 ;Remove the effects of volume multiplication + SAR EDX,7 + Add [EDI],EAX ;Add to master samples + Add [4+EDI],EDX + + Test [dsp+eon],CH ;Is echo turned on for this voice? + JZ short .NoChEcho ; No, Move to next voice + Add [8+EDI],EAX ;Add to master samples + Add [12+EDI],EDX + .NoChEcho: +%if VMETER + ;Save greatest sample output ---- + Mov ESI,EDX ;Save right sample + CDQ ;eax = abs(eax) + XOr EAX,EDX + Sub EAX,EDX + + Sub EAX,[EBX+vMaxL] ;if (EAX>mix.vMaxL) mix.vMaxL=EAX + CDQ + Not EDX + And EDX,EAX + Add [EBX+vMaxL],EDX + + Mov EAX,ESI + CDQ ;|Right| + XOr EAX,EDX + Sub EAX,EDX + + Sub EAX,[EBX+vMaxR] + CDQ + Not EDX + And EAX,EDX + Add [EBX+vMaxR],EAX +%endif + .VoiceOff: + + UpdateSrc ;Update sample position + + .VoiceDone: + Sub EBX,-80h ;Increase base pointer + + Add CH,CH ;Move bitmask over + JNZ .VoiceMix ; No, Loop + Add EDI,16 + + Dec dword [%$count] + JNZ .NextEmu + + + ;Create final output ===================== + Mov EAX,[%$num] + Mov [%$count],EAX + Mov ESI,mixBuf + +ALIGN 16 + NextSmp: + ;Multiply samples by main volume ------ + Mov EAX,[ESI] ;Load main samples + Mov EDX,[4+ESI] + IMul EAX,[volMainL] ;Multiply by main volume + IMul EDX,[volMainR] + SAR EAX,7 + SAR EDX,7 + Mov [ESI],EAX ;Store samples back into memory + Mov [4+ESI],EDX + + ;Echo --------------------------------- + Test byte [disEcho],-1 ;Is echo enabled? + JNZ .NoEcho ; No, Jump past expensive code + XOr EBX,EBX + Sub dword [echoCur],8 ;Move to next sample in echo buffer + SetNC BL + Dec EBX + And EBX,[echoDel] + Add [echoCur],EBX ;Start over, if end of buffer was reached + + Mov EDI,echoBuf ;EDI -> Delayed sample + Add EDI,[echoCur] + + Mov EAX,[EDI] ;Get current samples from echo buffer + Mov EDX,[4+EDI] + + ;Filter echo ----------------------- + Test byte [firEnabl],-1 ;Does song use a filter? + JZ .NoFilter ; No, Skip more expensive code + FIRFilterI + .NoFilter: + + Mov ECX,EAX + Mov EBX,EDX + IMul EAX,[volEchoL] ;Multiply delayed samples by echo volume + IMul EDX,[volEchoR] + SAR EAX,7 + SAR EDX,7 + Add [ESI],EAX ;Add to main output + Add [4+ESI],EDX + +%if STEREO + Mov EDX,EBX + Mov EAX,ECX + IMul ECX,[echoFB] ;Multiply delayed samples by feedback level + IMul EDX,[echoFBCT] ;Multiply delayed samples by crosstalk level + IMul EBX,[echoFB] + IMul EAX,[echoFBCT] + Add ECX,EDX ;Add crosstalk + Add EBX,EAX +%else + IMul ECX,[echoFB] + IMul EBX,[echoFB] +%endif + SAR ECX,7 + SAR EBX,7 + Add ECX,[8+ESI] ;Add delayed samples to echo samples + Add EBX,[12+ESI] + Mov [EDI],ECX ;Store feedback back into echo buffer + Mov [4+EDI],EBX + .NoEcho: + +%if MMETER + Mov EAX,[ESI] ;See VMETER above + CDQ + XOr EAX,EDX + Sub EAX,EDX + + Sub EAX,[aarMMaxL] + CDQ + Not EDX + And EAX,EDX + Add [aarMMaxL],EAX + + Mov EAX,[4+ESI] + CDQ + XOr EAX,EDX + Sub EAX,EDX + + Sub EAX,[aarMMaxR] + CDQ + Not EDX + And EAX,EDX + Add [aarMMaxR],EAX +%endif + + Add ESI,16 + + Dec dword [%$count] + JNZ NextSmp + + ;========================================= + ; Store output + + Test byte [dsp+flg],40h ;Is the DSP muted? + JNZ .MuteI ; Yes, Return silence + + Mov EBX,32768 + Mov ECX,[%$num] + Mov [%$count],ECX + Mov ESI,mixBuf + Mov EDI,[%$pBuf] + + Mov AL,[dspSize] + And AL,1 + Mov AH,[dspChn] + And AH,2 + Or AL,AH + + Test AL,3 + JZ .R16M + Test AL,1 + JZ .R16S + Test AL,2 + JZ .R8M + + ;8-bit Stereo ---------------------------- + .R8S: + Mov EAX,[ESI] + Mov EDX,[4+ESI] + Add EAX,EBX ;Unsign sample + Add EDX,EBX + + SHR EAX,8 ;Convert to 8-bit + Test AH,AH ;Is sample negative or more than 255? + JZ short .SmpOKL + SetS AL + Dec AL ;AL = 0 or 255, based on sign + .SmpOKL: + + SHR EDX,8 + Test DH,DH + JZ short .SmpOKR + SetS DL + Dec DL + .SmpOKR: + + Mov AH,DL + Mov [EDI],AX + Add ESI,16 + Add EDI,2 + + Dec ECX + JNZ short .R8S + StC + RetN EDI + + ;16-bit Stereo --------------------------- + .R16S: + Mov EDX,[ESI] + Mov EAX,[4+ESI] + + ;Clamp samples ------------------------ + LEA ECX,[EDX+EBX] ;Convert sample to unsigned, for test + SAR ECX,16 ;Is sample negative or more than 32767? + JZ short .SmpL ; No + Mov EDX,EBX ;EDX = -32768 + SetS DL ;Set DL based on sign + Dec EDX ;EDX = -32768 or 32767 + .SmpL: + + LEA ECX,[EAX+EBX] + SAR ECX,16 + JZ short .SmpR + Mov EAX,EBX + SetS AL + Dec EAX + .SmpR: + + Mov [EDI],DX ;Store samples + Mov [2+EDI],AX + Add ESI,16 + Add EDI,4 + + Dec dword [%$count] + JNZ short .R16S + StC ;Set carry, since output was made + RetS EDI ;Return pointer to end of output + + ;8-bit Mono ------------------------------ + .R8M: + Mov AX,[1+ESI] + + Sub AX,-128 ;Test for clipping + Test AH,AH + JZ short .Smp8OK + SetS AL + Dec AL + .Smp8OK: + + Mov [EDI],AL + Add ESI,16 + Inc EDI + + Dec ECX + JNZ short .R8M + StC + RetS EDI + + ;16-bit Mono ----------------------------- + .R16M: + Mov EAX,[ESI] + + LEA EDX,[EBX+EAX] ;Test for clipping + SAR EDX,16 + JZ short .Smp16OK + Mov EAX,8000h + SetS AL + Dec EAX + .Smp16OK: + + Mov [EDI],AX + Add ESI,16 + Add EDI,2 + + Dec ECX + JNZ short .R16M + StC + RetS EDI + +.MuteI: + Mov EAX,[%$pBuf] + Mov EDX,[%$num] + ClC + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate DSP (MMX) +; +;Emulates the DSP of the SNES using instructions from the Intel Multi-Media Extensions +;This mixing routine is coded for original hardware accuracy. It's the only routine that supports +;OPT_ANALOG. All output is stereo. +; +;In: +; pBuf-> Buffer to store output +; num = Number of samples to create (1 - MIX_SIZE) +; +;Out: +; CF = Set, samples were created +; EAX-> End of buffer +; +; CF = Clear, DSP is muted +; EDI = pBuf +; EDX = num +; +;Destroys: +; ECX,EDX,ESI,EDI +; MM0 = 0 +; MM1 = Main +; MM2 = Echo +; MM3 = VMax for main output +; MM4 = temp +; MM5 = temp +; MM6 = temp +; MM7 = temp + + +PROC EmuDSPX, pBuf, num +LOCALS count,8,fpShR30,ftemp +USES EBX + + PXOr MM0,MM0 ;MM0 = 0, always + +%if MMETER + MovQ MM3,[aarMMaxL] +%endif + + ;========================================= + ;Mix voices + + Mov EAX,[%$num] + Test EAX,EAX ;num can == 0 when OPT_ANALOG is enabled + JZ .Store + Mov [%$count],EAX + + XOr EAX,EAX + Test byte [dspOpts],OPT_ANALOG + SetNZ AL + ShL EAX,6 + LEA EDI,[EAX+mixBuf] + +ALIGN 16 + .NextEmu: + + ;Generate noise ----------------------- + NoiseGen + + ;Process single voice ----------------- + XOr ECX,ECX + Mov EBX,mix + PXOr MM1,MM1 + PXOr MM2,MM2 + + Mov CH,1 +ALIGN 16 + .VoiceMix: + XOr EAX,EAX + XOr EDX,EDX + + Test [voiceMix],CH + JZ .VoiceDone + + PitchMod ;Apply pitch modulation + UpdateEnv ;Update envelope + + ;Get sample ------------------------ + Mov EAX,[nSmp] + Test [dsp+non],CH + JNZ short .Noise + Mov ESI,[EBX+sIdx] + Mov EAX,[EBX+mDec] + Call [pInter] + .Noise: + + ;Mixing ---------------------------- + IMul EAX,[EBX+eVal] + SAR EAX,E_SHIFT+7+1 + Add EAX,EAX + Mov [EBX+mOut],EAX + + Test byte [EBX+mFlg],MFLG_MUTE + JNZ .VoiceOff + MovZX EAX,AX ;Truncate sample to 16-bits and dupilcate in MM5 + MovD MM4,EAX + PUnpckLDQ MM4,MM4 + + PMAddWD MM4,[EBX+mChnL] ;Multiply by channel volume + PSRAD MM4,7 +%if VMETER + ;Save greatest sample output ---- + MovQ MM7,[EBX+vMax] ;Get max voice output so far + MovQ MM6,MM4 ;Copy current voice output + MovQ MM5,MM4 + + PSRAD MM6,31 ;Create a mask from negative samples + PXOr MM5,MM6 ;Invert negative samples + PSubD MM5,MM6 ;Add 1 to negatives, |MM5| + + MovQ MM6,MM7 + PCmpGTD MM7,MM5 ;Create a mask from greater samples + PAnd MM6,MM7 ;Save greater samples + PAndN MM7,MM5 ;Clear lesser samples + POr MM7,MM6 ;MM7 = Greatest output so far + MovQ [EBX+vMax],MM7 +%endif + PAddSW MM1,MM4 ;Add to main output + + Test [dsp+eon],CH + JZ short .NoChEcho + PAddSW MM2,MM4 ;Add to echo + .NoChEcho: + .VoiceOff: + + UpdateSrc ;Update sample position + + .VoiceDone: + Sub EBX,-80h + + Add CH,CH + JNZ .VoiceMix + + PSLLD MM1,16 + PSLLD MM2,16 + PSRLD MM1,17 + PSRLD MM2,17 + PAddW MM1,MM1 + PAddW MM2,MM2 + MovQ [EDI],MM1 + MovQ [8+EDI],MM2 + + Add EDI,16 + + Dec dword [%$count] + JNZ .NextEmu + + + ;========================================= + ;Apply main volumes and mix in echo + + Mov EAX,[%$num] + Mov [%$count],EAX + + XOr EAX,EAX + Test byte [dspOpts],OPT_ANALOG + SetNZ AL + ShL EAX,6 + LEA ESI,[EAX+mixBuf] + +ALIGN 16 + .NextSmp: + ;Multiply samples by main volume ------ + MovQ MM1,[ESI] + PMAddWD MM1,[volMainL] + PSRAD MM1,7 + + Test byte [disEcho],-1 + JNZ .NoEcho + XOr EAX,EAX + Sub dword [echoCur],8 + SetNC AL + Dec EAX + And EAX,[echoDel] + Add [echoCur],EAX + + Mov EDI,echoBuf + Add EDI,[echoCur] + + MovQ MM2,[EDI] ;Load MM2 with echo samples incase filtering is off + + ;Filter echo ----------------------- + FIRFilterX + + MovQ MM5,MM2 + PMAddWD MM5,[volEchoL] + PSRAD MM5,7 + PAddD MM1,MM5 + +%if STEREO + MovQ MM7,MM2 + PSRLQ MM7,32 ;MM7 = PSwapD MM2 + PUnpckLDQ MM7,MM2 + PMAddWD MM2,[echoFB] ;Multiply old samples by feedback + PMAddWD MM7,[echoFBCT] + PAddD MM2,MM7 +%else + PMAddWD MM2,[echoFB] +%endif + PSLLD MM2,9 ;Shift result into upper 16-bits + PSRLD MM2,17 ;Zero fill upper word and clear LSB + PAddW MM2,MM2 ; " " + PAddSW MM2,[8+ESI] ;Add current samples and clamp result + MovQ [EDI],MM2 + .NoEcho: + +%if MMETER + MovQ MM6,MM1 ;See VMETER above + MovQ MM7,MM1 + + PSRAD MM6,31 + PXOr MM7,MM6 + PSubD MM7,MM6 + + MovQ MM6,MM3 + PCmpGTD MM3,MM7 + PAnd MM6,MM3 + PAndN MM3,MM7 + POr MM3,MM6 +%endif + + PackSSDW MM1,MM0 + MovD [ESI],MM1 + Add ESI,16 + + Dec dword [%$count] + JNZ .NextSmp + +%if MMETER + MovQ [aarMMaxL],MM3 +%endif + + ;========================================= + ; Store output + +.Store: + Test byte [dsp+flg],40h + JNZ MuteX + + Mov ESI,mixBuf + Mov EDI,[%$pBuf] + Mov ECX,[%$num] + + Test byte [dspOpts],OPT_ANALOG + JNZ .UseLow + + Cmp byte [dspSize],2 + JE .R16 + Cmp byte [dspSize],3 + JE .R24 + Cmp byte [dspSize],4 + JE .R32 + + EMMS + .RF: + FILd word [ESI] + FMul dword [fpShR15] + FILd word [2+ESI] + FMul dword [fpShR15] + FStP dword [4+EDI] + FStP dword [EDI] + + Add ESI,16 + Add EDI,8 + + Dec ECX + JNZ short .RF + StC + RetN EDI + + .R16: + Mov EAX,[ESI] + Mov [EDI],EAX + + Add ESI,16 + Add EDI,4 + + Dec ECX + JNZ short .R16 + EMMS + StC + RetN EDI + + .R24: + MovZX EAX,word [ESI] + Mov DX,[2+ESI] + ShL EAX,8 + Mov [EDI],EAX + Mov [4+EDI],DX + + Add ESI,16 + Add EDI,6 + + Dec ECX + JNZ short .R24 + EMMS + StC + RetN EDI + + .R32: + MovQ MM1,MM0 + PUnpckLWD MM1,[ESI] + MovQ [EDI],MM1 + + Add ESI,16 + Add EDI,8 + + Dec ECX + JNZ short .R32 + EMMS + StC + RetN EDI + +.UseLow: + Test ECX,ECX + JZ .NoUnpack + + Mov EAX,17491 ;Initialize white noise generator + Mov EDX,1999 + MovD MM6,EAX + MovD MM7,EDX + PUnpckLDQ MM6,MM6 + PUnpckLDQ MM7,MM7 + MovQ MM5,[lowRFI] + + Mov EBX,[lowAmp] + + Mov EAX,07E87A09h ;Clamp to -0.1dB (28-bits signed) + MovD MM4,EAX + PUnpckLDQ MM4,MM4 + +%if MMETER + MovQ MM3,[aarMMaxL] + PSLLD MM3,12 +%endif + + Mov EDI,32 + .Unpack: + PUnpckLWD MM1,[EDI*2+ESI] ;Get 16-bit samples + + PSRAD MM1,3 ;Add -72dB of white noise + PMulLW MM5,MM6 ;<-- causes a DC offset, maybe fix this some day + PAddW MM5,MM7 + PAddD MM1,MM5 + PSRAD MM1,9 ;Reduce to 20 bits + + MovD EAX,MM1 ;Multiply by volAmp + PSRLQ MM1,32 + MovD EDX,MM1 + IMul EAX,EBX + IMul EDX,EBX + MovD MM1,EAX + MovD MM2,EDX + PUnpckLDQ MM1,MM2 + +%if MMETER + MovQ MM2,MM1 + PSRAD MM2,31 + PXOr MM2,MM1 + + MovQ MM0,MM3 + PCmpGTD MM3,MM2 + PAnd MM0,MM3 + PAndN MM3,MM2 + POr MM3,MM0 +%endif + + ;Clamp samples --------------------- + PAddD MM1,MM4 ;Unsign samples + PSRAD MM1,1 + + MovQ MM2,MM1 ;Clamp < 0 + PSRAD MM1,31 + PAndN MM1,MM2 + + MovQ MM2,MM4 ;Clamp > MM4 + PCmpGTD MM2,MM1 + PAnd MM1,MM2 + PAndN MM2,MM4 + POr MM1,MM2 + + PAddD MM1,MM1 ;Sign samples + PSubD MM1,MM4 + PSLLD MM1,3 + + MovQ [EDI+ESI],MM1 + + Add EDI,8 + + Dec ECX + JNZ short .Unpack + + Mov EDI,[%$pBuf] + MovQ [lowRFI],MM5 + +%if MMETER + PSRAD MM3,12 + MovQ [aarMMaxL],MM3 + + PXOr MM0,MM0 +%endif + + .NoUnpack: + + Push EBP + Mov ECX,[realOutS] + Mov EBP,[lowDec] + Mov EBX,[lowCur] + + Cmp byte [dspSize],2 + JE .RL16 + Cmp byte [dspSize],3 + JE .RL24 + Cmp byte [dspSize],4 + JE .RL32 + + Mov dword [%$fpShR30],30800000h + + EMMS + .RLF: + Call AAFilter + + Mov [%$ftemp],EAX + Mov [4+%$ftemp],EDX + FILd dword [%$ftemp] + FILd dword [4+%$ftemp] + FMul dword [%$fpShR30] + FStP dword [4+EDI] + FMul dword [%$fpShR30] + FStP dword [EDI] + Mov [EDI],EAX + Mov [4+EDI],EDX + + XOr EAX,EAX + Add BP,[lowRate] + AdC AL,[2+lowRate] + LEA ESI,[EAX*8+ESI] + Add EDI,8 + + Dec ECX + JNZ .RLF + Jmp .DoneL + + .RL16: + Call AAFilter + + ShR EAX,14 + ShR EDX,14 + Mov [EDI],AX + Mov [2+EDI],DX + + XOr EAX,EAX + Add BP,[lowRate] + AdC AL,[2+lowRate] + LEA ESI,[EAX*8+ESI] + Add EDI,4 + + Dec ECX + JNZ .RL16 + Jmp .DoneL + + .RL24: + Call AAFilter + + ShL EAX,2 + ShR EDX,6 + ShRD EAX,EDX,8 + ShR EDX,8 + Mov [EDI],EAX + Mov [4+EDI],DX + + XOr EAX,EAX + Add BP,[lowRate] + AdC AL,[2+lowRate] + LEA ESI,[EAX*8+ESI] + Add EDI,6 + + Dec ECX + JNZ .RL24 + Jmp .DoneL + + .RL32: + Call AAFilter + + ShL EAX,2 + ShL EDX,2 + Mov [EDI],EAX + Mov [4+EDI],EDX + + XOr EAX,EAX + Add BP,[lowRate] + AdC AL,[2+lowRate] + LEA ESI,[EAX*8+ESI] + Add EDI,8 + + Dec ECX + JNZ .RL32 + +.DoneL: + Mov [lowCur],EBX + Mov [lowDec],EBP + Pop EBP + + Mov EDX,[%$num] + ShL EDX,3 + JZ short .Blah + MovQ MM1,[EDX+mixBuf] + MovQ MM2,[8+EDX+mixBuf] + MovQ MM3,[16+EDX+mixBuf] + MovQ MM4,[24+EDX+mixBuf] + MovQ [mixBuf],MM1 + MovQ [8+mixBuf],MM2 + MovQ [16+mixBuf],MM3 + MovQ [24+mixBuf],MM4 + .Blah: + + EMMS ;Empty MMX regs to keep FP operations from breaking + StC + RetS EDI + +MuteX: + Mov EAX,[%$pBuf] + Mov EDX,[%$num] + EMMS + ClC + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate DSP (Float) +; +;Emulates the DSP of the SNES using floating-point instructions. +;This routine is coded for numerical accuracy. +; +;In: +; pBuf-> Buffer to store output +; num = Number of samples to create (1 - MIX_SIZE) +; +;Out: +; CF = Set, samples were created +; EAX-> End of buffer +; +; CF = Clear, DSP is muted +; EDI = pBuf +; EDX = num +; +;Destroys: +; ECX,EDX,ESI,EDI,ST0-ST7 + +;----------------------------------------- +;Clamp a single precision float +; +;In: +; ESI -> Float to clamp +; EBX = +/- float value to clamp to +; +;Out: +; ST0 = Clamped value + +%macro ClampF32 0 + Mov EAX,[ESI] + XOr EDX,EDX + Add EAX,EAX ;Get absolute value of sample by shifting off sign bit + RCL EBX,1 ;Save sign of sample + Sub EAX,EBX + SetA DL ;if (sample < EBX) EDX = -1 + Dec EDX + And EAX,EDX ;Clamp sample + Add EAX,EBX + ShR EBX,1 ;Restore sign + RCR EAX,1 + Mov [ESI],EAX + FLd dword [ESI] +%endmacro + +PROC EmuDSPF, pBuf, num +LOCALS count,vMaxL,vMaxR,fSize,fpEShR +USES EBX + + Mov dword [%$fpEShR],(127-(E_SHIFT+7)) << 23 + + ;========================================= + ; Mix voices + + Mov EAX,[%$num] + Mov [%$count],EAX + Mov EDI,mixBuf +ALIGN 16 + .NextEmu: + + ;Generate Noise ======================= + NoiseGen + + ;Voice Loop =========================== + FLdZ + XOr ECX,ECX + Mov EBX,mix + FSt qword [EDI] + FStP qword [8+EDI] + + Mov CH,1 + .VoiceMix: + XOr EAX,EAX + XOr EDX,EDX + + Test [voiceMix],CH + JZ .VoiceDone + + PitchMod ;Apply pitch modulation + UpdateEnv ;Update envelope + + ;Get sample ======================== + Mov ESI,[EBX+sIdx] + Mov EAX,[EBX+mDec] + Call [pInter] + + Test [dsp+non],CH + JZ short .NoNoise + FStP ST + FILd dword [nSmp] + .NoNoise: + + ;Mixing ============================ + FIMul dword [EBX+eVal] + FMul dword [%$fpEShR] + FISt dword [EBX+mOut] + + Test byte [EBX+mFlg],MFLG_MUTE + JNZ .VoiceOff + + ;Adjust volumes to match target - + Mov EAX,[EBX+mTgtL] + XOr EDX,EDX + Cmp EAX,[EBX+mChnL] + JZ short .NoRampL + ;Choose ramp direction ------- + SetL DL ;DL = Ramp direction (0/1, up/down) + Test EAX,[EBX+mChnL] ;If mTgtL and mChnL are both negative, reverse the + SetS CL ; result of the comparison/direction + XOr DL,CL + FLd dword [EBX+mChnL] ;Load current volume + FLd dword [EDX*4+volRamp] ;Load +/- ramp amount + + ;Ramp volume ----------------- + FAddP ST1,ST + FStP dword [EBX+mChnL] + Test EAX,[EBX+mChnL] + SetS CL ;Reverse comparison if both are negative + XOr CL,DL + + ;Has target has been hit? ---- + Sub EAX,[EBX+mChnL] + SetG DL ;DL = 1, if volume hasn't reached destination + XOr DL,CL ;Reverse comparison if volume is moving down + Dec EDX + And EAX,EDX + Add [EBX+mChnL],EAX + .NoRampL: + + Mov EAX,[EBX+mTgtR] + XOr EDX,EDX + Cmp EAX,[EBX+mChnR] + JZ short .NoRampR + SetL DL + Test EAX,[EBX+mChnR] + SetS CL + XOr DL,CL + FLd dword [EBX+mChnR] + FLd dword [EDX*4+volRamp] + + FAddP ST1,ST + FStP dword [EBX+mChnR] + Test EAX,[EBX+mChnR] + SetS CL + XOr CL,DL + + Sub EAX,[EBX+mChnR] + SetG DL + XOr DL,CL + Dec EDX + And EAX,EDX + Add [EBX+mChnR],EAX + .NoRampR: + + ;Apply channel volumes ---------- + FLd ST + FMul dword [EBX+mChnR] + FXCh ST1 + FMul dword [EBX+mChnL] +%if VMETER + FLd ST1 + FAbs + FLd ST1 + FAbs + FIStP dword [%$vMaxL] + FIStP dword [%$vMaxR] +%endif + Test [dsp+eon],CH + JNZ short .VoiceEcho + FAdd dword [EDI] + FStP dword [EDI] + FAdd dword [4+EDI] + FSt dword [4+EDI] + Jmp short .NoVoiceEcho + + .VoiceEcho: + FLd ST + FAdd dword [EDI] + FStP dword [EDI] + FAdd dword [8+EDI] + FStP dword [8+EDI] + + FLd ST + FAdd dword [4+EDI] + FStP dword [4+EDI] + FAdd dword [12+EDI] + FSt dword [12+EDI] + + .NoVoiceEcho: +%if VMETER + ;Save greatest sample output ---- + Mov EAX,[EBX+vMaxL] + Sub EAX,[%$vMaxL] + CDQ + And EAX,EDX + Sub [EBX+vMaxL],EAX + + Mov EAX,[EBX+vMaxR] + Sub EAX,[%$vMaxR] + CDQ + And EAX,EDX + Sub [EBX+vMaxR],EAX +%endif + .VoiceOff: + FStP ST + + UpdateSrc ;Update sample position + + .VoiceDone: + Sub EBX,-80h + + Add CH,CH + JNC .VoiceMix + + Add EDI,16 + + Dec dword [%$count] + JNZ .NextEmu + + + ;========================================= + ; Apply main volumes and mix in echo + +%if MMETER + XOr EAX,EAX + Mov [%$vMaxL],EAX + Mov [%$vMaxR],EAX +%endif + + MovZX EBX,byte [dspChn] + ShL EBX,2 + Mov [%$fSize],EBX + + Mov EAX,[%$num] + Mov [%$count],EAX + Mov ESI,mixBuf + Mov EDI,mixBuf +ALIGN 16 + .NextSmp: + ;Multiply samples by main volume ------ + FLd dword [ESI] + FMul dword [volMainL] + FStP dword [EDI] + FLd dword [4+ESI] + FMul dword [volMainR] + FStP dword [4+EDI] + + Test byte [disEcho],-1 + JNZ .NoEcho + ;Advance echo sample pointer ------- + Mov EDX,[echoCur] + XOr EAX,EAX + Sub EDX,8 + SetNC AL + Dec EAX + And EAX,[echoDel] + Add EDX,EAX + Mov [echoCur],EDX + Add EDX,echoBuf + + FLd dword [4+EDX] ;FBR + FLd dword [EDX] ;FBR FBL + + ;Filter echo ----------------------- + FIRFilterF + FLd ST1 ;FBR FBL FBR + FLd ST1 ;FBR FBL FBR FBL + + ;Calculate echo feedback ----------- +%if STEREO + FLd ST ;FBR FBL FBR FBL FBL + FMul dword [echoFB] ;FBR FBL FBR FBL FBL*EchoFB + FLd ST2 ;FBR FBL FBR FBL EFBL FBR + FMul dword [echoFBCT] ;FBR FBL FBR FBL EFBL FBR*EchoFBCT + FAddP ST1,ST ;FBR FBL FBR FBL EFBL+EFBCR + FAdd dword [8+ESI] ;FBR FBL FBR FBL EFBL+EL + FStP dword [EDX] ;FBR FBL FBR FBL + + FMul dword [echoFBCT] ;FBR FBL FBR FBL*EchoFBCT + FXCh ST1 ;FBR FBL EFBCL FBR + FMul dword [echoFB] ;FBR FBL EFBCL FBR*EchoFB + FAddP ST1,ST ;FBR FBL EFBCL+EFBR + FAdd dword [12+ESI] ;FBR FBL EFBR+ER + FStP dword [4+EDX] ;FBR FBL +%else + FMul dword [echoFB] ;FBR FBL FBR FBL*EchoFB + FAdd dword [8+ESI] ;FBR FBL FBR EFBL+EL + FStP dword [EDX] ;FBR FBL FBR + + FMul dword [echoFB] ;FBR FBL FBR*EchoFB + FAdd dword [12+ESI] ;FBR FBL EFBR+ER + FStP dword [4+EDX] ;FBR FBL +%endif + + ;Add echo to main output ----------- + FMul dword [volEchoL] ;FBR FBL*EVolL + FXCh ST1 ;EchoL FBR + FMul dword [volEchoR] ;EchoL FBR*EVolR + + Cmp byte [dspChn],4 + JB short .Stereo + FStP dword [12+EDI] + FStP dword [8+EDI] + Jmp short .ChDone + .Stereo: + FAdd dword [4+EDI] ;EchoL EchoR+MainR + FStP dword [4+EDI] ;EchoL + FAdd dword [EDI] ;EchoL+MainL + FStP dword [EDI] ;(empty) + .ChDone: + + .NoEcho: + + Add ESI,16 + +%if MMETER + Mov EDX,[EDI] ;if (abs(out) > MMaxL) MMaxL = abs(out); + Mov EAX,[%$vMaxL] + Add EDX,EDX ;EDX = |Left| + ShR EDX,1 + Sub EAX,EDX + CDQ + And EAX,EDX + Sub [%$vMaxL],EAX + + Mov EDX,[4+EDI] + Mov EAX,[%$vMaxR] + Add EDX,EDX ;EDX = |Right| + ShR EDX,1 + Sub EAX,EDX + CDQ + And EAX,EDX + Sub [%$vMaxR],EAX +%endif + + Add EDI,[%$fSize] + + Dec dword [%$count] + JNZ .NextSmp + + ;========================================= + ; Store output + + Test byte [dsp+flg],40h + JNZ MuteF + + Mov AL,[dspSize] + MovZX ECX,byte [dspChn] + Mov ESI,mixBuf + Mov EDI,[%$pBuf] + IMul ECX,[%$num] + + Cmp AL,-4 + JE .OutFloat + + Mov EBX,4EFFFE00h ;EBX = 2147418112.0 (32767 << 16) + + Cmp AL,3 + JE short .Next24 + Cmp AL,4 + JE short .Next32 + + FLd dword [fpShR16] + .Next16: + ClampF32 + Add ESI,4 + FMul ST1 + FIStP word [EDI] + Add EDI,2 + Dec ECX + JNZ short .Next16 + FStP ST + Jmp .Done + + .Next24: + ClampF32 + FIStP dword [ESI] + Mov DL,[1+ESI] + Mov AX,[2+ESI] + Add ESI,4 + Mov [0+EDI],DL + Mov [1+EDI],AX + Add EDI,3 + Dec ECX + JNZ short .Next24 + Jmp .Done + + .Next32: + ClampF32 + Add ESI,4 + FIStP dword [EDI] + Add EDI,4 + Dec ECX + JNZ short .Next32 + Jmp .Done + +; ;*** Slower floating-point method for clamping *** +; FLd [fp2M] ;Load clamping range |Fp2M +; +; FLd dword [ESI] ;Load sample |Fp2M Left +; FLd ST ; |Fp2M Left Left +; FAbs ;Get absolute value |Fp2M Left |Left| +; FComP ST2 ;Cmp |Left|,Fp2M and pop |Fp2M Left +; FNStSW AX ;Store FPU flags in AX +; SAHF ;Store AH in x86 flags +; JBE short .SmpL +; FStP [fTemp] ; |Fp2M +; FLd ST ; |Fp2M Fp2M +; Test byte [3+ESI],80h ;Is sample negative? +; JZ short .SmpL ; No, Leave ST positive +; FChS ; Yes, Negate ST |Fp2M -Fp2M +; .SmpL: +; FIStP dword [EDI] ;Save sample as an int |Fp2M +; +; FStP ST ;Pop clipping range + + ;32-bit floating-point ------------------- + .OutFloat: + Rep MovSD + +.Done: + ;Save maximum output --------------------- + Mov EAX,[aarMMaxL] + FLd dword [%$vMaxL] + FMul dword [fpShR16] + FIStP dword [%$vMaxL] + Sub EAX,[%$vMaxL] + CDQ + And EAX,EDX + Sub [aarMMaxL],EAX + + Mov EAX,[aarMMaxR] + FLd dword [%$vMaxR] + FMul dword [fpShR16] + FIStP dword [%$vMaxR] + Sub EAX,[%$vMaxR] + CDQ + And EAX,EDX + Sub [aarMMaxR],EAX + + StC + RetS EDI + +MuteF: + Mov EAX,[%$pBuf] + Mov EDX,[%$num] + ClC + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate DSP (no mixing) +; +;Emulates the DSP of the SNES, except for pitch modulation, noise generator, and mixing. +; +;Notes: +; OutBuf can be null, in which case no output is created. If OutBuf is not null, silence will be +; returned. +; +;In: +; pBuf-> Buffer that would store output +; num = Number of samples to not create (1-3072) +; +;Out: +; EAX = outBuf +; CF = True, if EAX is null +; +;Destroys: +; ECX,EDX,ESI,EDI + +PROC EmuDSPN, pBuf, num +LOCALS count +USES EBX + + Mov EAX,[%$num] + Mov [%$count],EAX + + Mov EBX,mix + Mov CH,1 ;CH = Voice bitmask +ALIGN 16 + .NextVoice: + ;Voice Loop --------------------------- + Test [voiceMix],CH ;Is the current voice on? + JZ .VoiceDone ; No + + ;Figure new sample position ======== + Mov EAX,[EBX+mRate] + Mul dword [%$count] + Add [EBX+mDec],AX + SetC CL + ShRD EAX,EDX,16 + MovZX EDX,CL + Add EAX,EDX + Mov EDX,EAX + ShR EDX,4 ;EDX = Number of sample blocks to increase + And EAX,0Fh + Add EAX,EAX ;EAX = Position in sample RAM + + Add [EBX+sIdx],AL ;Increase current index + Test byte [EBX+sIdx],20h ;Have we moved into the next block? + JZ short .NoSInc ; No + And byte [EBX+sIdx],~20h ;Correct index in + Inc EDX + .NoSInc: + + Test EDX,EDX ;Do any blocks need to be decompressed? + JZ .NoBInc ; No + + Mov ESI,[EBX+bCur] ;ESI -> Current block + .NextBlk: + Mov AL,[ESI] + + Test AL,1 ;Is this the end block? + JZ short .NotEnd ; Not yet + Or [dsp+endx],CH ;Set end flag + Test AL,2 + JNZ short .LoopB + + Not CH ;Turn off voice + And [voiceMix],CH + Not CH + Mov dword [EBX+eVal],0 + Mov dword [EBX+mOut],0 + Or byte [EBX+mFlg],MFLG_OFF + And byte [EBX+mFlg],~MFLG_KOFF + Jmp .VoiceDone + + .LoopB: + Mov EDI,[EBX+pDSPV] + Mov ESI,[pAPURAM] + MovZX EAX,byte [EDI+srcn] + ShL EAX,2 + Add AH,[dsp+dir] + Mov SI,[2+EAX+ESI] + + .NotEnd: + + Push EDX,EBX ;Decompress block + LEA EDI,[EBX+sBuf] + MovSX EDX,word [EBX+sP1] + MovSX EBX,word [EBX+sP2] + Call [pDecomp] + Mov EAX,EBX + Pop EBX + Mov [EBX+sP1],DX + Pop EDX + Mov [EBX+sP2],AX + + Dec EDX + JNZ short .NextBlk + + Sub SI,9 ;Save block header and pointer for last block decomp + Mov AL,[ESI] + Mov [EBX+bCur],ESI + Mov [EBX+bHdr],AL + .NoBInc: + + XOr EAX,EAX + Mov [EBX+sBuf-4],EAX + Mov [EBX+sBuf-8],EAX + + ;Adjust envelope height ============ + Mov EDI,[%$count] +ALIGN 16 + .VoiceEnv: + Mov CL,[EBX+eMode] + Test CL,E_IDLE ;Is envelope constant? + JNZ .EnvDone ; Yes, Skip height adjustment + + Mov EAX,EDI + Cmp DI,[2+EBX+eCnt] ;EAX = min(eCnt, countN) + JB short .EmuS + Mov AX,[2+EBX+eCnt] + .EmuS: + Sub EDI,EAX + + Dec EAX + Sub [2+EBX+eCnt],AX + UpdateEnv ;Calculate envelope change + + Test EDI,EDI + JNZ .VoiceEnv + .EnvDone: + + ;Update ENVX and OUTX registers ==== + Mov ESI,dword [EBX+sIdx] + MovSX EAX,word [ESI] ;EAX = Current sample + IMul EAX,[EBX+eVal] + SAR EAX,E_SHIFT+7 ;Reduce sample to 16-bit number + Mov [EBX+mOut],EAX + + .VoiceDone: + Sub EBX,-80h ;Increase base pointer + + Add CH,CH ;Move bitmask over + JNC .NextVoice + + Mov EAX,[%$pBuf] ;Set carry if OutBuf is null, so EmuDSP doesn't crash + Cmp EAX,1 + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Play Sound Source + +PROC PlaySrc, pSrc, loop, rate +LOCALS pInter,inRAM,fp32k +USES ALL + + Mov EBX,src + + Mov EAX,[%$rate] + Test EAX,EAX + JNZ .New + + Mov EAX,[_Src(Blk)] + Test EAX,EAX + JZ .Done + + Mov dword [%$fp32k],47000000h ;32768.0 + + ShR EAX,16 + Cmp AX,[2+pAPURAM] + SetNE [%$inRAM] + + ;Select interpolation function ----------- + MovZX EDX,byte [dspMix] + MovZX EAX,byte [dspInter] + XOr ECX,ECX + Test EDX,EDX + SetNZ CL + Sub EDX,ECX ;If dspMix == MIX_NONE, default to MIX_INT + LEA EDX,[EDX*5] + Add EDX,EAX + Mov EAX,[EDX*4+intRout] + Mov [%$pInter],EAX + Mov EAX,[%$loop] + + .NextBuf: + ;===================================== + ;Fill mixBuf + + Mov ECX,2*2*MIX_SIZE + Mov EDI,mixBuf + Mov ESI,[_Src(Idx)] + + Sub EAX,ECX + Mov [%$loop],EAX + CDQ + And EAX,EDX + Add ECX,EAX + Mov [%$rate],ECX + + .NextSmp: + Mov EAX,[_Src(Dec)] ;Has sample index increased? + Add EAX,[_Src(Rate)] + Mov [_Src(Dec)],AX + ShR EAX,16 + JZ .NoSInc + + LEA ESI,[EAX*2+ESI] ;Have we reached the end of the block? + Test ESI,20h + JZ .NoSInc + + And ESI,~20h + + Mov EAX,[_Src(Blk)] + Mov AL,[EAX] + + Test AL,1 + JNZ short .Stop + Test AL,2 + JNZ short .LoopB + Jmp short .NotEndB + .NoSInc: + + ;Get sample ======================== + Mov EAX,[_Src(Dec)] + Call [%$pInter] + ShL EAX,15 + Mov [EDI],EAX + Cmp byte [dspMix],MIX_FLOAT + JNE short .NoStore + FMul dword [%$fp32k] + FIStP dword [EDI] + .NoStore: + Add EDI,4 + + Dec ECX + JNZ short .NextSmp + + Mov [_Src(Idx)],ESI + Jmp .Reduce + + ;Stop playback ------------------------ + .Stop: + Mov dword [_Src(Blk)],0 + Mov dword [%$loop],0 + Sub [%$rate],ECX + Jmp .Reduce + + ;Restart sample loop ------------------ + .LoopB: + Push ESI + Mov ESI,[_Src(Loop)] + Mov [_Src(Blk)],ESI + Jmp short .Decomp + + ;Move to next BRR block --------------- + .NotEndB: + Push ESI + XOr EAX,EAX + Add word [_Src(Blk)],9 + SetC AL ;If increase wrapped around 64KB... + And AL,[%$inRAM] ; ...and pointer is not in APU RAM, + Add [1+_Src(Blk)],AX ; then increase upper word + Mov ESI,[_Src(Blk)] + + ;Decompress sample block -------------- + .Decomp: + Mov EAX,[16+_Src(Buf)] + Mov EDX,[20+_Src(Buf)] + Mov [_Src(Buf)-16],EAX + Mov [_Src(Buf)-12],EDX + Mov EAX,[24+_Src(Buf)] + Mov EDX,[28+_Src(Buf)] + Mov [_Src(Buf)-8],EAX + Mov [_Src(Buf)-4],EDX + + Push EDI,EBX + + Mov AL,[ESI] + Test byte [%$inRAM],1 + JZ short .InRAM + Mov EDI,[1+ESI] + Mov EDX,[5+ESI] + LEA ESI,[_Src(BRR)-1] + Mov [1+ESI],EDI + Mov [5+ESI],EDX + .InRAM: + + LEA EDI,[_Src(Buf)] + MovSX EDX,word [_Src(P1)] + MovSX EBX,word [_Src(P2)] + Call [pDecomp] + Mov EAX,EBX + Pop EBX,EDI + Mov [_Src(P1)],DX + Mov [_Src(P2)],AX + + Pop ESI + Jmp .NoSInc + + ;===================================== + ;Reduce samples + + .Reduce: + + Mov EDX,[%$rate] ;EDX = number of samples generated this loop + Mov ESI,mixBuf ;ESI-> buffer containing samples + Mov EDI,[%$pSrc] ;EDI-> location to store output + LEA ECX,[EDX-1] + + Cmp byte [dspChn],1 + Mov AL,[dspSize] + JE .Mono + + Cmp AL,2 + JE .S16 + Cmp AL,3 + JE .S24 + + .S32: + Mov EAX,[ECX*4+ESI] + Mov [0+ECX*8+EDI],EAX + Mov [4+ECX*8+EDI],EAX + Dec ECX + JNS short .S32 + LEA EDI,[EDX*8+EDI] + Jmp .Converted + + .S24: + Mov AX,[2+ECX*4+ESI] + Mov [1+EDI],AX + Mov [4+EDI],AX + Mov AL,[1+ECX*4+ESI] + Mov [0+EDI],AL + Mov [3+EDI],AL + Add EDI,6 + Dec ECX + JNS short .S24 + LEA EDX,[EDX*3] + LEA EDI,[EDX*2+EDI] + Jmp .Converted + + .S16: + Mov AX,[2+ECX*4+ESI] + Mov [0+ECX*4+EDI],AX + Mov [2+ECX*4+EDI],AX + Dec ECX + JNS short .S16 + LEA EDI,[EDX*4+EDI] + Jmp short .Converted + + .Mono + + Cmp AL,1 + JE .M8 + + .M16: + Mov AX,[2+ECX*4+ESI] + Mov [ECX*2+EDI],AX + Dec ECX + JNS short .M16 + LEA EDI,[EDX*2+EDI] + Jmp short .Converted + + .M8: + Mov AL,[3+ECX*4+ESI] + XOr AL,80h + Mov [ECX+EDI],AL + Dec ECX + JNS short .M8 + Add EDI,EDX + Jmp short .Converted + + .Converted: + Mov [%$pSrc],EDI + + Mov EAX,[%$loop] + Test EAX,EAX + JG .NextBuf + + .Done: + RetN [%$pSrc] + + ;========================================= + ;Initialize source playback + + .New: + Mov EDX,EAX ;Calculate playback rate + ShL EAX,16 + ShR EDX,16 + Div dword [dspRate] + Cmp EAX,100000h + JBE short .RateOK + Mov EAX,100000h + .RateOK: + Mov [_Src(Rate)],EAX + Mov dword [_Src(Dec)],0 + + Mov ESI,[%$pSrc] + Mov EAX,[%$loop] ;Convert loop block into physical pointer + LEA EAX,[EAX*9] + Add EAX,ESI + + Test word [2+%$pSrc],-1 ;Determine if pointer is an index into APU RAM + JZ short .InAPURAM + Mov CX,[2+pAPURAM] + Cmp CX,[2+%$pSrc] + JNE short .HavePtr + + .InAPURAM: + Or ESI,[pAPURAM] + MovZX EAX,AX + Or EAX,[pAPURAM] + + .HavePtr + Mov [_Src(Blk)],ESI + Mov [_Src(Loop)],EAX + + LEA EDI,[_Src(Buf)] + Mov [_Src(Idx)],EDI + + XOr EAX,EAX + Mov [EDI-16],EAX ;Erase interpolation buffer + Mov [EDI-12],EAX + Mov [EDI-8],EAX + Mov [EDI-4],EAX + + Mov ECX,ESI + Mov AL,[ESI] ;Decompress first block + ShR ECX,16 + Cmp CX,[2+pAPURAM] + JE short .InAPURAM2 + Mov ECX,[1+ESI] + Mov EDX,[5+ESI] + LEA ESI,[_Src(BRR)-1] + Mov [1+ESI],ECX + Mov [5+ESI],EDX + .InAPURAM2: + Call [pDecomp] + Mov [srcP1],DX + Mov [srcP2],BX + + Mov EAX,[dspRate] ;Return actual logical sample rate + Mul dword [srcRate] + ShRD EAX,EDX,16 + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Unpack BRR Block +; +;Decompresses a 9-byte bit-rate reduced block into 16 16-bit PCM samples +; +;In: +; AL = Block header +; ESI-> Sample Block +; EDI-> Output buffer +; EDX = Last sample of previous block +; EBX = Next to last sample +; +;Out: +; ESI-> Next Block +; EDI-> After last sample +; EDX = Last sample +; EBX = Next to last sample +; +;Destroys: +; EAX + +ALIGN 16 +UnpackBRR: + + Push ECX,EBP + + MovZX ECX,AL + Inc SI ;Inc SI so pointer will wrap around a 16-bit value + + ;Expand delta values --------------------- + RoR ECX,4 + Sub CL,12 + Mov EBP,0F000F000h + Mov CH,8 + JA short .Invalid ;Check for invalid range + + Push EDX + Mov EBP,0FFFEFFFEh + Neg CL + .Unpack: + MovSX EAX,byte [ESI] + Inc SI + Mov DL,AL + + And EAX,-16 + ShL EAX,8 ;Left justify nybbles + ShL EDX,12 + ShR EAX,CL ;Shift right with sign extension + SAR DX,CL + And EAX,EBP ;Clear LSB + And EDX,EBP + + Mov [EDI],AX + Mov [2+EDI],DX + Add EDI,4 + + Dec CH + JNZ short .Unpack + + Pop EDX + Jmp short .Unpacked + + .Invalid: + Mov CL,[ESI] + Inc SI + + ShR CL,4 ;Check sign bit of nybble + SbB EAX,EAX + ShR CL,4 + SbB AX,AX + And EAX,EBP ;-4096 for negative nybbles, 0 for positive + + Mov [EDI],EAX + Add EDI,4 + + Dec CH + JNZ short .Invalid + + ;Apply filters to ADPCM data ------------- + .Unpacked: + ShLD EAX,ECX,4 ;Get filter type + Mov CL,8 ;Decompress 8 bytes (16 nybbles) + Test AL,0Ch ;Does block use ADPCM compression? + JZ short .NoFilter ; No + + Sub EDI,32 + Test AL,04h + JZ short .Filter2 + + Test AL,08h + JNZ .Filter3 + +ALIGN 16 + ;[Delta]+[Smp-1](15/16) ------------------ + .Filter1: + Mov EBX,EDX + Neg EDX + SAR EDX,5 + LEA EAX,[EDX*2+EBX] ;EAX = ((-p1 >> 4) & ~1) + p1 + Add AX,[EDI] ;EAX += delta + MovSX EDX,AX + Mov [EDI],AX + Add EDI,2 + + Mov EBX,EDX + Neg EDX + SAR EDX,5 + LEA EAX,[EDX*2+EBX] + Add AX,[EDI] + MovSX EDX,AX + Mov [EDI],AX + Add EDI,2 + + Dec CL + JNZ short .Filter1 + Pop EBP,ECX + Ret + + .NoFilter: + MovSX EDX,word [EDI-2] + MovSX EBX,word [EDI-4] + Pop EBP,ECX + Ret + +ALIGN 16 + ;[Delta]+[Smp-1](61/32)-[Smp-2](15/16) --- + .Filter2: + MovSX EBP,word [EDI] + + ;Subtract 15/16 of second sample ------ + Mov EAX,EBX + Neg EBX + SAR EAX,5 + LEA EAX,[EAX*2+EBX] ;s = ((p2 >> 4) & ~1) + -p2 + Mov EBX,EDX + + ;Add 61/32 of last sample ------------- + LEA EAX,[EDX*2+EAX] ;s += 2 * p1 + LEA EDX,[EDX*3] + Neg EDX + Add EAX,EBP ;s += delta + SAR EDX,6 + LEA EAX,[EDX*2+EAX] ;s += ((-3 * p1) >> 5) & ~1 + MovSX EDX,AX + + ;Clamp 16-bit sample to a 17-bit value + Add EAX,65536 ;if s >= -65536 && s <= 65534 + SAR EAX,17 + JZ short .F2AOK + SetS DL ;if s > 65534 then s = -2 + MovZX EDX,DL ;if s < -65536 then s = 0 + Dec EDX + Add EDX,EDX + .F2AOK: + + MovSX EBP,word [2+EDI] + Mov [EDI],EDX + + Mov EAX,EBX + Neg EBX + SAR EAX,5 + LEA EAX,[EAX*2+EBX] + Mov EBX,EDX + + LEA EAX,[EDX*2+EAX] + LEA EDX,[EDX*3] + Neg EDX + Add EAX,EBP + SAR EDX,6 + LEA EAX,[EDX*2+EAX] + MovSX EDX,AX + + Add EAX,65536 + SAR EAX,17 + JZ short .F2BOK + SetS DL + MovZX EDX,DL + Dec EDX + Add EDX,EDX + .F2BOK: + + Mov [2+EDI],DX + Add EDI,4 + + Dec CL + JNZ .Filter2 + Pop EBP,ECX + Ret + +ALIGN 16 + ;[Delta]+[Smp-1](115/64)-[Smp-2](13/16) -- + .Filter3: + MovSX EBP,word [EDI] + + ;Subtract 52/64 of second sample ------ + Mov EAX,EBX + LEA EBX,[EBX*3] + SAR EBX,5 + Neg EAX + LEA EAX,[EBX*2+EAX] ;s = -p2 + (((p2 * 3) >> 4) & ~1) + Mov EBX,EDX + + ;Add 115/64 of last sample ------------ + LEA EAX,[EDX*2+EAX] ;s += p1 * 2 + LEA EDX,[EBX*5] + LEA EDX,[EBX*8+EDX] + Add EAX,EBP + Neg EDX + SAR EDX,7 + LEA EAX,[EDX*2+EAX] ;s += ((-13 * p1) >> 6) & ~1 + MovSX EDX,AX + + Add EAX,65536 + SAR EAX,17 + JZ short .F3AOK + SetS DL + MovZX EDX,DL + Dec EDX + Add EDX,EDX + .F3AOK: + + MovSX EBP,word [2+EDI] + Mov [EDI],EDX + + Mov EAX,EBX + LEA EBX,[EBX*3] + SAR EBX,5 + Neg EAX + LEA EAX,[EBX*2+EAX] + Mov EBX,EDX + + LEA EAX,[EDX*2+EAX] + LEA EDX,[EBX*5] + LEA EDX,[EBX*8+EDX] + Add EAX,EBP + Neg EDX + SAR EDX,7 + LEA EAX,[EDX*2+EAX] + MovSX EDX,AX + + Add EAX,65536 + SAR EAX,17 + JZ short .F3BOK + SetS DL + MovZX EDX,DL + Dec EDX + Add EDX,EDX + .F3BOK: + + Mov [2+EDI],DX + Add EDI,4 + + Dec CL + JNZ .Filter3 + Pop EBP,ECX + Ret + +; ;SSE version ----------------------------- +; MovQ XMM4,[ESI] +; MovDQA XMM6,XMM4 +; PSRLQ XMM4,4 +; PUnpckLBW XMM4,XMM6 +; +; MovDQA XMM5,XMM4 +; PUnpckLBW XMM4,XMM0 +; PUnpckHBW XMM5,XMM0 +; +; PSLLW XMM4,12 +; PSLLW XMM5,12 +; PSRAW XMM4,XMM7 +; PSRAW XMM5,XMM7 +; +; MovDQA [EDI],XMM4 +; MovDQA [16+EDI],XMM5 + +; ;MMX version ----------------------------- +; MovQ MM4,[ESI] +; MovQ MM5,MM4 +; PSRLQ MM4,4 +; MovQ MM6,MM4 +; PUnpckLBW MM4,MM5 +; PUnpckHBW MM6,MM5 +; +; MovQ MM5,MM4 +; MovQ MM7,MM6 +; +; PUnpckLBW MM4,MM0 +; PUnpckHBW MM5,MM0 +; PUnpckLBW MM6,MM0 +; PUnpckHBW MM7,MM0 +; +; PSLLW MM4,12 +; PSLLW MM5,12 +; PSLLW MM6,12 +; PSLLW MM7,12 +; +; PSRAW MM4,MM3 +; PSRAW MM5,MM3 +; PSRAW MM6,MM3 +; PSRAW MM7,MM3 +; +; MovQ [00+EDI],MM4 +; MovQ [08+EDI],MM5 +; MovQ [16+EDI],MM6 +; MovQ [24+EDI],MM7 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Unpack BRR Block (Old school method) + +ALIGN 16 +UnpackBRROld: + + Push ECX + + ;Get range ------------------------------- + Mov CL,0CFh + Inc SI + Sub CL,AL ;CL = 12 - Range (change range from << to >>) + SetNC AH ;If result is negative (invalid range) add 3 + Dec AH + And AH,30h + Add CL,AH + ShR CL,4 + + Mov CH,8 + Test AL,0Ch + JZ short .Filter0 + + Add CL,10 ;Values will be shifted right from 32-bit values + Test AL,8 + JZ short .Filter1 + + Test AL,4 + JZ .Filter2 + + Jmp .Filter3 + +ALIGN 16 + ;[Smp] ----------------------------------- + .Filter0: + XOr EAX,EAX + XOr EDX,EDX + Mov AH,[ESI] + Mov DH,AH + And AH,0F0h + ShL DH,4 + + SAR AX,CL + SAR DX,CL + Mov [EDI],AX + Mov [2+EDI],DX + Add EDI,4 + + Inc SI + + Dec CH + JNZ short .Filter0 + MovSX EDX,DX + MovSX EBX,AX + Pop ECX + Ret + +ALIGN 16 + ;[Delta]+[Smp-1](15/16) ------------------ + .Filter1: + Mov EBX,[ESI] + And BL,0F0h + ShL EBX,24 + SAR EBX,CL + + Mov EAX,EDX + IMul EAX,60 + Add EBX,EAX + SAR EBX,6 + + Mov [EDI],EBX + + Mov EDX,[ESI] + ShL EDX,28 + SAR EDX,CL + + Mov EAX,EBX + IMul EAX,60 + Add EDX,EAX + SAR EDX,6 + + Mov [2+EDI],DX + Add EDI,4 + + Inc SI + + Dec CH + JNZ short .Filter1 + Pop ECX + Ret + +ALIGN 16 + ;[Delta]+[Smp-1](61/32)-[Smp-2](30/32) --- + .Filter2: + Mov EAX,[ESI] + And AL,0F0h + ShL EAX,24 + SAR EAX,CL + + ;Subtract 15/16 of second sample ------ + IMul EBX,60 + Sub EAX,EBX + Mov EBX,EDX + + ;Add 61/32 of last sample ------------- + IMul EDX,122 + Add EAX,EDX + SAR EAX,6 + + Mov [EDI],EAX + + Mov EDX,[ESI] + ShL EDX,28 + SAR EDX,CL + + IMul EBX,60 + Sub EDX,EBX + Mov EBX,EAX + + IMul EAX,122 + Add EDX,EAX + SAR EDX,6 + + Mov [2+EDI],DX + Add EDI,4 + + Inc SI + + Dec CH + JNZ .Filter2 + Pop ECX + Ret + +ALIGN 16 + ;[Delta]+[Smp-1](115/64)-[Smp-2](52/64) -- + .Filter3: + Mov EAX,[ESI] + And AL,0F0h + ShL EAX,24 + SAR EAX,CL + + ;Subtract 13/16 of second sample ------ + IMul EBX,52 + Sub EAX,EBX + Mov EBX,EDX + + ;Add 115/64 of last sample ------------ + IMul EDX,115 + Add EAX,EDX + SAR EAX,6 + + Mov [EDI],EAX + + Mov EDX,[ESI] + ShL EDX,28 + SAR EDX,CL + + IMul EBX,52 + Sub EDX,EBX + Mov EBX,EAX + + IMul EAX,115 + Add EDX,EAX + SAR EDX,6 + + Mov [2+EDI],DX + Add EDI,4 + + Inc SI + + Dec CH + JNZ .Filter3 + Pop ECX + Ret + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Unpack Sound Source + +PROC UnpackSrc, pSmp, pBlk, num, opts, prev1, prev2 +LOCALS method, blocks, temp +USES ALL + + ;Initialize local variables -------------- + Mov EDX,[%$opts] ;Select unpacking method based on option + XOr EAX,EAX + Test DL,BRR_OLDSMP + SetZ AL + Dec EAX + And EAX,UnpackBRROld-UnpackBRR + Add EAX,UnpackBRR + Mov [%$method],EAX + + XOr EAX,EAX + Mov [%$blocks],EAX + Mov [%$temp],EAX + + ;Initialize previous samples ------------- + LEA EBX,[%$temp] + XOr EAX,EAX ;if (prev2==NULL) prev2=&temp + Cmp [%$prev2],EAX + SetNE AL + Dec EAX + And EAX,EBX + Or [%$prev2],EAX + + XOr EAX,EAX ;if (prev1==NULL) prev1=&temp + Cmp [%$prev1],EAX + SetNE AL + Dec EAX + And EAX,EBX + Or [%$prev1],EAX + + Mov EBX,[%$prev1] ;EDX = *prev1 + Mov EDX,[EBX] + Mov EBX,[%$prev2] ;EBX = *prev2 + Mov EBX,[EBX] + + Mov ECX,[%$num] + Mov EDI,[%$pSmp] + + Mov AX,[2+%$pBlk] + Cmp AX,[2+pAPURAM] + JE short .InRAM + Test AX,AX + JZ short .InRAM + + ;Decompress samples not in APU RAM ------- + .NextC: + Mov ESI,[%$pBlk] + Add dword [%$pBlk],9 + + Mov AL,[8+ESI] ;Copy BRR block into 'mixBuf' + Mov [8+mixBuf],AL + Mov EAX,[4+ESI] + Mov [4+mixBuf],EAX + Mov EAX,[ESI] + Mov [mixBuf],EAX + + Mov ESI,mixBuf ;Decompress block + Call [%$method] + Inc dword [%$blocks] + Test byte [ESI-9],1 + JNZ short .Done + + Dec ECX + JNZ short .NextC + Jmp short .Done + + ;Decompress samples in APU RAM ----------- + .InRAM: + Mov ESI,[pAPURAM] + Mov SI,[%$pBlk] + + .Next: + Mov AL,[ESI] + Push EAX + Call [%$method] + Inc dword [%$blocks] + + Pop EAX + Test EAX,1 ;Have we reached the end block? + JNZ short .Done + + Mov EAX,ESI ;Has sample pointer wrapped around? + Sub EAX,[%$pBlk] + Cmp AX,-9 + JG short .Done + Dec ECX + JNZ short .Next + .Done: + + ;Return previous samples ----------------- + Mov EAX,[%$prev2] + Mov [EAX],EBX + Mov EBX,[%$prev1] + Mov [EBX],EDX + + Mov EAX,[%$blocks] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Pack PCM into BRR Block +; +;In: +; pIn -> Samples +; pOut -> Output buffer +; opts = Options +; BRR_LINEAR = Force linear compression (filter 0) +; BRR_LOOP = Set loop flag in block header +; prev1 = Last sample of previous block +; prev2 = Next to last sample +; +;Out: +; EDX = Last sample +; EBX = Next to last sample +; +;Destroys: +; nothing + +STRUC Blk + .brr resb 16 ; [0] + .range resb 1 ;[10] + resb 3 + .diff resd 1 ;[14] + .p1 resd 1 ;[18] + .p2 resd 1 ;[1C] +ENDSTRUC +%define _Blk(v) EDI + Blk. %+ v + +PROC PackBRR, pOut, pIn, opts, prev1, prev2 +LOCALS 128,blocks +USES ECX,ESI,EDI + + LEA EDI,[%$blocks] + Or dword [32+_Blk(diff)],-1 + Or dword [64+_Blk(diff)],-1 + Or dword [96+_Blk(diff)],-1 + + Push EAX + + ;========================================= + ;Linear Compression (Filter 0) + + ;Find maximum sample value --------------- + Mov ESI,[%$pIn] + Mov CL,16 + XOr EBX,EBX + .Next0: + MovSX EAX,word [ESI] + CDQ + XOr EAX,EDX + Add ESI,2 + BSR EAX,EAX + Cmp BL,AL + CMovB EBX,EAX + + Dec CL + JNZ short .Next0 + Sub ESI,32 + + Sub BL,2 + SetS CL + Dec CL + And CL,BL + Mov [_Blk(range)],CL + + ;Reduce samples to 4-bits ---------------- + XOr EAX,EAX + XOr EBX,EBX + Mov [_Blk(diff)],EAX + Mov CH,16 + .Test0: + Mov AX,[ESI] + And AL,~1 + SAR AX,CL + Mov [EBX+_Blk(brr)],AL + Inc EBX + ShL AX,CL + Sub AX,[ESI] + Add ESI,2 + CWD + XOr AX,DX + Sub AX,DX + Add [_Blk(diff)],EAX + + Dec CH + JNZ short .Test0 + + MovSX EAX,byte [14+_Blk(brr)] ;Create previous samples + ShL EAX,CL + Mov [_Blk(p2)],EAX + MovSX EAX,byte [15+_Blk(brr)] + ShL EAX,CL + Mov [_Blk(p1)],EAX + + Add EDI,32 + + Test byte [%$opts],BRR_LINEAR + JNZ .Done + + ;========================================= + ;Filter 1 + + Mov CL,0 + .Range1: + Mov ESI,[%$pIn] + + Mov EAX,[%$prev1] + Mov EDX,[%$prev2] + Mov [_Blk(p1)],EAX + Mov [_Blk(p2)],EDX + + XOr EBX,EBX + Mov [_Blk(diff)],EBX + + Mov CH,16 + .Next1: + Mov EDX,[_Blk(p1)] + Mov EAX,EDX + Neg EDX + SAR EDX,5 + LEA EDX,[EDX*2+EAX] + + MovSX EAX,word [ESI] + And AL,~1 + Sub EAX,EDX + SAR EAX,CL + + Cmp EAX,7 + JG .Broken1 + Cmp EAX,-8 + JL .Broken1 + + Mov [EBX+_Blk(brr)],AL + Inc EBX + + ShL EAX,CL + Add EAX,EDX + + Cmp EAX,32767 + JG .Broken1 + Cmp EAX,-32768 + JL .Broken1 + + Mov EDX,[_Blk(p1)] + Mov [_Blk(p2)],EDX + Mov [_Blk(p1)],EAX + + MovSX EDX,word [ESI] + Add ESI,2 + Sub EAX,EDX + CDQ + XOr EAX,EDX + Sub EAX,EDX + Add [_Blk(diff)],EAX + + Dec CH + JNZ short .Next1 + + Mov [_Blk(range)],CL + Jmp short .Good1 + + .Broken1: + + Inc CL + Cmp CL,12 + JBE .Range1 + Or dword [_Blk(diff)], -1 + + .Good1: + Add EDI,32 + + ;========================================= + ;Filter 2 + + Mov CL,0 + .Range2: + Mov ESI,[%$pIn] + + Mov EAX,[%$prev1] + Mov EDX,[%$prev2] + Mov [_Blk(p1)],EAX + Mov [_Blk(p2)],EDX + + XOr EBX,EBX + Mov [_Blk(diff)],EBX + + Mov CH,16 + .Next2: + Mov EAX,[_Blk(p2)] + Mov EDX,EAX + Neg EAX + SAR EDX,5 + LEA EDX,[EDX*2+EAX] + Mov [_Blk(p2)],EDX + + Mov EAX,[_Blk(p1)] + Mov EDX,EAX + Add EAX,EAX + Neg EDX + LEA EDX,[EDX*2+EDX] + SAR EDX,6 + LEA EDX,[EDX*2+EAX] + + MovSX EAX,word [ESI] + And AL,~1 + Sub EAX,EDX + Sub EAX,[_Blk(p2)] + SAR EAX,CL + + Cmp EAX,7 + JG .Broken2 + Cmp EAX,-8 + JL .Broken2 + + Mov [EBX+_Blk(brr)],AL + Inc EBX + + ShL EAX,CL + Add EAX,EDX + Add EAX,[_Blk(p2)] + + Cmp EAX,32767 + JG .Broken2 + Cmp EAX,-32768 + JL .Broken2 + + Mov EDX,[_Blk(p1)] + Mov [_Blk(p2)],EDX + Mov [_Blk(p1)],EAX + + MovSX EDX,word [ESI] + Add ESI,2 + Sub EAX,EDX + CDQ + XOr EAX,EDX + Sub EAX,EDX + Add [_Blk(diff)],EAX + + Dec CH + JNZ .Next2 + + Mov [_Blk(range)],CL + Jmp short .Good2 + + .Broken2: + + Inc CL + Cmp CL,12 + JBE .Range2 + Or dword [_Blk(diff)],-1 + + .Good2: + Add EDI,32 + + ;========================================= + ;Filter 3 + + Mov CL,0 + .Range3: + Mov ESI,[%$pIn] + + Mov EAX,[%$prev1] + Mov EDX,[%$prev2] + Mov [_Blk(p1)],EAX + Mov [_Blk(p2)],EDX + + XOr EBX,EBX + Mov [_Blk(diff)],EBX + + Mov CH,16 + .Next3: + Mov EDX,[_Blk(p2)] + Mov EAX,[_Blk(p2)] + LEA EDX,[EDX*3] + SAR EDX,5 + Neg EAX + LEA EAX,[EDX*2+EAX] + Mov [_Blk(p2)],EAX + + Mov EDX,[_Blk(p1)] + LEA EAX,[EDX*5] + LEA EAX,[EDX*8+EAX] + Add EDX,EDX + Neg EAX + SAR EAX,7 + LEA EDX,[EAX*2+EDX] + + MovSX EAX,word [ESI] + And AL,~1 + Sub EAX,EDX + Sub EAX,[_Blk(p2)] + SAR EAX,CL + + Cmp EAX,7 + JG .Broken3 + Cmp EAX,-8 + JL .Broken3 + + Mov [EBX+_Blk(brr)],AL + Inc EBX + + ShL EAX,CL + Add EAX,EDX + Add EAX,[_Blk(p2)] + + Cmp EAX,32767 + JG .Broken3 + Cmp EAX,-32768 + JL .Broken3 + + Mov EDX,[_Blk(p1)] + Mov [_Blk(p2)],EDX + Mov [_Blk(p1)],EAX + + MovSX EDX,word [ESI] + Add ESI,2 + Sub EAX,EDX + CDQ + XOr EAX,EDX + Sub EAX,EDX + Add [_Blk(diff)],EAX + + Dec CH + JNZ .Next3 + + Mov [_Blk(range)],CL + Jmp short .Good3 + + .Broken3: + + Inc CL + Cmp CL,12 + JBE .Range3 + Or dword [_Blk(diff)],-1 + + .Good3: + .Done: + + ;========================================= + ;Store Final Block + + ;Find best filter ------------------------ + XOr EDX,EDX ;EDX = Filter + LEA ESI,[%$blocks] ;ESI -> First filter block + Mov EAX,[Blk.diff+ESI] ;EAX = Total difference for first filter + Mov EBX,ESI ;EBX -> Array of block stuctures + + Mov CL,1 + .NextB: + Add EBX,32 + Cmp EAX,[Blk.diff+EBX] ;Is this a better filter? + JBE short .Better ; Nope, keep what we have + Mov EAX,[Blk.diff+EBX] + Mov ESI,EBX + Mov DL,CL + .Better: + + Inc CL + Cmp CL,4 + JB short .NextB + + ;Create header byte ---------------------- + Mov EDI,[%$pOut] + + Mov AL,[Blk.range+ESI] ;Get BRR range + ShL AL,4 + + ShL DL,2 ;Get filter number + Or AL,DL + + Mov DL,[%$opts] ;Set looping flag + And DL,BRR_LOOP + Or AL,DL + + Mov [EDI],AL ;Store header byte + Inc EDI + + ;Pack bytes into nybbles ----------------- + Mov ECX,7 + .Copy: + Mov AL,[ECX*2+ESI] + Mov DL,[1+ECX*2+ESI] + ShL AL,4 + And DL,0Fh ;Remove sign bits + Or AL,DL + Mov [ECX+EDI],AL + + Dec ECX + JNS short .Copy + + ;Return previous samples ----------------- + Mov EDX,[Blk.p1+ESI] + Mov EBX,[Blk.p2+ESI] + + Pop EAX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Pack PCM Samples into BRR Blocks + +;-------------------------------------------- +;Copies samples from an input buffer into a temporary buffer. If the input buffer doesn't contain +;enough samples to fill the temp, the remaining samples in the temp buffer are filled with silence. +; +;In: +; EDI-> Buffer to store samples in +; ESI-> Buffer containing samples +; EAX = Number of samples in input buffer +; +;Out: +; EAX = Number of samples left in input buffer +; ESI-> Next samples + +PROC CopySmp +USES ECX,EDX,EDI + + Push EAX + + Sub EAX,16 ;if (EAX >= 16) EAX = 16 + CDQ + And EAX,EDX + Add EAX,16 + + Mov ECX,EAX ;Copy samples to temp buffer + Mov EDX,EAX + Rep MovSW + + Mov EAX,ECX ;Fill remaning samples with 0 + Or ECX,16 + Sub ECX,EDX + Rep StoSW + + Pop EAX + Sub EAX,EDX ;Subtract copied samples from remaining count + +ENDP + +;-------------------------------------------- +;Interpolates samples using sinc +; +;In: +; pDest -> Buffer to store sixteen interpolated samples +; pSrc -> Buffer containing samples to interpolate from +; pInter-> Buffer containing eight samples used for interpolation function +; rate = Rate of sample increase +; delta = Interpolation delta +; +;Out: +; EAX = New delta +; EBX = New input pointer + +PROC Interpolate, pDest, pSrc, pInter, rate, delta +LOCALS cnt +USES ECX,EDX,ESI,EDI + + Mov EDX,[%$rate] + Mov EDI,[%$pDest] + + Test EDX,EDX + JZ short .Copy + + Mov EBX,[%$pSrc] + Mov ESI,[%$pInter] + Mov byte [%$cnt],16 + .Next: + Add [%$delta],EDX + JNC short .NotNext + Add EBX,2 + + Mov EAX,[2+ESI] + Mov ECX,[6+ESI] + Mov [ESI],EAX + Mov [4+ESI],ECX + + Mov EAX,[10+ESI] + Mov CX,[14+ESI] + Mov [8+ESI],EAX + Mov [12+ESI],ECX + + Mov AX,[EBX] + Mov [14+ESI],AX + .NotNext: + + MovZX EAX,word [2+%$delta] + Call SincF + FIStP word [EDI] + Add EDI,2 + + Dec byte [%$cnt] + JNZ short .Next + RetS [%$delta] + + .Copy: + Mov ESI,[%$pSrc] + LEA ECX,[EDX+8] + Mov EAX,[%$delta] + Rep MovSD + Mov EBX,ESI + +ENDP + +PROC PackSrc, pBlk, pSmp, pLen, pLoop, opts, prev1, prev2 +LOCALS len,loop,delta,rate,p1,p2,32,smp,32,inter +USES ALL + + ;Clear temp buffers ---------------------- + XOr EAX,EAX + LEA EDI,[%$smp] + LEA ECX,[EAX+8] + Rep StoSD + + Mov [%$inter],EAX ;Reset the first three samples in the interpolation + Mov [4+%$inter],EAX ; buffer incase there's no preloop section. + + ;Get previous samples -------------------- + Mov EBX,[%$prev2] + Mov EDX,[%$prev1] + + Test EBX,EBX ;If prev1 or prev2 is NULL, use default sample values + SetZ CH + Test EDX,EDX + SetZ CL + Test ECX,ECX + JNZ short .NoPrev + Mov EBX,[EBX] + Mov EDX,[EDX] + Mov [%$p2],EBX + Mov [%$p1],EDX + Or byte [%$opts],BRR_CONT + Jmp short .GotPrev + .NoPrev: + Mov [%$p2],EAX + Mov [%$p1],EAX + Mov [%$prev1],EAX + .GotPrev: + + ;Copy input length ----------------------- + Mov EBX,[%$pLen] + Mov EDX,[EBX] ;EDX = length of input + Test EDX,EDX + JZ .Error + Mov [%$len],EDX + Mov [EBX],EAX ;Set output length to 0 + + ;Create initial block of silence --------- + Test byte [%$opts],BRR_NOINIT|BRR_CONT + JNZ short .NoInit + Mov EDI,[%$pBlk] + Mov byte [EDI],0C0h + Mov [1+EDI],EAX + Mov [5+EDI],EAX + Add dword [%$pBlk],9 + Inc dword [EBX] ;Increase output length + .NoInit: + + ;Change options based on prev1 ---------- + Test byte [%$opts],BRR_CONT + JZ short .NoCont + Mov [%$pLoop],EAX ;Ignore pLoop if continuing compression + Jmp short .Cont + .NoCont: + Or byte [%$opts],BRR_END ;Force end block flag to be set + .Cont: + + Or EAX,[%$pLoop] + JNZ short .Loops + + ;One-shot sounds ====================== + LEA EAX,[EDX+15] ;Store final length + ShR EAX,4 + Add [EBX],EAX + + Mov ESI,[%$pSmp] ;Location of input samples + LEA EDI,[%$smp] ;Location to store temp samples (used by CopySmp) + + Mov ECX,[%$opts] + Mov AL,BRR_CONT|BRR_NOINIT + And AL,CL + Cmp AL,BRR_CONT|BRR_NOINIT + JE short .NoLinearO + Cmp AL,BRR_NOINIT + JNE short .NoLinearO + Or CL,BRR_LINEAR ;Force linear compression on the first block + .NoLinearO: + + Mov EAX,EDX + Mov EDX,[%$p1] + Mov EBX,[%$p2] + + .NextO: + Test EAX,EAX + JZ .Done + Call CopySmp + Call PackBRR,[%$pBlk],EDI,ECX,EDX,EBX + Add dword [%$pBlk],9 + Mov ECX,[%$opts] + Jmp short .NextO + + .Loops: + ;Looping sounds ======================= + Mov [%$loop],EAX + + Mov ECX,EDX + Sub ECX,EAX ;Loop must start before end of input + JBE .Error + Cmp ECX,16 ;Looping section must be at least 16 samples + JL .Error + + LEA EBX,[EAX+15] + ShR EBX,4 + Mov ESI,[%$pLen] + Mov EDI,[%$pLoop] + Add EBX,[ESI] + Mov [EDI],EBX ;pLoop -> Loop starting block + + Add ECX,15 + ShR ECX,4 + Add EBX,ECX + Mov [ESI],EBX + + Mov ECX,[%$opts] + Test CL,BRR_NOINIT + JE short .NoLinearL + Or ECX,BRR_LINEAR ;Force linear compression on the first block + .NoLinearL: + + ;Preloop section ---------------------- + Test EAX,EAX + JZ short .NoPreLoop + + XOr EDI,EDI ;If preloop is not a mutiple of 16, insert silence + Sub EDI,EAX + And EDI,0Fh + LEA EDI,[EDI*2+%$smp] + Mov ESI,[%$pSmp] + + Push ECX ;Copy samples to fill 16-sample buffer + Mov ECX,EAX + And EAX,-16 + And ECX,0Fh + Rep MovSW + Pop ECX + + LEA EDI,[%$smp] + Mov EDX,[%$p1] + Mov EBX,[%$p2] + Jmp short .StartPL + + .NextPL: ;Pack preloop samples + Call CopySmp + .StartPL: + Call PackBRR,[%$pBlk],EDI,ECX,EDX,EBX + Add dword [%$pBlk],9 + Mov ECX,[%$opts] + Test EAX,EAX + JNZ short .NextPL + + Mov [%$pSmp],ESI ;Save values + Mov [%$p1],EDX + Mov [%$p2],EBX + + Mov EAX,[ESI-6] + Mov EDX,[ESI-2] + Mov [%$inter],EAX + Mov [4+%$inter],DX + + .NoPreLoop: + + ;Looping section ---------------------- + Mov ESI,[%$pSmp] + Mov EAX,[ESI] + Mov EDX,[4+ESI] + Mov BX,[8+ESI] + Mov [6+%$inter],EAX + Mov [10+%$inter],EDX + Mov [14+%$inter],BX + + XOr EDX,EDX + Mov [%$delta],EDX + Mov EAX,[%$len] + Sub EAX,[%$loop] + Test AL,0Fh ;If the loop length is a multiple of 16, no + JZ short .NoInter ; interpolation is needed + LEA EBX,[EAX+15] + Mov EDX,EAX + And EBX,-16 ;EBX = length rounded up to next 16 sample boundary + XOr EAX,EAX ;EDX:EAX = length << 32 + Div EBX + Mov EDX,EAX ;EDX = [0.32] interpolation rate + .NoInter: + Mov [%$rate],EDX + + LEA EDI,[%$smp] + LEA ESI,[%$inter] + XOr EAX,EAX + Sub EAX,EDX + Mov EBX,[%$pSmp] + + .NextL: + Call Interpolate,EDI,[%$pSmp],ESI,[%$rate],[%$delta] + Mov [%$pSmp],EBX + Mov [%$delta],EAX + + Call PackBRR,[%$pBlk],EDI,ECX,[%$p1],[%$p2] + Mov [%$p1],EDX + Mov [%$p2],EBX + Add dword [%$pBlk],9 + Mov ECX,[%$opts] + Jmp short .NextL + + Mov EDI,[%$pBlk] + Or byte [EDI-9],BRR_LOOP ;TODO: Move this to the end of the function + + .Done: + + ;Return previous samples ----------------- + Mov CL,[%$opts] + Test CL,BRR_CONT + JZ short .NoPrevs + Mov EAX,[%$prev1] + Mov [EAX],EDX + Mov EAX,[%$prev2] + Mov [EAX],EBX + .NoPrevs: + + ;Set end block flag ---------------------- + Mov EAX,[%$pBlk] ;EAX -> end of block buffer + Test CL,BRR_END ;Check for user to specify end flag + Retc Z + + Or byte [EAX-9],BRR_END + + .Error: + +ENDP PackSrc + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;VMax to Decible + +PROC VMax2dBf, pList +LOCALS ftemp +USES ECX,EDX,ESI,EDI + + Mov EDI,[%$pList] ;EDI -> Output array + Mov ESI,mix+vMaxL ;ESI -> vMax values + XOr EAX,EAX + Mov CL,8 ;Eight voices + Mov dword [%$ftemp],40C00000h ;6.0 + FLd dword [%$ftemp] + + Mov dword [%$ftemp],42B40000h ;90.0 + FLd dword [%$ftemp] + Mov EDX,42C00000h ;EDX = -96.0 (negative infinity decibles) + + ;Floating-point Format =================== + ; + ; dB = 6.0 (Log vMax) - 90.0 + ; 2 + + ;Voice output ---------------------------- + .Voice: + Mov [EDI],EDX ;Default to -96.0 (-inf dB) + Cmp EAX,[ESI] ;Is vMaxL > -inf dB? + JE short .NoRangeL ; No, Move onto vMaxR + FLd ST1 + FILd dword [ESI] ;Load vMaxL into FPU + FYL2X ;Compute 6.0*Log2(vMaxL) + FSub ST1 ;Subtract 90.0 from result + FStP dword [EDI] ;Store dB as floating-point + Mov [ESI],EAX ;Reset vMaxL + .NoRangeL: + + Mov [4+EDI],EDX ;Do the same thing for vMaxR + Cmp EAX,[4+ESI] + JE short .NoRangeR + FLd ST + FILd dword [4+ESI] + FYL2X + FSub ST1 + FStP dword [4+EDI] + Mov [4+ESI],EAX + .NoRangeR: + Add EDI,8 + Sub ESI,-128 + + Dec CL + JNZ short .Voice + + ;Main output (mMax) ---------------------- + Mov ESI,mMaxL + Mov [EDI],EDX + Cmp EAX,[ESI] + JE short .NoRangeML + FLd ST1 + FILd dword [ESI] + FYL2X + FSub ST1 + FStP dword [EDI] + Mov [ESI],EAX + .NoRangeML: + + Mov ESI,mMaxR + Mov [4+EDI],EDX + Cmp EAX,[ESI] + JE short .NoRangeMR + FLd ST1 + FILd dword [ESI] + FYL2X + FSub ST1 + FStP dword [4+EDI] + Mov [ESI],EAX + .NoRangeMR: + + FStP ST + FStP ST + +ENDP VMax2dBf + +PROC VMax2dBi, pList +LOCALS ftemp +USES ECX,EDX,ESI,EDI + + Mov EDI,[%$pList] ;EDI -> Output array + Mov ESI,mix+vMaxL ;ESI -> vMax values + XOr EAX,EAX + Mov CL,8 ;Eight voices + Mov dword [%$ftemp],40C00000h ;6.0 + FLd dword [%$ftemp] + + Mov dword [%$ftemp],47800000h ;65536.0 + FLd dword [%$ftemp] + + ;Integer format ========================== + ; + ; dB = (6.0 (Log vMax) + 6.0) * 65536 + ; 2 + + ;Voice output ---------------------------- + .Voice: + Mov [EDI],EAX ;Default to 0 (negative infinity) + Cmp EAX,[ESI] ;Is vMaxL > -inf dB? + JE short .NoRangeL ; No, Move onto vMaxR + FLd ST1 + FILd dword [ESI] ;Load vMaxL into FPU + FYL2X ;Compute 6.0*Log2(vMaxL) + FAdd ST,ST2 ;Add 6.0 + FMul ST1 ;Multiply by 65536 + FIStP dword [EDI] ;Store dB as 16.16 integer + Mov [ESI],EAX ;Reset vMaxL + .NoRangeL: + + Mov [4+EDI],EAX ;Do the same thing for vMaxR + Cmp EAX,[4+ESI] + JE short .NoRangeR + FLd ST1 + FILd dword [4+ESI] + FYL2X + FAdd ST,ST2 + FMul ST1 + FIStP dword [4+EDI] + Mov [4+ESI],EAX + .NoRangeR: + Add EDI,8 + Sub ESI,-128 + + Dec CL + JNZ short .Voice + + ;Main output (mMax) ---------------------- + Mov ESI,mMaxL + Mov [EDI],EAX + Cmp EAX,[ESI] + JE short .NoRangeML + FLd ST1 + FILd dword [ESI] + FYL2X + FAdd ST,ST2 + FMul ST1 + FIStP dword [EDI] + Mov [ESI],EAX + .NoRangeML: + + Mov ESI,mMaxR + Mov [4+EDI],EAX + Cmp EAX,[ESI] + JE short .NoRangeMR + FLd ST1 + FILd dword [ESI] + FYL2X + FAdd ST,ST2 + FMul ST1 + FIStP dword [4+EDI] + Mov [ESI],EAX + .NoRangeMR: + + FStP ST + FStP ST + +ENDP VMax2dBi
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/DSP.Inc b/lib/snesapu/SNES/SNESAPU/DSP.Inc new file mode 100644 index 0000000000..34ed3f2816 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/DSP.Inc @@ -0,0 +1,877 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Program: SNES Digital Signal Processor Emulator +;Platform: 80386 / MMX +;Programmer: Anti Resonance +; +;"SNES" and "Super Nintendo Entertainment System" are trademarks of Nintendo Co., Limited and its +;subsidiary companies. +; +;This library is free software; you can redistribute it and/or modify it under the terms of the +;GNU Lesser General Public License as published by the Free Software Foundation; either version +;2.1 of the License, or (at your option) any later version. +; +;This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +;without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +;See the GNU Lesser General Public License for more details. +; +;You should have received a copy of the GNU Lesser General Public License along with this +;library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +;Boston, MA 02111-1307 USA +; +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Revision History: +; +; 2005.10.28 SNESAPU v3.0 +; + Added 8-point sinc interpolation +; + Added per voice anti-aliasing +; + Added quadraphonic output to the floating-point mixing routine +; + Removed the branches in the volume ramping code in EmuDSPF +; + Updated interpolation functions so PlaySrc could use them +; + Added -6dB attenuation to PlaySrc, so single sounds aren't so loud compared to the relative +; quietness of songs +; - Volume ramping was disabled when stereo controls were turned off (not sure why) +; - Fixed reverse stereo not working when stereo conrols were enabled, for real this time +; + Changed debugging interface +; + Removed option for generating Gaussian curve +; + DSP register jump table gets initialized at run-time +; - Fixed a bug in EmuDSP where muting the output buffer would crash +; - Fixed a bug when resetting the DSP with the FLG register +; + Made floating-point constants local variables in most functions +; + Created macros to calculate relative displacments for some global variables +; - Fixed DSPIn to store the new value in the registers after UpdateDSP is called. Also moved +; UpdateDSP to get called only if the register handler will be called. +; +; 2004.08.01 SNESAPU v2.1 +; + Improved monaural volume setting +; + Removed BRR look-up table +; + Moved all checks for SPC_NODSP from SPC700.Asm +; - Envelope height in direct gain mode could be garbage +; - Added a missing 16-bit clamp to the MMX Gaussian interpolation +; - Added a 16-bit clamp to the MMX FIR filter to work around a distortion bug in DQK +; - StartSrc wasn't wrapping source directory around 64k +; +; 2004.01.26 SNESAPU v2.0 +; + Added 24- and 32-bit sample support to MMX +; + Hardware anomalies emulates the DSP at 32kHz then upsamples the output +; + Replaced fake low-pass filter with a 32-tap FIR filter +; + Writing to the DSP registers from the SPC700 automatically calls EmuDSP. This should help keep +; the DSP and SPC700 in better syncronization. +; + Removed self modifying code +; + Moved fade out from APU.Asm +; - Output is inverted only when hardware anomalies are enabled +; - Obtained a more accurate copy of the gaussian look-up table used in the SNES +; - Gaussian interpolation correctly clears the LSB in MMX mixing mode +; - When using 4-point interpolation, source playback begins with the fourth sample +; - Removed 17-bit clamp from filter 1 +; - Envelopes are not based on a 33kHz clock (duh...) +; - Envelopes now use a sample counter instead of a fractional clock +; - FIR filter coefficients were getting applied in reverse order +; - Filtered samples get shifted right before accumulation, and LSB is cleared after accum. +; - Fixed PckWav (it was seriously broken) +; - Added support for BRR_??? options to PckWav +; +; 2003.07.29 SNESAPU v1.01 +; - Voice's inactive flag wasn't getting set after a key off +; +; 2003.06.30 +; - Made some minor changes to RestoreDSP +; - Fixed voices' pitch not getting changed in SetDSPOpt +; - Fixed a bug in the floating-point DSP volume register handler when STEREO = 0 +; +; 2003.06.18 SNESAPU v0.99c +; - Fixed interger overflow in MMX FIR filter +; +; 2003.06.14 SNESAPU v0.99b +; + Added support for old amplification values (<= 256) +; +; 2003.05.19 SNESAPU v0.99a +; - Fixed a bug in the i386 FIR filter +; +; 2003.04.28 SNESAPU v0.99 +; + InitDSP will select 386 or MMX depending on CPU +; + Added 16 and 24-bit sample output to EmuDSPF +; + Changed SetDSPAmp to decibels +; - Fixed several bugs in the envelope code so envelopes now work the same as the SNES (fixes a +; lot of songs) +; - Stopped caching loop points (fixes Castlevania IV, which likes to change sources after a +; key on) +; - Added clamping to the pitch modulation code (fixes CT) +; - Fixed a bug in KON/KOF emulation +; - Changed 16-bit mixing process to emulate the SNES +; - Improved low-pass filtering and simulated RF interference +; - Removed floating-point support from SetDSPAmp +; - Fixed another volume bug in SetDSPOpt when switching between integer and float routines +; +; 2003.02.10 SNESAPU v0.98 +; + Converted to NASM +; + Added SetDSPDbg, SaveDSP, RestoreDSP, SetDSPReg +; + Added a loop to EmuDSP so the number of samples passed in can be any value +; + Added floating-point support to SetDSPAmp +; + Added micro ramping to the channel volumes in the floating-point routine +; + Added 32-bit IEEE 754 output +; - Added a 17-bit clamp to the UnpckWav (fixes Hiouden; CT and FF6 are iffy now) +; - Added linear interpolation to FIR filter (fixes some songs that turned to noise) +; - SetDSPOpt wasn't clearing EAX, which was causing dspDecmp to sometimes contain a bad value +; - ENVX always gets updated, regardless of ADSR/GAIN flag (fixes Clue) +; +; 2001.10.02 SNESAPU v0.96 +; - freqTab was built from the wrong values (Hz instead of samples) +; - rateTab now uses 32-bits of precision instead of 16 +; - Values in rateTab >= 1.0 are now set to 0.99 to reduce the ill effects of fast envelopes when +; the sample rate is < 32kHz +; - Reset all internal pitch values when PMON is written to (fixes Asterix) +; - Clear bits in KOF if voice is inactive (improves EOS detection) +; - Switched to a static lookup table generated by the SNES for Gaussian interpolation +; - Damn near cracked the sample decompression +; - Optimized code a bit in EmuDSPN and inadvertently fixed a bug that was causing some songs to +; break after seeking +; - EmuDSPN wasn't checking the loop counter after an envelope update was performed (seeking no +; longer appears to hang) +; +; 2001.05.15 SNESAPU v0.95 +; + Added 4-point Gaussian interpolation +; + EmuDSPN now decompresses samples, so seeking doesn't lose voices +; + Added an option to FixSeek to disable resetting voices +; + Reverse stereo swaps the left/right volumes instead of physically swapping samples +; + More minor optimizations +; - Changed the sample decompression routine. Less songs should have problems. +; - Echo delay was incorrect for most sample rates +; - Did something to fix the noise generator not working in some songs +; +; 2001.03.02 SNESAPU v0.92 +; - Fixed the low-pass filter (I'm amazed it was sort of working at all) +; - Added a hack to UnpckWav to subtract 3 from the range if > 12 +; - Forgot that the DSP needs to be limited to 64K as well (Secret of Evermore doesn't crash) +; - Fixed a problem in the MMX dynamic code allocation +; - FixSeek wasn't resetting the DSP X registers (seeking works in Der Langrisser) +; +; 2001.01.26 SNESAPU v0.90 +; + Added voice monitor and VMax2dB for visualization +; + Added SetDSPPitch so people with SB/SBPro's and compatibles can hear the correct tone +; + Added SetDSPStereo for headphone listeners +; + Added SetDSPEFBCT to make the echo sound more natural +; + Added UnpackWave so waveforms could be decompressed by external programs +; + Added 32-bit floating point mixer for people with 24/32-bit sound cards +; + Added a reverse stereo option +; + Added GetProcInfo to detect 3DNow! and SSE capabilites +; + Added EmuDSP which automatically calls the mixing routine set in SetDSPOpt +; + Made a lot of minor optimizations to DSPIn and EmuDSP +; + Made separate register handlers in DSPIn for mono and floating-point +; - Changed SetDSPOpt to use individual parameters instead of a record +; - SetDSPOpt performs range checking +; - Fixed configuration implementation so options don't need to be reset for every song and they +; can be changed between calls to EmuDSP +; - Fixed mono related code (sounds better now) +; - Echo filter was never getting turned on +; - Implemented a better low-pass filter +; +; 2000.05.22 SNESAmp v1.2 +; + Added a low-pass filter +; + Added output monitor +; + Optimized the waveform code in EmuDSPN +; + Added FixSeek +; + SetDSPVol now uses a 16-bit value +; - FixDSP was marking voices as keyed even if the pitch was 0 +; - Correctly implemented echo volume (should be perfect now) +; +; 2000.04.05 SNESAmp v1.0 +; - Voices with a pitch of 0 can't be keyed on +; - ENVX becomes 0 when voice is keyed off +; - ENVX is set to 0 when envelope is in GAIN mode +; - Account for sample blocks with a range greater than 16-bits (ADPCM still not correct) +; +; 2000.03.17 SNESAmp v0.9 +; Copyright (C)1999-2006 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +%define DSP_INC + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Equates + +;CPU types ---------------------------------- +CPU_MMX EQU 01h +CPU_3DNOW EQU 02h +CPU_SIMD EQU 04h + +;Mixing routines (see source for additional notes) +MIX_NONE EQU 0 ;No mixing +MIX_INT EQU 1 ;Use integer instructions +MIX_FLOAT EQU 3 ;Use floating-point instructions + +;Interpolation routines --------------------- +INT_NONE EQU 0 ;None +INT_LINEAR EQU 1 ;Linear +INT_CUBIC EQU 2 ;Cubic +INT_GAUSS EQU 3 ;4-point Gaussian +INT_SINC EQU 4 ;8-point Sinc + +;DSP options -------------------------------- +OPT_ANALOG EQU 01h ;Simulate anomalies of the analog hardware +OPT_OLDSMP EQU 02h ;Old sample decompression routine +OPT_SURND EQU 04h ;"Surround" sound +OPT_REVERSE EQU 08h ;Reverse stereo samples +OPT_NOECHO EQU 10h ;Disable echo (must not be bit 0 or 5, see disEcho) +OPT_FILTER EQU 20h ;Pass each voice through an anti-aliasing filter + +;Automatic Amplification Reduction ---------- +AAR_TOFF EQU 0 ;Disable AAR +AAR_TON EQU 1 ;Enable AAR +AAR_TINC EQU 2 ;Enable AAR with auto increase +AAR_TDYN EQU 3 ;Enable dynamic AAR (not currently supported) +AAR_TYPE EQU 3 ;Mask for type of AAR +AAR_INC EQU 4 ;Auto increase is enabled +AAR_ON EQU 8 ;AAR is enabled, amp level can be adjusted + +;Voice muting ------------------------------- +MUTE_GET EQU -1 ;Do nothing (use to get mute state) +MUTE_OFF EQU 0 ;Unmute +MUTE_ON EQU 1 ;Mute +MUTE_SOLO EQU 2 ;Unmute voice and mute all others +MUTE_TOGGLE EQU 3 ;Toggles current mute state + +;PackSrc options ---------------------------- +BRR_END EQU 01h ;Set the end flag in the last block +BRR_LOOP EQU 02h ;Set the loop flag in all blocks +BRR_LINEAR EQU 04h ;Use linear compression for all blocks +BRR_NOINIT EQU 10h ;Don't create an initial block of silence +BRR_CONT EQU 80h ;Continue compression + +;UnpackSrc options -------------------------- +BRR_OLDSMP EQU 80h ;Use old sample decompression routine + +;Mixing flags ------------------------------- +MFLG_MUTE EQU 01h ;Voice is muted +MFLG_KOFF EQU 04h ;Voice is in the process of keying off +MFLG_OFF EQU 08h ;Voice is currently inactive + +;Envelope mode masks ------------------------ +E_ADJ EQU 00001b ;Adjustment: Constant(1/64 or 1/256) / Exp.(x255/256) +E_DIR EQU 00010b ;Direction: Decrease / Increase +E_DEST EQU 00100b ;Destination: Default(0 or 1) / Other(x/8 or .75) +E_ADSR EQU 01000b ;Envelope mode: Gain/ADSR +E_IDLE EQU 80h ;Envelope speed is set to 0 + +;Envelope modes ----------------------------- +E_DEC EQU 00000b ;Linear decrease +E_EXP EQU 00001b ;Exponential decrease +E_INC EQU 00010b ;Linear increase +E_BENT EQU 00110b ;Bent line increase +E_DIRECT EQU 00111b ;Direct gain +E_REL EQU 01000b ;Release mode +E_SUST EQU 01001b ;Sustain mode +E_ATT EQU 01010b ;Attack mode +E_DECAY EQU 01101b ;Decay mode + +;DSP debugging options ---------------------- +DSP_HALT EQU 80h ;Halt DSP emulation + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Structures + +;DSP registers ------------------------------ +STRUC DSPVoice + volL resb 1 ;Volume Left + volR resb 1 ;Volume Right + pitch resw 1 ;Pitch (Rate/32000) [2.12] + srcn resb 1 ;Sound source being played back + adsr1 resb 1 ;ADSR volume envelope (attack and decay rate) + adsr2 resb 1 ; " " (sustain level and rate) + gain resb 1 ;Envelope gain rate (if not using ADSR) + envx resb 1 ;Current envelope heigth [.7] + outx resb 1 ;Current sample being output [-.7] + resb 2 +ENDSTRUC + +STRUC DSPReg + resb 12 ;Settings for voice 0 + mvolL resb 1 ;Main Volume Left [-.7] + efb resb 1 ;Echo Feedback [-.7] + resb 1 + fc resb 1 ;Filter Coefficient 0 + resb 12 + mvolR resb 1 ;Main Volume Right [-.7] + resb 15 + evolL resb 1 ;Echo Volume Left [-.7] + pmon resb 1 ;Pitch Modulation on/off for each voice + resb 14 + evolR resb 1 ;Echo Volume Right [-.7] + non resb 1 ;Noise output on/off for each voice + resb 14 + kon resb 1 ;Key On for each voice + eon resb 1 ;Echo on/off for each voice + resb 14 + kof resb 1 ;Key Off for each voice + dir resb 1 ;Page containing source directory + resb 14 + flg resb 1 ;DSP flags and noise frequency + esa resb 1 ;Starting page used to store echo samples + resb 14 + endx resb 1 ;End block of sound source has been decoded + edl resb 1 ;Echo Delay in ms >> 4 + resb 2 +ENDSTRUC + +;Internal mixing settings ------------------- +STRUC VoiceMix + ;Visualization --- 08 + vMax + vMaxL resd 1 ;Maximum absolute sample output + vMaxR resd 1 + ;Voice ----------- 04 + pDSPV resd 1 ;-> voice registers in DSP + ;Waveform -------- 06 + bCur resd 1 ;-> current block + bHdr resb 1 ;Block Header for current block + mFlg resb 1 ;Mixing flags + ;Envelope -------- 22 + eMode resb 1 ;[3-0] Current envelope mode + ;[6-4] ADSR mode to switch into from Gain + ;[7] Envelope is not changing + eRIdx resb 1 ;Index in rateTab + eRate resd 1 ;Rate of envelope adjustment [16.16] + eCnt resd 1 ;Sample counter [16.16] + eVal resd 1 ;Current envelope height [.11] + eAdj resd 1 ;Amount to adjust envelope height + eDest resd 1 ;Envelope Destination [.11] + ;Samples --------- 56 + sP1 resw 1 ;Last sample decompressed (prev1) + sP2 resw 1 ;Second to last sample (prev2) + sIdx resd 1 ;-> current sample in sBuf + sBufP resw 8 ;Last 8 samples from previous block + sBuf resw 16 ;32 bytes for decompressed BRR block + ;Mixing ---------- 32 + mTgtL resd 1 ;Target volume (floating-point mixing only) + mTgtR resd 1 ; " " + mChnL resd 1 ;Channel volume [-24.7] + mChnR resd 1 ; " " + mRate resd 1 ;Pitch Rate after modulation [16.16] + mDec resd 1 ;Pitch Decimal [.16] (used as delta for interpolation) + mOut resd 1 ;Last sample output before chn vol (used for pitch mod) + resd 1 ;reserved +ENDSTRUC + +;Saved state (see SaveDSP) ------------------ +STRUC DSPState + .pReg resd 1 ;[0.0] -> DSP registers (128 bytes) + .pVoice resd 1 ;[0.4] -> Internal mixing settings (1k) + .pEcho resd 1 ;[0.8] -> echo buffer (bytes = sample rate * 1.92) + .amp resd 1 ;[0.C] Amplification (AAR is stored in upper 4 bits) +ENDSTRUC + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Public Variables + +PUBLIC dsp ;DSP registers +PUBLIC mix ;Mixing structures for each voice + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Exported Functions + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get Processor Information +; +;Detects if processor has MMX, 3DNow!, or SSE capabilities +; +;In: +; nothing +; +;Out: +; EAX = see CPU_??? equates +; +;Destroys: +; nothing + +PUBLIC GetProcInfo, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Initialize DSP +; +;Creates the lookup tables for interpolation, and sets the default mixing settings: +; +; mixType = MIX_INT +; numChn = 2 +; bits = 16 +; rate = 32000 +; inter = INT_GAUSS +; opts = 0 +; +;Destroys: +; EAX,ST0-7 + +PUBLIC InitDSP, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Reset DSP +; +;Resets the DSP registers, erases internal variables, resets the volume, and resets the halt flag +; +;Destroys: +; EAX + +PUBLIC ResetDSP, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set DSP Options +; +;Recalculates tables, changes the output sample rate, and sets up the mixing routine +; +;Notes: +; Range checking is performed on all parameters. If a parameter does not match the required +; range of values, the default value will be assumed. +; +; -1 can be used for any paramater that should remain unchanged. +; +; The OPT_ANALOG option only works when: +; mix = MIX_INT +; chn = 2 +; bits >= 16 +; rate >= 32000 +; +; Four channel output forces MIX_FLOAT +; +; In: +; mixType = Mixing routine (default MIX_INT) +; numChn = Number of channels (1, 2, or 4, default 2) +; bits = Sample size (mono 8 or 16; stereo 8, 16, 24, 32, or -32 [IEEE 754], default 16) +; rate = Sample rate (8000-192000, default 32000) +; inter = Interpolation type (default INT_GAUSS) +; opts = see OPT_??? equates +; +;Destroys: +; EAX + +PUBLIC SetDSPOpt, mixType:dword, numChn:dword, bits:dword, rate:dword, inter:dword, opts:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Debug DSP +; +;Installs a callback that gets called each time a value is written to the DSP data register. +;The function is called with the C calling convention. +; +;Upon entrance to the function: +; [ESP+4] = Current opcode (low word = PC) +; [ESP+8] = YA +; +;Note: +; DEBUG must be set to 1 in APU.Inc +; +;In: +; pTrace-> Debug function (NULL turns off the debug call, -1 leaves the current callback) +; opts = DSP debugging options (-1 leaves the current options) +; +;Out: +; Previously installed callback +; +;Destroys: +; nothing + +PUBLIC SetDSPDbg, pTrace:ptr, opts:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Fix DSP After Loading SPC file +; +;Initializes the internal mixer variables +; +;Destroys: +; EAX + +PUBLIC FixDSPLoad, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Fix DSP After Seeking +; +;Puts all DSP voices in a key off state and erases echo region. Call after using EmuDSPN. +; +;In: +; reset = true, reset all voices +; false, only erase memory +; +;Destroys: +; EAX + +PUBLIC FixDSPSeek, reset:byte + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Save DSP State +; +;Notes: +; Pointers in the DSPState structure can be NULL if you don't want a copy of that data +; +; The values in the Voice structures are modified as follows: +; dspv points to the registers pointed to by DSPState.pDSP +; bCur becomes an index into APU RAM (0-65535) +; eRate is adjusted for 32kHz +; eCnt is adjusted for 32kHz +; +; Only the current samples being used for the echo delay are copied into pEchoBuf, not necessarily +; all 240ms. +; +;In: +; pState -> Saved state structure +; +;Destroys: +; EAX + +PUBLIC SaveDSP, pState:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Restore DSP State +; +;Notes: +; Setting a pointer in DSPState to NULL will keep its corresponding internal data intact, except +; for the echo. If pEchoBuf is NULL, the internal echo buffer will be cleared. +; +; The members of the Voice structures must contain the same values as they would during emulation, +; except: +; bCur must be an index into APU RAM (0-65535), not a physical pointer +; eCnt must be the current sample count based on a 32kHz sample rate +; dspv, bHdr, and eRate do not need to be set because they will be reset to their correct values +; If the inactive flag in mFlg is set, then the remaining Voice members do not need to be set +; +; By setting eRate to -1, RestoreDSP will reset the following members: +; eRIdx, eRate, eCnt, eAdj, eDest +; eMode and eVal will remain unchanged +; +; By setting 'mRate' to -1, RestoreDSP will reset the following members: +; mTgtL/R, mChnL/R, mRate, mDec, and mOrgP +; mFlg and mOut will remain unchanged +; +; Only the amount of echo delay specified by DSP.EDL is copied from pEchoBuf +; +;In: +; pState -> Saved state structure +; +;Destroys: +; EAX + +PUBLIC RestoreDSP, pState:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Amplification Level +; +;Sets the amount to amplify the output during the final stage of mixing +; +;Note: +; Calling this function disables AAR for the current song +; +;In: +; amp = Amplification level [-15.16] (1.0 = SNES, negative values act as 0) +; +;Destroys: +; EAX + +PUBLIC SetDSPAmp, amp:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get Amplification Level +; +;Returns the current amplification level, which may have been changed due to AAR. +; +;Out: +; EAX = Current amp level + +PUBLIC GetDSPAmp + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Automatic Amplification Reduction +; +;Notes: +; -1 can be passed for any parameter that should remain unchanged +; Calling this function restarts AAR if it has been stopped due to amp reaching min or max or by +; calling SetDSPAmp +; This function has no effect if the emulator was not compiled with the VMETERM option set +; +;In: +; type = Type of AAR (see AAR_??? equates) +; thresh = Threshold at which to decrease amplification +; min = Minimum amplification level to decrease to +; max = Maximum amplification level to increase to +; +;Destroys: +; EAX + +PUBLIC SetDSPAAR, type:byte, thresh:dword, min:dword, max:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Song Length +; +;Sets the length of the song and fade +; +;Note: +; To set a song with no length, pass -1 and 0 for the song and fade, respectively +; +;In: +; song = Length of song (in 1/64000ths second) +; fade = Length of fade (in 1/64000ths second) +; +;Out: +; EAX = Total length + +PUBLIC SetDSPLength, song:dword, fade:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set DSP Base Pitch +; +;Adjusts the pitch of the DSP +; +;In: +; base = Base sample rate (32000 - Normal pitch, 32458 - Old SB cards, 32768 - Old ZSNES) +; +;Destroys: +; EAX + +PUBLIC SetDSPPitch, base:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Voice Stereo Separation +; +;Sets the amount to adjust the panning position of each voice +; +;In: +; sep = Separation [1.16] +; 1.0 - full separation (output is either left, center, or right) +; 0.5 - normal separation (output is unchanged) +; 0 - no separation (output is completely monaural) +; +;Destroys: +; EAX,ST0-7 + +PUBLIC SetDSPStereo, sep:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Echo Feedback Crosstalk +; +;Sets the amount of crosstalk between the left and right channel during echo feedback +; +;In: +; leak = Crosstalk amount [-1.15] +; 1.0 - no crosstalk (SNES) +; 0 - full crosstalk (mono/center) +; -1.0 - inverse crosstalk (L/R swapped) +; +;Destroys: +; EAX + +PUBLIC SetDSPEFBCT, leak:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set Voice Mute +; +;Prevents a voice's output from being mixed in with the main output +; +;In: +; voice = Voice to mute (0-7, only bits 2-0 are used) +; state = see MUTE_??? equates +; +;Out: +; true, if voice is muted + +PUBLIC SetDSPVoiceMute, voice:byte, state:byte + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Data Port +; +;Writes a value to a specified DSP register and alters the DSP accordingly. If the register write +;affects the output generated by the DSP, this function returns true. +; +;Note: +; SetDSPReg does not call the debugging callback +; +;In: +; reg = DSP Address +; val = DSP Data +; +;Out: +; EAX = True, if the DSP state was affected + +PUBLIC SetDSPReg, reg:byte, val:byte + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate DSP +; +;Emulates the DSP of the SNES +; +;Notes: +; If 'pBuf' is NULL, the routine MIX_NONE will be used +; Range checking is performed on 'len' +; +;In: +; pBuf-> Buffer to store output +; len = Length of buffer (in samples) +; +;Out: +; EAX -> End of buffer +; +;Destroys: +; ST0-ST7 + +PUBLIC EmuDSP, pBuf:ptr, len:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Play Sound Source +; +;Plays a single sound source using the settings defined by SetDSPOpt. All samples are output in +;integer form. The output is attenuated -6dB. +; +;Admittedly, this function is poorly coded in that it does two different things depending on the +;value of the last parameter. +; +;Note: +; If the source data is within APU RAM, the source pointer will wrap around within APU RAM +; +;In: +; pSrc-> Sound source to play (if upper 16 bits are 0, lower 16 bits will index APU RAM) +; loop = Block to loop on +; rate = Logical sample rate to play back at (actual playback rate may be less) +; +; pSrc-> Buffer to store decompressed and resampled data +; loop = Number of samples to generate +; rate = 0 +; +;Out: +; rate != 0, EAX = Sample rate sound will be played back at +; rate == 0, EAX-> End of buffer + +PUBLIC PlaySrc, pSrc:ptr, loop:dword, rate:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Unpack Sound Source +; +;Decompresses a series of BRR blocks into 16-bit PCM samples +; +;Notes: +; Regardless of the value in 'num', unpacking will stop when the end block is reached. +; The previous samples are only needed if you're making multiple calls to UnpackWave. If you're +; decompressing the entire sound in one call, you can disregard them. +; If the source data is within APU RAM, the source pointer will wrap around within APU RAM, +; though no more than 7281 BRR blocks will be processed. +; +;In: +; pSmp -> Location to store decompressed samples +; pBlk -> Sound source to decompress (if upper 16 bits are 0, lower 16 bits will index APU RAM) +; num = Number of blocks to decompress (0 to decompress entire sound) +; opts = Options (only one for now) +; BRR_OLDSMP = Traditional method of decompression +; prev1-> First previous sample (can be NULL if not needed) +; prev2-> Second previous sample +; +;Out: +; EAX = Number of blocks decompressed + +PUBLIC UnpackSrc, pSmp:ptr, pBlk:dword, num:dword, opts:dword, prev1:ptr, prev2:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Pack Sound Source +; +;Compresses a series of 16-bit PCM samples into BRR blocks. Generally sounds are packed in one +;call. However, if you want to implement some sort of real-time compression stream you can make +;multiple calls to PackSrc. See the options description below for more information on continuing +;compression. +; +;THIS FUNCTION HAS NOT BEEN FINISHED AND DOES NOT PACK LOOPED SOUNDS +; +;Notes: +; The previous samples are only needed if you're making multiple calls to PackSrc. If you're +; compressing the entire sound in one call, you can disregard them. +; No checking is performed on the output pointer to make sure the data wraps around if it resides +; within APU RAM. +; The looping section must be at least 16 samples. +; +;In: +; pBlk -> Location to store bit-rate reduced blocks +; pSmp -> Samples to compress +; pLen -> Number of samples to compress +; pLoop-> Sample index of loop point. Set this to NULL for one-shot sounds. This parameter is +; ignored if 'prev1' is not NULL. +; opts = Options. The way the options behave is different depending on if 'prev1' is NULL. +; +; If prev1 is NULL: +; BRR_END = This option has no effect. The last block's end flag will always be set. +; BRR_LOOP = By default, if pLoop is not NULL the last block's loop flag will +; automatically be set. This option causes the loop flag to be set in every +; block regardless of pLoop. +; BRR_LINEAR = By default, the best compression filter will be determined for each block. +; This option forces linear compression (filter 0) to be used for all blocks. +; BRR_NOINIT = By default, a block of silence is inserted at the beginning of the sound +; source. This option skips the silence and encodes samples into the first +; block. (The first block, whether it contains silence or samples, will +; always use linear compression. This is necessary in order for the +; decompression filter to be properly initialized when the source is played.) +; +; If prev1 is not NULL: +; BRR_END = Sets the end flag in the last block +; BRR_LOOP = This option causes the loop flag to be set in every block. +; BRR_LINEAR = By default, the best compression filter will be determined for each block. +; This option forces linear compression (filter 0) to be used for all blocks. +; BRR_NOINIT = By default, the first block is compressed with linear compression. This +; option causes the first block to be compressed with whichever filter is the +; best. +; prev1-> First previous sample. NULL if the value of the previous samples is not needed. +; (Generally the previous samples are only used if making multiple calls to PackSrc.) +; prev2-> Second previous sample. If this is NULL, 'prev1' is treated as NULL also. +; +;Out: +; EAX -> End of the data stored in pBlk, NULL if an error occurred. +; Possible errors: +; Length is 0 +; Loop point is greater than the number of samples +; Loop length is less than 16 +; pLen -> Number of blocks in sound source +; pLoop-> Starting block of loop + +PUBLIC PackSrc, pBlk:ptr, pSmp:ptr, pLen:ptr, pLoop:ptr, opts:dword, prev1:ptr, prev2:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;VMax to Decible +; +;Generates decible values for the peak output of each voice and the main output, the purpose of +;which is to provide the user with some sort of PPM (Peak Program Meter) visualization. +; +;The decible values are stored in an array, where the first 8 pairs of values are used for the +;left/right output of each voice and the last pair for the main output. +; +;The values generated can be 32-bit floats or 16.16 fixed-point integers. When floating-point +;numbers are chosen, the return values are in actual dB starting with -96 (which represents +;-infinity) on up to +0. When integers are chosen, the return values are in fixed point notation +;[16.16] and are positive starting with 0 (which represents -infinity) on up to +96 (or 600000h, +;representing +0db). +; +;After calling VMax2dB, the vMax values for both voice and main outputs are set to 0. +; +;Notes: +; The values for the main output will be higher than 0dB if clipping has occurred. +; +;In: +; pList-> Array to store 18 32-bit numbers +; +;Destroys: +; EAX + +PUBLIC VMax2dBf, pList:ptr +PUBLIC VMax2dBi, pList:ptr
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/DSP.h b/lib/snesapu/SNES/SNESAPU/DSP.h new file mode 100644 index 0000000000..d630323564 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/DSP.h @@ -0,0 +1,1120 @@ +/*************************************************************************************************** +* Program: Super Nintendo Entertainment System(tm) Digital Signal Processor Emulator * +* Platform: 80386 / MMX * +* Programmer: Anti Resonance * +* * +* "SNES" and "Super Nintendo Entertainment System" are trademarks of Nintendo Co., Limited and its * +* subsidiary companies. * +* * +* This library is free software; you can redistribute it and/or modify it under the terms of the * +* GNU Lesser General Public License as published by the Free Software Foundation; either version * +* 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * +* See the GNU Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License along with this * +* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * +* Boston, MA 02111-1307 USA * +* * +* ------------------------------------------------------------------------------------------------ * +* Revision History: * +* * +* 2005.xx.xx SNESAPU v3.0 * +* + Added 8-point sinc interpolation * +* + Added per voice anti-aliasing * +* + Added quadraphonic output to the floating-point mixing routine * +* + Removed the branches in the volume ramping code in EmuDSPF * +* + Updated interpolation functions so PlaySrc could use them * +* + Added -6dB attenuation to PlaySrc, so single sounds aren't so loud compared to the relative * +* quietness of songs * +* - Volume ramping was disabled when stereo controls were turned off (not sure why) * +* - Fixed reverse stereo not working when stereo conrols were enabled, for real this time * +* - Correcly moved the placement of volatile in the DSPDebug typedef * +* + Changed debugging interface * +* + Removed option for generating Gaussian curve * +* + DSP register jump table gets initialized at run-time * +* - Fixed a bug in EmuDSP where muting the output buffer would crash * +* - Fixed a bug when resetting the DSP with the FLG register * +* + Made floating-point constants local variables in most functions * +* + Created macros to calculate relative displacments for some global variables * +* - Fixed DSPIn to store the new value in the registers after UpdateDSP is called. Also moved * +* UpdateDSP to get called only if the register handler will be called. * +* * +* 2004.08.01 SNESAPU v2.1 * +* + Improved monaural volume setting * +* + Removed BRR look-up table * +* + Moved all checks for SPC_NODSP from SPC700.Asm * +* - Envelope height in direct gain mode could be garbage * +* - Added a missing 16-bit clamp to the MMX Gaussian interpolation * +* - Added a 16-bit clamp to the MMX FIR filter to work around a distortion bug in DQK * +* - StartSrc wasn't wrapping source directory around 64k * +* * +* 2004.01.26 SNESAPU v2.0 * +* + Added 24- and 32-bit sample support to MMX * +* + Hardware anomalies emulates the DSP at 32kHz then upsamples the output * +* + Replaced fake low-pass filter with a 32-tap FIR filter * +* + Writing to the DSP registers from the SPC700 automatically calls EmuDSP. This should help * +* keep the DSP and SPC700 in better syncronization. * +* + Removed self modifying code * +* + Moved fade out from APU.Asm * +* - Output is inverted only when hardware anomalies are enabled * +* - Obtained a more accurate copy of the gaussian look-up table used in the SNES * +* - Gaussian interpolation correctly clears the LSB in MMX mixing mode * +* - When using 4-point interpolation, source playback begins with the fourth sample * +* - Removed 17-bit clamp from filter 1 * +* - Envelopes are not based on a 33kHz clock (duh...) * +* - Envelopes now use a sample counter instead of a fractional clock * +* - FIR filter coefficients were getting applied in reverse order * +* - Filtered samples get shifted right before accumulation, and LSB is cleared after accum. * +* - Fixed PckWav (it was seriously broken) * +* - Added support for BRR_??? options to PckWav * +* * +* 2003.07.29 SNESAPU v1.01 * +* - Voice's inactive flag wasn't getting set after a key off * +* * +* 2003.06.30 * +* - Made some minor changes to RestoreDSP * +* - Fixed voices' pitch not getting changed in SetDSPOpt * +* - Fixed a bug in the floating-point DSP volume register handler when STEREO = 0 * +* * +* 2003.06.18 SNESAPU v0.99c * +* - Fixed interger overflow in MMX FIR filter * +* * +* 2003.06.14 SNESAPU v0.99b * +* + Added support for old amplification values (<= 256) * +* * +* 2003.05.19 SNESAPU v0.99a * +* - Fixed a bug in the i386 FIR filter * +* * +* 2003.04.28 SNESAPU v0.99 * +* + InitDSP will select 386 or MMX depending on CPU * +* + Added 16 and 24-bit sample output to EmuDSPF * +* + Changed SetDSPAmp to decibels * +* - Fixed several bugs in the envelope code so envelopes now work the same as the SNES (fixes a * +* lot of songs) * +* - Stopped caching loop points (fixes Castlevania IV, which likes to change sources after a * +* key on) * +* - Added clamping to the pitch modulation code (fixes CT) * +* - Fixed a bug in KON/KOF emulation * +* - Changed 16-bit mixing process to emulate the SNES * +* - Improved low-pass filtering and simulated RF interference * +* - Removed floating-point support from SetDSPAmp * +* - Fixed another volume bug in SetDSPOpt when switching between integer and float routines * +* * +* 2003.02.10 SNESAPU v0.98 * +* + Converted to NASM * +* + Added SetDSPDbg, SaveDSP, RestoreDSP, and SetDSPReg * +* + Added a loop to EmuDSP so the number of samples passed in can be any value * +* + Added floating-point support to SetDSPAmp * +* + Added micro ramping to the channel volumes in the floating-point routine * +* + Added 32-bit IEEE 754 output * +* - Added a 17-bit clamp to the UnpckWav (fixes Hiouden; CT and FF6 are iffy now) * +* - Added linear interpolation to FIR filter (fixes some songs that turned to noise) * +* - SetDSPOpt wasn't clearing EAX, which was causing dspDecmp to sometimes contain a bad value * +* - ENVX always gets updated, regardless of ADSR/GAIN flag (fixes Clue) * +* * +* 2001.10.02 SNESAPU v0.96 * +* - freqTab was built from the wrong values (Hz instead of samples) * +* - rateTab now uses 32-bits of precision instead of 16 * +* - Values in rateTab >= 1.0 are now set to 0.99 to reduce the ill effects of fast envelopes when * +* the sample rate is < 32kHz * +* - Reset all internal pitch values when PMON is written to (fixes Asterix) * +* - Clear bits in KOF if voice is inactive (improves EOS detection) * +* - Switched to a static lookup table generated by the SNES for Gaussian interpolation * +* - Damn near cracked the sample decompression * +* - Optimized code a bit in EmuDSPN and inadvertently fixed a bug that was causing some songs to * +* break after seeking * +* - EmuDSPN wasn't checking the loop counter after an envelope update was performed (seeking * +* no longer appears to hang) * +* * +* 2001.05.15 SNESAPU v0.95 * +* + Added 4-point Gaussian interpolation * +* + EmuDSPN now decompresses samples, so seeking doesn't lose voices * +* + Added an option to FixSeek to disable resetting voices * +* + Reverse stereo swaps the left/right volumes instead of physically swapping samples * +* + More minor optimizations * +* - Changed the sample decompression routine. Less songs should have problems. * +* - Echo delay was incorrect for most sample rates * +* - Did something to fix the noise generator not working in some songs * +* * +* 2001.03.02 SNESAPU v0.92 * +* - Fixed the low-pass filter (I'm amazed it was sort of working at all) * +* - Added a hack to UnpckWav to subtract 3 from the range if > 12 * +* - Forgot that the DSP needs to be limited to 64K as well (Secret of Evermore doesn't crash) * +* - Fixed a problem in the MMX dynamic code allocation * +* - FixSeek wasn't resetting the DSP X registers (seeking works in Der Langrisser) * +* * +* 2001.01.26 SNESAPU v0.90 * +* + Added voice monitor and VMax2dB for visualization * +* + Added SetDSPPitch so people with SB/SBPro's and compatibles can hear the correct tone * +* + Added SetDSPStereo for headphone listeners * +* + Added SetDSPEFBCT to make the echo sound more natural * +* + Added UnpackWave so waveforms could be decompressed by external programs * +* + Added 32-bit floating point mixer for people with 24/32-bit sound cards * +* + Added a reverse stereo option * +* + Added GetProcInfo to detect 3DNow! and SSE capabilites * +* + Added EmuDSP which automatically calls the mixing routine set in SetDSPOpt * +* + Made a lot of minor optimizations to DSPIn and EmuDSP * +* + Made separate register handlers in DSPIn for mono and floating-point * +* - Changed SetDSPOpt to use individual parameters instead of a record * +* - SetDSPOpt performs range checking * +* - Fixed configuration implementation so options don't need to be reset for every song and they * +* can be changed between calls to EmuDSP * +* - Fixed mono related code (sounds better now) * +* - Echo filter was never getting turned on * +* - Implemented a better low-pass filter * +* * +* 2000.05.22 SNESAmp v1.2 * +* + Added a low-pass filter * +* + Added output monitor * +* + Optimized the waveform code in EmuDSPN * +* + Added FixSeek * +* + SetDSPVol now uses a 16-bit value * +* - FixDSP was marking voices as keyed even if the pitch was 0 * +* - Correctly implemented echo volume (should be perfect now) * +* * +* 2000.04.05 SNESAmp v1.0 * +* - Voices with a pitch of 0 can't be keyed on * +* - EnvH becomes 0 when voice is keyed off * +* - EnvH is set to 0 when envelope is in gain mode * +* - Account for sample blocks with a range greater than 16-bits (ADPCM still not correct) * +* * +* 2000.03.17 SNESAmp v0.9 * +* Copyright (C)1999-2006 Alpha-II Productions * +***************************************************************************************************/ + +namespace A2 +{ +namespace SNES +{ + + //********************************************************************************************** + //Defines + + //CPU capability --------------------------- + enum CPUCaps + { + CPU_MMX = 1, //Multi-Media eXtensions + CPU_3DNOW = 2, //3DNow! extensions + CPU_SSE = 4 //Streaming SIMD Extensions + }; + + //Mixing routines (see DSP.Asm for details of each routine) + enum Mixing + { + MIX_INVALID = -1, + MIX_NONE = 0, //No mixing + MIX_INT = 1, //Use integer math + MIX_FLOAT = 3 //Use floating-point math + }; + + //Interpolation routines ------------------- + enum DSPInter + { + INT_INVALID = -1, + INT_NONE, //None + INT_LINEAR, //Linear + INT_CUBIC, //Cubic + INT_GAUSS, //4-point Gaussian + INT_SINC, //8-point Sinc + INT_TOTAL + }; + + //DSP options ------------------------------ + enum DSPOpts + { + OPT_ANALOG, //Simulate anomalies of the analog hardware + OPT_OLDSMP, //Old sample decompression routine + OPT_SURND, //"Surround" sound + OPT_REVERSE, //Reverse stereo samples + OPT_NOECHO, //Disable echo + OPT_FILTER, //Pass each voice through an anti-aliasing filter + }; + + //Automatic Amplification Reduction -------- + enum AARType + { + AAR_OFF, //Disable AAR + AAR_ON, //Enable AAR + AAR_INC, //Enable AAR with auto increase + AAR_TOTAL + }; + + //Voice muting ----------------------------- + enum MuteState + { + MUTE_GET = -1, //Do nothing (use to get mute state) + MUTE_OFF, //Unmute + MUTE_ON, //Mute + MUTE_SOLO, //Unmute voice and mute all others + MUTE_TOGGLE //Toggles current mute state + }; + + //PackSrc options -------------------------- + enum PackOpts + { + BRR_END, //Set the end flag in the last block + BRR_LOOP, //Set the loop flag in all blocks + BRR_LINEAR, //Use linear compression for all blocks + BRR_NOINIT = 4 //Don't create an initial block of silence + }; + + //UnpackSrc options ------------------------ + enum UnpackOpts + { + BRR_OLDSMP = 7 //Use old sample decompression routine + }; + + //Mixing flags ----------------------------- + static const u8 MFLG_MUTE = 0x01; //Voice is muted + static const u8 MFLG_KOFF = 0x04; //Voice is in the process of keying off + static const u8 MFLG_OFF = 0x08; //Voice is currently inactive + + //Envelope mode masks ---------------------- + static const u8 ENVM_ADJ = 0x01; //Adjustment: Const.(1/64 or 1/256) / Exp.(x255/256) + static const u8 ENVM_DIR = 0x02; //Direction: Decrease / Increase + static const u8 ENVM_DEST = 0x04; //Destination: Default(0 or 1) / Other(x/8 or .75) + static const u8 ENVM_ADSR = 0x08; //Envelope mode: Gain/ADSR + static const u8 ENVM_IDLE = 0x80; //Envelope is marked as idle, or not changing + static const u8 ENVM_MODE = 0x0F; //Envelope mode is stored in lower four bits + + //Envelope modes --------------------------- + enum EnvMode + { + ENV_DEC = 0, //Linear decrease + ENV_EXP = 1, //Exponential decrease + ENV_INC = 2, //Linear increase + ENV_BENT = 6, //Bent line increase + ENV_DIR = 7, //Direct setting + ENV_REL = 8, //Release mode (key off) + ENV_SUST = 9, //Sustain mode + ENV_ATTACK = 10, //Attack mode + ENV_DECAY = 13, //Decay mode + }; + + //Flag register bits ----------------------- + static const u8 FLG_RES = 0x80; //Reset DSP + static const u8 FLG_MUTE = 0x40; //Mute main output + static const u8 FLG_ECEN = 0x20; //Disable echo region + static const u8 FLG_NCK = 0x1F; //Mask for noise clock index + + //DSP debugging options -------------------- + + // Halt DSP: + // + // This flag causes EmuDSP() to return silence. Voices will not be updated and external writes + // to DSP registers will be ignored. + + enum DSPDbgOpts + { + DSP_HALT = 7 + }; + + + //********************************************************************************************** + //Structures + + //DSP registers ---------------------------- + struct Voice + { + s8 volL; //Volume Left + s8 volR; //Volume Right + u16 pitch; //Pitch (rate/32000) (2.12) + u8 srcn; //Sound source being played back + u8 adsr1; //ADSR volume envelope (attack and decay rate) + u8 adsr2; // " " (sustain level and rate) + u8 gain; //Envelope gain (if not using ADSR) + s8 envx; //Current envelope height (.7) + s8 outx; //Current sample being output (-.7) + s8 __r[6]; + }; + + struct FIR + { + s8 __r[15]; + s8 c; //Filter coefficient (-.7) + }; + + union Regs + { + Voice voice[8]; //Voice registers + + struct //Global registers + { + s8 __r00[12]; + s8 mvolL; //Main Volume Left (-.7) + s8 efb; //Echo Feedback (-.7) + s8 __r0E; + s8 c0; //FIR filter coefficent (-.7) + + s8 __r10[12]; + s8 mvolR; //Main Volume Right (-.7) + s8 __r1D; + s8 __r1E; + s8 c1; + + s8 __r20[12]; + s8 evolL; //Echo Volume Left (-.7) + u8 pmon; //Pitch Modulation on/off for each voice + s8 __r2E; + s8 c2; + + s8 __r30[12]; + s8 evolR; //Echo Volume Right (-.7) + u8 non; //Noise output on/off for each voice + s8 __r3E; + s8 c3; + + s8 __r40[12]; + u8 kon; //Key On for each voice + u8 eon; //Echo on/off for each voice + s8 __r4E; + s8 c4; + + s8 __r50[12]; + u8 kof; //Key Off for each voice + u8 dir; //Page containing source directory + s8 __r5E; + s8 c5; + + s8 __r60[12]; + u8 flg; //DSP flags and noise frequency (see FLG_???) + u8 esa; //Starting page used to store echo samples + s8 __r6E; + s8 c6; + + s8 __r70[12]; + u8 endx; //End block of sound source has been decoded + u8 edl; //Echo Delay in ms >> 4 + s8 __r7E; + s8 c7; + }; + + FIR fir[8]; //FIR filter + + u8 reg[128]; + }; + + //Internal mixing data --------------------- + struct MixData + { + //Visualization -- 08 + s32 vMaxL; //Maximum absolute sample output + s32 vMaxR; + //Voice ---------- 04 + Voice* pVoice; //-> voice registers in DSP + //Waveform ------- 06 + void* bCur; //-> current block + u8 bHdr; //Block Header for current block + u8 mFlg; //Mixing flags (see MFLG_???) + //Envelope ------- 22 + u8 eMode; //[3-0] Current mode (see ENV_???) + //[6-4] ADSR mode to switch into from Gain + //[7] Envelope is idle + u8 eRIdx; //Index in rateTab (0-31) + u32 eRate; //Rate of envelope adjustment (16.16) + u32 eCnt; //Sample counter (16.16) + u32 eVal; //Current envelope height (.11) + s32 eAdj; //Amount to adjust envelope height + u32 eDest; //Envelope Destination (.11) + //Samples -------- 56 + s16 sP1; //Last sample decompressed (prev1) + s16 sP2; //Second to last sample (prev2) + s16* sIdx; //-> current sample in sBuf + s16 sBufP[8]; //Last 8 samples from prev block + s16 sBuf[16]; //32 bytes for decompressed BRR block + //Mixing --------- 32 + f32 mTgtL; //Target volume (floating-point mixing only) + f32 mTgtR; // " " + s32 mChnL; //Channel Volume (-24.7) + s32 mChnR; // " " + u32 mRate; //Pitch Rate after modulation (16.16) + u32 mDec; //Pitch Decimal (.16) (used as delta for inter.) + s32 mOut; //Last sample output before chn vol (used for pmod) + u32 __r; //reserved + }; + + //Saved state ------------------------------ + //see SaveDSP for information about the values in the structure, and how they differ from the + //internal values + struct DSPState + { + Regs* pReg; //[0.0] -> DSP registers (128 bytes) + MixData* pVoice; //[0.4] -> Internal mixing settings (1k) + void* pEcho; //[0.8] -> echo buffer (bytes = sample rate * 1.92) + u32 amp; //[0.C] Amp level (AAR is stored in upper 4 bits) + }; + + + //Maximum sample output -------------------- + struct MaxOutF + { + struct Voice + { + f32 left,right; //Left/Right channel for each voice + } voice[8]; + + struct Main + { + f32 left,right; //Left/Right channel for main output + } main; + }; + + struct MaxOutI + { + struct Voice + { + s32 left,right; + } voice[8]; + + struct Main + { + s32 left,right; + } main; + }; + + + //********************************************************************************************** + // DSP Debugging Routine + // + // A prototype for a function that gets called for debugging purposes. + // + // The paramaters passed in can be modified, and on return will be used to update the internal + // registers. Set bit-7 of 'reg' to prevent the DSP from handling the write. + // + // For calling a pointer to another debugging routine, use the following macro. + // + // In: + // reg -> Current register (LOBYTE = register index) + // val = Value being written to register + + typedef void DSPDebug(u8* volatile reg, volatile u8 val); + +#ifdef __GNUC__ + #define _CallDSPDebug(proc, reg, val) \ + asm(" \ + pushl %1 \ + pushl %0 \ + calll %2 \ + popl %0 \ + popl %1 \ + " : "+m" (reg), "+m" (val) : "m" (proc)); +#else + #define _CallDSPDebug(proc, reg, val) \ + _asm \ + { \ + push dword ptr [val]; \ + push dword ptr [reg]; \ + call dword ptr [proc]; \ + pop dword ptr [reg]; \ + pop dword ptr [val]; \ + } +#endif + + +#if !defined(SNESAPU_DLL) || defined(DONT_IMPORT_SNESAPU) + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Private Declarations + + extern "C" Regs dsp; //DSP registers + extern "C" MixData mix[8]; //Mixing structures for each voice + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // External Functions + // + // NOTE: Unless otherwise stated, no range checking is performed. It is the caller's + // responsibilty to ensure paramaters passed are within a valid range. + +extern "C" { + + //********************************************************************************************** + // Get Processor Information + // + // Detects if processor has MMX, 3DNow!, or SSE capabilities + // + // Note: + // Values are or'd together. If a procesor has 3DNow!, it will return 03h for MMX and 3DNow!. + // + // Out: + // see enum CPUCaps + // + // Destroys: + // nothing + + u32 GetProcInfo(); + + + //********************************************************************************************** + // Initialize DSP + // + // Creates the lookup tables for interpolation, and sets the default mixing settings: + // + // mixType = MIX_INT + // numChn = 2 + // bits = 16 + // rate = 32000 + // inter = INT_GAUSS + // opts = None + // + // Note: + // Callers should use InitAPU() instead + // + // Thread safe: + // No + // + // Destroys: + // EAX,ST0-7 + + void InitDSP(); + + + //********************************************************************************************** + // Reset DSP + // + // Resets the DSP registers, erases internal variables, and resets the volume + // + // Note: + // Callers should use ResetAPU() instead + // + // Thread safe: + // No + // + // Destroys: + // EAX + + void ResetDSP(); + + + //********************************************************************************************** + // Set DSP Options + // + // Recalculates tables, changes the output sample rate, and sets up the mixing routine + // + // Notes: + // Range checking is performed on all parameters. If a parameter does not match the required + // range of values, the default value will be assumed. + // + // -1 (or Set::All) can be used for any paramater that should remain unchanged. + // + // The OPT_ANALOG option only works when: + // mix = MIX_INT + // chn = 2 + // bits >= 16 + // rate >= 32000 + // + // Four channel output forces MIX_FLOAT + // + // Callers should use SetAPUOpt() instead + // + // Thread safe: + // No, except when only changing the interpolation type + // + // In: + // mix = Mixing routine (see enum Mixing, default MIX_INT) + // chn = Number of channels (1, 2, or 4, default 2) + // bits = Sample size (mono 8 or 16; stereo 8, 16, 24, 32, or -32 [IEEE 754], default 16) + // rate = Sample rate (8000-192000, default 32000) + // inter = Interpolation type (see enum Interpolation, default INT_GAUSS) + // opts = see enum DSPOpts, Set::All() leaves the current options + // + // Destroys: + // EAX + + void SetDSPOpt(Mixing mix, u32 chn, u32 bits, u32 rate, DSPInter inter = INT_INVALID, + Set<DSPOpts> opts = ~Set<DSPOpts>()); + + + //********************************************************************************************** + // Debug DSP + // + // Installs a callback that gets called each time a value is written to the DSP data register. + // + // Note: + // The build option SA_DEBUG must be enabled + // + // Thread safe: + // No, but it may be called from with in the debug callback + // + // In: + // pTrace-> Debugging callback (NULL can be passed to disable the callback, -1 leaves the + // current callback in place) + // opts = DSP debugging flags (see enum DSPDbgOpts, Set.All() leaves the current flags) + // + // Out: + // Previously installed callback + + DSPDebug* SetDSPDbg(DSPDebug* pTrace, Set<DSPDbgOpts> opts = Set<DSPDbgOpts>()); + + + //********************************************************************************************** + // Fix DSP After Loading SPC File + // + // Initializes the internal mixer variables + // + // Note: + // Callers should use FixAPULoad() instead + // + // Thread safe: + // No + // + // Destroys: + // EAX + + void FixDSPLoad(); + + + //********************************************************************************************** + // Fix DSP After Seeking + // + // Puts all DSP voices in a key off state and erases echo region. Call after using EmuDSPN. + // + // Thread safe: + // No + // + // In: + // reset = true, reset all voices + // false, only erase memory + // + // Destroys: + // EAX + + void FixDSPSeek(bool reset); + + + //********************************************************************************************** + // Save DSP State + // + // Notes: + // Pointers in the DSPState structure can be NULL if you don't want a copy of that data + // + // The values in the Voice structures are modified as follows: + // dspv points to the registers pointed to by DSPState.pDSP + // bCur becomes an index into APU RAM (0-65535) + // eRate is adjusted for 32kHz + // eCnt is adjusted for 32kHz + // + // Only the current samples being used for the echo delay are copied into pEchoBuf, not + // necessarily all 240ms. + // + // Callers should use SaveAPU() instead + // + // Thread safe: + // No + // + // In: + // pState -> Saved state structure + // + // Destroys: + // EAX + + void SaveDSP(DSPState* state); + + + //********************************************************************************************** + // Restore DSP State + // + // Notes: + // Setting a pointer in DSPState to NULL will keep its corresponding internal data intact, except + // for the echo. If pEchoBuf is NULL, the internal echo buffer will be cleared. + // + // The members of the Voice structures must contain the same values as they would during + // emulation, except: + // bCur must be an index into APU RAM (0-65535), not a physical pointer + // eCnt must be the current sample count based on a 32kHz sample rate + // dspv, bHdr, and eRate do not need to be set because they will be reset to their correct vals + // If the inactive flag in mFlg is set, then the remaining Voice members do not need to be set + // + // By setting eRate to -1, RestoreDSP will reset the following members: + // eRIdx, eRate, eCnt, eAdj, eDest + // eMode and eVal will remain unchanged + // + // By setting 'mRate' to -1, RestoreDSP will reset the following members: + // mTgtL/R, mChnL/R, mRate, mDec, and mOrgP + // mFlg and mOut will remain unchanged + // + // Only the amount of echo delay specified by DSP.EDL is copied from pEchoBuf + // + // Callers should use RestoreAPU() instead + // + // Thread safe: + // No + // + // In: + // state -> Saved state structure + // + // Destroys: + // EAX + + void RestoreDSP(const DSPState& state); + + + //********************************************************************************************** + // Set Amplification Level + // + // Sets the amount to amplify the output during the final stage of mixing + // + // Note: + // Calling this function disables AAR for the current song + // + // Thread safe: + // No + // + // In: + // amp = Amplification level [-15.16] (1.0 = SNES, negative values act as 0) + // + // Destroys: + // EAX + + void SetDSPAmp(u32 amp); + + + //********************************************************************************************** + // Get Amplification Level + // + // Returns the current amplification level, which may have been changed due to AAR. + // + // Thread safe: + // Yes + // + // Out: + // Current amp level + + u32 GetDSPAmp(); + + + //********************************************************************************************** + // Set Automatic Amplification Reduction + // + // Notes: + // -1 can be passed for any parameter that should remain unchanged + // Calling this function restarts AAR if it has been stopped due to amp reaching min or max or by + // calling SetDSPAmp() + // + // Thread safe: + // No + // + // In: + // type = Type of AAR (see AAR_??? #defines) + // thresh = Threshold at which to decrease amplification + // min = Minimum amplification level to decrease to + // max = Maximum amplification level to increase to + // + // Destroys: + // EAX + + void SetDSPAAR(AARType type, u32 thresh, u32 min, u32 max); + + + //********************************************************************************************** + // Set Song Length + // + // Sets the length of the song and fade + // + // Note: + // To set a song with no length, pass -1 and 0 for the song and fade, respectively + // + // Thread safe: + // No + // + // In: + // song = Length of song (in 1/64000ths second) + // fade = Length of fade (in 1/64000ths second) + // + // Out: + // Total length + + u32 SetDSPLength(u32 song, u32 fade); + + + //********************************************************************************************** + // Set DSP Base Pitch + // + // Adjusts the pitch of the DSP + // + // Thread safe: + // No + // + // In: + // base = Base sample rate (32000 = Normal pitch, 32458 = Old SB cards, 32768 = Old ZSNES) + // + // Destroys: + // EAX + + void SetDSPPitch(u32 base); + + + //********************************************************************************************** + // Set Voice Stereo Separation + // + // Sets the amount to adjust the panning position of each voice + // + // Thread safe: + // No + // + // In: + // sep = Separation [1.16] + // 1.0 - full separation (output is either left, center, or right) + // 0.5 - normal separation (output is unchanged) + // 0 - no separation (output is completely monaural) + // + // Destroys: + // EAX,ST(0-7) + + void SetDSPStereo(u32 sep); + + + //********************************************************************************************** + // Set Echo Feedback Crosstalk + // + // Sets the amount of crosstalk between the left and right channel during echo feedback + // + // Thread safe: + // No + // + // In: + // leak = Crosstalk amount [-1.15] + // 1.0 - no crosstalk (SNES) + // 0 - full crosstalk (mono/center) + // -1.0 - inverse crosstalk (L/R swapped) + // + // Destroys: + // EAX + + void SetDSPEFBCT(s32 leak); + + + //********************************************************************************************** + // Set Voice Mute + // + // Prevents a voice's output from being mixed in with the main output + // + // Thread safe: + // Yes + // + // In: + // voice = Voice to mute (0-7, only bits 2-0 are used) + // state = see enum MuteState + // + // Out: + // true, if voice is muted + + bool SetDSPVoiceMute(u32 voice, MuteState state); + + + //********************************************************************************************** + // DSP Data Port + // + // Writes a value to a specified DSP register and alters the DSP accordingly. If the register + // write affects the output generated by the DSP, this function returns true. + // + // Note: + // SetDSPReg() does not call the debugging callback + // + // Thread safe: + // No + // + // In: + // reg = DSP Address + // val = DSP Data + // + // Out: + // true, if the DSP state was affected + + bool SetDSPReg(u8 reg, u8 val); + + + //********************************************************************************************** + // Emulate DSP + // + // Emulates the DSP of the SNES + // + // Notes: + // If 'pBuf' is NULL, the routine MIX_NONE will be used + // Range checking is performed on 'len' + // + // Callers should use EmuAPU() instead + // + // In: + // pBuf-> Buffer to store output + // len = Length of buffer (in samples, can be 0) + // + // Out: + // -> End of buffer + // + // Destroys: + // ST(0-7) + + void* EmuDSP(void* pBuf, s32 len); + + + //********************************************************************************************** + // Play Sound Source + // + // Plays a single sound source using the settings defined by SetDSPOpt. All samples are output + // in integer form. The output is attenuated -6dB. + // + // Admittedly, this function is poorly coded in that it does two different things depending on + // the value of the last parameter. + // + // Note: + // If the source data is within APU RAM, the source pointer will wrap around within APU RAM + // + // Thread safe: + // Yes, but can only be used to play one sound at a time + // + // In: + // pSrc-> Sound source to play (if upper 16 bits are 0, lower 16 bits will index APU RAM) + // loop = Block to loop on + // rate = Logical sample rate to play back at (actual playback rate may be less) + // + // pSrc-> Buffer to store decompressed and resampled data + // loop = Number of samples to generate + // rate = 0 + // + // Out: + // rate != 0, return value is sample rate sound will be played back at + // rate == 0, return value -> end of buffer + + void* PlaySrc(void* pSrc, u32 loop, u32 rate = 0); + + + //********************************************************************************************** + // Unpack Sound Source + // + // Decompresses a series of BRR blocks into 16-bit PCM samples + // + // Notes: + // Regardless of the value in 'num', unpacking will stop when the end block is reached. + // The previous samples are only needed if you're making multiple calls to UnpackWave. If + // you're decompressing the entire sound in one call, you can disregard them. + // If the source data is within APU RAM, the source pointer will wrap around within APU RAM, + // though no more than 7281 BRR blocks will be processed. + // + // Thread safe: + // Yes, but can only be used to unpack one sound at a time + // + // In: + // pSmp -> Location to store decompressed samples + // pBlk -> Sound source to decompress (if upper 16 bits are 0, lower 16 bits will index APU RAM) + // num = Number of blocks to decompress (0 to decompress entire sound) + // opts = Options (only one for now) + // BRR_OLDSMP = Traditional method of decompression + // prev1-> First previous sample (can be NULL if not needed) + // prev2-> Second previous sample + // + // Out: + // Number of blocks decompressed + + u32 UnpackSrc(s16* pSmp, const void* pBlk, u32 num, + Set<UnpackOpts> opts = Set<UnpackOpts>(), + s32* prev1 = 0, s32* prev2 = 0); + + + //********************************************************************************************** + // Pack Sound Source + // + // Compresses a series of 16-bit PCM samples into BRR blocks. Generally sounds are packed in + // one call. However, if you want to implement some sort of real-time compression stream you + // can makemultiple calls to PackSrc. See the options description below for more information on + // continuing compression. + // + // THIS FUNCTION HAS NOT BEEN FINISHED AND DOES NOT PACK LOOPED SOUNDS + // + // Notes: + // The previous samples are only needed if you're making multiple calls to PackSrc. If + // you're compressing the entire sound in one call, you can disregard them. + // No checking is performed on output pointer to make sure the data wraps around if it + // resides within APU RAM. + // The looping section must be at least 16 samples. + // + // Thread safe: + // Yes, but can only be used to pack one sound at a time + // + // In: + // pBlk -> Location to store bit-rate reduced blocks + // pSmp -> Samples to compress + // pLen -> Number of samples to compress + // pLoop-> Sample index of loop point. Set this to NULL for one-shot sounds. This parameter + // is ignored if 'prev1' is not NULL. + // opts = Options. The way the options behave is different depending on if 'prev1' is NULL. + // + // If prev1 is NULL: + // BRR_END = This option has no effect. The last block's end flag will always be + // set. + // BRR_LOOP = By default, if pLoop is not NULL the last block's loop flag will + // automatically be set. This option causes the loop flag to be set in + // every block regardless of pLoop. + // BRR_LINEAR = By default, the best compression filter will be determined for each + // block. This option forces linear compression (filter 0) to be used + // for all blocks. + // BRR_NOINIT = By default, a block of silence is inserted at the beginning of the + // sound source. This option skips the silence and encodes samples into + // the first block. (The first block, whether it contains silence or + // samples, will always use linear compression. This is necessary in + // order for the decompression filter to be properly initialized when + // the source is played.) + // + // If prev1 is not NULL: + // BRR_END = Sets the end flag in the last block + // BRR_LOOP = This option causes the loop flag to be set in every block. + // BRR_LINEAR = By default, the best compression filter will be determined for each + // block. This option forces linear compression (filter 0) to be used + // for all blocks. + // BRR_NOINIT = By default, the first block is compressed with linear compression. + // This option causes the first block to be compressed with whichever + // filter is the best. + // prev1-> First previous sample. NULL if the value of the previous samples is not needed. + // (Generally the previous samples are only used if making multiple calls to PackSrc.) + // prev2-> Second previous sample. If this is NULL, 'prev1' is treated as NULL also. + // + // Out: + // -> End of the data stored in pBlk, NULL if an error occurred. + // Possible errors: + // Length is 0 + // Loop point is greater than the number of samples + // Loop length is less than 16 + // pLen -> Number of blocks in sound source + // pLoop-> Starting block of loop + + void* PackSrc(void* pBlk, const void* pSmp, u32* pLen, u32* pLoop, + Set<PackOpts> opts = Set<PackOpts>(), + s32* prev1 = 0, s32* prev2 = 0); + + + //********************************************************************************************** + // VMax to Decible + // + // Generates decible values for the peak output of each voice and the main output, the purpose + // of which is to provide the user with some sort of PPM (Peak Program Meter) visualization. + // + // The values generated can be 32-bit floats or 16.16 fixed-point integers. When floating-point + // numbers are chosen, the return values are in actual dB starting with -96 (which represents + // -infinity) on up to +0. When integers are chosen, the return values are in fixed point + // notation [16.16] and are positive starting with 0 (which represents -infinity) on up to +96 + // (or 600000h, representing +0db). + // + // After calling VMax2dB, the vMax values for both voice and main outputs are set to 0. + // + // Notes: + // The values for the main output will be higher than 0dB if clipping has occurred. + // + // In: + // p -> Structure to store 32-bit numbers + // + // Destroys: + // EAX + + void VMax2dBf(MaxOutF* p); + void VMax2dBi(MaxOutI* p); + +} // extern "C" + +#endif //!SNESAPU_DLL + +} // namespace SNES +} // namespace A2 + diff --git a/lib/snesapu/SNES/SNESAPU/Makefile.in b/lib/snesapu/SNES/SNESAPU/Makefile.in new file mode 100644 index 0000000000..40494dc346 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/Makefile.in @@ -0,0 +1,35 @@ +ARCH=@ARCH@ +AS=nasm +OBJS=SPC700.o APU.o DSP.o SNESAPU.o +CFLAGS +=-D_LINUX -fPIC -I@abs_top_srcdir@/lib/snesapu +CXXFLAGS +=-D_LINUX -fPIC -I@abs_top_srcdir@/lib/snesapu +ifeq ($(findstring osx,$(ARCH)), osx) + CFLAGS+=-fno-common + CXXFLAGS+=-fno-common + ASFLAGS=-Xvc -DLINUX -DCDECL -O1 -f macho +else + ASFLAGS=-Xvc -DLINUX -O1 -f elf +endif + +SYSDIR=@abs_top_srcdir@/system/players/paplayer/ +SO=SNESAPU-$(ARCH).so +SLIB=$(SYSDIR)/$(SO) + +$(SLIB): $(OBJS) +ifeq ($(findstring osx,$(ARCH)), osx) + $(CC) -bundle -undefined dynamic_lookup \ + -isysroot /Developer/SDKs/MacOSX10.4u.sdk \ + -mmacosx-version-min=10.4 \ + -read_only_relocs suppress \ + -o $@ $(OBJS) + @abs_top_srcdir@/tools/Mach5/wrapper.rb $@;mv output.so $@ + chmod +x $@ +else + $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $(OBJS) \ + `cat @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.def` @abs_top_srcdir@/xbmc/cores/DllLoader/exports/wrapper.o +endif + +%o : %Asm + $(AS) $(ASFLAGS) -o $@ $< + +include @abs_top_srcdir@/Makefile.include diff --git a/lib/snesapu/SNES/SNESAPU/ReadMe.Txt b/lib/snesapu/SNES/SNESAPU/ReadMe.Txt new file mode 100644 index 0000000000..20057aa39e --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/ReadMe.Txt @@ -0,0 +1,204 @@ +Super Nintendo Entertainment System(tm) Audio Processor Unit Emulator +Dynamically Linking Library version 3.0 +============================================================================== + +This is the source code to SNESAPU.DLL, a Super NES APU emulator written for +IA-32 platforms. + +NOTE: The SPC700 emulator was rewritten for v3.0. This version of SNESAPU is + NOT backward compatible with versions older than 3.0. + +SNESAPU is the most accurate emulator available. Its mixing pipeline is +almost flawless (a few aspects are not precisely emulated due to bugs in the +hardware DSP microcode), and under some circumstances produces output +identical to the original hardware, bit for bit. + + + +Using the source in your own programs +------------------------------------- + +NOTE: When distributing SNESAPU.DLL with your own programs, you should check + the SNESAPU.DLL version at run-time to make sure it's compatible with + the version your code is compiled for. This should be done to prevent + users from switching your distributed copy of SNESAPU.DLL with another + build that may be incompatible. See the section Version Information + below. + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) +any later version. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the +Free Software Foundation, Inc., +59 Temple Place, Suite 330, +Boston, MA 02111-1307 USA + + + +Files +----- + +All of the emulation code was written in assembly for NASM v0.98.38 and +has been successfully compiled and executed under both Linux and Windows. + +The C++ code is used to create the DLL interface for 32-bit Windows platforms +and is only necessary if you're building SNESAPU.DLL. The SNESAPU project was +created with Visual C++ v6.0 SP6 with the output set to compile into the +C:\Windows\system32 directory. You will need to change the custom build steps +in the project to correctly locate nasmw.exe. + +All assembly source was written with tab stops set to three characters. +The C source was written with tab stops set to four characters. + +NOTES: Because of alignment issues with Win32 object files, extra padding may + need to be inserted into the DSP emulator. If the BSS is not page + aligned, a debug breakpoint will halt execution during initialization. + See line 236 in DSP.Asm for more information. + + The functions in SNESAPU.DLL are NOT programmed to be thread safe. + +SNESAPU - Main program +SPC700 - Executes programs written for the SPC700 +DSP - Performs all mixing and output +APU - Groups the SPC700 and DSP together and adds extra functionality + + + +Version Information +------------------- + +Use the Windows function VerQueryValue() to retrieve a pointer to the +VS_FIXEDFILEINFO structure. The example projects show how to do this. + +The FileVersion fields will contain the current version of SNESAPU, while +the ProductVersion fields will contain the oldest version this bulid of +SNESAPU is backward compatible with. + +The product version only increases when changes are made to the SNESAPU +interface. Additional emulation options or changes made to internal data +structures do not affect the interface. i.e. if a new interpolation function +is added, future versions will remain backward compatible because SetAPUOpt() +ignores invalid options. + +The four version fields are used as follows: + version major, 0 to 99 + version minor, 0 to 99 + sub version, 0 to 26 (If this value is != 0, add 0x60 to get the letter) + build number, 0 to 255 + +For example, if dwFileVersionMS == 0x2000A and dwFileVersionLS == 0x70005, +the version number is 2.10g build 5. + + + +Implementation +-------------- + +I wanted to create an emulator that had versitilty and was easy to use. +Since those two objectives fall on opposite ends of the spectrum, it was hard +to find a middle ground. I made the interface as easy as I could while still +retaining the functionality I wanted. + +I'll explain how to create a basic SPC player. If you want to add additional +features, you'll have to look at the header files for descriptions of the +functions. + +First, you need to link the DLL. If you include SNESAPU.h and SNESAPU.lib in +your project, your program should automatically load the DLL at run-time. +Otherwise, you'll need to use the LoadLibrary and GetProcAddress functions. + +Upon linking the DLL, the emulator will be initialized. The SPC700 will be +in a a state identical to powerup in the Super NES, and the DSP will be set +to return 16-bit stereo output at 32kHz. You can begin emulation at this +point, though all you will hear is silence as the SPC700 waits in a loop to +receive data. + +Allocate a buffer of 66048 bytes. Read an SPC file into this buffer, then +pass a pointer to the buffer to LoadSPCFile(). This function will reset the +APU state and copy the necessary data from the file. + +To emulate the APU, call EmuAPU(). Emulation can be based on clock cycles +or samples. + +Keep track of how much time has gone by by calling GetSPCTime(). One second +is equal to 64,000 counter increases. The counter is set to 0 by ResetAPU(). + +Summary: + +1) Link the DLL to initialize the emulator +2) Call LoadSPCFile() to copy the SPC into the APU +3) Call EmuAPU() to generate audio output +4) Go to step 3 until you wish to stop +5) To load another song, goto step 2 +6) Unlink the DLL + + + +DSP Registers +------------- + +The DSPRAM structure may look a bit intimidating, but it actually correctly +maps to the DSP registers and is quite easy to use. Here are some examples +of how to access the registers: + +Find out the instrument playing on voice 3 - + +i = dsp.voice[3].srcn; + + +Find out the delay of the echo in milliseconds - + +i = dsp.edl << 4; + + +Find out if a song uses echo filtering - + +i = dsp.fir[0].c; +if (i == 0x7F) i = 0; + +for (j=1; j<8; j++) + i |= dsp.fir[j].c; + +if (i) filter = true; + + + +Known Emulation Inaccuracies +---------------------------- + +- Songs from the game Vortex to not play correctly + +- 16-bit memory accesses do not wrap around + Instructions like MOV YA,0FFh or JMP [!0FFFFh+X] (where X = 0) will load the + second byte from the next contiguous location in physical RAM instead of + wrapping around an 8- or 16-bit memory address. However, memory address + calculations correctly wrap around, i.e. MOV A,0F0h+X (where X = 11h) will + properly load the byte at DP 01h. + +- Read checking is not performed on address calculations, i.e. MOV A,[0FDh]+Y + (where Y = 0) will not clear counters 0 and 1. + +- Read checking is not performed on AND1 when C = 0 + + + +Thanks +------ + +The SNESAPU.DLL would not exist without these people: + +Datschge, CaitSith, Gary Henderson, Marius Fodor, nZero, Nitro, Sycraft, and +the contributors to SNESmusic.org who keep finding all of the bugs + +SNESAPU is copyright (C)2001-2006 Alpha-II Productions (www.alpha-ii.com) + +"Super NES" and "Super Nintendo Entertainment System" are registered +trademarks of Nintendo (www.nintendo.com)
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/SNESAPU.cpp b/lib/snesapu/SNES/SNESAPU/SNESAPU.cpp new file mode 100644 index 0000000000..c55d15a383 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.cpp @@ -0,0 +1,56 @@ +/*************************************************************************************************** +* SNES(tm) APU Emulator DLL * +* Copyright (C)2001-2006 Alpha-II Productions * +***************************************************************************************************/ + +#define DONT_IMPORT_SNESAPU + +#include "Types.h" +#include "A2Misc.h" +#include "SNESAPU.h" + +using namespace A2::SNES; + + +//************************************************************************************************** +// Variables + +static u32 ApuOpt; + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Functions + +//************************************************************************************************** +extern "C" b8 _DllMainCRTStartup(u32 hinst, u32 dwReason, u32 lpReserved) +{ + switch (dwReason) + { + case 1: //DLL_PROCESS_ATTACH + ApuOpt = InitAPU(); + ResetAPU(); + break; + + case 0: //DLL_PROCESS_DETACH + ShutAPU(); + break; + } + + return 1; +} + + +//************************************************************************************************** +void* GetAPUData(DataType dt) +{ + switch(dt) + { + case(DATA_OPTIONS): return reinterpret_cast<void*>(ApuOpt); + case(DATA_RAM): return static_cast<void*>(pAPURAM); + case(DATA_DSP): return static_cast<void*>(&dsp); + case(DATA_MIX): return static_cast<void*>(&mix); + case(DATA_PROFILE): return static_cast<void*>((ApuOpt & SA_PROFILE) ? &profile : 0); + default: return (void*)0; + } +} + diff --git a/lib/snesapu/SNES/SNESAPU/SNESAPU.def b/lib/snesapu/SNES/SNESAPU/SNESAPU.def new file mode 100644 index 0000000000..b00cab7b63 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.def @@ -0,0 +1,40 @@ +LIBRARY SNESAPU + +DESCRIPTION "SNES APU Emulator v3.0" + +EXPORTS + GetAPUData + + ResetAPU + LoadSPCFile + SaveAPU + RestoreAPU + SetAPUOpt + SetAPUSmpClk + EmuAPU + SeekAPU + + SetSPCDbg + SetAPURAM + SetAPUPort + GetAPUPort + GetSPCTime + EmuSPC + + GetProcInfo + SetDSPDbg + SetDSPAmp + GetDSPAmp + SetDSPAAR + SetDSPLength + SetDSPPitch + SetDSPStereo + SetDSPEFBCT + SetDSPVoiceMute + SetDSPReg + EmuDSP + PlaySrc + UnpackSrc + PackSrc + VMax2dBf + VMax2dBi
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/SNESAPU.h b/lib/snesapu/SNES/SNESAPU/SNESAPU.h new file mode 100644 index 0000000000..351030d176 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.h @@ -0,0 +1,232 @@ +/*************************************************************************************************** +* Program: Super Nintendo Entertainment System(tm) Audio Processing Unit Emulator DLL * +* Platform: Intel 80386 & MMX * +* Programmer: Anti Resonance * +* * +* "SNES" and "Super Nintendo Entertainment System" are trademarks of Nintendo Co., Limited and its * +* subsidiary companies. * +* * +* This library is free software; you can redistribute it and/or modify it under the terms of the * +* GNU Lesser General Public License as published by the Free Software Foundation; either version * +* 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * +* See the GNU Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License along with this * +* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * +* Boston, MA 02111-1307 USA * +* * +* ------------------------------------------------------------------------------------------------ * +* Revision History: * +* * +* 2005.10.28 SNESAmp v3.3 * +* + Changed C interface to C++ * +* + Removed all sample packing code * +* + Made GetAPUData() better * +* * +* 2004.xx.xx SNESAmp v3.2 * +* - Added a build check for SSE2 processors in EightToSixteen() * +* - Fixed the memcpy() in BitRateReduce() when the loop point was < 16 * +* * +* 2004.01.26 * +* + Added BitRateReduce, a better function for creating BBR blocks from raw samples * +* * +* 2003.02.10 SNESAmp v2.99 * +* + Added version information to the DLL * +* + Moved GetAPUData to SNESAPU.cpp, since it's only needed for SNESAPU.DLL * +* + Moved memory allocation into SNESAPU.cpp, since memory allocation is system, and possibly * +* application, specific * +* * +* 2001.01.01 SNESAmp v2.0 * +* + Made some changes to the internal interface with the DSP * +* * +* 2000.12.14 Super Jukebox v3.0 * +* Copyright (C)2001-2006 Alpha-II Productions * +***************************************************************************************************/ + +#ifdef __SNESAPU_H__ +#error SNESAPU.h may only be #included once per file +#endif +#define __SNESAPU_H__ + +#define SNESAPU_DLL 0x03000000 //Don't include local function/variable definitions + +#include "SPC700.h" +#include "DSP.h" +#include "APU.h" + +namespace A2 +{ +namespace SNES +{ + + //********************************************************************************************** + // Type Definitions + + //See GetAPUData() for explanations of each + enum DataType + { + DATA_OPTIONS = -1, + DATA_RAM, + DATA_DSP, + DATA_MIX, + DATA_PROFILE + }; + +#ifndef DONT_IMPORT_SNESAPU + + //Function pointers to SNESAPU + struct Functions + { + void* (__stdcall *GetAPUData)(DataType type); + + void (__stdcall *ResetAPU)(); + void (__stdcall *LoadSPCFile)(const void* pSPC); + void (__stdcall *SaveAPU)(SPCState* pSPC, DSPState* pDSP); + void (__stdcall *RestoreAPU)(const SPCState& spc, const DSPState& dsp); + void (__stdcall *SetAPUOpt)(Mixing, u32 chn, u32 bits, u32 rate, + DSPInter inter = INT_INVALID, + Set<DSPOpts> opts = ~Set<DSPOpts>()); + void (__stdcall *SetAPUSmpClk)(u32 speed); + void* (__stdcall *EmuAPU)(void* pBuf, u32 cycles, u32 samples); + void (__stdcall *SeekAPU)(u32 time, bool fast); + + SPCDebug* (__stdcall *SetSPCDbg)(SPCDebug* pTrace, + Set<SPCDbgOpts> opts = Set<SPCDbgOpts>()); + void (__stdcall *SetAPURAM)(u32 addr, u8 val); + void (__stdcall *SetAPUPort)(u32 port, u8 val); + u8 (__stdcall *GetAPUPort)(u32 port); + u32 (__stdcall *GetSPCTime)(); + s32 (__stdcall *EmuSPC)(u32 cyc); + + u32 (__stdcall *GetProcInfo)(); + DSPDebug* (__stdcall *SetDSPDbg)(DSPDebug* pTrace, + Set<DSPDbgOpts> opts = Set<DSPDbgOpts>()); + void (__stdcall *SetDSPAmp)(u32 level); + u32 (__stdcall *GetDSPAmp)(); + void (__stdcall *SetDSPAAR)(AARType type, u32 thresh, u32 min, u32 max); + u32 (__stdcall *SetDSPLength)(u32 song, u32 fade); + void (__stdcall *SetDSPPitch)(u32 base); + void (__stdcall *SetDSPStereo)(u32 sep); + void (__stdcall *SetDSPEFBCT)(s32 leak); + bool (__stdcall *SetDSPVoiceMute)(u32 voice, MuteState state); + bool (__stdcall *SetDSPReg)(u8 reg, u8 val); + void* (__stdcall *EmuDSP)(void* pBuf, s32 size); + void* (__stdcall *PlaySrc)(void* pSrc, u32 loop, u32 rate); + u32 (__stdcall *UnpackSrc)(s16* pBuf, const void* pSrc, u32 num, + Set<UnpackOpts> opts = Set<UnpackOpts>(), + s32* prev1 = 0, s32* prev2 = 0); + void* (__stdcall *PackSrc)(void* pBlk, const void* pSmp, u32* pLen, u32* pLoop, + Set<PackOpts> opts = Set<PackOpts>(), + s32* prev1 = 0, s32* prev2 = 0); + void (__stdcall *VMax2dBf)(MaxOutF* p); + void (__stdcall *VMax2dBi)(MaxOutI* p); + }; + + + //********************************************************************************************** + //External Functions - See other header files for exported function descriptions + + #ifndef import + #define import __declspec(dllimport) + #endif + +extern "C" { + + //********************************************************************************************** + // Get Pointer to APU Internal Data + // + // Returns pointers to internal data structures. Access is provided solely for debugging + // purposes. All data should be considered read-only. + // + // DATA_OPTIONS + // Returns the build options specified in APU.Inc (the return value is a u32, not a void*) + // bits 23-0 Options (see #define SA_??? in APU.h) + // bits 31-24 SPC700 clock divisor (divide APU_CLK by the clock divisor to get the emulated + // clock speed of the SPC700) + // + // DATA_RAM + // Returns a pointer to the 64KB of APU RAM - apuRAM[65536] + // + // DATA_DSP + // Returns a pointer to the 128 bytes of DSP registers - DSPReg (DSP.h) + // + // DATA_MIX + // Returns a pointer to an array of 8 MixData structures - Voice[8] (DSP.h) + // + // DATA_PROFILE + // Returns a pointer to a profiling structure - APUProf (SPC700.h) + // If profiling isn't enabled (see #define SA_PROFILE, APU.h) the pointer returned will be + // NULL. + // + // In: + // type = Pointer to retrieve (see DataType) + // + // Out: + // Pointer to requested data + + import void* __stdcall GetAPUData(DataType type); + + + //********************************************************************************************** + // APU Functions + + import void __stdcall ResetAPU(); + import void __stdcall LoadSPCFile(const void* pSPC); + import void __stdcall SaveAPU(SPCState* pSPC, DSPState* pDSP); + import void __stdcall RestoreAPU(const SPCState& spc, const DSPState& dsp); + import void __stdcall SetAPUOpt(Mixing mix, u32 chn, u32 bits, u32 rate, + DSPInter inter = INT_INVALID, + Set<DSPOpts> opts = ~Set<DSPOpts>()); + import void __stdcall SetAPUSmpClk(u32 speed); + import void* __stdcall EmuAPU(void* pBuf, u32 cycles, u32 samples); + import void __stdcall SeekAPU(u32 time, bool fast); + + + //********************************************************************************************** + // SPC700 Functions + + import SPCDebug* __stdcall SetSPCDbg(SPCDebug* pTrace, + Set<SPCDbgOpts> opts = Set<SPCDbgOpts>()); + import void __stdcall SetAPURAM(u32 addr, u8 val); + import void __stdcall SetAPUPort(u32 port, u8 val); + import u8 __stdcall GetAPUPort(u32 port); + import u32 __stdcall GetSPCTime(); + import s32 __stdcall EmuSPC(u32 cyc); + + + //********************************************************************************************** + // DSP Functions + + import u32 __stdcall GetProcInfo(); + import DSPDebug* __stdcall SetDSPDbg(DSPDebug* pTrace, + Set<DSPDbgOpts> opts = Set<DSPDbgOpts>()); + import void __stdcall SetDSPAmp(u32 level); + import u32 __stdcall GetDSPAmp(); + import void __stdcall SetDSPAAR(AARType type, u32 thresh, u32 min, u32 max); + import u32 __stdcall SetDSPLength(u32 song, u32 fade); + import void __stdcall SetDSPPitch(u32 base); + import void __stdcall SetDSPStereo(u32 sep); + import void __stdcall SetDSPEFBCT(s32 leak); + import bool __stdcall SetDSPVoiceMute(u32 voice, MuteState state); + import bool __stdcall SetDSPReg(u8 reg, u8 val); + import void* __stdcall EmuDSP(void* pBuf, s32 size); + import void* __stdcall PlaySrc(void* pSrc, u32 loop, u32 rate = 0); + import u32 __stdcall UnpackSrc(s16* pBuf, const void* pSrc, u32 num, + Set<UnpackOpts> opts = Set<UnpackOpts>(), + s32* prev1 = 0, s32* prev2 = 0); + import void* __stdcall PackSrc(void* pBlk, const void* pSmp, u32* pLen, u32* pLoop, + Set<PackOpts> opts = Set<PackOpts>(), + s32* prev1 = 0, s32* prev2 = 0); + import void __stdcall VMax2dBf(MaxOutF* p); + import void __stdcall VMax2dBi(MaxOutI* p); + +} // extern "C" + +#endif //DONT_IMPORT_SNESAPU + +} // namespace SNES +} // namespace A2 + diff --git a/lib/snesapu/SNES/SNESAPU/SNESAPU.lib b/lib/snesapu/SNES/SNESAPU/SNESAPU.lib Binary files differnew file mode 100644 index 0000000000..d65268b2dc --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.lib diff --git a/lib/snesapu/SNES/SNESAPU/SNESAPU.sln b/lib/snesapu/SNES/SNESAPU/SNESAPU.sln new file mode 100644 index 0000000000..a04f15c04e --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SNESAPU", "SNESAPU.vcproj", "{9955D8F2-245C-4C30-AA16-6784BBB84E1E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {9955D8F2-245C-4C30-AA16-6784BBB84E1E}.Debug.ActiveCfg = Debug|Win32 + {9955D8F2-245C-4C30-AA16-6784BBB84E1E}.Debug.Build.0 = Debug|Win32 + {9955D8F2-245C-4C30-AA16-6784BBB84E1E}.Release.ActiveCfg = Release|Win32 + {9955D8F2-245C-4C30-AA16-6784BBB84E1E}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/lib/snesapu/SNES/SNESAPU/SNESAPU.vcproj b/lib/snesapu/SNES/SNESAPU/SNESAPU.vcproj new file mode 100644 index 0000000000..cee8b0c762 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.vcproj @@ -0,0 +1,290 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="SNESAPU" + ProjectGUID="{9955D8F2-245C-4C30-AA16-6784BBB84E1E}" + SccProjectName="" + SccLocalPath=""> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Release|Win32" + OutputDirectory=".\Release" + IntermediateDirectory=".\Release" + ConfigurationType="2" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalOptions="/opt:nowin98 " + Optimization="2" + InlineFunctionExpansion="2" + OptimizeForProcessor="2" + AdditionalIncludeDirectories="../.." + PreprocessorDefinitions="NDEBUG;_USRDLL" + StringPooling="TRUE" + RuntimeLibrary="4" + EnableFunctionLevelLinking="TRUE" + PrecompiledHeaderFile=".\Release/SNESAPU.pch" + AssemblerOutput="4" + AssemblerListingLocation=".\Release/" + ObjectFile=".\Release/" + ProgramDataBaseFileName=".\Release/" + BrowseInformation="1" + BrowseInformationFile=".\Release/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + CallingConvention="2" + CompileAs="0"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="odbc32.lib odbccp32.lib" + OutputFile="$(OUTPUT)\SNESAPU.DLL" + Version="2.40" + LinkIncremental="1" + SuppressStartupBanner="TRUE" + IgnoreAllDefaultLibraries="TRUE" + ModuleDefinitionFile=".\SNESAPU.def" + ImportLibrary="SNESAPU.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool" + PreprocessorDefinitions="NDEBUG" + SuppressStartupBanner="TRUE" + TargetEnvironment="1" + TypeLibraryName=".\Release/SNESAPU.tlb" + HeaderFileName=""/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="NDEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Debug|Win32" + OutputDirectory=".\Debug" + IntermediateDirectory=".\Debug" + ConfigurationType="2" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\.." + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SNESAPU_EXPORTS" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + PrecompiledHeaderFile=".\Debug/SNESAPU.pch" + AssemblerOutput="4" + AssemblerListingLocation=".\Debug/" + ObjectFile=".\Debug/" + ProgramDataBaseFileName=".\Debug/" + WarningLevel="3" + SuppressStartupBanner="TRUE" + DebugInformationFormat="4" + CallingConvention="2" + CompileAs="0"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="odbc32.lib odbccp32.lib" + OutputFile="$(OUTPUT)/snesapu.dll" + Version="0.96" + LinkIncremental="2" + SuppressStartupBanner="TRUE" + ModuleDefinitionFile=".\SNESAPU.def" + GenerateDebugInformation="TRUE" + ImportLibrary=".\Debug/SNESAPU.lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool" + PreprocessorDefinitions="_DEBUG" + MkTypLibCompatible="TRUE" + SuppressStartupBanner="TRUE" + TargetEnvironment="1" + TypeLibraryName=".\Debug/SNESAPU.tlb" + HeaderFileName=""/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool" + PreprocessorDefinitions="_DEBUG" + Culture="1033"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Library" + Filter=""> + <File + RelativePath="SNESAPU.cpp"> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="NDEBUG;_MBCS;_USRDLL;$(NoInherit)" + BrowseInformation="1"/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;SNESAPU_EXPORTS;$(NoInherit)" + BasicRuntimeChecks="3"/> + </FileConfiguration> + </File> + <File + RelativePath="SNESAPU.def"> + </File> + <File + RelativePath="SNESAPU.h"> + </File> + <File + RelativePath="Version.rc"> + </File> + </Filter> + <Filter + Name="Emulator" + Filter=""> + <File + RelativePath="APU.Asm"> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCustomBuildTool" + Description="Assembling $(InputPath)" + CommandLine="nasmw -Xvc -DWIN32 -DSTDCALL -O1 -f win32 -o "$(IntDir)"\"$(InputName)".obj "$(InputName)".Asm -l "$(InputName)".Lst" + AdditionalDependencies="..\..\Macro.Inc;APU.Inc;DSP.Inc;SPC700.Inc;" + Outputs="$(IntDir)\$(InputName).obj"/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCustomBuildTool" + Description="Assembling $(InputPath)" + CommandLine="nasmw -Xvc -DWIN32 -DSTDCALL -O1 -f win32 -o "$(IntDir)"\"$(InputName)".obj "$(InputName)".Asm" + AdditionalDependencies="..\..\Macro.Inc;APU.Inc;DSP.Inc;SPC700.Inc;" + Outputs="$(IntDir)\$(InputName).obj"/> + </FileConfiguration> + </File> + <File + RelativePath="APU.h"> + </File> + <File + RelativePath="APU.Inc"> + </File> + <File + RelativePath="DSP.Asm"> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCustomBuildTool" + Description="Assembling $(InputPath)" + CommandLine="nasmw -Xvc -DWIN32 -DSTDCALL -O1 -f win32 -o "$(IntDir)"\"$(InputName)".obj "$(InputName)".Asm -l "$(InputName)".Lst" + AdditionalDependencies="..\..\Macro.Inc;APU.Inc;DSP.Inc;SPC700.Inc;" + Outputs="$(IntDir)\$(InputName).obj"/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCustomBuildTool" + Description="Assembling $(InputPath)" + CommandLine="nasmw -Xvc -DWIN32 -DSTDCALL -O1 -f win32 -o "$(IntDir)"\"$(InputName)".obj "$(InputName)".Asm -l "$(InputName)".Lst" + AdditionalDependencies="..\..\Macro.Inc;DSP.Inc;SPC700.Inc;APU.Inc;" + Outputs="$(IntDir)\$(InputName).obj"/> + </FileConfiguration> + </File> + <File + RelativePath="DSP.h"> + </File> + <File + RelativePath="DSP.Inc"> + </File> + <File + RelativePath="..\..\Macro.Inc"> + </File> + <File + RelativePath="SPC700.Asm"> + <FileConfiguration + Name="Release|Win32"> + <Tool + Name="VCCustomBuildTool" + Description="Assembling $(InputPath)" + CommandLine="nasmw -Xvc -DWIN32 -DSTDCALL -O1 -f win32 -o "$(IntDir)"\"$(InputName)".obj "$(InputName)".Asm -l "$(InputName)".Lst" + AdditionalDependencies="..\..\Macro.Inc;APU.Inc;DSP.Inc;SPC700.Inc;" + Outputs="$(IntDir)\$(InputName).obj"/> + </FileConfiguration> + <FileConfiguration + Name="Debug|Win32"> + <Tool + Name="VCCustomBuildTool" + Description="Assembling $(InputPath)" + CommandLine="nasmw -Xvc -DWIN32 -DSTDCALL -O1 -f win32 -o "$(IntDir)"\"$(InputName)".obj "$(InputName)".Asm" + AdditionalDependencies="..\..\Macro.Inc;SPC700.Inc;DSP.Inc;APU.Inc;" + Outputs="$(IntDir)\$(InputName).obj"/> + </FileConfiguration> + </File> + <File + RelativePath="SPC700.h"> + </File> + <File + RelativePath="SPC700.Inc"> + </File> + </Filter> + <File + RelativePath="..\..\A2Misc.h"> + </File> + <File + RelativePath="ReadMe.txt"> + </File> + <File + RelativePath="..\..\Types.h"> + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/lib/snesapu/SNES/SNESAPU/SPC700.Asm b/lib/snesapu/SNES/SNESAPU/SPC700.Asm new file mode 100644 index 0000000000..1f9f49ffbf --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SPC700.Asm @@ -0,0 +1,4448 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Sony SPC700 Emulator +; Copyright (C)1999-2006 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +CPU 386 +BITS 32 + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Header files + +%include "../../Macro.Inc" +%include "APU.Inc" +%include "DSP.Inc" +%define INTERNAL +%include "SPC700.Inc" + +EXTERN SetDSPReg_A ;DSP.Asm - Used to write to the DSP registers + +%if DSPINTEG +EXTERN UpdateDSP ;DSP.Asm - Used to keep DSP in sync with SPC700 +%endif + +%if PROFILE +GLOBAL profile ;Needed by APU.Asm and DSP.Asm +%endif + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Equates + +;Clock cycles per operation ----------------- +T64_CYC EQU 384 ;64kHz timer clock divisor + +;Cleanup options ---------------------------- +na EQU 0 ;Option is not applicable to this instruction +RD EQU 010000b ;Instruction reads 8 bits from the direct page +RD16 EQU 010010b ; 16 bits from the direct page +RBIT EQU 010001b ; 1 bit from an absolute address +RA EQU 010101b ; 8 bits from an absolute address +WD EQU 100000b ;Instruction writes 8 bits to the direct page +WD16 EQU 100010b ; 16 bits to the direct page +WBIT EQU 100001b ; 1 bit to an absolute address +WA EQU 100101b ; 8 bits to an absolute address +RW EQU 110000b ;Instruction reads and writes 8 bits in the DP + +;Registers ---------------------------------- +%define PC SI ;Intel equivilants to SPC registers +%define A AL +%define Y AH +%define YA AX +%define X CH + +;Pointers ----------------------------------- +%define OP ESI ;Instruction operand +%define DPI EBX ;Direct Page Index +%define ABSL EDX+EDI ;Absolute Location +%define RAM EDI ;64K RAM + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Macros + +;All global variables are stored in the 128 bytes before APU RAM. Since EDI always points to APU +;RAM during emulation, this allows global variables to be accessed with an 8-bit displacement +;relative to EDI instead of a full 32-bit pointer. The end result is instructions that access +;global variables are three bytes smaller, and small is good. (When compiling SNESAPU.DLL, this +;also reduces the size of the .reloc section.) + +%define _Var(v) RAM - (GVar.sizeof - GVar. %+ v) ;Get global variable relative to EDI +%define _VarR(r,v) r - (GVar.sizeof - GVar. %+ v) ;Get global relative to any register +%define _PSW(f) PSWFlags. %+ f + RAM - (GVar.sizeof - GVar.regPSW) ;Get byte flag rel to EDI + +%if PROFILE +%define _Profile(t) Inc dword [EDX*4 + profile + Profile. %+ t] ;Increase profile counter +%define _ProfStart Call StartAPUProfile,Profile.spcTSC +%define _ProfEnd Call EndAPUProfile,Profile.spcTSC +%else +%define _Profile(t) +%define _ProfStart +%define _ProfEnd +%endif + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Convert Flags +; Convert flags between internal 8-byte and external 8-bit format + +;Unpacks the bit flags in CL into bytes and stores them in regPSW +; +;In: CL = PSW +; +;Out: regPSW = Flags +; BX indexes direct page selected by P +; EDX -> regPSW +; CL = Reflects state N and Z + +%macro _UnpackPSW 0 + LEA EDX,[7+_Var(regPSW)] + Mov BL,8 + %%Next: + RoL CL,1 + SetC [EDX] + Dec EDX + Dec BL + JNZ %%Next + Mov BH,[_PSW(P)] + And CL,82h + XOr CL,2 +%endmacro + +;Pack the byte flags in regPSW into bits and store them in CL +; +;In: regPSW +; +;Out: CL = Flags +; EDX -> regPSW + +%macro _PackPSW 0 + Test CL,CL + SetS [_PSW(N)] ;Set N and Z byte flags + SetZ [_PSW(Z)] + LEA EDX,[7+_Var(regPSW)] + Mov CL,2 + %%Next: + Or CL,[EDX] + Dec EDX + Add CL,CL + JNC %%Next + Or CL,[EDX] +%endmacro + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Structures + +;The flags are split up into eight bytes. During emulation, N and Z are not set. Instead, CL +;contains the result of the last instruction, and N and Z can be determined from it. Between calls +;to EmuSPC, though, N and Z will be set accordingly. All flags will contain a value of 1 or 0. + +STRUC PSWFlags + .C resb 1 ;Carry + .Z resb 1 ;Zero + .I resb 1 ;Interrupts Enabled (unused in the SNES) + .H resb 1 ;Half-Carry + .B resb 1 ;Software Break + .P resb 1 ;Direct Page selector + .V resb 1 ;Integer Overflow + .N resb 1 ;Negative +ENDSTRUC + +;APU RAM mapping + +STRUC MemMap + dp0 resb 0F0h ;Direct Page 0 + testReg resb 1 ; Test register + control resb 1 ; Control register + dspAddr resb 1 ; DSP Address + dspData resb 1 ; DSP Data + port0 resb 1 ; Port 0 + port1 resb 1 ; Port 1 + port2 resb 1 ; Port 2 + port3 resb 1 ; Port 3 + resb 1 ; unused + resb 1 ; unused + t0 resb 1 ; Timer 0 + t1 resb 1 ; Timer 1 + t2 resb 1 ; Timer 2 + c0 resb 1 ; Counter 0 + c1 resb 1 ; Counter 1 + c2 resb 1 ; Counter 2 + dp1 resb 100h ;Direct Page 1 + resb 0FD00h ;General Pages 2-254 + up resb 0C0h ;Uppermost Page 255 + ipl resb 040h ; Program used to transfer memory +ENDSTRUC + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Data + +SECTION .rodata ALIGN=32 + + ;Offsets of each opcode handler + opcOfs DW Opc00-Opc,Opc01-Opc,Opc02-Opc,Opc03-Opc,Opc04-Opc,Opc05-Opc,Opc06-Opc,Opc07-Opc + DW Opc08-Opc,Opc09-Opc,Opc0A-Opc,Opc0B-Opc,Opc0C-Opc,Opc0D-Opc,Opc0E-Opc,Opc0F-Opc + DW Opc10-Opc,Opc11-Opc,Opc12-Opc,Opc13-Opc,Opc14-Opc,Opc15-Opc,Opc16-Opc,Opc17-Opc + DW Opc18-Opc,Opc19-Opc,Opc1A-Opc,Opc1B-Opc,Opc1C-Opc,Opc1D-Opc,Opc1E-Opc,Opc1F-Opc + DW Opc20-Opc,Opc21-Opc,Opc22-Opc,Opc23-Opc,Opc24-Opc,Opc25-Opc,Opc26-Opc,Opc27-Opc + DW Opc28-Opc,Opc29-Opc,Opc2A-Opc,Opc2B-Opc,Opc2C-Opc,Opc2D-Opc,Opc2E-Opc,Opc2F-Opc + DW Opc30-Opc,Opc31-Opc,Opc32-Opc,Opc33-Opc,Opc34-Opc,Opc35-Opc,Opc36-Opc,Opc37-Opc + DW Opc38-Opc,Opc39-Opc,Opc3A-Opc,Opc3B-Opc,Opc3C-Opc,Opc3D-Opc,Opc3E-Opc,Opc3F-Opc + DW Opc40-Opc,Opc41-Opc,Opc42-Opc,Opc43-Opc,Opc44-Opc,Opc45-Opc,Opc46-Opc,Opc47-Opc + DW Opc48-Opc,Opc49-Opc,Opc4A-Opc,Opc4B-Opc,Opc4C-Opc,Opc4D-Opc,Opc4E-Opc,Opc4F-Opc + DW Opc50-Opc,Opc51-Opc,Opc52-Opc,Opc53-Opc,Opc54-Opc,Opc55-Opc,Opc56-Opc,Opc57-Opc + DW Opc58-Opc,Opc59-Opc,Opc5A-Opc,Opc5B-Opc,Opc5C-Opc,Opc5D-Opc,Opc5E-Opc,Opc5F-Opc + DW Opc60-Opc,Opc61-Opc,Opc62-Opc,Opc63-Opc,Opc64-Opc,Opc65-Opc,Opc66-Opc,Opc67-Opc + DW Opc68-Opc,Opc69-Opc,Opc6A-Opc,Opc6B-Opc,Opc6C-Opc,Opc6D-Opc,Opc6E-Opc,Opc6F-Opc + DW Opc70-Opc,Opc71-Opc,Opc72-Opc,Opc73-Opc,Opc74-Opc,Opc75-Opc,Opc76-Opc,Opc77-Opc + DW Opc78-Opc,Opc79-Opc,Opc7A-Opc,Opc7B-Opc,Opc7C-Opc,Opc7D-Opc,Opc7E-Opc,Opc7F-Opc + DW Opc80-Opc,Opc81-Opc,Opc82-Opc,Opc83-Opc,Opc84-Opc,Opc85-Opc,Opc86-Opc,Opc87-Opc + DW Opc88-Opc,Opc89-Opc,Opc8A-Opc,Opc8B-Opc,Opc8C-Opc,Opc8D-Opc,Opc8E-Opc,Opc8F-Opc + DW Opc90-Opc,Opc91-Opc,Opc92-Opc,Opc93-Opc,Opc94-Opc,Opc95-Opc,Opc96-Opc,Opc97-Opc + DW Opc98-Opc,Opc99-Opc,Opc9A-Opc,Opc9B-Opc,Opc9C-Opc,Opc9D-Opc,Opc9E-Opc,Opc9F-Opc + DW OpcA0-Opc,OpcA1-Opc,OpcA2-Opc,OpcA3-Opc,OpcA4-Opc,OpcA5-Opc,OpcA6-Opc,OpcA7-Opc + DW OpcA8-Opc,OpcA9-Opc,OpcAA-Opc,OpcAB-Opc,OpcAC-Opc,OpcAD-Opc,OpcAE-Opc,OpcAF-Opc + DW OpcB0-Opc,OpcB1-Opc,OpcB2-Opc,OpcB3-Opc,OpcB4-Opc,OpcB5-Opc,OpcB6-Opc,OpcB7-Opc + DW OpcB8-Opc,OpcB9-Opc,OpcBA-Opc,OpcBB-Opc,OpcBC-Opc,OpcBD-Opc,OpcBE-Opc,OpcBF-Opc + DW OpcC0-Opc,OpcC1-Opc,OpcC2-Opc,OpcC3-Opc,OpcC4-Opc,OpcC5-Opc,OpcC6-Opc,OpcC7-Opc + DW OpcC8-Opc,OpcC9-Opc,OpcCA-Opc,OpcCB-Opc,OpcCC-Opc,OpcCD-Opc,OpcCE-Opc,OpcCF-Opc + DW OpcD0-Opc,OpcD1-Opc,OpcD2-Opc,OpcD3-Opc,OpcD4-Opc,OpcD5-Opc,OpcD6-Opc,OpcD7-Opc + DW OpcD8-Opc,OpcD9-Opc,OpcDA-Opc,OpcDB-Opc,OpcDC-Opc,OpcDD-Opc,OpcDE-Opc,OpcDF-Opc + DW OpcE0-Opc,OpcE1-Opc,OpcE2-Opc,OpcE3-Opc,OpcE4-Opc,OpcE5-Opc,OpcE6-Opc,OpcE7-Opc + DW OpcE8-Opc,OpcE9-Opc,OpcEA-Opc,OpcEB-Opc,OpcEC-Opc,OpcED-Opc,OpcEE-Opc,OpcEF-Opc + DW OpcF0-Opc,OpcF1-Opc,OpcF2-Opc,OpcF3-Opc,OpcF4-Opc,OpcF5-Opc,OpcF6-Opc,OpcF7-Opc + DW OpcF8-Opc,OpcF9-Opc,OpcFA-Opc,OpcFB-Opc,OpcFC-Opc,OpcFD-Opc,OpcFE-Opc,OpcFF-Opc + + ;Pointers to function register handlers + funcRTab DD Func0r, Func1r, Func2r, Func3r, Func4r, Func5r, Func6r, Func7r + DD Func8r, Func9r, FuncAr, FuncBr, FuncCr, FuncDr, FuncEr, FuncFr + + funcWTab DD Func0w, Func1w, Func2w, Func3w, Func4w, Func5w, Func6w, Func7w, + DD Func8w, Func9w, FuncAw, FuncBw, FuncCw, FuncDw, FuncEw, FuncFw + + ;The 64 bytes of ROM in the IPL region + iplROM DB 0CDh,0EFh ;FFC0 Mov X,#EF + DB 0BDh ;FFC2 Mov SP,X ;SP = 01EFh + DB 0E8h,000h ;FFC3 Mov A,#00h + DB 0C6h ;FFC5 Mov (X),A ;Set all bytes in DP0 to 0 + DB 01Dh ;FFC6 Dec X + DB 0D0h,0FCh ;FFC7 BNE FFC5 + DB 08Fh,0AAh,0F4h ;FFC9 Mov 0F4h,#0AAh ;Notify 65816 that we're + DB 08Fh,0BBh,0F5h ;FFCC Mov 0F5h,#0BBh ; ready for transfer + DB 078h,0CCh,0F4h ;FFCF Cmp 0F4h,#0CCh + DB 0D0h,0FBh ;FFD2 BNE FFCF ;Wait for 65816 to respond + DB 02Fh,019h ;FFD4 Bra FFEF + DB 0EBh,0F4h ;FFD6 Mov Y,0F4h + DB 0D0h,0FCh ;FFD8 BNE FFD6 ;Wait for port0 == 0 + DB 07Eh,0F4h ;FFDA Cmp Y,0F4h + DB 0D0h,00Bh ;FFDC BNE FFE9 ;Wait for 65816 to send byte + DB 0E4h,0F5h ;FFDE Mov A,0F5h ;Get byte + DB 0CBh,0F4h ;FFE0 Mov 0F4h,Y ;Ack that we received byte + DB 0D7h,000h ;FFE2 Mov [00h]+Y,A ;Store byte in RAM + DB 0FCh ;FFE4 Inc Y ;Move to next byte + DB 0D0h,0F3h ;FFE5 BNE FFDA + DB 0ABh,001h ;FFE7 Inc 01h ;Move to next page + DB 010h,0EFh ;FFE9 BPl FFDA + DB 07Eh,0F4h ;FFEB Cmp Y,0F4h ;If (port0 - Y) > 1, get + DB 010h,0EBh ;FFED BPl FFDA ; next address + DB 0BAh,0F6h ;FFEF MovW YA,0F6h ;Get destination address + DB 0DAh,000h ;FFF1 MovW 00h,YA + DB 0BAh,0F4h ;FFF3 MovW YA,0F4h + DB 0C4h,0F4h ;FFF5 Mov 0F4h,A + DB 0DDh ;FFF7 Mov A,Y + DB 05Dh ;FFF8 Mov X,A + DB 0D0h,0DBh ;FFF9 BNE FFD6 ;If X == 0, begin execution + DB 01Fh,000h,000h ;FFFB Jmp [!0000h+X] + DB 0C0h,0FFh ;FFFE (Reset vector) + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Variables + +SECTION .bss ALIGN=64 + + opcTab resd 256 ;Pointers to each opcode handler + pAPURAM resd 1 ;Pointer to 64K SPC RAM + +STRUC GVar + .extraRAM resb 64 ;80 ;Memory used for writes when ROM reading is enabled + + .regYA resw 1 ;C0 + .regPC resw 1 ;C2 + .regSP resd 1 ;C4 ;Physical address of current Stack Pointer + .regPSW resb 8 ;C8 ;Flags in byte form + .regX resb 1 ;D0 ;Storage for registers between calls + + .dbgOpt resb 1 ;D1 ;Debugging options + resb 1 + + .t8kHz resb 1 ;D3 ;64kHz cycles left until 8kHz pulse + .t64kHz resd 1 ;D4 ;APU cycles left until 64kHz pulse + .t64Cnt resd 1 ;D8 ;64kHz counter (increased every 64kHz pulse) + .t64Last resd 1 ;DC ;Value of 't64Cnt' at the last counter read + .clkTotal resd 1 ;E0 ;Total number of clock cycles left to execute + .clkExec resd 1 ;E4 ;Number of cycles needed for 64kHz timer to increase + .clkLeft resd 1 ;E8 ;Number of cycles left to execute until timer increase + + .pDebug resd 1 ;EC ;Pointer to tracing routine + + .inPortCp resb 4 ;F0 ;In port copies [F4-F7h] + .outPort resb 4 ;F4 ;Out port values [2140-43h] + .oldCtrl resb 1 ;F8 ;Copy of [F1h] + resb 1 + .timer + .timer0 resb 1 ;FA ;Timer value written to [FAh] + .timer1 resb 1 ;FB ; [FBh] + .timer2 resb 1 ;FC ; [FCh] + .tStep + .t0Step resb 1 ;FD ;Timer pulses left until next counter increase, + .t1Step resb 1 ;FE ; aka up counters (even though I count down with them) + .t2Step resb 1 ;FF ; Copied from [FA-FCh] + .sizeof +ENDSTRUC + + resb GVar.sizeof + apuRAM resb 20000h + +%if PROFILE + profile resb Profile.sizeof +%else + profile +%endif + + + +;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß +; Code + +SECTION .text ALIGN=16 + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Initialize SPC700 + +PROC InitSPC +USES ECX,ESI,EDI + + Mov EDI,apuRAM + Add EDI,0FFFFh + Mov DI,0 + Mov [pAPURAM],EDI + + LEA EAX,[EDI+1FFh] + Mov [_Var(regSP)],EAX + + Mov AX,0F0h + Mov byte [1+EAX],80h ;IPL ROM reading enabled + Mov dword [0Ch+EAX],0F0F0F00h ;Counters set to 0Fh + Mov byte [_Var(timer0)],0FFh ;Timers set to 00h (ResetSPC will set the registers) + Mov byte [_Var(timer1)],0FFh + Mov byte [_Var(timer2)],0FFh + + Call SetSPCDbg,0,0 ;Disable debugging + + ;Generate table of opcode pointers ------- + ;The locations of the opcodes are stored as 16-bit offsets which are then converted to 32-bit + ;pointers by this code. The only reason this is done is to make the compiled executable smaller. + + Mov ESI,opcOfs + Mov EDI,opcTab + XOr ECX,ECX + .Next: + MovZX EAX,word [ECX*2+ESI] + Add EAX,Opc + Mov [ECX*4+EDI],EAX + Inc CL + JNZ short .Next + +%if PROFILE + XOr EAX,EAX + Mov EDI,profile + Mov ECX,Profile.sizeof/4 + Rep StoSD +%endif + + Mov EAX,[pAPURAM] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Reset SPC700 + +PROC ResetSPC +USES ECX,EDX,ESI,EDI + + ;Erase 64K SPC RAM ----------------------- + ;Technically the RAM doesn't get erased on reset, but I do this for debugging purposes. + Mov EDI,[pAPURAM] + Or EAX,-1 ;Fill RAM with STOPs + Mov ECX,0F0h / 4 + Rep StoSD + Add EDI,10h + Mov ECX,0FF00h / 4 + Rep StoSD + + ;Reset Function Registers ---------------- + ;<Verified by Blargg on 04.10.03> + Mov EDI,[pAPURAM] + LEA ESI,[EDI+0F0h] + XOr EAX,EAX + Mov [0+ESI],AL ;Test gets set to 00h + And byte [1+ESI],07h ;Timer status is preserved, other bits are reset + Or byte [1+ESI],80h + Mov [4+ESI],EAX ;Reset in-ports + Mov [_Var(outPort)],EAX ;Reset out-ports + Mov word [8+ESI],-1 ;See above comment on erasing RAM + Mov AL,[_Var(timer0)] ;Copy timers to RAM for FixSPCLoad + Inc AL + Mov [0Ah+ESI],AL + Mov AL,[_Var(timer1)] + Inc AL + Mov [0Bh+ESI],AL + Mov AL,[_Var(timer2)] + Inc AL + Mov [0Ch+ESI],AL + + ;Copy IPL ROM ---------------------------- + Mov ESI,iplROM ;Copy to RAM + Mov DI,ipl + Mov CL,10h + Rep MovSD + + Mov ESI,iplROM ;Copy to extra RAM + Mov EDI,[pAPURAM] + LEA EDI,[_Var(extraRAM)] + Mov CL,10h + Rep MovSD + + ;Reset internal variables ---------------- + Mov EDI,[pAPURAM] + Mov byte [_Var(t8kHz)],7 ;Reset timer clocks + Mov dword [_Var(t64kHz)],T64_CYC-CPU_CYC + Mov dword [_Var(t64Cnt)],0 + Mov dword [_Var(t64Last)],0 + + Call FixSPCLoad,0FFC0h,0,0,0,0,0 + + And byte [_Var(dbgOpt)],~SPC_HALT ;Reset the halt flag + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Debug SPC700 + +PROC SetSPCDbg, pTrace, opts +USES EDX,EDI + + Mov EDI,[pAPURAM] + + Mov AL,[%$opts] + Cmp AL,-1 + JE short .NoOpts ;Leave options as they are + And AL,SPC_HALT|SPC_STOP + Mov [_Var(dbgOpt)],AL ;Save options + .NoOpts: + + Mov EDX,[%$pTrace] + Mov EAX,[_Var(pDebug)] + Cmp EDX,-1 + JE short .NoFunc + Mov [_Var(pDebug)],EDX + Jmp short .Func + .NoFunc: + Mov EDX,EAX + .Func: + +%if DEBUG + ;Should tracing be enabled? -------------- + And byte [_Var(dbgOpt)],~SPC_TRACE ;Disable instruction tracing by default + + Test EDX,EDX ;Make sure function pointer isn't null... + SetZ DL + Test byte [_Var(dbgOpt)],SPC_STOP ;...and tracing option isn't disabled + SetNZ DH + Or DL,DH + Dec DL + And DL,SPC_TRACE + Or [_Var(dbgOpt)],DL +%endif + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Fix SPC700 After Load + +PROC FixSPCLoad, pc, a, y, x, psw, sp +USES ALL + + Mov EDI,[pAPURAM] + + ;Load registers -------------------------- + XOr EAX,EAX + Mov AX,[%$pc] + Mov [_Var(regPC)],AX + Mov AL,[%$a] + Mov AH,[%$y] + Mov [_Var(regYA)],AX + Mov AL,[%$x] + Mov [_Var(regX)],AL + Mov AL,[%$sp] + Mov [_Var(regSP)],AL + + Mov CL,[%$psw] + _UnpackPSW + + XOr EAX,EAX + Mov [RAM+testReg],AL + + Mov AL,[RAM+t0] ;Initialize timer counters + Mov [RAM+t0],AH + Dec AL + Mov [_Var(timer0)],AL + Mov [_Var(t0Step)],AL + + Mov AL,[RAM+t1] + Mov [RAM+t1],AH + Dec AL + Mov [_Var(timer1)],AL + Mov [_Var(t1Step)],AL + + Mov AL,[RAM+t2] + Mov [RAM+t2],AH + Dec AL + Mov [_Var(timer2)],AL + Mov [_Var(t2Step)],AL + + Mov EAX,[RAM+port0] ;Copy port values to InP + Mov [_Var(inPortCp)],EAX + + Mov AL,[RAM+control] ;Copy control register for comparisons + And AL,87h + Mov [_Var(oldCtrl)],AL + Mov [RAM+control],AL + + ShL EAX,29 ;Set disabled counters to 0 and clear upper nibble of + SAR EAX,15 ; enabled counters + SAR AX,7 + SAR AL,7 + ShL EAX,8 + And EAX,0F0F0F00h + And [RAM+t2],EAX + + ;Copy the correct extra RAM -------------- + Test byte [_Var(oldCtrl)],80h + Retc NZ + + LEA ESI,[RAM+ipl] + LEA EDI,[_Var(extraRAM)] + Mov ECX,10h + Rep MovSD + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Save SPC700's Current State of Operation + +PROC SaveSPC, pState +USES ALL + + Mov EBX,[%$pState] + + ;RAM ------------------------------------- + Mov EDI,[EBX+SPCState.pRAM] + Test EDI,EDI + JZ .NoRAM + Mov ESI,[pAPURAM] + + Test byte [ESI+control],80h + JNZ short .ROM + Mov ECX,10000h/4 ;If IPL writing is enabled, copy all 64k of RAM + Rep MovSD + Jmp short .NoXRAM + + .ROM: + Mov ECX,0FFC0h/4 + Rep MovSD + + Mov ESI,[pAPURAM] ;Copy extra RAM from saved buffer + LEA ESI,[_VarR(ESI,extraRAM)] + Or ECX,40h/4 + Rep MovSD + + .NoXRAM: + Mov EDI,[pAPURAM] + Mov ESI,[EBX+SPCState.pRAM] ;Copy timer values + Add ESI,0F0h + Mov AL,[_Var(timer0)] + Inc AL + Mov [0Ah+ESI],AL + Mov AL,[_Var(timer1)] + Inc AL + Mov [0Bh+ESI],AL + Mov AL,[_Var(timer2)] + Inc AL + Mov [0Ch+ESI],AL + .NoRAM: + + Mov EDI,[pAPURAM] + + ;Registers ------------------------------- + Mov AX,[_Var(regPC)] + Mov [EBX+SPCState.pc],AX + + Mov AX,[_Var(regYA)] + Mov [EBX+SPCState.a],AX + + Mov AL,[_Var(regX)] + Mov [EBX+SPCState.x],AL + + Mov AX,[_Var(regSP)] + Mov [EBX+SPCState.sp],AX + + LEA ECX,[_Var(regPSW)] + Mov AL,80h + .Flag: + Mov AH,[ECX] + Inc ECX + ShR EAX,1 + JNC short .Flag + Mov [EBX+SPCState.psw],AL + + ;Ports ----------------------------------- + Mov EAX,[_Var(outPort)] + Mov [EBX+SPCState.outP],EAX + + ;Timers ---------------------------------- + Mov EAX,[_Var(tStep)] + Mov [EBX+SPCState.upCnt],EAX + + Mov AL,[_Var(t8kHz)] + Mov [EBX+SPCState.t8kHz],AL + + Mov EAX,[_Var(t64kHz)] + Mov [EBX+SPCState.t64kHz],EAX + + Mov EAX,[_Var(t64Cnt)] + Mov [EBX+SPCState.t64Cnt],EAX + + ;Clear reserved bytes -------------------- + XOr EAX,EAX + Mov [EBX+SPCState._r1],EAX + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Restore SPC700's State of Operation + +PROC RestoreSPC, pState +USES ALL + + Mov EBX,[%$pState] + XOr EAX,EAX + Mov EDX,[pAPURAM] + + ;RAM ------------------------------------- + Mov ESI,[EBX+SPCState.pRAM] + Test ESI,ESI + JZ short .NoRAM + Mov EDI,EDX + Mov ECX,0FFC0h/4 + Rep MovSD + + LEA EDI,[_VarR(EDX,extraRAM)] + Or ECX,40h/4 + Rep MovSD + + Mov EDI,EDX + + Mov AL,[EDI+control] + And AL,87h + Mov [_Var(oldCtrl)],AL + + Mov EAX,[EDI+port0] + Mov [_Var(inPortCp)],EAX + + Mov ESI,iplROM + Test AL,80h + JNZ short .ROM + LEA ESI,[_Var(extraRAM)] + .ROM: + + Mov DI,ipl + Or ECX,40h/4 + Rep MovSD + + Mov EDI,EDX + LEA ESI,[EDX+0F0h] + Mov AL,[0Ah+ESI] + Mov [0Ah+ESI],CL + Dec AL + Mov [_Var(timer0)],AL + Mov AL,[0Bh+ESI] + Mov [0Bh+ESI],CL + Dec AL + Mov [_Var(timer1)],AL + Mov AL,[0Ch+ESI] + Mov [0Ch+ESI],CL + Dec AL + Mov [_Var(timer2)],AL + .NoRAM: + + Mov EDI,EDX + + ;Registers ------------------------------- + Mov AX,[EBX+SPCState.pc] + Mov [_Var(regPC)],AX + + Mov AX,[EBX+SPCState.a] + Mov [_Var(regYA)],AX + + Mov AL,[EBX+SPCState.x] + Mov [_Var(regX)],AL + + Mov AL,[EBX+SPCState.sp] + Mov [_Var(regSP)],AL + + Mov AL,[EBX+SPCState.psw] + LEA ECX,[_Var(regPSW)] + Mov EDX,7 + .Flag: + ShL AL,1 + SetC [EDX+ECX] + Dec EDX + JNS short .Flag + + ;Ports ----------------------------------- + Mov EAX,[EBX+SPCState.outP] + Mov [_Var(outPort)],EAX + + ;Timers ---------------------------------- + Mov EAX,[EBX+SPCState.upCnt] + Mov [_Var(t0Step)],AX + ShR EAX,16 + Mov [_Var(t2Step)],AL + + Mov AL,[EBX+SPCState.t8kHz] + Mov [_Var(t8kHz)],AL + + Mov EAX,[EBX+SPCState.t64kHz] + Mov [_Var(t64kHz)],EAX + + Mov EAX,[EBX+SPCState.t64Cnt] + Mov [_Var(t64Cnt)],EAX + And dword [_Var(t64Last)],0 + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Write to APU RAM from the SPC700 + +PROC SetAPURAM, addr, val +USES EDX + + ;Write value to memory ------------------- + Mov EDX,[pAPURAM] + Mov DX,[%$addr] + Mov AL,[%$val] + Mov [EDX],AL + + ;Check for special addresses ------------- + Cmp DX,ipl ;IPL ROM region + JAE short .WROM + + Dec DH ;Function registers + Cmp DX,-16 + Retc B + + ;Handle function register ---------------- + Push EBX,EDI,EBP + And EDX,0Fh + Mov EDI,[pAPURAM] + Mov EBP,.Return + Jmp [EDX*4+funcWTab] + .Return: + Pop EBP,EDI,EBX + RetS + + ;Handle IPL ROM -------------------------- + .WROM: +%if IPLW + Mov EAX,EDX + Mov AX,0F1h + Test byte [EAX],80h + Retc Z +%endif + + Push ECX,EDI + MovZX ECX,DX + Mov EDI,[pAPURAM] +%if IPLW + Mov AL,[EDX] + Mov [ECX+_Var(extraRAM)-ipl],AL + Mov AL,[ECX+iplROM-ipl] +%else + Mov AL,[ECX+_Var(extraRAM)-ipl] +%endif + Mov [EDX],AL + Pop EDI,ECX + RetS + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Write to APU Port + +PROC SetAPUPort, port, val +USES ECX,EDI + + Mov EDI,[pAPURAM] + Mov ECX,[%$port] + Mov AL,[%$val] + + ;Write value to port --------------------- + And ECX,3 + Mov [ECX+_Var(inPortCp)],AL + Mov [ECX+EDI+0F4h],AL + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Read from APU Port + +PROC GetAPUPort, port + + Mov EAX,[pAPURAM] + Mov AL,[%$port] + And AL,3 + Mov AL,[_VarR(EAX,outPort)] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get SPC700 Time + +PROC GetSPCTime + + Mov EAX,[pAPURAM] + Mov EAX,[_VarR(EAX,t64Cnt)] + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Profile Timers + +PROC StartAPUProfile, tsc + +%if PROFILE + Push EAX,ECX + Mov ECX,profile + Add ECX,[%$tsc] + DB 0Fh,31h ;RdTSC - Read Time-Stamp Counter + Mov [8+ECX],EAX + Mov [12+ECX],EDX + Pop ECX,EAX +%endif + +ENDP + + +PROC EndAPUProfile, tsc + +%if PROFILE + Push EAX,ECX + Mov ECX,profile + Add ECX,[%$tsc] + DB 0Fh,31h + Sub EAX,[8+ECX] + SbB EDX,[12+ECX] + Add [ECX],EAX + AdC [4+ECX],EDX + Pop ECX,EAX +%endif + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate SPC700 + +PROC EmuSPC, cyc +USES ALL + + _ProfStart + + Mov EDI,[pAPURAM] + XOr EAX,EAX ;Return 0, if SPC700 is halted + Mov EDX,SPCFetch ;Default to normal instruction fetching + + Test byte [_Var(dbgOpt)],SPC_HALT|SPC_TRACE +%if DEBUG + JZ short .NoHalt + Test byte [_Var(dbgOpt)],SPC_TRACE +; Jmp SPCDone + Mov EDX,SPCTrace ;Enable instruction tracing + .NoHalt: +%else + Retc NZ +%endif + + Mov EAX,[%$cyc] ;EAX = clock cycles to execute + Push EBP + Mov EBP,EDX ;EBP = Location to jump to after handling an opcode + + ;Setup clock cycle execution ------------- + ;clkLeft contains the number of clock cycles to emulate until the next 64kHz clock pulse or it's time to quit + + XOr EDX,EDX + Mov [_Var(clkTotal)],EAX + + Sub EAX,[_Var(t64kHz)] ;if (cyc > t64kHz) clkExec = t64kHz + SetA DL ;else clkExec = cyc + Dec EDX + And EAX,EDX + Add EAX,[_Var(t64kHz)] + + Mov [_Var(clkExec)],EAX ;Save the number we want to execute this lap + Mov [_Var(clkLeft)],EAX + + ;Load x86 registers ---------------------- + XOr ECX,ECX + Mov ESI,EDI + Mov EBX,EDI + Mov SI,[_Var(regPC)] + MovZX EAX,word [_Var(regYA)] + Mov CH,[_Var(regX)] + Mov BH,[_PSW(P)] + Mov CL,[_PSW(N)] + RoR CL,1 + Or CL,1 + XOr CL,[_PSW(Z)] + +%if DEBUG + Jmp EBP ;Jump into emulation routine +%else + Jmp SPCFetch +%endif + +SPCHalt: + XOr EDX,EDX + Mov [_Var(clkTotal)],EDX + +ALIGN 16 +SPCExit: + Test CL,CL + SetS [_PSW(N)] + SetZ [_PSW(Z)] + Mov [_Var(regPC)],SI ;Save emulated registers + Mov [_Var(regYA)],AX + Mov [_Var(regX)],CH + Pop EBP + +%if PROFILE + _ProfEnd +%endif + + Mov EAX,[_Var(clkTotal)] ;Return clock cycles left to emulate + +SPCDone: + +ENDP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Opcode Fetcher +; +;Fetches the next opcode and jumps to the appropriate handler. Keeps track of clock cycles emulated +;and timers. + + ;Fetching begins by first checking if more cycles need to be emulated before updating timer 2. + ;If so, then get the next opcode and emulate it. + +ALIGN 16 +SPCFetch: + Cmp dword [_Var(clkLeft)],0 ;Have we executed all clock cycles? + JL short SPCTimers ; Yes, Update timers + + MovZX EDX,byte [ESI] ;Fetch instruction opcode + _Profile(exec) + Inc PC ;Advance PC to first operand or next instruction + Jmp [EDX*4+opcTab] ;Jump to handler + + ;clkExec contains the number of clock cycles we wanted to emulate this round. By subtracting + ;clkLeft (which will be a number < 0) we get the actual number of cycles emulated, which will + ;be subtracted from the total number to be emulated (clkTotal) and used to update the timers. + +SPCTimers: + Mov EDX,[_Var(clkExec)] ;EDX = Actual number of clock cycles emulated + Sub EDX,[_Var(clkLeft)] + + ;Update timers --------------------------- + Sub [_Var(t64kHz)],EDX ;Subtract cycles from timer clock. Has it rolled over? + JNS short .NoTInc ; No, Don't update timers + Mov BL,[_Var(oldCtrl)] ;BL = Control register + + Add dword [_Var(t64kHz)],T64_CYC ;Restore timer 2 clock + Inc dword [_Var(t64Cnt)] ;Increase 64kHz counter + + ShL BL,6 ;Copy timer enable bit into CF + SbB byte [_Var(t2Step)],0 ;Decrease timer step if timer is enabled + JNC short .NoC2Inc ;If carry, counter needs to be increased + Mov BH,[_Var(timer2)] ;Restore the number of steps until counter increase + Inc byte [RAM+c2] ;Increase counter 2 + Mov [_Var(t2Step)],BH + And byte [RAM+c2],0Fh ;Only lower 4-bits are operable +%if CNTBK +%if DSPINTEG + Call UpdateDSP +%else + Mov EBP,SPCExit ;Signal the emu to exit so the DSP can catch up +%endif +%endif + .NoC2Inc: + + Dec byte [_Var(t8kHz)] + JNS short .NoTInc + Mov byte [_Var(t8kHz)],7 ;Reset clock pulse counter + + Add BL,BL + SbB byte [_Var(t1Step)],0 + JNC short .NoC1Inc + Mov BH,[_Var(timer1)] + Inc byte [RAM+c1] + Mov [_Var(t1Step)],BH + And byte [RAM+c1],0Fh +%if CNTBK +%if DSPINTEG + Call UpdateDSP +%else + Mov EBP,SPCExit +%endif +%endif + .NoC1Inc: + + Add BL,BL + SbB byte [_Var(t0Step)],0 + JNC short .NoC0Inc + Mov BH,[_Var(timer0)] + Inc byte [RAM+c0] + Mov [_Var(t0Step)],BH + And byte [RAM+c0],0Fh +%if CNTBK +%if DSPINTEG + Call UpdateDSP +%else + Mov EBP,SPCExit +%endif +%endif + .NoC0Inc: + .NoTInc: + + ;After the timers are updated, subtract the number of cycles emulated from the total number + ;passed in. If we've emulated all clock cycles requested by the caller, then quit. Otherwise + ;emulate until the next 64kHz clock pulse. + + Sub [_Var(clkTotal)],EDX ;Have we executed all clock cycles? + JLE SPCExit ; Yes, Quit + + Mov BX,AX ;Calculate number of cycles to emulate + Mov EAX,[_Var(t64kHz)] ;clkExec = (t64kHz < clkTotal) ? t64kHz : clkTotal + Sub EAX,[_Var(clkTotal)] + CDQ + And EAX,EDX + Add EAX,[_Var(clkTotal)] + Mov [_Var(clkLeft)],EAX + Mov [_Var(clkExec)],EAX + Mov AX,BX + + Mov BH,[_PSW(P)] ;Restore EBX + + Jmp EBP ;Return to fetcher + + ;Tracing operates like fetching, except a user specified vector is called before executing the + ;instruction. + +ALIGN 16 +SPCTrace: + Cmp dword [_Var(clkLeft)],0 ;Have we executed all clock cycles? + JL SPCTimers ; Yes, Update timers + +SPCBreak: + ;Call SPCTrace --------------------------- + Mov EBX,[_Var(timer)] + _PackPSW + MovZX EDX,CL ;EDX = PSW + ShR ECX,8 ;ECX = X + Sub EBX,[_Var(tStep)] + + Push EBX ;Pass up counters + Push dword [_Var(regSP)] ; "" SP + Push EDX ; "" PSW + Push ECX ; "" X + Push EAX ; "" YA + Push ESI ; "" PC + + Mov EAX,T64_CYC-CPU_CYC ;Pass number of cycles left until 64kHz increase + Sub EAX,[_Var(t64kHz)] + Add EAX,[_Var(clkExec)] + Sub EAX,[_Var(clkLeft)] + Mov CL,CPU_CYC + Div CL + Mov [23+ESP],AL + + Call [_Var(pDebug)] ;Call tracing routine + + ;Update registers ------------------------ + Pop EAX ;Pop PC + Mov SI,AX + Pop EAX ;Pop YA + And EAX,0FFFFh + Pop ECX ;Pop X + MovZX ECX,CL + ShL ECX,8 + Pop EDX ;Pop PSW + Mov CL,DL + _UnpackPSW + Pop EDX ;Pop SP + Mov [_Var(regSP)],DL + Pop EDX + + Test byte [_Var(dbgOpt)],SPC_HALT + JNZ SPCHalt + + Mov EBX,EDI + Mov BH,[_PSW(P)] + + MovZX EDX,byte [ESI] + Inc PC ;Move PC to first operand or next instruction + Jmp [EDX*4+opcTab] ;Jump to handler + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Macros for Instruction Emulation + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Load pointers +; Load DPI or ABSL (aliases for EBX) with the value needed by the instruction. + +;dp - Load DPI with the 8-bit immediate value +%macro _dp 0 + Mov BL,[OP] ;;BL-> Location in DP +%endmacro + +;dp - Load DPI with the 2nd 8-bit immediate value +%macro _dp2 0 + Inc PC + Mov BL,[OP] +%endmacro + +;(X) - Load DPI with the value in X +%macro _X 0 + Mov BL,X +%endmacro + +;(Y) - Load DPI with the value in Y +%macro _Y 0 + Mov BL,Y +%endmacro + +;dp+X - Load DPI with the 8-bit immediate value + X +%macro _dpX 0 + _dp + Add BL,X +%endmacro + +;dp+Y - Load DPI with the 8-bit immediate value + Y +%macro _dpY 0 + _dp + Add BL,Y +%endmacro + +;!abs - Load ABSL with the 16-bit immediate value +%macro _abs 0 + Mov DX,[OP] +%endmacro + +;!abs+X - Load ABSL with the 16-bit immediate value + X +%macro _absX 0 + Mov DL,X + Add DX,[OP] +%endmacro + +;!abs+Y - Load ABSL with the 16-bit immediate value + Y +%macro _absY 0 + Mov DL,Y + Add DX,[OP] +%endmacro + +;[dp+X] - Load ABSL with the indexed 16-bit value at [dp+X] +%macro _idpX 0 + _dpX + Mov DX,[DPI] +%endmacro + +;[dp]+Y - Load ABSL with the indexed 16-bit value at [dp] + Y +%macro _idpY 0 + _dp + Mov DL,Y + Add DX,[DPI] +%endmacro + +;mem.bit - Load CF with bit indexed by the bit.mem operand +; DX -> byte in mem 0-1FFFh +; CF = bit in byte +%macro _mbit 0 + Mov DX,[OP] + RoL DX,3 + BT [RAM],EDX +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Stack Operations + +;Pop byte off of stack +; Val - r/m to pop +%macro _Pop 1 + Inc byte [_Var(regSP)] ;;Increase SP + Mov EDX,[_Var(regSP)] ;;EDX -> Current stack position + Mov %1,[EDX] ;;Get value from stack +%endmacro + +;Pop PC off of stack +%macro _PopPC 0 + Mov EDX,[_Var(regSP)] + Mov PC,[1+EDX] + Add byte [_Var(regSP)],2 +%endmacro + +;Push byte onto stack +; Val - r/m to push +%macro _Push 1 + Mov EDX,[_Var(regSP)] + Dec byte [_Var(regSP)] ;;Decrease SP + Mov [EDX],%1 ;;Put value in stack +%endmacro + +;Push PC onto stack +%macro _PushPC 0-1 + Mov EDX,[_Var(regSP)] +%if %0 +%if %1 == 1 + LEA BX,[ESI+1] +%else + LEA BX,[ESI+2] +%endif + Mov [EDX-1],BX + Mov BH,[_PSW(P)] +%else + Mov [EDX-1],PC +%endif + Sub byte [_Var(regSP)],2 +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get flags from CPU +; The x86 CPU updates its flags with the execution of each instruction. These macros update +; regPSW with the x86 equivilants. + +;Get Carry flag +%macro _GetC 0 + SetC [_PSW(C)] +%endmacro + +;Get !Carry flag +%macro _GetCs 0 + SetNC [_PSW(C)] +%endmacro + +;Get Negative and Zero flags +%macro _GetNZ 0 +; SetS [_PSW(N)] +; SetZ [_PSW(Z)] +%endmacro + +;Get Negative, Zero, and Carry flags +%macro _GetNZC 0 + _GetC + _GetNZ +%endmacro + +;Get Negative, Zero, and !Carry flags (for compare instructions) +%macro _GetNZCs 0 + _GetCs + _GetNZ +%endmacro + +;Get Negative, Overflow, and Zero flags +%macro _GetNVZ 0 + _GetNZ + SetO [_PSW(V)] +%endmacro + +;Get Negative, Overflow, Half-Carry, Zero, and Carry flags (for addition instructions) +%macro _GetNVHZC 0 + _GetC + _GetNVZ +%if HALFC + Mov CL,AH + LAHF + Xchg CL,AH + Test CL,10h + SetNZ [_PSW(H)] + And CL,0C0h + XOr CL,40h +%endif +%endmacro + +;Get Negative, Overflow, Half-Carry, Zero, and Not Carry flags (for subtraction instructions) +%macro _GetNVHZCs 0 + _GetCs + _GetNVZ +%if HALFC + Mov CL,AH + LAHF + Xchg CL,AH + Test CL,10h + SetZ [_PSW(H)] + And CL,0C0h + XOr CL,40h +%endif +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Memory checkers +; Certain sections of memory can't be written to or read from, or trigger events when read or +; modified. This is the part that really slows emulation down, for every instruction involving +; memory multiple checks have to be performed. + +;Peform functions when specific memory locations have been written to + +%macro _WrChk 1 +%if %1 & 4 + Cmp DX,ipl ;;Was memory in the IPL ROM region modified? + JAE short %%WROM ;; Yes +%endif + + Dec DH + Cmp DX,-16 ;;Was a function register written to? +%if DEBUG + JAE short %%WReg ;; Yes, Jump to handler + Jmp EBP ;; No, Jump to next opcode + %%WReg: +%else + JB SPCFetch +%endif + And EDX,0Fh ;;EDX->Function register handler +%if %1 & 2 + Push EDX ;;Save low function register handler + Mov EBP,FuncNext +%endif + Jmp [EDX*4+funcWTab] + +%if %1 & 4 + %%WROM: +%if IPLW + Test byte [_Var(oldCtrl)],80h ;;Is ROM reading enabled? +%if DEBUG + JZ short %%WNext ;; No, write was okay +%else + JZ SPCFetch +%endif +%endif +%if IPLW + Mov BL,[EDX+RAM] ;;Copy byte written to extra RAM + Mov [EDX+_Var(extraRAM)-ipl],BL + Mov BL,[EDX+iplROM-ipl] ;;Replace byte from ROM +%else + Mov BL,[EDX+_Var(extraRAM)-ipl] +%endif + Mov [EDX+RAM],BL + %%WNext: + Jmp EBP +%endif +%endmacro + +;Perform functions when specific memory locations have been read + +%macro _RdChk 1 + Dec DH + +%if PROFILE + Cmp DX,-16 ;;Check if registers F0 - FF were read +%elif DSPBK + Cmp DX,-13 ;;Check if registers F3 - FF were read +%else + Cmp DX,-3 ;;Check if registers FD - FF were read +%endif + +%if %1 & 20h ;;If a write check happens afterwards, return here + JB short %%NotF + And EDX,0Fh + Push EBP + Mov EBP,%%Return + Jmp [EDX*4+funcRTab] + %%Return: + Pop EBP + %%NotF: +%elif DEBUG + JAE short %%ReadF + Jmp EBP + %%ReadF: + And EDX,0Fh + Jmp [EDX*4+funcRTab] +%else + JB SPCFetch + And EDX,0Fh + Jmp [EDX*4+funcRTab] +%endif +%endmacro + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Clean up after executing an instruction +; Updates flags to reflect instruction, checks memory read/writes, and decreases the clock counter +; the number of cycles it takes for the instruction to execute on the SPC700. If the opcode has +; any operands, PC is moved past them. + +; %1 = Number of clock cycles instruction takes to execute +; %2 = Number of bytes in expression +; %3 = Check memory +; 01h - EDX/EBX is used for address +; 02h - 16/8-bit operation +; 04h - check for access to IPL region +; 10h - perform read checking +; 20h - perform write checking +; %4 = Flags to be updated +; NZ, NZC(s), NVZ, NVZC(s) + +%macro _CleanUp 2-*.nolist + %if %0 >= 4 ;;Is flag paramater not blank? + _Get%4 ;; Yes, Get modified flags + %endif + + Sub dword [_Var(clkLeft)],%1*CPU_CYC ;Subtract cycles instruction takes to execute + + %if %2 == 2 + Inc PC + %elif %2 == 3 + Add PC,2 + %endif + + %if %0 < 3 ;;Has a memory check been specified? +%if DEBUG + Jmp EBP ;; No memory check, Grab next opcode +%else + Jmp SPCFetch +%endif + %else + %ifidn %3,na ;; False alarm, no mem check needs to be performed +%if DEBUG + Jmp EBP +%else + Jmp SPCFetch +%endif + %else + %if %3 & 10h ;;Does a read check need to be performed? + %if (%3 & 21h) == 0 ;;If BX contains the address, copy it to DX + Mov EDX,EBX + %endif + %if %3 & 22h ;;If input is RW or RD16, expand macro in-line + _RdChk %3 + %else + Jmp ReadDP + %endif + %endif + + %if %3 & 20h ;;Does a write check need to be performed? + %if (%3 & 1) == 0 ;;If BX contains the address, copy it to DX + Mov EDX,EBX + %endif + %if %3 & 2 ;;If input is WD16, expand macro in-line + _WrChk %3 + %elif %3 & 4 + Jmp WriteAbs + %else + Jmp WriteDP + %endif + %endif + %endif + %endif +%endmacro + +ALIGN 16 +ReadDP: + _RdChk RD + +ALIGN 16 +WriteDP: + _WrChk WD + +ALIGN 16 +WriteAbs: + _WrChk WA + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Opcode Handlers +; +;Ins - Desc +; Flags +;Form +; +;Ins: Mnemonic for instruction +; +;Desc: Long name for instruction +; +;Flags: Results of flag after execution +; ? - Unknown +; * - Reflects result of instruction +; 0 - Clear +; 1 - Set +; +;Form: Format implemented +; +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Upon entrance to an opcode handler, the registers will be: +; AL = SPC.A +; AH = SPC.Y +; CH = SPC.X +; CL = Result of previous instruction (Used to check for N and Z) +; EDX = Current opcode +; EBX-> Current direct page (BL is undefined) +; SI = SPC.PC+1 +; ESI-> First byte after opcode +; EDI-> Base of APU RAM +; EBP-> SPCFetch (or SPCTrace if debugging is enabled) +; +; CL, EDX, and BL may be freely modified during execution. Upon exit: +; - CL must contain the result of the operation, unless the instruction doesn't modify N and Z, +; in which case it must remain unmodified. +; - If the instruction accesses an absolute memory location, DX must index that location. +; - If the instruction accesses a direct page location, BX must index that location. +; - If the instruction accesses DP for its source and destination operands, DX must index the +; source location and BX the destination +; +; All other registers must remain unchanged, or reflect the results of the operation. +; +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Opcodes: +; +; 00 NOP 10 BPL 20 CLRP 30 BMI 40 SETP 50 BVC 60 CLRC 70 BVS +; 01 TCALL 11 TCALL 21 TCALL 31 TCALL 41 TCALL 51 TCALL 61 TCALL 71 TCALL +; 02 SET1 12 CLR1 22 SET1 32 CLR1 42 SET1 52 CLR1 62 SET1 72 CLR1 +; 03 BBS 13 BBC 23 BBS 33 BBC 43 BBS 53 BBC 63 BBS 73 BBC +; 04 OR 14 OR 24 AND 34 AND 44 EOR 54 EOR 64 CMP 74 CMP +; 05 OR 15 OR 25 AND 35 AND 45 EOR 55 EOR 65 CMP 75 CMP +; 06 OR 16 OR 26 AND 36 AND 46 EOR 56 EOR 66 CMP 76 CMP +; 07 OR 17 OR 27 AND 37 AND 47 EOR 57 EOR 67 CMP 77 CMP +; 08 OR 18 OR 28 AND 38 AND 48 EOR 58 EOR 68 CMP 78 CMP +; 09 OR 19 OR 29 AND 39 AND 49 EOR 59 EOR 69 CMP 79 CMP +; 0A OR1 1A DECW 2A OR1 3A INCW 4A AND1 5A CMPW 6A AND1 7A ADDW +; 0B ASL 1B ASL 2B ROL 3B ROL 4B LSR 5B LSR 6B ROR 7B ROR +; 0C ASL 1C ASL 2C ROL 3C ROL 4C LSR 5C LSR 6C ROR 7C ROR +; 0D PUSH 1D DEC 2D PUSH 3D INC 4D PUSH 5D MOV 6D PUSH 7D MOV +; 0E TSET1 1E CMP 2E CBNE 3E CMP 4E TCLR1 5E CMP 6E DBNZ 7E CMP +; 0F BRK 1F JMP 2F BRA 3F CALL 4F PCALL 5F JMP 6F RET 7F RETI +; +; 80 SETC 90 BCC A0 EI B0 BCS C0 DI D0 BNE E0 CLRV F0 BEQ +; 81 TCALL 91 TCALL A1 TCALL B1 TCALL C1 TCALL D1 TCALL E1 TCALL F1 TCALL +; 82 SET1 92 CLR1 A2 SET1 B2 CLR1 C2 SET1 D2 CLR1 E2 SET1 F2 CLR1 +; 83 BBS 93 BBC A3 BBS B3 BBC C3 BBS D3 BBC E3 BBS F3 BBC +; 84 ADC 94 ADC A4 SBC B4 SBC C4 MOV D4 MOV E4 MOV F4 MOV +; 85 ADC 95 ADC A5 SBC B5 SBC C5 MOV D5 MOV E5 MOV F5 MOV +; 86 ADC 96 ADC A6 SBC B6 SBC C6 MOV D6 MOV E6 MOV F6 MOV +; 87 ADC 97 ADC A7 SBC B7 SBC C7 MOV D7 MOV E7 MOV F7 MOV +; 88 ADC 98 ADC A8 SBC B8 SBC C8 CMP D8 MOV E8 MOV F8 MOV +; 89 ADC 99 ADC A9 SBC B9 SBC C9 MOV D9 MOV E9 MOV F9 MOV +; 8A EOR1 9A SUBW AA MOV1 BA MOVW CA MOV1 DA MOVW EA NOT1 FA MOV +; 8B DEC 9B DEC AB INC BB INC CB MOV DB MOV EB MOV FB MOV +; 8C DEC 9C DEC AC INC BC INC CC MOV DC DEC EC MOV FC INC +; 8D MOV 9D MOV AD CMP BD MOV CD MOV DD MOV ED NOTC FD MOV +; 8E POP 9E DIV AE POP BE DAA CE POP DE CBNE EE POP FE DBNZ +; 8F MOV 9F XCN AF MOV BF MOV CF MUL DF DAS EF SLEEP FF STOP + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;NOP - No Operation +; N V P B H I Z C +; + +;NOp +ALIGN 16 +Opc: ;This label marks the beginning of the opcode handlers +Opc00: + _CleanUp 2,1 ;Do nothing + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;ADC - Add with Carry +; N V P B H I Z C +; * * * * * + +;AdC A,#imm +ALIGN 16 +Opc88: + Cmp DH,[_PSW(C)] + AdC A,[OP] + Mov CL,A + _CleanUp 2,2,na,NVHZC + +;AdC A,dp +ALIGN 16 +Opc84: + _dp + Cmp DH,[_PSW(C)] + AdC A,[DPI] + Mov CL,A + _CleanUp 3,2,RD,NVHZC + +;AdC A,dp+X +ALIGN 16 +Opc94: + _dpX + Cmp DH,[_PSW(C)] + AdC A,[DPI] + Mov CL,A + _CleanUp 4,2,RD,NVHZC + +;AdC A,!abs +ALIGN 16 +Opc85: + _abs + ShR byte [_PSW(C)],1 + AdC A,[ABSL] + Mov CL,A + _CleanUp 4,3,RA,NVHZC + +;AdC A,!abs+X +ALIGN 16 +Opc95: + _absX + ShR byte [_PSW(C)],1 + AdC A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NVHZC + +;AdC A,!abs+Y +ALIGN 16 +Opc96: + _absY + ShR byte [_PSW(C)],1 + AdC A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NVHZC + +;AdC A,(X) +ALIGN 16 +Opc86: + _X + Cmp DH,[_PSW(C)] + AdC A,[DPI] + Mov CL,A + _CleanUp 3,1,RD,NVHZC + +;AdC A,[dp+X] +ALIGN 16 +Opc87: + _idpX + ShR byte [_PSW(C)],1 + AdC A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NVHZC + +;AdC A,[dp]+Y +ALIGN 16 +Opc97: + _idpY + ShR byte [_PSW(C)],1 + AdC A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NVHZC + +;AdC dp,#imm +ALIGN 16 +Opc98: + Mov CL,[OP] + _dp2 + Cmp DH,[_PSW(C)] + AdC CL,[DPI] + Mov [DPI],CL + _CleanUp 5,2,WD,NVHZC + +;AdC dp,dp +ALIGN 16 +Opc89: + _dp + Mov CL,[DPI] + Mov EDX,DPI + _dp2 + ShR byte [_PSW(C)],1 + AdC CL,[DPI] + Mov [DPI],CL + _CleanUp 6,2,RW,NVHZC + +;AdC (X),(Y) +ALIGN 16 +Opc99: + _Y + Mov CL,[DPI] + Mov EDX,DPI + _X + ShR byte [_PSW(C)],1 + AdC CL,[DPI] + Mov [DPI],CL + _CleanUp 5,1,RW,NVHZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;ADDW - Add Words +; N V P B H I Z C +; * * * * * + +;AddW YA,dp +ALIGN 16 +Opc7A: + _dp + Mov DX,YA + Add DX,[DPI] + SetNZ CL + Or CL,DH + Add A,[DPI] + AdC Y,[1+DPI] ;Half carry is based on the addition made to Y + _CleanUp 5,2,RD16,NVHZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;AND - Bit-wise Logical And +; N V P B H I Z C +; * * + +;And A,#imm +ALIGN 16 +Opc28: + And A,[OP] + Mov CL,A + _CleanUp 2,2,na,NZ + +;And A,dp +ALIGN 16 +Opc24: + _dp + And A,[DPI] + Mov CL,A + _CleanUp 3,2,RD,NZ + +;And A,dp+X +ALIGN 16 +Opc34: + _dpX + And A,[DPI] + Mov CL,A + _CleanUp 4,2,RD,NZ + +;And A,!abs +ALIGN 16 +Opc25: + _abs + And A,[ABSL] + Mov CL,A + _CleanUp 4,3,RA,NZ + +;And A,!abs+X +ALIGN 16 +Opc35: + _absX + And A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;And A,!abs+Y +ALIGN 16 +Opc36: + _absY + And A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;And A,(X) +ALIGN 16 +Opc26: + _X + And A,[DPI] + Mov CL,A + _CleanUp 3,1,RD,NZ + +;And A,[dp+X] +ALIGN 16 +Opc27: + _idpX + And A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;And A,[dp]+Y +ALIGN 16 +Opc37: + _idpY + And A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;And dp,#imm +ALIGN 16 +Opc38: + Mov CL,[OP] + _dp2 + And CL,[DPI] + Mov [DPI],CL + _CleanUp 5,2,WD,NZ + +;And dp,dp +ALIGN 16 +Opc29: + _dp + Mov CL,[DPI] + Mov EDX,DPI + _dp2 + And CL,[DPI] + Mov [DPI],CL + _CleanUp 6,2,RW,NZ + +;And (X),(Y) +ALIGN 16 +Opc39: + _Y + Mov CL,[DPI] + Mov EDX,DPI + _X + And CL,[DPI] + Mov [DPI],CL + _CleanUp 5,1,RW,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;AND1 - And Carry with Absolute Bit +; N V P B H I Z C +; * + +;And1 C,mem.bit +ALIGN 16 +Opc4A: + Test byte [_PSW(C)],1 ;Is carry zero? + JZ short .NC ; Yes, Result will be zero anyway so quit + _mbit + _GetC + ShR EDX,3 + _CleanUp 4,3,RBIT + .NC: + _CleanUp 4,3 ;Should check memory read here, but meh... + +;And1 C,/mem.bit +ALIGN 16 +Opc6A: + Test byte [_PSW(C)],1 + JZ short .NCN + _mbit + _GetCs + ShR EDX,3 + _CleanUp 4,3,RBIT + .NCN: + _CleanUp 4,3 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;ASL - Arithmetic Shift Left +; N V P B H I Z C +; * * * + +;ASL A +ALIGN 16 +Opc1C: + Add A,A + Mov CL,A + _CleanUp 2,1,na,NZC + +;ASL dp +ALIGN 16 +Opc0B: + _dp + ShL byte [DPI],1 + Mov CL,[DPI] + _CleanUp 4,2,WD,NZC + +;ASL dp+X +ALIGN 16 +Opc1B: + _dpX + ShL byte [DPI],1 + Mov CL,[DPI] + _CleanUp 5,2,WD,NZC + +;ASL !abs +ALIGN 16 +Opc0C: + _abs + ShL byte [ABSL],1 + Mov CL,[ABSL] + _CleanUp 5,3,WA,NZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;BBC - Branch If Bit Clear +; N V P B H I Z C +; + +;BBC dp.?,rel +ALIGN 16 +Opc13: +Opc33: +Opc53: +Opc73: +Opc93: +OpcB3: +OpcD3: +OpcF3: + _dp + ShR EDX,5 ;EDX = Bit to test + Inc PC ;Advance PC to displacment + BT [DPI],EDX ;Test requested bit, is it clear? + JC short .BCDone ; No, Clean up + _Profile(bbc) + MovSX EDX,byte [OP] ;EDX = Relative displacement + Add PC,DX ;Adjust PC + Sub dword [_Var(clkLeft)],2*CPU_CYC ;Subtract an additional 2 clock cycles + .BCDone: + Inc PC + _CleanUp 5,na,RD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;BBS - Branch If Bit Set +; N V P B H I Z C +; + +;BBS dp.?,rel +ALIGN 16 +Opc03: +Opc23: +Opc43: +Opc63: +Opc83: +OpcA3: +OpcC3: +OpcE3: + _dp + ShR EDX,5 + Inc PC + BT [DPI],EDX + JNC short .BSDone + _Profile(bbs) + MovSX EDX,byte [OP] + Add PC,DX + Sub dword [_Var(clkLeft)],2*CPU_CYC + .BSDone: + Inc PC + _CleanUp 5,na,RD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Bcc - Conditional Branch +; N V P B H I Z C +; + + ;----------------------------------------- + ;Branch on condition + ; %1 - Flag to Test or condition of CL to test + ; %2 - Condition of flag (0 or 1) + + %macro Bxx 1-2 + %if %0 == 2 + Test byte [_PSW(%1)],1 + %if %2 == 1 + JZ short %%Done + %else + JNZ short %%Done + %endif + %else + Test CL,CL + J%-1 short %%Done + %endif +%if PROFILE + ShR EDX,5 + _Profile(bxx) +%endif + MovSX EDX,byte [OP] + Add PC,DX + _CleanUp 4,2 + %%Done: + _CleanUp 2,2 + %endmacro + +; ShR DL,5 +; And DL,1 +; XOr DL,[_PSW(%1)] +; JNZ short %%BcDone + +;BCC rel - Branch if Carry Clear (JAE) +ALIGN 16 +Opc90: + Bxx C,0 + +;BCS rel - Branch if Carry Set (JB) +ALIGN 16 +OpcB0: + Bxx C,1 + +;BEQ rel - Branch if Equal (JE/JZ) +ALIGN 16 +OpcF0: + Bxx Z + +;BMI rel - Branch if Minus (JS) +ALIGN 16 +Opc30: + Bxx S + +;BNE rel - Branch if Not Equal (JNE/JNZ) +ALIGN 16 +OpcD0: + Bxx NZ + +;BPL rel - Branch if Plus (JNS) +ALIGN 16 +Opc10: + Bxx NS + +;BVC rel - Branch if Overflow Clear (JNO) +ALIGN 16 +Opc50: + Bxx V,0 + +;BVS rel - Branch if Overflow Set (JO) +ALIGN 16 +Opc70: + Bxx V,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;BRA - Branch (JMP Short) +; N V P B H I Z C +; + +;BRA rel +ALIGN 16 +Opc2F: + MovSX EDX,byte [OP] + Add PC,DX + _CleanUp 4,2 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;BRK - Software Interrupt +; N V P B H I Z C +; 1 0 + +;Brk +ALIGN 16 +Opc0F: + _PushPC + _PackPSW + _Push CL + And CL,82h ;_PackPSW destroyed the contents of CL + XOr CL,2 ;Adjust CL to reflect the state of N and Z + Mov PC,[0FFDEh+RAM] + Mov byte [_PSW(B)],1 + Mov byte [_PSW(I)],0 + _CleanUp 8,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CALL - Call Procedure +; N V P B H I Z C +; + +;Call !abs +ALIGN 16 +Opc3F: + _PushPC 2 + Mov PC,[OP] + _CleanUp 8,na + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CBNE - Compare with A, Branch if Not Equal +; N V P B H I Z C +; + +;CBNE dp,rel +ALIGN 16 +Opc2E: + _dp + Inc PC + Cmp A,[DPI] + JE short .NCBdp +%if PROFILE + Inc dword [profile+Profile.cbne] +%endif + MovSX EDX,byte [OP] + Add PC,DX + Sub dword [_Var(clkLeft)],2*CPU_CYC + .NCBdp: + Inc PC + _CleanUp 5,na,RD + +;CBNE dp+X,rel +ALIGN 16 +OpcDE: + _dpX + Inc PC + Cmp A,[DPI] + JE short .NCBdpx +%if PROFILE + Inc dword [4+profile+Profile.cbne] +%endif + MovSX EDX,byte [OP] + Add PC,DX + Sub dword [_Var(clkLeft)],2*CPU_CYC + .NCBdpx: + Inc PC + _CleanUp 6,na,RD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CLR1 - Clear Bit +; N V P B H I Z C +; + +;Clr1 dp.? +ALIGN 16 +Opc12: +Opc32: +Opc52: +Opc72: +Opc92: +OpcB2: +OpcD2: +OpcF2: + _dp + ShR EDX,5 + BTR [DPI],EDX + _CleanUp 4,2,WD + + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CLRC - Clear Carry Flag +; N V P B H I Z C +; 0 + +;ClrC +ALIGN 16 +Opc60: + Mov [_PSW(C)],DH + _CleanUp 2,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CLRP - Clear Direct Page Flag +; N V P B H I Z C +; 0 + +;ClrP +ALIGN 16 +Opc20: + Mov BH,DH + Mov [_PSW(P)],DH + _CleanUp 2,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CLRV - Clear Overflow and Half-carry Flags +; N V P B H I Z C +; 0 0 + +;ClrV +ALIGN 16 +OpcE0: + Mov [_PSW(V)],DH + Mov [_PSW(H)],DH + _CleanUp 2,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CMP - Compare Operands +; N V P B H I Z C +; * * * + +;Cmp A,#imm +ALIGN 16 +Opc68: + Mov CL,A + Sub CL,[OP] + _CleanUp 2,2,na,NZCs + +;Cmp X,#imm +ALIGN 16 +OpcC8: + Mov CL,X + Sub CL,[OP] + _CleanUp 2,2,na,NZCs + +;Cmp Y,#imm +ALIGN 16 +OpcAD: + Mov CL,Y + Sub CL,[OP] + _CleanUp 2,2,na,NZCs + +;Cmp A,dp +ALIGN 16 +Opc64: + _dp + Mov CL,A + Sub CL,[DPI] + _CleanUp 3,2,RD,NZCs + +;Cmp X,dp +ALIGN 16 +Opc3E: + _dp + Mov CL,X + Sub CL,[DPI] + _CleanUp 3,2,RD,NZCs + +;Cmp Y,dp +ALIGN 16 +Opc7E: + _dp + Mov CL,Y + Sub CL,[DPI] + _CleanUp 3,2,RD,NZCs + +;Cmp A,dp+X +ALIGN 16 +Opc74: + _dpX + Mov CL,A + Sub CL,[DPI] + _CleanUp 4,2,RD,NZCs + +;Cmp A,!abs +ALIGN 16 +Opc65: + _abs + Mov CL,A + Sub CL,[ABSL] + _CleanUp 4,3,RA,NZCs + +;Cmp X,!abs +ALIGN 16 +Opc1E: + _abs + Mov CL,X + Sub CL,[ABSL] + _CleanUp 4,3,RA,NZCs + +;Cmp Y,!abs +ALIGN 16 +Opc5E: + _abs + Mov CL,Y + Sub CL,[ABSL] + _CleanUp 4,3,RA,NZCs + +;Cmp A,!abs+X +ALIGN 16 +Opc75: + _absX + Mov CL,A + Sub CL,[ABSL] + _CleanUp 5,3,RA,NZCs + +;Cmp A,!abs+Y +ALIGN 16 +Opc76: + _absY + Mov CL,A + Sub CL,[ABSL] + _CleanUp 5,3,RA,NZCs + +;Cmp A,(X) +ALIGN 16 +Opc66: + _X + Mov CL,A + Sub CL,[DPI] + _CleanUp 3,1,RD,NZCs + +;Cmp A,[dp+X] +ALIGN 16 +Opc67: + _idpX + Mov CL,A + Sub CL,[ABSL] + _CleanUp 6,2,RA,NZCs + +;Cmp A,[dp]+Y +ALIGN 16 +Opc77: + _idpY + Mov CL,A + Sub CL,[ABSL] + _CleanUp 6,2,RA,NZCs + +;Cmp dp,#imm +ALIGN 16 +Opc78: + Mov DL,[OP] + _dp2 + Mov CL,[DPI] + Sub CL,DL + _CleanUp 5,2,RD,NZCs + +;Cmp dp,dp +ALIGN 16 +Opc69: + _dp + Mov DH,[DPI] + Mov DL,BL + _dp2 + Mov CL,[DPI] + Sub CL,DH + _GetCs + Mov DH,BH + _RdChk WD ;These last intructions are oddities that require two + _CleanUp 6,2,RD ; read checks + +;Cmp (X),(Y) +ALIGN 16 +Opc79: + _Y + Mov DH,[DPI] + Mov DL,BL + _X + Mov CL,[DPI] + Sub CL,DH + Mov DH,BH + _GetCs + _RdChk WD + _CleanUp 5,1,RD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;CMPW - Compare Words +; N V P B H I Z C +; * * * + +;CmpW YA,dp +ALIGN 16 +Opc5A: + _dp + Mov DX,YA + Sub DX,[DPI] + _GetCs + SetNZ CL + Or CL,DH + _CleanUp 4,2,RD16 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DAA - Decimal Adjust After Addition +; N V P B H I Z C +; * * * + +;DAA A +ALIGN 16 +OpcDF: + Mov DL,AH ;Save AH (Y) + Mov AH,[_PSW(H)] ;Set AF and CF in AH + ShL AH,4 + Or AH,[_PSW(C)] + SAHF ;Store AH into flags register + Mov AH,DL ;Restore AH + DAA ;Execute DAA on AL (A) + Mov CL,A + _CleanUp 3,1,na,NZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DAS - Decimal Adjust After Subtraction +; N V P B H I Z C +; * * * + +;DAS A +ALIGN 16 +OpcBE: + Mov DL,AH + Mov AH,[_PSW(H)] + ShL AH,4 + Or AH,[_PSW(C)] + XOr AH,11h ;Reverse flags for x86 + SAHF + Mov AH,DL + DAS + Mov CL,A + _CleanUp 3,1,na,NZCs + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DBNZ - Decrease, Branch if not Zero +; N V P B H I Z C +; + +;DBNZ Y,rel +ALIGN 16 +OpcFE: + Dec Y + JZ short .NoBranch +%if PROFILE + Inc dword [profile+Profile.dbnz] +%endif + MovSX EDX,byte [OP] + Add PC,DX + Sub dword [_Var(clkLeft)],2*CPU_CYC + .NoBranch: + _CleanUp 4,2 + +;DBNZ dp,rel +ALIGN 16 +Opc6E: + _dp + Inc PC + Dec byte [DPI] + JZ short .NoBranch +%if PROFILE + Inc dword [4+profile+Profile.dbnz] +%endif + MovSX EDX,byte [OP] + Add PC,DX + Sub dword [_Var(clkLeft)],2*CPU_CYC + .NoBranch: + Inc PC + _CleanUp 5,na,WD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DEC - Decrease Byte by 1 +; N V P B H I Z C +; * * + +;Dec A +ALIGN 16 +Opc9C: + Dec A + Mov CL,A + _CleanUp 2,1,na,NZ + +;Dec X +ALIGN 16 +Opc1D: + Dec X + Mov CL,X + _CleanUp 2,1,na,NZ + +;Dec Y +ALIGN 16 +OpcDC: + Dec Y + Mov CL,Y + _CleanUp 2,1,na,NZ + +;Dec dp +ALIGN 16 +Opc8B: + _dp + Dec byte [DPI] + Mov CL,[DPI] + _CleanUp 4,2,WD,NZ + +;Dec dp+X +ALIGN 16 +Opc9B: + _dpX + Dec byte [DPI] + Mov CL,[DPI] + _CleanUp 5,2,WD,NZ + +;Dec !abs +ALIGN 16 +Opc8C: + _abs + Dec byte [ABSL] + Mov CL,[ABSL] + _CleanUp 5,3,WA,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DECW - Decrease Word by 1 +; N V P B H I Z C +; * * + +;DecW dp +ALIGN 16 +Opc1A: + _dp + Dec word [DPI] + SetNZ CL + Or CL,[1+DPI] + _CleanUp 6,2,WD16,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DI - Disable Interrupts +; N V P B H I Z C +; 0 + +;DI +ALIGN 16 +OpcC0: + Mov [_PSW(I)],DH + _CleanUp 3,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DIV - Divide +;<Divide by zero and normal divide verified by Blargg on 04.10.03> +;<Division overflow created by Anomie and verified by CaitSith on 05.08.04> +; N V P B H I Z C +; * * * * + +;Div YA,X +ALIGN 16 +Opc9E: +%if HALFC + Mov DL,Y + Mov BL,X + And DL,0Fh + And BL,0Fh + Cmp DL,BL + SetAE [_PSW(H)] +%endif + + Test X,X ;Divide by 0? + JZ short .Div0 + Cmp Y,X ;Will division overflow? + JAE short .DivOF + + Div X + Mov [_PSW(V)],DH + Mov CL,A + _CleanUp 12,1,na,NZ + + .Div0: + RoL YA,8 + Not A + Mov byte [_PSW(V)],1 + Mov CL,A + _CleanUp 12,1,na,NZ + + .DivOF: + XOr EDX,EDX ;Perform division manually to get the same wacky result + Mov CL,9 ; as the SPC700 + + .Next: + SetNC BL ;if (Y >= X) C ^= 1 + Cmp Y,X + AdC BL,DL + ShR BL,1 + + JNC short .NoSub ;if (C) Y -= X + Sub Y,X + StC + .NoSub: + + RCL YA,1 + + Dec CL + JNZ short .Next + + RCR Y,1 + SetC [_PSW(V)] + Mov CL,A + _CleanUp 12,1,na,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;EI - Enable Interrupts +; N V P B H I Z C +; 1 + +;EI +ALIGN 16 +OpcA0: + Mov byte [_PSW(I)],1 + _CleanUp 3,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;EOR - Bit-wise Logical Exclusive Or +; N V P B H I Z C +; * * + +;EOr A,#imm +ALIGN 16 +Opc48: + XOr A,[OP] + Mov CL,A + _CleanUp 2,2,na,NZ + +;EOr A,dp +ALIGN 16 +Opc44: + _dp + XOr A,[DPI] + Mov CL,A + _CleanUp 3,2,RD,NZ + +;EOr A,dp+X +ALIGN 16 +Opc54: + _dpX + XOr A,[DPI] + Mov CL,A + _CleanUp 4,2,RD,NZ + +;EOr A,!abs +ALIGN 16 +Opc45: + _abs + XOr A,[ABSL] + Mov CL,A + _CleanUp 4,3,RA,NZ + +;EOr A,!abs+X +ALIGN 16 +Opc55: + _absX + XOr A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;EOr A,!abs+Y +ALIGN 16 +Opc56: + _absY + XOr A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;EOr A,(X) +ALIGN 16 +Opc46: + _X + XOr A,[DPI] + Mov CL,A + _CleanUp 3,1,RD,NZ + +;EOr A,[dp+X] +ALIGN 16 +Opc47: + _idpX + XOr A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;EOr A,[dp]+Y +ALIGN 16 +Opc57: + _idpY + XOr A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;EOr dp,#imm +ALIGN 16 +Opc58: + Mov CL,[OP] + _dp2 + XOr CL,[DPI] + Mov [DPI],CL + _CleanUp 5,2,WD,NZ + +;EOr dp,dp +ALIGN 16 +Opc49: + _dp + Mov CL,[DPI] + Mov EDX,DPI + _dp2 + XOr CL,[DPI] + Mov [DPI],CL + _CleanUp 6,2,RW,NZ + +;EOr (X),(Y) +ALIGN 16 +Opc59: + _Y + Mov CL,[DPI] + Mov EDX,DPI + _X + XOr CL,[DPI] + Mov [DPI],CL + _CleanUp 5,1,RW,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;EOR1 - XOr Carry with Absolute Bit +; N V P B H I Z C +; * + +;EOr1 C,mem.bit +ALIGN 16 +Opc8A: + Mov BL,DH + _mbit + RCL BL,1 + XOr [_PSW(C)],BL + ShR EDX,3 + _CleanUp 5,3,RBIT + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;INC - Increase Byte by 1 +; N V P B H I Z C +; * * + +;Inc A +ALIGN 16 +OpcBC: + Inc A + Mov CL,A + _CleanUp 2,1,na,NZ + +;Inc X +ALIGN 16 +Opc3D: + Inc X + Mov CL,X + _CleanUp 2,1,na,NZ + +;Inc Y +ALIGN 16 +OpcFC: + Inc Y + Mov CL,Y + _CleanUp 2,1,na,NZ + +;Inc dp +ALIGN 16 +OpcAB: + _dp + Inc byte [DPI] + Mov CL,[DPI] + _CleanUp 4,2,WD,NZ + +;Inc dp+X +ALIGN 16 +OpcBB: + _dpX + Inc byte [DPI] + Mov CL,[DPI] + _CleanUp 5,2,WD,NZ + +;Inc !abs +ALIGN 16 +OpcAC: + _abs + Inc byte [ABSL] + Mov CL,[ABSL] + _CleanUp 5,3,WA,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;INCW - Increase Word by 1 +; N V P B H I Z C +; * * + +;INCW dp +ALIGN 16 +Opc3A: + _dp + Inc word [DPI] + SetNZ CL + Or CL,[1+DPI] + _CleanUp 6,2,WD16,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;JMP - Indirect Jump +; N V P B H I Z C +; + +;Jmp !abs +ALIGN 16 +Opc5F: + Mov PC,[OP] + _CleanUp 3,na + +;Jmp [!abs+X] +ALIGN 16 +Opc1F: + _absX + Mov PC,[ABSL] + _CleanUp 6,na + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;LSR - Logical Shift Right +; N V P B H I Z C +; * * * + +;LSR A +ALIGN 16 +Opc5C: + ShR A,1 + Mov CL,A + _CleanUp 2,1,na,NZC + +;LSR dp +ALIGN 16 +Opc4B: + _dp + ShR byte [DPI],1 + Mov CL,[DPI] + _CleanUp 4,2,WD,NZC + +;LSR dp+X +ALIGN 16 +Opc5B: + _dpX + ShR byte [DPI],1 + Mov CL,[DPI] + _CleanUp 5,2,WD,NZC + +;LSR !abs +ALIGN 16 +Opc4C: + _abs + ShR byte [ABSL],1 + Mov CL,[ABSL] + _CleanUp 5,3,WA,NZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Load Immediate +; N V P B H I Z C +; * * + +;Mov A,#imm +ALIGN 16 +OpcE8: + Mov A,[OP] + Mov CL,A + _CleanUp 2,2,na,NZ + +;Mov X,#imm +ALIGN 16 +OpcCD: + Mov X,[OP] + Mov CL,X + _CleanUp 2,2,na,NZ + +;Mov Y,#imm +ALIGN 16 +Opc8D: + Mov Y,[OP] + Mov CL,Y + _CleanUp 2,2,na,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Copy Register +; N V P B H I Z C +; * * + +;Mov A,X +ALIGN 16 +Opc7D: + Mov A,X + Mov CL,X + _CleanUp 2,1,na,NZ + +;Mov A,Y +ALIGN 16 +OpcDD: + Mov A,Y + Mov CL,Y + _CleanUp 2,1,na,NZ + +;Mov X,A +ALIGN 16 +Opc5D: + Mov X,A + Mov CL,A + _CleanUp 2,1,na,NZ + +;Mov Y,A +ALIGN 16 +OpcFD: + Mov Y,A + Mov CL,A + _CleanUp 2,1,na,NZ + +;Mov X,SP +ALIGN 16 +Opc9D: + Mov X,byte [_Var(regSP)] + Mov CL,X + _CleanUp 2,1,na,NZ + +;Mov SP,X (flags not affected) +ALIGN 16 +OpcBD: + Mov byte [_Var(regSP)],X + _CleanUp 2,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Load Memory Into Register +; N V P B H I Z C +; * * + +;Mov A,dp +ALIGN 16 +OpcE4: + _dp + Mov A,[DPI] + Mov CL,A + _CleanUp 3,2,RD,NZ + +;Mov X,dp +ALIGN 16 +OpcF8: + _dp + Mov X,[DPI] + Mov CL,X + _CleanUp 3,2,RD,NZ + +;Mov Y,dp +ALIGN 16 +OpcEB: + _dp + Mov Y,[DPI] + Mov CL,Y + _CleanUp 3,2,RD,NZ + +;Mov A,dp+X +ALIGN 16 +OpcF4: + _dpX + Mov A,[DPI] + Mov CL,A + _CleanUp 4,2,RD,NZ + +;Mov X,dp+Y +ALIGN 16 +OpcF9: + _dpY + Mov X,[DPI] + Mov CL,X + _CleanUp 4,2,RD,NZ + +;Mov Y,dp+X +ALIGN 16 +OpcFB: + _dpX + Mov Y,[DPI] + Mov CL,Y + _CleanUp 4,2,RD,NZ + +;Mov A,!abs +ALIGN 16 +OpcE5: + _abs + Mov A,[ABSL] + Mov CL,A + _CleanUp 4,3,RA,NZ + +;Mov X,!abs +ALIGN 16 +OpcE9: + _abs + Mov X,[ABSL] + Mov CL,X + _CleanUp 4,3,RA,NZ + +;Mov Y,!abs +ALIGN 16 +OpcEC: + _abs + Mov Y,[ABSL] + Mov CL,Y + _CleanUp 4,3,RA,NZ + +;Mov A,!abs+X +ALIGN 16 +OpcF5: + _absX + Mov A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;Mov A,!abs+Y +ALIGN 16 +OpcF6: + _absY + Mov A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;Mov A,(X) +ALIGN 16 +OpcE6: + _X + Mov A,[DPI] + Mov CL,A + _CleanUp 3,1,RD,NZ + +;Mov A,[dp+X] +ALIGN 16 +OpcE7: + _idpX + Mov A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;Mov A,[dp]+Y +ALIGN 16 +OpcF7: + _idpY + Mov A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Store Register in Memory +; N V P B H I Z C +; + +;Mov dp,A +ALIGN 16 +OpcC4: + _dp + Mov [DPI],A + _CleanUp 4,2,WD + +;Mov dp,X +ALIGN 16 +OpcD8: + _dp + Mov [DPI],X + _CleanUp 4,2,WD + +;Mov dp,Y +ALIGN 16 +OpcCB: + _dp + Mov [DPI],Y + _CleanUp 4,2,WD + +;Mov dp+X,A +ALIGN 16 +OpcD4: + _dpX + Mov [DPI],A + _CleanUp 5,2,WD + +;Mov dp+Y,X +ALIGN 16 +OpcD9: + _dpY + Mov [DPI],X + _CleanUp 5,2,WD + +;Mov dp+X,Y +ALIGN 16 +OpcDB: + _dpX + Mov [DPI],Y + _CleanUp 5,2,WD + +;Mov !abs,A +ALIGN 16 +OpcC5: + _abs + Mov [ABSL],A + _CleanUp 5,3,WA + +;Mov !abs,X +ALIGN 16 +OpcC9: + _abs + Mov [ABSL],X + _CleanUp 5,3,WA + +;Mov !abs,Y +ALIGN 16 +OpcCC: + _abs + Mov [ABSL],Y + _CleanUp 5,3,WA + +;Mov !abs+X,A +ALIGN 16 +OpcD5: + _absX + Mov [ABSL],A + _CleanUp 6,3,WA + +;Mov !abs+Y,A +ALIGN 16 +OpcD6: + _absY + Mov [ABSL],A + _CleanUp 6,3,WA + +;Mov [dp+X],A +ALIGN 16 +OpcC7: + _idpX + Mov [ABSL],A + _CleanUp 7,2,WA + +;Mov [dp]+Y,A +ALIGN 16 +OpcD7: + _idpY + Mov [ABSL],A + _CleanUp 7,2,WA + +;Mov (X),A +ALIGN 16 +OpcC6: + _X + Mov [DPI],A + _CleanUp 4,1,WD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Move Data to Memory +; N V P B H I Z C +; + +;Mov dp,#imm +ALIGN 16 +Opc8F: + Mov DL,[OP] + _dp2 + Mov [DPI],DL + _CleanUp 5,2,WD + +;Mov dp,dp +ALIGN 16 +OpcFA: + _dp + Mov DH,[DPI] + Mov DL,BL + _dp2 + Mov [DPI],DH + Mov DH,BH + _CleanUp 5,2,RW + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Load Byte with Auto Increase +; N V P B H I Z C +; * * + +;Mov A,(X)+ +ALIGN 16 +OpcBF: + _X + Inc X + Mov A,[DPI] + Mov CL,A + _CleanUp 4,1,RD,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV - Store Byte with Auto Increase +; N V P B H I Z C +; + +;Mov (X)+,A +ALIGN 16 +OpcAF: + _X + Inc X + Mov [DPI],A + _CleanUp 4,1,WD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV1 - Load Absolute Bit Into Carry +; N V P B H I Z C +; * + +;Mov1 C,mem.bit +ALIGN 16 +OpcAA: + _mbit + _GetC + ShR EDX,3 + _CleanUp 4,3,RBIT + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOV1 - Store Carry in Absolute Bit +; N V P B H I Z C +; + +;Mov1 mem.bit,C +ALIGN 16 +OpcCA: + _abs + Push ECX + Mov CL,DH + And DH,1Fh + ShR CL,5 + Mov BL,0FEh + RoL BL,CL + And [ABSL],BL + Mov BL,[_PSW(C)] + ShL BL,CL + Or [ABSL],BL + Pop ECX + _CleanUp 6,3,WBIT + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MOVW - Move Word +; N V P B H I Z C +; * * + +;MovW YA,dp +ALIGN 16 +OpcBA: + _dp + Mov YA,[DPI] + Test YA,YA + SetNZ CL + Or CL,Y + _CleanUp 5,2,RD16,NZ + +;MovW dp,YA (flags not affected) +ALIGN 16 +OpcDA: + _dp + Mov [DPI],YA + _CleanUp 5,2,WD16 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;MUL - Multiply +; N V P B H I Z C +; * * + +;Mul YA +ALIGN 16 +OpcCF: + Mul Y + Test YA,YA + SetNZ CL + Or CL,Y + _CleanUp 9,1,na,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;NOT1 - Complement Absolute Bit +; N V P B H I Z C +; + +;Not1 mem.bit +ALIGN 16 +OpcEA: + Mov DX,[OP] + RoL DX,3 + BTC [RAM],EDX + ShR EDX,3 + _CleanUp 5,3,WBIT + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;NOTC - Complement Carry Flag +; N V P B H I Z C +; * + +;NotC +ALIGN 16 +OpcED: + XOr byte [_PSW(C)],1 + _CleanUp 3,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;OR - Bit-wise Logical Inclusive Or +; N V P B H I Z C +; * * + +;Or A,#imm +ALIGN 16 +Opc08: + Or A,[OP] + Mov CL,A + _CleanUp 2,2,na,NZ + +;Or A,dp +ALIGN 16 +Opc04: + _dp + Or A,[DPI] + Mov CL,A + _CleanUp 3,2,RD,NZ + +;Or A,dp+X +ALIGN 16 +Opc14: + _dpX + Or A,[DPI] + Mov CL,A + _CleanUp 4,2,RD,NZ + +;Or A,!abs +ALIGN 16 +Opc05: + _abs + Or A,[ABSL] + Mov CL,A + _CleanUp 4,3,RA,NZ + +;Or A,!abs+X +ALIGN 16 +Opc15: + _absX + Or A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;Or A,!abs+Y +ALIGN 16 +Opc16: + _absY + Or A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NZ + +;Or A,(X) +ALIGN 16 +Opc06: + _X + Or A,[DPI] + Mov CL,A + _CleanUp 3,1,RD,NZ + +;Or A,[dp+X] +ALIGN 16 +Opc07: + _idpX + Or A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;Or A,[dp]+Y +ALIGN 16 +Opc17: + _idpY + Or A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NZ + +;Or dp,#imm +ALIGN 16 +Opc18: + Mov CL,[OP] + _dp2 + Or CL,[DPI] + Mov [DPI],CL + _CleanUp 5,2,WD,NZ + +;Or dp,dp +ALIGN 16 +Opc09: + _dp + Mov CL,[DPI] + Mov EDX,DPI + _dp2 + Or CL,[DPI] + Mov [DPI],CL + _CleanUp 6,2,RW,NZ + +;Or (X),(Y) +ALIGN 16 +Opc19: + _Y + Mov CL,[DPI] + Mov EDX,DPI + _X + Or CL,[DPI] + Mov [DPI],CL + _CleanUp 5,1,RW,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;OR1 - Or Carry with Absolute Bit +; N V P B H I Z C +; * + +;Or1 C,mem.bit +ALIGN 16 +Opc0A: + _mbit + SetC BL + Or [_PSW(C)],BL + ShR EDX,3 + _CleanUp 5,3,RBIT + +;Or1 C,/mem.bit +ALIGN 16 +Opc2A: + _mbit + SetNC BL + Or [_PSW(C)],BL + ShR EDX,3 + _CleanUp 5,3,RBIT + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;PCALL - Call Procedure in Uppermost Page +; N V P B H I Z C +; + +;PCall up +ALIGN 16 +Opc4F: + _PushPC 1 + Mov DL,[OP] + Mov DH,0FFh + Mov ESI,EDX + _CleanUp 6,na + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;POP - Pop Register Off Stack +; N V P B H I Z C +; + +;Pop A +ALIGN 16 +OpcAE: + _Pop A + _CleanUp 4,1 + +;Pop X +ALIGN 16 +OpcCE: + _Pop X + _CleanUp 4,1 + +;Pop Y +ALIGN 16 +OpcEE: + _Pop Y + _CleanUp 4,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;POP - Pop Flags Off Stack +; N V P B H I Z C +; ? ? ? ? ? ? ? ? + +;Pop PSW +ALIGN 16 +Opc8E: + _Pop CL + _UnpackPSW + _CleanUp 4,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;PUSH - Push Register Onto Stack +; N V P B H I Z C +; + +;Push A +ALIGN 16 +Opc2D: + _Push A + _CleanUp 4,1 + +;Push X +ALIGN 16 +Opc4D: + _Push X + _CleanUp 4,1 + +;Push Y +ALIGN 16 +Opc6D: + _Push Y + _CleanUp 4,1 + +;Push PSW +ALIGN 16 +Opc0D: + _PackPSW + _Push CL + And CL,82h + XOr CL,2 + _CleanUp 4,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;RET - Return From Call +; N V P B H I Z C +; + +;Ret +ALIGN 16 +Opc6F: + _PopPC + _CleanUp 5,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;RETI - Return From Interrupt +; N V P B H I Z C +; ? ? ? ? ? ? ? ? + +;RetI +ALIGN 16 +Opc7F: + _Pop CL + _UnpackPSW + _PopPC + _CleanUp 6,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;ROL - Rotate 9-bits Left +; N V P B H I Z C +; * * * + +;RoL A +ALIGN 16 +Opc3C: + Cmp DH,[_PSW(C)] + RCL A,1 + Mov CL,A + _CleanUp 2,1,na,NZC + +;RoL dp +ALIGN 16 +Opc2B: + _dp + Cmp DH,[_PSW(C)] + RCL byte [DPI],1 + Mov CL,[DPI] + _CleanUp 4,2,WD,NZC + +;RoL dp+X +ALIGN 16 +Opc3B: + _dpX + Cmp DH,[_PSW(C)] + RCL byte [DPI],1 + Mov CL,[DPI] + _CleanUp 5,2,WD,NZC + +;RoL !abs +ALIGN 16 +Opc2C: + Cmp DH,[_PSW(C)] + _abs + RCL byte [ABSL],1 + Mov CL,[ABSL] + _CleanUp 5,3,WA,NZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;ROR - Rotate 9-bits Right +; N V P B H I Z C +; * * * + +;RoR A +ALIGN 16 +Opc7C: + Cmp DH,[_PSW(C)] + RCR A,1 + Mov CL,A + _CleanUp 2,1,na,NZC + +;RoR dp +ALIGN 16 +Opc6B: + _dp + Cmp DH,[_PSW(C)] + RCR byte [DPI],1 + Mov CL,[DPI] + _CleanUp 4,2,WD,NZC + +;RoR dp+X +ALIGN 16 +Opc7B: + _dpX + Cmp DH,[_PSW(C)] + RCR byte [DPI],1 + Mov CL,[DPI] + _CleanUp 5,2,WD,NZC + +;RoR !abs +ALIGN 16 +Opc6C: + Cmp DH,[_PSW(C)] + _abs + RCR byte [ABSL],1 + Mov CL,[ABSL] + _CleanUp 5,3,WA,NZC + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;SBC - Subtract with Carry +; N V P B H I Z C +; * * * * * + +;SbC A,#imm +ALIGN 16 +OpcA8: + Cmp byte [_PSW(C)],1 + SbB A,[OP] + Mov CL,A + _CleanUp 2,2,na,NVHZCs + +;SbC A,dp +ALIGN 16 +OpcA4: + _dp + Cmp byte [_PSW(C)],1 + SbB A,[DPI] + Mov CL,A + _CleanUp 3,2,RD,NVHZCs + +;SbC A,dp+X +ALIGN 16 +OpcB4: + _dpX + Cmp byte [_PSW(C)],1 + SbB A,[DPI] + Mov CL,A + _CleanUp 4,2,RD,NVHZCs + +;SbC A,!abs +ALIGN 16 +OpcA5: + _abs + Cmp byte [_PSW(C)],1 + SbB A,[ABSL] + Mov CL,A + _CleanUp 4,3,RA,NVHZCs + +;SbC A,!abs+X +ALIGN 16 +OpcB5: + _absX + Cmp byte [_PSW(C)],1 + SbB A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NVHZCs + +;SbC A,!abs+Y +ALIGN 16 +OpcB6: + _absY + Cmp byte [_PSW(C)],1 + SbB A,[ABSL] + Mov CL,A + _CleanUp 5,3,RA,NVHZCs + +;SbC A,(X) +ALIGN 16 +OpcA6: + _X + Cmp byte [_PSW(C)],1 + SbB A,[DPI] + Mov CL,A + _CleanUp 3,1,RD,NVHZCs + +;SbC A,[dp+X] +ALIGN 16 +OpcA7: + _idpX + Cmp byte [_PSW(C)],1 + SbB A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NVHZCs + +;SbC A,[dp]+Y +ALIGN 16 +OpcB7: + _idpY + Cmp byte [_PSW(C)],1 + SbB A,[ABSL] + Mov CL,A + _CleanUp 6,2,RA,NVHZCs + +;SbC dp,#imm +ALIGN 16 +OpcB8: + Mov CL,[OP] + _dp2 + Cmp byte [_PSW(C)],1 + SbB [DPI],CL + Mov CL,[DPI] + _CleanUp 5,2,WD,NVHZCs + +;SbC dp,dp +ALIGN 16 +OpcA9: + _dp + Mov CL,[DPI] + Mov EDX,DPI + _dp2 + Cmp byte [_PSW(C)],1 + SbB [DPI],CL + Mov CL,[DPI] + _CleanUp 6,2,RW,NVHZCs + +;SbC (X),(Y) +ALIGN 16 +OpcB9: + _Y + Mov CL,[DPI] + Mov EDX,DPI + _X + Cmp byte [_PSW(C)],1 + SbB [DPI],CL + Mov CL,[DPI] + _CleanUp 5,1,RW,NVHZCs + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;SET1 - Set Bit +; N V P B H I Z C +; + +;Set1 dp.? +ALIGN 16 +Opc02: +Opc22: +Opc42: +Opc62: +Opc82: +OpcA2: +OpcC2: +OpcE2: + _dp + ShR EDX,5 + BTS [DPI],EDX + _CleanUp 4,2,WD + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;SETC - Set Carry Flag +; N V P B H I Z C +; 1 + +;SetC +ALIGN 16 +Opc80: + Mov byte [_PSW(C)],1 + _CleanUp 2,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;SETP - Set Direct Page Flag +; N V P B H I Z C +; 1 0 + +;SetP +ALIGN 16 +Opc40: + Mov BH,1 + Mov [_PSW(I)],DH + Mov [_PSW(P)],BH + _CleanUp 2,1 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;SLEEP - Suspend the SPC700 and Timers +; N V P B H I Z C +; + +;Sleep +ALIGN 16 +OpcEF: + ;Fall through to STOP + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;STOP - Stop the SPC700 Clock +; N V P B H I Z C +; + +;Stop +OpcFF: + Or byte [_Var(dbgOpt)],SPC_HALT ;Halt emulation + Test byte [_Var(dbgOpt)],SPC_STOP ;If the user only wants debug calls on errors and... + SetZ DL + Dec EDX + Test EDX,[_Var(pDebug)] ;...has specified a debugging vector then call vector + JNZ SPCBreak + Jmp SPCHalt ; otherwise, exit fetch loop + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;SUBW - Subtract Word +; N V P B H I Z C +; * * * * * + +;SubW YA,dp +ALIGN 16 +Opc9A: + _dp + Mov DX,YA + Sub DX,[DPI] + SetNZ CL + Or CL,DH + Sub A,[DPI] + SbB Y,[1+DPI] ;Half-carry is based on the subtraction made from Y + _CleanUp 5,2,RD16,NVHZCs + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;TCALL - Call Pointer in Vector Table +; N V P B H I Z C +; + + ;----------------------------------------- + ;Table Call + ; %1 - Table Index + + %macro TCall 1 + _PushPC + Mov PC,word [((15-%1)*2)+RAM+ipl] + _CleanUp 8,na + %endmacro + +; Mov EBX,[_Var(regSP)] +; ShR EDX,3 +; Mov [EBX-1],PC +; XOr EDX,0FFDEh +; Sub byte [_Var(regSP)],2 +; Mov PC,[EDX+RAM] +; Mov BH,[_PSW(P)] + +;TCall 0 +ALIGN 16 +Opc01: + TCall 0 + +;TCall 1 +ALIGN 16 +Opc11: + TCall 1 + +;TCall 2 +ALIGN 16 +Opc21: + TCall 2 + +;TCall 3 +ALIGN 16 +Opc31: + TCall 3 + +;TCall 4 +ALIGN 16 +Opc41: + TCall 4 + +;TCall 5 +ALIGN 16 +Opc51: + TCall 5 + +;TCall 6 +ALIGN 16 +Opc61: + TCall 6 + +;TCall 7 +ALIGN 16 +Opc71: + TCall 7 + +;TCall 8 +ALIGN 16 +Opc81: + TCall 8 + +;TCall 9 +ALIGN 16 +Opc91: + TCall 9 + +;TCall 10 +ALIGN 16 +OpcA1: + TCall 10 + +;TCall 11 +ALIGN 16 +OpcB1: + TCall 11 + +;TCall 12 +ALIGN 16 +OpcC1: + TCall 12 + +;TCall 13 +ALIGN 16 +OpcD1: + TCall 13 + +;TCall 14 +ALIGN 16 +OpcE1: + TCall 14 + +;TCall 15 +ALIGN 16 +OpcF1: + TCall 15 + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;TCLR1 - Test and Clear Bits with A +; N V P B H I Z C +; * * + +;TClr1 !abs +ALIGN 16 +Opc4E: + _abs + Mov CL,[ABSL] + And CL,A + XOr [ABSL],CL + _CleanUp 6,3,WA,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;TSET1 - Test and Set Bits with A +; N V P B H I Z C +; * * + +;TSet1 !abs +ALIGN 16 +Opc0E: + _abs + Mov CL,[ABSL] + Or [ABSL],A + And CL,A + _CleanUp 6,3,WA,NZ + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;XCN - Exchange Nybbles +; N V P B H I Z C +; * * + +;XCN A +ALIGN 16 +Opc9F: + RoR A,4 + Mov CL,A + _CleanUp 5,1,na,NZ + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Function Register Handlers +; +;These are specific functions to handle data written to the function registers (F0h to FFh) +;Upon entrance to each handler EDX == the function register number (0 - F) + +ALIGN 16 +FuncNext: +;This is a bit of code to handle 16-bit writes to the F0h - FFh range. +;During a 16-bit write, EBP is loaded with the address here, and the address of the handler for the +;low byte is pushed onto the stack. After handling the low byte, the emulator will jump here where +;EBP will be restored then control will be passed to the handler for the high byte. + +%if DEBUG + XOr EDX,EDX + Test byte [_Var(dbgOpt)],SPC_TRACE + SetNZ DL + Dec EDX + And EDX,SPCFetch-SPCTrace + LEA EBP,[EDX+SPCTrace] +%else + Mov EBP,SPCFetch +%endif + Pop EDX + _Profile(func16) + Inc EDX + Cmp EDX,10h + JE short .NoReg + Jmp [EDX*4+funcWTab] +.NoReg: + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Test (Write) +; Funky things happen when the test register is written to. Apparently the timers only work if +; bit 3 is set. Setting bit 2 or clearing bit 1 halts execution. Setting bits 7-4 slows down the +; processor by varying amounts. +; Reading from the register always returns 0 <verified by Blargg on 04.10.03> + +ALIGN 16 +Func0w: + _Profile(funcw) + Mov DL,[RAM+testReg] + And DL,0F6h + XOr DL,2 + JNZ OpcFF ;If bits 7-4,2-1 are modified, halt processor + Mov [RAM+testReg],DH ;Reset register to 0 + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Control (Write) +; Checks control register for ROM reading enable, port reset, and timer enable. + +ALIGN 16 +Func1w: + _Profile(funcw) + + Mov BL,[RAM+control] + + ;ROM access ------------------------------ + Mov DL,[_Var(oldCtrl)] + XOr DL,BL ;Did ROM access change? + JNS short .NoRA ; No + + Push ECX,ESI ;Save X and PC + LEA ESI,[_Var(extraRAM)] ;Setup registers to move data from Extra RAM to + Mov DI,ipl ; IPL region + + Test BL,BL ;Is ROM readable? + Mov ECX,10h + JNS short .NoRR ; No, Perform move as intended + XChg ESI,EDI ;Reverse original operation, move IPL to Extra RAM + Rep MovSD + + Mov CL,10h ;Setup registers to move ROM program into IPL region + LEA EDI,[ESI-40h] + Mov ESI,iplROM + .NoRR: + + Rep MovSD ;Move iplROM or extraRAM to IPL ROM region + + Mov EDI,[pAPURAM] + Pop ESI,ECX + + .NoRA: + + ;Clear ports ----------------------------- + Test BL,30h ;Was a clear ports command written? + JZ short .NoCP ; No + Mov EDX,EBX + Not EDX ;Reverse command bits + ShL EDX,26 ;Create a mask based on bits 5 & 4 + SAR EDX,15 + SAR DX,15 + And dword [RAM+port0],EDX ;Reset in-ports + And dword [_Var(inPortCp)],EDX + .NoCP: + + ;Reset/Enable timers --------------------- + MovZX EDX,byte [_Var(oldCtrl)] ;DL = Timers currently enabled + And BL,87h + Mov [_Var(oldCtrl)],BL ;Store new timers enabled + Mov [RAM+control],BL + + Not DL + And DL,DL ;CL=1 if new timer=1 and old timer=0 + And DL,7 + JZ short .NoTR ; Quit if no timers are reset + ShR DL,1 ;Do we need to reset timer 0? + JNC short .NoRT0 ; No + Mov BL,[_Var(timer0)] ;Get timer register + Mov [RAM+c0],DH ;Reset counter + Mov [_Var(t0Step)],BL ;Reset timer step + .NoRT0: + + ShR DL,1 + JNC short .NoRT1 + Mov BL,[_Var(timer1)] + Mov [RAM+c1],DH + Mov [_Var(t1Step)],BL + .NoRT1: + + ShR DL,1 + JNC short .NoRT2 + Mov BL,[_Var(timer2)] + Mov [RAM+c2],DH + Mov [_Var(t2Step)],BL + .NoRT2: + + ;Branch free method (no longer works since timer values have to be decreased) +; ShL EDX,29 ;EBX = abc00000000000000000000000000000 +; SAR EDX,7 ;EBX = aaaaaaaabc0000000000000000000000 +; ShR EDX,8 ;EBX = 00000000aaaaaaaabc00000000000000 +; SAR DX,7 ;EBX = 00000000aaaaaaaabbbbbbbbc0000000 +; SAR DL,7 ;EBX = 00000000aaaaaaaabbbbbbbbcccccccc +; Not EDX ;EBX = 11111111AAAAAAAABBBBBBBBCCCCCCCC +; +; And [t0Step],EDX ;Reset internal up counters +; And [RAM+c0],EDX ;Reset counters +; Not EDX +; And EDX,[RAM+t0] ;Get new timer values +; Or [t0Step],EDX ;Update internal up counters + .NoTR: + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Data (Write) +; Updates the DSP RAM with the value written to the data port, and calls the DSP emulator to +; handle the new data. + +ALIGN 16 +Func3w: + _Profile(funcw) + Mov BL,CL + Push EAX,EBX + MovZX EBX,byte [RAM+dspAddr] + +%if PROFILE + _ProfEnd + Mov EDX,EBX + _Profile(dspw) +%endif + + Mov AL,[RAM+dspData] + Call SetDSPReg_A + +%if PROFILE + MovSX EDX,byte [RAM+dspAddr] + Test EDX,EDX + JS short .NoWrite + Add [EDX*4+profile+Profile.dspu],EAX + .NoWrite: + + And EDX,7Fh ;Update value at F3h. If profiling is disabled, fall + Mov DL,[EDX+dsp] ; through and let Func2w handle it. + Mov [RAM+dspData],DL +%endif + + Pop EBX,EAX + Mov CL,BL + +%if PROFILE + _ProfStart + Jmp EBP +%endif + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Address (Write) +; Loads 0F3h with value indexed in DSP RAM. In the SNES, reads from registers 80-FF mirror +; registers 00-7F. + +ALIGN 16 +Func2w: + _Profile(funcw) + Mov DL,[RAM+dspAddr] ;EDX = DSP register + And EDX,7Fh ;The MSB of the address is ignored when getting data + Mov DL,[EDX+dsp] ;Get byte from DSP RAM + Mov [RAM+dspData],DL ;Store byte in DSP data reg + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Out Ports 0-3 (Write) +; Moves data to out port memory, and replaces register with in port value + +ALIGN 16 +Func4w: +Func5w: +Func6w: +Func7w: + _Profile(funcw) + And EDX,3 + Mov BL,[EDX+RAM+port0] ;Copy byte to out port + Mov [EDX+_Var(outPort)],BL + Mov BL,[EDX+_Var(inPortCp)] ;Replace byte in RAM with in port + Mov [EDX+RAM+port0],BL + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Unused (Write) +; No special handling is associated with registers 0F8h and 0F9h + +ALIGN 16 +Func8w: +Func9w: + _Profile(funcw) + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Timers 0-2 (Write) +; Reading the timer registers always returns 00 <verified by Blargg on 04.10.03> + +ALIGN 16 +FuncAw: +FuncBw: +FuncCw: + _Profile(funcw) + Mov BL,0 + XChg BL,[EDX+RAM+0F0h] + Dec BL + Mov [EDX+RAM-10h],BL + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Counters 0-2 (Write) +; Writing to a counter register clears it <verified by Blargg on 04.10.03> + +ALIGN 16 +FuncDw: +FuncEw: +FuncFw: + _Profile(funcw) + Mov [EDX+RAM+0F0h],DH + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;DSP Data (Read) +; Break emulation of the SPC700 so the DSP can emulate, if the DSP is being read from. Breaking +; only happens on registers that get updated by the DSP (ENVX, OUTX, and ENDX). + +ALIGN 16 +Func3r: +%if PROFILE + _Profile(funcr) + Mov DL,[RAM+dspAddr] + And EDX,7Fh + _Profile(dspr) +%endif + +%if DSPBK + +%if PROFILE = 0 + Mov DL,[RAM+dspAddr] + And DL,7Fh +%endif + + Cmp DL,7Ch ;Is the register being read ENDX? + JE short .Break + And DL,0Fh + Sub DL,8 ;ENVX or OUTX? + Cmp DL,1 + JA short .NoBreak + .Break: + +%if DSPINTEG + +%if PROFILE + Inc dword [4+profile+Profile.update] + _ProfEnd + Call UpdateDSP + _ProfStart +%else + Push EBP ;Push return address + Jmp UpdateDSP +%endif ;PROFILE + +%else + Push EAX + Mov EBP,SPCExit + Mov EAX,[_Var(clkLeft)] + Add [_Var(clkExec)],EAX + Sub [_Var(clkLeft)],EAX + Pop EAX +%endif ;DSPINTEG + + .NoBreak: +%endif ;DSPBK + + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +; Reading from most registers doesn't do anything + +ALIGN 16 +Func0r: +Func1r: +Func2r: +Func4r: +Func5r: +Func6r: +Func7r: +Func8r: +Func9r: +FuncAr: +FuncBr: +FuncCr: + _Profile(funcr) + Jmp EBP + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Counters 0-2 (Read) +; If the speed hack is enabled, this skips emulated cycles so the next counter to increase will +; increase on the next 64kHz pulse. For games that poll the counters waiting for an increase, this +; guarantees the program will never have to poll the counters more than three consecutive times, +; which increases emulation speed. However, somewhere in this code is a flaw in my logic. For +; some songs the speed hack is buring cycles without the music advancing, and for today's >1GHz +; processors the performance increase is negligible. + +ALIGN 16 +FuncDr: +FuncEr: +FuncFr: + _Profile(funcr) +%if SPEED + Or DL,0F0h + Test byte [EDX+EDI],0Fh ;Is counter > 0? + Mov [EDX+EDI],DH ;Reset counter + JNZ .Quit ; Yes, No need to speed up then + + Test byte [_Var(oldCtrl)],7 ;Are any timers enabled? + JZ .Quit + + Push EAX,ECX + + ;Some poorly coded games check the counters and keep a running total of the increases while + ;processing the song data. If the counters are increased during each read, the counter + ;increases will accumulate quickly and the song will play at warp speed. + ; + ;To overcome this, if less than 64 CPU cycles go by between counter reads, we'll assume that + ;the program is polling the counters, otherwise we'll disable the speed hack. + + Mov ECX,[_Var(t64Last)] + Mov EAX,[_Var(t64Cnt)] + Add ECX,(64 * CPU_CYC) / T64_CYC ;Add 64 CPU cycles to the last count + Mov [_Var(t64Last)],EAX + Cmp EAX,ECX + JAE .NoHack ;If at least 64 CPU cycles have passed, quit + + ;Since we don't know which timers the program is using, the hack needs to advance the next + ;closest counter, regardless of which counter is actually being read. So the first step is + ;to figure out how many 64kHz pulses are needed for each counter to increase, then choose the + ;smallest value. + + MovZX EAX,byte [_Var(oldCtrl)] ;Determine number of 64kHz pulses until counter 0 or 1 + XOr ECX,ECX ; increases, whichever is less. + Test AL,2 + SetNZ CL + And AL,1 + Dec CL + Dec AL ;If the timer is disabled, default to max time + Or CL,[_Var(t1Step)] ;ECX = (control & 2) ? t1Step : 0FFh + Or AL,[_Var(t0Step)] ;EAX = (control & 1) ? t0Step : 0FFh + + Sub EAX,ECX ;if (EAX >= ECX) EAX = ECX + CDQ + And EAX,EDX + MovZX EDX,byte [_Var(t8kHz)] + Add EAX,ECX + LEA EAX,[EAX*8+EDX] ;EAX = EAX * 8 + t8kHz + + Test byte [_Var(oldCtrl)],4 ;ECX = (control & 4) ? t2Step : 0FFh + SetNZ CL + Dec CL + Or CL,[_Var(t2Step)] + + Sub EAX,ECX ;Does counter 0 or 1 have less time than counter 2? + CDQ + And EAX,EDX + Add EAX,ECX + JZ .NoHack ;No pulses to increase, so quit + + ;Once we've figured out the least number of cycles needed for a counter increase, we need to + ;make sure we have enough total cycles left to emulate. Then make sure we have enough cycles + ;to increase the 64kHz timer. + + LEA EAX,[EAX*3] ;Convert EAX to clock cycles + Mov ECX,[_Var(clkTotal)] + ShL EAX,7 + Sub ECX,[_Var(clkExec)] + Add ECX,[_Var(clkLeft)] ;ECX = clock cycles left to emulate + + Sub EAX,ECX ;if (EAX >= ECX) EAX = ECX + CDQ + And EAX,EDX + Add EAX,ECX + + Cmp EAX,T64_CYC ;Are there enough cycles for a 64kHz pulse? + JB short .NoHack ; No, Quit since nothing will be modified + + ;Now's the time to actually skip emulating clock cycles. The number of pulses are added to + ;t64Cnt and the cycles added to clkTotal, as if they really were emulated. If timer 2 is + ;enabled, the pulses are then subtracted from t2Step. Next the number of pulses is divided by 8 + ;to give us the number of 8kHz pulses to skip. Since up to 7 64kHz pulses could occur before one + ;8kHz pulse, the remainder is subtracted from t8kHz. If timers 1 and 0 are enabled, pulses are + ;subtracted from t1Step and t0Step respectively. + + XOr EDX,EDX ;Convert clock cycles back into 64kHz pulses, in case + Mov ECX,T64_CYC ; the value changed. + Div ECX + Add [_Var(t64Cnt)],EAX ;Add pulses to 64kHz counter + + LEA EDX,[EAX*3] ;clkTotal -= EAX * 384 + ShL EDX,7 + Mov CL,[_Var(oldCtrl)] + Sub [_Var(clkTotal)],EDX + + Test CL,4 ;if (control & 4) t2Step -= AL + SetZ DL + Dec DL + And DL,AL ;DL = (control & 4) ? AL : 0 + Sub [_Var(t2Step)],DL + + ShL EAX,5 ;AH = EAX / 8 (number of 8kHz ticks to skip) + Test CL,2 + SetZ DL + And CL,1 + ShR AL,5 ;AL = EAX % 8 (num 64kHz ticks left until 8kHz tick) + XOr CL,1 + Dec DL + Dec CL + + Sub [_Var(t8kHz)],AL ;t8kHz -= EAX % 8 + AdC AH,0 + And DL,AH ;DL = (control & 2) ? (EAX / 8) : 0 + And CL,AH ;CL = (control & 2) ? (EAX / 8) : 0 + And byte [_Var(t8kHz)],7 + Sub [_Var(t1Step)],DL + Sub [_Var(t0Step)],CL + + .NoHack: + Pop ECX,EAX + + .Quit: +%else + Mov [EDX+RAM+0F0h],DH ;Reset counter +%endif + Jmp EBP
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/SPC700.Inc b/lib/snesapu/SNES/SNESAPU/SPC700.Inc new file mode 100644 index 0000000000..a6b19fc81c --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SPC700.Inc @@ -0,0 +1,366 @@ +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Program: Sony SPC700 Emulator +;Platform: 80386 +;Programmer: Anti Resonance +; +;Thanks to Michael Abrash. It was reading the Graphics Programming Black Book that gave me the +;idea and inspiration to write this in the first place. +; +;This library is free software; you can redistribute it and/or modify it under the terms of the +;GNU Lesser General Public License as published by the Free Software Foundation; either version +;2.1 of the License, or (at your option) any later version. +; +;This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +;without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +;See the GNU Lesser General Public License for more details. +; +;You should have received a copy of the GNU Lesser General Public License along with this +;library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, +;Boston, MA 02111-1307 USA +; +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Revision History: +; +; 2005.10.28 SNESAPU v3.0 +; + Updated EmuSPC to use a jump table to execute opcodes :( +; + Removed InPortW +; + Added GetAPUPort +; + The memory address macros were getting the RAM pointer from pAPURAM instead of EDI +; + Macro _mbit now returns a bit instead of a memory location +; - IPL ROM wasn't getting copied to RAM on reset (don't know how I missed this one) +; - Fixed InitSPC and ResetSPC so function registers are properly preserved +; - Timer values are stored internally so reading FA-FC returns 0 +; - Rewrote speed hack (now it's faster and doesn't update disabled timers) +; + All global variables are now stored relative to EDI +; + Common cleanup configurations are now jumped to instead of being expanded within the opcode +; + CL contains the result of the previous operation and is used to determine the values of N and Z. +; Faster than setting the flags in memory. +; + Post read check for function registers now operates like the post write +; + Added profiling counters +; +; 2004.08.01 SNESAPU v2.1 +; + Moved all checks for SPC_NODSP into DSP.Asm +; +; 2004.01.26 SNESAPU v2.0 +; - Added code to call DSP emulator if 00F3 is read from +; - Updated opcode fetch code to work with new DSP syncronization +; +; 2003.06.30 +; - Fixed a bug where the counter would increase every pulse if the timer was disabled and set to 0 +; +; 2003.02.10 SNESAPU v0.98 +; + Added SaveSPC, RestoreSPC, and SetAPURAM +; + Coded support for BRK (still not sure if it's correct) +; - Rewrote SetSPCDbg and the opcode fetcher to fix some potential bugs +; - Added code to CntHack to try and isolate small polling loops (fixes the tempo problems some +; games were having) +; - Changed FixSPC, again, to copy extraRAM from APURAM[0FFC0h] if IPL reading is disabled in F1h +; (fixes Tales of Phantasia) +; - Fixed a lame bug when clearing the in-ports with F1h (fixes Dragon Quest) +; +; 2001.04.12 SNESAPU v0.95 +; + Added SPC_NODSP to the debugging options +; - Fixed some bugs with copying the IPL ROM and detecting reading ability +; +; 2001.01.29 SNESAmp v2.1 +; - Changed internal clock to 1.024MHz (I haven't noticed any differences, yet) +; +; 2001.01.01 SNESAmp v2.0 +; + Added an external counter to the 64kHz timer +; + Emulator is now based off of a 24.576MHz clock, so implementation of a 2.048 or 2.457MHz CPU +; clock will be easier internally and transparent externally +; + Added speed hack to SLEEP +; + Added option flags to SPCDebug to allow greater control over the SPC700 state +; + Added breaking ability to STOP in non-debug builds +; - In ports don't get cleared on load, regardless of setting in F1 +; - Speed hack is now implemented properly +; - PC is now operated on as a 16-bit quantity (Rudra no Hihou expects PC to roll over, which it +; wasn't doing when instructions were adding to ESI instead of SI) +; - Changed coding style a bit to make code easier to read and added more comments +; +; 2000.05.04 SNESAmp v1.2 +; - Control register is now set to 80h on reset +; +; 2000.04.04 SNESAmp v1.0 +; + Rearranged code so static branch prediction would be more accurate +; + Clock cycles to emulate is passed on the stack to EmuSPC +; + SPC700 registers are passed on the stack to the external debug routine +; + Eliminated ORG directive and rewrote code to conform to MASM syntax +; - Fixed some implementation bugs with the speed hack +; - Fixed a bug in the 16-bit WrPost macro, again +; +; 2000.03.17 SNESAmp v0.9 +; + Rewrote for compatibility in a 32-bit C environment +; + Added external functions to write to ports +; - SLEEP wasn't erasing the port status on the first time through +; +; 2000.02.22 SPC Tool v0.6 +; - Fixed a bug in the 16-bit WrPost macro +; +; 2000.02.01 SPC Tool v0.51 +; + Added a hack to eliminate polling the counters +; + Aligned many of the short jump destinations on paragraph boundaries +; + Rewrote the opcode header macro to compile into table form instead of packed form. +; (The compiled code is four times bigger, but it's now easier to align, port, and debug.) +; + Store flags as dwords so accesses to the DP don't cause a partial register stall +; - Fixed PCALL, STOP, and SLEEP +; +; 2000.01.21 SPC Tool v0.5 +; + Reprogrammed PSW handling for faster entry/exit +; +; 1999.12.27 SPC Tool v0.2 +; Copyright (C)1999-2006 Alpha-II Productions +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + +%define SPC700_INC + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Equates + +APU_CLK EQU 24576000 ;Number of clock cycles in one second + +;SPC700 debugging options ------------------- +SPC_HALT EQU 80h ;Halt emulation +SPC_TRACE EQU 40h ;Trace instruction execution (used internally) +SPC_STOP EQU 01h ;Only break on STOP instruction + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Structures + +STRUC SPCState + .pRAM resd 1 ;[0.0] -> APU's RAM (64k) + .pc resw 1 ;[0.4] Registers + .a resb 1 + .y resb 1 + .x resb 1 + .psw resb 1 + .sp resw 1 + .outP resb 4 ;[0.C] Out ports + + .upCnt resb 3 ;[1.0] Up counters for counter increase + .t8kHz resb 1 ;[1.3] # of cycles left until timer increase + .t64kHz resd 1 ;[1.4] # of cycles left until timer increase + .t64Cnt resd 1 ;[1.8] # of 64kHz ticks since emulation began + ._r1 resd 1 ;[1.C] reserved +ENDSTRUC + +STRUC Profile + .exec resd 256 ;Instruction executed + .bxx resd 8 ;Branch taken on BPL,BMI,BVC,BVS,BCC,BCS,BNE,BEQ + .bbc resd 8 ;Branch taken on BBC.bit + .bbs resd 8 ;Branch taken on BBS.bit + .cbne resd 2 ;Branch taken on CBNE dp,dp+X + .dbnz resd 2 ;Branch taken on DBNZ Y,dp + .funcr resd 16 ;Function register read + .funcw resd 16 ;Function register write + .func16 resd 16 ;Function register write via MOVW, INCW, or DECW + .dspr resd 128 ;DSP register read + .dspw resd 256 ;DSP register write + .dspu resd 128 ;DSP register write that affected the DSP state + .update resd 4 + .hostTSC resq 2 + .apuTSC resq 2 ;Time spent in EmuAPU + .spcTSC resq 2 ;Time spent in EmuSPC + .dspTSC resq 2 ;Time spent in EmuDSP + .sizeof +ENDSTRUC + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Public Variables + +PUBLIC pAPURAM ;Pointer to 64KB of APU RAM +PUBLIC profile ;Profiling counters + + +;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +;Exported Functions + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Initialize SPC700 +; +;This function is a remnant from the 16-bit assembly when dynamic code reallocation was used. +;Now it just initializes internal pointers. +; +;Out: +; EAX -> 64KB APU RAM + +PUBLIC InitSPC, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Reset SPC700 +; +;Sets all user memory to FF, resets the SPC700 execution time, resets the halt flag, and copies ROM +;into the IPL region +; +;Destroys: +; EAX + +PUBLIC ResetSPC, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Debug SPC700 +; +;Installs a callback that gets called before an instruction is executed. The function is called +;with the C calling convention. Non-pointer values are zero extended. +; +;Upon entrance to the function: +; [ESP+4] -> Current opcode (low word = PC) +; [ESP+8] = YA +; [ESP+12] = X +; [ESP+16] = PSW +; [ESP+20]-> Current stack (low word = SP) +; [ESP+24] = Clock cycle down count +; [0-1] 8kHz cycles left until counters 0 and 1 increase +; [2] 64kHz cycles left until counter 2 increases +; [3] CPU cycles left until 64kHz clock pulse +; +;Note: +; pTrace is always called when a STOP instruction is encountered, regardless of if the DEBUG +; option is set. DEBUG must be set to 1 in APU.Inc for pTrace to be called under other +; circumstances. +; +;In: +; pTrace-> Debug function (NULL turns off the debug call, -1 leaves the current callback) +; opts = SPC700 debugging options (-1 leaves the current options) +; +;Out: +; Previously installed callback + +PUBLIC SetSPCDbg, pTrace:ptr, opts:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Fix SPC700 After Loading SPC File +; +;Loads timer steps with the values in the timer registers, resets the counters, sets up the in/out +;ports, and stores the registers. +; +;In: +; SPC internal registers +; +;Destroys: +; EAX + +PUBLIC FixSPCLoad, pc:word, a:byte, y:byte, x:byte, psw:byte, sp:byte + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Save SPC700 State +; +;Note: +; Pointers in the SPCState structure can be NULL +; +;In: +; pState -> Saved state structure +; +;Destroys: +; EAX + +PUBLIC SaveSPC, pState:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Restore SPC700 State +; +;Note: +; Setting a pointer in SPCState to NULL will keep its corresponding internal data intact. +; +;In: +; pState -> Saved state structure +; +;Destroys: +; EAX + +PUBLIC RestoreSPC, pState:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Write to APU RAM via the SPC700 +; +;Writes a value to APU RAM via the SPC700. Use this instead of writing to RAM directly so any +;changes made by writing to function registers or the IPL region will be handled properly. +; +;In: +; addr = Address to write to (only the lower 16-bits are used) +; val = Value to write +; +;Destroys: +; EAX + +PUBLIC SetAPURAM, addr:dword, val:byte + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Set APU Port +; +;Writes a value to APU RAM via the APU in ports. This emulates writing to PPU registers 2140-2143h +;via the 65816. +; +;To change the APU out ports, write to function registers F4-F7h with SetAPURAM. +; +;In: +; addr = Port on which to write (only the lower 2-bits are used) +; val = Value to write +; +;Destroys: +; EAX + +PUBLIC SetAPUPort, addr:dword, val:byte + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get APU Port +; +;Reads the value stored in the APU out ports. This emulates reading from PPU registers 2140-2143h +;via the 65816. +; +;In: +; addr = Port to read value from (only the lower 2-bits are used) +; +;Out: +; EAX = Value of port + +PUBLIC GetAPUPort, addr:dword + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Get SPC700 Time +; +;Returns the number of 64kHz ticks that have gone by since the SPC700 was reset +; +;Out: +; EAX = Number of 64kHz cycles that have elapsed + +PUBLIC GetSPCTime, NULL + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Profile Timers +; +;In: +; tsc -> Two qwords +; +;Destroys: +; EDX + +PUBLIC StartAPUProfile, tsc:ptr +PUBLIC EndAPUProfile, tsc:ptr + + +;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +;Emulate SPC700 +; +;Emulates the SPC700 for the number of clock cycles specified or until a break condition is met, +;whichever happens first. (see the compile options CNTBK and DSPBK in APU.Inc) +; +;In: +; cyc = Number of 24.576MHz clock cycles to execute (signed, must be > 0) +; +;Out: +; EAX = Clock cycles left to execute (negative if more cycles than specified were emulated) + +PUBLIC EmuSPC, cyc:dword
\ No newline at end of file diff --git a/lib/snesapu/SNES/SNESAPU/SPC700.h b/lib/snesapu/SNES/SNESAPU/SPC700.h new file mode 100644 index 0000000000..821b26acd4 --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/SPC700.h @@ -0,0 +1,437 @@ +/*************************************************************************************************** +* Program: Sony SPC700 Emulator * +* Platform: 80386 * +* Programmer: Anti Resonance * +* * +* Thanks to Michael Abrash. It was reading the Graphics Programming Black Book that gave me the * +* idea and inspiration to write this in the first place. * +* * +* This library is free software; you can redistribute it and/or modify it under the terms of the * +* GNU Lesser General Public License as published by the Free Software Foundation; either version * +* 2.1 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * +* See the GNU Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License along with this * +* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * +* Boston, MA 02111-1307 USA * +* * +* ------------------------------------------------------------------------------------------------ * +* Revision History: * +* * +* 2005.xx.xx SNESAPU v3.0 * +* + Updated EmuSPC to use a jump table to execute opcodes :( * +* + Removed InPortW * +* + Added GetAPUPort * +* + The memory address macros were getting the RAM pointer from pAPURAM instead of EDI * +* + Macro _mbit now returns a bit instead of a memory location * +* - Correcly moved the placement of volatile in the SPCDebug typedef * +* - IPL ROM wasn't getting copied to RAM on reset (don't know how I missed this one) * +* - Fixed InitSPC and ResetSPC so function registers are properly preserved * +* - Timer values are stored internally so reading FA-FC returns 0 * +* - Rewrote speed hack (now it's faster and doesn't update disabled timers) * +* + All global variables are now stored relative to EDI * +* + Common cleanup configurations are now jumped to instead of being expanded within the opcode * +* + CL contains the result of the previous operation and is used to determine the values of * +* N and Z. Faster than setting the flags in memory. * +* + Post read check for function registers now operates like the post write * +* + Added profiling counters * +* * +* 2004.08.01 SNESAPU v2.1 * +* + Moved all checks for SPC_NODSP into DSP.Asm * +* * +* 2004.01.26 SNESAPU v2.0 * +* - Added code to call DSP emulator if 00F3 is read from * +* - Updated opcode fetch code to work with new DSP syncronization * +* * +* 2003.06.30 * +* - Fixed a bug where the counter would increase every pulse if the timer was disabled and set * +* to 0 * +* * +* 2003.02.10 SNESAPU v0.98 * +* + Added SaveSPC, RestoreSPC, and SetAPURAM * +* + Coded support for BRK (still not sure if it's correct) * +* - Rewrote SetSPCDbg and the opcode fetcher to fix some potential bugs * +* - Added code to CntHack to try and isolate small polling loops (fixes the tempo problems some * +* games were having) * +* - Changed FixSPC, again, to copy extraRAM from APURAM[0FFC0h] if IPL reading is disabled in F1h * +* (fixes Tales of Phantasia) * +* - Fixed a lame bug when clearing the in-ports with F1h (fixes Dragon Quest) * +* * +* 2001.04.12 SNESAPU v0.95 * +* + Added SPC_NODSP to the debugging options * +* - Fixed some bugs with copying the IPL ROM and detecting reading ability * +* * +* 2001.01.29 SNESAmp v2.1 * +* - Changed internal clock to 1.024MHz (I haven't noticed any differences, yet) * +* * +* 2001.01.01 SNESAmp v2.0 * +* + Added an external counter to the 64kHz timer * +* + Emulator is now based off of a 24.576MHz clock, so implementation of a 2.048 or 2.457MHz CPU * +* clock will be easier internally and transparent externally * +* + Added speed hack to SLEEP * +* + Added option flags to SPCDebug to allow greater control over the SPC700 state * +* + Added breaking ability to STOP in non-debug builds * +* - In ports don't get cleared on load, regardless of setting in F1 * +* - Speed hack is now implemented properly * +* - PC is now operated on as a 16-bit quantity (Rudra no Hihou expects PC to roll over, which it * +* wasn't doing when instructions were adding to ESI instead of SI) * +* - Changed coding style a bit to make code easier to read and added more comments * +* * +* 2000.05.04 SNESAmp v1.2 * +* - Control register is now set to 80h on reset * +* * +* 2000.04.04 SNESAmp v1.0 * +* + Rearranged code so static branch prediction would be more accurate * +* + Clock cycles to emulate is passed on the stack to EmuSPC * +* + SPC700 registers are passed on the stack to the external debug routine * +* + Eliminated ORG directive and rewrote code to conform to MASM syntax * +* - Fixed some implementation bugs with the speed hack * +* - Fixed a bug in the 16-bit WrPost macro, again * +* * +* 2000.03.17 SNESAmp v0.9 * +* + Rewrote for compatibility in a 32-bit C environment * +* + Added external functions to write to ports * +* - SLEEP wasn't erasing the port status on the first time through * +* * +* 2000.02.22 SPC Tool v0.6 * +* - Fixed a bug in the 16-bit WrPost macro * +* * +* 2000.02.01 SPC Tool v0.51 * +* + Added a hack to eliminate polling the counters * +* + Aligned many of the short jump destinations on paragraph boundaries * +* + Rewrote the opcode header macro to compile into table form instead of packed form. * +* (The compiled code is four times bigger, but it's now easier to align, port, and debug.) * +* + Store flags as dwords so accesses to the DP don't cause a partial register stall * +* - Fixed PCALL, STOP, and SLEEP * +* * +* 2000.01.21 SPC Tool v0.5 * +* + Reprogrammed PSW handling for faster entry/exit * +* * +* 1999.12.27 SPC Tool v0.2 * +* Copyright (C)1999-2006 Alpha-II Productions * +***************************************************************************************************/ + +namespace A2 +{ +namespace SNES +{ + + //********************************************************************************************** + // Defines + + static const s32 APU_CLK = 24576000; //Number of clock cycles in one second + + //SPC700 debugging options ----------------- + + enum SPCDbgOpts + { + // Halt SPC700: + // + // This flag causes EmuSPC() to return that cycles were emulated, but nothing will have + // happened. When set from within the debugging callback, EmuSPC() will update the internal + // registers then return before executing the next instruction. + // + // Executing a STOP or SLEEP instruction automatically sets this option. + + SPC_HALT = 7, + + // Only on STOP: + // + // By default, the debugging callback will be called before each instruction is executed. + // If you only want the callback to get called when a STOP or SLEEP instruction is + // encountered, specify this flag. + + SPC_STOP = 0 + }; + + + //********************************************************************************************** + // Structures + + //Saved state ------------------------------ + struct PSW + { + u8 c:1; //Carry + u8 z:1; //Zero + u8 i:1; //Interrupts Enabled (not used in the SNES) + u8 h:1; //Half-Carry (auxiliary) + u8 b:1; //Software Break + u8 p:1; //Direct Page Selector + u8 v:1; //Overflow + u8 n:1; //Negative (sign) + }; + + struct SPCState + { + void* pRAM; //[0.0] -> APU's RAM (64k) + u16 pc; //[0.4] Registers + u8 a,y,x; + PSW psw; + u16 sp; + u8 outp[4]; //[0.C] Out ports + + u8 upCnt[3]; //[1.0] Up counters for counter increase + u8 t8kHz; //[1.3] # of cycles left until 8kHz timer increase + u32 t64kHz; //[1.4] # of cycles left until 64kHz timer increase + u32 t64Cnt; //[1.8] # of 64kHz ticks since emulation began + u32 _r1; //[1.C] + }; + + + //********************************************************************************************** + // SPC700 Debugging Routine + // + // A prototype for a function that gets called for debugging purposes + // + // The paramaters passed in can be modified, and on return will be used to update the internal + // registers (except 'cnt'). Non-pointer values are zero extended. + // + // Note: + // When modifying PC or SP, only the lower word or byte needs to be modified, respectively; + // the upper bytes will be ignored. + // + // In: + // pc -> Current opcode (LOWORD = PC) + // ya = YA + // x = X + // psw = PSW + // sp -> Current stack byte (LOBYTE = SP) + // cnt = Clock cycle counters (four counters, one in each byte) + // [0-1] 8kHz up counters for timers 0 and 1 + // [2] 64kHz up counter for timer 2 + // [3] CPU cycles left until next 64kHz clock pulse + + typedef void SPCDebug(u8* volatile pc, volatile u16 ya, volatile u8 x, volatile PSW psw, + u8* volatile sp, volatile u32 cnt); + + +#if !defined(SNESAPU_DLL) || defined(DONT_IMPORT_SNESAPU) + + //////////////////////////////////////////////////////////////////////////////////////////////// + // Private Declarations + + extern "C" u8* pAPURAM; //Pointer to 64KB of APU RAM + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // External Functions + +extern "C" { + + //********************************************************************************************** + // Initialize SPC700 + // + // This function is a remnant from the 16-bit assembly when dynamic code reallocation was used. + // Now it just initializes internal pointers. + // + // Note: + // You should call InitAPU() instead + // + // Thread safe: + // No + // + // out: + // -> 64KB APU RAM + + u8* InitSPC(); + + + //********************************************************************************************** + // Reset SPC700 + // + // Sets all user memory to FF, resets the SPC700 execution time, resets the halt flag, and + // copies ROM into the IPL region + // + // Note: + // You should call ResetAPU() instead + // + // Thread safe: + // No + // + // Destroys: + // EAX + + void ResetSPC(); + + + //********************************************************************************************** + // Set SPC700 Debugging Routine + // + // Installs a callback that gets called before an instruction is executed, provided SPC700.Asm + // was compiled with the SA_DEBUG option set. + // + // This function is not safe to call during emulation from another thread, but it may be called + // from within the pDebug function. + // + // Note: + // pDebug is always called when a STOP or SLEEP instruction is encountered, regardless of if + // the SA_DEBUG option is set. + // + // Thread safe: + // No + // + // In: + // pDebug-> Debugging callback (NULL can be passed to disable the callback, -1 leaves the + // current callback in place) + // opts = SPC700 debugging flags (see SPCDbgOpts, Set::All() leaves the current flags) + // + // Out: + // Previously installed callback + + SPCDebug* SetSPCDbg(SPCDebug* pDebug, Set<SPCDbgOpts> opts = Set<SPCDbgOpts>()); + + + //********************************************************************************************** + // Fix SPC700 After Loading SPC File + // + // Loads timer steps with the values in the timer registers, resets the counters, sets up the + // in/out ports, and stores the registers. + // + // Note: + // You should call FixAPU() instead + // + // Thread safe: + // No + // + // In: + // SPC internal registers + // + // Destroys: + // EAX + + void FixSPCLoad(u16 pc, u8 a, u8 y, u8 x, u8 psw, u8 sp); + + + //********************************************************************************************** + // Save SPC700 State + // + // Note: + // Pointers in the SPCState structure can be NULL + // + // Thread safe: + // No + // + // In: + // pState -> Saved state structure + // + // Destroys: + // EAX + + void SaveSPC(SPCState* pState); + + + //********************************************************************************************** + // Restore SPC700 State + // + // Note: + // Setting a pointer in SPCState to NULL will keep its corresponding internal data intact. + // + // Thread safe: + // No + // + // In: + // state -> Saved state structure + // + // Destroys: + // EAX + + void RestoreSPC(const SPCState& state); + + + //********************************************************************************************** + // Write to APU RAM via the SPC700 + // + // Writes a value to APU RAM via the SPC700. Use this instead of writing to RAM directly so any + // changes made by writing to function registers or the IPL region will be handled properly. + // + // Thread safe: + // No + // + // In: + // addr = Address to write to (only the lower 16-bits are used) + // val = Value to write + // + // Destroys: + // EAX + + void SetAPURAM(u32 addr, u8 val); + + + //********************************************************************************************** + // Set APU Port + // + // Writes a value to APU RAM via the APU in ports. This emulates writing to PPU registers + // 2140-2143h via the 65816. + // + // To change the APU out ports, write to function registers F4-F7h with SetAPURAM(). + // + // Thread safe: + // No + // + // In: + // port = Port on which to write (only the lower 2-bits are used) + // val = Value to write + // + // Destroys: + // EAX + + void SetAPUPort(u32 addr, u8 val); + + + //********************************************************************************************** + // Get APU Port + // + // Reads the value stored in the APU out ports. This emulates reading from PPU registers 2140-2143h + // via the 65816. + // + // Thread safe: + // Yes + // + // In: + // addr = Port to read value from (only the lower 2-bits are used) + + u8 GetAPUPort(u32 addr); + + + //********************************************************************************************** + // Get SPC700 Time + // + // Returns the number of 64kHz ticks that have gone by since the SPC700 was reset + // + // Thread safe: + // Yes + // + // Out: + // Number of 64kHz cycles that have elapsed + + u32 GetSPCTime(); + + + //********************************************************************************************** + // Emulate SPC700 + // + // Emulates the SPC700 for the number of clock cycles specified or until a break condition is + // met, whichever happens first. (see compile options SA_CNTBK and SA_DSPBK) + // + // Notes: + // You should call EmuAPU() instead + // Passing values <= 0 will cause undeterminable results + // + // In: + // cyc = Number of 24.576MHz clock cycles to execute (must be > 0) + // + // Out: + // Clock cycles left to execute (negative if more cycles than specified were emulated) + + s32 EmuSPC(s32 cyc); + +} // extern "C" + +#endif //!SNESAPU_DLL + +} // namespace SNES +} // namespace A2 + diff --git a/lib/snesapu/SNES/SNESAPU/Version.rc b/lib/snesapu/SNES/SNESAPU/Version.rc new file mode 100644 index 0000000000..d8f692ad3c --- /dev/null +++ b/lib/snesapu/SNES/SNESAPU/Version.rc @@ -0,0 +1,121 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Japanese resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) +#ifdef _WIN32 +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT +#pragma code_page(932) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Japanese resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,0,0,0 + PRODUCTVERSION 3,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", "Sony SPC700 and DSP emulator library\0" + VALUE "CompanyName", "Alpha-II Productions http://www.alpha-ii.com\0" + VALUE "FileDescription", "Super NES APU Emulator\0" + VALUE "FileVersion", "3, 0, 0, 0\0" + VALUE "InternalName", "SNESAPU\0" + VALUE "LegalCopyright", "Copyright ©2001-2005 Alpha-II Productions\0" + VALUE "LegalTrademarks", "Super NES is a registered trademark of Nintendo\0" + VALUE "OriginalFilename", "SNESAPU.DLL\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "SNESAPU\0" + VALUE "ProductVersion", "3, 0, 0, 0\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + +#endif // !_MAC + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/lib/snesapu/Types.h b/lib/snesapu/Types.h new file mode 100644 index 0000000000..008ba36821 --- /dev/null +++ b/lib/snesapu/Types.h @@ -0,0 +1,262 @@ +//************************************************************************************************** +// Type Redefinitions +#ifndef SPC_TYPES_H +#define SPC_TYPES_H + +/* + * Copyright (C) 2005-2008 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +typedef void v0; + +#ifdef __cplusplus +#if defined __BORLANDC__ +typedef bool b8; +#else +typedef unsigned char b8; +#endif +#else +typedef char b8; +#endif + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +#if defined _MSC_VER || defined __BORLANDC__ +typedef unsigned __int64 u64; +#else +typedef unsigned long long int u64; +#endif + +typedef char s8; +typedef short s16; +typedef int s32; +#if defined _MSC_VER || defined __BORLANDC__ +typedef __int64 s64; +#else +typedef long long int s64; +#endif + +typedef float f32; +typedef double f64; +typedef long double f80; + +typedef struct DSPVoice +{ + s8 volL; //Volume Left + s8 volR; //Volume Right + u16 pitch; //Pitch (rate/32000) (3.11) + u8 srcn; //Sound source being played back + u8 adsr[2]; //Envelope rates for attack, decay, and sustain + u8 gain; //Envelope gain (if not using ADSR) + s8 envx; //Current envelope height (.7) + s8 outx; //Current sample being output (-.7) + s8 __r[6]; +} DSPVoice; + +typedef struct SPCState +{ + void *pRAM; //[0.0] -> APU's RAM (64k) + u32 _r1; //[0.4] reserved + u16 pc; //[0.8] Registers + u8 a,y,x; + u8 psw; + u16 sp; + + u32 t8kHz,t64kHz; //[1.0] # of cycles left until timer increase + u32 t64Cnt; //[1.8] # of 64kHz ticks since emulation began + u32 _r2; //[1.C] reserved + u8 upCnt[3]; //[2.0] Up counters for counter increase + u8 portMod; //[2.3] Flags for in ports that have been modified + u8 outp[4]; //[2.4] Out ports + u32 _r3[2]; //[2.8] reserved +} SPCState; + +typedef struct DSPFIR +{ + s8 __r[15]; + s8 c; //Filter coefficient +} DSPFIR; + +typedef union DSPReg +{ + DSPVoice voice[8]; //Voice registers + + struct //Global registers + { + s8 __r00[12]; + s8 mvolL; //Main Volume Left (-.7) + s8 efb; //Echo Feedback (-.7) + s8 __r0E; + s8 c0; //FIR filter coefficent (-.7) + + s8 __r10[12]; + s8 mvolR; //Main Volume Right (-.7) + s8 __r1D; + s8 __r1E; + s8 c1; + + s8 __r20[12]; + s8 evolL; //Echo Volume Left (-.7) + u8 pmon; //Pitch Modulation on/off for each voice + s8 __r2E; + s8 c2; + + s8 __r30[12]; + s8 evolR; //Echo Volume Right (-.7) + u8 non; //Noise output on/off for each voice + s8 __r3E; + s8 c3; + + s8 __r40[12]; + u8 kon; //Key On for each voice + u8 eon; //Echo on/off for each voice + s8 __r4E; + s8 c4; + + s8 __r50[12]; + u8 kof; //Key Off for each voice (instantiates release mode) + u8 dir; //Page containing source directory (wave table offsets) + s8 __r5E; + s8 c5; + + s8 __r60[12]; + u8 flg; //DSP flags and noise frequency + u8 esa; //Starting page used to store echo waveform + s8 __r6E; + s8 c6; + + s8 __r70[12]; + u8 endx; //Waveform has ended + u8 edl; //Echo Delay in ms >> 4 + s8 __r7E; + s8 c7; + } u; + + DSPFIR fir[8]; //FIR filter + + u8 reg[128]; +} DSPReg; + +typedef struct Voice +{ + //Voice -----------08 + DSPVoice *pVoice; //-> voice registers in DSP + u32 _r; + //Waveform --------06 + void *bCur; //-> current block + u8 bHdr; //Block Header for current block + u8 mFlg; //Mixing flags (see MixF) + //Envelope --------22 + u8 eMode; //[3-0] Current mode (see EnvM) + //[6-4] ADSR mode to switch into from Gain + //[7] Envelope is idle + u8 eRIdx; //Index in RateTab (0-31) + u32 eRate; //Rate of envelope adjustment (16.16) + u32 eCnt; //Sample counter (16.16) + u32 eVal; //Current envelope value + s32 eAdj; //Amount to adjust envelope height + u32 eDest; //Envelope Destination + //Visualization ---08 + s32 vMaxL; //Maximum absolute sample output + s32 vMaxR; + //Samples ---------52 + s32 sP1; //Last sample decompressed (prev1) + s32 sP2; //Second to last sample (prev2) + s16 *sIdx; //-> current sample in sBuf + s16 sBufP[4]; //Last 4 samples from previous block (needed for inter.) + s16 sBuf[16]; //32 bytes for decompressed sample blocks + //Mixing ----------32 + float mTgtL; //Target volume (floating-point routine only) + float mTgtR; // " " + s32 mChnL; //Channel Volume (-24.7) + s32 mChnR; // " " + u32 mRate; //Pitch Rate after modulation (16.16) + u32 mDec; //Pitch Decimal (.16) (used as delta for interpolation) + u32 mOrgP; //Original pitch rate converted from the DSP (16.16) + s32 mOut; //Last sample output before chn vol (used for pitch mod) +} Voice; + +typedef struct DSPState +{ + DSPReg *pDSP; //[0.0] -> DSP registers (128 bytes) + Voice *pVoice; //[0.4] -> Internal mixing settings (1k) + void *pEcho; //[0.8] -> echo buffer (bytes = sample rate * 1.92) + u32 _r1; //[0.C] reserved + + u32 vMMaxL,vMMaxR; //[1.0] Maximum output so far + u32 mAmp; //[1.8] Amplification + u8 vActive; //[1.C] Flags for each active voice + u8 _r2[3]; //[1.D] reserved +} DSPState; + +typedef enum +{ + SPC_EMULATOR_UNKNOWN = 0, + SPC_EMULATOR_ZSNES, + SPC_EMULATOR_SNES9X +} SPC_EmulatorType; + +typedef struct SPC_ID666 +{ + char songname[33]; + char gametitle[33]; + char dumper[17]; + char comments[33]; + char author[33]; + int playtime; + int fadetime; + SPC_EmulatorType emulator; +} SPC_ID666; + +//Interpolation routines ------------------- + enum DSPInter + { + INT_INVALID = -1, + INT_NONE, //None + INT_LINEAR, //Linear + INT_CUBIC, //Cubic + INT_GAUSS, //4-point Gaussian + INT_SINC, //8-point Sinc + INT_TOTAL + }; + + //DSP options ------------------------------ + enum DSPOpts + { + OPT_ANALOG, //Simulate anomalies of the analog hardware + OPT_OLDSMP, //Old sample decompression routine + OPT_SURND, //"Surround" sound + OPT_REVERSE, //Reverse stereo samples + OPT_NOECHO, //Disable echo + OPT_FILTER //Pass each voice through an anti-aliasing filter + }; + + //Mixing routines (see DSP.Asm for details of each routine) + enum Mixing + { + MIX_INVALID = -1, + MIX_NONE = 0, //No mixing + MIX_INT = 1, //Use integer math + MIX_FLOAT = 3 //Use floating-point math + }; + +#endif + diff --git a/lib/snesapu/lgpl.txt b/lib/snesapu/lgpl.txt new file mode 100644 index 0000000000..b1e3f5a263 --- /dev/null +++ b/lib/snesapu/lgpl.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + |