From c527acfc79f3bda8cfe13a7b51b0b16a32683960 Mon Sep 17 00:00:00 2001 From: Ian C Date: Mon, 29 Jun 2026 09:41:42 +0900 Subject: First partial working attempt at sound. Producing output, but very distorted --- src/Makefile | 4 +- src/audio.c | 30 ++++++++++++-- src/audio.h | 10 ++++- src/spec.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++- src/z80.c | 19 +++++++++ src/z80.h | 16 ++++++++ src/z80_private.h | 10 ++++- 7 files changed, 194 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 15db934..37e1d26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -85,7 +85,7 @@ clean: main.o: z80.h spec.h gfx.h gui.h memmenu.h config.h kbbmp.h exit.h tape.h audio.h main.o: util.h -spec.o: spec.h z80.h snap.h gfx.h gui.h config.h exit.h tape.h util.h +spec.o: spec.h z80.h snap.h gfx.h gui.h config.h exit.h tape.h util.h audio.h snap.o: snap.h z80.h util.h config.o: exit.h config.h gfx.o: gfx.h exit.h config.h util.h font.h @@ -99,4 +99,4 @@ symtochar.o: symtochar.h font.o: font.h z80.o: z80.h z80_private.h z80_decode.o: z80.h z80_private.h -audio.o: audio.c audio.h +audio.o: audio.c audio.h util.h diff --git a/src/audio.c b/src/audio.c index b5ce1df..7eef433 100644 --- a/src/audio.c +++ b/src/audio.c @@ -27,17 +27,41 @@ #include #include "audio.h" +#include "util.h" -/* ---------------------------------------- INTERFACES +/* ---------------------------------------- INTERNAL DATA */ +static SDL_AudioSpec actual; +static SDL_AudioDeviceID device; + +/* ---------------------------------------- INTERFACES +*/ int AUDIOInit(void) { - return 0; + SDL_AudioSpec spec = {0}; + + spec.freq = SAMPLE_RATE; + spec.format = AUDIO_S8; + spec.channels = 1; + spec.samples = 4096; + + device = SDL_OpenAudioDevice(NULL, 0, &spec, &actual, 0); + + if (device != 0) + { + SDL_PauseAudioDevice(device, 0); + } + + return device != 0; } -void AUDIOQueue(char *buffer, size_t len) +void AUDIOQueue(Uint8 *buffer, size_t len) { + if (device != 0) + { + SDL_QueueAudio(device, buffer, len); + } } diff --git a/src/audio.h b/src/audio.h index 83980e4..b799abd 100644 --- a/src/audio.h +++ b/src/audio.h @@ -28,6 +28,11 @@ #define ESPEC_AUDIO_H #include +#include + +/* The sample rate +*/ +#define SAMPLE_RATE 48000 /* ---------------------------------------- INTERFACES */ @@ -39,10 +44,11 @@ int AUDIOInit(void); -/* Add the passed buffer to the sound queue. It is a signed 8-bit mono +/* Add the passed buffer to the sound queue. It is an unsigned 8-bit mono sample which will be converted as appropriate by the audio interface. + Well turns out SDL does that for you. */ -void AUDIOQueue(char *buffer, size_t len); +void AUDIOQueue(Uint8 *buffer, size_t len); #endif diff --git a/src/spec.c b/src/spec.c index 2ddf661..6393ef2 100644 --- a/src/spec.c +++ b/src/spec.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "spec.h" #include "snap.h" #include "gfx.h" @@ -33,6 +34,7 @@ #include "config.h" #include "exit.h" #include "tape.h" +#include "audio.h" #include "util.h" #ifndef TRUE @@ -77,7 +79,9 @@ static Z80Byte mem[0x10000]; /* Number of cycles per scan lines and scan line control */ -static Z80Val SCAN_CYCLES=224; +#define SCAN_CYCLES 224 +#define FRAME_CYCLES 69888 + static int scanline=0; static int enable_screen=TRUE; @@ -202,6 +206,22 @@ static const MatrixMap keymap[]= {SDLK_UNKNOWN, 0,0,0,0}, }; +static size_t sample_buff_sz; +static double cycles_per_sample; + +typedef struct SoundRecord +{ + Z80Val cycle; + Uint8 level; + struct SoundRecord *next; +} SoundRecord; + +static struct +{ + SoundRecord *head; + SoundRecord *tail; + Uint8 last_level; +} sound; /* ---------------------------------------- DEBUG FUNCTIONS */ @@ -251,6 +271,39 @@ static void ClearKeys(void) } } +static void AddSoundRecord(Z80Val cycle, Uint8 level) +{ + SoundRecord *new = Malloc(sizeof *new); + + new->cycle = cycle; + new->level = level; + new->next = NULL; + + if (sound.tail) + { + sound.tail->next = new; + sound.tail = new; + } + else + { + sound.head = new; + sound.tail = new; + } +} + +static void ClearSoundRecord(void) +{ + while(sound.head) + { + SoundRecord *next = sound.head->next; + free(sound.head); + sound.head = next; + } + + sound.tail = NULL; +} + + static void DrawScanlineAt(int y, int sline) { int aline; @@ -485,6 +538,55 @@ static int CheckTimers(Z80 *z80, Z80Val val) { selected_out_tape--; } + + if (IConfig(CONF_SOUND)) + { + Uint8 *sample = Malloc(sample_buff_sz); + SoundRecord *current = sound.head; + int offset = 0; + + memset(sample, 0, sample_buff_sz); + + if (current) + { + int f; + int first_offset = (int)(current->cycle / cycles_per_sample); + + for(f = 0; f < first_offset; f++) + { + sample[f] = sound.last_level; + } + + while(current) + { + int next_offset; + + if (current->next) + { + next_offset = (int)(current->cycle / cycles_per_sample); + } + else + { + next_offset = sample_buff_sz; + } + + for(f = offset; f < next_offset; f++) + { + sample[f] = current->level; + } + + offset = next_offset; + sound.last_level = current->level; + current = current->next; + } + } + + AUDIOQueue(sample, sample_buff_sz); + + free(sample); + ClearSoundRecord(); + } + } /* Draw scanline @@ -494,7 +596,7 @@ static int CheckTimers(Z80 *z80, Z80Val val) if (y>=0 && ycycle=0; cpu->PC=0; + cpu->timer[eZ80_Timer_1] = 0; + cpu->timer[eZ80_Timer_2] = 0; + cpu->timer[eZ80_Timer_3] = 0; + cpu->AF.w=0xffff; cpu->BC.w=0xffff; cpu->DE.w=0xffff; @@ -362,4 +368,17 @@ const char *Z80Disassemble(Z80 *cpu, Z80Word *pc) #endif } + +Z80Val Z80GetTimer(Z80 *cpu, Z80Timer timer) +{ + return cpu->timer[timer]; +} + + +void Z80SetTimer(Z80 *cpu, Z80Timer timer, Z80Val value) +{ + cpu->timer[timer] = value; +} + + /* END OF FILE */ diff --git a/src/z80.h b/src/z80.h index 66385e7..601127d 100644 --- a/src/z80.h +++ b/src/z80.h @@ -151,6 +151,16 @@ typedef struct } Z80Label; +/* The Z80 provides a number of cycle timers +*/ +typedef enum +{ + eZ80_Timer_1, + eZ80_Timer_2, + eZ80_Timer_3 +} Z80Timer; + + /* ---------------------------------------- INTERFACES */ @@ -230,6 +240,12 @@ void Z80GetState(Z80 *cpu, Z80State *state); void Z80SetState(Z80 *cpu, const Z80State *state); +/* 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 */ void Z80SetLabels(Z80Label labels[]); diff --git a/src/z80_private.h b/src/z80_private.h index 628e23d..b015d6f 100644 --- a/src/z80_private.h +++ b/src/z80_private.h @@ -96,6 +96,8 @@ struct Z80 Z80Callback callback[eZ80_NO_CALLBACK][MAX_PER_CALLBACK]; int last_cb; + + Z80Val timer[3]; }; @@ -159,7 +161,13 @@ struct Z80 #define IS_IX_IY (cpu->shift==0xdd || cpu->shift==0xfd) #define OFFSET(off) off=(IS_IX_IY ? (Z80Relative)FETCH_BYTE:0) -#define TSTATE(n) cpu->cycle+=n +#define TSTATE(n) do \ + { \ + cpu->cycle+=n; \ + cpu->timer[eZ80_Timer_1]+=n; \ + cpu->timer[eZ80_Timer_2]+=n; \ + cpu->timer[eZ80_Timer_3]+=n; \ + } while(0) #define ADD_R(v) cpu->R=((cpu->R&0x80)|((cpu->R+(v))&0x7f)) #define INC_R ADD_R(1) -- cgit v1.3