aboutsummaryrefslogtreecommitdiff
path: root/lib/snesapu
diff options
context:
space:
mode:
authortheuni <theuni-nospam-@xbmc.org>2011-01-24 16:05:21 -0500
committertheuni <theuni-nospam-@xbmc.org>2011-01-24 16:05:21 -0500
commitc51b1189e3d5353e842991f5859ddcea0f73e426 (patch)
treeef2cb8a6184699aa614f3655dca4ce661cdc108e /lib/snesapu
parentbe61ebdc9e897fe40c6f371111724de79ddee8d5 (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')
-rw-r--r--lib/snesapu/A2Misc.h173
-rw-r--r--lib/snesapu/Macro.Inc373
-rw-r--r--lib/snesapu/SNES/SNESAPU/APU.Asm423
-rw-r--r--lib/snesapu/SNES/SNESAPU/APU.Inc249
-rw-r--r--lib/snesapu/SNES/SNESAPU/APU.h337
-rw-r--r--lib/snesapu/SNES/SNESAPU/DSP.Asm8191
-rw-r--r--lib/snesapu/SNES/SNESAPU/DSP.Inc877
-rw-r--r--lib/snesapu/SNES/SNESAPU/DSP.h1120
-rw-r--r--lib/snesapu/SNES/SNESAPU/Makefile.in35
-rw-r--r--lib/snesapu/SNES/SNESAPU/ReadMe.Txt204
-rw-r--r--lib/snesapu/SNES/SNESAPU/SNESAPU.cpp56
-rw-r--r--lib/snesapu/SNES/SNESAPU/SNESAPU.def40
-rw-r--r--lib/snesapu/SNES/SNESAPU/SNESAPU.h232
-rw-r--r--lib/snesapu/SNES/SNESAPU/SNESAPU.libbin0 -> 7886 bytes
-rw-r--r--lib/snesapu/SNES/SNESAPU/SNESAPU.sln21
-rw-r--r--lib/snesapu/SNES/SNESAPU/SNESAPU.vcproj290
-rw-r--r--lib/snesapu/SNES/SNESAPU/SPC700.Asm4448
-rw-r--r--lib/snesapu/SNES/SNESAPU/SPC700.Inc366
-rw-r--r--lib/snesapu/SNES/SNESAPU/SPC700.h437
-rw-r--r--lib/snesapu/SNES/SNESAPU/Version.rc121
-rw-r--r--lib/snesapu/Types.h262
-rw-r--r--lib/snesapu/lgpl.txt504
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
new file mode 100644
index 0000000000..d65268b2dc
--- /dev/null
+++ b/lib/snesapu/SNES/SNESAPU/SNESAPU.lib
Binary files differ
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 &quot;$(IntDir)&quot;\&quot;$(InputName)&quot;.obj &quot;$(InputName)&quot;.Asm -l &quot;$(InputName)&quot;.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 &quot;$(IntDir)&quot;\&quot;$(InputName)&quot;.obj &quot;$(InputName)&quot;.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 &quot;$(IntDir)&quot;\&quot;$(InputName)&quot;.obj &quot;$(InputName)&quot;.Asm -l &quot;$(InputName)&quot;.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 &quot;$(IntDir)&quot;\&quot;$(InputName)&quot;.obj &quot;$(InputName)&quot;.Asm -l &quot;$(InputName)&quot;.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 &quot;$(IntDir)&quot;\&quot;$(InputName)&quot;.obj &quot;$(InputName)&quot;.Asm -l &quot;$(InputName)&quot;.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 &quot;$(IntDir)&quot;\&quot;$(InputName)&quot;.obj &quot;$(InputName)&quot;.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!
+
+