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:
Tim Allen 2016-03-13 00:27:41 +11:00
parent 79e7e6ab9e
commit c33065fbd1
19 changed files with 324 additions and 213 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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