summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--68kinst.c6
-rw-r--r--Makefile5
-rw-r--r--m68k_to_x86.c74
-rw-r--r--m68k_to_x86.h4
-rw-r--r--render.h1
-rw-r--r--render_sdl.c39
-rw-r--r--runtime.S83
-rw-r--r--vdp.c214
-rw-r--r--vdp.h18
9 files changed, 420 insertions, 24 deletions
diff --git a/68kinst.c b/68kinst.c
index 257aa0b..4adc66f 100644
--- a/68kinst.c
+++ b/68kinst.c
@@ -1223,7 +1223,11 @@ int m68k_disasm(m68kinst * decoded, char * dst)
strcpy(dst+ret, cond_mnem[decoded->extra.cond]);
ret = strlen(dst);
if (decoded->op != M68K_SCC) {
- ret += sprintf(dst+ret, " #%d <%X>", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed);
+ if (decoded->op == M68K_DBCC) {
+ ret += sprintf(dst+ret, " d%d, #%d <%X>", decoded->dst.params.regs.pri, decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed);
+ } else {
+ ret += sprintf(dst+ret, " #%d <%X>", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed);
+ }
return ret;
}
break;
diff --git a/Makefile b/Makefile
index 2b5554e..b2b6d88 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,9 @@
LIBS=sdl
-all : dis trans stateview
+all : dis trans stateview blastem
+
+blastem : blastem.o 68kinst.o gen_x86.o m68k_to_x86.o runtime.o mem.o vdp.o render_sdl.o
+ $(CC) -o blastem blastem.o 68kinst.o gen_x86.o m68k_to_x86.o runtime.o mem.o vdp.o render_sdl.o `pkg-config --libs $(LIBS)`
dis : dis.o 68kinst.o
$(CC) -o dis dis.o 68kinst.o
diff --git a/m68k_to_x86.c b/m68k_to_x86.c
index 15ac69d..1f69dc5 100644
--- a/m68k_to_x86.c
+++ b/m68k_to_x86.c
@@ -151,6 +151,30 @@ uint8_t * translate_m68k_src(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68
ea->mode = MODE_REG_DIRECT;
ea->base = SCRATCH1;
break;
+ case MODE_ABSOLUTE:
+ case MODE_ABSOLUTE_SHORT:
+ if (inst->src.addr_mode == MODE_ABSOLUTE) {
+ out = cycles(out, BUS*2);
+ } else {
+ out = cycles(out, BUS);
+ }
+ out = mov_ir(out, inst->src.params.immed, SCRATCH1, SZ_D);
+ out = check_cycles(out);
+ switch (inst->extra.size)
+ {
+ case OPSIZE_BYTE:
+ out = call(out, (char *)m68k_read_byte_scratch1);
+ break;
+ case OPSIZE_WORD:
+ out = call(out, (char *)m68k_read_word_scratch1);
+ break;
+ case OPSIZE_LONG:
+ out = call(out, (char *)m68k_read_long_scratch1);
+ break;
+ }
+ ea->mode = MODE_REG_DIRECT;
+ ea->base = SCRATCH1;
+ break;
case MODE_IMMEDIATE:
if (inst->variant != VAR_QUICK) {
if (inst->extra.size == OPSIZE_LONG) {
@@ -332,6 +356,10 @@ uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options *
x86_ea src;
dst = translate_m68k_src(inst, &src, dst, opts);
reg = native_reg(&(inst->dst), opts);
+ //update statically set flags
+ dst = mov_ir(dst, 0, FLAG_V, SZ_B);
+ dst = mov_ir(dst, 0, FLAG_C, SZ_B);
+
if (src.mode == MODE_REG_DIRECT) {
flags_reg = src.base;
} else {
@@ -362,6 +390,9 @@ uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options *
} else {
dst = mov_irdisp8(dst, src.disp, CONTEXT, reg_offset(&(inst->dst)), inst->extra.size);
}
+ dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
+ dst = setcc_r(dst, CC_Z, FLAG_Z);
+ dst = setcc_r(dst, CC_S, FLAG_N);
break;
case MODE_AREG_PREDEC:
dec_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : 1);
@@ -386,6 +417,9 @@ uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options *
} else {
dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
}
+ dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
+ dst = setcc_r(dst, CC_Z, FLAG_Z);
+ dst = setcc_r(dst, CC_S, FLAG_N);
switch (inst->extra.size)
{
case OPSIZE_BYTE:
@@ -407,6 +441,40 @@ uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options *
}
}
break;
+ case MODE_ABSOLUTE:
+ case MODE_ABSOLUTE_SHORT:
+ if (src.mode == MODE_REG_DIRECT) {
+ if (src.base != SCRATCH1) {
+ dst = mov_rr(dst, src.base, SCRATCH1, inst->extra.size);
+ }
+ } else if (src.mode == MODE_REG_DISPLACE8) {
+ dst = mov_rdisp8r(dst, src.base, src.disp, SCRATCH1, inst->extra.size);
+ } else {
+ dst = mov_ir(dst, src.disp, SCRATCH1, inst->extra.size);
+ }
+ if (inst->dst.addr_mode == MODE_ABSOLUTE) {
+ dst = cycles(dst, BUS*2);
+ } else {
+ dst = cycles(dst, BUS);
+ }
+ dst = mov_ir(dst, inst->dst.params.immed, SCRATCH2, SZ_D);
+ dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
+ dst = setcc_r(dst, CC_Z, FLAG_Z);
+ dst = setcc_r(dst, CC_S, FLAG_N);
+ dst = check_cycles(dst);
+ switch (inst->extra.size)
+ {
+ case OPSIZE_BYTE:
+ dst = call(dst, (char *)m68k_write_byte);
+ break;
+ case OPSIZE_WORD:
+ dst = call(dst, (char *)m68k_write_word);
+ break;
+ case OPSIZE_LONG:
+ dst = call(dst, (char *)m68k_write_long_highfirst);
+ break;
+ }
+ break;
default:
printf("address mode %d not implemented (move dst)\n", inst->dst.addr_mode);
exit(1);
@@ -414,12 +482,6 @@ uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options *
//add cycles for prefetch
dst = cycles(dst, BUS);
- //update flags
- dst = mov_ir(dst, 0, FLAG_V, SZ_B);
- dst = mov_ir(dst, 0, FLAG_C, SZ_B);
- dst = cmp_ir(dst, 0, flags_reg, inst->extra.size);
- dst = setcc_r(dst, CC_Z, FLAG_Z);
- dst = setcc_r(dst, CC_S, FLAG_N);
dst = check_cycles(dst);
return dst;
}
diff --git a/m68k_to_x86.h b/m68k_to_x86.h
index 5dc04fd..f2b0a73 100644
--- a/m68k_to_x86.h
+++ b/m68k_to_x86.h
@@ -35,6 +35,8 @@ typedef struct {
uint32_t target_cycle;
uint32_t current_cycle;
uint16_t *mem_pointers[NUM_MEM_AREAS];
+ void *next_context;
+ uint16_t value;
native_map_slot *native_code_map;
void *options;
} m68k_context;
@@ -44,3 +46,5 @@ uint8_t * translate_m68k_stream(uint8_t * dst, uint8_t * dst_end, uint32_t addre
void start_68k_context(m68k_context * context, uint32_t address);
void init_x86_68k_opts(x86_68k_options * opts);
void init_68k_context(m68k_context * context, native_map_slot * native_code_map, void * opts);
+void m68k_reset(m68k_context * context);
+
diff --git a/render.h b/render.h
index eab718c..031a748 100644
--- a/render.h
+++ b/render.h
@@ -5,6 +5,7 @@
void render_init(int width, int height);
void render_context(vdp_context * context);
void render_wait_quit(vdp_context * context);
+void wait_render_frame(vdp_context * context);
#endif //RENDER_SDL_H_
diff --git a/render_sdl.c b/render_sdl.c
index d3e7ef9..49e545d 100644
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -6,6 +6,8 @@
SDL_Surface *screen;
uint8_t render_dbg = 0;
+uint32_t last_frame = 0;
+
void render_init(int width, int height)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
@@ -31,6 +33,7 @@ void render_context(vdp_context * context)
uint16_t *buf_16;
uint32_t *buf_32;
uint8_t b,g,r;
+ last_frame = SDL_GetTicks();
if (SDL_MUSTLOCK(screen)) {
if (SDL_LockSurface(screen) < 0) {
return;
@@ -43,7 +46,6 @@ void render_context(vdp_context * context)
} else {
repeat_y = repeat_x;
}
- printf("w: %d, h: %d, repeat_x: %d, repeat_y: %d\n", screen->clip_rect.w, screen->clip_rect.h, repeat_x, repeat_y);
switch (screen->format->BytesPerPixel) {
case 2:
buf_16 = (uint16_t *)screen->pixels;
@@ -151,3 +153,38 @@ void render_wait_quit(vdp_context * context)
}
}
+#define FRAME_DELAY 16
+#define MIN_DELAY 10
+
+void wait_render_frame(vdp_context * context)
+{
+ SDL_Event event;
+ while(SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ //TODO: Update emulated gamepads
+ if (event.key.keysym.sym == SDLK_LEFTBRACKET) {
+ render_dbg = !render_dbg;
+ render_context(context);
+ }
+ break;
+ case SDL_QUIT:
+ exit(0);
+ }
+ }
+ //TODO: Adjust frame delay so we actually get 60 FPS rather than 62.5 FPS
+ uint32_t current = SDL_GetTicks();
+ uint32_t desired = last_frame + FRAME_DELAY;
+ if (current < desired) {
+ uint32_t delay = last_frame + FRAME_DELAY - current;
+ //TODO: Calculate MIN_DELAY at runtime
+ if (delay > MIN_DELAY) {
+ SDL_Delay((delay/MIN_DELAY)*MIN_DELAY);
+ }
+ while ((desired) < SDL_GetTicks()) {
+ }
+ }
+ render_context(context);
+}
+
+
diff --git a/runtime.S b/runtime.S
index c4530c0..b9c91a0 100644
--- a/runtime.S
+++ b/runtime.S
@@ -1,15 +1,42 @@
.global handle_cycle_limit
handle_cycle_limit:
+ call m68k_save_context
+ mov %rsi, %rdi
+ call sync_components
+ mov %rax, %rsi
+ call m68k_load_context
+ ret
+
+do_vdp_port_write:
+ call m68k_save_context
+ mov %rcx, %rdx
+ call vdp_port_write
+ mov %rax, %rsi
+ call m68k_load_context
+ ret
+
+do_vdp_port_read:
+ call m68k_save_context
+ call vdp_port_read
+ mov %rax, %rsi
+ call m68k_load_context
+ mov 120(%rsi), %cx
ret
+bad_access_msg:
+ .asciz "Program tried to access illegal 68K address %X\n"
+
.global m68k_write_word
+ .global vdp_psg_w
m68k_write_word:
and $0xFFFFFF, %rdi
cmp $0x400000, %edi
jle cart_w
cmp $0xE00000, %edi
jge workram_w
+ cmp $0xC00000, %edi
+ jge vdp_psg_w
jmp inccycles
workram_w:
and $0xFFFF, %rdi
@@ -18,6 +45,48 @@ workram_w:
cart_w:
mov %cx, (%r8, %rdi)
jmp inccycles
+vdp_psg_w:
+ test $0x2700E0, %edi
+ jnz crash
+ and $0x1F, %edi
+ cmp $4, %edi
+ jl try_fifo_write
+ jmp do_vdp_port_write
+try_fifo_write:
+ push %rdx
+ push %rbx
+ /* fetch VDP context pointer from 68K context */
+ mov 112(%rsi), %rdx
+ /* get fifo_cur and compare it to fifo_end */
+ mov (%rdx), %rbx
+ cmp %rbx, 8(%rdx)
+ /* bail out if fifo is full */
+ je fifo_fallback
+ /* populate FIFO entry */
+ mov %cx, 4(%rbx) /* value */
+ movb $0, 6(%rbx) /* partial */
+ mov %eax, %ecx
+ shl $3, %ecx /* multiply by 68K cycle by 7 to get MCLK cycle */
+ sub %eax, %ecx
+ mov %ecx, (%rbx) /* cycle */
+ /* update fifo_cur and store back in 68K context */
+ add $8, %rbx
+ mov %rbx, (%rdx)
+ /* clear pending flag */
+ andb $0xEF, 19(%rdx)
+ pop %rbx
+ pop %rdx
+ jmp inccycles
+fifo_fallback:
+ pop %rbx
+ pop %rdx
+ jmp do_vdp_port_write
+crash:
+ mov %edi, %esi
+ lea bad_access_msg(%rip), %rdi
+ call printf
+ mov $1, %rdi
+ call exit
.global m68k_write_byte
m68k_write_byte:
@@ -64,20 +133,26 @@ m68k_read_word_scratch1:
jle cart
cmp $0xE00000, %ecx
jge workram
+ cmp $0xC00000, %edi
+ jge vdp_psg
xor %cx, %cx
+ dec %cx
jmp inccycles
workram:
and $0xFFFF, %rcx
mov (%r9, %rcx), %cx
jmp inccycles
+vdp_psg:
+ test $0x2700E0, %edi
+ jnz crash
+ and $0x1F, %edi
+ jmp do_vdp_port_read
cart:
mov (%r8, %rcx), %cx
inccycles:
add $4, %rax
cmp %rbp, %rax
- jge sync
- ret
-sync:
+ jge handle_cycle_limit
ret
.global m68k_read_long_scratch1
@@ -103,6 +178,7 @@ m68k_read_byte_scratch1:
cmp $0xE00000, %ecx
jge workram_b
xor %cl, %cl
+ dec %cl
jmp inccycles
workram_b:
and $0xFFFF, %rcx
@@ -146,6 +222,7 @@ m68k_save_context:
mov %r13d, 40(%rsi) /* a0 */
mov %r14d, 44(%rsi) /* a1 */
mov %r15d, 68(%rsi) /* a7 */
+ mov %eax, 76(%rsi) /* current cycle count */
ret
.global m68k_load_context
diff --git a/vdp.c b/vdp.c
index 5453f7d..edb3332 100644
--- a/vdp.c
+++ b/vdp.c
@@ -2,7 +2,6 @@
#include <stdlib.h>
#include <string.h>
-#define MCLKS_LINE 3420
#define NTSC_ACTIVE 225
#define PAL_ACTIVE 241
#define BUF_BIT_PRIORITY 0x40
@@ -20,16 +19,24 @@
#define FLAG_CAN_MASK 0x2
#define FLAG_MASKED 0x4
#define FLAG_WINDOW 0x8
+#define FLAG_PENDING 0x10
+#define FLAG_UNUSED_SLOT 0x20
+
+#define FIFO_SIZE 4
void init_vdp_context(vdp_context * context)
{
memset(context, 0, sizeof(context));
context->vdpmem = malloc(VRAM_SIZE);
context->framebuf = malloc(FRAMEBUF_SIZE);
+ memset(context->framebuf, 0, FRAMEBUF_SIZE);
context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
+ memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2);
context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;
context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE;
context->sprite_draws = MAX_DRAWS;
+ context->fifo_cur = malloc(sizeof(fifo_entry) * FIFO_SIZE);
+ context->fifo_end = context->fifo_cur + FIFO_SIZE;
}
void render_sprite_cells(vdp_context * context)
@@ -168,9 +175,52 @@ void read_sprite_x(uint32_t line, vdp_context * context)
}
}
+#define VRAM_READ 0
+#define VRAM_WRITE 1
+#define CRAM_READ 8
+#define CRAM_WRITE 3
+#define VSRAM_READ 4
+#define VSRAM_WRITE 5
+
void external_slot(vdp_context * context)
{
- //TODO: Implement me
+ fifo_entry * start = (context->fifo_end - FIFO_SIZE);
+ //TODO: Implement DMA
+ if (context->fifo_cur != start && start->cycle <= context->cycles) {
+ switch (context->cd & 0x7)
+ {
+ case VRAM_WRITE:
+ if (start->partial) {
+ printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1);
+ context->vdpmem[context->address ^ 1] = start->value;
+ } else {
+ printf("VRAM Write: %X to %X\n", start->value >> 8, context->address);
+ context->vdpmem[context->address] = start->value >> 8;
+ start->partial = 1;
+ //skip auto-increment and removal of entry from fifo
+ return;
+ }
+ break;
+ case CRAM_WRITE:
+ printf("CRAM Write: %X to %X\n", start->value, context->address);
+ context->cram[context->address & (CRAM_SIZE-1)] = start->value;
+ break;
+ case VSRAM_WRITE:
+ if ((context->address & 63) < VSRAM_SIZE) {
+ printf("VSRAM Write: %X to %X\n", start->value, context->address);
+ context->vsram[context->address & 63] = start->value;
+ }
+ break;
+ }
+ context->address += context->regs[REG_AUTOINC];
+ fifo_entry * cur = start+1;
+ if (cur < context->fifo_cur) {
+ memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur));
+ }
+ context->fifo_cur -= 1;
+ } else {
+ context->flags |= FLAG_UNUSED_SLOT;
+ }
}
#define WINDOW_RIGHT 0x80
@@ -213,7 +263,7 @@ void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_
}
offset = address + line_offset + (((column - 2) * 2) & mask);
context->col_1 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
- printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
+ //printf("Window | top: %d, bot: %d, left: %d, right: %d, base: %X, line: %X offset: %X, tile: %X, reg: %X\n", top_line, bottom_line, left_col, right_col, address, line_offset, offset, ((context->col_1 & 0x3FF) << 5), context->regs[REG_WINDOW]);
offset = address + line_offset + (((column - 1) * 2) & mask);
context->col_2 = (context->vdpmem[offset] << 8) | context->vdpmem[offset+1];
context->v_offset = (line) & 0x7;
@@ -524,7 +574,7 @@ void vdp_h40(uint32_t line, uint32_t linecyc, vdp_context * context)
address += line * 4;
context->hscroll_a = context->vdpmem[address] << 8 | context->vdpmem[address+1];
context->hscroll_b = context->vdpmem[address+2] << 8 | context->vdpmem[address+3];
- printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
+ //printf("%d: HScroll A: %d, HScroll B: %d\n", line, context->hscroll_a, context->hscroll_b);
break;
case 36:
//!HSYNC high
@@ -743,16 +793,58 @@ void latch_mode(vdp_context * context)
context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL);
}
+#define DISPLAY_ENABLE 0x40
+
+int is_refresh(vdp_context * context)
+{
+ uint32_t linecyc = context->cycles % MCLKS_LINE;
+ if (context->latched_mode & BIT_H40) {
+ linecyc = linecyc/16;
+ return (linecyc == 73 || linecyc == 105 || linecyc == 137 || linecyc == 169 || linecyc == 201);
+ } else {
+ linecyc = linecyc/20;
+ return (linecyc == 66 || linecyc == 98 || linecyc == 130 || linecyc == 162);
+ }
+}
+
+void check_render_bg(vdp_context * context, int32_t line)
+{
+ if (line > 0) {
+ line -= 1;
+ uint16_t * start = NULL, *end = NULL;
+ uint32_t linecyc = (context->cycles % MCLKS_LINE);
+ if (context->latched_mode & BIT_H40) {
+ linecyc /= 16;
+ if (linecyc >= 55 && linecyc <= 207 && !((linecyc-55) % 8)) {
+ uint32_t x = ((linecyc-55)&(~0xF))*2;
+ start = context->framebuf + line * 320 + x;
+ end = start + 16;
+ }
+ } else {
+ linecyc /= 20;
+ if (linecyc >= 48 && linecyc <= 168 && !((linecyc-48) % 8)) {
+ uint32_t x = ((linecyc-48)&(~0xF))*2;
+ start = context->framebuf + line * 256 + x;
+ end = start + 16;
+ }
+ }
+ while (start != end) {
+ *start = context->regs[REG_BG_COLOR] & 0x3F;
+ ++start;
+ }
+ }
+}
+
void vdp_run_context(vdp_context * context, uint32_t target_cycles)
{
while(context->cycles < target_cycles)
{
uint32_t line = context->cycles / MCLKS_LINE;
uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE;
- if (line < active_lines) {
- if (!line) {
- latch_mode(context);
- }
+ if (!line) {
+ latch_mode(context);
+ }
+ if (line < active_lines && context->regs[REG_MODE_2] & DISPLAY_ENABLE) {
//first sort-of active line is treated as 255 internally
//it's used for gathering sprite info for line
line = (line - 1) & 0xFF;
@@ -762,15 +854,26 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles)
if (context->latched_mode & BIT_H40){
//TODO: Deal with nasty clock switching during HBLANK
linecyc = linecyc/16;
- context->cycles += 16;
vdp_h40(line, linecyc, context);
+ context->cycles += 16;
} else {
linecyc = linecyc/20;
- context->cycles += 20;
vdp_h32(line, linecyc, context);
+ context->cycles += 20;
}
} else {
- //TODO: Empty FIFO
+ if (!is_refresh(context)) {
+ external_slot(context);
+ }
+ if (line < active_lines) {
+ check_render_bg(context, line);
+ }
+ if (context->latched_mode & BIT_H40){
+ //TODO: Deal with nasty clock switching during HBLANK
+ context->cycles += 16;
+ } else {
+ context->cycles += 20;
+ }
}
}
}
@@ -782,6 +885,94 @@ uint32_t vdp_run_to_vblank(vdp_context * context)
return context->cycles;
}
+void vdp_control_port_write(vdp_context * context, uint16_t value)
+{
+ printf("control port write: %X\n", value);
+ if (context->flags & FLAG_PENDING) {
+ context->address = (context->address & 0x3FFF) | (value << 14);
+ context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C);
+ context->flags &= ~FLAG_PENDING;
+ } else {
+ if ((value & 0xC000) == 0x8000) {
+ //Register write
+ uint8_t reg = (value >> 8) & 0x1F;
+ if (reg < VDP_REGS) {
+ printf("register %d set to %X\n", reg, value);
+ context->regs[reg] = value;
+ }
+ } else {
+ context->flags |= FLAG_PENDING;
+ context->address = (context->address &0xC000) | (value & 0x3FFF);
+ context->cd = (context->cd &0x3C) | (value >> 14);
+ }
+ }
+}
+
+void vdp_data_port_write(vdp_context * context, uint16_t value)
+{
+ printf("data port write: %X\n", value);
+ context->flags &= ~FLAG_PENDING;
+ if (context->fifo_cur == context->fifo_end) {
+ printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles);
+ }
+ while (context->fifo_cur == context->fifo_end) {
+ vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+ }
+ context->fifo_cur->cycle = context->cycles;
+ context->fifo_cur->value = value;
+ context->fifo_cur->partial = 0;
+ context->fifo_cur++;
+}
+
+uint16_t vdp_control_port_read(vdp_context * context)
+{
+ context->flags &= ~FLAG_PENDING;
+ uint16_t value = 0x3400;
+ if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) {
+ value |= 0x200;
+ }
+ if (context->fifo_cur == context->fifo_end) {
+ value |= 0x100;
+ }
+ //TODO: Lots of other bits in status port
+ return value;
+}
+
+uint16_t vdp_data_port_read(vdp_context * context)
+{
+ context->flags &= ~FLAG_PENDING;
+ if (!(context->cd & 1)) {
+ return 0;
+ }
+ //Not sure if the FIFO should be drained before processing a read or not, but it would make sense
+ context->flags &= ~FLAG_UNUSED_SLOT;
+ while (!(context->flags & FLAG_UNUSED_SLOT)) {
+ vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+ }
+ uint16_t value = 0;
+ switch (context->cd & 0x7)
+ {
+ case VRAM_READ:
+ value = context->vdpmem[context->address] << 8;
+ context->flags &= ~FLAG_UNUSED_SLOT;
+ while (!(context->flags & FLAG_UNUSED_SLOT)) {
+ vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20));
+ }
+ value |= context->vdpmem[context->address ^ 1];
+ break;
+ case CRAM_READ:
+ value = context->cram[(context->address/2) & (CRAM_SIZE-1)];
+ break;
+ case VSRAM_READ:
+ if (((context->address / 2) & 63) < VSRAM_SIZE) {
+ value = context->vsram[context->address & 63];
+ }
+ break;
+ }
+ context->address += context->regs[REG_AUTOINC];
+ return value;
+}
+
#define GST_VDP_REGS 0xFA
#define GST_VDP_MEM 0x12478
@@ -802,3 +993,4 @@ void vdp_load_savestate(vdp_context * context, FILE * state_file)
fseek(state_file, GST_VDP_MEM, SEEK_SET);
fread(context->vdpmem, 1, VRAM_SIZE, state_file);
}
+
diff --git a/vdp.h b/vdp.h
index 6c9bbf1..3016561 100644
--- a/vdp.h
+++ b/vdp.h
@@ -26,6 +26,8 @@
#define FBUF_SRC_S 0x6000
#define FBUF_SRC_BG 0x8000
+#define MCLKS_LINE 3420
+
enum {
REG_MODE_1=0,
REG_MODE_2,
@@ -58,6 +60,17 @@ typedef struct {
} sprite_info;
typedef struct {
+ uint32_t cycle;
+ uint16_t value;
+ uint8_t partial;
+} fifo_entry;
+
+typedef struct {
+ fifo_entry *fifo_cur;
+ fifo_entry *fifo_end;
+ uint16_t address;
+ uint8_t cd;
+ uint8_t flags;
//cycle count in MCLKs
uint32_t cycles;
uint8_t *vdpmem;
@@ -80,7 +93,6 @@ typedef struct {
uint16_t col_1;
uint16_t col_2;
uint8_t v_offset;
- uint8_t flags;
uint8_t *tmp_buf_a;
uint8_t *tmp_buf_b;
} vdp_context;
@@ -90,5 +102,9 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles);
//runs from current cycle count to VBLANK for the current mode, returns ending cycle count
uint32_t vdp_run_to_vblank(vdp_context * context);
void vdp_load_savestate(vdp_context * context, FILE * state_file);
+void vdp_control_port_write(vdp_context * context, uint16_t value);
+void vdp_data_port_write(vdp_context * context, uint16_t value);
+uint16_t vdp_control_port_read(vdp_context * context);
+uint16_t vdp_data_port_read(vdp_context * context);
#endif //VDP_H_