PAGE 59, 132 TITLE MSYIBM -- Module to perform VT100 emulation on IBM PC ; Update 7 Jan 86 IF1 %OUT >> Starting pass 1 ELSE %OUT >> Starting pass 2 ENDIF PUBLIC Term_PC, Gss, Close_Screen_PC, Get_Screen_Segment, Check_Table PUBLIC Set_up_script_processor_PC, Screen_Image_ptr, Mode_Line_ptr PUBLIC Old_Image, Set_up_dynamic_memory PUBLIC Save_command_mode_screen, Restore_command_mode_screen .SALL ;*************************************************************************** ; *Definitions* ... ; Global defs INCLUDE MsDefs.H ; Local defs Screen EQU 10h ; BIOS screen call Kb EQU 16h ; BIOS keyboard call PrScan EQU 72h ; print-screen scan code... Caps_State EQU 40h ; CAPS LOCK on Num_State EQU 20h ; NUM LOCK on alt_shift EQU 8H ; alt shift key down ctl_shift EQU 4H ; ctl key down left_shift EQU 2H ; left shift key down right_shift EQU 1H ; right shift key down timer EQU 40h ; timer port bel_prt EQU 61h ; speaker control CRT_status_port EQU 3DAh ; Crt status port Vertical_retrace EQU 8 ; Vertical retrace in progress ;*************************************************************************** ; *Data* ... DataS SEGMENT public 'datas' EXTRN PC_Subtype:BYTE, Force_mono:BYTE, m7171:BYTE, AutoWrap:BYTE EXTRN Preload_flag:BYTE, Preload_buffer:BYTE, PC_Type:BYTE EXTRN Video_page_addresses:WORD, Which_page:BYTE, CRT_Mode:BYTE EXTRN Count:WORD, Allow_blast:BYTE ; Fixed data, unchanging ... ; Character decoding dispatch tables ... ; Dispatch for single characters ... ChrTb1 DB Bell, Bs, Tab DB Lf, Ff, Cr DB Vt, ShO, ShI NChrTb1 EQU $-ChrTb1 ActTb1 DW Do_Bell, Do_Bs, Do_Tab DW IND, IND, Do_Cr DW IND, Do_SO, Do_SI DW Done_with_char ; Dispatch for things following an escape ... ChrTb2 DB '[', '(', ')' DB '#', 'D', 'E' DB 'M', 'Z', '7' DB '8', '=', '>' DB Esc NChrTb2 EQU $-ChrTb2 ActTb2 DW Do_left_square, Do_left_paren, Do_right_paren DW Do_pound_sign, IND, NEL DW RI, DECID, DECSC DW DECRC, DECKPAM, DECKPNM DW Restart_esc_seq_processing DW Done_with_char ; Dispatch for things following escape [ ... ChrTb3 DB 'A', 'B', 'C' DB 'D', 'H', 'J' DB 'K', 'c', 'f' DB 'h', 'i', 'l' DB 'm', 'n', 'q' DB 'r', Esc NChrTb3 EQU $-ChrTb3 ActTb3 DW CUU, CUD, CUF DW CUB, CUP, ED DW EL, DA, HVP DW SM, MC, RM DW SGR, DSR, DECLL DW DECSTBM, Restart_esc_seq_processing DW Done_with_char ; Dispatch for things following escape ( ... ChrTb4 DB 'B', '0', Esc NChrTb4 EQU $-ChrTb4 ActTb4 DW Lfp_B, Lfp_0, Restart_esc_seq_processing DW Done_with_char ; Dispatch for things following escape ) ... ChrTb5 DB 'B', '0', Esc NChrTb5 EQU $-ChrTb5 ActTb5 DW Rtp_B, Rtp_0, Restart_esc_seq_processing DW Done_with_char EVEN ; Try for word boundary ; Lookup tables to convert renditions into attribute bytes Color_attributes LABEL BYTE DB 007h ; * Normal * DB 00Fh ; Bold DB 001h ; Underscored DB 009h ; Bold Underscored DB 087h ; Blinking DB 08Fh ; Bold Blinking DB 081h ; Underscored Blinking DB 089h ; Bold Underscored Blinking DB 070h ; Inverted DB 078h ; Bold Inverted DB 071h ; Underscored Inverted DB 079h ; Bold Underscored Inverted DB 0F0h ; Blinking Inverted DB 0F8h ; Bold Blinking Inverted DB 0F1h ; Underscored Blinking Inverted DB 0F9h ; Bold Underscored Blinking Inverted ; Mode change table, used for switching video modes Mode_change_table LABEL BYTE DB 2 ; 0 => 2 DB 3 ; 1 => 3 DB 2 ; 2 => 2 DB 3 ; 3 => 3 DB 3 ; 4 => 3 DB 2 ; 5 => 2 DB 2 ; 6 => 2 DB 7 ; 7 => 7 DB 8 ; 8 => 8 DB 9 ; 9 => 9 DB 0Ah ; A => A DB 0Bh ; B => B DB 0Ch ; C => C DB 3 ; D => 3 DB 3 ; E => 3 DB 2 ; F => 2 DB 3 ; 10 => 3 ; Dispatch table for Load LEDs (DECLL) routine DECLL_Table LABEL WORD DW DECLL_0 ; 0 -- Clear all LEDs DW DECLL_1 ; 1 -- Set LED #1 DW DECLL_2 ; 2 -- Set LED #2 DW DECLL_3 ; 3 -- Set LED #3 DW DECLL_4 ; 4 -- Set LED #4 ; Dispatch table for Erase in Display (ED) routine ED_table LABEL WORD DW ED_0 ; 0 -- Clear to end of screen DW ED_1 ; 1 -- Clear to beginning of screen DW ED_2 ; 2 -- Clear entire screen ; Dispatch table for Erase in Line (EL) routine EL_table LABEL WORD DW EL_0 ; 0 -- Clear to end of line DW EL_1 ; 1 -- Clear to beginning of line DW EL_2 ; 2 -- Clear entire line ; Dispatch table for Set Graphic Rendition (SGR) routine SGR_Table LABEL WORD DW SGR_0 ; 0 -- Clear all renditions DW SGR_1 ; 1 -- Bold DW SGR_Ignore ; 2 DW SGR_Ignore ; 3 DW SGR_4 ; 4 -- Underscored DW SGR_5 ; 5 -- Blinking DW SGR_Ignore ; 6 DW SGR_7 ; 7 -- Inverted ; Dispatch table for Media Copy (MC) routine MC_Table LABEL WORD DW MC_0 ; 0 -- Print screen DW MC_1 ; 1 -- Print cursor line DW MC_Ignore ; 2 DW MC_Ignore ; 3 DW MC_4 ; 4 -- Printer controller or Auto Print off DW MC_5 ; 5 -- Printer controller or Auto Print on ; Strings to be sent for simulated VT100 keys PF1_str DB Esc,'OP',0 PF2_str DB Esc,'OQ',0 PF3_str DB Esc,'OR',0 PF4_str DB Esc,'OS',0 ; If cursor keys mode is not set ... Normal_PF7_str DB Esc,'[A',0 Normal_PF8_str DB Esc,'[B',0 Normal_PF9_str DB Esc,'[D',0 Normal_PF10_str DB Esc,'[C',0 ; If cursor keys mode is set ... Alternate_PF7_str DB Esc,'OA',0 Alternate_PF8_str DB Esc,'OB',0 Alternate_PF9_str DB Esc,'OD',0 Alternate_PF10_str DB Esc,'OC',0 ; As above, but in 7171 mode ... K71_PF1_str DB Esc,'1',0 K71_PF2_str DB Esc,'2',0 K71_PF3_str DB Esc,'3',0 K71_PF4_str DB Esc,'4',0 K71_PF5_str DB Esc,'5',0 K71_PF6_str DB Esc,'6',0 K71_PF7_str DB Esc,'7',0 K71_PF8_str DB Esc,'8',0 K71_PF9_str DB Esc,'9',0 K71_PF10_str DB Esc,'0',0 K71_PF11_str DB Esc,'-',0 K71_PF12_str DB Esc,'=',0 K71_Erase_EOL_str DB Esc,127,0 K71_Home_str DB 8,0 K71_Tab_str DB Esc,Esc,'OC',0 K71_Shift_Tab_str DB Esc,Esc,'OD',0 K71_End_str DB 'J'-100q,0 K71_Del_str DB 127,0 K71_Ins_str DB Esc,'On',0 Null_string DB 0 ; String with no characters ; If keypad application mode is not set ... Num_Zero_str DB '0',0 Num_One_str DB '1',0 Num_Two_str DB '2',0 Num_Three_str DB '3',0 Num_Four_str DB '4',0 Num_Five_str DB '5',0 Num_Six_str DB '6',0 Num_Seven_str DB '7',0 Num_Eight_str DB '8',0 Num_Nine_str DB '9',0 Num_Period_str DB '.',0 Num_Comma_str DB ',',0 Num_Minus_str DB '-',0 Num_Enter_str DB Cr,0 ; If keypad application mode is set ... Appl_Zero_str DB Esc,'Op',0 Appl_One_str DB Esc,'Oq',0 Appl_Two_str DB Esc,'Or',0 Appl_Three_str DB Esc,'Os',0 Appl_Four_str DB Esc,'Ot',0 Appl_Five_str DB Esc,'Ou',0 Appl_Six_str DB Esc,'Ov',0 Appl_Seven_str DB Esc,'Ow',0 Appl_Eight_str DB Esc,'Ox',0 Appl_Nine_str DB Esc,'Oy',0 Appl_Period_str DB Esc,'On',0 Appl_Comma_str DB Esc,'Ol',0 Appl_Minus_str DB Esc,'Om',0 Appl_Enter_str DB Esc,'OM',0 ; Scan codes for keys we have predefined ... Zero_key EQU 82 ; These are all the "keypad" keys One_key EQU 79 Two_key EQU 80 Three_key EQU 81 Four_key EQU 75 Five_key EQU 76 Six_key EQU 77 Seven_key EQU 71 Eight_key EQU 72 Nine_key EQU 73 Period_key EQU 83 Comma_key EQU 55 ; AKA "PrtSc *" Minus_key EQU 74 Enter_key EQU 78 ; AKA "grey +" PF1_key EQU 59 ; Use PC's PF keys 1-4 as VT100 PF1-4 PF2_key EQU 60 PF3_key EQU 61 PF4_key EQU 62 PF5_key EQU 63 ; Clone VTERM, takes you to command level PF6_key EQU 64 ; As above, takes you to help PF7_key EQU 65 ; Up-arrow PF8_key EQU 66 ; Down PF9_key EQU 67 ; Left PF10_key EQU 68 ; Right ... CtrlPrtSc EQU 114 ; CTRL + */PrtSc keys (set up by BIOS) Scroll_lock_key EQU 70 ; Scroll Lock, aka BREAK Tab_key EQU 15 ; Tab key EVEN ; Try for word boundary ; Dispatch table for keyboard activity Key_codes LABEL WORD DW Zero_key, One_key, Two_key DW Three_key, Four_key, Five_key DW Six_key, Seven_key, Eight_key DW Nine_key, Period_key, Comma_key DW Minus_key, Enter_key, PF1_key DW PF2_key, PF3_key, PF4_key DW PF5_key, PF6_key, PF7_key DW PF8_key, PF9_key, PF10_key Number_of_key_codes EQU ( $ - Key_codes ) / 2 Key_actions LABEL WORD Do_Zero_key DW If_keypad_application_mode_set DW Num_Zero_str DW Appl_Zero_str Do_One_key DW If_keypad_application_mode_set DW Num_One_str DW Appl_One_str Do_Two_key DW If_keypad_application_mode_set DW Num_Two_str DW Appl_Two_str Do_Three_key DW If_keypad_application_mode_set DW Num_Three_str DW Appl_Three_str Do_Four_key DW If_keypad_application_mode_set DW Num_Four_str DW Appl_Four_str Do_Five_key DW If_keypad_application_mode_set DW Num_Five_str DW Appl_Five_str Do_Six_key DW If_keypad_application_mode_set DW Num_Six_str DW Appl_Six_str Do_Seven_key DW If_keypad_application_mode_set DW Num_Seven_str DW Appl_Seven_str Do_Eight_key DW If_keypad_application_mode_set DW Num_Eight_str DW Appl_Eight_str Do_Nine_key DW If_keypad_application_mode_set DW Num_Nine_str DW Appl_Nine_str Do_Period_key DW If_keypad_application_mode_set DW Num_Period_str DW Appl_Period_str Do_Comma_key DW If_keypad_application_mode_set DW Num_Comma_str DW Appl_Comma_str Do_Minus_key DW If_keypad_application_mode_set DW Num_Minus_str DW Appl_Minus_str Do_Enter_key DW If_keypad_application_mode_set DW Num_Enter_str DW Appl_Enter_str Do_PF1_key DW Leave_alone DW PF1_str DW PF1_str Do_PF2_key DW Leave_alone DW PF2_str DW PF2_str Do_PF3_key DW Leave_alone DW PF3_str DW PF3_str Do_PF4_key DW Leave_alone DW PF4_str DW PF4_str Do_PF5_key DW Leave_emulator nop nop nop nop Do_PF6_key DW Call_Helper nop nop nop nop Do_PF7_key DW If_cursor_keys_mode_set DW Normal_PF7_str DW Alternate_PF7_str Do_PF8_key DW If_cursor_keys_mode_set DW Normal_PF8_str DW Alternate_PF8_str Do_PF9_key DW If_cursor_keys_mode_set DW Normal_PF9_str DW Alternate_PF9_str Do_PF10_key DW If_cursor_keys_mode_set DW Normal_PF10_str DW Alternate_PF10_str ; Alternate dispatch table for keyboard activity Key_codes_2 LABEL WORD DW Two_key, Four_key, Six_key DW Eight_key Number_of_key_codes_2 EQU ( $ - Key_codes_2 ) / 2 Key_actions_2 LABEL WORD Do_Two_key_2 DW If_cursor_keys_mode_set DW Normal_PF8_str DW Alternate_PF8_str Do_Four_key_2 DW If_cursor_keys_mode_set DW Normal_PF9_str DW Alternate_PF9_str Do_Six_key_2 DW If_cursor_keys_mode_set DW Normal_PF10_str DW Alternate_PF10_str Do_Eight_key_2 DW If_cursor_keys_mode_set DW Normal_PF7_str DW Alternate_PF7_str ; Other alternate dispatch table for IBM 7171 keyboard activity Key_codes_7171 LABEL WORD DW Zero_key, One_key, Two_key DW Three_key, Four_key, Five_key DW Six_key, Seven_key, Eight_key DW Nine_key, Period_key, Comma_key DW Minus_key, Enter_key, PF1_key DW PF2_key, PF3_key, PF4_key DW PF5_key, PF6_key, PF7_key DW PF8_key, PF9_key, PF10_key X EQU (alt_shift * 256) ; X is the alt_shift flag shifted into the left half ; 104 and 105 are scan codes for ALT F1 and ALT F2 DW X+104, X+105 ; 120, 121 etc. are scan codes for top row digits 1 .. 0 followed by - and = DW X+120, X+121, X+122 DW X+123, X+124, X+125 DW X+126, X+127, X+128 DW X+129, X+130, X+131 DW Tab_key DW (left_shift * 256) + Tab_key DW 14 ; Backarrow Number_of_key_codes_7171 EQU ( $ - Key_codes_7171 ) / 2 Key_actions_7171 LABEL WORD DW K71_Ins_str ; 0 (Ins) on numeric pad DW K71_End_str ; 1 DW Alternate_PF8_str ; 2 (down) DW Null_string ; 3 DW Alternate_PF9_str ; 4 (left) DW Null_string ; 5 DW Alternate_PF10_str ; 6 (right) DW K71_Home_str ; 7 DW Alternate_PF7_str ; 8 (up) DW Null_string ; 9 DW K71_Del_str ; period (Del) DW Null_string ; Comma (PrtSc) DW K71_Erase_EOL_str ; Grey - DW Appl_Enter_str ; Grey + DW K71_PF1_str ; F1 DW K71_PF2_str DW K71_PF3_str DW K71_PF4_str DW K71_PF5_str DW K71_PF6_str DW K71_PF7_str DW K71_PF8_str DW K71_PF9_str DW K71_PF10_str ; F10 DW K71_PF11_str ; F11 DW K71_PF12_str ; F12 DW K71_PF1_str ; F1 again (on top row this time) DW K71_PF2_str DW K71_PF3_str DW K71_PF4_str DW K71_PF5_str DW K71_PF6_str DW K71_PF7_str DW K71_PF8_str DW K71_PF9_str DW K71_PF10_str ; F10 DW K71_PF11_str ; F11 DW K71_PF12_str ; F12 (end of top row) DW K71_Tab_str DW K71_Shift_Tab_str DW Alternate_PF9_str ; Backarrow, usually DELete, now = left arrow ; Table of max per-loop character counts based on baud rate Baud_Char LABEL WORD DW 1 ; 45.5 baud 1 DW 1 ; 50 2 DW 1 ; 75 3 DW 1 ; 110 4 DW 2 ; 135 5 DW 3 ; 150 6 DW 5 ; 300 7 DW 10 ; 600 8 DW 20 ; 1200 9 DW 30 ; 1800 10 DW 33 ; 2000 11 DW 41 ; 2400 12 DW 82 ; 4800 13 DW 164 ; 9600 baud, enough for two full lines 14 DW 164 ; 19200 baud 15 DW 164 ; 23040 baud 16 DW 164 ; 38400 baud 17 Baud_table_size EQU 17 ; Number of baud rates known ; Maximum number of scrolls to do in a display cycle PUBLIC Maximum_scrolls Maximum_scrolls DW 1 ; Strings sent in response to incoming escape sequences DA_str DB Esc,'[?1;2c',0 ; Standard answer (for VT100 with AVO) to ; Device Attributes (DA) ;DA_str DB Esc,'[?6c',0 ; Standard answer (for VT102) to ; Device Attributes (DA) DSR_str DB Esc,'[0n',0 ; The No Malfunctions answer to Device ; Status Request (DSR) CPR_str DB Esc,'[',0 ; First two chars in CPR esc seq Ident_str DB Esc,'[?99c',0 ; Code to say that we are not just a VT100 No_memory DB Cr, Lf, '? Not enough memory for Terminal Emulation', Cr, Lf, '$' Lc DB ' lc ' ; For lower case CAPS DB 'CAPS' ; Caps lock Arrow DB 24,25,27,26 ; Up, down, left, right Num DB 'NumL' ; Num lock ; Table to translate graphic mode VT100 char into "best" IBM PC equivalent Graphic_translation_table LABEL BYTE DB 032 ; 95 DB 004 ; 96 DB 176 ; 97 DB 029 ; 98 DB 023 ; 99 DB 017 ; 100 DB 025 ; 101 DB 248 ; 102 DB 241 ; 103 DB 174 ; 104 DB 018 ; 105 DB 217 ; 106 DB 191 ; 107 DB 218 ; 108 DB 192 ; 109 DB 197 ; 110 DB 196 ; 111 DB 196 ; 112 DB 196 ; 113 DB 196 ; 114 DB 196 ; 115 DB 195 ; 116 DB 180 ; 117 DB 193 ; 118 DB 194 ; 119 DB 179 ; 120 DB 243 ; 121 DB 242 ; 122 DB 227 ; 123 DB 247 ; 124 DB 156 ; 125 DB 250 ; 126 ; Mode line templates -- attribute byte followed by string followed by ; NUL character (ASCII 0) -- two NULs in a row mark end-of-template Mode_template LABEL BYTE ; Mode line ; DB 70h,Space,Space,0 ; DB 71h,'F5',0,70h,'=Command mode',Space,Space,Space,0 ; DB 71h,'F6',0,70h,'=Help Esc chr=',0,71h DB 70h, ' ' VerDef DB ' ',0 DB 71h,'ALT-F6',0 DB 70h,'=Help Esc chr=',0,71h M_Esc DB '^[',0,70h,Space,Space,Space,Space,0 M_Prt DB 70h,' ',0 M_Prt_length=$-M_Prt ; Length of this item DB 70h,Space,Space,Space,Space,0,71h M_Caps DB ' lc ',0,70h,Space,Space,Space,0,71h M_KPad DB 24,25,27,26,0 DB 70h,Space,Space,Space,Space,0 M_L1 DB 70h,'L1',0,70h,Space,0 M_L2 DB 70h,'L2',0,70h,Space,0 M_L3 DB 70h,'L3',0,70h,Space,0 M_L4 DB 70h,'L4',0,70h,Space,Space,0 DB 0 M_Prt_On DB 71h,'Prt',0 M_Prt_Off DB 70h,' ',0 ; Template for "mode line" after user has hit the escape char Esc_chr_template LABEL BYTE DB 70h,Space,0 DB 71h,'?',0,70h,'=Help',Space,Space,Space,Space,0 DB 71h,'C',0,70h,'=Close',Space,Space,Space,Space,0 DB 71h,'S',0,70h,'=Status',Space,Space,Space,Space,0 DB 71h,'B',0,70h,'=send Break',Space,Space,Space,0 DB 71h,'M',0,70h,'=Mode line',Space,Space,Space,Space,0 DB 71h,'?',0,70h,' for full list',Space,0,0 offmsg DB 'Off' onmsg DB 'On ' EVEN ; Try for word boundary beldiv DW 2DCh ; 550 hz? ; Vt100 data, changeable ... ; Screen addressing data PUBLIC Max_number_of_chars, Number_of_chars_done, Image_ptr Max_number_of_chars DW 10 ; Max number of chars to do in Do_Screen Number_of_chars_done DW 0 ; How many chars we have done this pass Number_of_scrolls_done DW 0 ; Count of lines scrolled this display cycle Image_ptr DW ? ; Pointer to screen image, used by Put_Screen ; VT100 status flags and flag word Keypad_application_mode EQU 1 ; Determines data sent by keypad keys Cursor_keys_mode EQU 2 ; Determines data sent by arrow keys Private_esc_seq EQU 4 ; A q-mark was found in esc seq Printer_controller_mode EQU 8 ; Send all data to printer, not display Auto_copy_mode EQU 10h ; Send data to display AND printer Printer_wanted = Printer_controller_mode + Auto_copy_mode VT100_status_flags DB 0 ; Place to hold status flags Old_status_flags DB 0 ; Place to hold previous value ; Screen management flags Screen_open EQU 1 ; Command mode screen has already been saved Position_changed EQU 2 ; New position Line_changed EQU 4 ; Data on current line changed Screen_changed EQU 8 ; Data on more than one line changed Force_screen_update EQU 10h ; Update every character of both video pages, ; (on color card), which is slower than ; mollasses in January Changed EQU Position_changed + Line_changed + Screen_changed Screen_flags DB 0 ; Flags for managing the screen Hit_ESC DB 0 ; Flag to say we just hit an escape, used only ; while in printer controller mode Prev_shift_state DB 0FFh ; Set initial shift to something impossible ; Rendition / attribute data ... Bold EQU 1 ; Extra bright (SGR code 1) Underscored EQU 2 ; Underlined (SGR code 4) Blinking EQU 4 ; Blinking (SGR code 5) Inverted EQU 8 ; Inverse video (SGR code 7) Graphi EQU 10h ; Flag for graphics char set Rendition DB ? ; Currently set graphic rendition flags Attribute_Byte DB ? ; Current attribute ; VT100 LEDs L1 - L4 LED1 EQU 1 LED2 EQU 2 LED3 EQU 4 LED4 EQU 8 LEDs DB 0 ; Start with all four LEDs off LoWork DB 10 DUP (Space) ; Low level work area, bigger than needed ; Scrolling region Top_margin DB ? ; Offset of top line in scrolling region Bottom_margin DB ? ; Offset of bottom line in scrolling region EVEN ; Try for word boundary Script_processor DW 0 ; Address of script processor to run Line_Ptr DW ? ; Address of first char on line we are on Char_Ptr DW ? ; Address of the char we are on Previous_char DB ? ; Most recently seen char Backed_up_char DB ? ; backed-up char Char_set DB ? ; Character set we are in G0_set DB ? ; G0 character set G1_set DB ? ; G1 character set ; One byte of quickly-clobbered junk, terminated with $ just-in-case Tmp DB ?, '$' ; Things saved by DECSC and restored by DECRC Save_char_set DB ? ; Saved char set number Save_G0_set DB ? ; Saved G0 char set Save_G1_set DB ? ; Saved G1 char set Save_rendition DB ? ; Saved rendition Save_position DW ? ; Saved cursor position ; Another saved item ... Save_SP_for_abort DW ? ; Place to save a stack ptr EVEN ; Try for word boundary ; Numbers read in during an escape sequence NumCnt DB ? ; The count of numbers we have stored NumPtr DB ? ; Which one we are on Nums DB 10 DUP (?) ; Numbers we have seen ... EBufLn EQU 128 EBuffr DB EBufLn DUP (?) ; Escape buffer EBufGt DW ? ; Get ptr EBufPt DW ? ; Put ptr EBufCt DB ? ; Count of chars in buffer NewRow DB ? ; Place to put new row number ; Key redefinitions ktrntab DW ? ; address of translation table krpltab DW ? ; address of replacement table ktlen DW ? captrtn DW ? ; Routine to call for captured output portno DB ? ; Port number flags DB ? ; status flags, our copy of [bx].flg ; Things in flags1 ... prtscr EQU 80h ; print screen pressed lnwrap EQU 40h ; line wrap enabled inited EQU 08h ; been here before... flags1 DB 0 ; internal flags Esc_Ch DB ? argadr DW ? ; address of arg blk PUBLIC Cursor_position Cursor_position LABEL WORD ; Single name for both Column and Row Column DB ? ; Column we are in Row DB ? ; Row we are on Cursor DW ? ; Working place for Cursor pos DataS ENDS ; Dummy segment for allocated memory Alloc SEGMENT AT 0 ;*************************************************************************** ; The screen image, locally updated then blasted out to real screen memory ;*************************************************************************** Number_of_rows EQU 24 ; Length in rows Number_of_columns EQU 80 ; Width in characters Highest_row EQU Number_of_rows-1; Highest row number Highest_column EQU Number_of_columns-1 ; Highest column number Lower_right EQU (100h*Highest_row) + Highest_column ; Lower right corner of virtual screen ... Number_of_chars_on_VT100_screen EQU Number_of_columns*Number_of_rows ; Number of chars in a VT100 screen Number_of_rows_PC EQU 25 ; Length in rows including mode line Number_of_chars_on_PC_screen EQU Number_of_columns*Number_of_rows_PC ; Number of chars in IBM screen EVEN ; Try for word boundary ; Display update list PUBLIC Update_list Update_list DW (2 * Number_of_chars_on_PC_screen) DUP (?) ; One word location ; info followed by one word of char/attr ; for each update item Alloc_cursor_position DW ? ; Copy of current cursor position PUBLIC Screen_Image, Mode_line Screen_Image DW Number_of_chars_on_VT100_screen DUP (?) ; The screen buffer itself ; Mode line, follows immediately after screen image Mode_line DW Number_of_columns DUP (?) ; The previous screen contents, used to build update list Old_Image DW Number_of_chars_on_PC_screen DUP (?) ; Contents of screen on entry to terminal emulator, restored on exit Command_mode_cursor DW ? ; Saved cursor position from command mode Command_mode_screen DW Number_of_chars_on_PC_screen DUP (?) End_Alloc LABEL BYTE Alloc ENDS ;*************************************************************************** ; *Code* ... RetSkp MACRO ; Return skip jmp RSkp ENDM RetNop MACRO ; Return from subroutine, take up 3 bytes ret nop nop ENDM Code SEGMENT PUBLIC ; code segment EXTRN Quit_emulator:BYTE, Which_card:BYTE EXTRN PrtChr_PC:NEAR, OutChr_PC:NEAR, SendBr:NEAR, Helper:NEAR EXTRN SerIni_PC:NEAR, SerRst_PC:NEAR, Enter_Server:NEAR EXTRN Exit2:NEAR, Type_to_screen:NEAR, Force_card:BYTE EXTRN PosCur_PC:NEAR, Quick_Push:NEAR, ClrMod_PC:NEAR EXTRN Get_memory_block:NEAR, Set_up_video_card_pointers:NEAR EXTRN Go_to_page_zero:NEAR, Blank_current_page:NEAR EXTRN Switch_video_pages:NEAR, Beam_Off:NEAR, Beam_On:NEAR Datas_ds DW 0 ; Place to hold normal ds (DATAS) Alloc_ds DW 0 ; Place to hold ptr to allocated ds (ALLOC) Screen_Image_ptr DD 0 ; Doubleword pointer to VT100 image Mode_Line_ptr DD 0 ; Doubleword pointer to mode line Max_number_of_items DW 300 ; Max number of display update items to do ; during retrace in Update_nondisplayed_page Number_of_update_items DW 0 ; How many items in list? ASSUME cs:Code, ds:Datas, es:Datas ; Close out terminal emulation screen Close_Screen_PC PROC test Screen_flags, Screen_Open ; Should we restore? jz CLO_ret ; No call Restore_command_mode_screen ; Get back original screen and Screen_flags, NOT Screen_open ; Clear this flag CLO_ret: ret ; Done here Close_Screen_PC ENDP ; Perform terminal emulation Term_PC PROC ; terminal emulator entry point push es ; save caller's extra segment address mov argadr, ax ; save argument ptr mov ax, SEG datas mov es, ax call Set_up_dynamic_memory ; Try to get memory jnc TRM_0 mov ah, PrStr ; Code to type message mov dx, OFFSET No_memory ; Complain about lack of memory int Dos ; Display it mov Quit_emulator, 1 ; Flag that we want out jmp TRM_S3 ; Quit the program TRM_0: call argini ; Initialize options from arg address call Enter_text_mode ; This program requires text mode call Set_up_video_card_pointers ; Learn video mode, card type, etc. test Screen_flags, Screen_open ; Is the screen already "open"? jnz TRM_2 ; All set, don't worry call Save_command_mode_screen ; Save it or Screen_flags, Screen_open ; Set flag for later TRM_2: test flags1, inited ; Have we run yet? jz TRM_3 ; No, go initialize everything call ReInit_Screen ; Display our screen buffer on the screen jmp SHORT TRM_5 ; Join common code TRM_3: call Initialize_Screen ; Initialize screen and buffers call Flush_esc_buffer ; Initialize pointers into escape buffer call Do_mode_line ; Do proper thing with the mode line call Do_our_screen ; Get screen up-to-date or flags1, inited ; Remember that we've done this already TRM_5: mov Quit_emulator, 0 ; Clear this flag ; First, try for chars from keyboard PUBLIC Trm_Kb TRM_Kb: ; Before we are allowed to really check the keyboard, we have to see if ; we are running under control of a script. If we are, we have to call ; it at its "keyboard" entry instead of reading from the keyboard. cmp Script_processor, 0 ; Is there a script processor to run? je TRM_No_script ; No TRM_Ck: mov ah, 1 ; Code to see if anything there int Kb ; Check input buffer jnz TRM_S0 ; Get char mov al, 0FFh ; Say we don't have a character jmp SHORT TRM_S1 ; Pass this along to the processor TRM_S0: sub ah, ah ; Code zero int Kb ; Get the char from the buffer TRM_S1: sub ah, ah ; Code for "keyboard" entry call Script_processor ; Let it do its thing test Quit_emulator, 1 ; Is this flag set? jz TRM_Scr ; No, try for more keyboard chars ... TRM_S3: call Close_Screen_PC ; Go set things up right for command mode jmp SHORT TRM_Quit ; Go back to other program TRM_No_script: mov ah, 1 ; Code to see if anything there int Kb ; Check ... jz Trm_Scr ; nothing available... sub ah, ah ; Code zero int Kb ; get the char from the buffer push ax ; save character temporarily call GSS ; get shift state into al mov bl, al ; save shift state pop ax cmp al, Esc_Ch ; Escape character? jne TRM_Key ; No, go deal with the character mov si, OFFSET Esc_chr_template ; The template giving help for ; the escape char call Build_mode_line ; Turn it into a mode line call Do_our_screen ; Display it jmp SHORT TRM_Quit ; Go finish up here TRM_Key: call Handle_keystroke ; Do whatever is required for this keystroke test Quit_emulator, 1 ; Is this flag set? jz TRM_Kb ; No, try for more keyboard chars ... call Close_Screen_PC ; Go set things up right for command mode jmp TRM_Quit ; Go back to other program ; Second, handle any buffered port characters PUBLIC TRM_Scr TRM_Scr: call Do_Screen ; Call routine to process all buffered ; characters received over the comm port test Quit_emulator, 1 ; Is this flag set? jz TRM_Kb ; No, try for more keyboard chars ... call Close_Screen_PC ; Go set things up right for command mode ; jmp TRM_Quit ; Go back to other program ; User hit the magic character, leave this routine to see what he wants TRM_Quit: call Go_to_page_zero ; Be on page zero in display memory cmp Script_processor, 0 ; Is there a script processor active? je TRM_q_no_script ; No mov ah, 2 ; Code to shutdown call Script_processor ; Shut him down TRM_q_no_script: mov al, flags mov bx, argadr mov [bx].flgs, al ; update flags in arg block pop es ; restore segment register ret ; and return to caller Term_PC ENDP ; Initialize internal variables based on argument block we were passed argini PROC ; read passed arguments mov bx, argadr ; base of argument block mov al, [bx].flgs ; get flags and al, capt + havtt + lclecho + modoff mov flags, al ; mask for allowable and save mov al, [bx].prt mov portno, al ; update port number mov ax, [bx].captr mov captrtn, ax ; buffer capture routine mov ax, [bx].belld mov beldiv, ax ; bell divisor mov ax, [bx].klen mov ktlen, ax ; length of key redef tbl mov ax, [bx].ktab mov ktrntab, ax ; save key translation table mov ax, [bx].krpl mov krpltab, ax mov al, [bx].escc mov Esc_ch, al mov ax, 4 ; Get a 4 mov Max_number_of_chars, ax ; Store it as default mov al, [bx].BaudB ; Get baud bits cmp al, Baud_table_size ; Too big? jnb ARG_1 ; Yes, use the default shl al, 1 ; Double for word offset cbw ; Make full word mov bx, ax ; Copy to bx mov ax, Baud_Char[bx] ; Pick up the value for this baud rate mov Max_number_of_chars, ax ; Install as the per-loop limit ; in Do_Screen ARG_1: ret ; That's it argini ENDP Set_up_dynamic_memory PROC cmp Alloc_ds, 0 ; Is this already set up? jnz SDM_OK ; Yes, don't do another mov bx, OFFSET End_Alloc+15 ; Round up to next paragraph mov cl, 4 ; Shift count shr bx, cl ; Shift to get number of paras we need call Get_memory_block ; Get a memory block of the right size jc SDM_ret ; NG, die mov Alloc_ds, ax ; Save the address of our block mov WORD PTR Screen_Image_ptr+2, ax ; Save it here also ... mov WORD PTR Mode_Line_ptr+2, ax ; and here ... mov ax, OFFSET Screen_Image ; Offset of the screen image in its seg mov WORD PTR Screen_Image_ptr, ax ; Save for other users mov ax, OFFSET Mode_Line ; Offset of the mode line mov WORD PTR Mode_Line_ptr, ax ; Save it also mov ax, Datas ; Get ptr to DataS segment mov Datas_ds, ax ; Save it here SDM_OK: clc ; No error SDM_ret: ret Set_up_dynamic_memory ENDP ; Initialize internal screen buffer to blanks, put cursor in upper ; left corner of screen PUBLIC Initialize_screen, Rendition, Screen_flags Initialize_Screen PROC ; init screen stuff VTFlags = Keypad_application_mode + Cursor_keys_mode and VT100_status_flags, NOT VTFlags ; Turn off all relevant flags sub al, al ; Get a zero mov Char_set, al ; In char set 0 mov G0_set, al ; This set is non-graphics mov G1_set, al ; This set is non-graphics also mov Rendition, al ; Normal rendition mov Attribute_Byte, 7 ; Normal screen attribute (white on black) mov Top_margin, al ; Zero top of scrolling region mov al, Highest_row ; Get number of highest row on VT100 screen mov Bottom_margin, al ; Save as bottom of scrolling region mov ax, Alloc_ds ; Ptr to allocated data segment mov es, ax ; Load into es ASSUME es:Alloc cld ; Move forward mov ax, 0720h ; Normal video blank mov cx, Number_of_chars_on_PC_screen ; Number of bytes to lay down mov di, OFFSET Screen_Image ; Where to put the bytes rep stosw ; Zap the buffer mov cx, Number_of_chars_on_PC_screen ; Number of bytes to lay down mov di, OFFSET Old_Image ; Where to put the bytes rep stosw ; Zap the buffer mov ax, Datas_ds ; Ptr to normal data segment mov es, ax ; Load into es ASSUME es:Datas cmp cs:Which_card, 1 ; Color card? jne ISC_2 ; Nope, skip this call Blank_current_page ; Clear page zero call Switch_video_pages ; Switch to the other page call Blank_current_page ; Clear page one call Switch_video_pages ; Switch back to the first page ISC_2: sub ax, ax ; Make a zero mov Cursor_position, ax ; Store as cursor position jmp Set_pointers ; Go set up ptrs, return from there Initialize_Screen ENDP ; Re-initialize screen ... we have run this module before, but the ; screen needs to be fully restored PUBLIC ReInit_Screen ReInit_Screen PROC ; init screen stuff mov ax, Alloc_ds ; Ptr to allocated segment mov ds, ax ; Set ds to it nop ; Be nice to debugger mov es, ax ; es also ASSUME ds:Alloc, es:Alloc mov si, OFFSET Screen_Image mov di, OFFSET Old_Image mov cx, Number_of_chars_on_PC_screen cld ; Forwards rep movsw ; Force old image to match the screen mov ax, Datas_ds ; Ptr to allocated segment mov ds, ax ; Set ds to it nop ; Be nice to debugger mov es, ax ; es also ASSUME ds:Datas, es:Datas call Do_Mode_Line ; Make sure mode line is up-to-date call Set_up_video_card_pointers ; Adapt to the current card call Beam_Off ; Suppress video during copy of screen mov si, OFFSET Screen_Image ; Source OFFSET within segment sub di, di ; Start at zero mov cx, Number_of_chars_on_PC_screen ; Full PC screen push es ; Save reg ASSUME es:Nothing call Get_Screen_Segment ; Get address of our screen mov es, ax ; Install it mov ax, Alloc_ds ; Segment that holds the Screen_Image mov ds, ax ; which we must copy from ASSUME ds:Alloc cld ; Forwards rep movsw ; Blast out the screen pop es ; Get back reg ASSUME es:Datas mov ax, Datas_ds ; Normal segment mov ds, ax ; Restore it ASSUME ds:Datas call Beam_On ; Re-enable video mov ah, 2 ; Code to set cursor position sub bh, bh ; Page zero mov dx, Cursor_position ; Set up cursor position int Screen ; Do it ; We just updated the visible screen, now update the invisible one ... ; ... this is very slow (which is why we updated the visible screen first). or Screen_flags, Force_screen_update ; Do a whole lot of work jmp Do_our_screen ; Use standard routine to install it, ; return from there ReInit_Screen ENDP ; Save and restore command mode screen PUBLIC Save_command_mode_screen Save_command_mode_screen PROC call Beam_Off ; Suppress video during copy of screen sub si, si ; Start at zero mov di, OFFSET Command_mode_screen ; Destination OFFSET within segment mov cx, Number_of_chars_on_PC_screen ; Full PC screen push ds ; Save ds call Get_Screen_Segment ; Get address of our ds mov ds, ax ; Install it mov ax, Alloc_ds ; Pick up ptr to allocated segment mov es, ax ; Set up in es -- Command_mode_screen's seg ASSUME es:Alloc cld ; Forwards rep movsw ; Blast out the screen pop ds ; Get back ds mov ah, 3 ; Code to read cursor position sub bh, bh ; Page zero int Screen ; Get cursor position mov Command_mode_cursor, dx ; Save for later mov ax, Datas_ds ; Pick up normal ds mov es, ax ; Reinstall in es ASSUME es:DataS jmp Beam_On ; Re-enable video Save_command_mode_screen ENDP Restore_command_mode_screen PROC call Go_to_page_zero ; Be on page zero in display memory call Beam_Off ; Suppress video during copy of screen mov si, OFFSET Command_mode_screen ; Source OFFSET within segment sub di, di ; Start at zero mov cx, Number_of_chars_on_PC_screen ; Full PC screen push es ; Save reg call Get_Screen_Segment ; Get address of our screen mov es, ax ; Install it mov ax, Alloc_ds ; Pick up ptr to allocated segment mov ds, ax ; Set up in ds -- Command_mode_screen's seg ASSUME ds:Alloc cld ; Forwards rep movsw ; Blast out the screen pop es ; Get back reg mov ah, 2 ; Code to set cursor position sub bh, bh ; Page zero mov dx, Command_mode_cursor ; Get back saved cursor position int Screen ; Set cursor position mov ax, Datas_ds ; Pick up normal ds mov ds, ax ; Reinstall in es ASSUME ds:DataS jmp Beam_On ; Re-enable video Restore_command_mode_screen ENDP ; Assemble mode line, then install in screen buffer PUBLIC Do_mode_line Do_mode_line PROC test flags, modoff ; Is the mode line disabled? jz DML_Do_it ; No, go draw it jmp Erase_mode_line ; Blow away old mode line, ret from there DML_Do_it: mov ah, 2 ; Code to get shift states int Kb ; Get them mov dx, ax ; Save state for later mov al, Esc_ch ; Get the escape character cmp al, Space ; Is it a control char? jge DML_No_carat ; No, don't type the carat mov M_Esc,'^' ; Store a carat add al, 40h ; Convert char to printable version mov M_Esc + 1, al ; Store the fixed-up char jmp SHORT DML_Print ; Skip code for no carat DML_No_carat: mov M_Esc, al ; Store the char mov M_Esc + 1, Space ; Space following DML_Print: mov si, OFFSET M_Prt_Off ; Assume printing is off test VT100_status_flags, Printer_wanted ; In one of these modes? jnz DML_Print_on ; Yes, say Prt is on test flags1, prtscr ; Should we be printing? jz DML_Write_Prt ; No DML_Print_on: mov si, OFFSET M_Prt_On ; Printing is on DML_Write_Prt: mov di, OFFSET M_Prt ; Where to write the data mov cx, M_Prt_length ; The number of chars to copy cld ; Forwards rep movsb ; Copy the bytes into the mode line mov di, OFFSET M_Caps ; Put data here mov si, OFFSET Caps ; String to say CAPS mov cx, 4 ; Number of bytes test dl, Caps_State ; Is it set? jnz DML_KPad ; We are in CAPS state, go say so mov si, OFFSET Lc ; Not in CAPS state DML_KPad: rep movsb ; Copy the bytes mov di, OFFSET M_KPad ; Put data here mov si, OFFSET Num ; String to say NumL mov cx, 4 ; Number of bytes test dl, Num_State ; Is it set? jnz DML_LED ; We are in Num state, go say so mov si, OFFSET Arrow ; Not in Num state DML_LED: rep movsb ; Copy the bytes mov al, 70h ; Normal test LEDs, LED1 ; Is this LED lit? jz DML_L1 ; No mov al, 0f0h ; DML_L1: mov M_L1, al ; Set attr for this LED mov al, 70h ; Normal test LEDs, LED2 ; Is this LED lit? jz DML_L2 ; No mov al, 0f0h ; DML_L2: mov M_L2, al ; Set attr for this LED mov al, 70h ; Normal test LEDs, LED3 ; Is this LED lit? jz DML_L3 ; No mov al, 0f0h ; DML_L3: mov M_L3, al ; Set attr for this LED mov al, 70h ; Normal test LEDs, LED4 ; Is this LED lit? jz DML_L4 ; No mov al, 0f0h DML_L4: mov M_L4, al ; Set attr for this LED mov si, OFFSET Mode_template ; Start at beginning ; Fall through to ... ; Build mode line -- Use assembled template to generate the mode line ; Call with ; si/ ptr to template to use Build_mode_line: call Erase_mode_line ; Erase any existing mode line mov di, OFFSET Mode_line ; Store the mode line here mov ax, Alloc_ds ; Ptr to Allocated data segment mov es, ax ; Set up es ASSUME es:Alloc Next_Attr: lodsb ; Pick up attribute or al, al ; End-of-the-line? je BML_End ; Done building mode line cmp Force_mono, 0 ; Are we stuck with mono no matter what? jne Do_mono ; Yes cmp CRT_Mode, 7 ; Monochrome card? jne Use_attr ; Color, use what we have set up ; Monochrome card does rude things to attributes that look nice in color ; Use different attributes Do_mono: cmp al, 71h ; Highlighted? jne Use_attr ; No mov al, 70h ; Be a square, leave it just inverted Use_attr: mov bl, al ; Save attr in bl Next_Char: lodsb ; Get the next char or al, al ; Done with this attr? je Next_Attr ; Yes, go try for another stosb ; Store the byte in the mode line mov al, bl ; Get the current attr stosb ; Store it too jmp Next_Char ; Go do another char BML_End: mov ax, Datas ; Ptr to normal data segment mov es, ax ; Restore es ASSUME es:Datas ret Do_mode_line ENDP ; Set internal mode line to normal video spaces Erase_mode_line PROC mov di, OFFSET Mode_line ; Ptr to mode line mov cx, Number_of_columns ; Number of words in the line mov ax, Alloc_ds ; Ptr to allocated ds mov es, ax ; Install in es ASSUME es:Alloc mov ax, 0720h ; Normal blanks cld ; Moving forwards rep stosw ; Store this in the whole line mov ax, Datas_ds ; Ptr to normal ds mov es, ax ; Install in es ASSUME es:Datas or Screen_flags, Screen_changed ; Flag screen change ret ; Done here Erase_mode_line ENDP ; Routine to get as many chars from port as possible, ; and update screen accordingly PUBLIC Do_screen Do_Screen PROC cmp Count, 0 ; Are there any chars we haven't seen already? jne DOS_1 ; Yes, deal with them jmp No_more_chars ; No chars, don't waste time here DOS_1: sub ax, ax ; Get a zero mov Previous_char, al ; No last character mov Backed_up_char, al ; No backed-up character mov Number_of_chars_done, ax ; Haven't done any chars yet mov Number_of_scrolls_done, ax ; These either mov ax, OFFSET EBuffr ; Get pointer to beginning of esc buffer mov EBufGt, ax ; Save as new getter ptr so we will pick ; up any sequence already partially read mov Save_SP_for_abort, sp ; Save a copy of the current stack ptr ; in case we have to blow out an escape seq ; in the middle ... PUBLIC Get_next_char Get_next_char: cmp Allow_blast, 0 ; Flag set? jnz Got_1 ; Yes, skip this test mov ax, Number_of_chars_done ; Number done so far cmp ax, Max_number_of_chars ; Too many? jl Got_1 ; Not yet jmp No_more_chars ; Did all we`re allowed, pretend no more Got_1: inc ax ; Bump by one mov Number_of_chars_done, ax ; Store updated count mov al, VT100_status_flags ; Get current status flags mov Old_status_flags, al ; Save for later Call PullCh ; Get a char from somewhere jmp No_more_chars ; No char to get, done here cmp al, Esc ; Is it an escape? jne GNC_1 ; No mov Hit_ESC, 0FFh ; Flag that we hit an escape jmp Do_Escape ; Go handle it GNC_1: mov Hit_ESC, 0 ; Not in an esc seq test VT100_status_flags, Printer_controller_mode ; In funny mode? jz GNC_2 ; No, deal with it as usual jmp SHORT Done_with_char ; In this mode we mostly ignore chars GNC_2: cmp al, Space ; Compare with space ... jge Do_Add ; If space or greater, just add it mov bx, OFFSET ActTb1 ; Action table mov cx, NChrTb1 ; Number of chars to check mov dx, OFFSET ChrTb1 ; Character table to use jmp SHORT Check_Table ; Do the lookup and dispatch ; Add this char to the stored line Do_Add: call Add_this_char ; Add this char, fall thru to ... ; Done_with_char == Bottom of loop: ; ; If we were if "Printer Controller Mode" when we started this and we ; are still in it now, then now is the time to dump the sequence to ; the printer. ; This lets all intended ESC sequences make it to the printer, but ; suppresses both the $[5i that puts us into Printer Controller Mode ; (because we were not in the Mode to start) and the $[4i that makes ; us leave the mode (because we are not in the mode now). ; ; Close out any active Esc sequence. ; ; Go see if there are any more characters to be read. PUBLIC Done_with_char Done_with_char: mov ax, OFFSET EBuffr ; Get pointer to beginning of esc buffer mov EBufGt, ax ; Save as new getter ptr so we will pick ; up any sequence that we have just read test flags1, prtscr ; Should we be printing? jnz DWC_Print ; Yes ; Printer_wanted = Printer_controller_mode + Auto_copy_mode test VT100_status_flags, Printer_wanted ; In a print mode? jz DWC_0 ; No test Old_status_flags, Printer_wanted ; Were we before? jz DWC_0 ; No DWC_Print: mov bx, EBufGt ; Escape buf getter ptr cmp bx, EBufPt ; Compare with putter ptr je DWC_0 ; If same, no chars held there mov dl, [bx] ; Pick up the char inc EBufGt ; Bump the ptr mov ah, LstOut ; Code to print a character int DOS ; Print this character jmp DWC_Print ; See if any more to do DWC_0: sub al, al ; Make a zero mov Backed_up_char, al ; Clobber "backed-up" char call Flush_esc_buffer ; Trash escae buffer cmp Quit_emulator, 0 ; Are we quitting? jne No_more_chars ; Yes cmp Allow_blast, 0 ; Flag set? jnz DWC_1 ; Yes, skip slowdown test mov ax, Number_of_scrolls_done cmp ax, Maximum_scrolls ; Scrolled enough to have to stop and update? jge No_more_chars ; Yes DWC_1: jmp Get_next_char ; Do more ; Abort processing, possibly in the middle of decoding an esc seq Abort_esc_sequence: mov sp, Save_SP_for_abort ; Get back saved stack pointer jmp Done_with_char ; Go clean it up ; Leaving screen update state, install buffer in screen memory if required PUBLIC No_more_chars No_more_chars: mov ah, 2 ; Code to get shift state int KB ; Get it and al, Num_state + Caps_state ; Check only these cmp al, Prev_shift_state ; Did something change? je End_of_pass ; No mov Prev_shift_state, al ; Save for next time call Do_mode_line ; Go make a new mode line End_of_pass: test Screen_flags, Changed ; Did something change? jz EOP_1 ; No jmp Do_our_screen ; Use standard routine to install it, ; return from there EOP_1: ret ; Done here Do_Screen ENDP ; Routine to search a table of chars for a desired char, then ; dispatch to another routine based on where the string was found, ; or if it was not found ; ; Call with ... ; al/ char to search for ; bx/ table of jump addresses (each entry one word) ... number of ; entries is one greater than number of entries in search ; table -- final entry is used if the search fails ; cx/ number of bytes in search table ; dx/ table to search in (each entry one byte) ; Trashes most of these, also di ... Check_Table PROC mov di, dx ; Copy over addr of table cld ; Forwards repne scasb ; Search for char in table jne Chk_1 ; If search failed, don't back up ptr dec di ; Drop by one due to REPNE overshoot Chk_1: sub di, dx ; Get index of char shl di, 1 ; Double for word offset add bx, di ; Add displacement to base of action table jmp WORD PTR [bx] ; Dispatch to the desired routine Check_Table ENDP ; Routine to flush the Escape buffer Flush_esc_buffer PROC mov ax, OFFSET EBuffr ; Address of Escape buffer mov EBufGt, ax ; Set up as getter ptr mov EBufPt, ax ; Set up as putter ptr also mov EBufCt, 0 ; Clear ebuf count mov Hit_ESC, 0 ; Not in an esc seq now ret ; Done here Flush_esc_buffer ENDP ;****************************************************************************** ; *General utilities ... ; Get shift state into al. We only care about shift, ctl, and alt keys. ; Right shift is collapsed into left shift. Gss PROC mov ah, 2 int Kb ; get current shift state mov bl, al ; copy for a moment and bl, right_shift ; mask out all but right shift shl bl, 1 ; move right shift to left shift pos or al, bl ; collapse shift bits and al, (left_shift + alt_shift + ctl_shift) ret Gss ENDP ; Send_char_to_port -- Send the character in al out to the serial port ; Call with ; al/ char to be sent Send_char_to_port PROC test flags, lclecho ; Local echo on? jz SCP_Noecho ; No call Type_to_screen ; Echo every character immediately SCP_Noecho: mov ah, al ; this is where outchr expects it call OutChr_PC ; output to the port RetNop ret Send_char_to_port ENDP ; Returns with carry on if a character is available PUBLIC Get_char_from_port Get_char_from_port PROC push dx ; PrtChr_PC clobbers dx call PrtChr_PC ; Character at port? jmp SHORT portc1 ; Yes, go handle nop ; 3 bytes clc ; No carry -> no character jmp SHORT portc2 ; Go finish up portc1: and al, 7Fh ; We don't worry about parity here stc ; Have a character portc2: pop dx ; Get back saved reg ret ; and return Get_char_from_port ENDP PUBLIC Handle_keystroke Handle_keystroke PROC push ax ; Save original value mov al, ah ; put scan code into al mov ah, bl ; shift state into ah cmp ax, (256*ctl_shift) ; Is this control-break? jne Not_Break ; No pop ax ; Fix stack mov ah, DConIo mov dl, 0FFh int Dos ; Read the bogus ^C DOS gets jmp SendBr ; Use other routine to send the break Not_Break: cmp ax, (ctl_shift*256)+CtrlPrtSc jne Not_PrtSc call trnprs ; Toggle the print screen setting pop ax ret Not_PrtSc: cmp ax, (ctl_shift*256)+57 ; Control-space? jne Not_Ctrl_Space ; No sub al, al ; Want a null call Send_char_to_port ; Send it out pop ax ; Get back saved reg ret ; Done here Not_Ctrl_Space: cmp ax, (alt_shift*256)+108 ; Alt F5? jne Not_ALT5 ; No pop ax jmp Leave_emulator ; Want to get out Not_ALT5: cmp ax, (alt_shift*256)+109 ; ALT F6? jne Not_ALT6 ; No pop ax jmp Call_Helper ; Call the help module Not_ALT6: cmp ax, (alt_shift*256)+110 ; ALT F7? jne Not_ALT7 ; No pop ax jmp Leave_program ; Want to get all the way out Not_ALT7: cmp ax, (alt_shift*256)+111 ; ALT F8? jne Not_ALT8 ; No pop ax call Go_to_page_zero ; Go to base page mov dx, (256*24) + 0 ; Lower left corner of screen call PosCur_PC ; Go there call ClrMod_PC ; Erase mode line (it's in inverse video) call Quick_Push ; Push directly from here nop ; Skips on success nop nop call SerIni_PC ; In case it got turned off ... or Screen_flags, Force_screen_update ; Update the world jmp Do_our_screen ; Restore our screen, ret from there Not_ALT8: cmp m7171, 0 ; In 7171 mode? jz Not_7171 ; No mov cx, Number_of_key_codes_7171 ; Number of entries to check mov dx, OFFSET Key_codes_7171 ; Scan code table to use mov di, dx ; Copy over addr of table cld ; Forwards repne scasw ; Search for word in table jne HAN_1 ; Search failed, go do something else mov bx, OFFSET Key_actions_7171 ; Action table for 7171 mode mov cx, di ; Copy to cx dec cx ; Drop by one word due to REPNE overshoot dec cx ; ... sub cx, dx ; Get index of char add bx, cx ; Add displacement to base of action table mov si, [bx] ; Pick up addr of string to send call Send_str_to_port ; Send it out pop ax ; Get back reg ret ; Done here %OUT >> About half way through source file Not_7171: mov cx, Number_of_key_codes ; Number of entries to check mov dx, OFFSET Key_codes ; Scan code table to use mov di, dx ; Copy over addr of table cld ; Forwards repne scasw ; Search for word in table jne HAN_1 ; Search failed, go do something else mov bx, OFFSET Key_actions ; Action table mov cx, di ; Copy to cx dec cx ; Drop by one word due to REPNE overshoot dec cx ; ... sub cx, dx ; Get index of char add bx, cx ; Add displacement to base of action table add bx, cx ; Double the offset add bx, cx ; Triple it (6 bytes instead of 2) lea dx, 2[bx] ; Get 2 + > into dx call WORD PTR [bx] ; Dispatch to the desired routine, but come ; back when done ... pop ax ; Get back reg ret ; Done here ; Translate the scan code in ah according to the translate table ; given in ktrntab/krpltab, output to port. If no translation, ; use ascii char in al. HAN_1: test flags, havtt ; translate table given? jnz HAN_2 ; Yes, check it out pop ax ; Get back reg jmp SHORT HAN_3 ; Go output it normally HAN_2: mov di, ktrntab ; pick up translate tbl mov cx, ktlen ; length of tbl cld ; Forwards repne scasw ; look for our key pop ax ; recover character jne HAN_3 ; not found, forget it sub di, ktrntab ; get index into tbl sub di, 2 ; (minus 2 for pre-increment) mov bx, krpltab ; get replacement table mov si, [bx][di] ; and addr of replacement mov al, [si] ; Get first byte (length) cbw ; Blow up mov bx, ax ; Copy to bx inc si ; Point to translation string sub al, al ; Get a zero mov [si][bx], al ; Tie off the string at its declared length jmp Send_str_to_port ; Send it off to the port HAN_3: call Send_char_to_port ; just output single char ret ; and return trnprs: xor Flags1, PrtScr ; Flip the flag mov Prev_shift_state, 0FFh ; Store an impossible setting to force ; a redraw of mode line ret Handle_keystroke ENDP ; puts current screen segment in ax Get_screen_segment PROC mov ax, 0b000h ; assume bw for now cmp CRT_Mode, 7 ; 7 is bw (***) je scrse1 mov ax, 0b800h ; color card scrse1: ret Get_screen_segment ENDP ; Do_our_screen -- Internal routine to set up for and call Put_Screen Do_our_screen PROC mov ax, Alloc_ds ; Pick up ptr to allocated ds mov es, ax ; Install in es ASSUME es:Alloc mov ax, Cursor_position ; Pick up cursor pos from DATAS segment mov Alloc_cursor_position, ax ; Install it in the Alloc segment mov bx, OFFSET Screen_image ; Ptr to our internal screen image call Put_Screen mov ax, Datas_ds ; Restore normal pointer ... mov es, ax ; to es ASSUME es:Datas ret ; DOne here Do_our_screen ENDP ; Put_Screen -- Copy a new screen image and mode line into display memory ; call with ; es:bx/ ptr to image to put on the screen, compared to old image PUBLIC Put_Screen Put_Screen PROC ASSUME es:Nothing mov Image_ptr, bx ; Save parameter sub bx, 2 ; Back up to Cursor pos mov ax, es:[bx] ; Pick it up mov Cursor, ax ; Store in local variable cld ; Forwards test Screen_flags, Screen_changed + Line_changed + Force_screen_update jz PSC_Position ; If only position changed, update only it cmp cs:Which_card, 1 ; Color card? je PSC_Color ; Yes, go do strange update mov si, Image_ptr ; Source for transfer push ds ; Save ds push es ; Save es mov ax, es ; Copy es mov ds, ax ; to ds mov ax, 0B000h ; Address of mono card memory cmp cs:Which_card, 2 ; Is this really the EGA in color mode? jne Not_EGA ; No mov ax, 0B800h ; EGA in color mode, use color address Not_EGA: mov es, ax ; Install in es reg sub di, di ; Destination OFFSET within segment mov cx, Number_of_chars_on_PC_screen ; Full screen rep movsw ; Blast out the screen pop es ; Get back original es pop ds ; and ds jmp SHORT PSC_Position ; Go set position and finish up PUBLIC PSC_Color PSC_Color: call Build_update_list ; First build an update list of locations ; to be changed, updating Old_Image as we go mov ax, Number_of_update_items ; See how many there are to do or ax, ax ; Are there any? jnz PSC_Update ; Yes, go do them PSC_Position: mov dx, Cursor ; Get current cursor pos mov ah, 2 ; Code to set cursor position mov bh, Which_page ; The current page int Screen ; Set cursor position jmp SHORT PSC_Done ; Go finish up PSC_Update: call Update_nondisplayed_page ; Run the update list against the ; page the user can't see mov dx, Cursor ; Get current cursor pos mov ah, 2 ; Code to set cursor position mov bh, 1 ; Get a 1 sub bh, Which_page ; Minus current page number = other page int Screen ; Set cursor position call Switch_video_pages ; Then switch pages ... call Update_nondisplayed_page ; Then run the update list against the ; page we were just on mov dx, Cursor ; Get current cursor pos mov ah, 2 ; Code to set cursor position mov bh, 1 ; Get a 1 sub bh, Which_page ; Minus current page number = other page int Screen ; Set cursor position call Switch_video_pages ; Then switch pages ... PSC_Done: and Screen_flags, NOT ( Changed + Force_screen_update ) ; Screen + position are all set ret ; Go home PUBLIC Build_update_list Build_update_list: mov si, OFFSET Old_Image ; Ptr to old image mov di, Image_ptr ; Ptr to new image mov cx, Number_of_chars_on_PC_screen ; The number of words to compare sub bx, bx ; Clear count of update items test Screen_flags, Force_screen_update ; Want to do all locations? mov ax, Alloc_ds ; Pick up ptr to allocated segment mov ds, ax ; Make ds point to it ASSUME ds:Alloc jz BUL_Top ; No, go do it minimally BUL_Zap: add si, 2 ; Bump the pointers add di, 2 call BUL_Do_difference ; Do the next character dec cx ; Decr counter jcxz BUL_Bot ; No more to do jmp BUL_Zap ; Go update the next character BUL_Top: repe cmpsw ; Are the old and new words the same? je BUL_Bot ; This batch was all equal, done here call BUL_Do_difference ; Deal with the difference jcxz BUL_Bot ; If no more to do, stop now jmp BUL_Top ; Go check the next pair of locations ; Hit a difference, deal with it BUL_Do_difference: mov dx, si ; Copy source offset to dx mov ax, 2 + OFFSET Old_Image ; Pick up addr of buffer plus 2 sub dx, ax ; Get distance from start of buffer mov Update_list[bx], dx ; Store offset ptr in update list mov ax, es:-2[di] ; Pick up the new display value mov Update_list + 2[bx], ax ; Store it in the update list mov -2[si], ax ; Store it in the old image screen too add bx, 4 ; Move to next item ret ; Done here BUL_Bot: mov ax, Datas_ds ; Pick up ptr to normal segment mov ds, ax ; Make ds point to it ASSUME ds:Datas shr bx, 1 ; Convert bytes to items shr bx, 1 ; (divide by four) mov Number_of_update_items, bx ; Store the updated count of items ret ; Done here PUBLIC Update_nondisplayed_page Update_nondisplayed_page: push es ; Save es push WORD PTR Number_of_update_items ; Save count of items mov bl, 1 ; Get a one sub bl, Which_page ; Get number of page we ARE NOT on sub bh, bh ; Zero top half of word shl bx, 1 ; Double for word offset mov ax, Video_page_addresses[bx] ; Pick up address of screen memory mov es, ax ; Set es to point to the screen buffer mov bx, OFFSET Update_list ; Get ptr to update list mov cx, Number_of_update_items ; Get count of items to update mov dx, CRT_status_port ; Pick up status port number mov ax, Alloc_ds ; Pick up ptr to allocated segment mov ds, ax ; Install it in ds ASSUME ds:Alloc mov di, [bx] ; Get next address to update mov bp, 2[bx] ; Get value to store UNP_1: in al, dx ; Get status shr al, 1 ; Shift lower order bit into carry flag jc UNP_1 ; In retrace, wait until not retrace cli ; Ints off for this next loop UNP_2: in al, dx ; Get status shr al, 1 ; Shift lower order bit into carry flag jnc UNP_2 ; Not in retrace, wait until we are ; This update is for a single item, takes place during horizontal retrace xchg ax, bp ; Get back the char we need, save CRT flags stosw ; Store the new value sti ; Ints back on now add bx, 4 ; Move on to next item in list mov di, [bx] ; Get next address to update in al, dx ; Get status again test al, Vertical_retrace ; Test vertical retrace bit jnz UNP_Setup ; If in vertical retrace, take advantage of it mov bp, 2[bx] ; Get value to store loop UNP_1 ; Go do the next jmp SHORT UNP_Done ; Did them all, go finish up UNP_Setup: mov ax, 2[bx] ; Get value to store dec cx ; Decr to account for char we just did mov Number_of_update_items, cx ; First store the current count cmp cx, Max_number_of_items ; Too many? jle UNP_Ok ; No, this count is ok mov cx, Max_number_of_items ; Load up the maximum UNP_Ok: sub Number_of_update_items, cx ; Back off count by this many PUBLIC UNP_Inner ; This update is for a group of items, takes place during vertical retrace UNP_Inner: stosw ; Store the new value add bx, 4 ; Move on to next item in list mov di, [bx] ; Get next address to update mov ax, 2[bx] ; Get value to store loop UNP_Inner ; Go do the next mov bp, ax ; Save the char for later mov cx, Number_of_update_items ; Get number of items remaining or cx, cx ; Any left? jnz UNP_1 ; Still some left, go do them UNP_Done: mov ax, Datas_ds ; Ptr to normal data segment mov ds, ax ; Reinstall as ds ASSUME ds:Datas pop WORD PTR Number_of_update_items ; Get back count of item list pop es ; Get back saved es ret ; Done here ASSUME es:Datas Put_Screen ENDP ; Force all modes to be 80 by 25 text modes on IBM Color Graphics Adapter PUBLIC Enter_text_mode, Mode_Change_Table Enter_text_mode: mov ah, 15 ; Code to get current video mode int Screen ; Check it out cmp al, 10h ; Too high? ja ETM_Mode_ok ; Yeah, ignore it mov dh, al ; Save for a minute mov bx, OFFSET Mode_change_table ; Address of translation table xlat ; Change modes cmp dh, al ; Different? je ETM_Mode_ok ; Same thing, don't blink display push ax ; Save reg call Blank_current_page ; Clear out the graphic page pop ax ; Restore reg mov ah, 0 ; Code to set mode int Screen ; Set it up call Blank_current_page ; Clear out the text page ETM_Mode_ok: ret ; Done here PUBLIC Set_pointers Set_pointers PROC mov al, Row ; Pick up current row number mov bl, 2*Number_of_columns ; Size of a line mul bl ; Multiply it out add ax, OFFSET Screen_Image ; Add address of buffer mov Line_Ptr, ax ; Store line pointer mov bl, Column ; Pick up current column number sub bh, bh ; Clear high order half of word shl bx, 1 ; Shift left by 1 to double add ax, bx ; Add in the column offset mov Char_Ptr, ax ; Store character pointer or Screen_flags, Position_changed ; Flag screen change ret ; Done here Set_pointers ENDP ; Reset_esc_seq_reading -- Used to reset reading of Escape sequence Reset_esc_seq_reading PROC call Flush_esc_buffer ; First erase old escape buffer mov al, Esc ; Get an escape jmp Store_char_in_esc_buffer ; Store it as the start of a new esc seq Reset_esc_seq_reading ENDP ; Send_str_to_port -- Send a string to the port ; call with ; si/ ptr to string to send, zero terminated (ASCIZ) Send_str_to_port PROC cld ; Forwards lodsb ; Get a char from the string or al, al ; Equal to zero? je SSTP_1 ; Yes, quit on a null push si ; Save just in case call Send_char_to_port ; Send this char to the port pop si ; Get back saved ptr jmp Send_str_to_port ; Go try for another char SSTP_1: ret ; Hit null, done Send_str_to_port ENDP ; Send a different string depending on some flag Send_depending PROC If_cursor_keys_mode_set: test VT100_status_flags, Cursor_keys_mode ; See which way it is jz Leave_alone ; Normal, done here inc dx inc dx ; Bump twice jmp SHORT Leave_alone ; Go finish up If_keypad_application_mode_set: ; First, see if we are in NUM LOCK or ARROW KEY state ... push ax ; Save ax mov ah, 2 int Kb ; Get current shift state mov bl, al ; Copy to bl and bl, NUM_State ; Mask out all but NUM_State mov cl, 4 ; This many shr bl, cl ; Shift NUM_State into Shift state's position xor bl, al ; Keep a 1 if states are different pop ax ; Restore saved reg test bl, Left_Shift ; Are we effectively in NUM_State? jnz IFK_NUM_State ; We are effectively in NUM_State, go do it ; In arrow key state, either beep or dispatch an arrow key mov bx, OFFSET Key_actions_2 ; Action table mov cx, Number_of_key_codes_2 ; Number of entries to check mov dx, OFFSET Key_codes_2 ; Scan code table to use mov di, dx ; Copy over addr of table cld ; Forwards repne scasw ; Search for word in table je IFK_Got_it ; Keep going if we found the key jmp Boop ; Else, just beep to say "meaningless key" IFK_Got_it: mov cx, di ; Copy to cx dec cx ; Drop by one word due to REPNE overshoot dec cx ; ... sub cx, dx ; Get index of char add bx, cx ; Add displacement to base of action table add bx, cx ; Double the offset add bx, cx ; Triple it (6 bytes instead of 2) lea dx, 2[bx] ; Get 2 + > into dx jmp WORD PTR [bx] ; Dispatch to the desired routine IFK_NUM_State: test VT100_status_flags, Keypad_application_mode ; See which way it is jz Leave_alone ; Normal, done here inc dx inc dx ; Bump twice Leave_alone: mov bx, dx ; Copy to bx mov si, [bx] ; Pick up the proper pointer jmp Send_str_to_port ; Ship the string out to the port ; User hit ALT F7, first set flag for program exit, then ... Leave_program: call Exit2 ; Set exit flag nop nop nop ; User hit F5 or ALT F5, set flag to say we are leaving the emulator ... Leave_emulator: mov Quit_emulator, 1 ; Set flag to 1 ret ; Done here ; User hit F6, call the Helper module Call_Helper: call Go_to_page_zero ; Be on page zero in display memory mov dx, Cursor_position ; Get current cursor pos mov ah, 2 ; Code to set cursor position mov bh, Which_page ; The current page int Screen ; Set cursor position call Helper ; Call the help module or Screen_flags, Changed ; Mark that screen needs help call Do_our_screen ; Update screen mov dx, Cursor_position ; Get current cursor pos mov ah, 2 ; Code to set cursor position mov bh, Which_page ; The current page int Screen ; Set cursor position ret ; Done here Send_depending ENDP ;*************************************************************************** ; *Utilities for decoding VT100 Esc sequences and acting on them VT100_Utilities PROC ; Stolen from BIOS ... Beep: mov al, 10110110b ; timer initialization out timer + 3, al mov ax, beldiv ; Bell divisor out timer + 2, al mov al, ah out timer + 2, al ; output divisor in al, bel_prt mov ah, al ; remember original value or al, 3 ; turn speaker on out bel_prt, al mov cx, 8888h outbe1: loop outbe1 ; wait a while mov al, ah out bel_prt, al ; turn bell off ret ; and return ; Same thing, but a lower note ... PUBLIC Boop Boop: mov ax, beldiv ; Pick up current value push ax ; Save it shl ax, 1 ; Slide value left mov beldiv, ax ; Store it in beldiv call Beep ; Use the other routine pop WORD PTR beldiv ; Get back the saved value ret ; Restart escape sequence processing (hit new Esc) Restart_esc_seq_processing: call Reset_esc_seq_reading ; Reset this guy ... jmp Do_Escape ; Done with this one, start a new Esc seq ; Here to add a character to the current line at the current position in ; the current rendition (as set up in Attribute_byte) ... Advances ; Column and Char_Ptr unless in rightmost column, in which case the ; character is added but the position is not changed. ; ; call with ; al/ character to be added PUBLIC Add_this_char Add_this_char: call Do_Graphic_Char ; If we are in graphic mode, substitute ; proper graphic character for this one push ax ; Save reg mov ax, Alloc_ds ; Make es point to mov es, ax ; the allocated segment ASSUME es:Alloc pop ax ; Restore reg mov bx, Char_Ptr ; Ptr to char mov es:[bx], al ; Store the char inc bx ; Move to attr mov ah, Attribute_Byte ; Current attr mov es:[bx], ah ; Store it too inc bx ; Bump ptr over it mov ax, Datas_ds ; Make es point to mov es, ax ; the normal segment ASSUME es:Datas mov al, Column ; Current column cmp al, Highest_column ; In last column already? jne New_column ; No, go update ptrs ; We were in the rightmost column, and we just wrote a character ... ; So, if we are in AutoWrap mode then we now need to do a CrLf cmp AutoWrap, 0 ; In AutoWrap mode? jz Say_changed ; No, leave alone mov Column, 0 ; Simulate a Cr, the column is now zero mov ax, Line_Ptr ; Get line ptr mov Char_Ptr, ax ; Store as new char ptr call Move_down_scroll_up ; Call the linefeed routine jmp SHORT Say_changed ; Go finish up New_column: mov Char_Ptr, bx ; All ok, store new char ptr inc Column ; Bump column number too, fall through to ... ; Say that screen has changed, then return Say_changed: or Screen_flags, Screen_changed ; Flag screen change ret ; Done here ; If the Graphi bit is on in our rendition, and the character is in the ; graphic character range, then substitute the PC's closest graphic ... Do_Graphic_Char: test Rendition, Graphi ; Are we in graphics mode? jz DGC_done ; No cmp al, 95 ; Check bottom of range jb DGC_done ; Too low to worry about cmp al, 126 ; Check top of range ja DGC_done ; Too high to worry about sub al, 95 ; Pull character into range mov bx, OFFSET Graphic_translation_table ; Addr of table to use xlat Graphic_translation_table ; Translate char into graphic DGC_done: ret ; Done here ; Set new Tty graphic bit based on Char_set, G0_set and G1_set NewAtr: mov al, NOT Graphi ; Get graphic bit and Rendition, al ; Turn off bit for starters mov al, G0_set ; Assume set zero mov ah, Char_set ; Get which set we are in cmp ah, 0 ; Set 0? je Nw_1 ; Yes ... mov al, G1_set ; No, use set one Nw_1: or Rendition, al ; Set the bit accordingly ret ; and return ; Move_down_scroll_up -- Go to or create a new line, moving downwards PUBLIC Move_down_scroll_up Move_down_scroll_up: mov al, Row ; Which row we are on cmp al, Bottom_margin ; Are we on the bottom of the scrolling reg? je MDSU_2 ; Yes, go deal with the scroll cmp al, Highest_row ; Not bottom of scr reg, bot of screen? je MDSU_1 ; Yes, go deal with non-case inc Row ; Bump row number jmp Set_pointers ; Get new ptrs, then done here MDSU_1: ret ; Sequence to move down, can't ... ; Scroll region up by one line ... MDSU_2: mov bl, Number_of_columns * 2 ; Number of bytes in a column mov al, bl ; Create a working copy mul Top_margin ; Multiply by row number of top margin add ax, OFFSET Screen_image ; Address screen mov di, ax ; Install as destination sub bh, bh ; Clear high half of word add ax, bx ; Add in another line's worth mov si, ax ; Install as source mov al, Bottom_margin ; Start with bottom margin row number sub al, Top_margin ; Number of lines to move mul bl ; Multiply by number of bytes in a row shr ax, 1 ; Convert to number of words mov cx, ax ; Install as repeat count mov ax, Alloc_ds ; Set up ptrs to allocated segment in mov ds, ax ; ds and mov es, ax ; es ASSUME ds:Alloc, es:Alloc cld ; Move forwards as we copy rep movsw ; Scroll the region up by one row mov cx, Number_of_columns ; Number of words in a row mov ax, 0720h ; Normal spaces rep stosw ; Fill the new line with blanks mov ax, Datas_ds ; Return segment ptrs to normal mov ds, ax mov es, ax ASSUME ds:Datas, es:Datas inc Number_of_scrolls_done ; Keep track for early quit or Screen_flags, Screen_changed ; Flag screen change ret ; Done here ; Move_up_scroll_down -- Go to or create a new line, moving upwards PUBLIC Move_up_scroll_down Move_up_scroll_down: mov al, Row ; The row we are on cmp al, Top_margin ; Are we in the top row of the scrolling reg? je MUSD_2 ; Yes, go deal with the scroll or al, al ; Already as high as we can go? jz MUSD_1 ; Yes, go deal with non-case dec Row ; Bump row number jmp Set_pointers ; Get new ptrs, then done here MUSD_1: ret ; Sequence to move up, can't ... ; Scroll region down by one line ... MUSD_2: mov bl, Number_of_columns * 2 ; Number of bytes in a column mov al, bl ; Create a working copy mul Bottom_margin ; Multiply by row number of bottom margin add ax, OFFSET Screen_image + (Highest_column * 2) ; Address last word in the line mov di, ax ; Install as destination sub bh, bh ; Clear high half of word sub ax, bx ; Subtract out another line's worth mov si, ax ; Install as source mov al, Bottom_margin ; Start with bottom margin row number sub al, Top_margin ; Number of lines to move mul bl ; Multiply by number of bytes in a row shr ax, 1 ; Convert to number of words mov cx, ax ; Install as repeat count mov ax, Alloc_ds ; Set up ptrs to allocated segment in mov ds, ax ; ds and mov es, ax ; es ASSUME ds:Alloc, es:Alloc std ; Move backwards as we copy rep movsw ; Scroll the region down by one row mov cx, Number_of_columns ; Number of words in a row mov ax, 0720h ; Normal spaces rep stosw ; Fill the new line with blanks mov ax, Datas_ds ; Return segment ptrs to normal mov ds, ax mov es, ax ASSUME ds:Datas, es:Datas inc Number_of_scrolls_done ; Keep track for early quit or Screen_flags, Screen_changed ; Flag screen change ret ; Done here ; Return skip ... RSkp PROC pop bx ; Pick up return address add bx, 3 ; Bump up by 3 push bx ; Back on the stack ret ; Then just return ... RSkp ENDP ; Set attribute byte based on rendition PUBLIC Set_attribute_byte Set_attribute_byte: mov al, Rendition ; Load the current VT100 graphic rendition and al, Bold+Underscored+Blinking+Inverted ; Keep only these bits mov bx, OFFSET Color_attributes ; Addr of rend-to-attribute table xlat Color_attributes ; Translate rendition to attribute mov Attribute_byte, al ; Set up attribute ret ; Done here ; call PullCh ; Used to get the backed-up char, if any, or a character from the ; escape buffer, if any, else from the port, if any ... returns skip ; if a character was found ... ; ; Returns ... ; +1 if no char to be found ; +2 if found a char (from either buffer), char is in al PUBLIC PullCh PullCh PROC mov al, Backed_up_char ; Get the backed-up char cmp al, 0 ; Did we get one? je PCH_1 ; No, get a new char mov Backed_up_char, 0 ; Clear char for next time RetSkp ; Go RetSkp PCH_1: mov bx, EBufGt ; Escape buf getter ptr cmp bx, EBufPt ; Compare with putter ptr je PCH_2 ; If same, no chars held there mov al, [bx] ; Pick up the char inc EBufGt ; Bump the ptr jmp SHORT PCH_End ; Go finish up PCH_2: call Get_char_from_port ; See if another char is available jnc PCH_Ret ; If no char, just return ; We just got a char from the host, so if we are running under control of a ; script we need to let the script processor know what that character is. cmp Script_processor, 0 ; Is there a script processor to run? je PCH_No_script ; No mov ah, 1 ; Code for "character" entry call Script_processor ; Let it do its thing PCH_No_script: test flags, capt ; capturing output? jz PCH_4 ; no, forget this part push ax ; save char call captrtn ; give it captured character pop ax ; restore character and keep going PCH_4: or al, al ; Is the char a null? jz PCH_2 ; Got a null, go try again cmp al, DEL ; Or delete? je PCH_2 ; Yes, ignore this char and try again cmp al, 'X'-100q ; Control-X? je PCH_Abort ; Yes, blow out esc seq cmp al, 'Z'-100q ; Control-Z? je PCH_Abort ; Yes, blow out esc seq call Store_char_in_esc_buffer ; Store the char in the escape buffer PCH_End: mov Previous_char, al ; Save the char RetSkp ; Return with the char PCH_Abort: mov al, 177 ; Char decimal 177 is the IBM display char ; most closely corresponding to VT100's ; error blot ... half dots on ... call Add_this_char ; Write the character to the screen jmp Abort_esc_sequence ; ^X and ^Z will halt any esc sequence ... PCH_Ret: ret ; No char to get, just return PullCh ENDP ; call Store_char_in_esc_buffer ; Store a new character in the escape buffer for later rereading PUBLIC Store_char_in_esc_buffer Store_char_in_esc_buffer PROC mov ah, EBufCt ; Pick up count of chars so far cmp ah, EBufLn-1 ; Is the buffer full? jge Stc_E ; Full, give up mov di, EBufPt ; Get putter ptr cld ; Forwards stosb ; Store char in buffer mov EBufPt, di ; Store updated putter ptr mov EBufGt, di ; Store as getter ptr also to prevent rereading inc EBufCt ; Bump count of chars in buffer Stc_E: ret Store_char_in_esc_buffer ENDP ; call UnPull ; Used to back up the pull sequence by one character UnPull PROC mov al, Previous_char ; Pick up the last char mov Backed_up_char, al ; Save it as the backed-up char ret ; and return UnPull ENDP ; PulNum -- Use PullCh to read a number ; call PulNum ; Used to read in a number using PullCh ; Returns ... ; +1 if hit eof on call to PullCh ; +2 if got a number, number in dl PUBLIC PulNum PulNum: sub dl, dl ; First clear dl PN_1: call PullCh ; Get a char RetNop ; No cmp al, '0' ; Lower than 0? jl PN_2 ; Yes, done here cmp al, '9' ; Higher than 9? jg PN_2 ; Yes, done here sub al, '0' ; Make ASCII digit into decimal xchg al, dl ; Swap al and dl mov bl, 10 ; Get a ten mul bl ; Multiply old number by ten add dl, al ; Add in the new digit jmp PN_1 ; Go get another char PN_2: call UnPull ; Release the char we just read RetSkp ; Tell caller he has a number ; Get_numeric_params -- Get Vt100 Escape sequence numbers ... PUBLIC Get_numeric_params Get_numeric_params: sub ax, ax ; Get a zero mov NumCnt, al ; Clear count mov NumPtr, al ; and ptr GNP_1: call PulNum ; Try to get a number RetNop ; No call PullCh ; Get the number's terminating char RetNop ; No inc NumCnt ; Bump count of args mov bl, NumCnt ; Copy to bx sub bh, bh ; Clear high half mov Nums-1[bx], dl ; Store the number we got from PulNum cmp al, ';' ; Was terminator a semi-colon? je GNP_1 ; Yes, go get another argument ... call UnPull ; Un-get char so the other routine can get it RetSkp ; and return nicely ... ; Get_next_number -- Get a single number from previously stored numbers PUBLIC Get_next_number Get_next_number: inc NumPtr ; Bump ptr mov bl, NumPtr ; Get ptr into low half cmp bl, NumCnt ; Off end of list? jle GNN_1 ; No, keep going ret ; No more, done here GNN_1: sub bh, bh ; Zero high half mov al, Nums-1[bx] ; Get the next number RetSkp ; Tell caller he has a number ... ; Get a single numeric parameter, default is zero if no parameters. PUBLIC Default_zero Default_zero: call Get_next_number ; Pick up next arg, if any jmp SHORT D0_1 ; No more, go take default nop ; 3 bytes ... ret ; Got one, use it D0_1: sub al, al ; Default to 0 ret ; Done here ; Get a single numeric parameter, default is one (if param is zero ; or if there is no parameter). PUBLIC Default_one Default_one: call Get_next_number ; Pick up next arg, if any jmp SHORT D1_1 ; No more, go take default nop ; 3 bytes ... or al, al ; Is it zero? jnz D1_2 ; No, go use it D1_1: mov al, 1 ; Default to 1 D1_2: ret ; Done here ; Set up to run a script processor at selected times, or clear it with zero Set_up_script_processor_PC: mov Script_Processor, ax ; Set this up, or zero it out ret ; That was easy ; Convert an integer into its decimal ASCII representation in memory ; Call with ; ax/ number to be converted ; di/ ptr to area to write the string Nout: sub dx, dx ; Make a zero mov bx, 10 ; Get a ten div bx ; Divide ax by ten push dx ; Save remainder or ax, ax ; Quotient zero? jz Nout_1 ; Yes, don't dig deeper call Nout ; Recursively get the rest of the number Nout_1: pop ax ; Get back saved digit add al, '0' ; Convert this digit stosb ; Put it into the output area ret ; Done at this level VT100_Utilities ENDP ;****************************************************************************** ; *Character handling routines ... ; Some control characters ... Ctrl_Ch PROC ; Bell Do_Bell: call Beep ; Beep the bell jmp Done_with_char ; Tab Do_Tab: mov al, Column ; Get current column # or al, 7 ; Go to next tab stop inc al ; Fancy trick ... cmp al, Highest_column ; Too high? jle Dot_2 ; No mov al, Highest_column ; Truncate to width ... Dot_2: mov Column, al ; Save new column number jmp Set_ptrs_then_done ; Backspace Do_Bs: dec Column ; Decr the column jns Dob_1 ; Is it now negative? mov Column, 0 ; Set col to zero Dob_1: jmp Set_ptrs_then_done ; Set up new pointers ; Carriage return Do_Cr: mov Column, 0 ; Column is now zero mov ax, Line_Ptr ; Get line ptr mov Char_Ptr, ax ; Store as new char ptr or Screen_flags, Position_changed ; Flag screen change jmp Done_with_char ; Shift out Do_SO: mov al, 1 ; Get a one jmp SHORT DoSI_1 ; Join common code ; Shift in Do_SI: sub al, al ; Clear al DoSI_1: mov Char_set, al ; Put us in the G0 charset call NewAtr ; Go set up a new attr based on this jmp Done_with_char Ctrl_Ch ENDP ; Escape ... PUBLIC Do_escape Do_Escape PROC call PullCh ; Try to get a char jmp No_more_chars ; No more chars, try again later mov bx, OFFSET ActTb2 ; Action table mov cx, NChrTb2 ; Number of chars to check mov dx, OFFSET ChrTb2 ; The character table jmp Check_Table ; Go dispatch to the right routine Do_Escape ENDP ; Hit Escape left square bracket ($[), keep checking ... PUBLIC Do_left_square Do_left_square PROC call PullCh ; Look at the next char jmp No_more_chars ; No more chars, try again later or VT100_status_flags, Private_esc_seq ; Assume private esc sequence cmp al, '?' ; Hit qmark? je DSQ_1 ; Yes, "eat" it call UnPull ; Not qmark, get it back for Get_numeric_params and VT100_status_flags, NOT Private_esc_seq ; Not private esc sequence DSQ_1: call Get_numeric_params ; Get numeric parameters, if any jmp No_more_chars ; Bomb call PullCh ; Get the closing char of the sequence jmp No_more_chars ; No more chars, try again later test VT100_status_flags, Printer_controller_mode jz DSQ_2 ; Not in funny mode, continue as usual cmp al, 'i' ; Only seq that gets us out of funny mode .. je DSQ_2 ; That's it, go run with it jmp Done_with_char ; In funny mode, not seq to leave it, ignore DSQ_2: mov bx, OFFSET ActTb3 ; Action table mov cx, NChrTb3 ; Number of chars to check mov dx, OFFSET ChrTb3 ; The character table jmp Check_Table ; Go dispatch to the right routine Do_left_square ENDP ; Saw Escape left paren ... Do_left_paren PROC call PullCh ; Get next char jmp No_more_chars ; No more chars, try again later test VT100_status_flags, Printer_controller_mode jz LFP_1 ; Not in funny mode, continue as usual jmp Done_with_char ; In funny mode, not seq to leave it, ignore LFP_1: mov bx, OFFSET ActTb4 ; Action table mov cx, NChrTb4 ; Number of chars to check mov dx, OFFSET ChrTb4 ; The character table jmp Check_Table ; Go dispatch to the right routine Lfp_B: sub al, al ; Get a zero jmp SHORT Lfp_Mr ; Join common code Lfp_0: mov al, Graphi ; Get graphic bit Lfp_Mr: mov G0_set, al ; Set G0 charset to whatever call NewAtr ; Set it up jmp Done_with_char Do_left_paren ENDP ; Saw Escape right paren ... Do_right_paren PROC call PullCh ; Get next char jmp No_more_chars ; No more chars, try again later test VT100_status_flags, Printer_controller_mode jz RTP_1 ; Not in funny mode, continue as usual jmp Done_with_char ; In funny mode, not seq to leave it, ignore RTP_1: mov bx, OFFSET ActTb5 ; Action table mov cx, NChrTb5 ; Number of chars to check mov dx, OFFSET ChrTb5 ; The character table jmp Check_Table ; Go dispatch to the right routine Rtp_B: sub al, al ; Get a zero jmp SHORT Rtp_Mr ; Join common code Rtp_0: mov al, Graphi ; Get graphic bit Rtp_Mr: mov G1_set, al ; Set G1 charset to whatever call NewAtr ; Set it up jmp Done_with_char Do_right_paren ENDP ; Saw Escape pound sign ... Do_pound_sign PROC call PullCh ; Get next char jmp No_more_chars ; No more chars, try again later cmp al, Esc ; Escape? je DOP_1 ; Yes, do the decent thing jmp Done_with_char ; These are all not supported yet ... DOP_1: jmp Restart_esc_seq_processing ; Go get a new escape sequence Do_pound_sign ENDP ;*************************************************************************** ; *VT100 Escape Sequences as described in VT100 User Guide* ... VT100_Routines PROC ; Cursor Backward CUB: call Default_one ; Get argument, default to one mov ah, Column ; Get current column sub ah, al ; Back it up by requested # columns jns CUB_1 ; Is it now negative? sub ah, ah ; Make a zero CUB_1: mov Column, ah ; Set column to new value jmp SHORT Set_ptrs_then_done ; Cursor Down CUD: call Default_one ; Pick up the arg, default to 1 mov ah, Row ; Get current row add ah, al ; Add in requested # rows mov al, Highest_row ; Maximum offset for row number cmp ah, al ; Is new row number too high? jbe CUD_1 ; All is OK ... mov ah, al ; Use the maximum CUD_1: mov Row, ah ; Update the row jmp SHORT Set_ptrs_then_done ; Cursor Forward CUF: call Default_one ; Pick up the arg, default to 1 mov ah, Column ; Get current column add ah, al ; Add in requested # columns mov al, Highest_column ; Maximum offset cmp ah, al ; Is new column number too high? jbe CUF_1 ; All is OK ... mov ah, al ; Use the maximum CUF_1: mov Column, ah ; Update the column jmp SHORT Set_ptrs_then_done ; Cursor Position PUBLIC CUP CUP: call Default_one ; Pick up the first arg, default to 1 dec al ; Make it an offset jns CUP_1 ; Not negative, move on sub al, al ; Make it zero CUP_1: mov ah, Highest_row ; Maximum offset cmp al, ah ; Is requested row number too high? jbe CUP_2 ; In range, move on mov al, ah ; Use the maximum CUP_2: mov NewRow, al ; Save as new row number call Default_one ; Pick up the second arg, default to 1 dec al ; Make it an offset jns CUP_3 ; Not negative, move on sub al, al ; Make it zero CUP_3: mov ah, Highest_column ; Maximum offset cmp al, ah ; Is requested column number too high? jbe CUP_4 ; In range, move on mov al, ah ; Use the maximum CUP_4: mov Column, al ; Save as new column number mov al, NewRow ; Load up the requested row number mov Row, al ; Store it jmp SHORT Set_ptrs_then_done ; Cursor Up CUU: call Default_one ; Get argument, default to one mov ah, Row ; Get current row sub ah, al ; Back it up by requested # rows jns CUU_1 ; Is it now negative? sub ah, ah ; Make a zero CUU_1: mov Row, ah ; Set row to new value ; Fall thru to Set_ptrs_then_done ... ; Common finish up point for cursor positioning routines Set_ptrs_then_done: call Set_pointers ; Set up new pointers jmp Done_with_char ; Then are are done ; Device Attributes DA: test VT100_status_flags, Private_esc_seq ; Include a "?" ? jnz DA_ignore ; Yes, don't answer it call Default_zero ; Get parameter, if any or al, al ; Is it zero? jnz DA_ignore ; No, just ignore it mov si, OFFSET DA_Str ; What to send ... call Send_str_to_port ; Send the string to the port DA_ignore: jmp Done_with_char ; DEC (private) terminal ID DECID: jmp DA ; Same thing ; DEC (private) Keypad Application Mode DECKPAM: or VT100_status_flags, Keypad_application_mode ; Turn on the flag jmp Done_with_char ; DEC (private) Keypad Numeric Mode DECKPNM: and VT100_status_flags, NOT Keypad_application_mode ; Turn off the flag jmp Done_with_char ; DEC (private) Load LEDs PUBLIC DECLL DECLL: call Default_zero ; Get the first parameter, default zero DECLL_Loop: cmp al, 4 ; Highest value we have code for jg DECLL_Ignore ; Can't handle it cbw ; Blow up to full word shl ax, 1 ; Double offset for words mov bx, ax ; Copy to bx jmp DECLL_Table[bx] ; Dispatch thru table to correct routine DECLL_0: mov al, NOT (LED1 + LED2 + LED3 + LED4) and LEDs, al ; Turn off those bits ... jmp SHORT DECLL_Ignore ; Done with this part DECLL_1: mov al, LED1 ; Flag for LED # 1 jmp SHORT DECLL_Set ; Go set the bit DECLL_2: mov al, LED2 ; Flag for LED # 2 jmp SHORT DECLL_Set ; Go set the bit DECLL_3: mov al, LED3 ; Flag for LED # 3 jmp SHORT DECLL_Set ; Go set the bit DECLL_4: mov al, LED4 ; Flag for LED # 4 DECLL_Set: or LEDs, al ; Set the bit DECLL_Ignore: call Get_next_number ; Try to get another number jmp SHORT DECLL_Done ; No such luck ... nop ; 3 bytes jmp DECLL_Loop ; Got one, go do something with it DECLL_Done: mov Prev_shift_state, 0FFh ; Force set up of new mode line jmp Done_with_char ; Done with this one ; DEC (private) Restore Cursor DECRC: mov al, Save_char_set ; Pick up which set we were in mov Char_set, al ; Restore it mov al, Save_G0_set ; Pick up saved char set mov G0_set, al ; Restore it mov al, Save_G1_set ; Pick up saved char set mov G1_set, al ; Restore it mov al, Save_rendition ; Pick up rendition mov Rendition, al ; Restore it mov ax, Save_position ; Pick up old cursor position mov Cursor_position, ax ; Restore it jmp Set_ptrs_then_done ; Get a new ptr to the line we are on, done ; DEC (private) Save Cursor DECSC: mov al, Char_set ; Pick this up mov Save_char_set, al ; Save it mov al, G0_set ; Pick this up mov Save_G0_set, al ; Save it mov al, G1_set ; Pick this up mov Save_G1_set, al ; Save it mov al, Rendition ; Pick this up mov Save_rendition, al ; Save it mov ax, Cursor_position ; Pick this up mov Save_position, ax ; Save it jmp Set_ptrs_then_done ; Get a new ptr to the line we are on, done ; DEC (private) Set Top and Bottom Margins PUBLIC DECSTBM DECSTBM: call Default_one ; Try to get the top margin, def is 1 dec al ; Change to offset mov Top_margin, al ; Store it call Default_zero ; Try to get bottom margin, use zero for now or al, al ; Was it zero? jnz DST_1 ; No, use what we got mov al, Number_of_rows ; Default to number of rows on screen DST_1: dec al ; Change to offset mov Bottom_margin, al ; Store the margin we got ; This Esc sequence puts us in the home position ... sub ax, ax ; Get a zero mov Cursor_position, ax ; Move to upper left corner jmp Set_ptrs_then_done ; Set pointers, done with this char ; Device Status Report DSR: call Get_next_number ; Get a number jmp SHORT DSR_1 ; If no number, default to 5 nop ; 3 bytes cmp al, 5 ; Is it five? jne DSR_2 ; No, see what it is DSR_1: mov si, OFFSET DSR_str ; Load up ptr to string we want to send call Send_str_to_port ; Send it jmp Done_with_char DSR_2: cmp al, 6 ; Is it six? jne DSR_End ; No, then don't know what to do anyway mov si, OFFSET CPR_str ; Load ptr to string call Send_str_to_port ; Send it mov al, Row ; Pick up row number cbw ; Convert to word inc ax ; Row and col numbers start at 1, not zero mov di, OFFSET LoWork ; Ptr to work area push di ; Save it call Nout ; Write number to work area sub al, al ; Make a zero stosb ; Tie off the string pop si ; Get back ptr into si call Send_str_to_port ; Send the string along mov al, ';' ; A semicolon call Send_char_to_port ; Type it mov al, Column ; Pick up column number cbw ; Convert to word inc ax ; Row and col numbers start at 1, not zero mov di, OFFSET LoWork ; Ptr to work area push di ; Save it call Nout ; Write number to work area sub al, al ; Make a zero stosb ; Tie off the string pop si ; Get back ptr into si call Send_str_to_port ; Send the string along mov al, 'R' ; A capital R call Send_char_to_port ; Send to port, return from there DSR_End: jmp Done_with_char ; Can't deal with it ... ; Erase in Display PUBLIC ED ED: call Default_zero ; Get a param cmp al, 17 ; Is it our "magic" value? je ED_Magic ; It is ... cmp al, 18 ; Code to identify ourselves (non-VT100)? je ED_Identify ; It is ... cmp al, 19 ; Code to quit to command mode? je ED_Command ; It is ... cmp al, 20 ; Code to quit to DOS? je ED_Exit_to_DOS ; It is ... cmp al, 21 ; Code to preload a command? je ED_Preload ; It is ... cmp al, 2 ; Is it too high? jg ED_ignore ; Yes ... cbw ; Blow up to full word shl ax, 1 ; Double offset for words mov bx, ax ; Copy to bx jmp ED_Table[bx] ; Dispatch thru table to correct routine ED_Magic: call Go_to_page_zero ; Go to base page call Enter_Server ; Jump suddenly into Server mode nop ; Skips on success nop nop call SerIni_PC ; In case port got turned off or Screen_flags, Force_screen_update ; Update the world call Do_our_screen ; Restore our screen jmp SHORT ED_ignore ED_Identify: mov si, OFFSET Ident_str ; What to send ... call Send_str_to_port ; Send the string to the port jmp SHORT ED_ignore ED_Command: call Leave_emulator jmp SHORT ED_ignore ED_Exit_to_DOS: call Leave_program jmp SHORT ED_ignore ED_Preload: sub cx, cx ; Zero characters preloaded so far mov di, OFFSET Preload_buffer ; Point at the start of our buffer cld ; Forwards ED_ploop: push di ; Save register just in case call PullCh ; Get a new character jmp ED_p_no_more ; No such luck pop di ; Restore register cmp al, Esc ; Hit an escape? je ED_pdone ; Hit the end, don't store Esc cmp cx, 100 ; Got too many chars? jae ED_pdone ; Don't take too many stosb ; Store char in our buffer inc cx ; Count the character mov Preload_flag, 0FFh ; Flag that there is a preloaded command jmp ED_ploop ; Do more ED_p_no_more: pop di ; Fix up stack jmp No_more_chars ; Try again later ED_pdone: sub al, al ; Make a zero stosb ; Tie off the string ED_ignore: jmp Done_with_char ED_0: mov cx, Number_of_chars_on_VT100_screen ; Start with this number mov al, Row ; Which row we are on mov bh, Number_of_columns ; Words per row mul bh ; Total number of words above us on screen mov bx, ax ; Copy to bx mov al, Column ; Current column cbw ; Make full word add ax, bx ; Total number of chars "above" us sub cx, ax ; Numbers of words still to clear mov di, Char_ptr ; Where to start clobbering cld ; Move forward jmp ED_z ; Go finish up ... ED_1: mov al, Row ; Which row we are on mov bh, Number_of_columns ; Words per row mul bh ; Total number of words above us on screen mov cx, ax ; Copy to cx mov al, Column ; Current column cbw ; Make full word inc ax ; Bump by one to include current position add cx, ax ; Total number of chars "above" us mov di, Char_ptr ; Where to start clobbering std ; Move backward jmp ED_z ; Go finish up ... ED_2: mov cx, Number_of_chars_on_VT100_screen ; Number of chars to lay down mov di, OFFSET Screen_Image ; Where to put them cld ; Move forward ED_z: mov ax, Alloc_ds ; Allocated segment mov es, ax ; Set up es ASSUME es:Alloc mov ax, 0720h ; Want normal spaces rep stosw ; Clear ... mov ax, Datas_ds ; Normal segment mov es, ax ; Set up es ASSUME es:Datas or Screen_flags, Screen_changed ; Flag screen change jmp ED_ignore ; Erase in Line PUBLIC EL EL: call Default_zero ; Get a param cmp al, 2 ; Is it too high? jg EL_ignore ; Yes ... cbw ; Blow up to full word shl ax, 1 ; Double offset for words mov bx, ax ; Copy to bx jmp EL_Table[bx] ; Dispatch thru table to correct routine EL_0: mov cx, Number_of_columns ; Start with this number mov al, Column ; Column number we are in cbw ; Make full word sub cx, ax ; Number of chars to our right mov di, Char_ptr ; Where to start clobbering cld ; Move forward jmp EL_z ; Go finish up ... EL_1: mov al, Column ; Which column we are in cbw ; Make full word inc ax ; Bump by one to include current position mov cx, ax ; Copy to cx mov di, Char_ptr ; Where to start clobbering std ; Move backward jmp EL_z ; Go finish up ... EL_2: mov cx, Number_of_columns ; Number of words to lay down mov di, Line_ptr ; Where to put the bytes cld ; Move forward EL_z: mov ax, Alloc_ds ; Allocated segment mov es, ax ; Set up es ASSUME es:Alloc mov ax, 0720h ; Get a space, normal attribute rep stosw ; Zap the buffer mov ax, Datas_ds ; Normal segment mov es, ax ; Set up es ASSUME es:Datas or Screen_flags, Screen_changed ; Flag screen change EL_ignore: jmp Done_with_char ; Horizontal and Vertical Position HVP: jmp CUP ; Same thing as cursor position ; Index IND: call Move_down_scroll_up ; Create a new line, the usual way ... jmp Done_with_char ; Media Copy PUBLIC MC MC: call Default_zero ; Get the first parameter, default is 0 MC_Loop: or al, al ; See if negative js MC_Ignore ; Negative, ignore it cmp al, 5 ; Highest value we have code for jg MC_Ignore ; Can't handle it cbw ; Blow up to full word shl ax, 1 ; Double offset for words mov bx, ax ; Copy to bx jmp MC_Table[bx] ; Dispatch thru table to correct routine ; Print screen, not implemented MC_0: jmp SHORT MC_Ignore ; Print cursor line, not implemented MC_1: jmp SHORT MC_Ignore ; Printer controller off, or Auto print off MC_4: test VT100_status_flags, Private_esc_seq jz MC4_x ; Not private and VT100_status_flags, NOT Auto_copy_mode ; Turn it off jmp SHORT MC_Ignore MC4_x: and VT100_status_flags, NOT Printer_controller_mode ; Turn it off jmp SHORT MC_Ignore ; Printer controller on, or Auto print on MC_5: test VT100_status_flags, Private_esc_seq jz MC5_x ; Not private or VT100_status_flags, Auto_copy_mode ; Turn it on jmp SHORT MC_Ignore MC5_x: or VT100_status_flags, Printer_controller_mode ; Turn it on MC_Ignore: mov Prev_shift_state, 0FFh ; Force set up of new mode line jmp Done_with_char ; Done with this one ; Next Line NEL: mov Column, 0 ; Clear the column offset jmp IND ; Join common code ; Reverse Index RI: call Move_up_scroll_down ; Create a new line, the other way ... jmp Done_with_char ; Reset Mode PUBLIC RM RM: call Default_zero ; Get the first parameter, default is 0 RM_1: cmp al, 1 ; One? jne RM_2 ; No and VT100_status_flags, NOT Cursor_keys_mode ; Turn off the bit RM_2: call Get_next_number ; Try to get another number jmp Done_with_char jmp RM_1 ; Got one, go do something with it ; Select Graphic Rendition PUBLIC SGR SGR: call Default_zero ; Get the first parameter, default zero SGR_Loop: or al, al ; See if negative js SGR_Ignore ; Negative, ignore it cmp al, 7 ; Highest value we have code for jg SGR_Ignore ; Can't handle it cbw ; Blow up to full word shl ax, 1 ; Double offset for words mov bx, ax ; Copy to bx jmp SGR_Table[bx] ; Dispatch thru table to correct routine SGR_0: mov al, NOT (Blinking + Bold + Inverted + Underscored) ; These renditions ... and Rendition, al ; Turn off those bits ... jmp SHORT SGR_Ignore ; Done with this part SGR_1: mov al, Bold ; Flag for bold jmp SHORT SGR_Set ; Go set the bit SGR_4: mov al, Underscored ; Flag for underscored jmp SHORT SGR_Set ; Go set the bit SGR_5: mov al, Blinking ; Flag for blinking jmp SHORT SGR_Set ; Go set the bit SGR_7: mov al, Inverted ; Flag for inverted SGR_Set: or Rendition, al ; Set the bit SGR_Ignore: call Get_next_number ; Try to get another number jmp SHORT SGR_Done ; No such luck ... nop ; 3 bytes jmp SGR_Loop ; Got one, go do something with it SGR_Done: call Set_attribute_byte ; Set attr based on rendition jmp Done_with_char ; Done with this one ; Set Mode PUBLIC SM SM: call Default_zero ; Get the first parameter, default is 0 SM_1: cmp al, 1 ; One? jne SM_2 ; No or VT100_status_flags, Cursor_keys_mode ; Turn on the bit SM_2: call Get_next_number ; Try to get another number jmp Done_with_char jmp SM_1 ; Got one, go do something with it VT100_Routines ENDP Code ENDS END