main
psg.c
1/*
2 Copyright 2013 Michael Pavone
3 This file is part of BlastEm.
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
5*/
6#include "psg.h"
7#include "render.h"
8#include "blastem.h"
9#include <string.h>
10#include <stdlib.h>
11#include <stdio.h>
12#include <math.h>
13void psg_init(psg_context * context, uint32_t master_clock, uint32_t clock_div)
14{
15 memset(context, 0, sizeof(*context));
16 context->audio = render_audio_source(master_clock, clock_div, 1);
17 context->clock_inc = clock_div;
18 for (int i = 0; i < 4; i++) {
19 context->volume[i] = 0xF;
20 }
21}
22
23void psg_free(psg_context *context)
24{
25 render_free_source(context->audio);
26 free(context);
27}
28
29void psg_adjust_master_clock(psg_context * context, uint32_t master_clock)
30{
31 render_audio_adjust_clock(context->audio, master_clock, context->clock_inc);
32}
33
34void psg_write(psg_context * context, uint8_t value)
35{
36 if (value & 0x80) {
37 context->latch = value & 0x70;
38 uint8_t channel = value >> 5 & 0x3;
39 if (value & 0x10) {
40 context->volume[channel] = value & 0xF;
41 } else {
42 if (channel == 3) {
43 switch(value & 0x3)
44 {
45 case 0:
46 case 1:
47 case 2:
48 context->counter_load[3] = 0x10 << (value & 0x3);
49 context->noise_use_tone = 0;
50 break;
51 default:
52 context->counter_load[3] = context->counter_load[2];
53 context->noise_use_tone = 1;
54 }
55 context->noise_type = value & 0x4;
56 context->lsfr = 0x8000;
57 } else {
58 context->counter_load[channel] = (context->counter_load[channel] & 0x3F0) | (value & 0xF);
59 if (channel == 2 && context->noise_use_tone) {
60 context->counter_load[3] = context->counter_load[2];
61 }
62 }
63 }
64 } else {
65 if (!(context->latch & 0x10)) {
66 uint8_t channel = context->latch >> 5 & 0x3;
67 if (channel != 3) {
68 context->counter_load[channel] = (value << 4 & 0x3F0) | (context->counter_load[channel] & 0xF);
69 if (channel == 2 && context->noise_use_tone) {
70 context->counter_load[3] = context->counter_load[2];
71 }
72 }
73 }
74 }
75}
76
77#define PSG_VOL_DIV 14
78
79//table shamelessly swiped from PSG doc from smspower.org
80static int16_t volume_table[16] = {
81 32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV,
82 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV,
83 2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0
84};
85
86void psg_run(psg_context * context, uint32_t cycles)
87{
88 while (context->cycles < cycles) {
89 for (int i = 0; i < 4; i++) {
90 if (context->counters[i]) {
91 context->counters[i] -= 1;
92 }
93 if (!context->counters[i]) {
94 context->counters[i] = context->counter_load[i];
95 context->output_state[i] = !context->output_state[i];
96 if (i == 3 && context->output_state[i]) {
97 context->noise_out = context->lsfr & 1;
98 context->lsfr = (context->lsfr >> 1) | (context->lsfr << 15);
99 if (context->noise_type) {
100 //white noise
101 if (context->lsfr & 0x40) {
102 context->lsfr ^= 0x8000;
103 }
104 }
105 }
106 }
107 }
108
109 int16_t accum = 0;
110
111 for (int i = 0; i < 3; i++) {
112 if (context->output_state[i]) {
113 accum += volume_table[context->volume[i]];
114 }
115 }
116 if (context->noise_out) {
117 accum += volume_table[context->volume[3]];
118 }
119
120 render_put_mono_sample(context->audio, accum);
121
122 context->cycles += context->clock_inc;
123 }
124}
125
126void psg_serialize(psg_context *context, serialize_buffer *buf)
127{
128 save_int16(buf, context->lsfr);
129 save_buffer16(buf, context->counter_load, 4);
130 save_buffer16(buf, context->counters, 4);
131 save_buffer8(buf, context->volume, 4);
132 uint8_t output_state = context->output_state[0] << 3 | context->output_state[1] << 2
133 | context->output_state[2] << 1 | context->output_state[3]
134 | context->noise_use_tone << 4;
135 save_int8(buf, output_state);
136 save_int8(buf, context->noise_type);
137 save_int8(buf, context->latch);
138 save_int32(buf, context->cycles);
139}
140
141void psg_deserialize(deserialize_buffer *buf, void *vcontext)
142{
143 psg_context *context = vcontext;
144 context->lsfr = load_int16(buf);
145 load_buffer16(buf, context->counter_load, 4);
146 load_buffer16(buf, context->counters, 4);
147 load_buffer8(buf, context->volume, 4);
148 uint8_t output_state = load_int8(buf);
149 context->output_state[0] = output_state >> 3 & 1;
150 context->output_state[1] = output_state >> 2 & 1;
151 context->output_state[2] = output_state >> 1 & 1;
152 context->output_state[3] = output_state & 1;
153 context->noise_use_tone = output_state >> 4 & 1;
154 context->noise_type = load_int8(buf);
155 context->latch = load_int8(buf);
156 context->cycles = load_int32(buf);
157}