mirror of
https://github.com/irmen/ksim65.git
synced 2025-01-19 17:29:54 +00:00
fixed cycle times, implemented various illegal opcodes. NesTest now runs flawlessly
This commit is contained in:
parent
35cbe4e3ca
commit
5667c00d85
@ -7,11 +7,10 @@ import razorvine.ksim65.components.UByte
|
||||
|
||||
/**
|
||||
* 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 or https://sourceforge.net/p/moarnes/code/ci/master/tree/src/6502.c
|
||||
*/
|
||||
open class Cpu6502 : BusComponent() {
|
||||
open val name = "6502"
|
||||
var tracing: ((state: String) -> Unit)? = null
|
||||
var tracing: ((state: State) -> Unit)? = null
|
||||
var totalCycles = 0L
|
||||
protected set
|
||||
private var resetTime = System.nanoTime()
|
||||
@ -165,6 +164,9 @@ open class Cpu6502 : BusComponent() {
|
||||
*/
|
||||
override fun clock() {
|
||||
if (instrCycles == 0) {
|
||||
|
||||
tracing?.invoke(snapshot())
|
||||
|
||||
if(nmiAsserted || (irqAsserted && !regP.I)) {
|
||||
handleInterrupt()
|
||||
return
|
||||
@ -176,8 +178,6 @@ open class Cpu6502 : BusComponent() {
|
||||
currentOpcode = read(regPC)
|
||||
currentInstruction = instructions[currentOpcode]
|
||||
|
||||
// tracing and breakpoint handling
|
||||
tracing?.invoke(snapshot().toString())
|
||||
breakpoints[regPC]?.let {
|
||||
if (breakpoint(it)) return
|
||||
}
|
||||
@ -557,7 +557,9 @@ open class Cpu6502 : BusComponent() {
|
||||
val relative = readPc()
|
||||
fetchedAddress = if (relative >= 0x80) {
|
||||
regPC-(256-relative) and 0xffff
|
||||
} else regPC+relative and 0xffff
|
||||
} else {
|
||||
regPC+relative and 0xffff
|
||||
}
|
||||
false
|
||||
}
|
||||
AddrMode.Abs -> {
|
||||
@ -581,6 +583,7 @@ open class Cpu6502 : BusComponent() {
|
||||
(fetchedAddress and 0xff00) != hi shl 8
|
||||
}
|
||||
AddrMode.Ind -> {
|
||||
val extraCycle: Boolean
|
||||
var lo = readPc()
|
||||
var hi = readPc()
|
||||
fetchedAddress = lo or (hi shl 8)
|
||||
@ -589,13 +592,15 @@ open class Cpu6502 : BusComponent() {
|
||||
// not able to fetch an address which crosses the page boundary.
|
||||
lo = read(fetchedAddress)
|
||||
hi = read(fetchedAddress and 0xff00)
|
||||
extraCycle = true
|
||||
} else {
|
||||
// normal behavior
|
||||
lo = read(fetchedAddress)
|
||||
hi = read(fetchedAddress+1)
|
||||
extraCycle = false
|
||||
}
|
||||
fetchedAddress = lo or (hi shl 8)
|
||||
false
|
||||
extraCycle
|
||||
}
|
||||
AddrMode.IzX -> {
|
||||
// note: not able to fetch an address which crosses the (zero)page boundary
|
||||
@ -603,15 +608,17 @@ open class Cpu6502 : BusComponent() {
|
||||
val lo = read((fetchedAddress+regX) and 0xff)
|
||||
val hi = read((fetchedAddress+regX+1) and 0xff)
|
||||
fetchedAddress = lo or (hi shl 8)
|
||||
false
|
||||
// if this address is a different page, extra clock cycle:
|
||||
(fetchedAddress and 0xff00) != hi shl 8
|
||||
}
|
||||
AddrMode.IzY -> {
|
||||
// note: not able to fetch an address which crosses the (zero)page boundary
|
||||
fetchedAddress = readPc()
|
||||
val lo = read(fetchedAddress)
|
||||
val hi = read((fetchedAddress+1) and 0xff)
|
||||
val fetchedAddress1 = readPc()
|
||||
val lo = read(fetchedAddress1)
|
||||
val hi = read((fetchedAddress1+1) and 0xff)
|
||||
fetchedAddress = regY+(lo or (hi shl 8)) and 0xffff
|
||||
false
|
||||
// if this address is a different page, extra clock cycle:
|
||||
(fetchedAddress and 0xff00) != hi shl 8
|
||||
}
|
||||
AddrMode.Zpr, AddrMode.Izp, AddrMode.IaX -> {
|
||||
// addressing mode used by the 65C02 only
|
||||
@ -621,7 +628,7 @@ open class Cpu6502 : BusComponent() {
|
||||
}
|
||||
|
||||
protected open fun dispatchOpcode(opcode: Int): Boolean {
|
||||
when (opcode) {
|
||||
return when (opcode) {
|
||||
0x00 -> iBrk()
|
||||
0x01 -> iOra()
|
||||
0x02 -> iInvalid()
|
||||
@ -878,15 +885,14 @@ open class Cpu6502 : BusComponent() {
|
||||
0xfd -> iSbc()
|
||||
0xfe -> iInc()
|
||||
0xff -> iIsc()
|
||||
else -> { /* can't occur */ }
|
||||
else -> false /* can't occur */
|
||||
}
|
||||
return false // TODO determine if instructions can cause extra clock cycle
|
||||
}
|
||||
|
||||
|
||||
// official instructions
|
||||
|
||||
protected open fun iAdc() {
|
||||
protected open fun iAdc(): Boolean {
|
||||
val operand = getFetched()
|
||||
if (regP.D) {
|
||||
// BCD add
|
||||
@ -916,15 +922,18 @@ open class Cpu6502 : BusComponent() {
|
||||
regP.C = tmp > 0xff
|
||||
regA = tmp and 0xff
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iAnd() {
|
||||
protected fun iAnd(): Boolean {
|
||||
regA = regA and getFetched()
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iAsl() {
|
||||
protected fun iAsl(): Boolean {
|
||||
if (currentInstruction.mode == AddrMode.Acc) {
|
||||
regP.C = (regA and 0b10000000) != 0
|
||||
regA = (regA shl 1) and 0xff
|
||||
@ -938,44 +947,82 @@ open class Cpu6502 : BusComponent() {
|
||||
regP.Z = shifted == 0
|
||||
regP.N = (shifted and 0b10000000) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBcc() {
|
||||
if (!regP.C) regPC = fetchedAddress
|
||||
protected fun iBcc(): Boolean {
|
||||
if (!regP.C) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBcs() {
|
||||
if (regP.C) regPC = fetchedAddress
|
||||
protected fun iBcs(): Boolean {
|
||||
if (regP.C) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBeq() {
|
||||
if (regP.Z) regPC = fetchedAddress
|
||||
protected fun iBeq(): Boolean {
|
||||
if (regP.Z) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun iBit() {
|
||||
protected open fun iBit(): Boolean {
|
||||
val operand = getFetched()
|
||||
regP.Z = (regA and operand) == 0
|
||||
regP.V = (operand and 0b01000000) != 0
|
||||
regP.N = (operand and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBmi() {
|
||||
if (regP.N) regPC = fetchedAddress
|
||||
protected fun iBmi(): Boolean {
|
||||
if (regP.N) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBne() {
|
||||
if (!regP.Z) regPC = fetchedAddress
|
||||
protected fun iBne(): Boolean {
|
||||
if (!regP.Z) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBpl() {
|
||||
if (!regP.N) regPC = fetchedAddress
|
||||
protected fun iBpl(): Boolean {
|
||||
if (!regP.N) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun iBrk() {
|
||||
protected open fun iBrk(): Boolean {
|
||||
// handle BRK ('software interrupt')
|
||||
regPC++
|
||||
if(nmiAsserted)
|
||||
return // if an NMI occurs during BRK, the BRK won't get executed on 6502 (65C02 fixes this)
|
||||
return false // if an NMI occurs during BRK, the BRK won't get executed on 6502 (65C02 fixes this)
|
||||
pushStackAddr(regPC)
|
||||
regP.B = true
|
||||
pushStack(regP)
|
||||
@ -984,6 +1031,7 @@ open class Cpu6502 : BusComponent() {
|
||||
regPC = readWord(IRQ_vector)
|
||||
|
||||
// TODO prevent NMI from triggering immediately after IRQ/BRK... how does that work exactly?
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun handleInterrupt() {
|
||||
@ -1008,123 +1056,154 @@ open class Cpu6502 : BusComponent() {
|
||||
// TODO prevent NMI from triggering immediately after IRQ/BRK... how does that work exactly?
|
||||
}
|
||||
|
||||
protected fun iBvc() {
|
||||
if (!regP.V) regPC = fetchedAddress
|
||||
protected fun iBvc(): Boolean {
|
||||
if (!regP.V) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iBvs() {
|
||||
if (regP.V) regPC = fetchedAddress
|
||||
protected fun iBvs(): Boolean {
|
||||
if (regP.V) {
|
||||
if(fetchedAddress and 0xff00 != regPC and 0xff00)
|
||||
instrCycles++
|
||||
regPC = fetchedAddress
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iClc() {
|
||||
protected fun iClc(): Boolean {
|
||||
regP.C = false
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iCld() {
|
||||
protected fun iCld(): Boolean {
|
||||
regP.D = false
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iCli() {
|
||||
protected fun iCli(): Boolean {
|
||||
regP.I = false
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iClv() {
|
||||
protected fun iClv(): Boolean {
|
||||
regP.V = false
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iCmp() {
|
||||
val fetched = getFetched()
|
||||
protected fun iCmp(operandOverride: Int? = null): Boolean {
|
||||
val fetched = operandOverride ?: getFetched()
|
||||
regP.C = regA >= fetched
|
||||
regP.Z = regA == fetched
|
||||
regP.N = ((regA-fetched) and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iCpx() {
|
||||
protected fun iCpx(): Boolean {
|
||||
val fetched = getFetched()
|
||||
regP.C = regX >= fetched
|
||||
regP.Z = regX == fetched
|
||||
regP.N = ((regX-fetched) and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iCpy() {
|
||||
protected fun iCpy(): Boolean {
|
||||
val fetched = getFetched()
|
||||
regP.C = regY >= fetched
|
||||
regP.Z = regY == fetched
|
||||
regP.N = ((regY-fetched) and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun iDec() {
|
||||
protected open fun iDec(): Boolean {
|
||||
val data = (read(fetchedAddress)-1) and 0xff
|
||||
write(fetchedAddress, data)
|
||||
regP.Z = data == 0
|
||||
regP.N = (data and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iDex() {
|
||||
protected fun iDex(): Boolean {
|
||||
regX = (regX-1) and 0xff
|
||||
regP.Z = regX == 0
|
||||
regP.N = (regX and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iDey() {
|
||||
protected fun iDey(): Boolean {
|
||||
regY = (regY-1) and 0xff
|
||||
regP.Z = regY == 0
|
||||
regP.N = (regY and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iEor() {
|
||||
protected fun iEor(): Boolean {
|
||||
regA = regA xor getFetched()
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected open fun iInc() {
|
||||
protected open fun iInc(): Boolean {
|
||||
val data = (read(fetchedAddress)+1) and 0xff
|
||||
write(fetchedAddress, data)
|
||||
regP.Z = data == 0
|
||||
regP.N = (data and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iInx() {
|
||||
protected fun iInx(): Boolean {
|
||||
regX = (regX+1) and 0xff
|
||||
regP.Z = regX == 0
|
||||
regP.N = (regX and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iIny() {
|
||||
protected fun iIny(): Boolean {
|
||||
regY = (regY+1) and 0xff
|
||||
regP.Z = regY == 0
|
||||
regP.N = (regY and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iJmp() {
|
||||
protected fun iJmp(): Boolean {
|
||||
regPC = fetchedAddress
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iJsr() {
|
||||
protected fun iJsr(): Boolean {
|
||||
pushStackAddr(regPC-1)
|
||||
regPC = fetchedAddress
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iLda() {
|
||||
protected fun iLda(): Boolean {
|
||||
regA = getFetched()
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iLdx() {
|
||||
protected fun iLdx(): Boolean {
|
||||
regX = getFetched()
|
||||
regP.Z = regX == 0
|
||||
regP.N = (regX and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iLdy() {
|
||||
protected fun iLdy(): Boolean {
|
||||
regY = getFetched()
|
||||
regP.Z = regY == 0
|
||||
regP.N = (regY and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iLsr() {
|
||||
protected fun iLsr(): Boolean {
|
||||
if (currentInstruction.mode == AddrMode.Acc) {
|
||||
regP.C = (regA and 1) == 1
|
||||
regA = regA ushr 1
|
||||
@ -1138,39 +1217,47 @@ open class Cpu6502 : BusComponent() {
|
||||
regP.Z = shifted == 0
|
||||
regP.N = (shifted and 0b10000000) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iNop() {}
|
||||
protected fun iNop(): Boolean {
|
||||
return currentOpcode in listOf(0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc)
|
||||
}
|
||||
|
||||
protected fun iOra() {
|
||||
protected fun iOra(): Boolean {
|
||||
regA = regA or getFetched()
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iPha() {
|
||||
protected fun iPha(): Boolean {
|
||||
pushStack(regA)
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iPhp() {
|
||||
protected fun iPhp(): Boolean {
|
||||
val origBreakflag = regP.B
|
||||
regP.B = true
|
||||
pushStack(regP)
|
||||
regP.B = origBreakflag
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iPla() {
|
||||
protected fun iPla(): Boolean {
|
||||
regA = popStack()
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iPlp() {
|
||||
protected fun iPlp(): Boolean {
|
||||
regP.fromInt(popStack())
|
||||
regP.B = true // break is always 1 except when pushing on stack
|
||||
regP.B = false
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iRol() {
|
||||
protected fun iRol(): Boolean {
|
||||
val oldCarry = regP.C
|
||||
if (currentInstruction.mode == AddrMode.Acc) {
|
||||
regP.C = (regA and 0b10000000) != 0
|
||||
@ -1185,9 +1272,10 @@ open class Cpu6502 : BusComponent() {
|
||||
regP.Z = shifted == 0
|
||||
regP.N = (shifted and 0b10000000) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iRor() {
|
||||
protected fun iRor(): Boolean {
|
||||
val oldCarry = regP.C
|
||||
if (currentInstruction.mode == AddrMode.Acc) {
|
||||
regP.C = (regA and 1) == 1
|
||||
@ -1202,21 +1290,24 @@ open class Cpu6502 : BusComponent() {
|
||||
regP.Z = shifted == 0
|
||||
regP.N = (shifted and 0b10000000) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iRti() {
|
||||
protected fun iRti(): Boolean {
|
||||
regP.fromInt(popStack())
|
||||
regP.B = true // break is always 1 except when pushing on stack
|
||||
regP.B = false
|
||||
regPC = popStackAddr()
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iRts() {
|
||||
protected fun iRts(): Boolean {
|
||||
regPC = popStackAddr()
|
||||
regPC = (regPC+1) and 0xffff
|
||||
return false
|
||||
}
|
||||
|
||||
protected open fun iSbc() {
|
||||
val operand = getFetched()
|
||||
protected open fun iSbc(operandOverride: Int? = null): Boolean {
|
||||
val operand = operandOverride ?: getFetched()
|
||||
val tmp = (regA-operand-if (regP.C) 0 else 1) and 0xffff
|
||||
regP.V = (regA xor operand) and (regA xor tmp) and 0b10000000 != 0
|
||||
if (regP.D) {
|
||||
@ -1240,142 +1331,205 @@ open class Cpu6502 : BusComponent() {
|
||||
regP.C = tmp < 0x100
|
||||
regP.Z = (tmp and 0xff) == 0
|
||||
regP.N = (tmp and 0b10000000) != 0
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
protected fun iSec() {
|
||||
protected fun iSec(): Boolean {
|
||||
regP.C = true
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iSed() {
|
||||
protected fun iSed(): Boolean {
|
||||
regP.D = true
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iSei() {
|
||||
protected fun iSei(): Boolean {
|
||||
regP.I = true
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iSta() {
|
||||
protected fun iSta(): Boolean {
|
||||
write(fetchedAddress, regA)
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iStx() {
|
||||
protected fun iStx(): Boolean {
|
||||
write(fetchedAddress, regX)
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iSty() {
|
||||
protected fun iSty(): Boolean {
|
||||
write(fetchedAddress, regY)
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iTax() {
|
||||
protected fun iTax(): Boolean {
|
||||
regX = regA
|
||||
regP.Z = regX == 0
|
||||
regP.N = (regX and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iTay() {
|
||||
protected fun iTay(): Boolean {
|
||||
regY = regA
|
||||
regP.Z = regY == 0
|
||||
regP.N = (regY and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iTsx() {
|
||||
protected fun iTsx(): Boolean {
|
||||
regX = regSP
|
||||
regP.Z = regX == 0
|
||||
regP.N = (regX and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iTxa() {
|
||||
protected fun iTxa(): Boolean {
|
||||
regA = regX
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iTxs() {
|
||||
protected fun iTxs(): Boolean {
|
||||
regSP = regX
|
||||
return false
|
||||
}
|
||||
|
||||
protected fun iTya() {
|
||||
protected fun iTya(): Boolean {
|
||||
regA = regY
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
// unofficial/illegal 6502 instructions
|
||||
// see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
|
||||
// or https://github.com/quietust/nintendulator/blob/master/src/CPU.cpp (search for LogBadOps)
|
||||
// TODO: actually implement the illegal opcodes
|
||||
|
||||
private fun iAhx() {
|
||||
TODO("\$${hexB(currentOpcode)} - ahx - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
|
||||
private fun iAhx(): Boolean {
|
||||
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
|
||||
val value = regA and regX and (addrHi+1)
|
||||
write(fetchedAddress, value)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iAlr() {
|
||||
TODO("\$${hexB(currentOpcode)} - alr=asr - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iAlr(): Boolean {
|
||||
iAnd()
|
||||
iLsr()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iAnc() {
|
||||
TODO("\$${hexB(currentOpcode)} - anc - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iAnc(): Boolean {
|
||||
iAnd()
|
||||
regP.C = regP.N
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iArr() {
|
||||
TODO("\$${hexB(currentOpcode)} - arr - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iArr(): Boolean {
|
||||
iAnd()
|
||||
iRor()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iAxs() {
|
||||
TODO("\$${hexB(currentOpcode)} - axs - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iAxs(): Boolean {
|
||||
val oldA = regA
|
||||
regA = regA and regX
|
||||
regP.C = false
|
||||
iSbc()
|
||||
regX = regA
|
||||
regA = oldA
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iDcp() {
|
||||
TODO("\$${hexB(currentOpcode)} - dcp - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iDcp(): Boolean {
|
||||
val data = (read(fetchedAddress)-1) and 0xff
|
||||
write(fetchedAddress, data)
|
||||
iCmp(data)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iIsc() {
|
||||
TODO("\$${hexB(currentOpcode)} - isc=isb - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iIsc(): Boolean {
|
||||
val data = (read(fetchedAddress)+1) and 0xff
|
||||
write(fetchedAddress, data)
|
||||
iSbc(data)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iLas() {
|
||||
TODO("\$${hexB(currentOpcode)} - las=lar - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iLas(): Boolean {
|
||||
regA = regSP and getFetched()
|
||||
regX = regA
|
||||
regSP = regA
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iLax() {
|
||||
TODO("\$${hexB(currentOpcode)} - lax - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iLax(): Boolean {
|
||||
iLda()
|
||||
regX = regA
|
||||
return true
|
||||
}
|
||||
|
||||
private fun iRla() {
|
||||
TODO("\$${hexB(currentOpcode)} - rla - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iRla(): Boolean {
|
||||
iRol()
|
||||
iAnd()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRra() {
|
||||
TODO("\$${hexB(currentOpcode)} - rra - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iRra(): Boolean {
|
||||
iRor()
|
||||
iAdc()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSax() {
|
||||
TODO("\$${hexB(currentOpcode)} - sax - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iSax(): Boolean {
|
||||
write(fetchedAddress, regA and regX)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iShx() {
|
||||
TODO("\$${hexB(currentOpcode)} - shx - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iShx(): Boolean {
|
||||
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
|
||||
write(fetchedAddress, regX and (addrHi+1))
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iShy() {
|
||||
TODO("\$${hexB(currentOpcode)} - shy - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iShy(): Boolean {
|
||||
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
|
||||
write(fetchedAddress, regY and (addrHi+1))
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSlo() {
|
||||
TODO("\$${hexB(currentOpcode)} - slo=aso - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iSlo(): Boolean {
|
||||
iAsl()
|
||||
iOra()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSre() {
|
||||
TODO("\$${hexB(currentOpcode)} - sre=lse - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iSre(): Boolean {
|
||||
iLsr()
|
||||
iEor()
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iTas() {
|
||||
TODO("\$${hexB(currentOpcode)} - tas - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iTas(): Boolean {
|
||||
regSP = regA and regX
|
||||
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)
|
||||
write(fetchedAddress, regSP and addrHi)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iXaa() {
|
||||
TODO("\$${hexB(currentOpcode)} - xaa - ('illegal' instruction) @ \$${hexW(currentOpcodeAddress)}")
|
||||
private fun iXaa(): Boolean {
|
||||
regA = (regX and fetchedData)
|
||||
return false
|
||||
}
|
||||
|
||||
// invalid instruction (JAM / KIL / HLT)
|
||||
private fun iInvalid() {
|
||||
private fun iInvalid(): Boolean {
|
||||
throw InstructionError("invalid instruction encountered: opcode=${hexB(currentOpcode)} instr=${currentInstruction.mnemonic} @ ${hexW(currentOpcodeAddress)}")
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ class Cpu65C02 : Cpu6502() {
|
||||
}
|
||||
|
||||
override fun dispatchOpcode(opcode: Int): Boolean {
|
||||
when (opcode) {
|
||||
return when (opcode) {
|
||||
0x00 -> iBrk()
|
||||
0x01 -> iOra()
|
||||
0x02 -> iNop()
|
||||
@ -360,9 +360,8 @@ class Cpu65C02 : Cpu6502() {
|
||||
0xfd -> iSbc()
|
||||
0xfe -> iInc()
|
||||
0xff -> iBbs7()
|
||||
else -> { /* can't occur */ }
|
||||
else -> false /* can't occur */
|
||||
}
|
||||
return false // TODO determine if instructions can cause extra clock cycle
|
||||
}
|
||||
|
||||
// opcode list: http://www.oxyron.de/html/opcodesc02.html
|
||||
@ -624,7 +623,7 @@ class Cpu65C02 : Cpu6502() {
|
||||
/* fe */ Instruction("inc", AddrMode.AbsX, 7),
|
||||
/* ff */ Instruction("bbs7", AddrMode.Zpr, 5)).toTypedArray()
|
||||
|
||||
override fun iBrk() {
|
||||
override fun iBrk(): Boolean {
|
||||
// handle BRK ('software interrupt')
|
||||
regPC++
|
||||
pushStackAddr(regPC)
|
||||
@ -635,6 +634,7 @@ class Cpu65C02 : Cpu6502() {
|
||||
regPC = readWord(IRQ_vector)
|
||||
|
||||
// TODO prevent NMI from triggering immediately after IRQ/BRK... how does that work exactly?
|
||||
return false
|
||||
}
|
||||
|
||||
override fun handleInterrupt() {
|
||||
@ -642,16 +642,17 @@ class Cpu65C02 : Cpu6502() {
|
||||
regP.D = false // this is different from NMOS 6502
|
||||
}
|
||||
|
||||
override fun iBit() {
|
||||
override fun iBit(): Boolean {
|
||||
val data = getFetched()
|
||||
regP.Z = (regA and data) == 0
|
||||
if (currentInstruction.mode != AddrMode.Imm) {
|
||||
regP.V = (data and 0b01000000) != 0
|
||||
regP.N = (data and 0b10000000) != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun iAdc() {
|
||||
override fun iAdc(): Boolean {
|
||||
val value = getFetched()
|
||||
if (regP.D) {
|
||||
// BCD add
|
||||
@ -680,13 +681,14 @@ class Cpu65C02 : Cpu6502() {
|
||||
regP.C = tmp > 0xff
|
||||
regA = tmp and 0xff
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun iSbc() {
|
||||
override fun iSbc(operandOverride: Int?): Boolean {
|
||||
// see http://www.6502.org/tutorials/decimal_mode.html
|
||||
// and https://sourceforge.net/p/vice-emu/code/HEAD/tree/trunk/vice/src/65c02core.c#l1205
|
||||
// (the implementation below is based on the code used by Vice)
|
||||
val value = getFetched()
|
||||
val value = operandOverride ?: getFetched()
|
||||
var tmp = (regA-value-if (regP.C) 0 else 1) and 0xffff
|
||||
regP.V = (regA xor tmp) and (regA xor value) and 0b10000000 != 0
|
||||
if (regP.D) {
|
||||
@ -698,230 +700,323 @@ class Cpu65C02 : Cpu6502() {
|
||||
regP.Z = (tmp and 0xff) == 0
|
||||
regP.N = (tmp and 0b10000000) != 0
|
||||
regA = tmp and 0xff
|
||||
return false
|
||||
}
|
||||
|
||||
override fun iDec() {
|
||||
if (currentInstruction.mode == AddrMode.Acc) {
|
||||
override fun iDec(): Boolean {
|
||||
return if (currentInstruction.mode == AddrMode.Acc) {
|
||||
regA = (regA-1) and 0xff
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
false
|
||||
} else super.iDec()
|
||||
}
|
||||
|
||||
override fun iInc() {
|
||||
if (currentInstruction.mode == AddrMode.Acc) {
|
||||
override fun iInc(): Boolean {
|
||||
return if (currentInstruction.mode == AddrMode.Acc) {
|
||||
regA = (regA+1) and 0xff
|
||||
regP.Z = regA == 0
|
||||
regP.N = (regA and 0b10000000) != 0
|
||||
false
|
||||
} else super.iInc()
|
||||
}
|
||||
|
||||
private fun iBra() {
|
||||
private fun iBra(): Boolean {
|
||||
// unconditional branch
|
||||
regPC = fetchedAddress
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iTrb() {
|
||||
private fun iTrb(): Boolean {
|
||||
val data = getFetched()
|
||||
regP.Z = data and regA == 0
|
||||
write(fetchedAddress, data and regA.inv())
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iTsb() {
|
||||
private fun iTsb(): Boolean {
|
||||
val data = getFetched()
|
||||
regP.Z = data and regA == 0
|
||||
write(fetchedAddress, data or regA)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iStz() {
|
||||
private fun iStz(): Boolean {
|
||||
write(fetchedAddress, 0)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iWai() {
|
||||
private fun iWai(): Boolean {
|
||||
waiting = Wait.Waiting
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iStp() {
|
||||
private fun iStp(): Boolean {
|
||||
waiting = Wait.Stopped
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iPhx() {
|
||||
private fun iPhx(): Boolean {
|
||||
pushStack(regX)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iPlx() {
|
||||
private fun iPlx(): Boolean {
|
||||
regX = popStack()
|
||||
regP.Z = regX == 0
|
||||
regP.N = (regX and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iPhy() {
|
||||
private fun iPhy(): Boolean {
|
||||
pushStack(regY)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iPly() {
|
||||
private fun iPly(): Boolean {
|
||||
regY = popStack()
|
||||
regP.Z = regY == 0
|
||||
regP.N = (regY and 0b10000000) != 0
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr0() {
|
||||
private fun iBbr0(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 1 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 1 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr1() {
|
||||
private fun iBbr1(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 2 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 2 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr2() {
|
||||
private fun iBbr2(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 4 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 4 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr3() {
|
||||
private fun iBbr3(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 8 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 8 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr4() {
|
||||
private fun iBbr4(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 16 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 16 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr5() {
|
||||
private fun iBbr5(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 32 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 32 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr6() {
|
||||
private fun iBbr6(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 64 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 64 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbr7() {
|
||||
private fun iBbr7(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 128 == 0) regPC = fetchedAddressZpr
|
||||
if (data and 128 == 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs0() {
|
||||
private fun iBbs0(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 1 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 1 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs1() {
|
||||
private fun iBbs1(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 2 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 2 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs2() {
|
||||
private fun iBbs2(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 4 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 4 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs3() {
|
||||
private fun iBbs3(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 8 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 8 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs4() {
|
||||
private fun iBbs4(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 16 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 16 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs5() {
|
||||
private fun iBbs5(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 32 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 32 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs6() {
|
||||
private fun iBbs6(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 64 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 64 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iBbs7() {
|
||||
private fun iBbs7(): Boolean {
|
||||
val data = getFetched()
|
||||
if (data and 128 != 0) regPC = fetchedAddressZpr
|
||||
if (data and 128 != 0) {
|
||||
regPC = fetchedAddressZpr
|
||||
instrCycles++
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb0() {
|
||||
private fun iSmb0(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 1)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb1() {
|
||||
private fun iSmb1(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 2)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb2() {
|
||||
private fun iSmb2(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 4)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb3() {
|
||||
private fun iSmb3(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 8)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb4() {
|
||||
private fun iSmb4(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 16)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb5() {
|
||||
private fun iSmb5(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 32)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb6() {
|
||||
private fun iSmb6(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 64)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iSmb7() {
|
||||
private fun iSmb7(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data or 128)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb0() {
|
||||
private fun iRmb0(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b11111110)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb1() {
|
||||
private fun iRmb1(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b11111101)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb2() {
|
||||
private fun iRmb2(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b11111011)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb3() {
|
||||
private fun iRmb3(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b11110111)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb4() {
|
||||
private fun iRmb4(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b11101111)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb5() {
|
||||
private fun iRmb5(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b11011111)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb6() {
|
||||
private fun iRmb6(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b10111111)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun iRmb7() {
|
||||
private fun iRmb7(): Boolean {
|
||||
val data = getFetched()
|
||||
write(fetchedAddress, data and 0b01111111)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -72,13 +72,4 @@ abstract class FunctionalTestsBase {
|
||||
}
|
||||
fail("test failed")
|
||||
}
|
||||
|
||||
protected fun runTestExpectNotImplemented(testprogram: String) {
|
||||
try {
|
||||
runTest(testprogram)
|
||||
fail("expected to crash with NotImplementedError")
|
||||
} catch(nx: NotImplementedError) {
|
||||
// okay!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +230,24 @@ class Test6502CpuBasics {
|
||||
totalCycles = cycles
|
||||
instrCycles = 0
|
||||
}
|
||||
|
||||
override fun iAdc(): Boolean {
|
||||
// NES cpu doesn't have BCD mode
|
||||
val decimal = regP.D
|
||||
regP.D = false
|
||||
val result = super.iAdc()
|
||||
regP.D = decimal
|
||||
return result
|
||||
}
|
||||
|
||||
override fun iSbc(operandOverride: Int?): Boolean {
|
||||
// NES cpu doesn't have BCD mode
|
||||
val decimal = regP.D
|
||||
regP.D = false
|
||||
val result = super.iSbc(operandOverride)
|
||||
regP.D = decimal
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
val cpu = NesCpu()
|
||||
@ -245,21 +263,23 @@ class Test6502CpuBasics {
|
||||
bus.reset()
|
||||
cpu.resetTotalCycles(7) // that is what the nes rom starts with
|
||||
cpu.regPC = 0xc000
|
||||
var tracingSnapshot = cpu.snapshot()
|
||||
cpu.tracing = { tracingSnapshot=it }
|
||||
|
||||
val neslog = javaClass.getResource("nestest.log").readText().lineSequence()
|
||||
for(logline in neslog) {
|
||||
val s = cpu.snapshot() // TODO use cpu.tracing instead
|
||||
if(logline.isEmpty())
|
||||
break
|
||||
|
||||
cpu.step()
|
||||
|
||||
val nesAddressHex = logline.substring(0, 4).toInt(16)
|
||||
assertEquals(nesAddressHex, s.PC)
|
||||
assertEquals(nesAddressHex, tracingSnapshot.PC)
|
||||
|
||||
println("NES: $logline")
|
||||
val disassem = disassembler.disassembleOneInstruction(ram.data, s.PC, 0).first.substring(1)
|
||||
val spaces = " ".substring(disassem.length-1)
|
||||
println("EMU: $disassem $spaces A:${hexB(s.A)} X:${hexB(s.X)} Y:${hexB(s.Y)} P:${hexB(s.P.asInt())} SP:${hexB(s.SP)} PPU: 0, 0 CYC:${s.cycles}")
|
||||
|
||||
// TODO use cpu.tracing, as per https://forums.nesdev.com/viewtopic.php?t=19117 (i.e. BEFORE instruction gets executed):
|
||||
// "before fetching the first operation code byte, make an internal record of the program counter and other registers;
|
||||
// after reading the final operand byte, log all the values you stored back in step (1) plus the full instruction and its disassembly."
|
||||
// println("NES: $logline")
|
||||
// val disassem = disassembler.disassembleOneInstruction(ram.data, tracingSnapshot.PC, 0).first.substring(1)
|
||||
// val spaces = " ".substring(disassem.length-1)
|
||||
// println("EMU: $disassem $spaces A:${hexB(tracingSnapshot.A)} X:${hexB(tracingSnapshot.X)} Y:${hexB(tracingSnapshot.Y)} P:${hexB(tracingSnapshot.P.asInt())} SP:${hexB(tracingSnapshot.SP)} PPU: 0, 0 CYC:${tracingSnapshot.cycles}")
|
||||
|
||||
val nesRegsLog = logline.substring(48).split(':')
|
||||
val nesA = nesRegsLog[1].substring(0, 2).toShort(16)
|
||||
@ -268,17 +288,16 @@ class Test6502CpuBasics {
|
||||
val nesP = nesRegsLog[4].substring(0, 2).toInt(16)
|
||||
val nesSP = nesRegsLog[5].substring(0, 2).toInt(16)
|
||||
val nesCycles = nesRegsLog[7].toLong()
|
||||
assertEquals(nesA, s.A)
|
||||
assertEquals(nesX, s.X)
|
||||
assertEquals(nesY, s.Y)
|
||||
assertEquals(nesP, s.P.asInt())
|
||||
assertEquals(nesSP, s.SP)
|
||||
assertEquals(nesCycles, s.cycles)
|
||||
cpu.step()
|
||||
assertEquals(nesA, tracingSnapshot.A)
|
||||
assertEquals(nesX, tracingSnapshot.X)
|
||||
assertEquals(nesY, tracingSnapshot.Y)
|
||||
assertEquals(nesP, tracingSnapshot.P.asInt())
|
||||
assertEquals(nesSP, tracingSnapshot.SP)
|
||||
assertEquals(nesCycles, tracingSnapshot.cycles)
|
||||
}
|
||||
|
||||
// TODO test $02 and $03 for test results see http://www.qmtpro.com/~nes/misc/nestest.txt
|
||||
|
||||
fail("todo: test success condition")
|
||||
val two = ram[0x02]
|
||||
val three = ram[0x03]
|
||||
assertEquals(0, two, "test failed, code ${hexB(two)} ${hexB(three)}")
|
||||
}
|
||||
}
|
||||
|
@ -182,10 +182,11 @@ class Test6502TestSuiteC64Specific {
|
||||
runTest("cnto2")
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled("tests c64 specific hardware")
|
||||
fun testCputiming() {
|
||||
runTest("cputiming") // TODO fix this test once the cycle times are correct?
|
||||
runTest("cputiming")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2,328 +2,328 @@ import org.junit.jupiter.api.parallel.Execution
|
||||
import org.junit.jupiter.api.parallel.ExecutionMode
|
||||
import kotlin.test.*
|
||||
|
||||
// TODO: implement the illegal instructions and replace these tests with the 'real' runTest
|
||||
// TODO: implement the still missing illegal instructions and replace these tests with the 'real' runTest
|
||||
|
||||
@Execution(ExecutionMode.CONCURRENT)
|
||||
class Test6502TestSuiteIllegalInstructions: FunctionalTestsBase() {
|
||||
|
||||
@Test
|
||||
fun testAlrb() {
|
||||
runTestExpectNotImplemented("alrb")
|
||||
runTest("alrb") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAncb() {
|
||||
runTestExpectNotImplemented("ancb")
|
||||
runTest("ancb")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAneb() {
|
||||
runTestExpectNotImplemented("aneb")
|
||||
runTest("aneb") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testArrb() {
|
||||
runTestExpectNotImplemented("arrb")
|
||||
runTest("arrb") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsoa() {
|
||||
runTestExpectNotImplemented("asoa")
|
||||
runTest("asoa")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsoax() {
|
||||
runTestExpectNotImplemented("asoax")
|
||||
runTest("asoax")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsoay() {
|
||||
runTestExpectNotImplemented("asoay")
|
||||
runTest("asoay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsoix() {
|
||||
runTestExpectNotImplemented("asoix")
|
||||
runTest("asoix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsoiy() {
|
||||
runTestExpectNotImplemented("asoiy")
|
||||
runTest("asoiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsoz() {
|
||||
runTestExpectNotImplemented("asoz")
|
||||
runTest("asoz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAsozx() {
|
||||
runTestExpectNotImplemented("asozx")
|
||||
runTest("asozx")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAxsa() {
|
||||
runTestExpectNotImplemented("axsa")
|
||||
runTest("axsa")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAxsix() {
|
||||
runTestExpectNotImplemented("axsix")
|
||||
runTest("axsix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAxsz() {
|
||||
runTestExpectNotImplemented("axsz")
|
||||
runTest("axsz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAxszy() {
|
||||
runTestExpectNotImplemented("axszy")
|
||||
runTest("axszy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcma() {
|
||||
runTestExpectNotImplemented("dcma")
|
||||
runTest("dcma")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcmax() {
|
||||
runTestExpectNotImplemented("dcmax")
|
||||
runTest("dcmax")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcmay() {
|
||||
runTestExpectNotImplemented("dcmay")
|
||||
runTest("dcmay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcmix() {
|
||||
runTestExpectNotImplemented("dcmix")
|
||||
runTest("dcmix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcmiy() {
|
||||
runTestExpectNotImplemented("dcmiy")
|
||||
runTest("dcmiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcmz() {
|
||||
runTestExpectNotImplemented("dcmz")
|
||||
runTest("dcmz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDcmzx() {
|
||||
runTestExpectNotImplemented("dcmzx")
|
||||
runTest("dcmzx")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInsa() {
|
||||
runTestExpectNotImplemented("insa")
|
||||
runTest("insa")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInsax() {
|
||||
runTestExpectNotImplemented("insax")
|
||||
runTest("insax")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInsay() {
|
||||
runTestExpectNotImplemented("insay")
|
||||
runTest("insay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInsix() {
|
||||
runTestExpectNotImplemented("insix")
|
||||
runTest("insix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInsiy() {
|
||||
runTestExpectNotImplemented("insiy")
|
||||
runTest("insiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInsz() {
|
||||
runTestExpectNotImplemented("insz")
|
||||
runTest("insz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInszx() {
|
||||
runTestExpectNotImplemented("inszx")
|
||||
runTest("inszx")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLasay() {
|
||||
runTestExpectNotImplemented("lasay")
|
||||
runTest("lasay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaxa() {
|
||||
runTestExpectNotImplemented("laxa")
|
||||
runTest("laxa")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaxay() {
|
||||
runTestExpectNotImplemented("laxay")
|
||||
runTest("laxay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaxix() {
|
||||
runTestExpectNotImplemented("laxix")
|
||||
runTest("laxix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaxiy() {
|
||||
runTestExpectNotImplemented("laxiy")
|
||||
runTest("laxiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaxz() {
|
||||
runTestExpectNotImplemented("laxz")
|
||||
runTest("laxz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLaxzy() {
|
||||
runTestExpectNotImplemented("laxzy")
|
||||
runTest("laxzy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLsea() {
|
||||
runTestExpectNotImplemented("lsea")
|
||||
runTest("lsea")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLseax() {
|
||||
runTestExpectNotImplemented("lseax")
|
||||
runTest("lseax")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLseay() {
|
||||
runTestExpectNotImplemented("lseay")
|
||||
runTest("lseay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLseix() {
|
||||
runTestExpectNotImplemented("lseix")
|
||||
runTest("lseix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLseiy() {
|
||||
runTestExpectNotImplemented("lseiy")
|
||||
runTest("lseiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLsez() {
|
||||
runTestExpectNotImplemented("lsez")
|
||||
runTest("lsez")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLsezx() {
|
||||
runTestExpectNotImplemented("lsezx")
|
||||
runTest("lsezx")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLxab() {
|
||||
runTestExpectNotImplemented("lxab")
|
||||
runTest("lxab") // TODO fix something?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlaa() {
|
||||
runTestExpectNotImplemented("rlaa")
|
||||
runTest("rlaa")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlaax() {
|
||||
runTestExpectNotImplemented("rlaax")
|
||||
runTest("rlaax")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlaay() {
|
||||
runTestExpectNotImplemented("rlaay")
|
||||
runTest("rlaay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlaix() {
|
||||
runTestExpectNotImplemented("rlaix")
|
||||
runTest("rlaix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlaiy() {
|
||||
runTestExpectNotImplemented("rlaiy")
|
||||
runTest("rlaiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlaz() {
|
||||
runTestExpectNotImplemented("rlaz")
|
||||
runTest("rlaz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRlazx() {
|
||||
runTestExpectNotImplemented("rlazx")
|
||||
runTest("rlazx")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRraa() {
|
||||
runTestExpectNotImplemented("rraa")
|
||||
runTest("rraa")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRraax() {
|
||||
runTestExpectNotImplemented("rraax")
|
||||
runTest("rraax")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRraay() {
|
||||
runTestExpectNotImplemented("rraay")
|
||||
runTest("rraay")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRraix() {
|
||||
runTestExpectNotImplemented("rraix")
|
||||
runTest("rraix")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRraiy() {
|
||||
runTestExpectNotImplemented("rraiy")
|
||||
runTest("rraiy")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRraz() {
|
||||
runTestExpectNotImplemented("rraz")
|
||||
runTest("rraz")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRrazx() {
|
||||
runTestExpectNotImplemented("rrazx")
|
||||
runTest("rrazx")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSbxb() {
|
||||
runTestExpectNotImplemented("sbxb")
|
||||
runTest("sbxb") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShaay() {
|
||||
runTestExpectNotImplemented("shaay")
|
||||
runTest("shaay") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShaiy() {
|
||||
runTestExpectNotImplemented("shaiy")
|
||||
runTest("shaiy") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShsay() {
|
||||
runTestExpectNotImplemented("shsay")
|
||||
runTest("shsay") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShxay() {
|
||||
runTestExpectNotImplemented("shxay")
|
||||
runTest("shxay") // TODO fix?
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testShyax() {
|
||||
runTestExpectNotImplemented("shyax")
|
||||
runTest("shyax") // TODO fix?
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user