diff --git a/bsnes/gameboy/interface/interface.hpp b/bsnes/gameboy/interface/interface.hpp index 982fd989..1097716b 100755 --- a/bsnes/gameboy/interface/interface.hpp +++ b/bsnes/gameboy/interface/interface.hpp @@ -1,5 +1,6 @@ class Interface { public: + virtual void lcd_scanline() {} virtual void joyp_write(bool p15, bool p14) {} virtual void video_refresh(const uint8_t *data) {} diff --git a/bsnes/gameboy/lcd/lcd.cpp b/bsnes/gameboy/lcd/lcd.cpp index 7d12d321..c0644152 100755 --- a/bsnes/gameboy/lcd/lcd.cpp +++ b/bsnes/gameboy/lcd/lcd.cpp @@ -75,6 +75,7 @@ void LCD::render() { uint8_t *output = screen + status.ly * 160; for(unsigned n = 0; n < 160; n++) output[n] = (3 - line[n]) * 0x55; + system.interface->lcd_scanline(); } uint16 LCD::read_tile(bool select, unsigned x, unsigned y) { diff --git a/bsnes/snes/chip/icd2/icd2.cpp b/bsnes/snes/chip/icd2/icd2.cpp index e82704ec..4f332139 100755 --- a/bsnes/snes/chip/icd2/icd2.cpp +++ b/bsnes/snes/chip/icd2/icd2.cpp @@ -33,10 +33,6 @@ void ICD2::init() { } void ICD2::load() { - bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2181, 0x2182, { &ICD2::mmio_read, &icd2 }, { &ICD2::mmio_write, &icd2 }); - bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x420b, 0x420b, { &ICD2::mmio_read, &icd2 }, { &ICD2::mmio_write, &icd2 }); - bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2181, 0x2182, { &ICD2::mmio_read, &icd2 }, { &ICD2::mmio_write, &icd2 }); - bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x420b, 0x420b, { &ICD2::mmio_read, &icd2 }, { &ICD2::mmio_write, &icd2 }); } void ICD2::unload() { @@ -52,10 +48,8 @@ void ICD2::power() { void ICD2::reset() { create(ICD2::Enter, cpu.frequency / 5); - r2181 = 0x00; - r2182 = 0x00; - - r6000 = 0x00; + r6000_ly = 0x00; + r6000_row = 0x00; r6003 = 0x00; r6004 = 0xff; r6005 = 0xff; @@ -65,7 +59,9 @@ void ICD2::reset() { r7800 = 0x0000; mlt_req = 0; - for(unsigned n = 0; n < 320; n++) vram[n] = 0xff; + foreach(byte, lcd.buffer) byte = 0xff; + foreach(byte, lcd.output) byte = 0xff; + lcd.row = 0; packetsize = 0; joyp_id = 3; diff --git a/bsnes/snes/chip/icd2/interface/interface.cpp b/bsnes/snes/chip/icd2/interface/interface.cpp index 2dcac5b3..1608037f 100755 --- a/bsnes/snes/chip/icd2/interface/interface.cpp +++ b/bsnes/snes/chip/icd2/interface/interface.cpp @@ -1,5 +1,15 @@ #ifdef ICD2_CPP +//called on rendered lines 0-143 (not on Vblank lines 144-153) +void ICD2::lcd_scanline() { + if((GameBoy::lcd.status.ly & 7) == 0) { + lcd.row = (lcd.row + 1) & 3; + } + + 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); +} + void ICD2::joyp_write(bool p15, bool p14) { //joypad handling if(p15 == 1 && p14 == 1) { diff --git a/bsnes/snes/chip/icd2/interface/interface.hpp b/bsnes/snes/chip/icd2/interface/interface.hpp index 2f88ca91..531a9ce7 100755 --- a/bsnes/snes/chip/icd2/interface/interface.hpp +++ b/bsnes/snes/chip/icd2/interface/interface.hpp @@ -1,3 +1,4 @@ +void lcd_scanline(); void joyp_write(bool p15, bool p14); void video_refresh(const uint8_t *data); void audio_sample(int16_t center, int16_t left, int16_t right); diff --git a/bsnes/snes/chip/icd2/mmio/mmio.cpp b/bsnes/snes/chip/icd2/mmio/mmio.cpp index 9899f8be..0ee16ef5 100755 --- a/bsnes/snes/chip/icd2/mmio/mmio.cpp +++ b/bsnes/snes/chip/icd2/mmio/mmio.cpp @@ -1,38 +1,19 @@ #ifdef ICD2_CPP -uint8 ICD2::mmio_read(unsigned addr) { - if((uint16)addr == 0x2181) return cpu.mmio_read(addr); - if((uint16)addr == 0x2182) return cpu.mmio_read(addr); - if((uint16)addr == 0x420b) return cpu.mmio_read(addr); - return 0x00; -} +//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::mmio_write(unsigned addr, uint8 data) { - if((uint16)addr == 0x420b && data == 0x10) { - unsigned offset = (r2182 << 8) | (r2181 << 0); - r7800 = 0; - unsigned row = 0; - if(offset >= 0x5000 && offset <= 0x6540) row = (offset - 0x5000) / 320; - if(offset >= 0x6800 && offset <= 0x7d40) row = (offset - 0x6800) / 320; + for(unsigned y = 0; y < 8; y++) { + for(unsigned x = 0; x < 160; x++) { + unsigned pixel = *source++ / 0x55; + pixel ^= 3; - uint8 *source = GameBoy::lcd.screen + row * 160 * 8; - memset(vram, 0x00, 320); - - for(unsigned y = row * 8; y < row * 8 + 8; y++) { - for(unsigned x = 0; x < 160; x++) { - unsigned pixel = *source++ / 0x55; - pixel ^= 3; - - unsigned addr = (x / 8 * 16) + ((y & 7) * 2); - vram[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7)); - vram[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7)); - } + unsigned addr = y * 2 + (x / 8 * 16); + lcd.output[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7)); + lcd.output[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7)); } } - - if((uint16)addr == 0x2181) return cpu.mmio_write(addr, r2181 = data); - if((uint16)addr == 0x2182) return cpu.mmio_write(addr, r2182 = data); - if((uint16)addr == 0x420b) return cpu.mmio_write(addr, data); } uint8 ICD2::read(unsigned addr) { @@ -40,7 +21,9 @@ uint8 ICD2::read(unsigned addr) { //LY counter if(addr == 0x6000) { - return GameBoy::lcd.status.ly; + r6000_ly = GameBoy::lcd.status.ly; + r6000_row = lcd.row; + return r6000_ly; } //command ready port @@ -66,7 +49,7 @@ uint8 ICD2::read(unsigned addr) { //VRAM port if(addr == 0x7800) { - uint8 data = vram[r7800]; + uint8 data = lcd.output[r7800]; r7800 = (r7800 + 1) % 320; return data; } @@ -77,6 +60,17 @@ uint8 ICD2::read(unsigned addr) { void ICD2::write(unsigned addr, uint8 data) { addr &= 0xffff; + //VRAM port + if(addr == 0x6001) { + r6001 = data; + r7800 = 0; + + unsigned offset = (r6000_row - (4 - (r6001 - (r6000_ly & 3)))) & 3; + render(lcd.buffer + offset * 160 * 8); + + return; + } + //control port //d7: 0 = halt, 1 = reset //d5,d4: 0 = 1-player, 1 = 2-player, 2 = 4-player, 3 = ??? diff --git a/bsnes/snes/chip/icd2/mmio/mmio.hpp b/bsnes/snes/chip/icd2/mmio/mmio.hpp index a8607012..381cd332 100755 --- a/bsnes/snes/chip/icd2/mmio/mmio.hpp +++ b/bsnes/snes/chip/icd2/mmio/mmio.hpp @@ -1,16 +1,19 @@ -uint8 r2181; -uint8 r2182; -uint8 mmio_read(unsigned addr); -void mmio_write(unsigned addr, uint8 data); +void render(const uint8 *source); -uint8 r6000; -uint8 r6003; -uint8 r6004; -uint8 r6005; -uint8 r6006; -uint8 r6007; -uint8 r7000[16]; -unsigned r7800; -uint8 mlt_req; +uint8 r6000_ly; //SGB BIOS' cache of LY +uint8 r6000_row; //SGB BIOS' cache of ROW +uint8 r6001; //VRAM conversion +uint8 r6003; //control port +uint8 r6004; //joypad 1 +uint8 r6005; //joypad 2 +uint8 r6006; //joypad 3 +uint8 r6007; //joypad 4 +uint8 r7000[16]; //JOYP packet data +unsigned r7800; //VRAM offset +uint8 mlt_req; //number of active joypads -uint8 vram[320]; +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 +} lcd; diff --git a/bsnes/snes/chip/icd2/serialization.cpp b/bsnes/snes/chip/icd2/serialization.cpp index 1931daa5..5c31448e 100755 --- a/bsnes/snes/chip/icd2/serialization.cpp +++ b/bsnes/snes/chip/icd2/serialization.cpp @@ -18,10 +18,9 @@ void ICD2::serialize(serializer &s) { s.integer(bitdata); s.integer(bitoffset); - s.integer(r2181); - s.integer(r2182); - - s.integer(r6000); + s.integer(r6000_ly); + s.integer(r6000_row); + s.integer(r6001); s.integer(r6003); s.integer(r6004); s.integer(r6005); @@ -31,7 +30,9 @@ void ICD2::serialize(serializer &s) { s.integer(r7800); s.integer(mlt_req); - s.array(vram); + s.array(lcd.buffer); + s.array(lcd.output); + s.integer(lcd.row); } #endif diff --git a/bsnes/snes/snes.hpp b/bsnes/snes/snes.hpp index d4991082..5815e19a 100755 --- a/bsnes/snes/snes.hpp +++ b/bsnes/snes/snes.hpp @@ -1,7 +1,7 @@ namespace SNES { namespace Info { static const char Name[] = "bsnes"; - static const char Version[] = "078.05"; + static const char Version[] = "078.06"; static const unsigned SerializerVersion = 20; } }