GB: Start GBC support

This commit is contained in:
Jeffrey Pfau 2016-02-15 20:13:32 -08:00
parent 3cb5cdee94
commit 245a13af63
14 changed files with 346 additions and 88 deletions

View File

@ -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) {
if (audio->style == GB_AUDIO_DMG) {
audio->fadeCh3 = audio->nextCh3 + 2;
}
audio->nextCh3 += _updateChannel3(&audio->ch3, audio->style);
audio->ch3.readable = true;
}

View File

@ -165,7 +165,20 @@ void GBInterruptHandlerInit(struct LR35902InterruptHandler* irqh) {
}
void GBReset(struct LR35902Core* cpu) {
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;

View File

@ -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;

18
src/gb/interface.h Normal file
View File

@ -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

View File

@ -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 {
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 {
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];
}

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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) {

View File

@ -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*);

View File

@ -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);

View File

@ -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

View File

@ -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"

View File

@ -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