Compare commits

...

3 Commits

Author SHA1 Message Date
Matthew Laux
bc3f717285 add vram tile viewer thing 2022-07-14 02:11:20 -05:00
Matthew Laux
b1dca59463 run rom image, more instructions 2022-07-14 01:26:59 -05:00
Matthew Laux
261bb40563 first output: nintendo logo 2022-07-14 00:48:18 -05:00
5 changed files with 248 additions and 116 deletions

View File

@ -11,7 +11,7 @@ execute_process(COMMAND sdl2-config --libs
OUTPUT_VARIABLE SDL_LIBS OUTPUT_VARIABLE SDL_LIBS
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE)
set(CMAKE_CXX_FLAGS "-std=c++14 ${SDL_CFLAGS}") set(CMAKE_CXX_FLAGS "-std=c++14 -g -DGB6_DEBUG ${SDL_CFLAGS}")
add_executable(gb6 add_executable(gb6
../src/bootstrap.c ../src/bootstrap.c

View File

@ -9,6 +9,7 @@
#include "imgui/imgui_memory_editor.h" #include "imgui/imgui_memory_editor.h"
#include <stdio.h> #include <stdio.h>
#include <SDL.h> #include <SDL.h>
#include <SDL_timer.h>
#if defined(IMGUI_IMPL_OPENGL_ES2) #if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h> #include <SDL_opengles2.h>
#else #else
@ -49,6 +50,7 @@ GLuint make_output_texture() {
} }
unsigned char output_image[256 * 256 * 4]; unsigned char output_image[256 * 256 * 4];
unsigned char vram_tiles[256 * 96 * 4];
void convert_output(struct lcd *lcd) { void convert_output(struct lcd *lcd) {
int x, y; int x, y;
@ -57,14 +59,41 @@ void convert_output(struct lcd *lcd) {
for (x = 0; x < 256; x++) { for (x = 0; x < 256; x++) {
int val = lcd->buf[y * 256 + x]; int val = lcd->buf[y * 256 + x];
int fill = val ? 255 : 0; int fill = val ? 255 : 0;
output_image[out_index++] = val; output_image[out_index++] = fill;
output_image[out_index++] = val; output_image[out_index++] = fill;
output_image[out_index++] = val; output_image[out_index++] = fill;
output_image[out_index++] = 255; output_image[out_index++] = 255;
} }
} }
} }
void convert_vram(struct dmg *dmg) {
int tile_y, tile_x;
int off, in;
for (tile_y = 0; tile_y < 12; tile_y++) {
for (tile_x = 0; tile_x < 32; tile_x++) {
off = 256 * 8 * tile_y + 8 * tile_x;
in = 16 * (tile_y * 32 + tile_x);
int b, i;
for (b = 0; b < 16; b += 2) {
int data1 = dmg->video_ram[in + b];
int data2 = dmg->video_ram[in + b + 1];
for (i = 7; i >= 0; i--) {
// monochrome for now
int fill = (data1 & (1 << i)) ? 255 : 0;
vram_tiles[4 * off + 0] = fill;
vram_tiles[4 * off + 1] = fill;
vram_tiles[4 * off + 2] = fill;
vram_tiles[4 * off + 3] = 255;
//dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0;
off++;
}
off += 248;
}
}
}
}
char full_address_space[0x10000]; char full_address_space[0x10000];
void fill_memory_editor(struct dmg *dmg) void fill_memory_editor(struct dmg *dmg)
{ {
@ -164,6 +193,7 @@ int main(int argc, char *argv[])
// setup output // setup output
GLuint texture = make_output_texture(); GLuint texture = make_output_texture();
GLuint vram_texture = make_output_texture();
// Our state // Our state
bool z_flag = false; bool z_flag = false;
@ -173,8 +203,13 @@ int main(int argc, char *argv[])
// Main loop // Main loop
bool done = false; bool done = false;
unsigned int lastDrawTime = 0, currentTime;
while (!done) while (!done)
{ {
dmg_step(&dmg);
currentTime = SDL_GetTicks();
if (currentTime >= lastDrawTime + 16) {
// Poll and handle events (inputs, window resize, etc.) // Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
@ -195,8 +230,6 @@ int main(int argc, char *argv[])
ImGui_ImplSDL2_NewFrame(); ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame(); ImGui::NewFrame();
dmg_step(&dmg);
z_flag = flag_isset(dmg.cpu, FLAG_ZERO); z_flag = flag_isset(dmg.cpu, FLAG_ZERO);
n_flag = flag_isset(dmg.cpu, FLAG_SIGN); n_flag = flag_isset(dmg.cpu, FLAG_SIGN);
h_flag = flag_isset(dmg.cpu, FLAG_HALF_CARRY); h_flag = flag_isset(dmg.cpu, FLAG_HALF_CARRY);
@ -243,10 +276,20 @@ int main(int argc, char *argv[])
ImGui::End(); ImGui::End();
} }
{
ImGui::Begin("VRAM");
convert_vram(&dmg);
glBindTexture(GL_TEXTURE_2D, vram_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 96, 0, GL_RGBA, GL_UNSIGNED_BYTE, vram_tiles);
ImGui::Image((void*)(intptr_t) vram_texture, ImVec2(256, 96));
ImGui::End();
}
fill_memory_editor(&dmg); fill_memory_editor(&dmg);
editor.DrawWindow("Memory", full_address_space, 0x10000, 0x0000); editor.DrawWindow("Memory", full_address_space, 0x10000, 0x0000);
editor.DrawWindow("LCD", dmg.lcd->buf, 0x2000, 0);
// Rendering // Rendering
ImGui::Render(); ImGui::Render();
@ -255,6 +298,9 @@ int main(int argc, char *argv[])
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
lastDrawTime = currentTime;
}
} }
rom_free(&rom); rom_free(&rom);

109
src/cpu.c
View File

@ -110,23 +110,53 @@ static void dec_with_carry(struct cpu *regs, u8 *reg)
static u8 rotate_left(struct cpu *regs, u8 reg) static u8 rotate_left(struct cpu *regs, u8 reg)
{ {
// copy old leftmost bit to carry flag int old_carry = flag_isset(regs, FLAG_CARRY);
regs->f = (reg & 0x80) >> 3 | (regs->f & ~FLAG_CARRY); // copy old leftmost bit to carry flag, clear Z, N, H
regs->f = (reg & 0x80) >> 3;
// rotate // rotate
int result = reg << 1; int result = reg << 1 | old_carry;
// restore leftmost (now rightmost) bit if (!result) set_flag(regs, FLAG_ZERO);
result |= (regs->f & FLAG_CARRY) >> 4; return result;
}
static u8 rlc(struct cpu *cpu, u8 val)
{
int old_msb = (val & 0x80) >> 7;
int result = (val & 0x7f) << 1 | old_msb;
if (!result)
set_flag(cpu, FLAG_ZERO);
else clear_flag(cpu, FLAG_ZERO);
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
if (old_msb)
set_flag(cpu, FLAG_CARRY);
else clear_flag(cpu, FLAG_CARRY);
return result; return result;
} }
static u8 rotate_right(struct cpu *regs, u8 reg) static u8 rotate_right(struct cpu *regs, u8 reg)
{ {
// copy old rightmost bit to carry flag int old_carry = flag_isset(regs, FLAG_CARRY) << 7;
regs->f = (reg & 0x01) << 4 | (regs->f & ~FLAG_CARRY); // copy old rightmost bit to carry flag, clear ZNH
regs->f = (reg & 0x01) << 4;
// rotate // rotate
int result = reg >> 1; int result = old_carry | reg >> 1;
// restore rightmost bit to left if (!result) set_flag(regs, FLAG_ZERO);
result |= (regs->f & FLAG_CARRY) << 3; return result;
}
static u8 rrc(struct cpu *cpu, u8 val)
{
int old_lsb = (val & 1) << 7;
int result = old_lsb | (val & 0xfe) >> 1;
if (!result)
set_flag(cpu, FLAG_ZERO);
else clear_flag(cpu, FLAG_ZERO);
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
if (old_lsb)
set_flag(cpu, FLAG_CARRY);
else clear_flag(cpu, FLAG_CARRY);
return result; return result;
} }
@ -202,10 +232,18 @@ static void subtract(struct cpu *cpu, u8 value, int with_carry, int just_compare
if (with_carry && flag_isset(cpu, FLAG_CARRY)) { if (with_carry && flag_isset(cpu, FLAG_CARRY)) {
sum_full--; sum_full--;
} }
sum_trunc = (u8) sum_full; sum_trunc = (u8) (sum_full & 0xff);
set_flag(cpu, sum_trunc == 0 ? FLAG_ZERO : 0); if (!sum_trunc) {
set_flag(cpu, FLAG_ZERO);
} else {
clear_flag(cpu, FLAG_ZERO);
}
set_flag(cpu, FLAG_SIGN); set_flag(cpu, FLAG_SIGN);
set_flag(cpu, sum_full < sum_trunc ? FLAG_CARRY : 0); if (sum_full < sum_trunc) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
// TODO H // TODO H
if (!just_compare) { if (!just_compare) {
@ -296,10 +334,10 @@ static void extended_insn(struct cpu *cpu, u8 insn)
int reg = insn & 0x7; int reg = insn & 0x7;
u8 (*funcs[8])(struct cpu *, u8) = { u8 (*funcs[8])(struct cpu *, u8) = {
rlc,
rrc,
rotate_left, rotate_left,
rotate_right, rotate_right,
rotate_left, // TODO non-carry version
rotate_right,
shift_left, shift_left,
shift_right, shift_right,
swap, swap,
@ -356,7 +394,7 @@ void cpu_step(struct cpu *cpu)
cpu->pc += 2; cpu->pc += 2;
break; break;
case 0x07: // RLCA case 0x07: // RLCA
cpu->a = rotate_left(cpu, cpu->a); cpu->a = rlc(cpu, cpu->a);
break; break;
case 0x08: // LD (a16),SP case 0x08: // LD (a16),SP
write16(cpu, read16(cpu, cpu->pc), cpu->sp); write16(cpu, read16(cpu, cpu->pc), cpu->sp);
@ -544,6 +582,7 @@ void cpu_step(struct cpu *cpu)
case 0xc0: // RET NZ case 0xc0: // RET NZ
if (!flag_isset(cpu, FLAG_ZERO)) { if (!flag_isset(cpu, FLAG_ZERO)) {
cpu->pc = pop(cpu); cpu->pc = pop(cpu);
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
} }
break; break;
case 0xc9: // RET case 0xc9: // RET
@ -561,6 +600,7 @@ void cpu_step(struct cpu *cpu)
case 0xd2: // JP NC,a16 case 0xd2: // JP NC,a16
if (flag_isset(cpu, FLAG_CARRY)) { if (flag_isset(cpu, FLAG_CARRY)) {
cpu->pc = read16(cpu, cpu->pc); cpu->pc = read16(cpu, cpu->pc);
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
} }
break; break;
@ -657,6 +697,18 @@ void cpu_step(struct cpu *cpu)
case 0xc5: // PUSH BC case 0xc5: // PUSH BC
push(cpu, read_bc(cpu)); push(cpu, read_bc(cpu));
break; break;
case 0xc8: // RET Z
if (flag_isset(cpu, FLAG_ZERO)) {
cpu->pc = pop(cpu);
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xca: // JP Z, u16
if (flag_isset(cpu, FLAG_ZERO)) {
cpu->pc = read16(cpu, cpu->pc);
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xcb: case 0xcb:
extended_insn(cpu, read8(cpu, cpu->pc)); extended_insn(cpu, read8(cpu, cpu->pc));
cpu->pc++; cpu->pc++;
@ -665,13 +717,28 @@ void cpu_step(struct cpu *cpu)
add(cpu, read8(cpu, cpu->pc), 1); add(cpu, read8(cpu, cpu->pc), 1);
cpu->pc++; cpu->pc++;
break; break;
case 0xd1: // POP DE
write_de(cpu, pop(cpu));
break;
case 0xd5: // PUSH DE
push(cpu, read_de(cpu));
break;
case 0xe0: // LD (a8),A case 0xe0: // LD (a8),A
write8(cpu, 0xff00 + read8(cpu, cpu->pc), cpu->a); write8(cpu, 0xff00 + read8(cpu, cpu->pc), cpu->a);
cpu->pc++; cpu->pc++;
break; break;
case 0xe1: // POP HL
write_hl(cpu, pop(cpu));
break;
case 0xe2: // LD (C),A case 0xe2: // LD (C),A
write8(cpu, 0xff00 + cpu->c, cpu->a); write8(cpu, 0xff00 + cpu->c, cpu->a);
break; break;
case 0xe5: // PUSH HL
push(cpu, read_hl(cpu));
break;
case 0xe9: // JP HL
cpu->pc = read_hl(cpu);
break;
case 0xea: // LD (a16),A case 0xea: // LD (a16),A
write8(cpu, read16(cpu, cpu->pc), cpu->a); write8(cpu, read16(cpu, cpu->pc), cpu->a);
cpu->pc += 2; cpu->pc += 2;
@ -680,11 +747,21 @@ void cpu_step(struct cpu *cpu)
cpu->a = read8(cpu, 0xff00 + read8(cpu, cpu->pc)); cpu->a = read8(cpu, 0xff00 + read8(cpu, cpu->pc));
cpu->pc++; cpu->pc++;
break; break;
case 0xf1: // POP AF
write_af(cpu, pop(cpu));
break;
case 0xf2: // LD A,(C) case 0xf2: // LD A,(C)
cpu->a = read8(cpu, 0xff00 + cpu->c); cpu->a = read8(cpu, 0xff00 + cpu->c);
break; break;
case 0xf3: // DI case 0xf3: // DI
break; break;
case 0xf5: // PUSH AF
push(cpu, read_af(cpu));
break;
case 0xfa: // LD A,(u16)
cpu->a = read8(cpu, read16(cpu, cpu->pc));
cpu->pc += 2;
break;
case 0xfb: // EI case 0xfb: // EI
break; break;
default: default:

View File

@ -18,9 +18,10 @@ void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd)
u8 dmg_read(void *_dmg, u16 address) u8 dmg_read(void *_dmg, u16 address)
{ {
struct dmg *dmg = (struct dmg *) _dmg; struct dmg *dmg = (struct dmg *) _dmg;
if (address < 0x100) { // if (address < 0x100) {
return dmg_boot_rom[address]; // return dmg_boot_rom[address];
} else if (address < 0x4000) { // } else if (address < 0x4000) {
if (address < 0x4000) {
return dmg->rom->data[address]; return dmg->rom->data[address];
} else if (address < 0x8000) { } else if (address < 0x8000) {
// TODO switchable rom bank // TODO switchable rom bank
@ -68,6 +69,7 @@ void dmg_write(void *_dmg, u16 address, u8 data)
// not sure about any of this yet // not sure about any of this yet
} }
} }
void exit(int);
void dmg_step(void *_dmg) void dmg_step(void *_dmg)
{ {
@ -78,7 +80,8 @@ void dmg_step(void *_dmg)
cpu_step(dmg->cpu); cpu_step(dmg->cpu);
// each line takes 456 cycles // each line takes 456 cycles
if (dmg->cpu->cycle_count % 456 == 0) { if (dmg->cpu->cycle_count - dmg->last_lcd_update >= 456) {
dmg->last_lcd_update = dmg->cpu->cycle_count;
int next_scanline = lcd_step(dmg->lcd); int next_scanline = lcd_step(dmg->lcd);
if (next_scanline == 144) { if (next_scanline == 144) {
// vblank has started, draw all the stuff from ram into the lcd // vblank has started, draw all the stuff from ram into the lcd
@ -89,11 +92,14 @@ void dmg_step(void *_dmg)
int use_unsigned = lcdc & LCDC_BG_TILE_DATA; int use_unsigned = lcdc & LCDC_BG_TILE_DATA;
int tilebase = use_unsigned ? 0x8000 : 0x9000; int tilebase = use_unsigned ? 0x8000 : 0x9000;
printf("base is %04x\n", bg_base); printf("tile map: %04x, tile data: %04x\n", bg_base, tilebase);
int k, off = 0; int k = 0, off = 0;
for (k = 0; k < 1024; k++) { int tile_y = 0, tile_x = 0;
int tile = dmg_read(dmg, bg_base + k); for (tile_y = 0; tile_y < 32; tile_y++) {
for (tile_x = 0; tile_x < 32; tile_x++) {
off = 256 * 8 * tile_y + 8 * tile_x;
int tile = dmg_read(dmg, bg_base + (tile_y * 32 + tile_x));
int eff_addr; int eff_addr;
if (use_unsigned) { if (use_unsigned) {
eff_addr = tilebase + 16 * tile; eff_addr = tilebase + 16 * tile;
@ -104,12 +110,14 @@ void dmg_step(void *_dmg)
for (b = 0; b < 16; b += 2) { for (b = 0; b < 16; b += 2) {
int data1 = dmg_read(dmg, eff_addr + b); int data1 = dmg_read(dmg, eff_addr + b);
int data2 = dmg_read(dmg, eff_addr + b + 1); int data2 = dmg_read(dmg, eff_addr + b + 1);
for (i = 0; i < 8; i++) { for (i = 7; i >= 0; i--) {
// monochrome for now // monochrome for now
dmg->lcd->buf[off] |= (data1 & (1 << i)) ? 1 : 0; dmg->lcd->buf[off] = (data1 & (1 << i)) ? 1 : 0;
dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0; //dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0;
off++; off++;
} }
off += 248;
}
} }
} }

View File

@ -12,6 +12,7 @@ struct dmg {
u8 main_ram[0x2000]; u8 main_ram[0x2000];
u8 video_ram[0x2000]; u8 video_ram[0x2000];
u8 zero_page[0x80]; u8 zero_page[0x80];
u32 last_lcd_update;
}; };
void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd); void dmg_new(struct dmg *dmg, struct cpu *cpu, struct rom *rom, struct lcd *lcd);