2019-09-14 19:11:20 +00:00
|
|
|
package razorvine.ksim65
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-14 19:11:20 +00:00
|
|
|
import razorvine.ksim65.components.Address
|
|
|
|
import razorvine.ksim65.components.BusComponent
|
|
|
|
import razorvine.ksim65.components.MemoryComponent
|
|
|
|
import razorvine.ksim65.components.UByte
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-28 23:26:31 +00:00
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
/**
|
|
|
|
* 6502 cpu simulation (the NMOS version) including the 'illegal' opcodes.
|
2019-09-14 13:46:55 +00:00
|
|
|
* TODO: actually implement the illegal opcodes, see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
|
|
|
|
* TODO: add the optional additional cycles to certain instructions and addressing modes
|
2019-09-12 22:14:49 +00:00
|
|
|
*/
|
2019-09-29 09:29:11 +00:00
|
|
|
open class Cpu6502 : BusComponent() {
|
2019-09-15 03:04:57 +00:00
|
|
|
open val name = "6502"
|
2019-09-14 16:58:45 +00:00
|
|
|
var tracing: ((state:String) -> Unit)? = null
|
2019-09-27 20:38:36 +00:00
|
|
|
var totalCycles = 0L
|
2019-09-12 22:14:49 +00:00
|
|
|
protected set
|
2019-09-27 20:38:36 +00:00
|
|
|
private var resetTime = System.nanoTime()
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
var breakpointForBRK: BreakpointHandler? = null
|
|
|
|
|
2019-09-11 00:17:59 +00:00
|
|
|
class InstructionError(msg: String) : RuntimeException(msg)
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
const val NMI_vector = 0xfffa
|
|
|
|
const val RESET_vector = 0xfffc
|
|
|
|
const val IRQ_vector = 0xfffe
|
|
|
|
const val resetCycles = 8
|
|
|
|
}
|
|
|
|
|
|
|
|
class StatusRegister(
|
2019-09-11 00:19:33 +00:00
|
|
|
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
|
2019-09-11 00:17:59 +00:00
|
|
|
) {
|
2019-09-23 19:42:37 +00:00
|
|
|
fun asInt(): Int {
|
2019-09-11 00:17:59 +00:00
|
|
|
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)
|
2019-09-23 19:42:37 +00:00
|
|
|
)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 19:42:37 +00:00
|
|
|
fun fromInt(byte: Int) {
|
2019-09-11 00:17:59 +00:00
|
|
|
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 {
|
2019-09-23 19:42:37 +00:00
|
|
|
return asInt().toString(2).padStart(8, '0')
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-23 19:42:37 +00:00
|
|
|
override fun hashCode(): Int = asInt()
|
2019-09-11 00:17:59 +00:00
|
|
|
|
|
|
|
override fun equals(other: Any?): Boolean {
|
|
|
|
if (other !is StatusRegister)
|
|
|
|
return false
|
2019-09-23 19:42:37 +00:00
|
|
|
return asInt() == other.asInt()
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
/**
|
|
|
|
* 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)
|
2019-09-23 19:42:37 +00:00
|
|
|
|
|
|
|
class State (
|
|
|
|
val A: UByte,
|
|
|
|
val X: UByte,
|
|
|
|
val Y: UByte,
|
|
|
|
val SP: Address,
|
|
|
|
val P: StatusRegister,
|
|
|
|
val PC: Address,
|
|
|
|
val cycles: Long
|
2019-09-28 23:26:31 +00:00
|
|
|
) {
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-10-09 01:24:51 +00:00
|
|
|
enum class AddrMode {
|
2019-09-27 20:38:36 +00:00
|
|
|
Imp,
|
|
|
|
Acc,
|
|
|
|
Imm,
|
|
|
|
Zp,
|
|
|
|
Zpr, // special addressing mode used by the 65C02
|
|
|
|
ZpX,
|
|
|
|
ZpY,
|
|
|
|
Rel,
|
|
|
|
Abs,
|
|
|
|
AbsX,
|
|
|
|
AbsY,
|
|
|
|
Ind,
|
|
|
|
IzX,
|
|
|
|
IzY,
|
|
|
|
Izp, // special addressing mode used by the 65C02
|
|
|
|
IaX, // special addressing mode used by the 65C02
|
|
|
|
}
|
|
|
|
|
2019-10-09 01:24:51 +00:00
|
|
|
class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
|
2019-09-27 20:38:36 +00:00
|
|
|
|
2019-09-14 19:11:20 +00:00
|
|
|
var regA: Int = 0
|
|
|
|
var regX: Int = 0
|
|
|
|
var regY: Int = 0
|
|
|
|
var regSP: Int = 0
|
|
|
|
var regPC: Address = 0
|
|
|
|
val regP = StatusRegister()
|
2019-09-11 00:17:59 +00:00
|
|
|
var currentOpcode: Int = 0
|
2019-09-14 16:58:45 +00:00
|
|
|
protected set
|
|
|
|
var instrCycles: Int = 0
|
|
|
|
protected set
|
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected lateinit var currentInstruction: Instruction
|
2019-09-15 03:04:57 +00:00
|
|
|
val currentMnemonic: String
|
|
|
|
get() = currentInstruction.mnemonic
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-27 20:38:36 +00:00
|
|
|
val averageSpeedKhzSinceReset: Double
|
|
|
|
get() = totalCycles.toDouble() / (System.nanoTime() - resetTime) * 1_000_000
|
|
|
|
|
2019-09-23 19:42:37 +00:00
|
|
|
@Synchronized fun snapshot(): State {
|
|
|
|
val status = StatusRegister().also { it.fromInt(regP.asInt()) }
|
|
|
|
return State(regA.toShort(),
|
|
|
|
regX.toShort(),
|
|
|
|
regY.toShort(),
|
|
|
|
regSP,
|
|
|
|
status,
|
|
|
|
regPC,
|
|
|
|
totalCycles)
|
|
|
|
}
|
|
|
|
|
2019-09-11 00:17:59 +00:00
|
|
|
// has an interrupt been requested?
|
2019-09-17 00:47:21 +00:00
|
|
|
protected enum class Interrupt {
|
|
|
|
IRQ,
|
|
|
|
NMI
|
|
|
|
}
|
|
|
|
protected var pendingInterrupt: Interrupt? = null
|
2019-09-11 00:17:59 +00:00
|
|
|
|
|
|
|
// data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied)
|
2019-09-14 16:58:45 +00:00
|
|
|
protected var fetchedData: Int = 0
|
2019-09-11 00:17:59 +00:00
|
|
|
|
|
|
|
// all other addressing modes yield a fetched memory address
|
2019-09-12 22:14:49 +00:00
|
|
|
protected var fetchedAddress: Address = 0
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
private val breakpoints = mutableMapOf<Address, BreakpointHandler>()
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
fun addBreakpoint(address: Address, handler: BreakpointHandler) {
|
|
|
|
breakpoints[address] = handler
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 16:58:45 +00:00
|
|
|
fun removeBreakpoint(address: Address) = breakpoints.remove(address)
|
|
|
|
|
2019-09-16 23:18:32 +00:00
|
|
|
fun disassemble(memory: MemoryComponent, from: Address, to: Address) =
|
|
|
|
disassemble(memory.data, memory.startAddress, from, to)
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-10-09 01:24:51 +00:00
|
|
|
fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): Pair<List<String>, Address> {
|
2019-09-18 23:22:11 +00:00
|
|
|
var location = from
|
2019-09-11 00:17:59 +00:00
|
|
|
val result = mutableListOf<String>()
|
|
|
|
|
2019-09-18 23:22:11 +00:00
|
|
|
while (location <= to) {
|
2019-09-16 23:18:32 +00:00
|
|
|
val dis = disassembleOneInstruction(memory, location, baseAddress)
|
|
|
|
result.add(dis.first)
|
2019-09-18 23:22:11 +00:00
|
|
|
location += dis.second
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-10-09 01:24:51 +00:00
|
|
|
return Pair(result, location)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-18 23:22:11 +00:00
|
|
|
fun disassembleOneInstruction(memory: Array<UByte>, address: Address, baseAddress: Address): Pair<String, Int> {
|
2019-09-16 23:18:32 +00:00
|
|
|
val spacing1 = " "
|
|
|
|
val spacing2 = " "
|
|
|
|
val spacing3 = " "
|
2019-09-18 23:22:11 +00:00
|
|
|
val location = address-baseAddress
|
2019-09-16 23:18:32 +00:00
|
|
|
val byte = memory[location]
|
2019-09-23 20:09:00 +00:00
|
|
|
val line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
|
2019-09-16 23:18:32 +00:00
|
|
|
val opcode = instructions[byte.toInt()]
|
2019-09-18 23:22:11 +00:00
|
|
|
return when (opcode.mode) {
|
2019-09-16 23:18:32 +00:00
|
|
|
AddrMode.Acc -> {
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "$spacing1 ${opcode.mnemonic} a", 1)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Imp -> {
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "$spacing1 ${opcode.mnemonic}", 1)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Imm -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val value = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Zp -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Zpr -> {
|
|
|
|
// addressing mode used by the 65C02, put here for convenience
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
|
|
|
val rel = memory[location+2]
|
2019-09-16 23:18:32 +00:00
|
|
|
val target =
|
|
|
|
if (rel <= 0x7f)
|
2019-09-18 23:22:11 +00:00
|
|
|
location + 3 + rel + baseAddress
|
2019-09-16 23:18:32 +00:00
|
|
|
else
|
2019-09-18 23:22:11 +00:00
|
|
|
location + 3 - (256 - rel) + baseAddress
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}", 3)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Izp -> {
|
|
|
|
// addressing mode used by the 65C02, put here for convenience
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.IaX -> {
|
|
|
|
// addressing mode used by the 65C02, put here for convenience
|
2019-09-18 23:22:11 +00:00
|
|
|
val lo = memory[location+1]
|
|
|
|
val hi = memory[location+2]
|
2019-09-16 23:18:32 +00:00
|
|
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)", 3)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.ZpX -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.ZpY -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Rel -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val rel = memory[location+1]
|
2019-09-16 23:18:32 +00:00
|
|
|
val target =
|
|
|
|
if (rel <= 0x7f)
|
2019-09-18 23:22:11 +00:00
|
|
|
location + 2 + rel + baseAddress
|
2019-09-16 23:18:32 +00:00
|
|
|
else
|
2019-09-18 23:22:11 +00:00
|
|
|
location + 2 - (256 - rel) + baseAddress
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Abs -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val lo = memory[location+1]
|
|
|
|
val hi = memory[location+2]
|
2019-09-16 23:18:32 +00:00
|
|
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}", 3)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.AbsX -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val lo = memory[location+1]
|
|
|
|
val hi = memory[location+2]
|
2019-09-16 23:18:32 +00:00
|
|
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x", 3)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.AbsY -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val lo = memory[location+1]
|
|
|
|
val hi = memory[location+2]
|
2019-09-16 23:18:32 +00:00
|
|
|
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y", 3)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.Ind -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val lo = memory[location+1]
|
|
|
|
val hi = memory[location+2]
|
2019-09-16 23:18:32 +00:00
|
|
|
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})", 3)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.IzX -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
AddrMode.IzY -> {
|
2019-09-18 23:22:11 +00:00
|
|
|
val zpAddr = memory[location+1]
|
2019-09-23 20:09:00 +00:00
|
|
|
Pair(line + "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y", 2)
|
2019-09-16 23:18:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-15 03:04:57 +00:00
|
|
|
/**
|
|
|
|
* Reset the cpu
|
|
|
|
*/
|
2019-09-11 00:17:59 +00:00
|
|
|
override fun reset() {
|
2019-09-27 20:38:36 +00:00
|
|
|
regP.I = true
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = false
|
|
|
|
regP.Z = false
|
|
|
|
regP.D = false
|
|
|
|
regP.B = false
|
|
|
|
regP.V = false
|
|
|
|
regP.N = false
|
2019-09-27 20:38:36 +00:00
|
|
|
regSP = 0xfd
|
|
|
|
regPC = readWord(RESET_vector)
|
|
|
|
regA = 0
|
|
|
|
regX = 0
|
|
|
|
regY = 0
|
2019-09-11 00:17:59 +00:00
|
|
|
instrCycles = resetCycles // a reset takes time as well
|
|
|
|
currentOpcode = 0
|
2019-09-11 01:00:18 +00:00
|
|
|
currentInstruction = instructions[0]
|
2019-09-27 20:38:36 +00:00
|
|
|
totalCycles = 0
|
|
|
|
resetTime = System.nanoTime()
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-15 03:04:57 +00:00
|
|
|
/**
|
2019-10-04 18:39:57 +00:00
|
|
|
* Process once clock cycle in the cpu.
|
|
|
|
* Use this if goal is cycle-perfect emulation.
|
2019-09-15 03:04:57 +00:00
|
|
|
*/
|
2019-09-11 00:17:59 +00:00
|
|
|
override fun clock() {
|
|
|
|
if (instrCycles == 0) {
|
|
|
|
if (pendingInterrupt != null) {
|
|
|
|
// NMI or IRQ interrupt.
|
|
|
|
// handled by the BRK instruction logic.
|
|
|
|
currentOpcode = 0
|
2019-09-11 01:00:18 +00:00
|
|
|
currentInstruction = instructions[0]
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
2019-09-11 01:00:18 +00:00
|
|
|
// no interrupt, fetch next instruction from memory
|
2019-09-14 19:11:20 +00:00
|
|
|
currentOpcode = read(regPC)
|
2019-09-11 01:00:18 +00:00
|
|
|
currentInstruction = instructions[currentOpcode]
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
// tracing and breakpoint handling
|
2019-09-28 23:26:31 +00:00
|
|
|
tracing?.invoke(snapshot().toString())
|
2019-09-29 09:29:11 +00:00
|
|
|
breakpoints[regPC]?.let {
|
|
|
|
if(breakpoint(it))
|
|
|
|
return
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
if(currentOpcode==0x00)
|
|
|
|
breakpointForBRK?.let {
|
|
|
|
if(breakpoint(it))
|
|
|
|
return
|
|
|
|
}
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 19:11:20 +00:00
|
|
|
regPC++
|
2019-09-11 00:17:59 +00:00
|
|
|
instrCycles = currentInstruction.cycles
|
2019-09-11 01:00:18 +00:00
|
|
|
applyAddressingMode(currentInstruction.mode)
|
|
|
|
dispatchOpcode(currentOpcode)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
instrCycles--
|
|
|
|
totalCycles++
|
|
|
|
}
|
|
|
|
|
2019-09-29 09:29:11 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-09-15 03:04:57 +00:00
|
|
|
/**
|
2019-10-04 18:39:57 +00:00
|
|
|
* Execute one single complete instruction.
|
|
|
|
* Use this when the goal is emulation performance and not a cycle perfect system.
|
2019-09-15 03:04:57 +00:00
|
|
|
*/
|
2019-09-12 22:14:49 +00:00
|
|
|
open fun step() {
|
2019-10-04 18:39:57 +00:00
|
|
|
totalCycles += instrCycles
|
|
|
|
instrCycles = 0
|
2019-09-15 03:04:57 +00:00
|
|
|
clock()
|
2019-10-04 18:39:57 +00:00
|
|
|
totalCycles += instrCycles
|
|
|
|
instrCycles = 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-17 00:47:21 +00:00
|
|
|
fun nmi() {
|
|
|
|
pendingInterrupt = Interrupt.NMI
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-17 00:47:21 +00:00
|
|
|
fun irq() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (!regP.I)
|
2019-09-17 00:47:21 +00:00
|
|
|
pendingInterrupt = Interrupt.IRQ
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 13:46:55 +00:00
|
|
|
protected fun getFetched() =
|
2019-09-11 00:19:33 +00:00
|
|
|
if (currentInstruction.mode == AddrMode.Imm ||
|
|
|
|
currentInstruction.mode == AddrMode.Acc ||
|
|
|
|
currentInstruction.mode == AddrMode.Imp
|
|
|
|
)
|
|
|
|
fetchedData
|
|
|
|
else
|
|
|
|
read(fetchedAddress)
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-14 19:11:20 +00:00
|
|
|
protected fun readPc(): Int = bus.read(regPC++).toInt()
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected fun pushStackAddr(address: Address) {
|
2019-09-11 00:17:59 +00:00
|
|
|
val lo = address and 0xff
|
|
|
|
val hi = (address ushr 8)
|
|
|
|
pushStack(hi)
|
|
|
|
pushStack(lo)
|
|
|
|
}
|
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected fun pushStack(status: StatusRegister) {
|
2019-09-23 19:42:37 +00:00
|
|
|
pushStack(status.asInt())
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun pushStack(data: Int) {
|
2019-09-14 19:11:20 +00:00
|
|
|
write(regSP or 0x0100, data)
|
|
|
|
regSP = (regSP - 1) and 0xff
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun popStack(): Int {
|
2019-09-14 19:11:20 +00:00
|
|
|
regSP = (regSP + 1) and 0xff
|
|
|
|
return read(regSP or 0x0100)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 16:58:45 +00:00
|
|
|
protected fun popStackAddr(): Address {
|
2019-09-11 00:17:59 +00:00
|
|
|
val lo = popStack()
|
|
|
|
val hi = popStack()
|
|
|
|
return lo or (hi shl 8)
|
|
|
|
}
|
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected fun read(address: Address): Int = bus.read(address).toInt()
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun readWord(address: Address): Int = bus.read(address).toInt() or (bus.read(address + 1).toInt() shl 8)
|
2019-09-12 22:14:49 +00:00
|
|
|
protected fun write(address: Address, data: Int) = bus.write(address, data.toShort())
|
2019-09-11 00:17:59 +00:00
|
|
|
|
|
|
|
// opcodes table from http://www.oxyron.de/html/opcodes02.html
|
2019-10-09 01:24:51 +00:00
|
|
|
open val instructions: Array<Instruction> =
|
2019-09-12 22:14:49 +00:00
|
|
|
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) {
|
2019-09-11 01:00:18 +00:00
|
|
|
when (addrMode) {
|
|
|
|
AddrMode.Imp, AddrMode.Acc -> {
|
2019-09-14 19:11:20 +00:00
|
|
|
fetchedData = regA
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
AddrMode.Imm -> {
|
|
|
|
fetchedData = readPc()
|
|
|
|
}
|
|
|
|
AddrMode.Zp -> {
|
|
|
|
fetchedAddress = readPc()
|
|
|
|
}
|
|
|
|
AddrMode.ZpX -> {
|
|
|
|
// note: zeropage index will not leave Zp when page boundary is crossed
|
2019-09-14 19:11:20 +00:00
|
|
|
fetchedAddress = (readPc() + regX) and 0xff
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
AddrMode.ZpY -> {
|
|
|
|
// note: zeropage index will not leave Zp when page boundary is crossed
|
2019-09-14 19:11:20 +00:00
|
|
|
fetchedAddress = (readPc() + regY) and 0xff
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
AddrMode.Rel -> {
|
|
|
|
val relative = readPc()
|
|
|
|
fetchedAddress = if (relative >= 0x80) {
|
2019-09-14 19:11:20 +00:00
|
|
|
regPC - (256 - relative) and 0xffff
|
2019-09-11 01:00:18 +00:00
|
|
|
} else
|
2019-09-14 19:11:20 +00:00
|
|
|
regPC + relative and 0xffff
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
AddrMode.Abs -> {
|
|
|
|
val lo = readPc()
|
|
|
|
val hi = readPc()
|
|
|
|
fetchedAddress = lo or (hi shl 8)
|
|
|
|
}
|
|
|
|
AddrMode.AbsX -> {
|
|
|
|
val lo = readPc()
|
|
|
|
val hi = readPc()
|
2019-09-14 19:11:20 +00:00
|
|
|
fetchedAddress = regX + (lo or (hi shl 8)) and 0xffff
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
AddrMode.AbsY -> {
|
|
|
|
val lo = readPc()
|
|
|
|
val hi = readPc()
|
2019-09-14 19:11:20 +00:00
|
|
|
fetchedAddress = regY + (lo or (hi shl 8)) and 0xffff
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
AddrMode.Ind -> {
|
|
|
|
var lo = readPc()
|
|
|
|
var hi = readPc()
|
|
|
|
fetchedAddress = lo or (hi shl 8)
|
|
|
|
if (lo == 0xff) {
|
2019-10-07 20:10:22 +00:00
|
|
|
// emulate 6502 bug (fixed in 65C02):
|
|
|
|
// not able to fetch an address which crosses the page boundary.
|
2019-09-11 01:00:18 +00:00
|
|
|
lo = read(fetchedAddress)
|
|
|
|
hi = read(fetchedAddress and 0xff00)
|
|
|
|
} else {
|
|
|
|
// normal behavior
|
|
|
|
lo = read(fetchedAddress)
|
|
|
|
hi = read(fetchedAddress + 1)
|
|
|
|
}
|
|
|
|
fetchedAddress = lo or (hi shl 8)
|
|
|
|
}
|
|
|
|
AddrMode.IzX -> {
|
2019-10-09 01:24:51 +00:00
|
|
|
// note: not able to fetch an address which crosses the (zero)page boundary
|
2019-09-11 01:00:18 +00:00
|
|
|
fetchedAddress = readPc()
|
2019-09-14 19:11:20 +00:00
|
|
|
val lo = read((fetchedAddress + regX) and 0xff)
|
|
|
|
val hi = read((fetchedAddress + regX + 1) and 0xff)
|
2019-09-11 01:00:18 +00:00
|
|
|
fetchedAddress = lo or (hi shl 8)
|
|
|
|
}
|
|
|
|
AddrMode.IzY -> {
|
2019-10-09 01:24:51 +00:00
|
|
|
// note: not able to fetch an address which crosses the (zero)page boundary
|
2019-09-11 01:00:18 +00:00
|
|
|
fetchedAddress = readPc()
|
|
|
|
val lo = read(fetchedAddress)
|
|
|
|
val hi = read((fetchedAddress + 1) and 0xff)
|
2019-09-14 19:11:20 +00:00
|
|
|
fetchedAddress = regY + (lo or (hi shl 8)) and 0xffff
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
2019-09-13 20:14:32 +00:00
|
|
|
AddrMode.Zpr, AddrMode.Izp, AddrMode.IaX -> {
|
2019-09-12 22:14:49 +00:00
|
|
|
// addressing mode used by the 65C02 only
|
|
|
|
throw InstructionError("65c02 addressing mode not implemented on 6502")
|
|
|
|
}
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected open fun dispatchOpcode(opcode: Int) {
|
2019-09-11 01:00:18 +00:00
|
|
|
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()
|
2019-09-12 22:14:49 +00:00
|
|
|
else -> { /* can't occur */
|
|
|
|
}
|
2019-09-11 01:00:18 +00:00
|
|
|
}
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 01:00:18 +00:00
|
|
|
|
2019-09-11 00:17:59 +00:00
|
|
|
// official instructions
|
|
|
|
|
2019-09-14 13:46:55 +00:00
|
|
|
protected open fun iAdc() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val operand = getFetched()
|
2019-09-14 19:11:20 +00:00
|
|
|
if (regP.D) {
|
2019-09-11 00:17:59 +00:00
|
|
|
// 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)
|
2019-09-14 19:11:20 +00:00
|
|
|
var tmp = (regA and 0xf) + (operand and 0xf) + (if (regP.C) 1 else 0)
|
2019-09-11 00:17:59 +00:00
|
|
|
if (tmp > 9) tmp += 6
|
|
|
|
tmp = if (tmp <= 0x0f) {
|
2019-09-14 19:11:20 +00:00
|
|
|
(tmp and 0xf) + (regA and 0xf0) + (operand and 0xf0)
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
2019-09-14 19:11:20 +00:00
|
|
|
(tmp and 0xf) + (regA and 0xf0) + (operand and 0xf0) + 0x10
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
2019-09-14 19:11:20 +00:00
|
|
|
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
|
2019-09-11 00:17:59 +00:00
|
|
|
if (tmp and 0x1f0 > 0x90) tmp += 0x60
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = tmp > 0xf0 // original: (tmp and 0xff0) > 0xf0
|
|
|
|
regA = tmp and 0xff
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
|
|
|
// normal add
|
2019-09-14 19:11:20 +00:00
|
|
|
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
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iAnd() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = regA and getFetched()
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iAsl() {
|
2019-09-11 00:17:59 +00:00
|
|
|
if (currentInstruction.mode == AddrMode.Acc) {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = (regA and 0b10000000) != 0
|
|
|
|
regA = (regA shl 1) and 0xff
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
|
|
|
val data = read(fetchedAddress)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = (data and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
val shifted = (data shl 1) and 0xff
|
|
|
|
write(fetchedAddress, shifted)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = shifted == 0
|
|
|
|
regP.N = (shifted and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBcc() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (!regP.C) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBcs() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (regP.C) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBeq() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (regP.Z) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 13:46:55 +00:00
|
|
|
protected open fun iBit() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val operand = getFetched()
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = (regA and operand) == 0
|
|
|
|
regP.V = (operand and 0b01000000) != 0
|
|
|
|
regP.N = (operand and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBmi() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (regP.N) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBne() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (!regP.Z) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBpl() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (!regP.N) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected open fun iBrk() {
|
2019-09-11 00:17:59 +00:00
|
|
|
// handle BRK ('software interrupt') or a real hardware IRQ
|
2019-09-17 00:47:21 +00:00
|
|
|
if (pendingInterrupt != null) {
|
2019-09-14 19:11:20 +00:00
|
|
|
pushStackAddr(regPC - 1)
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
2019-09-14 19:11:20 +00:00
|
|
|
regPC++
|
|
|
|
pushStackAddr(regPC)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
2019-09-17 00:47:21 +00:00
|
|
|
regP.B = pendingInterrupt == null
|
2019-09-14 19:11:20 +00:00
|
|
|
pushStack(regP)
|
|
|
|
regP.I = true // interrupts are now disabled
|
2019-09-12 22:14:49 +00:00
|
|
|
// NMOS 6502 doesn't clear the D flag (CMOS 65C02 version does...)
|
2019-09-17 00:47:21 +00:00
|
|
|
regPC = readWord(if (pendingInterrupt==Interrupt.NMI) NMI_vector else IRQ_vector)
|
2019-09-11 00:17:59 +00:00
|
|
|
pendingInterrupt = null
|
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBvc() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (!regP.V) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iBvs() {
|
2019-09-14 19:11:20 +00:00
|
|
|
if (regP.V) regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iClc() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = false
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iCld() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.D = false
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iCli() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.I = false
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iClv() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.V = false
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iCmp() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val fetched = getFetched()
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = regA >= fetched
|
|
|
|
regP.Z = regA == fetched
|
|
|
|
regP.N = ((regA - fetched) and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iCpx() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val fetched = getFetched()
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = regX >= fetched
|
|
|
|
regP.Z = regX == fetched
|
|
|
|
regP.N = ((regX - fetched) and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iCpy() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val fetched = getFetched()
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = regY >= fetched
|
|
|
|
regP.Z = regY == fetched
|
|
|
|
regP.N = ((regY - fetched) and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected open fun iDec() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val data = (read(fetchedAddress) - 1) and 0xff
|
|
|
|
write(fetchedAddress, data)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = data == 0
|
|
|
|
regP.N = (data and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iDex() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regX = (regX - 1) and 0xff
|
|
|
|
regP.Z = regX == 0
|
|
|
|
regP.N = (regX and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iDey() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regY = (regY - 1) and 0xff
|
|
|
|
regP.Z = regY == 0
|
|
|
|
regP.N = (regY and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iEor() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = regA xor getFetched()
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-12 22:14:49 +00:00
|
|
|
protected open fun iInc() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val data = (read(fetchedAddress) + 1) and 0xff
|
|
|
|
write(fetchedAddress, data)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = data == 0
|
|
|
|
regP.N = (data and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iInx() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regX = (regX + 1) and 0xff
|
|
|
|
regP.Z = regX == 0
|
|
|
|
regP.N = (regX and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iIny() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regY = (regY + 1) and 0xff
|
|
|
|
regP.Z = regY == 0
|
|
|
|
regP.N = (regY and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iJmp() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iJsr() {
|
2019-09-14 19:11:20 +00:00
|
|
|
pushStackAddr(regPC - 1)
|
|
|
|
regPC = fetchedAddress
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iLda() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = getFetched()
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iLdx() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regX = getFetched()
|
|
|
|
regP.Z = regX == 0
|
|
|
|
regP.N = (regX and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iLdy() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regY = getFetched()
|
|
|
|
regP.Z = regY == 0
|
|
|
|
regP.N = (regY and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iLsr() {
|
2019-09-11 00:17:59 +00:00
|
|
|
if (currentInstruction.mode == AddrMode.Acc) {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = (regA and 1) == 1
|
|
|
|
regA = regA ushr 1
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
|
|
|
val data = read(fetchedAddress)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = (data and 1) == 1
|
2019-09-11 00:17:59 +00:00
|
|
|
val shifted = data ushr 1
|
|
|
|
write(fetchedAddress, shifted)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = shifted == 0
|
|
|
|
regP.N = (shifted and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iNop() {}
|
2019-09-11 00:17:59 +00:00
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iOra() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = regA or getFetched()
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iPha() {
|
2019-09-14 19:11:20 +00:00
|
|
|
pushStack(regA)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iPhp() {
|
2019-09-14 19:11:20 +00:00
|
|
|
val origBreakflag = regP.B
|
|
|
|
regP.B = true
|
|
|
|
pushStack(regP)
|
|
|
|
regP.B = origBreakflag
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iPla() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = popStack()
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iPlp() {
|
2019-09-23 19:42:37 +00:00
|
|
|
regP.fromInt(popStack())
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.B = true // break is always 1 except when pushing on stack
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iRol() {
|
2019-09-14 19:11:20 +00:00
|
|
|
val oldCarry = regP.C
|
2019-09-11 00:17:59 +00:00
|
|
|
if (currentInstruction.mode == AddrMode.Acc) {
|
2019-09-14 19:11:20 +00:00
|
|
|
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
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
|
|
|
val data = read(fetchedAddress)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = (data and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
val shifted = (data shl 1 and 0xff) or (if (oldCarry) 1 else 0)
|
|
|
|
write(fetchedAddress, shifted)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = shifted == 0
|
|
|
|
regP.N = (shifted and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iRor() {
|
2019-09-14 19:11:20 +00:00
|
|
|
val oldCarry = regP.C
|
2019-09-11 00:17:59 +00:00
|
|
|
if (currentInstruction.mode == AddrMode.Acc) {
|
2019-09-14 19:11:20 +00:00
|
|
|
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
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
|
|
|
val data = read(fetchedAddress)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = (data and 1) == 1
|
2019-09-11 00:17:59 +00:00
|
|
|
val shifted = (data ushr 1) or (if (oldCarry) 0b10000000 else 0)
|
|
|
|
write(fetchedAddress, shifted)
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.Z = shifted == 0
|
|
|
|
regP.N = (shifted and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iRti() {
|
2019-09-23 19:42:37 +00:00
|
|
|
regP.fromInt(popStack())
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.B = true // break is always 1 except when pushing on stack
|
|
|
|
regPC = popStackAddr()
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iRts() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regPC = popStackAddr()
|
|
|
|
regPC = (regPC + 1) and 0xffff
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-14 13:46:55 +00:00
|
|
|
protected open fun iSbc() {
|
2019-09-11 00:17:59 +00:00
|
|
|
val operand = getFetched()
|
2019-09-14 19:11:20 +00:00
|
|
|
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) {
|
2019-09-11 00:17:59 +00:00
|
|
|
// 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)
|
2019-09-14 19:11:20 +00:00
|
|
|
var tmpA = ((regA and 0xf) - (operand and 0xf) - if (regP.C) 0 else 1) and 0xffff
|
2019-09-11 00:17:59 +00:00
|
|
|
tmpA = if ((tmpA and 0x10) != 0) {
|
2019-09-14 19:11:20 +00:00
|
|
|
((tmpA - 6) and 0xf) or (regA and 0xf0) - (operand and 0xf0) - 0x10
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
2019-09-14 19:11:20 +00:00
|
|
|
(tmpA and 0xf) or (regA and 0xf0) - (operand and 0xf0)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
if ((tmpA and 0x100) != 0) tmpA -= 0x60
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = tmpA and 0xff
|
2019-09-11 00:17:59 +00:00
|
|
|
} else {
|
|
|
|
// normal subtract
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = tmp and 0xff
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = tmp < 0x100
|
|
|
|
regP.Z = (tmp and 0xff) == 0
|
|
|
|
regP.N = (tmp and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iSec() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.C = true
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iSed() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.D = true
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iSei() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regP.I = true
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iSta() {
|
2019-09-14 19:11:20 +00:00
|
|
|
write(fetchedAddress, regA)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iStx() {
|
2019-09-14 19:11:20 +00:00
|
|
|
write(fetchedAddress, regX)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iSty() {
|
2019-09-14 19:11:20 +00:00
|
|
|
write(fetchedAddress, regY)
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iTax() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regX = regA
|
|
|
|
regP.Z = regX == 0
|
|
|
|
regP.N = (regX and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iTay() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regY = regA
|
|
|
|
regP.Z = regY == 0
|
|
|
|
regP.N = (regY and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iTsx() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regX = regSP
|
|
|
|
regP.Z = regX == 0
|
|
|
|
regP.N = (regX and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iTxa() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = regX
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iTxs() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regSP = regX
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
protected fun iTya() {
|
2019-09-14 19:11:20 +00:00
|
|
|
regA = regY
|
|
|
|
regP.Z = regA == 0
|
|
|
|
regP.N = (regA and 0b10000000) != 0
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|
|
|
|
|
2019-09-11 23:31:25 +00:00
|
|
|
// unofficial/illegal 6502 instructions
|
2019-09-11 00:17:59 +00:00
|
|
|
|
|
|
|
private fun iAhx() {
|
|
|
|
TODO("ahx - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iAlr() {
|
|
|
|
TODO("alr=asr - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iAnc() {
|
|
|
|
TODO("anc - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iArr() {
|
|
|
|
TODO("arr - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iAxs() {
|
|
|
|
TODO("axs - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iDcp() {
|
|
|
|
TODO("dcp - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iIsc() {
|
|
|
|
TODO("isc=isb - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iLas() {
|
|
|
|
TODO("las=lar - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iLax() {
|
|
|
|
TODO("lax - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iRla() {
|
|
|
|
TODO("rla - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iRra() {
|
|
|
|
TODO("rra - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iSax() {
|
|
|
|
TODO("sax - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iShx() {
|
|
|
|
TODO("shx - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iShy() {
|
|
|
|
TODO("shy - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iSlo() {
|
|
|
|
TODO("slo=aso - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iSre() {
|
|
|
|
TODO("sre=lse - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iTas() {
|
|
|
|
TODO("tas - ('illegal' instruction)")
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun iXaa() {
|
|
|
|
TODO("xaa - ('illegal' instruction)")
|
|
|
|
}
|
2019-09-11 01:00:18 +00:00
|
|
|
|
|
|
|
// invalid instruction (JAM / KIL)
|
|
|
|
private fun iInvalid() {
|
|
|
|
throw InstructionError(
|
2019-09-13 23:10:10 +00:00
|
|
|
"invalid instruction encountered: opcode=${hexB(currentOpcode)} instr=${currentInstruction.mnemonic}"
|
2019-09-11 01:00:18 +00:00
|
|
|
)
|
|
|
|
}
|
2019-09-11 00:17:59 +00:00
|
|
|
}
|