ref: 26224f28352b47c8fd9f15670caf0caf277f23e5
parent: aeec4603581dd0004fdb853f1899c02093f10fde
author: Jean-André Santoni <jean.andre.santoni@gmail.com>
date: Sat Jul 5 12:29:02 EDT 2025
Add PSG emulation
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
CFLAGS += -g -O3 -fPIC -flto
LDFLAGS += -flto
-OBJ = sms.o z80.o mem.o vdp.o
+OBJ = sms.o z80.o mem.o vdp.o psg.o
.DEFAULT_GOAL := $(TARGET)
--- a/dat.h
+++ b/dat.h
@@ -52,12 +52,11 @@
enum {FREQ = 53203400,
- YMDIV = 7 * 6,
- CPUDIV = 7,
Z80DIV = 15,
RATE = 44100,
- SAMPDIV = FREQ / RATE,
SAVEFREQ = FREQ / 4,
+ PSGCLOCK = 3579545,
+ PSGDIV = 16,
};
enum {--- a/fns.h
+++ b/fns.h
@@ -8,7 +8,6 @@
uint8_t z80in(uint8_t);
void z80out(uint8_t, uint8_t);
void flush(void);
-void audioout(void);
void vdpmode(void);
void vdpctrl(uint8_t);
void vdpdata(uint8_t);
@@ -18,3 +17,6 @@
uint8_t vdphcounter(void);
uint8_t vdpvcounter(void);
void cramwrite(uint16_t, uint8_t);
+void psginit(const uint16_t, const int);
+void psgwrite(const uint8_t);
+uint16_t psgstep();
--- a/mem.c
+++ b/mem.c
@@ -157,6 +157,7 @@
}
}else if ((port >= 0x40) && (port < 0x80)){ // printf(" write to SN76489 PSG\n");+ psgwrite(v);
}else if ((port >= 0x80) && (port < 0xC0)){ // printf(" write to VDP\n");if ((port & 0x01) == 0x00)
--- /dev/null
+++ b/psg.c
@@ -1,0 +1,96 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "dat.h"
+
+#define NOISE_TAPPED 0x9
+
+static const uint8_t noise_table[3] = { 0x10, 0x20, 0x40 };+static const short vol_table[16] = {+ 8191, 6507, 5168, 4105, 3261, 2590, 2057,
+ 1642, 1298, 1031, 819, 650, 516, 410, 326, 0
+};
+
+static double cyclespersample;
+static double cycles;
+static uint16_t freqreg[4];
+static uint16_t countreg[4];
+static uint8_t attn[4];
+static bool flipflop[4];
+static uint16_t noise;
+static uint8_t curr_reg;
+static uint8_t curr_type;
+
+void
+psginit(const uint16_t rate, const int clockspd)
+{+ cyclespersample = (double)clockspd / (double)PSGDIV/ (double)rate;
+ cycles = cyclespersample;
+}
+
+void
+psgwrite(const uint8_t data)
+{+ bool first = data & 128;
+ if(first){+ curr_reg = (data >> 5) & 3;
+ curr_type = (data >> 4) & 1;
+ }
+
+ if(curr_type){+ attn[curr_reg] = data & 0x0F;
+ }else if(first && curr_reg == 3){+ freqreg[3] = data & 7;
+ noise = 0x8000;
+ }
+ else if(first)
+ freqreg[curr_reg] = (freqreg[curr_reg] & 0x3F0) | (data & 0x0F);
+ else
+ countreg[curr_reg] = freqreg[curr_reg] = (freqreg[curr_reg] & 0x0F) | ((data & 0x3F) << 4);
+}
+
+static inline uint16_t
+parity(uint16_t v)
+{+ v ^= v >> 8;
+ v ^= v >> 4;
+ v ^= v >> 2;
+ v ^= v >> 1;
+ v &= 1;
+ return v;
+}
+
+static inline uint16_t
+vol(uint8_t chn)
+{+ return (flipflop[chn] ? 1 : -1) * vol_table[attn[chn]];
+}
+
+int16_t
+psgstep()
+{+ while(cycles > 0){+ for(uint8_t i = 0; i < 4; i++){+ countreg[i]--;
+ if(!countreg[i]){+ if(i < 3){+ countreg[i] = freqreg[i];
+ flipflop[i] = !flipflop[i];
+ }else{+ uint8_t nf = freqreg[3] & 3;
+ uint8_t fb = (freqreg[3] >> 2) & 1;
+ countreg[3] = nf == 3 ? freqreg[2] : (0x10 << nf);
+
+ noise = (noise >> 1) | ((fb ? parity(noise & NOISE_TAPPED) : noise & 1) << 15);
+ flipflop[3] = (noise & 1);
+ }
+ }
+ }
+
+ cycles--;
+ }
+
+ cycles += cyclespersample;
+
+ return vol(0) + vol(1) + vol(2) + vol(3);
+}
--- a/sms.c
+++ b/sms.c
@@ -1,4 +1,5 @@
#include <fcntl.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -10,7 +11,7 @@
static retro_input_poll_t input_poll_cb;
static retro_video_refresh_t video_cb;
static retro_environment_t environ_cb;
-retro_audio_sample_batch_t audio_cb;
+retro_audio_sample_t audio_cb;
int t = 0;
uint32_t r[16];
@@ -21,6 +22,7 @@
uint8_t *mem = NULL;
uint8_t *pic = NULL;
int vdpclock = 0;
+int psgclock = 0;
uint8_t keys[2];
void
@@ -53,7 +55,7 @@
retro_get_system_av_info(struct retro_system_av_info *info)
{info->timing.fps = 60.0;
- info->timing.sample_rate = 48000;
+ info->timing.sample_rate = RATE;
info->geometry.base_width = 320;
info->geometry.base_height = 224;
@@ -77,6 +79,7 @@
return false;
loadrom(game->data);
+ psginit(RATE, PSGCLOCK);
vdpmode();
return true;
}
@@ -108,6 +111,7 @@
int counter = 0;
int total = 0;
+int samplescounter = 0;
void
retro_run(void)
@@ -143,7 +147,15 @@
video_cb(pic, 256, 192, 320*4);
- audioout();
+ for(int i = 0; i < 736; i++){+ int16_t frame = psgstep();
+ audio_cb(frame, frame);
+ samplescounter++;
+ }
+
+ //printf("Samples: %d\n", samplescounter);+ samplescounter = 0;
+
doflush = 0;
total = 0;
}
@@ -154,15 +166,7 @@
doflush = 1;
}
-static int16_t samples[736 * 2 * 2] = {0};-
void
-audioout(void)
-{- audio_cb(samples, 736);
-}
-
-void
retro_set_input_poll(retro_input_poll_t cb)
{input_poll_cb = cb;
@@ -187,7 +191,7 @@
}
void
-retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
+retro_set_audio_sample(retro_audio_sample_t cb)
{audio_cb = cb;
}
@@ -221,7 +225,7 @@
void * retro_get_memory_data(unsigned id) { return NULL; } void retro_unload_game(void) {} void retro_deinit(void) {}-void retro_set_audio_sample(retro_audio_sample_t cb) {}+void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {} void retro_cheat_reset(void) {} void retro_cheat_set(unsigned index, bool enabled, const char *code) {} bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) { return false; }--- a/z80.c
+++ b/z80.c
@@ -676,7 +676,7 @@
return 1;
}
if(z80irq != 0 && (intm & 0x80) != 0){- printf("irq\n");+ //printf("irq\n");push16(pc);
intm &= 0x3f;
switch(intm & 3){--
⑨