diff --git a/Makefile b/Makefile index aa27c58..3eeee31 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ include $(DEVKITARM)/ds_rules export TARGET := SpeccySE 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" diff --git a/arm7/source/main.c b/arm7/source/main.c index 60c029d..8aab3e0 100644 --- a/arm7/source/main.c +++ b/arm7/source/main.c @@ -36,7 +36,7 @@ extern void mmInstall( int fifo_channel ); //--------------------------------------------------------------------------------- void VblankHandler(void) { //--------------------------------------------------------------------------------- - Wifi_Update(); + //Wifi_Update(); } @@ -71,7 +71,7 @@ int main() { SetYtrigger(80); - installWifiFIFO(); + //installWifiFIFO(); installSoundFIFO(); installSystemFIFO(); diff --git a/arm9/gfx_data/pdev_bg0.png b/arm9/gfx_data/pdev_bg0.png index 67411bc..c85fcbf 100644 Binary files a/arm9/gfx_data/pdev_bg0.png and b/arm9/gfx_data/pdev_bg0.png differ diff --git a/arm9/gfx_data/pdev_tbg0.png b/arm9/gfx_data/pdev_tbg0.png index 38cbab6..a59aca5 100644 Binary files a/arm9/gfx_data/pdev_tbg0.png and b/arm9/gfx_data/pdev_tbg0.png differ diff --git a/arm9/source/CRC32.c b/arm9/source/CRC32.c index 476b3f0..93b1fcc 100644 --- a/arm9/source/CRC32.c +++ b/arm9/source/CRC32.c @@ -52,10 +52,9 @@ const u32 crc32_table[256] = { 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 -// the media... so instead we'll just hash the filename as a CRC which is good enough to identify it in the future. -// ----------------------------------------------------------------------------------------------------------------- +// -------------------------------------------------- +// Compute the CRC of a memory buffer of any size... +// -------------------------------------------------- u32 getCRC32(u8 *buf, u32 size) { u32 crc = 0xFFFFFFFF; diff --git a/arm9/source/SpeccySE.c b/arm9/source/SpeccySE.c index b4c6268..354d315 100644 --- a/arm9/source/SpeccySE.c +++ b/arm9/source/SpeccySE.c @@ -3,7 +3,7 @@ // // Copying and distribution of this emulator, its source code and associated // readme files, with or without modification, are permitted in any medium without -// royalty provided this copyright notice is used and wavemotion-dave and Marat +// royalty provided this copyright notice is used and wavemotion-dave and Marat // Fayzullin (Z80 core) are thanked profusely. // // The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md @@ -35,6 +35,12 @@ #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 DX = 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) // 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 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 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]; char initial_file[MAX_FILENAME_LEN] = ""; char initial_path[MAX_FILENAME_LEN] = ""; -char last_path[MAX_FILENAME_LEN] = ""; -char last_file[MAX_FILENAME_LEN] = ""; +char last_path[MAX_FILENAME_LEN] = ""; +char last_file[MAX_FILENAME_LEN] = ""; +// -------------------------------------------------- +// A few housekeeping vars to help with emulation... +// -------------------------------------------------- u8 last_speccy_mode = 99; u8 bFirstTime = 3; 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) // ----------------------------------------------------------------------------------------------- -// 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) 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 @@ -95,16 +108,18 @@ u8 key_debounce = 0; // A bit of key debounce // 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}; -// -------------------------------------------------------------------- +// ---------------------------------------------------------------------- // 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"))) = { JST_UP, //0 JST_DOWN, JST_LEFT, JST_RIGHT, JST_FIRE, - + META_KBD_A, //5 META_KBD_B, META_KBD_C, @@ -117,7 +132,7 @@ u16 keyCoresp[MAX_KEY_OPTIONS] __attribute__((section(".dtcm"))) = { META_KBD_J, META_KBD_K, //15 META_KBD_L, - META_KBD_M, + META_KBD_M, META_KBD_N, META_KBD_O, META_KBD_P, //20 @@ -142,7 +157,7 @@ u16 keyCoresp[MAX_KEY_OPTIONS] __attribute__((section(".dtcm"))) = { META_KBD_8, META_KBD_9, META_KBD_0, //40 - + META_KBD_SHIFT, META_KBD_SYMBOL, 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;} } - + return len; } @@ -232,20 +247,20 @@ s16 beeper_vol[4] __attribute__((section(".dtcm"))) = { 0x000, 0x200, 0x600, 0xA u32 vol __attribute__((section(".dtcm"))) = 0; ITCM_CODE void processDirectAudio(void) { - if (zx_AY_enabled) + if (zx_AY_enabled) { ay38910Mixer(2, mixbufAY, &myAY); } - + for (u8 i=0; i<2; i++) { // Smooth edges of beeper slightly... if (portFE & 0x10) {if (vol < 3) vol++;} else {if (vol) vol--;} - + if (breather) {return;} 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 } @@ -269,7 +284,7 @@ void setupStream(void) mmLoadEffect(SFX_CLICKNOQUIT); mmLoadEffect(SFX_KEYCLICK); mmLoadEffect(SFX_MUS_INTRO); - + //---------------------------------------------------------------- // open stream //---------------------------------------------------------------- @@ -300,7 +315,7 @@ void sound_chip_reset() memset(mixer, 0x00, sizeof(mixer)); mixer_read=0; mixer_write=0; - + // -------------------------------------------------------------------- // 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 ay38910DataW(0x3F, &myAY); // All OFF (negative logic) ay38910Mixer(4, mixbufAY, &myAY);// Do an initial mix conversion to clear the output - + memset(mixbufAY, 0x00, sizeof(mixbufAY)); } @@ -317,8 +332,8 @@ void sound_chip_reset() // ----------------------------------------------------------------------- void dsInstallSoundEmuFIFO(void) { - SoundPause(); // Pause any sound output - sound_chip_reset(); // Reset the SN, AY and SCC chips + SoundPause(); // Pause any sound output + sound_chip_reset(); // Reset the SN, AY and SCC chips setupStream(); // Setup maxmod stream... 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... // -------------------------------------------------------------- void ResetSpectrum(void) -{ +{ JoyState = 0x00000000; // Nothing pressed to start sound_chip_reset(); // Reset the AY chip @@ -354,7 +369,7 @@ void ResetSpectrum(void) TIMER2_CR=TIMER_ENABLE | TIMER_DIV_1024; timingFrames = 0; emuFps=0; - + bFirstTime = 2; bStartIn = 0; bottom_screen = 0; @@ -385,7 +400,7 @@ int getMemFree() { // returns the amount of free memory in bytes void ShowDebugZ80(void) { u8 idx=2; - + if (myGlobalConfig.debugger == 3) { sprintf(tmp, "PC %04X SP %04X", CPU.PC.W, CPU.SP.W); @@ -408,7 +423,7 @@ void ShowDebugZ80(void) DSPrint(0,idx++,7, tmp); idx++; - + sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[0], myAY.ayRegs[1], myAY.ayRegs[2], myAY.ayRegs[3]); DSPrint(0,idx++,7, tmp); 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); sprintf(tmp, "AY %02X %02X %02X %02X", myAY.ayRegs[12], myAY.ayRegs[13], myAY.ayRegs[14], myAY.ayRegs[15]); DSPrint(0,idx++,7, tmp); - - idx++; + + idx++; 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 Free %dK", getMemFree()/1024); DSPrint(0,idx++,7, tmp); - + // CPU Disassembly! // Put out the debug registers... @@ -440,7 +455,7 @@ void ShowDebugZ80(void) idx = 1; 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); } } @@ -483,12 +498,17 @@ void CassetteInsert(char *filename) fclose(inFile); tape_parse_blocks(last_file_size); tape_reset(); - + strcpy(last_file, filename); 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_EXIT 0 // Exit the menu #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_SKIP 99 // Skip this MENU choice -typedef struct +typedef struct { char *menu_string; u8 menu_action; } MenuItem_t; -typedef struct +typedef struct { char *title; u8 start_row; @@ -517,13 +537,13 @@ CassetteDiskMenu_t generic_cassette_menu = { "CASSETTE MENU", 3, { - {" PLAY CASSETTE ", MENU_ACTION_PLAY}, - {" STOP CASSETTE ", MENU_ACTION_STOP}, - {" SWAP CASSETTE ", MENU_ACTION_SWAP}, - {" REWIND CASSETTE ", MENU_ACTION_REWIND}, - {" POSITION CASSETTE", MENU_ACTION_POSITION}, - {" EXIT MENU ", MENU_ACTION_EXIT}, - {" NULL ", MENU_ACTION_END}, + {" PLAY CASSETTE ", MENU_ACTION_PLAY}, + {" STOP CASSETTE ", MENU_ACTION_STOP}, + {" SWAP CASSETTE ", MENU_ACTION_SWAP}, + {" REWIND CASSETTE ", MENU_ACTION_REWIND}, + {" POSITION CASSETTE ", MENU_ACTION_POSITION}, + {" EXIT MENU ", MENU_ACTION_EXIT}, + {" NULL ", MENU_ACTION_END}, }, }; @@ -537,30 +557,30 @@ u8 cassette_menu_items = 0; void CassetteMenuShow(bool bClearScreen, u8 sel) { cassette_menu_items = 0; - + if (bClearScreen) { - // ------------------------------------- - // Put up the Cassette menu background - // ------------------------------------- - BottomScreenCassette(); + // ------------------------------------- + // Put up the Cassette menu background + // ------------------------------------- + BottomScreenCassette(); } - + // --------------------------------------------------- // Pick the right context menu based on the machine // --------------------------------------------------- menu = &generic_cassette_menu; - + // 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 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); - cassette_menu_items++; + cassette_menu_items++; } - + // ---------------------------------------------------------------------------------------------- // 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: bExitMenu = true; break; - + case MENU_ACTION_PLAY: tape_play(); bExitMenu = true; break; - + case MENU_ACTION_STOP: tape_stop(); 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; 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_dampen = 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 >= 202) && (iTx < 255)) return MENU_CHOICE_MENU; } - + DisplayStatusLine(false); - + 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) { 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 >= 187) && (iTx < 222)) return MENU_CHOICE_MENU; else if ((iTx >= 222) && (iTx < 255)) return MENU_CHOICE_CASSETTE; - + DisplayStatusLine(false); } else {kbd_key = 0; last_kbd_key = 0;} @@ -865,7 +896,7 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key) // Ask for verification if (showMessage("DO YOU REALLY WANT TO", "RESET THE CURRENT GAME ?") == ID_SHM_YES) { - ResetSpectrum(); + ResetSpectrum(); } BottomScreenKeyboard(); SoundUnPause(); @@ -910,14 +941,14 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key) BottomScreenKeyboard(); SoundUnPause(); break; - + case MENU_CHOICE_DEFINE_KEYS: SoundPause(); SpeccySEChangeKeymap(); BottomScreenKeyboard(); SoundUnPause(); break; - + case MENU_CHOICE_POKE_MEMORY: SoundPause(); pok_select(); @@ -936,7 +967,7 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key) return 0; } -// Show tape blocks with filenames/descriptions... +// Show tape blocks with filenames/descriptions... u8 speccyTapePosition(void) { u8 sel = 0; @@ -947,7 +978,7 @@ u8 speccyTapePosition(void) DisplayFileNameCassette(); DSPrint(1,1,0," TAPE POSITIONS "); - + u8 max = tape_find_positions(); u8 screen_max = (max < 10 ? max:10); u8 offset = 0; @@ -956,7 +987,7 @@ u8 speccyTapePosition(void) sprintf(tmp, "%03d %-26s", TapePositionTable[offset+i].block_id, TapePositionTable[offset+i].description); DSPrint(1,3+i,(i==sel) ? 2:0,tmp); } - + while (1) { u16 keys = keysCurrent(); @@ -975,7 +1006,7 @@ u8 speccyTapePosition(void) } else { - if ((offset + screen_max) < max) + if ((offset + screen_max) < 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); WAITVBL;WAITVBL; - + return sel+offset; } // ---------------------------------------------------------------------------- // 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_down = 0; @@ -1060,13 +1091,13 @@ void SpeccySE_main(void) // Setup the debug buffer for DSi use debug_init(); - + // Get the ZX Spectrum Emulator ready spectrumInit(gpFic[ucGameAct].szName); spectrumSetPalette(); spectrumRun(); - + // Frame-to-frame timing... TIMER1_CR = 0; TIMER1_DATA=0; @@ -1081,9 +1112,9 @@ void SpeccySE_main(void) // Force the sound engine to turn on when we start emulation bStartSoundEngine = 10; - + bFirstTime = 2; - + // ------------------------------------------------------------------- // Stay in this loop running the Spectrum game until the user exits... // ------------------------------------------------------------------- @@ -1124,7 +1155,7 @@ void SpeccySE_main(void) } DisplayStatusLine(false); emuActFrames = 0; - + if (bStartIn) { if (--bStartIn == 0) @@ -1132,12 +1163,12 @@ void SpeccySE_main(void) tape_play(); } } - + if (bFirstTime) { 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 (myConfig.autoLoad) @@ -1178,7 +1209,7 @@ void SpeccySE_main(void) while (TIMER2_DATA < 655*(timingFrames+1)) { if (myGlobalConfig.showFPS == 2) break; // If Full Speed, break out... - if (tape_is_playing()) + if (tape_is_playing()) { mixer_read = mixer_write = 0; 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. // -------------------------------------------------------------- if (key_debounce > 0) key_debounce--; @@ -1230,12 +1261,12 @@ void SpeccySE_main(void) meta_key = handle_debugger_overlay(iTx, iTy); } // ------------------------------------------------------------ - // Test the touchscreen for various full keyboard handlers... + // Test the touchscreen for various full keyboard handlers... // ------------------------------------------------------------ else { meta_key = handle_spectrum_keyboard_press(iTx, iTy); - } + } if (kbd_key != 0) { @@ -1277,9 +1308,9 @@ void SpeccySE_main(void) // ------------------------------------------------------------------------ ucDEUX = 0; 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)) { @@ -1307,29 +1338,29 @@ void SpeccySE_main(void) { chuckie_key_up = 12; chuckie_key_down = 0; - } + } if (nds_key & KEY_DOWN) { chuckie_key_down = 12; chuckie_key_up = 0; - } + } if (nds_key & KEY_LEFT) { chuckie_key_left = 12; chuckie_key_right = 0; - } + } if (nds_key & KEY_RIGHT) { chuckie_key_right = 12; chuckie_key_left = 0; } - + if (chuckie_key_up) { chuckie_key_up--; nds_key |= KEY_UP; } - + if (chuckie_key_down) { chuckie_key_down--; @@ -1348,7 +1379,7 @@ void SpeccySE_main(void) nds_key |= KEY_RIGHT; } } - + // -------------------------------------------------------------------------------------------------- // 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--; last_mapped_key = 0; } - + // ------------------------------------------------------------------------------------------ // 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... // --------------------------------------------------------- JoyState = ucDEUX; - + // -------------------------------------------------- // Handle Auto-Fire if enabled in configuration... // -------------------------------------------------- @@ -1447,7 +1478,7 @@ void speccySEInit(void) unsigned short dmaVal =*(bgGetMapPtr(bg0)+51*32); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1),32*24*2); - // Put up the options screen + // Put up the options screen BottomScreenOptions(); // Find the files @@ -1457,7 +1488,7 @@ void speccySEInit(void) void BottomScreenOptions(void) { swiWaitForVBlank(); - + if (bottom_screen != 1) { // --------------------------------------------------- @@ -1466,11 +1497,11 @@ void BottomScreenOptions(void) bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0); bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0); bgSetPriority(bg0b,1);bgSetPriority(bg1b,0); - + decompress(mainmenuTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(mainmenuMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); dmaCopy((void*) mainmenuPal,(void*) BG_PALETTE_SUB,256*2); - + unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32); 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," "); } - + bottom_screen = 1; } @@ -1488,7 +1519,7 @@ void BottomScreenOptions(void) void BottomScreenKeyboard(void) { swiWaitForVBlank(); - + if (myGlobalConfig.debugger == 3) // Full Z80 Debug overrides things... put up the debugger overlay { // 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*) speccy_kbdPal,(void*) BG_PALETTE_SUB,256*2); } - + unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); - + bottom_screen = 2; show_tape_counter = 0; @@ -1520,21 +1551,21 @@ void BottomScreenKeyboard(void) void BottomScreenCassette(void) { swiWaitForVBlank(); - + // --------------------------------------------------- // Put up the cassette screen background... // --------------------------------------------------- bg0b = bgInitSub(0, BgType_Text8bpp, BgSize_T_256x256, 31,0); bg1b = bgInitSub(1, BgType_Text8bpp, BgSize_T_256x256, 29,0); bgSetPriority(bg0b,1);bgSetPriority(bg1b,0); - + decompress(cassetteTiles, bgGetGfxPtr(bg0b), LZ77Vram); decompress(cassetteMap, (void*) bgGetMapPtr(bg0b), LZ77Vram); dmaCopy((void*) cassettePal,(void*) BG_PALETTE_SUB,256*2); - + unsigned short dmaVal = *(bgGetMapPtr(bg1b)+24*32); dmaFillWords(dmaVal | (dmaVal<<16),(void*) bgGetMapPtr(bg1b),32*24*2); - + bottom_screen = 3; } @@ -1544,16 +1575,16 @@ void BottomScreenCassette(void) ********************************************************************************/ void speccySEInitCPU(void) { - // ----------------------------------------- - // Init Main Memory and VDP Video Memory - // ----------------------------------------- - memset(RAM_Memory, 0x00, sizeof(RAM_Memory)); - memset(RAM_Memory128, 0x00, sizeof(RAM_Memory128)); + // ----------------------------------------- + // Init Main Memory for the Spectrum + // ----------------------------------------- + memset(RAM_Memory, 0x00, sizeof(RAM_Memory)); + memset(RAM_Memory128, 0x00, sizeof(RAM_Memory128)); - // ----------------------------------------------- - // Init bottom screen do display the ZX Keyboard - // ----------------------------------------------- - BottomScreenKeyboard(); + // ----------------------------------------------- + // Init bottom screen do display the ZX Keyboard + // ----------------------------------------------- + BottomScreenKeyboard(); } // ------------------------------------------------------------- @@ -1561,8 +1592,8 @@ void speccySEInitCPU(void) // ------------------------------------------------------------- void irqVBlank(void) { - // Manage time - vusCptVBL++; + // Manage time + vusCptVBL++; } // ---------------------------------------------------------------------- @@ -1583,7 +1614,7 @@ void LoadBIOSFiles(void) size = ReadFileCarefully("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("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); @@ -1591,35 +1622,35 @@ void LoadBIOSFiles(void) if (!size) size = ReadFileCarefully("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("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("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) bSpeccyBiosFound = true; else memset(SpectrumBios, 0xFF, 0x4000); size = ReadFileCarefully("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("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("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) bSpeccyBiosFound = true; else memset(SpectrumBios128, 0xFF, 0x8000); } -/********************************************************************************* +/************************************************************************************ * Program entry point - check if an argument has been passed in probably from TWL++ - ********************************************************************************/ + ***********************************************************************************/ int main(int argc, char **argv) { // Init sound @@ -1648,11 +1679,11 @@ int main(int argc, char **argv) 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(); LoadBIOSFiles(); - + // ----------------------------------------------------------------- // And do an initial load of configuration... We'll match it up // with the game that was selected later... @@ -1662,7 +1693,7 @@ int main(int argc, char **argv) // Handle command line argument... mostly for TWL++ 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) { static char path[128]; @@ -1682,7 +1713,7 @@ int main(int argc, char **argv) else { cmd_line_file[0]=0; // No file passed on command line... - + if (myGlobalConfig.lastDir && (strlen(myGlobalConfig.szLastPath) > 2)) { chdir(myGlobalConfig.szLastPath); // Try to start back where we last were... @@ -1699,7 +1730,7 @@ int main(int argc, char **argv) } SoundPause(); - + 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 // 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. @@ -1767,7 +1798,7 @@ void debug_init() { if (!debug_buffer) { - if (isDSiMode()) + if (isDSiMode()) { MAX_DEBUG_BUF_SIZE = (1024*1024*2); // 2MB!! debug_buffer = malloc(MAX_DEBUG_BUF_SIZE); diff --git a/arm9/source/SpeccySE.h b/arm9/source/SpeccySE.h index 38d244a..6d9dc86 100644 --- a/arm9/source/SpeccySE.h +++ b/arm9/source/SpeccySE.h @@ -3,7 +3,7 @@ // // Copying and distribution of this emulator, its source code and associated // readme files, with or without modification, are permitted in any medium without -// royalty provided this copyright notice is used and wavemotion-dave and Marat +// royalty provided this copyright notice is used and wavemotion-dave and Marat // Fayzullin (ColEM core) are thanked profusely. // // 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_POKE_MEMORY 0x07 #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. @@ -110,7 +110,7 @@ extern u32 DX, DY; #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. // 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 @@ -129,8 +129,8 @@ extern char initial_file[]; extern char initial_path[]; extern u16 nds_key; extern u8 kbd_key; -extern u16 vusCptVBL; // Video Management -extern u16 *pVidFlipBuf; // Video flipping buffer +extern u16 vusCptVBL; +extern u16 *pVidFlipBuf; extern u16 keyCoresp[MAX_KEY_OPTIONS]; extern u16 NDS_keyMap[]; extern u8 soundEmuPause; diff --git a/arm9/source/SpeccyUtils.c b/arm9/source/SpeccyUtils.c index 166ff8a..7214702 100644 --- a/arm9/source/SpeccyUtils.c +++ b/arm9/source/SpeccyUtils.c @@ -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. // ------------------------------------------------------------------------- int Filescmp (const void *c1, const void *c2) @@ -1652,7 +1652,7 @@ void spectrumRun(void) 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,0xD8, // Blue 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 strcpy(initial_file, filename); 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. // ----------------------------------------------------------------------- if (myGlobalConfig.lastDir) @@ -1730,7 +1730,7 @@ u8 loadgame(const char *filename) SaveConfig(FALSE); } } - + // Get file size the 'fast' way - use fstat() instead of fseek() or ftell() struct stat stbuf; (void)fstat(fileno(handle), &stbuf); diff --git a/arm9/source/SpeccyUtils.h b/arm9/source/SpeccyUtils.h index c4f84f3..2b72152 100644 --- a/arm9/source/SpeccyUtils.h +++ b/arm9/source/SpeccyUtils.h @@ -3,7 +3,7 @@ // // Copying and distribution of this emulator, its source code and associated // readme files, with or without modification, are permitted in any medium without -// royalty provided this copyright notice is used and wavemotion-dave and Marat +// royalty provided this copyright notice is used and wavemotion-dave and Marat // Fayzullin (ColEM core) are thanked profusely. // // The SpeccySE emulator is offered as-is, without any warranty. Please see readme.md @@ -17,18 +17,18 @@ #define MAX_FILES 2048 #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 CONFIG_VERSION 0x0004 - + #define SPECCY_FILE 0x01 #define DIRECTORY 0x02 - + #define ID_SHM_CANCEL 0x00 #define ID_SHM_YES 0x01 #define ID_SHM_NO 0x02 - + #define DPAD_NORMAL 0 #define DPAD_DIAGONALS 1 #define DPAD_CHUCKIE 2 @@ -137,7 +137,7 @@ extern u8 RAM_Memory128[0x20000]; extern u8 *MemoryMap[4]; extern AY38910 myAY; -extern FISpeccy gpFic[MAX_FILES]; +extern FISpeccy gpFic[MAX_FILES]; extern int uNbRoms; extern int ucGameAct; extern int ucGameChoice; diff --git a/arm9/source/cpu/z80/cz80/Z80.c b/arm9/source/cpu/z80/cz80/Z80.c index 7a6acff..afb97e1 100644 --- a/arm9/source/cpu/z80/cz80/Z80.c +++ b/arm9/source/cpu/z80/cz80/Z80.c @@ -591,20 +591,8 @@ void ExecOneInstruction(void) { register byte I; register pair J; - u8 render = zx_ScreenRendering; 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++); CPU.TStates += Cycles_NoM1Wait[I]; @@ -646,7 +634,8 @@ ITCM_CODE void ExecZ80_Speccy(u32 RunToCycles) { register byte I; 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) { @@ -658,7 +647,18 @@ ITCM_CODE void ExecZ80_Speccy(u32 RunToCycles) // ---------------------------------------------------------------------------------------- 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++); diff --git a/arm9/source/highscore.c b/arm9/source/highscore.c index 5aab714..7693233 100644 --- a/arm9/source/highscore.c +++ b/arm9/source/highscore.c @@ -38,10 +38,10 @@ void highscore_save(void); // --------------------------------------------------------- // 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 score[7]; // Six digits of score + char score[7]; // Six digits of score char reserved; // For the future... u16 year; // Date score was achieved. We'll auto-fill this from DS time 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 -// so we can re-use the last initials for the last high-score entered. Saves time +// 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 // for most people who are always the ones using their DS system. // ----------------------------------------------------------------------------------- struct highscore_full_t @@ -88,10 +88,10 @@ u32 highscore_checksum(void) { char *ptr = (char *)&highscores; u32 sum = 0; - + for (int i=0; i<(int)sizeof(highscores) - 4; i++) { - sum = *ptr++; + sum = *ptr++; } 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 // 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; - + strcpy(highscores.last_initials, " "); - + // ------------------------------------------------------ // See if the high score file exists... if so, read it! // ------------------------------------------------------ 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 // -------------------------------------------- - if (highscores.version != HS_VERSION) + if (highscores.version != HS_VERSION) { create_defaults = 1; } @@ -127,30 +126,11 @@ void highscore_init(void) { create_defaults = 1; } - - if (upgrade_database) - { - for (int i=400; i 0) { // Swap... @@ -330,7 +310,7 @@ void highscore_sort(short foundIdx) } } } - } + } } @@ -347,7 +327,7 @@ void highscore_entry(short foundIdx, u32 crc) char dampen=0; time_t unixTime = time(NULL); struct tm* timeStruct = gmtime((const time_t *)&unixTime); - + DSPrint(2,19,0, (char*)"UP/DN/LEFT/RIGHT ENTER SCORE"); DSPrint(2,20,0, (char*)"PRESS START TO SAVE SCORE "); DSPrint(2,21,0, (char*)"PRESS SELECT TO CANCEL "); @@ -363,7 +343,7 @@ void highscore_entry(short foundIdx, u32 crc) swiWaitForVBlank(); if (keysCurrent() & KEY_SELECT) {bEntryDone=1;} - if (keysCurrent() & KEY_START) + if (keysCurrent() & KEY_START) { strcpy(highscores.last_initials, score_entry.initials); 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 (entry_idx < 8) entry_idx++; + if (entry_idx < 8) entry_idx++; blink=25; dampen=15; } - if (keysCurrent() & KEY_LEFT) + if (keysCurrent() & KEY_LEFT) { - if (entry_idx > 0) entry_idx--; + if (entry_idx > 0) entry_idx--; blink=25; dampen=15; } @@ -468,7 +448,7 @@ void highscore_entry(short foundIdx, u32 crc) } DSPrint(3,16, 0, (char*)hs_line); } - + show_scores(foundIdx, true); } @@ -483,7 +463,7 @@ void highscore_options(short foundIdx, u32 crc) char blink=0; unsigned short entry_idx=0; char dampen=0; - + DSPrint(2,16,0, (char*)" NOTE: "); DSPrint(2,19,0, (char*)" UP/DN/LEFT/RIGHT ENTER NOTES"); 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); options = highscores.highscore_table[foundIdx].options; - + while (!bEntryDone) { swiWaitForVBlank(); if (keysCurrent() & KEY_SELECT) {bEntryDone=1;} - if (keysCurrent() & KEY_START) + if (keysCurrent() & KEY_START) { strcpy(highscores.highscore_table[foundIdx].notes, notes); 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 (entry_idx < 9) entry_idx++; + if (entry_idx < 9) entry_idx++; blink=25; dampen=15; } - if (keysCurrent() & KEY_LEFT) + if (keysCurrent() & KEY_LEFT) { - if (entry_idx > 0) entry_idx--; + if (entry_idx > 0) entry_idx--; blink=25; dampen=15; } @@ -550,7 +530,7 @@ void highscore_options(short foundIdx, u32 crc) dampen=10; } - if (keysCurrent() & KEY_X) + if (keysCurrent() & KEY_X) { if ((options & HS_OPT_SORTMASK) == HS_OPT_SORTLOW) { @@ -573,14 +553,14 @@ void highscore_options(short foundIdx, u32 crc) highscore_showoptions(options); dampen=15; } - - // Clear the entire game of scores... + + // Clear the entire game of scores... if ((keysCurrent() & KEY_L) && (keysCurrent() & KEY_R)) { highscores.highscore_table[foundIdx].crc = 0x00000000; highscores.highscore_table[foundIdx].options = 0x0000; strcpy(highscores.highscore_table[foundIdx].notes, " "); - strcpy(notes, " "); + strcpy(notes, " "); for (int j=0; j<10; j++) { 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; } show_scores(foundIdx, false); - highscore_save(); - } + highscore_save(); + } } else { @@ -606,17 +586,17 @@ void highscore_options(short foundIdx, u32 crc) } DSPrint(9,16, 0, (char*)hs_line); } - + 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 // 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. // ------------------------------------------------------------------------ -void highscore_display(u32 crc) +void highscore_display(u32 crc) { short foundIdx = -1; short firstBlank = -1; @@ -646,12 +626,12 @@ void highscore_display(u32 crc) break; } } - + if (foundIdx == -1) { - foundIdx = firstBlank; + foundIdx = firstBlank; } - + show_scores(foundIdx, true); while (!bDone) @@ -661,7 +641,7 @@ void highscore_display(u32 crc) if (keysCurrent() & KEY_X) highscore_entry(foundIdx, crc); if (keysCurrent() & KEY_Y) highscore_options(foundIdx, crc); } - + BottomScreenKeyboard(); } diff --git a/arm9/source/pok.c b/arm9/source/pok.c index 40db75d..6cd513e 100644 --- a/arm9/source/pok.c +++ b/arm9/source/pok.c @@ -25,9 +25,9 @@ #include "printf.h" // ---------------------------------------------------------------------------------- -// 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 -// POKEs here. Too much wasted memory and for now, we're keeping this very simple. +// 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 +// 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 // produce extra lives, invulnerability or weapon upgrades. // ---------------------------------------------------------------------------------- @@ -66,7 +66,7 @@ void pok_apply(u8 sel) { u8 bank = Pokes[sel].pok_bank[j]; u16 value = Pokes[sel].pok_val[j]; - + if (value == 256) // Must ask user for value... { value = 0; @@ -81,7 +81,7 @@ void pok_apply(u8 sel) DSPrint(28,22,2,tmp); WAITVBL; } - + while ((keysCurrent() & (KEY_UP | KEY_DOWN | KEY_A ))!=0); // Wait for release DSPrint(0,22,0," "); } @@ -102,13 +102,13 @@ u8 num_pokes = 0; u8 pok_readfile(void) { if (last_file_crc_poke_read == file_crc) return num_pokes; - + last_file_crc_poke_read = file_crc; - + // Zero out all pokes before reading file memset(Pokes, 0x00, sizeof(Pokes)); num_pokes = 0; - + // POK files must be in a ./pok subdirectory sprintf(szLoadFile,"pok/%s", initial_file); @@ -128,7 +128,7 @@ u8 pok_readfile(void) fgets(szLine, 255, infile); char *ptr = szLine; - + if (szLine[0] == 'N') { 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 Pokes[num_pokes].pok_mem[mem_idx] = atoi(ptr); 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 (szLine[0] == 'Z') - { + { if (mem_idx < MAX_POK_MEM) num_pokes++; else memset(Pokes[num_pokes].pok_mem, 0x00, sizeof(Pokes[num_pokes].pok_mem)); if (num_pokes >= MAX_POKES) break; } } - + if (szLine[0] == 'Y') break; } while (!feof(infile)); fclose(infile); } - + return num_pokes; } #define POKES_PER_SCREEN 16 -// Show tape blocks with filenames/descriptions... +// Show tape blocks with filenames/descriptions... void pok_select(void) { char tmp[33]; @@ -174,7 +174,7 @@ void pok_select(void) BottomScreenOptions(); u8 max = pok_readfile(); - + if (max == 0) // No POKEs found... { DSPrint(0,8,0, " NO .POK FILE WAS FOUND "); @@ -188,7 +188,7 @@ void pok_select(void) else { DSPrint(0,23,0,"PRESS A TO APPLY POKE, B TO EXIT"); - + u8 screen_max = (max < POKES_PER_SCREEN ? max:POKES_PER_SCREEN); u8 offset = 0; 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); if (Pokes[offset+i].pok_applied) DSPrint(0,4+i,2,"@"); else DSPrint(0,4+i,0," "); } - + while (1) { u16 keys = keysCurrent(); @@ -212,7 +212,7 @@ void pok_select(void) } if (keys & KEY_B) {break;} - + if (keys & KEY_DOWN) { if (sel < (screen_max-1)) @@ -226,7 +226,7 @@ void pok_select(void) } else { - if ((offset + screen_max) < max) + if ((offset + screen_max) < 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," "); } } - WAITVBL;WAITVBL;WAITVBL;WAITVBL; + WAITVBL;WAITVBL;WAITVBL; } } } @@ -266,7 +266,7 @@ void pok_select(void) { 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++) { if (i < screen_max) @@ -281,17 +281,17 @@ void pok_select(void) DSPrint(0,4+i,0," "); } } - WAITVBL;WAITVBL;WAITVBL;WAITVBL; + WAITVBL;WAITVBL;WAITVBL; } } } } } - + while (keysCurrent()) { WAITVBL;WAITVBL; } - + return; } diff --git a/arm9/source/saveload.c b/arm9/source/saveload.c index a4a4f30..7fe171b 100644 --- a/arm9/source/saveload.c +++ b/arm9/source/saveload.c @@ -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 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() { u32 spare = 0; @@ -167,7 +168,7 @@ void spectrumSaveState() ptr = RAM_Memory128; mem_size = 0x20000; } - + // ------------------------------------------------------------------- // Compress the RAM data using 'high' compression ratio... it's // still quite fast for such small memory buffers and often shrinks @@ -226,7 +227,7 @@ void spectrumLoadState() // Read Last Directory Path / Tape File if (retVal) retVal = fread(&last_path, sizeof(last_path), 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 // reload that as the user might have swapped tapes to side 2, etc. @@ -236,7 +237,7 @@ void spectrumLoadState() chdir(last_path); CassetteInsert(last_file); } - + // Load CZ80 CPU 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 // 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")); DSPrint(13,0,0,tmpStr); diff --git a/arm9/source/screenshot.c b/arm9/source/screenshot.c index 1eb66ee..d9d388a 100644 --- a/arm9/source/screenshot.c +++ b/arm9/source/screenshot.c @@ -54,13 +54,11 @@ bool screenshotbmp(const char* filename) { // --------------------------------------------------------- // The screenshot requires a bit less than 100K of memory... - // We steal this from the back end of the big read-only - // memory block. If we have a tape that is huge, we would - // run into problems but it would be astronomically rare - // and it's better than wasting another 100K bytes that is - // mostly underutilized. + // We steal this from the compression buffer which is not + // otherwise used except when save/loading save states. // --------------------------------------------------------- - u8 *temp = (u8*)ROM_Memory + (sizeof(ROM_Memory) - (100*1024)); + extern u8 CompressBuffer[]; + u8 *temp = (u8*)CompressBuffer; HEADER *header= (HEADER*)temp; INFOHEADER *infoheader = (INFOHEADER*)(temp + sizeof(HEADER)); diff --git a/arm9/source/spectrum.c b/arm9/source/spectrum.c index 4f042f9..9094cf2 100644 --- a/arm9/source/spectrum.c +++ b/arm9/source/spectrum.c @@ -44,7 +44,19 @@ ITCM_CODE unsigned char cpu_readport_speccy(register unsigned short Port) static u8 bNonSpecialKeyWasPressed = 0; 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 // 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; - 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]; 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) { @@ -338,16 +352,18 @@ ITCM_CODE void speccy_render_screen_line(u8 line) 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; 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 // ---------------------------------------------------------------- @@ -377,12 +393,17 @@ ITCM_CODE void speccy_render_screen_line(u8 line) u8 ink = (attr & 0x07); // Ink Color is the foreground 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 & 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... { - // 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]; } @@ -567,9 +588,11 @@ void speccy_reset(void) static const u8 contend_delay[3] = {4,3,5}; 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. + // ---------------------------------------------- if (speccy_mode < MODE_SNA) { tape_parse_blocks(last_file_size); @@ -580,7 +603,10 @@ void speccy_reset(void) { 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 { @@ -756,12 +782,12 @@ void speccy_reset(void) ITCM_CODE u32 speccy_run(void) { ++zx_current_line; // This is the pixel line we're working on... - + // ---------------------------------------------- // Execute 1 scanline worth of CPU instructions. // - // We break this up into four pieces in order - // to get more chances to render the audio beeper + // We break this up into pieces in order to + // get more chances to render the audio beeper // which requires a fairly high sample rate... // ----------------------------------------------- if (tape_state) @@ -817,8 +843,8 @@ ITCM_CODE u32 speccy_run(void) zx_current_line = 0; zx_ScreenRendering = 0; CPU.IRequest = INT_RST38; - IntZ80(&CPU, CPU.IRequest); CPU.TStates_IRequest = CPU.TStates; + IntZ80(&CPU, CPU.IRequest); return 0; // End of frame } diff --git a/arm9/source/tapeload.c b/arm9/source/tapeload.c index fdaa0b0..e5f497c 100644 --- a/arm9/source/tapeload.c +++ b/arm9/source/tapeload.c @@ -24,6 +24,13 @@ #include "SpeccyUtils.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 BLOCK_ID_STANDARD 0x10 @@ -52,9 +59,11 @@ // Yes, this is special. It happens frequently enough we trap on the high bit here... #define SEND_DATA_BITS 0x80 - -// Some defaults mostly for the .TAP files as the .TZX -// will override some/many of these... +// --------------------------------------------------------- +// Some defaults mostly for the .TAP files and +// standard load blocks for the .TZX format. +// Custom/turbo blocks will override some/many of these... +// --------------------------------------------------------- #define DEFAULT_PILOT_LENGTH 2168 #define DEFAULT_DATA_ZERO_PULSE_WIDTH 855 #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]; 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) { memset(TapePositionTable, 0x00, sizeof(TapePositionTable)); @@ -132,7 +145,7 @@ u8 tape_find_positions(void) { strcpy(TapePositionTable[pos_idx].description, TapeBlocks[i].description); 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) { @@ -145,14 +158,17 @@ u8 tape_find_positions(void) { strcpy(TapePositionTable[pos_idx].description, TapeBlocks[i].block_filename); TapePositionTable[pos_idx].block_id = i; - pos_idx++; + if (++pos_idx == 255) break; // That's all we can handle } } } 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 B = (CPU.BC.B.h-1) & 0xFF; @@ -442,15 +458,20 @@ void tape_parse_blocks(int tapeSize) 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) { return (tape_state != TAPE_STOP); @@ -485,14 +506,23 @@ void tape_position(u8 newPos) 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; void tape_frame(void) { char tmp[5]; - + 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 (bottom_screen == 2) @@ -500,7 +530,7 @@ void tape_frame(void) sprintf(tmp, "%03d", (tape_bytes_processed/1000) % 1000); DSPrint(2, 20, 0, tmp); show_tape_counter = 30; // Keep showing the counter for a half second - + // Show the tape icon lit in green 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. // ----------------------------------------- if (show_tape_counter == 0) @@ -523,8 +553,8 @@ void tape_frame(void) { DSPrint(2, 21, 2, "!\"#"); DSPrint(2, 22, 2, "ABC"); - } - + } + show_tape_counter = 30; } } @@ -984,7 +1014,7 @@ ld_sample: B-=3; // Reduce counter by 3 goto ld_sample; // Take another sample. } - + 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. diff --git a/png/cassette.bmp b/png/cassette.bmp index 7743e77..4a5811d 100644 Binary files a/png/cassette.bmp and b/png/cassette.bmp differ diff --git a/readme.md b/readme.md index c96dade..7046121 100644 --- a/readme.md +++ b/readme.md @@ -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, you're likely going to want ZXDS. +Version History : +----------------------- +Version 1.0 - 14-Apr-2025 by wavemotion-dave +* First official release of Speccy-SE! +