OXYGENHUB
# 8e8d9cad1884 x16-emulator / src / vera_psg.c
2.9 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
03a3459e Andy McCall 1
// Commander X16 Emulator
2
// Copyright (c) 2020 Frank van den Hoef
3
// All rights reserved. License: 2-clause BSD
4
5
#include "vera_psg.h"
6
7
#include <stdbool.h>
8
#include <string.h>
9
10
enum waveform {
11
	WF_PULSE = 0,
12
	WF_SAWTOOTH,
13
	WF_TRIANGLE,
14
	WF_NOISE,
15
};
16
17
struct channel {
18
	uint16_t freq;
19
	uint16_t volume;
20
	bool     left, right;
21
	uint8_t  pw;
22
	uint8_t  waveform;
23
24
	uint16_t noiseval;
25
	uint32_t phase;
26
};
27
28
static struct channel channels[16];
29
30
static uint16_t volume_lut[64] = {
31
	  0,                                           4,   8,  12,
32
	 16,  17,  18,  20,  21,  22,  23,  25,  26,  28,  30,  31,
33
	 33,  35,  37,  40,  42,  45,  47,  50,  53,  56,  60,  63,
34
	 67,  71,  75,  80,  85,  90,  95, 101, 107, 113, 120, 127,
35
	135, 143, 151, 160, 170, 180, 191, 202, 214, 227, 241, 255,
36
	270, 286, 303, 321, 341, 361, 382, 405, 429, 455, 482, 511
37
};
38
39
static uint16_t noise_state;
40
41
void
42
psg_reset(void)
43
{
44
	memset(channels, 0, sizeof(channels));
45
	noise_state = 1;
46
}
47
48
void
49
psg_writereg(uint8_t reg, uint8_t val)
50
{
51
	reg &= 0x3f;
52
53
	int ch  = reg / 4;
54
	int idx = reg & 3;
55
56
	switch (idx) {
57
		case 0: channels[ch].freq = (channels[ch].freq & 0xFF00) | val; break;
58
		case 1: channels[ch].freq = (channels[ch].freq & 0x00FF) | (val << 8); break;
59
		case 2: {
60
			channels[ch].right  = (val & 0x80) != 0;
61
			channels[ch].left   = (val & 0x40) != 0;
62
			channels[ch].volume = volume_lut[val & 0x3F];
63
			break;
64
		}
65
		case 3: {
66
			channels[ch].pw       = val & 0x3F;
67
			channels[ch].waveform = val >> 6;
68
			break;
69
		}
70
	}
71
}
72
73
static void
74
render(int16_t *left, int16_t *right)
75
{
76
	int16_t l = 0;
77
	int16_t r = 0;
78
79
	for (int i = 0; i < 16; i++) {
80
		// In FPGA implementation, noise values are generated every system clock and
81
		// the channel update is run sequentially. So, even if both two channels are
82
		// fetching a noise value in the same sample, they should have different values
83
		noise_state = (noise_state << 1) | (((noise_state >> 1) ^ (noise_state >> 2) ^ (noise_state >> 4) ^ (noise_state >> 15)) & 1);
84
85
		struct channel *ch = &channels[i];
86
87
		uint32_t new_phase = (ch->left || ch->right) ? ((ch->phase + ch->freq) & 0x1FFFF) : 0;
88
		if ((ch->phase & 0x10000) && !(new_phase & 0x10000)) {
89
			ch->noiseval = (noise_state >> 1) & 0x3F;
90
		}
91
		ch->phase = new_phase;
92
93
		uint32_t v = 0;
94
		switch (ch->waveform) {
95
			case WF_PULSE: v = ((ch->phase >> 10) > ch->pw) ? 0 : 0x3F; break;
96
    		case WF_SAWTOOTH: v = (ch->phase >> 11) ^ ((ch->pw ^ 0x3f) & 0x3f); break;
97
			case WF_TRIANGLE: v = ((ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F)) ^ ((ch->pw ^ 0x3f) & 0x3f); break;		
98
			case WF_NOISE: v = ch->noiseval; break;
99
		}
100
		int16_t sv = (v ^ 0x20);
101
		if (sv & 0x20) {
102
			sv |= 0xFFC0;
103
		}
104
105
		int16_t val = sv * ch->volume;
106
107
		if (ch->left) {
108
			l += val >> 3;
109
		}
110
		if (ch->right) {
111
			r += val >> 3;
112
		}
113
	}
114
115
	*left  = l;
116
	*right = r;
117
}
118
119
void
120
psg_render(int16_t *buf, unsigned num_samples)
121
{
122
	while (num_samples--) {
123
		render(&buf[0], &buf[1]);
124
		buf += 2;
125
	}
126
}