Is SDCC 4.2.0 broken?

Par pizzapower

Expert (82)

Portrait de pizzapower

24-05-2022, 08:29

I don't know if I am doing something completely wrong here, but if I write an assembly function that uses the new __sdcccall(1) calling convention and accepts 3 uint8_t parameters (in A, L and stack according to the manual) and OUTs them to debugdevice like this:

func.z80:

.globl _func

_func::
        out (#0x02f), a

        ld a, l
        out (#0x02f), a

        ld hl, #2
        add hl, sp
        ld a, (hl)
        out (#0x02f), a

        ret

And I call it like this:

extern void func(uint8_t i, uint8_t j, uint8_t k) __sdcccall(1); /* just to be sure */

void call_func() 
{
    uint8_t i;

    for (i = 0; i < 10; ++i)
    {
        func(11 + i, 6, 32 + i);
        func(11 + i, 7, 64 + i);
        func(11 + i, 8, 96 + i);
    }
}

Running it through openmsx-debugger,

  • on first call, A = 0x0B, L = 06 and stack contains 0x20 right before call _func, which is correct.
  • on second call, A = 0x0, L = 07 and stack contains 0x60, which is wrong (both A and stack are wrong).

Am I forgetting something? What's going on here? It used to work in 4.1.0... Question

!login ou Inscrivez-vous pour poster

Par aoineko

Hero (546)

Portrait de aoineko

24-05-2022, 09:23

Did you check the assembly code generated by call_func()?

Your assembly code for func() looks good, but you can compare it with the assembly code that SDCC would generate if the function was written in C.

static __sfr __at(0x02f) out2f;
void func(uint8_t i, uint8_t j, uint8_t k)
{
    out2f = i;
    out2f = j;
    out2f = k;
}

Par pizzapower

Expert (82)

Portrait de pizzapower

24-05-2022, 10:15

Good idea! C-generated func looks different, specially the last 3 lines.

out (#2f), a

ld a, l
out (#2f), a

ld iy, #02
add iy, sp
ld a, (iy+#00)
out (#2f), a

pop hl
inc sp
jp (hl)

I suppose inc sp is used to re-adjust the stack that was truncated to put an 8-bit value. It can be found in call_func as well when pushing the last 8-bit parameter into the stack. I am not a fan of all these changes. It looks kinda hackish and obfuscates clean assembly code with SDCC-specific idiom and it also makes the reuse of previously compiled libraries impossible. I guess that the need for pop hl is not documented anywhere in the manual to boot. Eek!

And call_func looks weird. Does it really need to call push de twice?

ld c, #00
ld e, c
ld a, e
add a, #20
ld d, a
ld a, e
add a, #0b
ld b, a
push bc
push de
push de
inc sp
ld l, #06
ld a, b
call _func

Par Bengalack

Hero (609)

Portrait de Bengalack

28-05-2022, 08:14

pizzapower wrote:

Good idea! C-generated func looks different, specially the last 3 lines.

pop hl
inc sp
jp (hl)

Never seen this from generated code before, but this does the same as a ret. But inc sp should only be there if __z88dk_callee is used I think. In your case, the callee should not clean up the stack.

pizzapower wrote:

And call_func looks weird. Does it really need to call push de twice?

ld c, #00
ld e, c
ld a, e
add a, #20
ld d, a
ld a, e
add a, #0b
ld b, a
push bc
push de
push de
inc sp
ld l, #06
ld a, b
call _func

is there more context here? Normally those pushes has correcsponding pop de, pop bc after the func-call + a djnz "push bc"-loc.

You might have found a bug in sdcc of course. Always a possibility, but I know they have a huge number of automatic tests, so it should work Smile

Par Bengalack

Hero (609)

Portrait de Bengalack

28-05-2022, 12:52

To help the compiler optimize you could also have added this to the signature:

__preserves_regs(b,c,d,e,iyl,iyh);

which would remove one push/pop-pair of de.