Mercurial > repos > blastem
comparison psg.c @ 354:15dd6418fe67
Initial PSG support. Mostly works, noise channel is borked though.
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 23 May 2013 23:42:42 -0700 |
parents | |
children | fcd31d19dddd |
comparison
equal
deleted
inserted
replaced
353:a60e527cd21f | 354:15dd6418fe67 |
---|---|
1 #include "psg.h" | |
2 #include "render.h" | |
3 #include <string.h> | |
4 #include <stdlib.h> | |
5 | |
6 void psg_init(psg_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t samples_frame) | |
7 { | |
8 memset(context, 0, sizeof(*context)); | |
9 context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame); | |
10 context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame); | |
11 context->buffer_inc = (double)sample_rate / (double)clock_rate; | |
12 context->samples_frame = samples_frame; | |
13 for (int i = 0; i < 4; i++) { | |
14 context->volume[i] = 0xF; | |
15 } | |
16 } | |
17 | |
18 void psg_write(psg_context * context, uint8_t value) | |
19 { | |
20 if (value & 0x80) { | |
21 context->latch = value & 0x70; | |
22 uint8_t channel = value >> 5 & 0x3; | |
23 if (value & 0x10) { | |
24 context->volume[channel] = value & 0xF; | |
25 } else { | |
26 if (channel == 3) { | |
27 switch(value & 0x3) | |
28 { | |
29 case 0: | |
30 case 1: | |
31 case 2: | |
32 context->counter_load[3] = 0x10 << (value & 0x3); | |
33 context->noise_use_tone = 0; | |
34 break; | |
35 default: | |
36 context->counter_load[3] = context->counter_load[2]; | |
37 context->noise_use_tone = 1; | |
38 } | |
39 context->noise_type = value & 0x4; | |
40 context->lsfr = 0x8000; | |
41 } else { | |
42 context->counter_load[channel] = (context->counter_load[channel] & 0x3F0) | (value & 0xF); | |
43 if (channel == 2 && context->noise_use_tone) { | |
44 context->counter_load[3] = context->counter_load[2]; | |
45 } | |
46 } | |
47 } | |
48 } else { | |
49 if (!(context->latch & 0x10)) { | |
50 uint8_t channel = context->latch >> 5 & 0x3; | |
51 if (channel != 3) { | |
52 context->counter_load[channel] = (value << 4 & 0x3F0) | (context->counter_load[channel] & 0xF); | |
53 if (channel == 2 && context->noise_use_tone) { | |
54 context->counter_load[3] = context->counter_load[2]; | |
55 } | |
56 } | |
57 } | |
58 } | |
59 } | |
60 | |
61 #define PSG_VOL_DIV 2 | |
62 | |
63 //table shamelessly swiped from PSG doc from smspower.org | |
64 int16_t volume_table[16] = { | |
65 32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV, | |
66 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV, | |
67 2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0 | |
68 }; | |
69 | |
70 void psg_run(psg_context * context, uint32_t cycles) | |
71 { | |
72 while (context->cycles < cycles) { | |
73 for (int i = 0; i < 4; i++) { | |
74 if (context->counters[i]) { | |
75 context->counters[i] -= 1; | |
76 } | |
77 if (!context->counters[i]) { | |
78 context->counters[i] = context->counter_load[i]; | |
79 context->output_state[i] = !context->output_state[i]; | |
80 if (i == 3 && context->output_state[i]) { | |
81 context->noise_out = context->lsfr & 1; | |
82 context->lsfr = (context->lsfr >> 1) | (context->lsfr << 15); | |
83 if (context->noise_type) { | |
84 //white noise | |
85 if (context->lsfr & 0x40) { | |
86 context->lsfr ^= 0x8000; | |
87 } | |
88 } | |
89 } | |
90 } | |
91 } | |
92 context->buffer_fraction += context->buffer_inc; | |
93 if (context->buffer_fraction >= 1.0) { | |
94 context->buffer_fraction -= 1.0; | |
95 int16_t acc = 0; | |
96 for (int i = 0; i < 3; i++) { | |
97 if (context->output_state[i]) { | |
98 acc += volume_table[context->volume[i]]; | |
99 } | |
100 } | |
101 if (context->noise_out) { | |
102 acc += volume_table[context->volume[3]]; | |
103 } | |
104 context->audio_buffer[context->buffer_pos++] = acc; | |
105 if (context->buffer_pos == context->samples_frame) { | |
106 render_wait_audio(context); | |
107 } | |
108 } | |
109 context->cycles++; | |
110 } | |
111 } | |
112 |