mirror of https://github.com/mgba-emu/mgba.git
GB: Implement sprites, SRAM
This commit is contained in:
parent
5cd84799be
commit
8750f78808
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@ struct GBVideoSoftwareRenderer {
|
|||
uint8_t wx;
|
||||
|
||||
GBRegisterLCDC lcdc;
|
||||
|
||||
struct GBObj* obj[40];
|
||||
int oamMax;
|
||||
bool oamDirty;
|
||||
};
|
||||
|
||||
void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue