mirror of
https://github.com/irmen/ksim65.git
synced 2024-06-06 22:29:33 +00:00
implemented rest of the missing 65c02 instructions
This commit is contained in:
parent
ed9fd58d3b
commit
b4d4d1b381
|
@ -7,7 +7,7 @@ package razorvine.ksim65.components
|
|||
/**
|
||||
* 6502 cpu simulation (the NMOS version) including the 'illegal' opcodes.
|
||||
*/
|
||||
open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
||||
open class Cpu6502(private val stopOnBrk: Boolean = false) : BusComponent() {
|
||||
var tracing: Boolean = false
|
||||
var totalCycles: Long = 0
|
||||
protected set
|
||||
|
@ -118,9 +118,7 @@ open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
|||
return if (msb == 0 && allowSingleByte)
|
||||
hexB(lsb)
|
||||
else
|
||||
hexB(msb) + hexB(
|
||||
lsb
|
||||
)
|
||||
hexB(msb) + hexB(lsb)
|
||||
}
|
||||
|
||||
internal fun hexB(number: Short): String = hexB(number.toInt())
|
||||
|
@ -144,9 +142,7 @@ open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
|||
|
||||
while (address <= (to - baseAddress)) {
|
||||
val byte = memory[address]
|
||||
var line = "\$${hexW(address)} ${hexB(
|
||||
byte
|
||||
)} "
|
||||
var line = "\$${hexW(address)} ${hexB(byte)} "
|
||||
address++
|
||||
val opcode = instructions[byte.toInt()]
|
||||
when (opcode.mode) {
|
||||
|
@ -158,39 +154,42 @@ open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
|||
}
|
||||
AddrMode.Imm -> {
|
||||
val value = memory[address++]
|
||||
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(
|
||||
value
|
||||
)}"
|
||||
line += "${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}"
|
||||
}
|
||||
AddrMode.Zp -> {
|
||||
val zpAddr = memory[address++]
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(
|
||||
zpAddr
|
||||
)}"
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}"
|
||||
}
|
||||
AddrMode.Zpr -> {
|
||||
// addressing mode used by the 65C02 only
|
||||
TODO("disassemble ZPR addressing mode")
|
||||
// addressing mode used by the 65C02, put here for convenience
|
||||
val zpAddr = memory[address++]
|
||||
val rel = memory[address++]
|
||||
val target =
|
||||
if (rel <= 0x7f)
|
||||
address + rel
|
||||
else
|
||||
address - (256 - rel)
|
||||
line += "${hexB(zpAddr)} ${hexB(rel)} $spacing3 ${opcode.mnemonic} \$${hexB(zpAddr)}, \$${hexW(target, true)}"
|
||||
}
|
||||
AddrMode.Izp -> {
|
||||
// addressing mode used by the 65C02 only
|
||||
TODO("disassemble IZP addressing mode")
|
||||
// addressing mode used by the 65C02, put here for convenience
|
||||
val zpAddr = memory[address++]
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$(${hexB(zpAddr)})"
|
||||
}
|
||||
AddrMode.IaX -> {
|
||||
// addressing mode used by the 65C02 only
|
||||
TODO("disassemble IaX addressing mode")
|
||||
// addressing mode used by the 65C02, put here for convenience
|
||||
val lo = memory[address++]
|
||||
val hi = memory[address++]
|
||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$(${hexW(absAddr)},x)"
|
||||
}
|
||||
AddrMode.ZpX -> {
|
||||
val zpAddr = memory[address++]
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(
|
||||
zpAddr
|
||||
)},x"
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x"
|
||||
}
|
||||
AddrMode.ZpY -> {
|
||||
val zpAddr = memory[address++]
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(
|
||||
zpAddr
|
||||
)},y"
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y"
|
||||
}
|
||||
AddrMode.Rel -> {
|
||||
val rel = memory[address++]
|
||||
|
@ -199,56 +198,39 @@ open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
|||
address + rel
|
||||
else
|
||||
address - (256 - rel)
|
||||
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(
|
||||
target,
|
||||
true
|
||||
)}"
|
||||
line += "${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target, true)}"
|
||||
}
|
||||
AddrMode.Abs -> {
|
||||
val lo = memory[address++]
|
||||
val hi = memory[address++]
|
||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||
line += "${hexB(lo)} ${hexB(
|
||||
hi
|
||||
)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
|
||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}"
|
||||
}
|
||||
AddrMode.AbsX -> {
|
||||
val lo = memory[address++]
|
||||
val hi = memory[address++]
|
||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||
line += "${hexB(lo)} ${hexB(
|
||||
hi
|
||||
)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
|
||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x"
|
||||
}
|
||||
AddrMode.AbsY -> {
|
||||
val lo = memory[address++]
|
||||
val hi = memory[address++]
|
||||
val absAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||
line += "${hexB(lo)} ${hexB(
|
||||
hi
|
||||
)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
|
||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y"
|
||||
}
|
||||
AddrMode.Ind -> {
|
||||
val lo = memory[address++]
|
||||
val hi = memory[address++]
|
||||
val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
|
||||
line += "${hexB(lo)} ${hexB(
|
||||
hi
|
||||
)} $spacing3 ${opcode.mnemonic} (\$${hexW(
|
||||
indirectAddr
|
||||
)})"
|
||||
line += "${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})"
|
||||
}
|
||||
AddrMode.IzX -> {
|
||||
val zpAddr = memory[address++]
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(
|
||||
zpAddr
|
||||
)},x)"
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)"
|
||||
}
|
||||
AddrMode.IzY -> {
|
||||
val zpAddr = memory[address++]
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(
|
||||
zpAddr
|
||||
)}),y"
|
||||
line += "${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y"
|
||||
}
|
||||
}
|
||||
result.add(line)
|
||||
|
@ -300,11 +282,7 @@ open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
|||
}
|
||||
|
||||
if (stopOnBrk && currentOpcode == 0) {
|
||||
throw InstructionError(
|
||||
"stopped on BRK instruction at ${hexW(
|
||||
PC
|
||||
)}"
|
||||
)
|
||||
throw InstructionError("stopped on BRK instruction at ${hexW(PC)}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1472,9 +1450,7 @@ open class Cpu6502(private val stopOnBrk: Boolean) : BusComponent() {
|
|||
// invalid instruction (JAM / KIL)
|
||||
private fun iInvalid() {
|
||||
throw InstructionError(
|
||||
"invalid instruction encountered: opcode=${hexB(
|
||||
currentOpcode
|
||||
)} instr=${currentInstruction.mnemonic}"
|
||||
"invalid instruction encountered: opcode=${hexB(currentOpcode)} instr=${currentInstruction.mnemonic}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package razorvine.ksim65.components
|
|||
/**
|
||||
* 65C02 cpu simulation (the CMOS version of the 6502).
|
||||
*/
|
||||
class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
||||
class Cpu65C02(stopOnBrk: Boolean = false) : Cpu6502(stopOnBrk) {
|
||||
|
||||
enum class Wait {
|
||||
Normal,
|
||||
|
@ -53,6 +53,9 @@ class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
|||
}
|
||||
}
|
||||
|
||||
// branch-relative address fetched by the ZpR addressing mode
|
||||
private var fetchedAddressZpr: Address = 0
|
||||
|
||||
override fun applyAddressingMode(addrMode: AddrMode) {
|
||||
when (addrMode) {
|
||||
AddrMode.Imp, AddrMode.Acc, AddrMode.Imm,
|
||||
|
@ -62,12 +65,23 @@ class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
|||
super.applyAddressingMode(addrMode)
|
||||
}
|
||||
AddrMode.Ind -> {
|
||||
// not able to fetch an address which crosses the page boundary (6502, fixed in 65C02)
|
||||
TODO("IND addrmode fixed for 65c02")
|
||||
var lo = readPc()
|
||||
var hi = readPc()
|
||||
fetchedAddress = lo or (hi shl 8)
|
||||
// 65c02 doesn't have the page bug of the 6502
|
||||
lo = read(fetchedAddress)
|
||||
hi = read(fetchedAddress + 1)
|
||||
fetchedAddress = lo or (hi shl 8)
|
||||
}
|
||||
AddrMode.Zpr -> {
|
||||
// addressing mode used by the 65C02 only
|
||||
TODO("ZPR addressing mode")
|
||||
// combination of zp addresssing + relative branch addressing
|
||||
fetchedAddress = readPc()
|
||||
val relative = readPc()
|
||||
fetchedAddressZpr = if (relative >= 0x80) {
|
||||
PC - (256 - relative) and 0xffff
|
||||
} else
|
||||
PC + relative and 0xffff
|
||||
}
|
||||
AddrMode.Izp -> {
|
||||
// addressing mode used by the 65C02 only
|
||||
|
@ -463,7 +477,7 @@ class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
|||
/* 69 */ Instruction("adc", AddrMode.Imm, 2),
|
||||
/* 6a */ Instruction("ror", AddrMode.Acc, 2),
|
||||
/* 6b */ Instruction("nop", AddrMode.Imp, 1),
|
||||
/* 6c */ Instruction("jmp", AddrMode.Ind, 5),
|
||||
/* 6c */ Instruction("jmp", AddrMode.Ind, 6),
|
||||
/* 6d */ Instruction("adc", AddrMode.Abs, 4),
|
||||
/* 6e */ Instruction("ror", AddrMode.Abs, 6),
|
||||
/* 6f */ Instruction("bbr6", AddrMode.Zpr, 5),
|
||||
|
@ -632,21 +646,19 @@ class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
|||
pendingInterrupt = null
|
||||
}
|
||||
|
||||
// TODO some opcodes gained a new addressing mode that we have to deal with
|
||||
|
||||
override fun iDec() {
|
||||
if(currentInstruction.mode==AddrMode.Acc) {
|
||||
TODO("dec A")
|
||||
// Status.Z = data == 0
|
||||
// Status.N = (data and 0b10000000) != 0
|
||||
A = (A - 1) and 0xff
|
||||
Status.Z = A == 0
|
||||
Status.N = (A and 0b10000000) != 0
|
||||
} else super.iDec()
|
||||
}
|
||||
|
||||
override fun iInc() {
|
||||
if(currentInstruction.mode==AddrMode.Acc) {
|
||||
TODO("inc A")
|
||||
// Status.Z = data == 0
|
||||
// Status.N = (data and 0b10000000) != 0
|
||||
A = (A + 1) and 0xff
|
||||
Status.Z = A == 0
|
||||
Status.N = (A and 0b10000000) != 0
|
||||
} else super.iInc()
|
||||
}
|
||||
|
||||
|
@ -656,11 +668,15 @@ class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
|||
}
|
||||
|
||||
private fun iTrb() {
|
||||
TODO("trb")
|
||||
val m = read(fetchedAddress)
|
||||
Status.Z = m and A ==0
|
||||
write(fetchedAddress, m and A.inv())
|
||||
}
|
||||
|
||||
private fun iTsb() {
|
||||
TODO("tsb")
|
||||
val m = read(fetchedAddress)
|
||||
Status.Z = m and A ==0
|
||||
write(fetchedAddress, m or A)
|
||||
}
|
||||
|
||||
private fun iStz() {
|
||||
|
@ -696,133 +712,178 @@ class Cpu65C02(stopOnBrk: Boolean) : Cpu6502(stopOnBrk) {
|
|||
}
|
||||
|
||||
private fun iBbr0() {
|
||||
TODO("brr0")
|
||||
val x = hexB(2)
|
||||
val y = hexW(2)
|
||||
val z = hexB(3.toShort())
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 1 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr1() {
|
||||
TODO("brr1")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 2 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr2() {
|
||||
TODO("brr2")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 4 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr3() {
|
||||
TODO("brr3")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 8 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr4() {
|
||||
TODO("brr4")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 16 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr5() {
|
||||
TODO("brr5")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 32 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr6() {
|
||||
TODO("brr6")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 64 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbr7() {
|
||||
TODO("brr7")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 128 == 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs0() {
|
||||
TODO("bbs0")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 1 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs1() {
|
||||
TODO("bbs1")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 2 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs2() {
|
||||
TODO("bbs2")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 4 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs3() {
|
||||
TODO("bbs3")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 8 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs4() {
|
||||
TODO("bbs4")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 16 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs5() {
|
||||
TODO("bbs5")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 32 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs6() {
|
||||
TODO("bbs6")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 64 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iBbs7() {
|
||||
TODO("bbs7")
|
||||
val data = read(fetchedAddress)
|
||||
if(data and 128 != 0)
|
||||
PC = fetchedAddressZpr
|
||||
}
|
||||
|
||||
private fun iSmb0() {
|
||||
TODO("smb0")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 1)
|
||||
}
|
||||
|
||||
private fun iSmb1() {
|
||||
TODO("smb1")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 2)
|
||||
}
|
||||
|
||||
private fun iSmb2() {
|
||||
TODO("smb2")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 4)
|
||||
}
|
||||
|
||||
private fun iSmb3() {
|
||||
TODO("smb3")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 8)
|
||||
}
|
||||
|
||||
private fun iSmb4() {
|
||||
TODO("smb4")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 16)
|
||||
}
|
||||
|
||||
private fun iSmb5() {
|
||||
TODO("smb5")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 32)
|
||||
}
|
||||
|
||||
private fun iSmb6() {
|
||||
TODO("smb6")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 64)
|
||||
}
|
||||
|
||||
private fun iSmb7() {
|
||||
TODO("smb7")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data or 128)
|
||||
}
|
||||
|
||||
private fun iRmb0() {
|
||||
TODO("rmb0")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b11111110)
|
||||
}
|
||||
|
||||
private fun iRmb1() {
|
||||
TODO("rmb1")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b11111101)
|
||||
}
|
||||
|
||||
private fun iRmb2() {
|
||||
TODO("rmb2")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b11111011)
|
||||
}
|
||||
|
||||
private fun iRmb3() {
|
||||
TODO("rmb3")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b11110111)
|
||||
}
|
||||
|
||||
private fun iRmb4() {
|
||||
TODO("rmb4")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b11101111)
|
||||
}
|
||||
|
||||
private fun iRmb5() {
|
||||
TODO("rmb5")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b11011111)
|
||||
}
|
||||
|
||||
private fun iRmb6() {
|
||||
TODO("rmb6")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b10111111)
|
||||
}
|
||||
|
||||
private fun iRmb7() {
|
||||
TODO("rmb7")
|
||||
val data = read(fetchedAddress)
|
||||
write(fetchedAddress, data and 0b01111111)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package razorvine.ksim65.components
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAddress, endAddress) {
|
||||
private val memory = ShortArray(endAddress - startAddress + 1)
|
||||
|
@ -44,12 +45,23 @@ class Ram(startAddress: Address, endAddress: Address) : MemoryComponent(startAdd
|
|||
*/
|
||||
fun load(filename: String, address: Address) {
|
||||
val bytes = File(filename).readBytes()
|
||||
bytes.forEachIndexed { index, byte ->
|
||||
load(bytes, address)
|
||||
}
|
||||
|
||||
fun load(source: URL, address: Address) {
|
||||
val bytes = source.readBytes()
|
||||
load(bytes, address)
|
||||
}
|
||||
|
||||
fun load(data: Array<UByte>, address: Address) =
|
||||
data.forEachIndexed { index, byte -> memory[address + index] = byte }
|
||||
|
||||
fun load(data: ByteArray, address: Address) =
|
||||
data.forEachIndexed { index, byte ->
|
||||
memory[address + index] =
|
||||
if (byte >= 0)
|
||||
byte.toShort()
|
||||
else
|
||||
(256 + byte).toShort()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,8 @@ class Test6502CpuBasics {
|
|||
val ram = Ram(0, 0xffff)
|
||||
ram[Cpu6502.RESET_vector] = 0x00
|
||||
ram[Cpu6502.RESET_vector +1] = 0x10
|
||||
ram.load("src/test/kotlin/testfiles/bcdtest.bin", 0x1000)
|
||||
val bytes = javaClass.getResource("bcdtest.bin")!!
|
||||
ram.load(bytes, 0x1000)
|
||||
bus.add(ram)
|
||||
bus.reset()
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import kotlin.test.*
|
|||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
|
||||
@Disabled("this test suite takes a long time")
|
||||
//@Disabled("this test suite takes a long time")
|
||||
class Test6502TestSuite {
|
||||
|
||||
val cpu: Cpu6502 = Cpu6502(stopOnBrk = false)
|
||||
|
@ -46,7 +46,7 @@ class Test6502TestSuite {
|
|||
}
|
||||
|
||||
private fun runTest(testprogram: String) {
|
||||
ram.loadPrg("test/6502testsuite/$testprogram")
|
||||
ram.loadPrg("src/test/kotlin/6502testsuite/$testprogram")
|
||||
bus.reset()
|
||||
cpu.SP = 0xfd
|
||||
cpu.Status.fromByte(0b00100100)
|
||||
|
|
|
@ -797,7 +797,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB0 $43
|
||||
writeMem(memory, 0x0000, listOf(0x07, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -822,7 +822,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB1 $43
|
||||
writeMem(memory, 0x0000, listOf(0x17, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -848,7 +848,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB2 $43
|
||||
writeMem(memory, 0x0000, listOf(0x27, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -873,7 +873,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB3 $43
|
||||
writeMem(memory, 0x0000, listOf(0x37, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -898,7 +898,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB4 $43
|
||||
writeMem(memory, 0x0000, listOf(0x47, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -923,7 +923,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB5 $43
|
||||
writeMem(memory, 0x0000, listOf(0x57, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -948,7 +948,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB6 $43
|
||||
writeMem(memory, 0x0000, listOf(0x67, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -974,7 +974,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b11111111
|
||||
// $0000 RMB7 $43
|
||||
writeMem(memory, 0x0000, listOf(0x77, 0x43))
|
||||
val expected = 0b01010101
|
||||
val expected = 0b01110101
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1027,7 +1027,7 @@ class Test65C02 : TestCommon6502() {
|
|||
writeMem(memory, 0x0000, listOf(0x87, 0x43))
|
||||
mpu.step()
|
||||
assertEquals(0x0002, mpu.PC)
|
||||
assertEquals(5, mpu.totalCycles.toInt())
|
||||
assertEquals(5 + Cpu65C02.resetCycles, mpu.totalCycles.toInt())
|
||||
val expected = 0b00000001
|
||||
assertEquals(expected, memory[0x0043].toInt())
|
||||
}
|
||||
|
@ -1037,7 +1037,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB0 $43
|
||||
writeMem(memory, 0x0000, listOf(0x87, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1062,7 +1062,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB1 $43
|
||||
writeMem(memory, 0x0000, listOf(0x97, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1088,7 +1088,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB2 $43
|
||||
writeMem(memory, 0x0000, listOf(0xA7, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1114,7 +1114,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB3 $43
|
||||
writeMem(memory, 0x0000, listOf(0xB7, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1140,7 +1140,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB4 $43
|
||||
writeMem(memory, 0x0000, listOf(0xC7, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1165,7 +1165,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB5 $43
|
||||
writeMem(memory, 0x0000, listOf(0xD7, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1190,7 +1190,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB6 $43
|
||||
writeMem(memory, 0x0000, listOf(0xE7, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1215,7 +1215,7 @@ class Test65C02 : TestCommon6502() {
|
|||
memory[0x0043] = 0b00000000
|
||||
// $0000 SMB7 $43
|
||||
writeMem(memory, 0x0000, listOf(0xF7, 0x43))
|
||||
val expected = 0b11001100
|
||||
val expected = 0b11101100
|
||||
mpu.Status.fromByte(expected)
|
||||
mpu.step()
|
||||
assertEquals(expected, mpu.Status.asByte().toInt())
|
||||
|
@ -1518,7 +1518,7 @@ class Test65C02 : TestCommon6502() {
|
|||
writeMem(memory, 0x0000, listOf(0x80, 0x10))
|
||||
mpu.step()
|
||||
assertEquals(0x12, mpu.PC)
|
||||
assertEquals(2 + Cpu65C02.resetCycles, mpu.totalCycles.toInt())
|
||||
assertEquals(3 + Cpu65C02.resetCycles, mpu.totalCycles.toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1545,4 +1545,63 @@ class Test65C02 : TestCommon6502() {
|
|||
assertEquals(0x0205, mpu.PC)
|
||||
assertEquals(3 + Cpu65C02.resetCycles, mpu.totalCycles.toInt())
|
||||
}
|
||||
|
||||
// BBR and BBS
|
||||
@Test
|
||||
fun test_bbr_all_set_doesnt_branch() {
|
||||
mpu as Cpu65C02
|
||||
mpu.PC = 0
|
||||
memory[0xfe] = 0xff
|
||||
writeMem(memory, 0, listOf(0x0f, 0xfe, 0x40,
|
||||
0x1f, 0xfe, 0x40,
|
||||
0x2f, 0xfe, 0x40,
|
||||
0x3f, 0xfe, 0x40,
|
||||
0x4f, 0xfe, 0x40,
|
||||
0x5f, 0xfe, 0x40,
|
||||
0x6f, 0xfe, 0x40,
|
||||
0x7f, 0xfe, 0x40,
|
||||
0xea))
|
||||
repeat(8) { mpu.step() }
|
||||
assertNotEquals(0x0040, mpu.PC)
|
||||
assertEquals(0xea, memory[mpu.PC])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_bbr_branches() {
|
||||
mpu as Cpu65C02
|
||||
mpu.PC = 0
|
||||
memory[0xfe] = 0b10111111 // bit 6 cleared
|
||||
writeMem(memory, 0, listOf(0x6f, 0xfe, 0x40)) // BBR6 $fe, $0040
|
||||
mpu.step()
|
||||
assertEquals(0x0043, mpu.PC)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_bbs_all_clear_doesnt_branch() {
|
||||
mpu as Cpu65C02
|
||||
mpu.PC = 0
|
||||
memory[0xfe] = 0
|
||||
writeMem(memory, 0, listOf(0x8f, 0xfe, 0x40,
|
||||
0x9f, 0xfe, 0x40,
|
||||
0xaf, 0xfe, 0x40,
|
||||
0xbf, 0xfe, 0x40,
|
||||
0xcf, 0xfe, 0x40,
|
||||
0xdf, 0xfe, 0x40,
|
||||
0xef, 0xfe, 0x40,
|
||||
0xff, 0xfe, 0x40,
|
||||
0xea))
|
||||
repeat(8) { mpu.step() }
|
||||
assertNotEquals(0x0040, mpu.PC)
|
||||
assertEquals(0xea, memory[mpu.PC])
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_bbs_branches() {
|
||||
mpu as Cpu65C02
|
||||
mpu.PC = 0
|
||||
memory[0xfe] = 0b01000000 // bit 6 set
|
||||
writeMem(memory, 0, listOf(0xef, 0xfe, 0x40)) // BBS6 $fe, $0040
|
||||
mpu.step()
|
||||
assertEquals(0x0043, mpu.PC)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
import razorvine.ksim65.components.Cpu6502
|
||||
import razorvine.ksim65.components.Cpu65C02
|
||||
import razorvine.ksim65.components.Ram
|
||||
import java.io.File
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
class TestDisassembler {
|
||||
|
||||
@Test
|
||||
fun testDisassembleAllOpcodes() {
|
||||
val cpu = Cpu6502(true)
|
||||
fun testDisassembleAll6502Opcodes() {
|
||||
val cpu = Cpu6502()
|
||||
val memory = Ram(0, 0xffff)
|
||||
memory.load("src/test/kotlin/testfiles/disassem_instr_test.prg", 0x1000 - 2)
|
||||
val binfile = javaClass.classLoader.getResourceAsStream("disassem_instr_test.prg")?.readAllBytes()!!
|
||||
memory.load(binfile, 0x1000-2)
|
||||
val result = cpu.disassemble(memory, 0x1000, 0x1221)
|
||||
assertEquals(256, result.size)
|
||||
assertEquals("\$1000 69 01 adc #\$01", result[0])
|
||||
|
||||
val reference = File("src/test/kotlin/testfiles/disassem_ref_output.txt").readLines()
|
||||
val reference = javaClass.classLoader.getResource("disassem_ref_output.txt")?.readText()!!.trim().lines()
|
||||
assertEquals(256, reference.size)
|
||||
for (line in result.zip(reference)) {
|
||||
if (line.first != line.second) {
|
||||
|
@ -24,6 +25,75 @@ class TestDisassembler {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisassembleRockwell65C02() {
|
||||
val cpu = Cpu65C02()
|
||||
val memory = Ram(0, 0x1000)
|
||||
val source = javaClass.classLoader.getResource("disassem_r65c02.bin")!!
|
||||
memory.load(source, 0)
|
||||
val resultLines = cpu.disassemble(memory, 0x0000, 0x0050)
|
||||
val result = resultLines.joinToString("\n")
|
||||
assertEquals("""${'$'}0000 07 12 rmb0 ${'$'}12
|
||||
${'$'}0002 17 12 rmb1 ${'$'}12
|
||||
${'$'}0004 27 12 rmb2 ${'$'}12
|
||||
${'$'}0006 37 12 rmb3 ${'$'}12
|
||||
${'$'}0008 47 12 rmb4 ${'$'}12
|
||||
${'$'}000a 57 12 rmb5 ${'$'}12
|
||||
${'$'}000c 67 12 rmb6 ${'$'}12
|
||||
${'$'}000e 77 12 rmb7 ${'$'}12
|
||||
${'$'}0010 87 12 smb0 ${'$'}12
|
||||
${'$'}0012 97 12 smb1 ${'$'}12
|
||||
${'$'}0014 a7 12 smb2 ${'$'}12
|
||||
${'$'}0016 b7 12 smb3 ${'$'}12
|
||||
${'$'}0018 c7 12 smb4 ${'$'}12
|
||||
${'$'}001a d7 12 smb5 ${'$'}12
|
||||
${'$'}001c e7 12 smb6 ${'$'}12
|
||||
${'$'}001e f7 12 smb7 ${'$'}12
|
||||
${'$'}0020 0f 12 2a bbr0 ${'$'}12, ${'$'}4d
|
||||
${'$'}0023 1f 12 27 bbr1 ${'$'}12, ${'$'}4d
|
||||
${'$'}0026 2f 12 24 bbr2 ${'$'}12, ${'$'}4d
|
||||
${'$'}0029 3f 12 21 bbr3 ${'$'}12, ${'$'}4d
|
||||
${'$'}002c 4f 12 1e bbr4 ${'$'}12, ${'$'}4d
|
||||
${'$'}002f 5f 12 1b bbr5 ${'$'}12, ${'$'}4d
|
||||
${'$'}0032 6f 12 18 bbr6 ${'$'}12, ${'$'}4d
|
||||
${'$'}0035 8f 12 15 bbs0 ${'$'}12, ${'$'}4d
|
||||
${'$'}0038 9f 12 12 bbs1 ${'$'}12, ${'$'}4d
|
||||
${'$'}003b af 12 0f bbs2 ${'$'}12, ${'$'}4d
|
||||
${'$'}003e bf 12 0c bbs3 ${'$'}12, ${'$'}4d
|
||||
${'$'}0041 cf 12 09 bbs4 ${'$'}12, ${'$'}4d
|
||||
${'$'}0044 df 12 06 bbs5 ${'$'}12, ${'$'}4d
|
||||
${'$'}0047 ef 12 03 bbs6 ${'$'}12, ${'$'}4d
|
||||
${'$'}004a ff 12 00 bbs7 ${'$'}12, ${'$'}4d
|
||||
${'$'}004d 00 brk
|
||||
${'$'}004e 00 brk
|
||||
${'$'}004f 00 brk
|
||||
${'$'}0050 00 brk""", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisassembleWDC65C02() {
|
||||
val cpu = Cpu65C02()
|
||||
val memory = Ram(0, 0x1000)
|
||||
val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin")!!
|
||||
memory.load(source, 0)
|
||||
val resultLines = cpu.disassemble(memory, 0x0000, 0x0015)
|
||||
val result = resultLines.joinToString("\n")
|
||||
assertEquals("""${'$'}0000 cb wai
|
||||
${'$'}0001 db stp
|
||||
${'$'}0002 3a dec a
|
||||
${'$'}0003 1a inc a
|
||||
${'$'}0004 64 12 stz ${'$'}12
|
||||
${'$'}0006 14 12 trb ${'$'}12
|
||||
${'$'}0008 04 12 tsb ${'$'}12
|
||||
${'$'}000a 72 12 adc ${'$'}(12)
|
||||
${'$'}000c b2 12 lda ${'$'}(12)
|
||||
${'$'}000e 92 12 sta ${'$'}(12)
|
||||
${'$'}0010 7c 00 20 jmp ${'$'}(2000,x)
|
||||
${'$'}0013 00 brk
|
||||
${'$'}0014 00 brk
|
||||
${'$'}0015 00 brk""", result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Binary file not shown.
BIN
src/test/resources/disassem_r65c02.bin
Normal file
BIN
src/test/resources/disassem_r65c02.bin
Normal file
Binary file not shown.
BIN
src/test/resources/disassem_wdc65c02.bin
Normal file
BIN
src/test/resources/disassem_wdc65c02.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user