annotate psg.c @ 989:d70000fdff0b

Implemented IR and undefined bits of info word for address error exception frames
author Michael Pavone <pavone@retrodev.com>
date Wed, 27 Apr 2016 21:39:17 -0700
parents e6dc30231b83
children 8d032a368dd5
rev   line source
pavone@467 1 /*
pavone@467 2 Copyright 2013 Michael Pavone
pavone@483 3 This file is part of BlastEm.
pavone@467 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.
pavone@467 5 */
pavone@354 6 #include "psg.h"
pavone@354 7 #include "render.h"
pavone@505 8 #include "blastem.h"
pavone@354 9 #include <string.h>
pavone@354 10 #include <stdlib.h>
pavone@838 11 #include <stdio.h>
pavone@964 12 #include <math.h>
pavone@964 13 #define LOWPASS_CUTOFF 3390
pavone@838 14
pavone@380 15 void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame)
pavone@354 16 {
pavone@354 17 memset(context, 0, sizeof(*context));
pavone@354 18 context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
pavone@354 19 context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
pavone@380 20 context->clock_inc = clock_div;
pavone@483 21 context->sample_rate = sample_rate;
pavone@354 22 context->samples_frame = samples_frame;
pavone@964 23 double rc = (1.0 / (double)LOWPASS_CUTOFF) / (2.0 * M_PI);
pavone@964 24 double dt = 1.0 / ((double)master_clock / (double)clock_div);
pavone@964 25 double alpha = dt / (dt + rc);
pavone@964 26 context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
pavone@483 27 psg_adjust_master_clock(context, master_clock);
pavone@354 28 for (int i = 0; i < 4; i++) {
pavone@354 29 context->volume[i] = 0xF;
pavone@354 30 }
pavone@354 31 }
pavone@354 32
pavone@884 33 void psg_free(psg_context *context)
pavone@884 34 {
pavone@884 35 free(context->audio_buffer);
pavone@884 36 //TODO: Figure out how to make this 100% safe
pavone@884 37 //audio thread could still be using this
pavone@884 38 free(context->back_buffer);
pavone@884 39 free(context);
pavone@884 40 }
pavone@884 41
pavone@963 42 #define BUFFER_INC_RES 0x40000000UL
pavone@483 43
pavone@483 44 void psg_adjust_master_clock(psg_context * context, uint32_t master_clock)
pavone@483 45 {
pavone@483 46 uint64_t old_inc = context->buffer_inc;
pavone@483 47 context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc;
pavone@483 48 }
pavone@483 49
pavone@354 50 void psg_write(psg_context * context, uint8_t value)
pavone@354 51 {
pavone@354 52 if (value & 0x80) {
pavone@354 53 context->latch = value & 0x70;
pavone@354 54 uint8_t channel = value >> 5 & 0x3;
pavone@354 55 if (value & 0x10) {
pavone@354 56 context->volume[channel] = value & 0xF;
pavone@354 57 } else {
pavone@354 58 if (channel == 3) {
pavone@354 59 switch(value & 0x3)
pavone@354 60 {
pavone@354 61 case 0:
pavone@354 62 case 1:
pavone@354 63 case 2:
pavone@354 64 context->counter_load[3] = 0x10 << (value & 0x3);
pavone@354 65 context->noise_use_tone = 0;
pavone@354 66 break;
pavone@354 67 default:
pavone@354 68 context->counter_load[3] = context->counter_load[2];
pavone@354 69 context->noise_use_tone = 1;
pavone@354 70 }
pavone@354 71 context->noise_type = value & 0x4;
pavone@354 72 context->lsfr = 0x8000;
pavone@354 73 } else {
pavone@354 74 context->counter_load[channel] = (context->counter_load[channel] & 0x3F0) | (value & 0xF);
pavone@354 75 if (channel == 2 && context->noise_use_tone) {
pavone@354 76 context->counter_load[3] = context->counter_load[2];
pavone@354 77 }
pavone@354 78 }
pavone@354 79 }
pavone@354 80 } else {
pavone@354 81 if (!(context->latch & 0x10)) {
pavone@354 82 uint8_t channel = context->latch >> 5 & 0x3;
pavone@354 83 if (channel != 3) {
pavone@354 84 context->counter_load[channel] = (value << 4 & 0x3F0) | (context->counter_load[channel] & 0xF);
pavone@354 85 if (channel == 2 && context->noise_use_tone) {
pavone@354 86 context->counter_load[3] = context->counter_load[2];
pavone@354 87 }
pavone@354 88 }
pavone@354 89 }
pavone@354 90 }
pavone@354 91 }
pavone@354 92
pavone@522 93 #define PSG_VOL_DIV 14
pavone@354 94
pavone@354 95 //table shamelessly swiped from PSG doc from smspower.org
pavone@354 96 int16_t volume_table[16] = {
pavone@354 97 32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV,
pavone@483 98 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV,
pavone@354 99 2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0
pavone@354 100 };
pavone@354 101
pavone@354 102 void psg_run(psg_context * context, uint32_t cycles)
pavone@354 103 {
pavone@354 104 while (context->cycles < cycles) {
pavone@354 105 for (int i = 0; i < 4; i++) {
pavone@354 106 if (context->counters[i]) {
pavone@354 107 context->counters[i] -= 1;
pavone@354 108 }
pavone@354 109 if (!context->counters[i]) {
pavone@354 110 context->counters[i] = context->counter_load[i];
pavone@354 111 context->output_state[i] = !context->output_state[i];
pavone@354 112 if (i == 3 && context->output_state[i]) {
pavone@354 113 context->noise_out = context->lsfr & 1;
pavone@354 114 context->lsfr = (context->lsfr >> 1) | (context->lsfr << 15);
pavone@354 115 if (context->noise_type) {
pavone@354 116 //white noise
pavone@354 117 if (context->lsfr & 0x40) {
pavone@354 118 context->lsfr ^= 0x8000;
pavone@354 119 }
pavone@354 120 }
pavone@354 121 }
pavone@354 122 }
pavone@354 123 }
pavone@838 124
pavone@963 125 context->last_sample = context->accum;
pavone@963 126 context->accum = 0;
pavone@963 127
pavone@838 128 for (int i = 0; i < 3; i++) {
pavone@838 129 if (context->output_state[i]) {
pavone@838 130 context->accum += volume_table[context->volume[i]];
pavone@838 131 }
pavone@838 132 }
pavone@838 133 if (context->noise_out) {
pavone@838 134 context->accum += volume_table[context->volume[3]];
pavone@838 135 }
pavone@964 136 int32_t tmp = context->accum * context->lowpass_alpha + context->last_sample * (0x10000 - context->lowpass_alpha);
pavone@964 137 context->accum = tmp >> 16;
pavone@838 138
pavone@354 139 context->buffer_fraction += context->buffer_inc;
pavone@483 140 if (context->buffer_fraction >= BUFFER_INC_RES) {
pavone@483 141 context->buffer_fraction -= BUFFER_INC_RES;
pavone@964 142 int32_t tmp = context->last_sample * ((context->buffer_fraction << 16) / context->buffer_inc);
pavone@964 143 tmp += context->accum * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
pavone@963 144 context->audio_buffer[context->buffer_pos++] = tmp >> 16;
pavone@964 145
pavone@354 146 if (context->buffer_pos == context->samples_frame) {
pavone@505 147 if (!headless) {
pavone@505 148 render_wait_psg(context);
pavone@505 149 }
pavone@354 150 }
pavone@354 151 }
pavone@380 152 context->cycles += context->clock_inc;
pavone@354 153 }
pavone@354 154 }
pavone@354 155