Merge branch 'newcpu'

This commit is contained in:
edmccard 2012-04-14 07:38:57 -04:00
commit bcc294b888
23 changed files with 5273 additions and 2071 deletions

View File

@ -16,6 +16,15 @@ Build by running `make` in the `src` directory; if the dependencies aren't insta
make GTKD=/path/to/gtkd DERELICT=/path/to/Derelict2 make GTKD=/path/to/gtkd DERELICT=/path/to/Derelict2
``` ```
### Testing
There are tests for the 6502/65C02 emulation:
```
cd test
rdmd runtests.d --help
```
### Use ### Use
For now, see README.orig For now, see README.orig

1401
cmos.txt Normal file

File diff suppressed because it is too large Load Diff

1609
nmos.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,13 @@
COMPILE_OPTS = -op -Jdata -I$(GTKD)/src \ COMPILE_OPTS = -op -Jdata -I$(GTKD)/src \
-I$(GTKD)/srcgl -I$(DERELICT)/import \ -I$(GTKD)/srcgl -I$(DERELICT)/import \
-inline -release -O -noboundscheck -inline -release -O -noboundscheck \
-version=OpNestedSwitch
LINK_OPTS = -L-lpthread -L-lGL -L-ldl -L-lX11 \ LINK_OPTS = -L-lpthread -L-lGL -L-ldl -L-lX11 \
-L-L$(GTKD) -L-lgtkd -L-lgtkdgl \ -L-L$(GTKD) -L-lgtkd -L-lgtkdgl \
-L-L$(DERELICT)/lib -L-lDerelictSDL -L-lDerelictUtil -L-L$(DERELICT)/lib -L-lDerelictSDL -L-lDerelictUtil
ALL_SRC = $(shell find -name "*.d" \! -path "./cpu/*") ALL_SRC = $(shell find -name "*.d" \! -name "ctfe*")
all: ${ALL_SRC} all: ${ALL_SRC}
dmd $(COMPILE_OPTS) ${ALL_SRC} -oftwoapple ${LINK_OPTS} dmd $(COMPILE_OPTS) ${ALL_SRC} -oftwoapple ${LINK_OPTS}

1269
src/cpu/ctfe_d6502.d Normal file

File diff suppressed because it is too large Load Diff

173
src/cpu/d6502.d Normal file
View File

@ -0,0 +1,173 @@
/+
+ cpu/d6502.d
+
+ Copyright: 2012 Ed McCardell, 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module cpu.d6502;
import std.array, std.format;
import cpu.ctfe_d6502;
template is6502(T)
{
enum is6502 = __traits(getMember, T, "_chip") == "6502";
}
template is65C02(T)
{
enum is65C02 = __traits(getMember, T, "_chip") == "65C02";
}
final class Cpu(string chip, MEM, CLK)
if (__traits(compiles, {
MEM m; ubyte val; ushort addr;
val = m.read(addr);
m.write(addr, val);
CLK c; int cycles;
version(Cumulative) c.tick(cycles);
else c.tick();
}))
{
static assert(chip == "6502" || chip == "65C02" || chip == "65c02");
enum _isCpu = true;
enum _chip = (chip == "6502" ? "6502" : "65C02");
MEM memory;
CLK clock;
ubyte A, X, Y, S;
ushort PC;
// The status flags.
ubyte N, Z;
bool V, D, I, C;
version(OpDelegates)
{
mixin(OpArrayDef());
version(Cumulative) { int cycles; }
ushort address, base;
ubyte data;
}
this(MEM memory, CLK clock)
{
this.memory = memory;
this.clock = clock;
version(OpDelegates) mixin(OpArrayInit());
}
final void statusFromByte(ubyte p)
{
N = p;
V = ((p & 0x40) != 0);
D = ((p & 0x08) != 0);
I = ((p & 0x04) != 0);
Z = ((p & 0x02) ? 0 : 1);
C = ((p & 0x01) != 0);
}
final ubyte statusToByte()
{
return (C ? 0x01 : 0) |
((Z == 0) ? 0x02 : 0) |
(I ? 0x04 : 0) |
(D ? 0x08 : 0) |
0x30 | // break and reserved both set
(V ? 0x40 : 0) |
(N & 0x80);
}
bool keepRunning;
bool signalActive;
bool resetLow;
final void run(bool continuous)
{
keepRunning = continuous;
ubyte opcode;
static if (!opArray)
{
version(Cumulative) { int cycles; }
ushort address, base;
ubyte data;
}
do
{
version(Cumulative) { static if (!opArray) cycles = 1; }
else { clock.tick(); }
if (signalActive) handleSignals();
opcode = memory.read(PC++);
mixin(OpExecute(_chip));
} while (keepRunning);
}
// TODO: irq/nmi
void handleSignals()
{
if (resetLow) doReset();
// XXX fix when more than one signal
signalActive = resetLow;
}
void doReset()
{
mixin(Tick() ~ Tick() ~
Peek(STACK) ~ DecSP() ~
Peek(STACK) ~ DecSP() ~
Peek(STACK) ~ DecSP());
I = true;
resetLow = false;
mixin(ReadWord(_PC, "RESET_VECTOR") ~
Done());
}
version(OpDelegates) mixin (OpMethods(_chip));
}
enum ushort IRQ_VECTOR = 0xFFFE;
enum ushort RESET_VECTOR = 0xFFFC;
//alias Cpu!("6502", false, false) T1;
//alias Cpu!("6502", false, true) T2;
//alias Cpu!("6502", true, false) T3;
//alias Cpu!("6502", true, true) T4;
//alias Cpu!("65C02", false, false) T5;
//alias Cpu!("65C02", false, true) T6;
//alias Cpu!("65C02", true, false) T7;
//alias Cpu!("65C02", true, true) T8;
/+
void main()
{
import std.stdio;
writeln(OpBody(0x11, "6502", true, false));
}
+/

190
src/cpu/data_d6502.d Normal file
View File

@ -0,0 +1,190 @@
module cpu.data_d6502;
// Opcode names.
immutable OP_NAMES_6502 = [
"BRK", "ORA", "KIL", "SLO", "NOP", "ORA", "ASL", "SLO",
"PHP", "ORA", "ASL", "ANC", "NOP", "ORA", "ASL", "SLO",
"BPL", "ORA", "KIL", "SLO", "NOP", "ORA", "ASL", "SLO",
"CLC", "ORA", "NOP", "SLO", "NOP", "ORA", "ASL", "SLO",
"JSR", "AND", "KIL", "RLA", "BIT", "AND", "ROL", "RLA",
"PLP", "AND", "ROL", "ANC", "BIT", "AND", "ROL", "RLA",
"BMI", "AND", "KIL", "RLA", "NOP", "AND", "ROL", "RLA",
"SEC", "AND", "NOP", "RLA", "NOP", "AND", "ROL", "RLA",
"RTI", "EOR", "KIL", "SRE", "NOP", "EOR", "LSR", "SRE",
"PHA", "EOR", "LSR", "ALR", "JMP", "EOR", "LSR", "SRE",
"BVC", "EOR", "KIL", "SRE", "NOP", "EOR", "LSR", "SRE",
"CLI", "EOR", "NOP", "SRE", "NOP", "EOR", "LSR", "SRE",
"RTS", "ADC", "KIL", "RRA", "NOP", "ADC", "ROR", "RRA",
"PLA", "ADC", "ROR", "ARR", "JMP", "ADC", "ROR", "RRA",
"BVS", "ADC", "KIL", "RRA", "NOP", "ADC", "ROR", "RRA",
"SEI", "ADC", "NOP", "RRA", "NOP", "ADC", "ROR", "RRA",
"NOP", "STA", "NOP", "SAX", "STY", "STA", "STX", "SAX",
"DEY", "NOP", "TXA", "XAA", "STY", "STA", "STX", "SAX",
"BCC", "STA", "KIL", "AHX", "STY", "STA", "STX", "SAX",
"TYA", "STA", "TXS", "TAS", "SHY", "STA", "SHX", "AHX",
"LDY", "LDA", "LDX", "LAX", "LDY", "LDA", "LDX", "LAX",
"TAY", "LDA", "TAX", "LAX", "LDY", "LDA", "LDX", "LAX",
"BCS", "LDA", "KIL", "LAX", "LDY", "LDA", "LDX", "LAX",
"CLV", "LDA", "TSX", "LAS", "LDY", "LDA", "LDX", "LAX",
"CPY", "CMP", "NOP", "DCP", "CPY", "CMP", "DEC", "DCP",
"INY", "CMP", "DEX", "AXS", "CPY", "CMP", "DEC", "DCP",
"BNE", "CMP", "KIL", "DCP", "NOP", "CMP", "DEC", "DCP",
"CLD", "CMP", "NOP", "DCP", "NOP", "CMP", "DEC", "DCP",
"CPX", "SBC", "NOP", "ISC", "CPX", "SBC", "INC", "ISC",
"INX", "SBC", "NOP", "SBC", "CPX", "SBC", "INC", "ISC",
"BEQ", "SBC", "KIL", "ISC", "NOP", "SBC", "INC", "ISC",
"SED", "SBC", "NOP", "ISC", "NOP", "SBC", "INC", "ISC"
];
immutable OP_NAMES_65C02 = [
"BRK", "ORA", "NOP", "NOP", "TSB", "ORA", "ASL", "NOP",
"PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "NOP",
"BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "NOP",
"CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "NOP",
"JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "NOP",
"PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "NOP",
"BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "NOP",
"SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "NOP",
"RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "NOP",
"PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "NOP",
"BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "NOP",
"CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "NOP",
"RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "NOP",
"PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "NOP",
"BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "NOP",
"SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "NOP",
"BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "NOP",
"DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "NOP",
"BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "NOP",
"TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "NOP",
"LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "NOP",
"TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "NOP",
"BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "NOP",
"CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "NOP",
"CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "NOP",
"INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "NOP",
"BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "NOP",
"CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "NOP",
"CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "NOP",
"INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "NOP",
"BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "NOP",
"SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "NOP"
];
// Addressing modes.
enum { IMP, IMM, ZP, ZPX, ZPY, IZX, IZY, ABS, ABX, ABY, IND, REL,
ZPI, ABI, NP1, NP8, KIL }
immutable ADDR_MODES_6502 = [
IMP, IZX, KIL, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPX, ZPX,
IMP, ABY, IMP, ABY, ABX, ABX, ABX, ABX,
ABS, IZX, KIL, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPX, ZPX,
IMP, ABY, IMP, ABY, ABX, ABX, ABX, ABX,
IMP, IZX, KIL, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPX, ZPX,
IMP, ABY, IMP, ABY, ABX, ABX, ABX, ABX,
IMP, IZX, KIL, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, IND, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPX, ZPX,
IMP, ABY, IMP, ABY, ABX, ABX, ABX, ABX,
IMM, IZX, IMM, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPY, ZPY,
IMP, ABY, IMP, ABY, ABX, ABX, ABY, ABY,
IMM, IZX, IMM, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPY, ZPY,
IMP, ABY, IMP, ABY, ABX, ABX, ABY, ABY,
IMM, IZX, IMM, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPX, ZPX,
IMP, ABY, IMP, ABY, ABX, ABX, ABX, ABX,
IMM, IZX, IMM, IZX, ZP, ZP, ZP, ZP,
IMP, IMM, IMP, IMM, ABS, ABS, ABS, ABS,
REL, IZY, KIL, IZY, ZPX, ZPX, ZPX, ZPX,
IMP, ABY, IMP, ABY, ABX, ABX, ABX, ABX
];
immutable ADDR_MODES_65C02 = [
IMP, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZP, ZPX, ZPX, NP1,
IMP, ABY, IMP, NP1, ABS, ABX, ABX, NP1,
ABS, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPX, NP1,
IMP, ABY, IMP, NP1, ABX, ABX, ABX, NP1,
IMP, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPX, NP1,
IMP, ABY, IMP, NP1, NP8, ABX, ABX, NP1,
IMP, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, IND, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPX, NP1,
IMP, ABY, IMP, NP1, ABI, ABX, ABX, NP1,
REL, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPY, NP1,
IMP, ABY, IMP, NP1, ABX, ABX, ABX, NP1,
IMM, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPY, NP1,
IMP, ABY, IMP, NP1, ABX, ABX, ABY, NP1,
IMM, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPX, NP1,
IMP, ABY, IMP, NP1, ABX, ABX, ABX, NP1,
IMM, IZX, IMM, NP1, ZP, ZP, ZP, NP1,
IMP, IMM, IMP, NP1, ABS, ABS, ABS, NP1,
REL, IZY, ZPI, NP1, ZPX, ZPX, ZPX, NP1,
IMP, ABY, IMP, NP1, ABX, ABX, ABX, NP1
];
// Page-crossing extra cycles.
immutable EXTRA_CYCLES_6502 = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1,
];
immutable EXTRA_CYCLES_65C02 = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
];

View File

@ -1,152 +0,0 @@
/+
+ d6502/base.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module d6502.base;
enum Strict : bool
{
no, yes
}
enum Cumulative : bool
{
no, yes
}
string hexByte(int decByte)
{
int highNybble = (decByte & 0xF0) >> 4;
int lowNybble = decByte & 0x0F;
string digits = "0123456789ABCDEF";
return digits[highNybble..(highNybble + 1)] ~
digits[lowNybble..(lowNybble + 1)];
}
final class StatusRegister
{
bool carry, decimal, interrupt, overflow;
ubyte zero_, negative_;
ubyte toByte()
{
return (carry ? 0x01 : 0) |
((zero_ == 0) ? 0x02 : 0) |
(interrupt ? 0x04 : 0) |
(decimal ? 0x08 : 0) |
0x30 | // break and reserved both set
(overflow ? 0x40 : 0) |
(negative_ & 0x80);
}
void fromByte(ubyte val)
{
carry = ((val & 0x01) != 0);
zero_ = ((val & 0x02) ? 0 : 1);
interrupt = ((val & 0x04) != 0);
decimal = ((val & 0x08) != 0);
overflow = ((val & 0x40) != 0);
negative_ = val;
}
}
class CpuBase(bool strict, bool cumulative)
{
enum _isCpuBase = true;
static if (strict) enum _isStrict = true;
static if (cumulative) enum _isCumulative = true;
static string AbstractOpcodes()
{
string abstractOpcodes;
for (int op = 0; op < 256; ++op)
abstractOpcodes ~= "abstract void opcode" ~ hexByte(op) ~ "();\n";
return abstractOpcodes;
}
mixin(AbstractOpcodes());
ushort programCounter;
ubyte accumulator, xIndex, yIndex, stackPointer;
StatusRegister flag;
bool signalActive, irqActive, resetActive, nmiActive, nmiArmed;
ushort opcodePC;
ubyte opcode, operand1, operand2;
final ubyte[] save()
{
ubyte[] data = new ubyte[12];
data[0] = programCounter & 0xFF;
data[1] = programCounter >> 8;
data[2] = accumulator;
data[3] = xIndex;
data[4] = yIndex;
data[5] = stackPointer;
data[6] = flag.toByte();
data[7] = (signalActive ? 1 : 0);
data[8] = (irqActive ? 1 : 0);
data[9] = (resetActive ? 1 : 0);
data[10] = (nmiActive ? 1 : 0);
data[11] = (nmiArmed ? 1 : 0);
return data;
}
final void restore(ubyte[] data)
{
assert (data.length >= 12);
programCounter = data[0] | (data[1] << 8);
accumulator = data[2];
xIndex = data[3];
yIndex = data[4];
stackPointer = data[5];
flag.fromByte(data[6]);
signalActive = ((data[7] == 0) ? false : true);
irqActive = ((data[8] == 0) ? false : true);
resetActive = ((data[9] == 0) ? false : true);
nmiActive = ((data[10] == 0) ? false : true);
nmiArmed = ((data[11] == 0) ? false : true);
}
final void reboot()
{
restore([0, 0, 0, 0, 0, 0xFF, 0, 0, 0, 0, 0, 1]);
}
ubyte delegate(ushort addr) memoryRead;
void delegate(ushort addr, ubyte val) memoryWrite;
debug(disassemble)
{
string delegate(ushort addr) memoryName;
}
static if (cumulative) void delegate(int cycles) tick;
else void delegate() tick;
abstract void run(bool continuous);
abstract void stop();
abstract void resetLow();
abstract void nmiLow(bool signalLow);
abstract void irqLow(bool signalLow);
}

View File

@ -1,266 +0,0 @@
/+
+ d6502/cmos.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module d6502.cmos;
import d6502.base;
import d6502.cpu;
class Cmos(bool strict, bool cumulative) : Cpu!(strict, cumulative)
{
enum _isCMOS = true;
this()
{
super();
spuriousAddress = &programCounter;
}
final override void do_IRQ_or_NMI(ushort vector)
{
super.do_IRQ_or_NMI(vector);
flag.decimal = false;
}
final override void doReset()
{
super.doReset();
flag.decimal = false;
}
final override void dec_addWithCarry(ubyte val)
{
super.dec_addWithCarry(val);
peek(programCounter);
// TODO: fix this
static if (cumulative) tick(1);
flag.zero_ = flag.negative_ = accumulator;
}
final override void dec_subWithCarry(ubyte val)
{
int a = accumulator;
int al = (a & 0x0F) - (val & 0x0F) - !flag.carry;
a = a - val - !flag.carry;
if (a < 0)
a = a - 0x60;
if (al < 0)
a = a - 0x06;
uint diff = accumulator - val - !flag.carry;
flag.overflow =
((accumulator ^ diff) & 0x80) &&
((accumulator ^ val) & 0x80);
flag.carry = (diff < 0x100);
peek(programCounter);
// TODO: fix this
static if (cumulative) tick(1);
flag.zero_ = flag.negative_ = accumulator = cast(ubyte)a;
}
final void addrZeropageI()
{
ubyte vector = readByteOperand();
primaryAddress = readWord(vector, cast(ubyte)(vector + 1));
}
final void addrNone()
{
static if (cumulative) tick(totalCycles);
}
final ubyte testSet(ubyte val)
{
flag.zero_ = val & accumulator;
return val | accumulator;
}
final ubyte testReset(ubyte val)
{
flag.zero_ = val & accumulator;
return val & ~accumulator;
}
static string RMW(string action)
{
return "peek(primaryAddress);\n" ~
"writeFinal(primaryAddress, (flag.zero_ = flag.negative_ = " ~
action ~ "(readVal = read(primaryAddress))));\n";
}
static string TestModify(string action)
{
return "peek(primaryAddress);\n" ~
"writeFinal(primaryAddress, " ~
action ~ "(readVal = read(primaryAddress)));\n";
}
static string ReadNOP()
{
return "readVal = readFinal(primaryAddress);\n";
}
static string ManualAddress(string name, int[] opcodes,
string mode)
{
string modes = "[[\"" ~ name ~ "\", \"NA\"], \n";
for (int op = 0; op < opcodes.length; ++op)
{
int opcode = opcodes[op];
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"" ~ mode ~ "\"]";
if (op != (opcodes.length - 1)) modes ~= ", ";
modes ~= "\n";
}
return modes ~ "]\n";
}
mixin(Opcode(mixin(Type2Address(
"ASL", "Read", [0x06, 0x0E, 0x16, 0x1E])),
RMW("shiftLeft")));
mixin(Opcode(mixin(Type2Address(
"LSR", "Read", [0x46, 0x4E, 0x56, 0x5E])),
RMW("shiftRight")));
mixin(Opcode(mixin(Type2Address(
"ROL", "Read", [0x26, 0x2E, 0x36, 0x3E])),
RMW("rotateLeft")));
mixin(Opcode(mixin(Type2Address(
"ROR", "Read", [0x66, 0x6E, 0x76, 0x7E])),
RMW("rotateRight")));
mixin(Opcode(mixin(Type2Address(
"INC", "Read", [0xE6, 0xEE, 0xF6])),
RMW("increment")));
mixin(Opcode(mixin(Type2Address(
"DEC", "Read", [0xC6, 0xCE, 0xD6])),
RMW("decrement")));
mixin(Opcode(mixin(Type2Address(
"INC", "Write", [0xFE])),
RMW("increment")));
mixin(Opcode(mixin(Type2Address(
"DEC", "Write", [0xDE])),
RMW("decrement")));
mixin(Opcode(mixin(Type2Address(
"BIT", "Read", [0x34, 0x3C])),
BitTest()));
mixin(Opcode([["ORA", "Read"], ["12", "ZeropageI()"]],
Read("accumulator |=")));
mixin(Opcode([["AND", "Read"], ["32", "ZeropageI()"]],
Read("accumulator &=")));
mixin(Opcode([["EOR", "Read"], ["52", "ZeropageI()"]],
Read("accumulator ^=")));
mixin(Opcode([["LDA", "Read"], ["B2", "ZeropageI()"]],
Read("accumulator =")));
mixin(Opcode([["CMP", "Read"], ["D2", "ZeropageI()"]],
Compare("accumulator")));
mixin(Opcode([["ADC", "Read"], ["72", "ZeropageI()"]],
Decimal("addWithCarry")));
mixin(Opcode([["SBC", "Read"], ["F2", "ZeropageI()"]],
Decimal("subWithCarry")));
mixin(Opcode([["STA", "Write"], ["92", "ZeropageI()"]],
Write("accumulator")));
mixin(RegisterOpcode("DEA", "3A", "accumulator -= 1"));
mixin(RegisterOpcode("INA", "1A", "accumulator += 1"));
mixin(SimpleOpcode("PHX", "DA", "push(xIndex)"));
mixin(SimpleOpcode("PHY", "5A", "push(yIndex)"));
mixin(RegisterOpcode("PLX", "FA", "xIndex = pull()"));
mixin(RegisterOpcode("PLY", "7A", "yIndex = pull()"));
mixin(BranchOpcode("BRA", "80", "true"));
mixin(Opcode([["TRB", "Read"],
["14", "Zeropage()"], ["1C", "Absolute"]],
TestModify("testReset")));
mixin(Opcode(mixin(Type2Address(
"TSB", "Read", [0x04, 0x0C])),
TestModify("testSet")));
mixin(Opcode([["STZ", "Write"],
["64", "Zeropage()"], ["74", "ZeropageX()"],
["9C", "Absolute()"], ["9E", "AbsoluteX(true)"]],
Write("0")));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x02, 0x22, 0x42, 0x62, 0x82, 0xC2, 0xE2],
"Immediate")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x44],
"Zeropage()")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x54, 0xD4, 0xF4],
"ZeropageX()")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0xDC, 0xFC],
"AbsoluteX(false)")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83, 0x93,
0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, 0x07, 0x17, 0x27, 0x37,
0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7,
0xE7, 0xF7, 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, 0x0F, 0x1F,
0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF,
0xCF, 0xDF, 0xEF, 0xFF], "None()")),
""));
/* NOP8 */
void opcode5C()
{
readByteOperand();
peek(programCounter++);
peek(0xFF00 | operand1);
peek(0xFFFF);
peek(0xFFFF);
peek(0xFFFF);
peek(0xFFFF);
static if (cumulative) tick(totalCycles);
}
/* JMP ($$$$) */
override void opcode6C()
{
ushort vector = readWordOperand();
peek(programCounter);
programCounter = readWord(vector, cast(ushort)(vector + 1));
static if (cumulative) tick(totalCycles);
}
/* JMP ($$$$,X) */
void opcode7C()
{
baseAddress = readWordOperand();
peek(programCounter);
ushort vector = cast(ushort)(baseAddress + xIndex);
programCounter = readWord(vector, cast(ushort)(vector + 1));
static if (cumulative) tick(totalCycles);
}
/* BIT #$$ */
void opcode89()
{
readVal = operand1 = readFinal(programCounter++);
flag.zero_ = accumulator & readVal;
}
}

View File

@ -1,751 +0,0 @@
/+
+ d6502/cpu.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module d6502.cpu;
import d6502.base;
class Cpu(bool strict, bool cumulative) : CpuBase!(strict, cumulative)
{
static string InitOpcodes()
{
string initCalls;
for (int op = 0; op < 256; ++op)
{
initCalls ~= "opcodes[0x" ~ hexByte(op) ~ "] = &opcode" ~
hexByte(op) ~ ";\n";
}
return initCalls;
}
this()
{
mixin(InitOpcodes());
flag = new StatusRegister();
}
const ushort STACK_BASE = 0x0100;
const ushort NMI_VECTOR = 0xFFFA;
const ushort RESET_VECTOR = 0xFFFC;
const ushort IRQ_VECTOR = 0xFFFE;
void delegate()[256] opcodes;
bool continueExecution;
static if (cumulative) int totalCycles;
debug(disassemble)
{
import hacking.debugger;
import std.stdio;
}
final override void run(bool continuous)
{
assert ((memoryRead !is null) && (memoryWrite !is null));
assert (tick !is null);
continueExecution = continuous;
do
{
if (signalActive) handleSignals();
static if (cumulative) totalCycles = 0;
opcodePC = programCounter;
opcode = read(programCounter++);
/+ TODO: call sync delegate +/
opcodes[opcode]();
debug(disassemble)
{
writefln(Debugger.disassemble(this, cmosMap)
~ Debugger.displayRegisters(this));
}
} while (continueExecution);
}
final override void stop()
{
continueExecution = false;
}
final override void resetLow()
{
resetActive = signalActive = true;
}
final override void nmiLow(bool signalLow)
{
nmiActive = signalLow;
if (!signalLow) nmiArmed = true;
signalActive = testSignals();
}
final override void irqLow(bool signalLow)
{
irqActive = signalLow;
signalActive = testSignals();
}
final bool testSignals()
{
return (resetActive || nmiActive || irqActive);
}
final void handleSignals()
{
bool checkNMI()
{
if (nmiActive && nmiArmed)
{
nmiArmed = false;
return true;
}
return false;
}
if (resetActive) doReset();
else if (checkNMI()) do_IRQ_or_NMI(NMI_VECTOR);
else if ((!flag.interrupt) && irqActive) do_IRQ_or_NMI(IRQ_VECTOR);
}
void do_IRQ_or_NMI(ushort vector)
{
doInterrupt(vector, (flag.toByte() & ~0x10));
}
final void doInterrupt(ushort vector, ubyte statusByte)
{
pushWord(programCounter);
push(statusByte);
flag.interrupt = true;
programCounter = readWord(vector, cast(ushort)(vector + 1));
static if (cumulative) tick(totalCycles);
}
void doReset()
{
static if (cumulative)
{
totalCycles += 2;
}
else
{
tick(); tick();
}
peek(STACK_BASE + stackPointer);
--stackPointer;
peek(STACK_BASE + stackPointer);
--stackPointer;
peek(STACK_BASE + stackPointer);
--stackPointer;
flag.interrupt = true;
resetActive = false;
signalActive = testSignals();
programCounter = readWord(RESET_VECTOR, RESET_VECTOR + 1);
static if (cumulative) tick(totalCycles);
}
final ubyte read(ushort addr)
{
static if (cumulative) ++totalCycles;
else tick();
return memoryRead(addr);
}
final void write(ushort addr, ubyte val)
{
static if (cumulative) ++totalCycles;
else tick();
memoryWrite(addr, val);
}
final void peek(ushort addr)
{
static if (cumulative) ++totalCycles;
else tick();
static if (strict) memoryRead(addr);
}
final void poke(ushort addr, ubyte val)
{
static if (cumulative) ++totalCycles;
else tick();
static if (strict) memoryWrite(addr, val);
}
final ubyte readFinal(ushort addr)
{
static if (cumulative) tick(++totalCycles);
else
{
tick();
}
return memoryRead(addr);
}
final void writeFinal(ushort addr, ubyte val)
{
static if (cumulative) tick(++totalCycles);
else
{
tick();
}
memoryWrite(addr, val);
}
final ushort readWord(ushort addrLo, ushort addrHi)
{
ushort word = read(addrLo);
return word | (read(addrHi) << 8);
}
final void push(ubyte val)
{
write((STACK_BASE + stackPointer), val);
--stackPointer;
/+ TODO: call stack overflow delegate +/
}
final void pushWord(ushort val)
{
push(val >> 8);
push(val & 0xFF);
}
final ubyte readStack()
{
++stackPointer;
/+ TODO: call stack underflow delegate +/
return read(STACK_BASE + stackPointer);
}
final ubyte pull()
{
peek(STACK_BASE + stackPointer);
return readStack();
}
final ushort pullWord()
{
ushort word = pull();
return word | (readStack() << 8);
}
final ubyte readByteOperand()
{
return (operand1 = read(programCounter++));
}
final ushort readWordOperand()
{
operand1 = read(programCounter++);
operand2 = read(programCounter++);
return (operand1 | (operand2 << 8));
}
ushort* spuriousAddress;
ushort badAddress, baseAddress, primaryAddress;
ubyte readVal, writeVal;
final ushort tryShortcut(bool noShortcut, ushort goodAddress)
{
badAddress = (baseAddress & 0xFF00) | cast(ubyte)goodAddress;
if (badAddress != goodAddress)
peek(*spuriousAddress);
else if (noShortcut)
peek(goodAddress);
return goodAddress;
}
final void addrRelative(byte offset)
{
peek(programCounter);
baseAddress = programCounter;
programCounter = tryShortcut(false,
cast(ushort)(programCounter + offset));
}
final void addrZeropage()
{
primaryAddress = readByteOperand();
}
final void addrAbsolute()
{
primaryAddress = readWordOperand();
}
final void addrZeropageX()
{
baseAddress = badAddress = readByteOperand();
peek(*spuriousAddress);
primaryAddress = cast(ubyte)(baseAddress + xIndex);
}
final void addrZeropageY()
{
baseAddress = badAddress = readByteOperand();
peek(*spuriousAddress);
primaryAddress = cast(ubyte)(baseAddress + yIndex);
}
final void addrIndirectX()
{
baseAddress = badAddress = readByteOperand();
peek(*spuriousAddress);
ushort vector = cast(ubyte)(baseAddress + xIndex);
primaryAddress = readWord(vector, cast(ubyte)(vector + 1));
}
final void addrAbsoluteX(bool write)
{
baseAddress = readWordOperand();
primaryAddress = tryShortcut(write,
cast(ushort)(baseAddress + xIndex));
}
final void addrAbsoluteY(bool write)
{
baseAddress = readWordOperand();
primaryAddress = tryShortcut(write,
cast(ushort)(baseAddress + yIndex));
}
final void addrIndirectY(bool write)
{
ubyte vector = readByteOperand();
baseAddress = readWord(vector, cast(ubyte)(vector + 1));
primaryAddress = tryShortcut(write,
cast(ushort)(baseAddress + yIndex));
}
void dec_addWithCarry(ubyte val)
{
int a = accumulator;
int al = (a & 0x0F) + (val & 0x0F) + flag.carry;
if (al >= 0x0A)
al = ((al + 0x06) & 0x0F) + 0x10;
a = (a & 0xF0) + (val & 0xF0) + al;
flag.negative_ = cast(ubyte)a;
flag.zero_ = cast(ubyte)(accumulator + val + flag.carry);
flag.overflow =
(!((accumulator ^ val) & 0x80)) && ((val ^ a) & 0x80);
if (a >= 0xA0)
a = a + 0x60;
flag.carry = (a >= 0x100);
accumulator = cast(ubyte)a;
}
void dec_subWithCarry(ubyte val)
{
int a = accumulator;
int al = (a & 0x0F) - (val & 0x0F) - !flag.carry;
if (al < 0)
al = ((al - 0x06) & 0x0F) - 0x10;
a = (a & 0xF0) - (val & 0xF0) + al;
if (a < 0)
a = a - 0x60;
uint diff = accumulator - val - !flag.carry;
flag.overflow =
((accumulator ^ diff) & 0x80) &&
((accumulator ^ val) & 0x80);
flag.carry = (diff < 0x100);
flag.zero_ = flag.negative_ = cast(ubyte)diff;
accumulator = cast(ubyte)a;
}
final void hex_addWithCarry(ubyte val)
{
uint sum = accumulator + val + flag.carry;
flag.overflow =
(!((accumulator ^ val) & 0x80)) && ((val ^ sum) & 0x80);
flag.carry = (sum > 0xFF);
flag.zero_ = flag.negative_ = (accumulator = cast(ubyte)sum);
}
final void hex_subWithCarry(ubyte val)
{
uint diff = accumulator - val - (flag.carry ? 0 : 1);
flag.overflow =
((accumulator ^ diff) & 0x80) &&
((accumulator ^ val) & 0x80);
flag.carry = (diff < 0x100);
flag.zero_ = flag.negative_ = (accumulator = cast(ubyte)diff);
}
final ubyte compare(ubyte reg, ubyte val)
{
flag.carry = (reg >= val);
return cast(ubyte)(reg - val);
}
final void bitTest(ubyte val)
{
flag.negative_ = val;
flag.zero_ = accumulator & val;
flag.overflow = ((val & 0x40) != 0);
}
final ubyte shiftLeft(ubyte val)
{
flag.carry = (val > 0x7F);
return cast(ubyte)(val << 1);
}
final ubyte rotateLeft(ubyte val)
{
bool oldCarry = flag.carry;
flag.carry = (val > 0x7F);
val = cast(ubyte)(val << 1 | (oldCarry ? 1 : 0));
return val;
}
final ubyte shiftRight(ubyte val)
{
flag.carry = ((val & 0x01) != 0);
return val >> 1;
}
final ubyte rotateRight(ubyte val)
{
bool oldCarry = flag.carry;
flag.carry = ((val & 0x01) != 0);
val = (val >> 1 | (oldCarry ? 0x80 : 0));
return val;
}
final ubyte increment(ubyte val)
{
return cast(ubyte)(val + 1);
}
final ubyte decrement(ubyte val)
{
return cast(ubyte)(val - 1);
}
static string SimpleOpcode(string name, string opcode, string action)
{
string code = "peek(programCounter);\n";
code ~= (action == "") ? "" : (action ~ ";");
static if (cumulative) code ~= "tick(totalCycles);\n";
return "override void opcode" ~ opcode ~ "()\n{\n" ~ code ~ "\n}\n";
}
static string UpdateNZ(string action)
{
return "flag.zero_ = flag.negative_ = (" ~ action ~ ");" ~ "\n";
}
static string RegisterOpcode(string name, string opcode, string action)
{
string code = "peek(programCounter);\n";
code ~= UpdateNZ(action);
static if (cumulative) code ~= "tick(totalCycles);\n";
return "override void opcode" ~ opcode ~ "()\n{\n" ~ code ~ "}\n";
}
static string BranchOpcode(string name, string opcode, string action)
{
string code = "readByteOperand();\n" ~
"if (" ~ action ~ ") addrRelative(cast(byte)operand1);\n";
static if (cumulative) code ~= "tick(totalCycles);\n";
return "override void opcode" ~ opcode ~ "()\n{\n" ~ code ~ "}\n";
}
static string Type1Address(string name, string rw, int[] opcodes)
{
string type = (rw == "Write") ? "true" : "false";
string modes = "[[\"" ~ name ~ "\", \"" ~ rw ~ "\"], \n";
for (int op = 0; op < opcodes.length; ++op)
{
int opcode = opcodes[op];
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"";
final switch ((opcode & 0b00011100) >> 2)
{
case 0:
modes ~= "IndirectX()";
break;
case 1:
modes ~= "Zeropage()";
break;
case 2:
modes ~= "Immediate";
break;
case 3:
modes ~= "Absolute()";
break;
case 4:
modes ~= "IndirectY("~ type ~ ")";
break;
case 5:
modes ~= "ZeropageX()";
break;
case 6:
modes ~= "AbsoluteY(" ~ type ~ ")";
break;
case 7:
modes ~= "AbsoluteX(" ~ type ~ ")";
break;
}
modes ~= "\"]";
if (op != (opcodes.length - 1)) modes ~= ", ";
modes ~= "\n";
}
return modes ~ "]\n";
}
static string Type2Address(string name, string rw, int[] opcodes)
{
string type = (rw == "Write") ? "true" : "false";
string index = (name[2] == 'X') ? "Y" : "X";
string modes = "[[\"" ~ name ~ "\", \"" ~ rw ~ "\"], \n";
for (int op = 0; op < opcodes.length; ++op)
{
int opcode = opcodes[op];
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"";
final switch ((opcode & 0b00011100) >> 2)
{
case 0:
modes ~= "Immediate";
break;
case 1:
modes ~= "Zeropage()";
break;
case 3:
modes ~= "Absolute()";
break;
case 5:
modes ~= "Zeropage" ~ index ~ "()";
break;
case 7:
modes ~= "Absolute" ~ index ~ "(" ~ type ~ ")";
break;
}
modes ~= "\"]";
if (op != (opcodes.length - 1)) modes ~= ", ";
modes ~= "\n";
}
return modes ~ "]\n";
}
static string Opcode(string[][] details, string action)
{
string methods;
for (int op = 1; op < details.length; ++op)
{
methods ~= "override void opcode" ~ details[op][0] ~ "()\n{\n";
if (details[op][1] == "Immediate")
{
methods ~= "primaryAddress = programCounter++;\n" ~
action ~ "operand1 = readVal;\n";
}
else
{
methods ~= "addr" ~ details[op][1] ~ ";\n" ~ action;
}
methods ~= "}\n";
}
return methods;
}
static string Read(string action)
{
return UpdateNZ(action ~ " (readVal = readFinal(primaryAddress))");
}
static string Decimal(string action)
{
string code = action ~ "(readVal = readFinal(primaryAddress));\n";
return "if (flag.decimal) dec_" ~ code ~
"else hex_" ~ code;
}
static string Compare(string action)
{
return UpdateNZ("compare(" ~ action ~
", (readVal = readFinal(primaryAddress)))");
}
static string Write(string action)
{
return "writeFinal(primaryAddress, " ~ action ~ ");\n";
}
static string BitTest()
{
return "bitTest(readVal = readFinal(primaryAddress));\n";
}
mixin(SimpleOpcode("CLC", "18", "flag.carry = false"));
mixin(SimpleOpcode("SEC", "38", "flag.carry = true"));
mixin(SimpleOpcode("CLI", "58", "flag.interrupt = false"));
mixin(SimpleOpcode("SEI", "78", "flag.interrupt = true"));
mixin(SimpleOpcode("CLV", "B8", "flag.overflow = false"));
mixin(SimpleOpcode("CLD", "D8", "flag.decimal = false"));
mixin(SimpleOpcode("SED", "F8", "flag.decimal = true"));
mixin(SimpleOpcode("NOP", "EA", ""));
mixin(SimpleOpcode("PHP", "08", "push(flag.toByte())"));
mixin(SimpleOpcode("PLP", "28", "flag.fromByte(pull())"));
mixin(SimpleOpcode("PHA", "48", "push(accumulator)"));
mixin(SimpleOpcode("TXS", "9A", "stackPointer = xIndex"));
mixin(RegisterOpcode("PLA", "68", "accumulator = pull()"));
mixin(RegisterOpcode("TSX", "BA", "xIndex = stackPointer"));
mixin(RegisterOpcode("TAX", "AA", "xIndex = accumulator"));
mixin(RegisterOpcode("TXA", "8A", "accumulator = xIndex"));
mixin(RegisterOpcode("DEX", "CA", "xIndex -= 1"));
mixin(RegisterOpcode("INX", "E8", "xIndex += 1"));
mixin(RegisterOpcode("TAY", "A8", "yIndex = accumulator"));
mixin(RegisterOpcode("TYA", "98", "accumulator = yIndex"));
mixin(RegisterOpcode("DEY", "88", "yIndex -= 1"));
mixin(RegisterOpcode("INY", "C8", "yIndex += 1"));
mixin(BranchOpcode("BPL", "10", "flag.negative_ < 0x80"));
mixin(BranchOpcode("BMI", "30", "flag.negative_ > 0x7F"));
mixin(BranchOpcode("BVC", "50", "!flag.overflow"));
mixin(BranchOpcode("BVS", "70", "flag.overflow"));
mixin(BranchOpcode("BCC", "90", "!flag.carry"));
mixin(BranchOpcode("BCS", "B0", "flag.carry"));
mixin(BranchOpcode("BNE", "D0", "flag.zero_ != 0"));
mixin(BranchOpcode("BEQ", "F0", "flag.zero_ == 0"));
mixin(RegisterOpcode("ASL A", "0A",
"accumulator = shiftLeft(accumulator)"));
mixin(RegisterOpcode("ROL A", "2A",
"accumulator = rotateLeft(accumulator)"));
mixin(RegisterOpcode("LSR A", "4A",
"accumulator = shiftRight(accumulator)"));
mixin(RegisterOpcode("ROR A", "6A",
"accumulator = rotateRight(accumulator)"));
mixin(Opcode(mixin(Type1Address(
"LDA", "Read", [0xA1, 0xA5, 0xA9, 0xAD, 0xB1, 0xB5, 0xB9, 0xBD])),
Read("accumulator =")));
mixin(Opcode(mixin(Type1Address(
"ORA", "Read", [0x01, 0x05, 0x09, 0x0D, 0x11, 0x15, 0x19, 0x1D])),
Read("accumulator |=")));
mixin(Opcode(mixin(Type1Address(
"AND", "Read", [0x21, 0x25, 0x29, 0x2D, 0x31, 0x35, 0x39, 0x3D])),
Read("accumulator &=")));
mixin(Opcode(mixin(Type1Address(
"EOR", "Read", [0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D])),
Read("accumulator ^=")));
mixin(Opcode(mixin(Type1Address(
"ADC", "Read", [0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D])),
Decimal("addWithCarry")));
mixin(Opcode(mixin(Type1Address(
"SBC", "Read", [0xE1, 0xE5, 0xE9, 0xED, 0xF1, 0xF5, 0xF9, 0xFD])),
Decimal("subWithCarry")));
mixin(Opcode(mixin(Type1Address(
"CMP", "Read", [0xC1, 0xC5, 0xC9, 0xCD, 0xD1, 0xD5, 0xD9, 0xDD])),
Compare("accumulator")));
mixin(Opcode(mixin(Type1Address(
"STA", "Write", [0x81, 0x85, 0x8D, 0x91, 0x95, 0x99, 0x9D])),
Write("accumulator")));
mixin(Opcode(mixin(Type2Address(
"LDX", "Read", [0xA2, 0xA6, 0xAE, 0xB6, 0xBE])),
Read("xIndex =")));
mixin(Opcode(mixin(Type2Address(
"LDY", "Read", [0xA0, 0xA4, 0xAC, 0xB4, 0xBC])),
Read("yIndex =")));
mixin(Opcode(mixin(Type2Address(
"CPX", "Read", [0xE0, 0xE4, 0xEC])),
Compare("xIndex")));
mixin(Opcode(mixin(Type2Address(
"CPY", "Read", [0xC0, 0xC4, 0xCC])),
Compare("yIndex")));
mixin(Opcode(mixin(Type2Address(
"STX", "Write", [0x86, 0x8E, 0x96])),
Write("xIndex")));
mixin(Opcode(mixin(Type2Address(
"STY", "Write", [0x84, 0x8C, 0x94])),
Write("yIndex")));
mixin(Opcode(mixin(Type2Address(
"BIT", "Read", [0x24, 0x2C])),
BitTest()));
/* BRK */
final override void opcode00()
{
peek(programCounter);
++programCounter;
doInterrupt(IRQ_VECTOR, flag.toByte());
}
/* JSR */
final override void opcode20()
{
ushort finalAddress = (operand1 = read(programCounter++));
peek(STACK_BASE + stackPointer);
pushWord(programCounter);
finalAddress |= ((operand2 = read(programCounter)) << 8);
static if (cumulative) tick(totalCycles);
programCounter = finalAddress;
}
/* RTI */
final override void opcode40()
{
peek(programCounter);
flag.fromByte(pull());
programCounter = readStack() | (readStack() << 8);
static if (cumulative) tick(totalCycles);
}
/* JMP $$$$ */
final override void opcode4C()
{
programCounter = readWordOperand();
static if (cumulative) tick(totalCycles);
}
/* RTS */
final override void opcode60()
{
peek(programCounter);
programCounter = pullWord();
peek(programCounter);
static if (cumulative) tick(totalCycles);
++programCounter;
}
}

View File

@ -1,71 +0,0 @@
/+
+ d6502/nmosbase.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module d6502.nmosbase;
import d6502.cpu;
class NmosBase(bool strict, bool cumulative) : Cpu!(strict, cumulative)
{
enum _isNMOS = true;
this()
{
super();
spuriousAddress = &badAddress;
}
static string RMW(string action)
{
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
action ~ "(readVal));\n";
}
mixin(Opcode(mixin(Type2Address(
"ASL", "Write", [0x06, 0x0E, 0x16, 0x1E])),
RMW("shiftLeft")));
mixin(Opcode(mixin(Type2Address(
"LSR", "Write", [0x46, 0x4E, 0x56, 0x5E])),
RMW("shiftRight")));
mixin(Opcode(mixin(Type2Address(
"ROL", "Write", [0x26, 0x2E, 0x36, 0x3E])),
RMW("rotateLeft")));
mixin(Opcode(mixin(Type2Address(
"ROR", "Write", [0x66, 0x6E, 0x76, 0x7E])),
RMW("rotateRight")));
mixin(Opcode(mixin(Type2Address(
"INC", "Write", [0xE6, 0xEE, 0xF6, 0xFE])),
RMW("increment")));
mixin(Opcode(mixin(Type2Address(
"DEC", "Write", [0xC6, 0xCE, 0xD6, 0xDE])),
RMW("decrement")));
/* JMP ($$$$) */
override void opcode6C()
{
ushort vector = readWordOperand();
programCounter = readWord(vector,
(vector & 0xFF00) | cast(ubyte)(vector + 1));
static if (cumulative) tick(totalCycles);
}
}

View File

@ -1,365 +0,0 @@
/+
+ d6502/nmosundoc.d
+
+ Copyright: 2007 Gerald Stocker
+
+ This file is part of twoapple-reboot.
+
+ twoapple-reboot is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ twoapple-reboot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with twoapple-reboot; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+/
module d6502.nmosundoc;
import d6502.base;
import d6502.nmosbase;
class NmosUndoc(bool strict, bool cumulative) : NmosBase!(strict, cumulative)
{
this()
{
super();
}
final void addrHalt()
{
programCounter--;
/* TODO: check with the timer how many ticks until it would
* stop me? */
static if (cumulative) tick(totalCycles);
}
final void addrImplied()
{
peek(programCounter);
static if (cumulative) tick(totalCycles);
}
final void strange(ubyte val)
{
version(Commodore64)
{
ubyte hiAddr = cast(ubyte)((primaryAddress >> 8) + 1);
val = val & hiAddr;
ushort addr = (badAddress == primaryAddress) ? primaryAddress :
((val << 8) | (primaryAddress & 0xFF));
writeFinal(addr, val);
}
else
{
ubyte hiAddr = cast(ubyte)((baseAddress >> 8) + 1);
writeFinal(primaryAddress, val & hiAddr);
}
}
static string UndocAddress(string name, string rw, int[] opcodes)
{
string type = (rw == "Write") ? "true" : "false";
string modes = "[[\"" ~ name ~ "\", \"" ~ rw ~ "\"], \n";
string index = (name[2] == 'X') ? "Y" : "X";
for (int op = 0; op < opcodes.length; ++op)
{
int opcode = opcodes[op];
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"";
final switch ((opcode & 0b00011100) >> 2)
{
case 0:
modes ~= "IndirectX()";
break;
case 1:
modes ~= "Zeropage()";
break;
case 3:
modes ~= "Absolute()";
break;
case 4:
modes ~= "IndirectY("~ type ~ ")";
break;
case 5:
modes ~= "Zeropage" ~ index ~ "()";
break;
case 7:
modes ~= "AbsoluteY(" ~ type ~ ")";
break;
}
modes ~= "\"]";
if (op != (opcodes.length - 1)) modes ~= ", ";
modes ~= "\n";
}
return modes ~ "]\n";
}
static string ManualAddress(string name, int[] opcodes,
string mode)
{
string modes = "[[\"" ~ name ~ "\", \"NA\"], \n";
for (int op = 0; op < opcodes.length; ++op)
{
int opcode = opcodes[op];
modes ~= "[\"" ~ hexByte(opcode) ~ "\", \"" ~ mode ~ "\"]";
if (op != (opcodes.length - 1)) modes ~= ", ";
modes ~= "\n";
}
return modes ~ "]\n";
}
static string Halt()
{
/+ TODO: have this do something useful +/
return "\n";
}
static string ReadNOP()
{
return "readVal = readFinal(primaryAddress);\n";
}
static string RMW_Read(string action1, string action2)
{
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
"(writeVal = " ~ action1 ~ "(readVal)));\n" ~
"flag.zero_ = flag.negative_ = (" ~action2 ~ " writeVal);\n";
}
static string RMW_Compare(string action1, string action2)
{
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
"writeFinal(primaryAddress, " ~
"(writeVal = " ~ action1 ~ "(readVal)));\n" ~
"flag.zero_ = flag.negative_ = " ~
"compare(" ~ action2 ~ ", writeVal);\n";
}
static string RMW_Decimal(string action1, string action2)
{
return "poke(primaryAddress, (readVal = read(primaryAddress)));\n" ~
"writeFinal(primaryAddress, flag.zero_ = flag.negative_ = " ~
"(writeVal = " ~ action1 ~ "(readVal)));\n" ~
"if (flag.decimal) dec_" ~ action2 ~ "(writeVal);\n" ~
"else hex_" ~ action2 ~ "(writeVal);\n";
}
mixin(Opcode(mixin(ManualAddress(
"HLT", [0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x92, 0xB2,
0xD2, 0xF2], "Halt()")),
Halt()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x1A, 0x3A, 0x5A, 0x7A, 0xDA, 0xFA], "Implied()")),
""));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x0C],
"Absolute()")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x1C, 0x3C, 0x5C, 0x7C, 0xDC, 0xFC],
"AbsoluteX(false)")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x80, 0x82, 0x89, 0xC2, 0xE2],
"Immediate")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x04, 0x44, 0x64],
"Zeropage()")),
ReadNOP()));
mixin(Opcode(mixin(ManualAddress(
"NOP", [0x14, 0x34, 0x54, 0x74, 0xD4, 0xF4],
"ZeropageX()")),
ReadNOP()));
mixin(Opcode(mixin(UndocAddress(
"LAX", "Read", [0xA3, 0xA7, 0xAF, 0xB3, 0xB7, 0xBF])),
Read("accumulator = xIndex =")));
mixin(Opcode(mixin(UndocAddress(
"SAX", "Write", [0x83, 0x87, 0x97, 0x8F])),
Write("accumulator & xIndex")));
mixin(Opcode(mixin(Type1Address(
"ASO", "Write", [0x03, 0x07, 0x0F, 0x13, 0x17, 0x1B, 0x1F])),
RMW_Read("shiftLeft", "accumulator |=")));
mixin(Opcode(mixin(Type1Address(
"RLA", "Write", [0x23, 0x27, 0x2F, 0x33, 0x37, 0x3B, 0x3F])),
RMW_Read("rotateLeft", "accumulator &=")));
mixin(Opcode(mixin(Type1Address(
"LSE", "Write", [0x43, 0x47, 0x4F, 0x53, 0x57, 0x5B, 0x5F])),
RMW_Read("shiftRight", "accumulator ^=")));
mixin(Opcode(mixin(Type1Address(
"DCM", "Write", [0xC3, 0xC7, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF])),
RMW_Compare("decrement", "accumulator")));
mixin(Opcode(mixin(Type1Address(
"RRA", "Write", [0x63, 0x67, 0x6F, 0x73, 0x77, 0x7B, 0x7F])),
RMW_Decimal("rotateRight", "addWithCarry")));
mixin(Opcode(mixin(Type1Address(
"INS", "Write", [0xE3, 0xE7, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF])),
RMW_Decimal("increment", "subWithCarry")));
/* ANC #$$ */
override void opcode0B()
{
readVal = operand1 = readFinal(programCounter);
flag.zero_ = flag.negative_ = (accumulator = readVal);
flag.carry = (flag.negative_ > 0x7F);
}
/* ANC #$$ */
override void opcode2B()
{
readVal = operand1 = readFinal(programCounter);
flag.zero_ = flag.negative_ = (accumulator = readVal);
flag.carry = (flag.negative_ > 0x7F);
}
/* ALR #$$ */
override void opcode4B()
{
readVal = operand1 = readFinal(programCounter);
flag.zero_ = flag.negative_ =
(accumulator = shiftRight(accumulator & readVal));
}
/* ARR #$$ */
override void opcode6B()
{
readVal = operand1 = readFinal(programCounter);
ubyte val = readVal & accumulator;
if (flag.decimal) {
ubyte temp = cast(ubyte)((val >> 1) + (flag.carry ? 0x80 : 0));
flag.zero_ = flag.negative_ = temp;
flag.overflow = (((temp ^ val) & 0x40) != 0);
if ((readVal & 0x0F) + (val & 0x01) > 5)
temp = (temp & 0xF0) + ((temp + 0x6) & 0x0F);
if (val + (val & 0x10) >= 0x60)
{
temp += 0x60;
flag.carry = true;
}
else
flag.carry = false;
accumulator = temp;
}
else {
accumulator = cast(ubyte)((val >> 1) + (flag.carry ? 0x80 : 0));
flag.zero_ = flag.negative_ = accumulator;
val >>= 7;
flag.carry = (val != 0);
flag.overflow = ((val ^ ((accumulator >> 5) & 1)) != 0);
}
}
/* ANE #$$ */
override void opcode8B()
{
// unstable
readVal = operand1 = readFinal(programCounter++);
version(Atari8Bit)
{
flag.zero_ = flag.negative_ =
(accumulator & xIndex & readVal);
accumulator &= xIndex & (operand1 | 0xEF);
}
else
{
flag.zero_ = flag.negative_ =
(accumulator &= (xIndex & readVal));
}
}
/* SHA ($$),Y */
void opcode93()
{
addrIndirectY(true);
strange(accumulator & xIndex);
}
/* SHA $$$$,Y */
void opcode9F()
{
addrAbsoluteY(true);
strange(accumulator & xIndex);
}
/* SHS $$$$,Y */
void opcode9B()
{
addrAbsoluteY(true);
strange(stackPointer = (accumulator & xIndex));
}
/* SHY $$$$,X */
void opcode9C()
{
addrAbsoluteX(true);
strange(yIndex);
}
/* SHX $$$$,Y */
void opcode9E()
{
addrAbsoluteY(true);
strange(xIndex);
}
/* LAX #$$ */
override void opcodeAB()
{
readVal = operand1 = readFinal(programCounter);
version(Commodore128)
{
// not unstable?
flag.zero_ = flag.negative_ =
(accumulator = readVal);
}
else
{
// unstable
version(Commodore64)
{
accumulator |= 0xEE;
}
flag.zero_ = flag.negative_ =
(accumulator &= readVal);
}
xIndex = accumulator;
}
/* LAS $$$$,Y */
override void opcodeBB()
{
addrAbsoluteY(false);
readVal = readFinal(primaryAddress);
flag.zero_ = flag.negative_ =
(xIndex = accumulator = (stackPointer & readVal));
}
/* SBX #$$ */
override void opcodeCB()
{
readVal = operand1 = readFinal(programCounter++);
xIndex &= accumulator;
flag.zero_ = flag.negative_ = compare(xIndex, readVal);
}
/* SBC #$$ */
override void opcodeEB()
{
readVal = operand1 = readFinal(programCounter++);
if (flag.decimal) dec_subWithCarry(readVal);
else hex_subWithCarry(readVal);
}
}

View File

@ -24,9 +24,7 @@ module system.base;
import timer; import timer;
import memory; import memory;
import d6502.base; import cpu.d6502;
private alias d6502.base.CpuBase!(Strict.no, Cumulative.no) CpuBase;
import ui.sound; import ui.sound;
import ui.inputevents; import ui.inputevents;
@ -36,14 +34,31 @@ import system.video;
import iomem; import iomem;
import video.base; import video.base;
import system.peripheral; import system.peripheral;
import ioummu;
class System
class SystemBase
{
Video video_;
abstract void reboot();
abstract void reset();
abstract uint checkpoint();
abstract uint sinceCheckpoint(uint cp);
abstract void execute();
}
class System(string chip) : SystemBase
{ {
Timer timer; Timer timer;
Timer.Cycle deviceCycle; Timer.Cycle deviceCycle;
AddressDecoder decoder; AddressDecoder decoder;
SoftSwitchPage switches; SoftSwitchPage switches;
CpuBase cpu;
Cpu!(chip, AddressDecoder, Timer) cpu;
// XXX
bool* cpuRun, signalActive, resetLow;
IOMem ioMem; IOMem ioMem;
Peripherals peripherals; Peripherals peripherals;
@ -73,15 +88,15 @@ class System
} }
} }
Video video_;
Memory memory_; Memory memory_;
IO io_; IO io_;
abstract IO newIO(); static if (chip == "65C02")
abstract CpuBase newCpu(); {
abstract Video newVideo(ubyte[] vidRom); AuxiliaryCard auxCard;
abstract IOMem newIOMem(); MMU mmu;
abstract Peripherals newPeripherals(); IOU iou;
}
this(ubyte[] romDump) this(ubyte[] romDump)
{ {
@ -91,23 +106,33 @@ class System
initIO(null); // XXX where is vidRom passed in? initIO(null); // XXX where is vidRom passed in?
decoder.nullRead = &video_.scanner.floatingBus; decoder.nullRead = &video_.scanner.floatingBus;
peripherals = newPeripherals(); static if (chip == "6502")
peripherals.install(cpu, decoder, memory_.mainRom); peripherals = new Peripherals_II();
else
peripherals = new Peripherals_IIe();
peripherals.install(decoder, memory_.mainRom);
ioMem.initialize(decoder, switches, timer, peripherals); ioMem.initialize(decoder, switches, timer, peripherals);
input.onReset = &reset; input.onReset = &reset;
switches.setFloatingBus(&video_.scanner.floatingBus); switches.setFloatingBus(&video_.scanner.floatingBus);
} }
void reboot() override void reboot()
{ {
cpu.reboot(); // XXX replace
//cpu.reboot();
deviceCycle.restart(); deviceCycle.restart();
memory_.reboot(); memory_.reboot();
ioMem.reboot(); ioMem.reboot();
io_.reboot(); io_.reboot();
peripherals.reboot(); peripherals.reboot();
video_.reboot(); video_.reboot();
static if (chip == "65C02")
{
auxCard.reboot();
mmu.reboot();
}
} }
void initTimer() void initTimer()
@ -120,56 +145,92 @@ class System
void initMemory(ubyte[] romDump) void initMemory(ubyte[] romDump)
{ {
static if (chip == "65C02")
{
mmu = new MMU();
mmu.ioMem = new IOMem_IIe();
mmu.ioMem.setRom(romDump);
}
memory_ = new Memory(romDump); memory_ = new Memory(romDump);
decoder = new AddressDecoder(); decoder = new AddressDecoder();
switches = new SoftSwitchPage(); switches = new SoftSwitchPage();
decoder.installSwitches(switches); decoder.installSwitches(switches);
decoder.install(memory_.mainRam); decoder.install(memory_.mainRam);
decoder.install(memory_.mainRom); decoder.install(memory_.mainRom);
ioMem = newIOMem(); static if (chip == "6502")
ioMem = new IOMem();
else
{
ioMem = mmu.ioMem;
auxCard = new Extended80ColumnCard();
mmu.init(switches, auxCard, decoder, memory_.mainRam,
memory_.mainRom);
}
} }
void initCpu() void initCpu()
{ {
cpu = newCpu(); cpu = new Cpu!(chip, AddressDecoder, Timer)(decoder, timer);
// XXX
cpuRun = &cpu.keepRunning;
signalActive = &cpu.signalActive;
resetLow = &cpu.resetLow;
debug(disassemble) cpu.memoryName = &decoder.memoryReadName; debug(disassemble) cpu.memoryName = &decoder.memoryReadName;
cpu.tick = &timer.tick;
timer.onPrimaryStop(&primaryStop); timer.onPrimaryStop(&primaryStop);
cpu.memoryRead = &decoder.read;
cpu.memoryWrite = &decoder.write;
} }
void initIO(ubyte[] vidRom) void initIO(ubyte[] vidRom)
{ {
io_ = newIO(); static if (chip == "6502")
video_ = newVideo(vidRom); {
io_ = new IO_II(switches, timer, deviceCycle);
video_ = new Video_II(switches, memory_.vidPages, timer, vidRom,
&io_.kbd.peekLatch, decoder);
}
else
{
io_ = new IO_IIe(switches, timer, deviceCycle);
video_ = new Video_IIe(switches, memory_.vidPages, timer, vidRom,
&io_.kbd.peekLatch, auxCard.vidPages);
iou = new IOU(io_, video_.signal);
iou.initSwitches(switches);
mmu.initIO(video_.scanner, &io_.kbd.peekLatch);
}
} }
bool primaryStop() bool primaryStop()
{ {
cpu.stop(); *cpuRun = false;
return true; return true;
} }
void reset() override void reset()
{ {
static if (chip == "65C02")
{
auxCard.reset();
mmu.reset();
}
peripherals.reset(); peripherals.reset();
cpu.resetLow(); *signalActive = true;
*resetLow = true;
} }
uint checkpoint() override uint checkpoint()
{ {
return timer.primaryCounter.currentLength; return timer.primaryCounter.currentLength;
} }
uint sinceCheckpoint(uint cp) override uint sinceCheckpoint(uint cp)
{ {
uint currentLength = timer.primaryCounter.currentLength; uint currentLength = timer.primaryCounter.currentLength;
return ((currentLength == timer.primaryCounter.startLength) ? return ((currentLength == timer.primaryCounter.startLength) ?
cp : (cp - currentLength)); cp : (cp - currentLength));
} }
void execute() override void execute()
{ {
cpu.run(true); cpu.run(true);
@ -181,126 +242,3 @@ class System
// XXX peripherals get notification // XXX peripherals get notification
} }
} }
class II : System
{
import d6502.nmosundoc : NmosUndoc;
int revision;
this(ubyte[] romDump)
{
// XXX FIXME XXX
revision = int.max;
super(romDump);
}
CpuBase newCpu()
{
return new NmosUndoc!(Strict.no, Cumulative.no)();
}
IO newIO()
{
return new IO_II(switches, timer, deviceCycle);
}
Video newVideo(ubyte[] vidRom)
{
return new Video_II(switches, memory_.vidPages, timer, vidRom,
&io_.kbd.peekLatch, decoder);
}
IOMem newIOMem()
{
return new IOMem();
}
Peripherals newPeripherals()
{
return new Peripherals_II();
}
}
import ioummu;
class IIe : System
{
import d6502.cmos : Cmos;
AuxiliaryCard auxCard;
MMU mmu;
IOU iou;
this(ubyte[] romDump)
{
// XXX if different or no aux card?
//auxMemory = new Memory();
super(romDump);
}
void reboot()
{
super.reboot();
auxCard.reboot();
mmu.reboot();
}
void reset()
{
auxCard.reset();
mmu.reset();
super.reset();
}
CpuBase newCpu()
{
// XXX this is enhanced
return new Cmos!(Strict.no, Cumulative.no)();
}
IO newIO()
{
return new IO_IIe(switches, timer, deviceCycle);
}
Video newVideo(ubyte[] vidRom)
{
return new Video_IIe(switches, memory_.vidPages, timer, vidRom,
&io_.kbd.peekLatch, auxCard.vidPages);
}
IOMem newIOMem()
{
return mmu.ioMem;
}
Peripherals newPeripherals()
{
return new Peripherals_IIe();
}
void initMemory(ubyte[] romDump)
{
mmu = new MMU();
mmu.ioMem = new IOMem_IIe();
mmu.ioMem.setRom(romDump);
super.initMemory(romDump);
// XXX XXX XXX
// allow for different card from config
auxCard = new Extended80ColumnCard();
mmu.init(switches, auxCard, decoder, memory_.mainRam,
memory_.mainRom);
}
void initIO(ubyte[] vidRom)
{
super.initIO(vidRom);
iou = new IOU(io_, video_.signal);
iou.initSwitches(switches);
mmu.initIO(video_.scanner, &io_.kbd.peekLatch);
}
}

View File

@ -23,9 +23,6 @@
module system.peripheral; module system.peripheral;
import memory; import memory;
import d6502.base;
private alias d6502.base.CpuBase!(Strict.no, Cumulative.no) CpuBase;
import peripheral.base; import peripheral.base;
import peripheral.diskii; import peripheral.diskii;
@ -36,7 +33,7 @@ class Peripherals
{ {
Peripheral[8] cards; Peripheral[8] cards;
abstract void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom); abstract void install(AddressDecoder decoder, Rom mainRom);
void reboot() void reboot()
{ {
@ -59,7 +56,7 @@ class Peripherals
class Peripherals_II : Peripherals class Peripherals_II : Peripherals
{ {
void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom) void install(AddressDecoder decoder, Rom mainRom)
{ {
auto diskController = new Controller(); auto diskController = new Controller();
cards[6] = diskController; // XXX cards[6] = diskController; // XXX
@ -78,7 +75,7 @@ class Peripherals_II : Peripherals
class Peripherals_IIe : Peripherals class Peripherals_IIe : Peripherals
{ {
void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom) void install(AddressDecoder decoder, Rom mainRom)
{ {
auto diskController = new Controller(); auto diskController = new Controller();
cards[6] = diskController; // XXX cards[6] = diskController; // XXX

View File

@ -20,7 +20,7 @@
import std.stdio; import std.stdio;
import d6502.base; import cpu.d6502;
import timer; import timer;
import memory; import memory;
import system.base; import system.base;
@ -39,20 +39,6 @@ import peripheral.diskii;
import peripheral.langcard; import peripheral.langcard;
import peripheral.saturn128; import peripheral.saturn128;
class TestSystem : II
{
this(ubyte[] romDump)
{
super(romDump);
}
void setRom(ubyte[] rom_data)
{
uint rom_len = cast(uint)rom_data.length;
memory_.mainRom.data_[0..12288] = rom_data[(rom_len - 12288)..rom_len];
}
}
import std.file; import std.file;
import std.string; import std.string;
import device.speaker; import device.speaker;
@ -86,11 +72,11 @@ void main(string[] args)
TwoappleFile romFile = TwoappleFilePicker.open("ROM file", &checkRomFile); TwoappleFile romFile = TwoappleFilePicker.open("ROM file", &checkRomFile);
if (romFile is null) return; if (romFile is null) return;
System sys; SystemBase sys;
if ((args.length > 1) && (args[1] == "--iie")) if ((args.length > 1) && (args[1] == "--iie"))
sys = new IIe(cast(ubyte[])std.file.read(romFile.fileName)); sys = new System!"65C02"(cast(ubyte[])std.file.read(romFile.fileName));
else else
sys = new II(cast(ubyte[])std.file.read(romFile.fileName)); sys = new System!"6502"(cast(ubyte[])std.file.read(romFile.fileName));
appWindow.initSystem(sys); appWindow.initSystem(sys);
// XXX hack // XXX hack
appWindow.configChanged = true; appWindow.configChanged = true;

View File

@ -54,7 +54,7 @@ class TwoappleMainWindow : MainWindow
import gtk.Alignment; import gtk.Alignment;
import peripheral.base; import peripheral.base;
System system; SystemBase system;
Label speedLabel; Label speedLabel;
ToolItem speedItem; ToolItem speedItem;
@ -122,7 +122,7 @@ class TwoappleMainWindow : MainWindow
} }
} }
void initSystem(System sys) void initSystem(SystemBase sys)
{ {
showAll(); showAll();
system = sys; system = sys;

View File

@ -1,10 +1,130 @@
module test.base; module test.base;
import std.algorithm, std.array, std.conv, std.exception, std.stdio, import std.algorithm, std.array, std.conv, std.exception, std.getopt,
std.string; std.stdio, std.string;
import test.cpu, test.opcodes; import test.cpu, test.opcodes;
import cpu.data_d6502;
version(Strict)
enum strict = true;
else
enum strict = false;
version(Cumulative)
enum cumulative = true;
else
enum cumulative = false;
/*
* A test is a combination of setups, an expectation, a runner, and a
* reporter.
*/
/*
* A setup function for a given opcode puts cpu, data, info, and msg
* into the appropriate state and then calls the next function (see
* testCallNext) with the modified values. To setup multiple
* scenarios, just call the next function multiple times.
*
* Values for cpu registers are set up using the setXXX(cpu, val)
* functions; see testCallNext and OpInfo for descriptions of other
* types of information that may need to be set up.
*
* Example:
*
* // prepare the accumulator with a value different from expected
* setA(cpu, ~0x10);
* // prepare memory with LDA #$10 at address $1000
* setPC(cpu, 0x1000);
* callNext("LDA immediate, positive", [Block(0x1000, [0xA9, 0x10])]);
*/
alias void delegate(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
testsetup;
/*
* A mixin that simplifies calling the next setup function.
*
* newMsg will be appended to the current msg and passed to the next
* function.
*
* To place values in memory, pass an array of Blocks as the second
* parameter. It will be appended to the current data.
*/
template testCallNext()
{
void callNext(string newMsg = "", Block[] newData = [])
{
if (*next !is null)
next.run(opcode, cpu, data ~ newData, info, msg ~ newMsg);
}
}
/*
* A block of memory with a given base address.
*
* For example, `Block(0x1000, [0xA9, 0x10])`
*/
struct Block
{
const ushort base;
const(ubyte[]) data;
string toString() const
{
return format("Block(%0.4X, [%s])", base, formatMemory(data));
}
}
// Information about expected opcode execution.
struct OpInfo
{
// The effective address, if any.
ushort addr;
// The data to be read or written, if any.
ubyte data;
// The length of the opcode + operands.
int len;
}
class TestSetup
{
testsetup setup;
TestSetup next;
auto static opCall(testsetup d)
{
auto obj = new TestSetup();
obj.setup = d;
return obj;
}
void run(ubyte opcode, CpuInfo cpu = CpuInfo(), Block[] data = [],
OpInfo info = OpInfo(), string msg = "")
{
setup(opcode, cpu, data, info, msg, &next);
}
}
TestSetup connect(TestSetup first, TestSetup[] rest...)
{
if (!(rest.empty))
{
auto x = first;
while (x.next !is null) x = x.next;
x.next = connect(rest[0], rest[1..$]);
}
return first;
}
/* /*
@ -104,19 +224,6 @@ public:
} }
// A block of memory with a given base address.
struct Block
{
const ushort base;
const(ubyte[]) data;
string toString() const
{
return format("Block(%0.4X, [%s])", base, formatMemory(data));
}
}
/* /*
* Formats data as a string of 2-digit hex bytes, separated by spaces. * Formats data as a string of 2-digit hex bytes, separated by spaces.
* *
@ -134,57 +241,6 @@ string formatMemory(const(ubyte[]) data, size_t max = 3)
} }
struct OpInfo
{
ushort addr;
ubyte data;
int len;
bool write;
}
alias void delegate(ubyte, CpuInfo, Block[], OpInfo, string, TestSetup*)
testsetup;
class TestSetup
{
testsetup setup;
TestSetup next;
auto static opCall(testsetup d)
{
auto obj = new TestSetup();
obj.setup = d;
return obj;
}
void run(ubyte opcode, CpuInfo cpu = CpuInfo(), Block[] data = [],
OpInfo info = OpInfo(), string msg = "")
{
setup(opcode, cpu, data, info, msg, &next);
}
}
TestSetup connect(TestSetup first, TestSetup[] rest...)
{
if (!(rest.empty))
{
auto x = first;
while (x.next !is null) x = x.next;
x.next = connect(rest[0], rest[1..$]);
}
return first;
}
template testCallNext()
{
void callNext(string newMsg = "", Block[] newData = [])
{
if (*next !is null)
next.run(opcode, cpu, data ~ newData, info, msg ~ newMsg);
}
}
// Does nothing. // Does nothing.
auto setup_none() auto setup_none()
{ {
@ -1232,8 +1288,8 @@ auto setup_op_LSR(bool isAcc)
} }
// For ASO. // For SLO.
auto setup_op_ASO() auto setup_op_SLO()
{ {
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info, auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next) string msg, TestSetup* next)
@ -1249,7 +1305,7 @@ auto setup_op_ASO()
callNext("acc 0x20 "); callNext("acc 0x20 ");
} }
return connect(TestSetup(&setup), return connect(TestSetup(&setup),
setup_rmw(false, "ASO ", setup_asl_data())); setup_rmw(false, "SLO ", setup_asl_data()));
} }
// For RLA. // For RLA.
@ -1270,8 +1326,8 @@ auto setup_op_RLA()
} }
// For LSE. // For SRE.
auto setup_op_LSE() auto setup_op_SRE()
{ {
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info, auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next) string msg, TestSetup* next)
@ -1284,7 +1340,7 @@ auto setup_op_LSE()
callNext("acc 0xFF "); callNext("acc 0xFF ");
} }
return connect(TestSetup(&setup), return connect(TestSetup(&setup),
setup_rmw(false, "LSE ", setup_right_data())); setup_rmw(false, "SRE ", setup_right_data()));
} }
@ -1382,8 +1438,8 @@ auto setup_op_SBC(bool cmos)
} }
// For INS. // For ISC.
auto setup_op_INS() auto setup_op_ISC()
{ {
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info, auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next) string msg, TestSetup* next)
@ -1400,7 +1456,7 @@ auto setup_op_INS()
} }
return connect(TestSetup(&setup), setup_flag(Flag.C), setup_flag(Flag.D), return connect(TestSetup(&setup), setup_flag(Flag.C), setup_flag(Flag.D),
setup_rmw(false, "INS ", setup_inc_data())); setup_rmw(false, "ISC ", setup_inc_data()));
} }
@ -1427,8 +1483,8 @@ auto setup_op_cmp(Reg reg)
} }
// For DCM. // For DCP.
auto setup_op_DCM() auto setup_op_DCP()
{ {
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info, auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next) string msg, TestSetup* next)
@ -1445,7 +1501,7 @@ auto setup_op_DCM()
} }
return connect(TestSetup(&setup), setup_flag(Flag.C), return connect(TestSetup(&setup), setup_flag(Flag.C),
setup_rmw(false, "DCM ", setup_dec_data())); setup_rmw(false, "DCP ", setup_dec_data()));
} }
@ -2105,8 +2161,8 @@ auto expect_LSR(bool isAcc)
} }
// For ASO. // For SLO.
auto expect_ASO() auto expect_SLO()
{ {
void expect(ref Expected expected, const OpInfo info) void expect(ref Expected expected, const OpInfo info)
{ {
@ -2138,8 +2194,8 @@ auto expect_RLA()
} }
// For LSE. // For SRE.
auto expect_LSE() auto expect_SRE()
{ {
void expect(ref Expected expected, const OpInfo info) void expect(ref Expected expected, const OpInfo info)
{ {
@ -2306,8 +2362,8 @@ auto expect_SBC(bool cmos)
} }
// For INS. // For ISC.
auto expect_INS() auto expect_ISC()
{ {
void expect(ref Expected expected, const OpInfo info) void expect(ref Expected expected, const OpInfo info)
{ {
@ -2342,8 +2398,8 @@ auto expect_cmp(Reg reg)
} }
// For DCM. // For DCP.
auto expect_DCM() auto expect_DCP()
{ {
void expect(ref Expected expected, const OpInfo info) void expect(ref Expected expected, const OpInfo info)
{ {
@ -2510,12 +2566,12 @@ if (isCpu!T)
get_expect([0x04, 0x44, 0x64], "NOP"); get_expect([0x04, 0x44, 0x64], "NOP");
get_expect([0x14, 0x34, 0x54, 0x74, 0xD4, 0xF4], "NOP"); get_expect([0x14, 0x34, 0x54, 0x74, 0xD4, 0xF4], "NOP");
get_both([0x83, 0x87, 0x8F, 0x97], "SAX"); get_both([0x83, 0x87, 0x8F, 0x97], "SAX");
get_both([0x03, 0x07, 0x0F, 0x13, 0x17, 0x1B, 0x1F], "ASO"); get_both([0x03, 0x07, 0x0F, 0x13, 0x17, 0x1B, 0x1F], "SLO");
get_both([0x23, 0x27, 0x2F, 0x33, 0x37, 0x3B, 0x3F], "RLA"); get_both([0x23, 0x27, 0x2F, 0x33, 0x37, 0x3B, 0x3F], "RLA");
get_both([0x43, 0x47, 0x4F, 0x53, 0x57, 0x5B, 0x5F], "LSE"); get_both([0x43, 0x47, 0x4F, 0x53, 0x57, 0x5B, 0x5F], "SRE");
get_both([0x63, 0x67, 0x6F, 0x73, 0x77, 0x7B, 0x7F], "RRA"); get_both([0x63, 0x67, 0x6F, 0x73, 0x77, 0x7B, 0x7F], "RRA");
get_both([0xE3, 0xE7, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF], "INS"); get_both([0xE3, 0xE7, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF], "ISC");
get_both([0xC3, 0xC7, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF], "DCM"); get_both([0xC3, 0xC7, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF], "DCP");
get_both([0xEB], "SBC", "false"); get_both([0xEB], "SBC", "false");
// TODO: implement these opcode tests // TODO: implement these opcode tests
@ -2671,7 +2727,7 @@ if (isCpu!T)
cycles = 2; cycles = 2;
return [Bus(Action.READ, pc)] ~ return [Bus(Action.READ, pc)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+1)]); [Bus(Action.READ, pc+1)]);
} }
@ -2688,7 +2744,7 @@ if (isCpu!T)
cycles = 3; cycles = 3;
return [Bus(Action.READ, pc)] ~ return [Bus(Action.READ, pc)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+1)]) ~ [Bus(Action.READ, pc+1)]) ~
[Bus(Action.WRITE, sp)]; [Bus(Action.WRITE, sp)];
} }
@ -2707,7 +2763,7 @@ if (isCpu!T)
cycles = 4; cycles = 4;
return [Bus(Action.READ, pc)] ~ return [Bus(Action.READ, pc)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+1), [Bus(Action.READ, pc+1),
Bus(Action.READ, sp)]) ~ Bus(Action.READ, sp)]) ~
[Bus(Action.READ, sp1)]; [Bus(Action.READ, sp1)];
@ -2728,7 +2784,7 @@ if (isCpu!T)
cycles = 2 + decimal; cycles = 2 + decimal;
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~ Bus(Action.READ, pc+1)] ~
If!decimal(If!(isStrict!T)( If!decimal(If!(strict)(
[Bus(Action.READ, pc+2)])); [Bus(Action.READ, pc+2)]));
} }
@ -2750,7 +2806,7 @@ if (isCpu!T)
cycles = 2 + branch + px; cycles = 2 + branch + px;
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~ Bus(Action.READ, pc+1)] ~
If!branch(If!(isStrict!T)( If!branch(If!(strict)(
[Bus(Action.READ, pc+2)] ~ [Bus(Action.READ, pc+2)] ~
If!px( If!px(
[Bus(Action.READ, wrongAddr)]))); [Bus(Action.READ, wrongAddr)])));
@ -2805,7 +2861,7 @@ if (isCpu!T)
cycles = 3; // + accesses_end cycles = 3; // + accesses_end
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~ Bus(Action.READ, pc+1)] ~
If!(isStrict!T)( If!(strict)(
If!(isNMOS!T)( If!(isNMOS!T)(
[Bus(Action.READ, op1)]) ~ [Bus(Action.READ, op1)]) ~
If!(isCMOS!T)( If!(isCMOS!T)(
@ -2861,7 +2917,7 @@ if (isCpu!T)
cycles = 5; // + accesses_end cycles = 5; // + accesses_end
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~ Bus(Action.READ, pc+1)] ~
If!(isStrict!T)( If!(strict)(
If!(isNMOS!T)( If!(isNMOS!T)(
[Bus(Action.READ, op1)]) ~ [Bus(Action.READ, op1)]) ~
If!(isCMOS!T)( If!(isCMOS!T)(
@ -2962,14 +3018,14 @@ if (isCpu!T)
if (guess != right) if (guess != right)
{ {
cycles += 1; cycles += 1;
return If!(isStrict!T)( return If!(strict)(
If!(isNMOS!T)([Bus(Action.READ, guess)]) ~ If!(isNMOS!T)([Bus(Action.READ, guess)]) ~
If!(isCMOS!T)([Bus(Action.READ, pc + opLen)])); // XXX If!(isCMOS!T)([Bus(Action.READ, pc + opLen)])); // XXX
} }
else if (noShortcut) else if (noShortcut)
{ {
cycles += 1; cycles += 1;
return If!(isStrict!T)([Bus(Action.READ, guess)]); return If!(strict)([Bus(Action.READ, guess)]);
} }
else else
{ {
@ -2998,13 +3054,13 @@ if (isCpu!T)
cycles += (rmw ? 3 : (write ? 1 : (1 + decimal))); cycles += (rmw ? 3 : (write ? 1 : (1 + decimal)));
return If!read( return If!read(
[Bus(Action.READ, addr)] ~ [Bus(Action.READ, addr)] ~
If!decimal(If!(isStrict!T)( If!decimal(If!(strict)(
[Bus(Action.READ, pc + opLen)]))) ~ [Bus(Action.READ, pc + opLen)]))) ~
If!write( If!write(
[Bus(Action.WRITE, addr)]) ~ [Bus(Action.WRITE, addr)]) ~
If!rmw( If!rmw(
[Bus(Action.READ, addr)] ~ [Bus(Action.READ, addr)] ~
If!(isStrict!T)( If!(strict)(
If!(isNMOS!T)( If!(isNMOS!T)(
[Bus(Action.WRITE, addr)]) ~ [Bus(Action.WRITE, addr)]) ~
If!(isCMOS!T)( If!(isCMOS!T)(
@ -3028,12 +3084,12 @@ if (isCpu!T)
cycles = 6; cycles = 6;
return [Bus(Action.READ, pc)] ~ return [Bus(Action.READ, pc)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+1), [Bus(Action.READ, pc+1),
Bus(Action.READ, sp)]) ~ Bus(Action.READ, sp)]) ~
[Bus(Action.READ, sp1), [Bus(Action.READ, sp1),
Bus(Action.READ, sp2)] ~ Bus(Action.READ, sp2)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, ret)]); [Bus(Action.READ, ret)]);
} }
@ -3053,7 +3109,7 @@ if (isCpu!T)
cycles = 6; cycles = 6;
return [Bus(Action.READ, pc)] ~ return [Bus(Action.READ, pc)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+1), [Bus(Action.READ, pc+1),
Bus(Action.READ, sp)]) ~ Bus(Action.READ, sp)]) ~
[Bus(Action.READ, sp1), [Bus(Action.READ, sp1),
@ -3076,7 +3132,7 @@ if (isCpu!T)
cycles = 7; cycles = 7;
return [Bus(Action.READ, pc)] ~ return [Bus(Action.READ, pc)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+1)]) ~ [Bus(Action.READ, pc+1)]) ~
[Bus(Action.WRITE, sp), [Bus(Action.WRITE, sp),
Bus(Action.WRITE, sp1), Bus(Action.WRITE, sp1),
@ -3100,7 +3156,7 @@ if (isCpu!T)
cycles = 6; cycles = 6;
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~ Bus(Action.READ, pc+1)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, sp)]) ~ [Bus(Action.READ, sp)]) ~
[Bus(Action.WRITE, sp), [Bus(Action.WRITE, sp),
Bus(Action.WRITE, sp1), Bus(Action.WRITE, sp1),
@ -3139,7 +3195,7 @@ if (isCpu!T)
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1), Bus(Action.READ, pc+1),
Bus(Action.READ, pc+2)] ~ Bus(Action.READ, pc+2)] ~
If!(isStrict!T)(If!(isCMOS!T)( If!(strict)(If!(isCMOS!T)(
[Bus(Action.READ, pc+3)])) ~ // XXX [Bus(Action.READ, pc+3)])) ~ // XXX
[Bus(Action.READ, ial), [Bus(Action.READ, ial),
Bus(Action.READ, iah)]; Bus(Action.READ, iah)];
@ -3169,7 +3225,7 @@ if (isCpu!T && isCMOS!T)
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1), Bus(Action.READ, pc+1),
Bus(Action.READ, pc+2)] ~ Bus(Action.READ, pc+2)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+3)]) ~ // XXX [Bus(Action.READ, pc+3)]) ~ // XXX
[Bus(Action.READ, ial), [Bus(Action.READ, ial),
Bus(Action.READ, iah)]; Bus(Action.READ, iah)];
@ -3195,7 +3251,7 @@ if (isCpu!T && isCMOS!T)
cycles = 8; cycles = 8;
return [Bus(Action.READ, pc), return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~ Bus(Action.READ, pc+1)] ~
If!(isStrict!T)( If!(strict)(
[Bus(Action.READ, pc+2), [Bus(Action.READ, pc+2),
Bus(Action.READ, weird), Bus(Action.READ, weird),
Bus(Action.READ, 0xFFFF), Bus(Action.READ, 0xFFFF),
@ -3334,3 +3390,59 @@ void test_opcode_timing(T)(ubyte opcode, busreport report)
auto run = connect(setup, run_timing_test!T(expected, report)); auto run = connect(setup, run_timing_test!T(expected, report));
run.run(opcode); run.run(opcode);
} }
struct CheckOptions
{
enum Addr
{
IMP, IMM, ZP, ZPX, ZPY, IZX, IZY, ABS, ABX, ABY, IND, REL,
ZPI, ABI, NP1, NP8, KIL
}
string[] opcodes;
ubyte[] codes6502;
ubyte[] codes65C02;
Addr[] addr;
this(string[] args)
{
getopt(args, "op", &opcodes, "addr", &addr);
foreach (op; opcodes)
{
if (op.startsWith("0x") || op.startsWith("0X"))
op = op[2..$];
if (isNumeric(op))
{
int code = to!int(op, 16);
if (code >= 0x00 && code <= 0xFF)
{
codes6502 ~= [cast(ubyte)code];
codes65C02 ~= [cast(ubyte)code];
}
}
else
{
foreach (code, name; OP_NAMES_6502)
if (name == op) codes6502 ~= [cast(ubyte)code];
foreach (code, name; OP_NAMES_65C02)
if (name == op) codes65C02 ~= [cast(ubyte)code];
}
}
foreach (a; addr)
{
foreach (code, mode; ADDR_MODES_6502)
if (a == mode) codes6502 ~= [cast(ubyte)code];
foreach (code, mode; ADDR_MODES_65C02)
if (a == mode) codes65C02 ~= [cast(ubyte)code];
}
if (opcodes.empty && addr.empty)
{
codes6502 = codes65C02 = new ubyte[256];
foreach (code; 0..256)
{
codes6502[code] = cast(ubyte)code;
}
}
}
}

View File

@ -8,66 +8,71 @@ module test.cpu;
import std.conv, std.exception, std.random, std.string, std.traits; import std.conv, std.exception, std.random, std.string, std.traits;
public import d6502.nmosundoc : NmosUndoc; public import cpu.d6502 : Cpu, is6502, is65C02;
public import d6502.cmos : Cmos;
import test.base : strict, cumulative;
// True if T is the type of a cpu. // True if T is the type of a cpu.
template isCpu(T) template isCpu(T)
{ {
enum isCpu = __traits(hasMember, T, "_isCpuBase"); enum isCpu = __traits(hasMember, T, "_isCpu");
} }
// True if the cpu type T represents a 6502. // True if the cpu type T represents a 6502.
template isNMOS(T) template isNMOS(T)
{ {
enum isNMOS = __traits(hasMember, T, "_isNMOS"); enum isNMOS = is6502!T;
} }
// True if the cpu type T represents a 65C02. // True if the cpu type T represents a 65C02.
template isCMOS(T) template isCMOS(T)
{ {
enum isCMOS = __traits(hasMember, T, "_isCMOS"); enum isCMOS = is65C02!T;
} }
// True if the cpu type T accesses memory on every cycle.
template isStrict(T)
{
enum isStrict = __traits(hasMember, T, "_isStrict");
}
// True if the cpu type T aggregates ticks. class TestIO
template isCumulative(T)
{ {
enum isCumulative = __traits(hasMember, T, "_isCumulative"); ubyte delegate(ushort) dread;
ubyte read(ushort addr) { return dread(addr); }
void delegate(ushort, ubyte) dwrite;
void write(ushort addr, ubyte val) { dwrite(addr, val); }
static if (cumulative)
{
void delegate(int) dtick;
void tick(int cycles) { dtick(cycles); }
}
else
{
void delegate() dtick;
void tick() { dtick(); }
}
} }
/* /*
* The type of a cpu, based on its architecture (6502 or 65C02) and * The type of a cpu, based on its architecture (6502 or 65C02).
* its timing characteristics (strict or not bus access, cumulative or
* not cycle reporting).
*/ */
template CPU(string arch, bool strict, bool cumulative) template CPU(string arch, M = TestIO, C = TestIO)
{ {
static if (arch == "65c02" || arch == "65C02") alias Cpu!(arch, M, C) CPU;
alias Cmos!(strict, cumulative) CPU;
else static if (arch == "6502")
alias NmosUndoc!(strict, cumulative) CPU;
else static assert(0);
} }
auto makeCpu(T)(CpuInfo info) auto makeCpu(T)(CpuInfo info = CpuInfo())
if (isCpu!T) if (isCpu!T)
{ {
auto cpu = new T(); auto tio = new TestIO();
cpu.programCounter = info.PC; auto cpu = new T(tio, tio);
cpu.stackPointer = info.SP; cpu.PC = info.PC;
cpu.flag.fromByte(info.S); cpu.S = info.SP;
cpu.accumulator = info.A; cpu.statusFromByte(info.S);
cpu.xIndex = info.X; cpu.A = info.A;
cpu.yIndex = info.Y; cpu.X = info.X;
cpu.Y = info.Y;
return cpu; return cpu;
} }
@ -75,14 +80,14 @@ if (isCpu!T)
void connectMem(T, S)(T cpu, ref S mem) void connectMem(T, S)(T cpu, ref S mem)
if (isCpu!T) if (isCpu!T)
{ {
static if (isCumulative!T) static if (cumulative)
void tick(int cycles) {} void tick(int cycles) {}
else else
void tick() {} void tick() {}
cpu.memoryRead = &mem.read; cpu.memory.dread = &mem.read;
cpu.memoryWrite = &mem.write; cpu.memory.dwrite = &mem.write;
cpu.tick = &tick; cpu.clock.dtick = &tick;
} }
@ -99,9 +104,9 @@ auto recordCycles(T)(T cpu)
if (isCpu!T) if (isCpu!T)
{ {
auto cycles = new int; auto cycles = new int;
auto wrappedTick = cpu.tick; auto wrappedTick = cpu.clock.dtick;
static if (isCumulative!T) static if (cumulative)
{ {
void tick(int cyc) void tick(int cyc)
{ {
@ -117,7 +122,7 @@ if (isCpu!T)
wrappedTick(); wrappedTick();
} }
} }
cpu.tick = &tick; cpu.clock.dtick = &tick;
return constRef(cycles); return constRef(cycles);
} }
@ -155,9 +160,9 @@ if (isCpu!T)
auto record = new Bus[actions]; auto record = new Bus[actions];
int c; int c;
enforce(cpu.memoryRead !is null && cpu.memoryWrite !is null); enforce(cpu.memory.dread !is null && cpu.memory.dwrite !is null);
auto wrappedRead = cpu.memoryRead; auto wrappedRead = cpu.memory.dread;
auto wrappedWrite = cpu.memoryWrite; auto wrappedWrite = cpu.memory.dwrite;
ubyte read(ushort addr) ubyte read(ushort addr)
{ {
@ -177,8 +182,8 @@ if (isCpu!T)
wrappedWrite(addr, val); wrappedWrite(addr, val);
} }
cpu.memoryRead = &read; cpu.memory.dread = &read;
cpu.memoryWrite = &write; cpu.memory.dwrite = &write;
return record; return record;
} }
@ -210,8 +215,8 @@ enum Action : ushort { NONE, READ, WRITE }
void runUntilBRK(T)(T cpu) void runUntilBRK(T)(T cpu)
if (isCpu!T) if (isCpu!T)
{ {
assert(cpu.memoryRead !is null); assert(cpu.memory.dread !is null);
auto wrappedRead = cpu.memoryRead; auto wrappedRead = cpu.memory.dread;
ubyte read(ushort addr) ubyte read(ushort addr)
{ {
@ -219,7 +224,7 @@ if (isCpu!T)
return wrappedRead(addr); return wrappedRead(addr);
} }
cpu.memoryRead = &read; cpu.memory.dread = &read;
try { cpu.run(true); } catch (StopException e) {} try { cpu.run(true); } catch (StopException e) {}
} }
@ -244,32 +249,27 @@ struct CpuInfo
string toString() const string toString() const
{ {
return format("PC %0.4X SP %0.2X S %0.2X A %0.2X X %0.2X Y %0.2X", return format("PC %0.4X SP %0.2X S %0.8b A %0.2X X %0.2X Y %0.2X",
PC, SP, S, A, X, Y); PC, SP, S, A, X, Y);
} }
static CpuInfo fromCpu(T)(T cpu) static CpuInfo fromCpu(T)(T cpu)
{ {
CpuInfo info; CpuInfo info;
info.PC = cpu.programCounter; info.PC = cpu.PC;
info.SP = cpu.stackPointer; info.SP = cpu.S;
info.A = cpu.accumulator; info.A = cpu.A;
info.X = cpu.xIndex; info.X = cpu.X;
info.Y = cpu.yIndex; info.Y = cpu.Y;
info.S = cpu.flag.toByte(); info.S = cpu.statusToByte();
return info; return info;
} }
} }
// Sets the program counter. // Sets the program counter.
void setPC(T)(T cpu, int addr) void setPC(T)(ref T cpu, int addr)
if (isCpu!T) if (isCpu!T || is(T == CpuInfo))
{
cpu.programCounter = cast(ushort)addr;
}
void setPC(T : CpuInfo)(ref T cpu, int addr)
{ {
cpu.PC = cast(ushort)addr; cpu.PC = cast(ushort)addr;
} }
@ -279,14 +279,10 @@ void incPC(T : CpuInfo)(ref T cpu, int amt = 1)
cpu.PC = pageCrossAdd(cpu.PC, amt); cpu.PC = pageCrossAdd(cpu.PC, amt);
} }
// Returns the program counter.
ushort getPC(T)(T cpu)
if (isCpu!T)
{
return cpu.programCounter;
}
ushort getPC(T : CpuInfo)(ref T cpu) // Returns the program counter.
ushort getPC(T)(ref T cpu)
if (isCpu!T || is(T == CpuInfo))
{ {
return cpu.PC; return cpu.PC;
} }
@ -302,7 +298,7 @@ void setSP(T)(T cpu, int val)
if (isCpu!T) if (isCpu!T)
{ {
assert(val < 0x0200); assert(val < 0x0200);
cpu.stackPointer = cast(ubyte)val; cpu.S = cast(ubyte)val;
} }
void setSP(T : CpuInfo)(ref T cpu, int val) void setSP(T : CpuInfo)(ref T cpu, int val)
@ -329,7 +325,7 @@ void decSP(T : CpuInfo)(ref T cpu, int amt = -1)
ushort getSP(T)(T cpu) ushort getSP(T)(T cpu)
if (isCpu!T) if (isCpu!T)
{ {
return 0x100 | cpu.stackPointer; return 0x100 | cpu.S;
} }
ushort getSP(T : CpuInfo)(ref T cpu) ushort getSP(T : CpuInfo)(ref T cpu)
@ -369,75 +365,48 @@ if (isCpu!T || is(T == CpuInfo))
} }
// Sets the A register. // Sets the A register.
void setA(T)(T cpu, int val) void setA(T)(ref T cpu, int val)
if (isCpu!T) if (isCpu!T || is(T == CpuInfo))
{
cpu.accumulator = cast(ubyte)val;
}
void setA(T : CpuInfo)(ref T cpu, int val)
{ {
cpu.A = cast(ubyte)val; cpu.A = cast(ubyte)val;
} }
// Returns the A register.
ubyte getA(T)(T cpu, int val)
if (isCpu!T)
{
return cpu.accumulator;
}
ubyte getA(T : CpuInfo)(ref T cpu) // Returns the A register.
ubyte getA(T)(ref T cpu)
if (isCpu!T || is(T == CpuInfo))
{ {
return cpu.A; return cpu.A;
} }
// Sets the X register. // Sets the X register.
void setX(T)(T cpu, int val) void setX(T)(ref T cpu, int val)
if (isCpu!T) if (isCpu!T || is(T == CpuInfo))
{
cpu.xIndex = cast(ubyte)val;
}
void setX(T : CpuInfo)(ref T cpu, int val)
{ {
cpu.X = cast(ubyte)val; cpu.X = cast(ubyte)val;
} }
// Returns the X register.
ubyte getX(T)(T cpu)
if (isCpu!T)
{
return cpu.xIndex;
}
ubyte getX(T : CpuInfo)(ref T cpu) // Returns the X register.
ubyte getX(T)(ref T cpu)
if (isCpu!T || is(T == CpuInfo))
{ {
return cpu.X; return cpu.X;
} }
// Sets the Y register. // Sets the Y register.
void setY(T)(T cpu, int val) void setY(T)(ref T cpu, int val)
if (isCpu!T) if (isCpu!T || is(T == CpuInfo))
{
cpu.yIndex = cast(ubyte)val;
}
void setY(T : CpuInfo)(ref T cpu, int val)
{ {
cpu.Y = cast(ubyte)val; cpu.Y = cast(ubyte)val;
} }
// Returns the Y register.
ubyte getY(T)(T cpu)
if (isCpu!T)
{
return cpu.yIndex;
}
ubyte getY(T : CpuInfo)(ref T cpu) // Returns the Y register.
ubyte getY(T)(ref T cpu)
if (isCpu!T || is(T == CpuInfo))
{ {
return cpu.Y; return cpu.Y;
} }
@ -472,9 +441,9 @@ string flagToString(Flag f)
void setFlag(T)(T cpu, Flag[] flags...) void setFlag(T)(T cpu, Flag[] flags...)
if (isCpu!T) if (isCpu!T)
{ {
auto reg = cpu.flag.toByte(); auto reg = cpu.statusToByte();
foreach (flag; flags) reg |= flag; foreach (flag; flags) reg |= flag;
cpu.flag.fromByte(reg); cpu.statusFromByte(reg);
} }
void setFlag(T : CpuInfo)(ref T cpu, Flag[] flags...) void setFlag(T : CpuInfo)(ref T cpu, Flag[] flags...)
@ -486,9 +455,9 @@ void setFlag(T : CpuInfo)(ref T cpu, Flag[] flags...)
void clearFlag(T)(T cpu, Flag[] flags...) void clearFlag(T)(T cpu, Flag[] flags...)
if (isCpu!T) if (isCpu!T)
{ {
auto reg = cpu.flag.toByte(); auto reg = cpu.statusToByte();
foreach (flag; flags) reg &= ~flag; foreach (flag; flags) reg &= ~flag;
cpu.flag.fromByte(reg); cpu.statusFromByte(reg);
} }
void clearFlag(T : CpuInfo)(ref T cpu, Flag[] flags...) void clearFlag(T : CpuInfo)(ref T cpu, Flag[] flags...)
@ -500,7 +469,8 @@ void clearFlag(T : CpuInfo)(ref T cpu, Flag[] flags...)
bool getFlag(T)(T cpu, Flag f) bool getFlag(T)(T cpu, Flag f)
if (isCpu!T) if (isCpu!T)
{ {
return (cpu.flag.toByte() & f) != 0; return (cpu.statusToByte() & f) != 0;
return false;
} }
bool getFlag(T : CpuInfo)(ref T cpu, Flag f) bool getFlag(T : CpuInfo)(ref T cpu, Flag f)
@ -513,7 +483,7 @@ bool getFlag(T : CpuInfo)(ref T cpu, Flag f)
void setStatus(T)(T cpu, int val) void setStatus(T)(T cpu, int val)
if (isCpu!T) if (isCpu!T)
{ {
cpu.flag.fromByte(cast(ubyte)val); cpu.statusFromByte(cast(ubyte)val);
} }
void setStatus(T : CpuInfo)(ref T cpu, int val) void setStatus(T : CpuInfo)(ref T cpu, int val)
@ -525,7 +495,8 @@ void setStatus(T : CpuInfo)(ref T cpu, int val)
ubyte getStatus(T)(T cpu) ubyte getStatus(T)(T cpu)
if (isCpu!T) if (isCpu!T)
{ {
return cpu.flag.toByte(); return cpu.statusToByte();
return 0;
} }
ubyte getStatus(T : CpuInfo)(ref T cpu) ubyte getStatus(T : CpuInfo)(ref T cpu)
@ -604,8 +575,6 @@ if (isCpu!T || is(T == CpuInfo))
case /*BNE*/ 0xD0: setFlag(cpu, Flag.Z); break; case /*BNE*/ 0xD0: setFlag(cpu, Flag.Z); break;
case /*BEQ*/ 0xF0: clearFlag(cpu, Flag.Z); break; case /*BEQ*/ 0xF0: clearFlag(cpu, Flag.Z); break;
default: default:
if (isCMOS!T)
enforce(opcode != 0x80, "BRA can never not branch");
enforce(0, format("not a branching opcpde %0.2X", opcode)); enforce(0, format("not a branching opcpde %0.2X", opcode));
} }
} }

140
test/runtests.d Normal file
View File

@ -0,0 +1,140 @@
import std.array, std.exception, std.getopt, std.process, std.stdio,
std.traits;
enum OpDefs
{
None,
Delegates = 1,
Switch = 2,
NestedSwitch = 4,
All = 7
}
enum Tests
{
None,
Func = 1,
Bus = 2,
Dec = 4,
All = 7
}
string[OpDefs] defStrings;
string[Tests] fNames;
static this()
{
fNames = [
Tests.Func:" test_func.d ",
Tests.Bus:" test_bus.d ",
Tests.Dec:" test_decimal.d "
];
}
version(DigitalMars)
{
static this()
{
defStrings = [
OpDefs.Delegates:" -version=OpDelegates",
OpDefs.Switch:" -version=OpSwitch",
OpDefs.NestedSwitch:" -version=OpNestedSwitch"
];
}
string[] stStrings = [" ", " -version=Strict"];
string[] cmStrings = [" ", " -version=Cumulative"];
}
else version(GNU)
{
static assert(false, "TODO: add support for GDC.");
}
else version(LDC)
{
static assert(false, "TODO: add support for LDC.");
}
else
static assert(false, "Unknown compiler.");
OpDefs opdefs;
bool strict, cumulative;
Tests tests;
bool help;
OpDefs[] deflist;
Tests[] testlist;
void main(string[] args)
{
if (args.length == 1)
writeln("(running default tests; use --help for options)");
getopt(args,
std.getopt.config.passThrough,
"def", &deflist,
"test", &testlist,
"help", &help);
if (help)
{
writeln(
`Options:
--test=type Func, Bus, Dec, or All
--def=style Delegates, Switch, or NestedSwitch
--op=num test opcode 'num' (num is hex)
--op=name test all opcodes named 'name'
--addr=mode test all opcodes with addressing mode 'mode'
(All options con be specified multiple times.
--op and --addr have no effect on decimal mode tests.)`
);
return;
}
foreach(def; deflist) opdefs |= def;
foreach(test; testlist) tests |= test;
try
{
runTests(args);
}
catch (ErrnoException e) {}
}
void runTests(string[] args)
{
// If no opdef specified, use Delegates.
if (opdefs == OpDefs.None) opdefs = OpDefs.Delegates;
int defcount;
foreach (def; EnumMembers!OpDefs)
if ((opdefs & def) && def != OpDefs.All) defcount++;
// If no tests specified, run all (but exclude Dec by default if
// running with more than one opdef).
if (tests == Tests.None)
tests = Tests.Func | Tests.Bus;
if (!defcount) tests |= Tests.Dec;
foreach (def; EnumMembers!OpDefs)
if ((opdefs & def) && def != OpDefs.All)
foreach (test; EnumMembers!Tests)
if ((tests & test) && test != Tests.All)
runTest(def, test, args[1..$]);
}
void runTest(OpDefs def, Tests test, string[] args)
{
writeln("Using ", defStrings[def]);
foreach (s; [false, true])
{
foreach (c; [false, true])
{
writeln("With strict=", s, " cumulative=", c);
string cmdline = defStrings[def] ~ stStrings[s] ~ cmStrings[c] ~
fNames[test] ~ join(args, " ");
system("rdmd --force -I.. -I../src -version=RunTest " ~ cmdline);
}
}
}

View File

@ -1,39 +1,20 @@
import std.stdio;
import test.base, test.cpu; import test.base, test.cpu;
void main() void main(string[] args)
{ {
auto opts = CheckOptions(args);
auto report = report_timing_debug(); auto report = report_timing_debug();
alias CPU!("65C02", false, false) T1; alias CPU!("6502") T1;
for (int op = 0x00; op < 0x100; op++) writeln("Testing bus/timing, 6502");
foreach (op; opts.codes6502)
test_opcode_timing!T1(cast(ubyte)op, report); test_opcode_timing!T1(cast(ubyte)op, report);
alias CPU!("65C02", true, false) T2; alias CPU!("65C02") T2;
for (int op = 0x00; op < 0x100; op++) writeln("Testing bus/timing, 65C02");
foreach (op; opts.codes65C02)
test_opcode_timing!T2(cast(ubyte)op, report); test_opcode_timing!T2(cast(ubyte)op, report);
alias CPU!("6502", false, false) T3;
for (int op = 0x00; op < 0x100; op++)
test_opcode_timing!T3(cast(ubyte)op, report);
alias CPU!("6502", true, false) T4;
for (int op = 0x00; op < 0x100; op++)
test_opcode_timing!T4(cast(ubyte)op, report);
alias CPU!("65C02", false, true) T5;
for (int op = 0x00; op < 0x100; op++)
test_opcode_timing!T5(cast(ubyte)op, report);
alias CPU!("65C02", true, true) T6;
for (int op = 0x00; op < 0x100; op++)
test_opcode_timing!T6(cast(ubyte)op, report);
alias CPU!("6502", false, true) T7;
for (int op = 0x00; op < 0x100; op++)
test_opcode_timing!T7(cast(ubyte)op, report);
alias CPU!("6502", true, true) T8;
for (int op = 0x00; op < 0x100; op++)
test_opcode_timing!T8(cast(ubyte)op, report);
} }

View File

@ -1,4 +0,0 @@
rdmd @testopts test_func.d
rdmd @testopts test_bus.d
rdmd @testopts test_decimal.d

View File

@ -211,10 +211,21 @@ if (isCpu!T)
mem.write(0x8055, 0x84); mem.write(0x8055, 0x84);
} }
auto cpu = new T(); version(Benchmark)
connectMem(cpu, mem); {
auto runner = new BreakRunner(mem);
auto cpu = new T(runner, runner);
runner.keepRunning = &cpu.keepRunning;
setPC(cpu, 0x8000); setPC(cpu, 0x8000);
cpu.run(true);
}
else
{
auto cpu = makeCpu!T();
setPC(cpu, 0x8000);
connectMem(cpu, mem);
runUntilBRK(cpu); runUntilBRK(cpu);
}
if (mem[0x8003]) if (mem[0x8003])
{ {
// TODO: check data block to find out what failed exactly // TODO: check data block to find out what failed exactly
@ -226,9 +237,43 @@ if (isCpu!T)
version(Benchmark) version(Benchmark)
{ {
import std.datetime, std.stdio; import std.datetime, std.stdio;
final class BreakRunner
{
TestMemory* mem;
bool* keepRunning;
this(ref TestMemory mem)
{
this.mem = &mem;
}
final ubyte read(ushort addr)
{
if (addr == 0xfffe)
{
*keepRunning = false;
return 0x00;
}
else if (addr == 0xffff)
{
return 0x80;
}
else return mem.read(addr);
}
final void write(ushort addr, ubyte val)
{
mem.write(addr, val);
}
static if (cumulative) { final void tick(int) {} }
else { final void tick() {} }
}
void f0() void f0()
{ {
testDecimalMode!(CPU!("65C02", false, false))(); testDecimalMode!(CPU!("65C02", BreakRunner, BreakRunner))();
} }
void main() void main()
@ -243,24 +288,10 @@ else
{ {
void main() void main()
{ {
writeln("Testing decimal mode, NMOS(Strict.no, Cumulative.no)"); writeln("Testing decimal mode, 6502");
testDecimalMode!(CPU!("6502", false, false))(); testDecimalMode!(CPU!("6502"))();
writeln("Testing decimal mode, CMOS(Strict.no, Cumulative.no)");
testDecimalMode!(CPU!("65C02", false, false))();
writeln("Testing decimal mode, NMOS(Strict.no, Cumulative.yes)"); writeln("Testing decimal mode, 65C02");
testDecimalMode!(CPU!("6502", false, true))(); testDecimalMode!(CPU!("65C02"))();
writeln("Testing decimal mode, CMOS(Strict.no, Cumulative.yes)");
testDecimalMode!(CPU!("65C02", false, true))();
writeln("Testing decimal mode, NMOS(Strict.yes, Cumulative.no)");
testDecimalMode!(CPU!("6502", true, false))();
writeln("Testing decimal mode, CMOS(Strict.yes, Cumulative.no)");
testDecimalMode!(CPU!("65C02", true, false))();
writeln("Testing decimal mode, NMOS(Strict.yes, Cumulative.yes)");
testDecimalMode!(CPU!("6502", true, true))();
writeln("Testing decimal mode, CMOS(Strict.yes, Cumulative.yes)");
testDecimalMode!(CPU!("65C02", true, true))();
} }
} }

View File

@ -1,15 +1,20 @@
import std.stdio;
import test.base, test.cpu; import test.base, test.cpu;
void main() void main(string[] args)
{ {
auto opts = CheckOptions(args);
auto report = report_debug(); auto report = report_debug();
alias CPU!("65C02", false, false) T1; alias CPU!("6502") T1;
foreach (opcode; 0..255) writeln("Testing functionality, 6502");
foreach (opcode; opts.codes6502)
test_one_opcode!T1(cast(ubyte)opcode, report); test_one_opcode!T1(cast(ubyte)opcode, report);
alias CPU!("6502", false, false) T2; alias CPU!("65C02") T2;
foreach (opcode; 0..255) writeln("Testing functionality, 65C02");
foreach (opcode; opts.codes65C02)
test_one_opcode!T2(cast(ubyte)opcode, report); test_one_opcode!T2(cast(ubyte)opcode, report);
} }