all repos — mgba @ b7e80deda0b85e0056a1a6b95ff06cf192e5385b

mGBA Game Boy Advance Emulator

GB Video: Initial rendering work
Jeffrey Pfau jeffrey@endrift.com
Tue, 19 Jan 2016 22:10:38 -0800
commit

b7e80deda0b85e0056a1a6b95ff06cf192e5385b

parent

5fb2cfde1db99c351986488955301bfb7766d224

4 files changed, 90 insertions(+), 18 deletions(-)

jump to
M src/gb/renderers/software.csrc/gb/renderers/software.c

@@ -5,6 +5,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this

* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "software.h" +#include "gb/io.h" #include "util/memory.h" static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer);

@@ -16,6 +17,8 @@ static void GBVideoSoftwareRendererDrawScanline(struct GBVideoRenderer* renderer, int y);

static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer); static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels); static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, unsigned stride, void* pixels); + +static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, int y); #ifdef COLOR_16_BIT #ifdef COLOR_5_6_5

@@ -74,7 +77,29 @@ // TODO

} static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; - // TODO + switch (address) { + case REG_LCDC: + softwareRenderer->lcdc = value; + break; + case REG_BGP: + softwareRenderer->bgPalette[0] = GB_PALETTE[value & 3]; + softwareRenderer->bgPalette[1] = GB_PALETTE[(value >> 2) & 3]; + softwareRenderer->bgPalette[2] = GB_PALETTE[(value >> 4) & 3]; + softwareRenderer->bgPalette[3] = GB_PALETTE[(value >> 6) & 3]; + break; + case REG_OBP0: + softwareRenderer->objPalette[0][0] = GB_PALETTE[value & 3]; + softwareRenderer->objPalette[0][1] = GB_PALETTE[(value >> 2) & 3]; + softwareRenderer->objPalette[0][2] = GB_PALETTE[(value >> 4) & 3]; + softwareRenderer->objPalette[0][3] = GB_PALETTE[(value >> 6) & 3]; + break; + case REG_OBP1: + softwareRenderer->objPalette[1][0] = GB_PALETTE[value & 3]; + softwareRenderer->objPalette[1][1] = GB_PALETTE[(value >> 2) & 3]; + softwareRenderer->objPalette[1][2] = GB_PALETTE[(value >> 4) & 3]; + softwareRenderer->objPalette[1][3] = GB_PALETTE[(value >> 6) & 3]; + break; + } return value; }

@@ -83,7 +108,8 @@ struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;

color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; - // TODO + GBVideoSoftwareRendererDrawBackground(softwareRenderer, y); + size_t x; #ifdef COLOR_16_BIT #if defined(__ARM_NEON) && !defined(__APPLE__)

@@ -106,3 +132,28 @@ mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4);

softwareRenderer->temporaryBuffer = 0; } } + +static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, int y) { + uint8_t* maps = &renderer->d.vram[GB_BASE_MAP]; + if (GBRegisterLCDCIsTileMap(renderer->lcdc)) { + maps += GB_SIZE_MAP; + } + uint8_t* data = renderer->d.vram; + if (!GBRegisterLCDCIsTileData(renderer->lcdc)) { + data += 0x1000; + } + int x; + for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; ++x) { + int bgTile; + if (GBRegisterLCDCIsTileData(renderer->lcdc)) { + bgTile = maps[(x >> 3) + (0x20 * (y >> 3))]; + } else { + bgTile = ((int8_t*) maps)[(x >> 3) + (0x20 * (y >> 3))]; + } + uint8_t tileDataLower = data[(bgTile * 8 + (y & 7)) * 2]; + uint8_t tileDataUpper = data[(bgTile * 8 + (y & 7)) * 2 + 1]; + tileDataUpper >>= 7 - (x & 7); + tileDataLower >>= 7 - (x & 7); + renderer->row[x] = renderer->bgPalette[((tileDataUpper & 1) << 1) | (tileDataLower & 1)]; + } +}
M src/gb/renderers/software.hsrc/gb/renderers/software.h

@@ -24,7 +24,12 @@ int outputBufferStride;

uint32_t row[GB_VIDEO_HORIZONTAL_PIXELS]; + color_t bgPalette[4]; + color_t objPalette[2][4]; + uint32_t* temporaryBuffer; + + GBRegisterLCDC lcdc; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
M src/gb/video.csrc/gb/video.c

@@ -82,38 +82,42 @@ }

if (video->nextMode <= 0) { switch (video->mode) { case 0: - if (video->ly < GB_VIDEO_VERTICAL_TOTAL_PIXELS) { - video->renderer->drawScanline(video->renderer, video->ly); - } ++video->ly; video->p->memory.io[REG_LY] = video->ly; - if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) { - video->ly = 0; + if (video->ly < GB_VIDEO_VERTICAL_PIXELS) { + video->renderer->drawScanline(video->renderer, video->ly); + video->nextMode = GB_VIDEO_MODE_2_LENGTH; + video->mode = 2; + if (GBRegisterSTATIsOAMIRQ(video->stat)) { + video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); + GBUpdateIRQs(video->p); + } + } else { + video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH; + video->mode = 1; ++video->frameCounter; video->renderer->finishFrame(video->renderer); - video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH; - video->mode = 1; if (GBRegisterSTATIsVblankIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); GBUpdateIRQs(video->p); - } else { + } + break; + case 1: + ++video->ly; + if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) { + video->ly = 0; video->nextMode = GB_VIDEO_MODE_2_LENGTH; video->mode = 2; if (GBRegisterSTATIsOAMIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } + } else { + video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH; } - break; - case 1: - video->nextMode = GB_VIDEO_MODE_2_LENGTH; - video->mode = 2; - if (GBRegisterSTATIsOAMIRQ(video->stat)) { - video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); - GBUpdateIRQs(video->p); - } + video->p->memory.io[REG_LY] = video->ly; break; case 2: video->nextMode = GB_VIDEO_MODE_3_LENGTH;

@@ -144,6 +148,7 @@ // TODO: Does enabling the LCD start in vblank?

video->mode = 2; video->nextMode = GB_VIDEO_MODE_2_LENGTH; video->nextEvent = video->nextMode; + video->eventDiff = 0; video->stat = GBRegisterSTATSetMode(video->stat, video->mode); video->p->memory.io[REG_STAT] = video->stat; video->eventDiff = 0;
M src/gb/video.hsrc/gb/video.h

@@ -24,6 +24,9 @@ GB_VIDEO_HORIZONTAL_LENGTH = GB_VIDEO_MODE_0_LENGTH + GB_VIDEO_MODE_2_LENGTH + GB_VIDEO_MODE_3_LENGTH,

GB_VIDEO_MODE_1_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VBLANK_PIXELS, GB_VIDEO_TOTAL_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VERTICAL_TOTAL_PIXELS, + + GB_BASE_MAP = 0x1800, + GB_SIZE_MAP = 0x0400 }; struct GBVideoRenderer {

@@ -43,6 +46,13 @@ uint8_t* vram;

}; DECL_BITFIELD(GBRegisterLCDC, uint8_t); +DECL_BIT(GBRegisterLCDC, BgEnable, 0); +DECL_BIT(GBRegisterLCDC, ObjEnable, 1); +DECL_BIT(GBRegisterLCDC, ObjSize, 2); +DECL_BIT(GBRegisterLCDC, TileMap, 3); +DECL_BIT(GBRegisterLCDC, TileData, 4); +DECL_BIT(GBRegisterLCDC, Window, 5); +DECL_BIT(GBRegisterLCDC, WindowTileMap, 6); DECL_BIT(GBRegisterLCDC, Enable, 7); DECL_BITFIELD(GBRegisterSTAT, uint8_t);

@@ -68,6 +78,7 @@

int32_t nextMode; uint8_t* vram; + uint8_t* vramBank; int32_t frameCounter; int frameskip;