title 'Executive Command Processor' ; ;---------------------------------------------------------------- ; Executive Command Processor ; ; This module provides services much in the way that a dos does. ; It is called by a RESTART or anything else, it interprets ; register setups and performs the functions specified. ; This module interfaces into the I/O drivers rather than ; providing its own I/O. It does however have some code to ; provide some of the more general purpose routines. ; ; This program is Copyright (C) 1987 by SME Systems P/L ; 22 Queen Street Mitcham ; ; ; Written by Richard Holmes 08/12/1987 ; Last Update by Richard Holmes 20/02/1988 ; ; Added the price print function 36 05/02/88 ; Added the time print function 37 06/02/88 ; Added ????$HDLR interrupt handlers 20/02/88 ;---------------------------------------------------------------- ; maclib z80 ; ; maclib iocmd ; I/O Driver command library public exec ; The ONLY entry point. ; ; Interrupt handlers public con$hdlr, aux$hdlr ; Con and aux ints public tmr0$hdlr, tmr1$hdlr ; Timer ints public int0$hdlr, int1$hdlr, int2$hdlr ; INT pin ints public csio$hdlr ; CSIO ints ; ; I/O Drivers for the character display devices extrn sys$ini extrn con$ini,con$ist,con$ost,con$inp,con$out,con$cmd ; Console extrn aux$ini,aux$ist,aux$ost,aux$inp,aux$out,aux$cmd ; Aux serial extrn lcd$ini,lcd$ost,lcd$out,lcd$cmd ; Main LCD extrn prn$ini,prn$ost,prn$out ; Printer ; extrn ms$delay,clr$wdt ; extrn clk$rd,clk$wr extrn clr$led,set$led,tog$led,zro$led extrn set$bel,clr$bel extrn set$csh,clr$csh ; extrn hrs,min ; cmd$max equ 69 ; Total of 69 commands allowed max. max$chn equ 6 ; Last used channel number ; psh$max equ 5 ; Maximum channel "pushes" allowed psh$siz equ 18 ; Bytes to "push" (routines * 3) ; cr equ 0dh lf equ 0ah esc equ 01bh ; ; I/O Commands for intelligent devices to process. ; clr$scr equ 01 ; Clear screen clr$eos equ 02 ; Clear to end of screen clr$eol equ 03 ; Clear to end of line cur$adr equ 04 ; Cursor address vid$att equ 05 ; Select video attribute (flash, reverse etc) ; page ;---------------------------------------------------------------- ; The Executive processor ; ; On Entry ; C = command number, ; All else are parameters. ;---------------------------------------------------------------- ; exec: push h push b push d ; lxi h,table mvi b,0 ; BC = offset. dad b dad b ; HL = table base + (2 * offset) ; mov e,m inx h mov d,m ; DE = address from table xchg ; Hl -> routine ; Restore registers and goto routine pop d pop b xthl ; Top stack = address, HL restored ret ; Goes to routine. ; ;---------------------------------------------------------------- ; A table of addresses of the routines. ;---------------------------------------------------------------- ; table: dw cmd$0 ; Reset hardware dw cmd$1 ; dw cmd$2 ; dw cmd$3 ; dw cmd$4 ; dw cmd$5 ; dw cmd$6 ; dw cmd$7 ; dw cmd$8 ; dw cmd$9 ; ; dw cmd$10 ; dw cmd$11 ; dw cmd$12 ; dw cmd$13 ; dw cmd$14 ; dw cmd$15 ; dw cmd$16 ; dw cmd$17 ; dw cmd$18 ; dw cmd$19 ; ; dw cmd$20 ; dw cmd$21 ; dw cmd$22 ; dw cmd$23 ; dw cmd$24 ; dw cmd$25 ; dw cmd$26 ; dw cmd$27 ; dw cmd$28 ; dw cmd$29 ; ; dw cmd$30 ; dw cmd$31 ; dw cmd$32 ; dw cmd$33 ; dw cmd$34 ; dw cmd$35 ; dw cmd$36 ; dw cmd$37 ; dw cmd$38 ; dw cmd$39 ; ; dw cmd$40 ; dw cmd$41 ; dw cmd$42 ; dw cmd$43 ; dw cmd$44 ; dw cmd$45 ; dw cmd$46 ; dw cmd$47 ; dw cmd$48 ; dw cmd$49 ; ; dw cmd$50 ; dw cmd$51 ; dw cmd$52 ; dw cmd$53 ; dw cmd$54 ; dw cmd$55 ; dw cmd$56 ; dw cmd$57 ; dw cmd$58 ; dw cmd$59 ; ; dw cmd$60 dw cmd$61 dw cmd$62 dw cmd$63 dw cmd$64 dw cmd$65 dw cmd$66 dw cmd$67 dw cmd$68 dw cmd$69 ; page ;---------------------------------------------------------------- ; T H E C O M M A N D S ;---------------------------------------------------------------- ; cmd$0: ; Reset EXEC, initialize call clr$wdt ; Init all the interrupt handers to be returns mvi a,(RET) ; Loads a return into A sta con$hdlr sta aux$hdlr sta tmr0$hdlr sta tmr1$hdlr sta int0$hdlr sta int1$hdlr sta int2$hdlr sta csio$hdlr ; call clr$wdt call sys$ini ; Initialize all hardware xra a sta dsp$flg ; Clear the display the code flag sta exe$dbg ; Clear the debugging flag sta lzb$mode ; Print all characters LZB mode call psh$pop$ini ; ; Initialize/Reset all I/O channels. xra a ; Force ALL channel initialize call init$chan ; Initialize channels routine mvi a,1 ; Main console. call sel$chn ; Channel select console first time. ; ; call zro$led ; Clear leds jmp exec$end ; ; ; cmd$1: ; Select I/O Channel for I/O call sel$chn ; Select the channel jmp exec$end ; cmd$2: ; Reset One/all i/o channels call init$chan jmp exec$end ; cmd$3: ; Return current channel number lda cur$chn jmp exec$end ; cmd$4: ; Read current channel into accumulator call chn$inp jmp exec$end ; cmd$5: ; Write to current channel call chn$out jmp exec$end ; ; cmd$6: ; Return channel input status call chn$ist jmp exec$end ; cmd$7: ; Return channel output status call chn$ost jmp exec$end ; cmd$8: ; Print string at mDE ldax d inx d ; -> next ora a jz exec$end call chn$out ; Channel output routine jr cmd$8 ; ; cmd$9: ; Print string at return address xthl ; get address of string (ret address) push psw inline2: mov a,m inx h ; point to next character ora a jrz inline3 call chn$out jr inline2 inline3: pop psw xthl ; load return address after the '$' jmp exec$end ; ; ; cmd$10: ; Print X-Y prefixed string at return address xthl ; HL -> string, old hl in stack xchg ; now DE --> string call setxy ; set it up xchg ; now hl --> string start again call print xthl ; hl = original value, stack = return address jmp exec$end ; ;---------------------------------------------------------------- ; Print the string --> by DE. Use the two bytes at the start of it ; as a screen address. ;---------------------------------------------------------------- ; xypstring: push h call setxy ; set up screen xchg ; now hl --> string start call print xchg ; Restore DE --> past end of string pop h ret ; ; ; ---- Utility to print a string till a $. ---- ; On return HL -> to next byte after the string (code maybe) ; print: push psw inx h inx h ; skip over cursor address print2: mov a,m inx h ; Point to next character ora a ; null is allowed to end a string jrz print3 call chn$out jr print2 ; print3: pop psw ret ; ;---------------------------------------------------------------- ; Set the cursor up according to two bytes in ram which contain ; the X and Y addresses in them. The bytes --> by DE. ;---------------------------------------------------------------- ; setxy: push d ; save push h xchg ; HL --> bytes mov d,m ; load X value inx h mov e,m call cmd$12 ; Cursor Set up pop h ; restore all now pop d ret ; ;---------------------------------------------------------------- ; Print the MENU at mDE. A menu is a list of asciiz strings ; each prefixed with an X-Y cursor address. This code will ; put each string at the specified address. The routine is ; terminated with an 0FFh byte. ;---------------------------------------------------------------- ; cmd$11: ; Print menu at mDE push d ; save all push h push psw xchg ; Hl -> menu of X-Y prefixed strings pmenu2: call clrwdt ; Reset watchdog mov d,m ; X address inx h mov e,m inx h call cmd$12 ; Cursor address the string now pmenu$string: mov a,m inx h ; -> next byte ora a ; Is it 00 ? jrz pmenu$eos ; Exit on 00 to end ofline code call chn$out ; Send the byte to the channel jr pmenu$string ; Keep printing till the 00 ; pmenu$eos: mov a,m ; Is next byte 0FFh to terminate. cpi 0ffh ; end of menu ?? jrnz pmenu2 ; pop psw pop h pop d jmp exec$end ; ; cmd$12: ; Cursor address the screen/channel mvi c,cur$adr ; Cursor address command call chn$cmd ; Process the command via the channel jmp exec$end ; cmd$13: ; Clear screen mvi c,clr$scr ; Clear all screen code call chn$cmd jmp exec$end ; ; cmd$14: ; Clear to end of line mvi c,clr$eol ; Clear to end of line call chn$cmd jmp exec$end ; cmd$15: ; Clear to end of screen mvi c,clr$eos ; Clear to end of screen code call chn$cmd jmp exec$end ; cmd$16: ; Select visual attribute mvi c,vid$att ; Set visual attribute call chn$cmd jmp exec$end ; cmd$17: ; Print accumulator as 2 hex digits prhex: push psw rrc rrc rrc rrc call phexl pop psw phexl: ani 0fh adi 90h daa aci 40h daa call chn$out jmp exec$end ; ; cmd$18: ; Print accumulator as decimal 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 chn$out ; print units ; pop psw pop d pop b pop h jmp exec$end ; ; cmd$19: ; Print DE as 4 hex digits push psw push d mov a,d call prhex mov a,e call prhex pop d pop psw ret jmp exec$end ; ; cmd$20: ; Print DE as decimal unsigned decimal, with LZB 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. lda lzb$mode mov c,a ; Load the flag lda ?result+2 ; Get the MSDigit ani 0fh call ccoe ; Conditional output with lzb ; 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 chn$out pop b pop d pop h jmp exec$end ; ;================================================================ ; ; Print HLDE as a 32 bit decimal number ; ;================================================================ ; cmd$21: ; Print HLDE as 32 bit unsigned decimal push h push b ; call convert$hlde ; ; ---------------- Now print the result ---------------- ; ; Use the specified leading zero print type. ; mvi b,05 ; bytes = 10 digits lda lzb$mode mov c,a ; Load leading zero printing type lxi h,?result+4 ; -> MSDigit lzb$hlde: mov a,m rar rar rar rar call ccoe ; See if last digit and if so, force the number out. mov a,b cpi 1 jnz lz$hlde1 mvi c,080h ; Force digits out of lz printer lz$hlde1: mov a,m call ccoe ; dcx h ; -> next byte djnz lzb$hlde ; pop b pop h ret ; ;---------------------------------------------------------------- ; Convert 32 bits HLDE into 10 ascii decimal digits in the ; ?result buffer. ;---------------------------------------------------------------- ; convert$hlde: sded ?binnum ; save LSW shld ?binnum + 2 ; Save MSW ; ; Do the conversion lxi h,?result ; -> result buffer mvi b,5 ; bytes to clear hlde1: mvi m,00 inx h djnz hlde1 ; clear 5 bytes = 10 digits ; mvi b,32 ; 32 bits to convert hlde$loop: lxi h,?binnum mvi c,4 ; bytes in the binary number xra a ; clear carry h$rloop: mov a,m ral mov m,a inx h dcr c jnz h$rloop ; keep rotating till C = 0 ; lxi h,?result ; restore the result address mvi c,5 ; 5 byte result = 10 digits ; h$bloop: mov a,m adc m daa mov m,a ; save inx h dcr c jnz h$bloop ; djnz hlde$loop ; do for all bits requited. ; ret ; ;---------------------------------------------------------------- ; Set the LZB Mode ; ; LZB modes ; 00 = normal LZB, default ; 01 = Force all characters out ; 02 = Space fill all leading zeros ;---------------------------------------------------------------- ; cmd$22: ; Set leading Zero blanking mode push b mvi c,080h ; 80 hex internally = nolzbing cpi 2 jrz cmd$22$do ; mvi c,040h ; 40h internally = lzb cpi 1 jrz cmd$22$do mvi c,0 ; All else = 0 so normal lzbing. ; cmd$22$do: mov a,c sta lzb$mode pop b jmp exec$end ; page ;================================================================ ; ---- 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, then edited. ; ; On Entry ; DE -> console buffer max size byte ; ; On Exit ; buffer filled from console to max size limit ; All registers preserved ; ;================================================================ ; cmd$23: 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 chn$inp ; 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 ; mvi a,7 ; Else load the bell code call chn$out ; And send to 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 chn$out ; if not then print & return mov e,a ; else save character mvi a,'^' ; indicate a control character call chn$out mov a,e ; restore character adi 040h ; make printable jmp chn$out ; ; 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 chn$out ; Go back a char not req-r for cp/m mvi a,' ' ; erase the character call chn$out mvi a,08 jmp chn$out ; 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 ; ; cmd$24: ; Read a hex number from channel call ihhl ; Read HEX into HL jmp exec$end ; cmd$25: ; Read a decimal number from channel call idhl ; Read decimal into HL jmp exec$end ; cmd$26: ; Print a space space: mvi a,' ' call chn$out jmp exec$end ; cmd$27: ; Do a carriage return and line-feed mvi a,cr call chn$out mvi a,lf call chn$out jmp exec$end ; cmd$28: ; Push the current channel push h push b push d ; call psh$chn ; pop d pop b pop h ; jmp exec$end ; cmd$29: ; Pop the current channel push h push b push d ; call pop$chn ; pop d pop b pop h jmp exec$end ; ; cmd$30: ; Read RS-232 channel 0 (console) call con$inp jmp exec$end ; cmd$31: ; Write to RS-232 channel 0 (console) call con$out jmp exec$end ; cmd$32: ; Return RS-232 channel 0 input status call con$ist jmp exec$end ; cmd$33: ; Return RS-232 channel 0 output status call con$ost jmp exec$end ; cmd$34: ; Read real time clock call clk$rd jmp exec$end ; cmd$35: ; Write to real time clock call clk$wr jmp exec$end ; ;---------------------------------------------------------------- ; ==== Dinos Requirement ==== ; ; Print an integer as if it were a price. ;---------------------------------------------------------------- ; cmd$36: ; push h push d push b lxi h,?result call hexbcd ; convert to ascii in internal buffer ; mvi a,'$' call chn$out ; Print leading dollar sign ; Now print the 5 digit number. Suppress leading digits. lda lzb$mode mov c,a ; Load the flag lda ?result+2 ; Get the MSDigit ani 0fh call ccoe ; Conditional output with lzb ; lda ?result + 1 push psw rar rar rar rar call ccoe ; pop psw call ccoe ; ; Print a '.' then force the last two digits out. mvi a,'.' call chn$out ; call prn$last2 ; Print last 2 digits pop b pop d pop h jmp exec$end ; ;---------------------------------------------------------------- ; Print a time in HH:MM format. This is a DINOS routine ; that is used a LOT. ;---------------------------------------------------------------- ; cmd$37: ; push h push d push b ; ; Do hours lda hrs mov e,a mvi d,0 ; DE = hours now. lxi h,?result call hexbcd ; convert to ascii in internal buffer call prn$last2 ; mvi a,':' call chn$out ; Channel output print. ; ; Do minutes. lda min mov e,a mvi d,0 ; DE = Minutes now. lxi h,?result call hexbcd ; convert to ascii in internal buffer call prn$last2 ; pop b pop d pop h jmp exec$end ; ; A subroutine to print the least significant 2 digits of a ; converted number. This is used to force out a time and ; for printing a PRICE where the last 2 digits are required. ; prn$last2: lda ?result ; Least significant 2 digits push psw rar rar rar rar call nibasc call chn$out ; Print low nibble. Was high nibble ; pop psw call nibasc ; Always print last digit jmp chn$out ; ;---------------------------------------------------------------- ; ---- Print DE as a time. ---- ; ; On Entry ; D = hours ; E = minutes ;---------------------------------------------------------------- ; cmd$38: ; push h push d push b ; push d ; mov e,d ; Make E = hours mvi d,0 ; DE = hours now. lxi h,?result call hexbcd ; convert to ascii in internal buffer call prn$last2 ; mvi a,':' call chn$out ; Channel output print. ; ; Do minutes. pop d mvi d,0 ; DE = Minutes now. lxi h,?result call hexbcd ; convert to ascii in internal buffer call prn$last2 ; pop b pop d pop h jmp exec$end ; ; cmd$39: ; jmp exec$end ; cmd$40: ; Clear watchdog call clr$wdt jmp exec$end ; cmd$41: ; Read switch number in (A) jmp exec$end ; cmd$42: ; Clear all leds call zro$led jmp exec$end ; cmd$43: ; Set led number in A call set$led jmp exec$end ; cmd$44: ; Clear led number in A call clr$led jmp exec$end ; cmd$45: ; Toggle led number in A call tog$led jmp exec$end ; ; ; ---- Do a delay of milliseconds in DE ---- ; cmd$46: ; Delay milliseconds in DE call ms$delay jmp exec$end ; cmd$47: ; Turn on beeper call set$bel jmp exec$end ; cmd$48: ; Turn off the beeper call clr$bel jmp exec$end ; cmd$49: ; Turn on cash drawer (open) call set$csh jmp exec$end ; ; cmd$50: ; Cash drawer off (closed) call clr$csh jmp exec$end ; cmd$51: ; Compare two strings jmp exec$end ; cmd$52: ; Compare string to table of strings jmp exec$end ; cmd$53: ; Index via A into table of words and retun vaue in A lda usr$a mov c,a xchg ; HL -> table now dad b ; HL = HL + offset dad b ; (2 * offset) mov e,m inx h mov d,m xchg ; HL = the word jmp exec$end ; cmd$54: ; Convert low nibble of A to ascii nibasc: ani 0fh adi 090h daa aci 040h daa jmp exec$end ; cmd$55: ; Capitalize accumulator caps: cpi 'a' ; Convert lower case to upper jc exec$end cpi 'z'+1 jnc exec$end ani 5fh jmp exec$end ; cmd$56: ; Test memory jmp exec$end ; cmd$57: ; Put A into the EXEC code display flag. 00 = off lda usr$a sta dsp$flg ; Save to the display flag jmp exec$end ; ; cmd$58: ; Clear cumulatinve checksum push h lxi h,0 ; do a clear shld rem pop h jmp exec$end ; ; ;---------------------------------------------------------------- ; This routine generates a polynomial to generate a 16 bit cyclic- ; redundancy checksum. The checksum is able to distinguish between ; two very similar data items since any differences in quickly ; show up in the checksum. This is useful for data integrity checking ; in disk files or over modem lines etc. ; ; A cyclic-redundancy-check number is generated by the ccitt ; standard polynominal: ; x^16 + x^15 + x^13 + x^7 + x^4 + x^2 + x + 1 ; ; The routine for doing this is in 8080 and comes from the ; 'EDN' magazine June 5 1979 issue Page 84. Written By Fred Gutman. ; ; Written R.C.H. 22/09/83 ; Last Update R.C.H. 26/08/88 ;---------------------------------------------------------------- ; ; On Entry ; DE -> Memory to be checksummed ; BC = bytes to be checksummed ; ; On Exit ; HL = result ; psw lost ;---------------------------------------------------------------- ; cmd$59: push d push b lhld rem ; loop59: call clr$wdt ; Stop watchdog ldax d ; Get character sta mess ; Save character ; mov a,h ; High 8 bits of remainder ani 128 ; q-bit mask push psw ; save status dad h ; 2 x r(x) lda mess ; message bit in lsb add l mov l,a pop psw jz qb2 ; if q-bit is zero qb: mov a,h xri 0a0h ; ms half of gen. poly mov h,a mov a,l xri 97h ; ls half of gen. poly mov l,a qb2: inx d ; -> next memory variable dcx b ; Decrement byte count mov a,b ora c jnz loop59 ; Keep on till BC = 0 ; shld rem ; Save result pop b pop d jmp exec$end ; page ;================================================================ ; Interrupt vector routines. ; ; These routines are used to fill in the jump table in ram ; so that an external program can tie into the interrupt system. ; The CLR routine loads a simple RETURN into ALL the vectors ; so that they are effectively turned OFF. ; ; ; On Entry to these routines, ; DE -> Driver address ; ; On Exit ; ????$hdlr is loaded with a JMP
;---------------------------------------------------------------- ; cmd$60: ; DE-install all interrupt handlers. This is handy. mvi a,(ret) sta con$hdlr sta aux$hdlr sta tmr0$hdlr sta tmr1$hdlr sta int0$hdlr sta int1$hdlr sta int2$hdlr sta csio$hdlr jmp exec$end ; cmd$61: sded con$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta con$hdlr jmp exec$end ; cmd$62: sded aux$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta aux$hdlr jmp exec$end ; ; cmd$63: sded tmr0$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta tmr0$hdlr jmp exec$end ; ; cmd$64: sded tmr1$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta tmr1$hdlr jmp exec$end ; ; cmd$65: sded int0$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta int0$hdlr jmp exec$end ; ; cmd$66: sded int1$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta int1$hdlr jmp exec$end ; ; cmd$67: sded int2$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta int2$hdlr jmp exec$end ; ; cmd$68: sded csio$hdlr+1 ; Save address mvi a,(JMP) ; Load a jump instruction sta csio$hdlr jmp exec$end ; ; ; cmd$69: jmp exec$end ; ; exec$end: ; All routines EXIT via this point ret ; ;---------------------------------------------------------------- ; Select a channel. ; ; Do this by moving the addresses of the channel routines ; into the channel addresses in ram. ; ; On Entry ; A = channel number to be used ;---------------------------------------------------------------- ; sel$chn: ora a rz cpi max$chn + 1 rnc ; push h push b push d ; sta cur$chn mov c,a mvi b,0 ; BC = offset into a table of words lxi h,ini$tbl ; -> table of initialization addresses lxi d,chn$ini ; The one to be loaded call load$chn ; Load words ; lxi h,ist$tbl lxi d,chn$ist call load$chn ; lxi h,ost$tbl lxi d,chn$ost call load$chn ; lxi h,inp$tbl lxi d,chn$inp call load$chn ; lxi h,out$tbl lxi d,chn$out call load$chn ; lxi h,iocmd$tbl lxi d,chn$cmd call load$chn mvi a,(jmp) ; Load a jump instruction sta chn$ini ; Channel Initialize sta chn$ist ; Channel Input status sta chn$ost ; Channel Output status sta chn$inp ; Channel Input sta chn$out ; Channel Output sta chn$cmd ; Channel command processor pop d pop b pop h ret ; load$chn: push b dad b dad b ; HL -> word address of routine ; HL -> address ; DE -> Channel jmp instruction inx d ; -> address lxi b,2 ; 2 bytes to move ldir ; Copy address from tyable -> channel vector pop b ret ; ;---------------------------------------------------------------- ; Tables I/O routines. ; ; Note that this table sets up the order in which the channel ; numbers work so that changing the order completely changes ; what is called. ; ; This table has been setup AS; ; Channel 1 = Console ; 2 = Aux device ; 3 = spare ; 4 = spare ; 5 = Printer ; 6 = LCD driver ; ini$tbl: ; Channel Initialize dw dev$null dw con$ini ; Console Init required dw aux$ini ; AUX init dw dev$null ; No spare init required dw dev$null ; No spare init required dw prn$ini ; No centronic printer init required dw lcd$ini ; Initialize the LCD driver ; ist$tbl: ; Channel Input status dw dev$null dw con$ist ; Console port dw aux$ist ; AUX port dw dev$null ; Spare serial 1 dw dev$null ; Spare serial 2 dw dev$null ; Printer dw dev$null ; LCD ; ost$tbl: ; Channel Output status dw dev$null dw con$ost ; Console port dw aux$ost ; AUX port dw dev$null ; Spare serial 1 dw dev$null ; Spare serial 2 dw prn$ost ; Printer ready flag dw lcd$ost ; LCD ready fla ; inp$tbl: ; Channel Input dw dev$null dw con$inp ; Console port dw aux$inp ; AUX port dw dev$null ; Spare serial 1 dw dev$null ; Spare serial 2 dw dev$null ; Printers dont recrive dw dev$null ; LCDs dont receive ; out$tbl: ; Channel Output dw dev$null dw con$out ; Console port dw aux$out ; AUX port dw dev$null ; Spare serial 1 dw dev$null ; Spare serial 2 dw prn$out ; Printer output dw lcd$out ; LCD output ; iocmd$tbl: ; Channel I/O Command processors dw dev$null dw con$cmd ; Console port dw aux$cmd ; AUX port dw dev$null ; Spare serial 1 dw dev$null ; Spare serial 2 dw dev$null ; No Printer command processor dw lcd$cmd ; LCD output command processor ; ; The null driver. Simple return/no function. ; dev$null: ret ; ;---------------------------------------------------------------- ; Push / Pop Initialize ; ; Setup push / pop counter and the next save address pointer. ; ; Push Channel ; ; Take all the channel jump addresses and save them into the ; "push" stack in ram. ; ; POP Channel ; Do the reverse. Copy the saved jumps back into the ram area. ; ;---------------------------------------------------------------- ; psh$pop$ini: xra a sta psh$cnt ; No pushes yet lxi h,psh$stk ; -> stack to push into shld psh$ptr ; Save as a push pointer ret ; ; -- Push a channel by copying to ram. -- ; psh$chn: lda psh$cnt cpi psh$max ; At max ? jrz psh$pop$err inr a sta psh$cnt ; Flags another push done ; Do the push lxi b,psh$siz ; Push size lded psh$ptr ; DESTINATION -> next free ram lxi h,channels ; SOURCE -> channel routines. ldir ; Copy channels -> ram sded psh$ptr ; Saves the new pointer jmp psh$pop$ok ; Exit with an "ok" flag. ; ; Get from stack area to the jump vectors ; pop$chn: lda psh$cnt ora a jz psh$pop$err dcr a sta psh$cnt ; One less to pop later. ; ; Move data from stack area back to the jump vectors lhld psh$ptr ; Get the source address dcx h ; -> last USED byte in the "stack" lxi d,channels + psh$siz - 1 ; -> Destination lxi b,psh$siz ; Bytes lddr ; Move DOWN inx h ; Adjust to -> next free shld psh$ptr ; Save end pointer ; psh$pop$ok: xra a ora a ret ; ; -- On an error, return a carry -- ; psh$pop$err: xra a stc ; CARRY = error ret ; ;---------------------------------------------------------------- ; Initialize I/O Channels. ; ; On Entry ; If A = 0 ; initialize all I/O channels ; ; > 0 ; only initialize the required channel. ;---------------------------------------------------------------- ; init$chan: ora a jnz chn$ini ; > 0 and initialize current channel only ; ; ELSE... ; ---- Initialize ALL the I/O Channels in the system ---- ; init$chan$all: push h push b mvi c,1 ; Starting channel mvi b,6 ; Total channels icl: call clr$wdt mov a,c ; Channel number push b call sel$chn ; Select a channel call chn$ini ; Initialize channel. pop b inr c call clr$wdt djnz icl ; pop b pop b ret ; ;================================================================ ; 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 ; ;================================================================ ; ; 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 ; ;---------------------------------------------------------------- ; 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 ? jrz ccoe$space ; If not, exit. call nibasc ; Else convert A to ascii jmp chn$out ; And channel print it. ; ccoe$space: bit 6,c rz ; Clea bit 6 = not space fill mvi a,' ' jmp chn$out ; Print a channel space ; ; ================ D A T A ================ ; dseg ; mess: db 0 ; char for crc calc rem: dw 0 ;crc remainder storage ; ?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 ; lzb$mode ds 1 ; Leading zero blanking mode ; cur$chn ds 1 ; Current output channel ; dsp$flg ds 1 ; EXEC display code flag. exe$dbg ds 1 ; EXEC debugging flag. ; cur$cmd ds 1 ; Current command usr$a ds 1 ; Saved Accumulator usr$sp ds 2 ; Saved SP usr$de ds 2 ; Saved DE usr$hl ds 2 ; Saved HL usr$bc ds 2 ; Saved BC ; channels: chn$ini ds 3 ; Channel Initialize chn$ist ds 3 ; Channel Input status chn$ost ds 3 ; Channel Output status chn$inp ds 3 ; Channel Input chn$out ds 3 ; Channel Output chn$cmd ds 3 ; Channel COMMAND Processor ; ; The following are the flags and buffer to impliment the push/pop ; facility that allows the user to push and pop channel pointers. ; psh$ptr ds 2 ; -> next free byte in "push" stack psh$cnt ds 1 ; Push counter ; psh$stk ds psh$max * psh$siz ; Channel push stack ; ; Interrupt handlers. These are either RET or JMP instructions. The ; exec routines are provided to select either. ; con$hdlr: ds 3 aux$hdlr: ds 3 tmr0$hdlr: ds 3 tmr1$hdlr: ds 3 int0$hdlr: ds 3 int1$hdlr; ds 3 int2$hdlr: ds 3 csio$hdlr: ds 3 ; ; end ; ;