sccz80 allow return 8-bit values in L register

Requests for features
Post Reply
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

I had the issue returning 8-bit values (like bool) in L not read properly by the caller, until someone noticed me that __smallc uses only HL or DEHL.

It would be good if we could return 8-bit values using only L register, so the asm code could be more consistent to use with sccz80 or sdcc. Also sometimes is more convenient to check bits or similar then returning as required, i.e:

Code: Select all

; we have hl = pointer
...
; return false if condition, make a copy of L
ld e, l
ld l, 0 ; return false if condition check
; make some check 
ret nz ; i.e. this is the flag condition to return false for the check
ld l, e ; if not restore L and continue
...
We have jp cc instead ret cc, but as said sometimes is better the ret cc version, and cannot do push hl because if we ret then the stack is wrong. This is specially true for moved code, on which we must avoid the use of JP, and JR lacks of some condition checks that RET has.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: sccz80 allow return 8-bit values in L register

Post by jorgegv »

Mmmm acording to documentation, the L register is used when returning 8-bit values from functions.

And the following code seems to confirm it:

Code: Select all

// zcc +zx --list --c-code-in-asm main.c -o main -create-app

#include <stdint.h>

uint8_t return_twice( uint8_t a ) {
    return a*2;
}

void main( void ) {
    uint8_t a;
    a = return_twice(17);
}
Looking at the .c.lis file generated, the computed value is stored in L at the end of the function:

Code: Select all

     5                          ; Function return_twice flags 0x00000200 __smallc
     5                          ; unsigned char uint8_treturn_twice(unsigned char a)
     5                          ; parameter 'unsigned char a' at sp+2 size(1)
     5                                  C_LINE  5,"main.c::return_twice::0::0"
main.c::return_twice::0::0:
     5                          ._return_twice
     5                                  C_LINE  5,"main.c::return_twice::0::0"
     5                          ;    return a*2;
     5                                  C_LINE  6,"main.c::return_twice::1::1"
main.c::return_twice::1::1:
     6                                  C_LINE  6,"main.c::return_twice::1::1"
     6  0000  210200                    ld      hl,2    ;const
     6  0003  39                        add     hl,sp
     6  0004  6e                        ld      l,(hl)
     6  0005  2600                      ld      h,0
     6  0007  29                        add     hl,hl
     6  0008  2600                      ld      h,0
     6  000a  c9                        ret

And the return value it is received (and used) in main:

Code: Select all

     9                          ; Function main flags 0x00000000 __stdc
     9                          ; void main()
     9                                  C_LINE  9,"main.c::main::0::2"
main.c::main::0::2:
     9                          ._main
     9                                  C_LINE  9,"main.c::main::0::2"
     9                          ;    uint8_t a;
     9                                  C_LINE  10,"main.c::main::1::3"
main.c::main::1::3:
    10                                  C_LINE  10,"main.c::main::1::3"
    10                          ;    a = return_twice(17);
    10                                  C_LINE  11,"main.c::main::1::3"
    11                                  C_LINE  11,"main.c::main::1::3"
    11  000b  3b                        dec     sp
    11  000c  210000                    ld      hl,0    ;const
    11  000f  39                        add     hl,sp
    11  0010  e5                        push    hl
    11                          ;17;
    11                                  C_LINE  12,"main.c::main::1::3"
    12  0011  211100                    ld      hl,17   ;const
    12  0014  e5                        push    hl
    12  0015  cd0000                    call    _return_twice
    12  0018  c1                        pop     bc
    12  0019  d1                        pop     de
    12  001a  7d                        ld      a,l
    12  001b  12                        ld      (de),a
    12                          ;}
    12  001c  33                        inc     sp
    12  001d  c9                        ret
So what is exactly your problem? :cool:
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

Re: sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

This issue https://github.com/z88dk/z88dk/issues/2264
And according to __smallc calling convention, it uses HL, probably that's why I had issues when using only L for a bool return value and reading without manual casting.
So seems an issue about reading the value, that the compiler assumes using HL, not the function itself returning value in L. Notice that the assembly for the function sets H to 0, as using only L that should not be required.
stefano
Well known member
Posts: 2137
Joined: Mon Jul 16, 2007 7:39 pm

Re: sccz80 allow return 8-bit values in L register

Post by stefano »

In short, you're expecting sccz80 to force h to zero, right?
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

Re: sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

stefano wrote: Wed May 24, 2023 10:28 am In short, you're expecting sccz80 to force h to zero, right?
The opposite. It actually forces to zero because __smallc calling convention uses it. I'd like to be able to use only L as return value, ignoring H.
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

Re: sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

Or if you mean for reading the returning value (instead the generated code), then yes use it as 0 or simply ignore. Could be done probably applying an autocasting to the function returning type.
User avatar
dom
Well known member
Posts: 2076
Joined: Sun Jul 15, 2007 10:01 pm

Re: sccz80 allow return 8-bit values in L register

Post by dom »

I'm reluctant to add another calling convention when there's one that's fairly close. The __z88dk_sdccdecl convention does work in this way (with R->L pushing of parameters + char parameters pushed as 8 bits).

I suspect the way I'd deal with this without too much upheaval is:

Code: Select all

ex de,hl
ld hl,0
; check
ret nz
ex de,hl
ret
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

Re: sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

Notice we lose extra registers if using 16-bit. Depending the situation the backup could be done in A and use DE for the check itself.

Using __z88dk_sdccdecl could be used but better from the start, so don't requiring to rewrite so many functions for correctly reading parameters.

Currently what I do is manually casting to the expected 8-bit value at the caller.
Timmy
Well known member
Posts: 392
Joined: Sat Mar 10, 2012 4:18 pm

Re: sccz80 allow return 8-bit values in L register

Post by Timmy »

I'd rather propose __smallc to not set h to zero in that case. It basically wastes a register.

That said, I'd rather prefer no change at all.
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

Re: sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

DarkSchneider wrote: Wed May 24, 2023 10:58 am Could be done probably applying an autocasting to the function returning type.
I think a compiler option to do that for 8-bit returning values could do the trick. Casting works nice and no matter if the functions uses HL or only L for returning values, as the issue is later when the compiled code reads that value.
Timmy
Well known member
Posts: 392
Joined: Sat Mar 10, 2012 4:18 pm

Re: sccz80 allow return 8-bit values in L register

Post by Timmy »

DarkSchneider wrote: Sun May 28, 2023 7:23 am I think a compiler option to do that for 8-bit returning values could do the trick. Casting works nice and no matter if the functions uses HL or only L for returning values, as the issue is later when the compiled code reads that value.
This sounds like you just want the compiler to insert a LD H,0 for you into every function that returns a bool or a byte type?

Doesn't that basically just make every single function larger? And if you have many functions (written by you, or other peoples libraries) the size of whole executable blows up with this change?

I am really against any change that increase sizes for every single function I write, especially because 80% of my time is because the compiled code is too big and I resort to writing in assembly only to reduce the sizes.

My guess is that the easiest solution would be to append LD H,0 into the very few functions that you write. My guess that would only make the total size larger by like 20 bytes instead of 500.
User avatar
jorgegv
Well known member
Posts: 287
Joined: Wed Nov 18, 2020 5:08 pm

Re: sccz80 allow return 8-bit values in L register

Post by jorgegv »

Timmy wrote: Sun May 28, 2023 8:20 am
DarkSchneider wrote: Sun May 28, 2023 7:23 am I think a compiler option to do that for 8-bit returning values could do the trick. Casting works nice and no matter if the functions uses HL or only L for returning values, as the issue is later when the compiled code reads that value.
This sounds like you just want the compiler to insert a LD H,0 for you into every function that returns a bool or a byte type?
But it seems the compiler is already doing that, see my disassembly at the top of the post. The compiler is zeroing H when returning single bytes in L for no useful reason.

And what DarkSchneider asks for is to have the compiler not do it (please correct me if I did not understand correctly)
DarkSchneider
Member
Posts: 71
Joined: Sun Apr 01, 2018 4:02 pm

Re: sccz80 allow return 8-bit values in L register

Post by DarkSchneider »

I am in the side of reading the returning value. And yes inserting LD H, 0 is what actually does, which is not a concern for me, if it was made by that way.
I have no intention for having to rewrite all library functions to a new calling convention.
I was interested in that if a function returns an 8-bit value, and is made in ASM by oneself (instead compiled), be able to read only that 8-bit value from the caller, so it reads only L instead HL.
Actually can by manual casting, I.e:

Code: Select all

uint16_t v = myfunc();
Gets the value of HL even if myfunc is:

Code: Select all

uint8_t myfunc();
But we not set H to 0. But:

Code: Select all

v = (uint8_t)myfunc();
Works and only gets L.
Post Reply