interrupt system, vblank, key input, don't know if any of it works

This commit is contained in:
Matthew Laux 2022-07-20 00:45:38 -05:00
parent 1b5f02ef7a
commit 5735d65470
5 changed files with 230 additions and 14 deletions

View File

@ -103,6 +103,54 @@ void fill_memory_editor(struct dmg *dmg)
}
}
struct key_input {
int scancode;
int button;
int field;
};
struct key_input key_inputs[] = {
{ SDL_SCANCODE_D, BUTTON_RIGHT, FIELD_JOY },
{ SDL_SCANCODE_A, BUTTON_LEFT, FIELD_JOY },
{ SDL_SCANCODE_W, BUTTON_UP, FIELD_JOY },
{ SDL_SCANCODE_S, BUTTON_DOWN, FIELD_JOY },
{ SDL_SCANCODE_L, BUTTON_A, FIELD_ACTION },
{ SDL_SCANCODE_K, BUTTON_B, FIELD_ACTION },
{ SDL_SCANCODE_N, BUTTON_SELECT, FIELD_ACTION },
{ SDL_SCANCODE_M, BUTTON_START, FIELD_ACTION },
{ 0 }
};
int scancode_to_joy[] = {
SDL_SCANCODE_D,
SDL_SCANCODE_A,
SDL_SCANCODE_W,
SDL_SCANCODE_S,
SDL_SCANCODE_L,
SDL_SCANCODE_K,
SDL_SCANCODE_N,
SDL_SCANCODE_M
};
int scancode_buttons[] = {
BUTTON_RIGHT,
BUTTON_LEFT,
BUTTON_UP,
BUTTON_DOWN,
BUTTON_A,
BUTTON_B,
BUTTON_SELECT,
BUTTON_START,
};
int scancode_fields[] = {
FIELD_JOY,
FIELD_JOY,
FIELD_JOY,
FIELD_JOY,
FIELD_ACTION,
FIELD_ACTION,
FIELD_ACTION,
FIELD_ACTION,
};
// Main code
int main(int argc, char *argv[])
{
@ -112,6 +160,8 @@ int main(int argc, char *argv[])
struct lcd lcd = { 0 };
int executed;
int paused = 0;
int pause_next = 0;
if (argc < 2) {
printf("no rom specified\n");
@ -206,7 +256,12 @@ int main(int argc, char *argv[])
unsigned int lastDrawTime = 0, currentTime;
while (!done)
{
dmg_step(&dmg);
if (!paused) {
dmg_step(&dmg);
}
if (pause_next) {
paused = 1;
}
currentTime = SDL_GetTicks();
if (currentTime >= lastDrawTime + 16) {
@ -219,10 +274,25 @@ int main(int argc, char *argv[])
while (SDL_PollEvent(&event))
{
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT)
if (event.type == SDL_QUIT) {
done = true;
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
}
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE
&& event.window.windowID == SDL_GetWindowID(window)) {
done = true;
}
if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) {
struct key_input *key = key_inputs;
while (key->scancode) {
if (key->scancode == event.key.keysym.scancode) {
dmg_set_button(&dmg, key->field, key->button, event.type == SDL_KEYDOWN);
break;
}
key++;
}
}
}
// Start the Dear ImGui frame
@ -261,6 +331,20 @@ int main(int argc, char *argv[])
ImGui::SameLine();
ImGui::Checkbox("C", &c_flag);
if (ImGui::Button("Run")) {
paused = 0;
pause_next = 0;
}
ImGui::SameLine();
if (ImGui::Button("Stop")) {
paused = 1;
}
ImGui::SameLine();
if (ImGui::Button("Step")) {
paused = 0;
pause_next = 1;
}
ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
ImGui::End();
}
@ -271,7 +355,7 @@ int main(int argc, char *argv[])
convert_output(dmg.lcd);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, output_image);
ImGui::Image((void*)(intptr_t) texture, ImVec2(256, 256));
ImGui::Image((void*)(intptr_t) texture, ImVec2(512, 512));
ImGui::End();
}
@ -290,6 +374,7 @@ int main(int argc, char *argv[])
fill_memory_editor(&dmg);
editor.DrawWindow("Memory", full_address_space, 0x10000, 0x0000);
editor.DrawWindow("ROM", dmg.rom->data, 0x8000, 0);
// Rendering
ImGui::Render();

View File

@ -91,6 +91,7 @@ static void inc_with_carry(struct cpu *regs, u8 *reg)
clear_flag(regs, FLAG_SIGN);
if(*reg == 0xff || *reg == 0x0f)
set_flag(regs, FLAG_HALF_CARRY);
else clear_flag(regs, FLAG_HALF_CARRY);
(*reg)++;
if(*reg == 0)
set_flag(regs, FLAG_ZERO);
@ -102,6 +103,7 @@ static void dec_with_carry(struct cpu *regs, u8 *reg)
set_flag(regs, FLAG_SIGN);
if(*reg == 0x00 || *reg == 0x10)
set_flag(regs, FLAG_HALF_CARRY);
else clear_flag(regs, FLAG_HALF_CARRY);
(*reg)--;
if(*reg == 0)
set_flag(regs, FLAG_ZERO);
@ -218,10 +220,22 @@ static void add(struct cpu *cpu, u8 value, int with_carry)
sum_full++;
}
sum_trunc = (u8) sum_full;
set_flag(cpu, sum_trunc == 0 ? FLAG_ZERO : 0);
if (sum_trunc == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
clear_flag(cpu, FLAG_ZERO);
}
clear_flag(cpu, FLAG_SIGN);
set_flag(cpu, sum_full > sum_trunc ? FLAG_CARRY : 0);
// TODO H
if (sum_full > sum_trunc) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
if (((cpu->a & 0xf) + (value & 0xf)) & 0x10) {
set_flag(cpu, FLAG_HALF_CARRY);
} else {
clear_flag(cpu, FLAG_HALF_CARRY);
}
cpu->a = sum_trunc;
}
@ -239,12 +253,16 @@ static void subtract(struct cpu *cpu, u8 value, int with_carry, int just_compare
clear_flag(cpu, FLAG_ZERO);
}
set_flag(cpu, FLAG_SIGN);
if (sum_full < sum_trunc) {
if (value > cpu->a) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
// TODO H
if (((cpu->a & 0xf) - (value & 0xf)) & 0x10) {
set_flag(cpu, FLAG_HALF_CARRY);
} else {
clear_flag(cpu, FLAG_HALF_CARRY);
}
if (!just_compare) {
cpu->a = sum_trunc;
@ -382,14 +400,50 @@ static void conditional_jump(struct cpu *cpu, u8 opc, u8 neg_op, int flag) {
}
}
static u16 handlers[] = { 0x40, 0x48, 0x50, 0x58, 0x60 };
static u16 check_interrupts(struct cpu *cpu)
{
int k;
if (!cpu->interrupt_enable) {
return 0;
}
u16 enabled = cpu->mem_read(cpu->mem_model, 0xffff);
u16 requested = cpu->mem_read(cpu->mem_model, 0xff0f);
for (k = 0; k < NUM_INTERRUPTS; k++) {
int check = 1 << k;
if ((enabled & check) && (requested & check)) {
// clear request flag for this interrupt and disable all further
// interrupts until service routine executes EI or RETI
cpu->mem_write(cpu->mem_model, 0xff0f, requested & ~check);
cpu->interrupt_enable = 0;
return handlers[k];
}
}
return 0;
}
void cpu_step(struct cpu *cpu)
{
u8 temp;
u16 temp16;
u16 temp16, intr_dest;
intr_dest = check_interrupts(cpu);
if (intr_dest) {
push(cpu, cpu->pc);
cpu->pc = intr_dest;
return;
}
u8 opc = cpu->mem_read(cpu->mem_model, cpu->pc);
#ifdef GB6_DEBUG
printf("0x%04x %s\n", cpu->pc, instructions[opc].format);
#endif
cpu->pc++;
cpu->cycle_count += instructions[opc].cycles;
switch (opc) {
@ -727,8 +781,10 @@ void cpu_step(struct cpu *cpu)
}
break;
case 0xca: // JP Z, u16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (flag_isset(cpu, FLAG_ZERO)) {
cpu->pc = read16(cpu, cpu->pc);
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
@ -742,6 +798,10 @@ void cpu_step(struct cpu *cpu)
case 0xd5: // PUSH DE
push(cpu, read_de(cpu));
break;
case 0xd9: // RETI
cpu->pc = pop(cpu);
cpu->interrupt_enable = 1;
break;
case 0xe0: // LD (a8),A
write8(cpu, 0xff00 + read8(cpu, cpu->pc), cpu->a);
cpu->pc++;
@ -773,6 +833,7 @@ void cpu_step(struct cpu *cpu)
cpu->a = read8(cpu, 0xff00 + cpu->c);
break;
case 0xf3: // DI
cpu->interrupt_enable = 0;
break;
case 0xf5: // PUSH AF
push(cpu, read_af(cpu));
@ -782,6 +843,7 @@ void cpu_step(struct cpu *cpu)
cpu->pc += 2;
break;
case 0xfb: // EI
cpu->interrupt_enable = 1;
break;
default:
printf("unknown opcode 0x%02x %s\n", opc, instructions[opc].format);

View File

@ -16,6 +16,7 @@ struct cpu
u16 sp;
u16 pc;
u32 cycle_count;
u8 interrupt_enable;
u8 (*mem_read)(void *, u16);
void (*mem_write)(void *, u16, u8);
@ -37,4 +38,11 @@ int flag_isset(struct cpu *cpu, int flag);
#define FLAG_HALF_CARRY 0x20
#define FLAG_CARRY 0x10
#define INT_VBLANK (1 << 0)
#define INT_LCDSTAT (1 << 1)
#define INT_TIMER (1 << 2)
#define INT_SERIAL (1 << 3)
#define INT_JOYPAD (1 << 4)
#define NUM_INTERRUPTS 5
#endif

View File

@ -13,6 +13,33 @@ void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd)
dmg->cpu = cpu;
dmg->rom = rom;
dmg->lcd = lcd;
dmg->joypad = 0xf; // nothing pressed
dmg->action_buttons = 0xf;
}
void dmg_set_button(struct dmg *dmg, int field, int button, int pressed)
{
u8 *mod;
if (field == FIELD_JOY) {
mod = &dmg->joypad;
} else if (field == FIELD_ACTION) {
mod = &dmg->action_buttons;
} else {
printf("setting invalid button state\n");
return;
}
if (pressed) {
*mod &= ~button;
} else {
*mod |= button;
}
}
static u8 get_button_state(struct dmg *dmg)
{
return dmg->action_selected ? dmg->action_buttons : dmg->joypad;
}
u8 dmg_read(void *_dmg, u16 address)
@ -37,6 +64,12 @@ u8 dmg_read(void *_dmg, u16 address)
return lcd_read(dmg->lcd, address);
} else if (address >= 0xff80 && address <= 0xfffe) {
return dmg->zero_page[address - 0xff80];
} else if (address == 0xff00) {
return get_button_state(dmg);
} else if (address == 0xff0f) {
return dmg->interrupt_requested;
} else if (address == 0xffff) {
return dmg->interrupt_enabled;
} else {
// not sure about any of this yet
// commented out bc of memory view window
@ -65,11 +98,21 @@ void dmg_write(void *_dmg, u16 address, u8 data)
lcd_write(dmg->lcd, address, data);
} else if (address >= 0xff80 && address <= 0xfffe) {
dmg->zero_page[address - 0xff80] = data;
} else if (address == 0xff00) {
dmg->action_selected = data & (1 << 5);
} else if (address == 0xff0f) {
dmg->interrupt_requested = data;
} else if (address == 0xffff) {
dmg->interrupt_enabled = data;
} else {
// not sure about any of this yet
}
}
void exit(int);
void dmg_request_interrupt(struct dmg *dmg, int nr)
{
dmg->interrupt_requested |= nr;
}
void dmg_step(void *_dmg)
{
@ -85,6 +128,7 @@ void dmg_step(void *_dmg)
int next_scanline = lcd_step(dmg->lcd);
if (next_scanline == 144) {
// vblank has started, draw all the stuff from ram into the lcd
dmg_request_interrupt(dmg, INT_VBLANK);
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
int bg_base = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800;
@ -92,8 +136,6 @@ void dmg_step(void *_dmg)
int use_unsigned = lcdc & LCDC_BG_TILE_DATA;
int tilebase = use_unsigned ? 0x8000 : 0x9000;
printf("tile map: %04x, tile data: %04x\n", bg_base, tilebase);
int k = 0, off = 0;
int tile_y = 0, tile_x = 0;
for (tile_y = 0; tile_y < 32; tile_y++) {

View File

@ -5,6 +5,18 @@
#include "rom.h"
#include "lcd.h"
#define FIELD_JOY 1
#define FIELD_ACTION 2
#define BUTTON_RIGHT (1 << 0)
#define BUTTON_LEFT (1 << 1)
#define BUTTON_UP (1 << 2)
#define BUTTON_DOWN (1 << 3)
#define BUTTON_A (1 << 0)
#define BUTTON_B (1 << 1)
#define BUTTON_SELECT (1 << 2)
#define BUTTON_START (1 << 3)
struct dmg {
struct cpu *cpu;
struct rom *rom;
@ -13,9 +25,16 @@ struct dmg {
u8 video_ram[0x2000];
u8 zero_page[0x80];
u32 last_lcd_update;
int action_selected; // non-0 if A/B/start/select selected, 0 for directions
u8 interrupt_enabled;
u8 interrupt_requested;
u8 joypad;
u8 action_buttons;
};
void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd);
void dmg_set_button(struct dmg *dmg, int field, int button, int pressed);
// why did i make these void *
u8 dmg_read(void *dmg, u16 address);