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