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
```
### Testing
There are tests for the 6502/65C02 emulation:
```
cd test
rdmd runtests.d --help
```
### Use
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 \
-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 \
-L-L$(GTKD) -L-lgtkd -L-lgtkdgl \
-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}
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 memory;
import d6502.base;
private alias d6502.base.CpuBase!(Strict.no, Cumulative.no) CpuBase;
import cpu.d6502;
import ui.sound;
import ui.inputevents;
@ -36,14 +34,31 @@ import system.video;
import iomem;
import video.base;
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.Cycle deviceCycle;
AddressDecoder decoder;
SoftSwitchPage switches;
CpuBase cpu;
Cpu!(chip, AddressDecoder, Timer) cpu;
// XXX
bool* cpuRun, signalActive, resetLow;
IOMem ioMem;
Peripherals peripherals;
@ -73,15 +88,15 @@ class System
}
}
Video video_;
Memory memory_;
IO io_;
abstract IO newIO();
abstract CpuBase newCpu();
abstract Video newVideo(ubyte[] vidRom);
abstract IOMem newIOMem();
abstract Peripherals newPeripherals();
static if (chip == "65C02")
{
AuxiliaryCard auxCard;
MMU mmu;
IOU iou;
}
this(ubyte[] romDump)
{
@ -91,23 +106,33 @@ class System
initIO(null); // XXX where is vidRom passed in?
decoder.nullRead = &video_.scanner.floatingBus;
peripherals = newPeripherals();
peripherals.install(cpu, decoder, memory_.mainRom);
static if (chip == "6502")
peripherals = new Peripherals_II();
else
peripherals = new Peripherals_IIe();
peripherals.install(decoder, memory_.mainRom);
ioMem.initialize(decoder, switches, timer, peripherals);
input.onReset = &reset;
switches.setFloatingBus(&video_.scanner.floatingBus);
}
void reboot()
override void reboot()
{
cpu.reboot();
// XXX replace
//cpu.reboot();
deviceCycle.restart();
memory_.reboot();
ioMem.reboot();
io_.reboot();
peripherals.reboot();
video_.reboot();
static if (chip == "65C02")
{
auxCard.reboot();
mmu.reboot();
}
}
void initTimer()
@ -120,56 +145,92 @@ class System
void initMemory(ubyte[] romDump)
{
static if (chip == "65C02")
{
mmu = new MMU();
mmu.ioMem = new IOMem_IIe();
mmu.ioMem.setRom(romDump);
}
memory_ = new Memory(romDump);
decoder = new AddressDecoder();
switches = new SoftSwitchPage();
decoder.installSwitches(switches);
decoder.install(memory_.mainRam);
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()
{
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;
cpu.tick = &timer.tick;
timer.onPrimaryStop(&primaryStop);
cpu.memoryRead = &decoder.read;
cpu.memoryWrite = &decoder.write;
}
void initIO(ubyte[] vidRom)
{
io_ = newIO();
video_ = newVideo(vidRom);
static if (chip == "6502")
{
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()
{
cpu.stop();
*cpuRun = false;
return true;
}
void reset()
override void reset()
{
static if (chip == "65C02")
{
auxCard.reset();
mmu.reset();
}
peripherals.reset();
cpu.resetLow();
*signalActive = true;
*resetLow = true;
}
uint checkpoint()
override uint checkpoint()
{
return timer.primaryCounter.currentLength;
}
uint sinceCheckpoint(uint cp)
override uint sinceCheckpoint(uint cp)
{
uint currentLength = timer.primaryCounter.currentLength;
return ((currentLength == timer.primaryCounter.startLength) ?
cp : (cp - currentLength));
}
void execute()
override void execute()
{
cpu.run(true);
@ -181,126 +242,3 @@ class System
// 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;
import memory;
import d6502.base;
private alias d6502.base.CpuBase!(Strict.no, Cumulative.no) CpuBase;
import peripheral.base;
import peripheral.diskii;
@ -36,7 +33,7 @@ class Peripherals
{
Peripheral[8] cards;
abstract void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom);
abstract void install(AddressDecoder decoder, Rom mainRom);
void reboot()
{
@ -59,7 +56,7 @@ class Peripherals
class Peripherals_II : Peripherals
{
void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom)
void install(AddressDecoder decoder, Rom mainRom)
{
auto diskController = new Controller();
cards[6] = diskController; // XXX
@ -78,7 +75,7 @@ class Peripherals_II : Peripherals
class Peripherals_IIe : Peripherals
{
void install(CpuBase cpu, AddressDecoder decoder, Rom mainRom)
void install(AddressDecoder decoder, Rom mainRom)
{
auto diskController = new Controller();
cards[6] = diskController; // XXX

View File

@ -20,7 +20,7 @@
import std.stdio;
import d6502.base;
import cpu.d6502;
import timer;
import memory;
import system.base;
@ -39,20 +39,6 @@ import peripheral.diskii;
import peripheral.langcard;
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.string;
import device.speaker;
@ -86,11 +72,11 @@ void main(string[] args)
TwoappleFile romFile = TwoappleFilePicker.open("ROM file", &checkRomFile);
if (romFile is null) return;
System sys;
SystemBase sys;
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
sys = new II(cast(ubyte[])std.file.read(romFile.fileName));
sys = new System!"6502"(cast(ubyte[])std.file.read(romFile.fileName));
appWindow.initSystem(sys);
// XXX hack
appWindow.configChanged = true;

View File

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

View File

@ -1,10 +1,130 @@
module test.base;
import std.algorithm, std.array, std.conv, std.exception, std.stdio,
std.string;
import std.algorithm, std.array, std.conv, std.exception, std.getopt,
std.stdio, std.string;
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.
*
@ -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.
auto setup_none()
{
@ -1232,8 +1288,8 @@ auto setup_op_LSR(bool isAcc)
}
// For ASO.
auto setup_op_ASO()
// For SLO.
auto setup_op_SLO()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
@ -1249,7 +1305,7 @@ auto setup_op_ASO()
callNext("acc 0x20 ");
}
return connect(TestSetup(&setup),
setup_rmw(false, "ASO ", setup_asl_data()));
setup_rmw(false, "SLO ", setup_asl_data()));
}
// For RLA.
@ -1270,8 +1326,8 @@ auto setup_op_RLA()
}
// For LSE.
auto setup_op_LSE()
// For SRE.
auto setup_op_SRE()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
@ -1284,7 +1340,7 @@ auto setup_op_LSE()
callNext("acc 0xFF ");
}
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.
auto setup_op_INS()
// For ISC.
auto setup_op_ISC()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
@ -1400,7 +1456,7 @@ auto setup_op_INS()
}
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.
auto setup_op_DCM()
// For DCP.
auto setup_op_DCP()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
@ -1445,7 +1501,7 @@ auto setup_op_DCM()
}
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.
auto expect_ASO()
// For SLO.
auto expect_SLO()
{
void expect(ref Expected expected, const OpInfo info)
{
@ -2138,8 +2194,8 @@ auto expect_RLA()
}
// For LSE.
auto expect_LSE()
// For SRE.
auto expect_SRE()
{
void expect(ref Expected expected, const OpInfo info)
{
@ -2306,8 +2362,8 @@ auto expect_SBC(bool cmos)
}
// For INS.
auto expect_INS()
// For ISC.
auto expect_ISC()
{
void expect(ref Expected expected, const OpInfo info)
{
@ -2342,8 +2398,8 @@ auto expect_cmp(Reg reg)
}
// For DCM.
auto expect_DCM()
// For DCP.
auto expect_DCP()
{
void expect(ref Expected expected, const OpInfo info)
{
@ -2510,12 +2566,12 @@ if (isCpu!T)
get_expect([0x04, 0x44, 0x64], "NOP");
get_expect([0x14, 0x34, 0x54, 0x74, 0xD4, 0xF4], "NOP");
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([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([0xE3, 0xE7, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF], "INS");
get_both([0xC3, 0xC7, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF], "DCM");
get_both([0xE3, 0xE7, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF], "ISC");
get_both([0xC3, 0xC7, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF], "DCP");
get_both([0xEB], "SBC", "false");
// TODO: implement these opcode tests
@ -2671,7 +2727,7 @@ if (isCpu!T)
cycles = 2;
return [Bus(Action.READ, pc)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+1)]);
}
@ -2688,7 +2744,7 @@ if (isCpu!T)
cycles = 3;
return [Bus(Action.READ, pc)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+1)]) ~
[Bus(Action.WRITE, sp)];
}
@ -2707,7 +2763,7 @@ if (isCpu!T)
cycles = 4;
return [Bus(Action.READ, pc)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+1),
Bus(Action.READ, sp)]) ~
[Bus(Action.READ, sp1)];
@ -2728,7 +2784,7 @@ if (isCpu!T)
cycles = 2 + decimal;
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~
If!decimal(If!(isStrict!T)(
If!decimal(If!(strict)(
[Bus(Action.READ, pc+2)]));
}
@ -2750,7 +2806,7 @@ if (isCpu!T)
cycles = 2 + branch + px;
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~
If!branch(If!(isStrict!T)(
If!branch(If!(strict)(
[Bus(Action.READ, pc+2)] ~
If!px(
[Bus(Action.READ, wrongAddr)])));
@ -2805,7 +2861,7 @@ if (isCpu!T)
cycles = 3; // + accesses_end
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~
If!(isStrict!T)(
If!(strict)(
If!(isNMOS!T)(
[Bus(Action.READ, op1)]) ~
If!(isCMOS!T)(
@ -2861,7 +2917,7 @@ if (isCpu!T)
cycles = 5; // + accesses_end
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~
If!(isStrict!T)(
If!(strict)(
If!(isNMOS!T)(
[Bus(Action.READ, op1)]) ~
If!(isCMOS!T)(
@ -2962,14 +3018,14 @@ if (isCpu!T)
if (guess != right)
{
cycles += 1;
return If!(isStrict!T)(
return If!(strict)(
If!(isNMOS!T)([Bus(Action.READ, guess)]) ~
If!(isCMOS!T)([Bus(Action.READ, pc + opLen)])); // XXX
}
else if (noShortcut)
{
cycles += 1;
return If!(isStrict!T)([Bus(Action.READ, guess)]);
return If!(strict)([Bus(Action.READ, guess)]);
}
else
{
@ -2998,13 +3054,13 @@ if (isCpu!T)
cycles += (rmw ? 3 : (write ? 1 : (1 + decimal)));
return If!read(
[Bus(Action.READ, addr)] ~
If!decimal(If!(isStrict!T)(
If!decimal(If!(strict)(
[Bus(Action.READ, pc + opLen)]))) ~
If!write(
[Bus(Action.WRITE, addr)]) ~
If!rmw(
[Bus(Action.READ, addr)] ~
If!(isStrict!T)(
If!(strict)(
If!(isNMOS!T)(
[Bus(Action.WRITE, addr)]) ~
If!(isCMOS!T)(
@ -3028,12 +3084,12 @@ if (isCpu!T)
cycles = 6;
return [Bus(Action.READ, pc)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+1),
Bus(Action.READ, sp)]) ~
[Bus(Action.READ, sp1),
Bus(Action.READ, sp2)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, ret)]);
}
@ -3053,7 +3109,7 @@ if (isCpu!T)
cycles = 6;
return [Bus(Action.READ, pc)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+1),
Bus(Action.READ, sp)]) ~
[Bus(Action.READ, sp1),
@ -3076,7 +3132,7 @@ if (isCpu!T)
cycles = 7;
return [Bus(Action.READ, pc)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+1)]) ~
[Bus(Action.WRITE, sp),
Bus(Action.WRITE, sp1),
@ -3100,7 +3156,7 @@ if (isCpu!T)
cycles = 6;
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, sp)]) ~
[Bus(Action.WRITE, sp),
Bus(Action.WRITE, sp1),
@ -3139,7 +3195,7 @@ if (isCpu!T)
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1),
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, ial),
Bus(Action.READ, iah)];
@ -3169,7 +3225,7 @@ if (isCpu!T && isCMOS!T)
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1),
Bus(Action.READ, pc+2)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+3)]) ~ // XXX
[Bus(Action.READ, ial),
Bus(Action.READ, iah)];
@ -3195,7 +3251,7 @@ if (isCpu!T && isCMOS!T)
cycles = 8;
return [Bus(Action.READ, pc),
Bus(Action.READ, pc+1)] ~
If!(isStrict!T)(
If!(strict)(
[Bus(Action.READ, pc+2),
Bus(Action.READ, weird),
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));
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;
public import d6502.nmosundoc : NmosUndoc;
public import d6502.cmos : Cmos;
public import cpu.d6502 : Cpu, is6502, is65C02;
import test.base : strict, cumulative;
// True if T is the type of a cpu.
template isCpu(T)
{
enum isCpu = __traits(hasMember, T, "_isCpuBase");
enum isCpu = __traits(hasMember, T, "_isCpu");
}
// True if the cpu type T represents a 6502.
template isNMOS(T)
{
enum isNMOS = __traits(hasMember, T, "_isNMOS");
enum isNMOS = is6502!T;
}
// True if the cpu type T represents a 65C02.
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.
template isCumulative(T)
class TestIO
{
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
* its timing characteristics (strict or not bus access, cumulative or
* not cycle reporting).
* The type of a cpu, based on its architecture (6502 or 65C02).
*/
template CPU(string arch, bool strict, bool cumulative)
template CPU(string arch, M = TestIO, C = TestIO)
{
static if (arch == "65c02" || arch == "65C02")
alias Cmos!(strict, cumulative) CPU;
else static if (arch == "6502")
alias NmosUndoc!(strict, cumulative) CPU;
else static assert(0);
alias Cpu!(arch, M, C) CPU;
}
auto makeCpu(T)(CpuInfo info)
auto makeCpu(T)(CpuInfo info = CpuInfo())
if (isCpu!T)
{
auto cpu = new T();
cpu.programCounter = info.PC;
cpu.stackPointer = info.SP;
cpu.flag.fromByte(info.S);
cpu.accumulator = info.A;
cpu.xIndex = info.X;
cpu.yIndex = info.Y;
auto tio = new TestIO();
auto cpu = new T(tio, tio);
cpu.PC = info.PC;
cpu.S = info.SP;
cpu.statusFromByte(info.S);
cpu.A = info.A;
cpu.X = info.X;
cpu.Y = info.Y;
return cpu;
}
@ -75,14 +80,14 @@ if (isCpu!T)
void connectMem(T, S)(T cpu, ref S mem)
if (isCpu!T)
{
static if (isCumulative!T)
static if (cumulative)
void tick(int cycles) {}
else
void tick() {}
cpu.memoryRead = &mem.read;
cpu.memoryWrite = &mem.write;
cpu.tick = &tick;
cpu.memory.dread = &mem.read;
cpu.memory.dwrite = &mem.write;
cpu.clock.dtick = &tick;
}
@ -99,9 +104,9 @@ auto recordCycles(T)(T cpu)
if (isCpu!T)
{
auto cycles = new int;
auto wrappedTick = cpu.tick;
auto wrappedTick = cpu.clock.dtick;
static if (isCumulative!T)
static if (cumulative)
{
void tick(int cyc)
{
@ -117,7 +122,7 @@ if (isCpu!T)
wrappedTick();
}
}
cpu.tick = &tick;
cpu.clock.dtick = &tick;
return constRef(cycles);
}
@ -155,9 +160,9 @@ if (isCpu!T)
auto record = new Bus[actions];
int c;
enforce(cpu.memoryRead !is null && cpu.memoryWrite !is null);
auto wrappedRead = cpu.memoryRead;
auto wrappedWrite = cpu.memoryWrite;
enforce(cpu.memory.dread !is null && cpu.memory.dwrite !is null);
auto wrappedRead = cpu.memory.dread;
auto wrappedWrite = cpu.memory.dwrite;
ubyte read(ushort addr)
{
@ -177,8 +182,8 @@ if (isCpu!T)
wrappedWrite(addr, val);
}
cpu.memoryRead = &read;
cpu.memoryWrite = &write;
cpu.memory.dread = &read;
cpu.memory.dwrite = &write;
return record;
}
@ -210,8 +215,8 @@ enum Action : ushort { NONE, READ, WRITE }
void runUntilBRK(T)(T cpu)
if (isCpu!T)
{
assert(cpu.memoryRead !is null);
auto wrappedRead = cpu.memoryRead;
assert(cpu.memory.dread !is null);
auto wrappedRead = cpu.memory.dread;
ubyte read(ushort addr)
{
@ -219,7 +224,7 @@ if (isCpu!T)
return wrappedRead(addr);
}
cpu.memoryRead = &read;
cpu.memory.dread = &read;
try { cpu.run(true); } catch (StopException e) {}
}
@ -244,32 +249,27 @@ struct CpuInfo
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);
}
static CpuInfo fromCpu(T)(T cpu)
{
CpuInfo info;
info.PC = cpu.programCounter;
info.SP = cpu.stackPointer;
info.A = cpu.accumulator;
info.X = cpu.xIndex;
info.Y = cpu.yIndex;
info.S = cpu.flag.toByte();
info.PC = cpu.PC;
info.SP = cpu.S;
info.A = cpu.A;
info.X = cpu.X;
info.Y = cpu.Y;
info.S = cpu.statusToByte();
return info;
}
}
// Sets the program counter.
void setPC(T)(T cpu, int addr)
if (isCpu!T)
{
cpu.programCounter = cast(ushort)addr;
}
void setPC(T : CpuInfo)(ref T cpu, int addr)
void setPC(T)(ref T cpu, int addr)
if (isCpu!T || is(T == CpuInfo))
{
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);
}
// 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;
}
@ -302,7 +298,7 @@ void setSP(T)(T cpu, int val)
if (isCpu!T)
{
assert(val < 0x0200);
cpu.stackPointer = cast(ubyte)val;
cpu.S = cast(ubyte)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)
if (isCpu!T)
{
return 0x100 | cpu.stackPointer;
return 0x100 | cpu.S;
}
ushort getSP(T : CpuInfo)(ref T cpu)
@ -369,75 +365,48 @@ if (isCpu!T || is(T == CpuInfo))
}
// Sets the A register.
void setA(T)(T cpu, int val)
if (isCpu!T)
{
cpu.accumulator = cast(ubyte)val;
}
void setA(T : CpuInfo)(ref T cpu, int val)
void setA(T)(ref T cpu, int val)
if (isCpu!T || is(T == CpuInfo))
{
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;
}
// Sets the X register.
void setX(T)(T cpu, int val)
if (isCpu!T)
{
cpu.xIndex = cast(ubyte)val;
}
void setX(T : CpuInfo)(ref T cpu, int val)
void setX(T)(ref T cpu, int val)
if (isCpu!T || is(T == CpuInfo))
{
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;
}
// Sets the Y register.
void setY(T)(T cpu, int val)
if (isCpu!T)
{
cpu.yIndex = cast(ubyte)val;
}
void setY(T : CpuInfo)(ref T cpu, int val)
void setY(T)(ref T cpu, int val)
if (isCpu!T || is(T == CpuInfo))
{
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;
}
@ -472,9 +441,9 @@ string flagToString(Flag f)
void setFlag(T)(T cpu, Flag[] flags...)
if (isCpu!T)
{
auto reg = cpu.flag.toByte();
auto reg = cpu.statusToByte();
foreach (flag; flags) reg |= flag;
cpu.flag.fromByte(reg);
cpu.statusFromByte(reg);
}
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...)
if (isCpu!T)
{
auto reg = cpu.flag.toByte();
auto reg = cpu.statusToByte();
foreach (flag; flags) reg &= ~flag;
cpu.flag.fromByte(reg);
cpu.statusFromByte(reg);
}
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)
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)
@ -513,7 +483,7 @@ bool getFlag(T : CpuInfo)(ref T cpu, Flag f)
void setStatus(T)(T cpu, int val)
if (isCpu!T)
{
cpu.flag.fromByte(cast(ubyte)val);
cpu.statusFromByte(cast(ubyte)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)
if (isCpu!T)
{
return cpu.flag.toByte();
return cpu.statusToByte();
return 0;
}
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 /*BEQ*/ 0xF0: clearFlag(cpu, Flag.Z); break;
default:
if (isCMOS!T)
enforce(opcode != 0x80, "BRA can never not branch");
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;
void main()
void main(string[] args)
{
auto opts = CheckOptions(args);
auto report = report_timing_debug();
alias CPU!("65C02", false, false) T1;
for (int op = 0x00; op < 0x100; op++)
alias CPU!("6502") T1;
writeln("Testing bus/timing, 6502");
foreach (op; opts.codes6502)
test_opcode_timing!T1(cast(ubyte)op, report);
alias CPU!("65C02", true, false) T2;
for (int op = 0x00; op < 0x100; op++)
alias CPU!("65C02") T2;
writeln("Testing bus/timing, 65C02");
foreach (op; opts.codes65C02)
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);
}
auto cpu = new T();
connectMem(cpu, mem);
version(Benchmark)
{
auto runner = new BreakRunner(mem);
auto cpu = new T(runner, runner);
runner.keepRunning = &cpu.keepRunning;
setPC(cpu, 0x8000);
cpu.run(true);
}
else
{
auto cpu = makeCpu!T();
setPC(cpu, 0x8000);
connectMem(cpu, mem);
runUntilBRK(cpu);
}
if (mem[0x8003])
{
// TODO: check data block to find out what failed exactly
@ -226,9 +237,43 @@ if (isCpu!T)
version(Benchmark)
{
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()
{
testDecimalMode!(CPU!("65C02", false, false))();
testDecimalMode!(CPU!("65C02", BreakRunner, BreakRunner))();
}
void main()
@ -243,24 +288,10 @@ else
{
void main()
{
writeln("Testing decimal mode, NMOS(Strict.no, Cumulative.no)");
testDecimalMode!(CPU!("6502", false, false))();
writeln("Testing decimal mode, CMOS(Strict.no, Cumulative.no)");
testDecimalMode!(CPU!("65C02", false, false))();
writeln("Testing decimal mode, 6502");
testDecimalMode!(CPU!("6502"))();
writeln("Testing decimal mode, NMOS(Strict.no, Cumulative.yes)");
testDecimalMode!(CPU!("6502", false, true))();
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))();
writeln("Testing decimal mode, 65C02");
testDecimalMode!(CPU!("65C02"))();
}
}

View File

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