; DOITRTL.MAC (= DO IT Compiler Run-Time Library) ; ----------- ; ; "Compiler Selbstgeschneidert", ; Helmut Richter, ; C'T Magazine, May 1986, p.67 ; ; (Retyped by Emmanuel ROCHE.) ; ;-------------------------------- .Z80 ; M80 v3.44 ASEG ; Absolute code ORG 0100H ; Standard CP/M COMmand file ;-------------------------------- ; ASCII characters used. ; lf EQU 0AH ; Line Feed cr EQU 0DH ; Carriage Return ;plus ;minus ;zero ;dollar ;space ;colon (:) ;-------------------------------- ; Variables used. ; Wstart EQU 0000H ; Warm Start via 0000H address BDOS EQU 0005H ; Standard entry point ; ;-------------------------------- ; Run-Time Library des DOIT Compiler. ; Version 1.0 Datum: 28 Feb 1986 ;-------------------------------- ; Start of code. ; NOP ; First instruction is No OPeration... JP Start ; Zum ende des RTL springen JP ReadInt ;------------------------ JP ReadChar ; Vektorenleiste fuer die JP WriteInt ; System-funktionen. JP WriteChar ; JP AddOp ;------------------------ JP SubOp ; Vektorenleiste fuer die JP NegOp ; arithmetischen Operationen. JP MultOp ; JP DivOp ; JP ModOp ; JP OddOp ;------------------------ JP EqlOp ; Vektorenleiste fuer die JP NeqOp ; logischen Operationen. JP GtrOp ; JP LssOp ; JP GeqOp ; JP LeqOp ; ; ;-------------------------------- ; Systemfunktionen ;-------------------------------- ReadInt: ; ; Liest eine Integerzahl mit fuenf genau ; Ziffern und Vorzeichen von der Tastatur. ; ; In: Returnadresse ; Out: Eingelesene Zahl ; Benutzte register: HL, DE, C ; LD DE, InpMsg ; Return-adresse bleibt auf stack CALL Print ; Zuerst '? ' schreiben LD B, 05H ; Hoechstens 5 Ziffern LD HL, StrBuf + 1 ; Pufferzeiger ist HL CALL SigNum ; Vorzeichen behandeln CALL Ziffer ; Ziffern einlesen CALL Zwert ; Integerwert errechnen POP HL ; Ergebnis nach HL EX (SP), HL ; Return-adresse nach HL, JP (HL) ; ergebnis auf Stack, return. ; SigNum: LD A, '+' ; Plus speichern LD (StrBuf), A ; CALL ReadCh ; Erstes Zeichen lesen CP '+' ; Positives Vorzeichen? RET Z ; Dann keine Behandlung CP '-' ; Kein "-" JR NZ, Zcheck ; Liegt Ziffer vor? LD (StrBuf), A ; "-" schreiben und RET ; OHNE B zu veraendern! ; Ziffer: CALL ReadCh ; Naechstes Zeichen lesen CALL Zcheck ; Ist er Ziffer? LD A, B ; Schon 5 Ziffern gelesen? OR A ; JR NZ, Ziffer ; Falls 67nein, weiter lesen, RET ; sonst fertig. ; Zcheck: CP '0' ; Testen, ob Ziffer vorliegt JR C, Fehler ; und ggf. Ziffer zwischen- CP ':' ; speichern. JR NC, Fehler ; Fehlermeldung sonst LD (HL), A ; Ziffer puffern INC HL ; Naechster Pufferplatz DEC B ; und Ziffer. RET ; ; Zwert: LD BC, StrBuf + 1 ; LD H, 00H ; H loeschen LD A, (BC) ; Erste Ziffer in Zahl SUB 30H ; verwandeln und nach HL LD L, A ; bringen, Zwert1: INC BC ; anschliessen die restlichen. LD A, (BC) ; Ziffern auf HL * 10 addieren CP '$' ; Schluss? JR Z, Zwert2 ; Wenn ja, Schleife verlassen SUB 30H ; Ziffer in Akku zu Zahl SCF ; Carry Flag loeschen CCF ; Wird C=1 --> Overflow ADC HL, HL ; HL' := HL * 2 JP PE, Ovrflo ; HL < 0 --> Overflow PUSH HL ; ADC HL, HL ; HL' := HL * 4 JP PE, Ovrflo ; Overflow testen ADC HL, HL ; HL' := HL * 8 JP PE, Ovrflo ; Overflow testen POP DE ; DE := HL * 2 ADC HL, DE ; HL' := HL * 10 JP PE, Ovrflo ; Overflow testen LD E, A ; Neue Zahl nach E LD D, 00H ; D loeschen, ADD HL, DE ; HL' := 10 * HL + Zahl JR Zwert1 ; Naechste Ziffer Zwert2: LD A, (StrBuf) ; Jetzt: Vorzeichen behandeln CP '-' ; JR NZ, Zwert3 ; Soll Ziffer Positiv sein? PUSH HL ; Falls nein, CALL NegOp ; zweierKomplement bilden. POP HL ; Zwert3: EX (SP), HL ; Return-Adresse nach HL, JP (HL) ; ergebnis auf Stack, return. ; Fehler: LD DE, Fmsg ; Fehlermeldung ausgeben CALL Print ; CALL Wait ; Auf BLANK warten JP Wstart ; Programmabbruch ; ;-------------------------------- ReadChar: ; ; Liest ein Zeichen von der Tastatur. ; ; In: Returnadresse ; Out: Eingelesener Character ; Benutzte Register: HL, DE ; POP HL ; Return-adresse vom Stack holen CALL ReadCh ; LD E, A ; Character und LD D, '$' ; fuellzeichen nach DE. PUSH DE ; Character auf Stack schreiben JP (HL) ; Return ; ;-------------------------------- WriteInt: ; ; Schreibt eine Integerzahl auf Bildschirm. ; ; In: Returnadresse, Integer ; Out: -- ; Benutzte Register: HL, DE, BC, AF ; POP BC ; Return-Adresse vom Stack POP HL ; Zu schreibende Integer PUSH BC ; Return-Adresse sichern CALL Vorzei ; Vorzeichen verabeiten LD BC, StrBuf + 1 ; Zeiger auf Stringpuffer LD DE, 10000 ; 5 dezimalstelle bearbeiten CALL IntConv ; und ggf. Ziffer puffern. LD DE, 1000 ; 4 dezimalstelle bearbeiten CALL IntConv ; und ggf. Ziffer puffern. LD DE, 100 ; 3 dezimalstelle bearbeiten CALL IntConv ; und ggf. Ziffer puffern. LD DE, 10 ; 2 dezimalstelle bearbeiten CALL IntConv ; und ggf. Ziffer puffern. LD DE, 1 ; 1 dezimalstelle bearbeiten CALL IntConv ; und ggf. Ziffer puffern. LD DE, StrBuf ; Ziffern auf den Bildschirm CALL Print ; Schreiben RET ; Ende Write_Integer ; Vorzei: LD A, '+' ; Plus speichern LD (StrBuf), A ; BIT 7, H ; Integer in HL < 0 ? RET Z ; Z=1 --> Bit 7=0 LD A, '-' ; Integer in HL < 0 LD (StrBuf), A ; Minus eintragen PUSH HL ; Und Integer CALL NegOp ; ZweierKomplementieren POP HL ; RET ; Fertig mit Vorzeichen ; IntConv: XOR A ; Int0: SCF ; Carry Flag zuruecksetzen CCF ; SBC HL, DE ; Subtraktionsversuch BIT 7, H ; Integer in HL < 0 ? JR NZ, Int1 ; Einmal zuviel subtrahiert INC A ; Subtraktion Ok, nochmal JR Int0 ; HL -- Dezimalzahl in DE Int1: ADD HL, DE ; letzes SBC rueckgaengig ADD A, 30H ; machen. Zahl in A --> Ziffer LD (BC), A ; und Ziffer puffern INC BC ; naechster Pufferplatz RET ; und fertig. ; ;-------------------------------- WriteChar: ; ; Schreib ein Zeichen auf den Bildschirm. ; ; In: Returnadresse, Zeichen ; Out: -- ; Benutzte Register: HL, DE ; POP HL ; Return-Adresse vom Stack holen POP DE ; Zeichen vom Stack holen CALL WriteCh ; JP (HL) ; Return ; ;-------------------------------- ; Arithmetischen Operationen ;-------------------------------- SubOp: ; ; Subtrahiert zwei Integerzahlen, indem es den zweiten ; Operanden (zweier --> complementiert, dann ADD anspringt. ; ; In: Returnadresse, Operand_2, Operand_1 ; Out: Resultat = Operand_1 - Operand_2 ; Benutzte Register: HL, DE, AF ; POP BC ; Return-Adresse vom Stack holen CALL NegOp ; 2. Operanden liegt auf TOP_Stack JR Sub2 ; Wert komplementieren und ; dann einfach addieren. ; ;-------------------------------- AddOp: ; ; Addiert zwei Integerzahlen. ; ; In: Returnadresse, Openrand_2, Operand_1 ; Out: Resultat = Operand_1 + Operand_2 ; Benutzte Register: HL, DE, AF ; POP BC ; Return-Adresse vom stack holen Sub2: POP DE ; 2. Operanden vom Stack holen POP HL ; 1. Operanden vom Stack holen XOR A ; Loeschen Carry Flag ADC HL, DE ; Operanden addieren und JP PE, Ovrflo ; overflow testen. PUSH HL ; Ergebnis und PUSH BC ; ruecksprungadresse auf Stack RET ; schreiben und return. ; ;-------------------------------- NegOp: ; ; Negiert eine Integerzahl durch Bildung ihres Zweier-Komplementes. ; ; In: Returnadresse, Operand ; Out: Resultat = NEG (Operand) ; Benutzte Register: HL, DE, AF ; POP HL ; Return-Adresse vom Stack holen POP DE ; Operant vom Stack holen LD A, E ; und byteweise Komplementieren. CPL ; (EinerKomplement) LD E, A ; LD A, D ; CPL ; LD D, A ; Durch Addition von 1 erh{lt INC DE ; man des ZweierKomplement. PUSH DE ; Negierten Operanden auf Stack JP (HL) ; Return ; ;-------------------------------- MultOp: ; ; Multipliert zwei Integerzahlen. ; ; Bessere Methodiken siehe C'T-Sonderheft, S.65 ff ; und "Programmierung des Z-80" von R. Zaks. ; ; In: Returnadresse, Operand_2, Operand_1 ; Out: Resultat = Operand_1 * Operand_2 ; Benutzte Register: HL, DE, AF ; POP HL ; Return Adresse vom Stack holen POP DE ; Multiplikator holen LD C, D ; Multiplikator nach A und C LD A, E ; LD B, 16 ; 16 Operationsschritte POP DE ; Multiplikand holen PUSH HL ; Return Adresse sichern LD HL, 0000H ; Mult: SRL C ; Multiplikator oben und unten nach RRA ; rechts rotieren. JR NC, NoAdd ; C=1 --> einmal addieren CCF ; ADC HL, DE ; Ergebnis = Ergebnis + Multiplikand JP PE, Ovrflo ; Overflow? NoAdd: EX DE, HL ; DE nach links schieben ( x2) ADC HL, HL ; 16-Bit Shift links EX DE, HL ; DJNZ Mult ; Naechstes Bit EX (SP), HL ; Ergebnis auf Stack, HL <-- Ret Adr. JP (HL) ; und fertig. ; ;-------------------------------- DivOp: ; ; Nicht implementiert. ; POP HL ; Return Adresse vom Stack holen POP BC ; Dividend vom Stack holen POP DE ; Divisor vom Stack holen LD DE, 0000H ; Ergebnis = 0 auf Stack bringen, PUSH DE ; JP (HL) ; und return. ; ;-------------------------------- ModOp: ; ; Nicht implementiert. ; JR DivOp ; Null liefern ; ;-------------------------------- Ovrflo: ; ; Behandelt Integerover/underflow durch Programmbbruch. ; ; In: Returnadresse ; Out: -- ; Benutzte Register: HL, DE, AF ; LD DE, OvrMsg ; Auf Overflow Message zeigen JR NC, Ovr1 ; Negativ-negativ Ueberlauf? LD DE, UndeMsg ; Ja: Auf Underflow Message zeigen Ovr1: CALL Print ; Fehlermeldung ausgeben LD DE, StopMsg ; Abbruchmeldung geben CALL Print ; CALL Wait ; JP Wstart ; und Programmende. ; ;-------------------------------- Print: ; ; Schreibt einen Text Bildschirm (CP/M Funktion 9). ; ; In: Returnadresse, Zeiger auf Text in DE ; Out: -- ; Benutzte Register: DE, C ; PUSH AF ; PUSH BC ; PUSH HL ; LD C, 09H ; String auf CRT senden; CALL BDOS ; String muss mit "$" enden. POP HL ; POP BC ; POP AF ; RET ; ; ;-------------------------------- ; Logischen Operationen ; Achtung: Sammlung muss vervollstaendigt werden; ; nicht alle Unterprogramme sind programmiert. ;-------------------------------- OddOp: ; ; Prueft auf Ungeradzahligkeit. ; ; In: Returnadresse, Top_Stack ; Out: Top_Stack = 0000H (FALSE), falls Operand EVEN, ; = FFFFH (TRUE), falls Operand ODD. ; Benutzte Register: HL, AF ; POP HL ; Return Adresse vom Stack holen POP DE ; Operand vom Stack holen PUSH HL ; Return Adresse wieder auf Stack XOR A ; Akku loeschen BIT 0, E ; Zahl 0DD --> Z-Flag gesetzt JR Z, False ; Bit 0 = 0. Zahl ist gerade JR True ; Bit 0 = 1. Zahl ist ungerade ; ;-------------------------------- EqlOp: ; ; Prueft auf Gleichheit. ; ; In: Returnadresse, Operand_2, Operand_1 ; Out: Top_Stack = TRUE, falls Operand_1 = Operand_2 ; = FALSE sonst ; Benutzte Register: HL, DE, AF ; POP BC ; Return Adresse vom Stack holen POP DE ; 2. Operanden vom Stack holen POP HL ; 1. Operanden vom Stack holen PUSH BC ; Return Adresse wider auf Stack XOR A ; Akku loeschen SBC HL, DE ; HL = DE ? JR Z, True ; Ja JR False ; Nein ; ;-------------------------------- NeqOp: ; ; Prueft auf Ungleichheit. ; ; In: Returnadresse, Operand_2, Operand_1 ; Out: Top_Stack = TRUE, falls Operand_1 <> Operand_2 ; = FALSE sonst ; Benutzte Register: HL, DE, AF ; POP BC ; Return Adresse vom Stack holen POP DE ; 2. Operanden vom Stack holen POP HL ; 1. Operanden vom Stack holen PUSH BC ; Return Adresse wieder auf Stack XOR A ; Akku loeschen SBC HL, DE ; HL <> DE ? JR Z, False ; Ja, d.h. HL nicht <> DE JR True ; Nein, d.h. HL <> DE ; GtrOp: JR False ; LssOp: JR False ; Nach SBC testen auf Carry Flag GeqOp: JR False ; Nach SBC testen auf No Carry Flag LeqOp: JR False ; ; ;-------------------------------- True: ; ; Ausgaenge der logischen Funktionen. ; (Funktionsergebnis 00H = FALSE, alles andere = TRUE.) ; ; In: A = 00H ; Out: Funktionsergebnis ; Benutzte Register: HL, DE, A ; DEC A ; A <-- 0FFH False: LD D, A ; DE mit Funktionsergebnis LD E, A ; laden, POP HL ; Return Adresse vom Stack holen, PUSH DE ; Ergebnis auf Stack schreiben und JP (HL) ; return der logischen Operationen. ; ;-------------------------------- ; Read a character. ; ReadCh: PUSH BC ; Ein Zeichen von der Tastatur PUSH DE ; lesen PUSH HL ; LD C, 01H ; CALL BDOS ; Zeichen ist in A POP HL ; F wird zerstoert POP DE ; POP BC ; RET ; ; ;-------------------------------- ; Write a character. ; WriteCh:PUSH AF ; Ein Zeichen auf CRT PUSH BC ; schreiben. PUSH HL ; LD C, 02H ; CALL BDOS ; POP HL ; POP BC ; POP AF ; RET ; ; ;-------------------------------- ; Wait until key pressed. ; Wait: LD DE, EnteMsg ; CALL Print ; Wait1: CALL ReadCh ; Warten auf SPACE CP 20H ; JR NZ, Wait1 ; RET ; ; ;-------------------------------- ; Messages. ; OvrMsg: DB cr, lf, 'Integer Overflow', cr, lf, '$' UndeMsg:DB cr, lf, 'Integer Underflow', cr, lf, '$' InpMsg: DB cr, lf, '? $' Fmsg: DB cr, lf, 'Ziffer erwartet$', 00H, 00H StopMsg:DB cr, lf, 'Programm abgebrochen$' EnteMsg:DB ' - LEERTASTE druecken', cr, lf, '$' StrBuf: DB '+', 00H, 00H, 00H, 00H, 00H, '$' ; 6 Byte Puffer fuer Integer und Vorzeichen ; ; Zwei Byte fuer Stack-pointer-Zwischenspeicherung reservieren. ; ZWSTORE = MCode_Offset-2 ; ZwStore:DW 0000H ; ;-------------------------------- ; Der folgende JUMP liegt bereits auf der Startadresse des ; Maschinenprogramms und wird beim Kopieren des Laufzeit- ; systems ueberschrieben. ; Es muss gelten: MCode_Offset := START!! ;-------------------------------- ; Warmstart, falls das RTL versehentlich ; als maschinenprogramm aufgerufen wird. ; Start: JP 0000H ; Warm Start ; ;-------------------------------- ; END 0100H ; Standard CP/M COMmand file