page 60,132 title MSYIBM screen and keyboard handlers ; edit history: ; ; [v2.29] ; Ansi set graphics rendition support extended to handle color attributes ; in addition to monochrome ; Direct moves to and from the video buffer modified using TOPVIEW ; interrupt codes, so that KERMIT can run in the background under ; TOPVIEW and take advantage of TOPVIEW windowing support. ; ; ; [v2.28] ; Don't scroll in inverse video lines (again). ; Backspace in column 0 backs up a line in wrap mode (from Greg Small, ; UC Berkeley) ; Ansi set graphics rendition support (suggested by Greg Small, UC Berkeley) ; escape sequences of the form esc [ p1;p2;...;pn m where pi is ; one of 0,1,4,5,7 for normal, high-intensity, underline, blink, ; and reverse, respectively. ; JD May 1985 ; [v2.27] ; Fixed destructive tab problem (received from many people) ; Fixed insert/delete line problem when at bottom/top of screen. ; Newlines never scroll in inverse video lines ; Implemented cursor save/restore (from Univ. Md.) ; "Echo" status on mode corrected. ; JD, 6 December 1984 public term, gss, lclyini ; entry points include msdefs.h ; some character definitions chesc equ 27 bel equ 7 print_out equ 05h ; dos function to print to printer pbout equ 02h ; dos function to print a character prscan equ 72h ; print-screen scan code... upscan equ 49h ; up page dnscan equ 51h ; down page ctlup equ 84h ; ctl-up page ctldn equ 76h ; ctl-down page homscn equ 47h ; home screen endscn equ 4fh ; end of screen screen equ 10h ; bios screen call kb equ 16h ; keyboard interrupt alt_shift equ 8H ; alt shift key down ctl_shift equ 4H ; ctl key down left_shift equ 2H ; left shift key down right_shift equ 1H ; right shift key down timer equ 40h ; timer port bel_prt equ 61h ; speaker control crt_status equ 3dah ; crt status port disp_enb equ 8 ; display enable bit uparr equ 48h ; scan codes for arrow keys dnarr equ 50h lftarr equ 4bh rgtarr equ 4dh modfrm struc ; format of mode line db 'Esc chr: ' m_echr db 2 dup (?) db ', Port: ' m_prt db 1 dup (?) db ', Speed: ' m_baud db 4 dup (?) db ', Parity: ' m_par db 4 dup (?) db ', Echo: ' m_echo db 3 dup (?) db ', Type ' m_hlp db 2 dup (?) db '? for Help' modfrm ends datas segment public 'datas' waste db 100h dup (?) ;*** need this junk because assembler ;*** generates non-relocatable offsets ;*** for things like ;*** "sub di,offset foo" ;*** if offset foo < 100H ; stuff for screen routines flags db ? ; status flags... flags1 db 0 ; internal flags. prtscr equ 80h ; print screen pressed inited equ 08h ; been here before... wrapped equ 04h ; on if wrapped on last char... cursor dw ? esc_ch db ? argadr dw ? ; address of arg blk ckeys db 0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn db uparr,dnarr,lftarr,rgtarr lckeys equ $-ckeys ; ckacts must parallel ckeys above... ckacts dw trnbrk,trnprs,dnwpg,upwpg,endwnd,homwnd,upwind,dnwind dw trnupw,trndnw,trnlfw,trnrgw uptrn db esc,'A' dntrn db esc,'B' rgtrn db esc,'C' lftrn db esc,'D' spctab db chesc,cr,lf,bs,tab,bel lspctab equ $-spctab spcjmp dw outesc,outcr,outlf,outbs,outtab,outbel ; must match spctab esctab db 'YABCDEFGHIJKLM' db 'NOZ@[pq ; mode line buffer ; routine to call for captured output captrtn dw ? oldcur dw 0 ; save'd cursor position ; some static data for mode line unkbaud db 'Unk ' ; must be 4 chars... baudn db '45.5' db ' 50' db ' 75' db ' 110' db ' 135' db ' 150' db ' 300' db ' 600' db '1200' db '1800' db '2000' db '2400' db '4800' db '9600' db '19.2' db '38.4' baudnsiz equ 16 ; # of baud rates known (tbl size / 4) parnams db 'Even' db 'Mark' db 'None' db 'Odd ' ; must be 4 chars db 'Spc ' offmsg db 'Off' onmsg db 'On ' lclmsg db 'Lcl' remmsg db 'Rem' ; storage for multi-window stuff swidth equ 80 slen equ 24 npgs equ 5 ; # of pages on each side bsize equ swidth*slen*npgs*2 scrsav dw ? ; circular buffer. To work properly, the buffer size should be an exact ; multiple of swidth*2 cbuf struc pp dw ? ; place ptr in buffer bend dw ? ; end of buffer orig dw ? ; buffer origin lcnt dw 0 ; # of lines in buffer. cbuf ends tlbuf dw ? ; db swidth*2 dup (?) blbuf dw ? ; db swidth*2 dup (?) twnd cbuf <> bwnd cbuf <> portno db ? prton db 'Printer: on' prtnlen equ $-prton prtoff db 'Printer: off' prtflen equ $-prtoff lincur dw ? datas ends code segment public ; code segment extrn prtchr:near,outchr:near,sendbr:near,malloc:near assume cs:code,ds:datas,es:datas ; do initialization local to this module... lclyini proc near mov ax,swidth*slen*2 call malloc mov scrsav,ax ; place to save screens mov ax,swidth*2 call malloc mov tlbuf,ax mov ax,swidth*2 call malloc mov blbuf,ax ; top and bottom temp line buffers mov ax,bsize call malloc mov twnd.pp,ax mov twnd.orig,ax add ax,bsize-1 mov twnd.bend,ax mov ax,bsize call malloc mov bwnd.pp,ax mov bwnd.orig,ax add ax,bsize-1 mov bwnd.bend,ax ret lclyini endp scrini proc near ; init screen stuff mov ah,15 ; read video state... int screen mov crt_mode,al ; save crt mode cmp ah,crt_cols ; is real # of cols < passed? jge scrin1 ; no mov crt_cols,ah ; yes, save # of cols scrin1: mov dl,crt_cols ; # of cols again mov dh,crt_lins ; and # of rows dec dl dec dh mov low_rgt,dx ; save away window address mov insmod,0 ; not in insert mode mov dx,cursor ; assume old cursor test flags1,inited ; have we been here before? jnz scrin4 ; yes, use old cursor mov curattr,07 ; else set nice screen attribute mov ttstate,offset outtt0 ; normal screen state mov ah,3 ; figure out where cursor is xor bh,bh ; page 0 int screen ; read cursor position mov lincur,cx ; save line (?) cursor value cmp dh,crt_lins ; past logical end of screen? jb scrin2 ; no, keep going mov dh,byte ptr low_rgt+1 ; yes, just use lower right corner scrin2: cmp dl,crt_cols ; maybe past right margin jb scrin3 ; no, use the way it is mov dl,byte ptr low_rgt scrin3: mov cursor,dx ; init cursor scrin4: mov ah,2 xor bh,bh int screen ; set cursor in case it moved ret scrini endp argini proc near ; read passed arguments mov bx,argadr ; base of argument block mov al,[bx].flgs ; get flags and al,capt+emheath+havtt+trnctl+lclecho+modoff+lnwrap mov flags,al ; mask for allowable and save and flags1,not (prtscr) ; these are allowable ; (others remain). mov al,[bx].prt cmp al,portno ; using same port? je argin1 ; yes, go on and flags1,not inited ; else re-init stuff argin1: mov portno,al ; update port number mov al,[bx].cols mov crt_cols,al mov al,[bx].rows mov crt_lins,al ; init # of rows and cols mov ax,[bx].captr mov captrtn,ax ; buffer capture routine mov ax,[bx].belld mov beldiv,ax ; bell divisor mov ax,[bx].klen mov ktlen,ax ; length of key redef tbl mov ax,[bx].ktab mov ktrntab,ax ; save key translation table mov ax,[bx].krpl mov krpltab,ax mov al,[bx].escc mov esc_ch,al ret ; that's it argini endp modlin proc near ; turn on mode line mov al,esc_ch mov modbuf.m_echr,' ' ; first char is initial space mov modbuf.m_hlp,' ' ; goes here too. cmp al,32 ; printable? jnb modl1 ; yes, keep going add al,40h ; made printable mov modbuf.m_echr,'^' ; note control char mov modbuf.m_hlp,'^' modl1: mov modbuf.m_echr+1,al ; fill in character mov modbuf.m_hlp+1,al mov bx,argadr ; get argument block mov al,[bx].baudb ; get baud bits mov si,offset unkbaud ; assume unknown baud cmp al,baudnsiz ; too big? jnb modl2 ; yes, use default mov cl,2 ; each is 4 bytes long shl al,cl mov ah,0 add ax,offset baudn mov si,ax modl2: mov cx,size m_baud ; length of baud space mov di,offset modbuf.m_baud rep movsb ; copy in baud rate mov al,[bx].parity ; get parity code mov cl,2 ; each is 4 bytes long... shl al,cl mov ah,0 add ax,offset parnams ; names of parity settings mov si,ax mov cx,4 ; each is 4 long mov di,offset modbuf.m_par rep movsb mov si,offset remmsg ; Assume remote echoing. test flags,lclecho ; Is remote side echoing? jz modl4 ; Yes, keep going mov si,offset lclmsg ; Else it's local echoing. modl4: mov cx,3 ; size of on/off mov di,offset modbuf.m_echo rep movsb mov al,'1' cmp portno,1 ; port 1? je modl5 ; yes, keep going mov al,'2' modl5: mov modbuf.m_prt,al ; fill in port number mov cx,size modfrm ; this is size of mode line mov si,offset modbuf ; mode line image ; alternate entry to write an alternate mode line modwrt: push cx push si ; save mode line and size mov cx,1800h ; line 25... mov dx,184fh mov ax,600h ; scroll to clear the line mov bh,70h ; inverse video int screen mov dx,24 * 100h mov bh,0 mov ah,2 ; set cursor position int screen pop si pop cx ; restore these modl6: lodsb ; get a byte mov ah,14 ; write to terminal mov bh,0 ; page 0 int screen loop modl6 ; write out entire mode line mov dx,cursor mov ah,2 mov bh,0 int screen ; put cursor back where it belongs ret ; and return modlin endp clrmod proc near ; clear mode line mov ax,600h ; blank window mov cx,24 * 100h ; beginning of window mov dx,24 * 100h + 79 ; end of window mov bh,07 ; nice attribute int screen ; clear mode line ret ; and return clrmod endp term proc near ; terminal emulator entry point mov argadr,ax ; save argument ptr push es ; save caller's extra segment address mov ax,seg datas mov es,ax call argini ; init options from arg address call scrini ; init screen stuff test flags1,inited ; have we run yet? jz term1 ; no, forget this part call restscr ; restore screen term1: or flags1,inited ; remember we've run already. call clrmod ; empty mode line test flags,modoff ; is mode line disabled? jnz lp ; yes, skip it call modlin ; turn on mode line lp: call portchr ; char at port? jnc chkinp ; no, keep going call outtty ; print on terminal chkinp: mov ah,1 int kb jz lp ; nothing available... xor ah,ah int kb ; get the char from the buffer push ax ; save character temporarily call gss ; get shift state into al mov bl,al ; save shift state pop ax cmp al,esc_ch ; escape character? je quit ; yes, stop here call trnout ; translate if nec., output to prt jmp chkinp ; and keep going quit: call clrmod ; erase mode line call savescr ; save screen mov al,flags mov bx,argadr mov [bx].flgs,al ; update flags in arg block pop es ; restore segment register ret ; and return to caller term endp ; get shift state into al. We only care about shift, ctl, and alt keys. ; right shift is collapsed into left shift. gss proc near mov ah,2 int kb ; get current shift state mov bl,al ; copy for a moment and bl,right_shift ; mask out all but right shift shl bl,1 ; move right shift to left shift pos or al,bl ; collapse shift bits and al,(left_shift + alt_shift + ctl_shift) ret gss endp ; save the screen so we can restore it ; maybe save cursor also. savescr proc near push ds push es ;save across scrseg call [v2.29] call scrseg ; get tv screen buffer [v2.29] mov si,di ; set source offset [v2.29] mov di,scrsav ; place to put screen mov cx,80*24 ; # of words on screen call scrwait ; wait for screen to be ready push es ; transfer es to ds [v2.29] pop ds ; address screen pop es ; restore entry es [v2.29] rep movsw ; save the screen pop ds ; restore this ret ; and return savescr endp ; restore screen from scrsav buffer restscr proc near push es mov si,scrsav ; source mov cx,80*24 call scrseg ; get tv screen buffer [v2.29] call scrwait rep movsw ; restore it mov cx,80*24 ; set length for scrsync [v2.29] call scrsync ; synchronize topview [v2.29] pop es ret restscr endp ; send the character in al out to the serial port ; handle echoing also... outprt proc near test flags,lclecho ; echoing? jz outpr1 ; no, forget it push ax ; save char call outtty ; print it pop ax ; restore outpr1: mov ah,al ; this is where outchr expects it call outchr ; output to the port nop nop nop ; skip returns... ret outprt endp ; returns with carry on if a character is available portchr proc near call prtchr ; character at port? jmp short portc1 ; yes, go handle nop ; skip return is stupid... clc ; no carry -> no character ret ; and return... portc1: and al,7fh ; we don't worry about parity here stc ; have a character ret ; and return portchr endp ; translate the scan code in ah according to the translate table ; given in ktrntab/krpltab, output to port. If no translation, ; use ascii char in al. (should probably include shift state ; somewhere). Shift state is in bl. trnout proc near test flags,havtt ; translate table given? jz trnou3 ; no, just output character push ax ; save original value mov al,ah ; put scan code into ah mov ah,bl ; shift state into top half. mov di,ktrntab ; pick up translate tbl mov cx,ktlen ; length of tbl repne scasw ; look for our key pop ax ; recover character jne trnou3 ; not found, forget it sub di,ktrntab ; get index into tbl sub di,2 ; (minus 2 for pre-increment) mov bx,krpltab ; get replacement table mov si,[bx][di] ; and addr of replacement mov cl,[si] ; get first byte (length) xor ch,ch ; clear high-order byte inc si ; point to translation string trnou2: lodsb ; get a byte push si push cx ; save important registers call outprt ; send to port pop cx pop si loop trnou2 ; send all chars ret ; and return trnou3: cmp ah,4eh ;*** plus key thing? je trnmod ; yes, go toggle mode line cmp al,0 ; is it a special code? jne trnou4 ; no, don't do this mov al,ah ; get scan code mov cx,lckeys ; length of table mov di,offset ckeys ; table address repne scasb mov al,0 ; ascii code was 0... jne trnou4 ; not found, keep going sub di,offset ckeys+1 ; get table offset shl di,1 ; shift for word offset jmp ckacts[di] ; jump to appropriate routine trnou4: call outprt ; just output single char ret ; and return trnmod: test flags,modoff ; mode line already off? jnz trnm1 ; yes, go turn on call clrmod ; no, clear mode line here or flags,modoff ; turn on flag ret ; and return trnm1: call modlin ; turn on mode line and flags,not modoff ; clear flag ret ; and return trnbrk: mov ah,dconio mov dl,0ffH int dos ; read the bogus ^C DOS gets. call sendbr ret trnprs: xor flags1,prtscr ; flip the flag and flags,not modoff ; turn on mode line mov si,offset prton mov cx,prtnlen test flags1,prtscr ; did it go on? jnz trnpr1 ; yes, say so mov si,offset prtoff mov cx,prtflen trnpr1: call modwrt ; write into mode line ret ; and return ; common entry for arrow keys trnarr: mov cx,2 ; length is always 2 jmp trnou2 ; go send definition trnupw: mov si,offset uptrn jmp trnarr trndnw: mov si,offset dntrn jmp trnarr trnlfw: mov si,offset lftrn jmp trnarr trnrgw: mov si,offset rgtrn jmp trnarr trnout endp ; move viewing window up (screen moves down). ; alternate entry upwin2 doesn't beep if invalid. upwind proc near mov ax,tlbuf ; place to put line temporarily mov bx,offset twnd ; where to get lines from call getcirc ; try to get a line jnc upwin3 ; have a line, go show it call outbel ; else ring bel ret ; and return upwin2: mov ax,tlbuf mov bx,offset twnd call getcirc jnc upwin3 ret ; this just rets if no line avail. upwin3: mov ax,blbuf ; place for bottom line call getbot ; fetch bottom line mov ax,blbuf mov bx,offset bwnd call putcirc ; save in circular buffer mov ax,701h ; scroll down one line xor cx,cx ; from top mov dx,low_rgt ; to bottom mov bh,07 ; normal attributes int screen ; scroll it down mov si,tlbuf ; where to get line from mov cx,swidth ; length of line push es call scrseg ; get tv screen buffer [v2.29] call scrwait rep movsw ; copy the line in mov cx,swidth ; set length for scrsync [v2.29] call scrsync ; synchronize topview [v2.29] pop es ; restore this ret ; and return upwind endp ; move viewing window down a line (screen scrolls up) ; entry dwin2 does same w/out checking to see if scroll is legal dnwind proc near mov ax,blbuf ; place to put line temporarily mov bx,offset bwnd ; where to get lines from call getcirc ; try to get a line jnc dnwin3 ; have a line, go show it call outbel ; else ring bel ret ; and return dnwin2: mov ax,blbuf mov bx,offset bwnd call getcirc jnc dnwin3 ret ; this just rets if no line avail. dnwin3: call scrprep ; save top line mov ax,601h ; scroll up one line xor cx,cx ; from top mov dx,low_rgt ; to bottom mov bh,07 ; normal attribute int screen ; scroll it down mov dx,low_rgt mov dl,0 ; get addr of last line call scrloc mov di,ax ; this is offset in dest mov si,blbuf ; where to get line from mov cx,swidth ; length of line push es push di ; save offset in dest [v2.29] call scrseg ; get tv screen buffer [v2.29] pop ax ; restore offset in dest [v2.29] add di,ax ; offset to last line [v2.29] call scrwait rep movsw ; copy the line in mov cx,swidth ; set length for scrsync [v2.29] call scrsync ; synchronize topview [v2.29] pop es ; restore this ret ; and return dnwind endp ; move viewing window down as much as possible... endwnd proc near mov cx,1000 ; large number of lines jmp dnwp1 ; and enter dwnpg endwnd endp ; scroll viewing window down (contents move up) crt_lins times... dnwpg proc near mov cl,crt_lins mov ch,0 dnwp1: push cx ; save this call dnwin2 pop cx loop dnwp1 ret ; and return dnwpg endp ; home viewing window homwnd proc near mov cx,1000 ; large # of lines jmp upwp1 ; join upwpg homwnd endp ; scroll viewing window up (screen moves down) a page upwpg proc near mov cl,crt_lins mov ch,0 upwp1: push cx call upwin2 pop cx loop upwp1 ret ; and return upwpg endp ; get the bottom line into the buffer pointed to by ax. getbot proc near push ds mov di,ax ; save dest mov cx,swidth mov dx,low_rgt mov dl,0 call scrloc mov si,ax push es ; save es across scrseg [v2.29] push si ; save screen location [v2.29] call scrseg ; get tv screen buffer [v2.29] mov si,di ; set scrn buff offset [v2.29] pop ax ; restore screen loc [v2.29] add si,ax ; relative to scrn buf [v2.29] call scrwait push es ; transfer es to ds [v2.29] pop ds pop es ; set dest segment [v2.29] rep movsw pop ds ret getbot endp ; get a line into the circular buffer. Pass the buffer structure ; in bx, the pointer to the line in ax. putcirc proc near push si push di push cx push dx mov di,[bx].pp ; pick up buffer ptr add di,2*swidth ; increment to next avail slot cmp di,[bx].bend ; past end? jb putci1 ; no, leave alone mov di,[bx].orig ; else start at beginning putci1: mov [bx].pp,di ; update ptr mov si,ax ; this is source mov cx,swidth rep movsw ; copy into buffer cmp [bx].lcnt,npgs*slen ; can we increment it? jae putci2 ; no, keep going inc [bx].lcnt ; else count this line putci2: pop dx pop cx pop di pop si ; restore registers ret putcirc endp ; get a line from the circular buffer, removing it from the buffer. ; returns with carry on if the buffer is empty. ; pass the buffer structure in bx, the buffer to copy the line into ; in ax. getcirc proc near push si push di push cx push dx cmp [bx].lcnt,0 ; any lines in buffer? jne getci1 ; yes, ok to take one out. stc ; else set carry jmp short getcir3 ; and return getci1: mov si,[bx].pp ; this is source mov di,ax ; this is dest mov cx,swidth ; # of chars to copy rep movsw mov si,[bx].pp ; get ptr again sub si,2*swidth ; move back cmp si,[bx].orig ; compare to origin jae getcir2 ; still in range, continue mov si,[bx].bend ; else use end of buffer sub si,2*swidth-1 ; minus length of a piece getcir2:mov [bx].pp,si ; update ptr dec [bx].lcnt ; decrement # of lines in buffer clc ; make sure no carry getcir3:pop dx pop cx pop di pop si ret getcirc endp ; call before scrolling to save top line... scrprep proc near push ds push es ; save across scrseg [v2.29] mov cx,swidth ; length of line call scrseg ; get tv screen buffer [v2.29] mov si,di ; set source offset [v2.29] mov di,tlbuf ; place to put line temporarily call scrwait push es ; transfer es [v2.29] pop ds pop es ; restore entry es [v2.29] rep movsw ; copy the line pop ds ; restore this mov ax,tlbuf mov bx,offset twnd ; this is where it goes call putcirc ; put into buffer ret ; and return scrprep endp ; put the character in al to the screen outtty proc near test flags,capt ; capturing output? jz outnoc ; no, forget this part push ax ; save char call captrtn ; give it captured character pop ax ; restore character and keep going outnoc: test flags1,prtscr ; should we be printing? jz outnop ; no, keep going push ax mov ah,print_out mov dl,al ; put character here for dos... int dos pop ax outnop: test flags,emheath ; emulating heath? jnz outnop1 ; yup, go do something smart mov dl,al mov ah,pbout int dos ; else let dos print char ret ; and return outnop1:mov dx,cursor ; these may need cursor... jmp ttstate ; jump according to current state outtt0: cmp al,32 ; special character? jb outtt1 ; yes, handle specially... cmp insmod,0 ; in insert mode? je outnrm ; no, output normal push ax ; save character call inschr ; insert a character pop ax outnrm: xor bh,bh ; current page mov cx,1 ; only one char mov bl,curattr ; with current attribute mov ah,9 int screen ; put onto screen mov dx,cursor ; get cursor pos currt: inc dl ; bump col cmp dl,crt_cols ; see if in range jb setcur ; in range, go set cursor test flags,lnwrap ; in wrap mode? jz outign ; no, just return w/out updating cursor wrap: xor dl,dl inc dh ; handle wrap or flags1,wrapped ; remember we wrapped jmp short setc0 ; and don't turn off flag setcur: and flags1,not wrapped ; if moving cursor, turn off wrap flag setc0: cmp dh,crt_lins jb setc1 ; not off end, keep going push dx ; save row/col call scrprep ; save top line in window buf mov ax,0601h ; scroll up one line xor cx,cx ; from 0,0 mov dx,low_rgt ; to 24,80 mov bh,7 ; nice attribute int screen ; do the scroll pop dx mov dh,crt_lins ; go to bottom line again... dec dh setc1: xor bh,bh ; page is 0 mov cursor,dx ; save cursor pos mov ah,2 int screen ; set cursor outign: ret ; and return ; special character (in al) outtt1: mov di,offset spctab ; special char table mov cx,lspctab ; length of tbl repne scasb ; look for char in tbl jz outtt2 ; found, go do something with it test flags,trnctl ; are we allowed to print carets? jz outign ; no, just ignore it. push ax ; save char mov al,'^' call outtty ; print caret pop ax add al,'A'-1 ; make printable jmp outtty ; print, then return outtt2: mov dx,cursor ; might need cursor pos sub di,offset spctab+1 ; get index of char shl di,1 ; double for word offset jmp spcjmp[di] ; and go handle ; special char routines. cursor is in dx, char in al outlf:;;;[jrd] test flags1,wrapped ; just wrapped? ;;;[jrd] jz outlf1 ; no, proceed normally ;;;[jrd] and flags1,not wrapped ; else turn off wrap flag ;;;[jrd] ret ; and skip this lf outlf1: inc dh ; bump row jmp setcur outcr:;;;[jrd] test flags1,wrapped ; just wrapped? ;;;[jrd] jz outcr1 ; no, continue ;;;[jrd] ret ; yes, already in col 0 outcr1: xor dl,dl ; set col to 0 jmp setcur outbs: or dl,dl jg outbs1 ; > col 0, just back up test flags,lnwrap ; auto wrap on? jz outbs2 ; no, stay at left margin or dh,dh ; at top of screen? jz outbs2 ; yes, stay where we are mov dl,crt_cols ; get rightmost column dec dh ; go up a row outbs1: dec dl ; back up a column outbs2: jmp setcur ; go set cursor outtab: mov dl,byte ptr cursor ; get initial column add dl,8 ; tab is at most 8 columns and dl,not 111b ; round down to a multiple of 8 cmp dl,crt_cols ; out of range? jae outta1 ; yes, skip it jmp setcur ; else set it outta1: test flags,lnwrap ; in wrap mode? jnz outta2 ; yes, wrap to next line mov dl,byte ptr low_rgt ; else just move to right margin jmp setcur outta2: jmp wrap ; stolen from bios outbel: mov al,10110110b ; timer initialization out timer+3,al mov ax,beldiv ; bel divisor out timer+2,al mov al,ah out timer+2,al ; output divisor in al,bel_prt mov ah,al ; remember original value or al,3 ; turn speaker on out bel_prt,al mov cx,8888h outbe1: loop outbe1 ; wait a while mov al,ah out bel_prt,al ; turn bell off ret ; and return outesc: mov ttstate,offset escseq ; expect escape sequence. ret ; and return ; escape-char handling routines escseq: mov ttstate,offset outtt0 ; put state back to normal mov di,offset esctab ; escape char tbl mov cx,lesctab ; length of tbl repne scasb ; look for it in tbl jz escsq1 ; found, go use it jmp outnoc ; not there, print it (already logged) escsq1: sub di,offset esctab+1 ; get offset into tbl shl di,1 ; convert to word offset jmp escjmp[di] ; and go dispatch on it ; escape dispatch routines revind: cmp dh,0 jle revin1 dec dh ; back up a row jmp setcur ; and go set cursor revin1: push dx ; save cursor pos mov ax,701h ; scroll down one line xor cx,cx ; from top mov dx,low_rgt ; to bottom mov bh,07 ; normal attributes int screen ; scroll it down pop dx ; restore cursor. mov dh,0 ; set row back to 0 jmp setcur curup: cmp dh,0 ; w/in range? jle curu1 ; no, skip this dec dh ; else back up curu1: jmp setcur ; and go set position curdwn: inc dh jmp setcur ; increment row (setcur can scroll!) ; currt is above clrscr: call curhom ; go home cursor jmp clreow ; then clear to end of window curhom: xor dx,dx ; move to 0,0 jmp setcur clreow: cmp dl,0 ; at beginning of line? jz clrw1 ; yes, skip this part... push dx ; remember cursor pos call clreol ; clear to end of this line pop dx inc dh ; bump row xor dl,dl ; start from col 0 clrw1: cmp dh,crt_lins ; last line on screen jnb clrw2 ; if not in range, forget it mov ax,700h ; clear whole window mov cx,dx ; this is beginning mov dx,low_rgt mov bh,07 ; normal attribute int screen ; go clear it clrw2: ret ; and return clreol: push es mov cl,crt_cols ; last col + 1 sub cl,dl ; this is # of chars to move xor ch,ch jcxz clrl1 call scrloc ; compute screen location (to ax) mov di,ax push di ; hold screen disp. [v2.29] call scrseg ; get tv screen buffer [v2.29] pop ax ; restore screen disp. [v2.29] add di,ax ; relative to di [v2.29] call scrwait ; wait for retrace mov ah,07 ; current attribute mov al,' ' ; fill char push cx ; save length for scrsync[v2.29] rep stosw ; fill line with spaces pop cx ; set length for scrsync [v2.29] call scrsync ; synchronize topview [v2.29] clrl1: pop es ret ; and return inslin: mov al,1 ; scroll one line ; alternate entry if inserting more then one line inslin1:mov ch,dh ; start at current row xor cl,cl ; column 0 mov dx,low_rgt mov ah,7h ; scroll down. mov bh,07 ; normal attribute cmp ch,dh ; moving last line down? jne insli2 ; no, keep going mov al,0 ; yes, just clear it insli2: int screen ret dellin: mov al,1 ; scroll 1 line ; alternate entry if deleting more than one line dellin1:mov ch,dh ; start at current row xor cl,cl ; column 0 mov dx,low_rgt mov ah,6h ; scroll up. mov bh,07 ; normal attribute cmp ch,dh ; deleting last line? jne delli2 ; no, go on mov al,0 ; yes, just blank it delli2: int screen ret delchr: push ds push es pushf ; these may get changed... mov cl,crt_cols dec cl sub cl,dl ; from what we're fiddling) xor ch,ch jcxz delch1 ; none to move, forget it call scrloc ; compute location mov di,ax push di ; save screen disp. [v2.29] call scrseg ; get tv screen buffer [v2.29] pop ax ; restore screen disp. [v2.29] add ax,di ; relative to di [v2.29] mov di,ax ; put it in di and si [v2.29] mov si,ax add si,2 ; source is next position over call scrwait ; wait for retrace push ds ; save ds [v2.29] push es ; copy es to ds [v2.29] pop ds ; address screen segment push cx ; save length for scrsync [v2.29] rep movsw ; delete it mov byte ptr [di],' ' ; kill char at end of line pop cx ; set length for scrsync [v2.29] pop ds ; restore data segment [v2.29] call scrsync ; synchronize topview [v2.29] delch1: popf pop es pop ds ret inschr: push ds push es ; save these as well pushf ; might as well save flags... mov dx,cursor ; this is place to do it mov cl,crt_cols dec cl ; mov cl,79 ; this is last col to move, +1 for length sub cl,dl ; compute distance to end xor ch,ch ; clear top half of offset jcxz insch1 ; nothing to move... mov dl,crt_cols sub dl,2 ; last col to move ; mov dl,78 ; this is address of last col to move call scrloc ; compute pos push ax ; save screen disp. [v2.29] call scrseg ; get tv screen buffer [v2.29] mov si,di ; to si [v2.29] pop ax ; restore screen disp. [v2.29] add ax,si ; relative to di [v2.29] mov si,ax mov di,ax add di,2 ; destination is one byte over... std ; remember to move us backwards call scrwait ; wait until save to write push ds ; save data segment ptr [v2.29] push es ; copy es to ds [v2.29] pop ds ; address screen segment push cx ; save length for scrsync [v2.29] rep movsw ; move each char and attribute pop cx ; set length for scrsync [v2.29] sub di,2 ; destination is one byte over...[v2.29] add di,cx ; .. accounting for the [v2.29] add di,cx ; .. backwards move [v2.29] add di,cx ; .. accounting for the [v2.29] add di,cx ; .. backwards move [v2.29] pop ds ; restore data segment pointer [v2.29] call scrsync ; synchronize topview [v2.29] insch1: popf pop es pop ds ret ; and return noins: mov insmod,0 ; turn off insert mode ret ; and return movcur: mov wcoord,2 ; want two coordinates... mov ttstate,offset getcoord ret ; and return vtident: mov si,offset vtidstr mov cx,lvtidst vtid1: lodsb ; get a byte from the string push si ; have to save from outprt push cx call outprt ; send to serial port pop cx pop si loop vtid1 ; go thru all chars ret ; and return entins: mov insmod,0ffh ; enter insert mode... ret ; and return doansi: mov cx,ansargl ; # of arg bytes mov al,0 mov di,offset ansarg ; point to arg list rep stosb ; clear it mov ansidx,0 ; none seen yet mov ttstate,offset getaarg ; state is get ansi argument ret getaarg:cmp al,'0' jb getaa1 ; in range for digit? cmp al,'9' ja getaa1 sub al,'0' ; convert to binary mov dl,al ; tuck away mov bx,ansidx mov al,ansarg[bx] mov dh,10 mul dh ; shift sum add al,dl ; add in this digit (what about ovfl?) mov ansarg[bx],al ret ; and return getaa1: cmp al,'?' ; the dreaded question mark? jne getaa2 mov ttstate,offset ignn ; we ignore these... mov igncnt,2 ; this is how many chars come after him ret getaa2: cmp al,';' ; argument separator? jne getaa5 ; no, keep going inc ansidx ; yes, increment index cmp ansidx,ansargl ; is it too big? jb getaa6 ; no, keep going ;### this isn't good, but it beats having these parameters written ; all over our data segment... mov ansidx,1 ; yes, throw some away getaa6: ret ; and return getaa5: mov ttstate,offset outtt0 ; reset state mov dx,cursor ; this needs cursor position mov bl,ansarg xchg al,bl ; put argument in nice place cmp bl,'L' ; insert line? jne getaa3 jmp inslin1 ; and go do it getaa3: cmp bl,'M' ; maybe delete line? jne getaa4 jmp dellin1 getaa4: cmp bl,'m' ; maybe set graphic rendition jne getaa7 ; no, keep going mov cx,ansidx ; argument index inc cx ; allow for 0 index origin... mov si,offset ansarg call setgrph ; go set it all up getaa7: ret ; ignore. ; set graphic rendition... enter with si/ pointer to argument table, ; cx/ length of table setgrph: mov ah,curattr ; get current attributes jcxz setgr3 ; empty table, stop here setgr1: lodsb ; get a byte from the table push cx mov di,offset grtab ; table of recognizable attributes mov cx,grtlen ; length of table repne scasb ; look for our char jne setgr2 ; not found, keep going sub di,offset grtab+1 ; get table index and ah,attatab[di] ; keep correct bits or ah,attotab[di] ; and turn on appropriate new bits setgr2: pop cx ; restore counter loop setgr1 ; loop thru table setgr3: mov curattr,ah ; restore attributes ret ; and return invvid: mov curattr,70h ; attribute for inverse video ret nrmvid: mov curattr,07h ; attribute for normal video ret dowrap: or flags,lnwrap ; turn on wrap mode ret ; and return nowrap: and flags,not lnwrap ; turn off wrap mode ret ; and return ; get a coordinate. getcoord: sub al,32 ; coordinates offset by 32 mov si,wcoord dec si mov byte ptr coord[si],al ; fill in appropriate coordinate mov wcoord,si ; update flag jnz getco1 ; more needed, can't do anything yet mov ttstate,offset outtt0 ; reset state mov dx,coord ; get coordinates jmp setcur ; and go jump there getco1: ret ; ignore following igncnt characters ignn: dec igncnt ; decrement count jnz ignn1 mov ttstate,offset outtt0 ; put state back to normal if done ignn1: ret ; save cursor savecur: mov oldcur,dx ret ; restore cursor restcur: mov dx,oldcur jmp setcur setmode: mov modesw,offset setmo1 mov ttstate,offset getmod ret ; al contains argument digit... setmo1: cmp al,4 ; set block mode? jne setmo2 ; no, go on mov cx,10ch ; block cursor mov ah,1 ; set cursor... int screen setmo2: ret clrmode: mov modesw,offset clrmo1 mov ttstate,offset getmod ret ; al contains argument digit clrmo1: cmp al,4 ; is it set block mode? jne clrmo2 mov cx,lincur mov ah,1 int screen clrmo2: ret ; get a single-digit mode argument getmod: mov ttstate,offset outtt0 ; reset state cmp al,'0' jb getmo1 ; out of range cmp al,'9' ja getmo1 sub al,'0' ; convert to numeric jmp modesw ; return to whoever wanted argument getmo1: ret ; just ignore if out of range outtty endp ; computes screen location to ax, given row and col in dx. ; trashes ax,bx scrloc proc near mov al,dh ; get row mov bl,crt_cols ;** row size mul bl ; multiply by row size xor dh,dh ; clear col add ax,dx ; this is current position sal ax,1 ; double for attributes ret scrloc endp ; puts current screen segment in ax [v2.29] scrseg proc near ; [v2.29] xor di,di ; screen location 0 [v2.29] mov ax,0b000h ; assume bw for now [v2.29] cmp crt_mode,7 ; 7 is bw (***) [v2.29] je scrseg1 ; [v2.29] mov ax,0b800h ; color card [v2.29] scrseg1: mov es,ax ; [v2.29] mov tv_segs,es ; save hardware [v2.29] mov tv_sego,di ; screen address [v2.29] mov tv_mode,1 ; assume running under topview [v2.29] mov ah,0feh ; topview query [v2.29] int screen ; if topview is there [v2.29] mov ax,es ; [v2.29] cmp ax,tv_segs ; ... he will update es:di [v2.29] jne scrseg2 ; ... to point to a virtual [v2.29] cmp di,tv_sego ; ... screen buffer which we [v2.29] jne scrseg2 ; ... can update while running [v2.29] mov tv_mode,0 ; .. in background mode [v2.29] scrseg2: ret ; [v2.29] scrseg endp ; [v2.29] ; [v2.29] ; synchronize a topview provided virtual screen buffer [v2.29] ; with the image the user sees [v2.29] scrsync proc near ; [v2.29] cmp tv_mode,0 ; topview mode? [v2.29] je tvsyn1 ; no, skip dos call [v2.29] sub di,cx ; back up to start byte [v2.29] sub di,cx ; .. after word moves [v2.29] mov ah,0ffh ; topview synch request [v2.29] int screen ; call him [v2.29] tvsyn1: ret ; [v2.29] scrsync endp ; [v2.29] ; wait for retrace so can write to screen memory scrwait proc near cmp tv_mode,1 ; topview mode? [v2.29] je scrwa3 ; yes, no waiting [v2.29] cmp crt_mode,7 ; bw mode? je scrwa3 ; yes, no waiting push dx mov dx,crt_status scrwa1: in al,dx test al,disp_enb ; display enable? jnz scrwa1 ; yes, keep waiting scrwa2: in al,dx test al,disp_enb ; now wait for it to go off jz scrwa2 ; so can have whole cycle pop dx scrwa3: ret ; that was easy... scrwait endp code ends if1 %out [End of pass 1] else %out [End of assembly] endif end