| | oziphantom
Registered: Oct 2014 Posts: 490 |
6502 Static Analyser
I've been working on a python script that I can run over 64tass's output to help me track bugs. At the moment it has the concept of a function, trashing a register or variable or modifying it. This then allows me to put in commands to verify that a function does not trash something.
pressA
;&&trashes a,x,y,Pointer1,Pointer1+1,Pointer2,Pointer2+1
;&&modifies CursorX
lda CursorX
beq KeyboardRoutine._exitPLA
clc
adc #24
tay
jsr restoreVerticalDigitsBar ;&&preserve y
dey
jsr setVerticalDigitsBarToCurrent
dec CursorX
rts
So here the system knows what is trashed. What is modified and assets that `restoreVerticalDigitsBar` does not trash or modify y.
When I wrote `restoreVerticalDigitsBar` it didn't but at some point I might add a feature, re factor the code to save a clock or so, and then trash y, which will give me a really hard to track down bug. Here the Build will catch and error for me.
The system will parse the code and report anything that is trashed that is not reported. It is a little annoying in that a parent function must also list everything its child functions trash, but this keeps it simple and allows you to see the whole tree when you look at just one function. The script also spits out a single HTML file which kind of acts like documentation. It will list name, address, size, what it calls,trashes and modifies. Handy for when you need to look up a function in the Win Vice debugger.
What else do you think would be useful? |
|
| | Digger
Registered: Mar 2005 Posts: 437 |
Have you seen this test framework for C64? http://64bites.com/64spec/
Might be easier to mix in some tests to check if the regs were trashed. |
| | soci
Registered: Sep 2003 Posts: 480 |
This test framework adds some extra code through macros and needs the output to be executed to give results. So 64spec is hardly static.
Was this used by the author on a real world program beyond writing the tests to test itself? Or it's just a proof of concept that it can be done. I'm not sure that it's all that practical until I see it in actual use.
I know at least that oziphantom is eating his own dog food. It's quite light on the amount of instrumentation needed, only some special comments at the right places. |
| | Oswald
Registered: Apr 2002 Posts: 5094 |
anyone has a script for adding breakpoints via comments, exports labels to vice etc ? |
| | soci
Registered: Sep 2003 Posts: 480 |
VICE labels can be had with "--vice-labels --labels=file.l".
I'd create source defined breakpoints using a specially named labels instead of a comments. Example Makefile fragment for compiling and starting VICE with breakpoint(s) set:
64tass -a -B $< -o $@ -L $@.dasm --vice-labels -l $@.l
sed -i 's/^al \([^ ]*\) .*breakpoint/break \1/' $@.l
x64 -moncommands $@.l -autostartprgmode 1 -autostart-warp +truedrive +cart $@
The sed script replaces any "al" line containing "breakpoint" in the label's name with a "break" command. The result looks like this:...
al 9c7 .xp1
break 80e
al 956 .a1
... Something similar could be done on windows as well using gnuwin32 sed or by other means.
But this is slightly off topic. |
| | Skate
Registered: Jul 2003 Posts: 494 |
@soci: win10 now has a built in ubuntu bash. from now on forget about windows builds, we can now directly use original linux sources. finally... :) |
| | Stone
Registered: Oct 2006 Posts: 172 |
This is outside the scope of what OP is talking about, but one thing that would be really neat is assertions implemented not as generated assembly code, but as breakpoint handlers in an emulator. Example:
getchar:
ldy #$00
lda (zp), y
ASSERT(A < $80)
rts
The assembler (or preprocessor) would generate a breakpoint at the ASSERT line and a corresponding breakpoint handler which is evaluated by the emulator each time the breakpoint is hit, but only stops execution when the assertion fails.
The great benefit of this is of course that the generated assembly code is exactly the same, whether assertions are enabled or not. Timing and codesize are completely unaffected. |
| | oziphantom
Registered: Oct 2014 Posts: 490 |
For getting 64tass labels loaded into vice.
You can use the --vicelables option but this only gets you the global and none of the locals which makes debugging functions harder. Also you don't get the members of structs just the first one.
I use this to build ( Relaunched64 script )
64tass.exe -a SOURCEFILE -o OUTFILE --no-caret-diag --dump-labels -l SOURCENAME.tass -D BDD=0 -D CRT=0 -L SOURCENAME.list --verbose-list
cscript D:\pathstuff\dumpToVice.vbs SOURCENAME.tass SOURCENAME.vice
D:\emulation\WinVICE-2.4-x86\x64 -autostart SOURCENAME.prg +confirmexit -reu -reusize 128 -moncommands SOURCENAME.vice
Where dumpToVice.vbs is
set args = WScript.Arguments
num = args.Count
Dim objFS, objFile
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFile = objFS.CreateTextFile(args.Item(1),True)
Set inFile = objFS.OpenTextFile(args.Item(0))
' D:\GitHub\Qwack64\qwak.asm:1627:1: CollisionBoxesW = $1067
Do While Not inFile.AtEndOfStream
blah = inFile.ReadLine()
tokens = Split(blah,":")
'tokens(0) = D
'tokens(1) = file
'tokens(2) = line
'tokens(3) = char
'tokens(4) = Thing = X
If( InStr(tokens(1), "structs") = 0 ) Then
If( InStr(tokens(0), "command line") = 0 ) Then
If( InStr(blah, ":=" ) = 0 ) Then
label = LTrim(tokens(4))
If( Left(label,1) = "k" ) Then
'WScript.Echo label
secondChar = Mid(label,2,1)
If( LCase(secondChar) = secondChar ) Then
writeLine(label)
End IF
Else
writeLine(label)
End If
'If( InStr(tokens(4), "=") > 0 ) then objFile.WriteLine (blah & "; dummy")
End If
End If
Else
label = LTrim(tokens(4))
If( Left(label,1) = "s" ) Then
'WScript.Echo label
secondChar = Mid(label,2,1)
If( LCase(secondChar) = secondChar ) Then
writeLine(label)
End IF
Else
writeLine(label)
End If
End If
Loop
Sub writeLine(label)
parts = Split(label,"=")
name = Replace(Trim(parts(0)),".","_")
addr = Trim(parts(1))
If (InStr(addr,"$") = 0 ) Then
addr = "$"+Hex(addr)
End If
objFile.WriteLine("al "+addr+" ."+name)
End Sub
This way code like
convert2PuzzleRowsTo4_4
;&&trashes a,x,y,ZPTemp1,ZPTemp2,ZPTemp3,ZPTemp4,ZPTemp5,Pointer3
#STAB ZPTemp6,ZPTemp4
_4lineLoop
#STAB ZPTemp7,ZPTemp3
_wordLoop
ldy #3
lda PuzzleBuffer,x
sta ZPTemp5
_byteLoop
will export
convert2PuzzleRowsTo4_4
convert2PuzzleRowsTo4_4__4lineLoop
convert2PuzzleRowsTo4_4__wordLoop
convert2PuzzleRowsTo4_4__byteLoop
into the labels for VICE
And
sGameData .struct
lives .byte ?
score .byte ?,?,?,?,?,?
high .byte ?,?,?,?,?,?
currLevel .byte ?
.ends
GameData dStruct sGameData;
will export
GameData_lives
GameData_score
GameData_high
GameData_currLevel
For asserts and other testing I suggest MartinPipers's BDD6502. This lets you make some test scripts fairly easily to test your 6502 logic. It does not emulate a 64, just a 6502. So you can't check that it interrupts on Clock 5 of Raster line 37, there is no VIC. But you can ask it to check the state of a register or status flags to "assert" that a value is what you expect or not something.
I added some extra opcodes to it, which replace the illegal ops ( which it doesn't support )
PAT .macro
.byte $02
.endm
PXT .macro
.byte $12
.endm
PYT .macro
.byte $22
.endm
TTA .macro
.byte $32
.endm
TTX .macro
.byte $42
.endm
TTY .macro
.byte $52
.endm
INR .macro
.byte $62
.endm
PAT,PXT,PYT pushes A/X/Y onto an internal testing stack
TTA,TTX,TTY compares the current A/X/Y to the value on the testing stack. Thus allowing you to assert that A,X,Y are unchanged. Something my new SA mostly does, but it can't handle all cases.
INR = ignore return, this is for when you do an RTS to perform a JMP ( ) and don't want the system to make a new "test stack frame"
I does try to run at 1Mhz, but it will do all of an opcode then wait that many clocks. Which for the purposes of testing the 6502 logic is pointless, so I added an Overclocked 6502 option which runs as fast as it can. So my tests take 5 seconds and not 80.
BDD6502 takes labels in ACME format not Tass format. While I could modify it to take the RAM Tass, I already worked around it using this TASStoACME format. It takes the verbose format as above to give all locals, and struct members.
set args = WScript.Arguments
num = args.Count
Dim objFS, objFile
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objFile = objFS.CreateTextFile(args.Item(1),True)
Set inFile = objFS.OpenTextFile(args.Item(0))
' D:\GitHub\Qwack64\qwak.asm:1627:1: CollisionBoxesW = $1067
Do While Not inFile.AtEndOfStream
blah = inFile.ReadLine()
tokens = Split(blah,":")
'tokens(0) = D
'tokens(1) = file
'tokens(2) = line
'tokens(3) = char
'tokens(4) = Thing = X
If( InStr(tokens(1), "structs") = 0 ) Then
If( InStr(tokens(0), "command line") = 0 ) Then
If( InStr(blah, ":=" ) = 0 ) Then
label = LTrim(tokens(4))
If( Left(label,1) = "k" ) Then
'WScript.Echo label
secondChar = Mid(label,2,1)
If( LCase(secondChar) = secondChar ) Then
writeLine(label)
End IF
Else
writeLine(label)
End If
'If( InStr(tokens(4), "=") > 0 ) then objFile.WriteLine (blah & "; dummy")
End If
End If
Else
label = LTrim(tokens(4))
If( Left(label,1) = "s" ) Then
'WScript.Echo label
secondChar = Mid(label,2,1)
If( LCase(secondChar) = secondChar ) Then
writeLine(label)
End IF
Else
writeLine(label)
End If
End If
Loop
Sub writeLine(label)
parts = Split(label,"=")
name = Replace(Trim(parts(0)),".","_")
addr = Trim(parts(1))
If (InStr(addr,"$") = 0 ) Then
addr = "$"+Hex(addr)
End If
objFile.WriteLine(name + "="+addr+"; dummy")
End Sub |
| | soci
Registered: Sep 2003 Posts: 480 |
Quoting oziphantomFor getting 64tass labels loaded into vice.
You can use the --vicelables option but this only gets you the global and none of the locals which makes debugging functions harder. Also you don't get the members of structs just the first one.
This was so for a long time but this limitation was fixed in the last release:
https://sourceforge.net/p/tass64/news/2016/07/64tass-1521228/
"* Scoped variables are now output in VICE labels
Now scoped variables are included in VICE listing, which can help debugging a lot."
Due to a VICE parsing limitation a colon is used for separation. I've patched VICE to accept dots as well, and hope to change the output to dots someday once the update propagates sufficiently.
Quoting SteinThe assembler (or preprocessor) would generate a breakpoint at the ASSERT line and a corresponding breakpoint handler which is evaluated by the emulator each time the breakpoint is hit, but only stops execution when the assertion fails.
Interesting idea, reminds me to think about another postponed feature which could be used to do something like this. |
| | JackAsser
Registered: Jun 2002 Posts: 2014 |
Quote: This is outside the scope of what OP is talking about, but one thing that would be really neat is assertions implemented not as generated assembly code, but as breakpoint handlers in an emulator. Example:
getchar:
ldy #$00
lda (zp), y
ASSERT(A < $80)
rts
The assembler (or preprocessor) would generate a breakpoint at the ASSERT line and a corresponding breakpoint handler which is evaluated by the emulator each time the breakpoint is hit, but only stops execution when the assertion fails.
The great benefit of this is of course that the generated assembly code is exactly the same, whether assertions are enabled or not. Timing and codesize are completely unaffected.
help cond
Syntax: condition <checknum> if <cond_expr>
Abbreviation: cond
Each time the specified checkpoint is examined, the condition is
evaluated. If it evalutes to true, the checkpoint is activated.
Otherwise, it is ignored. If registers are specified in the expression,
the values used are those at the time the checkpoint is examined, not
when the condition is set.
The condition can use registers (A, X, Y, PC, SP, FL and other cpu
specific registers (see manual)) and compare them (==, !=, <, >, <=, >=)
against other registers or constants.
Registers can be the registers of other devices; this is denoted by
a memspace prefix (i.e., c:, 8:, 9:, 10:, 11:
Examples: A == 0, X == Y, 8:X == X)
Just saying... |
| | oziphantom
Registered: Oct 2014 Posts: 490 |
using the verbose list option, you could put say
;&&assert (vice compatible condition here )
and then parse the listing and then append break XXXX (condtion) to a labels file? I've never been able to work out the Condition format properly, but I could try a quick test.
I've also thought about making a "protected memory" map.
So the SA spits out a bunch of
RAM,CODE, DATA, NoMansLand ranges.
CODE gets a LOAD and STORE break point put on it, to error if it is changed, DATA gets a break point put on it to make sure it is not incorectly ran, NoMansLand gets a Break,LOAD,STORE set on it as you code should not be touching it and if it is then it is a bug. RAM gets nothing set on it. |
| | Stone
Registered: Oct 2006 Posts: 172 |
Quoting JackAsser
help cond
Syntax: condition <checknum> if <cond_expr>
Abbreviation: cond
...
Just saying...
Yes, I'm aware of conditional breakpoints in vice.
TODO:
"The assembler (or preprocessor) would generate a breakpoint at the ASSERT line and a corresponding breakpoint handler" |
|