Update to v097r11 release.

byuu says:

Alright, well interrupts are in. At least Vblank is.

I also fixed a bug in vector() indexing, MoDRM mod!=3&&reg==6 using SS
instead of DS, opcodes a0-a3 allowing segment override, and added the
"irq_disable" stuff to the relevant opcodes to suppress IRQs after
certain instructions.

But unfortunately ... still no go on Riviera. It's not reading any
unmapped ports, and although it enables Vblank IRQs and they set port
$b4's status bit, the game never sets the IE flag, so no interrupts ever
actually fire. The game does indeed appear to be sitting in a rather
huge loop, which is probably dependent upon some RAM variable being set
from the Vblank IRQ, but I don't know how I'm supposed to be triggering
it.

... I'm really quite stumped here >_>
This commit is contained in:
Tim Allen 2016-02-05 08:18:06 +11:00
parent 7a748e093e
commit a89a3da77a
15 changed files with 201 additions and 86 deletions

View File

@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "097.10"; static const string Version = "097.11";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -83,11 +83,13 @@ auto V30MZ::opReturnFarImm() {
r.sp += offset; r.sp += offset;
} }
//cf iret
auto V30MZ::opReturnInt() { auto V30MZ::opReturnInt() {
wait(9); wait(9);
r.ip = pop(); r.ip = pop();
r.cs = pop(); r.cs = pop();
r.f = pop(); r.f = pop();
state.poll = false;
} }
auto V30MZ::opInt3() { auto V30MZ::opInt3() {
@ -136,6 +138,7 @@ auto V30MZ::opPushReg(uint16& reg) {
auto V30MZ::opPopReg(uint16& reg) { auto V30MZ::opPopReg(uint16& reg) {
reg = pop(); reg = pop();
if(&reg == &r.ss) state.poll = false;
} }
//9c pushf //9c pushf
@ -148,6 +151,7 @@ auto V30MZ::opPushFlags() {
auto V30MZ::opPopFlags() { auto V30MZ::opPopFlags() {
wait(2); wait(2);
r.f = pop(); r.f = pop();
state.poll = false;
} }
//60 pusha //60 pusha

View File

@ -24,4 +24,5 @@ auto V30MZ::opClearFlag(bool& flag) {
auto V30MZ::opSetFlag(bool& flag) { auto V30MZ::opSetFlag(bool& flag) {
wait(3); wait(3);
flag = true; flag = true;
if(&flag == &r.f.i) state.poll = false;
} }

View File

@ -3,22 +3,25 @@
//36 ss: //36 ss:
//3e ds: //3e ds:
auto V30MZ::opSegment(uint16 segment) { auto V30MZ::opSegment(uint16 segment) {
state.prefix = true;
prefix.segment = segment; prefix.segment = segment;
state.prefix = true;
state.poll = false;
} }
//f2 repnz //f2 repnz:
//f3 repz //f3 repz:
auto V30MZ::opRepeat(bool flag) { auto V30MZ::opRepeat(bool flag) {
wait(4); wait(4);
if(r.cx == 0) return; if(r.cx == 0) return;
state.prefix = true;
prefix.repeat = flag; prefix.repeat = flag;
state.prefix = true;
state.poll = false;
} }
//f0 lock //f0 lock:
auto V30MZ::opLock() { auto V30MZ::opLock() {
state.prefix = true; state.prefix = true;
state.poll = false;
} }
//9b wait //9b wait

View File

@ -8,23 +8,27 @@ auto V30MZ::opMoveRegMem(Size size) {
setReg(size, getMem(size)); setReg(size, getMem(size));
} }
//8c mov memw,seg
auto V30MZ::opMoveMemSeg() { auto V30MZ::opMoveMemSeg() {
modRM(); modRM();
setMem(Word, getSeg()); setMem(Word, getSeg());
state.poll = false;
} }
//8e mov seg,memw
auto V30MZ::opMoveSegMem() { auto V30MZ::opMoveSegMem() {
wait(1); wait(1);
modRM(); modRM();
setSeg(getMem(Word)); setSeg(getMem(Word));
if((modrm.reg & 3) == 3) state.poll = false;
} }
auto V30MZ::opMoveAccMem(Size size) { auto V30MZ::opMoveAccMem(Size size) {
setAcc(size, read(size, r.ds, fetch(Word))); setAcc(size, read(size, segment(r.ds), fetch(Word)));
} }
auto V30MZ::opMoveMemAcc(Size size) { auto V30MZ::opMoveMemAcc(Size size) {
write(size, r.ds, fetch(Word), getAcc(size)); write(size, segment(r.ds), fetch(Word), getAcc(size));
} }
auto V30MZ::opMoveRegImm(uint8& reg) { auto V30MZ::opMoveRegImm(uint8& reg) {

View File

@ -20,7 +20,7 @@ auto V30MZ::modRM() -> void {
case 3: modrm.segment = segment(r.ss); modrm.address = r.bp + r.di; break; case 3: modrm.segment = segment(r.ss); modrm.address = r.bp + r.di; break;
case 4: modrm.segment = segment(r.ds); modrm.address = r.si; break; case 4: modrm.segment = segment(r.ds); modrm.address = r.si; break;
case 5: modrm.segment = segment(r.ds); modrm.address = r.di; break; case 5: modrm.segment = segment(r.ds); modrm.address = r.di; break;
case 6: modrm.segment = segment(r.ds); modrm.address = r.bp; break; case 6: modrm.segment = segment(r.ss); modrm.address = r.bp; break;
case 7: modrm.segment = segment(r.ds); modrm.address = r.bx; break; case 7: modrm.segment = segment(r.ds); modrm.address = r.bx; break;
} }
if(modrm.mod == 1) modrm.address += (int8)fetch(Byte); if(modrm.mod == 1) modrm.address += (int8)fetch(Byte);

View File

@ -18,9 +18,11 @@ namespace Processor {
#include "disassembler.cpp" #include "disassembler.cpp"
auto V30MZ::exec() -> void { auto V30MZ::exec() -> void {
state.poll = true;
if(state.halt) return wait(1); if(state.halt) return wait(1);
#if 0 #if 0
static uint counter = 0;
static uint16 cs = 0, ip = 0; static uint16 cs = 0, ip = 0;
if(cs != r.cs || ip != r.ip) print(disassemble(cs = r.cs, ip = r.ip), "\n"); if(cs != r.cs || ip != r.ip) print(disassemble(cs = r.cs, ip = r.ip), "\n");
#endif #endif
@ -30,8 +32,8 @@ auto V30MZ::exec() -> void {
if(state.prefix) { if(state.prefix) {
state.prefix = false; state.prefix = false;
} else { } else {
prefix.segment = nothing;
prefix.repeat = nothing; prefix.repeat = nothing;
prefix.segment = nothing;
} }
} }
@ -302,8 +304,8 @@ auto V30MZ::instruction() -> void {
auto V30MZ::interrupt(uint8 vector) -> void { auto V30MZ::interrupt(uint8 vector) -> void {
state.halt = false; state.halt = false;
auto ip = read(Word, 0x0000, vector * 2 + 0); auto ip = read(Word, 0x0000, vector * 4 + 0);
auto cs = read(Word, 0x0000, vector * 2 + 2); auto cs = read(Word, 0x0000, vector * 4 + 2);
push(r.f); push(r.f);
push(r.cs); push(r.cs);
@ -318,25 +320,26 @@ auto V30MZ::interrupt(uint8 vector) -> void {
} }
auto V30MZ::power() -> void { auto V30MZ::power() -> void {
state.halt = false; state.halt = false;
state.poll = true;
state.prefix = false; state.prefix = false;
prefix.repeat = nothing;
prefix.segment = nothing; prefix.segment = nothing;
prefix.repeat = nothing;
r.ip = 0x0000;
r.ax = 0x0000; r.ax = 0x0000;
r.bx = 0x0000;
r.cx = 0x0000; r.cx = 0x0000;
r.dx = 0x0000; r.dx = 0x0000;
r.bx = 0x0000;
r.sp = 0x0000;
r.bp = 0x0000;
r.si = 0x0000; r.si = 0x0000;
r.di = 0x0000; r.di = 0x0000;
r.bp = 0x0000;
r.sp = 0x0000;
r.cs = 0xffff;
r.ds = 0x0000;
r.es = 0x0000; r.es = 0x0000;
r.cs = 0xffff;
r.ss = 0x0000; r.ss = 0x0000;
r.ds = 0x0000;
r.ip = 0x0000;
r.f = 0x8000; r.f = 0x8000;
} }

View File

@ -7,7 +7,6 @@ namespace Processor {
struct V30MZ { struct V30MZ {
using Size = uint; using Size = uint;
enum : uint { Byte = 1, Word = 2, Long = 4 }; enum : uint { Byte = 1, Word = 2, Long = 4 };
struct ModRM;
virtual auto wait(uint clocks = 1) -> void = 0; virtual auto wait(uint clocks = 1) -> void = 0;
virtual auto read(uint20 addr) -> uint8 = 0; virtual auto read(uint20 addr) -> uint8 = 0;
@ -197,13 +196,14 @@ struct V30MZ {
auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string; auto disassemble(uint16 cs, uint16 ip, bool registers = true, bool bytes = true) -> string;
struct State { struct State {
bool halt; bool halt; //set to true for hlt instruction; blocks execution until next interrupt
bool prefix; bool poll; //set to false to suppress interrupt polling between CPU instructions
bool prefix; //set to true for prefix instructions; prevents flushing of Prefix struct
} state; } state;
struct Prefix { struct Prefix {
maybe<uint16> segment; maybe<bool> repeat; //repnz, repz
maybe<bool> repeat; maybe<uint16> segment; //cs, es, ss, ds
} prefix; } prefix;
struct ModRM { struct ModRM {
@ -211,24 +211,24 @@ struct V30MZ {
uint3 reg; uint3 reg;
uint3 mem; uint3 mem;
uint16 address;
uint16 segment; uint16 segment;
uint16 address;
} modrm; } modrm;
struct Registers { struct Registers {
uint16 ip;
union { uint16 ax; struct { uint8 order_lsb2(al, ah); }; }; union { uint16 ax; struct { uint8 order_lsb2(al, ah); }; };
union { uint16 bx; struct { uint8 order_lsb2(bl, bh); }; };
union { uint16 cx; struct { uint8 order_lsb2(cl, ch); }; }; union { uint16 cx; struct { uint8 order_lsb2(cl, ch); }; };
union { uint16 dx; struct { uint8 order_lsb2(dl, dh); }; }; union { uint16 dx; struct { uint8 order_lsb2(dl, dh); }; };
union { uint16 bx; struct { uint8 order_lsb2(bl, bh); }; };
uint16 sp;
uint16 bp;
uint16 si; uint16 si;
uint16 di; uint16 di;
uint16 bp;
uint16 sp;
uint16 cs;
uint16 ds;
uint16 es; uint16 es;
uint16 cs;
uint16 ss; uint16 ss;
uint16 ds;
uint16 ip;
uint8* b[8]{&al, &cl, &dl, &bl, &ah, &ch, &dh, &bh}; uint8* b[8]{&al, &cl, &dl, &bl, &ah, &ch, &dh, &bh};
uint16* w[8]{&ax, &cx, &dx, &bx, &sp, &bp, &si, &di}; uint16* w[8]{&ax, &cx, &dx, &bx, &sp, &bp, &si, &di};

View File

@ -5,6 +5,7 @@ namespace WonderSwan {
CPU cpu; CPU cpu;
#include "memory.cpp" #include "memory.cpp"
#include "io.cpp" #include "io.cpp"
#include "interrupt.cpp"
#include "dma.cpp" #include "dma.cpp"
auto CPU::Enter() -> void { auto CPU::Enter() -> void {
@ -18,6 +19,7 @@ auto CPU::main() -> void {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
} }
poll();
exec(); exec();
} }
} }
@ -55,17 +57,24 @@ auto CPU::power() -> void {
create(CPU::Enter, 3072000); create(CPU::Enter, 3072000);
iomap[0x00a0] = this; iomap[0x00a0] = this;
iomap[0x00b0] = this;
iomap[0x00b2] = this;
iomap[0x00b4] = this;
iomap[0x00b6] = this;
if(WSC() || SC()) { if(WSC() || SC()) {
for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this; for(uint p = 0x0040; p <= 0x0049; p++) iomap[p] = this;
iomap[0x0062] = this; iomap[0x0062] = this;
} }
s.dmaSource = 0x00000; r.dmaSource = 0x00000;
s.dmaTarget = 0x0000; r.dmaTarget = 0x0000;
s.dmaLength = 0x0000; r.dmaLength = 0x0000;
s.dmaEnable = false; r.dmaEnable = false;
s.dmaMode = 0; r.dmaMode = 0;
r.interruptBase = 0x00;
r.interruptEnable = 0x00;
r.interruptStatus = 0x00;
} }
} }

View File

@ -1,4 +1,15 @@
struct CPU : Processor::V30MZ, Thread, IO { struct CPU : Processor::V30MZ, Thread, IO {
enum class Interrupt : uint {
SerialSend,
Input,
Cartridge,
SerialReceive,
LineCompare,
VblankTimer,
Vblank,
HblankTimer,
};
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
@ -20,23 +31,37 @@ struct CPU : Processor::V30MZ, Thread, IO {
auto portRead(uint16 addr) -> uint8 override; auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override; auto portWrite(uint16 addr, uint8 data) -> void override;
//interrupt.cpp
auto poll() -> void;
auto raise(Interrupt) -> void;
auto lower(Interrupt) -> void;
//dma.cpp //dma.cpp
auto dmaTransfer() -> void; auto dmaTransfer() -> void;
struct State { struct Registers {
//$0040-0042 DMA_SRC //$0040-0042 DMA_SRC
uint20 dmaSource; uint20 dmaSource;
//$0044-0045 DMA_DST //$0044-0045 DMA_DST
uint16_ dmaTarget; uint16_ dmaTarget;
//$0046-0047 DMA_LEN //$0046-0047 DMA_LEN
uint16_ dmaLength; uint16_ dmaLength;
//$0048 DMA_CTRL //$0048 DMA_CTRL
bool dmaEnable; bool dmaEnable;
bool dmaMode; //0 = increment; 1 = decrement bool dmaMode; //0 = increment; 1 = decrement
} s;
//$00b0 INT_BASE
uint8 interruptBase;
//$00b2 INT_ENABLE
uint8 interruptEnable;
//$00b4 INT_STATUS
uint8 interruptStatus;
} r;
}; };
extern CPU cpu; extern CPU cpu;

View File

@ -1,29 +1,29 @@
auto CPU::dmaTransfer() -> void { auto CPU::dmaTransfer() -> void {
//length of 0 or SRAM source address cause immediate termination //length of 0 or SRAM source address cause immediate termination
if(s.dmaLength == 0 || s.dmaSource.b2 == 1) { if(r.dmaLength == 0 || r.dmaSource.b2 == 1) {
s.dmaEnable = false; r.dmaEnable = false;
return; return;
} }
wait(5); wait(5);
while(s.dmaLength) { while(r.dmaLength) {
wait(2); wait(2);
uint16 data = 0; uint16 data = 0;
//once DMA is started; SRAM reads still incur time penalty, but do not transfer //once DMA is started; SRAM reads still incur time penalty, but do not transfer
if(s.dmaSource.b2 != 1) { if(r.dmaSource.b2 != 1) {
data |= read(s.dmaSource + 0) << 0; data |= read(r.dmaSource + 0) << 0;
data |= read(s.dmaSource + 1) << 8; data |= read(r.dmaSource + 1) << 8;
write(s.dmaTarget + 0, data >> 0); write(r.dmaTarget + 0, data >> 0);
write(s.dmaTarget + 1, data >> 8); write(r.dmaTarget + 1, data >> 8);
} }
if(s.dmaMode == 0) { if(r.dmaMode == 0) {
s.dmaSource += 2; r.dmaSource += 2;
s.dmaTarget += 2; r.dmaTarget += 2;
} else { } else {
s.dmaSource -= 2; r.dmaSource -= 2;
s.dmaTarget -= 2; r.dmaTarget -= 2;
} }
s.dmaLength -= 2; r.dmaLength -= 2;
}; };
s.dmaEnable = false; r.dmaEnable = false;
} }

View File

@ -0,0 +1,21 @@
auto CPU::poll() -> void {
if(!state.poll || !(r.interruptStatus & r.interruptEnable)) return;
state.halt = false;
if(!V30MZ::r.f.i) return;
for(int n = 7; n >= 0; n--) {
if(r.interruptStatus & r.interruptEnable & (1 << n)) {
interrupt(r.interruptBase + n);
}
}
}
auto CPU::raise(Interrupt i) -> void {
auto mask = 1 << (uint)i;
r.interruptStatus |= mask;
}
auto CPU::lower(Interrupt i) -> void {
auto mask = 1 << (uint)i;
r.interruptStatus &= ~mask;
}

View File

@ -1,19 +1,19 @@
auto CPU::portRead(uint16 addr) -> uint8 { auto CPU::portRead(uint16 addr) -> uint8 {
//DMA_SRC //DMA_SRC
if(addr == 0x0040) return s.dmaSource.b0; if(addr == 0x0040) return r.dmaSource.b0;
if(addr == 0x0041) return s.dmaSource.b1; if(addr == 0x0041) return r.dmaSource.b1;
if(addr == 0x0042) return s.dmaSource.b2; if(addr == 0x0042) return r.dmaSource.b2;
//DMA_DST //DMA_DST
if(addr == 0x0044) return s.dmaTarget.b0; if(addr == 0x0044) return r.dmaTarget.b0;
if(addr == 0x0045) return s.dmaTarget.b1; if(addr == 0x0045) return r.dmaTarget.b1;
//DMA_LEN //DMA_LEN
if(addr == 0x0046) return s.dmaLength.b0; if(addr == 0x0046) return r.dmaLength.b0;
if(addr == 0x0047) return s.dmaLength.b1; if(addr == 0x0047) return r.dmaLength.b1;
//DMA_CTRL //DMA_CTRL
if(addr == 0x0048) return s.dmaEnable << 7 | s.dmaMode << 0; if(addr == 0x0048) return r.dmaEnable << 7 | r.dmaMode << 0;
//WSC_SYSTEM //WSC_SYSTEM
if(addr == 0x0062) { if(addr == 0x0062) {
@ -22,44 +22,78 @@ auto CPU::portRead(uint16 addr) -> uint8 {
//HW_FLAGS //HW_FLAGS
if(addr == 0x00a0) { if(addr == 0x00a0) {
return 1 << 7 //1 = built-in self-test passed return (
| 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width 1 << 7 //1 = built-in self-test passed
| !WS() << 1 //0 = WonderSwan; 1 = WonderSwan Color or SwanCrystal | 1 << 2 //0 = 8-bit bus width; 1 = 16-bit bus width
| 1 << 0; //0 = BIOS mapped; 1 = cartridge mapped | !WS() << 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;
return r.interruptBase;
}
//INT_ENABLE
if(addr == 0x00b2) return r.interruptEnable;
//INT_STATUS
if(addr == 0x00b4) return r.interruptStatus;
return 0x00; return 0x00;
} }
auto CPU::portWrite(uint16 addr, uint8 data) -> void { auto CPU::portWrite(uint16 addr, uint8 data) -> void {
//DMA_SRC //DMA_SRC
if(addr == 0x0040) { s.dmaSource.b0 = data & ~1; return; } if(addr == 0x0040) { r.dmaSource.b0 = data & ~1; return; }
if(addr == 0x0041) { s.dmaSource.b1 = data; return; } if(addr == 0x0041) { r.dmaSource.b1 = data; return; }
if(addr == 0x0042) { s.dmaSource.b2 = data; return; } if(addr == 0x0042) { r.dmaSource.b2 = data; return; }
//DMA_DST //DMA_DST
if(addr == 0x0044) { s.dmaTarget.b0 = data & ~1; return; } if(addr == 0x0044) { r.dmaTarget.b0 = data & ~1; return; }
if(addr == 0x0045) { s.dmaTarget.b1 = data; return; } if(addr == 0x0045) { r.dmaTarget.b1 = data; return; }
//DMA_LEN //DMA_LEN
if(addr == 0x0046) { s.dmaLength.b0 = data & ~1; return; } if(addr == 0x0046) { r.dmaLength.b0 = data & ~1; return; }
if(addr == 0x0047) { s.dmaLength.b1 = data; return; } if(addr == 0x0047) { r.dmaLength.b1 = data; return; }
//DMA_CTRL //DMA_CTRL
if(addr == 0x0048) { if(addr == 0x0048) {
s.dmaEnable = (uint1)(data >> 7); r.dmaEnable = (uint1)(data >> 7);
s.dmaMode = (uint1)(data >> 0); r.dmaMode = (uint1)(data >> 0);
if(s.dmaEnable) dmaTransfer(); if(r.dmaEnable) dmaTransfer();
return; return;
} }
//WSC_SYSTEM //WSC_SYSTEM
if(addr == 0x0062) { if(addr == 0x0062) {
//todo: d0 = 1 powers off system //todo: d0 = 1 powers off system
return;
} }
//HW_FLAGS //HW_FLAGS
if(addr == 0x00a0) { if(addr == 0x00a0) {
//todo: d2 (bus width) bit is writable; but ... it will do very bad things //todo: d2 (bus width) bit is writable; but ... it will do very bad things
return;
}
//INT_BASE
if(addr == 0x00b0) {
r.interruptBase = WS() ? (data & ~7) : (data & ~1);
return;
}
//INT_ENABLE
if(addr == 0x00b2) {
r.interruptEnable = data;
return;
}
//INT_ACK
if(addr == 0x00b6) {
r.interruptStatus &= ~data;
return;
} }
} }

View File

@ -17,15 +17,24 @@ auto PPU::main() -> void {
} }
step(256); step(256);
scanline();
status.hclk = 0;
if(++status.vclk == 159) {
status.vclk = 0;
video.refresh();
}
} }
} }
auto PPU::scanline() -> void {
status.hclk = 0;
status.vclk++;
if(status.vclk == 144) {
cpu.raise(CPU::Interrupt::Vblank);
}
if(status.vclk == 159) frame();
}
auto PPU::frame() -> void {
status.vclk = 0;
video.refresh();
}
auto PPU::step(uint clocks) -> void { auto PPU::step(uint clocks) -> void {
status.hclk += clocks; status.hclk += clocks;

View File

@ -4,6 +4,8 @@ struct PPU : Thread, IO {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;
auto scanline() -> void;
auto frame() -> void;
auto step(uint clocks) -> void; auto step(uint clocks) -> void;
auto power() -> void; auto power() -> void;