Compare commits

...

4 Commits

Author SHA1 Message Date
Matthew Laux 580c349fc7 a few more instructions 2022-07-26 00:39:15 -05:00
Matthew Laux 6ab729f377 it goes off the rails at the gameplay screen on tetris 2022-07-26 00:10:01 -05:00
Matthew Laux bbb398f524 implement shifts 2022-07-25 23:48:22 -05:00
Matthew Laux bc980ec1e5 add more instructions, fix INC/DEC [HL], first attempt at lcd "mode" timing 2022-07-25 22:55:13 -05:00
5 changed files with 172 additions and 25 deletions

View File

@ -27,7 +27,6 @@ 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;
@ -60,13 +59,15 @@ GLuint make_output_texture() {
return image_texture;
}
unsigned char default_palette[] = { 0, 0x55, 0xaa, 0xff };
void convert_output(struct lcd *lcd) {
int x, y;
int out_index = 0;
for (y = 0; y < 256; y++) {
for (x = 0; x < 256; x++) {
int val = lcd->buf[y * 256 + x];
int fill = 255 - val * 85;
int fill = default_palette[val];
//int fill = val ? 255 : 0;
output_image[out_index++] = fill;
output_image[out_index++] = fill;
@ -103,12 +104,14 @@ void convert_vram(struct dmg *dmg) {
}
}
void fill_memory_editor(struct dmg *dmg)
static ImU8 read_mem(const ImU8* data, size_t off)
{
int k;
for (k = 0; k < 0x10000; k++) {
full_address_space[k] = dmg_read(dmg, k);
}
return dmg_read((void *) data, (u16) off);
}
static void write_mem(ImU8 *data, size_t off, ImU8 d)
{
dmg_write(data, (u16) off, d);
}
// Main code
@ -193,6 +196,9 @@ int main(int argc, char *argv[])
GLuint texture = make_output_texture();
GLuint vram_texture = make_output_texture();
editor.ReadFn = read_mem;
editor.WriteFn = write_mem;
// for flag checkboxes
bool z_flag = false;
bool n_flag = false;
@ -318,10 +324,7 @@ int main(int argc, char *argv[])
ImGui::End();
}
fill_memory_editor(&dmg);
editor.DrawWindow("Memory", full_address_space, 0x10000, 0x0000);
editor.DrawWindow("ROM", dmg.rom->data, 0x8000, 0);
editor.DrawWindow("Memory", &dmg, 0x10000, 0x0000);
// Rendering
ImGui::Render();

112
src/cpu.c
View File

@ -164,11 +164,55 @@ static u8 rrc(struct cpu *cpu, u8 val)
static u8 shift_left(struct cpu *cpu, u8 value)
{
return 0;
int result = value << 1;
if (result == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
clear_flag(cpu, FLAG_ZERO);
}
if (value & 0x80) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
return result;
}
static u8 shift_right(struct cpu *cpu, u8 value)
{
int result = (signed) value >> 1;
if (result == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
clear_flag(cpu, FLAG_ZERO);
}
if (value & 0x1) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
return result;
}
static u8 srl(struct cpu *cpu, u8 value)
{
int result = value >> 1;
if (result == 0) {
set_flag(cpu, FLAG_ZERO);
} else {
clear_flag(cpu, FLAG_ZERO);
}
if (value & 0x1) {
set_flag(cpu, FLAG_CARRY);
} else {
clear_flag(cpu, FLAG_CARRY);
}
clear_flag(cpu, FLAG_SIGN);
clear_flag(cpu, FLAG_HALF_CARRY);
return 0;
}
@ -368,7 +412,7 @@ static void extended_insn(struct cpu *cpu, u8 insn)
shift_left,
shift_right,
swap,
shift_right // TODO SRL
srl,
};
#ifdef GB6_DEBUG
@ -462,6 +506,9 @@ void cpu_step(struct cpu *cpu)
write_bc(cpu, read16(cpu, cpu->pc));
cpu->pc += 2;
break;
case 0x02: // LD (BC), A
write8(cpu, read_bc(cpu), cpu->a);
break;
case 0x0f: // RRCA
cpu->a = rrc(cpu, cpu->a);
break;
@ -515,8 +562,8 @@ void cpu_step(struct cpu *cpu)
case 0x2d: dec_with_carry(cpu, &cpu->l); break;
case 0x33: cpu->sp++; break;
case 0x34: temp = read8(cpu, read_hl(cpu)); inc_with_carry(cpu, &temp); break;
case 0x35: temp = read8(cpu, read_hl(cpu)); dec_with_carry(cpu, &temp); break;
case 0x34: temp = read8(cpu, read_hl(cpu)); inc_with_carry(cpu, &temp); write8(cpu, read_hl(cpu), temp); break;
case 0x35: temp = read8(cpu, read_hl(cpu)); dec_with_carry(cpu, &temp); write8(cpu, read_hl(cpu), temp); break;
case 0x3b: cpu->sp--; break;
case 0x3c: inc_with_carry(cpu, &cpu->a); break;
@ -592,8 +639,8 @@ void cpu_step(struct cpu *cpu)
case 0x6d: break; // copy L to L
case 0x6e: cpu->l = read8(cpu, read_hl(cpu)); break;
case 0x6f: cpu->l = cpu->a; break;
// dest = *HL
// dest is *HL
case 0x70: write8(cpu, read_hl(cpu), cpu->b); break;
case 0x71: write8(cpu, read_hl(cpu), cpu->c); break;
case 0x72: write8(cpu, read_hl(cpu), cpu->d); break;
@ -658,6 +705,9 @@ void cpu_step(struct cpu *cpu)
write8(cpu, read_hl(cpu), cpu->a);
write_hl(cpu, read_hl(cpu) - 1);
break;
case 0x39: // ADD HL, SP
add16(cpu, cpu->sp);
break;
case 0xc0: // RET NZ
if (!flag_isset(cpu, FLAG_ZERO)) {
cpu->pc = pop(cpu);
@ -684,6 +734,30 @@ void cpu_step(struct cpu *cpu)
case 0xc3: // JP a16
cpu->pc = read16(cpu, cpu->pc);
break;
case 0xc4: // CALL NZ, u16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (!flag_isset(cpu, FLAG_ZERO)) {
push(cpu, cpu->pc);
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xcc: // CALL Z, u16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
if (flag_isset(cpu, FLAG_ZERO)) {
push(cpu, cpu->pc);
cpu->pc = temp16;
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xd0: // RET NC
if (!flag_isset(cpu, FLAG_CARRY)) {
cpu->pc = pop(cpu);
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xd2: // JP NC,a16
temp16 = read16(cpu, cpu->pc);
cpu->pc += 2;
@ -692,6 +766,12 @@ void cpu_step(struct cpu *cpu)
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0xd8: // RET C
if (flag_isset(cpu, FLAG_CARRY)) {
cpu->pc = pop(cpu);
cpu->cycle_count += instructions[opc].cycles_branch - instructions[opc].cycles;
}
break;
case 0x80: add(cpu, cpu->b, 0); break;
case 0x81: add(cpu, cpu->c, 0); break;
@ -736,6 +816,16 @@ void cpu_step(struct cpu *cpu)
case 0x9e: subtract(cpu, read8(cpu, read_hl(cpu)), 1, 0); break;
case 0x9f: subtract(cpu, cpu->a, 1, 0); break;
case 0xd6: // SUB A, u8
subtract(cpu, read8(cpu, cpu->pc), 0, 0);
cpu->pc++;
break;
case 0xde: // SBC A, u8
subtract(cpu, read8(cpu, cpu->pc), 0, 1);
cpu->pc++;
break;
// AND
case 0xa0: and(cpu, cpu->b); break;
case 0xa1: and(cpu, cpu->c); break;
@ -793,6 +883,9 @@ void cpu_step(struct cpu *cpu)
case 0xf7: push(cpu, cpu->pc); cpu->pc = 0x30; break;
case 0xff: push(cpu, cpu->pc); cpu->pc = 0x38; break;
case 0x27: // DAA
break;
case 0x76: // HALT
break;
@ -866,6 +959,13 @@ void cpu_step(struct cpu *cpu)
case 0xf5: // PUSH AF
push(cpu, read_af(cpu));
break;
case 0xf8: // LD HL, SP+i8
write_hl(cpu, cpu->sp + (signed) read8(cpu, cpu->pc));
cpu->pc++;
break;
case 0xf9: // LD SP, HL
cpu->sp = read_hl(cpu);
break;
case 0xfa: // LD A,(u16)
cpu->a = read8(cpu, read16(cpu, cpu->pc));
cpu->pc += 2;

View File

@ -86,7 +86,7 @@ u8 dmg_read(void *_dmg, u16 address)
// not sure about any of this yet
// commented out bc of memory view window
// fprintf(stderr, "don't know how to read 0x%04x\n", address);
return 0;
return 0xff;
}
}
@ -119,8 +119,8 @@ void dmg_write(void *_dmg, u16 address, u8 data)
} else if (address >= 0xff80 && address <= 0xfffe) {
dmg->zero_page[address - 0xff80] = data;
} else if (address == 0xff00) {
dmg->joypad_selected = data & (1 << 4);
dmg->action_selected = data & (1 << 5);
dmg->joypad_selected = !(data & (1 << 4));
dmg->action_selected = !(data & (1 << 5));
} else if (address == 0xff0f) {
dmg->interrupt_requested = data;
} else if (address == 0xffff) {
@ -144,22 +144,33 @@ void dmg_step(void *_dmg)
cpu_step(dmg->cpu);
// each line takes 456 cycles
if (dmg->cpu->cycle_count - dmg->last_lcd_update >= 456) {
int cycle_diff = dmg->cpu->cycle_count - dmg->last_lcd_update;
if (cycle_diff >= 456) {
dmg->last_lcd_update = dmg->cpu->cycle_count;
int next_scanline = lcd_step(dmg->lcd);
// update LYC
if (next_scanline == lcd_read(dmg->lcd, REG_LYC)) {
lcd_set_bit(dmg->lcd, REG_STAT, STAT_FLAG_MATCH);
dmg_request_interrupt(dmg, INT_LCDSTAT);
if (lcd_isset(dmg->lcd, REG_STAT, STAT_INTR_SOURCE_MATCH)) {
dmg_request_interrupt(dmg, INT_LCDSTAT);
}
} else {
lcd_clear_bit(dmg->lcd, REG_STAT, STAT_FLAG_MATCH);
}
if (next_scanline >= 144 && next_scanline < 154) {
lcd_set_mode(dmg->lcd, 1);
}
if (next_scanline == 144) {
// vblank has started, draw all the stuff from ram into the lcd
dmg_request_interrupt(dmg, INT_VBLANK);
dmg_request_interrupt(dmg, INT_SERIAL);
if (lcd_isset(dmg->lcd, REG_STAT, STAT_INTR_SOURCE_VBLANK)) {
dmg_request_interrupt(dmg, INT_LCDSTAT);
}
int lcdc = lcd_read(dmg->lcd, REG_LCDC);
int bg_base = (lcdc & LCDC_BG_TILE_MAP) ? 0x9c00 : 0x9800;
@ -186,8 +197,8 @@ void dmg_step(void *_dmg)
int data1 = dmg_read(dmg, eff_addr + b);
int data2 = dmg_read(dmg, eff_addr + b + 1);
for (i = 7; i >= 0; i--) {
dmg->lcd->buf[off] = ((data1 & (1 << i)) ? 1 : 0);// << 1;
//dmg->lcd->buf[off] |= (data1 & (1 << i)) ? 1 : 0;
dmg->lcd->buf[off] = ((data1 & (1 << i)) ? 1 : 0) << 1;
dmg->lcd->buf[off] |= (data2 & (1 << i)) ? 1 : 0;
off++;
}
off += 248;
@ -199,5 +210,19 @@ void dmg_step(void *_dmg)
lcd_copy(dmg->lcd);
lcd_draw(dmg->lcd);
}
} else {
int scan = lcd_read(dmg->lcd, REG_LY);
if (scan < 144) {
if (cycle_diff < 80) {
lcd_set_mode(dmg->lcd, 2);
} else if (cycle_diff < 230) {
// just midpoint between 168 to 291, todo improve
lcd_set_mode(dmg->lcd, 3);
} else {
lcd_set_mode(dmg->lcd, 0);
}
} else {
// in vblank. mode should stay as 1
}
}
}

View File

@ -15,6 +15,18 @@ void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit)
lcd_write(lcd, addr, lcd_read(lcd, addr) & ~(1 << bit));
}
int lcd_isset(struct lcd *lcd, u16 addr, u8 bit)
{
u8 val = lcd_read(lcd, addr);
return val & (1 << bit);
}
void lcd_set_mode(struct lcd *lcd, int mode)
{
u8 val = lcd_read(lcd, REG_STAT);
lcd_write(lcd, REG_STAT, (val & 0xfc) | mode);
}
void lcd_new(struct lcd *lcd)
{
lcd->buf = malloc(256 * 256);

View File

@ -24,6 +24,11 @@
#define REG_LCD_LAST REG_WX
#define STAT_FLAG_MATCH 2
#define STAT_INTR_SOURCE_HBLANK 3
#define STAT_INTR_SOURCE_VBLANK 4
#define STAT_INTR_SOURCE_MODE2 5
#define STAT_INTR_SOURCE_MATCH 6
#define LCDC_ENABLE_BG (1 << 0)
#define LCDC_ENABLE_OBJ (1 << 1)
@ -50,6 +55,8 @@ void lcd_put_pixel(struct lcd *lcd, u8 x, u8 y, u8 value);
void lcd_set_bit(struct lcd *lcd, u16 addr, u8 bit);
void lcd_clear_bit(struct lcd *lcd, u16 addr, u8 bit);
int lcd_isset(struct lcd *lcd, u16 addr, u8 bit);
void lcd_set_mode(struct lcd *lcd, int mode);
// i feel like i'm going to need to call this every cycle and update regs
int lcd_step(struct lcd *lcd);