;------------------------------------------------------------------------; ; ANSI.COM - Replacement for the ANSI.SYS console device driver. ; ; Unlike ANSI.SYS which must be installed at boot time, ANSI.COM ; ; can be installed and uninstalled at anytime. Enhancements include ; ; a fast screen write and variable sized keyboard reassignment buffer. ; ; ; ; Update 3/2/89 - Fix for DOS function 0Bh, Check Standard Input Status ; ; and STDIN in ANSI_INT_21 handler. ; ; Leading zero inserted for Device Status Report for ; ; single digit cursor positions. ; ; INFORMATION typo 40 for 40H. ; ; WRITE_FAST modified to handle CR and LF instead of ; ; WRITE_CHAR. ; ; Update 3/7/89 - Fix for CLS in graphics mode. Version 1.2 ; ; ; ; Update 8/8/89 - STI added to INT 21 and 29 handler. Version 1.3 ; ;: ; ;: Update 01/01/90 - [KON | KOFF] and [PON | POFF] added Version 1.31 ; ;: (all changes marked with ;: - Gary Meeker) ; ;| Update 03/23/90 - /Q option added for no output when exectued 1.32 ; ;| (all changes marked with ;| - Gary Meeker) ; ;; Update 05/16/90 - removed Syntax from output unless needed 1.33 ; ;; added /T test for load. ; ;; (all changes marked with ;; - Gary Meeker) ; ;. Update 10/28/90 - Added /P nn top of screen protection mode 1.34 ; ;. (all changes marked with ;. - Gary Meeker) ; ;@ Update 12/01/90 - Added PCBoard @X## color code support 1.35 ; ;@ also added @Variable@ Support ; ;@ added /S to load stats data from file ; ;@ (all changes marked with ;@ - Gary Meeker) ; ;& ; ;& Update 12/12/90 - My mistake, I didn't realize @X00 saved 1.36 ; ;& the color and @XFF restored it. Works now. ; ;& Also made prompts return to start of line ; ;& when cleared (like PCBoard does) & QON ; ;& now changes @MORE@ & @PAUSE@ into @WAIT@ ; ;& (all changes marked with ;& - Gary Meeker) ; ;$ Update 4/22/91 - Change set cursor routine. If new position 1.37 ; ;$ is off the screen use the last row or column. ; ;$ Added switch to not include PCB code. ; ;$ Corrected syntax display. ; ;$ Modified CLS to honor protected (/P) lines. ; ;$ Modified /T to returned status switches. ; ;$ Modified /P to accept * to mean current row-1. ; ;$ (all changes marked with ;$ - Wayne Mingee) ; ; ; ; PC Magazine - Michael J. Mefford ; ;------------------------------------------------------------------------; PCB equ 0 ;$ 1 = if PCB code wanted _TEXT SEGMENT PUBLIC 'CODE' ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT ORG 100H START: JMP INITIALIZE ; DATA AREA SIGNATURE DB CR,SPACE,SPACE,SPACE,CR,LF COPYRIGHT DB "ANSI 1.3g (C) 1988 Ziff Communications Co.",CR,LF ;. PROGRAMMER DB "PC Magazine ",BOX," Michael J. Mefford",CR,LF,LF,"$" DB CTRL_Z CR EQU 13 LF EQU 10 CTRL_Z EQU 26 SPACE EQU 32 BOX EQU 254 ESC_CHAR EQU 27 SINGLE_QUOTE EQU 39 DOUBLE_QUOTE EQU 34 BELL EQU 7 BS EQU 8 TAB EQU 9 OFF EQU 1 ON EQU 2 SLOW EQU 4 FAST EQU 8 KOFF EQU 16 ;: KON EQU 32 ;: POFF EQU 64 ;: PON EQU 128 ;: STATUS_MASK EQU 1111110000000001B ;: Set Mask and Bit ANSI_STATE DW ESC_STATE STATUS DB ON OR FAST OR KON OR PON ;: PARAMETERS DB "OFF",0,"ON",0,0,"SLOWFASTKOFFKON",0,"POFFPON",0 ;: LAST_PARAMETER EQU $ - PARAMETERS OLD_INT_29 DW ?,? OLD_INT_16 DW ?,? OLD_INT_21 DW ?,? if PCB ;$ OLD_INT_08 DW ?,? ;@ Save Timer Interupt endif ;$ ATTRIBUTE DB 7 if PCB ;$ SAVE_ATTRIBUTE DB ? ;& For saving attribute endif ;$ SAVE_POSITION DW 0 LINE_WRAP DB ON QUOTE_TYPE DB ? ESC_COUNT DW 0 NUMBER_COUNT DW 0 COMMAND_TABLE LABEL BYTE DB "H", "A", "B", "C", "D", "f", "n", "s", "u", "K", "m", "h", "l", "p", "J" COMMAND_LENGTH EQU $ - COMMAND_TABLE DW CURS_POSITION, CURSOR_UP, CURSOR_DOWN, CURS_FORWARD, CURS_BACKWARD DW HORZ_VERT_POS, DEVICE_STATUS, SAVE_CURSOR, RESTORE_CURS, ERASE_IN_LINE DW SGR, SET_MODE, RESET_MODE, REASSIGNMENT, CLS COMMAND_END EQU $ - 2 ATTRIBUTE_TABLE LABEL BYTE DB 00,01,04,05,07,08,30,31,32,33,34,35,36,37,40,41,42,43,44,45,46,47 ATTRIBUTE_LENGTH EQU $ - ATTRIBUTE_TABLE ;Format: AND mask,OR mask DB 000H,07H, 0FFH,08H, 0F8H,01H, 0FFH,80H, 0F8H,70H, 088H,00H DB 0F8H,00H, 0F8H,04H, 0F8H,02H, 0F8H,06H, 0F8H,01H, 0F8H,05H DB 0F8H,03H, 0F8H,07H, 08FH,00H, 08FH,40H, 08FH,20H, 08FH,60H DB 08FH,10H, 08FH,50H, 08FH,30H, 08FH,70H ATTRIBUTE_END EQU $ - 2 if PCB ;$ AT_VARIABLE_TABLE LABEL BYTE ;@ DB '@CLS@CLREOL@AUTOMORE@POFF@PON@QOFF@QON@BEEP@MORE@PAUSE@WAIT@HANGUP@' ;@ DB 'OPTEXT@FIRSTU@FIRST@USER@CITY@HOMEPHONE@DATAPHONE@PROLTR@PRODESC@' ;@ DB 'EXPDATE@LASTDATEON@LASTTIMEON@INCONF@CONFNAME@BOARDNAME@' ;@ DB 'LASTCALLERNODE@LASTCALLERSYSTEM@EVENT@SYSOPIN@SYSOPOUT@BPS@NODE@' ;@ DB 'SYSDATE@SYSTIME@FILERATIO@BYTERATIO@' ;@ DB 'SECURITY@NUMCALLS@NUMTIMESON@TIMELEFT@TIMELIMIT@TIMEUSED@TOTALTIME@' ;@ DB 'BYTESLEFT@BYTELIMIT@DLFILES@UPFILES@KBLEFT@KBLIMIT@CONFNUM@' ;@ DB 'CURMSGNUM@HIGHMSGNUM@LOWMSGNUM@MSGREAD@MSGLEFT@NUMBLT@NUMDIR@' ;@ DB 'DAYBYTES@MINLEFT@DLBYTES@UPBYTES@EXPDAYS@' ;@ LAST_VARIABLE EQU $ - 1 ;@ JUMP_TABLE LABEL WORD ;@ DW CLS, ERASE_IN_LINE, AUTOMORE, AUTOOFF, AUTOOFF, QOFF, QON ;&;@ DW BEEP, MORE, PAUSE, WAIT1, SKIP_IT ;@ SUB_TABLE LABEL BYTE ;@ DB 8,15,15,25,24,13,13,1,47 ;@ DB 8,8,5,33,13,63 ;@ DB 52,52,5,5,5,5,2 ;@ DB 0,0,4,4 ;@ DB 2,4,2,2,2,2,2 ;@ DB 4,4,2,2,4,4,2 ;@ DB 4,4,4,4,4,2,2 ;@ DB 4,2,4,4,2 ;@ SUB_LIST LABEL BYTE ;@ DB '!OPTEXT!' ;@ OPTTEXT DB 'GARY',0,0,0,0,0,0,0,0,0,0,0 ;@ FirstU DB 'Gary',0,0,0,0,0,0,0,0,0,0,0 ;@ First DB 'GARY MEEKER',0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;@ User DB 'LAWRENCEVILLE, GA',0,0,0,0,0,0,0 ;@ City DB '404 995-2699',0 ;@ HomePhone DB '404 962-1788',0 ;@ DataPhone DB 'Z' ;@ Proltr DB 'Zmodem (Batch U/L and D/L)', 15 DUP (0) ;@ ProDesc DB '01-01-91' ;@ ExpDate DB '12-01-90' ;@ LastDateOn DB '08:00' ;@ LastTimeOn DB 'Magazine (1) Conference ',0,0,0,0,0,0,0,0,0 ;@ InConf DB 'Magazine',0,0,0,0,0 ;@ ConfName DB 'SHARP Technical Support Line BBS', 31 DUP (0) ;@ BoardName DB 'MIKE BATE (RIVERDALE, GA)', 27 DUP (0) ;@ LastCallerNode DB 'GARY MEEKER (LAWRENCEVILLE, GA)', 21 DUP (0) ;@ LastCallerSystem DB '04:30' ;@ Event DB '08:30' ;@ SysopIn DB '17:00' ;@ SysopOut DB '2400',0 ;@ bps DB '1',0 ;@ Node DD 48 ;@ FileRatio DD 128 ;@ ByteRatio DW 120 ;@ Security DD 23946 ;@ numcalls DW 2107 ;@ numtimeson DW 61 ;@ timeleft DW 121 ;@ timelimit DW 59 ;@ timeused DW 59 ;@ totaltime DD 9508313 ;@ bytesleft DD 10238976 ;@ bytelimit DW 44 ;@ dlfiles DW 1234 ;@ upfiles DD 9285 ;@ KBLeft DD 9999 ;@ KBLimit DW 1 ;@ ConfNum DD 4734 ;@ CurMsgNum DD 4745 ;@ HighMsgNum DD 2 ;@ LowMsgNum DD 3456 ;@ MsgRead DD 987 ;@ MsgLeft DW 20 ;@ NumBlt DW 31 ;@ NumDir DD 30663 ;@ DayBytes DW 61 ;@ MinLeft DD 730663 ;@ DLBytes DD 12345678 ;@ UpBytes DW 0 ;@ ExpDays SUB_LENGTH EQU $ - SUB_LIST PAUSE_TIMER DW 182 ;@ PAUSE_DELAY DW 182 ;@ 10 Seconds MORE_DELAY DW 182 * 12 ;@ 2 Minute WAIT_DELAY DW 182 * 12 ;@ 2 Minute WAIT_PROMPT DB 'press enter to continue' ;@ WAIT_LENGTH DW $ - WAIT_PROMPT ;@ MORE_PROMPT DB '(61 min left), (H)elp, More?' ;@ MORE_LENGTH DW $ - MORE_PROMPT ;@ COMMA DB 0 ;@ Q_STATE DB 0 ;& endif ;$ BIOS_ACTIVE_PAGE EQU 62H ACTIVE_PAGE DB ? ADDR_6845 DW ? BIOS_CRT_MODE EQU 49H CRT_MODE DB ? CRT_COLS DW ? CRT_LEN DW ? CRT_START DW ? CRT_DATA_LENGTH EQU $ - CRT_MODE CURSOR_POSN LABEL WORD CURSOR_COL DB ? CURSOR_ROW DB ? CRT_ROWS DB ? START_SCROLL DB 0 ;. Number of rows to protect from scrolling up DOS_INPUT DB OFF BUSY_FLAG DB OFF REASSIGN_FLAG DB OFF REMOVE_FLAG DB ON REASSIGN_COUNT DW 0 REASSIGN_POS DW ? FUNCTION_16 DB ? ZR EQU 1000000B ESC_BUFFER_SIZE EQU 126 REASSIGNMENT_DEFAULT EQU 200 REASSIGNMENT_SIZE DW REASSIGNMENT_DEFAULT REASSIGNMENT_MAX EQU 60 * 1024 REASSIGN_END DW REASSIGNMENT_BUFFER BUFFER_END DW REASSIGNMENT_BUFFER + REASSIGNMENT_DEFAULT ; CODE AREA ;************* INTERRUPT HANDLERS *************; ;------------------------------------------------------------------------------; ; INT 29 is an undocumented interrupt called by DOS for output to the console. ; ;------------------------------------------------------------------------------; ANSI_INT_29 PROC FAR STI PUSH AX ;Save all registers. PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI PUSH DS PUSH ES PUSH BP CLD ;All string operations forward. MOV BX,CS ;Point to our data segment. MOV DS,BX MOV ES,BX CALL ANSI_STATE ;Call the current state of ; the ANSI Esc sequence. POP BP POP ES POP DS POP DI POP SI POP DX POP CX POP BX POP AX ;Restore all registers and IRET ; and return ANSI_INT_29 ENDP ;------------------------------------------------; ANSI_INT_21 PROC FAR PUSHF CMP AH,0BH ;If DOS function 0Bh (Check JNZ CK_INPUT ; Standard Input Status) CMP CS:REASSIGN_FLAG,ON ; and reassignment in progress, JNZ CK_INPUT ; including a Device Status CALL DWORD PTR CS:OLD_INT_21 ; Request, then process call in MOV AL,0FFH ; case control break pressed; IRET ; then return 0FFh for char ready CK_INPUT: CMP AH,3FH ;Read from STDIN? JNZ CK_CONSOLE OR BX,BX JZ INPUT CK_CONSOLE: CMP AH,0AH ;If DOS Int 21 functions Ah, JZ INPUT ; 7h, 1h or console input (6h) CMP AH,7 ; then tell INT 16, DOS input JZ INPUT ; is active. CMP AH,1 JZ INPUT CMP AH,8 JZ INPUT CMP AH,6 JNZ QUICK21_EXIT CMP DL,0FFH JZ INPUT QUICK21_EXIT: POPF ;If not, let the original JMP DWORD PTR CS:OLD_INT_21 ; interrupt process the call. INPUT: POPF MOV CS:DOS_INPUT,ON ;INT 16 handler flag. PUSHF CALL DWORD PTR CS:OLD_INT_21 ;Emulate an interrupt. MOV CS:DOS_INPUT,OFF ;Turn flag back off. STI RET 2 ;Return with current flags. ANSI_INT_21 ENDP ;-----------------------------------------------------------------; ; If we get here via a DOS console input call, any awaiting key ; ; in keyboard buffer is checked to see if it is to be reassigned. : ;-----------------------------------------------------------------; ANSI_INT_16 PROC FAR STI ;Interrupts back on. PUSHF ;Preserve flags on stack. PUSH BP PUSH DS PUSH AX PUSH CS ;Point to our data. POP DS CMP DOS_INPUT,OFF ;If not called via a DOS JZ QUICK16_EXIT ; console input call, exit. CMP BUSY_FLAG,ON ;If already processing a call, JZ QUICK16_EXIT ; exit. CLD ;All string operations forward. MOV BP,SP ;Base reference to flags on stack MOV FUNCTION_16,AH ;Save function request. AND AH,NOT 10H ;Strip possible extended request. JZ GET_KEY ;If zero, it's wait for a key. DEC AH ;Else, decrement. If zero it's JZ KEY_STATUS ; key status, else exit. QUICK16_EXIT: POP AX ;Restore registers and let POP DS ; original interrupt handler POP BP ; process the call. POPF JMP DWORD PTR CS:OLD_INT_16 KEY_STATUS: OR BYTE PTR [BP+6],ZR ;Assume none ready; ZR = 1. GET_KEY: MOV BUSY_FLAG,ON ;Non-reentrant; flag busy. POP AX PUSH BX ;Save some more registers. PUSH CX PUSH DX PUSH SI PUSH DI CMP REASSIGN_FLAG,ON ;Already in process of reassign? JZ CK_BUFFER ;If yes, check kbd buffer. PUSHF ;Else, emulate an interrupt. CALL DWORD PTR OLD_INT_16 PUSHF ;Status call results in flags. TEST FUNCTION_16,1 ;Was it a status call? JZ CK_DUP ;If no, check key returned. POPF ;Else, retrieve status results. JZ INT16_EXIT ;If zero, no key waiting. AND BYTE PTR [BP+6],NOT ZR ;Else, ZR = 0. PUSHF ;PUSHF just to keep stack right. CK_DUP: POPF ;Fix stack. TEST STATUS,KOFF ;:If KOFF then we don't do JNZ INT16_EXIT ;:re-assignments MOV BX,AX ;Match procedure wants key in BX. CALL CK_MATCH ;Check reassignment buffer. JC INT16_EXIT ;If no match, exit. MOV AX,[DI] ;Else, retrieve string length. SUB AX,3 ;Adjust for length word and match ADD DI,3 ; byte; same for string pointer. OR BL,BL ;Extended key? ie. key = 0. JNZ STORE_COUNT ;If no, save length and pointer. DEC AX ;Else, adjust for extended code. INC DI STORE_COUNT: MOV REASSIGN_COUNT,AX ;Save string length. MOV REASSIGN_POS,DI ;Save pointer to string. MOV REASSIGN_FLAG,ON ;Flag that replacement in effect. CK_BUFFER: TEST FUNCTION_16,1 ;Was it a key wait function call? JNZ RETRIEVE ;If no, key status; get it. CMP REMOVE_FLAG,OFF ;Removed it from kbd buffer? JZ RETRIEVE ;If yes, get reassignment. MOV AH,FUNCTION_16 ;Else, eat the replaced character INT 16H ; via INT 16. MOV REMOVE_FLAG,OFF ;Flag character eaten. RETRIEVE: MOV DI,REASSIGN_POS ;Retrieve pointer to string. MOV AX,[DI] ;Retrieve replacement character. TEST FUNCTION_16,1 ;Is it a key wait function call? JZ REMOVE ;If yes, bump pointer up one. AND BYTE PTR [BP+6],NOT ZR ;If no, status call; ZR = 0 JMP SHORT INT16_EXIT ;All done. REMOVE: INC REASSIGN_POS ;Move pointer to next character. DEC REASSIGN_COUNT ;One less character to replace. JZ REASSIGN_DONE ;If end of string, reset flags. OR AL,AL ;Else, extended replacement? JNZ INT16_EXIT ;If no, done here. INC REASSIGN_POS ;Else adjust pointers. DEC REASSIGN_COUNT JNZ INT16_EXIT ;End of string? REASSIGN_DONE: MOV REASSIGN_FLAG,OFF ;If yes, reset flags. MOV REMOVE_FLAG,ON INT16_EXIT: MOV BUSY_FLAG,OFF ;Reset the busy flag. POP DI ;Restore registers. POP SI POP DX POP CX POP BX POP DS POP BP POPF ;Flags on stack have status RET 2 ; results; kill old flags. ANSI_INT_16 ENDP if PCB ;$ ANSI_INT_08 PROC FAR ;@ CMP CS:[PAUSE_TIMER],0 ;@ JE SKIP_TIMER ;@ DEC CS:[PAUSE_TIMER] ;@ SKIP_TIMER: JMP DWORD PTR CS:OLD_INT_08 ;@ ANSI_INT_08 ENDP ;@ endif ;$ ;************* ANSI ESCAPE STATE ROUTINES ************* ; ESC_STATE: MOV BX,OFFSET BRACKET_STATE ;Assume Esc character. CMP AL,ESC_CHAR ;Is it Esc? If yes, store JZ SHORT_JUMP ; char. and next state. if PCB ;$ CMP DOS_INPUT,ON ;@ Called via DOS Input? JE WRITE_CHAR2 ;@ Yes, Nevermind MOV BX,OFFSET ATX_STATE ;@ No, Assume @ Character CMP AL,"@" ;@ Is it @? If yes, store JZ SHORT_JUMP ;@ char. and next state. WRITE_CHAR2: endif ;$ JMP WRITE_CHAR ;@ Else, normal char; write it. ;------------------------------------------------; BRACKET_STATE: MOV BX,OFFSET SPECIAL_STATE ;Assume left bracket. CMP AL,"[" ;Is it left bracket? If yes, SHORT_JUMP: JZ STORE_STATE ;store char. and next state. JMP FLUSH_BUFFER ;Else, flush Esc out of buffer. ;---------------------------------------------------------------; ; Must process [2J (CLS) regardless if ANSI.COM ON or OFF. ; ;---------------------------------------------------------------; SPECIAL_STATE: MOV BX,OFFSET CLS_STATE ;Assume Erase in Display code. CMP AL,"2" ;Is it the "2" ? JZ STORE_STATE ;If yes, progress to next state. TEST STATUS,OFF ;Else, is ANSI OFF ? JNZ FLUSH_BUFFER ;If yes, flush Esc, bracket. MOV BX,OFFSET PARAM_STATE ;Else, check for first Set, CMP AL,"=" ; Reset Mode characters of JZ STORE_STATE ; "=" and "?". CMP AL,"?" JZ STORE_STATE ;If found, store. JMP SHORT DO_PARAMETER ;Else, check other codes. ;------------------------------------------------; CLS_STATE: CMP AL,"J" ;Is it second character of CLS? MOV DI,OFFSET COMMAND_END ;If yes, execute JZ EXECUTE ; Erase in Display procedure. TEST STATUS,OFF ;ANSI OFF? If yes, flush buffer. JNZ FLUSH_BUFFER ; If not fall through to process. MOV BYTE PTR NUMBER_BUFFER,2 ;Store the previous 2. DO_PARAMETER: MOV ANSI_STATE,OFFSET PARAM_STATE ;Parameter state. ;------------------------------------------------; PARAM_STATE: CALL CK_QUOTE ;Is it single or double quotes? JZ STORE_STATE ;If yes, store string state. CMP AL,";" ;Is it semi-colon delimiter? JNZ CK_NUMBER ;If no, check if number. INC NUMBER_COUNT ;Else, increment number count. JMP SHORT BUFFER_CHAR ;Buffer the semi-colon. CK_NUMBER: CMP AL,"0" ;Is it below 0 ? JB FLUSH_BUFFER ;If yes, illegal; flush buffer. CMP AL,"9" ;Else, is it above 9 ? JA DO_COMMAND ;If yes, check for command char. CALL ACCUMULATE ;Else it's a number; accumulate. JMP SHORT BUFFER_CHAR ;Buffer the character. DO_COMMAND: MOV DI,OFFSET COMMAND_TABLE ;Point to legal ANSI commands. MOV CX,COMMAND_LENGTH ;Number of commands. REPNZ SCASB ;Check for a match. JNZ FLUSH_BUFFER ;If no match, flush sequence. INC NUMBER_COUNT ;Else, increment for last number. MOV DI,OFFSET COMMAND_END ;Point to appropriate command SHL CX,1 ; processing procedure. SUB DI,CX EXECUTE: CALL GET_BIOS_DATA ;Get cursor position data. CALL DS:[DI] ;Do command subroutine. JMP SHORT FLUSH_END ;Clear buffer. ;------------------------------------------------; QUOTE_STATE: CMP AL,QUOTE_TYPE ;Is it an ending string quote? JNZ BUFFER_CHAR ;If no, buffer literal. MOV BX,OFFSET PARAM_STATE ;Else, back to parameter parsing. ;------------------------------------------------; STORE_STATE: MOV ANSI_STATE,BX ;Store the ANSI state. ;------------------------------------------------; BUFFER_CHAR: MOV DI,ESC_COUNT ;Buffer the character in case CMP DI,ESC_BUFFER_SIZE ; ending ANSI command is illegal. JZ FLUSH_BUFFER ;If buffer full, flush. ADD DI,OFFSET ESC_BUFFER ;Point to next buffer position. STOSB ;Store the character. INC ESC_COUNT ;Increment the sequence count. RET ;------------------------------------------------; FLUSH_BUFFER: ;$ if PCB ;$ CALL BUFFER_CHAR ;@ Buffer current character also! FLUSH_BUFF2: ;@ else ;$ PUSH AX ;Save the current character. endif ;$ MOV SI,OFFSET ESC_BUFFER ;Point to the sequence buffer. MOV CX,ESC_COUNT ;Count of buffered characters. NEXT_FLUSH: LODSB ;Retrieve one. PUSH CX ;Save counter and pointer. PUSH SI CALL WRITE_CHAR ;Write character to screen. POP SI ;Restore counter and pointer. POP CX LOOP NEXT_FLUSH ;Flush entire buffer. ife PCB ;$ POP AX ;@Retrieve last character. CALL WRITE_CHAR ;@Write it also. endif ;$ FLUSH_END: MOV ESC_COUNT,0 ;Reset counter. MOV ANSI_STATE,OFFSET ESC_STATE ;Back to Esc state. MOV NUMBER_COUNT,0 ;Reset parameter counter. PUSH CS ;Point to our data. POP ES MOV DI,OFFSET NUMBER_BUFFER ;Clear number buffer MOV CX,ESC_BUFFER_SIZE / 2 ; to zeros. XOR AX,AX REP STOSW RET if PCB ;$ ;------------------------------------------------;@ COLOR_STATE: CMP AL,"0" ;@ Is it below 0 ? JB FLUSH_BUFFER ;@ If yes, illegal; flush buffer. CMP AL,"9" ;@ Else, is it 0 - 9 ? JBE DO_COLOR ;@ If yes, Make color. CMP AL,'A' ;@ Is it below A ? JB FLUSH_BUFFER ;@ If yes, illegal; flush buffer. CMP AL,'F' ;@ Is it Above F ? JA FLUSH_BUFFER ;@ If yes, illegal; flush buffer. SUB AL,7 ;@ Else, adjust for Hex DO_COLOR: CALL ACCUM_COLOR ;@ Accumulate Color. INC NUMBER_COUNT ;@ Count the character CMP NUMBER_COUNT,2 ;@ Already got two? BUFFER_JUMP: JNE SHORT BUFFER_CHAR ;&;@ No, Buffer the character. TEST STATUS,OFF ;@ Else, is ANSI OFF ? JNZ FLUSH_BUFFER ;@ If yes, flush Esc, bracket. INC CL ;& Is it @XFF restore code? JNZ COLOR_2 ;& No, Check for save code MOV CL,SAVE_ATTRIBUTE ;& Yes, Get back saved attribute JMP SHORT COLOR_3 ;& and set it COLOR_2: DEC CL ;& Is it @X00 save code JNZ COLOR_3 ;& No, just set it then MOV CL,ATTRIBUTE ;& Yes, Save the attribute MOV SAVE_ATTRIBUTE,CL ;& JMP FLUSH_END ;& and flush it COLOR_3: MOV ATTRIBUTE,CL ;@ Set Attribute JMP FLUSH_END ;@ Clear Buffer ;------------------------------------------------;@ VARIABLE_SUB: CMP AL,'@' ;@ Is it another @ JB SHORT_FLUSH ;@ No, and not valid, so flush JNZ BUFFER_JUMP ;&;@ No, Buffer the Char (indirect) MOV DX,OFFSET AT_VARIABLE_TABLE ;@ Point to Variable List XOR BX,BX ;@ Position in Variable List NEXT_VARI: MOV DI,DX ;@ Load DI INC BX ;@ Count the Variable MOV SI,OFFSET ESC_BUFFER ;@ Point to our Variable MOV CX,OFFSET LAST_VARIABLE ;@ Point to End of list SUB CX,DI ;@ Minus our current location MOV AL,'@' ;@ We need to start on these REPNE SCASB ;@ so find one JCXZ SHORT_FLUSH ;@ End of List? Yes MOV DX,DI ;@ No,Save Variable list pointer DEC DI ;@ No, Back up to the @ MOV CX,ESC_COUNT ;@ Length of Variable REP CMPSB ;@ See if this one matches? JNZ NEXT_VARI ;@ Didn't match that one! CMP [DI],AL ;@ Last Character has to be an @ JNZ NEXT_VARI ;@ Nope DEC BX ;@ Back up 1 CMP BX,11 ;@ Is it an Action code JA NOT_ACTION ;@ No SHL BX,1 ;@ BX x 2 for proper offset MOV DI,OFFSET JUMP_TABLE ;@ Point to command table ADD DI,BX ;@ Adjust to the desired command JMP EXECUTE ;@ Execute the command NOT_ACTION: MOV SI, OFFSET SUB_TABLE ;@ Point to Sub Table MOV AX,BX ;@ Save Variable Number MOV CX,BX ;@ Copy Variable Number XOR BX,BX ;@ Zero Offset XOR DX,DX ;@ Zero out initial length SUB CX,11 ;@ Adjust Variable Number NEXT_OFFSET: ADD BX,DX ;@ Add last Length to Offset MOV DL,[SI] ;@ Get Length INC SI ;@ Bump the pointer LOOP NEXT_OFFSET ;@ Do all of them MOV CX,DX ;@ Get last Length MOV SI,OFFSET SUB_LIST ;@ Point to the substitue buffer. ADD SI,BX ;@ Add Offset SUB AX,34 ;@ Is It a String Variable JB IS_STRING ;@ Yes, Flush our Substitute CMP AX,1 ;@ Is it Date ot Time? JBE IS_TIMEDATE ;@ No, It's a Number SUB AX,2 ;@ Adjust Variable number CALL NUMBER_PARSE ;@ Create a Numeric value then JMP SHORT VAR_END ;@ Flush our Number IS_TIMEDATE: CALL MAKE_TIMEDATE ;@ IS_STRING: MOV DI,SI ;@ Copy Pointer to DI for SCASB MOV AL,0 ;@ We look for a Zero REPNE SCASB ;@ Findit it JNE GOT_END ;@ Skip if we didn't hit a Zero DEC DI ;@ Back up if we did GOT_END: MOV CX,DI ;@ Get Current Location SUB CX,SI ;@ Subtract Starting Location VAR_END: JMP NEXT_FLUSH ;@ Flush our Substitute SHORT_FLUSH: PUSH AX ;& Save Character CALL FLUSH_BUFF2 ;@ Flush what we have now POP AX ;& Restore Character JMP ESC_STATE ;& And save the new state ;------------------------------------------------;@ ATX_STATE: MOV BX,OFFSET COLOR_STATE ;@ Assume 'X' Character CMP AL,"X" ;@ Is it X? If yes, store JZ SHORT_STORE ;@ char. and next state. CMP AL,'@' ;@ Is it another '@' already? JZ WRITE_CHAR ;@ Yes! CMP AL,ESC_CHAR ;& Is it an Escape JE SHORT_FLUSH ;& Yes, so flush buffer & restart MOV BX,OFFSET VARIABLE_SUB ;@ No, Must be a Variable SHORT_STORE: JMP STORE_STATE ;@ Store the state endif ;$ ;------------------------------------------------; ; Slow video writes are via BIOS Write TTY. ; ;------------------------------------------------; WRITE_CHAR: CMP AL,BELL ;Let BIOS process BS and BELL. JZ WRITE_TTY CMP AL,BS JZ WRITE_TTY CALL GET_BIOS_DATA ;Get BIOS video data. CMP AL,TAB ;Is character a TAB? JNZ CK_ACTIVE ;If no, process normally. MOV CX,CURSOR_POSN ;Else, expand TAB to AND CX,7 ; appropriate space characters. NEG CX ADD CX,8 NEXT_TAB: PUSH CX MOV AL,SPACE CALL CK_ACTIVE POP CX LOOP NEXT_TAB RET CK_ACTIVE: TEST STATUS,OFF ;Is ANSI OFF? JNZ WRITE_TTY ;If yes, write via BIOS TTY. CK_FAST: CALL CK_SLOW_TEXT ;Is ANSI SLOW or in graphics JNC WRITE_FAST ; mode? If no, write fast. CMP AL,CR ;Let BIOS handle CR and LF. JZ WRITE_TTY CMP AL,LF JZ WRITE_TTY WRITE_SLOW: PUSH AX ;Else, write character/attribute MOV CX,1 ; at current cursor position MOV BH,ACTIVE_PAGE ; via BIOS. MOV BL,ATTRIBUTE MOV AH,9 INT 10H POP AX CMP LINE_WRAP,ON ;Is line wrap on? JZ TTY ;If yes, continue. MOV CX,CRT_COLS ;Else, cursor at rightmost DEC CL ; column? CMP CL,CURSOR_COL ;If yes, continue, else JNZ TTY ; return without writing. RET ;------------------------------------------------; WRITE_TTY: MOV BL,7 ;Attribute in graphics mode. TTY: MOV AH,0EH INT 10H RET ;----------------------------------------------------------------------------; ; Fast screen writes are directly to the video buffer without retrace check. ; ;----------------------------------------------------------------------------; WRITE_FAST: PUSH ES ;Preserve extra segment. MOV DX,CURSOR_POSN ;Retrieve cursor position. CMP AL,CR ;Carriage return? JNZ CK_LINEFEED ;If no, check linefeed. XOR DL,DL ;Else, cursor to first column. JMP SHORT UPDATE_CURSOR CK_LINEFEED: CALL VIDEO_SETUP ;Calculate video address. CMP AL,LF ;Linefeed? JZ NEXT_ROW ;If yes, next row. MOV AH,ATTRIBUTE ;Retrieve attribute. STOSW ;Put char/attrib in video buffer. INC DL ;Increment cursor column. CMP DL,BYTE PTR CRT_COLS ;End of row? JB UPDATE_CURSOR ;If no, update cursor. CMP LINE_WRAP,OFF ;Else, line wrap off? JZ FAST_END ;If yes, don't move cursor. XOR DL,DL ;Else, column zero. NEXT_ROW: INC DH ;Next row. CALL INFORMATION ;Get displayable row info. CMP DH,AL ;Beyond the bottom of screen? JBE UPDATE_CURSOR ;If no, update cursor. DEC DH ;Else, cursor to original row. MOV AX,CRT_COLS ;. Get number of columns PUSH DX ;. Save Cursor position PUSH AX ;. Save Columns SHL AX,1 ;. Twice for Char/Attribute MOV SI,AX ;. Starting offset in SI MOV DL,START_SCROLL ;. Where we allow scroll to start MUL DL ;. Calculate offset in AX SUB DH,DL ;. Reduce line count MOV DI,CRT_START ;Point destination to top. ADD DI,AX ;. Add offset ADD SI,DI ;. Point source to second row POP AX ;. Get Back Columns PUSH AX ;. Save it again MUL DH ;Times displayable rows - 1. MOV CX,AX ; equals char/attrib to scroll. PUSH DS ;Save data segment and PUSH ES ; point to video segment. POP DS REP MOVSW ;Scroll the screen. POP DS ;Restore data segment. POP CX ;Retrieve CRT columns. POP DX ;.Get back Cursor info MOV AL,SPACE ;Write space/attrib to MOV AH,ATTRIBUTE ; bottom row. REP STOSW UPDATE_CURSOR: CALL SET_CURSOR ;Update the cursor position. FAST_END: POP ES ;Restore extra segment. RET if PCB ;$ ;@************ SUPPORT ROUTINES *************; QOFF: MOV AL,0FFH ;& Show QOFF JMP SHORT QON2 ;& QON: XOR AL,AL ;& Show QON QON2: MOV Q_STATE,AL ;& Store the state RET ;& All done BEEP: MOV AL,7 ;@ JMP WRITE_CHAR ;@ MORE: MOV AX,MORE_DELAY ;@ JMP SHORT PAUSE2 ;@ PAUSE: MOV AX,PAUSE_DELAY ;@ PAUSE2: CMP Q_STATE,0 ;& Are we at a QOFF state JNZ WAIT1 ;& Yes, then these are WAITs MOV SI,OFFSET MORE_PROMPT ;@ Point to More? Prompt MOV CX,MORE_LENGTH ;@ Get the Length of it JMP SHORT WAIT2 ;@ Go Wait WAIT1: MOV AX,WAIT_DELAY ;@ MOV SI,OFFSET WAIT_PROMPT ;@ Point to Wait Prompt MOV CX,WAIT_LENGTH ;@ Get the Length of it WAIT2: MOV PAUSE_TIMER,AX ;@ ; PUSH CX ;@ Save Length WAIT2_LOOP: LODSB ;@ Get Character PUSH CX ;@ PUSH SI ;@ CALL WRITE_CHAR ;@ Output it POP SI ;@ POP CX ;@ LOOP WAIT2_LOOP ;@ Do all the Characters WAIT_LOOP: MOV AH,1 ;@ KeyBoard Status INT 16H ;@ Keyboard I/O Services JNZ CONTINUE ;@ Key Hit! CMP PAUSE_TIMER,0 ;@ Timer Run out? JNE WAIT_LOOP ;@ No JMP SHORT WAIT3 ;@ Yes CONTINUE: MOV AH,0 ;@ KeyBoard Read INT 16H ;@ Keyboard I/O Services WAIT3: ;@ XOR CL,CL ;@ Clear the Attribute XCHG CL,ATTRIBUTE ;& and PUSH CX ;& save it too MOV DX,CURSOR_POSN ;& Get Cursor MOV DL,0 ;& First column CALL SET_CURSOR ;& Set the new Cursor CALL ERASE_2 ;& Erase the line ;& ;& The following lines (and the PUSH CX above) will clear only the PROMPT ;& and leave the cursor where it was when the prompt appears. This was ;& different than PCBoard 14.5 so I changed it to clear the entire line ;& via the above four lines. Left the old code in case someome wanted it. ;& ;& POP CX ;@ Get back Length ;&ERASE_LOOP: PUSH CX ;@ Save it again ;& MOV AL,BS ;@ Send BS ;& CALL WRITE_CHAR ;@ character ;& MOV AL,' ' ;@ and then space ;& CALL WRITE_CHAR ;@ character to erase ;& MOV AL,BS ;@ then BS again ;& CALL WRITE_CHAR ;@ to backup. ;& POP CX ;@ get back length again ;& LOOP ERASE_LOOP ;@ Erase entire prompt. POP CX ;& Restore MOV ATTRIBUTE,CL ;& the Attribute RET ;@ AUTOOFF: MOV AX,WAIT_DELAY ;@ Restore More to a Wait JMP SHORT AUTO2 ;@ AUTOMORE: MOV AX,PAUSE_DELAY ;@ Make More a Pause AUTO2: MOV MORE_DELAY,AX ;@ SKIP_IT: RET ;@ endif ;$ ;************* SUPPORT ROUTINES *************; CK_QUOTE: MOV BX,OFFSET QUOTE_STATE ;Assume quote state. MOV AH,DOUBLE_QUOTE CMP AL,AH ;Is it double quote? JZ GOT_QUOTE ;If yes, string delimiter. MOV AH,SINGLE_QUOTE ;Is it single quote? CMP AL,AH ;Is yes, string delimiter. JNZ QUOTE_END ;Else, return ZR = 0. GOT_QUOTE: MOV QUOTE_TYPE,AH ;Store as matching string end. QUOTE_END: RET ;------------------------------------------------; ACCUMULATE: PUSH AX ;Preserve number character. SUB AL,"0" ;Convert ASCII to binary. MOV CL,AL ;Save the number. MOV AX,10 ;Multiply previous count by 10. MOV BX,NUMBER_COUNT MUL BYTE PTR NUMBER_BUFFER[BX] ADD AL,CL ;Add in new number MOV BYTE PTR NUMBER_BUFFER[BX],AL ; and store. POP AX RET if PCB ;$ ;------------------------------------------------;@ ACCUM_COLOR: PUSH AX ;@ Preserve number character. SUB AL,"0" ;@ Convert ASCII to hex MOV AH,BYTE PTR NUMBER_BUFFER ;@ Get current number SHL AH,1 ;@ Shift left 4 bits SHL AH,1 ;@ SHL AH,1 ;@ SHL AH,1 ;@ OR AH,AL ;@ Add in new number MOV BYTE PTR NUMBER_BUFFER,AH ;@ and store. MOV CL,AH ;@ Return with Color in CL POP AX ;@ RET ;@ NUMBER_PARSE: MOV BX,[SI] ;@ Get low word MOV DX,AX ;@ Save Variable Number XOR AX,AX ;@ Assume Integer CMP CX,4 ;@ Is that a Long Integer? JNE SHORT_INT ;@ No, Integer MOV AX,[SI + 2] ;@ Yes, Get high word SHORT_INT: MOV DI, OFFSET PARSE_BUFFER ;@ Point DI at number space MOV CX,10 ;@ Set divisor to 10 XOR SI,SI ;@ Clear SI as counter MOV COMMA,2 ;@ Assume a Ratio CMP DX,2 ;@ Is it a Ratio? JB GETDIGIT ;@ No RESET_COMMA: MOV COMMA,4 ;@ Set Comma Counter GETDIGIT: DEC DI ;@ Point DI at correct character INC SI ;@ Register that we have a character DEC COMMA ;@ Do we need a Comma? JNZ NO_COMMA ;@ No MOV BYTE PTR [DI],',' ;@ Yes, So Put one in! JMP RESET_COMMA ;@ And restart the Comma count. NO_COMMA: XOR DX,DX ;@ Clear DX to take remainder DIV CX ;@ Divide AX first (High word) MOV BP,AX ;@ Save quotient MOV AX,BX ;@ Get low word DIV CX ;@ DX had leftover from first divide MOV BX,AX ;@ Save quotient MOV AX,BP ;@ Put high word back ADD DL,30h ;@ Make it an ASCII digit from remainder MOV [DI],DL ;@ Put it in our string OR AX,AX ;@ Is high word zero? JNZ GETDIGIT ;@ No keep going OR BX,BX ;@ Is low word zero? JNZ GETDIGIT ;@ No keep going MOV CX,SI ;@ Digit count to CX MOV SI,DI ;@ String Pointer to SI MOV DI, OFFSET PARSE_BUFFER ;@ Point DI at number space CMP BYTE PTR [DI][-2],',' ;@ Was it a Ratio JNE NOT_RATIO ;@ No, nevermind MOV BYTE PTR [DI][-2],'.' ;@ Change comma to Decimal MOV BYTE PTR [DI],":" ;@ Yes, Add ':1' to it MOV BYTE PTR [DI][1],"1" ;@ ADD CX,2 ;@ Account for two more characers NOT_RATIO: RET ;@ MAKE_TIMEDATE: MOV DI, OFFSET NUMBER_BUFFER ;@ Point to Number Buffer MOV SI,DI ;@ Save it in SI too JE MAKE_TIME ;@ It was TIME on Entry! MOV CX,8 ;@ It was DATE! Length = 8 PUSH CX ;@ Save Length MOV AH,04H ;@ Get date service INT 1AH ;@ Call BIOS - return codes as follows: PUSH CX ;@ CH = Century (19-20) CL = Year (00-99) PUSH DX ;@ DH = Month (1-12) DL = Day (00-31) MOV AL,DH ;@ Month CALL ASCII ;@ Convert byte to ASCII digits MOV AL,'-' ;@ STOSB ;@ POP AX ;@ Day - was DX when pushed CALL ASCII ;@ Convert byte to ASCII digits MOV AL,'-' ;@ STOSB ;@ POP AX ;@ Year - was CX when pushed POP CX ;@ Restore Length JMP SHORT ASCII ;@ Convert byte to ASCII digit MAKE_TIME: MOV CX,5 ;@ Length = 5 PUSH CX ;@ Save Length MOV AH,02H ;@ Get time service INT 1AH ;@ Call BIOS - return codes as follows: PUSH CX ;@ CH = Hours (0-23) CL = Minutes (0-59) MOV AL,CH ;@ Hours CALL ASCII ;@ Convert byte to ASCII digits MOV AL,':' ;@ STOSB ;@ POP AX ;@ Minutes - was CX when pushed POP CX ;@ Restore Length ASCII: MOV AH,AL ;@ Need to get BCD Digits SHR AL,1 ;@ MSD in AL SHR AL,1 ;@ lower SHR AL,1 ;@ 4 SHR AL,1 ;@ Bits AND AH,0FH ;@ And LSD in AH cleanly OR AX,3030H ;@ Convert to ASCII STOSW ;@ Stuff Digits into Buffer (in reverse) RET ;@ SI Points to String Data endif ;$ ;---------------------------------------------------------------------------; ; OUTPUT: CY = 1 if write SLOW mode or in graphics mode; CY = 0 otherwise. ; ;---------------------------------------------------------------------------; CK_SLOW_TEXT: TEST STATUS,SLOW JNZ SLOW_MODE CMP CRT_MODE,7 JZ TEXT_MODE CMP CRT_MODE,3 JA SLOW_MODE TEXT_MODE: CLC RET SLOW_MODE: STC RET ;-------------------------------------; ; OUTPUT: AL = Screen rows minus one ; ;-------------------------------------; INFORMATION: PUSH DS ;Save data segment. MOV AX,40H ;Point to BIOS data. MOV DS,AX MOV AL,DS:[84H] ;Retrieve rows - 1. OR AL,AL ;BIOS supported? JNZ INFO_END ;If yes, done here. MOV AL,24 ;Else, assume 25 lines. INFO_END: POP DS RET ;------------------------------------------------------------------------------; ; INPUT: DX = Cursor position. ; ; OUTPUT: ES = Video buffer segment; DI = Video buffer offset; AX,DX preserved ; ;------------------------------------------------------------------------------; VIDEO_SETUP: PUSH AX MOV AX,CRT_COLS ;Retrieve CRT columns. MUL DH ;Times cursor row. MOV BL,DL XOR BH,BH ADD AX,BX ;Plus cursor column. SHL AX,1 ;Times two for attribute. MOV DI,CRT_START ;Plus starting video offset. ADD DI,AX ;Equals destination. MOV BX,0B000H ;Assume mono card. CMP ADDR_6845,3B4H ;Is it mono port? JZ VIDEO_SEGMENT ;If yes, guessed right. ADD BX,800H ;Else, point to color segment. VIDEO_SEGMENT: MOV ES,BX POP AX RET ;------------------------------------------------; ; Move BIOS video data into our data segment. ; ;------------------------------------------------; GET_BIOS_DATA: PUSH DS PUSH DI ;Point to BIOS data segment. MOV BX,40H MOV DS,BX MOV SI,BIOS_ACTIVE_PAGE ;Start with active page. MOV DI,OFFSET ACTIVE_PAGE MOVSB ;Retrieve active page MOVSW ; and address of 6845 port. MOV SI,BIOS_CRT_MODE ;Retrieve CRT mode, CRT columns, MOV CX,CRT_DATA_LENGTH ; CRT length, CRT start. REP MOVSB MOV BL,ES:ACTIVE_PAGE ;Use active page as index XOR BH,BH ; of active cursor position. SHL BX,1 ADD SI,BX MOVSW POP DI POP DS RET ;----------------------------------------------------------------------------; ; OUTPUT: BL = First parameter; BH = Second parameter; DX = cursor position. ; ;----------------------------------------------------------------------------; ADJUST_NUMBER: MOV BX,WORD PTR NUMBER_BUFFER OR BH,BH JNZ ADJUST_AL ;If either parameter zero, INC BH ; increment to convert ADJUST_AL: OR BL,BL ; to base one. JNZ ADJUST_END INC BL ADJUST_END: MOV DX,CURSOR_POSN ;Return cursor position. RET ;--------------------------------------------------------------------------; ; INPUT: AX = number; BP = 0 if console output; BP = 1 if storage output. ; ;--------------------------------------------------------------------------; DECIMAL_OUT: MOV BX,10 ;Divisor of ten. XOR CX,CX ;Zero in counter. NEXT_COUNT: XOR DX,DX ;Zero in high half. DIV BX ;Divide by ten. ADD DL,"0" ;Convert to ASCII. PUSH DX ;Save results. INC CX ;Also increment count. CMP AX,0 ;Are we done? JNZ NEXT_COUNT ;Continue until zero. OR BP,BP ;If BP zero, output to screen. JNZ DEVICE_OUTPUT ;Else, store in buffer. REPORT_OUTPUT: POP AX ;Retrieve number. CALL PRINT_CHAR ;Display it. LOOP REPORT_OUTPUT RET DEVICE_OUTPUT: CMP CX,2 ;Two digits? JZ TWO_DIGITS ;If yes, process normally. MOV AL,"0" ;Else, store leading zero. STOSB TWO_DIGITS: POP AX ;Retrieve number. STOSB ;Store it LOOP TWO_DIGITS RET ;************* ANSI COMMAND SUBSET *************; CLS: MOV BH,7 ;Assume normal attribute. MOV BL,BYTE PTR CRT_MODE ;Get current video mode. CMP BL,4 JBE CK_CLS_STATUS ;If text mode then check if CMP BL,7 ; ANSI active. JZ CK_CLS_STATUS XOR BH,BH ;Else, use black attribute TEST STATUS,OFF JNZ CLS_SLOW ;If ANSI inactive, via BIOS. JMP SHORT CK_CLS_SLOW ;Else, check if FAST or SLOW. CK_CLS_STATUS: TEST STATUS,OFF ;Is ANSI OFF? JNZ CLS_SLOW ;If yes, CLS via BIOS. MOV BH,ATTRIBUTE ;Else, current attribute. CK_CLS_SLOW: CALL CK_SLOW_TEXT JC CLS_SLOW ;If ANSI SLOW, via BIOS. CLS_FAST: MOV AH,BH ;Else, attribute in high half. MOV AL,SPACE ;Space character in low half. XOR DX,DX ;Cursor position home. MOV DH,START_SCROLL ;$ protect lines CALL VIDEO_SETUP ;Calculate video address. MOV CX,CRT_LEN ;Retrieve CRT length. SHR CX,1 ;Times two for attribute. REP STOSW ;Write directly to video buffer. JMP SHORT SET_CURSOR ;Set the cursor top left corner. CLS_SLOW: CALL INFORMATION ;Get displayable rows. MOV DH,AL ;Store in DH. MOV DL,BYTE PTR CRT_COLS ;Retrieve CRT columns. DEC DL ;Adjust to zero base. XOR CX,CX ;Scroll active page. MOV AX,600H INT 10H XOR DX,DX ;Home the cursor. JMP SHORT SET_CURSOR ;------------------------------------------------; CURS_POSITION: ;These two commands are the same. HORZ_VERT_POS: CALL INFORMATION ;Returns AL = screen rows. CALL ADJUST_NUMBER ;Returns BL,BH = parameters. SUB BX,101H ;Zero based. CMP BL,AL ;If cursor request within ;$ JA CURSOR_END ; boundaries of screen, set it. JBE ROWOK ;$boundaries of screen, set it. MOV BL,AL ;$Set to last row ROWOK: CMP BH,BYTE PTR CRT_COLS ;$ ;$ JAE CURSOR_END JB COLOK ;$ MOV BH,BYTE PTR CRT_COLS ;$Set to DEC BH ;$ last column COLOK: XCHG BH,BL ; MOV DX,BX SET_CURSOR: MOV BH,ACTIVE_PAGE ;Set cursor via BIOS. MOV AH,2 INT 10H CURSOR_END: RET ;------------------------------------------------; CURSOR_UP: CALL ADJUST_NUMBER ;Move cursor up one or more rows OR DH,DH ; without changing column. JZ CURSOR_END ;If already at top, ignore. SUB DH,BL JNC SET_CURSOR ;Stay in bounds. XOR DH,DH JMP SHORT SET_CURSOR ;------------------------------------------------; CURSOR_DOWN: CALL INFORMATION ;Returns AL = screen rows. CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor CMP DH,AL ;Move cursor down requested JZ CURSOR_END ; rows without changing column. ADD DH,BL CMP DH,AL JNA SET_CURSOR MOV DH,AL ;Stay in bounds. JMP SHORT SET_CURSOR ;------------------------------------------------; CURS_FORWARD: CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor MOV BH,BYTE PTR CRT_COLS ;Retrieve displayable columns. DEC BH ;Adjust to zero base. CMP DL,BH ;Move cursor forward one or more JZ CURSOR_END ; columns without changing row. ADD DL,BL CMP DL,BH JNA SET_CURSOR MOV DL,BH ;Stay in bounds. JMP SHORT SET_CURSOR ;------------------------------------------------; CURS_BACKWARD: CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor OR DL,DL ;Move cursor backward one or JZ CURSOR_END ; more columns without changing SUB DL,BL ; row. Ignore if already left. JNC SET_CURSOR XOR DL,DL ;Stay in bounds. JMP SHORT SET_CURSOR ;--------------------------------------------------------; ; Report cursor position via console; Format: ESC[#;#R ; ;--------------------------------------------------------; DEVICE_STATUS: MOV DI,OFFSET DEVICE_STATUS_BUFFER MOV AL,ESC_CHAR STOSB ;Store leading Esc, bracket MOV AL,"[" ;in special Device Status Buffer. STOSB MOV AL,CURSOR_ROW ;Retrieve cursor row. XOR AH,AH ;Zero in high half. INC AX ;Convert to base one. MOV BP,1 ;Flag as storage. CALL DECIMAL_OUT ;Convert to ASCII. MOV AL,";" ;Store delimiting semi-colon. STOSB MOV AL,CURSOR_COL ;Do same with cursor column. XOR AH,AH INC AX CALL DECIMAL_OUT MOV AL,"R" ;Add command character STOSB MOV AL,CR ; and carriage return. STOSB SUB DI,OFFSET DEVICE_STATUS_BUFFER ;Setup for console out. MOV REASSIGN_COUNT,DI MOV REASSIGN_POS,OFFSET DEVICE_STATUS_BUFFER MOV REASSIGN_FLAG,ON MOV REMOVE_FLAG,OFF RET ;------------------------------------------------; SAVE_CURSOR: MOV DX,CURSOR_POSN MOV SAVE_POSITION,DX RET ;------------------------------------------------; RESTORE_CURS: MOV DX,SAVE_POSITION JMP SET_CURSOR ;------------------------------------------------; ERASE_IN_LINE: MOV DX,CURSOR_POSN ;Erase from the cursor to ERASE_2: MOV CX,CRT_COLS ;& the end of the line, including SUB CL,DL ; the current cursor position. CALL CK_SLOW_TEXT JC ERASE_SLOW ;If ANSI ON and not graphics, CALL VIDEO_SETUP ; write directly to video buffer MOV AL,SPACE ; with space/attribute. MOV AH,ATTRIBUTE REP STOSW RET ERASE_SLOW: MOV CX,DX ;Else, erase SLOW via MOV DL,BYTE PTR CRT_COLS ; BIOS scroll active page. DEC DL MOV BH,ATTRIBUTE MOV AX,601H INT 10H RET ;------------------------------------------------; ; Set Graphics Rendition ; ;------------------------------------------------; SGR: MOV SI,OFFSET NUMBER_BUFFER ;Point to number parameters. NEXT_SGR: LODSB ;Retrieve parameter. MOV DI,OFFSET ATTRIBUTE_TABLE ;Look up attribute in MOV CX,ATTRIBUTE_LENGTH ; translation table. REPNZ SCASB JNZ SGR_LOOP MOV DI,OFFSET ATTRIBUTE_END SHL CX,1 SUB DI,CX MOV AX,[DI] AND ATTRIBUTE,AL ;If match, AND with strip mask. OR ATTRIBUTE,AH ;OR with add mask. SGR_LOOP: DEC NUMBER_COUNT ;Do all parameters. JNZ NEXT_SGR RET ;------------------------------------------------; SET_MODE: MOV AH,ON ;Assume command 7, JMP SHORT CK_WRAP ; turn line wrap on. ;------------------------------------------------; RESET_MODE: MOV AH,OFF ;Assume turn line wrap off. CK_WRAP: MOV AL,BYTE PTR NUMBER_BUFFER ;Retrieve number parameter. CMP AL,7 ;Is it 7? JZ SET_WRAP ;If yes, set wrap mode. JB DO_MODE ;1 - 6 valid modes. CMP AL,14 ;14 - 16 valid modes. JB MODE_END CMP AL,19 JA MODE_END ;If above 16, illegal. DO_MODE: XOR AH,AH ;Else, set video mode INT 10H ; to parameter. RET SET_WRAP: MOV LINE_WRAP,AH MODE_END: RET ;--------------------------------------------------------------------; ; Keyboard Key Reassignment: First convert ASCII numbers to binary, ; ; parse out delimiting semi-colons, and quote string delimiters. ; ;--------------------------------------------------------------------; REASSIGNMENT: TEST STATUS,POFF ;:If assignment are OFF, JZ NOT_POFF ;: then exit as if JMP ASSIGN_FLUSH ;: we are out of room. NOT_POFF: ;: MOV CX,ESC_COUNT ;Retrieve length of Esc sequence. DEC CX ;Adjust. MOV SI,OFFSET ESC_BUFFER + 2 ;Point past Esc, bracket. MOV DI,OFFSET PARSE_BUFFER ;Point to parse storage. NEXT_STRING: MOV QUOTE_TYPE,0 ;Reset quote type. NEXT_NUM: XOR DL,DL ;Use DL to carry number; init = 0 XOR BP,BP ;Use BP as number flag. NEXT_PARAM: LODSB ;Retrieve a byte. DEC CX ;Decrement string length counter. JZ PARSE_END ;If zero, done. MOV AH,QUOTE_TYPE ;Else, retrieve quote type. OR AH,AH ;If zero, not in string. JNZ QUOTE_MODE ;Else, go to string mode. CALL CK_QUOTE ;Is character a quote? JZ NEXT_PARAM ;If yes, ignore. CMP AL,";" ;Else, is it semi-colon? JZ STORE_NUMBER ;If yes, number delimiter. SUB AL,"0" ;Else, must be number; convert MOV DH,AL ; to binary; save in DH. MOV AX,10 ;Multiply current total by ten. MUL DL ADD AL,DH ;Add new number. MOV DL,AL ;Save in DL. MOV BP,1 ;Flag that number mode active. JMP SHORT NEXT_PARAM ;Next parameter. STORE_NUMBER: OR BP,BP ;If number mode flag not set then JZ NEXT_PARAM ;semi-colon not prefaced with no. MOV AL,DL ;Else, store number. STOSB JMP SHORT NEXT_NUM ;Reset number carrying registers. QUOTE_MODE: CMP AL,AH ;Is current char a string ending JZ NEXT_STRING ; quote? If yes, exit quote mode STOSB ;Else, store char as literal. JMP SHORT NEXT_PARAM ;----------------------------------------------------------------------------; ; Remove duplicate assignments if removal leaves room for new assignment. ; ; If no duplicate and room, add new assignment, else flush buffer to screen. ; ;----------------------------------------------------------------------------; PARSE_END: OR BP,BP ;Was last parameter a number? JZ CK_LENGTH ;If no, skip. MOV AL,DL ;Else, store the last number. STOSB CK_LENGTH: SUB DI,OFFSET PARSE_BUFFER - 2 ;Calculate string length MOV AX,DI ; including word for length. MOV BX,WORD PTR PARSE_BUFFER ;BL=new first ASCII; BH=2nd. MOV CX,5 ;String length has to be at OR BL,BL ;least word + 2 for old + new = 5 JZ CK_LEGAL ; for extended key reassignment. DEC CX ;And word + 1 for old + new = 4 CK_LEGAL: CMP AX,CX ; for regular ASCII reassignment. JB ASSIGN_FLUSH ;If not, illegal; flush buffer. MOV BP,BUFFER_END ;BP to carry buffer end pointer. CALL CK_MATCH ;Is char already reassigned? JC CK_ROOM ;If no, check room for new. CK_REMOVE: MOV CX,DX ;REASSIGN_END - string end SUB CX,SI ; = bytes to move. JZ CK_ROOM ;If zero, last string. SUB DX,[DI] ;Is REASSIGN_END - old string ADD DX,AX ; + new string > CMP DX,BP ; BUFFER_END? JA ASSIGN_FLUSH ;If yes, flush buffer to screen. REP MOVSB ;Else, move them down in buffer. JMP SHORT STORE_NEW CK_ROOM: ADD DX,AX ;New string + current strings. CMP DX,BP ;Greater than buffer size? JA ASSIGN_FLUSH ;If yes, flush new string. STORE_NEW: MOV SI,OFFSET PARSE_BUFFER ;Else, room for new string. STOSW ;Store string length first. MOV CX,AX ;Adjust counter. DEC CX DEC CX REP MOVSB ;Store the actual string. MOV REASSIGN_END,DI ;Store new string end. RET ASSIGN_FLUSH: MOV AL,"p" ;If buffer full, flush string JMP FLUSH_BUFFER ; including ending "p" command. ;------------------------------------------------------------------------------; ; INPUT: BL = first ASCII code to match; BH = extended if BL = 0 ; ; OUTPUT: CY = 1 if no match found; CY = 0 if match found. ; ; DI points to string length of match; DI+2 points to start of string. ; ; SI = end of string; DX = REASSIGN_END; BP = BUFFER_END ; ;------------------------------------------------------------------------------; CK_MATCH: MOV DX,REASSIGN_END ;Current strings end. MOV SI,OFFSET REASSIGNMENT_BUFFER ;Point to buffer. NEXT_MATCH: MOV DI,SI ;Current record. CMP DI,DX ;End of strings? JAE NO_MATCH ;If yes, no match. MOV CX,[DI+2] ;CL=current first ASCII; CH=2nd ADD SI,[DI] ;Point to next record. CMP BL,CL ;First characters match? JNZ NEXT_MATCH ;If no, check next record. OR BL,BL ;Extended code? ie zero. JNZ MATCH ;If no, then match. CMP BH,CH ;Else, check second code. JNZ NEXT_MATCH ;If not same, no match. MATCH: CLC ;Else return with CY = 0 RET NO_MATCH: STC ;No match; CY = 1. RET ;----------------------------------------------------------------------; ; Buffer area will write over disposable data and initialization code. ; ;----------------------------------------------------------------------; ESC_BUFFER = $ NUMBER_BUFFER = ESC_BUFFER + ESC_BUFFER_SIZE PARSE_BUFFER = NUMBER_BUFFER DEVICE_STATUS_BUFFER = PARSE_BUFFER + ESC_BUFFER_SIZE DEVICE_STATUS_SIZE = 11 REASSIGNMENT_BUFFER = DEVICE_STATUS_BUFFER + DEVICE_STATUS_SIZE ; DISPOSABLE DATA ; --------------- SYNTAX LABEL BYTE DB "Syntax: ANSI [FAST | SLOW][ON | OFF][KON | KOFF][PON | POFF]",CR,LF ;: DB " [/B nnn][/C][/Q][/U][/T][/S][/P n]",CR,LF ;. ;@ ;: DB "FAST = direct screen writes; default",CR,LF ;: DB "SLOW = screen writes via BIOS",CR,LF ;: DB "ON/OFF = active/inactive; default is ON",CR,LF ;: DB "KON/KOFF = active/inactive reassignments; default is ON",CR,LF ;: DB "PON/POFF = active/inactive NEW reassignments; default is ON",CR,LF ;: DB "nnn = buffer size in bytes (0 - 60K) reserved for key reassignment; " DB "default 200",CR,LF DB "/Q = Quiet, no output when executed",CR,LF ;: DB "/U = Uninstall",CR,LF ;: DB "/T = Test if loaded",CR,LF ;$ if PCB ;$ DB "/S = Load stats from ANSICOM.SYS",CR,LF ;@ endif ;$ DB "/Pn = Protect n lines from scrolling at top" ;. CR_LF DB CR,LF,LF,"$" STATUS_MSG DB "Status: $" BUFFER_MSG DB CR,LF,"Buffer size: $" BYTES_FREE DB CR,LF,"Bytes free: $" ANSI_SYS_MSG DB "ANSI.SYS is installed so " NOT_INSTALLED DB "ANSI.COM not installed",CR,LF,"$" CON DB "CON" CON_OFFSET EQU 10 QUIET DB 0 ;| Quiet Mode SYNTAX_FLAG DB 0 ;; Syntax Display Flag SIZE_MSG DB "Uninstall to change buffer size",CR,LF,LF,"$" UNLOAD_MSG DB "ANSI can't be uninstalled",CR,LF DB "Uninstall resident programs in reverse order",CR,LF,"$" NOT_ENOUGH DB "Not enough memory",CR,LF,"$" ALLOCATE_MSG DB "Memory allocation error",CR,LF,BELL,"$" INSTALL_MSG DB "Installed",CR,LF,"$" UNINSTALL_MSG DB "Uninstalled",CR,LF,"$" if PCB ;$ NO_FILE_MSG DB "ANSICOM.SYS not found",CR,LF,"$" ;@ FILE_ERROR_MSG DB "Error reading ANSICOM.SYS",CR,LF,"$" ;@ FILE_NAME DB "ANSICOM.SYS",0 ;@ endif ;$ ;--------------------------------------------------------------------; ; Search memory for a copy of our code, to see if already installed. ; ;--------------------------------------------------------------------; INITIALIZE PROC NEAR CLD ;All string operations forward. MOV BX,OFFSET START ;Point to start of code. NOT BYTE PTR [BX] ;Change a byte so no false match. XOR DX,DX ;Start at segment zero. MOV AX,CS ;Store our segment in AX. NEXT_PARAG: INC DX ;Next paragraph. MOV ES,DX CMP DX,AX ;Is it our segment? JZ ANNOUNCE ;If yes, search is done. MOV SI,BX ;Else, point to our signature. MOV DI,BX ; and offset of possible match. MOV CX,16 ;Check 16 bytes for match. REP CMPSB JNZ NEXT_PARAG ;If no match, keep looking. ;------------------------------------------------; ANNOUNCE: ;| MOV SI,81H ;Point to command line. NEXT_CAP: LODSB ;Capitalize parameters. CMP AL,CR JZ PARSE CMP AL,"a" JB NEXT_CAP CMP AL,"z" JA NEXT_CAP AND BYTE PTR [SI - 1],5FH JMP SHORT NEXT_CAP ;------------------------------------------------; PARSE: MOV SI,81H ;Point to command line again. NEXT_PARA: XOR AX,AX ;Position in status parameters. MOV BX,4 ;Status parameters each 4 bytes. NEXT_STATUS: MOV DI,OFFSET PARAMETERS ;Point to "OFF ON SLOWFAST" ADD DI,AX ;Point to next parameter. ADD AX,BX CMP AX,LAST_PARAMETER ;:Check all 8 possible statuses JA CK_SWITCHES PUSH SI ;Save command line pointer. MOV CX,2 ;Check first two bytes for match. CMP AX,20 ;: 3 Bytes for JB Len_OK ;: KON | KOFF | PON | POFF INC CX ;: Len_OK: ;: REP CMPSB POP SI ;Recover command line pointer. JNZ NEXT_STATUS DIV BL ;If match, divide offset by four. ;: ;: Next 12 lines of routine changed ;: ;: MOV CL,1 ;Set a bit to match offset. ;:NEXT_SHIFT: SHL CL,1 ;: DEC AL ;: JNZ NEXT_SHIFT ;: SHR CL,1 ;Adjust. ;: MOV AH,STATUS_MASK ;Retrieve appropriate ON/OFF ;: CMP CL,ON ; or FAST/SLOW mask. ;: JBE ADD_STATUS ;: ROL AH,1 ;: ROL AH,1 ;: ;:ADD_STATUS: AND ES:STATUS,AH ;Mask off old status. ;: OR ES:STATUS,CL ;Add new status. ;: MOV CL,AL ;:place count in CL (1 - 8) MOV AX,STATUS_MASK ;:Retrieve appropriate ON/OFF ;:FAST|SLOW|KON|KOFF|PON|POFF DEC CL ;:Make 1 - 8 into 0 - 7 JZ Skip_1 ;:In position already if zero SHL AL,CL ;:Shift bit into position Skip_1: SHR CL,1 ;:divide count by 2 JZ Skip_2 ;: ROL AH,CL ;:Shift Mask into position ROL AH,CL ;: Skip_2: ;: AND ES:STATUS,AH ;Mask off old status. OR ES:STATUS,AL ;:Add new status. INC SI ;Bump command line pointer. INC SI CK_SWITCHES: LODSB ;Get a byte. CMP AL,CR ;Is it carriage return? JNE QUES ;$ JMP INSTALL ;If yes, done here. QUES: CMP AL,"?" ;;Request for Syntax? JNE CK_SW ;; No - MOV BYTE PTR SYNTAX_FLAG, 1 ;; Yes-Show Syntax CK_SW: CMP AL,"/" ;;Is there a switch character? JNE NEXT_PARA ;;If no, keep looking. GOT_SWITCH: LODSB ;;Else, get the switch character. CMP AL,"B" ;Is it "B" ? JNZ CK_C ;If no, check "C". CALL CK_INSTALLED ;Else, see if already installed. JZ GET_SIZE ;If no, get buffer request size. MOV DX,OFFSET SIZE_MSG ;Else, display error message. CALL PRINT_STRING NEXT_PARA2: JMP SHORT NEXT_PARA GET_SIZE: CALL DECIMAL_INPUT ;Get number parameter. CMP BX,REASSIGNMENT_MAX ;If greater than maximum, use JBE STORE_BUFFER ; maximum, else use requested MOV BX,REASSIGNMENT_MAX ; size. STORE_BUFFER: MOV REASSIGNMENT_SIZE,BX ADD BX,OFFSET REASSIGNMENT_BUFFER ;Calculate end of buffer. MOV BUFFER_END,BX JMP NEXT_PARA ;;Too far for direct jump CK_C: CMP AL,"C" ;Is it "C" ? if PCB ;$ JNZ CK_S ;@ If not, check "S". else ;$ JNZ CK_P ;$ If not, check "P". endif ;$ MOV ES:REASSIGN_END,OFFSET REASSIGNMENT_BUFFER ;Clear buffer JMP NEXT_PARA ;Else, next parmater. if PCB ;$ CK_S: CMP AL,"S" ;@ Is It "S" ? JNZ CK_P ;. If not, check "P". CALL LOAD_STATS ;@ Yes, Load Stats JMP NEXT_PARA2 ;@ Next parameter (indirect) endif ;$ CK_P: CMP AL,"P" ;. Is it "P" ? JNZ CK_Q ;|If not, check "Q". CMP BYTE PTR [SI], '*' ;$Use current row-1? JNE CK_PN ;$no PUSH SI ;$Get CALL GET_BIOS_DATA ;$ current MOV BL, ES:CURSOR_ROW ;$ row DEC BL ;$ -1 POP SI ;$ JMP CK_PS ;$ CK_PN: CALL DECIMAL_INPUT ;. Get number of lines CK_PS: MOV ES:START_SCROLL,BL ;. Store the value JMP NEXT_PARA2 ;. Next parameter (indirect) CK_Q: CMP AL,"Q" ;|See if Quiet Mode JNE CK_U ;|If not, check "U". NOT QUIET ;|If yes turn on Quiet JMP NEXT_PARA ;Else, next parmater. CK_U: CMP AL,"U" ;Is it "U" ? JE DO_U ;If yes, try to uninstall. CMP AL,"T" ;; Test if Installed JE DO_U MOV BYTE PTR SYNTAX_FLAG, 1 ;; Syntax Error JMP NEXT_PARA ;Else, next parmater. ;------------------------------------------------; INSTALL: MOV DX,OFFSET SIGNATURE ;|Display our signature. CALL PRINT_STRING ;| CMP BYTE PTR SYNTAX_FLAG,0 ;;Display Syntax? JE SKIP_SYNTAX ;;No MOV DX,OFFSET SYNTAX ;|Yes, Display syntax. CALL PRINT_STRING ;| SKIP_SYNTAX: CALL STATUS_REPORT ;;|Display status. CALL CK_INSTALLED ;Check if already installed. JZ CK_AVAILABLE ;If no, see if enough memory. XOR AL,AL ;;Else, done. EXIT2: JMP EXIT ;Exit with ERRORLEVEL = 0. DO_U: CALL CK_INSTALLED ;Else, see if installed. MOV DX,OFFSET NOT_INSTALLED ;If no, exit with error message. JZ LILLY_PAD ;Too far for short jump. CMP AL,"T" ;;Was it a test MOV AL, STATUS ;$return switches as JE EXIT2 ;;$Yes Too far for short jump. JMP UNINSTALL ;$Else, uninstall. ;--------------------------------; ; This is the install procedure. ; ;--------------------------------; CK_AVAILABLE: MOV BP,OFFSET REASSIGNMENT_BUFFER ;TSR ends at end ADD BP,REASSIGNMENT_SIZE ; of reassignment buffer. ADD BP,15 ;Round up. CMP BP,DS:[6] ;Buffer > PSP bytes in segment? MOV DX,OFFSET NOT_ENOUGH ;If yes, exit without installing JBE CK_ANSI ;@ JMP MSG_EXIT ;@with message CK_ANSI: MOV AX,3529H ;@Get undocumented INT 29 vector. INT 21H MOV SI,OFFSET CON ;Does it point to ANSI.SYS? MOV DI,CON_OFFSET ;Check by looking for "CON" MOV CX,3 ; as device name. REP CMPSB MOV DX,OFFSET ANSI_SYS_MSG ;Exit with error message if LILLY_PAD: JZ MSG_EXIT ;ANSI.SYS loaded. MOV OLD_INT_29[0],BX ;Else save old interrupt. MOV OLD_INT_29[2],ES MOV DX,OFFSET ANSI_INT_29 ;Install new interrupt. MOV AX,2529H INT 21H MOV AX,3516H ;Get INT 16 vector. INT 21H MOV OLD_INT_16[0],BX ;Save old interrupt. MOV OLD_INT_16[2],ES MOV DX,OFFSET ANSI_INT_16 ;Install new interrupt. MOV AX,2516H INT 21H MOV AX,3521H ;Get DOS 21h interrupt. INT 21H MOV OLD_INT_21[0],BX ;Save old interrupt. MOV OLD_INT_21[2],ES MOV DX,OFFSET ANSI_INT_21 ;Install new interrupt. MOV AX,2521H INT 21H if PCB ;$ MOV AX,3508H ;@ Get INT 08 vector. INT 21H ;@ MOV OLD_INT_08[0],BX ;@ Save old interrupt. MOV OLD_INT_08[2],ES ;@ MOV DX,OFFSET ANSI_INT_08 ;@ Install new interrupt. MOV AX,2508H ;@ INT 21H ;@ endif ;$ MOV AX,DS:[2CH] ;Get environment segment. MOV ES,AX MOV AH,49H ;Free up environment. INT 21H MOV DX,OFFSET INSTALL_MSG ;Display install message. CALL PRINT_STRING CALL FLUSH_END ;Setup the number buffer. MOV DX,BP ;Retrieve resident byte request. MOV CL,4 SHR DX,CL ;Convert to paragraphs. MOV AX,3100H ;Return error code of zero. INT 21H ;Terminate but stay resident. ;-------------------------------------------------------------------; ; Exit. Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ; ;-------------------------------------------------------------------; MSG_EXIT: CALL PRINT_STRING ERROR_EXIT: MOV AL,1 ;ERRORLEVEL = 1. EXIT: MOV AH,4CH ;Terminate. INT 21H ;---------------------------------------------------; ; This subroutine uninstalls the resident ANSI.COM. ; ;---------------------------------------------------; UNINSTALL: AND ES:STATUS,NOT ON ;Turn OFF ANSI, just in case OR ES:STATUS,OFF ; can't uninstall. MOV CX,ES ;Save segment in CX. MOV AX,3529H ;Get interrupt 29h. INT 21H CMP BX,OFFSET ANSI_INT_29 ;Has it been hooked by another? ife PCB ;$ JNZ UNINSTALL_ERR ;$If no, exit with error message. else ;$ JNZ UNINSTALL_ERR2 ;@If no, exit with error message. endif ;$ MOV BX,ES CMP BX,CX ;Is the segment vector same? ife PCB ;$ JNZ UNINSTALL_ERR ;$If no, exit with error message. else ;$ JNZ UNINSTALL_ERR2 ;@If no, exit with error message. MOV AX,3508H ;@Get interrupt 08h. INT 21H ;@ CMP BX,OFFSET ANSI_INT_08 ;@Has it been hooked by another? JNZ UNINSTALL_ERR ;@If yes, exit with error message. MOV BX,ES ;@ CMP BX,CX ;@Is the segment vector same? UNINSTALL_ERR2:JNZ UNINSTALL_ERR ;@If no, exit with error message. endif ;$ MOV AX,3516H ;Get interrupt 16h. INT 21H CMP BX,OFFSET ANSI_INT_16 ;Has it been hooked by another? JNZ UNINSTALL_ERR ;If yes, exit with error message. MOV BX,ES CMP BX,CX ;Is the segment vector same? JNZ UNINSTALL_ERR ;If no, exit with error message. MOV AX,3521H ;Get interrupt 21h. INT 21H CMP BX,OFFSET ANSI_INT_21 ;Has it been hooked by another? JNZ UNINSTALL_ERR ;If yes, exit with error message. MOV BX,ES CMP BX,CX ;Is the segment vector same? JNZ UNINSTALL_ERR ;If no, exit with error message. MOV AH,49H ;Return memory to system pool. INT 21H MOV DX,OFFSET ALLOCATE_MSG JC MSG_EXIT ;Display message if problem. MOV DX,ES:OLD_INT_29[0] ;Restore old INT 29. MOV DS,ES:OLD_INT_29[2] MOV AX,2529H INT 21H if PCB ;$ MOV DX,ES:OLD_INT_08[0] ;@Restore old INT 08. MOV DS,ES:OLD_INT_08[2] ;@ MOV AX,2508H ;@ INT 21H ;@ endif ;$ MOV DX,ES:OLD_INT_16[0] ;Restore old INT 16. MOV DS,ES:OLD_INT_16[2] MOV AX,2516H INT 21H MOV DX,ES:OLD_INT_21[0] ;Restore old INT 21. MOV DS,ES:OLD_INT_21[2] MOV AX,2521H INT 21H PUSH CS POP DS ;Point to our data. MOV DX,OFFSET UNINSTALL_MSG ;Display uninstall message. CALL PRINT_STRING OR AL,AL ;Exit with ERRORLEVEL = 0. JMP EXIT UNINSTALL_ERR: MOV ES,CX ;If error, display OFF status. CALL STATUS_REPORT MOV DX,OFFSET UNLOAD_MSG ;And exit with error message. JMP MSG_EXIT INITIALIZE ENDP ;--------------------------------------------------; ; INPUT: SI points to parameter start. ; ; OUTPUT: SI points to parameter end; BX = number. ; ;--------------------------------------------------; DECIMAL_INPUT PROC NEAR XOR BX,BX ;Start with zero as number. NEXT_DECIMAL: LODSB ;Get a character. CMP AL,CR ;Carriage return? JZ ADJUST_DEC ;If yes, done here. CMP AL,"/" ;Forward slash? JZ ADJUST_DEC ;If yes, done here. SUB AL,"0" ;ASCII to binary. JC NEXT_DECIMAL ;If not between 0 and 9, skip. CMP AL,9 JA NEXT_DECIMAL CBW ;Convert byte to word. XCHG AX,BX ;Swap old and new number. MOV CX,10 ;Shift to left by multiplying MUL CX ; last entry by ten. JC DECIMAL_ERROR ;If carry, too big. ADD BX,AX ;Add new number and store in BX. JNC NEXT_DECIMAL ;If not carry, next number. DECIMAL_ERROR: MOV BX,-1 ;Else, too big; return -1. ADJUST_DEC: DEC SI ;Adjust pointer. RET DECIMAL_INPUT ENDP ;-------------------------------------------------------; ; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ; ;-------------------------------------------------------; CK_INSTALLED: PUSH AX ;; MOV AX,ES MOV BX,CS CMP AX,BX ;Compare segments. POP AX ;; RET ;------------------------------------------------; STATUS_REPORT: MOV DX,OFFSET STATUS_MSG CALL PRINT_STRING ;Display "Status: ". MOV BL,ES:STATUS MOV BH,1 NEXT_REPORT: TEST BL,BH JZ LOOP_STATUS MOV DL,BH ;: ;: Next 9 Lines changed ;: ;: MOV AX,-1 ;:NEXT_BIT: INC AX ;: SHR DL,1 ;: JNC NEXT_BIT ;: SHL AX,1 ;: SHL AX,1 ;: MOV SI,OFFSET PARAMETERS ;Display appropriate ON or OFF, ;: ADD SI,AX ; SLOW or FAST. ;: MOV CX,4 ;: MOV SI,OFFSET PARAMETERS ;:Display appropriate ON or OFF, ;: SLOW|FAST. KON|KOFF|PON|POFF MOV CX,4 ;:Each four characters long NEXT_BIT: SHR DL,1 ;:Is this our Word? JC REPORT ;:Yes - Print it ADD SI,CX ;:No - Skip over it JMP NEXT_BIT ;:test the next bit REPORT: LODSB CALL PRINT_CHAR LOOP REPORT MOV AL, " " ;: Space words CALL PRINT_CHAR ;: LOOP_STATUS: SHL BH,1 JNC NEXT_REPORT ;: Check all 8 bits ;: MOV DX,OFFSET BUFFER_MSG ;Display "Buffer size: ". CALL PRINT_STRING MOV AX,ES:REASSIGNMENT_SIZE PUSH AX XOR BP,BP CALL DECIMAL_OUT ;Display the size. MOV DX,OFFSET BYTES_FREE ;Display "Bytes free: ". CALL PRINT_STRING POP AX ADD AX,OFFSET REASSIGNMENT_BUFFER SUB AX,ES:REASSIGN_END CALL DECIMAL_OUT ;Display the bytes free. MOV DX,OFFSET CR_LF CALL PRINT_STRING RET ;------------------------------------------------; PRINT_CHAR: MOV DL,AL MOV AH,2 ;Print character via DOS. OR AL,AL ;:Skip NULLs JNZ SHORT DOS_INT ;: RET ;: PRINT_STRING: MOV AH,9 ;Print string via DOS. DOS_INT: CMP QUIET,0 ;|Do we suppress output JNE NO_PRINT ;|Yes INT 21H ;|No NO_PRINT: ;| RET if PCB ;$ LOAD_STATS: MOV DX,OFFSET FILE_NAME ;@ Stat filename MOV AX,3D00H ;@ Open File INT 21H ;@ DOS Interupt MOV DX,OFFSET NO_FILE_MSG ;@ Assume no file JNC OPEN_OK ;@ Ok, So far MSG_EXIT2: JMP MSG_EXIT ;@ No file found probably OPEN_OK: MOV BX,AX ;@ This is our Handle number PUSH DS ;@ Save DS PUSH ES ;@ Set ES to DS POP DS ;@ MOV DX, OFFSET SUB_LIST ;@ Point to the Sub_List MOV CX, SUB_LENGTH ;@ Get the Length ot the List MOV AH,3FH ;@ Read from Handle INT 21H ;@ DOS Interupt POP DS ;@ Restore DS PUSHF ;@ Save results PUSH AX ;@ MOV AH,3EH ;@ Close Handle INT 21H ;@ DOS Interupt POP AX ;@ Restore results POPF ;@ MOV DX,OFFSET FILE_ERROR_MSG ;@ Assume an error occured JC MSG_EXIT2 ;@ Yep, it did XCHG AX,CX ;@ Swap Bytes Read JCXZ MSG_EXIT2 ;@ Something went wrong CMP AX,CX ;@ Did we read the full amount JNE MSG_EXIT2 ;@ No RET ;@ All went well! endif ;$ _TEXT ENDS END START