annotate psg.c @ 380:1c8d74f2ab0b

Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
author Mike Pavone <pavone@retrodev.com>
date Mon, 03 Jun 2013 21:43:38 -0700
parents 62177cc39049
children 41c079dbee73
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
380
1c8d74f2ab0b Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
Mike Pavone <pavone@retrodev.com>
parents: 364
diff changeset
6 void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame)
354
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);
380
1c8d74f2ab0b Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
Mike Pavone <pavone@retrodev.com>
parents: 364
diff changeset
11 double clock_rate = (double)master_clock / (double)clock_div;
1c8d74f2ab0b Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
Mike Pavone <pavone@retrodev.com>
parents: 364
diff changeset
12 context->buffer_inc = ((double)sample_rate / (double)master_clock) * clock_div;
1c8d74f2ab0b Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
Mike Pavone <pavone@retrodev.com>
parents: 364
diff changeset
13 context->clock_inc = clock_div;
354
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
14 context->samples_frame = samples_frame;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
15 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
16 context->volume[i] = 0xF;
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 }
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 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
21 {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
22 if (value & 0x80) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
23 context->latch = value & 0x70;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
24 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
25 if (value & 0x10) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
26 context->volume[channel] = value & 0xF;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
27 } else {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
28 if (channel == 3) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
29 switch(value & 0x3)
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
30 {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
31 case 0:
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
32 case 1:
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
33 case 2:
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
34 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
35 context->noise_use_tone = 0;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
36 break;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
37 default:
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
38 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
39 context->noise_use_tone = 1;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
40 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
41 context->noise_type = value & 0x4;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
42 context->lsfr = 0x8000;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
43 } else {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
44 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
45 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
46 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
47 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
48 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
49 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
50 } else {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
51 if (!(context->latch & 0x10)) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
52 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
53 if (channel != 3) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
54 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
55 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
56 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
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 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
62
355
fcd31d19dddd Fix clipping in PSG core
Mike Pavone <pavone@retrodev.com>
parents: 354
diff changeset
63 #define PSG_VOL_DIV 4
354
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
64
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
65 //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
66 int16_t volume_table[16] = {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
67 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
68 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
69 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
70 };
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 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
73 {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
74 while (context->cycles < cycles) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
75 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
76 if (context->counters[i]) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
77 context->counters[i] -= 1;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
78 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
79 if (!context->counters[i]) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
80 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
81 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
82 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
83 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
84 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
85 if (context->noise_type) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
86 //white noise
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
87 if (context->lsfr & 0x40) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
88 context->lsfr ^= 0x8000;
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 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
93 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
94 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
95 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
96 context->buffer_fraction -= 1.0;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
97 int16_t acc = 0;
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
98 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
99 if (context->output_state[i]) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
100 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
101 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
102 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
103 if (context->noise_out) {
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
104 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
105 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
106 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
107 if (context->buffer_pos == context->samples_frame) {
364
62177cc39049 Incredibly broken YM2612 support plus a fix to Z80 bus request
Mike Pavone <pavone@retrodev.com>
parents: 355
diff changeset
108 render_wait_psg(context);
354
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
109 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
110 }
380
1c8d74f2ab0b Make the PSG and YM2612 use the master clock internal with an increment based on clock divider so that they stay perflectly in sync. Run both the PSG and YM2612 whenver one of them needs to be run.
Mike Pavone <pavone@retrodev.com>
parents: 364
diff changeset
111 context->cycles += context->clock_inc;
354
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
112 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
113 }
15dd6418fe67 Initial PSG support. Mostly works, noise channel is borked though.
Mike Pavone <pavone@retrodev.com>
parents:
diff changeset
114