# HG changeset patch # User Michael Pavone # Date 1478330591 25200 # Node ID faa3a4617f62ac08ef4388017e2b808cef2b242a # Parent 1ab30d427db8a910fe70bbd0043c870860407874 Get Jaguar video interrupt working diff -r 1ab30d427db8 -r faa3a4617f62 68kinst.h --- a/68kinst.h Mon Oct 31 18:41:42 2016 -0700 +++ b/68kinst.h Sat Nov 05 00:23:11 2016 -0700 @@ -329,7 +329,8 @@ VECTOR_TRAP_12, VECTOR_TRAP_13, VECTOR_TRAP_14, - VECTOR_TRAP_15 + VECTOR_TRAP_15, + VECTOR_USER0 = 64 } m68k_vector; typedef int (*format_label_fun)(char * dst, uint32_t address, void * data); diff -r 1ab30d427db8 -r faa3a4617f62 blastem.c --- a/blastem.c Mon Oct 31 18:41:42 2016 -0700 +++ b/blastem.c Sat Nov 05 00:23:11 2016 -0700 @@ -171,7 +171,7 @@ } } if (context->int_cycle > context->current_cycle && context->int_pending == INT_PENDING_SR_CHANGE) { - context->int_pending = 0; + context->int_pending = INT_PENDING_NONE; } /*if (context->int_cycle != old_int_cycle) { printf("int cycle changed to: %d, level: %d @ %d(%d), frame: %d, vcounter: %d, hslot: %d, mask: %d, hint_counter: %d\n", context->int_cycle, context->int_num, v_context->cycles, context->current_cycle, v_context->frame, v_context->vcounter, v_context->hslot, context->status & 0x7, v_context->hint_counter); diff -r 1ab30d427db8 -r faa3a4617f62 jag_video.c --- a/jag_video.c Mon Oct 31 18:41:42 2016 -0700 +++ b/jag_video.c Sat Nov 05 00:23:11 2016 -0700 @@ -187,6 +187,27 @@ OBJ_STOP }; +uint32_t jag_cycles_to_halfline(jag_video *context, uint32_t target) +{ + uint32_t cycles = context->regs[VID_HPERIOD] - (context->regs[VID_HCOUNT & 0x3FF]); + uint32_t num_lines; + if (context->regs[VID_VCOUNT] < target) { + num_lines = target - 1 - context->regs[VID_VCOUNT]; + } else { + num_lines = target + context->regs[VID_VPERIOD] - context->regs[VID_VCOUNT]; + } + cycles += num_lines * context->regs[VID_HPERIOD]; + return cycles; +} + +uint32_t jag_next_vid_interrupt(jag_video *context) +{ + if (context->regs[VID_VINT] > context->regs[VID_VPERIOD]) { + return 0xFFFFFFF; + } + return context->cycles + jag_cycles_to_halfline(context, context->regs[VID_VINT]); +} + void op_run(jag_video *context) { while (context->op.cycles < context->cycles) @@ -547,6 +568,9 @@ context->regs[VID_VCOUNT] = 0; } else { context->regs[VID_VCOUNT]++; + if (context->regs[VID_VCOUNT] == context->regs[VID_VINT]) { + context->cpu_int_pending |= BIT_CPU_VID_INT_ENABLED; + } } } else { context->regs[VID_HCOUNT]++; diff -r 1ab30d427db8 -r faa3a4617f62 jag_video.h --- a/jag_video.h Mon Oct 31 18:41:42 2016 -0700 +++ b/jag_video.h Sat Nov 05 00:23:11 2016 -0700 @@ -96,6 +96,7 @@ uint8_t pclock_div; uint8_t pclock_counter; uint8_t mode; + uint8_t cpu_int_pending; object_processor op; @@ -105,5 +106,6 @@ jag_video *jag_video_init(void); void jag_video_run(jag_video *context, uint32_t target_cycle); void jag_video_reg_write(jag_video *context, uint32_t address, uint16_t value); +uint32_t jag_next_vid_interrupt(jag_video *context); #endif //JAG_VIDEO_H_ diff -r 1ab30d427db8 -r faa3a4617f62 jaguar.c --- a/jaguar.c Mon Oct 31 18:41:42 2016 -0700 +++ b/jaguar.c Sat Nov 05 00:23:11 2016 -0700 @@ -3,6 +3,7 @@ #include #include #include "m68k_core.h" +#include "68kinst.h" #include "jaguar.h" #include "util.h" #include "debug.h" @@ -58,6 +59,38 @@ { } +void jag_update_m68k_int(jaguar_context *system) +{ + m68k_context *m68k = system->m68k; + if (m68k->sync_cycle - m68k->current_cycle > system->max_cycles) { + m68k->sync_cycle = m68k->current_cycle + system->max_cycles; + } + //TODO: Support other interrupt sources + if (!system->cpu_int_control || (m68k->status & 0x7)) { + m68k->int_cycle = CYCLE_NEVER; + } else if(system->cpu_int_control & system->video->cpu_int_pending) { + m68k->int_cycle = m68k->current_cycle; + //supposedly all interrupts on the jaguar are "level 0" autovector interrupts + //which I assume means they're abusing the "spurious interrupt" vector + m68k->int_num = VECTOR_USER0 - VECTOR_SPURIOUS_INTERRUPT; + } else { + m68k->int_cycle = jag_next_vid_interrupt(system->video); + m68k->int_num = VECTOR_USER0 - VECTOR_SPURIOUS_INTERRUPT; + } + + if (m68k->int_cycle > m68k->current_cycle && m68k->int_pending == INT_PENDING_SR_CHANGE) { + m68k->int_pending = INT_PENDING_NONE; + } + + m68k->target_cycle = m68k->int_cycle < m68k->sync_cycle ? m68k->int_cycle : m68k->sync_cycle; + if (m68k->should_return) { + m68k->target_cycle = m68k->current_cycle; + } else if (m68k->target_cycle < m68k->current_cycle) { + //Changes to SR can result in an interrupt cycle that's in the past + //This can cause issues with the implementation of STOP though + m68k->target_cycle = m68k->current_cycle; + } +} void rom0_write_16(uint32_t address, jaguar_context *system, uint16_t value) { @@ -111,8 +144,19 @@ case 2: system->memcon2 = value; break; + case 0xE0: + printf("INT1 write: %X\n", value); + system->cpu_int_control = value & 0x1F; + system->video->cpu_int_pending &= ~(value >> 8); + //TODO: apply mask to int pending fields on other components once they are implemented + break; + case 0xE2: + //no real handling of bus conflicts presently, so this doesn't really need to do anything yet + printf("INT2 write: %X\n", value); + break; default: jag_video_reg_write(system->video, address, value); + jag_update_m68k_int(system); break; } } else if (address < 0x100800) { @@ -144,7 +188,15 @@ fprintf(stderr, "Unhandled write to GPU registers %X: %X\n", address, value); if (address == 0x102116 && (value & 1)) { FILE *f = fopen("gpu.bin", "wb"); - fwrite(system->gpu_local, 1, sizeof(system->gpu_local), f); + uint8_t buf[4]; + for (int i = 0; i < GPU_RAM_BYTES/sizeof(uint32_t); i++) + { + buf[0] = system->gpu_local[i] >> 24; + buf[1] = system->gpu_local[i] >> 16; + buf[2] = system->gpu_local[i] >> 8; + buf[3] = system->gpu_local[i]; + fwrite(buf, 1, sizeof(buf), f); + } fclose(f); } } else { @@ -154,7 +206,7 @@ } else if (address < 0x11A100) { if (address < 0x110000) { //GPU Local RAM - uint32_t offset = address >> 2 & (GPU_RAM_BYTES / sizeof(uint32_t) - 1); + uint32_t offset = address >> 2 & (GPU_RAM_BYTES / sizeof(uint32_t) - 1); uint32_t value32 = value; if (address & 2) { system->gpu_local[offset] &= 0xFFFF0000; @@ -201,7 +253,16 @@ if (address < 0x101000) { if (address < 0x100400) { //Video mode / Memory control registers - fprintf(stderr, "Unhandled read from video mode/memory control registers - %X\n", address); + switch (address & 0x3FE) + { + case 0xE0: + puts("INT1 read"); + //TODO: Bitwise or with cpu_int_pending fields from other components once implemented + return system->video->cpu_int_pending; + break; + default: + fprintf(stderr, "Unhandled read from video mode/memory control registers - %X\n", address); + } } else if (address < 0x100800) { //CLUT address = address >> 1 & 255; @@ -377,10 +438,17 @@ { jaguar_context *system = context->system; jag_video_run(system->video, context->current_cycle); + jag_update_m68k_int(system); if (context->current_cycle > 0x10000000) { context->current_cycle -= 0x10000000; system->video->cycles -= 0x10000000; } + if (context->int_ack) { + context->int_ack = 0; + //hack until 68K core more properly supports non-autovector interrupts + context->status |= 1; + } + jag_update_m68k_int(system); return context; } @@ -431,6 +499,8 @@ system->bios_size = bios_size; system->cart = cart; system->cart_size = cart_size; + //TODO: Figure out a better default for this and make it configurable + system->max_cycles = 3000; memmap_chunk *jag_m68k_map = calloc(8, sizeof(memmap_chunk)); for (uint32_t start = 0, index=0; index < 8; index++, start += 0x200000) @@ -450,6 +520,7 @@ m68k_options *opts = malloc(sizeof(m68k_options)); init_m68k_opts(opts, jag_m68k_map, 8, 2); system->m68k = init_68k_context(opts, handle_m68k_reset); + system->m68k->sync_cycle = system->max_cycles; system->m68k->system = system; system->video = jag_video_init(); system->video->system = system; diff -r 1ab30d427db8 -r faa3a4617f62 jaguar.h --- a/jaguar.h Mon Oct 31 18:41:42 2016 -0700 +++ b/jaguar.h Sat Nov 05 00:23:11 2016 -0700 @@ -17,6 +17,8 @@ uint32_t memcon1; uint32_t memcon2; uint32_t rom_cycles; + uint32_t max_cycles; + uint16_t cpu_int_control; uint16_t write_latch; uint8_t write_pending; @@ -27,6 +29,8 @@ uint8_t memcon_written; } jaguar_context; +#define BIT_CPU_VID_INT_ENABLED 0x01 + uint64_t jag_read_phrase(jaguar_context *system, uint32_t address, uint32_t *cycles); uint32_t jag_write_phrase(jaguar_context *system, uint32_t address, uint64_t value); diff -r 1ab30d427db8 -r faa3a4617f62 m68k_core.h --- a/m68k_core.h Mon Oct 31 18:41:42 2016 -0700 +++ b/m68k_core.h Sat Nov 05 00:23:11 2016 -0700 @@ -18,7 +18,8 @@ #define M68K_OPT_BROKEN_READ_MODIFY 1 -#define INT_PENDING_SR_CHANGE 8 +#define INT_PENDING_SR_CHANGE 254 +#define INT_PENDING_NONE 255 typedef void (*start_fun)(uint8_t * addr, void * context); diff -r 1ab30d427db8 -r faa3a4617f62 m68k_core_x86.c --- a/m68k_core_x86.c Mon Oct 31 18:41:42 2016 -0700 +++ b/m68k_core_x86.c Sat Nov 05 00:23:11 2016 -0700 @@ -2762,7 +2762,7 @@ code->stack_off = tmp_stack_off; *do_int = code->cur - (do_int+1); //implement 1 instruction latency - cmp_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); + cmp_irdisp(code, INT_PENDING_NONE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); do_int = code->cur + 1; jcc(code, CC_NZ, do_int); //store current interrupt number so it doesn't change before we start processing the vector @@ -2824,6 +2824,8 @@ //update status register and_irdisp(code, 0xF8, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B); mov_rdispr(code, opts->gen.context_reg, offsetof(m68k_context, int_num), opts->gen.scratch1, SZ_B); + //need to separate int priority and interrupt vector, but for now mask out large interrupt numbers + and_ir(code, 0x7, opts->gen.scratch1, SZ_B); or_ir(code, 0x20, opts->gen.scratch1, SZ_B); or_rrdisp(code, opts->gen.scratch1, opts->gen.context_reg, offsetof(m68k_context, status), SZ_B); @@ -2843,7 +2845,7 @@ shl_ir(code, 2, opts->gen.scratch1, SZ_D); add_ir(code, 0x60, opts->gen.scratch1, SZ_D); //clear out pending flag - mov_irdisp(code, 0, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); + mov_irdisp(code, INT_PENDING_NONE, opts->gen.context_reg, offsetof(m68k_context, int_pending), SZ_B); //read vector call(code, opts->read_32); call(code, opts->native_addr_and_sync);