Compare commits

...

22 commits
4.8 ... 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
31 changed files with 2308 additions and 1658 deletions

Binary file not shown.

View file

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

View file

@ -23,7 +23,7 @@ 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.
The new Banksets scheme is fully supported - this new banking/memory handling is designed The Banksets scheme is fully supported - this new banking/memory handling is designed
for homebrew authors to provide increased ROM density and improved packing and access of for homebrew authors to provide increased ROM density and improved packing and access of
graphics data vs code. This allows for games that would have been difficult or impossible graphics data vs code. This allows for games that would have been difficult or impossible
without 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.
@ -31,9 +31,12 @@ Features :
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 to track down the highscore.rom file! 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-2024 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 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, in 2005 by Greg Stanton under the GNU General Public License and, as such,
@ -72,10 +75,8 @@ Known Issues and Limitations:
- Lightgun is not supported. - Lightgun is not supported.
- Paddles are not supported. - Paddles are not supported.
- PAL is not supported - use NTSC instead. - PAL is not supported - use NTSC instead.
- Artifacting is not supported which means Tower Toppler is unplayable.
- Souper mapper (Ricky & Vicky) is not supported. - Souper mapper (Ricky & Vicky) is not supported.
- Keystone Koppers and ARTI have slight sound issues as Pokey emulation is not perfect. - Keystone Koppers and ARTI have slight sound issues as Pokey emulation is not perfect.
- Games greater than 1MB (1024K + 128b header) are not supported.
- Only one Pokey is supported at 4000h, 800h or 450h (no Dual Pokey). - Only one Pokey is supported at 4000h, 800h or 450h (no Dual Pokey).
- XM is not supported (beyond HSC and Pokey). - XM is not supported (beyond HSC and Pokey).
@ -86,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.
@ -95,8 +100,8 @@ Controls :
* B : Fire button 2 * B : Fire button 2
* SELECT : SELECT Button * SELECT : SELECT Button
* START : PAUSE Button * START : PAUSE Button
* X : Pan Screen Down * X : Configurable (default: Pan Screen Down)
* Y : Pan Screen Up * 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 * L + R + X : Hold for 1 second to swap LCD screens top/bottom
@ -106,11 +111,10 @@ Controls :
that utilize it. This allows all of the DS buttons to map into the game - exactly that utilize it. This allows all of the DS buttons to map into the game - exactly
as labeled (D-Pad plus ABXY, Left Shoulder, Right Shoulder and Start, Select). as labeled (D-Pad plus ABXY, Left Shoulder, Right Shoulder and Start, Select).
High Score Saving works if you have highscore.rom (exact name and case) in your High Score Saving works if you have highscore.rom (exact name) in your
roms directory where you load your games... also, you MUST press the HSC button roms directory where you load your games... The .hsc backing file will be written
if you want to snap the Saved Scores out to the flash card. It's not something automatically as the game runs. Only games programmed to use the highscore cart
I want to do as the game runs... so you must do it... the high scores will also will save scores.
auto-save if you quit the emulator or select a new game.
Configuration : Configuration :
---------- ----------
@ -134,8 +138,6 @@ The following schemes are supported:
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 - sometimes including the developer :)
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 : Of Mice, Men and Screen Resolutions :
@ -170,7 +172,8 @@ this while you are playing - usually when you lose a life you can tap the X butt
more useable scanlines for actual gameplay. Think of this like you're at the arcade and you have to glance up to see your 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 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 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. 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 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). your current configuration (which includes your screen offset/scaling tweaks on a per-game basis).
@ -197,6 +200,33 @@ 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 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. * 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) * Fix for Supercarts so that they start in bank 0 (Legend of Silverpeak should now load)

View file

@ -26,16 +26,13 @@ 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)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -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: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,5 +1,5 @@
// ===================================================================================== // =====================================================================================
// Copyright (c) 2022-2024 Dave Bernazzani (wavemotion-dave) // Copyright (c) 2022-2025 Dave Bernazzani (wavemotion-dave)
// //
// Copying and distribution of this emulator, it's source code and associated // Copying and distribution of this emulator, it's source code and associated
// readme files, with or without modification, are permitted in any medium without // readme files, with or without modification, are permitted in any medium without
@ -59,11 +59,14 @@ u8 full_speed __attribute__((section(".dtcm"))) = 0;
u8 fpsDisplay __attribute__((section(".dtcm"))) = 0; u8 fpsDisplay __attribute__((section(".dtcm"))) = 0;
u8 bEmulatorRun __attribute__((section(".dtcm"))) = 1; u8 bEmulatorRun __attribute__((section(".dtcm"))) = 1;
u8 bNoDatabase __attribute__((section(".dtcm"))) = 0; u8 bNoDatabase __attribute__((section(".dtcm"))) = 0;
u8 bSkipBIOS __attribute__((section(".dtcm"))) = 0;
u32 snes_adaptor __attribute__((section(".dtcm"))) = 0x0000FFFF; u32 snes_adaptor __attribute__((section(".dtcm"))) = 0x0000FFFF;
extern u32 tiaBufIdx; extern u32 tiaBufIdx;
char fpsbuf[34]; char fpsbuf[34];
char dbgbuf[40]; char dbgbuf[40];
u8 bJustSavedHSC = 0;
u8 spamHSC = 0;
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Some vars for listing filenames of ROMs... 1K of ROMs is plenty // Some vars for listing filenames of ROMs... 1K of ROMs is plenty
@ -82,7 +85,6 @@ int bg0s, bg1s; // sub BG pointers
int debug[MAX_DEBUG]={0}; int debug[MAX_DEBUG]={0};
u8 DEBUG_DUMP = 0; u8 DEBUG_DUMP = 0;
uint video_height; // Actual video height
u16 *bufVideo; // Video flipping buffer u16 *bufVideo; // Video flipping buffer
gamecfg GameConf; // Game Config svg gamecfg GameConf; // Game Config svg
@ -96,6 +98,37 @@ u8 soundEmuPause = 1;
#define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); #define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank();
static u8 trace_x=0;
static u8 trace_y=0;
static u8 trace_done = 0;
void Trace(word data)
{
if (trace_done) return;
sprintf(fpsbuf, "%04X", data);
dsPrintValue(trace_x,trace_y,0, fpsbuf);
if (trace_y < 22) { trace_y++; }
else
{
trace_y = 0;
if (trace_x < 24) { trace_x += 5;} else {trace_x = 0; trace_done=1;}
}
}
void Trace2(word addr, u8 data)
{
if (trace_done) return;
sprintf(fpsbuf, "%04X=%02X", addr, data);
dsPrintValue(trace_x,trace_y,0, fpsbuf);
if (trace_y < 22) { trace_y++; }
else
{
trace_y = 0;
if (trace_x < 23) { trace_x += 8;} else {trace_x = 0; trace_done=1;}
}
}
static void DumpDebugData(void) static void DumpDebugData(void)
{ {
if (DEBUG_DUMP) if (DEBUG_DUMP)
@ -106,13 +139,15 @@ static void DumpDebugData(void)
dsPrintValue(0,2+i,0, fpsbuf); dsPrintValue(0,2+i,0, fpsbuf);
} }
//sprintf(fpsbuf, "PC = %04X", sally_pc.w);
//dsPrintValue(0,10,0, fpsbuf);
extern byte last_illegal_opcode; extern byte last_illegal_opcode;
if (last_illegal_opcode > 0) if (last_illegal_opcode > 0)
{ {
sprintf(fpsbuf, "ILLEGAL OP=%02X", last_illegal_opcode); sprintf(fpsbuf, "ILLEGAL OP=%02X", last_illegal_opcode);
dsPrintValue(10,23,0, fpsbuf); dsPrintValue(14,23,0, fpsbuf);
} }
} }
} }
@ -170,6 +205,8 @@ void FadeToColor(unsigned char ucSens, unsigned short ucBG, unsigned char ucScr,
} }
} }
u8 tchepres_delay = 0;
u8 tchepres_value = 0;
#define tchepres(a) \ #define tchepres(a) \
keyboard_data[GameConf.DS_Pad[a]] = 1; keyboard_data[GameConf.DS_Pad[a]] = 1;
@ -180,12 +217,29 @@ uint8_t shift_dampen = 0;
ITCM_CODE void vblankIntr() ITCM_CODE void vblankIntr()
{ {
if (bRefreshXY || temp_shift) if (bios_show_counter)
{
bios_show_counter--;
xdxBG = 0x0100;
ydyBG = 0x0100;
cxBG = (32 << 8);
cyBG = (8 << 8);
REG_BG2PA = xdxBG;
REG_BG2PD = ydyBG;
REG_BG3PA = xdxBG;
REG_BG3PD = ydyBG;
REG_BG2X = cxBG;
REG_BG2Y = cyBG;
REG_BG3X = cxBG;
REG_BG3Y = cyBG;
bRefreshXY = 1;
}
else if (bRefreshXY || temp_shift)
{ {
cxBG = (myCartInfo.xOffset << 8); cxBG = (myCartInfo.xOffset << 8);
cyBG = (myCartInfo.yOffset + temp_shift) << 8; cyBG = (myCartInfo.yOffset + temp_shift) << 8;
xdxBG = ((320 / myCartInfo.xScale) << 8) | (320 % myCartInfo.xScale) ; xdxBG = ((320 / myCartInfo.xScale) << 8) | (320 % myCartInfo.xScale) ;
ydyBG = ((video_height / myCartInfo.yScale) << 8) | (video_height % myCartInfo.yScale); ydyBG = ((234 / myCartInfo.yScale) << 8) | (234 % myCartInfo.yScale);
REG_BG2X = cxBG; REG_BG2X = cxBG;
REG_BG2Y = cyBG; REG_BG2Y = cyBG;
@ -228,13 +282,13 @@ void dsInitScreenMain(void)
if (isDSiMode()) isDS_LITE = false; if (isDSiMode()) isDS_LITE = false;
else isDS_LITE = true; else isDS_LITE = true;
vramSetBankB(VRAM_B_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06820000 - 64K used for the is_memory_writable[] check and 64K for extra RAM vramSetBankB(VRAM_B_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06820000 - 96K for Cart Bankswitch cache and 32K for 2X Bankswitched RAM
vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 - Used for Cart Bankswitch vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 - Used for Cart Bankswitch cache (272K contiguous below)
vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 - Used for Cart Bankswitch vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 - Used for Cart Bankswitch cache
vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 - .. vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 - Used for Cart Bankswitch cache
vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 - .. vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 - Used for Cart Bankswitch cache
vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 - .. vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 - Used for Cart Bankswitch cache
vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 - .. vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 - Used for Cart Bankswitch cache
} }
void dsInitTimer(void) void dsInitTimer(void)
@ -248,13 +302,14 @@ void dsShowScreenEmu(void)
// Change vram // Change vram
videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE); videoSetMode(MODE_5_2D | DISPLAY_BG2_ACTIVE | DISPLAY_BG3_ACTIVE);
vramSetBankA(VRAM_A_MAIN_BG_0x06000000); vramSetBankA(VRAM_A_MAIN_BG_0x06000000);
vramSetBankB(VRAM_B_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06820000 - 64K used for the is_memory_writable[] check and 64K for extra RAM vramSetBankB(VRAM_B_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06820000 - 96K for Cart Bankswitch cache and 32K for 2X Bankswitched RAM
vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 - Used for Cart Bankswitch vramSetBankD(VRAM_D_LCD ); // Not using this for video but 128K of faster RAM always useful! Mapped at 0x06860000 - Used for Cart Bankswitch cache (272K contiguous below)
vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 - Used for Cart Bankswitch vramSetBankE(VRAM_E_LCD ); // Not using this for video but 64K of faster RAM always useful! Mapped at 0x06880000 - Used for Cart Bankswitch cache
vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 - .. vramSetBankF(VRAM_F_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06890000 - Used for Cart Bankswitch cache
vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 - .. vramSetBankG(VRAM_G_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x06894000 - Used for Cart Bankswitch cache
vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 - .. vramSetBankH(VRAM_H_LCD ); // Not using this for video but 32K of faster RAM always useful! Mapped at 0x06898000 - Used for Cart Bankswitch cache
vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 - .. vramSetBankI(VRAM_I_LCD ); // Not using this for video but 16K of faster RAM always useful! Mapped at 0x068A0000 - Used for Cart Bankswitch cache
bg0 = bgInit(3, BgType_Bmp8, BgSize_B8_512x512, 0,0); bg0 = bgInit(3, BgType_Bmp8, BgSize_B8_512x512, 0,0);
bg1 = bgInit(2, BgType_Bmp8, BgSize_B8_512x512, 0,0); bg1 = bgInit(2, BgType_Bmp8, BgSize_B8_512x512, 0,0);
@ -266,7 +321,7 @@ void dsShowScreenEmu(void)
cxBG = (myCartInfo.xOffset << 8); cxBG = (myCartInfo.xOffset << 8);
cyBG = (myCartInfo.yOffset << 8); cyBG = (myCartInfo.yOffset << 8);
xdxBG = ((320 / myCartInfo.xScale) << 8) | (320 % myCartInfo.xScale) ; xdxBG = ((320 / myCartInfo.xScale) << 8) | (320 % myCartInfo.xScale) ;
ydyBG = ((video_height / myCartInfo.yScale) << 8) | (video_height % myCartInfo.yScale); ydyBG = ((234 / myCartInfo.yScale) << 8) | (234 % myCartInfo.yScale);
REG_BG2PB = 0; REG_BG2PB = 0;
REG_BG2PC = 0; REG_BG2PC = 0;
@ -330,6 +385,10 @@ void dsLoadGame(char *filename)
debug[i] = 0; debug[i] = 0;
} }
trace_x=0;
trace_y=0;
trace_done = 0;
// load card game if ok // load card game if ok
if (cartridge_Load(filename) != 0) if (cartridge_Load(filename) != 0)
{ {
@ -347,6 +406,8 @@ void dsLoadGame(char *filename)
prosystem_Reset(); prosystem_Reset();
lastSample = 0; lastSample = 0;
bJustSavedHSC = 0;
spamHSC = 0;
if (DEBUG_DUMP) if (DEBUG_DUMP)
{ {
@ -394,6 +455,9 @@ void dsLoadGame(char *filename)
TIMER0_DATA=0; TIMER0_DATA=0;
TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024; TIMER0_CR=TIMER_ENABLE|TIMER_DIV_1024;
tchepres_delay = 0;
tchepres_value = 0;
SoundUnPause(); SoundUnPause();
} }
} }
@ -428,7 +492,7 @@ bool dsWaitOnQuit(void) {
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
strcpy(szName,"Quit a7800DS ?"); strcpy(szName,"Quit a7800DS ?");
dsPrintValue(16-strlen(szName)/2,3,0,szName); dsPrintValue(16-strlen(szName)/2,5,0,szName);
sprintf(szName,"%s","A TO CONFIRM, B TO GO BACK"); sprintf(szName,"%s","A TO CONFIRM, B TO GO BACK");
dsPrintValue(16-strlen(szName)/2,23,0,szName); dsPrintValue(16-strlen(szName)/2,23,0,szName);
@ -621,12 +685,13 @@ unsigned int dsWaitForRom(void)
while (keysCurrent() & KEY_B); while (keysCurrent() & KEY_B);
} }
if (keysCurrent() & (KEY_A | KEY_Y | KEY_SELECT)) // Select ROM if (keysCurrent() & (KEY_A | KEY_Y | KEY_SELECT | KEY_START)) // Select ROM
{ {
if (!proromlist[ucFicAct].directory) if (!proromlist[ucFicAct].directory)
{ {
if (keysCurrent() & KEY_Y) bEmulatorRun = false; else bEmulatorRun=true; if (keysCurrent() & KEY_Y) bEmulatorRun = false; else bEmulatorRun=true;
if (keysCurrent() & KEY_SELECT) bNoDatabase = true; else bNoDatabase=false; if (keysCurrent() & KEY_SELECT) bNoDatabase = true; else bNoDatabase=false;
if (keysCurrent() & KEY_START) bSkipBIOS = true; else bSkipBIOS=false;
if (keysCurrent() & KEY_X) DEBUG_DUMP = 1; else DEBUG_DUMP=0; if (keysCurrent() & KEY_X) DEBUG_DUMP = 1; else DEBUG_DUMP=0;
bRet=true; bRet=true;
bDone=true; bDone=true;
@ -750,7 +815,7 @@ void dsPrintValue(int x, int y, unsigned int isSelect, char *pchStr)
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// MAXMOD streaming setup and handling... // MAXMOD streaming setup and handling...
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
#define sample_rate 31400 // To rough match the TIA driver for the Atari 7800 - we purposely undershoot slightly #define sample_rate 31500 // To rough match the TIA driver for the Atari 7800 - we purposely undershoot slightly (263 x 60 x 2 = 31560)
#define buffer_size (256) // Enough buffer that we don't have to fill it too often but not so big as to create lag #define buffer_size (256) // Enough buffer that we don't have to fill it too often but not so big as to create lag
mm_ds_system sys __attribute__((section(".dtcm"))); mm_ds_system sys __attribute__((section(".dtcm")));
@ -781,6 +846,8 @@ ITCM_CODE mm_word OurSoundMixer(mm_word len, mm_addr dest, mm_stream_formats for
int new_len = (len>>1); int new_len = (len>>1);
while (new_len--) while (new_len--)
{ {
if (tia_wait) tia_wait--;
if (myTiaBufIdx != tiaBufIdx) if (myTiaBufIdx != tiaBufIdx)
{ {
*p++ = tia_buffer[myTiaBufIdx++]; *p++ = tia_buffer[myTiaBufIdx++];
@ -912,16 +979,84 @@ __attribute__ ((noinline)) void toggle_zoom(void)
bRefreshXY = true; bRefreshXY = true;
} }
static u8 special_hsc_entry=0;
u8 keys_touch __attribute__((section(".dtcm"))) = 0;
void handle_touch_screen_input(void)
{
unsigned int romSel;
short iTx,iTy;
touchPosition touch;
touchRead(&touch);
iTx = touch.px;
iTy = touch.py;
if ((iTx>2) && (iTx<67) && (iTy>154) && (iTy<171)) { // POWER
SoundPause();
mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
if (dsWaitOnQuit()) emu_state=A7800_QUITSTDS;
else SoundUnPause();
}
else if ((iTx>238) && (iTx<256) && (iTy>0) && (iTy<20)) { // Full Speed Toggle ... upper right corner...
full_speed = 1-full_speed;
if (full_speed) dsPrintValue(30,0,0,"FS"); else dsPrintValue(30,0,0," ");
dampen=60;
}
else if ((iTx>=0) && (iTx<18) && (iTy>0) && (iTy<20)) { // FPS Toggle ... upper left corner...
fpsDisplay = 1-fpsDisplay; gTotalAtariFrames=0; if (!fpsDisplay) dsPrintValue(0,0,0," ");
dampen=60;
}
else if ((iTx>67) && (iTx<128) && (iTy>154) && (iTy<171)) { // PAUSE
if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
tchepres_value = 10;
tchepres_delay = 5;
tchepres(10);
}
else if ((iTx>128) && (iTx<193) && (iTy>154) && (iTy<171)) { // SELECT
if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
tchepres_value = 11;
tchepres_delay = 5;
tchepres(11);
}
else if ((iTx>193) && (iTx<254) && (iTy>154) && (iTy<171)) { // RESET
if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
tchepres_value = 6;
tchepres_delay = 5;
tchepres(6);
}
else if ((iTx>90) && (iTx<110) && (iTy>90) && (iTy<110)) { // Atari Logo - Activate HSC Maintenence Mode (only on High Score screen)
special_hsc_entry=70;
}
else if ((iTx>69) && (iTx<180) && (iTy>21) && (iTy<62)) // Cartridge slot
{
SoundPause();
// Find files in current directory and show it
proFindFiles();
romSel=dsWaitForRom();
if (romSel) {emu_state=A7800_PLAYINIT; dsLoadGame(proromlist[ucFicAct].filename); if (full_speed) dsPrintValue(30,0,0,"FS"); else dsPrintValue(30,0,0," ");}
else SoundUnPause();
}
else if ((iTx>190) && (iTx<230) && (iTy>22) && (iTy<62)) // Gear Icon (Settings)
{
SoundPause();
ShowConfig();
SoundUnPause();
}
else if ((iTx>10) && (iTx<58) && (iTy>22) && (iTy<62)) // Magnifying Glass (zoom)
{
if (!keys_touch) toggle_zoom();
}
}
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// This is where the action happens! The main loop runs continually and clocks // This is where the action happens! The main loop runs continually and clocks
// out the 60 frames per second of the 7800 Prosystem // out the 60 frames per second of the 7800 Prosystem
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
ITCM_CODE void dsMainLoop(void) void dsMainLoop(void)
{ {
static u8 special_hsc_entry=0;
static short int last_keys_pressed = 999; static short int last_keys_pressed = 999;
unsigned int keys_pressed,keys_touch=0, romSel; unsigned int keys_pressed;
short iTx,iTy;
timer_reset(); timer_reset();
@ -978,6 +1113,13 @@ ITCM_CODE void dsMainLoop(void)
memset(keyboard_data, 0x00, 15); // Not the difficulty switches which are the two bytes after this... memset(keyboard_data, 0x00, 15); // Not the difficulty switches which are the two bytes after this...
} }
// If we have recently pressed any of the console keys... keep it pressed for a minimum duration
if (tchepres_delay)
{
tchepres_delay--;
tchepres(tchepres_value);
}
scanKeys(); scanKeys();
keys_pressed = keysCurrent(); keys_pressed = keysCurrent();
@ -986,75 +1128,7 @@ ITCM_CODE void dsMainLoop(void)
// if touch screen pressed // if touch screen pressed
if (keys_pressed & KEY_TOUCH) if (keys_pressed & KEY_TOUCH)
{ {
touchPosition touch; handle_touch_screen_input();
touchRead(&touch);
iTx = touch.px;
iTy = touch.py;
if ((iTx>8) && (iTx<55) && (iTy>154) && (iTy<171)) { // 32,160 -> 64,168 POWER
SoundPause();
mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
if (dsWaitOnQuit()) emu_state=A7800_QUITSTDS;
else SoundUnPause();
}
else if ((iTx>240) && (iTx<256) && (iTy>0) && (iTy<20)) { // Full Speed Toggle ... upper right corner...
full_speed = 1-full_speed;
if (full_speed) dsPrintValue(30,0,0,"FS"); else dsPrintValue(30,0,0," ");
dampen=60;
}
else if ((iTx>=0) && (iTx<16) && (iTy>0) && (iTy<20)) { // FPS Toggle ... upper left corner...
fpsDisplay = 1-fpsDisplay; gTotalAtariFrames=0; if (!fpsDisplay) dsPrintValue(0,0,0," ");
dampen=60;
}
else if ((iTx>63) && (iTx<105) && (iTy>154) && (iTy<171)) { // 72,160 -> 105,168 PAUSE
if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
tchepres(10);
}
else if ((iTx>152) && (iTx<198) && (iTy>154) && (iTy<171)) { // 142,160 -> 175,168 SELECT
if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
tchepres(11);
}
else if ((iTx>208) && (iTx<251) && (iTy>154) && (iTy<171)) { // 191,160 -> 224,168 RESET
if (keys_touch == 0) mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
tchepres(6);
}
else if ((iTx>90) && (iTx<110) && (iTy>90) && (iTy<110)) { // Atari Logo - Activate HSC Maintenence Mode (only on High Score screen)
special_hsc_entry=70;
}
else if ((iTx>115) && (iTx<144) && (iTy>154) && (iTy<171)) { // Snap HSC SRAM file
if (high_score_cart_loaded)
{
dsPrintValue(13,0,0, "SAVING");
mmEffect(SFX_KEYCLICK); // Play short key click for feedback...
WAITVBL;WAITVBL;
cartridge_SaveHighScoreSram();
dsPrintValue(13,0,0, " ");
}
dampen=60;
continue;
}
else if ((iTx>69) && (iTx<180) && (iTy>22) && (iTy<62)) // Cartridge slot
{
SoundPause();
// Find files in current directory and show it
proFindFiles();
romSel=dsWaitForRom();
if (romSel) { emu_state=A7800_PLAYINIT; dsLoadGame(proromlist[ucFicAct].filename); if (full_speed) dsPrintValue(30,0,0,"FS"); else dsPrintValue(30,0,0," ");}
else
{
SoundUnPause();
}
}
else if ((iTx>190) && (iTx<230) && (iTy>22) && (iTy<62)) // Gear Icon (Settings)
{
SoundPause();
ShowConfig();
SoundUnPause();
}
else if ((iTx>10) && (iTx<58) && (iTy>22) && (iTy<62)) // Magnifying Glass (zoom)
{
toggle_zoom();
}
keys_touch=1; keys_touch=1;
} else keys_touch=0; } else keys_touch=0;
@ -1063,16 +1137,16 @@ ITCM_CODE void dsMainLoop(void)
last_keys_pressed = keys_pressed; last_keys_pressed = keys_pressed;
if ((myCartInfo.cardctrl1 != SNES)) if ((myCartInfo.cardctrl1 != SNES))
{ {
if ( (keys_pressed & KEY_SELECT) ) { tchepres(11); } // BUTTON SELECT if ( (keys_pressed & KEY_SELECT) ) { tchepres_value = 11; tchepres_delay = 5;tchepres(11); } // BUTTON SELECT
} }
if ((myCartInfo.cardctrl1 != TWIN) && (myCartInfo.cardctrl1 != SNES)) if ((myCartInfo.cardctrl1 != TWIN) && (myCartInfo.cardctrl1 != SNES))
{ {
if ( (keys_pressed & KEY_START) ) {tchepres(10);} // BUTTON PAUSE if ( (keys_pressed & KEY_START) ) { tchepres_value = 10; tchepres_delay = 5; tchepres(10); } // BUTTON PAUSE
} }
if (myCartInfo.cardctrl1 == SOTA) if (myCartInfo.cardctrl1 == SOTA)
{ {
if ( (keys_pressed & KEY_R) ) { myCartInfo.xOffset +=28; bRefreshXY = true; } if ( (keys_pressed & (KEY_R | KEY_X)) ) { if (myCartInfo.xOffset < 50) myCartInfo.xOffset +=28; bRefreshXY = true; }
if ( (keys_pressed & KEY_L) ) { myCartInfo.xOffset -=28; bRefreshXY = true; } if ( (keys_pressed & (KEY_L | KEY_Y)) ) { if (myCartInfo.xOffset > 28) myCartInfo.xOffset -=28; bRefreshXY = true; }
} }
if (dampen < 6) dampen = 6; if (dampen < 6) dampen = 6;
} }
@ -1080,7 +1154,7 @@ ITCM_CODE void dsMainLoop(void)
snes_adaptor = 0x0000FFFF; // Nothing pressed to start snes_adaptor = 0x0000FFFF; // Nothing pressed to start
// manage a7800 pad // manage a7800 joystick
if ( (keys_pressed & KEY_UP) ) { tchepres(0); snes_adaptor &= 0xFFEF;} // UP if ( (keys_pressed & KEY_UP) ) { tchepres(0); snes_adaptor &= 0xFFEF;} // UP
if ( (keys_pressed & KEY_DOWN) ) { tchepres(1); snes_adaptor &= 0xFFDF;} // DOWN if ( (keys_pressed & KEY_DOWN) ) { tchepres(1); snes_adaptor &= 0xFFDF;} // DOWN
if ( (keys_pressed & KEY_LEFT) ) { tchepres(2); snes_adaptor &= 0xFFBF;} // LEFT if ( (keys_pressed & KEY_LEFT) ) { tchepres(2); snes_adaptor &= 0xFFBF;} // LEFT
@ -1110,8 +1184,42 @@ ITCM_CODE void dsMainLoop(void)
{ {
if ( (keys_pressed & KEY_A) ) { tchepres(4); snes_adaptor &= 0xFEFF;} // BUTTON #1 if ( (keys_pressed & KEY_A) ) { tchepres(4); snes_adaptor &= 0xFEFF;} // BUTTON #1
if ( (keys_pressed & KEY_B) ) { tchepres(5); snes_adaptor &= 0xFFFE;} // BUTTON #2 if ( (keys_pressed & KEY_B) ) { tchepres(5); snes_adaptor &= 0xFFFE;} // BUTTON #2
if ( (keys_pressed & KEY_Y) ) { temp_shift = 16; shift_dampen = 1;} // Shift Screen Down
if ( (keys_pressed & KEY_X) ) { temp_shift = -16; shift_dampen = 1;} // Shift Screen Up if (myCartInfo.cardctrl1 != SOTA)
{
if ( (keys_pressed & KEY_Y) )
{
switch (myCartInfo.yButton)
{
case KEY_MAP_DEFAULT: { temp_shift = 16; shift_dampen = 1; break; } // Shift Screen Down
case KEY_MAP_PANUP: { temp_shift = -16; shift_dampen = 1; break; } // Shift Screen Up
case KEY_MAP_PANDN: { temp_shift = 16; shift_dampen = 1; break; } // Shift Screen Down
case KEY_MAP_JOYUP: { tchepres(0); snes_adaptor &= 0xFFEF; break; } // Joystick Up
case KEY_MAP_JOYDN: { tchepres(1); snes_adaptor &= 0xFFDF; break; } // Joystick Down
case KEY_MAP_JOYLEFT: { tchepres(2); snes_adaptor &= 0xFFBF; break; } // Joystick Left
case KEY_MAP_JOYRIGHT: { tchepres(3); snes_adaptor &= 0xFF7F; break; } // Joystick Right
case KEY_MAP_JOYB1: { tchepres(4); snes_adaptor &= 0xFEFF; break; } // Joystick Button #1
case KEY_MAP_JOYB2: { tchepres(5); snes_adaptor &= 0xFFFE; break; } // Joystick Button #2
case KEY_MAP_PAUSE: { tchepres_value = 10; tchepres_delay = 5; tchepres(10); break; } // Console Pause Button
}
}
if ( (keys_pressed & KEY_X) )
{
switch (myCartInfo.xButton)
{
case KEY_MAP_DEFAULT: { temp_shift = -16; shift_dampen = 1; break; } // Shift Screen Up
case KEY_MAP_PANUP: { temp_shift = -16; shift_dampen = 1; break; } // Shift Screen Up
case KEY_MAP_PANDN: { temp_shift = 16; shift_dampen = 1; break; } // Shift Screen Down
case KEY_MAP_JOYUP: { tchepres(0); snes_adaptor &= 0xFFEF; break; } // Joystick Up
case KEY_MAP_JOYDN: { tchepres(1); snes_adaptor &= 0xFFDF; break; } // Joystick Down
case KEY_MAP_JOYLEFT: { tchepres(2); snes_adaptor &= 0xFFBF; break; } // Joystick Left
case KEY_MAP_JOYRIGHT: { tchepres(3); snes_adaptor &= 0xFF7F; break; } // Joystick Right
case KEY_MAP_JOYB1: { tchepres(4); snes_adaptor &= 0xFEFF; break; } // Joystick Button #1
case KEY_MAP_JOYB2: { tchepres(5); snes_adaptor &= 0xFFFE; break; } // Joystick Button #2
case KEY_MAP_PAUSE: { tchepres_value = 10; tchepres_delay = 5; tchepres(10); break; } // Console Pause Button
}
}
}
} }
if ((keys_pressed & KEY_R) || (keys_pressed & KEY_L)) if ((keys_pressed & KEY_R) || (keys_pressed & KEY_L))
@ -1124,9 +1232,26 @@ ITCM_CODE void dsMainLoop(void)
// ------------------------------------------------------------- // -------------------------------------------------------------
if (TIMER1_DATA >= 32728) // 1000MS (1 sec) if (TIMER1_DATA >= 32728) // 1000MS (1 sec)
{ {
TIMER1_CR = 0; if (bHSC_dirty) // Check to see if the High Score area has changed ... if so, snap out the .hsc file
TIMER1_DATA = 0; {
TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024; if (high_score_cart_loaded && myCartInfo.hsc) // Check if HSC is enabled
{
if (++spamHSC <= 8) // Check that we haven't been spamming the HSC saving...
{
dsPrintValue(11,0,0, "HSC SAVING");
cartridge_SaveHighScoreSram();
bJustSavedHSC = 1;
}
}
bHSC_dirty = 0;
}
else if (bJustSavedHSC)
{
bJustSavedHSC = 0;
if (spamHSC <= 8) spamHSC = 0;
dsPrintValue(11,0,0, " ");
}
if (fpsDisplay) if (fpsDisplay)
{ {
int fps = gTotalAtariFrames; int fps = gTotalAtariFrames;
@ -1145,6 +1270,11 @@ ITCM_CODE void dsMainLoop(void)
} }
} }
DumpDebugData(); DumpDebugData();
// And finally reset the frame timer...
TIMER1_CR = 0;
TIMER1_DATA = 0;
TIMER1_CR=TIMER_ENABLE | TIMER_DIV_1024;
} }
break; break;
} }

View file

@ -57,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,5 +1,5 @@
// ===================================================================================== // =====================================================================================
// Copyright (c) 2022-2024 Dave Bernazzani (wavemotion-dave) // Copyright (c) 2022-2025 Dave Bernazzani (wavemotion-dave)
// //
// Copying and distribution of this emulator, it's source code and associated // Copying and distribution of this emulator, it's source code and associated
// readme files, with or without modification, are permitted in any medium without // readme files, with or without modification, are permitted in any medium without
@ -65,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;
} }
@ -138,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?
} }
} }
@ -200,12 +200,13 @@ 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", "SNES2ATARI"}, &myCartInfo.cardctrl1, 7}, {"LEFT JOY", 0, {"NONE", "JOYSTICK", "LIGHTGUN", "PADDLES", "TWIN STICKS", "SOTA", "SNES2ATARI"}, &myCartInfo.cardctrl1, 7},
{"RIGHT JOY", 0, {"NONE", "JOYSTICK", "LIGHTGUN", "PADDLES", "TWIN STICKS", "SOTA", "SNES2ATARI"}, &myCartInfo.cardctrl2, 7}, {"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},
@ -213,8 +214,6 @@ const struct options_t Game_Option_Table[] =
{"X JIGGLE", 1, {"+1", "+256"}, (u8*)&myCartInfo.xJiggle, 2}, {"X JIGGLE", 1, {"+1", "+256"}, (u8*)&myCartInfo.xJiggle, 2},
{"Y JIGGLE", 1, {"+1", "+256"}, (u8*)&myCartInfo.yJiggle, 2}, {"Y JIGGLE", 1, {"+1", "+256"}, (u8*)&myCartInfo.yJiggle, 2},
{NULL, 0, {"", ""}, NULL, 1}, {NULL, 0, {"", ""}, NULL, 1},
}; };

View file

@ -34,13 +34,28 @@
// --------------------------- // ---------------------------
// Config handling... // Config handling...
// --------------------------- // ---------------------------
#define CONFIG_VER 0x0009 #define CONFIG_VER 0x000B
#define MAX_CONFIGS 680 #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,11 +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 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.
@ -93,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
{ {
@ -122,9 +129,11 @@ 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));
} }
} }
@ -134,9 +143,9 @@ void cartridge_SwapRAM_DragonFlyStyle(u8 data)
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
{ {
@ -286,6 +295,8 @@ static void cartridge_ReadHeader(const byte* header) {
// bit 1 : 0=component, 1=composite // bit 1 : 0=component, 1=composite
// bit 2 : 0=single region, 1=multi-region // bit 2 : 0=single region, 1=multi-region
myCartInfo.region = header[57] & 1; myCartInfo.region = header[57] & 1;
//use_composite_filtering = header[57] & 2;
use_composite_filtering = 0;
// High Score Support // High Score Support
// bit 0 : HSC // bit 0 : HSC
@ -391,7 +402,6 @@ static void cartridge_ReadHeader(const byte* header) {
} }
} }
myCartInfo.dma_adjust = 0;
last_bank = 255; last_bank = 255;
last_ex_ram_bank = 0; last_ex_ram_bank = 0;
ex_ram_bank = 0; ex_ram_bank = 0;
@ -399,7 +409,7 @@ static void cartridge_ReadHeader(const byte* header) {
write_only_pokey_at_4000 = false; write_only_pokey_at_4000 = false;
ex_ram_bank_df = 0; 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
} }
@ -472,12 +482,18 @@ 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));
memset((u8*)0x06830000, 0x00, (64*1024)); // Clear this 128K chunk of fast VRAM as we use it for RAM bankswitch // -----------------------------------------------------------------
// 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*)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
@ -510,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;
} }
@ -537,6 +556,14 @@ 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:
@ -630,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:
@ -657,7 +688,7 @@ 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);
} }
@ -732,14 +763,54 @@ void cartridge_Release( )
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;
use_composite_filtering = 0;
cart_restore = 0;
write_only_pokey_at_4000 = false; 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,6 +36,10 @@
#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
@ -108,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,243 +31,305 @@
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[] = {
// The original NTSC Commercial Games // The original NTSC Commercial Games
{"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 {"0be996d25144966d", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 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 {"877dcc97a775ed55", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 7, 22, 264, 230, 0}, // title=Alien Brigade
{"07342c78619ba6ffcc61c10e907e3b50", "Asteroids", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 230, 0}, // title=Asteroids {"07342c78619ba6ff", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 17, 256, 230, 0}, // title=Asteroids
{"8fc3a695eaea3984912d98ed4a543376", "Ballblazer", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 6, 12, 262, 220, 0}, // title=Ballblazer {"8fc3a695eaea3984", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 240, 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 {"42682415906c21c6", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 155, 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 {"f5f6b69c5eb4b55f", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 2, 15, 262, 226, 0}, // title=Basketbrawl
{"5a09946e57dbe30408a8f253a28d07db", "Centipede", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 24, 16, 300, 230, 0}, // title=Centipede {"5a09946e57dbe304", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 24, 16, 300, 230, 0}, // title=Centipede
{"93e4387864b014c155d7c17877990d1e", "Choplifter", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Choplifter {"93e4387864b014c1", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 210, 0, 15, 256, 226, 0}, // title=Choplifter
{"2e8e28f6ad8b9b9267d518d880c73ebb", "Commando", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 20, 0, 12, 256, 218, 1}, // title=Commando {"2e8e28f6ad8b9b92", CT_SUPCAR, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 218, 0}, // 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 {"db691469128d9a42", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 18, 256, 229, 0}, // title=Crack'ed
{"a94e4560b6ad053a1c24e096f1262ebf", "Crossbow", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 5, 12, 267, 231, 0}, // title=Crossbow {"a94e4560b6ad053a", CT_SUPLRG, POKEY_NONE, LGN, LGN, DIFF_A, DIFF_A, NTSC, HSC_NO, 150, 9, 17, 272, 234, 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 {"179b76ff729d4849", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 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 {"95ac811c7d27af00", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 240, 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 {"731879ea82fc0ca2", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 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 {"5e332fbfc1e0fc74", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Donkey Kong Jr
{"19f1ee292a23636bd57d408b62de79c7", "Donkey Kong Orig", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 14, 256, 220, 0}, // title=Donkey Kong {"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)
{"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 {"19f1ee292a23636b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Donkey Kong
{"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 {"543484c00ba23373", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 12, 256, 220, 0}, // title=Double Dragon
{"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 {"2251a6a0f3aec84c", CT_ABSOLU, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 12, 256, 220, 0}, // title=F-18 Hornet
{"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 {"d25d5d19188e9f14", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 155, 0, 12, 256, 220, 0}, // title=Fatal Run
{"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 {"07dbbfe612a0a28e", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 12, 14, 282, 226, 0}, // title=Fight Night
{"fb8d803b328b2e442548f7799cfa9a4a", "Galaga", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 229, 0}, // title=Galaga {"cf76b00244105b8e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 255, 227, 0}, // title=Food Fight
{"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 {"fb8d803b328b2e44", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 12, 256, 229, 0}, // title=Galaga
{"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 {"fd9e78e201b6baaf", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 0, 17, 256, 227, 0}, // title=Hat Trick
{"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 {"c3672482ca93f70e", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 180, 0, 17, 256, 230, 0}, // title=Ikari Warriors
{"045fd12050b7f2b842d5970f2414e912", "Jinks", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 3, 12, 261, 234, 0}, // title=Jinks {"baebc9246c087e89", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 12, 256, 220, 0}, // title=Impossible Mission
{"f18b3b897a25ab3885b43b4bd141b396", "Joust", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 234, 0}, // title=Joust {"045fd12050b7f2b8", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 3, 12, 261, 234, 1}, // title=Jinks
{"c3a5a8692a423d43d9d28dd5b7d109d9", "Karateka", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Karateka {"f18b3b897a25ab38", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 140, 0, 17, 256, 234, 0}, // title=Joust
{"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 {"c3a5a8692a423d43", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 0, 12, 256, 220, 0}, // title=Karateka
{"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 {"f57d0af323d4e173", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 22, 17, 276, 225, 0}, // title=Kung Fu Master
{"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 {"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
{"431ca060201ee1f9eb49d44962874049", "Mario Bros", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 14, 256, 220, 0}, // title=Mario Bros. {"bc1e905db1008493", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 13, 256, 226, 0}, // title=Midnight Mutants
{"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 {"431ca060201ee1f9", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 140, 0, 14, 256, 220, 0}, // title=Mario Bros.
{"bedc30ec43587e0c98fc38c39c1ef9d0", "Meltdown", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Meltdown {"37b5692e33a98115", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 125, 0, 12, 256, 220, 0}, // title=Mat Mania Challenge
{"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 {"bedc30ec43587e0c", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 12, 256, 220, 0}, // title=Meltdown
{"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 {"3bc8f554cf86f813", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Motor Psycho
{"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 {"fc0ea52a9fac5572", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 170, 0, 17, 256, 224, 0}, // title=Ms. Pac-Man
{"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 {"220121f771fc4b98", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 10, 20, 270, 234, 0}, // title=Ninja Golf
{"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 {"74569571a208f8b0", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 245, 0, 12, 256, 224, 0}, // title=One On One
{"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 {"1a5207870dec6fae", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 120, 0, 12, 256, 220, 0}, // title=Pete Rose Baseball
{"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 {"33aea1e2b6634a1d", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 175, 0, 15, 256, 226, 0}, // title=Planet Smashers
{"ac03806cef2558fc795a7d5d8dba7bc0", "Rampage", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Rampage {"584582bb09ee8122", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 210, 39, 15, 320, 230, 0}, // title=Pole Position II
{"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 {"ac03806cef2558fc", CT_ACTVIS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 1, 16, 259, 234, 0}, // title=Rampage
{"66ecaafe1b82ae68ffc96267aaf7a4d7", "Robotron", CT_NORMAL, POKEY_NONE, TWIN,TWIN, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 5, 13, 270, 234, 0}, // title=Robotron {"383ed9bd1efb9b6c", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 1}, // title=Realsports Baseball
{"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 {"66ecaafe1b82ae68", CT_NORMAL, POKEY_NONE, TWIN,TWIN, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 5, 13, 270, 234, 0}, // title=Robotron
{"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 {"980c35ae9625773a", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 8, 18, 265, 234, 0}, // title=Scrapyard Dog
{"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 {"cbb0746192540a13", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 13, 256, 220, 0}, // title=Summer Games
{"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' {"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
{"5c4f752371a523f15e9980fea73b874d", "Tank Command", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Tank Command {"59b5793bece1c80f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 130, 0, 12, 256, 220, 0}, // title=Super Skatebordin'
{"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 {"5c4f752371a523f1", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 150, 0, 12, 256, 220, 0}, // title=Tank Command
{"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 {"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
{"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 {"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
{"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 {"208ef955fa90a298", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Touchdown Football
{"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 {"8d64763db3100aad", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 8, 320, 234, 1}, // title=Tower Toppler
{"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 {"427cb05d0a1abb06", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 150, 0, 3, 256, 197, 0}, // title=Water Ski
{"05fb699db9eef564e2fe45c568746dbc", "Xenophobe", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 15, 13, 284, 234, 0}, // title=Xenophobe {"3799d72f78dda2ee", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 170, 0, 13, 256, 220, 0}, // title=Winter Games
{"d7dc17379aa25e5ae3c14b9e780c6f6d", "Xevious", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 220, 0}, // title=Xevious {"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 // Prototypes and Hacks
{"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 {"4332c24e4f3bc72e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=3D Asteroids
{"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 {"1745feadabb24e7c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Impossible Mission
{"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 {"20660b667df538ec", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 24, 16, 300, 230, 0}, // title=Centipede - Frameless Hack
{"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 {"8f7eb10ad0bd7547", CT_FRACTALUS, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 180, 0, 12, 256, 220, 0}, // title=Rescue On Fractalus
{"06204dadc975be5e5e37e7cc66f984cf", "Gato", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Gato {"06204dadc975be5e", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Gato
{"17b3b764d33eae9b5260f01df7bb9d2f", "Klax", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 20, 256, 234, 0}, // title=Klax (fixed) {"17b3b764d33eae9b", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 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 {"5fb805f2b69820a9", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 130, 0, 20, 256, 234, 0}, // title=Klax
{"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 {"017066f522908081", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 145, 0, 12, 256, 220, 0}, // title=Missing in Action
{"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 {"ec206c8db4316eb1", CT_SUPROM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Pit Fighter
{"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 {"74f0283c566bdee8", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 180, 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) {"86546808dc60961c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 180, 0, 17, 256, 234, 0}, // title=Plutos (non-XM)
{"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 {"1745feadabb24e7c", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 0, 12, 256, 220, 0}, // title=Possible Mission
{"b697d9c2d1b9f6cb21041286d1bbfa7f", "Sentinel", CT_SUPROM, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Sentinel {"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 // Bob (pacmanplus) Games
{"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 {"89b8b3df46733e0c", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 0, 12, 256, 220, 0}, // title=Armor Attack II
{"eea04359df6770d66b0d97c2cea1932f", "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 (20230627) {"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)
{"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 (NTSC) (20071014) {"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)
{"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 {"3d38281ed8a8d8c7", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 245, 30, 9, 320, 210, 0}, // title=Astro Blaster
{"55ffe535897c368be7a80d582f6a68cb", "Astro Blaster", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 0, 30, 9, 320, 210, 0}, // title=Astro Blaster (20230627) {"55ffe535897c368b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 245, 30, 9, 320, 210, 0}, // title=Astro Blaster (20230627)
{"a51e5df28a0fe8c52e9d28fb5f8e44a6", "Astro Fighter", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 32, 9, 320, 213, 0}, // title=Astro Fighter {"a51e5df28a0fe8c5", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 32, 9, 320, 213, 0}, // title=Astro Fighter
{"8feb090fb1aee5cc19c2d8f36a4f64ae", "Astro Fighter", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 32, 9, 320, 213, 0}, // title=Astro Fighter (v1.1) {"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)
{"7cdfbe37634e7dcd4dc67db7edbcd3ba", "Baby Pac-Man", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 32, 12, 320, 222, 0}, // title=Baby Pac Man {"7cdfbe37634e7dcd", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 250, 32, 12, 320, 222, 0}, // title=Baby Pac Man
{"2b31dfab41dce110dd136fd06606e1ca", "Baby Pac-Man", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 32, 12, 320, 222, 0}, // title=Baby Pac Man (20230627) {"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)
{"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 {"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
{"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 {"299d31c8e181fdd0", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 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 {"2d2fe4da9f1bae10", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 9, 256, 232, 0}, // title=Crazy Otto
{"100551363027dc5f093d049a5fd00933", "Crazy Otto", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 9, 256, 232, 0}, // title=Crazy Otto (20230627) {"100551363027dc5f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 165, 0, 9, 256, 232, 0}, // title=Crazy Otto (20230627)
{"6287727ab36391a62f728bbdee88675c", "Failsafe", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 14, 257, 220, 0}, // title=FailSafe (NTSC) (20100227) {"6287727ab36391a6", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 250, 0, 14, 257, 220, 0}, // title=FailSafe (NTSC) (20100227)
{"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) {"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)
{"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) (20211025) {"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)
{"2f4ae1015a345652b36004a8c62a4ac6", "Galaxian", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 14, 5, 282, 211, 0}, // title=Galaxian {"2f4ae1015a345652", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 14, 17, 283, 227, 0}, // title=Galaxian
{"686a4e4dde0eca5c7984829c4d30d3cf", "Galaxian", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 14, 5, 282, 211, 0}, // title=Galaxian (v1.1) {"686a4e4dde0eca5c", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 14, 17, 283, 227, 0}, // title=Galaxian (v1.1)
{"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 {"e54edc299e72d22d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 4, 14, 268, 234, 0}, // title=Jr Pac-Man
{"bde3abe40d302d8c4c65c9690c05dbc4", "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 (20230627) {"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)
{"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 {"6b8600aabd11f834", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 3, 12, 256, 225, 0}, // title=KC Munchkin
{"aa0b9560d6610378bda58f09f265d6ad", "KC Munchkin", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 3, 12, 256, 225, 0}, // title=KC Munchkin (20230627) {"aa0b9560d6610378", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 160, 3, 12, 256, 225, 0}, // title=KC Munchkin (20230627)
{"927edf157f88b8f5863254d9a65f05a8", "KC Munchkin", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 3, 12, 256, 225, 0}, // title=KC Munchkin (Alt Movement) (20170409) {"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)
{"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 {"c3f6201d6a9388e8", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 13, 256, 220, 0}, // title=Meteor Shower
{"a44241d782ee14b53483487cc8216274", "Meteor Shower", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 13, 256, 220, 0}, // title=Meteor Shower (v1.1) {"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)
{"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 {"9ff38ea62004201d", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 210, 30, 20, 320, 234, 0}, // title=Moon Cresta
{"a56f26e6d21b2ae4880f0fd410243cc1", "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 (v1.1) {"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)
{"cf007563fe94cacf5ea5295dc93ce9ef", "Ms. Pac-Man Bob", 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) {"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)
{"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 {"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
{"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 {"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
{"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) {"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)
{"6a432fd1e9d42a294bdb04d2c3d1e17f", "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 - Ultimate Edition (POKEY) (4000) {"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)
{"200ef9c7b36afd3b1b6ce32065a259bb", "Pac-Man Collection - Remastered", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_YES, 0, 10, 11, 281, 231, 0}, // title=Pac-Man Collection - Remastered (TIA) {"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)
{"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 {"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)
{"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 {"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
{"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 {"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
{"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 {"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
{"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) {"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
{"39dc7f6f39f9b3e341a5ffea76e71fb1", "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 (20230627) {"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)
{"2686f20449c339f7d31671f1cdec7249", "Pac-Man 40th Anniversary", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 11, 17, 279, 233, 0}, // title=Pac-Man Collection - 40th Anniversary Edition (Short Mazes) (20230627) {"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)
{"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 {"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)
{"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 {"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)
{"803743fe18600f292456539906464421", "Rip-Off", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 256, 233, 0}, // title=Rip-Off (20230627) {"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
{"a3a85e507d6f718972b1464ce1aaf8a4", "Scramble", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 30, 5, 320, 205, 0}, // title=Scramble (homebrew) {"43525a0405184875", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 17, 256, 233, 0}, // title=Rip-Off
{"31b20a4710e691300bb4aa62cf02284c", "Scramble", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 30, 5, 320, 205, 0}, // title=Scramble (20230627) {"803743fe18600f29", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 160, 0, 17, 256, 233, 0}, // title=Rip-Off (20230627)
{"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) {"a3a85e507d6f7189", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 250, 30, 5, 320, 205, 0}, // title=Scramble (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) {"31b20a4710e69130", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 250, 30, 5, 320, 205, 0}, // title=Scramble (20230627)
{"6a898d52ef050cdb89442e91cc529d14", "Space Invaders", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 30, 8, 320, 210, 0}, // title=Space Invaders (v1.1) {"771cb4609347657f", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 200, 0, 12, 256, 220, 0}, // title=Space Duel (homebrew)
{"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 {"6adf79558a3d7f5b", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 165, 30, 8, 320, 210, 0}, // title=Space Invaders (Homebrew)
{"88b9de0eba37ba516590fa8b860155f0", "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 (20230627) {"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)
{"79df20ee86a989e669158bcb9d113e8a", "UniWarS", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 9, 5, 282, 202, 0}, // title=UniWarS {"7ab539bb0e99e1e5", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 205, 0, 5, 256, 225, 0}, // title=Super Pac-Man
{"0db69825c81171e62fd2bda526665c22", "UniWarS", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 9, 5, 282, 202, 0}, // title=UniWarS (v1.1) {"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)
// Other homebrews {"",CT_NORMAL,0,0,0,0,0,0,0,0,0,0,0},
{"fd9353d42cca5f81fe7af866592b94c3", "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)
{"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)
{"6f157f421c7ed5d952b393122d37915e", "2048", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 22, 14, 299, 230, 0}, // title=2048 (RC1a) (20211113)
{"a837a34f540fd1371bfcfb8e8af4c375", "7ix", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 14, 16, 286, 234, 0}, // title=7iX (20220305)
{"ff056f2858f14fc4725fcb0015d78d3b", "A Roach in Space", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 3, 15, 261, 234, 0}, // title=A Roach In Space - Part II - Electric Bugaloo (20201119)
{"d99bff88cd3cce191c26f5755842eb21", "Arkanoid (ChunkyPixel Games)", 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 (ChunkyPixel Games)", 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)
{"4a8a22cff154f479f1ddaa386f21fc39", "Boom", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 7, 256, 220, 0}, // title=Boom!
{"9fa7743a016c9b7015ee1d386326f88e", "b*nQ", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, -3, 0, 18, 256, 223, 0}, // title=BonQ (Final Atariage)
{"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)
{"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)
{"825c03c049306c16bd654d9d0e344cf3", "Cartesian Chaos", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 260, 234, 0}, // title=Cartesian Chaos (v11) (20221218)
{"a4b5d742860beb25c29def4530194c1e", "Chase", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 8, 19, 269, 233, 0}, // title=Chase (20201231)
{"0c2f248a1ae9bfd14b1bcc1bd9f3a41e", "Danger Zone", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 14, 256, 234, 0}, // title=Danger Zone (RC-4C) (NTSC Demo) (20201231)
{"dd1cfc933d2bfacc613ef745c1366438", "Donkey Kong PK-XM", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 210, 0}, // title=Donkey Kong PK-XM (NTSC) (Demo) (v1.2)
{"77164df89ae49b4dd72906a21e787233", "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 (20210731)
{"8c2798f929a43317f300d3ccbe25918e", "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 (Demo Dec 2022)
{"7b7825ca2c79148f1c4ade6baacc1a76", "Dragon's Cache", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 6, 15, 256, 232, 0}, // title=Dragon's Cache (20210207)
{"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 (Beta 4)
{"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 (20151022)
{"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 A)
{"6053233cb59c0b4ca633623fd76c4576", "Froggie", CT_NORMAL, POKEY_AT_4000, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 34, 8, 320, 205, 0}, // title=Froggie (NTSC) (Final Release)
{"9daaac9b25783a7e3c8858f3987ed18d", "Frogus", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 224, 0}, // title=Frogus (20221020)
{"c2e131a091ceed2e04e71f19219b7804", "Game of the Bear", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 15, 256, 234, 0}, // title=Game of the Bear - Polar Opposites (RC1) (20230211)
{"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 (20170910)
{"1c9deabc48f07d1bf2c68731fccd27b5", "Keystone Koppers", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Keystone Koppers (Demo Dec 22)
{"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 (20210116)
{"33dbb58f9ee73e9f476b4ebbc8190c88", "Knight Guy In Low Res", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 12, 256, 233, 0}, // title=Knight Guy In Low Res World - Castle Days (RC 01-1)
{"3ec728e116017be89c552a85a8f86d90", "Knight Guy - Quest", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 12, 256, 233, 0}, // title=Knight Guy - Quest For Something (20210423)
{"7abd9e0a6321e813d7528aa7f2c8fb40", "Knight Guy In Another", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_B, DIFF_B, NTSC, HSC_NO, 0, 0, 12, 256, 233, 0}, // title=Knight Guy - In Another Castle (RC1b)
{"271864e0978278a3e2fb04273db69d57", "Legend of Silverpeak", CT_SUPCAR, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 7, 256, 201, 1}, // title=Legend of Silverpeak (1.01)
{"181a9978d9da7a7e21f770808cc681f2", "Merlain", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 17, 320, 234, 0}, // title=Merlain
{"3f80432f156088bf328cff15842766ee", "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) (POKEY 450) (20230305)
{"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 (20220306)
{"d1b56eae7227c12d0122bb84925c89c0", "Morf", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 17, 256, 224, 0}, // title=Morf (20220314)
{"ac5c99ac01c96ad92832c0544889a702", "Ninjish Guy - Perilous Island", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 16, 256, 234, 0}, // title=Ninjish Guy - Perilous Island (20211107)
{"3d9c52142f9e53f516d3408daad376f3", "Oozy The Goo - Gaiden", CT_SUPLRG, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 17, 256, 234, 0}, // title=Oozy The Goo - Gaiden (20231001)
{"6ac5a7f8b6a3198ed08abb9866753763", "PentaGo", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 2, 17, 260, 234, 0}, // title=PentaGo (Demo) (20230422)
{"a662862f20362fc5eb5c651065cbd51c", "Petscii Robots", CT_BANKSHALT,POKEY_AT_800,SNES,JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 10, 256, 214, 0}, // title=Attack of the Petscii Robots (Demo) (POKEY 800)
{"0254afa887fcfc8c4b1a63b41b9ba613", "Plumb Luck DX", CT_SUPRAM, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 16, 17, 289, 220, 0}, // title=Plumb Luck DX (RC2) (20230410)
{"61e6a16889b62216116eea136269a4c0", "Popeye", CT_SUPLRG, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 10, 256, 210, 0}, // title=Popeye 2.40 Demo
{"b6561537290e6e25e1249394366c3c63", "Robbo", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 220, 0}, // title=Robbo (20160513)
{"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=Robots Rumble (20220217)
{"9bd70c06d3386f76f8162881699a777a", "Serpentine", CT_SUPRAM, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 12, 256, 220, 0}, // title=Serpentine (20161029)
{"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 (20171202)
{"40567f50c569a60cc461cdf0e2853ff4", "Slide Boy in Maze Land", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 230, 0}, // title=Slide Boy in Maze Land (RC1) (20210515)
{"91f4cb1f642ff1de936a74672dce7198", "Space Peril", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_YES, 0, 0, 16, 256, 230, 0}, // title=Space Peril (NTSC) (v8) (20210907)
{"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 (NTSC) (20201223)
{"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 Atari Age (NTSC) (Joystick) (POKEY 4000)
{"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
{"23ac803eabfb8c6118b600a8177c94a4", "Lyra the Tenrec", CT_NORMAL, POKEY_AT_450, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 0, 12, 256, 234, 0}, // title=Lyra the Tenrec (20240104)
{"ff825fcbed9bf6993edd422fcc592673", "Touchdown Challenge", CT_NORMAL, POKEY_NONE, JOY, JOY, DIFF_A, DIFF_A, NTSC, HSC_NO, 0, 1, 30, 258, 234, 0}, // title=Touchdown Challenge (v2_21) (20230225)
{"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
{"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 (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; extern u8 bNoDatabase;
bool bFound = false; bool bFound = false;
uint16 i; uint16 i;
// Uppercase the filename... to make searching easier.
for(int j = 0; j < strlen(cartridge_filename); j++) cartridge_filename[j] = toupper(cartridge_filename[j]);
// See if we've been asked to skip the internal database // See if we've been asked to skip the internal database
if (!bNoDatabase) if(!bNoDatabase)
{ {
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// First see if we've got a match in our external A7800DS.DAT configuration database... // First see if we've got a match in our external A7800DS.DAT configuration database...
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
for (i=0; i<MAX_CONFIGS; i++) for(i = 0; i < MAX_CONFIGS; i++)
{ {
if (!strcmp(allConfigs.cart[i].digest,(char *) digest)) if(!strncmp(allConfigs.cart[i].half_digest, (char *) digest, 16))
{ {
memcpy(&myCartInfo, &allConfigs.cart[i], sizeof(myCartInfo)); memcpy( & myCartInfo, & allConfigs.cart[i], sizeof(myCartInfo));
bFound = true; bFound = true;
break; break;
} }
} }
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
// If we didn't find it in the config database, we can look in the internal database table... // If we didn't find it in the config database, we can look in the internal database table...
// --------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------
if (!bFound) if(!bFound)
{ {
/* Look up mapper in game list by md5sum */ /* Look up mapper in game list by md5sum */
for(i = 0; strlen(game_list[i].digest); i++) for(i = 0; strlen(game_list[i].half_digest); i++)
{ {
if (!strcmp(game_list[i].digest,(char *) digest)) if(!strncmp(game_list[i].half_digest, (char *) digest, 16))
{ {
memcpy(&myCartInfo, &game_list[i], sizeof(myCartInfo)); memcpy( & myCartInfo, & game_list[i], sizeof(myCartInfo));
if (!isDSiMode()) myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; // DS-Lite defaults to frame skipping no matter what the DB says... user can override 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.palette = 1; // Force this if not specifically found by md5
myCartInfo.xJiggle = 64; myCartInfo.xJiggle = 64;
myCartInfo.yJiggle = 16; myCartInfo.yJiggle = 16;
@ -277,37 +341,30 @@ bool database_Load(byte *digest)
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// If we didn't find a definitive md5 match above, look up game by cart title in the .A78 header // If we didn't find a definitive md5 match above, look up game by name in our name mapping table.
// or even by the name of the ROM filename as it will give us a clue as to the game identity.
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
if (!bFound) if(!bFound)
{ {
for(i = 0; strlen(game_list[i].header_name); i++) for(int k = 0; strlen(NameMap[k].name1); k++)
{ {
if (myCartInfo.region == NTSC) if(myCartInfo.region == NTSC)
{ {
if ( (!strcmp(game_list[i].header_name,(char *) cartridge_title)) || if((strstr(cartridge_filename, NameMap[k].name1)) && (strstr(cartridge_filename, NameMap[k].name2))) // If both names are found in the filename...
(strstr((char *) cartridge_filename, game_list[i].header_name) != NULL) )
{ {
memcpy(&myCartInfo, &game_list[i], sizeof(myCartInfo)); /* Look up mapper in game list by md5sum from the Name Mapper table */
strcpy(myCartInfo.digest, (char *)digest); for(i = 0; strlen(game_list[i].half_digest); i++)
if (!isDSiMode()) myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; // DS-Lite defaults to frame skipping no matter what the DB says... user can override {
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.palette = 1; // Force this if not specifically found by md5
myCartInfo.xJiggle = 64; myCartInfo.xJiggle = 64;
myCartInfo.yJiggle = 16; myCartInfo.yJiggle = 16;
bFound = true; bFound = true;
break;
// -------------------------------------------------------------------------------------- }
// As a sanity check... if the card type ended up as 'NORMAL' but the ROM is larger
// than NORMAL would support - we adjust it here. This prevents soft matches in
// the database from causing problems (I'm looking at you various flavors of Donkey Kong)
// --------------------------------------------------------------------------------------
if (myCartInfo.cardtype == CT_NORMAL)
{
if (cartridge_size == (144*1024)) myCartInfo.cardtype = CT_SUPLRG;
else myCartInfo.cardtype = (cartridge_size <= (52*1024)) ? CT_NORMAL:CT_SUPROM;
} }
break; break;
} }
} }
@ -315,39 +372,64 @@ bool database_Load(byte *digest)
} }
} }
// No matter what... override for Tower Toppler to make it playable...
if(strcmp((char *) digest, (char *) "8d64763db3100aadc552db5e6868506a") == 0) // Tower Toppler
{
use_composite_filtering = 76;
myCartInfo.frameSkip = FRAMESKIP_AGGRESSIVE; // It's the only way we stand a chance.
myCartInfo.cardctrl1 = SOTA;
myCartInfo.xOffset = 32;
myCartInfo.yOffset = 8;
myCartInfo.xScale = 320;
myCartInfo.yScale = 234;
bFound = 1;
}
else if(strcmp((char *) digest, (char *) "f5150c0fc1948832211e57852abb0c6e") == 0) // 7800 Utility Cart
{
use_composite_filtering = 1;
bFound = 1;
}
// 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.spare1 = 0; myCartInfo.xButton = KEY_MAP_DEFAULT;
myCartInfo.yButton = KEY_MAP_DEFAULT;
myCartInfo.spare2 = 0; myCartInfo.spare2 = 0;
myCartInfo.spare3 = 0; myCartInfo.spare3 = 0;
myCartInfo.spare4 = 1; myCartInfo.spare4 = 1;
myCartInfo.spare5 = 0; myCartInfo.spare5 = 0;
myCartInfo.palette = 1; myCartInfo.palette = 1;
myCartInfo.dma_adjust = 0;
myCartInfo.xJiggle = 64; myCartInfo.xJiggle = 64;
myCartInfo.yJiggle = 16; 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;
@ -357,14 +439,50 @@ bool database_Load(byte *digest)
// -------------------------------------------------------- // --------------------------------------------------------
// 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;
}
}
} }
} }

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,27 +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 spare0;
u8 spare1;
u8 hasHeader; u8 hasHeader;
u8 palette; u8 palette;
u8 xJiggle; u8 xJiggle;
u8 yJiggle; u8 yJiggle;
u8 spare0; u8 xButton;
u8 spare1; u8 yButton;
u8 spare2; u8 spare2;
u8 spare3; u8 spare3;
u8 spare4; u8 spare4;
s16 spare5; 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

@ -218,6 +218,9 @@ bool cartridge_SaveHighScoreSram(void)
return false; // 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.
} }
// Make sure something actually changed before writing...
if (memcmp(high_score_sram, memory_ram+HS_SRAM_START, HS_SRAM_SIZE) != 0)
{
memcpy(high_score_sram, memory_ram+HS_SRAM_START, HS_SRAM_SIZE); // Copy from main memory to SRAM buffer memcpy(high_score_sram, memory_ram+HS_SRAM_START, HS_SRAM_SIZE); // Copy from main memory to SRAM buffer
make_hsc_name(); // HSC filename is same as base filename with .hsc extension make_hsc_name(); // HSC filename is same as base filename with .hsc extension
@ -229,6 +232,7 @@ bool cartridge_SaveHighScoreSram(void)
fwrite(high_score_sram, HS_SRAM_SIZE, 1, handle); fwrite(high_score_sram, HS_SRAM_SIZE, 1, handle);
fclose(handle); fclose(handle);
} }
}
return true; // We at least made the attempt to write out the .hsc save file return true; // We at least made the attempt to write out the .hsc save file
} }
@ -266,6 +270,8 @@ static bool cartridge_LoadHighScoreSram(void)
memory_Write(HS_SRAM_START + i, high_score_sram[i]); memory_Write(HS_SRAM_START + i, high_score_sram[i]);
} }
bHSC_dirty = 0; // We don't consider the init of SRAM directly above to be a 'write' (no need to persist)
return true; // HSC SRAM is ready to go! return true; // HSC SRAM is ready to go!
} }

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

@ -22,33 +22,38 @@
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// 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; u32 snes_bit_pos = 0;
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 < 0x4000; index++)
for(index = 0; index < 16384/2; index++) { {
ptr[index] = 0xFFFF; memory_ram[index] = 0x00;
is_memory_writable[index >> 8] = 1;
} }
bHSC_dirty = 0;
snes_bit_pos = 0; snes_bit_pos = 0;
bINPTCTRL_locked = 0;
} }
@ -57,10 +62,11 @@ void memory_Reset( )
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
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]; byte tmp_byte = memory_ram[INTFLG];
memory_ram[INTFLG] &= 0x7f; memory_ram[INTFLG] &= 0x7f;
@ -72,18 +78,18 @@ ITCM_CODE byte memory_Read_Slower(word address)
return memory_ram[INTIM]; 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 // 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. // good enough as nothing else should be mapped in this region except POKEY.
if ((address & 0xFFC0) == 0x440) 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)); if((address & 0xFFF0) == 0x800) return pokey_GetRegister(0x4000 | (address & 0xF));
} }
} }
return memory_ram[address]; return memory_ram[address];
@ -97,11 +103,11 @@ 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(myCartInfo.pokeyType == POKEY_AT_4000)
{ {
if ((address & 0xFFF0) == 0x4000) if((address & 0xFFF0) == 0x4000)
{ {
pokey_SetRegister(address, data); pokey_SetRegister(address, data);
return; return;
@ -111,12 +117,12 @@ ITCM_CODE void memory_Write(word address, byte data)
{ {
// Not quite accurate as it will catch anything from 0x440 to 0x4C0 but that's // 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. // good enough as nothing else should be mapped in this region except POKEY.
if ((address & 0xFFC0) == 0x440) if((address & 0xFFC0) == 0x440)
{ {
pokey_SetRegister(0x4000 | (address & 0x0F), data); pokey_SetRegister(0x4000 | (address & 0x0F), data);
return; return;
} }
if ((address & 0xFFF0) == 0x800) // Pokey @800 if((address & 0xFFF0) == 0x800) // Pokey @800
{ {
pokey_SetRegister(0x4000 | (address & 0x0F), data); pokey_SetRegister(0x4000 | (address & 0x0F), data);
return; return;
@ -124,43 +130,63 @@ ITCM_CODE void memory_Write(word address, byte data)
} }
} }
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(is_memory_writable[address >> 8])
{ {
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// If this write would be in normal (non bankset) memory, we need to keep the bankset // 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. // memory up to date as well... This speeds up processing in Maria for banksets handling.
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
extern byte banksets_memory[]; extern u16 banksets_mask; extern byte banksets_memory[];
if (!(address & banksets_mask)) banksets_memory[address] = data; extern u16 banksets_mask;
if(!(address & banksets_mask)) banksets_memory[address] = data;
if ((address & 0xF800)) // Base RAM is at 0x1800 and HSC SRAM is at 0x1000 so this will find anything that is RAM... 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. // For banking RAM we need to keep the shadow up to date.
if ((address & 0xC000) == 0x4000) if((address & 0x5000) == 0x4000)
{ {
extern u8 *shadow_ram; extern u8 * shadow_ram;
shadow_ram[address] = data; shadow_ram[address] = data;
if (myCartInfo.cardtype == CARTRIDGE_TYPE_FRACTALUS) if(myCartInfo.cardtype == CARTRIDGE_TYPE_FRACTALUS)
{ {
// Special EXRAM/A8 handling... mirror ram // Special EXRAM/A8 handling... mirror ram
memory_ram[address ^0x0100] = data; memory_ram[address ^ 0x0100] = data;
} }
}
//else if ((address & 0xF800) == 0x1000) // HSC RAM - someday we might trigger on this and auto-save the .hsc SRAM file
memory_ram[address] = data; memory_ram[address] = data;
return; return;
} }
else if((address & 0xF800) == 0x1000) // HSC RAM - set the dirty bit so we persist the .hsc file in the main loop
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) {
if(memory_ram[address] != data)
switch(address) { {
case INPTCTRL: memory_ram[address] = data;
if(data == 22 && cartridge_IsLoaded( )) { if(address != 0x1007 && (address < 0x17FA)) // Don't count the 'function' address nor the temp score...
cartridge_Store( ); {
bHSC_dirty = 1;
} }
break; }
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: case INPT0:
break; break;
case INPT1: case INPT1:
@ -176,7 +202,7 @@ ITCM_CODE void memory_Write(word address, byte data)
case BACKGRND: case BACKGRND:
memory_ram[BACKGRND] = data; memory_ram[BACKGRND] = data;
bg8 = data; bg8 = data;
bg32 = data | (data<<8) | (data<<16) | (data<<24); bg32 = data | (data << 8) | (data << 16) | (data << 24);
break; break;
case CHARBASE: case CHARBASE:
memory_ram[CHARBASE] = data; memory_ram[CHARBASE] = data;
@ -216,24 +242,26 @@ ITCM_CODE void memory_Write(word address, byte data)
riot_SetDRB(data); riot_SetDRB(data);
break; break;
case SWCHA: case SWCHA:
if (myCartInfo.cardctrl1 == SNES) if(myCartInfo.cardctrl1 == SNES)
{ {
extern byte riot_dra; extern byte riot_dra;
if ((data & 0x20) != (riot_dra & 0x20)) // Change of Latch state if((data & 0x20) != (riot_dra & 0x20)) // Change of Latch state
{ {
snes_bit_pos = 0; snes_bit_pos = 0;
if (snes_adaptor & (1 << snes_bit_pos)) memory_ram[INPT4] |= 0x80; else memory_ram[INPT4] &= 0x7F; 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) != (riot_dra & 0x10)) // Change of Clock state
{ {
if (data & 0x10) // Clock going High if(data & 0x10) // Clock going High
{ {
snes_bit_pos++; snes_bit_pos++;
} }
else // Clock going low else // Clock going low
{ {
if (snes_bit_pos >= 17) snes_bit_pos=0; 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; if(snes_adaptor & (1 << snes_bit_pos)) memory_ram[INPT4] |= 0x80;
else memory_ram[INPT4] &= 0x7F;
} }
} }
} }
@ -257,34 +285,40 @@ ITCM_CODE void memory_Write(word address, byte data)
break; break;
default: default:
memory_ram[address] = data; 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. #ifdef RAM_MIRRORS_ENABLED
if (address >= 8256) // ------------------------------------------------------
// 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) // 0x2040 -> 0x20ff (0x2000)
if(address >= 8256 && address <= 8447) if(address <= 0x20FF)
{ {
memory_ram[address - 8192] = data; memory_ram[address & 0x00FF] = data;
} }
// 0x2140 -> 0x21ff (0x2000) // 0x2140 -> 0x21ff (0x2100)
else if(address >= 8512 && address <= 8703) else if(address >= 0x2140 && address <= 0x21FF)
{ {
memory_ram[address - 8192] = data; memory_ram[address & 0x01FF] = data;
} }
} }
else if (address <= 511) else if(address < 0x200)
{ {
// 0x40 -> 0xff (0x2000) // 0x40 -> 0xff (0x2000)
if(address >= 64 && address <= 255) if(address >= 0x40 && address <= 0xFF)
{ {
memory_ram[address + 8192] = data; memory_ram[address | 0x2000] = data;
} }
// 0x140 -> 0x1ff (0x2000) // 0x140 -> 0x1ff (0x2100)
else if(address >= 320 && address <= 511) else if(address >= 0x140 && address <= 0x1FF)
{ {
memory_ram[address + 8192] = data; memory_ram[address | 0x2000] = data;
} }
} }
#endif #endif
break; break;
} }
} }
@ -297,35 +331,32 @@ ITCM_CODE void memory_Write(word address, byte 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++;
* ramPtr++ = * dataPtr++;
* ramPtr++ = * dataPtr++;
} }
while (--size2); while(--size2);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -333,6 +364,6 @@ ITCM_CODE void memory_WriteROMFast(word address, u32 size, const u32* data)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
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,6 +406,8 @@ extern u32 tiaBufIdx;
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void pokey_Process(void) ITCM_CODE void pokey_Process(void)
{ {
if (tia_wait) return;
byte* sampleCntrPtrB = ((byte*)&pokey_sampleCount[0]) + 1; byte* sampleCntrPtrB = ((byte*)&pokey_sampleCount[0]) + 1;
while (1) while (1)
@ -474,15 +476,23 @@ ITCM_CODE void pokey_Process(void)
//currentValue = (currentValue >> 1); //currentValue = (currentValue >> 1);
if (currentValue > 127) {currentValue = 127;} // Clip if (currentValue > 127) {currentValue = 127;} // Clip
// 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++] = (u16)((currentValue<<8) | currentValue); tia_buffer[tiaBufIdx++] = (u16)((currentValue<<8) | currentValue);
tiaBufIdx &= (SNDLENGTH-1); 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;

View file

@ -34,31 +34,39 @@ 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,10 +75,10 @@ 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++;
@ -78,29 +86,39 @@ ITCM_CODE void prosystem_ExecuteFrame(const byte* input)
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...
// -------------------------------------------------------------------------------------------
// Note: this is not accurate. It should be 263 scanlines total but it doesn't work for all
// 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++)
{ {
prosystem_cycles = 0; prosystem_cycles = 0;
if (maria_scanline & 0x10) if(maria_scanline == 21) // Maria can start to do her thing... We've had 20 VBLANK scanlines
{ {
memory_ram[MSTAT] = 0; memory_ram[MSTAT] = 0; // Out of the vertical blank
framePtr = (word*)(maria_surface); framePtr = (word * )(maria_surface);
sally_Execute(HBLANK_BEFORE_DMA); sally_Execute(CYCLES_BEFORE_DMA);
maria_RenderScanlineTOP( ); maria_RenderScanlineTOP();
// Cycle Stealing happens here... with a fudge adjustment... // Cycle Stealing happens here...
if (maria_cycles > 0) maria_cycles += (int)myCartInfo.dma_adjust; prosystem_cycles += ((maria_cycles + 3) >> 2) << 2; // Always a multiple of 4
prosystem_cycles += maria_cycles; if(riot_and_wsync & 2) riot_UpdateTimer(maria_cycles >> 2);
if(riot_and_wsync&2) riot_UpdateTimer( maria_cycles >> 2 );
} }
else else
{ {
sally_Execute(HBLANK_BEFORE_DMA); sally_Execute(CYCLES_BEFORE_DMA);
} }
sally_Execute(CYCLES_PER_SCANLINE); sally_Execute(CYCLES_PER_SCANLINE);
@ -109,36 +127,43 @@ ITCM_CODE void prosystem_ExecuteFrame(const byte* input)
{ {
pokey_Process(); pokey_Process();
pokey_Scanline(); 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) }
else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// Now handle the Main display area... // Now handle the Main display area...
// ------------------------------------------------------------ // ------------------------------------------------------------
for (; maria_scanline < 258; maria_scanline++) for(; maria_scanline < 263; maria_scanline++)
{ {
prosystem_cycles = 0; prosystem_cycles = 0;
if (maria_scanline == 25) if(maria_scanline >= 30)
{
// --------------------------------------------------------------------------
// We can start to render the scanlines if we are not skipping this frame.
// The DS/DSi only has 192 vertical pixels and we can perform some hardware
// 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
{ {
// -----------------------------------------------------------------------------------
// At line 25 we can start to render the scanlines if we are not skipping this frame.
// -----------------------------------------------------------------------------------
bRenderFrame = gTotalAtariFrames & frameSkipMask; bRenderFrame = gTotalAtariFrames & frameSkipMask;
} }
else if (maria_scanline == 247)
{
bRenderFrame = 0; // At line 247 we can stop rendering frames...
} }
sally_Execute(HBLANK_BEFORE_DMA); sally_Execute(CYCLES_BEFORE_DMA);
maria_RenderScanline( ); maria_RenderScanline();
// Cycle Stealing happens here... with a fudge adjustment... // Cycle Stealing happens here...
if (maria_cycles > 0) maria_cycles += (int)myCartInfo.dma_adjust; prosystem_cycles += ((maria_cycles + 3) >> 2) << 2; // Always a multiple of 4
prosystem_cycles += maria_cycles; if(riot_and_wsync & 2) riot_UpdateTimer(maria_cycles >> 2);
if(riot_and_wsync&2) riot_UpdateTimer( maria_cycles >> 2 );
sally_Execute(CYCLES_PER_SCANLINE); sally_Execute(CYCLES_PER_SCANLINE);
@ -146,38 +171,20 @@ ITCM_CODE void prosystem_ExecuteFrame(const byte* input)
{ {
pokey_Process(); pokey_Process();
pokey_Scanline(); 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)
} }
else tia_Process(); // If all we have to deal with is the TIA, we can do so at 31KHz
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(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)
} }
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// 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

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

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

@ -31,8 +31,7 @@ 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")));
@ -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:
@ -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:

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]));
} }
// 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]); tia_buffer[tiaBufIdx++] = (samp[1] << 8) | (samp[0]);
tiaBufIdx &= (SNDLENGTH-1); 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

@ -25,6 +25,7 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // 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>
@ -59,8 +60,12 @@ int main(int argc, char **argv)
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)
{ {

BIN
logo.bmp

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

After

Width:  |  Height:  |  Size: 630 B