Saturday, February 16, 2019

Building a Simple trace utility

One of the early development tools for assembly language programmers was a trace tool. This was a simple tool that you ran and it would print out what the program was running at every step of the process. This type of tool could be considered the predecessor to the debugger. My eventual plans are to turn this tool into a debugger eventually. For developing the random number generator libraries I don't need that much of a utility. All that is really needed is the ability to load an assembly language file, assemble it, then run the file printing the command being executed and the state of the registers.
Trace utilities can be thought of as an automated single-step through the program that runs until a break statement is encountered. It is handy for seeing what is happening in non-linear programs such as the following demo:

    LDA #0
LDX #3
loop:
CLC
ADC #3
DEX
BNE loop
BRK

While the above example may be fairly simply to manually figure out, seeing what is happening line-by-line can still be useful for debugging as it is very easy to overlook something obvious, especially if it is a mistake that you have made yourself. It is also very handy for demonstrating what a piece of code is doing, which is also very important for explaining the mathematical functions that I will be writing in the remainder of this chapter.

People who have been following this series from the beginning will remember that I said that an emulator was essentially a disassembler that also had an interpreter attached to it. As we already have a dissasembler, and we have an emulator that can run just an individual instruction while storing the state of the registers in an accessible data class, the actual trace part of the trace utility is simply calling the dissasembler to get the assembly instructions, then running that step, then grabbing the register state and printing the results.

// run until break
m6502.state.ip = 0
var traceString = ""
var ipAddress = m6502.state.ip
while (memoryManager.read( ipAddress ) != 0) {
traceString = "(" + m6502.state.tick +") " +
ipAddress.toString(16) + ":" +
m6502.disassembleStep(m6502.state.ip)
m6502.step()
ipAddress = m6502.state.ip
traceString = traceString + "# A:" + m6502.state.acc.toString(16) +
", X:" + m6502.state.x.toString(16) +
", Y:" + m6502.state.y.toString(16) +
", Flags:" + m6502.state.flags.toString(16)
println(traceString)
}

When this is run on our sample program, we get the following results.

(0) 0:LDA #$0# A:0, X:0, Y:0, Flags:22
(2) 2:LDX #$3# A:0, X:3, Y:0, Flags:20
(4) 4:CLC # A:0, X:3, Y:0, Flags:20
(6) 5:ADC #$3# A:3, X:3, Y:0, Flags:20
(8) 7:DEX # A:3, X:2, Y:0, Flags:20
(10) 8:BNE $4# A:3, X:2, Y:0, Flags:20
(13) 4:CLC # A:3, X:2, Y:0, Flags:20
(15) 5:ADC #$3# A:6, X:2, Y:0, Flags:20
(17) 7:DEX # A:6, X:1, Y:0, Flags:20
(19) 8:BNE $4# A:6, X:1, Y:0, Flags:20
(22) 4:CLC # A:6, X:1, Y:0, Flags:20
(24) 5:ADC #$3# A:9, X:1, Y:0, Flags:20
(26) 7:DEX # A:9, X:0, Y:0, Flags:22
(28) 8:BNE $4# A:9, X:0, Y:0, Flags:22

Not the prettiest of output, but that can be easily tweaked. So with a very tiny amount of work, we now have a useful tool for writing and testing chunks of assembly language. We are now ready to start building the math libraries that we will be needing for writing our random number generators. We will start covering multiplication next fortnight.

No comments:

Post a Comment