mirror of
https://github.com/irmen/prog8.git
synced 2024-10-25 00:24:16 +00:00
fixed IZY addressing mode address calc
added test harness for Wolfgang Lorenz's 6502 test suite
This commit is contained in:
parent
e61d3df380
commit
651f0ec445
45
sim65/src/C64KernalStubs.kt
Normal file
45
sim65/src/C64KernalStubs.kt
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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')}")
|
||||
}
|
||||
|
||||
}
|
||||
|
98
sim65/src/Sim65MainBCDtest.kt
Normal file
98
sim65/src/Sim65MainBCDtest.kt
Normal 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')}")
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -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 ->
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user