summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pavone <pavone@retrodev.com>2013-05-05 22:56:42 -0700
committerMike Pavone <pavone@retrodev.com>2013-05-05 22:56:42 -0700
commit87ee3ef62cde9cd8126882612c6c2243774c6271 (patch)
treeffafbb62f995fb6d9f7df7d35d85228052d01ed3
parent2cf27dd7417d678cdc0d0bfa4648796e23452297 (diff)
Add a YM2612 stub implementation with just timers and status registers so that games that depend on it can run.
-rw-r--r--Makefile4
-rw-r--r--blastem.c83
-rw-r--r--blastem.h10
-rw-r--r--m68k_to_x86.h2
-rw-r--r--ym2612.c96
-rw-r--r--ym2612.h29
6 files changed, 198 insertions, 26 deletions
diff --git a/Makefile b/Makefile
index b1d315e..b6cd937 100644
--- a/Makefile
+++ b/Makefile
@@ -7,8 +7,8 @@ endif
all : dis trans stateview blastem
-blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o render_sdl.o
- $(CC) -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o render_sdl.o `pkg-config --libs $(LIBS)`
+blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o render_sdl.o
+ $(CC) -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o z80inst.o z80_to_x86.o x86_backend.o runtime.o zruntime.o mem.o vdp.o ym2612.o render_sdl.o `pkg-config --libs $(LIBS)`
dis : dis.o 68kinst.o
$(CC) -o dis dis.o 68kinst.o
diff --git a/blastem.c b/blastem.c
index 0697712..4b6cffc 100644
--- a/blastem.c
+++ b/blastem.c
@@ -165,11 +165,14 @@ void sync_z80(z80_context * z_context, uint32_t mclks)
m68k_context * sync_components(m68k_context * context, uint32_t address)
{
//TODO: Handle sync targets smaller than a single frame
- vdp_context * v_context = context->video_context;
- z80_context * z_context = context->next_cpu;
+ genesis_context * gen = context->system;
+ vdp_context * v_context = gen->vdp;
+ z80_context * z_context = gen->z80;
uint32_t mclks = context->current_cycle * MCLKS_PER_68K;
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;
//printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks);
vdp_run_context(v_context, MCLKS_PER_FRAME);
if (!headless) {
@@ -354,6 +357,7 @@ uint32_t zram_counter = 0;
m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value)
{
+ genesis_context * gen = context->system;
if (location < 0x10000) {
if (busack_cycle > context->current_cycle) {
busack = new_busack;
@@ -363,7 +367,16 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
location &= 0x7FFF;
if (location < 0x4000) {
z80_ram[location & 0x1FFF] = value;
- z80_handle_code_write(location & 0x1FFF, context->next_cpu);
+ z80_handle_code_write(location & 0x1FFF, gen->z80);
+ } else if (location < 0x6000) {
+ ym_run(gen->ym, context->current_cycle);
+ if (location & 1) {
+ ym_data_write(gen->ym, value);
+ } else if(location & 2) {
+ ym_address_write_part2(gen->ym, value);
+ } else {
+ ym_address_write_part1(gen->ym, value);
+ }
}
}
} else {
@@ -388,7 +401,7 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
}
} else {
if (location == 0x1100) {
- sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+ sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
if (busack_cycle > context->current_cycle) {
busack = new_busack;
busack_cycle = CYCLE_NEVER;
@@ -410,16 +423,15 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
fwrite(z80_ram, 1, sizeof(z80_ram), f);
fclose(f);
#endif
- z80_context * z_context = context->next_cpu;
//TODO: Add necessary delay between release of busreq and resumption of execution
- z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+ gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
}
busreq = 0;
busack_cycle = CYCLE_NEVER;
busack = 1;
}
} else if (location == 0x1200) {
- sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+ sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
if (value & 1) {
if (reset && busreq) {
new_busack = 0;
@@ -427,10 +439,9 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
}
//TODO: Deal with the scenario in which reset is not asserted long enough
if (reset) {
- z80_context * z_context = context->next_cpu;
need_reset = 1;
//TODO: Add necessary delay between release of reset and start of execution
- z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+ gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
}
reset = 0;
} else {
@@ -444,6 +455,7 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value
m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value)
{
+ genesis_context * gen = context->system;
if (location < 0x10000) {
if (busack_cycle > context->current_cycle) {
busack = new_busack;
@@ -453,7 +465,16 @@ m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t va
location &= 0x7FFF;
if (location < 0x4000) {
z80_ram[location & 0x1FFE] = value >> 8;
- z80_handle_code_write(location & 0x1FFE, context->next_cpu);
+ z80_handle_code_write(location & 0x1FFE, gen->z80);
+ } else if (location < 0x6000) {
+ ym_run(gen->ym, context->current_cycle);
+ if (location & 1) {
+ ym_data_write(gen->ym, value >> 8);
+ } else if(location & 2) {
+ ym_address_write_part2(gen->ym, value >> 8);
+ } else {
+ ym_address_write_part1(gen->ym, value >> 8);
+ }
}
}
} else {
@@ -479,7 +500,7 @@ m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t va
} else {
//printf("IO Write of %X to %X @ %d\n", value, location, context->current_cycle);
if (location == 0x1100) {
- sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+ sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
if (busack_cycle > context->current_cycle) {
busack = new_busack;
busack_cycle = CYCLE_NEVER;
@@ -501,16 +522,15 @@ m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t va
fwrite(z80_ram, 1, sizeof(z80_ram), f);
fclose(f);
#endif
- z80_context * z_context = context->next_cpu;
//TODO: Add necessary delay between release of busreq and resumption of execution
- z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+ gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
}
busreq = 0;
busack_cycle = CYCLE_NEVER;
busack = 1;
}
} else if (location == 0x1200) {
- sync_z80(context->next_cpu, context->current_cycle * MCLKS_PER_68K);
+ sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K);
if (value & 0x100) {
if (reset && busreq) {
new_busack = 0;
@@ -518,10 +538,9 @@ m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t va
}
//TODO: Deal with the scenario in which reset is not asserted long enough
if (reset) {
- z80_context * z_context = context->next_cpu;
need_reset = 1;
//TODO: Add necessary delay between release of reset and start of execution
- z_context->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
+ gen->z80->current_cycle = (context->current_cycle * MCLKS_PER_68K) / MCLKS_PER_Z80;
}
reset = 0;
} else {
@@ -541,6 +560,7 @@ uint8_t version_reg = NO_DISK | USA;
m68k_context * io_read(uint32_t location, m68k_context * context)
{
+ genesis_context *gen = context->system;
if (location < 0x10000) {
if (busack_cycle > context->current_cycle) {
busack = new_busack;
@@ -550,6 +570,9 @@ m68k_context * io_read(uint32_t location, m68k_context * context)
location &= 0x7FFF;
if (location < 0x4000) {
context->value = z80_ram[location & 0x1FFF];
+ } else if (location < 0x6000) {
+ ym_run(gen->ym, context->current_cycle);
+ context->value = ym_read_status(gen->ym);
} else {
context->value = 0xFF;
}
@@ -601,6 +624,7 @@ m68k_context * io_read(uint32_t location, m68k_context * context)
m68k_context * io_read_w(uint32_t location, m68k_context * context)
{
+ genesis_context * gen = context->system;
if (location < 0x10000) {
if (busack_cycle > context->current_cycle) {
busack = new_busack;
@@ -608,12 +632,16 @@ m68k_context * io_read_w(uint32_t location, m68k_context * context)
}
if (!(busack || reset)) {
location &= 0x7FFF;
+ uint16_t value;
if (location < 0x4000) {
- context->value = z80_ram[location & 0x1FFE];
- context->value |= context->value << 8;
+ value = z80_ram[location & 0x1FFE];
+ } else if (location < 0x6000) {
+ ym_run(gen->ym, context->current_cycle);
+ value = ym_read_status(gen->ym);
} else {
- context->value = 0xFFFF;
+ value = 0xFF;
}
+ context->value = value | (value << 8);
} else {
context->value = 0xFFFF;
}
@@ -894,16 +922,17 @@ m68k_context * debugger(m68k_context * context, uint32_t address)
return context;
}
-void init_run_cpu(vdp_context * vcontext, z80_context * zcontext, int debug, FILE * address_log)
+void init_run_cpu(genesis_context * gen, int debug, FILE * address_log)
{
m68k_context context;
x86_68k_options opts;
+ gen->m68k = &context;
init_x86_68k_opts(&opts);
opts.address_log = address_log;
init_68k_context(&context, opts.native_code_map, &opts);
- context.video_context = vcontext;
- context.next_cpu = zcontext;
+ context.video_context = gen->vdp;
+ context.system = gen;
//cartridge ROM
context.mem_pointers[0] = cart;
context.target_cycle = context.sync_cycle = MCLKS_PER_FRAME/MCLKS_PER_68K;
@@ -972,6 +1001,9 @@ int main(int argc, char ** argv)
init_vdp_context(&v_context);
+ ym2612_context y_context;
+ ym_init(&y_context);
+
z80_context z_context;
x86_z80_options z_opts;
init_x86_z80_opts(&z_opts);
@@ -982,6 +1014,11 @@ int main(int argc, char ** argv)
z_context.int_cycle = CYCLE_NEVER;
z_context.mem_pointers[1] = z_context.mem_pointers[2] = (uint8_t *)cart;
- init_run_cpu(&v_context, &z_context, debug, address_log);
+ genesis_context gen;
+ gen.z80 = &z_context;
+ gen.vdp = &v_context;
+ gen.ym = &y_context;
+
+ init_run_cpu(&gen, debug, address_log);
return 0;
}
diff --git a/blastem.h b/blastem.h
index 6ba1335..65be5c2 100644
--- a/blastem.h
+++ b/blastem.h
@@ -3,6 +3,9 @@
#include <stdint.h>
#include "m68k_to_x86.h"
+#include "z80_to_x86.h"
+#include "ym2612.h"
+#include "vdp.h"
typedef struct {
uint32_t th_counter;
@@ -12,6 +15,13 @@ typedef struct {
uint8_t input[3];
} io_port;
+typedef struct {
+ m68k_context *m68k;
+ z80_context *z80;
+ vdp_context *vdp;
+ ym2612_context *ym;
+} genesis_context;
+
#define GAMEPAD_TH0 0
#define GAMEPAD_TH1 1
#define GAMEPAD_EXTRA 2
diff --git a/m68k_to_x86.h b/m68k_to_x86.h
index 503f083..7a80517 100644
--- a/m68k_to_x86.h
+++ b/m68k_to_x86.h
@@ -43,7 +43,7 @@ typedef struct {
native_map_slot *native_code_map;
void *options;
uint8_t ram_code_flags[32/8];
- void *next_cpu;
+ void *system;
} m68k_context;
uint8_t * translate_m68k(uint8_t * dst, struct m68kinst * inst, x86_68k_options * opts);
diff --git a/ym2612.c b/ym2612.c
new file mode 100644
index 0000000..9be8b6c
--- /dev/null
+++ b/ym2612.c
@@ -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;
+}
+
diff --git a/ym2612.h b/ym2612.h
new file mode 100644
index 0000000..6b99dd0
--- /dev/null
+++ b/ym2612.h
@@ -0,0 +1,29 @@
+#ifndef YM2612_H_
+#define YM2612_H_
+
+#include <stdint.h>
+
+#define NUM_SHARED_REGS (0x30-0x21)
+#define NUM_PART_REGS (0xB7-0x30)
+
+typedef struct {
+ uint32_t current_cycle;
+ uint32_t write_cycle;
+ uint8_t *selected_reg;
+ uint16_t timer_a;
+ uint8_t timer_b;
+ uint8_t reg_num;
+ uint8_t status;
+ uint8_t part1_regs[NUM_SHARED_REGS+NUM_PART_REGS];
+ uint8_t part2_regs[NUM_PART_REGS];
+} ym2612_context;
+
+void ym_init(ym2612_context * context);
+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);
+void ym_data_write(ym2612_context * context, uint8_t value);
+uint8_t ym_read_status(ym2612_context * context);
+
+#endif //YM2612_H_
+