1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-06-01 21:41:31 +00:00
ksim65/src/main/kotlin/razorvine/ksim65/Cpu6502.kt

1536 lines
52 KiB
Kotlin

package razorvine.ksim65
import razorvine.ksim65.components.Address
import razorvine.ksim65.components.BusComponent
import razorvine.ksim65.components.UByte
/**
* 6502 cpu simulation (the NMOS version) including the 'illegal' opcodes.
*/
open class Cpu6502 : BusComponent() {
open val name = "6502"
var tracing: ((state: State) -> Unit)? = null
var totalCycles = 0L
protected set
private var resetTime = System.nanoTime()
var breakpointForBRK: BreakpointHandler? = null
class InstructionError(msg: String) : RuntimeException(msg)
companion object {
const val NMI_vector = 0xfffa
const val RESET_vector = 0xfffc
const val IRQ_vector = 0xfffe
}
class StatusRegister(var C: Boolean = false, var Z: Boolean = false, var I: Boolean = false, var D: Boolean = false,
var B: Boolean = false, var V: Boolean = false, var N: Boolean = false) {
fun asInt(): Int {
return (0b00100000
or (if (N) 0b10000000 else 0)
or (if (V) 0b01000000 else 0)
or (if (B) 0b00010000 else 0)
or (if (D) 0b00001000 else 0)
or (if (I) 0b00000100 else 0)
or (if (Z) 0b00000010 else 0)
or (if (C) 0b00000001 else 0))
}
fun fromInt(byte: Int) {
N = (byte and 0b10000000) != 0
V = (byte and 0b01000000) != 0
B = (byte and 0b00010000) != 0
D = (byte and 0b00001000) != 0
I = (byte and 0b00000100) != 0
Z = (byte and 0b00000010) != 0
C = (byte and 0b00000001) != 0
}
override fun toString(): String {
return asInt().toString(2).padStart(8, '0')
}
override fun hashCode(): Int = asInt()
override fun equals(other: Any?): Boolean {
if (other !is StatusRegister) return false
return asInt() == other.asInt()
}
}
/**
* Breakpoint handlers have to return this to specify to the CPU simulator
* what should happen after the breakpoint code has executed.
*
* Setting changePC will continue execution from a different memory location.
* Setting changeOpcode will execute a different opcode in place of the one
* that's actually on the location of the breakpoint.
* (it's a bit limited; you can only use one-byte instructions for this)
* Setting causeBRK will simulate a software interrupt via BRK,
* without having to actually have a BRK in the breakpoint's memory location
* (this is the same as changeOpcode=0x00)
*/
class BreakpointResultAction(val changePC: Address? = null, val changeOpcode: Int? = null, val causeBRK: Boolean = false)
class State(val A: UByte, val X: UByte, val Y: UByte, val SP: Address, val P: StatusRegister, val PC: Address, val cycles: Long) {
override fun toString(): String {
return "cycle:$cycles - pc=${hexW(PC)} "+"A=${hexB(A)} "+"X=${hexB(X)} "+"Y=${hexB(Y)} "+
"SP=${hexB(SP)} "+" n="+(if (P.N) "1" else "0")+" v="+(if (P.V) "1" else "0")+
" b="+(if (P.B) "1" else "0")+" d="+(if (P.D) "1" else "0")+" i="+(if (P.I) "1" else "0")+
" z="+(if (P.Z) "1" else "0")+" c="+(if (P.C) "1" else "0")
}
}
enum class AddrMode {
Imp, Acc, Imm, Zp, ZpX, ZpY, Rel, Abs, AbsX, AbsY, Ind, IzX, IzY,
// modes used only by the 65C02:
Zpr, Izp, IaX
}
class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
var regA: Int = 0
var regX: Int = 0
var regY: Int = 0
var regSP: Int = 0
var regPC: Address = 0
val regP = StatusRegister()
var irqAsserted = false
var nmiAsserted = false
var currentOpcode: Int = 0
protected set
var currentOpcodeAddress: Address = 0 // the PC can be changed already depending on the addressing mode
protected set
var instrCycles: Int = 0
protected set
val isLooping: Boolean get() {
// jump loop detection
return (previousOpcodeAddress == currentOpcodeAddress) && !(nmiAsserted || irqAsserted)
}
private var previousOpcodeAddress: Address = 0xffff
lateinit var currentInstruction: Instruction
val averageSpeedKhzSinceReset: Double
get() = totalCycles.toDouble()/(System.nanoTime()-resetTime)*1_000_000
@Synchronized
fun snapshot(): State {
val status = StatusRegister().also { it.fromInt(regP.asInt()) }
return State(regA.toShort(), regX.toShort(), regY.toShort(), regSP, status, regPC, totalCycles)
}
// data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied)
protected var fetchedData: Int = 0
// all other addressing modes yield a fetched memory address
protected var fetchedAddress: Address = 0
private val breakpoints = mutableMapOf<Address, BreakpointHandler>()
fun addBreakpoint(address: Address, handler: BreakpointHandler) { breakpoints[address] = handler }
fun removeBreakpoint(address: Address) = breakpoints.remove(address)
/**
* Reset the cpu
*/
override fun reset() {
// TODO don't perform all of the reset logic immediately, handle the reset 'pin' in the regular clock() instead (much like a NMI)
regP.I = true
regP.C = false
regP.Z = false
regP.D = false
regP.B = false
regP.V = false
regP.N = false
regSP = 0xfd
regPC = readWord(RESET_vector)
regA = 0
regX = 0
regY = 0
instrCycles = 8 // a reset takes 8 clock cycles
currentOpcode = 0
currentInstruction = instructions[0]
totalCycles = 0
resetTime = System.nanoTime()
}
/**
* Process once clock cycle in the cpu.
* Use this if goal is cycle-perfect emulation.
*/
override fun clock() {
if (instrCycles == 0) {
tracing?.invoke(snapshot())
if(nmiAsserted || (irqAsserted && !regP.I)) {
handleInterrupt()
return
}
// no interrupt, fetch next instruction from memory
previousOpcodeAddress = currentOpcodeAddress
currentOpcodeAddress = regPC
currentOpcode = read(regPC)
currentInstruction = instructions[currentOpcode]
breakpoints[regPC]?.let {
if (breakpoint(it)) return
}
if (currentOpcode == 0x00) breakpointForBRK?.let {
if (breakpoint(it)) return
}
regPC++
instrCycles = currentInstruction.cycles
val extraCycleFromAddr = applyAddressingMode(currentInstruction.mode)
val extraCycleFromInstr = dispatchOpcode(currentOpcode)
if(extraCycleFromAddr and extraCycleFromInstr)
instrCycles++
}
instrCycles--
totalCycles++
}
private fun breakpoint(handler: BreakpointHandler): Boolean {
val oldPC = regPC
val result = handler(this, regPC)
when {
result.changePC != null -> regPC = result.changePC
result.changeOpcode != null -> {
currentOpcode = result.changeOpcode
currentInstruction = instructions[currentOpcode]
}
result.causeBRK -> {
currentOpcode = 0x00
currentInstruction = instructions[0x00]
}
}
return if (regPC != oldPC) {
clock()
true
} else false
}
/**
* Execute one single complete instruction.
* Use this when the goal is emulation performance and not a cycle perfect system.
*/
open fun step() {
totalCycles += instrCycles
instrCycles = 0
clock()
totalCycles += instrCycles
instrCycles = 0
}
protected fun getFetched() =
if (currentInstruction.mode == AddrMode.Imm || currentInstruction.mode == AddrMode.Acc || currentInstruction.mode == AddrMode.Imp) fetchedData
else read(fetchedAddress)
protected fun readPc(): Int = bus.read(regPC++).toInt()
protected fun pushStackAddr(address: Address) {
val lo = address and 0xff
val hi = (address ushr 8)
pushStack(hi)
pushStack(lo)
}
protected fun pushStack(status: StatusRegister) {
pushStack(status.asInt())
}
protected fun pushStack(data: Int) {
write(regSP or 0x0100, data)
regSP = (regSP-1) and 0xff
}
protected fun popStack(): Int {
regSP = (regSP+1) and 0xff
return read(regSP or 0x0100)
}
protected fun popStackAddr(): Address {
val lo = popStack()
val hi = popStack()
return lo or (hi shl 8)
}
protected fun read(address: Address): Int = bus.read(address).toInt()
protected fun readWord(address: Address): Int = bus.read(address).toInt() or (bus.read(address+1).toInt() shl 8)
protected fun write(address: Address, data: Int) = bus.write(address, data.toShort())
// opcodes table from http://www.oxyron.de/html/opcodes02.html
open val instructions: Array<Instruction> = listOf(
/* 00 */ Instruction("brk", AddrMode.Imp, 7),
/* 01 */ Instruction("ora", AddrMode.IzX, 6),
/* 02 */ Instruction("???", AddrMode.Imp, 2),
/* 03 */ Instruction("slo", AddrMode.IzX, 8),
/* 04 */ Instruction("nop", AddrMode.Zp, 3),
/* 05 */ Instruction("ora", AddrMode.Zp, 3),
/* 06 */ Instruction("asl", AddrMode.Zp, 5),
/* 07 */ Instruction("slo", AddrMode.Zp, 5),
/* 08 */ Instruction("php", AddrMode.Imp, 3),
/* 09 */ Instruction("ora", AddrMode.Imm, 2),
/* 0a */ Instruction("asl", AddrMode.Acc, 2),
/* 0b */ Instruction("anc", AddrMode.Imm, 2),
/* 0c */ Instruction("nop", AddrMode.Abs, 4),
/* 0d */ Instruction("ora", AddrMode.Abs, 4),
/* 0e */ Instruction("asl", AddrMode.Abs, 6),
/* 0f */ Instruction("slo", AddrMode.Abs, 6),
/* 10 */ Instruction("bpl", AddrMode.Rel, 2),
/* 11 */ Instruction("ora", AddrMode.IzY, 5),
/* 12 */ Instruction("???", AddrMode.Imp, 2),
/* 13 */ Instruction("slo", AddrMode.IzY, 8),
/* 14 */ Instruction("nop", AddrMode.ZpX, 4),
/* 15 */ Instruction("ora", AddrMode.ZpX, 4),
/* 16 */ Instruction("asl", AddrMode.ZpX, 6),
/* 17 */ Instruction("slo", AddrMode.ZpX, 6),
/* 18 */ Instruction("clc", AddrMode.Imp, 2),
/* 19 */ Instruction("ora", AddrMode.AbsY, 4),
/* 1a */ Instruction("nop", AddrMode.Imp, 2),
/* 1b */ Instruction("slo", AddrMode.AbsY, 7),
/* 1c */ Instruction("nop", AddrMode.AbsX, 4),
/* 1d */ Instruction("ora", AddrMode.AbsX, 4),
/* 1e */ Instruction("asl", AddrMode.AbsX, 7),
/* 1f */ Instruction("slo", AddrMode.AbsX, 7),
/* 20 */ Instruction("jsr", AddrMode.Abs, 6),
/* 21 */ Instruction("and", AddrMode.IzX, 6),
/* 22 */ Instruction("???", AddrMode.Imp, 2),
/* 23 */ Instruction("rla", AddrMode.IzX, 8),
/* 24 */ Instruction("bit", AddrMode.Zp, 3),
/* 25 */ Instruction("and", AddrMode.Zp, 3),
/* 26 */ Instruction("rol", AddrMode.Zp, 5),
/* 27 */ Instruction("rla", AddrMode.Zp, 5),
/* 28 */ Instruction("plp", AddrMode.Imp, 4),
/* 29 */ Instruction("and", AddrMode.Imm, 2),
/* 2a */ Instruction("rol", AddrMode.Acc, 2),
/* 2b */ Instruction("anc", AddrMode.Imm, 2),
/* 2c */ Instruction("bit", AddrMode.Abs, 4),
/* 2d */ Instruction("and", AddrMode.Abs, 4),
/* 2e */ Instruction("rol", AddrMode.Abs, 6),
/* 2f */ Instruction("rla", AddrMode.Abs, 6),
/* 30 */ Instruction("bmi", AddrMode.Rel, 2),
/* 31 */ Instruction("and", AddrMode.IzY, 5),
/* 32 */ Instruction("???", AddrMode.Imp, 2),
/* 33 */ Instruction("rla", AddrMode.IzY, 8),
/* 34 */ Instruction("nop", AddrMode.ZpX, 4),
/* 35 */ Instruction("and", AddrMode.ZpX, 4),
/* 36 */ Instruction("rol", AddrMode.ZpX, 6),
/* 37 */ Instruction("rla", AddrMode.ZpX, 6),
/* 38 */ Instruction("sec", AddrMode.Imp, 2),
/* 39 */ Instruction("and", AddrMode.AbsY, 4),
/* 3a */ Instruction("nop", AddrMode.Imp, 2),
/* 3b */ Instruction("rla", AddrMode.AbsY, 7),
/* 3c */ Instruction("nop", AddrMode.AbsX, 4),
/* 3d */ Instruction("and", AddrMode.AbsX, 4),
/* 3e */ Instruction("rol", AddrMode.AbsX, 7),
/* 3f */ Instruction("rla", AddrMode.AbsX, 7),
/* 40 */ Instruction("rti", AddrMode.Imp, 6),
/* 41 */ Instruction("eor", AddrMode.IzX, 6),
/* 42 */ Instruction("???", AddrMode.Imp, 2),
/* 43 */ Instruction("sre", AddrMode.IzX, 8),
/* 44 */ Instruction("nop", AddrMode.Zp, 3),
/* 45 */ Instruction("eor", AddrMode.Zp, 3),
/* 46 */ Instruction("lsr", AddrMode.Zp, 5),
/* 47 */ Instruction("sre", AddrMode.Zp, 5),
/* 48 */ Instruction("pha", AddrMode.Imp, 3),
/* 49 */ Instruction("eor", AddrMode.Imm, 2),
/* 4a */ Instruction("lsr", AddrMode.Acc, 2),
/* 4b */ Instruction("alr", AddrMode.Imm, 2),
/* 4c */ Instruction("jmp", AddrMode.Abs, 3),
/* 4d */ Instruction("eor", AddrMode.Abs, 4),
/* 4e */ Instruction("lsr", AddrMode.Abs, 6),
/* 4f */ Instruction("sre", AddrMode.Abs, 6),
/* 50 */ Instruction("bvc", AddrMode.Rel, 2),
/* 51 */ Instruction("eor", AddrMode.IzY, 5),
/* 52 */ Instruction("???", AddrMode.Imp, 2),
/* 53 */ Instruction("sre", AddrMode.IzY, 8),
/* 54 */ Instruction("nop", AddrMode.ZpX, 4),
/* 55 */ Instruction("eor", AddrMode.ZpX, 4),
/* 56 */ Instruction("lsr", AddrMode.ZpX, 6),
/* 57 */ Instruction("sre", AddrMode.ZpX, 6),
/* 58 */ Instruction("cli", AddrMode.Imp, 2),
/* 59 */ Instruction("eor", AddrMode.AbsY, 4),
/* 5a */ Instruction("nop", AddrMode.Imp, 2),
/* 5b */ Instruction("sre", AddrMode.AbsY, 7),
/* 5c */ Instruction("nop", AddrMode.AbsX, 4),
/* 5d */ Instruction("eor", AddrMode.AbsX, 4),
/* 5e */ Instruction("lsr", AddrMode.AbsX, 7),
/* 5f */ Instruction("sre", AddrMode.AbsX, 7),
/* 60 */ Instruction("rts", AddrMode.Imp, 6),
/* 61 */ Instruction("adc", AddrMode.IzX, 6),
/* 62 */ Instruction("???", AddrMode.Imp, 2),
/* 63 */ Instruction("rra", AddrMode.IzX, 8),
/* 64 */ Instruction("nop", AddrMode.Zp, 3),
/* 65 */ Instruction("adc", AddrMode.Zp, 3),
/* 66 */ Instruction("ror", AddrMode.Zp, 5),
/* 67 */ Instruction("rra", AddrMode.Zp, 5),
/* 68 */ Instruction("pla", AddrMode.Imp, 4),
/* 69 */ Instruction("adc", AddrMode.Imm, 2),
/* 6a */ Instruction("ror", AddrMode.Acc, 2),
/* 6b */ Instruction("arr", AddrMode.Imm, 2),
/* 6c */ Instruction("jmp", AddrMode.Ind, 5),
/* 6d */ Instruction("adc", AddrMode.Abs, 4),
/* 6e */ Instruction("ror", AddrMode.Abs, 6),
/* 6f */ Instruction("rra", AddrMode.Abs, 6),
/* 70 */ Instruction("bvs", AddrMode.Rel, 2),
/* 71 */ Instruction("adc", AddrMode.IzY, 5),
/* 72 */ Instruction("???", AddrMode.Imp, 2),
/* 73 */ Instruction("rra", AddrMode.IzY, 8),
/* 74 */ Instruction("nop", AddrMode.ZpX, 4),
/* 75 */ Instruction("adc", AddrMode.ZpX, 4),
/* 76 */ Instruction("ror", AddrMode.ZpX, 6),
/* 77 */ Instruction("rra", AddrMode.ZpX, 6),
/* 78 */ Instruction("sei", AddrMode.Imp, 2),
/* 79 */ Instruction("adc", AddrMode.AbsY, 4),
/* 7a */ Instruction("nop", AddrMode.Imp, 2),
/* 7b */ Instruction("rra", AddrMode.AbsY, 7),
/* 7c */ Instruction("nop", AddrMode.AbsX, 4),
/* 7d */ Instruction("adc", AddrMode.AbsX, 4),
/* 7e */ Instruction("ror", AddrMode.AbsX, 7),
/* 7f */ Instruction("rra", AddrMode.AbsX, 7),
/* 80 */ Instruction("nop", AddrMode.Imm, 2),
/* 81 */ Instruction("sta", AddrMode.IzX, 6),
/* 82 */ Instruction("nop", AddrMode.Imm, 2),
/* 83 */ Instruction("sax", AddrMode.IzX, 6),
/* 84 */ Instruction("sty", AddrMode.Zp, 3),
/* 85 */ Instruction("sta", AddrMode.Zp, 3),
/* 86 */ Instruction("stx", AddrMode.Zp, 3),
/* 87 */ Instruction("sax", AddrMode.Zp, 3),
/* 88 */ Instruction("dey", AddrMode.Imp, 2),
/* 89 */ Instruction("nop", AddrMode.Imm, 2),
/* 8a */ Instruction("txa", AddrMode.Imp, 2),
/* 8b */ Instruction("xaa", AddrMode.Imm, 2),
/* 8c */ Instruction("sty", AddrMode.Abs, 4),
/* 8d */ Instruction("sta", AddrMode.Abs, 4),
/* 8e */ Instruction("stx", AddrMode.Abs, 4),
/* 8f */ Instruction("sax", AddrMode.Abs, 4),
/* 90 */ Instruction("bcc", AddrMode.Rel, 2),
/* 91 */ Instruction("sta", AddrMode.IzY, 6),
/* 92 */ Instruction("???", AddrMode.Imp, 2),
/* 93 */ Instruction("ahx", AddrMode.IzY, 6),
/* 94 */ Instruction("sty", AddrMode.ZpX, 4),
/* 95 */ Instruction("sta", AddrMode.ZpX, 4),
/* 96 */ Instruction("stx", AddrMode.ZpY, 4),
/* 97 */ Instruction("sax", AddrMode.ZpY, 4),
/* 98 */ Instruction("tya", AddrMode.Imp, 2),
/* 99 */ Instruction("sta", AddrMode.AbsY, 5),
/* 9a */ Instruction("txs", AddrMode.Imp, 2),
/* 9b */ Instruction("tas", AddrMode.AbsY, 5),
/* 9c */ Instruction("shy", AddrMode.AbsX, 5),
/* 9d */ Instruction("sta", AddrMode.AbsX, 5),
/* 9e */ Instruction("shx", AddrMode.AbsY, 5),
/* 9f */ Instruction("ahx", AddrMode.AbsY, 5),
/* a0 */ Instruction("ldy", AddrMode.Imm, 2),
/* a1 */ Instruction("lda", AddrMode.IzX, 6),
/* a2 */ Instruction("ldx", AddrMode.Imm, 2),
/* a3 */ Instruction("lax", AddrMode.IzX, 6),
/* a4 */ Instruction("ldy", AddrMode.Zp, 3),
/* a5 */ Instruction("lda", AddrMode.Zp, 3),
/* a6 */ Instruction("ldx", AddrMode.Zp, 3),
/* a7 */ Instruction("lax", AddrMode.Zp, 3),
/* a8 */ Instruction("tay", AddrMode.Imp, 2),
/* a9 */ Instruction("lda", AddrMode.Imm, 2),
/* aa */ Instruction("tax", AddrMode.Imp, 2),
/* ab */ Instruction("lax", AddrMode.Imm, 2),
/* ac */ Instruction("ldy", AddrMode.Abs, 4),
/* ad */ Instruction("lda", AddrMode.Abs, 4),
/* ae */ Instruction("ldx", AddrMode.Abs, 4),
/* af */ Instruction("lax", AddrMode.Abs, 4),
/* b0 */ Instruction("bcs", AddrMode.Rel, 2),
/* b1 */ Instruction("lda", AddrMode.IzY, 5),
/* b2 */ Instruction("???", AddrMode.Imp, 2),
/* b3 */ Instruction("lax", AddrMode.IzY, 5),
/* b4 */ Instruction("ldy", AddrMode.ZpX, 4),
/* b5 */ Instruction("lda", AddrMode.ZpX, 4),
/* b6 */ Instruction("ldx", AddrMode.ZpY, 4),
/* b7 */ Instruction("lax", AddrMode.ZpY, 4),
/* b8 */ Instruction("clv", AddrMode.Imp, 2),
/* b9 */ Instruction("lda", AddrMode.AbsY, 4),
/* ba */ Instruction("tsx", AddrMode.Imp, 2),
/* bb */ Instruction("las", AddrMode.AbsY, 4),
/* bc */ Instruction("ldy", AddrMode.AbsX, 4),
/* bd */ Instruction("lda", AddrMode.AbsX, 4),
/* be */ Instruction("ldx", AddrMode.AbsY, 4),
/* bf */ Instruction("lax", AddrMode.AbsY, 4),
/* c0 */ Instruction("cpy", AddrMode.Imm, 2),
/* c1 */ Instruction("cmp", AddrMode.IzX, 6),
/* c2 */ Instruction("nop", AddrMode.Imm, 2),
/* c3 */ Instruction("dcp", AddrMode.IzX, 8),
/* c4 */ Instruction("cpy", AddrMode.Zp, 3),
/* c5 */ Instruction("cmp", AddrMode.Zp, 3),
/* c6 */ Instruction("dec", AddrMode.Zp, 5),
/* c7 */ Instruction("dcp", AddrMode.Zp, 5),
/* c8 */ Instruction("iny", AddrMode.Imp, 2),
/* c9 */ Instruction("cmp", AddrMode.Imm, 2),
/* ca */ Instruction("dex", AddrMode.Imp, 2),
/* cb */ Instruction("axs", AddrMode.Imm, 2),
/* cc */ Instruction("cpy", AddrMode.Abs, 4),
/* cd */ Instruction("cmp", AddrMode.Abs, 4),
/* ce */ Instruction("dec", AddrMode.Abs, 6),
/* cf */ Instruction("dcp", AddrMode.Abs, 6),
/* d0 */ Instruction("bne", AddrMode.Rel, 2),
/* d1 */ Instruction("cmp", AddrMode.IzY, 5),
/* d2 */ Instruction("???", AddrMode.Imp, 2),
/* d3 */ Instruction("dcp", AddrMode.IzY, 8),
/* d4 */ Instruction("nop", AddrMode.ZpX, 4),
/* d5 */ Instruction("cmp", AddrMode.ZpX, 4),
/* d6 */ Instruction("dec", AddrMode.ZpX, 6),
/* d7 */ Instruction("dcp", AddrMode.ZpX, 6),
/* d8 */ Instruction("cld", AddrMode.Imp, 2),
/* d9 */ Instruction("cmp", AddrMode.AbsY, 4),
/* da */ Instruction("nop", AddrMode.Imp, 2),
/* db */ Instruction("dcp", AddrMode.AbsY, 7),
/* dc */ Instruction("nop", AddrMode.AbsX, 4),
/* dd */ Instruction("cmp", AddrMode.AbsX, 4),
/* de */ Instruction("dec", AddrMode.AbsX, 7),
/* df */ Instruction("dcp", AddrMode.AbsX, 7),
/* e0 */ Instruction("cpx", AddrMode.Imm, 2),
/* e1 */ Instruction("sbc", AddrMode.IzX, 6),
/* e2 */ Instruction("nop", AddrMode.Imm, 2),
/* e3 */ Instruction("isc", AddrMode.IzX, 8),
/* e4 */ Instruction("cpx", AddrMode.Zp, 3),
/* e5 */ Instruction("sbc", AddrMode.Zp, 3),
/* e6 */ Instruction("inc", AddrMode.Zp, 5),
/* e7 */ Instruction("isc", AddrMode.Zp, 5),
/* e8 */ Instruction("inx", AddrMode.Imp, 2),
/* e9 */ Instruction("sbc", AddrMode.Imm, 2),
/* ea */ Instruction("nop", AddrMode.Imp, 2),
/* eb */ Instruction("sbc", AddrMode.Imm, 2),
/* ec */ Instruction("cpx", AddrMode.Abs, 4),
/* ed */ Instruction("sbc", AddrMode.Abs, 4),
/* ee */ Instruction("inc", AddrMode.Abs, 6),
/* ef */ Instruction("isc", AddrMode.Abs, 6),
/* f0 */ Instruction("beq", AddrMode.Rel, 2),
/* f1 */ Instruction("sbc", AddrMode.IzY, 5),
/* f2 */ Instruction("???", AddrMode.Imp, 2),
/* f3 */ Instruction("isc", AddrMode.IzY, 8),
/* f4 */ Instruction("nop", AddrMode.ZpX, 4),
/* f5 */ Instruction("sbc", AddrMode.ZpX, 4),
/* f6 */ Instruction("inc", AddrMode.ZpX, 6),
/* f7 */ Instruction("isc", AddrMode.ZpX, 6),
/* f8 */ Instruction("sed", AddrMode.Imp, 2),
/* f9 */ Instruction("sbc", AddrMode.AbsY, 4),
/* fa */ Instruction("nop", AddrMode.Imp, 2),
/* fb */ Instruction("isc", AddrMode.AbsY, 7),
/* fc */ Instruction("nop", AddrMode.AbsX, 4),
/* fd */ Instruction("sbc", AddrMode.AbsX, 4),
/* fe */ Instruction("inc", AddrMode.AbsX, 7),
/* ff */ Instruction("isc", AddrMode.AbsX, 7)).toTypedArray()
protected open fun applyAddressingMode(addrMode: AddrMode): Boolean {
// an addressing mode can cause an extra clock cycle on certain instructions
return when (addrMode) {
AddrMode.Imp, AddrMode.Acc -> {
fetchedData = regA
false
}
AddrMode.Imm -> {
fetchedData = readPc()
false
}
AddrMode.Zp -> {
fetchedAddress = readPc()
false
}
AddrMode.ZpX -> {
// note: zeropage index will not leave Zp when page boundary is crossed
fetchedAddress = (readPc()+regX) and 0xff
false
}
AddrMode.ZpY -> {
// note: zeropage index will not leave Zp when page boundary is crossed
fetchedAddress = (readPc()+regY) and 0xff
false
}
AddrMode.Rel -> {
val relative = readPc()
fetchedAddress = if (relative >= 0x80) {
regPC-(256-relative) and 0xffff
} else {
regPC+relative and 0xffff
}
false
}
AddrMode.Abs -> {
val lo = readPc()
val hi = readPc()
fetchedAddress = lo or (hi shl 8)
false
}
AddrMode.AbsX -> {
val lo = readPc()
val hi = readPc()
fetchedAddress = regX+(lo or (hi shl 8)) and 0xffff
// if this address is a different page, extra clock cycle:
(fetchedAddress and 0xff00) != hi shl 8
}
AddrMode.AbsY -> {
val lo = readPc()
val hi = readPc()
fetchedAddress = regY+(lo or (hi shl 8)) and 0xffff
// if this address is a different page, extra clock cycle:
(fetchedAddress and 0xff00) != hi shl 8
}
AddrMode.Ind -> {
val extraCycle: Boolean
var lo = readPc()
var hi = readPc()
fetchedAddress = lo or (hi shl 8)
if (lo == 0xff) {
// emulate 6502 bug (fixed in 65C02):
// not able to fetch an address which crosses the page boundary.
lo = read(fetchedAddress)
hi = read(fetchedAddress and 0xff00)
extraCycle = true
} else {
// normal behavior
lo = read(fetchedAddress)
hi = read(fetchedAddress+1)
extraCycle = false
}
fetchedAddress = lo or (hi shl 8)
extraCycle
}
AddrMode.IzX -> {
// note: not able to fetch an address which crosses the (zero)page boundary
fetchedAddress = readPc()
val lo = read((fetchedAddress+regX) and 0xff)
val hi = read((fetchedAddress+regX+1) and 0xff)
fetchedAddress = lo or (hi shl 8)
// if this address is a different page, extra clock cycle:
(fetchedAddress and 0xff00) != hi shl 8
}
AddrMode.IzY -> {
// note: not able to fetch an address which crosses the (zero)page boundary
val fetchedAddress1 = readPc()
val lo = read(fetchedAddress1)
val hi = read((fetchedAddress1+1) and 0xff)
fetchedAddress = regY+(lo or (hi shl 8)) and 0xffff
// if this address is a different page, extra clock cycle:
(fetchedAddress and 0xff00) != hi shl 8
}
AddrMode.Zpr, AddrMode.Izp, AddrMode.IaX -> {
// addressing mode used by the 65C02 only
throw InstructionError("65c02 addressing mode not implemented on 6502")
}
}
}
protected open fun dispatchOpcode(opcode: Int): Boolean {
return when (opcode) {
0x00 -> iBrk()
0x01 -> iOra()
0x02 -> iInvalid()
0x03 -> iSlo()
0x04 -> iNop()
0x05 -> iOra()
0x06 -> iAsl()
0x07 -> iSlo()
0x08 -> iPhp()
0x09 -> iOra()
0x0a -> iAsl()
0x0b -> iAnc()
0x0c -> iNop()
0x0d -> iOra()
0x0e -> iAsl()
0x0f -> iSlo()
0x10 -> iBpl()
0x11 -> iOra()
0x12 -> iInvalid()
0x13 -> iSlo()
0x14 -> iNop()
0x15 -> iOra()
0x16 -> iAsl()
0x17 -> iSlo()
0x18 -> iClc()
0x19 -> iOra()
0x1a -> iNop()
0x1b -> iSlo()
0x1c -> iNop()
0x1d -> iOra()
0x1e -> iAsl()
0x1f -> iSlo()
0x20 -> iJsr()
0x21 -> iAnd()
0x22 -> iInvalid()
0x23 -> iRla()
0x24 -> iBit()
0x25 -> iAnd()
0x26 -> iRol()
0x27 -> iRla()
0x28 -> iPlp()
0x29 -> iAnd()
0x2a -> iRol()
0x2b -> iAnc()
0x2c -> iBit()
0x2d -> iAnd()
0x2e -> iRol()
0x2f -> iRla()
0x30 -> iBmi()
0x31 -> iAnd()
0x32 -> iInvalid()
0x33 -> iRla()
0x34 -> iNop()
0x35 -> iAnd()
0x36 -> iRol()
0x37 -> iRla()
0x38 -> iSec()
0x39 -> iAnd()
0x3a -> iNop()
0x3b -> iRla()
0x3c -> iNop()
0x3d -> iAnd()
0x3e -> iRol()
0x3f -> iRla()
0x40 -> iRti()
0x41 -> iEor()
0x42 -> iInvalid()
0x43 -> iSre()
0x44 -> iNop()
0x45 -> iEor()
0x46 -> iLsr()
0x47 -> iSre()
0x48 -> iPha()
0x49 -> iEor()
0x4a -> iLsr()
0x4b -> iAlr()
0x4c -> iJmp()
0x4d -> iEor()
0x4e -> iLsr()
0x4f -> iSre()
0x50 -> iBvc()
0x51 -> iEor()
0x52 -> iInvalid()
0x53 -> iSre()
0x54 -> iNop()
0x55 -> iEor()
0x56 -> iLsr()
0x57 -> iSre()
0x58 -> iCli()
0x59 -> iEor()
0x5a -> iNop()
0x5b -> iSre()
0x5c -> iNop()
0x5d -> iEor()
0x5e -> iLsr()
0x5f -> iSre()
0x60 -> iRts()
0x61 -> iAdc()
0x62 -> iInvalid()
0x63 -> iRra()
0x64 -> iNop()
0x65 -> iAdc()
0x66 -> iRor()
0x67 -> iRra()
0x68 -> iPla()
0x69 -> iAdc()
0x6a -> iRor()
0x6b -> iArr()
0x6c -> iJmp()
0x6d -> iAdc()
0x6e -> iRor()
0x6f -> iRra()
0x70 -> iBvs()
0x71 -> iAdc()
0x72 -> iInvalid()
0x73 -> iRra()
0x74 -> iNop()
0x75 -> iAdc()
0x76 -> iRor()
0x77 -> iRra()
0x78 -> iSei()
0x79 -> iAdc()
0x7a -> iNop()
0x7b -> iRra()
0x7c -> iNop()
0x7d -> iAdc()
0x7e -> iRor()
0x7f -> iRra()
0x80 -> iNop()
0x81 -> iSta()
0x82 -> iNop()
0x83 -> iSax()
0x84 -> iSty()
0x85 -> iSta()
0x86 -> iStx()
0x87 -> iSax()
0x88 -> iDey()
0x89 -> iNop()
0x8a -> iTxa()
0x8b -> iXaa()
0x8c -> iSty()
0x8d -> iSta()
0x8e -> iStx()
0x8f -> iSax()
0x90 -> iBcc()
0x91 -> iSta()
0x92 -> iInvalid()
0x93 -> iAhx()
0x94 -> iSty()
0x95 -> iSta()
0x96 -> iStx()
0x97 -> iSax()
0x98 -> iTya()
0x99 -> iSta()
0x9a -> iTxs()
0x9b -> iTas()
0x9c -> iShy()
0x9d -> iSta()
0x9e -> iShx()
0x9f -> iAhx()
0xa0 -> iLdy()
0xa1 -> iLda()
0xa2 -> iLdx()
0xa3 -> iLax()
0xa4 -> iLdy()
0xa5 -> iLda()
0xa6 -> iLdx()
0xa7 -> iLax()
0xa8 -> iTay()
0xa9 -> iLda()
0xaa -> iTax()
0xab -> iLax()
0xac -> iLdy()
0xad -> iLda()
0xae -> iLdx()
0xaf -> iLax()
0xb0 -> iBcs()
0xb1 -> iLda()
0xb2 -> iInvalid()
0xb3 -> iLax()
0xb4 -> iLdy()
0xb5 -> iLda()
0xb6 -> iLdx()
0xb7 -> iLax()
0xb8 -> iClv()
0xb9 -> iLda()
0xba -> iTsx()
0xbb -> iLas()
0xbc -> iLdy()
0xbd -> iLda()
0xbe -> iLdx()
0xbf -> iLax()
0xc0 -> iCpy()
0xc1 -> iCmp()
0xc2 -> iNop()
0xc3 -> iDcp()
0xc4 -> iCpy()
0xc5 -> iCmp()
0xc6 -> iDec()
0xc7 -> iDcp()
0xc8 -> iIny()
0xc9 -> iCmp()
0xca -> iDex()
0xcb -> iAxs()
0xcc -> iCpy()
0xcd -> iCmp()
0xce -> iDec()
0xcf -> iDcp()
0xd0 -> iBne()
0xd1 -> iCmp()
0xd2 -> iInvalid()
0xd3 -> iDcp()
0xd4 -> iNop()
0xd5 -> iCmp()
0xd6 -> iDec()
0xd7 -> iDcp()
0xd8 -> iCld()
0xd9 -> iCmp()
0xda -> iNop()
0xdb -> iDcp()
0xdc -> iNop()
0xdd -> iCmp()
0xde -> iDec()
0xdf -> iDcp()
0xe0 -> iCpx()
0xe1 -> iSbc()
0xe2 -> iNop()
0xe3 -> iIsc()
0xe4 -> iCpx()
0xe5 -> iSbc()
0xe6 -> iInc()
0xe7 -> iIsc()
0xe8 -> iInx()
0xe9 -> iSbc()
0xea -> iNop()
0xeb -> iSbc()
0xec -> iCpx()
0xed -> iSbc()
0xee -> iInc()
0xef -> iIsc()
0xf0 -> iBeq()
0xf1 -> iSbc()
0xf2 -> iInvalid()
0xf3 -> iIsc()
0xf4 -> iNop()
0xf5 -> iSbc()
0xf6 -> iInc()
0xf7 -> iIsc()
0xf8 -> iSed()
0xf9 -> iSbc()
0xfa -> iNop()
0xfb -> iIsc()
0xfc -> iNop()
0xfd -> iSbc()
0xfe -> iInc()
0xff -> iIsc()
else -> false /* can't occur */
}
}
// official instructions
protected open fun iAdc(): Boolean {
val operand = getFetched()
if (regP.D) {
// BCD add
// see http://www.6502.org/tutorials/decimal_mode.html
// and http://nesdev.com/6502.txt
// and https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/6510core.c#l598
// (the implementation below is based on the code used by Vice)
var tmp = (regA and 0xf)+(operand and 0xf)+(if (regP.C) 1 else 0)
if (tmp > 9) tmp += 6
tmp = if (tmp <= 0x0f) {
(tmp and 0xf)+(regA and 0xf0)+(operand and 0xf0)
} else {
(tmp and 0xf)+(regA and 0xf0)+(operand and 0xf0)+0x10
}
regP.Z = regA+operand+(if (regP.C) 1 else 0) and 0xff == 0
regP.N = tmp and 0b10000000 != 0
regP.V = (regA xor tmp) and 0x80 != 0 && (regA xor operand) and 0b10000000 == 0
if (tmp and 0x1f0 > 0x90) tmp += 0x60
regP.C = tmp > 0xf0 // original: (tmp and 0xff0) > 0xf0
regA = tmp and 0xff
} else {
// normal add
val tmp = operand+regA+if (regP.C) 1 else 0
regP.N = (tmp and 0b10000000) != 0
regP.Z = (tmp and 0xff) == 0
regP.V = (regA xor operand).inv() and (regA xor tmp) and 0b10000000 != 0
regP.C = tmp > 0xff
regA = tmp and 0xff
}
return true
}
protected fun iAnd(): Boolean {
regA = regA and getFetched()
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return true
}
protected fun iAsl(): Boolean {
if (currentInstruction.mode == AddrMode.Acc) {
regP.C = (regA and 0b10000000) != 0
regA = (regA shl 1) and 0xff
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
} else {
val data = read(fetchedAddress)
regP.C = (data and 0b10000000) != 0
val shifted = (data shl 1) and 0xff
write(fetchedAddress, shifted)
regP.Z = shifted == 0
regP.N = (shifted and 0b10000000) != 0
}
return false
}
protected fun iBcc(): Boolean {
if (!regP.C) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected fun iBcs(): Boolean {
if (regP.C) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected fun iBeq(): Boolean {
if (regP.Z) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected open fun iBit(): Boolean {
val operand = getFetched()
regP.Z = (regA and operand) == 0
regP.V = (operand and 0b01000000) != 0
regP.N = (operand and 0b10000000) != 0
return false
}
protected fun iBmi(): Boolean {
if (regP.N) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected fun iBne(): Boolean {
if (!regP.Z) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected fun iBpl(): Boolean {
if (!regP.N) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected open fun iBrk(): Boolean {
// handle BRK ('software interrupt')
regPC++
if(nmiAsserted)
return false // if an NMI occurs during BRK, the BRK won't get executed on 6502 (65C02 fixes this)
pushStackAddr(regPC)
regP.B = true
pushStack(regP)
regP.I = true // interrupts are now disabled
// NMOS 6502 doesn't clear the D flag (CMOS 65C02 version does...)
regPC = readWord(IRQ_vector)
// TODO prevent NMI from triggering immediately after IRQ/BRK... how does that work exactly?
return false
}
protected open fun handleInterrupt() {
// handle NMI or IRQ -- very similar to the BRK opcode above
pushStackAddr(regPC)
regPC++
regP.B = false
pushStack(regP)
regP.I = true // interrupts are now disabled
// NMOS 6502 doesn't clear the D flag (CMOS 65C02 version does...)
// jump to the appropriate irq vector and clear the assertion status of the irq
// (hmm... should the cpu do that? or is this the peripheral's job?)
if(nmiAsserted) {
regPC = readWord(NMI_vector)
nmiAsserted = false
} else {
regPC = readWord(IRQ_vector)
irqAsserted = false
}
// TODO prevent NMI from triggering immediately after IRQ/BRK... how does that work exactly?
}
protected fun iBvc(): Boolean {
if (!regP.V) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected fun iBvs(): Boolean {
if (regP.V) {
if(fetchedAddress and 0xff00 != regPC and 0xff00)
instrCycles++
regPC = fetchedAddress
instrCycles++
}
return false
}
protected fun iClc(): Boolean {
regP.C = false
return false
}
protected fun iCld(): Boolean {
regP.D = false
return false
}
protected fun iCli(): Boolean {
regP.I = false
return false
}
protected fun iClv(): Boolean {
regP.V = false
return false
}
protected fun iCmp(operandOverride: Int? = null): Boolean {
val fetched = operandOverride ?: getFetched()
regP.C = regA >= fetched
regP.Z = regA == fetched
regP.N = ((regA-fetched) and 0b10000000) != 0
return true
}
protected fun iCpx(): Boolean {
val fetched = getFetched()
regP.C = regX >= fetched
regP.Z = regX == fetched
regP.N = ((regX-fetched) and 0b10000000) != 0
return false
}
protected fun iCpy(): Boolean {
val fetched = getFetched()
regP.C = regY >= fetched
regP.Z = regY == fetched
regP.N = ((regY-fetched) and 0b10000000) != 0
return false
}
protected open fun iDec(): Boolean {
val data = (read(fetchedAddress)-1) and 0xff
write(fetchedAddress, data)
regP.Z = data == 0
regP.N = (data and 0b10000000) != 0
return false
}
protected fun iDex(): Boolean {
regX = (regX-1) and 0xff
regP.Z = regX == 0
regP.N = (regX and 0b10000000) != 0
return false
}
protected fun iDey(): Boolean {
regY = (regY-1) and 0xff
regP.Z = regY == 0
regP.N = (regY and 0b10000000) != 0
return false
}
protected fun iEor(): Boolean {
regA = regA xor getFetched()
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return true
}
protected open fun iInc(): Boolean {
val data = (read(fetchedAddress)+1) and 0xff
write(fetchedAddress, data)
regP.Z = data == 0
regP.N = (data and 0b10000000) != 0
return false
}
protected fun iInx(): Boolean {
regX = (regX+1) and 0xff
regP.Z = regX == 0
regP.N = (regX and 0b10000000) != 0
return false
}
protected fun iIny(): Boolean {
regY = (regY+1) and 0xff
regP.Z = regY == 0
regP.N = (regY and 0b10000000) != 0
return false
}
protected fun iJmp(): Boolean {
regPC = fetchedAddress
return false
}
protected fun iJsr(): Boolean {
pushStackAddr(regPC-1)
regPC = fetchedAddress
return false
}
protected fun iLda(): Boolean {
regA = getFetched()
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return true
}
protected fun iLdx(): Boolean {
regX = getFetched()
regP.Z = regX == 0
regP.N = (regX and 0b10000000) != 0
return true
}
protected fun iLdy(): Boolean {
regY = getFetched()
regP.Z = regY == 0
regP.N = (regY and 0b10000000) != 0
return true
}
protected fun iLsr(): Boolean {
if (currentInstruction.mode == AddrMode.Acc) {
regP.C = (regA and 1) == 1
regA = regA ushr 1
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
} else {
val data = read(fetchedAddress)
regP.C = (data and 1) == 1
val shifted = data ushr 1
write(fetchedAddress, shifted)
regP.Z = shifted == 0
regP.N = (shifted and 0b10000000) != 0
}
return false
}
protected fun iNop(): Boolean {
return currentOpcode in listOf(0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc)
}
protected fun iOra(): Boolean {
regA = regA or getFetched()
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return true
}
protected fun iPha(): Boolean {
pushStack(regA)
return false
}
protected fun iPhp(): Boolean {
val origBreakflag = regP.B
regP.B = true
pushStack(regP)
regP.B = origBreakflag
return false
}
protected fun iPla(): Boolean {
regA = popStack()
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return false
}
protected fun iPlp(): Boolean {
regP.fromInt(popStack())
regP.B = false
return false
}
protected fun iRol(): Boolean {
val oldCarry = regP.C
if (currentInstruction.mode == AddrMode.Acc) {
regP.C = (regA and 0b10000000) != 0
regA = (regA shl 1 and 0xff) or (if (oldCarry) 1 else 0)
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
} else {
val data = read(fetchedAddress)
regP.C = (data and 0b10000000) != 0
val shifted = (data shl 1 and 0xff) or (if (oldCarry) 1 else 0)
write(fetchedAddress, shifted)
regP.Z = shifted == 0
regP.N = (shifted and 0b10000000) != 0
}
return false
}
protected fun iRor(): Boolean {
val oldCarry = regP.C
if (currentInstruction.mode == AddrMode.Acc) {
regP.C = (regA and 1) == 1
regA = (regA ushr 1) or (if (oldCarry) 0b10000000 else 0)
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
} else {
val data = read(fetchedAddress)
regP.C = (data and 1) == 1
val shifted = (data ushr 1) or (if (oldCarry) 0b10000000 else 0)
write(fetchedAddress, shifted)
regP.Z = shifted == 0
regP.N = (shifted and 0b10000000) != 0
}
return false
}
protected fun iRti(): Boolean {
regP.fromInt(popStack())
regP.B = false
regPC = popStackAddr()
return false
}
protected fun iRts(): Boolean {
regPC = popStackAddr()
regPC = (regPC+1) and 0xffff
return false
}
protected open fun iSbc(operandOverride: Int? = null): Boolean {
val operand = operandOverride ?: getFetched()
val tmp = (regA-operand-if (regP.C) 0 else 1) and 0xffff
regP.V = (regA xor operand) and (regA xor tmp) and 0b10000000 != 0
if (regP.D) {
// BCD subtract
// see http://www.6502.org/tutorials/decimal_mode.html
// and http://nesdev.com/6502.txt
// and https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/6510core.c#l1396
// (the implementation below is based on the code used by Vice)
var tmpA = ((regA and 0xf)-(operand and 0xf)-if (regP.C) 0 else 1) and 0xffff
tmpA = if ((tmpA and 0x10) != 0) {
((tmpA-6) and 0xf) or (regA and 0xf0)-(operand and 0xf0)-0x10
} else {
(tmpA and 0xf) or (regA and 0xf0)-(operand and 0xf0)
}
if ((tmpA and 0x100) != 0) tmpA -= 0x60
regA = tmpA and 0xff
} else {
// normal subtract
regA = tmp and 0xff
}
regP.C = tmp < 0x100
regP.Z = (tmp and 0xff) == 0
regP.N = (tmp and 0b10000000) != 0
return true
}
protected fun iSec(): Boolean {
regP.C = true
return false
}
protected fun iSed(): Boolean {
regP.D = true
return false
}
protected fun iSei(): Boolean {
regP.I = true
return false
}
protected fun iSta(): Boolean {
write(fetchedAddress, regA)
return false
}
protected fun iStx(): Boolean {
write(fetchedAddress, regX)
return false
}
protected fun iSty(): Boolean {
write(fetchedAddress, regY)
return false
}
protected fun iTax(): Boolean {
regX = regA
regP.Z = regX == 0
regP.N = (regX and 0b10000000) != 0
return false
}
protected fun iTay(): Boolean {
regY = regA
regP.Z = regY == 0
regP.N = (regY and 0b10000000) != 0
return false
}
protected fun iTsx(): Boolean {
regX = regSP
regP.Z = regX == 0
regP.N = (regX and 0b10000000) != 0
return false
}
protected fun iTxa(): Boolean {
regA = regX
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return false
}
protected fun iTxs(): Boolean {
regSP = regX
return false
}
protected fun iTya(): Boolean {
regA = regY
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return false
}
// unofficial/illegal 6502 instructions
// see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
// or https://github.com/quietust/nintendulator/blob/master/src/CPU.cpp (search for LogBadOps)
// TODO: actually implement the illegal opcodes
private fun iAhx(): Boolean {
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
val value = regA and regX and (addrHi+1)
write(fetchedAddress, value)
return false
}
private fun iAlr(): Boolean {
iAnd()
iLsr()
return false
}
private fun iAnc(): Boolean {
iAnd()
regP.C = regP.N
return false
}
private fun iArr(): Boolean {
iAnd()
iRor()
return false
}
private fun iAxs(): Boolean {
val oldA = regA
regA = regA and regX
regP.C = false
iSbc()
regX = regA
regA = oldA
return false
}
private fun iDcp(): Boolean {
val data = (read(fetchedAddress)-1) and 0xff
write(fetchedAddress, data)
iCmp(data)
return false
}
private fun iIsc(): Boolean {
val data = (read(fetchedAddress)+1) and 0xff
write(fetchedAddress, data)
iSbc(data)
return false
}
private fun iLas(): Boolean {
regA = regSP and getFetched()
regX = regA
regSP = regA
regP.Z = regA == 0
regP.N = (regA and 0b10000000) != 0
return false
}
private fun iLax(): Boolean {
iLda()
regX = regA
return true
}
private fun iRla(): Boolean {
iRol()
iAnd()
return false
}
private fun iRra(): Boolean {
iRor()
iAdc()
return false
}
private fun iSax(): Boolean {
write(fetchedAddress, regA and regX)
return false
}
private fun iShx(): Boolean {
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
write(fetchedAddress, regX and (addrHi+1))
return false
}
private fun iShy(): Boolean {
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
write(fetchedAddress, regY and (addrHi+1))
return false
}
private fun iSlo(): Boolean {
iAsl()
iOra()
return false
}
private fun iSre(): Boolean {
iLsr()
iEor()
return false
}
private fun iTas(): Boolean {
regSP = regA and regX
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
write(fetchedAddress, regSP and addrHi)
return false
}
private fun iXaa(): Boolean {
regA = (regX and fetchedData)
return false
}
// invalid instruction (JAM / KIL / HLT)
private fun iInvalid(): Boolean {
throw InstructionError("invalid instruction encountered: opcode=${hexB(currentOpcode)} instr=${currentInstruction.mnemonic} @ ${hexW(currentOpcodeAddress)}")
}
}