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

View File

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

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]
};
// -----------------------------------------------------------------------------------------------------------------
// 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;

View File

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

View File

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

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.
// -------------------------------------------------------------------------
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);

View File

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

View File

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

View File

@ -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<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...
if (create_defaults) // Doesn't exist yet or is invalid... create defaults and save it...
{
strcpy(highscores.last_initials, " ");
for (int i=0; i<MAX_HS_GAMES; i++)
{
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
// directory is created if it doesn't exist (mostly likely does if using TWL++)
// ------------------------------------------------------------------------------------
void highscore_save(void)
void highscore_save(void)
{
FILE *fp;
@ -188,7 +168,7 @@ void highscore_save(void)
{
mkdir("/data", 0777); // Otherwise create the directory...
}
// --------------------------------------------------------
// Set our current highscore file version and checksum...
// --------------------------------------------------------
@ -229,13 +209,13 @@ void highscore_showoptions(u16 options)
DSPrint(22,5,0, (char*)"[ALPHA]");
}
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)
{
@ -244,19 +224,19 @@ void show_scores(short foundIdx, bool bShowLegend)
{
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].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]);
}
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);
}
DSPrint(3,6+i, 0, hs_line);
}
if (bShowLegend)
{
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)
strcpy(cmp1, "999999");
else
else
strcpy(cmp1, highscores.highscore_table[foundIdx].scores[j+1].score);
if (strcmp(highscores.highscore_table[foundIdx].scores[j].score, "000000") == 0)
strcpy(cmp2, "999999");
else
else
strcpy(cmp2, highscores.highscore_table[foundIdx].scores[j].score);
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)
strcpy(cmp1, "------");
else
else
strcpy(cmp1, highscores.highscore_table[foundIdx].scores[j+1].score);
if (strcmp(highscores.highscore_table[foundIdx].scores[j].score, "000000") == 0)
strcpy(cmp2, "------");
else
else
strcpy(cmp2, highscores.highscore_table[foundIdx].scores[j].score);
if (strcmp(cmp1, cmp2) > 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();
}

View File

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

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

View File

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

View File

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

View File

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

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,
you're likely going to want ZXDS.
Version History :
-----------------------
Version 1.0 - 14-Apr-2025 by wavemotion-dave
* First official release of Speccy-SE!