shithub: sms

Download patch

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){
--