title 'CORE Board Subroutines' ; ;---------------------------------------------------------------- ; ---- CORE SUBROUTINES Module ---- ; ; This is a library of subroutines for CORE BOARD that must ; do simple functions like arithmetic, caps, delay etc. ; ; Written By Richard Holmes 04-02-86 ; Last Update by Richard Holmes 29-06-86 ; ;---------------------------------------------------------------- ; public phacc,prhex,phexl,crlf ; ptxt public caps,delay,bell public nibasc,hexbcd,pdde,phde,pdacc public pdhl,get$yn,get$yns,makasc public space,space2,space3,lz$pdde,lz$pdacc public slash,colon,comma,idhl,ihhl,get$txt public inline,phhl,ptxt ; public rnd$ini,rnd$8,rnd$16 ; extrn clr$wdt,cie,coe,cst ; maclib core maclib z80 ; ;---------------------------------------------------------------- ; Print the message at the return address till a '$' or ; a null. ; Return to the program at the address just after the null ; or '$'. ;---------------------------------------------------------------- ; inline: xthl ; get address of string (ret address) push psw inline2: mov a,m inx h ; point to next character cpi '$' jrz inline3 ora a jrz inline3 call coe jr inline2 inline3: pop psw xthl ; load return address after the '$' ret ; back to code immediately after string ; ; ;================================================================ ; The following two entry points read ascii from the KEYBOARD ; and convert to a number into the HL register pair. ; ; 1) IDHL Read a DECIMAL number into HL. Note that the result ; is HEX still so that it can be used as a counter ; ie. 100 input returns HL = 64. ; 2) IHHL Read a HEX number into HL ; ; Both routines return zero in A if the last character read was a legal ; digit else A will contain the error character. ;================================================================ ; idhl: call get$buf ; load the buffer from console lxi h,0 lda bufsiz ora a rz ; quit if nothing read ; Now read the buffer, condition, put into HL. push b ; save push d mov b,a ; use as a counter idhl2: call get$chr ; Get a character ; Convert to a binary value now of 0..9 sui '0' jrc inp$err ; Error since a non number cpi 9 + 1 ; Check if greater than 9 jrnc inp$err ; Now shift the result to the right by multiplying by 10 then add in this digit mov d,h ; copy HL -> DE mov e,l dad h ; * 2 dad h ; * 4 dad d ; * 5 dad h ; * 10 total now ; Now add in the digit from the buffer mov e,a mvi d,00 dad d ; all done now ; Loop on till all characters done djnz idhl2 ; do next character from buffer jr inp$end ; all done ; ; ;---------------------------------------------------------------- ; Read a HEX number into HL from the keyboard. ;---------------------------------------------------------------- ; ihhl: call get$buf lxi h,00 lda bufsiz ora a rz ; return if no character read ; push b push d ; save mov b,a ; ihhl2: call get$chr ; get a character ; Now convert the nibble to a hex digit 0..F sui '0' cpi 9 + 1 jrc ihhl3 ; mask in then sui 'A'-'0'-10 cpi 16 jrnc inp$err ; ; Shift the result left 4 bits and MASK in the digit in A ihhl3: dad h dad h dad h dad h ; shifted right 4 now ora l ; mask in the digit mov l,a ; put back djnz ihhl2 ; keep on till all digits done ; inp$end: xra a ; Zero is a goo exit inp$end2: pop d pop b ret ; inp$err: ; Here when a non digit is encountered lda buftmp jr inp$end2 ; ; Subroutines for shared code etc.... ; get$buf: ; Load the buffer from the screen via CBUFF. push d mvi a,6 sta buffer ; Set up ready for user xra a sta buffer+1 ; clear buffer original value lxi d,buffer call get$txt ; Get a text buffer full pop d lxi h,buftxt ; point to the start of text shld bufadr ; set up a pointer lxi h,00 ; clear the result register ret ; ; Get a character from the buffer, capitalize it on the way ; get$chr: push h lhld bufadr mov a,m ; get the character sta buftmp ; save the character inx h ; point to next character shld bufadr pop h ; restore ; Now capitalize it jmp caps ; ;================================================================ ; ---- Read a text string ---- ; ; This routine reads a line of input from the console and puts it into ; a standard CP/M console buffer pointed to by DE on entry. This is ; a little nicer that CP/M as it allows buffers to be pre-initialized ; so that it is printed when the buffer is input so that defaults can ; be loaded before entry of data. ; ; On Entry ; DE -> console buffer max size byte ; ; On Exit ; buffer filled from console to max size limit ; All registers preserved ; ;================================================================ ; get$txt: push psw ldax d ; get buffer size in bytes ora a jz cbuff$end push h push b push d xchg ; put string address into HL mov c,a ; Now C = buffer maximum size init: mvi b,00 ; characters read = 0 inx h ; hl -> size of character read now ; ; Here we detect if there is some data in the buffer to be pre printed ; and if there is the we print it. ; mov a,m ; get number of chars. in the buffer inx h ; point to string space now. ora a jrz rdloop ; Print the initialized character string, save the size for later mov b,a push b ; save init2: mov a,m ; get the character inx h ; point to next string space byte call dspchr ; print it, maybe control character djnz init2 ; print all characters pop b ; restore # of characters ; ; ; On entry here HL-> string space, next free byte, B = number of characters ; in the string. C = number of bytes in the buffer. ; rdloop: call cie ; Fetch a character cpi 0dh ; end if carriage return jrz exitrd ; exit cpi 0ah jrz exitrd cpi 08 ; backspace ?? jrnz rdlp1 ; if not then continue call backsp ; else backspace jr rdloop ; keep on backspacing rdlp1: cpi 018h ; delete line ? jrnz rdlp2 del1: call backsp ; delete a character jrnz del1 ; keep on till all character deaded jr rdloop ; start again ebonettes ; ; If here we check if the buffer is full. If so we ring the bell rdlp2: mov e,a ; save the character mov a,b ; load byte count cmp c ; is it equal to the maximum ? jrc strch ; store the character if not full call bell ; else ring the bell jr rdloop ; get more characters ; ; Buffer not full so save the character strch: mov a,e ; get character mov m,a ; save it inx h ; point to next buffer byte inr b ; increment byte count call dspchr ; display the (maybe control) character jr rdloop ; do again, more characters ; ; Display a control character by preceeding it with a '^' ; dspchr: cpi 020h ; was it a space ? jnc coe ; if not then print & return mov e,a ; else save character mvi a,'^' ; indicate a control character call coe mov a,e ; restore character adi 040h ; make printable jmp coe ; ; Send a backspace and detect if at the start of the line. ; backsp: mov a,b ; get character count ora a rz ; return if line empty dcx h ; decrement byte pointer mov a,m ; get the character cpi 020h ; is it a control character ? jrnc bsp1 ; if not then delete 1 char only call bsp ; send a backspace bsp1: call bsp ; backspace 1 dcr b ; one less string byte ret ; ; Send the backspace bsp: mvi a,08 call coe ; Go back a char not req-r for cp/m mvi a,' ' ; erase the character call coe mvi a,08 jmp coe ; send and return ; ; Set the number of bytes read into the buffer byte at DE + 1. ; exitrd: pop d ; restore all registers (buffer addr) mov a,b ; get # of characters inx d stax d ; save in characters read byte dcx d ; restore de ; pop b pop h cbuff$end: pop psw ora a ; Clear carry ret ; ;================================================================ ; ; Conversion routines ; ;================================================================ ; ; ---- Convert low nibble of A to ascii ---- ; ;================================================================ ; nibasc: ani 0fh adi 090h daa aci 040h daa ret ; ;================================================================ ; ; Convert the hex number in DE into DECIMAL in HL ; ;================================================================ ; hexbcd: sded ?binnum ; save the number to convert push b push h ; Do the conversion mvi b,3 ; 3 bytes to clear hexbcd1: mvi m,00 inx h djnz hexbcd1 ; clear 3 bytes ; mvi b,16 ; 16 bits to convert cloop: lxi h,?binnum mvi c,2 ; bytes in the binary number xra a ; clear carry rloop: mov a,m ral mov m,a inx h dcr c jnz rloop ; keep rotating till C = 0 ; pop h push h ; restore the result address mvi c,3 ; 3 byte result = 6 digits ; bloop: mov a,m adc m daa mov m,a ; save inx h dcr c jnz bloop ; djnz cloop ; do for all bits requited. ; pop h pop b ; clear stack ret ; trick code here boys ; ;================================================================ ; Print HL as decimals ;================================================================ ; pdhl: push d push h xchg ; DE = value call pdde ; Print it pop h pop d ret ; ;================================================================ ; ; ---- Print DE as decimal ---- ; ;================================================================ ; pdde: push h push d lxi h,?result call hexbcd ; convert to ascii in internal buffer ; Now print the 5 digit number lda ?result+2 ; get the MSDigit ora a ; A null ? - Suppress it if so jrz pdde1 call nibasc ; convert lower nibble call coe ; Print it ; Now do other 4 digits pdde1: lda ?result+1 call prhex ; Print it high 2 lda ?result call prhex ; low 2 pop d pop h ret ; ;================================================================ ; ; ---- Leading Zero Print of DE in decimal ---- ; ;================================================================ ; lz$pdde: push h push d push b lxi h,?result call hexbcd ; convert to ascii in internal buffer ; Now print the 5 digit number. Suppress leading digits. mvi c,0 lda ?result+2 ; Get the MSDigit ani 0fh call ccoe ; Conditional output ; lda ?result + 1 push psw rar rar rar rar call ccoe ; pop psw call ccoe ; lda ?result ; Least significant 2 digits push psw rar rar rar rar call ccoe ; pop psw call nibasc ; Always print last digit call coe pop b pop d pop h ret ; ;---------------------------------------------------------------- ; Conditional print of A. This is part of the leading zero ; printing routine. It prints a '0' if register C is > 0. ; If the number in A is > 0, register C is set to 80 so ; that all following numbers are forced out. ; ;---------------------------------------------------------------- ; ccoe: ani 0000$1111b ; Eliminate top 4 bits ora a ; Low nibble > 0 ? jrz ccoe1 setb 7,c ; Set top bit. ; ccoe1: bit 7,c ; Forcing number out ? rz ; If not, exit. call nibasc ; Else convert A to ascii jmp coe ; And print it. ; ;================================================================ ; ---- Print HL as 4 hex digits ---- ;================================================================ ; phhl: xchg call phde xchg ret ; ;================================================================ ; ; ---- Print DE as 4 hex digits ---- ; ;================================================================ ; phde: push psw push d mov a,d call prhex mov a,e call prhex pop d pop psw ret ; ;================================================================ ; ; ---- Print decimal accumulator ---- ; Note that if A > 99 the top digit is also printed, not if < 99 ; ;================================================================ ; pdacc: push d push h push psw mov e,a mvi d,0 lxi h,?result call hexbcd pop psw push psw cpi 99 jrc pdacc1 ; < 99, skip it lda ?result + 1 ; Top digit call nibasc call coe ; Print top digit 1 or 2 pdacc1: lda ?result + 0 call prhex ; Print low 2 digits pop psw pop h pop d ret ; ;================================================================ ; ; ---- Leading zero print the accumulator in decimal ---- ; ;================================================================ ; lz$pdacc: push h push b push d push psw mov e,a mvi d,0 lxi h,?result call hexbcd lda ?result + 1 mvi c,0 ; LZB status register clear call ccoe ; Print hundreds digit ; Second top lda ?result push psw rar rar rar rar call ccoe ; Print tens digit ; pop psw call nibasc call coe ; print units ; pop psw pop d pop b pop h ret ; ;================================================================ ; ; Delay the number of milliseconds in DE ; ;================================================================ ; delay: mov a,e ora d rz ; push d ; save it delay2: call clr$wdt call delay3 dcx d ; one less millisecond less overhead mov a,d ora e jrnz delay2 ; keep on till DE = 0 pop d ; restore users initial value ret ; back to user ; ; Delay 1 millisecond less the overhead involved in the above code. ; ; This routine must delay 3957 t-states ; delay3: push b ; 11 mvi b,224 ; 7 delay4: ; This loop does (4 + 13) * 230 - 5 = 3905 t nop ; 4 djnz delay4 ; 13 ; Fudge 14 machine cycles lxi b,0 ; 10 nop ; 4 pop b ; 10 ; ret ; ;================================================================ ; ; ---- Capitalize the accumulator ---- ; ;================================================================ ; caps: cpi 'a' ; Convert lower case to upper rc cpi 'z'+1 rnc ani 5fh cpi 03 jz 0h ; Exit if control C ret ; ;================================================================ ; ; ---- Ring the console bell ---- ; ;================================================================ ; bell: push psw mvi a,7 call coe pop psw ret ; ;================================================================ ; ; ---- Print a cr/lf pair ---- ; ;================================================================ ; crlf: push psw mvi a,cr call coe mvi a,lf call coe pop psw ret ; ;================================================================ ; ; Print text at DE till a 00 or a '$' ; ;================================================================ ; ptxt: ldax d ora a rz cpi '$' rz call coe inx d jr ptxt ; ;================================================================ ; ; ---- Get a Y or an N from console ---- ; ;================================================================ ; get$yn: call cie cpi cr jrz get$yn cpi lf jrz get$yn ; cpi esc rz call caps call coe cpi 'Y' rz cpi 'N' rz ; Not valid then call bell ; Ding a ling call bsp ; Backspace jr get$yn ; Wait for a N or Y ; ;================================================================ ; ; ---- Get a Y, an N or an S from console ---- ; ;================================================================ ; get$yns: call cie cpi cr jrz get$yns cpi lf jrz get$yns ; cpi esc rz call caps call coe cpi 'Y' rz cpi 'N' rz cpi 'S' rz ; Not valid then call bell call bsp jr get$yn ; Wait for a N or Y ; ;================================================================ ; Here the character in A is converted into ascii or is given ; a '.' if hex. The result is put into A for easy access via coe. ;================================================================ ; makasc: ani 07fh ; Make in the range cpi 020h ; Lower than a space is illegal jrc noasc ; Not ascii cpi 07bh ; Higher than upper case is illegal too rc ; return with ascii character in C noasc: mvi A,02eh ; Replace with a '.' ret ; ;================================================================ ; ; ---- Space printing on the console ---- ; ;================================================================ ; space3: call space space2: call space space: mvi a,' ' jmp coe ; ;================================================================ ; ; ---- Print a comma ---- ; ;================================================================ ; comma: push psw mvi a,',' call coe pop psw ret ; ; ;================================================================ ; ; ---- Print a slash ---- ; ;================================================================ ; slash: push psw mvi a,'/' call coe pop psw ret ; ;================================================================ ; ; ---- Print a colon ---- ; ;================================================================ ; colon: push psw mvi a,':' call coe pop psw ret ; ;================================================================ ; ; ---- Print the hex digits in A ---- ; ;================================================================ ; phacc: prhex: push psw rrc rrc rrc rrc call phexl pop psw phexl: ani 0fh adi 90h daa aci 40h daa jmp coe ; ;---------------------------------------------------------------- ; Random number generator. This is used for testing the UCP ;---------------------------------------------------------------- ; ;---------------------------------------------------------------- ; Initialize random generator. ; ; On entry ; DE -> string to randomize, length at front ;---------------------------------------------------------------- ; rnd$ini: ldax d cpi 5 rc ; Error if less than 5 elements in the array push d push b mov b,a ; Load counter xchg inx h ; Now HL -> first seed byte ldar ; Get refresh register value dcr b ; Do one less than the required initloop: add m rrc mov m,a inr m mov a,m inx h djnz initloop ; Restore and exit gracefully xchg ; Restore HL pop b pop d ; Restore other registers ret ; ;---------------------------------------------------------------- ; Return 16 bit random in HL. ; ; On entry ; DE -> random string ;---------------------------------------------------------------- ; rnd$16: call rnd$8 mov h,a ; msb byte call rnd$8 mov l,a ; lsb byte ret ; ;---------------------------------------------------------------- ; Return an 8 bit random number in A ; ; On entry ; DE -> random string ;---------------------------------------------------------------- ; rnd$8: ldax d ; A = number of seeds cpi 5 ; Check if less than 5 seed values rc ; Return with a carry to indicate an error ; Here we load the number of cells into B then decrtement so as to skip ; these which are operated on later. push b push d ; Saver address of seed array mov b,a dcr b dcr b inx d ; DE -> first seed in the array xchg ; Put memory pointer into HL ; ;Loop for N-2 times. loop: inr m ;INCREMENT SEED VALUE. mov a,m inx h ;HL POINTS TO NEXT SEED VALUE IN ARRAY. add m rrc ;ROTATE RIGHT CIRCULAR ACCUMULATOR. mov m,a djnz loop ; ; Last iteration to compute random byte in register a. inx h ; HL -> last byte in the array add m cma ; complement the accumulator rrc ; rotate it right mov m,a xra a ; Clear carry mov a,m ; Re-load value, carry not set. ; ; Restore the registers and return with the value in A ERROR: xchg ; Restore HL pop d pop b ret ; dseg ; ?binnum ds 10 ?result ds 10 buftmp db 00 ; A temporary character store bufadr: db 00,00 buffer: db 6 ; maximum characters bufsiz: db 00 ; characters read buftxt: db 00,00,00,00,00,00 ; text buffer ; end ; ;