Mercurial > repos > blastem
comparison rf5c164.c @ 2082:485834c0fea7
Forgot to add PCM source files
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 03 Feb 2022 23:41:40 -0800 |
parents | |
children | b0dcf5c9f353 |
comparison
equal
deleted
inserted
replaced
2081:cfd53c94fffb | 2082:485834c0fea7 |
---|---|
1 #include <stdio.h> | |
2 #include "rf5c164.h" | |
3 | |
4 enum { | |
5 ENV, | |
6 PAN, | |
7 FDL, | |
8 FDH, | |
9 LSL, | |
10 LSH, | |
11 ST, | |
12 CTRL, | |
13 CHAN_ENABLE | |
14 }; | |
15 | |
16 enum { | |
17 START, | |
18 NORMAL, | |
19 LOOP | |
20 }; | |
21 | |
22 #define FLAG_SOUNDING 0x80 | |
23 #define FLAG_PENDING 0x01 | |
24 | |
25 void rf5c164_init(rf5c164* pcm, uint32_t mclks, uint32_t divider) | |
26 { | |
27 pcm->audio = render_audio_source("rf5c164", mclks, divider * 384, 2); | |
28 pcm->clock_step = divider * 4; | |
29 } | |
30 | |
31 //48 cycles per channel | |
32 //1 external write per 16 cycles | |
33 //3 external writes per channel/sample | |
34 // TCE pulse width is 200ns @ 10Mhz aka 2 cycles | |
35 // total cycle is longer than TCE pulse, guessing 4 cycles | |
36 // 12 memory access slots per channel sample | |
37 // 3 for external memory writes | |
38 // 1 for internal register writes? | |
39 // 1 for refresh? | |
40 // 6 for register reads? | |
41 // or maybe 7 for register reads and writes happen when register is read? | |
42 | |
43 #define CHECK pcm->cycle += pcm->clock_step; pcm->step++; if (pcm->cycle >= cycle) break | |
44 #define CHECK_LOOP pcm->cycle += pcm->clock_step; pcm->step = 0; if (pcm->cycle >= cycle) break | |
45 | |
46 static void write_if_not_sounding(rf5c164* pcm) | |
47 { | |
48 if (!(pcm->flags & FLAG_SOUNDING) && (pcm->flags & FLAG_PENDING) | |
49 && pcm->pending_address >= 0x1000) { | |
50 pcm->ram[(pcm->pending_address & 0xFFF) | pcm->ram_bank] = pcm->pending_byte; | |
51 pcm->flags &= ~FLAG_PENDING; | |
52 } | |
53 } | |
54 | |
55 static void write_always(rf5c164* pcm) | |
56 { | |
57 if ((pcm->flags & FLAG_PENDING) && pcm->pending_address >= 0x1000) { | |
58 pcm->ram[(pcm->pending_address & 0xFFF) | pcm->ram_bank] = pcm->pending_byte; | |
59 pcm->flags &= ~FLAG_PENDING; | |
60 } | |
61 } | |
62 | |
63 void rf5c164_run(rf5c164* pcm, uint32_t cycle) | |
64 { | |
65 //TODO: Timing of this is all educated guesses based on documentation, do some real measurements | |
66 while (pcm->cycle < cycle) | |
67 { | |
68 switch (pcm->step) | |
69 { | |
70 case 0: | |
71 //handle internal memory write | |
72 // RF5C164 datasheet seems to suggest only 1 "internal memory" write every 48 cycles when the chip is not sounding (globally) | |
73 // and 1 write every 384 cycles when the chip is sounding (globally). However, games seem to expect to be able to write faster than that | |
74 // RF5C68 datasheet suggests internal memory writes are unrestricted regardless of sounding status | |
75 /*if ((pcm->cur_channel == pcm->selected_channel || !(pcm->flags & FLAG_SOUNDING)) && (pcm->flags & FLAG_PENDING) | |
76 && pcm->pending_address <= ST | |
77 ) { | |
78 printf("pcm_write commit chan %d, %X - %X @ %u\n", pcm->selected_channel, pcm->pending_address, pcm->pending_byte, pcm->cycle); | |
79 pcm->channels[pcm->selected_channel].regs[pcm->pending_address] = pcm->pending_byte; | |
80 pcm->flags &= ~FLAG_PENDING; | |
81 }*/ | |
82 write_if_not_sounding(pcm); | |
83 CHECK; | |
84 case 1: | |
85 if ((pcm->flags & FLAG_SOUNDING) && !(pcm->channel_enable & (1 << pcm->cur_channel))) { | |
86 if (pcm->channels[pcm->cur_channel].state == START) { | |
87 pcm->channels[pcm->cur_channel].cur_ptr = pcm->channels[pcm->cur_channel].regs[ST] << 19; | |
88 //printf("chan %d START %X (%X raw)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); | |
89 pcm->channels[pcm->cur_channel].state = NORMAL; | |
90 } else if (pcm->channels[pcm->cur_channel].state == LOOP) { | |
91 pcm->channels[pcm->cur_channel].cur_ptr = (pcm->channels[pcm->cur_channel].regs[LSH] << 19) | (pcm->channels[pcm->cur_channel].regs[LSL] << 11); | |
92 //printf("chan %d LOOP %X (%X raw)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); | |
93 pcm->channels[pcm->cur_channel].state = NORMAL; | |
94 } | |
95 } | |
96 write_if_not_sounding(pcm); | |
97 CHECK; | |
98 case 2: { | |
99 if ((pcm->flags & FLAG_SOUNDING) && !(pcm->channel_enable & (1 << pcm->cur_channel))) { | |
100 uint8_t byte = pcm->ram[pcm->channels[pcm->cur_channel].cur_ptr >> 11]; | |
101 if (byte == 0xFF) { | |
102 pcm->channels[pcm->cur_channel].state = LOOP; | |
103 } else { | |
104 pcm->channels[pcm->cur_channel].sample = byte; | |
105 } | |
106 } | |
107 write_if_not_sounding(pcm); | |
108 CHECK; | |
109 } | |
110 case 3: | |
111 write_always(pcm); | |
112 CHECK; | |
113 case 4: | |
114 if ((pcm->flags & FLAG_SOUNDING) && !(pcm->channel_enable & (1 << pcm->cur_channel))) { | |
115 pcm->channels[pcm->cur_channel].cur_ptr += (pcm->channels[pcm->cur_channel].regs[FDH] << 8) | pcm->channels[pcm->cur_channel].regs[FDL]; | |
116 pcm->channels[pcm->cur_channel].cur_ptr &= 0x7FFFFFF; | |
117 } | |
118 write_if_not_sounding(pcm); | |
119 CHECK; | |
120 case 5: | |
121 write_if_not_sounding(pcm); | |
122 CHECK; | |
123 case 6: | |
124 write_if_not_sounding(pcm); | |
125 CHECK; | |
126 case 7: | |
127 write_always(pcm); | |
128 CHECK; | |
129 case 8: | |
130 write_if_not_sounding(pcm); | |
131 CHECK; | |
132 case 9: | |
133 if ((pcm->flags & FLAG_SOUNDING) && !(pcm->channel_enable & (1 << pcm->cur_channel))) { | |
134 int16_t sample = pcm->channels[pcm->cur_channel].sample & 0x7F; | |
135 if (!(pcm->channels[pcm->cur_channel].sample & 0x80)) { | |
136 sample = -sample; | |
137 } | |
138 sample *= pcm->channels[pcm->cur_channel].regs[ENV]; | |
139 int16_t left = (sample * (pcm->channels[pcm->cur_channel].regs[PAN] >> 4)) >> 5; | |
140 int16_t right = (sample * (pcm->channels[pcm->cur_channel].regs[PAN] & 0xF)) >> 5; | |
141 //printf("chan %d, raw %X, sample %d, left %d, right %d, ptr %X (raw %X)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].sample, sample, left, right, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); | |
142 //TODO: saturating add | |
143 pcm->left += left; | |
144 pcm->right += right; | |
145 } | |
146 write_if_not_sounding(pcm); | |
147 CHECK; | |
148 case 10: | |
149 //refresh? | |
150 CHECK; | |
151 case 11: | |
152 write_always(pcm); | |
153 pcm->cur_channel++; | |
154 pcm->cur_channel &= 7; | |
155 if (!pcm->cur_channel) { | |
156 render_put_stereo_sample(pcm->audio, pcm->left, pcm->right); | |
157 pcm->left = pcm->right = 0; | |
158 } | |
159 CHECK_LOOP; | |
160 } | |
161 } | |
162 } | |
163 | |
164 void rf5c164_write(rf5c164* pcm, uint16_t address, uint8_t value) | |
165 { | |
166 //printf("pcm_write %X - %X @ %u\n", address, value, pcm->cycle); | |
167 if (address == CTRL) { | |
168 pcm->flags &= ~FLAG_SOUNDING; | |
169 pcm->flags |= value & FLAG_SOUNDING; | |
170 if (value & 0x40) { | |
171 pcm->selected_channel = value & 0x7; | |
172 //printf("selected channel %d\n", pcm->selected_channel); | |
173 } else { | |
174 pcm->ram_bank = value << 12 & 0xF000; | |
175 //printf("selected RAM bank %X\n", pcm->ram_bank); | |
176 } | |
177 if (!(pcm->flags & FLAG_SOUNDING)) { | |
178 pcm->left = pcm->right = 0; | |
179 } | |
180 } else if (address == CHAN_ENABLE) { | |
181 uint8_t changed = pcm->channel_enable ^ value; | |
182 pcm->channel_enable = value; | |
183 for (int i = 0; i < 8; i++) | |
184 { | |
185 int mask = 1 << i; | |
186 if (changed & mask & value) { | |
187 pcm->channels[i].state = START; | |
188 } | |
189 } | |
190 } else if (address <= ST) { | |
191 //See note in first step of rf5c164_run | |
192 pcm->channels[pcm->selected_channel].regs[address] = value; | |
193 } else { | |
194 pcm->pending_address = address; | |
195 pcm->pending_byte = value; | |
196 pcm->flags |= FLAG_PENDING; | |
197 } | |
198 } | |
199 | |
200 uint8_t rf5c164_read(rf5c164* pcm, uint16_t address) | |
201 { | |
202 if (address >= 0x10 && address < 0x20) { | |
203 uint16_t chan = address >> 1 & 0x7; | |
204 if (address & 1) { | |
205 return pcm->channels[chan].cur_ptr >> 19; | |
206 } else { | |
207 return pcm->channels[chan].cur_ptr >> 11; | |
208 } | |
209 } else if (address >= 0x1000 && !(pcm->flags & FLAG_SOUNDING)) { | |
210 return pcm->ram[pcm->ram_bank | (address & 0xFFF)]; | |
211 } else { | |
212 return 0xFF; | |
213 } | |
214 } |