mirror of
https://github.com/mlaux/gb6.git
synced 2025-04-16 07:37:24 +00:00
interrupt system, vblank, key input, don't know if any of it works
This commit is contained in:
parent
1b5f02ef7a
commit
5735d65470
@ -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();
|
||||
|
76
src/cpu.c
76
src/cpu.c
@ -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);
|
||||
|
@ -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
|
||||
|
48
src/dmg.c
48
src/dmg.c
@ -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++) {
|
||||
|
19
src/dmg.h
19
src/dmg.h
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user