diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | blastem.c | 83 | ||||
-rw-r--r-- | blastem.h | 10 | ||||
-rw-r--r-- | m68k_to_x86.h | 2 | ||||
-rw-r--r-- | ym2612.c | 96 | ||||
-rw-r--r-- | ym2612.h | 29 |
6 files changed, 198 insertions, 26 deletions
@@ -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 @@ -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; } @@ -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_ + |