Jump to content
  • 0

Emulator Debugger halting biased?


kliepatsch
 Share

Question

Hi,

in my current project, I could cope with bugs mostly with the help of the "poor man's debugger", namely

lda #65
jsr CHROUT

which prints a character on the screen if it gets to the piece of code where I put it.

However, as I have come across bugs that are difficult to reproduce and appear randomly to me, I started looking into how to use the built-in debugger. I thought it might be useful to cope with such bugs that are difficult to reproduce. When such a bug occurs and my program hangs, I could simply start the debugger (by pressing F12) and see where my code gets stuck.

However, when I tried this technique, I noticed that the emulator halts preferably at always the same set of addresses.

In my case, it halts most frequently at $038B and $E023, which I suspect to be X16 native code, and apart from that, some other places in memory (including my own code). However, I would expect the debugger to halt anywhere in the program's execution path equally likely, simply randomly distributed over the whole code. Because I know that my main loop gets executed many times a second.

Why would the debugger behave in such a way and often stop at the exact same address? Any thoughts?

Edit: to clarify: this behavior even occurs during normal execution of my program.

Edited by kliepatsch
Clarification
Link to comment
Share on other sites

9 answers to this question

Recommended Posts

  • 0

The debugger would preferentially halt at particular addresses because of the timing of keyboard polling in the emulator. This happens once per frame, in response to the VERA module deciding to fire its VSync interrupt. This is a mostly arbitrary choice: it's convenient to do this here, as opposed to slowing down the primary CPU/execution loop when the vast, overwhelming majority of checks for keyboard input are going to come back empty anyways. The kernal's interrupt handler, which is what you're seeing, also kicks off in response to the VERA module's VSync interrupt.

Since the keyboard polling happens at a regular interval relative to that kernal interrupt handler, pausing execution through the keyboard at any given time is going to cause the debugger to stop at roughly the same execution addresses each time. $038B would likely be part of the kernal's trampoline logic for bank swaps, $E023 is deep in ROM.

Edit:

I don't happen to know anything about your project, but if you're compiling with cc65, I would definitely recommend specifying `-Ln symbols.sym` in your linker options. This will produce a file name "symbols.sym", which contains a plaintext listing of all the symbols in your project, along with the hexademical address they occur at. With this, you can find a symbol and then set a debug breakpoint at it, so you can stop the debugger exactly where you suspect something is going wrong.

For instance, I have an ASM function named "generate_sin_curve", so in my symbols.sym file, I can find the following line:

al 003267 .generate_sin_curve

From this line, I know I can run the debugger, load my program, then hit F12 and type in "d 3267" and press enter to send the debugger directly to the start of that function. I can press F9 to set a debug breakpoint, then F5 to resume running the emulator. I can then run my program, and the debugger will pause execution immediately before running the instruction at that address.

Edited by StephenHorn
  • Thanks 2
Link to comment
Share on other sites

  • 0

Thanks @StephenHorn for the clear explanation. Now I can indeed very well imagine why that's happening.

Also thanks (also @JimmyDansbo) for the explanations on how to use the debugger, might become useful to me. Yes, I am using ca65, so the link symbol table is nice to know.

For the problem I am currently chasing down, however, the "normal" use of a debugger won't help. I think meanwhile I got closer to my problem. I can kinda reproduce it by smashing certain keys in order to eventually trigger a very unfortunate timing inside my program. I guess it's got something to do with the interplay between interrupts and the main program. Most of the time, the code gets executed correctly, but in rare cases it does not.

But for the 97 % of the cases where it's bugs in the straight execution of code, the techniques you mentioned may indeed become useful to me.

Edited by kliepatsch
Link to comment
Share on other sites

  • 0
On 1/10/2021 at 9:20 PM, StephenHorn said:

I don't happen to know anything about your project, but if you're compiling with cc65, I would definitely recommend specifying `-Ln symbols.sym` in your linker options. This will produce a file name "symbols.sym", which contains a plaintext listing of all the symbols in your project, along with the hexademical address they occur at. With this, you can find a symbol and then set a debug breakpoint at it, so you can stop the debugger exactly where you suspect something is going wrong.

For instance, I have an ASM function named "generate_sin_curve", so in my symbols.sym file, I can find the following line:

al 003267 .generate_sin_curve

From this line, I know I can run the debugger, load my program, then hit F12 and type in "d 3267" and press enter to send the debugger directly to the start of that function. I can press F9 to set a debug breakpoint, then F5 to resume running the emulator. I can then run my program, and the debugger will pause execution immediately before running the instruction at that address.

Now I decided to actually try this, and I don't get it to work. I am compiling using the following command
 

Quote

cl65 -t cx16 -o CONCERTO.PRG -C cx16-asm.cfg -u __EXEHDR__ -Ln symbols.sym "example_full.asm"

But the generated symbols.sym contains only several predefined addresses:

Quote

al 0054FB .__BSS_LOAD__
al 0054FB .__BSS_RUN__
al 000000 .__BSS_SIZE__
al 000001 .__EXEHDR__
al 009F00 .__HIMEM__
al 000001 .__LOADADDR__
al 000000 .__ZP_FILEOFFS__
al 000038 .__ZP_LAST__
al 00005E .__ZP_SIZE__
al 000022 .__ZP_START__

I tried without the "-C cx16-asm.cfg -u __EXEHDR__" options, in which case this list is different, but I do not get my own symbols to show up. Any idea what I am doing wrong?

Link to comment
Share on other sites

  • 0
6 minutes ago, kliepatsch said:

Any idea what I am doing wrong?

Given that the creation of a symbol file is performed by the linker, I'd assume the issue is that only exported symbols are listed. Try adding a .export for each symbol that you need the location of.

  • Thanks 1
Link to comment
Share on other sites

  • 0

@kliepatsch Debugging 6502 assembly can be quite hard.

It would be interesting to hear what techniques are used by others.

I usually do the following:

  • I try to divide code into functions - the smaller the better - with well defined input and output. Test each function separately to ensure it works. Also test that functions work properly if the input is invalid.
  • I think it's very valuable to comment what each step of the code is supposed to do. It helps you think more clearly about the code.
  • Before using any tools to debug the code, I usually do the debugging in my head by just reading the code and thinking about what could go wrong. In most cases I find bugs with this method.
  • If that is not successful, my next method is to store intermediate results/values in memory that I can look at after testing the function. This is often very helpful.
  • I usually resort to the emulator's debugger as a last resort. Sometimes the debugger works great. But often I have a hard time making it work efficiently. Maybe I'm not using it right.
Edited by Stefan
Link to comment
Share on other sites

  • 0
Posted (edited)
11 hours ago, Stefan said:

It would be interesting to hear what techniques are used by others.

Here's how I do it most of the time

  • Commenting is very important to me, too. Otherwise, I'd very quickly loose track of what's going on. Not to imagine if I could figure it out after coming back to the code after some time!
  • Looking at the code, yes, that helps in many cases. But it can be quite tiring.
  • I use the "poor man's debugger" mentioned in the original post. It is easy to use and helps figuring out the path of execution, e.g. which branch it took. Just put the two lines in the branch you want to test, recompile, run, done.
  • In my current project I also defined a macro that lets me print a memory location to screen in decimal (0-255). I call that macro in a place where it's executed regularly so I can monitor the value in real-time. I even used it several times to monitor fixed-size arrays in real-time. This was invaluable when debugging a "relatively complicated" data structure, albeit a bit tedious to setup at first.
  • Only in very difficult cases I have resorted to the built-in debugger. I found the .byte $db to define a break point quite convenient. But maybe with more practice I could use it more efficiently...
Edited by kliepatsch
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.

Guest
Answer this question...

×   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.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use