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}