diff ym2612.c @ 288:a8ee7934a1f8

Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
author Mike Pavone <pavone@retrodev.com>
date Sun, 05 May 2013 22:56:42 -0700
parents
children cc39629e8d06
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ym2612.c	Sun May 05 22:56:42 2013 -0700
@@ -0,0 +1,96 @@
+#include <string.h>
+#include "ym2612.h"
+
+#define BUSY_CYCLES 17
+#define TIMERA_UPDATE_PERIOD 144
+
+#define REG_TIMERA_HIGH 0x3 // 0x24
+#define REG_TIMERA_LOW 0x4  // 0x25
+#define REG_TIMERB 0x5      // 0x26
+#define REG_TIME_CTRL 0x6   // 0x27
+
+#define BIT_TIMERA_ENABLE 0x1
+#define BIT_TIMERB_ENABLE 0x2
+#define BIT_TIMERA_OVEREN 0x4
+#define BIT_TIMERB_OVEREN 0x8
+#define BIT_TIMERA_RESET  0x10
+#define BIT_TIMERB_RESET  0x20
+
+#define BIT_STATUS_TIMERA 0x1
+#define BIT_STATUS_TIMERB 0x2
+
+void ym_init(ym2612_context * context)
+{
+	memset(context, 0, sizeof(*context));
+}
+
+void ym_run(ym2612_context * context, uint32_t to_cycle)
+{
+	uint32_t delta = to_cycle - context->current_cycle;
+	//Timers won't be perfect with this, but it's good enough for now
+	//once actual FM emulation is in place the timers should just be 
+	//decremented/reloaded on the appropriate ticks
+	uint32_t timer_delta = to_cycle / TIMERA_UPDATE_PERIOD;
+	if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_ENABLE) {
+		if (timer_delta > context->timer_a) {
+			if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERA_OVEREN) {
+				context->status |= BIT_STATUS_TIMERA;
+			}
+			uint32_t rem_delta = timer_delta - (context->timer_a+1);
+			uint16_t timer_val = (context->part1_regs[REG_TIMERA_HIGH] << 2) | (context->part1_regs[REG_TIMERA_LOW] & 0x3);
+			context->timer_a = timer_val - (rem_delta % (timer_val + 1));
+		} else {
+			context->timer_a -= timer_delta;
+		}
+	}
+	timer_delta /= 16; //Timer B runs at 1/16th the speed of Timer A
+	if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_ENABLE) {
+		if (timer_delta > context->timer_b) {
+			if (context->part1_regs[REG_TIME_CTRL] & BIT_TIMERB_OVEREN) {
+				context->status |= BIT_STATUS_TIMERB;
+			}
+			uint32_t rem_delta = timer_delta - (context->timer_b+1);
+			uint8_t timer_val = context->part1_regs[REG_TIMERB];
+			context->timer_b = timer_val - (rem_delta % (timer_val + 1));
+		} else {
+			context->timer_a -= timer_delta;
+		}
+	}
+	context->current_cycle = to_cycle;
+	if (to_cycle >= context->write_cycle + BUSY_CYCLES) {
+		context->status &= 0x7F;
+	}
+}
+
+void ym_address_write_part1(ym2612_context * context, uint8_t address)
+{
+	if (address >= 0x21 && address < 0xB7) {
+		context->selected_reg = context->part1_regs + address - 0x21;
+	} else {
+		context->selected_reg = NULL;
+	}
+}
+
+void ym_address_write_part2(ym2612_context * context, uint8_t address)
+{
+	if (address >= 0x30 && address < 0xB7) {
+		context->selected_reg = context->part1_regs + address - 0x30;
+	} else {
+		context->selected_reg = NULL;
+	}
+}
+
+void ym_data_write(ym2612_context * context, uint8_t value)
+{
+	if (context->selected_reg && !(context->status & 0x80)) {
+		*context->selected_reg = value;
+		context->write_cycle = context->current_cycle;
+		context->selected_reg = NULL;//TODO: Verify this
+	}
+}
+
+uint8_t ym_read_status(ym2612_context * context)
+{
+	return context->status;
+}
+