Writing and compiling code for a home made system

Discussion about other targets
Post Reply
mpalmer
New member
Posts: 2
Joined: Thu Aug 20, 2015 10:33 am

Writing and compiling code for a home made system

Post by mpalmer »

Hi there. First time poster, apologies if this is posted in the wrong section.

I'm in the early stages of making my own Z80 system on a chip (based on an FPGA). It's very simple, with a black and white graphic display (no text) and a port (at 0xFF) to control eight LEDs.

I've had no problems when writing assembly code. I place the code into RAM and it ticks along just fine. Once I had success with that it was evident I needed a Z80 C compiler to make the coding aspects faster and easier. I tried both SDCC and Z88DK and decided on the latter for ease of use.

Now I keep hitting a brick wall and I don't know what I'm missing.

I've written some simple C code and compiled into a binary file (with an origin of 0, as that's where the execution point begins). I convert the binary file into a file that can be read by the FPGA, each byte value converted to text in a Xilinx .COE file (this is just a file of text values to be loaded into FPGA internal memory like a kind of 'ROM' - shouldn't be important). When I execute it, nothing happens - the LED port is not accessed, and no other value is written to memory.

I suspect I'm being naive, but if I compile the code with an origin of 0x0000 and place it in memory right there, shouldn't it run?

I've come up with a list of potential problems...
- The origin may be 0x0000 but the execution point is somewhere further in the code, so the program is never running. I'm just 'executing' the data and stack or something.
- There's more to these BIN output files than I know and converting them straight to .COE might be the wrong thing to do.
- There's some problem during compilation. As this is a different computer system I haven't been using any platform selection (like +CPC or +ZX81 etc). However I have been using the stdlib.h to access the bpoke and outp functions. Problem here?
- My C code is catastrophically dumb.

Anyone have any idea what's going on?

Here's my code...

// testing z80 c code

#include <stdlib.h>

main()
{
unsigned long int i = 0;
unsigned char j = 0, k = 0;
static char led_pattern[4] = {0xF0, 0xFF, 0x0F, 0x81}; // runs through some led patterns

while(1) {
for(i = 0; i < 2500000; i++); // to slow things down a bit (Z80 running at 25 MHz)

outp(0x00FF, led_pattern[j++]); // let port at 0xFF

bpoke(0x03ff, k++); // write a counter to memory, can be seen though VGA output
bpoke(0x0300, k);
bpoke(0x10ff, k);


if (j > 3) // for cycling though led_pattern values
j = 0;
}
}
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

mpalmer wrote:I've had no problems when writing assembly code. I place the code into RAM and it ticks along just fine. Once I had success with that it was evident I needed a Z80 C compiler to make the coding aspects faster and easier. I tried both SDCC and Z88DK and decided on the latter for ease of use.
You don't have to choose. The newset version of z88dk in the nightly download allows you to use sdcc to compile in combination with z88dk's library.

You can read about it here (docs under construction):
http://www.z88dk.org/wiki/doku.php?id=temp:front

There is an embedded target already there for embedded systems like yours. Because it's a general target there is no stdio/stdin/stdout but you can use sprintf and sscanf families on strings if you need them.

Using sdcc and the new clib you can compile like so:

zcc +embedded -vn -clib=sdcc_iy --max-allocs-per-node200000 test.c -o test [-lm for floats]

Using sccz80 (z88dk's compiler) compile like this:

zcc +embedded -vn -clib=new test.c -o test [-lm for floats]

sdcc is very good at standards compliance (by default it's C99) and it will produce faster code than sccz80 although the difference is not so dramatic if most execution time is spent in the libraries as they are written in assembly language. We also find that sccz80 tends to generate smaller code especially with longs, statics and floats when these hints are followed for best code generation: http://www.z88dk.org/wiki/doku.php?id=t ... inary_size

For the embedded target if you don't change the ORG address, it defaults to 0 and when the ORG is 0 the crt generates all the z80 restarts and whatnot in the bottom 100 bytes for you. The embedded target also has the z80 clock set at 4MHz by default which you should change if you use the precise delay library routines (there are delay milliseconds and delay cycles functions).

For your particular program, the new c library does not have bpoke() and outp() is now z80_outp(). Bpoke I actually removed because there is a C-standard, yet wordy, way of doing the equivalent ("*(unsigned char *)(0x4000) = 0xaa;") and the namechange on outp() was because we started to make use of namespaces to avoid naming collisions.

z80_outp() will work with both sdcc and sccz80 but sdcc also maps the i/o space to special function registers. You can declare an sfr mapped to some i/o address and then use it like any memory pointer. The sfr can be a 16-bit or 8-bit port. sdcc will then generate inlined in,out instructions rather than function calls to z80_outp().

That's the news :) Now we can talk about the classic library and the version of z88dk you're likely using.
I've written some simple C code and compiled into a binary file (with an origin of 0, as that's where the execution point begins). I convert the binary file into a file that can be read by the FPGA, each byte value converted to text in a Xilinx .COE file (this is just a file of text values to be loaded into FPGA internal memory like a kind of 'ROM' - shouldn't be important). When I execute it, nothing happens - the LED port is not accessed, and no other value is written to memory.

I suspect I'm being naive, but if I compile the code with an origin of 0x0000 and place it in memory right there, shouldn't it run?
How are you setting the ORG address? The ORG is selected inside the CRT (and there will be a default there) so you actually have to change that manually or specify it on the compile line (-zorg=0 I think it is).
- There's more to these BIN output files than I know and converting them straight to .COE might be the wrong thing to do.
The output is exactly what you think it is -- a raw binary that is loaded at the ORG address and executed from the ORG address. With the classic library this binary is meant to be run from RAM as it will contain code, data and bss mixed together. Another thought is the location of the stack pointer should be checked -- again this is set to a default by the CRT.
- There's some problem during compilation. As this is a different computer system I haven't been using any platform selection (like +CPC or +ZX81 etc). However I have been using the stdlib.h to access the bpoke and outp functions. Problem here?
Ah, the +target is mandatory because it selects the CRT, the library to use and the path to the header files. If you don't specify one, it chooses the z88 as target so that's not right for you!

In the classic c lib, the generic target is +test. The crt is here: z88dk/lib/test_crt0.asm and you can see the org is set to 0 and sp to $ffff. (crts for the new clib are in z88dk/libsrc/_DEVELOPMENT/target/{machine}/{machine}_crt.asm)
- My C code is catastrophically dumb.
Anyone have any idea what's going on?
It looks alright to me. I tried your compile with +zx target and it was fine. Embarrassingly, sccz80 crashed for the +test target so I'm looking at that now.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

Embarrassingly, sccz80 crashed for the +test target so I'm looking at that now.
The compile was fine -- my libraries weren't up to date.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

I made sure your program would also work with the new clib and sdcc:

Code: Select all

// testing z80 c code
// zcc +embedded -vn -clib=sdcc_iy --max-allocs-per-node200000 test.c -o test  (sdcc compile)
// zcc +embedded -vn -clib=new test.c -o test  (sccz80 compile)

#pragma output CRT_MODEL = 0       // RAM model
#pragma output CRT_ORG_DATA = 0    // DATA appends to CODE
#pragma output CRT_ORG_BSS = 0     // BSS appends to DATA

#include <stdlib.h>
#include <z80.h>

#define bpoke(a,b) (*(unsigned char *)(a) = b)

main()
{
    unsigned long int        i = 0;
    unsigned char            j = 0, k = 0;
    static unsigned char     led_pattern[4] = {0xF0, 0xFF, 0x0F, 0x81};    // runs through some led patterns

    while(1) {
        for(i = 0; i < 2500000; i++);        // to slow things down a bit (Z80 running at 25 MHz)

        z80_outp(0x00FF, led_pattern[j++]);        // let port at 0xFF

        bpoke(0x03ff, k++);                    // write a counter to memory, can be seen though VGA output
        bpoke(0x0300, k);
        bpoke(0x10ff, k);

        if (j > 3)                            // for cycling though led_pattern values
            j = 0;
    }
}
Alterations:

* bpoke() changed to macro
* outp() changed to z80_outp(), #include <z80.h>
* led_pattern[4] changed to unsigned char. sdcc gave warnings because members have most significant bit set (indicating negative char)
* pragma selects ram model. This means the output binary will contain everything -- code, data and bss put together and the binary should be run from RAM. The default for +embedded is a ROM target where DATA and BSS would be separate out for placement in RAM.
* pragmas to indicate DATA and BSS are appended to CODE section (ORG = 0)


The output is ORGed to 0 by default. Because the ORG is 0, the first few dozen bytes are filled in with z80 restarts, im1 entry and nmi all set to harmless. Total size is about 1600 bytes but that includes a 1k heap (we didn't change), code to init the heap and a mutex with associated error strings, and a few other things that don't need to be there.
mpalmer
New member
Posts: 2
Joined: Thu Aug 20, 2015 10:33 am

Post by mpalmer »

Verrry nice... the +embedded in the new nightly download did the trick with the original code. Thanks a lot!

Will try out with sdcc when I get the chance. Cheers, Matt.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

mpalmer wrote:Verrry nice... the +embedded in the new nightly download did the trick with the original code. Thanks a lot!
Will try out with sdcc when I get the chance. Cheers, Matt.
Glad to hear it worked :)

In the new clib:

I've added z80_bpoke(), z80_wpoke(), z80_lpoke(), z80_bpeek(), z80_wpeek(), z80_lpeek() macros to the z80.h header file which will appear starting with the Aug24 nightly build. We're trying to get the documentation done for a next release so things are still a little bit fluid and these macros are definitely more convenient than writing them out by hand.

In your program you do have to watch the memory map. The new clib compile is around 1500-1600 bytes occupying address 0-1600 or so, due in large part to the 1024-byte heap, and your program is poking around at address 1023 in the compiled binary. There's no ill effect because that poke is inside the heap which goes unused.

Although I set the pragmas to override the embedded target defaults to generate a RAM model program, you do probably want the default compressed ROM model. For standalone targets, when the C program exits the most sensible thing to do is to restart it so that's what the embedded target does by default (CRT_ENABLE_RESTART is 1). But to restart successfully, variables have to be properly initialized and that doesn't happen with the RAM model. With the RAM model you only get correct initialization for the first run.

These are details you want to look into at another time and are described here under 'pragmas':
http://www.z88dk.org/wiki/doku.php?id=temp:front#crt

The library also supplies terminal drivers so if you're ambitious you could add text terminals with windowing and line editing capability. A keyboard and some sort of display is all that is needed. Without the keyboard you can still do the output terminals.
alvin
Well known member
Posts: 1872
Joined: Mon Jul 16, 2007 7:39 pm

Post by alvin »

In case others come along to read this, some new notes on compiling for an embedded target using the new clib here:

http://www.z88dk.org/wiki/doku.php?id=t ... _with_sdcc

The notes apply to both sdcc and sccz80 compiles. Unfortunately documentation is very much lagging development so what is available is still of a temporary nature.

We're still finding ways to make things easier and what is new compared to the discussion above is you can choose to compile for the embedded target using a ram model (startup=0), rom model (startup=1) or compressed rom model (startup=2) from the compile line without having to configure anything. Just compile and go.

As mentioned above, if the code org is 0 as it is by default, the crt will generate do-nothing restarts, im1 routine and nmi routine. A pragma in the C source allows your program to indicate which of these it will implement. For each implemented restart or interrupt routine, the crt will insert a jump at the correct location to named function that your program must implement.

sdcc using z88dk libs will generate binaries up to half the size of those generated by sdcc alone, though more likely in the 15% smaller range. The more library functions used, the smaller and faster the code will be. sccz80 is normally generating slower code except when the program uses a lot of floats, longs and (less so) statics. In these cases sccz80 can generate faster code as well as smaller code, sometimes 15% smaller.
Post Reply