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

Setting up BATCH files

This is our second lesson, part 1 of programming the 128k ZX Spectrum. I have split up this lesson into several parts as I have greatly expanded the source code. Even though the code is still simple, I have split up the code into three different areas. Our eventual goal for this lesson is to combine our 3 memory sections into one program. We are not, in this lesson, going for anything spectacular, this is going to be simple code. Even though we are combining 3 sections into one program, the ram-low section and Ram 0 section are going to be storing assembly variables. The nice thing is that assembly variables can be easily seen in C. The irritating thing is that you have to slightly alter the name making it difficult to trace at times. In the end, we are going to be able to place different routines in different memory sections.

If you missed out on lesson 1, it’s available at https://zxspectrumcoding.wordpress.com/2021/07/25/128k-programming-basics-using-z88dk-and-the-sccz80-compiler-part-1/ .

First, let’s download the project files, they are available at

https://github.com/andydansby/ZX_Spectrum_128k_programming_lesson2 .

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_2. 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.

So, let’s dive in.

So many batch files

One of the first things you will notice is the abundance of batch files. Each batch file has its purpose. I do not recommend running any other batch file other than the one in the root directory – compile.bat.

The compile batch file is a top-down batch file that runs each separate batch in the correct order. Let’s take a look at it.

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 utils
    copy "bas2tap.exe" "..\magic"
    copy "loader.bas" "..\magic"
cd ..

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

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

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

echo on

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

cd OBJECTS
	call magic.bat
cd ..

cd UNCONTENDED
    call rammain.bat
cd ..

cd magic
    call voodoo.bat
cd ..

REM FINISHED COMPILE
call beep.bat

This is all straightforward, we set our path and clear the screen. We then switch to the utils directory and we create a loader and copy it to the /magic directory. We compile the contended section, then the RAM 0 section, and finally the uncontended section as object files and move them to the \objects directory. Next, we call the magic.bat batch file which combines the object files. Next, we run a true compile on the combined object files. The binaries are copied and then we run the voodoo batch file. Finally, we run beep.bat to let you know it’s all done.

I do want to point out bas2.tap was written by Martijn van der Heide and converts basic programs written in notepad to a TAP file loadable on a ZX Spectrum. We’ll explore it a bit later on.

If you are using code::blocks, let’s look at our ramlow batch file, you will notice it is a bit different than our ramlow found in lesson 1. In code::blocks, it’s found under others > contended > ramlow.bat.

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

cd codemaps
	del contended.o
cd ..

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

if not exist "contended.o" (
call error.bat
)

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

@REM Cleanup
del zcc_opt.def

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

@call beep.bat

This batch file starts by doing a bit of a clean-up and then creates an object file.

If you don’t know what an object file is, read the article I wrote about it at https://zxspectrumcoding.wordpress.com/2019/12/25/z88dk-bank-switching-part-6-object-file/ .

An object file is the compiled output of a program before the linker is run. The magic of getting Z88dk to output an object file is by using the -o flag. This tells the compiler to create an object file and stop. You will notice a contended.o file output when you run this compiler string.

If you have any trouble due to a coding error, the contended object file is not created, if that’s the case, then our next line

if not exist "contended.o" (
call error.bat
)

is kicked into gear. The error batch file just pauses the compiling at that point so that you will notice the error. The batch file is very simple.

echo on
rem AN ERROR HAS OCCURED in CONTENDED
rem press CTRL-C to exit
pause

It gives notice that you have an error in the contended object file and then pauses the compilation.

Next from the main compile batch file, we run the RAM0 batch file. This is almost a carbon copy of the contended batch file. It can be found in code::blocks, it’s found under others > RAM0 > RAM0.bat.

Everything that is in the RAM0 batch file is in the contended batch file, so no new ground is broken. The same goes for the uncontended batch file.

The uncontended batch file is found in code::blocks under others > UNCONTENDED > uncontended.bat.

Once created, all object files are then moved to the /object directory. We will then run magic.bat.

The magic batch file looks like this.

rem combine all object files

if not exist "contended.o" (
call error.bat
)
if not exist "RAM0.o" (
call error.bat
)
if not exist "uncontended.o" (
call error.bat
)


z80asm --output=ramALL.o contended.o RAM0.o uncontended.o

if not exist "ramALL.o" (
call error.bat
)

z80nm ramALL.o > ramALL.txt
	@rem output.txt will have the listing of routines

REM move the object file created above into the RAMMAIN directory to compile the test.c file
copy "ramALL.o" "..\UNCONTENDED\"

First, we perform an error check, if any errors are found (missing an object file), we will pause the compiling process.

Next comes an important line.

z80asm --output=ramALL.o contended.o RAM0.o uncontended.o

Z80asm is part of Z88dk and is an assembler, this takes all of our object files (remember that object files are the compiled output of your C program) and combines them all. This creates a file called ramAll.o, which in itself is an object file.

Again, we perform an error check, pause if there is an error, or continue to z80nm with the line.

z80nm ramALL.o > ramALL.txt

This takes the output of the ramAll object file and places all of the routines and their section in a text file for the programmers’ observation. The ramall object file is then moved to the uncontended folder.

With all of the object files now created, we are ready to perform a true compile and linker. This is performed by calling the rammain batch file.

The rammain batch file is found in code::blocks under others > UNCONTENDED > uncontended.bat.

The file looks like this:

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

rem compile to a TAP file

rem --------------------------------------------------------------
zcc +zx -v -m -startup=30 -clib=new ramALL.o -o compiled -pragma-include:zpragma.inc
rem check above for errors
rem --------------------------------------------------------------

@rem have at the end
call cleanup.bat
call beep.bat

The line of interest is this compilation string.

zcc +zx -v -m -startup=30 -clib=new ramALL.o -o compiled -pragma-include:zpragma.inc

This will take our ramALL object file (which is a combination of all of our sections) and then link them together. This will output as binary files.

Finally, we perform a cleanup of the files which will copy the files to the /magic directory.

Finally, there’s one major batch file left, the voodoo batch file. Here we are going to take all of our binary files and create actual tap files to send to the ZX Spectrum 128k. The voodoo batch file is found in code::blocks under others > magic > voodoo.bat.

The file looks like this:

appmake +zx -b compiled_CODE.bin -o uncontended.tap --org 32768 --noloader --blockname uncontended

appmake +zx -b compiled_CONTENDED.bin -o contended.tap --org 24200 --noloader --blockname contended

appmake +zx -b compiled_BANK_00.bin -o bank00.tap --org 49152 --noloader --blockname bank00

bas2tap -a10 -s128K loader.bas basloader.tap

copy /b basloader.tap + bank00.tap + contended.tap + uncontended.tap  1output.tap

del "loader.bas"
del "bank00.tap"
del "uncontended.tap"
del "contended.tap"

move "compiled_BANK_00.bin" "bin\"
move "compiled_CODE.bin" "bin\"
move "compiled_CONTENDED.bin" "bin\"
move "compiled_UNASSIGNED.bin" "bin\"

Here we are introduced to appmake as a standalone program. Its job is to take the binary file and make a tap file loadable into the ZX Spectrum.

The line

appmake +zx -b compiled_CODE.bin -o uncontended.tap --org 32768 --noloader --blockname uncontended

takes our uncontended binary and sets the originating address at 32768 which is in the uncontended portion of memory.

The line

appmake +zx -b compiled_CONTENDED.bin -o contended.tap --org 24200 --noloader --blockname contended

takes our contended binary and sets the originating address at 24200 which is in the contended portion of memory.

The line

appmake +zx -b compiled_BANK_00.bin -o bank00.tap --org 49152 --noloader --blockname bank00

takes our RAM0 section of memory and places it at the address at 49152 which is the start of the RAM0 section.

At this point, we have our 3 different TAP files to load into our 128k Spectrum (at this point it will work on a 48k ZX Spectrum), now we will need to make our basic loader, this is done with our next command in our voodoo batch file.

bas2tap -a10 -s128K loader.bas basloader.tap

As mentioned earlier, bas2tap was written by Martijn van der Heide of ThunderWare Research Center. It will take the file loader.bas, name the loader as basloader.tap, set the file name as 128k, and autostarts the basic loader in line 10.

The BASIC loader is found in code::blocks under others > utils > loader.bas open the file and you will see:

10 CLEAR VAL "24199"
15 PRINT 65536 - USR 7962
25 POKE 23739,111
30 REM LOAD"loadscreen"SCREEN$
40 LOAD""CODE 49152
50 LOAD""CODE 24200
60 LOAD""CODE 32768
65 POKE 23739,244
70 RANDOMIZE USR 32768

This BASIC program is pretty simple. We clear the addresses above 24199. Line 15 is a developer line to show how much space we have left after loading our BASIC program (in this particular case, we have 115 bytes leftover). Line 25 is a system variable poke that will prevent the loading name from printing to the screen. Line 30 is commented out as we do not have a loading screen at all. Line 40-60 load our code in the sections in the order we want. Line 65 redirects the output to show to the screen again (we are using it because we have a simple CRT that does not use control codes). Finally, line 70 runs our C program.

Our next line in the voodoo batch file is:

copy /b basloader.tap + bank00.tap + contended.tap + uncontended.tap  1output.tap

This is a DOS copy command that combines all of our TAP files into a single file called 1output.tap. We use the /b flag to indicate that this is a binary file that we are trying to copy.

Finally, we perform some housekeeping to clear up some files.

Whew, as I said earlier, that’s a lot of batch files. Could it be easier? Yes, it probably could, but it makes the compiling flow good for me and keeps most things simple and separated.

In our next part, we are going to delve into our memory map setup, pragmas and actual C code.

Until next time.

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.

One thought on “128k Programming basics using Z88DK and the SCCZ80 compiler lesson 2, part 1”

Leave a comment