Skip to content

Commit a98e84c

Browse files
committed
Replace SID with SGU-1 Sound Generator Unit 1
1 parent 0b8f1f6 commit a98e84c

11 files changed

Lines changed: 795 additions & 53 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ add_executable(emu
3535
src/chips/pwm.c
3636
src/chips/cgia.c
3737
src/chips/ria816.c
38+
src/chips/sgu1.cc
3839
src/chips/tca6416a.c
3940
src/systems/x65.c
4041
src/x65.c
@@ -47,6 +48,7 @@ add_executable(emu
4748
src/ui/ui_app_log.cc
4849
src/ui/ui_x65.cc
4950
src/util/ringbuffer.c
51+
src/chips/su/su.cpp
5052
${CMAKE_CURRENT_BINARY_DIR}/version.c
5153
)
5254
target_link_libraries(emu

src/chips/sgu1.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include "./sgu1.h"
2+
#include "./su/su.h"
3+
4+
#include <string.h>
5+
#ifdef _MSC_VER
6+
#define _USE_MATH_DEFINES
7+
#endif
8+
#include <math.h> /* tanh */
9+
#ifndef CHIPS_ASSERT
10+
#include <assert.h>
11+
#define CHIPS_ASSERT(c) assert(c)
12+
#endif
13+
14+
/* extract 8-bit data bus from 64-bit pins */
15+
#define SGU1_GET_DATA(p) ((uint8_t)(((p) & 0xFF0000ULL) >> 16))
16+
/* merge 8-bit data bus value into 64-bit pins */
17+
#define SGU1_SET_DATA(p, d) \
18+
{ p = (((p) & ~0xFF0000ULL) | (((d) << 16) & 0xFF0000ULL)); }
19+
/* fixed point precision for sample period */
20+
#define SGU1_FIXEDPOINT_SCALE (16)
21+
/* move bit into first position */
22+
#define SGU1_BIT(val, bitnr) ((val >> bitnr) & 1)
23+
24+
#define SGU_SU ((SoundUnit*)(sgu->su))
25+
26+
void sgu1_init(sgu1_t* sgu, const sgu1_desc_t* desc) {
27+
CHIPS_ASSERT(sid && desc);
28+
CHIPS_ASSERT(desc->tick_hz > 0);
29+
CHIPS_ASSERT(desc->sound_hz > 0);
30+
memset(sgu, 0, sizeof(*sgu));
31+
sgu->sound_hz = desc->sound_hz;
32+
sgu->sample_period = (desc->tick_hz * SGU1_FIXEDPOINT_SCALE) / desc->sound_hz;
33+
sgu->sample_counter = sgu->sample_period;
34+
sgu->sample_mag = desc->magnitude;
35+
sgu->su = new SoundUnit();
36+
SGU_SU->Init();
37+
}
38+
39+
void sgu1_reset(sgu1_t* sgu) {
40+
CHIPS_ASSERT(sid);
41+
SGU_SU->Reset();
42+
sgu->sample_counter = sgu->sample_period;
43+
sgu->sample = 0.0f;
44+
sgu->pins = 0;
45+
}
46+
47+
/* tick the sound generation, return true when new sample ready */
48+
static uint64_t _sgu1_tick(sgu1_t* sgu, uint64_t pins) {
49+
/* new sample? */
50+
sgu->sample_counter -= SGU1_FIXEDPOINT_SCALE;
51+
if (sgu->sample_counter <= 0) {
52+
sgu->sample_counter += sgu->sample_period;
53+
short l, r;
54+
SGU_SU->NextSample(&l, &r);
55+
sgu->sample = (float)(l + r) / 32768.0f * sgu->sample_mag;
56+
pins |= SGU1_SAMPLE;
57+
}
58+
else {
59+
pins &= ~SGU1_SAMPLE;
60+
}
61+
return pins;
62+
}
63+
64+
/* read a register */
65+
static uint64_t _sgu1_read(sgu1_t* sgu, uint64_t pins) {
66+
uint8_t reg = pins & SGU1_ADDR_MASK;
67+
uint8_t data = ((unsigned char*)SGU_SU->chan)[reg];
68+
SGU1_SET_DATA(pins, data);
69+
return pins;
70+
}
71+
72+
/* write a register */
73+
static void _sgu1_write(sgu1_t* sgu, uint64_t pins) {
74+
uint8_t reg = pins & SGU1_ADDR_MASK;
75+
uint8_t data = SGU1_GET_DATA(pins);
76+
((unsigned char*)SGU_SU->chan)[reg] = data;
77+
}
78+
79+
/* the all-in-one tick function */
80+
uint64_t sgu1_tick(sgu1_t* sgu, uint64_t pins) {
81+
CHIPS_ASSERT(sid);
82+
83+
/* first perform the regular per-tick actions */
84+
pins = _sgu1_tick(sgu, pins);
85+
86+
/* register read/write */
87+
if (pins & SGU1_CS) {
88+
if (pins & SGU1_RW) {
89+
pins = _sgu1_read(sgu, pins);
90+
}
91+
else {
92+
_sgu1_write(sgu, pins);
93+
}
94+
}
95+
sgu->pins = pins;
96+
return pins;
97+
}

src/chips/sgu1.h

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#pragma once
2+
/*#
3+
# sgu1.h
4+
5+
SGU-1 Sound Generator Unit 1 emulation
6+
7+
## Emulated Pins
8+
9+
***********************************
10+
* +-----------+ *
11+
* CS --->| |<--- A0 *
12+
* RW --->| |... *
13+
* | |<--- A4 *
14+
* | SGU-1 | *
15+
* | |<--> D0 *
16+
* | |... *
17+
* | |<--> D7 *
18+
* | | *
19+
* +-----------+ *
20+
***********************************
21+
22+
The emulation has an additional "virtual pin" which is set to active
23+
whenever a new sample is ready (SGU1_SAMPLE).
24+
25+
## Links
26+
27+
-
28+
29+
## 0BSD license
30+
31+
Copyright (c) 2025 Tomasz Sterna
32+
33+
Permission to use, copy, modify, and/or distribute this software for any
34+
purpose with or without fee is hereby granted.
35+
36+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
37+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
38+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
39+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
41+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
42+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43+
#*/
44+
45+
#include <stdint.h>
46+
#include <stdbool.h>
47+
48+
#ifdef __cplusplus
49+
extern "C" {
50+
#endif
51+
52+
// address bus pins A0..A5
53+
#define SGU1_PIN_A0 (0)
54+
#define SGU1_PIN_A1 (1)
55+
#define SGU1_PIN_A2 (2)
56+
#define SGU1_PIN_A3 (3)
57+
#define SGU1_PIN_A4 (4)
58+
#define SGU1_PIN_A5 (5)
59+
60+
// data bus pins D0..D7
61+
#define SGU1_PIN_D0 (16)
62+
#define SGU1_PIN_D1 (17)
63+
#define SGU1_PIN_D2 (18)
64+
#define SGU1_PIN_D3 (19)
65+
#define SGU1_PIN_D4 (20)
66+
#define SGU1_PIN_D5 (21)
67+
#define SGU1_PIN_D6 (22)
68+
#define SGU1_PIN_D7 (23)
69+
70+
// shared control pins
71+
#define SGU1_PIN_RW (24) /* same as M6502_RW */
72+
73+
// chip-specific pins
74+
#define SGU1_PIN_CS (40) /* chip-select */
75+
#define SGU1_PIN_SAMPLE (41) /* virtual "audio sample ready" pin */
76+
77+
// pin bit masks
78+
#define SGU1_A0 (1ULL << SGU1_PIN_A0)
79+
#define SGU1_A1 (1ULL << SGU1_PIN_A1)
80+
#define SGU1_A2 (1ULL << SGU1_PIN_A2)
81+
#define SGU1_A3 (1ULL << SGU1_PIN_A3)
82+
#define SGU1_A4 (1ULL << SGU1_PIN_A4)
83+
#define SGU1_A5 (1ULL << SGU1_PIN_A5)
84+
#define SGU1_ADDR_MASK (0x3F)
85+
#define SGU1_D0 (1ULL << SGU1_PIN_D0)
86+
#define SGU1_D1 (1ULL << SGU1_PIN_D1)
87+
#define SGU1_D2 (1ULL << SGU1_PIN_D2)
88+
#define SGU1_D3 (1ULL << SGU1_PIN_D3)
89+
#define SGU1_D4 (1ULL << SGU1_PIN_D4)
90+
#define SGU1_D5 (1ULL << SGU1_PIN_D5)
91+
#define SGU1_D6 (1ULL << SGU1_PIN_D6)
92+
#define SGU1_D7 (1ULL << SGU1_PIN_D7)
93+
#define SGU1_RW (1ULL << SGU1_PIN_RW)
94+
#define SGU1_CS (1ULL << SGU1_PIN_CS)
95+
#define SGU1_SAMPLE (1ULL << SGU1_PIN_SAMPLE)
96+
97+
// channel registers
98+
#define SGU1_CHAN_FREQ_LO (0)
99+
#define SGU1_CHAN_FREQ_HI (1)
100+
#define SGU1_CHAN_VOL (2)
101+
#define SGU1_CHAN_PAN (3)
102+
#define SGU1_CHAN_FLAGS0 (4)
103+
#define SGU1_CHAN_FLAGS1 (5)
104+
#define SGU1_CHAN_CUTOFF_LO (6)
105+
#define SGU1_CHAN_CUTOFF_HI (7)
106+
#define SGU1_CHAN_DUTY (8)
107+
#define SGU1_CHAN_RESON (9)
108+
#define SGU1_CHAN_PCMPOS_LO (10)
109+
#define SGU1_CHAN_PCMPOS_HI (11)
110+
#define SGU1_CHAN_PCMBND_LO (12)
111+
#define SGU1_CHAN_PCMBND_HI (13)
112+
#define SGU1_CHAN_PCMRST_LO (14)
113+
#define SGU1_CHAN_PCMRST_HI (15)
114+
#define SGU1_CHAN_SWFREQ_SPEED_LO (16)
115+
#define SGU1_CHAN_SWFREQ_SPEED_HI (17)
116+
#define SGU1_CHAN_SWFREQ_AMT (18)
117+
#define SGU1_CHAN_SWFREQ_BOUND (19)
118+
#define SGU1_CHAN_SWVOL_SPEED_LO (20)
119+
#define SGU1_CHAN_SWVOL_SPEED_HI (21)
120+
#define SGU1_CHAN_SWVOL_AMT (22)
121+
#define SGU1_CHAN_SWVOL_BOUND (23)
122+
#define SGU1_CHAN_SWCUT_SPEED_LO (24)
123+
#define SGU1_CHAN_SWCUT_SPEED_HI (25)
124+
#define SGU1_CHAN_SWCUT_AMT (26)
125+
#define SGU1_CHAN_SWCUT_BOUND (27)
126+
#define SGU1_CHAN_SPECIAL1C (28)
127+
#define SGU1_CHAN_SPECIAL1D (29)
128+
#define SGU1_CHAN_RESTIMER_LO (20)
129+
#define SGU1_CHAN_RESTIMER_HI (31)
130+
131+
// setup parameters for sgu1_init()
132+
typedef struct {
133+
int tick_hz; // frequency at which sgu1_tick() will be called in Hz
134+
int sound_hz; // sound sample frequency
135+
float magnitude; // output sample magnitude (0=silence to 1=max volume)
136+
} sgu1_desc_t;
137+
138+
// tsu instance state
139+
typedef struct {
140+
int sound_hz;
141+
// sound unit instance
142+
void* su;
143+
// sample generation state
144+
int sample_period;
145+
int sample_counter;
146+
float sample_mag;
147+
float sample;
148+
// debug inspection
149+
uint64_t pins;
150+
} sgu1_t;
151+
152+
// initialize a new sgu1_t instance
153+
void sgu1_init(sgu1_t* sgu, const sgu1_desc_t* desc);
154+
// reset a sgu1_t instance
155+
void sgu1_reset(sgu1_t* sgu);
156+
// tick a sgu1_t instance
157+
uint64_t sgu1_tick(sgu1_t* sgu, uint64_t pins);
158+
159+
#ifdef __cplusplus
160+
} // extern "C"
161+
#endif

0 commit comments

Comments
 (0)