Mercurial > repos > blastem
view blastem.c @ 103:a71544cd01ea
Don't pre-emptively translate code at interrupt vectors as some PD ROMs have these pointing at junk. Need some kind of heuristic for detecting garbage if I'm going to translate them ahead of time by default.
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 27 Dec 2012 22:48:54 -0800 |
parents | dd3c680c618c |
children | 11d72e471104 |
line wrap: on
line source
#include "68kinst.h" #include "m68k_to_x86.h" #include "mem.h" #include "vdp.h" #include "render.h" #include "blastem.h" #include <stdio.h> #include <stdlib.h> #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] | 0xC; } } 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); 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]; translate_m68k_stream(address, &context); address = cart[0x70/2] << 16 | cart[0x72/2]; translate_m68k_stream(address, &context); address = cart[0x78/2] << 16 | cart[0x7A/2]; translate_m68k_stream(address, &context);*/ address = cart[2] << 16 | cart[3]; translate_m68k_stream(address, &context); m68k_reset(&context); return 0; }