mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
4b29f4bad7
commit
810cbdafb4
|
@ -0,0 +1,2 @@
|
|||
higan/profile/WonderSwan.sys/internal.rom
|
||||
higan/profile/WonderSwan Color.sys/internal.rom
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
system name:WonderSwan Color
|
||||
eeprom name=internal.rom size=2048
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
system name:WonderSwan
|
||||
eeprom name=internal.rom size=128
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -26,6 +26,8 @@ struct Cartridge : IO {
|
|||
string name;
|
||||
} rom, ram;
|
||||
|
||||
EEPROM eeprom;
|
||||
|
||||
struct Information {
|
||||
string manifest;
|
||||
string title;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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 {
|
||||
|
|
|
@ -9,10 +9,13 @@ struct ID {
|
|||
|
||||
enum : uint {
|
||||
SystemManifest,
|
||||
SystemIPLROM,
|
||||
SystemEEPROM,
|
||||
|
||||
Manifest,
|
||||
ROM,
|
||||
RAM,
|
||||
EEPROM,
|
||||
};
|
||||
|
||||
enum : uint {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue