Compare commits

...

44 commits
4.4 ... main

Author SHA1 Message Date
Dave Bernazzani
d57a21997a Version 5.1 released - hotfix to improve config efficiency. 2025-02-28 06:21:18 -05:00
wavemotion-dave
849c18eb2b
Update README.md 2025-02-28 05:54:36 -05:00
wavemotion-dave
c1d61673a7
Update README.md 2025-02-28 05:53:29 -05:00
Dave Bernazzani
ec31048360 Minor correction to the readme file for v5.1 2025-02-27 06:30:36 -05:00
Dave Bernazzani
08a5ed111b Version 5.1 - see readme.md file for details. 2025-02-26 10:56:25 -05:00
Dave Bernazzani
941f3c6302 Cleanup of source code formatting and some database improvements. 2025-02-26 08:00:27 -05:00
Dave Bernazzani
1043fbc291 Switched to 20 VBLANK scanlines and 262 total scanlines. 2025-02-25 18:16:17 -05:00
Dave Bernazzani
07057b5051 More cleanup for the Frame rendering handler 2025-02-25 17:27:20 -05:00
Dave Bernazzani
81fedf5a06 Reworked configuration - first pass at adding memory for global config. 2025-02-25 09:02:30 -05:00
Dave Bernazzani
7ee7f113e3 And yet another round of cleanup in the frame rendering. 2025-02-25 07:06:42 -05:00
Dave Bernazzani
a8f7c879f4 A round of cleanup around Prosystem Frame handling to try and dial in on getting as many games to work properly both with and without BIOS enabled. 2025-02-24 18:53:51 -05:00
Dave Bernazzani
30d892e564 And yet more cleanup for BIOS handling 2025-02-24 12:16:03 -05:00
Dave Bernazzani
a2bb1219de And yet more cleanup for BIOS handling. 2025-02-23 14:46:37 -05:00
Dave Bernazzani
4761ace958 Another round of cleanup for BIOS/Cart swap handling. 2025-02-23 12:19:46 -05:00
Dave Bernazzani
fb3e058e2b Overhaul of Maria Cycle counting to improve accuracy. First pass at adding BIOS support. 2025-02-23 10:18:53 -05:00
Dave Bernazzani
92ac5d7a20 Version 5.0 - See readme.md for details. 2025-02-21 07:05:39 -05:00
Dave Bernazzani
b80e0211f2 Another cleanup pass of the new composite filtering to handle artifacts. 2025-02-19 12:44:32 -05:00
Dave Bernazzani
5510d23ae1 More cleanup for Kangaroo mode and first pass at adding composite/artifact handling for Tower Toppler. 2025-02-18 08:37:59 -05:00
Dave Bernazzani
3f67763fd9 Fix for KANGAROO_MODE (though it's still not enabled by default) 2025-02-17 11:38:41 -05:00
Dave Bernazzani
19ec089aa9 Version 4.9 Released. See readme.md for details. 2025-02-17 08:58:26 -05:00
Dave Bernazzani
e452594769 Adding in RAM mirrors for improved emulation accuracy. Slight optmization to memory read handler to offset that small hit to speed due to improved accuracy. 2025-02-16 09:23:50 -05:00
Dave Bernazzani
b3a96e11ea Version 4.8 - see readme for details. 2025-02-15 17:24:31 -05:00
Dave Bernazzani
f116ec6863 Some tech specs for the Atari that were useful for me 2024-06-15 07:26:04 -04:00
Dave Bernazzani
13d89fe935 Readme updated for version 4.7 2024-05-11 07:54:47 -04:00
Dave Bernazzani
ef779ea625 Version 4.7 - see readme for details. 2024-05-11 07:48:34 -04:00
wavemotion-dave
c08f1b5329
Update README.md 2024-05-11 07:10:21 -04:00
wavemotion-dave
66d344522b
Update README.md 2024-05-11 07:09:32 -04:00
Dave Bernazzani
287894babc 4.6d with database cleanup, debug improvements and minor tweaks. 2024-05-11 07:06:15 -04:00
Dave Bernazzani
4ff31b149c Updated readme for version 4.6c 2024-05-10 07:43:34 -04:00
Dave Bernazzani
f1540e71a9 4.6c with fix for Legend of Silverpeak (swap in bank 0 by default) and improvement for screen shift using X/Y so the screen does not jitter when you hold the button down. 2024-05-09 09:16:33 -04:00
Dave Bernazzani
a57f6293c8 4.6b adding a number of new and updated homebrews to the internal database using Trebor's PRO PACK from January 2024. 2024-05-08 08:04:19 -04:00
wavemotion-dave
e07035aa31
Explained Screen Pan Up/Down 2024-05-07 07:55:41 -04:00
wavemotion-dave
c1e90fdb59
Added section on screen tweaking. 2024-05-07 07:54:29 -04:00
Dave Bernazzani
8fe0219f2c Version 4.6a with the new ability to shift/pan the screen up/down using the X/Y buttons. This allows for 16 extra pixels to be shifted into view for games that utilize a significant number of extra scanlines. 2024-05-07 07:27:38 -04:00
wavemotion-dave
7a5fdc4db2
Update README.md for version 4.6 2024-05-06 09:54:01 -04:00
wavemotion-dave
44c7432e6e
Update README.md for version 4.6 2024-05-06 09:42:05 -04:00
Dave Bernazzani
90abe106ce Version 4.6 with full support for the .a78 v4 headers, improved HSC support, and other sundry improvements. See readme.md for details. 2024-05-06 09:02:41 -04:00
Dave Bernazzani
30f0a004c0 Readme updated for version 4.5b 2024-05-05 11:06:01 -04:00
Dave Bernazzani
07887cc610 Version 4.5b with new Zoom magnifying glass icon to toggle a 1:1 zoom the display (with centering) - useful for games with text or high score entry. Each game that utilizes the High Score Cart (HSC) will get their own 2K of "battery backed" SRAM. 2024-05-05 10:59:15 -04:00
Dave Bernazzani
7441d9ff2a Version 4.5b with new Zoom magnifying glass icon to toggle a 1:1 zoom the display (with centering) - useful for games with text or high score entry. Each game that utilizes the High Score Cart (HSC) will get their own 2K of "battery backed" SRAM. 2024-05-05 10:25:58 -04:00
Dave Bernazzani
0b705b495c Version 4.5a with some sanity checks added to ensure a larger than 48K cart does not ever overwrite memory if inadvertently set (or detected) to be a CF_NORMAL type cart. This was causing problems with the Donkey Kong XM demo cart. 2024-05-04 08:06:14 -04:00
wavemotion-dave
a6f3527016 Version 4.5 with improved display output (small fonts look better) and SNES pad support. 2022-11-18 10:42:30 -05:00
wavemotion-dave
455e2d24f3 The new SNES2Atari scheme for the 7800 is supported. Choose this as a controller type for the few games that utilize it. Hopefully more will follow! 2022-11-17 07:32:14 -05:00
wavemotion-dave
4abfabf98f
Minor typos corrected. 2022-11-15 07:08:08 -05:00
39 changed files with 3357 additions and 2009 deletions

Binary file not shown.

View file

@ -1,4 +1,4 @@
VERSION=4.4 VERSION=5.1
TARGNAME=A7800DS TARGNAME=A7800DS
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------

263
README.md
View file

@ -2,11 +2,11 @@
a7800DS is an Atari ProSystem 7800 console emulator for the DS/DSi. a7800DS is an Atari ProSystem 7800 console emulator for the DS/DSi.
To use this emulator, you must use compatibles rom with a78/bin format. To use this emulator, you must use compatible roms with a78/bin format.
Strongly recommend you use NTSC roms... PAL ones have more scanlines and will render Strongly recommend you use NTSC roms... PAL ones have more scanlines and will render
more slowly and since the sound core is tied to scanlines, the sound will be wrong. more slowly and since the sound core is tied to scanlines, the sound will be wrong.
All the debug on this port to the DS has been done with NTSC roms - seek them out! All the debug on this port to the DS has been done with NTSC roms - seek them out!
Do not ask me about such files, I don't have them. A search with Google will certainly Do not ask me about such files, I only supply the emulator. A search with Google will certainly
help you. The emulator was developed using Trebor's "7800 ProPack" of well-curated games. help you. The emulator was developed using Trebor's "7800 ProPack" of well-curated games.
The emulator will auto-start in /roms/a7800 or /roms/a78 if those directories exist. The emulator will auto-start in /roms/a7800 or /roms/a78 if those directories exist.
@ -23,23 +23,42 @@ Features :
All popular bank-switching schemes are supported including an extra 16K of RAM at 4000h. All popular bank-switching schemes are supported including an extra 16K of RAM at 4000h.
Pokey support at 4000h, 800h and 450h - change this in Configuration if it's not auto-detected. Pokey support at 4000h, 800h and 450h - change this in Configuration if it's not auto-detected.
Starting with version 4.3, the new Banksets scheme is fully supported - this new banking/memory handling The Banksets scheme is fully supported - this new banking/memory handling is designed
is designed for homebrew authors to provide increased ROM density and improved packing and access for homebrew authors to provide increased ROM density and improved packing and access of
of graphics data vs code. This allows for games that would have been difficult or impossible without graphics data vs code. This allows for games that would have been difficult or impossible
the scheme. See http://7800.8bitdev.org/index.php/Bankset_Bankswitching for more details. without the scheme. See http://7800.8bitdev.org/index.php/Bankset_Bankswitching for more details.
Add highscore.rom for 7800 High Score saving. This can be in /roms/bios, /data/bios Add highscore.rom for 7800 High Score saving. This can be in /roms/bios, /data/bios
or in the same directory as the emulator. It's worth the effort! or in the same directory as the emulator. It's worth the effort to track down the highscore.rom file!
If you want to use a real Atari 7800 BIOS, find yourself the 4K version and place it
into the same directory as mentioned in the paragraph above.
Copyright : Copyright :
---------- ----------
A7800DS is Copyright 2021-2022 by Dave Bernazzani (wavemotion-dave). A7800DS is Copyright 2021-2025 by Dave Bernazzani (wavemotion-dave).
This emulator is based heavily upon ProSystem and that emulator was released
in 2005 by Greg Stanton under the GNU General Public License and, as such,
this derived work is released in the same manner. The original license text
that Greg used is included here:
```
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
```
Copying and distribution of this emulator, it's source code and associated
readme files, with or without modification, are permitted in any medium without
royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition),
Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely.
Philosophy : Philosophy :
---------- ----------
@ -48,16 +67,18 @@ problematic in terms of accuracy - often resulting in small graphical glitches a
other artifacts on screen. However, in the past year I've worked out many of those other artifacts on screen. However, in the past year I've worked out many of those
issues and most of the games look, sound and play great now. However, do not expect issues and most of the games look, sound and play great now. However, do not expect
perfect emulation - if you're looking for a highly accurate emulator for the 7800 perfect emulation - if you're looking for a highly accurate emulator for the 7800
7800 ProSystem, this isn't it - try MAME/MESS or A7800. But if you're looking to enjoy ProSystem, this isn't it - try A7800 or JS7800 instead. But if you're looking to enjoy
some classic 7800 console goodness on your DS/DSi then you've come to the right place! some classic 7800 console goodness on your DS/DSi then you've come to the right place!
Known Issues and Limitations: Known Issues and Limitations:
---------- ----------
- Lightgun is not supported. - Lightgun is not supported.
- Paddles are not supported. - Paddles are not supported.
- Games greater than 1MB (1024K + 128b header) are not supported. - PAL is not supported - use NTSC instead.
- Only one Pokey is supported at 4000h, 800h and 450h (no Dual Pokey) - Souper mapper (Ricky & Vicky) is not supported.
- XM is not supported (beyond HSC and Pokey) - Keystone Koppers and ARTI have slight sound issues as Pokey emulation is not perfect.
- Only one Pokey is supported at 4000h, 800h or 450h (no Dual Pokey).
- XM is not supported (beyond HSC and Pokey).
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
How to use a7800DS : How to use a7800DS :
@ -66,6 +87,10 @@ Place .NDS on your SD card and launch with Twilight Menu++ or Unlaunch.
If you want to run on a flash cart place it as you would any homebrew pretty If you want to run on a flash cart place it as you would any homebrew pretty
much anywhere on your flashcart SD card. much anywhere on your flashcart SD card.
If you want to use a real Atari 7800 BIOS, find yourself the 4K version and place
this file named exactly 7800.rom in the /roms/bios directory (alternate locations
are /data/bios or you can place it in the same directory as the emulator).
When the emulator starts, click on the cartridge slot to choose a file. Use Up/Down When the emulator starts, click on the cartridge slot to choose a file. Use Up/Down
to select a file, then use A to load it. to select a file, then use A to load it.
@ -75,19 +100,23 @@ Controls :
* B : Fire button 2 * B : Fire button 2
* SELECT : SELECT Button * SELECT : SELECT Button
* START : PAUSE Button * START : PAUSE Button
* X : FPS Display * X : Configurable (default: Pan Screen Down)
* Y : Fire Button 1 (for those that prefer not using A) * Y : Configurable (default: Pan Screen Up)
* L/R + DPAD : Used to Shift Offsets and Scale Screen to desired ratio * L/R + DPAD : Used to Shift Offsets and Scale Screen to desired ratio
* L + R + X : Hold for 1 second to swap LCD screens top/bottom
Use stylus on buttons for other actions on bottom screen. Use stylus on buttons for other actions on bottom screen.
High Score Saving works if you have highscore.rom (exact name and case) in your The new SNES2Atari adaptor is supported as a controller type for the few new games
roms directory where you load your games... also, you MUST press the HSC button that utilize it. This allows all of the DS buttons to map into the game - exactly
if you want to snap the Saved Scores out to the flash card. It's not something as labeled (D-Pad plus ABXY, Left Shoulder, Right Shoulder and Start, Select).
I want to do as the game runs... so you must do it... the high scores will also
auto-save if you quit the emulator or select a new game.
Configuration : High Score Saving works if you have highscore.rom (exact name) in your
roms directory where you load your games... The .hsc backing file will be written
automatically as the game runs. Only games programmed to use the highscore cart
will save scores.
Configuration :
---------- ----------
Generally you would use this to select a bankswitching scheme if the proper type wasn't auto-detected. Generally you would use this to select a bankswitching scheme if the proper type wasn't auto-detected.
The following schemes are supported: The following schemes are supported:
@ -104,27 +133,59 @@ The following schemes are supported:
* BANKSETS HALT RAM Games that use the new Banksets handling with 16K of RAM at 0x4000 (Maria and Sally see different 16K RAM). * BANKSETS HALT RAM Games that use the new Banksets handling with 16K of RAM at 0x4000 (Maria and Sally see different 16K RAM).
* ACTIVISION Mostly for Double Dragon and Rampage by Activision * ACTIVISION Mostly for Double Dragon and Rampage by Activision
* ABSOLETE Only for the F-18 Hornet game by Absolete Entertainment * ABSOLETE Only for the F-18 Hornet game by Absolete Entertainment
* FRACTALUS Only for the Rescue on Fractalus prototype (not a complete game but neat to see) * FRACTALUS Only for the Rescue on Fractalus prototype (not a complete game but neat to see). This is EXRAM/A8.
The X/Y settings can be tweaked to position the 7800 emulated screen on the DS screen as best you can. Unfortunately
the 7800 screen resolution isn't a perfect match for the DS 256x192 so you'll have to stretch and offset a bit. Most
games have defaults that look good enough.
Frame Skipping can be OFF (show all frames), Moderate (Show 3/4 frames) or Agressive (only show 1/2 frames). The latter is Frame Skipping can be OFF (show all frames), Moderate (Show 3/4 frames) or Agressive (only show 1/2 frames). The latter is
only really needed for the DS-Lite/Phat where the faster DSi CPU isn't available. only really needed for the DS-Lite/Phat where the faster DSi CPU isn't available.
Don't touch the DMA Cycle Adjustment unless you understand them... and most people don't :)
Press START to save off your configuration - if you made changes you should re-load the game to ensure all settings are applied. Press START to save off your configuration - if you made changes you should re-load the game to ensure all settings are applied.
Of Mice, Men and Screen Resolutions :
----------
The DS/DSi has a native screen resolution of 256x192. This is not ideal for the Atari 7800 where many of the games are 320
pixels across and often more than 192 scanlines. The original Atari NTSC spec called for 192 vertical scanlines and a few
of the early games (Asteroids, Ms. Pac-Man, etc.) did stick to that but most later games utilize more vertical scanlines.
A typical NTSC TV can handle 230+ scanlines fairly well and so many of the Atari 7800 games utilize some number of extra
scanlines to pack as much awesome gameplay as possible onto the screen.
This is a problem for our hero, the DS/DSi. Fortunately the DS has the ability to scale/stretch as needed. But when doing
so, there will be missing scanlines. For example, if the game utilizes 200 scalines but the DS can only show 192, there are
8 scanlines that must go missing... if scaled down to 192 pixels to fit the screen, these extra scalines might be in the
middle of the playfield which is not great. If you scale the screen up (using Configuration options or the L/R shoulder
buttons in conjunction with the D-PAD to shift/scale the sceren), then some of the pixels (left or right, up or down) will
be cropped off the screen. This might not be a big deal - sometimes the very top and bottom of a game are just clouds or
ground that can be safely cropped without any loss in gameplay. For many games, the top 16 pixels are the score / lives
remaining.
Most games have scaling defaults that look good enough. Some games will cut off a few pixel lines at the top and bottom - but
they will still be perfectly playalbe. However there are some thigns you can (and should!) do to help.
Recent versions of the emulator have a magnifying glass icon that will zoom and center the display to 1:1 of the actual 7800 output.
This will crop some pixels off the sides and top/bottom but is useful to temporarily zoom up to enter things like high scores
where the text may be hard to read when shrunk down.
More importantly, you can utilize the X and Y buttons as a pan down/up. This is massively useful for games that have a score at
the top of the display (Galaxian, Space Invaders, Popeye, bonQ, etc). Here you can stretch up the screen in the Y direction so
that it is nearly 1:1 and shift the score off the top of the screen. Then, during gameplay, you can tap the X button to temporarily
pan the display down so the score comes into view briefly... and it will magically shift back up after a half second. You can do
this while you are playing - usually when you lose a life you can tap the X button to quickly glace at your score. This gives
more useable scanlines for actual gameplay. Think of this like you're at the arcade and you have to glance up to see your
score when focused on the field of play. It takes a little getting used to but this mechanism really helps map the more complicated
game graphics onto the small sceren. Of course youc an always scale the screen down to it's totally visible - but there will be
some loss of scanline information. Experiment and determine what works best for you. Many of the popular games already have
the screen set to perfectly pan up/down to bring in the score/status while leaving the playfield as close to 1:1 as possible.
And remember - once you get your screen settings the way you want, be sure to go into the GEAR icon and hit START to save out
your current configuration (which includes your screen offset/scaling tweaks on a per-game basis).
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Credits: Credits:
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
* Thanks Wintermute for devkitpro and libnds (http://www.devkitpro.org). * Thanks Wintermute for devkitpro and libnds (http://www.devkitpro.org).
* Greg Stanton for ProSystem source code (https://home.comcast.net/~gscottstanton/) an Atari 7800 emulator. * Greg Stanton for ProSystem source code (https://home.comcast.net/~gscottstanton/) an Atari 7800 emulator.
* zx81 (http://zx81.zx81.free.fr/serendipity_fr/) for PSP A7800 version (that helped me a lot to understand ProSystem). * zx81 (http://zx81.zx81.free.fr/serendipity_fr/) for PSP A7800 version (that helped to understand ProSystem).
* raz0red (http://www.twitchasylum.com/forum/viewtopic.php?t=519) for WII7800 (that helped me a lot to fix some timing problems). * raz0red (http://www.twitchasylum.com/forum/viewtopic.php?t=519) for WII7800 (that helped me to fix some timing problems).
* The folks at AtariAge who helped weed out many of the old ProSystem Maria rendering bugs. * The folks at AtariAge who helped weed out many of the old ProSystem Maria rendering and timing issues.
* The MAXMOD audio library is Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org). See https://github.com/devkitPro/maxmod * The MAXMOD audio library is Copyright (c) 2008, Mukunda Johnson (mukunda@maxmod.org). See https://github.com/devkitPro/maxmod
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -139,6 +200,51 @@ Updates by wavemotion-dave: https://github.com/wavemotion-dave/A7800DS
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
History : History :
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
V5.1 : 27-Feb-2025 by wavemotion-dave
* 7800 BIOS is now supported. Place bios exactly named '7800.rom' in /roms/bios, /data/bios or same directory as the emulator itself.
* New internal database with many cleanups and corrections. Now compliant with Trebor Pro-Pack v8_16 from early 2025.
* New configuration option to allow mapping both both X and Y buttons.
* Maria and CPU cycle counting are much closer to real hardware - all of the DMA Cycle adjustments/hacks have been removed.
* This fixes a number of small problems such as Pole Position II joystick selection of track when game loads.
V5.0 : 21-Feb-2025 by wavemotion-dave
* Kangaroo mode fixed and fully implemented. Cleans up some small graphical glitches on a number of games.
* Composite Artifacting implemented for Tower Toppler and Jinks.
* Sound handler fixed so that drop-outs of sounds are eliminated (or at least greatly minimized).
* The X button on the NDS is now configurable to a range of joystick/console buttons.
V4.9 : 17-Feb-2025 by wavemotion-dave
* High Score (HSC) auto-save improvements - no longer write the backing .hsc file if the write didn't actually change HSC data.
* Improved Sally emulation accuracy and optimization pass to render the games 3-4% faster.
* Improved Pokey emulation - missing sounds on games like Ballblazer are now much better.
* Improved memory emulation for more accurate mirror handling - 7800 Utility cart now shows this as a PASS.
* Improved memory caching to help with the really big games (those 512K or larger).
* All but two games on the DSi are now rendered without any form of frameskip. Older DS-Lite/Phat reduces frameskip due to new optimizations.
V4.8 : 15-Feb-2025 by wavemotion-dave
* High Score (HSC) now auto-saves the .hsc file after it is written by the game. The HSC button is gone.
* Smoother console button operation so that a press is registered more consistently and with better debounce.
* Improved magnifying glass icon debounce so that it registers more consistently and with better debounce.
* New game icon to align with the other emulators in the Atari lineup on the DS.
V4.7 : 11-May-2024 by wavemotion-dave
* X and Y buttons now shift the screen down/up by 16 pixels so you can position the score off-screen and use these to pan up/down to see it.
* Fix for Supercarts so that they start in bank 0 (Legend of Silverpeak should now load)
* Internal database tweaks for the latest homebrews and ports.
* Other minor tweaks and improvements as time permitted.
V4.6 : 06-May-2024 by wavemotion-dave
* Each game that utilizes a High Score Cart (HSC) gets its own 2K .hsc file
* Improved High Score Cart (HSC) emulation - improved initialization of the SRAM contents.
* Sanity checks added so that carts marked as 'NORMAL' (or selected as such) but are larger than 48K will not corrupt memory.
* New magnifying glass icon to ZOOM (and center) the display 1:1 with real 7800 output. This will crop on a DS/DSi since it only has 256x192 but very useful to toggle the 1:1 zoom for High Score Entries, etc.
* Support for .a78 V4 headers (will fall back to V3 if not available)
V4.5 : 18-Nov-2022 by wavemotion-dave
* SNES2Atari adaptor supported.
* Improved display output to smooth over the fonts a bit.
* Small tweaks to internal database to ensure everything looks as good as possible.
V4.4 : 14-Nov-2022 by wavemotion-dave V4.4 : 14-Nov-2022 by wavemotion-dave
* New palette options from the Trebor 'Pro Pack' of colors. COOL, WARM (default), and HOT allow you to shift the color temperature slightly on a per-game basis. * New palette options from the Trebor 'Pro Pack' of colors. COOL, WARM (default), and HOT allow you to shift the color temperature slightly on a per-game basis.
* Improved sound channel mixing so as not to halve the volume when mixing POKEY + TIA. * Improved sound channel mixing so as not to halve the volume when mixing POKEY + TIA.
@ -237,7 +343,7 @@ V2.8 : 02-Nov-2021 by wavemotion-dave
V2.7 : 02-Apr-2021 by wavemotion-dave V2.7 : 02-Apr-2021 by wavemotion-dave
* New support for the latest homebrews: Galaxian final * New support for the latest homebrews: Galaxian final
* Added ability to swap screens using L+R+A * Added ability to swap screens using L+R+A
V2.6 : 02-Apr-2021 by wavemotion-dave V2.6 : 02-Apr-2021 by wavemotion-dave
* Faster directory/file listing. * Faster directory/file listing.
@ -324,86 +430,3 @@ V1.1 : 09-Dec-2020 by wavemotion-dave
V1.0 : 24/05/2011 V1.0 : 24/05/2011
* Initial release based on my a320 version (which is based on Prosystem 1.0.3) * Initial release based on my a320 version (which is based on Prosystem 1.0.3)
* Compiled with last version of Devkitpro/libnds, so DSi compatible \o/ * Compiled with last version of Devkitpro/libnds, so DSi compatible \o/
Compatibility Table (on the faster DSi)
```
GAME MD5SUM HSC FPS NOTES
========================================================================================================================
Ace Of Aces 0be996d25144966d5541c9eb4919b289 NO 60 Near perfect
Alien Brigade 877dcc97a775ed55081864b2dbf5f1e2 NO 60 Near perfect
Arkanoid Demo 212ee2a6e66d8bb7fbf26f343cc8dc19 NO 60 Playable though paddles would be nice.
Asteroids 07342c78619ba6ffcc61c10e907e3b50 YES 60 Near perfect
Asteroids Deluxe a65f79ad4a0bbdecd59d5f7eb3623fd7 YES 60 Near perfect
Astro Blaster 3d38281ed8a8d8c7cd457a18c92c8604 YES 60 Near perfect
Astro Fighter a51e5df28a0fe8c52e9d28fb5f8e44a6 YES 60 Near perfect
Baby Pac Man 7cdfbe37634e7dcd4dc67db7edbcd3ba NO 60 Near perfect
Ballblazer 8fc3a695eaea3984912d98ed4a543376 NO 60 Near perfect
Barnyard Blaster 42682415906c21c6af80e4198403ffda NO 60 Needs Lightgun Support (not implemented)
Basketbrawl f5f6b69c5eb4b55fc163158d1a6b423e NO 60 Near perfect
Beef Drop (Final) 78b1061d651ef806becac1dd3fda29a0 YES 60 Near perfect
BonQ (Final) 9fa7743a016c9b7015ee1d386326f88e YES 60 Near perfect
Centipede 5a09946e57dbe30408a8f253a28d07db YES 60 Near perfect
Choplifter 93e4387864b014c155d7c17877990d1e NO 60 Near perfect
Commando 2e8e28f6ad8b9b9267d518d880c73ebb NO 60 Near perfect
Crack'ed db691469128d9a4217ec7e315930b646 NO 60 Would be better with lightgun support.
Crossbow a94e4560b6ad053a1c24e096f1262ebf NO 60 Would be better with lightgun support.
Dark Chambers 179b76ff729d4849b8f66a502398acae NO 60 Near perfect
Desert Falcon 95ac811c7d27af0032ba090f28c107bd NO 60 Near perfect
Dig Dug 731879ea82fc0ca245e39e036fe293e6 YES 60 Near perfect
Donkey Kong 19f1ee292a23636bd57d408b62de79c7 NO 60 Near perfect
Donkey Kong Jr 5e332fbfc1e0fc74223d2e73271ce650 NO 60 Near perfect
Donkey Kong XM Demo c3107d3e3e17d67e3a11d47a5946a4f3 NO 60 Near perfect
Double Dragon 543484c00ba233736bcaba2da20eeea9 NO 60 Near perfect
Dragon's Descent 94009ccfdcd4f55d24033ca06269ba6a NO 60 Near perfect
Drakers Quest I fab1290f9a4c4f2b4d831c8a57f969f5 NO 60 Near perfect
Drakers Quest II a9f29004412621f20ad9f5c51cc11486 NO 60 Near perfect
Dungeon Stalker b3143adbbb7d7d189e918e5b29d55a72 YES 60 Near perfect
E.X.O Alpha 12 faea27017447dd8bae677d91125a8135 YES 60 Near perfect
F-18 Hornet 2251a6a0f3aec84cc0aff66fc9fa91e8 NO 60 A few small graphical glitches but playable.
Failsafe (homebrew) 6287727ab36391a62f728bbdee88675c YES 60 Near perfect
Fatal Run d25d5d19188e9f149977c49eb0367cd1 NO 60 Near perfect
Food Fight cf76b00244105b8e03cdc37677ec1073 YES 60 Near perfect
Frenzy (with Berzerk) e7d89669a7f92ec2cc99d9663a28671c YES 60 Near perfect
Froggie 6053233cb59c0b4ca633623fd76c4576 YES 60 Near perfect
Galaga fb8d803b328b2e442548f7799cfa9a4a YES 60 Near perfect
Hat Trick fd9e78e201b6baafddfd3e1fbfe6ba31 NO 60 Near perfect
Ikari Warriors c3672482ca93f70eafd9134b936c3feb NO 60 Near perfect
Impossible Mission baebc9246c087e893dfa489632157180 NO 60 Near perfect
Jinks 045fd12050b7f2b842d5970f2414e912 NO 60 Near perfect
Joust f18b3b897a25ab3885b43b4bd141b396 YES 60 Near perfect
Jr. Pac-Man e54edc299e72d22d0ba05d16f3393e8c YES 60 Near perfect
Klax 17b3b764d33eae9b5260f01df7bb9d2f NO 60 Near perfect
Karateka c3a5a8692a423d43d9d28dd5b7d109d9 NO 60 Minor glitches.
Kung Fu Master f57d0af323d4e173fb49ed447f0563d7 NO 60 Near perfect
Mario Bros. 431ca060201ee1f9eb49d44962874049 NO 60 Near perfect
Meteor Shower c3f6201d6a9388e860328c963a3301cc YES 60 Near perfect - my favorite homebrew!
Midnight Mutants bc1e905db1008493a9632aa83ab4682b NO 60 Near perfect
Moon Cresta 9ff38ea62004201d870caa8bd9463525 YES 60 Near perfect
Ms. Pac-Man fc0ea52a9fac557251b65ee680d951e5 YES 60 Near perfect
Ninja Golf 220121f771fc4b98cef97dc040e8d378 NO 60 Near perfect
One On One 74569571a208f8b0b1ccfb22d7c914e1 NO 60 Near perfect
Pac-Man Collection 5013b69cb05b21a1194ce48517df7bfc YES 60 Near perfect
Pac-Man Collection 40th 1330d23ebad9b5ded92ebeacdf305abd YES 60 Near perfect - Damn this is one fine collection!
Planet Smashers 33aea1e2b6634a1dec8c7006d9afda22 NO 60 Near perfect
Plutos 86546808dc60961cdb1b20e761c50ab1 NO 60 Near perfect
Pole Position II 584582bb09ee8122e7fc09dc7d1ed813 NO 60 Near perfect
Popeye Demo 2.9 640c7827fe63082efbaffc13f7983744 YES 60 Near perfect.
Spire of the Ancients 19844117863cd38d4e1e4cbc867ae599 NO 60 Near perfect
Rampage ac03806cef2558fc795a7d5d8dba7bc0 NO 60 Near perfect
Realsports Baseball 383ed9bd1efb9b6cb3388a777678c928 NO 60 Any voice rendering is slow.
Robotron 66ecaafe1b82ae68ffc96267aaf7a4d7 YES 60 Near perfect
Scramble a3a85e507d6f718972b1464ce1aaf8a4 YES 60 Near perfect
Scrapyard Dog 980c35ae9625773a450aa7ef51751c04 NO 60 Near perfect
Serpentine 9bd70c06d3386f76f8162881699a777a YES 60 Near perfect
Space Duel 771cb4609347657f63e6f0eb26036e35 YES 60 Near perfect
Space Invaders 6adf79558a3d7f5beca1bb8d34337417 YES 60 Near perfect
Summer Games cbb0746192540a13b4c7775c7ce2021f NO 60 Plays fine - font hard to read on small screen.
Super Skatebordin 59b5793bece1c80f77b55d60fb39cb94 NO 60 Near perfect
Super Circus Atariage 81cee326b99d6831de10a566e338bd25 YES 60 Near perfect
Time Salvo a60e4b608505d1fb201703b266f754a7 YES 60 Near perfect
UniWarS 79df20ee86a989e669158bcb9d113e8a YES 60 Near perfect
Winter Games 3799d72f78dda2ee87b0ef8bf7b91186 NO 60 Plays fine - font hard to read on small screen.
Xenophobe 05fb699db9eef564e2fe45c568746dbc NO 60 Near perfect
Xevious d7dc17379aa25e5ae3c14b9e780c6f6d YES 60 Near perfect
```

Binary file not shown.

View file

@ -77,8 +77,6 @@ int main() {
installSystemFIFO(); installSystemFIFO();
//installSoundEmuFIFO();
irqSet(IRQ_VCOUNT, VcountHandler); irqSet(IRQ_VCOUNT, VcountHandler);
irqSet(IRQ_VBLANK, VblankHandler); irqSet(IRQ_VBLANK, VblankHandler);

View file

@ -14,10 +14,10 @@ include $(DEVKITARM)/ds_rules
# DATA is a list of directories containing binary files # DATA is a list of directories containing binary files
# all directories are relative to this makefile # all directories are relative to this makefile
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
BUILD := build BUILD := build
SOURCES := source/emu source SOURCES := source/emu source
INCLUDES := source/emu source include INCLUDES := source/emu source include
DATA := data DATA := data
GRAPHICS := gfx GRAPHICS := gfx
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -26,17 +26,14 @@ GRAPHICS := gfx
#ARCH := -mthumb -mthumb-interwork #ARCH := -mthumb -mthumb-interwork
ARCH := ARCH :=
#CFLAGS := -g -Wall -O2 -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math $(ARCH)
CFLAGS := -Wall -Ofast -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math -fsigned-char $(ARCH) CFLAGS := -Wall -Ofast -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math -fsigned-char $(ARCH)
CFLAGS += $(INCLUDE) -DARM9 CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
#ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s ASFLAGS := $(ARCH) -march=armv5te -mtune=arm946e-s
ASFLAGS := $(ARCH) -march=armv5te -mtune=arm946e-s
#LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=ds_arm9.specs $(ARCH) -Wl,-Map,$(notdir $*.map)
LDFLAGS = -specs=ds_arm9.specs $(ARCH) -Wl,-Map,$(notdir $*.map)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project # any extra libraries we wish to link with the project
@ -56,7 +53,7 @@ LIBDIRS := $(LIBNDS)
ifneq ($(BUILD),$(notdir $(CURDIR))) ifneq ($(BUILD),$(notdir $(CURDIR)))
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
export ARM9ELF := $(CURDIR)/$(TARGET).elf export ARM9ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD) export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
@ -123,13 +120,8 @@ $(ARM9ELF) : $(OFILES)
@$(bin2o) @$(bin2o)
%.s %.h : %.png %.s %.h : %.png
# grit $< -fts -W3 -gT! -gzl -gB16 -gb -o$*
grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl grit $^ -o $@ -gt -mrt -mR8 -mLs -gzl -mzl
#%.gif.o : %.gif
# @echo $(notdir $<)
# @$(bin2o)
%.wav.o : %.wav %.wav.o : %.wav
@echo $(notdir $<) @echo $(notdir $<)
@$(bin2o) @$(bin2o)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,29 @@
// =====================================================================================
// Copyright (c) 2022-2024 Dave Bernazzani (wavemotion-dave)
//
// Copying and distribution of this emulator, it's source code and associated
// readme files, with or without modification, are permitted in any medium without
// royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition),
// Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely.
//
// A7800DS emulator is offered as-is, without any warranty.
//
// The original GPL license:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// =====================================================================================
#ifndef _A7800UTILS_H #ifndef _A7800UTILS_H
#define _A7800UTILS_H #define _A7800UTILS_H
@ -31,16 +57,10 @@ typedef struct {
extern gamecfg GameConf; extern gamecfg GameConf;
extern uint video_height; // Actual video height
extern unsigned short *bufVideo; // Video buffer extern unsigned short *bufVideo; // Video buffer
extern void FadeToColor(unsigned char ucSens, unsigned short ucBG, unsigned char ucScr, unsigned char valEnd, unsigned char uWait); extern void FadeToColor(unsigned char ucSens, unsigned short ucBG, unsigned char ucScr, unsigned char valEnd, unsigned char uWait);
extern unsigned long crc32 (unsigned int crc, const unsigned char *buf, unsigned int len);
extern void vblankIntr(); extern void vblankIntr();
extern void dsInitScreenMain(void); extern void dsInitScreenMain(void);
extern void dsInitTimer(void); extern void dsInitTimer(void);
extern void dsShowScreenEmu(void); extern void dsShowScreenEmu(void);

View file

@ -1,9 +1,29 @@
// ===================================================================================================== // =====================================================================================
// Stella DSi - Improved Version by Dave Bernazzani (wavemotion) // Copyright (c) 2022-2025 Dave Bernazzani (wavemotion-dave)
// //
// See readme.txt for a list of everything that has changed in the baseline 1.0 code. // Copying and distribution of this emulator, it's source code and associated
// ===================================================================================================== // readme files, with or without modification, are permitted in any medium without
// royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition),
// Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely.
//
// A7800DS emulator is offered as-is, without any warranty.
//
// The original GPL license:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// =====================================================================================
#include <nds.h> #include <nds.h>
#include <stdio.h> #include <stdio.h>
#include <fat.h> #include <fat.h>
@ -26,6 +46,8 @@ static int display_options_list(bool bFullDisplay);
extern void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr); extern void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr);
extern void dsShowScreenMain(bool); extern void dsShowScreenMain(bool);
char strBuf[35];
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Write out the A7800DS.DAT configuration file to capture the settings for // Write out the A7800DS.DAT configuration file to capture the settings for
// each game. We can store more than 350 game settings! // each game. We can store more than 350 game settings!
@ -43,11 +65,11 @@ void SaveConfig(bool bShow)
// Find the slot we should save into... // Find the slot we should save into...
for (slot=0; slot<MAX_CONFIGS; slot++) for (slot=0; slot<MAX_CONFIGS; slot++)
{ {
if (strcmp(allConfigs.cart[slot].digest, myCartInfo.digest) == 0) // Got a match?! if (strncmp(allConfigs.cart[slot].half_digest, myCartInfo.half_digest, 16) == 0) // Got a match?!
{ {
break; break;
} }
if (strcmp(allConfigs.cart[slot].digest, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") == 0) // Didn't find it... use a blank slot... if (strcmp(allConfigs.cart[slot].half_digest, "xxxxxxxxxxxxxxxx") == 0) // Didn't find it... use a blank slot...
{ {
break; break;
} }
@ -116,8 +138,8 @@ static void SetDefaultGameConfig(void)
// Init the entire database // Init the entire database
for (int slot=0; slot<MAX_CONFIGS; slot++) for (int slot=0; slot<MAX_CONFIGS; slot++)
{ {
strcpy(allConfigs.cart[slot].digest, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); strcpy(allConfigs.cart[slot].half_digest, "xxxxxxxxxxxxxxxx");
// TBD - do more. // TBD - do more?
} }
} }
@ -178,23 +200,25 @@ const struct options_t Game_Option_Table[] =
{"HIGHSCORE", 0, {"DISABLED", "ENABLED"}, &myCartInfo.hsc, 2}, {"HIGHSCORE", 0, {"DISABLED", "ENABLED"}, &myCartInfo.hsc, 2},
{"FRAMESKIP", 0, {"DISABLED", "MEDIUM 3/4", "HIGH 1/2"}, &myCartInfo.frameSkip, 3}, {"FRAMESKIP", 0, {"DISABLED", "MEDIUM 3/4", "HIGH 1/2"}, &myCartInfo.frameSkip, 3},
{"POKEY", 0, {"NONE", "AT 4000", "AT 450", "AT 800"}, &myCartInfo.pokeyType, 4}, {"POKEY", 0, {"NONE", "AT 4000", "AT 450", "AT 800"}, &myCartInfo.pokeyType, 4},
{"DMA ADJUST", 2, {"-25","+50"}, (u8*)&myCartInfo.dma_adjust, 2},
{"LEFT DIFF", 0, {"A", "B"}, &myCartInfo.diff1, 2}, {"LEFT DIFF", 0, {"A", "B"}, &myCartInfo.diff1, 2},
{"RIGHT DIFF", 0, {"A", "B"}, &myCartInfo.diff2, 2}, {"RIGHT DIFF", 0, {"A", "B"}, &myCartInfo.diff2, 2},
{"PALETTE", 0, {"CRT V2 COOL", "CRT V2 WARM", "CRT V2 HOT"}, &myCartInfo.palette, 3}, {"PALETTE", 0, {"CRT V2 COOL", "CRT V2 WARM", "CRT V2 HOT"}, &myCartInfo.palette, 3},
{"LEFT JOY", 0, {"NONE", "JOYSTICK", "LIGHTGUN", "PADDLES", "TWIN STICKS", "SOTA"}, &myCartInfo.cardctrl1, 6}, {"LEFT JOY", 0, {"NONE", "JOYSTICK", "LIGHTGUN", "PADDLES", "TWIN STICKS", "SOTA", "SNES2ATARI"}, &myCartInfo.cardctrl1, 7},
{"RIGHT JOY", 0, {"NONE", "JOYSTICK", "LIGHTGUN", "PADDLES", "TWIN STICKS", "SOTA"}, &myCartInfo.cardctrl2, 6}, {"RIGHT JOY", 0, {"NONE", "JOYSTICK", "LIGHTGUN", "PADDLES", "TWIN STICKS", "SOTA", "SNES2ATARI"}, &myCartInfo.cardctrl2, 7},
{"X BUTTON", 0, {"DEFAULT", "PAN UP", "PAN DOWN", "JOY UP", "JOY DOWN", "JOY LEFT", "JOY RIGHT", "JOY B1", "JOY B2", "CONSOLE PAUSE"}, &myCartInfo.xButton, 10},
{"Y BUTTON", 0, {"DEFAULT", "PAN UP", "PAN DOWN", "JOY UP", "JOY DOWN", "JOY LEFT", "JOY RIGHT", "JOY B1", "JOY B2", "CONSOLE PAUSE"}, &myCartInfo.yButton, 10},
{"X OFFSET", 2, {"-50", "+50"}, (u8*)&myCartInfo.xOffset, 2}, {"X OFFSET", 2, {"-50", "+50"}, (u8*)&myCartInfo.xOffset, 2},
{"Y OFFSET", 2, {"-50", "+50"}, (u8*)&myCartInfo.yOffset, 2}, {"Y OFFSET", 2, {"-50", "+50"}, (u8*)&myCartInfo.yOffset, 2},
{"X SCALE", 2, {"+200", "+320"}, (u8*)&myCartInfo.xScale, 2}, {"X SCALE", 2, {"+200", "+320"}, (u8*)&myCartInfo.xScale, 2},
{"Y SCALE", 2, {"+180", "+256"}, (u8*)&myCartInfo.yScale, 2}, {"Y SCALE", 2, {"+180", "+234"}, (u8*)&myCartInfo.yScale, 2},
{"X JIGGLE", 1, {"+1", "+256"}, (u8*)&myCartInfo.xJiggle, 2},
{"Y JIGGLE", 1, {"+1", "+256"}, (u8*)&myCartInfo.yJiggle, 2},
{NULL, 0, {"", ""}, NULL, 1}, {NULL, 0, {"", ""}, NULL, 1},
}; };
void display_line(u8 idx, u8 highlight) void display_line(u8 idx, u8 highlight)
{ {
char strBuf[35];
if (Game_Option_Table[idx].isNumeric == 1) // Unsigned 8 bit if (Game_Option_Table[idx].isNumeric == 1) // Unsigned 8 bit
{ {
sprintf(strBuf, " %-11s : %-15d", Game_Option_Table[idx].label, *(Game_Option_Table[idx].option_val)); sprintf(strBuf, " %-11s : %-15d", Game_Option_Table[idx].label, *(Game_Option_Table[idx].option_val));

View file

@ -1,8 +1,29 @@
// ===================================================================================================== // =====================================================================================
// Stella DSi - Improved Version by Dave Bernazzani (wavemotion) // Copyright (c) 2022-2024 Dave Bernazzani (wavemotion-dave)
// //
// See readme.txt for a list of everything that has changed in the baseline 1.0 code. // Copying and distribution of this emulator, it's source code and associated
// ===================================================================================================== // readme files, with or without modification, are permitted in any medium without
// royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition),
// Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely.
//
// A7800DS emulator is offered as-is, without any warranty.
//
// The original GPL license:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// =====================================================================================
#ifndef __CONFIG_H #ifndef __CONFIG_H
#define __CONFIG_H #define __CONFIG_H
@ -13,13 +34,28 @@
// --------------------------- // ---------------------------
// Config handling... // Config handling...
// --------------------------- // ---------------------------
#define CONFIG_VER 0x0007 #define CONFIG_VER 0x000B
#define MAX_CONFIGS 700 #define MAX_CONFIGS 640
struct AllConfig_t struct AllConfig_t
{ {
u16 config_ver; u16 config_ver;
u8 global_buttonAB;
u8 global_showBios;
u8 global_spare1;
u8 global_spare2;
u8 global_spare3;
u8 global_spare4;
u8 global_spare5;
u8 global_spare6;
u8 global_spare7;
u8 global_spare10;
u8 global_spare11;
u8 global_spare12;
u8 global_spare13;
u8 global_spare14;
u8 global_unused[512];
Database_Entry cart[MAX_CONFIGS]; Database_Entry cart[MAX_CONFIGS];
u32 crc32; u32 crc32;
}; };

View file

@ -29,10 +29,18 @@
char cartridge_title[256]; char cartridge_title[256];
byte cartridge_digest[256]; byte cartridge_digest[256];
byte bios_digest[256];
char cartridge_filename[256]; char cartridge_filename[256];
byte header[128] = {0}; // We might have a header... this will buffer it
word cardtype = 0x0000; word cardtype = 0x0000;
bool write_only_pokey_at_4000 = false; u8 write_only_pokey_at_4000 __attribute__((section(".dtcm"))) = false;
u8 use_composite_filtering __attribute__((section(".dtcm"))) = 0;
u8 bios_show_counter __attribute__((section(".dtcm"))) = 0;
u8 bios_available __attribute__((section(".dtcm"))) = 0;
u8 cart_restore = 0; // After the cart is stored, we set this flag to indicate a swap will restore only the top 4K
u8 cart_restore_buffer[0x1000]; // Used to store the cart space that coincides with the BIOS data at the top 4K
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// We allow cart sizes up to 1024K which is pretty huge - I've not seen any ROMs bigger than this. // We allow cart sizes up to 1024K which is pretty huge - I've not seen any ROMs bigger than this.
@ -92,9 +100,9 @@ ITCM_CODE void cartridge_WriteBank(word address, byte bank)
if (last_ex_ram_bank != ex_ram_bank) if (last_ex_ram_bank != ex_ram_bank)
{ {
u32 *src = ex_ram_bank ? (u32*)0x06830000 : (u32*)0x06834000; // Only for the DSi.. see DS_LITE handling below u32 *src = ex_ram_bank ? (u32*)0x06838000 : (u32*)0x0683C000; // Only for the DSi.. see DS_LITE handling below
u32 *dest = (u32*)(memory_ram+0x4000); u32 *dest = (u32*)(memory_ram+0x4000);
shadow_ram = ex_ram_bank ? (u8*)0x06830000 : (u8*)0x06834000; // // Only for the DSi.. see DS_LITE handling below shadow_ram = ex_ram_bank ? (u8*)0x06838000 : (u8*)0x0683C000; // Only for the DSi.. see DS_LITE handling below
if (isDS_LITE) // Unfortunately non DSi can't write 8-bit values to LCD RAM... so we have to do this the slow way if (isDS_LITE) // Unfortunately non DSi can't write 8-bit values to LCD RAM... so we have to do this the slow way
{ {
@ -121,21 +129,23 @@ ITCM_CODE void cartridge_WriteBank(word address, byte bank)
uint offset = bank * 16384; uint offset = bank * 16384;
if (offset < (272*1024)) // If we are in fast VRAM memory if (offset < (272*1024)) // If we are in fast VRAM memory
memory_WriteROMFast(address, (16384/(4*4)), (u32*)(0x06860000 + offset)); memory_WriteROMFast(address, (16384/(8*4)), (u32*)(0x06860000 + offset));
else if (offset < (368*1024)) // If we are in fast VRAM memory (we have 96K more here)
memory_WriteROMFast(address, (16384/(8*4)), (u32*)(0x06820000 + offset-(272*1024)));
else // Normal memory - a little slower but that's the best we can do... else // Normal memory - a little slower but that's the best we can do...
memory_WriteROMFast(address, (16384/(4*4)), (u32*)(cartridge_buffer + offset)); memory_WriteROMFast(address, (16384/(8*4)), (u32*)(cartridge_buffer + offset));
} }
} }
ITCM_CODE void cartridge_SwapRAM_DragonFlyStyle(u8 data) void cartridge_SwapRAM_DragonFlyStyle(u8 data)
{ {
ex_ram_bank_df = data & 0x01; ex_ram_bank_df = data & 0x01;
if (last_ex_ram_bank_df != ex_ram_bank_df) if (last_ex_ram_bank_df != ex_ram_bank_df)
{ {
u32 *src = ex_ram_bank_df ? (u32*)0x06830000 : (u32*)0x06834000; // Only for the DSi.. see DS_LITE handling below u32 *src = ex_ram_bank_df ? (u32*)0x06838000 : (u32*)0x0683C000; // Only for the DSi.. see DS_LITE handling below
u32 *dest = (u32*)(memory_ram+0x4000); u32 *dest = (u32*)(memory_ram+0x4000);
shadow_ram = ex_ram_bank_df ? (u8*)0x06830000 : (u8*)0x06834000; // Only for the DSi.. see DS_LITE handling below shadow_ram = ex_ram_bank_df ? (u8*)0x06838000 : (u8*)0x0683C000; // Only for the DSi.. see DS_LITE handling below
if (isDS_LITE) // Unfortunately non DSi can't write 8-bit values to LCD RAM... so we have to do this the slow way if (isDS_LITE) // Unfortunately non DSi can't write 8-bit values to LCD RAM... so we have to do this the slow way
{ {
@ -158,10 +168,21 @@ ITCM_CODE void cartridge_SwapRAM_DragonFlyStyle(u8 data)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ReadHeader // ReadHeader
// Atari 7800 carts come in two major flavors... .bin is just the flat
// binary of the game ROM and does not contain any information about
// what kind of cartridge it is - we can only guess by using the internal
// database and/or the size of the ROM (i.e. 128K ROMs are usually SUPERCART).
//
// But .a78 files add a 128 byte header at the front of the game ROM and give
// information about what kind of ROM mapper is used, sound options, controller
// options, etc. And there are multiple versions of the header - we currently
// support the long-standing V3 header and the newer (2023) V4 specifications.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
char temp[33] = {0};
static void cartridge_ReadHeader(const byte* header) { static void cartridge_ReadHeader(const byte* header) {
uint index; uint index;
char temp[33] = {0};
memset(temp, 0x00, sizeof(temp));
for(index = 0; index < 32; index++) { for(index = 0; index < 32; index++) {
temp[index] = header[index + 17]; temp[index] = header[index + 17];
} }
@ -180,7 +201,7 @@ static void cartridge_ReadHeader(const byte* header) {
// bit 2 = ram at $4000 // bit 2 = ram at $4000
// bit 3 = rom at $4000 // bit 3 = rom at $4000
// bit 4 = second to last bank at $4000 // bit 4 = second to last bank at $4000 (EXFIX)
// bit 5 = banked ram // bit 5 = banked ram
// bit 6 = pokey at $450 // bit 6 = pokey at $450
// bit 7 = mirror ram at $4000 // bit 7 = mirror ram at $4000
@ -201,8 +222,8 @@ static void cartridge_ReadHeader(const byte* header) {
if (cardtype & 0x0002) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART; if (cardtype & 0x0002) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART;
if (cardtype & 0x0004) myCartInfo.cardtype = ((cardtype & 0x0002) ? CARTRIDGE_TYPE_SUPERCART_RAM : CARTRIDGE_TYPE_FLAT_WITH_RAM); if (cardtype & 0x0004) myCartInfo.cardtype = ((cardtype & 0x0002) ? CARTRIDGE_TYPE_SUPERCART_RAM : CARTRIDGE_TYPE_FLAT_WITH_RAM);
if (cardtype & 0x0008) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_LARGE; if (cardtype & 0x0008) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_LARGE; // EXROM
if (cardtype & 0x0010) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_ROM; if (cardtype & 0x0010) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_ROM; // EXFIX
if (cardtype & 0x0020) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_RAMX2; if (cardtype & 0x0020) myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_RAMX2;
if (cardtype & 0x0100) myCartInfo.cardtype = CARTRIDGE_TYPE_ACTIVISION; if (cardtype & 0x0100) myCartInfo.cardtype = CARTRIDGE_TYPE_ACTIVISION;
if (cardtype & 0x0200) myCartInfo.cardtype = CARTRIDGE_TYPE_ABSOLUTE; if (cardtype & 0x0200) myCartInfo.cardtype = CARTRIDGE_TYPE_ABSOLUTE;
@ -216,20 +237,179 @@ static void cartridge_ReadHeader(const byte* header) {
if (cardtype & 0x0040) myCartInfo.pokeyType = POKEY_AT_450; if (cardtype & 0x0040) myCartInfo.pokeyType = POKEY_AT_450;
if (cardtype & 0x8000) myCartInfo.pokeyType = POKEY_AT_800; if (cardtype & 0x8000) myCartInfo.pokeyType = POKEY_AT_800;
if (cardtype & 0x0001) myCartInfo.pokeyType = POKEY_AT_4000; if (cardtype & 0x0001) myCartInfo.pokeyType = POKEY_AT_4000;
// ========================
// 0 = none
// 1 = 7800 joystick
// 2 = lightgun
// 3 = paddle
// 4 = trakball
// 5 = 2600 joystick
// 6 = 2600 driving
// 7 = 2600 keypad
// 8 = ST mouse
// 9 = Amiga mouse
// 10 = AtariVox/SaveKey
// 11 = SNES2Atari
// ========================
switch(header[55])
{
case 1:
myCartInfo.cardctrl1 = CARTRIDGE_CONTROLLER_JOYSTICK;
break;
case 2:
myCartInfo.cardctrl1 = CARTRIDGE_CONTROLLER_LIGHTGUN;
break;
case 3:
myCartInfo.cardctrl1 = CARTRIDGE_CONTROLLER_PADDLES;
break;
case 11:
myCartInfo.cardctrl1 = CARTRIDGE_CONTROLLER_SNES2ATARI;
break;
default:
myCartInfo.cardctrl1 = CARTRIDGE_CONTROLLER_JOYSTICK;
break;
}
switch(header[56])
{
case 1:
myCartInfo.cardctrl2 = CARTRIDGE_CONTROLLER_JOYSTICK;
break;
case 2:
myCartInfo.cardctrl2 = CARTRIDGE_CONTROLLER_LIGHTGUN;
break;
case 3:
myCartInfo.cardctrl2 = CARTRIDGE_CONTROLLER_PADDLES;
break;
case 11:
myCartInfo.cardctrl2 = CARTRIDGE_CONTROLLER_SNES2ATARI;
break;
default:
myCartInfo.cardctrl2 = CARTRIDGE_CONTROLLER_JOYSTICK;
break;
}
myCartInfo.cardctrl1 = header[55]; // Region:
myCartInfo.cardctrl2 = header[56]; // bit 0 : 0=NTSC, 1=PAL
// bit 1 : 0=component, 1=composite
// bit 2 : 0=single region, 1=multi-region
myCartInfo.region = header[57] & 1; myCartInfo.region = header[57] & 1;
myCartInfo.hsc = (header[58]&1 ? HSC_YES:HSC_NO); //use_composite_filtering = header[57] & 2;
myCartInfo.dma_adjust = 0; use_composite_filtering = 0;
last_bank = 255;
last_ex_ram_bank = 0; // High Score Support
ex_ram_bank = 0; // bit 0 : HSC
last_ex_ram_bank_df = 0; // bit 1 : AtariVox/SaveKey
write_only_pokey_at_4000 = false; myCartInfo.hsc = (header[58]&1 ? HSC_YES:HSC_NO);
ex_ram_bank_df = 0;
// ----------------------------------------------------------------------------
// Now check for the V4 extended header which overrides anything done above...
// The V4 header is mainly for mappers and audio support. Everything else
// is handled by the fields in V3 above.
// ----------------------------------------------------------------------------
if (header[0] == 0x04)
{
// Mapper:
// 0 = Linear
// 1 = SuperGame
// 2 = Activision
// 3 = Absolute
// 4 = Souper
switch (header[64])
{
case 0: // Linear
// Mapper Options:
// bit 7 : Bankset ROM
// bits 0-1 : Option at @4000 (0=none, 1=16K RAM, 2=8K EXRAM/A8, 3=32K EXRAM/M2)
if (header[65] & 0x80)
{
switch (header[65] & 0x03)
{
case 0: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS; break;
case 1: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS_RAM; break;
case 2: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS; break;
case 3: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS_HALTRAM; break;
}
}
else
{
switch (header[65] & 0x03)
{
case 0: myCartInfo.cardtype = CARTRIDGE_TYPE_NORMAL; break;
case 1: myCartInfo.cardtype = CARTRIDGE_TYPE_FLAT_WITH_RAM; break;
case 2: myCartInfo.cardtype = CARTRIDGE_TYPE_FRACTALUS; break;
case 3: break; // There is no 32K of EXRAM/M2 for non Bankset (not supported - don't change anything detected by V3 header)
}
}
break;
case 1: // Super Game
// Mapper Options:
// bit 7 : Bankset ROM
// bits 0-2 : Option at @4000 (0=none, 1=16K RAM, 2=8K EXRAM/A8, 3=32K EXRAM/M2, 4=EXROM, 5=EXFIX, 6=32k EXRAM/X2)
if (header[65] & 0x80)
{
switch (header[65] & 0x07)
{
case 0: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS; break; // No option
case 1: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS_RAM; break; // 16K RAM
case 2: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS; break; // 8K EXRAM/A8
case 3: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS_HALTRAM; break; // 32K EXRAM/M2
case 6: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS_HALTRAM; break; // 32K EXRAM/X2
default: myCartInfo.cardtype = CARTRIDGE_TYPE_BANKSETS; break; // Best we can do - mapper option doesn't make sense to us
}
}
else
{
switch (header[65] & 0x07)
{
case 0: myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART; break; // No option
case 1: myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_RAM; break; // 16K RAM
case 2: break; // 8K EXRAM/A8 (not supported - don't change anything detected by V3 header)
case 3: myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_RAMX2; break; // 32K EXRAM/M2
case 4: myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_LARGE; break; // EXROM
case 5: myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_ROM; break; // EXFIX
case 6: myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_RAMX2; break; // 32K EXRAM/X2
default: break; // Unused (not supported - don't change anything detected by V3 header)
}
}
break;
case 2: // Activision
myCartInfo.cardtype = CARTRIDGE_TYPE_ACTIVISION;
break;
case 3: // Absolute
myCartInfo.cardtype = CARTRIDGE_TYPE_ABSOLUTE;
break;
case 4: // Souper (Ricky & Vicky)
myCartInfo.cardtype = CARTRIDGE_TYPE_SUPERCART_RAM; // TODO: This is wrong. Should be SOUPER but not yet supported.
break;
}
// v4+ audio:
// bit 5 : ADPCM Audio Stream @420
// bit 4 : COVOX @430
// bit 3 : YM2151 @460
// bits 0-2 : POKEY (0=none, 1=@440, 2=@450, 3=@450+@440, 4=@800, 5=@4000)
switch (header[67] & 0x07)
{
case 0: myCartInfo.pokeyType = POKEY_NONE; break;
case 1: myCartInfo.pokeyType = POKEY_NONE; break; // Pokey at 440 not supported
case 2: myCartInfo.pokeyType = POKEY_AT_450; break;
case 3: myCartInfo.pokeyType = POKEY_AT_450; break; // Dual pokey not supported
case 4: myCartInfo.pokeyType = POKEY_AT_800; break;
case 5: myCartInfo.pokeyType = POKEY_AT_4000; break;
}
}
last_bank = 255;
last_ex_ram_bank = 0;
ex_ram_bank = 0;
last_ex_ram_bank_df = 0;
write_only_pokey_at_4000 = false;
ex_ram_bank_df = 0;
if (isDS_LITE) shadow_ram = ex_ram_bank ? (u8*)(ex_ram_buffer+0x0000) : (u8*)(ex_ram_buffer+0x4000); // for DS-Lite if (isDS_LITE) shadow_ram = ex_ram_bank ? (u8*)(ex_ram_buffer+0x0000) : (u8*)(ex_ram_buffer+0x4000); // for DS-Lite
else shadow_ram = ex_ram_bank ? (u8*)0x06830000 : (u8*)0x06834000; // // Only for the DSi.. see DS_LITE handling above else shadow_ram = ex_ram_bank ? (u8*)0x06838000 : (u8*)0x0683C000; // // Only for the DSi.. see DS_LITE handling above
shadow_ram -= 0x4000; // Makes for faster indexing in Memory.c shadow_ram -= 0x4000; // Makes for faster indexing in Memory.c
} }
@ -250,7 +430,6 @@ static bool _cartridge_Load(uint size)
memset(banksets_memory, 0xFF, sizeof(banksets_memory)); // Clear the banksets ROM memory area memset(banksets_memory, 0xFF, sizeof(banksets_memory)); // Clear the banksets ROM memory area
memset(banksets_memory+0x4000, 0x00, 0x4000); // Clear banksets RAM memory area memset(banksets_memory+0x4000, 0x00, 0x4000); // Clear banksets RAM memory area
static byte header[128] = {0}; // We might have a header... this will buffer it
for(index = 0; index < 128; index++) for(index = 0; index < 128; index++)
{ {
header[index] = cartridge_buffer[index]; header[index] = cartridge_buffer[index];
@ -302,13 +481,19 @@ static bool _cartridge_Load(uint size)
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
u32 *fast_mem = (u32*)0x06860000; u32 *fast_mem = (u32*)0x06860000;
memcpy(fast_mem, cartridge_buffer, (272 * 1024)); memcpy(fast_mem, cartridge_buffer, (272 * 1024));
// -----------------------------------------------------------------
// And another 96K bytes of cart into this VRAM area. This gives us
// 272K above + 96K here = 368K of cache to help with memory copy.
// -----------------------------------------------------------------
fast_mem = (u32*)0x06820000;
memcpy(fast_mem, cartridge_buffer + (272 * 1024), (96 * 1024));
memset((u8*)0x06830000, 0x00, (64*1024)); // Clear this 128K chunk of fast VRAM as we use it for RAM bankswitch memset((u8*)0x06838000, 0x00, (32*1024)); // Clear this 32K chunk of fast VRAM as we use it for additional 2X RAM bankswitch (two banks of 16K)
hash_Compute(cartridge_buffer, cartridge_size, cartridge_digest); hash_Compute(cartridge_buffer, cartridge_size, cartridge_digest);
return true; return true;
} }
extern unsigned long crc32 (unsigned int crc, const unsigned char *buf, unsigned int len);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Load // Load
@ -341,6 +526,9 @@ bool cartridge_Load(char *filename)
if(!_cartridge_Load(size)) return false; if(!_cartridge_Load(size)) return false;
strcpy(cartridge_filename, filename); strcpy(cartridge_filename, filename);
cart_restore = 0;
return true; return true;
} }
@ -367,21 +555,30 @@ bool cartridge_Load(char *filename)
void cartridge_Store( ) void cartridge_Store( )
{ {
uint offset, lastBank; uint offset, lastBank;
if (cart_restore)
{
memory_WriteROM(0xF000, 0x1000, cart_restore_buffer);
return;
}
cart_restore = 1;
switch(myCartInfo.cardtype) switch(myCartInfo.cardtype)
{ {
case CARTRIDGE_TYPE_NORMAL: case CARTRIDGE_TYPE_NORMAL:
memory_WriteROM(65536 - cartridge_size, cartridge_size, cartridge_buffer); if (cartridge_size <= 49152) memory_WriteROM(65536 - cartridge_size, cartridge_size, cartridge_buffer);
break; break;
case CARTRIDGE_TYPE_FLAT_WITH_RAM: case CARTRIDGE_TYPE_FLAT_WITH_RAM:
memory_WriteROM(65536 - cartridge_size, cartridge_size, cartridge_buffer); if (cartridge_size <= 49152) memory_WriteROM(65536 - cartridge_size, cartridge_size, cartridge_buffer);
memory_ClearROM(16384, 16384); memory_ClearROM(16384, 16384);
break; break;
case CARTRIDGE_TYPE_SUPERCART: case CARTRIDGE_TYPE_SUPERCART:
offset = cartridge_size - 16384; offset = cartridge_size - 16384;
memory_WriteROM(49152, 16384, cartridge_buffer + offset); memory_WriteROM(49152, 16384, cartridge_buffer + offset);
memory_WriteROM(32768, 16384, cartridge_buffer + cartridge_GetBankOffset(0));
break; break;
case CARTRIDGE_TYPE_SUPERCART_LARGE: case CARTRIDGE_TYPE_SUPERCART_LARGE:
@ -423,7 +620,7 @@ void cartridge_Store( )
break; break;
case CARTRIDGE_TYPE_FRACTALUS: case CARTRIDGE_TYPE_FRACTALUS:
memory_WriteROM(65536 - cartridge_size, cartridge_size, cartridge_buffer); if (cartridge_size <= 49152) memory_WriteROM(65536 - cartridge_size, cartridge_size, cartridge_buffer);
memory_ClearROM(0x4000, 0x4000); memory_ClearROM(0x4000, 0x4000);
break; break;
@ -460,12 +657,16 @@ void cartridge_Store( )
memset(&banksets_memory[0x4000], 0x00, 0x4000); memset(&banksets_memory[0x4000], 0x00, 0x4000);
break; break;
} }
// Save off the top 4K in case BIOS is swapped in...
memcpy(cart_restore_buffer, memory_ram + 0xF000, 0x1000);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Cart Write - this may cause a bankswitch // Cart Write - this may cause a bankswitch
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void cartridge_Write(word address, byte data) { ITCM_CODE void cartridge_Write(word address, byte data)
{
switch(myCartInfo.cardtype) switch(myCartInfo.cardtype)
{ {
case CARTRIDGE_TYPE_SUPERCART: case CARTRIDGE_TYPE_SUPERCART:
@ -487,12 +688,12 @@ ITCM_CODE void cartridge_Write(word address, byte data) {
break; break;
case CARTRIDGE_TYPE_ABSOLUTE: case CARTRIDGE_TYPE_ABSOLUTE:
if(address == 32768 && (data == 1 || data == 2)) if ((address == 0x8000) && (data == 1 || data == 2))
{ {
cartridge_WriteBank(16384, data-1); cartridge_WriteBank(16384, data-1);
} }
break; break;
case CARTRIDGE_TYPE_ACTIVISION: case CARTRIDGE_TYPE_ACTIVISION:
if(address >= 65408) if(address >= 65408)
{ {
@ -540,11 +741,11 @@ ITCM_CODE void cartridge_Write(word address, byte data) {
// IsLoaded // IsLoaded
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
bool cartridge_IsLoaded( ) { bool cartridge_IsLoaded( ) {
return (cartridge_buffer != NULL)? true: false; return (cartridge_size > 0) ? true:false;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Release // Release cart, save SRAM if used, set common vars back to system default.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void cartridge_Release( ) void cartridge_Release( )
{ {
@ -555,21 +756,61 @@ void cartridge_Release( )
// These values need to be reset so that moving between carts works // These values need to be reset so that moving between carts works
// consistently. This seems to be a ProSystem emulator bug. // consistently. This seems to be a ProSystem emulator bug.
// //
myCartInfo.cardtype = CT_NORMAL; myCartInfo.cardtype = CT_NORMAL;
myCartInfo.region = NTSC; myCartInfo.region = NTSC;
myCartInfo.pokeyType = POKEY_NONE; myCartInfo.pokeyType = POKEY_NONE;
myCartInfo.hsc = false; myCartInfo.hsc = false;
myCartInfo.cardctrl1 = 0; myCartInfo.cardctrl1 = 0;
myCartInfo.cardctrl2 = 0; myCartInfo.cardctrl2 = 0;
myCartInfo.hasHeader = false; myCartInfo.hasHeader = false;
myCartInfo.dma_adjust = 0; myCartInfo.biosTimeout = 160;
last_bank = 255; last_bank = 255;
last_ex_ram_bank = 0; last_ex_ram_bank = 0;
ex_ram_bank = 0; ex_ram_bank = 0;
last_ex_ram_bank_df = 0; last_ex_ram_bank_df = 0;
ex_ram_bank_df = 0; ex_ram_bank_df = 0;
write_only_pokey_at_4000 = false; use_composite_filtering = 0;
cart_restore = 0;
write_only_pokey_at_4000 = false;
if (isDS_LITE) shadow_ram = ex_ram_bank ? (u8*)(ex_ram_buffer+0x0000) : (u8*)(ex_ram_buffer+0x4000); // for DS-Lite if (isDS_LITE) shadow_ram = ex_ram_bank ? (u8*)(ex_ram_buffer+0x0000) : (u8*)(ex_ram_buffer+0x4000); // for DS-Lite
else shadow_ram = ex_ram_bank ? (u8*)0x06830000 : (u8*)0x06834000; // // Only for the DSi.. see DS_LITE handling above else shadow_ram = ex_ram_bank ? (u8*)0x06838000 : (u8*)0x0683C000; // // Only for the DSi.. see DS_LITE handling above
shadow_ram -= 0x4000; // Makes for faster indexing in Memory.c shadow_ram -= 0x4000; // Makes for faster indexing in Memory.c
} }
static unsigned char bios_data[0x1000];
void bios_check_and_load(void)
{
bios_available = 0;
FILE *romfile = fopen("7800.rom", "rb");
if (romfile == NULL) romfile = fopen("/roms/bios/7800.rom", "rb");
if (romfile == NULL) romfile = fopen("/data/bios/7800.rom", "rb");
if (romfile == NULL) romfile = fopen("7800.bin", "rb");
if (romfile == NULL) romfile = fopen("/roms/bios/7800.bin", "rb");
if (romfile == NULL) romfile = fopen("/data/bios/7800.bin", "rb");
if (romfile == NULL) romfile = fopen("/roms/bios/a7800.bin", "rb");
if (romfile == NULL) romfile = fopen("/data/bios/a7800.bin", "rb");
if (romfile != NULL)
{
fread(bios_data, 0x1000, 1, romfile);
fclose(romfile);
hash_Compute(bios_data, 0x1000, bios_digest);
if ( (strcmp((char *)bios_digest, "0763f1ffb006ddbe32e52d497ee848ae") == 0) ||
(strcmp((char *)bios_digest, "b32526ea179dc9ab9b2e5f8a2662b298") == 0)) // We only allow the stock NTSC bios (old or new version)
{
bios_available = 1;
}
}
}
void bios_Store(void)
{
if (bios_available && !bSkipBIOS)
{
memory_WriteROM(0xF000, 0x1000, bios_data);
}
}
/*************************** End of file ****************************/

View file

@ -36,18 +36,24 @@
#define MAX_CART_SIZE (1024 * 1024) // 1MB Cart is HUGE! #define MAX_CART_SIZE (1024 * 1024) // 1MB Cart is HUGE!
extern u8 use_composite_filtering;
extern u8 bios_show_counter;
extern u8 bios_available;
#define CARTRIDGE_CONTROLLER_NONE 0 #define CARTRIDGE_CONTROLLER_NONE 0
#define CARTRIDGE_CONTROLLER_JOYSTICK 1 #define CARTRIDGE_CONTROLLER_JOYSTICK 1
#define CARTRIDGE_CONTROLLER_LIGHTGUN 2 #define CARTRIDGE_CONTROLLER_LIGHTGUN 2
#define CARTRIDGE_CONTROLLER_PADDLES 3 #define CARTRIDGE_CONTROLLER_PADDLES 3
#define CARTRIDGE_CONTROLLER_TWIN_STICKS 4 #define CARTRIDGE_CONTROLLER_TWIN_STICKS 4
#define CARTRIDGE_CONTROLLER_SOTA 5 #define CARTRIDGE_CONTROLLER_SOTA 5
#define CARTRIDGE_CONTROLLER_SNES2ATARI 6
#define JOY CARTRIDGE_CONTROLLER_JOYSTICK #define JOY CARTRIDGE_CONTROLLER_JOYSTICK
#define LGN CARTRIDGE_CONTROLLER_LIGHTGUN #define LGN CARTRIDGE_CONTROLLER_LIGHTGUN
#define PAD CARTRIDGE_CONTROLLER_PADDLES #define PAD CARTRIDGE_CONTROLLER_PADDLES
#define TWIN CARTRIDGE_CONTROLLER_TWIN_STICKS #define TWIN CARTRIDGE_CONTROLLER_TWIN_STICKS
#define SOTA CARTRIDGE_CONTROLLER_SOTA #define SOTA CARTRIDGE_CONTROLLER_SOTA
#define SNES CARTRIDGE_CONTROLLER_SNES2ATARI
#define CARTRIDGE_CYCLE_STEALING_MASK 0x01 #define CARTRIDGE_CYCLE_STEALING_MASK 0x01
#define CARTRIDGE_WSYNC_MASK 0x02 #define CARTRIDGE_WSYNC_MASK 0x02
@ -106,6 +112,8 @@ extern void cartridge_Store( );
extern void cartridge_Write(word address, byte data); extern void cartridge_Write(word address, byte data);
extern bool cartridge_IsLoaded( ); extern bool cartridge_IsLoaded( );
extern void cartridge_Release( ); extern void cartridge_Release( );
extern void bios_check_and_load(void);
extern void bios_Store(void);
extern char cartridge_title[256]; extern char cartridge_title[256];
extern byte cartridge_digest[256]; extern byte cartridge_digest[256];
extern char cartridge_filename[256]; extern char cartridge_filename[256];

View file

@ -22,6 +22,8 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Database.cpp // Database.cpp
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#include <ctype.h>
#include "Database.h" #include "Database.h"
#include "ProSystem.h" #include "ProSystem.h"
#include "../config.h" #include "../config.h"
@ -29,284 +31,462 @@
Database_Entry myCartInfo __attribute__((section(".dtcm"))); Database_Entry myCartInfo __attribute__((section(".dtcm")));
extern uint cartridge_size; extern uint cartridge_size;
// To get the md5sum of an .a78 file with header in Linux: dd bs=1 skip=128 if=somefile.a78 | md5sum
Database_Entry game_list[] = { Database_Entry game_list[] = {
{"f5150c0fc1948832211e57852abb0c6e", "Utility Cart", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=7800 Utility Cart
{"4332c24e4f3bc72e7fe1b77adf66c2b7", "3D Asteroids", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=3D Asteroids
{"0be996d25144966d5541c9eb4919b289", "Ace of Aces", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Ace Of Aces
{"877dcc97a775ed55081864b2dbf5f1e2", "Alien Brigade", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 7, 22, 264, 230, 1}, // title=Alien Brigade
{"d99bff88cd3cce191c26f5755842eb21", "Arkanoid", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 9, 256, 200, 0}, // title=Arkanoid 78b Demo (purposely set HSC to false - game HSC is buggy)
{"212ee2a6e66d8bb7fbf26f343cc8dc19", "Arkanoid", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 9, 256, 200, 0}, // title=Arkanoid 78b Demo (purposely set HSC to false - game HSC is buggy)
{"89b8b3df46733e0c4d57aeb9bb245e6f", "Armor Attack II", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Armor Attack II
{"a65f79ad4a0bbdecd59d5f7eb3623fd7", "Asteroids Deluxe (NTSC)", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 230, 0}, // title=Asteroids Deluxe
{"07342c78619ba6ffcc61c10e907e3b50", "Asteroids", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 230, 0}, // title=Asteroids
{"3d38281ed8a8d8c7cd457a18c92c8604", "Astro Blaster", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 0, 30, 9, 320, 210, 0}, // title=Astro Blaster
{"a51e5df28a0fe8c52e9d28fb5f8e44a6", "Astro Fighter", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 32, 9, 320, 210, 0}, // title=Astro Fighter
{"22e9fd9233348a572d49d80186af5d7f", "Petscii Robots", CT_BANKSHALT,POKEY_AT_800,JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 10, 256, 214, 0}, // title=Attack of the Petscii Robots (full)
{"0a5ccd326307024a2012e8d9272101b0", "Petscii Robots", CT_BANKSHALT,POKEY_AT_800,JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 10, 256, 214, 0}, // title=Attack of the Petscii Robots (shareware)
{"7cdfbe37634e7dcd4dc67db7edbcd3ba", "Baby Pac-Man", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 32, 12, 320, 221, 0}, // title=Baby Pac Man
{"8fc3a695eaea3984912d98ed4a543376", "Ballblazer", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 6, 12, 262, 220, 0}, // title=Ballblazer
{"42682415906c21c6af80e4198403ffda", "Barnyard Blaster", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Barnyard Blaster
{"f5f6b69c5eb4b55fc163158d1a6b423e", "Basketbrawl", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 1}, // title=Basketbrawl
{"78b1061d651ef806becac1dd3fda29a0", "Beef Drop", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 8, 256, 220, 0}, // title=Beef Drop (Final Atariage)
{"34483432b92f565f4ced82a141119164", "Bentley Bear", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 3, 14, 256, 220, 0}, // title=Bentley Bear's Crystal Quest
{"4e325918a8b3bbcf2f9405040abcfc6d", "Older b*nQ", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, -3, 0, 18, 256, 220, 0}, // title=BonQ (found on Atairage Age site)
{"9fa7743a016c9b7015ee1d386326f88e", "b*nQ", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, -3, 0, 18, 256, 220, 0}, // title=BonQ (final Atariage)
{"4a8a22cff154f479f1ddaa386f21fc39", "Boom", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 7, 256, 220, 0}, // title=Boom!
{"000b5888d2489f7e256d80a0848ecd14", "Cannon in D for Defense", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 223, 0}, // title=Cannon in D for Defense (demo 03)
{"5a09946e57dbe30408a8f253a28d07db", "Centipede", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 24, 16, 300, 230, 0}, // title=Centipede
{"20660b667df538ec32a8e1b998438604", "Frameless Centipede", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 24, 16, 300, 230, 0}, // title=Centipede - Frameless Hack
{"a4b5d742860beb25c29def4530194c1e", "Chase", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 8, 19, 269, 233, 0}, // title=Chase
{"93e4387864b014c155d7c17877990d1e", "Choplifter", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Choplifter
{"2e8e28f6ad8b9b9267d518d880c73ebb", "Commando", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 20, 0, 12, 256, 220, 1}, // title=Commando
{"db691469128d9a4217ec7e315930b646", "Crack'ed", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 18, 256, 229, 0}, // title=Crack'ed
{"299d31c8e181fdd011df2014451bdf0f", "Crazy Brix", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 5, 256, 203, 0}, // title=Crazy Brix
{"2d2fe4da9f1bae102fa8ca2d8830a626", "Crazy Otto", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 9, 256, 232, 0}, // title=Crazy Otto
{"a94e4560b6ad053a1c24e096f1262ebf", "Crossbow", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 5, 12, 267, 231, 0}, // title=Crossbow
{"179b76ff729d4849b8f66a502398acae", "Dark Chambers", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 13, 256, 219, 0}, // title=Dark Chambers
{"95ac811c7d27af0032ba090f28c107bd", "Desert Falcon", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 6, 19, 261, 234, 0}, // title=Desert Falcon
{"731879ea82fc0ca245e39e036fe293e6", "Dig Dug", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Dig Dug
{"5e332fbfc1e0fc74223d2e73271ce650", "Donkey Kong Jr", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 14, 256, 220, 0}, // title=Donkey Kong Jr
{"19f1ee292a23636bd57d408b62de79c7", "Donkey Kong", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 14, 256, 220, 0}, // title=Donkey Kong
{"c3107d3e3e17d67e3a11d47a5946a4f3", "DONKEY KONG XM", CT_SUPLRG, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 210, 0}, // title=Donkey Kong XM Demo (purposely set HSC to false - game HSC is buggy)
{"543484c00ba233736bcaba2da20eeea9", "Double Dragon", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 1}, // title=Double Dragon
{"94009ccfdcd4f55d24033ca06269ba6a", "Dragon's Descent", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 234, 0}, // title=Dragon's Descent 1.7
{"cad9b532a4ced6793e18ba7237e44d40", "Dragon's Havoc", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 6, 15, 256, 232, 0}, // title=Dragon's Havoc
{"2a3cb324b75af461fc974b02e6b30b5d", "Dragon's Havoc", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 6, 15, 256, 232, 0}, // title=Dragon's Havoc - Newest 2022
{"a9f29004412621f20ad9f5c51cc11486", "Draker Quest II", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 230, 0}, // title=Draker Quest II
{"fab1290f9a4c4f2b4d831c8a57f969f5", "Draker Quest", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 230, 0}, // title=Draker Quest
{"b3143adbbb7d7d189e918e5b29d55a72", "Dungeon Stalker", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 15, 256, 220, 0}, // title=Dungeon Stalker (homebrew)
{"7099cc31cbc492d525d218efe88b8685", "xxxxx", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 230, 0}, // title=E.X.O. (Alpha)
{"faea27017447dd8bae677d91125a8135", "xxxxx", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 230, 0}, // title=E.X.O. (Beta)
{"a44e8b7b7881beb0fe3c71a1a04441c8", "EXO", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 234, 0}, // title=E.X.O. (RC Demo)
{"2251a6a0f3aec84cc0aff66fc9fa91e8", "F-18 Hornet", CT_ABSOLU, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=F-18 Hornet
{"6287727ab36391a62f728bbdee88675c", "Failsafe", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 14, 257, 220, 0}, // title=Failsafe (homebrew)
{"d25d5d19188e9f149977c49eb0367cd1", "Fatal Run", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Fatal Run
{"07dbbfe612a0a28e283c01545e59f25e", "Fight Night", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Fight Night
{"cf76b00244105b8e03cdc37677ec1073", "Food Fight", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 255, 227, 0}, // title=Food Fight
{"e7d89669a7f92ec2cc99d9663a28671c", "Frenzy (w-Berzerk)", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, -5, 30, 6, 320, 206, 0}, // title=Frenzy (with Berzerk) (homebrew)
{"26031dea7251fb861cb55f86742c9d6e", "Frenzy (w-Berzerk)", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, -5, 30, 6, 320, 206, 0}, // title=Frenzy (with Berzerk) (homebrew) - Newest 2021
{"6053233cb59c0b4ca633623fd76c4576", "Froggie", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 34, 8, 320, 205, 0}, // title=Froggie (homebrew)
{"4b8f22fc4b022a7ea1b4148195b85d4a", "Frogus", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 224, 0}, // title=Frogus
{"fb8d803b328b2e442548f7799cfa9a4a", "Galaga", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 229, 0}, // title=Galaga
{"2f4ae1015a345652b36004a8c62a4ac6", "Galaxian", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 14, 5, 282, 200, 0}, // title=Galaxian
{"06204dadc975be5e5e37e7cc66f984cf", "Gato", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Gato
{"e443f7fb5be3283dd44c0f5d80c3a7b3", "GoSub", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 13, 12, 280, 234, 0}, // title=GoSub
{"1e21bf1d9d7b3c0cebaac576964c9eb2", "Graze Suit Alpha", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 11, 256, 220, 1}, // title=Graze Suit Alpha
{"fd9e78e201b6baafddfd3e1fbfe6ba31", "Hat Trick", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 17, 256, 227, 0}, // title=Hat Trick
{"c3672482ca93f70eafd9134b936c3feb", "Ikari Warriors", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 17, 256, 230, 0}, // title=Ikari Warriors
{"baebc9246c087e893dfa489632157180", "Impossible Mission", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Impossible Mission
{"1745feadabb24e7cefc375904c73fa4c", "Alternate Impossible Mission", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Impossible Mission
{"045fd12050b7f2b842d5970f2414e912", "Jinks", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 3, 12, 261, 234, 0}, // title=Jinks
{"f18b3b897a25ab3885b43b4bd141b396", "Joust", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 234, 0}, // title=Joust
{"e54edc299e72d22d0ba05d16f3393e8c", "Jr. Pac-Man (NTSC)", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 4, 14, 268, 234, 0}, // title=Jr Pac-Man
{"c3a5a8692a423d43d9d28dd5b7d109d9", "Karateka", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Karateka
{"6b8600aabd11f834448e910801f4e0bc", "KC Munchkin", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 3, 12, 256, 225, 0}, // title=KC Munchkin
{"2d8fc6f9d3f5a6ca99d13c601f9adbb9", "Keystone", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Keystone_Koppers_RC4_demo.a78
{"17b3b764d33eae9b5260f01df7bb9d2f", "Klax", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 20, 256, 234, 0}, // title=Klax (fixed)
{"5fb805f2b69820a9b196f5fed2a23c99", "Klax", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 20, 256, 234, 0}, // title=Klax
{"1d47c3802135d864dc1d922ec27aa708", "Knight Guy On Board", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 12, 256, 233, 0}, // title=Knight Guy On Board - 30 Squares Of Fate
{"f57d0af323d4e173fb49ed447f0563d7", "Kung Fu Master", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 22, 17, 276, 225, 0}, // title=Kung Fu Master
{"fe657a1b8a34b2c0e1d2171166d40926", "Legend of Silverpeak", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 7, 256, 201, 1}, // title=Legend of Silverpeak
{"431ca060201ee1f9eb49d44962874049", "Mario", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 14, 256, 220, 0}, // title=Mario Bros.
{"37b5692e33a98115e574185fa8398c22", "Mat Mania Challenge", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Mat Mania Challenge
{"f2f5e5841e4dda89a2faf8933dc33ea6", "Mean 18 Ultimate Golf", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Mean 18 Ultimate Golf
{"bedc30ec43587e0c98fc38c39c1ef9d0", "Meltdown", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Meltdown
{"c3f6201d6a9388e860328c963a3301cc", "Meteor Shower", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 13, 256, 220, 0}, // title=Meteor Shower
{"bc1e905db1008493a9632aa83ab4682b", "Midnight Mutants", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 13, 256, 226, 1}, // title=Midnight Mutants
{"6afb86a5d2d890fba68169f136d41b79", "Millie and Molly", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 14, 256, 220, 0}, // title=Millie and Molly (Demo)
{"017066f522908081ec3ee624f5e4a8aa", "Missing in Action", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Missing in Action
{"181a9978d9da7a7e21f770808cc681f2", "Merlain", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 320, 234, 0}, // title=Merlain
{"5fe8ef9e368acd5699e5a453082b7636", "Morf", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 17, 256, 224, 0}, // title=Morf
{"1c860298a8966cc8e176ab8453b172c3", "Monster Maze", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 14, 256, 220, 0}, // title=Monster Maze
{"9ff38ea62004201d870caa8bd9463525", "Moon Cresta (NTSC)", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 30, 5, 320, 210, 0}, // title=Moon Cresta
{"3bc8f554cf86f8132a623cc2201a564b", "Motor Psycho", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 1}, // title=Motor Psycho
{"fc0ea52a9fac557251b65ee680d951e5", "Ms. Pac-Man", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 224, 0}, // title=Ms. Pac-Man
{"cf007563fe94cacf5ea5295dc93ce9ef", "Ms. Pac-Man", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 9, 256, 217, 0}, // title=Ms. Pac-Man (Bob's Enhancements)
{"2a17dc5a61be342dd00af719cc335852", "Ms Pac-Man 320", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 5, 16, 265, 230, 0}, // title=Ms Pac-Man 320
{"220121f771fc4b98cef97dc040e8d378", "Ninja Golf", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 10, 20, 270, 234, 1}, // title=Ninja Golf
{"74569571a208f8b0b1ccfb22d7c914e1", "One On One", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 224, 0}, // title=One On One
{"60982f430b762343d53e48f70acfa6d0", "Pac-Man 320", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 6, 17, 264, 233, 0}, // title=Pac-Man 320
{"5013b69cb05b21a1194ce48517df7bfc", "Pac-Man Collection", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 0, 10, 11, 281, 231, 0}, // title=Pac-Man Collection (homebrew)
{"f2512870a8abc82df42b2721f8c9e7af", "Pac-Man 40th Anniversary", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 9, 10, 278, 221, 0}, // title=Pac-Man Collection 40th Anniversary Edition (homebrew) - Newest
{"a59d362e3a391ff1482131aa0818ad3e", "Pac-Man 40th Anniversary", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 9, 10, 278, 221, 0}, // title=Pac-Man Collection 40th Anniversary Edition (homebrew) - Older
{"1330d23ebad9b5ded92ebeacdf305abd", "Pac-Man 40th Anniversary", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 9, 10, 278, 221, 0}, // title=Pac-Man Collection 40th Anniversary Edition (homebrew) - PMC_XM Newest
{"2b60de20a55056a11e6412a22445296f", "Pac-Man XM-S", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 11, 17, 279, 233, 0}, // title=Pac-Man Collection 40th - Short Mazes (2022) - Newest
{"c80edcd555cd3d81f664e5f02826dc26", "Pac-Man XM-S", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 11, 17, 279, 233, 0}, // title=Pac-Man Collection 40th - Short Mazes (2022)
{"d0bf3b841ad4bbd356e9588874749a13", "Pac-Man Plus 320", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 10, 12, 274, 233, 0}, // title=Pac-Man Plus 320
{"1a5207870dec6fae9111cb747e20d8e3", "Pete Rose Baseball", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Pete Rose Baseball
{"ec206c8db4316eb1ebce9fc960da7d8f", "Pit Fighter", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Pit Fighter
{"33aea1e2b6634a1dec8c7006d9afda22", "Planet Smashers", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 15, 256, 226, 0}, // title=Planet Smashers
{"74f0283c566bdee8543e4fdc5cb8b201", "Plutos XM", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 17, 256, 234, 0}, // title=Plutos XM
{"86546808dc60961cdb1b20e761c50ab1", "Plutos", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 17, 256, 234, 0}, // title=Plutos (non-XM)
{"584582bb09ee8122e7fc09dc7d1ed813", "Pole Position II", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 35, 12, 320, 230, 0}, // title=Pole Position II
{"1745feadabb24e7cefc375904c73fa4c", "Possible Mission", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Possible Mission
{"0710aa2620435230da6a7cf3f620210d", "Demo 2.7f Popeye", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 10, 256, 210, 0}, // title=Popeye Demo 2.7f (homebrew)
{"81187dbc08934214bd55187d4b8149dd", "Demo 2.8 Popeye", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 10, 256, 210, 0}, // title=Popeye Demo 2.8 (homebrew)
{"640c7827fe63082efbaffc13f7983744", "Demo 2.9 Popeye", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 10, 256, 210, 0}, // title=Popeye Demo 2.9 (homebrew)
{"631262731c193928d01365d2bc31b64f", "Popeye", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 10, 256, 210, 0}, // title=Popeye Demo 1.0RC
{"ac03806cef2558fc795a7d5d8dba7bc0", "Rampage", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Rampage
{"383ed9bd1efb9b6cb3388a777678c928", "Realsports Baseball", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 1}, // title=Realsports Baseball
{"130b1a3792c65dfe107d7a49592449ed", "Realsports Baseball", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 1}, // title=Realsports Baseball
{"8f7eb10ad0bd75474abf0c6c36c08486", "Rescue On Fractalus", CT_FRACTALUS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Rescue On Fractalus
{"43525a0405184875c2ecfd0196886a34", "Rip-Off", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 233, 0}, // title=Rip-Off
{"66ecaafe1b82ae68ffc96267aaf7a4d7", "Robotron", CT_NORMAL, POKEY_NONE, TWIN,TWIN, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 5, 13, 270, 234, 0}, // title=Robotron
{"b6561537290e6e25e1249394366c3c63", "Robbo", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Robbo
{"fc525819ec2bdc4a30bb2e55524f8d81", "Robot's Rumble", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 11, 256, 217, 0}, // title=Robot's Rumble (2022)
{"a3a85e507d6f718972b1464ce1aaf8a4", "Scramble", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 30, 5, 320, 205, 0}, // title=Scramble (homebrew)
{"980c35ae9625773a450aa7ef51751c04", "Scrapyard Dog", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 1}, // title=Scrapyard Dog
{"b697d9c2d1b9f6cb21041286d1bbfa7f", "Sentinel", CT_SUPROM, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Sentinel
{"9bd70c06d3386f76f8162881699a777a", "Serpentine", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Serpentine (homebrew)
{"96f69b85e0b43bbebbbd59bb8276a372", "Sick Pickles", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 233, 0}, // title=Sick Pickles
{"771cb4609347657f63e6f0eb26036e35", "Space Duel", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Space Duel (homebrew)
{"6adf79558a3d7f5beca1bb8d34337417", "Space Invaders", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 30, 8, 320, 210, 0}, // title=Space Invaders (Homebrew)
{"19844117863cd38d4e1e4cbc867ae599", "Spire of the Ancients", CT_SUPLRG, POKEY_NONE, SOTA,SOTA, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 31, 17, 320, 230, 1}, // title=Spire of the Ancients
{"19844117863cd38d4e1e4cbc867ae599", "SotA", CT_SUPLRG, POKEY_NONE, SOTA,SOTA, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 31, 17, 320, 230, 1}, // title=Spire of the Ancients
{"cbb0746192540a13b4c7775c7ce2021f", "Summer Games", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 13, 256, 220, 0}, // title=Summer Games
{"81cee326b99d6831de10a566e338bd25", "Super Circus AA-NTSC-joy-4000", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 13, 256, 220, 0}, // title=Super Circus Atariage (Pokey 4000)
{"cc18e3b37a507c4217eb6cb1de8c8538", "Super Huey UH-IX", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Super Huey UH-IX
{"7ab539bb0e99e1e5a1c89230bde64610", "Super Pac-Man", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 5, 256, 225, 0}, // title=Super Pac-Man
{"59b5793bece1c80f77b55d60fb39cb94", "Super Skatebordin'", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Super Skatebordin'
{"44f862bca77d68b56b32534eda5c198d", "Tank Command", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Tank Command
{"1af475ff6429a160752b592f0f92b287", "Title Match Pro Wrestling", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Title Match Pro Wrestling
{"a60e4b608505d1fb201703b266f754a7", "Time Salvo", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 230, 0}, // title=Time Salvo
{"c3903ab01a51222a52197dbfe6538ecf", "Tomcat F-14 Simulator", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Tomcat F-14 Simulator
{"208ef955fa90a29815eb097bce89bace", "Touchdown Football", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Touchdown Football
{"8d64763db3100aadc552db5e6868506a", "Tower Toppler", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 8, 256, 230, 0}, // title=Tower Toppler
{"79df20ee86a989e669158bcb9d113e8a", "UniWarS", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 9, 5, 282, 200, 0}, // title=UniWarS
{"acf63758ecf3f3dd03e9d654ae6b69b7", "Water Ski", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 3, 256, 197, 0}, // title=Water Ski
{"427cb05d0a1abb068998e2760d77f4fb", "Water Ski", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 3, 256, 197, 0}, // title=Water Ski
{"3799d72f78dda2ee87b0ef8bf7b91186", "Winter Games", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 13, 256, 220, 0}, // title=Winter Games
{"846751861993b907c512cc9c10c67035", "Wizards Dungeon", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 18, 256, 224, 0}, // title=Wizards Dungeon
{"05fb699db9eef564e2fe45c568746dbc", "Xenophobe", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 15, 13, 284, 234, 0}, // title=Xenophobe
{"d7dc17379aa25e5ae3c14b9e780c6f6d", "Xevious", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 220, 0}, // title=Xevious
{"e1b290ee690c0cd6525773a2025177d5", "1942", CT_SUPRAMX2,POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 6, 256, 203, 1}, // title=1942 (Standard Banking RAM)
{"0fec9c1f5973c7a1dc55318ec97d8d17", "1942", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 6, 256, 203, 1}, // title=1942 (Dragonfly Banking RAM)
{"","",CT_NORMAL,0,0,0,0,0,0,0,0,0,0,0}, // The original NTSC Commercial Games
{"0be996d25144966d", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Ace Of Aces
{"877dcc97a775ed55", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 7, 22, 264, 230, 0}, // title=Alien Brigade
{"07342c78619ba6ff", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 17, 256, 230, 0}, // title=Asteroids
{"8fc3a695eaea3984", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 240, 6, 12, 262, 220, 0}, // title=Ballblazer
{"42682415906c21c6", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 155, 0, 12, 256, 220, 0}, // title=Barnyard Blaster
{"f5f6b69c5eb4b55f", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 2, 15, 262, 226, 0}, // title=Basketbrawl
{"5a09946e57dbe304", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 24, 16, 300, 230, 0}, // title=Centipede
{"93e4387864b014c1", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 210, 0, 15, 256, 226, 0}, // title=Choplifter
{"2e8e28f6ad8b9b92", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 218, 0}, // title=Commando
{"db691469128d9a42", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 18, 256, 229, 0}, // title=Crack'ed
{"a94e4560b6ad053a", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 150, 9, 17, 272, 234, 0}, // title=Crossbow
{"179b76ff729d4849", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 0, 13, 256, 219, 0}, // title=Dark Chambers
{"95ac811c7d27af00", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 240, 6, 19, 261, 234, 0}, // title=Desert Falcon
{"731879ea82fc0ca2", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 12, 256, 220, 0}, // title=Dig Dug
{"5e332fbfc1e0fc74", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Donkey Kong Jr
{"743c47f500d095f2", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Donkey Kong Jr (alt)
{"19f1ee292a23636b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Donkey Kong
{"543484c00ba23373", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 12, 256, 220, 0}, // title=Double Dragon
{"2251a6a0f3aec84c", CT_ABSOLU, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 12, 256, 220, 0}, // title=F-18 Hornet
{"d25d5d19188e9f14", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 155, 0, 12, 256, 220, 0}, // title=Fatal Run
{"07dbbfe612a0a28e", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 12, 14, 282, 226, 0}, // title=Fight Night
{"cf76b00244105b8e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 255, 227, 0}, // title=Food Fight
{"fb8d803b328b2e44", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 12, 256, 229, 0}, // title=Galaga
{"fd9e78e201b6baaf", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 0, 17, 256, 227, 0}, // title=Hat Trick
{"c3672482ca93f70e", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 180, 0, 17, 256, 230, 0}, // title=Ikari Warriors
{"baebc9246c087e89", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 12, 256, 220, 0}, // title=Impossible Mission
{"045fd12050b7f2b8", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 3, 12, 261, 234, 1}, // title=Jinks
{"f18b3b897a25ab38", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 256, 234, 0}, // title=Joust
{"c3a5a8692a423d43", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 0, 12, 256, 220, 0}, // title=Karateka
{"f57d0af323d4e173", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 22, 17, 276, 225, 0}, // title=Kung Fu Master
{"f2f5e5841e4dda89", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Mean 18 Ultimate Golf
{"bc1e905db1008493", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 13, 256, 226, 0}, // title=Midnight Mutants
{"431ca060201ee1f9", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Mario Bros.
{"37b5692e33a98115", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 0, 12, 256, 220, 0}, // title=Mat Mania Challenge
{"bedc30ec43587e0c", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 12, 256, 220, 0}, // title=Meltdown
{"3bc8f554cf86f813", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Motor Psycho
{"fc0ea52a9fac5572", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 170, 0, 17, 256, 224, 0}, // title=Ms. Pac-Man
{"220121f771fc4b98", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 10, 20, 270, 234, 0}, // title=Ninja Golf
{"74569571a208f8b0", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 245, 0, 12, 256, 224, 0}, // title=One On One
{"1a5207870dec6fae", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 12, 256, 220, 0}, // title=Pete Rose Baseball
{"33aea1e2b6634a1d", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 175, 0, 15, 256, 226, 0}, // title=Planet Smashers
{"584582bb09ee8122", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 210, 39, 15, 320, 230, 0}, // title=Pole Position II
{"ac03806cef2558fc", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 1, 16, 259, 234, 0}, // title=Rampage
{"383ed9bd1efb9b6c", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 1}, // title=Realsports Baseball
{"66ecaafe1b82ae68", CT_NORMAL, POKEY_NONE, TWIN,TWIN, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 5, 13, 270, 234, 0}, // title=Robotron
{"980c35ae9625773a", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 8, 18, 265, 234, 0}, // title=Scrapyard Dog
{"cbb0746192540a13", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 13, 256, 220, 0}, // title=Summer Games
{"cc18e3b37a507c42", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 0, 12, 256, 220, 0}, // title=Super Huey UH-IX
{"59b5793bece1c80f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 220, 0}, // title=Super Skatebordin'
{"5c4f752371a523f1", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 150, 0, 12, 256, 220, 0}, // title=Tank Command
{"1af475ff6429a160", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 0, 12, 256, 220, 0}, // title=Title Match Pro Wrestling
{"c3903ab01a51222a", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 12, 256, 220, 0}, // title=Tomcat F-14 Simulator
{"208ef955fa90a298", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Touchdown Football
{"8d64763db3100aad", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 8, 320, 234, 1}, // title=Tower Toppler
{"427cb05d0a1abb06", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 150, 0, 3, 256, 197, 0}, // title=Water Ski
{"3799d72f78dda2ee", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 170, 0, 13, 256, 220, 0}, // title=Winter Games
{"90fa275f9f2a65b3", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 170, 0, 13, 256, 220, 0}, // title=Winter Games (alt)
{"05fb699db9eef564", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 15, 13, 284, 234, 0}, // title=Xenophobe
{"d7dc17379aa25e5a", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 0, 16, 256, 220, 0}, // title=Xevious
// Prototypes and Hacks
{"4332c24e4f3bc72e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=3D Asteroids
{"1745feadabb24e7c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Impossible Mission
{"20660b667df538ec", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 24, 16, 300, 230, 0}, // title=Centipede - Frameless Hack
{"8f7eb10ad0bd7547", CT_FRACTALUS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 180, 0, 12, 256, 220, 0}, // title=Rescue On Fractalus
{"06204dadc975be5e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Gato
{"17b3b764d33eae9b", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 20, 256, 234, 0}, // title=Klax (fixed)
{"5fb805f2b69820a9", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 20, 256, 234, 0}, // title=Klax
{"017066f522908081", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 145, 0, 12, 256, 220, 0}, // title=Missing in Action
{"ec206c8db4316eb1", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Pit Fighter
{"74f0283c566bdee8", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 180, 0, 17, 256, 234, 0}, // title=Plutos XM
{"86546808dc60961c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 180, 0, 17, 256, 234, 0}, // title=Plutos (non-XM)
{"1745feadabb24e7c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Possible Mission
{"b697d9c2d1b9f6cb", CT_SUPROM, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 5, 15, 258, 220, 0}, // title=Sentinel
// Bob (pacmanplus) Games
{"89b8b3df46733e0c", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 0, 12, 256, 220, 0}, // title=Armor Attack II
{"eea04359df6770d6", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 0, 12, 256, 220, 0}, // title=Armor Attack II (20230627)
{"a65f79ad4a0bbdec", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 0, 17, 256, 230, 0}, // title=Asteroids Deluxe (NTSC) (20071014)
{"3d38281ed8a8d8c7", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 245, 30, 9, 320, 210, 0}, // title=Astro Blaster
{"55ffe535897c368b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 245, 30, 9, 320, 210, 0}, // title=Astro Blaster (20230627)
{"a51e5df28a0fe8c5", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 32, 9, 320, 213, 0}, // title=Astro Fighter
{"8feb090fb1aee5cc", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 32, 9, 320, 213, 0}, // title=Astro Fighter (v1.1)
{"7cdfbe37634e7dcd", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 32, 12, 320, 222, 0}, // title=Baby Pac Man
{"2b31dfab41dce110", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 32, 12, 320, 222, 0}, // title=Baby Pac Man (20230627)
{"34483432b92f565f", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 3, 14, 256, 220, 0}, // title=Bentley Bear's Crystal Quest
{"299d31c8e181fdd0", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 5, 256, 203, 0}, // title=Crazy Brix
{"2d2fe4da9f1bae10", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 9, 256, 232, 0}, // title=Crazy Otto
{"100551363027dc5f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 9, 256, 232, 0}, // title=Crazy Otto (20230627)
{"6287727ab36391a6", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 250, 0, 14, 257, 220, 0}, // title=FailSafe (NTSC) (20100227)
{"e7d89669a7f92ec2", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 255, 30, 6, 320, 206, 0}, // title=Frenzy (with Berzerk) (homebrew)
{"26031dea7251fb86", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 255, 30, 6, 320, 206, 0}, // title=Frenzy (with Berzerk) (20211025)
{"2f4ae1015a345652", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 14, 17, 283, 227, 0}, // title=Galaxian
{"686a4e4dde0eca5c", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 14, 17, 283, 227, 0}, // title=Galaxian (v1.1)
{"e54edc299e72d22d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 4, 14, 268, 234, 0}, // title=Jr Pac-Man
{"bde3abe40d302d8c", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 4, 14, 268, 234, 0}, // title=Jr Pac-Man (20230627)
{"6b8600aabd11f834", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 3, 12, 256, 225, 0}, // title=KC Munchkin
{"aa0b9560d6610378", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 3, 12, 256, 225, 0}, // title=KC Munchkin (20230627)
{"927edf157f88b8f5", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 3, 12, 256, 225, 0}, // title=KC Munchkin (Alt Movement) (20170409)
{"c3f6201d6a9388e8", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 13, 256, 220, 0}, // title=Meteor Shower
{"a44241d782ee14b5", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 13, 256, 220, 0}, // title=Meteor Shower (v1.1)
{"9ff38ea62004201d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 30, 20, 320, 234, 0}, // title=Moon Cresta
{"a56f26e6d21b2ae4", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 30, 20, 320, 234, 0}, // title=Moon Cresta (v1.1)
{"cf007563fe94cacf", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 215, 0, 9, 256, 217, 0}, // title=Ms. Pac-Man (Bob's Enhancements)
{"2a17dc5a61be342d", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 5, 16, 265, 230, 0}, // title=Ms Pac-Man 320
{"60982f430b762343", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 220, 6, 17, 264, 233, 0}, // title=Pac-Man 320
{"5013b69cb05b21a1", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 210, 10, 11, 281, 231, 0}, // title=Pac-Man Collection (homebrew)
{"6a432fd1e9d42a29", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 210, 10, 11, 281, 231, 0}, // title=Pac-Man Collection - Ultimate Edition (POKEY) (4000)
{"ce1cb2f5d2238449", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 210, 10, 11, 281, 231, 0}, // title=Pac-Man Collection - Ultimate Edition (POKEY) (4000)
{"200ef9c7b36afd3b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 210, 10, 11, 281, 231, 0}, // title=Pac-Man Collection - Remastered (TIA)
{"f2512870a8abc82d", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 9, 10, 278, 221, 0}, // title=Pac-Man Collection 40th Anniversary Edition (homebrew) - Newest
{"a59d362e3a391ff1", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 220, 9, 10, 278, 221, 0}, // title=Pac-Man Collection 40th Anniversary Edition (homebrew) - Older
{"1330d23ebad9b5de", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 220, 9, 10, 278, 221, 0}, // title=Pac-Man Collection 40th Anniversary Edition (homebrew) - PMC_XM Newest
{"2b60de20a55056a1", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 11, 17, 279, 233, 0}, // title=Pac-Man Collection 40th - Short Mazes (2022) - Newest
{"c80edcd555cd3d81", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 11, 17, 279, 233, 0}, // title=Pac-Man Collection 40th - Short Mazes (2022)
{"39dc7f6f39f9b3e3", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 9, 10, 278, 221, 0}, // title=Pac-Man Collection - 40th Anniversary Edition (20230627)
{"2686f20449c339f7", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 11, 17, 279, 233, 0}, // title=Pac-Man Collection - 40th Anniversary Edition (Short Mazes) (20230627)
{"2b51ebf2f371d079", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 10, 18, 271, 234, 0}, // title=Pac-Man - Energy Drink Edition (20230707)
{"d0bf3b841ad4bbd3", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 10, 12, 274, 233, 0}, // title=Pac-Man Plus 320
{"43525a0405184875", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 17, 256, 233, 0}, // title=Rip-Off
{"803743fe18600f29", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 17, 256, 233, 0}, // title=Rip-Off (20230627)
{"a3a85e507d6f7189", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 250, 30, 5, 320, 205, 0}, // title=Scramble (homebrew)
{"31b20a4710e69130", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 250, 30, 5, 320, 205, 0}, // title=Scramble (20230627)
{"771cb4609347657f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 0, 12, 256, 220, 0}, // title=Space Duel (homebrew)
{"6adf79558a3d7f5b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 30, 8, 320, 210, 0}, // title=Space Invaders (Homebrew)
{"6a898d52ef050cdb", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 30, 8, 320, 210, 0}, // title=Space Invaders (v1.1)
{"7ab539bb0e99e1e5", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 0, 5, 256, 225, 0}, // title=Super Pac-Man
{"88b9de0eba37ba51", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 0, 5, 256, 225, 0}, // title=Super Pac-Man (20230627)
{"79df20ee86a989e6", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 9, 18, 282, 230, 0}, // title=UniWarS
{"0db69825c81171e6", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 9, 18, 282, 230, 0}, // title=UniWarS (v1.1)
{"f41f651417c23410", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 240, 26, 12, 320, 225, 0}, // title=Super Cobra
{"fd9353d42cca5f81", CT_SUPRAMX2,POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 190, 0, 6, 256, 203, 1}, // title=1942 (Standard Banking RAM)
{"e1b290ee690c0cd6", CT_SUPRAMX2,POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 190, 0, 6, 256, 203, 1}, // title=1942 (Standard Banking RAM)
{"0fec9c1f5973c7a1", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 190, 0, 6, 256, 203, 1}, // title=1942 (Dragonfly Banking RAM)
{"6f157f421c7ed5d9", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 22, 14, 299, 230, 0}, // title=2048 (RC1a) (20211113)
{"a837a34f540fd137", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 14, 16, 286, 234, 0}, // title=7iX (20220305)
{"ff056f2858f14fc4", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 3, 15, 261, 234, 0}, // title=A Roach In Space - Part II - Electric Bugaloo (20201119)
{"d99bff88cd3cce19", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 9, 256, 200, 0}, // title=Arkanoid 78b Demo (purposely set HSC to false - game HSC is buggy)
{"212ee2a6e66d8bb7", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 9, 256, 200, 0}, // title=Arkanoid 78b Demo (purposely set HSC to false - game HSC is buggy)
{"e1da4c3ea0d26ae0", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 9, 256, 200, 0}, // title=Arkanoid 78b Demo (purposely set HSC to false - game HSC is buggy)
{"0d05659a7d0eef02", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 1, 16, 268, 234, 0}, // title=ARTI Public Demo
{"4a8a22cff154f479", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 7, 256, 220, 0}, // title=Boom!
{"9fa7743a016c9b70", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 27, 256, 234, 0}, // title=BonQ (Final Atariage)
{"78b1061d651ef806", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 135, 0, 16, 256, 234, 0}, // title=Beef Drop (Final Atariage)
{"b11b1a2bae8a1d0c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 125, 3, 13, 264, 227, 0}, // title=Bernie and the Cubic Conundrum (Alpha 10)
{"a34cd425d0c087d0", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 125, 6, 16, 264, 233, 0}, // title=Bernie and the Tower of Doom (RC1) (Demo)
{"000b5888d2489f7e", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 0, 12, 256, 223, 0}, // title=Cannon in D for Defense (demo 03)
{"825c03c049306c16", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 125, 0, 16, 260, 234, 0}, // title=Cartesian Chaos (v11) (20221218)
{"a4b5d742860beb25", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 8, 19, 269, 233, 0}, // title=Chase (20201231)
{"0c2f248a1ae9bfd1", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 234, 0}, // title=Danger Zone (RC-4C) (NTSC Demo) (20201231)
{"fab7b59dd580dce0", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 2, 17, 260, 229, 0}, // title=Death Merchant (v1_30)
{"dd1cfc933d2bfacc", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 185, 0, 12, 256, 210, 0}, // title=Donkey Kong PK-XM (NTSC) (Demo) (v1.2)
{"c3107d3e3e17d67e", CT_SUPLRG, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 185, 0, 20, 256, 223, 0}, // title=Donkey Kong XM DEMO
{"312363c7691fa51e", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 180, 0, 17, 256, 216, 0}, // title=Donkey Kong Remix DEMO
{"77164df89ae49b4d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 125, 0, 16, 256, 234, 0}, // title=Dragon's Descent (20210731)
{"8c2798f929a43317", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 6, 15, 256, 232, 0}, // title=Dragon's Havoc (Demo Dec 2022)
{"7b7825ca2c79148f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 125, 6, 15, 256, 232, 0}, // title=Dragon's Cache (20210207)
{"a9f29004412621f2", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 16, 256, 230, 0}, // title=Draker Quest II
{"fab1290f9a4c4f2b", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 120, 0, 17, 256, 230, 0}, // title=Draker Quest (Beta 4)
{"b3143adbbb7d7d18", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 9, 256, 231, 0}, // title=Dungeon Stalker (20151022)
{"a44e8b7b7881beb0", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 135, 0, 16, 256, 234, 0}, // title=E.X.O. (RC Demo A)
{"6053233cb59c0b4c", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 34, 4, 320, 216, 0}, // title=Froggie (NTSC) (Final Release)
{"9daaac9b25783a7e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 16, 256, 224, 0}, // title=Frogus (20221020)
{"c2e131a091ceed2e", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 15, 256, 234, 0}, // title=Game of the Bear - Polar Opposites (RC1) (20230211)
{"e443f7fb5be3283d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 13, 12, 280, 234, 0}, // title=GoSub
{"1e21bf1d9d7b3c0c", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 11, 256, 220, 0}, // title=Graze Suit Alpha (20170910)
{"1c9deabc48f07d1b", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 115, 0, 12, 256, 220, 0}, // title=Keystone Koppers (Demo Dec 22)
{"d41d8cd98f00b204", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 115, 0, 12, 256, 220, 0}, // title=Keystone Koppers (RC4 Demo)
{"1d47c3802135d864", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 233, 0}, // title=Knight Guy On Board - 30 Squares Of Fate (20210116)
{"0916973898e3b6b8", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 233, 0}, // title=Knight Guy On Board - 30 Squares Of Fate (Demo 3)
{"33dbb58f9ee73e9f", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 233, 0}, // title=Knight Guy In Low Res World - Castle Days (RC 01-1)
{"3ec728e116017be8", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 233, 0}, // title=Knight Guy - Quest For Something (20210423)
{"7abd9e0a6321e813", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 233, 0}, // title=Knight Guy - In Another Castle (RC1b)
{"271864e0978278a3", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 7, 256, 201, 0}, // title=Legend of Silverpeak (1.01)
{"7abd9e0a6321e813", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 135, 6, 16, 264, 234, 0}, // title=Lunar Patrol (v83) (20241231) (2354A1FF)
{"23ac803eabfb8c61", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 12, 256, 234, 0}, // title=Lyra the Tenrec (20240104)
{"181a9978d9da7a7e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 320, 234, 0}, // title=Merlain
{"3f80432f156088bf", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 14, 256, 220, 0}, // title=Millie And Molly (Demo) (POKEY 450) (20230305)
{"1c860298a8966cc8", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 14, 256, 220, 0}, // title=Monster Maze (20220306)
{"d1b56eae7227c12d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 17, 256, 224, 0}, // title=Morf (20220314)
{"ac5c99ac01c96ad9", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 16, 256, 234, 0}, // title=Ninjish Guy - Perilous Island (20211107)
{"3d9c52142f9e53f5", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 17, 256, 234, 0}, // title=Oozy The Goo - Gaiden (20231001)
{"6ac5a7f8b6a3198e", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 125, 2, 17, 260, 234, 0}, // title=PentaGo (Demo) (20230422)
{"a662862f20362fc5", CT_BANKSHALT,POKEY_AT_800,SNES,JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 10, 256, 214, 0}, // title=Attack of the Petscii Robots (Demo) (POKEY 800)
{"0254afa887fcfc8c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 16, 17, 289, 220, 0}, // title=Plumb Luck DX (RC2) (20230410)
{"61e6a16889b62216", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 256, 220, 0}, // title=Popeye 2.40 Demo
{"fab49206f4b041fa", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 256, 220, 0}, // title=Popeye 2.40 j7800 Demo
{"b6561537290e6e25", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 12, 256, 220, 0}, // title=Robbo (20160513)
{"fc525819ec2bdc4a", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 11, 256, 217, 0}, // title=Robots Rumble (20220217)
{"9bd70c06d3386f76", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 12, 256, 220, 0}, // title=Serpentine (20161029)
{"96f69b85e0b43bbe", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 120, 0, 12, 256, 233, 0}, // title=Sick Pickles (20171202)
{"40567f50c569a60c", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 16, 256, 230, 0}, // title=Slide Boy in Maze Land (RC1) (20210515)
{"91f4cb1f642ff1de", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 110, 0, 16, 256, 230, 0}, // title=Space Peril (NTSC) (v8) (20210907)
{"19844117863cd38d", CT_SUPLRG, POKEY_NONE, SOTA,SOTA, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 31, 17, 320, 230, 0}, // title=Spire of the Ancients (NTSC) (20201223)
{"81cee326b99d6831", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 0, 13, 256, 220, 0}, // title=Super Circus Atari Age (NTSC) (Joystick) (POKEY 4000)
{"02508e6df5e173b4", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 0, 13, 256, 220, 0}, // title=Super Circus Atari Age (NTSC) (Joystick) (POKEY 0450)
{"a60e4b608505d1fb", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 16, 256, 230, 0}, // title=Time Salvo
{"ff825fcbed9bf699", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 17, 256, 220, 0}, // title=Touchdown Challenge (v2_21) (20230225)
{"0d7e2674d802b412", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 13, 256, 233, 0}, // title=Tunnels of Hyperion
{"f5150c0fc1948832", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 12, 256, 240, 0}, // title=7800 Utility Cart
{"846751861993b907", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 130, 0, 16, 256, 231, 0}, // title=Wizards Dungeon (20211111)
{"",CT_NORMAL,0,0,0,0,0,0,0,0,0,0,0},
}; };
NameMap_t NameMap[] =
{
{"POPEYE", "POPEYE", "61e6a16889b62216"},
{"EXO", "EXO", "a44e8b7b7881beb0"},
{"KOPPERS", "KOPPERS", "d41d8cd98f00b204"},
{"TIME", "SALVO", "a60e4b608505d1fb"},
{"CHRISTMAS", "SALVO", "a60e4b608505d1fb"},
{"MERLAIN", "MERLAIN", "181a9978d9da7a7e"},
{"TOUCHDOWN", "CHALLENGE", "ff825fcbed9bf699"},
{"OOZY", "GAIDEN", "3d9c52142f9e53f5"},
{"MILLIE", "MOLLY", "3f80432f156088bf"},
{"SILVERPEAK", "SILVERPEAK", "271864e0978278a3"},
{"LUNAR", "PATROL", "7abd9e0a6321e813"},
{"ATTACK", "PETSCII", "a662862f20362fc5"},
{"KNIGHT", "BOARD", "0916973898e3b6b8"},
{"KNIGHT", "WORLD", "33dbb58f9ee73e9f"},
{"KNIGHT", "SOMETHING", "3ec728e116017be8"},
{"KNIGHT", "ANOTHER", "7abd9e0a6321e813"},
{"DRAGON", "DESCENT", "77164df89ae49b4d"},
{"DRAGON", "HAVOC", "8c2798f929a43317"},
{"DRAGON", "CACHE", "7b7825ca2c79148f"},
{"ARKANOID", "ARKANOID", "d99bff88cd3cce19"},
{"ARTI", "ATRI", "0d05659a7d0eef02"},
{"CANNON", "DEFENSE", "000b5888d2489f7e"},
{"1942", "1942", "fd9353d42cca5f81"},
{"WIZARD", "DUNGEON", "846751861993b907"},
{"UTILITY", "CART", "f5150c0fc1948832"},
{"BONQ", "BONQ", "9fa7743a016c9b70"},
{"BEEF", "DROP", "78b1061d651ef806"},
{"BERNIE", "CUBIC", "b11b1a2bae8a1d0c"},
{"BERNIE", "TOWER", "a34cd425d0c087d0"},
{"DEATH", "MERCHANT", "fab7b59dd580dce0"},
{"TUNNELS", "HYPERION", "0d7e2674d802b412"},
{"SLIDE", "MAZE", "40567f50c569a60c"},
{"CARTESIAN", "CHAOS", "825c03c049306c16"},
{"DANGER", "ZONE", "0c2f248a1ae9bfd1"},
{"SCRAMBLE", "SCRAMBLE", "a3a85e507d6f7189"},
{"KLAX", "KLAX", "17b3b764d33eae9b"},
{"PENTAGO", "PENTAGO", "6ac5a7f8b6a3198e"},
{"","",""}
};
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// This happens AFTER The rom is loaded and the header info (if any) has // This happens AFTER The rom is loaded and the header info (if any) has
// been read out. Here we can adjust based on the hash table above... which // been read out. Here we can adjust based on the hash table above... which
// is mainly used for headerless roms and a few special cases were we want // is mainly used for headerless roms and a few special cases were we want
// to correct the Y-offsets to make the game well centered/scaled on screen. // to correct the Y-offsets to make the game well centered/scaled on screen.
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
bool database_Load(byte *digest) bool database_Load(byte * digest)
{ {
extern u8 bNoDatabase;
bool bFound = false; bool bFound = false;
uint16 i; uint16 i;
// -------------------------------------------------------------------------------------- // Uppercase the filename... to make searching easier.
// First see if we've got a match in our external A7800DS.DAT configuration database... for(int j = 0; j < strlen(cartridge_filename); j++) cartridge_filename[j] = toupper(cartridge_filename[j]);
// --------------------------------------------------------------------------------------
for (i=0; i<MAX_CONFIGS; i++) // See if we've been asked to skip the internal database
if(!bNoDatabase)
{ {
if (!strcmp(allConfigs.cart[i].digest,(char *) digest)) // --------------------------------------------------------------------------------------
{ // First see if we've got a match in our external A7800DS.DAT configuration database...
memcpy(&myCartInfo, &allConfigs.cart[i], sizeof(myCartInfo)); // --------------------------------------------------------------------------------------
bFound = true; for(i = 0; i < MAX_CONFIGS; i++)
break; {
} if(!strncmp(allConfigs.cart[i].half_digest, (char *) digest, 16))
{
memcpy( & myCartInfo, & allConfigs.cart[i], sizeof(myCartInfo));
bFound = true;
break;
}
}
// ---------------------------------------------------------------------------------------------
// If we didn't find it in the config database, we can look in the internal database table...
// ---------------------------------------------------------------------------------------------
if(!bFound)
{
/* Look up mapper in game list by md5sum */
for(i = 0; strlen(game_list[i].half_digest); i++)
{
if(!strncmp(game_list[i].half_digest, (char *) digest, 16))
{
memcpy( & myCartInfo, & game_list[i], sizeof(myCartInfo));
if(!isDSiMode()) myCartInfo.frameSkip = ((cartridge_size <= (48 * 1024)) ? FRAMESKIP_MEDIUM : FRAMESKIP_AGGRESSIVE); // Older DS-Lite/Phat needs help
myCartInfo.palette = 1; // Force this if not specifically found by md5
myCartInfo.xJiggle = 64;
myCartInfo.yJiggle = 16;
bFound = true;
break;
}
}
}
// -----------------------------------------------------------------------------------------------
// If we didn't find a definitive md5 match above, look up game by name in our name mapping table.
// -----------------------------------------------------------------------------------------------
if(!bFound)
{
for(int k = 0; strlen(NameMap[k].name1); k++)
{
if(myCartInfo.region == NTSC)
{
if((strstr(cartridge_filename, NameMap[k].name1)) && (strstr(cartridge_filename, NameMap[k].name2))) // If both names are found in the filename...
{
/* Look up mapper in game list by md5sum from the Name Mapper table */
for(i = 0; strlen(game_list[i].half_digest); i++)
{
if(!strncmp(game_list[i].half_digest, (char *) NameMap[k].half_digest, 16))
{
memcpy( & myCartInfo, & game_list[i], sizeof(myCartInfo));
if(!isDSiMode()) myCartInfo.frameSkip = ((cartridge_size <= (48 * 1024)) ? FRAMESKIP_MEDIUM : FRAMESKIP_AGGRESSIVE); // Older DS-Lite/Phat needs help
myCartInfo.palette = 1; // Force this if not specifically found by md5
myCartInfo.xJiggle = 64;
myCartInfo.yJiggle = 16;
bFound = true;
break;
}
}
break;
}
}
}
}
} }
// No matter what... override for Tower Toppler to make it playable...
// --------------------------------------------------------------------------------------------- if(strcmp((char *) digest, (char *) "8d64763db3100aadc552db5e6868506a") == 0) // Tower Toppler
// If we didn't find it in the config database, we can look in the internal database table...
// ---------------------------------------------------------------------------------------------
if (!bFound)
{ {
/* Look up mapper in game list by md5sum */ use_composite_filtering = 76;
for(i = 0; strlen(game_list[i].digest); i++) myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; // It's the only way we stand a chance.
{ myCartInfo.cardctrl1 = SOTA;
if (!strcmp(game_list[i].digest,(char *) digest)) myCartInfo.xOffset = 32;
{ myCartInfo.yOffset = 8;
memcpy(&myCartInfo, &game_list[i], sizeof(myCartInfo)); myCartInfo.xScale = 320;
if (!isDSiMode()) myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; // DS-Lite defaults to frame skipping no matter what the DB says... user can override myCartInfo.yScale = 234;
myCartInfo.palette = 1; // Force this if not specifically found by md5 bFound = 1;
bFound = true;
break;
}
}
} }
else if(strcmp((char *) digest, (char *) "f5150c0fc1948832211e57852abb0c6e") == 0) // 7800 Utility Cart
// -----------------------------------------------------------------------------------------------
// If we didn't find a definitive md5 match above, look up game by cart title in the .A78 header
// or even by the name of the ROM filename as it will give us a clue as to the game identity.
// -----------------------------------------------------------------------------------------------
if (!bFound)
{ {
for(i = 0; strlen(game_list[i].header_name); i++) use_composite_filtering = 1;
{ bFound = 1;
if (myCartInfo.region == NTSC)
{
if ( (!strcmp(game_list[i].header_name,(char *) cartridge_title)) ||
(strstr((char *) cartridge_filename, game_list[i].header_name) != NULL) )
{
memcpy(&myCartInfo, &game_list[i], sizeof(myCartInfo));
strcpy(myCartInfo.digest, (char *)digest);
if (!isDSiMode()) myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; // DS-Lite defaults to frame skipping no matter what the DB says... user can override
myCartInfo.palette = 1; // Force this if not specifically found by md5
bFound = true;
break;
}
}
}
} }
// Override for Jinks to enable composite filtering
else if(strcmp((char *) digest, (char *) "045fd12050b7f2b842d5970f2414e912") == 0) // Jinks
{
use_composite_filtering = 1;
bFound = 1;
}
else use_composite_filtering = 0;
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Default scaling options below if not found... these are close enough... // Default scaling options below if not found... these are close enough...
// We can make some educated guesses on cart and frameskip... // We can make some educated guesses on cart and frameskip...
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
if (!bFound) if(!bFound)
{ {
strcpy(myCartInfo.digest, (char *)digest); strncpy(myCartInfo.half_digest, (char *) digest, 16);
strncpy(myCartInfo.header_name, (char *) cartridge_filename, 32); myCartInfo.half_digest[16] = 0;
myCartInfo.header_name[32] = 0; myCartInfo.xOffset = 0;
myCartInfo.xOffset = 0; myCartInfo.yOffset = 13;
myCartInfo.yOffset = 13; myCartInfo.xScale = 256;
myCartInfo.xScale = 256; myCartInfo.yScale = 220;
myCartInfo.yScale = 220; myCartInfo.diff1 = DIFF_A;
myCartInfo.diff1 = DIFF_A; myCartInfo.diff2 = DIFF_A;
myCartInfo.diff2 = DIFF_A; myCartInfo.xButton = KEY_MAP_DEFAULT;
myCartInfo.spare1 = 0; myCartInfo.yButton = KEY_MAP_DEFAULT;
myCartInfo.spare2 = 0; myCartInfo.spare2 = 0;
myCartInfo.spare3 = 0; myCartInfo.spare3 = 0;
myCartInfo.palette = 1; myCartInfo.spare4 = 1;
myCartInfo.spare16 = 0x0000; myCartInfo.spare5 = 0;
myCartInfo.dma_adjust = 0; myCartInfo.palette = 1;
myCartInfo.xJiggle = 64;
myCartInfo.yJiggle = 16;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// If the game has no .a78 header, do our best to guess these... // If the game has no .a78 header, do our best to guess these...
// ------------------------------------------------------------------- // -------------------------------------------------------------------
if (myCartInfo.hasHeader == false) if(myCartInfo.hasHeader == false)
{ {
myCartInfo.region = NTSC; myCartInfo.region = NTSC;
if (cartridge_size == (144*1024)) myCartInfo.cardtype = CT_SUPLRG; if(cartridge_size == (144 * 1024)) myCartInfo.cardtype = CT_SUPLRG;
else myCartInfo.cardtype = (cartridge_size <= (52*1024)) ? CT_NORMAL:CT_SUPROM; else myCartInfo.cardtype = (cartridge_size <= (52 * 1024)) ? CT_NORMAL : CT_SUPROM;
myCartInfo.pokeyType = POKEY_NONE; myCartInfo.pokeyType = POKEY_NONE;
myCartInfo.cardctrl1 = JOY; myCartInfo.cardctrl1 = JOY;
myCartInfo.cardctrl2 = JOY; myCartInfo.cardctrl2 = JOY;
myCartInfo.hsc = false; myCartInfo.hsc = false;
} }
// -------------------------------------------------------- // --------------------------------------------------------
// Do our best guess as to whether we should frameskip... // Do our best guess as to whether we should frameskip...
// -------------------------------------------------------- // --------------------------------------------------------
if (isDSiMode()) // DSi can handle many games in full framerate if(isDSiMode()) // DSi can handle most games in full framerate... default to disable frameskip for the DSi
{ {
if (cartridge_size <= (130*1024)) myCartInfo.frameSkip = FRAMESKIP_DISABLE; // For smaller cart sizes, don't frameskip myCartInfo.frameSkip = FRAMESKIP_DISABLE;
else myCartInfo.frameSkip = FRAMESKIP_MEDIUM;
} }
else // DS-Lite defaults to frame skipping else // DS-Lite defaults to some level of frame skipping
{ {
myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; myCartInfo.frameSkip = ((cartridge_size <= (48 * 1024)) ? FRAMESKIP_MEDIUM : FRAMESKIP_AGGRESSIVE); // Non-banked carts get light frameskip... otherwise agressive
}
}
// Lastly - use the internal database to always try and find a BIOS timeout value... we don't let the user override this one...
myCartInfo.biosTimeout = 160;
bFound = 0;
for(i = 0; strlen(game_list[i].half_digest); i++) // Search through entire internal database...
{
if(!strncmp(game_list[i].half_digest, (char *) digest, 16)) // Search by md5sum
{
myCartInfo.biosTimeout = game_list[i].biosTimeout;
bFound = 1;
break;
}
}
if(!bFound) // And if not found - search by name to find a reasonable bios timeout value
{
for(int k = 0; strlen(NameMap[k].name1); k++)
{
if(myCartInfo.region == NTSC)
{
if((strstr(cartridge_filename, NameMap[k].name1)) && (strstr(cartridge_filename, NameMap[k].name2))) // If both names are found in the filename...
{
/* Look up bios timeout in game list by md5sum from the Name Mapper table */
for(i = 0; strlen(game_list[i].half_digest); i++)
{
if(!strncmp(game_list[i].half_digest, (char *) NameMap[k].half_digest, 16))
{
myCartInfo.biosTimeout = game_list[i].biosTimeout;
bFound = true;
break;
}
}
break;
}
}
} }
} }
return true; return true;
} }
// End of file

View file

@ -28,8 +28,7 @@
#include "Cartridge.h" #include "Cartridge.h"
typedef struct { typedef struct {
char digest[33]; char half_digest[17];
char header_name[33];
u8 cardtype; u8 cardtype;
u8 pokeyType; u8 pokeyType;
u8 cardctrl1; u8 cardctrl1;
@ -38,23 +37,48 @@ typedef struct {
u8 diff2; u8 diff2;
u8 region; u8 region;
u8 hsc; u8 hsc;
s16 dma_adjust; u8 biosTimeout;
s16 xOffset; s16 xOffset;
s16 yOffset; s16 yOffset;
s16 xScale; s16 xScale;
s16 yScale; s16 yScale;
u8 frameSkip; u8 frameSkip;
u8 hasHeader; u8 spare0;
u8 spare1; u8 spare1;
u8 hasHeader;
u8 palette;
u8 xJiggle;
u8 yJiggle;
u8 xButton;
u8 yButton;
u8 spare2; u8 spare2;
u8 spare3; u8 spare3;
u8 palette; u8 spare4;
s16 spare16; u8 spare5;
u8 spare6;
u8 spare7;
} Database_Entry; } Database_Entry;
typedef struct {
char *name1;
char *name2;
char *half_digest;
} NameMap_t;
extern Database_Entry myCartInfo; extern Database_Entry myCartInfo;
#define KEY_MAP_DEFAULT 0
#define KEY_MAP_PANUP 1
#define KEY_MAP_PANDN 2
#define KEY_MAP_JOYUP 3
#define KEY_MAP_JOYDN 4
#define KEY_MAP_JOYLEFT 5
#define KEY_MAP_JOYRIGHT 6
#define KEY_MAP_JOYB1 7
#define KEY_MAP_JOYB2 8
#define KEY_MAP_PAUSE 9
extern void database_Initialize( ); extern void database_Initialize( );
extern bool database_Load(byte *digest); extern bool database_Load(byte *digest);

View file

@ -232,9 +232,7 @@ void hash_Compute(const byte* source, uint length, byte * dest) {
for(index = 0; index < 16; index++) { for(index = 0; index < 16; index++) {
digest[index] = bufferptr[index]; digest[index] = bufferptr[index];
} }
//char buffer[33] = {0};
sprintf((char *)dest, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); sprintf((char *)dest, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]);
//return buffer;
} }

View file

@ -5,7 +5,7 @@
// //
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright 2005 Greg Stanton // Copyright 2005 Greg Stanton
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or // the Free Software Foundation; either version 2 of the License, or
@ -21,6 +21,18 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// HighScore.cpp // HighScore.cpp
//
// The HSC hardware uses the Toshiba TC5516APL chip which is 2048 x 8bit (2K)
// and is maintained by a battery CR2032 at 3V
//
// With an SRAM of this vintage, the contents of an uninitialized / freshly
// powered up chip would likely be random values. For emulation purposes, we
// are initializing the SRAM contents on a fresh board as if they were zeros.
//
// With this emulation, each game gets their own private 2K SRAM for the
// HSC memory. Further, to avoid problems with some games that do not initialize
// the SRAM, we are going to use a fresh snapshot of the full 2K of SRAM right
// after a known good cart (Asteroids - NTSC) has initialized the memory.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#include <fat.h> #include <fat.h>
#include <dirent.h> #include <dirent.h>
@ -29,106 +41,200 @@
#include "Cartridge.h" #include "Cartridge.h"
#include "Database.h" #include "Database.h"
#define HS_SRAM_START 0x1000 // The 7800 RAM memory location of the high score cartridge SRAM #define HS_SRAM_START 0x1000 // The 7800 RAM memory location of the high score cartridge SRAM
#define HS_SRAM_SIZE 2048 // The size of the high score cartridge SRAM #define HS_SRAM_SIZE 2048 // The size of the high score cartridge SRAM and gets loaded at 7800 memory address 0x1000
#define VIRTUAL_SRAM_BLOCKS 8 // We support 8 virtual high score blocks to keep the now crowded HSC filling up #define HSC_CART_ROM_SIZE 4096 // The highscore.rom is exactly 4K in size and gets loaded at 7800 memory address 0x3000
#define HSC_CART_ROM_SIZE 4096 // The highscore.rom is exactly 4K in size and gets loaded at 7800 memory address 0x3000
#define HSC_TITLE_SIZE 33 // Includes 32 chars for title name and 1 char for size static byte high_score_sram[HS_SRAM_SIZE]; // Buffer for the actual 2K of HSC data
static byte high_score_rom[HSC_CART_ROM_SIZE]; // Buffer for the 4K of high score ROM (highscore.rom)
byte high_score_cart_loaded = false; // Flips to true if the High Score Cart is loaded in memory
// -----------------------------------------------------------------------------------------------------
// This is a snapshot of an initialized HSC after Asteroids (NTSC) initialized it with the name "HSC"
// A few of the modern homebrews don't do a good job of initializing the HSC and so we use this as
// as default starting SRAM (of 0x00 before init) which is squeaky clean and ready for use by the game.
// -----------------------------------------------------------------------------------------------------
unsigned char A7800DS_00_sram[HS_SRAM_SIZE] = {
0x00, 0x00, 0x68, 0x83, 0xaa, 0x55, 0x9c, 0x02, 0x07, 0x12, 0x02, 0x1f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static byte high_score_cart_loaded = false;
static u32 last_hs_chksum = 0xFFFFEEEF;
static byte high_score_sram[VIRTUAL_SRAM_BLOCKS][HS_SRAM_SIZE];
static byte high_score_rom[HSC_CART_ROM_SIZE];
static byte hs_virtual_slot = 0;
/* /*
* Saves the high score cartridge SRAM * Saves the high score cartridge SRAM
* *
* return Whether the save was successful * return Whether the save was successful (false if game is not supporting HSC)
*/ */
bool cartridge_SaveHighScoreSram(void) static char szName[256+4];
{ static void make_hsc_name(void)
bool status = false; {
word retries = 3; // Init HSC filename is same as base filename with .hsc
DIR* dir = opendir("sav");
if (dir) closedir(dir); // Directory exists... close it out and move on.
else mkdir("sav", 0777); // Otherwise create the directory...
sprintf(szName,"sav/%s", cartridge_filename);
int len = strlen(szName);
szName[len-3] = 'h';
szName[len-2] = 's';
szName[len-1] = 'c';
szName[len-0] = 0;
}
// ------------------------------------------------------------------------------------
// Write the full 2K of HSC - a bit wasteful for a single game but we've got plenty
// of space and it turns out to be more flexibile for each game to have their own HSC.
// ------------------------------------------------------------------------------------
bool cartridge_SaveHighScoreSram(void)
{
if(!high_score_cart_loaded || !myCartInfo.hsc) if(!high_score_cart_loaded || !myCartInfo.hsc)
{ {
// If we didn't load the high score cartridge, or don't have an HSC enabled cart: don't save. return false; // If we didn't load the high score cartridge, or don't have an HSC enabled cart: don't save.
return false;
} }
// ------------------------------------------------------------------------------------------------ // Make sure something actually changed before writing...
// We are being fairly paranoid here mainly because there are games that corrupt the SRAM area if (memcmp(high_score_sram, memory_ram+HS_SRAM_START, HS_SRAM_SIZE) != 0)
// of the high score cart... XENOPHOBE is one. The Donkey Kong XM Homebrew is another. So we
// want to make sure we aren't saving crap out - this is fairly robust ... so far...
// ------------------------------------------------------------------------------------------------
while (status == false)
{ {
u32 chksum = 0; memcpy(high_score_sram, memory_ram+HS_SRAM_START, HS_SRAM_SIZE); // Copy from main memory to SRAM buffer
for( uint i = 0; i < HS_SRAM_SIZE; i++ )
{ make_hsc_name(); // HSC filename is same as base filename with .hsc extension
high_score_sram[hs_virtual_slot][i] = memory_ram[HS_SRAM_START+i];
chksum += high_score_sram[hs_virtual_slot][i];
}
// ------------------------------------------------------------ // Write out the .hsc file
// Make sure there is something different/worth saving... FILE *handle = fopen(szName, "wb");
// ------------------------------------------------------------ if (handle != NULL)
if ((chksum != last_hs_chksum) && (chksum != 0)) {
{ fwrite(high_score_sram, HS_SRAM_SIZE, 1, handle);
// ----------------------------------------------------------------------------------------------------------- fclose(handle);
// Check to make sure the High Score Cart "Magic Numbers" are right... otherwise corrupt and don't save... }
// -----------------------------------------------------------------------------------------------------------
if ((high_score_sram[hs_virtual_slot][2] == 0x68) && (high_score_sram[hs_virtual_slot][3] == 0x83) && (high_score_sram[hs_virtual_slot][4] == 0xaa) && (high_score_sram[hs_virtual_slot][5] == 0x55) && (high_score_sram[hs_virtual_slot][6] == 0x9c))
{
// ------------------------------------------------------------------------------------------------
// Make sure the other virtual slots are valid... if not, copy the one known 'good' slot to those
// ------------------------------------------------------------------------------------------------
for (u8 i=0; i<VIRTUAL_SRAM_BLOCKS; i++)
{
if (i != hs_virtual_slot) // We already know hs_virtual_slot is good
{
// Check for magic numbers in the other virtual slots...
if (!((high_score_sram[i][2] == 0x68) && (high_score_sram[i][3] == 0x83) && (high_score_sram[i][4] == 0xaa) && (high_score_sram[i][5] == 0x55) && (high_score_sram[i][6] == 0x9c)))
{
// Bad virutal slot memory found... initialize it with the good one
memcpy(high_score_sram[i], high_score_sram[hs_virtual_slot], HS_SRAM_SIZE);
}
}
}
// ---------------------------------------------------------------------------------------
// On the chance that the HS cart 'name' has been altered in the current slot... copy it
// ---------------------------------------------------------------------------------------
for (u8 i=0; i<VIRTUAL_SRAM_BLOCKS; i++)
{
if (i != hs_virtual_slot) // We already know hs_virtual_slot is good
{
// Copy the title from the current slot...
memcpy(&high_score_sram[i][8], &high_score_sram[hs_virtual_slot][8], HSC_TITLE_SIZE);
}
}
last_hs_chksum = chksum;
DIR* dir = opendir("/data");
if (dir) closedir(dir); // Directory exists.
else mkdir("/data", 0777); // Doesn't exist - make it...
FILE* file = fopen("/data/A7800DS.sram", "wb+");
if( file != NULL )
{
if( fwrite( high_score_sram, 1, HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS, file ) != HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS )
{
status = false;
} else status = true;
fflush(file);
fclose(file);
}
}
}
if (--retries == 0) break;
} }
return status; return true; // We at least made the attempt to write out the .hsc save file
} }
/* /*
@ -137,74 +243,36 @@ bool cartridge_SaveHighScoreSram(void)
* return Whether the load was successful * return Whether the load was successful
*/ */
static bool cartridge_LoadHighScoreSram(void) static bool cartridge_LoadHighScoreSram(void)
{ {
bool status = false; if(!high_score_cart_loaded || !myCartInfo.hsc)
word retries=3;
// ----------------------------------------------------------------------------------------
// Based on the first character of the cart name, we will pick a virtual high score slot
// This is mainly to spread out the increasingly crowded HSC memory which only supports
// a total of 69 entries (and this includes difficulty settings on a per game basis).
// ----------------------------------------------------------------------------------------
if (toupper(cartridge_filename[0]) <= 'C') hs_virtual_slot = 0;
else if (toupper(cartridge_filename[0]) <= 'F') hs_virtual_slot = 1;
else if (toupper(cartridge_filename[0]) <= 'J') hs_virtual_slot = 2;
else if (toupper(cartridge_filename[0]) <= 'N') hs_virtual_slot = 3;
else if (toupper(cartridge_filename[0]) <= 'Q') hs_virtual_slot = 4;
else if (toupper(cartridge_filename[0]) <= 'T') hs_virtual_slot = 5;
else if (toupper(cartridge_filename[0]) <= 'Z') hs_virtual_slot = 6;
else hs_virtual_slot = 7;
// -------------------------------------------------
// Start with a blank (0xFF) SRAM block in memory
// -------------------------------------------------
for( uint i = 0; i < HS_SRAM_SIZE; i++ )
{ {
memory_Write( HS_SRAM_START + i, 0xFF); return false; // If we didn't load the high score cartridge, or don't have an HSC enabled cart: don't save.
}
make_hsc_name(); // HSC filename is same as base filename with .hsc extension
// Read back the .hsc file
FILE *handle = fopen(szName, "rb");
if (handle != NULL)
{
fread(high_score_sram, HS_SRAM_SIZE, 1, handle);
fclose(handle);
}
else
{
// No .hsc file was available... so set the SRAM to a known good init value
memcpy(high_score_sram, A7800DS_00_sram, HS_SRAM_SIZE);
}
// Copy from SRAM buffer to main memory for use by the game
for(uint i = 0; i < HS_SRAM_SIZE; i++)
{
memory_Write(HS_SRAM_START + i, high_score_sram[i]);
} }
while (status == false) bHSC_dirty = 0; // We don't consider the init of SRAM directly above to be a 'write' (no need to persist)
{
FILE* file = fopen("/data/A7800DS.sram", "rb" );
if( file == NULL )
{
memset(high_score_sram, 0xFF, HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS);
status = false;
}
else
{
word sram_file_size = fread( high_score_sram, 1, HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS, file );
if (sram_file_size == HS_SRAM_SIZE) // Older 2K format... replicate this across the new 8 virtual slots!
{
for (u8 i=1; i<VIRTUAL_SRAM_BLOCKS; i++)
{
memcpy(high_score_sram[i], high_score_sram[0], HS_SRAM_SIZE);
}
sram_file_size = HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS;
}
// Make sure the SRAM memory file is exactly the right size...
if( sram_file_size == HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS )
{
last_hs_chksum = 0;
for( uint i = 0; i < HS_SRAM_SIZE; i++ )
{
memory_Write( HS_SRAM_START + i, high_score_sram[hs_virtual_slot][i] );
last_hs_chksum += high_score_sram[hs_virtual_slot][i];
}
status = true;
}
else // Something is wrong... just blank out the SRAM area and let the HSC initialize it
{
memset(high_score_sram, 0xFF, HS_SRAM_SIZE*VIRTUAL_SRAM_BLOCKS);
status = false;
}
fclose(file);
}
if (--retries == 0) break;
}
return status; return true; // HSC SRAM is ready to go!
} }
/* /*
@ -212,12 +280,11 @@ static bool cartridge_LoadHighScoreSram(void)
* *
* return Whether the load was successful * return Whether the load was successful
*/ */
bool cartridge_LoadHighScoreCart(void) bool cartridge_LoadHighScoreCart(void)
{ {
if( !myCartInfo.hsc || myCartInfo.region != 0 ) if( !myCartInfo.hsc || myCartInfo.region != 0 )
{ {
// Only load the cart if it is enabled and the region is NTSC return false; // Only load the cart if it is enabled and the region is NTSC
return false;
} }
FILE* file = fopen("highscore.rom", "rb" ); FILE* file = fopen("highscore.rom", "rb" );
@ -226,21 +293,24 @@ bool cartridge_LoadHighScoreCart(void)
if (file == NULL) file = fopen("/roms/bios/highscore.rom", "rb" ); if (file == NULL) file = fopen("/roms/bios/highscore.rom", "rb" );
if (file == NULL) file = fopen("/data/bios/highscore.rom", "rb" ); if (file == NULL) file = fopen("/data/bios/highscore.rom", "rb" );
if( file != NULL ) if(file != NULL)
{ {
fread(high_score_rom, 1, HSC_CART_ROM_SIZE, file ); fread(high_score_rom, 1, HSC_CART_ROM_SIZE, file);
cartridge_LoadHighScoreSram(); for( uint i = 0; i < HSC_CART_ROM_SIZE; i++ )
for( uint i = 0; i < HSC_CART_ROM_SIZE; i++ ) {
{ memory_Write(0x3000 + i, high_score_rom[i]);
memory_Write( 0x3000 + i, high_score_rom[i] ); }
} high_score_cart_loaded = true;
high_score_cart_loaded = true;
// Now read in the associated .hsc SRAM file (or set SRAM to defaults)
cartridge_LoadHighScoreSram();
} }
else else
{ {
high_score_cart_loaded = false; high_score_cart_loaded = false;
} }
return high_score_cart_loaded; return high_score_cart_loaded;
} }
// End of file

View file

@ -36,6 +36,7 @@
extern bool cartridge_LoadHighScoreCart(void); extern bool cartridge_LoadHighScoreCart(void);
extern bool cartridge_SaveHighScoreSram(void); extern bool cartridge_SaveHighScoreSram(void);
extern bool cartridge_SaveHighScoreSram(void);
extern byte high_score_cart_loaded;
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -35,8 +35,8 @@
#include "shared.h" #include "shared.h"
extern void maria_Reset( ); extern void maria_Reset( );
extern ITCM_CODE void maria_RenderScanline(void); extern void maria_RenderScanline(void);
extern ITCM_CODE void maria_RenderScanlineTOP(void); extern void maria_RenderScanlineTOP(void);
extern void maria_Clear( ); extern void maria_Clear( );
extern word* maria_surface; extern word* maria_surface;
extern uint maria_scanline; extern uint maria_scanline;

View file

@ -5,7 +5,7 @@
// //
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Copyright 2005 Greg Stanton // Copyright 2005 Greg Stanton
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or // the Free Software Foundation; either version 2 of the License, or
@ -22,292 +22,348 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Memory.cpp // Memory.cpp
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#include "ProSystem.h" #include "ProSystem.h"
#include "Memory.h" #include "Memory.h"
#include "Maria.h" #include "Maria.h"
#include "Database.h" #include "Database.h"
byte memory_ram[MEMORY_SIZE] ALIGN(32) = {0}; byte memory_ram[MEMORY_SIZE] ALIGN(32) = {0};
u8 *is_memory_writable = (u8*)0x06820000; u8 is_memory_writable[256] __attribute__((section(".dtcm")));
u32 snes_bit_pos = 0;
extern bool ram_dirty[]; u8 bHSC_dirty = 0;
extern bool write_only_pokey_at_4000; u8 bINPTCTRL_locked = 0;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Reset // Reset
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void memory_Reset( ) void memory_Reset()
{ {
uint index; uint index;
for(index = 0; index < MEMORY_SIZE; index++) { for(index = 0x4000; index < MEMORY_SIZE; index++)
memory_ram[index] = 0; {
is_memory_writable[index] = 0; memory_ram[index] = 0xff;
} is_memory_writable[index >> 8] = 0;
u16 *ptr = (u16*)is_memory_writable; }
for(index = 0; index < 16384/2; index++) { for(index = 0; index < 0x4000; index++)
ptr[index] = 0xFFFF; {
} memory_ram[index] = 0x00;
is_memory_writable[index >> 8] = 1;
}
bHSC_dirty = 0;
snes_bit_pos = 0;
bINPTCTRL_locked = 0;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Read // Read
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE byte memory_Read_Slower(word address) ITCM_CODE byte memory_Read_Slower(word address)
{ {
if (address & 0x8000) return memory_ram[address]; extern u8 write_only_pokey_at_4000;
else if ((address & 0xFFFC) == 0x284)
{ if((address & 0xFFFC) == 0x284)
if (address & 0x1) {
{ if(address & 0x1)
byte tmp_byte = memory_ram[INTFLG]; {
memory_ram[INTFLG] &= 0x7f; byte tmp_byte = memory_ram[INTFLG];
return tmp_byte; memory_ram[INTFLG] &= 0x7f;
} return tmp_byte;
else }
{ else
memory_ram[INTFLG] &= 0x7f; {
return memory_ram[INTIM]; memory_ram[INTFLG] &= 0x7f;
} return memory_ram[INTIM];
} }
else if (myCartInfo.pokeyType) }
{ else if(myCartInfo.pokeyType)
if (myCartInfo.pokeyType == POKEY_AT_4000) {
{ if(myCartInfo.pokeyType == POKEY_AT_4000)
if (((address & 0xFFF0) == 0x4000) && (!write_only_pokey_at_4000)) return pokey_GetRegister(address); {
} if(((address & 0xFFF0) == 0x4000) && (!write_only_pokey_at_4000)) return pokey_GetRegister(address);
else }
{ else
// Not quite accurate as it will catch anything from 0x440 to 0x4C0 but that's {
// good enough as nothing else should be mapped in this region except POKEY. // Not quite accurate as it will catch anything from 0x440 to 0x4C0 but that's
if ((address & 0xFFC0) == 0x440) return pokey_GetRegister(0x4000 | (address & 0xF)); // good enough as nothing else should be mapped in this region except POKEY.
if ((address & 0xFFF0) == 0x800) return pokey_GetRegister(0x4000 | (address & 0xF)); if((address & 0xFFC0) == 0x440) return pokey_GetRegister(0x4000 | (address & 0xF));
} if((address & 0xFFF0) == 0x800) return pokey_GetRegister(0x4000 | (address & 0xF));
} }
return memory_ram[address]; }
return memory_ram[address];
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Write // Write
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void memory_Write(word address, byte data) ITCM_CODE void memory_Write(word address, byte data)
{ {
extern u32 bg32, maria_charbase; extern u32 bg32, maria_charbase;
extern u8 bg8; extern u8 bg8;
if (myCartInfo.pokeyType) if(unlikely(myCartInfo.pokeyType))
{
if (myCartInfo.pokeyType == POKEY_AT_4000)
{
if ((address & 0xFFF0) == 0x4000)
{
pokey_SetRegister(address, data);
return;
}
}
else
{
// Not quite accurate as it will catch anything from 0x440 to 0x4C0 but that's
// good enough as nothing else should be mapped in this region except POKEY.
if ((address & 0xFFC0) == 0x440)
{
pokey_SetRegister(0x4000 | (address & 0x0F), data);
return;
}
if ((address & 0xFFF0) == 0x800) // Pokey @800
{
pokey_SetRegister(0x4000 | (address & 0x0F), data);
return;
}
}
}
if (((u8*)0x06820000)[address]) // We use 64K of faster VRAM to hold the bits to tell us if this area is writable... speeds up things slightly...
{
// ---------------------------------------------------------------------------------------
// If this write would be in normal (non bankset) memory, we need to keep the bankset
// memory up to date as well... This speeds up processing in Maria for banksets handling.
// ---------------------------------------------------------------------------------------
extern byte banksets_memory[]; extern u16 banksets_mask;
if (!(address & banksets_mask)) banksets_memory[address] = data;
if ((address & 0xF800)) // Base RAM is at 0x1800 so this will find anything that is RAM...
{ {
// For banking RAM we need to keep the shadow up to date. if(myCartInfo.pokeyType == POKEY_AT_4000)
if ((address & 0xC000) == 0x4000) {
{ if((address & 0xFFF0) == 0x4000)
extern u8 *shadow_ram;
shadow_ram[address] = data;
if (myCartInfo.cardtype == CARTRIDGE_TYPE_FRACTALUS)
{ {
// Special EXRAM/A8 handling... mirror ram pokey_SetRegister(address, data);
memory_ram[address ^0x0100] = data; return;
} }
} }
memory_ram[address] = data; else
return;
}
if (address >= 0x460 && address < 0x480) return; // XM/Yamaha is mapped into this area... do not respond to it as we are not XM capable (yet)
switch(address) {
case INPTCTRL:
if(data == 22 && cartridge_IsLoaded( )) {
cartridge_Store( );
}
break;
case INPT0:
break;
case INPT1:
break;
case INPT2:
break;
case INPT3:
break;
case INPT4:
break;
case INPT5:
break;
case BACKGRND:
memory_ram[BACKGRND] = data;
bg8 = data;
bg32 = data | (data<<8) | (data<<16) | (data<<24);
break;
case CHARBASE:
memory_ram[CHARBASE] = data;
maria_charbase = data;
break;
case AUDC0:
tia_audc[0] = data & 15;
tia_MemoryChannel(0);
break;
case AUDC1:
tia_audc[1] = data & 15;
tia_MemoryChannel(1);
break;
case AUDF0:
tia_audf[0] = data & 31;
tia_MemoryChannel(0);
break;
case AUDF1:
tia_audf[1] = data & 31;
tia_MemoryChannel(1);
break;
case AUDV0:
tia_audv[0] = (data & 15) << 2;
tia_MemoryChannel(0);
break;
case AUDV1:
tia_audv[1] = (data & 15) << 2;
tia_MemoryChannel(1);
break;
case WSYNC:
memory_ram[WSYNC] = true;
riot_and_wsync |= 0x01;
break;
case SWCHB:
/*gdement: Writing here actually writes to DRB inside the RIOT chip.
This value only indirectly affects output of SWCHB.*/
riot_SetDRB(data);
break;
case SWCHA:
riot_SetDRA(data);
break;
case TIM1T:
case TIM1T | 0x8:
riot_SetTimer(TIM1T, data);
break;
case TIM8T:
case TIM8T | 0x8:
riot_SetTimer(TIM8T, data);
break;
case TIM64T:
case TIM64T | 0x8:
riot_SetTimer(TIM64T, data);
break;
case T1024T:
case T1024T | 0x8:
riot_SetTimer(T1024T, data);
break;
default:
memory_ram[address] = data;
#ifdef RAM_MIRRORS_ENABLED // Technically the RAM mirrors are here but we don't really care... we assume anyone using a mirror will read back from that same mirror location.
if (address >= 8256)
{ {
// 0x2040 -> 0x20ff (0x2000) // Not quite accurate as it will catch anything from 0x440 to 0x4C0 but that's
if(address >= 8256 && address <= 8447) // good enough as nothing else should be mapped in this region except POKEY.
{ if((address & 0xFFC0) == 0x440)
memory_ram[address - 8192] = data; {
} pokey_SetRegister(0x4000 | (address & 0x0F), data);
// 0x2140 -> 0x21ff (0x2000) return;
else if(address >= 8512 && address <= 8703) }
{ if((address & 0xFFF0) == 0x800) // Pokey @800
memory_ram[address - 8192] = data; {
} pokey_SetRegister(0x4000 | (address & 0x0F), data);
return;
}
} }
else if (address <= 511)
{
// 0x40 -> 0xff (0x2000)
if(address >= 64 && address <= 255)
{
memory_ram[address + 8192] = data;
}
// 0x140 -> 0x1ff (0x2000)
else if(address >= 320 && address <= 511)
{
memory_ram[address + 8192] = data;
}
}
#endif
break;
} }
}
else if(is_memory_writable[address >> 8])
{ {
cartridge_Write(address, data); // ---------------------------------------------------------------------------------------
} // If this write would be in normal (non bankset) memory, we need to keep the bankset
// memory up to date as well... This speeds up processing in Maria for banksets handling.
// ---------------------------------------------------------------------------------------
extern byte banksets_memory[];
extern u16 banksets_mask;
if(!(address & banksets_mask)) banksets_memory[address] = data;
if((address & 0x5000)) // This will catch RAM at 0x4000 and HSC at 0x1000
{
// For banking RAM we need to keep the shadow up to date.
if((address & 0x5000) == 0x4000)
{
extern u8 * shadow_ram;
shadow_ram[address] = data;
if(myCartInfo.cardtype == CARTRIDGE_TYPE_FRACTALUS)
{
// Special EXRAM/A8 handling... mirror ram
memory_ram[address ^ 0x0100] = data;
}
memory_ram[address] = data;
return;
}
else if((address & 0xF800) == 0x1000) // HSC RAM - set the dirty bit so we persist the .hsc file in the main loop
{
if(memory_ram[address] != data)
{
memory_ram[address] = data;
if(address != 0x1007 && (address < 0x17FA)) // Don't count the 'function' address nor the temp score...
{
bHSC_dirty = 1;
}
}
return;
}
}
else if((address & 0xFFE0) == 0x460) return; // XM/Yamaha is mapped into 460 - 47F... do not respond to it as we are not XM capable (yet)
// ---------------------------------------------------------------------
// Until we are 'locked' into a mode, address range 0x00 to 0x1f
// (overlapping the TIA) will respond as write-only register INPTCTRL
// ---------------------------------------------------------------------
if((address & 0xFCFF) <= 0x1f)
{
if(!bINPTCTRL_locked)
{
if(data & 0x04) cartridge_Store();
else bios_Store();
}
if(data & 0x01) bINPTCTRL_locked = 1;
}
switch(address)
{
case INPT0:
break;
case INPT1:
break;
case INPT2:
break;
case INPT3:
break;
case INPT4:
break;
case INPT5:
break;
case BACKGRND:
memory_ram[BACKGRND] = data;
bg8 = data;
bg32 = data | (data << 8) | (data << 16) | (data << 24);
break;
case CHARBASE:
memory_ram[CHARBASE] = data;
maria_charbase = data;
break;
case AUDC0:
tia_audc[0] = data & 15;
tia_MemoryChannel(0);
break;
case AUDC1:
tia_audc[1] = data & 15;
tia_MemoryChannel(1);
break;
case AUDF0:
tia_audf[0] = data & 31;
tia_MemoryChannel(0);
break;
case AUDF1:
tia_audf[1] = data & 31;
tia_MemoryChannel(1);
break;
case AUDV0:
tia_audv[0] = (data & 15) << 2;
tia_MemoryChannel(0);
break;
case AUDV1:
tia_audv[1] = (data & 15) << 2;
tia_MemoryChannel(1);
break;
case WSYNC:
memory_ram[WSYNC] = true;
riot_and_wsync |= 0x01;
break;
case SWCHB:
/*gdement: Writing here actually writes to DRB inside the RIOT chip.
This value only indirectly affects output of SWCHB.*/
riot_SetDRB(data);
break;
case SWCHA:
if(myCartInfo.cardctrl1 == SNES)
{
extern byte riot_dra;
if((data & 0x20) != (riot_dra & 0x20)) // Change of Latch state
{
snes_bit_pos = 0;
if(snes_adaptor & (1 << snes_bit_pos)) memory_ram[INPT4] |= 0x80;
else memory_ram[INPT4] &= 0x7F;
}
if((data & 0x10) != (riot_dra & 0x10)) // Change of Clock state
{
if(data & 0x10) // Clock going High
{
snes_bit_pos++;
}
else // Clock going low
{
if(snes_bit_pos >= 17) snes_bit_pos = 0;
if(snes_adaptor & (1 << snes_bit_pos)) memory_ram[INPT4] |= 0x80;
else memory_ram[INPT4] &= 0x7F;
}
}
}
riot_SetDRA(data);
break;
case TIM1T:
case TIM1T | 0x8:
riot_SetTimer(TIM1T, data);
break;
case TIM8T:
case TIM8T | 0x8:
riot_SetTimer(TIM8T, data);
break;
case TIM64T:
case TIM64T | 0x8:
riot_SetTimer(TIM64T, data);
break;
case T1024T:
case T1024T | 0x8:
riot_SetTimer(T1024T, data);
break;
default:
memory_ram[address] = data;
#ifdef RAM_MIRRORS_ENABLED
// ------------------------------------------------------
// Handle the RAM mirrors that the 7800 presents...
//
// 0x2040 - 0x20FF RAM block 0 (mirror of 0x0040-00FF)
// 0x2140 - 0x21FF RAM block 1 (mirror of 0x0140-01FF)
// ------------------------------------------------------
if(address >= 0x2040)
{
// 0x2040 -> 0x20ff (0x2000)
if(address <= 0x20FF)
{
memory_ram[address & 0x00FF] = data;
}
// 0x2140 -> 0x21ff (0x2100)
else if(address >= 0x2140 && address <= 0x21FF)
{
memory_ram[address & 0x01FF] = data;
}
}
else if(address < 0x200)
{
// 0x40 -> 0xff (0x2000)
if(address >= 0x40 && address <= 0xFF)
{
memory_ram[address | 0x2000] = data;
}
// 0x140 -> 0x1ff (0x2100)
else if(address >= 0x140 && address <= 0x1FF)
{
memory_ram[address | 0x2000] = data;
}
}
#endif
break;
}
}
else
{
cartridge_Write(address, data);
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// WriteROM // WriteROM
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void memory_WriteROM(word address, u32 size, const byte* data) ITCM_CODE void memory_WriteROM(word address, u32 size, const byte * data)
{ {
u32* ramPtr = (u32*)&memory_ram[address]; memcpy( & memory_ram[address], data, size);
u32* romPtr = (u32*)&is_memory_writable[address]; memset( & is_memory_writable[address >> 8], 0x00, size >> 8);
u32* dataPtr = (u32*)data;
for (u32 i=0; i<(size>>2); i++)
{
*ramPtr++ = *dataPtr++;
*romPtr++ = 0x00000000;
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// WriteROMFast (assumes is_memory_writable[] already set properly) // WriteROMFast (assumes is_memory_writable[] already set properly)
// size is already in multiples of u32 dwords // size is already in multiples of 8x u32 dwords (32 bytes)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void memory_WriteROMFast(word address, u32 size, const u32* data) ITCM_CODE void memory_WriteROMFast(word address, u32 size, const u32 * data)
{ {
u32* ramPtr = (u32*)&memory_ram[address]; u32 * ramPtr = (u32 * ) & memory_ram[address];
u32* dataPtr = (u32*)data; u32 * dataPtr = (u32 * ) data;
u16 size2 = size; u32 size2 = size;
do do {
{ * ramPtr++ = * dataPtr++;
*ramPtr++ = *dataPtr++; * ramPtr++ = * dataPtr++;
*ramPtr++ = *dataPtr++; * ramPtr++ = * dataPtr++;
*ramPtr++ = *dataPtr++; * ramPtr++ = * dataPtr++;
*ramPtr++ = *dataPtr++; * ramPtr++ = * dataPtr++;
} * ramPtr++ = * dataPtr++;
while (--size2); * ramPtr++ = * dataPtr++;
* ramPtr++ = * dataPtr++;
}
while(--size2);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ClearROM // ClearROM
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void memory_ClearROM(word address, word size) void memory_ClearROM(word address, word size)
{ {
memset(&memory_ram[address], 0x00, size); memset( & memory_ram[address], 0x00, size);
memset(&is_memory_writable[address], 0xFF, size); memset( & is_memory_writable[address >> 8], 0xFF, size >> 8);
} }

View file

@ -33,16 +33,19 @@
#include "shared.h" #include "shared.h"
extern u8 bHSC_dirty;
extern byte memory_ram[MEMORY_SIZE]; extern byte memory_ram[MEMORY_SIZE];
extern void memory_Reset( ); extern void memory_Reset( );
extern ITCM_CODE byte memory_Read_Slower(word address); extern byte memory_Read_Slower(word address);
extern void memory_Write(word address, byte data); extern void memory_Write(word address, byte data);
extern void memory_WriteZP(word address, byte data);
inline byte memory_Read(word address) inline byte memory_Read(word address)
{ {
if (!(address & 0xFE00)) return memory_ram[address]; // This happens a lot... so it speeds up emulation if (address & 0x4E00) return memory_Read_Slower(address); // If these bits are set, might be POKEY access... or RIOT read
return memory_Read_Slower(address); else return memory_ram[address];
} }
extern void memory_WriteROM(word address, u32 size, const byte* data); extern void memory_WriteROM(word address, u32 size, const byte* data);

View file

@ -176,7 +176,7 @@ void pokey_Reset( )
} }
ITCM_CODE byte pokey_GetRegister(word address) byte pokey_GetRegister(word address)
{ {
byte data = 0; byte data = 0;
@ -406,8 +406,10 @@ extern u32 tiaBufIdx;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void pokey_Process(void) ITCM_CODE void pokey_Process(void)
{ {
byte* sampleCntrPtrB = ((byte*)&pokey_sampleCount[0]) + 1; if (tia_wait) return;
byte* sampleCntrPtrB = ((byte*)&pokey_sampleCount[0]) + 1;
while (1) while (1)
{ {
int currentValue; int currentValue;
@ -472,17 +474,25 @@ ITCM_CODE void pokey_Process(void)
currentValue = (currentValue << 2) + 8; currentValue = (currentValue << 2) + 8;
currentValue += TIA_Sample(); currentValue += TIA_Sample();
//currentValue = (currentValue >> 1); //currentValue = (currentValue >> 1);
if (currentValue > 127) {debug[0]++; currentValue = 127;} // Clip if (currentValue > 127) {currentValue = 127;} // Clip
tia_buffer[tiaBufIdx++] = (u16)((currentValue<<8) | currentValue); // We have filled the buffer... let the buffer drain a bit
tiaBufIdx &= (SNDLENGTH-1); if (((tiaBufIdx+1) & (SNDLENGTH-1)) == myTiaBufIdx)
{
tia_wait = (SNDLENGTH >> 2);
}
else
{
tia_buffer[tiaBufIdx++] = (u16)((currentValue<<8) | currentValue);
tiaBufIdx &= (SNDLENGTH-1);
}
return; return;
} }
} }
} }
ITCM_CODE u16 pokey_ProcessNow(void) u16 pokey_ProcessNow(void)
{ {
byte* sampleCntrPtrB = ((byte*)&pokey_sampleCount[0]) + 1; byte* sampleCntrPtrB = ((byte*)&pokey_sampleCount[0]) + 1;
@ -550,7 +560,7 @@ ITCM_CODE u16 pokey_ProcessNow(void)
currentValue = (currentValue << 2) + 8; currentValue = (currentValue << 2) + 8;
currentValue += TIA_Sample(); currentValue += TIA_Sample();
//currentValue = (currentValue >> 1); //currentValue = (currentValue >> 1);
if (currentValue > 127) {debug[0]++; currentValue = 127;} // Clip if (currentValue > 127) {currentValue = 127;} // Clip
return (u16)((currentValue << 8) | currentValue); return (u16)((currentValue << 8) | currentValue);
} }

View file

@ -34,32 +34,40 @@ uint32 bg32 __attribute__((section(".dtcm"))) = 0;
uint bRenderFrame __attribute__((section(".dtcm"))) = 0; uint bRenderFrame __attribute__((section(".dtcm"))) = 0;
#define HBLANK_BEFORE_DMA 34 // Number of cycles in a HBLANK #define CYCLES_BEFORE_DMA 28 // Number of cycles before DMA kicks in (really 7 CPU cycles)
#define CYCLES_PER_SCANLINE 454 // 454 Cycles per Scanline in an NTSC system (really 113.5 CPU cycles) #define CYCLES_PER_SCANLINE 454 // 454 Cycles per Scanline in an NTSC system (really 113.5 CPU cycles)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Reset // Reset
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void prosystem_Reset( ) void prosystem_Reset()
{ {
if(cartridge_IsLoaded( )) if(cartridge_IsLoaded())
{ {
sally_Reset( ); sally_Reset();
region_Reset( ); region_Reset();
tia_Clear( ); tia_Clear();
tia_Reset( ); tia_Reset();
pokey_Clear( ); pokey_Clear();
pokey_Reset( ); pokey_Reset();
memory_Reset( ); memory_Reset();
maria_Clear( ); maria_Clear();
maria_Reset( ); maria_Reset();
riot_Reset ( ); riot_Reset();
cartridge_LoadHighScoreCart(); cartridge_LoadHighScoreCart();
cartridge_Store( );
prosystem_cycles = sally_ExecuteRES( ); cartridge_Store(); // Always call this - it may setup some RAM or other stuff below the BIOS region...
}
// Load 7800 BIOS if available... otherwise direct load the CART
if(bios_available && !bSkipBIOS)
{
bios_Store();
bios_show_counter = myCartInfo.biosTimeout;
}
prosystem_cycles = sally_ExecuteRES();
}
} }
@ -67,117 +75,116 @@ void prosystem_Reset( )
// ExecuteFrame - this is hand-tuned for NTSC output with hard-coded // ExecuteFrame - this is hand-tuned for NTSC output with hard-coded
// NTSC frame values ... this will not work properly if a PAL ROM is used. // NTSC frame values ... this will not work properly if a PAL ROM is used.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void prosystem_ExecuteFrame(const byte* input) ITCM_CODE void prosystem_ExecuteFrame(const byte * input)
{ {
extern u16 gTotalAtariFrames; extern u16 gTotalAtariFrames;
extern word *framePtr; extern word * framePtr;
extern uint maria_cycles; extern uint maria_cycles;
gTotalAtariFrames++; gTotalAtariFrames++;
bRenderFrame = 0; bRenderFrame = 0;
riot_SetInput(input); riot_SetInput(input);
// ------------------------------------------------------------ // ---------------------------------------------------------------------
// Handle the TOP area first... speeds up processing below... // Handle the VERTICAL BLANK area first... speeds up processing below...
// ------------------------------------------------------------ // ---------------------------------------------------------------------
for (maria_scanline = 1; maria_scanline <= 16; maria_scanline++) memory_ram[MSTAT] = 128; // Into the Vertical Blank...
{
prosystem_cycles = 0; // -------------------------------------------------------------------------------------------
// Note: this is not accurate. It should be 263 scanlines total but it doesn't work for all
if (maria_scanline & 0x10) // games at 263 so we've gone with 262 for maximum compatibility. This is likely due to some
// emulation cycle counting inaccuracies. At 263, some games run too fast (Asteroids, Deluxe)
// and some crash (Robotron) and some issues with Pole Position II and the joystick selection
// of a course to play. I've also seen graphical glitches in Crossbow when loaded via the
// BIOS and all kinds of graphical oddities in Xenophobe. Be very careful if you change this...
// be sure you understand the consequences (and this developer doesn't... so be warned!).
// -------------------------------------------------------------------------------------------
for(maria_scanline = 1; maria_scanline <= 21; maria_scanline++)
{ {
memory_ram[MSTAT] = 0; prosystem_cycles = 0;
framePtr = (word*)(maria_surface);
sally_Execute(HBLANK_BEFORE_DMA); if(maria_scanline == 21) // Maria can start to do her thing... We've had 20 VBLANK scanlines
{
maria_RenderScanlineTOP( ); memory_ram[MSTAT] = 0; // Out of the vertical blank
framePtr = (word * )(maria_surface);
// Cycle Stealing happens here... with a fudge adjustment... sally_Execute(CYCLES_BEFORE_DMA);
if (maria_cycles > 0) maria_cycles += (int)myCartInfo.dma_adjust;
prosystem_cycles += maria_cycles; maria_RenderScanlineTOP();
if(riot_and_wsync&2) riot_UpdateTimer( maria_cycles >> 2 );
// Cycle Stealing happens here...
prosystem_cycles += ((maria_cycles + 3) >> 2) << 2; // Always a multiple of 4
if(riot_and_wsync & 2) riot_UpdateTimer(maria_cycles >> 2);
}
else
{
sally_Execute(CYCLES_BEFORE_DMA);
}
sally_Execute(CYCLES_PER_SCANLINE);
if(myCartInfo.pokeyType) // If pokey enabled, we process 1 pokey sample and 1 TIA sample. Good enough.
{
pokey_Process();
pokey_Scanline();
}
else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz
} }
else
{
sally_Execute(HBLANK_BEFORE_DMA);
}
sally_Execute(CYCLES_PER_SCANLINE);
if(myCartInfo.pokeyType) // If pokey enabled, we process 1 pokey sample and 1 TIA sample. Good enough.
{
pokey_Process();
pokey_Scanline();
} else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz (or half that for DS LITE)
}
// ------------------------------------------------------------
// Now handle the Main display area...
// ------------------------------------------------------------
for (; maria_scanline < 258; maria_scanline++)
{
prosystem_cycles = 0;
if (maria_scanline == 25)
{
// -----------------------------------------------------------------------------------
// At line 25 we can start to render the scanlines if we are not skipping this frame.
// -----------------------------------------------------------------------------------
bRenderFrame = gTotalAtariFrames & frameSkipMask;
}
else if (maria_scanline == 247)
{
bRenderFrame = 0; // At line 247 we can stop rendering frames...
}
sally_Execute(HBLANK_BEFORE_DMA);
maria_RenderScanline( ); // ------------------------------------------------------------
// Now handle the Main display area...
// Cycle Stealing happens here... with a fudge adjustment... // ------------------------------------------------------------
if (maria_cycles > 0) maria_cycles += (int)myCartInfo.dma_adjust; for(; maria_scanline < 263; maria_scanline++)
prosystem_cycles += maria_cycles;
if(riot_and_wsync&2) riot_UpdateTimer( maria_cycles >> 2 );
sally_Execute(CYCLES_PER_SCANLINE);
if(myCartInfo.pokeyType) // If pokey enabled, we process 1 pokey sample and 1 TIA sample. Good enough.
{ {
pokey_Process(); prosystem_cycles = 0;
pokey_Scanline();
} else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz (or half that for DS LITE)
}
memory_ram[MSTAT] = 128;
// ---------------------------------------------------------------------------
// And at the bottom, we no longer have to render anything Maria-related...
// ---------------------------------------------------------------------------
for (; maria_scanline < 262; maria_scanline++)
{
prosystem_cycles = 0;
sally_Execute(HBLANK_BEFORE_DMA);
sally_Execute(CYCLES_PER_SCANLINE); if(maria_scanline >= 30)
if(myCartInfo.pokeyType) // If pokey enabled, we process 1 pokey sample and 1 TIA sample. Good enough. {
{ // --------------------------------------------------------------------------
pokey_Process(); // We can start to render the scanlines if we are not skipping this frame.
pokey_Scanline(); // The DS/DSi only has 192 vertical pixels and we can perform some hardware
} else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz (or half that for DS LITE) // scaling but there is a point at which we really can't show any more lines.
} // To that end, we'll render ~223 lines which is good enough for most games.
// If a game uses more overscan area - those scanlines won't show on screen.
// ---------------------------------------------------------------------------
if(maria_scanline >= 253)
{
bRenderFrame = 0; // We can stop rendering frames... DS can't show it anyway with limited vertical resolution.
}
else
{
bRenderFrame = gTotalAtariFrames & frameSkipMask;
}
}
sally_Execute(CYCLES_BEFORE_DMA);
maria_RenderScanline();
// Cycle Stealing happens here...
prosystem_cycles += ((maria_cycles + 3) >> 2) << 2; // Always a multiple of 4
if(riot_and_wsync & 2) riot_UpdateTimer(maria_cycles >> 2);
sally_Execute(CYCLES_PER_SCANLINE);
if(myCartInfo.pokeyType) // If pokey enabled, we process 1 pokey sample and 1 TIA sample. Good enough.
{
pokey_Process();
pokey_Scanline();
}
else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Close // Close
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void prosystem_Close( ) { void prosystem_Close()
cartridge_Release( ); {
maria_Reset( ); cartridge_Release();
maria_Clear( ); maria_Reset();
memory_Reset( ); maria_Clear();
tia_Reset( ); memory_Reset();
tia_Clear( ); tia_Reset();
tia_Clear();
} }

View file

@ -40,11 +40,14 @@
#include "shared.h" #include "shared.h"
//#define KANGAROO_MODE_SUPPORTED 1 // Uncomment this for KANGAROO support. Slightly slower and virtually no game uses it... but more accurate #define likely(x) __builtin_expect(!!(x), 1)
//#define RAM_MIRRORS_ENABLED 1 // Uncomment if you want to have RAM mirrors handled (slower and no games need it as of this writing) #define unlikely(x) __builtin_expect(!!(x), 0)
#define RAM_MIRRORS_ENABLED 1 // Uncomment if you want to have RAM mirrors handled (slower and no games need it as of this writing)
extern int debug[]; extern int debug[];
extern u8 isDS_LITE; extern u8 isDS_LITE;
extern u8 bSkipBIOS;
// Difficulty switches... // Difficulty switches...
#define DIFF_A 0 #define DIFF_A 0
@ -55,5 +58,7 @@ extern void prosystem_ExecuteFrame(const byte* input);
extern void prosystem_Close( ); extern void prosystem_Close( );
extern byte prosystem_frame; extern byte prosystem_frame;
extern uint prosystem_cycles; extern uint prosystem_cycles;
extern void Trace(word data);
extern void Trace2(word addr, u8 data);
#endif #endif

View file

@ -20,7 +20,7 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Region.h // Region.c
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#include "Region.h" #include "Region.h"
#include "Database.h" #include "Database.h"
@ -231,11 +231,6 @@ const byte REGION_PALETTE_NTSC_CRT_HOT[ ] = {
}; };
// ----------------------------------------------------------------------------
// Reset
// ----------------------------------------------------------------------------
extern uint video_height;
// --------------------------------- // ---------------------------------
// We only support NTSC for A7800DS // We only support NTSC for A7800DS
// --------------------------------- // ---------------------------------
@ -244,5 +239,4 @@ void region_Reset( )
if(myCartInfo.palette == 0) palette_Load(REGION_PALETTE_NTSC_CRT_COOL); if(myCartInfo.palette == 0) palette_Load(REGION_PALETTE_NTSC_CRT_COOL);
if(myCartInfo.palette == 1) palette_Load(REGION_PALETTE_NTSC_CRT_WARM); if(myCartInfo.palette == 1) palette_Load(REGION_PALETTE_NTSC_CRT_WARM);
if(myCartInfo.palette == 2) palette_Load(REGION_PALETTE_NTSC_CRT_HOT); if(myCartInfo.palette == 2) palette_Load(REGION_PALETTE_NTSC_CRT_HOT);
video_height = 234;
} }

View file

@ -24,17 +24,17 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#include "Riot.h" #include "Riot.h"
uint riot_and_wsync __attribute__((section(".dtcm"))) = 0x00; uint riot_and_wsync __attribute__((section(".dtcm"))) = 0x00;
int riot_timer __attribute__((section(".dtcm"))) = TIM64T; int riot_timer __attribute__((section(".dtcm"))) = TIM64T;
int riot_intervals __attribute__((section(".dtcm"))); int riot_intervals __attribute__((section(".dtcm"))) = 0;
byte riot_dra __attribute__((section(".dtcm"))) = 0; byte riot_dra __attribute__((section(".dtcm"))) = 0;
byte riot_drb __attribute__((section(".dtcm"))) = 0; byte riot_drb __attribute__((section(".dtcm"))) = 0;
uint riot_elapsed __attribute__((section(".dtcm"))); uint riot_elapsed __attribute__((section(".dtcm"))) = 0;
int riot_currentTime __attribute__((section(".dtcm"))); int riot_currentTime __attribute__((section(".dtcm"))) = 0;
uint riot_clocks __attribute__((section(".dtcm"))); uint riot_clocks __attribute__((section(".dtcm"))) = 0;
uint riot_shift __attribute__((section(".dtcm")))=0; uint riot_shift __attribute__((section(".dtcm"))) = 0;
void riot_Reset(void) { void riot_Reset(void) {
riot_SetDRA(0); riot_SetDRA(0);
@ -194,55 +194,6 @@ ITCM_CODE void riot_SetInput(const byte* input) {
memory_ram[INPT2] &= 0x7f; memory_ram[INPT2] &= 0x7f;
} }
} }
/*
if (input[0x00] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x80; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x80 ;
if (input[0x01] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x40; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x40;
if (input[0x02] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x20; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x20;
if (input[0x03] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x10; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x10;
if(input[0x04]) {
memory_ram[INPT0] = memory_ram[INPT0] | 0x80;
memory_ram[INPT4] = memory_ram[INPT4] &~ 0x80;
}
else {
memory_ram[INPT0] = memory_ram[INPT0] &~ 0x80;
memory_ram[INPT4] = memory_ram[INPT4] | 0x80;
}
if(input[0x05]) {
memory_ram[INPT1] = memory_ram[INPT1] | 0x80;
memory_ram[INPT4] = memory_ram[INPT4] &~ 0x80;
}
else {
memory_ram[INPT1] = memory_ram[INPT1] &~ 0x80;
memory_ram[INPT4] = memory_ram[INPT4] | 0x80;
}
if (input[0x06] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x08; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x08;
if (input[0x07] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x04; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x04;
if (input[0x08] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x02; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x02;
if (input[0x09] ) memory_ram[SWCHA] = memory_ram[SWCHA] &~ 0x01; else memory_ram[SWCHA] = memory_ram[SWCHA] | 0x01;
if(input[0x0a]) {
memory_ram[INPT2] = memory_ram[INPT2] | 0x80;
memory_ram[INPT5] = memory_ram[INPT5] &~ 0x80;
}
else {
memory_ram[INPT2] = memory_ram[INPT2] &~ 0x80;
memory_ram[INPT5] = memory_ram[INPT5] | 0x80;
}
if(input[0x0b]) {
memory_ram[INPT3] = memory_ram[INPT3] | 0x80;
memory_ram[INPT5] = memory_ram[INPT5] &~ 0x80;
}
else {
memory_ram[INPT3] = memory_ram[INPT3] &~ 0x80;
memory_ram[INPT5] = memory_ram[INPT5] | 0x80;
}
if (input[0x0c]) memory_ram[SWCHB] = memory_ram[SWCHB] &~ 0x01; else memory_ram[SWCHB] = memory_ram[SWCHB] | 0x01;
if (input[0x0d]) memory_ram[SWCHB] = memory_ram[SWCHB] &~ 0x02; else memory_ram[SWCHB] = memory_ram[SWCHB] | 0x02;
if (input[0x0e]) memory_ram[SWCHB] = memory_ram[SWCHB] &~ 0x08; else memory_ram[SWCHB] = memory_ram[SWCHB] | 0x08;
if (input[0x0f]) memory_ram[SWCHB] = memory_ram[SWCHB] &~ 0x40; else memory_ram[SWCHB] = memory_ram[SWCHB] | 0x40;
if (input[0x10]) memory_ram[SWCHB] = memory_ram[SWCHB] &~ 0x80; else memory_ram[SWCHB] = memory_ram[SWCHB] | 0x80;
*/
} }
/*********************************************************************************** /***********************************************************************************
@ -266,7 +217,7 @@ void riot_SetDRB(byte data) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// SetTimer // SetTimer
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void riot_SetTimer(word timer, byte intervals) void riot_SetTimer(word timer, byte intervals)
{ {
riot_timer = timer; riot_timer = timer;
riot_intervals = intervals; riot_intervals = intervals;

View file

@ -40,6 +40,7 @@ extern void riot_UpdateTimer(byte cycles);
extern uint riot_and_wsync; extern uint riot_and_wsync;
extern int riot_timer; extern int riot_timer;
extern int riot_intervals; extern int riot_intervals;
extern u32 snes_adaptor;
extern void riot_SetDRA(byte data); extern void riot_SetDRA(byte data);
extern void riot_SetDRB(byte data); extern void riot_SetDRB(byte data);

View file

@ -31,10 +31,9 @@ byte sally_y __attribute__((section(".dtcm"))) = 0;
uint sally_p __attribute__((section(".dtcm"))) = 0; uint sally_p __attribute__((section(".dtcm"))) = 0;
uint sally_s __attribute__((section(".dtcm"))) = 0; uint sally_s __attribute__((section(".dtcm"))) = 0;
PCUnion sally_pc __attribute__((section(".dtcm"))) = {0}; static PCUnion sally_pc __attribute__((section(".dtcm"))) = {0};
static PCUnion sally_address __attribute__((section(".dtcm"))); static PCUnion sally_address __attribute__((section(".dtcm")));
static uint sally_cyclesX4 __attribute__((section(".dtcm"))); static uint sally_cyclesX4 __attribute__((section(".dtcm")));
byte last_illegal_opcode = 0; byte last_illegal_opcode = 0;
@ -56,24 +55,25 @@ static const Vector SALLY_RES = {65533, 65532};
static const Vector SALLY_NMI = {65531, 65530}; static const Vector SALLY_NMI = {65531, 65530};
static const Vector SALLY_IRQ = {65535, 65534}; static const Vector SALLY_IRQ = {65535, 65534};
// Cycle table assumes jump is taken - the Sally_Branch() handler will back off 4 cycles if needed
static uint SALLY_CYCLESX4[256] __attribute__((section(".dtcm"))) = static uint SALLY_CYCLESX4[256] __attribute__((section(".dtcm"))) =
{ {
7*4,6*4,0*4,0*4,0*4,3*4,5*4,0*4,3*4,2*4,2*4,2*4,0*4,4*4,6*4,0*4, //00 7*4,6*4,0*4,0*4,0*4,3*4,5*4,0*4,3*4,2*4,2*4,2*4,0*4,4*4,6*4,0*4, //00
2*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //10 3*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //10
6*4,6*4,0*4,0*4,3*4,3*4,5*4,0*4,4*4,2*4,2*4,2*4,4*4,4*4,6*4,0*4, //20 6*4,6*4,0*4,0*4,3*4,3*4,5*4,0*4,4*4,2*4,2*4,2*4,4*4,4*4,6*4,0*4, //20
2*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //30 3*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //30
6*4,6*4,0*4,0*4,0*4,3*4,5*4,0*4,3*4,2*4,2*4,2*4,3*4,4*4,6*4,0*4, //40 6*4,6*4,0*4,0*4,0*4,3*4,5*4,0*4,3*4,2*4,2*4,2*4,3*4,4*4,6*4,0*4, //40
2*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //50 3*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //50
6*4,6*4,0*4,0*4,0*4,3*4,5*4,0*4,4*4,2*4,2*4,0*4,5*4,4*4,6*4,0*4, //60 6*4,6*4,0*4,0*4,0*4,3*4,5*4,0*4,4*4,2*4,2*4,0*4,5*4,4*4,6*4,0*4, //60
2*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //70 3*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4, //70
0*4,6*4,0*4,6*4,3*4,3*4,3*4,3*4,2*4,0*4,2*4,0*4,4*4,4*4,4*4,4*4, //80 0*4,6*4,0*4,6*4,3*4,3*4,3*4,3*4,2*4,0*4,2*4,0*4,4*4,4*4,4*4,4*4, //80
2*4,6*4,0*4,0*4,4*4,4*4,4*4,4*4,2*4,5*4,2*4,0*4,0*4,5*4,0*4,0*4, //90 3*4,6*4,0*4,0*4,4*4,4*4,4*4,4*4,2*4,5*4,2*4,0*4,0*4,5*4,0*4,0*4, //90
2*4,6*4,2*4,6*4,3*4,3*4,3*4,3*4,2*4,2*4,2*4,0*4,4*4,4*4,4*4,4*4, //A0 2*4,6*4,2*4,6*4,3*4,3*4,3*4,3*4,2*4,2*4,2*4,0*4,4*4,4*4,4*4,4*4, //A0
2*4,5*4,0*4,5*4,4*4,4*4,4*4,4*4,2*4,4*4,2*4,0*4,4*4,4*4,4*4,4*4, //B0 3*4,5*4,0*4,5*4,4*4,4*4,4*4,4*4,2*4,4*4,2*4,0*4,4*4,4*4,4*4,4*4, //B0
2*4,6*4,0*4,8*4,3*4,3*4,5*4,5*4,2*4,2*4,2*4,0*4,4*4,4*4,6*4,6*4, //C0 2*4,6*4,0*4,8*4,3*4,3*4,5*4,5*4,2*4,2*4,2*4,0*4,4*4,4*4,6*4,6*4, //C0
2*4,5*4,0*4,8*4,0*4,4*4,6*4,6*4,2*4,4*4,0*4,7*4,0*4,4*4,7*4,7*4, //D0 3*4,5*4,0*4,8*4,0*4,4*4,6*4,6*4,2*4,4*4,0*4,7*4,0*4,4*4,7*4,7*4, //D0
2*4,6*4,0*4,0*4,3*4,3*4,5*4,0*4,2*4,2*4,2*4,0*4,4*4,4*4,6*4,0*4, //E0 2*4,6*4,0*4,0*4,3*4,3*4,5*4,0*4,2*4,2*4,2*4,0*4,4*4,4*4,6*4,0*4, //E0
2*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4 //F0 3*4,5*4,0*4,0*4,0*4,4*4,6*4,0*4,2*4,4*4,0*4,0*4,0*4,4*4,7*4,0*4 //F0
}; };
@ -85,7 +85,7 @@ static uint SALLY_CYCLESX4[256] __attribute__((section(".dtcm"))) =
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_Push(byte data) static inline void sally_Push(byte data)
{ {
memory_ram[sally_s | 256] = data; memory_Write(sally_s | 256, data); // This could also write one of the mirrors...
sally_s--; sally_s--;
} }
@ -101,32 +101,37 @@ static inline byte sally_Pop( )
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Flags // Flags
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_Flags(byte data) static inline __attribute__((always_inline)) void sally_Flags(byte data)
{ {
sally_p = (sally_p & ~(_fN | _fZ)) | (data ? (data & _fN) : _fZ); sally_p = (sally_p & ~(_fN | _fZ)) | (data ? (data & _fN) : _fZ);
} }
static inline void sally_FlagsFastCmp(byte data) // For faster compare handling... static inline __attribute__((always_inline)) void sally_FlagsFastCmp(byte data) // For faster compare handling...
{ {
sally_p = (sally_p & 0x7C) | (data ? (data & _fN) : (_fZ|_fC)); sally_p = (sally_p & ~(_fN | _fZ | _fC)) | (data ? (data & _fN) : (_fZ | _fC));
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Branch // Branch
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_Branch(byte branch) { static inline __attribute__((always_inline)) void sally_Branch(byte branch)
if(branch) {
if (likely(branch))
{ {
uint carry = sally_pc.w; uint carry = sally_pc.w;
sally_pc.w += (signed char)sally_address.b.l; sally_pc.w += (signed char)sally_address.b.l;
sally_cyclesX4 += ((carry ^ sally_pc.w) & 0x100) ? 8:4; if ((carry ^ sally_pc.w) & 0x100) sally_cyclesX4 += 4; // Add an extra 4 cycles if we've crossed a page boundary
}
else // 95% of the time, the branch is not taken... so we've built into the cycle table the assumption the branch was taken and we back off 4 cycles if not
{
sally_cyclesX4 -= 4;
} }
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Delay // Delay
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_Delay(byte delta) { static inline __attribute__((always_inline)) void sally_Delay(byte delta) {
if (((word)sally_address.b.l + (word)delta) & 0xFF00) sally_cyclesX4 += 4; if (((word)sally_address.b.l + (word)delta) & 0xFF00) sally_cyclesX4 += 4;
} }
@ -170,8 +175,8 @@ static inline void sally_Indirect( ) {
lpair base; lpair base;
base.w = memory_Read_Fast(sally_pc.w++); base.w = memory_Read_Fast(sally_pc.w++);
base.b.h = memory_Read_Fast(sally_pc.w++); base.b.h = memory_Read_Fast(sally_pc.w++);
sally_address.w = memory_Read_Fast(base.w); sally_address.w = memory_Read(base.w);
sally_address.b.h = memory_Read_Fast(base.w + 1); sally_address.b.h = memory_Read(base.w + 1);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -226,8 +231,8 @@ static inline void sally_ZeroPageY( ) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ADC // ADC
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_ADC( ) { static void sally_ADC( ) {
byte data = memory_Read_Fast(sally_address.w); byte data = memory_Read(sally_address.w);
if(sally_p & _fD) { if(sally_p & _fD) {
word al = (sally_a & 15) + (data & 15) + (sally_p & _fC); word al = (sally_a & 15) + (data & 15) + (sally_p & _fC);
word ah = (sally_a >> 4) + (data >> 4); word ah = (sally_a >> 4) + (data >> 4);
@ -453,8 +458,8 @@ static inline void sally_CLV( ) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// CMP // CMP
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_CMP( ) { static inline __attribute__((always_inline)) void sally_CMP( ) {
byte data = memory_Read_Fast(sally_address.w);//memory_Read(sally_address.w); byte data = memory_Read(sally_address.w);
if(sally_a >= data) { if(sally_a >= data) {
sally_p |= _fC; sally_p |= _fC;
@ -501,8 +506,8 @@ static inline void sally_CPY( ) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// DEC // DEC
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_DEC( ) { static void sally_DEC( ) {
byte data = memory_Read_Fast(sally_address.w); byte data = memory_Read(sally_address.w);
memory_Write(sally_address.w, --data); memory_Write(sally_address.w, --data);
sally_Flags(data); sally_Flags(data);
} }
@ -532,8 +537,8 @@ static inline void sally_EOR( ) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// INC // INC
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_INC( ) { static void sally_INC( ) {
byte data = memory_Read_Fast(sally_address.w); byte data = memory_Read(sally_address.w);
memory_Write(sally_address.w, ++data); memory_Write(sally_address.w, ++data);
sally_Flags(data); sally_Flags(data);
} }
@ -573,11 +578,18 @@ static inline void sally_JSR( ) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// LDA // LDA
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_LDA( ) { static inline __attribute__((always_inline)) void sally_LDA( ) {
sally_a = memory_Read(sally_address.w); sally_a = memory_Read(sally_address.w);
sally_Flags(sally_a); sally_Flags(sally_a);
} }
// Same as sally_LDA() above but when you know we are in basic memory_ram[]
static inline __attribute__((always_inline)) void sally_LDA_fast( ) {
sally_a = memory_Read_Fast(sally_address.w);
sally_Flags(sally_a);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// LDX // LDX
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -586,6 +598,12 @@ static inline void sally_LDX( ) {
sally_Flags(sally_x); sally_Flags(sally_x);
} }
// Same as sally_LDX() above but when you know we are in basic memory_ram[]
static inline void sally_LDX_fast( ) {
sally_x = memory_Read_Fast(sally_address.w);
sally_Flags(sally_x);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// LDY // LDY
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -594,6 +612,12 @@ static inline void sally_LDY( ) {
sally_Flags(sally_y); sally_Flags(sally_y);
} }
// Same as sally_LDY() above but when you know we are in basic memory_ram[]
static inline void sally_LDY_fast( ) {
sally_y = memory_Read_Fast(sally_address.w);
sally_Flags(sally_y);
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// LSRA // LSRA
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -757,8 +781,8 @@ static inline void sally_RTS( ) {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// SBC // SBC
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static inline void sally_SBC( ) { static void sally_SBC( ) {
byte data = memory_Read_Fast(sally_address.w); byte data = memory_Read(sally_address.w);
if(sally_p & _fD) { if(sally_p & _fD) {
word al = (sally_a & 15) - (data & 15) - !(sally_p & _fC); word al = (sally_a & 15) - (data & 15) - !(sally_p & _fC);
@ -954,22 +978,6 @@ uint sally_ExecuteNMI( ) {
return 7; return 7;
} }
// ----------------------------------------------------------------------------
// Execute IRQ
// ----------------------------------------------------------------------------
uint sally_ExecuteIRQ( ) {
if(!(sally_p & _fI)) {
sally_Push(sally_pc.b.h);
sally_Push(sally_pc.b.l);
sally_p &= ~_fB;
sally_Push(sally_p);
sally_p |= _fI;
sally_pc.b.l = memory_ram[SALLY_IRQ.L];
sally_pc.b.h = memory_ram[SALLY_IRQ.H];
}
return 7;
}
extern uint prosystem_cycles; extern uint prosystem_cycles;
ITCM_CODE void sally_Execute(unsigned int cycles ) ITCM_CODE void sally_Execute(unsigned int cycles )
@ -1410,18 +1418,21 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
goto next_inst; goto next_inst;
l_0x84: l_0x84:
sally_ZeroPage( ); memory_Write(memory_ram[sally_pc.w++], sally_y);
sally_STY( ); //sally_ZeroPage( );
//sally_STY( );
goto next_inst; goto next_inst;
l_0x85: l_0x85:
sally_ZeroPage( ); memory_Write(memory_ram[sally_pc.w++], sally_a);
sally_STA( ); //sally_ZeroPage( );
//sally_STA( );
goto next_inst; goto next_inst;
l_0x86: l_0x86:
sally_ZeroPage( ); memory_Write(memory_ram[sally_pc.w++], sally_x);
sally_STX( ); //sally_ZeroPage( );
//sally_STX( );
goto next_inst; goto next_inst;
l_0x88: l_0x88:
@ -1462,7 +1473,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
sally_STY( ); sally_STY( );
goto next_inst; goto next_inst;
l_0x95: l_0x95:
sally_ZeroPageX( ); sally_ZeroPageX( );
sally_STA( ); sally_STA( );
goto next_inst; goto next_inst;
@ -1492,7 +1503,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xa0: l_0xa0:
sally_Immediate( ); sally_Immediate( );
sally_LDY( ); sally_LDY_fast( );
goto next_inst; goto next_inst;
l_0xa1: l_0xa1:
@ -1502,22 +1513,22 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xa2: l_0xa2:
sally_Immediate( ); sally_Immediate( );
sally_LDX( ); sally_LDX_fast( );
goto next_inst; goto next_inst;
l_0xa4: l_0xa4:
sally_ZeroPage( ); sally_y = memory_Read_Fast(memory_Read_Fast(sally_pc.w++)); // Skip the intermediate set of sally_address to save a few CPU cycles
sally_LDY( ); sally_Flags(sally_y);
goto next_inst; goto next_inst;
l_0xa5: l_0xa5:
sally_ZeroPage( ); sally_a = memory_Read_Fast(memory_Read_Fast(sally_pc.w++)); // Skip the intermediate set of sally_address to save a few CPU cycles
sally_LDA( ); sally_Flags(sally_a);
goto next_inst; goto next_inst;
l_0xa6: l_0xa6:
sally_ZeroPage( ); sally_x = memory_Read_Fast(memory_Read_Fast(sally_pc.w++)); // Skip the intermediate set of sally_address to save a few CPU cycles
sally_LDX( ); sally_Flags(sally_x);
goto next_inst; goto next_inst;
l_0xa8: l_0xa8:
@ -1526,7 +1537,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xa9: l_0xa9:
sally_Immediate( ); sally_Immediate( );
sally_LDA( ); sally_LDA_fast( );
goto next_inst; goto next_inst;
l_0xaa: l_0xaa:
@ -1561,17 +1572,17 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xb4: l_0xb4:
sally_ZeroPageX( ); sally_ZeroPageX( );
sally_LDY( ); sally_LDY_fast( );
goto next_inst; goto next_inst;
l_0xb5: l_0xb5:
sally_ZeroPageX( ); sally_ZeroPageX( );
sally_LDA( ); sally_LDA_fast( );
goto next_inst; goto next_inst;
l_0xb6: l_0xb6:
sally_ZeroPageY( ); sally_ZeroPageY( );
sally_LDX( ); sally_LDX_fast( );
goto next_inst; goto next_inst;
l_0xb8: l_0xb8:
@ -1614,7 +1625,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xc1: l_0xc1:
sally_IndirectX( ); sally_IndirectX( );
//sally_CMP( ); //sally_CMP( );
data_cmp = memory_Read_Fast(sally_address.w); data_cmp = memory_Read(sally_address.w);
sally_FlagsFastCmp(sally_a - data_cmp); sally_FlagsFastCmp(sally_a - data_cmp);
if(sally_a > data_cmp) sally_p |= _fC; if(sally_a > data_cmp) sally_p |= _fC;
goto next_inst; goto next_inst;
@ -1661,7 +1672,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xcd: l_0xcd:
sally_Absolute( ); sally_Absolute( );
//sally_CMP( ); //sally_CMP( );
data_cmp = memory_Read_Fast(sally_address.w); data_cmp = memory_Read(sally_address.w);
sally_FlagsFastCmp(sally_a - data_cmp); sally_FlagsFastCmp(sally_a - data_cmp);
if(sally_a > data_cmp) sally_p |= _fC; if(sally_a > data_cmp) sally_p |= _fC;
goto next_inst; goto next_inst;
@ -1679,7 +1690,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xd1: l_0xd1:
sally_IndirectY( ); sally_IndirectY( );
//sally_CMP( ); //sally_CMP( );
data_cmp = memory_Read_Fast(sally_address.w); data_cmp = memory_Read(sally_address.w);
sally_FlagsFastCmp(sally_a - data_cmp); sally_FlagsFastCmp(sally_a - data_cmp);
if(sally_a > data_cmp) sally_p |= _fC; if(sally_a > data_cmp) sally_p |= _fC;
sally_Delay(sally_y); sally_Delay(sally_y);
@ -1696,7 +1707,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xd9: l_0xd9:
sally_AbsoluteY( ); sally_AbsoluteY( );
//sally_CMP( ); //sally_CMP( );
data_cmp = memory_Read_Fast(sally_address.w); data_cmp = memory_Read(sally_address.w);
sally_FlagsFastCmp(sally_a - data_cmp); sally_FlagsFastCmp(sally_a - data_cmp);
if(sally_a > data_cmp) sally_p |= _fC; if(sally_a > data_cmp) sally_p |= _fC;
sally_Delay(sally_y); sally_Delay(sally_y);
@ -1705,7 +1716,7 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xdd: l_0xdd:
sally_AbsoluteX( ); sally_AbsoluteX( );
//sally_CMP( ); //sally_CMP( );
data_cmp = memory_Read_Fast(sally_address.w); data_cmp = memory_Read(sally_address.w);
sally_FlagsFastCmp(sally_a - data_cmp); sally_FlagsFastCmp(sally_a - data_cmp);
if(sally_a > data_cmp) sally_p |= _fC; if(sally_a > data_cmp) sally_p |= _fC;
sally_Delay(sally_x); sally_Delay(sally_x);
@ -1867,14 +1878,14 @@ ITCM_CODE void sally_Execute(unsigned int cycles )
l_0xa7: l_0xa7:
sally_ZeroPage( ); sally_ZeroPage( );
sally_LDA( ); sally_LDA_fast( );
sally_LDX( ); sally_LDX_fast( );
goto next_inst; goto next_inst;
l_0xb7: l_0xb7:
sally_ZeroPageY( ); sally_ZeroPageY( );
sally_LDA( ); sally_LDA_fast( );
sally_LDX( ); sally_LDX_fast( );
goto next_inst; goto next_inst;
l_0x87: l_0x87:
@ -2037,3 +2048,4 @@ next_inst:
} }
} }
// End of file

View file

@ -46,16 +46,13 @@ extern void sally_Reset( );
extern uint sally_ExecuteInstruction( ); extern uint sally_ExecuteInstruction( );
extern uint sally_ExecuteRES( ); extern uint sally_ExecuteRES( );
extern uint sally_ExecuteNMI( ); extern uint sally_ExecuteNMI( );
extern uint sally_ExecuteIRQ( );
extern byte sally_a; extern byte sally_a;
extern byte sally_x; extern byte sally_x;
extern byte sally_y; extern byte sally_y;
extern uint sally_p; extern uint sally_p;
extern uint sally_s; extern uint sally_s;
extern PCUnion sally_pc;
extern bool wsync_happened; extern bool wsync_happened;
extern void sally_Execute(unsigned int cycles ); extern void sally_Execute(unsigned int cycles );
extern void sally_Execute_Fast(unsigned int cycles );
#endif #endif

View file

@ -59,6 +59,7 @@ byte tia_audv[2] __attribute__((section(".dtcm"))) = {0};
static byte tia_poly4Cntr[2] __attribute__((section(".dtcm"))) = {0}; static byte tia_poly4Cntr[2] __attribute__((section(".dtcm"))) = {0};
static byte tia_poly5Cntr[2] __attribute__((section(".dtcm"))) = {0}; static byte tia_poly5Cntr[2] __attribute__((section(".dtcm"))) = {0};
static u16 tia_poly9Cntr[2] __attribute__((section(".dtcm"))) = {0}; static u16 tia_poly9Cntr[2] __attribute__((section(".dtcm"))) = {0};
u16 tia_wait __attribute__((section(".dtcm"))) = 0;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ProcessChannel // ProcessChannel
@ -140,6 +141,7 @@ void tia_Reset( ) {
tia_poly9Cntr[index] = 0; tia_poly9Cntr[index] = 0;
} }
tia_Clear( ); tia_Clear( );
tia_wait = 0;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -152,8 +154,6 @@ void tia_Clear( ) {
} }
} }
// Same as TIA_Process but designed for Pokey integration... // Same as TIA_Process but designed for Pokey integration...
ITCM_CODE int TIA_Sample(void) ITCM_CODE int TIA_Sample(void)
{ {
@ -184,6 +184,9 @@ ITCM_CODE int TIA_Sample(void)
ITCM_CODE void tia_Process(void) ITCM_CODE void tia_Process(void)
{ {
u32 samp[2]; u32 samp[2];
if (tia_wait) return;
for(u8 index = 0; index < 2; index++) for(u8 index = 0; index < 2; index++)
{ {
if(tia_counter[0] > 1) if(tia_counter[0] > 1)
@ -206,8 +209,17 @@ ITCM_CODE void tia_Process(void)
} }
samp[index] = ((tia_volume[0] + tia_volume[1])); samp[index] = ((tia_volume[0] + tia_volume[1]));
} }
tia_buffer[tiaBufIdx++] = (samp[1] << 8) | (samp[0]);
tiaBufIdx &= (SNDLENGTH-1); // We have filled the buffer... let the buffer drain a bit
if (((tiaBufIdx+1) & (SNDLENGTH-1)) == myTiaBufIdx)
{
tia_wait = (SNDLENGTH >> 2);
}
else
{
tia_buffer[tiaBufIdx++] = (samp[1] << 8) | (samp[0]);
tiaBufIdx &= (SNDLENGTH-1);
}
} }

View file

@ -62,7 +62,8 @@ extern u16 tia_ProcessNow(void);
extern byte tia_volume[2]; extern byte tia_volume[2];
extern uint tia_counter[2]; extern uint tia_counter[2];
extern uint tia_counterMax[2]; extern uint tia_counterMax[2];
extern u16 tia_wait;
extern u32 myTiaBufIdx;
inline void tia_MemoryChannel(byte channel) inline void tia_MemoryChannel(byte channel)

View file

@ -8,6 +8,5 @@ typedef unsigned short word;
typedef unsigned int uint; typedef unsigned int uint;
extern unsigned short *bufVideo; // Video buffer extern unsigned short *bufVideo; // Video buffer
extern uint video_height;
#endif #endif

View file

@ -1,4 +1,31 @@
// =====================================================================================
// Copyright (c) 2022-2024 Dave Bernazzani (wavemotion-dave)
//
// Copying and distribution of this emulator, it's source code and associated
// readme files, with or without modification, are permitted in any medium without
// royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition),
// Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely.
//
// A7800DS emulator is offered as-is, without any warranty.
//
// The original GPL license:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// =====================================================================================
#include <stdio.h> #include <stdio.h>
#include <time.h>
#include <fat.h> #include <fat.h>
#include <nds.h> #include <nds.h>
#include <maxmod9.h> #include <maxmod9.h>
@ -32,9 +59,13 @@ int main(int argc, char **argv)
intro_logo(); intro_logo();
dsInitScreenMain(); dsInitScreenMain();
emu_state = A7800_MENUINIT; emu_state = A7800_MENUINIT;
srand(time(NULL));
LoadConfig(); LoadConfig();
bios_check_and_load();
//load rom file via args if a rom path is supplied //load rom file via args if a rom path is supplied
if(argc > 1) if(argc > 1)
{ {

View file

@ -1,3 +1,29 @@
// =====================================================================================
// Copyright (c) 2022-2024 Dave Bernazzani (wavemotion-dave)
//
// Copying and distribution of this emulator, it's source code and associated
// readme files, with or without modification, are permitted in any medium without
// royalty provided this copyright notice is used and wavemotion-dave (Phoenix-Edition),
// Alekmaul (original port) and Greg Stanton (ProSystem Emulator) are thanked profusely.
//
// A7800DS emulator is offered as-is, without any warranty.
//
// The original GPL license:
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// =====================================================================================
#ifndef _MAIN_H #ifndef _MAIN_H
#define _MAIN_H #define _MAIN_H

BIN
logo.bmp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 630 B

BIN
techdocs/HSC-7800.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

192
techdocs/a78header.asm Normal file
View file

@ -0,0 +1,192 @@
; A78 Header v4.2
;
; Use this file to add an a78 header via the source code of your ROM.
;
; _Implementation Notes_
;
; * Include this header near the beginning of your DASM source, but after
; your initial ROM ORG statement.
; * Change the fields withn the file to describe your game's hardware
; requirements to emulators and flash carts.
; * All unused/reserved bits and bytes must be set to zero.
.ROMSIZE = $20000 ; Update with your total ROM size.
; Uncomment the following entry to force older v3 style headers.
;V3ONLY = 1
; Uncomment the following entry to break support with platforms that only
; have v3 headers implemented.
;V4ONLY = 1
; Auto-header ROM allocation follows. If the current address is page aligned,
; we backup 128 bytes. This may cause issues if you use a different ORG+RORG
; at the start of your ROM - in that case, account for the 128 bytes of
; header within your game ROM start ORG+RORG statements.
if ( . & $FF ) = 0 ; Check if we're at an even page.
ORG (. - 128),0 ; If so, go -128 bytes, for header space.
else
ORG .,0 ; In case zero-fill wasn't specified
endif ; orginally.
SEG ROM
.HEADER = .
; Format detection - do not modify.
#ifconst V3ONLY
DC.B 3 ; 0 header major version
#else
DC.B 4 ; 0 header major version
#endif ; V3ONLY
DC.B "ATARI7800" ; 1..16 header magic string - zero pad
ORG .HEADER+$11,0
DC.B "Game Name Here" ; 17..48 cartridge title string - zero pad
ORG .HEADER+$31,0
DC.B (.ROMSIZE>>24) ; 49..52 cartridge ROM size
DC.B (.ROMSIZE>>16&$FF)
DC.B (.ROMSIZE>>8&$FF)
DC.B (.ROMSIZE&$FF)
#ifnconst V4ONLY
; The following 2 cartridge type bytes are deprecated as of header v4.0.
; It's recommended that you still populate these bytes for support with
; platforms that don't yet support v4.
DC.B %00000000 ; 53 cartridge type A
DC.B %00000000 ; 54 cartridge type B
; _Cartridge Type A_
; bit 7 ; POKEY @ $0800 - $080F
; bit 6 ; EXRAM/M2 (halt banked RAM)
; bit 5 ; BANKSET
; bit 4 ; SOUPER
; bit 3 ; YM2151 @ $0460 - $0461
; bit 2 ; POKEY @ $0440 - $044F
; bit 1 ; ABSOLUTE
; bit 0 ; ACTIVISION
; _Cartridge Type B_
; bit 7 ; EXRAM/A8 (mirror RAM)
; bit 6 ; POKEY @ $0450 - $045F
; bit 5 ; EXRAM/X2 (hotspot banked RAM)
; bit 4 ; EXFIX (2nd last bank @ $4000)
; bit 3 ; EXROM (ROM @ $4000)
; bit 2 ; EXRAM (RAM @ $4000)
; bit 1 ; SUPERGAME
; bit 0 ; POKEY @ $4000 - $7FFF
#else
DC.B %11111111
DC.B %11111111
#endif ; !V4ONLY
DC.B 1 ; 55 controller 1 device type
DC.B 1 ; 56 controller 2 device type
; 0 = none
; 1 = 7800 joystick
; 2 = lightgun
; 3 = paddle
; 4 = trakball
; 5 = 2600 joystick
; 6 = 2600 driving
; 7 = 2600 keypad
; 8 = ST mouse
; 9 = Amiga mouse
; 10 = AtariVox
; 11 = SNES2Atari
; 12 = Mega7800
DC.B %00000000 ; 57 tv type
; bits 7..3 ; reserved
; bit 2 ; 0:single-region,1:multi-region
; bit 1 ; 0:component,1:composite
; bit 0 ; 0:NTSC,1:PAL
DC.B %00000000 ; 58 save peripheral
; bits 7..2 ; reserved
; bit 1 ; SaveKey/AtariVox
; bit 0 ; High Score Cart (HSC)
; The following irq source byte is deprecated as of header v4.0.
; It's recommended that you still populate this byte for support with
; platforms that don't yet support v4.
ORG .HEADER+62,0
DC.B %00000000 ; 62 external irq source
; bits 7..5 ; reserved
; bit 4 ; POKEY @ $0800 - $080F
; bit 3 ; YM2151 @ $0460 - $0461
; bit 2 ; POKEY @ $0440 - $044F
; bit 1 ; POKEY @ $0450 - $045F
; bit 0 ; POKEY @ $4000 - $7FFF
DC.B %00000000 ; 63 slot passthrough device
; bits 7..1 ; reserved
; bit 0 ; XM module
#ifnconst V3ONLY
; The following 6 bytes are v4 header specific. You should populate
; them with valid info if you're not using V3ONLY, because they will
; take precedence over v3 headers.
DC.B 0 ; 64 mapper
; 0 = linear
; 1 = supergame
; 2 = activision
; 3 = absolute
; 4 = souper
DC.B 0 ; 65 mapper options
; linear_
; bit 7 ; bankset rom
; bits 0-1 ; option @4000...
; 0 = none
; 1 = 16K EXRAM
; 2 = 8K EXRAM/A8
; 3 = 32K EXRAM/M2
; supergame_
; bit 7 ; bankset rom
; bits 0-2 ; option @4000...
; 0 = none
; 1 = 16K EXRAM
; 2 = 8K EXRAM/A8
; 3 = 32K EXRAM/M2
; 4 = 16K EXROM
; 5 = EXFIX
; 6 = 32K EXRAM/X2
DC.B %00000000 ; 66 audio hi
DC.B %00000000 ; 67 audio lo
; bit 5 ; adpcm@420
; bit 4 ; covox@430
; bit 3 ; ym2151@460
; bits 0-2 ; pokey...
; 0 = none
; 1 = pokey@440
; 2 = pokey@450
; 3 = dual pokey @440+@450
; 4 = pokey@800
; 5 = pokey@4000
DC.B %00000000 ; 68 interrupt hi
DC.B %00000000 ; 69 interrupt lo
; bit 2 ; YM2151
; bit 1 ; pokey 2 (@440)
; bit 0 ; pokey 1 (@4000, @450, or @800)
#endif ; !V3ONLY
ORG .HEADER+100,0 ; 100..127 footer magic string
DC.B "ACTUAL CART DATA STARTS HERE"