Im Magazin „MC" wurde im Juni 1984 der folgende Artikel abgedruckt.
Hier weitere Tipps, wie der Z80 bei Unterprogrammen eingesetzt werden kann.
Mathias Neuhaus

Z80-Kniffe

Im folgenden stellen wir Ihnen einige Z80-Routinen vor, die häufig vorkommende Probleme auf elegante Weise lösen.

Bei der Ausgabe von Texten hat sich dieses Unterprogramm bewährt:
OUTTEXT:
	EX	(SP),HL
	PUSH	AF

OUTT1:
	LD	A,(HL)
	INC	HL
	CP	ETX	; ein frei definiertes
			; Ende-Zeichen
	JR	Z,OUTT2	;
	CALL	OUTCHAR	; Ausgabe d. ASCII-Zeichens im Akku
	JR	OUTT1	;

OUTT2:
	POP	AF
	EX	(SP),HL
	RET

Der Aufruf der Routine muß dann so aussehen:
	:
	CALL	OUTTEXT
	DEFM	'Textzeichen',ETX
	:
Das ETX-(End of Text)-Zeichen ist dann natürlich dasselbe wie bei OUTTEXT, also zum Beispiel ASCII-ETX (03hex) oder CR (0D hex), und darf innerhalb des Textes nicht vorkommen.
Die Routine OUTTEXT läuft dann folgendermaßen:
Durch EX (SP),HL wird die Rückkehradresse (sie ist die Anfangsadresse des Textes!) vom Stack nach HL geladen und gleichzeitig der Inhalt von HL gerettet. Dann wird der Text zeichenweise ausgegeben und dabei HL jeweils erhöht. Nachdem das Textende an ETX erkannt worden ist, zeigt HL auf das erste Byte hinter dem Text. Dies muß nun der nächste Befehl im Programm sein. Durch EX (SP),HL wird diese Adresse als Return-Adresse auf dem Stack abgelegt. RET führt also zum Rücksprung zu dem auf ETX folgenden Befehl.
Wer Texte lieber durch eine Längenangabe begrenzt, dem hilft folgende Routine:
OUTCOUNTED:
	EX	(SP),HL
	PUSH	AF
	PUSH	BC
	LD	B,(HL)	; Zähler nach
			; Register B
	INC	HL	;
OUTC1:
	LD	A,(HL)
	INC	HL
	CALL	OUTCHAR	; Ausgabe des ASCII-
			; Zeichens im Akku
	DJNZ	OUTC1	;
	POP	BC
	POP	AF
	EX	(SP),HL
	RET
Der Aufruf muß so aussehen:
	:
	CALL	OUTCOUNT
	DEFB	TEND-TANF
			; Differenz zwischen
			; Ende und Anfang,
			; also Textlänge
TANF:
	DEFM	'Textzeichen'
			;
TEND:
	:
('Textzeichen' ist natürlich in beiden Fällen durch den auszugebenden Text zu ersetzen!)
Steht das Hauptprogramm (der Text) noch dazu im RAM, so lassen sich z.B. Fehler- oder Zeilennummern sehr leicht ausgeben:
	:
	CALL	OUTTEXT
	DEFM	'Fehler: xx',ETX
ERRCODE	EQU	$-3
	:
ERRCODE EQU $-3 gibt dem Assembler an, daß die Speicherzelle drei Byte vor der, auf die der aktuelle Programmzähler zeigt, ERRCODE heißen soll. Es kann also das erste 'x' als ERRCODE adressiert werden. Indem man nun vor dem Aufruf von OUTTEXT die beiden 'x' durch die Fehlernummer überschreibt, wird die Ausgabe von Fehlermeldungen ein Klacks.
Wer eine Routine benötigt, die feststellt, in welchen Speicherbereich sie geladen wurde, der kommt mit folgendem Programmstück schnell zum Ziel:
RELOC:
	CALL	RETURN
REL1:
	DEC	SP
	DEC	SP
	EX	(SP),HL
;
;	HL enthält nun die Adresse von REL1
;
	:
die eigentliche Routine
	POP	HL
RETURN:
	RET
Durch den Befehl CALL RETURN wird der Prozessor veranlaßt, die Adresse des folgenden Befehles, hier DEC SP mit der symbolischen Adresse REL1, auf dem Stack abzulegen (für ein späteres RETURN). Nach der Rückkehr steht diese Adresse immer noch auf dem Stack, nur zeigt der Stackpointer SP nun auf das nächsthöhere Element auf dem Stack. Durch die beiden „DEC SP"-Befehle wird der Stackpointer nun auf dieses Element gerichtet. Der Befehl EX (SP),HL sichert nun HL auf dem Stack, gleichzeitig steht in HL die Adresse von REL1. Wer das ganze öfter benötigt, kommt mit:
RELOC:
	CALL	RETURN
REL1:
;	HL = REL1
	:
die benötigte Routine
	:
	POP	HL
	RET
;
RETURN:
	EX	(SP),HL
	PUSH	HL
	RET
noch günstiger weg. Hier wird die Adresse von REL1 schon in der Routine RETURN ermittelt. Um den Rücksprung an die richtige Stelle zu ermöglichen, muß die Adresse aus HL auf den Stack gebracht werden (sie wurde durch EX (SP),HL entfernt!). Die weitere Adressenrechnung kann nun wie in mc 1/81, Seite 24, beschrieben erfolgen, nur muß bei Angabe der Distanz darauf geachtet werden, daß HL nicht die Adresse von RELOC, sondern die Adresse von REL1, also RELOC + 3, enthält.

Eingescanned von Werner Cirsovius
November 2002
© Franzis' Verlag