; DOITRTL.MAC (= DO IT Compiler Run-Time Library) ; ----------- ; ; "Compiler Home made", ; 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 of the DOIT Compiler. ; Version 1.0 Datum: 28 Feb 1986 ;-------------------------------- ; Start of code. ; NOP ; First instruction is No OPeration... JP Start ; Jump to the end of the RTL JP ReadInt ;------------------------ JP ReadChar ; jump vector list for the JP WriteInt ; System functions JP WriteChar ; JP AddOp ;------------------------ JP SubOp ; Jump vector list for the JP NegOp ; arithmetic Operations. JP MultOp ; JP DivOp ; JP ModOp ; JP OddOp ;------------------------ JP EqlOp ; Jump vector list for the JP NeqOp ; logical Operations. JP GtrOp ; JP LssOp ; JP GeqOp ; JP LeqOp ; ; ;-------------------------------- ; System functions ;-------------------------------- ReadInt: ; ; Reads an Integer with exactly five ; numbers and a sign from the keyboard ; ; In: Returnadress ; Out: Number read from keybouard ; Used Registers: HL, DE, C ; LD DE, InpMsg ; Return-adress stays on the stack CALL Print ; Fisrt print the '? ' LD B, 05H ; Max 5 numbers to read LD HL, StrBuf + 1 ; Bufferpointer is HL CALL SigNum ; process sign character CALL Number ; read the numbers CALL Nvalue ; calculate the integer from the input POP HL ; Transfer result to HL EX (SP), HL ; Return-adress to HL, JP (HL) ; Result is on the stack,return ; SigNum: LD A, '+' ; save the '+' sign LD (StrBuf), A ; in the first buffer position CALL ReadCh ; Read first character CP '+' ; Is it a '+' sign? RET Z ; If so, no action needed CP '-' ; Is it a '-' character JR NZ, Ncheck ; If no, it must be a number, go check LD (StrBuf), A ; "-" into buffer RET ; WITHOUT changing B Register ; Number: CALL ReadCh ; Read next number CALL Ncheck ; Is it a number ? LD A, B ; How many number have been read OR A ; so far ? JR NZ, Number ; Not yet 5 read, get next RET ; 5 read, ready ; Ncheck: CP '0' ; test if A contains a valid number JR C, Error ; and temporarily save it CP ':' ; if it is JR NC, Error ; Print error if not LD (HL), A ; Put in in buffer INC HL ; Point to next buffer location DEC B ; decrement number to go RET ; ; Nvalue: LD BC, StrBuf + 1 ; LD H, 00H ; Clear H LD A, (BC) ; Get first number from buffer SUB 30H ; Convert ASCII to decimal LD L, A ; put it in L Nvalu1: INC BC ; point to next number LD A, (BC) ; Get next number CP '$' ; Is it the stop character '$' ? JR Z, Nvalu2 ; Yes : leave loop SUB 30H ; Convert ASCII to decimal SCF ; clear Carry Flag CCF ; If Carry=1 --> Overflow ADC HL, HL ; HL' := HL * 2 JP PE, Ovrflo ; HL < 0 --> Overflow PUSH HL ; save this, 8*HL +2*HL(this) ADC HL, HL ; HL' := HL * 4 JP PE, Ovrflo ; Test for Overflow ADC HL, HL ; HL' := HL * 8 JP PE, Ovrflo ; test for Overflow POP DE ; DE := HL * 2 ADC HL, DE ; HL' := HL * 10 JP PE, Ovrflo ; Test for Overflow LD E, A ; Save temporarily in E LD D, 00H ; clear D ADD HL, DE ; HL' := 10 * HL + number JR Nvalu1 ; Next number Nvalu2: LD A, (StrBuf) ; Now : process sign character CP '-' ; JR NZ, Nvalu3 ; If not '-', it must be '+' PUSH HL ; Negative number ! CALL NegOp ; calculate the two's complement POP HL ; Nvalu3: EX (SP), HL ; Return-Adress to HL, JP (HL) ; result on the stack, return ; Error: LD DE, Fmsg ; print error message CALL Print ; CALL Wait ; Wait for 'space' JP Wstart ; quit program ; ;-------------------------------- ReadChar: ; ; Reads a character from the keyboard ; ; In: Returnadress ; Out: Character read from keyboard ; Used Registers: HL, DE ; POP HL ; Get Return-adresse from Stack CALL ReadCh ; LD E, A ; Character and LD D, '$' ; stopcharacter in DE. PUSH DE ; Put it on the Stack JP (HL) ; Return ; ;-------------------------------- WriteInt: ; ; Writes an integer on the screen ; ; In: Returnadress, Integer ; Out: -- ; Used Registers: HL, DE, BC, AF ; POP BC ; Return-Adress from the Stack POP HL ; Integer to write from stack PUSH BC ; save Return-Adress CALL Sign ; process sign character LD BC, StrBuf + 1 ; pointer to string buffer LD DE, 10000 ; 5th decimal place CALL IntConv ; convert LD DE, 1000 ; 4th decimal place CALL IntConv ; convert LD DE, 100 ; 3rd decimal place CALL IntConv ; convert LD DE, 10 ; 2nd decimal place CALL IntConv ; convert LD DE, 1 ; 1st decimal place CALL IntConv ; convert LD DE, StrBuf ; load buffer with converted number CALL Print ; and Call actual write routine RET ; return ; Sign: LD A, '+' ; save '+' characte 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 ; Put '-' in buffer PUSH HL ; and Integer CALL NegOp ; to two's complement POP HL ; RET ; return ; IntConv: XOR A ; Int0: SCF ; clear carry flag CCF ; SBC HL, DE ; subtract BIT 7, H ; Integer in HL < 0 ? JR NZ, Int1 ; subtracted once too many INC A ; Ok, subtract again JR Int0 ; HL -- decimal number in DE Int1: ADD HL, DE ; revert last SBC, result in A ADD A, 30H ; now the number, convert to ASCII LD (BC), A ; save to buffer INC BC ; point to next buffer position RET ; ; ;-------------------------------- WriteChar: ; ; Write a character to the screen ; ; In: Returnadress, character ; Out: -- ; Used Registers: HL, DE ; POP HL ; get return address from stack POP DE ; get character from stack CALL WriteCh ; call actual write routine JP (HL) ; Return ; ;-------------------------------- ; Arithmetic operations ;-------------------------------- SubOp: ; ; Subtracts two integers, if the second operand ; is two's cpmplement, ADD instead ; ; In: Returnadress, Operand_2, Operand_1 ; Out: Result = Operand_1 - Operand_2 ; Used Registers: HL, DE, AF ; POP BC ; get return address from stack CALL NegOp ; If Operand_2 (next on stack) JR Sub2 ; negative just complement it and ; then just ADD ; ;-------------------------------- AddOp: ; ; Adds two integers ; ; In: Returnadress, Openrand_2, Operand_1 ; Out: Result = Operand_1 + Operand_2 ; Used Registers: HL, DE, AF ; POP BC ; Get return address from stack Sub2: POP DE ; get Operand_2 from stack POP HL ; get Operand_1 from stack XOR A ; clear carru flag ADC HL, DE ; Add Operand_1 and Operand_2 JP PE, Ovrflo ; and test for overflow PUSH HL ; Put result and returnaddress PUSH BC ; on stack RET ; ; ;-------------------------------- NegOp: ; ; Builds two's complement of Operand on stack, ; and returns it on stack ; ; In: Returnadress, Operand ; Out: Result = NEG (Operand) ; Used Registers: HL, DE, AF ; POP HL ; Get return address from stack POP DE ; Get operand from stack LD A, E ; Build two's complement bytewise CPL ; One's complement of A LD E, A ; save it LD A, D ; other byte CPL ; LD D, A ; By adding 1 to the one's complement INC DE ; You get the two's complement PUSH DE ; put the result on the stack JP (HL) ; Return ; ;-------------------------------- MultOp: ; ; Multiples two integers ; ; For better methods see C'T-Sonderheft, S.65 ff ; and "Programming the Z-80" by Rodnay. Zaks. ; ; In: Returnadress, Operand_2, Operand_1 ; Out: Result = Operand_1 * Operand_2 ; Used Registers: HL, DE, AF ; POP HL ; get return address from stack POP DE ; Get Multiplicator from stack LD C, D ; Multiplicator in A and C LD A, E ; LD B, 16 ; 16 steps to the operation POP DE ; get multiplicant from stack PUSH HL ; save return address temporarily on stack LD HL, 0000H ; to free use of HL Mult: SRL C ; Rotate multiplicator to right (both RRA ; bytes) JR NC, NoAdd ; C=1 --> add one time CCF ; ADC HL, DE ; Result = Result + Multiplicant JP PE, Ovrflo ; Overflow? NoAdd: EX DE, HL ; shift DE left ( x2) ADC HL, HL ; 16-Bit Shift left EX DE, HL ; DJNZ Mult ; Next Bit EX (SP), HL ; Result on Stack, HL <-- Ret Adr. JP (HL) ; ; ;-------------------------------- DivOp: ; ; Not implemented !!!! ; (allways returns 0000, see also ModOp) ; POP HL ; Get return address from stack POP BC ; Get Dividend from Stack POP DE ; Get Divisor from Stack LD DE, 0000H ; Result = 0 on Stack PUSH DE ; JP (HL) ; ; ;-------------------------------- ModOp: ; ; Not Implemented !!!! ; ( must be implemented different if DivOp is ; really implemented) ; JR DivOp ; return result 0 via DivOp ; ;-------------------------------- Ovrflo: ; ; Reacts to Integer over/underflow by quitiing program ; ; In: Returnadress ; Out: -- ; Used Registers: HL, DE, AF ; LD DE, OvrMsg ; Point to owverflow message JR NC, Ovr1 ; Negativ-negativ ? LD DE, UndeMsg ; yes: point to Underflow Message Ovr1: CALL Print ; print message LD DE, StopMsg ; print quit message CALL Print ; CALL Wait ; JP Wstart ; Quit program ; ;-------------------------------- Print: ; ; Write a text to the screen (CP/M Function 9). ; ; In: Returnadress, pointer to Text in DE ; Out: -- ; Used Registers: DE, C ; PUSH AF ; PUSH BC ; PUSH HL ; LD C, 09H ; print string to screen CALL BDOS ; String must end with "$". POP HL ; POP BC ; POP AF ; RET ; ; ;-------------------------------- ; Logical Operations ; Attention:Not all implemented, must be completed ;-------------------------------- OddOp: ; ; tests if integer is odd or even ; ; In: Returnadress, Top_Stack ; Out: Top_Stack = 0000H (FALSE), if Operand EVEN, ; = FFFFH (TRUE), if Operand ODD. ; Used Registers: HL, AF ; POP HL ; Get return address from stack POP DE ; Get Operand from stack PUSH HL ; Save return address x on stack XOR A ; Clear Accumularor BIT 0, E ; 0DD value --> Z-Flag set JR Z, False ; Bit 0 = 0. Even value jr true ; bit 0 = 1. Odd value ; ;-------------------------------- eqlop: ; ; Test if operands are equal ; ; in: returnadress, operand_2, operand_1 ; out: top_stack = true, if operand_1 = operand_2 ; = false if not equal ; Used Registers: hl, de, af ; POP BC ; Get return address from stack POP DE ; Get Operand_2 from stack. POP HL ; Get Operand_1 from stack. PUSH BC ; Save return address on stack XOR A ; Clear Accumulator SBC HL, DE ; HL = DE ? JR Z, True ; Yes JR False ; No ; ;-------------------------------- NeqOp: ; ; Test if are NOT equal ; ; In: Returnadress, Operand_2, Operand_1 ; Out: Top_Stack = TRUE, if Operand_1 <> Operand_2 ; = FALSE if not ; Used Registers: HL, DE, AF ; POP BC ; Get Return Address from stack POP DE ; Get Operand_2 from stack POP HL ; Get Operand_1 from stack PUSH BC ; Save return address on stack XOR A ; Clear Accumulator SBC HL, DE ; HL <> DE ? JR Z, False ; Yes, so HL not <> DE JR True ; No, so HL <> DE ; GtrOp: JR False ; LssOp: JR False ; Test on Carry after SBC GeqOp: JR False ; Test on No Carry after SBC LeqOp: JR False ; ; ;-------------------------------- True: ; ; Last part of logical function code ; (Result 00H = FALSE, anything else = TRUE.) ; ; In: A = 00H ; Out: Result ; Used Registers: HL, DE, A ; DEC A ; A <-- 0FFH False: LD D, A ; DE with Result LD E, A ; POP HL ; get return address from stack PUSH DE ; put Result on stack JP (HL) ; exit point of all logical functions ; ;-------------------------------- ; Read a character. ; ReadCh: PUSH BC ; Read a character from keyboard PUSH DE ; PUSH HL ; LD C, 01H ; CALL BDOS ; Character will be in A POP HL ; F is destroyed POP DE ; POP BC ; RET ; ; ;-------------------------------- ; Write a character. ; WriteCh:PUSH AF ; Write a character to the screen PUSH BC ; 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 ; Wait for 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, 'Number expected$', 00H, 00H StopMsg: DB cr, lf, 'Program terminated$' EnteMsg: DB ' - press SPACE bar', cr, lf, '$' StrBuf: DB '+', 00H, 00H, 00H, 00H, 00H, '$' ; 6 Byte Buffer for Integer and sign character ; ; Reserve two Byte for Stack-pointer-Temporary saves ; TMSPTOR = MCode_Offset-2 ; TmpStor:DW 0000H ; ;-------------------------------- ; The followong JUMP is on the start address of the ; Machine code program and will be overwritten when the ; run time code is loaded ; The following MUST be true : MCode_Offset := START!! ;-------------------------------- ; Warmstart, if the RTL is accidentally ; run as machine code program ; Start: JP 0000H ; Warm Start ; ;-------------------------------- ; END 0100H ; Standard CP/M COMmand file