NAME msy55x ; File MSY55X.ASM ; Sanyo version by Robert W. Babcock and Joe H. White ; For version which replaces the BIOS keycode translation table, ; use -dMODIFIED flag for MASM ; Last edit 11 May 1988 ; edit history: ; 15 Sept 1987 version which runs with or without optional video board [rwb] ; 1 Jan 1988 version 2.30 ; Add mods to msyibm to produce Sanyo version 16 Aug, 1987 [rwb] ; 24 Dec 1987 Restore startup screen attributes at Kermit prompts. [jrd] ; 21 Dec 1987 Fix memory size sign problem for >640K systems. From Edgar Butt ; 4 Dec 1987 cleanup mode switching, add more Video 7 material. [jrd] ; 8 Nov 1987 Add EGA mode switching, from Terry Kennedy. ; 1 Nov 1987 Add support for Tektronix, based on work by Brian Holley. [jrd] ; 13 Oct 1987 Revise memory allocation sequence to avoid small holes. [jrd] ; 2 Oct 1987 Make log file character width match Set Display but 8 bits when ; debugging. [jrd] ; 12 Sept 1987 clarify sequence of translation and 8 bit display. [jrd] ; 27 Aug 1987 Do Translation before logging. [jrd] ; 18 Aug 1987 Change ESC to escape for MASM 4.5+ [jrd] ; 28 July 1987 Fix scron problem in screen save. [jrd] ; 16 June 1987 Remove global byte jwait (wait for vertical retrace), embedd ; code for proc scrwait in proc scroff. Replace calls for scrwait with ; calls to scron/scroff. Thanks to Dave Tweten (dt). [jrd] ; 11 June 1987 Add control of automatic screen roll back when new characters ; are to be displayed; default is off (no roll back). [jrd] ; 8 June 1987 Add keypad application mode tests to emit single chars. [jrd] ; 10 May 1987 Move input translation into terminal emulator, leave copy ; here for terminal type None, use mszibm byte anspflg to sense print ; screen is active, print translated characters but don't translate if ; debugging is active. [jrd] ; 28 March 1987 Make low_rgt screen coord word a global parameter. ; Add support for variable length screens and cursor size changes with ; EGA boards. Tests ok with 25, 35, 43, 50 lines with &/without MS Windows. ; EGA Memory locations 40:84H and 40:87H are used in this process. ; Use savadr dw as place to save screen: usually screen page 1 if screen ; dimensions are non-standard (80x25), else memory buffer scrsav. [jrd] ; 21 March 1987 Translate arriving Connect mode chars via table rxtable. [jrd] ; Add 132 Column support for Tseng Labs EVA board via procedure chgdsp, ; add restore scrolled screen before writing to it. From David L. Knoell [dlk] ; Modify msy and msz to use variable screen length and widths. [dlk] and [jrd] ; 17 March 1987 Reduce screen roll back buffer to half the memory to do the ; same number of screens. [jrd] ; 12 Jan 1987 Add keyboard translator, remove older code. [jrd] ; 1 Oct 1986 Version 2.29a public term, lclyini ; entry points public prtbout, prtnout, csrtype, scrmod, scrseg, scrsync public scroff, scron, atsclr, vtscru, vtscrd, scrloc, trnmod, telmsy public chgdsp, vtroll, crt_lins, crt_cols, getflgs, tv_mode ; action verb procedures for keyboard translator public uparrw, dnarrw, rtarr, lfarr, pf1, pf2, pf3, pf4 public kp0, kp1, kp2, kp3, kp4, kp5, kp6, kp7, kp8, kp9 public kpminus, kpcoma, kpenter, kpdot, chrout, cstatus, cquit public cquery, dmpscn, vtans52, vtinit, dnwpg, upwpg, endwnd, homwnd public upone, dnone, trnprs, dumpscr, modlin, modwrt, snull public klogon, klogof, cdos, chang ifdef MODIFIED ; [jhw] public ktest,dkeytab,skeyptr ; for diagnostics [jhw] endif ; [jhw] public vtemu, crt_mode, scbattr, refresh, low_rgt ; data public savescr, restscr public usevb ; [rwb] ifdef MODIFIED public rbtabl, sbtabl ; bios key set/reset [jhw] endif include mssdef.h ; some definitions ; hardware crt_status equ 3dah ; CGA crt status port disp_enb equ 8 ; CGA display enable bit crtmset equ 3D8H ; CGA CRT mode set port. screen equ 10h ; bios screen interrupt biostty equ 0eh ; Bios screen tty write mode cmd8251 equ 2ah ; i/o address of 8251 command port [jhw] rxrdy equ 02h ; 8251 receiver ready bit [jhw] modfrm struc ; format of mode (status) line db 'Esc-chr: ' ; do not write in last column. m_echr db 2 dup (?) db ' help: ' m_hlp db 2 dup (?) db '? port:' m_prt db 1 dup (?) db ' speed:' m_baud db 5 dup (?) db ' parity:' m_par db 4 dup (?) db ' echo:' m_echo db 3 dup (?) m_term db 13 dup (' ') ; 13 bytes for term type m_prn db 3 dup (' ') ; show PRN when printer is on modfrm ends datas segment para public 'datas' extrn flags:byte, mar_top:byte, mar_bot:byte, portval:word extrn filtst:byte, dmpname:byte, kbdflg:byte, rxtable:byte extrn anspflg:byte, tekflg:byte ; [begin jhw] ifdef MODIFIED ; ; stuff for replacement key definition table for the Sanyo BIOS ; dkeytab dw 00000h,04F00h,05100h,04603h,04900h,00F00h,00000h,05200h dw 00E08h,00F09h,01C0Ah,04700h,00000h,01C0Dh,00000h,00000h dw 03B00h,03C00h,03D00h,03E00h,03F00h,04000h,04100h,04200h dw 04300h,04400h,00000h,0011Bh,04B00h,04D00h,04800h,05000h dw 03920h,00221h,02822h,00423h,00524h,00625h,00826h,02827h dw 00A28h,00B29h,0372Ah,00D2Bh,0332Ch,00C2Dh,0342Eh,0352Fh dw 00B30h,00231h,00332h,00433h,00534h,00635h,00736h,00837h dw 00938h,00A39h,0273Ah,0273Bh,0333Ch,00D3Dh,0343Eh,0353Fh dw 00340h,01E41h,03042h,02E43h,02044h,01245h,02146h,02247h dw 02348h,01749h,0244Ah,0254Bh,0264Ch,0324Dh,0314Eh,0184Fh dw 01950h,01051h,01352h,01F53h,01454h,01655h,02F56h,01157h dw 02D58h,01559h,02C5Ah,01A5Bh,02B5Ch,01B5Dh,0075Eh,00C5Fh dw 02960h,01E61h,03062h,02E63h,02064h,01265h,02166h,02267h dw 02368h,01769h,0246Ah,0256Bh,0266Ch,0326Dh,0316Eh,0186Fh dw 01970h,01071h,01372h,01F73h,01474h,01675h,02F76h,01177h dw 02D78h,01579h,02C7Ah,01A7Bh,02B7Ch,01B7Dh,0297Eh,05300h dw 00080h,00081h,00082h,00083h,00084h,00085h,00086h,00087h dw 00088h,00089h,0008Ah,0008Bh,0008Ch,0008Dh,0008Eh,0008Fh dw 00090h,00091h,00092h,00093h,00094h,00095h,00096h,00097h dw 00098h,00099h,0009Ah,0009Bh,0009Ch,0009Dh,0009Eh,0009Fh dw 000A0h,000A1h,000A2h,000A3h,000A4h,000A5h,000A6h,000A7h dw 000A8h,000A9h,000AAh,000ABh,000ACh,000ADh,000AEh,000AFh dw 000B0h,000B1h,000B2h,000B3h,000B4h,000B5h,000B6h,000B7h dw 000B8h,000B9h,000BAh,000BBh,000BCh,000BDh,000BEh,000BFh dw 000C0h,000C1h,000C2h,000C3h,000C4h,000C5h,000C6h,000C7h dw 000C8h,000C9h,000CAh,000CBh,000CCh,000CDh,000CEh,000CFh dw 000D0h,000D1h,000D2h,000D3h,000D4h,000D5h,000D6h,000D7h dw 000D8h,000D9h,000DAh,000DBh,000DCh,000DDh,000DEh,000DFh dw 000E0h,000E1h,000E2h,000E3h,000E4h,000E5h,000E6h,000E7h dw 000E8h,000E9h,000EAh,000EBh,000ECh,000EDh,000EEh,000EFh dw 000F0h,000F1h,000F2h,000F3h,000F4h,000F5h,000F6h,000F7h dw 000F8h,000F9h,000FAh,000FBh,000FCh,000FDh,000FEh,000FFh dw 00000h,08400h,07600h,08500h,08600h,08700h,00000h,08800h dw 00E7Fh,00009h,07500h,07700h,00000h,08900h,00000h,00000h dw 06800h,06900h,06A00h,06B00h,06C00h,06D00h,06E00h,06F00h dw 07000h,07100h,00000h,08A1Bh,07300h,07400h,08B00h,08C00h dw 08D20h,07800h,06200h,07A00h,07B00h,07C00h,07E00h,06400h dw 08000h,08100h,07F00h,08300h,06500h,09300h,06600h,06700h dw 05D00h,05400h,05500h,05600h,05700h,05800h,05900h,05A00h dw 05B00h,05C00h,06100h,06300h,08E00h,05E00h,08F00h,09000h dw 07900h,01E00h,03000h,02E00h,02000h,01200h,02100h,02200h dw 02300h,01700h,02400h,02500h,02600h,03200h,03100h,01800h dw 01900h,01000h,01300h,01F00h,01400h,01600h,02F00h,01100h dw 02D00h,01500h,02C00h,01A1Bh,02B1Ch,01B1Dh,07D00h,08200h dw 00300h,01E01h,03002h,02E03h,02004h,01205h,02106h,02207h dw 02308h,01709h,0240Ah,0250Bh,0260Ch,0320Dh,0310Eh,0180Fh dw 01910h,01011h,01312h,01F13h,01414h,01615h,02F16h,01117h dw 02D18h,01519h,02C1Ah,05F00h,0911Ch,06000h,0071Eh,09200h dw 00080h,00081h,00082h,00083h,00084h,00085h,00086h,00087h dw 00088h,00089h,0008Ah,0008Bh,0008Ch,0008Dh,0008Eh,0008Fh dw 00090h,00091h,00092h,00093h,00094h,00095h,00096h,00097h dw 00098h,00099h,0009Ah,0009Bh,0009Ch,0009Dh,0009Eh,0009Fh dw 000A0h,000A1h,000A2h,000A3h,000A4h,000A5h,000A6h,000A7h dw 000A8h,000A9h,000AAh,000ABh,000ACh,000ADh,000AEh,000AFh dw 000B0h,000B1h,000B2h,000B3h,000B4h,000B5h,000B6h,000B7h dw 000B8h,000B9h,000BAh,000BBh,000BCh,000BDh,000BEh,000BFh dw 000C0h,000C1h,000C2h,000C3h,000C4h,000C5h,000C6h,000C7h dw 000C8h,000C9h,000CAh,000CBh,000CCh,000CDh,000CEh,000CFh dw 000D0h,000D1h,000D2h,000D3h,000D4h,000D5h,000D6h,000D7h dw 000D8h,000D9h,000DAh,000DBh,000DCh,000DDh,000DEh,000DFh dw 000E0h,000E1h,000E2h,000E3h,000E4h,000E5h,000E6h,000E7h dw 000E8h,000E9h,000EAh,000EBh,000ECh,000EDh,000EEh,000EFh dw 000F0h,000F1h,000F2h,000F3h,000F4h,000F5h,000F6h,000F7h dw 000F8h,000F9h,000FAh,000FBh,000FCh,000FDh,000FEh,000FFh skeyptr dw 0,0 ; for address of pointer in bios to key ; scan code/char translation table ckeyptr dw 0,0 ; same for control keys (seg:offset) kinitf db 0 ; flag to indicate pointers were found keyng db "Unable to Find BIOS Key Translation Table Pointers" db cr,lf,'$' ; [end jhw] endif ; stuff for screen routines yflags db ? ; status flags... flags1 db 0 ; internal flags (but used in mszibm). prtscr equ 1 ; print screen pressed inited equ 08h ; been here before... vtinited db 0 ; flag for emulator having been inited cursor dw ? esc_ch db ? parmsk db ? ; 8/7 bit parity mask, for reception argadr dw ? ; address of arg blk ega_mode db 0 ; non-zero if IBM EGA is in use. tvhere equ 0feh ; Topview active query tvsynch equ 0ffh ; Topview resynch request tv_segs dw ? ; Topview virtual screen, segment tv_sego dw ? ; and offset tv_mode db 0 ; flag, 0 = no Topview. savadr dw 2 dup (?) ; offset then segment of saved screen savflg dw ? ; low_rgt at time of screen save ; swid2m db swidth * 2 ; used as divisor in getcirc [jhw] curpost dw 0 ; temp storage for cursor position[jhw] usevb db ? ; 1 if video board, 0 if Sanyo mode [rwb] vbmsg db CR,'Video board in use',CR,LF,'$' novbmsg db CR,'Sanyo mode screen',CR,LF,'$' ; The following are used to turn the display back on (after scrolling etc.) msets db 2CH,28H,2DH,29H,2AH,2EH,1EH,29H vtemu emulst <> ; emulator flags ansflgs db 0 ; ANSI flags trmtyp db 0 ; most recent terminal type mtty db ' TTY ' ; no terminal type (mode line) lincur dw ? ; cursor type save area scbattr db ? ; Screen background attribute oldattr db ? ; screen attributes at init time curattr db ? ; current attribute temp dw ? ; scratch storage modtemp db 0 ; temp to hold Kermit modeline status captrtn dw ? ; routine to call for captured output dmphand dw ? ; screen dump file handle dumpbuf db 132 dup (?), cr, lf ; 134 byte dump work buffer dumpsep db FF,cr,lf ; screen image separators dmperr db ' Cannot open file to save screen to disk $' crlf db cr,lf,'$' ; some static data for mode line modbuf modfrm <> ; mode line buffer unkbaud db 'unkwn' ; must be 5 chars... baudn db ' 45.5',' 50 ',' 75 ',' 110 ','134.5',' 150 ',' 300 ',' 600 ' db ' 1200',' 1800',' 2000',' 2400',' 4800',' 9600','19200','38400' db '57.6K','115 K' baudnsiz equ 18 ; # of baud rates known (tbl size / 4) parnams db 'even','mark','none','odd ','spc ' lclmsg db 'loc' remmsg db 'rem' portno db ? ; storage for multi-window stuff swidth equ 80 ; max screen width slen equ 24 ; and length of text npages equ 10 ; # of pages of scrolling on each side crt_norm db ? ; video mode for normal screen crt_mode db ? ; video mode (typ 3, must be text) crt_cols db ? ; number of screen columns (typ 80) crt_lins db 24 ; number of screen rows - 1 (typ 24) low_rgt dw ? ; lower right corner of text window ; high = row address (typ 23) ; low = column address (typ 79) inipara dw ? ; initial paragraphs of scroll memory scrsav dw ? ; segment address of save area refresh db 0 ; screen refresh (0=wait for retrace) vtroll db 0 ; auto roll back allowed (0 = no). ; circular buffer for screen roll back. cbuf struc pp dw ? ; place ptr in buffer bend dw ? ; end of buffer orig dw ? ; buffer origin lcnt dw 0 ; # of lines in buffer. lmax dw ? ; max lines of buffer. cbuf ends twnd cbuf <> ; top screen spill-buffer struct bwnd cbuf <> ; bottom screen spill buffer struct datas ends code segment public 'code' extrn beep:near, prtchr:near, outchr:near, sbrk:near, pcwait:near extrn isfile:near, strlen:near, strcpy:near ; in mssfil extrn anstty:near,ansini:near,ansrei:near ; in mszibm extrn anstat:near,anskbi:near,ansdsl:near ; in mszibm extrn ans52t:near, vsinit:near ; in mszibm extrn msuinit:near, keybd:near ; in msuibm extrn tekini:near,tekcls:near,tekemu:near,tekend:near ;in msgibm assume cs:code, ds:datas, es:datas ; do initialization local to this module... ; Dynamically allocates 4000 bytes for screen save/restore buffer plus ; 320 to 38400 bytes for screen scroll back buffers. Tries to leave space ; for Command.com before enlarging buffers. [jrd] lclyini proc near ; Figure out whether a Video board is in use by writing 2 different [rwb] ; characters to the same screen location with a DOS call and reading ; back from the correspnding video board memory location. Cursor is known ; to be in the first column before doing this, but may not be in the last row ; if the screen was recently cleared. mov ax,0b800h ; VB segment mov es,ax mov ah,3 mov bh,0 int screen ; read cursor position mov ax,0a0h ; length of row (2 bytes/char) mul dh ; offset of current row, col 0 push ax ; save offset mov ax,0a00h+'i' mov cx,1 int SCREEN ; display 'i' pop si ; recover offset cmp byte ptr es:[si],'i' ; read character back (maybe) jne lclynovb ; ne = no video board push si ; save offset again mov ax,0a00h+'V' int SCREEN ; display 'V' pop si ; recover offset cmp byte ptr es:[si],'V' ; read it back jne lclynovb ; ne = no video board mov usevb,1 ; say video board mov dx,offset vbmsg jmp short lclyvb lclynovb: mov usevb,0 ; say no video board mov dx,offset novbmsg lclyvb: mov ah,9 int dos ; display msg with video board status call msuinit ; initialize keyboard module msuxxx mov ax,swidth*(slen+1)*2 ; (80 char + 80 attr) * 25 lines call sbrk ; memory allocation routine (mssker) ;if we get here them we have the lines mov scrsav,ax ; memory segment for save screens ; screen roll back buffers mov bx,0ffffh ; ask for all of memory, to get size mov ah,alloc ; allocate all of memory (must fail) int dos ; bx has # free paragraphs mov ax,bx ; ax has copy of number free paragraphs sub ax,24000D/16 ; space for Command.com copy #2 jbe lclyin1 ; be = not enough for it. [ebb] cmp ax,(swidth*slen+15)/16 ; minimum roll back space left over? jbe lclyin1 ; be = not even that much cmp ax,(swidth*slen*npages+7)/8 ; paragraphs wanted for roll back jbe lclyin2 ; be = enough but not more than needed mov ax,(swidth*slen*npages+7)/8 ; limit to our actual needs jmp short lclyin2 ; ask for all we really want lclyin1:mov ax,(4*swidth+15)/16 ; use minimum needed paragraphs lclyin2:mov inipara,ax ; save for later resizing of buffers mov cl,4 ; convert paragraphs to bytes shl ax,cl ; for sbrk call sbrk ; ask for that many bytes ;if we get here them we have the space mov bwnd.orig,ax ; memory segment, bottom window area mov twnd.orig,ax ; top. same place for both buffers! mov ax,inipara ; # paragraphs allocated by DOS mov cl,3 ; 2**3 = 8 shl ax,cl ; paragraphs to words (char + attrib) xor dx,dx ; clear extended size mov cx,swidth ; number of chars per line in buffer div cx ; ax = number of lines in buffer mov bwnd.lmax,ax ; max lines per buffer (quotient) mov twnd.lmax,ax ; max lines per buffer add cx,cx ; count char and attribute per item xor dx,dx ; clear extended numerator mul cx ; ax = effective # bytes per buffer dec ax ; adjust for counting from zero mov bwnd.bend,ax ; offset of last byte in buffer mov twnd.bend,ax ; offset of last byte in buffer mov bwnd.pp,0 ; offset of first byte in buffer mov twnd.pp,0 ; offset of first byte in buffer mov bwnd.lcnt,0 ; number of lines occupied in buffer mov twnd.lcnt,0 ; number of lines occupied in buffer call scrseg ; test running in an Environment call scrmod ; read video state, get crt_mode. mov al,crt_mode mov crt_norm,al ; save as normal mode mov ah,8 ; read current attributes xor bh,bh ; page 0 int screen mov scbattr,ah ; save video attributes mov oldattr,ah ; and here too test usevb,1 ; video board in use? [rwb] jnz lcliniv ; nz = yes [rwb] ; [begin jhw] ; store ank segment:offset in topview ; pointers left over from IBM vers. ; (the ank buffer, sometimes called ; shadow buffer is an array of 4000 ; bytes containing the char/attrib ; of all characters currently on ; the screen. call scrseg ; get ank ram seg:offset in es:di mov tv_segs,es ; store segment mov tv_sego,di ; store offset lcliniv: ifdef MODIFIED ; find ptr to keyboard translation table (pointer is in BIOS keyboard ; interrupt service routine ktest: xor ax,ax ; set segment register to mov es,ax ; point to interrupt vector table mov si,es:[3ech] ; get offset of int 0fb ser.rtn mov es,es:[3eeh] ; and segment mov cx,30h ; set safety counter lcini3: cmp word ptr es:[si],1ec5h ; look for "lds bx" je lcini4 ; found? yes, jump out of loop inc si ; increment and try again loop lcini3 jmp lclng ; not found, go tattle lcini4: inc si inc si ; point to arg of lds bx mov di,es:[si] ; es:di pts at pointer to std key table mov word ptr skeyptr,di ; store address of pointer mov word ptr skeyptr+2,es ; mov cx,30h ; set another safety counter lcini5: cmp word ptr es:[si],1ec5h ; look for lds bx again je lcini6 ; jmp out of loop if found inc si ; point to next location loop lcini5 ; and try again lcini6: inc si ; point to arg of lds bx inc si mov di,es:[si] ; get pointer to control key table ptr mov word ptr ckeyptr,di ; store address of pointer mov word ptr ckeyptr+2,es les si,dword ptr skeyptr cmp word ptr es:[si],0 ; check present value in pointer jne lclng ; should be fc00:0000 (0000 first) cmp word ptr es:[si+2],0fc00h jne lclng les si,dword ptr ckeyptr ; check pointer for control key table cmp word ptr es:[si],200h ; should be fc00:0200 (200 first) jne lclng cmp word ptr es:[si+2],0fc00h jne lclng mov kinitf,1 ; success, remember that jmp lcini7 ; and go on lclng: mov kinitf,0 ; failure, remember that too mov ax,0900h ; and notify someone mov dx,offset keyng int 21h mov word ptr skeyptr,0 ; zero pointers to pointers mov word ptr skeyptr+2,0 mov word ptr ckeyptr,0 mov word ptr ckeyptr+2,0 endif lcini7: ; [end jhw] call vsinit ; init terminal emulator module MSZ ret lclyini endp ; ; [begin jhw] ifdef MODIFIED sbtabl proc near ; set ptr (in bios) to new key table cmp kinitf,1 ; did we find the pointer? jne sbtab9 ; no, skip this stuff push di push es push ax mov ax,offset dkeytab shr ax,1 shr ax,1 shr ax,1 shr ax,1 add ax,seg dkeytab ; get segment of new table les di,dword ptr skeyptr ; get address of standard key tab. ptr mov word ptr es:[di+2],ax ; put seg. in -- offset doesn't change les di,dword ptr ckeyptr ; get address of control key tab. ptr mov word ptr es:[di+2],ax ; put segment in like before pop ax ; that was it! pop es pop di sbtab9: ret sbtabl endp ; rbtabl proc near ; set ptr (in bios) to new key table cmp kinitf,1 ; did we find the pointer? jne rbtab9 ; no, skip this stuff push di push es push ax mov ax,0fc00h ; get segment of original table les di,dword ptr skeyptr ; get address of standard key tab. ptr mov word ptr es:[di+2],ax ; put seg. in -- offset doesn't change les di,dword ptr ckeyptr ; get address of control key tab. ptr mov word ptr es:[di+2],ax ; put segment in like before pop ax ; that was it! pop es pop di rbtab9: ret rbtabl endp endif ; [end jhw] scrini proc near ; init screen stuff call scrmod ; get screen mode, low_rgt mov ah,3 ; get cursor position and char. xor bh,bh ; page 0 int screen mov lincur,cx ; save cursor type (scan line #'s) mov dx,cursor ; assume old cursor scrin6: test flags1,inited ; have we been here before? jnz scrin4 ; nz = yes, use old cursor mov ah,oldattr ; get init time attributes mov curattr,ah ; and set nice screen attribute mov scbattr,ah mov ah,3 ; figure out where cursor is xor bh,bh ; page 0 int screen ; read cursor position, in dx scrin4: cmp dh,byte ptr low_rgt+1 ; past logical end of screen? jb scrin2 ; b = no, keep going mov dh,byte ptr low_rgt+1 ; yes, just use lower right corner scrin2: cmp dl,byte ptr low_rgt ; maybe past right margin jb scrin3 ; b = no, use the way it is mov dl,byte ptr low_rgt scrin3: mov cursor,dx ; init cursor mov ah,2 ; set cursor position xor bh,bh ; page zero int screen ; set cursor in case it moved ret scrini endp ; Routine to initialize VT102/52/Heath-19 terminal emulator. vtinit proc near cmp flags.vtflg,0 ; doing emulation? je vtinix ; e = no cmp tekflg,0 ; Tek mode active? jne vtini2 ; ne = yes, do it's reinit or vtinited,inited call ansflg ; update ansi flags mov al,yflags ; Pass the flags. mov bx,argadr ; Get address of argument block mov dl,[bx].baudb ; Baud rate code in dl mov dh,[bx].parity ; Parity code in bits mov cl,4 ; 0-3 of dh shl dh,cl test flags.remflg,d8bit ; eight bit display? jnz vtini1 ; nz = yes or dh,07H ; Just say 7 data bits. call ansini ; call startup routine in mszibm. ret vtini1: or dh,8 ; say 8 bits call ansini vtinix: clc ret vtini2: call tekcls ; clear Tek screen clc ret vtinit 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 yflags,al ; mask for allowable and save mov al,[bx].prt mov portno,al ; update port number mov al,[bx].rows mov crt_lins,al ; init # of rows and cols mov ax,[bx].captr mov captrtn,ax ; buffer capture routine mov al,[bx].escc mov esc_ch,al mov parmsk,0ffh ; parity mask, assume parity = None cmp [bx].parity,parnon ; is parity None? je argini1 ; e = yes, keep all 8 bits mov parmsk,07fh ; else keep lower 7 bits argini1: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,5eh ; caret, note control char mov modbuf.m_hlp,5eh 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 ; nb = yes, use default mov cl,size m_baud ; each is 5 bytes long mul 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 push es ; save es push ds pop es ; set es to datas segment cld 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 yflags,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' ; only port one possible on Sanyo [rwb] modl5: mov modbuf.m_prt,al ; fill in port number mov cx,8 ; blank out terminal id field mov si,offset mtty ; assume no terminal emulation. mov di,offset modbuf.m_term ; destination rep movsb ; copy it in. mov modbuf.m_prn,' ' ; assume not printing the screen mov modbuf.m_prn+1,' ' mov modbuf.m_prn+2,' ' test anspflg,prtscr ; doing a print the screen? jz modl5a ; z = no. mov modbuf.m_prn,'P' ; yes. display PRN at end of line mov modbuf.m_prn+1,'R' mov modbuf.m_prn+2,'N' modl5a: mov cx,size modfrm ; this is size of mode line mov si,offset modbuf ; mode line image pop es ; alternate entry to write an alternate mode line modwrt: push cx push si ; save mode line and size mov ah,3 ; read cursor position xor bx,bx ; screen page 0 int screen mov cursor,dx ; save cursor position call trmatt ; Get terminal attributes and ah,77h ; omit blinking/bold attributes mov bh,ah ; get video attribute mov dx,low_rgt ; right most column inc dh ; refer to status line mov ch,dh ; bottom line [dlk] mov cl,0 ; left col = 0 (first) [dlk] mov ax,600h ; scroll to clear the line int screen mov dh,byte ptr low_rgt+1 ; refer to status line inc dh xor dl,dl ; left most column mov bh,0 mov ah,2 ; set cursor position int screen pop si pop cx ; restore these cmp cl,crt_cols ; mode line longer than screen? jbe modl6 ; le = no mov cl,crt_cols ; else do just one line's worth dec cx ; don't let screen scroll modl6: cld lodsb ; get a byte mov ah,14 ; write to terminal mov bh,0 ; page 0 int screen loop modl6 ; write out entire mode line cmp flags.vtflg,0 ; emulating? je modl7 ; e = no and yflags,not modoff ; update local flags (mode line on) mov al,yflags ; Yes - update flags also call ansdsl ; get extras from emulator modl7: 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 call trmatt ; Get terminal screen attributes mov bh,al ; Use screen background attribute mov ax,600h ; blank window mov dx,low_rgt ; right most column inc dh ; refer to status line mov cx,dx ; bottom line [dlk] xor cl,cl ; left most column int screen ; clear mode line ret ; and return clrmod endp ; Fetch screen attributes from emulator (if emulating). It exists mainly ; so that the reverse video will work. Returns the current mode ; line background attribute in ah, the current screen background in al, ; and the current "cursor" (foreground) attribute in bl. (Note: anstat ; returns status yflags in bh). trmatt proc near ; Get attributes cmp flags.vtflg,0 ; emulating? je trmat1 ; No, just do simple stuff. mov al,yflags ; anstat expects flags byte in al. call anstat ; Fetch emulator status/attributes ret trmat1: mov al,scbattr ; Background attributes. mov bl,curattr ; And cursor attribute. mov ah,al ; where modlin needs them and ah,77h ; get colors part, no blink/bold rol ah,1 ; reverse them rol ah,1 rol ah,1 rol ah,1 ret trmatt endp ; Get byte yflags of terminal emulator passed in AL. Used in mode line ; handling when 25th line is used by the emulator. [jrd] telmsy proc near mov yflags,al ; get the updated flags call ansflg ; and any other emulator info ret telmsy endp ;[IU2] This routine updates the ANSI status flags from the emulator, ; and passes the "yflags" byte to the VT100 emulator also. ansflg proc near push ax ; Save acs over call push bx mov al,yflags call anstat ; Get status and attributes mov ansflgs,bh ; Save. pop bx pop ax ret ansflg endp getflgs proc near ; supply yflags for terminal emulators mov al,yflags ret getflgs endp term proc near ; terminal mode entry point mov argadr,ax ; save argument ptr call argini ; init options from arg address call scrini ; init screen stuff ifdef MODIFIED call sbtabl ; reset kbd table [jhw] endif test flags1,inited ; have we run yet? jz term1 ; z = no, so no saved screen yet call restscr ; restore screen term1: or flags1,inited ; remember we've run already. cmp flags.vtflg,0 ; current terminal type = None? je term3a ; e = yes, nothing to init. mov al,yflags ; tell emulator we are back cmp vtinited,inited ; inited emulator yet? je term3 ; e = yes cmp tekflg,0 ; Tek mode still active? jne term3a ; ne = yes, no re-init here call vtinit ; init it now jmp term3a term3: call ansrei ; reinit the emulator call ansflg ; and get its flags term3a: cmp flags.modflg,0 ; is mode line disabled? je term2a ; e = yes, disabled cmp flags.vtflg,0 ; emulating a terminal? jne term1a ; ne = yes, can have mode line cmp trmtyp,0 ; previous terminal type = none? jne term2 ; ne = no. need to clear mode line. jmp term2a ; yes, let 25th line be intact term1a: test yflags,modoff ; is mode line toggled off? jnz term2 ; nz = yes, clear the line. cmp flags.vtflg,tttek ; going to be a Tek terminal? je term2a ; e = yes, no mode line call modlin ; turn on mode line jmp term2a term2: call clrmod ; ensure its off term2a: mov al,flags.vtflg ; current terminal type mov trmtyp,al ; place to remember it til next time cmp flags.vtflg,tttek ; Tek mode? je term4 ; e = yes cmp tekflg,0 ; Tek mode active within DEC stuff? je lp ; e = no term4: call tekini ; reinit to get graphics screen lp: call portchr ; char at port? jc lp1 ; yes, go display it [begin jhw] ; check for receipt of character which ; came in without generating interrupt in al,cmd8251 ; get port status test al,rxrdy ; is character in 8251? jz chkinp ; no, go on cli ; yes, hold interrupts and int 0fah ; call the interrupt service routine sti ; re-enable interrupts call portchr ; get the character jnc chkinp ; if there was no character, go on lp1: call outtty ; else display it [end jhw] chkinp: call keybd ; call keyboard translator in msu jnc lp ; nc = no char or have processed it ; carry set = quit Connect mode. quit: call tekend ; [bjh] mov ah,3 ; get cursor position xor bh,bh ; page 0 int screen mov cursor,dx ; save position call savescr ; save screen cmp flags.vtflg,0 ; emulating? je quit1 ; e = no mov ax,0600h ; clear mode line with old attributes mov bh,oldattr ; attributes mov dx,low_rgt ; right most column inc dh ; refer to status line mov cx,dx ; bottom line [dlk] xor cl,cl ; left most column int screen ; clear the mode line quit1: mov ah,oldattr ; attributes at init time mov scbattr,ah ; background = original state mov cx,lincur ; cursor type at startup mov ah,1 int screen ; restore cursor type quit3: mov ah,2 ; Position cursor mov bh,0 ; Page 0 mov dx,low_rgt ; bottom line inc dh ; status line position xor dl,dl ; left most column int screen ; Do it. mov al,yflags mov bx,argadr mov [bx].flgs,al ; update flags in arg block ifdef MODIFIED call rbtabl ; reset key trans table ptr [jhw] endif ret ; and return to caller term endp ; put the character in al to the screen outtty proc near cmp flags.vtflg,0 ; emulating a terminal? jne outnoc ; ne = yes, emulator handles printing test flags.remflg,d8bit ; keep 8 bits for displays? jnz outnp9 ; nz = yes, 8 bits if possible and al,7fh ; remove high bit outnp9: cmp rxtable+256,0 ; translation turned off? je outnp7 ; e = yes, no translation push bx mov bx,offset rxtable ; address of translate table xlatb ; new char is in al pop bx outnp7: test anspflg,prtscr ; should we be printing? jz outnop ; no, keep going push ax mov ah,lstout ; write to system printer device mov dl,al int dos pop ax jnc outnop ; nc = successful print push ax call beep ; else make a noise and call trnprs ; turn off printing pop ax outnop: test yflags,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: cmp tekflg,0 ; Tek mode active? jne outnp6 ; ne = yes, skip screen rolling cmp vtroll,0 ; auto roll back allowed? jz outnp6 ; z = no, leave screen as is. cmp bwnd.lcnt,0 ; is screen rolled back? [dlk] je outnp6 ; e = no call endwnd ; restore screen before writing [dlk] outnp6: cmp flags.vtflg,0 ; emulating a terminal? jnz outnop1 ; nz = yup, go do something smart test yflags,trnctl ; debug? if so use Bios tty mode jz outnp4 ; z = no mov ah,biostty ; Bios tty screen write cmp al,7fh ; Ascii Del char or greater? jb outnp1 ; b = no je outnp0 ; e = Del char push ax ; save the char mov al,7eh ; output a tilde for 8th bit int screen pop ax ; restore char and al,7fh ; strip high bit outnp0: cmp al,7fh ; is char now a DEL? jne outnp1 ; ne = no and al,3fH ; strip next highest bit (Del --> '?') jmp outnp2 ; send, preceded by caret outnp1: cmp al,' ' ; control char? jae outnp3 ; ae = no add al,'A'-1 ; make visible outnp2: push ax ; save char mov al,5eh ; caret int screen ; display it pop ax ; recover the non-printable char outnp3: int screen ret outnp4: cmp al,bell ; bell (Control G)? jne outnp5 ; ne = no jmp beep ; use short beep, avoid char loss. outnp5: mov dl,al ; write without intervention. mov ah,conout int dos ; else let dos display char ret ; and return outnop1:cmp flags.vtflg,tttek ; doing Tektronix emulation? je outnop2 ; e = yes, use Tek emulator cmp tekflg,0 ; Tek submode active? jne outnop2 ; ne = yes, use Tek emulator jmp anstty ; call terminal emulator routine & ret outnop2:jmp tekemu ; use Tek emulator and return outtty endp ;[IU2] Here to output character to port with no echo (like escape sequences ; sent by PF keys, responses to requests from the host, etc. It is ; wrong thinking to echo these). prtbout proc near ; Global routine now. mov ah,al ; This is where outchr expects it call outchr nop ; Ignore skip return. nop nop ret prtbout endp ;[IU2] Here to output an unsigned 8-bit number (in al) to the port without ; echoing. Used by terminal emulator escape sequence output. prtnout proc near mov bl,10 ; Output in base 10. jmp prtno2 ; Ensure at least a zero. prtno1: cmp al,0 jne prtno2 ; Yes - do more digits ret ; No - return from recursive call. prtno2: mov ah,0 ; Clear previous remainder. div bl ; Divide off a digit push ax ; Push remainder (in ah) on stack call prtno1 ; Recur. pop ax ; Pop off a digit add ah,'0' ; Make it ASCII call outchr ; send to port nop nop nop ret prtnout endp ; send the character in al out to the serial port; handle echoing. ; Can send an 8 bit char while displaying only 7 bits locally. outprt proc near test yflags,lclecho ; echoing? jz outpr1 ; z = 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,parmsk ; apply 8/7 bit parity mask stc ; have a character ret ; and return portchr endp ;;; Action routines (verbs) for keyboard translator KEYBD in msuibm. ; These are invoked by a jump instruction. Return carry clear for normal ; processing, return carry set for invoking Quit (kbdflg has transfer char). uparrw: mov al,'A' ; cursor keys jmp short comarr dnarrw: mov al,'B' jmp short comarr rtarr: mov al,'C' jmp short comarr lfarr: mov al,'D' comarr: push ax ; save final char mov al,escape ; Output an escape. call outprt ; Output, echo permitted cmp flags.vtflg,tttek ; Tek terminal? jne comar0 ; ne = yes, use VT100 codes cmp flags.vtflg,ttvt100 ; VT100 terminal emulation? jne comar2 ; No, do VT52/HEATH-19 sequence. comar0: call ansflg ; Update flags all around. mov al,'[' ; Maybe this next? test ansflgs,decckm ; Cursor key mode reset? je comar1 ; Yes, output the "[" mov al,'O' ; No, set, use the "O". comar1: call outprt ; Output it (echo permitted). comar2: pop ax ; recover final char call outprt ; Output to port (echo permitted) clc ret pf1: mov al,'P' ; keypad function keys 1-4 jmp short compf pf2: mov al,'Q' jmp short compf pf3: mov al,'R' jmp short compf pf4: mov al,'S' compf: push ax ; save final char mov al,escape ; Output an escape. call prtbout call ansflg ; get emulator flags test ansflgs,decanm ; ansi mode? jz short compf1 ; z = no mov al,'O' ; send an "O". call prtbout ; Output it. compf1: pop ax ; Get the saved char back call prtbout ; Output to port clc ret kp0: mov al,'p' ; keypad numeric keys jmp short comkp kp1: mov al,'q' jmp short comkp kp2: mov al,'r' jmp short comkp kp3: mov al,'s' jmp short comkp kp4: mov al,'t' jmp short comkp kp5: mov al,'u' jmp short comkp kp6: mov al,'v' jmp short comkp kp7: mov al,'w' jmp short comkp kp8: mov al,'x' jmp short comkp kp9: mov al,'y' jmp short comkp kpminus:mov al,'m' jmp short comkp kpcoma: mov al,'l' jmp short comkp kpenter:mov al,'M' jmp short comkp kpdot: mov al,'n' comkp: test ansflgs,deckpam ; keypad application mode active? jnz comkp3 ; nz = yes, use escape sequences sub al,40h ; deduct offset to numeric symbols jmp comkp0 ; and send that single char comkp3: push ax ; save final char mov al,escape ; Output an escape. call prtbout mov al,'O' ; Output the "O" cmp flags.vtflg,ttvt100 ; VT100 mode? je comkp1 ; e = yes, use "O" code cmp flags.vtflg,tttek ; Tek terminal je comkp1 ; e = yes, use VT100 codes test ansflgs,decanm ; ANSI (alt application keypad) mode? jnz comkp1 ; nz = yes, use "O" comkp2: mov al,'?' ; else use "?" instead of "O". comkp1: call prtbout pop ax ; recover final char comkp0: call prtbout ; send it clc ret klogon proc near ; resume logging (if any) test flags.capflg,logses ; session logging enabled? jz klogn ; z = no, forget it or argadr.flgs,capt ; turn on capture flag or yflags,capt ; set local msy flag as well call ansflg ; tell emulator klogn: clc ret klogon endp klogof proc near ; suspend logging (if any) and argadr.flgs,not capt ; stop capturing and yflags,not capt ; reset local msy flag as well call ansflg ; tell emulator klogo: clc ret klogof endp snull proc near ; send a null byte mov al,0 ; the null call prtbout ; send without logging and local echo clc ret snull endp ; general character out for emulator chrout: cmp flags.vtflg,0 ; emulating? je chrou5 ; e = no call anskbi ; Yes, say we had keyboard input. cmp al,cr ; A CR? jne chrou5 ; No - just output it and return call ansflg ; Yes - update VT100 flags test ansflgs,anslnm ; ANSI new-line mode set? jz chrou5 ; No - just send the cr call outprt ; Yes - output a carriage-return mov al,lf ; Followed by a line feed. chrou5: call outprt clc ret ; these commands invoke Quit cdos: mov al,'P' ; Push to DOS jmp short cmdcom cstatus:mov al,'S' ; Status jmp short cmdcom cquit: mov al,'C' ; Exit Connect mode jmp short cmdcom cquery: mov al,'?' ; Help jmp short cmdcom chang: mov al,'H' ; Hangup, drop DTR & RTS jmp short cmdcom cmdcom: mov kbdflg,al ; pass char to msster.asm via kbdflg stc ; signal that Quit is needed ret dmpscn proc near ; dump screen to file call savescr ; save screen to buffer call dumpscr ; do buffer to file clc ; do not exit Connect mode ret dmpscn endp ;[IU2] Routine to toggle VT100/VT52/Heath-19 modes in VT100 emulator. vtans52 proc near cmp flags.vtflg,0 ; emulating? je vtans5 ; e = no call ans52t ; Call MSZ toggle-it routine. call ansflg ; Update flags. clc ; clear c bit so don't exit Connect. vtans5: ret vtans52 endp ; Toggle Mode Line trnmod: cmp flags.modflg,0 ; is mode line enabled? je trnm2 ; e = no, don't touch it cmp flags.vtflg,tttek ; Tek mode? je trnm2 ; yes cmp tekflg,0 ; Tek submode? jne trnm2 ; ne = yes, no mode line changes test yflags,modoff ; mode line already off? jnz trnm1 ; yes, go turn on call clrmod ; no, clear mode line here or yflags,modoff ; turn on flag call ansflg ; Update flags all around. clc ; clear c bit so don't exit Connect ret ; and return trnm1: and yflags,not modoff ; Clear flag first. call modlin ; Then turn on mode line. call ansflg ; Update flags all around. trnm2: clc ret trnprs: push ax ; toggle ^ PrtSc screen to printer test anspflg,prtscr ; are we currently printing? jnz trnpr2 ; nz = yes, its on and going off mov ah,ioctl mov al,7 ; get output status of printer push bx mov bx,4 ; file handle for system printer int dos pop bx jc trnpr1 ; c = printer not ready cmp al,0ffh ; Ready status? je trnpr2 ; e = Ready trnpr1: call beep ; Not Ready, complain jmp trnpr3 ; and ignore request trnpr2: xor anspflg,prtscr ; flip the flag test yflags,modoff ; mode line off? jnz trnpr3 ; nz = yes call modlin ; else rewrite mode line trnpr3: pop ax clc ; return carry clear (don't quit) ret ;;;;; General screen management routines for IBM PC ; computes screen location to ax, given row and col in [dh,dl], resp. ; trashes dx scrloc proc near mov al,dh ; get row xor ah,ah ; clear ah mul crt_cols ; multiply by number of columns xor dh,dh ; clear row add ax,dx ; this is current position shl ax,1 ; double for attributes ret scrloc endp ; Routine to set cursor type. Pass cursor type in al: 0 = No cursor, ; 1 = Underline cursor, 2 = Block cursor. All cursors blink due to hardware. ; Routine frags any ac that video ints frag. ; For EGA boards running in non-25 line mode the cursor emulation is turned ; off during cursor shape changing and restored afterward. It's another ; ega Feature. [jrd] csrtype proc near push cx ; save the reg test usevb,1 ; video board present? jz sancsr ; z = no mov ah,1 ; Video fxn for set cursor type mov cx,0F00H ; Assume no cursor cmp al,0 ; No cursor? je csrty2 ; Right - set it and be done with it. mov cx,0607H ; No, use CGA underline cursor cmp al,2 ; Block? jne csrty2 ; No - set it now. csrty1: xor ch,ch ; Yes - make it a block csrty2: csrty4: int screen ; regular cursor shape setting pop cx ret ; [begin jhw] ; Sanyo does not support small/large cursor changes without patching the ; BIOS. Since there are so many BIOS versions around, I considered it to be ; too risky to try to implement that here. So the following routine modifies ; the flash rate instead of the cursor size -- 0 for no cursor, 1 for slow ; (normal) flash, and 2 for rapid flash. sancsr: push ax ; save cursor type mov ax,0100h ; Sanyo bios is sequence sensitive on xor cx,cx ; cursor type calls. Can't set fast int SCREEN ; cursor from no cursor -- so set to pop ax ; normal, then set to desired type cmp al,0 jne csr1 mov cx,2600h ; set for no cursor jmp csr3 csr1: cmp al,1 jne csr2 xor cx,cx ; set for slow cursor (normal) jmp csr3 csr2: mov cx,107h ; set for quick flash csr3: mov ah,1 ; ah=1 for cursor set bios call int SCREEN pop cx ret ; [end jhw] csrtype endp ; Save the entire screen in a buffer so we can restore and/or dump it. ; Saves regular (80x25) screens to memory buffer scrsav and other sized ; screens to video memory page 1. Resultant save place put into savadr ; (offset then segment) and current low_rgt size info in savflg. Note, ; some Environments (TopView/Windows etc) may not permit use of page 1. [jrd] savescr proc near push es push ds push ax push cx push si push di call scrseg ; get screen segment in ax and es:di push ax ; save screen segment mov si,0 test usevb,1 ; video board in use? jnz savscv1 ; nz = yes mov si,di savscv1: mov di,scrsav ; place to put screen (memory buff) mov savadr+2,di ; working seg address for restore mov savadr,0 ; and no offset for memory buffer call scrmod ; ascertain video mode and screen mov ax,low_rgt ; text screen lower right (typ 23,79) mov savflg,ax ; save it for screen restore inc al ; number of columns add ah,2 ; plus status line = number of rows cmp al,swidth ; same as preset screen space (80)? ja savsc1 ; a = no, use screen video page 1 cmp ah,slen+1 ; same as preset screen length (24)? je savsc3 ; e = yes, use our memory buffer savsc1: mul ah ; times rows = characters on screen shl ax,1 ; times two for attributes = page 1 mov cx,ax ; cx = working copy of screen size and cx,000fh ; get lower four bits for offset part mov savadr,cx ; save offset in this word mov cl,4 shr ax,cl ; compute number of paragraphs pop di ; source screen address push di ; restore again add di,ax ; add paragraphs, point di to page 1 mov savadr+2,di ; and save segment in this word savsc3: mov es,savadr+2 ; segment of storage area mov di,savadr ; offset of same mov ax,low_rgt ; lower right of text screen inc al ; number of columns on screen add ah,2 ; number of rows on screen mul ah ; number of characters on the screen mov cx,ax ; save this in counter cx call scroff ; turn off screen [dt] pop ds ; address screen cld rep movsw ; save the screen pop di pop si pop cx pop ax pop ds ; restore this call scron ; turn on screen [dt] pop es ret savescr endp ; restore screen from buffer (offset and seg in savadr, text coord in savflg). ; Restores all screen lines. [jrd] restscr proc near push es mov ax,savflg ; saved low_rgt text screen coord add ah,2 ; number of screen lines inc al ; number of screen columns mul ah ; columns time lines = # characters mov cx,ax ; save this in counter cx push cx ; save count call scrseg ; get address of screen in es:di call scroff ; turn off screen [dt] test usevb,1 ; video board in use? jz rstscv1 ; z = no push ds ; save original data segment mov si,savadr ; offset of storage area push savadr+2 ; segment of same pop ds ; put storage segment into ds cld rep movsw ; restore data to screen pop ds ; recover original data segment jmp short rstscv2 rstscv1:mov si,savadr push savadr+2 pop es dec cx add si,cx ; move to end, reload backwards add si,cx xor ax,ax mov bx,low_rgt inc bh ; also do mode line call atsclr ; clear screen before restoring mov al,' ' mov ah,scbattr mov temp,ax ; blank with background attribute mov dx,low_rgt ; lower right corner inc dh ; including mode line rstsc1: mov ax,es:[si] cmp ax,temp je rstsc2 ; skip over blanks mov ah,2 ; set cursor function int SCREEN ; position cursor mov ax,es:[si] ; get char and attribute mov bl,ah mov cx,1 ; number of chars to display mov ah,9 ; write attribute and char function int SCREEN ; display char rstsc2: dec si dec si dec dl ; back one column jge rstsc1 ; jump unless row done mov dl,byte ptr low_rgt ; end of row dec dh ; on to previous column jge rstsc1 ; jump unless all done ; rstscv2: mov ah,2 ; set cursor position mov bh,0 ; page 0 mov dx,cursor ; saved cursor position int SCREEN ; position cursor call scron ; turn on screen [dt] pop cx ; recover count call scrsync ; synch Topview with new screen pop es ret restscr 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 reads the screen image saved by savescr so call savescr call first. dumpscr proc near push ax push bx push cx push dx mov dmphand,-1 ; preset illegal handle mov dx,offset dmpname ; name of disk file, from mssset mov ax,dx ; where isfile wants name ptr call isfile ; what kind of file is this? jc dmp5 ; c = no such file, create it test byte ptr filtst.dta+21,1fh ; file attributes, ok to write? jnz dmp0 ; nz = no. mov al,1 ; writing mov ah,open2 ; open existing file int dos jc dmp0 ; c = failure mov dmphand,ax ; save file handle mov bx,ax ; need handle here mov cx,0ffffh ; setup file pointer mov dx,-1 ; and offset mov al,2 ; move to eof minus one byte mov ah,lseek ; seek the end int dos jmp dmp1 dmp5: test filtst.fstat,80h ; access problem? jnz dmp0 ; nz = yes mov ah,creat2 ; file did not exist mov cx,20h ; attributes, archive bit int dos mov dmphand,ax ; save file handle jnc dmp1 ; nc = ok dmp0: mov ah,3 ; get cursor position xor bx,bx ; page 0 int screen push dx ; save it mov dh,byte ptr low_rgt+1 ; go to status line inc dh xor dl,dl ; left most column mov ah,2 ; position cursor int screen mov dx,offset dmperr ; say no can do mov ah,prstr int dos pop dx ; get original cursor position mov ah,2 ; position cursor xor bx,bx ; page 0 int screen pop dx pop cx pop bx pop ax clc ret dmp1: mov ah,ioctl ; is destination ready for output? mov al,7 ; test output status mov bx,dmphand ; handle int dos jc dmp0 ; c = error cmp al,0ffh ; ready? jne dmp0 ; ne = not ready. push di ; read screen buffer, write lines push si push es mov cl,byte ptr low_rgt+1 ; number of lines - 2 add cl,2 ; number of line on screen xor ch,ch mov si,savadr ; offset in storage area dmp2: push cx ; save outer loop counter mov es,savadr+2 ; get storage segment mov di,offset dumpbuf ; data segment memory mov cl,byte ptr savflg ; number of columns on screen - 1 inc cl ; number of columns on screen xor ch,ch dmp3: mov ax,word ptr es:[si] ; read char + attribute mov byte ptr [di],al ; store just char, don't use es: inc si ; update pointers inc si inc di loop dmp3 ; do for each column std ; set scan backward mov cl,byte ptr savflg ; number of columns on screen - 1 inc cl ; number of columns on screen xor ch,ch push es mov ax,ds mov es,ax ; set es to data segment for es:di mov di,offset dumpbuf ; start of line add di,cx ; plus length of line dec di ; minus 1 equals end of line mov al,' ' ; thing to scan over repe scasb ; scan until non-space cld ; set direction forward pop es jz dmp3a ; z = all spaces inc cx inc di dmp3a: mov word ptr [di+1],0A0Dh ; append cr/lf add cx,2 ; line count + cr/lf mov dx,offset dumpbuf ; array to be written mov bx,dmphand ; need file handle mov ah,write2 ; write the line int dos pop cx ; get line counter again jc dmp3b ; c = error loop dmp2 ; do next line mov dx,offset dumpsep ; put in formfeed/cr/lf mov cx,3 ; three bytes overall mov ah,write2 ; write them dmp3b: mov bx,dmphand ; file handle int dos mov ah,close2 ; close the file now int dos dmp6: pop es pop si pop di pop dx pop cx pop bx pop ax clc ret dumpscr endp ; Get CRT mode - returns mode in variable crt_mode, ; updates crt_cols and low_rgt. ; For EGA active it looks in Bios work memory 40:84H for number of rows. [jrd] scrmod proc near push ax push dx mov ah,15 ; Get current video state. int screen mov crt_mode,al ; Store CRT mode value. mov crt_cols,ah ; store # of cols mov dl,ah ; # of cols again mov dh,crt_lins ; and # of rows (constant from msster) scrmod4: dec dl ; max text column, count from zero dec dh ; max text row, count from zero mov low_rgt,dx ; save away window address pop dx pop ax ret ; And return. scrmod endp ; Get screen segment - returns screen segment in ax, and full address in es:di scrseg proc near test usevb,1 ; video board in use? jz scrsegv ; z = no xor di,di ; start at beginning of screen (0,0) mov ax,0B800H ; No - video memory is here on color jmp short scrse1 scrsegv:push bx push cx mov ah,71h ; find shadow screen int SCREEN push ds mov ds,ax mov ax,[bx] ; ax:bx points to variable with segment mov bx,cx ; can't do mov from [cx] mov di,[bx] ; ax:cx points to variable with offset mov es,ax pop ds pop cx pop bx cmp crt_mode,12 ; graphics set? jb scrse1 ; b = no mov ax,0A000H ; graphics scrse1: mov es,ax ; tell Topview our hardware address needs mov tv_segs,es ; save our hardware screen address mov tv_sego,di ; segment and offset form scrse2: ret scrseg endp ; Synchronize a Topview provided virtual screen buffer with the image ; seen by the user. Requires cx = number of words written to screen ; (char & attribute bytes) and es:di = ENDING address of screen write. ; Changes ax and di. scrsync proc near cmp tv_mode,0 ; Topview mode active? je scrsyn1 ; e = no, skip DOS call below sub di,cx ; backup to start byte (cx = words) sub di,cx ; after storing words to screen mov ah,tvsynch ; tell Topview we have changed screen int screen ; so user sees updated screen scrsyn1:ret scrsync endp ; The following two routines are used to turn off the display while we ; are reading or writing the screen in one of the color card modes. ; Turn screen off for (known) color card modes only. All regs preserved. ; Includes code for old procedure scrwait. 16 June 1987 [jrd] scroff proc near test usevb,1 ; video board in use? jz scrofx ; z = no cmp refresh,0 ; slow refresh? jne scrofx ; ne = no wait cmp ega_mode,0 ; Extended Graphics Adapter in use? jne scrofx ; ne = yes, no waiting. cmp tv_mode,0 ; Topview mode? jne scrofx ; ne = yes, no waiting cmp crt_mode,7 ; B&W card? jnb scrofx ; Yes - just return. push ax ; Save ax and dx. push dx mov dx,crt_status ; CGA: Wait for vertical retrace scrof1: in al,dx test al,disp_enb ; display enabled? jnz scrof1 ; yes, keep waiting scrof2: in al,dx test al,disp_enb ; now wait for it to go off jz scrof2 ; so can have whole cycle mov dx,crtmset ; Output to CRT mode set port. mov al,25H ; This shuts down the display out dx,al ; Dumb, but card is too.. pop dx ; Restore acs. pop ax scrofx: ret ; And return. scroff endp ; Turn screen on for (known) color card modes only ; All registers are preserved. scron proc near test usevb,1 ; video board in use? jz scronx ; z = no cmp refresh,0 ; slow refresh? jne scronx ; ne = no wait cmp ega_mode,0 ; Extended Graphics Adapter in use? jne scronx ; ne = yes, no waiting. cmp tv_mode,0 ; Topview mode? jne scronx ; ne = yes, no waiting cmp crt_mode,7 ; B&W card? jnb scronx ; Yes - just return. push ax ; Save ax, dx, and si push dx push si mov al,crt_mode ; Convert crt_mode to a word xor ah,ah mov si,ax ; Get it in a usable register mov al,msets[si] ; Fetch the modeset byte mov dx,crtmset ; This port out dx,al ; Flash it back on pop si ; Restore acs. pop dx pop ax scronx: ret ; And return. scron endp ; Screen clearing routine. [IU] ; ; Call: ax/ coordinates of first screen location to be cleared. ; bx/ coordinates of last location to be cleared. ; Coord: ah = row [0-24], al = column [0-79]. Preserves all registers. [jrd] atsclr: push ax ; Save some acs push cx push dx test usevb,1 ; video board in use? jz atsclv ; z = no mov dx,bx ; Compute last screen offset in ax push ax call scrmod ; update column length pop ax ; scrmod zaps ax push ax call scrloc ; get screen start address in ax mov cx,ax ; Save it in cx for a minute pop dx ; Compute first screen offset in ax call scrloc sub cx,ax ; Compute number of locs to clear... add cx,2 sar cx,1 ; Make byte count a word count jle atscl2 ; If nothing to clear, then vamos. push di ; Save some more acs push es ; save es push ax ; save around call call scrseg ; Get address of screen in ax, es:di pop ax ; recover displacement add di,ax ; displacement memory address mov ah,scbattr ; Use current screen background attr. mov al,' ' ; Use space for fill mov dl,byte ptr low_rgt ; line length - 1 inc dl ; line length xor dh,dh cmp cx,dx ; Blanking a line or less?? jg atscl1 ; No - make scroff disable display atscl1: call scroff ; Turn screen off if color card. push cx ; save word count for Topview cld rep stosw ; Blit... (excuse PDP-10ese please) pop cx ; recover word count call scrsync ; synch Topview call scron ; Turn screen back on if color card. pop es ; Restore segment register pop di ; And destination index jmp short atscl2 atsclv: push bx push di push si mov cx,ax mov dx,bx mov ax,600h mov bh,scbattr int SCREEN pop si pop di pop bx atscl2: pop dx ; restore regs pop cx pop ax ret ; Scrolling routines. vtscru scrolls up one row, vtscrd scrolls down one ; row. atsprep is called before scrolling up to save the top line in the ; circular buffer. All registers are preserved. ; Screen-roll down. Move text down one line, for terminal emulator only. vtscrd: push ax ; Upgraded by [jrd] push bx push cx push dx mov ax,701H ; scroll down one line mov ch,mar_top ; top margin line mov cl,0 ; left most column mov dh,mar_bot ; bottom margin line mov dl,byte ptr low_rgt ; right most column mov bh,scbattr ; attributes int screen ; scroll it down pop dx pop cx pop bx pop ax clc ret ; worker routine for vtscru/d atsprep:push es ; upgraded from older version [jrd] call scroff ; turn off color screen call scrseg ; get display address in es:di mov si,di ; si will be source mov bx,offset twnd ; this is where it goes call putcirc ; put screen line in circular buffer pop es ; and that call scron ; turn on screen again ret ; and return ; Screen scroll up one line (text moves up) for terminal emulator use. vtscru: push ax ; Upgraded by [jrd] push bx push cx push dx push si push di cmp mar_top,0 ; scrolling the top screen line? ja scru1 ; a = no. don't save anything call atsprep ; save top line scru1: mov ax,601H ; scroll up one line mov dh,mar_bot ; bottom row mov dl,byte ptr low_rgt ; right most column mov ch,mar_top ; top row of scrolling region mov cl,0 ; left most column mov bh,scbattr ; background attributes int screen ; scroll up that region pop di ; Restore the rest of the regs. pop si pop dx pop cx pop bx pop ax clc ret ; And return ;screen text roll up, version for manual scrolling only mscru: push ax ; Upgraded by [jrd] push bx push cx push dx push si push di cmp bwnd.lcnt,0 ; any lines in bottom window? je mscru2 ; e = no, so ignore request call atsprep ; save top line mscru1: mov ax,601H ; scroll up one line mov dx,low_rgt ; lower right corner xor cx,cx ; top row of scrolling region mov bh,scbattr ; background attributes int screen ; scroll up that region call scroff mov dx,low_rgt mov dl,0 ; location is lower left corner call scrloc ; get count from display start push es push ax ; save count call scrseg ; get screen's segment into ax, es:di pop ax ; recover count add di,ax ; destination memory address (es:di) mov bx,offset bwnd ; source of lines call getcirc ; get line from circ buf to screen pop es ; restore es call scron ; turn on the screen mscru2: pop di ; Restore the rest of the regs. pop si pop dx pop cx pop bx pop ax ret ; prep for screen scroll down. ; copies bottom scroll line from screen to bottom window buffer. ; destroys ax,cx,dx,si,di. getbot proc near ; Upgraded from old version [jrd] push es call scroff ; turn off screen mov dx,low_rgt ; from screen location, row mov dl,0 ; starting in col 0 call scrseg ; get adaptor's offset into es:di call scrloc ; get offset in display buffer in ax add di,ax ; source addr in display buffer es:di mov si,di ; screen is source (si) mov bx,offset bwnd ; buffer to use (bottom window) call putcirc ; copy bottom screen line to circ buf pop es call scron ; turn on display again ret getbot endp ;screen text scroll down, for manual mode only mscrd: push ax ; Upgraded by [jrd] push bx push cx push dx push si push di cmp twnd.lcnt,0 ; any lines left in top window? je mscrd1 ; e = no, ingore request call getbot ; fetch bottom line from screen mov ax,701H ; scroll down one line xor cx,cx ; top left corner mov dx,low_rgt ; bottom right corner mov bh,scbattr ; attributes int screen ; scroll it down call scroff ; turn off display push es call scrseg ; get segment address of screen mov bx,offset twnd ; buffer to use (top window) call getcirc ; copy from circ buf to screen pop es call scron ; turn on display again mscrd1: pop di ; Restore the rest of the ACs. pop si pop dx pop cx pop bx pop ax ret ; move viewing window down as much as possible (text moves up) endwnd proc near ; go to end of scrolling text push cx mov cx,bwnd.lcnt ; all bottom window lines [dlk] jmp dnwp0 ; and enter dwnpg endwnd endp dnone proc near ; move text up one line [jrd] push cx mov cx,1 jmp dnwp0 dnone endp ; scroll viewing window down (text moves up) one page (24 lines) dnwpg proc near push cx mov cl,byte ptr low_rgt+1 ; number of rows, excl status inc cl ; count from 1, not 0 mov ch,0 dnwp0: ; additional entry point cmp bwnd.lcnt,cx ; enough lines in bottom line buffer? jge dnwp1 ; ge = we have that many lines stored mov cx,bwnd.lcnt ; do as many as we have dnwp1: jcxz dnwp2 ; z = nothing to do cmp tekflg,0 ; Tek mode active? jne dnwp2 ; ne = yes, no scrolling call mscru ; scroll up text one line loop dnwp1 dnwp2: pop cx clc ret dnwpg endp ; home viewing window homwnd proc near push cx mov cx,twnd.lcnt ; all top window lines [dlk] jmp upwp0 ; join upwpg homwnd endp upone proc near ; move text down one line [jrd] push cx mov cx,1 jmp upwp0 upone endp ; scroll viewing window up (text moves down) a page (24 lines) upwpg proc near push cx mov cl,byte ptr low_rgt+1 ; number of rows, excl status line inc cl ; count from 1, not 0 mov ch,0 upwp0: ; additional entry point cmp twnd.lcnt,cx ; enough lines in top line buffer? jae upwp1 ; ae = at least as many as requested mov cx,twnd.lcnt ; do only as many as are stored. upwp1: jcxz upwp2 ; z = no lines to scroll cmp tekflg,0 ; Tek mode active? jne upwp2 ; ne = yes, no scrolling call mscrd ; roll down text one line loop upwp1 upwp2: pop cx clc ret upwpg endp ; Put a line into the circular buffer. Pass the buffer structure in bx. ; Source is tv_segs:si which is the current screen address. ; Rewritten by [jrd] putcirc proc near push es mov cl,crt_cols ; number of columns xor ch,ch mov es,[bx].orig ; get segment of memory area cmp bx,offset bwnd ; bottom buffer? je putci6 ; e = yes mov di,twnd.pp ; pick up buffer ptr (offset from es) add di,cx ; increment to next available slot add di,cx ; char and attribute cmp di,twnd.bend ; would line extend beyond buffer? jb putci1 ; b = not beyond end mov di,0 ; else start at the beginning putci1: mov twnd.pp,di ; update ptr cld ; set direction to forward push ds ; save regular datas seg reg mov ds,tv_segs ; use screen segment for ds:si rep movsw ; copy into buffer pop ds ; restore regular datas segment mov cx,twnd.lmax ; line capacity of buffer dec cx ; minus one work space line cmp twnd.lcnt,cx ; can we increment line count? jae putci1b ; ae = no, keep going inc twnd.lcnt ; else count this line putci1b:cmp bwnd.lcnt,0 ; any lines in bottom buffer? je putci2 ; e = no mov cx,bwnd.pp ; see if we overlap bot buf cmp cx,twnd.pp ; is this line in bot buf area? jne putci2 ; ne = no add cl,crt_cols ; move bottom pointer one slot earlier adc ch,0 add cl,crt_cols ; words adc ch,0 cmp cx,bwnd.bend ; beyond end of buffer? jb putci1a ; b = no mov cx,0 ; yes, start at beginning of buffer putci1a:mov bwnd.pp,cx ; new bottom pointer dec bwnd.lcnt ; one less line in bottom buffer putci2: pop es ret putci6: ; bottom buffer add cx,cx ; words worth cmp bwnd.lcnt,0 ; any lines in the buffer yet? jne putci7 ; ne = yes mov di,twnd.pp ; get latest used slot of top buff add di,cx ; where first free (?) slot starts cmp di,bwnd.bend ; are we now beyond the buffer? jb putci6a ; b = no mov di,0 ; yes, start at beginning of buffer putci6a:add di,cx ; start of second free (?) slot cmp di,bwnd.bend ; are we now beyond the buffer? jb putci6b ; b = no mov di,0 ; yes, start at beginning of buffer putci6b:mov cx,twnd.lmax ; buffer line capacity sub cx,twnd.lcnt ; minus number used by top buffer sub cx,2 ; minus one work slot and one we need cmp cx,0 ; overused some slots? jge putci8 ; ge = enough to share add twnd.lcnt,cx ; steal these from top window beginning jmp short putci8 putci7: mov es,bwnd.orig ; get segment of memory area mov di,bwnd.pp ; pick up buffer ptr (offset from es) cmp di,0 ; would line start before buffer? jne putci7a ; ne = after start of buffer mov di,bwnd.bend ; else start at the end minus one slot inc di putci7a:sub di,cx putci8: mov bwnd.pp,di ; update ptr (this is latest used slot) mov cl,crt_cols xor ch,ch cld ; set direction to forward push ds ; save regular datas seg reg mov ds,tv_segs ; use screen segment for ds:si rep movsw ; copy into buffer pop ds ; restore regular datas segment mov cx,bwnd.lmax ; line capacity of buffer cmp bwnd.lcnt,cx ; can we increment line count? jae putci8b ; ae = no, keep going inc bwnd.lcnt ; else count this line putci8b:cmp twnd.lcnt,0 ; any lines in top line buf? je putci9 ; e = no mov cx,twnd.pp ; yes, see if we used last top line cmp cx,bwnd.pp ; where we just wrote jne putci9 ; not same place, so all is well dec twnd.lcnt ; one less line in top window cmp cx,0 ; currently at start of buffer? jne putci8a ; ne = no mov cx,twnd.bend ; yes inc cx putci8a:sub cl,crt_cols ; back up top window sbb ch,0 sub cl,crt_cols ; by one line sbb ch,0 mov twnd.pp,cx ; next place to read putci9: pop es 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. ; Destination preset in es:di which is the current screen address. ; Rewritten by [jrd] getcirc proc near cmp [bx].lcnt,0 ; any lines in buffer? jne getci1 ; ne = yes, ok to take one out. stc ; else set carry ret ; and return getci1: ; top and bottom window common code test usevb,1 ; video board in use? jz getci1v ; z = no mov cl,crt_cols ; # of chars to copy xor ch,ch push cx ; save around calls mov si,[bx].pp ; this is source cld ; set direction to forward push ds ; save original ds mov ds,[bx].orig ; use seg address of buffer for si rep movsw pop ds ; recover original data segment pop cx ; length for Topview push cx ; save again jmp getci2v getci1v:mov si,[bx].pp ; this is source [begin jhw] push es ; calculate screen location push di call scrseg ; get start of ank seg in es:di pop ax ; restore current curs posit. in ank seg sub ax,di ; compute cursor offset from above pop es div swid2m ; row in al,col in ah xchg ah,al ; ax now contains row/col push ax mov al,' ' ; [rwb] mov ah,scbattr ; [rwb] mov temp,ax ; blank with background attribute [rwb] mov ax,0300h ; save present cursor position int SCREEN mov curpost,dx pop dx ; retrieve row/col (was in ax) ; write line to screen push bx ; save bx before reusing in bios call push es ; save es mov es,[bx].orig ; get segment for circular buffer mov cx,swidth ; # of characters to copy per line getci1a: mov bx,es:[si] ; get character and attribute [rwb] cmp bx,temp ; is it a blank with proper attr [rwb] je getci1x ; yes, no need to display [rwb] push si ; potentially zapped by int 10h [rwb] push cx ; save character count for loop counter mov ax,0200h int SCREEN ; position the cursor mov ax,bx ; get attr/char in ah,al [rwb] mov bl,ah mov cx,1 mov ah,09 int SCREEN ; display character with attribute pop cx ; restore character count pop si ; [rwb] getci1x: inc si inc si cmp dl,79 ; are we at end of line? jae getci1b inc dl getci1b: loop getci1a ; loop until line is written pop es ; restore es ; mov dx,curpost ; restore the cursor to where it was mov ax,0200h int SCREEN ; above code assumes dx is preserved ; through bios calls [end jhw] pop bx ; restore bx [jhw] mov cx,swidth ; length for Topview push cx ; save again getci2v: call scrsync ; synch Topview mov si,[bx].pp ; get ptr again pop cx add cx,cx ; words cmp bx,offset bwnd ; bottom window? je getci6 ; e = yes sub si,cx ; top window, move back jnc getcir2 ; nc = still in buffer, continue mov si,twnd.bend ; else use end of buffer sub si,cx ; minus length of a piece inc si getcir2:mov twnd.pp,si ; update ptr dec twnd.lcnt ; decrement # of lines in buffer clc ; make sure no carry ret getci6: ; bottom window add si,cx ; words, move back (bot buf = reverse) cmp si,bwnd.bend ; still in buffer? jb getci7 ; b = yes mov si,0 ; else use beginning of buffer getci7: mov bwnd.pp,si ; update ptr dec bwnd.lcnt ; decrement # of lines in buffer clc ; make sure no carry ret getcirc endp ; ; CHGDSP dummy routine which sets 132 col display boards on ibm ; not useful on 550 chgdsp proc near ret ; return to caller chgdsp 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 if1 %out [End of pass 1] else %out [End of assembly] endif end