From b7e8b634595445325d10f8fcddcb7d6cdaa8a922 Mon Sep 17 00:00:00 2001 From: Ian C Date: Tue, 3 Aug 2021 20:26:23 +0000 Subject: First attempt at sound --- include/config.h | 1 + include/spec.h | 4 ++++ include/z80.h | 17 ++++++++++++++++- include/z80_private.h | 10 +++++++++- source/config.c | 9 +++++++-- source/main.c | 9 +++++++++ source/spec.c | 49 +++++++++++++++++++++++++++++++++++++++++++++---- source/z80.c | 16 ++++++++++++++++ 8 files changed, 107 insertions(+), 8 deletions(-) diff --git a/include/config.h b/include/config.h index b57c3ef..800960b 100644 --- a/include/config.h +++ b/include/config.h @@ -29,6 +29,7 @@ typedef enum { DSSPEC_STICKY_SHIFT, DSSPEC_LOAD_DEFAULT_SNAPSHOT, + DSSPEC_SOUND, DSSPEC_NUM_CONFIG_ITEMS } DSSPEC_ConfigItem; diff --git a/include/spec.h b/include/spec.h index 47549a3..85fb5e7 100644 --- a/include/spec.h +++ b/include/spec.h @@ -32,6 +32,10 @@ #include "z80.h" #include "keyboard.h" +/* Sample rate for sound emulation +*/ +#define SAMPLE_RATE 22050 + /* Initialise the SPEC */ diff --git a/include/z80.h b/include/z80.h index ef24fd0..ab095d8 100644 --- a/include/z80.h +++ b/include/z80.h @@ -134,13 +134,23 @@ typedef int (*Z80Callback)(Z80 *cpu, Z80Val data); */ typedef enum { - eZ80_Instruction, /* data = no cycles since reset */ + eZ80_Instruction, /* data = cycles as returned by Z80Cycles */ eZ80_EDHook, /* data = byte after ED opcode (only for NOP opcodes) */ eZ80_Halt, /* data = 1 halt raised, 0 halt cleared by int */ eZ80_RETI, /* data = ignored */ eZ80_NO_CALLBACK /* leave at end */ } Z80CallbackReason; +/* Defines cycle timers +*/ +typedef enum +{ + Z80_TIMER_1, + Z80_TIMER_2, + Z80_TIMER_3, + Z80_NO_TIMERS +} Z80Timer; + /* Flags in the F register */ @@ -235,6 +245,11 @@ void Z80Exec(Z80 *cpu); Z80Val Z80Cycles(Z80 *cpu); void Z80ResetCycles(Z80 *cpu, Z80Val cycles); +/* Timers that count in cycle counts +*/ +Z80Val Z80GetTimer(Z80 *cpu, Z80Timer timer); +void Z80SetTimer(Z80 *cpu, Z80Timer timer, Z80Val cycles); + /* Set address to label mappings for the disassembler */ diff --git a/include/z80_private.h b/include/z80_private.h index 10cf97e..e0b3381 100644 --- a/include/z80_private.h +++ b/include/z80_private.h @@ -48,6 +48,8 @@ struct Z80Private { Z80Val cycle; + Z80Val timer[Z80_NO_TIMERS]; + int halt; Z80Byte shift; @@ -178,7 +180,13 @@ static inline Z80Word PEEKW(Z80Word addr) #define IS_IX_IY (PRIV->shift==0xdd || PRIV->shift==0xfd) #define OFFSET(off) off=(IS_IX_IY ? (Z80Relative)FETCH_BYTE:0) -#define TSTATE(n) PRIV->cycle+=n +#define TSTATE(n) do \ + { \ + PRIV->cycle+=n; \ + PRIV->timer[Z80_TIMER_1]+=n; \ + PRIV->timer[Z80_TIMER_2]+=n; \ + PRIV->timer[Z80_TIMER_3]+=n; \ + } while(0) #define ADD_R(v) cpu->R=((cpu->R&0x80)|((cpu->R+(v))&0x7f)) #define INC_R ADD_R(1) diff --git a/source/config.c b/source/config.c index 8198290..b16f1b3 100644 --- a/source/config.c +++ b/source/config.c @@ -40,7 +40,8 @@ const char *conf_filename = "SPEC48.CFG"; const char *conf_entry[DSSPEC_NUM_CONFIG_ITEMS]= { "sticky_shift", - "load_default_snapshot" + "load_default_snapshot", + "sound" }; @@ -49,7 +50,8 @@ const char *conf_entry[DSSPEC_NUM_CONFIG_ITEMS]= int DSSPEC_Config[DSSPEC_NUM_CONFIG_ITEMS]= { TRUE, - FALSE + FALSE, + TRUE }; @@ -126,6 +128,9 @@ const char *ConfigDesc(DSSPEC_ConfigItem item) case DSSPEC_LOAD_DEFAULT_SNAPSHOT: return "LOAD DEFAULT SNAPSHOT"; + case DSSPEC_SOUND: + return "SOUND"; + default: return "UNKNOWN"; } diff --git a/source/main.c b/source/main.c index 46eccfe..b7d3318 100644 --- a/source/main.c +++ b/source/main.c @@ -235,8 +235,16 @@ int main(int argc, char *argv[]) { Z80 *z80; int quit = FALSE; + float mix[12] = {1.0, 1.0}; gfxInit(GSP_RGB565_OES, GSP_RGB565_OES, FALSE); + ndspInit(); + + ndspSetOutputMode(NDSP_OUTPUT_MONO); + ndspChnSetInterp(0, NDSP_INTERP_NONE); + ndspChnSetRate(0, SAMPLE_RATE); + ndspChnSetFormat(0, NDSP_FORMAT_MONO_PCM8); + ndspChnSetMix(0, mix); FB_Init(); @@ -352,6 +360,7 @@ int main(int argc, char *argv[]) } } + ndspExit(); gfxExit(); return 0; diff --git a/source/spec.c b/source/spec.c index 6cdc7c6..ef132d4 100644 --- a/source/spec.c +++ b/source/spec.c @@ -59,6 +59,7 @@ #define ED_LOAD 0xf1 #define SCAN_CYCLES 224 +#define FRAME_CYCLES 69888 /* The 3DS screen */ @@ -153,6 +154,17 @@ static struct {7,0x10}, {7,0x08}, {7,0x04}, {7,0x02}, {7,0x01} /* B - SPACE */ }; +/* Sound +*/ +#define SAMPLE_BUFF_SZ (SAMPLE_RATE/60) + +#define SAMPLE_CYCLE (FRAME_CYCLES/SAMPLE_BUFF_SZ) + +static u8 *sample[2]; +static int sound_buffer; +static u8 sound_level; +static int sound_ptr; +static ndspWaveBuf wave_buff[2]; /* ---------------------------------------- PRIVATE FUNCTIONS */ @@ -160,7 +172,7 @@ static void RomPatch(void) { static const Z80Byte save[]= { - 0xed, ED_SAVE, /* (SAVE) */ + 0xed, ED_SAVE, /* (SAVE) */ 0xc9, /* RET */ 0xff /* End of patch */ }; @@ -263,6 +275,13 @@ static int CheckTimers(Z80 *z80, Z80Val val) { int ret = TRUE; + if (DSSPEC_Config[DSSPEC_SOUND] && + Z80GetTimer(z80, Z80_TIMER_1) >= SAMPLE_CYCLE) + { + Z80SetTimer(z80, Z80_TIMER_1, 0); + sample[sound_buffer][sound_ptr++] = sound_level; + } + if (val > SCAN_CYCLES) { int y; @@ -287,6 +306,15 @@ static int CheckTimers(Z80 *z80, Z80Val val) Z80Interrupt(z80,0xff); + if (DSSPEC_Config[DSSPEC_SOUND]) + { + ndspChnWaveBufAdd(0, wave_buff + sound_buffer); + sound_buffer = !sound_buffer; + memset(sample[sound_buffer], 0, SAMPLE_BUFF_SZ); + sound_ptr = 0; + Z80SetTimer(z80, Z80_TIMER_1, 0); + } + ret = FALSE; } @@ -298,8 +326,6 @@ static int CheckTimers(Z80 *z80, Z80Val val) { DrawScanline(y, scanline); } - - /* TODO: Process sound emulation */ } return ret; @@ -361,6 +387,18 @@ void SPECInit(Z80 *z80) Z80LodgeCallback(z80,eZ80_EDHook,EDCallback); Z80LodgeCallback(z80,eZ80_Instruction,CheckTimers); + /* Create sound buffers and wave vars + */ + sample[0] = linearAlloc(SAMPLE_BUFF_SZ); + sample[1] = linearAlloc(SAMPLE_BUFF_SZ); + + wave_buff[0].data_vaddr = sample[0]; + wave_buff[0].nsamples = SAMPLE_BUFF_SZ; + wave_buff[1].data_vaddr = sample[1]; + wave_buff[1].nsamples = SAMPLE_BUFF_SZ; + + sound_buffer = 0; + SPECReset(z80); } @@ -465,6 +503,7 @@ void SPECWritePort(Z80 *z80, Z80Word port, Z80Byte val) { case 0xfe: /* ULA */ border = val & 0x07; + sound_level = val & 0x10 ? 128:0; break; case 0xfb: /* ZX Printer */ @@ -486,11 +525,13 @@ void SPECReset(Z80 *z80) matrix[f]=0x1f; Z80Reset(z80); - Z80ResetCycles(z80,0); border = 0; scanline = 0; + sound_buffer = 0; + sound_ptr = 0; + /* Set up screen */ c=0; diff --git a/source/z80.c b/source/z80.c index e5f6c92..790aeec 100644 --- a/source/z80.c +++ b/source/z80.c @@ -170,6 +170,10 @@ Z80 *Z80Init(Z80ReadMemory read_memory, void Z80Reset(Z80 *cpu) { PRIV->cycle=0; + PRIV->timer[Z80_TIMER_1]=0; + PRIV->timer[Z80_TIMER_2]=0; + PRIV->timer[Z80_TIMER_3]=0; + cpu->PC=0; cpu->AF.w=0xffff; @@ -209,6 +213,18 @@ void Z80ResetCycles(Z80 *cpu, Z80Val cycles) } +Z80Val Z80GetTimer(Z80 *cpu, Z80Timer timer) +{ + return PRIV->timer[timer]; +} + + +void Z80SetTimer(Z80 *cpu, Z80Timer timer, Z80Val cycles) +{ + PRIV->timer[timer] = cycles; +} + + int Z80LodgeCallback(Z80 *cpu, Z80CallbackReason reason, Z80Callback callback) { int f; -- cgit v1.2.3