diff --git a/src/emulator.c b/src/emulator.c index 666db89..8c5cab7 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -2,12 +2,19 @@ Released under the terms of the MIT License. See LICENSE for details. */ #include +#include #include "emulator.h" #include "logging.h" +#define SCREEN_WIDTH 3 * (160 + 96) // TODO: define elsewhere; use scale +#define SCREEN_HEIGHT 3 * (144 + 48) + static GameGear *global_gg; +static SDL_Window *window; +SDL_Renderer* renderer; + /* Signal handler for SIGINT. */ @@ -19,12 +26,56 @@ static void handle_sigint(int sig) } /* + TODO: ... +*/ +static void setup_graphics() +{ + if (SDL_Init(SDL_INIT_VIDEO) < 0) + FATAL("SDL failed to initialize: %s", SDL_GetError()); + + SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, + SDL_WINDOW_BORDERLESS/* |SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE ? */, + &window, &renderer); + + if (!window) + FATAL("SDL failed to create a window: %s", SDL_GetError()); + if (!renderer) + FATAL("SDL failed to create a renderer: %s", SDL_GetError()); + + SDL_SetRenderDrawColor(renderer, 0x33, 0x33, 0x33, 0xFF); + SDL_RenderClear(renderer); + SDL_RenderPresent(renderer); +} + +/* GameGear callback: handle SDL logic at the end of a frame. */ static void draw_frame(GameGear *gg) { - (void) gg; - // TODO: SDL draw / switch buffers here + SDL_RenderPresent(renderer); + + SDL_Event e; + while (SDL_PollEvent(&e)) { + if (e.type == SDL_QUIT) { + gamegear_power_off(gg); + return; + } + } + + SDL_SetRenderDrawColor(renderer, 0x33, 0x33, 0x33, 0xFF); + SDL_RenderClear(renderer); +} + +/* + TODO: ... +*/ +static void cleanup_graphics() +{ + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + window = NULL; + renderer = NULL; + SDL_Quit(); } /* @@ -37,6 +88,7 @@ void emulate(GameGear *gg) global_gg = gg; signal(SIGINT, handle_sigint); gamegear_set_callback(gg, draw_frame); + setup_graphics(); gamegear_simulate(gg); @@ -47,6 +99,7 @@ void emulate(GameGear *gg) if (DEBUG_LEVEL) gamegear_print_state(gg); + cleanup_graphics(); gamegear_clear_callback(gg); signal(SIGINT, SIG_DFL); global_gg = NULL; diff --git a/src/io.c b/src/io.c index 4c4cc7a..c66172a 100644 --- a/src/io.c +++ b/src/io.c @@ -34,6 +34,8 @@ bool io_check_irq(IO *io) uint8_t io_port_read(IO *io, uint8_t port) { if (port <= 0x06) { + if (port == 0x00) + return 0xC0; // TODO: GG specific registers; initial state: C0 7F FF 00 FF 00 FF } else if (port <= 0x3F) { return 0xFF; @@ -47,8 +49,10 @@ uint8_t io_port_read(IO *io, uint8_t port) return vdp_read_control(io->vdp); } else if (port == 0xCD || port == 0xDC) { // TODO: Return the I/O port A/B register + return 0xFF; } else if (port == 0xC1 || port == 0xDD) { // TODO: Return the I/O port B/misc. register + return 0xFF; } else { return 0xFF; } diff --git a/src/vdp.c b/src/vdp.c index de6a978..f2c8f28 100644 --- a/src/vdp.c +++ b/src/vdp.c @@ -2,6 +2,7 @@ Released under the terms of the MIT License. See LICENSE for details. */ #include +#include #include "vdp.h" #include "util.h" @@ -11,6 +12,8 @@ #define CODE_REG_WRITE 2 #define CODE_CRAM_WRITE 3 +extern SDL_Renderer* renderer; + /* Initialize the Video Display Processor (VDP). */ @@ -100,6 +103,89 @@ static uint8_t get_backdrop_addr(const VDP *vdp) } /* + TODO: ... +*/ +static void draw_pixel(uint8_t y, uint8_t x, uint16_t color) +{ + uint8_t r = 0x11 * (color & 0x000F); + uint8_t g = 0x11 * ((color & 0x00F0) >> 4); + uint8_t b = 0x11 * ((color & 0x0F00) >> 8); + + SDL_SetRenderDrawColor(renderer, r, g, b, 0xFF); + + uint8_t i, j; + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) + SDL_RenderDrawPoint(renderer, 3 * x + i, 3 * y + j); + } +} + +/* + Draw the background of the current scanline. +*/ +static void draw_background(VDP *vdp) +{ + uint8_t *pnt = vdp->vram + get_pnt_base(vdp); + + uint8_t row = (vdp->v_counter + vdp->regs[0x09]) % (28 * 8); + uint8_t col; + + for (col = 6; col < 32 - 6; col++) { + uint16_t index = (row >> 3) * 32 + col; + uint16_t tile = pnt[2 * index] + (pnt[2 * index + 1] << 8); + uint16_t pattern = tile & 0x01FF; + bool palette = tile & 0x0800; + bool priority = tile & 0x1000; + bool vflip = tile & 0x0400; + bool hflip = tile & 0x0200; + + uint8_t offy = vflip ? (7 - row % 8) : (row % 8); + uint8_t *pixels = &vdp->vram[pattern * 32 + 4 * offy]; + uint8_t bp0 = pixels[0], bp1 = pixels[1], + bp2 = pixels[2], bp3 = pixels[3]; + + uint8_t idx, i, offx; + uint16_t color; + + for (i = 0; i < 8; i++) { + idx = ((bp0 >> i) & 1) + + (((bp1 >> i) & 1) << 1) + + (((bp2 >> i) & 1) << 2) + + (((bp3 >> i) & 1) << 3); + color = vdp->cram[2 * idx] + (vdp->cram[2 * idx + 1] << 8); + offx = hflip ? (col * 8 + (7 - i)) : (col * 8 + i); + draw_pixel(vdp->v_counter, offx, color); + } + } +} + +/* + Draw sprites in the current scanline. +*/ +static void draw_sprites(VDP *vdp) +{ + uint8_t *sat = vdp->vram + get_sat_base(vdp); + uint8_t spritebuf[8], nsprites = 0, i; + + for (i = 0; i < 64; i++) { + uint8_t y = sat[i] - 1; + if (vdp->v_counter >= y && vdp->v_counter < y + 8) { + DEBUG("sprite draw!!!") + // TODO + } + } +} + +/* + Draw the current scanline. +*/ +static void draw_scanline(VDP *vdp) +{ + draw_background(vdp); + draw_sprites(vdp); +} + +/* Advance the V counter for the next scanline. */ static void advance_scanline(VDP *vdp) @@ -114,15 +200,12 @@ static void advance_scanline(VDP *vdp) } /* - Simulate one scanline within the VDP. - - TODO: elaborate + Simulate one line within the VDP. */ void vdp_simulate_line(VDP *vdp) { - if (vdp->v_counter >= 0x18 && vdp->v_counter < 0xA8) { - // TODO: draw current line - } + if (vdp->v_counter >= 0x18 && vdp->v_counter < 0xA8) + draw_scanline(vdp); if (vdp->v_counter == 0xC0) vdp->stat_int = true; advance_scanline(vdp); @@ -277,4 +360,36 @@ void vdp_dump_registers(const VDP *vdp) DEBUG("- %04X %04X %04X %04X %04X %04X %04X %04X", w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]) } + + return; + + DEBUG("Dumping PNT:") + for (uint16_t i = 0; i < 28 * 32; i += 32) { + uint16_t w[32]; + for (uint8_t j = 0; j < 32; j++) + w[j] = vdp->vram[get_pnt_base(vdp) + 2 * (i + j)] + + (vdp->vram[get_pnt_base(vdp) + 2 * (i + j) + 1] << 8); + + DEBUG("- %03X %03X %03X %03X %03X %03X %03X %03X" + " %03X %03X %03X %03X %03X %03X %03X %03X" + " %03X %03X %03X %03X %03X %03X %03X %03X" + " %03X %03X %03X %03X %03X %03X %03X %03X", + w[0x00], w[0x01], w[0x02], w[0x03], w[0x04], w[0x05], w[0x06], w[0x07], + w[0x08], w[0x09], w[0x0A], w[0x0B], w[0x0C], w[0x0D], w[0x0E], w[0x0F], + w[0x10], w[0x11], w[0x12], w[0x13], w[0x14], w[0x15], w[0x16], w[0x17], + w[0x18], w[0x19], w[0x1A], w[0x1B], w[0x1C], w[0x1D], w[0x1E], w[0x1F]) + } + + DEBUG("Dumping PGT:") + for (uint16_t i = 0; i < /* 512 */ 16; i++) { + uint32_t w[8]; + for (uint8_t j = 0; j < 8; j++) + w[j] = vdp->vram[32 * i + 4 * j] + + (vdp->vram[32 * i + 4 * j + 1] << 8) + + (vdp->vram[32 * i + 4 * j + 2] << 16) + + (vdp->vram[32 * i + 4 * j + 3] << 24); + + DEBUG("- 0x%04X: %08X %08X %08X %08X %08X %08X %08X %08X", i, + w[0], w[1], w[2], w[3], w[4], w[5], w[6], w[7]) + } }