mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-08 17:03:47 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fc86eae6d | ||
|
|
927c97eb06 | ||
|
|
cf09d41669 | ||
|
|
724747ac9e | ||
|
|
e1e275eb38 | ||
|
|
e30fcade43 | ||
|
|
42dbf73d18 |
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
||||
include nall/Makefile
|
||||
snes := snes
|
||||
gameboy := gameboy
|
||||
profile := accuracy
|
||||
profile := compatibility
|
||||
ui := ui
|
||||
|
||||
# debugger
|
||||
|
||||
+206
-41
@@ -3013,6 +3013,11 @@
|
||||
</cartridge>
|
||||
<cartridge sha256="05eb897d7696555790591c431c9d55a43ff9a1c12162443c17c5fcddfa5eb3c5">
|
||||
<name>Alien vs. Predator (USA)</name>
|
||||
<cheat>
|
||||
<description>Invincibility</description>
|
||||
<code>1D3B-0FAD</code>
|
||||
<code>6D22-D4D7</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite health</description>
|
||||
<code>C238-0FDD</code>
|
||||
@@ -3043,10 +3048,18 @@
|
||||
<code>3CEA-67D8</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>One hit kills most enemies</description>
|
||||
<code>DFB5-AD67</code>
|
||||
<code>7E1125FF</code>
|
||||
<code>7E1AA5FF</code>
|
||||
<description>One hit kills</description>
|
||||
<code>6D2D-AFA7</code>
|
||||
<code>6D37-64DD</code>
|
||||
<code>6DB8-A765</code>
|
||||
<code>6D39-DD6F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere</description>
|
||||
<code>402B-6FD7</code>
|
||||
<code>D12B-0D07</code>
|
||||
<code>D12D-0D67</code>
|
||||
<code>D13A-A4DD</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Disc power-ups give 1 disc instead of 6 (handicap)</description>
|
||||
@@ -9024,20 +9037,20 @@
|
||||
<cartridge sha256="c88a882ad72dfa07a9b1eb8a2183aa10057d60877a02edf90cf2cd8c78abe65e">
|
||||
<name>Combatribes, The (USA)</name>
|
||||
<cheat>
|
||||
<description>Invincibility</description>
|
||||
<description>Invincibility - both players</description>
|
||||
<code>2DB1-A7D4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite health</description>
|
||||
<description>Invincibility - P1</description>
|
||||
<code>7E193401</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite health - both players</description>
|
||||
<code>3CB5-6F64</code>
|
||||
<code>CBB5-6FA4</code>
|
||||
<code>A6B5-64D4</code>
|
||||
<code>62B5-6464</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite credits</description>
|
||||
<code>A2B3-A404</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite health - P1</description>
|
||||
<code>7E1794C8</code>
|
||||
@@ -9046,12 +9059,24 @@
|
||||
<description>Infinite health - P2</description>
|
||||
<code>7E1796C8</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite credits</description>
|
||||
<code>A2B3-A404</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite credits (alt)</description>
|
||||
<code>7E156409</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>One hit kills</description>
|
||||
<description>Hit anywhere - P1</description>
|
||||
<code>3DBB-076D</code>
|
||||
<code>70BC-0D6D</code>
|
||||
<code>DDBB-07AD</code>
|
||||
<code>DDBC-0DDD</code>
|
||||
<code>EDBC-0D0D</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>One hit kills - both players</description>
|
||||
<code>3DA3-64AF</code>
|
||||
<code>DDA3-67DF</code>
|
||||
<code>DDA3-670F</code>
|
||||
@@ -12230,7 +12255,7 @@
|
||||
<code>BAC9-3FBF</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Pick up to 9 points of any attribute instead of 5</description>
|
||||
<description>9 points of any attribute instead of 5</description>
|
||||
<code>DBBC-1F27</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
@@ -17467,6 +17492,12 @@
|
||||
<code>CBE8-47AF</code>
|
||||
<code>EEEA-4DDF</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - both players</description>
|
||||
<code>4062-1DA6</code>
|
||||
<code>4062-14D6</code>
|
||||
<code>406C-1DA6</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Both players can select same character</description>
|
||||
<code>33C5-1404</code>
|
||||
@@ -17554,13 +17585,6 @@
|
||||
<description>Infinite time</description>
|
||||
<code>C2F9-84DF</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Instant super energy</description>
|
||||
<code>CBB5-5D07</code>
|
||||
<code>46B5-5D67</code>
|
||||
<code>DDB5-5DA7</code>
|
||||
<code>4BB5-5F07</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite super energy - P1</description>
|
||||
<code>7E051663</code>
|
||||
@@ -17577,6 +17601,19 @@
|
||||
<description>Infinite throw time - P2</description>
|
||||
<code>7E068010</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - both players</description>
|
||||
<code>3DA7-E76D</code>
|
||||
<code>D5A0-EDDD</code>
|
||||
<code>DDA7-E7AD</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Instant super energy</description>
|
||||
<code>CBB5-5D07</code>
|
||||
<code>46B5-5D67</code>
|
||||
<code>DDB5-5DA7</code>
|
||||
<code>4BB5-5F07</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Have the Pipe - P1</description>
|
||||
<code>7E051401</code>
|
||||
@@ -18103,6 +18140,16 @@
|
||||
<code>DDE3-0FAC</code>
|
||||
</cheat>
|
||||
</cartridge>
|
||||
<cartridge sha256="bc6b0344a434644c391ac69a53c7720c51e2d3c5c082b8d78598ae4df100c464">
|
||||
<name>Foreman For Real (USA)</name>
|
||||
<cheat>
|
||||
<description>Infinite stamina - P1</description>
|
||||
<code>7E11AE64</code>
|
||||
<code>7E107227</code>
|
||||
<code>7E043A78</code>
|
||||
<code>7E045A64</code>
|
||||
</cheat>
|
||||
</cartridge>
|
||||
<cartridge sha256="dcceb5be536c9b91bf34f65e7fcec4b55a78af0192323cf86f3b9555072037ee">
|
||||
<name>Fun 'n Games (USA)</name>
|
||||
<cheat>
|
||||
@@ -36725,6 +36772,14 @@
|
||||
<description>Infinite Boomerangs</description>
|
||||
<code>DDA5-3700</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Walk through walls</description>
|
||||
<code>6D64-CDAF</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Jump through ceilings</description>
|
||||
<code>6D68-1FD4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Get 2x energy from sacred hearts</description>
|
||||
<code>746B-4D6A</code>
|
||||
@@ -37446,6 +37501,32 @@
|
||||
<code>F7B7-D46E</code>
|
||||
</cheat>
|
||||
</cartridge>
|
||||
<cartridge sha256="65f7123ab4fc9bad24b1976da8e84976cc6977861626a05d4e2cce132ef3fd42">
|
||||
<name>Psycho Dream (Japan)</name>
|
||||
<cheat>
|
||||
<description>Invincibility (blinking)</description>
|
||||
<code>DDB7-8D6D</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite time</description>
|
||||
<code>DD7F-7407</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite magic</description>
|
||||
<code>69BE-77DF</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere</description>
|
||||
<code>6DDD-E71F</code>
|
||||
<code>6DD4-EDCF</code>
|
||||
<code>6DD7-EF3F</code>
|
||||
<code>6DD0-E74F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Best weapon on pick-up</description>
|
||||
<code>CBC7-74AD</code>
|
||||
</cheat>
|
||||
</cartridge>
|
||||
<cartridge sha256="475c9baa1c2b76a6b3119e47d32814dc1c08e84e23250ae015bb0bccea915637">
|
||||
<name>Push-Over (USA)</name>
|
||||
<cheat>
|
||||
@@ -40927,6 +41008,49 @@
|
||||
</cartridge>
|
||||
<cartridge sha256="4c15013131351e694e05f22e38bb1b3e4031dedac77ec75abecebe8520d82d5f">
|
||||
<name>Secret of Mana (USA)</name>
|
||||
<cheat>
|
||||
<description>Protection from most hits (disable to kill enemies)</description>
|
||||
<code>8208-776D</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Enemies die instantly</description>
|
||||
<code>CD7E-7D67</code>
|
||||
<code>DD7E-7DA7</code>
|
||||
<code>DD7E-7FD7</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere</description>
|
||||
<code>40A7-6765</code>
|
||||
<code>402C-0F60</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Level 99 after first enemy</description>
|
||||
<code>B606-7F6F</code>
|
||||
<code>DD78-5F64</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Max weapon damage</description>
|
||||
<code>BA0A-5404</code>
|
||||
<code>CB0A-54D4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Change screens to max out Strength, Agility, Constitution, Intelligence and Wisdom</description>
|
||||
<code>DD03-ED04</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Weapon bar never decreases</description>
|
||||
<code>82A0-6466</code>
|
||||
<code>82E0-0FD4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Walk through walls</description>
|
||||
<code>DD85-A7D1</code>
|
||||
<code>DD85-64A1</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Everything is free</description>
|
||||
<code>6D06-7F6F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Strength for level 16 is 90</description>
|
||||
<code>9C06-81AD</code>
|
||||
@@ -40973,11 +41097,11 @@
|
||||
<code>DDE8-E9C4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Cup of wishes costs nothing</description>
|
||||
<description>Cup Of Wishes costs nothing</description>
|
||||
<code>DDEC-E9C4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Medical herb costs nothing</description>
|
||||
<description>Medical Herb costs nothing</description>
|
||||
<code>DDEC-E944</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
@@ -40985,20 +41109,20 @@
|
||||
<code>DDE7-7047</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hair ribbon costs nothing</description>
|
||||
<description>Hair Ribbon costs nothing</description>
|
||||
<code>DDE8-E144</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Rabite cap costs nothing</description>
|
||||
<description>Rabite Cap costs nothing</description>
|
||||
<code>DDE8-E1C4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Faerie walnut costs nothing</description>
|
||||
<description>Faerie Walnut costs nothing</description>
|
||||
<code>DDEC-E0C4</code>
|
||||
<code>DDEC-E034</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Royal jam costs nothing</description>
|
||||
<description>Royal Jam costs nothing</description>
|
||||
<code>DDEC-E044</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
@@ -41009,14 +41133,6 @@
|
||||
<description>Staying at Neko's costs nothing instead of 30</description>
|
||||
<code>DDAB-E715</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Protection from most hits (Switch off to kill enemies)</description>
|
||||
<code>8208-776D</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Change screens to max out Strength, Agility, Constitution, Intelligence and Wisdom</description>
|
||||
<code>DD03-ED04</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Start with 255 GP</description>
|
||||
<code>EE28-EDAF</code>
|
||||
@@ -46605,6 +46721,14 @@
|
||||
<description>Infinite shots for most weapons</description>
|
||||
<code>A689-0FD7</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere (cannot use whip to grab onto things, press Y to moon jump instead)</description>
|
||||
<code>B428-AFA4</code>
|
||||
<code>C728-A4D4</code>
|
||||
<code>DD2C-A764</code>
|
||||
<code>6D20-A7A4</code>
|
||||
<code>6D21-AF64</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Fully powered whip with first power-up</description>
|
||||
<code>DD24-AFD7</code>
|
||||
@@ -46620,7 +46744,7 @@
|
||||
<code>EE26-6767</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Have maxed out hearts with each heart pick-up</description>
|
||||
<description>Max hearts on pick-up</description>
|
||||
<code>DD2B-6D07</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
@@ -52461,6 +52585,14 @@
|
||||
<description>Automatic and infinite continues</description>
|
||||
<code>82A6-4FA4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P1</description>
|
||||
<code>6D69-149F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P2</description>
|
||||
<code>6D6f-1F9F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Leonardo is replaced by Rat King</description>
|
||||
<code>DCCA-1405</code>
|
||||
@@ -52592,6 +52724,14 @@
|
||||
<description>One hit kills (disable when you reach Shredder's first fight)</description>
|
||||
<code>6D6C-0F2F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P1</description>
|
||||
<code>EA66-67AA</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P2</description>
|
||||
<code>EA6B-67AA</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Start with 1 life instead of 3</description>
|
||||
<code>DD28-67D9</code>
|
||||
@@ -54966,14 +55106,12 @@
|
||||
<cartridge sha256="cb2fdfce61858063bf4c9da4228381c3ec3abe423f4d378cddd174ae4adb261e">
|
||||
<name>Ultimate Mortal Kombat 3 (USA)</name>
|
||||
<cheat>
|
||||
<description>Invincibility (throws disable CPU)</description>
|
||||
<code>C132-CDBF</code>
|
||||
<code>1432-CD2F</code>
|
||||
<code>2D32-CFFF</code>
|
||||
<code>D032-CF9F</code>
|
||||
<code>6D32-CFBF</code>
|
||||
<code>0D32-CF2F</code>
|
||||
<description>Invincibility (throws damage CPU)</description>
|
||||
<code>3D3C-C79F</code>
|
||||
<code>6D33-3DBF</code>
|
||||
<code>7D38-CDFF</code>
|
||||
<code>DF3C-C7BF</code>
|
||||
<code>E138-CD9F</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite health - P1</description>
|
||||
@@ -56585,6 +56723,13 @@
|
||||
<description>Always win - P1</description>
|
||||
<code>7B86-84A4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P1</description>
|
||||
<code>7DFE-776D</code>
|
||||
<code>ADFE-746D</code>
|
||||
<code>DDFE-74AD</code>
|
||||
<code>FDFE-77DD</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Win a draw - P1</description>
|
||||
<code>D409-8F04</code>
|
||||
@@ -56639,6 +56784,16 @@
|
||||
</cartridge>
|
||||
<cartridge sha256="159d5341d13d6801324e8271f7191c0223617c9d30984676319b2df7937c78c0">
|
||||
<name>World Heroes 2 (USA)</name>
|
||||
<cheat>
|
||||
<description>Invincibility - P1</description>
|
||||
<code>1D82-AF6D</code>
|
||||
<code>1D83-0F64</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Invincibility - P2</description>
|
||||
<code>1D82-0D0D</code>
|
||||
<code>1D84-DD04</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Infinite health - P1</description>
|
||||
<code>7E057480</code>
|
||||
@@ -56651,6 +56806,16 @@
|
||||
<description>Infinite time</description>
|
||||
<code>7E052C99</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P1</description>
|
||||
<code>6D8A-07DD</code>
|
||||
<code>6D8F-D7D4</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>Hit anywhere - P2</description>
|
||||
<code>6D82-AD0D</code>
|
||||
<code>6D83-0D04</code>
|
||||
</cheat>
|
||||
<cheat>
|
||||
<description>One win wins the match - P1</description>
|
||||
<code>7E057002</code>
|
||||
|
||||
@@ -125,7 +125,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
if(type == TypeGameBoy) {
|
||||
xml << "<cartridge rtc='" << gameboy_has_rtc(data, size) << "'>\n";
|
||||
if(gameboy_ram_size(data, size) > 0) {
|
||||
xml << " <ram size='" << hex(gameboy_ram_size(data, size)) << "'/>\n";
|
||||
xml << " <ram size='0x" << hex(gameboy_ram_size(data, size)) << "'/>\n";
|
||||
}
|
||||
xml << "</cartridge>\n";
|
||||
xmlMemoryMap = xml.transform("'", "\"");
|
||||
@@ -158,6 +158,17 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </icd2>\n";
|
||||
} else if(has_cx4) {
|
||||
xml << " <hitachidsp model='HG51B169' frequency='20000000' data='cx4.bin' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n";
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-7f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-ff:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <mmio>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </mmio>\n";
|
||||
xml << " </hitachidsp>\n";
|
||||
} else if(has_spc7110) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-0f:8000-ffff'/>\n";
|
||||
@@ -167,9 +178,9 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
|
||||
xml << " <spc7110>\n";
|
||||
xml << " <mcu>\n";
|
||||
xml << " <map address='d0-ff:0000-ffff' offset='100000' size='" << hex(size - 0x100000) << "'/>\n";
|
||||
xml << " <map address='d0-ff:0000-ffff' offset='0x100000' size='0x" << hex(size - 0x100000) << "'/>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='30:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
@@ -194,7 +205,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
@@ -215,7 +226,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
@@ -233,7 +244,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
@@ -241,14 +252,14 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
} else if(mapper == ExHiROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='400000'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='400000'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='shadow' address='00-3f:8000-ffff' offset='0x400000'/>\n";
|
||||
xml << " <map mode='linear' address='40-7f:0000-ffff' offset='0x400000'/>\n";
|
||||
xml << " <map mode='shadow' address='80-bf:8000-ffff' offset='0x000000'/>\n";
|
||||
xml << " <map mode='linear' address='c0-ff:0000-ffff' offset='0x000000'/>\n";
|
||||
xml << " </rom>\n";
|
||||
|
||||
if(ram_size > 0) {
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
if((rom_size > 0x200000) || (ram_size > 32 * 1024)) {
|
||||
@@ -266,10 +277,10 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " <map mode='linear' address='80-bf:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:6000-7fff' size='2000'/>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:6000-7fff' size='0x2000'/>\n";
|
||||
xml << " <map mode='linear' address='60-7f:0000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:6000-7fff' size='2000'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:6000-7fff' size='0x2000'/>\n";
|
||||
xml << " <map mode='linear' address='e0-ff:0000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " <mmio>\n";
|
||||
@@ -290,11 +301,11 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " <map mode='direct' address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
xml << " </mcu>\n";
|
||||
xml << " <iram size='800'>\n";
|
||||
xml << " <iram size='0x800'>\n";
|
||||
xml << " <map mode='linear' address='00-3f:3000-37ff'/>\n";
|
||||
xml << " <map mode='linear' address='80-bf:3000-37ff'/>\n";
|
||||
xml << " </iram>\n";
|
||||
xml << " <bwram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <bwram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='40-4f:0000-ffff'/>\n";
|
||||
xml << " </bwram>\n";
|
||||
xml << " <mmio>\n";
|
||||
@@ -304,12 +315,12 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " </sa1>\n";
|
||||
} else if(mapper == BSCLoROM) {
|
||||
xml << " <rom>\n";
|
||||
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='000000'/>\n";
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='100000'/>\n";
|
||||
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='200000'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='100000'/>\n";
|
||||
xml << " <map mode='linear' address='00-1f:8000-ffff' offset='0x000000'/>\n";
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff' offset='0x100000'/>\n";
|
||||
xml << " <map mode='linear' address='80-9f:8000-ffff' offset='0x200000'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff' offset='0x100000'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='70-7f:0000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-ff:0000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
@@ -325,7 +336,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " <map mode='shadow' address='80-9f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:0000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='" << hex(ram_size) << "'>\n";
|
||||
xml << " <ram size='0x" << hex(ram_size) << "'>\n";
|
||||
xml << " <map mode='linear' address='20-3f:6000-7fff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:6000-7fff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
@@ -362,7 +373,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " <map mode='linear' address='20-3f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='a0-bf:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='20000'>\n";
|
||||
xml << " <ram size='0x20000'>\n";
|
||||
xml << " <map mode='linear' address='60-63:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='e0-e3:8000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
@@ -372,7 +383,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " <map mode='linear' address='40-5f:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='c0-df:8000-ffff'/>\n";
|
||||
xml << " </rom>\n";
|
||||
xml << " <ram size='20000'>\n";
|
||||
xml << " <ram size='0x20000'>\n";
|
||||
xml << " <map mode='linear' address='70-73:8000-ffff'/>\n";
|
||||
xml << " <map mode='linear' address='f0-f3:8000-ffff'/>\n";
|
||||
xml << " </ram>\n";
|
||||
@@ -399,15 +410,8 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
xml << " </sdd1>\n";
|
||||
}
|
||||
|
||||
if(has_cx4) {
|
||||
xml << " <cx4>\n";
|
||||
xml << " <map address='00-3f:6000-7fff'/>\n";
|
||||
xml << " <map address='80-bf:6000-7fff'/>\n";
|
||||
xml << " </cx4>\n";
|
||||
}
|
||||
|
||||
if(has_dsp1) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
|
||||
xml << " <necdsp model='uPD7725' frequency='8000000' program='dsp1b.bin' sha256='4d42db0f36faef263d6b93f508e8c1c4ae8fc2605fd35e3390ecc02905cd420c'>\n";
|
||||
if(dsp1_mapper == DSP1LoROM1MB) {
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
@@ -440,7 +444,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
|
||||
if(has_dsp2) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
|
||||
xml << " <necdsp model='uPD7725' frequency='8000000' program='dsp2.bin' sha256='5efbdf96ed0652790855225964f3e90e6a4d466cfa64df25b110933c6cf94ea1'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
@@ -453,7 +457,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
|
||||
if(has_dsp3) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
|
||||
xml << " <necdsp model='uPD7725' frequency='8000000' program='dsp3.bin' sha256='2e635f72e4d4681148bc35429421c9b946e4f407590e74e31b93b8987b63ba90'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='20-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='a0-bf:8000-bfff'/>\n";
|
||||
@@ -466,7 +470,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
|
||||
if(has_dsp4) {
|
||||
xml << " <necdsp revision='upd7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
|
||||
xml << " <necdsp model='uPD7725' frequency='8000000' program='dsp4.bin' sha256='63ede17322541c191ed1fdf683872554a0a57306496afc43c59de7c01a6e764a'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='30-3f:8000-bfff'/>\n";
|
||||
xml << " <map address='b0-bf:8000-bfff'/>\n";
|
||||
@@ -486,7 +490,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
|
||||
if(has_st010) {
|
||||
xml << " <necdsp revision='upd96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
|
||||
xml << " <necdsp model='uPD96050' frequency='10000000' program='st0010.bin' sha256='55c697e864562445621cdf8a7bf6e84ae91361e393d382a3704e9aa55559041e'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60:0000'/>\n";
|
||||
xml << " <map address='e0:0000'/>\n";
|
||||
@@ -503,7 +507,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
|
||||
}
|
||||
|
||||
if(has_st011) {
|
||||
xml << " <necdsp revision='upd96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
|
||||
xml << " <necdsp model='uPD96050' frequency='15000000' program='st0011.bin' sha256='651b82a1e26c4fa8dd549e91e7f923012ed2ca54c1d9fd858655ab30679c2f0e'>\n";
|
||||
xml << " <dr>\n";
|
||||
xml << " <map address='60:0000'/>\n";
|
||||
xml << " <map address='e0:0000'/>\n";
|
||||
|
||||
+17
-16
@@ -1,11 +1,11 @@
|
||||
snes_objects := snes-system
|
||||
snes_objects := snes-system snes-controller
|
||||
snes_objects += snes-cartridge snes-cheat
|
||||
snes_objects += snes-memory snes-cpucore snes-smpcore
|
||||
snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
|
||||
snes_objects += snes-nss snes-icd2 snes-superfx snes-sa1 snes-necdsp
|
||||
snes_objects += snes-bsx snes-srtc snes-sdd1 snes-spc7110 snes-cx4
|
||||
snes_objects += snes-nss snes-icd2 snes-superfx snes-sa1 snes-necdsp snes-hitachidsp
|
||||
snes_objects += snes-bsx snes-srtc snes-sdd1 snes-spc7110
|
||||
snes_objects += snes-obc1 snes-st0018 snes-sufamiturbo
|
||||
snes_objects += snes-msu1 snes-serial
|
||||
snes_objects += snes-msu1 snes-link
|
||||
objects += $(snes_objects)
|
||||
|
||||
ifeq ($(profile),accuracy)
|
||||
@@ -28,29 +28,30 @@ else ifeq ($(profile),performance)
|
||||
snesppu := $(snes)/alt/ppu-performance
|
||||
endif
|
||||
|
||||
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/)
|
||||
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
|
||||
obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/)
|
||||
obj/snes-smpcore.o : $(snes)/smp/core/core.cpp $(call rwildcard,$(snes)/smp/core/)
|
||||
obj/snes-cpu.o : $(snescpu)/cpu.cpp $(call rwildcard,$(snescpu)/)
|
||||
obj/snes-smp.o : $(snessmp)/smp.cpp $(call rwildcard,$(snessmp)/)
|
||||
obj/snes-dsp.o : $(snesdsp)/dsp.cpp $(call rwildcard,$(snesdsp)/)
|
||||
obj/snes-ppu.o : $(snesppu)/ppu.cpp $(call rwildcard,$(snesppu)/)
|
||||
obj/snes-cartridge.o: $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/*
|
||||
obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/*
|
||||
obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/) $(call rwildcard,$(snes)/audio/) $(call rwildcard,$(snes)/input/)
|
||||
obj/snes-controller.o: $(snes)/controller/controller.cpp $(call rwildcard,$(snes)/controller/)
|
||||
obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/)
|
||||
obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/)
|
||||
obj/snes-smpcore.o : $(snes)/smp/core/core.cpp $(call rwildcard,$(snes)/smp/core/)
|
||||
obj/snes-cpu.o : $(snescpu)/cpu.cpp $(call rwildcard,$(snescpu)/)
|
||||
obj/snes-smp.o : $(snessmp)/smp.cpp $(call rwildcard,$(snessmp)/)
|
||||
obj/snes-dsp.o : $(snesdsp)/dsp.cpp $(call rwildcard,$(snesdsp)/)
|
||||
obj/snes-ppu.o : $(snesppu)/ppu.cpp $(call rwildcard,$(snesppu)/)
|
||||
obj/snes-cartridge.o : $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/*
|
||||
obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/*
|
||||
|
||||
obj/snes-nss.o : $(snes)/chip/nss/nss.cpp $(call rwildcard,$(snes)/chip/nss/)
|
||||
obj/snes-icd2.o : $(snes)/chip/icd2/icd2.cpp $(call rwildcard,$(snes)/chip/icd2/)
|
||||
obj/snes-superfx.o : $(snes)/chip/superfx/superfx.cpp $(call rwildcard,$(snes)/chip/superfx/)
|
||||
obj/snes-sa1.o : $(snes)/chip/sa1/sa1.cpp $(call rwildcard,$(snes)/chip/sa1/)
|
||||
obj/snes-necdsp.o : $(snes)/chip/necdsp/necdsp.cpp $(call rwildcard,$(snes)/chip/necdsp/)
|
||||
obj/snes-hitachidsp.o : $(snes)/chip/hitachidsp/hitachidsp.cpp $(call rwildcard,$(snes)/chip/hitachidsp/)
|
||||
obj/snes-bsx.o : $(snes)/chip/bsx/bsx.cpp $(call rwildcard,$(snes)/chip/bsx/)
|
||||
obj/snes-srtc.o : $(snes)/chip/srtc/srtc.cpp $(snes)/chip/srtc/*
|
||||
obj/snes-sdd1.o : $(snes)/chip/sdd1/sdd1.cpp $(snes)/chip/sdd1/*
|
||||
obj/snes-spc7110.o : $(snes)/chip/spc7110/spc7110.cpp $(snes)/chip/spc7110/*
|
||||
obj/snes-cx4.o : $(snes)/chip/cx4/cx4.cpp $(snes)/chip/cx4/*
|
||||
obj/snes-obc1.o : $(snes)/chip/obc1/obc1.cpp $(snes)/chip/obc1/*
|
||||
obj/snes-st0018.o : $(snes)/chip/st0018/st0018.cpp $(snes)/chip/st0018/*
|
||||
obj/snes-sufamiturbo.o: $(snes)/chip/sufamiturbo/sufamiturbo.cpp $(snes)/chip/sufamiturbo/*
|
||||
obj/snes-msu1.o : $(snes)/chip/msu1/msu1.cpp $(snes)/chip/msu1/*
|
||||
obj/snes-serial.o : $(snes)/chip/serial/serial.cpp $(snes)/chip/serial/*
|
||||
obj/snes-link.o : $(snes)/chip/link/link.cpp $(snes)/chip/link/*
|
||||
@@ -23,6 +23,9 @@ void CPU::step(unsigned clocks) {
|
||||
Processor &chip = *coprocessors[i];
|
||||
chip.clock -= clocks * (uint64)chip.frequency;
|
||||
}
|
||||
input.port1->clock -= clocks * (uint64)input.port1->frequency;
|
||||
input.port2->clock -= clocks * (uint64)input.port2->frequency;
|
||||
synchronize_controllers();
|
||||
}
|
||||
|
||||
void CPU::synchronize_smp() {
|
||||
@@ -41,13 +44,18 @@ void CPU::synchronize_ppu() {
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::synchronize_coprocessor() {
|
||||
void CPU::synchronize_coprocessors() {
|
||||
for(unsigned i = 0; i < coprocessors.size(); i++) {
|
||||
Processor &chip = *coprocessors[i];
|
||||
if(chip.clock < 0) co_switch(chip.thread);
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::synchronize_controllers() {
|
||||
if(input.port1->clock < 0) co_switch(input.port1->thread);
|
||||
if(input.port2->clock < 0) co_switch(input.port2->thread);
|
||||
}
|
||||
|
||||
void CPU::Enter() { cpu.enter(); }
|
||||
|
||||
void CPU::enter() {
|
||||
|
||||
@@ -7,7 +7,8 @@ public:
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_smp();
|
||||
void synchronize_ppu();
|
||||
void synchronize_coprocessor();
|
||||
void synchronize_coprocessors();
|
||||
void synchronize_controllers();
|
||||
|
||||
uint8 pio();
|
||||
bool joylatch();
|
||||
@@ -41,7 +42,6 @@ private:
|
||||
enum : unsigned {
|
||||
DramRefresh,
|
||||
HdmaRun,
|
||||
ControllerLatch,
|
||||
};
|
||||
};
|
||||
nall::priority_queue<unsigned> queue;
|
||||
|
||||
@@ -15,13 +15,13 @@ uint8 CPU::mmio_read(unsigned addr) {
|
||||
|
||||
case 0x4016: {
|
||||
uint8 result = regs.mdr & 0xfc;
|
||||
result |= input.port_read(0) & 3;
|
||||
result |= input.port1->data() & 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
case 0x4017: {
|
||||
uint8 result = (regs.mdr & 0xe0) | 0x1c;
|
||||
result |= input.port_read(1) & 3;
|
||||
result |= input.port2->data() & 3;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -127,10 +127,8 @@ void CPU::mmio_write(unsigned addr, uint8 data) {
|
||||
}
|
||||
|
||||
case 0x4016: {
|
||||
bool old_latch = status.joypad_strobe_latch;
|
||||
bool new_latch = data & 1;
|
||||
status.joypad_strobe_latch = new_latch;
|
||||
if(old_latch != new_latch) input.poll();
|
||||
input.port1->latch(data & 1);
|
||||
input.port2->latch(data & 1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ void CPU::queue_event(unsigned id) {
|
||||
switch(id) {
|
||||
case QueueEvent::DramRefresh: return add_clocks(40);
|
||||
case QueueEvent::HdmaRun: return hdma_run();
|
||||
case QueueEvent::ControllerLatch: return ppu.latch_counters();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +61,7 @@ void CPU::add_clocks(unsigned clocks) {
|
||||
void CPU::scanline() {
|
||||
synchronize_smp();
|
||||
synchronize_ppu();
|
||||
synchronize_coprocessor();
|
||||
synchronize_coprocessors();
|
||||
system.scanline();
|
||||
|
||||
if(vcounter() == 0) hdma_init();
|
||||
@@ -73,10 +72,6 @@ void CPU::scanline() {
|
||||
queue.enqueue(1104 + 8, QueueEvent::HdmaRun);
|
||||
}
|
||||
|
||||
if(vcounter() == input.latchy) {
|
||||
queue.enqueue(input.latchx, QueueEvent::ControllerLatch);
|
||||
}
|
||||
|
||||
bool nmi_valid = status.nmi_valid;
|
||||
status.nmi_valid = vcounter() >= (ppu.overscan() == false ? 225 : 240);
|
||||
if(!nmi_valid && status.nmi_valid) {
|
||||
@@ -87,16 +82,20 @@ void CPU::scanline() {
|
||||
}
|
||||
|
||||
if(status.auto_joypad_poll_enabled && vcounter() == (ppu.overscan() == false ? 227 : 242)) {
|
||||
input.poll();
|
||||
run_auto_joypad_poll();
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::run_auto_joypad_poll() {
|
||||
input.port1->latch(1);
|
||||
input.port2->latch(1);
|
||||
input.port1->latch(0);
|
||||
input.port2->latch(0);
|
||||
|
||||
uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0;
|
||||
for(unsigned i = 0; i < 16; i++) {
|
||||
uint8 port0 = input.port_read(0);
|
||||
uint8 port1 = input.port_read(1);
|
||||
uint8 port0 = input.port1->data();
|
||||
uint8 port1 = input.port2->data();
|
||||
|
||||
joy1 |= (port0 & 1) ? (0x8000 >> i) : 0;
|
||||
joy2 |= (port1 & 1) ? (0x8000 >> i) : 0;
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
alwaysinline unsigned operator=(unsigned data) {
|
||||
n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10;
|
||||
h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01;
|
||||
return data;
|
||||
}
|
||||
|
||||
alwaysinline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); }
|
||||
|
||||
@@ -21,20 +21,20 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) {
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_necdsp = false;
|
||||
has_hitachidsp = false;
|
||||
has_srtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_spc7110rtc = false;
|
||||
has_cx4 = false;
|
||||
has_obc1 = false;
|
||||
has_st0018 = false;
|
||||
has_msu1 = false;
|
||||
has_serial = false;
|
||||
has_link = false;
|
||||
|
||||
nvram.reset();
|
||||
|
||||
parse_xml(xml_list);
|
||||
//print(xml_list[0], "\n\n");
|
||||
print(xml_list[0], "\n\n");
|
||||
|
||||
if(ram_size > 0) {
|
||||
ram.map(allocate<uint8>(ram_size, 0xff), ram_size);
|
||||
|
||||
@@ -38,15 +38,15 @@ public:
|
||||
readonly<bool> has_superfx;
|
||||
readonly<bool> has_sa1;
|
||||
readonly<bool> has_necdsp;
|
||||
readonly<bool> has_hitachidsp;
|
||||
readonly<bool> has_srtc;
|
||||
readonly<bool> has_sdd1;
|
||||
readonly<bool> has_spc7110;
|
||||
readonly<bool> has_spc7110rtc;
|
||||
readonly<bool> has_cx4;
|
||||
readonly<bool> has_obc1;
|
||||
readonly<bool> has_st0018;
|
||||
readonly<bool> has_msu1;
|
||||
readonly<bool> has_serial;
|
||||
readonly<bool> has_link;
|
||||
|
||||
struct NonVolatileRAM {
|
||||
const string id;
|
||||
@@ -104,18 +104,20 @@ private:
|
||||
void xml_parse_superfx(xml_element&);
|
||||
void xml_parse_sa1(xml_element&);
|
||||
void xml_parse_necdsp(xml_element&);
|
||||
void xml_parse_hitachidsp(xml_element&);
|
||||
void xml_parse_bsx(xml_element&);
|
||||
void xml_parse_sufamiturbo(xml_element&);
|
||||
void xml_parse_supergameboy(xml_element&);
|
||||
void xml_parse_srtc(xml_element&);
|
||||
void xml_parse_sdd1(xml_element&);
|
||||
void xml_parse_spc7110(xml_element&);
|
||||
void xml_parse_cx4(xml_element&);
|
||||
void xml_parse_obc1(xml_element&);
|
||||
void xml_parse_setarisc(xml_element&);
|
||||
void xml_parse_msu1(xml_element&);
|
||||
void xml_parse_serial(xml_element&);
|
||||
void xml_parse_link(xml_element&);
|
||||
|
||||
unsigned xml_parse_hex(const string&);
|
||||
unsigned xml_parse_unsigned(const string&);
|
||||
void xml_parse_address(Mapping&, const string&);
|
||||
void xml_parse_mode(Mapping&, const string&);
|
||||
};
|
||||
|
||||
+142
-51
@@ -40,16 +40,16 @@ void Cartridge::parse_xml_cartridge(const char *data) {
|
||||
if(node.name == "superfx") xml_parse_superfx(node);
|
||||
if(node.name == "sa1") xml_parse_sa1(node);
|
||||
if(node.name == "necdsp") xml_parse_necdsp(node);
|
||||
if(node.name == "hitachidsp") xml_parse_hitachidsp(node);
|
||||
if(node.name == "bsx") xml_parse_bsx(node);
|
||||
if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node);
|
||||
if(node.name == "srtc") xml_parse_srtc(node);
|
||||
if(node.name == "sdd1") xml_parse_sdd1(node);
|
||||
if(node.name == "spc7110") xml_parse_spc7110(node);
|
||||
if(node.name == "cx4") xml_parse_cx4(node);
|
||||
if(node.name == "obc1") xml_parse_obc1(node);
|
||||
if(node.name == "setarisc") xml_parse_setarisc(node);
|
||||
if(node.name == "msu1") xml_parse_msu1(node);
|
||||
if(node.name == "serial") xml_parse_serial(node);
|
||||
if(node.name == "link") xml_parse_link(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,8 +72,8 @@ void Cartridge::xml_parse_rom(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = rom.size();
|
||||
mapping.append(m);
|
||||
@@ -83,7 +83,7 @@ void Cartridge::xml_parse_rom(xml_element &root) {
|
||||
|
||||
void Cartridge::xml_parse_ram(xml_element &root) {
|
||||
foreach(attr, root.attribute) {
|
||||
if(attr.name == "size") ram_size = hex(attr.content);
|
||||
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
|
||||
}
|
||||
|
||||
foreach(leaf, root.element) {
|
||||
@@ -92,8 +92,8 @@ void Cartridge::xml_parse_ram(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = ram_size;
|
||||
mapping.append(m);
|
||||
@@ -120,7 +120,7 @@ void Cartridge::xml_parse_nss(xml_element &root) {
|
||||
unsigned value = 0x0000;
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "name") name = attr.parse();
|
||||
if(attr.name == "value") value = (uint16)hex(attr.content);
|
||||
if(attr.name == "value") value = (uint16)xml_parse_hex(attr.content);
|
||||
}
|
||||
information.nss.option[number].append({ hex<4>(value), ":", name });
|
||||
}
|
||||
@@ -161,15 +161,15 @@ void Cartridge::xml_parse_superfx(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
}
|
||||
} else if(node.name == "ram") {
|
||||
foreach(attr, node.attribute) {
|
||||
if(attr.name == "size") ram_size = hex(attr.content);
|
||||
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
|
||||
}
|
||||
|
||||
foreach(leaf, node.element) {
|
||||
@@ -178,8 +178,8 @@ void Cartridge::xml_parse_superfx(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = ram_size;
|
||||
mapping.append(m);
|
||||
@@ -212,8 +212,8 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
@@ -237,8 +237,8 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = 2048;
|
||||
mapping.append(m);
|
||||
@@ -246,7 +246,7 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
|
||||
}
|
||||
} else if(node.name == "bwram") {
|
||||
foreach(attr, node.attribute) {
|
||||
if(attr.name == "size") ram_size = hex(attr.content);
|
||||
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
|
||||
}
|
||||
|
||||
foreach(leaf, node.element) {
|
||||
@@ -255,8 +255,8 @@ void Cartridge::xml_parse_sa1(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = ram_size;
|
||||
mapping.append(m);
|
||||
@@ -288,11 +288,11 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
|
||||
string sha256;
|
||||
|
||||
foreach(attr, root.attribute) {
|
||||
if(attr.name == "revision") {
|
||||
if(attr.content == "upd7725" ) necdsp.revision = NECDSP::Revision::uPD7725;
|
||||
if(attr.content == "upd96050") necdsp.revision = NECDSP::Revision::uPD96050;
|
||||
if(attr.name == "model") {
|
||||
if(attr.content == "uPD7725" ) necdsp.revision = NECDSP::Revision::uPD7725;
|
||||
if(attr.content == "uPD96050") necdsp.revision = NECDSP::Revision::uPD96050;
|
||||
} else if(attr.name == "frequency") {
|
||||
necdsp.frequency = decimal(attr.content);
|
||||
necdsp.frequency = xml_parse_unsigned(attr.content);
|
||||
} else if(attr.name == "program") {
|
||||
program = attr.content;
|
||||
} else if(attr.name == "sha256") {
|
||||
@@ -371,6 +371,85 @@ void Cartridge::xml_parse_necdsp(xml_element &root) {
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::xml_parse_hitachidsp(xml_element &root) {
|
||||
has_hitachidsp = true;
|
||||
hitachidsp.frequency = 20000000;
|
||||
|
||||
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = 0x000000;
|
||||
|
||||
string dataROM, sha256;
|
||||
|
||||
foreach(attr, root.attribute) {
|
||||
if(attr.name == "frequency") {
|
||||
hitachidsp.frequency = xml_parse_unsigned(attr.content);
|
||||
} else if(attr.name == "data") {
|
||||
dataROM = attr.content;
|
||||
} else if(attr.name == "sha256") {
|
||||
sha256 = attr.content;
|
||||
}
|
||||
}
|
||||
|
||||
string path = { dir(system.interface->path(Slot::Base, ".dsp")), dataROM };
|
||||
file fp;
|
||||
if(fp.open(path, file::mode::read) == false) {
|
||||
system.interface->message({ "Warning: Hitachi DSP data ", dataROM, " is missing." });
|
||||
} else if(fp.size() != 1024 * 3) {
|
||||
system.interface->message({ "Warning: Hitachi DSP data ", dataROM, " is of the wrong file size." });
|
||||
fp.close();
|
||||
} else {
|
||||
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = fp.readl(3);
|
||||
|
||||
if(sha256 != "") {
|
||||
//XML file specified SHA256 sum for program. Verify file matches the hash.
|
||||
fp.seek(0);
|
||||
uint8 data[3072];
|
||||
fp.read(data, 3072);
|
||||
|
||||
sha256_ctx sha;
|
||||
uint8 hash[32];
|
||||
sha256_init(&sha);
|
||||
sha256_chunk(&sha, data, 3072);
|
||||
sha256_final(&sha);
|
||||
sha256_hash(&sha, hash);
|
||||
|
||||
string filehash;
|
||||
foreach(n, hash) filehash.append(hex<2>(n));
|
||||
|
||||
if(sha256 != filehash) {
|
||||
system.interface->message({ "Warning: Hitachi DSP data ", dataROM, " SHA256 sum is incorrect." });
|
||||
}
|
||||
}
|
||||
|
||||
fp.close();
|
||||
}
|
||||
|
||||
foreach(node, root.element) {
|
||||
if(node.name == "rom") {
|
||||
foreach(leaf, node.element) {
|
||||
if(leaf.name == "map") {
|
||||
Mapping m({ &HitachiDSP::rom_read, &hitachidsp }, { &HitachiDSP::rom_write, &hitachidsp });
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(node.name == "mmio") {
|
||||
foreach(leaf, node.element) {
|
||||
Mapping m({ &HitachiDSP::dsp_read, &hitachidsp }, { &HitachiDSP::dsp_write, &hitachidsp });
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::xml_parse_bsx(xml_element &root) {
|
||||
if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return;
|
||||
|
||||
@@ -382,8 +461,8 @@ void Cartridge::xml_parse_bsx(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
@@ -434,8 +513,8 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = memory.size();
|
||||
if(m.size) mapping.append(m);
|
||||
@@ -445,7 +524,7 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) {
|
||||
unsigned ram_size = 0;
|
||||
|
||||
foreach(attr, slot.attribute) {
|
||||
if(attr.name == "size") ram_size = hex(attr.content);
|
||||
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
|
||||
}
|
||||
|
||||
foreach(leaf, slot.element) {
|
||||
@@ -455,8 +534,8 @@ void Cartridge::xml_parse_sufamiturbo(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
if(m.size == 0) m.size = ram_size;
|
||||
if(m.size) mapping.append(m);
|
||||
@@ -531,7 +610,7 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
|
||||
Mapping m({ &SPC7110::mcu_read, &spc7110 }, { &SPC7110::mcu_write, &spc7110 });
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "offset") spc7110.data_rom_offset = hex(attr.content);
|
||||
if(attr.name == "offset") spc7110.data_rom_offset = xml_parse_hex(attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
@@ -548,7 +627,7 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
|
||||
}
|
||||
} else if(node.name == "ram") {
|
||||
foreach(attr, node.attribute) {
|
||||
if(attr.name == "size") ram_size = hex(attr.content);
|
||||
if(attr.name == "size") ram_size = xml_parse_hex(attr.content);
|
||||
}
|
||||
|
||||
foreach(leaf, node.element) {
|
||||
@@ -557,8 +636,8 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
|
||||
foreach(attr, leaf.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
if(attr.name == "mode") xml_parse_mode(m, attr.content);
|
||||
if(attr.name == "offset") m.offset = hex(attr.content);
|
||||
if(attr.name == "size") m.size = hex(attr.content);
|
||||
if(attr.name == "offset") m.offset = xml_parse_hex(attr.content);
|
||||
if(attr.name == "size") m.size = xml_parse_hex(attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
@@ -579,20 +658,6 @@ void Cartridge::xml_parse_spc7110(xml_element &root) {
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::xml_parse_cx4(xml_element &root) {
|
||||
has_cx4 = true;
|
||||
|
||||
foreach(node, root.element) {
|
||||
if(node.name == "map") {
|
||||
Mapping m({ &Cx4::read, &cx4 }, { &Cx4::write, &cx4 });
|
||||
foreach(attr, node.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::xml_parse_obc1(xml_element &root) {
|
||||
has_obc1 = true;
|
||||
|
||||
@@ -635,8 +700,34 @@ void Cartridge::xml_parse_msu1(xml_element &root) {
|
||||
}
|
||||
}
|
||||
|
||||
void Cartridge::xml_parse_serial(xml_element &root) {
|
||||
has_serial = true;
|
||||
void Cartridge::xml_parse_link(xml_element &root) {
|
||||
has_link = true;
|
||||
link.frequency = 1;
|
||||
link.program = "";
|
||||
|
||||
foreach(attr, root.attribute) {
|
||||
if(attr.name == "frequency") link.frequency = xml_parse_unsigned(attr.content);
|
||||
if(attr.name == "program") link.program = attr.content;
|
||||
}
|
||||
|
||||
foreach(node, root.element) {
|
||||
if(node.name == "map") {
|
||||
Mapping m({ &Link::read, &link }, { &Link::write, &link });
|
||||
foreach(attr, node.attribute) {
|
||||
if(attr.name == "address") xml_parse_address(m, attr.content);
|
||||
}
|
||||
mapping.append(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned Cartridge::xml_parse_hex(const string &s) {
|
||||
return hex(s);
|
||||
}
|
||||
|
||||
unsigned Cartridge::xml_parse_unsigned(const string &s) {
|
||||
if(s.beginswith("0x")) return hex(s);
|
||||
return integer(s);
|
||||
}
|
||||
|
||||
void Cartridge::xml_parse_address(Mapping &m, const string &data) {
|
||||
|
||||
@@ -15,7 +15,7 @@ void Cheat::enable(bool state) {
|
||||
}
|
||||
|
||||
void Cheat::synchronize() {
|
||||
memcpy(bus.lookup, lookup, 16 * 1024 * 1024);
|
||||
memset(override, 0x00, 16 * 1024 * 1024);
|
||||
code_enabled = false;
|
||||
|
||||
for(unsigned i = 0; i < size(); i++) {
|
||||
@@ -26,16 +26,16 @@ void Cheat::synchronize() {
|
||||
code_enabled = true;
|
||||
|
||||
unsigned addr = mirror(code.addr[n]);
|
||||
bus.lookup[addr] = 0xff;
|
||||
override[addr] = true;
|
||||
if((addr & 0xffe000) == 0x7e0000) {
|
||||
//mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff
|
||||
unsigned mirroraddr;
|
||||
for(unsigned x = 0; x <= 0x3f; x++) {
|
||||
mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff);
|
||||
bus.lookup[mirroraddr] = 0xff;
|
||||
override[mirroraddr] = true;
|
||||
|
||||
mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff);
|
||||
bus.lookup[mirroraddr] = 0xff;
|
||||
override[mirroraddr] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,25 +62,16 @@ uint8 Cheat::read(unsigned addr) const {
|
||||
}
|
||||
|
||||
void Cheat::init() {
|
||||
bus.reader[0xff] = [](unsigned addr) {
|
||||
bus.reader[cheat.lookup[addr]](bus.target[addr]);
|
||||
return cheat.read(addr);
|
||||
};
|
||||
|
||||
bus.writer[0xff] = [](unsigned addr, uint8 data) {
|
||||
return bus.writer[cheat.lookup[addr]](bus.target[addr], data);
|
||||
};
|
||||
|
||||
memcpy(lookup, bus.lookup, 16 * 1024 * 1024);
|
||||
memset(override, 0x00, 16 * 1024 * 1024);
|
||||
}
|
||||
|
||||
Cheat::Cheat() {
|
||||
lookup = new uint8[16 * 1024 * 1024];
|
||||
override = new uint8[16 * 1024 * 1024];
|
||||
system_enabled = true;
|
||||
}
|
||||
|
||||
Cheat::~Cheat() {
|
||||
delete[] lookup;
|
||||
delete[] override;
|
||||
}
|
||||
|
||||
//===============
|
||||
|
||||
@@ -10,6 +10,7 @@ struct CheatCode {
|
||||
class Cheat : public linear_vector<CheatCode> {
|
||||
public:
|
||||
enum class Type : unsigned { ProActionReplay, GameGenie };
|
||||
uint8 *override;
|
||||
|
||||
bool enabled() const;
|
||||
void enable(bool);
|
||||
@@ -24,7 +25,6 @@ public:
|
||||
static bool encode(string&, unsigned, uint8, Type);
|
||||
|
||||
private:
|
||||
uint8 *lookup;
|
||||
bool system_enabled;
|
||||
bool code_enabled;
|
||||
bool cheat_enabled;
|
||||
|
||||
@@ -8,16 +8,16 @@ struct Coprocessor : Processor {
|
||||
#include <snes/chip/superfx/superfx.hpp>
|
||||
#include <snes/chip/sa1/sa1.hpp>
|
||||
#include <snes/chip/necdsp/necdsp.hpp>
|
||||
#include <snes/chip/hitachidsp/hitachidsp.hpp>
|
||||
#include <snes/chip/bsx/bsx.hpp>
|
||||
#include <snes/chip/srtc/srtc.hpp>
|
||||
#include <snes/chip/sdd1/sdd1.hpp>
|
||||
#include <snes/chip/spc7110/spc7110.hpp>
|
||||
#include <snes/chip/cx4/cx4.hpp>
|
||||
#include <snes/chip/obc1/obc1.hpp>
|
||||
#include <snes/chip/st0018/st0018.hpp>
|
||||
#include <snes/chip/sufamiturbo/sufamiturbo.hpp>
|
||||
#include <snes/chip/msu1/msu1.hpp>
|
||||
#include <snes/chip/serial/serial.hpp>
|
||||
#include <snes/chip/link/link.hpp>
|
||||
|
||||
void Coprocessor::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
//=============
|
||||
//Cx4 emulation
|
||||
//=============
|
||||
//Used in Rockman X2/X3 (Megaman X2/X3)
|
||||
//Portions (c) anomie, Overload, zsKnight, Nach, byuu
|
||||
|
||||
#include <snes/snes.hpp>
|
||||
|
||||
#define CX4_CPP
|
||||
namespace SNES {
|
||||
|
||||
Cx4 cx4;
|
||||
|
||||
#include "serialization.cpp"
|
||||
#include "data.cpp"
|
||||
#include "functions.cpp"
|
||||
#include "oam.cpp"
|
||||
#include "opcodes.cpp"
|
||||
|
||||
void Cx4::init() {
|
||||
}
|
||||
|
||||
void Cx4::load() {
|
||||
}
|
||||
|
||||
void Cx4::unload() {
|
||||
}
|
||||
|
||||
uint32 Cx4::ldr(uint8 r) {
|
||||
uint16 addr = 0x0080 + (r * 3);
|
||||
return (reg[addr + 0] << 0)
|
||||
| (reg[addr + 1] << 8)
|
||||
| (reg[addr + 2] << 16);
|
||||
}
|
||||
|
||||
void Cx4::str(uint8 r, uint32 data) {
|
||||
uint16 addr = 0x0080 + (r * 3);
|
||||
reg[addr + 0] = (data >> 0);
|
||||
reg[addr + 1] = (data >> 8);
|
||||
reg[addr + 2] = (data >> 16);
|
||||
}
|
||||
|
||||
void Cx4::mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh) {
|
||||
int64 rx = x & 0xffffff;
|
||||
int64 ry = y & 0xffffff;
|
||||
if(rx & 0x800000)rx |= ~0x7fffff;
|
||||
if(ry & 0x800000)ry |= ~0x7fffff;
|
||||
|
||||
rx *= ry;
|
||||
|
||||
rl = (rx) & 0xffffff;
|
||||
rh = (rx >> 24) & 0xffffff;
|
||||
}
|
||||
|
||||
uint32 Cx4::sin(uint32 rx) {
|
||||
r0 = rx & 0x1ff;
|
||||
if(r0 & 0x100)r0 ^= 0x1ff;
|
||||
if(r0 & 0x080)r0 ^= 0x0ff;
|
||||
if(rx & 0x100) {
|
||||
return sin_table[r0 + 0x80];
|
||||
} else {
|
||||
return sin_table[r0];
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Cx4::cos(uint32 rx) {
|
||||
return sin(rx + 0x080);
|
||||
}
|
||||
|
||||
void Cx4::immediate_reg(uint32 start) {
|
||||
r0 = ldr(0);
|
||||
for(uint32 i = start; i < 48; i++) {
|
||||
if((r0 & 0x0fff) < 0x0c00) {
|
||||
ram[r0 & 0x0fff] = immediate_data[i];
|
||||
}
|
||||
r0++;
|
||||
}
|
||||
str(0, r0);
|
||||
}
|
||||
|
||||
void Cx4::transfer_data() {
|
||||
uint32 src;
|
||||
uint16 dest, count;
|
||||
|
||||
src = (reg[0x40]) | (reg[0x41] << 8) | (reg[0x42] << 16);
|
||||
count = (reg[0x43]) | (reg[0x44] << 8);
|
||||
dest = (reg[0x45]) | (reg[0x46] << 8);
|
||||
|
||||
for(uint32 i=0;i<count;i++) {
|
||||
write(dest++, bus.read(src++));
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::write(unsigned addr, uint8 data) {
|
||||
addr &= 0x1fff;
|
||||
|
||||
if(addr < 0x0c00) {
|
||||
//ram
|
||||
ram[addr] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr < 0x1f00) {
|
||||
//unmapped
|
||||
return;
|
||||
}
|
||||
|
||||
//command register
|
||||
reg[addr & 0xff] = data;
|
||||
|
||||
if(addr == 0x1f47) {
|
||||
//memory transfer
|
||||
transfer_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if(addr == 0x1f4f) {
|
||||
//c4 command
|
||||
if(reg[0x4d] == 0x0e && !(data & 0xc3)) {
|
||||
//c4 test command
|
||||
reg[0x80] = data >> 2;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(data) {
|
||||
case 0x00: op00(); break;
|
||||
case 0x01: op01(); break;
|
||||
case 0x05: op05(); break;
|
||||
case 0x0d: op0d(); break;
|
||||
case 0x10: op10(); break;
|
||||
case 0x13: op13(); break;
|
||||
case 0x15: op15(); break;
|
||||
case 0x1f: op1f(); break;
|
||||
case 0x22: op22(); break;
|
||||
case 0x25: op25(); break;
|
||||
case 0x2d: op2d(); break;
|
||||
case 0x40: op40(); break;
|
||||
case 0x54: op54(); break;
|
||||
case 0x5c: op5c(); break;
|
||||
case 0x5e: op5e(); break;
|
||||
case 0x60: op60(); break;
|
||||
case 0x62: op62(); break;
|
||||
case 0x64: op64(); break;
|
||||
case 0x66: op66(); break;
|
||||
case 0x68: op68(); break;
|
||||
case 0x6a: op6a(); break;
|
||||
case 0x6c: op6c(); break;
|
||||
case 0x6e: op6e(); break;
|
||||
case 0x70: op70(); break;
|
||||
case 0x72: op72(); break;
|
||||
case 0x74: op74(); break;
|
||||
case 0x76: op76(); break;
|
||||
case 0x78: op78(); break;
|
||||
case 0x7a: op7a(); break;
|
||||
case 0x7c: op7c(); break;
|
||||
case 0x89: op89(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::writeb(uint16 addr, uint8 data) {
|
||||
write(addr, data);
|
||||
}
|
||||
|
||||
void Cx4::writew(uint16 addr, uint16 data) {
|
||||
write(addr + 0, data >> 0);
|
||||
write(addr + 1, data >> 8);
|
||||
}
|
||||
|
||||
void Cx4::writel(uint16 addr, uint32 data) {
|
||||
write(addr + 0, data >> 0);
|
||||
write(addr + 1, data >> 8);
|
||||
write(addr + 2, data >> 16);
|
||||
}
|
||||
|
||||
uint8 Cx4::read(unsigned addr) {
|
||||
addr &= 0x1fff;
|
||||
|
||||
if(addr < 0x0c00) {
|
||||
return ram[addr];
|
||||
}
|
||||
|
||||
if(addr >= 0x1f00) {
|
||||
return reg[addr & 0xff];
|
||||
}
|
||||
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
uint8 Cx4::readb(uint16 addr) {
|
||||
return read(addr);
|
||||
}
|
||||
|
||||
uint16 Cx4::readw(uint16 addr) {
|
||||
return read(addr) | (read(addr + 1) << 8);
|
||||
}
|
||||
|
||||
uint32 Cx4::readl(uint16 addr) {
|
||||
return read(addr) | (read(addr + 1) << 8) + (read(addr + 2) << 16);
|
||||
}
|
||||
|
||||
void Cx4::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Cx4::reset() {
|
||||
memset(ram, 0, 0x0c00);
|
||||
memset(reg, 0, 0x0100);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,96 +0,0 @@
|
||||
class Cx4 {
|
||||
public:
|
||||
void init();
|
||||
void load();
|
||||
void unload();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 read(unsigned addr);
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
uint8 ram[0x0c00];
|
||||
uint8 reg[0x0100];
|
||||
uint32 r0, r1, r2, r3, r4, r5, r6, r7,
|
||||
r8, r9, r10, r11, r12, r13, r14, r15;
|
||||
|
||||
static const uint8 immediate_data[48];
|
||||
static const uint16 wave_data[40];
|
||||
static const uint32 sin_table[256];
|
||||
|
||||
static const int16 SinTable[512];
|
||||
static const int16 CosTable[512];
|
||||
|
||||
int16 C4WFXVal, C4WFYVal, C4WFZVal, C4WFX2Val, C4WFY2Val, C4WFDist, C4WFScale;
|
||||
int16 C41FXVal, C41FYVal, C41FAngleRes, C41FDist, C41FDistVal;
|
||||
|
||||
void C4TransfWireFrame();
|
||||
void C4TransfWireFrame2();
|
||||
void C4CalcWireFrame();
|
||||
void C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color);
|
||||
void C4DrawWireFrame();
|
||||
void C4DoScaleRotate(int row_padding);
|
||||
|
||||
public:
|
||||
uint32 ldr(uint8 r);
|
||||
void str(uint8 r, uint32 data);
|
||||
void mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh);
|
||||
uint32 sin(uint32 rx);
|
||||
uint32 cos(uint32 rx);
|
||||
|
||||
void transfer_data();
|
||||
void immediate_reg(uint32 num);
|
||||
|
||||
void op00_00();
|
||||
void op00_03();
|
||||
void op00_05();
|
||||
void op00_07();
|
||||
void op00_08();
|
||||
void op00_0b();
|
||||
void op00_0c();
|
||||
|
||||
void op00();
|
||||
void op01();
|
||||
void op05();
|
||||
void op0d();
|
||||
void op10();
|
||||
void op13();
|
||||
void op15();
|
||||
void op1f();
|
||||
void op22();
|
||||
void op25();
|
||||
void op2d();
|
||||
void op40();
|
||||
void op54();
|
||||
void op5c();
|
||||
void op5e();
|
||||
void op60();
|
||||
void op62();
|
||||
void op64();
|
||||
void op66();
|
||||
void op68();
|
||||
void op6a();
|
||||
void op6c();
|
||||
void op6e();
|
||||
void op70();
|
||||
void op72();
|
||||
void op74();
|
||||
void op76();
|
||||
void op78();
|
||||
void op7a();
|
||||
void op7c();
|
||||
void op89();
|
||||
|
||||
uint8 readb(uint16 addr);
|
||||
uint16 readw(uint16 addr);
|
||||
uint32 readl(uint16 addr);
|
||||
|
||||
void writeb(uint16 addr, uint8 data);
|
||||
void writew(uint16 addr, uint16 data);
|
||||
void writel(uint16 addr, uint32 data);
|
||||
};
|
||||
|
||||
extern Cx4 cx4;
|
||||
@@ -1,187 +0,0 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
const uint8 Cx4::immediate_data[48] = {
|
||||
0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
|
||||
0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0xff, 0x7f,
|
||||
0x00, 0x80, 0x00, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0xff,
|
||||
0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x00
|
||||
};
|
||||
|
||||
const uint16 Cx4::wave_data[40] = {
|
||||
0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e,
|
||||
0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020a, 0x020c, 0x020e,
|
||||
0x0400, 0x0402, 0x0404, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e,
|
||||
0x0600, 0x0602, 0x0604, 0x0606, 0x0608, 0x060a, 0x060c, 0x060e,
|
||||
0x0800, 0x0802, 0x0804, 0x0806, 0x0808, 0x080a, 0x080c, 0x080e
|
||||
};
|
||||
|
||||
const uint32 Cx4::sin_table[256] = {
|
||||
0x000000, 0x000324, 0x000648, 0x00096c, 0x000c8f, 0x000fb2, 0x0012d5, 0x0015f6,
|
||||
0x001917, 0x001c37, 0x001f56, 0x002273, 0x002590, 0x0028aa, 0x002bc4, 0x002edb,
|
||||
0x0031f1, 0x003505, 0x003817, 0x003b26, 0x003e33, 0x00413e, 0x004447, 0x00474d,
|
||||
0x004a50, 0x004d50, 0x00504d, 0x005347, 0x00563e, 0x005931, 0x005c22, 0x005f0e,
|
||||
0x0061f7, 0x0064dc, 0x0067bd, 0x006a9b, 0x006d74, 0x007049, 0x007319, 0x0075e5,
|
||||
0x0078ad, 0x007b70, 0x007e2e, 0x0080e7, 0x00839c, 0x00864b, 0x0088f5, 0x008b9a,
|
||||
0x008e39, 0x0090d3, 0x009368, 0x0095f6, 0x00987f, 0x009b02, 0x009d7f, 0x009ff6,
|
||||
0x00a267, 0x00a4d2, 0x00a736, 0x00a994, 0x00abeb, 0x00ae3b, 0x00b085, 0x00b2c8,
|
||||
0x00b504, 0x00b73a, 0x00b968, 0x00bb8f, 0x00bdae, 0x00bfc7, 0x00c1d8, 0x00c3e2,
|
||||
0x00c5e4, 0x00c7de, 0x00c9d1, 0x00cbbb, 0x00cd9f, 0x00cf7a, 0x00d14d, 0x00d318,
|
||||
0x00d4db, 0x00d695, 0x00d848, 0x00d9f2, 0x00db94, 0x00dd2d, 0x00debe, 0x00e046,
|
||||
0x00e1c5, 0x00e33c, 0x00e4aa, 0x00e60f, 0x00e76b, 0x00e8bf, 0x00ea09, 0x00eb4b,
|
||||
0x00ec83, 0x00edb2, 0x00eed8, 0x00eff5, 0x00f109, 0x00f213, 0x00f314, 0x00f40b,
|
||||
0x00f4fa, 0x00f5de, 0x00f6ba, 0x00f78b, 0x00f853, 0x00f912, 0x00f9c7, 0x00fa73,
|
||||
0x00fb14, 0x00fbac, 0x00fc3b, 0x00fcbf, 0x00fd3a, 0x00fdab, 0x00fe13, 0x00fe70,
|
||||
0x00fec4, 0x00ff0e, 0x00ff4e, 0x00ff84, 0x00ffb1, 0x00ffd3, 0x00ffec, 0x00fffb,
|
||||
0x000000, 0xfffcdb, 0xfff9b7, 0xfff693, 0xfff370, 0xfff04d, 0xffed2a, 0xffea09,
|
||||
0xffe6e8, 0xffe3c8, 0xffe0a9, 0xffdd8c, 0xffda6f, 0xffd755, 0xffd43b, 0xffd124,
|
||||
0xffce0e, 0xffcafa, 0xffc7e8, 0xffc4d9, 0xffc1cc, 0xffbec1, 0xffbbb8, 0xffb8b2,
|
||||
0xffb5af, 0xffb2af, 0xffafb2, 0xffacb8, 0xffa9c1, 0xffa6ce, 0xffa3dd, 0xffa0f1,
|
||||
0xff9e08, 0xff9b23, 0xff9842, 0xff9564, 0xff928b, 0xff8fb6, 0xff8ce6, 0xff8a1a,
|
||||
0xff8752, 0xff848f, 0xff81d1, 0xff7f18, 0xff7c63, 0xff79b4, 0xff770a, 0xff7465,
|
||||
0xff71c6, 0xff6f2c, 0xff6c97, 0xff6a09, 0xff6780, 0xff64fd, 0xff6280, 0xff6009,
|
||||
0xff5d98, 0xff5b2d, 0xff58c9, 0xff566b, 0xff5414, 0xff51c4, 0xff4f7a, 0xff4d37,
|
||||
0xff4afb, 0xff48c5, 0xff4697, 0xff4470, 0xff4251, 0xff4038, 0xff3e27, 0xff3c1e,
|
||||
0xff3a1b, 0xff3821, 0xff362e, 0xff3444, 0xff3260, 0xff3085, 0xff2eb2, 0xff2ce7,
|
||||
0xff2b24, 0xff296a, 0xff27b7, 0xff260d, 0xff246b, 0xff22d2, 0xff2141, 0xff1fb9,
|
||||
0xff1e3a, 0xff1cc3, 0xff1b55, 0xff19f0, 0xff1894, 0xff1740, 0xff15f6, 0xff14b4,
|
||||
0xff137c, 0xff124d, 0xff1127, 0xff100a, 0xff0ef6, 0xff0dec, 0xff0ceb, 0xff0bf4,
|
||||
0xff0b05, 0xff0a21, 0xff0945, 0xff0874, 0xff07ac, 0xff06ed, 0xff0638, 0xff058d,
|
||||
0xff04eb, 0xff0453, 0xff03c4, 0xff0340, 0xff02c5, 0xff0254, 0xff01ec, 0xff018f,
|
||||
0xff013b, 0xff00f1, 0xff00b1, 0xff007b, 0xff004e, 0xff002c, 0xff0013, 0xff0004
|
||||
};
|
||||
|
||||
const int16 Cx4::SinTable[512] = {
|
||||
0, 402, 804, 1206, 1607, 2009, 2410, 2811,
|
||||
3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997,
|
||||
6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126,
|
||||
9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167,
|
||||
12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090,
|
||||
15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869,
|
||||
18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475,
|
||||
20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884,
|
||||
23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073,
|
||||
25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020,
|
||||
27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707,
|
||||
28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117,
|
||||
30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237,
|
||||
31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057,
|
||||
32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568,
|
||||
32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765,
|
||||
32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647,
|
||||
32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214,
|
||||
32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471,
|
||||
31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425,
|
||||
30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086,
|
||||
28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466,
|
||||
27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583,
|
||||
25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453,
|
||||
23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097,
|
||||
20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537,
|
||||
18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800,
|
||||
15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910,
|
||||
12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896,
|
||||
9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786,
|
||||
6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611,
|
||||
3211, 2811, 2410, 2009, 1607, 1206, 804, 402,
|
||||
0, -402, -804, -1206, -1607, -2009, -2410, -2811,
|
||||
-3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997,
|
||||
-6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126,
|
||||
-9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167,
|
||||
-12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090,
|
||||
-15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869,
|
||||
-18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475,
|
||||
-20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884,
|
||||
-23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073,
|
||||
-25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020,
|
||||
-27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707,
|
||||
-28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117,
|
||||
-30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237,
|
||||
-31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057,
|
||||
-32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568,
|
||||
-32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765,
|
||||
-32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647,
|
||||
-32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214,
|
||||
-32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471,
|
||||
-31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425,
|
||||
-30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086,
|
||||
-28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466,
|
||||
-27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583,
|
||||
-25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453,
|
||||
-23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097,
|
||||
-20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537,
|
||||
-18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800,
|
||||
-15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910,
|
||||
-12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896,
|
||||
-9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786,
|
||||
-6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611,
|
||||
-3211, -2811, -2410, -2009, -1607, -1206, -804, -402
|
||||
};
|
||||
|
||||
const int16 Cx4::CosTable[512] = {
|
||||
32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647,
|
||||
32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214,
|
||||
32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471,
|
||||
31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425,
|
||||
30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086,
|
||||
28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466,
|
||||
27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583,
|
||||
25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453,
|
||||
23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097,
|
||||
20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537,
|
||||
18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800,
|
||||
15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910,
|
||||
12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896,
|
||||
9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786,
|
||||
6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611,
|
||||
3211, 2811, 2410, 2009, 1607, 1206, 804, 402,
|
||||
0, -402, -804, -1206, -1607, -2009, -2410, -2811,
|
||||
-3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997,
|
||||
-6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126,
|
||||
-9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167,
|
||||
-12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090,
|
||||
-15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869,
|
||||
-18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475,
|
||||
-20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884,
|
||||
-23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073,
|
||||
-25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020,
|
||||
-27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707,
|
||||
-28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117,
|
||||
-30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237,
|
||||
-31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057,
|
||||
-32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568,
|
||||
-32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765,
|
||||
-32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647,
|
||||
-32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214,
|
||||
-32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471,
|
||||
-31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425,
|
||||
-30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086,
|
||||
-28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466,
|
||||
-27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583,
|
||||
-25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453,
|
||||
-23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097,
|
||||
-20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537,
|
||||
-18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800,
|
||||
-15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910,
|
||||
-12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896,
|
||||
-9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786,
|
||||
-6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611,
|
||||
-3211, -2811, -2410, -2009, -1607, -1206, -804, -402,
|
||||
0, 402, 804, 1206, 1607, 2009, 2410, 2811,
|
||||
3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997,
|
||||
6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126,
|
||||
9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167,
|
||||
12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090,
|
||||
15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869,
|
||||
18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475,
|
||||
20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884,
|
||||
23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073,
|
||||
25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020,
|
||||
27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707,
|
||||
28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117,
|
||||
30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237,
|
||||
31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057,
|
||||
32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568,
|
||||
32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,251 +0,0 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
#include <math.h>
|
||||
#define Tan(a) (CosTable[a] ? ((((int32)SinTable[a]) << 16) / CosTable[a]) : 0x80000000)
|
||||
#define sar(b, n) ((b) >> (n))
|
||||
#ifdef PI
|
||||
#undef PI
|
||||
#endif
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
|
||||
//Wireframe Helpers
|
||||
void Cx4::C4TransfWireFrame() {
|
||||
double c4x = (double)C4WFXVal;
|
||||
double c4y = (double)C4WFYVal;
|
||||
double c4z = (double)C4WFZVal - 0x95;
|
||||
double tanval, c4x2, c4y2, c4z2;
|
||||
|
||||
//Rotate X
|
||||
tanval = -(double)C4WFX2Val * PI * 2 / 128;
|
||||
c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval);
|
||||
c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval);
|
||||
|
||||
//Rotate Y
|
||||
tanval = -(double)C4WFY2Val * PI * 2 / 128;
|
||||
c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval);
|
||||
c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval);
|
||||
|
||||
//Rotate Z
|
||||
tanval = -(double)C4WFDist * PI * 2 / 128;
|
||||
c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval);
|
||||
c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval);
|
||||
|
||||
//Scale
|
||||
C4WFXVal = (int16)(c4x * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95);
|
||||
C4WFYVal = (int16)(c4y * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95);
|
||||
}
|
||||
|
||||
void Cx4::C4CalcWireFrame() {
|
||||
C4WFXVal = C4WFX2Val - C4WFXVal;
|
||||
C4WFYVal = C4WFY2Val - C4WFYVal;
|
||||
|
||||
if(abs(C4WFXVal) > abs(C4WFYVal)) {
|
||||
C4WFDist = abs(C4WFXVal) + 1;
|
||||
C4WFYVal = (256 * (long)C4WFYVal) / abs(C4WFXVal);
|
||||
C4WFXVal = (C4WFXVal < 0) ? -256 : 256;
|
||||
} else if(C4WFYVal != 0) {
|
||||
C4WFDist = abs(C4WFYVal) + 1;
|
||||
C4WFXVal = (256 * (long)C4WFXVal) / abs(C4WFYVal);
|
||||
C4WFYVal = (C4WFYVal < 0) ? -256 : 256;
|
||||
} else {
|
||||
C4WFDist = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::C4TransfWireFrame2() {
|
||||
double c4x = (double)C4WFXVal;
|
||||
double c4y = (double)C4WFYVal;
|
||||
double c4z = (double)C4WFZVal;
|
||||
double tanval, c4x2, c4y2, c4z2;
|
||||
|
||||
//Rotate X
|
||||
tanval = -(double)C4WFX2Val * PI * 2 / 128;
|
||||
c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval);
|
||||
c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval);
|
||||
|
||||
//Rotate Y
|
||||
tanval = -(double)C4WFY2Val * PI * 2 / 128;
|
||||
c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval);
|
||||
c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval);
|
||||
|
||||
//Rotate Z
|
||||
tanval = -(double)C4WFDist * PI * 2 / 128;
|
||||
c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval);
|
||||
c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval);
|
||||
|
||||
//Scale
|
||||
C4WFXVal = (int16)(c4x * C4WFScale / 0x100);
|
||||
C4WFYVal = (int16)(c4y * C4WFScale / 0x100);
|
||||
}
|
||||
|
||||
void Cx4::C4DrawWireFrame() {
|
||||
uint32 line = readl(0x1f80);
|
||||
uint32 point1, point2;
|
||||
int16 X1, Y1, Z1;
|
||||
int16 X2, Y2, Z2;
|
||||
uint8 Color;
|
||||
|
||||
for(int32 i = ram[0x0295]; i > 0; i--, line += 5) {
|
||||
if(bus.read(line) == 0xff && bus.read(line + 1) == 0xff) {
|
||||
int32 tmp = line - 5;
|
||||
while(bus.read(tmp + 2) == 0xff && bus.read(tmp + 3) == 0xff && (tmp + 2) >= 0) { tmp -= 5; }
|
||||
point1 = (read(0x1f82) << 16) | (bus.read(tmp + 2) << 8) | bus.read(tmp + 3);
|
||||
} else {
|
||||
point1 = (read(0x1f82) << 16) | (bus.read(line) << 8) | bus.read(line + 1);
|
||||
}
|
||||
point2 = (read(0x1f82) << 16) | (bus.read(line + 2) << 8) | bus.read(line + 3);
|
||||
|
||||
X1=(bus.read(point1 + 0) << 8) | bus.read(point1 + 1);
|
||||
Y1=(bus.read(point1 + 2) << 8) | bus.read(point1 + 3);
|
||||
Z1=(bus.read(point1 + 4) << 8) | bus.read(point1 + 5);
|
||||
X2=(bus.read(point2 + 0) << 8) | bus.read(point2 + 1);
|
||||
Y2=(bus.read(point2 + 2) << 8) | bus.read(point2 + 3);
|
||||
Z2=(bus.read(point2 + 4) << 8) | bus.read(point2 + 5);
|
||||
Color = bus.read(line + 4);
|
||||
C4DrawLine(X1, Y1, Z1, X2, Y2, Z2, Color);
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color) {
|
||||
//Transform coordinates
|
||||
C4WFXVal = (int16)X1;
|
||||
C4WFYVal = (int16)Y1;
|
||||
C4WFZVal = Z1;
|
||||
C4WFScale = read(0x1f90);
|
||||
C4WFX2Val = read(0x1f86);
|
||||
C4WFY2Val = read(0x1f87);
|
||||
C4WFDist = read(0x1f88);
|
||||
C4TransfWireFrame2();
|
||||
X1 = (C4WFXVal + 48) << 8;
|
||||
Y1 = (C4WFYVal + 48) << 8;
|
||||
|
||||
C4WFXVal = (int16)X2;
|
||||
C4WFYVal = (int16)Y2;
|
||||
C4WFZVal = Z2;
|
||||
C4TransfWireFrame2();
|
||||
X2 = (C4WFXVal + 48) << 8;
|
||||
Y2 = (C4WFYVal + 48) << 8;
|
||||
|
||||
//Get line info
|
||||
C4WFXVal = (int16)(X1 >> 8);
|
||||
C4WFYVal = (int16)(Y1 >> 8);
|
||||
C4WFX2Val = (int16)(X2 >> 8);
|
||||
C4WFY2Val = (int16)(Y2 >> 8);
|
||||
C4CalcWireFrame();
|
||||
X2 = (int16)C4WFXVal;
|
||||
Y2 = (int16)C4WFYVal;
|
||||
|
||||
//Render line
|
||||
for(int32 i = C4WFDist ? C4WFDist : 1; i > 0; i--) {
|
||||
if(X1 > 0xff && Y1 > 0xff && X1 < 0x6000 && Y1 < 0x6000) {
|
||||
uint16 addr = (((Y1 >> 8) >> 3) << 8) - (((Y1 >> 8) >> 3) << 6) + (((X1 >> 8) >> 3) << 4) + ((Y1 >> 8) & 7) * 2;
|
||||
uint8 bit = 0x80 >> ((X1 >> 8) & 7);
|
||||
ram[addr + 0x300] &= ~bit;
|
||||
ram[addr + 0x301] &= ~bit;
|
||||
if(Color & 1) ram[addr + 0x300] |= bit;
|
||||
if(Color & 2) ram[addr + 0x301] |= bit;
|
||||
}
|
||||
X1 += X2;
|
||||
Y1 += Y2;
|
||||
}
|
||||
}
|
||||
|
||||
void Cx4::C4DoScaleRotate(int row_padding) {
|
||||
int16 A, B, C, D;
|
||||
|
||||
//Calculate matrix
|
||||
int32 XScale = readw(0x1f8f);
|
||||
int32 YScale = readw(0x1f92);
|
||||
|
||||
if(XScale & 0x8000)XScale = 0x7fff;
|
||||
if(YScale & 0x8000)YScale = 0x7fff;
|
||||
|
||||
if(readw(0x1f80) == 0) { //no rotation
|
||||
A = (int16)XScale;
|
||||
B = 0;
|
||||
C = 0;
|
||||
D = (int16)YScale;
|
||||
} else if(readw(0x1f80) == 128) { //90 degree rotation
|
||||
A = 0;
|
||||
B = (int16)(-YScale);
|
||||
C = (int16)XScale;
|
||||
D = 0;
|
||||
} else if(readw(0x1f80) == 256) { //180 degree rotation
|
||||
A = (int16)(-XScale);
|
||||
B = 0;
|
||||
C = 0;
|
||||
D = (int16)(-YScale);
|
||||
} else if(readw(0x1f80) == 384) { //270 degree rotation
|
||||
A = 0;
|
||||
B = (int16)YScale;
|
||||
C = (int16)(-XScale);
|
||||
D = 0;
|
||||
} else {
|
||||
A = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * XScale, 15);
|
||||
B = (int16)(-sar(SinTable[readw(0x1f80) & 0x1ff] * YScale, 15));
|
||||
C = (int16) sar(SinTable[readw(0x1f80) & 0x1ff] * XScale, 15);
|
||||
D = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * YScale, 15);
|
||||
}
|
||||
|
||||
//Calculate Pixel Resolution
|
||||
uint8 w = read(0x1f89) & ~7;
|
||||
uint8 h = read(0x1f8c) & ~7;
|
||||
|
||||
//Clear the output RAM
|
||||
memset(ram, 0, (w + row_padding / 4) * h / 2);
|
||||
|
||||
int32 Cx = (int16)readw(0x1f83);
|
||||
int32 Cy = (int16)readw(0x1f86);
|
||||
|
||||
//Calculate start position (i.e. (Ox, Oy) = (0, 0))
|
||||
//The low 12 bits are fractional, so (Cx<<12) gives us the Cx we want in
|
||||
//the function. We do Cx*A etc normally because the matrix parameters
|
||||
//already have the fractional parts.
|
||||
int32 LineX = (Cx << 12) - Cx * A - Cx * B;
|
||||
int32 LineY = (Cy << 12) - Cy * C - Cy * D;
|
||||
|
||||
//Start loop
|
||||
uint32 X, Y;
|
||||
uint8 byte;
|
||||
int32 outidx = 0;
|
||||
uint8 bit = 0x80;
|
||||
|
||||
for(int32 y = 0; y < h; y++) {
|
||||
X = LineX;
|
||||
Y = LineY;
|
||||
for(int32 x = 0; x < w; x++) {
|
||||
if((X >> 12) >= w || (Y >> 12) >= h) {
|
||||
byte = 0;
|
||||
} else {
|
||||
uint32 addr = (Y >> 12) * w + (X >> 12);
|
||||
byte = read(0x600 + (addr >> 1));
|
||||
if(addr & 1) { byte >>= 4; }
|
||||
}
|
||||
|
||||
//De-bitplanify
|
||||
if(byte & 1) ram[outidx ] |= bit;
|
||||
if(byte & 2) ram[outidx + 1] |= bit;
|
||||
if(byte & 4) ram[outidx + 16] |= bit;
|
||||
if(byte & 8) ram[outidx + 17] |= bit;
|
||||
|
||||
bit >>= 1;
|
||||
if(!bit) {
|
||||
bit = 0x80;
|
||||
outidx += 32;
|
||||
}
|
||||
|
||||
X += A; //Add 1 to output x => add an A and a C
|
||||
Y += C;
|
||||
}
|
||||
outidx += 2 + row_padding;
|
||||
if(outidx & 0x10) {
|
||||
outidx &= ~0x10;
|
||||
} else {
|
||||
outidx -= w * 4 + row_padding;
|
||||
}
|
||||
LineX += B; //Add 1 to output y => add a B and a D
|
||||
LineY += D;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
//Build OAM
|
||||
void Cx4::op00_00() {
|
||||
uint32 oamptr = ram[0x626] << 2;
|
||||
for(int32 i = 0x1fd; i > oamptr && i >= 0; i -= 4) {
|
||||
//clear oam-to-be
|
||||
if(i >= 0) ram[i] = 0xe0;
|
||||
}
|
||||
|
||||
uint16 globalx, globaly;
|
||||
uint32 oamptr2;
|
||||
int16 sprx, spry;
|
||||
uint8 sprname, sprattr;
|
||||
uint8 sprcount;
|
||||
|
||||
globalx = readw(0x621);
|
||||
globaly = readw(0x623);
|
||||
oamptr2 = 0x200 + (ram[0x626] >> 2);
|
||||
|
||||
if(!ram[0x620]) return;
|
||||
|
||||
sprcount = 128 - ram[0x626];
|
||||
uint8 offset = (ram[0x626] & 3) * 2;
|
||||
uint32 srcptr = 0x220;
|
||||
|
||||
for(int i = ram[0x620]; i > 0 && sprcount > 0; i--, srcptr += 16) {
|
||||
sprx = readw(srcptr) - globalx;
|
||||
spry = readw(srcptr + 2) - globaly;
|
||||
sprname = ram[srcptr + 5];
|
||||
sprattr = ram[srcptr + 4] | ram[srcptr + 6];
|
||||
|
||||
uint32 spraddr = readl(srcptr + 7);
|
||||
if(bus.read(spraddr)) {
|
||||
int16 x, y;
|
||||
for(int sprcnt = bus.read(spraddr++); sprcnt > 0 && sprcount > 0; sprcnt--, spraddr += 4) {
|
||||
x = (int8)bus.read(spraddr + 1);
|
||||
if(sprattr & 0x40) {
|
||||
x = -x - ((bus.read(spraddr) & 0x20) ? 16 : 8);
|
||||
}
|
||||
x += sprx;
|
||||
if(x >= -16 && x <= 272) {
|
||||
y = (int8)bus.read(spraddr + 2);
|
||||
if(sprattr & 0x80) {
|
||||
y = -y - ((bus.read(spraddr) & 0x20) ? 16 : 8);
|
||||
}
|
||||
y += spry;
|
||||
if(y >= -16 && y <= 224) {
|
||||
ram[oamptr ] = (uint8)x;
|
||||
ram[oamptr + 1] = (uint8)y;
|
||||
ram[oamptr + 2] = sprname + bus.read(spraddr + 3);
|
||||
ram[oamptr + 3] = sprattr ^ (bus.read(spraddr) & 0xc0);
|
||||
ram[oamptr2] &= ~(3 << offset);
|
||||
if(x & 0x100) ram[oamptr2] |= 1 << offset;
|
||||
if(bus.read(spraddr) & 0x20) ram[oamptr2] |= 2 << offset;
|
||||
oamptr += 4;
|
||||
sprcount--;
|
||||
offset = (offset + 2) & 6;
|
||||
if(!offset)oamptr2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(sprcount > 0) {
|
||||
ram[oamptr ] = (uint8)sprx;
|
||||
ram[oamptr + 1] = (uint8)spry;
|
||||
ram[oamptr + 2] = sprname;
|
||||
ram[oamptr + 3] = sprattr;
|
||||
ram[oamptr2] &= ~(3 << offset);
|
||||
if(sprx & 0x100) ram[oamptr2] |= 3 << offset;
|
||||
else ram[oamptr2] |= 2 << offset;
|
||||
oamptr += 4;
|
||||
sprcount--;
|
||||
offset = (offset + 2) & 6;
|
||||
if(!offset) oamptr2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Scale and Rotate
|
||||
void Cx4::op00_03() {
|
||||
C4DoScaleRotate(0);
|
||||
}
|
||||
|
||||
//Transform Lines
|
||||
void Cx4::op00_05() {
|
||||
C4WFX2Val = read(0x1f83);
|
||||
C4WFY2Val = read(0x1f86);
|
||||
C4WFDist = read(0x1f89);
|
||||
C4WFScale = read(0x1f8c);
|
||||
|
||||
//Transform Vertices
|
||||
uint32 ptr = 0;
|
||||
for(int32 i = readw(0x1f80); i > 0; i--, ptr += 0x10) {
|
||||
C4WFXVal = readw(ptr + 1);
|
||||
C4WFYVal = readw(ptr + 5);
|
||||
C4WFZVal = readw(ptr + 9);
|
||||
C4TransfWireFrame();
|
||||
|
||||
//Displace
|
||||
writew(ptr + 1, C4WFXVal + 0x80);
|
||||
writew(ptr + 5, C4WFYVal + 0x50);
|
||||
}
|
||||
|
||||
writew(0x600, 23);
|
||||
writew(0x602, 0x60);
|
||||
writew(0x605, 0x40);
|
||||
writew(0x600 + 8, 23);
|
||||
writew(0x602 + 8, 0x60);
|
||||
writew(0x605 + 8, 0x40);
|
||||
|
||||
ptr = 0xb02;
|
||||
uint32 ptr2 = 0;
|
||||
|
||||
for(int32 i = readw(0xb00); i > 0; i--, ptr += 2, ptr2 += 8) {
|
||||
C4WFXVal = readw((read(ptr + 0) << 4) + 1);
|
||||
C4WFYVal = readw((read(ptr + 0) << 4) + 5);
|
||||
C4WFX2Val = readw((read(ptr + 1) << 4) + 1);
|
||||
C4WFY2Val = readw((read(ptr + 1) << 4) + 5);
|
||||
C4CalcWireFrame();
|
||||
writew(ptr2 + 0x600, C4WFDist ? C4WFDist : 1);
|
||||
writew(ptr2 + 0x602, C4WFXVal);
|
||||
writew(ptr2 + 0x605, C4WFYVal);
|
||||
}
|
||||
}
|
||||
|
||||
//Scale and Rotate
|
||||
void Cx4::op00_07() {
|
||||
C4DoScaleRotate(64);
|
||||
}
|
||||
|
||||
//Draw Wireframe
|
||||
void Cx4::op00_08() {
|
||||
C4DrawWireFrame();
|
||||
}
|
||||
|
||||
//Disintegrate
|
||||
void Cx4::op00_0b() {
|
||||
uint8 width, height;
|
||||
uint32 startx, starty;
|
||||
uint32 srcptr;
|
||||
uint32 x, y;
|
||||
int32 scalex, scaley;
|
||||
int32 cx, cy;
|
||||
int32 i, j;
|
||||
|
||||
width = read(0x1f89);
|
||||
height = read(0x1f8c);
|
||||
cx = readw(0x1f80);
|
||||
cy = readw(0x1f83);
|
||||
|
||||
scalex = (int16)readw(0x1f86);
|
||||
scaley = (int16)readw(0x1f8f);
|
||||
startx = -cx * scalex + (cx << 8);
|
||||
starty = -cy * scaley + (cy << 8);
|
||||
srcptr = 0x600;
|
||||
|
||||
for(i = 0; i < (width * height) >> 1; i++) {
|
||||
write(i, 0);
|
||||
}
|
||||
|
||||
for(y = starty, i = 0;i < height; i++, y += scaley) {
|
||||
for(x = startx, j = 0;j < width; j++, x += scalex) {
|
||||
if((x >> 8) < width && (y >> 8) < height && (y >> 8) * width + (x >> 8) < 0x2000) {
|
||||
uint8 pixel = (j & 1) ? (ram[srcptr] >> 4) : (ram[srcptr]);
|
||||
int32 index = (y >> 11) * width * 4 + (x >> 11) * 32 + ((y >> 8) & 7) * 2;
|
||||
uint8 mask = 0x80 >> ((x >> 8) & 7);
|
||||
|
||||
if(pixel & 1) ram[index ] |= mask;
|
||||
if(pixel & 2) ram[index + 1] |= mask;
|
||||
if(pixel & 4) ram[index + 16] |= mask;
|
||||
if(pixel & 8) ram[index + 17] |= mask;
|
||||
}
|
||||
if(j & 1) srcptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Bitplane Wave
|
||||
void Cx4::op00_0c() {
|
||||
uint32 destptr = 0;
|
||||
uint32 waveptr = read(0x1f83);
|
||||
uint16 mask1 = 0xc0c0;
|
||||
uint16 mask2 = 0x3f3f;
|
||||
|
||||
for(int j = 0; j < 0x10; j++) {
|
||||
do {
|
||||
int16 height = -((int8)read(waveptr + 0xb00)) - 16;
|
||||
for(int i = 0; i < 40; i++) {
|
||||
uint16 temp = readw(destptr + wave_data[i]) & mask2;
|
||||
if(height >= 0) {
|
||||
if(height < 8) {
|
||||
temp |= mask1 & readw(0xa00 + height * 2);
|
||||
} else {
|
||||
temp |= mask1 & 0xff00;
|
||||
}
|
||||
}
|
||||
writew(destptr + wave_data[i], temp);
|
||||
height++;
|
||||
}
|
||||
waveptr = (waveptr + 1) & 0x7f;
|
||||
mask1 = (mask1 >> 2) | (mask1 << 6);
|
||||
mask2 = (mask2 >> 2) | (mask2 << 6);
|
||||
} while(mask1 != 0xc0c0);
|
||||
destptr += 16;
|
||||
|
||||
do {
|
||||
int16 height = -((int8)read(waveptr + 0xb00)) - 16;
|
||||
for(int i = 0; i < 40; i++) {
|
||||
uint16 temp = readw(destptr + wave_data[i]) & mask2;
|
||||
if(height >= 0) {
|
||||
if(height < 8) {
|
||||
temp |= mask1 & readw(0xa10 + height * 2);
|
||||
} else {
|
||||
temp |= mask1 & 0xff00;
|
||||
}
|
||||
}
|
||||
writew(destptr + wave_data[i], temp);
|
||||
height++;
|
||||
}
|
||||
waveptr = (waveptr + 1) & 0x7f;
|
||||
mask1 = (mask1 >> 2) | (mask1 << 6);
|
||||
mask2 = (mask2 >> 2) | (mask2 << 6);
|
||||
} while(mask1 != 0xc0c0);
|
||||
destptr += 16;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,228 +0,0 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
//Sprite Functions
|
||||
void Cx4::op00() {
|
||||
switch(reg[0x4d]) {
|
||||
case 0x00: op00_00(); break;
|
||||
case 0x03: op00_03(); break;
|
||||
case 0x05: op00_05(); break;
|
||||
case 0x07: op00_07(); break;
|
||||
case 0x08: op00_08(); break;
|
||||
case 0x0b: op00_0b(); break;
|
||||
case 0x0c: op00_0c(); break;
|
||||
}
|
||||
}
|
||||
|
||||
//Draw Wireframe
|
||||
void Cx4::op01() {
|
||||
memset(ram + 0x300, 0, 2304);
|
||||
C4DrawWireFrame();
|
||||
}
|
||||
|
||||
//Propulsion
|
||||
void Cx4::op05() {
|
||||
int32 temp = 0x10000;
|
||||
if(readw(0x1f83)) {
|
||||
temp = sar((temp / readw(0x1f83)) * readw(0x1f81), 8);
|
||||
}
|
||||
writew(0x1f80, temp);
|
||||
}
|
||||
|
||||
//Set Vector length
|
||||
void Cx4::op0d() {
|
||||
C41FXVal = readw(0x1f80);
|
||||
C41FYVal = readw(0x1f83);
|
||||
C41FDistVal = readw(0x1f86);
|
||||
double tanval = sqrt(((double)C41FYVal) * ((double)C41FYVal) + ((double)C41FXVal) * ((double)C41FXVal));
|
||||
tanval = (double)C41FDistVal / tanval;
|
||||
C41FYVal = (int16)(((double)C41FYVal * tanval) * 0.99);
|
||||
C41FXVal = (int16)(((double)C41FXVal * tanval) * 0.98);
|
||||
writew(0x1f89, C41FXVal);
|
||||
writew(0x1f8c, C41FYVal);
|
||||
}
|
||||
|
||||
//Triangle
|
||||
void Cx4::op10() {
|
||||
r0 = ldr(0);
|
||||
r1 = ldr(1);
|
||||
|
||||
r4 = r0 & 0x1ff;
|
||||
if(r1 & 0x8000)r1 |= ~0x7fff;
|
||||
else r1 &= 0x7fff;
|
||||
|
||||
mul(cos(r4), r1, r5, r2);
|
||||
r5 = (r5 >> 16) & 0xff;
|
||||
r2 = (r2 << 8) + r5;
|
||||
|
||||
mul(sin(r4), r1, r5, r3);
|
||||
r5 = (r5 >> 16) & 0xff;
|
||||
r3 = (r3 << 8) + r5;
|
||||
|
||||
str(0, r0);
|
||||
str(1, r1);
|
||||
str(2, r2);
|
||||
str(3, r3);
|
||||
str(4, r4);
|
||||
str(5, r5);
|
||||
}
|
||||
|
||||
//Triangle
|
||||
void Cx4::op13() {
|
||||
r0 = ldr(0);
|
||||
r1 = ldr(1);
|
||||
|
||||
r4 = r0 & 0x1ff;
|
||||
|
||||
mul(cos(r4), r1, r5, r2);
|
||||
r5 = (r5 >> 8) & 0xffff;
|
||||
r2 = (r2 << 16) + r5;
|
||||
|
||||
mul(sin(r4), r1, r5, r3);
|
||||
r5 = (r5 >> 8) & 0xffff;
|
||||
r3 = (r3 << 16) + r5;
|
||||
|
||||
str(0, r0);
|
||||
str(1, r1);
|
||||
str(2, r2);
|
||||
str(3, r3);
|
||||
str(4, r4);
|
||||
str(5, r5);
|
||||
}
|
||||
|
||||
//Pythagorean
|
||||
void Cx4::op15() {
|
||||
C41FXVal = readw(0x1f80);
|
||||
C41FYVal = readw(0x1f83);
|
||||
C41FDist = (int16)sqrt((double)C41FXVal * (double)C41FXVal + (double)C41FYVal * (double)C41FYVal);
|
||||
writew(0x1f80, C41FDist);
|
||||
}
|
||||
|
||||
//Calculate distance
|
||||
void Cx4::op1f() {
|
||||
C41FXVal = readw(0x1f80);
|
||||
C41FYVal = readw(0x1f83);
|
||||
if(!C41FXVal) {
|
||||
C41FAngleRes = (C41FYVal > 0) ? 0x080 : 0x180;
|
||||
} else {
|
||||
double tanval = ((double)C41FYVal) / ((double)C41FXVal);
|
||||
C41FAngleRes = (short)(atan(tanval) / (PI * 2) * 512);
|
||||
C41FAngleRes = C41FAngleRes;
|
||||
if(C41FXVal < 0) {
|
||||
C41FAngleRes += 0x100;
|
||||
}
|
||||
C41FAngleRes &= 0x1ff;
|
||||
}
|
||||
writew(0x1f86, C41FAngleRes);
|
||||
}
|
||||
|
||||
//Trapezoid
|
||||
void Cx4::op22() {
|
||||
int16 angle1 = readw(0x1f8c) & 0x1ff;
|
||||
int16 angle2 = readw(0x1f8f) & 0x1ff;
|
||||
int32 tan1 = Tan(angle1);
|
||||
int32 tan2 = Tan(angle2);
|
||||
int16 y = readw(0x1f83) - readw(0x1f89);
|
||||
int16 left, right;
|
||||
|
||||
for(int32 j = 0; j < 225; j++, y++) {
|
||||
if(y >= 0) {
|
||||
left = sar((int32)tan1 * y, 16) - readw(0x1f80) + readw(0x1f86);
|
||||
right = sar((int32)tan2 * y, 16) - readw(0x1f80) + readw(0x1f86) + readw(0x1f93);
|
||||
|
||||
if(left < 0 && right < 0) {
|
||||
left = 1;
|
||||
right = 0;
|
||||
} else if(left < 0) {
|
||||
left = 0;
|
||||
} else if(right < 0) {
|
||||
right = 0;
|
||||
}
|
||||
|
||||
if(left > 255 && right > 255) {
|
||||
left = 255;
|
||||
right = 254;
|
||||
} else if(left > 255) {
|
||||
left = 255;
|
||||
} else if(right > 255) {
|
||||
right = 255;
|
||||
}
|
||||
} else {
|
||||
left = 1;
|
||||
right = 0;
|
||||
}
|
||||
ram[j + 0x800] = (uint8)left;
|
||||
ram[j + 0x900] = (uint8)right;
|
||||
}
|
||||
}
|
||||
|
||||
//Multiply
|
||||
void Cx4::op25() {
|
||||
r0 = ldr(0);
|
||||
r1 = ldr(1);
|
||||
mul(r0, r1, r0, r1);
|
||||
str(0, r0);
|
||||
str(1, r1);
|
||||
}
|
||||
|
||||
//Transform Coords
|
||||
void Cx4::op2d() {
|
||||
C4WFXVal = readw(0x1f81);
|
||||
C4WFYVal = readw(0x1f84);
|
||||
C4WFZVal = readw(0x1f87);
|
||||
C4WFX2Val = read (0x1f89);
|
||||
C4WFY2Val = read (0x1f8a);
|
||||
C4WFDist = read (0x1f8b);
|
||||
C4WFScale = readw(0x1f90);
|
||||
C4TransfWireFrame2();
|
||||
writew(0x1f80, C4WFXVal);
|
||||
writew(0x1f83, C4WFYVal);
|
||||
}
|
||||
|
||||
//Sum
|
||||
void Cx4::op40() {
|
||||
r0 = 0;
|
||||
for(uint32 i=0;i<0x800;i++) {
|
||||
r0 += ram[i];
|
||||
}
|
||||
str(0, r0);
|
||||
}
|
||||
|
||||
//Square
|
||||
void Cx4::op54() {
|
||||
r0 = ldr(0);
|
||||
mul(r0, r0, r1, r2);
|
||||
str(1, r1);
|
||||
str(2, r2);
|
||||
}
|
||||
|
||||
//Immediate Register
|
||||
void Cx4::op5c() {
|
||||
str(0, 0x000000);
|
||||
immediate_reg(0);
|
||||
}
|
||||
|
||||
//Immediate Register (Multiple)
|
||||
void Cx4::op5e() { immediate_reg( 0); }
|
||||
void Cx4::op60() { immediate_reg( 3); }
|
||||
void Cx4::op62() { immediate_reg( 6); }
|
||||
void Cx4::op64() { immediate_reg( 9); }
|
||||
void Cx4::op66() { immediate_reg(12); }
|
||||
void Cx4::op68() { immediate_reg(15); }
|
||||
void Cx4::op6a() { immediate_reg(18); }
|
||||
void Cx4::op6c() { immediate_reg(21); }
|
||||
void Cx4::op6e() { immediate_reg(24); }
|
||||
void Cx4::op70() { immediate_reg(27); }
|
||||
void Cx4::op72() { immediate_reg(30); }
|
||||
void Cx4::op74() { immediate_reg(33); }
|
||||
void Cx4::op76() { immediate_reg(36); }
|
||||
void Cx4::op78() { immediate_reg(39); }
|
||||
void Cx4::op7a() { immediate_reg(42); }
|
||||
void Cx4::op7c() { immediate_reg(45); }
|
||||
|
||||
//Immediate ROM
|
||||
void Cx4::op89() {
|
||||
str(0, 0x054336);
|
||||
str(1, 0xffffff);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,39 +0,0 @@
|
||||
#ifdef CX4_CPP
|
||||
|
||||
void Cx4::serialize(serializer &s) {
|
||||
s.array(ram);
|
||||
s.array(reg);
|
||||
|
||||
s.integer(r0);
|
||||
s.integer(r1);
|
||||
s.integer(r2);
|
||||
s.integer(r3);
|
||||
s.integer(r4);
|
||||
s.integer(r5);
|
||||
s.integer(r6);
|
||||
s.integer(r7);
|
||||
s.integer(r8);
|
||||
s.integer(r9);
|
||||
s.integer(r10);
|
||||
s.integer(r11);
|
||||
s.integer(r12);
|
||||
s.integer(r13);
|
||||
s.integer(r14);
|
||||
s.integer(r15);
|
||||
|
||||
s.integer(C4WFXVal);
|
||||
s.integer(C4WFYVal);
|
||||
s.integer(C4WFZVal);
|
||||
s.integer(C4WFX2Val);
|
||||
s.integer(C4WFY2Val);
|
||||
s.integer(C4WFDist);
|
||||
s.integer(C4WFScale);
|
||||
|
||||
s.integer(C41FXVal);
|
||||
s.integer(C41FYVal);
|
||||
s.integer(C41FAngleRes);
|
||||
s.integer(C41FDist);
|
||||
s.integer(C41FDistVal);
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#include <snes/snes.hpp>
|
||||
|
||||
#define HITACHIDSP_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "memory.cpp"
|
||||
#include "opcodes.cpp"
|
||||
#include "registers.cpp"
|
||||
#include "serialization.cpp"
|
||||
HitachiDSP hitachidsp;
|
||||
|
||||
void HitachiDSP::Enter() { hitachidsp.enter(); }
|
||||
|
||||
void HitachiDSP::enter() {
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
switch(state) {
|
||||
case State::Idle:
|
||||
step(1);
|
||||
break;
|
||||
case State::DMA:
|
||||
for(unsigned n = 0; n < regs.dma_length; n++) {
|
||||
bus.write(regs.dma_target + n, bus.read(regs.dma_source + n));
|
||||
step(2);
|
||||
}
|
||||
state = State::Idle;
|
||||
break;
|
||||
case State::Execute:
|
||||
unsigned offset = regs.program_offset + regs.pc * 2;
|
||||
opcode = bus_read(offset + 0) << 0;
|
||||
opcode |= bus_read(offset + 1) << 8;
|
||||
regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff);
|
||||
exec();
|
||||
step(1);
|
||||
break;
|
||||
}
|
||||
|
||||
synchronize_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
void HitachiDSP::init() {
|
||||
}
|
||||
|
||||
void HitachiDSP::load() {
|
||||
}
|
||||
|
||||
void HitachiDSP::unload() {
|
||||
}
|
||||
|
||||
void HitachiDSP::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void HitachiDSP::reset() {
|
||||
create(HitachiDSP::Enter, frequency);
|
||||
state = State::Idle;
|
||||
|
||||
regs.n = 0;
|
||||
regs.z = 0;
|
||||
regs.c = 0;
|
||||
|
||||
regs.dma_source = 0x000000;
|
||||
regs.dma_length = 0x0000;
|
||||
regs.dma_target = 0x000000;
|
||||
regs.r1f48 = 0x00;
|
||||
regs.program_offset = 0x000000;
|
||||
regs.r1f4c = 0x00;
|
||||
regs.page_number = 0x0000;
|
||||
regs.program_counter = 0x00;
|
||||
regs.r1f50 = 0x33;
|
||||
regs.r1f51 = 0x00;
|
||||
regs.r1f52 = 0x01;
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
//Hitachi HG51B169
|
||||
|
||||
class HitachiDSP : public Coprocessor {
|
||||
public:
|
||||
unsigned frequency;
|
||||
//uint16 programROM[2][256];
|
||||
uint24 dataROM[1024];
|
||||
uint8 dataRAM[3072];
|
||||
uint24 stack[8];
|
||||
uint16 opcode;
|
||||
enum class State : unsigned { Idle, DMA, Execute } state;
|
||||
#include "registers.hpp"
|
||||
|
||||
static void Enter();
|
||||
void enter();
|
||||
|
||||
void init();
|
||||
void load();
|
||||
void unload();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
//memory.cpp
|
||||
uint8 bus_read(unsigned addr);
|
||||
void bus_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 rom_read(unsigned addr);
|
||||
void rom_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 dsp_read(unsigned addr);
|
||||
void dsp_write(unsigned addr, uint8 data);
|
||||
|
||||
//opcodes.cpp
|
||||
void push();
|
||||
void pull();
|
||||
unsigned sa();
|
||||
unsigned ri();
|
||||
unsigned np();
|
||||
void exec();
|
||||
|
||||
//registers.cpp
|
||||
unsigned reg_read(unsigned n) const;
|
||||
void reg_write(unsigned n, unsigned data);
|
||||
|
||||
void serialize(serializer&);
|
||||
};
|
||||
|
||||
extern HitachiDSP hitachidsp;
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
#ifdef HITACHIDSP_CPP
|
||||
|
||||
uint8 HitachiDSP::bus_read(unsigned addr) {
|
||||
if((addr & 0x408000) == 0x008000) return bus.read(addr);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void HitachiDSP::bus_write(unsigned addr, uint8 data) {
|
||||
if((addr & 0x40e000) == 0x006000) return bus.write(addr, data);
|
||||
}
|
||||
|
||||
uint8 HitachiDSP::rom_read(unsigned addr) {
|
||||
if(co_active() == cpu.thread) {
|
||||
if(state == State::Idle) return cartridge.rom.read(addr);
|
||||
if((addr & 0x40ffe0) == 0x00ffe0) return regs.vector[addr & 0x1f];
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
if(co_active() == hitachidsp.thread) {
|
||||
return cartridge.rom.read(addr);
|
||||
}
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void HitachiDSP::rom_write(unsigned addr, uint8 data) {
|
||||
}
|
||||
|
||||
uint8 HitachiDSP::dsp_read(unsigned addr) {
|
||||
addr &= 0x1fff;
|
||||
|
||||
//Data RAM
|
||||
if((addr >= 0x0000 && addr <= 0x0bff) || (addr >= 0x1000 && addr <= 0x1bff)) {
|
||||
return dataRAM[addr & 0x0fff];
|
||||
}
|
||||
|
||||
//MMIO
|
||||
switch(addr) {
|
||||
case 0x1f40: return regs.dma_source >> 0;
|
||||
case 0x1f41: return regs.dma_source >> 8;
|
||||
case 0x1f42: return regs.dma_source >> 16;
|
||||
case 0x1f43: return regs.dma_length >> 0;
|
||||
case 0x1f44: return regs.dma_length >> 8;
|
||||
case 0x1f45: return regs.dma_target >> 0;
|
||||
case 0x1f46: return regs.dma_target >> 8;
|
||||
case 0x1f47: return regs.dma_target >> 16;
|
||||
case 0x1f48: return regs.r1f48;
|
||||
case 0x1f49: return regs.program_offset >> 0;
|
||||
case 0x1f4a: return regs.program_offset >> 8;
|
||||
case 0x1f4b: return regs.program_offset >> 16;
|
||||
case 0x1f4c: return regs.r1f4c;
|
||||
case 0x1f4d: return regs.page_number >> 0;
|
||||
case 0x1f4e: return regs.page_number >> 8;
|
||||
case 0x1f4f: return regs.program_counter;
|
||||
case 0x1f50: return regs.r1f50;
|
||||
case 0x1f51: return regs.r1f51;
|
||||
case 0x1f52: return regs.r1f52;
|
||||
case 0x1f53: case 0x1f54: case 0x1f55: case 0x1f56:
|
||||
case 0x1f57: case 0x1f58: case 0x1f59: case 0x1f5a:
|
||||
case 0x1f5b: case 0x1f5c: case 0x1f5d: case 0x1f5e:
|
||||
case 0x1f5f: return ((state != State::Idle) << 6) | ((state == State::Idle) << 1);
|
||||
}
|
||||
|
||||
//Vector
|
||||
if(addr >= 0x1f60 && addr <= 0x1f7f) {
|
||||
return regs.vector[addr & 0x1f];
|
||||
}
|
||||
|
||||
//GPRs
|
||||
if((addr >= 0x1f80 && addr <= 0x1faf) || (addr >= 0x1fc0 && addr <= 0x1fef)) {
|
||||
unsigned index = (addr & 0x3f) / 3; //0..15
|
||||
unsigned shift = ((addr & 0x3f) % 3) * 8; //0, 8, 16
|
||||
return regs.gpr[index] >> shift;
|
||||
}
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
void HitachiDSP::dsp_write(unsigned addr, uint8 data) {
|
||||
addr &= 0x1fff;
|
||||
|
||||
//Data RAM
|
||||
if((addr >= 0x0000 && addr <= 0x0bff) || (addr >= 0x1000 && addr <= 0x1bff)) {
|
||||
dataRAM[addr & 0x0fff] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
//MMIO
|
||||
switch(addr) {
|
||||
case 0x1f40: regs.dma_source = (regs.dma_source & 0xffff00) | (data << 0); return;
|
||||
case 0x1f41: regs.dma_source = (regs.dma_source & 0xff00ff) | (data << 8); return;
|
||||
case 0x1f42: regs.dma_source = (regs.dma_source & 0x00ffff) | (data << 16); return;
|
||||
case 0x1f43: regs.dma_length = (regs.dma_length & 0xff00) | (data << 0); return;
|
||||
case 0x1f44: regs.dma_length = (regs.dma_length & 0x00ff) | (data << 8); return;
|
||||
case 0x1f45: regs.dma_target = (regs.dma_target & 0xffff00) | (data << 0); return;
|
||||
case 0x1f46: regs.dma_target = (regs.dma_target & 0xff00ff) | (data << 8); return;
|
||||
case 0x1f47: regs.dma_target = (regs.dma_target & 0x00ffff) | (data << 16);
|
||||
if(state == State::Idle) state = State::DMA;
|
||||
return;
|
||||
case 0x1f48: regs.r1f48 = data & 0x01; return;
|
||||
case 0x1f49: regs.program_offset = (regs.program_offset & 0xffff00) | (data << 0); return;
|
||||
case 0x1f4a: regs.program_offset = (regs.program_offset & 0xff00ff) | (data << 8); return;
|
||||
case 0x1f4b: regs.program_offset = (regs.program_offset & 0x00ffff) | (data << 16); return;
|
||||
case 0x1f4c: regs.r1f4c = data & 0x03; return;
|
||||
case 0x1f4d: regs.page_number = (regs.page_number & 0x7f00) | ((data & 0xff) << 0); return;
|
||||
case 0x1f4e: regs.page_number = (regs.page_number & 0x00ff) | ((data & 0x7f) << 8); return;
|
||||
case 0x1f4f: regs.program_counter = data;
|
||||
if(state == State::Idle) {
|
||||
regs.pc = regs.page_number * 256 + regs.program_counter;
|
||||
state = State::Execute;
|
||||
}
|
||||
return;
|
||||
case 0x1f50: regs.r1f50 = data & 0x77; return;
|
||||
case 0x1f51: regs.r1f51 = data & 0x01; return;
|
||||
case 0x1f52: regs.r1f52 = data & 0x01; return;
|
||||
}
|
||||
|
||||
//Vector
|
||||
if(addr >= 0x1f60 && addr <= 0x1f7f) {
|
||||
regs.vector[addr & 0x1f] = data;
|
||||
return;
|
||||
}
|
||||
|
||||
//GPRs
|
||||
if((addr >= 0x1f80 && addr <= 0x1faf) || (addr >= 0x1fc0 && addr <= 0x1fef)) {
|
||||
unsigned index = (addr & 0x3f) / 3;
|
||||
switch((addr & 0x3f) % 3) {
|
||||
case 0: regs.gpr[index] = (regs.gpr[index] & 0xffff00) | (data << 0); return;
|
||||
case 1: regs.gpr[index] = (regs.gpr[index] & 0xff00ff) | (data << 8); return;
|
||||
case 2: regs.gpr[index] = (regs.gpr[index] & 0x00ffff) | (data << 16); return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+356
@@ -0,0 +1,356 @@
|
||||
#ifdef HITACHIDSP_CPP
|
||||
|
||||
void HitachiDSP::push() {
|
||||
stack[7] = stack[6];
|
||||
stack[6] = stack[5];
|
||||
stack[5] = stack[4];
|
||||
stack[4] = stack[3];
|
||||
stack[3] = stack[2];
|
||||
stack[2] = stack[1];
|
||||
stack[1] = stack[0];
|
||||
stack[0] = regs.pc;
|
||||
}
|
||||
|
||||
void HitachiDSP::pull() {
|
||||
regs.pc = stack[0];
|
||||
stack[0] = stack[1];
|
||||
stack[1] = stack[2];
|
||||
stack[2] = stack[3];
|
||||
stack[3] = stack[4];
|
||||
stack[4] = stack[5];
|
||||
stack[5] = stack[6];
|
||||
stack[6] = stack[7];
|
||||
stack[7] = 0x0000;
|
||||
}
|
||||
|
||||
//Shift-A: math opcodes can shift A register prior to ALU operation
|
||||
unsigned HitachiDSP::sa() {
|
||||
switch(opcode & 0x0300) { default:
|
||||
case 0x0000: return regs.a << 0;
|
||||
case 0x0100: return regs.a << 1;
|
||||
case 0x0200: return regs.a << 8;
|
||||
case 0x0300: return regs.a << 16;
|
||||
}
|
||||
}
|
||||
|
||||
//Register-or-Immediate: most opcodes can load from a register or immediate
|
||||
unsigned HitachiDSP::ri() {
|
||||
if(opcode & 0x0400) return opcode & 0xff;
|
||||
return reg_read(opcode & 0xff);
|
||||
}
|
||||
|
||||
//New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes)
|
||||
unsigned HitachiDSP::np() {
|
||||
if(opcode & 0x0200) return (regs.p << 8) | (opcode & 0xff);
|
||||
return (regs.pc & 0xffff00) | (opcode & 0xff);
|
||||
}
|
||||
|
||||
void HitachiDSP::exec() {
|
||||
if((opcode & 0xffff) == 0x0000) {
|
||||
//0000 0000 0000 0000
|
||||
//nop
|
||||
}
|
||||
|
||||
else if((opcode & 0xdd00) == 0x0800) {
|
||||
//00.0 10.0 .... ....
|
||||
//jump i
|
||||
if(opcode & 0x2000) push();
|
||||
regs.pc = np();
|
||||
}
|
||||
|
||||
else if((opcode & 0xdd00) == 0x0c00) {
|
||||
//00.0 11.0 .... ....
|
||||
//jumpeq i
|
||||
if(regs.z) {
|
||||
if(opcode & 0x2000) push();
|
||||
regs.pc = np();
|
||||
}
|
||||
}
|
||||
|
||||
else if((opcode & 0xdd00) == 0x1000) {
|
||||
//00.1 00.0 .... ....
|
||||
//jumpge i
|
||||
if(regs.c) {
|
||||
if(opcode & 0x2000) push();
|
||||
regs.pc = np();
|
||||
}
|
||||
}
|
||||
|
||||
else if((opcode & 0xdd00) == 0x1400) {
|
||||
//00.1 01.0 .... ....
|
||||
//jumpmi i
|
||||
if(regs.n) {
|
||||
if(opcode & 0x2000) push();
|
||||
regs.pc = np();
|
||||
}
|
||||
}
|
||||
|
||||
else if((opcode & 0xffff) == 0x1c00) {
|
||||
//0001 1100 0000 0000
|
||||
//loop?
|
||||
}
|
||||
|
||||
else if((opcode & 0xfffe) == 0x2500) {
|
||||
//0010 0101 0000 000.
|
||||
//skiplt/skipge
|
||||
if(regs.c == (opcode & 1)) regs.pc++;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfffe) == 0x2600) {
|
||||
//0010 0110 0000 000.
|
||||
//skipne/skipeq
|
||||
if(regs.z == (opcode & 1)) regs.pc++;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfffe) == 0x2700) {
|
||||
//0010 0111 0000 000.
|
||||
//skipmi/skippl
|
||||
if(regs.n == (opcode & 1)) regs.pc++;
|
||||
}
|
||||
|
||||
else if((opcode & 0xffff) == 0x3c00) {
|
||||
//0011 1100 0000 0000
|
||||
//ret
|
||||
pull();
|
||||
}
|
||||
|
||||
else if((opcode & 0xffff) == 0x4000) {
|
||||
//0100 0000 0000 0000
|
||||
//rdbus
|
||||
regs.busdata = bus_read(regs.busaddr++);
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0x4800) {
|
||||
//0100 1... .... ....
|
||||
//cmpr a<<n,ri
|
||||
int result = ri() - sa();
|
||||
regs.n = result & 0x800000;
|
||||
regs.z = (uint24)result == 0;
|
||||
regs.c = result >= 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0x5000) {
|
||||
//0101 0... .... ....
|
||||
//cmp a<<n,ri
|
||||
int result = sa() - ri();
|
||||
regs.n = result & 0x800000;
|
||||
regs.z = (uint24)result == 0;
|
||||
regs.c = result >= 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x5900) {
|
||||
//0101 1.01 .... ....
|
||||
//sxb
|
||||
regs.a = (int8)ri();
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x5a00) {
|
||||
//0101 1.10 .... ....
|
||||
//sxw
|
||||
regs.a = (int16)ri();
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x6000) {
|
||||
//0110 0.00 .... ....
|
||||
//ld a,ri
|
||||
regs.a = ri();
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x6100) {
|
||||
//0110 0.01 .... ....
|
||||
//ld ?,ri
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x6300) {
|
||||
//0110 0.11 .... ....
|
||||
//ld p,ri
|
||||
regs.p = ri();
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x6800) {
|
||||
//0110 1.00 .... ....
|
||||
//rdraml
|
||||
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0);
|
||||
if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xffff00) | (dataRAM[target] << 0);
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x6900) {
|
||||
//0110 1.01 .... ....
|
||||
//rdramh
|
||||
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0);
|
||||
if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xff00ff) | (dataRAM[target] << 8);
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x6a00) {
|
||||
//0110 1.10 .... ....
|
||||
//rdramb
|
||||
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0);
|
||||
if(target < 0xc00) regs.ramdata = (regs.ramdata & 0x00ffff) | (dataRAM[target] << 16);
|
||||
}
|
||||
|
||||
else if((opcode & 0xffff) == 0x7000) {
|
||||
//0111 0000 0000 0000
|
||||
//rdrom
|
||||
regs.romdata = dataROM[regs.a & 0x3ff];
|
||||
}
|
||||
|
||||
else if((opcode & 0xff00) == 0x7c00) {
|
||||
//0111 1100 .... ....
|
||||
//ld pl,i
|
||||
regs.p = (regs.p & 0xff00) | ((opcode & 0xff) << 0);
|
||||
}
|
||||
|
||||
else if((opcode & 0xff00) == 0x7d00) {
|
||||
//0111 1101 .... ....
|
||||
//ld ph,i
|
||||
regs.p = (regs.p & 0x00ff) | ((opcode & 0xff) << 8);
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0x8000) {
|
||||
//1000 0... .... ....
|
||||
//add a<<n,ri
|
||||
int result = sa() + ri();
|
||||
regs.a = result;
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
regs.c = result > 0xffffff;
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0x8800) {
|
||||
//1000 1... .... ....
|
||||
//subr a<<n,ri
|
||||
int result = ri() - sa();
|
||||
regs.a = result;
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
regs.c = result >= 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0x9000) {
|
||||
//1001 0... .... ....
|
||||
//sub a<<n,ri
|
||||
int result = sa() - ri();
|
||||
regs.a = result;
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
regs.c = result >= 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0x9800) {
|
||||
//1001 1.00 .... ....
|
||||
//mul a,ri
|
||||
int64 x = (int24)regs.a;
|
||||
int64 y = (int24)ri();
|
||||
x *= y;
|
||||
regs.accl = x >> 0ull;
|
||||
regs.acch = x >> 24ull;
|
||||
regs.n = regs.acch & 0x800000;
|
||||
regs.z = x == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0xa800) {
|
||||
//1010 1... .... ....
|
||||
//xor a<<n,ri
|
||||
regs.a = sa() ^ ri();
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0xb000) {
|
||||
//1011 0... .... ....
|
||||
//and a<<n,ri
|
||||
regs.a = sa() & ri();
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xf800) == 0xb800) {
|
||||
//1011 1... .... ....
|
||||
//or a<<n,ri
|
||||
regs.a = sa() | ri();
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xc000) {
|
||||
//1100 0.00 .... ....
|
||||
//shr a,ri
|
||||
regs.a = regs.a >> ri();
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xc800) {
|
||||
//1100 1.00 .... ....
|
||||
//asr a,ri
|
||||
regs.a = (int24)regs.a >> ri();
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xd000) {
|
||||
//1101 0.00 .... ....
|
||||
//ror a,ri
|
||||
uint24 length = ri();
|
||||
regs.a = (regs.a >> length) | (regs.a << (24 - length));
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xd800) {
|
||||
//1101 1.00 .... ....
|
||||
//shl a,ri
|
||||
regs.a = regs.a << ri();
|
||||
regs.n = regs.a & 0x800000;
|
||||
regs.z = regs.a == 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xff00) == 0xe000) {
|
||||
//1110 0000 .... ....
|
||||
//st r,a
|
||||
reg_write(opcode & 0xff, regs.a);
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xe800) {
|
||||
//1110 1.00 .... ....
|
||||
//wrraml
|
||||
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0);
|
||||
if(target < 0xc00) dataRAM[target] = regs.ramdata >> 0;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xe900) {
|
||||
//1110 1.01 .... ....
|
||||
//wrramh
|
||||
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0);
|
||||
if(target < 0xc00) dataRAM[target] = regs.ramdata >> 8;
|
||||
}
|
||||
|
||||
else if((opcode & 0xfb00) == 0xea00) {
|
||||
//1110 1.10 .... ....
|
||||
//wrramb
|
||||
uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0);
|
||||
if(target < 0xc00) dataRAM[target] = regs.ramdata >> 16;
|
||||
}
|
||||
|
||||
else if((opcode & 0xff00) == 0xf000) {
|
||||
//1111 0000 .... ....
|
||||
//swap a,r
|
||||
uint24 source = reg_read(opcode & 0xff);
|
||||
uint24 target = regs.a;
|
||||
regs.a = source;
|
||||
reg_write(opcode & 0xff, target);
|
||||
}
|
||||
|
||||
else if((opcode & 0xffff) == 0xfc00) {
|
||||
//1111 1100 0000 0000
|
||||
//halt
|
||||
state = State::Idle;
|
||||
}
|
||||
|
||||
else {
|
||||
print("Hitachi DSP: invalid opcode @ ", hex<4>(regs.pc - 1), " = ", hex<4>(opcode), "\n");
|
||||
state = State::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#ifdef HITACHIDSP_CPP
|
||||
|
||||
unsigned HitachiDSP::reg_read(unsigned n) const {
|
||||
switch(n) {
|
||||
case 0x00: return regs.a;
|
||||
case 0x01: return regs.acch;
|
||||
case 0x02: return regs.accl;
|
||||
case 0x03: return regs.busdata;
|
||||
case 0x08: return regs.romdata;
|
||||
case 0x0c: return regs.ramdata;
|
||||
case 0x13: return regs.busaddr;
|
||||
case 0x1c: return regs.ramaddr;
|
||||
case 0x50: return 0x000000;
|
||||
case 0x51: return 0xffffff;
|
||||
case 0x52: return 0x00ff00;
|
||||
case 0x53: return 0xff0000;
|
||||
case 0x54: return 0x00ffff;
|
||||
case 0x55: return 0xffff00;
|
||||
case 0x56: return 0x800000;
|
||||
case 0x57: return 0x7fffff;
|
||||
case 0x58: return 0x008000;
|
||||
case 0x59: return 0x007fff;
|
||||
case 0x5a: return 0xff7fff;
|
||||
case 0x5b: return 0xffff7f;
|
||||
case 0x5c: return 0x010000;
|
||||
case 0x5d: return 0xfeffff;
|
||||
case 0x5e: return 0x000100;
|
||||
case 0x5f: return 0x00feff;
|
||||
case 0x60: return regs.gpr[ 0];
|
||||
case 0x61: return regs.gpr[ 1];
|
||||
case 0x62: return regs.gpr[ 2];
|
||||
case 0x63: return regs.gpr[ 3];
|
||||
case 0x64: return regs.gpr[ 4];
|
||||
case 0x65: return regs.gpr[ 5];
|
||||
case 0x66: return regs.gpr[ 6];
|
||||
case 0x67: return regs.gpr[ 7];
|
||||
case 0x68: return regs.gpr[ 8];
|
||||
case 0x69: return regs.gpr[ 9];
|
||||
case 0x6a: return regs.gpr[10];
|
||||
case 0x6b: return regs.gpr[11];
|
||||
case 0x6c: return regs.gpr[12];
|
||||
case 0x6d: return regs.gpr[13];
|
||||
case 0x6e: return regs.gpr[14];
|
||||
case 0x6f: return regs.gpr[15];
|
||||
}
|
||||
return 0x000000;
|
||||
}
|
||||
|
||||
void HitachiDSP::reg_write(unsigned n, unsigned data) {
|
||||
switch(n) {
|
||||
case 0x00: regs.a = data; return;
|
||||
case 0x01: regs.acch = data; return;
|
||||
case 0x02: regs.accl = data; return;
|
||||
case 0x03: regs.busdata = data; return;
|
||||
case 0x08: regs.romdata = data; return;
|
||||
case 0x0c: regs.ramdata = data; return;
|
||||
case 0x13: regs.busaddr = data; return;
|
||||
case 0x1c: regs.ramaddr = data; return;
|
||||
case 0x60: regs.gpr[ 0] = data; return;
|
||||
case 0x61: regs.gpr[ 1] = data; return;
|
||||
case 0x62: regs.gpr[ 2] = data; return;
|
||||
case 0x63: regs.gpr[ 3] = data; return;
|
||||
case 0x64: regs.gpr[ 4] = data; return;
|
||||
case 0x65: regs.gpr[ 5] = data; return;
|
||||
case 0x66: regs.gpr[ 6] = data; return;
|
||||
case 0x67: regs.gpr[ 7] = data; return;
|
||||
case 0x68: regs.gpr[ 8] = data; return;
|
||||
case 0x69: regs.gpr[ 9] = data; return;
|
||||
case 0x6a: regs.gpr[10] = data; return;
|
||||
case 0x6b: regs.gpr[11] = data; return;
|
||||
case 0x6c: regs.gpr[12] = data; return;
|
||||
case 0x6d: regs.gpr[13] = data; return;
|
||||
case 0x6e: regs.gpr[14] = data; return;
|
||||
case 0x6f: regs.gpr[15] = data; return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
struct Registers {
|
||||
uint24 pc;
|
||||
uint16 p;
|
||||
bool n;
|
||||
bool z;
|
||||
bool c;
|
||||
|
||||
uint24 a;
|
||||
uint24 acch;
|
||||
uint24 accl;
|
||||
uint24 busdata;
|
||||
uint24 romdata;
|
||||
uint24 ramdata;
|
||||
uint24 busaddr;
|
||||
uint24 ramaddr;
|
||||
uint24 gpr[16];
|
||||
|
||||
//MMIO
|
||||
uint24 dma_source; //$1f40-$1f42
|
||||
uint24 dma_length; //$1f43-$1f44
|
||||
uint24 dma_target; //$1f45-$1f47
|
||||
uint8 r1f48; //$1f48
|
||||
uint24 program_offset; //$1f49-$1f4b
|
||||
uint8 r1f4c; //$1f4c
|
||||
uint16 page_number; //$1f4d-$1f4e
|
||||
uint8 program_counter; //$1f4f
|
||||
uint8 r1f50; //$1f50
|
||||
uint8 r1f51; //$1f51
|
||||
uint8 r1f52; //$1f52
|
||||
uint8 vector[32]; //$1f60-$1f7f
|
||||
} regs;
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#ifdef HITACHIDSP_CPP
|
||||
|
||||
void HitachiDSP::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
|
||||
s.array(dataRAM);
|
||||
foreach(n, stack) s.integer(n);
|
||||
s.integer(opcode);
|
||||
s.integer((unsigned&)state);
|
||||
|
||||
s.integer(regs.pc);
|
||||
s.integer(regs.p);
|
||||
s.integer(regs.n);
|
||||
s.integer(regs.z);
|
||||
s.integer(regs.c);
|
||||
|
||||
s.integer(regs.a);
|
||||
s.integer(regs.acch);
|
||||
s.integer(regs.accl);
|
||||
s.integer(regs.busdata);
|
||||
s.integer(regs.romdata);
|
||||
s.integer(regs.ramdata);
|
||||
s.integer(regs.busaddr);
|
||||
s.integer(regs.ramaddr);
|
||||
foreach(n, regs.gpr) s.integer(n);
|
||||
|
||||
s.integer(regs.dma_source);
|
||||
s.integer(regs.dma_length);
|
||||
s.integer(regs.dma_target);
|
||||
s.integer(regs.r1f48);
|
||||
s.integer(regs.program_offset);
|
||||
s.integer(regs.r1f4c);
|
||||
s.integer(regs.page_number);
|
||||
s.integer(regs.program_counter);
|
||||
s.integer(regs.r1f50);
|
||||
s.integer(regs.r1f51);
|
||||
s.integer(regs.r1f52);
|
||||
s.array(regs.vector);
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#include <snes/snes.hpp>
|
||||
|
||||
#define LINK_HPP
|
||||
namespace SNES {
|
||||
|
||||
Link link;
|
||||
|
||||
void Link::Enter() { link.enter(); }
|
||||
|
||||
void Link::enter() {
|
||||
while(true) {
|
||||
cpu.synchronize_coprocessors();
|
||||
unsigned clocks = 1;
|
||||
if(link_run) clocks = link_run();
|
||||
step(clocks);
|
||||
synchronize_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
void Link::init() {
|
||||
}
|
||||
|
||||
void Link::load() {
|
||||
if(opened()) close();
|
||||
string basename = system.interface->path(Cartridge::Slot::Base, "");
|
||||
string name = program != "" ? program : notdir(basename);
|
||||
string path = dir(basename);
|
||||
if(open(name, path)) {
|
||||
link_power = sym("link_power");
|
||||
link_reset = sym("link_reset");
|
||||
link_run = sym("link_run" );
|
||||
link_read = sym("link_read" );
|
||||
link_write = sym("link_write");
|
||||
}
|
||||
}
|
||||
|
||||
void Link::unload() {
|
||||
if(opened()) close();
|
||||
}
|
||||
|
||||
void Link::power() {
|
||||
if(link_power) link_power();
|
||||
create(Link::Enter, frequency);
|
||||
}
|
||||
|
||||
void Link::reset() {
|
||||
if(link_reset) link_reset();
|
||||
create(Link::Enter, frequency);
|
||||
}
|
||||
|
||||
uint8 Link::read(unsigned addr) {
|
||||
if(link_read) return link_read(addr);
|
||||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void Link::write(unsigned addr, uint8 data) {
|
||||
if(link_write) return link_write(addr, data);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
class Link : public Coprocessor, public library {
|
||||
public:
|
||||
string program;
|
||||
|
||||
static void Enter();
|
||||
void enter();
|
||||
void init();
|
||||
void load();
|
||||
void unload();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 read(unsigned addr);
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
private:
|
||||
function<void ()> link_power;
|
||||
function<void ()> link_reset;
|
||||
function<unsigned ()> link_run;
|
||||
function<uint8 (unsigned)> link_read;
|
||||
function<void (unsigned, uint8)> link_write;
|
||||
};
|
||||
|
||||
extern Link link;
|
||||
@@ -1,16 +1,16 @@
|
||||
#ifdef NECDSP_CPP
|
||||
|
||||
uint8 NECDSP::sr_read(unsigned) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
return regs.sr >> 8;
|
||||
}
|
||||
|
||||
void NECDSP::sr_write(unsigned, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
}
|
||||
|
||||
uint8 NECDSP::dr_read(unsigned) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
if(regs.sr.drc == 0) {
|
||||
//16-bit
|
||||
if(regs.sr.drs == 0) {
|
||||
@@ -29,7 +29,7 @@ uint8 NECDSP::dr_read(unsigned) {
|
||||
}
|
||||
|
||||
void NECDSP::dr_write(unsigned, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
if(regs.sr.drc == 0) {
|
||||
//16-bit
|
||||
if(regs.sr.drs == 0) {
|
||||
@@ -48,7 +48,7 @@ void NECDSP::dr_write(unsigned, uint8 data) {
|
||||
}
|
||||
|
||||
uint8 NECDSP::dp_read(unsigned addr) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
bool hi = addr & 1;
|
||||
addr = (addr >> 1) & 2047;
|
||||
|
||||
@@ -60,7 +60,7 @@ uint8 NECDSP::dp_read(unsigned addr) {
|
||||
}
|
||||
|
||||
void NECDSP::dp_write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
bool hi = addr & 1;
|
||||
addr = (addr >> 1) & 2047;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//NEC uPD7725
|
||||
//NEC uPD96050
|
||||
|
||||
class NECDSP : public Coprocessor {
|
||||
public:
|
||||
enum class Revision : unsigned { uPD7725, uPD96050 } revision;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifdef NECDSP_CPP
|
||||
|
||||
void NECDSP::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
|
||||
s.array(dataRAM);
|
||||
|
||||
s.array(regs.stack);
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
#define OBC1_CPP
|
||||
namespace SNES {
|
||||
|
||||
OBC1 obc1;
|
||||
|
||||
#include "serialization.cpp"
|
||||
OBC1 obc1;
|
||||
|
||||
void OBC1::init() {
|
||||
}
|
||||
@@ -30,45 +29,46 @@ void OBC1::reset() {
|
||||
|
||||
uint8 OBC1::read(unsigned addr) {
|
||||
addr &= 0x1fff;
|
||||
if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr);
|
||||
|
||||
switch(addr) { default: //never used, avoids compiler warning
|
||||
case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0);
|
||||
case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1);
|
||||
case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2);
|
||||
case 0x1ff3: return ram_read(status.baseptr + (status.address << 2) + 3);
|
||||
case 0x1ff4: return ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
case 0x1ff5: case 0x1ff6: case 0x1ff7: return ram_read(addr);
|
||||
switch(addr) {
|
||||
case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0);
|
||||
case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1);
|
||||
case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2);
|
||||
case 0x1ff3: return ram_read(status.baseptr + (status.address << 2) + 3);
|
||||
case 0x1ff4: return ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
}
|
||||
|
||||
return ram_read(addr);
|
||||
}
|
||||
|
||||
void OBC1::write(unsigned addr, uint8 data) {
|
||||
addr &= 0x1fff;
|
||||
if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data);
|
||||
|
||||
switch(addr) {
|
||||
case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); break;
|
||||
case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); break;
|
||||
case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); break;
|
||||
case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); break;
|
||||
case 0x1ff4: {
|
||||
uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
|
||||
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
|
||||
} break;
|
||||
case 0x1ff5: {
|
||||
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
|
||||
ram_write(addr, data);
|
||||
} break;
|
||||
case 0x1ff6: {
|
||||
status.address = (data & 0x7f);
|
||||
status.shift = (data & 3) << 1;
|
||||
ram_write(addr, data);
|
||||
} break;
|
||||
case 0x1ff7: {
|
||||
ram_write(addr, data);
|
||||
} break;
|
||||
case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); return;
|
||||
case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); return;
|
||||
case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); return;
|
||||
case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); return;
|
||||
case 0x1ff4: {
|
||||
uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200);
|
||||
temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift);
|
||||
ram_write(status.baseptr + (status.address >> 2) + 0x200, temp);
|
||||
} return;
|
||||
case 0x1ff5:
|
||||
status.baseptr = (data & 1) ? 0x1800 : 0x1c00;
|
||||
ram_write(addr, data);
|
||||
return;
|
||||
case 0x1ff6:
|
||||
status.address = (data & 0x7f);
|
||||
status.shift = (data & 3) << 1;
|
||||
ram_write(addr, data);
|
||||
return;
|
||||
case 0x1ff7:
|
||||
ram_write(addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
return ram_write(addr, data);
|
||||
}
|
||||
|
||||
uint8 OBC1::ram_read(unsigned addr) {
|
||||
@@ -79,7 +79,4 @@ void OBC1::ram_write(unsigned addr, uint8 data) {
|
||||
cartridge.ram.write(addr & 0x1fff, data);
|
||||
}
|
||||
|
||||
OBC1::OBC1() {}
|
||||
OBC1::~OBC1() {}
|
||||
|
||||
}
|
||||
@@ -10,8 +10,6 @@ public:
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
void serialize(serializer&);
|
||||
OBC1();
|
||||
~OBC1();
|
||||
|
||||
private:
|
||||
uint8 ram_read(unsigned addr);
|
||||
|
||||
@@ -7,12 +7,12 @@ unsigned SA1::CPUIRAM::size() const {
|
||||
}
|
||||
|
||||
uint8 SA1::CPUIRAM::read(unsigned addr) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
return sa1.iram.read(addr);
|
||||
}
|
||||
|
||||
void SA1::CPUIRAM::write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
sa1.iram.write(addr, data);
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ unsigned SA1::CPUBWRAM::size() const {
|
||||
}
|
||||
|
||||
uint8 SA1::CPUBWRAM::read(unsigned addr) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
if(dma) return sa1.dma_cc1_read(addr);
|
||||
return cartridge.ram.read(addr);
|
||||
}
|
||||
|
||||
void SA1::CPUBWRAM::write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
cartridge.ram.write(addr, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,13 +178,13 @@ void SA1::mmc_write(unsigned addr, uint8 data) {
|
||||
}
|
||||
|
||||
uint8 SA1::mmc_cpu_read(unsigned addr) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||
return cpubwram.read(addr);
|
||||
}
|
||||
|
||||
void SA1::mmc_cpu_write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||
cpubwram.write(addr, data);
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ uint8 SA1::mmio_r230e() {
|
||||
}
|
||||
|
||||
uint8 SA1::mmio_read(unsigned addr) {
|
||||
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
|
||||
(co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu());
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
@@ -469,7 +469,7 @@ uint8 SA1::mmio_read(unsigned addr) {
|
||||
}
|
||||
|
||||
void SA1::mmio_write(unsigned addr, uint8 data) {
|
||||
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
|
||||
(co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu());
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
#include <snes/snes.hpp>
|
||||
|
||||
#define SERIAL_CPP
|
||||
namespace SNES {
|
||||
|
||||
Serial serial;
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
static void snesserial_tick(unsigned clocks) { serial.add_clocks(clocks * 8); }
|
||||
static uint8 snesserial_read() { return serial.read(); }
|
||||
static void snesserial_write(uint8 data) { serial.write(data); }
|
||||
|
||||
void Serial::Enter() { serial.enter(); }
|
||||
|
||||
void Serial::enter() {
|
||||
data1 = 0;
|
||||
data2 = 0;
|
||||
add_clocks(256 * 8); //warm-up
|
||||
if(flowcontrol()) data2 = 1;
|
||||
if(main) main(snesserial_tick, snesserial_read, snesserial_write);
|
||||
while(true) add_clocks(frequency); //snesserial_main() fallback
|
||||
}
|
||||
|
||||
void Serial::add_clocks(unsigned clocks) {
|
||||
step(clocks);
|
||||
synchronize_cpu();
|
||||
}
|
||||
|
||||
uint8 Serial::read() {
|
||||
while(cpu.joylatch() == 0) add_clocks(1);
|
||||
while(cpu.joylatch() == 1) add_clocks(1);
|
||||
add_clocks(4);
|
||||
|
||||
uint8 data = 0;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
add_clocks(8);
|
||||
data = (cpu.joylatch() << 7) | (data >> 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Serial::write(uint8 data) {
|
||||
if(flowcontrol()) while(cpu.pio() & 0x80) add_clocks(1);
|
||||
add_clocks(8);
|
||||
|
||||
data1 = 1;
|
||||
add_clocks(8);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
data1 = (data & 1) ^ 1;
|
||||
data >>= 1;
|
||||
add_clocks(8);
|
||||
}
|
||||
|
||||
data1 = 0;
|
||||
add_clocks(8);
|
||||
}
|
||||
|
||||
uint8 Serial::mmio_read(unsigned addr) {
|
||||
cpu.synchronize_coprocessor();
|
||||
switch(addr & 1) { default:
|
||||
case 0: return cpu.mmio_read(addr);
|
||||
case 1: return cpu.mmio_read(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void Serial::mmio_write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
switch(addr & 1) { default:
|
||||
case 0: cpu.mmio_write(addr, data); break;
|
||||
case 1: cpu.mmio_write(addr, data); break;
|
||||
}
|
||||
}
|
||||
|
||||
void Serial::init() {
|
||||
}
|
||||
|
||||
void Serial::load() {
|
||||
if(opened()) close();
|
||||
string basename = system.interface->path(Cartridge::Slot::Base, "");
|
||||
string name = notdir(basename);
|
||||
string path = dir(basename);
|
||||
if(open(name, path)) {
|
||||
baudrate = sym("snesserial_baudrate");
|
||||
flowcontrol = sym("snesserial_flowcontrol");
|
||||
main = sym("snesserial_main");
|
||||
}
|
||||
|
||||
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, { &Serial::mmio_read, &serial }, { &Serial::mmio_write, &serial });
|
||||
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, { &Serial::mmio_read, &serial }, { &Serial::mmio_write, &serial });
|
||||
}
|
||||
|
||||
void Serial::unload() {
|
||||
if(opened()) close();
|
||||
}
|
||||
|
||||
void Serial::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Serial::reset() {
|
||||
create(Serial::Enter, baudrate() * 8);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
class Serial : public Coprocessor, public library, public property<Serial> {
|
||||
public:
|
||||
static void Enter();
|
||||
void enter();
|
||||
void init();
|
||||
void load();
|
||||
void unload();
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
|
||||
readonly<bool> data1;
|
||||
readonly<bool> data2;
|
||||
|
||||
void add_clocks(unsigned clocks);
|
||||
uint8 read();
|
||||
void write(uint8 data);
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
private:
|
||||
function<unsigned ()> baudrate;
|
||||
function<bool ()> flowcontrol;
|
||||
function<void (void (*)(unsigned), uint8_t (*)(), void (*)(uint8_t))> main;
|
||||
};
|
||||
|
||||
extern Serial serial;
|
||||
@@ -1,9 +0,0 @@
|
||||
#ifdef SERIAL_CPP
|
||||
|
||||
void Serial::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
s.integer((bool&)data1);
|
||||
s.integer((bool&)data2);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifdef SUPERFX_CPP
|
||||
|
||||
uint8 SuperFX::mmio_read(unsigned addr) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||
@@ -53,7 +53,7 @@ uint8 SuperFX::mmio_read(unsigned addr) {
|
||||
}
|
||||
|
||||
void SuperFX::mmio_write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessor();
|
||||
cpu.synchronize_coprocessors();
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#include <snes/snes.hpp>
|
||||
|
||||
#define CONTROLLER_CPP
|
||||
namespace SNES {
|
||||
|
||||
#include "gamepad/gamepad.cpp"
|
||||
#include "multitap/multitap.cpp"
|
||||
#include "mouse/mouse.cpp"
|
||||
#include "superscope/superscope.cpp"
|
||||
#include "justifier/justifier.cpp"
|
||||
#include "serial/serial.cpp"
|
||||
|
||||
void Controller::Enter() {
|
||||
if(co_active() == input.port1->thread) input.port1->enter();
|
||||
if(co_active() == input.port2->thread) input.port2->enter();
|
||||
}
|
||||
|
||||
void Controller::enter() {
|
||||
while(true) step(1);
|
||||
}
|
||||
|
||||
void Controller::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
synchronize_cpu();
|
||||
}
|
||||
|
||||
void Controller::synchronize_cpu() {
|
||||
if(CPU::Threaded == true) {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
} else {
|
||||
while(clock >= 0) cpu.enter();
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::iobit() {
|
||||
switch(port) {
|
||||
case Controller::Port1: return cpu.pio() & 0x40;
|
||||
case Controller::Port2: return cpu.pio() & 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::iobit(bool data) {
|
||||
switch(port) {
|
||||
case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break;
|
||||
case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break;
|
||||
}
|
||||
}
|
||||
|
||||
Controller::Controller(bool port) : port(port) {
|
||||
if(!thread) create(Controller::Enter, 1);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
// SNES controller port pinout:
|
||||
// -------------------------------
|
||||
// | (1) (2) (3) (4) | (5) (6) (7) )
|
||||
// -------------------------------
|
||||
// pin name port1 port2
|
||||
// 1: +5v
|
||||
// 2: clock $4016 read $4017 read
|
||||
// 3: latch $4016.d0 write $4016.d0 write
|
||||
// 4: data1 $4016.d0 read $4017.d0 read
|
||||
// 5: data2 $4016.d1 read $4017.d1 read
|
||||
// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read
|
||||
// 7: gnd
|
||||
|
||||
struct Controller : Processor {
|
||||
enum : bool { Port1 = 0, Port2 = 1 };
|
||||
const bool port;
|
||||
|
||||
static void Enter();
|
||||
virtual void enter();
|
||||
void step(unsigned clocks);
|
||||
void synchronize_cpu();
|
||||
|
||||
bool iobit();
|
||||
void iobit(bool data);
|
||||
virtual uint2 data() { return 0; }
|
||||
virtual void latch(bool data) {}
|
||||
Controller(bool port);
|
||||
};
|
||||
|
||||
#include "gamepad/gamepad.hpp"
|
||||
#include "multitap/multitap.hpp"
|
||||
#include "mouse/mouse.hpp"
|
||||
#include "superscope/superscope.hpp"
|
||||
#include "justifier/justifier.hpp"
|
||||
#include "serial/serial.hpp"
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#ifdef CONTROLLER_CPP
|
||||
|
||||
uint2 Gamepad::data() {
|
||||
if(counter >= 16) return 1;
|
||||
uint2 result = system.interface->input_poll(port, Input::Device::Joypad, 0, counter);
|
||||
if(latched == 0) counter++;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Gamepad::latch(bool data) {
|
||||
if(latched == data) return;
|
||||
latched = data;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
Gamepad::Gamepad(bool port) : Controller(port) {
|
||||
latched = 0;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
struct Gamepad : Controller {
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Gamepad(bool port);
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
unsigned counter;
|
||||
};
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#ifdef CONTROLLER_CPP
|
||||
|
||||
void Justifier::enter() {
|
||||
unsigned prev = 0;
|
||||
while(true) {
|
||||
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
|
||||
|
||||
signed x = (active == 0 ? x1 : x2), y = (active == 0 ? y1 : y2);
|
||||
bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
|
||||
|
||||
if(offscreen == false) {
|
||||
unsigned target = y * 1364 + (x + 24) * 4;
|
||||
if(next >= target && prev < target) {
|
||||
//CRT raster detected, toggle iobit to latch counters
|
||||
iobit(0);
|
||||
iobit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(next < prev) {
|
||||
int nx1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X);
|
||||
int ny1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y);
|
||||
nx1 += x1;
|
||||
ny1 += y1;
|
||||
x1 = max(-16, min(256 + 16, nx1));
|
||||
y1 = max(-16, min(240 + 16, ny1));
|
||||
|
||||
if(chained == true) {
|
||||
int nx2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X);
|
||||
int ny2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y);
|
||||
nx2 += x2;
|
||||
ny2 += y2;
|
||||
x2 = max(-16, min(256 + 16, nx2));
|
||||
y2 = max(-16, min(240 + 16, ny2));
|
||||
}
|
||||
} else {
|
||||
//sleep until PPU counters are close to latch position
|
||||
unsigned diff = abs((signed)y - cpu.vcounter());
|
||||
if(diff >= 2) step((diff - 2) * 1364);
|
||||
}
|
||||
|
||||
prev = next;
|
||||
step(2);
|
||||
}
|
||||
}
|
||||
|
||||
uint2 Justifier::data() {
|
||||
if(counter >= 32) return 1;
|
||||
|
||||
if(counter == 0) {
|
||||
trigger1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger);
|
||||
start1 = system.interface->input_poll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start);
|
||||
if(chained) {
|
||||
trigger2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger);
|
||||
start2 = system.interface->input_poll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start);
|
||||
}
|
||||
}
|
||||
|
||||
switch(counter++) {
|
||||
case 0: return 0;
|
||||
case 1: return 0;
|
||||
case 2: return 0;
|
||||
case 3: return 0;
|
||||
case 4: return 0;
|
||||
case 5: return 0;
|
||||
case 6: return 0;
|
||||
case 7: return 0;
|
||||
case 8: return 0;
|
||||
case 9: return 0;
|
||||
case 10: return 0;
|
||||
case 11: return 0;
|
||||
|
||||
case 12: return 1; //signature
|
||||
case 13: return 1; // ||
|
||||
case 14: return 1; // ||
|
||||
case 15: return 0; // ||
|
||||
|
||||
case 16: return 0;
|
||||
case 17: return 1;
|
||||
case 18: return 0;
|
||||
case 19: return 1;
|
||||
case 20: return 0;
|
||||
case 21: return 1;
|
||||
case 22: return 0;
|
||||
case 23: return 1;
|
||||
|
||||
case 24: return trigger1;
|
||||
case 25: return trigger2;
|
||||
case 26: return start1;
|
||||
case 27: return start2;
|
||||
case 28: return active;
|
||||
|
||||
case 29: return 0;
|
||||
case 30: return 0;
|
||||
case 31: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Justifier::latch(bool data) {
|
||||
if(latched == data) return;
|
||||
latched = data;
|
||||
counter = 0;
|
||||
if(latched == 0) active = !active; //toggle between both controllers, even when unchained
|
||||
}
|
||||
|
||||
Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) {
|
||||
create(Controller::Enter, 21477272);
|
||||
latched = 0;
|
||||
counter = 0;
|
||||
active = 0;
|
||||
|
||||
if(chained == false) {
|
||||
x1 = 256 / 2;
|
||||
y1 = 240 / 2;
|
||||
x2 = -1;
|
||||
y2 = -1;
|
||||
} else {
|
||||
x1 = 256 / 2 - 16;
|
||||
y1 = 240 / 2;
|
||||
x2 = 256 / 2 + 16;
|
||||
y2 = 240 / 2;
|
||||
}
|
||||
|
||||
trigger1 = false;
|
||||
trigger2 = false;
|
||||
|
||||
start1 = false;
|
||||
start2 = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
struct Justifier : Controller {
|
||||
void enter();
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Justifier(bool port, bool chained);
|
||||
|
||||
//private:
|
||||
const bool chained; //true if the second justifier is attached to the first
|
||||
bool latched;
|
||||
unsigned counter;
|
||||
|
||||
bool active;
|
||||
signed x1, x2;
|
||||
signed y1, y2;
|
||||
bool trigger1, trigger2;
|
||||
bool start1, start2;
|
||||
};
|
||||
Executable
+69
@@ -0,0 +1,69 @@
|
||||
#ifdef CONTROLLER_CPP
|
||||
|
||||
uint2 Mouse::data() {
|
||||
if(counter >= 32) return 1;
|
||||
|
||||
int position_x = system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right
|
||||
int position_y = system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down
|
||||
|
||||
bool direction_x = position_x < 0; //0 = right, 1 = left
|
||||
bool direction_y = position_y < 0; //0 = down, 1 = up
|
||||
|
||||
if(position_x < 0) position_x = -position_x; //abs(position_x)
|
||||
if(position_y < 0) position_y = -position_y; //abs(position_y)
|
||||
|
||||
position_x = min(127, position_x); //range = 0 - 127
|
||||
position_y = min(127, position_y);
|
||||
|
||||
switch(counter++) { default:
|
||||
case 0: return 0;
|
||||
case 1: return 0;
|
||||
case 2: return 0;
|
||||
case 3: return 0;
|
||||
case 4: return 0;
|
||||
case 5: return 0;
|
||||
case 6: return 0;
|
||||
case 7: return 0;
|
||||
|
||||
case 8: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right);
|
||||
case 9: return system.interface->input_poll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left);
|
||||
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
|
||||
case 11: return 0; // ||
|
||||
|
||||
case 12: return 0; //signature
|
||||
case 13: return 0; // ||
|
||||
case 14: return 0; // ||
|
||||
case 15: return 1; // ||
|
||||
|
||||
case 16: return (direction_y);
|
||||
case 17: return (position_y >> 6) & 1;
|
||||
case 18: return (position_y >> 5) & 1;
|
||||
case 19: return (position_y >> 4) & 1;
|
||||
case 20: return (position_y >> 3) & 1;
|
||||
case 21: return (position_y >> 2) & 1;
|
||||
case 22: return (position_y >> 1) & 1;
|
||||
case 23: return (position_y >> 0) & 1;
|
||||
|
||||
case 24: return (direction_x);
|
||||
case 25: return (position_x >> 6) & 1;
|
||||
case 26: return (position_x >> 5) & 1;
|
||||
case 27: return (position_x >> 4) & 1;
|
||||
case 28: return (position_x >> 3) & 1;
|
||||
case 29: return (position_x >> 2) & 1;
|
||||
case 30: return (position_x >> 1) & 1;
|
||||
case 31: return (position_x >> 0) & 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::latch(bool data) {
|
||||
if(latched == data) return;
|
||||
latched = data;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
Mouse::Mouse(bool port) : Controller(port) {
|
||||
latched = 0;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
struct Mouse : Controller {
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Mouse(bool port);
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
unsigned counter;
|
||||
};
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#ifdef CONTROLLER_CPP
|
||||
|
||||
uint2 Multitap::data() {
|
||||
if(latched) return 2; //multitap detection
|
||||
unsigned index, port1, port2;
|
||||
|
||||
if(iobit()) {
|
||||
index = counter1;
|
||||
if(index >= 16) return 3;
|
||||
counter1++;
|
||||
port1 = 0; //controller 1
|
||||
port2 = 1; //controller 2
|
||||
} else {
|
||||
index = counter2;
|
||||
if(index >= 16) return 3;
|
||||
counter2++;
|
||||
port1 = 2; //controller 3
|
||||
port2 = 3; //controller 4
|
||||
}
|
||||
|
||||
bool data1 = system.interface->input_poll(port, Input::Device::Multitap, port1, index);
|
||||
bool data2 = system.interface->input_poll(port, Input::Device::Multitap, port2, index);
|
||||
return (data2 << 1) | (data1 << 0);
|
||||
}
|
||||
|
||||
void Multitap::latch(bool data) {
|
||||
if(latched == data) return;
|
||||
latched = data;
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
|
||||
Multitap::Multitap(bool port) : Controller(port) {
|
||||
latched = 0;
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
struct Multitap : Controller {
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Multitap(bool port);
|
||||
|
||||
private:
|
||||
bool latched;
|
||||
unsigned counter1;
|
||||
unsigned counter2;
|
||||
};
|
||||
Executable
+146
@@ -0,0 +1,146 @@
|
||||
#ifdef CONTROLLER_CPP
|
||||
|
||||
//Serial communications cable emulation:
|
||||
//The SNES controller ports can be used for bi-directional serial communication
|
||||
//when wired to a specialized controller. This class implements said controller,
|
||||
//for the primary purpose of testing code outside of real hardware.
|
||||
|
||||
//The basic idea is to wire the SNES controller pins to a UART, such as
|
||||
//the MAX232N; or a serial->USB cable, such as the FTDI TTL-232R-5V.
|
||||
|
||||
//Connection Diagram:
|
||||
//[SNES] [UART] [Purpose]
|
||||
// Latch RXD Data transfer
|
||||
// Data1 TXD Data transfer
|
||||
// Data2 RTS Flow control (optional)
|
||||
// IOBit CTS Flow control (optional)
|
||||
// GND GND Circuit completion
|
||||
|
||||
//The SNES software program will have to use specially timed code to send and
|
||||
//receive data at a specific baud-rate; whereas the PC handles timing via the
|
||||
//UART.
|
||||
|
||||
//The emulator implementation is designed so that the same PC-side program can
|
||||
//be used both under emulation and on real hardware. It does this by linking to
|
||||
//a dynamic library for timing, read and write operations. This library is
|
||||
//responsible for setting both the baud-rate and flow control setting. The
|
||||
//SNES-side software program must know about and respect the library setting.
|
||||
|
||||
static void snesserial_tick(unsigned clocks);
|
||||
static uint8 snesserial_read();
|
||||
static void snesserial_write(uint8 data);
|
||||
|
||||
void Serial::enter() {
|
||||
if(enable == false) while(true) step(1); //fallback, in case library was not found
|
||||
step(256 * 8); //simulate warm-up delay
|
||||
if(flowcontrol()) data2 = 1;
|
||||
main(snesserial_tick, snesserial_read, snesserial_write); //stubs for Serial::step, Serial::read, Serial::write
|
||||
while(true) step(1); //fallback, in case snesserial_main() returns (it should never do so)
|
||||
}
|
||||
|
||||
uint8 Serial::read() {
|
||||
while(latched == 0) step(1);
|
||||
while(latched == 1) step(1);
|
||||
step(4);
|
||||
|
||||
uint8 data = 0;
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
step(8);
|
||||
data = (latched << 7) | (data >> 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Serial::write(uint8 data) {
|
||||
if(flowcontrol()) while(iobit()) step(1);
|
||||
step(8);
|
||||
|
||||
data1 = 1;
|
||||
step(8);
|
||||
|
||||
for(unsigned i = 0; i < 8; i++) {
|
||||
data1 = (data & 1) ^ 1;
|
||||
data >>= 1;
|
||||
step(8);
|
||||
}
|
||||
|
||||
data1 = 0;
|
||||
step(8);
|
||||
}
|
||||
|
||||
uint2 Serial::data() {
|
||||
return (data2 << 1) | (data1 << 0);
|
||||
}
|
||||
|
||||
void Serial::latch(bool data) {
|
||||
latched = data;
|
||||
}
|
||||
|
||||
Serial::Serial(bool port) : Controller(port) {
|
||||
enable = false;
|
||||
string basename = system.interface->path(Cartridge::Slot::Base, "");
|
||||
string name = notdir(basename);
|
||||
string path = dir(basename);
|
||||
if(open(name, path)) {
|
||||
baudrate = sym("snesserial_baudrate");
|
||||
flowcontrol = sym("snesserial_flowcontrol");
|
||||
main = sym("snesserial_main");
|
||||
if(baudrate && flowcontrol && main) enable = true;
|
||||
}
|
||||
create(Controller::Enter, enable ? baudrate() * 8 : 1);
|
||||
|
||||
latched = false;
|
||||
data1 = 0;
|
||||
data2 = 0;
|
||||
}
|
||||
|
||||
Serial::~Serial() {
|
||||
if(opened()) close();
|
||||
}
|
||||
|
||||
//stubs needed to call into class objects from global function pointers
|
||||
|
||||
static void snesserial_tick(unsigned clocks) {
|
||||
if(co_active() == input.port1->thread) {
|
||||
if(dynamic_cast<Serial*>(input.port1)) {
|
||||
return ((Serial*)input.port1)->step(clocks);
|
||||
}
|
||||
}
|
||||
|
||||
if(co_active() == input.port2->thread) {
|
||||
if(dynamic_cast<Serial*>(input.port2)) {
|
||||
return ((Serial*)input.port2)->step(clocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8 snesserial_read() {
|
||||
if(co_active() == input.port1->thread) {
|
||||
if(dynamic_cast<Serial*>(input.port1)) {
|
||||
return ((Serial*)input.port1)->read();
|
||||
}
|
||||
}
|
||||
|
||||
if(co_active() == input.port2->thread) {
|
||||
if(dynamic_cast<Serial*>(input.port2)) {
|
||||
return ((Serial*)input.port2)->read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void snesserial_write(uint8 data) {
|
||||
if(co_active() == input.port1->thread) {
|
||||
if(dynamic_cast<Serial*>(input.port1)) {
|
||||
return ((Serial*)input.port1)->write(data);
|
||||
}
|
||||
}
|
||||
|
||||
if(co_active() == input.port2->thread) {
|
||||
if(dynamic_cast<Serial*>(input.port2)) {
|
||||
return ((Serial*)input.port2)->write(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
struct Serial : Controller, public library {
|
||||
void enter();
|
||||
uint8 read();
|
||||
void write(uint8 data);
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
Serial(bool port);
|
||||
~Serial();
|
||||
|
||||
private:
|
||||
bool enable;
|
||||
function<unsigned ()> baudrate;
|
||||
function<bool ()> flowcontrol;
|
||||
function<void (void (*)(unsigned), uint8_t (*)(), void (*)(uint8_t))> main;
|
||||
|
||||
bool latched;
|
||||
bool data1;
|
||||
bool data2;
|
||||
};
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
#ifdef CONTROLLER_CPP
|
||||
|
||||
//The Super Scope is a light-gun: it detects the CRT beam cannon position,
|
||||
//and latches the counters by toggling iobit. This only works on controller
|
||||
//port 2, as iobit there is connected to the PPU H/V counter latch.
|
||||
//(PIO $4201.d7)
|
||||
|
||||
//It is obviously not possible to perfectly simulate an IR light detecting
|
||||
//a CRT beam cannon, hence this class will read the PPU raster counters.
|
||||
|
||||
//A Super Scope can still technically be used in port 1, however it would
|
||||
//require manual polling of PIO ($4201.d6) to determine when iobit was written.
|
||||
//Note that no commercial game ever utilizes a Super Scope in port 1.
|
||||
|
||||
void SuperScope::enter() {
|
||||
unsigned prev = 0;
|
||||
while(true) {
|
||||
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
|
||||
|
||||
if(offscreen == false) {
|
||||
unsigned target = y * 1364 + (x + 24) * 4;
|
||||
if(next >= target && prev < target) {
|
||||
//CRT raster detected, toggle iobit to latch counters
|
||||
iobit(0);
|
||||
iobit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if(next < prev) {
|
||||
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
|
||||
int nx = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X);
|
||||
int ny = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y);
|
||||
nx += x;
|
||||
ny += y;
|
||||
x = max(-16, min(256 + 16, nx));
|
||||
y = max(-16, min(240 + 16, ny));
|
||||
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
|
||||
} else {
|
||||
//sleep until PPU counters are close to latch position
|
||||
unsigned diff = abs((signed)y - cpu.vcounter());
|
||||
if(diff >= 2) step((diff - 2) * 1364);
|
||||
}
|
||||
|
||||
prev = next;
|
||||
step(2);
|
||||
}
|
||||
}
|
||||
|
||||
uint2 SuperScope::data() {
|
||||
if(counter >= 8) return 1;
|
||||
|
||||
if(counter == 0) {
|
||||
//turbo is a switch; toggle is edge sensitive
|
||||
bool newturbo = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo);
|
||||
if(newturbo && !turbo) {
|
||||
turbo = !turbo; //toggle state
|
||||
turbolock = true;
|
||||
} else {
|
||||
turbolock = false;
|
||||
}
|
||||
|
||||
//trigger is a button
|
||||
//if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive
|
||||
trigger = false;
|
||||
bool newtrigger = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger);
|
||||
if(newtrigger && (turbo || !triggerlock)) {
|
||||
trigger = true;
|
||||
triggerlock = true;
|
||||
} else if(!newtrigger) {
|
||||
triggerlock = false;
|
||||
}
|
||||
|
||||
//cursor is a button; it is always level sensitive
|
||||
cursor = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor);
|
||||
|
||||
//pause is a button; it is always edge sensitive
|
||||
pause = false;
|
||||
bool newpause = system.interface->input_poll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause);
|
||||
if(newpause && !pauselock) {
|
||||
pause = true;
|
||||
pauselock = true;
|
||||
} else if(!newpause) {
|
||||
pauselock = false;
|
||||
}
|
||||
|
||||
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
|
||||
}
|
||||
|
||||
switch(counter++) {
|
||||
case 0: return offscreen ? 0 : trigger;
|
||||
case 1: return cursor;
|
||||
case 2: return turbo;
|
||||
case 3: return pause;
|
||||
case 4: return 0;
|
||||
case 5: return 0;
|
||||
case 6: return offscreen;
|
||||
case 7: return 0; //noise (1 = yes)
|
||||
}
|
||||
}
|
||||
|
||||
void SuperScope::latch(bool data) {
|
||||
if(latched == data) return;
|
||||
latched = data;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
SuperScope::SuperScope(bool port) : Controller(port) {
|
||||
create(Controller::Enter, 21477272);
|
||||
latched = 0;
|
||||
counter = 0;
|
||||
|
||||
//center cursor onscreen
|
||||
x = 256 / 2;
|
||||
y = 240 / 2;
|
||||
|
||||
trigger = false;
|
||||
cursor = false;
|
||||
turbo = false;
|
||||
pause = false;
|
||||
offscreen = false;
|
||||
|
||||
turbolock = false;
|
||||
triggerlock = false;
|
||||
pauselock = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
struct SuperScope : Controller {
|
||||
void enter();
|
||||
uint2 data();
|
||||
void latch(bool data);
|
||||
SuperScope(bool port);
|
||||
|
||||
//private:
|
||||
bool latched;
|
||||
unsigned counter;
|
||||
|
||||
signed x, y;
|
||||
|
||||
bool trigger;
|
||||
bool cursor;
|
||||
bool turbo;
|
||||
bool pause;
|
||||
bool offscreen;
|
||||
|
||||
bool turbolock;
|
||||
bool triggerlock;
|
||||
bool pauselock;
|
||||
};
|
||||
@@ -23,6 +23,9 @@ void CPU::step(unsigned clocks) {
|
||||
Processor &chip = *coprocessors[i];
|
||||
chip.clock -= clocks * (uint64)chip.frequency;
|
||||
}
|
||||
input.port1->clock -= clocks * (uint64)input.port1->frequency;
|
||||
input.port2->clock -= clocks * (uint64)input.port2->frequency;
|
||||
synchronize_controllers();
|
||||
}
|
||||
|
||||
void CPU::synchronize_smp() {
|
||||
@@ -41,13 +44,18 @@ void CPU::synchronize_ppu() {
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::synchronize_coprocessor() {
|
||||
void CPU::synchronize_coprocessors() {
|
||||
for(unsigned i = 0; i < coprocessors.size(); i++) {
|
||||
Processor &chip = *coprocessors[i];
|
||||
if(chip.clock < 0) co_switch(chip.thread);
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::synchronize_controllers() {
|
||||
if(input.port1->clock < 0) co_switch(input.port1->thread);
|
||||
if(input.port2->clock < 0) co_switch(input.port2->thread);
|
||||
}
|
||||
|
||||
void CPU::Enter() { cpu.enter(); }
|
||||
|
||||
void CPU::enter() {
|
||||
|
||||
@@ -7,7 +7,8 @@ public:
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_smp();
|
||||
void synchronize_ppu();
|
||||
void synchronize_coprocessor();
|
||||
void synchronize_coprocessors();
|
||||
void synchronize_controllers();
|
||||
|
||||
uint8 port_read(uint2 port) const;
|
||||
void port_write(uint2 port, uint8 data);
|
||||
|
||||
@@ -33,10 +33,8 @@ void CPU::mmio_w2183(uint8 data) {
|
||||
//strobing $4016.d0 affects both controller port latches.
|
||||
//$4017 bit 0 writes are ignored.
|
||||
void CPU::mmio_w4016(uint8 data) {
|
||||
bool old_latch = status.joypad_strobe_latch;
|
||||
bool new_latch = data & 1;
|
||||
status.joypad_strobe_latch = new_latch;
|
||||
if(old_latch != new_latch) input.poll();
|
||||
input.port1->latch(data & 1);
|
||||
input.port2->latch(data & 1);
|
||||
}
|
||||
|
||||
//JOYSER0
|
||||
@@ -44,7 +42,7 @@ void CPU::mmio_w4016(uint8 data) {
|
||||
//1-0 = Joypad serial data
|
||||
uint8 CPU::mmio_r4016() {
|
||||
uint8 r = regs.mdr & 0xfc;
|
||||
r |= input.port_read(0) & 3;
|
||||
r |= input.port1->data();
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -54,7 +52,7 @@ uint8 CPU::mmio_r4016() {
|
||||
//1-0 = Joypad serial data
|
||||
uint8 CPU::mmio_r4017() {
|
||||
uint8 r = (regs.mdr & 0xe0) | 0x1c;
|
||||
r |= input.port_read(1) & 3;
|
||||
r |= input.port2->data();
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,15 @@ void CPU::step_auto_joypad_poll() {
|
||||
status.auto_joypad_active = status.auto_joypad_counter <= 15;
|
||||
|
||||
if(status.auto_joypad_active && status.auto_joypad_poll) {
|
||||
if(status.auto_joypad_counter == 0) input.poll();
|
||||
if(status.auto_joypad_counter == 0) {
|
||||
input.port1->latch(1);
|
||||
input.port2->latch(1);
|
||||
input.port1->latch(0);
|
||||
input.port2->latch(0);
|
||||
}
|
||||
|
||||
uint8 port0 = input.port_read(0);
|
||||
uint8 port1 = input.port_read(1);
|
||||
uint2 port0 = input.port1->data();
|
||||
uint2 port1 = input.port2->data();
|
||||
|
||||
status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1);
|
||||
status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1);
|
||||
|
||||
@@ -12,10 +12,7 @@ void CPU::add_clocks(unsigned clocks) {
|
||||
unsigned ticks = clocks >> 1;
|
||||
while(ticks--) {
|
||||
tick();
|
||||
if(hcounter() & 2) {
|
||||
input.tick();
|
||||
poll_interrupts();
|
||||
}
|
||||
if(hcounter() & 2) poll_interrupts();
|
||||
}
|
||||
|
||||
step(clocks);
|
||||
@@ -40,7 +37,7 @@ void CPU::scanline() {
|
||||
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
||||
synchronize_ppu();
|
||||
synchronize_smp();
|
||||
synchronize_coprocessor();
|
||||
synchronize_coprocessors();
|
||||
system.scanline();
|
||||
|
||||
if(vcounter() == 0) {
|
||||
|
||||
+23
-328
@@ -2,343 +2,38 @@
|
||||
|
||||
Input input;
|
||||
|
||||
uint8 Input::port_read(bool portnumber) {
|
||||
if(cartridge.has_serial() && portnumber == 1) {
|
||||
return (serial.data2() << 1) | (serial.data1() << 0);
|
||||
void Input::connect(bool port, Input::Device id) {
|
||||
Controller *&controller = (port == Controller::Port1 ? port1 : port2);
|
||||
if(controller) {
|
||||
delete controller;
|
||||
controller = 0;
|
||||
}
|
||||
|
||||
port_t &p = port[portnumber];
|
||||
|
||||
switch(p.device) {
|
||||
case Device::Joypad: {
|
||||
if(cpu.joylatch() == 0) {
|
||||
if(p.counter0 >= 16) return 1;
|
||||
return system.interface->input_poll(portnumber, p.device, 0, p.counter0++);
|
||||
} else {
|
||||
return system.interface->input_poll(portnumber, p.device, 0, 0);
|
||||
}
|
||||
} //case Device::Joypad
|
||||
|
||||
case Device::Multitap: {
|
||||
if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0
|
||||
|
||||
unsigned deviceidx, deviceindex0, deviceindex1;
|
||||
uint8 mask = (portnumber == 0 ? 0x40 : 0x80);
|
||||
|
||||
if(cpu.pio() & mask) {
|
||||
deviceidx = p.counter0;
|
||||
if(deviceidx >= 16) return 3;
|
||||
p.counter0++;
|
||||
|
||||
deviceindex0 = 0; //controller 1
|
||||
deviceindex1 = 1; //controller 2
|
||||
} else {
|
||||
deviceidx = p.counter1;
|
||||
if(deviceidx >= 16) return 3;
|
||||
p.counter1++;
|
||||
|
||||
deviceindex0 = 2; //controller 3
|
||||
deviceindex1 = 3; //controller 4
|
||||
}
|
||||
|
||||
return (system.interface->input_poll(portnumber, p.device, deviceindex0, deviceidx) << 0)
|
||||
| (system.interface->input_poll(portnumber, p.device, deviceindex1, deviceidx) << 1);
|
||||
} //case Device::Multitap
|
||||
|
||||
case Device::Mouse: {
|
||||
if(p.counter0 >= 32) return 1;
|
||||
|
||||
int position_x = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::X); //-n = left, 0 = center, +n = right
|
||||
int position_y = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Y); //-n = up, 0 = center, +n = right
|
||||
|
||||
bool direction_x = position_x < 0; //0 = right, 1 = left
|
||||
bool direction_y = position_y < 0; //0 = down, 1 = up
|
||||
|
||||
if(position_x < 0) position_x = -position_x; //abs(position_x)
|
||||
if(position_y < 0) position_y = -position_y; //abs(position_x)
|
||||
|
||||
position_x = min(127, position_x); //range = 0 - 127
|
||||
position_y = min(127, position_y); //range = 0 - 127
|
||||
|
||||
switch(p.counter0++) { default:
|
||||
case 0: return 0;
|
||||
case 1: return 0;
|
||||
case 2: return 0;
|
||||
case 3: return 0;
|
||||
case 4: return 0;
|
||||
case 5: return 0;
|
||||
case 6: return 0;
|
||||
case 7: return 0;
|
||||
|
||||
case 8: return system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Right);
|
||||
case 9: return system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Left);
|
||||
case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused)
|
||||
case 11: return 0; // ||
|
||||
|
||||
case 12: return 0; //signature
|
||||
case 13: return 0; // ||
|
||||
case 14: return 0; // ||
|
||||
case 15: return 1; // ||
|
||||
|
||||
case 16: return (direction_y) & 1;
|
||||
case 17: return (position_y >> 6) & 1;
|
||||
case 18: return (position_y >> 5) & 1;
|
||||
case 19: return (position_y >> 4) & 1;
|
||||
case 20: return (position_y >> 3) & 1;
|
||||
case 21: return (position_y >> 2) & 1;
|
||||
case 22: return (position_y >> 1) & 1;
|
||||
case 23: return (position_y >> 0) & 1;
|
||||
|
||||
case 24: return (direction_x) & 1;
|
||||
case 25: return (position_x >> 6) & 1;
|
||||
case 26: return (position_x >> 5) & 1;
|
||||
case 27: return (position_x >> 4) & 1;
|
||||
case 28: return (position_x >> 3) & 1;
|
||||
case 29: return (position_x >> 2) & 1;
|
||||
case 30: return (position_x >> 1) & 1;
|
||||
case 31: return (position_x >> 0) & 1;
|
||||
}
|
||||
} //case Device::Mouse
|
||||
|
||||
case Device::SuperScope: {
|
||||
if(portnumber == 0) break; //Super Scope in port 1 not supported ...
|
||||
if(p.counter0 >= 8) return 1;
|
||||
|
||||
if(p.counter0 == 0) {
|
||||
//turbo is a switch; toggle is edge sensitive
|
||||
bool turbo = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Turbo);
|
||||
if(turbo && !p.superscope.turbolock) {
|
||||
p.superscope.turbo = !p.superscope.turbo; //toggle state
|
||||
p.superscope.turbolock = true;
|
||||
} else if(!turbo) {
|
||||
p.superscope.turbolock = false;
|
||||
}
|
||||
|
||||
//trigger is a button
|
||||
//if turbo is active, trigger is level sensitive; otherwise it is edge sensitive
|
||||
p.superscope.trigger = false;
|
||||
bool trigger = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Trigger);
|
||||
if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) {
|
||||
p.superscope.trigger = true;
|
||||
p.superscope.triggerlock = true;
|
||||
} else if(!trigger) {
|
||||
p.superscope.triggerlock = false;
|
||||
}
|
||||
|
||||
//cursor is a button; it is always level sensitive
|
||||
p.superscope.cursor = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Cursor);
|
||||
|
||||
//pause is a button; it is always edge sensitive
|
||||
p.superscope.pause = false;
|
||||
bool pause = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Pause);
|
||||
if(pause && !p.superscope.pauselock) {
|
||||
p.superscope.pause = true;
|
||||
p.superscope.pauselock = true;
|
||||
} else if(!pause) {
|
||||
p.superscope.pauselock = false;
|
||||
}
|
||||
|
||||
p.superscope.offscreen =
|
||||
p.superscope.x < 0 || p.superscope.x >= 256
|
||||
|| p.superscope.y < 0 || p.superscope.y >= (ppu.overscan() ? 240 : 225);
|
||||
}
|
||||
|
||||
switch(p.counter0++) {
|
||||
case 0: return p.superscope.trigger;
|
||||
case 1: return p.superscope.cursor;
|
||||
case 2: return p.superscope.turbo;
|
||||
case 3: return p.superscope.pause;
|
||||
case 4: return 0;
|
||||
case 5: return 0;
|
||||
case 6: return p.superscope.offscreen;
|
||||
case 7: return 0; //noise (1 = yes)
|
||||
}
|
||||
} //case Device::SuperScope
|
||||
|
||||
case Device::Justifier:
|
||||
case Device::Justifiers: {
|
||||
if(portnumber == 0) break; //Justifier in port 1 not supported ...
|
||||
if(p.counter0 >= 32) return 1;
|
||||
|
||||
if(p.counter0 == 0) {
|
||||
p.justifier.trigger1 = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Trigger);
|
||||
p.justifier.start1 = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Start);
|
||||
|
||||
if(p.device == Device::Justifiers) {
|
||||
p.justifier.trigger2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Trigger);
|
||||
p.justifier.start2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Start);
|
||||
} else {
|
||||
p.justifier.x2 = -1;
|
||||
p.justifier.y2 = -1;
|
||||
|
||||
p.justifier.trigger2 = false;
|
||||
p.justifier.start2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
switch(p.counter0++) {
|
||||
case 0: return 0;
|
||||
case 1: return 0;
|
||||
case 2: return 0;
|
||||
case 3: return 0;
|
||||
case 4: return 0;
|
||||
case 5: return 0;
|
||||
case 6: return 0;
|
||||
case 7: return 0;
|
||||
case 8: return 0;
|
||||
case 9: return 0;
|
||||
case 10: return 0;
|
||||
case 11: return 0;
|
||||
|
||||
case 12: return 1; //signature
|
||||
case 13: return 1; // ||
|
||||
case 14: return 1; // ||
|
||||
case 15: return 0; // ||
|
||||
|
||||
case 16: return 0;
|
||||
case 17: return 1;
|
||||
case 18: return 0;
|
||||
case 19: return 1;
|
||||
case 20: return 0;
|
||||
case 21: return 1;
|
||||
case 22: return 0;
|
||||
case 23: return 1;
|
||||
|
||||
case 24: return p.justifier.trigger1;
|
||||
case 25: return p.justifier.trigger2;
|
||||
case 26: return p.justifier.start1;
|
||||
case 27: return p.justifier.start2;
|
||||
case 28: return p.justifier.active;
|
||||
|
||||
case 29: return 0;
|
||||
case 30: return 0;
|
||||
case 31: return 0;
|
||||
}
|
||||
} //case Device::Justifier(s)
|
||||
} //switch(p.device)
|
||||
|
||||
//no device connected
|
||||
return 0;
|
||||
}
|
||||
|
||||
//scan all input; update cursor positions if needed
|
||||
void Input::update() {
|
||||
system.interface->input_poll();
|
||||
port_t &p = port[1];
|
||||
|
||||
switch(p.device) {
|
||||
case Device::SuperScope: {
|
||||
int x = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::X);
|
||||
int y = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::Y);
|
||||
x += p.superscope.x;
|
||||
y += p.superscope.y;
|
||||
p.superscope.x = max(-16, min(256 + 16, x));
|
||||
p.superscope.y = max(-16, min(240 + 16, y));
|
||||
|
||||
latchx = p.superscope.x;
|
||||
latchy = p.superscope.y;
|
||||
} break;
|
||||
|
||||
case Device::Justifier:
|
||||
case Device::Justifiers: {
|
||||
int x1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::X);
|
||||
int y1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::Y);
|
||||
x1 += p.justifier.x1;
|
||||
y1 += p.justifier.y1;
|
||||
p.justifier.x1 = max(-16, min(256 + 16, x1));
|
||||
p.justifier.y1 = max(-16, min(240 + 16, y1));
|
||||
|
||||
int x2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::X);
|
||||
int y2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::Y);
|
||||
x2 += p.justifier.x2;
|
||||
y2 += p.justifier.y2;
|
||||
p.justifier.x2 = max(-16, min(256 + 16, x2));
|
||||
p.justifier.y2 = max(-16, min(240 + 16, y2));
|
||||
|
||||
if(p.justifier.active == 0) {
|
||||
latchx = p.justifier.x1;
|
||||
latchy = p.justifier.y1;
|
||||
} else {
|
||||
latchx = (p.device == Device::Justifiers ? p.justifier.x2 : -1);
|
||||
latchy = (p.device == Device::Justifiers ? p.justifier.y2 : -1);
|
||||
}
|
||||
} break;
|
||||
switch(id) { default:
|
||||
case Device::None: controller = new Controller(port); break;
|
||||
case Device::Joypad: controller = new Gamepad(port); break;
|
||||
case Device::Multitap: controller = new Multitap(port); break;
|
||||
case Device::Mouse: controller = new Mouse(port); break;
|
||||
case Device::SuperScope: controller = new SuperScope(port); break;
|
||||
case Device::Justifier: controller = new Justifier(port, false); break;
|
||||
case Device::Justifiers: controller = new Justifier(port, true); break;
|
||||
case Device::Serial: controller = new Serial(port); break;
|
||||
}
|
||||
|
||||
if(latchy < 0 || latchy >= (ppu.overscan() ? 240 : 225) || latchx < 0 || latchx >= 256) {
|
||||
//cursor is offscreen, set to invalid position so counters are not latched
|
||||
latchx = ~0;
|
||||
latchy = ~0;
|
||||
} else {
|
||||
//cursor is onscreen
|
||||
latchx += 40; //offset trigger position to simulate hardware latching delay
|
||||
latchx <<= 2; //dot -> clock conversion
|
||||
latchx += 2; //align trigger on half-dot ala interrupts (speed optimization for sCPU::add_clocks)
|
||||
switch(port) {
|
||||
case Controller::Port1: config.controller_port1 = id; break;
|
||||
case Controller::Port2: config.controller_port2 = id; break;
|
||||
}
|
||||
}
|
||||
|
||||
void Input::port_set_device(bool portnumber, Device device) {
|
||||
port_t &p = port[portnumber];
|
||||
|
||||
p.device = device;
|
||||
p.counter0 = 0;
|
||||
p.counter1 = 0;
|
||||
|
||||
//set iobit to true if device is capable of latching PPU counters
|
||||
iobit = port[1].device == Device::SuperScope
|
||||
|| port[1].device == Device::Justifier
|
||||
|| port[1].device == Device::Justifiers;
|
||||
latchx = -1;
|
||||
latchy = -1;
|
||||
|
||||
if(device == Device::SuperScope) {
|
||||
p.superscope.x = 256 / 2;
|
||||
p.superscope.y = 240 / 2;
|
||||
|
||||
p.superscope.trigger = false;
|
||||
p.superscope.cursor = false;
|
||||
p.superscope.turbo = false;
|
||||
p.superscope.pause = false;
|
||||
p.superscope.offscreen = false;
|
||||
|
||||
p.superscope.turbolock = false;
|
||||
p.superscope.triggerlock = false;
|
||||
p.superscope.pauselock = false;
|
||||
} else if(device == Device::Justifier) {
|
||||
p.justifier.active = 0;
|
||||
p.justifier.x1 = 256 / 2;
|
||||
p.justifier.y1 = 240 / 2;
|
||||
p.justifier.x2 = -1;
|
||||
p.justifier.y2 = -1;
|
||||
|
||||
p.justifier.trigger1 = false;
|
||||
p.justifier.trigger2 = false;
|
||||
p.justifier.start1 = false;
|
||||
p.justifier.start2 = false;
|
||||
} else if(device == Device::Justifiers) {
|
||||
p.justifier.active = 0;
|
||||
p.justifier.x1 = 256 / 2 - 16;
|
||||
p.justifier.y1 = 240 / 2;
|
||||
p.justifier.x2 = 256 / 2 + 16;
|
||||
p.justifier.y2 = 240 / 2;
|
||||
|
||||
p.justifier.trigger1 = false;
|
||||
p.justifier.trigger2 = false;
|
||||
p.justifier.start1 = false;
|
||||
p.justifier.start2 = false;
|
||||
}
|
||||
Input::Input() {
|
||||
connect(Controller::Port1, Input::Device::Joypad);
|
||||
connect(Controller::Port2, Input::Device::Joypad);
|
||||
}
|
||||
|
||||
void Input::poll() {
|
||||
port[0].counter0 = 0;
|
||||
port[0].counter1 = 0;
|
||||
port[1].counter0 = 0;
|
||||
port[1].counter1 = 0;
|
||||
|
||||
port[1].justifier.active = !port[1].justifier.active;
|
||||
}
|
||||
|
||||
void Input::init() {
|
||||
Input::~Input() {
|
||||
if(port1) delete port1;
|
||||
if(port2) delete port2;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,4 @@
|
||||
class Input {
|
||||
public:
|
||||
struct Input {
|
||||
enum class Device : unsigned {
|
||||
None,
|
||||
Joypad,
|
||||
@@ -8,6 +7,7 @@ public:
|
||||
SuperScope,
|
||||
Justifier,
|
||||
Justifiers,
|
||||
Serial,
|
||||
};
|
||||
|
||||
enum class JoypadID : unsigned {
|
||||
@@ -28,61 +28,12 @@ public:
|
||||
X = 0, Y = 1, Trigger = 2, Start = 3,
|
||||
};
|
||||
|
||||
uint8 port_read(bool port);
|
||||
void port_set_device(bool port, Device device);
|
||||
void init();
|
||||
void poll();
|
||||
void update();
|
||||
Controller *port1;
|
||||
Controller *port2;
|
||||
|
||||
//light guns (Super Scope, Justifier(s)) strobe IOBit whenever the CRT
|
||||
//beam cannon is detected. this needs to be tested at the cycle level
|
||||
//(hence inlining here for speed) to avoid 'dead space' during DRAM refresh.
|
||||
//iobit is updated during port_set_device(),
|
||||
//latchx, latchy are updated during update() (once per frame)
|
||||
alwaysinline void tick() {
|
||||
//only test if Super Scope or Justifier is connected
|
||||
if(iobit && cpu.vcounter() == latchy && cpu.hcounter() == latchx) {
|
||||
ppu.latch_counters();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool iobit;
|
||||
int16_t latchx, latchy;
|
||||
|
||||
struct port_t {
|
||||
Device device;
|
||||
unsigned counter0; //read counters
|
||||
unsigned counter1;
|
||||
|
||||
struct superscope_t {
|
||||
int x, y;
|
||||
|
||||
bool trigger;
|
||||
bool cursor;
|
||||
bool turbo;
|
||||
bool pause;
|
||||
bool offscreen;
|
||||
|
||||
bool turbolock;
|
||||
bool triggerlock;
|
||||
bool pauselock;
|
||||
} superscope;
|
||||
|
||||
struct justifier_t {
|
||||
bool active;
|
||||
|
||||
int x1, x2;
|
||||
int y1, y2;
|
||||
|
||||
bool trigger1, trigger2;
|
||||
bool start1, start2;
|
||||
} justifier;
|
||||
} port[2];
|
||||
|
||||
friend class System;
|
||||
friend class Video;
|
||||
friend class CPU;
|
||||
void connect(bool port, Input::Device id);
|
||||
Input();
|
||||
~Input();
|
||||
};
|
||||
|
||||
extern Input input;
|
||||
@@ -52,6 +52,7 @@ MappedRAM::MappedRAM() : data_(0), size_(0), write_protect_(false) {}
|
||||
//Bus
|
||||
|
||||
uint8 Bus::read(unsigned addr) {
|
||||
if(cheat.override[addr]) return cheat.read(addr);
|
||||
return reader[lookup[addr]](target[addr]);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ void SMP::port_write(uint2 port, uint8 data) {
|
||||
}
|
||||
|
||||
alwaysinline uint8 SMP::op_busread(uint16 addr) {
|
||||
if((addr & 0xfff0) != 0x00f0) return ram_read(addr);
|
||||
|
||||
unsigned result;
|
||||
|
||||
switch(addr) {
|
||||
@@ -57,28 +55,25 @@ alwaysinline uint8 SMP::op_busread(uint16 addr) {
|
||||
return 0x00;
|
||||
|
||||
case 0xfd: //T0OUT -- 4-bit counter value
|
||||
result = timer0.stage3_ticks & 15;
|
||||
result = timer0.stage3_ticks;
|
||||
timer0.stage3_ticks = 0;
|
||||
return result;
|
||||
|
||||
case 0xfe: //T1OUT -- 4-bit counter value
|
||||
result = timer1.stage3_ticks & 15;
|
||||
result = timer1.stage3_ticks;
|
||||
timer1.stage3_ticks = 0;
|
||||
return result;
|
||||
|
||||
case 0xff: //T2OUT -- 4-bit counter value
|
||||
result = timer2.stage3_ticks & 15;
|
||||
result = timer2.stage3_ticks;
|
||||
timer2.stage3_ticks = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0x00; //never used, avoids compiler warning
|
||||
return ram_read(addr);
|
||||
}
|
||||
|
||||
alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) {
|
||||
ram_write(addr, data); //all writes, even to MMIO registers, appear on bus
|
||||
if((addr & 0xfff0) != 0x00f0) return;
|
||||
|
||||
switch(addr) {
|
||||
case 0xf0: //TEST
|
||||
if(regs.p.p) break; //writes only valid when P flag is clear
|
||||
@@ -176,6 +171,8 @@ alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) {
|
||||
case 0xff: //T2OUT -- read-only registers
|
||||
break;
|
||||
}
|
||||
|
||||
ram_write(addr, data); //all writes, even to MMIO registers, appear on bus
|
||||
}
|
||||
|
||||
void SMP::op_io() {
|
||||
|
||||
+5
-2
@@ -1,8 +1,8 @@
|
||||
namespace SNES {
|
||||
namespace Info {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "079";
|
||||
static const unsigned SerializerVersion = 20;
|
||||
static const char Version[] = "080";
|
||||
static const unsigned SerializerVersion = 21;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace SNES {
|
||||
typedef int32_t int32;
|
||||
typedef int64_t int64;
|
||||
|
||||
typedef int_t<24> int24;
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
@@ -128,6 +130,7 @@ namespace SNES {
|
||||
#include "profile-performance.hpp"
|
||||
#endif
|
||||
|
||||
#include <snes/controller/controller.hpp>
|
||||
#include <snes/system/system.hpp>
|
||||
#include <snes/chip/chip.hpp>
|
||||
#include <snes/cartridge/cartridge.hpp>
|
||||
|
||||
@@ -62,13 +62,12 @@ void System::serialize_all(serializer &s) {
|
||||
if(cartridge.has_superfx()) superfx.serialize(s);
|
||||
if(cartridge.has_sa1()) sa1.serialize(s);
|
||||
if(cartridge.has_necdsp()) necdsp.serialize(s);
|
||||
if(cartridge.has_hitachidsp()) hitachidsp.serialize(s);
|
||||
if(cartridge.has_srtc()) srtc.serialize(s);
|
||||
if(cartridge.has_sdd1()) sdd1.serialize(s);
|
||||
if(cartridge.has_spc7110()) spc7110.serialize(s);
|
||||
if(cartridge.has_cx4()) cx4.serialize(s);
|
||||
if(cartridge.has_obc1()) obc1.serialize(s);
|
||||
if(cartridge.has_msu1()) msu1.serialize(s);
|
||||
if(cartridge.has_serial()) serial.serialize(s);
|
||||
}
|
||||
|
||||
//perform dry-run state save:
|
||||
|
||||
@@ -21,7 +21,6 @@ void System::run() {
|
||||
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
input.update();
|
||||
video.update();
|
||||
}
|
||||
}
|
||||
@@ -59,7 +58,6 @@ void System::runthreadtosave() {
|
||||
scheduler.enter();
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
|
||||
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
|
||||
input.update();
|
||||
video.update();
|
||||
}
|
||||
}
|
||||
@@ -74,24 +72,23 @@ void System::init(Interface *interface_) {
|
||||
superfx.init();
|
||||
sa1.init();
|
||||
necdsp.init();
|
||||
hitachidsp.init();
|
||||
bsxsatellaview.init();
|
||||
bsxcartridge.init();
|
||||
bsxflash.init();
|
||||
srtc.init();
|
||||
sdd1.init();
|
||||
spc7110.init();
|
||||
cx4.init();
|
||||
obc1.init();
|
||||
st0018.init();
|
||||
msu1.init();
|
||||
serial.init();
|
||||
link.init();
|
||||
|
||||
video.init();
|
||||
audio.init();
|
||||
input.init();
|
||||
|
||||
input.port_set_device(0, config.controller_port1);
|
||||
input.port_set_device(1, config.controller_port2);
|
||||
input.connect(0, config.controller_port1);
|
||||
input.connect(1, config.controller_port2);
|
||||
}
|
||||
|
||||
void System::term() {
|
||||
@@ -116,14 +113,14 @@ void System::load() {
|
||||
if(cartridge.has_superfx()) superfx.load();
|
||||
if(cartridge.has_sa1()) sa1.load();
|
||||
if(cartridge.has_necdsp()) necdsp.load();
|
||||
if(cartridge.has_hitachidsp()) hitachidsp.load();
|
||||
if(cartridge.has_srtc()) srtc.load();
|
||||
if(cartridge.has_sdd1()) sdd1.load();
|
||||
if(cartridge.has_spc7110()) spc7110.load();
|
||||
if(cartridge.has_cx4()) cx4.load();
|
||||
if(cartridge.has_obc1()) obc1.load();
|
||||
if(cartridge.has_st0018()) st0018.load();
|
||||
if(cartridge.has_msu1()) msu1.load();
|
||||
if(cartridge.has_serial()) serial.load();
|
||||
if(cartridge.has_link()) link.load();
|
||||
|
||||
serialize_init();
|
||||
cheat.init();
|
||||
@@ -140,14 +137,14 @@ void System::unload() {
|
||||
if(cartridge.has_superfx()) superfx.unload();
|
||||
if(cartridge.has_sa1()) sa1.unload();
|
||||
if(cartridge.has_necdsp()) necdsp.unload();
|
||||
if(cartridge.has_hitachidsp()) hitachidsp.unload();
|
||||
if(cartridge.has_srtc()) srtc.unload();
|
||||
if(cartridge.has_sdd1()) sdd1.unload();
|
||||
if(cartridge.has_spc7110()) spc7110.unload();
|
||||
if(cartridge.has_cx4()) cx4.unload();
|
||||
if(cartridge.has_obc1()) obc1.unload();
|
||||
if(cartridge.has_st0018()) st0018.unload();
|
||||
if(cartridge.has_msu1()) msu1.unload();
|
||||
if(cartridge.has_serial()) serial.unload();
|
||||
if(cartridge.has_link()) link.unload();
|
||||
}
|
||||
|
||||
void System::power() {
|
||||
@@ -176,24 +173,26 @@ void System::power() {
|
||||
if(cartridge.has_superfx()) superfx.power();
|
||||
if(cartridge.has_sa1()) sa1.power();
|
||||
if(cartridge.has_necdsp()) necdsp.power();
|
||||
if(cartridge.has_hitachidsp()) hitachidsp.power();
|
||||
if(cartridge.has_srtc()) srtc.power();
|
||||
if(cartridge.has_sdd1()) sdd1.power();
|
||||
if(cartridge.has_spc7110()) spc7110.power();
|
||||
if(cartridge.has_cx4()) cx4.power();
|
||||
if(cartridge.has_obc1()) obc1.power();
|
||||
if(cartridge.has_st0018()) st0018.power();
|
||||
if(cartridge.has_msu1()) msu1.power();
|
||||
if(cartridge.has_serial()) serial.power();
|
||||
if(cartridge.has_link()) link.power();
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2);
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
||||
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
|
||||
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
||||
if(cartridge.has_serial()) cpu.coprocessors.append(&serial);
|
||||
if(cartridge.has_link()) cpu.coprocessors.append(&link);
|
||||
|
||||
scheduler.init();
|
||||
input.update();
|
||||
input.connect(0, config.controller_port1);
|
||||
input.connect(1, config.controller_port2);
|
||||
}
|
||||
|
||||
void System::reset() {
|
||||
@@ -212,26 +211,26 @@ void System::reset() {
|
||||
if(cartridge.has_superfx()) superfx.reset();
|
||||
if(cartridge.has_sa1()) sa1.reset();
|
||||
if(cartridge.has_necdsp()) necdsp.reset();
|
||||
if(cartridge.has_hitachidsp()) hitachidsp.reset();
|
||||
if(cartridge.has_srtc()) srtc.reset();
|
||||
if(cartridge.has_sdd1()) sdd1.reset();
|
||||
if(cartridge.has_spc7110()) spc7110.reset();
|
||||
if(cartridge.has_cx4()) cx4.reset();
|
||||
if(cartridge.has_obc1()) obc1.reset();
|
||||
if(cartridge.has_st0018()) st0018.reset();
|
||||
if(cartridge.has_msu1()) msu1.reset();
|
||||
if(cartridge.has_serial()) serial.reset();
|
||||
if(cartridge.has_link()) link.reset();
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2);
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp);
|
||||
if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp);
|
||||
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
||||
if(cartridge.has_serial()) cpu.coprocessors.append(&serial);
|
||||
if(cartridge.has_link()) cpu.coprocessors.append(&link);
|
||||
|
||||
scheduler.init();
|
||||
input.port_set_device(0, config.controller_port1);
|
||||
input.port_set_device(1, config.controller_port2);
|
||||
input.update();
|
||||
input.connect(0, config.controller_port1);
|
||||
input.connect(1, config.controller_port2);
|
||||
}
|
||||
|
||||
void System::scanline() {
|
||||
|
||||
@@ -47,10 +47,22 @@ void Video::draw_cursor(uint16_t color, int x, int y) {
|
||||
}
|
||||
|
||||
void Video::update() {
|
||||
switch(input.port[1].device) {
|
||||
case Input::Device::SuperScope: draw_cursor(0x001f, input.port[1].superscope.x, input.port[1].superscope.y); break;
|
||||
case Input::Device::Justifiers: draw_cursor(0x02e0, input.port[1].justifier.x2, input.port[1].justifier.y2); //fallthrough
|
||||
case Input::Device::Justifier: draw_cursor(0x001f, input.port[1].justifier.x1, input.port[1].justifier.y1); break;
|
||||
switch(config.controller_port2) {
|
||||
case Input::Device::SuperScope:
|
||||
if(dynamic_cast<SuperScope*>(input.port2)) {
|
||||
SuperScope &device = (SuperScope&)*input.port2;
|
||||
draw_cursor(0x7c00, device.x, device.y);
|
||||
}
|
||||
break;
|
||||
case Input::Device::Justifier:
|
||||
case Input::Device::Justifiers:
|
||||
if(dynamic_cast<Justifier*>(input.port2)) {
|
||||
Justifier &device = (Justifier&)*input.port2;
|
||||
draw_cursor(0x001f, device.x1, device.y1);
|
||||
if(device.chained == false) break;
|
||||
draw_cursor(0x02e0, device.x2, device.y2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t *data = (uint16_t*)ppu.output;
|
||||
|
||||
@@ -77,7 +77,7 @@ void snes_set_input_state(snes_input_state_t input_state) {
|
||||
}
|
||||
|
||||
void snes_set_controller_port_device(bool port, unsigned device) {
|
||||
SNES::input.port_set_device(port, (SNES::Input::Device)device);
|
||||
SNES::input.connect(port, (SNES::Input::Device)device);
|
||||
}
|
||||
|
||||
void snes_set_cartridge_basename(const char *basename) {
|
||||
@@ -86,8 +86,8 @@ void snes_set_cartridge_basename(const char *basename) {
|
||||
|
||||
void snes_init(void) {
|
||||
SNES::system.init(&interface);
|
||||
SNES::input.port_set_device(0, SNES::Input::Device::Joypad);
|
||||
SNES::input.port_set_device(1, SNES::Input::Device::Joypad);
|
||||
SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad);
|
||||
SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad);
|
||||
}
|
||||
|
||||
void snes_term(void) {
|
||||
|
||||
@@ -10,13 +10,14 @@ extern "C" {
|
||||
#define SNES_PORT_1 0
|
||||
#define SNES_PORT_2 1
|
||||
|
||||
#define SNES_DEVICE_NONE 0
|
||||
#define SNES_DEVICE_JOYPAD 1
|
||||
#define SNES_DEVICE_MULTITAP 2
|
||||
#define SNES_DEVICE_MOUSE 3
|
||||
#define SNES_DEVICE_SUPER_SCOPE 4
|
||||
#define SNES_DEVICE_JUSTIFIER 5
|
||||
#define SNES_DEVICE_JUSTIFIERS 6
|
||||
#define SNES_DEVICE_NONE 0
|
||||
#define SNES_DEVICE_JOYPAD 1
|
||||
#define SNES_DEVICE_MULTITAP 2
|
||||
#define SNES_DEVICE_MOUSE 3
|
||||
#define SNES_DEVICE_SUPER_SCOPE 4
|
||||
#define SNES_DEVICE_JUSTIFIER 5
|
||||
#define SNES_DEVICE_JUSTIFIERS 6
|
||||
#define SNES_DEVICE_SERIAL_CABLE 7
|
||||
|
||||
#define SNES_DEVICE_ID_JOYPAD_B 0
|
||||
#define SNES_DEVICE_ID_JOYPAD_Y 1
|
||||
|
||||
@@ -82,9 +82,13 @@ void MainWindow::create() {
|
||||
systemPort2Justifiers.setText("Justifiers");
|
||||
systemPort2.append(systemPort2Justifiers);
|
||||
|
||||
systemPort2Serial.setText("Serial Cable");
|
||||
systemPort2.append(systemPort2Serial);
|
||||
|
||||
RadioItem::group(
|
||||
systemPort2None, systemPort2Gamepad, systemPort2Multitap, systemPort2Mouse,
|
||||
systemPort2SuperScope, systemPort2Justifier, systemPort2Justifiers
|
||||
systemPort2SuperScope, systemPort2Justifier, systemPort2Justifiers,
|
||||
systemPort2Serial
|
||||
);
|
||||
|
||||
append(system);
|
||||
@@ -235,6 +239,7 @@ void MainWindow::create() {
|
||||
if(config.controller.port2 == 4) systemPort2SuperScope.setChecked();
|
||||
if(config.controller.port2 == 5) systemPort2Justifier.setChecked();
|
||||
if(config.controller.port2 == 6) systemPort2Justifiers.setChecked();
|
||||
if(config.controller.port2 == 7) systemPort2Serial.setChecked();
|
||||
|
||||
if(config.video.scale == 1) settingsVideoMode1x.setChecked();
|
||||
if(config.video.scale == 2) settingsVideoMode2x.setChecked();
|
||||
@@ -289,6 +294,7 @@ void MainWindow::create() {
|
||||
systemPort2SuperScope.onTick = []() { config.controller.port2 = 4; utility.setControllers(); };
|
||||
systemPort2Justifier.onTick = []() { config.controller.port2 = 5; utility.setControllers(); };
|
||||
systemPort2Justifiers.onTick = []() { config.controller.port2 = 6; utility.setControllers(); };
|
||||
systemPort2Serial.onTick = []() { config.controller.port2 = 7; utility.setControllers(); };
|
||||
|
||||
settingsVideoMode1x.onTick = []() { utility.setScale(1); };
|
||||
settingsVideoMode2x.onTick = []() { utility.setScale(2); };
|
||||
|
||||
@@ -23,6 +23,7 @@ struct MainWindow : TopLevelWindow {
|
||||
RadioItem systemPort2SuperScope;
|
||||
RadioItem systemPort2Justifier;
|
||||
RadioItem systemPort2Justifiers;
|
||||
RadioItem systemPort2Serial;
|
||||
|
||||
Menu settings;
|
||||
Menu settingsVideoMode;
|
||||
|
||||
@@ -82,7 +82,7 @@ void InputMapper::Gamepad::create(const char *deviceName, const char *configName
|
||||
}
|
||||
|
||||
int16_t InputMapper::Gamepad::poll(unsigned id) {
|
||||
switch(id) {
|
||||
switch((SNES::Input::JoypadID)id) {
|
||||
case SNES::Input::JoypadID::Up: return up.poll();
|
||||
case SNES::Input::JoypadID::Down: return down.poll() & !up.poll();
|
||||
case SNES::Input::JoypadID::Left: return left.poll();
|
||||
@@ -118,7 +118,7 @@ void InputMapper::Mouse::create(const char *deviceName, const char *configName)
|
||||
}
|
||||
|
||||
int16_t InputMapper::Mouse::poll(unsigned id) {
|
||||
switch(id) {
|
||||
switch((SNES::Input::MouseID)id) {
|
||||
case SNES::Input::MouseID::X: return x.poll();
|
||||
case SNES::Input::MouseID::Y: return y.poll();
|
||||
case SNES::Input::MouseID::Left: return left.poll();
|
||||
@@ -150,7 +150,7 @@ void InputMapper::SuperScope::create(const char *deviceName, const char *configN
|
||||
}
|
||||
|
||||
int16_t InputMapper::SuperScope::poll(unsigned id) {
|
||||
switch(id) {
|
||||
switch((SNES::Input::SuperScopeID)id) {
|
||||
case SNES::Input::SuperScopeID::X: return x.poll();
|
||||
case SNES::Input::SuperScopeID::Y: return y.poll();
|
||||
case SNES::Input::SuperScopeID::Trigger: return trigger.poll();
|
||||
@@ -182,7 +182,7 @@ void InputMapper::Justifier::create(const char *deviceName, const char *configNa
|
||||
}
|
||||
|
||||
int16_t InputMapper::Justifier::poll(unsigned id) {
|
||||
switch(id) {
|
||||
switch((SNES::Input::JustifierID)id) {
|
||||
case SNES::Input::JustifierID::X: return x.poll();
|
||||
case SNES::Input::JustifierID::Y: return y.poll();
|
||||
case SNES::Input::JustifierID::Trigger: return trigger.poll();
|
||||
|
||||
@@ -118,6 +118,7 @@ void Application::main(int argc, char **argv) {
|
||||
|
||||
if(SNES::cartridge.loaded()) {
|
||||
if(application.pause == true || (config.settings.focusPolicy == 0 && mainWindow.focused() == false)) {
|
||||
audio.clear();
|
||||
usleep(20 * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -40,20 +40,21 @@ void Utility::showMessage(const string &text) {
|
||||
|
||||
void Utility::setControllers() {
|
||||
switch(config.controller.port1) {
|
||||
case 0: SNES::input.port_set_device(0, SNES::Input::Device::None); break;
|
||||
case 1: SNES::input.port_set_device(0, SNES::Input::Device::Joypad); break;
|
||||
case 2: SNES::input.port_set_device(0, SNES::Input::Device::Multitap); break;
|
||||
case 3: SNES::input.port_set_device(0, SNES::Input::Device::Mouse); break;
|
||||
case 0: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::None); break;
|
||||
case 1: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad); break;
|
||||
case 2: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Multitap); break;
|
||||
case 3: SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Mouse); break;
|
||||
}
|
||||
|
||||
switch(config.controller.port2) {
|
||||
case 0: SNES::input.port_set_device(1, SNES::Input::Device::None); break;
|
||||
case 1: SNES::input.port_set_device(1, SNES::Input::Device::Joypad); break;
|
||||
case 2: SNES::input.port_set_device(1, SNES::Input::Device::Multitap); break;
|
||||
case 3: SNES::input.port_set_device(1, SNES::Input::Device::Mouse); break;
|
||||
case 4: SNES::input.port_set_device(1, SNES::Input::Device::SuperScope); break;
|
||||
case 5: SNES::input.port_set_device(1, SNES::Input::Device::Justifier); break;
|
||||
case 6: SNES::input.port_set_device(1, SNES::Input::Device::Justifiers); break;
|
||||
case 0: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::None); break;
|
||||
case 1: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad); break;
|
||||
case 2: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Multitap); break;
|
||||
case 3: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Mouse); break;
|
||||
case 4: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::SuperScope); break;
|
||||
case 5: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Justifier); break;
|
||||
case 6: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Justifiers); break;
|
||||
case 7: SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Serial); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user