Integrate new cpu
This commit is contained in:
parent
23b0a96c1c
commit
d100f706fc
|
@ -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}
|
||||
|
|
164
src/d6502/base.d
164
src/d6502/base.d
|
@ -1,164 +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]);
|
||||
}
|
||||
|
||||
struct _Mem
|
||||
{
|
||||
ubyte delegate(ushort addr) read;
|
||||
void delegate(ushort addr, ubyte val) write;
|
||||
}
|
||||
_Mem memory;
|
||||
|
||||
struct _Clock
|
||||
{
|
||||
static if (cumulative)
|
||||
void delegate(int cycles) tick;
|
||||
else
|
||||
void delegate() tick;
|
||||
}
|
||||
_Clock clock;
|
||||
|
||||
debug(disassemble)
|
||||
{
|
||||
string delegate(ushort addr) memoryName;
|
||||
}
|
||||
|
||||
abstract void run(bool continuous);
|
||||
abstract void stop();
|
||||
abstract void resetLow();
|
||||
abstract void nmiLow(bool signalLow);
|
||||
abstract void irqLow(bool signalLow);
|
||||
}
|
266
src/d6502/cmos.d
266
src/d6502/cmos.d
|
@ -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;
|
||||
}
|
||||
}
|
751
src/d6502/cpu.d
751
src/d6502/cpu.d
|
@ -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 ((memory.read !is null) && (memory.write !is null));
|
||||
assert (clock.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) clock.tick(totalCycles);
|
||||
}
|
||||
|
||||
void doReset()
|
||||
{
|
||||
static if (cumulative)
|
||||
{
|
||||
totalCycles += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
clock.tick(); clock.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) clock.tick(totalCycles);
|
||||
}
|
||||
|
||||
final ubyte read(ushort addr)
|
||||
{
|
||||
static if (cumulative) ++totalCycles;
|
||||
else clock.tick();
|
||||
return memory.read(addr);
|
||||
}
|
||||
|
||||
final void write(ushort addr, ubyte val)
|
||||
{
|
||||
static if (cumulative) ++totalCycles;
|
||||
else clock.tick();
|
||||
memory.write(addr, val);
|
||||
}
|
||||
|
||||
final void peek(ushort addr)
|
||||
{
|
||||
static if (cumulative) ++totalCycles;
|
||||
else clock.tick();
|
||||
static if (strict) memory.read(addr);
|
||||
}
|
||||
|
||||
final void poke(ushort addr, ubyte val)
|
||||
{
|
||||
static if (cumulative) ++totalCycles;
|
||||
else clock.tick();
|
||||
static if (strict) memory.write(addr, val);
|
||||
}
|
||||
|
||||
final ubyte readFinal(ushort addr)
|
||||
{
|
||||
static if (cumulative) clock.tick(++totalCycles);
|
||||
else
|
||||
{
|
||||
clock.tick();
|
||||
}
|
||||
return memory.read(addr);
|
||||
}
|
||||
|
||||
final void writeFinal(ushort addr, ubyte val)
|
||||
{
|
||||
static if (cumulative) clock.tick(++totalCycles);
|
||||
else
|
||||
{
|
||||
clock.tick();
|
||||
}
|
||||
memory.write(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 ~= "clock.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 ~= "clock.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 ~= "clock.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) clock.tick(totalCycles);
|
||||
programCounter = finalAddress;
|
||||
}
|
||||
|
||||
/* RTI */
|
||||
final override void opcode40()
|
||||
{
|
||||
peek(programCounter);
|
||||
flag.fromByte(pull());
|
||||
programCounter = readStack() | (readStack() << 8);
|
||||
static if (cumulative) clock.tick(totalCycles);
|
||||
}
|
||||
|
||||
/* JMP $$$$ */
|
||||
final override void opcode4C()
|
||||
{
|
||||
programCounter = readWordOperand();
|
||||
static if (cumulative) clock.tick(totalCycles);
|
||||
}
|
||||
|
||||
/* RTS */
|
||||
final override void opcode60()
|
||||
{
|
||||
peek(programCounter);
|
||||
programCounter = pullWord();
|
||||
peek(programCounter);
|
||||
static if (cumulative) clock.tick(totalCycles);
|
||||
++programCounter;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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" ~
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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,7 +106,10 @@ class System
|
|||
initIO(null); // XXX where is vidRom passed in?
|
||||
decoder.nullRead = &video_.scanner.floatingBus;
|
||||
|
||||
peripherals = newPeripherals();
|
||||
static if (chip == "6502")
|
||||
peripherals = new Peripherals_II();
|
||||
else
|
||||
peripherals = new Peripherals_IIe();
|
||||
peripherals.install(decoder, memory_.mainRom);
|
||||
ioMem.initialize(decoder, switches, timer, peripherals);
|
||||
|
||||
|
@ -99,15 +117,22 @@ class System
|
|||
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.clock.tick = &timer.tick;
|
||||
timer.onPrimaryStop(&primaryStop);
|
||||
cpu.memory.read = &decoder.read;
|
||||
cpu.memory.write = &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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue