Dec 082011
 

A new version of the SDCC C compiler is available. You can use SDCC to develop for the Amstrad CPC, e.g. with using the SDCC Code::Blocks template or the article in the CPCWiki. In the new version, there are a lot of optimisations for the Z80 backend, so be sure to update if you already use the old version. You can download it from http://sdcc.sourceforge.net.

Changes:

  • include/pic, non-free/include/pic, lib/pic, non-free/lib/pic renamed to */pic14
  • implemented –fomit-frame-pointer for mcs51
  • support for use of sdcc in z88dk (–reserve-regs-iy and –no-optsdcc-in-asm options; smallc calling convention)
  • new register allocator in the z80 and gbz80 ports (optimal when using –opt-code-size and a sufficiently high value for –max-allocs-per-node for the z80 port)
    C99 designated initializers
  • added strxfrm() and strcoll() functions; strerror() is the only string handling function still missing
  • added support for pic18f2xk22/pic18f4xk22 family (requires gputils > 0.14.0 and –enable-new-pics configure flag)
  • added support for enhanced core pic14 devices (requires gputils > 0.14.0 and –enable-new-pics configure flag)
  • setjmp() / longjmp() for the z80 port
  • _Bool / bool for the hc08, pic16 and pic14 ports
  • sdcpp synchronized with GNU cpp 4.6.1
  • z180 port
  • very basic (no integer constants, multiplication, division, shifts or use as return value) support for data types long long, unsigned long long, int_fast64_t, int_least64_t, int64_t, uint_fast64_t, uint_least64_t, uint64_t in the z80, z180, r2k and gbz80 ports
  • r2k port for the Rabbit 2000 and Rabbit 3000
  • Numerous feature requests and bug fixes are included as well.
Oct 272011
 

Norecess decided to release his self-developed IDE, which he uses to create programs and demos for the Amstrad CPC (e.g. his demo Phreaks or the command shell QuickCMD). So you now can have a look at it and use it, if you want, for your own productions (read the dedicated thread in the CPC Wiki forums on how to use the IDE). But be aware that he won’t give you support for the program! You can download it from Norecess’ homepage.

CPC related plugins of the IDE:

  • CPCBigFile: embeed a set of resources into multiple small files (such as BANK.C4, BANK.C5, etc.) to be put later inside a DSK CPC image file. Additionally to that, a description file is outputted so the position/size in memory of each files can be easily retreived later.
  • CPCBinToC: convert a binary resource into a C file
  • CPCBitmap: convert a bitmap resource to CPC format, multiple conversion options are available
  • CPCDSK: inject resources into a DSK CPC image file. Internally makes use of excellent ManageDSK tool. Each resources in the DSK can be edited, cf. execution address etc.
  • CPCPacker: compress a set of resources. Exomizer or BitBuster can be choosen.
  • CPCRawBitmap: convert a bitmap resource to a custom RAW data format. It has been used by example with Phreaks demo to convert all frames into a single large array of data.
  • CPCText: outputs RAW data from an edited text in UI. User has to provide charset (font).
  • HexEdit: simple hexadecimal editor. Default viewer for unknown file-types.
  • ImageEdit: simple image viewer
  • SDCC: compiler for C/ASM files (makes use of SDCC2Pasmo utility to use Maxam’s syntax in assembly source-code)
  • PixelCalculator: useful tool which helped me a lot to compute pixel masking in MODE 0
  • SourceEdit: source editor, internally based on excellent Scite editor
Sep 042011
 

I updated my Code::blocks (a powerfull open source development environment) template, which allows you to compile the “Hello world!” example of Norecess’ SDCC tutorial. The main changes are the update of SDCC2Pasmo and ManageDSK to the latest version. I also added an example file ‘defines.h’ which shows how to create multi-line assembler defines. I also defined the BREAKPOINT keyword, which you can add in your source code. WinApe will automatically start its debugger if it has to execute this byte sequence. You can download it from Norecess’ website and at the end of this post.

Changes:

  • added SDCC2Pasmo v1.5
  • added ManageDSK v0.20
  • added defines.h to show definition of multi-line assembler definitions
  • added example BREAKPOINT (for WinApe breakpoints) and MODE definitions

Entwickeln mit Z88dk

 

Wenn man „mal eben“ ein Programm erstellen will ohne in die Tiefen von Assembler einsteigen zu wollen so greift man normalerweise auf das mitgelieferte BASIC im CPC zurück. Soll es dann doch mal etwas schneller sein, so bleibt meist nur noch der Assembler übrig und wenn man dann merkt, dass die eigenen Assembler Kenntnisse mit den Jahren dann doch etwas eingerostet sind wünscht man sich eine andere Programmiersprache, eventuell sogar eine, mit der man sowieso schon tagtäglich arbeiten muss. Warum also nicht den CPC in C programmieren, schliesslich soll C doch eine relativ hardwarenahe Sprache sein. Jetzt am besten noch eine einfache Entwicklungsplattform für den PC und schon könnte man einfach und schnell einsteigen.
Bei der Suche nach einem C-Compiler für Z80 Systeme stösst man relativ schnell auf diverse kommerzielle Compiler und zwei Compiler aus dem OpenSource Bereich: SDCC und Z88dk. An dieser Stelle soll es nur um letzteren gehen.

Was ist Z88dk?

Z88dk ist ein Cross-Compiler für die Sprache Small-C, der neben dem eigentlichen C-Compiler auch eine C Standard-Bibliothek für viele Z80 Rechner mitbringt. Z88dk erweitert Small-C um viele Funktionen von ANSI-C, wobei die Funktionen weggelassen wurden, die sich auf einem Z80 System nur ineffizient realisieren lassen. Der Name Z88dk deutet auf den Ursprung des Compilers hin. Er wurde für den Cambridge Z88 entwickelt und unterstützt mittlerweile mehr als 20 unterschiedliche Z80 Systeme.

Installation

Für die Installation unter Windows sollte am besten der Installer verwendet werden. Er kopiert die notwendigen Dateien an die richtige Stelle und setzt ein paar Umgebungsvariablen unter Windows. Leider wird nicht automatisch das Programmverzeichnis von Z88dk in die Umgebungsvariablen eingetragen, sodass man hier selbst Hand anlegen muss. Man muss also unter „Start-Einstellungen-Systemsteuerung-System“ im Reiter „Erweitert“ den Button „Umgebungsvariablen“ anklicken und bei den Benutzervariablen die Variable PATH um den Pfad zu dem Programmverzeichnis von Z88dk erweitern. Bei einer Standard-Installation ist das „C:\Programme\z88dk\bin“. An dieser Stelle sollte man daran denken, dass die neu gesetzten Umgebungsvariablen erst dann wirksam werden, wenn man eine neue Eingabeaufforderung öffnet.
Alle Anwender, die Z88dk unter Linux verwenden wollen, sollten sich das tar.gz Paket laden und den Compiler entsprechend selbst kompilieren. Solange man nicht die aktuelle CVS Version verwendet, sollte es dabei keinerlei Probleme geben.
Alle weiteren Programmierbeispiele werden sich allerdings auf Windows und Flynn’s WinCPC beziehen, da so die Binärdateien einfacher zu handhaben sind. Eine kleine Anmerkung am Rand: Unter Linux kann man WinCPC mit WINE verwenden.

Hallo Welt!

Eine Einführung in die C-Programmierung kann natürlich an dieser Stelle nicht gegeben werden, dennoch soll anhand einiger Beispiele die Arbeit mit Z88dk beschrieben werden und wie fängt man üblicherweise bei einer neuen Programmiersprache an? Genau, mit „Hello World!“. Listing 1 zeigt den Quellcode des Programms.

 

Selec All Code:
1
2
3
4
5
6
7
#include <stdio.h>
 
void main(){
 
 printf(„Hello World!);
 
}
Listing 1: Hello World! – hello.c

 

Selec All Code:
1
zcc +cpc -lcpcfs -O3 -create-app hello.c
Listing 2: Kompilieren von hello.c

 

Das Programm erklärt sich eigentlich von selbst. Es wird die Standard-Ein-/Ausgabebibliothek stdio geladen und danach mit dem Befehl printf der Text ausgegeben.
Als nächstes möchte man sein Programm natürlich kompilieren und auf dem CPC ausprobieren. Dafür verwendet man den in Listing 2 angegebenen Befehl, wobei die folgenden Werte übergeben werden müssen:

  • +cpc – Als Zielplattform soll der CPC verwendet werden
  • -lcpcfs – Standardbibliothek für Ein- und Ausgabe mit einbinden
  • -O3 – Maximale Optimierung einschalten
  • -create-app – Es wird zusätzlich zur eigentlichen Binärdatei eine Datei mit AMSDOS Header generiert

Nachdem das Kompilieren erfolgreich durchgeführt worden ist, finden sich in dem Verzeichnis die Dateien a.bin und a.cpc. Die Datei mit der Dateiendung „.cpc“ ist die Datei, die den AMSDOS Header beinhaltet. Sie könnte z.B. mit einem Programm wie CPCFS direkt in ein DSK-Image geschrieben werden oder auf den CPC übertragen werden. Wir werden an dieser Stelle aber den Emulator WinCPC von Flynn verwenden, da er die Möglichkeit bietet, Binärdateien direkt in den Speicher zu laden.
Z88dk erzeugt immer Programme, die an der Adresse &6000 beginnen. Um das Programm also in WinCPC auszuführen muss man mit „File – load binary file“ die Datei a.bin auswählen. Nach dem Bestätigen wird dann noch die Ladeadresse eingegeben, also &6000. Die Datei liegt jetzt im Speicher von WinCPC und kann mit CALL &6000 gestartet werden. Wenn nichts falsch gemacht worden ist, sollte der CPC „Hello World!“ ausgeben.

5kb? Was für ein Biest!

Wie man leicht erkennen kann, hat Z88dk eine 5kb grosse Binärdatei erzeugt. Warum ist das so? Auch wenn printf relativ mächtig ist und dementsprechend viel Platz verwendet, ist der Compiler in der Lage zu erkennen, ob die volle Funktionalität von printf überhaupt verwendet wird. Wird nicht die volle Funktionalität verwendet, ersetzt der Compiler automatisch printf durch eine einfachere Ausgabefunktion. Daran kann es also nicht liegen, da wir eine sehr einfache Ausgabe verwendet haben.
Schaut man sich einmal die CPCFS Bibliothek von Z88dk an, so fällt sofort auf, dass hier ein ganzer Block mit Null-Bytes belegt ist. Ein Blick in den Quellcode verrät, dass die Bibliothek sowohl für die Dateieingabe als auch für die Dateiausgabe 2kb reserviert (AMSDOS). Abhilfe schafft an dieser Stelle die Bibliothek ndos, die nur eine Dummy Datei Ein-/Ausgabe unterstützt. Also ersetzt man in dem Befehl aus Listing 2 -lcpcfs durch -lndos und kompiliert nochmal die Datei hello.c und siehe da: die Datei ist nun nur noch 331 Byte gross.

Darf’s noch etwas schneller sein?

Wie bei den meisten C-Compilern ist es auch bei Z88dk möglich Assembler im Quelltext einzugeben. Dadurch ist es möglich, wichtige Funktionen, die häufig angesprungen werden oder zeitkritisch sind, zu optimieren, während man die unkritischen Routinen weiterhin in C schreiben kann.
An dieser Stelle muss man wissen, wie in C Variablen an Funktionen übergeben werden und wie die einzelnen Datentypen definiert sind. Tabelle 1 enthält die verschiedenen Datentypen und deren Grösse in Bytes. Natürlich werden auch Pointer unterstützt, welche jeweils 2 Bytes Speicher belegen.

 

TypGrößeMinMax
char1 Byte-128127
unsigned char1 Byte0255
int2 Byte-3276732767
unsigned int2 Byte065535
long4 Byte-21474836472147483647
unsigned long4 Byte04294967296
Tabelle 1: Datentypen in Z88dk

 

Die Assembleranweisungen werden bei Z88dk in einem Block abgelegt, der durch die Anweisung #asm und #endasm geklammert ist.
Listing 3 ist ein relativ unnützes, kleines Programm, das die Übergabe von Parametern an C Funktionen demonstrieren soll. Es werden nacheinander die Buchstaben ‘A’-'E’ auf dem Bildschirm ausgegeben und die Zahl 42 an den aufrufenden Programmteil zurückgegeben.
Wie man an dem Programm in Listing 3 sieht, werden die Parameter über den Stack an die Funktion weitergegeben. Tabelle 2 zeigt den Aufbau des Stacks.

 

Selec All Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
void printchar( char code, int codeint, long codeint2 ){
 
#asm
   ld hl,6 //code (SP+6)
   add hl,sp //'A'
   ld a,(hl)
   call 0xbb5a
 
   dec hl //highbyte codeint (SP+5)
   ld a,(hl) //'B'
   call 0xbb5a
 
   dec hl //lowbyte codeint (SP+4)
   ld a,(hl) //'C'
   call 0xbb5a
 
   dec hl //SP+3
   ld a,(hl) //'D'
   call 0xbb5a
 
   dec hl //SP+2
   ld a,(hl) //'E'
   call 0xbb5a
 
   ld hl,42 //Rueckgabewert
 
   ret
 
#endasm
 
}
 
void main(){
 
   int a = printchar('A', 'B'*256+'C', 'D'*256+'E');
 
}
Listing 3: Beispiel für Variablenübergaben an Funktionen

 

AdresseParameter
SPStackpointer für den Rücksprung
SP+22 Byte int Wert (codeint2)
SP+42 Byte int Wert (codeint)
SP+61 Byte char Wert (code)
Tabelle 2: Aufbau des Stacks in Listing 3

 

TypRegister
longdehl
inthl
charh=0,l
Tabelle 3: Rückgabewerte nach Datentyp

Um die Werte zu laden wird HL als Pointer auf den Speicherbereich mit den übergebenen Parametern verwendet. Als Startwert bekommt HL den höchsten Parameter, in diesem Fall ‘code’ zugewiesen, was laut Tabelle 2 einen Wert von SP+6 entspricht. HL wird deshalb mit dem Wert 6 geladen und der Stackpointer dazuaddiert. Der Wert wird jetzt ausgelesen und in A gespeichert, damit die ROM Routine BB5A diesen Wert auf dem Bildschirm ausgeben kann. Als nächstes wird der Wert von HL dekrementiert, sodass HL jetzt auf das Highbyte von ‘codeint’ zeigt. Auch hier wird der Wert wieder nach A geladen und ausgegeben. Die weiteren Befehle lesen die Bytes der entsprechenden Übergabeparameter analog aus.
Kompiliert man das Programm, so erhält man eine gerade einmal 107 Byte grosse Binärdatei.

Wie geht’s weiter?

Wer sich gerne weiter mit Z88dk befassen möchte, sollte auf jeden Fall die Dokumentation lesen und sich eingehender mit den mitgelieferten Beispielprogrammen beschäftigen. Aus Platz- und Zeitgründen konnte hier auch nicht auf die Erstellung von Softwarebibliotheken eingegangen werden.
Möchte man mehr Informationen zu den mitgelieferten Systembibliotheken, sollte man in das Include Verzeichnis von Z88dk schauen. An dieser Stelle liegen die Header-Dateien der Bibliotheken.

Ausblick

Mit Z88dk ist es möglich komplexe Programme relativ einfach zu erstellen und das ohne allzugrossen Geschwindigkeitsverlust wie bei BASIC. Die Integration von Z80 Assembler in den Quellcode bietet ausserdem die Möglichkeit spezifische Optimierungen vorzunehmen.
Schaut man sich ein wenig bei den Ports für die anderen Z80 Systeme um, so stellt man fest, dass es dort einige Bibliotheken gibt, die sicherlich für die Programmierung des CPCs auch recht interessant wären, z.B. die sehr umfangreiche Sprite-Bibliothek für den ZX Spectrum.
Spezielle Bibliotheken für Hardware könnten zudem die Programmierung von Anwendungsprogrammen erleichtern, bei denen es nicht nur auf die reine Geschwindigkeit ankommt. Auch eine Portierung des Z88 TCP/IP Stacks könnte durchaus interessant sein und mittels Far-Pointern kann man die 64kb Barriere knacken.
Durch die einfache Portierbarkeit von Z88dk wäre es ausserdem ohne hohen Aufwand möglich den Compiler an andere Betriebssysteme wie FutureOS oder SymbOS anzupassen.
Schlussendlich wird Z88dk natürlich keinen Assembler ersetzen können, aber im Vergleich Code, Programmieraufwand und Wartbarkeit schneidet die C Lösung sicherlich nicht schlecht ab.

Written April, 2006 by Octoate