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

1477 lines
51 KiB
Kotlin
Raw Normal View History

package razorvine.ksim65
2019-09-11 00:17:59 +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-12 22:14:49 +00:00
/**
* 6502 cpu simulation (the NMOS version) including the 'illegal' opcodes.
* 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
*/
open class Cpu6502(private val stopOnBrk: Boolean = false) : 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-11 00:17:59 +00:00
var totalCycles: Long = 0
2019-09-12 22:14:49 +00:00
protected set
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
}
2019-09-14 16:58:45 +00:00
protected enum class AddrMode {
2019-09-11 00:17:59 +00:00
Imp,
Acc,
Imm,
Zp,
2019-09-12 22:14:49 +00:00
Zpr, // special addressing mode used by the 65C02
2019-09-11 00:17:59 +00:00
ZpX,
ZpY,
Rel,
Abs,
AbsX,
AbsY,
Ind,
IzX,
2019-09-12 22:14:49 +00:00
IzY,
2019-09-13 20:14:32 +00:00
Izp, // special addressing mode used by the 65C02
IaX, // special addressing mode used by the 65C02
2019-09-11 00:17:59 +00:00
}
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
) {
fun asByte(): UByte {
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)
).toShort()
}
fun fromByte(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 asByte().toString(2).padStart(8, '0')
}
override fun hashCode(): Int = asByte().toInt()
override fun equals(other: Any?): Boolean {
if (other !is StatusRegister)
return false
return asByte() == other.asByte()
}
}
2019-09-14 16:58:45 +00:00
protected class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int)
2019-09-11 00:17:59 +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
// has an interrupt been requested?
2019-09-11 23:31:25 +00:00
protected var pendingInterrupt: Pair<Boolean, BusComponent>? = 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-14 16:58:45 +00:00
class BreakpointResult(val newPC: Address?, val newOpcode: Int?)
private val breakpoints = mutableMapOf<Address, (cpu: Cpu6502, pc: Address) -> BreakpointResult>()
2019-09-11 00:17:59 +00:00
2019-09-14 16:58:45 +00:00
fun addBreakpoint(address: Address, action: (cpu: Cpu6502, pc: Address) -> BreakpointResult) {
2019-09-11 00:17:59 +00:00
breakpoints[address] = action
}
2019-09-14 16:58:45 +00:00
fun removeBreakpoint(address: Address) = breakpoints.remove(address)
fun hexW(number: Address, allowSingleByte: Boolean = false): String {
2019-09-11 23:31:25 +00:00
val msb = number ushr 8
val lsb = number and 0xff
return if (msb == 0 && allowSingleByte)
hexB(lsb)
else
hexB(msb) + hexB(lsb)
2019-09-11 23:31:25 +00:00
}
2019-09-14 16:58:45 +00:00
fun hexB(number: Short): String = hexB(number.toInt())
2019-09-11 23:31:25 +00:00
2019-09-14 16:58:45 +00:00
fun hexB(number: Int): String {
2019-09-11 23:31:25 +00:00
val hexdigits = "0123456789abcdef"
val loNibble = number and 15
val hiNibble = number ushr 4
return hexdigits[hiNibble].toString() + hexdigits[loNibble]
}
2019-09-11 00:17:59 +00:00
fun disassemble(component: MemoryComponent, from: Address, to: Address) =
disassemble(component.copyOfMem(), component.startAddress, from, to)
2019-09-11 00:17:59 +00:00
fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): List<String> {
2019-09-14 15:25:41 +00:00
var location = from - baseAddress
2019-09-11 00:17:59 +00:00
val spacing1 = " "
val spacing2 = " "
val spacing3 = " "
val result = mutableListOf<String>()
2019-09-14 15:25:41 +00:00
while (location <= (to - baseAddress)) {
val byte = memory[location]
var line = "\$${hexW(location+baseAddress)} ${hexB(byte)} "
location++
val opcode = instructions[byte.toInt()]
2019-09-11 00:17:59 +00:00
when (opcode.mode) {
AddrMode.Acc -> {
line += "$spacing1 ${opcode.mnemonic} a"
}
AddrMode.Imp -> {
line += "$spacing1 ${opcode.mnemonic}"
}
AddrMode.Imm -> {
2019-09-14 15:25:41 +00:00
val value = memory[location++]
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
2019-09-11 00:17:59 +00:00
}
AddrMode.Zp -> {
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
2019-09-11 00:17:59 +00:00
}
2019-09-12 22:14:49 +00:00
AddrMode.Zpr -> {
// addressing mode used by the 65C02, put here for convenience
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
val rel = memory[location++]
val target =
if (rel <= 0x7f)
2019-09-14 15:25:41 +00:00
location + rel + baseAddress
else
2019-09-14 15:25:41 +00:00
location - (256 - rel) + baseAddress
line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}"
2019-09-12 22:14:49 +00:00
}
AddrMode.Izp -> {
// addressing mode used by the 65C02, put here for convenience
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})"
2019-09-13 20:14:32 +00:00
}
AddrMode.IaX -> {
// addressing mode used by the 65C02, put here for convenience
2019-09-14 15:25:41 +00:00
val lo = memory[location++]
val hi = memory[location++]
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)"
2019-09-12 22:14:49 +00:00
}
2019-09-11 00:17:59 +00:00
AddrMode.ZpX -> {
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
2019-09-11 00:17:59 +00:00
}
AddrMode.ZpY -> {
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
2019-09-11 00:17:59 +00:00
}
AddrMode.Rel -> {
2019-09-14 15:25:41 +00:00
val rel = memory[location++]
2019-09-11 00:17:59 +00:00
val target =
2019-09-11 00:19:33 +00:00
if (rel <= 0x7f)
2019-09-14 15:25:41 +00:00
location + rel + baseAddress
2019-09-11 00:19:33 +00:00
else
2019-09-14 15:25:41 +00:00
location - (256 - rel) + baseAddress
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
2019-09-11 00:17:59 +00:00
}
AddrMode.Abs -> {
2019-09-14 15:25:41 +00:00
val lo = memory[location++]
val hi = memory[location++]
2019-09-11 00:17:59 +00:00
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
2019-09-11 00:17:59 +00:00
}
AddrMode.AbsX -> {
2019-09-14 15:25:41 +00:00
val lo = memory[location++]
val hi = memory[location++]
2019-09-11 00:17:59 +00:00
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
2019-09-11 00:17:59 +00:00
}
AddrMode.AbsY -> {
2019-09-14 15:25:41 +00:00
val lo = memory[location++]
val hi = memory[location++]
2019-09-11 00:17:59 +00:00
val absAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
2019-09-11 00:17:59 +00:00
}
AddrMode.Ind -> {
2019-09-14 15:25:41 +00:00
val lo = memory[location++]
val hi = memory[location++]
2019-09-11 00:17:59 +00:00
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})"
2019-09-11 00:17:59 +00:00
}
AddrMode.IzX -> {
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
2019-09-11 00:17:59 +00:00
}
AddrMode.IzY -> {
2019-09-14 15:25:41 +00:00
val zpAddr = memory[location++]
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
2019-09-11 00:17:59 +00:00
}
}
result.add(line)
}
return result
}
2019-09-15 03:04:57 +00:00
/**
* Reset the cpu
*/
2019-09-11 00:17:59 +00:00
override fun reset() {
regSP = 0xfd
regPC = readWord(RESET_vector)
regA = 0
regX = 0
regY = 0
regP.C = false
regP.Z = false
regP.I = true
regP.D = false
regP.B = false
regP.V = false
regP.N = false
2019-09-11 00:17:59 +00:00
instrCycles = resetCycles // a reset takes time as well
currentOpcode = 0
currentInstruction = instructions[0]
2019-09-11 00:17:59 +00:00
}
2019-09-15 03:04:57 +00:00
/**
* Process once clock cycle in the cpu
*/
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
currentInstruction = instructions[0]
2019-09-11 00:17:59 +00:00
} else {
// no interrupt, fetch next instruction from memory
currentOpcode = read(regPC)
currentInstruction = instructions[currentOpcode]
2019-09-11 00:17:59 +00:00
2019-09-14 16:58:45 +00:00
tracing?.invoke(logState())
2019-09-11 00:17:59 +00:00
breakpoints[regPC]?.let { breakpoint ->
val oldPC = regPC
val result = breakpoint(this, regPC)
2019-09-14 16:58:45 +00:00
if(result.newPC!=null)
regPC = result.newPC
if (regPC != oldPC)
2019-09-11 00:17:59 +00:00
return clock()
2019-09-14 16:58:45 +00:00
else if(result.newOpcode!=null) {
currentOpcode = result.newOpcode
currentInstruction = instructions[currentOpcode]
2019-09-14 16:58:45 +00:00
}
2019-09-11 00:17:59 +00:00
}
if (stopOnBrk && currentOpcode == 0) {
throw InstructionError("stopped on BRK instruction at ${hexW(regPC)}")
2019-09-11 00:17:59 +00:00
}
}
regPC++
2019-09-11 00:17:59 +00:00
instrCycles = currentInstruction.cycles
applyAddressingMode(currentInstruction.mode)
dispatchOpcode(currentOpcode)
2019-09-11 00:17:59 +00:00
}
instrCycles--
totalCycles++
}
2019-09-15 03:04:57 +00:00
/**
* Execute one single complete instruction
*/
2019-09-12 22:14:49 +00:00
open fun step() {
2019-09-15 03:04:57 +00:00
while (instrCycles > 0) clock()
clock()
while (instrCycles > 0) clock()
2019-09-11 00:17:59 +00:00
}
fun nmi(source: BusComponent) {
pendingInterrupt = Pair(true, source)
}
fun irq(source: BusComponent) {
if (!regP.I)
2019-09-11 00:17:59 +00:00
pendingInterrupt = Pair(false, source)
}
2019-09-14 16:58:45 +00:00
fun logState(): String =
"cycle:$totalCycles - pc=${hexW(regPC)} " +
"A=${hexB(regA)} " +
"X=${hexB(regX)} " +
"Y=${hexB(regY)} " +
"SP=${hexB(regSP)} " +
" n=" + (if (regP.N) "1" else "0") +
" v=" + (if (regP.V) "1" else "0") +
" b=" + (if (regP.B) "1" else "0") +
" d=" + (if (regP.D) "1" else "0") +
" i=" + (if (regP.I) "1" else "0") +
" z=" + (if (regP.Z) "1" else "0") +
" c=" + (if (regP.C) "1" else "0") +
2019-09-11 00:19:33 +00:00
" icycles=$instrCycles instr=${hexB(currentOpcode)}:${currentInstruction.mnemonic}"
2019-09-11 00:17:59 +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
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-11 00:17:59 +00:00
pushStack(status.asByte().toInt())
}
2019-09-11 23:31:25 +00:00
protected fun pushStack(data: Int) {
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 {
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
protected 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) {
when (addrMode) {
AddrMode.Imp, AddrMode.Acc -> {
fetchedData = regA
}
AddrMode.Imm -> {
fetchedData = readPc()
}
AddrMode.Zp -> {
fetchedAddress = readPc()
}
AddrMode.ZpX -> {
// note: zeropage index will not leave Zp when page boundary is crossed
fetchedAddress = (readPc() + regX) and 0xff
}
AddrMode.ZpY -> {
// note: zeropage index will not leave Zp when page boundary is crossed
fetchedAddress = (readPc() + regY) and 0xff
}
AddrMode.Rel -> {
val relative = readPc()
fetchedAddress = if (relative >= 0x80) {
regPC - (256 - relative) and 0xffff
} else
regPC + relative and 0xffff
}
AddrMode.Abs -> {
val lo = readPc()
val hi = readPc()
fetchedAddress = lo or (hi shl 8)
}
AddrMode.AbsX -> {
val lo = readPc()
val hi = readPc()
fetchedAddress = regX + (lo or (hi shl 8)) and 0xffff
}
AddrMode.AbsY -> {
val lo = readPc()
val hi = readPc()
fetchedAddress = regY + (lo or (hi shl 8)) and 0xffff
}
AddrMode.Ind -> {
// not able to fetch an address which crosses the page boundary (6502, fixed in 65C02)
var lo = readPc()
var hi = readPc()
fetchedAddress = lo or (hi shl 8)
if (lo == 0xff) {
// emulate bug
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 -> {
// note: not able to fetch an adress which crosses the 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)
}
AddrMode.IzY -> {
// note: not able to fetch an adress which crosses the page boundary
fetchedAddress = readPc()
val lo = read(fetchedAddress)
val hi = read((fetchedAddress + 1) and 0xff)
fetchedAddress = regY + (lo or (hi shl 8)) and 0xffff
}
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 00:17:59 +00:00
2019-09-11 23:31:25 +00:00
protected open fun dispatchOpcode(opcode: Int) {
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 00:17:59 +00:00
}
2019-09-11 00:17:59 +00:00
// official instructions
protected open fun iAdc() {
2019-09-11 00:17:59 +00:00
val operand = getFetched()
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)
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) {
(tmp and 0xf) + (regA and 0xf0) + (operand and 0xf0)
2019-09-11 00:17:59 +00:00
} else {
(tmp and 0xf) + (regA and 0xf0) + (operand and 0xf0) + 0x10
2019-09-11 00:17:59 +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
regP.C = tmp > 0xf0 // original: (tmp and 0xff0) > 0xf0
regA = tmp and 0xff
2019-09-11 00:17:59 +00:00
} 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
2019-09-11 00:17:59 +00:00
}
}
2019-09-11 23:31:25 +00:00
protected fun iAnd() {
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) {
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)
regP.C = (data and 0b10000000) != 0
2019-09-11 00:17:59 +00:00
val shifted = (data shl 1) and 0xff
write(fetchedAddress, shifted)
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() {
if (!regP.C) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iBcs() {
if (regP.C) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iBeq() {
if (regP.Z) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
protected open fun iBit() {
2019-09-11 00:17:59 +00:00
val operand = getFetched()
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() {
if (regP.N) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iBne() {
if (!regP.Z) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iBpl() {
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
val interrupt = pendingInterrupt
val nmi = interrupt?.first == true
if (interrupt != null) {
pushStackAddr(regPC - 1)
2019-09-11 00:17:59 +00:00
} else {
regPC++
pushStackAddr(regPC)
2019-09-11 00:17:59 +00:00
}
regP.B = interrupt == null
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...)
regPC = readWord(if (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() {
if (!regP.V) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iBvs() {
if (regP.V) regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iClc() {
regP.C = false
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iCld() {
regP.D = false
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iCli() {
regP.I = false
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iClv() {
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()
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()
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()
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)
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() {
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() {
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() {
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)
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() {
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() {
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() {
regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iJsr() {
pushStackAddr(regPC - 1)
regPC = fetchedAddress
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iLda() {
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() {
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() {
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) {
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)
regP.C = (data and 1) == 1
2019-09-11 00:17:59 +00:00
val shifted = data ushr 1
write(fetchedAddress, shifted)
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() {
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() {
pushStack(regA)
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iPhp() {
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() {
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() {
regP.fromByte(popStack())
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() {
val oldCarry = regP.C
2019-09-11 00:17:59 +00:00
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
2019-09-11 00:17:59 +00:00
} else {
val data = read(fetchedAddress)
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)
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() {
val oldCarry = regP.C
2019-09-11 00:17:59 +00:00
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
2019-09-11 00:17:59 +00:00
} else {
val data = read(fetchedAddress)
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)
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() {
regP.fromByte(popStack())
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() {
regPC = popStackAddr()
regPC = (regPC + 1) and 0xffff
2019-09-11 00:17:59 +00:00
}
protected open fun iSbc() {
2019-09-11 00:17:59 +00:00
val operand = 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) {
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)
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) {
((tmpA - 6) and 0xf) or (regA and 0xf0) - (operand and 0xf0) - 0x10
2019-09-11 00:17:59 +00:00
} else {
(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
regA = tmpA and 0xff
2019-09-11 00:17:59 +00:00
} else {
// normal subtract
regA = tmp and 0xff
2019-09-11 00:17:59 +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() {
regP.C = true
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iSed() {
regP.D = true
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iSei() {
regP.I = true
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iSta() {
write(fetchedAddress, regA)
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iStx() {
write(fetchedAddress, regX)
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iSty() {
write(fetchedAddress, regY)
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iTax() {
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() {
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() {
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() {
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() {
regSP = regX
2019-09-11 00:17:59 +00:00
}
2019-09-11 23:31:25 +00:00
protected fun iTya() {
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)")
}
// invalid instruction (JAM / KIL)
private fun iInvalid() {
throw InstructionError(
"invalid instruction encountered: opcode=${hexB(currentOpcode)} instr=${currentInstruction.mnemonic}"
)
}
2019-09-11 00:17:59 +00:00
}