summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan C <ianc@noddybox.co.uk>2021-08-03 20:26:23 +0000
committerIan C <ianc@noddybox.co.uk>2021-08-03 20:26:23 +0000
commitb7e8b634595445325d10f8fcddcb7d6cdaa8a922 (patch)
tree7ecd54e8539cbe1abd37ffda8d3fcd9eb0ddf258
parent30c4591b629bb96e9ff99aca24d0e0e51cedbf23 (diff)
First attempt at sound
-rw-r--r--include/config.h1
-rw-r--r--include/spec.h4
-rw-r--r--include/z80.h17
-rw-r--r--include/z80_private.h10
-rw-r--r--source/config.c9
-rw-r--r--source/main.c9
-rw-r--r--source/spec.c49
-rw-r--r--source/z80.c16
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;