Update to v083r05 release.

(r04 was not posted to the WIP thread)

byuu says:

NES: passes ppu_sprite_overflow tests 01, 02, 05.
Game Boy: uses DMG BIOS (the one with the slow title scroll) or SGB
BIOS, based upon how you load the game.
Game Boy Color: Everything except the IR port is emulated. I don't have
any plans to allow linking two instances of bsnes. And that's frankly
never going to happen over netplay anyway, due to latency requirements
of the serial/IR ports.
The new DMA stuff is possibly incorrect, my test games don't seem to use
it.
Zelda: DX usually resets or crashes on the intro right before the beach
scene. I'm not sure why. Skip the intro and the game plays fine.

This is the best I can do when the most up-to-date GB/C reference
document is over ten years old and half-assed (pandocs.)
I could really use some help from anyone who understands the system.
Probably the worst part of my emulation at the moment is the interrupt
system.
Lots of things real hardware doesn't allow (DMA outside HRAM, CGB DMA to
invalid addresses, etc) isn't blocked yet.
LCD renderer is still scanline-based, which is just terrible. Doesn't
seem to be any good docs on cycle-level operation. I only know that it's
incredibly pathological and variable.
This commit is contained in:
Tim Allen 2011-10-27 11:00:17 +11:00
parent 6b708de893
commit 118a393c4c
44 changed files with 1003 additions and 325 deletions

View File

@ -47,12 +47,14 @@ void APU::main() {
master.run();
interface->audioSample(master.center, master.left, master.right);
if(++clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
clock += 2;
if(clock >= 0) co_switch(scheduler.active_thread = cpu.thread);
}
}
void APU::power() {
create(Main, 4194304);
create(Main, 8 * 1024 * 1024);
for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
for(auto &n : mmio_data) n = 0x00;

View File

@ -16,14 +16,11 @@ namespace GameBoy {
#include "serialization.cpp"
Cartridge cartridge;
void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
void Cartridge::load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
if(size == 0) size = 32768;
romdata = allocate<uint8>(romsize = size, 0xff);
if(data) memcpy(romdata, data, size);
//uint32_t crc = crc32_calculate(data, size);
//print("CRC32 = ", hex<4>(crc), "\n");
info.mapper = Mapper::Unknown;
info.ram = false;
info.battery = false;
@ -64,7 +61,7 @@ void Cartridge::load(const string &markup, const uint8_t *data, unsigned size) {
}
ramdata = new uint8_t[ramsize = info.ramsize]();
system.load();
system.load(revision);
loaded = true;
sha256 = nall::sha256(romdata, romsize);
@ -101,12 +98,28 @@ void Cartridge::ram_write(unsigned addr, uint8 data) {
}
uint8 Cartridge::mmio_read(uint16 addr) {
if(bootrom_enable && within<0x0000, 0x00ff>(addr)) return System::BootROM::sgb[addr];
if(addr == 0xff50) return 0x00;
if(bootrom_enable) {
const uint8 *data = nullptr;
switch(system.revision()) { default:
case System::Revision::GameBoy: data = System::BootROM::dmg; break;
case System::Revision::SuperGameBoy: data = System::BootROM::sgb; break;
case System::Revision::GameBoyColor: data = System::BootROM::cgb; break;
}
if(addr >= 0x0000 && addr <= 0x00ff) return data[addr];
if(addr >= 0x0200 && addr <= 0x08ff && system.cgb()) return data[addr - 256];
}
return mapper->mmio_read(addr);
}
void Cartridge::mmio_write(uint16 addr, uint8 data) {
if(bootrom_enable && addr == 0xff50) bootrom_enable = false;
if(bootrom_enable && addr == 0xff50) {
bootrom_enable = false;
return;
}
mapper->mmio_write(addr, data);
}

View File

@ -45,7 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
MMIO *mapper;
bool bootrom_enable;
void load(const string &markup, const uint8_t *data, unsigned size);
void load(System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
void unload();
uint8 rom_read(unsigned addr);

View File

@ -571,6 +571,11 @@ void CPU::op_halt() {
}
void CPU::op_stop() {
if(status.speed_switch) {
status.speed_switch = 0;
status.speed_double ^= 1;
return;
}
status.stop = true;
while(status.stop == true) op_io();
}

View File

@ -94,15 +94,43 @@ void CPU::interrupt_exec(uint16 pc) {
}
void CPU::power() {
create(Main, 4194304);
create(Main, 8 * 1024 * 1024);
for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM
for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror)
for(unsigned n = 0xff00; n <= 0xff0f; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xff80; n <= 0xffff; n++) bus.mmio[n] = this; //HRAM+IE
for(unsigned n = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM
for(unsigned n = 0; n < 8192; n++) wram[n] = 0x00;
for(unsigned n = 0; n < 128; n++) hram[n] = 0x00;
bus.mmio[0xff00] = this; //JOYP
bus.mmio[0xff01] = this; //SB
bus.mmio[0xff02] = this; //SC
bus.mmio[0xff04] = this; //DIV
bus.mmio[0xff05] = this; //TIMA
bus.mmio[0xff06] = this; //TMA
bus.mmio[0xff07] = this; //TAC
bus.mmio[0xff0f] = this; //IF
bus.mmio[0xff46] = this; //DMA
bus.mmio[0xffff] = this; //IE
if(system.cgb()) {
bus.mmio[0xff4d] = this; //KEY1
bus.mmio[0xff51] = this; //HDMA1
bus.mmio[0xff52] = this; //HDMA2
bus.mmio[0xff53] = this; //HDMA3
bus.mmio[0xff54] = this; //HDMA4
bus.mmio[0xff55] = this; //HDMA5
bus.mmio[0xff56] = this; //RP
bus.mmio[0xff6c] = this; //???
bus.mmio[0xff70] = this; //SVBK
bus.mmio[0xff72] = this; //???
bus.mmio[0xff73] = this; //???
bus.mmio[0xff74] = this; //???
bus.mmio[0xff75] = this; //???
bus.mmio[0xff76] = this; //???
bus.mmio[0xff77] = this; //???
}
for(auto &n : wram) n = 0x00;
for(auto &n : hram) n = 0x00;
r[PC] = 0x0000;
r[SP] = 0x0000;
@ -143,6 +171,23 @@ void CPU::power() {
status.interrupt_request_stat = 0;
status.interrupt_request_vblank = 0;
status.speed_double = 0;
status.speed_switch = 0;
status.dma_source = 0;
status.dma_target = 0;
status.dma_mode = 0;
status.dma_length = 0;
status.ff6c = 0;
status.ff72 = 0;
status.ff73 = 0;
status.ff74 = 0;
status.ff75 = 0;
status.wram_bank = 1;
status.interrupt_enable_joypad = 0;
status.interrupt_enable_serial = 0;
status.interrupt_enable_timer = 0;

View File

@ -54,6 +54,32 @@ struct CPU : Processor, MMIO {
bool interrupt_request_stat;
bool interrupt_request_vblank;
//$ff4d KEY1
bool speed_double;
bool speed_switch;
//$ff51,$ff52 HDMA1,HDMA2
uint16 dma_source;
//$ff53,$ff54 HDMA3,HDMA4
uint16 dma_target;
//$ff55 HDMA5
bool dma_mode;
uint16 dma_length;
//$ff6c ???
uint8 ff6c;
//$ff70 SVBK
uint3 wram_bank;
//$ff72-$ff75 ???
uint8 ff72;
uint8 ff73;
uint8 ff74;
uint8 ff75;
//$ffff IE
bool interrupt_enable_joypad;
bool interrupt_enable_serial;
@ -62,7 +88,7 @@ struct CPU : Processor, MMIO {
bool interrupt_enable_vblank;
} status;
uint8 wram[8192];
uint8 wram[32768]; //GB=8192, GBC=32768
uint8 hram[128];
static void Main();

View File

@ -1,5 +1,11 @@
#ifdef CPU_CPP
unsigned CPU::wram_addr(uint16 addr) const {
addr &= 0x1fff;
if(addr < 0x1000) return addr;
return (status.wram_bank * 0x1000) + (addr & 0x0fff);
}
void CPU::mmio_joyp_poll() {
unsigned button = 0, dpad = 0;
@ -21,8 +27,7 @@ void CPU::mmio_joyp_poll() {
}
uint8 CPU::mmio_read(uint16 addr) {
if(addr >= 0xc000 && addr <= 0xdfff) return wram[addr & 0x1fff];
if(addr >= 0xe000 && addr <= 0xfdff) return wram[addr & 0x1fff];
if(addr >= 0xc000 && addr <= 0xfdff) return wram[wram_addr(addr)];
if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f];
if(addr == 0xff00) { //JOYP
@ -65,6 +70,26 @@ uint8 CPU::mmio_read(uint16 addr) {
| (status.interrupt_request_vblank << 0);
}
if(addr == 0xff4d) { //KEY1
return (status.speed_double << 7);
}
if(addr == 0xff55) { //HDMA5
return (status.dma_length / 16) - 1;
}
if(addr == 0xff56) { //RP
return 0x02;
}
if(addr == 0xff6c) return 0xfe | status.ff6c;
if(addr == 0xff72) return status.ff72;
if(addr == 0xff73) return status.ff73;
if(addr == 0xff74) return status.ff74;
if(addr == 0xff75) return 0x8f | status.ff75;
if(addr == 0xff76) return 0x00;
if(addr == 0xff77) return 0x00;
if(addr == 0xffff) { //IE
return (status.interrupt_enable_joypad << 4)
| (status.interrupt_enable_serial << 3)
@ -77,8 +102,7 @@ uint8 CPU::mmio_read(uint16 addr) {
}
void CPU::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0xc000 && addr <= 0xdfff) { wram[addr & 0x1fff] = data; return; }
if(addr >= 0xe000 && addr <= 0xfdff) { wram[addr & 0x1fff] = data; return; }
if(addr >= 0xc000 && addr <= 0xfdff) { wram[wram_addr(addr)] = data; return; }
if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; }
if(addr == 0xff00) { //JOYP
@ -131,6 +155,85 @@ void CPU::mmio_write(uint16 addr, uint8 data) {
return;
}
if(addr == 0xff46) { //DMA
for(unsigned n = 0x00; n <= 0x9f; n++) {
bus.write(0xfe00 + n, bus.read((data << 8) + n));
add_clocks(8);
}
return;
}
if(addr == 0xff4d) { //KEY1
status.speed_switch = data & 0x01;
return;
}
if(addr == 0xff51) { //HDMA1
status.dma_source = (status.dma_source & 0x00ff) | (data << 8);
return;
}
if(addr == 0xff52) { //HDMA2
status.dma_source = (status.dma_source & 0xff00) | (data << 0);
return;
}
if(addr == 0xff53) { //HDMA3
status.dma_target = (status.dma_target & 0x00ff) | (data << 8);
return;
}
if(addr == 0xff54) { //HDMA4
status.dma_target = (status.dma_target & 0xff00) | (data << 0);
return;
}
if(addr == 0xff55) { //HDMA5
status.dma_mode = data & 0x80;
status.dma_length = ((data & 0x7f) + 1) * 16;
if(status.dma_mode == 0) do {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(8);
} while(--status.dma_length);
return;
}
if(addr == 0xff56) { //RP
return;
}
if(addr == 0xff6c) { //???
status.ff6c = data & 0x01;
return;
}
if(addr == 0xff72) { //???
status.ff72 = data;
return;
}
if(addr == 0xff73) { //???
status.ff73 = data;
return;
}
if(addr == 0xff74) { //???
status.ff74 = data;
return;
}
if(addr == 0xff75) { //???
status.ff75 = data & 0x70;
return;
}
if(addr == 0xff70) { //SVBK
status.wram_bank = data & 0x07;
if(status.wram_bank == 0) status.wram_bank = 1;
return;
}
if(addr == 0xffff) { //IE
status.interrupt_enable_joypad = data & 0x10;
status.interrupt_enable_serial = data & 0x08;

View File

@ -1,3 +1,4 @@
unsigned wram_addr(uint16 addr) const;
void mmio_joyp_poll();
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -49,6 +49,23 @@ void CPU::serialize(serializer &s) {
s.integer(status.interrupt_request_stat);
s.integer(status.interrupt_request_vblank);
s.integer(status.speed_double);
s.integer(status.speed_switch);
s.integer(status.dma_source);
s.integer(status.dma_target);
s.integer(status.dma_mode);
s.integer(status.dma_length);
s.integer(status.ff6c);
s.integer(status.wram_bank);
s.integer(status.ff72);
s.integer(status.ff73);
s.integer(status.ff74);
s.integer(status.ff75);
s.integer(status.interrupt_enable_joypad);
s.integer(status.interrupt_enable_serial);
s.integer(status.interrupt_enable_timer);

View File

@ -2,20 +2,20 @@
void CPU::op_io() {
cycle_edge();
add_clocks(4);
add_clocks(8 >> status.speed_double);
}
uint8 CPU::op_read(uint16 addr) {
cycle_edge();
uint8 r = bus.read(addr);
add_clocks(4);
add_clocks(8 >> status.speed_double);
return r;
}
void CPU::op_write(uint16 addr, uint8 data) {
cycle_edge();
bus.write(addr, data);
add_clocks(4);
add_clocks(8 >> status.speed_double);
}
void CPU::cycle_edge() {

View File

@ -1,5 +1,3 @@
//4194304hz (4 * 1024 * 1024)
//70224 clocks/frame
// 456 clocks/scanline
// 154 scanlines/frame
@ -13,17 +11,17 @@ void CPU::add_clocks(unsigned clocks) {
scheduler.exit(Scheduler::ExitReason::StepEvent);
status.clock += clocks;
if(status.clock >= 4194304) {
status.clock -= 4194304;
if(status.clock >= 8 * 1024 * 1024) {
status.clock -= 8 * 1024 * 1024;
cartridge.mbc3.second();
}
//4194304 / N(hz) - 1 = mask
if((status.clock & 15) == 0) timer_262144hz();
if((status.clock & 63) == 0) timer_65536hz();
if((status.clock & 255) == 0) timer_16384hz();
if((status.clock & 511) == 0) timer_8192hz();
if((status.clock & 1023) == 0) timer_4096hz();
//8MHz / N(hz) - 1 = mask
if((status.clock & 31) == 0) timer_262144hz();
if((status.clock & 127) == 0) timer_65536hz();
if((status.clock & 511) == 0) timer_16384hz();
if((status.clock & 1023) == 0) timer_8192hz();
if((status.clock & 2047) == 0) timer_4096hz();
lcd.clock -= clocks;
if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread);
@ -79,4 +77,14 @@ void CPU::timer_4096hz() {
}
}
void CPU::hblank() {
if(status.dma_mode == 1 && status.dma_length) {
for(unsigned n = 0; n < 16; n++) {
bus.write(status.dma_target++, bus.read(status.dma_source++));
add_clocks(8);
}
status.dma_length -= 16;
}
}
#endif

View File

@ -4,6 +4,7 @@ void timer_65536hz();
void timer_16384hz();
void timer_8192hz();
void timer_4096hz();
void hblank();
//opcode.cpp
void op_io();

View File

@ -4,12 +4,12 @@
namespace GameBoy {
namespace Info {
static const char Name[] = "bgameboy";
static const unsigned SerializerVersion = 2;
static const unsigned SerializerVersion = 3;
}
}
/*
bgameboy - Game Boy emulator
bgameboy - Game Boy, Super Game Boy, and Game Boy Color emulator
author: byuu
license: GPLv3
project started: 2010-12-27

View File

@ -10,7 +10,7 @@ void Interface::lcdScanline() {
void Interface::joypWrite(bool p15, bool p14) {
}
void Interface::videoRefresh(const uint8_t *data) {
void Interface::videoRefresh(const uint16_t *data) {
}
void Interface::audioSample(int16_t center, int16_t left, int16_t right) {
@ -29,8 +29,8 @@ bool Interface::cartridgeLoaded() {
return cartridge.loaded();
}
void Interface::loadCartridge(const string &markup, const uint8_t *data, unsigned size) {
cartridge.load(markup, data, size);
void Interface::loadCartridge(GameBoy::System::Revision revision, const string &markup, const uint8_t *data, unsigned size) {
cartridge.load(revision, markup, data, size);
system.power();
}

View File

@ -3,14 +3,14 @@ public:
virtual void lcdScanline();
virtual void joypWrite(bool p15, bool p14);
virtual void videoRefresh(const uint8_t *data);
virtual void videoRefresh(const uint16_t *data);
virtual void audioSample(int16_t center, int16_t left, int16_t right);
virtual bool inputPoll(unsigned id);
virtual void initialize(Interface*);
virtual bool cartridgeLoaded();
virtual void loadCartridge(const string &markup, const uint8_t *data, unsigned size);
virtual void loadCartridge(GameBoy::System::Revision revision, const string &markup, const uint8_t *data, unsigned size);
virtual void unloadCartridge();
enum class Memory : unsigned {

185
bsnes/gameboy/lcd/cgb.cpp Executable file
View File

@ -0,0 +1,185 @@
#ifdef LCD_CPP
void LCD::cgb_render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x0000;
origin[n] = Origin::None;
}
if(status.display_enable == true) {
cgb_render_bg();
if(status.window_display_enable == true) cgb_render_window();
if(status.obj_enable == true) cgb_render_ob();
}
uint16_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
//Attributes:
//0x80: 0 = OAM priority, 1 = BG priority
//0x40: vertical flip
//0x20: horizontal flip
//0x08: VRAM bank#
//0x07: palette#
void LCD::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data) {
unsigned tmaddr = 0x1800 + (select << 10);
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
tile = vram[0x0000 + tmaddr];
attr = vram[0x2000 + tmaddr];
unsigned tdaddr = attr & 0x08 ? 0x2000 : 0x0000;
if(status.bg_tiledata_select == 0) {
tdaddr += 0x1000 + ((int8)tile << 4);
} else {
tdaddr += 0x0000 + (tile << 4);
}
y &= 7;
if(attr & 0x40) y ^= 7;
tdaddr += y << 1;
data = vram[tdaddr++] << 0;
data |= vram[tdaddr++] << 8;
if(attr & 0x20) data = hflip(data);
}
void LCD::cgb_render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned tile, attr, data;
cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
for(unsigned ox = 0; ox < 160; ox++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= bgpd[palette_index++] << 0;
palette |= bgpd[palette_index++] << 8;
palette &= 0x7fff;
line[ox] = palette;
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data);
}
}
void LCD::cgb_render_window() {
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned tile, attr, data;
cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
for(unsigned ox = 0; ox < 160; ox++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= bgpd[palette_index++] << 0;
palette |= bgpd[palette_index++] << 8;
palette &= 0x7fff;
if(ox - (status.wx - 7) < 160u) {
line[ox] = palette;
origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG);
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data);
}
}
//Attributes:
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
//0x40: vertical flip
//0x20: horizontal flip
//0x08: VRAM bank#
//0x07: palette#
void LCD::cgb_render_ob() {
const unsigned Height = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= Height) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2];
unsigned attr = oam[n + 3];
sy = status.ly - sy;
if(sy >= Height) continue;
if(attr & 0x40) sy ^= (Height - 1);
unsigned tdaddr = (attr & 0x08 ? 0x2000 : 0x0000) + (tile << 4) + (sy << 1), data = 0;
data |= vram[tdaddr++] << 0;
data |= vram[tdaddr++] << 8;
if(attr & 0x20) data = hflip(data);
for(unsigned tx = 0; tx < 8; tx++) {
unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(index == 0) continue;
unsigned palette_index = ((attr & 0x07) << 3) + (index << 1);
unsigned palette = 0;
palette |= obpd[palette_index++] << 0;
palette |= obpd[palette_index++] << 8;
palette &= 0x7fff;
unsigned ox = sx + tx;
if(ox < 160) {
//When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window
if(status.bg_enable) {
if(origin[ox] == Origin::BGP) continue;
if(attr & 0x80) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) {
if(line[ox] > 0) continue;
}
}
}
line[ox] = palette;
origin[ox] = Origin::OB;
}
}
}
}
#endif

145
bsnes/gameboy/lcd/dmg.cpp Executable file
View File

@ -0,0 +1,145 @@
#ifdef LCD_CPP
void LCD::dmg_render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
}
if(status.display_enable == true) {
if(status.bg_enable == true) dmg_render_bg();
if(status.window_display_enable == true) dmg_render_window();
if(status.obj_enable == true) dmg_render_ob();
}
uint16_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
uint16 LCD::dmg_read_tile(bool select, unsigned x, unsigned y) {
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
} else {
tdaddr = 0x0000 + (vram[tmaddr] << 4);
}
tdaddr += (y & 7) << 1;
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
}
void LCD::dmg_render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = bgp[palette];
origin[ox] = Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = dmg_read_tile(status.bg_tilemap_select, ix, iy);
}
}
void LCD::dmg_render_window() {
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned data = dmg_read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160u) {
line[ox] = bgp[palette];
origin[ox] = Origin::BG;
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = dmg_read_tile(status.window_tilemap_select, ix, iy);
}
}
//Attributes:
//0x80: 0 = OBJ above BG, 1 = BG above OBJ
//0x40: vertical flip
//0x20: horizontal flip
//0x10: palette#
void LCD::dmg_render_ob() {
const unsigned Height = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= Height) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2];
unsigned attr = oam[n + 3];
sy = status.ly - sy;
if(sy >= Height) continue;
if(attr & 0x40) sy ^= (Height - 1);
unsigned tdaddr = (tile << 4) + (sy << 1), data = 0;
data |= vram[tdaddr++] << 0;
data |= vram[tdaddr++] << 8;
if(attr & 0x20) data = hflip(data);
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = obp[(bool)(attr & 0x10)][palette];
unsigned ox = sx + tx;
if(ox < 160) {
if(attr & 0x80) {
if(origin[ox] == Origin::BG) {
if(line[ox] > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OB;
}
}
}
}
#endif

View File

@ -1,14 +1,20 @@
#include <gameboy/gameboy.hpp>
//LY = 0-153
//Raster = 0-143
//Vblank = 144-153
//LX = 0-455
#define LCD_CPP
namespace GameBoy {
#include "dmg.cpp"
#include "cgb.cpp"
#include "mmio/mmio.cpp"
#include "serialization.cpp"
LCD lcd;
static unsigned linectr;
void LCD::Main() {
lcd.main();
}
@ -19,7 +25,7 @@ void LCD::main() {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
add_clocks(4);
add_clocks(8);
status.lx += 4;
if(status.lx >= 456) scanline();
@ -29,6 +35,7 @@ void LCD::main() {
if(status.display_enable && status.lx == 252) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
}
}
@ -48,7 +55,9 @@ void LCD::scanline() {
if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly < 144) render();
if(status.ly < 144) {
system.cgb() == false ? dmg_render() : cgb_render();
}
if(status.display_enable && status.ly == 144) {
cpu.interrupt_raise(CPU::Interrupt::Vblank);
@ -65,157 +74,50 @@ void LCD::frame() {
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
void LCD::render() {
for(unsigned n = 0; n < 160; n++) {
line[n] = 0x00;
origin[n] = Origin::None;
}
if(status.display_enable == true) {
if(status.bg_enable == true) render_bg();
if(status.window_display_enable == true) render_window();
if(status.obj_enable == true) render_obj();
}
uint8_t *output = screen + status.ly * 160;
for(unsigned n = 0; n < 160; n++) output[n] = line[n];
interface->lcdScanline();
}
uint16 LCD::read_tile(bool select, unsigned x, unsigned y) {
unsigned tmaddr = 0x1800 + (select << 10), tdaddr;
tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff;
if(status.bg_tiledata_select == 0) {
tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4);
} else {
tdaddr = 0x0000 + (vram[tmaddr] << 4);
}
tdaddr += (y & 7) << 1;
return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8);
}
void LCD::render_bg() {
unsigned iy = (status.ly + status.scy) & 255;
unsigned ix = status.scx, tx = ix & 7;
unsigned data = read_tile(status.bg_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
line[ox] = status.bgp[palette];
origin[ox] = Origin::BG;
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.bg_tilemap_select, ix, iy);
}
}
void LCD::render_window() {
if(status.ly - status.wy >= 144u) return;
if(status.wx >= 167u) return;
unsigned iy = status.wyc++;
unsigned ix = (7 - status.wx) & 255, tx = ix & 7;
unsigned data = read_tile(status.window_tilemap_select, ix, iy);
for(unsigned ox = 0; ox < 160; ox++) {
uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0)
| ((data & (0x8000 >> tx)) ? 2 : 0);
if(ox - (status.wx - 7) < 160U) {
line[ox] = status.bgp[palette];
origin[ox] = Origin::Window;
}
ix = (ix + 1) & 255;
tx = (tx + 1) & 7;
if(tx == 0) data = read_tile(status.window_tilemap_select, ix, iy);
}
}
void LCD::render_obj() {
enum : unsigned { Priority = 0x80, YFlip = 0x40, XFlip = 0x20, Palette = 0x10 };
unsigned obj_size = (status.obj_size == 0 ? 8 : 16);
unsigned sprite[10], sprites = 0;
//find first ten sprites on this scanline
for(unsigned s = 0; s < 40; s++) {
unsigned sy = oam[(s << 2) + 0] - 16;
unsigned sx = oam[(s << 2) + 1] - 8;
sy = status.ly - sy;
if(sy >= obj_size) continue;
sprite[sprites++] = s;
if(sprites == 10) break;
}
//sort by X-coordinate, when equal, lower address comes first
for(unsigned x = 0; x < sprites; x++) {
for(unsigned y = x + 1; y < sprites; y++) {
signed sx = oam[(sprite[x] << 2) + 1] - 8;
signed sy = oam[(sprite[y] << 2) + 1] - 8;
if(sy < sx) {
sprite[x] ^= sprite[y];
sprite[y] ^= sprite[x];
sprite[x] ^= sprite[y];
}
}
}
//render backwards, so that first sprite has highest priority
for(signed s = sprites - 1; s >= 0; s--) {
unsigned n = sprite[s] << 2;
unsigned sy = oam[n + 0] - 16;
unsigned sx = oam[n + 1] - 8;
unsigned tile = oam[n + 2];
unsigned attribute = oam[n + 3];
sy = status.ly - sy;
if(sy >= obj_size) continue;
if(attribute & YFlip) sy ^= (obj_size - 1);
unsigned tdaddr = (tile << 4) + (sy << 1);
uint8 d0 = vram[tdaddr + 0];
uint8 d1 = vram[tdaddr + 1];
unsigned xflip = attribute & XFlip ? 7 : 0;
for(unsigned tx = 0; tx < 8; tx++) {
uint8 palette = ((d0 & (0x80 >> tx)) ? 1 : 0)
| ((d1 & (0x80 >> tx)) ? 2 : 0);
if(palette == 0) continue;
palette = status.obp[(bool)(attribute & Palette)][palette];
unsigned ox = sx + (tx ^ xflip);
if(ox <= 159) {
if(attribute & Priority) {
if(origin[ox] == Origin::BG || origin[ox] == Origin::Window) {
if(line[ox] > 0) continue;
}
}
line[ox] = palette;
origin[ox] = Origin::OBJ;
}
}
}
unsigned LCD::hflip(unsigned data) const {
return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5)
| ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1)
| ((data & 0x0808) << 1) | ((data & 0x0404) << 3)
| ((data & 0x0202) << 5) | ((data & 0x0101) << 7);
}
void LCD::power() {
create(Main, 4194304);
create(Main, 8 * 1024 * 1024);
for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM
for(unsigned n = 0xff40; n <= 0xff4b; n++) bus.mmio[n] = this; //MMIO
for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM
for(unsigned n = 0; n < 8192; n++) vram[n] = 0x00;
for(unsigned n = 0; n < 160; n++) oam [n] = 0x00;
bus.mmio[0xff40] = this; //LCDC
bus.mmio[0xff41] = this; //STAT
bus.mmio[0xff42] = this; //SCY
bus.mmio[0xff43] = this; //SCX
bus.mmio[0xff44] = this; //LY
bus.mmio[0xff45] = this; //LYC
bus.mmio[0xff47] = this; //BGP
bus.mmio[0xff48] = this; //OBP0
bus.mmio[0xff49] = this; //OBP1
bus.mmio[0xff4a] = this; //WY
bus.mmio[0xff4b] = this; //WX
for(unsigned n = 0; n < 160 * 144; n++) screen[n] = 0x00;
if(system.cgb()) {
bus.mmio[0xff4f] = this; //VBK
bus.mmio[0xff68] = this; //BGPI
bus.mmio[0xff69] = this; //BGPD
bus.mmio[0xff6a] = this; //OBPI
bus.mmio[0xff6b] = this; //OBPD
}
for(auto &n : screen) n = 0x0000;
for(auto &n : line) n = 0x0000;
for(auto &n : origin) n = Origin::None;
for(auto &n : vram) n = 0x00;
for(auto &n : oam) n = 0x00;
for(auto &n : bgp) n = 0x00;
for(auto &n : obp[0]) n = 0x00;
for(auto &n : obp[1]) n = 0x00;
for(auto &n : bgpd) n = 0x0000;
for(auto &n : obpd) n = 0x0000;
status.lx = 0;
status.wyc = 0;
@ -238,15 +140,16 @@ void LCD::power() {
status.scx = 0;
status.ly = 0;
status.lyc = 0;
for(unsigned n = 0; n < 4; n++) {
status.bgp[n] = n;
status.obp[0][n] = n;
status.obp[1][n] = n;
}
status.wy = 0;
status.wx = 0;
status.vram_bank = 0;
status.bgpi_increment = 0;
status.bgpi = 0;
status.obpi_increment = 0;
status.obpi = 0;
}
LCD::LCD() {

View File

@ -33,38 +33,57 @@ struct LCD : Processor, MMIO {
//$ff45 LYC
uint8 lyc;
//$ff47 BGP
uint8 bgp[4];
//$ff48 OBP0
//$ff49 OBP1
uint8 obp[2][4];
//$ff4a WY
uint8 wy;
//$ff4b WX
uint8 wx;
//$ff4f VBK
bool vram_bank;
//$ff68 BGPI
bool bgpi_increment;
uint6 bgpi;
//$ff6a OBPI
bool obpi_increment;
uint8 obpi;
} status;
uint8 screen[160 * 144];
uint8 vram[8192];
uint8 oam[160];
uint8 line[160];
struct Origin { enum : unsigned { None, BG, Window, OBJ }; };
uint16 screen[160 * 144];
uint16 line[160];
struct Origin { enum : unsigned { None, BG, BGP, OB }; };
uint8 origin[160];
uint8 vram[16384]; //GB = 8192, GBC = 16384
uint8 oam[160];
uint8 bgp[4];
uint8 obp[2][4];
uint8 bgpd[64];
uint8 obpd[64];
static void Main();
void main();
void add_clocks(unsigned clocks);
void scanline();
void frame();
void render();
uint16 read_tile(bool select, unsigned x, unsigned y);
void render_bg();
void render_window();
void render_obj();
unsigned hflip(unsigned data) const;
//dmg.cpp
void dmg_render();
uint16 dmg_read_tile(bool select, unsigned x, unsigned y);
void dmg_render_bg();
void dmg_render_window();
void dmg_render_ob();
//cgb.cpp
void cgb_render();
void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data);
void cgb_render_bg();
void cgb_render_window();
void cgb_render_ob();
void power();

View File

@ -1,7 +1,11 @@
#ifdef LCD_CPP
unsigned LCD::vram_addr(uint16 addr) const {
return (status.vram_bank * 0x2000) + (addr & 0x1fff);
}
uint8 LCD::mmio_read(uint16 addr) {
if(addr >= 0x8000 && addr <= 0x9fff) return vram[addr & 0x1fff];
if(addr >= 0x8000 && addr <= 0x9fff) return vram[vram_addr(addr)];
if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff];
if(addr == 0xff40) { //LCDC
@ -47,24 +51,24 @@ uint8 LCD::mmio_read(uint16 addr) {
}
if(addr == 0xff47) { //BGP
return (status.bgp[3] << 6)
| (status.bgp[2] << 4)
| (status.bgp[1] << 2)
| (status.bgp[0] << 0);
return (bgp[3] << 6)
| (bgp[2] << 4)
| (bgp[1] << 2)
| (bgp[0] << 0);
}
if(addr == 0xff48) { //OBP0
return (status.obp[0][3] << 6)
| (status.obp[0][2] << 4)
| (status.obp[0][1] << 2)
| (status.obp[0][0] << 0);
return (obp[0][3] << 6)
| (obp[0][2] << 4)
| (obp[0][1] << 2)
| (obp[0][0] << 0);
}
if(addr == 0xff49) { //OBP1
return (status.obp[1][3] << 6)
| (status.obp[1][2] << 4)
| (status.obp[1][1] << 2)
| (status.obp[1][0] << 0);
return (obp[1][3] << 6)
| (obp[1][2] << 4)
| (obp[1][1] << 2)
| (obp[1][0] << 0);
}
if(addr == 0xff4a) { //WY
@ -75,11 +79,19 @@ uint8 LCD::mmio_read(uint16 addr) {
return status.wx;
}
if(addr == 0xff69) { //BGPD
return bgpd[status.bgpi];
}
if(addr == 0xff6b) { //OBPD
return obpd[status.obpi];
}
return 0x00;
}
void LCD::mmio_write(uint16 addr, uint8 data) {
if(addr >= 0x8000 && addr <= 0x9fff) { vram[addr & 0x1fff] = data; return; }
if(addr >= 0x8000 && addr <= 0x9fff) { vram[vram_addr(addr)] = data; return; }
if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; }
if(addr == 0xff40) { //LCDC
@ -126,32 +138,27 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
return;
}
if(addr == 0xff46) { //DMA
for(unsigned n = 0x00; n <= 0x9f; n++) bus.write(0xfe00 + n, bus.read((data << 8) + n));
return;
}
if(addr == 0xff47) { //BGP
status.bgp[3] = (data >> 6) & 3;
status.bgp[2] = (data >> 4) & 3;
status.bgp[1] = (data >> 2) & 3;
status.bgp[0] = (data >> 0) & 3;
bgp[3] = (data >> 6) & 3;
bgp[2] = (data >> 4) & 3;
bgp[1] = (data >> 2) & 3;
bgp[0] = (data >> 0) & 3;
return;
}
if(addr == 0xff48) { //OBP0
status.obp[0][3] = (data >> 6) & 3;
status.obp[0][2] = (data >> 4) & 3;
status.obp[0][1] = (data >> 2) & 3;
status.obp[0][0] = (data >> 0) & 3;
obp[0][3] = (data >> 6) & 3;
obp[0][2] = (data >> 4) & 3;
obp[0][1] = (data >> 2) & 3;
obp[0][0] = (data >> 0) & 3;
return;
}
if(addr == 0xff49) { //OBP1
status.obp[1][3] = (data >> 6) & 3;
status.obp[1][2] = (data >> 4) & 3;
status.obp[1][1] = (data >> 2) & 3;
status.obp[1][0] = (data >> 0) & 3;
obp[1][3] = (data >> 6) & 3;
obp[1][2] = (data >> 4) & 3;
obp[1][1] = (data >> 2) & 3;
obp[1][0] = (data >> 0) & 3;
return;
}
@ -164,6 +171,33 @@ void LCD::mmio_write(uint16 addr, uint8 data) {
status.wx = data;
return;
}
if(addr == 0xff4f) { //VBK
status.vram_bank = data & 1;
return;
}
if(addr == 0xff68) { //BGPI
status.bgpi_increment = data & 0x80;
status.bgpi = data & 0x3f;
return;
}
if(addr == 0xff69) { //BGPD
bgpd[status.bgpi] = data;
if(status.bgpi_increment) status.bgpi++;
return;
}
if(addr == 0xff6a) { //OBPI
status.obpi_increment = data & 0x80;
status.obpi = data & 0x3f;
}
if(addr == 0xff6b) { //OBPD
obpd[status.obpi] = data;
if(status.obpi_increment) status.obpi++;
}
}
#endif

View File

@ -1,2 +1,3 @@
unsigned vram_addr(uint16 addr) const;
uint8 mmio_read(uint16 addr);
void mmio_write(uint16 addr, uint8 data);

View File

@ -3,6 +3,18 @@
void LCD::serialize(serializer &s) {
Processor::serialize(s);
s.array(screen);
s.array(line);
s.array(origin);
s.array(vram);
s.array(oam);
s.array(bgp);
s.array(obp[0]);
s.array(obp[1]);
s.array(bgpd);
s.array(obpd);
s.integer(status.lx);
s.integer(status.wyc);
@ -22,21 +34,20 @@ void LCD::serialize(serializer &s) {
s.integer(status.scy);
s.integer(status.scx);
s.integer(status.ly);
s.integer(status.lyc);
s.array(status.bgp);
s.array(status.obp[0]);
s.array(status.obp[1]);
s.integer(status.wy);
s.integer(status.wx);
s.array(screen);
s.array(vram);
s.array(oam);
s.array(line);
s.array(origin);
s.integer(status.vram_bank);
s.integer(status.bgpi_increment);
s.integer(status.bgpi);
s.integer(status.obpi_increment);
s.integer(status.obpi);
}
#endif

View File

@ -0,0 +1,135 @@
#ifdef SYSTEM_CPP
//SHA256 = 4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9
const uint8_t System::BootROM::cgb[2048] = {
0x31,0xfe,0xff,0x3e,0x02,0xc3,0x7c,0x00,0xd3,0x00,0x98,0xa0,0x12,0xd3,0x00,0x80,
0x00,0x40,0x1e,0x53,0xd0,0x00,0x1f,0x42,0x1c,0x00,0x14,0x2a,0x4d,0x19,0x8c,0x7e,
0x00,0x7c,0x31,0x6e,0x4a,0x45,0x52,0x4a,0x00,0x00,0xff,0x53,0x1f,0x7c,0xff,0x03,
0x1f,0x00,0xff,0x1f,0xa7,0x00,0xef,0x1b,0x1f,0x00,0xef,0x1b,0x00,0x7c,0x00,0x00,
0xff,0x03,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c,
0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,
0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9,
0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x58,0x43,0xe0,0x70,0x3e,0xfc,
0xe0,0x47,0xcd,0x75,0x02,0xcd,0x00,0x02,0x26,0xd0,0xcd,0x03,0x02,0x21,0x00,0xfe,
0x0e,0xa0,0xaf,0x22,0x0d,0x20,0xfc,0x11,0x04,0x01,0x21,0x10,0x80,0x4c,0x1a,0xe2,
0x0c,0xcd,0xc6,0x03,0xcd,0xc7,0x03,0x13,0x7b,0xfe,0x34,0x20,0xf1,0x11,0x72,0x00,
0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0xcd,0xf0,0x03,0x3e,0x01,0xe0,0x4f,
0x3e,0x91,0xe0,0x40,0x21,0xb2,0x98,0x06,0x4e,0x0e,0x44,0xcd,0x91,0x02,0xaf,0xe0,
0x4f,0x0e,0x80,0x21,0x42,0x00,0x06,0x18,0xf2,0x0c,0xbe,0x20,0xfe,0x23,0x05,0x20,
0xf7,0x21,0x34,0x01,0x06,0x19,0x78,0x86,0x2c,0x05,0x20,0xfb,0x86,0x20,0xfe,0xcd,
0x1c,0x03,0x18,0x02,0x00,0x00,0xcd,0xd0,0x05,0xaf,0xe0,0x70,0x3e,0x11,0xe0,0x50,
0x21,0x00,0x80,0xaf,0x22,0xcb,0x6c,0x28,0xfb,0xc9,0x2a,0x12,0x13,0x0d,0x20,0xfa,
0xc9,0xe5,0x21,0x0f,0xff,0xcb,0x86,0xcb,0x46,0x28,0xfc,0xe1,0xc9,0x11,0x00,0xff,
0x21,0x03,0xd0,0x0e,0x0f,0x3e,0x30,0x12,0x3e,0x20,0x12,0x1a,0x2f,0xa1,0xcb,0x37,
0x47,0x3e,0x10,0x12,0x1a,0x2f,0xa1,0xb0,0x4f,0x7e,0xa9,0xe6,0xf0,0x47,0x2a,0xa9,
0xa1,0xb0,0x32,0x47,0x79,0x77,0x3e,0x30,0x12,0xc9,0x3e,0x80,0xe0,0x68,0xe0,0x6a,
0x0e,0x6b,0x2a,0xe2,0x05,0x20,0xfb,0x4a,0x09,0x43,0x0e,0x69,0x2a,0xe2,0x05,0x20,
0xfb,0xc9,0xc5,0xd5,0xe5,0x21,0x00,0xd8,0x06,0x01,0x16,0x3f,0x1e,0x40,0xcd,0x4a,
0x02,0xe1,0xd1,0xc1,0xc9,0x3e,0x80,0xe0,0x26,0xe0,0x11,0x3e,0xf3,0xe0,0x12,0xe0,
0x25,0x3e,0x77,0xe0,0x24,0x21,0x30,0xff,0xaf,0x0e,0x10,0x22,0x2f,0x0d,0x20,0xfb,
0xc9,0xcd,0x11,0x02,0xcd,0x62,0x02,0x79,0xfe,0x38,0x20,0x14,0xe5,0xaf,0xe0,0x4f,
0x21,0xa7,0x99,0x3e,0x38,0x22,0x3c,0xfe,0x3f,0x20,0xfa,0x3e,0x01,0xe0,0x4f,0xe1,
0xc5,0xe5,0x21,0x43,0x01,0xcb,0x7e,0xcc,0x89,0x05,0xe1,0xc1,0xcd,0x11,0x02,0x79,
0xd6,0x30,0xd2,0x06,0x03,0x79,0xfe,0x01,0xca,0x06,0x03,0x7d,0xfe,0xd1,0x28,0x21,
0xc5,0x06,0x03,0x0e,0x01,0x16,0x03,0x7e,0xe6,0xf8,0xb1,0x22,0x15,0x20,0xf8,0x0c,
0x79,0xfe,0x06,0x20,0xf0,0x11,0x11,0x00,0x19,0x05,0x20,0xe7,0x11,0xa1,0xff,0x19,
0xc1,0x04,0x78,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x07,0x7b,
0xe0,0x13,0x3e,0x87,0xe0,0x14,0xfa,0x02,0xd0,0xfe,0x00,0x28,0x0a,0x3d,0xea,0x02,
0xd0,0x79,0xfe,0x01,0xca,0x91,0x02,0x0d,0xc2,0x91,0x02,0xc9,0x0e,0x26,0xcd,0x4a,
0x03,0xcd,0x11,0x02,0xcd,0x62,0x02,0x0d,0x20,0xf4,0xcd,0x11,0x02,0x3e,0x01,0xe0,
0x4f,0xcd,0x3e,0x03,0xcd,0x41,0x03,0xaf,0xe0,0x4f,0xcd,0x3e,0x03,0xc9,0x21,0x08,
0x00,0x11,0x51,0xff,0x0e,0x05,0xcd,0x0a,0x02,0xc9,0xc5,0xd5,0xe5,0x21,0x40,0xd8,
0x0e,0x20,0x7e,0xe6,0x1f,0xfe,0x1f,0x28,0x01,0x3c,0x57,0x2a,0x07,0x07,0x07,0xe6,
0x07,0x47,0x3a,0x07,0x07,0x07,0xe6,0x18,0xb0,0xfe,0x1f,0x28,0x01,0x3c,0x0f,0x0f,
0x0f,0x47,0xe6,0xe0,0xb2,0x22,0x78,0xe6,0x03,0x5f,0x7e,0x0f,0x0f,0xe6,0x1f,0xfe,
0x1f,0x28,0x01,0x3c,0x07,0x07,0xb3,0x22,0x0d,0x20,0xc7,0xe1,0xd1,0xc1,0xc9,0x0e,
0x00,0x1a,0xe6,0xf0,0xcb,0x49,0x28,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x1a,
0xe6,0x0f,0xcb,0x49,0x20,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x13,0xcb,0x41,
0x28,0x0d,0xd5,0x11,0xf8,0xff,0xcb,0x49,0x28,0x03,0x11,0x08,0x00,0x19,0xd1,0x0c,
0x79,0xfe,0x18,0x20,0xcc,0xc9,0x47,0xd5,0x16,0x04,0x58,0xcb,0x10,0x17,0xcb,0x13,
0x17,0x15,0x20,0xf6,0xd1,0x22,0x23,0x22,0x23,0xc9,0x3e,0x19,0xea,0x10,0x99,0x21,
0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,0xc9,
0x3e,0x01,0xe0,0x4f,0xcd,0x00,0x02,0x11,0x07,0x06,0x21,0x80,0x80,0x0e,0xc0,0x1a,
0x22,0x23,0x22,0x23,0x13,0x0d,0x20,0xf7,0x11,0x04,0x01,0xcd,0x8f,0x03,0x01,0xa8,
0xff,0x09,0xcd,0x8f,0x03,0x01,0xf8,0xff,0x09,0x11,0x72,0x00,0x0e,0x08,0x23,0x1a,
0x22,0x13,0x0d,0x20,0xf9,0x21,0xc2,0x98,0x06,0x08,0x3e,0x08,0x0e,0x10,0x22,0x0d,
0x20,0xfc,0x11,0x10,0x00,0x19,0x05,0x20,0xf3,0xaf,0xe0,0x4f,0x21,0xc2,0x98,0x3e,
0x08,0x22,0x3c,0xfe,0x18,0x20,0x02,0x2e,0xe2,0xfe,0x28,0x20,0x03,0x21,0x02,0x99,
0xfe,0x38,0x20,0xed,0x21,0xd8,0x08,0x11,0x40,0xd8,0x06,0x08,0x3e,0xff,0x12,0x13,
0x12,0x13,0x0e,0x02,0xcd,0x0a,0x02,0x3e,0x00,0x12,0x13,0x12,0x13,0x13,0x13,0x05,
0x20,0xea,0xcd,0x62,0x02,0x21,0x4b,0x01,0x7e,0xfe,0x33,0x20,0x0b,0x2e,0x44,0x1e,
0x30,0x2a,0xbb,0x20,0x49,0x1c,0x18,0x04,0x2e,0x4b,0x1e,0x01,0x2a,0xbb,0x20,0x3e,
0x2e,0x34,0x01,0x10,0x00,0x2a,0x80,0x47,0x0d,0x20,0xfa,0xea,0x00,0xd0,0x21,0xc7,
0x06,0x0e,0x00,0x2a,0xb8,0x28,0x08,0x0c,0x79,0xfe,0x4f,0x20,0xf6,0x18,0x1f,0x79,
0xd6,0x41,0x38,0x1c,0x21,0x16,0x07,0x16,0x00,0x5f,0x19,0xfa,0x37,0x01,0x57,0x7e,
0xba,0x28,0x0d,0x11,0x0e,0x00,0x19,0x79,0x83,0x4f,0xd6,0x5e,0x38,0xed,0x0e,0x00,
0x21,0x33,0x07,0x06,0x00,0x09,0x7e,0xe6,0x1f,0xea,0x08,0xd0,0x7e,0xe6,0xe0,0x07,
0x07,0x07,0xea,0x0b,0xd0,0xcd,0xe9,0x04,0xc9,0x11,0x91,0x07,0x21,0x00,0xd9,0xfa,
0x0b,0xd0,0x47,0x0e,0x1e,0xcb,0x40,0x20,0x02,0x13,0x13,0x1a,0x22,0x20,0x02,0x1b,
0x1b,0xcb,0x48,0x20,0x02,0x13,0x13,0x1a,0x22,0x13,0x13,0x20,0x02,0x1b,0x1b,0xcb,
0x50,0x28,0x05,0x1b,0x2b,0x1a,0x22,0x13,0x1a,0x22,0x13,0x0d,0x20,0xd7,0x21,0x00,
0xd9,0x11,0x00,0xda,0xcd,0x64,0x05,0xc9,0x21,0x12,0x00,0xfa,0x05,0xd0,0x07,0x07,
0x06,0x00,0x4f,0x09,0x11,0x40,0xd8,0x06,0x08,0xe5,0x0e,0x02,0xcd,0x0a,0x02,0x13,
0x13,0x13,0x13,0x13,0x13,0xe1,0x05,0x20,0xf0,0x11,0x42,0xd8,0x0e,0x02,0xcd,0x0a,
0x02,0x11,0x4a,0xd8,0x0e,0x02,0xcd,0x0a,0x02,0x2b,0x2b,0x11,0x44,0xd8,0x0e,0x02,
0xcd,0x0a,0x02,0xc9,0x0e,0x60,0x2a,0xe5,0xc5,0x21,0xe8,0x07,0x06,0x00,0x4f,0x09,
0x0e,0x08,0xcd,0x0a,0x02,0xc1,0xe1,0x0d,0x20,0xec,0xc9,0xfa,0x08,0xd0,0x11,0x18,
0x00,0x3c,0x3d,0x28,0x03,0x19,0x20,0xfa,0xc9,0xcd,0x1d,0x02,0x78,0xe6,0xff,0x28,
0x0f,0x21,0xe4,0x08,0x06,0x00,0x2a,0xb9,0x28,0x08,0x04,0x78,0xfe,0x0c,0x20,0xf6,
0x18,0x2d,0x78,0xea,0x05,0xd0,0x3e,0x1e,0xea,0x02,0xd0,0x11,0x0b,0x00,0x19,0x56,
0x7a,0xe6,0x1f,0x5f,0x21,0x08,0xd0,0x3a,0x22,0x7b,0x77,0x7a,0xe6,0xe0,0x07,0x07,
0x07,0x5f,0x21,0x0b,0xd0,0x3a,0x22,0x7b,0x77,0xcd,0xe9,0x04,0xcd,0x28,0x05,0xc9,
0xcd,0x11,0x02,0xfa,0x43,0x01,0xcb,0x7f,0x28,0x04,0xe0,0x4c,0x18,0x28,0x3e,0x04,
0xe0,0x4c,0x3e,0x01,0xe0,0x6c,0x21,0x00,0xda,0xcd,0x7b,0x05,0x06,0x10,0x16,0x00,
0x1e,0x08,0xcd,0x4a,0x02,0x21,0x7a,0x00,0xfa,0x00,0xd0,0x47,0x0e,0x02,0x2a,0xb8,
0xcc,0xda,0x03,0x0d,0x20,0xf8,0xc9,0x01,0x0f,0x3f,0x7e,0xff,0xff,0xc0,0x00,0xc0,
0xf0,0xf1,0x03,0x7c,0xfc,0xfe,0xfe,0x03,0x07,0x07,0x0f,0xe0,0xe0,0xf0,0xf0,0x1e,
0x3e,0x7e,0xfe,0x0f,0x0f,0x1f,0x1f,0xff,0xff,0x00,0x00,0x01,0x01,0x01,0x03,0xff,
0xff,0xe1,0xe0,0xc0,0xf0,0xf9,0xfb,0x1f,0x7f,0xf8,0xe0,0xf3,0xfd,0x3e,0x1e,0xe0,
0xf0,0xf9,0x7f,0x3e,0x7c,0xf8,0xe0,0xf8,0xf0,0xf0,0xf8,0x00,0x00,0x7f,0x7f,0x07,
0x0f,0x9f,0xbf,0x9e,0x1f,0xff,0xff,0x0f,0x1e,0x3e,0x3c,0xf1,0xfb,0x7f,0x7f,0xfe,
0xde,0xdf,0x9f,0x1f,0x3f,0x3e,0x3c,0xf8,0xf8,0x00,0x00,0x03,0x03,0x07,0x07,0xff,
0xff,0xc1,0xc0,0xf3,0xe7,0xf7,0xf3,0xc0,0xc0,0xc0,0xc0,0x1f,0x1f,0x1e,0x3e,0x3f,
0x1f,0x3e,0x3e,0x80,0x00,0x00,0x00,0x7c,0x1f,0x07,0x00,0x0f,0xff,0xfe,0x00,0x7c,
0xf8,0xf0,0x00,0x1f,0x0f,0x0f,0x00,0x7c,0xf8,0xf8,0x00,0x3f,0x3e,0x1c,0x00,0x0f,
0x0f,0x0f,0x00,0x7c,0xff,0xff,0x00,0x00,0xf8,0xf8,0x00,0x07,0x0f,0x0f,0x00,0x81,
0xff,0xff,0x00,0xf3,0xe1,0x80,0x00,0xe0,0xff,0x7f,0x00,0xfc,0xf0,0xc0,0x00,0x3e,
0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x16,0x36,0xd1,0xdb,0xf2,0x3c,0x8c,
0x92,0x3d,0x5c,0x58,0xc9,0x3e,0x70,0x1d,0x59,0x69,0x19,0x35,0xa8,0x14,0xaa,0x75,
0x95,0x99,0x34,0x6f,0x15,0xff,0x97,0x4b,0x90,0x17,0x10,0x39,0xf7,0xf6,0xa2,0x49,
0x4e,0x43,0x68,0xe0,0x8b,0xf0,0xce,0x0c,0x29,0xe8,0xb7,0x86,0x9a,0x52,0x01,0x9d,
0x71,0x9c,0xbd,0x5d,0x6d,0x67,0x3f,0x6b,0xb3,0x46,0x28,0xa5,0xc6,0xd3,0x27,0x61,
0x18,0x66,0x6a,0xbf,0x0d,0xf4,0x42,0x45,0x46,0x41,0x41,0x52,0x42,0x45,0x4b,0x45,
0x4b,0x20,0x52,0x2d,0x55,0x52,0x41,0x52,0x20,0x49,0x4e,0x41,0x49,0x4c,0x49,0x43,
0x45,0x20,0x52,0x7c,0x08,0x12,0xa3,0xa2,0x07,0x87,0x4b,0x20,0x12,0x65,0xa8,0x16,
0xa9,0x86,0xb1,0x68,0xa0,0x87,0x66,0x12,0xa1,0x30,0x3c,0x12,0x85,0x12,0x64,0x1b,
0x07,0x06,0x6f,0x6e,0x6e,0xae,0xaf,0x6f,0xb2,0xaf,0xb2,0xa8,0xab,0x6f,0xaf,0x86,
0xae,0xa2,0xa2,0x12,0xaf,0x13,0x12,0xa1,0x6e,0xaf,0xaf,0xad,0x06,0x4c,0x6e,0xaf,
0xaf,0x12,0x7c,0xac,0xa8,0x6a,0x6e,0x13,0xa0,0x2d,0xa8,0x2b,0xac,0x64,0xac,0x6d,
0x87,0xbc,0x60,0xb4,0x13,0x72,0x7c,0xb5,0xae,0xae,0x7c,0x7c,0x65,0xa2,0x6c,0x64,
0x85,0x80,0xb0,0x40,0x88,0x20,0x68,0xde,0x00,0x70,0xde,0x20,0x78,0x20,0x20,0x38,
0x20,0xb0,0x90,0x20,0xb0,0xa0,0xe0,0xb0,0xc0,0x98,0xb6,0x48,0x80,0xe0,0x50,0x1e,
0x1e,0x58,0x20,0xb8,0xe0,0x88,0xb0,0x10,0x20,0x00,0x10,0x20,0xe0,0x18,0xe0,0x18,
0x00,0x18,0xe0,0x20,0xa8,0xe0,0x20,0x18,0xe0,0x00,0x20,0x18,0xd8,0xc8,0x18,0xe0,
0x00,0xe0,0x40,0x28,0x28,0x28,0x18,0xe0,0x60,0x20,0x18,0xe0,0x00,0x00,0x08,0xe0,
0x18,0x30,0xd0,0xd0,0xd0,0x20,0xe0,0xe8,0xff,0x7f,0xbf,0x32,0xd0,0x00,0x00,0x00,
0x9f,0x63,0x79,0x42,0xb0,0x15,0xcb,0x04,0xff,0x7f,0x31,0x6e,0x4a,0x45,0x00,0x00,
0xff,0x7f,0xef,0x1b,0x00,0x02,0x00,0x00,0xff,0x7f,0x1f,0x42,0xf2,0x1c,0x00,0x00,
0xff,0x7f,0x94,0x52,0x4a,0x29,0x00,0x00,0xff,0x7f,0xff,0x03,0x2f,0x01,0x00,0x00,
0xff,0x7f,0xef,0x03,0xd6,0x01,0x00,0x00,0xff,0x7f,0xb5,0x42,0xc8,0x3d,0x00,0x00,
0x74,0x7e,0xff,0x03,0x80,0x01,0x00,0x00,0xff,0x67,0xac,0x77,0x13,0x1a,0x6b,0x2d,
0xd6,0x7e,0xff,0x4b,0x75,0x21,0x00,0x00,0xff,0x53,0x5f,0x4a,0x52,0x7e,0x00,0x00,
0xff,0x4f,0xd2,0x7e,0x4c,0x3a,0xe0,0x1c,0xed,0x03,0xff,0x7f,0x5f,0x25,0x00,0x00,
0x6a,0x03,0x1f,0x02,0xff,0x03,0xff,0x7f,0xff,0x7f,0xdf,0x01,0x12,0x01,0x00,0x00,
0x1f,0x23,0x5f,0x03,0xf2,0x00,0x09,0x00,0xff,0x7f,0xea,0x03,0x1f,0x01,0x00,0x00,
0x9f,0x29,0x1a,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x7f,0x02,0x1f,0x00,0x00,0x00,
0xff,0x7f,0xe0,0x03,0x06,0x02,0x20,0x01,0xff,0x7f,0xeb,0x7e,0x1f,0x00,0x00,0x7c,
0xff,0x7f,0xff,0x3f,0x00,0x7e,0x1f,0x00,0xff,0x7f,0xff,0x03,0x1f,0x00,0x00,0x00,
0xff,0x03,0x1f,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x3f,0x03,0x93,0x01,0x00,0x00,
0x00,0x00,0x00,0x42,0x7f,0x03,0xff,0x7f,0xff,0x7f,0x8c,0x7e,0x00,0x7c,0x00,0x00,
0xff,0x7f,0xef,0x1b,0x80,0x61,0x00,0x00,0xff,0x7f,0x00,0x7c,0xe0,0x03,0x1f,0x7c,
0x1f,0x00,0xff,0x03,0x40,0x41,0x42,0x20,0x21,0x22,0x80,0x81,0x82,0x10,0x11,0x12,
0x12,0xb0,0x79,0xb8,0xad,0x16,0x17,0x07,0xba,0x05,0x7c,0x13,0x00,0x00,0x00,0x00,
};
#endif

View File

@ -1,6 +1,6 @@
#ifdef SYSTEM_CPP
//MD5SUM = 32fbbd84168d3482956eb3c5051637f5
//SHA256 = cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7
const uint8_t System::BootROM::dmg[256] = {
0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e,
0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0,

View File

@ -1,6 +1,6 @@
#ifdef SYSTEM_CPP
//MD5SUM = d574d4f9c12f305074798f54c091a8b4
//SHA256 = 0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360
const uint8_t System::BootROM::sgb[256] = {
0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,
0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,

View File

@ -5,6 +5,7 @@ namespace GameBoy {
#include "bootrom-dmg.cpp"
#include "bootrom-sgb.cpp"
#include "bootrom-cgb.cpp"
#include "serialization.cpp"
System system;
@ -37,7 +38,8 @@ void System::init() {
assert(interface != 0);
}
void System::load() {
void System::load(Revision revision) {
this->revision = revision;
serialize_init();
}

View File

@ -4,10 +4,21 @@ enum class Input : unsigned {
Up, Down, Left, Right, B, A, Select, Start,
};
struct System {
struct System : property<System> {
enum class Revision : unsigned {
GameBoy,
SuperGameBoy,
GameBoyColor,
};
readonly<Revision> revision;
inline bool dmg() const { return revision == Revision::GameBoy; }
inline bool sgb() const { return revision == Revision::SuperGameBoy; }
inline bool cgb() const { return revision == Revision::GameBoyColor; }
struct BootROM {
static const uint8 dmg[256];
static const uint8 sgb[256];
static const uint8 dmg[ 256];
static const uint8 sgb[ 256];
static const uint8 cgb[2048];
} bootROM;
void run();
@ -15,7 +26,7 @@ struct System {
void runthreadtosave();
void init();
void load();
void load(Revision);
void power();
unsigned clocks_executed;

View File

@ -251,65 +251,47 @@ void prg_write(unsigned addr, uint8 data) {
}
unsigned chr_sprite_addr(unsigned addr) {
unsigned bank;
if(chr_mode == 0) {
bank = chr_sprite_bank[7];
auto bank = chr_sprite_bank[7];
return (bank * 0x2000) + (addr & 0x1fff);
}
if(chr_mode == 1) {
bank = (addr < 0x1000) ? chr_sprite_bank[3]
:/*addr < 0x2000 */ chr_sprite_bank[7];
auto bank = chr_sprite_bank[(addr / 0x1000) * 4 + 3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
bank = (addr < 0x0800) ? chr_sprite_bank[1]
: (addr < 0x1000) ? chr_sprite_bank[3]
: (addr < 0x1800) ? chr_sprite_bank[5]
:/*addr < 0x2000 */ chr_sprite_bank[7];
auto bank = chr_sprite_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
bank = (addr < 0x0400) ? chr_sprite_bank[0]
: (addr < 0x0800) ? chr_sprite_bank[1]
: (addr < 0x0c00) ? chr_sprite_bank[2]
: (addr < 0x1000) ? chr_sprite_bank[3]
: (addr < 0x1400) ? chr_sprite_bank[4]
: (addr < 0x1800) ? chr_sprite_bank[5]
: (addr < 0x1c00) ? chr_sprite_bank[6]
:/*addr < 0x2000 */ chr_sprite_bank[7];
auto bank = chr_sprite_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}
unsigned chr_bg_addr(unsigned addr) {
addr &= 0x0fff;
unsigned bank;
if(chr_mode == 0) {
bank = chr_bg_bank[3];
auto bank = chr_bg_bank[3];
return (bank * 0x2000) + (addr & 0x0fff);
}
if(chr_mode == 1) {
bank = chr_bg_bank[3];
auto bank = chr_bg_bank[3];
return (bank * 0x1000) + (addr & 0x0fff);
}
if(chr_mode == 2) {
bank = (addr < 0x0800) ? chr_bg_bank[1]
:/*addr < 0x1000 */ chr_bg_bank[3];
auto bank = chr_bg_bank[(addr / 0x0800) * 2 + 1];
return (bank * 0x0800) + (addr & 0x07ff);
}
if(chr_mode == 3) {
bank = (addr < 0x0400) ? chr_bg_bank[0]
: (addr < 0x0800) ? chr_bg_bank[1]
: (addr < 0x0c00) ? chr_bg_bank[2]
:/*addr < 0x1000 */ chr_bg_bank[3];
auto bank = chr_bg_bank[(addr / 0x0400)];
return (bank * 0x0400) + (addr & 0x03ff);
}
}

View File

@ -24,12 +24,12 @@ void PPU::tick() {
if(status.ly == 241 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 241 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
if(status.ly == 260 && status.lx == 340) status.sprite_zero_hit = 0, status.sprite_overflow = 0;
if(status.ly == 260 && status.lx == 340) status.nmi_hold = 0;
if(status.ly == 261 && status.lx == 0) status.nmi_flag = status.nmi_hold;
if(status.ly == 261 && status.lx == 2) cpu.set_nmi_line(status.nmi_enable && status.nmi_flag);
if(status.ly == 261 && status.lx == 0) status.sprite_zero_hit = 0;
clock += 4;
if(clock >= 0) co_switch(cpu.thread);
@ -298,6 +298,7 @@ void PPU::raster_pixel() {
if(status.bg_enable == false) palette = 0;
if(status.bg_edge_enable == false && status.lx < 8) palette = 0;
if(status.sprite_enable == true)
for(signed sprite = 7; sprite >= 0; sprite--) {
if(status.sprite_edge_enable == false && status.lx < 8) continue;
if(raster.oam[sprite].id == 64) continue;
@ -328,7 +329,7 @@ void PPU::raster_pixel() {
}
void PPU::raster_sprite() {
if(status.sprite_enable == false) return;
if(raster_enable() == false) return;
unsigned n = raster.oam_iterator++;
signed ly = (status.ly == 261 ? -1 : status.ly);

View File

@ -59,8 +59,8 @@ void ICD2::reset() {
r7800 = 0x0000;
mlt_req = 0;
for(auto &byte : lcd.buffer) byte = 0;
for(auto &byte : lcd.output) byte = 0;
for(auto &n : lcd.buffer) n = 0;
for(auto &n : lcd.output) n = 0;
lcd.row = 0;
packetsize = 0;

View File

@ -7,7 +7,7 @@ void ICD2::lcdScanline() {
}
unsigned offset = (lcd.row * 160 * 8) + ((GameBoy::lcd.status.ly & 7) * 160);
memcpy(lcd.buffer + offset, GameBoy::lcd.screen + GameBoy::lcd.status.ly * 160, 160);
memcpy(lcd.buffer + offset, GameBoy::lcd.screen + GameBoy::lcd.status.ly * 160, 160 * sizeof(uint16));
}
void ICD2::joypWrite(bool p15, bool p14) {
@ -80,7 +80,7 @@ void ICD2::joypWrite(bool p15, bool p14) {
packetlock = true;
}
void ICD2::videoRefresh(const uint8_t *data) {
void ICD2::videoRefresh(const uint16_t *data) {
}
void ICD2::audioSample(int16_t center, int16_t left, int16_t right) {

View File

@ -1,6 +1,6 @@
void lcdScanline();
void joypWrite(bool p15, bool p14);
void videoRefresh(const uint8_t *data);
void videoRefresh(const uint16_t *data);
void audioSample(int16_t center, int16_t left, int16_t right);
bool inputPoll(unsigned id);

View File

@ -1,8 +1,8 @@
#ifdef ICD2_CPP
//convert linear pixel data { 0x00, 0x55, 0xaa, 0xff } to 2bpp planar tiledata
void ICD2::render(const uint8 *source) {
memset(lcd.output, 0x00, 320);
void ICD2::render(const uint16 *source) {
memset(lcd.output, 0x00, 320 * sizeof(uint16));
for(unsigned y = 0; y < 8; y++) {
for(unsigned x = 0; x < 160; x++) {

View File

@ -1,4 +1,4 @@
void render(const uint8 *source);
void render(const uint16 *source);
uint8 r6000_ly; //SGB BIOS' cache of LY
uint8 r6000_row; //SGB BIOS' cache of ROW
@ -13,7 +13,7 @@ unsigned r7800; //VRAM offset
uint8 mlt_req; //number of active joypads
struct LCD {
uint8 buffer[4 * 160 * 8]; //four tile rows of linear video data
uint8 output[320]; //one tile row of 2bpp video data
unsigned row; //active ICD2 rendering tile row
uint16 buffer[4 * 160 * 8]; //four tile rows of linear video data
uint16 output[320]; //one tile row of 2bpp video data
unsigned row; //active ICD2 rendering tile row
} lcd;

View File

@ -57,7 +57,7 @@ void Interface::loadSufamiTurboCartridge(const CartridgeData &base, const Cartri
void Interface::loadSuperGameBoyCartridge(const CartridgeData &base, const CartridgeData &slot) {
cartridge.rom.copy(base.data, base.size);
GameBoy::cartridge.load(slot.markup, slot.data, slot.size);
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, slot.markup, slot.data, slot.size);
cartridge.load(Cartridge::Mode::SuperGameBoy, base.markup);
system.power();
}

View File

@ -41,12 +41,13 @@ FileBrowser::FileBrowser() {
fileList.onActivate = openButton.onTick = { &FileBrowser::fileListActivate, this };
filterModes.append({ "Default", "", { "*" } });
filterModes.append({ "NES", "", { "*.fc", "*.nes" } });
filterModes.append({ "SNES", "", { "*.sfc" } });
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } });
filterModes.append({ "Satellaview", "", { "*.bs" } });
filterModes.append({ "SufamiTurbo", "", { "*.st" } });
filterModes.append({ "Default", "", { "*" } });
filterModes.append({ "NES", "", { "*.fc", "*.nes" } });
filterModes.append({ "SNES", "", { "*.sfc" } });
filterModes.append({ "GameBoy", "", { "*.gb", "*.gbc" } });
filterModes.append({ "GameBoyColor", "", { "*.gbc" } });
filterModes.append({ "Satellaview", "", { "*.bs" } });
filterModes.append({ "SufamiTurbo", "", { "*.st" } });
mode = &filterModes[Mode::Default];
for(auto &mode : filterModes) config.attach(mode.path, mode.name);

View File

@ -9,7 +9,7 @@ struct FileBrowser : Window {
Label filterLabel;
Button openButton;
struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, Satellaview, SufamiTurbo }; };
struct Mode { enum : unsigned { Default, NES, SNES, GameBoy, GameBoyColor, Satellaview, SufamiTurbo }; };
void open(const string &title, unsigned mode, function<void (string)> callback);
FileBrowser();

View File

@ -10,6 +10,7 @@ MainWindow::MainWindow() {
cartridgeLoadSNES.setText("Load SNES Cartridge ...");
cartridgeLoadNES.setText("Load NES Cartridge ...");
cartridgeLoadGameBoy.setText("Load Game Boy Cartridge ...");
cartridgeLoadGameBoyColor.setText("Load Game Boy Color Cartridge ...");
cartridgeLoadSatellaviewSlotted.setText("Load Satellaview-Slotted Cartridge ...");
cartridgeLoadSatellaview.setText("Load Satellaview Cartridge ...");
cartridgeLoadSufamiTurbo.setText("Load Sufami Turbo Cartridge ...");
@ -99,6 +100,7 @@ MainWindow::MainWindow() {
cartridgeMenu.append(cartridgeLoadNES);
cartridgeMenu.append(cartridgeLoadSNES);
cartridgeMenu.append(cartridgeLoadGameBoy);
cartridgeMenu.append(cartridgeLoadGameBoyColor);
cartridgeMenu.append(cartridgeSeparator);
cartridgeMenu.append(cartridgeLoadSatellaviewSlotted);
cartridgeMenu.append(cartridgeLoadSatellaview);
@ -211,7 +213,13 @@ MainWindow::MainWindow() {
cartridgeLoadGameBoy.onTick = [&] {
fileBrowser->open("Load Cartridge - Game Boy", FileBrowser::Mode::GameBoy, [](string filename) {
interface->gameBoy.loadCartridge(filename);
interface->gameBoy.loadCartridge(GameBoy::System::Revision::GameBoy, filename);
});
};
cartridgeLoadGameBoyColor.onTick = [&] {
fileBrowser->open("Load Cartridge - Game Boy Color", FileBrowser::Mode::GameBoyColor, [](string filename) {
interface->gameBoy.loadCartridge(GameBoy::System::Revision::GameBoyColor, filename);
});
};

View File

@ -6,6 +6,7 @@ struct MainWindow : Window {
Item cartridgeLoadSNES;
Item cartridgeLoadNES;
Item cartridgeLoadGameBoy;
Item cartridgeLoadGameBoyColor;
Separator cartridgeSeparator;
Item cartridgeLoadSatellaviewSlotted;
Item cartridgeLoadSatellaview;

View File

@ -1,4 +1,4 @@
bool InterfaceGameBoy::loadCartridge(const string &filename) {
bool InterfaceGameBoy::loadCartridge(GameBoy::System::Revision revision, const string &filename) {
uint8_t *data;
unsigned size;
if(interface->loadFile(filename, data, size) == false) return false;
@ -7,7 +7,7 @@ bool InterfaceGameBoy::loadCartridge(const string &filename) {
interface->baseName = nall::basename(filename);
GameBoyCartridge info(data, size);
GameBoy::Interface::loadCartridge(info.markup, data, size);
GameBoy::Interface::loadCartridge(revision, info.markup, data, size);
delete[] data;
if(GameBoy::Interface::memorySize(GameBoy::Interface::Memory::RAM) > 0) {
@ -19,6 +19,7 @@ bool InterfaceGameBoy::loadCartridge(const string &filename) {
}
}
GameBoy::interface = this;
interface->loadCartridge(::Interface::Mode::GameBoy);
return true;
}
@ -51,18 +52,32 @@ bool InterfaceGameBoy::loadState(const string &filename) {
//
void InterfaceGameBoy::videoRefresh(const uint8_t *data) {
void InterfaceGameBoy::videoRefresh(const uint16_t *data) {
static uint16_t output[160 * 144];
static uint32_t palette[] = {
0x9bbc0f, 0x8bac0f, 0x306230, 0x0f380f
};
for(unsigned y = 0; y < 144; y++) {
const uint8_t *sp = data + y * 160;
uint16_t *dp = output + y * 160;
for(unsigned x = 0; x < 160; x++) {
uint32_t color = palette[*sp++];
*dp++ = ((color & 0xf80000) >> 9) | ((color & 0x00f800) >> 6) | ((color & 0x0000f8) >> 3);
if(GameBoy::system.cgb() == false) { //L2
static uint32_t palette[] = {
0x9bbc0f, 0x8bac0f, 0x306230, 0x0f380f
};
for(unsigned y = 0; y < 144; y++) {
const uint16_t *sp = data + y * 160;
uint16_t *dp = output + y * 160;
for(unsigned x = 0; x < 160; x++) {
uint32_t color = palette[*sp++];
*dp++ = ((color & 0xf80000) >> 9) | ((color & 0x00f800) >> 6) | ((color & 0x0000f8) >> 3);
}
}
}
if(GameBoy::system.cgb() == true) { //BGR555
for(unsigned y = 0; y < 144; y++) {
const uint16_t *sp = data + y * 160;
uint16_t *dp = output + y * 160;
for(unsigned x = 0; x < 160; x++) {
uint16_t color = *sp++;
*dp++ = ((color >> 10) & 0x001f) | (color & 0x03e0) | ((color << 10) & 0x7c00);
}
}
}

View File

@ -1,11 +1,11 @@
struct InterfaceGameBoy : GameBoy::Interface {
bool loadCartridge(const string &filename);
bool loadCartridge(GameBoy::System::Revision revision, const string &filename);
void unloadCartridge();
bool saveState(const string &filename);
bool loadState(const string &filename);
void videoRefresh(const uint8_t *data);
void videoRefresh(const uint16_t *data);
void audioSample(int16_t csample, int16_t lsample, int16_t rsample);
bool inputPoll(unsigned id);
};

View File

@ -74,8 +74,8 @@ bool Interface::loadCartridge(const string &filename) {
bool result = false;
if(filename.endswith(".nes")) result = nes.loadCartridge(filename);
if(filename.endswith(".sfc")) result = snes.loadCartridge(filename);
if(filename.endswith(".gb" )) result = gameBoy.loadCartridge(filename);
if(filename.endswith(".gbc")) result = gameBoy.loadCartridge(filename);
if(filename.endswith(".gb" )) result = gameBoy.loadCartridge(GameBoy::System::Revision::GameBoy, filename);
if(filename.endswith(".gbc")) result = gameBoy.loadCartridge(GameBoy::System::Revision::GameBoyColor, filename);
return result;
}

View File

@ -27,7 +27,7 @@ void Application::run() {
}
Application::Application(int argc, char **argv) {
title = "bsnes v083.03";
title = "bsnes v083.05";
application = this;
quit = false;

View File

@ -29,6 +29,9 @@ void Utility::setMode(Interface::Mode mode) {
}
else if(mode == Interface::Mode::GameBoy) {
mainWindow->gameBoyMenu.setText(
GameBoy::system.cgb() == false ? "Game Boy" : "Game Boy Color"
);
mainWindow->setTitle(notdir(interface->baseName));
mainWindow->gameBoyMenu.setVisible(true);
dspaudio.setChannels(2);