video: first signs of life
The video code now writes out a bmp file per-frame. With this patch applied, the Nintendo logo is rendered in each frame. There is still plenty of work to be done. Pretty much everything is hardcoded to make the bootrom work.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef _BMP_H_
|
||||
#define _BMP_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,15 +1,120 @@
|
||||
#include "gbemu/video.h"
|
||||
#include "gbemu/memory.h"
|
||||
#include "common/bmp.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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);
|
||||
|
||||
Reference in New Issue
Block a user