Cleanup for the 1.0 release.

This commit is contained in:
Dave Bernazzani 2025-04-13 09:26:08 -04:00
parent a0aaae083c
commit bdde6bed8c
18 changed files with 380 additions and 310 deletions

View File

@ -15,7 +15,7 @@ include $(DEVKITARM)/ds_rules
export TARGET := SpeccySE export TARGET := SpeccySE
export TOPDIR := $(CURDIR) export TOPDIR := $(CURDIR)
export VERSION := 0.9a export VERSION := 1.0
ICON := -b $(CURDIR)/logo.bmp "SpeccySE $(VERSION);wavemotion-dave;https://github.com/wavemotion-dave/SpeccySE" ICON := -b $(CURDIR)/logo.bmp "SpeccySE $(VERSION);wavemotion-dave;https://github.com/wavemotion-dave/SpeccySE"

View File

@ -36,7 +36,7 @@ extern void mmInstall( int fifo_channel );
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void VblankHandler(void) { void VblankHandler(void) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
Wifi_Update(); //Wifi_Update();
} }
@ -71,7 +71,7 @@ int main() {
SetYtrigger(80); SetYtrigger(80);
installWifiFIFO(); //installWifiFIFO();
installSoundFIFO(); installSoundFIFO();
installSystemFIFO(); installSystemFIFO();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -52,10 +52,9 @@ const u32 crc32_table[256] = {
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, // 248 [0xF8 .. 0xFF] 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, // 248 [0xF8 .. 0xFF]
}; };
// ----------------------------------------------------------------------------------------------------------------- // --------------------------------------------------
// For disk and data pack games which may write back data, we don't want to base the CRC on the actual contents of // Compute the CRC of a memory buffer of any size...
// the media... so instead we'll just hash the filename as a CRC which is good enough to identify it in the future. // --------------------------------------------------
// -----------------------------------------------------------------------------------------------------------------
u32 getCRC32(u8 *buf, u32 size) u32 getCRC32(u8 *buf, u32 size)
{ {
u32 crc = 0xFFFFFFFF; u32 crc = 0xFFFFFFFF;

View File

@ -3,7 +3,7 @@
// //
// Copying and distribution of this emulator, its source code and associated // Copying and distribution of this emulator, its 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
// royalty provided this copyright notice is used and wavemotion-dave and Marat // royalty provided this copyright notice is used and wavemotion-dave and Marat
// Fayzullin (Z80 core) are thanked profusely. // Fayzullin (Z80 core) are thanked profusely.
// //
// The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md // The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md
@ -35,6 +35,12 @@
#include "printf.h" #include "printf.h"
// -----------------------------------------------------------------
// Most handy for development of the emulator is a set of 16 R/W
// registers and a couple of index vars... we show this when
// global settings is set to show the 'Debugger'. It's amazing
// how handy these general registers are for emulation development.
// -----------------------------------------------------------------
u32 debug[0x10]={0}; u32 debug[0x10]={0};
u32 DX = 0; u32 DX = 0;
u32 DY = 0; u32 DY = 0;
@ -44,19 +50,26 @@ u32 DY = 0;
// 0x0000-0x3FFF Spectrum BIOS. Either 48.rom or 128.rom (bank 0 or 1) // 0x0000-0x3FFF Spectrum BIOS. Either 48.rom or 128.rom (bank 0 or 1)
// 0x4000-0xFFFF Spectrum 48K of RAM / Memory // 0x4000-0xFFFF Spectrum 48K of RAM / Memory
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
u8 RAM_Memory[0x10000] ALIGN(32) = {0}; // The Z80 Memory is 64K u8 RAM_Memory[0x10000] ALIGN(32) = {0}; // The Z80 Memory is 64K
u8 RAM_Memory128[0x20000] ALIGN(32) = {0}; // The Z80 Memory is 64K but we expand this for a 128K model u8 RAM_Memory128[0x20000] ALIGN(32) = {0}; // The Z80 Memory is 64K but we expand this for a 128K model
u8 SpectrumBios[0x4000] = {0}; // We keep the 16k ZX Spectrum 48K BIOS around u8 SpectrumBios[0x4000] = {0}; // We keep the 16k ZX Spectrum 48K BIOS around
u8 SpectrumBios128[0x8000] = {0}; // We keep the 32k ZX Spectrum 128K BIOS around u8 SpectrumBios128[0x8000] = {0}; // We keep the 32k ZX Spectrum 128K BIOS around
u8 ROM_Memory[MAX_TAPE_SIZE]; // This is where we keep the raw untouched file as read from the SD card (.TAP, .TZX, .Z80, etc) u8 ROM_Memory[MAX_TAPE_SIZE]; // This is where we keep the raw untouched file as read from the SD card (.TAP, .TZX, .Z80, etc)
// ----------------------------------------------------------------------------
// We track the most recent directory and file loaded... both the initial one
// (for the CRC32) and subsequent additional tape loads (Side 2, Side B, etc)
// ----------------------------------------------------------------------------
static char cmd_line_file[256]; static char cmd_line_file[256];
char initial_file[MAX_FILENAME_LEN] = ""; char initial_file[MAX_FILENAME_LEN] = "";
char initial_path[MAX_FILENAME_LEN] = ""; char initial_path[MAX_FILENAME_LEN] = "";
char last_path[MAX_FILENAME_LEN] = ""; char last_path[MAX_FILENAME_LEN] = "";
char last_file[MAX_FILENAME_LEN] = ""; char last_file[MAX_FILENAME_LEN] = "";
// --------------------------------------------------
// A few housekeeping vars to help with emulation...
// --------------------------------------------------
u8 last_speccy_mode = 99; u8 last_speccy_mode = 99;
u8 bFirstTime = 3; u8 bFirstTime = 3;
u8 bottom_screen = 0; u8 bottom_screen = 0;
@ -77,9 +90,9 @@ u8 bSpeccyBiosFound = false;
u8 soundEmuPause __attribute__((section(".dtcm"))) = 1; // Set to 1 to pause (mute) sound, 0 is sound unmuted (sound channels active) u8 soundEmuPause __attribute__((section(".dtcm"))) = 1; // Set to 1 to pause (mute) sound, 0 is sound unmuted (sound channels active)
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
// This set of critical vars is what determines the machine type - // This set of critical vars is what determines the machine type -
// ----------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------
u8 speccy_mode __attribute__((section(".dtcm"))) = 0; // See defines for the various modes... u8 speccy_mode __attribute__((section(".dtcm"))) = 0; // See defines for the various modes...
u8 kbd_key __attribute__((section(".dtcm"))) = 0; // 0 if no key pressed, othewise the ASCII key (e.g. 'A', 'B', '3', etc) u8 kbd_key __attribute__((section(".dtcm"))) = 0; // 0 if no key pressed, othewise the ASCII key (e.g. 'A', 'B', '3', etc)
u16 nds_key __attribute__((section(".dtcm"))) = 0; // 0 if no key pressed, othewise the NDS keys from keysCurrent() or similar u16 nds_key __attribute__((section(".dtcm"))) = 0; // 0 if no key pressed, othewise the NDS keys from keysCurrent() or similar
u8 last_mapped_key __attribute__((section(".dtcm"))) = 0; // The last mapped key which has been pressed - used for key click feedback u8 last_mapped_key __attribute__((section(".dtcm"))) = 0; // The last mapped key which has been pressed - used for key click feedback
@ -95,16 +108,18 @@ u8 key_debounce = 0; // A bit of key debounce
// The DS/DSi has 12 keys that can be mapped // The DS/DSi has 12 keys that can be mapped
u16 NDS_keyMap[12] __attribute__((section(".dtcm"))) = {KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_A, KEY_B, KEY_X, KEY_Y, KEY_R, KEY_L, KEY_START, KEY_SELECT}; u16 NDS_keyMap[12] __attribute__((section(".dtcm"))) = {KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_A, KEY_B, KEY_X, KEY_Y, KEY_R, KEY_L, KEY_START, KEY_SELECT};
// -------------------------------------------------------------------- // ----------------------------------------------------------------------
// The key map for the ZX Spectrum... mapped into the NDS controller // The key map for the ZX Spectrum... mapped into the NDS controller
// -------------------------------------------------------------------- // We allow mapping of the 5 joystick 'presses' (up, down, left, right
// and fire) along with all 40 of the possible ZX Spectrum keyboard keys.
// ----------------------------------------------------------------------
u16 keyCoresp[MAX_KEY_OPTIONS] __attribute__((section(".dtcm"))) = { u16 keyCoresp[MAX_KEY_OPTIONS] __attribute__((section(".dtcm"))) = {
JST_UP, //0 JST_UP, //0
JST_DOWN, JST_DOWN,
JST_LEFT, JST_LEFT,
JST_RIGHT, JST_RIGHT,
JST_FIRE, JST_FIRE,
META_KBD_A, //5 META_KBD_A, //5
META_KBD_B, META_KBD_B,
META_KBD_C, META_KBD_C,
@ -117,7 +132,7 @@ u16 keyCoresp[MAX_KEY_OPTIONS] __attribute__((section(".dtcm"))) = {
META_KBD_J, META_KBD_J,
META_KBD_K, //15 META_KBD_K, //15
META_KBD_L, META_KBD_L,
META_KBD_M, META_KBD_M,
META_KBD_N, META_KBD_N,
META_KBD_O, META_KBD_O,
META_KBD_P, //20 META_KBD_P, //20
@ -142,7 +157,7 @@ u16 keyCoresp[MAX_KEY_OPTIONS] __attribute__((section(".dtcm"))) = {
META_KBD_8, META_KBD_8,
META_KBD_9, META_KBD_9,
META_KBD_0, //40 META_KBD_0, //40
META_KBD_SHIFT, META_KBD_SHIFT,
META_KBD_SYMBOL, META_KBD_SYMBOL,
META_KBD_SPACE, META_KBD_SPACE,
@ -217,7 +232,7 @@ ITCM_CODE mm_word OurSoundMixer(mm_word len, mm_addr dest, mm_stream_formats for
} }
if (breather) {breather -= (len*2); if (breather < 0) breather = 0;} if (breather) {breather -= (len*2); if (breather < 0) breather = 0;}
} }
return len; return len;
} }
@ -232,20 +247,20 @@ s16 beeper_vol[4] __attribute__((section(".dtcm"))) = { 0x000, 0x200, 0x600, 0xA
u32 vol __attribute__((section(".dtcm"))) = 0; u32 vol __attribute__((section(".dtcm"))) = 0;
ITCM_CODE void processDirectAudio(void) ITCM_CODE void processDirectAudio(void)
{ {
if (zx_AY_enabled) if (zx_AY_enabled)
{ {
ay38910Mixer(2, mixbufAY, &myAY); ay38910Mixer(2, mixbufAY, &myAY);
} }
for (u8 i=0; i<2; i++) for (u8 i=0; i<2; i++)
{ {
// Smooth edges of beeper slightly... // Smooth edges of beeper slightly...
if (portFE & 0x10) {if (vol < 3) vol++;} if (portFE & 0x10) {if (vol < 3) vol++;}
else {if (vol) vol--;} else {if (vol) vol--;}
if (breather) {return;} if (breather) {return;}
s16 sample = mixbufAY[i]; s16 sample = mixbufAY[i];
if (beeper_vol[vol]) if (beeper_vol[vol])
{ {
sample += beeper_vol[vol] + (8 - (int)(rand() & 0xF)); // Sample plus a bit of white noise to break up aliasing sample += beeper_vol[vol] + (8 - (int)(rand() & 0xF)); // Sample plus a bit of white noise to break up aliasing
} }
@ -269,7 +284,7 @@ void setupStream(void)
mmLoadEffect(SFX_CLICKNOQUIT); mmLoadEffect(SFX_CLICKNOQUIT);
mmLoadEffect(SFX_KEYCLICK); mmLoadEffect(SFX_KEYCLICK);
mmLoadEffect(SFX_MUS_INTRO); mmLoadEffect(SFX_MUS_INTRO);
//---------------------------------------------------------------- //----------------------------------------------------------------
// open stream // open stream
//---------------------------------------------------------------- //----------------------------------------------------------------
@ -300,7 +315,7 @@ void sound_chip_reset()
memset(mixer, 0x00, sizeof(mixer)); memset(mixer, 0x00, sizeof(mixer));
mixer_read=0; mixer_read=0;
mixer_write=0; mixer_write=0;
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// The AY sound chip is for the ZX Spectrum 128K // The AY sound chip is for the ZX Spectrum 128K
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -308,7 +323,7 @@ void sound_chip_reset()
ay38910IndexW(0x07, &myAY); // Register 7 is ENABLE ay38910IndexW(0x07, &myAY); // Register 7 is ENABLE
ay38910DataW(0x3F, &myAY); // All OFF (negative logic) ay38910DataW(0x3F, &myAY); // All OFF (negative logic)
ay38910Mixer(4, mixbufAY, &myAY);// Do an initial mix conversion to clear the output ay38910Mixer(4, mixbufAY, &myAY);// Do an initial mix conversion to clear the output
memset(mixbufAY, 0x00, sizeof(mixbufAY)); memset(mixbufAY, 0x00, sizeof(mixbufAY));
} }
@ -317,8 +332,8 @@ void sound_chip_reset()
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
void dsInstallSoundEmuFIFO(void) void dsInstallSoundEmuFIFO(void)
{ {
SoundPause(); // Pause any sound output SoundPause(); // Pause any sound output
sound_chip_reset(); // Reset the SN, AY and SCC chips sound_chip_reset(); // Reset the SN, AY and SCC chips
setupStream(); // Setup maxmod stream... setupStream(); // Setup maxmod stream...
bStartSoundEngine = 5; // Volume will 'unpause' after 5 frames in the main loop. bStartSoundEngine = 5; // Volume will 'unpause' after 5 frames in the main loop.
} }
@ -332,7 +347,7 @@ void dsInstallSoundEmuFIFO(void)
// the RESET button on the touch-screen... // the RESET button on the touch-screen...
// -------------------------------------------------------------- // --------------------------------------------------------------
void ResetSpectrum(void) void ResetSpectrum(void)
{ {
JoyState = 0x00000000; // Nothing pressed to start JoyState = 0x00000000; // Nothing pressed to start
sound_chip_reset(); // Reset the AY chip sound_chip_reset(); // Reset the AY chip
@ -354,7 +369,7 @@ void ResetSpectrum(void)
TIMER2_CR=TIMER_ENABLE | TIMER_DIV_1024; TIMER2_CR=TIMER_ENABLE | TIMER_DIV_1024;
timingFrames = 0; timingFrames = 0;
emuFps=0; emuFps=0;
bFirstTime = 2; bFirstTime = 2;
bStartIn = 0; bStartIn = 0;
bottom_screen = 0; bottom_screen = 0;
@ -385,7 +400,7 @@ int getMemFree() { // returns the amount of free memory in bytes
void ShowDebugZ80(void) void ShowDebugZ80(void)
{ {
u8 idx=2; u8 idx=2;
if (myGlobalConfig.debugger == 3) if (myGlobalConfig.debugger == 3)
{ {
sprintf(tmp, "PC %04X SP %04X", CPU.PC.W, CPU.SP.W); sprintf(tmp, "PC %04X SP %04X", CPU.PC.W, CPU.SP.W);
@ -408,7 +423,7 @@ void ShowDebugZ80(void)
DSPrint(0,idx++,7, tmp); DSPrint(0,idx++,7, tmp);
idx++; idx++;
sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[0], myAY.ayRegs[1], myAY.ayRegs[2], myAY.ayRegs[3]); sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[0], myAY.ayRegs[1], myAY.ayRegs[2], myAY.ayRegs[3]);
DSPrint(0,idx++,7, tmp); DSPrint(0,idx++,7, tmp);
sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[4], myAY.ayRegs[5], myAY.ayRegs[6], myAY.ayRegs[7]); sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[4], myAY.ayRegs[5], myAY.ayRegs[6], myAY.ayRegs[7]);
@ -417,13 +432,13 @@ void ShowDebugZ80(void)
DSPrint(0,idx++,7, tmp); DSPrint(0,idx++,7, tmp);
sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[12], myAY.ayRegs[13], myAY.ayRegs[14], myAY.ayRegs[15]); sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[12], myAY.ayRegs[13], myAY.ayRegs[14], myAY.ayRegs[15]);
DSPrint(0,idx++,7, tmp); DSPrint(0,idx++,7, tmp);
idx++; idx++;
sprintf(tmp, "LOAD: %-9s", loader_type); DSPrint(0,idx++, 7, tmp); sprintf(tmp, "LOAD: %-9s", loader_type); DSPrint(0,idx++, 7, tmp);
sprintf(tmp, "MEM Used %dK", getMemUsed()/1024); DSPrint(0,idx++,7, tmp); sprintf(tmp, "MEM Used %dK", getMemUsed()/1024); DSPrint(0,idx++,7, tmp);
sprintf(tmp, "MEM Free %dK", getMemFree()/1024); DSPrint(0,idx++,7, tmp); sprintf(tmp, "MEM Free %dK", getMemFree()/1024); DSPrint(0,idx++,7, tmp);
// CPU Disassembly! // CPU Disassembly!
// Put out the debug registers... // Put out the debug registers...
@ -440,7 +455,7 @@ void ShowDebugZ80(void)
idx = 1; idx = 1;
for (u8 i=0; i<4; i++) for (u8 i=0; i<4; i++)
{ {
sprintf(tmp, "D%d %-7ld %04lX D%d %-7ld %04lX", i, (s32)debug[i], (debug[i] < 0xFFFF ? debug[i]:0xFFFF), 4+i, (s32)debug[4+i], (debug[4+i] < 0xFFFF ? debug[4+i]:0xFFFF)); sprintf(tmp, "D%d %-7ld %04lX D%d %-7ld %04lX", i, (s32)debug[i], (debug[i] < 0xFFFF ? debug[i]:0xFFFF), 4+i, (s32)debug[4+i], (debug[4+i] < 0xFFFF ? debug[4+i]:0xFFFF));
DSPrint(0,idx++,0, tmp); DSPrint(0,idx++,0, tmp);
} }
} }
@ -483,12 +498,17 @@ void CassetteInsert(char *filename)
fclose(inFile); fclose(inFile);
tape_parse_blocks(last_file_size); tape_parse_blocks(last_file_size);
tape_reset(); tape_reset();
strcpy(last_file, filename); strcpy(last_file, filename);
getcwd(last_path, MAX_FILENAME_LEN); getcwd(last_path, MAX_FILENAME_LEN);
} }
} }
// ----------------------------------------------------------------------
// The Cassette Menu can be called up directly from the keyboard graphic
// and allows the user to rewind the tape, swap in a new tape, etc.
// ----------------------------------------------------------------------
#define MENU_ACTION_END 255 // Always the last sentinal value #define MENU_ACTION_END 255 // Always the last sentinal value
#define MENU_ACTION_EXIT 0 // Exit the menu #define MENU_ACTION_EXIT 0 // Exit the menu
#define MENU_ACTION_PLAY 1 // Play Cassette #define MENU_ACTION_PLAY 1 // Play Cassette
@ -500,13 +520,13 @@ void CassetteInsert(char *filename)
#define MENU_ACTION_RESET 98 // Reset the machine #define MENU_ACTION_RESET 98 // Reset the machine
#define MENU_ACTION_SKIP 99 // Skip this MENU choice #define MENU_ACTION_SKIP 99 // Skip this MENU choice
typedef struct typedef struct
{ {
char *menu_string; char *menu_string;
u8 menu_action; u8 menu_action;
} MenuItem_t; } MenuItem_t;
typedef struct typedef struct
{ {
char *title; char *title;
u8 start_row; u8 start_row;
@ -517,13 +537,13 @@ CassetteDiskMenu_t generic_cassette_menu =
{ {
"CASSETTE MENU", 3, "CASSETTE MENU", 3,
{ {
{" PLAY CASSETTE ", MENU_ACTION_PLAY}, {" PLAY CASSETTE ", MENU_ACTION_PLAY},
{" STOP CASSETTE ", MENU_ACTION_STOP}, {" STOP CASSETTE ", MENU_ACTION_STOP},
{" SWAP CASSETTE ", MENU_ACTION_SWAP}, {" SWAP CASSETTE ", MENU_ACTION_SWAP},
{" REWIND CASSETTE ", MENU_ACTION_REWIND}, {" REWIND CASSETTE ", MENU_ACTION_REWIND},
{" POSITION CASSETTE", MENU_ACTION_POSITION}, {" POSITION CASSETTE ", MENU_ACTION_POSITION},
{" EXIT MENU ", MENU_ACTION_EXIT}, {" EXIT MENU ", MENU_ACTION_EXIT},
{" NULL ", MENU_ACTION_END}, {" NULL ", MENU_ACTION_END},
}, },
}; };
@ -537,30 +557,30 @@ u8 cassette_menu_items = 0;
void CassetteMenuShow(bool bClearScreen, u8 sel) void CassetteMenuShow(bool bClearScreen, u8 sel)
{ {
cassette_menu_items = 0; cassette_menu_items = 0;
if (bClearScreen) if (bClearScreen)
{ {
// ------------------------------------- // -------------------------------------
// Put up the Cassette menu background // Put up the Cassette menu background
// ------------------------------------- // -------------------------------------
BottomScreenCassette(); BottomScreenCassette();
} }
// --------------------------------------------------- // ---------------------------------------------------
// Pick the right context menu based on the machine // Pick the right context menu based on the machine
// --------------------------------------------------- // ---------------------------------------------------
menu = &generic_cassette_menu; menu = &generic_cassette_menu;
// Display the menu title // Display the menu title
DSPrint(16-(strlen(menu->title)/2), menu->start_row, 6, menu->title); DSPrint(15-(strlen(menu->title)/2), menu->start_row, 6, menu->title);
// And display all of the menu items // And display all of the menu items
while (menu->menulist[cassette_menu_items].menu_action != MENU_ACTION_END) while (menu->menulist[cassette_menu_items].menu_action != MENU_ACTION_END)
{ {
DSPrint(16-(strlen(menu->menulist[cassette_menu_items].menu_string)/2), menu->start_row+2+cassette_menu_items, (cassette_menu_items == sel) ? 7:6, menu->menulist[cassette_menu_items].menu_string); DSPrint(16-(strlen(menu->menulist[cassette_menu_items].menu_string)/2), menu->start_row+2+cassette_menu_items, (cassette_menu_items == sel) ? 7:6, menu->menulist[cassette_menu_items].menu_string);
cassette_menu_items++; cassette_menu_items++;
} }
// ---------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------
// And near the bottom, display the file/rom/disk/cassette that is currently loaded into memory. // And near the bottom, display the file/rom/disk/cassette that is currently loaded into memory.
// ---------------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------------
@ -613,12 +633,12 @@ void CassetteMenu(void)
case MENU_ACTION_EXIT: case MENU_ACTION_EXIT:
bExitMenu = true; bExitMenu = true;
break; break;
case MENU_ACTION_PLAY: case MENU_ACTION_PLAY:
tape_play(); tape_play();
bExitMenu = true; bExitMenu = true;
break; break;
case MENU_ACTION_STOP: case MENU_ACTION_STOP:
tape_stop(); tape_stop();
bExitMenu = true; bExitMenu = true;
@ -673,7 +693,9 @@ void CassetteMenu(void)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Show the Mini Menu - highlight the selected row. // Show the Mini Menu - highlight the selected row. This can be called
// up directly from the ZX Keyboard Graphic - allows the user to quit
// the current game, set high scores, save/load game state, etc.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
u8 mini_menu_items = 0; u8 mini_menu_items = 0;
void MiniMenuShow(bool bClearScreen, u8 sel) void MiniMenuShow(bool bClearScreen, u8 sel)
@ -764,6 +786,12 @@ u8 MiniMenu(void)
} }
// -------------------------------------------------------------------------
// Keyboard handler - mapping DS touch screen virtual keys to keyboard keys
// that we can feed into the key processing handler in spectrum.c when the
// IO port is read.
// -------------------------------------------------------------------------
u8 last_special_key = 0; u8 last_special_key = 0;
u8 last_special_key_dampen = 0; u8 last_special_key_dampen = 0;
u8 last_kbd_key = 0; u8 last_kbd_key = 0;
@ -828,12 +856,15 @@ u8 handle_spectrum_keyboard_press(u16 iTx, u16 iTy) // ZX Spectrum keyboard
else if ((iTx >= 54) && (iTx < 202)) kbd_key = ' '; else if ((iTx >= 54) && (iTx < 202)) kbd_key = ' ';
else if ((iTx >= 202) && (iTx < 255)) return MENU_CHOICE_MENU; else if ((iTx >= 202) && (iTx < 255)) return MENU_CHOICE_MENU;
} }
DisplayStatusLine(false); DisplayStatusLine(false);
return MENU_CHOICE_NONE; return MENU_CHOICE_NONE;
} }
// -----------------------------------------------------------------------------------
// Special version of the debugger overlay... handling just a small subset of keys...
// -----------------------------------------------------------------------------------
u8 handle_debugger_overlay(u16 iTx, u16 iTy) u8 handle_debugger_overlay(u16 iTx, u16 iTy)
{ {
if ((iTy >= 165) && (iTy < 192)) // Bottom row is where the debugger keys are... if ((iTy >= 165) && (iTy < 192)) // Bottom row is where the debugger keys are...
@ -848,7 +879,7 @@ u8 handle_debugger_overlay(u16 iTx, u16 iTy)
if ((iTx >= 156) && (iTx < 187)) kbd_key = KBD_KEY_RET; if ((iTx >= 156) && (iTx < 187)) kbd_key = KBD_KEY_RET;
if ((iTx >= 187) && (iTx < 222)) return MENU_CHOICE_MENU; if ((iTx >= 187) && (iTx < 222)) return MENU_CHOICE_MENU;
else if ((iTx >= 222) && (iTx < 255)) return MENU_CHOICE_CASSETTE; else if ((iTx >= 222) && (iTx < 255)) return MENU_CHOICE_CASSETTE;
DisplayStatusLine(false); DisplayStatusLine(false);
} }
else {kbd_key = 0; last_kbd_key = 0;} else {kbd_key = 0; last_kbd_key = 0;}
@ -865,7 +896,7 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key)
// Ask for verification // Ask for verification
if (showMessage("DO YOU REALLY WANT TO", "RESET THE CURRENT GAME ?") == ID_SHM_YES) if (showMessage("DO YOU REALLY WANT TO", "RESET THE CURRENT GAME ?") == ID_SHM_YES)
{ {
ResetSpectrum(); ResetSpectrum();
} }
BottomScreenKeyboard(); BottomScreenKeyboard();
SoundUnPause(); SoundUnPause();
@ -910,14 +941,14 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key)
BottomScreenKeyboard(); BottomScreenKeyboard();
SoundUnPause(); SoundUnPause();
break; break;
case MENU_CHOICE_DEFINE_KEYS: case MENU_CHOICE_DEFINE_KEYS:
SoundPause(); SoundPause();
SpeccySEChangeKeymap(); SpeccySEChangeKeymap();
BottomScreenKeyboard(); BottomScreenKeyboard();
SoundUnPause(); SoundUnPause();
break; break;
case MENU_CHOICE_POKE_MEMORY: case MENU_CHOICE_POKE_MEMORY:
SoundPause(); SoundPause();
pok_select(); pok_select();
@ -936,7 +967,7 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key)
return 0; return 0;
} }
// Show tape blocks with filenames/descriptions... // Show tape blocks with filenames/descriptions...
u8 speccyTapePosition(void) u8 speccyTapePosition(void)
{ {
u8 sel = 0; u8 sel = 0;
@ -947,7 +978,7 @@ u8 speccyTapePosition(void)
DisplayFileNameCassette(); DisplayFileNameCassette();
DSPrint(1,1,0," TAPE POSITIONS "); DSPrint(1,1,0," TAPE POSITIONS ");
u8 max = tape_find_positions(); u8 max = tape_find_positions();
u8 screen_max = (max < 10 ? max:10); u8 screen_max = (max < 10 ? max:10);
u8 offset = 0; u8 offset = 0;
@ -956,7 +987,7 @@ u8 speccyTapePosition(void)
sprintf(tmp, "%03d %-26s", TapePositionTable[offset+i].block_id, TapePositionTable[offset+i].description); sprintf(tmp, "%03d %-26s", TapePositionTable[offset+i].block_id, TapePositionTable[offset+i].description);
DSPrint(1,3+i,(i==sel) ? 2:0,tmp); DSPrint(1,3+i,(i==sel) ? 2:0,tmp);
} }
while (1) while (1)
{ {
u16 keys = keysCurrent(); u16 keys = keysCurrent();
@ -975,7 +1006,7 @@ u8 speccyTapePosition(void)
} }
else else
{ {
if ((offset + screen_max) < max) if ((offset + screen_max) < max)
{ {
offset += 10; offset += 10;
screen_max = ((max-offset) < 10 ? (max-offset):10); screen_max = ((max-offset) < 10 ? (max-offset):10);
@ -1031,16 +1062,16 @@ u8 speccyTapePosition(void)
} }
} }
} }
while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0); while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0);
WAITVBL;WAITVBL; WAITVBL;WAITVBL;
return sel+offset; return sel+offset;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Chuckie-Style d-pad keeps moving in the last known direction for a few more // Chuckie-Style d-pad keeps moving in the last known direction for a few more
// frames to help make those hairpin turns up and off ladders much easier... // frames to help make those hairpin turns up and off ladders much easier...
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
u8 chuckie_key_up = 0; u8 chuckie_key_up = 0;
u8 chuckie_key_down = 0; u8 chuckie_key_down = 0;
@ -1060,13 +1091,13 @@ void SpeccySE_main(void)
// Setup the debug buffer for DSi use // Setup the debug buffer for DSi use
debug_init(); debug_init();
// Get the ZX Spectrum Emulator ready // Get the ZX Spectrum Emulator ready
spectrumInit(gpFic[ucGameAct].szName); spectrumInit(gpFic[ucGameAct].szName);
spectrumSetPalette(); spectrumSetPalette();
spectrumRun(); spectrumRun();
// Frame-to-frame timing... // Frame-to-frame timing...
TIMER1_CR = 0; TIMER1_CR = 0;
TIMER1_DATA=0; TIMER1_DATA=0;
@ -1081,9 +1112,9 @@ void SpeccySE_main(void)
// Force the sound engine to turn on when we start emulation // Force the sound engine to turn on when we start emulation
bStartSoundEngine = 10; bStartSoundEngine = 10;
bFirstTime = 2; bFirstTime = 2;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Stay in this loop running the Spectrum game until the user exits... // Stay in this loop running the Spectrum game until the user exits...
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@ -1124,7 +1155,7 @@ void SpeccySE_main(void)
} }
DisplayStatusLine(false); DisplayStatusLine(false);
emuActFrames = 0; emuActFrames = 0;
if (bStartIn) if (bStartIn)
{ {
if (--bStartIn == 0) if (--bStartIn == 0)
@ -1132,12 +1163,12 @@ void SpeccySE_main(void)
tape_play(); tape_play();
} }
} }
if (bFirstTime) if (bFirstTime)
{ {
if (--bFirstTime == 0) if (--bFirstTime == 0)
{ {
// Tape Loader - Put the LOAD "" into the keyboard buffer // Tape Loader - Put the LOAD "" into the keyboard buffer
if (speccy_mode < MODE_SNA) if (speccy_mode < MODE_SNA)
{ {
if (myConfig.autoLoad) if (myConfig.autoLoad)
@ -1178,7 +1209,7 @@ void SpeccySE_main(void)
while (TIMER2_DATA < 655*(timingFrames+1)) while (TIMER2_DATA < 655*(timingFrames+1))
{ {
if (myGlobalConfig.showFPS == 2) break; // If Full Speed, break out... if (myGlobalConfig.showFPS == 2) break; // If Full Speed, break out...
if (tape_is_playing()) if (tape_is_playing())
{ {
mixer_read = mixer_write = 0; mixer_read = mixer_write = 0;
bStartSoundEngine = 5; // Unpause sound after 5 frames bStartSoundEngine = 5; // Unpause sound after 5 frames
@ -1197,7 +1228,7 @@ void SpeccySE_main(void)
} }
// -------------------------------------------------------------- // --------------------------------------------------------------
// Hold the key press for a brief instant... To allow the // Hold the key press for a brief instant... To allow the
// emulated ZX Spectrum to 'see' the key briefly... Good enough. // emulated ZX Spectrum to 'see' the key briefly... Good enough.
// -------------------------------------------------------------- // --------------------------------------------------------------
if (key_debounce > 0) key_debounce--; if (key_debounce > 0) key_debounce--;
@ -1230,12 +1261,12 @@ void SpeccySE_main(void)
meta_key = handle_debugger_overlay(iTx, iTy); meta_key = handle_debugger_overlay(iTx, iTy);
} }
// ------------------------------------------------------------ // ------------------------------------------------------------
// Test the touchscreen for various full keyboard handlers... // Test the touchscreen for various full keyboard handlers...
// ------------------------------------------------------------ // ------------------------------------------------------------
else else
{ {
meta_key = handle_spectrum_keyboard_press(iTx, iTy); meta_key = handle_spectrum_keyboard_press(iTx, iTy);
} }
if (kbd_key != 0) if (kbd_key != 0)
{ {
@ -1277,9 +1308,9 @@ void SpeccySE_main(void)
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
ucDEUX = 0; ucDEUX = 0;
nds_key = keysCurrent(); // Get any current keys pressed on the NDS nds_key = keysCurrent(); // Get any current keys pressed on the NDS
// ----------------------------------------- // -----------------------------------------
// Check various key combinations first... // Check various key combinations first...
// ----------------------------------------- // -----------------------------------------
if ((nds_key & KEY_L) && (nds_key & KEY_R) && (nds_key & KEY_X)) if ((nds_key & KEY_L) && (nds_key & KEY_R) && (nds_key & KEY_X))
{ {
@ -1307,29 +1338,29 @@ void SpeccySE_main(void)
{ {
chuckie_key_up = 12; chuckie_key_up = 12;
chuckie_key_down = 0; chuckie_key_down = 0;
} }
if (nds_key & KEY_DOWN) if (nds_key & KEY_DOWN)
{ {
chuckie_key_down = 12; chuckie_key_down = 12;
chuckie_key_up = 0; chuckie_key_up = 0;
} }
if (nds_key & KEY_LEFT) if (nds_key & KEY_LEFT)
{ {
chuckie_key_left = 12; chuckie_key_left = 12;
chuckie_key_right = 0; chuckie_key_right = 0;
} }
if (nds_key & KEY_RIGHT) if (nds_key & KEY_RIGHT)
{ {
chuckie_key_right = 12; chuckie_key_right = 12;
chuckie_key_left = 0; chuckie_key_left = 0;
} }
if (chuckie_key_up) if (chuckie_key_up)
{ {
chuckie_key_up--; chuckie_key_up--;
nds_key |= KEY_UP; nds_key |= KEY_UP;
} }
if (chuckie_key_down) if (chuckie_key_down)
{ {
chuckie_key_down--; chuckie_key_down--;
@ -1348,7 +1379,7 @@ void SpeccySE_main(void)
nds_key |= KEY_RIGHT; nds_key |= KEY_RIGHT;
} }
} }
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
// There are 12 NDS buttons (D-Pad, XYAB, L/R and Start+Select) - we allow mapping of any of these. // There are 12 NDS buttons (D-Pad, XYAB, L/R and Start+Select) - we allow mapping of any of these.
// -------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------
@ -1385,7 +1416,7 @@ void SpeccySE_main(void)
if (chuckie_key_right) chuckie_key_right--; if (chuckie_key_right) chuckie_key_right--;
last_mapped_key = 0; last_mapped_key = 0;
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
// Finally, check if there are any buffered keys that need to go into the keyboard handling. // Finally, check if there are any buffered keys that need to go into the keyboard handling.
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
@ -1395,7 +1426,7 @@ void SpeccySE_main(void)
// Accumulate all bits above into the Joystick State var... // Accumulate all bits above into the Joystick State var...
// --------------------------------------------------------- // ---------------------------------------------------------
JoyState = ucDEUX; JoyState = ucDEUX;
// -------------------------------------------------- // --------------------------------------------------
// Handle Auto-Fire if enabled in configuration... // Handle Auto-Fire if enabled in configuration...
// -------------------------------------------------- // --------------------------------------------------
@ -1447,7 +1478,7 @@ void speccySEInit(void)
unsigned short dmaVal =*(bgGetMapPtr(bg0)+51*32); unsigned short dmaVal =*(bgGetMapPtr(bg0)+51*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1),32*24*2); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1),32*24*2);
// Put up the options screen // Put up the options screen
BottomScreenOptions(); BottomScreenOptions();
// Find the files // Find the files
@ -1457,7 +1488,7 @@ void speccySEInit(void)
void BottomScreenOptions(void) void BottomScreenOptions(void)
{ {
swiWaitForVBlank(); swiWaitForVBlank();
if (bottom_screen != 1) if (bottom_screen != 1)
{ {
// --------------------------------------------------- // ---------------------------------------------------
@ -1466,11 +1497,11 @@ void BottomScreenOptions(void)
bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0); bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0);
bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0); bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0);
bgSetPriority(bg0b,1);bgSetPriority(bg1b,0); bgSetPriority(bg0b,1);bgSetPriority(bg1b,0);
decompress(mainmenuTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(mainmenuTiles, bgGetGfxPtr(bg0b), LZ77Vram);
decompress(mainmenuMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); decompress(mainmenuMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
dmaCopy((void*) mainmenuPal,(void*) BG_PALETTE_SUB,256*2); dmaCopy((void*) mainmenuPal,(void*) BG_PALETTE_SUB,256*2);
unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32); unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
} }
@ -1478,7 +1509,7 @@ void BottomScreenOptions(void)
{ {
for (u8 i=0; i<23; i++) DSPrint(0,i,0," "); for (u8 i=0; i<23; i++) DSPrint(0,i,0," ");
} }
bottom_screen = 1; bottom_screen = 1;
} }
@ -1488,7 +1519,7 @@ void BottomScreenOptions(void)
void BottomScreenKeyboard(void) void BottomScreenKeyboard(void)
{ {
swiWaitForVBlank(); swiWaitForVBlank();
if (myGlobalConfig.debugger == 3) // Full Z80 Debug overrides things... put up the debugger overlay if (myGlobalConfig.debugger == 3) // Full Z80 Debug overrides things... put up the debugger overlay
{ {
// Init bottom screen // Init bottom screen
@ -1505,10 +1536,10 @@ void BottomScreenKeyboard(void)
dmaCopy((void*) bgGetMapPtr(bg0b)+32*30*2,(void*) bgGetMapPtr(bg1b),32*24*2); dmaCopy((void*) bgGetMapPtr(bg0b)+32*30*2,(void*) bgGetMapPtr(bg1b),32*24*2);
dmaCopy((void*) speccy_kbdPal,(void*) BG_PALETTE_SUB,256*2); dmaCopy((void*) speccy_kbdPal,(void*) BG_PALETTE_SUB,256*2);
} }
unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32); unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
bottom_screen = 2; bottom_screen = 2;
show_tape_counter = 0; show_tape_counter = 0;
@ -1520,21 +1551,21 @@ void BottomScreenKeyboard(void)
void BottomScreenCassette(void) void BottomScreenCassette(void)
{ {
swiWaitForVBlank(); swiWaitForVBlank();
// --------------------------------------------------- // ---------------------------------------------------
// Put up the cassette screen background... // Put up the cassette screen background...
// --------------------------------------------------- // ---------------------------------------------------
bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0); bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0);
bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0); bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0);
bgSetPriority(bg0b,1);bgSetPriority(bg1b,0); bgSetPriority(bg0b,1);bgSetPriority(bg1b,0);
decompress(cassetteTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(cassetteTiles, bgGetGfxPtr(bg0b), LZ77Vram);
decompress(cassetteMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); decompress(cassetteMap, (void*) bgGetMapPtr(bg0b), LZ77Vram);
dmaCopy((void*) cassettePal,(void*) BG_PALETTE_SUB,256*2); dmaCopy((void*) cassettePal,(void*) BG_PALETTE_SUB,256*2);
unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32); unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32);
dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2);
bottom_screen = 3; bottom_screen = 3;
} }
@ -1544,16 +1575,16 @@ void BottomScreenCassette(void)
********************************************************************************/ ********************************************************************************/
void speccySEInitCPU(void) void speccySEInitCPU(void)
{ {
// ----------------------------------------- // -----------------------------------------
// Init Main Memory and VDP Video Memory // Init Main Memory for the Spectrum
// ----------------------------------------- // -----------------------------------------
memset(RAM_Memory, 0x00, sizeof(RAM_Memory)); memset(RAM_Memory, 0x00, sizeof(RAM_Memory));
memset(RAM_Memory128, 0x00, sizeof(RAM_Memory128)); memset(RAM_Memory128, 0x00, sizeof(RAM_Memory128));
// ----------------------------------------------- // -----------------------------------------------
// Init bottom screen do display the ZX Keyboard // Init bottom screen do display the ZX Keyboard
// ----------------------------------------------- // -----------------------------------------------
BottomScreenKeyboard(); BottomScreenKeyboard();
} }
// ------------------------------------------------------------- // -------------------------------------------------------------
@ -1561,8 +1592,8 @@ void speccySEInitCPU(void)
// ------------------------------------------------------------- // -------------------------------------------------------------
void irqVBlank(void) void irqVBlank(void)
{ {
// Manage time // Manage time
vusCptVBL++; vusCptVBL++;
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -1583,7 +1614,7 @@ void LoadBIOSFiles(void)
size = ReadFileCarefully("48.rom", SpectrumBios, 0x4000, 0); size = ReadFileCarefully("48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/roms/bios/48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/data/bios/48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/data/bios/48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("48k.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("48k.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/48k.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/roms/bios/48k.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/data/bios/48k.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/data/bios/48k.rom", SpectrumBios, 0x4000, 0);
@ -1591,35 +1622,35 @@ void LoadBIOSFiles(void)
if (!size) size = ReadFileCarefully("speccy.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("speccy.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/speccy.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/roms/bios/speccy.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/data/bios/speccy.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/data/bios/speccy.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("zxs48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("zxs48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/zxs48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/roms/bios/zxs48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/data/bios/zxs48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/data/bios/zxs48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("spec48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("spec48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/spec48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/roms/bios/spec48.rom", SpectrumBios, 0x4000, 0);
if (!size) size = ReadFileCarefully("/data/bios/spec48.rom", SpectrumBios, 0x4000, 0); if (!size) size = ReadFileCarefully("/data/bios/spec48.rom", SpectrumBios, 0x4000, 0);
if (size) bSpeccyBiosFound = true; else memset(SpectrumBios, 0xFF, 0x4000); if (size) bSpeccyBiosFound = true; else memset(SpectrumBios, 0xFF, 0x4000);
size = ReadFileCarefully("128.rom", SpectrumBios128, 0x8000, 0); size = ReadFileCarefully("128.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/128.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("/roms/bios/128.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("/data/bios/128.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("/data/bios/128.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("128k.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("128k.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/128k.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("/roms/bios/128k.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("/data/bios/128k.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("/data/bios/128k.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("zxs128.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("zxs128.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("/roms/bios/zxs128.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("/roms/bios/zxs128.rom", SpectrumBios128, 0x8000, 0);
if (!size) size = ReadFileCarefully("/data/bios/zxs128.rom", SpectrumBios128, 0x8000, 0); if (!size) size = ReadFileCarefully("/data/bios/zxs128.rom", SpectrumBios128, 0x8000, 0);
if (size) bSpeccyBiosFound = true; else memset(SpectrumBios128, 0xFF, 0x8000); if (size) bSpeccyBiosFound = true; else memset(SpectrumBios128, 0xFF, 0x8000);
} }
/********************************************************************************* /************************************************************************************
* Program entry point - check if an argument has been passed in probably from TWL++ * Program entry point - check if an argument has been passed in probably from TWL++
********************************************************************************/ ***********************************************************************************/
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
// Init sound // Init sound
@ -1648,11 +1679,11 @@ int main(int argc, char **argv)
irqEnable(IRQ_VBLANK); irqEnable(IRQ_VBLANK);
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Grab the BIOS before we try to switch any DIRECTORYories around... // Grab the BIOS before we try to switch any directories around...
// ----------------------------------------------------------------- // -----------------------------------------------------------------
useVRAM(); useVRAM();
LoadBIOSFiles(); LoadBIOSFiles();
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// And do an initial load of configuration... We'll match it up // And do an initial load of configuration... We'll match it up
// with the game that was selected later... // with the game that was selected later...
@ -1662,7 +1693,7 @@ int main(int argc, char **argv)
// Handle command line argument... mostly for TWL++ // Handle command line argument... mostly for TWL++
if (argc > 1) if (argc > 1)
{ {
// We want to start in the DIRECTORYory where the file is being launched... // We want to start in the directory where the file is being launched...
if (strchr(argv[1], '/') != NULL) if (strchr(argv[1], '/') != NULL)
{ {
static char path[128]; static char path[128];
@ -1682,7 +1713,7 @@ int main(int argc, char **argv)
else else
{ {
cmd_line_file[0]=0; // No file passed on command line... cmd_line_file[0]=0; // No file passed on command line...
if (myGlobalConfig.lastDir && (strlen(myGlobalConfig.szLastPath) > 2)) if (myGlobalConfig.lastDir && (strlen(myGlobalConfig.szLastPath) > 2))
{ {
chdir(myGlobalConfig.szLastPath); // Try to start back where we last were... chdir(myGlobalConfig.szLastPath); // Try to start back where we last were...
@ -1699,7 +1730,7 @@ int main(int argc, char **argv)
} }
SoundPause(); SoundPause();
srand(time(NULL)); srand(time(NULL));
// ------------------------------------------------------------ // ------------------------------------------------------------
@ -1749,7 +1780,7 @@ int main(int argc, char **argv)
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// The code below is a handy set of debug tools that allows us to // The code below is a handy set of debug tools that allows us to
// write printf() like strings out to a file. Basically we accumulate // write printf() like strings out to a file. Basically we accumulate
// the strings into a large RAM buffer and then when the L+R shoulder // the strings into a large RAM buffer and then when the L+R shoulder
// buttons are pressed and held, we will snapshot out the debug.log file. // buttons are pressed and held, we will snapshot out the debug.log file.
@ -1767,7 +1798,7 @@ void debug_init()
{ {
if (!debug_buffer) if (!debug_buffer)
{ {
if (isDSiMode()) if (isDSiMode())
{ {
MAX_DEBUG_BUF_SIZE = (1024*1024*2); // 2MB!! MAX_DEBUG_BUF_SIZE = (1024*1024*2); // 2MB!!
debug_buffer = malloc(MAX_DEBUG_BUF_SIZE); debug_buffer = malloc(MAX_DEBUG_BUF_SIZE);

View File

@ -3,7 +3,7 @@
// //
// Copying and distribution of this emulator, its source code and associated // Copying and distribution of this emulator, its 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
// royalty provided this copyright notice is used and wavemotion-dave and Marat // royalty provided this copyright notice is used and wavemotion-dave and Marat
// Fayzullin (ColEM core) are thanked profusely. // Fayzullin (ColEM core) are thanked profusely.
// //
// The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md // The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md
@ -27,7 +27,7 @@ extern u32 DX, DY;
#define MENU_CHOICE_DEFINE_KEYS 0x06 #define MENU_CHOICE_DEFINE_KEYS 0x06
#define MENU_CHOICE_POKE_MEMORY 0x07 #define MENU_CHOICE_POKE_MEMORY 0x07
#define MENU_CHOICE_CASSETTE 0x08 #define MENU_CHOICE_CASSETTE 0x08
#define MENU_CHOICE_MENU 0xFF // Special brings up a menu of choices #define MENU_CHOICE_MENU 0xFF // Special brings up a mini-menu of choices
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Joystick UP, RIGHT, LEFT, DOWN and the FIRE button for the Kempston Joystick. // Joystick UP, RIGHT, LEFT, DOWN and the FIRE button for the Kempston Joystick.
@ -110,7 +110,7 @@ extern u32 DX, DY;
#define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); #define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank();
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// This massive patch table consumes 256K (64 x 4 byte function pointers) // This massive patch table consumes 256K (64 x 4 byte function pointers)
// to allow us faster access to patch routines for tape edge detection. // to allow us faster access to patch routines for tape edge detection.
// We put it in LCD VRAM as this is slightly faster access on the DS/DSi. // We put it in LCD VRAM as this is slightly faster access on the DS/DSi.
// 99% of this massive array will be zeroes but we don't have another use // 99% of this massive array will be zeroes but we don't have another use
@ -129,8 +129,8 @@ extern char initial_file[];
extern char initial_path[]; extern char initial_path[];
extern u16 nds_key; extern u16 nds_key;
extern u8 kbd_key; extern u8 kbd_key;
extern u16 vusCptVBL; // Video Management extern u16 vusCptVBL;
extern u16 *pVidFlipBuf; // Video flipping buffer extern u16 *pVidFlipBuf;
extern u16 keyCoresp[MAX_KEY_OPTIONS]; extern u16 keyCoresp[MAX_KEY_OPTIONS];
extern u16 NDS_keyMap[]; extern u16 NDS_keyMap[];
extern u8 soundEmuPause; extern u8 soundEmuPause;

View File

@ -291,7 +291,7 @@ void dsDisplayFiles(u16 NoDebGame, u8 ucSel)
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Standard qsort routine for the games - we sort all DIRECTORYory // Standard qsort routine for the games - we sort all directory
// listings first and then a case-insenstive sort of all games. // listings first and then a case-insenstive sort of all games.
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
int Filescmp (const void *c1, const void *c2) int Filescmp (const void *c1, const void *c2)
@ -1652,7 +1652,7 @@ void spectrumRun(void)
BottomScreenKeyboard(); // Show the game-related screen with keypad / keyboard BottomScreenKeyboard(); // Show the game-related screen with keypad / keyboard
} }
u8 ZX_Spectrum_palette[16*3] = { u8 ZX_Spectrum_palette[16*3] = {
0x00,0x00,0x00, // Black 0x00,0x00,0x00, // Black
0x00,0x00,0xD8, // Blue 0x00,0x00,0xD8, // Blue
0xD8,0x00,0x00, // Red 0xD8,0x00,0x00, // Red
@ -1718,9 +1718,9 @@ u8 loadgame(const char *filename)
// Save the initial filename and file - we need it for save/restore of state // Save the initial filename and file - we need it for save/restore of state
strcpy(initial_file, filename); strcpy(initial_file, filename);
getcwd(initial_path, MAX_FILENAME_LEN); getcwd(initial_path, MAX_FILENAME_LEN);
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// See if we are loading a file from a directory different than our // See if we are loading a file from a directory different than our
// last saved directory... if so, we save this new directory as default. // last saved directory... if so, we save this new directory as default.
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
if (myGlobalConfig.lastDir) if (myGlobalConfig.lastDir)
@ -1730,7 +1730,7 @@ u8 loadgame(const char *filename)
SaveConfig(FALSE); SaveConfig(FALSE);
} }
} }
// Get file size the 'fast' way - use fstat() instead of fseek() or ftell() // Get file size the 'fast' way - use fstat() instead of fseek() or ftell()
struct stat stbuf; struct stat stbuf;
(void)fstat(fileno(handle), &stbuf); (void)fstat(fileno(handle), &stbuf);

View File

@ -3,7 +3,7 @@
// //
// Copying and distribution of this emulator, its source code and associated // Copying and distribution of this emulator, its 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
// royalty provided this copyright notice is used and wavemotion-dave and Marat // royalty provided this copyright notice is used and wavemotion-dave and Marat
// Fayzullin (ColEM core) are thanked profusely. // Fayzullin (ColEM core) are thanked profusely.
// //
// The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md // The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md
@ -17,18 +17,18 @@
#define MAX_FILES 2048 #define MAX_FILES 2048
#define MAX_FILENAME_LEN 160 #define MAX_FILENAME_LEN 160
#define MAX_TAPE_SIZE (640*1024) // 640K is big enough for any .TAP/.TZX or Snapshot #define MAX_TAPE_SIZE (640*1024) // 640K is big enough for any .TAP/.TZX or Snapshot
#define MAX_CONFIGS 1000 #define MAX_CONFIGS 1000
#define CONFIG_VERSION 0x0004 #define CONFIG_VERSION 0x0004
#define SPECCY_FILE 0x01 #define SPECCY_FILE 0x01
#define DIRECTORY 0x02 #define DIRECTORY 0x02
#define ID_SHM_CANCEL 0x00 #define ID_SHM_CANCEL 0x00
#define ID_SHM_YES 0x01 #define ID_SHM_YES 0x01
#define ID_SHM_NO 0x02 #define ID_SHM_NO 0x02
#define DPAD_NORMAL 0 #define DPAD_NORMAL 0
#define DPAD_DIAGONALS 1 #define DPAD_DIAGONALS 1
#define DPAD_CHUCKIE 2 #define DPAD_CHUCKIE 2
@ -137,7 +137,7 @@ extern u8 RAM_Memory128[0x20000];
extern u8 *MemoryMap[4]; extern u8 *MemoryMap[4];
extern AY38910 myAY; extern AY38910 myAY;
extern FISpeccy gpFic[MAX_FILES]; extern FISpeccy gpFic[MAX_FILES];
extern int uNbRoms; extern int uNbRoms;
extern int ucGameAct; extern int ucGameAct;
extern int ucGameChoice; extern int ucGameChoice;

View File

@ -591,20 +591,8 @@ void ExecOneInstruction(void)
{ {
register byte I; register byte I;
register pair J; register pair J;
u8 render = zx_ScreenRendering;
u32 RunToCycles = CPU.TStates+1; u32 RunToCycles = CPU.TStates+1;
// ----------------------------------------------------------------------------------------
// If we are in contended memory - add penalty. This is not cycle accurate but we want to
// at least make an attempt to get closer on the cycle timing. So we simply use an 'average'
// penalty of 4 cycles if we are in contended memory while the screen is rendering. It's
// rough but gets us close enough to play games. We can improve this later...
// ----------------------------------------------------------------------------------------
if (render)
{
if ((CPU.PC.W & 0xC000) == 0x4000) CPU.TStates += zx_contend_delay;
}
I=OpZ80(CPU.PC.W++); I=OpZ80(CPU.PC.W++);
CPU.TStates += Cycles_NoM1Wait[I]; CPU.TStates += Cycles_NoM1Wait[I];
@ -646,7 +634,8 @@ ITCM_CODE void ExecZ80_Speccy(u32 RunToCycles)
{ {
register byte I; register byte I;
register pair J; register pair J;
u8 render = zx_ScreenRendering; u8 render = zx_ScreenRendering; // Slightly faster access from stack
u8 delay = zx_contend_delay; // Slightly faster access from stack
while (CPU.TStates < RunToCycles) while (CPU.TStates < RunToCycles)
{ {
@ -658,7 +647,18 @@ ITCM_CODE void ExecZ80_Speccy(u32 RunToCycles)
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
if (render) if (render)
{ {
if ((CPU.PC.W & 0xC000) == 0x4000) CPU.TStates += zx_contend_delay; if (CPU.PC.W & 0x4000) // Either 0x4000 or 0xC000
{
if (CPU.PC.W & 0x8000) // Must be 0xC000
{
// For the ZX 128K bank, we contend if the bank is odd (1,3,5,7)
if (zx_128k_mode && (portFD & 1)) CPU.TStates += delay;
}
else // Must be 0x4000 - we contend on any video access (both 48K and 128K)
{
CPU.TStates += delay;
}
}
} }
I=OpZ80(CPU.PC.W++); I=OpZ80(CPU.PC.W++);

View File

@ -38,10 +38,10 @@ void highscore_save(void);
// --------------------------------------------------------- // ---------------------------------------------------------
// Each score has this stuff... Initials, score and date. // Each score has this stuff... Initials, score and date.
// --------------------------------------------------------- // ---------------------------------------------------------
struct score_t struct score_t
{ {
char initials[4]; // With NULL this is only 3 ascii characters char initials[4]; // With NULL this is only 3 ascii characters
char score[7]; // Six digits of score char score[7]; // Six digits of score
char reserved; // For the future... char reserved; // For the future...
u16 year; // Date score was achieved. We'll auto-fill this from DS time u16 year; // Date score was achieved. We'll auto-fill this from DS time
u8 month; u8 month;
@ -60,8 +60,8 @@ struct highscore_t
}; };
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// We save up to 550 games worth of scores. We also have a spot for default initials // We save up to 550 games worth of scores. We also have a spot for default initials
// so we can re-use the last initials for the last high-score entered. Saves time // so we can re-use the last initials for the last high-score entered. Saves time
// for most people who are always the ones using their DS system. // for most people who are always the ones using their DS system.
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
struct highscore_full_t struct highscore_full_t
@ -88,10 +88,10 @@ u32 highscore_checksum(void)
{ {
char *ptr = (char *)&highscores; char *ptr = (char *)&highscores;
u32 sum = 0; u32 sum = 0;
for (int i=0; i<(int)sizeof(highscores) - 4; i++) for (int i=0; i<(int)sizeof(highscores) - 4; i++)
{ {
sum = *ptr++; sum = *ptr++;
} }
return sum; return sum;
} }
@ -101,23 +101,22 @@ u32 highscore_checksum(void)
// Read the high score file, if it exists. If it doesn't exist or if the file // Read the high score file, if it exists. If it doesn't exist or if the file
// is not the right version and/or is corrupt (crc check), reset to defaults. // is not the right version and/or is corrupt (crc check), reset to defaults.
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
void highscore_init(void) void highscore_init(void)
{ {
u8 upgrade_database = 0;
u8 create_defaults = 0; u8 create_defaults = 0;
strcpy(highscores.last_initials, " "); strcpy(highscores.last_initials, " ");
// ------------------------------------------------------ // ------------------------------------------------------
// See if the high score file exists... if so, read it! // See if the high score file exists... if so, read it!
// ------------------------------------------------------ // ------------------------------------------------------
if (ReadFileCarefully("/data/SpeccySE.hi", (u8*)&highscores, sizeof(highscores), 0)) if (ReadFileCarefully("/data/SpeccySE.hi", (u8*)&highscores, sizeof(highscores), 0))
{ {
// -------------------------------------------- // --------------------------------------------
// If the high score version is wrong or if // If the high score version is wrong or if
// the checksum is wrong, reset to defaults // the checksum is wrong, reset to defaults
// -------------------------------------------- // --------------------------------------------
if (highscores.version != HS_VERSION) if (highscores.version != HS_VERSION)
{ {
create_defaults = 1; create_defaults = 1;
} }
@ -127,30 +126,11 @@ void highscore_init(void)
{ {
create_defaults = 1; create_defaults = 1;
} }
if (upgrade_database) if (create_defaults) // Doesn't exist yet or is invalid... create defaults and save it...
{
for (int i=400; i<MAX_HS_GAMES; i++)
{
highscores.highscore_table[i].crc = 0x00000000;
strcpy(highscores.highscore_table[i].notes, " ");
highscores.highscore_table[i].options = 0x0000;
for (int j=0; j<10; j++)
{
strcpy(highscores.highscore_table[i].scores[j].score, "000000");
strcpy(highscores.highscore_table[i].scores[j].initials, " ");
highscores.highscore_table[i].scores[j].reserved = 0;
highscores.highscore_table[i].scores[j].year = 0;
highscores.highscore_table[i].scores[j].month = 0;
highscores.highscore_table[i].scores[j].day = 0;
}
}
highscore_save();
}
else if (create_defaults) // Doesn't exist yet or is invalid... create defaults and save it...
{ {
strcpy(highscores.last_initials, " "); strcpy(highscores.last_initials, " ");
for (int i=0; i<MAX_HS_GAMES; i++) for (int i=0; i<MAX_HS_GAMES; i++)
{ {
highscores.highscore_table[i].crc = 0x00000000; highscores.highscore_table[i].crc = 0x00000000;
@ -175,7 +155,7 @@ void highscore_init(void)
// Save the high score file to disc. This gets saved in the /data directory and this // Save the high score file to disc. This gets saved in the /data directory and this
// directory is created if it doesn't exist (mostly likely does if using TWL++) // directory is created if it doesn't exist (mostly likely does if using TWL++)
// ------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------
void highscore_save(void) void highscore_save(void)
{ {
FILE *fp; FILE *fp;
@ -188,7 +168,7 @@ void highscore_save(void)
{ {
mkdir("/data", 0777); // Otherwise create the directory... mkdir("/data", 0777); // Otherwise create the directory...
} }
// -------------------------------------------------------- // --------------------------------------------------------
// Set our current highscore file version and checksum... // Set our current highscore file version and checksum...
// -------------------------------------------------------- // --------------------------------------------------------
@ -229,13 +209,13 @@ void highscore_showoptions(u16 options)
DSPrint(22,5,0, (char*)"[ALPHA]"); DSPrint(22,5,0, (char*)"[ALPHA]");
} }
else else
{ {
DSPrint(22,5,0, (char*)" "); DSPrint(22,5,0, (char*)" ");
} }
} }
// ----------------------------------------------------- // -----------------------------------------------------
// Show the 10 scores for this game... // Show the 10 scores for this game...
// ----------------------------------------------------- // -----------------------------------------------------
void show_scores(short foundIdx, bool bShowLegend) void show_scores(short foundIdx, bool bShowLegend)
{ {
@ -244,19 +224,19 @@ void show_scores(short foundIdx, bool bShowLegend)
{ {
if ((highscores.highscore_table[foundIdx].options & HS_OPT_SORTMASK) == HS_OPT_SORTTIME) if ((highscores.highscore_table[foundIdx].options & HS_OPT_SORTMASK) == HS_OPT_SORTTIME)
{ {
sprintf(hs_line, "%04d-%02d-%02d %-3s %c%c:%c%c.%c%c", highscores.highscore_table[foundIdx].scores[i].year, highscores.highscore_table[foundIdx].scores[i].month,highscores.highscore_table[foundIdx].scores[i].day, sprintf(hs_line, "%04d-%02d-%02d %-3s %c%c:%c%c.%c%c", highscores.highscore_table[foundIdx].scores[i].year, highscores.highscore_table[foundIdx].scores[i].month,highscores.highscore_table[foundIdx].scores[i].day,
highscores.highscore_table[foundIdx].scores[i].initials, highscores.highscore_table[foundIdx].scores[i].score[0], highscores.highscore_table[foundIdx].scores[i].score[1], highscores.highscore_table[foundIdx].scores[i].initials, highscores.highscore_table[foundIdx].scores[i].score[0], highscores.highscore_table[foundIdx].scores[i].score[1],
highscores.highscore_table[foundIdx].scores[i].score[2], highscores.highscore_table[foundIdx].scores[i].score[3], highscores.highscore_table[foundIdx].scores[i].score[4], highscores.highscore_table[foundIdx].scores[i].score[2], highscores.highscore_table[foundIdx].scores[i].score[3], highscores.highscore_table[foundIdx].scores[i].score[4],
highscores.highscore_table[foundIdx].scores[i].score[5]); highscores.highscore_table[foundIdx].scores[i].score[5]);
} }
else else
{ {
sprintf(hs_line, "%04d-%02d-%02d %-3s %-6s ", highscores.highscore_table[foundIdx].scores[i].year, highscores.highscore_table[foundIdx].scores[i].month,highscores.highscore_table[foundIdx].scores[i].day, sprintf(hs_line, "%04d-%02d-%02d %-3s %-6s ", highscores.highscore_table[foundIdx].scores[i].year, highscores.highscore_table[foundIdx].scores[i].month,highscores.highscore_table[foundIdx].scores[i].day,
highscores.highscore_table[foundIdx].scores[i].initials, highscores.highscore_table[foundIdx].scores[i].score); highscores.highscore_table[foundIdx].scores[i].initials, highscores.highscore_table[foundIdx].scores[i].score);
} }
DSPrint(3,6+i, 0, hs_line); DSPrint(3,6+i, 0, hs_line);
} }
if (bShowLegend) if (bShowLegend)
{ {
DSPrint(1,16,0, (char*)" "); DSPrint(1,16,0, (char*)" ");
@ -286,11 +266,11 @@ void highscore_sort(short foundIdx)
{ {
if (strcmp(highscores.highscore_table[foundIdx].scores[j+1].score, "000000") == 0) if (strcmp(highscores.highscore_table[foundIdx].scores[j+1].score, "000000") == 0)
strcpy(cmp1, "999999"); strcpy(cmp1, "999999");
else else
strcpy(cmp1, highscores.highscore_table[foundIdx].scores[j+1].score); strcpy(cmp1, highscores.highscore_table[foundIdx].scores[j+1].score);
if (strcmp(highscores.highscore_table[foundIdx].scores[j].score, "000000") == 0) if (strcmp(highscores.highscore_table[foundIdx].scores[j].score, "000000") == 0)
strcpy(cmp2, "999999"); strcpy(cmp2, "999999");
else else
strcpy(cmp2, highscores.highscore_table[foundIdx].scores[j].score); strcpy(cmp2, highscores.highscore_table[foundIdx].scores[j].score);
if (strcmp(cmp1, cmp2) < 0) if (strcmp(cmp1, cmp2) < 0)
{ {
@ -304,13 +284,13 @@ void highscore_sort(short foundIdx)
{ {
if (strcmp(highscores.highscore_table[foundIdx].scores[j+1].score, "000000") == 0) if (strcmp(highscores.highscore_table[foundIdx].scores[j+1].score, "000000") == 0)
strcpy(cmp1, "------"); strcpy(cmp1, "------");
else else
strcpy(cmp1, highscores.highscore_table[foundIdx].scores[j+1].score); strcpy(cmp1, highscores.highscore_table[foundIdx].scores[j+1].score);
if (strcmp(highscores.highscore_table[foundIdx].scores[j].score, "000000") == 0) if (strcmp(highscores.highscore_table[foundIdx].scores[j].score, "000000") == 0)
strcpy(cmp2, "------"); strcpy(cmp2, "------");
else else
strcpy(cmp2, highscores.highscore_table[foundIdx].scores[j].score); strcpy(cmp2, highscores.highscore_table[foundIdx].scores[j].score);
if (strcmp(cmp1, cmp2) > 0) if (strcmp(cmp1, cmp2) > 0)
{ {
// Swap... // Swap...
@ -330,7 +310,7 @@ void highscore_sort(short foundIdx)
} }
} }
} }
} }
} }
@ -347,7 +327,7 @@ void highscore_entry(short foundIdx, u32 crc)
char dampen=0; char dampen=0;
time_t unixTime = time(NULL); time_t unixTime = time(NULL);
struct tm* timeStruct = gmtime((const time_t *)&unixTime); struct tm* timeStruct = gmtime((const time_t *)&unixTime);
DSPrint(2,19,0, (char*)"UP/DN/LEFT/RIGHT ENTER SCORE"); DSPrint(2,19,0, (char*)"UP/DN/LEFT/RIGHT ENTER SCORE");
DSPrint(2,20,0, (char*)"PRESS START TO SAVE SCORE "); DSPrint(2,20,0, (char*)"PRESS START TO SAVE SCORE ");
DSPrint(2,21,0, (char*)"PRESS SELECT TO CANCEL "); DSPrint(2,21,0, (char*)"PRESS SELECT TO CANCEL ");
@ -363,7 +343,7 @@ void highscore_entry(short foundIdx, u32 crc)
swiWaitForVBlank(); swiWaitForVBlank();
if (keysCurrent() & KEY_SELECT) {bEntryDone=1;} if (keysCurrent() & KEY_SELECT) {bEntryDone=1;}
if (keysCurrent() & KEY_START) if (keysCurrent() & KEY_START)
{ {
strcpy(highscores.last_initials, score_entry.initials); strcpy(highscores.last_initials, score_entry.initials);
memcpy(&highscores.highscore_table[foundIdx].scores[9], &score_entry, sizeof(score_entry)); memcpy(&highscores.highscore_table[foundIdx].scores[9], &score_entry, sizeof(score_entry));
@ -377,14 +357,14 @@ void highscore_entry(short foundIdx, u32 crc)
{ {
if ((keysCurrent() & KEY_RIGHT) || (keysCurrent() & KEY_A)) if ((keysCurrent() & KEY_RIGHT) || (keysCurrent() & KEY_A))
{ {
if (entry_idx < 8) entry_idx++; if (entry_idx < 8) entry_idx++;
blink=25; blink=25;
dampen=15; dampen=15;
} }
if (keysCurrent() & KEY_LEFT) if (keysCurrent() & KEY_LEFT)
{ {
if (entry_idx > 0) entry_idx--; if (entry_idx > 0) entry_idx--;
blink=25; blink=25;
dampen=15; dampen=15;
} }
@ -468,7 +448,7 @@ void highscore_entry(short foundIdx, u32 crc)
} }
DSPrint(3,16, 0, (char*)hs_line); DSPrint(3,16, 0, (char*)hs_line);
} }
show_scores(foundIdx, true); show_scores(foundIdx, true);
} }
@ -483,7 +463,7 @@ void highscore_options(short foundIdx, u32 crc)
char blink=0; char blink=0;
unsigned short entry_idx=0; unsigned short entry_idx=0;
char dampen=0; char dampen=0;
DSPrint(2,16,0, (char*)" NOTE: "); DSPrint(2,16,0, (char*)" NOTE: ");
DSPrint(2,19,0, (char*)" UP/DN/LEFT/RIGHT ENTER NOTES"); DSPrint(2,19,0, (char*)" UP/DN/LEFT/RIGHT ENTER NOTES");
DSPrint(2,20,0, (char*)" X=TOGGLE SORT, L+R=CLR SCORE"); DSPrint(2,20,0, (char*)" X=TOGGLE SORT, L+R=CLR SCORE");
@ -492,13 +472,13 @@ void highscore_options(short foundIdx, u32 crc)
strcpy(notes, highscores.highscore_table[foundIdx].notes); strcpy(notes, highscores.highscore_table[foundIdx].notes);
options = highscores.highscore_table[foundIdx].options; options = highscores.highscore_table[foundIdx].options;
while (!bEntryDone) while (!bEntryDone)
{ {
swiWaitForVBlank(); swiWaitForVBlank();
if (keysCurrent() & KEY_SELECT) {bEntryDone=1;} if (keysCurrent() & KEY_SELECT) {bEntryDone=1;}
if (keysCurrent() & KEY_START) if (keysCurrent() & KEY_START)
{ {
strcpy(highscores.highscore_table[foundIdx].notes, notes); strcpy(highscores.highscore_table[foundIdx].notes, notes);
highscores.highscore_table[foundIdx].options = options; highscores.highscore_table[foundIdx].options = options;
@ -512,14 +492,14 @@ void highscore_options(short foundIdx, u32 crc)
{ {
if ((keysCurrent() & KEY_RIGHT) || (keysCurrent() & KEY_A)) if ((keysCurrent() & KEY_RIGHT) || (keysCurrent() & KEY_A))
{ {
if (entry_idx < 9) entry_idx++; if (entry_idx < 9) entry_idx++;
blink=25; blink=25;
dampen=15; dampen=15;
} }
if (keysCurrent() & KEY_LEFT) if (keysCurrent() & KEY_LEFT)
{ {
if (entry_idx > 0) entry_idx--; if (entry_idx > 0) entry_idx--;
blink=25; blink=25;
dampen=15; dampen=15;
} }
@ -550,7 +530,7 @@ void highscore_options(short foundIdx, u32 crc)
dampen=10; dampen=10;
} }
if (keysCurrent() & KEY_X) if (keysCurrent() & KEY_X)
{ {
if ((options & HS_OPT_SORTMASK) == HS_OPT_SORTLOW) if ((options & HS_OPT_SORTMASK) == HS_OPT_SORTLOW)
{ {
@ -573,14 +553,14 @@ void highscore_options(short foundIdx, u32 crc)
highscore_showoptions(options); highscore_showoptions(options);
dampen=15; dampen=15;
} }
// Clear the entire game of scores... // Clear the entire game of scores...
if ((keysCurrent() & KEY_L) && (keysCurrent() & KEY_R)) if ((keysCurrent() & KEY_L) && (keysCurrent() & KEY_R))
{ {
highscores.highscore_table[foundIdx].crc = 0x00000000; highscores.highscore_table[foundIdx].crc = 0x00000000;
highscores.highscore_table[foundIdx].options = 0x0000; highscores.highscore_table[foundIdx].options = 0x0000;
strcpy(highscores.highscore_table[foundIdx].notes, " "); strcpy(highscores.highscore_table[foundIdx].notes, " ");
strcpy(notes, " "); strcpy(notes, " ");
for (int j=0; j<10; j++) for (int j=0; j<10; j++)
{ {
strcpy(highscores.highscore_table[foundIdx].scores[j].score, "000000"); strcpy(highscores.highscore_table[foundIdx].scores[j].score, "000000");
@ -591,8 +571,8 @@ void highscore_options(short foundIdx, u32 crc)
highscores.highscore_table[foundIdx].scores[j].day = 0; highscores.highscore_table[foundIdx].scores[j].day = 0;
} }
show_scores(foundIdx, false); show_scores(foundIdx, false);
highscore_save(); highscore_save();
} }
} }
else else
{ {
@ -606,17 +586,17 @@ void highscore_options(short foundIdx, u32 crc)
} }
DSPrint(9,16, 0, (char*)hs_line); DSPrint(9,16, 0, (char*)hs_line);
} }
show_scores(foundIdx, true); show_scores(foundIdx, true);
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Entry point for the high score table. We are passed in the crc of the // Entry point for the high score table. We are passed in the crc of the
// current game. We use the crc to check the high score database and see // current game. We use the crc to check the high score database and see
// if there is already saved highscore data for this game. At the point // if there is already saved highscore data for this game. At the point
// where this is called, the high score init has already been called. // where this is called, the high score init has already been called.
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void highscore_display(u32 crc) void highscore_display(u32 crc)
{ {
short foundIdx = -1; short foundIdx = -1;
short firstBlank = -1; short firstBlank = -1;
@ -646,12 +626,12 @@ void highscore_display(u32 crc)
break; break;
} }
} }
if (foundIdx == -1) if (foundIdx == -1)
{ {
foundIdx = firstBlank; foundIdx = firstBlank;
} }
show_scores(foundIdx, true); show_scores(foundIdx, true);
while (!bDone) while (!bDone)
@ -661,7 +641,7 @@ void highscore_display(u32 crc)
if (keysCurrent() & KEY_X) highscore_entry(foundIdx, crc); if (keysCurrent() & KEY_X) highscore_entry(foundIdx, crc);
if (keysCurrent() & KEY_Y) highscore_options(foundIdx, crc); if (keysCurrent() & KEY_Y) highscore_options(foundIdx, crc);
} }
BottomScreenKeyboard(); BottomScreenKeyboard();
} }

View File

@ -25,9 +25,9 @@
#include "printf.h" #include "printf.h"
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
// I've seen a few rare POKEs that are massive - e.g. Jet Set Willy has a near // I've seen a few rare POKEs that are massive - e.g. Jet Set Willy has a near
// re-write of a routine to change the jumping ... We don't support those large // re-write of a routine to change the jumping ... We don't support those large
// POKEs here. Too much wasted memory and for now, we're keeping this very simple. // POKEs here. Too much wasted memory and for now, we're keeping this very simple.
// This should handle about 99% of all POKEs out there. Most games use it to // This should handle about 99% of all POKEs out there. Most games use it to
// produce extra lives, invulnerability or weapon upgrades. // produce extra lives, invulnerability or weapon upgrades.
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
@ -66,7 +66,7 @@ void pok_apply(u8 sel)
{ {
u8 bank = Pokes[sel].pok_bank[j]; u8 bank = Pokes[sel].pok_bank[j];
u16 value = Pokes[sel].pok_val[j]; u16 value = Pokes[sel].pok_val[j];
if (value == 256) // Must ask user for value... if (value == 256) // Must ask user for value...
{ {
value = 0; value = 0;
@ -81,7 +81,7 @@ void pok_apply(u8 sel)
DSPrint(28,22,2,tmp); DSPrint(28,22,2,tmp);
WAITVBL; WAITVBL;
} }
while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0); // Wait for release while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0); // Wait for release
DSPrint(0,22,0," "); DSPrint(0,22,0," ");
} }
@ -102,13 +102,13 @@ u8 num_pokes = 0;
u8 pok_readfile(void) u8 pok_readfile(void)
{ {
if (last_file_crc_poke_read == file_crc) return num_pokes; if (last_file_crc_poke_read == file_crc) return num_pokes;
last_file_crc_poke_read = file_crc; last_file_crc_poke_read = file_crc;
// Zero out all pokes before reading file // Zero out all pokes before reading file
memset(Pokes, 0x00, sizeof(Pokes)); memset(Pokes, 0x00, sizeof(Pokes));
num_pokes = 0; num_pokes = 0;
// POK files must be in a ./pok subdirectory // POK files must be in a ./pok subdirectory
sprintf(szLoadFile,"pok/%s", initial_file); sprintf(szLoadFile,"pok/%s", initial_file);
@ -128,7 +128,7 @@ u8 pok_readfile(void)
fgets(szLine, 255, infile); fgets(szLine, 255, infile);
char *ptr = szLine; char *ptr = szLine;
if (szLine[0] == 'N') if (szLine[0] == 'N')
{ {
memcpy(Pokes[num_pokes].pok_name, szLine+1, 30); memcpy(Pokes[num_pokes].pok_name, szLine+1, 30);
@ -142,28 +142,28 @@ u8 pok_readfile(void)
while (*ptr != ' ') ptr++; while (*ptr == ' ') ptr++; // Skip to next field while (*ptr != ' ') ptr++; while (*ptr == ' ') ptr++; // Skip to next field
Pokes[num_pokes].pok_mem[mem_idx] = atoi(ptr); Pokes[num_pokes].pok_mem[mem_idx] = atoi(ptr);
while (*ptr != ' ') ptr++; while (*ptr == ' ') ptr++; // Skip to next field while (*ptr != ' ') ptr++; while (*ptr == ' ') ptr++; // Skip to next field
Pokes[num_pokes].pok_val[mem_idx] = atoi(ptr); Pokes[num_pokes].pok_val[mem_idx] = atoi(ptr);
if (mem_idx < (MAX_POK_MEM-1)) mem_idx++; if (mem_idx < (MAX_POK_MEM-1)) mem_idx++;
if (szLine[0] == 'Z') if (szLine[0] == 'Z')
{ {
if (mem_idx < MAX_POK_MEM) num_pokes++; if (mem_idx < MAX_POK_MEM) num_pokes++;
else memset(Pokes[num_pokes].pok_mem, 0x00, sizeof(Pokes[num_pokes].pok_mem)); else memset(Pokes[num_pokes].pok_mem, 0x00, sizeof(Pokes[num_pokes].pok_mem));
if (num_pokes >= MAX_POKES) break; if (num_pokes >= MAX_POKES) break;
} }
} }
if (szLine[0] == 'Y') break; if (szLine[0] == 'Y') break;
} while (!feof(infile)); } while (!feof(infile));
fclose(infile); fclose(infile);
} }
return num_pokes; return num_pokes;
} }
#define POKES_PER_SCREEN 16 #define POKES_PER_SCREEN 16
// Show tape blocks with filenames/descriptions... // Show tape blocks with filenames/descriptions...
void pok_select(void) void pok_select(void)
{ {
char tmp[33]; char tmp[33];
@ -174,7 +174,7 @@ void pok_select(void)
BottomScreenOptions(); BottomScreenOptions();
u8 max = pok_readfile(); u8 max = pok_readfile();
if (max == 0) // No POKEs found... if (max == 0) // No POKEs found...
{ {
DSPrint(0,8,0, " NO .POK FILE WAS FOUND "); DSPrint(0,8,0, " NO .POK FILE WAS FOUND ");
@ -188,7 +188,7 @@ void pok_select(void)
else else
{ {
DSPrint(0,23,0,"PRESS A TO APPLY POKE, B TO EXIT"); DSPrint(0,23,0,"PRESS A TO APPLY POKE, B TO EXIT");
u8 screen_max = (max < POKES_PER_SCREEN ? max:POKES_PER_SCREEN); u8 screen_max = (max < POKES_PER_SCREEN ? max:POKES_PER_SCREEN);
u8 offset = 0; u8 offset = 0;
for (u8 i=0; i < screen_max; i++) for (u8 i=0; i < screen_max; i++)
@ -197,7 +197,7 @@ void pok_select(void)
DSPrint(1,4+i,(i==sel) ? 2:0,tmp); DSPrint(1,4+i,(i==sel) ? 2:0,tmp);
if (Pokes[offset+i].pok_applied) DSPrint(0,4+i,2,"@"); else DSPrint(0,4+i,0," "); if (Pokes[offset+i].pok_applied) DSPrint(0,4+i,2,"@"); else DSPrint(0,4+i,0," ");
} }
while (1) while (1)
{ {
u16 keys = keysCurrent(); u16 keys = keysCurrent();
@ -212,7 +212,7 @@ void pok_select(void)
} }
if (keys & KEY_B) {break;} if (keys & KEY_B) {break;}
if (keys & KEY_DOWN) if (keys & KEY_DOWN)
{ {
if (sel < (screen_max-1)) if (sel < (screen_max-1))
@ -226,7 +226,7 @@ void pok_select(void)
} }
else else
{ {
if ((offset + screen_max) < max) if ((offset + screen_max) < max)
{ {
offset += POKES_PER_SCREEN; offset += POKES_PER_SCREEN;
screen_max = ((max-offset) < POKES_PER_SCREEN ? (max-offset):POKES_PER_SCREEN); screen_max = ((max-offset) < POKES_PER_SCREEN ? (max-offset):POKES_PER_SCREEN);
@ -245,7 +245,7 @@ void pok_select(void)
DSPrint(0,4+i,0," "); DSPrint(0,4+i,0," ");
} }
} }
WAITVBL;WAITVBL;WAITVBL;WAITVBL; WAITVBL;WAITVBL;WAITVBL;
} }
} }
} }
@ -266,7 +266,7 @@ void pok_select(void)
{ {
offset -= POKES_PER_SCREEN; offset -= POKES_PER_SCREEN;
screen_max = ((max-offset) < POKES_PER_SCREEN ? (max-offset):POKES_PER_SCREEN); screen_max = ((max-offset) < POKES_PER_SCREEN ? (max-offset):POKES_PER_SCREEN);
sel = 0; sel = POKES_PER_SCREEN-1;
for (u8 i=0; i < POKES_PER_SCREEN; i++) for (u8 i=0; i < POKES_PER_SCREEN; i++)
{ {
if (i < screen_max) if (i < screen_max)
@ -281,17 +281,17 @@ void pok_select(void)
DSPrint(0,4+i,0," "); DSPrint(0,4+i,0," ");
} }
} }
WAITVBL;WAITVBL;WAITVBL;WAITVBL; WAITVBL;WAITVBL;WAITVBL;
} }
} }
} }
} }
} }
while (keysCurrent()) while (keysCurrent())
{ {
WAITVBL;WAITVBL; WAITVBL;WAITVBL;
} }
return; return;
} }

View File

@ -55,7 +55,8 @@ u8 spare[512] = {0x00}; // We keep some spare bytes so we can use th
static char szLoadFile[256]; // We build the filename out of the base filename and tack on .sav, .ee, etc. static char szLoadFile[256]; // We build the filename out of the base filename and tack on .sav, .ee, etc.
static char tmpStr[32]; static char tmpStr[32];
static u8 CompressBuffer[150*1024]; u8 CompressBuffer[150*1024]; // Big enough to handle compression of even full 128K games - we also steal this memory for screen snapshot use
void spectrumSaveState() void spectrumSaveState()
{ {
u32 spare = 0; u32 spare = 0;
@ -167,7 +168,7 @@ void spectrumSaveState()
ptr = RAM_Memory128; ptr = RAM_Memory128;
mem_size = 0x20000; mem_size = 0x20000;
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Compress the RAM data using 'high' compression ratio... it's // Compress the RAM data using 'high' compression ratio... it's
// still quite fast for such small memory buffers and often shrinks // still quite fast for such small memory buffers and often shrinks
@ -226,7 +227,7 @@ void spectrumLoadState()
// Read Last Directory Path / Tape File // Read Last Directory Path / Tape File
if (retVal) retVal = fread(&last_path, sizeof(last_path), 1, handle); if (retVal) retVal = fread(&last_path, sizeof(last_path), 1, handle);
if (retVal) retVal = fread(&last_file, sizeof(last_file), 1, handle); if (retVal) retVal = fread(&last_file, sizeof(last_file), 1, handle);
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// If the last known file was a tap file (.tap or .tzx) we want to // If the last known file was a tap file (.tap or .tzx) we want to
// reload that as the user might have swapped tapes to side 2, etc. // reload that as the user might have swapped tapes to side 2, etc.
@ -236,7 +237,7 @@ void spectrumLoadState()
chdir(last_path); chdir(last_path);
CassetteInsert(last_file); CassetteInsert(last_file);
} }
// Load CZ80 CPU // Load CZ80 CPU
if (retVal) retVal = fread(&CPU, sizeof(CPU), 1, handle); if (retVal) retVal = fread(&CPU, sizeof(CPU), 1, handle);
@ -320,7 +321,7 @@ void spectrumLoadState()
// Decompress the previously compressed RAM and put it back into the // Decompress the previously compressed RAM and put it back into the
// right memory location... this is quite fast all things considered. // right memory location... this is quite fast all things considered.
// ------------------------------------------------------------------ // ------------------------------------------------------------------
(void)lzav_decompress( CompressBuffer, dest_memory, comp_len, mem_size ); (void)lzav_decompress( CompressBuffer, dest_memory, comp_len, mem_size );
strcpy(tmpStr, (retVal ? "OK ":"ERR")); strcpy(tmpStr, (retVal ? "OK ":"ERR"));
DSPrint(13,0,0,tmpStr); DSPrint(13,0,0,tmpStr);

View File

@ -54,13 +54,11 @@ bool screenshotbmp(const char* filename) {
// --------------------------------------------------------- // ---------------------------------------------------------
// The screenshot requires a bit less than 100K of memory... // The screenshot requires a bit less than 100K of memory...
// We steal this from the back end of the big read-only // We steal this from the compression buffer which is not
// memory block. If we have a tape that is huge, we would // otherwise used except when save/loading save states.
// run into problems but it would be astronomically rare
// and it's better than wasting another 100K bytes that is
// mostly underutilized.
// --------------------------------------------------------- // ---------------------------------------------------------
u8 *temp = (u8*)ROM_Memory + (sizeof(ROM_Memory) - (100*1024)); extern u8 CompressBuffer[];
u8 *temp = (u8*)CompressBuffer;
HEADER *header= (HEADER*)temp; HEADER *header= (HEADER*)temp;
INFOHEADER *infoheader = (INFOHEADER*)(temp + sizeof(HEADER)); INFOHEADER *infoheader = (INFOHEADER*)(temp + sizeof(HEADER));

View File

@ -44,7 +44,19 @@ ITCM_CODE unsigned char cpu_readport_speccy(register unsigned short Port)
static u8 bNonSpecialKeyWasPressed = 0; static u8 bNonSpecialKeyWasPressed = 0;
if ((Port & 1) == 0) // Any Even Address will cause the ULA to respond if ((Port & 1) == 0) // Any Even Address will cause the ULA to respond
{ {
// ----------------------------------------------------------------------------------------
// If we are rendering the screen, a read from the ULA supplied port will produce
// a cycle penalty. This is not cycle accurate but we simply use an 'average'
// penalty of 4 cycles if we are in contended memory while the screen is rendering. It's
// rough but gets us close enough to play games. We can improve this later...
// ----------------------------------------------------------------------------------------
if (zx_ScreenRendering)
{
CPU.TStates += zx_contend_delay;
}
// -------------------------------------------------------- // --------------------------------------------------------
// If we are not playing the tape but we got a hit on the // If we are not playing the tape but we got a hit on the
// loader we can start the tape in motion - auto play... // loader we can start the tape in motion - auto play...
@ -78,7 +90,7 @@ ITCM_CODE unsigned char cpu_readport_speccy(register unsigned short Port)
// ----------------------------- // -----------------------------
u8 key = (portFE & 0x18) ? 0x00 : 0x40; u8 key = (portFE & 0x18) ? 0x00 : 0x40;
for (u8 i=0; i< (kbd_keys_pressed ? kbd_keys_pressed:1); i++) // Always one pass at least for joysticks... for (u8 i=0; i< kbd_keys_pressed; i++) // We may have more than one key pressed...
{ {
kbd_key = kbd_keys[i]; kbd_key = kbd_keys[i];
word inv = ~Port; word inv = ~Port;
@ -315,7 +327,9 @@ u32 zx_colors_extend32[16] __attribute__((section(".dtcm"))) =
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Render one screen line of pixels. This is called on every visible scanline. // Render one screen line of pixels. This is called on every visible scanline
// and is heavily optimized to draw as fast as possible. Since the screen is
// often drawing background (paper vs ink), that's handled via look-up table.
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
ITCM_CODE void speccy_render_screen_line(u8 line) ITCM_CODE void speccy_render_screen_line(u8 line)
{ {
@ -338,16 +352,18 @@ ITCM_CODE void speccy_render_screen_line(u8 line)
if (tape_play_skip_frame & 0x1F) return; if (tape_play_skip_frame & 0x1F) return;
} }
if (!isDSiMode() && (flash_timer & 1)) return; // For DS-Lite/Phat, we draw every other frame... // -----------------------------------------------------------
// For DS-Lite/Phat, we draw every other frame to gain speed.
// -----------------------------------------------------------
if (!isDSiMode() && (flash_timer & 1)) return;
// For the ZX 128K, we might be using page 7 for video display... it's rare, but possible... // -----------------------------------------------------------------------
// Render the current line into our NDS video memory. For the ZX 128K, we
// might be using page 7 for video display... it's rare, but possible...
// -----------------------------------------------------------------------
if (zx_128k_mode) zx_ScreenPage = RAM_Memory128 + ((portFD & 0x08) ? 7:5) * 0x4000; if (zx_128k_mode) zx_ScreenPage = RAM_Memory128 + ((portFD & 0x08) ? 7:5) * 0x4000;
else zx_ScreenPage = RAM_Memory + 0x4000; else zx_ScreenPage = RAM_Memory + 0x4000;
// --------------------------------------------------
// Render the current line into our NDS video memory
// --------------------------------------------------
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// The color attribute is stored independently from the pixel data // The color attribute is stored independently from the pixel data
// ---------------------------------------------------------------- // ----------------------------------------------------------------
@ -377,12 +393,17 @@ ITCM_CODE void speccy_render_screen_line(u8 line)
u8 ink = (attr & 0x07); // Ink Color is the foreground u8 ink = (attr & 0x07); // Ink Color is the foreground
if (attr & 0x40) ink |= 0x08; // Brightness if (attr & 0x40) ink |= 0x08; // Brightness
// ------------------------------------------------------------------------------------------------------------------------
// I've tried look-up tables here, but nothing was as fast as checking the bit and shifting ink/paper to the right spot...
// ------------------------------------------------------------------------------------------------------------------------
*vidBuf++ = (((pixel & 0x80) ? ink:paper)) | (((pixel & 0x40) ? ink:paper) << 8) | (((pixel & 0x20) ? ink:paper) << 16) | (((pixel & 0x10) ? ink:paper) << 24); *vidBuf++ = (((pixel & 0x80) ? ink:paper)) | (((pixel & 0x40) ? ink:paper) << 8) | (((pixel & 0x20) ? ink:paper) << 16) | (((pixel & 0x10) ? ink:paper) << 24);
*vidBuf++ = (((pixel & 0x08) ? ink:paper)) | (((pixel & 0x04) ? ink:paper) << 8) | (((pixel & 0x02) ? ink:paper) << 16) | (((pixel & 0x01) ? ink:paper) << 24); *vidBuf++ = (((pixel & 0x08) ? ink:paper)) | (((pixel & 0x04) ? ink:paper) << 8) | (((pixel & 0x02) ? ink:paper) << 16) | (((pixel & 0x01) ? ink:paper) << 24);
} }
else // Just drawing all background which is common... else // Just drawing all background which is common...
{ {
// Draw background directly to the screen // ------------------------------------------------------------------
// Draw background directly to the screen via extended look-up table
// ------------------------------------------------------------------
*vidBuf++ = zx_colors_extend32[paper]; *vidBuf++ = zx_colors_extend32[paper];
*vidBuf++ = zx_colors_extend32[paper]; *vidBuf++ = zx_colors_extend32[paper];
} }
@ -567,9 +588,11 @@ void speccy_reset(void)
static const u8 contend_delay[3] = {4,3,5}; static const u8 contend_delay[3] = {4,3,5};
zx_contend_delay = contend_delay[myConfig.contention]; zx_contend_delay = contend_delay[myConfig.contention];
// A bit wasteful to decompress again... but // ----------------------------------------------
// we want to ensure that the memory is exactly // Decompress the Z80/SNA snapshot here...
// We want to ensure that the memory is exactly
// as it should be when we reset the system. // as it should be when we reset the system.
// ----------------------------------------------
if (speccy_mode < MODE_SNA) if (speccy_mode < MODE_SNA)
{ {
tape_parse_blocks(last_file_size); tape_parse_blocks(last_file_size);
@ -580,7 +603,10 @@ void speccy_reset(void)
{ {
speccy_decompress_z80(last_file_size); speccy_decompress_z80(last_file_size);
} }
// else must be a diagnostic/ROM cart of some kind...
// -------------------------------------------------------------
// Handle the various snapshot formats... Z80 and SNA supported
// -------------------------------------------------------------
if (speccy_mode == MODE_SNA) // SNA snapshot if (speccy_mode == MODE_SNA) // SNA snapshot
{ {
@ -756,12 +782,12 @@ void speccy_reset(void)
ITCM_CODE u32 speccy_run(void) ITCM_CODE u32 speccy_run(void)
{ {
++zx_current_line; // This is the pixel line we're working on... ++zx_current_line; // This is the pixel line we're working on...
// ---------------------------------------------- // ----------------------------------------------
// Execute 1 scanline worth of CPU instructions. // Execute 1 scanline worth of CPU instructions.
// //
// We break this up into four pieces in order // We break this up into pieces in order to
// to get more chances to render the audio beeper // get more chances to render the audio beeper
// which requires a fairly high sample rate... // which requires a fairly high sample rate...
// ----------------------------------------------- // -----------------------------------------------
if (tape_state) if (tape_state)
@ -817,8 +843,8 @@ ITCM_CODE u32 speccy_run(void)
zx_current_line = 0; zx_current_line = 0;
zx_ScreenRendering = 0; zx_ScreenRendering = 0;
CPU.IRequest = INT_RST38; CPU.IRequest = INT_RST38;
IntZ80(&CPU, CPU.IRequest);
CPU.TStates_IRequest = CPU.TStates; CPU.TStates_IRequest = CPU.TStates;
IntZ80(&CPU, CPU.IRequest);
return 0; // End of frame return 0; // End of frame
} }

View File

@ -24,6 +24,13 @@
#include "SpeccyUtils.h" #include "SpeccyUtils.h"
#include "printf.h" #include "printf.h"
// -------------------------------------------------------------------
// From a look around a massive number of .TZX files, I haven't seen
// any that had more than about 1400 blocks which is pretty huge...
// So as to not soak up too much NDS memory, we're capping the number
// of blocks defined in a TZX file at 2048 which should handle just
// about anything...
// -------------------------------------------------------------------
#define MAX_TAPE_BLOCKS 2048 #define MAX_TAPE_BLOCKS 2048
#define BLOCK_ID_STANDARD 0x10 #define BLOCK_ID_STANDARD 0x10
@ -52,9 +59,11 @@
// Yes, this is special. It happens frequently enough we trap on the high bit here... // Yes, this is special. It happens frequently enough we trap on the high bit here...
#define SEND_DATA_BITS 0x80 #define SEND_DATA_BITS 0x80
// ---------------------------------------------------------
// Some defaults mostly for the .TAP files as the .TZX // Some defaults mostly for the .TAP files and
// will override some/many of these... // standard load blocks for the .TZX format.
// Custom/turbo blocks will override some/many of these...
// ---------------------------------------------------------
#define DEFAULT_PILOT_LENGTH 2168 #define DEFAULT_PILOT_LENGTH 2168
#define DEFAULT_DATA_ZERO_PULSE_WIDTH 855 #define DEFAULT_DATA_ZERO_PULSE_WIDTH 855
#define DEFAULT_DATA_ONE_PULSE_WIDTH 1710 #define DEFAULT_DATA_ONE_PULSE_WIDTH 1710
@ -115,6 +124,10 @@ inline byte OpZ80(word A) {return *(MemoryMap[(A)>>14] + ((A)&0x3FFF));}
TapePositionTable_t TapePositionTable[255]; TapePositionTable_t TapePositionTable[255];
extern char strcasestr (const char *big, const char *little); extern char strcasestr (const char *big, const char *little);
// --------------------------------------------------------
// Look for headers and blocks with descriptions and use
// those as index position for our cassette manager.
// --------------------------------------------------------
u8 tape_find_positions(void) u8 tape_find_positions(void)
{ {
memset(TapePositionTable, 0x00, sizeof(TapePositionTable)); memset(TapePositionTable, 0x00, sizeof(TapePositionTable));
@ -132,7 +145,7 @@ u8 tape_find_positions(void)
{ {
strcpy(TapePositionTable[pos_idx].description, TapeBlocks[i].description); strcpy(TapePositionTable[pos_idx].description, TapeBlocks[i].description);
TapePositionTable[pos_idx].block_id = i; TapePositionTable[pos_idx].block_id = i;
pos_idx++; if (++pos_idx == 255) break; // That's all we can handle
} }
else if (strlen(TapeBlocks[i].block_filename) > 2) else if (strlen(TapeBlocks[i].block_filename) > 2)
{ {
@ -145,14 +158,17 @@ u8 tape_find_positions(void)
{ {
strcpy(TapePositionTable[pos_idx].description, TapeBlocks[i].block_filename); strcpy(TapePositionTable[pos_idx].description, TapeBlocks[i].block_filename);
TapePositionTable[pos_idx].block_id = i; TapePositionTable[pos_idx].block_id = i;
pos_idx++; if (++pos_idx == 255) break; // That's all we can handle
} }
} }
} }
return pos_idx; return pos_idx;
} }
// Used in the pre-loader in the standard ROM... speeds up a ~1 second loop // --------------------------------------------------------
// Used in the pre-loader in the standard ROM... speeds up
// a roughly ~1 second loop and every little bit helps.
// --------------------------------------------------------
u8 tape_preloader_delay(void) u8 tape_preloader_delay(void)
{ {
u8 B = (CPU.BC.B.h-1) & 0xFF; u8 B = (CPU.BC.B.h-1) & 0xFF;
@ -442,15 +458,20 @@ void tape_parse_blocks(int tapeSize)
break; break;
} }
} }
// -----------------------------------------------------------------------------------------
// Sometimes the final block will have a long gap - but it's not needed as the tape is done
// playing at that point... so we cut this short which helps the emulator stop the tape.
// -----------------------------------------------------------------------------------------
TapeBlocks[num_blocks_available-1].gap_delay_after = 0;
} }
// -----------------------------------------------------------------------------------------
// Sometimes the final block will have a long gap - but it's not needed as the tape is done
// playing at that point... so we cut this short which helps the emulator stop the tape.
// -----------------------------------------------------------------------------------------
TapeBlocks[num_blocks_available-1].gap_delay_after = 0;
} }
// --------------------------------------------------------
// Some utility functions to know when the tape is playing
// and also to reset or play the current tape.
// --------------------------------------------------------
u8 tape_is_playing(void) u8 tape_is_playing(void)
{ {
return (tape_state != TAPE_STOP); return (tape_state != TAPE_STOP);
@ -485,14 +506,23 @@ void tape_position(u8 newPos)
current_block = TapePositionTable[newPos].block_id; current_block = TapePositionTable[newPos].block_id;
} }
// Called every frame // --------------------------------------------------------
// Called every frame - we use this to display a rough
// tape counter that is really just the number of 1K
// chunks that have been moved from the virtual tape into
// the Spectrum memory.
// --------------------------------------------------------
u8 show_tape_counter = 0; u8 show_tape_counter = 0;
void tape_frame(void) void tape_frame(void)
{ {
char tmp[5]; char tmp[5];
if (show_tape_counter) show_tape_counter--; if (show_tape_counter) show_tape_counter--;
// ----------------------------------------------
// If the tape is playing, show the counter and
// show the cassette icon in a green color.
// ----------------------------------------------
if (tape_state) if (tape_state)
{ {
if (bottom_screen == 2) if (bottom_screen == 2)
@ -500,7 +530,7 @@ void tape_frame(void)
sprintf(tmp, "%03d", (tape_bytes_processed/1000) % 1000); sprintf(tmp, "%03d", (tape_bytes_processed/1000) % 1000);
DSPrint(2, 20, 0, tmp); DSPrint(2, 20, 0, tmp);
show_tape_counter = 30; // Keep showing the counter for a half second show_tape_counter = 30; // Keep showing the counter for a half second
// Show the tape icon lit in green // Show the tape icon lit in green
if (myGlobalConfig.debugger <= 2) if (myGlobalConfig.debugger <= 2)
{ {
@ -509,9 +539,9 @@ void tape_frame(void)
} }
} }
} }
// ----------------------------------------- // -----------------------------------------
// If we are done showing the counter, show // If we are done showing the counter, show
// the stock cassette icon and no counter. // the stock cassette icon and no counter.
// ----------------------------------------- // -----------------------------------------
if (show_tape_counter == 0) if (show_tape_counter == 0)
@ -523,8 +553,8 @@ void tape_frame(void)
{ {
DSPrint(2, 21, 2, "!\"#"); DSPrint(2, 21, 2, "!\"#");
DSPrint(2, 22, 2, "ABC"); DSPrint(2, 22, 2, "ABC");
} }
show_tape_counter = 30; show_tape_counter = 30;
} }
} }
@ -984,7 +1014,7 @@ ld_sample:
B-=3; // Reduce counter by 3 B-=3; // Reduce counter by 3
goto ld_sample; // Take another sample. goto ld_sample; // Take another sample.
} }
CPU.TStates += 54; // It takes 54 total cycles when we take another pass around the loop CPU.TStates += 54; // It takes 54 total cycles when we take another pass around the loop
if (--B) goto ld_sample; // If no time-out... take another sample. if (--B) goto ld_sample; // If no time-out... take another sample.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 144 KiB

View File

@ -227,3 +227,8 @@ the benefit of running directly from SD card via TWL++ or Unlaunch or similar. B
really if you're looking for world-class ZX emulation for your DS/DSi handheld, really if you're looking for world-class ZX emulation for your DS/DSi handheld,
you're likely going to want ZXDS. you're likely going to want ZXDS.
Version History :
-----------------------
Version 1.0 - 14-Apr-2025 by wavemotion-dave
* First official release of Speccy-SE!