Coding for CPC6128 in 2023
I started a new project in my spare time and slowly new software, which is not a game, is being created for the Amstrad CPC. I thought it might be interesting to share thoughts from this process.
Language and compiler
The first choice to make is the language in which the software is to be written in the first place. As with targeting more modern platforms (Mac, Linux, Windows), it’s worth asking yourself a few questions:
- Which programming languages will help me achieve my goal faster, and which will help me achieve my goal but more slowly?
- Are any of these languages associated with the availability of libraries to facilitate the most common tasks?
- Do any of these languages have a pleasant to use graphical development environment?
- How do I debug my program?
When writing software that has targets modern platforms, languages that help me (friendly syntax, readable, little code necessary to achieve good results, compiler capable of catching many programmer errors) would be, for example, Python, TypeScript, C#, maybe even Java (still touching the idea of “little code, big effect”). On the other side of the medal – the unfavorable – would be assembler, Go, pure JS, Ruby (because I do not know it, and everything I’ve seen in Ruby eats gigabytes of memory), PHP (just because no one will want to read this code afterwards). Somewhere in the middle of this scale would be C, Ada and Object Pascal (they do not hinder work).
For 8-bit platforms, the situation is a little different. Anything that requires a runtime environment, or introduces one internally, is off the limits. Dynamic, on-the-fly languages – there’s no memory or processing power. We need data structures that carry as little additional information as possible added indirectly, processing as early as possible into the most executable and optimized form. Additionally, if we want the compiler to also work on the target platform, the language must allow for fairly simple parsing and solving all dependencies between symbols. This gives us numerous variants of Pascal, C, assembler and Forth.
I decided to start with a higher level language, not to have to worry about the smallest details right away, with the possibility of using inline fragments written in the Z80 assembler.
Yes, I know, real men write in assembler. Real men do not buy honey, they chew bees.
SDCC
First candidate: SDCC (Small Device C Compiler). It’s a C compiler capable of compiling for “small computers” CPUs, of which the most interesting for us will be MOS 6502 (Commodore) and Z80 (Amstrad, Spectrum, MSX).
Development environments? The biggest players on the market aren’t going to be super sueful here, but SDCC is supported by CodeBlocks, Eclipse, and its compiler is also used in a dedicated environment and SDK in one — CPCTelera.
The project is surprisingly still maintained. I say “amazingly” because when something has a homepage on SourceForge, I automatically think the last update was 15 years ago.
Advantages:
- C, a universal programming language known since 1972
- works on Windows, Linux, macOS
- does not force a specific target platform (machine or operating system),
- optimizes the code, tries to choose the Z80 processor registers for application as good as possible.
CPCTelera
This one is a whole SDK (a set of many tools and libraries) for Amstrad, created mainly for game development (let’s face it, I’m in the minority, believing that it’s worth creating anything else on 8-bit).
Basically, we still write code with SDCC as a compiler in mind, but many problems have already been solved for us. That would include drawing and animating so-called sprites (those small graphics depicting a hero or opponents), playing music in the format of (included) Arkos Tracker, creating ready-made floppy disk images, converting many graphics and sound formats, or at a lower level – faster text display than the system one.
Features:
- Works on Windows, Linux, macOS
- Here, however, we are talking only about Amstrad CPC (Plus, possibly).
- Some included libraries have comments that will help… the Spanish-speaking programmers.
I have used CPCTelera for example to create the title screen animation for my videos.
Z88DK
Next, I checked out and made several attempts of using the z88dk toolkit, which actually uses the sdcc compiler underneath.
As one of the few, it allows one to compile for the CP/M system with a single switch, which raises hopes for more portable code (it would mean that we can release it with much smaller modifications to other computers that support CP/M, such as Commodore 128, Spectrum +3, Altair, Apple II, BBC Micro, Epson… You name it). Tempting!
The z88dk actually contains two compilers, SCC80
and ZSDCC
, and which one is used depends on the arguments passed to the main ZCC
tool… So, things get complicated pretty quickly.
Features:
- It works everywhere too
- Also C and Z80 asm
- CP/M support and several SDKs for different platforms
My build process involved a small Makefile defining how to compile the code (which of the multiple platform switches to use), create a floppy disk image (using iDSK, a separate utility). Then you just need to unmount and mount the floppy image in the emulator, reset, run the code again… and you can test!
ccz80
This tool is a certain deviation from strict adherence to standards. Although the name ccz80
may suggest that it is some kind of “C compiler for Z80”, in fact, we are talking about a separate programming language here. It is based on the C language, but also slightly modified and simplified (even if there is no need to declare the main
function). Along with the compiler, we get a small IDE:
The syntax also simplifies specifying whether a function takes arguments and returns output through stack arguments (default), processor registers, as well as whether to copy the function content directly into the calling code, rather than calling it. These are the typical things to worry about when you have little memory, CPU cycles and registers. The tool comes with libraries providing the most needed functionalities for CPC, CP/M, MSX and Spectrum systems/platforms.
Inserting assembly language fragments is easy, you do not even have to say that it’s assembly, just insert its instruction, comma-separated, as text strings in the body of the function. 🤷🏻♂️
Features:
- Written in .NET
- Simple, but still non-standard language.
- Documentation is virtually non-existent.
- Supports various target platforms, including CP/M.
Coffee break and reflections
All the packages I’ve talked about so far stick pretty close to one language, C. Noteworthy, we’re talking about the base C language, not C++. My memories of programming for CPC decades ago consist more of writing unreadable code in BASIC (it was more readable in a paper notebook than on the screen), and reading listings from “Bajtek” of examples in Turbo Pascal, dreaming that I would also be able to use it.
On more modern platforms, I had experience with C++ in tiny quantities, modifying existing fragments rather than writing a project from scratch. Coding for the PC, I also started with Object Pascal (formerly called Delphi language). However, I quickly embraced languages and environments providing more built-in memory management mechanisms, or a rather loose approach to data types. Professionally, I’ve spent most of my time with TypeScript (JavaScript), Python, Java, GoLang, and I’ve also experimented with C#.
So, I can write simple programs, e.g., for some algorithmic task, in C++, but can I stand creating the entire piece of software in it? Will I always remember to free up memory, to declare each function either before using it or twice, then updating its declaration and its definition separately? Will I remember that it would be appropriate to separate these declarations into header files with the extension “.h”, and will I finally understand the difference between #include “file.h” and #include <file.h>
? Perhaps…?
But C is not C++, we lack not only classes1, but also certain concepts that after many years in the industry has taken for granted – for example, Boolean type and true
and false
constants. Remembering that C did not understand the word true
in the code was a bit of a shock, and a moment of reality check. Similarly, the fact that in this language you also have to list all the variables before the piece of logic in the code that is going to use it, seemed a bit off. You might have a habit of declaring a variable right where it’s first needed – not going to work here. Additionally, a function argument can be a pointer to some variable, but also a constant pointer (const char *buffer
), and you start to wonder what it actually means for the compiler in practice… or is the int
type 8, 16 or maybe 32 bits on your machine?
Perhaps, if I don’t know what I’m doing on the highest level, or whether the library I’ve chosen supports certain features or not, or even how to most efficiently do certain things on an 8-bit computer (“oh, so writing a line of text can take THAT long?”), then I shouldn’t add existential doubts and uncertainty of every detail of the language syntax.
Maybe the ability to target every computer by targeting CP/M is not the most important thing for me, especially if my solution has not yet been tested on any?
1. Object-oriented programming can still be fine because, in my opinion, it is primarily about the proper abstraction of data structures.
CPCBasic Compiler
This is where CPC Basic Compiler, from the same author as ccz80, comes in, all in yellow and blue. In a way, it belongs more to “this side” of the thoughts from the previous section.
Although we are talking about CPC Basic, it is a cross-compiler, so – like previous candidates – it’s a tool that we use on a different platform (PC) than the target one (CPC).
The software is written in C#, so it runs on .NET platform, which in itself is an interesting choice (far from its low-level compilation target). At this point, I realized that ccz80 itself is also written for .NET.
When you type RUN
, instead of running, the program will be compiled and saved to disk in the Outputs
directory (by default in .bin
format, which is executable machine code ready to run for Amstrad). Here we can easily change the output file format to assembly code, a ready-to-use floppy disk image, a snapshot of the machine, as well as a direct execution in the emulator.
The concept is fascinating. It would seem that it allows you to use the language that is already well documented in the computer manual itself. It would let you use existing code written in BASIC, but make it work much faster, and the process itself is effortless (at least the compilation stage). Instead of looking for framework-specific API calls, you can choose from extensive standard (and often better documented) commands that control graphics, sound, text, and I/O devices.
However, watch out! The tool’s webpage lists many differences in the behavior of the compiled program compared to the same code written directly in the “real” BASIC. Many of them are critical, for example, no stopping of the program in case of an uncontrolled error, or that numeric variables are unsigned integers (positive numbers only). The support for real numbers is instead provided by its own, separately-managed array of such. So, you cannot use the “regular” mathematical functions, instead you get custom RSX command. For example, instead of x = SIN y
you can use… oh dear… |CREAL,0,x:|CREAL,1,y:|SIN,1,2
… and then read this value only as an integer or string representation. Moreover, declarations of arrays, symbols, and variables are global… The list goes on and on.
So, this is not exactly CPC Basic, despite the name.
Turbo Rascal Syntax error, “;” expected but “BEGIN”
No, I did not paste an error message accidentally, this is the name of the language and development tool! It is also sometimes abbreviated as “Turbo Rascal Syntax Error” (TRSE) or simply “Turbo Rascal” 🙂
It provides a more complete IDE (development environment), i.e., a compiler and editor integrated into one, along with other useful tools.
TRSE was created as a game development tool primarily for the Commodore 64, but over time, support for the Z80 processor was added, and consequently, for platforms such as Amstrad, ZX Spectrum, GameBoy.
As the name suggests, the language in which we write will be very similar to Turbo Pascal, but also modified. As in the case of ccz80, we have a similarity to a standard and universal language, but still with its adaptation to specific applications. In both cases, this probably means that we will be tied to their “mother” editors, unable to use some more advanced modern IDE. This is not a big disadvantage, but you may be used to modern tools being able to, for example, change the name of a variable or function in every place where it is used, without touching other – coincidentally same named – things in the code.
This environment seems to have a certain advantage—the integrated editor provides help and documentation that is often more difficult to find in the other cases:
On the other hand, even in the attached screenshot you can see that not everything looks like in Pascal – for example, the definition of the record type is de facto done using the keyword var
, and with type
it won’t even work… So, we do not quite have a standard programming language, and once again we should mind some deviations, a price for writing relatively light code targeting simpler computer systems.
However, one can hope that this is one of the tools that will allow you to write common logic for many platforms effortlessly, and separate the platform-specific parts, still implementing them in the same development environment.
An additional advantage that stands out on day one is that with one button (F5
) you can compile and run our program in the emulator. No more creating a file as a separate step, placing it on the floppy disk image as another. No more manually (yet virtually) putting the floppy in the emulated disk drive, or typing the RUN "MYTHING"
command. Just right away, single button taking you from code to execution in the emulator (however, only one emulator, Caprice32, supports it, so it is required if you want to test your code in this way).
Opinions?
Which tool convinced you the most? I’ll give TRSE a chance.
Bibliography
StackOverflow: Why are C compilers average in the Z80? Why do C to Z80 compilers produce poor code? – Retrocomputing Stack Exchange