	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 + <addr of <addr of routine>> 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 + <addr of <addr of routine>> 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

