GB: Implement sprites, SRAM

This commit is contained in:
Jeffrey Pfau 2016-01-21 19:30:51 -08:00
parent 5cd84799be
commit 8750f78808
6 changed files with 210 additions and 16 deletions

View File

@ -57,6 +57,9 @@ void GBMemoryDeinit(struct GB* gb) {
if (gb->memory.rom) {
mappedMemoryFree(gb->memory.rom, gb->memory.romSize);
}
if (gb->memory.sram) {
mappedMemoryFree(gb->memory.sram, 0x8000);
}
}
void GBMemoryReset(struct GB* gb) {
@ -67,6 +70,10 @@ void GBMemoryReset(struct GB* gb) {
gb->memory.wramBank = &gb->memory.wram[GB_SIZE_WORKING_RAM_BANK0];
gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0];
gb->memory.currentBank = 1;
gb->memory.sram = anonymousMemoryMap(0x8000); // TODO: Persist
gb->memory.sramCurrentBank = 0;
memset(&gb->video.oam, 0, sizeof(gb->video.oam));
const struct GBCartridge* cart = &gb->memory.rom[0x100];
switch (cart->type) {
@ -138,8 +145,10 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
return gb->video.vram[address & (GB_SIZE_VRAM - 1)];
case GB_REGION_EXTERNAL_RAM:
case GB_REGION_EXTERNAL_RAM + 1:
// TODO
return 0;
if (memory->sramAccess) {
return gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)];
}
return 0xFF;
case GB_REGION_WORKING_RAM_BANK0:
case GB_REGION_WORKING_RAM_BANK0 + 2:
return memory->wram[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
@ -149,9 +158,14 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) {
if (address < GB_BASE_OAM) {
return memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)];
}
if (address < GB_BASE_UNUSABLE) {
if (gb->video.mode < 2) {
return gb->video.oam.raw[address & 0xFF];
}
return 0xFF;
}
if (address < GB_BASE_IO) {
// TODO
return 0;
return 0xFF;
}
if (address < GB_BASE_HRAM) {
return GBIORead(gb, address & (GB_SIZE_IO - 1));
@ -185,7 +199,9 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
return;
case GB_REGION_EXTERNAL_RAM:
case GB_REGION_EXTERNAL_RAM + 1:
// TODO
if (memory->sramAccess) {
gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value;
}
return;
case GB_REGION_WORKING_RAM_BANK0:
case GB_REGION_WORKING_RAM_BANK0 + 2:
@ -197,8 +213,13 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) {
default:
if (address < GB_BASE_OAM) {
memory->wramBank[address & (GB_SIZE_WORKING_RAM_BANK0 - 1)] = value;
} else if (address < GB_BASE_UNUSABLE) {
if (gb->video.mode < 2) {
gb->video.oam.raw[address & 0xFF] = value;
gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF);
}
} else if (address < GB_BASE_IO) {
// TODO
// TODO: Log
} else if (address < GB_BASE_HRAM) {
GBIOWrite(gb, address & (GB_SIZE_IO - 1), value);
} else if (address < GB_BASE_IE) {
@ -282,11 +303,29 @@ static void _switchBank(struct GBMemory* memory, int bank) {
memory->currentBank = bank;
}
static void _switchSramBank(struct GBMemory* memory, int bank) {
size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM;
memory->sramBank = &memory->sram[bankStart];
memory->sramCurrentBank = bank;
}
void _GBMBC1(struct GBMemory* memory, uint16_t address, uint8_t value) {
int bank = value & 0x1F;
switch (address >> 13) {
case 0x0:
// TODO
switch (value) {
case 0:
memory->sramAccess = false;
break;
case 0xA:
memory->sramAccess = true;
_switchSramBank(memory, memory->sramCurrentBank);
break;
default:
// TODO
break;
}
break;
break;
case 0x1:
if (!bank) {
@ -305,7 +344,18 @@ void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
int bank = value & 0x7F;
switch (address >> 13) {
case 0x0:
// TODO
switch (value) {
case 0:
memory->sramAccess = false;
break;
case 0xA:
memory->sramAccess = true;
_switchSramBank(memory, memory->sramCurrentBank);
break;
default:
// TODO
break;
}
break;
case 0x1:
if (!bank) {
@ -313,6 +363,11 @@ void _GBMBC3(struct GBMemory* memory, uint16_t address, uint8_t value) {
}
_switchBank(memory, bank);
break;
case 0x2:
if (value < 4) {
_switchSramBank(memory, value);
}
break;
}
}
@ -324,7 +379,19 @@ void _GBMBC5(struct GBMemory* memory, uint16_t address, uint8_t value) {
int bank = value & 0x7F;
switch (address >> 13) {
case 0x0:
// TODO
switch (value) {
case 0:
memory->sramAccess = false;
break;
case 0xA:
memory->sramAccess = true;
_switchSramBank(memory, memory->sramCurrentBank);
break;
default:
// TODO
break;
}
break;
break;
case 0x1:
_switchBank(memory, bank);

View File

@ -20,6 +20,7 @@ enum {
GB_BASE_WORKING_RAM_BANK0 = 0xC000,
GB_BASE_WORKING_RAM_BANK1 = 0xD000,
GB_BASE_OAM = 0xFE00,
GB_BASE_UNUSABLE = 0xFEA0,
GB_BASE_IO = 0xFF00,
GB_BASE_HRAM = 0xFF80,
GB_BASE_IE = 0xFFFF

View File

@ -12,6 +12,7 @@ static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer);
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer);
static void GBVideoSoftwareRendererReset(struct GBVideoRenderer* renderer);
static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam);
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoSoftwareRendererDrawScanline(struct GBVideoRenderer* renderer, int y);
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);
@ -19,6 +20,9 @@ static void GBVideoSoftwareRendererGetPixels(struct GBVideoRenderer* renderer, u
static void GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, unsigned stride, void* pixels);
static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int x, int y, int sx, int sy);
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int y);
static void _cleanOAM(struct GBVideoSoftwareRenderer* renderer);
#ifdef COLOR_16_BIT
#ifdef COLOR_5_6_5
@ -36,6 +40,7 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) {
renderer->d.deinit = GBVideoSoftwareRendererDeinit;
renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister;
renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM;
renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM;
renderer->d.drawScanline = GBVideoSoftwareRendererDrawScanline;
renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;
renderer->d.getPixels = 0;
@ -65,6 +70,8 @@ static void GBVideoSoftwareRendererReset(struct GBVideoRenderer* renderer) {
softwareRenderer->scx = 0;
softwareRenderer->wy = 0;
softwareRenderer->wx = 0;
softwareRenderer->oamMax = 0;
softwareRenderer->oamDirty = false;
}
static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) {
@ -76,6 +83,13 @@ static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, u
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
// TODO
}
static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
UNUSED(oam);
softwareRenderer->oamDirty = true;
}
static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
switch (address) {
@ -119,7 +133,14 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer*
static void GBVideoSoftwareRendererDrawScanline(struct GBVideoRenderer* renderer, int y) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
size_t x;
for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; ++x) {
softwareRenderer->row[x] = GB_PALETTE[0];
}
if (softwareRenderer->oamDirty) {
_cleanOAM(softwareRenderer);
}
uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) {
@ -127,15 +148,27 @@ static void GBVideoSoftwareRendererDrawScanline(struct GBVideoRenderer* renderer
}
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, 0, y, softwareRenderer->scx, softwareRenderer->scy);
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc)) {
if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy < GB_VIDEO_VERTICAL_PIXELS) {
maps = &softwareRenderer->d.vram[GB_BASE_MAP];
if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) {
maps += GB_SIZE_MAP;
}
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, -7, y, softwareRenderer->wx - 7, softwareRenderer->wy);
GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, 0, y, 7 - softwareRenderer->wx, -softwareRenderer->wy);
}
size_t x;
int spriteHeight = 8;
if (GBRegisterLCDCIsObjSize(softwareRenderer->lcdc)) {
spriteHeight = 16;
}
int i;
for (i = 0; i < softwareRenderer->oamMax; ++i) {
// TODO: Sprite sizes
if (y >= softwareRenderer->obj[i]->y - 16 && y < softwareRenderer->obj[i]->y - 16 + spriteHeight) {
GBVideoSoftwareRendererDrawObj(softwareRenderer, softwareRenderer->obj[i], y);
}
}
color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y];
#ifdef COLOR_16_BIT
#if defined(__ARM_NEON) && !defined(__APPLE__)
_to16Bit(row, softwareRenderer->row, GB_VIDEO_HORIZONTAL_PIXELS);
@ -149,6 +182,24 @@ static void GBVideoSoftwareRendererDrawScanline(struct GBVideoRenderer* renderer
#endif
}
static void _cleanOAM(struct GBVideoSoftwareRenderer* renderer) {
// TODO: GBC differences
renderer->oamMax = 0;
int o = 0;
int i;
for (i = 0; i < 40; ++i) {
uint8_t y = renderer->d.oam->obj[i].y;
if (y < 16 || y >= GB_VIDEO_VERTICAL_PIXELS + 16) {
continue;
}
// TODO: Sort
renderer->obj[o] = &renderer->d.oam->obj[i];
++o;
}
renderer->oamMax = o;
renderer->oamDirty = false;
}
static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) {
struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer;
@ -165,9 +216,6 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
}
int topY = (((y + sy) >> 3) & 0x1F) * 0x20;
int bottomY = (y + sy) & 7;
if (x < 0) {
x = 0;
}
for (; x < GB_VIDEO_HORIZONTAL_PIXELS; ++x) {
int topX = ((x + sx) >> 3) & 0x1F;
int bottomX = 7 - ((x + sx) & 7);
@ -184,3 +232,45 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer
renderer->row[x] = renderer->bgPalette[((tileDataUpper & 1) << 1) | (tileDataLower & 1)];
}
}
static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int y) {
uint8_t* data = renderer->d.vram;
int tileOffset = 0;
int bottomY;
if (GBObjAttributesIsYFlip(obj->attr)) {
bottomY = 7 - ((y - obj->y - 16) & 7);
if (y - obj->y < -8) {
++tileOffset;
}
} else {
bottomY = (y - obj->y - 16) & 7;
if (y - obj->y >= -8) {
++tileOffset;
}
}
int end = GB_VIDEO_HORIZONTAL_PIXELS;
if (obj->x < end) {
end = obj->x;
}
int x = obj->x - 8;
if (x < 0) {
x = 0;
}
for (; x < end; ++x) {
int bottomX;
if (GBObjAttributesIsXFlip(obj->attr)) {
bottomX = (x - obj->x) & 7;
} else {
bottomX = 7 - ((x - obj->x) & 7);
}
int objTile = obj->tile + tileOffset;
uint8_t tileDataLower = data[(objTile * 8 + bottomY) * 2];
uint8_t tileDataUpper = data[(objTile * 8 + bottomY) * 2 + 1];
tileDataUpper >>= bottomX;
tileDataLower >>= bottomX;
color_t current = renderer->row[x];
if (((tileDataUpper | tileDataLower) & 1) && (!GBObjAttributesIsPriority(obj->attr) || current == GB_PALETTE[0])) {
renderer->row[x] = renderer->bgPalette[((tileDataUpper & 1) << 1) | (tileDataLower & 1)];
}
}
}

View File

@ -35,6 +35,10 @@ struct GBVideoSoftwareRenderer {
uint8_t wx;
GBRegisterLCDC lcdc;
struct GBObj* obj[40];
int oamMax;
bool oamDirty;
};
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);

View File

@ -15,6 +15,7 @@ static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer);
static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer);
static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address);
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam);
static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y);
static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);
static void GBVideoDummyRendererGetPixels(struct GBVideoRenderer* renderer, unsigned* stride, const void** pixels);
@ -25,6 +26,7 @@ static struct GBVideoRenderer dummyRenderer = {
.deinit = GBVideoDummyRendererDeinit,
.writeVideoRegister = GBVideoDummyRendererWriteVideoRegister,
.writeVRAM = GBVideoDummyRendererWriteVRAM,
.writeOAM = GBVideoDummyRendererWriteOAM,
.drawScanline = GBVideoDummyRendererDrawScanline,
.finishFrame = GBVideoDummyRendererFinishFrame,
.getPixels = GBVideoDummyRendererGetPixels
@ -53,6 +55,8 @@ void GBVideoReset(struct GBVideo* video) {
}
video->vram = anonymousMemoryMap(GB_SIZE_VRAM);
video->renderer->vram = video->vram;
memset(&video->oam, 0, sizeof(video->oam));
video->renderer->oam = &video->oam;
video->renderer->deinit(video->renderer);
video->renderer->init(video->renderer);
@ -200,6 +204,12 @@ static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint
// Nothing to do
}
static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint8_t oam) {
UNUSED(renderer);
UNUSED(oam);
// Nothing to do
}
static void GBVideoDummyRendererDrawScanline(struct GBVideoRenderer* renderer, int y) {
UNUSED(renderer);
UNUSED(y);

View File

@ -29,6 +29,24 @@ enum {
GB_SIZE_MAP = 0x0400
};
DECL_BITFIELD(GBObjAttributes, uint8_t);
DECL_BIT(GBObjAttributes, Palette, 4);
DECL_BIT(GBObjAttributes, XFlip, 5);
DECL_BIT(GBObjAttributes, YFlip, 6);
DECL_BIT(GBObjAttributes, Priority, 7);
struct GBObj {
uint8_t y;
uint8_t x;
uint8_t tile;
GBObjAttributes attr;
};
union GBOAM {
struct GBObj obj[40];
uint8_t raw[160];
};
struct GBVideoRenderer {
void (*init)(struct GBVideoRenderer* renderer);
void (*reset)(struct GBVideoRenderer* renderer);
@ -36,6 +54,7 @@ struct GBVideoRenderer {
uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value);
void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address);
void (*writeOAM)(struct GBVideoRenderer* renderer, uint8_t oam);
void (*drawScanline)(struct GBVideoRenderer* renderer, int y);
void (*finishFrame)(struct GBVideoRenderer* renderer);
@ -43,6 +62,7 @@ struct GBVideoRenderer {
void (*putPixels)(struct GBVideoRenderer* renderer, unsigned stride, void* pixels);
uint8_t* vram;
union GBOAM* oam;
};
DECL_BITFIELD(GBRegisterLCDC, uint8_t);
@ -80,6 +100,8 @@ struct GBVideo {
uint8_t* vram;
uint8_t* vramBank;
union GBOAM oam;
int32_t frameCounter;
int frameskip;
int frameskipCounter;