retro-vault / libcpm3-z80

Standard C library for CP/M 3.
MIT License
11 stars 7 forks source link

Compile error with the newest SDCC #3

Closed agl7 closed 1 year ago

agl7 commented 1 year ago

Hello,

when I try to compile the library i get following error:

# Create build dir.
mkdir -p /home/ag/sdcc/libcpm3-z80/build
# Remove bin dir (we are going to write again).
rm -f -r /home/ag/sdcc/libcpm3-z80/bin
# And re-create!
mkdir -p /home/ag/sdcc/libcpm3-z80/bin
# Pass current build and bin directories.
make -C lib/libsdcc-z80 BUILD_DIR=/home/ag/sdcc/libcpm3-z80/build BIN_DIR=/home/ag/sdcc/libcpm3-z80/bin
make[1]: Entering directory '/home/ag/sdcc/libcpm3-z80/lib/libsdcc-z80'
# Create build dir.
mkdir -p /home/ag/sdcc/libcpm3-z80/build
# Remove bin dir (we are going to write again).
rm -f -r /home/ag/sdcc/libcpm3-z80/bin
# And re-create!
mkdir -p /home/ag/sdcc/libcpm3-z80/bin
make -C src
make[2]: Entering directory '/home/ag/sdcc/libcpm3-z80/lib/libsdcc-z80/src'
sdcc -c -o /home/ag/sdcc/libcpm3-z80/build/_divslong.rel _divslong.c --std-c11 -mz80 -I. --no-std-crt0 --nostdinc --nostdlib --debug
sdcc -c -o /home/ag/sdcc/libcpm3-z80/build/_divslonglong.rel _divslonglong.c --std-c11 -mz80 -I. --no-std-crt0 --nostdinc --nostdlib --debug
sdcc -c -o /home/ag/sdcc/libcpm3-z80/build/_divulong.rel _divulong.c --std-c11 -mz80 -I. --no-std-crt0 --nostdinc --nostdlib --debug
sdcc -c -o /home/ag/sdcc/libcpm3-z80/build/_divulonglong.rel _divulonglong.c --std-c11 -mz80 -I. --no-std-crt0 --nostdinc --nostdlib --debug
sdcc -c -o /home/ag/sdcc/libcpm3-z80/build/_fs2schar.rel _fs2schar.c --std-c11 -mz80 -I. --no-std-crt0 --nostdinc --nostdlib --debug
./float.h:76: error 91: extern definition for '__fslt' mismatches with declaration.
at 1: error 177: previously defined here
./float.h:77: error 91: extern definition for '__fseq' mismatches with declaration.
make[2]: *** [Makefile:19: /home/ag/sdcc/libcpm3-z80/build/_fs2schar.rel] Error 1
make[2]: Leaving directory '/home/ag/sdcc/libcpm3-z80/lib/libsdcc-z80/src'
make[1]: *** [Makefile:43: src] Error 2
make[1]: Leaving directory '/home/ag/sdcc/libcpm3-z80/lib/libsdcc-z80'
make: *** [Makefile:60: lib/libsdcc-z80] Error 2
ag@Galaxy:~/sdcc/libcpm3-z80$ 

SDCC version 4.2.0

My Linux system is a Debian system!

Best Regards agl7

tstih commented 1 year ago

Thank you. Looking into it. It seems they did some changes in compiler. I tried with 4.0.0 (the one that comes with apt-get) and it works fine. Will fix it this week.

agl7 commented 1 year ago

Hello tstih,

I have Debian unstable in my /etc/apt/sources.list and Debian 12 (bookworm) is released since 3 days, so I have at this moment "bookworm". So I have under this Debian version SDCC with version 4.2.0! You can download this new version 4.2.0 also from the Web site of SDCC. 4.2.0 is the last stable version.

When you look for the errors, please test also hello.c / hello.com if it runs under CP/M 3.0 !!!

If you don't have a CP/M 3.0 system, download my Z80 emulator (https://yaze-ag.de). Compile it and test hello.com with it. There is CP/M 3.0 running default. With the utility R.COM inside the emulator you can import files from the HOST system (Linux). Run R.COM without any parameter then it shows you how you can use this utility. The filenames under Linux have to be in lower case and will be imported as upper case ("hello.com" will be imported as "HELLO.COM")!

Best Regards agl7

agl7 commented 1 year ago

Hello tstih, I want to ask you if you have fixed the libcpm3-z80 for the SDCC version 4.2.0? Best Regards agl7

agl7 commented 1 year ago

Hello Tomaz Stih,

I want to ask if you have fixed the problem of your libcpm3-z80 with the SDCC 4.2.0 Compiler? Will you also test if the programm hello.c/hello.com runs under CP/M 3.0?

Best Regards Andreas

Am 12.06.23 um 20:59 schrieb Tomaz Stih:

Thank you. Looking into it. It seems they did some changes in compiler. I tried with 4.0.0 (the one that comes with apt-get) and it works fine. Will fix it this week.

— Reply to this email directly, view it on GitHub https://github.com/tstih/libcpm3-z80/issues/3#issuecomment-1587903609, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJGXV6IJUUKREPLKL5IV57LXK5RIVANCNFSM6AAAAAAZDHIQR4. You are receiving this because you authored the thread.Message ID: @.***>

-- Dipl.-Ing.(FH) Andreas Gerlich Dominikus-Zimmermann-Str. 6 D-86956 Schongau Germany

Tel.: +49(0)8861/6 90 95 11 (phone with voice box) +49(0) xxx/x xx xx xx (mobile phone)

GPG-Key: 5D8EA14A Fingerprint: 4450 3C20 E8B4 E492 D822 BEB4 3B97 10F8 5D8E A14A

University of Ulm, Germany open source project "Yet Another Z80 Emulator by AG": -->http://yaze-ag.de/

rofl0r commented 1 year ago

i got it to compile by applying this patch to libsdcc-z80 directory: http://ix.io/4zvV

rofl0r commented 1 year ago

can confirm that the hello.com produced by running make in the hello/ directory doesnt work, it just outputs gibberish in CPM3, and then fails with an invalid BDOS call. curl http://ix.io/4zyO | base64 -d | gunzip > hello.com. it's also 15 KB, which is far from optimal. even on linux with musl libc i can create a native hello binary that's only 5KB using gcc hello.c -o hello -s -static

tstih commented 1 year ago

Problem diagnosed. This is causing it:

The linker sdld does not support function-level linking. The standard library, therefore, uses one source file per function.

Usually linker only takes what it needs, but SDCC takes the entire file if it needs one function from it. Hence to reduce the size I need to separate functions each to their own file. Any volunteers for the task? If not I'll fix this over the weekend.

As for the gibberish the CP/M calls 5 for BDOS functions and the only two functions used are to write char and to return to OS. After I'm done with reducing the library size we can test this with a few lines of code. Standard library does some trivial initializations For Which CP/M and platform are you using? If you don't provide your own platform initialization by passing make PLATFORM=<platform name> it will use z80-none. This will define _memtop function (see: platform.c) as size_t ptr=(size_t )0x0006; so perhaps it is not pointing to the bottom of your BDOS as BDOS address is 5, not 6 (a bug?) _memtop is used by the memory allocator and it should point to the end of your heap. For the test hardcode it to 0xc000 and try.

rofl0r commented 1 year ago

Hence to reduce the size I need to separate functions each to their own file

i wrote this tool for this task many years ago https://github.com/rofl0r/c-split . some manual touchups might be required after use.

Which CP/M and platform are you using?

yaze-ag uses z80 and CPM/3 by default, however afaik it's an opensource rewrite of it, not digital's.

i wasn't aware that PLATFORM needs to be passed - will try it out later today.

tstih commented 1 year ago

I started splitting the files. It goes fast.

rofl0r commented 1 year ago

when i set PLATFORM to any value, the else branch in platform.c kicks in and the lib is compiled without '__memtop' and '_libinit', causing linker errors when making the hello example. what is supposed to happen ?

rofl0r commented 1 year ago

i added

+void libinit() {}
+
+/* mem top */
+size_t _memtop() {
+    return 0xc000;
+}

to the #else branch in platform.c resulting hello.com is : curl http://ix.io/4zT5 | base64 -d | gunzip > hellocpm3.com but it's still printing gibberish.

tstih commented 1 year ago

That was correct fix - this is how the platform works. If you invent your own platform, you provide your own libinit() which initializes your platform library and _memtop() which returns top of heap. You are meant to create your own library with these two functions (and possibly some time.h overrides) to fully connect the library. I am almost done with spliting the files, but I can't reproduce the gibberish. Is there a CP/M emulator that I can use to reproduce gibberish and try to fix it? When you say Yaze, are you using Yaze/ZPM3N10 for tests?

tstih commented 1 year ago

One theory about program exit is: if you are using CP/M that only has a floppy drive then the floppy drive needs an operating system installed. Because the library exits the program by executing BDOS reset:

    ;; BDOS exit (reset) return control to CP/M.
    ld      c,#0
    jp      5

This reloads the CP/M. Could this be the problem?

rofl0r commented 1 year ago

i downloaded https://www.mathematik.uni-ulm.de/users/ag/yaze-ag/devel/yaze-ag-2.51.3.tar.gz , untarred it, ran make -f Makefile_linux_64_intel_corei7 then in the same dir ./yaze which loads the emulator and a couple "ydsk" files it ships. the one mounted on A: has by default CPM3. once loaded, it shows a couple screens with usage information, one of them is how to use r.com to copy host files to inside the emulator. so i did r /tmp/hellocpm.com and then hellocpm, and gibberish is the result, followed by some BDOS error about accessing the wrong drive. if i run hellocpm with an argument, no gibberish is printed, but neither hello world. paste follows:

A>hellocpm 232321

PM3 Error On M0:  Invalid Drive
Function = 33  File = �0���Ee."�

for some reason my ix.io link above doesnt work anymore, the gzipped hellocpm can be found here: https://0x0.st/H1te.b64

tstih commented 1 year ago

I'm sorry, I can't reproduce it. It is possible that there is a problem with newer version of SDCC or the patch that you applied or with your yaze. I placed hello.com inside the yaze dir hello ectory (not the root tmp) and run then do r tmp/hello.com and it all works well. I'm attaching screenshot and hello.png (which you can rename to hello.com and run inside CP/M). Does it work with my executable, compiled by: SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ez80_z80/ds390/TININative/ds400/hc08/s08/stm8/pdk13/pdk14/pdk15 4.0.0 #11528 (Linux) yaze

tstih commented 1 year ago

I'll install latest SDCC and try with that and your patch to see what happens.

rofl0r commented 1 year ago

yes, your binary works. i was assuming you'd be testing with SDCC 4.2.0 too, so it's almost certain that the issue stems from there. maybe a codegen bug, maybe some new sections, maybe some libsdcc-z80 functions differing from what the builtin libsdcc expects. speaking of which, your libsdcc-z80 seems almost a vanilla copy of what's shipped with sdcc, why not just use the builtin one ?

tstih commented 1 year ago

The reason for separating libsdcc-z80 from sdcc was because in the sdcc it is closely coupled with the Standard C Libary and I wanted to have code separation. The functions in libsdcc-z80 are compiler support only: they are there to enable types such as long and float. And Standard C library is a library. I'll upgrade to sdcc 4.2 today, after I finish separating files and see what happens. Will report results in the evening.

tstih commented 1 year ago

Size reduction is finished. An empty C program is now 480 bytes and the hello world is 6KB, but with a reason. Hello world program supports types float and long hence all functions from libsdcc-z80 for these types this are linked. And it supports a large set of parameters. In the following days I'll try to provide a more economic version of some of the functions (I have them already developed in assembler in idp-udev library). Probably I can come to 4KB, but not much more. Z80 simply doesn't have support for types and if you want full printf set of parameters it takes division, multiplication to do it.

Now I'm going to upgrade the library for SDCC 4.2.

tstih commented 1 year ago

Good news and bad news. 1) I was able to reproduce the bug by installing a Virtual Machine with SDCC 4.2 2) The compiler is much more optimized and final code is now 5KB 3) The garbage happens at very basic level. I switched off all library initialization and replaced printf with puts creating 0.3KB binary, but it still crashes. 4) Since this code is now nothing but startup plus one function, I'm comparing the new startup code with my own, probably something with segments and initialization.

tstih commented 1 year ago

Allright, the problem has been fixed! In version 4.1.12 sdcc changed the function calling convention i.e. how parameters are passed to functions. To make matters worse it made this the default convention without warning making all legacy assembly code, such as our bdos.s fail. If your entire program is in C that's fine. But if parts are in assembly then you need to add __sdcccall(0) to all legacy functions or rewrite them. Please confirm the fix so I can close the issue. The amount of testing the library is limited so I'lll be glad of any other bug you find.

rofl0r commented 1 year ago

nice job! i renamed the old checkout of libcpm3, then did a fresh git clone --recursive https://github.com/tstih/libcpm3-z80 and i immediately noticed the message Submodule path 'lib/libsdcc-z80': checked out '54f0893f45d9caf5a89a440e0dce5ab30 which seems to point to an old version of libsdcc-z80. and indeed, after make we get the old errors about float.h and __fslt etc. btw i looked over your changes, and couldnt find changes to platform.c using the _memtop we discussed, which means that passing PLATFORM=whatever to make, libcpm3 remains broken.

tstih commented 1 year ago

Allright. These are two issues and only one of them is a bug.

1) Submodule has not been refreshed. That's was a bug and I just fixed it.

2) The missing two externals are a feature. When you don't pass the platform it uses default (i.e. your top of heap is under BDOS). If you pass platform it is expected that you link a platform library that brings its own libinit()which initializes platform specific library at the same time it initializes standard library.

For example, I created platform partner and linked the libraries libsdcc-z80, libcpm3-z80, and libpartner. libpartner brings its own _memtop and libinit().

The simplest way to mitigate this is by simply defining these two in your main.c like

void libinit(void) {} int _memtop(void) { return 0xc000 }

But I suspect that the default version without platform would work with the standard cpm just as well. So if you just do make it should work But if you do make PLATFORM=yaze then add a libyaze and implement these two functions specific to yaze.

As for the platform.c changes are not needed. The _memtop gets the address of BDOS from the address 6 which is correct as on the address 5 there is opcode to call whatever is on address 6 and 7. And that's top of heap so it will never overwriite BDOS, but will use everything up to BDOS.

And in CP/M we are allowed to overwrite CCP.

image

tstih commented 1 year ago

I did one more change to make sure it compiles in both -- the old SDCC used by most people because they use package managers and don't compile SDCC, and the new one. It seems to work with both now.

rofl0r commented 1 year ago

did a fresh clone, make -j8, then went to hello/ , make, resulting in a 685 byte hello.com. transfered it curl https://0x0.st/H1d6.com.b64 | base64 -d | gunzip > heelo.com, loaded it in yaze, but it still prints gibberish:

A>r heelo.com

READ V-2.23-3.01 (20-Jan-2017) YAZE-AG V2.51
Read from "heelo.com" and write to "HEELO.COM".
0.75kB written.
closeFile, transfered bytes: 685

A>heelo
C) 98 CALDERAgggggggggggggggggggggggggggggg�"D��! B�HH@ $!��!$�"A!      D$D�$!" �      A� 
          �I��<��C) 98 CALDERAgggggggggggggggggggggggggggggg�"D��! B�HH@        $!��!$�"A!      D$D�$!" �      A� 
                                  �I��<��
ZPM3 Error On U0:  Invalid Drive
Function = 100 File = .
rofl0r commented 1 year ago

maybe you find it of interest, my own hello.c that works with sdcc and inside yaze:

/* test program cp/m - z80 - use sdcc without crt0. */

/* the first function is the entry point, do not remove this */
void _start(void) __naked{
    //ld c, #0
    //call 5

    __asm;
    ld sp, #0xa000
    add ix, sp
    call _main
    jp 0
    __endasm;
}

/* arg1 comes in hl */
void putstr(const char *const str) __sdcccall(1)
{
  str;
  __asm
    ex de, hl
    ld c,#9
    call 5
  __endasm;
}

int main(void) {
  putstr("hello world\r\n$");
  return 0;
}
sdcc -mz80 --code-loc 0x100 --no-std-crt0 --debug hello_real.c 
objcopy -Iihex -Obinary hello_real.ihx hello_real.com
tstih commented 1 year ago

It's just not my day, shouldn't be doing this on Sunday.. I re-introduced the bug by irrational pre-processor checks.

#if(__SDCC_VERSION_MAJOR>=4 && __SDCC_VERSION_MINOR>=1 && __SDCC_VERSION_PATCH>=12)
/* nothing */
#else
#define __sdcccall(a)
#endif

This of course fails because 4.3.0 only satisfies the first two conditions.

Now it's really fixed.

rofl0r commented 1 year ago

indeed, everything works now! thanks!

i guess the issue can be closed.