Compare commits

...

2 Commits

Author SHA1 Message Date
Matthew Laux 8d87ee1f26 clean up imgui code, a few more instructions for today 2022-07-20 01:01:38 -05:00
Matthew Laux 5735d65470 interrupt system, vblank, key input, don't know if any of it works 2022-07-20 00:45:38 -05:00
5 changed files with 230 additions and 53 deletions

View File

@ -1,8 +1,3 @@
// Dear ImGui: standalone example application for SDL2 + OpenGL
// (SDL is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#include "imgui/imgui.h"
#include "imgui/imgui_impl_sdl.h"
#include "imgui/imgui_impl_opengl3.h"
@ -10,11 +5,7 @@
#include <stdio.h>
#include <SDL.h>
#include <SDL_timer.h>
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h>
#else
#include <SDL_opengl.h>
#endif
#include <string>
extern "C" {
@ -34,6 +25,28 @@ static const char *L_FORMAT = "L: 0x%02x";
static const char *SP_FORMAT = "SP: 0x%02x";
static const char *PC_FORMAT = "PC: 0x%02x";
unsigned char output_image[256 * 256 * 4];
unsigned char vram_tiles[256 * 96 * 4];
unsigned char full_address_space[0x10000];
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 }
};
static MemoryEditor editor;
GLuint make_output_texture() {
@ -43,15 +56,10 @@ GLuint make_output_texture() {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non power-of-two textures
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same
return image_texture;
}
unsigned char output_image[256 * 256 * 4];
unsigned char vram_tiles[256 * 96 * 4];
void convert_output(struct lcd *lcd) {
int x, y;
int out_index = 0;
@ -94,7 +102,6 @@ void convert_vram(struct dmg *dmg) {
}
}
char full_address_space[0x10000];
void fill_memory_editor(struct dmg *dmg)
{
int k;
@ -112,6 +119,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");
@ -133,24 +142,13 @@ int main(int argc, char *argv[])
cpu.pc = 0x100;
// Setup SDL
// (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems,
// depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!)
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0)
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// Decide GL+GLSL versions
#if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char* glsl_version = "#version 100";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#elif defined(__APPLE__)
#if defined(__APPLE__)
// GL 3.2 Core + GLSL 150
const char* glsl_version = "#version 150";
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
@ -171,7 +169,7 @@ int main(int argc, char *argv[])
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+OpenGL3 example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
SDL_Window* window = SDL_CreateWindow("gb6 debug", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, gl_context);
SDL_GL_SetSwapInterval(0); // disable vsync
@ -179,9 +177,8 @@ int main(int argc, char *argv[])
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
// Setup Dear ImGui style
ImGui::StyleColorsDark();
@ -195,7 +192,7 @@ int main(int argc, char *argv[])
GLuint texture = make_output_texture();
GLuint vram_texture = make_output_texture();
// Our state
// for flag checkboxes
bool z_flag = false;
bool n_flag = false;
bool h_flag = false;
@ -204,9 +201,14 @@ int main(int argc, char *argv[])
// Main loop
bool done = false;
unsigned int lastDrawTime = 0, currentTime;
while (!done)
{
dmg_step(&dmg);
while (!done) {
if (!paused) {
dmg_step(&dmg);
}
if (pause_next) {
paused = 1;
}
currentTime = SDL_GetTicks();
if (currentTime >= lastDrawTime + 16) {
@ -219,10 +221,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
@ -235,9 +252,8 @@ int main(int argc, char *argv[])
h_flag = flag_isset(dmg.cpu, FLAG_HALF_CARRY);
c_flag = flag_isset(dmg.cpu, FLAG_CARRY);
// 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
{
ImGui::Begin("State"); // Create a window called "Hello, world!" and append into it.
ImGui::Begin("State");
ImGui::Text(A_FORMAT, dmg.cpu->a);
ImGui::Text(B_FORMAT, dmg.cpu->b);
@ -261,6 +277,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 +301,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 +320,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();
@ -303,8 +334,6 @@ int main(int argc, char *argv[])
}
}
rom_free(&rom);
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplSDL2_Shutdown();
@ -314,5 +343,7 @@ int main(int argc, char *argv[])
SDL_DestroyWindow(window);
SDL_Quit();
rom_free(&rom);
return 0;
}

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) {
@ -399,6 +453,9 @@ void cpu_step(struct cpu *cpu)
write_bc(cpu, read16(cpu, cpu->pc));
cpu->pc += 2;
break;
case 0x0f: // RRCA
cpu->a = rrc(cpu, cpu->a);
break;
case 0x11: // LD DE,d16
write_de(cpu, read16(cpu, cpu->pc));
cpu->pc += 2;
@ -596,6 +653,14 @@ void cpu_step(struct cpu *cpu)
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xc2: // JP NZ, u16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (!flag_isset(cpu, FLAG_ZERO)) {
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xc9: // RET
cpu->pc = pop(cpu);
break;
@ -609,8 +674,10 @@ void cpu_step(struct cpu *cpu)
cpu->pc = read16(cpu, cpu->pc);
break;
case 0xd2: // JP NC,a16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (flag_isset(cpu, FLAG_CARRY)) {
cpu->pc = read16(cpu, cpu->pc);
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
@ -727,8 +794,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 +811,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 +846,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 +856,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,7 +136,7 @@ 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);
printf("%04x %04x %04x\n", bg_base, window_base, tilebase);
int k = 0, off = 0;
int tile_y = 0, tile_x = 0;

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);