all repos — mgba @ 245a13af63adec5b26e087950e1bb5f649f872fb

mGBA Game Boy Advance Emulator

GB: Start GBC support
Jeffrey Pfau jeffrey@endrift.com
Mon, 15 Feb 2016 20:13:32 -0800
commit

245a13af63adec5b26e087950e1bb5f649f872fb

parent

3cb5cdee9416de28f6f76f4a2e57b8fb4cf408cb

M src/gb/audio.csrc/gb/audio.c

@@ -286,7 +286,7 @@ if (audio->playingCh3) {

if (audio->nextEvent == INT_MAX) { audio->eventDiff = 0; } - audio->ch3.readable = false; + audio->ch3.readable = audio->style != GB_AUDIO_DMG; // TODO: Don't need p if (audio->p) { // TODO: Where does this cycle delay come from?

@@ -535,7 +535,9 @@ audio->ch3.readable = false;

audio->fadeCh3 = INT_MAX; } if (audio->nextCh3 <= 0) { - audio->fadeCh3 = audio->nextCh3 + 2; + if (audio->style == GB_AUDIO_DMG) { + audio->fadeCh3 = audio->nextCh3 + 2; + } audio->nextCh3 += _updateChannel3(&audio->ch3, audio->style); audio->ch3.readable = true; }
M src/gb/gb.csrc/gb/gb.c

@@ -165,7 +165,20 @@ irqh->halt = GBHalt;

} void GBReset(struct LR35902Core* cpu) { - cpu->a = 1; + struct GB* gb = (struct GB*) cpu->master; + + const struct GBCartridge* cart = (const struct GBCartridge*) &gb->memory.rom[0x100]; + if (cart->cgb & 0x80) { + gb->model = GB_MODEL_CGB; + gb->audio.style = GB_AUDIO_CGB; + cpu->a = 0x11; + } else { + // TODO: SGB + gb->model = GB_MODEL_DMG; + gb->audio.style = GB_AUDIO_DMG; + cpu->a = 1; + } + cpu->f.packed = 0xB0; cpu->b = 0; cpu->c = 0x13;

@@ -175,8 +188,6 @@ cpu->h = 1;

cpu->l = 0x4D; cpu->sp = 0xFFFE; cpu->pc = 0x100; - - struct GB* gb = (struct GB*) cpu->master; if (gb->yankedRomSize) { gb->memory.romSize = gb->yankedRomSize;
M src/gb/gb.hsrc/gb/gb.h

@@ -13,6 +13,7 @@

#include "lr35902/lr35902.h" #include "gb/audio.h" +#include "gb/interface.h" #include "gb/memory.h" #include "gb/timer.h" #include "gb/video.h"

@@ -50,6 +51,7 @@ struct GBMemory memory;

struct GBVideo video; struct GBTimer timer; struct GBAudio audio; + enum GBModel model; struct mCoreSync* sync;
A src/gb/interface.h

@@ -0,0 +1,18 @@

+/* Copyright (c) 2013-2016 Jeffrey Pfau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ +#ifndef GB_INTERFACE_H +#define GB_INTERFACE_H + +#include "util/common.h" + +enum GBModel { + GB_MODEL_DMG, + GB_MODEL_SGB, + GB_MODEL_CGB, + GB_MODEL_AGB +}; + +#endif
M src/gb/io.csrc/gb/io.c

@@ -35,6 +35,12 @@ [REG_NR50] = 0x00,

[REG_NR51] = 0x00, [REG_NR52] = 0x70, [REG_STAT] = 0x80, + [REG_VBK] = 0xFE, + [REG_OCPS] = 0x40, + [REG_BCPS] = 0x40, + [REG_UNK6C] = 0xFE, + [REG_SVBK] = 0xF8, + [REG_UNK75] = 0x8F, [REG_IE] = 0xE0, };

@@ -96,7 +102,9 @@ case REG_NR11:

if (gb->audio.enable) { GBAudioWriteNR11(&gb->audio, value); } else { - GBAudioWriteNR11(&gb->audio, value & _registerMask[REG_NR11]); + if (gb->audio.style == GB_AUDIO_DMG) { + GBAudioWriteNR11(&gb->audio, value & _registerMask[REG_NR11]); + } value = 0; } break;

@@ -125,7 +133,9 @@ case REG_NR21:

if (gb->audio.enable) { GBAudioWriteNR21(&gb->audio, value); } else { - GBAudioWriteNR21(&gb->audio, value & _registerMask[REG_NR21]); + if (gb->audio.style == GB_AUDIO_DMG) { + GBAudioWriteNR21(&gb->audio, value & _registerMask[REG_NR21]); + } value = 0; } break;

@@ -248,7 +258,7 @@ case REG_WAVE_C:

case REG_WAVE_D: case REG_WAVE_E: case REG_WAVE_F: - if (!gb->audio.playingCh3) { + if (!gb->audio.playingCh3 || gb->audio.style != GB_AUDIO_DMG) { gb->audio.ch3.wavedata8[address - REG_WAVE_0] = value; } else if(gb->audio.ch3.readable) { gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1] = value;

@@ -279,11 +289,14 @@ case REG_SCY:

case REG_SCX: case REG_WY: case REG_WX: + GBVideoProcessDots(&gb->video); + value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value); + break; case REG_BGP: case REG_OBP0: case REG_OBP1: GBVideoProcessDots(&gb->video); - value = gb->video.renderer->writeVideoRegister(gb->video.renderer, address, value); + GBVideoWritePalette(&gb->video, address, value); break; case REG_STAT: GBVideoWriteSTAT(&gb->video, value);

@@ -293,12 +306,43 @@ gb->memory.ie = value;

GBUpdateIRQs(gb); return; default: + if (gb->model >= GB_MODEL_CGB) { + switch (address) { + case REG_VBK: + GBVideoSwitchBank(&gb->video, value); + break; + case REG_BCPS: + gb->video.bcpIndex = value & 0x3F; + gb->video.bcpIncrement = value & 0x80; + break; + case REG_BCPD: + GBVideoProcessDots(&gb->video); + GBVideoWritePalette(&gb->video, address, value); + break; + case REG_OCPS: + gb->video.ocpIndex = value & 0x3F; + gb->video.ocpIncrement = value & 0x80; + break; + case REG_OCPD: + GBVideoProcessDots(&gb->video); + GBVideoWritePalette(&gb->video, address, value); + break; + case REG_SVBK: + GBMemorySwitchWramBank(&gb->memory, value); + break; + default: + goto failed; + } + goto success; + } + failed: mLOG(GB_IO, STUB, "Writing to unknown register FF%02X:%02X", address, value); if (address >= GB_SIZE_IO) { return; } break; } + success: gb->memory.io[address] = value; }

@@ -345,7 +389,7 @@ case REG_WAVE_D:

case REG_WAVE_E: case REG_WAVE_F: if (gb->audio.playingCh3) { - if (gb->audio.ch3.readable) { + if (gb->audio.ch3.readable || gb->audio.style != GB_AUDIO_DMG) { return gb->audio.ch3.wavedata8[gb->audio.ch3.window >> 1]; } else { return 0xFF;

@@ -390,8 +434,19 @@ case REG_WX:

// Handled transparently by the registers break; default: + if (gb->model >= GB_MODEL_CGB) { + switch (address) { + case REG_SVBK: + case REG_VBK: + // Handled transparently by the registers + goto success; + default: + break; + } + } mLOG(GB_IO, STUB, "Reading from unknown register FF%02X", address); return 0xFF; } + success: return gb->memory.io[address] | _registerMask[address]; }
M src/gb/io.hsrc/gb/io.h

@@ -80,6 +80,28 @@ REG_OBP0 = 0x48,

REG_OBP1 = 0x49, REG_WY = 0x4A, REG_WX = 0x4B, + + // CGB + REG_KEY1 = 0x4D, + REG_VBK = 0x4F, + REG_HDMA1 = 0x51, + REG_HDMA2 = 0x52, + REG_HDMA3 = 0x53, + REG_HDMA4 = 0x54, + REG_HDMA5 = 0x55, + REG_RP = 0x56, + REG_BCPS = 0x68, + REG_BCPD = 0x69, + REG_OCPS = 0x6A, + REG_OCPD = 0x6B, + REG_UNK6C = 0x6C, + REG_SVBK = 0x70, + REG_UNK72 = 0x72, + REG_UNK73 = 0x73, + REG_UNK74 = 0x74, + REG_UNK75 = 0x75, + REG_UNK76 = 0x76, + REG_UNK77 = 0x77 }; struct GB;
M src/gb/memory.csrc/gb/memory.c

@@ -79,7 +79,7 @@ if (gb->memory.wram) {

mappedMemoryFree(gb->memory.wram, GB_SIZE_WORKING_RAM); } gb->memory.wram = anonymousMemoryMap(GB_SIZE_WORKING_RAM); - gb->memory.wramBank = &gb->memory.wram[GB_SIZE_WORKING_RAM_BANK0]; + GBMemorySwitchWramBank(&gb->memory, 1); gb->memory.romBank = &gb->memory.rom[GB_SIZE_CART_BANK0]; gb->memory.currentBank = 1; gb->memory.sramCurrentBank = 0;

@@ -140,6 +140,15 @@ GBMemoryDeinit(gb);

} } +void GBMemorySwitchWramBank(struct GBMemory* memory, int bank) { + bank &= 7; + if (!bank) { + bank = 1; + } + memory->wramBank = &memory->wram[GB_SIZE_WORKING_RAM_BANK0 * bank]; + memory->wramCurrentBank = bank; +} + uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { struct GB* gb = (struct GB*) cpu->master; struct GBMemory* memory = &gb->memory;

@@ -156,7 +165,7 @@ case GB_REGION_CART_BANK1 + 3:

return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; case GB_REGION_VRAM: case GB_REGION_VRAM + 1: - return gb->video.vram[address & (GB_SIZE_VRAM - 1)]; + return gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)]; case GB_REGION_EXTERNAL_RAM: case GB_REGION_EXTERNAL_RAM + 1: if (memory->rtcAccess) {

@@ -211,7 +220,7 @@ return;

case GB_REGION_VRAM: case GB_REGION_VRAM + 1: // TODO: Block access in wrong modes - gb->video.vram[address & (GB_SIZE_VRAM - 1)] = value; + gb->video.vramBank[address & (GB_SIZE_VRAM_BANK0 - 1)] = value; return; case GB_REGION_EXTERNAL_RAM: case GB_REGION_EXTERNAL_RAM + 1:
M src/gb/memory.hsrc/gb/memory.h

@@ -44,7 +44,8 @@ };

enum { GB_SIZE_CART_BANK0 = 0x4000, - GB_SIZE_VRAM = 0x2000, + GB_SIZE_VRAM = 0x4000, + GB_SIZE_VRAM_BANK0 = 0x2000, GB_SIZE_EXTERNAL_RAM = 0x2000, GB_SIZE_WORKING_RAM = 0x8000, GB_SIZE_WORKING_RAM_BANK0 = 0x1000,

@@ -78,6 +79,7 @@ int currentBank;

uint8_t* wram; uint8_t* wramBank; + int wramCurrentBank; bool sramAccess; uint8_t* sram;

@@ -108,6 +110,7 @@ void GBMemoryInit(struct GB* gb);

void GBMemoryDeinit(struct GB* gb); void GBMemoryReset(struct GB* gb); +void GBMemorySwitchWramBank(struct GBMemory* memory, int bank); uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address); void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value);
M src/gb/renderers/software.csrc/gb/renderers/software.c

@@ -8,10 +8,10 @@

#include "gb/io.h" #include "util/memory.h" -static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer); +static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer); -static void GBVideoSoftwareRendererReset(struct GBVideoRenderer* renderer); static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); +static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax); static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y); static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer);

@@ -21,21 +21,11 @@

static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int y, int sx, int sy); static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y); -#ifdef COLOR_16_BIT -#ifdef COLOR_5_6_5 -static const color_t GB_PALETTE[4] = { 0xFFFF, 0x528A, 0x2945, 0x0000}; -#else -static const color_t GB_PALETTE[4] = { 0x7FFF, 0x294A, 0x14A5, 0x0000}; -#endif -#else -static const color_t GB_PALETTE[4] = { 0xFFFFFF, 0xACACAC, 0x565656, 0x000000}; -#endif - void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->d.init = GBVideoSoftwareRendererInit; - renderer->d.reset = GBVideoSoftwareRendererReset; renderer->d.deinit = GBVideoSoftwareRendererDeinit; renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister; + renderer->d.writePalette = GBVideoSoftwareRendererWritePalette, renderer->d.drawRange = GBVideoSoftwareRendererDrawRange; renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline; renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame;

@@ -45,30 +35,25 @@

renderer->temporaryBuffer = 0; } -static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer) { - GBVideoSoftwareRendererReset(renderer); - +static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; + softwareRenderer->scy = 0; + softwareRenderer->scx = 0; + softwareRenderer->wy = 0; + softwareRenderer->currentWy = 0; + softwareRenderer->wx = 0; + softwareRenderer->model = model; int y; for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) { color_t* row = &softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * y]; int x; for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; ++x) { - row[x] = GB_PALETTE[0]; + row[x] = softwareRenderer->palette[0]; } } } -static void GBVideoSoftwareRendererReset(struct GBVideoRenderer* renderer) { - struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; - softwareRenderer->scy = 0; - softwareRenderer->scx = 0; - softwareRenderer->wy = 0; - softwareRenderer->currentWy = 0; - softwareRenderer->wx = 0; -} - static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; UNUSED(softwareRenderer);

@@ -80,24 +65,6 @@ switch (address) {

case REG_LCDC: softwareRenderer->lcdc = value; break; - case REG_BGP: - softwareRenderer->palette[0] = GB_PALETTE[value & 3]; - softwareRenderer->palette[1] = GB_PALETTE[(value >> 2) & 3]; - softwareRenderer->palette[2] = GB_PALETTE[(value >> 4) & 3]; - softwareRenderer->palette[3] = GB_PALETTE[(value >> 6) & 3]; - break; - case REG_OBP0: - softwareRenderer->palette[8 * 4 + 0] = GB_PALETTE[value & 3]; - softwareRenderer->palette[8 * 4 + 1] = GB_PALETTE[(value >> 2) & 3]; - softwareRenderer->palette[8 * 4 + 2] = GB_PALETTE[(value >> 4) & 3]; - softwareRenderer->palette[8 * 4 + 3] = GB_PALETTE[(value >> 6) & 3]; - break; - case REG_OBP1: - softwareRenderer->palette[9 * 4 + 0] = GB_PALETTE[value & 3]; - softwareRenderer->palette[9 * 4 + 1] = GB_PALETTE[(value >> 2) & 3]; - softwareRenderer->palette[9 * 4 + 2] = GB_PALETTE[(value >> 4) & 3]; - softwareRenderer->palette[9 * 4 + 3] = GB_PALETTE[(value >> 6) & 3]; - break; case REG_SCY: softwareRenderer->scy = value; break;

@@ -113,6 +80,26 @@ softwareRenderer->wx = value;

break; } return value; +} + +static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) { + struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; +#ifdef COLOR_16_BIT +#ifdef COLOR_5_6_5 + color_t color = 0; + color |= (value & 0x001F) << 11; + color |= (value & 0x03E0) << 1; + color |= (value & 0x7C00) >> 10; +#else + color_t color = value; +#endif +#else + color_t color = 0; + color |= (value << 3) & 0xF8; + color |= (value << 6) & 0xF800; + color |= (value << 9) & 0xF80000; +#endif + softwareRenderer->palette[index] = color; } static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax) {

@@ -174,6 +161,7 @@ }

static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer* renderer, uint8_t* maps, int startX, int endX, int y, int sx, int sy) { uint8_t* data = renderer->d.vram; + uint8_t* attr = &maps[GB_SIZE_VRAM_BANK0]; if (!GBRegisterLCDCIsTileData(renderer->lcdc)) { data += 0x1000; }

@@ -186,6 +174,8 @@ int x;

if ((startX + sx) & 7) { int startX2 = startX + 8 - ((startX + sx) & 7); for (x = startX; x < startX2; ++x) { + uint8_t* localData = data; + int localY = bottomY; int topX = ((x + sx) >> 3) & 0x1F; int bottomX = 7 - ((x + sx) & 7); int bgTile;

@@ -194,15 +184,32 @@ bgTile = maps[topX + topY];

} else { bgTile = ((int8_t*) maps)[topX + topY]; } - uint8_t tileDataLower = data[(bgTile * 8 + bottomY) * 2]; - uint8_t tileDataUpper = data[(bgTile * 8 + bottomY) * 2 + 1]; + int p = 0; + if (renderer->model >= GB_MODEL_CGB) { + GBObjAttributes attrs = attr[topX + topY]; + p = GBObjAttributesGetCGBPalette(attrs) * 4; + if (GBObjAttributesIsBank(attrs)) { + localData += GB_SIZE_VRAM_BANK0; + } + if (GBObjAttributesIsYFlip(attrs)) { + localY = 7 - bottomY; + } + if (GBObjAttributesIsXFlip(attrs)) { + bottomX = 7 - bottomX; + bottomX = 7 - bottomX; + } + } + uint8_t tileDataLower = data[(bgTile * 8 + localY) * 2]; + uint8_t tileDataUpper = data[(bgTile * 8 + localY) * 2 + 1]; tileDataUpper >>= bottomX; tileDataLower >>= bottomX; - renderer->row[x] = ((tileDataUpper & 1) << 1) | (tileDataLower & 1); + renderer->row[x] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1); } startX = startX2; } for (x = startX; x < endX; x += 8) { + uint8_t* localData = data; + int localY = bottomY; int topX = ((x + sx) >> 3) & 0x1F; int bgTile; if (GBRegisterLCDCIsTileData(renderer->lcdc)) {

@@ -210,16 +217,40 @@ bgTile = maps[topX + topY];

} else { bgTile = ((int8_t*) maps)[topX + topY]; } - uint8_t tileDataLower = data[(bgTile * 8 + bottomY) * 2]; - uint8_t tileDataUpper = data[(bgTile * 8 + bottomY) * 2 + 1]; - renderer->row[x + 7] = ((tileDataUpper & 1) << 1) | (tileDataLower & 1); - renderer->row[x + 6] = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); - renderer->row[x + 5] = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); - renderer->row[x + 4] = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); - renderer->row[x + 3] = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); - renderer->row[x + 2] = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); - renderer->row[x + 1] = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); - renderer->row[x + 0] = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); + int p = 0; + if (renderer->model >= GB_MODEL_CGB) { + GBObjAttributes attrs = attr[topX + topY]; + p = GBObjAttributesGetCGBPalette(attrs) * 4; + if (GBObjAttributesIsBank(attrs)) { + localData += GB_SIZE_VRAM_BANK0; + } + if (GBObjAttributesIsYFlip(attrs)) { + localY = 7 - bottomY; + } + if (GBObjAttributesIsXFlip(attrs)) { + uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2]; + uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1]; + renderer->row[x + 0] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1); + renderer->row[x + 1] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); + renderer->row[x + 2] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); + renderer->row[x + 3] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); + renderer->row[x + 4] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); + renderer->row[x + 5] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); + renderer->row[x + 6] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); + renderer->row[x + 7] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); + continue; + } + } + uint8_t tileDataLower = localData[(bgTile * 8 + localY) * 2]; + uint8_t tileDataUpper = localData[(bgTile * 8 + localY) * 2 + 1]; + renderer->row[x + 7] = p | ((tileDataUpper & 1) << 1) | (tileDataLower & 1); + renderer->row[x + 6] = p | (tileDataUpper & 2) | ((tileDataLower & 2) >> 1); + renderer->row[x + 5] = p | ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2); + renderer->row[x + 4] = p | ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3); + renderer->row[x + 3] = p | ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4); + renderer->row[x + 2] = p | ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5); + renderer->row[x + 1] = p | ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6); + renderer->row[x + 0] = p | ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7); } }

@@ -252,7 +283,15 @@ ++tileOffset;

} } uint8_t mask = GBObjAttributesIsPriority(obj->attr) ? 0 : 0x20; - int p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4; + int p; + if (renderer->model >= GB_MODEL_CGB) { + p = (GBObjAttributesGetCGBPalette(obj->attr) + 8) * 4; + if (GBObjAttributesIsBank(obj->attr)) { + data += GB_SIZE_VRAM_BANK0; + } + } else { + p = (GBObjAttributesGetPalette(obj->attr) + 8) * 4; + } int bottomX; int x; for (x = startX; x < endX; ++x) {
M src/gb/renderers/software.hsrc/gb/renderers/software.h

@@ -9,6 +9,7 @@

#include "util/common.h" #include "core/core.h" +#include "gb/gb.h" #include "gb/video.h" struct GBVideoSoftwareRenderer {

@@ -30,6 +31,7 @@ uint8_t wx;

uint8_t currentWy; GBRegisterLCDC lcdc; + enum GBModel model; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*);
M src/gb/video.csrc/gb/video.c

@@ -12,10 +12,10 @@ #include "gb/io.h"

#include "util/memory.h" -static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer); -static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer); +static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer); static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); +static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax); static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y); static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer);

@@ -25,9 +25,9 @@ static void _cleanOAM(struct GBVideo* video, int y);

static struct GBVideoRenderer dummyRenderer = { .init = GBVideoDummyRendererInit, - .reset = GBVideoDummyRendererReset, .deinit = GBVideoDummyRendererDeinit, .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, + .writePalette = GBVideoDummyRendererWritePalette, .drawRange = GBVideoDummyRendererDrawRange, .finishScanline = GBVideoDummyRendererFinishScanline, .finishFrame = GBVideoDummyRendererFinishFrame,

@@ -58,12 +58,13 @@ if (video->vram) {

mappedMemoryFree(video->vram, GB_SIZE_VRAM); } video->vram = anonymousMemoryMap(GB_SIZE_VRAM); + GBVideoSwitchBank(video, 0); 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); + video->renderer->init(video->renderer, video->p->model); } void GBVideoDeinit(struct GBVideo* video) {

@@ -75,7 +76,7 @@ void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* renderer) {

video->renderer->deinit(video->renderer); video->renderer = renderer; renderer->vram = video->vram; - video->renderer->init(video->renderer); + video->renderer->init(video->renderer, video->p->model); } int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) {

@@ -257,13 +258,84 @@ void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) {

video->stat = (video->stat & 0x7) | (value & 0x78); } -static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer) { - UNUSED(renderer); - // Nothing to do +void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) { + static const uint16_t dmgPalette[4] = { 0x7FFF, 0x294A, 0x14A5, 0x0000}; + if (video->p->model < GB_MODEL_CGB) { + switch (address) { + case REG_BGP: + video->palette[0] = dmgPalette[value & 3]; + video->palette[1] = dmgPalette[(value >> 2) & 3]; + video->palette[2] = dmgPalette[(value >> 4) & 3]; + video->palette[3] = dmgPalette[(value >> 6) & 3]; + video->renderer->writePalette(video->renderer, 0, video->palette[0]); + video->renderer->writePalette(video->renderer, 1, video->palette[1]); + video->renderer->writePalette(video->renderer, 2, video->palette[2]); + video->renderer->writePalette(video->renderer, 3, video->palette[3]); + break; + case REG_OBP0: + video->palette[8 * 4 + 0] = dmgPalette[value & 3]; + video->palette[8 * 4 + 1] = dmgPalette[(value >> 2) & 3]; + video->palette[8 * 4 + 2] = dmgPalette[(value >> 4) & 3]; + video->palette[8 * 4 + 3] = dmgPalette[(value >> 6) & 3]; + video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]); + video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]); + video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]); + video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]); + break; + case REG_OBP1: + video->palette[9 * 4 + 0] = dmgPalette[value & 3]; + video->palette[9 * 4 + 1] = dmgPalette[(value >> 2) & 3]; + video->palette[9 * 4 + 2] = dmgPalette[(value >> 4) & 3]; + video->palette[9 * 4 + 3] = dmgPalette[(value >> 6) & 3]; + video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]); + video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]); + video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]); + video->renderer->writePalette(video->renderer, 9 * 4 + 3, video->palette[9 * 4 + 3]); + break; + } + } else { + switch (address) { + case REG_BCPD: + if (video->bcpIndex & 1) { + video->palette[video->bcpIndex >> 1] &= 0x00FF; + video->palette[video->bcpIndex >> 1] |= value << 8; + } else { + video->palette[video->bcpIndex >> 1] &= 0xFF00; + video->palette[video->bcpIndex >> 1] |= value; + } + video->renderer->writePalette(video->renderer, video->bcpIndex >> 1, video->palette[video->bcpIndex >> 1]); + if (video->bcpIncrement) { + ++video->bcpIndex; + video->bcpIndex &= 0x3F; + } + break; + case REG_OCPD: + if (video->ocpIndex & 1) { + video->palette[8 * 4 + (video->ocpIndex >> 1)] &= 0x00FF; + video->palette[8 * 4 + (video->ocpIndex >> 1)] |= value << 8; + } else { + video->palette[8 * 4 + (video->ocpIndex >> 1)] &= 0xFF00; + video->palette[8 * 4 + (video->ocpIndex >> 1)] |= value; + } + video->renderer->writePalette(video->renderer, 8 * 4 + (video->ocpIndex >> 1), video->palette[8 * 4 + (video->ocpIndex >> 1)]); + if (video->ocpIncrement) { + ++video->ocpIndex; + video->ocpIndex &= 0x3F; + } + break; + } + } } -static void GBVideoDummyRendererReset(struct GBVideoRenderer* renderer) { +void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) { + value &= 1; + video->vramBank = &video->vram[value * GB_SIZE_VRAM_BANK0]; + video->vramCurrentBank = value; +} + +static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { UNUSED(renderer); + UNUSED(model); // Nothing to do }

@@ -276,6 +348,12 @@ static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) {

UNUSED(renderer); UNUSED(address); return value; +} + +static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) { + UNUSED(renderer); + UNUSED(index); + UNUSED(value); } static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** obj, size_t oamMax) {
M src/gb/video.hsrc/gb/video.h

@@ -8,6 +8,8 @@ #define GB_VIDEO_H

#include "util/common.h" +#include "core/interface.h" +#include "gb/interface.h" #include "gb/memory.h" enum {

@@ -31,6 +33,8 @@ GB_SIZE_MAP = 0x0400

}; DECL_BITFIELD(GBObjAttributes, uint8_t); +DECL_BITS(GBObjAttributes, CGBPalette, 0, 3); +DECL_BIT(GBObjAttributes, Bank, 3); DECL_BIT(GBObjAttributes, Palette, 4); DECL_BIT(GBObjAttributes, XFlip, 5); DECL_BIT(GBObjAttributes, YFlip, 6);

@@ -48,12 +52,13 @@ struct GBObj obj[40];

uint8_t raw[160]; }; +enum GBModel; struct GBVideoRenderer { - void (*init)(struct GBVideoRenderer* renderer); - void (*reset)(struct GBVideoRenderer* renderer); + void (*init)(struct GBVideoRenderer* renderer, enum GBModel model); void (*deinit)(struct GBVideoRenderer* renderer); uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); + void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value); void (*drawRange)(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj** objOnLine, size_t nObj); void (*finishScanline)(struct GBVideoRenderer* renderer, int y); void (*finishFrame)(struct GBVideoRenderer* renderer);

@@ -101,11 +106,19 @@ int32_t dotCounter;

uint8_t* vram; uint8_t* vramBank; + int vramCurrentBank; union GBOAM oam; struct GBObj* objThisLine[10]; int objMax; + int bcpIndex; + bool bcpIncrement; + int ocpIndex; + bool ocpIncrement; + + uint16_t palette[128]; + int32_t frameCounter; int frameskip; int frameskipCounter;

@@ -120,5 +133,7 @@ void GBVideoProcessDots(struct GBVideo* video);

void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value); void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value); +void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value); +void GBVideoSwitchBank(struct GBVideo* video, uint8_t value); #endif
M src/gba/interface.hsrc/gba/interface.h

@@ -3,8 +3,8 @@ *

* This Source Code Form is subject to the terms of the Mozilla Public * 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/. */ -#ifndef INTERFACE_H -#define INTERFACE_H +#ifndef GBA_INTERFACE_H +#define GBA_INTERFACE_H #include "util/common.h"
M src/platform/qt/Window.cppsrc/platform/qt/Window.cpp

@@ -275,6 +275,7 @@ void Window::selectROM() {

QStringList formats{ "*.gba", "*.gb", + "*.gbc", #if defined(USE_LIBZIP) || defined(USE_ZLIB) "*.zip", #endif

@@ -296,6 +297,7 @@ void Window::replaceROM() {

QStringList formats{ "*.gba", "*.gb", + "*.gbc", #if defined(USE_LIBZIP) || defined(USE_ZLIB) "*.zip", #endif