summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Fabio Priuli <etabeta78@users.noreply.github.com>2009-09-09 17:14:42 +0000
committer Fabio Priuli <etabeta78@users.noreply.github.com>2009-09-09 17:14:42 +0000
commit2bc40569462efa64ed66f76fe03f795fd8ec5dc6 (patch)
tree59672a7657d2a5edde0722450a6f7e381fbe3e6e
parent740ae26ae6f10a54a172ff78863370c8ed5cb45c (diff)
SNES: Added emulation for the DSP-3 and DSP-4 add-on chips, based on latest ZSNES [ZSNES Team, Fabio Priuli]
out of the whatsnew, I hope the way I worded the license exception (at the top of the files) is fine to avoid any issue
-rw-r--r--.gitattributes3
-rw-r--r--src/mame/machine/snes.c30
-rw-r--r--src/mame/machine/snesdsp3.c1154
-rw-r--r--src/mame/machine/snesdsp4.c2179
-rw-r--r--src/mame/machine/snesdsp4.h104
-rw-r--r--src/mame/mame.mak2
6 files changed, 3472 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
index a853c064fed..f87db013d34 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2881,6 +2881,9 @@ src/mame/machine/slikshot.c svneol=native#text/plain
src/mame/machine/snes.c svneol=native#text/plain
src/mame/machine/snesdsp1.c svneol=native#text/plain
src/mame/machine/snesdsp2.c svneol=native#text/plain
+src/mame/machine/snesdsp3.c svneol=native#text/plain
+src/mame/machine/snesdsp4.c svneol=native#text/plain
+src/mame/machine/snesdsp4.h svneol=native#text/plain
src/mame/machine/snesobc1.c svneol=native#text/plain
src/mame/machine/snesrtc.c svneol=native#text/plain
src/mame/machine/snessdd1.c svneol=native#text/plain
diff --git a/src/mame/machine/snes.c b/src/mame/machine/snes.c
index 647ae6542fe..a63cf8985c5 100644
--- a/src/mame/machine/snes.c
+++ b/src/mame/machine/snes.c
@@ -58,6 +58,8 @@ static struct
// add-on chip emulators
#include "machine/snesdsp1.c"
#include "machine/snesdsp2.c"
+#include "machine/snesdsp3.c"
+#include "machine/snesdsp4.c"
#include "machine/snesobc1.c"
#include "machine/snesrtc.c"
#include "machine/snessdd1.c"
@@ -1475,6 +1477,8 @@ READ8_HANDLER( snes_r_bank1 )
value = (address < 0xc000) ? DSP1_getDr() : DSP1_getSr();
else if ((snes_cart.mode == SNES_MODE_20) && (snes_has_addon_chip == HAS_DSP2) && (offset >= 0x200000))
value = (address < 0xc000) ? DSP2_read() : 0x00;
+ else if ((snes_has_addon_chip == HAS_DSP3) && (offset >= 0x200000))
+ value = DSP3_read(address);
else
value = snes_ram[offset];
@@ -1514,6 +1518,10 @@ READ8_HANDLER( snes_r_bank2 )
value = (address < 0xc000) ? DSP1_getDr() : DSP1_getSr();
else if ((snes_cart.mode == SNES_MODE_20) && (snes_has_addon_chip == HAS_DSP2))
value = (address < 0xc000) ? DSP2_read() : 0x00;
+ else if (snes_has_addon_chip == HAS_DSP3)
+ value = DSP3_read(address);
+ else if (snes_has_addon_chip == HAS_DSP4)
+ value = (address < 0xc000) ? DSP4_read() : 0x80;
else
value = snes_ram[0x300000 + offset];
@@ -1637,6 +1645,10 @@ READ8_HANDLER( snes_r_bank6 )
value = (address < 0xc000) ? DSP1_getDr() : DSP1_getSr();
else if ((snes_cart.mode == SNES_MODE_20) && (snes_has_addon_chip == HAS_DSP2) && (offset >= 0x200000))
value = (address < 0xc000) ? DSP2_read() : 0x00;
+ else if ((snes_has_addon_chip == HAS_DSP3) && (offset >= 0x200000))
+ value = DSP3_read(address);
+ else if ((snes_has_addon_chip == HAS_DSP4) && (offset >= 0x300000))
+ value = (address < 0xc000) ? DSP4_read() : 0x80;
else
value = snes_ram[0x800000 + offset];
@@ -1699,6 +1711,8 @@ WRITE8_HANDLER( snes_w_bank1 )
DSP1_setDr(data);
else if ((snes_cart.mode == SNES_MODE_20) && (snes_has_addon_chip == HAS_DSP2) && (offset >= 0x200000) && (address < 0xc000))
DSP2_write(data);
+ else if ((snes_has_addon_chip == HAS_DSP3) && (offset >= 0x200000))
+ DSP3_write(address, data);
else
logerror( "Attempt to write to ROM address: %X\n", offset );
}
@@ -1733,6 +1747,10 @@ WRITE8_HANDLER( snes_w_bank2 )
DSP1_setDr(data);
else if ((snes_cart.mode == SNES_MODE_20) && (snes_has_addon_chip == HAS_DSP2) && (address < 0xc000))
DSP2_write(data);
+ else if (snes_has_addon_chip == HAS_DSP3)
+ DSP3_write(address, data);
+ else if ((snes_has_addon_chip == HAS_DSP4) && (address < 0xc000))
+ DSP4_write(data);
else
logerror("Attempt to write to ROM address: %X\n", offset + 0x300000);
}
@@ -1837,6 +1855,10 @@ WRITE8_HANDLER( snes_w_bank6 )
DSP1_setDr(data);
else if ((snes_cart.mode == SNES_MODE_20) && (snes_has_addon_chip == HAS_DSP2) && (offset >= 0x200000) && (address < 0xc000))
DSP2_write(data);
+ else if ((snes_has_addon_chip == HAS_DSP3) && (offset >= 0x200000))
+ DSP3_write(address, data);
+ else if ((snes_has_addon_chip == HAS_DSP4) && (offset >= 0x300000) && (address < 0xc000))
+ DSP4_write(data);
else if (snes_has_addon_chip == HAS_SUPERFX && cputag_get_cpu(space->machine, "superfx") != NULL)
logerror( "snes_w_bank6 hit (ROM) in Super FX mode, please fix me\n" );
else
@@ -1957,6 +1979,14 @@ static void snes_init_ram(running_machine *machine)
DSP2_reset();
break;
+ case HAS_DSP3:
+ InitDSP3();
+ break;
+
+ case HAS_DSP4:
+ InitDSP4();
+ break;
+
case HAS_OBC1:
obc1_init();
break;
diff --git a/src/mame/machine/snesdsp3.c b/src/mame/machine/snesdsp3.c
new file mode 100644
index 00000000000..e983376e122
--- /dev/null
+++ b/src/mame/machine/snesdsp3.c
@@ -0,0 +1,1154 @@
+/************************************************************************
+
+ DSP-3 emulator code
+
+ Copyright (c) 2003-2009 John Weidman, Kris Bleakley, Lancer, Nach,
+ z80 gaiden, and Jonas Quinn
+
+ This code is released by ZSNES Team under GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ The implementation below is released under the MAME license for use
+ in MAME, MESS and derivatives by permission of the authors.
+
+************************************************************************/
+
+UINT16 DSP3_DataROM[1024] = {
+ 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100,
+ 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001,
+ 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100,
+ 0x0000, 0x000f, 0x0400, 0x0200, 0x0140, 0x0400, 0x0200, 0x0040,
+ 0x007d, 0x007e, 0x007e, 0x007b, 0x007c, 0x007d, 0x007b, 0x007c,
+ 0x0002, 0x0020, 0x0030, 0x0000, 0x000d, 0x0019, 0x0026, 0x0032,
+ 0x003e, 0x004a, 0x0056, 0x0062, 0x006d, 0x0079, 0x0084, 0x008e,
+ 0x0098, 0x00a2, 0x00ac, 0x00b5, 0x00be, 0x00c6, 0x00ce, 0x00d5,
+ 0x00dc, 0x00e2, 0x00e7, 0x00ec, 0x00f1, 0x00f5, 0x00f8, 0x00fb,
+ 0x00fd, 0x00ff, 0x0100, 0x0100, 0x0100, 0x00ff, 0x00fd, 0x00fb,
+ 0x00f8, 0x00f5, 0x00f1, 0x00ed, 0x00e7, 0x00e2, 0x00dc, 0x00d5,
+ 0x00ce, 0x00c6, 0x00be, 0x00b5, 0x00ac, 0x00a2, 0x0099, 0x008e,
+ 0x0084, 0x0079, 0x006e, 0x0062, 0x0056, 0x004a, 0x003e, 0x0032,
+ 0x0026, 0x0019, 0x000d, 0x0000, 0xfff3, 0xffe7, 0xffdb, 0xffce,
+ 0xffc2, 0xffb6, 0xffaa, 0xff9e, 0xff93, 0xff87, 0xff7d, 0xff72,
+ 0xff68, 0xff5e, 0xff54, 0xff4b, 0xff42, 0xff3a, 0xff32, 0xff2b,
+ 0xff25, 0xff1e, 0xff19, 0xff14, 0xff0f, 0xff0b, 0xff08, 0xff05,
+ 0xff03, 0xff01, 0xff00, 0xff00, 0xff00, 0xff01, 0xff03, 0xff05,
+ 0xff08, 0xff0b, 0xff0f, 0xff13, 0xff18, 0xff1e, 0xff24, 0xff2b,
+ 0xff32, 0xff3a, 0xff42, 0xff4b, 0xff54, 0xff5d, 0xff67, 0xff72,
+ 0xff7c, 0xff87, 0xff92, 0xff9e, 0xffa9, 0xffb5, 0xffc2, 0xffce,
+ 0xffda, 0xffe7, 0xfff3, 0x002b, 0x007f, 0x0020, 0x00ff, 0xff00,
+ 0xffbe, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffc1, 0x0001, 0x0002, 0x0045,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffc5, 0x0003, 0x0004, 0x0005, 0x0047, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffca, 0x0006, 0x0007, 0x0008,
+ 0x0009, 0x004a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffd0, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x004e, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffd7, 0x000f, 0x0010, 0x0011,
+ 0x0012, 0x0013, 0x0014, 0x0053, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffdf, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b,
+ 0x0059, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffe8, 0x001c, 0x001d, 0x001e,
+ 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0060, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xfff2, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a,
+ 0x002b, 0x002c, 0x0068, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xfffd, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0071,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffc7, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d,
+ 0x003e, 0x003f, 0x0040, 0x0041, 0x007b, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffd4, 0x0000, 0x0001, 0x0002,
+ 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a,
+ 0x000b, 0x0044, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffe2, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012,
+ 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0050, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xfff1, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x005d, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffcb, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d,
+ 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+ 0x006b, 0x0000, 0x0000, 0x0000, 0xffdc, 0x0000, 0x0001, 0x0002,
+ 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a,
+ 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0044, 0x0000, 0x0000,
+ 0xffee, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016,
+ 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e,
+ 0x001f, 0x0020, 0x0054, 0x0000, 0xffee, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0065,
+ 0xffbe, 0x0000, 0xfeac, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffc1, 0x0001, 0x0002, 0xfead,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffc5, 0x0003, 0x0004, 0x0005, 0xfeaf, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffca, 0x0006, 0x0007, 0x0008,
+ 0x0009, 0xfeb2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffd0, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0xfeb6, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffd7, 0x000f, 0x0010, 0x0011,
+ 0x0012, 0x0013, 0x0014, 0xfebb, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffdf, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b,
+ 0xfec1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffe8, 0x001c, 0x001d, 0x001e,
+ 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0xfec8, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xfff2, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a,
+ 0x002b, 0x002c, 0xfed0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xfffd, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0xfed9,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffc7, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d,
+ 0x003e, 0x003f, 0x0040, 0x0041, 0xfee3, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xffd4, 0x0000, 0x0001, 0x0002,
+ 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a,
+ 0x000b, 0xfeac, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffe2, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012,
+ 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0xfeb8, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xfff1, 0x0019, 0x001a, 0x001b,
+ 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0xfec5, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0xffcb, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d,
+ 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+ 0xfed3, 0x0000, 0x0000, 0x0000, 0xffdc, 0x0000, 0x0001, 0x0002,
+ 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a,
+ 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0xfeac, 0x0000, 0x0000,
+ 0xffee, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016,
+ 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e,
+ 0x001f, 0x0020, 0xfebc, 0x0000, 0xffee, 0x0021, 0x0022, 0x0023,
+ 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0xfecd,
+ 0x0154, 0x0218, 0x0110, 0x00b0, 0x00cc, 0x00b0, 0x0088, 0x00b0,
+ 0x0044, 0x00b0, 0x0000, 0x00b0, 0x00fe, 0xff07, 0x0002, 0x00ff,
+ 0x00f8, 0x0007, 0x00fe, 0x00ee, 0x07ff, 0x0200, 0x00ef, 0xf800,
+ 0x0700, 0x00ee, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0001,
+ 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000,
+ 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0x0001, 0x0000, 0x0001,
+ 0x0001, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000,
+ 0xffff, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xffff,
+ 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0044, 0x0088, 0x00cc,
+ 0x0110, 0x0154, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+void (*SetDSP3)( void );
+void DSP3_Command( void );
+
+UINT16 DSP3_DR;
+UINT16 DSP3_SR;
+UINT16 DSP3_MemoryIndex;
+
+void DSP3_Reset( void )
+{
+ DSP3_DR = 0x0080;
+ DSP3_SR = 0x0084;
+ SetDSP3 = &DSP3_Command;
+}
+
+void DSP3_MemorySize( void )
+{
+ DSP3_DR = 0x0300;
+ SetDSP3 = &DSP3_Reset;
+}
+
+void DSP3_TestMemory( void )
+{
+ DSP3_DR = 0x0000;
+ SetDSP3 = &DSP3_Reset;
+}
+
+void DSP3_DumpDataROM( void )
+{
+ DSP3_DR = DSP3_DataROM[DSP3_MemoryIndex++];
+ if (DSP3_MemoryIndex == 1024)
+ SetDSP3 = &DSP3_Reset;
+}
+
+void DSP3_MemoryDump( void )
+{
+ DSP3_MemoryIndex = 0;
+ SetDSP3 = &DSP3_DumpDataROM;
+ DSP3_DumpDataROM();
+}
+
+INT16 DSP3_WinLo;
+INT16 DSP3_WinHi;
+
+void DSP3_OP06( void )
+{
+ DSP3_WinLo = (UINT8)(DSP3_DR);
+ DSP3_WinHi = (UINT8)(DSP3_DR >> 8);
+ DSP3_Reset();
+}
+
+void DSP3_OP03( void )
+{
+ INT16 Lo = (UINT8)(DSP3_DR);
+ INT16 Hi = (UINT8)(DSP3_DR >> 8);
+ INT16 Ofs = (DSP3_WinLo * Hi << 1) + (Lo << 1);
+ DSP3_DR = Ofs >> 1;
+ SetDSP3 = &DSP3_Reset;
+}
+
+INT16 DSP3_AddLo;
+INT16 DSP3_AddHi;
+
+void DSP3_OP07_B( void )
+{
+ INT16 Ofs = (DSP3_WinLo * DSP3_AddHi << 1) + (DSP3_AddLo << 1);
+ DSP3_DR = Ofs >> 1;
+ SetDSP3 = &DSP3_Reset;
+}
+
+void DSP3_OP07_A( void )
+{
+ INT16 Lo = (UINT8)(DSP3_DR);
+ INT16 Hi = (UINT8)(DSP3_DR >> 8);
+
+ if (Lo & 1) Hi += (DSP3_AddLo & 1);
+
+ DSP3_AddLo += Lo;
+ DSP3_AddHi += Hi;
+
+ if (DSP3_AddLo < 0)
+ DSP3_AddLo += DSP3_WinLo;
+ else
+ if (DSP3_AddLo >= DSP3_WinLo)
+ DSP3_AddLo -= DSP3_WinLo;
+
+ if (DSP3_AddHi < 0)
+ DSP3_AddHi += DSP3_WinHi;
+ else
+ if (DSP3_AddHi >= DSP3_WinHi)
+ DSP3_AddHi -= DSP3_WinHi;
+
+ DSP3_DR = DSP3_AddLo | (DSP3_AddHi << 8) | ((DSP3_AddHi >> 8) & 0xff);
+ SetDSP3 = &DSP3_OP07_B;
+}
+
+void DSP3_OP07( void )
+{
+ UINT32 dataOfs = ((DSP3_DR << 1) + 0x03b2) & 0x03ff;
+
+ DSP3_AddHi = DSP3_DataROM[dataOfs];
+ DSP3_AddLo = DSP3_DataROM[dataOfs + 1];
+
+ SetDSP3 = &DSP3_OP07_A;
+ DSP3_SR = 0x0080;
+}
+
+UINT16 DSP3_Codewords;
+UINT16 DSP3_Outwords;
+UINT16 DSP3_Symbol;
+UINT16 DSP3_BitCount;
+UINT16 DSP3_Index;
+UINT16 DSP3_Codes[512];
+UINT16 DSP3_BitsLeft;
+UINT16 DSP3_ReqBits;
+UINT16 DSP3_ReqData;
+UINT16 DSP3_BitCommand;
+UINT8 DSP3_BaseLength;
+UINT16 DSP3_BaseCodes;
+UINT16 DSP3_BaseCode;
+UINT8 DSP3_CodeLengths[8];
+UINT16 DSP3_CodeOffsets[8];
+UINT16 DSP3_LZCode;
+UINT8 DSP3_LZLength;
+
+UINT16 DSP3_X;
+UINT16 DSP3_Y;
+
+void DSP3_Coordinate( void )
+{
+ DSP3_Index++;
+
+ switch (DSP3_Index)
+ {
+ case 3:
+ {
+ if (DSP3_DR == 0xffff)
+ DSP3_Reset();
+ break;
+ }
+ case 4:
+ {
+ DSP3_X = DSP3_DR;
+ break;
+ }
+ case 5:
+ {
+ DSP3_Y = DSP3_DR;
+ DSP3_DR = 1;
+ break;
+ }
+ case 6:
+ {
+ DSP3_DR = DSP3_X;
+ break;
+ }
+ case 7:
+ {
+ DSP3_DR = DSP3_Y;
+ DSP3_Index = 0;
+ break;
+ }
+ }
+}
+
+UINT8 DSP3_Bitmap[8];
+UINT8 DSP3_Bitplane[8];
+UINT16 DSP3_BMIndex;
+UINT16 DSP3_BPIndex;
+UINT16 DSP3_Count;
+
+void DSP3_Convert_A( void )
+{
+ if (DSP3_BMIndex < 8)
+ {
+ DSP3_Bitmap[DSP3_BMIndex++] = (UINT8) (DSP3_DR);
+ DSP3_Bitmap[DSP3_BMIndex++] = (UINT8) (DSP3_DR >> 8);
+
+ if (DSP3_BMIndex == 8)
+ {
+ short i, j;
+ for (i=0; i < 8; i++)
+ for (j=0; j < 8; j++)
+ {
+ DSP3_Bitplane[j] <<= 1;
+ DSP3_Bitplane[j] |= (DSP3_Bitmap[i] >> j) & 1;
+ }
+
+ DSP3_BPIndex = 0;
+ DSP3_Count--;
+ }
+ }
+
+ if (DSP3_BMIndex == 8)
+ {
+ if (DSP3_BPIndex == 8)
+ {
+ if (!DSP3_Count) DSP3_Reset();
+ DSP3_BMIndex = 0;
+ }
+ else
+ {
+ DSP3_DR = DSP3_Bitplane[DSP3_BPIndex++];
+ DSP3_DR |= DSP3_Bitplane[DSP3_BPIndex++] << 8;
+ }
+ }
+}
+
+void DSP3_Convert( void )
+{
+ DSP3_Count = DSP3_DR;
+ DSP3_BMIndex = 0;
+ SetDSP3 = &DSP3_Convert_A;
+}
+
+UINT8 DSP3_GetBits(UINT8 Count)
+{
+ if (!DSP3_BitsLeft)
+ {
+ DSP3_BitsLeft = Count;
+ DSP3_ReqBits = 0;
+ }
+
+ do {
+ if (!DSP3_BitCount)
+ {
+ DSP3_SR = 0xC0;
+ return 0;
+ }
+
+ DSP3_ReqBits <<= 1;
+ if (DSP3_ReqData & 0x8000) DSP3_ReqBits++;
+ DSP3_ReqData <<= 1;
+
+ DSP3_BitCount--;
+ DSP3_BitsLeft--;
+
+ } while (DSP3_BitsLeft);
+
+ return 1;
+}
+
+void DSP3_Decode_Data( void )
+{
+ if (!DSP3_BitCount)
+ {
+ if (DSP3_SR & 0x40)
+ {
+ DSP3_ReqData = DSP3_DR;
+ DSP3_BitCount += 16;
+ }
+ else
+ {
+ DSP3_SR = 0xC0;
+ return;
+ }
+ }
+
+ if (DSP3_LZCode == 1)
+ {
+ if (!DSP3_GetBits(1))
+ return;
+
+ if (DSP3_ReqBits)
+ DSP3_LZLength = 12;
+ else
+ DSP3_LZLength = 8;
+
+ DSP3_LZCode++;
+ }
+
+ if (DSP3_LZCode == 2)
+ {
+ if (!DSP3_GetBits(DSP3_LZLength))
+ return;
+
+ DSP3_LZCode = 0;
+ DSP3_Outwords--;
+ if (!DSP3_Outwords) SetDSP3 = &DSP3_Reset;
+
+ DSP3_SR = 0x80;
+ DSP3_DR = DSP3_ReqBits;
+ return;
+ }
+
+ if (DSP3_BaseCode == 0xffff)
+ {
+ if (!DSP3_GetBits(DSP3_BaseLength))
+ return;
+
+ DSP3_BaseCode = DSP3_ReqBits;
+ }
+
+ if (!DSP3_GetBits(DSP3_CodeLengths[DSP3_BaseCode]))
+ return;
+
+ DSP3_Symbol = DSP3_Codes[DSP3_CodeOffsets[DSP3_BaseCode] + DSP3_ReqBits];
+ DSP3_BaseCode = 0xffff;
+
+ if (DSP3_Symbol & 0xff00)
+ {
+ DSP3_Symbol += 0x7f02;
+ DSP3_LZCode++;
+ }
+ else
+ {
+ DSP3_Outwords--;
+ if (!DSP3_Outwords)
+ SetDSP3 = &DSP3_Reset;
+ }
+
+ DSP3_SR = 0x80;
+ DSP3_DR = DSP3_Symbol;
+}
+
+void DSP3_Decode_Tree( void )
+{
+ if (!DSP3_BitCount)
+ {
+ DSP3_ReqData = DSP3_DR;
+ DSP3_BitCount += 16;
+ }
+
+ if (!DSP3_BaseCodes)
+ {
+ DSP3_GetBits(1);
+ if (DSP3_ReqBits)
+ {
+ DSP3_BaseLength = 3;
+ DSP3_BaseCodes = 8;
+ }
+ else
+ {
+ DSP3_BaseLength = 2;
+ DSP3_BaseCodes = 4;
+ }
+ }
+
+ while (DSP3_BaseCodes)
+ {
+ if (!DSP3_GetBits(3))
+ return;
+
+ DSP3_ReqBits++;
+
+ DSP3_CodeLengths[DSP3_Index] = (UINT8) DSP3_ReqBits;
+ DSP3_CodeOffsets[DSP3_Index] = DSP3_Symbol;
+ DSP3_Index++;
+
+ DSP3_Symbol += 1 << DSP3_ReqBits;
+ DSP3_BaseCodes--;
+ }
+
+ DSP3_BaseCode = 0xffff;
+ DSP3_LZCode = 0;
+
+ SetDSP3 = &DSP3_Decode_Data;
+ if (DSP3_BitCount) DSP3_Decode_Data();
+}
+
+void DSP3_Decode_Symbols( void )
+{
+ DSP3_ReqData = DSP3_DR;
+ DSP3_BitCount += 16;
+
+ do {
+
+ if (DSP3_BitCommand == 0xffff)
+ {
+ if (!DSP3_GetBits(2)) return;
+ DSP3_BitCommand = DSP3_ReqBits;
+ }
+
+ switch (DSP3_BitCommand)
+ {
+ case 0:
+ {
+ if (!DSP3_GetBits(9)) return;
+ DSP3_Symbol = DSP3_ReqBits;
+ break;
+ }
+ case 1:
+ {
+ DSP3_Symbol++;
+ break;
+ }
+ case 2:
+ {
+ if (!DSP3_GetBits(1)) return;
+ DSP3_Symbol += 2 + DSP3_ReqBits;
+ break;
+ }
+ case 3:
+ {
+ if (!DSP3_GetBits(4)) return;
+ DSP3_Symbol += 4 + DSP3_ReqBits;
+ break;
+ }
+ }
+
+ DSP3_BitCommand = 0xffff;
+
+ DSP3_Codes[DSP3_Index++] = DSP3_Symbol;
+ DSP3_Codewords--;
+
+ } while (DSP3_Codewords);
+
+ DSP3_Index = 0;
+ DSP3_Symbol = 0;
+ DSP3_BaseCodes = 0;
+
+ SetDSP3 = &DSP3_Decode_Tree;
+ if (DSP3_BitCount) DSP3_Decode_Tree();
+}
+
+void DSP3_Decode_A( void )
+{
+ DSP3_Outwords = DSP3_DR;
+ SetDSP3 = &DSP3_Decode_Symbols;
+ DSP3_BitCount = 0;
+ DSP3_BitsLeft = 0;
+ DSP3_Symbol = 0;
+ DSP3_Index = 0;
+ DSP3_BitCommand = 0xffff;
+ DSP3_SR = 0xC0;
+}
+
+void DSP3_Decode( void )
+{
+ DSP3_Codewords = DSP3_DR;
+ SetDSP3 = &DSP3_Decode_A;
+}
+
+
+// Opcodes 1E/3E bit-perfect to 'dsp3-intro' log
+// src: adapted from SD Gundam X/G-Next
+
+INT16 op3e_x;
+INT16 op3e_y;
+
+INT16 op1e_terrain[0x2000];
+INT16 op1e_cost[0x2000];
+INT16 op1e_weight[0x2000];
+
+INT16 op1e_cell;
+INT16 op1e_turn;
+INT16 op1e_search;
+
+INT16 op1e_x;
+INT16 op1e_y;
+
+INT16 op1e_min_radius;
+INT16 op1e_max_radius;
+
+INT16 op1e_max_search_radius;
+INT16 op1e_max_path_radius;
+
+INT16 op1e_lcv_radius;
+INT16 op1e_lcv_steps;
+INT16 op1e_lcv_turns;
+
+void DSP3_OP3E( void )
+{
+ op3e_x = (UINT8)(DSP3_DR & 0x00ff);
+ op3e_y = (UINT8)((DSP3_DR & 0xff00)>>8);
+
+ DSP3_OP03();
+
+ op1e_terrain[ DSP3_DR ] = 0x00;
+ op1e_cost[ DSP3_DR ] = 0xff;
+ op1e_weight[ DSP3_DR ] = 0;
+
+ op1e_max_search_radius = 0;
+ op1e_max_path_radius = 0;
+}
+
+void DSP3_OP1E_A( void );
+void DSP3_OP1E_A1( void );
+void DSP3_OP1E_A2( void );
+void DSP3_OP1E_A3( void );
+
+void DSP3_OP1E_B( void );
+void DSP3_OP1E_B1( void );
+void DSP3_OP1E_B2( void );
+
+void DSP3_OP1E_C( void );
+void DSP3_OP1E_C1( void );
+void DSP3_OP1E_C2( void );
+
+void DSP3_OP1E_D( INT16, INT16 *, INT16 * );
+void DSP3_OP1E_D1( INT16 move, INT16 *lo, INT16 *hi );
+
+void DSP3_OP1E( void )
+{
+ int lcv;
+
+ op1e_min_radius = (UINT8)(DSP3_DR & 0x00ff);
+ op1e_max_radius = (UINT8)((DSP3_DR & 0xff00)>>8);
+
+ if( op1e_min_radius == 0 )
+ op1e_min_radius++;
+
+ if( op1e_max_search_radius >= op1e_min_radius )
+ op1e_min_radius = op1e_max_search_radius+1;
+
+ if( op1e_max_radius > op1e_max_search_radius )
+ op1e_max_search_radius = op1e_max_radius;
+
+ op1e_lcv_radius = op1e_min_radius;
+ op1e_lcv_steps = op1e_min_radius;
+
+ op1e_lcv_turns = 6;
+ op1e_turn = 0;
+
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+
+ for( lcv = 0; lcv < op1e_min_radius; lcv++ )
+ DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y );
+
+ DSP3_OP1E_A();
+}
+
+void DSP3_OP1E_A( void )
+{
+ int lcv;
+
+ if( op1e_lcv_steps == 0 ) {
+ op1e_lcv_radius++;
+
+ op1e_lcv_steps = op1e_lcv_radius;
+
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+
+ for( lcv = 0; lcv < op1e_lcv_radius; lcv++ )
+ DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y );
+ }
+
+ if( op1e_lcv_radius > op1e_max_radius ) {
+ op1e_turn++;
+ op1e_lcv_turns--;
+
+ op1e_lcv_radius = op1e_min_radius;
+ op1e_lcv_steps = op1e_min_radius;
+
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+
+ for( lcv = 0; lcv < op1e_min_radius; lcv++ )
+ DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y );
+ }
+
+ if( op1e_lcv_turns == 0 ) {
+ DSP3_DR = 0xffff;
+ DSP3_SR = 0x0080;
+ SetDSP3 = &DSP3_OP1E_B;
+ return;
+ }
+
+ DSP3_DR = (UINT8)(op1e_x) | ((UINT8)(op1e_y)<<8);
+ DSP3_OP03();
+
+ op1e_cell = DSP3_DR;
+
+ DSP3_SR = 0x0080;
+ SetDSP3 = &DSP3_OP1E_A1;
+}
+
+void DSP3_OP1E_A1( void )
+{
+ DSP3_SR = 0x0084;
+ SetDSP3 = &DSP3_OP1E_A2;
+}
+
+void DSP3_OP1E_A2( void )
+{
+ op1e_terrain[ op1e_cell ] = (UINT8)(DSP3_DR & 0x00ff);
+
+ DSP3_SR = 0x0084;
+ SetDSP3 = &DSP3_OP1E_A3;
+}
+
+void DSP3_OP1E_A3( void )
+{
+ op1e_cost[ op1e_cell ] = (UINT8)(DSP3_DR & 0x00ff);
+
+ if( op1e_lcv_radius == 1 ) {
+ if( op1e_terrain[ op1e_cell ] & 1 ) {
+ op1e_weight[ op1e_cell ] = 0xff;
+ } else {
+ op1e_weight[ op1e_cell ] = op1e_cost[ op1e_cell ];
+ }
+ }
+ else {
+ op1e_weight[ op1e_cell ] = 0xff;
+ }
+
+ DSP3_OP1E_D( (INT16)(op1e_turn+2), &op1e_x, &op1e_y );
+ op1e_lcv_steps--;
+
+ DSP3_SR = 0x0080;
+ DSP3_OP1E_A();
+}
+
+
+void DSP3_OP1E_B( void )
+{
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+ op1e_lcv_radius = 1;
+
+ op1e_search = 0;
+
+ DSP3_OP1E_B1();
+
+ SetDSP3 = &DSP3_OP1E_C;
+}
+
+
+void DSP3_OP1E_B1( void )
+{
+ while( op1e_lcv_radius < op1e_max_radius ) {
+ op1e_y--;
+
+ op1e_lcv_turns = 6;
+ op1e_turn = 5;
+
+ while( op1e_lcv_turns ) {
+ op1e_lcv_steps = op1e_lcv_radius;
+
+ while( op1e_lcv_steps ) {
+ DSP3_OP1E_D1( op1e_turn, &op1e_x, &op1e_y );
+
+ if( 0 <= op1e_y && op1e_y < DSP3_WinHi &&
+ 0 <= op1e_x && op1e_x < DSP3_WinLo ) {
+ DSP3_DR = (UINT8)(op1e_x) | ((UINT8)(op1e_y)<<8);
+ DSP3_OP03();
+
+ op1e_cell = DSP3_DR;
+ if( op1e_cost[ op1e_cell ] < 0x80 &&
+ op1e_terrain[ op1e_cell ] < 0x40 ) {
+ DSP3_OP1E_B2();
+ } // end cell perimeter
+ }
+
+ op1e_lcv_steps--;
+ } // end search line
+
+ op1e_turn--;
+ if( op1e_turn == 0 ) op1e_turn = 6;
+
+ op1e_lcv_turns--;
+ } // end circle search
+
+ op1e_lcv_radius++;
+ } // end radius search
+}
+
+
+void DSP3_OP1E_B2( void )
+{
+ INT16 cell;
+ INT16 path;
+ INT16 x,y;
+ INT16 lcv_turns;
+
+ path = 0xff;
+ lcv_turns = 6;
+
+ while( lcv_turns ) {
+ x = op1e_x;
+ y = op1e_y;
+
+ DSP3_OP1E_D1( lcv_turns, &x, &y );
+
+ DSP3_DR = (UINT8)(x) | ((UINT8)(y)<<8);
+ DSP3_OP03();
+
+ cell = DSP3_DR;
+
+ if( 0 <= y && y < DSP3_WinHi &&
+ 0 <= x && x < DSP3_WinLo ) {
+
+ if( op1e_terrain[ cell ] < 0x80 || op1e_weight[ cell ] == 0 ) {
+ if( op1e_weight[ cell ] < path ) {
+ path = op1e_weight[ cell ];
+ }
+ }
+ } // end step travel
+
+ lcv_turns--;
+ } // end while turns
+
+ if( path != 0xff ) {
+ op1e_weight[ op1e_cell ] = path + op1e_cost[ op1e_cell ];
+ }
+}
+
+
+void DSP3_OP1E_C( void )
+{
+ int lcv;
+
+ op1e_min_radius = (UINT8)(DSP3_DR & 0x00ff);
+ op1e_max_radius = (UINT8)((DSP3_DR & 0xff00)>>8);
+
+ if( op1e_min_radius == 0 )
+ op1e_min_radius++;
+
+ if( op1e_max_path_radius >= op1e_min_radius )
+ op1e_min_radius = op1e_max_path_radius+1;
+
+ if( op1e_max_radius > op1e_max_path_radius )
+ op1e_max_path_radius = op1e_max_radius;
+
+ op1e_lcv_radius = op1e_min_radius;
+ op1e_lcv_steps = op1e_min_radius;
+
+ op1e_lcv_turns = 6;
+ op1e_turn = 0;
+
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+
+ for( lcv = 0; lcv < op1e_min_radius; lcv++ )
+ DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y );
+
+ DSP3_OP1E_C1();
+}
+
+
+void DSP3_OP1E_C1( void )
+{
+ int lcv;
+
+ if( op1e_lcv_steps == 0 ) {
+ op1e_lcv_radius++;
+
+ op1e_lcv_steps = op1e_lcv_radius;
+
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+
+ for( lcv = 0; lcv < op1e_lcv_radius; lcv++ )
+ DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y );
+ }
+
+ if( op1e_lcv_radius > op1e_max_radius ) {
+ op1e_turn++;
+ op1e_lcv_turns--;
+
+ op1e_lcv_radius = op1e_min_radius;
+ op1e_lcv_steps = op1e_min_radius;
+
+ op1e_x = op3e_x;
+ op1e_y = op3e_y;
+
+ for( lcv = 0; lcv < op1e_min_radius; lcv++ )
+ DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y );
+ }
+
+ if( op1e_lcv_turns == 0 ) {
+ DSP3_DR = 0xffff;
+ DSP3_SR = 0x0080;
+ SetDSP3 = &DSP3_Reset;
+ return;
+ }
+
+ DSP3_DR = (UINT8)(op1e_x) | ((UINT8)(op1e_y)<<8);
+ DSP3_OP03();
+
+ op1e_cell = DSP3_DR;
+
+ DSP3_SR = 0x0080;
+ SetDSP3 = &DSP3_OP1E_C2;
+}
+
+
+void DSP3_OP1E_C2( void )
+{
+ DSP3_DR = op1e_weight[ op1e_cell ];
+
+ DSP3_OP1E_D( (INT16)(op1e_turn+2), &op1e_x, &op1e_y );
+ op1e_lcv_steps--;
+
+ DSP3_SR = 0x0084;
+ SetDSP3 = &DSP3_OP1E_C1;
+}
+
+
+void DSP3_OP1E_D( INT16 move, INT16 *lo, INT16 *hi )
+{
+ UINT32 dataOfs = ((move << 1) + 0x03b2) & 0x03ff;
+ INT16 Lo;
+ INT16 Hi;
+
+ DSP3_AddHi = DSP3_DataROM[dataOfs];
+ DSP3_AddLo = DSP3_DataROM[dataOfs + 1];
+
+ Lo = (UINT8)(*lo);
+ Hi = (UINT8)(*hi);
+
+ if (Lo & 1) Hi += (DSP3_AddLo & 1);
+
+ DSP3_AddLo += Lo;
+ DSP3_AddHi += Hi;
+
+ if (DSP3_AddLo < 0)
+ DSP3_AddLo += DSP3_WinLo;
+ else
+ if (DSP3_AddLo >= DSP3_WinLo)
+ DSP3_AddLo -= DSP3_WinLo;
+
+ if (DSP3_AddHi < 0)
+ DSP3_AddHi += DSP3_WinHi;
+ else
+ if (DSP3_AddHi >= DSP3_WinHi)
+ DSP3_AddHi -= DSP3_WinHi;
+
+ *lo = DSP3_AddLo;
+ *hi = DSP3_AddHi;
+}
+
+
+void DSP3_OP1E_D1( INT16 move, INT16 *lo, INT16 *hi )
+{
+ //UINT32 dataOfs = ((move << 1) + 0x03b2) & 0x03ff;
+ INT16 Lo;
+ INT16 Hi;
+
+ const unsigned short HiAdd[] = {
+ 0x00, 0xFF, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00
+ };
+ const unsigned short LoAdd[] = {
+ 0x00, 0x00, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x00
+ };
+
+ if( (*lo) & 1 )
+ DSP3_AddHi = HiAdd[ move + 8 ];
+ else
+ DSP3_AddHi = HiAdd[ move + 0 ];
+ DSP3_AddLo = LoAdd[ move ];
+
+ Lo = (UINT8)(*lo);
+ Hi = (UINT8)(*hi);
+
+ if (Lo & 1) Hi += (DSP3_AddLo & 1);
+
+ DSP3_AddLo += Lo;
+ DSP3_AddHi += Hi;
+
+ *lo = DSP3_AddLo;
+ *hi = DSP3_AddHi;
+}
+
+
+void DSP3_OP10( void )
+{
+ if( DSP3_DR == 0xffff ) {
+ DSP3_Reset();
+ } else {
+ // absorb 2 bytes
+ DSP3_DR = DSP3_DR;
+ }
+}
+
+
+void DSP3_OP0C_A( void )
+{
+ // absorb 2 bytes
+
+ DSP3_DR = 0;
+ SetDSP3 = &DSP3_Reset;
+}
+
+
+void DSP3_OP0C( void )
+{
+ // absorb 2 bytes
+
+ DSP3_DR = 0;
+ //SetDSP3 = &DSP3_OP0C_A;
+ SetDSP3 = &DSP3_Reset;
+}
+
+
+void DSP3_OP1C_C( void )
+{
+ // return 2 bytes
+ DSP3_DR = 0;
+ SetDSP3 = &DSP3_Reset;
+}
+
+
+void DSP3_OP1C_B( void )
+{
+ // absorb 2 bytes
+
+ // return 2 bytes
+ DSP3_DR = 0;
+ SetDSP3 = &DSP3_OP1C_C;
+}
+
+
+void DSP3_OP1C_A( void )
+{
+ // absorb 2 bytes
+
+ SetDSP3 = &DSP3_OP1C_B;
+}
+
+
+void DSP3_OP1C( void )
+{
+ // absorb 2 bytes
+
+ SetDSP3 = &DSP3_OP1C_A;
+}
+
+
+void DSP3_Command( void )
+{
+ if (DSP3_DR < 0x40)
+ {
+ switch (DSP3_DR)
+ {
+ case 0x02: SetDSP3 = &DSP3_Coordinate; break;
+ case 0x03: SetDSP3 = &DSP3_OP03; break;
+ case 0x06: SetDSP3 = &DSP3_OP06; break;
+ case 0x07: SetDSP3 = &DSP3_OP07; return;
+ case 0x0c: SetDSP3 = &DSP3_OP0C; break;
+ case 0x0f: SetDSP3 = &DSP3_TestMemory; break;
+ case 0x10: SetDSP3 = &DSP3_OP10; break;
+ case 0x18: SetDSP3 = &DSP3_Convert; break;
+ case 0x1c: SetDSP3 = &DSP3_OP1C; break;
+ case 0x1e: SetDSP3 = &DSP3_OP1E; break;
+ case 0x1f: SetDSP3 = &DSP3_MemoryDump; break;
+ case 0x38: SetDSP3 = &DSP3_Decode; break;
+ case 0x3e: SetDSP3 = &DSP3_OP3E; break;
+ default:
+ return;
+ }
+ DSP3_SR = 0x0080;
+ DSP3_Index = 0;
+ }
+}
+
+
+void DSP3_write( UINT16 dsp3_address, UINT8 dsp3_byte )
+{
+ if (dsp3_address < 0xC000)
+ {
+ if (DSP3_SR & 0x04)
+ {
+ DSP3_DR = (DSP3_DR & 0xff00) + dsp3_byte;
+ (*SetDSP3)();
+ }
+ else
+ {
+ DSP3_SR ^= 0x10;
+
+ if (DSP3_SR & 0x10)
+ DSP3_DR = (DSP3_DR & 0xff00) + dsp3_byte;
+ else
+ {
+ DSP3_DR = (DSP3_DR & 0x00ff) + (dsp3_byte << 8);
+ (*SetDSP3)();
+ }
+ }
+ }
+}
+
+UINT8 DSP3_read( UINT16 dsp3_address )
+{
+ UINT8 value;
+ if (dsp3_address < 0xC000)
+ {
+ if (DSP3_SR & 0x04)
+ {
+ value = (UINT8) DSP3_DR;
+ (*SetDSP3)();
+ }
+ else
+ {
+ DSP3_SR ^= 0x10;
+
+ if (DSP3_SR & 0x10)
+ value = (UINT8) (DSP3_DR);
+ else
+ {
+ value = (UINT8) (DSP3_DR >> 8);
+ (*SetDSP3)();
+ }
+ }
+
+ }
+ else
+ {
+ value = (UINT8) DSP3_SR;
+ }
+
+ return value;
+}
+
+void InitDSP3( void )
+{
+ DSP3_Reset();
+}
diff --git a/src/mame/machine/snesdsp4.c b/src/mame/machine/snesdsp4.c
new file mode 100644
index 00000000000..a9aee95ee49
--- /dev/null
+++ b/src/mame/machine/snesdsp4.c
@@ -0,0 +1,2179 @@
+/************************************************************************
+
+ DSP-4 emulator code
+
+ Copyright (c) 2004-2009 Dreamer Nom, John Weidman, Kris Bleakley,
+ Nach, z80 gaiden, and Jonas Quinn
+
+ This code is released by ZSNES Team under GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ The implementation below is released under the MAME license for use
+ in MAME, MESS and derivatives by permission of the authors.
+
+************************************************************************/
+
+
+/*
+Due recognition and credit are given on Overload's DSP website.
+Thank those contributors for their hard work on this chip.
+
+
+Fixed-point math reminder:
+
+[sign, integer, fraction]
+1.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0')
+1.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0')
+*/
+
+#include "snesdsp4.h"
+
+struct DSP4_t DSP4;
+struct DSP4_vars_t DSP4_vars;
+
+INLINE UINT16 READ_WORD(UINT8 *addr)
+{
+ return (addr[0]) + (addr[1] << 8);
+}
+
+INLINE UINT32 READ_DWORD(UINT8 *addr)
+{
+ return (addr[0]) + (addr[1] << 8) + (addr[2] << 16) + (addr[3] << 24);
+}
+
+INLINE void WRITE_WORD(UINT8 *addr, UINT16 data)
+{
+ addr[0] = data;
+ addr[1] = data >> 8;
+}
+
+
+//////////////////////////////////////////////////////////////
+
+// input protocol
+
+static INT16 DSP4_READ_WORD( void )
+{
+ INT16 out;
+
+ out = READ_WORD(DSP4.parameters + DSP4.in_index);
+ DSP4.in_index += 2;
+
+ return out;
+}
+
+static INT32 DSP4_READ_DWORD( void )
+{
+ INT32 out;
+
+ out = READ_DWORD(DSP4.parameters + DSP4.in_index);
+ DSP4.in_index += 4;
+
+ return out;
+}
+
+
+//////////////////////////////////////////////////////////////
+
+// output protocol
+
+#define DSP4_CLEAR_OUT() \
+{ DSP4.out_count = 0; DSP4.out_index = 0; }
+
+#define DSP4_WRITE_BYTE( d ) \
+{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count++; }
+
+#define DSP4_WRITE_WORD( d ) \
+{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count += 2; }
+
+#ifndef MSB_FIRST
+#define DSP4_WRITE_16_WORD( d ) \
+{ memcpy(DSP4.output + DSP4.out_count, ( d ), 32); DSP4.out_count += 32; }
+#else
+#define DSP4_WRITE_16_WORD( d ) \
+{ INT16 *p = ( d ), *end = ( d )+16; \
+ for (; p != end; p++) \
+ { \
+ WRITE_WORD( DSP4.output + DSP4.out_count, *p ); \
+ } \
+ DSP4.out_count += 32; \
+}
+#endif
+
+#ifdef PRINT_OP
+#define DSP4_WRITE_DEBUG( x, d ) \
+ WRITE_WORD( nop + x, d );
+#endif
+
+#ifdef DEBUG_DSP
+#define DSP4_WRITE_DEBUG( x, d ) \
+ WRITE_WORD( nop + x, d );
+#endif
+
+//////////////////////////////////////////////////////////////
+
+// used to wait for dsp i/o
+
+#define DSP4_WAIT( x ) \
+ DSP4.in_index = 0; DSP4_vars.DSP4_Logic = x; return;
+
+//////////////////////////////////////////////////////////////
+
+// 1.7.8 -> 1.15.16
+#define SEX78( a ) ( ( (INT32) ( (INT16) (a) ) ) << 8 )
+
+// 1.15.0 -> 1.15.16
+#define SEX16( a ) ( ( (INT32) ( (INT16) (a) ) ) << 16 )
+
+#ifdef PRINT_OP
+#define U16( a ) ( (UINT16) ( a ) )
+#endif
+
+#ifdef DEBUG_DSP
+#define U16( a ) ( (UINT16) ( a ) )
+#endif
+
+//////////////////////////////////////////////////////////////
+
+// Attention: This lookup table is not verified
+static const UINT16 div_lut[64] = { 0x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249, 0x1000, 0x0e38,
+ 0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888, 0x0800, 0x0787, 0x071c, 0x06bc,
+ 0x0666, 0x0618, 0x05d1, 0x0590, 0x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469,
+ 0x0444, 0x0421, 0x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348,
+ 0x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9, 0x02aa, 0x029c,
+ 0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253, 0x0249, 0x023e, 0x0234, 0x022b,
+ 0x0222, 0x0219, 0x0210, 0x0208, };
+INT16 DSP4_Inverse(INT16 value)
+{
+ // saturate bounds
+ if (value < 0)
+ {
+ value = 0;
+ }
+ if (value > 63)
+ {
+ value = 63;
+ }
+
+ return div_lut[value];
+}
+
+//////////////////////////////////////////////////////////////
+
+// Prototype
+void DSP4_OP0B(UINT8 *draw, INT16 sp_x, INT16 sp_y, INT16 sp_attr, UINT8 size, UINT8 stop);
+
+//////////////////////////////////////////////////////////////
+
+// OP00
+void DSP4_Multiply(INT16 Multiplicand, INT16 Multiplier, INT32 *Product)
+{
+ *Product = (Multiplicand * Multiplier << 1) >> 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+
+void DSP4_OP01( void )
+{
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ case 3:
+ goto resume3; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // process initial inputs
+
+ // sort inputs
+ DSP4_vars.world_y = DSP4_READ_DWORD();
+ DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
+ DSP4_vars.viewport_bottom = DSP4_READ_WORD();
+ DSP4_vars.world_x = DSP4_READ_DWORD();
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
+ DSP4_vars.world_yofs = DSP4_READ_WORD();
+ DSP4_vars.world_dy = DSP4_READ_DWORD();
+ DSP4_vars.world_dx = DSP4_READ_DWORD();
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_READ_WORD(); // 0x0000
+ DSP4_vars.world_xenv = DSP4_READ_DWORD();
+ DSP4_vars.world_ddy = DSP4_READ_WORD();
+ DSP4_vars.world_ddx = DSP4_READ_WORD();
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // initial (x,y,offset) at starting DSP4_vars.raster line
+ DSP4_vars.view_x1 = (INT16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16);
+ DSP4_vars.view_y1 = (INT16)(DSP4_vars.world_y >> 16);
+ DSP4_vars.view_xofs1 = (INT16)(DSP4_vars.world_x >> 16);
+ DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
+ DSP4_vars.view_turnoff_x = 0;
+ DSP4_vars.view_turnoff_dx = 0;
+
+ // first DSP4_vars.raster line
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
+
+ do
+ {
+ ////////////////////////////////////////////////////
+ // process one iteration of projection
+
+ // perspective projection of world (x,y,scroll) points
+ // based on the current projection lines
+ DSP4_vars.view_x2 = (INT16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ));
+ DSP4_vars.view_y2 = (INT16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15);
+ DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
+ DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
+
+
+ // 1. World x-location before transformation
+ // 2. Viewer x-position at the next
+ // 3. World y-location before perspective projection
+ // 4. Viewer y-position below the horizon
+ // 5. Number of DSP4_vars.raster lines drawn in this iteration
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD((UINT16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16));
+ DSP4_WRITE_WORD(DSP4_vars.view_x2);
+ DSP4_WRITE_WORD((UINT16)(DSP4_vars.world_y >> 16));
+ DSP4_WRITE_WORD(DSP4_vars.view_y2);
+
+ //////////////////////////////////////////////////////
+
+ // SR = 0x00
+
+ // determine # of DSP4_vars.raster lines used
+ DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2;
+
+ // prevent overdraw
+ if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
+ DSP4_vars.segments = 0;
+ else
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
+
+ // don't draw outside the window
+ if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
+ {
+ DSP4_vars.segments = 0;
+
+ // flush remaining DSP4_vars.raster lines
+ if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
+ }
+
+ // SR = 0x80
+
+ DSP4_WRITE_WORD(DSP4_vars.segments);
+
+ //////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ INT32 px_dx, py_dy;
+ INT32 x_scroll, y_scroll;
+
+ // SR = 0x00
+
+ // linear interpolation (lerp) between projected points
+ px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+
+ // starting step values
+ x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
+ y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
+
+ // SR = 0x80
+
+ // rasterize line
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
+ {
+ // 1. HDMA memory pointer (bg1)
+ // 2. vertical scroll offset ($210E)
+ // 3. horizontal scroll offset ($210D)
+
+ DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
+ DSP4_WRITE_WORD((UINT16)((y_scroll + 0x8000) >> 16));
+ DSP4_WRITE_WORD((UINT16)((x_scroll + 0x8000) >> 16));
+
+
+ // update memory address
+ DSP4_vars.poly_ptr[0][0] -= 4;
+
+ // update screen values
+ x_scroll += px_dx;
+ y_scroll += py_dy;
+ }
+ }
+
+ ////////////////////////////////////////////////////
+ // Post-update
+
+ // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
+ DSP4_vars.view_x1 = DSP4_vars.view_x2;
+ DSP4_vars.view_y1 = DSP4_vars.view_y2;
+ DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
+ DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
+
+ // add deltas for projection lines
+ DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx);
+ DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy);
+
+ // update projection lines
+ DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv);
+ DSP4_vars.world_y += DSP4_vars.world_dy;
+
+ // update road turnoff position
+ DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
+
+ ////////////////////////////////////////////////////
+ // command check
+
+ // scan next command
+ DSP4.in_count = 2;
+ DSP4_WAIT(1) resume1 :
+
+ // check for termination
+ DSP4_vars.distance = DSP4_READ_WORD();
+ if (DSP4_vars.distance == -0x8000)
+ break;
+
+ // road turnoff
+ if( (UINT16) DSP4_vars.distance == 0x8001 )
+ {
+ DSP4.in_count = 6;
+ DSP4_WAIT(2) resume2:
+
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_vars.view_turnoff_x = DSP4_READ_WORD();
+ DSP4_vars.view_turnoff_dx = DSP4_READ_WORD();
+
+ // factor in new changes
+ DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
+ DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
+
+ // update stepping values
+ DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
+
+ DSP4.in_count = 2;
+ DSP4_WAIT(1)
+ }
+
+ // already have 2 bytes read
+ DSP4.in_count = 6;
+ DSP4_WAIT(3) resume3 :
+
+ // inspect inputs
+ DSP4_vars.world_ddy = DSP4_READ_WORD();
+ DSP4_vars.world_ddx = DSP4_READ_WORD();
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // no envelope here
+ DSP4_vars.world_xenv = 0;
+ }
+ while (1);
+
+ // terminate op
+ DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+
+void DSP4_OP03( void )
+{
+ DSP4_vars.OAM_RowMax = 33;
+ memset(DSP4_vars.OAM_Row, 0, 64);
+}
+
+
+//////////////////////////////////////////////////////////////
+
+
+void DSP4_OP05( void )
+{
+ DSP4_vars.OAM_index = 0;
+ DSP4_vars.OAM_bits = 0;
+ memset(DSP4_vars.OAM_attr, 0, 32);
+ DSP4_vars.sprite_count = 0;
+}
+
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP06( void )
+{
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_16_WORD(DSP4_vars.OAM_attr);
+}
+
+//////////////////////////////////////////////////////////////
+
+
+void DSP4_OP07( void )
+{
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // sort inputs
+
+ DSP4_vars.world_y = DSP4_READ_DWORD();
+ DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
+ DSP4_vars.viewport_bottom = DSP4_READ_WORD();
+ DSP4_vars.world_x = DSP4_READ_DWORD();
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
+ DSP4_vars.world_yofs = DSP4_READ_WORD();
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_vars.view_y2 = DSP4_READ_WORD();
+ DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_x2 = DSP4_READ_WORD();
+ DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // initial (x,y,offset) at starting DSP4_vars.raster line
+ DSP4_vars.view_x1 = (INT16)(DSP4_vars.world_x >> 16);
+ DSP4_vars.view_y1 = (INT16)(DSP4_vars.world_y >> 16);
+ DSP4_vars.view_xofs1 = DSP4_vars.view_x1;
+ DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
+
+ // first DSP4_vars.raster line
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
+
+
+ do
+ {
+ ////////////////////////////////////////////////////
+ // process one iteration of projection
+
+ // add shaping
+ DSP4_vars.view_x2 += DSP4_vars.view_dx;
+ DSP4_vars.view_y2 += DSP4_vars.view_dy;
+
+ // vertical scroll calculation
+ DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
+ DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
+
+ // 1. Viewer x-position at the next
+ // 2. Viewer y-position below the horizon
+ // 3. Number of DSP4_vars.raster lines drawn in this iteration
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(DSP4_vars.view_x2);
+ DSP4_WRITE_WORD(DSP4_vars.view_y2);
+
+ //////////////////////////////////////////////////////
+
+ // SR = 0x00
+
+ // determine # of DSP4_vars.raster lines used
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
+
+ // prevent overdraw
+ if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
+ DSP4_vars.segments = 0;
+ else
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
+
+ // don't draw outside the window
+ if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
+ {
+ DSP4_vars.segments = 0;
+
+ // flush remaining DSP4_vars.raster lines
+ if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
+ }
+
+ // SR = 0x80
+
+ DSP4_WRITE_WORD(DSP4_vars.segments);
+
+ //////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ INT32 px_dx, py_dy;
+ INT32 x_scroll, y_scroll;
+
+ // SR = 0x00
+
+ // linear interpolation (lerp) between projected points
+ px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+
+ // starting step values
+ x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
+ y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
+
+ // SR = 0x80
+
+ // rasterize line
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
+ {
+ // 1. HDMA memory pointer (bg2)
+ // 2. vertical scroll offset ($2110)
+ // 3. horizontal scroll offset ($210F)
+
+ DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
+ DSP4_WRITE_WORD((UINT16)((y_scroll + 0x8000) >> 16));
+ DSP4_WRITE_WORD((UINT16)((x_scroll + 0x8000) >> 16));
+
+ // update memory address
+ DSP4_vars.poly_ptr[0][0] -= 4;
+
+ // update screen values
+ x_scroll += px_dx;
+ y_scroll += py_dy;
+ }
+ }
+
+ /////////////////////////////////////////////////////
+ // Post-update
+
+ // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
+ DSP4_vars.view_x1 = DSP4_vars.view_x2;
+ DSP4_vars.view_y1 = DSP4_vars.view_y2;
+ DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
+ DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
+
+ ////////////////////////////////////////////////////
+ // command check
+
+ // scan next command
+ DSP4.in_count = 2;
+ DSP4_WAIT(1) resume1 :
+
+ // check for opcode termination
+ DSP4_vars.distance = DSP4_READ_WORD();
+ if (DSP4_vars.distance == -0x8000)
+ break;
+
+ // already have 2 bytes in queue
+ DSP4.in_count = 10;
+ DSP4_WAIT(2) resume2 :
+
+ // inspect inputs
+ DSP4_vars.view_y2 = DSP4_READ_WORD();
+ DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_x2 = DSP4_READ_WORD();
+ DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+ }
+ while (1);
+
+ DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP08( void )
+{
+ INT16 win_left, win_right;
+ INT16 view_x[2], view_y[2];
+ INT16 envelope[2][2];
+
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // process initial inputs for two polygons
+
+ // clip values
+ DSP4_vars.poly_clipRt[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_clipRt[0][1] = DSP4_READ_WORD();
+ DSP4_vars.poly_clipRt[1][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_clipRt[1][1] = DSP4_READ_WORD();
+
+ DSP4_vars.poly_clipLf[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_clipLf[0][1] = DSP4_READ_WORD();
+ DSP4_vars.poly_clipLf[1][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_clipLf[1][1] = DSP4_READ_WORD();
+
+ // unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6)
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+
+ // unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7)
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+
+ // polygon centering (left,right)
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[0][1] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][1] = DSP4_READ_WORD();
+
+ // HDMA pointer locations
+ DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[0][1] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[1][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[1][1] = DSP4_READ_WORD();
+
+ // starting DSP4_vars.raster line below the horizon
+ DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_bottom[0][1] = DSP4_READ_WORD();
+ DSP4_vars.poly_bottom[1][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_bottom[1][1] = DSP4_READ_WORD();
+
+ // top boundary line to clip
+ DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[0][1] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[1][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[1][1] = DSP4_READ_WORD();
+
+ // unknown
+ // (ex. 1P = $2FC8, $0034, $FF5C, $0035)
+ //
+ // (ex. 2P = $3178, $0034, $FFCC, $0035)
+ // (ex. 2P = $2FC8, $0034, $FFCC, $0035)
+
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+ DSP4_READ_WORD();
+
+ // look at guidelines for both polygon shapes
+ DSP4_vars.distance = DSP4_READ_WORD();
+ view_x[0] = DSP4_READ_WORD();
+ view_y[0] = DSP4_READ_WORD();
+ view_x[1] = DSP4_READ_WORD();
+ view_y[1] = DSP4_READ_WORD();
+
+ // envelope shaping guidelines (one frame only)
+ envelope[0][0] = DSP4_READ_WORD();
+ envelope[0][1] = DSP4_READ_WORD();
+ envelope[1][0] = DSP4_READ_WORD();
+ envelope[1][1] = DSP4_READ_WORD();
+
+ // starting base values to project from
+ DSP4_vars.poly_start[0] = view_x[0];
+ DSP4_vars.poly_start[1] = view_x[1];
+
+ // starting DSP4_vars.raster lines to begin drawing
+ DSP4_vars.poly_raster[0][0] = view_y[0];
+ DSP4_vars.poly_raster[0][1] = view_y[0];
+ DSP4_vars.poly_raster[1][0] = view_y[1];
+ DSP4_vars.poly_raster[1][1] = view_y[1];
+
+ // starting distances
+ DSP4_vars.poly_plane[0] = DSP4_vars.distance;
+ DSP4_vars.poly_plane[1] = DSP4_vars.distance;
+
+ // SR = 0x00
+
+ // re-center coordinates
+ win_left = DSP4_vars.poly_cx[0][0] - view_x[0] + envelope[0][0];
+ win_right = DSP4_vars.poly_cx[0][1] - view_x[0] + envelope[0][1];
+
+ // saturate offscreen data for polygon #1
+ if (win_left < DSP4_vars.poly_clipLf[0][0])
+ {
+ win_left = DSP4_vars.poly_clipLf[0][0];
+ }
+ if (win_left > DSP4_vars.poly_clipRt[0][0])
+ {
+ win_left = DSP4_vars.poly_clipRt[0][0];
+ }
+ if (win_right < DSP4_vars.poly_clipLf[0][1])
+ {
+ win_right = DSP4_vars.poly_clipLf[0][1];
+ }
+ if (win_right > DSP4_vars.poly_clipRt[0][1])
+ {
+ win_right = DSP4_vars.poly_clipRt[0][1];
+ }
+
+ // SR = 0x80
+
+ // initial output for polygon #1
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_BYTE(win_left & 0xff);
+ DSP4_WRITE_BYTE(win_right & 0xff);
+
+
+ do
+ {
+ INT16 polygon;
+ ////////////////////////////////////////////////////
+ // command check
+
+ // scan next command
+ DSP4.in_count = 2;
+ DSP4_WAIT(1) resume1 :
+
+ // terminate op
+ DSP4_vars.distance = DSP4_READ_WORD();
+ if (DSP4_vars.distance == -0x8000)
+ break;
+
+ // already have 2 bytes in queue
+ DSP4.in_count = 16;
+
+ DSP4_WAIT(2) resume2 :
+
+ // look at guidelines for both polygon shapes
+ view_x[0] = DSP4_READ_WORD();
+ view_y[0] = DSP4_READ_WORD();
+ view_x[1] = DSP4_READ_WORD();
+ view_y[1] = DSP4_READ_WORD();
+
+ // envelope shaping guidelines (one frame only)
+ envelope[0][0] = DSP4_READ_WORD();
+ envelope[0][1] = DSP4_READ_WORD();
+ envelope[1][0] = DSP4_READ_WORD();
+ envelope[1][1] = DSP4_READ_WORD();
+
+ ////////////////////////////////////////////////////
+ // projection begins
+
+ // init
+ DSP4_CLEAR_OUT();
+
+
+ //////////////////////////////////////////////
+ // solid polygon renderer - 2 shapes
+
+ for (polygon = 0; polygon < 2; polygon++)
+ {
+ INT32 left_inc, right_inc;
+ INT16 x1_final, x2_final;
+ INT16 env[2][2];
+ INT16 poly;
+
+ // SR = 0x00
+
+ // # DSP4_vars.raster lines to draw
+ DSP4_vars.segments = DSP4_vars.poly_raster[polygon][0] - view_y[polygon];
+
+ // prevent overdraw
+ if (DSP4_vars.segments > 0)
+ {
+ // bump drawing cursor
+ DSP4_vars.poly_raster[polygon][0] = view_y[polygon];
+ DSP4_vars.poly_raster[polygon][1] = view_y[polygon];
+ }
+ else
+ DSP4_vars.segments = 0;
+
+ // don't draw outside the window
+ if (view_y[polygon] < DSP4_vars.poly_top[polygon][0])
+ {
+ DSP4_vars.segments = 0;
+
+ // flush remaining DSP4_vars.raster lines
+ if (view_y[polygon] >= DSP4_vars.poly_top[polygon][0])
+ DSP4_vars.segments = view_y[polygon] - DSP4_vars.poly_top[polygon][0];
+ }
+
+ // SR = 0x80
+
+ // tell user how many DSP4_vars.raster structures to read in
+ DSP4_WRITE_WORD(DSP4_vars.segments);
+
+ // normal parameters
+ poly = polygon;
+
+ /////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ INT32 win_left, win_right;
+
+ // road turnoff selection
+ if( (UINT16) envelope[ polygon ][ 0 ] == (UINT16) 0xc001 )
+ poly = 1;
+ else if( envelope[ polygon ][ 1 ] == 0x3fff )
+ poly = 1;
+
+ ///////////////////////////////////////////////
+ // left side of polygon
+
+ // perspective correction on additional shaping parameters
+ env[0][0] = envelope[polygon][0] * DSP4_vars.poly_plane[poly] >> 15;
+ env[0][1] = envelope[polygon][0] * DSP4_vars.distance >> 15;
+
+ // project new shapes (left side)
+ x1_final = view_x[poly] + env[0][0];
+ x2_final = DSP4_vars.poly_start[poly] + env[0][1];
+
+ // interpolate between projected points with shaping
+ left_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ if (DSP4_vars.segments == 1)
+ left_inc = -left_inc;
+
+ ///////////////////////////////////////////////
+ // right side of polygon
+
+ // perspective correction on additional shaping parameters
+ env[1][0] = envelope[polygon][1] * DSP4_vars.poly_plane[poly] >> 15;;
+ env[1][1] = envelope[polygon][1] * DSP4_vars.distance >> 15;
+
+ // project new shapes (right side)
+ x1_final = view_x[poly] + env[1][0];
+ x2_final = DSP4_vars.poly_start[poly] + env[1][1];
+
+
+ // interpolate between projected points with shaping
+ right_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ if (DSP4_vars.segments == 1)
+ right_inc = -right_inc;
+
+ ///////////////////////////////////////////////
+ // update each point on the line
+
+ win_left = SEX16(DSP4_vars.poly_cx[polygon][0] - DSP4_vars.poly_start[poly] + env[0][0]);
+ win_right = SEX16(DSP4_vars.poly_cx[polygon][1] - DSP4_vars.poly_start[poly] + env[1][0]);
+
+ // update DSP4_vars.distance drawn into world
+ DSP4_vars.poly_plane[polygon] = DSP4_vars.distance;
+
+ // rasterize line
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
+ {
+ INT16 x_left, x_right;
+
+ // project new coordinates
+ win_left += left_inc;
+ win_right += right_inc;
+
+ // grab integer portion, drop fraction (no rounding)
+ x_left = (INT16)(win_left >> 16);
+ x_right = (INT16)(win_right >> 16);
+
+ // saturate offscreen data
+ if (x_left < DSP4_vars.poly_clipLf[polygon][0])
+ x_left = DSP4_vars.poly_clipLf[polygon][0];
+ if (x_left > DSP4_vars.poly_clipRt[polygon][0])
+ x_left = DSP4_vars.poly_clipRt[polygon][0];
+ if (x_right < DSP4_vars.poly_clipLf[polygon][1])
+ x_right = DSP4_vars.poly_clipLf[polygon][1];
+ if (x_right > DSP4_vars.poly_clipRt[polygon][1])
+ x_right = DSP4_vars.poly_clipRt[polygon][1];
+
+ // 1. HDMA memory pointer
+ // 2. Left window position ($2126/$2128)
+ // 3. Right window position ($2127/$2129)
+
+ DSP4_WRITE_WORD(DSP4_vars.poly_ptr[polygon][0]);
+ DSP4_WRITE_BYTE(x_left & 0xff);
+ DSP4_WRITE_BYTE(x_right & 0xff);
+
+
+ // update memory pointers
+ DSP4_vars.poly_ptr[polygon][0] -= 4;
+ DSP4_vars.poly_ptr[polygon][1] -= 4;
+ } // end rasterize line
+ }
+
+ ////////////////////////////////////////////////
+ // Post-update
+
+ // new projection spot to continue rasterizing from
+ DSP4_vars.poly_start[polygon] = view_x[poly];
+ } // end polygon rasterizer
+ }
+ while (1);
+
+ // unknown output
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(0);
+
+
+ DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP09( void )
+{
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ case 3:
+ goto resume3; break;
+ case 4:
+ goto resume4; break;
+ case 5:
+ goto resume5; break;
+ case 6:
+ goto resume6; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // process initial inputs
+
+ // grab screen information
+ DSP4_vars.viewport_cx = DSP4_READ_WORD();
+ DSP4_vars.viewport_cy = DSP4_READ_WORD();
+ DSP4_READ_WORD(); // 0x0000
+ DSP4_vars.viewport_left = DSP4_READ_WORD();
+ DSP4_vars.viewport_right = DSP4_READ_WORD();
+ DSP4_vars.viewport_top = DSP4_READ_WORD();
+ DSP4_vars.viewport_bottom = DSP4_READ_WORD();
+
+ // starting DSP4_vars.raster line below the horizon
+ DSP4_vars.poly_bottom[0][0] = DSP4_vars.viewport_bottom - DSP4_vars.viewport_cy;
+ DSP4_vars.poly_raster[0][0] = 0x100;
+
+ do
+ {
+ ////////////////////////////////////////////////////
+ // check for new sprites
+
+ DSP4.in_count = 4;
+ DSP4_WAIT(1) resume1 :
+
+ ////////////////////////////////////////////////
+ // DSP4_vars.raster overdraw check
+
+ DSP4_vars.raster = DSP4_READ_WORD();
+
+ // continue updating the DSP4_vars.raster line where overdraw begins
+ if (DSP4_vars.raster < DSP4_vars.poly_raster[0][0])
+ {
+ DSP4_vars.sprite_clipy = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster);
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.raster;
+ }
+
+ /////////////////////////////////////////////////
+ // identify sprite
+
+ // op termination
+ DSP4_vars.distance = DSP4_READ_WORD();
+ if (DSP4_vars.distance == -0x8000)
+ goto terminate;
+
+
+ // no sprite
+ if (DSP4_vars.distance == 0x0000)
+ {
+ continue;
+ }
+
+ ////////////////////////////////////////////////////
+ // process projection information
+
+ // vehicle sprite
+ if ((UINT16) DSP4_vars.distance == 0x9000)
+ {
+ INT16 car_left, car_right, car_back;
+ INT16 impact_left, impact_back;
+ INT16 world_spx, world_spy;
+ INT16 view_spx, view_spy;
+ UINT16 energy;
+
+ // we already have 4 bytes we want
+ DSP4.in_count = 14;
+ DSP4_WAIT(2) resume2 :
+
+ // filter inputs
+ energy = DSP4_READ_WORD();
+ impact_back = DSP4_READ_WORD();
+ car_back = DSP4_READ_WORD();
+ impact_left = DSP4_READ_WORD();
+ car_left = DSP4_READ_WORD();
+ DSP4_vars.distance = DSP4_READ_WORD();
+ car_right = DSP4_READ_WORD();
+
+ // calculate car's world (x,y) values
+ world_spx = car_right - car_left;
+ world_spy = car_back;
+
+ // add in collision vector [needs bit-twiddling]
+ world_spx -= energy * (impact_left - car_left) >> 16;
+ world_spy -= energy * (car_back - impact_back) >> 16;
+
+ // perspective correction for world (x,y)
+ view_spx = world_spx * DSP4_vars.distance >> 15;
+ view_spy = world_spy * DSP4_vars.distance >> 15;
+
+ // convert to screen values
+ DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx;
+ DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - view_spy);
+
+ // make the car's (x)-coordinate available
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(world_spx);
+
+ // grab a few remaining vehicle values
+ DSP4.in_count = 4;
+ DSP4_WAIT(3) resume3 :
+
+ // add vertical lift factor
+ DSP4_vars.sprite_y += DSP4_READ_WORD();
+ }
+ // terrain sprite
+ else
+ {
+ INT16 world_spx, world_spy;
+ INT16 view_spx, view_spy;
+
+ // we already have 4 bytes we want
+ DSP4.in_count = 10;
+ DSP4_WAIT(4) resume4 :
+
+ // sort loop inputs
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_raster[0][1] = DSP4_READ_WORD();
+ world_spx = DSP4_READ_WORD();
+ world_spy = DSP4_READ_WORD();
+
+ // compute base DSP4_vars.raster line from the bottom
+ DSP4_vars.segments = DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster;
+
+ // perspective correction for world (x,y)
+ view_spx = world_spx * DSP4_vars.distance >> 15;
+ view_spy = world_spy * DSP4_vars.distance >> 15;
+
+ // convert to screen values
+ DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx - DSP4_vars.poly_cx[0][0];
+ DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - DSP4_vars.segments + view_spy;
+ }
+
+ // default sprite size: 16x16
+ DSP4_vars.sprite_size = 1;
+ DSP4_vars.sprite_attr = DSP4_READ_WORD();
+
+ ////////////////////////////////////////////////////
+ // convert tile data to SNES OAM format
+
+ do
+ {
+ UINT16 header;
+
+ INT16 sp_x, sp_y, sp_attr, sp_dattr;
+ INT16 sp_dx, sp_dy;
+ INT16 pixels;
+
+ UINT8 draw;
+
+ DSP4.in_count = 2;
+ DSP4_WAIT(5) resume5 :
+
+ draw = 1;
+
+ // opcode termination
+ DSP4_vars.raster = DSP4_READ_WORD();
+ if (DSP4_vars.raster == -0x8000)
+ goto terminate;
+
+ // stop code
+ if (DSP4_vars.raster == 0x0000 && !DSP4_vars.sprite_size)
+ break;
+
+ // toggle sprite size
+ if (DSP4_vars.raster == 0x0000)
+ {
+ DSP4_vars.sprite_size = !DSP4_vars.sprite_size;
+ continue;
+ }
+
+ // check for valid sprite header
+ header = DSP4_vars.raster;
+ header >>= 8;
+ if (header != 0x20 &&
+ header != 0x2e && //This is for attractor sprite
+ header != 0x40 &&
+ header != 0x60 &&
+ header != 0xa0 &&
+ header != 0xc0 &&
+ header != 0xe0)
+ break;
+
+ // read in rest of sprite data
+ DSP4.in_count = 4;
+ DSP4_WAIT(6) resume6 :
+
+ draw = 1;
+
+ /////////////////////////////////////
+ // process tile data
+
+ // sprite deltas
+ sp_dattr = DSP4_vars.raster;
+ sp_dy = DSP4_READ_WORD();
+ sp_dx = DSP4_READ_WORD();
+
+ // update coordinates to screen space
+ sp_x = DSP4_vars.sprite_x + sp_dx;
+ sp_y = DSP4_vars.sprite_y + sp_dy;
+
+ // update sprite nametable/attribute information
+ sp_attr = DSP4_vars.sprite_attr + sp_dattr;
+
+ // allow partially visibile tiles
+ pixels = DSP4_vars.sprite_size ? 15 : 7;
+
+ DSP4_CLEAR_OUT();
+
+ // transparent tile to clip off parts of a sprite (overdraw)
+ if (DSP4_vars.sprite_clipy - pixels <= sp_y &&
+ sp_y <= DSP4_vars.sprite_clipy &&
+ sp_x >= DSP4_vars.viewport_left - pixels &&
+ sp_x <= DSP4_vars.viewport_right &&
+ DSP4_vars.sprite_clipy >= DSP4_vars.viewport_top - pixels &&
+ DSP4_vars.sprite_clipy <= DSP4_vars.viewport_bottom)
+ {
+ DSP4_OP0B(&draw, sp_x, DSP4_vars.sprite_clipy, 0x00EE, DSP4_vars.sprite_size, 0);
+ }
+
+
+ // normal sprite tile
+ if (sp_x >= DSP4_vars.viewport_left - pixels &&
+ sp_x <= DSP4_vars.viewport_right &&
+ sp_y >= DSP4_vars.viewport_top - pixels &&
+ sp_y <= DSP4_vars.viewport_bottom &&
+ sp_y <= DSP4_vars.sprite_clipy)
+ {
+ DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4_vars.sprite_size, 0);
+ }
+
+
+ // no following OAM data
+ DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1);
+ }
+ while (1);
+ }
+ while (1);
+
+ terminate : DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+const UINT16 OP0A_Values[16] = { 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150, 0xfe80,
+ 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0 };
+
+void DSP4_OP0A(INT16 n2, INT16 *o1, INT16 *o2, INT16 *o3, INT16 *o4)
+{
+ *o4 = OP0A_Values[(n2 & 0x000f)];
+ *o3 = OP0A_Values[(n2 & 0x00f0) >> 4];
+ *o2 = OP0A_Values[(n2 & 0x0f00) >> 8];
+ *o1 = OP0A_Values[(n2 & 0xf000) >> 12];
+}
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP0B(UINT8 *draw, INT16 sp_x, INT16 sp_y, INT16 sp_attr, UINT8 size, UINT8 stop)
+{
+ INT16 Row1, Row2;
+
+ // SR = 0x00
+
+ // align to nearest 8-pixel row
+ Row1 = (sp_y >> 3) & 0x1f;
+ Row2 = (Row1 + 1) & 0x1f;
+
+ // check boundaries
+ if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb)))
+ {
+ *draw = 0;
+ }
+ if (size)
+ {
+ if (DSP4_vars.OAM_Row[Row1] + 1 >= DSP4_vars.OAM_RowMax)
+ *draw = 0;
+ if (DSP4_vars.OAM_Row[Row2] + 1 >= DSP4_vars.OAM_RowMax)
+ *draw = 0;
+ }
+ else
+ {
+ if (DSP4_vars.OAM_Row[Row1] >= DSP4_vars.OAM_RowMax)
+ {
+ *draw = 0;
+ }
+ }
+
+ // emulator fail-safe (unknown if this really exists)
+ if (DSP4_vars.sprite_count >= 128)
+ {
+ *draw = 0;
+ }
+
+ // SR = 0x80
+
+ if (*draw)
+ {
+ // Row tiles
+ if (size)
+ {
+ DSP4_vars.OAM_Row[Row1] += 2;
+ DSP4_vars.OAM_Row[Row2] += 2;
+ }
+ else
+ {
+ DSP4_vars.OAM_Row[Row1]++;
+ }
+
+ // yield OAM output
+ DSP4_WRITE_WORD(1);
+
+ // pack OAM data: x,y,name,attr
+ DSP4_WRITE_BYTE(sp_x & 0xff);
+ DSP4_WRITE_BYTE(sp_y & 0xff);
+ DSP4_WRITE_WORD(sp_attr);
+
+ DSP4_vars.sprite_count++;
+
+ // OAM: size,msb data
+ // save post-oam table data for future retrieval
+ DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= ((sp_x <0 || sp_x> 255) << DSP4_vars.OAM_bits);
+ DSP4_vars.OAM_bits++;
+
+ DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= (size << DSP4_vars.OAM_bits);
+ DSP4_vars.OAM_bits++;
+
+ // move to next byte in buffer
+ if (DSP4_vars.OAM_bits == 16)
+ {
+ DSP4_vars.OAM_bits = 0;
+ DSP4_vars.OAM_index++;
+ }
+ }
+ else if (stop)
+ {
+ // yield no OAM output
+ DSP4_WRITE_WORD(0);
+ }
+}
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP0D( void )
+{
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // process initial inputs
+
+ // sort inputs
+ DSP4_vars.world_y = DSP4_READ_DWORD();
+ DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
+ DSP4_vars.viewport_bottom = DSP4_READ_WORD();
+ DSP4_vars.world_x = DSP4_READ_DWORD();
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
+ DSP4_vars.world_yofs = DSP4_READ_WORD();
+ DSP4_vars.world_dy = DSP4_READ_DWORD();
+ DSP4_vars.world_dx = DSP4_READ_DWORD();
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_READ_WORD(); // 0x0000
+ DSP4_vars.world_xenv = SEX78(DSP4_READ_WORD());
+ DSP4_vars.world_ddy = DSP4_READ_WORD();
+ DSP4_vars.world_ddx = DSP4_READ_WORD();
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // initial (x,y,offset) at starting DSP4_vars.raster line
+ DSP4_vars.view_x1 = (INT16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16);
+ DSP4_vars.view_y1 = (INT16)(DSP4_vars.world_y >> 16);
+ DSP4_vars.view_xofs1 = (INT16)(DSP4_vars.world_x >> 16);
+ DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
+
+ // first DSP4_vars.raster line
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
+
+
+ do
+ {
+ ////////////////////////////////////////////////////
+ // process one iteration of projection
+
+ // perspective projection of world (x,y,scroll) points
+ // based on the current projection lines
+ DSP4_vars.view_x2 = (INT16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ));
+ DSP4_vars.view_y2 = (INT16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15);
+ DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
+ DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
+
+ // 1. World x-location before transformation
+ // 2. Viewer x-position at the current
+ // 3. World y-location before perspective projection
+ // 4. Viewer y-position below the horizon
+ // 5. Number of DSP4_vars.raster lines drawn in this iteration
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD((UINT16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16));
+ DSP4_WRITE_WORD(DSP4_vars.view_x2);
+ DSP4_WRITE_WORD((UINT16)(DSP4_vars.world_y >> 16));
+ DSP4_WRITE_WORD(DSP4_vars.view_y2);
+
+ //////////////////////////////////////////////////////////
+
+ // SR = 0x00
+
+ // determine # of DSP4_vars.raster lines used
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
+
+ // prevent overdraw
+ if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
+ DSP4_vars.segments = 0;
+ else
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
+
+ // don't draw outside the window
+ if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
+ {
+ DSP4_vars.segments = 0;
+
+ // flush remaining DSP4_vars.raster lines
+ if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
+ }
+
+ // SR = 0x80
+
+ DSP4_WRITE_WORD(DSP4_vars.segments);
+
+ //////////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ INT32 px_dx, py_dy;
+ INT32 x_scroll, y_scroll;
+
+ // SR = 0x00
+
+ // linear interpolation (lerp) between projected points
+ px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+
+ // starting step values
+ x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
+ y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
+
+ // SR = 0x80
+
+ // rasterize line
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
+ {
+ // 1. HDMA memory pointer (bg1)
+ // 2. vertical scroll offset ($210E)
+ // 3. horizontal scroll offset ($210D)
+
+ DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
+ DSP4_WRITE_WORD((UINT16)((y_scroll + 0x8000) >> 16));
+ DSP4_WRITE_WORD((UINT16)((x_scroll + 0x8000) >> 16));
+
+
+ // update memory address
+ DSP4_vars.poly_ptr[0][0] -= 4;
+
+ // update screen values
+ x_scroll += px_dx;
+ y_scroll += py_dy;
+ }
+ }
+
+ /////////////////////////////////////////////////////
+ // Post-update
+
+ // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
+ DSP4_vars.view_x1 = DSP4_vars.view_x2;
+ DSP4_vars.view_y1 = DSP4_vars.view_y2;
+ DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
+ DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
+
+ // add deltas for projection lines
+ DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx);
+ DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy);
+
+ // update projection lines
+ DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv);
+ DSP4_vars.world_y += DSP4_vars.world_dy;
+
+ ////////////////////////////////////////////////////
+ // command check
+
+ // scan next command
+ DSP4.in_count = 2;
+ DSP4_WAIT(1) resume1 :
+
+ // inspect input
+ DSP4_vars.distance = DSP4_READ_WORD();
+
+ // terminate op
+ if (DSP4_vars.distance == -0x8000)
+ break;
+
+ // already have 2 bytes in queue
+ DSP4.in_count = 6;
+ DSP4_WAIT(2) resume2:
+
+ // inspect inputs
+ DSP4_vars.world_ddy = DSP4_READ_WORD();
+ DSP4_vars.world_ddx = DSP4_READ_WORD();
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // no envelope here
+ DSP4_vars.world_xenv = 0;
+ }
+ while (1);
+
+ DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+
+void DSP4_OP0E( void )
+{
+ DSP4_vars.OAM_RowMax = 16;
+ memset(DSP4_vars.OAM_Row, 0, 64);
+}
+
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP0F( void )
+{
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ case 3:
+ goto resume3; break;
+ case 4:
+ goto resume4; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // process initial inputs
+
+ // sort inputs
+ DSP4_READ_WORD(); // 0x0000
+ DSP4_vars.world_y = DSP4_READ_DWORD();
+ DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
+ DSP4_vars.viewport_bottom = DSP4_READ_WORD();
+ DSP4_vars.world_x = DSP4_READ_DWORD();
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
+ DSP4_vars.world_yofs = DSP4_READ_WORD();
+ DSP4_vars.world_dy = DSP4_READ_DWORD();
+ DSP4_vars.world_dx = DSP4_READ_DWORD();
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_READ_WORD(); // 0x0000
+ DSP4_vars.world_xenv = DSP4_READ_DWORD();
+ DSP4_vars.world_ddy = DSP4_READ_WORD();
+ DSP4_vars.world_ddx = DSP4_READ_WORD();
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // initial (x,y,offset) at starting DSP4_vars.raster line
+ DSP4_vars.view_x1 = (INT16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16);
+ DSP4_vars.view_y1 = (INT16)(DSP4_vars.world_y >> 16);
+ DSP4_vars.view_xofs1 = (INT16)(DSP4_vars.world_x >> 16);
+ DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
+ DSP4_vars.view_turnoff_x = 0;
+ DSP4_vars.view_turnoff_dx = 0;
+
+ // first DSP4_vars.raster line
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
+
+
+ do
+ {
+ ////////////////////////////////////////////////////
+ // process one iteration of projection
+
+ // perspective projection of world (x,y,scroll) points
+ // based on the current projection lines
+ DSP4_vars.view_x2 = (INT16)(((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16) * DSP4_vars.distance >> 15);
+ DSP4_vars.view_y2 = (INT16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15);
+ DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
+ DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
+
+ // 1. World x-location before transformation
+ // 2. Viewer x-position at the next
+ // 3. World y-location before perspective projection
+ // 4. Viewer y-position below the horizon
+ // 5. Number of DSP4_vars.raster lines drawn in this iteration
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD((UINT16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16));
+ DSP4_WRITE_WORD(DSP4_vars.view_x2);
+ DSP4_WRITE_WORD((UINT16)(DSP4_vars.world_y >> 16));
+ DSP4_WRITE_WORD(DSP4_vars.view_y2);
+
+ //////////////////////////////////////////////////////
+
+ // SR = 0x00
+
+ // determine # of DSP4_vars.raster lines used
+ DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2;
+
+ // prevent overdraw
+ if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
+ DSP4_vars.segments = 0;
+ else
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
+
+ // don't draw outside the window
+ if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
+ {
+ DSP4_vars.segments = 0;
+
+ // flush remaining DSP4_vars.raster lines
+ if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
+ }
+
+ // SR = 0x80
+
+ DSP4_WRITE_WORD(DSP4_vars.segments);
+
+ //////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ INT32 px_dx, py_dy;
+ INT32 x_scroll, y_scroll;
+
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++)
+ {
+ // grab inputs
+ DSP4.in_count = 4;
+ DSP4_WAIT(1);
+ resume1 :
+ for (;;)
+ {
+ INT16 distance;
+ INT16 color, red, green, blue;
+
+ distance = DSP4_READ_WORD();
+ color = DSP4_READ_WORD();
+
+ // U1+B5+G5+R5
+ red = color & 0x1f;
+ green = (color >> 5) & 0x1f;
+ blue = (color >> 10) & 0x1f;
+
+ // dynamic lighting
+ red = (red * distance >> 15) & 0x1f;
+ green = (green * distance >> 15) & 0x1f;
+ blue = (blue * distance >> 15) & 0x1f;
+ color = red | (green << 5) | (blue << 10);
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(color);
+ break;
+ }
+ }
+
+ //////////////////////////////////////////////////////
+
+ // SR = 0x00
+
+ // linear interpolation (lerp) between projected points
+ px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+
+
+ // starting step values
+ x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
+ y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
+
+ // SR = 0x80
+
+ // rasterize line
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
+ {
+ // 1. HDMA memory pointer
+ // 2. vertical scroll offset ($210E)
+ // 3. horizontal scroll offset ($210D)
+
+ DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
+ DSP4_WRITE_WORD((UINT16)((y_scroll + 0x8000) >> 16));
+ DSP4_WRITE_WORD((UINT16)((x_scroll + 0x8000) >> 16));
+
+ // update memory address
+ DSP4_vars.poly_ptr[0][0] -= 4;
+
+ // update screen values
+ x_scroll += px_dx;
+ y_scroll += py_dy;
+ }
+ }
+
+ ////////////////////////////////////////////////////
+ // Post-update
+
+ // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
+ DSP4_vars.view_x1 = DSP4_vars.view_x2;
+ DSP4_vars.view_y1 = DSP4_vars.view_y2;
+ DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
+ DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
+
+ // add deltas for projection lines
+ DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx);
+ DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy);
+
+ // update projection lines
+ DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv);
+ DSP4_vars.world_y += DSP4_vars.world_dy;
+
+ // update road turnoff position
+ DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
+
+ ////////////////////////////////////////////////////
+ // command check
+
+ // scan next command
+ DSP4.in_count = 2;
+ DSP4_WAIT(2) resume2:
+
+ // check for termination
+ DSP4_vars.distance = DSP4_READ_WORD();
+ if (DSP4_vars.distance == -0x8000)
+ break;
+
+ // road splice
+ if( (UINT16) DSP4_vars.distance == 0x8001 )
+ {
+ DSP4.in_count = 6;
+ DSP4_WAIT(3) resume3:
+
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_vars.view_turnoff_x = DSP4_READ_WORD();
+ DSP4_vars.view_turnoff_dx = DSP4_READ_WORD();
+
+ // factor in new changes
+ DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
+ DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 );
+
+ // update stepping values
+ DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx;
+
+ DSP4.in_count = 2;
+ DSP4_WAIT(2)
+ }
+
+ // already have 2 bytes in queue
+ DSP4.in_count = 6;
+ DSP4_WAIT(4) resume4 :
+
+ // inspect inputs
+ DSP4_vars.world_ddy = DSP4_READ_WORD();
+ DSP4_vars.world_ddx = DSP4_READ_WORD();
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // no envelope here
+ DSP4_vars.world_xenv = 0;
+ }
+ while (1);
+
+ // terminate op
+ DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+
+void DSP4_OP10( void )
+{
+ DSP4.waiting4command = 0;
+
+ // op flow control
+ switch (DSP4_vars.DSP4_Logic)
+ {
+ case 1:
+ goto resume1; break;
+ case 2:
+ goto resume2; break;
+ case 3:
+ goto resume3; break;
+ }
+
+ ////////////////////////////////////////////////////
+ // sort inputs
+
+ DSP4_READ_WORD(); // 0x0000
+ DSP4_vars.world_y = DSP4_READ_DWORD();
+ DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_top[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD();
+ DSP4_vars.viewport_bottom = DSP4_READ_WORD();
+ DSP4_vars.world_x = DSP4_READ_DWORD();
+ DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD();
+ DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD();
+ DSP4_vars.world_yofs = DSP4_READ_WORD();
+ DSP4_vars.distance = DSP4_READ_WORD();
+ DSP4_vars.view_y2 = DSP4_READ_WORD();
+ DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_x2 = DSP4_READ_WORD();
+ DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_yofsenv = DSP4_READ_WORD();
+
+ // initial (x,y,offset) at starting DSP4_vars.raster line
+ DSP4_vars.view_x1 = (INT16)(DSP4_vars.world_x >> 16);
+ DSP4_vars.view_y1 = (INT16)(DSP4_vars.world_y >> 16);
+ DSP4_vars.view_xofs1 = DSP4_vars.view_x1;
+ DSP4_vars.view_yofs1 = DSP4_vars.world_yofs;
+
+ // first DSP4_vars.raster line
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0];
+
+ do
+ {
+ ////////////////////////////////////////////////////
+ // process one iteration of projection
+
+ // add shaping
+ DSP4_vars.view_x2 += DSP4_vars.view_dx;
+ DSP4_vars.view_y2 += DSP4_vars.view_dy;
+
+ // vertical scroll calculation
+ DSP4_vars.view_xofs2 = DSP4_vars.view_x2;
+ DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2;
+
+ // 1. Viewer x-position at the next
+ // 2. Viewer y-position below the horizon
+ // 3. Number of DSP4_vars.raster lines drawn in this iteration
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(DSP4_vars.view_x2);
+ DSP4_WRITE_WORD(DSP4_vars.view_y2);
+
+ //////////////////////////////////////////////////////
+
+ // SR = 0x00
+
+ // determine # of DSP4_vars.raster lines used
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2;
+
+ // prevent overdraw
+ if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0])
+ DSP4_vars.segments = 0;
+ else
+ DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2;
+
+ // don't draw outside the window
+ if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0])
+ {
+ DSP4_vars.segments = 0;
+
+ // flush remaining DSP4_vars.raster lines
+ if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0])
+ DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0];
+ }
+
+ // SR = 0x80
+
+ DSP4_WRITE_WORD(DSP4_vars.segments);
+
+ //////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++)
+ {
+ // grab inputs
+ DSP4.in_count = 4;
+ DSP4_WAIT(1);
+ resume1 :
+ for (;;)
+ {
+ INT16 distance;
+ INT16 color, red, green, blue;
+
+ distance = DSP4_READ_WORD();
+ color = DSP4_READ_WORD();
+
+ // U1+B5+G5+R5
+ red = color & 0x1f;
+ green = (color >> 5) & 0x1f;
+ blue = (color >> 10) & 0x1f;
+
+ // dynamic lighting
+ red = (red * distance >> 15) & 0x1f;
+ green = (green * distance >> 15) & 0x1f;
+ blue = (blue * distance >> 15) & 0x1f;
+ color = red | (green << 5) | (blue << 10);
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(color);
+ break;
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////
+
+ // scan next command if no SR check needed
+ if (DSP4_vars.segments)
+ {
+ INT32 px_dx, py_dy;
+ INT32 x_scroll, y_scroll;
+
+ // SR = 0x00
+
+ // linear interpolation (lerp) between projected points
+ px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+ py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1;
+
+ // starting step values
+ x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1);
+ y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs);
+
+ // SR = 0x80
+
+ // rasterize line
+ for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++)
+ {
+ // 1. HDMA memory pointer (bg2)
+ // 2. vertical scroll offset ($2110)
+ // 3. horizontal scroll offset ($210F)
+
+ DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]);
+ DSP4_WRITE_WORD((UINT16)((y_scroll + 0x8000) >> 16));
+ DSP4_WRITE_WORD((UINT16)((x_scroll + 0x8000) >> 16));
+
+ // update memory address
+ DSP4_vars.poly_ptr[0][0] -= 4;
+
+ // update screen values
+ x_scroll += px_dx;
+ y_scroll += py_dy;
+ }
+ }
+
+ /////////////////////////////////////////////////////
+ // Post-update
+
+ // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn
+ DSP4_vars.view_x1 = DSP4_vars.view_x2;
+ DSP4_vars.view_y1 = DSP4_vars.view_y2;
+ DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2;
+ DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2;
+
+ ////////////////////////////////////////////////////
+ // command check
+
+ // scan next command
+ DSP4.in_count = 2;
+ DSP4_WAIT(2) resume2 :
+
+ // check for opcode termination
+ DSP4_vars.distance = DSP4_READ_WORD();
+ if (DSP4_vars.distance == -0x8000)
+ break;
+
+ // already have 2 bytes in queue
+ DSP4.in_count = 10;
+ DSP4_WAIT(3) resume3 :
+
+
+ // inspect inputs
+ DSP4_vars.view_y2 = DSP4_READ_WORD();
+ DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ DSP4_vars.view_x2 = DSP4_READ_WORD();
+ DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15;
+ }
+ while (1);
+
+ DSP4.waiting4command = 1;
+}
+
+//////////////////////////////////////////////////////////////
+
+void DSP4_OP11(INT16 A, INT16 B, INT16 C, INT16 D, INT16 *M)
+{
+ // 0x155 = 341 = Horizontal Width of the Screen
+ *M = ((A * 0x0155 >> 2) & 0xf000) |
+ ((B * 0x0155 >> 6) & 0x0f00) |
+ ((C * 0x0155 >> 10) & 0x00f0) |
+ ((D * 0x0155 >> 14) & 0x000f);
+}
+
+
+
+
+
+/////////////////////////////////////////////////////////////
+//Processing Code
+/////////////////////////////////////////////////////////////
+
+void InitDSP4( void )
+{
+ memset(&DSP4, 0, sizeof(DSP4));
+ DSP4.waiting4command = 1;
+}
+
+void DSP4_write( UINT8 dsp4_byte )
+{
+ // clear pending read
+ if (DSP4.out_index < DSP4.out_count)
+ {
+ DSP4.out_index++;
+ return;
+ }
+
+ if (DSP4.waiting4command)
+ {
+ if (DSP4.half_command)
+ {
+ DSP4.command |= (dsp4_byte << 8);
+ DSP4.in_index = 0;
+ DSP4.waiting4command = 0;
+ DSP4.half_command = 0;
+ DSP4.out_count = 0;
+ DSP4.out_index = 0;
+
+ DSP4_vars.DSP4_Logic = 0;
+
+
+ switch (DSP4.command)
+ {
+ case 0x0000:
+ DSP4.in_count = 4; break;
+ case 0x0001:
+ DSP4.in_count = 44; break;
+ case 0x0003:
+ DSP4.in_count = 0; break;
+ case 0x0005:
+ DSP4.in_count = 0; break;
+ case 0x0006:
+ DSP4.in_count = 0; break;
+ case 0x0007:
+ DSP4.in_count = 34; break;
+ case 0x0008:
+ DSP4.in_count = 90; break;
+ case 0x0009:
+ DSP4.in_count = 14; break;
+ case 0x000a:
+ DSP4.in_count = 6; break;
+ case 0x000b:
+ DSP4.in_count = 6; break;
+ case 0x000d:
+ DSP4.in_count = 42; break;
+ case 0x000e:
+ DSP4.in_count = 0; break;
+ case 0x000f:
+ DSP4.in_count = 46; break;
+ case 0x0010:
+ DSP4.in_count = 36; break;
+ case 0x0011:
+ DSP4.in_count = 8; break;
+ default:
+ DSP4.waiting4command = 1;
+ break;
+ }
+ }
+ else
+ {
+ DSP4.command = dsp4_byte;
+ DSP4.half_command = 1;
+ }
+ }
+ else
+ {
+ DSP4.parameters[DSP4.in_index] = dsp4_byte;
+ DSP4.in_index++;
+ }
+
+ if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index)
+ {
+ // Actually execute the command
+ DSP4.waiting4command = 1;
+ DSP4.out_index = 0;
+ DSP4.in_index = 0;
+
+ switch (DSP4.command)
+ {
+ // 16-bit multiplication
+ case 0x0000:
+ {
+ INT16 multiplier, multiplicand;
+ INT32 product;
+
+ multiplier = DSP4_READ_WORD();
+ multiplicand = DSP4_READ_WORD();
+
+ DSP4_Multiply(multiplicand, multiplier, &product);
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD((UINT16)(product));
+ DSP4_WRITE_WORD((UINT16)(product >> 16));
+ }
+ break;
+
+ // single-player track projection
+ case 0x0001:
+ DSP4_OP01(); break;
+
+ // single-player selection
+ case 0x0003:
+ DSP4_OP03(); break;
+
+ // clear OAM
+ case 0x0005:
+ DSP4_OP05(); break;
+
+ // transfer OAM
+ case 0x0006:
+ DSP4_OP06(); break;
+
+ // single-player track turnoff projection
+ case 0x0007:
+ DSP4_OP07(); break;
+
+ // solid polygon projection
+ case 0x0008:
+ DSP4_OP08(); break;
+
+ // sprite projection
+ case 0x0009:
+ DSP4_OP09(); break;
+
+ // unknown
+ case 0x000A:
+ {
+ INT16 in2a;
+ INT16 out1a, out2a, out3a, out4a;
+
+ /* in1a = */ DSP4_READ_WORD();
+ in2a = DSP4_READ_WORD();
+ /* in3a = */ DSP4_READ_WORD();
+
+ DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a);
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(out1a);
+ DSP4_WRITE_WORD(out2a);
+ DSP4_WRITE_WORD(out3a);
+ DSP4_WRITE_WORD(out4a);
+ }
+ break;
+
+ // set OAM
+ case 0x000B:
+ {
+ INT16 sp_x = DSP4_READ_WORD();
+ INT16 sp_y = DSP4_READ_WORD();
+ INT16 sp_attr = DSP4_READ_WORD();
+ UINT8 draw = 1;
+
+ DSP4_CLEAR_OUT();
+
+ DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1);
+ }
+ break;
+
+ // multi-player track projection
+ case 0x000D:
+ DSP4_OP0D(); break;
+
+ // multi-player selection
+ case 0x000E:
+ DSP4_OP0E(); break;
+
+ // single-player track projection with lighting
+ case 0x000F:
+ DSP4_OP0F(); break;
+
+ // single-player track turnoff projection with lighting
+ case 0x0010:
+ DSP4_OP10(); break;
+
+ // unknown: horizontal mapping command
+ case 0x0011:
+ {
+ INT16 a, b, c, d, m;
+
+
+ d = DSP4_READ_WORD();
+ c = DSP4_READ_WORD();
+ b = DSP4_READ_WORD();
+ a = DSP4_READ_WORD();
+
+ DSP4_OP11(a, b, c, d, &m);
+
+ DSP4_CLEAR_OUT();
+ DSP4_WRITE_WORD(m);
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+UINT8 DSP4_read( void )
+{
+ UINT8 value;
+
+ if (DSP4.out_count)
+ {
+ value = (UINT8) DSP4.output[DSP4.out_index&0x1FF];
+ DSP4.out_index++;
+ if (DSP4.out_count == DSP4.out_index)
+ DSP4.out_count = 0;
+ }
+ else
+ {
+ value = 0xff;
+ }
+
+ return value;
+}
diff --git a/src/mame/machine/snesdsp4.h b/src/mame/machine/snesdsp4.h
new file mode 100644
index 00000000000..1bb08d7e1b2
--- /dev/null
+++ b/src/mame/machine/snesdsp4.h
@@ -0,0 +1,104 @@
+#ifndef SNESDSP4_H
+#define SNESDSP4_H
+
+#define FALSE 0
+#define TRUE 1
+
+
+struct DSP4_t
+{
+ UINT8 waiting4command;
+ UINT8 half_command;
+ UINT16 command;
+ UINT32 in_count;
+ UINT32 in_index;
+ UINT32 out_count;
+ UINT32 out_index;
+ UINT8 parameters[512];
+ UINT8 output[512];
+};
+
+extern struct DSP4_t DSP4;
+
+struct DSP4_vars_t
+{
+ // op control
+ INT8 DSP4_Logic; // controls op flow
+
+
+ // projection format
+ INT16 lcv; // loop-control variable
+ INT16 distance; // z-position INTo virtual world
+ INT16 raster; // current raster line
+ INT16 segments; // number of raster lines drawn
+
+ // 1.15.16 or 1.15.0 [sign, INTeger, fraction]
+ INT32 world_x; // line of x-projection in world
+ INT32 world_y; // line of y-projection in world
+ INT32 world_dx; // projection line x-delta
+ INT32 world_dy; // projection line y-delta
+ INT16 world_ddx; // x-delta increment
+ INT16 world_ddy; // y-delta increment
+ INT32 world_xenv; // world x-shaping factor
+ INT16 world_yofs; // world y-vertical scroll
+
+ INT16 view_x1; // current viewer-x
+ INT16 view_y1; // current viewer-y
+ INT16 view_x2; // future viewer-x
+ INT16 view_y2; // future viewer-y
+ INT16 view_dx; // view x-delta factor
+ INT16 view_dy; // view y-delta factor
+ INT16 view_xofs1; // current viewer x-vertical scroll
+ INT16 view_yofs1; // current viewer y-vertical scroll
+ INT16 view_xofs2; // future viewer x-vertical scroll
+ INT16 view_yofs2; // future viewer y-vertical scroll
+ INT16 view_yofsenv; // y-scroll shaping factor
+ INT16 view_turnoff_x; // road turnoff data
+ INT16 view_turnoff_dx; // road turnoff delta factor
+
+
+ // drawing area
+
+ INT16 viewport_cx; // x-center of viewport window
+ INT16 viewport_cy; // y-center of render window
+ INT16 viewport_left; // x-left of viewport
+ INT16 viewport_right; // x-right of viewport
+ INT16 viewport_top; // y-top of viewport
+ INT16 viewport_bottom; // y-bottom of viewport
+
+
+ // sprite structure
+
+ INT16 sprite_x; // projected x-pos of sprite
+ INT16 sprite_y; // projected y-pos of sprite
+ INT16 sprite_attr; // obj attributes
+ UINT8 sprite_size; // sprite size: 8x8 or 16x16
+ INT16 sprite_clipy; // visible line to clip pixels off
+ INT16 sprite_count;
+
+ // generic projection variables designed for
+ // two solid polygons + two polygon sides
+
+ INT16 poly_clipLf[2][2]; // left clip boundary
+ INT16 poly_clipRt[2][2]; // right clip boundary
+ INT16 poly_ptr[2][2]; // HDMA structure poINTers
+ INT16 poly_raster[2][2]; // current raster line below horizon
+ INT16 poly_top[2][2]; // top clip boundary
+ INT16 poly_bottom[2][2]; // bottom clip boundary
+ INT16 poly_cx[2][2]; // center for left/right poINTs
+ INT16 poly_start[2]; // current projection poINTs
+ INT16 poly_plane[2]; // previous z-plane distance
+
+
+ // OAM
+ INT16 OAM_attr[16]; // OAM (size,MSB) data
+ INT16 OAM_index; // index INTo OAM table
+ INT16 OAM_bits; // offset INTo OAM table
+
+ INT16 OAM_RowMax; // maximum number of tiles per 8 aligned pixels (row)
+ INT16 OAM_Row[32]; // current number of tiles per row
+};
+
+extern struct DSP4_vars_t DSP4_vars;
+
+#endif
diff --git a/src/mame/mame.mak b/src/mame/mame.mak
index 1558e32ece6..f7f29582ecb 100644
--- a/src/mame/mame.mak
+++ b/src/mame/mame.mak
@@ -1849,6 +1849,8 @@ $(DRIVERS)/mpu4.o: $(MAMESRC)/drivers/mpu4drvr.c
$(DRIVERS)/neogeo.o: $(MAMESRC)/drivers/neodrvr.c
$(MACHINE)/snes.o: $(MAMESRC)/machine/snesdsp1.c \
$(MAMESRC)/machine/snesdsp2.c \
+ $(MAMESRC)/machine/snesdsp3.c \
+ $(MAMESRC)/machine/snesdsp4.c \
$(MAMESRC)/machine/snesobc1.c \
$(MAMESRC)/machine/snesrtc.c \
$(MAMESRC)/machine/snessdd1.c