Jump to content

Debugging aide

Recommended Posts

So it's been a few weeks since I posted to the Xtal thread, and it's not because I've stopped on it. The next self-appointed task was to get printf() working, and to do that I decided I ought to get floating point numbers working. I'm planning on using Woz-format floats, and although it's old code, it's not straightforward code...


Eventually I decided I wanted something I could easily integrate into the workflow, and since I'm on a Mac, Altirra isn't as easy to use as it might be on a PC. What I really wanted to do was run the code backwards and forwards stepping over it, jumping back and trying again etc. I could get some of that from the simulator from @dmsc, but I wanted something a bit more interactive. So qxtal was born. In the future it may form the basis of an interactive source-level debugger, but for now it does what I need...



The main UI is fairly simple at launch, the obvious basics with the processor-registers (A,X,Y, PC, status, stack). There's only one active button at the top, which loads an XEX. To make things a little easier, you can embed (and xtal does :)) a symbol table into the XEX file, as a block (or blocks) in the Atari-binary format, which looks like...


$FF $FF        - First two bytes indicate it's a binary file
$lo $lo        - Next two bytes are the start address to load data at
$hi $hi        - Next two bytes are the last address to fill with data

There then follows sufficient data to fill $hi - $lo + 1 bytes (note the +1, this is an *inclusive* range)

A series of these blocks can be catenated together, and will load data in sequence to different ranges of addresses 


  • If a block covers $02E0,$02E1, then it is considered the execution address for the entire file once loaded, and the computer will JSR to that location once the file is completely loaded.
  • If a block covers $02E2,$02E3, then it is considered a mid-load call, and once the block has finished loading, the computer will immediately JSR to the address in $02E2,$02E3.


For both of these locations, the routine should end with RTS

Qxtal slightly abuses this to provide an embedded symbol table. The idea is that the assembler will create a block (or multiple ones if a single block length would be longer than the code-length) at the start of the binary file which is just data, loaded at the same offset as the actual binary code (so it will be over-written by the code). That said, if the code is < 256 bytes and the symbol table is more than 256 bytes, then the block-size for the symbol table will be 256 bytes

The symbol-table data within each block follows the form:

$60    - the hex code for RTS, in case this is ever executed (!)
$53    - the hex code for 'S'
$59    - the hex code for 'Y'
$4D    - the hex code for 'M'

Then a series of chunks:
 $lo, $hi       - two bytes to represent the address
 $len           - one byte to represent how long the string is
 <len bytes>    - the string that forms the label name
 ...            - repeated until all the symbols are defined

Followed by two more bytes which are a 16-bit checksum of the entire block apart from the checksum itself. The checksum is simply the sum of all bytes in
the block, modulo 16 bits.

 $lo, $hi        - checksum


If the symbol table is ever left in the binary file, it ought to be loaded and ignored, then subsequently overwritten by the binary, and the binary can still be executed. Even if something tries to call into this "code block", the first byte is RTS so it ought to be harmless. Qxtal however, can load, check that this really is a symbol table, parse the symbols, and use them in the UI. You don't actually *have* to have the symbols in the XEX, but it makes it a lot easier if you do :) 




You can see the labels being used in both the actual label declaration, and in the disassembly - even in things like 'STA ascii,Y'. At this point the 'Simulate' button is enabled and tapping on it will run the code through the simulator (which is a carbon-copy clone of dmsc's work). You get the dump of the actual tracing, instruction by instruction, alongside the assembly source code, thus:



At this point you can highlight points in the trace and see the state of the CPU in the state widget at the upper left... Note that the state widget captures the state just *before* the instruction is executed, so if it the statement says "LDA #$FF', then A changes on the next instruction. The up/down arrow keys will allow you to proceed to the next/previous statement, and the CPU state 

will update to reflect the new position in the instruction stream. You can also jump to any point in the instruction stream by just clicking on it in the center widget and the state will be as if you'd gone through the instructions one by one.


The simulator also tracks memory-reads, memory-writes, and instruction-fetches. The video clip below shows me pressing/holding down on down-arrow to iterate through the instruction stream, having set the memory-display to $6000 (where the program loads). You get a "heat-map" {green, blue, red, then the same but bordered} in a non-linear fashion to try and capture information over a wide range. 



Each square in the top box represents a single memory location, 16 bytes per row, 16 rows to make a single page of data (though it doesn't have to start at a page boundary). Each square in the lower box represents an actual page of memory (again, 16x16). I'll probably put some numbers on here to make it clearer...


Clicking on any square in the lower box will set the view in the upper box to that page, which makes it easy to maneuver around memory, and see if there are any unexpected calls or rogue vectors :) . The same display works for data-reads and data-writes, which can be useful to see how heavy your use of memory is between ZP and normal memory.


There's a few other things in there as well:


  • Clicking and highlighting any line in either of the two widgets on the right (assembly, or instruction stream) will highlight any occurrence of the same instruction in the other widget - so you can see loops easily in the instruction stream by clicking in the assembly, for example.
  • The memory view (when you're looking at data-reads or data-writes) will highlight changes in red in the memory-display, so it's easy to track things as they happen
  • hovering over the boxes at the lower right will show you the actual value (not just the range indicated by the colour) of reads/writes/accesses to that memory address/page.
  • You can toggle a breakpoint (currently just unconditional) by right-clicking in the assembly (the rightmost pane) view, as below




I do intend to make the breakpoints a lot more powerful, somehow. I was kind of hoping to see a QT version of the NSPredicateEditor which makes it trivial to set up "if this, then that" sequences - which would be ideal for breakpoint conditions... But I can't find anything in QT to do that (which is kind of surprising, I always felt that QT was a close match to Cocoa, the Mac framework). I chose QT (and wrote in C++ rather than ObjectiveC with Cocoa) so that if someone else did want to use this, they wouldn't have to be using a Mac, but I was hoping this would be a relatively quick coding exercise...


Anyway, this is actually in a semi-useful state now. There's a laundry-list that could be worked on to make it better, but (as the thread is entitled), I currently see it as an aide to debugging, not a project in its own right. It's a means to an end, that’s all for now. As and when I want to turn it into a real source-level debugger, I'll revisit it, things like:


  • Breakpoints list / management area (that's what the lower-middle part was intended to be)
  • The afore-mentioned breakpoints predicate editor
  • Call-graph extraction and display
  • Interactive changes (edit assembly, reassemble, resimulate, show changes)
  • Syntax highlighting in the assembly / instruction stream
  • Parsing bytes into more-meaningful higher-level constructs (eg: these 4 bytes are a float of value...)
  • Monitor the filesystem so when the XEX changes, it's auto-loaded and ready to go


... but for now it is what it is, and if you want to check it out, it's in the xtal repository under 'qxtal'. I ought to note that I haven't actually tried compiling it under linux or Windows yet, but I'm not aware of anything that's Mac-specific in the code. It's possible Windows might need a bit of massaging to cope with paths etc.




  • Like 6
Link to comment
Share on other sites



This is Commodore 64, Atari XL/XE and NES code and memory debugger that works in real time. It is quick prototyping tool where you can play with Commodore 64 machine and its


C64 Debugger embeds VICE v3.1 C64 emulation engine created by The VICE Team and Atari800 v3.1.0 emulation engine created by the Atari800 team.

  • Like 1
Link to comment
Share on other sites

Ok, there was one thing I couldn't just leave hanging, the breakpoint editor. You can now add/remove/edit breakpoints of reasonable complexity (you can keep adding criteria, I wrote a simpler version of NSPredicateEditor in QT). This actually makes the tool quite useful now.




The goal of this is that you don't need to continually re-run - the only time you ought to need to re-load the binary is after a code-change. Since the state is tracked after every instruction and you can just point/click at instructions to get to the one you want, it's easy to track down errors and see why this jump isn't being taken, because (skip back a bit) that block has the wrong logic etc. The breakpoints are useful to stop simulation just after something has gone wrong, so all the setup is laid bare for you to skip around in, tracking down the issue.


  • Like 3
Link to comment
Share on other sites

Minor update really but since this is likely to be the last for a while - I intend to start using this in anger now, so the only changes will be when obvious deficiencies surface - I thought it's worth mentioning.


QT has a cross-platform filesystem-monitoring object, so now when you go back to your "build system" (in my case, that's "make") and regenerate the XEX after making changes, the UI will automatically reload the new source when it sees the file change on disk. And it'll keep any breakpoints you have set as well.


It's not smart enough (yet) to move the breakpoints to the correct place based on source-code changes - that'd need source-level debugging to be supported and that's not even on the radar yet. Given how I intend to use this thing though, it's not a huge deal. Breakpoints are really just to get you past a whole slew of initialization, setup etc. to somewhere close to where the problem is, because the ability to step back in time through instructions and memory mean as long as you're *past* where you want to be, you can easily get to where you want to be (and then probably re-home the breakpoint :) )





The screen-capture is showing me having an XEX loaded up and then typing 'make clean && make' a couple of times to regenerate the XEX file. The instruction stream in the middle disappears, then I run the simulator again, then lather, rinse, repeat.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...