From 42f8e98caba519a7519103afee81bf4d399e403a Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Wed, 4 Sep 2019 22:23:31 +0200 Subject: [PATCH] cpu unit test suite ported from Py65 --- sim65/src/components/Cpu6502.kt | 350 +- sim65/src/components/Ram.kt | 3 + sim65/test/Test6502.kt | 321 + sim65/test/Test6502CpuBasics.kt | 25 + sim65/test/Test65C02.kt | 1547 ++++ sim65/test/TestCommon6502.kt | 6866 +++++++++++++++++ .../{DisassemTest.kt => TestDisassembler.kt} | 6 +- 7 files changed, 8950 insertions(+), 168 deletions(-) create mode 100644 sim65/test/Test6502.kt create mode 100644 sim65/test/Test6502CpuBasics.kt create mode 100644 sim65/test/Test65C02.kt create mode 100644 sim65/test/TestCommon6502.kt rename sim65/test/{DisassemTest.kt => TestDisassembler.kt} (87%) diff --git a/sim65/src/components/Cpu6502.kt b/sim65/src/components/Cpu6502.kt index 121fd1beb..961e73adb 100644 --- a/sim65/src/components/Cpu6502.kt +++ b/sim65/src/components/Cpu6502.kt @@ -8,9 +8,9 @@ interface ICpu { fun disassemble(component: MemMappedComponent, from: Address, to: Address) = disassemble(component.cloneMem(), component.startAddress, from, to) - fun dumpState() fun clock() fun reset() + fun step() var tracing: Boolean val totalCycles: Int @@ -98,17 +98,30 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu 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() + } } - private var instrCycles: Int = 0 - private var A: Int = 0 - private var X: Int = 0 - private var Y: Int = 0 - private var sp: Int = 0 - private var pc: Address = 0 - private val status = StatusRegister() - private var currentOpcode: Int = 0 + var instrCycles: Int = 0 + var A: Int = 0 + var X: Int = 0 + var Y: Int = 0 + var SP: Int = 0 + var PC: Address = 0 + val Status = StatusRegister() + var currentOpcode: Int = 0 + var waiting: Boolean = false // 65c02 private lateinit var currentInstruction: Instruction // data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied) @@ -221,30 +234,31 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } override fun reset() { - sp = 0xfd - pc = readWord(RESET_vector) + SP = 0xfd + PC = readWord(RESET_vector) A = 0 X = 0 Y = 0 - status.C = false - status.Z = false - status.I = false - status.D = false - status.B = false - status.V = false - status.N = false + Status.C = false + Status.Z = false + Status.I = false + Status.D = false + Status.B = false + Status.V = false + Status.N = false instrCycles = 8 currentOpcode = 0 currentInstruction = opcodes[0] + waiting = false } override fun clock() { if (instrCycles == 0) { - currentOpcode = read(pc++) + currentOpcode = read(PC++) currentInstruction = opcodes[currentOpcode] if (tracing) { - dumpState() + printState() } if (!currentInstruction.official && !illegalInstrsAllowed) { @@ -260,19 +274,25 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu totalCycles++ } - override fun dumpState() { - println("cycle:$totalCycles - pc=${hexW(pc)} " + + override fun step() { + // step a whole instruction + while(instrCycles>0) clock() // instruction subcycles + clock() // the actual instruction execution cycle + } + + fun printState() { + println("cycle:$totalCycles - pc=${hexW(PC)} " + "A=${hexB(A)} " + "X=${hexB(X)} " + "Y=${hexB(Y)} " + - "SP=${hexB(sp)} " + - " n=" + (if (status.N) "1" else "0") + - " v=" + (if (status.V) "1" else "0") + - " b=" + (if (status.B) "1" else "0") + - " d=" + (if (status.D) "1" else "0") + - " i=" + (if (status.I) "1" else "0") + - " z=" + (if (status.Z) "1" else "0") + - " c=" + (if (status.C) "1" else "0") + + "SP=${hexB(SP)} " + + " n=" + (if (Status.N) "1" else "0") + + " v=" + (if (Status.V) "1" else "0") + + " b=" + (if (Status.B) "1" else "0") + + " d=" + (if (Status.D) "1" else "0") + + " i=" + (if (Status.I) "1" else "0") + + " z=" + (if (Status.Z) "1" else "0") + + " c=" + (if (Status.C) "1" else "0") + " icycles=$instrCycles instr=${hexB(currentOpcode)}:${currentInstruction.mnemonic}" ) } @@ -306,9 +326,9 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu private fun amRel() { val relative = readPc().toByte() fetchedAddress = if (relative >= 0x80) - pc - (256 - relative) + PC - (256 - relative) else - pc + relative + PC + relative } private fun amAbs() { @@ -362,7 +382,7 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu fetchedAddress = Y + (lo or (hi shl 8)) } - private fun readPc(): Int = bus.read(pc++).toInt() + private fun readPc(): Int = bus.read(PC++).toInt() private fun pushStackAddr(address: Address) { val lo = address and 255 @@ -376,13 +396,13 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } private fun pushStack(data: Int) { - write(sp or 0x0100, data) - sp = (sp - 1) and 0xff + write(SP or 0x0100, data) + SP = (SP - 1) and 0xff } private fun popStack(): Int { - sp = (sp + 1) and 0xff - return read(sp or 0x0100) + SP = (SP + 1) and 0xff + return read(SP or 0x0100) } private fun popStackAddr(): Address { @@ -668,113 +688,113 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } else { read(fetchedAddress) } - if (status.D) { + if (Status.D) { // BCD add - var lo = (A and 0x0f) + (operand and 0x0f) + if (status.C) 1 else 0 + var lo = (A and 0x0f) + (operand and 0x0f) + if (Status.C) 1 else 0 if (lo and 0xff > 9) lo += 6 var hi = (A shr 4) + (operand shr 4) + if (lo > 15) 1 else 0 if (hi and 0xff > 9) hi += 6 var result = lo and 0x0f or (hi shl 4) result = result and 0xff - status.C = hi > 15 - status.Z = result == 0 - status.V = false // BCD never sets overflow flag - status.N = false // BCD is never negative on NMOS 6502 (bug) + Status.C = hi > 15 + Status.Z = result == 0 + Status.V = false // BCD never sets overflow flag + Status.N = false // BCD is never negative on NMOS 6502 (bug) } else { // normal add - val result = A + operand + if (status.C) 1 else 0 - status.C = result > 255 - status.V = (A xor operand).inv() and (A xor result) and 0x0080 != 0 + val result = A + operand + if (Status.C) 1 else 0 + Status.C = result > 255 + Status.V = (A xor operand).inv() and (A xor result) and 0x0080 != 0 A = result and 255 - status.N = (A and 0b10000000) != 0 - status.Z = A == 0 + Status.N = (A and 0b10000000) != 0 + Status.Z = A == 0 } } private fun iAnd() { A = A and fetchedData - status.Z = A == 0 - status.N = (A and 0b10000000) != 0 + Status.Z = A == 0 + Status.N = (A and 0b10000000) != 0 } private fun iAsl() { if (currentInstruction.mode == AddrMode.Acc) { - status.C = (A and 0b10000000) == 1 + Status.C = (A and 0b10000000) == 1 A = (A shl 1) and 255 - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } else { val data = read(fetchedAddress) - status.C = (data and 0b10000000) == 1 + Status.C = (data and 0b10000000) == 1 val shifted = (data shl 1) and 255 write(fetchedAddress, shifted) - status.Z = shifted == 0 - status.N = (shifted and 0b10000000) == 1 + Status.Z = shifted == 0 + Status.N = (shifted and 0b10000000) == 1 } } private fun iBcc() { - if (!status.C) pc = fetchedAddress + if (!Status.C) PC = fetchedAddress } private fun iBcs() { - if (status.C) pc = fetchedAddress + if (Status.C) PC = fetchedAddress } private fun iBeq() { - if (status.Z) pc = fetchedAddress + if (Status.Z) PC = fetchedAddress } private fun iBit() { - status.Z = (A and fetchedData) == 0 - status.V = (fetchedData and 0b01000000) != 0 - status.N = (fetchedData and 0b10000000) != 0 + Status.Z = (A and fetchedData) == 0 + Status.V = (fetchedData and 0b01000000) != 0 + Status.N = (fetchedData and 0b10000000) != 0 } private fun iBmi() { - if (status.N) pc = fetchedAddress + if (Status.N) PC = fetchedAddress } private fun iBne() { - if (!status.Z) pc = fetchedAddress + if (!Status.Z) PC = fetchedAddress } private fun iBpl() { - if (!status.N) pc = fetchedAddress + if (!Status.N) PC = fetchedAddress } private fun iBrk() { - pc++ - status.I = true - status.B = true - pushStackAddr(pc) - pushStack(status) - status.B = false - pc = readWord(IRQ_vector) + PC++ + Status.I = true + Status.B = true + pushStackAddr(PC) + pushStack(Status) + Status.B = false + PC = readWord(IRQ_vector) } private fun iBvc() { - if (!status.V) pc = fetchedAddress + if (!Status.V) PC = fetchedAddress } private fun iBvs() { - if (status.V) pc = fetchedAddress + if (Status.V) PC = fetchedAddress } private fun iClc() { - status.C = false + Status.C = false } private fun iCld() { - status.D = false + Status.D = false } private fun iCli() { - status.I = false + Status.I = false } private fun iClv() { - status.V = false + Status.V = false } private fun iCmp() { @@ -784,9 +804,9 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } else { read(fetchedAddress) } - status.C = A >= fetched - status.Z = A == fetched - status.N = ((A - fetched) and 0b10000000) == 1 + Status.C = A >= fetched + Status.Z = A == fetched + Status.N = ((A - fetched) and 0b10000000) == 1 } private fun iCpx() { @@ -796,9 +816,9 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } else { read(fetchedAddress) } - status.C = X >= fetched - status.Z = X == fetched - status.N = ((X - fetched) and 0b10000000) == 1 + Status.C = X >= fetched + Status.Z = X == fetched + Status.N = ((X - fetched) and 0b10000000) == 1 } private fun iCpy() { @@ -808,28 +828,28 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } else { read(fetchedAddress) } - status.C = Y >= fetched - status.Z = Y == fetched - status.N = ((Y - fetched) and 0b10000000) == 1 + Status.C = Y >= fetched + Status.Z = Y == fetched + Status.N = ((Y - fetched) and 0b10000000) == 1 } private fun iDec() { val data = (read(fetchedAddress) - 1) and 255 write(fetchedAddress, data) - status.Z = data == 0 - status.N = (data and 0b10000000) == 1 + Status.Z = data == 0 + Status.N = (data and 0b10000000) == 1 } private fun iDex() { X = (X - 1) and 255 - status.Z = X == 0 - status.N = (X and 0b10000000) == 1 + Status.Z = X == 0 + Status.N = (X and 0b10000000) == 1 } private fun iDey() { Y = (Y - 1) and 255 - status.Z = Y == 0 - status.N = (Y and 0b10000000) == 1 + Status.Z = Y == 0 + Status.N = (Y and 0b10000000) == 1 } private fun iEor() { @@ -838,36 +858,36 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } else { A xor read(fetchedAddress) } - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } private fun iInc() { val data = (read(fetchedAddress) + 1) and 255 write(fetchedAddress, data) - status.Z = data == 0 - status.N = (data and 0b10000000) == 1 + Status.Z = data == 0 + Status.N = (data and 0b10000000) == 1 } private fun iInx() { X = (X + 1) and 255 - status.Z = X == 0 - status.N = (X and 0b10000000) == 1 + Status.Z = X == 0 + Status.N = (X and 0b10000000) == 1 } private fun iIny() { Y = (Y + 1) and 255 - status.Z = Y == 0 - status.N = (Y and 0b10000000) == 1 + Status.Z = Y == 0 + Status.N = (Y and 0b10000000) == 1 } private fun iJmp() { - pc = fetchedAddress + PC = fetchedAddress } private fun iJsr() { - pushStackAddr(pc) - pc = fetchedAddress + pushStackAddr(PC) + PC = fetchedAddress } private fun iLda() { @@ -875,8 +895,8 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu fetchedData else read(fetchedAddress) - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } private fun iLdx() { @@ -884,8 +904,8 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu fetchedData else read(fetchedAddress) - status.Z = X == 0 - status.N = (X and 0b10000000) == 1 + Status.Z = X == 0 + Status.N = (X and 0b10000000) == 1 } private fun iLdy() { @@ -893,23 +913,23 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu fetchedData else read(fetchedAddress) - status.Z = Y == 0 - status.N = (Y and 0b10000000) == 1 + Status.Z = Y == 0 + Status.N = (Y and 0b10000000) == 1 } private fun iLsr() { if (currentInstruction.mode == AddrMode.Acc) { - status.C = (A and 1) == 1 + Status.C = (A and 1) == 1 A = A ushr 1 - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } else { val data = read(fetchedAddress) - status.C = (data and 1) == 1 + Status.C = (data and 1) == 1 val shifted = data ushr 1 write(fetchedAddress, shifted) - status.Z = shifted == 0 - status.N = (shifted and 0b10000000) == 1 + Status.Z = shifted == 0 + Status.N = (shifted and 0b10000000) == 1 } } @@ -920,8 +940,8 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu A or fetchedData else A or read(fetchedAddress) - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } private fun iPha() { @@ -929,60 +949,60 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } private fun iPhp() { - pushStack(status) + pushStack(Status) } private fun iPla() { A = popStack() - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } private fun iPlp() { - status.fromByte(popStack()) + Status.fromByte(popStack()) } private fun iRol() { - val oldCarry = status.C + val oldCarry = Status.C if (currentInstruction.mode == AddrMode.Acc) { - status.C = (A and 0b10000000) == 1 + Status.C = (A and 0b10000000) == 1 A = (A shl 1) or (if (oldCarry) 1 else 0) - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } else { val data = read(fetchedAddress) - status.C = (data and 0b10000000) == 1 + Status.C = (data and 0b10000000) == 1 val shifted = (data shl 1) or (if (oldCarry) 1 else 0) and 255 write(fetchedAddress, shifted) - status.Z = shifted == 0 - status.N = (shifted and 0b10000000) == 1 + Status.Z = shifted == 0 + Status.N = (shifted and 0b10000000) == 1 } } private fun iRor() { - val oldCarry = status.C + val oldCarry = Status.C if (currentInstruction.mode == AddrMode.Acc) { - status.C = (A and 1) == 1 + Status.C = (A and 1) == 1 A = (A ushr 1) or (if (oldCarry) 0b10000000 else 0) - status.Z = A == 0 - status.N = (A and 0b10000000) == 1 + Status.Z = A == 0 + Status.N = (A and 0b10000000) == 1 } else { val data = read(fetchedAddress) - status.C = (data and 1) == 1 + Status.C = (data and 1) == 1 val shifted = (data ushr 1) or (if (oldCarry) 0b10000000 else 0) write(fetchedAddress, shifted) - status.Z = shifted == 0 - status.N = (shifted and 0b10000000) == 1 + Status.Z = shifted == 0 + Status.N = (shifted and 0b10000000) == 1 } } private fun iRti() { - status.fromByte(popStack()) - pc = popStackAddr() + Status.fromByte(popStack()) + PC = popStackAddr() } private fun iRts() { - pc = popStackAddr() + PC = popStackAddr() } private fun iSbc() { @@ -991,39 +1011,39 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu } else { read(fetchedAddress) } - if (status.D) { - var lo = (A and 0x0f) - (operand and 0x0f) - if (status.C) 0 else 1 + if (Status.D) { + var lo = (A and 0x0f) - (operand and 0x0f) - if (Status.C) 0 else 1 if (lo and 0x10 != 0) lo -= 6 var h = (A shr 4) - (operand shr 4) - if (lo and 0x10 != 0) 1 else 0 if (h and 0x10 != 0) h -= 6 val result = lo and 0x0f or (h shl 4 and 0xff) - status.C = h and 255 < 15 - status.Z = result == 0 - status.V = false // BCD never sets overflow flag - status.N = false // BCD is never negative on NMOS 6502 (bug) + Status.C = h and 255 < 15 + Status.Z = result == 0 + Status.V = false // BCD never sets overflow flag + Status.N = false // BCD is never negative on NMOS 6502 (bug) A = result and 255 } else { // normal sub val invertedOperand = operand xor 255 - val result = A + invertedOperand + if (status.C) 1 else 0 - status.C = result > 255 - status.V = (A xor invertedOperand) and (A xor result) and 0x0080 != 0 + val result = A + invertedOperand + if (Status.C) 1 else 0 + Status.C = result > 255 + Status.V = (A xor invertedOperand) and (A xor result) and 0x0080 != 0 A = result and 255 - status.N = (A and 0b10000000) != 0 - status.Z = A == 0 + Status.N = (A and 0b10000000) != 0 + Status.Z = A == 0 } } private fun iSec() { - status.C = true + Status.C = true } private fun iSed() { - status.D = true + Status.D = true } private fun iSei() { - status.I = true + Status.I = true } private fun iSta() { @@ -1040,36 +1060,36 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu private fun iTax() { X = A - status.Z = X == 0 - status.N = (X and 0b10000000) != 0 + Status.Z = X == 0 + Status.N = (X and 0b10000000) != 0 } private fun iTay() { Y = A - status.Z = Y == 0 - status.N = (Y and 0b10000000) != 0 + Status.Z = Y == 0 + Status.N = (Y and 0b10000000) != 0 } private fun iTsx() { - X = status.asByte().toInt() - status.Z = X == 0 - status.N = (X and 0b10000000) != 0 + X = Status.asByte().toInt() + Status.Z = X == 0 + Status.N = (X and 0b10000000) != 0 } private fun iTxa() { A = X - status.Z = A == 0 - status.N = (A and 0b10000000) != 0 + Status.Z = A == 0 + Status.N = (A and 0b10000000) != 0 } private fun iTxs() { - status.fromByte(X) + Status.fromByte(X) } private fun iTya() { A = Y - status.Z = A == 0 - status.N = (A and 0b10000000) != 0 + Status.Z = A == 0 + Status.N = (A and 0b10000000) != 0 } // unofficial/illegal instructions diff --git a/sim65/src/components/Ram.kt b/sim65/src/components/Ram.kt index a665428fb..9353eba5d 100644 --- a/sim65/src/components/Ram.kt +++ b/sim65/src/components/Ram.kt @@ -11,6 +11,9 @@ class Ram(startAddress: Address, endAddress: Address): MemMappedComponent(startA memory[address-startAddress] = data } + operator fun get(address: Address) = read(address) + operator fun set(address: Address, data: UByte) = write(address, data) + override fun cloneMem(): Array = memory.toTypedArray() override fun clock() { } diff --git a/sim65/test/Test6502.kt b/sim65/test/Test6502.kt new file mode 100644 index 000000000..dc095236a --- /dev/null +++ b/sim65/test/Test6502.kt @@ -0,0 +1,321 @@ +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import kotlin.test.* + + +/* + +Unit test suite adapted from Py65 https://github.com/mnaberez/py65 + +Copyright (c) 2008-2018, Mike Naberezny and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +class Test6502 : TestCommon6502() { + // NMOS 6502 tests + + // ADC Indirect, Indexed (X) + + @Test + fun test_adc_ind_indexed_has_page_wrap_bug() { + mpu.A = 0x01 + mpu.X = 0xFF + // $0000 ADC ($80,X) + // $007f Vector to $BBBB (read if page wrapped) + // $017f Vector to $ABCD (read if no page wrap) + writeMem(memory, 0x0000, listOf(0x61, 0x80)) + writeMem(memory, 0x007f, listOf(0xBB, 0xBB)) + writeMem(memory, 0x017f, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x01 + memory[0xBBBB] = 0x02 + mpu.step() + assertEquals(0x03, mpu.A) + } + + // ADC Indexed, Indirect (Y) + + @Test + fun test_adc_indexed_ind_y_has_page_wrap_bug() { + mpu.PC = 0x1000 + mpu.A = 0x42 + mpu.Y = 0x02 + // $1000 ADC ($FF),Y + writeMem(memory, 0x1000, listOf(0x71, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x14 // read if no page wrap + memory[0x0012] = 0x42 // read if page wrapped + mpu.step() + assertEquals(0x84, mpu.A) + } + + // LDA Zero Page, X-Indexed + + @Test + fun test_lda_zp_x_indexed_page_wraps() { + mpu.A = 0x00 + mpu.X = 0xFF + // $0000 LDA $80,X + writeMem(memory, 0x0000, listOf(0xB5, 0x80)) + memory[0x007F] = 0x42 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x42, mpu.A) + } + + // AND Indexed, Indirect (Y) + + @Test + fun test_and_indexed_ind_y_has_page_wrap_bug() { + mpu.PC = 0x1000 + mpu.A = 0x42 + mpu.Y = 0x02 + // $1000 AND ($FF),Y + writeMem(memory, 0x1000, listOf(0x31, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x00 // read if no page wrap + memory[0x0012] = 0xFF // read if page wrapped + mpu.step() + assertEquals(0x42, mpu.A) + } + + // BRK + + @Test + fun test_brk_preserves_decimal_flag_when_it_is_set() { + mpu.Status.D = true + // $C000 BRK + memory[0xC000] = 0x00 + mpu.PC = 0xC000 + mpu.step() + assertTrue(mpu.Status.B) + assertTrue(mpu.Status.D) + } + + @Test + fun test_brk_preserves_decimal_flag_when_it_is_clear() { + // $C000 BRK + memory[0xC000] = 0x00 + mpu.PC = 0xC000 + mpu.step() + assertTrue(mpu.Status.B) + assertFalse(mpu.Status.D) + } + + // CMP Indirect, Indexed (X) + + @Test + fun test_cmp_ind_x_has_page_wrap_bug() { + mpu.A = 0x42 + mpu.X = 0xFF + // $0000 CMP ($80,X) + // $007f Vector to $BBBB (read if page wrapped) + // $017f Vector to $ABCD (read if no page wrap) + writeMem(memory, 0x0000, listOf(0xC1, 0x80)) + writeMem(memory, 0x007f, listOf(0xBB, 0xBB)) + writeMem(memory, 0x017f, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + memory[0xBBBB] = 0x42 + mpu.step() + assertTrue(mpu.Status.Z) + } + + // CMP Indexed, Indirect (Y) + + @Test + fun test_cmp_indexed_ind_y_has_page_wrap_bug() { + mpu.PC = 0x1000 + mpu.A = 0x42 + mpu.Y = 0x02 + // $1000 CMP ($FF),Y + writeMem(memory, 0x1000, listOf(0xd1, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x14 // read if no page wrap + memory[0x0012] = 0x42 // read if page wrapped + mpu.step() + assertTrue(mpu.Status.Z) + } + + // EOR Indirect, Indexed (X) + + @Test + fun test_eor_ind_x_has_page_wrap_bug() { + mpu.A = 0xAA + mpu.X = 0xFF + // $0000 EOR ($80,X) + // $007f Vector to $BBBB (read if page wrapped) + // $017f Vector to $ABCD (read if no page wrap) + writeMem(memory, 0x0000, listOf(0x41, 0x80)) + writeMem(memory, 0x007f, listOf(0xBB, 0xBB)) + writeMem(memory, 0x017f, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + memory[0xBBBB] = 0xFF + mpu.step() + assertEquals(0x55, mpu.A) + } + + // EOR Indexed, Indirect (Y) + + @Test + fun test_eor_indexed_ind_y_has_page_wrap_bug() { + mpu.PC = 0x1000 + mpu.A = 0xAA + mpu.Y = 0x02 + // $1000 EOR ($FF),Y + writeMem(memory, 0x1000, listOf(0x51, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x00 // read if no page wrap + memory[0x0012] = 0xFF // read if page wrapped + mpu.step() + assertEquals(0x55, mpu.A) + } + + // LDA Indirect, Indexed (X) + + @Test + fun test_lda_ind_indexed_x_has_page_wrap_bug() { + mpu.A = 0x00 + mpu.X = 0xff + // $0000 LDA ($80,X) + // $007f Vector to $BBBB (read if page wrapped) + // $017f Vector to $ABCD (read if no page wrap) + writeMem(memory, 0x0000, listOf(0xA1, 0x80)) + writeMem(memory, 0x007f, listOf(0xBB, 0xBB)) + writeMem(memory, 0x017f, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x42 + memory[0xBBBB] = 0xEF + mpu.step() + assertEquals(0xEF, mpu.A) + } + + // LDA Indexed, Indirect (Y) + + @Test + fun test_lda_indexed_ind_y_has_page_wrap_bug() { + mpu.PC = 0x1000 + mpu.A = 0x00 + mpu.Y = 0x02 + // $1000 LDA ($FF),Y + writeMem(memory, 0x1000, listOf(0xb1, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x14 // read if no page wrap + memory[0x0012] = 0x42 // read if page wrapped + mpu.step() + assertEquals(0x42, mpu.A) + } + + // LDA Zero Page, X-Indexed + + @Test + fun test_lda_zp_x_has_page_wrap_bug() { + mpu.A = 0x00 + mpu.X = 0xFF + // $0000 LDA $80,X + writeMem(memory, 0x0000, listOf(0xB5, 0x80)) + memory[0x007F] = 0x42 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x42, mpu.A) + } + + // JMP Indirect + + @Test + fun test_jmp_jumps_to_address_with_page_wrap_bug() { + memory[0x00ff] = 0 + // $0000 JMP ($00) + writeMem(memory, 0, listOf(0x6c, 0xff, 0x00)) + mpu.step() + assertEquals(0x6c00, mpu.PC) + assertEquals(5, mpu.totalCycles) + } + + // ORA Indexed, Indirect (Y) + + @Test + fun test_ora_indexed_ind_y_has_page_wrap_bug() { + mpu.PC = 0x1000 + mpu.A = 0x00 + mpu.Y = 0x02 + // $1000 ORA ($FF),Y + writeMem(memory, 0x1000, listOf(0x11, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x00 // read if no page wrap + memory[0x0012] = 0x42 // read if page wrapped + mpu.step() + assertEquals(0x42, mpu.A) + } + + // SBC Indexed, Indirect (Y) + + @Test + fun test_sbc_indexed_ind_y_has_page_wrap_bug() { + + mpu.PC = 0x1000 + mpu.Status.C = true + mpu.A = 0x42 + mpu.Y = 0x02 + // $1000 SBC ($FF),Y + writeMem(memory, 0x1000, listOf(0xf1, 0xff)) + // Vector + memory[0x00ff] = 0x10 // low byte + memory[0x0100] = 0x20 // high byte if no page wrap + memory[0x0000] = 0x00 // high byte if page wrapped + // Data + memory[0x2012] = 0x02 // read if no page wrap + memory[0x0012] = 0x03 // read if page wrapped + mpu.step() + assertEquals(0x3f, mpu.A) + } +} diff --git a/sim65/test/Test6502CpuBasics.kt b/sim65/test/Test6502CpuBasics.kt new file mode 100644 index 000000000..c33ca53f4 --- /dev/null +++ b/sim65/test/Test6502CpuBasics.kt @@ -0,0 +1,25 @@ +import org.junit.jupiter.api.Test +import sim65.components.Bus +import sim65.components.Cpu6502 +import kotlin.test.assertEquals + +class Test6502CpuBasics { + + @Test + fun testCpuFlagsAfterReset() { + val cpu = Cpu6502(true) + val bus = Bus() + bus.add(cpu) + cpu.reset() + assertEquals(0xfd, cpu.SP) + assertEquals(0xffff, cpu.PC) + assertEquals(0, cpu.totalCycles) + assertEquals(8, cpu.instrCycles) + assertEquals(0, cpu.A) + assertEquals(0, cpu.X) + assertEquals(0, cpu.Y) + assertEquals(0, cpu.currentOpcode) + assertEquals(Cpu6502.StatusRegister(C = false, Z = false, I = false, D = false, B = false, V = false, N = false), cpu.Status) + } + +} diff --git a/sim65/test/Test65C02.kt b/sim65/test/Test65C02.kt new file mode 100644 index 000000000..921fc3596 --- /dev/null +++ b/sim65/test/Test65C02.kt @@ -0,0 +1,1547 @@ +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import kotlin.test.* + + +/* + +Unit test suite adapted from Py65 https://github.com/mnaberez/py65 + +Copyright (c) 2008-2018, Mike Naberezny and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +@Disabled("there is no 65C02 cpu implementation at this time") // TODO create a 65c02 cpu! +class Test65C02 : TestCommon6502() { + // CMOS 65C02 Tests + + // Reset + + @Test + fun test_reset_clears_decimal_flag() { + // W65C02S Datasheet, Apr 14 2009, Table 7-1 Operational Enhancements + // NMOS 6502 decimal flag = indetermine after reset, CMOS 65C02 = 0 + mpu.Status.D = true + mpu.reset() + assertFalse(mpu.Status.D) + } + + // ADC Zero Page, Indirect + + @Test + fun test_adc_bcd_off_zp_ind_carry_clear_in_accumulator_zeroes() { + mpu.A = 0x00 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_adc_bcd_off_zp_ind_carry_set_in_accumulator_zero() { + mpu.A = 0 + mpu.Status.C = true + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + } + + @Test + fun test_adc_bcd_off_zp_ind_carry_clear_in_no_carry_clear_out() { + mpu.A = 0x01 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFE + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_adc_bcd_off_zp_ind_carry_clear_in_carry_set_out() { + mpu.A = 0x02 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_adc_bcd_off_zp_ind_overflow_cleared_no_carry_01_plus_01() { + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_zp_ind_overflow_cleared_no_carry_01_plus_ff() { + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_zp_ind_overflow_set_no_carry_7f_plus_01() { + mpu.Status.C = false + mpu.A = 0x7f + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_zp_ind_overflow_set_no_carry_80_plus_ff() { + mpu.Status.C = false + mpu.A = 0x80 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_zp_ind_overflow_set_on_40_plus_40() { + mpu.A = 0x40 + // $0000 ADC ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x72, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + } + + // AND Zero Page, Indirect + + @Test + fun test_and_zp_ind_all_zeros_setting_zero_flag() { + mpu.A = 0xFF + // $0000 AND ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x32, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + @Test + fun test_and_zp_ind_zeros_and_ones_setting_negative_flag() { + mpu.A = 0xFF + // $0000 AND ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x32, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xAA + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + // BIT (Absolute, X-Indexed) + + @Test + fun test_bit_abs_x_copies_bit_7_of_memory_to_n_flag_when_0() { + mpu.Status.N = false + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0xFF + mpu.A = 0xFF + mpu.step() + assertTrue(mpu.Status.N) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + @Test + fun test_bit_abs_x_copies_bit_7_of_memory_to_n_flag_when_1() { + mpu.Status.N = true + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0xFF + mpu.step() + assertFalse(mpu.Status.N) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + @Test + fun test_bit_abs_x_copies_bit_6_of_memory_to_v_flag_when_0() { + mpu.Status.V = false + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0xFF + mpu.A = 0xFF + mpu.step() + assertTrue(mpu.Status.V) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + @Test + fun test_bit_abs_x_copies_bit_6_of_memory_to_v_flag_when_1() { + mpu.Status.V = true + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0xFF + mpu.step() + assertFalse(mpu.Status.V) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + @Test + fun test_bit_abs_x_stores_result_of_and_in_z_preserves_a_when_1() { + mpu.Status.Z = false + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0xFEED]) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + @Test + fun test_bit_abs_x_stores_result_of_and_nonzero_in_z_preserves_a() { + mpu.Status.Z = true + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0x01 + mpu.A = 0x01 + mpu.step() + assertFalse(mpu.Status.Z) // result of AND is non-zero + assertEquals(0x01, mpu.A) + assertEquals(0x01, memory[0xFEED]) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + @Test + fun test_bit_abs_x_stores_result_of_and_when_zero_in_z_preserves_a() { + mpu.Status.Z = false + mpu.X = 0x02 + // $0000 BIT $FEEB,X + writeMem(memory, 0x0000, listOf(0x3C, 0xEB, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) // result of AND is zero + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0xFEED]) + assertEquals(4, mpu.totalCycles) + assertEquals(0x0003, mpu.PC) + } + + // BIT (Immediate) + + @Test + fun test_bit_imm_does_not_affect_n_and_z_flags() { + mpu.Status.N = true + mpu.Status.V = true + // $0000 BIT #$FF + writeMem(memory, 0x0000, listOf(0x89, 0xff)) + mpu.A = 0x00 + mpu.step() + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertEquals(0x00, mpu.A) + assertEquals(2, mpu.totalCycles) + assertEquals(0x02, mpu.PC) + } + + @Test + fun test_bit_imm_stores_result_of_and_in_z_preserves_a_when_1() { + mpu.Status.Z = false + // $0000 BIT #$00 + writeMem(memory, 0x0000, listOf(0x89, 0x00)) + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) + assertEquals(0x01, mpu.A) + assertEquals(2, mpu.totalCycles) + assertEquals(0x02, mpu.PC) + } + + @Test + fun test_bit_imm_stores_result_of_and_when_nonzero_in_z_preserves_a() { + mpu.Status.Z = true + // $0000 BIT #$01 + writeMem(memory, 0x0000, listOf(0x89, 0x01)) + mpu.A = 0x01 + mpu.step() + assertFalse(mpu.Status.Z) // result of AND is non-zero + assertEquals(0x01, mpu.A) + assertEquals(2, mpu.totalCycles) + assertEquals(0x02, mpu.PC) + } + + @Test + fun test_bit_imm_stores_result_of_and_when_zero_in_z_preserves_a() { + mpu.Status.Z = false + // $0000 BIT #$00 + writeMem(memory, 0x0000, listOf(0x89, 0x00)) + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) // result of AND is zero + assertEquals(0x01, mpu.A) + assertEquals(2, mpu.totalCycles) + assertEquals(0x02, mpu.PC) + } + + // BIT (Zero Page, X-Indexed) + + @Test + fun test_bit_zp_x_copies_bit_7_of_memory_to_n_flag_when_0() { + mpu.Status.N = false + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0xFF + mpu.X = 0x03 + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertTrue(mpu.Status.N) + } + + @Test + fun test_bit_zp_x_copies_bit_7_of_memory_to_n_flag_when_1() { + mpu.Status.N = true + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0x00 + mpu.X = 0x03 + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertFalse(mpu.Status.N) + } + + @Test + fun test_bit_zp_x_copies_bit_6_of_memory_to_v_flag_when_0() { + mpu.Status.V = false + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0xFF + mpu.X = 0x03 + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertTrue(mpu.Status.V) + } + + @Test + fun test_bit_zp_x_copies_bit_6_of_memory_to_v_flag_when_1() { + mpu.Status.V = true + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0x00 + mpu.X = 0x03 + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertFalse(mpu.Status.V) + } + + @Test + fun test_bit_zp_x_stores_result_of_and_in_z_preserves_a_when_1() { + mpu.Status.Z = false + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0x00 + mpu.X = 0x03 + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0x0010 + mpu.X]) + } + + @Test + fun test_bit_zp_x_stores_result_of_and_when_nonzero_in_z_preserves_a() { + mpu.Status.Z = true + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0x01 + mpu.X = 0x03 + mpu.A = 0x01 + mpu.step() + assertFalse(mpu.Status.Z) // result of AND is non-zero + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertEquals(0x01, mpu.A) + assertEquals(0x01, memory[0x0010 + mpu.X]) + } + + @Test + fun test_bit_zp_x_stores_result_of_and_when_zero_in_z_preserves_a() { + mpu.Status.Z = false + // $0000 BIT $0010,X + writeMem(memory, 0x0000, listOf(0x34, 0x10)) + memory[0x0013] = 0x00 + mpu.X = 0x03 + mpu.A = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + assertTrue(mpu.Status.Z) // result of AND is zero + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0x0010 + mpu.X]) + } + + // BRK + + @Test + fun test_brk_clears_decimal_flag() { + mpu.Status.D = true + // $C000 BRK + memory[0xC000] = 0x00 + mpu.PC = 0xC000 + mpu.step() + assertTrue(mpu.Status.B) + assertFalse(mpu.Status.D) + } + + // CMP Zero Page, Indirect + + @Test + fun test_cmp_zpi_sets_z_flag_if_equal() { + mpu.A = 0x42 + // $0000 AND ($10) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xd2, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x42 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x42, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + @Test + fun test_cmp_zpi_resets_z_flag_if_unequal() { + mpu.A = 0x43 + // $0000 AND ($10) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xd2, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x42 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x43, mpu.A) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // EOR Zero Page, Indirect + + @Test + fun test_eor_zp_ind_flips_bits_over_setting_z_flag() { + mpu.A = 0xFF + // $0000 EOR ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x52, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0xABCD]) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_eor_zp_ind_flips_bits_over_setting_n_flag() { + mpu.A = 0x00 + // $0000 EOR ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x52, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0xABCD]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + // INC Accumulator + + @Test + fun test_inc_acc_increments_accum() { + memory[0x0000] = 0x1A + mpu.A = 0x42 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x43, mpu.A) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + @Test + fun test_inc_acc_increments_accum_rolls_over_and_sets_zero_flag() { + memory[0x0000] = 0x1A + mpu.A = 0xFF + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + @Test + fun test_inc_acc_sets_negative_flag_when_incrementing_above_7F() { + memory[0x0000] = 0x1A + mpu.A = 0x7F + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + } + + // JMP Indirect + + @Test + fun test_jmp_ind_does_not_have_page_wrap_bug() { + writeMem(memory, 0x10FF, listOf(0xCD, 0xAB)) + // $0000 JMP ($10FF) + writeMem(memory, 0, listOf(0x6c, 0xFF, 0x10)) + mpu.step() + assertEquals(0xABCD, mpu.PC) + assertEquals(6, mpu.totalCycles) + } + + // JMP Indirect Absolute X-Indexed + + @Test + fun test_jmp_iax_jumps_to_address() { + mpu.X = 2 + // $0000 JMP ($ABCD,X) + // $ABCF Vector to $1234 + writeMem(memory, 0x0000, listOf(0x7C, 0xCD, 0xAB)) + writeMem(memory, 0xABCF, listOf(0x34, 0x12)) + mpu.step() + assertEquals(0x1234, mpu.PC) + assertEquals(6, mpu.totalCycles) + } + + // LDA Zero Page, Indirect + + @Test + fun test_lda_zp_ind_loads_a_sets_n_flag() { + mpu.A = 0x00 + // $0000 LDA ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xB2, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_lda_zp_ind_loads_a_sets_z_flag() { + mpu.A = 0x00 + // $0000 LDA ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xB2, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // ORA Zero Page, Indirect + + @Test + fun test_ora_zp_ind_zeroes_or_zeros_sets_z_flag() { + mpu.Status.Z = false + mpu.A = 0x00 + mpu.Y = 0x12 // These should not affect the ORA + mpu.X = 0x34 + // $0000 ORA ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x12, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_ora_zp_ind_turns_bits_on_sets_n_flag() { + mpu.Status.N = false + mpu.A = 0x03 + // $0000 ORA ($0010) + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x12, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x82 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + // PHX + + @Test + fun test_phx_pushes_x_and_updates_sp() { + mpu.X = 0xAB + // $0000 PHX + memory[0x0000] = 0xDA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.X) + assertEquals(0xAB, memory[0x01FF]) + assertEquals(0xFE, mpu.SP) + assertEquals(3, mpu.totalCycles) + } + + // PHY + + @Test + fun test_phy_pushes_y_and_updates_sp() { + mpu.Y = 0xAB + // $0000 PHY + memory[0x0000] = 0x5A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.Y) + assertEquals(0xAB, memory[0x01FF]) + assertEquals(0xFE, mpu.SP) + assertEquals(3, mpu.totalCycles) + } + + // PLX + + @Test + fun test_plx_pulls_top_byte_from_stack_into_x_and_updates_sp() { + // $0000 PLX + memory[0x0000] = 0xFA + memory[0x01FF] = 0xAB + mpu.SP = 0xFE + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.X) + assertEquals(0xFF, mpu.SP) + assertEquals(4, mpu.totalCycles) + } + + // PLY + + @Test + fun test_ply_pulls_top_byte_from_stack_into_y_and_updates_sp() { + // $0000 PLY + memory[0x0000] = 0x7A + memory[0x01FF] = 0xAB + mpu.SP = 0xFE + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.Y) + assertEquals(0xFF, mpu.SP) + assertEquals(4, mpu.totalCycles) + } + + // RMB0 + + @Test + fun test_rmb0_clears_bit_0_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB0 $43 + writeMem(memory, 0x0000, listOf(0x07, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b11111110 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_rmb0_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB0 $43 + writeMem(memory, 0x0000, listOf(0x07, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB1 + + @Test + fun test_rmb1_clears_bit_1_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB1 $43 + writeMem(memory, 0x0000, listOf(0x17, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b11111101 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_rmb1_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB1 $43 + writeMem(memory, 0x0000, listOf(0x17, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB2 + + @Test + fun test_rmb2_clears_bit_2_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB2 $43 + writeMem(memory, 0x0000, listOf(0x27, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b11111011 + assertEquals(expected, memory[0x0043].toInt()) + + } + + @Test + fun test_rmb2_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB2 $43 + writeMem(memory, 0x0000, listOf(0x27, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB3 + + @Test + fun test_rmb3_clears_bit_3_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB3 $43 + writeMem(memory, 0x0000, listOf(0x37, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b11110111 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_rmb3_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB3 $43 + writeMem(memory, 0x0000, listOf(0x37, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB4 + + @Test + fun test_rmb4_clears_bit_4_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB4 $43 + writeMem(memory, 0x0000, listOf(0x47, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b11101111 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_rmb4_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB4 $43 + writeMem(memory, 0x0000, listOf(0x47, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB5 + + @Test + fun test_rmb5_clears_bit_5_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB5 $43 + writeMem(memory, 0x0000, listOf(0x57, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b11011111 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_rmb5_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB5 $43 + writeMem(memory, 0x0000, listOf(0x57, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB6 + + @Test + fun test_rmb6_clears_bit_6_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB6 $43 + writeMem(memory, 0x0000, listOf(0x67, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b10111111 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_rmb6_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB6 $43 + writeMem(memory, 0x0000, listOf(0x67, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // RMB7 + + @Test + fun test_rmb7_clears_bit_7_without_affecting_other_bits() { + memory[0x0043] = 0b11111111 + // $0000 RMB7 $43 + writeMem(memory, 0x0000, listOf(0x77, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b01111111 + assertEquals(expected, memory[0x0043].toInt()) + + } + + @Test + fun test_rmb7_does_not_affect_status_register() { + memory[0x0043] = 0b11111111 + // $0000 RMB7 $43 + writeMem(memory, 0x0000, listOf(0x77, 0x43)) + val expected = 0b01010101 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // STA Zero Page, Indirect + + @Test + fun test_sta_zp_ind_stores_a_leaves_a_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + // $0000 STA ($0010) + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0x92, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0xFF, memory[0xFEED]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sta_zp_ind_stores_a_leaves_a_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + // $0000 STA ($0010) + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0x92, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, memory[0xFEED]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // SMB0 + + @Test + fun test_smb0_sets_bit_0_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB0 $43 + writeMem(memory, 0x0000, listOf(0x87, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b00000001 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_smb0_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB0 $43 + writeMem(memory, 0x0000, listOf(0x87, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB1 + + @Test + fun test_smb1_sets_bit_1_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB1 $43 + writeMem(memory, 0x0000, listOf(0x97, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b00000010 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_smb1_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB1 $43 + writeMem(memory, 0x0000, listOf(0x97, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB2 + + @Test + fun test_smb2_sets_bit_2_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB2 $43 + writeMem(memory, 0x0000, listOf(0xA7, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b00000100 + assertEquals(expected, memory[0x0043].toInt()) + + } + + @Test + fun test_smb2_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB2 $43 + writeMem(memory, 0x0000, listOf(0xA7, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB3 + + @Test + fun test_smb3_sets_bit_3_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB3 $43 + writeMem(memory, 0x0000, listOf(0xB7, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b00001000 + assertEquals(expected, memory[0x0043].toInt()) + + } + + @Test + fun test_smb3_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB3 $43 + writeMem(memory, 0x0000, listOf(0xB7, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB4 + + @Test + fun test_smb4_sets_bit_4_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB4 $43 + writeMem(memory, 0x0000, listOf(0xC7, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b00010000 + assertEquals(expected, memory[0x0043].toInt()) + + } + + @Test + fun test_smb4_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB4 $43 + writeMem(memory, 0x0000, listOf(0xC7, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB5 + + @Test + fun test_smb5_sets_bit_5_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB5 $43 + writeMem(memory, 0x0000, listOf(0xD7, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b00100000 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_smb5_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB5 $43 + writeMem(memory, 0x0000, listOf(0xD7, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB6 + + @Test + fun test_smb6_sets_bit_6_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB6 $43 + writeMem(memory, 0x0000, listOf(0xE7, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b01000000 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_smb6_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB6 $43 + writeMem(memory, 0x0000, listOf(0xE7, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SMB7 + + @Test + fun test_smb7_sets_bit_7_without_affecting_other_bits() { + memory[0x0043] = 0b00000000 + // $0000 SMB7 $43 + writeMem(memory, 0x0000, listOf(0xF7, 0x43)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + val expected = 0b10000000 + assertEquals(expected, memory[0x0043].toInt()) + } + + @Test + fun test_smb7_does_not_affect_status_register() { + memory[0x0043] = 0b00000000 + // $0000 SMB7 $43 + writeMem(memory, 0x0000, listOf(0xF7, 0x43)) + val expected = 0b11001100 + mpu.Status.fromByte(expected) + mpu.step() + assertEquals(expected, mpu.Status.asByte().toInt()) + } + + // SBC Zero Page, Indirect + + @Test + fun test_sbc_zp_ind_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC ($10) + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF2, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_zp_ind_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC ($10) + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF2, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_zp_ind_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC ($10) + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF2, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_zp_ind_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC ($10) + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF2, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED] = 0x02 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // STZ Zero Page + + @Test + fun test_stz_zp_stores_zero() { + memory[0x0032] = 0x88 + // #0000 STZ $32 + memory[0x0000] = 0x64 + memory[0x0001] = 0x32 + assertEquals(0x88, memory[0x0032]) + mpu.step() + assertEquals(0x00, memory[0x0032]) + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + } + + // STZ Zero Page, X-Indexed + + @Test + fun test_stz_zp_x_stores_zero() { + memory[0x0032] = 0x88 + // $0000 STZ $32,X + memory[0x0000] = 0x74 + memory[0x0001] = 0x32 + assertEquals(0x88, memory[0x0032]) + mpu.step() + assertEquals(0x00, memory[0x0032]) + assertEquals(0x0002, mpu.PC) + assertEquals(4, mpu.totalCycles) + } + + // STZ Absolute + + @Test + fun test_stz_abs_stores_zero() { + memory[0xFEED] = 0x88 + // $0000 STZ $FEED + writeMem(memory, 0x0000, listOf(0x9C, 0xED, 0xFE)) + assertEquals(0x88, memory[0xFEED]) + mpu.step() + assertEquals(0x00, memory[0xFEED]) + assertEquals(0x0003, mpu.PC) + assertEquals(4, mpu.totalCycles) + } + + // STZ Absolute, X-Indexed + + @Test + fun test_stz_abs_x_stores_zero() { + memory[0xFEED] = 0x88 + mpu.X = 0x0D + // $0000 STZ $FEE0,X + writeMem(memory, 0x0000, listOf(0x9E, 0xE0, 0xFE)) + assertEquals(0x88, memory[0xFEED]) + assertEquals(0x0D, mpu.X) + mpu.step() + assertEquals(0x00, memory[0xFEED]) + assertEquals(0x0003, mpu.PC) + assertEquals(5, mpu.totalCycles) + } + + // TSB Zero Page + + @Test + fun test_tsb_zp_ones() { + memory[0x00BB] = 0xE0 + // $0000 TSB $BD + writeMem(memory, 0x0000, listOf(0x04, 0xBB)) + mpu.A = 0x70 + assertEquals(0xE0, memory[0x00BB]) + mpu.step() + assertEquals(0xF0, memory[0x00BB]) + assertFalse(mpu.Status.Z) + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + } + + @Test + fun test_tsb_zp_zeros() { + memory[0x00BB] = 0x80 + // $0000 TSB $BD + writeMem(memory, 0x0000, listOf(0x04, 0xBB)) + mpu.A = 0x60 + assertEquals(0x80, memory[0x00BB]) + mpu.step() + assertEquals(0xE0, memory[0x00BB]) + assertTrue(mpu.Status.Z) + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + } + + // TSB Absolute + + @Test + fun test_tsb_abs_ones() { + memory[0xFEED] = 0xE0 + // $0000 TSB $FEED + writeMem(memory, 0x0000, listOf(0x0C, 0xED, 0xFE)) + mpu.A = 0x70 + assertEquals(0xE0, memory[0xFEED]) + mpu.step() + assertEquals(0xF0, memory[0xFEED]) + assertFalse(mpu.Status.Z) + assertEquals(0x0003, mpu.PC) + assertEquals(6, mpu.totalCycles) + } + + @Test + fun test_tsb_abs_zeros() { + memory[0xFEED] = 0x80 + // $0000 TSB $FEED + writeMem(memory, 0x0000, listOf(0x0C, 0xED, 0xFE)) + mpu.A = 0x60 + assertEquals(0x80, memory[0xFEED]) + mpu.step() + assertEquals(0xE0, memory[0xFEED]) + assertTrue(mpu.Status.Z) + assertEquals(0x0003, mpu.PC) + assertEquals(6, mpu.totalCycles) + } + + // TRB Zero Page + + @Test + fun test_trb_zp_ones() { + memory[0x00BB] = 0xE0 + // $0000 TRB $BD + writeMem(memory, 0x0000, listOf(0x14, 0xBB)) + mpu.A = 0x70 + assertEquals(0xE0, memory[0x00BB]) + mpu.step() + assertEquals(0x80, memory[0x00BB]) + assertFalse(mpu.Status.Z) + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + } + + @Test + fun test_trb_zp_zeros() { + memory[0x00BB] = 0x80 + // $0000 TRB $BD + writeMem(memory, 0x0000, listOf(0x14, 0xBB)) + mpu.A = 0x60 + assertEquals(0x80, memory[0x00BB]) + mpu.step() + assertEquals(0x80, memory[0x00BB]) + assertTrue(mpu.Status.Z) + assertEquals(0x0002, mpu.PC) + assertEquals(5, mpu.totalCycles) + } + + // TRB Absolute + + @Test + fun test_trb_abs_ones() { + memory[0xFEED] = 0xE0 + // $0000 TRB $FEED + writeMem(memory, 0x0000, listOf(0x1C, 0xED, 0xFE)) + mpu.A = 0x70 + assertEquals(0xE0, memory[0xFEED]) + mpu.step() + assertEquals(0x80, memory[0xFEED]) + assertFalse(mpu.Status.Z) + assertEquals(0x0003, mpu.PC) + assertEquals(6, mpu.totalCycles) + } + + @Test + fun test_trb_abs_zeros() { + memory[0xFEED] = 0x80 + // $0000 TRB $FEED + writeMem(memory, 0x0000, listOf(0x1C, 0xED, 0xFE)) + mpu.A = 0x60 + assertEquals(0x80, memory[0xFEED]) + mpu.step() + assertEquals(0x80, memory[0xFEED]) + assertTrue(mpu.Status.Z) + assertEquals(0x0003, mpu.PC) + assertEquals(6, mpu.totalCycles) + } + + @Test + fun test_dec_a_decreases_a() { + // $0000 DEC A + memory[0x0000] = 0x3a + mpu.A = 0x48 + mpu.step() + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + assertEquals(0x47, mpu.A) + } + + @Test + fun test_dec_a_sets_zero_flag() { + // $0000 DEC A + memory[0x0000] = 0x3a + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + assertEquals(0x00, mpu.A) + } + + @Test + fun test_dec_a_wraps_at_zero() { + // $0000 DEC A + memory[0x0000] = 0x3a + mpu.A = 0x00 + mpu.step() + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + assertEquals(0xFF, mpu.A) + } + + @Test + fun test_bra_forward() { + // $0000 BRA $10 + writeMem(memory, 0x0000, listOf(0x80, 0x10)) + mpu.step() + assertEquals(0x12, mpu.PC) + assertEquals(2, mpu.totalCycles) + } + + @Test + fun test_bra_backward() { + // $0240 BRA $F0 + writeMem(memory, 0x0000, listOf(0x80, 0xF0)) + mpu.PC = 0x0204 + mpu.step() + assertEquals(0x1F6, mpu.PC) + assertEquals(3, mpu.totalCycles) // Crossed boundry + } + + // WAI + + @Test + fun test_wai_sets_waiting() { + assertFalse(mpu.waiting) + // $0240 WAI + memory[0x0204] = 0xcb + mpu.PC = 0x0204 + mpu.step() + assertTrue(mpu.waiting) + assertEquals(0x0205, mpu.PC) + assertEquals(3, mpu.totalCycles) + } +} diff --git a/sim65/test/TestCommon6502.kt b/sim65/test/TestCommon6502.kt new file mode 100644 index 000000000..8aff7ba45 --- /dev/null +++ b/sim65/test/TestCommon6502.kt @@ -0,0 +1,6866 @@ +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import sim65.components.* +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + + +/* + +Unit test suite adapted from Py65 https://github.com/mnaberez/py65 + +Copyright (c) 2008-2018, Mike Naberezny and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + + +@TestInstance(TestInstance.Lifecycle.PER_METHOD) +abstract class TestCommon6502 { + // Tests common to 6502-based microprocessors + + val mpu = Cpu6502(true) // TODO make a 65C02 cpu as well and let the subclasses testsuites define the appropriate instance + val memory = Ram(0, 0xffff) + val bus = Bus() + + init { + bus.add(mpu) + bus.add(memory) + mpu.reset() + } + + companion object { + // processor flags + const val fNEGATIVE = 128 + const val fOVERFLOW = 64 + const val fUNUSED = 32 + const val fBREAK = 16 + const val fDECIMAL = 8 + const val fINTERRUPT = 4 + const val fZERO = 2 + const val fCARRY = 1 + } + + // test helpers + fun writeMem(memory: MemMappedComponent, startAddress: Address, data: Iterable) { + var addr = startAddress + data.forEach { memory.write(addr++, it) } + } + + + // Reset + @Test + fun test_reset_sets_registers_to_initial_states() { + + mpu.reset() + assertEquals(0xFF, mpu.SP) + assertEquals(0, mpu.A) + assertEquals(0, mpu.X) + assertEquals(0, mpu.Y) + assertEquals(Cpu6502.StatusRegister(C = false, Z = false, I = false, D = false, B = true, V = false, N = false), mpu.Status) + } + + // ADC Absolute + @Test + fun test_adc_bcd_off_absolute_carry_clear_in_accumulator_zeroes() { + mpu.A = 0 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + // assertEquals(0x10000, memory.size) + + memory[0xC000] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_adc_bcd_off_absolute_carry_set_in_accumulator_zero() { + mpu.A = 0 + mpu.Status.C = true + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + } + + @Test + fun test_adc_bcd_off_absolute_carry_clear_in_no_carry_clear_out() { + mpu.A = 0x01 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0xFE + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_adc_bcd_off_absolute_carry_clear_in_carry_set_out() { + mpu.A = 0x02 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_adc_bcd_off_absolute_overflow_clr_no_carry_01_plus_01() { + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_absolute_overflow_clr_no_carry_01_plus_ff() { + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0xff + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_absolute_overflow_set_no_carry_7f_plus_01() { + mpu.Status.C = false + mpu.A = 0x7f + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_absolute_overflow_set_no_carry_80_plus_ff() { + mpu.Status.C = false + mpu.A = 0x80 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0xff + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + } + + @Test + fun test_adc_bcd_off_absolute_overflow_set_on_40_plus_40() { + mpu.Status.V = false + mpu.A = 0x40 + // $0000 ADC $C000 + writeMem(memory, 0x0000, listOf(0x6D, 0x00, 0xC0)) + memory[0xC000] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + } + + // ADC Zero Page + @Test + fun test_adc_bcd_off_zp_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0 + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_zp_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.Status.C = true + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_zp_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0xFE + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_zp_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_zp_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0xff + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0xff + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_overflow_set_on_40_plus_40() { + + mpu.A = 0x40 + mpu.Status.V = false + // $0000 ADC $00B0 + writeMem(memory, 0x0000, listOf(0x65, 0xB0)) + memory[0x00B0] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + // ADC Immediate + + } + + @Test + fun test_adc_bcd_off_immediate_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0 + // $0000 ADC #$00 + writeMem(memory, 0x0000, listOf(0x69, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_immediate_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.Status.C = true + // $0000 ADC #$00 + writeMem(memory, 0x0000, listOf(0x69, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_immediate_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + // $0000 ADC #$FE + writeMem(memory, 0x0000, listOf(0x69, 0xFE)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_immediate_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + // $0000 ADC #$FF + writeMem(memory, 0x0000, listOf(0x69, 0xFF)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_immediate_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC #$01 + writeMem(memory, 0x000, listOf(0x69, 0x01)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_immediate_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC #$FF + writeMem(memory, 0x000, listOf(0x69, 0xff)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_immediate_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + // $0000 ADC #$01 + writeMem(memory, 0x000, listOf(0x69, 0x01)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_immediate_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + // $0000 ADC #$FF + writeMem(memory, 0x000, listOf(0x69, 0xff)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_immediate_overflow_set_on_40_plus_40() { + + mpu.A = 0x40 + // $0000 ADC #$40 + writeMem(memory, 0x0000, listOf(0x69, 0x40)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_on_immediate_79_plus_00_carry_set() { + + mpu.Status.D = true + mpu.Status.C = true + mpu.A = 0x79 + // $0000 ADC #$00 + writeMem(memory, 0x0000, listOf(0x69, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_on_immediate_6f_plus_00_carry_set() { + + mpu.Status.D = true + mpu.Status.C = true + mpu.A = 0x6f + // $0000 ADC #$00 + writeMem(memory, 0x0000, listOf(0x69, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x76, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_on_immediate_9c_plus_9d() { + + mpu.Status.D = true + mpu.Status.C = false + mpu.A = 0x9c + // $0000 ADC #$9d + // $0002 ADC #$9d + writeMem(memory, 0x0000, listOf(0x69, 0x9d)) + writeMem(memory, 0x0002, listOf(0x69, 0x9d)) + mpu.step() + assertEquals(0x9f, mpu.A) + assertTrue(mpu.Status.C) + mpu.step() + assertEquals(0x0004, mpu.PC) + assertEquals(0x93, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + + // ADC Absolute, X-Indexed + + } + + @Test + fun test_adc_bcd_off_abs_x_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_abs_x_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_abs_x_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0xFE + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_abs_x_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + mpu.X = 0x03 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_abs_x_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_x_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0xff + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_x_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_x_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0xff + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_x_overflow_set_on_40_plus_40() { + + mpu.Status.V = false + mpu.A = 0x40 + mpu.X = 0x03 + // $0000 ADC $C000,X + writeMem(memory, 0x0000, listOf(0x7D, 0x00, 0xC0)) + memory[0xC000 + mpu.X] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + // ADC Absolute, Y-Indexed + + } + + @Test + fun test_adc_bcd_off_abs_y_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_abs_y_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.Y = 0x03 + mpu.Status.C = true + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_abs_y_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + mpu.Y = 0x03 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0xFE + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_abs_y_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + mpu.Y = 0x03 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_abs_y_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_y_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_y_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_y_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_abs_y_overflow_set_on_40_plus_40() { + + mpu.Status.V = false + mpu.A = 0x40 + mpu.Y = 0x03 + // $0000 ADC $C000,Y + writeMem(memory, 0x0000, listOf(0x79, 0x00, 0xC0)) + memory[0xC000 + mpu.Y] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + // ADC Zero Page, X-Indexed + + } + + @Test + fun test_adc_bcd_off_zp_x_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_zp_x_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_zp_x_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0xFE + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_zp_x_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_zp_x_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_x_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_x_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_x_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0xff + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_zp_x_overflow_set_on_40_plus_40() { + + mpu.Status.V = false + mpu.A = 0x40 + mpu.X = 0x03 + // $0000 ADC $0010,X + writeMem(memory, 0x0000, listOf(0x75, 0x10)) + memory[0x0010 + mpu.X] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + // ADC Indirect, Indexed (X) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFE + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_ind_indexed_overflow_set_on_40_plus_40() { + + mpu.Status.V = false + mpu.A = 0x40 + mpu.X = 0x03 + // $0000 ADC ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x61, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + // ADC Indexed, Indirect (Y) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_carry_clear_in_accumulator_zeroes() { + + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_carry_set_in_accumulator_zero() { + + mpu.A = 0 + mpu.Y = 0x03 + mpu.Status.C = true + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_carry_clear_in_no_carry_clear_out() { + + mpu.A = 0x01 + mpu.Y = 0x03 + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFE + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_carry_clear_in_carry_set_out() { + + mpu.A = 0x02 + mpu.Y = 0x03 + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, mpu.A) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_overflow_clr_no_carry_01_plus_01() { + + mpu.Status.C = false + mpu.A = 0x01 + mpu.Y = 0x03 + // $0000 $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_overflow_clr_no_carry_01_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x01 + mpu.Y = 0x03 + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_overflow_set_no_carry_7f_plus_01() { + + mpu.Status.C = false + mpu.A = 0x7f + mpu.Y = 0x03 + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_overflow_set_no_carry_80_plus_ff() { + + mpu.Status.C = false + mpu.A = 0x80 + mpu.Y = 0x03 + // $0000 $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x7f, mpu.A) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_adc_bcd_off_indexed_ind_overflow_set_on_40_plus_40() { + + mpu.Status.V = false + mpu.A = 0x40 + mpu.Y = 0x03 + // $0000 ADC ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x71, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertTrue(mpu.Status.V) + assertFalse(mpu.Status.Z) + + // AND (Absolute) + + } + + @Test + fun test_and_absolute_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + // $0000 AND $ABCD + writeMem(memory, 0x0000, listOf(0x2D, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_absolute_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + // $0000 AND $ABCD + writeMem(memory, 0x0000, listOf(0x2D, 0xCD, 0xAB)) + memory[0xABCD] = 0xAA + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND (Absolute) + + } + + @Test + fun test_and_zp_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + // $0000 AND $0010 + writeMem(memory, 0x0000, listOf(0x25, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_zp_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + // $0000 AND $0010 + writeMem(memory, 0x0000, listOf(0x25, 0x10)) + memory[0x0010] = 0xAA + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND (Immediate) + + } + + @Test + fun test_and_immediate_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + // $0000 AND #$00 + writeMem(memory, 0x0000, listOf(0x29, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_immediate_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + // $0000 AND #$AA + writeMem(memory, 0x0000, listOf(0x29, 0xAA)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND (Absolute, X-Indexed) + + } + + @Test + fun test_and_abs_x_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 AND $ABCD,X + writeMem(memory, 0x0000, listOf(0x3d, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_abs_x_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 AND $ABCD,X + writeMem(memory, 0x0000, listOf(0x3d, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0xAA + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND (Absolute, Y-Indexed) + + } + + @Test + fun test_and_abs_y_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 AND $ABCD,X + writeMem(memory, 0x0000, listOf(0x39, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_abs_y_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 AND $ABCD,X + writeMem(memory, 0x0000, listOf(0x39, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xAA + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND Indirect, Indexed (X) + + } + + @Test + fun test_and_ind_indexed_x_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 AND ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x21, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_ind_indexed_x_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 AND ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x21, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0xAA + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND Indexed, Indirect (Y) + + } + + @Test + fun test_and_indexed_ind_y_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 AND ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x31, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_indexed_ind_y_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 AND ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x31, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xAA + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // AND Zero Page, X-Indexed + + } + + @Test + fun test_and_zp_x_all_zeros_setting_zero_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 AND $0010,X + writeMem(memory, 0x0000, listOf(0x35, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_and_zp_x_all_zeros_and_ones_setting_negative_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 AND $0010,X + writeMem(memory, 0x0000, listOf(0x35, 0x10)) + memory[0x0010 + mpu.X] = 0xAA + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ASL Accumulator + + } + + @Test + fun test_asl_accumulator_sets_z_flag() { + + mpu.A = 0x00 + // $0000 ASL A + memory[0x0000] = 0x0A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_asl_accumulator_sets_n_flag() { + + mpu.A = 0x40 + // $0000 ASL A + memory[0x0000] = 0x0A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_asl_accumulator_shifts_out_zero() { + + mpu.A = 0x7F + // $0000 ASL A + memory[0x0000] = 0x0A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xFE, mpu.A) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_asl_accumulator_shifts_out_one() { + + mpu.A = 0xFF + // $0000 ASL A + memory[0x0000] = 0x0A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xFE, mpu.A) + assertTrue(mpu.Status.C) + + } + + @Test + fun test_asl_accumulator_80_sets_z_flag() { + + mpu.A = 0x80 + mpu.Status.Z = false + // $0000 ASL A + memory[0x0000] = 0x0A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + // ASL Absolute + + } + + @Test + fun test_asl_absolute_sets_z_flag() { + + // $0000 ASL $ABCD + writeMem(memory, 0x0000, listOf(0x0E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_asl_absolute_sets_n_flag() { + + // $0000 ASL $ABCD + writeMem(memory, 0x0000, listOf(0x0E, 0xCD, 0xAB)) + memory[0xABCD] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, memory[0xABCD]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_asl_absolute_shifts_out_zero() { + + mpu.A = 0xAA + // $0000 ASL $ABCD + writeMem(memory, 0x0000, listOf(0x0E, 0xCD, 0xAB)) + memory[0xABCD] = 0x7F + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0xABCD]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_asl_absolute_shifts_out_one() { + + mpu.A = 0xAA + // $0000 ASL $ABCD + writeMem(memory, 0x0000, listOf(0x0E, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0xABCD]) + assertTrue(mpu.Status.C) + + // ASL Zero Page + + } + + @Test + fun test_asl_zp_sets_z_flag() { + + // $0000 ASL $0010 + writeMem(memory, 0x0000, listOf(0x06, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_asl_zp_sets_n_flag() { + + // $0000 ASL $0010 + writeMem(memory, 0x0000, listOf(0x06, 0x10)) + memory[0x0010] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, memory[0x0010]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_asl_zp_shifts_out_zero() { + + mpu.A = 0xAA + // $0000 ASL $0010 + writeMem(memory, 0x0000, listOf(0x06, 0x10)) + memory[0x0010] = 0x7F + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0x0010]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_asl_zp_shifts_out_one() { + + mpu.A = 0xAA + // $0000 ASL $0010 + writeMem(memory, 0x0000, listOf(0x06, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0x0010]) + assertTrue(mpu.Status.C) + + // ASL Absolute, X-Indexed + + } + + @Test + fun test_asl_abs_x_indexed_sets_z_flag() { + + mpu.X = 0x03 + // $0000 ASL $ABCD,X + writeMem(memory, 0x0000, listOf(0x1E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_asl_abs_x_indexed_sets_n_flag() { + + mpu.X = 0x03 + // $0000 ASL $ABCD,X + writeMem(memory, 0x0000, listOf(0x1E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_asl_abs_x_indexed_shifts_out_zero() { + + mpu.A = 0xAA + mpu.X = 0x03 + // $0000 ASL $ABCD,X + writeMem(memory, 0x0000, listOf(0x1E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x7F + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_asl_abs_x_indexed_shifts_out_one() { + + mpu.A = 0xAA + mpu.X = 0x03 + // $0000 ASL $ABCD,X + writeMem(memory, 0x0000, listOf(0x1E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.C) + + // ASL Zero Page, X-Indexed + + } + + @Test + fun test_asl_zp_x_indexed_sets_z_flag() { + + mpu.X = 0x03 + // $0000 ASL $0010,X + writeMem(memory, 0x0000, listOf(0x16, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_asl_zp_x_indexed_sets_n_flag() { + + mpu.X = 0x03 + // $0000 ASL $0010,X + writeMem(memory, 0x0000, listOf(0x16, 0x10)) + memory[0x0010 + mpu.X] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_asl_zp_x_indexed_shifts_out_zero() { + + mpu.X = 0x03 + mpu.A = 0xAA + // $0000 ASL $0010,X + writeMem(memory, 0x0000, listOf(0x16, 0x10)) + memory[0x0010 + mpu.X] = 0x7F + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_asl_zp_x_indexed_shifts_out_one() { + + mpu.X = 0x03 + mpu.A = 0xAA + // $0000 ASL $0010,X + writeMem(memory, 0x0000, listOf(0x16, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xAA, mpu.A) + assertEquals(0xFE, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.C) + + // BCC + + } + + @Test + fun test_bcc_carry_clear_branches_relative_forward() { + + mpu.Status.C = false + // $0000 BCC +6 + writeMem(memory, 0x0000, listOf(0x90, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + + } + + @Test + fun test_bcc_carry_clear_branches_relative_backward() { + + mpu.Status.C = false + mpu.PC = 0x0050 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + // $0000 BCC -6 + writeMem(memory, 0x0050, listOf(0x90, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_bcc_carry_set_does_not_branch() { + + mpu.Status.C = true + // $0000 BCC +6 + writeMem(memory, 0x0000, listOf(0x90, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + + // BCS + + } + + @Test + fun test_bcs_carry_set_branches_relative_forward() { + + mpu.Status.C = true + // $0000 BCS +6 + writeMem(memory, 0x0000, listOf(0xB0, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + + } + + @Test + fun test_bcs_carry_set_branches_relative_backward() { + + mpu.Status.C = true + mpu.PC = 0x0050 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + // $0000 BCS -6 + writeMem(memory, 0x0050, listOf(0xB0, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_bcs_carry_clear_does_not_branch() { + + mpu.Status.C = false + // $0000 BCS +6 + writeMem(memory, 0x0000, listOf(0xB0, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + + // BEQ + + } + + @Test + fun test_beq_zero_set_branches_relative_forward() { + + mpu.Status.Z = true + // $0000 BEQ +6 + writeMem(memory, 0x0000, listOf(0xF0, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + + } + + @Test + fun test_beq_zero_set_branches_relative_backward() { + + mpu.Status.Z = true + mpu.PC = 0x0050 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + // $0000 BEQ -6 + writeMem(memory, 0x0050, listOf(0xF0, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_beq_zero_clear_does_not_branch() { + + mpu.Status.Z = false + // $0000 BEQ +6 + writeMem(memory, 0x0000, listOf(0xF0, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + + // BIT (Absolute) + + } + + @Test + fun test_bit_abs_copies_bit_7_of_memory_to_n_flag_when_0() { + + mpu.Status.N = false + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0xFF + mpu.A = 0xFF + mpu.step() + assertTrue(mpu.Status.N) + + } + + @Test + fun test_bit_abs_copies_bit_7_of_memory_to_n_flag_when_1() { + + mpu.Status.N = true + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0xFF + mpu.step() + assertFalse(mpu.Status.N) + + } + + @Test + fun test_bit_abs_copies_bit_6_of_memory_to_v_flag_when_0() { + + mpu.Status.V = false + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0xFF + mpu.A = 0xFF + mpu.step() + assertTrue(mpu.Status.V) + + } + + @Test + fun test_bit_abs_copies_bit_6_of_memory_to_v_flag_when_1() { + + mpu.Status.V = true + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0xFF + mpu.step() + assertFalse(mpu.Status.V) + + } + + @Test + fun test_bit_abs_stores_result_of_and_in_z_preserves_a_when_1() { + + mpu.Status.Z = false + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0xFEED]) + + } + + @Test + fun test_bit_abs_stores_result_of_and_when_nonzero_in_z_preserves_a() { + + mpu.Status.Z = true + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0x01 + mpu.A = 0x01 + mpu.step() + assertFalse(mpu.Status.Z) // result of AND is non-zero + assertEquals(0x01, mpu.A) + assertEquals(0x01, memory[0xFEED]) + + } + + @Test + fun test_bit_abs_stores_result_of_and_when_zero_in_z_preserves_a() { + + mpu.Status.Z = false + // $0000 BIT $FEED + writeMem(memory, 0x0000, listOf(0x2C, 0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.A = 0x01 + mpu.step() + assertTrue(mpu.Status.Z) // result of AND is zero + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0xFEED]) + + // BIT (Zero Page) + + } + + @Test + fun test_bit_zp_copies_bit_7_of_memory_to_n_flag_when_0() { + + mpu.Status.N = false + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0xFF + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertTrue(mpu.Status.N) + + } + + @Test + fun test_bit_zp_copies_bit_7_of_memory_to_n_flag_when_1() { + + mpu.Status.N = true + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0x00 + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_bit_zp_copies_bit_6_of_memory_to_v_flag_when_0() { + + mpu.Status.V = false + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0xFF + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertTrue(mpu.Status.V) + + } + + @Test + fun test_bit_zp_copies_bit_6_of_memory_to_v_flag_when_1() { + + mpu.Status.V = true + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0x00 + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertFalse(mpu.Status.V) + + } + + @Test + fun test_bit_zp_stores_result_of_and_in_z_preserves_a_when_1() { + + mpu.Status.Z = false + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0x00 + mpu.A = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertTrue(mpu.Status.Z) + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0x0010]) + + } + + @Test + fun test_bit_zp_stores_result_of_and_when_nonzero_in_z_preserves_a() { + + mpu.Status.Z = true + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0x01 + mpu.A = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertFalse(mpu.Status.Z) // result of AND is non-zero + assertEquals(0x01, mpu.A) + assertEquals(0x01, memory[0x0010]) + + } + + @Test + fun test_bit_zp_stores_result_of_and_when_zero_in_z_preserves_a() { + + mpu.Status.Z = false + // $0000 BIT $0010 + writeMem(memory, 0x0000, listOf(0x24, 0x10)) + memory[0x0010] = 0x00 + mpu.A = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(3, mpu.totalCycles) + assertTrue(mpu.Status.Z) // result of AND is zero + assertEquals(0x01, mpu.A) + assertEquals(0x00, memory[0x0010]) + + // BMI + + } + + @Test + fun test_bmi_negative_set_branches_relative_forward() { + + mpu.Status.N = true + // $0000 BMI +06 + writeMem(memory, 0x0000, listOf(0x30, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + + } + + @Test + fun test_bmi_negative_set_branches_relative_backward() { + + mpu.Status.N = true + mpu.PC = 0x0050 + // $0000 BMI -6 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + writeMem(memory, 0x0050, listOf(0x30, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_bmi_negative_clear_does_not_branch() { + + mpu.Status.N = false + // $0000 BEQ +6 + writeMem(memory, 0x0000, listOf(0x30, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + + // BNE + + } + + @Test + fun test_bne_zero_clear_branches_relative_forward() { + + mpu.Status.Z = false + // $0000 BNE +6 + writeMem(memory, 0x0000, listOf(0xD0, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + + } + + @Test + fun test_bne_zero_clear_branches_relative_backward() { + + mpu.Status.Z = false + mpu.PC = 0x0050 + // $0050 BNE -6 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + writeMem(memory, 0x0050, listOf(0xD0, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_bne_zero_set_does_not_branch() { + + mpu.Status.Z = true + // $0000 BNE +6 + writeMem(memory, 0x0000, listOf(0xD0, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + + // BPL + + } + + @Test + fun test_bpl_negative_clear_branches_relative_forward() { + + mpu.Status.N = false + // $0000 BPL +06 + writeMem(memory, 0x0000, listOf(0x10, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + + } + + @Test + fun test_bpl_negative_clear_branches_relative_backward() { + + mpu.Status.N = false + mpu.PC = 0x0050 + // $0050 BPL -6 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + writeMem(memory, 0x0050, listOf(0x10, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_bpl_negative_set_does_not_branch() { + + mpu.Status.N = true + // $0000 BPL +6 + writeMem(memory, 0x0000, listOf(0x10, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + + // BRK + + } + + @Test + fun test_brk_pushes_pc_plus_2_and_status_then_sets_pc_to_irq_vector() { + + writeMem(memory, 0xFFFE, listOf(0xCD, 0xAB)) + // $C000 BRK + memory[0xC000] = 0x00 + mpu.PC = 0xC000 + mpu.step() + assertEquals(0xABCD, mpu.PC) + + assertEquals(0xC0, memory[0x1FF]) // PCH + assertEquals(0x02, memory[0x1FE]) // PCL + assertEquals(fBREAK or fUNUSED, memory[0x1FD].toInt()) // Status + assertEquals(0xFC, mpu.SP) + + assertEquals(fBREAK or fUNUSED or fINTERRUPT, mpu.Status.asByte().toInt()) + } + + // BVC + + @Test + fun test_bvc_overflow_clear_branches_relative_forward() { + mpu.Status.V = false + // $0000 BVC +6 + writeMem(memory, 0x0000, listOf(0x50, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + } + + @Test + fun test_bvc_overflow_clear_branches_relative_backward() { + mpu.Status.V = false + mpu.PC = 0x0050 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + // $0050 BVC -6 + writeMem(memory, 0x0050, listOf(0x50, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + } + + @Test + fun test_bvc_overflow_set_does_not_branch() { + mpu.Status.V = true + // $0000 BVC +6 + writeMem(memory, 0x0000, listOf(0x50, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + } + + // BVS + + @Test + fun test_bvs_overflow_set_branches_relative_forward() { + mpu.Status.V = true + // $0000 BVS +6 + writeMem(memory, 0x0000, listOf(0x70, 0x06)) + mpu.step() + assertEquals(0x0002 + 0x06, mpu.PC) + } + + @Test + fun test_bvs_overflow_set_branches_relative_backward() { + + mpu.Status.V = true + mpu.PC = 0x0050 + val rel = (0x06 xor 0xFF + 1) // two's complement of 6 + // $0050 BVS -6 + writeMem(memory, 0x0050, listOf(0x70, rel.toShort())) + mpu.step() + assertEquals(0x0052 + rel, mpu.PC) + + } + + @Test + fun test_bvs_overflow_clear_does_not_branch() { + mpu.Status.V = false + // $0000 BVS +6 + writeMem(memory, 0x0000, listOf(0x70, 0x06)) + mpu.step() + assertEquals(0x0002, mpu.PC) + } + + // CLC + + @Test + fun test_clc_clears_carry_flag() { + mpu.Status.C = true + // $0000 CLC + memory[0x0000] = 0x18 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertFalse(mpu.Status.C) + } + + // CLD + + @Test + fun test_cld_clears_decimal_flag() { + mpu.Status.D = true + // $0000 CLD + memory[0x0000] = 0xD8 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertFalse(mpu.Status.D) + } + + // CLI + @Test + fun test_cli_clears_interrupt_mask_flag() { + mpu.Status.I = true + // $0000 CLI + memory[0x0000] = 0x58 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertFalse(mpu.Status.I) + } + + // CLV + @Test + fun test_clv_clears_overflow_flag() { + mpu.Status.V = true + // $0000 CLV + memory[0x0000] = 0xB8 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertFalse(mpu.Status.V) + } + + // Compare instructions + + // See http://6502.org/tutorials/compare_instructions.html + // and http://www.6502.org/tutorials/compare_beyond.html + // Cheat sheet: + // - Comparison is actually subtraction "register - memory" + // - Z contains equality result (1 equal, 0 not equal) + // - C contains result of unsigned comparison (0 if A=m) + // - N holds MSB of subtraction result (*NOT* of signed subtraction) + // - V is not affected by comparison + // - D has no effect on comparison + + // CMP Immediate + @Test + fun test_cmp_imm_sets_zero_carry_clears_neg_flags_if_equal() { + // Comparison: A == m + + // $0000 CMP #10 , A will be 10 + writeMem(memory, 0x0000, listOf(0xC9, 10)) + mpu.A = 10 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + @Test + fun test_cmp_imm_clears_zero_carry_takes_neg_if_less_unsigned() { + // Comparison: A < m (unsigned) + + // $0000 CMP #10 , A will be 1 + writeMem(memory, 0x0000, listOf(0xC9, 10)) + mpu.A = 1 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertTrue(mpu.Status.N) // 0x01-0x0A=0xF7 + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_cmp_imm_clears_zero_sets_carry_takes_neg_if_less_signed() { + // Comparison: A < #nn (signed), A negative + + // $0000 CMP #1, A will be -1 (0xFF) + writeMem(memory, 0x0000, listOf(0xC9, 1)) + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertTrue(mpu.Status.N) // 0xFF-0x01=0xFE + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) // A>m unsigned + + } + + @Test + fun test_cmp_imm_clears_zero_carry_takes_neg_if_less_signed_nega() { + // Comparison: A < m (signed), A and m both negative + + // $0000 CMP #0xFF (-1), A will be -2 (0xFE) + writeMem(memory, 0x0000, listOf(0xC9, 0xFF)) + mpu.A = 0xFE + mpu.step() + assertEquals(0x0002, mpu.PC) + assertTrue(mpu.Status.N) // 0xFE-0xFF=0xFF + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) // A m (unsigned) + + // $0000 CMP #1 , A will be 10 + writeMem(memory, 0x0000, listOf(0xC9, 1)) + mpu.A = 10 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertFalse(mpu.Status.N) // 0x0A-0x01 = 0x09 + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) // A>m unsigned + } + + @Test + fun test_cmp_imm_clears_zero_carry_takes_neg_if_more_signed() { + // Comparison: A > m (signed), memory negative + + // $0000 CMP #$FF (-1), A will be 2 + writeMem(memory, 0x0000, listOf(0xC9, 0xFF)) + mpu.A = 2 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertFalse(mpu.Status.N) // 0x02-0xFF=0x01 + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) // A m (signed), A and m both negative + + // $0000 CMP #$FE (-2), A will be -1 (0xFF) + writeMem(memory, 0x0000, listOf(0xC9, 0xFE)) + mpu.A = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertFalse(mpu.Status.N) // 0xFF-0xFE=0x01 + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) // A>m unsigned + } + + // CPX Immediate + @Test + fun test_cpx_imm_sets_zero_carry_clears_neg_flags_if_equal() { + // Comparison: X == m + + // $0000 CPX #$20 + writeMem(memory, 0x0000, listOf(0xE0, 0x20)) + mpu.X = 0x20 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + } + + // CPY Immediate + @Test + fun test_cpy_imm_sets_zero_carry_clears_neg_flags_if_equal() { + // Comparison: Y == m + + // $0000 CPY #$30 + writeMem(memory, 0x0000, listOf(0xC0, 0x30)) + mpu.Y = 0x30 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + } + + // DEC Absolute + @Test + fun test_dec_abs_decrements_memory() { + + // $0000 DEC 0xABCD + writeMem(memory, 0x0000, listOf(0xCE, 0xCD, 0xAB)) + memory[0xABCD] = 0x10 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x0F, memory[0xABCD]) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_dec_abs_below_00_rolls_over_and_sets_negative_flag() { + + // $0000 DEC 0xABCD + writeMem(memory, 0x0000, listOf(0xCE, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + } + + @Test + fun test_dec_abs_sets_zero_flag_when_decrementing_to_zero() { + + // $0000 DEC 0xABCD + writeMem(memory, 0x0000, listOf(0xCE, 0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // DEC Zero Page + @Test + fun test_dec_zp_decrements_memory() { + + // $0000 DEC 0x0010 + writeMem(memory, 0x0000, listOf(0xC6, 0x10)) + memory[0x0010] = 0x10 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x0F, memory[0x0010]) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_dec_zp_below_00_rolls_over_and_sets_negative_flag() { + + // $0000 DEC 0x0010 + writeMem(memory, 0x0000, listOf(0xC6, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + } + + @Test + fun test_dec_zp_sets_zero_flag_when_decrementing_to_zero() { + + // $0000 DEC 0x0010 + writeMem(memory, 0x0000, listOf(0xC6, 0x10)) + memory[0x0010] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // DEC Absolute, X-Indexed + @Test + fun test_dec_abs_x_decrements_memory() { + + // $0000 DEC 0xABCD,X + writeMem(memory, 0x0000, listOf(0xDE, 0xCD, 0xAB)) + mpu.X = 0x03 + memory[0xABCD + mpu.X] = 0x10 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x0F, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_dec_abs_x_below_00_rolls_over_and_sets_negative_flag() { + + // $0000 DEC 0xABCD,X + writeMem(memory, 0x0000, listOf(0xDE, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + } + + @Test + fun test_dec_abs_x_sets_zero_flag_when_decrementing_to_zero() { + + // $0000 DEC 0xABCD,X + writeMem(memory, 0x0000, listOf(0xDE, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // DEC Zero Page, X-Indexed + @Test + fun test_dec_zp_x_decrements_memory() { + + // $0000 DEC 0x0010,X + writeMem(memory, 0x0000, listOf(0xD6, 0x10)) + mpu.X = 0x03 + memory[0x0010 + mpu.X] = 0x10 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x0F, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_dec_zp_x_below_00_rolls_over_and_sets_negative_flag() { + + // $0000 DEC 0x0010,X + writeMem(memory, 0x0000, listOf(0xD6, 0x10)) + mpu.X = 0x03 + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + } + + @Test + fun test_dec_zp_x_sets_zero_flag_when_decrementing_to_zero() { + + // $0000 DEC 0x0010,X + writeMem(memory, 0x0000, listOf(0xD6, 0x10)) + mpu.X = 0x03 + memory[0x0010 + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // DEX + @Test + fun test_dex_decrements_x() { + mpu.X = 0x10 + // $0000 DEX + memory[0x0000] = 0xCA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x0F, mpu.X) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_dex_below_00_rolls_over_and_sets_negative_flag() { + mpu.X = 0x00 + // $0000 DEX + memory[0x0000] = 0xCA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xFF, mpu.X) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_dex_sets_zero_flag_when_decrementing_to_zero() { + mpu.X = 0x01 + // $0000 DEX + memory[0x0000] = 0xCA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + } + + // DEY + @Test + fun test_dey_decrements_y() { + mpu.Y = 0x10 + // $0000 DEY + memory[0x0000] = 0x88 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x0F, mpu.Y) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + } + + @Test + fun test_dey_below_00_rolls_over_and_sets_negative_flag() { + mpu.Y = 0x00 + // $0000 DEY + memory[0x0000] = 0x88 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xFF, mpu.Y) + assertTrue(mpu.Status.N) + } + + @Test + fun test_dey_sets_zero_flag_when_decrementing_to_zero() { + + mpu.Y = 0x01 + // $0000 DEY + memory[0x0000] = 0x88 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + } + + // EOR Absolute + @Test + fun test_eor_absolute_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + writeMem(memory, 0x0000, listOf(0x4D, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0xABCD]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_absolute_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + writeMem(memory, 0x0000, listOf(0x4D, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0xABCD]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Zero Page + + } + + @Test + fun test_eor_zp_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + writeMem(memory, 0x0000, listOf(0x45, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0x0010]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_zp_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + writeMem(memory, 0x0000, listOf(0x45, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0x0010]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Immediate + + } + + @Test + fun test_eor_immediate_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + writeMem(memory, 0x0000, listOf(0x49, 0xFF)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_immediate_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + writeMem(memory, 0x0000, listOf(0x49, 0xFF)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Absolute, X-Indexed + + } + + @Test + fun test_eor_abs_x_indexed_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x5D, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_abs_x_indexed_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x5D, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Absolute, Y-Indexed + + } + + @Test + fun test_eor_abs_y_indexed_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + writeMem(memory, 0x0000, listOf(0x59, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0xABCD + mpu.Y]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_abs_y_indexed_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + mpu.Y = 0x03 + writeMem(memory, 0x0000, listOf(0x59, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0xABCD + mpu.Y]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Indirect, Indexed (X) + + } + + @Test + fun test_eor_ind_indexed_x_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x41, 0x10)) // => EOR listOf($0010,X) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) // => Vector to $ABCD + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0xABCD]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_ind_indexed_x_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x41, 0x10)) // => EOR listOf($0010,X) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) // => Vector to $ABCD + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0xABCD]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Indexed, Indirect (Y) + + } + + @Test + fun test_eor_indexed_ind_y_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + writeMem(memory, 0x0000, listOf(0x51, 0x10)) // => EOR listOf($0010),Y + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) // => Vector to $ABCD + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0xABCD + mpu.Y]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_indexed_ind_y_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + mpu.Y = 0x03 + writeMem(memory, 0x0000, listOf(0x51, 0x10)) // => EOR listOf($0010),Y + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) // => Vector to $ABCD + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0xABCD + mpu.Y]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // EOR Zero Page, X-Indexed + + } + + @Test + fun test_eor_zp_x_indexed_flips_bits_over_setting_z_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x55, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0xFF, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_eor_zp_x_indexed_flips_bits_over_setting_n_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x55, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, mpu.A) + assertEquals(0xFF, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // INC Absolute + + } + + @Test + fun test_inc_abs_increments_memory() { + + writeMem(memory, 0x0000, listOf(0xEE, 0xCD, 0xAB)) + memory[0xABCD] = 0x09 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x0A, memory[0xABCD]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_abs_increments_memory_rolls_over_and_sets_zero_flag() { + + writeMem(memory, 0x0000, listOf(0xEE, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_abs_sets_negative_flag_when_incrementing_above_7F() { + + writeMem(memory, 0x0000, listOf(0xEE, 0xCD, 0xAB)) + memory[0xABCD] = 0x7F + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, memory[0xABCD]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + // INC Zero Page + + } + + @Test + fun test_inc_zp_increments_memory() { + + writeMem(memory, 0x0000, listOf(0xE6, 0x10)) + memory[0x0010] = 0x09 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x0A, memory[0x0010]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_zp_increments_memory_rolls_over_and_sets_zero_flag() { + + writeMem(memory, 0x0000, listOf(0xE6, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_zp_sets_negative_flag_when_incrementing_above_7F() { + + writeMem(memory, 0x0000, listOf(0xE6, 0x10)) + memory[0x0010] = 0x7F + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, memory[0x0010]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + // INC Absolute, X-Indexed + + } + + @Test + fun test_inc_abs_x_increments_memory() { + + writeMem(memory, 0x0000, listOf(0xFE, 0xCD, 0xAB)) + mpu.X = 0x03 + memory[0xABCD + mpu.X] = 0x09 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x0A, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_abs_x_increments_memory_rolls_over_and_sets_zero_flag() { + + writeMem(memory, 0x0000, listOf(0xFE, 0xCD, 0xAB)) + mpu.X = 0x03 + memory[0xABCD + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_abs_x_sets_negative_flag_when_incrementing_above_7F() { + + writeMem(memory, 0x0000, listOf(0xFE, 0xCD, 0xAB)) + mpu.X = 0x03 + memory[0xABCD + mpu.X] = 0x7F + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + // INC Zero Page, X-Indexed + + } + + @Test + fun test_inc_zp_x_increments_memory() { + + writeMem(memory, 0x0000, listOf(0xF6, 0x10)) + mpu.X = 0x03 + memory[0x0010 + mpu.X] = 0x09 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x0A, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_zp_x_increments_memory_rolls_over_and_sets_zero_flag() { + + writeMem(memory, 0x0000, listOf(0xF6, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inc_zp_x_sets_negative_flag_when_incrementing_above_7F() { + + writeMem(memory, 0x0000, listOf(0xF6, 0x10)) + memory[0x0010 + mpu.X] = 0x7F + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + // INX + + } + + @Test + fun test_inx_increments_x() { + + mpu.X = 0x09 + memory[0x0000] = 0xE8 // => INX + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x0A, mpu.X) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_inx_above_FF_rolls_over_and_sets_zero_flag() { + + mpu.X = 0xFF + memory[0x0000] = 0xE8 // => INX + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_inx_sets_negative_flag_when_incrementing_above_7F() { + + mpu.X = 0x7f + memory[0x0000] = 0xE8 // => INX + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + + // INY + + } + + @Test + fun test_iny_increments_y() { + + mpu.Y = 0x09 + memory[0x0000] = 0xC8 // => INY + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x0A, mpu.Y) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_iny_above_FF_rolls_over_and_sets_zero_flag() { + + mpu.Y = 0xFF + memory[0x0000] = 0xC8 // => INY + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_iny_sets_negative_flag_when_incrementing_above_7F() { + + mpu.Y = 0x7f + memory[0x0000] = 0xC8 // => INY + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + + // JMP Absolute + + } + + @Test + fun test_jmp_abs_jumps_to_absolute_address() { + + // $0000 JMP $ABCD + writeMem(memory, 0x0000, listOf(0x4C, 0xCD, 0xAB)) + mpu.step() + assertEquals(0xABCD, mpu.PC) + + // JMP Indirect + + } + + @Test + fun test_jmp_ind_jumps_to_indirect_address() { + + // $0000 JMP ($ABCD) + writeMem(memory, 0x0000, listOf(0x6C, 0x00, 0x02)) + writeMem(memory, 0x0200, listOf(0xCD, 0xAB)) + mpu.step() + assertEquals(0xABCD, mpu.PC) + + // JSR + + } + + @Test + fun test_jsr_pushes_pc_plus_2_and_sets_pc() { + + // $C000 JSR $FFD2 + writeMem(memory, 0xC000, listOf(0x20, 0xD2, 0xFF)) + mpu.PC = 0xC000 + mpu.step() + assertEquals(0xFFD2, mpu.PC) + assertEquals(0xFD, mpu.SP) + assertEquals(0xC0, memory[0x01FF]) // PCH + assertEquals(0x02, memory[0x01FE]) // PCL+2 + + // LDA Absolute + + } + + @Test + fun test_lda_absolute_loads_a_sets_n_flag() { + + mpu.A = 0x00 + // $0000 LDA $ABCD + writeMem(memory, 0x0000, listOf(0xAD, 0xCD, 0xAB)) + memory[0xABCD] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_absolute_loads_a_sets_z_flag() { + + mpu.A = 0xFF + // $0000 LDA $ABCD + writeMem(memory, 0x0000, listOf(0xAD, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDA Zero Page + + } + + @Test + fun test_lda_zp_loads_a_sets_n_flag() { + + mpu.A = 0x00 + // $0000 LDA $0010 + writeMem(memory, 0x0000, listOf(0xA5, 0x10)) + memory[0x0010] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_zp_loads_a_sets_z_flag() { + + mpu.A = 0xFF + // $0000 LDA $0010 + writeMem(memory, 0x0000, listOf(0xA5, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDA Immediate + + } + + @Test + fun test_lda_immediate_loads_a_sets_n_flag() { + + mpu.A = 0x00 + // $0000 LDA #$80 + writeMem(memory, 0x0000, listOf(0xA9, 0x80)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_immediate_loads_a_sets_z_flag() { + + mpu.A = 0xFF + // $0000 LDA #$00 + writeMem(memory, 0x0000, listOf(0xA9, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDA Absolute, X-Indexed + + } + + @Test + fun test_lda_abs_x_indexed_loads_a_sets_n_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 LDA $ABCD,X + writeMem(memory, 0x0000, listOf(0xBD, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_abs_x_indexed_loads_a_sets_z_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 LDA $ABCD,X + writeMem(memory, 0x0000, listOf(0xBD, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lda_abs_x_indexed_does_not_page_wrap() { + + mpu.A = 0 + mpu.X = 0xFF + // $0000 LDA $0080,X + writeMem(memory, 0x0000, listOf(0xBD, 0x80, 0x00)) + memory[0x0080 + mpu.X] = 0x42 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x42, mpu.A) + + // LDA Absolute, Y-Indexed + + } + + @Test + fun test_lda_abs_y_indexed_loads_a_sets_n_flag() { + + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 LDA $ABCD,Y + writeMem(memory, 0x0000, listOf(0xB9, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_abs_y_indexed_loads_a_sets_z_flag() { + + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 LDA $ABCD,Y + writeMem(memory, 0x0000, listOf(0xB9, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lda_abs_y_indexed_does_not_page_wrap() { + + mpu.A = 0 + mpu.Y = 0xFF + // $0000 LDA $0080,X + writeMem(memory, 0x0000, listOf(0xB9, 0x80, 0x00)) + memory[0x0080 + mpu.Y] = 0x42 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x42, mpu.A) + + // LDA Indirect, Indexed (X) + + } + + @Test + fun test_lda_ind_indexed_x_loads_a_sets_n_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 LDA ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xA1, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_ind_indexed_x_loads_a_sets_z_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 LDA ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xA1, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDA Indexed, Indirect (Y) + + } + + @Test + fun test_lda_indexed_ind_y_loads_a_sets_n_flag() { + + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 LDA ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xB1, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_indexed_ind_y_loads_a_sets_z_flag() { + + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 LDA ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0xB1, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDA Zero Page, X-Indexed + + } + + @Test + fun test_lda_zp_x_indexed_loads_a_sets_n_flag() { + + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 LDA $10,X + writeMem(memory, 0x0000, listOf(0xB5, 0x10)) + memory[0x0010 + mpu.X] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_lda_zp_x_indexed_loads_a_sets_z_flag() { + + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 LDA $10,X + writeMem(memory, 0x0000, listOf(0xB5, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDX Absolute + + } + + @Test + fun test_ldx_absolute_loads_x_sets_n_flag() { + + mpu.X = 0x00 + // $0000 LDX $ABCD + writeMem(memory, 0x0000, listOf(0xAE, 0xCD, 0xAB)) + memory[0xABCD] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldx_absolute_loads_x_sets_z_flag() { + + mpu.X = 0xFF + // $0000 LDX $ABCD + writeMem(memory, 0x0000, listOf(0xAE, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDX Zero Page + + } + + @Test + fun test_ldx_zp_loads_x_sets_n_flag() { + + mpu.X = 0x00 + // $0000 LDX $0010 + writeMem(memory, 0x0000, listOf(0xA6, 0x10)) + memory[0x0010] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldx_zp_loads_x_sets_z_flag() { + + mpu.X = 0xFF + // $0000 LDX $0010 + writeMem(memory, 0x0000, listOf(0xA6, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDX Immediate + + } + + @Test + fun test_ldx_immediate_loads_x_sets_n_flag() { + + mpu.X = 0x00 + // $0000 LDX #$80 + writeMem(memory, 0x0000, listOf(0xA2, 0x80)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldx_immediate_loads_x_sets_z_flag() { + + mpu.X = 0xFF + // $0000 LDX #$00 + writeMem(memory, 0x0000, listOf(0xA2, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDX Absolute, Y-Indexed + + } + + @Test + fun test_ldx_abs_y_indexed_loads_x_sets_n_flag() { + + mpu.X = 0x00 + mpu.Y = 0x03 + // $0000 LDX $ABCD,Y + writeMem(memory, 0x0000, listOf(0xBE, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldx_abs_y_indexed_loads_x_sets_z_flag() { + + mpu.X = 0xFF + mpu.Y = 0x03 + // $0000 LDX $ABCD,Y + writeMem(memory, 0x0000, listOf(0xBE, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDX Zero Page, Y-Indexed + + } + + @Test + fun test_ldx_zp_y_indexed_loads_x_sets_n_flag() { + + mpu.X = 0x00 + mpu.Y = 0x03 + // $0000 LDX $0010,Y + writeMem(memory, 0x0000, listOf(0xB6, 0x10)) + memory[0x0010 + mpu.Y] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldx_zp_y_indexed_loads_x_sets_z_flag() { + + mpu.X = 0xFF + mpu.Y = 0x03 + // $0000 LDX $0010,Y + writeMem(memory, 0x0000, listOf(0xB6, 0x10)) + memory[0x0010 + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDY Absolute + + } + + @Test + fun test_ldy_absolute_loads_y_sets_n_flag() { + + mpu.Y = 0x00 + // $0000 LDY $ABCD + writeMem(memory, 0x0000, listOf(0xAC, 0xCD, 0xAB)) + memory[0xABCD] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldy_absolute_loads_y_sets_z_flag() { + + mpu.Y = 0xFF + // $0000 LDY $ABCD + writeMem(memory, 0x0000, listOf(0xAC, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDY Zero Page + + } + + @Test + fun test_ldy_zp_loads_y_sets_n_flag() { + + mpu.Y = 0x00 + // $0000 LDY $0010 + writeMem(memory, 0x0000, listOf(0xA4, 0x10)) + memory[0x0010] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldy_zp_loads_y_sets_z_flag() { + + mpu.Y = 0xFF + // $0000 LDY $0010 + writeMem(memory, 0x0000, listOf(0xA4, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDY Immediate + + } + + @Test + fun test_ldy_immediate_loads_y_sets_n_flag() { + + mpu.Y = 0x00 + // $0000 LDY #$80 + writeMem(memory, 0x0000, listOf(0xA0, 0x80)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldy_immediate_loads_y_sets_z_flag() { + + mpu.Y = 0xFF + // $0000 LDY #$00 + writeMem(memory, 0x0000, listOf(0xA0, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDY Absolute, X-Indexed + + } + + @Test + fun test_ldy_abs_x_indexed_loads_x_sets_n_flag() { + + mpu.Y = 0x00 + mpu.X = 0x03 + // $0000 LDY $ABCD,X + writeMem(memory, 0x0000, listOf(0xBC, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldy_abs_x_indexed_loads_x_sets_z_flag() { + + mpu.Y = 0xFF + mpu.X = 0x03 + // $0000 LDY $ABCD,X + writeMem(memory, 0x0000, listOf(0xBC, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LDY Zero Page, X-Indexed + + } + + @Test + fun test_ldy_zp_x_indexed_loads_x_sets_n_flag() { + + mpu.Y = 0x00 + mpu.X = 0x03 + // $0000 LDY $0010,X + writeMem(memory, 0x0000, listOf(0xB4, 0x10)) + memory[0x0010 + mpu.X] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_ldy_zp_x_indexed_loads_x_sets_z_flag() { + + mpu.Y = 0xFF + mpu.X = 0x03 + // $0000 LDY $0010,X + writeMem(memory, 0x0000, listOf(0xB4, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + // LSR Accumulator + + } + + @Test + fun test_lsr_accumulator_rotates_in_zero_not_carry() { + + mpu.Status.C = true + // $0000 LSR A + memory[0x0000] = (0x4A) + mpu.A = 0x00 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_accumulator_sets_carry_and_zero_flags_after_rotation() { + + mpu.Status.C = false + // $0000 LSR A + memory[0x0000] = (0x4A) + mpu.A = 0x01 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_accumulator_rotates_bits_right() { + + mpu.Status.C = true + // $0000 LSR A + memory[0x0000] = (0x4A) + mpu.A = 0x04 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x02, mpu.A) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + // LSR Absolute + + } + + @Test + fun test_lsr_absolute_rotates_in_zero_not_carry() { + + mpu.Status.C = true + // $0000 LSR $ABCD + writeMem(memory, 0x0000, listOf(0x4E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_absolute_sets_carry_and_zero_flags_after_rotation() { + + mpu.Status.C = false + // $0000 LSR $ABCD + writeMem(memory, 0x0000, listOf(0x4E, 0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_absolute_rotates_bits_right() { + + mpu.Status.C = true + // $0000 LSR $ABCD + writeMem(memory, 0x0000, listOf(0x4E, 0xCD, 0xAB)) + memory[0xABCD] = 0x04 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x02, memory[0xABCD]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + // LSR Zero Page + + } + + @Test + fun test_lsr_zp_rotates_in_zero_not_carry() { + + mpu.Status.C = true + // $0000 LSR $0010 + writeMem(memory, 0x0000, listOf(0x46, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_zp_sets_carry_and_zero_flags_after_rotation() { + + mpu.Status.C = false + // $0000 LSR $0010 + writeMem(memory, 0x0000, listOf(0x46, 0x10)) + memory[0x0010] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_zp_rotates_bits_right() { + + mpu.Status.C = true + // $0000 LSR $0010 + writeMem(memory, 0x0000, listOf(0x46, 0x10)) + memory[0x0010] = 0x04 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, memory[0x0010]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + // LSR Absolute, X-Indexed + + } + + @Test + fun test_lsr_abs_x_indexed_rotates_in_zero_not_carry() { + + mpu.Status.C = true + mpu.X = 0x03 + // $0000 LSR $ABCD,X + writeMem(memory, 0x0000, listOf(0x5E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_abs_x_indexed_sets_c_and_z_flags_after_rotation() { + + mpu.Status.C = false + mpu.X = 0x03 + // $0000 LSR $ABCD,X + writeMem(memory, 0x0000, listOf(0x5E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_abs_x_indexed_rotates_bits_right() { + + mpu.Status.C = true + // $0000 LSR $ABCD,X + writeMem(memory, 0x0000, listOf(0x5E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x04 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x02, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + // LSR Zero Page, X-Indexed + + } + + @Test + fun test_lsr_zp_x_indexed_rotates_in_zero_not_carry() { + + mpu.Status.C = true + mpu.X = 0x03 + // $0000 LSR $0010,X + writeMem(memory, 0x0000, listOf(0x56, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_zp_x_indexed_sets_carry_and_zero_flags_after_rotation() { + + mpu.Status.C = false + mpu.X = 0x03 + // $0000 LSR $0010,X + writeMem(memory, 0x0000, listOf(0x56, 0x10)) + memory[0x0010 + mpu.X] = 0x01 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertTrue(mpu.Status.C) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_lsr_zp_x_indexed_rotates_bits_right() { + + mpu.Status.C = true + mpu.X = 0x03 + // $0000 LSR $0010,X + writeMem(memory, 0x0000, listOf(0x56, 0x10)) + memory[0x0010 + mpu.X] = 0x04 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x02, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + assertFalse(mpu.Status.N) + + // NOP + + } + + @Test + fun test_nop_does_nothing() { + + // $0000 NOP + memory[0x0000] = 0xEA + mpu.step() + assertEquals(0x0001, mpu.PC) + + // ORA Absolute + + } + + @Test + fun test_ora_absolute_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + // $0000 ORA $ABCD + writeMem(memory, 0x0000, listOf(0x0D, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_absolute_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + // $0000 ORA $ABCD + writeMem(memory, 0x0000, listOf(0x0D, 0xCD, 0xAB)) + memory[0xABCD] = 0x82 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Zero Page + + } + + @Test + fun test_ora_zp_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + // $0000 ORA $0010 + writeMem(memory, 0x0000, listOf(0x05, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_zp_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + // $0000 ORA $0010 + writeMem(memory, 0x0000, listOf(0x05, 0x10)) + memory[0x0010] = 0x82 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Immediate + + } + + @Test + fun test_ora_immediate_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + // $0000 ORA #$00 + writeMem(memory, 0x0000, listOf(0x09, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_immediate_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + // $0000 ORA #$82 + writeMem(memory, 0x0000, listOf(0x09, 0x82)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Absolute, X + + } + + @Test + fun test_ora_abs_x_indexed_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 ORA $ABCD,X + writeMem(memory, 0x0000, listOf(0x1D, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_abs_x_indexed_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + mpu.X = 0x03 + // $0000 ORA $ABCD,X + writeMem(memory, 0x0000, listOf(0x1D, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x82 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Absolute, Y + + } + + @Test + fun test_ora_abs_y_indexed_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 ORA $ABCD,Y + writeMem(memory, 0x0000, listOf(0x19, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_abs_y_indexed_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + mpu.Y = 0x03 + // $0000 ORA $ABCD,Y + writeMem(memory, 0x0000, listOf(0x19, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x82 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Indirect, Indexed (X) + + } + + @Test + fun test_ora_ind_indexed_x_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 ORA ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x01, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_ind_indexed_x_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + mpu.X = 0x03 + // $0000 ORA ($0010,X) + // $0013 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x01, 0x10)) + writeMem(memory, 0x0013, listOf(0xCD, 0xAB)) + memory[0xABCD] = 0x82 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Indexed, Indirect (Y) + + } + + @Test + fun test_ora_indexed_ind_y_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 ORA ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x11, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_indexed_ind_y_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + mpu.Y = 0x03 + // $0000 ORA ($0010),Y + // $0010 Vector to $ABCD + writeMem(memory, 0x0000, listOf(0x11, 0x10)) + writeMem(memory, 0x0010, listOf(0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x82 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // ORA Zero Page, X + + } + + @Test + fun test_ora_zp_x_indexed_zeroes_or_zeros_sets_z_flag() { + + mpu.Status.Z = false + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 ORA $0010,X + writeMem(memory, 0x0000, listOf(0x15, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_ora_zp_x_indexed_turns_bits_on_sets_n_flag() { + + mpu.Status.N = false + mpu.A = 0x03 + mpu.X = 0x03 + // $0000 ORA $0010,X + writeMem(memory, 0x0000, listOf(0x15, 0x10)) + memory[0x0010 + mpu.X] = 0x82 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x83, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + // PHA + + } + + @Test + fun test_pha_pushes_a_and_updates_sp() { + + mpu.A = 0xAB + // $0000 PHA + memory[0x0000] = 0x48 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.A) + assertEquals(0xAB, memory[0x01FF]) + assertEquals(0xFE, mpu.SP) + } + + // PHP + @Test + fun test_php_pushes_processor_status_and_updates_sp() { + for (flags in 0 until 0x100) { + mpu.Status.fromByte(flags or fBREAK or fUNUSED) + // $0000 PHP + memory[0x0000] = 0x08 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals((flags or fBREAK or fUNUSED), memory[0x1FF].toInt()) + assertEquals(0xFE, mpu.SP) + } + + } + + // PLA + @Test + fun test_pla_pulls_top_byte_from_stack_into_a_and_updates_sp() { + + // $0000 PLA + memory[0x0000] = 0x68 + memory[0x01FF] = 0xAB + mpu.SP = 0xFE + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.A) + assertEquals(0xFF, mpu.SP) + + // PLP + + } + + @Test + fun test_plp_pulls_top_byte_from_stack_into_flags_and_updates_sp() { + + // $0000 PLP + memory[0x0000] = 0x28 + memory[0x01FF] = 0xBA // must have BREAK and UNUSED set + mpu.SP = 0xFE + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xBA, mpu.Status.asByte()) + assertEquals(0xFF, mpu.SP) + + // ROL Accumulator + + } + + @Test + fun test_rol_accumulator_zero_and_carry_zero_sets_z_flag() { + + mpu.A = 0x00 + mpu.Status.C = false + // $0000 ROL A + memory[0x0000] = 0x2A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_accumulator_80_and_carry_zero_sets_z_flag() { + + mpu.A = 0x80 + mpu.Status.C = false + mpu.Status.Z = false + // $0000 ROL A + memory[0x0000] = 0x2A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_accumulator_zero_and_carry_one_clears_z_flag() { + + mpu.A = 0x00 + mpu.Status.C = true + // $0000 ROL A + memory[0x0000] = 0x2A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x01, mpu.A) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_accumulator_sets_n_flag() { + + mpu.A = 0x40 + mpu.Status.C = true + // $0000 ROL A + memory[0x0000] = 0x2A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x81, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_rol_accumulator_shifts_out_zero() { + + mpu.A = 0x7F + mpu.Status.C = false + // $0000 ROL A + memory[0x0000] = 0x2A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xFE, mpu.A) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_rol_accumulator_shifts_out_one() { + + mpu.A = 0xFF + mpu.Status.C = false + // $0000 ROL A + memory[0x0000] = 0x2A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xFE, mpu.A) + assertTrue(mpu.Status.C) + + // ROL Absolute + + } + + @Test + fun test_rol_absolute_zero_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + // $0000 ROL $ABCD + writeMem(memory, 0x0000, listOf(0x2E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_absolute_80_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + mpu.Status.Z = false + // $0000 ROL $ABCD + writeMem(memory, 0x0000, listOf(0x2E, 0xCD, 0xAB)) + memory[0xABCD] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_absolute_zero_and_carry_one_clears_z_flag() { + + mpu.A = 0x00 + mpu.Status.C = true + // $0000 ROL $ABCD + writeMem(memory, 0x0000, listOf(0x2E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, memory[0xABCD]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_absolute_sets_n_flag() { + + mpu.Status.C = true + // $0000 ROL $ABCD + writeMem(memory, 0x0000, listOf(0x2E, 0xCD, 0xAB)) + memory[0xABCD] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x81, memory[0xABCD]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_rol_absolute_shifts_out_zero() { + + mpu.Status.C = false + // $0000 ROL $ABCD + writeMem(memory, 0x0000, listOf(0x2E, 0xCD, 0xAB)) + memory[0xABCD] = 0x7F + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFE, memory[0xABCD]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_rol_absolute_shifts_out_one() { + + mpu.Status.C = false + // $0000 ROL $ABCD + writeMem(memory, 0x0000, listOf(0x2E, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFE, memory[0xABCD]) + assertTrue(mpu.Status.C) + + // ROL Zero Page + + } + + @Test + fun test_rol_zp_zero_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + // $0000 ROL $0010 + writeMem(memory, 0x0000, listOf(0x26, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_zp_80_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + mpu.Status.Z = false + // $0000 ROL $0010 + writeMem(memory, 0x0000, listOf(0x26, 0x10)) + memory[0x0010] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_zp_zero_and_carry_one_clears_z_flag() { + + mpu.A = 0x00 + mpu.Status.C = true + // $0000 ROL $0010 + writeMem(memory, 0x0000, listOf(0x26, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, memory[0x0010]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_zp_sets_n_flag() { + + mpu.Status.C = true + // $0000 ROL $0010 + writeMem(memory, 0x0000, listOf(0x26, 0x10)) + memory[0x0010] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x81, memory[0x0010]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_rol_zp_shifts_out_zero() { + + mpu.Status.C = false + // $0000 ROL $0010 + writeMem(memory, 0x0000, listOf(0x26, 0x10)) + memory[0x0010] = 0x7F + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFE, memory[0x0010]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_rol_zp_shifts_out_one() { + + mpu.Status.C = false + // $0000 ROL $0010 + writeMem(memory, 0x0000, listOf(0x26, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFE, memory[0x0010]) + assertTrue(mpu.Status.C) + + // ROL Absolute, X-Indexed + + } + + @Test + fun test_rol_abs_x_indexed_zero_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + mpu.X = 0x03 + // $0000 ROL $ABCD,X + writeMem(memory, 0x0000, listOf(0x3E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_abs_x_indexed_80_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + mpu.Status.Z = false + mpu.X = 0x03 + // $0000 ROL $ABCD,X + writeMem(memory, 0x0000, listOf(0x3E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x80 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_abs_x_indexed_zero_and_carry_one_clears_z_flag() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROL $ABCD,X + writeMem(memory, 0x0000, listOf(0x3E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x01, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_abs_x_indexed_sets_n_flag() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROL $ABCD,X + writeMem(memory, 0x0000, listOf(0x3E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x40 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x81, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_rol_abs_x_indexed_shifts_out_zero() { + + mpu.X = 0x03 + mpu.Status.C = false + // $0000 ROL $ABCD,X + writeMem(memory, 0x0000, listOf(0x3E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x7F + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFE, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_rol_abs_x_indexed_shifts_out_one() { + + mpu.X = 0x03 + mpu.Status.C = false + // $0000 ROL $ABCD,X + writeMem(memory, 0x0000, listOf(0x3E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFE, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.C) + + // ROL Zero Page, X-Indexed + + } + + @Test + fun test_rol_zp_x_indexed_zero_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x36, 0x10)) + // $0000 ROL $0010,X + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_zp_x_indexed_80_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + mpu.Status.Z = false + mpu.X = 0x03 + writeMem(memory, 0x0000, listOf(0x36, 0x10)) + // $0000 ROL $0010,X + memory[0x0010 + mpu.X] = 0x80 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_zp_x_indexed_zero_and_carry_one_clears_z_flag() { + + mpu.X = 0x03 + mpu.Status.C = true + writeMem(memory, 0x0000, listOf(0x36, 0x10)) + // $0000 ROL $0010,X + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x01, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_rol_zp_x_indexed_sets_n_flag() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROL $0010,X + writeMem(memory, 0x0000, listOf(0x36, 0x10)) + memory[0x0010 + mpu.X] = 0x40 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x81, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.Z) + + } + + @Test + fun test_rol_zp_x_indexed_shifts_out_zero() { + + mpu.X = 0x03 + mpu.Status.C = false + // $0000 ROL $0010,X + writeMem(memory, 0x0000, listOf(0x36, 0x10)) + memory[0x0010 + mpu.X] = 0x7F + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFE, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_rol_zp_x_indexed_shifts_out_one() { + + mpu.X = 0x03 + mpu.Status.C = false + // $0000 ROL $0010,X + writeMem(memory, 0x0000, listOf(0x36, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFE, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.C) + + // ROR Accumulator + + } + + @Test + fun test_ror_accumulator_zero_and_carry_zero_sets_z_flag() { + + mpu.A = 0x00 + mpu.Status.C = false + // $0000 ROR A + memory[0x0000] = 0x6A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_ror_accumulator_zero_and_carry_one_rotates_in_sets_n_flags() { + + mpu.A = 0x00 + mpu.Status.C = true + // $0000 ROR A + memory[0x0000] = 0x6A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + } + + @Test + fun test_ror_accumulator_shifts_out_zero() { + + mpu.A = 0x02 + mpu.Status.C = true + // $0000 ROR A + memory[0x0000] = 0x6A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x81, mpu.A) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_ror_accumulator_shifts_out_one() { + + mpu.A = 0x03 + mpu.Status.C = true + // $0000 ROR A + memory[0x0000] = 0x6A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x81, mpu.A) + assertTrue(mpu.Status.C) + + // ROR Absolute + + } + + @Test + fun test_ror_absolute_zero_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + // $0000 ROR $ABCD + writeMem(memory, 0x0000, listOf(0x6E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_ror_absolute_zero_and_carry_one_rotates_in_sets_n_flags() { + + mpu.Status.C = true + // $0000 ROR $ABCD + writeMem(memory, 0x0000, listOf(0x6E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, memory[0xABCD]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + } + + @Test + fun test_ror_absolute_shifts_out_zero() { + + mpu.Status.C = true + // $0000 ROR $ABCD + writeMem(memory, 0x0000, listOf(0x6E, 0xCD, 0xAB)) + memory[0xABCD] = 0x02 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x81, memory[0xABCD]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_ror_absolute_shifts_out_one() { + + mpu.Status.C = true + // $0000 ROR $ABCD + writeMem(memory, 0x0000, listOf(0x6E, 0xCD, 0xAB)) + memory[0xABCD] = 0x03 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x81, memory[0xABCD]) + assertTrue(mpu.Status.C) + + // ROR Zero Page + + } + + @Test + fun test_ror_zp_zero_and_carry_zero_sets_z_flag() { + + mpu.Status.C = false + // $0000 ROR $0010 + writeMem(memory, 0x0000, listOf(0x66, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_ror_zp_zero_and_carry_one_rotates_in_sets_n_flags() { + + mpu.Status.C = true + // $0000 ROR $0010 + writeMem(memory, 0x0000, listOf(0x66, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, memory[0x0010]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + } + + @Test + fun test_ror_zp_zero_absolute_shifts_out_zero() { + + mpu.Status.C = true + // $0000 ROR $0010 + writeMem(memory, 0x0000, listOf(0x66, 0x10)) + memory[0x0010] = 0x02 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x81, memory[0x0010]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_ror_zp_shifts_out_one() { + + mpu.Status.C = true + // $0000 ROR $0010 + writeMem(memory, 0x0000, listOf(0x66, 0x10)) + memory[0x0010] = 0x03 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x81, memory[0x0010]) + assertTrue(mpu.Status.C) + + // ROR Absolute, X-Indexed + + } + + @Test + fun test_ror_abs_x_indexed_zero_and_carry_zero_sets_z_flag() { + + mpu.X = 0x03 + mpu.Status.C = false + // $0000 ROR $ABCD,X + writeMem(memory, 0x0000, listOf(0x7E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_ror_abs_x_indexed_z_and_c_1_rotates_in_sets_n_flags() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROR $ABCD,X + writeMem(memory, 0x0000, listOf(0x7E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x80, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + } + + @Test + fun test_ror_abs_x_indexed_shifts_out_zero() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROR $ABCD,X + writeMem(memory, 0x0000, listOf(0x7E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x02 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x81, memory[0xABCD + mpu.X]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_ror_abs_x_indexed_shifts_out_one() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROR $ABCD,X + writeMem(memory, 0x0000, listOf(0x7E, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x03 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x81, memory[0xABCD + mpu.X]) + assertTrue(mpu.Status.C) + + // ROR Zero Page, X-Indexed + + } + + @Test + fun test_ror_zp_x_indexed_zero_and_carry_zero_sets_z_flag() { + + mpu.X = 0x03 + mpu.Status.C = false + // $0000 ROR $0010,X + writeMem(memory, 0x0000, listOf(0x76, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.Z) + assertFalse(mpu.Status.N) + + } + + @Test + fun test_ror_zp_x_indexed_zero_and_carry_one_rotates_in_sets_n_flags() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROR $0010,X + writeMem(memory, 0x0000, listOf(0x76, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x80, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.N) + + } + + @Test + fun test_ror_zp_x_indexed_zero_absolute_shifts_out_zero() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROR $0010,X + writeMem(memory, 0x0000, listOf(0x76, 0x10)) + memory[0x0010 + mpu.X] = 0x02 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x81, memory[0x0010 + mpu.X]) + assertFalse(mpu.Status.C) + + } + + @Test + fun test_ror_zp_x_indexed_shifts_out_one() { + + mpu.X = 0x03 + mpu.Status.C = true + // $0000 ROR $0010,X + writeMem(memory, 0x0000, listOf(0x76, 0x10)) + memory[0x0010 + mpu.X] = 0x03 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x81, memory[0x0010 + mpu.X]) + assertTrue(mpu.Status.C) + + // RTI + + } + + @Test + fun test_rti_restores_status_and_pc_and_updates_sp() { + + // $0000 RTI + memory[0x0000] = 0x40 + writeMem(memory, 0x01FD, listOf(0xFC, 0x03, 0xC0)) // Status, PCL, PCH + mpu.SP = 0xFC + + mpu.step() + assertEquals(0xC003, mpu.PC) + assertEquals(0xFC, mpu.Status.asByte()) + assertEquals(0xFF, mpu.SP) + + } + + @Test + fun test_rti_forces_break_and_unused_flags_high() { + + // $0000 RTI + memory[0x0000] = 0x40 + writeMem(memory, 0x01FD, listOf(0x00, 0x03, 0xC0)) // Status, PCL, PCH + mpu.SP = 0xFC + + mpu.step() + assertTrue(mpu.Status.B) + // assertEquals(fUNUSED, mpu.Status.asByte().toInt() and fUNUSED) + } + + // RTS + @Test + fun test_rts_restores_pc_and_increments_then_updates_sp() { + + // $0000 RTS + memory[0x0000] = 0x60 + writeMem(memory, 0x01FE, listOf(0x03, 0xC0)) // PCL, PCH + mpu.PC = 0x0000 + mpu.SP = 0xFD + + mpu.step() + assertEquals(0xC004, mpu.PC) + assertEquals(0xFF, mpu.SP) + + } + + @Test + fun test_rts_wraps_around_top_of_memory() { + + // $1000 RTS + memory[0x1000] = 0x60 + writeMem(memory, 0x01FE, listOf(0xFF, 0xFF)) // PCL, PCH + mpu.PC = 0x1000 + mpu.SP = 0xFD + + mpu.step() + assertEquals(0x0000, mpu.PC) + assertEquals(0xFF, mpu.SP) + } + + // SBC Absolute + @Test + fun test_sbc_abs_all_zeros_and_no_borrow_is_zero() { + + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC $ABCD + writeMem(memory, 0x0000, listOf(0xED, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC $ABCD + writeMem(memory, 0x0000, listOf(0xED, 0xCD, 0xAB)) + memory[0xABCD] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC $ABCD + writeMem(memory, 0x0000, listOf(0xED, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC $ABCD + writeMem(memory, 0x0000, listOf(0xED, 0xCD, 0xAB)) + memory[0xABCD] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // SBC Zero Page + @Test + fun test_sbc_zp_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC $10 + writeMem(memory, 0x0000, listOf(0xE5, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_zp_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC $10 + writeMem(memory, 0x0000, listOf(0xE5, 0x10)) + memory[0x0010] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_zp_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // => SBC $10 + writeMem(memory, 0x0000, listOf(0xE5, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_zp_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // => SBC $10 + writeMem(memory, 0x0000, listOf(0xE5, 0x10)) + memory[0x0010] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // SBC Immediate + + @Test + fun test_sbc_imm_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC #$00 + writeMem(memory, 0x0000, listOf(0xE9, 0x00)) + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_imm_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC #$01 + writeMem(memory, 0x0000, listOf(0xE9, 0x01)) + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_imm_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC #$00 + writeMem(memory, 0x0000, listOf(0xE9, 0x00)) + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_imm_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC #$02 + writeMem(memory, 0x0000, listOf(0xE9, 0x02)) + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + @Test + fun test_sbc_bcd_on_immediate_0a_minus_00_carry_set() { + mpu.Status.D = true + mpu.Status.C = true + mpu.A = 0x0a + // $0000 SBC #$00 + writeMem(memory, 0x0000, listOf(0xe9, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x0a, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + @Test + fun test_sbc_bcd_on_immediate_9a_minus_00_carry_set() { + mpu.Status.D = true + mpu.Status.C = true + mpu.A = 0x9a + // $0000 SBC #$00 + writeMem(memory, 0x0000, listOf(0xe9, 0x00)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x9a, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + @Test + fun test_sbc_bcd_on_immediate_00_minus_01_carry_set() { + mpu.Status.D = true + mpu.Status.V = true + mpu.Status.Z = true + mpu.Status.C = true + mpu.Status.N = false + mpu.A = 0x00 + // => $0000 SBC #$00 + writeMem(memory, 0x0000, listOf(0xe9, 0x01)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x99, mpu.A) + assertTrue(mpu.Status.N) + assertFalse(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertFalse(mpu.Status.C) + } + + @Test + fun test_sbc_bcd_on_immediate_20_minus_0a_carry_unset() { + mpu.Status.D = true + mpu.A = 0x20 + // $0000 SBC #$00 + writeMem(memory, 0x0000, listOf(0xe9, 0x0a)) + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x1f, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.V) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // SBC Absolute, X-Indexed + + @Test + fun test_sbc_abs_x_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC $FEE0,X + writeMem(memory, 0x0000, listOf(0xFD, 0xE0, 0xFE)) + mpu.X = 0x0D + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_x_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC $FEE0,X + writeMem(memory, 0x0000, listOf(0xFD, 0xE0, 0xFE)) + mpu.X = 0x0D + memory[0xFEED] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_x_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC $FEE0,X + writeMem(memory, 0x0000, listOf(0xFD, 0xE0, 0xFE)) + mpu.X = 0x0D + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_x_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC $FEE0,X + writeMem(memory, 0x0000, listOf(0xFD, 0xE0, 0xFE)) + mpu.X = 0x0D + memory[0xFEED] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // SBC Absolute, Y-Indexed + + @Test + fun test_sbc_abs_y_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC $FEE0,Y + writeMem(memory, 0x0000, listOf(0xF9, 0xE0, 0xFE)) + mpu.Y = 0x0D + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_y_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC $FEE0,Y + writeMem(memory, 0x0000, listOf(0xF9, 0xE0, 0xFE)) + mpu.Y = 0x0D + memory[0xFEED] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_y_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC $FEE0,Y + writeMem(memory, 0x0000, listOf(0xF9, 0xE0, 0xFE)) + mpu.Y = 0x0D + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_abs_y_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC $FEE0,Y + writeMem(memory, 0x0000, listOf(0xF9, 0xE0, 0xFE)) + mpu.Y = 0x0D + memory[0xFEED] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // SBC Indirect, Indexed (X) + + @Test + fun test_sbc_ind_x_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC ($10,X) + // $0013 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xE1, 0x10)) + writeMem(memory, 0x0013, listOf(0xED, 0xFE)) + mpu.X = 0x03 + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_ind_x_downto_zero_no_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC ($10,X) + // $0013 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xE1, 0x10)) + writeMem(memory, 0x0013, listOf(0xED, 0xFE)) + mpu.X = 0x03 + memory[0xFEED] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_ind_x_downto_zero_with_borrow_sets_z_clears_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC ($10,X) + // $0013 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xE1, 0x10)) + writeMem(memory, 0x0013, listOf(0xED, 0xFE)) + mpu.X = 0x03 + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_ind_x_downto_four_with_borrow_clears_z_n() { + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC ($10,X) + // $0013 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xE1, 0x10)) + writeMem(memory, 0x0013, listOf(0xED, 0xFE)) + mpu.X = 0x03 + memory[0xFEED] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + } + + // SBC Indexed, Indirect (Y) + + @Test + fun test_sbc_ind_y_all_zeros_and_no_borrow_is_zero() { + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 SBC ($10),Y + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF1, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + } + + @Test + fun test_sbc_ind_y_downto_zero_no_borrow_sets_z_clears_n() { + + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC ($10),Y + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF1, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED + mpu.Y] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_sbc_ind_y_downto_zero_with_borrow_sets_z_clears_n() { + + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC ($10),Y + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF1, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_sbc_ind_y_downto_four_with_borrow_clears_z_n() { + + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC ($10),Y + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0xF1, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED + mpu.Y] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + + // SBC Zero Page, X-Indexed + + } + + @Test + fun test_sbc_zp_x_all_zeros_and_no_borrow_is_zero() { + + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x00 + // $0000 SBC $10,X + writeMem(memory, 0x0000, listOf(0xF5, 0x10)) + mpu.X = 0x0D + memory[0x001D] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_sbc_zp_x_downto_zero_no_borrow_sets_z_clears_n() { + + mpu.Status.D = false + mpu.Status.C = true // borrow = 0 + mpu.A = 0x01 + // $0000 SBC $10,X + writeMem(memory, 0x0000, listOf(0xF5, 0x10)) + mpu.X = 0x0D + memory[0x001D] = 0x01 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_sbc_zp_x_downto_zero_with_borrow_sets_z_clears_n() { + + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x01 + // $0000 SBC $10,X + writeMem(memory, 0x0000, listOf(0xF5, 0x10)) + mpu.X = 0x0D + memory[0x001D] = 0x00 + mpu.step() + assertEquals(0x00, mpu.A) + assertFalse(mpu.Status.N) + assertTrue(mpu.Status.C) + assertTrue(mpu.Status.Z) + + } + + @Test + fun test_sbc_zp_x_downto_four_with_borrow_clears_z_n() { + + mpu.Status.D = false + mpu.Status.C = false // borrow = 1 + mpu.A = 0x07 + // $0000 SBC $10,X + writeMem(memory, 0x0000, listOf(0xF5, 0x10)) + mpu.X = 0x0D + memory[0x001D] = 0x02 + mpu.step() + assertEquals(0x04, mpu.A) + assertFalse(mpu.Status.N) + assertFalse(mpu.Status.Z) + assertTrue(mpu.Status.C) + + // SEC + + } + + @Test + fun test_sec_sets_carry_flag() { + + mpu.Status.C = false + // $0000 SEC + memory[0x0000] = 0x038 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertTrue(mpu.Status.C) + + // SED + + } + + @Test + fun test_sed_sets_decimal_mode_flag() { + + mpu.Status.D = false + // $0000 SED + memory[0x0000] = 0xF8 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertTrue(mpu.Status.D) + } + + // SEI + + @Test + fun test_sei_sets_interrupt_disable_flag() { + mpu.Status.I = false + // $0000 SEI + memory[0x0000] = 0x78 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertTrue(mpu.Status.I) + } + + // STA Absolute + + @Test + fun test_sta_absolute_stores_a_leaves_a_and_n_flag_unchanged() { + + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + // $0000 STA $ABCD + writeMem(memory, 0x0000, listOf(0x8D, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + + } + + @Test + fun test_sta_absolute_stores_a_leaves_a_and_z_flag_unchanged() { + + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + // $0000 STA $ABCD + writeMem(memory, 0x0000, listOf(0x8D, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + + // STA Zero Page + + } + + @Test + fun test_sta_zp_stores_a_leaves_a_and_n_flag_unchanged() { + + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + // $0000 STA $0010 + writeMem(memory, 0x0000, listOf(0x85, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + + } + + @Test + fun test_sta_zp_stores_a_leaves_a_and_z_flag_unchanged() { + + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + // $0000 STA $0010 + writeMem(memory, 0x0000, listOf(0x85, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + + // STA Absolute, X-Indexed + + } + + @Test + fun test_sta_abs_x_indexed_stores_a_leaves_a_and_n_flag_unchanged() { + + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 STA $ABCD,X + writeMem(memory, 0x0000, listOf(0x9D, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD + mpu.X]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + + } + + @Test + fun test_sta_abs_x_indexed_stores_a_leaves_a_and_z_flag_unchanged() { + + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 STA $ABCD,X + writeMem(memory, 0x0000, listOf(0x9D, 0xCD, 0xAB)) + memory[0xABCD + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.X]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STA Absolute, Y-Indexed + + @Test + fun test_sta_abs_y_indexed_stores_a_leaves_a_and_n_flag_unchanged() { + + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 STA $ABCD,Y + writeMem(memory, 0x0000, listOf(0x99, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD + mpu.Y]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sta_abs_y_indexed_stores_a_leaves_a_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 STA $ABCD,Y + writeMem(memory, 0x0000, listOf(0x99, 0xCD, 0xAB)) + memory[0xABCD + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD + mpu.Y]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STA Indirect, Indexed (X) + + @Test + fun test_sta_ind_indexed_x_stores_a_leaves_a_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 STA ($0010,X) + // $0013 Vector to $FEED + writeMem(memory, 0x0000, listOf(0x81, 0x10)) + writeMem(memory, 0x0013, listOf(0xED, 0xFE)) + memory[0xFEED] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0xFEED]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sta_ind_indexed_x_stores_a_leaves_a_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 STA ($0010,X) + // $0013 Vector to $FEED + writeMem(memory, 0x0000, listOf(0x81, 0x10)) + writeMem(memory, 0x0013, listOf(0xED, 0xFE)) + memory[0xFEED] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0xFEED]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STA Indexed, Indirect (Y) + + @Test + fun test_sta_indexed_ind_y_stores_a_leaves_a_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + mpu.Y = 0x03 + // $0000 STA ($0010),Y + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0x91, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0xFEED + mpu.Y]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sta_indexed_ind_y_stores_a_leaves_a_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + mpu.Y = 0x03 + // $0000 STA ($0010),Y + // $0010 Vector to $FEED + writeMem(memory, 0x0000, listOf(0x91, 0x10)) + writeMem(memory, 0x0010, listOf(0xED, 0xFE)) + memory[0xFEED + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0xFEED + mpu.Y]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STA Zero Page, X-Indexed + + @Test + fun test_sta_zp_x_indexed_stores_a_leaves_a_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.A = 0xFF + mpu.X = 0x03 + // $0000 STA $0010,X + writeMem(memory, 0x0000, listOf(0x95, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010 + mpu.X]) + assertEquals(0xFF, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sta_zp_x_indexed_stores_a_leaves_a_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.A = 0x00 + mpu.X = 0x03 + // $0000 STA $0010,X + writeMem(memory, 0x0000, listOf(0x95, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertEquals(0x00, mpu.A) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STX Absolute + + @Test + fun test_stx_absolute_stores_x_leaves_x_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.X = 0xFF + // $0000 STX $ABCD + writeMem(memory, 0x0000, listOf(0x8E, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD]) + assertEquals(0xFF, mpu.X) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_stx_absolute_stores_x_leaves_x_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.X = 0x00 + // $0000 STX $ABCD + writeMem(memory, 0x0000, listOf(0x8E, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertEquals(0x00, mpu.X) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STX Zero Page + + @Test + fun test_stx_zp_stores_x_leaves_x_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.X = 0xFF + // $0000 STX $0010 + writeMem(memory, 0x0000, listOf(0x86, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010]) + assertEquals(0xFF, mpu.X) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_stx_zp_stores_x_leaves_x_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.X = 0x00 + // $0000 STX $0010 + writeMem(memory, 0x0000, listOf(0x86, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertEquals(0x00, mpu.X) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STX Zero Page, Y-Indexed + + @Test + fun test_stx_zp_y_indexed_stores_x_leaves_x_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.X = 0xFF + mpu.Y = 0x03 + // $0000 STX $0010,Y + writeMem(memory, 0x0000, listOf(0x96, 0x10)) + memory[0x0010 + mpu.Y] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010 + mpu.Y]) + assertEquals(0xFF, mpu.X) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_stx_zp_y_indexed_stores_x_leaves_x_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.X = 0x00 + mpu.Y = 0x03 + // $0000 STX $0010,Y + writeMem(memory, 0x0000, listOf(0x96, 0x10)) + memory[0x0010 + mpu.Y] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.Y]) + assertEquals(0x00, mpu.X) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STY Absolute + + @Test + fun test_sty_absolute_stores_y_leaves_y_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.Y = 0xFF + // $0000 STY $ABCD + writeMem(memory, 0x0000, listOf(0x8C, 0xCD, 0xAB)) + memory[0xABCD] = 0x00 + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0xFF, memory[0xABCD]) + assertEquals(0xFF, mpu.Y) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sty_absolute_stores_y_leaves_y_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.Y = 0x00 + // $0000 STY $ABCD + writeMem(memory, 0x0000, listOf(0x8C, 0xCD, 0xAB)) + memory[0xABCD] = 0xFF + mpu.step() + assertEquals(0x0003, mpu.PC) + assertEquals(0x00, memory[0xABCD]) + assertEquals(0x00, mpu.Y) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STY Zero Page + + @Test + fun test_sty_zp_stores_y_leaves_y_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.Y = 0xFF + // $0000 STY $0010 + writeMem(memory, 0x0000, listOf(0x84, 0x10)) + memory[0x0010] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010]) + assertEquals(0xFF, mpu.Y) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sty_zp_stores_y_leaves_y_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.Y = 0x00 + // $0000 STY $0010 + writeMem(memory, 0x0000, listOf(0x84, 0x10)) + memory[0x0010] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010]) + assertEquals(0x00, mpu.Y) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // STY Zero Page, X-Indexed + + @Test + fun test_sty_zp_x_indexed_stores_y_leaves_y_and_n_flag_unchanged() { + val flags = 0xFF and fNEGATIVE.inv() + mpu.Status.fromByte(flags) + mpu.Y = 0xFF + mpu.X = 0x03 + // $0000 STY $0010,X + writeMem(memory, 0x0000, listOf(0x94, 0x10)) + memory[0x0010 + mpu.X] = 0x00 + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0xFF, memory[0x0010 + mpu.X]) + assertEquals(0xFF, mpu.Y) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + @Test + fun test_sty_zp_x_indexed_stores_y_leaves_y_and_z_flag_unchanged() { + val flags = 0xFF and fZERO.inv() + mpu.Status.fromByte(flags) + mpu.Y = 0x00 + mpu.X = 0x03 + // $0000 STY $0010,X + writeMem(memory, 0x0000, listOf(0x94, 0x10)) + memory[0x0010 + mpu.X] = 0xFF + mpu.step() + assertEquals(0x0002, mpu.PC) + assertEquals(0x00, memory[0x0010 + mpu.X]) + assertEquals(0x00, mpu.Y) + assertEquals(flags, mpu.Status.asByte().toInt()) + } + + // TAX + + @Test + fun test_tax_transfers_accumulator_into_x() { + mpu.A = 0xAB + mpu.X = 0x00 + // $0000 TAX + memory[0x0000] = 0xAA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.A) + assertEquals(0xAB, mpu.X) + } + + @Test + fun test_tax_sets_negative_flag() { + mpu.Status.N = false + mpu.A = 0x80 + mpu.X = 0x00 + // $0000 TAX + memory[0x0000] = 0xAA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + } + + @Test + fun test_tax_sets_zero_flag() { + mpu.Status.Z = false + mpu.A = 0x00 + mpu.X = 0xFF + // $0000 TAX + memory[0x0000] = 0xAA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + } + + // TAY + + @Test + fun test_tay_transfers_accumulator_into_y() { + mpu.A = 0xAB + mpu.Y = 0x00 + // $0000 TAY + memory[0x0000] = 0xA8 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.A) + assertEquals(0xAB, mpu.Y) + } + + @Test + fun test_tay_sets_negative_flag() { + mpu.Status.N = false + mpu.A = 0x80 + mpu.Y = 0x00 + // $0000 TAY + memory[0x0000] = 0xA8 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + } + + @Test + fun test_tay_sets_zero_flag() { + mpu.Status.Z = false + mpu.A = 0x00 + mpu.Y = 0xFF + // $0000 TAY + memory[0x0000] = 0xA8 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + } + + // TSX + + @Test + fun test_tsx_transfers_stack_pointer_into_x() { + mpu.SP = 0xAB + mpu.X = 0x00 + // $0000 TSX + memory[0x0000] = 0xBA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.SP) + assertEquals(0xAB, mpu.X) + } + + @Test + fun test_tsx_sets_negative_flag() { + mpu.Status.N = false + mpu.SP = 0x80 + mpu.X = 0x00 + // $0000 TSX + memory[0x0000] = 0xBA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.SP) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + } + + @Test + fun test_tsx_sets_zero_flag() { + mpu.Status.Z = false + mpu.SP = 0x00 + mpu.Y = 0xFF + // $0000 TSX + memory[0x0000] = 0xBA + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.SP) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + } + + // TXA + + @Test + fun test_txa_transfers_x_into_a() { + mpu.X = 0xAB + mpu.A = 0x00 + // $0000 TXA + memory[0x0000] = 0x8A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.A) + assertEquals(0xAB, mpu.X) + } + + @Test + fun test_txa_sets_negative_flag() { + mpu.Status.N = false + mpu.X = 0x80 + mpu.A = 0x00 + // $0000 TXA + memory[0x0000] = 0x8A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertEquals(0x80, mpu.X) + assertTrue(mpu.Status.N) + } + + @Test + fun test_txa_sets_zero_flag() { + mpu.Status.Z = false + mpu.X = 0x00 + mpu.A = 0xFF + // $0000 TXA + memory[0x0000] = 0x8A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.A) + assertEquals(0x00, mpu.X) + assertTrue(mpu.Status.Z) + } + + // TXS + + @Test + fun test_txs_transfers_x_into_stack_pointer() { + mpu.X = 0xAB + // $0000 TXS + memory[0x0000] = 0x9A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.SP) + assertEquals(0xAB, mpu.X) + } + + @Test + fun test_txs_does_not_set_negative_flag() { + mpu.Status.N = false + mpu.X = 0x80 + // $0000 TXS + memory[0x0000] = 0x9A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.SP) + assertEquals(0x80, mpu.X) + assertFalse(mpu.Status.N) + } + + @Test + fun test_txs_does_not_set_zero_flag() { + mpu.Status.Z = false + mpu.X = 0x00 + // $0000 TXS + memory[0x0000] = 0x9A + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x00, mpu.SP) + assertEquals(0x00, mpu.X) + assertFalse(mpu.Status.Z) + } + + // TYA + + @Test + fun test_tya_transfers_y_into_a() { + mpu.Y = 0xAB + mpu.A = 0x00 + // $0000 TYA + memory[0x0000] = 0x98 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0xAB, mpu.A) + assertEquals(0xAB, mpu.Y) + } + + @Test + fun test_tya_sets_negative_flag() { + mpu.Status.N = false + mpu.Y = 0x80 + mpu.A = 0x00 + // $0000 TYA + memory[0x0000] = 0x98 + mpu.step() + assertEquals(0x0001, mpu.PC) + assertEquals(0x80, mpu.A) + assertEquals(0x80, mpu.Y) + assertTrue(mpu.Status.N) + } + + @Test + fun test_tya_sets_zero_flag() { + mpu.Status.Z = false + mpu.Y = 0x00 + mpu.A = 0xFF + // $0000 TYA + memory[0x0000] = 0x98 + mpu.step() + assertEquals(0x00, mpu.A) + assertEquals(0x00, mpu.Y) + assertTrue(mpu.Status.Z) + assertEquals(0x0001, mpu.PC) + } + + @Test + fun test_brk_interrupt() { + writeMem(memory, 0xFFFE, listOf(0x00, 0x04)) + + writeMem(memory, 0x0000, listOf(0xA9, 0x01, // LDA #$01 + 0x00, 0xEA, // BRK + skipped byte + 0xEA, 0xEA, // NOP, NOP + 0xA9, 0x03)) // LDA #$03 + + writeMem(memory, 0x0400, listOf(0xA9, 0x02, // LDA #$02 + 0x40)) // RTI + + mpu.step() // LDA #$01 + assertEquals(0x01, mpu.A) + assertEquals(0x0002, mpu.PC) + mpu.step() // BRK + assertEquals(0x0400, mpu.PC) + mpu.step() // LDA #$02 + assertEquals(0x02, mpu.A) + assertEquals(0x0402, mpu.PC) + mpu.step() // RTI + + assertEquals(0x0004, mpu.PC) + mpu.step() // A NOP + mpu.step() // The second NOP + + mpu.step() // LDA #$03 + assertEquals(0x03, mpu.A) + assertEquals(0x0008, mpu.PC) + } +} diff --git a/sim65/test/DisassemTest.kt b/sim65/test/TestDisassembler.kt similarity index 87% rename from sim65/test/DisassemTest.kt rename to sim65/test/TestDisassembler.kt index 21ae406f1..3b5029c9f 100644 --- a/sim65/test/DisassemTest.kt +++ b/sim65/test/TestDisassembler.kt @@ -10,15 +10,15 @@ class TestDisassembler { fun testDisassembleAllOpcodes() { val cpu = Cpu6502(true) val memory = Ram(0, 0xffff) - memory.load("test/testfiles/disassem_instr_test.prg", 0x1000-2) + memory.load("test/testfiles/disassem_instr_test.prg", 0x1000 - 2) val result = cpu.disassemble(memory, 0x1000, 0x1221) assertEquals(256, result.size) assertEquals("\$1000 69 01 adc #\$01", result[0]) val reference = File("test/testfiles/disassem_ref_output.txt").readLines() assertEquals(256, reference.size) - for(line in result.zip(reference)) { - if(line.first!=line.second) { + for (line in result.zip(reference)) { + if (line.first != line.second) { fail("disassembled instruction mismatch: '${line.first}', expected '${line.second}'") } }