Update to v097r16 release.

byuu says:

Changelog:
- sfc/ppu/sprite updated to use new .bit(s) functions; masked sizes
  better; added valid flags instead of using magic numbers
- ws/ppu updates to use new .bit(s) functions
- ws/ppu: added line compare interrupt support
- added ws/eeprom; emulation of WS/WSC internal EEPROM and cartridge
  EEPROM (1kbit - 16kbit supported)
- added basic read/write handlers for remaining WS/WSC PPU registers

WS EEPROM emulation is basically a direct copy of trap15's code. Still
some unknown areas in there, but hopefully it's enough to get further
into games that depend on EEPROM support. Note that you'll have to
manually add the eeprom line to the manifest for now, as icarus doesn't
know how to detect EEPROM/sizes yet.

I figured the changes to the SNES PPU sprites would slow it down a tad,
but it actually sped it up. Most of the impact from the integer classes
are gone now.
This commit is contained in:
Tim Allen 2016-02-18 21:32:22 +11:00
parent 4b29f4bad7
commit 810cbdafb4
25 changed files with 656 additions and 221 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
higan/profile/WonderSwan.sys/internal.rom
higan/profile/WonderSwan Color.sys/internal.rom

View File

@ -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
}

View File

@ -1 +1,2 @@
system name:WonderSwan Color
eeprom name=internal.rom size=2048

View File

@ -1 +1,2 @@
system name:WonderSwan
eeprom name=internal.rom size=128

View File

@ -62,12 +62,12 @@ privileged:
friend class Video;
struct Debugger {
hook<void (uint16, uint8)> vram_read;
hook<void (uint16, uint8)> oam_read;
hook<void (uint16, uint8)> cgram_read;
hook<void (uint16, uint8)> vram_write;
hook<void (uint16, uint8)> oam_write;
hook<void (uint16, uint8)> cgram_write;
hook<auto (uint16, uint8) -> void> vram_read;
hook<auto (uint16, uint8) -> void> oam_read;
hook<auto (uint16, uint8) -> void> cgram_read;
hook<auto (uint16, uint8) -> void> vram_write;
hook<auto (uint16, uint8) -> void> oam_write;
hook<auto (uint16, uint8) -> void> cgram_write;
} debugger;
};

View File

@ -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);
}
}

View File

@ -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};

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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/)

View File

@ -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;

View File

@ -26,6 +26,8 @@ struct Cartridge : IO {
string name;
} rom, ram;
EEPROM eeprom;
struct Information {
string manifest;
string title;

63
higan/ws/cartridge/io.cpp Normal file
View File

@ -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);
}

View File

@ -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;
}
}

135
higan/ws/eeprom/eeprom.cpp Normal file
View File

@ -0,0 +1,135 @@
#include <ws/ws.hpp>
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;
}
}
}

View File

@ -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;
};

View File

@ -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 {

View File

@ -9,10 +9,13 @@ struct ID {
enum : uint {
SystemManifest,
SystemIPLROM,
SystemEEPROM,
Manifest,
ROM,
RAM,
EEPROM,
};
enum : uint {

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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;

25
higan/ws/system/io.cpp Normal file
View File

@ -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);
}

View File

@ -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 {

View File

@ -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;

View File

@ -42,9 +42,10 @@ namespace WonderSwan {
int64 clock = 0;
};
#include <ws/memory/memory.hpp>
#include <ws/eeprom/eeprom.hpp>
#include <ws/system/system.hpp>
#include <ws/scheduler/scheduler.hpp>
#include <ws/memory/memory.hpp>
#include <ws/cartridge/cartridge.hpp>
#include <ws/cpu/cpu.hpp>
#include <ws/ppu/ppu.hpp>