list off, nocond, nogen IOPVERSION equ -1 IF IOPVERSION title TTY: TTY Driver (Resident in IOP) ELSE title TTY: TTY Driver ENDIF list on ; Note about the usage of the Z80 registers: ; ; The address of the TTY structure being accessed is loaded into ; the DE and the IY registers when the drivers are first called by ; the operating system. These are preserved throughout the drivers. ; ; All registers except possibly AF are preserved by most subroutines. ; The exceptions are the high level subroutines (those close to the operating ; system). The subroutines which do alter a register other than AF ; are clearly marked. entry ttopen, ttinrdy, ttread, ttinput, ttiflush, ttflush entry ttwrite, ttoutq, ttoutput, ttgmode, ttsmode ext getmode, setmode ext getq, putq, unputq IF IOPVERSION ext haputc, haputlast, haputnot, hacontinue, hawait ext hgetc ext httysig ext iofork, ioforkadr, iotime ext qdmsktbe, qdunmtbe ELSE ext hwait ext ioputc, iogetc ext sgttysig ext tumsktbe, tuunmtbe ENDIF ext hwakeup ext addhla, hladdr, lowercase list nocond, nogen *maclib macros.z80 *include jsysequ.z80 *include waitdef.z80 *include headdef.z80 *include queuedef.z80 *include modeequ.z80 *include ttydef.z80 ; ----------------------------------------------------------------------------- subttl Miscellaneous Definitions ; constants TTOHIGH equ 60 ; output queue high-water mark and TTOLOW equ 24 ; low-water mark TTIHIGH equ 132 ; type-ahead queue high-water mark and TTILOW equ 24 ; low-water mark (TANDEM mode) PARITY equ 7 ; parity bit ; ASCII characters BELL equ 'G'-'@' CNTRL_C equ 'C'-'@' X_OFF equ 'S'-'@' ; CNTRL_S X_ON equ 'Q'-'@' ; CNTRL_Q CNTRL_U equ 'U'-'@' EOFILE equ 'Z'-'@' ESC equ '['-'@' DEL equ 07FH ; ----------------------------------------------------------------------------- subttl Open Routine ttopen: ; called when the device's data structure is first loaded ; call with: interrupts disabled ; de and iy -> device structure ; the structure having been zeroed ; alters: af ; load the non-zero default values ; ld (iy+tt_openct),0 ; open count ld (iy+tt_mode+md_ispeed),S_UNINIT; the baudrate is uninitialized ld (iy+tt_mode+md_mode1),^ECHO|^CRDEVICE ; ld (iy+tt_mode+md_moded),0 ld (iy+tt_mode+md_mode2),^PAUSE|^ABENABLE ; ld (iy+tt_mode+md_mode3),0 ; auxilliary erasure character (in addition to DEL and BS) ; ld (iy+tt_mode+md_erase),0 ; erasure echo character (if it is 'R', the echo is a Rubout: BS-SPACE-BS) ld (iy+tt_mode+md_delecho),'R' ld (iy+tt_mode+md_lkill),CNTRL_U ; line kill character PGLENGTH equ 24 ld (iy+tt_mode+md_length),PGLENGTH ; page length ld (iy+tt_palnct),PGLENGTH ; init pause line count ld (iy+tt_fflnct),PGLENGTH ; init formfeed line count ; page width (0 is equivalent to an infinite width) ; ld (iy+tt_mode+md_width),0 ret ; ----------------------------------------------------------------------------- subttl Read Routine ttread: ; de and iy -> tty structure ; alters af, b, hl rd0: di bit RDWAIT,(iy+tt_stat) ; if another process is already waiting jr nz,rdwait_ ; for input, then join it in sleep. ld a,(iy+tt_inpq+q_ct) ; no process is waiting. and a,a ; is the input queue empty? jr nz,rd6 ; yes. load the input queue from the type-ahead queue. ; if the input queue empties before a delimiter is found, then ; go to sleep. in that case the input interrupt routine TTINPUT ; will finish loading the input queue & then awaken this process. ld hl,tt_tahq add hl,de ; hl -> type-ahead queue rd4: call getq ; get char from the type-ahead queue jr c,rdwait_ ; was the type-ahead queue empty? ld b,a ; save the character bit BINARY,(iy+tt_mode+md_mode3); no. BINARY mode? jr nz,rd5 bit INPSTOP,(iy+tt_stat2) ; no. was input stopped (TANDEM mode)? call nz,ckinlow ; if so, send start character? rd5: set RDXFER,(iy+tt_stat2) ; inhibit echoing characters typed ei ; while this transfer is taking place. ld a,b call pputinpq ; process & put the character into di ; the input queue. res RDXFER,(iy+tt_stat2) jr nz,rd4 ; delimiter? IF IOPVERSION rd6: ; the input queue is ready to be read. ; deliver a byte from the input queue to the process doing the read. ei ld hl,tt_inpq ; yes add hl,de ; get character from the input queue call getq ld b,a ; save it ; check for EOF bit RAW,(iy+tt_mode+md_mode1); raw mode? jr nz,rd7 ld a,(iy+tt_mode+md_mode3) ; no and a,^BINARY|^CBREAK ; binary or character-break mode? jr nz,rd7 ld a,b ; no res PARITY,a cp a,EOFILE ; is the character an EOF character? jr z,rdeof ; if so, treat it as an EOF rd7: ld a,(iy+tt_inpq+q_ct) ; no. is this the last character? and a,a ld a,b jr z,rdlast call haputc ; no. deliver it and a,a ret rdeof: call haputnot ; report end of file to host and a,a ret rdlast: call haputlast ; deliver last character to host and a,a ret rdwait_:set RDWAIT,(iy+tt_stat) ld a,HmRDWAIT ; tell host to wait until notified ei ; call hawait ; that input is ready and a,a ret ELSE rd6: ; the input queue is ready to be read. ; deliver input queue contents to the process doing the read. ld hl,tt_inpq ; yes add hl,de ; hl -> input queue rd8: call getq ; get character ei ; (be sure interrupts are enabled) jr c,rdx ; got one? ld b,a ; yes. save it ; check for EOF bit RAW,(iy+tt_mode+md_mode1); raw mode? jr nz,rd9 ld a,(iy+tt_mode+md_mode3) and a,^BINARY|^CBREAK ; binary or character-break mode? ld a,b jr nz,rd9 res PARITY,a ; no cp a,EOFILE ; is the character an EOF character? jr z,rdx ; if so, treat it as an EOF ld a,b rd9: call ioputc ; no. deliver it to the Cromix caller jr nc,rd8 ; done? rdx: and a,a ; yes ret rdwait_:set RDWAIT,(iy+tt_stat) ld a,HmRDWAIT ; tell host to wait until notified call hwait ; that input is ready ei ; & then check to make sure another jr rd0 ; process didn't empty the buffer. ENDIF ckinlow: ; check whether to send start character (TANDEM mode) ; alters: af ld a,(iy+tt_tahq+q_ct) ; yes cp a,TTILOW jr z,il2 ; is the queue at the low mark? and a,a ; no. is it empty? ret nz il2: res INPSTOP,(iy+tt_stat2) ; empty set PUTSTART,(iy+tt_stat2) ; ready for more input, so output jp ckoutput ; a start character ; ----------------------------------------------------------------------------- subttl Process and Put a Character into the Input Queue pputinpq: ; process a character and, if appropriate, put it ; into the input queue. ; call with: de and iy -> tty structure ; a = the character ; returns: z-flag set if delimiter ; or RAW, BINARY, or CBREAK mode push bc push hl ld b,a ; save the character ld hl,tt_inpq add hl,de ; hl -> input queue structure bit BINARY,(iy+tt_mode+md_mode3) jr nz,ppi6 ; binary mode? res PARITY,a ; no bit RAW,(iy+tt_mode+md_mode1) jr nz,ppi2 ; raw mode? bit CBREAK,(iy+tt_mode+md_mode3) jr nz,ppi2 ; no. character-break mode? cp a,DEL jr z,ppidel ; DEL? cp a,'\b' jr z,ppidel ; backspace? cp a,(iy+tt_mode+md_erase) jr z,ppidel ; auxilliary erase character? cp a,(iy+tt_mode+md_lkill) jr z,ppikill ; line kill? ppi2: cp a,'\r' ; CR? jr nz,ppi4 bit CRDEVICE,(iy+tt_mode+md_mode1) ; yes. CR device? jr z,ppi4 ld b,'\n' ; yes. change the CR to a NL ppi4: ld a,b bit LCASE,(iy+tt_mode+md_mode1) call nz,lowercase ; convert to lower case? ppi6: call putq ; put the char into input queue jr c,ppifull ; queue full? call echo_c ; no. echo the character bit RAW,(iy+tt_mode+md_mode1) jr nz,ppidelim ; raw mode? ld a,(iy+tt_mode+md_mode3) ; no and a,^BINARY|^CBREAK ; binary or character-break mode? jr nz,ppidelim ld a,b ; no res PARITY,a cp a,'\n' ; no. newline? jr z,ppidelim cp a,EOFILE ; no. EOF character? jr z,ppidelim cp a,ESC jr z,ppiesc ppinodelim: ; not a delimiter xor a,a inc a ; reset z-flag ppix: pop hl pop bc ret ppiesc: ; ESCape character bit ESCRETN,(iy+tt_mode+md_mode3) jr z,ppinodelim ; it's an ESC. return the line? ; yes ppidelim: ; delimiter. return the line cp a,a ; set z-flag jr ppix ppidel: ; delete the last character put into the input queue call unputq ; take last char from input queue jr c,ppiwarn ; queue empty? set DELETION,(iy+tt_stat) ; no call echo_c res DELETION,(iy+tt_stat) jr ppinodelim ppikill: ; line kill. ; delete the current line being put into the input queue. ld bc,0 ; initialize character counter jr ppik3 ppik2: inc bc res PARITY,a cp a,' ' ; was it a control character? jr nc,ppik3 inc bc ; yes. give it an extra count ppik3: call unputq ; take last char from the input queue jr nc,ppik2 ; done? ld a,c ; yes or a,b ; were any characters removed? jr z,ppikx ld a,(iy+tt_mode+md_delecho); yes. 'R' for Rubout? cp a,'R' jr z,ppik4 ld a,(iy+tt_mode+md_lkill) ; no. echo the linekill character call echo_c ; ld a,'\n' ; and a newline jr ppik6 ppik4: ld (iy+tt_klct),c ; store the number of chars removed ld (iy+tt_klct+1),b ; ; yes. a Rubout is a sequence of ld (iy+tt_klseq),RUBSEQLN ; 3 characters (BS-SPACE-BS). xor a,a ; output a NULL to insure that ppik6: call echooutq ; it all starts ppikx: jr ppinodelim ppifull: ; the input queue is full ;; call ttiflush ; flush input & type-ahead queues set INPOVFL,(iy+tt_stat2) ; overflow flag call warn ; ring bell to warn user jr ppidelim ; return the queue's contents to user ppiwarn: ; error: ring the bell to warn the user call warn jr ppinodelim ; ----------------------------------------------------------------------------- subttl Input Interrupt ttinput: ; put a character into the input queue or the type-ahead queue. ; (called by input interrupt service only.) ; called with: de and iy -> tty structure ; a = character ; alters: af, bc, hl (the interrupt handler saves them) ld b,a ; save character bit BINARY,(iy+tt_mode+md_mode3) jr nz,ti6 ; if binary mode, skip checks ; check parity ld a,(iy+tt_mode+md_mode1) ; get parity mode bits and a,^ODD|^EVEN ; if both reset or both set, ld c,a ; don't check parity ld a,b jr z,ti1a ; if neither set, reset char parity bit jp pe,ti1b ; if both set, don't change char parity and a,a ; is char parity even? jp pe,ti0 bit ODD,c ; odd parity ok? jr nz,ti1a jp tipari ; no. ring bell ti0: bit EVEN,c ; even parity ok? jp z,tipari ; if not, ring bell ti1a: res PARITY,b ti1b: res PARITY,a bit RAW,(iy+tt_mode+md_mode1) jr nz,ti2 ; a-little-raw mode? ; check for X-OFF and X-ON cp a,X_OFF ; output-stop character? jr z,stopout cp a,X_ON ; output-start character? jr z,restartout ti2: cp a,CNTRL_C ; abort character? jr nz,ti4 bit ABENABLE,(iy+tt_mode+md_mode2) ; yes. aborts enabled? jp nz,tiabort ti4: cp a,(iy+tt_mode+md_usignal); is this a user-signal char? jr nz,ti6 bit SGENABLE,(iy+tt_mode+md_mode2); yes. user-signal key enabled? jr z,ti6 call tiusrsig ; yes. send SIGUSER signal bit SIGALLC,(iy+tt_mode+md_mode2); if SIGALLC also enabled, put ret z ; char into input stream ti6: ld a,b ; retrieve character bit RDWAIT,(iy+tt_stat) ; waiting for input? jr z,ti8 ld h,(iy+tt_mode+md_length); yes. re-init the pause line counter ld (iy+tt_palnct),h call pputinpq ; prepare & put char into input queue ret nz ; delimiter or BINARY, CBREAK, or RAW? res RDWAIT,(iy+tt_stat) ; yes, one or the other. ld a,HmRDWAIT ; input is ready, jp hwakeup ; wake the host up ti8: ld hl,tt_tahq ; not waiting for input, add hl,de ; so put the character call putq ; into the type-ahead qweue. jr c,tifull ; queue full? bit BINARY,(iy+tt_mode+md_mode3); no. BINARY mode? ret nz ld a,(iy+tt_mode+md_mode2) ; no. send SIGUSER signal for and a,^SIGALLC|^SGENABLE ; each character typed-ahead if cp a,^SIGALLC ; SIGALLC but not SGENABLE call z,tiusrsig bit TANDEM,(iy+tt_mode+md_mode1); tandem mode? call nz,ckinhigh ; if so, send stop character? bit NOTIMMECHO,(iy+tt_mode+md_mode2); skip echoing characters ret nz ; typed ahead? bit RDXFER,(iy+tt_stat2) ; if transfer between type-ahead & in- ret nz ; put queues is taking place,don't echo ld a,b res PARITY,a cp a,'\r' ; no. CR? jr nz,ti10 bit CRDEVICE,(iy+tt_mode+md_mode1) ; yes. CR device? jr z,ti10 ld a,'\n' ; yes. change the CR into a NL ti10: jr echo_c ; echo character & return stopout: ; stop output interrupts set OUTPSTOP,(iy+tt_stat) ret restartout: ; restart output interrupts ; (also called by TTSMODE) ; call with: interrupts disabled ld a,(iy+tt_stat) and a,^WRITSTOP|^OUTPSTOP ; writing stopped (screen pause) ret z ; or output interrupts stopped? res WRITSTOP,(iy+tt_stat) ; yes, one or the other res OUTPSTOP,(iy+tt_stat) IF IOPVERSION bit WRITSTOP,a ; was it screen pause? call nz,ppo$$nl ; if so, output a newline ENDIF ld a,HmSCWAIT call hwakeup ; wake host up (in case screen paused) jp ckoutput ; check the output queue tiusrsig: ; either the user-signal key was pushed ; or SIGALLC mode is selected ld c,SIGUSER jr tisignal tiabort: ; the CNTRL-C key was pushed call restartout ; restart output in case it was stopped call ttiflush ; flush the input queues ld c,SIGABORT tisignal: ld l,(iy+devno) ld h,(iy+devno+1) ; hl = device number IF IOPVERSION jp httysig ; report signal to the host & return ELSE jp sgttysig ; report the signal & return ENDIF ckinhigh: ; check whether to send stop character (TANDEM mode) ; alters: af ld a,(iy+tt_tahq+q_ct) ; get type-ahead character count cp a,TTIHIGH ; too full? ret c bit INPSTOP,(iy+tt_stat2) ; yes. has input already been stopped? ret nz set INPSTOP,(iy+tt_stat2) ; no set PUTSTOP,(iy+tt_stat2) ; output a stop character jp ckoutput ; tifull: ; type-ahead queue full tipari: ; parity error warn: ; warn user by ringing bell unless -ECHO, TANDEM, RAW, or BINARY mode ld a,(iy+tt_mode+md_mode1) xor a,^ECHO and a,^ECHO|^TANDEM|^RAW ret nz ; -ECHO, TANDEM, or RAW mode? bit BINARY,(iy+tt_mode+md_mode3) ret nz ; no. BINARY mode? ld a,BELL ; no. ring bell jr echooutq ; ----------------------------------------------------------------------------- subttl Character Echo echo_c: ; call with: de and iy -> tty structure ; a = character bit ECHO,(iy+tt_mode+md_mode1) ; echo mode? ret z bit BINARY,(iy+tt_mode+md_mode3) ; yes. binary mode? ret nz ; a disconnected TUART inputs a stream of NULLs. to avoid filling the output ; queue with garbage, don't ECHO NULLs. (but, of course, NULLs can be OUTPUT.) and a,not[^PARITY] ; strip parity bit ret z ; NULL? cp a,'\n' ; no. newline? jr z,echonl cp a,'\r' ; no. CR? jr z,echooutq ; no. if it is DEL, BS, or the auxilliary erase character, ; echo as an erasure (for the benefit of type-ahead). cp a,DEL ; DEL? jr z,echoeras cp a,'\b' ; no. backspace? jr z,echoeras cp a,(iy+tt_mode+md_erase) ; no. auxilliary erase character? jr z,echoeras cp a,' ' ; no. control character? jr nc,putecho push af ; yes ld a,'^' call putecho pop af add a,'@' putecho: bit DELETION,(iy+tt_stat) ; deletion? jr z,echooutq echoeras: ; deletion ld a,(iy+tt_mode+md_delecho); yes. get erase echo character cp a,'R' ; Rubout? jr nz,echooutq call ec6 ; yes. output backspc-space-backspc ld a,' ' call echooutq ec6: ld a,'\b' echooutq: ; process & put an echo character into output queue set ECHOING,(iy+tt_stat2) ; set echo flag call pputoutq ; process & put char into output queue res ECHOING,(iy+tt_stat2) ; the echoing is finished ret echonl: ; echo a newline bit NOECNL,(iy+tt_mode+md_mode2) ret nz ; echo NLs? ld a,'\r' ; yes call echooutq ld a,'\n' jr putecho ; ----------------------------------------------------------------------------- subttl Input-Ready Test ttinrdy: ; call with: de and iy -> tty structure ; returns: a = 0 if there is no character, and ; a = 1 if there is a character in either the ; input queue or the type-ahead queue. ; also returns: z-flag reset if there is a character in ; either of the queues, and ; cy-flag also reset if there is ; a character in the input queue. ld a,(iy+tt_inpq+q_ct) and a,a jr nz,cix or a,(iy+tt_tahq+q_ct) ret z scf cix: ld a,1 ret ; ----------------------------------------------------------------------------- subttl Write Routine ttwrite: ; de and iy -> tty structure ; alters af, h IF IOPVERSION call hgetc ; get character from system ld h,a ; save it di ld a,(iy+tt_stat) and a,^WRITSTOP|^OUTPSTOP ; writing stopped or outp interrupts jr nz,wr20 ; stopped? ld a,(iy+tt_outq+q_ct) ; neither cp a,TTOHIGH ; too many characters jr nc,wr10 ; in the output queue? ei call hacontinue ; tell host the char has been accepted ld a,h ; for output & iop ready for next cmd. call pputoutq ; put char into the queue and a,a ; clear cy ret wr10: ; output queue is too full set WRWAIT,(iy+tt_stat) ; tell host to wait (sleep) until ld a,HmWRWAIT ; notified that the output queue wr12: ei ; call hawait ; has drained sufficiently and a,a ; & then to send the character again. ret wr20: ld a,HmSCWAIT ; tell host to wait until notified jr wr12 ; that a start char has been input ; & then to send the character again ELSE wr2: call iogetc ; get character from system ccf ret nc ; got one? ld h,a ; yes. save it wr3: di ld a,(iy+tt_stat) and a,^WRITSTOP|^OUTPSTOP ; writing stopped or outp interrupts jr nz,wr20 ; stopped? ld a,(iy+tt_outq+q_ct) ; no. get character count cp a,TTOHIGH jr c,wr8 ; too many chars in the queue? set WRWAIT,(iy+tt_stat) ld a,HmWRWAIT ; yes. wait until it has drained wr6: call hwait ; sufficiently jr wr3 ; when awakened, check again wr8: ei ld a,h ; retrieve character call pputoutq jr wr2 wr20: ld a,HmSCWAIT ; wait until notified jr wr6 ; that a start char has been input ENDIF ; ----------------------------------------------------------------------------- subttl Process and Put a Character into the Output Queue pputoutq: ; process character and put it into the output queue. ; this routine sometimes puts several characters into ; the output queue when it is called. e.g., when tabs ; are expanded as spaces. ; call with: de and iy -> tty structure ; a = character cp a,' ' jr c,ppo6 ; control character? ; no. if BINARY or RAW mode or if column count <= width - 1, (or if width = 0), ; put the character into the output queue. (otherwise, discard it). bit BINARY,(iy+tt_mode+md_mode3) jr nz,ppo2 ; binary mode? bit RAW,(iy+tt_mode+md_mode1) jr nz,ppo2 ; no. raw mode? push af ; no ld a,(iy+tt_mode+md_width) dec a cp a,(iy+tt_colct) ; is the screen width exceeded? call c,ppowrap ; if so, check whether to wrap-around. jr c,ppo4 ; if cy-flag, discard character ppo1: pop af ppo2: inc (iy+tt_colct) ; no jp ttoutq ppo4: bit ECHOING,(iy+tt_stat2) ; yes. but if this is an echo, jr nz,ppo1 ; don't truncate. pop af ; throw the character away. (truncate.) ret ppo6: ; it's a control character IF IOPVERSION and a,a ; NULL? jr nz,ppo6a bit BINARY,(iy+tt_mode+md_mode3) ; yes. binary mode? ; if not, place a pair of NULLS into output queue. ; when the output interrupt finds a pair of NULLS, it outputs a single NULL. jp z,pponull ppo6a: ENDIF cp a,'\n' ; newline? jp z,pponl cp a,'\t' ; tab? jr z,ppotab cp a,'\r' ; CR? jp z,ppocr cp a,'\f' ; formfeed? jp z,ppoff cp a,'\b' ; backspace? jp nz,ttoutq ; if not, put the char into the queue ; it's a backspace ('\b') ld a,(iy+tt_colct) and a,a ; column 0? jr nz,ppo8 ld a,(iy+tt_palnct) ; yes. backup to the previous line cp a,(iy+tt_mode+md_length) jr c,ppo7 xor a,a ppo7: inc a ld (iy+tt_palnct),a ld a,(iy+tt_mode+md_width) ppo8: dec a ld (iy+tt_colct),a ld a,'\b' call ttoutq ld a,(iy+tt_mode+md_moded) ; get the delay code and a,BSDELAY ; extract the BS delay (in bit 7) IF IOPVERSION rlca ; shift to bit 0 jp delay ; (0, 0.1 seconds delay) ELSE rlca ; shift to bit 2 rlca rlca jp delay ; (0, 4 nulls) ENDIF ppotab: ; it's a TAB ld a,(iy+tt_colct) add a,8 and a,~7 dec a push bc ld b,a ld a,(iy+tt_mode+md_width) dec a cp a,b ; would the page width be ld a,b ; exceeded? pop bc bit BINARY,(iy+tt_mode+md_mode3) jr nz,ppot2 ; binary mode? jr c,ppowrap ; no. if width exceeded, check wrap bit XTAB,(iy+tt_mode+md_mode1) ; width ok. expand TABs? jr nz,ppot4 ppot2: inc a ; no. output a TAB character ld (iy+tt_colct),a ld a,'\t' call ttoutq ld a,(iy+tt_mode+md_moded) ; get the delay code and a,TABDELAY ; extract the TAB delay (in bits 2 & 3) IF IOPVERSION rrca ; shift to bits 0 & 1 rrca jp delay ; (0, 0.1, 0.2, 0.3 seconds delay) ELSE jp delay ; (0, 4, 8, 12 nulls) ENDIF ppot4: ; expand the TAB by outputting SPACEs ld a,' ' call pputoutq ld a,(iy+tt_colct) and a,7 jr nz,ppot4 ret ppowrap: bit WRAP,(iy+tt_mode+md_mode2); wrap around to the next line? scf ; if not, set cy-flag so that character ret z ; will be discarded call pponl ; yes and a,a ; do not discard character ret pponl: ; send a newline call ppo$nl ; issue a newline ret z ; if z-flag, done bit BINARY,(iy+tt_mode+md_mode3); binary mode? ret nz ; if so, done ld a,(iy+tt_mode+md_bmarg) ; no. into the bottom margin? cp a,(iy+tt_fflnct) ret c ; if not, done ppoff: ; send a formfeed bit BINARY,(iy+tt_mode+md_mode3); binary mode? jr nz,ppof2 bit XFF,(iy+tt_mode+md_mode2) ; no. expand formfeed? jr nz,ppoxff ppof2: ld a,'\f' ; no call ttoutq ld a,(iy+tt_mode+md_moded) ; get the delay code and a,FFDELAY ; extract the FF delay (in bit 6) IF IOPVERSION rrca ; shift to bit 3 rrca rrca call delay ; (0, 0.8 seconds delay) ELSE rlca ; shift to bit 7 call delay ; (0, 128 nulls) ENDIF ld (iy+tt_colct),0 jr ppotop ppoxff: ; expand formfeed ppof8: call ppo$nl ; output NLs until top of page jr nz,ppof8 ; is reached ret ppo$nl: ; if bottom margin > 0, returns z-flag set if at top of page dec (iy+tt_palnct) jr nz,ppo$$nl ; is the pause line count zero? ld a,(iy+tt_mode+md_length); yes ld (iy+tt_palnct),a ; reload pause line counter ; if not-PAUSE mode, or BINARY or RAW mode, don't pause. ; (pauses allowed with CBREAK mode) bit PAUSE,(iy+tt_mode+md_mode2) jr z,ppo$$nl bit BINARY,(iy+tt_mode+md_mode3) jr nz,ppo$$nl bit RAW,(iy+tt_mode+md_mode1) jr nz,ppo$$nl ; if bottom margin > 0, don't pause ld a,(iy+tt_mode+md_bmarg) and a,a jr nz,ppo$$nl bit ECHOING,(iy+tt_stat2) ; are we echoing a character? jr z,ppon2 ld (iy+tt_palnct),1 ; yes. don't pause, but don't run jr ppo$$nl ; through a whole page either. ppon2: set WRITSTOP,(iy+tt_stat) ; no. no more writing until the start call ppocr ; char is input IF IOPVERSION jp warn ; ring bell. since bottom margin = 0, ; don't worry about the z-flag ELSE call warn ; ring bell ld a,HmSCWAIT ; wait until the start char is input call hwait ; and then output a newline ENDIF ppo$$nl:; returns: z-flag set if at top of page bit BINARY,(iy+tt_mode+md_mode3) ; binary mode? jr nz,ppon8 bit CRDEVICE,(iy+tt_mode+md_mode1) ; no. CR device? call nz,ppocr ; if so, output a CR first. ppon8: ld a,'\n' ; put a newline into output queue call ttoutq ld a,(iy+tt_mode+md_moded) ; get the delay code and a,NLDELAY ; extract the NL delay (in bits 0 & 1) IF IOPVERSION call delay ; (0, 0.1, 0.2, 0.3 seconds delay) ELSE rlca ; shift to bits 2 & 3 rlca call delay ; (0, 4, 8, 12 nulls) ENDIF ld (iy+tt_colct),0 ; column-0 dec (iy+tt_fflnct) ; is the ff line count zero? ret nz ppotop: ld a,(iy+tt_mode+md_length) ; yes. top of page ld (iy+tt_fflnct),a ; reload ff line counter ret ppocr: ; send a CR ld (iy+tt_colct),0 ; reload the column counter ld a,'\r' call ttoutq ; send a CR ld a,(iy+tt_mode+md_moded) ; get the delay code and a,CRDELAY ; extract the CR delay (in bits 4 & 5) IF IOPVERSION rrca ; shift to bits 0 & 1 rrca rrca rrca ; (0, 0.1, 0.2, 0.3 seconds delay) ELSE rrca ; shift to bits 2 & 3 rrca ; (0, 4, 8, 12 nulls) ENDIF ; (continue below) ; ----------------------------------------------------------------------------- IF IOPVERSION delay: ; put a NULL followed by a byte specifying the number ; of tenths of seconds into the output queue ; call with: de and iy -> tty structure ; a = number of tenth-seconds to delay and a,a ; delay = 0? ret z bit BINARY,(iy+tt_mode+md_mode3) ; no. ret nz ; if binary mode, don't delay pponull: ; if it's a NULL, enter here to output a pair of NULLs push hl ld h,a ; save the byte ld a,i ; get interrupt state ld a,h pop hl push af ; save the byte & interrupt state. di ; while interrupts are disabled, xor a,a ; stuff a NULL into the output queue call outq ; & then put the byte (the number of pop af ; tenth-seconds) push af ; into the output queue call ttoutq ; in the usual way. pop af ; restore interrupt state. ret po ei ret ELSE delay: ; delay by putting NULLs in the output queue ; call with: de and iy -> tty structure ; a = number of nulls to output and a,a ; no NULLs? ret z bit BINARY,(iy+tt_mode+md_mode3) ; no, some NULLs ret nz ; if binary mode, don't do it bit ECHOING,(iy+tt_stat2) ; are we echoing a character? ret nz ; if so, don't output the NULLs push bc ld b,a nl2: xor a,a ; a = NULL call ttoutq djnz nl2 pop bc ret ENDIF ; ----------------------------------------------------------------------------- subttl Put a Character into the Output Queue ttoutq: ; put a character into the output queue ; call with: de and iy -> tty structure ; a = character call outq ; put char into output queue bit INT_DUE,(iy+tt_stat) ; is an interrupt expected? ret nz ; if so, done call ckoutput ; no. check the output queue and a,a ret outq: ; stuff character into the output queue ; call with: a = character save bc,hl ld b,0 ; time-out after 256 trials oq2: push af ld hl,tt_outq add hl,de ; hl -> output queue call putq ; put character there jr c,oq10 ; are all character buffers used up? pop af ; no. the output queue has the char oqx: restor bc,hl and a,a ret oq10: ; no room for character in output queue because all character buffers ; are in use, or because queue count = 255 (from too many echoes) pop af bit ECHOING,(iy+tt_stat2) ; is this only an echo? jr nz,oqx ; if so, discard it djnz oq2 ; no. time-out? jr oqx ; yes. discard the character ; ----------------------------------------------------------------------------- subttl Output Interrupt ckoutput: ; output a character from the output queue ; unless an output interrupt is due. ; (called only by TTOUTQ, RESTARTOUT, CKINHIGH, and CKINLOW.) ; call with: de and iy -> tty structure ; alters: af ld a,i ; save interrupt state di push af bit INT_DUE,(iy+tt_stat) ; if no interrupt is due, output call z,cko2 ; a character from output queue pop af ret po ; restore interrupt state ei ret cko2: IF not IOPVERSION ; unmask TBE interrupts (necessary only for the C-1 (Single Card Terminal)) push bc ld a,(iy+devno) ld c,(iy+dataport) call tuunmtbe pop bc ENDIF ; perform the output interrupt routine ; ttoutput: ; output a character from the output queue. ; (called only by CKOUTPUT and by output interrupt service.) ; call with: interrupts disabled ; de and iy -> tty structure ; alters: af (the interrupt handler saves it) push bc push hl ld c,(iy+dataport) ; data port number ld a,(iy+tt_stat2) ; time to SEND a start or a stop and a,^PUTSTOP|^PUTSTART ; character (TANDEM mode)? jr z,ou1 bit PUTSTART,a ; yes. send a start character? ld a,X_ON jr nz,ou0 ld a,X_OFF ; no, send a stop character. ou0: res PUTSTOP,(iy+tt_stat2) res PUTSTART,(iy+tt_stat2) jr ou8 ou1: bit OUTPSTOP,(iy+tt_stat) ; no. have we RECEIVED a stop char? jp nz,ou20 ld l,(iy+tt_klct) ; no. are we echoing a killed line? ld h,(iy+tt_klct+1) ld a,l or a,h jr z,ou4 dec (iy+tt_klseq) ; yes. decrement sequence count ld a,(iy+tt_klseq) ; and get it jr nz,ou2 ; at the end of the sequence? ld (iy+tt_klseq),RUBSEQLN ; yes. reinitialize sequence count dec hl ; decrement the character count ld (iy+tt_klct),l ; and update it ld (iy+tt_klct+1),h ; ou2: ld hl,rubseq call addhla ; add the current sequence count (in a) ld a,(hl) jr ou8 rubseq: db '\b \b' ; the Rubout sequence (in reverse) RUBSEQLN equ $ - rubseq ou4: ld hl,tt_outq add hl,de ; hl -> output queue call getq ; get character jr c,ou20 bit BINARY,(iy+tt_mode+md_mode3) jr nz,ou8 ; if binary mode, don't change char IF IOPVERSION and a,a ; NULL? jr nz,ou4a call getq ; yes. get the next character jr c,ou10 ; (if there is none, put the NULL back) and a,a ; is the next char also a NULL? jr nz,ou30 ; if not, a = amount of delay ou4a: ENDIF ld l,(iy+tt_mode+md_mode1) ; get parity mode bits bit ODD,l ; ODD? jr z,ou5 bit EVEN,l ; yes. EVEN also? jr nz,ou8 ; if both, leave parity unchanged and a,not[^PARITY] ; no. ODD but not EVEN jp po,ou8 ; if stripped char is odd parity, done jr ou6 ; char is even parity. set parity bit ou5: and a,not[^PARITY] ; not ODD. strip char's parity bit jp pe,ou8 ; if char now even, done regardless bit EVEN,l ; char is odd parity. EVEN set? jr z,ou8 ; if not EVEN (& not ODD), ok ou6: set PARITY,a ou8: ld l,(iy+tt_oroutine) ld h,(iy+tt_oroutine+1) call hladdr ; perform the output set INT_DUE,(iy+tt_stat) ou9: call wrwake? ; is it time to wake the process up? oux: pop hl pop bc and a,a ; clear cy ret IF IOPVERSION ou10: ; got a NULL, but the next character wasn't in the queue yet, ; so put the NULL back into the queue xor a,a call putq ENDIF ou20: ; nothing was output from the queue, so no further interrupts are due res INT_DUE,(iy+tt_stat) IF not IOPVERSION ; mask TBE interrupts (necessary only for the C-1 (Single Card Terminal)) ld a,(iy+devno) ; minor device number call tumsktbe ENDIF jr ou9 IF IOPVERSION ou30: ; set up an output delay ; pass the delay subroutine the following registers: ; a = the number of tenth-seconds to delay ; b = the minor device number ; c = the data port number ; de and iy -> the device structure ld h,a ; save ld b,(iy+devno) ; minor device number ld a,b call qdmsktbe ; first mask output interrupts ld a,h ; and then ld hl,do_delay ; fork & execute DO_DELAY which ld (ioforkadr),hl ; will unmask output interrupts call iofork ; after a delay. jr oux do_delay: ; delay for a given amount of time before unmasking outputs ; call with: de and iy -> the device structure ; a = the number of tenth-seconds to delay ; b = minor device number ; c = data port number di call iotime ; wait until (a) tenth-seconds have elapsed ld a,b ; minor device number call qdunmtbe ; unmask output interrupts before calling call ttoutput ; the interrupt output routine. call wrwake? ; check whether to awaken the output process ei ret ENDIF wrwake?: ; awaken the output process? bit WRWAIT,(iy+tt_stat) ; waiting for output queue to drain? ret z ld a,(iy+tt_outq+q_ct) ; yes cp a,TTOLOW jr z,ww2 ; is the queue at its low-water mark? and a,a ; no. is it empty? ret nz ww2: ; ready for more output, so awaken the output process res WRWAIT,(iy+tt_stat) ld a,HmWRWAIT IF IOPVERSION bit WAKIOP,(iy+tt_stat2) ; should we tell host to awaken us? jr z,ww4 ; (i.e., awaken this iop process)? res WAKIOP,(iy+tt_stat2) ; yes ld a,HmWAKIOP ENDIF ww4: jp hwakeup ; ----------------------------------------------------------------------------- subttl Flush Queues ttflush: ; call with: de -> device structure push hl ld hl,tt_outq ; displacement of output queue call flush ; flush it pop hl ttiflush: ; call with: de -> device structure push hl ld hl,tt_tahq ; displacement of type-ahead queue call flush ; flush it ld hl,tt_inpq ; displacement of input queue call flush ; flush it pop hl xor a,a ; return zero ret flush: add hl,de fl2: call getq jr nc,fl2 ret ; ----------------------------------------------------------------------------- subttl Getmode & Setmode Routines ttgmode: ; a = mode parameter index ; de -> device structure ; returns parameter in a-register ; alters af, hl ld hl,tt_mode jp getmode ttsmode: ; a = index ; de and iy -> device structure ; b = new parameter value(s) ; c = mask for new parameter values ; returns with: a = the old parameter values ; alters af, b, hl ld hl,tt_mode call setmode push af ; save value or error code di call restartout ; restart output in case the mode ei ; was changed to BINARY or RAW pop af ret ; =============================================================================