mirror of
https://github.com/wavemotion-dave/SpeccySE.git
synced 2025-06-18 13:55:33 -04:00
Version 1.0 released! See readme.md file.
This commit is contained in:
parent
bdde6bed8c
commit
28d72d5ddb
BIN
SpeccySE.nds
BIN
SpeccySE.nds
Binary file not shown.
Binary file not shown.
1595
arm7/build/.map
1595
arm7/build/.map
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -198,6 +198,10 @@ u16 mixer_read __attribute__((section(".dtcm"))) = 0;
|
||||
u16 mixer_write __attribute__((section(".dtcm"))) = 0;
|
||||
s16 mixer[WAVE_DIRECT_BUF_SIZE+1];
|
||||
|
||||
|
||||
// The games normally run at the proper 100% speed, but user can override from 80% to 120%
|
||||
u16 GAME_SPEED_PAL[] __attribute__((section(".dtcm"))) = {655, 596, 547, 728, 818 };
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// maxmod will call this routine when the buffer is half-empty and requests that
|
||||
// we fill the sound buffer with more samples. They will request 'len' samples and
|
||||
@ -270,6 +274,32 @@ ITCM_CODE void processDirectAudio(void)
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
// The user can override the core emulation speed from 80% to 120% to make games play faster/slow
|
||||
// than normal. We must adjust the MaxMode sample frequency to match or else we will not have the
|
||||
// proper number of samples in our sound buffer... this isn't perfect but it's reasonably good!
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
static u8 last_game_speed = 0;
|
||||
static u32 sample_rate_adjust[] = {100, 110, 120, 90, 80};
|
||||
void newStreamSampleRate(void)
|
||||
{
|
||||
if (last_game_speed != myConfig.gameSpeed)
|
||||
{
|
||||
last_game_speed = myConfig.gameSpeed;
|
||||
mmStreamClose();
|
||||
|
||||
// Adjust the sample rate to match the core emulation speed... user can override from 80% to 120%
|
||||
int new_sample_rate = (sample_rate * sample_rate_adjust[myConfig.gameSpeed]) / 100;
|
||||
myStream.sampling_rate = new_sample_rate; // sample_rate for the ZX to match the AY/Beeper drivers
|
||||
myStream.buffer_length = buffer_size; // buffer length = (512+16)
|
||||
myStream.callback = OurSoundMixer; // set callback function
|
||||
myStream.format = MM_STREAM_16BIT_STEREO; // format = stereo 16-bit
|
||||
myStream.timer = MM_TIMER0; // use hardware timer 0
|
||||
myStream.manual = false; // use automatic filling
|
||||
mmStreamOpen(&myStream);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// Setup the maxmod audio stream - this will be a 16-bit Stereo PCM output at 55KHz which
|
||||
// sounds about right for the ZX Spectrum AY chip (we mix in beeper tones as well)
|
||||
@ -957,7 +987,7 @@ u8 __attribute__((noinline)) handle_meta_key(u8 meta_key)
|
||||
break;
|
||||
|
||||
case MENU_CHOICE_CASSETTE:
|
||||
if (speccy_mode <= MODE_SNA) // Only show if we have a tape loaded
|
||||
if ((speccy_mode <= MODE_SNA) || (speccy_mode == MODE_BIOS)) // Only show if we have a tape loaded
|
||||
{
|
||||
CassetteMenu();
|
||||
}
|
||||
@ -1110,6 +1140,8 @@ void SpeccySE_main(void)
|
||||
timingFrames = 0;
|
||||
emuFps=0;
|
||||
|
||||
newStreamSampleRate();
|
||||
|
||||
// Force the sound engine to turn on when we start emulation
|
||||
bStartSoundEngine = 10;
|
||||
|
||||
@ -1160,7 +1192,19 @@ void SpeccySE_main(void)
|
||||
{
|
||||
if (--bStartIn == 0)
|
||||
{
|
||||
tape_play();
|
||||
// ---------------------------------------------------------
|
||||
// If we are running in ZX81 mode, we now copy the .P file
|
||||
// into memory and the ZX81 emulation will take over...
|
||||
// ---------------------------------------------------------
|
||||
if (speccy_mode == MODE_ZX81)
|
||||
{
|
||||
u8 *ptr = MemoryMap[16393>>14] + (16393&0x3FFF);
|
||||
memcpy(ptr, ROM_Memory+0x4000, last_file_size-0x4000);
|
||||
}
|
||||
else // Otherwise, play the ZX Spectrum tape!
|
||||
{
|
||||
tape_play();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1181,9 +1225,15 @@ void SpeccySE_main(void)
|
||||
{
|
||||
BufferKey('J'); BufferKey(KBD_KEY_SYMBOL); BufferKey('P'); BufferKey(KBD_KEY_SYMBOL); BufferKey('P'); BufferKey(KBD_KEY_RET);
|
||||
}
|
||||
if (myConfig.autoLoad && (myConfig.tapeSpeed == 0)) bStartIn = 2; // Start tape in 2 frames...
|
||||
if (myConfig.autoLoad && (myConfig.tapeSpeed == 0)) bStartIn = 2; // Start tape in 2 seconds...
|
||||
}
|
||||
}
|
||||
else if (speccy_mode == MODE_ZX81)
|
||||
{
|
||||
BufferKey('6'); BufferKey(254); BufferKey('6'); BufferKey(254); BufferKey('6'); BufferKey(254); BufferKey(KBD_KEY_RET); BufferKey(255); BufferKey(255); BufferKey(255); BufferKey(255); BufferKey(255);
|
||||
BufferKey('M'); BufferKey(255); BufferKey('5'); BufferKey(254); BufferKey('0'); BufferKey(254); BufferKey('0'); BufferKey(254); BufferKey('0'); BufferKey(254); BufferKey(KBD_KEY_RET); BufferKey(255);
|
||||
bStartIn = 10; // Start P-File in 10 seconds... (it takes 6-7 seconds to process those keys above... slow processing on the ZX81 emulation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1206,7 +1256,7 @@ void SpeccySE_main(void)
|
||||
//
|
||||
// This is how we time frame-to frame to keep the game running at 50FPS
|
||||
// ----------------------------------------------------------------------
|
||||
while (TIMER2_DATA < 655*(timingFrames+1))
|
||||
while (TIMER2_DATA < GAME_SPEED_PAL[myConfig.gameSpeed]*(timingFrames+1))
|
||||
{
|
||||
if (myGlobalConfig.showFPS == 2) break; // If Full Speed, break out...
|
||||
if (tape_is_playing())
|
||||
|
@ -106,6 +106,7 @@ extern u32 DX, DY;
|
||||
#define MODE_SNA 5
|
||||
#define MODE_Z80 6
|
||||
#define MODE_BIOS 7
|
||||
#define MODE_ZX81 8
|
||||
|
||||
#define WAITVBL swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank(); swiWaitForVBlank();
|
||||
|
||||
|
@ -344,39 +344,57 @@ void speccySEFindFiles(u8 bTapeOnly)
|
||||
else {
|
||||
if ((strlen(szFile)>4) && (strlen(szFile)<(MAX_FILENAME_LEN-4)) && (szFile[0] != '.') && (szFile[0] != '_')) // For MAC don't allow files starting with an underscore
|
||||
{
|
||||
if (!bTapeOnly) // If we're loading tape files only, exclude .z80 and .sna snapshots
|
||||
{
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".z80") == 0) ) {
|
||||
if (bTapeOnly == 2) // Load P files only
|
||||
{
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".p") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".sna") == 0) ) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!bTapeOnly) // If we're loading tape files only, exclude .z80 and .sna snapshots
|
||||
{
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".z80") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".sna") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".rom") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".z81") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".tap") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".rom") == 0) ) {
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".tzx") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".tap") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
if ( (strcasecmp(strrchr(szFile, '.'), ".tzx") == 0) ) {
|
||||
strcpy(gpFic[uNbFile].szName,szFile);
|
||||
gpFic[uNbFile].uType = SPECCY_FILE;
|
||||
uNbFile++;
|
||||
countZX++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -781,6 +799,23 @@ void Sinclair1(void)
|
||||
myConfig.keymap[11] = 31; // NDS SELECT mapped to '1'
|
||||
}
|
||||
|
||||
// 5 (left), 6 (down), 7 (up), 8 (right)
|
||||
void Cursors(void)
|
||||
{
|
||||
myConfig.keymap[0] = 37; // UP
|
||||
myConfig.keymap[1] = 36; // DOWN
|
||||
myConfig.keymap[2] = 35; // LEFT
|
||||
myConfig.keymap[3] = 38; // RIGHT
|
||||
myConfig.keymap[4] = 44; // Return
|
||||
myConfig.keymap[5] = 43; // Space
|
||||
myConfig.keymap[6] = 43; // Space
|
||||
myConfig.keymap[7] = 43; // Space
|
||||
myConfig.keymap[8] = 41; // NDS R Button mapped to SHIFT
|
||||
myConfig.keymap[9] = 42; // NDS L Button mapped to SYMBOL
|
||||
myConfig.keymap[10] = 40; // NDS START mapped to '0'
|
||||
myConfig.keymap[11] = 31; // NDS SELECT mapped to '1'
|
||||
}
|
||||
|
||||
|
||||
void SetDefaultGlobalConfig(void)
|
||||
{
|
||||
@ -803,7 +838,7 @@ void SetDefaultGameConfig(void)
|
||||
myConfig.dpad = DPAD_NORMAL; // Normal DPAD use - mapped to joystick
|
||||
myConfig.autoLoad = 1; // Default is to to auto-load TAP and TZX games
|
||||
myConfig.loadAs = 0; // Default load is 48K
|
||||
myConfig.reserved2 = 0;
|
||||
myConfig.gameSpeed = 0; // Default is 100% game speed
|
||||
myConfig.reserved3 = 0;
|
||||
myConfig.reserved4 = 0;
|
||||
myConfig.reserved5 = 0;
|
||||
@ -891,8 +926,10 @@ const struct options_t Option_Table[2][20] =
|
||||
{"AUTO STOP", {"NO", "YES"}, &myConfig.autoStop, 2},
|
||||
{"AUTO FIRE", {"OFF", "ON"}, &myConfig.autoFire, 2},
|
||||
{"TAPE SPEED", {"NORMAL", "ACCELERATED"}, &myConfig.tapeSpeed, 2},
|
||||
{"GAME SPEED", {"100%", "110%", "120%", "90%", "80%"}, &myConfig.gameSpeed, 5},
|
||||
{"BUS CONTEND", {"NORMAL", "LIGHT", "HEAVY"}, &myConfig.contention, 3},
|
||||
{"NDS D-PAD", {"NORMAL", "DIAGONALS", "CHUCKIE"}, &myConfig.dpad, 3},
|
||||
|
||||
{NULL, {"", ""}, NULL, 1},
|
||||
},
|
||||
// Global Options
|
||||
@ -1058,14 +1095,15 @@ void DisplayKeymapName(u32 uY)
|
||||
u8 keyMapType = 0;
|
||||
void SwapKeymap(void)
|
||||
{
|
||||
keyMapType = (keyMapType+1) % 5;
|
||||
keyMapType = (keyMapType+1) % 6;
|
||||
switch (keyMapType)
|
||||
{
|
||||
case 0: MapPlayer1(); DSPrint(10,3,0,("KEMPSTON P1")); break;
|
||||
case 1: Sinclair1(); DSPrint(10,3,0,("SINCLAIR P1")); break;
|
||||
case 2: MapQAOP(); DSPrint(10,3,0,(" QAOP ")); break;
|
||||
case 3: MapWASD(); DSPrint(10,3,0,(" WASD ")); break;
|
||||
case 4: MapZXSpace(); DSPrint(10,3,0,(" ZX SPACE ")); break;
|
||||
case 2: Cursors(); DSPrint(10,3,0,(" CURSORS ")); break;
|
||||
case 3: MapQAOP(); DSPrint(10,3,0,(" QAOP ")); break;
|
||||
case 4: MapWASD(); DSPrint(10,3,0,(" WASD ")); break;
|
||||
case 5: MapZXSpace(); DSPrint(10,3,0,(" ZX SPACE ")); break;
|
||||
}
|
||||
WAITVBL;WAITVBL;WAITVBL;WAITVBL;
|
||||
DSPrint(10,3,0,(" "));
|
||||
@ -1306,6 +1344,8 @@ void ReadFileCRCAndConfig(void)
|
||||
if (strstr(gpFic[ucGameChoice].szName, ".TZX") != 0) speccy_mode = MODE_TZX;
|
||||
if (strstr(gpFic[ucGameChoice].szName, ".rom") != 0) speccy_mode = MODE_BIOS;
|
||||
if (strstr(gpFic[ucGameChoice].szName, ".ROM") != 0) speccy_mode = MODE_BIOS;
|
||||
if (strstr(gpFic[ucGameChoice].szName, ".z81") != 0) speccy_mode = MODE_ZX81;
|
||||
if (strstr(gpFic[ucGameChoice].szName, ".Z81") != 0) speccy_mode = MODE_ZX81;
|
||||
|
||||
FindConfig(); // Try to find keymap and config for this file...
|
||||
}
|
||||
@ -1589,7 +1629,9 @@ void ProcessBufferedKeys(void)
|
||||
{
|
||||
buf_held = BufferedKeys[BufferedKeysReadIdx];
|
||||
BufferedKeysReadIdx = (BufferedKeysReadIdx+1) % 32;
|
||||
if (buf_held == 255) {buf_held = 0; next_dampen_time=60;} else next_dampen_time = 10;
|
||||
if (buf_held == 255) {buf_held = 0; next_dampen_time=30;}
|
||||
else if (buf_held == 254) {buf_held = 0; next_dampen_time=20;}
|
||||
else next_dampen_time = 10;
|
||||
} else buf_held = 0;
|
||||
dampen = 0;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ struct __attribute__((__packed__)) Config_t
|
||||
u8 dpad;
|
||||
u8 autoLoad;
|
||||
u8 loadAs;
|
||||
u8 reserved2;
|
||||
u8 gameSpeed;
|
||||
u8 reserved3;
|
||||
u8 reserved4;
|
||||
u8 reserved5;
|
||||
|
@ -263,7 +263,7 @@ void zx_bank(u8 new_bank)
|
||||
if (portFD & 0x20) return; // Lock out - no more bank swaps allowed
|
||||
|
||||
// Map in the correct bios segment... make sure this isn't a diagnostic ROM
|
||||
if (speccy_mode != MODE_BIOS)
|
||||
if ((speccy_mode != MODE_BIOS) && (speccy_mode != MODE_ZX81))
|
||||
{
|
||||
MemoryMap[0] = SpectrumBios128 + ((new_bank & 0x10) ? 0x4000 : 0x0000);
|
||||
}
|
||||
@ -670,6 +670,20 @@ void speccy_reset(void)
|
||||
MemoryMap[3] = RAM_Memory128 + (0 * 0x4000) + 0x0000; // Bank 0
|
||||
}
|
||||
}
|
||||
else if (speccy_mode == MODE_ZX81)
|
||||
{
|
||||
// Move the BIOS/Diagnostic ROM into memory...
|
||||
memcpy(RAM_Memory, ROM_Memory, 0x4000); // Load diagnostics ROM into place - the rest of the file is the P-File
|
||||
|
||||
// And force 128K mode needed for ZX81 emulation
|
||||
zx_128k_mode = 1;
|
||||
myConfig.loadAs = 1;
|
||||
|
||||
// Now set the memory map to point to the right banks...
|
||||
MemoryMap[1] = RAM_Memory128 + (5 * 0x4000) + 0x0000; // Bank 5
|
||||
MemoryMap[2] = RAM_Memory128 + (2 * 0x4000) + 0x0000; // Bank 2
|
||||
MemoryMap[3] = RAM_Memory128 + (0 * 0x4000) + 0x0000; // Bank 0
|
||||
}
|
||||
else if (speccy_mode < MODE_SNA) // TAP or TZX file - 48K or 128K
|
||||
{
|
||||
// BIOS will be loaded further below...
|
||||
@ -765,7 +779,7 @@ void speccy_reset(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (speccy_mode != MODE_BIOS)
|
||||
if ((speccy_mode != MODE_BIOS) && (speccy_mode != MODE_ZX81))
|
||||
{
|
||||
// Load the correct BIOS into place... either 48K Spectrum or 128K
|
||||
if (zx_128k_mode) memcpy(RAM_Memory, SpectrumBios128, 0x4000); // Load ZX 128K BIOS into place
|
||||
|
35
readme.md
35
readme.md
@ -13,6 +13,7 @@ Features :
|
||||
* Loads .TZX files up to 640K total length (can swap tapes mid-game)
|
||||
* Loads .SNA snapshots (48K only)
|
||||
* Loads .Z80 snapshots (V1, V2 and V3 formats, 48K or 128K)
|
||||
* Loads .Z81 files for ZX81 emulation (see below)
|
||||
* Loads .ROM files up to 16K in place of standard BIOS (diagnostics, etc)
|
||||
* Supports .POK files (same name as base game and stored in POK subdir)
|
||||
* Kempston and Sinclair joystick support
|
||||
@ -150,7 +151,16 @@ under that as you wish. The emulator can support a file listing of up
|
||||
to 2000 files with names no longer than 160 characters (so please keep
|
||||
your filenames on the shorter side... although the emulator can scroll
|
||||
the filename, there only about 30 characters can be shown on the screen
|
||||
at a time).
|
||||
at a time).
|
||||
|
||||
One option that is of particular note is the ability to run the game
|
||||
at a speed other than normal 100%. Some games were designed to run
|
||||
a bit too fast to be enjoyable. Other games were a bit too slow. Using
|
||||
this optional adjustment, you can run a game anywhere from 80% of full
|
||||
speed (slower than normal) to 120% (faster than normal). The sound driver
|
||||
should auto-adjust and while the music / sounds will sound faster/slower,
|
||||
it should match the core emulation speed perfectly. This can be adjusted
|
||||
on a per-game basis.
|
||||
|
||||
As for the per-game options, you can set things like the auto fire
|
||||
for the joystick and the aforementioned CHUCKIE mode of joystick d-pad
|
||||
@ -192,7 +202,6 @@ an alternate "[a]" version will load. Usually one tape image dump is
|
||||
as good as any other - but keep searching and put yourself together a
|
||||
library of known good working images for Speccy-SE.
|
||||
|
||||
|
||||
ROM Support :
|
||||
-----------------------
|
||||
The emulator allows you to load a .ROM file directly into the same memory
|
||||
@ -200,6 +209,28 @@ location as the BIOS (+0000 to +4000). Only up to 16K can be loaded in this
|
||||
way. This is mainly used to load diagnostic test programs such as the
|
||||
amazing RETROLEUM DIAGROM.
|
||||
|
||||
ZX81 Support :
|
||||
-----------------------
|
||||
The emulator supports the Paul Farrow ZX81 emulator for the ZX 128k machines.
|
||||
|
||||
http://www.fruitcake.plus.com/Sinclair/Interface2/Cartridges/Interface2_RC_New_ZX81.htm
|
||||
|
||||
To make this work, download the 16K Interface 2 ROM for the emulator - either Edition 2
|
||||
or Edition 3 (do not use Edition 1 or the 'bugfix version'). Take this ROM file
|
||||
and concatenate it with a ZX81 .p file for the game you want to play.
|
||||
|
||||
So let's say you want to play the original ZX81 Mazogs. Obtain the mazogs.p file and the
|
||||
aforementioned ZX81 emulator ROM and do the following:
|
||||
|
||||
Linux: cat S128_ZX81_ED2_ROM.bin mazogs.p > mazogs.z81
|
||||
Windows: copy /b S128_ZX81_ED2_ROM.bin mazogs.p mazogs.z81
|
||||
|
||||
|
||||
This will produce a .z81 file that is roughly 25K in size... it contains the emulator + the game .p file
|
||||
in one binary image. This .z81 file is now loadable directly into Speccy-SE - when you pick the game, it
|
||||
will automatically insert the keystrokes needed to get the emulator running. This takes about 10 seconds...
|
||||
don't touch any virtual keys until the ZX81 game is fully loaded.
|
||||
|
||||
POK Support :
|
||||
-----------------------
|
||||
The emulator supports .pok files. The .pok file should have the same base
|
||||
|
Loading…
Reference in New Issue
Block a user