Trying to do CP/M file I/O with the classic library...

alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

alank2 wrote:I've come up with a working thing for getch:

Code: Select all

int getch() __smallc __naked
 {
   __asm

   push af
   push bc
   push de

   ld c,6
   ld e,0xfd
   call 5

   ld h,0
   ld l,a

   pop de
   pop bc
   pop af


   ret
   __endasm;
 }
Do I have to push/pop registers that change? besides HL that are being returned?
No, the only requirement is for zsdcc as it must have ix preserved (and sometimes iy if the compile does not have -clib=sdcc_iy or --reserve-regs-iy):

Code: Select all

int getch() __smallc __naked
 {
   __asm

   push ix

   ld c,6
   ld e,0xfd
   call 5

   ld h,0
   ld l,a

   pop ix
   ret

   __endasm;
 }
Otherwise it looks ok. There isn't really another way to do this :)
If you're concerned about the push ix/pop ix you could condition that with "IF __SDCC ... ENDIF"
If I make it return an unsigned char, can I just set L?
For zsdcc yes but for sccz80 it must be in HL. Again you can do some conditional assembly if you want "IF __SCCZ80 ... ENDIF".
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Ah... __SDCC and __SCCZ80 are a newlib thing.
@dom we'll need something similar for classic lib.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Also note that the return type is int (16 bits) so if you want to return 8-bits it would have to be changed to unsigned char. Standard C wants int on that function because you're also supposed to be able to return EOF which is typically -1.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

I know I read somewhere what the different between -clib=sdcc_ix and -clib=sdcc_iy was, but I can't find it now.

If I use "-clib=sdcc_iy" then do I have to preserve IX, or IY ?

I only plan on compiling this code with "-clib=sdcc_iy"
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

alank2 wrote:I know I read somewhere what the different between -clib=sdcc_ix and -clib=sdcc_iy was, but I can't find it now.
"-clib=sdcc_iy" adds "--reserve-regs-iy" to the compile line so that sdcc does not use the IY register (which works most of the time but the compiler will still use IY on occasion; on these occasions it does not expect IY to be preserved). The compile also uses the IY version of the new library which means the library will use IY as its index register in the asm implementation. So in this compile, sdcc uses IX and the library uses IY so that most of the time they stay away from each other and the code size is a bit smaller.

"clib=sdcc_ix" (to which "--reserve-regs-iy" can be manually added) uses the IX version of the new library. So sdcc uses IX for its frame pointer and the library uses IX as index register in the asm implementation. The library must take extra steps to preserve IX and this leads to larger code.

It also happens that when sdcc is given "--reserve-regs-iy" it has a different code generation pattern, one that is more easily optimized in the peephole step. So we've added a lot of optimizations that take advantage of this at -SO3 level. With "-SO3" and "--reserve-regs-iy" in place you can get better code out, much better than sdcc itself.

It's always preferred to use "-clib=sdcc_iy" but there are some targets that only have one index register available so in these cases, there may be no choice but to use "-clib=sdcc_ix".
If I use "-clib=sdcc_iy" then do I have to preserve IX, or IY ?
I only plan on compiling this code with "-clib=sdcc_iy"
Just IX. For "-clib=sdcc_ix" it would have to be both to be safe. You can distingush them further in conditional code with "IF __SDCC_IY..." or "IF __SDCC_IX". "__SDCC" is defined for both cases. Newlib only for these defines for the moment.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

Thank you for the help!!!
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

I'm getting an error:

\AppData\Local\Temp\zcc5AFC4.asm"
Error at file 'main.c' line 1455: syntax error
1 errors occurred during assembly

main.c is not 1455 lines long. If I look at the zcc5AFC4.asm:

1454: ld e, a
1455: ld a, +((_s2) / 256)
1456: adc a,0x00
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

alank2 wrote:I'm getting an error:

\AppData\Local\Temp\zcc5AFC4.asm"
Error at file 'main.c' line 1455: syntax error
1 errors occurred during assembly

main.c is not 1455 lines long. If I look at the zcc5AFC4.asm:

1454: ld e, a
1455: ld a, +((_s2) / 256)
1456: adc a,0x00
That's not a syntax error. Do you have a zip available so I can try the compile myself?

The error reporting was changed recently to provide more information to debuggers. It requires more information from the compilers but zsdcc does not provide that information yet so if an error is found in zsdcc's output by the assembler or linker, the error line number will refer to the translated asm file instead of the c source.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I'd be remiss if I didn't add the preferred way of integrating asm functions into projects.

The original inlined C:

Code: Select all

int getch() __smallc __naked
 {
   __asm

   push ix

   ld c,6
   ld e,0xfd
   call 5

   ld h,0
   ld l,a

   pop ix
   ret

   __endasm;
 }
The preferred way is to put the asm implementation into a separate file:

"getch.asm"

Code: Select all

SECTION code_user

PUBLIC _getch
PUBLIC asm_getch

_getch:

   ; gather params from stack

asm_getch:

   ; asm entry point and implementation

   push ix

   ld c,6
   ld e,0xfd
   call 5

   ld h,0
   ld l,a

   pop ix
   ret
And then a protoype in a header file tells the compiler how to call the function:

Code: Select all

extern int getch(void);
You can add "__smallc", "__z88dk_fastcall", "__z88dk_callee" decorations as needed. A new one not possible with the other method is a "__preserves_regs(a,b,c,..)" list that informs the compiler which registers are left unmodified by the asm function. Then zsdcc can generate code knowing the function call won't change some registers. In this case a call to bdos will wipe out everything so a preserve_regs list won't be used.

A compile then adds "getch.asm" to the compile line as part of list of source files.
User avatar
dom
Well known member
Posts: 2076
Joined: Sun Jul 15, 2007 10:01 pm

Post by dom »

alvin wrote:Ah... __SDCC and __SCCZ80 are a newlib thing.
@dom we'll need something similar for classic lib.
I don't think we do, in classic we compile the library once to work with both sdcc and sccz80. Hence we follow sccz80 standards which work with sdcc with the following modifications to pure sccz80 convention:

- ix has to be saved if there are any use of ix
- char arguments, in this case either promote to int or decorate the prototype with "__z88dk_sdccdecl __smallc" (in that order) which will make sccz80 use sdcc char calling conventions when calling and also when generating C functions
- floating point - I've not got an easy recipe for this since I've not run into it yet
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

alvin wrote:That's not a syntax error. Do you have a zip available so I can try the compile myself?
The code is something I want to keep private - hopefully the below is enough to figure out why it compiles with -SO2, but fails with -SO3.

This is from the ASM file in the function where the line number was near...

-SO2 - compiles ok:

Code: Select all

;        ---------------------------------
; Function test
; ---------------------------------
_test:
        push        ix
        ld        ix,0
        add        ix,sp
        push        af
        dec        sp
        ld        hl,_s1
        push        hl
        call        _myfunc2
        pop        af
        ld        e,0x00
        ld        (ix-2),0x00
        ld        (ix-1),0x00
l_test_00111:
        ld        a, +((_s1) & 0xFF)
        add        a,(ix-1)
        ld        l, a
        ld        a, +((_s1) / 256)
        adc        a,0x00
        ld        h, a
        ld        b, (hl)
        ld        d, e
        inc        d
        ld        a, b
        or        a, a
        jr        Z,l_test_00107
        push        de
        push        bc
        inc        sp
        call        _myfunc3
        inc        sp
        ld        b, l
        push        bc
        inc        sp
        call        _myfunc1
        inc        sp
        ld        c, l
        pop        de
        ld        (ix-3),c
        ld        a,(ix-2)
        or        a, a
        jr        Z,l_test_00105
        ld        c, e
        ld        e, d
        ld        a, +((_s2) & 0xFF)
        add        a, c
        ld        c, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        b, a
        ld        a,(ix-3)
        add        a,0x1d
        add        a,+((_mystring2) & 0xFF)
        ld        l, a
        ld        a, +((_mystring2) / 256)
        adc        a,0x00
        ld        h, a
        ld        a, (hl)
        ld        (bc), a
        ld        (ix-2),0x00
        jr        l_test_00112
l_test_00105:
        ld        a,(ix-3)
        sub        a,0x1c
        jr        NZ,l_test_00102
        ld        (ix-2),0x01
        jr        l_test_00112
l_test_00102:
        ld        c, e
        ld        e, d
        ld        a, +((_s2) & 0xFF)
        add        a, c
        ld        c, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        b, a
        ld        a, +((_mystring2) & 0xFF)
        add        a,(ix-3)
        ld        l, a
        ld        a, +((_mystring2) / 256)
        adc        a,0x00
        ld        h, a
        ld        a, (hl)
        ld        (bc), a
l_test_00112:
        inc        (ix-1)
        jr        l_test_00111
l_test_00107:
        ld        a,(ix-2)
        or        a, a
        jr        Z,l_test_00109
        ld        c, e
        ld        e, d
        ld        a, +((_s2) & 0xFF)
        add        a, c
        ld        c, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        b, a
        ld        a, (_mystring2 + 28)
        ld        (bc), a
l_test_00109:
        ld        hl,_s2
        ld        d,0x00
        add        hl, de
        ld        (hl),0x00
        ld        hl,_s2
        push        hl
        ld        hl,___str_2
        push        hl
        call        _printf
        ld        sp,ix
        pop        ix
        ret
        SECTION rodata_compiler
___str_2:
        DEFM "   :%s"
        DEFB 0x0a
        DEFB 0x0d
        DEFB 0x00
        SECTION code_compiler
-SO3 - fails with error message:

Code: Select all

;        ---------------------------------
; Function test
; ---------------------------------
_test:
        push        ix
        ld        ix,0
        add        ix,sp
        dec        sp
        ld        hl,_s1
        push        hl
        call        _myfunc2
        pop        af
        ld        c,0x00
        ld        e,c
        ld        (ix-1),0x00
l_test_00111:
        ld        a, +((_s1) & 0xFF)
        add        a,(ix-1)
        ld        l, a
        ld        a, +((_s1) / 256)
        adc        a,0x00
        ld        h, a
        ld        d, (hl)
        ld        b, c
        inc        b
        ld        a, d
        or        a, a
        jr        Z,l_test_00107
        push        bc
        push        de
        push        de
        inc        sp
        call        _myfunc3
        inc        sp
        pop        de
        ld        a,l
        push        de
        push        af
        inc        sp
        call        _myfunc1
        inc        sp
        pop        de
        pop        bc
        ld        a, e
        or        a, a
        jr        Z,l_test_00105
        ld        e, c
        ld        c, b
        ld        a, +((_s2) & 0xFF)
        add        a, e
        ld        e, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        d, a
        ld        a, l
        add        a,0x1d+<(_mystring2)
        ld        l, a
        ld        a, +((_mystring2) / 256)
        adc        a,0x00
        ld        h, a
        ld        a, (hl)
        ld        (de), a
        ld        e,0x00
        jr        l_test_00112
l_test_00105:
        ld        a, l
        sub        a,0x1c
        jr        NZ,l_test_00102
        ld        e,0x01
        jr        l_test_00112
l_test_00102:
        ld        d, c
        ld        c, b
        ld        a, +((_s2) & 0xFF)
        add        a, d
        ld        b, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        d, a
        ld        a, +((_mystring2) & 0xFF)
        add        a, l
        ld        l, a
        ld        a, +((_mystring2) / 256)
        adc        a,0x00
        ld        h, a
        ld        a, (hl)
        ld        l, b
        ld        h, d
        ld        (hl), a
l_test_00112:
        inc        (ix-1)
        jr        l_test_00111
l_test_00107:
        ld        a, e
        or        a, a
        jr        Z,l_test_00109
        ld        e, c
        ld        c, b
        ld        a, +((_s2) & 0xFF)
        add        a, e
        ld        e, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        d, a
        ld        a, (_mystring2 + 28)
        ld        (de), a
l_test_00109:
        ld        b,0x00
        ld        hl,_s2
        add        hl, bc
        ld        (hl),0x00
        ld        hl,_s2
        push        hl
        ld        hl,___str_2
        push        hl
        call        _printf
        pop        af
        pop        af
        inc        sp
        pop        ix
        ret
        SECTION rodata_compiler
___str_2:
        DEFM "   :%s"
        DEFB 0x0a
        DEFB 0x0d
        DEFB 0x00
        SECTION code_compiler
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Thanks alank2, it is enough. I can see the syntax error:

Code: Select all

add    a,0x1d+<(_mystring2)
There is a rule that is failing to combine these two adds properly:

Code: Select all

add    a,0x1d
add    a,+((_mystring2) & 0xFF)
I'll see if I can spot the rule.

If you come on before I find it, would you mind translating the same file to asm with the applied rules in the asm output as comments so the specific rule can be looked up?

zcc +cpm -vn -a --fverbose-asm -clib=sdcc_iy -SO3 --max-allocs-per-node200000 foo.c
(use same optimization settings)

-> foo.c.asm , copy same function for posting.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

It takes a long time to compile...

Code: Select all

;        ---------------------------------
; Function test
; ---------------------------------
;        Register assignment might be sub-optimal.
; Stack space usage: 1 bytes.
_test:
        push        ix
        ld        ix,0
        add        ix,sp
        dec        sp
;        genIpush
;fetchPairLong
;fetchLitPair
        ld        hl,_s1
        push        hl
;        genCall
        call        _myfunc2
        pop        af
;        genAssign
;        genAssign
        ld        c,0x00
        ld        e,c
; peephole z88dk-606an2
;        genAssign
;        AOP_STK for _test_c1_1_506
        ld        (ix-1),0x00
;        genLabel
l_test_00111:
;        genPlus
;        AOP_STK for _test_c1_1_506
        ld        a, +((_s1) & 0xFF)
        add        a,(ix-1)
        ld        l, a
        ld        a, +((_s1) / 256)
        adc        a,0x00
        ld        h, a
;        genPointerGet
        ld        d, (hl)
;        genPlus
        ld        b, c
        inc        b
;        genIfx
        ld        a, d
        or        a, a
        jr        Z,l_test_00107
; peephole 154 changed absolute to relative conditional jump.
;        genIpush
        push        bc
        push        de
        push        de
        inc        sp
;        genCall
        call        _myfunc3
        inc        sp
        pop        de
        ld        a,l
; peephole z88dk-621
;        genIpush
; peephole 44 eleminated dead pop/push pair.
        push        de
        push        af
        inc        sp
;        genCall
        call        _myfunc1
        inc        sp
        pop        de
; peephole z88dk-621
        pop        bc
; peephole z88dk-621
;        genAssign
; peephole 9 loaded l from l directly instead of going through a.
; peephole 0 removed redundant load from l into l.
;        genIfx
        ld        a, e
        or        a, a
        jr        Z,l_test_00105
; peephole 154 changed absolute to relative conditional jump.
;        genAssign
        ld        e, c
;        genAssign
        ld        c, b
;        genPlus
        ld        a, +((_s2) & 0xFF)
        add        a, e
        ld        e, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        d, a
;        genPlus
        ld        a, l
;        genPlus
; peephole 91 removed loads by exploiting commutativity of addition.
        add        a,0x1d+<(_mystring2)
; peephole z88dk-485e5
        ld        l, a
        ld        a, +((_mystring2) / 256)
        adc        a,0x00
        ld        h, a
;        genPointerGet
        ld        a, (hl)
;        genAssign (pointer)
;        isBitvar = 0
        ld        (de), a
;        genAssign
        ld        e,0x00
;        genGoto
        jr        l_test_00112
; peephole 153 changed absolute to relative unconditional jump.
;        genLabel
l_test_00105:
;        genCmpEq
        ld        a, l
        sub        a,0x1c
; peephole 62 removed jp by using inverse jump logic
; peephole 149 removed unused label l_test_00136$.
        jr        NZ,l_test_00102
; peephole 155 changed absolute to relative conditional jump.
; peephole 63 removed jp by using inverse jump logic
; peephole 149 removed unused label l_test_00137$.
;        genAssign
        ld        e,0x01
;        genGoto
        jr        l_test_00112
; peephole 153 changed absolute to relative unconditional jump.
;        genLabel
l_test_00102:
;        genAssign
        ld        d, c
;        genAssign
        ld        c, b
;        genPlus
        ld        a, +((_s2) & 0xFF)
        add        a, d
        ld        b, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        d, a
;        genPlus
        ld        a, +((_mystring2) & 0xFF)
        add        a, l
        ld        l, a
        ld        a, +((_mystring2) / 256)
        adc        a,0x00
        ld        h, a
;        genPointerGet
        ld        a, (hl)
;        genAssign (pointer)
;        isBitvar = 0
;fetchPairLong
        ld        l, b
        ld        h, d
        ld        (hl), a
;        genLabel
l_test_00112:
;        genPlus
;        AOP_STK for _test_c1_1_506
        inc        (ix-1)
;        genGoto
        jr        l_test_00111
; peephole 153 changed absolute to relative unconditional jump.
;        genLabel
l_test_00107:
;        genIfx
        ld        a, e
        or        a, a
        jr        Z,l_test_00109
; peephole 154 changed absolute to relative conditional jump.
;        genAssign
        ld        e, c
;        genAssign
        ld        c, b
;        genPlus
        ld        a, +((_s2) & 0xFF)
        add        a, e
        ld        e, a
        ld        a, +((_s2) / 256)
        adc        a,0x00
        ld        d, a
;        genPointerGet
        ld        a, (_mystring2 + 28)
;        genAssign (pointer)
;        isBitvar = 0
        ld        (de), a
;        genLabel
l_test_00109:
;        genPlus
;fetchPairLong
;fetchLitPair
        ld        b,0x00
        ld        hl,_s2
; peephole z88dk-339a
        add        hl, bc
;        genAssign (pointer)
;        isBitvar = 0
        ld        (hl),0x00
;        genIpush
;fetchPairLong
;fetchLitPair
        ld        hl,_s2
        push        hl
;        genIpush
;fetchPairLong
;fetchLitPair
        ld        hl,___str_2
        push        hl
;        genCall
        call        _printf
        pop        af
        pop        af
;        genLabel
; peephole 149 removed unused label l_test_00113$.
;        genEndFunction
        inc        sp
        pop        ix
        ret
        SECTION rodata_compiler
___str_2:
        DEFM "   :%s"
        DEFB 0x0a
        DEFB 0x0d
        DEFB 0x00
        SECTION code_compiler
;        genLabel
;        genFunction
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

It takes a long time to compile...
Yes that's max optimization but I meant use the same optimization level you're using to make sure the same code pattern happens :) But it's all good, the problem is still there.

Can I get you to make a small change to file "z88dk/libsrc/_DEVELOPMENT/sdcc_peeph.3"

Replace:

Code: Select all

replace restart {
        add        a,#%1
        add        a,#%2
} by {
        add        a,#%1+%2
        ; peephole z88dk-485e5
}
with

Code: Select all

replace restart {
        add        a,#%1
        add        a,#%2
} by {
        add        a,#%1+#%2
        ; peephole z88dk-485e5
}
and see if the problem goes away?
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

This changes the error to : integer '270' out of range
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Instead of fixing it, I think the rule needs to be removed so I will do that now:

Code: Select all

//replace restart {
//    add    a,#%1
//    add    a,#%2
//} by {
//    add    a,#%1+#%2
//    ; peephole z88dk-485e5
//}
The reason is this substitution does not have the same behaviour for the carry flag and we've been bitten by that in additions before where sdcc splits 16-bit or 32-bit additions into 8-bit quantities that need carry propagated properly. The sdcc peepholer is able to detect if register values are used after a code snippet but it can't tell if the carry flag state is important.
alank2
Member
Posts: 116
Joined: Wed Mar 01, 2017 7:24 pm

Post by alank2 »

It does compile now with -SO3; thanks!!!
Post Reply