mirror of https://github.com/bsnes-emu/bsnes.git
Update to v097r24 release.
byuu says: Changelog: - WS: fixed bug when IRQs triggered during a rep string instruction - WS: added sprite attribute caching (per-scanline); absolutely massive speed-up - WS: emulated limit of 32 sprites per scanline - WS: emulated the extended PPU register bit behavior based on the DISP_CTRL tile bit-depth setting - WS: added "Rotate" key binding; can be used to flip the WS display between horizontal and vertical in real-time The prefix emulation may not be 100% hardware-accurate, but the edge cases should be extreme enough to not come up in the WS library. No way to get the emulation 100% down without intensive hardware testing. trap15 pointed me at a workflow diagram for it, but that diagram is impossible without a magic internal stack frame that grows with every IRQ, and can thus grow infinitely large. The rotation thing isn't exactly the most friendly set-up, but oh well. I'll see about adding a default rotation setting to manifests, so that games like GunPey can start in the correct orientation. After that, if the LCD orientation icon turns out to be reliable, then I'll start using that. But if there are cases where it's not reliable, then I'll leave it to manual button presses. Speaking of icons, I'll need a set of icons to render on the screen. Going to put them to the top right on vertical orientation, and on the bottom left for horizontal orientation. Just outside of the video output, of course. Overall, WS is getting pretty far along, but still some major bugs in various games. I really need sound emulation, though. Nobody's going to use this at all without that.
This commit is contained in:
parent
79e7e6ab9e
commit
c33065fbd1
|
@ -6,7 +6,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "097.23";
|
||||
static const string Version = "097.24";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
//36 ss:
|
||||
//3e ds:
|
||||
auto V30MZ::opSegment(uint16 segment) {
|
||||
prefix.segment = segment;
|
||||
if(prefixes.size() >= 7) prefixes.removeLast();
|
||||
prefixes.prepend(opcode);
|
||||
state.prefix = true;
|
||||
state.poll = false;
|
||||
}
|
||||
|
@ -11,15 +12,17 @@ auto V30MZ::opSegment(uint16 segment) {
|
|||
//f2 repnz:
|
||||
//f3 repz:
|
||||
auto V30MZ::opRepeat(bool flag) {
|
||||
if(prefixes.size() >= 7) prefixes.removeLast();
|
||||
prefixes.prepend(opcode);
|
||||
wait(4);
|
||||
if(r.cx == 0) return;
|
||||
prefix.repeat = flag;
|
||||
state.prefix = true;
|
||||
state.poll = false;
|
||||
}
|
||||
|
||||
//f0 lock:
|
||||
auto V30MZ::opLock() {
|
||||
if(prefixes.size() >= 7) prefixes.removeLast();
|
||||
prefixes.prepend(opcode);
|
||||
state.prefix = true;
|
||||
state.poll = false;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
auto V30MZ::opInString(Size size) {
|
||||
wait(5);
|
||||
if(!repeat() || r.cx) {
|
||||
auto data = in(size, r.dx);
|
||||
write(size, r.es, r.di, data);
|
||||
r.di += r.f.d ? -size : size;
|
||||
|
||||
if(prefix.repeat && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
@ -12,11 +14,13 @@ auto V30MZ::opInString(Size size) {
|
|||
|
||||
auto V30MZ::opOutString(Size size) {
|
||||
wait(6);
|
||||
if(!repeat() || r.cx) {
|
||||
auto data = read(size, segment(r.ds), r.si);
|
||||
out(size, r.dx, data);
|
||||
r.si += r.f.d ? -size : size;
|
||||
|
||||
if(prefix.repeat && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
@ -24,12 +28,14 @@ auto V30MZ::opOutString(Size size) {
|
|||
|
||||
auto V30MZ::opMoveString(Size size) {
|
||||
wait(4);
|
||||
if(!repeat() || r.cx) {
|
||||
auto data = read(size, segment(r.ds), r.si);
|
||||
write(size, r.es, r.di, data);
|
||||
r.si += r.f.d ? -size : size;
|
||||
r.di += r.f.d ? -size : size;
|
||||
|
||||
if(prefix.repeat && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
@ -37,13 +43,17 @@ auto V30MZ::opMoveString(Size size) {
|
|||
|
||||
auto V30MZ::opCompareString(Size size) {
|
||||
wait(5);
|
||||
if(!repeat() || r.cx) {
|
||||
auto x = read(size, segment(r.ds), r.si);
|
||||
auto y = read(size, r.es, r.di);
|
||||
r.si += r.f.d ? -size : size;
|
||||
r.di += r.f.d ? -size : size;
|
||||
alSub(size, x, y);
|
||||
|
||||
if(prefix.repeat && prefix.repeat() == r.f.z && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
if(repeat() == RepeatWhileZero && r.f.z == 1) return;
|
||||
if(repeat() == RepeatWhileNotZero && r.f.z == 0) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
@ -51,10 +61,12 @@ auto V30MZ::opCompareString(Size size) {
|
|||
|
||||
auto V30MZ::opStoreString(Size size) {
|
||||
wait(2);
|
||||
if(!repeat() || r.cx) {
|
||||
write(size, r.es, r.di, getAcc(size));
|
||||
r.di += r.f.d ? -size : size;
|
||||
|
||||
if(prefix.repeat && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
@ -64,10 +76,12 @@ auto V30MZ::opStoreString(Size size) {
|
|||
//ad lodsw
|
||||
auto V30MZ::opLoadString(Size size) {
|
||||
wait(2);
|
||||
if(!repeat() || r.cx) {
|
||||
setAcc(size, read(size, segment(r.ds), r.si));
|
||||
r.si += r.f.d ? -size : size;
|
||||
|
||||
if(prefix.repeat && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
@ -77,12 +91,16 @@ auto V30MZ::opLoadString(Size size) {
|
|||
//af scasw
|
||||
auto V30MZ::opScanString(Size size) {
|
||||
wait(3);
|
||||
if(!repeat() || r.cx) {
|
||||
auto x = getAcc(size);
|
||||
auto y = read(size, r.es, r.di);
|
||||
r.di += r.f.d ? -size : size;
|
||||
alSub(size, x, y);
|
||||
|
||||
if(prefix.repeat && prefix.repeat() == r.f.z && --r.cx) {
|
||||
if(!repeat() || !--r.cx) return;
|
||||
if(repeat() == RepeatWhileZero && r.f.z == 1) return;
|
||||
if(repeat() == RepeatWhileNotZero && r.f.z == 0) return;
|
||||
|
||||
state.prefix = true;
|
||||
r.ip--;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
auto V30MZ::repeat() -> uint8 {
|
||||
for(auto prefix : prefixes) {
|
||||
if(prefix == RepeatWhileZero) return prefix;
|
||||
if(prefix == RepeatWhileNotZero) return prefix;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto V30MZ::segment(uint16 segment) -> uint16 {
|
||||
if(prefix.segment) return prefix.segment();
|
||||
for(auto prefix : prefixes) {
|
||||
if(prefix == SegmentOverrideES) return r.es;
|
||||
if(prefix == SegmentOverrideCS) return r.cs;
|
||||
if(prefix == SegmentOverrideSS) return r.ss;
|
||||
if(prefix == SegmentOverrideDS) return r.ds;
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,28 +21,67 @@ auto V30MZ::debug(string text) -> void {
|
|||
print(text, "\n");
|
||||
}
|
||||
|
||||
auto V30MZ::power() -> void {
|
||||
state.halt = false;
|
||||
state.poll = true;
|
||||
state.prefix = false;
|
||||
prefixes.reset();
|
||||
|
||||
r.ax = 0x0000;
|
||||
r.cx = 0x0000;
|
||||
r.dx = 0x0000;
|
||||
r.bx = 0x0000;
|
||||
r.sp = 0x0000;
|
||||
r.bp = 0x0000;
|
||||
r.si = 0x0000;
|
||||
r.di = 0x0000;
|
||||
r.es = 0x0000;
|
||||
r.cs = 0xffff;
|
||||
r.ss = 0x0000;
|
||||
r.ds = 0x0000;
|
||||
r.ip = 0x0000;
|
||||
r.f = 0x8000;
|
||||
}
|
||||
|
||||
auto V30MZ::exec() -> void {
|
||||
state.poll = true;
|
||||
state.prefix = false;
|
||||
if(state.halt) return wait(1);
|
||||
|
||||
#if 0
|
||||
static uint counter = 0;
|
||||
static uint16 cs = 0, ip = 0;
|
||||
if(cs != r.cs || ip != r.ip) print(disassemble(cs = r.cs, ip = r.ip), "\n");
|
||||
#endif
|
||||
|
||||
instruction();
|
||||
if(!state.prefix) prefixes.reset();
|
||||
}
|
||||
|
||||
if(state.prefix) {
|
||||
auto V30MZ::interrupt(uint8 vector) -> void {
|
||||
state.halt = false;
|
||||
state.poll = true;
|
||||
state.prefix = false;
|
||||
} else {
|
||||
prefix.repeat = nothing;
|
||||
prefix.segment = nothing;
|
||||
|
||||
//if an IRQ fires during a rep string instruction;
|
||||
//flush prefix queue and seek back to first prefix.
|
||||
//this allows the transfer to resume after the IRQ.
|
||||
if(prefixes) {
|
||||
r.ip -= prefixes.size();
|
||||
prefixes.reset();
|
||||
}
|
||||
|
||||
auto ip = read(Word, 0x0000, vector * 4 + 0);
|
||||
auto cs = read(Word, 0x0000, vector * 4 + 2);
|
||||
|
||||
push(r.f);
|
||||
push(r.cs);
|
||||
push(r.ip);
|
||||
|
||||
r.f.m = true;
|
||||
r.f.i = false;
|
||||
r.f.b = false;
|
||||
|
||||
r.ip = ip;
|
||||
r.cs = cs;
|
||||
}
|
||||
|
||||
auto V30MZ::instruction() -> void {
|
||||
auto opcode = fetch();
|
||||
opcode = fetch();
|
||||
wait(1);
|
||||
|
||||
switch(opcode) {
|
||||
|
@ -305,46 +344,4 @@ auto V30MZ::instruction() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto V30MZ::interrupt(uint8 vector) -> void {
|
||||
state.halt = false;
|
||||
|
||||
auto ip = read(Word, 0x0000, vector * 4 + 0);
|
||||
auto cs = read(Word, 0x0000, vector * 4 + 2);
|
||||
|
||||
push(r.f);
|
||||
push(r.cs);
|
||||
push(r.ip);
|
||||
|
||||
r.f.m = true;
|
||||
r.f.i = false;
|
||||
r.f.b = false;
|
||||
|
||||
r.ip = ip;
|
||||
r.cs = cs;
|
||||
}
|
||||
|
||||
auto V30MZ::power() -> void {
|
||||
state.halt = false;
|
||||
state.poll = true;
|
||||
state.prefix = false;
|
||||
|
||||
prefix.repeat = nothing;
|
||||
prefix.segment = nothing;
|
||||
|
||||
r.ax = 0x0000;
|
||||
r.cx = 0x0000;
|
||||
r.dx = 0x0000;
|
||||
r.bx = 0x0000;
|
||||
r.sp = 0x0000;
|
||||
r.bp = 0x0000;
|
||||
r.si = 0x0000;
|
||||
r.di = 0x0000;
|
||||
r.es = 0x0000;
|
||||
r.cs = 0xffff;
|
||||
r.ss = 0x0000;
|
||||
r.ds = 0x0000;
|
||||
r.ip = 0x0000;
|
||||
r.f = 0x8000;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,15 @@ namespace Processor {
|
|||
struct V30MZ {
|
||||
using Size = uint;
|
||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||
enum : uint {
|
||||
SegmentOverrideES = 0x26,
|
||||
SegmentOverrideCS = 0x2e,
|
||||
SegmentOverrideSS = 0x36,
|
||||
SegmentOverrideDS = 0x3e,
|
||||
Lock = 0xf0,
|
||||
RepeatWhileNotZero = 0xf2,
|
||||
RepeatWhileZero = 0xf3,
|
||||
};
|
||||
|
||||
virtual auto wait(uint clocks = 1) -> void = 0;
|
||||
virtual auto read(uint20 addr) -> uint8 = 0;
|
||||
|
@ -15,12 +24,13 @@ struct V30MZ {
|
|||
virtual auto out(uint16 port, uint8 data) -> void = 0;
|
||||
|
||||
auto debug(string text) -> void;
|
||||
auto exec() -> void;
|
||||
auto instruction() -> void;
|
||||
auto interrupt(uint8 vector) -> void;
|
||||
auto power() -> void;
|
||||
auto exec() -> void;
|
||||
auto interrupt(uint8 vector) -> void;
|
||||
auto instruction() -> void;
|
||||
|
||||
//registers.cpp
|
||||
auto repeat() -> uint8;
|
||||
auto segment(uint16) -> uint16;
|
||||
|
||||
auto getAcc(Size) -> uint32;
|
||||
|
@ -202,10 +212,8 @@ struct V30MZ {
|
|||
bool prefix; //set to true for prefix instructions; prevents flushing of Prefix struct
|
||||
} state;
|
||||
|
||||
struct Prefix {
|
||||
maybe<bool> repeat; //repnz, repz
|
||||
maybe<uint16> segment; //cs, es, ss, ds
|
||||
} prefix;
|
||||
uint8 opcode;
|
||||
vector<uint8> prefixes;
|
||||
|
||||
struct ModRM {
|
||||
uint2 mod;
|
||||
|
|
|
@ -55,7 +55,7 @@ auto CPU::power() -> void {
|
|||
iomap[0x00b5] = this;
|
||||
iomap[0x00b6] = this;
|
||||
|
||||
if(WSC() || SC()) {
|
||||
if(system.model() != Model::WonderSwan) {
|
||||
for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this;
|
||||
iomap[0x0062] = this;
|
||||
}
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
auto CPU::keypadRead() -> uint4 {
|
||||
uint1 orientation = 0;
|
||||
uint4 data = 0;
|
||||
|
||||
if(r.ypadEnable) {
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y1) << 0;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y2) << 1;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y3) << 2;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::Y4) << 3;
|
||||
data |= system.keypad.y1 << 0;
|
||||
data |= system.keypad.y2 << 1;
|
||||
data |= system.keypad.y3 << 2;
|
||||
data |= system.keypad.y4 << 3;
|
||||
}
|
||||
|
||||
if(r.xpadEnable) {
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::X1) << 0;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::X2) << 1;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::X3) << 2;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::X4) << 3;
|
||||
data |= system.keypad.x1 << 0;
|
||||
data |= system.keypad.x2 << 1;
|
||||
data |= system.keypad.x3 << 2;
|
||||
data |= system.keypad.x4 << 3;
|
||||
}
|
||||
|
||||
if(r.buttonEnable) {
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::Start) << 1;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::A) << 2;
|
||||
data |= interface->inputPoll(orientation, 0, (uint)Keypad::B) << 3;
|
||||
data |= system.keypad.start << 1;
|
||||
data |= system.keypad.a << 2;
|
||||
data |= system.keypad.b << 3;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
@ -44,22 +43,23 @@ auto CPU::portRead(uint16 addr) -> uint8 {
|
|||
|
||||
//WSC_SYSTEM
|
||||
if(addr == 0x0062) {
|
||||
return SC() << 7;
|
||||
return (system.model() == Model::SwanCrystal) << 7;
|
||||
}
|
||||
|
||||
//HW_FLAGS
|
||||
if(addr == 0x00a0) {
|
||||
bool model = system.model() != Model::WonderSwan;
|
||||
return (
|
||||
1 << 7 //1 = built-in self-test passed
|
||||
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
|
||||
| !WS() << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal
|
||||
| model << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal
|
||||
| 1 << 0 //0 = BIOS mapped; 1 = cartridge mapped
|
||||
);
|
||||
}
|
||||
|
||||
//INT_BASE
|
||||
if(addr == 0x00b0) {
|
||||
if(WS()) return r.interruptBase | 3;
|
||||
if(system.model() == Model::WonderSwan) return r.interruptBase | 3;
|
||||
return r.interruptBase;
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ auto CPU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
|
||||
//INT_BASE
|
||||
if(addr == 0x00b0) {
|
||||
r.interruptBase = WS() ? data & ~7 : data & ~1;
|
||||
r.interruptBase = (system.model() == Model::WonderSwan) ? data & ~7 : data & ~1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ Interface::Interface() {
|
|||
device.input.append({ 8, 0, "B"});
|
||||
device.input.append({ 9, 0, "A"});
|
||||
device.input.append({10, 0, "Start"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
device.input.append({11, 0, "Rotate"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
|
@ -50,7 +51,8 @@ Interface::Interface() {
|
|||
device.input.append({ 8, 0, "B"});
|
||||
device.input.append({ 9, 0, "A"});
|
||||
device.input.append({10, 0, "Start"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
device.input.append({11, 0, "Rotate"});
|
||||
device.order = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
|
||||
this->device.append(device);
|
||||
}
|
||||
|
||||
|
@ -93,10 +95,10 @@ auto Interface::group(uint id) -> uint {
|
|||
case ID::RAM:
|
||||
case ID::EEPROM:
|
||||
switch(system.model()) {
|
||||
case System::Model::WonderSwan:
|
||||
case Model::WonderSwan:
|
||||
return ID::WonderSwan;
|
||||
case System::Model::WonderSwanColor:
|
||||
case System::Model::SwanCrystal:
|
||||
case Model::WonderSwanColor:
|
||||
case Model::SwanCrystal:
|
||||
return ID::WonderSwanColor;
|
||||
}
|
||||
}
|
||||
|
@ -104,8 +106,8 @@ auto Interface::group(uint id) -> uint {
|
|||
}
|
||||
|
||||
auto Interface::load(uint id) -> void {
|
||||
if(id == ID::WonderSwan) system.load(System::Model::WonderSwan);
|
||||
if(id == ID::WonderSwanColor) system.load(System::Model::WonderSwanColor);
|
||||
if(id == ID::WonderSwan) system.load(Model::WonderSwan);
|
||||
if(id == ID::WonderSwanColor) system.load(Model::WonderSwanColor);
|
||||
}
|
||||
|
||||
auto Interface::save() -> void {
|
||||
|
|
|
@ -12,7 +12,13 @@ auto PPU::portRead(uint16 addr) -> uint8 {
|
|||
}
|
||||
|
||||
//BACK_COLOR
|
||||
if(addr == 0x0001) return r.backColor;
|
||||
if(addr == 0x0001) {
|
||||
if(!system.depth()) {
|
||||
return r.backColor.bits(0,2);
|
||||
} else {
|
||||
return r.backColor.bits(0,7);
|
||||
}
|
||||
}
|
||||
|
||||
//LINE_CUR
|
||||
if(addr == 0x0002) return status.vclk;
|
||||
|
@ -21,7 +27,13 @@ auto PPU::portRead(uint16 addr) -> uint8 {
|
|||
if(addr == 0x0003) return r.lineCompare;
|
||||
|
||||
//SPR_BASE
|
||||
if(addr == 0x0004) return r.spriteBase;
|
||||
if(addr == 0x0004) {
|
||||
if(!system.depth()) {
|
||||
return r.spriteBase.bits(0,4);
|
||||
} else {
|
||||
return r.spriteBase.bits(0,5);
|
||||
}
|
||||
}
|
||||
|
||||
//SPR_FIRST
|
||||
if(addr == 0x0005) return r.spriteFirst;
|
||||
|
@ -30,7 +42,13 @@ auto PPU::portRead(uint16 addr) -> uint8 {
|
|||
if(addr == 0x0006) return r.spriteCount;
|
||||
|
||||
//MAP_BASE
|
||||
if(addr == 0x0007) return r.screenTwoMapBase << 4 | r.screenOneMapBase << 0;
|
||||
if(addr == 0x0007) {
|
||||
if(!system.depth()) {
|
||||
return r.screenTwoMapBase.bits(0,2) << 4 | r.screenOneMapBase.bits(0,2) << 0;
|
||||
} else {
|
||||
return r.screenTwoMapBase.bits(0,3) << 4 | r.screenOneMapBase.bits(0,3) << 0;
|
||||
}
|
||||
}
|
||||
|
||||
//SCR2_WIN_X0
|
||||
if(addr == 0x0008) return r.screenTwoWindowX0;
|
||||
|
@ -122,11 +140,7 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
|
||||
//BACK_COLOR
|
||||
if(addr == 0x0001) {
|
||||
if(WS()) {
|
||||
r.backColor = data.bits(0,2);
|
||||
} else {
|
||||
r.backColor = data.bits(0,7);
|
||||
}
|
||||
r.backColor = data;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -138,11 +152,7 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
|
||||
//SPR_BASE
|
||||
if(addr == 0x0004) {
|
||||
if(WS()) {
|
||||
r.spriteBase = data.bits(4,0);
|
||||
} else {
|
||||
r.spriteBase = data.bits(5,0);
|
||||
}
|
||||
r.spriteBase = data.bits(0,5);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -160,13 +170,8 @@ auto PPU::portWrite(uint16 addr, uint8 data) -> void {
|
|||
|
||||
//MAP_BASE
|
||||
if(addr == 0x0007) {
|
||||
if(WS()) {
|
||||
r.screenTwoMapBase = data.bits(6,4);
|
||||
r.screenOneMapBase = data.bits(2,0);
|
||||
} else {
|
||||
r.screenTwoMapBase = data.bits(7,4);
|
||||
r.screenOneMapBase = data.bits(3,0);
|
||||
}
|
||||
r.screenOneMapBase = data.bits(0,3);
|
||||
r.screenTwoMapBase = data.bits(4,7);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace WonderSwan {
|
|||
|
||||
PPU ppu;
|
||||
#include "io.cpp"
|
||||
#include "render-sprite.cpp"
|
||||
#include "render-mono.cpp"
|
||||
#include "render-color.cpp"
|
||||
#include "video.cpp"
|
||||
|
@ -14,6 +15,7 @@ auto PPU::Enter() -> void {
|
|||
|
||||
auto PPU::main() -> void {
|
||||
if(status.vclk < 144) {
|
||||
renderSpriteDecode();
|
||||
for(uint x = 0; x < 224; x++) {
|
||||
if(!system.color()) {
|
||||
renderMonoBack();
|
||||
|
|
|
@ -12,6 +12,9 @@ struct PPU : Thread, IO {
|
|||
auto portRead(uint16 addr) -> uint8 override;
|
||||
auto portWrite(uint16 addr, uint8 data) -> void override;
|
||||
|
||||
//render-sprite.cpp
|
||||
auto renderSpriteDecode() -> void;
|
||||
|
||||
//render-mono.cpp
|
||||
auto renderMonoFetch(uint14 offset, uint3 y, uint3 x) -> uint2;
|
||||
auto renderMonoBack() -> void;
|
||||
|
@ -34,6 +37,18 @@ struct PPU : Thread, IO {
|
|||
uint hclk;
|
||||
} status;
|
||||
|
||||
struct Sprite {
|
||||
uint8 x;
|
||||
uint8 y;
|
||||
uint1 vflip;
|
||||
uint1 hflip;
|
||||
uint1 priority;
|
||||
uint1 window;
|
||||
uint4 palette; //renderSpriteDecode() always sets bit3
|
||||
uint9 tile;
|
||||
};
|
||||
vector<Sprite> sprites;
|
||||
|
||||
struct Pixel {
|
||||
enum class Source : uint { Back, ScreenOne, ScreenTwo, Sprite };
|
||||
Source source;
|
||||
|
|
|
@ -72,33 +72,21 @@ auto PPU::renderColorScreenTwo() -> void {
|
|||
auto PPU::renderColorSprite() -> void {
|
||||
if(!r.spriteEnable) return;
|
||||
|
||||
bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1
|
||||
&& status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1;
|
||||
uint16 spriteBase = r.spriteBase << 9;
|
||||
bool windowInside = status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1;
|
||||
for(auto& sprite : sprites) {
|
||||
if(r.spriteWindowEnable && sprite.window && !windowInside) continue;
|
||||
if(status.hclk < sprite.x) continue;
|
||||
if(status.hclk > sprite.x + 7) continue;
|
||||
|
||||
uint7 spriteIndex = r.spriteFirst;
|
||||
uint8 spriteCount = min(128, (uint)r.spriteCount);
|
||||
while(spriteCount--) {
|
||||
uint32 sprite = iram.read(spriteBase + (spriteIndex++ << 2), Long);
|
||||
if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue;
|
||||
|
||||
uint8 spriteY = sprite.bits(16,23);
|
||||
uint8 spriteX = sprite.bits(24,31);
|
||||
|
||||
if(status.vclk < spriteY) continue;
|
||||
if(status.vclk > (uint8)(spriteY + 7)) continue;
|
||||
if(status.hclk < spriteX) continue;
|
||||
if(status.hclk > (uint8)(spriteX + 7)) continue;
|
||||
|
||||
uint16 tileOffset = 0x4000 + (sprite.bits(0,8) << 5);
|
||||
uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0);
|
||||
uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0);
|
||||
uint16 tileOffset = 0x4000 + (sprite.tile << 5);
|
||||
uint3 tileY = (uint8)(status.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0);
|
||||
uint3 tileX = (uint8)(status.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0);
|
||||
uint4 tileColor = renderColorFetch(tileOffset, tileY, tileX);
|
||||
if(tileColor == 0) continue;
|
||||
if(!sprite.bit(13) && pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
|
||||
uint12 color = iram.read(0xff00 + (sprite.bits(9,11) << 5) + (tileColor << 1), Word);
|
||||
uint12 color = iram.read(0xfe00 + (sprite.palette << 5) + (tileColor << 1), Word);
|
||||
pixel = {Pixel::Source::Sprite, color};
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ auto PPU::renderMonoScreenOne() -> void {
|
|||
uint8 scrollY = status.vclk + r.scrollOneY;
|
||||
uint8 scrollX = status.hclk + r.scrollOneX;
|
||||
|
||||
uint14 tilemapOffset = r.screenOneMapBase << 11;
|
||||
uint14 tilemapOffset = r.screenOneMapBase.bits(0,2) << 11;
|
||||
tilemapOffset += (scrollY >> 3) << 6;
|
||||
tilemapOffset += (scrollX >> 3) << 1;
|
||||
|
||||
|
@ -53,7 +53,7 @@ auto PPU::renderMonoScreenTwo() -> void {
|
|||
uint8 scrollX = status.hclk + r.scrollTwoX;
|
||||
uint8 scrollY = status.vclk + r.scrollTwoY;
|
||||
|
||||
uint14 tilemapOffset = r.screenTwoMapBase << 11;
|
||||
uint14 tilemapOffset = r.screenTwoMapBase.bits(0,2) << 11;
|
||||
tilemapOffset += (scrollY >> 3) << 6;
|
||||
tilemapOffset += (scrollX >> 3) << 1;
|
||||
|
||||
|
@ -72,34 +72,22 @@ auto PPU::renderMonoScreenTwo() -> void {
|
|||
auto PPU::renderMonoSprite() -> void {
|
||||
if(!r.spriteEnable) return;
|
||||
|
||||
bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1
|
||||
&& status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1;
|
||||
uint14 spriteBase = r.spriteBase << 9;
|
||||
bool windowInside = status.hclk >= r.spriteWindowX0 && status.hclk <= r.spriteWindowX1;
|
||||
for(auto& sprite : sprites) {
|
||||
if(r.spriteWindowEnable && sprite.window && !windowInside) continue;
|
||||
if(status.hclk < sprite.x) continue;
|
||||
if(status.hclk > sprite.x + 7) continue;
|
||||
|
||||
uint7 spriteIndex = r.spriteFirst;
|
||||
uint8 spriteCount = min(128, (uint)r.spriteCount);
|
||||
while(spriteCount--) {
|
||||
uint32 sprite = iram.read(spriteBase + (spriteIndex++ << 2), Long);
|
||||
if(r.spriteWindowEnable && sprite.bit(12) && !windowInside) continue;
|
||||
|
||||
uint8 spriteY = sprite.bits(16,23);
|
||||
uint8 spriteX = sprite.bits(24,31);
|
||||
|
||||
if(status.vclk < spriteY) continue;
|
||||
if(status.vclk > (uint8)(spriteY + 7)) continue;
|
||||
if(status.hclk < spriteX) continue;
|
||||
if(status.hclk > (uint8)(spriteX + 7)) continue;
|
||||
|
||||
uint14 tileOffset = 0x2000 + (sprite.bits(0,8) << 4);
|
||||
uint3 tileY = (uint8)(status.vclk - spriteY) ^ (sprite.bit(15) ? 7 : 0);
|
||||
uint3 tileX = (uint8)(status.hclk - spriteX) ^ (sprite.bit(14) ? 7 : 0);
|
||||
uint14 tileOffset = 0x2000 + (sprite.tile << 4);
|
||||
uint3 tileY = (uint8)(status.vclk - sprite.y) ^ (sprite.vflip ? 7 : 0);
|
||||
uint3 tileX = (uint8)(status.hclk - sprite.x) ^ (sprite.hflip ? 7 : 0);
|
||||
uint2 tileColor = renderMonoFetch(tileOffset, tileY, tileX);
|
||||
if(sprite.bit(11) && tileColor == 0) continue;
|
||||
if(!sprite.bit(13) && pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
if(sprite.palette.bit(2) && tileColor == 0) continue;
|
||||
if(!sprite.priority && pixel.source == Pixel::Source::ScreenTwo) continue;
|
||||
|
||||
uint3 paletteColor = r.palette[8 + sprite.bits(9,11)].color[tileColor];
|
||||
uint3 paletteColor = r.palette[sprite.palette].color[tileColor];
|
||||
uint4 poolColor = 15 - r.pool[paletteColor];
|
||||
pixel = {Pixel::Source::Sprite, poolColor << 0 | poolColor << 4 | poolColor << 8};
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
auto PPU::renderSpriteDecode() -> void {
|
||||
sprites.reset();
|
||||
sprites.reserve(32);
|
||||
if(!r.spriteEnable) return;
|
||||
|
||||
uint offset = 0;
|
||||
bool windowInside = status.vclk >= r.spriteWindowY0 && status.vclk <= r.spriteWindowY1;
|
||||
uint16 spriteBase = r.spriteBase.bits(0, 4 + system.depth()) << 9;
|
||||
uint7 spriteIndex = r.spriteFirst;
|
||||
uint8 spriteCount = min(128, (uint)r.spriteCount);
|
||||
while(spriteCount--) {
|
||||
uint32 attributes = iram.read(spriteBase + (spriteIndex++ << 2), Long);
|
||||
|
||||
Sprite sprite;
|
||||
sprite.x = attributes.bits(24,31);
|
||||
if(sprite.x > 224) continue;
|
||||
sprite.y = attributes.bits(16,23);
|
||||
if(status.vclk < sprite.y) continue;
|
||||
if(status.vclk > sprite.y + 7) continue;
|
||||
sprite.vflip = attributes.bit(15);
|
||||
sprite.hflip = attributes.bit(14);
|
||||
sprite.priority = attributes.bit(13);
|
||||
sprite.window = attributes.bit(12);
|
||||
if(r.spriteWindowEnable && sprite.window && !windowInside) continue;
|
||||
sprite.palette = 8 + attributes.bits(9,11);
|
||||
sprite.tile = attributes.bits(0,8);
|
||||
|
||||
sprites.append(sprite);
|
||||
if(sprites.size() >= 32) break;
|
||||
}
|
||||
}
|
|
@ -24,12 +24,32 @@ auto Video::power() -> void {
|
|||
}
|
||||
|
||||
auto Video::refresh() -> void {
|
||||
for(uint y = 0; y < 144; y++) {
|
||||
auto source = ppu.output + y * 224;
|
||||
if(system.orientation() == 0) {
|
||||
for(uint y = 0; y < 224; y++) {
|
||||
auto target = output() + y * 224;
|
||||
if(y < 40 || y >= 184) {
|
||||
memory::fill(target, 224 * sizeof(uint32));
|
||||
continue;
|
||||
}
|
||||
auto source = ppu.output + (y - 40) * 224;
|
||||
for(uint x = 0; x < 224; x++) {
|
||||
auto color = paletteStandard[*source++];
|
||||
*(output() + (y + 40) * 224 + x) = color;
|
||||
//*(output() + (223 - x) * 224 + 40 + y) = color;
|
||||
*target++ = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(system.orientation() == 1) {
|
||||
for(uint y = 0; y < 224; y++) {
|
||||
auto target = output() + y * 224;
|
||||
memory::fill(target, 40 * sizeof(uint32));
|
||||
memory::fill(target + 184, 40 * sizeof(uint32));
|
||||
target += 40;
|
||||
for(uint x = 0; x < 144; x++) {
|
||||
auto source = ppu.output + x * 224 + (223 - y);
|
||||
auto color = paletteStandard[*source];
|
||||
*target++ = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,9 +7,11 @@ System system;
|
|||
|
||||
auto System::loaded() const -> bool { return _loaded; }
|
||||
auto System::model() const -> Model { return _model; }
|
||||
auto System::orientation() const -> bool { return _orientation; }
|
||||
auto System::color() const -> bool { return r.color; }
|
||||
auto System::planar() const -> bool { return r.format == 0; }
|
||||
auto System::packed() const -> bool { return r.format == 1; }
|
||||
auto System::depth() const -> bool { return r.depth == 1; }
|
||||
|
||||
auto System::init() -> void {
|
||||
}
|
||||
|
@ -19,6 +21,7 @@ auto System::term() -> void {
|
|||
|
||||
auto System::load(Model model) -> void {
|
||||
_model = model;
|
||||
_orientation = 0;
|
||||
|
||||
interface->loadRequest(ID::SystemManifest, "manifest.bml", true);
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
|
@ -67,6 +70,24 @@ auto System::power() -> void {
|
|||
|
||||
auto System::run() -> void {
|
||||
while(scheduler.enter() != Scheduler::Event::Frame);
|
||||
|
||||
bool rotate = keypad.rotate;
|
||||
keypad.y1 = interface->inputPoll(_orientation, 0, 0);
|
||||
keypad.y2 = interface->inputPoll(_orientation, 0, 1);
|
||||
keypad.y3 = interface->inputPoll(_orientation, 0, 2);
|
||||
keypad.y4 = interface->inputPoll(_orientation, 0, 3);
|
||||
keypad.x1 = interface->inputPoll(_orientation, 0, 4);
|
||||
keypad.x2 = interface->inputPoll(_orientation, 0, 5);
|
||||
keypad.x3 = interface->inputPoll(_orientation, 0, 6);
|
||||
keypad.x4 = interface->inputPoll(_orientation, 0, 7);
|
||||
keypad.b = interface->inputPoll(_orientation, 0, 8);
|
||||
keypad.a = interface->inputPoll(_orientation, 0, 9);
|
||||
keypad.start = interface->inputPoll(_orientation, 0, 10);
|
||||
keypad.rotate = interface->inputPoll(_orientation, 0, 11);
|
||||
|
||||
if(!rotate && keypad.rotate) {
|
||||
_orientation = !_orientation;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,21 +1,11 @@
|
|||
enum class Keypad : uint {
|
||||
Y1, Y2, Y3, Y4,
|
||||
X1, X2, X3, X4,
|
||||
B, A, Start,
|
||||
};
|
||||
|
||||
struct System : IO {
|
||||
enum class Model : uint {
|
||||
WonderSwan, //SW-001 (ASWAN)
|
||||
WonderSwanColor, //WSC-001 (SPHINX)
|
||||
SwanCrystal, //SCT-001 (SPHINX2)
|
||||
};
|
||||
|
||||
auto loaded() const -> bool;
|
||||
auto model() const -> Model;
|
||||
auto orientation() const -> bool;
|
||||
auto color() const -> bool;
|
||||
auto planar() const -> bool;
|
||||
auto packed() const -> bool;
|
||||
auto depth() const -> bool;
|
||||
|
||||
auto init() -> void;
|
||||
auto term() -> void;
|
||||
|
@ -33,6 +23,13 @@ struct System : IO {
|
|||
|
||||
EEPROM eeprom;
|
||||
|
||||
struct Keypad {
|
||||
bool y1, y2, y3, y4;
|
||||
bool x1, x2, x3, x4;
|
||||
bool b, a, start;
|
||||
bool rotate;
|
||||
} keypad;
|
||||
|
||||
struct Registers {
|
||||
//$0060 DISP_MODE
|
||||
uint1 depth;
|
||||
|
@ -44,6 +41,7 @@ struct System : IO {
|
|||
privileged:
|
||||
bool _loaded = false;
|
||||
Model _model = Model::WonderSwan;
|
||||
bool _orientation = 0; //0 = horizontal, 1 = vertical
|
||||
};
|
||||
|
||||
extern System system;
|
||||
|
|
|
@ -20,6 +20,14 @@ namespace WonderSwan {
|
|||
#include <libco/libco.h>
|
||||
|
||||
namespace WonderSwan {
|
||||
enum class Model : uint {
|
||||
WonderSwan, //SW-001 (ASWAN)
|
||||
WonderSwanColor, //WSC-001 (SPHINX)
|
||||
SwanCrystal, //SCT-001 (SPHINX2)
|
||||
};
|
||||
|
||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||
|
||||
struct Thread {
|
||||
~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
|
@ -42,8 +50,6 @@ namespace WonderSwan {
|
|||
int64 clock = 0;
|
||||
};
|
||||
|
||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||
|
||||
#include <ws/memory/memory.hpp>
|
||||
#include <ws/eeprom/eeprom.hpp>
|
||||
#include <ws/system/system.hpp>
|
||||
|
@ -52,10 +58,6 @@ namespace WonderSwan {
|
|||
#include <ws/cpu/cpu.hpp>
|
||||
#include <ws/ppu/ppu.hpp>
|
||||
#include <ws/apu/apu.hpp>
|
||||
|
||||
inline auto WS() { return system.model() == System::Model::WonderSwan; }
|
||||
inline auto WSC() { return system.model() == System::Model::WonderSwanColor; }
|
||||
inline auto SC() { return system.model() == System::Model::SwanCrystal; }
|
||||
}
|
||||
|
||||
#include <ws/interface/interface.hpp>
|
||||
|
|
Loading…
Reference in New Issue