Z88DK and Fuse, list level debugging

The place for codemasters or beginners to talk about programming any language for the Spectrum.
Post Reply
dfzx
Dizzy
Posts: 86
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Z88DK and Fuse, list level debugging

Post by dfzx » Mon Aug 27, 2018 8:47 am

I posted this to the Z88DK list which is probably where it belongs, but that's a low traffic environment and one or two people here might also find it interesting. So with apologies for the cross post:

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:



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.

Fuse is free software and I have a fork on SourceForge. I'm not sure where this is going yet, so can't really provide a static link to the fork. If you want a copy of the Perl script or the Fuse fork, by all means PM me.
4 x

Fred
Berk
Posts: 32
Joined: Tue Feb 27, 2018 3:15 am

Re: Z88DK and Fuse, list level debugging

Post by Fred » Mon Aug 27, 2018 10:33 am

That looks really cool :D
0 x

User avatar
Stefan
Berk
Posts: 27
Joined: Mon Nov 13, 2017 9:51 pm
Location: Belgium
Contact:

Re: Z88DK and Fuse, list level debugging

Post by Stefan » Mon Aug 27, 2018 10:36 am

Cool! As a quick win, some very basic colour coding just of c vs assembly lines could make it even easier to use.
0 x

User avatar
bob_fossil
Microbot
Posts: 103
Joined: Mon Nov 13, 2017 6:09 pm

Re: Z88DK and Fuse, list level debugging

Post by bob_fossil » Mon Aug 27, 2018 9:44 pm

Cheers for doing this and explaining the map and list files.

I've been using a modified version of fbzx to debug my Z88DK projects. I added a basic debugger and the ability to break into the emulator from the source code by inlining in a 'do nothing' opcode, so I could look at the assembly at a specific point to see what I was doing wrong.

For Knockabout, I was banging my head trying to debug a function using border colours to show where I'd got to in it and dumping variables out to screen memory to read with the debugger as I didn't know the location of the variables. In the end I took a different approach and took the C code and turned it into a console app on my PC using the terminal to output basic graphics to the screen. With the aid of a proper IDE I found the problem in minutes.

I used a similar approach for debugging and developing the C end of Thoroughly Modern Willy, though I upgraded the graphics to use SDL on the PC (I created a surface the same resolution as the Spectrum screen). Through some #ifdefs I ended up with code I could compile for the PC or Spectrum. I still had to manually debug the assembly on the Spectrum but the ability to do watches and add breakpoints in the IDE made it a lot easier to try things out and fix problems.

Using your explanation I've added a symbol lookup to my custom fbzx so I can now disassemble or dump memory from a symbol in the map file as well as an absolute address. I've also started to add a very basic .lis parser so I can get the source code for a given address.
0 x

dfzx
Dizzy
Posts: 86
Joined: Mon Nov 13, 2017 6:55 pm
Location: New Forest, UK
Contact:

Re: Z88DK and Fuse, list level debugging

Post by dfzx » Tue Aug 28, 2018 9:50 am

I can't help a great deal with your travails, Bob, but it was somehow comforting to know I'm not the only with these problems. :) You're clearly further forward than I am; I've not written any Spectrum program of use as yet, and certainly not a finished game.

I've used exactly the same trick with border colours, it being the only simple way to communicate with the user as to program location or variable value. Not exactly an advanced interface though. :lol:

I also thought about writing some sort of compatibility layer which would allow a Spectrum C program to be compiled for a PC environment, but given that I'm interested in the SP1 library I'd need to get a pretty accurate emulation of the Spectrum memory, screen and Z80 working before it would be useful. I thought about using something like QEmu, but decided that improving the Spectrum emulator tooling would probably be more productive. I'm still very much in the realm of making it up as I go along though. :)

I think there's a bit more legs in this tagged source idea yet. One thing that comes to mind is to genericise it: instead of my address tag on each line, put something there carrying a bit more information. A small JSON block comes to mind, which could carry various values relevant to the generating environment - address, syntax hinting as @Stefan suggested, an indicator of whether a line is C code in a comment, Z80 opcode, assembler directive like DEFB, etc. The presentation level (Fuse) could then read that information and use it to show, hide, highlight, etc., different lines and types as required. What I can't see at the moment is a way to get variable values visible in the emulator's debugger. It would require a way to get C variable types and sizes, plus the location the compiler has put them in the stack frame.

Before any of that stuff, I'd like to find a way to get symbols into the Fuse debugger. Or a decent memory map explorer. And probably quite a few other things. :)
0 x

User avatar
bob_fossil
Microbot
Posts: 103
Joined: Mon Nov 13, 2017 6:09 pm

Re: Z88DK and Fuse, list level debugging

Post by bob_fossil » Tue Aug 28, 2018 9:38 pm

I still need to finish off Knockabout and Thoroughly Modern Willy was only six screens so I'm hardly a paragon of finished games. :)

I took a higher level approach and abstacted the graphics so that I could concentrate on the game logic. I'm not using SP1 as it's a bit of a black box and seems a bit memory hungry. It would have been overkill for the games where I'm just displaying 8x8 or 16x16 tiles to the screen.

I'm not sure if you can get the Z88dk compiler to generate files that describe structures and their types. One approach would be to manually create a specification file that names a structure and the types of it's members (or even write a basic parser which hunts for 'struct' in your C files). You could then load that in alongside the .map and .lis files and then have a debugger command to interpret a memory address as a named type (a bit like the dt command in windbg)
0 x

Post Reply