# HG changeset patch # User Mike Pavone # Date 1356587448 28800 # Node ID c339559f1d4f8d60a460d2b197f8e3bd2327eb2c # Parent 60b5c9e2f4e0b7e190938ca6626f95bebbcbae74 Forgot to add blastem main file earlier diff -r 60b5c9e2f4e0 -r c339559f1d4f blastem.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/blastem.c Wed Dec 26 21:50:48 2012 -0800 @@ -0,0 +1,503 @@ +#include "68kinst.h" +#include "m68k_to_x86.h" +#include "mem.h" +#include "vdp.h" +#include "render.h" +#include "blastem.h" +#include +#include + +#define CARTRIDGE_WORDS 0x200000 +#define RAM_WORDS 32 * 1024 +#define MCLKS_PER_68K 7 +//TODO: Figure out the exact value for this +#define MCLKS_PER_FRAME (MCLKS_LINE*262) +#define CYCLE_NEVER 0xFFFFFFFF + +uint16_t cart[CARTRIDGE_WORDS]; +uint16_t ram[RAM_WORDS]; + +io_port gamepad_1; +io_port gamepad_2; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +int load_rom(char * filename) +{ + FILE * f = fopen(filename, "rb"); + if (!f) { + return 0; + } + fseek(f, 0, SEEK_END); + long filesize = ftell(f); + fseek(f, 0, SEEK_SET); + fread(cart, 2, MIN(filesize/2, CARTRIDGE_WORDS), f); + fclose(f); + for(unsigned short * cur = cart; cur - cart < (filesize/2); ++cur) + { + *cur = (*cur >> 8) | (*cur << 8); + } + //TODO: Mirror ROM + return 1; +} + +uint16_t read_dma_value(uint32_t address) +{ + //addresses here are word addresses (i.e. bit 0 corresponds to A1), so no need to do div by 2 + if (address < 0x200000) { + return cart[address]; + } else if(address >= 0x700000) { + return ram[address & 0x7FFF]; + } + //TODO: Figure out what happens when you try to DMA from weird adresses like IO or banked Z80 area + return 0; +} + +#define VINT_CYCLE ((MCLKS_LINE * 226)/MCLKS_PER_68K) + +m68k_context * sync_components(m68k_context * context) +{ + //TODO: Handle sync targets smaller than a single frame + vdp_context * v_context = context->next_context; + uint32_t mclks = context->current_cycle * MCLKS_PER_68K; + if (mclks >= MCLKS_PER_FRAME) { + //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); + vdp_run_context(v_context, MCLKS_PER_FRAME); + wait_render_frame(v_context); + mclks -= MCLKS_PER_FRAME; + vdp_adjust_cycles(v_context, MCLKS_PER_FRAME); + io_adjust_cycles(&gamepad_1, context->current_cycle, MCLKS_PER_FRAME/MCLKS_PER_68K); + io_adjust_cycles(&gamepad_2, context->current_cycle, MCLKS_PER_FRAME/MCLKS_PER_68K); + context->current_cycle -= MCLKS_PER_FRAME/MCLKS_PER_68K; + if (mclks) { + vdp_run_context(v_context, mclks); + } + if (v_context->regs[REG_MODE_2] & 0x20 && ((context->status & 0x7) < 6)) { + if (context->int_cycle > VINT_CYCLE) { + context->int_cycle = VINT_CYCLE; + context->int_num = 6; + if (context->int_cycle < context->sync_cycle) { + context->target_cycle = context->int_cycle; + } + } + } else { + context->int_cycle = 0xFFFFFFFF; + context->target_cycle = context->sync_cycle; + } + } else { + //printf("running VDP for %d cycles\n", mclks - v_context->cycles); + vdp_run_context(v_context, mclks); + if (v_context->regs[REG_MODE_2] & 0x20 && ((context->status & 0x7) < 6)) { + if (context->int_cycle > VINT_CYCLE) { + context->int_cycle = VINT_CYCLE; + context->int_num = 6; + if (context->int_cycle < context->sync_cycle && context->int_cycle < context->current_cycle) { + context->target_cycle = context->int_cycle; + } + } + if (context->int_cycle <= context->current_cycle) { + context->int_cycle = CYCLE_NEVER; + context->target_cycle = context->sync_cycle; + } + } else { + context->int_cycle = CYCLE_NEVER; + context->target_cycle = context->sync_cycle; + } + } + return context; +} + +m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_t value) +{ + //printf("vdp_port write: %X, value: %X, cycle: %d\n", vdp_port, value, context->current_cycle); + sync_components(context); + vdp_context * v_context = context->next_context; + if (vdp_port < 0x10) { + if (vdp_port < 4) { + vdp_data_port_write(v_context, value); + } else if(vdp_port < 8) { + int blocked = vdp_control_port_write(v_context, value); + if (blocked) { + while(v_context->flags & FLAG_DMA_RUN) { + vdp_run_dma_done(v_context, MCLKS_PER_FRAME); + if (v_context->cycles >= MCLKS_PER_FRAME) { + wait_render_frame(v_context); + vdp_adjust_cycles(v_context, MCLKS_PER_FRAME); + io_adjust_cycles(&gamepad_1, v_context->cycles/MCLKS_PER_68K, MCLKS_PER_FRAME/MCLKS_PER_68K); + io_adjust_cycles(&gamepad_2, v_context->cycles/MCLKS_PER_68K, MCLKS_PER_FRAME/MCLKS_PER_68K); + } + } + context->current_cycle = v_context->cycles / MCLKS_PER_68K; + } else { + if (v_context->regs[REG_MODE_2] & 0x20 && ((context->status & 0x7) < 6)) { + if (context->int_cycle > VINT_CYCLE) { + context->int_cycle = VINT_CYCLE; + context->int_num = 6; + if (context->int_cycle < context->sync_cycle) { + context->target_cycle = context->int_cycle; + } + } + } else { + context->int_cycle = 0xFFFFFFFF; + context->target_cycle = context->sync_cycle; + } + } + } else { + printf("Illegal write to HV Counter port %X\n", vdp_port); + exit(1); + } + context->current_cycle = v_context->cycles/MCLKS_PER_68K; + } else if (vdp_port < 0x18) { + //TODO: Implement PSG + } else { + //TODO: Implement undocumented test register(s) + } + return context; +} + +m68k_context * vdp_port_read(uint32_t vdp_port, m68k_context * context) +{ + sync_components(context); + vdp_context * v_context = context->next_context; + if (vdp_port < 0x10) { + if (vdp_port < 4) { + context->value = vdp_data_port_read(v_context); + } else if(vdp_port < 8) { + context->value = vdp_control_port_read(v_context); + } else { + //TODO: Implement H/V counter + context->value = 0; + } + context->current_cycle = v_context->cycles/MCLKS_PER_68K; + } else { + printf("Illegal read from PSG or test register port %X\n", vdp_port); + exit(1); + } + return context; +} + +#define TH 0x40 +#define TH_TIMEOUT 8000 +#define Z80_ACK_DELAY 3 //TODO: Calculate this on the fly based on how synced up the Z80 and 68K clocks are + +uint8_t reset = 1; +uint8_t busreq = 0; +uint8_t busack = 0; +uint32_t busack_cycle = CYCLE_NEVER; +uint8_t new_busack = 0; + +void io_adjust_cycles(io_port * pad, uint32_t current_cycle, uint32_t deduction) +{ + uint8_t control = pad->control | 0x80; + uint8_t th = control & pad->output; + if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { + printf("adjust_cycles | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, current_cycle); + } + if (current_cycle >= pad->timeout_cycle) { + pad->th_counter = 0; + } else { + pad->timeout_cycle -= deduction; + } + if (busack_cycle < CYCLE_NEVER && current_cycle < busack_cycle) { + busack_cycle -= deduction; + } +} + +void io_data_write(io_port * pad, m68k_context * context, uint8_t value) +{ + if (pad->control & TH) { + //check if TH has changed + if ((pad->output & TH) ^ (value & TH)) { + if (context->current_cycle >= pad->timeout_cycle) { + pad->th_counter = 0; + } + if (!(value & TH)) { + pad->th_counter++; + } + pad->timeout_cycle = context->current_cycle + TH_TIMEOUT; + } + } + pad->output = value; +} + +void io_data_read(io_port * pad, m68k_context * context) +{ + uint8_t control = pad->control | 0x80; + uint8_t th = control & pad->output; + uint8_t input; + if (context->current_cycle >= pad->timeout_cycle) { + pad->th_counter = 0; + } + if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { + printf("io_data_read | control: %X, TH: %X, GAMEPAD_TH0: %X, GAMEPAD_TH1: %X, TH Counter: %d, Timeout: %d, Cycle: %d\n", control, th, pad->input[GAMEPAD_TH0], pad->input[GAMEPAD_TH1], pad->th_counter,pad->timeout_cycle, context->current_cycle); + } + if (th) { + if (pad->th_counter == 2) { + input = pad->input[GAMEPAD_EXTRA]; + } else { + input = pad->input[GAMEPAD_TH1]; + } + } else { + if (pad->th_counter == 2) { + input = pad->input[GAMEPAD_TH0] | 0xF; + } else if(pad->th_counter == 3) { + input = pad->input[GAMEPAD_TH0] & 0x30; + } else { + input = pad->input[GAMEPAD_TH0]; + } + } + context->value = ((~input) & (~control)) | (pad->output & control); + /*if (pad->input[GAMEPAD_TH0] || pad->input[GAMEPAD_TH1]) { + printf ("value: %X\n", context->value); + }*/ +} + +m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value) +{ + if (location < 0x100) { + switch(location/2) + { + case 0x1: + io_data_write(&gamepad_1, context, value); + break; + case 0x2: + io_data_write(&gamepad_2, context, value); + break; + case 0x3://PORT C Data + break; + case 0x4: + gamepad_1.control = value; + break; + case 0x5: + gamepad_2.control = value; + break; + } + } else { + if (location == 0x1100) { + if (busack_cycle > context->current_cycle) { + busack = new_busack; + busack_cycle = CYCLE_NEVER; + } + if (value & 1) { + busreq = 1; + if(!reset) { + busack_cycle = context->current_cycle + Z80_ACK_DELAY; + new_busack = 1; + } + } else { + busreq = 0; + busack_cycle = CYCLE_NEVER; + busack = 0; + } + } else if (location == 0x1200) { + if (value & 1) { + if (reset && busreq) { + new_busack = 1; + busack_cycle = context->current_cycle + Z80_ACK_DELAY; + } + reset = 0; + } else { + busack = 0; + reset = 1; + } + } + } + return context; +} + +m68k_context * io_write_w(uint32_t location, m68k_context * context, uint16_t value) +{ + if (location < 0x100) { + switch(location/2) + { + case 0x1: + io_data_write(&gamepad_1, context, value); + break; + case 0x2: + io_data_write(&gamepad_2, context, value); + break; + case 0x3://PORT C Data + break; + case 0x4: + gamepad_1.control = value; + break; + case 0x5: + gamepad_2.control = value; + break; + } + } else { + printf("IO Write of %X to %X\n", value, location); + if (location == 0x1100) { + if (busack_cycle > context->current_cycle) { + busack = new_busack; + busack_cycle = CYCLE_NEVER; + } + if (value & 0x100) { + busreq = 1; + if(!reset) { + busack_cycle = context->current_cycle + Z80_ACK_DELAY; + new_busack = 1; + } + } else { + busreq = 0; + busack_cycle = CYCLE_NEVER; + busack = 0; + } + } else if (location == 0x1200) { + if (value & 0x100) { + if (reset && busreq) { + new_busack = 1; + busack_cycle = context->current_cycle + Z80_ACK_DELAY; + } + reset = 0; + } else { + busack = 0; + reset = 1; + } + } + } + return context; +} + +m68k_context * io_read(uint32_t location, m68k_context * context) +{ + if (location < 0x100) { + switch(location/2) + { + case 0x0: + //version bits should be 0 for now since we're not emulating TMSS + //Not sure about the other bits + context->value = 0; + break; + case 0x1: + io_data_read(&gamepad_1, context); + break; + case 0x2: + io_data_read(&gamepad_2, context); + break; + case 0x3://PORT C Data + break; + case 0x4: + context->value = gamepad_1.control; + break; + case 0x5: + context->value = gamepad_2.control; + break; + } + } else { + if (location == 0x1100) { + if (busack_cycle > context->current_cycle) { + busack = new_busack; + busack_cycle = CYCLE_NEVER; + } + context->value = (!reset) && busack; + printf("Byte read of BUSREQ returned %d\n", context->value); + } else if (location == 0x1200) { + context->value = !reset; + } else { + printf("Byte read of unknown IO location: %X\n", location); + } + } + return context; +} + +m68k_context * io_read_w(uint32_t location, m68k_context * context) +{ + if (location < 0x100) { + switch(location/2) + { + case 0x0: + //version bits should be 0 for now since we're not emulating TMSS + //Not sure about the other bits + context->value = 0; + break; + case 0x1: + io_data_read(&gamepad_1, context); + break; + case 0x2: + io_data_read(&gamepad_2, context); + break; + case 0x3://PORT C Data + break; + case 0x4: + context->value = gamepad_1.control; + break; + case 0x5: + context->value = gamepad_2.control; + break; + case 0x6: + //PORT C Control + context->value = 0; + break; + } + context->value = context->value | (context->value << 8); + printf("Word read to %X returned %d\n", location, context->value); + } else { + if (location == 0x1100) { + if (busack_cycle > context->current_cycle) { + busack = new_busack; + busack_cycle = CYCLE_NEVER; + } + context->value = ((!reset) && busack) << 8; + printf("Word read of BUSREQ returned %d\n", context->value); + } else if (location == 0x1200) { + context->value = (!reset) << 8; + } else { + printf("Word read of unknown IO location: %X\n", location); + } + } + return context; +} + +int main(int argc, char ** argv) +{ + if (argc < 2) { + fputs("Usage: blastem FILENAME\n", stderr); + return 1; + } + if(!load_rom(argv[1])) { + fprintf(stderr, "Failed to open %s for reading\n", argv[1]); + return 1; + } + int width = 320; + int height = 240; + if (argc > 2) { + width = atoi(argv[2]); + if (argc > 3) { + height = atoi(argv[3]); + } else { + height = (width/320) * 240; + } + } + render_init(width, height); + size_t size = 1024 * 1024; + uint8_t * transbuf = alloc_code(&size); + + x86_68k_options opts; + m68k_context context; + vdp_context v_context; + + init_x86_68k_opts(&opts); + init_68k_context(&context, opts.native_code_map, &opts); + init_vdp_context(&v_context); + context.next_context = &v_context; + //cartridge ROM + context.mem_pointers[0] = cart; + context.target_cycle = context.sync_cycle = MCLKS_PER_FRAME/MCLKS_PER_68K; + //work RAM + context.mem_pointers[1] = ram; + uint32_t address; + address = cart[0x68/2] << 16 | cart[0x6A/2]; + uint8_t * end = transbuf + size; + transbuf = translate_m68k_stream(transbuf, end, address, &context); + address = cart[0x70/2] << 16 | cart[0x72/2]; + transbuf = translate_m68k_stream(transbuf, end, address, &context); + address = cart[0x78/2] << 16 | cart[0x7A/2]; + transbuf = translate_m68k_stream(transbuf, end, address, &context); + address = cart[2] << 16 | cart[3]; + translate_m68k_stream(transbuf, end, address, &context); + m68k_reset(&context); + return 0; +}