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

@ -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;
@ -51,12 +57,19 @@ u8 SpectrumBios128[0x8000] = {0}; // We keep the 32k ZX Spectrum 128K
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;
@ -95,9 +108,11 @@ 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,
@ -489,6 +504,11 @@ void CassetteInsert(char *filename)
}
}
// ----------------------------------------------------------------------
// 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
@ -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},
},
};
@ -540,10 +560,10 @@ void CassetteMenuShow(bool bClearScreen, u8 sel)
if (bClearScreen)
{
// -------------------------------------
// Put up the Cassette menu background
// -------------------------------------
BottomScreenCassette();
// -------------------------------------
// Put up the Cassette menu background
// -------------------------------------
BottomScreenCassette();
}
// ---------------------------------------------------
@ -552,7 +572,7 @@ void CassetteMenuShow(bool bClearScreen, u8 sel)
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)
@ -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;
@ -834,6 +862,9 @@ u8 handle_spectrum_keyboard_press(u16 iTx, u16 iTy) // ZX Spectrum keyboard
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...
@ -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++;
}
// ----------------------------------------------------------------------
@ -1617,9 +1648,9 @@ void LoadBIOSFiles(void)
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,7 +1679,7 @@ 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();
@ -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];

View File

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

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

@ -91,7 +91,7 @@ u32 highscore_checksum(void)
for (int i=0; i<(int)sizeof(highscores) - 4; i++)
{
sum = *ptr++;
sum = *ptr++;
}
return sum;
}
@ -103,7 +103,6 @@ u32 highscore_checksum(void)
// ------------------------------------------------------------------------------
void highscore_init(void)
{
u8 upgrade_database = 0;
u8 create_defaults = 0;
strcpy(highscores.last_initials, " ");
@ -128,26 +127,7 @@ 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, " ");

View File

@ -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,7 +281,7 @@ void pok_select(void)
DSPrint(0,4+i,0," ");
}
}
WAITVBL;WAITVBL;WAITVBL;WAITVBL;
WAITVBL;WAITVBL;WAITVBL;
}
}
}

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;

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

@ -45,6 +45,18 @@ ITCM_CODE unsigned char cpu_readport_speccy(register unsigned short Port)
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
{
@ -760,8 +786,8 @@ ITCM_CODE u32 speccy_run(void)
// ----------------------------------------------
// 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,7 +506,12 @@ 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)
{
@ -493,6 +519,10 @@ void tape_frame(void)
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)

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!