From e8fe5e47970fee8a5c5b38469afb0dd1b7c1d86d Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Wed, 12 Jul 2017 17:48:08 -0400 Subject: [PATCH] Implement some missing graphics features (fixes #4) * Sprite collision flag * Background tile priority flag * Display blank flag --- src/emulator.c | 4 +++- src/vdp.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/emulator.c b/src/emulator.c index 89373da..1606db3 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -39,6 +39,8 @@ static void setup_graphics(bool fullscreen, unsigned scale) if (SDL_Init(SDL_INIT_VIDEO) < 0) FATAL("SDL failed to initialize: %s", SDL_GetError()); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); + uint32_t flags; if (fullscreen) flags = SDL_WINDOW_FULLSCREEN_DESKTOP; @@ -64,7 +66,7 @@ static void setup_graphics(bool fullscreen, unsigned scale) sizeof(uint32_t) * GG_SCREEN_WIDTH * GG_SCREEN_HEIGHT); SDL_RenderSetLogicalSize(emu.renderer, GG_SCREEN_WIDTH, GG_SCREEN_HEIGHT); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); + SDL_SetTextureBlendMode(emu.texture, SDL_BLENDMODE_BLEND); SDL_SetWindowTitle(emu.window, "crater"); SDL_ShowCursor(SDL_DISABLE); SDL_GL_SetSwapInterval(1); // Vsync diff --git a/src/vdp.c b/src/vdp.c index 32edfaa..47b1fa8 100644 --- a/src/vdp.c +++ b/src/vdp.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2014-2016 Ben Kurtovic +/* Copyright (C) 2014-2017 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ #include @@ -18,6 +18,9 @@ #define CODE_REG_WRITE 2 #define CODE_CRAM_WRITE 3 +#define COLBUF_BG_PRIORITY 0x10 +#define COLBUF_OPAQUE_SPRITE 0x20 + /* Initialize the Video Display Processor (VDP). @@ -90,6 +93,14 @@ static bool should_frame_interrupt(const VDP *vdp) } /* + Return whether the display should be visible or completely blank. +*/ +static bool is_display_visible(const VDP *vdp) +{ + return vdp->regs[0x01] & 0x40; +} + +/* Get the height of all sprites in patterns (1 pattern = 8x8 pixels). */ static uint8_t get_sprite_height(const VDP *vdp) @@ -180,24 +191,41 @@ static uint16_t get_color(const VDP *vdp, uint8_t index, bool palette) } /* + Toggle visibility of the display. + + This sets the alpha channel of every pixel to be 0xFF (if true) + or 0x00 (if false). +*/ +static void toggle_display_visibility(VDP *vdp, bool visible) +{ + if (!vdp->pixels) + return; + + uint8_t a = visible ? 0xFF : 0x00; + for (size_t i = 0; i < 160 * 144; i++) + vdp->pixels[i] = (a << 24) | (vdp->pixels[i] & 0x00FFFFFF); +} + +/* Draw a pixel onto our pixel array at the given coordinates. The color should be in BGR444 format, as returned by get_color(). */ static void draw_pixel(VDP *vdp, uint8_t y, uint8_t x, uint16_t color) { + uint8_t a = is_display_visible(vdp) ? 0xFF : 0x00; uint8_t r = 0x11 * (color & 0x000F); uint8_t g = 0x11 * ((color & 0x00F0) >> 4); uint8_t b = 0x11 * ((color & 0x0F00) >> 8); - uint32_t argb = (0xFF << 24) + (r << 16) + (g << 8) + b; + uint32_t argb = (a << 24) + (r << 16) + (g << 8) + b; vdp->pixels[y * 160 + x] = argb; } /* Draw the background of the current scanline. */ -static void draw_background(VDP *vdp) +static void draw_background(VDP *vdp, uint8_t *colbuf) { uint8_t src_row = (vdp->v_counter + get_bg_vscroll(vdp)) % (28 << 3); uint8_t dst_row = vdp->v_counter - 0x18; @@ -216,8 +244,6 @@ static void draw_background(VDP *vdp) bool vflip = tile & 0x0400; bool hflip = tile & 0x0200; - (void) priority; // TODO - uint8_t vshift = vflip ? (7 - src_row % 8) : (src_row % 8), hshift; uint8_t pixel, index; int16_t dst_col; @@ -232,6 +258,9 @@ static void draw_background(VDP *vdp) index = read_pattern(vdp, pattern, vshift, hshift); color = get_color(vdp, index, palette); draw_pixel(vdp, dst_row, dst_col, color); + + if (priority && index != 0) + colbuf[dst_col] |= COLBUF_BG_PRIORITY; } } } @@ -239,7 +268,7 @@ static void draw_background(VDP *vdp) /* Draw sprites in the current scanline. */ -static void draw_sprites(VDP *vdp) +static void draw_sprites(VDP *vdp, uint8_t *colbuf) { uint8_t *sat = vdp->vram + get_sat_base(vdp); uint8_t spritebuf[8], nsprites = 0, i; @@ -258,8 +287,6 @@ static void draw_sprites(VDP *vdp) } } - // TODO: collisions - uint8_t dst_row = vdp->v_counter - 0x18; while (nsprites-- > 0) { @@ -288,11 +315,18 @@ static void draw_sprites(VDP *vdp) dst_col = x + pixel - (6 << 3); if (dst_col < 0 || dst_col >= 160) continue; + if (colbuf[dst_col] & COLBUF_BG_PRIORITY) + continue; index = read_pattern(vdp, pattern, vshift, pixel); if (index == 0) continue; + if (colbuf[dst_col] & COLBUF_OPAQUE_SPRITE) + vdp->flags |= FLAG_SPR_COL; + else + colbuf[dst_col] |= COLBUF_OPAQUE_SPRITE; + color = get_color(vdp, index, 1); draw_pixel(vdp, dst_row, dst_col, color); } @@ -307,8 +341,9 @@ static void draw_scanline(VDP *vdp) if (!vdp->pixels) return; - draw_background(vdp); - draw_sprites(vdp); + uint8_t colbuf[160] = {0x00}; + draw_background(vdp, colbuf); + draw_sprites(vdp, colbuf); } /* @@ -396,6 +431,16 @@ uint8_t vdp_read_data(VDP *vdp) } /* + Set the given VDP register. +*/ +static void write_reg(VDP *vdp, uint8_t reg, uint8_t byte) +{ + if (reg == 0x01 && (vdp->regs[reg] & 0x40) != (byte & 0x40)) + toggle_display_visibility(vdp, byte & 0x40); + vdp->regs[reg] = byte; +} + +/* Write a byte into the VDP's control port. Depending on the status of the control flag, this will either update the @@ -425,7 +470,7 @@ void vdp_write_control(VDP *vdp, uint8_t byte) } else if (vdp->control_code == CODE_REG_WRITE) { uint8_t reg = byte & 0x0F; if (reg <= VDP_REGS) - vdp->regs[reg] = vdp->control_addr & 0xFF; + write_reg(vdp, reg, vdp->control_addr & 0xFF); } }