diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..31714c94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +higan/profile/WonderSwan.sys/internal.rom +higan/profile/WonderSwan Color.sys/internal.rom diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 5cab08fc..ddf1c18d 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -6,7 +6,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "097.15"; + static const string Version = "097.16"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; @@ -17,8 +17,6 @@ namespace Emulator { static const string Profile = "Balanced"; #elif defined(PROFILE_PERFORMANCE) static const string Profile = "Performance"; - #else - static const string Profile = "Accuracy"; #endif } diff --git a/higan/profile/WonderSwan Color.sys/manifest.bml b/higan/profile/WonderSwan Color.sys/manifest.bml index 4f4af405..22c9faea 100644 --- a/higan/profile/WonderSwan Color.sys/manifest.bml +++ b/higan/profile/WonderSwan Color.sys/manifest.bml @@ -1 +1,2 @@ system name:WonderSwan Color + eeprom name=internal.rom size=2048 diff --git a/higan/profile/WonderSwan.sys/manifest.bml b/higan/profile/WonderSwan.sys/manifest.bml index b2be29cf..9c778cc0 100644 --- a/higan/profile/WonderSwan.sys/manifest.bml +++ b/higan/profile/WonderSwan.sys/manifest.bml @@ -1 +1,2 @@ system name:WonderSwan + eeprom name=internal.rom size=128 diff --git a/higan/sfc/ppu/ppu.hpp b/higan/sfc/ppu/ppu.hpp index 285c775d..6d691286 100644 --- a/higan/sfc/ppu/ppu.hpp +++ b/higan/sfc/ppu/ppu.hpp @@ -62,12 +62,12 @@ privileged: friend class Video; struct Debugger { - hook vram_read; - hook oam_read; - hook cgram_read; - hook vram_write; - hook oam_write; - hook cgram_write; + hook void> vram_read; + hook void> oam_read; + hook void> cgram_read; + hook void> vram_write; + hook void> oam_write; + hook void> cgram_write; } debugger; }; diff --git a/higan/sfc/ppu/serialization.cpp b/higan/sfc/ppu/serialization.cpp index cc064d7f..f8bddf62 100644 --- a/higan/sfc/ppu/serialization.cpp +++ b/higan/sfc/ppu/serialization.cpp @@ -137,16 +137,16 @@ auto PPU::Background::serialize(serializer& s) -> void { } auto PPU::Sprite::serialize(serializer& s) -> void { - for(unsigned i = 0; i < 128; i++) { - s.integer(list[i].x); - s.integer(list[i].y); - s.integer(list[i].character); - s.integer(list[i].nameselect); - s.integer(list[i].vflip); - s.integer(list[i].hflip); - s.integer(list[i].priority); - s.integer(list[i].palette); - s.integer(list[i].size); + for(auto n : range(128)) { + s.integer(list[n].x); + s.integer(list[n].y); + s.integer(list[n].character); + s.integer(list[n].nameselect); + s.integer(list[n].vflip); + s.integer(list[n].hflip); + s.integer(list[n].priority); + s.integer(list[n].palette); + s.integer(list[n].size); } s.integer(t.x); @@ -156,17 +156,21 @@ auto PPU::Sprite::serialize(serializer& s) -> void { s.integer(t.tile_count); s.integer(t.active); - for(unsigned n = 0; n < 2; n++) { - s.array(t.item[n]); - for(unsigned i = 0; i < 34; i++) { - s.integer(t.tile[n][i].x); - s.integer(t.tile[n][i].priority); - s.integer(t.tile[n][i].palette); - s.integer(t.tile[n][i].hflip); - s.integer(t.tile[n][i].d0); - s.integer(t.tile[n][i].d1); - s.integer(t.tile[n][i].d2); - s.integer(t.tile[n][i].d3); + for(auto p : range(2)) { + for(auto n : range(32)) { + s.integer(t.item[p][n].valid); + s.integer(t.item[p][n].index); + } + for(auto n : range(34)) { + s.integer(t.tile[p][n].valid); + s.integer(t.tile[p][n].x); + s.integer(t.tile[p][n].priority); + s.integer(t.tile[p][n].palette); + s.integer(t.tile[p][n].hflip); + s.integer(t.tile[p][n].d0); + s.integer(t.tile[p][n].d1); + s.integer(t.tile[p][n].d2); + s.integer(t.tile[p][n].d3); } } diff --git a/higan/sfc/ppu/sprite/list.cpp b/higan/sfc/ppu/sprite/list.cpp index fd9114a0..aff53120 100644 --- a/higan/sfc/ppu/sprite/list.cpp +++ b/higan/sfc/ppu/sprite/list.cpp @@ -1,38 +1,38 @@ auto PPU::Sprite::update(uint addr, uint8 data) -> void { if(addr < 0x0200) { - uint n = addr >> 2; + uint n = addr >> 2; //sprite# addr &= 3; if(addr == 0) { - list[n].x = (list[n].x & 0x100) | data; + list[n].x.bits(0,7) = data; } else if(addr == 1) { list[n].y = data; } else if(addr == 2) { list[n].character = data; } else { //(addr == 3) - list[n].vflip = data & 0x80; - list[n].hflip = data & 0x40; - list[n].priority = (data >> 4) & 3; - list[n].palette = (data >> 1) & 7; - list[n].nameselect = data & 1; + list[n].vflip = data.bit (7); + list[n].hflip = data.bit (6); + list[n].priority = data.bits(5,4); + list[n].palette = data.bits(3,1); + list[n].nameselect = data.bit (0); } } else { - uint n = (addr & 0x1f) << 2; - list[n + 0].x = ((data & 0x01) << 8) | (list[n + 0].x & 0xff); - list[n + 0].size = data & 0x02; - list[n + 1].x = ((data & 0x04) << 6) | (list[n + 1].x & 0xff); - list[n + 1].size = data & 0x08; - list[n + 2].x = ((data & 0x10) << 4) | (list[n + 2].x & 0xff); - list[n + 2].size = data & 0x20; - list[n + 3].x = ((data & 0x40) << 2) | (list[n + 3].x & 0xff); - list[n + 3].size = data & 0x80; + uint n = (addr & 0x1f) << 2; //sprite# + list[n + 0].x.bit(8) = data.bit(0); + list[n + 0].size = data.bit(1); + list[n + 1].x.bit(8) = data.bit(2); + list[n + 1].size = data.bit(3); + list[n + 2].x.bit(8) = data.bit(4); + list[n + 2].size = data.bit(5); + list[n + 3].x.bit(8) = data.bit(6); + list[n + 3].size = data.bit(7); } } auto PPU::Sprite::synchronize() -> void { - for(uint n = 0; n < 544; n++) update(n, ppu.oam[n]); + for(auto n : range(544)) update(n, ppu.oam[n]); } -auto PPU::Sprite::SpriteItem::width() const -> uint{ +auto PPU::Sprite::Object::width() const -> uint{ if(size == 0) { static uint width[] = { 8, 8, 8, 16, 16, 32, 16, 16}; return width[ppu.sprite.regs.base_size]; @@ -42,7 +42,7 @@ auto PPU::Sprite::SpriteItem::width() const -> uint{ } } -auto PPU::Sprite::SpriteItem::height() const -> uint { +auto PPU::Sprite::Object::height() const -> uint { if(size == 0) { if(ppu.sprite.regs.interlace && ppu.sprite.regs.base_size >= 6) return 16; static uint height[] = { 8, 8, 8, 16, 16, 32, 32, 32}; diff --git a/higan/sfc/ppu/sprite/sprite.cpp b/higan/sfc/ppu/sprite/sprite.cpp index 68e82501..5b0f46e2 100644 --- a/higan/sfc/ppu/sprite/sprite.cpp +++ b/higan/sfc/ppu/sprite/sprite.cpp @@ -9,7 +9,7 @@ auto PPU::Sprite::address_reset() -> void { } auto PPU::Sprite::set_first_sprite() -> void { - regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127); + regs.first_sprite = !self.regs.oam_priority ? 0 : self.regs.oam_addr >> 2; } auto PPU::Sprite::frame() -> void { @@ -28,27 +28,27 @@ auto PPU::Sprite::scanline() -> void { auto oam_item = t.item[t.active]; auto oam_tile = t.tile[t.active]; - if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disable == false) address_reset(); + if(t.y == (!self.regs.overscan ? 225 : 240) && !self.regs.display_disable) address_reset(); if(t.y >= (!self.regs.overscan ? 224 : 239)) return; - memset(oam_item, 0xff, 32); //default to invalid - for(unsigned i = 0; i < 34; i++) oam_tile[i].x = 0xffff; //default to invalid + for(auto n : range(32)) oam_item[n].valid = false; //default to invalid + for(auto n : range(34)) oam_tile[n].valid = false; //default to invalid - for(unsigned i = 0; i < 128; i++) { - unsigned sprite = (regs.first_sprite + i) & 127; - if(on_scanline(list[sprite]) == false) continue; + for(auto n : range(128)) { + uint7 sprite = regs.first_sprite + n; + if(!on_scanline(list[sprite])) continue; if(t.item_count++ >= 32) break; - oam_item[t.item_count - 1] = sprite; + oam_item[t.item_count - 1] = {true, sprite}; } - if(t.item_count > 0 && oam_item[t.item_count - 1] != 0xff) { - ppu.regs.oam_iaddr = 0x0200 + (oam_item[t.item_count - 1] >> 2); + if(t.item_count > 0 && oam_item[t.item_count - 1].valid) { + ppu.regs.oam_iaddr = 0x0200 + (oam_item[t.item_count - 1].index >> 2); } } -auto PPU::Sprite::on_scanline(SpriteItem& sprite) -> bool { +auto PPU::Sprite::on_scanline(Object& sprite) -> bool { if(sprite.x > 256 && (sprite.x + sprite.width() - 1) < 512) return false; - signed height = (regs.interlace == false ? sprite.height() : (sprite.height() >> 1)); + int height = sprite.height() >> regs.interlace; if(t.y >= sprite.y && t.y < (sprite.y + height)) return true; if((sprite.y + height) >= 256 && t.y < ((sprite.y + height) & 255)) return true; return false; @@ -59,18 +59,18 @@ auto PPU::Sprite::run() -> void { output.sub.priority = 0; auto oam_tile = t.tile[!t.active]; - unsigned priority_table[] = { regs.priority0, regs.priority1, regs.priority2, regs.priority3 }; - unsigned x = t.x++; + uint priority_table[] = {regs.priority0, regs.priority1, regs.priority2, regs.priority3}; + uint x = t.x++; - for(unsigned n = 0; n < 34; n++) { + for(auto n : range(34)) { auto tile = oam_tile[n]; - if(tile.x == 0xffff) break; + if(!tile.valid) break; int px = x - sclip<9>(tile.x); if(px & ~7) continue; - unsigned mask = 0x80 >> (tile.hflip == false ? px : 7 - px); - unsigned color; + uint mask = 0x80 >> (!tile.hflip ? px : 7 - px); + uint color; color = ((bool)(tile.d0 & mask)) << 0; color |= ((bool)(tile.d1 & mask)) << 1; color |= ((bool)(tile.d2 & mask)) << 2; @@ -94,13 +94,13 @@ auto PPU::Sprite::tilefetch() -> void { auto oam_item = t.item[t.active]; auto oam_tile = t.tile[t.active]; - for(signed i = 31; i >= 0; i--) { - if(oam_item[i] == 0xff) continue; - auto sprite = list[oam_item[i]]; + for(int i = 31; i >= 0; i--) { + if(!oam_item[i].valid) continue; + auto sprite = list[oam_item[i].index]; - unsigned tile_width = sprite.width() >> 3; - signed x = sprite.x; - signed y = (t.y - sprite.y) & 0xff; + uint tile_width = sprite.width() >> 3; + int x = sprite.x; + int y = (t.y - sprite.y) & 0xff; if(regs.interlace) y <<= 1; if(sprite.vflip) { @@ -114,7 +114,7 @@ auto PPU::Sprite::tilefetch() -> void { } if(regs.interlace) { - y = (sprite.vflip == false ? y + self.field() : y - self.field()); + y = !sprite.vflip ? y + self.field() : y - self.field(); } x &= 511; @@ -130,19 +130,20 @@ auto PPU::Sprite::tilefetch() -> void { chry &= 15; chry <<= 4; - for(unsigned tx = 0; tx < tile_width; tx++) { - unsigned sx = (x + (tx << 3)) & 511; + for(uint tx = 0; tx < tile_width; tx++) { + uint sx = (x + (tx << 3)) & 511; if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; if(t.tile_count++ >= 34) break; - unsigned n = t.tile_count - 1; + uint n = t.tile_count - 1; + oam_tile[n].valid = true; oam_tile[n].x = sx; oam_tile[n].priority = sprite.priority; oam_tile[n].palette = 128 + (sprite.palette << 4); oam_tile[n].hflip = sprite.hflip; - unsigned mx = (sprite.hflip == false) ? tx : ((tile_width - 1) - tx); - unsigned pos = tiledata_addr + ((chry + ((chrx + mx) & 15)) << 5); + uint mx = !sprite.hflip ? tx : (tile_width - 1) - tx; + uint pos = tiledata_addr + ((chry + ((chrx + mx) & 15)) << 5); uint16 addr = (pos & 0xffe0) + ((y & 7) * 2); oam_tile[n].d0 = ppu.vram[addr + 0]; @@ -161,16 +162,16 @@ auto PPU::Sprite::tilefetch() -> void { } auto PPU::Sprite::reset() -> void { - for(unsigned i = 0; i < 128; i++) { - list[i].x = 0; - list[i].y = 0; - list[i].character = 0; - list[i].nameselect = 0; - list[i].vflip = 0; - list[i].hflip = 0; - list[i].priority = 0; - list[i].palette = 0; - list[i].size = 0; + for(auto n : range(128)) { + list[n].x = 0; + list[n].y = 0; + list[n].character = 0; + list[n].nameselect = 0; + list[n].vflip = 0; + list[n].hflip = 0; + list[n].priority = 0; + list[n].palette = 0; + list[n].size = 0; } synchronize(); @@ -181,17 +182,21 @@ auto PPU::Sprite::reset() -> void { t.tile_count = 0; t.active = 0; - for(unsigned n = 0; n < 2; n++) { - memset(t.item[n], 0, 32); - for(unsigned i = 0; i < 34; i++) { - t.tile[n][i].x = 0; - t.tile[n][i].priority = 0; - t.tile[n][i].palette = 0; - t.tile[n][i].hflip = 0; - t.tile[n][i].d0 = 0; - t.tile[n][i].d1 = 0; - t.tile[n][i].d2 = 0; - t.tile[n][i].d3 = 0; + for(auto p : range(2)) { + for(auto n : range(32)) { + t.item[p][n].valid = false; + t.item[p][n].index = 0; + } + for(auto n : range(34)) { + t.tile[p][n].valid = false; + t.tile[p][n].x = 0; + t.tile[p][n].priority = 0; + t.tile[p][n].palette = 0; + t.tile[p][n].hflip = 0; + t.tile[p][n].d0 = 0; + t.tile[p][n].d1 = 0; + t.tile[p][n].d2 = 0; + t.tile[p][n].d3 = 0; } } diff --git a/higan/sfc/ppu/sprite/sprite.hpp b/higan/sfc/ppu/sprite/sprite.hpp index 2f1542a5..29d55cd1 100644 --- a/higan/sfc/ppu/sprite/sprite.hpp +++ b/higan/sfc/ppu/sprite/sprite.hpp @@ -1,22 +1,28 @@ struct Sprite { - struct SpriteItem { - uint16 x; - uint16 y; + struct Object { + uint9 x; + uint8 y; uint8 character; bool nameselect; bool vflip; bool hflip; - uint8 priority; - uint8 palette; + uint2 priority; + uint3 palette; bool size; alwaysinline auto width() const -> uint; alwaysinline auto height() const -> uint; } list[128]; - struct TileItem { - uint16 x; - uint16 priority; - uint16 palette; + struct Item { + bool valid; + uint7 index; + }; + + struct Tile { + bool valid; + uint9 x; + uint2 priority; + uint8 palette; bool hflip; uint8 d0, d1, d2, d3; }; @@ -29,8 +35,8 @@ struct Sprite { uint tile_count; bool active; - uint8 item[2][32]; - TileItem tile[2][34]; + Item item[2][32]; + Tile tile[2][34]; } t; struct Regs { @@ -41,7 +47,7 @@ struct Sprite { uint3 base_size; uint2 nameselect; uint16 tiledata_addr; - uint8 first_sprite; + uint7 first_sprite; uint priority0; uint priority1; @@ -54,7 +60,7 @@ struct Sprite { struct Output { struct Pixel { - uint priority; //0 = none (transparent) + uint2 priority; //0 = none (transparent) uint8 palette; } main, sub; } output; @@ -74,7 +80,7 @@ struct Sprite { auto tilefetch() -> void; auto reset() -> void; - auto on_scanline(SpriteItem&) -> bool; + auto on_scanline(Object&) -> bool; auto serialize(serializer&) -> void; diff --git a/higan/ws/GNUmakefile b/higan/ws/GNUmakefile index dd1ea7d9..f61e5b6e 100644 --- a/higan/ws/GNUmakefile +++ b/higan/ws/GNUmakefile @@ -1,5 +1,5 @@ ws_objects := ws-interface ws-system ws-scheduler -ws_objects += ws-memory ws-cartridge +ws_objects += ws-memory ws-eeprom ws-cartridge ws_objects += ws-cpu ws-ppu ws-apu objects += $(ws_objects) @@ -7,6 +7,7 @@ obj/ws-interface.o: $(ws)/interface/interface.cpp $(call rwildcard,$(ws)/interfa obj/ws-system.o: $(ws)/system/system.cpp $(call rwildcard,$(ws)/system/) obj/ws-scheduler.o: $(ws)/scheduler/scheduler.cpp $(call rwildcard,$(ws)/scheduler/) obj/ws-memory.o: $(ws)/memory/memory.cpp $(call rwildcard,$(ws)/memory/) +obj/ws-eeprom.o: $(ws)/eeprom/eeprom.cpp $(call rwildcard,$(ws)/eeprom/) obj/ws-cartridge.o: $(ws)/cartridge/cartridge.cpp $(call rwildcard,$(ws)/cartridge/) obj/ws-cpu.o: $(ws)/cpu/cpu.cpp $(call rwildcard,$(ws)/cpu/) obj/ws-ppu.o: $(ws)/ppu/ppu.cpp $(call rwildcard,$(ws)/ppu/) diff --git a/higan/ws/cartridge/cartridge.cpp b/higan/ws/cartridge/cartridge.cpp index bf636c5d..7c415f93 100644 --- a/higan/ws/cartridge/cartridge.cpp +++ b/higan/ws/cartridge/cartridge.cpp @@ -4,6 +4,7 @@ namespace WonderSwan { Cartridge cartridge; #include "memory.cpp" +#include "io.cpp" auto Cartridge::load() -> void { information.manifest = ""; @@ -26,7 +27,14 @@ auto Cartridge::load() -> void { ram.size = node["size"].natural(); ram.mask = bit::round(ram.size) - 1; if(ram.size) ram.data = new uint8[ram.mask + 1](); - if(ram.name) interface->loadRequest(ID::RAM, ram.name, true); + if(ram.name) interface->loadRequest(ID::RAM, ram.name, false); + } + + if(auto node = document["board/eeprom"]) { + eeprom.setName(node["name"].text()); + eeprom.setSize(node["size"].natural() / sizeof(uint16)); + eeprom.erase(); + if(eeprom.name()) interface->loadRequest(ID::EEPROM, eeprom.name(), false); } information.title = document["information/title"].text(); @@ -48,10 +56,9 @@ auto Cartridge::unload() -> void { } auto Cartridge::power() -> void { - iomap[0x00c0] = this; - iomap[0x00c1] = this; - iomap[0x00c2] = this; - iomap[0x00c3] = this; + eeprom.power(); + + for(uint n = 0x00c0; n <= 0x00c8; n++) iomap[n] = this; r.bank_rom0 = 0xff; r.bank_rom1 = 0xff; diff --git a/higan/ws/cartridge/cartridge.hpp b/higan/ws/cartridge/cartridge.hpp index ae74b764..06676029 100644 --- a/higan/ws/cartridge/cartridge.hpp +++ b/higan/ws/cartridge/cartridge.hpp @@ -26,6 +26,8 @@ struct Cartridge : IO { string name; } rom, ram; + EEPROM eeprom; + struct Information { string manifest; string title; diff --git a/higan/ws/cartridge/io.cpp b/higan/ws/cartridge/io.cpp new file mode 100644 index 00000000..4d21b83f --- /dev/null +++ b/higan/ws/cartridge/io.cpp @@ -0,0 +1,63 @@ +auto Cartridge::portRead(uint16 addr) -> uint8 { + //BANK_ROM2 + if(addr == 0x00c0) return r.bank_rom2; + + //BANK_SRAM + if(addr == 0x00c1) return r.bank_sram; + + //BANK_ROM0 + if(addr == 0x00c2) return r.bank_rom0; + + //BANK_ROM1 + if(addr == 0x00c3) return r.bank_rom1; + + //EEP_DATA + if(addr == 0x00c4) return eeprom.read(EEPROM::DataLo); + if(addr == 0x00c5) return eeprom.read(EEPROM::DataHi); + + //EEP_ADDR + if(addr == 0x00c6) return eeprom.read(EEPROM::AddressLo); + if(addr == 0x00c7) return eeprom.read(EEPROM::AddressHi); + + //EEP_STATUS + if(addr == 0x00c8) return eeprom.read(EEPROM::Status); + + return 0x00; +} + +auto Cartridge::portWrite(uint16 addr, uint8 data) -> void { + //BANK_ROM2 + if(addr == 0x00c0) { + r.bank_rom2 = data; + return; + } + + //BANK_SRAM + if(addr == 0x00c1) { + r.bank_sram = data; + return; + } + + //BANK_ROM0 + if(addr == 0x00c2) { + r.bank_rom0 = data; + return; + } + + //BANK_ROM1 + if(addr == 0x00c3) { + r.bank_rom1 = data; + return; + } + + //EEP_DATA + if(addr == 0x00c4) return eeprom.write(EEPROM::DataLo, data); + if(addr == 0x00c5) return eeprom.write(EEPROM::DataHi, data); + + //EEP_ADDR + if(addr == 0x00c6) return eeprom.write(EEPROM::AddressLo, data); + if(addr == 0x00c7) return eeprom.write(EEPROM::AddressHi, data); + + //EEP_CMD + if(addr == 0x00c8) return eeprom.write(EEPROM::Command, data); +} diff --git a/higan/ws/cartridge/memory.cpp b/higan/ws/cartridge/memory.cpp index 7811149c..67b8584b 100644 --- a/higan/ws/cartridge/memory.cpp +++ b/higan/ws/cartridge/memory.cpp @@ -24,45 +24,3 @@ auto Cartridge::ramWrite(uint addr, uint8 data) -> void { if(!ram.data) return; ram.data[addr & ram.mask] = data; } - -auto Cartridge::portRead(uint16 addr) -> uint8 { - if(addr == 0x00c0) { - return r.bank_rom2; - } - - if(addr == 0x00c1) { - return r.bank_sram; - } - - if(addr == 0x00c2) { - return r.bank_rom0; - } - - if(addr == 0x00c3) { - return r.bank_rom1; - } - - return 0x00; -} - -auto Cartridge::portWrite(uint16 addr, uint8 data) -> void { - if(addr == 0x00c0) { - r.bank_rom2 = data; - return; - } - - if(addr == 0x00c1) { - r.bank_sram = data; - return; - } - - if(addr == 0x00c2) { - r.bank_rom0 = data; - return; - } - - if(addr == 0x00c3) { - r.bank_rom1 = data; - return; - } -} diff --git a/higan/ws/eeprom/eeprom.cpp b/higan/ws/eeprom/eeprom.cpp new file mode 100644 index 00000000..35932ab1 --- /dev/null +++ b/higan/ws/eeprom/eeprom.cpp @@ -0,0 +1,135 @@ +#include + +namespace WonderSwan { + +auto EEPROM::name() const -> string { return _name; } +auto EEPROM::data() -> uint16* { return _data; } +auto EEPROM::size() const -> uint { return _size; } + +auto EEPROM::setName(string name) -> void { + _name = name; +} + +auto EEPROM::setSize(uint size) -> void { + _size = bit::round(size); +} + +auto EEPROM::erase() -> void { + for(auto& word : _data) word = 0xffff; +} + +auto EEPROM::power() -> void { + r.latch = 0; + r.address = 0; + r.unknown = false; + r.writeRequested = false; + r.readRequested = false; + r.writeCompleted = false; + r.readCompleted = false; + r.writeProtect = false; +} + +auto EEPROM::read(uint port) -> uint8 { + if(!_size) return 0x00; + + if(port == DataLo) return r.latch.byte(0); + if(port == DataHi) return r.latch.byte(1); + + if(port == AddressLo) return r.address.byte(0); + if(port == AddressHi) return r.address.byte(1); + + if(port == Status) return ( + 1 << 7 + | r.unknown << 6 + | r.writeRequested << 5 + | r.readRequested << 4 + | r.writeCompleted << 1 + | r.readCompleted << 0 + ); + + return 0x00; +} + +auto EEPROM::write(uint port, uint8 data) -> void { + if(!_size) return; + + if(port == DataLo) { + r.latch.byte(0) = data; + return; + } + + if(port == DataHi) { + r.latch.byte(1) = data; + return; + } + + if(port == AddressLo) { + r.address.byte(0) = data; + return; + } + + if(port == AddressHi) { + r.address.byte(1) = data; + return; + } + + if(port == Command) { + r.unknown = data.bit(6); + r.writeRequested = data.bit(5); + r.readRequested = data.bit(4); + execute(); + return; + } +} + +auto EEPROM::execute() -> void { + auto bits = bit::first(_size); + auto address = r.address.bits(0, bits - 1); + auto special = r.address.bits(bits - 2, bits - 1); + auto command = r.address.bits(bits, bits + 1); + auto start = r.address.bit(bits + 2); + if(!start) return; + + //write disable + if(command == 0 && special == 0) { + r.writeProtect = true; + } + + //write all + if(command == 0 && special == 1 && !r.writeProtect) { + for(auto n : range(_size)) _data[n] = r.latch; + } + + //erase all + if(command == 0 && special == 2 && !r.writeProtect) { + for(auto n : range(_size)) _data[n] = 0xffff; + } + + //write enable + if(command == 0 && special == 3) { + r.writeProtect = false; + } + + //write word + if(command == 1 && r.writeRequested && !r.writeProtect) { + _data[address] = r.latch; + r.writeRequested = false; + r.writeCompleted = true; + } + + //read word + if(command == 2 && r.readRequested) { + r.latch = _data[address]; + r.readRequested = false; + r.readCompleted = true; + } + + //erase word + if(command == 3 && r.writeRequested && !r.writeProtect) { + _data[address] = 0xffff; + r.writeRequested = false; + r.writeCompleted = true; + } +} + +} diff --git a/higan/ws/eeprom/eeprom.hpp b/higan/ws/eeprom/eeprom.hpp new file mode 100644 index 00000000..d9c165ce --- /dev/null +++ b/higan/ws/eeprom/eeprom.hpp @@ -0,0 +1,49 @@ +//93LC46 ( 64x16-bit) [ 128 bytes] +//93LC56 ( 128x16-bit) [ 256 bytes] +//93LC66 ( 256x16-bit) [ 512 bytes] +//93LC76 ( 512x16-bit) [1024 bytes] +//93LC86 (1024x16-bit) [2048 bytes] + +struct EEPROM { + enum : uint { + DataLo, + DataHi, + AddressLo, + AddressHi, + Status, + Command = Status, + }; + + auto name() const -> string; + auto data() -> uint16*; + auto size() const -> uint; + + auto setName(string name) -> void; + auto setSize(uint size) -> void; + + auto erase() -> void; + auto power() -> void; + auto read(uint) -> uint8; + auto write(uint, uint8) -> void; + +private: + auto execute() -> void; + + string _name; + uint16 _data[1024]; + uint _size = 0; //in words + + struct Registers { + uint16 latch; + uint16 address; + + bool unknown; + bool writeRequested; + bool readRequested; + + bool writeCompleted; + bool readCompleted; + + bool writeProtect; + } r; +}; diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index 429b4e3e..ea242bf1 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -85,10 +85,13 @@ auto Interface::sha256() -> string { auto Interface::group(uint id) -> uint { switch(id) { case ID::SystemManifest: + case ID::SystemIPLROM: + case ID::SystemEEPROM: return 0; case ID::Manifest: case ID::ROM: case ID::RAM: + case ID::EEPROM: switch(system.revision()) { case System::Revision::WonderSwan: return ID::WonderSwan; @@ -106,7 +109,9 @@ auto Interface::load(uint id) -> void { } auto Interface::save() -> void { - if(cartridge.ram.name) interface->saveRequest(ID::RAM, cartridge.ram.name); + if(auto name = system.eeprom.name()) interface->saveRequest(ID::SystemEEPROM, name); + if(auto name = cartridge.ram.name) interface->saveRequest(ID::RAM, name); + if(auto name = cartridge.eeprom.name()) interface->saveRequest(ID::EEPROM, name); } auto Interface::load(uint id, const stream& stream) -> void { @@ -114,6 +119,10 @@ auto Interface::load(uint id, const stream& stream) -> void { system.information.manifest = stream.text(); } + if(id == ID::SystemEEPROM) { + stream.read((uint8_t*)system.eeprom.data(), min(system.eeprom.size() * sizeof(uint16), stream.size())); + } + if(id == ID::Manifest) { cartridge.information.manifest = stream.text(); } @@ -125,12 +134,24 @@ auto Interface::load(uint id, const stream& stream) -> void { if(id == ID::RAM) { stream.read((uint8_t*)cartridge.ram.data, min(cartridge.ram.size, stream.size())); } + + if(id == ID::EEPROM) { + stream.read((uint8_t*)cartridge.eeprom.data(), min(cartridge.eeprom.size() * sizeof(uint16), stream.size())); + } } auto Interface::save(uint id, const stream& stream) -> void { + if(id == ID::SystemEEPROM) { + stream.write((uint8_t*)system.eeprom.data(), system.eeprom.size() * sizeof(uint16)); + } + if(id == ID::RAM) { stream.write((uint8_t*)cartridge.ram.data, cartridge.ram.size); } + + if(id == ID::EEPROM) { + stream.write((uint8_t*)cartridge.eeprom.data(), cartridge.eeprom.size() * sizeof(uint16)); + } } auto Interface::unload() -> void { diff --git a/higan/ws/interface/interface.hpp b/higan/ws/interface/interface.hpp index 35da5edd..31e043ad 100644 --- a/higan/ws/interface/interface.hpp +++ b/higan/ws/interface/interface.hpp @@ -9,10 +9,13 @@ struct ID { enum : uint { SystemManifest, + SystemIPLROM, + SystemEEPROM, Manifest, ROM, RAM, + EEPROM, }; enum : uint { diff --git a/higan/ws/ppu/io.cpp b/higan/ws/ppu/io.cpp index fa9e0480..a0262e21 100644 --- a/higan/ws/ppu/io.cpp +++ b/higan/ws/ppu/io.cpp @@ -14,6 +14,12 @@ auto PPU::portRead(uint16 addr) -> uint8 { //BACK_COLOR if(addr == 0x0001) return r.backColorPalette << 4 | r.backColorIndex << 0; + //LINE_CUR + if(addr == 0x0002) return status.vclk; + + //LINE_CMP + if(addr == 0x0003) return r.lineCompare; + //SPR_BASE if(addr == 0x0004) return r.spriteBase; @@ -26,6 +32,30 @@ auto PPU::portRead(uint16 addr) -> uint8 { //MAP_BASE if(addr == 0x0007) return r.screenTwoMapBase << 4 | r.screenOneMapBase << 0; + //SCR2_WIN_X0 + if(addr == 0x0008) return r.screenTwoWindowX0; + + //SCR2_WIN_Y0 + if(addr == 0x0009) return r.screenTwoWindowY0; + + //SCR2_WIN_X1 + if(addr == 0x000a) return r.screenTwoWindowX1; + + //SCR2_WIN_Y1 + if(addr == 0x000b) return r.screenTwoWindowY1; + + //SPR_WIN_X0 + if(addr == 0x000c) return r.spriteWindowX0; + + //SPR_WIN_Y0 + if(addr == 0x000d) return r.spriteWindowY0; + + //SPR_WIN_X1 + if(addr == 0x000e) return r.spriteWindowX1; + + //SPR_WIN_Y1 + if(addr == 0x000f) return r.spriteWindowY1; + //SCR1_X if(addr == 0x0010) return r.scrollOneX; @@ -53,17 +83,27 @@ auto PPU::portRead(uint16 addr) -> uint8 { ); } - //PALMONO_POOL_0 - if(addr == 0x001c) return r.monoPool[0] << 0 | r.monoPool[1] << 4; + //LCD_VTOTAL + if(addr == 0x0016) return r.vtotal; - //PALMONO_POOL_1 - if(addr == 0x001d) return r.monoPool[2] << 0 | r.monoPool[3] << 4; + //LCD_VBLANK + if(addr == 0x0017) return r.vblank; - //PALMONO_POOL_2 - if(addr == 0x001e) return r.monoPool[4] << 0 | r.monoPool[5] << 4; + //PALMONO_POOL + if(addr >= 0x001c && addr <= 0x001f) { + return ( + r.pool[addr.bits(1,0) * 2 + 1] << 4 + | r.pool[addr.bits(1,0) * 2 + 0] << 0 + ); + } - //PALMONO_POOL_3 - if(addr == 0x001f) return r.monoPool[6] << 0 | r.monoPool[7] << 4; + //PALMONO + if(addr >= 0x0020 && addr <= 0x003f) { + return ( + r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 1] << 4 + | r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 0] << 0 + ); + } //DISP_MODE if(addr == 0x0060) { @@ -81,12 +121,12 @@ auto PPU::portRead(uint16 addr) -> uint8 { auto PPU::portWrite(uint16 addr, uint8 data) -> void { //DISP_CTRL if(addr == 0x0000) { - r.screenTwoWindowEnable = data & 0x20; - r.screenTwoWindowMode = data & 0x10; - r.spriteWindowEnable = data & 0x08; - r.spriteEnable = data & 0x04; - r.screenTwoEnable = data & 0x02; - r.screenOneEnable = data & 0x01; + r.screenTwoWindowEnable = data.bit(5); + r.screenTwoWindowMode = data.bit(4); + r.spriteWindowEnable = data.bit(3); + r.spriteEnable = data.bit(2); + r.screenTwoEnable = data.bit(1); + r.screenOneEnable = data.bit(0); return; } @@ -94,48 +134,102 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { if(addr == 0x0001) { if(WS()) { r.backColorPalette = 0; - r.backColorIndex = (uint3)(data >> 0); + r.backColorIndex = data.bits(2,0); } else { - r.backColorPalette = (uint4)(data >> 4); - r.backColorIndex = (uint4)(data >> 0); + r.backColorPalette = data.bits(7,4); + r.backColorIndex = data.bits(3,0); } return; } + //LINE_CMP + if(addr == 0x0003) { + r.lineCompare = data; + return; + } + //SPR_BASE if(addr == 0x0004) { if(WS()) { - r.spriteBase = (uint5)data; + r.spriteBase = data.bits(4,0); } else { - r.spriteBase = (uint6)data; + r.spriteBase = data.bits(5,0); } return; } //SPR_FIRST if(addr == 0x0005) { - r.spriteFirst = data; + r.spriteFirst = data.bits(6,0); return; } //SPR_COUNT if(addr == 0x0006) { - r.spriteCount = data; + r.spriteCount = data; //0 - 128 return; } //MAP_BASE if(addr == 0x0007) { if(WS()) { - r.screenTwoMapBase = (uint3)(data >> 4); - r.screenOneMapBase = (uint3)(data >> 0); + r.screenTwoMapBase = data.bits(6,4); + r.screenOneMapBase = data.bits(2,0); } else { - r.screenTwoMapBase = (uint4)(data >> 4); - r.screenOneMapBase = (uint4)(data >> 0); + r.screenTwoMapBase = data.bits(7,4); + r.screenOneMapBase = data.bits(3,0); } return; } + //SCR2_WIN_X0 + if(addr == 0x0008) { + r.screenTwoWindowX0 = data; + return; + } + + //SCR2_WIN_Y0 + if(addr == 0x0009) { + r.screenTwoWindowY0 = data; + return; + } + + //SCR2_WIN_X1 + if(addr == 0x000a) { + r.screenTwoWindowX1 = data; + return; + } + + //SCR2_WIN_Y1 + if(addr == 0x000b) { + r.screenTwoWindowY1 = data; + return; + } + + //SPR_WIN_X0 + if(addr == 0x000c) { + r.spriteWindowX0 = data; + return; + } + + //SPR_WIN_Y0 + if(addr == 0x000d) { + r.spriteWindowY0 = data; + return; + } + + //SPR_WIN_X1 + if(addr == 0x000e) { + r.spriteWindowX1 = data; + return; + } + + //SPR_WIN_Y1 + if(addr == 0x000f) { + r.spriteWindowY1 = data; + return; + } + //SCR1_X if(addr == 0x0010) { r.scrollOneX = data; @@ -168,48 +262,46 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void { //LCD_ICON if(addr == 0x0015) { - r.iconAux3 = data & 0x20; - r.iconAux2 = data & 0x10; - r.iconAux1 = data & 0x08; - r.iconHorizontal = data & 0x04; - r.iconVertical = data & 0x02; - r.iconSleep = data & 0x01; + r.iconAux3 = data.bit(5); + r.iconAux2 = data.bit(4); + r.iconAux1 = data.bit(3); + r.iconHorizontal = data.bit(2); + r.iconVertical = data.bit(1); + r.iconSleep = data.bit(0); return; } - //PALMONO_POOL_0 - if(addr == 0x001c) { - r.monoPool[0] = data >> 0; - r.monoPool[1] = data >> 4; + //LCD_VTOTAL + if(addr == 0x0016) { + r.vtotal = data; return; } - //PALMONO_POOL_1 - if(addr == 0x001d) { - r.monoPool[2] = data >> 0; - r.monoPool[3] = data >> 4; + //LCD_VBLANK + if(addr == 0x0017) { + r.vblank = data; return; } - //PALMONO_POOL_2 - if(addr == 0x001e) { - r.monoPool[4] = data >> 0; - r.monoPool[5] = data >> 4; + //PALMONO_POOL + if(addr >= 0x001c && addr <= 0x001f) { + r.pool[addr.bits(1,0) * 2 + 1] = data.bits(7,4); + r.pool[addr.bits(1,0) * 2 + 0] = data.bits(3,0); return; } - //PALMONO_POOL_3 - if(addr == 0x001f) { - r.monoPool[6] = data >> 0; - r.monoPool[7] = data >> 4; + //PALMONO + if(addr >= 0x0020 && addr <= 0x003f) { + r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 1] = data.bits(6,4); + r.palette[addr.bits(3,1)].color[addr.bit(0) * 2 + 0] = data.bits(2,0); return; } //DISP_MODE if(addr == 0x0060) { - r.bpp = data & 0x80; - r.color = data & 0x40; - r.format = data & 0x20; + r.bpp = data.bit(7); + r.color = data.bit(6); + r.format = data.bit(5); r.u0060 = data & 0b1011; return; } diff --git a/higan/ws/ppu/ppu.cpp b/higan/ws/ppu/ppu.cpp index 684befeb..7c92aad7 100644 --- a/higan/ws/ppu/ppu.cpp +++ b/higan/ws/ppu/ppu.cpp @@ -18,6 +18,9 @@ auto PPU::main() -> void { auto PPU::scanline() -> void { status.hclk = 0; status.vclk++; + if(status.vclk == r.lineCompare) { + cpu.raise(CPU::Interrupt::LineCompare); + } if(status.vclk == 144) { cpu.raise(CPU::Interrupt::Vblank); } @@ -40,10 +43,8 @@ auto PPU::step(uint clocks) -> void { auto PPU::power() -> void { create(PPU::Enter, 3'072'000); - for(uint n = 0x0000; n <= 0x0001; n++) iomap[n] = this; - for(uint n = 0x0004; n <= 0x0007; n++) iomap[n] = this; - for(uint n = 0x0010; n <= 0x0015; n++) iomap[n] = this; - for(uint n = 0x001c; n <= 0x001f; n++) iomap[n] = this; + for(uint n = 0x0000; n <= 0x0017; n++) iomap[n] = this; + for(uint n = 0x001c; n <= 0x003f; n++) iomap[n] = this; iomap[0x0060] = this; for(auto& n : output) n = 0; @@ -51,6 +52,8 @@ auto PPU::power() -> void { status.vclk = 0; status.hclk = 0; + r.lineCompare = 0xff; + video.power(); } diff --git a/higan/ws/ppu/ppu.hpp b/higan/ws/ppu/ppu.hpp index aa6bad8a..5dd77eb2 100644 --- a/higan/ws/ppu/ppu.hpp +++ b/higan/ws/ppu/ppu.hpp @@ -11,7 +11,7 @@ struct PPU : Thread, IO { auto portRead(uint16 addr) -> uint8 override; auto portWrite(uint16 addr, uint8 data) -> void override; - uint16_t output[224 * 144] = {0}; + uint16 output[224 * 144]; struct Status { uint vclk; @@ -31,6 +31,9 @@ struct PPU : Thread, IO { uint4 backColorIndex; uint4 backColorPalette; + //$0003 LINE_CMP + uint8 lineCompare; + //$0004 SPR_BASE uint6 spriteBase; @@ -44,6 +47,30 @@ struct PPU : Thread, IO { uint4 screenTwoMapBase; uint4 screenOneMapBase; + //$0008 SCR2_WIN_X0 + uint8 screenTwoWindowX0; + + //$0009 SCR2_WIN_Y0 + uint8 screenTwoWindowY0; + + //$000a SCR2_WIN_X1 + uint8 screenTwoWindowX1; + + //$000b SCR2_WIN_Y1 + uint8 screenTwoWindowY1; + + //$000c SPR_WIN_X0 + uint8 spriteWindowX0; + + //$000d SPR_WIN_Y0 + uint8 spriteWindowY0; + + //$000e SPR_WIN_X1 + uint8 spriteWindowX1; + + //$000f SPR_WIN_Y1 + uint8 spriteWindowY1; + //$0010 SCR1_X uint8 scrollOneX; @@ -67,8 +94,19 @@ struct PPU : Thread, IO { bool iconVertical; bool iconSleep; + //$0016 LCD_VTOTAL + uint8 vtotal; + + //$0017 LCD_VBLANK + uint8 vblank; + //$001c-001f PALMONO_POOL - uint4 monoPool[8]; + uint4 pool[8]; + + //$0020-003f PALMONO + struct Palette { + uint3 color[4]; + } palette[16]; //$0060 DISP_MODE bool bpp; diff --git a/higan/ws/system/io.cpp b/higan/ws/system/io.cpp new file mode 100644 index 00000000..a7010750 --- /dev/null +++ b/higan/ws/system/io.cpp @@ -0,0 +1,25 @@ +auto System::portRead(uint16 addr) -> uint8 { + //IEEP_DATA + if(addr == 0x00ba) return eeprom.read(EEPROM::DataLo); + if(addr == 0x00bb) return eeprom.read(EEPROM::DataHi); + + //IEEP_ADDR + if(addr == 0x00bc) return eeprom.read(EEPROM::AddressLo); + if(addr == 0x00bd) return eeprom.read(EEPROM::AddressHi); + + //IEEP_CMD + if(addr == 0x00be) return eeprom.read(EEPROM::Status); +} + +auto System::portWrite(uint16 addr, uint8 data) -> void { + //IEEP_DATA + if(addr == 0x00ba) return eeprom.write(EEPROM::DataLo, data); + if(addr == 0x00bb) return eeprom.write(EEPROM::DataHi, data); + + //IEEP_ADDR + if(addr == 0x00bc) return eeprom.write(EEPROM::AddressLo, data); + if(addr == 0x00bd) return eeprom.write(EEPROM::AddressHi, data); + + //IEEP_CMD + if(addr == 0x00be) return eeprom.write(EEPROM::Command, data); +} diff --git a/higan/ws/system/system.cpp b/higan/ws/system/system.cpp index 6bdb5f19..84194c73 100644 --- a/higan/ws/system/system.cpp +++ b/higan/ws/system/system.cpp @@ -3,6 +3,7 @@ namespace WonderSwan { System system; +#include "io.cpp" auto System::loaded() const -> bool { return _loaded; } auto System::revision() const -> Revision { return _revision; } @@ -21,23 +22,37 @@ auto System::load(Revision revision) -> void { //note: IPLROM is currently undumped; otherwise we'd load it here ... + if(auto node = document["system/eeprom"]) { + eeprom.setName(node["name"].text()); + eeprom.setSize(node["size"].natural() / sizeof(uint16)); + eeprom.erase(); + interface->loadRequest(ID::SystemEEPROM, eeprom.name(), false); + } + cartridge.load(); _loaded = true; } auto System::unload() -> void { if(!loaded()) return; + + eeprom.setName(""); + eeprom.setSize(0); + cartridge.unload(); _loaded = false; } auto System::power() -> void { IO::power(); + eeprom.power(); cpu.power(); ppu.power(); apu.power(); cartridge.power(); scheduler.power(); + + for(uint n = 0x00ba; n <= 0x00be; n++) iomap[n] = this; } auto System::run() -> void { diff --git a/higan/ws/system/system.hpp b/higan/ws/system/system.hpp index 6e07ba2b..b7a8fe2a 100644 --- a/higan/ws/system/system.hpp +++ b/higan/ws/system/system.hpp @@ -1,4 +1,4 @@ -struct System { +struct System : IO { enum class Revision : uint { WonderSwan, //SW-001 (ASWAN) WonderSwanColor, //WSC-001 (SPHINX) @@ -15,10 +15,15 @@ struct System { auto power() -> void; auto run() -> void; + auto portRead(uint16 addr) -> uint8 override; + auto portWrite(uint16 addr, uint8 data) -> void override; + struct Information { string manifest; } information; + EEPROM eeprom; + privileged: bool _loaded = false; Revision _revision = Revision::WonderSwan; diff --git a/higan/ws/ws.hpp b/higan/ws/ws.hpp index 27b7b8b2..166536a5 100644 --- a/higan/ws/ws.hpp +++ b/higan/ws/ws.hpp @@ -42,9 +42,10 @@ namespace WonderSwan { int64 clock = 0; }; + #include + #include #include #include - #include #include #include #include