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:
2018-07-18 19:32:03 -07:00
parent cd66ad8a89
commit 53cd4ab7e5
7 changed files with 165 additions and 63 deletions

View File

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