mirror of
https://github.com/libretro/blastem.git
synced 2026-11-07 10:15:58 -10:00
Get Jaguar video interrupt working
This commit is contained in:
1 parent
e895e82ba2
commit
dd260c76cd
8 files changed
+113
-8
No files matched your search
@@ -329,7 +329,8 @@ typedef enum {
|
||||
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);
|
||||
|
||||
@@ -171,7 +171,7 @@ void adjust_int_cycle(m68k_context * context, vdp_context * v_context)
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
+24
@@ -187,6 +187,27 @@ enum {
|
||||
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 @@ void jag_video_run(jag_video *context, uint32_t target_cycle)
|
||||
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]++;
|
||||
|
||||
@@ -96,6 +96,7 @@ typedef struct {
|
||||
uint8_t pclock_div;
|
||||
uint8_t pclock_counter;
|
||||
uint8_t mode;
|
||||
uint8_t cpu_int_pending;
|
||||
|
||||
object_processor op;
|
||||
|
||||
@@ -105,5 +106,6 @@ typedef struct {
|
||||
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_
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include "m68k_core.h"
|
||||
#include "68kinst.h"
|
||||
#include "jaguar.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
@@ -58,6 +59,38 @@ void handle_mouse_moved(int mouse, uint16_t x, uint16_t y, int16_t deltax, int16
|
||||
{
|
||||
}
|
||||
|
||||
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 @@ void rom0_write_16(uint32_t address, jaguar_context *system, uint16_t value)
|
||||
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 @@ void rom0_write_16(uint32_t address, jaguar_context *system, uint16_t value)
|
||||
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 @@ void rom0_write_16(uint32_t address, jaguar_context *system, uint16_t value)
|
||||
} 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 @@ uint16_t rom0_read_16(uint32_t address, jaguar_context *system)
|
||||
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 @@ m68k_context * sync_components(m68k_context * context, uint32_t address)
|
||||
{
|
||||
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 @@ jaguar_context *init_jaguar(uint16_t *bios, uint32_t bios_size, uint16_t *cart,
|
||||
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 @@ jaguar_context *init_jaguar(uint16_t *bios, uint32_t bios_size, uint16_t *cart,
|
||||
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;
|
||||
|
||||
@@ -17,6 +17,8 @@ typedef struct {
|
||||
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 @@ typedef struct {
|
||||
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);
|
||||
|
||||
|
||||
+2
-1
@@ -18,7 +18,8 @@ struct m68kinst;
|
||||
|
||||
#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);
|
||||
|
||||
|
||||
+4
-2
@@ -2762,7 +2762,7 @@ void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chu
|
||||
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 @@ void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chu
|
||||
//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 @@ void init_m68k_opts(m68k_options * opts, memmap_chunk * memmap, uint32_t num_chu
|
||||
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);
|
||||
|
||||
Reference in new issue
Block a user