fixed IZY addressing mode address calc

added test harness for Wolfgang Lorenz's 6502 test suite
This commit is contained in:
Irmen de Jong 2019-09-08 01:23:37 +02:00
parent e61d3df380
commit 651f0ec445
8 changed files with 216 additions and 65 deletions

View File

@ -0,0 +1,45 @@
package sim65
import sim65.components.Address
import sim65.components.Cpu6502
import sim65.components.ICpu
import sim65.components.Ram
object C64KernalStubs {
lateinit var ram: Ram
fun handleBreakpoint(cpu: ICpu, pc: Address) {
cpu as Cpu6502
when(pc) {
0xffd2 -> {
// CHROUT
val char = C64Screencodes.decodeScreencode(listOf(cpu.A.toShort()), true)
if(char=="m")
println()
else
print(char)
cpu.currentOpcode = 0x60 // rts to end the stub
}
0xffe4 -> {
// GETIN
print("[Input required:] ")
val s = readLine()
if(s.isNullOrEmpty())
cpu.A = 0
else
cpu.A = C64Screencodes.encodeScreencode(s, true).first().toInt()
cpu.currentOpcode = 0x60 // rts to end the stub
}
0xe16f -> {
// LOAD/VERIFY
val loc = ram.read(0xbb).toInt() or (ram.read(0xbc).toInt() shl 8)
val len = ram.read(0xb7).toInt()
val filename = C64Screencodes.decodeScreencode((loc until loc+len).map { ram.read(it) }.toList(), true).toLowerCase()
println("\n[loading $filename ...]")
ram.loadPrg("c64tests/$filename")
cpu.PC = 0x0816 // continue in next module
}
}
}
}

View File

@ -1,8 +1,9 @@
package sim65
import kotlinx.cli.*
import sim65.C64KernalStubs.handleBreakpoint
import sim65.components.*
import sim65.components.Cpu6502.Companion.hexB
import sim65.components.Cpu6502.Companion.RESET_vector
import kotlin.system.exitProcess
@ -28,71 +29,38 @@ private fun startSimulator(args: Array<String>) {
exitProcess(1)
}
val bootRom = listOf<UByte>(
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0x00,0x90, // NMI vector
0x00,0x10, // RESET vector
0x00,0xa0 // IRQ vector
).toTypedArray()
val cpu = Cpu6502(enableIllegal)
cpu.tracing = true
val cpu = Cpu6502(enableIllegal, stopOnBrk=true)
cpu.tracing = false
// create the system bus and add device to it.
// note that the order is relevant w.r.t. where reads and writes are going.
val bus = Bus()
bus.add(cpu)
bus.add(Rom(0xff00, 0xffff, bootRom))
bus.add(Parallel(0xd000, 0xd001))
bus.add(Timer(0xd100, 0xd103))
val ram = Ram(0, 0xffff)
ram.set(0xc000, 0xa9) // lda #0
ram.set(0xc001, 0x00)
ram.set(0xc002, 0x85) // sta $02
ram.set(0xc003, 0x02)
ram.set(0xc004, 0x4c) // jmp $0816
ram.set(0xc005, 0x16)
ram.set(0xc006, 0x08)
ram.set(RESET_vector, 0x00)
ram.set(RESET_vector+1, 0xc0)
bus.add(ram)
bus.reset()
ram.loadPrg("c64tests/0start")
C64KernalStubs.ram = ram
ram.load("sim65/test/testfiles/ram.bin", 0x8000)
ram.load("sim65/test/testfiles/bcdtest.bin", 0x1000)
//ram.dump(0x8000, 0x802f)
//cpu.disassemble(ram, 0x8000, 0x802f)
cpu.breakpoint(0xffd2, ::handleBreakpoint)
cpu.breakpoint(0xffe4, ::handleBreakpoint)
cpu.breakpoint(0xe16f, ::handleBreakpoint)
bus.reset()
try {
while (true) {
bus.clock()
}
} catch(e: InstructionError) {
println(">>> INSTRUCTION ERROR: ${e.message}")
}
if(ram.read(0x0400)==0.toShort())
println("BCD TEST: OK!")
else {
val code = ram.read(0x0400)
val v1 = ram.read(0x0401)
val v2 = ram.read(0x0402)
val predictedA = ram.read(0x00fc)
val actualA = ram.read(0x00fd)
val predictedF = ram.read(0x00fe)
val actualF = ram.read(0x00ff)
println("BCD TEST: FAIL!! code=${hexB(code)} value1=${hexB(v1)} value2=${hexB(v2)}")
println(" predictedA=${hexB(predictedA)}")
println(" actualA=${hexB(actualA)}")
println(" predictedF=${predictedF.toString(2).padStart(8,'0')}")
println(" actualF=${actualF.toString(2).padStart(8,'0')}")
}
}

View File

@ -0,0 +1,98 @@
package sim65
import kotlinx.cli.*
import sim65.components.*
import sim65.components.Cpu6502.Companion.hexB
import kotlin.system.exitProcess
fun main(args: Array<String>) {
printSoftwareHeader2()
startSimulator2(args)
}
internal fun printSoftwareHeader2() {
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
println("\nSim65 6502 cpu simulator v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
}
private fun startSimulator2(args: Array<String>) {
val cli = CommandLineInterface("sim65", printHelpByDefault = false)
val enableIllegal by cli.flagArgument("-ill", "enable the illegal instructions")
try {
cli.parse(args)
} catch (e: Exception) {
exitProcess(1)
}
val bootRom = listOf<UByte>(
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0x00,0x90, // NMI vector
0x00,0x10, // RESET vector
0x00,0xa0 // IRQ vector
).toTypedArray()
val cpu = Cpu6502(enableIllegal, true)
cpu.tracing = true
// create the system bus and add device to it.
// note that the order is relevant w.r.t. where reads and writes are going.
val bus = Bus()
bus.add(cpu)
bus.add(Rom(0xff00, 0xffff, bootRom))
bus.add(Parallel(0xd000, 0xd001))
bus.add(Timer(0xd100, 0xd103))
val ram = Ram(0, 0xffff)
bus.add(ram)
bus.reset()
ram.load("sim65/test/testfiles/ram.bin", 0x8000)
ram.load("sim65/test/testfiles/bcdtest.bin", 0x1000)
//ram.dump(0x8000, 0x802f)
//cpu.disassemble(ram, 0x8000, 0x802f)
try {
while (true) {
bus.clock()
}
} catch(e: InstructionError) {
}
if(ram.read(0x0400)==0.toShort())
println("BCD TEST: OK!")
else {
val code = ram.read(0x0400)
val v1 = ram.read(0x0401)
val v2 = ram.read(0x0402)
val predictedA = ram.read(0x00fc)
val actualA = ram.read(0x00fd)
val predictedF = ram.read(0x00fe)
val actualF = ram.read(0x00ff)
println("BCD TEST: FAIL!! code=${hexB(code)} value1=${hexB(v1)} value2=${hexB(v2)}")
println(" predictedA=${hexB(predictedA)}")
println(" actualA=${hexB(actualA)}")
println(" predictedF=${predictedF.toString(2).padStart(8,'0')}")
println(" actualF=${actualF.toString(2).padStart(8,'0')}")
}
}

View File

@ -10,6 +10,7 @@ interface ICpu {
fun clock()
fun reset()
fun step()
fun breakpoint(address: Address, action: (cpu: ICpu, pc: Address) -> Unit)
var tracing: Boolean
val totalCycles: Int
@ -20,7 +21,7 @@ interface ICpu {
// TODO: make a 65c02 variant as well (and re-enable the unit tests for that).
class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu {
class Cpu6502(private val illegalInstrsAllowed: Boolean, private val stopOnBrk: Boolean) : BusComponent(), ICpu {
override var tracing: Boolean = false
override var totalCycles: Int = 0
private set
@ -147,6 +148,12 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu
AddrMode.IzY to ::amIzy
)
private val breakpoints = mutableMapOf<Address, (cpu: ICpu, pc: Address) -> Unit>()
override fun breakpoint(address: Address, action: (cpu: ICpu, pc: Address) -> Unit) {
breakpoints[address] = action
}
override fun disassemble(memory: Array<UByte>, baseAddress: Address, from: Address, to: Address): List<String> {
var address = from - baseAddress
val spacing1 = " "
@ -255,17 +262,29 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu
override fun clock() {
if (instrCycles == 0) {
currentOpcode = read(PC++)
currentOpcode = read(PC)
currentInstruction = opcodes[currentOpcode]
if (tracing) {
printState()
}
if (tracing) printState()
if (!currentInstruction.official && !illegalInstrsAllowed) {
throw InstructionError("illegal instructions not enabled")
}
breakpoints[PC]?.let {
val oldPC = PC
val oldOpcode = currentOpcode
it(this, PC)
if (PC != oldPC)
return clock()
if (oldOpcode != currentOpcode)
currentInstruction = opcodes[currentOpcode]
}
if (currentOpcode == 0 && stopOnBrk) {
throw InstructionError("stopped on BRK instruction at ${hexW(PC)}")
}
PC++
instrCycles = currentInstruction.cycles
addressingModes.getValue(currentInstruction.mode)()
currentInstruction.execute()
@ -342,13 +361,13 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu
private fun amAbsx() {
val lo = readPc()
val hi = readPc()
fetchedAddress = (X + (lo or (hi shl 8))) and 0xffff
fetchedAddress = X + (lo or (hi shl 8)) and 0xffff
}
private fun amAbsy() {
val lo = readPc()
val hi = readPc()
fetchedAddress = (Y + (lo or (hi shl 8))) and 0xffff
fetchedAddress = Y + (lo or (hi shl 8)) and 0xffff
}
private fun amInd() {
@ -381,7 +400,7 @@ class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu
fetchedAddress = readPc()
val lo = read(fetchedAddress)
val hi = read((fetchedAddress + 1) and 255)
fetchedAddress = (Y + lo or (hi shl 8)) and 65535
fetchedAddress = Y + (lo or (hi shl 8)) and 65535
}
private fun getFetched(): Int {

View File

@ -18,12 +18,33 @@ class Ram(startAddress: Address, endAddress: Address): MemMappedComponent(startA
override fun clock() { }
override fun reset() { memory.fill(0) }
override fun reset() {
// contents of RAM doesn't change on a reset
}
fun fill(data: UByte) {
memory.fill(data)
}
/**
* load a c64-style prg program at the given address,
* this file has the load address as the first two bytes.
*/
fun loadPrg(filename: String) {
val bytes = File(filename).readBytes()
val address = (bytes[0].toInt() or (bytes[1].toInt() shl 8)) and 65535
bytes.drop(2).forEachIndexed { index, byte ->
memory[address+index] =
if(byte>=0)
byte.toShort()
else
(256+byte).toShort()
}
}
/**
* load a binary program at the given address
*/
fun load(filename: String, address: Address) {
val bytes = File(filename).readBytes()
bytes.forEachIndexed { index, byte ->

View File

@ -7,7 +7,7 @@ class Test6502CpuBasics {
@Test
fun testCpuFlagsAfterReset() {
val cpu = Cpu6502(true)
val cpu = Cpu6502(true, true)
val bus = Bus()
bus.add(cpu)
cpu.reset()

View File

@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 mpu = Cpu6502(true, 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()

View File

@ -8,7 +8,7 @@ class TestDisassembler {
@Test
fun testDisassembleAllOpcodes() {
val cpu = Cpu6502(true)
val cpu = Cpu6502(true, true)
val memory = Ram(0, 0xffff)
memory.load("test/testfiles/disassem_instr_test.prg", 0x1000 - 2)
val result = cpu.disassemble(memory, 0x1000, 0x1221)