Getting Started into RC2014 programming

1,469 views
Skip to first unread message

gfcwfzkm

unread,
Jan 29, 2017, 6:42:41 AM1/29/17
to RC2014-Z80
Hi,

I just got my RC2014 and played a bit around with BASIC but I would like to do more.
Are there any good sites that explain how to get into Assembler or C for the Z80 or better, for the RC2014 Board?

A tutorial series would be awesome there.

~gfc

A A

unread,
Jan 29, 2017, 4:25:25 PM1/29/17
to RC2014-Z80

Probably best answered by Philip or Felipu but there is a rc2014 fork of z88dk that is shaping an rc2014-specific target.  z88dk is a z80 development environment with c compiler and assembler tools; you can write in c, asm or a mix of the two.

https://github.com/RC2014Z80/z88dk

On using z88dk for a generic z80 embedded computer you can read this:

https://www.z88dk.org/wiki/doku.php?id=libnew:target_embedded

Every target (like rc2014) is a specialization of that.  The embedded target knows nothing about any attached hardware so there is no underlying stdin, stdout, stderr or FILE mechanism except you can do file i/o to memory (memstreams) and use the sprintf/sscanf families on buffers.

The current rc2014 target targets only a standalone ROM, that is it generates an 8k rom that can replace the basic rom.  Philip's written drivers for the acia connects stdin, stdout, stderr to the acia so you can printf and scanf from that.  8k is pretty tight for c -- there is a quick accumulation of library functions that occurs for all c programs before most of what gets used is in the binary and thereafter the program size largely depends on actual c code.  Other binary generation modes should be added such as generating executables for RAM that can be loaded and run from basic; these could optionally use Philip's drivers as well.  The drives are interrupt driven so they need a way to be hooked into the im 1 interrupt.  On a Basic machine I don't know if that's possible but if not the rc2014 can do what many other retro-computers did and that is use im2 mode and connect that to the isr.  Or someone could try to use the drivers built into Basic.

You can see a sample compile for ROM target in this post:

https://groups.google.com/forum/#!msg/rc2014-z80/TS94mZHt0zg/MddPZ21-FgAJ

Just for fun, another thread implemented a heap sort in basic.  z88dk has a heap container type in its abstract data types library (called a priority queue) that can be used as a heap sort.
Err, maybe I will post that a little later as my nephew just arrived.




phillip.stevens

unread,
Jan 29, 2017, 6:09:26 PM1/29/17
to RC2014-Z80

On Sunday, January 29, 2017 at 4:42:41 AM UTC-7, gfcwfzkm wrote:
 
I just got my RC2014 and played a bit around with BASIC but I would like to do more.
Are there any good sites that explain how to get into Assembler or C for the Z80 or better, for the RC2014 Board?

A tutorial series would be awesome there.


Yes, I was wondering about the best way to answer your question. It depends on what you mean by "getting into Assembler or C", or whether you're speaking specifically about the RC2014 or more generally about learning programming.

Generally, there are lots of tutorials about Z80 assembler on the 'net, and you can go there for the syntax, concepts, and cookbooks like Leventhal-Saville.
I just started writing Z80 assembler by writing Z80 assembler and dug into writing the ACIA code for the RC2014 (with a lot of props from 'net guides and a few old books from Amazon too). A slow process, but learning by doing is the best way.

Because of the way the I started, by deriving from the init code written for the SBC by Grant Searle, the assembler I used is the TASM, and the code and the assembler are in this repo.
 
Probably best answered by Philip or Feilipu but there is a rc2014 fork of z88dk that is shaping an rc2014-specific target.  z88dk is a z80 development environment with c compiler and assembler tools; you can write in c, asm or a mix of the two.

https://github.com/RC2014Z80/z88dk


The z88dk repo is the master now. Our version we'll only use to support some further developments, as they arise. Therefore our version might slip behind the z88dk master, so best to use it where you can.

z88dk has quite a lot of options. The RC2014 target is quite simple, and is based on either a 32k RAM model, a ROM model with 32k RAM, or a compressed ROM model with 32k RAM. The standard model is compressed ROM with 32k RAM. The configuration options are detailed here.

For rc2014, you can use this style command line if you have a single C file called test_example.c and want to generate the 32k RAM / 8k compressed ROM target.

zcc +rc2014 -subtype=rom_zx7 -v -m -SO3 --max-allocs-per-node200000 --c-code-in-asm --list test_example.c -o test_example -create-app

That will create a HEX file that you can load into your RC2014 using an EEPROM writer like the TL866, or other method of your choice.

Enjoy
 

A A

unread,
Jan 29, 2017, 6:52:23 PM1/29/17
to RC2014-Z80


On Sunday, January 29, 2017 at 2:25:25 PM UTC-7, A A wrote:

Just for fun, another thread implemented a heap sort in basic.  z88dk has a heap container type in its abstract data types library (called a priority queue) that can be used as a heap sort.

Here it is:


// zcc +rc2014 -subtype=rom -v -m -SO3 --max-allocs-per-node200000 --c-code-in-asm --list heapsort.c -o heapsort -create-app
//
// -subtype=rom    : make uncompressed rom mapped to address 0x0 (see z88dk/lib/config/rc2014.cfg for options this selects)
// -v              : tell us what zcc is up to, -vn for quiet
// -m              : create a map file of symbols ("timer_start" and "timer_end" defined below will appear in there)
// -SO3            : select the aggressive peephole set
// --max-allocs-per-node200000 : optimization level is set very high
// --c-code-in-asm : intersperse C statements in the .lis file
// --list          : generate a .lis file containing asm translation just prior to link step
// -create-app     : generate an intex hex file .ihx as well as .rom/.bin raw binary to be loaded at address 0x0
//                 : the actual compiler output is heapsort_BSS.bin, heapsort_DATA.bin, heapsort_CODE.bin

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <adt/wa_priority_queue.h>
#include <intrinsic.h>

#pragma output CLIB_OPT_PRINTF       = 0x202   // enable %u%s only
#pragma output CLIB_OPT_SCANF        = 0x2     // enable %u only
#pragma output CLIB_MALLOC_HEAP_SIZE = 5000    // specify a 5k heap

wa_priority_queue_t queue;

// greater than compare for ascending order because we want the priority
// queue to extract largest first and write that to the end of the array

int compare(unsigned int *a, unsigned int *b)
{
    return *b - *a;
}

void main(void)
{
    static unsigned int i;
    static unsigned int num;
    static unsigned int *p;
   
    queue.data = NULL;
   
    while (1)
    {
        // how many numbers are we sorting
        
        printf("\nHow many values do you want to sort? ");
        scanf("%u", &num);
        
        // get memory to hold the numbers
        
        free(queue.data);
        
        if ((num == 0) || ((p = malloc(num*sizeof(*p))) == NULL))
        {
            printf("Can't malloc %u items, try again.\n", num);
            continue;
        }
        
        // fill array with random numbers
        
        for (i=0; i!=num; ++i)
            p[i] = rand();
        
        // print the random numbers generated
        
        printf("\nSORTING SAMPLE OF %u NUMBERS: ", num);
        for (i=0; i!=num; ++i)
            printf("%u%s", p[i], (i != num-1) ? ", " : "\n");

intrinsic_label(timer_start);

        // turn the non-empty random array into a heap
        
        wa_priority_queue_init(&queue, p, num, compare);   // queue contains zero items, max capacity = num
        wa_priority_queue_resize(&queue, num);             // resize to num items, uncovered items in array assumed valid

        // sort items by popping them out of the priority queue max number first
        // could print them here but let's time it without printf
        
        while (!wa_priority_queue_empty(&queue))
            wa_priority_queue_pop(&queue);

intrinsic_label(timer_stop);

        // print the result out
        
        printf("\nAFTER SORTING: ");
        for (i=0; i!=num; ++i)
            printf("%u%s", p[i], (i != num-1) ? ", " : "\n\n");

    }
}


 The abstract data types are modeled on C++ stl containers so if you know C++ you may recognize the priority queue type.  This is C so there are no constructors or destructors but instead *_init and *_destroy.  The containers can only hold two types of data :- bytes (8-bit) or words (16-bit) but words can also mean pointers so there is no limitation.  The above is using "wa_priority_queue_t" which is a word array priority queue.  word = 16-bit items, array = stored in a fixed size array, priority queue = heap.  There is a byte variation and a vector variation; vector means the queue can expand dynamically via realloc.

The compile line is at the top, the final output is "heapsort.bin" or "heapsort.ihx" depending on whether you want a raw binary or intel hex format.  In both cases the ORG is 0x0.

I inserted two assembly language labels using non-standard function "intrinsic_label(...);".  The address corresponding to these two labels can be seen in the heapsort.map file.  This will allow timing of the heapsort using TICKS (a command line z80 emulator that comes with z88dk that counts how long a code fragment takes in z80 cycles).

TICKS simulates a z80 computer with 64k ram and nothing else which means we should avoid interactions with hw like printf and scanf if we want TICKS to work.  If you comment out the printf and scanf and hard-code the number of items to 500 by setting "num=500;", do another compile and find out what the label addresses are from the map file, we can run TICKS to find out how fast the sort was.

From my compile's map file:

timer_start = 0x0fd3
timer_stop = 0x1004

Then run TICKS on the output binary:

ticks heapsort.bin -start fd3 -stop 1004

The result is 4,842,526 cycles or 657 milliseconds @ 7.372800 MHz.


> Because of the way the I started, by deriving from the init code written for the SBC by Grant Searle, the assembler I used is the TASM, and the code and the assembler are in this repo.

There's nothing wrong with using another assembler but I will just point out that if you used z88dk's assembler all you need to do is put your asm code in a file ending in ".asm" and add it to the zcc compile line.  zcc will assemble it for you, knowing that this is an asm file from the file extension.  You can add as many .c or .asm files as you want to the zcc compile line and all of it will be put into the output binary.  You do have to realize that this is a linking assembler - most other assemblers are not -- so you should become aware of the scoping keywords PUBLIC (make this symbol visible outside this asm file) and EXTERN (this symbol is defined outside this asm file) as well as SECTION (put the assembled bytes into the specified container which will be placed according the to memory map during the linking phase).  This is described a bit here: https://www.z88dk.org/wiki/doku.php?id=libnew:target_embedded#mixing_c_and_assembly_language

If you don't want to write C at all, you just need to create a "_main" function in one of your asm files.  You can still call the c library if you want or make use of the rc2014 driver by calling its assembly language functions directly.

phillip.stevens

unread,
Jan 29, 2017, 7:24:46 PM1/29/17
to RC2014-Z80
There's nothing wrong with using another assembler but I will just point out that if you used z88dk's assembler all you need to do is put your asm code in a file ending in ".asm" and add it to the zcc compile line.

If you don't want to write C at all, you just need to create a "_main" function in one of your asm files.  You can still call the c library if you want or make use of the rc2014 driver by calling its assembly language functions directly.

A little bit off topic, but is there a zcc (z80asm) config option to force generation of Z180 (64180) specific code?
Asking for a friend...

Phillip

 

A A

unread,
Jan 29, 2017, 9:38:20 PM1/29/17
to RC2014-Z80


On Sunday, January 29, 2017 at 5:24:46 PM UTC-7, phillip.stevens wrote:
A little bit off topic, but is there a zcc (z80asm) config option to force generation of Z180 (64180) specific code?
Asking for a friend...


I thought z80asm supported the z180 but it may have been removed while changes have been made in the past year.
https://github.com/z88dk/z88dk/issues/49

I wouldn't mind adding a basic z180 target (like the embedded is for the z80 now) just so we can make some library changes to accommodate the chip.  The 8x8 integer multiply probably means the integer math subroutines can be faster.  I don't think we can improve on the float math because it's tied very much into being able to hold two floats in BCDEHL and BCDEHL'.  The z80.h and multithreading related may need some tweaks.

I'm not as familiar with the z180 either so I'm not sure what a basic memory map should look like given the banked memory space and how the on chip peripherals should be handled but that could be a longer term goal with input from others.
 

phillip.stevens

unread,
Jan 29, 2017, 11:18:20 PM1/29/17
to RC2014-Z80
On Sunday, January 29, 2017 at 5:24:46 PM UTC-7, phillip.stevens wrote:
A little bit off topic, but is there a zcc (z80asm) config option to force generation of Z180 (64180) specific code?
Asking for a friend...


I thought z80asm supported the z180 but it may have been removed while changes have been made in the past year.
https://github.com/z88dk/z88dk/issues/49

I wouldn't mind adding a basic z180 target (like the embedded is for the z80 now) just so we can make some library changes to accommodate the chip.



I'm not as familiar with the z180 either so I'm not sure what a basic memory map should look like given the banked memory space and how the on chip peripherals should be handled but that could be a longer term goal with input from others.

I think the only immediate issue is the IO base and interrupt location assignment, but more on the enhancement request.
The memory map will depend on the individual implementation.

A future RC2014-z180 board would integrate the clock and ACIA onto one board, but use the existing digital IO, RAM, and ROM boards.

The cool thing about the z180 is the doubled internal clock. So using the same crystal speed as the existing rc2014-z80, the internal clock would be 14MHz and hence everything could be twice as fast internally.

Colin Little

unread,
Feb 7, 2017, 1:13:46 PM2/7/17
to RC2014-Z80
Hi, I've been playing with z88dk to compile a simple 'helloworld .c file (saved as test_example.c) but cannot seem to get rid of  'cannot copy crt0 file' error
I am using the example instruction:
zcc +rc2014 -subtype=rom_zx7-v -m -SO3 --max-allocs-per-node200000 --c-code-in-asm --list test_example.c -o test_example -create-app
Has anyone found a way through this???
Thanks

A A

unread,
Feb 7, 2017, 1:55:07 PM2/7/17
to RC2014-Z80


On Tuesday, February 7, 2017 at 11:13:46 AM UTC-7, Colin Little wrote:
Hi, I've been playing with z88dk to compile a simple 'helloworld .c file (saved as test_example.c) but cannot seem to get rid of  'cannot copy crt0 file' error
I am using the example instruction:
zcc +rc2014 -subtype=rom_zx7-v -m -SO3 --max-allocs-per-node200000 --c-code-in-asm --list test_example.c -o test_example -create-app
Has anyone found a way through this???


test_example.c:

#include <stdio.h>

#pragma output CLIB_OPT_PRINTF = 0   // disable all % converters

void main(void)
{
    printf("Hello World!\n");
}

 
zcc +rc2014 -subtype=rom_zx7 -v -m -SO3 --max-allocs-per-node200000 --c-code-in-asm --list test_example.c -o test_example -create-app

works for me but note I added a space before the "-v" so it didn't run into the previous option.

With -v active the full compile story:

ZX7: Optimal LZ77/LZSS compression by Einar Saukas
File converted from 197 to 93 bytes! (delta 3)
type "c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/target/rc2014/rc2014_crt.asm" >> "C:\Users\alvin\AppData\Local\Temp\zcc15643.asm"

PROCESSING test_example.c
zsdcpp -D__Z88DK -DZ80 -DRC2014 -D__RC2014__ -D__RC2014 -D__SDCC -D__SDCC_IY -DZ88DK_USES_SDCC=1 -I"." -I"c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/include/_DEVELOPMENT/sdcc" "test_example.c" "C:\Users\alvin\AppData\Local\Temp\zcc15642.i2"
zpragma < "C:\Users\alvin\AppData\Local\Temp\zcc15642.i2" > "C:\Users\alvin\AppData\Local\Temp\zcc15642.i"
zsdcc --constseg rodata_compiler --max-allocs-per-node200000 --reserve-regs-iy -mz80 --no-optsdcc-in-asm --c1mode --emit-externs --no-peep --peep-file "c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/sdcc_peeph.3" < "C:\Users\alvin\AppData\Local\Temp\zcc15642.i" -o "C:\Users\alvin\AppData\Local\Temp\zcc15642.opt"
copt "c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/sdcc_opt.1" < "C:\Users\alvin\AppData\Local\Temp\zcc15642.opt" > "C:\Users\alvin\AppData\Local\Temp\zcc15642.op1"
copt "c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/sdcc_opt.9" < "C:\Users\alvin\AppData\Local\Temp\zcc15642.op1" > "C:\Users\alvin\AppData\Local\Temp\zcc15642.op2"
copt "c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/sdcc_opt.2" < "C:\Users\alvin\AppData\Local\Temp\zcc15642.op2" > "C:\Users\alvin\AppData\Local\Temp\zcc15642.asm"
type "C:\Users\alvin\AppData\Local\Temp\zcc15642.asm" >> "C:\Users\alvin\AppData\Local\Temp\zcc15642.tmp"
type "C:\Users\alvin\AppData\Local\Temp\zcc15642.tmp" >> "C:\Users\alvin\AppData\Local\Temp\zcc15642.asm"
z80asm -D__SDCC -D__SDCC_IY -I"c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/target/rc2014" --list -s -I"c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/lib" "C:\Users\alvin\AppData\Local\Temp\zcc15642.asm"

GENERATING OUTPUT
type "C:\Users\alvin\AppData\Local\Temp\zcc15642.lis" >> "test_example.c.lis"
z80asm -b -d -o"test_example" -m -s -L. -L"c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/lib/sdcc_iy" -D__SDCC -D__SDCC_IY -I"c:\Projects\z80\z88dk\z88dk\lib\config\..\..\/libsrc/_DEVELOPMENT/target/rc2014" --list -irc2014 "C:\Users\alvin\AppData\Local\Temp\zcc15643.asm" "C:\Users\alvin\AppData\Local\Temp\zcc15642.o"
appmake +rom --ihex -b "test_example" -c "C:\Users\alvin\AppData\Local\Temp\zcc15643"
type "C:\Users\alvin\AppData\Local\Temp\zcc15643.map" >> "test_example.map"
type "C:\Users\alvin\AppData\Local\Temp\zcc15643.lis" >> "test_example.lis"

Output is "test_example.bin" or "test_example.ihx" ORGed at 0x0 for burning to rom or running from ram at that address if you can do it.

If that doesn't work do you have a current copy of z88dk? Cannot copy crt0 file sounds like it can't find an rc2014 target.
You should see a directory z88dk/libsrc/_DEVELOPMENT/target/rc2014

Installation info:
https://github.com/z88dk/z88dk#installation

In particular, if you don't want to clone via github, the nightly build will give you a complete package with binaries
(if windows or macosx) and z80 libraries already built. Just set up the environment variables and go. Update by
getting another nightly build and replacing the current one (if you have your own files in there, save them first
but preferably work someplace else). Linux you still have to DIY.

A A

unread,
Feb 7, 2017, 2:19:52 PM2/7/17
to RC2014-Z80


On Tuesday, February 7, 2017 at 11:55:07 AM UTC-7, A A wrote:

test_example.c:

#include <stdio.h>

#pragma output CLIB_OPT_PRINTF = 0   // disable all % converters

void main(void)
{
    printf("Hello World!\n");
}

 

I should add there seems to be a small risk in this program that the full "hello world" will not print.  By default when the program exits, the crt will disable interrupts and enter an infinite loop.  If the acia driver has not completely sent that output string by the time interrupt are disabled, it won't get a chance to finish since it's interrupt driven.  You can get rid of the "di" the crt inserts on exit by adding a pragma "#pragma output CRT_ENABLE_EIDI = 0" into the c source or you can just do a forever loop at the end of main().


A A

unread,
Feb 7, 2017, 2:51:34 PM2/7/17
to RC2014-Z80
I should really collect my thoughts over a half hour but the other possibility is you haven't set the ZCCCFG environment
variable properly to point at the right place.

https://www.z88dk.org/wiki/doku.php?id=temp:front#installation

Colin Little

unread,
Feb 7, 2017, 3:25:22 PM2/7/17
to RC2014-Z80
Many thanks AA for all your help and advice on this - there are a number of things that I need to do - I will certainly re-load with the latest nightly build and start again with the pointers you have given me......

Colin Little

unread,
Feb 7, 2017, 4:57:01 PM2/7/17
to RC2014-Z80
Many thanks again AA - reloaded everything and compiling 100% correct !!!!
Reply all
Reply to author
Forward
0 new messages