Update to v103r06 release.

byuu says:

Changelog:

  - processor/spc700: restored fetch/load/store/pull/push shorthand
    functions
  - processor/spc700: split functions that tested the algorithm used (`op
    != &SPC700:...`) to separate instructions
      - mostly for code clarity over code size: it was awkward having
        cycle counts change based on a function parameter
  - processor/spc700: implemented Overload's new findings on which
    cycles are truly internal (no bus reads)
  - sfc/smp: TEST register emulation has been vastly improved¹

¹: it turns out that TEST.d4,d5 is the external clock divider (used
when accessing RAM through the DSP), and TEST.d6,d7 is the internal
clock divider (used when accessing IPLROM, IO registers, or during idle
cycles.)

The DSP (24576khz) feeds its clock / 12 through to the SMP (2048khz).
The clock divider setting further divides the clock by 2, 4, 8, or 16.
Since 8 and 16 are not cleanly divislbe by 12, the SMP cycle count
glitches out and seems to take 10 and 2 clocks instead of 8 or 16. This
can on real hardware either cause the SMP to run very slowly, or more
likely, crash the SMP completely until reset.

What's even stranger is the timers aren't affected by this. They still
clock by 2, 4, 8, or 16.

Note that technically I could divide my own clock counters by 24 and
reduce these to {1,2,5,10} and {1,2,4,8}, I instead chose to divide by
12 to better illustrate this hardware issue and better model that the
SMP clock runs at 2048khz and not 1024khz.

Further, note that things aren't 100% perfect yet. This seems to throw
off some tests, such as blargg's `test_timer_speed`. I can't tell how
far off I am because blargg's test tragically doesn't print out fail
values. But you can see the improvements in that higan is now passing
all of Revenant's tests that were obviously completely wrong before.
This commit is contained in:
Tim Allen 2017-07-03 17:24:47 +10:00
parent 40802b0b9f
commit 16f736307e
12 changed files with 370 additions and 303 deletions

View File

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

View File

@ -95,10 +95,6 @@ auto SPC700::algorithmSBC(uint8 x, uint8 y) -> uint8 {
return algorithmADC(x, ~y);
}
auto SPC700::algorithmST(uint8 x, uint8 y) -> uint8 {
return y;
}
//
auto SPC700::algorithmADW(uint16 x, uint16 y) -> uint16 {

View File

@ -2,7 +2,7 @@
#define fp(name) &SPC700::algorithm##name
auto SPC700::instruction() -> void {
switch(read(PC++)) {
switch(fetch()) {
op(0x00, NoOperation)
op(0x01, CallTable, 0)
op(0x02, AbsoluteBitSet, 0, true)
@ -12,7 +12,7 @@ auto SPC700::instruction() -> void {
op(0x06, IndirectXRead, fp(OR))
op(0x07, IndexedIndirectRead, fp(OR), X)
op(0x08, ImmediateRead, fp(OR), A)
op(0x09, DirectWriteDirect, fp(OR))
op(0x09, DirectDirectModify, fp(OR))
op(0x0a, AbsoluteBitModify, 0)
op(0x0b, DirectModify, fp(ASL))
op(0x0c, AbsoluteModify, fp(ASL))
@ -27,7 +27,7 @@ auto SPC700::instruction() -> void {
op(0x15, AbsoluteIndexedRead, fp(OR), X)
op(0x16, AbsoluteIndexedRead, fp(OR), Y)
op(0x17, IndirectIndexedRead, fp(OR), Y)
op(0x18, DirectWriteImmediate, fp(OR))
op(0x18, DirectImmediateModify, fp(OR))
op(0x19, IndirectXWriteIndirectY, fp(OR))
op(0x1a, DirectModifyWord, -1)
op(0x1b, DirectIndexedModify, fp(ASL), X)
@ -44,7 +44,7 @@ auto SPC700::instruction() -> void {
op(0x26, IndirectXRead, fp(AND))
op(0x27, IndexedIndirectRead, fp(AND), X)
op(0x28, ImmediateRead, fp(AND), A)
op(0x29, DirectWriteDirect, fp(AND))
op(0x29, DirectDirectModify, fp(AND))
op(0x2a, AbsoluteBitModify, 1)
op(0x2b, DirectModify, fp(ROL))
op(0x2c, AbsoluteModify, fp(ROL))
@ -59,7 +59,7 @@ auto SPC700::instruction() -> void {
op(0x35, AbsoluteIndexedRead, fp(AND), X)
op(0x36, AbsoluteIndexedRead, fp(AND), Y)
op(0x37, IndirectIndexedRead, fp(AND), Y)
op(0x38, DirectWriteImmediate, fp(AND))
op(0x38, DirectImmediateModify, fp(AND))
op(0x39, IndirectXWriteIndirectY, fp(AND))
op(0x3a, DirectModifyWord, +1)
op(0x3b, DirectIndexedModify, fp(ROL), A)
@ -76,7 +76,7 @@ auto SPC700::instruction() -> void {
op(0x46, IndirectXRead, fp(EOR))
op(0x47, IndexedIndirectRead, fp(EOR), X)
op(0x48, ImmediateRead, fp(EOR), A)
op(0x49, DirectWriteDirect, fp(EOR))
op(0x49, DirectDirectModify, fp(EOR))
op(0x4a, AbsoluteBitModify, 2)
op(0x4b, DirectModify, fp(LSR))
op(0x4c, AbsoluteModify, fp(LSR))
@ -91,9 +91,9 @@ auto SPC700::instruction() -> void {
op(0x55, AbsoluteIndexedRead, fp(EOR), X)
op(0x56, AbsoluteIndexedRead, fp(EOR), Y)
op(0x57, IndirectIndexedRead, fp(EOR), Y)
op(0x58, DirectWriteImmediate, fp(EOR))
op(0x58, DirectImmediateModify, fp(EOR))
op(0x59, IndirectXWriteIndirectY, fp(EOR))
op(0x5a, DirectReadWord, fp(CPW))
op(0x5a, DirectCompareWord, fp(CPW))
op(0x5b, DirectIndexedModify, fp(LSR), X)
op(0x5c, ImpliedModify, fp(LSR), A)
op(0x5d, Transfer, A, X)
@ -108,7 +108,7 @@ auto SPC700::instruction() -> void {
op(0x66, IndirectXRead, fp(CMP))
op(0x67, IndexedIndirectRead, fp(CMP), X)
op(0x68, ImmediateRead, fp(CMP), A)
op(0x69, DirectWriteDirect, fp(CMP))
op(0x69, DirectDirectCompare, fp(CMP))
op(0x6a, AbsoluteBitModify, 3)
op(0x6b, DirectModify, fp(ROR))
op(0x6c, AbsoluteModify, fp(ROR))
@ -123,8 +123,8 @@ auto SPC700::instruction() -> void {
op(0x75, AbsoluteIndexedRead, fp(CMP), X)
op(0x76, AbsoluteIndexedRead, fp(CMP), Y)
op(0x77, IndirectIndexedRead, fp(CMP), Y)
op(0x78, DirectWriteImmediate, fp(CMP))
op(0x79, IndirectXWriteIndirectY, fp(CMP))
op(0x78, DirectImmediateCompare, fp(CMP))
op(0x79, IndirectXCompareIndirectY, fp(CMP))
op(0x7a, DirectReadWord, fp(ADW))
op(0x7b, DirectIndexedModify, fp(ROR), X)
op(0x7c, ImpliedModify, fp(ROR), A)
@ -140,13 +140,13 @@ auto SPC700::instruction() -> void {
op(0x86, IndirectXRead, fp(ADC))
op(0x87, IndexedIndirectRead, fp(ADC), X)
op(0x88, ImmediateRead, fp(ADC), A)
op(0x89, DirectWriteDirect, fp(ADC))
op(0x89, DirectDirectModify, fp(ADC))
op(0x8a, AbsoluteBitModify, 4)
op(0x8b, DirectModify, fp(DEC))
op(0x8c, AbsoluteModify, fp(DEC))
op(0x8d, ImmediateRead, fp(LD), Y)
op(0x8e, PullP)
op(0x8f, DirectWriteImmediate, fp(ST))
op(0x8f, DirectImmediateWrite)
op(0x90, Branch, CF == 0)
op(0x91, CallTable, 9)
op(0x92, AbsoluteBitSet, 4, false)
@ -155,7 +155,7 @@ auto SPC700::instruction() -> void {
op(0x95, AbsoluteIndexedRead, fp(ADC), X)
op(0x96, AbsoluteIndexedRead, fp(ADC), Y)
op(0x97, IndirectIndexedRead, fp(ADC), Y)
op(0x98, DirectWriteImmediate, fp(ADC))
op(0x98, DirectImmediateModify, fp(ADC))
op(0x99, IndirectXWriteIndirectY, fp(ADC))
op(0x9a, DirectReadWord, fp(SBW))
op(0x9b, DirectIndexedModify, fp(DEC), X)
@ -172,7 +172,7 @@ auto SPC700::instruction() -> void {
op(0xa6, IndirectXRead, fp(SBC))
op(0xa7, IndexedIndirectRead, fp(SBC), X)
op(0xa8, ImmediateRead, fp(SBC), A)
op(0xa9, DirectWriteDirect, fp(SBC))
op(0xa9, DirectDirectModify, fp(SBC))
op(0xaa, AbsoluteBitModify, 5)
op(0xab, DirectModify, fp(INC))
op(0xac, AbsoluteModify, fp(INC))
@ -187,7 +187,7 @@ auto SPC700::instruction() -> void {
op(0xb5, AbsoluteIndexedRead, fp(SBC), X)
op(0xb6, AbsoluteIndexedRead, fp(SBC), Y)
op(0xb7, IndirectIndexedRead, fp(SBC), Y)
op(0xb8, DirectWriteImmediate, fp(SBC))
op(0xb8, DirectImmediateModify, fp(SBC))
op(0xb9, IndirectXWriteIndirectY, fp(SBC))
op(0xba, DirectReadWord, fp(LDW))
op(0xbb, DirectIndexedModify, fp(INC), X)
@ -253,7 +253,7 @@ auto SPC700::instruction() -> void {
op(0xf7, IndirectIndexedRead, fp(LD), Y)
op(0xf8, DirectRead, fp(LD), X)
op(0xf9, DirectIndexedRead, fp(LD), X, Y)
op(0xfa, DirectWriteDirect, fp(ST))
op(0xfa, DirectDirectWrite)
op(0xfb, DirectIndexedRead, fp(LD), Y, X)
op(0xfc, ImpliedModify, fp(INC), Y)
op(0xfd, Transfer, A, Y)

View File

@ -1,16 +1,16 @@
auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
uint16 address = fetch();
address |= fetch() << 8;
uint3 bit = address >> 13;
address &= 0x1fff;
uint8 data = read(address);
switch(mode) {
case 0: //or addr:bit
idle(address);
idle();
CF |= data.bit(bit);
break;
case 1: //or !addr:bit
idle(address);
idle();
CF |= !data.bit(bit);
break;
case 2: //and addr:bit
@ -20,14 +20,14 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
CF &= !data.bit(bit);
break;
case 4: //eor addr:bit
idle(address);
idle();
CF ^= data.bit(bit);
break;
case 5: //ld addr:bit
CF = data.bit(bit);
break;
case 6: //st addr:bit
idle(address);
idle();
data.bit(bit) = CF;
write(address, data);
break;
@ -39,118 +39,118 @@ auto SPC700::instructionAbsoluteBitModify(uint3 mode) -> void {
}
auto SPC700::instructionAbsoluteBitSet(uint3 bit, bool value) -> void {
uint8 address = read(PC++);
uint8 data = read(page(address));
uint8 address = fetch();
uint8 data = load(address);
data.bit(bit) = value;
write(page(address), data);
store(address, data);
}
auto SPC700::instructionAbsoluteRead(fpb op, uint8& target) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
uint16 address = fetch();
address |= fetch() << 8;
uint8 data = read(address);
target = alu(target, data);
}
auto SPC700::instructionAbsoluteModify(fps op) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
uint16 address = fetch();
address |= fetch() << 8;
uint8 data = read(address);
write(address, alu(data));
}
auto SPC700::instructionAbsoluteWrite(uint8& data) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
uint16 address = fetch();
address |= fetch() << 8;
read(address);
write(address, data);
}
auto SPC700::instructionAbsoluteIndexedRead(fpb op, uint8& index) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
idle(PC - 1);
uint16 address = fetch();
address |= fetch() << 8;
idle();
uint8 data = read(address + index);
A = alu(A, data);
}
auto SPC700::instructionAbsoluteIndexedWrite(uint8& index) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
idle(PC - 1);
uint16 address = fetch();
address |= fetch() << 8;
idle();
read(address + index);
write(address + index, A);
}
auto SPC700::instructionBranch(bool take) -> void {
uint8 data = read(PC++);
uint8 data = fetch();
if(!take) return;
idle(PC - 1);
idle(PC - 1);
idle();
idle();
PC += (int8)data;
}
auto SPC700::instructionBranchBit(uint3 bit, bool match) -> void {
uint8 address = read(PC++);
uint8 data = read(page(address));
idle(page(address));
uint8 displacement = read(PC++);
uint8 address = fetch();
uint8 data = load(address);
load(address);
uint8 displacement = fetch();
if(data.bit(bit) != match) return;
idle(PC - 1);
idle(PC - 1);
idle();
idle();
PC += (int8)displacement;
}
auto SPC700::instructionBranchNotDirect() -> void {
uint8 address = read(PC++);
uint8 data = read(page(address));
idle(page(address));
uint8 displacement = read(PC++);
uint8 address = fetch();
uint8 data = load(address);
load(address);
uint8 displacement = fetch();
if(A == data) return;
idle(PC - 1);
idle(PC - 1);
idle();
idle();
PC += (int8)displacement;
}
auto SPC700::instructionBranchNotDirectDecrement() -> void {
uint8 address = read(PC++);
uint8 data = read(page(address));
write(page(address), --data);
uint8 displacement = read(PC++);
uint8 address = fetch();
uint8 data = load(address);
store(address, --data);
uint8 displacement = fetch();
if(data == 0) return;
idle(PC - 1);
idle(PC - 1);
idle();
idle();
PC += (int8)displacement;
}
auto SPC700::instructionBranchNotDirectX() -> void {
uint8 address = read(PC++);
idle(PC - 1);
uint8 data = read(page(address + X));
idle(page(address + X));
uint8 displacement = read(PC++);
uint8 address = fetch();
idle();
uint8 data = load(address + X);
idle();
uint8 displacement = fetch();
if(A == data) return;
idle(PC - 1);
idle(PC - 1);
idle();
idle();
PC += (int8)displacement;
}
auto SPC700::instructionBranchNotYDecrement() -> void {
idle(PC);
idle(PC);
uint8 displacement = read(PC++);
read(PC);
idle();
uint8 displacement = fetch();
if(--Y == 0) return;
idle(PC - 1);
idle(PC - 1);
idle();
idle();
PC += (int8)displacement;
}
auto SPC700::instructionBreak() -> void {
idle(PC);
write(stack(S--), PC >> 8);
write(stack(S--), PC >> 0);
write(stack(S--), P);
idle(stack(S + 1));
read(PC);
push(PC >> 8);
push(PC >> 0);
push(P);
idle();
uint16 address = read(0xffde + 0);
address |= read(0xffde + 1) << 8;
PC = address;
@ -159,31 +159,31 @@ auto SPC700::instructionBreak() -> void {
}
auto SPC700::instructionCallAbsolute() -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
idle(stack(S));
write(stack(S--), PC >> 8);
write(stack(S--), PC >> 0);
idle(stack(S + 1));
idle(stack(S + 1));
uint16 address = fetch();
address |= fetch() << 8;
idle();
push(PC >> 8);
push(PC >> 0);
idle();
idle();
PC = address;
}
auto SPC700::instructionCallPage() -> void {
uint8 address = read(PC++);
idle(PC - 1);
write(stack(S--), PC >> 8);
write(stack(S--), PC >> 0);
idle(stack(S + 1));
uint8 address = fetch();
idle();
push(PC >> 8);
push(PC >> 0);
idle();
PC = 0xff00 | address;
}
auto SPC700::instructionCallTable(uint4 vector) -> void {
idle(PC);
idle(stack(S));
write(stack(S--), PC >> 8);
write(stack(S--), PC >> 0);
idle(stack(S + 1));
read(PC);
idle();
push(PC >> 8);
push(PC >> 0);
idle();
uint16 address = 0xffde - (vector << 1);
uint16 pc = read(address + 0);
pc |= read(address + 1) << 8;
@ -191,14 +191,14 @@ auto SPC700::instructionCallTable(uint4 vector) -> void {
}
auto SPC700::instructionComplementCarry() -> void {
idle(PC);
idle(PC);
read(PC);
idle();
CF = !CF;
}
auto SPC700::instructionDecimalAdjustAdd() -> void {
idle(PC);
idle(PC);
read(PC);
idle();
if(CF || A > 0x99) {
A += 0x60;
CF = 1;
@ -211,8 +211,8 @@ auto SPC700::instructionDecimalAdjustAdd() -> void {
}
auto SPC700::instructionDecimalAdjustSub() -> void {
idle(PC);
idle(PC);
read(PC);
idle();
if(!CF || A > 0x99) {
A -= 0x60;
CF = 0;
@ -225,99 +225,136 @@ auto SPC700::instructionDecimalAdjustSub() -> void {
}
auto SPC700::instructionDirectRead(fpb op, uint8& target) -> void {
uint8 address = read(PC++);
uint8 data = read(page(address));
uint8 address = fetch();
uint8 data = load(address);
target = alu(target, data);
}
auto SPC700::instructionDirectModify(fps op) -> void {
uint8 address = read(PC++);
uint8 data = read(page(address));
write(page(address), alu(data));
uint8 address = fetch();
uint8 data = load(address);
store(address, alu(data));
}
auto SPC700::instructionDirectWriteWord() -> void {
uint8 address = read(PC++);
idle(page(address + 0));
write(page(address + 0), A);
write(page(address + 1), Y);
auto SPC700::instructionDirectWrite(uint8& data) -> void {
uint8 address = fetch();
load(address);
store(address, data);
}
auto SPC700::instructionDirectWriteDirect(fpb op) -> void {
uint8 source = read(PC++);
uint8 rhs = read(page(source));
uint8 target = read(PC++);
uint8 lhs;
if(op != &SPC700::algorithmST) lhs = read(page(target));
auto SPC700::instructionDirectDirectCompare(fpb op) -> void {
uint8 source = fetch();
uint8 rhs = load(source);
uint8 target = fetch();
uint8 lhs = load(target);
lhs = alu(lhs, rhs);
op != &SPC700::algorithmCMP ? write(page(target), lhs) : idle(page(target));
load(target);
}
auto SPC700::instructionDirectWriteImmediate(fpb op) -> void {
uint8 immediate = read(PC++);
uint8 address = read(PC++);
uint8 data = read(page(address));
auto SPC700::instructionDirectDirectModify(fpb op) -> void {
uint8 source = fetch();
uint8 rhs = load(source);
uint8 target = fetch();
uint8 lhs = load(target);
lhs = alu(lhs, rhs);
store(target, lhs);
}
auto SPC700::instructionDirectDirectWrite() -> void {
uint8 source = fetch();
uint8 data = load(source);
uint8 target = fetch();
store(target, data);
}
auto SPC700::instructionDirectImmediateCompare(fpb op) -> void {
uint8 immediate = fetch();
uint8 address = fetch();
uint8 data = load(address);
data = alu(data, immediate);
op != &SPC700::algorithmCMP ? write(page(address), data) : idle(page(address));
load(address);
}
auto SPC700::instructionDirectImmediateModify(fpb op) -> void {
uint8 immediate = fetch();
uint8 address = fetch();
uint8 data = load(address);
data = alu(data, immediate);
store(address, data);
}
auto SPC700::instructionDirectImmediateWrite() -> void {
uint8 immediate = fetch();
uint8 address = fetch();
load(address);
store(address, immediate);
}
auto SPC700::instructionDirectCompareWord(fpw op) -> void {
uint8 address = fetch();
uint16 data = load(address + 0);
data |= load(address + 1) << 8;
YA = alu(YA, data);
}
auto SPC700::instructionDirectReadWord(fpw op) -> void {
uint8 address = read(PC++);
uint16 data = read(page(address + 0));
if(op != &SPC700::algorithmCPW) idle(page(address + 0));
data |= read(page(address + 1)) << 8;
uint8 address = fetch();
uint16 data = load(address + 0);
load(address + 0);
data |= load(address + 1) << 8;
YA = alu(YA, data);
}
auto SPC700::instructionDirectModifyWord(int adjust) -> void {
uint8 address = read(PC++);
uint16 data = read(page(address + 0)) + adjust;
write(page(address + 0), data >> 0);
data += read(page(address + 1)) << 8;
write(page(address + 1), data >> 8);
uint8 address = fetch();
uint16 data = load(address + 0) + adjust;
store(address + 0, data >> 0);
data += load(address + 1) << 8;
store(address + 1, data >> 8);
ZF = data == 0;
NF = data & 0x8000;
}
auto SPC700::instructionDirectWrite(uint8& data) -> void {
uint8 address = read(PC++);
idle(page(address));
write(page(address), data);
auto SPC700::instructionDirectWriteWord() -> void {
uint8 address = fetch();
load(address + 0);
store(address + 0, A);
store(address + 1, Y);
}
auto SPC700::instructionDirectIndexedRead(fpb op, uint8& target, uint8& index) -> void {
uint8 address = read(PC++);
idle(PC - 1);
uint8 data = read(page(address + index));
uint8 address = fetch();
idle();
uint8 data = load(address + index);
target = alu(target, data);
}
auto SPC700::instructionDirectIndexedModify(fps op, uint8& index) -> void {
uint8 address = read(PC++);
idle(PC - 1);
uint8 data = read(page(address + index));
write(page(address + index), alu(data));
uint8 address = fetch();
idle();
uint8 data = load(address + index);
store(address + index, alu(data));
}
auto SPC700::instructionDirectIndexedWrite(uint8& data, uint8& index) -> void {
uint8 address = read(PC++);
idle(PC - 1);
idle(page(address + index));
write(page(address + index), data);
uint8 address = fetch();
idle();
load(address + index);
store(address + index, data);
}
auto SPC700::instructionDivide() -> void {
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
read(PC);
idle();
idle();
idle();
idle();
idle();
idle();
idle();
idle();
idle();
idle();
uint16 ya = YA;
//overflow set if quotient >= 256
HF = (Y & 15) >= (X & 15);
@ -338,125 +375,133 @@ auto SPC700::instructionDivide() -> void {
}
auto SPC700::instructionExchangeNibble() -> void {
idle(PC);
idle(PC);
idle(PC);
idle(PC);
read(PC);
idle();
idle();
idle();
A = A >> 4 | A << 4;
ZF = A == 0;
NF = A & 0x80;
}
auto SPC700::instructionFlagSet(bool& flag, bool value) -> void {
idle(PC);
if(&flag == &IF) idle(PC);
read(PC);
if(&flag == &IF) idle();
flag = value;
}
auto SPC700::instructionImmediateRead(fpb op, uint8& target) -> void {
uint8 data = read(PC++);
uint8 data = fetch();
target = alu(target, data);
}
auto SPC700::instructionImpliedModify(fps op, uint8& target) -> void {
idle(PC);
read(PC);
target = alu(target);
}
auto SPC700::instructionIndexedIndirectRead(fpb op, uint8& index) -> void {
uint8 indirect = read(PC++);
idle(PC - 1);
uint16 address = read(page(indirect + index + 0));
address |= read(page(indirect + index + 1)) << 8;
uint8 indirect = fetch();
idle();
uint16 address = load(indirect + index + 0);
address |= load(indirect + index + 1) << 8;
uint8 data = read(address);
A = alu(A, data);
}
auto SPC700::instructionIndexedIndirectWrite(uint8& data, uint8& index) -> void {
uint8 indirect = read(PC++);
idle(PC - 1);
uint16 address = read(page(indirect + index + 0));
address |= read(page(indirect + index + 1)) << 8;
uint8 indirect = fetch();
idle();
uint16 address = load(indirect + index + 0);
address |= load(indirect + index + 1) << 8;
read(address);
write(address, data);
}
auto SPC700::instructionIndirectIndexedRead(fpb op, uint8& index) -> void {
uint8 indirect = read(PC++);
uint16 address = read(page(indirect + 0));
address |= read(page(indirect + 1)) << 8;
idle(page(indirect + 1));
uint8 indirect = fetch();
uint16 address = load(indirect + 0);
address |= load(indirect + 1) << 8;
idle();
uint8 data = read(address + index);
A = alu(A, data);
}
auto SPC700::instructionIndirectIndexedWrite(uint8& data, uint8& index) -> void {
uint8 indirect = read(PC++);
uint16 address = read(page(indirect + 0));
address |= read(page(indirect + 1)) << 8;
idle(page(indirect + 1));
uint8 indirect = fetch();
uint16 address = load(indirect + 0);
address |= load(indirect + 1) << 8;
idle();
read(address + index);
write(address + index, data);
}
auto SPC700::instructionIndirectXRead(fpb op) -> void {
idle(PC);
uint8 data = read(page(X));
read(PC);
uint8 data = load(X);
A = alu(A, data);
}
auto SPC700::instructionIndirectXWrite(uint8& data) -> void {
idle(PC);
idle(page(X));
write(page(X), data);
read(PC);
load(X);
store(X, data);
}
auto SPC700::instructionIndirectXIncrementRead(uint8& data) -> void {
idle(PC);
idle(page(X), false); //quirk: does not read internal SMP registers
data = read(page(X++));
read(PC);
data = load(X++);
idle(); //quirk: consumes extra idle cycle compared to most read instructions
ZF = data == 0;
NF = data & 0x80;
}
auto SPC700::instructionIndirectXIncrementWrite(uint8& data) -> void {
idle(PC);
idle(page(X), false); //quirk: does not read internal SMP registers
write(page(X++), data);
read(PC);
idle(); //quirk: not a read cycle as with most write instructions
store(X++, data);
}
auto SPC700::instructionIndirectXCompareIndirectY(fpb op) -> void {
read(PC);
uint8 rhs = load(Y);
uint8 lhs = load(X);
lhs = alu(lhs, rhs);
load(X);
}
auto SPC700::instructionIndirectXWriteIndirectY(fpb op) -> void {
idle(PC);
uint8 rhs = read(page(Y));
uint8 lhs = read(page(X));
read(PC);
uint8 rhs = load(Y);
uint8 lhs = load(X);
lhs = alu(lhs, rhs);
op != &SPC700::algorithmCMP ? write(page(X), lhs) : idle(page(X));
store(X, lhs);
}
auto SPC700::instructionJumpAbsolute() -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
uint16 address = fetch();
address |= fetch() << 8;
PC = address;
}
auto SPC700::instructionJumpIndirectX() -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
idle(PC - 1);
uint16 address = fetch();
address |= fetch() << 8;
idle();
uint16 pc = read(address + X + 0);
pc |= read(address + X + 1) << 8;
PC = pc;
}
auto SPC700::instructionMultiply() -> void {
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
idle(PC);
read(PC);
idle();
idle();
idle();
idle();
idle();
idle();
idle();
uint16 ya = Y * A;
A = ya >> 0;
Y = ya >> 8;
@ -466,70 +511,70 @@ auto SPC700::instructionMultiply() -> void {
}
auto SPC700::instructionNoOperation() -> void {
idle(PC);
read(PC);
}
auto SPC700::instructionOverflowClear() -> void {
idle(PC);
read(PC);
HF = 0;
VF = 0;
}
auto SPC700::instructionPull(uint8& data) -> void {
idle(PC);
idle(stack(S));
data = read(stack(++S));
read(PC);
idle();
data = pull();
}
auto SPC700::instructionPullP() -> void {
idle(PC);
idle(stack(S));
P = read(stack(++S));
read(PC);
idle();
P = pull();
}
auto SPC700::instructionPush(uint8 data) -> void {
idle(PC);
write(stack(S--), data);
idle(stack(S + 1));
read(PC);
push(data);
idle();
}
auto SPC700::instructionReturnInterrupt() -> void {
idle(PC);
idle(stack(S));
P = read(stack(++S));
uint16 address = read(stack(++S));
address |= read(stack(++S)) << 8;
read(PC);
idle();
P = pull();
uint16 address = pull();
address |= pull() << 8;
PC = address;
}
auto SPC700::instructionReturnSubroutine() -> void {
idle(PC);
idle(stack(S));
uint16 address = read(stack(++S));
address |= read(stack(++S)) << 8;
read(PC);
idle();
uint16 address = pull();
address |= pull() << 8;
PC = address;
}
auto SPC700::instructionStop() -> void {
r.stop = true;
while(r.stop && !synchronizing()) {
idle(PC);
idle(PC);
read(PC);
idle();
}
}
auto SPC700::instructionTestSetBitsAbsolute(bool set) -> void {
uint16 address = read(PC++);
address |= read(PC++) << 8;
uint16 address = fetch();
address |= fetch() << 8;
uint8 data = read(address);
ZF = (A - data) == 0;
NF = (A - data) & 0x80;
idle(address);
read(address);
write(address, set ? data | A : data & ~A);
}
auto SPC700::instructionTransfer(uint8& from, uint8& to) -> void {
idle(PC);
read(PC);
to = from;
if(&to == &S) return;
ZF = to == 0;
@ -539,7 +584,7 @@ auto SPC700::instructionTransfer(uint8& from, uint8& to) -> void {
auto SPC700::instructionWait() -> void {
r.wait = true;
while(r.wait && !synchronizing()) {
idle(PC);
idle(PC);
read(PC);
idle();
}
}

View File

@ -0,0 +1,19 @@
auto SPC700::fetch() -> uint8 {
return read(PC++);
}
auto SPC700::load(uint8 address) -> uint8 {
return read(PF << 8 | address);
}
auto SPC700::store(uint8 address, uint8 data) -> void {
return write(PF << 8 | address, data);
}
auto SPC700::pull() -> uint8 {
return read(1 << 8 | ++S);
}
auto SPC700::push(uint8 data) -> void {
return write(1 << 8 | S--, data);
}

View File

@ -22,20 +22,13 @@ namespace Processor {
#define alu (this->*op)
#include "memory.cpp"
#include "algorithms.cpp"
#include "instructions.cpp"
#include "instruction.cpp"
#include "serialization.cpp"
#include "disassembler.cpp"
auto SPC700::page(uint8 address) const -> uint16 {
return PF << 8 | address;
}
auto SPC700::stack(uint8 address) const -> uint16 {
return 1 << 8 | address;
}
auto SPC700::power() -> void {
PC = 0x0000;
YA = 0x0000;

View File

@ -3,7 +3,7 @@
namespace Processor {
struct SPC700 {
virtual auto idle(uint16 address, bool read = true) -> void = 0;
virtual auto idle() -> void = 0;
virtual auto read(uint16 address) -> uint8 = 0;
virtual auto write(uint16 addessr, uint8 data) -> void = 0;
virtual auto synchronizing() const -> bool = 0;
@ -11,11 +11,15 @@ struct SPC700 {
virtual auto readDisassembler(uint16 address) -> uint8 { return 0; }
//spc700.cpp
inline auto page(uint8 address) const -> uint16;
inline auto stack(uint8 address) const -> uint16;
auto power() -> void;
//memory.cpp
inline auto fetch() -> uint8;
inline auto load(uint8 address) -> uint8;
inline auto store(uint8 address, uint8 data) -> void;
inline auto pull() -> uint8;
inline auto push(uint8 data) -> void;
//instruction.cpp
auto instruction() -> void;
@ -33,7 +37,6 @@ struct SPC700 {
auto algorithmROL(uint8) -> uint8;
auto algorithmROR(uint8) -> uint8;
auto algorithmSBC(uint8, uint8) -> uint8;
auto algorithmST (uint8, uint8) -> uint8;
auto algorithmADW(uint16, uint16) -> uint16;
auto algorithmCPW(uint16, uint16) -> uint16;
auto algorithmLDW(uint16, uint16) -> uint16;
@ -67,8 +70,13 @@ struct SPC700 {
auto instructionDirectRead(fpb, uint8&) -> void;
auto instructionDirectModify(fps) -> void;
auto instructionDirectWrite(uint8&) -> void;
auto instructionDirectWriteDirect(fpb) -> void;
auto instructionDirectWriteImmediate(fpb) -> void;
auto instructionDirectDirectCompare(fpb) -> void;
auto instructionDirectDirectModify(fpb) -> void;
auto instructionDirectDirectWrite() -> void;
auto instructionDirectImmediateCompare(fpb) -> void;
auto instructionDirectImmediateModify(fpb) -> void;
auto instructionDirectImmediateWrite() -> void;
auto instructionDirectCompareWord(fpw) -> void;
auto instructionDirectReadWord(fpw) -> void;
auto instructionDirectModifyWord(int) -> void;
auto instructionDirectWriteWord() -> void;
@ -88,6 +96,7 @@ struct SPC700 {
auto instructionIndirectXWrite(uint8&) -> void;
auto instructionIndirectXIncrementRead(uint8&) -> void;
auto instructionIndirectXIncrementWrite(uint8&) -> void;
auto instructionIndirectXCompareIndirectY(fpb) -> void;
auto instructionIndirectXWriteIndirectY(fpb) -> void;
auto instructionJumpAbsolute() -> void;
auto instructionJumpIndirectX() -> void;

View File

@ -96,10 +96,8 @@ auto SMP::busWrite(uint16 addr, uint8 data) -> void {
io.ramWritable = data.bit (1);
io.ramDisable = data.bit (2);
io.timersEnable = data.bit (3);
io.ramSpeed = data.bits(4,5);
io.romIOSpeed = data.bits(6,7);
io.timerStep = (1 << io.romIOSpeed) + (2 << io.ramSpeed);
io.externalWaitStates = data.bits(4,5);
io.internalWaitStates = data.bits(6,7);
timer0.synchronizeStage1();
timer1.synchronizeStage1();
@ -199,30 +197,19 @@ auto SMP::busWrite(uint16 addr, uint8 data) -> void {
ramWrite(addr, data); //all writes, even to I/O registers, appear on bus
}
auto SMP::speed(uint16 addr) const -> uint {
static const uint waitStates[4] = {1, 2, 5, 10};
if((addr & 0xfff0) == 0x00f0) return waitStates[io.romIOSpeed];
if(addr >= 0xffc0 && io.iplromEnable) return waitStates[io.romIOSpeed];
return waitStates[io.ramSpeed];
}
auto SMP::idle(uint16 addr, bool read) -> void {
step(24 * speed(addr));
if(read) busRead(addr);
cycleEdge();
auto SMP::idle() -> void {
wait();
}
auto SMP::read(uint16 addr) -> uint8 {
step(24 * speed(addr));
wait(addr);
uint8 data = busRead(addr);
cycleEdge();
return data;
}
auto SMP::write(uint16 addr, uint8 data) -> void {
step(24 * speed(addr));
wait(addr);
busWrite(addr, data);
cycleEdge();
}
auto SMP::readDisassembler(uint16 addr) -> uint8 {

View File

@ -4,7 +4,6 @@ auto SMP::serialize(serializer& s) -> void {
s.integer(io.clockCounter);
s.integer(io.dspCounter);
s.integer(io.timerStep);
s.integer(io.apu0);
s.integer(io.apu1);
@ -15,8 +14,8 @@ auto SMP::serialize(serializer& s) -> void {
s.integer(io.ramWritable);
s.integer(io.ramDisable);
s.integer(io.timersEnable);
s.integer(io.ramSpeed);
s.integer(io.romIOSpeed);
s.integer(io.externalWaitStates);
s.integer(io.internalWaitStates);
s.integer(io.iplromEnable);

View File

@ -34,7 +34,7 @@ auto SMP::load(Markup::Node node) -> bool {
auto SMP::power() -> void {
SPC700::power();
create(Enter, system.apuFrequency());
create(Enter, system.apuFrequency() / 12.0);
r.pc.byte.l = iplrom[62];
r.pc.byte.h = iplrom[63];
@ -42,7 +42,6 @@ auto SMP::power() -> void {
//timing
io.clockCounter = 0;
io.dspCounter = 0;
io.timerStep = (1 << 0) + (2 << 0);
//external
io.apu0 = 0x00;
@ -55,8 +54,8 @@ auto SMP::power() -> void {
io.ramWritable = true;
io.ramDisable = false;
io.timersEnable = true;
io.ramSpeed = 0;
io.romIOSpeed = 0;
io.externalWaitStates = 0;
io.internalWaitStates = 0;
//$00f1
io.iplromEnable = true;

View File

@ -21,7 +21,6 @@ private:
//timing
uint clockCounter;
uint dspCounter;
uint timerStep;
//external
uint8 apu0;
@ -34,8 +33,8 @@ private:
uint1 ramWritable;
uint1 ramDisable;
uint1 timersEnable;
uint2 ramSpeed;
uint2 romIOSpeed;
uint2 externalWaitStates;
uint2 internalWaitStates;
//$00f1
bool iplromEnable;
@ -63,8 +62,7 @@ private:
auto busRead(uint16 addr) -> uint8;
auto busWrite(uint16 addr, uint8 data) -> void;
auto speed(uint16 addr) const -> uint;
auto idle(uint16 addr, bool read = true) -> void override;
auto idle() -> void override;
auto read(uint16 addr) -> uint8 override;
auto write(uint16 addr, uint8 data) -> void override;
@ -80,16 +78,17 @@ private:
bool enable;
uint8 target;
auto tick() -> void;
auto step(uint clocks) -> void;
auto synchronizeStage1() -> void;
};
Timer<192> timer0;
Timer<192> timer1;
Timer< 24> timer2;
Timer<128> timer0;
Timer<128> timer1;
Timer< 16> timer2;
alwaysinline auto step(uint clocks) -> void;
alwaysinline auto cycleEdge() -> void;
inline auto wait(maybe<uint16> address = nothing) -> void;
inline auto step(uint clocks) -> void;
inline auto stepTimers(uint clocks) -> void;
};
extern SMP smp;

View File

@ -1,3 +1,24 @@
//DSP clock (~24576khz) / 12 (~2048khz) is fed into the SMP
//from here, the wait states value is really a clock divider of {2, 4, 8, 16}
//because dividers of 8 and 16 are not evenly divislbe into 12, the SMP glitches
//in these two cases, the SMP ends up consuming 10 and 20 cycles instead
//this causes unpredictable behavior on real hardware
//sometimes the SMP will run far slower than expected
//other times (and more likely), the SMP will deadlock until the system is reset
//the timers are not affected by this and advance by their expected values
auto SMP::wait(maybe<uint16> addr) -> void {
static const uint cycleWaitStates[4] = {2, 4, 10, 20};
static const uint timerWaitStates[4] = {2, 4, 8, 16};
uint waitStates = io.externalWaitStates;
if(!addr) waitStates = io.internalWaitStates; //idle cycles
else if((*addr & 0xfff0) == 0x00f0) waitStates = io.internalWaitStates; //IO registers
else if(*addr >= 0xffc0 && io.iplromEnable) waitStates = io.internalWaitStates; //IPLROM
step(cycleWaitStates[waitStates]);
stepTimers(timerWaitStates[waitStates]);
}
auto SMP::step(uint clocks) -> void {
Thread::step(clocks);
synchronize(dsp);
@ -11,15 +32,15 @@ auto SMP::step(uint clocks) -> void {
#endif
}
auto SMP::cycleEdge() -> void {
timer0.tick();
timer1.tick();
timer2.tick();
auto SMP::stepTimers(uint clocks) -> void {
timer0.step(clocks);
timer1.step(clocks);
timer2.step(clocks);
}
template<uint Frequency> auto SMP::Timer<Frequency>::tick() -> void {
template<uint Frequency> auto SMP::Timer<Frequency>::step(uint clocks) -> void {
//stage 0 increment
stage0 += smp.io.timerStep;
stage0 += clocks;
if(stage0 < Frequency) return;
stage0 -= Frequency;