Back in the distant past when I was just learning C, I often convinced myself that the compiler had a bug and my C 101 code was correct. Of course, I was wrong 100% of the time.

Well, today, I found one. This one took a while to find because my computer doesn’t have communication yet. I have to debug by loading code and then looking at waveforms on a 2 channel scope.

To aid in my debugging I created a new function:

 1 .area _CODE
 3 _ShowByte::
 4  ld iy,#2
 5  add iy,sp
 6  in a, (#0x80)
 7  res 2, a
 8  out (#0x80), a
 9  ld a,0 (iy)
10  rlca
11  rlca
12  rlca
13  rlca
14  and a,#0x0F
15  or a, #0xA0
16  out (#0x83), a
17  ld a, #0x28
18  out (#0x84), a
19  ld a,0 (iy)
20  rlca
21  rlca
22  rlca
23  rlca
24  and a,#0xf0
25  or a, #0x05
26  out (#0x83), a
27  ld a, #0x28
28  out (#0x84), a
29  in a, (#0x80)
30  nop
31  nop
32  nop
33  set 2, a
34  out (#0x80), a
35  ret

This drops GPIO2 (which must be made an output elsewhere) and then outputs a byte with a leading 0xa nibble and a trailing 0x5 nibble. I did this because I need one probe on GPIO 2 and the other on the data, so I can’t see the clock line. (I might be able to use the REF feature of the scope though…) Anyway, with the prototype extern void ShowByte(uint8_t); I can output a byte that the scope can see. Now, main() calls putchar() which calls send(). And the argument seemed to be getting corrupted at send(). Why? I looked at the uart.asm file and found the reason:

 1 ;uart.c:151: bool send(uint8_t b) __critical
 2 ; ---------------------------------
 3 ; Function send
 4 ; ---------------------------------
 5 _send::
 6  ld a,i
 7  di
 8  push af
 9 ;uart.c:153: ShowByte(b);
10  ld hl, #2+0
11  add hl, sp
12  ld a, (hl)
13  push af
14  inc sp
15  call _ShowByte
16  inc sp

This was generated by the compiler. And it’s wrong wrong wrong!

The first three instructions handle the __critical part. They store the interrupt register into a, disable interrupts, and push af onto the stack so that the interrupts can be restored at the end of the call.

The next part tries to get the argument. In theory, the argument should be at the top of the stack, just on the other side of the return address. Or sp+2. And indeed we see hl gets 2 plus sp and then we load that into a, push af, inc the stack pointer because it’s just a byte, and then calls ShowByte.

But, this function is __critical. The stack isn’t the 8 bit argument followed by the 16 bit return address. It’s the 8 bit argument, followed by the return address, followed by 16 bits of interrupt restore data. I’m not getting my argument–I’m getting the lower part of the return address!

Wow! I need to rethink how I do critical sections! I took off the __critical and rebuilt. Here is that snippet:

 1 ; ---------------------------------
 2 ; Function send
 3 ; ---------------------------------
 4 _send::
 5 ;uart.c:153: ShowByte(b);
 6  ld hl, #2+0
 7  add hl, sp
 8  ld a, (hl)
 9  push af
10  inc sp
11  call _ShowByte
12  inc sp

Except for the missing interrupt disable, it’s the same–it gets the byte 2 bytes back. This one works in my testing. I’m getting the right byte on my scope.

I’m wondering if that +0 is supposed to account for the push af but it’s failing to update… I gotta figure out a game plan. And then submit a bug report if one isn’t already there on this.