title Quadart TTY Driver (Resident in the IOP) entry qdtty, QTTY1DN ext qdinit ext qd_dtr, qd_xdtr, qdsmodm, qdgmodm ext qd_cts?, qdhxstat ext ttopen, ttread, ttinrdy, ttinput, ttiflush, ttflush ext ttwrite, ttoutq, ttoutput, ttgmode, ttsmode ext iotime, iowait ext haputc, haputlast, haputpair, hacontinue ext hawait, haiowait, haabort ext hgetc, hgetpair, hwakeup, httysig, hpttysig ext getq ext qdsetup, qdshut ext qdenbrx, qddisrx ext qdressio, qdrestbe ext qdtbreak, qdbreak, qdjoin ext qdsetspeed ext addhla list nogen, nocond *maclib macros.z80 *include jsysequ.z80 *include waitdef.z80 *include iopdef.z80 *include headdef.z80 *include queuedef.z80 *include modeequ.z80 *include ttydef.z80 *include iottydef.z80 ; ASCII characters ESC equ '[' - '@' STX equ 'B' - '@' PARITY equ 7 ; parity bit ; ----------------------------------------------------------------------------- subttl Device Command Table qdtty: ; table of host commands for qdttys. ; the index into this table is in the high-order bits ; of the CMDREG byte received from the Host. dw qdinit ; interface initialization routine dw qdttopen dw qdttclose dw qdttread dw qdttwrite dw qdttgmode dw qdttsmode QTTY1DN equ 0200h ; Cromix device number for QTTY1 ; ----------------------------------------------------------------------------- subttl Device Open qdttopen: ; de = device number ; alters af, bc, de, hl, ix, iy di ld bc,qdttiint ; input interrupt routine ld hl,qdttoint ; output interrupt routine ld ix,qdttxstat ; EXT/STAT (modem) interrupt routine ld iy,qdtty ; addr of device command table call qdsetup ; set up quadart for the device jr c,opx ; error? ; no. de and iy -> the dev structure jr nz,op2 ; has this device been loaded already? ; no, then b = iop channel number call ttopen ; load default values into structure ld hl,qdttout ; addr of low-level open routine ld (iy+tt_oroutine),l ld (iy+tt_oroutine+1),h ld (iy+tt_chno),b ; store the iop channel number bit DIALUP,(iy+devno) ; is this a dial-up device (mtty)? jr z,op2 ; Yes. Discard Quadart when the device is finally closed (so it could be used ; by another type of device, i.e., by a device which uses the same Quadart ; port but has a different major device number.) ; Also, if the phone hangs up, send SIGHANGUP signal to all processes which ; use this device as their controlling TTY. ld a,(iy+tt_mode+md_mode3) ;; or a,^DISCARD|^HUPENAB|^SIGHUPALL or a,^DISCARD|^SIGHUPALL ld (iy+tt_mode+md_mode3),a op2: ; the device has been loaded into a data structure inc (iy+tt_openct) ; increment open count jr z,operrx ; too many opens? ld c,(iy+dataport) ; no. SIO data port number call qdenbrx ; enable receiver ld a,(iy+tt_chno) ; get iop channel number call haputc ; report iop channel number to the host and a,a opx: ei ret operrx: dec (iy+tt_openct) ; too many opens ld a,?devopen scf jr opx ; report the error to host wait_cts: ; wait for Clear To Send ; call with: interrupts disabled ; c = data port number ; b = minor device number ; returns: cy set if aborted ct1: ld a,b call qd_cts? ; Clear To Send? ret nc set CTSWAIT,(iy+tt_stat) ; no. wait until it is. call haiowait ; host & iop processes both wait jr nc,ct1 ; were we aborted? if not, check again ret ; yes ; ----------------------------------------------------------------------------- subttl Device Close qdttclose: ; call with: de and iy -> the device structure ; alters: af, c, e di and a,a ; clear cy dec (iy+tt_openct) ; decrement the open count jr nz,cl10 ; is it now zero? ; Yes. Wait until the output queue empties cl2: bit INT_DUE,(iy+tt_stat) ; Wait until the output queue jr z,cl4 ; & the UART buffer are both empty. set WRWAIT,(iy+tt_stat) ; Not empty yet. set WAKIOP,(iy+tt_stat2) call haiowait ; Host & iop processes both wait. jr nc,cl2 ; unless wait was aborted, check again. call xdtr? ; wait aborted. drop DTR? jr cl20 cl4: ; The wait above was not aborted. ; Put host to sleep because XDTR? can take a long time ei ld a,HmWAKIOP ; tell host to wait until awaken call hawait ; by the IOP & then to awaken the IOP di call xdtr? ; we were not aborted. drop DTR? ld a,HmWAKIOP ; wake the host up again call hwakeup ; call iowait ; suspend this iop process jr c,cl20 ; (the host will wake it up). ; cy-flag set if the wait was aborted call hacontinue ; answer host bit DISCARD,(iy+tt_mode+md_mode3); discard the device? jr z,clx cl8: call ttflush ; yes. flush the input & output queues ld a,(iy+tt_chno) ; iop channel number call qdshut ; give up the quadart clx: and a,a ei ret cl10: ; Device not finally closed yet call hacontinue ; answer host jr clx cl20: ; The wait was aborted call haabort ; answer host call ttflush ; flush the input & output queues jr cl8 xdtr?: ; Check whether drop the DTR line ; (Called only after the last close of the device) ; Call with: interrupts disabled xor a,a ld (iy+tt_stat),a ; clear the status bits ld (iy+tt_stat2),a bit HUPENAB,(iy+tt_mode+md_mode3); hang up? (device is all closed.) ret z xdtr: xor a,a ld (iy+tt_xstatus),a ; clear the EXT/STAT store ld c,(iy+dataport) ; yes. get SIO data port number ld b,(iy+devno) ; minor device number ld a,b call qd_xdtr ; reset DTR (thereby hanging phone up) di ; (QD_XDTR enables interrupts) ld a,b ld b,S_HANGUP ; if the high speed line is on, call qdsetspeed ; turn it off. jp qddisrx ; disable receiver ; ----------------------------------------------------------------------------- subttl Device Read & Write Routines qdttread: ; call with de and iy -> the device structure jp ttread qdttwrite: ; call with de and iy -> the device structure jp ttwrite ; ----------------------------------------------------------------------------- subttl Interrupt Routines qdttiint: ; input interrupt routine ; called with: de -> the device structure ; af, bc, de, hl, ix, iy saved push de pop iy ; de and iy -> the device structure ld c,(iy+dataport) in h,(c) ld a,(iy+tt_mode+md_mode3) bit FNKEYS,a jr z,iin4 ; function keys enabled? bit BINARY,a ; yes. binary mode? jr nz,iin4 ; 3102 function key decoding bit STXFLG,(iy+tt_stat) ; no. was the last char an STX? res STXFLG,(iy+tt_stat) jr nz,iin2 ; if so, acknowledge the char ld a,h res PARITY,a ; no cp a,STX ; is this char an STX? jr nz,iin4 set STXFLG,(iy+tt_stat) ; yes iin2: ld a,STX ; acknowledge receipt of the char call ttoutq iin4: ld a,h ; return the character in a-reg. jp ttinput qdttoint: ; output interrupt routine ; called with: de -> the device structure ; af, bc, de, hl, ix, iy saved push de pop iy ; de and iy -> the device structure bit INT_DUE,(iy+tt_stat) ; should interrupts be stopped for now? jp nz,ttoutput ; if not, output the next byte ld c,(iy+dataport) ; yes. reset tbe interrupt pending jp qdrestbe ; until next character is output. qdttout: ; low-level output routine ; (the address of this routine is stored at TT_OROUTINE) ; called with: de and iy -> device structure ; a = character ; c = data port number out (c),a ret qdttxstat: ; EXT/STAT (modem) interrupt routine ; called with: de -> device data structure push de pop iy ld l,(iy+tt_xstatus) ; get old value of EXT/STAT call qdhxstat ; let the Quadart handle it ld (iy+tt_xstatus),h ; save the new value of EXT/STAT ret nc ; done? jr nz,hungup ; no. are CTS & DCD both off? ; no. CTS & DCD are both on bit CTSWAIT,(iy+tt_stat) ; waiting for the CTS? ret z res CTSWAIT,(iy+tt_stat) ; yes ld a,HmWAKIOP ; wake the host up so that the host jp hwakeup ; will awaken the iop process & ; it will finish opening the device. hungup: ; the modem has hung up. send hangup signals. bit CTSWAIT,(iy+tt_stat) ; yes. waiting for CTS? ret nz ; if so, don't abort ld a,(iy+tt_openct) ; no. and a,a ; is the device open? ret z ; if not, don't abort save bc,de,hl ; no. ld c,SIGHANGUP load hl,(iy+devno) ; send hup signal to all processes bit SIGHUPALL,(iy+tt_mode+md_mode3); which use this device call nz,httysig ; as their controlling TTY? ld c,SGPHANGUP load de,(iy+tt_psighup) ; send hangup signal test de ; to a particular process? call nz,hpttysig ; if so, signal process pointed to xor a,a ; by de-register stor aa,(iy+tt_psighup) ; clear the process address restor bc,de,hl ret ; ----------------------------------------------------------------------------- subttl Device Getmode qdttgmode: ; call with: de and iy -> the device structure ; alters af, bc, hl call hgetc ; get mode parameter index from host cp a,MD_STATUS jr z,tg10 ; get the RDA status? cp a,MD_PSIGHUP ; no. get process to be signalled if jr z,tg12 ; modem hangs up? cp a,MD_MODEM ; no. get the CTS and DCD modem bits? jr z,tg14 call ttgmode ; no ret c tgx: jp haputlast ; return the parameter value to host tg10: call ttinrdy ; yes and a,a ; clear the TTINRDY cy-flag jr tgx tg12: load hl,(iy+tt_psighup) ; send addr of process table of process jr tg16 ; to be signalled if modem hangs up tg14: ld c,(iy+dataport) call qdgmodm ; Get CTS, DCD, RTS, & RI modem lines tg16: jp haputpair ; and return them to host ; ----------------------------------------------------------------------------- subttl Device Setmode qdttsmode: ; call with: de and iy -> the device structure ; alters af, bc, hl call hgetc ; get mode parameter index from host ld h,a ; save it call hacontinue ; tell host its command has been read call hgetpair ; BC = parameter value & mask ld a,h ; index cp a,MD_PSIGHUP ; set process to be signalled if jr z,sm10 ; modem hangs up? cp a,MD_PSIGHUP - 1 ; cancel signalling if modem hangs up? jr z,sm20 ld hl,smx ; put the address of the exit push hl ; on the stack cp a,MD_OSPEED + 1 ; MD_ISPEED or MD_OSPEED? jp c,setspeed cp a,MD_IFLUSH ; no. flush input buffers? jp z,ttiflush cp a,MD_FNKEYS ; no. set 3102 function keys? jr z,setfnkeys ;; cp a,MD_BREAK ; no. output break? ;; jr z,obreak ;; cp a,MD_DTR ; No. Change the DTR modem line? ;; jr z,chgdtr cp a,MD_MODEM ; No. Change any modem lines? jr z,chgmodem jp ttsmode ; Set the mode1, mode2, or mode3 bit ; & return to SMX. smx: ; exit jp nc,haputlast ; if no error, return old value ; (in A-reg) to the host cp a,?signal ; error. were we aborted? scf ret nz ; if not, the cy-flag will be set. call haabort ; yes. abort host process. and a,a ; (this is not treated as an error.) ret sm10: ; add address of process to be signalled if modem hangs up load hl,(iy+tt_psighup) ; get the stored process tbl address test hl ; is one there? call nz,cpbchl ; if so, same as the user's value? ret c sm12: stor bc,(iy+tt_psighup) ; same. store this one there call haputpair ; return the old number ret sm20: ; cancel the signalling of the user's process when modem hangs up load hl,(iy+tt_psighup) ; get the stored process tbl address test hl ; is it already cancelled? call nz,cpbchl ; if not, same as the user's value? ret c ld bc,0 ; yes. zero the stored value. jr sm12 cpbchl: and a,a sbc hl,bc ; same as the new signal device number? load hl,bc ret z ld a,?badvalue ; no. error scf ret ; ----------------------------------------------------------------------------- subttl Setmode: function keys setfnkeys: ; b = 0 to turn fn keys off, b <> 0 to turn them on ; de and iy -> tty structure di ld a,ESC call ttoutq ld a,'.' call ttoutq ld a,b and a,a ; turn function keys on? ld a,'9' ; (an '9' turns them on) ld h,(iy+tt_mode+md_mode3) ld l,h set FNKEYS,l jr nz,of2 dec a ; no, off (send an '8') res FNKEYS,l of2: call ttoutq ld (iy+tt_mode+md_mode3),l ; update ei ld a,h ; report the original state of the and a,^FNKEYS ; function keys ret z ; were they on? dec a ; yes. a = -1 ret ; ----------------------------------------------------------------------------- subttl Setmode: Change modem lines chgmodem: ; Change modem lines ; DE and IY -> TTY data structure ; C = mask for which bits are to be changed ; B = mask for the direction these bits are to be changed ; Returns: A = previous state di call qdsmodm ei ret ; ----------------------------------------------------------------------------- subttl Setmode: baudrate setspeed: ld c,(iy+dataport) ld h,(iy+tt_mode+md_ispeed); save the old Cromix speed code ld a,b ; get the new Cromix speed code cp a,S_HANGUP ; hang phone up when device is closed? jr z,sshangup di call qd_dtr ; no. turn on Data Terminal Ready ei ld a,b cp a,S_CTSWAIT ; wait for Clear To Send? jr z,ssctswait ;; cp a,h ; is new speed code same as the old? ;; call nz,ttflush ; if not, flush all buffers and a,a bit DIALUP,(iy+devno) ; dial-up modem? call nz,ssctswait ; if so, wait for CTS jr c,ssx2 ; aborted? ld a,b ; no bit Sfl_AUTO,a ; set baudrate automatically? jr nz,ss2 cp a,S_NOCHG ; no. is the new code NOCHG jr nz,ss4 ; (no-change)? ld a,h ; yes. has the baudrate ever been cp a,S_UNINIT ; initialized? (if not, treat it as jr nz,ssx ; (AUTOBAUD rather than NOCHG.) ss2: ; set the baudrate automatically ld (iy+tt_mode+md_ispeed),^Sfl_AUTO; zero the speed byte push hl ; save the old speed code call autobaud ; yes pop hl jr nc,ssx ; were we aborted? ld a,?signal ; yes jr ssx2 ss4: ; set a specific baudrate ld a,(iy+devno) ; minor device number call qdsetspeed ; do change it jr c,sserrx ; error? call ttflush ; no. flush all buffers ssx: ld a,h ; return old speed code ld (iy+tt_mode+md_ispeed),b; store new speed code and a,a ssx2: ei ret sshangup: ; hang phone up set HUPENAB,(iy+tt_mode+md_mode3) jr ssx ssctswait: ; wait for Clear To Send push bc di ld b,(iy+devno) call wait_cts ; wait for CTS ld a,h ; return old speed code if not aborted jr nc,ctswx ; were we aborted? call xdtr ; yes. drop DTR ld a,?signal scf ctswx: pop bc ei ret sserrx: ; unknown speed code value ld a,?badvalue scf ei ret ; ----------------------------------------------------------------------------- subttl Setmode: automatic baudrate autobaud:; call with: de and iy -> the tty structure ; returns with: b = the new speed code ; receiver enabled ; This routine enables interrupts, but returns with them disabled. ; It will set the baudrate of terminals interfaced ; to serial Quadarts. It will automatically ; set the baudrate on the 3102. It will set the baudrate ; on 3100s and 3101s if the RETURN key is pressed several times. ; alters: af, bc, hl ei ld a,HmWAKIOP ; tell host to wait until awaken call hawait ; by the iop & then to awaken di ; the iop itself ld a,(iy+tt_mode+md_mode1) ; save the ECHO state push af ; res ECHO,(iy+tt_mode+md_mode1) ab12: ld c,(iy+dataport) ; SIO data port number di ld a,(iy+devno) ; minor device number ld b,S_19200 ; start with 19200 baud call qdsetspeed ld b,02 ; send a 0.2 second break call qdtbreak ; to the 3102 terminal. jr c,ababrt1 ; were we aborted? ld b,10 ; no. number of retries ; before re-sending BREAK ab13: push bc ; c = command port number ld b,S_19200 ; Cromix speed code for 19200 baud ab14: ; next baudrate ld a,(iy+devno) ; minor device number call qdsetspeed jr c,ab16 ; illegal speed code? call ttflush ; no. flush all buffers call timedchar ; get character jr nc,ab15a ; got one? and a,a ; no. timed-out? jr z,ab18 ; if so, start at top baudrate again. jr ababrt0 ; we were aborted. ab15a: call timedchar ; get another character jr nc,ab15b ; got one? and a,a ; no. timed-out? jr z,ab18 ; if so, start at top baudrate again. jr ababrt0 ; we were aborted. ab15b: cp a,'\r' ; CR? jr z,abx cp a,'\n' ; NL? jr z,abx ab16: djnz ab14 ; done (Cromix speed code = 0)? ab18: pop bc ; yes. time for another BREAK? djnz ab13 ; if not, start at top baudrate again jp ab12 ; yes abx: ; Got the NL. The baudrate is set. pop af ; clean stack pop af ; restore the ECHO state again ld (iy+tt_mode+md_mode1),a ld a,HmWAKIOP ; wake the host up call hwakeup ; call iowait ; suspend this iop process ; (the host will wake it up). ret c ; was the wait aborted? set Sfl_AUTO,b ; no. return speed code for storage ret ; in the iop device structure. ababrt0: pop af ; clean stack ababrt1:pop af ; restore the mode state again ld (iy+tt_mode+md_mode1),a scf ; we were aborted. ret ; ----------------------------------------------------------------------------- subttl Setmode: timed character for autobaud ;* timedchar get a character with a 1-second time-out ; call with: interrupts disabled ; de and iy -> tty ; this routine enables interrupts (IOTIME), but returns with them disabled. ; returns with: cy set & a = 0 if timed-out without getting character ; cy set & a = -1 if aborted ; o/w, returns character in a timedchar: push bc push hl ld c,5 ; multiplier to yield 1 second tch2: call ttinrdy ; is there a char in either queue? jr nz,tch8 xor a,a ; (not aborted) dec c ; has 1 second elapsed yet? scf jr z,tchx ld a,2 ; no. suspend this iop process call iotime ; for 0.2 seconds & then try again jr nc,tch2 ; were we aborted? ld a,-1 ; yes jr tchx tch8: ld hl,tt_inpq jr nc,tch9 ; is the char in the input queue? ld hl,tt_tahq ; no, the type-ahead queue. tch9: add hl,de call getq ; get the character from the queue and a,not ^PARITY ; reset parity & clear cy tchx: pop hl pop bc ret ; =============================================================================