mirror of https://github.com/mgba-emu/mgba.git
GB: Start GBC support
This commit is contained in:
parent
3cb5cdee94
commit
245a13af63
|
@ -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;
|
||||
}
|
||||
|
|
15
src/gb/gb.c
15
src/gb/gb.c
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
61
src/gb/io.c
61
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 {
|
||||
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];
|
||||
}
|
||||
|
|
22
src/gb/io.h
22
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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue