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 {
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 License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

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

View File

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

View File

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

View File

@ -8,23 +8,27 @@ auto V30MZ::opMoveRegMem(Size size) {
setReg(size, getMem(size));
}
//8c mov memw,seg
auto V30MZ::opMoveMemSeg() {
modRM();
setMem(Word, getSeg());
state.poll = false;
}
//8e mov seg,memw
auto V30MZ::opMoveSegMem() {
wait(1);
modRM();
setSeg(getMem(Word));
if((modrm.reg & 3) == 3) state.poll = false;
}
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) {
write(size, r.ds, fetch(Word), getAcc(size));
write(size, segment(r.ds), fetch(Word), getAcc(size));
}
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 4: modrm.segment = segment(r.ds); modrm.address = r.si; 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;
}
if(modrm.mod == 1) modrm.address += (int8)fetch(Byte);

View File

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

View File

@ -7,7 +7,6 @@ namespace Processor {
struct V30MZ {
using Size = uint;
enum : uint { Byte = 1, Word = 2, Long = 4 };
struct ModRM;
virtual auto wait(uint clocks = 1) -> void = 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;
struct State {
bool halt;
bool prefix;
bool halt; //set to true for hlt instruction; blocks execution until next interrupt
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;
struct Prefix {
maybe<uint16> segment;
maybe<bool> repeat;
maybe<bool> repeat; //repnz, repz
maybe<uint16> segment; //cs, es, ss, ds
} prefix;
struct ModRM {
@ -211,24 +211,24 @@ struct V30MZ {
uint3 reg;
uint3 mem;
uint16 address;
uint16 segment;
uint16 address;
} modrm;
struct Registers {
uint16 ip;
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 dx; struct { uint8 order_lsb2(dl, dh); }; };
union { uint16 bx; struct { uint8 order_lsb2(bl, bh); }; };
uint16 sp;
uint16 bp;
uint16 si;
uint16 di;
uint16 bp;
uint16 sp;
uint16 cs;
uint16 ds;
uint16 es;
uint16 cs;
uint16 ss;
uint16 ds;
uint16 ip;
uint8* b[8]{&al, &cl, &dl, &bl, &ah, &ch, &dh, &bh};
uint16* w[8]{&ax, &cx, &dx, &bx, &sp, &bp, &si, &di};

View File

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

View File

@ -1,4 +1,15 @@
struct CPU : Processor::V30MZ, Thread, IO {
enum class Interrupt : uint {
SerialSend,
Input,
Cartridge,
SerialReceive,
LineCompare,
VblankTimer,
Vblank,
HblankTimer,
};
static auto Enter() -> void;
auto main() -> void;
@ -20,23 +31,37 @@ struct CPU : Processor::V30MZ, Thread, IO {
auto portRead(uint16 addr) -> uint8 override;
auto portWrite(uint16 addr, uint8 data) -> void override;
//interrupt.cpp
auto poll() -> void;
auto raise(Interrupt) -> void;
auto lower(Interrupt) -> void;
//dma.cpp
auto dmaTransfer() -> void;
struct State {
//$0040-0042 DMA_SRC
struct Registers {
//$0040-0042 DMA_SRC
uint20 dmaSource;
//$0044-0045 DMA_DST
//$0044-0045 DMA_DST
uint16_ dmaTarget;
//$0046-0047 DMA_LEN
//$0046-0047 DMA_LEN
uint16_ dmaLength;
//$0048 DMA_CTRL
//$0048 DMA_CTRL
bool dmaEnable;
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;

View File

@ -1,29 +1,29 @@
auto CPU::dmaTransfer() -> void {
//length of 0 or SRAM source address cause immediate termination
if(s.dmaLength == 0 || s.dmaSource.b2 == 1) {
s.dmaEnable = false;
if(r.dmaLength == 0 || r.dmaSource.b2 == 1) {
r.dmaEnable = false;
return;
}
wait(5);
while(s.dmaLength) {
while(r.dmaLength) {
wait(2);
uint16 data = 0;
//once DMA is started; SRAM reads still incur time penalty, but do not transfer
if(s.dmaSource.b2 != 1) {
data |= read(s.dmaSource + 0) << 0;
data |= read(s.dmaSource + 1) << 8;
write(s.dmaTarget + 0, data >> 0);
write(s.dmaTarget + 1, data >> 8);
if(r.dmaSource.b2 != 1) {
data |= read(r.dmaSource + 0) << 0;
data |= read(r.dmaSource + 1) << 8;
write(r.dmaTarget + 0, data >> 0);
write(r.dmaTarget + 1, data >> 8);
}
if(s.dmaMode == 0) {
s.dmaSource += 2;
s.dmaTarget += 2;
if(r.dmaMode == 0) {
r.dmaSource += 2;
r.dmaTarget += 2;
} else {
s.dmaSource -= 2;
s.dmaTarget -= 2;
r.dmaSource -= 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 {
//DMA_SRC
if(addr == 0x0040) return s.dmaSource.b0;
if(addr == 0x0041) return s.dmaSource.b1;
if(addr == 0x0042) return s.dmaSource.b2;
if(addr == 0x0040) return r.dmaSource.b0;
if(addr == 0x0041) return r.dmaSource.b1;
if(addr == 0x0042) return r.dmaSource.b2;
//DMA_DST
if(addr == 0x0044) return s.dmaTarget.b0;
if(addr == 0x0045) return s.dmaTarget.b1;
if(addr == 0x0044) return r.dmaTarget.b0;
if(addr == 0x0045) return r.dmaTarget.b1;
//DMA_LEN
if(addr == 0x0046) return s.dmaLength.b0;
if(addr == 0x0047) return s.dmaLength.b1;
if(addr == 0x0046) return r.dmaLength.b0;
if(addr == 0x0047) return r.dmaLength.b1;
//DMA_CTRL
if(addr == 0x0048) return s.dmaEnable << 7 | s.dmaMode << 0;
if(addr == 0x0048) return r.dmaEnable << 7 | r.dmaMode << 0;
//WSC_SYSTEM
if(addr == 0x0062) {
@ -22,44 +22,78 @@ auto CPU::portRead(uint16 addr) -> uint8 {
//HW_FLAGS
if(addr == 0x00a0) {
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
| 1 << 0; //0 = BIOS mapped; 1 = cartridge mapped
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
| 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;
}
auto CPU::portWrite(uint16 addr, uint8 data) -> void {
//DMA_SRC
if(addr == 0x0040) { s.dmaSource.b0 = data & ~1; return; }
if(addr == 0x0041) { s.dmaSource.b1 = data; return; }
if(addr == 0x0042) { s.dmaSource.b2 = data; return; }
if(addr == 0x0040) { r.dmaSource.b0 = data & ~1; return; }
if(addr == 0x0041) { r.dmaSource.b1 = data; return; }
if(addr == 0x0042) { r.dmaSource.b2 = data; return; }
//DMA_DST
if(addr == 0x0044) { s.dmaTarget.b0 = data & ~1; return; }
if(addr == 0x0045) { s.dmaTarget.b1 = data; return; }
if(addr == 0x0044) { r.dmaTarget.b0 = data & ~1; return; }
if(addr == 0x0045) { r.dmaTarget.b1 = data; return; }
//DMA_LEN
if(addr == 0x0046) { s.dmaLength.b0 = data & ~1; return; }
if(addr == 0x0047) { s.dmaLength.b1 = data; return; }
if(addr == 0x0046) { r.dmaLength.b0 = data & ~1; return; }
if(addr == 0x0047) { r.dmaLength.b1 = data; return; }
//DMA_CTRL
if(addr == 0x0048) {
s.dmaEnable = (uint1)(data >> 7);
s.dmaMode = (uint1)(data >> 0);
if(s.dmaEnable) dmaTransfer();
r.dmaEnable = (uint1)(data >> 7);
r.dmaMode = (uint1)(data >> 0);
if(r.dmaEnable) dmaTransfer();
return;
}
//WSC_SYSTEM
if(addr == 0x0062) {
//todo: d0 = 1 powers off system
return;
}
//HW_FLAGS
if(addr == 0x00a0) {
//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);
status.hclk = 0;
if(++status.vclk == 159) {
status.vclk = 0;
video.refresh();
}
scanline();
}
}
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 {
status.hclk += clocks;

View File

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