mirror of https://github.com/mgba-emu/mgba.git
GB Video: Initial rendering work
This commit is contained in:
parent
5fb2cfde1d
commit
b7e80deda0
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "software.h"
|
#include "software.h"
|
||||||
|
|
||||||
|
#include "gb/io.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
|
||||||
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer);
|
static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer);
|
||||||
|
@ -17,6 +18,8 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
|
||||||
static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
|
static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
|
||||||
static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, unsigned stride, 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_16_BIT
|
||||||
#ifdef COLOR_5_6_5
|
#ifdef COLOR_5_6_5
|
||||||
static const color_t GB_PALETTE[4] = { 0xFFFF, 0x39C7, 0x18C3, 0x0000};
|
static const color_t GB_PALETTE[4] = { 0xFFFF, 0x39C7, 0x18C3, 0x0000};
|
||||||
|
@ -74,7 +77,29 @@ static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, u
|
||||||
}
|
}
|
||||||
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
|
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
|
||||||
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +108,8 @@ static void GBVideoSoftwareRendererDrawScanline(struct GBVideoRenderer* renderer
|
||||||
|
|
||||||
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
|
||||||
|
|
||||||
// TODO
|
GBVideoSoftwareRendererDrawBackground(softwareRenderer, y);
|
||||||
|
|
||||||
size_t x;
|
size_t x;
|
||||||
#ifdef COLOR_16_BIT
|
#ifdef COLOR_16_BIT
|
||||||
#if defined(__ARM_NEON) && !defined(__APPLE__)
|
#if defined(__ARM_NEON) && !defined(__APPLE__)
|
||||||
|
@ -106,3 +132,28 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer)
|
||||||
softwareRenderer->temporaryBuffer = 0;
|
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)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,12 @@ struct GBVideoSoftwareRenderer {
|
||||||
|
|
||||||
uint32_t row[GB_VIDEO_HORIZONTAL_PIXELS];
|
uint32_t row[GB_VIDEO_HORIZONTAL_PIXELS];
|
||||||
|
|
||||||
|
color_t bgPalette[4];
|
||||||
|
color_t objPalette[2][4];
|
||||||
|
|
||||||
uint32_t* temporaryBuffer;
|
uint32_t* temporaryBuffer;
|
||||||
|
|
||||||
|
GBRegisterLCDC lcdc;
|
||||||
};
|
};
|
||||||
|
|
||||||
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
|
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
|
||||||
|
|
|
@ -82,38 +82,42 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {
|
||||||
if (video->nextMode <= 0) {
|
if (video->nextMode <= 0) {
|
||||||
switch (video->mode) {
|
switch (video->mode) {
|
||||||
case 0:
|
case 0:
|
||||||
if (video->ly < GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
|
|
||||||
video->renderer->drawScanline(video->renderer, video->ly);
|
|
||||||
}
|
|
||||||
++video->ly;
|
++video->ly;
|
||||||
video->p->memory.io[REG_LY] = video->ly;
|
video->p->memory.io[REG_LY] = video->ly;
|
||||||
if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
|
if (video->ly < GB_VIDEO_VERTICAL_PIXELS) {
|
||||||
video->ly = 0;
|
video->renderer->drawScanline(video->renderer, video->ly);
|
||||||
++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 {
|
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
||||||
video->mode = 2;
|
video->mode = 2;
|
||||||
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
||||||
GBUpdateIRQs(video->p);
|
GBUpdateIRQs(video->p);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
video->nextMode = GB_VIDEO_HORIZONTAL_LENGTH;
|
||||||
|
video->mode = 1;
|
||||||
|
++video->frameCounter;
|
||||||
|
video->renderer->finishFrame(video->renderer);
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
++video->ly;
|
||||||
video->mode = 2;
|
if (video->ly >= GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
|
||||||
if (GBRegisterSTATIsOAMIRQ(video->stat)) {
|
video->ly = 0;
|
||||||
video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT);
|
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
||||||
GBUpdateIRQs(video->p);
|
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->p->memory.io[REG_LY] = video->ly;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
video->nextMode = GB_VIDEO_MODE_3_LENGTH;
|
video->nextMode = GB_VIDEO_MODE_3_LENGTH;
|
||||||
|
@ -144,6 +148,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
|
||||||
video->mode = 2;
|
video->mode = 2;
|
||||||
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
video->nextMode = GB_VIDEO_MODE_2_LENGTH;
|
||||||
video->nextEvent = video->nextMode;
|
video->nextEvent = video->nextMode;
|
||||||
|
video->eventDiff = 0;
|
||||||
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
video->stat = GBRegisterSTATSetMode(video->stat, video->mode);
|
||||||
video->p->memory.io[REG_STAT] = video->stat;
|
video->p->memory.io[REG_STAT] = video->stat;
|
||||||
video->eventDiff = 0;
|
video->eventDiff = 0;
|
||||||
|
|
|
@ -24,6 +24,9 @@ enum {
|
||||||
|
|
||||||
GB_VIDEO_MODE_1_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VBLANK_PIXELS,
|
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_VIDEO_TOTAL_LENGTH = GB_VIDEO_HORIZONTAL_LENGTH * GB_VIDEO_VERTICAL_TOTAL_PIXELS,
|
||||||
|
|
||||||
|
GB_BASE_MAP = 0x1800,
|
||||||
|
GB_SIZE_MAP = 0x0400
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBVideoRenderer {
|
struct GBVideoRenderer {
|
||||||
|
@ -43,6 +46,13 @@ struct GBVideoRenderer {
|
||||||
};
|
};
|
||||||
|
|
||||||
DECL_BITFIELD(GBRegisterLCDC, uint8_t);
|
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_BIT(GBRegisterLCDC, Enable, 7);
|
||||||
|
|
||||||
DECL_BITFIELD(GBRegisterSTAT, uint8_t);
|
DECL_BITFIELD(GBRegisterSTAT, uint8_t);
|
||||||
|
@ -68,6 +78,7 @@ struct GBVideo {
|
||||||
int32_t nextMode;
|
int32_t nextMode;
|
||||||
|
|
||||||
uint8_t* vram;
|
uint8_t* vram;
|
||||||
|
uint8_t* vramBank;
|
||||||
|
|
||||||
int32_t frameCounter;
|
int32_t frameCounter;
|
||||||
int frameskip;
|
int frameskip;
|
||||||
|
|
Loading…
Reference in New Issue