List-level debugging :)

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
derekfountain
Member
Posts: 50
Joined: Mon Mar 26, 2018 1:49 pm

List-level debugging :)

Post by derekfountain »

Since I started with Z88DK it's become apparent that I've been a bit spoilt over the years with source level debugging. Now I haven't got it I realise how much I rely on it. When my Spectrum program doesn't work the options appear to be to either stare at the C code hoping for inspiration, or single step through the generated Z80 code hoping to be able to follow what's going on. I went looking for an alternative.

The map file produced when you give the -m flag to zcc produces a table of symbols and their addresses in the final executable. Adding the --list option along with the --c-code-in-asm option produce listing files containing the generated assembly language, commented with the original C code. Given that the map contains the addresses of all the functions, and the *.c.lis files contain the offset of each Z80 instruction from the the start of the list file, with a bit of munging it's possible to work out the absolute final address of any instruction in the compiled program.

I wrote a Perl script to do this munging: it takes the map and listing files, works out all the instruction addresses, then concatenates the listing files to generate a single listing file where each line is pre-pended with the instruction address. So it looks, for example, like this:

Code: Select all

...
0x9079 ++               l_gameloop_00129:
0x9079 ++               ;gameloop.c:164: if( in_key_pressed( IN_KEY_SCANCODE_SPACE ) ) {
0x9079 ++   21 7F 01            ld      hl,0x017f
0x907C ++   CD 00 00            call    _in_key_pressed_fastcall
0x907F ++   4D                  ld      c, l
0x9080 ++   7C                  ld      a, h
0x9081 ++   B1                  or      a,c
0x9082 ++   28 0A               jr      Z,l_gameloop_00104
0x9084 ++               ;gameloop.c:165: game_state->key_pressed = 1;
0x9084 ++   DD 6E FA            ld      l,(ix-6)
0x9087 ++   DD 66 FB            ld      h,(ix-5)
0x908A ++   36 01               ld      (hl),0x01
0x908C ++   18 10               jr      l_gameloop_00141
0x908E ++               l_gameloop_00104:
0x908E ++               ;gameloop.c:167: game_state->key_pressed = 0;
0x908E ++   DD 6E FA            ld      l,(ix-6)
0x9091 ++   DD 66 FB            ld      h,(ix-5)
0x9094 ++   36 00               ld      (hl),0x00
...
The "0xXXXX ++" tag on the left side is what my script adds to the single output file - the address of each instruction. I refer to this file as the "tagged source".

I then went to the Fuse emulator debugger source and added a Gtk text widget into the window. The Gtk text widget has the concept of "marks" which allow a point in the text to be identified by a name, a short text string. The widget seems happy to have thousands of these marks, so I wrote some code to load my tagged source file into it, using each line's address tag as a text widget mark. This allows the Gtk text widget containing my tagged source to be able to find and scroll the text to the instruction at any given address.

Final step: the Fuse debugger has a hook which allows the dialog to be updated as the emulator advances the Z80 program counter, so I hooked my Gtk text widget to that with a bit of code which takes the PC register, converts it to a string, finds that string as a mark in the text and scrolls to it. The effect is that as you click the "step" button in the Fuse debugger, the C-and-assembler mixed code in the text window follows the program counter.

Obviously you're still stepping though Z80 instructions, not C code, and you don't get the see the local variables unless you pay attention to the registers or the position in the stack frame they're in. But you do get the context of the C program. You can see the C code the Z80 is working though, and you can see all the symbols which are in that C code, including function names, statically allocated variables and compiler generated labels. It's not hard to see how the Z80 flags are controlling loops, etc.

I recorded a short desktop video, showing it working:

https://youtu.be/TmpHy_x2Fko

I don't know how useful anyone else might find this, but for me, at my level of experience, I'm finding myself using it all the time.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

That's very nice work. I think it can be pretty helpful because that's exactly what I am doing when debugging compile errors.
benjymous
New member
Posts: 3
Joined: Fri Nov 01, 2019 9:05 am

Post by benjymous »

Did you get any further with this?

I've been considering adding hooks into Fuse so it can be used as a debug target from VSCode - that way you should be able to step through your original C sources in the editor, and have it synced with Fuse's debugger

I found this library written by a google engineer that looks like it should provide the C++ interfaces needed for VSCode's debugger to hook into

https://github.com/google/cppdap/
Post Reply