mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
6b708de893
commit
118a393c4c
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,6 +4,7 @@ void timer_65536hz();
|
|||
void timer_16384hz();
|
||||
void timer_8192hz();
|
||||
void timer_4096hz();
|
||||
void hblank();
|
||||
|
||||
//opcode.cpp
|
||||
void op_io();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
unsigned vram_addr(uint16 addr) const;
|
||||
uint8 mmio_read(uint16 addr);
|
||||
void mmio_write(uint16 addr, uint8 data);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ struct MainWindow : Window {
|
|||
Item cartridgeLoadSNES;
|
||||
Item cartridgeLoadNES;
|
||||
Item cartridgeLoadGameBoy;
|
||||
Item cartridgeLoadGameBoyColor;
|
||||
Separator cartridgeSeparator;
|
||||
Item cartridgeLoadSatellaviewSlotted;
|
||||
Item cartridgeLoadSatellaview;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue