128k Programming basics using Z88DK and the SCCZ80 compiler lesson 1

Unassociated Programming

This is the first part of a multipart series that will ease us into programming on the 128k Sinclair ZX Spectrum. Today, we are going to start by creating 3 unassociated programs that will compile to different sections of memory. Since the lower portion of memory in the ZX Spectrum is contended (meaning shared with the ULA), it is thus a bit slower and programs running in it are subject to being halted for a short while. We will still compile for contended as well as uncontended memory.

Today, we are going to start at some basics of 128k programming, this will be creating 3 simple programs all very similar with 1 exception, where the program is placed in memory.

First let’s download the project files, they are available at https://github.com/andydansby/ZX_Spectrum_128k_programming_lesson1. The IDE that we are going to be using is CodeBlocks (available at https://www.codeblocks.org/) though you can still use notepad ++ if you prefer it.

The version of Z88DK that I am using is 1.99c. We are going to be using the SCCZ80 and the new library. The directory that I am using for my installation of Z88DK is C:\z88dk199c and the directory that I am using for my example is C:\z88dk199c\games\128k_programming_1. If you use a different drive or directory, you will have to adjust the batch files accordingly. All of the text will be written as if you are using the same directories as I am.

Finally, I am using Windows 10 for my OS, if you are using Linux, you will have to compile another way.

OK, let’s open file explorer and browse to C:\z88dk199c\games\128k_programming_1\128k programming 1. The file to open with CodeBlocks is 128k programming 1.cbp. You will be presented with a lot of files, don’t let that get overwhelming, codeblocks will help you with organizing these files. The reasoning behind the abundance of files is that we are in reality creating 3 programs for this lesson. The 3 programs cannot really reference or even see one another, that will come in a different lesson. For now, we are going to be creating 3 TAP’s.

The first file we will be looking at is found in the Others folder. Let’s open the ramlow.bat file.

Batch files

Here’s our batch script.

SET PATH=c:\z88dk199c;c:\z88dk199c\bin;c:\z88dk199c\lib\;c:\z88dk199c\lib\clibs;c:\z88dk199c\lib\config

cd codemaps
	del objects.o
cd ..

del contended.tap

cls

rem this creates an object file
zcc +zx -vn -SO3 -c -clib=new -o objects.o -startup=30 --fsigned-char @ramlow.lst

rem compile to a TAP file
zcc +zx -vn -SO3 -m -lm -clib=new @ramlow.lst -pragma-include:zpragma.inc -startup=30  -create-app -o contended


copy "objects.o" "contended.o"
move "contended.o" "..\"
move "objects.o" "codemaps\"
move "contended.map" "codemaps\"


@REM Cleanup
del zcc_opt.def
del contended_CODE.bin
del contended_UNASSIGNED.bin

REM a nice map view
cd codemaps
	echo on
	@REM all these objects match up
	z80nm objects.o
	z80nm objects.o > contended.txt
	copy "contended.txt" "..\"
	echo off
cd ..


@call beep.bat

The line:

SET PATH=c:\z88dk199c;c:\z88dk199c\bin;c:\z88dk199c\lib\;c:\z88dk199c\lib\clibs;c:\z88dk199c\lib\config

We are setting the path to your Z88dk installation, adjust your path accordingly.

The lines:

cd codemaps
	del objects.o
cd ..

We are performing a bit of cleanup.

In the line:

rem this creates an object file
zcc +zx -vn -SO3 -c -clib=new -o objects.o -startup=30 --fsigned-char @ramlow.lst

We are creating an object file, we are not really using it now, but it will create an object file and a map of the object file. We will look at it after we compile.

In the line:

rem compile to a TAP file
zcc +zx -vn -SO3 -m -lm -clib=new @ramlow.lst -pragma-include:zpragma.inc -startup=30  -create-app -o contended

We compile the actual program and create a TAP file with it.

Finally in the batch file, look at the command

z80nm objects.o > contended.txt

z80nm is a handy Z88dk program that takes the object file you created and shows the symbols that can be used. In a future lesson, we will take those symbols so that sections of your program can find the addresses and data of functions of other compilations which is how we are going to tie everything together.

LST file

Let’s now open the ramlow.lst file.

contended.c
; ramlow.asm
; .lst files commented with semi-colon
; must have blank line after this

This is a list of files to be compiled, it’s referenced in the both compiling strings in our above batch file with the option @ramlow.lst. This is certainly more convenient than listing each file individually in the compilation string. Here we are compiling contended.c but not ramlow.asm as it is commented out.

Pragma file

Pragma functions are meant as specialized instructions to the compiler to enable certain functions and they are important with our ability for compiling to target 128k machines.

A look at our pragma file will show:

#pragma output __MMAP = -1
// memory map is provided in file "mmap.inc"-1

#pragma output CRT_ORG_CODE = 24000
// main binary program start

#pragma output REGISTER_SP = 0xbffe
// keep stack out of top 16k

// #pragma output CRT_ENABLE_CLOSE = 0
// don't bother closing files on exit
// #pragma output CRT_ENABLE_EIDI       = 0
// don't di on start or exit; loader does di before start
// #pragma output CRT_ON_EXIT           = 0
// jump to address 0 at exit
// 0x10001 = halt on program exit

#pragma output CLIB_MALLOC_HEAP_SIZE = 0
// no heap
#pragma output CLIB_STDIO_HEAP_SIZE = 0
// no heap for opening files

// #pragma output CLIB_FOPEN_MAX = -1
// no FILE* list
// #pragma output CLIB_OPEN_MAX = -1
// no fd table

I’ve listed all of the Pragma’s that I could find and commented most of them out as we are not using them here.

Let’s look at the ones that I am using.

#pragma output __MMAP = -1

This points to the customized memory map that we will be using. The custom memory map is in a file called mmap.inc. We’ll look at it after the Pragma’s.

#pragma output CRT_ORG_CODE = 24000

This is where our CRT (C Run Time) actual program will start. In this particular case we are in contended memory (16384 to 49151) fairly low. This address will allow us to skip screen memory, system variables and allow a BASIC loader to fit before we start loading our binary into memory.

#pragma output REGISTER_SP = 0xbffe

Indicates where we want our stack to be placed. We are keeping our stack in Uncontended memory so that the stack is fast and we are also keeping our stack out of Bankable memory so that it will not be accidentally switched out during a bankswitch routine. The stack builds downwards from this address. The stack could actually go into 0xBFFF, but I am wasting a byte as I sometimes use it as a marker.

#pragma output CLIB_MALLOC_HEAP_SIZE = 0
#pragma output CLIB_STDIO_HEAP_SIZE = 0

I’m not using heaps here, so I am making the size 0.

Finally, in our example program I am not using a heap of any sort, so I am making the size 0 to reduce our program’s footprint.

Memory Map

Here is an important key to 128k programming, the custom memory map. Found in mmap.inc:

The important bits to mmap.inc are:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; sections defined
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SECTION BANK_00
org 0xc000		;	49152
SECTION BANK_01
org 0xc000		;	49152
SECTION BANK_03
org 0xc000		;	49152
SECTION BANK_04
org 0xc000		;	49152
SECTION BANK_06
org 0xc000		;	49152
;;-------------------------------------
SECTION CONTENDED
org 0x5DC0		;	24000		
SECTION CONTENDED_END
org 0x7FFF
;	32767

Here we are defining the start of each SECTION and assigning a memory position to it. We’re not just yet using SECTIONs as they are used officially for assembly files and we’re not ready to go there yet.

That’s about it for our setup files and batch file, let’s now examine our simple C program.

Contended Main

In our sources folder, let’s open contended.c

// using sccz80 1.99c
//assembly files are in ramlow.asm as indicated by ramlow.lst
//#pragma output CRT_ORG_CODE = 24000

//CONTENDED RAM
#pragma include:zpragma.inc

#include <arch/zx.h>
#include <stdio.h>//standard input output
#include <input.h>
#include <stdlib.h>//standard library

#include "variables.h"
#include "externs.h"
#include "routines.h"

void main(void)
{
	doSomething();
	printf ("Main ()\n");

	tom = 2;
	dick = 2;
	harry = add_two_numbers (tom, dick);
	printf ("Add 2 numbers 2+2 = %d\n", harry);
	return (0);
}

Not ground breaking stuff here, but some notes to go with this.

//#pragma output CRT_ORG_CODE = 24000

//CONTENDED RAM
#pragma include:zpragma.inc

You could have actually placed the #pragma in the C program, I like to place it in the zpragma.inc file.

Variables tom, dick and harry are actually defined in variables.h. The functions doSomething and add_two_numbers are found in routines.h. The program returns back to Sinclair Basic, but since the stack is set to below 49152 and the program clears at 24000, you won’t have much RAM to work with.

To compile this program, open an administrator version of command prompt, drill to the

C:\z88dk199c\games\128k_programming_1\CONTENDED (my particular directory), and run ramlow.bat. Your TAP will be compiled in about a minute.

Uncontended

ur uncontended (fast RAM) is our premium area to have our code, but on a ZX Spectrum 128, we can only use from 32168 to 49151, anything high can be bank switched out. That doesn’t mean we can use it, we just have to program 49152 to 65535 in a separate module.

What this affects the most is the stack, we have to make sure that our stack does not go any higher than 49151.

You will immediately notice that the program for the Uncontended RAM (found in main.c under the uncontended folder, is very similar to the contended program.

I’m going to point out our differences.

Rammain.bat only changed the name of the lst list of compiling programs.

The lst is only the list of programs that Z88dk considers for compiling.

Mmap is almost completely identical, check the section uncontended and uncontended_end, the other mmap instead showed contended and contended_end, it doesn’t matter now, we are not using the mmap in this iteration of the program as we are not using sections just yet.

Finally, look at zpragma.inc, The line:

#pragma output CRT_ORG_CODE = 32768
// main binary program start        32768

Maps our program to uncontended RAM, whereas the contended pragma was pointed to 24000.

Our line:

#pragma output REGISTER_SP = 0xbffe
// keep stack out of top 16k

Is the same in the contended RAM, and as it shows, it’s meant to keep the stack out of the Bankable ram.

That’s the only real difference to make the program compile at a different address.

In the uncontended program, we are defining our variables in variables.h instead of in the main.c and we are multiplying two numbers instead of adding two numbers. There are other slight differences but all are superfluous.

Compiling the program is done by running rammain.bat within the uncontended folder from the command prompt, be sure to run the Administrator mode.

RAM 0

Our ram 0 (fast RAM) is in our bankable area and we’re allowed to have code here, but I would advise keeping our main.c out of it, since if we want to bank switch, we will lose this once it’s banked out. It’s also important to keep our stack out of it as well. However, for this demo, we are going to break both of these rules.

For this demo, we are not bank switching and want to run a program from 49152 and we will want to keep the stack at Ramtop so it will not crash on returning to BASIC. Usually, when writing C programs, we will not be returning to BASIC, but we will in this one circumstance.

Again, you will immediately notice that the program for RAM0 (found in main.c under the RAM0 folder, is very similar to the contended program and the uncontended RAM.

Just like the differences between uncontended and contended RAM, the batch file changes slightly to the name of the list we are compiling.

Mmap is identical to the contended version of the same file.

Finally, look at zpragma.inc, The line:

#pragma output CRT_ORG_CODE = 49152
// main binary program start 

Maps our program to RAM0.

Our line:

#pragma output REGISTER_SP = 0xffff
// put stack in the top 16k

Just for our demo, we are keeping our stack in the area where I have told you not to keep your stack, but in this circumstance, we are returning to BASIC, and keeping our stack below the main program will crash BASIC, we are changing the location to RAM TOP.

Compiling the program is done by running RAM0.bat within the RAM0 folder from the command prompt, be sure to run the Administrator mode.

COMPILING ALL

Now, we have our 3 separate programs, I want to point out the compile.bat batch file. This is found in the root directory of our lesson.

SET PATH=c:\z88dk199c;c:\z88dk199c\bin;c:\z88dk199c\lib\;c:\z88dk199c\lib\clibs;c:\z88dk199c\lib\config;C:\Program Files\SDCC\bin

cls

cd CONTENDED
	call ramlow.bat
	move "contended.tap" "..\"
	move "contended.txt" "..\"
cd ..

cd RAM0
	call ram0.bat
	move "ram0.tap" "..\"
	move "ram0.txt" "..\"
cd ..

cd UNCONTENDED
	call rammain.bat
	move "uncontended.tap" "..\"
	move "rammain.txt" "..\"
cd ..

echo on

move "contended.o" "OBJECTS\"
move "ram0.o" "OBJECTS\"
move "uncontended.o" "OBJECTS\"

REM FINISHED COMPILE
call beep.bat

This is the “overhead compiling” batch file. Running this batch file, we will call each program, contended, uncontended and RAM0 and afterward, move each tap and object file from those sub-directories.

That’s about it for lesson 1, we have created 3 separate programs, each mapped to a different memory area. The major problem is that each separate program is an entity unto itself and does not communicate to one another. In our next lesson, we are going to start that process.

As always, happy coding.

Author: andydansby

I'm a hobbyist coder working with the ZX Spectrum. Living in New York state near the Syracuse area. I grew up in Virgina. The first computer I owned was a TRS-80 given to us, my parents bought me was a Timex Sinclair 2068 and the Sinclair bug was set for my childhood.

2 thoughts on “128k Programming basics using Z88DK and the SCCZ80 compiler lesson 1”

Leave a comment