diff --git a/gbdb_boot_test b/gbdb_boot_test index 38ab951..f52f7e1 100644 --- a/gbdb_boot_test +++ b/gbdb_boot_test @@ -59,59 +59,38 @@ assert $c == 0x12 runto 0x27 assert $pc == 0x27 -step -assert $pc == 0x28 +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; +regs; peek; step; -step -assert $pc == 0x95 - -step -assert $pc == 0x96 - -step -assert $pc == 0x98 - -step -assert $pc == 0x99 - -step -assert $pc == 0x9b - -step -assert $pc == 0x9c - -step -assert $pc == 0x9d - -step -assert $pc == 0x9f - -step -assert $pc == 0xa0 - -step -assert $pc == 0xa1 - -runto 0xa3 -assert $pc == 0xa3 -step - -assert $pc == 0xa4 - -step -assert $pc == 0xa5 - -step -assert $pc == 0xa6 - -step -assert $pc == 0xa7 - -step -assert $pc == 0x2b - -step -assert $pc == 0x96 runto 0x34 assert $pc == 0x34 @@ -220,7 +199,7 @@ assert $de == 0x00d8 assert $hl == 0x014d # -runto 0x2213 +# runto 0x2213 regs stat diff --git a/src/apps/gbdb.c b/src/apps/gbdb.c index e4400b5..31716b5 100644 --- a/src/apps/gbdb.c +++ b/src/apps/gbdb.c @@ -88,7 +88,7 @@ static void init(void) cpu_ops.undef_d3 = breakpoint_cb; lr35902_init(&cpu, memory, &cpu_ops); - gb_video_init(&video); + gb_video_init(&video, memory); gb_mem_init(memory, &video); } diff --git a/src/common/bmp.h b/src/common/bmp.h index 1276075..025d9ef 100644 --- a/src/common/bmp.h +++ b/src/common/bmp.h @@ -1,3 +1,6 @@ +#ifndef _BMP_H_ +#define _BMP_H_ + #include enum bmp_type { @@ -12,3 +15,5 @@ struct bmp *bmp_new(enum bmp_type type, uint32_t pixels_width, char *bmp_get_pixel_buffer(struct bmp *bmp); int bmp_write_file(struct bmp *bmp, const char *filename); + +#endif diff --git a/src/gbemu/cpu.c b/src/gbemu/cpu.c index 0ecf396..8943e77 100644 --- a/src/gbemu/cpu.c +++ b/src/gbemu/cpu.c @@ -260,8 +260,8 @@ static const unsigned int cb_extra_cycles[256] = { 0 }; /* TODO: In general, check that we pop/push things correctly */ #define POP_16(cpu, dst) \ do { \ - (dst) = gb_mem_read((cpu)->memory, (cpu)->sp++); \ - (dst) |= (uint16_t) gb_mem_read((cpu)->memory, (cpu)->sp++) << 8; \ + (dst) = (uint16_t) gb_mem_read((cpu)->memory, (cpu)->sp++) << 8; \ + (dst) |= gb_mem_read((cpu)->memory, (cpu)->sp++); \ } while (0) #define PUSH_16(cpu, src) \ @@ -322,6 +322,7 @@ static const unsigned int cb_extra_cycles[256] = { 0 }; tmp = tmp << 1; \ (tmp) |= ci; \ (cpu)->zf = (tmp) == 0; \ + reg = tmp; \ } while (0) int lr35902_cycle(struct lr35902_state *cpu) @@ -464,7 +465,7 @@ int lr35902_cycle(struct lr35902_state *cpu) LD_D16(cpu, cpu->hl); break; case 0x22: /* LD (HL+), A */ - cpu->a = gb_mem_read(cpu->memory, cpu->hl++); + gb_mem_write(cpu->memory, cpu->hl++, cpu->a); break; case 0x23: /* INC HL */ cpu->hl++; @@ -492,8 +493,7 @@ int lr35902_cycle(struct lr35902_state *cpu) ADD_16(cpu, cpu->hl, cpu->hl); break; case 0x2a: /* LD A, (HL+) */ - cpu->a = gb_mem_read(cpu->memory, cpu->hl); - cpu->hl--; + cpu->a = gb_mem_read(cpu->memory, cpu->hl++); break; case 0x2b: /* DEC HL */ cpu->hl--; @@ -564,8 +564,7 @@ int lr35902_cycle(struct lr35902_state *cpu) ADD_16(cpu, cpu->hl, cpu->sp); break; case 0x3a: /* LD A, (HL-) */ - cpu->a = gb_mem_read(cpu->memory, cpu->hl); - cpu->hl--; + cpu->a = gb_mem_read(cpu->memory, cpu->hl--); break; case 0x3b: /* DEC SP */ cpu->sp--; @@ -1016,6 +1015,7 @@ int lr35902_cycle(struct lr35902_state *cpu) case 0x0d: case 0x0e: case 0x0f: + ASSERT(0); case 0x10: RL(cpu, cpu->b); break; @@ -1080,6 +1080,7 @@ int lr35902_cycle(struct lr35902_state *cpu) case 0x3d: case 0x3e: case 0x3f: + ASSERT(0); case 0x40: case 0x41: case 0x42: diff --git a/src/gbemu/memory.c b/src/gbemu/memory.c index 99ecf59..96a27be 100644 --- a/src/gbemu/memory.c +++ b/src/gbemu/memory.c @@ -52,6 +52,11 @@ void gb_mem_write(struct gb_memory *cpu, uint16_t addr, uint8_t val) case 0xFF40 ... 0xFF4B: gb_video_mem_write(video, addr, val); break; + case 0x8000 ... 0x87FF: + case 0x9800 ... 0x9BFF: + printf("Wrote [0x%x]=%x\n", addr, val); + ram[addr] = val; + break; case 0 ... 0x100: if (bootrom_mapped) { break; diff --git a/src/gbemu/video.c b/src/gbemu/video.c index f9d141f..09ecd59 100644 --- a/src/gbemu/video.c +++ b/src/gbemu/video.c @@ -1,15 +1,120 @@ #include "gbemu/video.h" +#include "gbemu/memory.h" +#include "common/bmp.h" #include +#include /* TODO: This whole implementation is very simple.*/ /* TODO: Actual graphics output */ /* TODO: Implementation of most registers */ /* TODO: Interrupts */ -void gb_video_init(struct gb_video *video) +static struct bmp *bmp; + +#define BUFFER_WIDTH (256) +#define BUFFER_HEIGHT (256) +#define TILE_WIDTH (8) +#define TILE_HEIGHT (8) +#define TILES_WIDE (BUFFER_WIDTH / TILE_WIDTH) +#define TILES_HIGH (BUFFER_HEIGHT / TILE_HEIGHT) +#define TILE_BYTES (16) + +static uint8_t color_tbl[4] = { + 255, + 160, + 80, + 0, +}; + +void gb_video_init(struct gb_video *video, struct gb_memory *memory) { memset(video, 0, sizeof(*video)); + video->memory = memory; + + bmp = bmp_new(BMP_GRAYSCALE, BUFFER_WIDTH, BUFFER_HEIGHT); +} + +static void gb_video_fetch_tile_data(struct gb_video *video, uint8_t *databuf, + uint8_t index, int log) +{ + int i; + int logged = 0; + + uint16_t tile_addr = 0x8000; + + for (i = 0; i < TILE_BYTES; i++) { + uint16_t addr = tile_addr + (index * TILE_BYTES) + i; + databuf[i] = gb_mem_read(video->memory, addr); + if (log && databuf[i] != 0) { + printf("addr=%x, databuf[%d]=%x\n", addr, i, databuf[i]); + logged = 1; + } + } + + if (log && logged) { + printf("\n"); + } +} + +static void gb_video_screenshot(struct gb_video *video) +{ + static int id = 0; + char *buffer = bmp_get_pixel_buffer(bmp); + int tile_x, tile_y, i; + char filename[32]; + uint16_t bg_addr = 0x9800; + uint8_t tile_data[TILE_BYTES]; + uint8_t tile; + int log = id == 30; + + snprintf(filename, sizeof(filename), "screenshot-%d.bmp", id++); + printf("Rendering screenshot %s\n", filename); + + if (!(video->lcdcont & 1)) { + goto out; + } + + for (tile_y = 0; tile_y < TILES_HIGH; tile_y++) { + for (tile_x = 0; tile_x < TILES_WIDE; tile_x++) { + + /* For each tile, to render, fetch the tile data */ + uint16_t addr = bg_addr + (tile_y * TILES_WIDE) + tile_x; + + tile = gb_mem_read(video->memory, addr); + gb_video_fetch_tile_data(video, tile_data, tile, log); + + if (log) { + printf("Tile_x=%d, tile_y=%d, addr=%x, tile=%d\n", + tile_x, tile_y, addr, tile); + } + + for (i = 0 ; i < TILE_WIDTH * TILE_HEIGHT; i++) { + int offset_x = i % TILE_WIDTH; + int offset_y = i / TILE_WIDTH; + uint8_t td_index = offset_y * 2; + int total_x, total_y; + uint8_t color; + + color = tile_data[td_index + 1] >> (i % 8) & 1; + color <<= 1; + color |= (tile_data[td_index] >> (i % 8)) & 1; + + total_x = tile_x * TILE_WIDTH + (7 - offset_x); + total_y = tile_y * TILE_HEIGHT + offset_y; + + if (log && color != 0) { + printf("offset=%d, total_x=%d, total_y=%d, td_index=%d, color=%d\n", + total_y * BUFFER_WIDTH + total_x, total_x, total_y, td_index, color); + } + + buffer[(BUFFER_HEIGHT - total_y) * BUFFER_WIDTH + total_x] = color_tbl[color]; + } + } + } + +out: + bmp_write_file(bmp, filename); } void gb_video_cycle(struct gb_video *video, int cycles) @@ -20,6 +125,9 @@ void gb_video_cycle(struct gb_video *video, int cycles) video->curline += 1; if (video->curline > LCD_Y_MAX) { video->curline = 0; + if (video->lcdcont & 1 << 7) { + gb_video_screenshot(video); + } } } } diff --git a/src/gbemu/video.h b/src/gbemu/video.h index 4facce5..d2a2719 100644 --- a/src/gbemu/video.h +++ b/src/gbemu/video.h @@ -6,6 +6,8 @@ #include +#include "gbemu/memory.h" + struct gb_video { /* Comments ripped http://fms.komkon.org/GameBoy/Tech/Software.html */ @@ -90,9 +92,11 @@ struct gb_video { /******************************/ int line_counter; + + struct gb_memory *memory; }; -void gb_video_init(struct gb_video *video); +void gb_video_init(struct gb_video *video, struct gb_memory *memory); void gb_video_cycle(struct gb_video *video, int cycles); uint8_t gb_video_mem_read(struct gb_video *video, uint16_t addr); void gb_video_mem_write(struct gb_video *video, uint16_t addr, uint8_t val);