summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blastem.c10
-rw-r--r--psg.c2
-rw-r--r--psg.h1
-rw-r--r--render.h4
-rw-r--r--render_sdl.c65
-rw-r--r--ym2612.c66
-rw-r--r--ym2612.h11
-rw-r--r--ztestrun.c5
8 files changed, 124 insertions, 40 deletions
diff --git a/blastem.c b/blastem.c
index e3f5744..affb600 100644
--- a/blastem.c
+++ b/blastem.c
@@ -198,7 +198,7 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
sync_z80(z_context, mclks);
if (mclks >= mclks_per_frame) {
ym_run(gen->ym, context->current_cycle);
- gen->ym->current_cycle -= ((mclks_per_frame/MCLKS_PER_68K) / 6) * 6;
+ gen->ym->current_cycle -= mclks_per_frame/MCLKS_PER_68K;
//printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks);
vdp_run_context(v_context, mclks_per_frame);
psg_run(gen->psg, mclks/MCLKS_PER_PSG);
@@ -534,8 +534,8 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
if(!reset && !busreq) {
busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY;
new_busack = Z80_REQ_ACK;
- busreq = 1;
}
+ busreq = 1;
} else {
if (busreq) {
dputs("releasing z80 bus");
@@ -635,8 +635,8 @@ m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t va
if(!reset && !busreq) {
busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY;
new_busack = Z80_REQ_ACK;
- busreq = 1;
}
+ busreq = 1;
} else {
if (busreq) {
dprintf("releasing Z80 bus @ %d\n", (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80);
@@ -1420,6 +1420,8 @@ void detect_region()
#define PSG_CLKS_NTSC (3579545/16)
#define PSG_CLKS_PAL (3546893/16)
+#define YM_CLKS_NTSC 7670454
+#define YM_CLKS_PAL 7600485
int main(int argc, char ** argv)
{
@@ -1505,7 +1507,7 @@ int main(int argc, char ** argv)
init_vdp_context(&v_context);
ym2612_context y_context;
- ym_init(&y_context);
+ ym_init(&y_context, render_sample_rate(), fps == 60 ? YM_CLKS_NTSC : YM_CLKS_PAL, render_audio_buffer());
psg_context p_context;
psg_init(&p_context, render_sample_rate(), fps == 60 ? PSG_CLKS_NTSC : PSG_CLKS_PAL, render_audio_buffer());
diff --git a/psg.c b/psg.c
index e035182..1052436 100644
--- a/psg.c
+++ b/psg.c
@@ -103,7 +103,7 @@ void psg_run(psg_context * context, uint32_t cycles)
}
context->audio_buffer[context->buffer_pos++] = acc;
if (context->buffer_pos == context->samples_frame) {
- render_wait_audio(context);
+ render_wait_psg(context);
}
}
context->cycles++;
diff --git a/psg.h b/psg.h
index 7d36f5c..be7ca6a 100644
--- a/psg.h
+++ b/psg.h
@@ -9,7 +9,6 @@ typedef struct {
double buffer_fraction;
double buffer_inc;
uint32_t buffer_pos;
- uint32_t back_pos;
uint32_t cycles;
uint32_t samples_frame;
uint16_t lsfr;
diff --git a/render.h b/render.h
index d366ebb..2696786 100644
--- a/render.h
+++ b/render.h
@@ -3,10 +3,12 @@
#include "vdp.h"
#include "psg.h"
+#include "ym2612.h"
void render_init(int width, int height, char * title, uint32_t fps);
void render_context(vdp_context * context);
void render_wait_quit(vdp_context * context);
-void render_wait_audio(psg_context * context);
+void render_wait_psg(psg_context * context);
+void render_wait_ym(ym2612_context * context);
int wait_render_frame(vdp_context * context, int frame_limit);
void render_fps(uint32_t fps);
uint32_t render_audio_buffer();
diff --git a/render_sdl.c b/render_sdl.c
index aa4fe4f..0ea68ff 100644
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -16,15 +16,16 @@ uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 2
uint32_t min_delay;
uint32_t frame_delay = 1000/60;
-int16_t * current_audio = NULL;
-int16_t * next_audio = NULL;
+int16_t * current_psg = NULL;
+int16_t * current_ym = NULL;
uint32_t buffer_samples, sample_rate;
uint32_t missing_count;
SDL_mutex * audio_mutex;
SDL_cond * audio_ready;
-SDL_cond * audio_cond;
+SDL_cond * psg_cond;
+SDL_cond * ym_cond;
uint8_t quitting = 0;
void audio_callback(void * userdata, uint8_t *byte_stream, int len)
@@ -32,21 +33,33 @@ void audio_callback(void * userdata, uint8_t *byte_stream, int len)
//puts("audio_callback");
int16_t * stream = (int16_t *)byte_stream;
int samples = len/(sizeof(int16_t)*2);
- int16_t * source_buf;
+ int16_t * psg_buf, * ym_buf;
uint8_t local_quit;
SDL_LockMutex(audio_mutex);
- while (!current_audio && !quitting) {
- SDL_CondWait(audio_ready, audio_mutex);
- }
+ psg_buf = NULL;
+ ym_buf = NULL;
+ do {
+ if (!psg_buf) {
+ psg_buf = current_psg;
+ current_psg = NULL;
+ SDL_CondSignal(psg_cond);
+ }
+ if (!ym_buf) {
+ ym_buf = current_ym;
+ current_ym = NULL;
+ SDL_CondSignal(ym_cond);
+ }
+ if (!quitting && (!psg_buf || !ym_buf)) {
+ SDL_CondWait(audio_ready, audio_mutex);
+ }
+ } while(!quitting && (!psg_buf || !ym_buf));
+
local_quit = quitting;
- source_buf = current_audio;
- current_audio = NULL;
- SDL_CondSignal(audio_cond);
SDL_UnlockMutex(audio_mutex);
if (!local_quit) {
for (int i = 0; i < samples; i++) {
- *(stream++) = source_buf[i];
- *(stream++) = source_buf[i];
+ *(stream++) = psg_buf[i] + *(ym_buf++);
+ *(stream++) = psg_buf[i] + *(ym_buf++);
}
}
}
@@ -113,7 +126,8 @@ void render_init(int width, int height, char * title, uint32_t fps)
frame_delay = 1000/fps;
audio_mutex = SDL_CreateMutex();
- audio_cond = SDL_CreateCond();
+ psg_cond = SDL_CreateCond();
+ ym_cond = SDL_CreateCond();
audio_ready = SDL_CreateCond();
SDL_AudioSpec desired, actual;
@@ -460,17 +474,32 @@ int wait_render_frame(vdp_context * context, int frame_limit)
return ret;
}
-void render_wait_audio(psg_context * context)
+void render_wait_psg(psg_context * context)
+{
+ SDL_LockMutex(audio_mutex);
+ while (current_psg != NULL) {
+ SDL_CondWait(psg_cond, audio_mutex);
+ }
+ current_psg = context->audio_buffer;
+ SDL_CondSignal(audio_ready);
+
+ context->audio_buffer = context->back_buffer;
+ context->back_buffer = current_psg;
+ SDL_UnlockMutex(audio_mutex);
+ context->buffer_pos = 0;
+}
+
+void render_wait_ym(ym2612_context * context)
{
SDL_LockMutex(audio_mutex);
- while (current_audio != NULL) {
- SDL_CondWait(audio_cond, audio_mutex);
+ while (current_ym != NULL) {
+ SDL_CondWait(ym_cond, audio_mutex);
}
- current_audio = context->audio_buffer;
+ current_ym = context->audio_buffer;
SDL_CondSignal(audio_ready);
context->audio_buffer = context->back_buffer;
- context->back_buffer = current_audio;
+ context->back_buffer = current_ym;
SDL_UnlockMutex(audio_mutex);
context->buffer_pos = 0;
}
diff --git a/ym2612.c b/ym2612.c
index 73dbc7e..810b676 100644
--- a/ym2612.c
+++ b/ym2612.c
@@ -1,10 +1,12 @@
#include <string.h>
#include <math.h>
#include <stdio.h>
+#include <stdlib.h>
#include "ym2612.h"
+#include "render.h"
#define BUSY_CYCLES 17
-#define TIMERA_UPDATE_PERIOD 144
+#define OP_UPDATE_PERIOD 144
enum {
REG_TIMERA_HIGH = 0x24,
@@ -75,16 +77,20 @@ uint16_t rate_table_base[] = {
uint16_t rate_table[64];
#define MAX_ENVELOPE 0xFFC
-
+#define YM_DIVIDER 2
uint16_t round_fixed_point(double value, int dec_bits)
{
return value * (1 << dec_bits) + 0.5;
}
-void ym_init(ym2612_context * context)
+void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t sample_limit)
{
memset(context, 0, sizeof(*context));
+ context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2);
+ context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2);
+ context->buffer_inc = (double)sample_rate / (double)(clock_rate/OP_UPDATE_PERIOD);
+ context->sample_limit = sample_limit*2;
for (int i = 0; i < NUM_OPERATORS; i++) {
context->operators[i].envelope = MAX_ENVELOPE;
context->operators[i].env_phase = PHASE_RELEASE;
@@ -134,9 +140,8 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
//printf("Running YM2612 from cycle %d to cycle %d\n", context->current_cycle, to_cycle);
//TODO: Fix channel update order OR remap channels in register write
for (; context->current_cycle < to_cycle; context->current_cycle += 6) {
- uint32_t update_cyc = context->current_cycle % 144;
//Update timers at beginning of 144 cycle period
- if (!update_cyc && context->timer_control & BIT_TIMERA_ENABLE) {
+ if (!context->current_op && context->timer_control & BIT_TIMERA_ENABLE) {
if (context->timer_a) {
context->timer_a--;
} else {
@@ -146,7 +151,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
context->timer_a = context->timer_a_load;
}
if (context->timer_control & BIT_TIMERB_ENABLE) {
- uint32_t b_cyc = (context->current_cycle / 144) % 16;
+ uint32_t b_cyc = (context->current_cycle / OP_UPDATE_PERIOD) % 16;
if (!b_cyc) {
if (context->timer_b) {
context->timer_b--;
@@ -160,10 +165,9 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
}
}
//Update Envelope Generator
- if (update_cyc == 0 || update_cyc == 72) {
- uint32_t env_cyc = context->current_cycle / 72;
- uint32_t op = env_cyc % 24;
- env_cyc /= 24;
+ if (!(context->current_op % 3)) {
+ uint32_t env_cyc = context->env_counter;
+ uint32_t op = context->current_env_op;
ym_operator * operator = context->operators + op;
ym_channel * channel = context->channels + op/4;
uint8_t rate;
@@ -214,15 +218,18 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
operator->env_phase = PHASE_SUSTAIN;
}
}
-
-
+ }
+ context->current_env_op++;
+ if (context->current_env_op == NUM_OPERATORS) {
+ context->current_env_op = 0;
+ context->env_counter++;
}
}
//Update Phase Generator
- uint32_t channel = update_cyc / 24;
+ uint32_t channel = context->current_op / 4;
if (channel != 5 || !context->dac_enable) {
- uint32_t op = (update_cyc) / 6;
+ uint32_t op = context->current_op;
//printf("updating operator %d of channel %d\n", op, channel);
ym_operator * operator = context->operators + op;
ym_channel * chan = context->channels + channel;
@@ -303,6 +310,32 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
}
//puts("operator update done");
}
+ context->current_op++;
+ if (context->current_op == NUM_OPERATORS) {
+ context->current_op = 0;
+ context->buffer_fraction += context->buffer_inc;
+ if (context->buffer_fraction > 1.0) {
+ context->buffer_fraction -= 1.0;
+ context->audio_buffer[context->buffer_pos] = 0;
+ context->audio_buffer[context->buffer_pos + 1] = 0;
+ for (int i = 0; i < NUM_CHANNELS; i++) {
+ uint16_t value = context->channels[i].output & 0x3FE0;
+ if (value & 0x2000) {
+ value |= 0xC000;
+ }
+ if (context->channels[i].lr & 0x80) {
+ context->audio_buffer[context->buffer_pos] += value / 2;
+ }
+ if (context->channels[i].lr & 0x40) {
+ context->audio_buffer[context->buffer_pos+1] += value / 2;
+ }
+ }
+ context->buffer_pos += 2;
+ if (context->buffer_pos == context->sample_limit) {
+ render_wait_ym(context);
+ }
+ }
+ }
}
if (context->current_cycle >= context->write_cycle + BUSY_CYCLES) {
context->status &= 0x7F;
@@ -312,12 +345,14 @@ void ym_run(ym2612_context * context, uint32_t to_cycle)
void ym_address_write_part1(ym2612_context * context, uint8_t address)
{
+ //printf("address_write_part1: %X\n", address);
context->selected_reg = address;
context->selected_part = 0;
}
void ym_address_write_part2(ym2612_context * context, uint8_t address)
{
+ //printf("address_write_part2: %X\n", address);
context->selected_reg = address;
context->selected_part = 1;
}
@@ -393,6 +428,7 @@ void ym_update_phase_inc(ym2612_context * context, ym_operator * operator, uint3
//0.5
inc >>= 1;
}
+ operator->phase_inc = inc;
}
void ym_data_write(ym2612_context * context, uint8_t value)
@@ -440,9 +476,11 @@ void ym_data_write(ym2612_context * context, uint8_t value)
case REG_DAC:
if (context->dac_enable) {
context->channels[5].output = (((int16_t)value) - 0x80) << 6;
+ //printf("DAC Write %X(%d)\n", context->channels[5].output, context->channels[5].output);
}
break;
case REG_DAC_ENABLE:
+ //printf("DAC Enable: %X\n", value);
context->dac_enable = value & 0x80;
break;
}
diff --git a/ym2612.h b/ym2612.h
index 272322f..a296659 100644
--- a/ym2612.h
+++ b/ym2612.h
@@ -35,12 +35,21 @@ typedef struct {
} ym_channel;
typedef struct {
+ int16_t *audio_buffer;
+ int16_t *back_buffer;
+ double buffer_fraction;
+ double buffer_inc;
+ uint32_t buffer_pos;
+ uint32_t sample_limit;
uint32_t current_cycle;
uint32_t write_cycle;
ym_operator operators[NUM_OPERATORS];
ym_channel channels[NUM_CHANNELS];
uint16_t timer_a;
uint16_t timer_a_load;
+ uint16_t env_counter;
+ uint8_t current_op;
+ uint8_t current_env_op;
uint8_t timer_b;
uint8_t timer_b_load;
uint8_t timer_control;
@@ -50,7 +59,7 @@ typedef struct {
uint8_t selected_part;
} ym2612_context;
-void ym_init(ym2612_context * context);
+void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t clock_rate, uint32_t sample_limit);
void ym_run(ym2612_context * context, uint32_t to_cycle);
void ym_address_write_part1(ym2612_context * context, uint8_t address);
void ym_address_write_part2(ym2612_context * context, uint8_t address);
diff --git a/ztestrun.c b/ztestrun.c
index b74d064..162a94d 100644
--- a/ztestrun.c
+++ b/ztestrun.c
@@ -24,6 +24,11 @@ z80_context * z80_write_ym(uint16_t location, z80_context * context, uint8_t val
return context;
}
+z80_context * z80_vdp_port_write(uint16_t location, z80_context * context, uint8_t value)
+{
+ return context;
+}
+
int main(int argc, char ** argv)
{
long filesize;