Update to v080 release.

byuu says:

This release adds low-level emulation of the Hitachi HG51B169 DSP, which
was used in Mega Man X2 and Mega Man X3 as the Cx4 chip. It also fixes
a regression in both the sound core and cheat engine.

You will now need the HG51B169 data ROM to play MMX2/MMX3.

Once again, Cx4 LLE could not have been possible without the help of Dr.
Decapitator, Jonas Quinn, Overload and Segher. Be sure to thank them,
please!

Changelog:
* added Cx4 low-level emulation; removed Cx4 high-level emulation code
* fixed S-SMP synchronization to S-CPU on CPUIO writes
* controllers now have their own threads and classes
* serial controller is now emulated as an actual controller, rather than
  as a coprocessor
* added link coprocessor module for special chip research and homebrew
* fixed cheat codes that target mask ROM addresses [Cydrak]
* fixed compilation error with the latest GCC 4.6.0 beta releases
* added flexibility to XML memory mapping file format
* updated to mightymo's latest cheat pack (2011-06-20)
This commit is contained in:
Tim Allen 2011-06-26 22:51:37 +10:00
parent 927c97eb06
commit 5fc86eae6d
39 changed files with 569 additions and 369 deletions

View File

@ -1,7 +1,7 @@
include nall/Makefile
snes := snes
gameboy := gameboy
profile := accuracy
profile := compatibility
ui := ui
# debugger

View File

@ -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>

View File

@ -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("'", "\"");
@ -159,7 +159,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
xml << " <map address='80-bf:6000-7fff'/>\n";
xml << " </icd2>\n";
} else if(has_cx4) {
xml << " <hitachidsp model='HG51B169' frequency='20000000' program='cx4.bin' sha256='ae8d4d1961b93421ff00b3caa1d0f0ce7783e749772a3369c36b3dbf0d37ef18'>\n";
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";
@ -178,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";
@ -205,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)) {
@ -226,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)) {
@ -244,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";
@ -252,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)) {
@ -277,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";
@ -301,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";
@ -315,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";
@ -336,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";
@ -373,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";
@ -383,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";
@ -411,7 +411,7 @@ SNESCartridge::SNESCartridge(const uint8_t *data, unsigned size) {
}
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";
@ -444,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";
@ -457,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";
@ -470,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";
@ -490,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";
@ -507,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";

View File

@ -5,7 +5,7 @@ snes_objects += snes-cpu snes-smp snes-dsp snes-ppu
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-link
snes_objects += snes-msu1 snes-link
objects += $(snes_objects)
ifeq ($(profile),accuracy)
@ -54,5 +54,4 @@ 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/*

View File

@ -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() {

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -29,13 +29,12 @@ void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) {
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);

View File

@ -46,7 +46,6 @@ public:
readonly<bool> has_obc1;
readonly<bool> has_st0018;
readonly<bool> has_msu1;
readonly<bool> has_serial;
readonly<bool> has_link;
struct NonVolatileRAM {
@ -115,9 +114,10 @@ private:
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&);
};

View File

@ -49,7 +49,6 @@ void Cartridge::parse_xml_cartridge(const char *data) {
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);
}
}
@ -73,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);
@ -84,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) {
@ -93,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);
@ -121,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 });
}
@ -162,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) {
@ -179,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);
@ -213,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);
}
@ -238,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);
@ -247,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) {
@ -256,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);
@ -289,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") {
@ -378,24 +377,24 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = 0x000000;
string program, sha256;
string dataROM, sha256;
foreach(attr, root.attribute) {
if(attr.name == "frequency") {
hitachidsp.frequency = decimal(attr.content);
} else if(attr.name == "program") {
program = attr.content;
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")), program };
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 program ", program, " is missing." });
system.interface->message({ "Warning: Hitachi DSP data ", dataROM, " is missing." });
} else if(fp.size() != 1024 * 3) {
system.interface->message({ "Warning: Hitachi DSP program ", program, " is of the wrong file size." });
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);
@ -417,7 +416,7 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
foreach(n, hash) filehash.append(hex<2>(n));
if(sha256 != filehash) {
system.interface->message({ "Warning: Hitachi DSP program ", program, " SHA256 sum is incorrect." });
system.interface->message({ "Warning: Hitachi DSP data ", dataROM, " SHA256 sum is incorrect." });
}
}
@ -425,25 +424,29 @@ void Cartridge::xml_parse_hitachidsp(xml_element &root) {
}
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 });
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);
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);
}
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);
}
}
}
@ -458,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);
}
@ -510,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);
@ -521,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) {
@ -531,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);
@ -607,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);
}
@ -624,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) {
@ -633,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);
}
@ -697,17 +700,13 @@ 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 = decimal(attr.content);
if(attr.name == "frequency") link.frequency = xml_parse_unsigned(attr.content);
if(attr.name == "program") link.program = attr.content;
}
@ -722,6 +721,15 @@ void Cartridge::xml_parse_link(xml_element &root) {
}
}
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) {
lstring part;
part.split(":", data);

View File

@ -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;
}
//===============

View File

@ -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;

View File

@ -17,7 +17,6 @@ struct Coprocessor : Processor {
#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) {

View File

@ -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_coprocessors();
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_coprocessors();
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);
}
}

View File

@ -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;

View File

@ -1,9 +0,0 @@
#ifdef SERIAL_CPP
void Serial::serialize(serializer &s) {
Processor::serialize(s);
s.integer((bool&)data1);
s.integer((bool&)data2);
}
#endif

View File

@ -8,6 +8,7 @@ namespace SNES {
#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();

View File

@ -32,3 +32,4 @@ struct Controller : Processor {
#include "mouse/mouse.hpp"
#include "superscope/superscope.hpp"
#include "justifier/justifier.hpp"
#include "serial/serial.hpp"

View File

@ -1,12 +1,20 @@
#ifdef CONTROLLER_CPP
void Justifier::enter() {
unsigned prev = 0;
while(true) {
static unsigned prev = 0;
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
if(next >= target && prev < target) {
iobit(0);
iobit(1);
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) {
@ -25,18 +33,14 @@ void Justifier::enter() {
x2 = max(-16, min(256 + 16, nx2));
y2 = max(-16, min(240 + 16, ny2));
}
if(active == 0) {
bool offscreen = (x1 < 0 || y1 < 0 || x1 >= 256 || y1 >= (ppu.overscan() ? 240 : 225));
target = offscreen ? -1 : y1 * 1364 + (x1 + 24) * 4;
} else {
bool offscreen = (x2 < 0 || y2 < 0 || x2 >= 256 || y2 >= (ppu.overscan() ? 240 : 225));
target = offscreen ? -1 : y2 * 1364 + (x2 + 24) * 4;
}
} 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(4);
step(2);
}
}
@ -117,8 +121,6 @@ Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chaine
y2 = 240 / 2;
}
target = -1;
trigger1 = false;
trigger2 = false;

View File

@ -12,7 +12,6 @@ struct Justifier : Controller {
bool active;
signed x1, x2;
signed y1, y2;
signed target;
bool trigger1, trigger2;
bool start1, start2;
};

View File

@ -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

View File

@ -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;
};

View File

@ -5,17 +5,25 @@
//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) {
static unsigned prev = 0;
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
if(next >= target && prev < target) {
iobit(0);
iobit(1);
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) {
@ -27,11 +35,14 @@ void SuperScope::enter() {
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));
target = offscreen ? -1 : y * 1364 + (x + 24) * 4;
} 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(4);
step(2);
}
}
@ -101,7 +112,6 @@ SuperScope::SuperScope(bool port) : Controller(port) {
//center cursor onscreen
x = 256 / 2;
y = 240 / 2;
target = -1;
trigger = false;
cursor = false;

View File

@ -8,7 +8,7 @@ struct SuperScope : Controller {
bool latched;
unsigned counter;
signed x, y, target;
signed x, y;
bool trigger;
bool cursor;

View File

@ -6,8 +6,6 @@ void CPU::step_auto_joypad_poll() {
status.auto_joypad_active = status.auto_joypad_counter <= 15;
if(status.auto_joypad_active && status.auto_joypad_poll) {
synchronize_controllers();
if(status.auto_joypad_counter == 0) {
input.port1->latch(1);
input.port2->latch(1);

View File

@ -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);

View File

@ -17,6 +17,7 @@ void Input::connect(bool port, Input::Device id) {
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;
}
switch(port) {

View File

@ -7,6 +7,7 @@ struct Input {
SuperScope,
Justifier,
Justifiers,
Serial,
};
enum class JoypadID : unsigned {

View File

@ -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]);
}

View File

@ -1,7 +1,7 @@
namespace SNES {
namespace Info {
static const char Name[] = "bsnes";
static const char Version[] = "079.06";
static const char Version[] = "080";
static const unsigned SerializerVersion = 21;
}
}

View File

@ -68,7 +68,6 @@ void System::serialize_all(serializer &s) {
if(cartridge.has_spc7110()) spc7110.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:

View File

@ -82,7 +82,6 @@ void System::init(Interface *interface_) {
obc1.init();
st0018.init();
msu1.init();
serial.init();
link.init();
video.init();
@ -121,7 +120,6 @@ void System::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();
@ -146,7 +144,6 @@ void System::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();
}
@ -183,7 +180,6 @@ void System::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);
@ -192,7 +188,6 @@ void System::power() {
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();
@ -223,7 +218,6 @@ void System::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);
@ -232,7 +226,6 @@ void System::reset() {
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();

View File

@ -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) {

View File

@ -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

View File

@ -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); };

View File

@ -23,6 +23,7 @@ struct MainWindow : TopLevelWindow {
RadioItem systemPort2SuperScope;
RadioItem systemPort2Justifier;
RadioItem systemPort2Justifiers;
RadioItem systemPort2Serial;
Menu settings;
Menu settingsVideoMode;

View File

@ -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;
}

View File

@ -54,6 +54,7 @@ void Utility::setControllers() {
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;
}
}