empathicqubit/vscode-cc65-debugger

Mapping of (local) Variables doesn't match

Opened this issue · 14 comments

The mapping to local Variable seems of by a few Bytes.
See screenshot for an example. In this screenshot, the method being called was passed a key code (Commodore F, which equals 187 or 0xee). As seen in the screenshot, the character code in pressedKey is actually stored in the C stack at $cffb while the debugger assumes it to be at $cff9.

image

Also, what I noticed is, that the debugger does not pick up local variables which have been defined in nested scopes. Could this be the also the reason for the offsets in the local variable address lookup?

Unfortunately, I don't have a small repo, at least not yet. I am trying to find more time for putting together a simple repro scenario.

This is VSCode, latest version:
image

This is Vice version 3.6.1, and I am using the internal version of CC65.
OS is Windows 11.

Which Commodore platform are you building for?

Also please try turning off the runahead option

Which Commodore platform are you building for?

C64.

Also please try turning off the runahead option

Will try later today.

So, I experimented a bit further.
What I think happens is that the mapping does only fit from the moment on where all the local variables have been declared and probably also defined.

I could see that the address of a local variable changed the moment another variable got declared and defined:

The following line is about to be executed:

image

After it has executed, the address of pressedKey changed to reflect the correct one:

image

And slightly OT:

I don't know if this is on your end, but I tried to use the donate site, and I failed. If you have another way, please advise! I really love this extension!

image

Thanks! I haven't fixed the donate site yet. I'm not sure if I can legally accept donations for the next couple of years, so I might have to shut that off for now. I appreciate it though!

The behavior you're seeing might be a limitation in the way I coded it. I'll try to see if I can improve that slightly

I didn't fix the problem yet. I'm not sure if there's a good solution, but now there is a notice that the variables could be wrong if it doesn't think that the stack is in the right place.

So, help me understand:
Does the mapping of the variable change as they become discovered?
What is the condition that lets you know the stack might be off?

Thanks!

The mapping changes as the variables are pushed onto the stack. The debug file uses an offset which isn't correct until the stack grows down to include all the variables. I look at the instructions and try to guess where the stack pointer stops getting manipulated. That seems to be after the first execution line, after all the variable assignments are complete.

This is the code that checks for the warning line. It checks each C line to see if it jumps to decsp/subsp or anything else which grows the stack down. If the C line doesn't go there then it assumes the stack is finished being initialized.

https://github.com/empathicqubit/vscode-cc65-debugger/blob/master/src/lib/disassembly.ts#L154

Could we include a kind of meta directive as a comment, which would force the mapping to a certain address and which the parser could take into account? I know it's a hack, but it would help certain scenarios a lot, I guess.

// VS65-OFFSET-DEBUGGER-MAPPING: -4

You would reset that mapping, if you encountered additional local variables, but could as well add a new directive which would be applied from that spot on.

I wonder if there's a way to implement that that doesn't require changing the source files. I intentionally avoided parsing them to reduce some complexity.

If you could get it to work for both regular and block comments that would probably be okay.

I wonder if there's a way to implement that that doesn't require changing the source files.

Yes, that would definitely be best.
I never looked at the debug file - but if you had a list of the variables which can be used in the context, could you probably hold them in a loc-var lookup table, know their sizes, check them off, when they have been used, and adjust the pointer(s) accordingly? Just as an idea.

The problem is that I don't think it's possible to check them off with 100% certainty. Given this code:

unsigned char test_local_vars_main(void) {
    static unsigned char weehah;
    static unsigned char bonza = 0x42;
    register unsigned char blarg = 1;
    register unsigned char blerg = 2;
    unsigned char i = 3;
    unsigned int j = 4, k = 4;
    unsigned int *random;
    unsigned char *lol;
    signed char whoa;
    struct hello wow;
    thingy *cool;
    union xy xy;
    xy.xy.x = 0x01;
    xy.xy.y = 0x02;
    cool = &wow;
    random = 0x03fc;
    *random = 0x3003;
    globby = 0x34;
    whoa = -1;
    wow.j = 3;
    wow.k = 4;
    wow.l.m = 5;
    wow.l.n = 6;
    i = 0x23;
    j = 0x1337;
    weehah = 0x59;
    lol = "copter";

    return 0;
}

These local definitions are produced:

csym	id=3,name="blarg",scope=3,type=0,sc=auto,offs=-1
csym	id=4,name="blerg",scope=3,type=0,sc=auto,offs=-2
csym	id=5,name="i",scope=3,type=0,sc=auto,offs=-3
csym	id=6,name="j",scope=3,type=0,sc=auto,offs=-5
csym	id=7,name="k",scope=3,type=0,sc=auto,offs=-7
csym	id=8,name="random",scope=3,type=0,sc=auto,offs=-9
csym	id=9,name="lol",scope=3,type=0,sc=auto,offs=-11
csym	id=10,name="whoa",scope=3,type=0,sc=auto,offs=-12
csym	id=11,name="wow",scope=3,type=0,sc=auto,offs=-16
csym	id=12,name="cool",scope=3,type=0,sc=auto,offs=-18
csym	id=13,name="xy",scope=3,type=0,sc=auto,offs=-20
scope	id=3,name="_test_local_vars_main",mod=1,type=scope,size=179,parent=2,sym=21,span=121+122+123
span	id=121,seg=3,start=0,size=1,type=2
span	id=122,seg=2,start=3,size=1
span	id=123,seg=0,start=29,size=179
seg	id=0,name="CODE",start=0x000840,size=0x01C9,addrsize=absolute,type=ro,oname="program.c64",ooffs=65
seg	id=1,name="RODATA",start=0x000A09,size=0x0007,addrsize=absolute,type=ro,oname="program.c64",ooffs=522
seg	id=2,name="BSS",start=0x000A56,size=0x0004,addrsize=absolute,type=rw
seg	id=3,name="DATA",start=0x000A10,size=0x002A,addrsize=absolute,type=rw,oname="program.c64",ooffs=529
seg	id=4,name="ZEROPAGE",start=0x000002,size=0x001A,addrsize=zeropage,type=rw
seg	id=5,name="NULL",start=0x000000,size=0x0000,addrsize=absolute,type=rw
seg	id=6,name="ONCE",start=0x000A56,size=0x0026,addrsize=absolute,type=ro,oname="program.c64",ooffs=599
seg	id=7,name="STARTUP",start=0x00080D,size=0x0033,addrsize=absolute,type=ro,oname="program.c64",ooffs=14
seg	id=8,name="INIT",start=0x000A3A,size=0x001C,addrsize=absolute,type=rw,oname="program.c64",ooffs=571
seg	id=9,name="EXEHDR",start=0x000801,size=0x000C,addrsize=absolute,type=ro,oname="program.c64",ooffs=2
seg	id=10,name="LOADADDR",start=0x0007FF,size=0x0002,addrsize=absolute,type=ro,oname="program.c64",ooffs=0

Edit: I think the problem is that I'm measuring from where the stack currently is rather than from where the function starts. I guess I would need to track where the stack is at the beginning of the function. I'm not sure if that would be reliable or if would slow things down. There's a limited amount of information reported when a tracepoint is hit, so I would probably need a full breakpoint at the beginning of each function. I wanted to change the monitor to allow other binary commands to run when a tracepoint is hit, but I haven't added that yet. So you would need to add a starting breakpoint for each function that has a user breakpoint set, and I guess if the emulator was paused you would just have to use the old, slightly broken technique.