name msxapc ; File MSXAPC.ASM ; Last modification: 20 April 1986 ; System dependent module for NEC APC ; ************ START MSXAPC.ASM *********************************** ; Kermit system dependent module for NEC Advanced Personal Computer (APC) ; Ron Blanford, University of Washington, August 1984 ; Added global entry point vstat for use by Status in mssset. ; Also trimmed off trailing commas in publics. Joe R. Doupnik 12 March 1986 ; Add global procedures ihosts and ihostr to handle host initialization ; when packets are to be sent or received by us,resp. 24 March 1986 ; Add global procedure dtrlow (with discon in place of serhng) to force DTR ; and RTS low in response to Kermit command Hangup. [jrd] ; Add global procedure Dumpscr, called by Ter in file msster, to dump screen ; to a file. Just does a beep for now. 13 April 1986 [jrd] ; Include modifications by Ron Blanford to Dobaud and Getbaud. 14 April 1986 ; In proc Outchr add override of xon from chkxon sending routine. ; This makes a hand typed Xoff supress the xon flow control character sent ; automatically as the receiver buffer empties. 20 April 1986 [jrd] ; ; ; Modified to get key scan codes directly from fifo buffer in IO.SYS ; This version works with MS-DOS Versions 2.00 (000) and 2.11 (001); it ; should work with future MS-DOS revisions so long as NECIS maintains ; the configuration table in IO.SYS - the thing most apt to fail is the ; key autorepeat because kbrepflg is furthest from a hook in config table. ; If this happens the equate for offset of kbrepflg will need adjusting. ; Ian Gibbons, University of Hawaii, 10/26/84 ; Fixed incorrect timer command port assignment so that program will run ; under MS-DOS 2.00 as well as 2.11. ; Added autodetermination of color/mono using crttype byte in IO.SYS; this ; works only under DOS 2.11 (and future above ?) ; IG 10/28/84 ; Added direct CRT I/O handling through BIOS in order to increase speed and ; flexibility of help menus. It gives only an imitation window but it works ; fairly well. ; IG 10/31/84 ; Added simplified mode line data to the [Connecting to host...]" line which ; has been moved here from MSTERM in order to have more system dependent ; control. ; IG 11/4/84 ; Make unredefined break/stop key act as a scroll/noscroll key in telnet by ; sending alternately a ^S and a ^Q. ; IG 11/5/84 ; By now code for this MSXAPC module could well be divided into separate ; transmission and terminal modules, but I don't want to spend more time ; on it. It's not as readable as it might be, but so far as I know ; everything works.IG 11/6/84 public serini, serrst, clrbuf, outchr, coms, vts, vtstat, dodel public ctlu, cmblnk, locate, lclini, lclrst, prtchr, dobaud public discon, clearl, dodisk, getbaud, beep public count, xofsnt, puthlp, putmod, clrmod, poscur public sendbr, term, machnam, setktab, setkhlp, showkey public ihosts, ihostr, dtrlow, dumpscr ; [jrd] include mssdef.h false equ 0 true equ 1 BIOS EQU 0DCH ; NEC-APC BIOS interrupt call fifoclr equ 6 ; clears keyboard fifo buffer crtcmd equ 7 ; Direct CRT I/O command call ; port assignments for 8251 serial controllers ; Standard interface mndata equ 30H ; Data port (read/write) mnst1a equ 32H ; Status port (when read) mncmda equ 32H ; Command port (when written) mnst2a equ 34H ; Alternate status port (when read) mnmska equ 34H ; Mask port (when written) mntdca equ 36H ; Transmit disable port (write only) ; Optional (H14) interface mndatb equ 31H ; Data port (read/write) mnst1b equ 33H ; Status port (when read) mncmdb equ 33H ; Command port (when written) mnst2b equ 35H ; Alternate status port (when read) mnmskb equ 35H ; Mask port (when written) mntdcb equ 37H ; Transmit disable port (write only) ; Status bits from mnst1 txrdy equ 01H ; Bit for output ready. rxrdy equ 02H ; Bit for input ready. ; Command values for mncmd ccmd equ 37H ; RTS & DTR high, RX & TX enabled, reset ERR cbrk equ 08H ; break enabled cmode equ 40H ; enable mode reset mmode equ 4EH ; 16x rate, 8 data, no parity, 1 stop ; Mask values for mnmsk txmsk equ 01H ; disables transmit ready interrupt rxmsk equ 02H ; disables receive ready interrupt tbemsk equ 04H ; disables transmit buffer empty interrupt ; port assignments for 8253 timers ; Standard interface tmdata equ 2BH ; data port tmcmda equ 2FH ; command port (Was 27H Ian 10/27/84) ; Optional (H14) interface tmdatb equ 61H ; data port tmcmdb equ 67H ; command port ; values for tmcmd which select timer channel and mode tmsela equ 76H ; Channel 1, mode 3 (standard port) tmselb equ 36H ; Channel 0, mode 3 (optional port) ; Timer information for current port selection tmrinfo struc tmdat dw 0 ; data port tmcmd dw 0 ; command port tmsel db 0 ; byte which selects channel and mode tmrinfo ends ; port assignments for 8259 interrupt controllers ; Standard interface intcmda equ 20H ; Command port (master controller) intmska equ 22H ; Mask port ictmsk equ 08H ; Timer interrupt mask (to master) icsmska equ 02H ; Standard serial interrupt mask (to master) icsvcta equ 11H ; Interrupt vector for standard interface ; Optional (H14) interface ; The interrupt request vector for the optional (H14) serial interface is ; jumper-selectable to any of vectors IR2, IR5, IR8, or IR12. NEC recommends ; that IR8 be used, so that has been selected as the default here. To use ; any of the other vectors, set the following conditionals appropriately. ; Only one of the following should be true: IR2 equ false ; interrupt vector 2 IR5 equ false ; interrupt vector 5 IR8 equ true ; interrupt vector 8 IR12 equ false ; interrupt vector 12 IF IR2 intcmdb equ 20H ; Command port (master controller) intmskb equ 22H ; Mask port icsmskb equ 04H ; Interrupt mask icsvctb equ 12H ; Interrupt table index ENDIF IF IR5 intcmdb equ 20H ; Command port (master controller) intmskb equ 22H ; Mask port icsmskb equ 20H ; Interrupt mask icsvctb equ 15H ; Interrupt table index ENDIF IF IR8 intcmdb equ 28H ; Command port (slave controller) intmskb equ 2AH ; Mask port icsmskb equ 02H ; Interrupt mask icsvctb equ 19H ; Interrupt table index ENDIF IF IR12 intcmdb equ 28H ; Command port (slave controller) intmskb equ 2AH ; Mask port icsmskb equ 20H ; Interrupt mask icsvctb equ 1DH ; Interrupt table index ENDIF crtinfo struc ; Structure for color/mono formatting info nrmseq db esc,'[0m$' ; Default invseq db esc,'[7m$000' ; Inverse (extra space for color) bldseq db esc,'[17m$' ; Bold video nrmcrt dw 8080H ; Normal data for direct crt i/o bldcrt dw 9090H ; Inverse data for direct crt i/o crtinfo ends icEOI equ 20H ; generic end of interrupt for intcmd kbfifosiz equ 64 ; size of fifo buffer in IO.SYS ; miscellaneous constants ctrlP equ 10H ; ^P. ctrl_q equ 11H ; ^Q ctrl_s equ 13H ; ^S printkey equ 00FFH ; Scan code of unshifted PRINT key. brkstp equ 0096H ; Scan code of unshifted break/stop key. mntrgh equ bufsiz*3/4 ; High XON/XOFF trigger = 3/4 of buffer full. ; external variables used: ; drives - # of disk drives on system ; flags - global flags as per flginfo structure defined in pcdefs ; trans - global transmission parameters, trinfo struct defined in pcdefs ; portval - pointer to current portinfo structure (currently either port1 ; or port2) ; port1, port2 - portinfo structures for the corresponding ports ; global variables defined in this module: ; xofsnt, xofrcv - tell whether we saw or sent an xoff. datas segment public 'datas' extrn drives:byte,flags:byte, trans:byte extrn portval:word, port1:byte, port2:byte, dmpname:byte ; [jrd] machnam db 'NEC APC$' nyimsg db cr,lf,'Not implemented$' badbd db cr,lf,'Unimplemented baud rate$' bdkscn db cr,lf,'Unable to install key translate table' db cr,lf,'Possibly incompatible MS-DOS version',cr,lf db cr,lf,'(MS-KERMIT can be used without keyboard translation' db ' feature)',cr,lf,'$' nokbtr db cr,lf,'No key translation table is installed$' hngmsg db cr,lf,' The phone should have hungup.',cr,lf,'$' ; [jrd] hnghlp db cr,lf,' The modem control lines DTR and RTS for the current' db ' port are forced low (off)' db cr,lf,' to hangup the phone. Normally, Kermit leaves them' db ' high (on) when it exits.' db cr,lf,'$' ; [jrd] rdbuf db 80 dup (?) ; temp buf [jrd] prtmsg db cr,lf,'You cannot redefine the PRINT key$' escmsg db cr,lf,'You cannot redefine the current CTRL-ESCAPE key$' hngcfm db cr,lf,'Please confirm DISCONNECT command. (Y/N) $' dismsg db cr,lf,'Disconnecting for 3 seconds',cr,lf,'$' rcnmsg db cr,lf,'Reconnected',cr,lf,'$' cnmsg db cr,lf,'Connecting to host at ' cnmsgb db ' ' db ' baud on port ' cnmsgp db ' ',cr,lf,'(Type $' cnmsg1 db ' C to return to PC)',cr,lf,lf,lf,'$' crlf db cr,lf,'$' delstr db BS,BS,' ',BS,'$' ; Delete string. clrlin db cr,'$' ; Clear line (just the cr part). ceolseq db esc,'[K$' ; Clear to end of line cpseq db esc,'=rc' ; rc replaced by row and column before display clrseq db 01EH,01AH,'$' ; Home cursor and clear screen lstpos dw 0 ; column position for printer echoing ; Storage for color and mono formatting strings nrmcol db esc,'[0m$' invcol db esc,'[3;21m$' ; Yellow with green overline bldcol db esc,'[21m$' ; Yellow ncrtcol dw 8080H ; Green bcrtcol dw 0A0A0H ; Yellow ;nrmmon db esc,'[0m$' ;invmon db esc,'[7m$000' ; Inverse video ;bldmon db esc,'[17m$' ; Bold video ;ncrtmon dw 8080H ; Normal green ;bcrtmon dw 9090H ; Inverse video formdat crtinfo <> formdtl db $-formdat nocur db esc,'[>5h$' ; Disable cursor recur db esc,'[>5l$' ; Reenable cursor storcur db esc,'[s$' ; Store cursor position rstcur db esc,'[u$' ; Restore cursor position upcur db esc,'[A$' ; Move cursor up one line savcur dw 0 ; storage for cursor position msglns db 0 ; No of lines in help message rnoc dw 0 ; Raw NOC for screen write ; Data area for direct CRT I/O EVEN ; Force alignment to word boundary dispadr db 2000 dup (0) ; Storage for string data attradr db 2000 dup (0) ; Storage for attribute data ; Command block for direct CRT I/O cmd db 0 ; CRT command number la db 0 ; Line address (0-24 binary) ca db 0 ; Column address (0-79 binary) noc dw 0 ; Number of chars (0-2000 binary) dispptr dw dispadr ; Pointer to string data block dispseg dw datas ; attrptr dw attradr ; Pointer to attribute data block attrseg dw datas ourarg termarg <> modem mdminfo timer tmrinfo ourflgs db 0 ; Flags for telnet options fprint equ 80H ; echo screen output to printer movcur equ 40H ; cursor moved - needs resetting kbtrflg equ 20H ; local flag showing if kb trans.is enabled autorepflg equ 10H ; is this an autorepeat cycle inited equ 8H ; are we initiating first call to term tlnxof equ 4H ; have we sent a ^S in telnet with brk/stp savscn dw 0 ; save last key-in for auto repeat if needed escscan dw 0 ; scan code for current ctrl-escape key oldsera dw ? ; old serial handler for standard port oldsega dw ? ; segment of above oldmska db ? ; old interrupt controller mask portina db 0 ; Has comm port been initialized. oldserb dw ? ; old serial handler for optional port oldsegb dw ? ; segment of same. oldmskb db ? ; old interrupt controller mask portinb db 0 ; Has comm port been initialized. dosseg dw 40H ; Segment to read IO.SYS zero dw 0 ; Use to load segment regs for low core ; Space for addresses in IO.SYS to be calculated at run time in 'lclini' configptr equ 3H ; Offset of pointer to base of config table configbas dw 0 ; base address of config table in IO.SYS fifobas dw 0 ; equ 0A62H for DOS 2.11 ; offsets from configbas statlnptr equ 0CH kbinptr equ 2CH ; kbin addr = base of fifodata area (fifobas) crtptr equ 48H ; offsets from fifobas kbin equ 0 ; fifobas kbout equ 1 ; fifobas + 1 kbfifo equ 2 ; fifobas + 2 sfkeyflg equ 043H ; fifobas + 043H kbrepflg equ 0D9H ; fifobas + 0D9H xofsnt db 0 ; Say if we sent an XOFF. xofrcv db 0 ; Say if we received an XOFF. ; variables for serial interrupt handler source db bufsiz DUP (?) ; Buffer for data from port. srcpnt dw 0 ; Pointer in buffer (DI). count dw 0 ; Number of chars in int buffer. savesi dw 0 ; Save SI register here. dw 80 DUP (?) ; local stack for interrupt processing mnstk dw ? mnsp dw ? ; remote stack info mnsseg dw ? shkbuf db 300 dup (?) ; room to display key definition shkmsg db ' Scan code: ' shkmln equ $-shkmsg shkms1 db cr,lf,' Definition: ' shkm1ln equ $-shkms1 setktab db 24 mkeyw 'BACKSPACE',9CH mkeyw 'F1',80H mkeyw 'F2',81H mkeyw 'F3',82H mkeyw 'F4',83H mkeyw 'F5',84H mkeyw 'F6',85H mkeyw 'F7',86H mkeyw 'F8',87H mkeyw 'F9',88H mkeyw 'F10',89H mkeyw 'F11',8AH mkeyw 'F12',8BH mkeyw 'F13',8CH mkeyw 'F14',8DH mkeyw 'F15',8EH mkeyw 'F16',8FH mkeyw 'F17',90H mkeyw 'F18',91H mkeyw 'F19',92H mkeyw 'F20',93H mkeyw 'F21',94H mkeyw 'F22',95H mkeyw 'SCAN',-1 setkhlp db cr,lf,'Either keyname: Backspace, F1, ...,F22',cr,lf db ' or "SCAN" followed by scan code' db ' (given by SHOW KEY)',cr,lf,'$' comptab db 7 mkeyw '1',1 mkeyw '2',0 mkeyw 'COM1',1 mkeyw 'COM2',0 mkeyw 'H14',0 mkeyw 'OPTIONAL',0 mkeyw 'STANDARD',1 bddat label word dw 0D30H ; 45.5 baud dw 0C00H ; 50 baud dw 0800H ; 75 baud dw 0574H ; 110 baud dw 0476H ; 134.5 baud dw 0400H ; 150 baud dw 0200H ; 300 baud dw 0100H ; 600 baud dw 0080H ; 1200 baud dw 0055H ; 1800 baud dw 004DH ; 2000 baud dw 0040H ; 2400 baud dw 0020H ; 4800 baud dw 0010H ; 9600 baud dw 0008H ; 19200 baud dw 0004H ; 38400 baud (not tested - may not work) ; some static data for mode line unkbaud db ' Unk ' ; must be 5 chars... baudn db ' 45.5' ; [g4 start] 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 '19200' ; [g4 end] baudnsiz equ 15 ; # of baud rates known (tbl size / 5) datas ends code segment public 'code' extrn comnd:near, dopar:near, escprt:near extrn sleep:near ; [jrd] assume cs:code,ds:datas ; local initialization routine, called by Kermit initialization. LCLINI PROC NEAR cld mov flags.vtflg,0 ; turn off heath emulation push es mov es,dosseg mov bx,configptr mov bx,es:[bx] ; Get offset of configuration table in IO.SYS mov configbas,bx ; store it mov cl,fifoclr ; Clear fifobuffer in case user has been int bios ; typing while program was loading mov bx,es:[bx].kbinptr ; Should be address of kbin mov cl,es:[bx] ; Confirm we're in right location cmp cl,byte ptr es:1[bx] ; by showing kbin = kbout jne lclin1 ; Fails for DOS 2.00 (no kbinptr) mov fifobas,bx ; Kbin is base of fifo area or ourflgs,kbtrflg ; Show we have a key tranlate table jmp short lclin2 ; Localization finished lclin1: mov bx,configbas ; DOS 2.00 does have pointer to status line mov bx,es:[bx].statlnptr ; Address of 'MS-DOS' in status line sub bx,6BH ; Work back to where kbin should be mov cl,es:[bx] ; Confirm we're in right location cmp cl,byte ptr es:1[bx] ; by showing kbin = kbout jnz lclin3 ; Nothing else to try - report failure mov fifobas,bx ; We've made it! or ourflgs,kbtrflg ; Show we have a key tranlate table xor ax,ax ; Set zero flag for mono format default jmp short lcli20 lclin2: mov bx,configbas mov bx,es:[bx].crtptr ; Get address of crttype byte mov al,es:[bx] ; get the crttype byte test al,1 ; Is it a color crt? lcli20: mov dx,ds ; DOS 2.00 has no crt data and mov es,dx ; and defaults to mono mov cl,formdtl ; Length of crt format block mov di,offset formdat ; Where we want data jnz lcli21 ; No, it's color jmp short lclin4 lcli21: mov si,offset nrmcol ; Source of color data rep movsb ; Move data to working location jmp short lclin4 lclin3: mov dx,offset bdkscn call tmsg and ourflgs,not kbtrflg ; Show no translate table enabled lclin4: mov dx,offset formdat.nrmseq ; set to our normal background color call tmsg pop es ret LCLINI ENDP ; Local reset routine, called upon exit from Kermit LCLRST PROC NEAR ret LCLRST ENDP ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. The only problem is that a value of two ; is returned for single drive systems to be consistent ; with the idea of the system having logical drives A and ; B. Returns normally. DODISK PROC NEAR mov ah,gcurdsk ; current disk value to AL. int dos mov dl,al ; put current disk in DL. mov ah,seldsk ; select current disk. int dos ; get number of drives in AL. mov drives,al ret DODISK ENDP ; show the definition of a key. The terminal argument block (which contains ; the address and length of the definition tables) is passed in ax. ; Returns a string to print in AX, length of same in CX. ; Returns normally. ; In this version, the complete untranslated key scan codes are obtained ; from the fifo buffer in IO.SYS. The fifo buffer pointers are then updated ; to show that the key has been read. Certain key scan codes which are ; intercepted in the kb interrupt routine give a blank or functional response ; to SHOW KEY (eg FNC + CTRL + BREAK-STOP, PRINT, and CTRL + 0...9 ) ; and these cannot be translated. SHOWKEY PROC NEAR push es test ourflgs,kbtrflg jnz showk0 push ax ; Keep stack balanced mov bx,ax and [bx].flgs,not havtt ; reset flag mov dx,offset nokbtr ; Inform no table installed and return jmp short shoerr showk0: push ax ; save the terminal argument block mov al,trans.escchr ; calculate scan code for ctrl-escape key add al,40H ; uncontrolify escape char mov ah,2 ; control byte mov escscan,ax ; save it showk1: call inscan ; get key-in scan code from IO.SYS jmp short showk1 ; Nothing there yet - keep trying nop call inckbo ; increment kbout pointer cmp al,printkey ; Is it the print key (any version) ? jnz show11 mov dx,offset prtmsg ; If so complain jmp short shoerr show11: cmp al,byte ptr escscan ; Is it current ctrl-escape key ? jnz show12 test ah,02 ; With ctrl + anything jz show12 mov dx,offset escmsg ; Then complain jmp short shoerr show12: cld mov cx,ds mov es,cx push ax ; save scan code mov di,offset shkbuf ; move 'Scan code' message to buffer mov si,offset shkmsg mov cx,shkmln rep movsb call nout ; add scan code to buffer mov si,offset shkms1 ; move 'Definition' message to buffer mov cx,shkm1ln rep movsb pop ax ; retrieve scan code pop bx ; and terminal argument block mov cx,[bx].klen ; length of translation table jcxz showk3 ; no table, key not defined push di mov di,[bx].ktab ; get table address repne scasw ; look for scan code mov si,di pop di jne showk3 ; not defined sub si,[bx].ktab ; compute entry offset in table sub si,2 add si,[bx].krpl ; index to replacement mov si,[si] ; get its address mov cl,[si] ; get its length mov ch,0 inc si rep movsb ; transfer replacement to display buffer showk3: mov ax,offset shkbuf ; return address of buffer in ax mov cx,di ; and length in cx sub cx,ax pop es ret shoerr: call tmsg mov cx,0 ; pop ax ; get rid of junk pop es ret SHOWKEY ENDP ; copy numeric value from AX to ASCII buffer indicated by DI. DI is updated. NOUT PROC NEAR mov dx,0 ; zero high word mov bx,10 ; divide div bx push dx ; save remainder digit or ax,ax ; anything left? jz nout1 ; no, start output phase call nout nout1: pop ax ; retrieve a digit add al,'0' ; make it ASCII stosb ; put it in buffer ret NOUT ENDP ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. PRTCHR PROC NEAR call chkxon ; see if we have to xon the host. cmp count,0 jnz prtch2 jmp rskp ; No data - check console. prtch2: pushf ; save current interrupt value cli ; disable interrupts while manipulating pointers mov si,savesi lodsb ; get a byte cmp si,offset source + bufsiz ; bigger than buffer? jb prtch1 ; no, keep going mov si,offset source ; yes, wrap around prtch1: dec count mov savesi,si mov dx,count ; return # of chars in buffer popf ; restore original interrupt flag ret PRTCHR ENDP ; local routine to see if we have to transmit an xon CHKXON PROC NEAR push bx mov bx,portval cmp [bx].floflg,0 ; doing flow control? je chkxo1 ; no, skip all this cmp xofsnt,false ; have we sent an xoff? je chkxo1 ; no, forget it cmp count,mntrgh ; below trigger? jae chkxo1 ; no, forget it mov ax,[bx].flowc ; ah gets xon call outchr ; send it nop ; ignore failure nop nop mov xofsnt,false ; remember we've sent an xon. chkxo1: pop bx ; restore register ret ; and return CHKXON ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. OUTCHR PROC NEAR mov bp,portval cmp ds:[bp].floflg,0 ; Are we doing flow control. je outch2 ; No, just continue. sub cx,cx ; clear counter cmp ah,byte ptr [bp].flowc ; sending xoff? [jrd] jne outch1 ; ne = no mov xofsnt,false ; supress xon from chkxon buffer routine outch1: cmp xofrcv,true ; Are we being held? jne outch2 ; No - it's OK to go on. loop outch1 ; held, try for a while mov xofrcv,false ; timed out, force it off and fall thru. outch2: push dx ; Save register. sub cx,cx mov al,ah ; Parity routine works on AL. call dopar ; Set parity appropriately. mov ah,al ; Don't overwrite character with status. mov dx,modem.mdstat ; port status register outch3: in al,dx test al,txrdy ; Transmitter ready? jnz outch4 ; Yes loop outch3 jmp outch5 ; Timeout outch4: mov al,ah ; Now send it out mov dx,modem.mddat out dx,al pop dx jmp rskp outch5: pop dx ret OUTCHR ENDP ; IHOSTS - Initialize the host by sending XON, or equivalent, and enter the ; cycle of clear input buffer, wait 1 second, test if buffer empty then exit ; else repeat cycle. Requires that the port be initialized before hand. ; Ihosts is used by the local send-file routine just after initializing ; the serial port. ; 22 March 1986 [jrd] IHOSTS PROC NEAR push ax ; save the registers push bx push cx push dx mov bx,portval ; port indicator mov ax,[bx].flowc ; put Go-ahead flow control char in ah call outchr ; send it (release Host's output queue) nop ; outchr can do skip return nop nop ihosts1:call clrbuf ; clear out interrupt buffer mov ax,1 ; sleep for 1 second call sleep ; procedure sleep is in msscom.asm call prtchr ; check for char at port jmp ihosts1 ; have a char in al, repeat wait/read cycle nop ; prtchr does skip return on empty buffer pop dx ; empty buffer. we are done here. pop cx pop bx pop ax ret IHOSTS ENDP ; IHOSTR - initialize the remote host for our reception of a file by ; sending the flow-on character (XON typically) to release any held ; data. Called by receive-file code just after initializing the serial ; port. 22 March 1986 [jrd] IHOSTR PROC NEAR push ax ; save regs push bx push cx mov bx,portval ; port indicator mov ax,[bx].flowc ; put Go-ahead flow control char in ah call outchr ; send it (release Host's output queue) nop ; outchr can do skip return nop nop pop cx pop bx pop ax ret IHOSTR ENDP DTRLOW PROC NEAR ; Global proc to Hangup the Phone by making ; DTR and RTS low. mov ah,cmtxt ; allow text to be able to display help mov bx,offset rdbuf ; dummy buffer mov dx,offset hnghlp ; help message call comnd ; get a confirm jmp r call discon ; try using preexisting routine ; [jrd] call serhng ; drop DTR and RTS ; [jrd] mov ah,prstr ; give a nice message ; [jrd] mov dx,offset hngmsg ; [jrd] int dos jmp rskp DTRLOW ENDP ; Hang up the Phone. Similar to SERRST except it just forces DTR and RTS low ; to terminate the connection. 29 March 1986 [jrd] ; Calling this twice without intervening calls to serini should be harmless. ; Returns normally. ; SERHNG is Not Yet Implemented. Use existing procedure discon below. ; Send a break out the current serial port. Returns normally. SENDBR PROC NEAR mov dx,modem.mdcom ; send to command port mov al,cbrk+ccmd ; add break to normal command out dx,al sub cx,cx ; wait a while sndbr1: loop sndbr1 mov al,ccmd ; restore normal command out dx,al ret ; and return. SENDBR ENDP DISCON PROC NEAR mov dx,offset hngcfm call besure ; Get confimation of command jmp short discn1 nop mov dx,offset dismsg ; Say what we're doing call tmsg mov al,CCMD xor al,2 ; Reset bit to drop DTR. mov dx,modem.mdcom out dx,al mov bx,05H ; Set outer counter 5 X --> 3sec. pause2: xor cx,cx pause3: push bx ; Waste time for 600ms. pop bx loop pause3 ; Loop on inner loop. dec bx jnz pause2 ; Loop on outer loop. or al,2 ; Set bit to enable DTR again. out dx,al mov dx,offset rcnmsg call tmsg discn1: ret DISCON ENDP BESURE PROC NEAR ; Receives addr of prompt in DX. call tmsg mov ah,conin int dos and al,137Q ; Convert to upper case if necessary. cmp al,'Y' jz besur1 ; We must return rskp for a 'Y'/'y' mov dx,offset crlf ; For any other character input send a cr/lf. call tmsg ret ; And return. besur1: jmp rskp BESURE ENDP ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Returns normally. CLRBUF PROC NEAR pushf ; save current interrupt value cli ; disable interrupts mov ax,offset source ; reset pointers to beginning of buffer mov srcpnt,ax mov savesi,ax mov count,0 popf ; restore original interrupt value ret CLRBUF ENDP ; Set the baud rate for the current port, based on the value in the ; portinfo structure. On entry, previous value of baud rate is saved in AX. ; Returns normally. DOBAUD PROC NEAR push ax ; save regs. [jrd] push bx push dx push bp mov bp,portval mov bx,ds:[bp].baud ;make sure new value is valid cmp bx,BAUDSIZ ; [RB] don't get stuck at 1200 baud jae dobd0 ; [RB] shl bx,1 add bx,offset bddat cmp word ptr [bx],0FFH jne dobd1 ; [RB] dobd0: mov ds:[bp].baud,ax ; [RB] replace bad rate with previous value mov dx,offset badbd jmp tmsg dobd1: mov dx,timer.tmcmd ; [RB] timer command port mov al,timer.tmsel ;select proper channel and mode out dx,al mov ax,[bx] ;get timer initializer for this rate mov dx,timer.tmdat ;timer data port out dx,al ;output low byte mov al,ah out dx,al ;output high byte pop bp ; [jrd] pop dx pop bx pop ax ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is called any time the baud rate is to be displayed. ; We can't determine the baud rate on the APC, so just set it ; to the default of 1200 if the value is currently illegal. GETBAUD PROC NEAR push bx ; [jrd] push bp ; Additions by Ron Blanford 14 April 1986 mov bp,portval mov bx,ds:[bp].baud cmp bx,BAUDSIZ jae getbd2 shl bx,1 add bx,offset bddat cmp word ptr [bx],0FFh jne getbd3 getbd2: mov ds:[bp].baud,B1200 getbd3: pop bp pop bx ; [jrd] ret GETBAUD ENDP ; Set the mode for the current port. This is part of the serial ; initialization routine. DOMODE PROC NEAR mov dx,modem.mdcom ;send 3 zeros to command port to reset chip mov al,0 out dx,al mov al,0 out dx,al mov al,0 out dx,al mov al,cmode ;enable mode setting out dx,al push ax ;allow 8251 time to reset pop ax push ax pop ax mov al,mmode ;mode: 16x rate, 8 data, no parity, 1 stop out dx,al mov al,ccmd ;RTS & DTR high, RX & TX enabled, reset errors out dx,al ret DOMODE ENDP ; Reassure user about connection to the host. Tell him what escape ; sequence to use to return and the communications port and baud ; rate being used. [19b] DOMSG PROC NEAR push ax ; save regs. [jrd] push bx push cx push si mov bx,offset ourarg ; get argument block mov al,[bx].baudb ; get baud bits mov si,offset unkbaud ; Assume unknown baud. cmp al,baudnsiz ; too big? jnb dmsg12 ; yes, use default mov cl,2 ; each is 5 bytes long shl al,cl ; 4 X add al,[bx].baudb ; make 4+1 = 5 mov ah,0 add ax,offset baudn mov si,ax dmsg12: mov cx,5 ; length of baud space mov di,offset cnmsgb rep movsb ; copy in baud rate mov al,'1' cmp ourarg.prt,1 ; One means port 1 je dmsg15 ; yes, keep going mov al,'2' ; Zero means port 2 dmsg15: mov cnmsgp,al ; fill in port number mov dx,offset cnmsg call tmsg call escprt ; in MSSET mov dx,offset cnmsg1 call tmsg pop si ; [jrd] pop cx pop bx pop ax ret DOMSG ENDP ; set the current port. COMS PROC NEAR mov dx,offset comptab ;get port selection mov bx,0 mov ah,cmkey call comnd jmp r push bx mov ah,cmcfm ;get a confirmation call comnd jmp comx nop pop bx mov flags.comflg,bl ;save port selection cmp flags.comflg,1 jne coms2 mov ax,offset port1 ;set to run on port 1 mov portval,ax call resetb ;reset port 2, if in use call inita ;set up port 1 ret coms2: mov ax,offset port2 ;set to run on port 2 mov portval,ax call reseta ;reset port 1, if in use call initb ;set up port 2 ret comx: pop bx ret COMS ENDP ; initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; Returns normally. SERINI PROC NEAR cmp flags.comflg,1 jne seri2 call resetb call inita ret seri2: call reseta call initb ret SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Returns normally. SERRST PROC NEAR call reseta ;reset port 1 call resetb ;reset port 2 mov dx,offset recur ; Reenable cursor display call tmsg and ourflgs,not inited ; Reset init flag for term usage ret SERRST ENDP ; Local routine to initialize the standard serial port INITA PROC NEAR cmp portina,1 ; Did we initialize port already? [21c] je inita0 ; Yes, so just leave. [21c] push es cli ; Disable interrupts mov ax,offset port1 mov portval,ax xor ax,ax ; Address low memory mov es,ax mov ax,es:[4*icsvcta] ; save standard port interrupt vector mov oldsera,ax mov ax,es:[4*icsvcta+2] mov oldsega,ax mov ax,offset serint ; point to our routine mov es:[4*icsvcta],ax ; point at our serial routine mov es:[4*icsvcta+2],cs ; our segment mov dx,intmska ; set up standard port... in al,dx mov oldmska,al ; save old master controller mask ; NEC recommends that the timer interrupt be disabled during interrupt- ; driven serial I/O, but this disables the clock display and keyboard ; repeat. I have not had any problems leaving it enabled, so I will ; leave it alone here. If problems develop, uncomment the following ; line to disable timer interrupts. -- RonB ; or al,ictmsk ; disable timer interrupt and al,not icsmska ; enable serial interrupt at master controller out dx,al mov dx,mnmska ; enable serial interrupt at port mov al,txmsk+tbemsk ; disable tx and tbe interrupts (enable rx) out dx,al mov dx,mntdca ; enable operation of serial port mov al,0 out dx,al mov modem.mddat,mndata mov modem.mdstat,mnst1a mov modem.mdcom,mncmda mov timer.tmdat,tmdata mov timer.tmcmd,tmcmda mov timer.tmsel,tmsela call domode call dobaud mov portina,1 ; Remember port has been initialized. call clrbuf ; Clear input buffer. sti ; Allow interrupts pop es inita0: ret INITA ENDP ; Local routine to initialize the optional (H14) serial port INITB PROC NEAR cmp portinb,1 ; Did we initialize port already? [21c] je initb0 ; Yes, so just leave. [21c] push es cli ; Disable interrupts mov ax,offset port2 mov portval,ax xor ax,ax ; Address low memory mov es,ax mov ax,es:[4*icsvctb] ; save optional port interrupt vector mov oldserb,ax mov ax,es:[4*icsvctb+2] mov oldsegb,ax mov ax,offset serint ; point to our routine mov es:[4*icsvctb],ax ; point at our serial routine mov es:[4*icsvctb+2],cs ; our segment mov dx,intmskb ; set up optional port... in al,dx mov oldmskb,al ; save old master or slave controller mask and al,not icsmskb ; enable serial interrupt at controller out dx,al mov dx,mnmskb ; enable serial interrupt at port mov al,txmsk+tbemsk ; disable tx and tbe interrupts (enable rx) out dx,al mov dx,mntdcb ; enable operation of serial port mov al,0 out dx,al mov modem.mdstat,mnst1b mov modem.mddat,mndatb mov modem.mdcom,mncmdb mov timer.tmdat,tmdatb mov timer.tmcmd,tmcmdb mov timer.tmsel,tmselb call domode call dobaud mov portinb,1 ; Remember port has been initialized. call clrbuf ; Clear input buffer. sti ; Allow interrupts pop es initb0: ret INITB ENDP ; Reset standard serial port RESETA PROC NEAR cmp portina,0 ; Did we reset port already? je rsta0 ; Yes, so just leave. push es cli ; Disable interrupts xor ax,ax ; Address low memory mov es,ax mov ax,oldsera ; Restore interrupt vector mov es:[4*icsvcta],ax mov ax,oldsega mov es:[4*icsvcta+2],ax mov dx,intmska ; restore old master controller mask mov al,oldmska out dx,al mov dx,mnmska ; disable serial interrupts at port mov al,txmsk+rxmsk+tbemsk out dx,al mov portina,0 ; Remember port has been reset sti ; Allow interrupts pop es rsta0: ret RESETA ENDP ; Reset optional (H14) serial port RESETB PROC NEAR cmp portinb,0 ; Did we reset port already? je rstb0 ; Yes, so just leave. push es cli ; Disable interrupts xor ax,ax ; Address low memory mov es,ax mov ax,oldserb ; Restore interrupt vector mov es:[4*icsvctb],ax mov ax,oldsegb mov es:[4*icsvctb+2],ax mov dx,intmskb ; restore old slave controller mask mov al,oldmskb out dx,al mov dx,mnmskb ; disable serial interrupts at port mov al,txmsk+rxmsk+tbemsk out dx,al mov portinb,0 ; Remember port has been reset sti ; Allow interrupts pop es rstb0: ret RESETB ENDP ; serial port interrupt routine. This is not accessible outside this ; module, handles serial port receiver interrupts. SERINT PROC NEAR push ds ; save these on remote stack push ax mov ax,seg datas ; get our own data segment mov ds,ax mov mnsp,sp ; save remote stack information mov mnsseg,ss mov sp,offset mnstk ; switch to local stack mov ss,ax push es ; and save remaining registers push bp push di push si push dx push cx push bx mov es,ax call mnproc ; process the interrupt mov al,icEOI cmp flags.comflg,1 ; If using standard port je intr1 mov dx,intcmdb ; or H14 vectored to master cmp dx,intcmda je intr1 ; only signal End of Interrupt to master, out dx,al ; otherwise signal to both slave and master. intr1: mov dx,intcmda out dx,al pop bx ; restore registers from stack pop cx pop dx pop si pop di pop bp pop es mov ax,mnsseg ; switch back to remote stack mov ss,ax mov ax,mnsp mov sp,ax pop ax pop ds iret ; handler for serial input mnproc: cld mov di,srcpnt ; get buffer pointer mov dx,modem.mdstat ; is data available? in al,dx test al,rxrdy jz mnpro7 mov dx,modem.mddat ; read data in al,dx or al,al jz mnpro7 ; Ignore nulls. cmp al,7FH ; Ignore rubouts, too. jz mnpro7 mov ah,al and ah,7fH ; only consider low-order 7 bits for flow ctl. mov bp,portval cmp ds:[bp].floflg,0 ; Doing flow control? je mnpro4 ; Nope. mov bx,ds:[bp].flowc ; Flow control char (BH = XON, BL = XOFF). cmp ah,bl ; Is it an XOFF? jne mnpro3 ; Nope, go on. mov xofrcv,true ; Set the flag. jmp short mnpro7 mnpro3: cmp ah,bh ; Get an XON? jne mnpro4 ; No, go on. mov xofrcv,false ; Clear our flag. jmp mnpro7 mnpro4: stosb cmp di,offset source + bufsiz jb mnpro5 ; not past end... mov di,offset source ; wrap buffer around mnpro5: mov srcpnt,di ; update ptr inc count cmp ds:[bp].floflg,0 ; Doing flow control? je mnpro7 ; No, just leave. cmp xofsnt,true ; Have we sent an XOFF? je mnpro7 ; Yes. cmp count,mntrgh ; Past the high trigger point? jbe mnpro7 ; No, we're within our limit. mov ah,bl ; Get the XOFF. call outchr ; Send it. nop ; ignore failure. nop nop mov xofsnt,true ; Remember we sent it. mnpro7: ret SERINT ENDP ; Dumb terminal emulator. Anyone wishing to enhance it is encouraged ; to do so. TERM PROC NEAR push es test ourflgs,inited ; Have we been here before jnz term01 ; if so, skip this stuff or ourflgs,inited ; show we've been here test ourflgs,kbtrflg jnz term0 mov bx,ax and [bx].flgs,not havtt ; If no table then reset flag term0: mov si,ax ; save argument block locally mov di,offset ourarg mov ax,ds mov es,ax mov cx,size termarg rep movsb mov al,trans.escchr ; Calculate scan code for cntrl-escape char add al,40H ; Uncontollify escape char mov ah,02H ; Control byte for escape scan code mov escscan,ax ; save it call domsg ; tell user how we're connecting. term01: test ourflgs,movcur ; Do we need to reset cursor position jz term1 mov dx,offset cmd mov cx,savcur mov word ptr la,cx mov noc,0 mov cmd,1 ; Bios move cursor call mov cl,crtcmd int bios and ourflgs, not movcur term1: call prtchr ; Serial port input processor jmp short term2 ; ...have a char nop jmp term4 ; no char, continue term2: and al,7FH ; only use ASCII in terminal mode push ax mov dl,al mov ah,conout int dos ; display char pop ax test ourarg.flgs,capt ; are we capturing output? jz term3 push ax call ourarg.captr pop ax term3: test ourflgs,fprint ; are we echoing to printer? jz term4 call lstchr term4: test ourflgs,kbtrflg jz term50 call inscan jmp short term1 nop cmp al,printkey ; All shifts of print key do special duty. jne term41 cmp ah,0 jne term40 ; but toggle printer only if unshifted print xor ourflgs,fprint ; go toggle printer term40: call inckbo ; increment kbout pointer jmp term1 term41: cmp al,byte ptr escscan ; Is it current escape key jne term42 test ah,02 ; with ctrl + anything ? jz term42 call inckbo jmp short termx ; it's ctrl-escape key so just return term42: call trnout ; Returns rskp if char not sent. jmp short term1 ; Translation found and already sent. nop ; no translation so move char via DCONIO cmp ax,brkstp ; is it unredefined/unshifted break/stop key jnz term50 ; if not, just continue mov ax,ctrl_s ; get a ^S ready test ourflgs,tlnxof ; have we already sent a ^S ? jz term43 ; no, we can just set flag and send the ^S mov ax,ctrl_q ; yes, so now we need a ^Q term43: xor ourflgs,tlnxof ; change the flag in either case call sndhst ; send our ^S/ ^Q - IO.SYS 2.11 won't do it mov ah,dconio ; do a dummy read to clear IO.SYS flush flag mov dl,0FFH int dos jmp term1 ; and go back for more term50: mov ah,dconio ; Keyboard input processor mov dl,0FFH int dos ; check console jnz term51 jmp term1 ; no char, continue .Too far for rel jmp. term51: cmp al,ourarg.escc ; is it the escape char? je termx ; allows use of unredef left arrow key to esc call sndhst ; no translation, just send it out ; Check if it is a function key that user has defined in MS-DOS mov es,dosseg mov bx,fifobas cmp es:byte ptr sfkeyflg[bx],0 jnz term50 ; if so, go back to dconio for rest of chars jmp term1 ; and go back for more termx: pop es ret ; do appropriate translations on input key, and transmit ; if translation entry found it sends char(s) to sndhst and returns normally ; if no translation entry is found, returns rskp with unsent scan code in ax ; so that CONIN in IO.SYS can do its translation if neessary. trnout: test ourflgs,kbtrflg ; is there a translation table? jz trnou3 mov cx,ourarg.klen ; get table length and origin mov di,ourarg.ktab push es mov bx,ds mov es,bx jcxz trnou3 ; Needed for case of table zero length repne scasw ; look for key jne trnou3 ; if not found, return rskp sub di,ourarg.ktab ; reset to offset of replacement sub di,2 add di,ourarg.krpl mov si,[di] mov cl,[si] ; get length of replacement mov ch,0 jcxz trnou3 ; if length is zero, send nothing inc si trnou1: lodsb ; get replacement character push si push cx call sndhst ; send it to port pop cx pop si loop trnou1 ; continue until translation complete trnou2: call inckbo ; increment kbout pointer pop es ret ; return after translating and sending trnou3: pop es jmp rskp ; plain characters return rskp ; get key-in scan code from fifo buffer in IO.SYS ; if gets a key-in, skip returns with scan code in ax ; returns normally if no key-in inscan: push es mov es,dosseg ; Address IO.SYS segment with es and ourflgs,not autorepflg ; Reset auto repeat flag mov bx,fifobas ; Offset of fifobas in IO.SYS (from LCLINI) insca1: mov al,es:[bx].kbout ; Get value of kbin pointer cmp al,es:[bx].kbin ; Compare value of kbout pointer jz insca2 ; If equal, no key-in yet so exit sub ah,ah add bx,ax ; Calculate address pointed to (-2) mov ax,es:2[bx] ; Get scan code pointed to by kbout. xchg ah,al ; Get control byte -> ah, data byte -> al. mov savscn,ax ; Save scan data in case key repeat needed. pop es jmp rskp insca2: mov bx,fifobas cmp byte ptr es:[bx].kbrepflg,0 ; Is it time to repeat last key-in jz inscax ; Nope so exit mov ax,savscn ; Get last key-in or ourflgs,autorepflg ; Show we are on auto repeat cycle pop es ; And make it look like new jmp rskp inscax: pop es ret ; increments kbout pointer (with reset to zero) and returns normally ; (on auto repeat cycles resets kbrepflg and returns ) inckbo: push es mov es,dosseg mov bx,fifobas test ourflgs,autorepflg ; new key-in or autorepeat jnz inckb2 mov cl,0 ; Update kbout pointer to fifo cli cmp byte ptr es:[bx].kbout,kbfifosiz-2 ; End of fifo buff ? je inckb1 ; Yes, so start back at beginning mov cl,es:[bx].kbout ; No, just update our place add cl,2 inckb1: mov es:[bx].kbout,cl ; Write back new pointer value sti jmp short inckbx inckb2: mov byte ptr es:[bx].kbrepflg,0 ; Reset autorepeat flag in IO.SYS inckbx: pop es ret ; send character in AL to port, with possible local echo sndhst: push ax mov ah,al call outchr ; send char to port nop ; ...don't care if it fails nop nop pop ax test ourarg.flgs,lclecho ; doing local echo? jz sndhs2 mov dl,al mov ah,conout int dos ; if so, display char sndhs2: ret ; send character to printer. The only special case is the tab, which must ; be expanded to spaces because MS-DOS doesn't. lstchr: cmp al,tab jne lstch2 mov ax,lstpos ; current column position mov cx,8 ; # of spaces = 8 - (column % 8) div cl sub cl,ah add lstpos,cx ; update the column position mov al,' ' lstch1: call lstch4 ; print all the spaces loop lstch1 ret lstch2: cmp al,cr ; CR returns column count to zero jne lstch3 mov lstpos,0 lstch3: cmp al,' ' ; only printable characters are counted jb lstch4 cmp al,del je lstch4 inc lstpos lstch4: mov dl,al ; print the character in any case mov ah,lstout int dos ret TERM ENDP ; Set heath emulation on/off. VTS PROC NEAR mov dx,offset nyimsg jmp tmsg VTS ENDP ; Save the screen to a buffer and then append buffer to a disk file. [jrd] ; Default filename is Kermit.scn; actual file can be a device too. Filename ; is determined by mssset and is passed as pointer dmpname. VTSTAT PROC NEAR ; For Status display [jrd] ret ; no emulator status to display VTSTAT ENDP ; Save the screen to a buffer and then append buffer to a disk file. [jrd] ; Default filename is Kermit.scn; actual file can be a device too. Filename ; is determined by mssset and is passed as pointer dmpname. DUMPSCR PROC NEAR ; Dumps screen contents to a file. Just Beeps here call beep ; [jrd] ret DUMPSCR ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR push ax ; save regs. [jrd] push cx push si cmp dh,25 ; out of range just assumes high value jb poscu1 mov dh,24 poscu1: cmp dl,80 jb poscu2 mov dl,79 poscu2: add dx,2020H ; add offset for ADM cursor addressing mov cpseq+2,dh mov cpseq+3,dl mov si,offset cpseq ; print sequence (ESC=rc) mov cx,4 posc1: lodsb mov dl,al mov ah,conout int dos loop posc1 pop si pop cx ; [jrd] pop ax ret POSCUR ENDP ; Locate; homes cursor position and disables its display. Returns normally. LOCATE PROC NEAR mov dx,offset nocur ; Disable cursor call tmsg mov dx,0 ; Go to top left corner of screen. jmp poscur LOCATE ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov dx,offset delstr ; Erase weird character. jmp tmsg DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov dx,offset clrlin ; this just goes to left margin... call tmsg jmp clearl ; now clear line CTLU ENDP ; Clear to the end of the current line. Returns normally. CLEARL PROC NEAR mov dx,offset ceolseq ; clear sequence jmp tmsg CLEARL ENDP ; This routine blanks the screen and homes the cursor. Returns normally. CMBLNK PROC NEAR mov dx,offset clrseq ; clear screen and home cursor sequence jmp tmsg CMBLNK ENDP ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. PUTMOD PROC NEAR push dx ; preserve message mov dx,24*100H ; line 24 call poscur mov dx,offset formdat.invseq ; put into inverse video call tmsg pop dx ; print the message call tmsg mov dx,offset formdat.nrmseq ; normal video jmp tmsg ; Jump to return PUTMOD ENDP ; clear the mode line written by putmod. Returns normally. CLRMOD PROC NEAR mov dx,24*100H call poscur mov dx,offset ceolseq jmp tmsg CLRMOD ENDP ; Put a help message in a box at the top of the screen. ; This one uses inverse video (or yellow if color) ; Pass the message in ax, terminated by a null. Returns normally. PUTHLP PROC NEAR cld mov dx,ax ; Prepare to pass message to 'getnoc' call getnoc ; mov rnoc,cx ; This is unformatted NOC in message mov cx,5 ; Calculate formatted area needed (in words) rol bx,cl ; BX is no of lf's. mov NOC,bx mov cx,2 ror bx,cl add NOC,bx ; This is Lines X 40 add NOC,80 ; Current line + one more mov cx,NOC mov si,dx ; Source of message given us mov di,attrptr ; Pointer to screen attrib area mov ax,formdat.bldcrt ; Need bold attribute rep stosw ; Cover attribute area needed mov ax,formdat.nrmcrt ; Rest of screen needs normal color mov cx,1000 ; Whole screen sub cx,NOC ; Attribute area left to cover rep stosw ; Do it mov cx,1000 ; Fill screen data area with null bytes mov di,dispptr ; Pointer to screen data area xor ax,ax ; This is source of null bytes rep stosw ; Prepare clean screen data area mov cx,rnoc ; No of unformatted data bytes mov di,dispptr ; Destination is screen data area pthlp0: push di ; Save start of current line pthlp1: lodsb ; Load data bytes 1-by-1 cmp al,cr ; Is this one an eol? jz pthlp2 ; Yep - handle cr/lf's ourselves stosb ; No - move character loop pthlp1 ; And go back for next jmp short pthlp3 ; Finished moving chars, so exit. pthlp2: dec cx ; Account for cr and lf dec cx inc si ; Skip the lf pop di ; Get start of current line. add di,80 ; Adjust pointer to next line jmp short pthlp0 ; And go back for more pthlp3: pop di mov noc,2000 ; We are going to write over whole screen mov cmd,2 ; Get cursor position before writing mov dx,offset cmd mov cl,crtcmd int bios ; Get cursor call mov cx,word ptr la mov savcur,cx ; Store cursor position mov la,24 ; Prepare to roll down 24 lines mov ca,0 mov cmd,3 ; Screen roll down command mov dx,offset cmd mov cl,crtcmd int bios ; Do it! mov la,0 ; Begin our msg on top line of screen mov cmd,1 ; String write command int bios ; Write it. mov dx,offset upcur call tmsg mov dx,offset upcur ; Put cursor up onto clean screen call tmsg or ourflgs,movcur ; Tell that we've moved the cursor ret PUTHLP ENDP ; Receives message pointer in DX, terminating character in CL ; Returns with message ptr in DX, NOC in CX, and number of lf's in BX. GETNOC PROC NEAR cld mov di,dx mov al,cl ; Move terminator to AL mov cx,2000 ; Longest acceptible message. repnz scasb ; Look for terminator jz gtnoc1 xor cx,cx ret ; Error return gtnoc1: sub di,dx ; Calculate NOC mov cx,di ; Move it to counter dec cx ; Discount terminator push cx ; Save NOC mov di,dx mov al,lf xor bx,bx gtnoc2: repnz scasb ; Now count lf's jcxz gtnoc3 ; If the counter ran out inc bx jmp short gtnoc2 gtnoc3: pop cx ; Recover NOC ret GETNOC ENDP ; Produce a short beep. Returns normally. BEEP PROC NEAR mov dl,bell mov ah,conout int dos ret BEEP ENDP ; Prints $-terminated message in dx, for local use only TMSG PROC NEAR mov ah,prstr int dos ret TMSG ENDP ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP ; Jumping here is the same as a ret. R PROC NEAR ret R ENDP code ends end ; END OF MSXAPC.ASM