diff --git a/src/gb/audio.c b/src/gb/audio.c index ded348521..51949555f 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -286,7 +286,7 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) { 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 @@ int32_t GBAudioProcessEvents(struct GBAudio* audio, int32_t cycles) { 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; } diff --git a/src/gb/gb.c b/src/gb/gb.c index 0c9179f53..2a8bdc8b1 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -165,7 +165,20 @@ void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) { } 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; @@ -176,8 +189,6 @@ void GBReset(struct LR35902Core* cpu) { cpu->sp = 0xFFFE; cpu->pc = 0x100; - struct GB* gb = (struct GB*) cpu->master; - if (gb->yankedRomSize) { gb->memory.romSize = gb->yankedRomSize; gb->yankedRomSize = 0; diff --git a/src/gb/gb.h b/src/gb/gb.h index 251370a9d..cbf57ebcf 100644 --- a/src/gb/gb.h +++ b/src/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 GB { struct GBVideo video; struct GBTimer timer; struct GBAudio audio; + enum GBModel model; struct mCoreSync* sync; diff --git a/src/gb/interface.h b/src/gb/interface.h new file mode 100644 index 000000000..a7f53df2b --- /dev/null +++ b/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 diff --git a/src/gb/io.c b/src/gb/io.c index bf26f8f3a..c2a3541b6 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -35,6 +35,12 @@ const static uint8_t _registerMask[] = { [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 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { 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 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { 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 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { 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 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { 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 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t 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 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { 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 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { // 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]; } diff --git a/src/gb/io.h b/src/gb/io.h index a908f468e..7c5cec231 100644 --- a/src/gb/io.h +++ b/src/gb/io.h @@ -80,6 +80,28 @@ enum GBIORegisters { 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; diff --git a/src/gb/memory.c b/src/gb/memory.c index c985bf170..86d9f512d 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -79,7 +79,7 @@ void GBMemoryReset(struct GB* gb) { 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 @@ void GBMemoryReset(struct GB* 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 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { 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 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { 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: diff --git a/src/gb/memory.h b/src/gb/memory.h index 5c45c47f1..9bf06e9c2 100644 --- a/src/gb/memory.h +++ b/src/gb/memory.h @@ -44,7 +44,8 @@ enum { 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 @@ struct GBMemory { 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); diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 7d6623adf..409e93df7 100644 --- a/src/gb/renderers/software.c +++ b/src/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 GBVideoSoftwareRendererPutPixels(struct GBVideoRenderer* renderer, u 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,28 +35,23 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->temporaryBuffer = 0; } -static void GBVideoSoftwareRendererInit(struct GBVideoRenderer* renderer) { - GBVideoSoftwareRendererReset(renderer); - - struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; - - 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]; - } - } -} - -static void GBVideoSoftwareRendererReset(struct GBVideoRenderer* 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] = softwareRenderer->palette[0]; + } + } } static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer) { @@ -80,24 +65,6 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* 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; @@ -115,6 +82,26 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* 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) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP]; @@ -174,6 +161,7 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) 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 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer 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 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer } 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 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer } 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 @@ static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* rende } } 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) { diff --git a/src/gb/renderers/software.h b/src/gb/renderers/software.h index a40741a86..6ea90a6e1 100644 --- a/src/gb/renderers/software.h +++ b/src/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 @@ struct GBVideoSoftwareRenderer { uint8_t currentWy; GBRegisterLCDC lcdc; + enum GBModel model; }; void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer*); diff --git a/src/gb/video.c b/src/gb/video.c index cee17fa62..42d297fef 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -12,10 +12,10 @@ #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 @@ void GBVideoReset(struct GBVideo* video) { 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* ren 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 } @@ -278,6 +350,12 @@ static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* re 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) { UNUSED(renderer); UNUSED(endX); diff --git a/src/gb/video.h b/src/gb/video.h index c35b1e911..2790fafd9 100644 --- a/src/gb/video.h +++ b/src/gb/video.h @@ -8,6 +8,8 @@ #include "util/common.h" +#include "core/interface.h" +#include "gb/interface.h" #include "gb/memory.h" enum { @@ -31,6 +33,8 @@ enum { }; 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 @@ union GBOAM { 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 @@ struct GBVideo { 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 diff --git a/src/gba/interface.h b/src/gba/interface.h index ac98ad000..e655b3e6f 100644 --- a/src/gba/interface.h +++ b/src/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" diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 3d554254e..174318164 100644 --- a/src/platform/qt/Window.cpp +++ b/src/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