mirror of
https://github.com/irmen/prog8.git
synced 2024-12-02 07:49:27 +00:00
1548 lines
44 KiB
Kotlin
1548 lines
44 KiB
Kotlin
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)
|
|
}
|
|
}
|