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