366 lines
11 KiB
D
366 lines
11 KiB
D
/+
|
|
+ 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);
|
|
}
|
|
}
|