	PAGE 59, 132

	TITLE MsMenu -- Dynamic menu generator for Kermit and descendents

; Update 6 Jan 86

IF1
 %OUT >> Starting pass 1
ELSE
 %OUT >> Starting pass 2
ENDIF

	PUBLIC Menu, Quick_menu, Menu_file_name, Reload_menu_flag

;***************************************************************************
; *Definitions* ...

; Global defs

	INCLUDE MsDefs.H

Color_attr=30h			; Black on Cyan
Color_highlight=1Eh		; Yellow on Blue
Color_header_attr=74h		; Red on White

Mono_attr=70h			; Black on white
Mono_highlight=07h		; White on black

Upper_left=218			; Box drawing chars
Upper_right=191
Lower_left=192
Lower_right=217
Horizontal=196
Vertical=179
Left_tee=180
Right_tee=195

Screen	EQU 10h			; BIOS screen call
Kb	EQU 16h			; BIOS keyboard call

Number_of_columns EQU 80	; Width in characters
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

; Flags in Screen_flags (in MSYIBM)

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

; Scan codes for keys we have predefined ...

Escape_key EQU 1
Space_key EQU 57

F6_key	EQU 64
ALT_F6_key EQU 109

Left_key EQU 75
Right_key EQU 77

Up_key EQU 72
Down_key EQU 80

PgUp_key EQU 73
PgDn_key EQU 81

Home_key EQU 71
End_key	EQU 79

Return_key EQU 28

Seg_is_Alloc MACRO
	call Use_our_own_ds
    ASSUME ds:MAlloc, es:MAlloc
	ENDM

Seg_is_Datas MACRO
	call Back_to_normal_ds
    ASSUME ds:DataS, es:DataS
	ENDM

DataS	SEGMENT PUBLIC 'DataS'

	EXTRN Which_page:BYTE, Screen_flags:BYTE, Old_Image:WORD
	EXTRN Video_page_addresses:WORD, m7171:BYTE, In_menu_mode:BYTE
	EXTRN Force_mono:BYTE, m7171:BYTE, TakAdr:WORD, TakLev:BYTE

Menu_file_name LABEL BYTE
	Program_name
	DB '.MNU', 60 DUP (0)

Saved_mode_line DW Number_of_columns DUP (?) ; Saved image of Mode line

Characters LABEL BYTE
	DB	Home_key,	End_key,	PgUp_key
	DB	PgDn_key,	Up_key,		Down_key
	DB	Left_key,	Right_key,	Return_key
N_Characters EQU $-Characters
Actions	LABEL WORD
	DW 	Do_Home,	Do_End,		Do_PgUp
	DW	Do_PgDn,	Do_Up,		Do_Down
	DW	Do_Up,		Do_Down,	Do_Return
	DW	Do_other

No_memory DB '      ***  Not enough memory for MENU  --  Press any key to continue ***$'

Cr_str	DB Cr, '$'

Cant_find_file DB '        *****   Missing '
	Program_name
	DB '.MNU  --  Press any key to continue  *****$'

Ctrl_C_static_text DB 'Set Mode Command',0,0
Ctrl_C_stext_len EQU $ - Ctrl_C_static_text

Reload_menu_flag DB 0		; Flag that file needs reloading

DataS	ENDS


; Dummy segment for allocated memory

MAlloc	SEGMENT AT 0

Menu_items DW 20 DUP (?)	; Ptrs to texts for menu item names
Menu_data DW 20 DUP (?)		; Ptrs to data for menu items
Ctrl_C_ptr DW 1 DUP (?)		; Ptr to ^C data line
N_items	DW ?			; The number of items in our list

File_handle DW ?		; File handle for menu file
Which_item DW ?			; Item number we are on, from 0
Last_item DW ?			; Number of the last item
Width_counter DW ?		; Working counter of chars per line
Item_width DW ?			; Number of characters per item
Header_ptr DW ?			; OFFSET of Header text
Header_length DW ?		; Length of Header text
Menu_window_top DW ?		; Topmost row of menu
Menu_window_left DW ?		; Leftmost column of menu
Full_keystroke DW ?		; Scan code in high half, ASCII value in low

Which_line DW ?			; Current line number on screen

Our_cursor DW ?			; Our position
Our_Image DW Number_of_chars_on_PC_screen DUP (?) ; Our buffer

Menu_database DB 100*74 DUP (?)	; Where we store the Menu text
Menu_file_buffer DB 2048 DUP (?) ; Buffer for reading Menu file from disk

Unhighlight DB ?		; Normal attribute
Highlight DB ?			; Highlighted attribute
Header_attribute DB ?		; Header line attribute
Ctrl_C_data DB 20 DUP (?)	; Place to hold ^C text

End_MAlloc LABEL BYTE		; Ptr to end of what we need

MAlloc	ENDS


Code	SEGMENT PUBLIC

	EXTRN Put_Screen:NEAR, Get_Screen_Segment:NEAR, Boop:NEAR
	EXTRN Which_card:BYTE, SPath:NEAR, Check_table:NEAR
	EXTRN Get_memory_block:NEAR, PutErr_PC:NEAR, Screen_Image_ptr:DWORD
	EXTRN Mode_line_ptr:DWORD, Comnd:NEAR, Go_to_page_zero:NEAR
	EXTRN Save_command_mode_screen:NEAR, Restore_command_mode_screen:NEAR

    ASSUME CS:Code, DS:DataS, ES:DataS

Normal_ds DW ?			; Ptr back to ds on entry
MAlloc_ds DW ?			; Address of MAlloc segment

MFlags	DB 0			; Internal flags, kept in code segment
				;  for easy access

Bye_bye EQU 1			; Flag to leave
Been_here EQU 2			; Already did once-only initialization
Newlin	EQU 4			; We are at the start of a text line
New_item EQU 8			; We have seen an item but no data yet
In_header EQU 10h		; In a header definition

Menu	PROC

	mov ah, CmCfm
	call Comnd		; Get a confirm
	 jmp R

Quick_menu:
	call Do_menu		; Call routine to do the work
	 nop
	 nop
	 nop
	jmp RSkp		; We return skip no matter what

Do_menu:
	and cs:MFlags, NOT Bye_bye	; Flag that we aren't leaving yet

	test cs:MFlags, Been_here	; Been here before?
	 jz MNU_go		;  No ...

; We have been here before, so we already have memory allocated ...
;  Do we need to reload the database?

	cmp Reload_menu_flag, 0	; Is the flag to reload set?
	 jne MNU_reload		;  Yes, reload it

	call Use_our_own_ds	; Switch to Alloc segment
	call Install_screen	; Save, then blank the background
	jmp SHORT MNU_1		; Go use the database we already have set up

MNU_reload:
	mov Reload_menu_flag, 0	; Clear flag for next time
	mov ax, MAlloc_ds	; Reuse old memory
	jmp SHORT H_ok		; Join common code

MNU_go:

; First, get some memory to work in

; *** The silly assembler won't let me do this, saying
;	Constant was expected
;   SHR is an assembly-time operator according to the manual, so this
;    construct should be legit. 
;   Still a problem from IBM MASM 1.0 up to and including Microsoft MASM
;    version 4.0.  [tested jan 86] 

;	mov bx, (OFFSET End_MAlloc+15) SHR 4 ; Lowest location we don't use,
				;  round up to next higher paragraph,
				;  then convert to number of paragraphs

	mov bx, OFFSET End_MAlloc+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
	 jnc H_OK		;  Ok so far ...

	mov dx, OFFSET No_memory ; Complain
	jmp Do_mode_line_error	; Display noisy error message, wait for
				;  keystroke, then exit, restoring
				;  clobbered mode line

H_OK:	mov MAlloc_ds, ax	; Save the address of our block

	mov bx, ds		; Set up ptr back to ds
	mov Normal_ds, bx	; Save ptr back to old one
    Seg_is_Alloc		; Alloc segment
	call Build_Menu_database ; Build the database
	test cs:MFlags, Bye_bye	; Time to leave?
	 jz MNU_0		;  Not yet

; *** Properly speaking, we really ought to release the memory we
;	were just allocated, since we can't use it ... maybe later ***

	jmp Back_to_normal_ds	; Oh well ...

    ASSUME ds:MAlloc, es:MAlloc
MNU_0:	or cs:MFlags, Been_here	; Flag for later
	call Install_screen	; Set up to call Put_Screen
	add Item_width, 2	; Add in some padding for menu items

	mov ax, 78		; Determine the number of spaces to the
	sub ax, Item_width	;  left of the menu
	shr ax, 1		; Divide leftover by two
	mov Menu_window_left, ax ; Store the answer

	mov ax, 21		; Count lines outside window
	sub ax, N_items		; Subtract for items (4 others preallocated)
	shr ax, 1		; Divide total rows by 2
	mov Menu_window_top, ax	; Store the answer

MNU_1:	call Build_menu_box	; Build outline / background for Menu window

	call Fill_menu_frame	; Put menu items and header in frame

	mov Which_item, 0	; This is the one we want
	call Highlight_line	; Make it stand out

MNU_2:	call Do_the_screen	; Display this frame

	call Check_keystrokes	; See what to do next
	test cs:MFlags, Bye_bye	; Time to leave?
	 jz MNU_2		;  No, go update the frame

    Seg_is_Datas		; Datas segment
	call Set_up_macro	; Set up a macro to run when we get out
	call Go_to_page_zero	; Back to normal
	jmp Restore_command_mode_screen


	PUBLIC Set_up_macro

Set_up_macro:
	mov ax, MAlloc_ds	; MAlloc segment
	mov ds, ax		; Install in ds
    ASSUME ds:MAlloc	
	cld			; Forwards
	inc TakLev		; Bump take level
	add takadr, SIZE takinfo ; Address new take frame

	mov bx, Which_item	; Item number that was selected
	shl bx, 1		; Double for word offset
	mov si, Menu_data[bx]	; Pick up pointer to data for this item

	mov bx, es:TakAdr	; Get TAKE frame addr into bx
	mov BYTE PTR es:[bx].TakFcb, 0FFh ; Mark as a macro

	lea di, es:[bx].TakBuf	; Put it in the TAKE buffer
	sub cx, cx		; Clear counter

CPC_1:	lodsb			; Get a byte
	or al, al		; Null?
	 jne CPC_3

	mov al, Cr		; Load up a Cr for end-of-line
	stosb			; Store it
	inc cx			; Count this char

	lodsb			; Is there another data line?
	cmp al, "+"		; Well?
	 jne CPC_4		;  No, quit

	jmp CPC_1		; Go do the next line of data

CPC_3:	stosb			; Deposit char
	inc cx			; Count this char
	or al, al		; Hit end?
	 jne CPC_1		;  No
	
	PUBLIC CPC_4, CPC_1, CPC_ret, CPC_3

CPC_4:	lea ax, es:[bx].takbuf	; Pick up ptr to start of TAKE buffer
	mov es:[bx].takptr, ax	; Init buffer ptr
	mov es:[bx].takchl, cl	; Chars remaining
	mov es:[bx].takcnt, cx	;  and all chars
	mov es:[bx].takcnt+2, 0	; Clear high order half

CPC_ret:
	mov ax, Normal_ds	; DataS segment
	mov ds, ax		; Install in ds
    ASSUME ds:DataS
	ret			; Done here

Bombed:	call Boop		; Bombed, make a noise
	or cs:MFlags, Bye_bye	; Go home
	ret


	PUBLIC Build_Menu_database

    ASSUME ds:MAlloc, es:MAlloc
Build_Menu_database:

	mov ax, Normal_ds	; Switch ds to DataS
	mov ds, ax
    ASSUME ds:DataS

	mov si, OFFSET Ctrl_C_static_text ; Ptr to source
	mov di, OFFSET Ctrl_C_data ; Ptr to target
	cld			; Forwards
	mov cx, Ctrl_C_stext_len ; The number of bytes to copy
	rep movsb		; Copy the string

	mov ax, MAlloc_ds	; Switch ds back to MAlloc
	mov ds, ax
    ASSUME ds:Malloc

	mov ax, OFFSET Ctrl_C_data ; Pick up ptr to control-C text
	mov Ctrl_C_ptr, ax	; Store it away for later

	mov N_items, 0		; No items yet
	mov Item_width, 0	; This is the widest item seen so far
    Seg_is_Datas		; Datas segment
	mov ax, OFFSET Menu_file_name ; Name of file to get
	call spath		; Search path for file, DSK: first
	 jnc BUI_ok		;  Got it, go work with it

	mov dx, OFFSET Cant_find_file ; Complain
	call Do_mode_line_error ; Wait for a key
	jmp Use_our_own_ds	; Reset ds and return NG

BUI_ok:	mov dx, ax		; Copy ptr to name to dx
	mov ax, (Open2*100h) + 0 ; Open file for input
	int DOS			; Try to open the file
	 jc Bombed		;  Huh?

    Seg_is_Alloc		; Alloc segment
	mov File_handle, ax	; Save file handle for later
	mov di, OFFSET Menu_database ; Ptr to start of our database
	cld			; Build forwards
	or MFlags, Newlin	; We start on a new line

BUI_disk_read:
	mov ah, ReadF2		; Code to read from file
	mov bx, File_handle	; DOS file handle
	mov cx, SIZE Menu_file_buffer ; Number of characters to try for
	mov dx, OFFSET Menu_file_buffer ; Where to put them
	int DOS			; Read from the Menu file
	 jc BUI_finish		;  Stop on error

	or ax, ax		; Check for zero bytes read
	 jz BUI_finish		; Hit EOF, go finish it up

	mov cx, ax		; Byte count for this record
	inc cx			; We decr once too many times
	mov si, OFFSET Menu_file_buffer ; Ptr to buffer

BUI_lp:	loop BUI_next_char	; If more chars, go get one
	jmp SHORT BUI_disk_read	; Buffer is empty, try to refill it

BUI_next_char:
	lodsb			; Get the next byte

	cmp al, Space		; In the control-char range?
	 jl BUI_control		;  Yes, don't store it, check for linefeed

	stosb			; Store the char
	test MFlags, Newlin	; At start of a line?
	 jz BUI_not_first_char	;  No

	call BUI_do_first_char	; Use routine to do it
	jmp BUI_lp

BUI_not_first_char:
	inc Width_counter	; Track the size of each line
	jmp Bui_lp		; Go try for another char

 %OUT >> About half way through source file


BUI_control:
	cmp al, Lf		; Line feed?
	 jne BUI_lp		;  No

	or MFlags, Newlin	; We are now on a new line
	sub al, al		; Make a zero
	stosb			; Tie off the last line

	test MFlags, New_item + In_header ; Was this an item or hdr line?
	 jz BUI_lp		;  No

	mov ax, Width_counter	; Pick up current width

	test MFlags, In_header	; Header?
	 jz BUI_tag		;  No

	mov Header_length, ax	; Save length for header centering

BUI_tag:
	cmp ax, Item_width	; Compare with previous maximum
	 jle BUI_lp		;  Not bigger

	mov Item_width, ax	; Save this new maximum
	jmp BUI_lp		; Go get another character

BUI_finish:
	mov ax, N_items		; Count of items
	dec ax			; Drop by 1
	mov Last_item, ax	; Store as number of last item

	sub al, al		; Make a zero
	mov cx, 13		; Make several blank lines after Menu database
	rep stosb		; Write a string of zeros

	mov ah, 3eh		; Code to close a file
	int DOS			; Close the file

	ret			; Done here

BUI_do_first_char:
	and MFlags, NOT (Newlin + In_header) ; Turn off some flags
	mov Width_counter, 0	; Start line length counter to zero

	cmp al, "%"		; Code for header?
	 jne DFC_not_hdr	;  No

	mov Header_ptr, di	; Store header pointer for later
	or MFlags, In_header	; We want special length tracking
	jmp SHORT DFC_ret

DFC_Not_hdr:
	cmp al, "*"		; Code for item?
	 jne DFC_not_item	;  No

	mov bx, N_items		; Get pointer to the current row
	shl bx, 1		; Double for word offset
	mov Menu_items[bx], di	; Store pointer to the item

	inc N_items		; This is a new item, add one to the count
	or MFlags, New_item	; Flag that we need a data pointer
	jmp SHORT DFC_ret

DFC_Not_item:
	cmp al, "+"		; Code for data?
	 jne DFC_ret		;  No

	test MFlags, New_item	; Only store ptr to the first line of data
	 jz DFC_ret		;  Ignore this one, ptr already set

	mov bx, N_items		; Get pointer to the current row
	dec bx			; Pull back to previous row
	shl bx, 1		; Double for word offset
	mov Menu_data[bx], di	; Store pointer to the data
	and MFlags, NOT New_item ; Clear flag for next time

DFC_ret:
	ret			; Done here


Install_Screen:
    Seg_is_Datas		; Datas segment
	call Save_command_mode_screen ; First, save what is already up there
    Seg_is_Alloc		; Alloc segment

	mov ax, 0720h		; White blanks on black background
	mov di, OFFSET Our_Image ; Ptr to our buffer
	mov cx, Number_of_chars_on_PC_screen
	cld			; Forwards
	rep stosw		; Clear our buffer

	mov bx, OFFSET Our_Image ; Ptr to our buffer

	mov ax, Normal_ds	; Use normal again
	mov ds, ax		; Install in ds
    ASSUME ds:DataS

	or Screen_flags, Force_screen_update
	call Put_Screen		; Set up both video pages

	mov ax, MAlloc_ds	; Use our own segment
	mov ds, ax		; Install in ds
    ASSUME ds:MAlloc
	ret


	PUBLIC Build_menu_box

Build_menu_box:

; Set up proper attribute based on card type

	mov ah, Mono_attr	; Mono
	mov Unhighlight, ah
	mov ah, Mono_highlight
	mov Highlight, ah
	mov Header_attribute, ah

    Seg_is_Datas		; Datas segment
	cmp Force_mono, 0	; Are we stuck with mono?
	 jne MON_0		;  Yes

	call Get_Screen_Segment
	cmp ax, 0b000h		; Mono?
	 je MON_0		;  It is, go use it

    Seg_is_Alloc		; Alloc segment
	mov ah, Color_attr	; Color, use it instead
	mov Unhighlight, ah
	mov ah, Color_highlight
	mov Highlight, ah
	mov ah, Color_header_attr
	mov Header_attribute, ah
	jmp SHORT MON_1

MON_0:
    Seg_is_Alloc		; Alloc segment

MON_1:	mov ax, Menu_window_top	; Row number of top of window
	mov Which_line, ax	; Save it for later
	call Make_new_ptr	; Get a new DI

; Do top row of Menu window

	mov al, Upper_left	; Graphic character for upper left corner
	mov ah, Unhighlight	; Attribute for no highlighting
	stosw

	mov al, Horizontal
	mov cx, Item_width
	rep stosw

	mov al, Upper_right
	stosw

	inc Which_line		; Bump to next row
	call Make_new_ptr	; Point to it

	mov al, Vertical
	stosw

	mov al, Space		; Do the header line
	mov ah, Header_attribute
	mov cx, Item_width
	rep stosw

	mov al, Vertical
	mov ah, Unhighlight
	stosw

	inc Which_line		; Bump to next row
	call Make_new_ptr	; Point to it

	mov al, Right_tee
	stosw

	mov al, Horizontal
	mov cx, Item_width
	rep stosw

	mov al, Left_tee
	stosw

	inc Which_line		; Bump to next row
	call Make_new_ptr	; Point to it

	mov cx, N_items		; Do one line for each item

; Do the middle rows of the Menu window

LP_1:	push cx

	mov al, Vertical
	stosw

	mov al, Space
	mov cx, Item_width
	rep stosw

	mov al, Vertical
	stosw

	pop cx

	inc Which_line		; Bump to next row
	call Make_new_ptr	; Point to it

	loop LP_1

; Do bottom row of Menu window

	mov al, Lower_left
	stosw

	mov al, Horizontal
	mov cx, Item_width
	rep stosw

	mov al, Lower_right
	stosw

	mov Our_cursor, (25*256)+0 ; Put cursor where it won't be seen
	ret


; Make ptr to another item

New_line:
	mov di, Which_item	; Number of the menu item we are doing
	add di, Menu_window_top	; Adjust for top of list of menu items
	add di, 3
	mov Which_line, di	; Set up as the line to work on
;	jmp Make_new_ptr	; Make a new pointer to the line

; Set up a pointer to this row

Make_new_ptr:
	push ax			; Save reg
	push bx
	mov ax, Which_line	; Row number of topmost row
	mov bh, 2 * Number_of_columns ; Convert to bytes 
	mul bh

	add ax, Menu_window_left ; Account for columns to the left
	add ax, Menu_window_left
	add ax, OFFSET Our_Image ; Finally, add in the address of the buffer
	mov di, ax		; Set up as destination
	pop bx
	pop ax
	ret			; Go use it

	PUBLIC Fill_menu_frame

Fill_menu_frame:
	mov ax, Menu_window_top	; Row number to start writing to
	inc ax			; Bump it again
	mov Which_line, ax	; Save it
	call Make_new_ptr	; Get a new DI
	add di, 2		; Skip over border

	mov bx, Item_width	; Pick up item width
	sub bx, Header_length	; Subtract length of header
; bx := bx / 2 for centering
; bx := bx * 2 for byte-to-word conversion
	and bx, NOT 1		; Force it EVEN

	add di, bx		; Adjust ptr for Header centering

; Do the header

	mov si, Header_ptr	; Use the header text here

F00_lp_inner:
	lodsb			; Pick up the next byte
	or al, al		; Check for zero
	 jz F00_tag		;  Hit end

	stosb			; Store this char
	inc di			; Skip attribute
	jmp F00_lp_inner	; Go do another char

; Do the items

F00_tag:
	mov Which_item, 0	; Start on item zero

FIL_lp_outer:
	mov bx, Which_item	; Item we are on
	shl bx, 1		; Double for word offset
	mov si, Menu_items[bx]	; Ptr to item text
	call New_line		; Set up di for this line
	add di, 4		; Skip over the border and one space

FIL_lp_inner:
	lodsb			; Pick up the next byte
	or al, al		; Check for zero
	 jz FIL_end_of_inner	;  Hit end

	stosb			; Store this char
	inc di			; Skip attribute
	jmp FIL_lp_inner	; Go do another char

; End of inner loop, skip chars until we hit a zero byte

FIL_end_of_inner:
	inc Which_item		; Bump to next item
	mov ax, Which_item	; Get it in ax
	cmp ax, Last_item	; Over the hill?
	 jle FIL_keep_going

	ret			; Done filling

FIL_keep_going:
	inc Which_line		; Move to next row
	call Make_new_ptr	; Get a new DI
	add di, 4		; Skip over border and first column

	jmp FIL_lp_outer	; Go try for another line

	PUBLIC Do_other

Do_other:
    Seg_is_Alloc		; Alloc segment
	cmp BYTE PTR Full_keystroke, 3 ; Was it a control-C?
	 jne DOT_0		;  No

	mov Which_item, 20	; Pretend item 20
	jmp Do_return		; Pretend selected

DOT_0:	mov bx, Which_item	; The current item
	mov Which_line, bx	; Save it in another place

	mov ah, BYTE PTR Full_keystroke ; Pick up ASCII value of key user hit
	cmp ah, 'a'		; Compare with lower case 'a'
	 jb DOT_loop		;  Too low
	cmp ah, 'z'		; Compare with lower case 'z'
	 ja DOT_loop		;  Too high
	sub ah, 32		; Upcase this character

DOT_loop:
	push bx			; Save reg
	shl bx, 1		; Double for word offset
	mov si, Menu_items[bx]	; Get ptr to item text string
	pop bx			; Restore reg
	lodsb			; Get the first letter
	cmp al, 'a'		; Compare with lower case 'a'
	 jb DOT_No_upcase	;  Too low
	cmp al, 'z'		; Compare with lower case 'z'
	 ja DOT_No_upcase	;  Too high
	sub al, 32		; Upcase this character

DOT_No_upcase:
	cmp al, ah		; Do we have a winner?
	 jne No_match		;  Not yet ...

; Have a hit ...
	push bx			; Save new line
	mov bx, Which_line	; Get back ptr to old line
	mov Which_item, bx	; Set up for highlight routine
	call Unhighlight_line	; Unhiughlight the old line
	pop WORD PTR Which_item	; Get back new line
	call Highlight_line	; Highlight it
	call Do_the_screen	; Show these changes ...
	jmp Do_return		; Treat as if Return pressed while on this

No_match:
	mov bx, Which_item	; Item we are pointing to now
	inc bx			; Go up by 1
	cmp bx, Last_item	; Did we go too high?
	 jle No_prob		;  No problem

	sub bx, bx		; Move to the first item instead

No_prob:
	mov Which_item, bx	; Store the new item number
	cmp bx, Which_line	; Back to start?
	 je Do_boop		;  Yeah, complain

	jmp DOT_loop		; Go try the next one

Do_boop:
    Seg_is_Datas		; Datas segment
	call Boop		; Give low beep
    Seg_is_Alloc		; Alloc segment
;	jmp Check_keystrokes	; Try again

Check_keystrokes:
	mov ah, 1		; Code to see if a char is ready
	int Kb			; Have BIOS tell us
	 jz Check_keystrokes	;  None, waste time

	sub ah, ah		; Zero is code to read the char
	int Kb			; Have BIOS get it for us

	mov Full_keystroke, ax	; Save the whole thing here
    Seg_is_Datas		; Datas segment
	mov al, ah		; Copy scan code to al
	mov bx, OFFSET Actions	; Action table
	mov cx, N_Characters	; Number of scan codes to check
	mov dx, OFFSET Characters ; The character table
	jmp Check_Table		; Go dispatch to the right routine

    ASSUME ds:Malloc, es:MAlloc

Do_the_screen:
	mov bx, OFFSET Our_Image ; Set ptr to our screen

	mov ax, Normal_ds	; Put_Screen likes normal ds
	mov ds, ax
    ASSUME ds:DataS

	or Screen_flags, Screen_changed ; Flag that Put_Screen has to work
	call Put_Screen		; Update screen

	mov ax, es		; Copy es ...
	mov ds, ax		;  ... to ds
    ASSUME ds:MAlloc

	ret			; Done here

Move_highlight_up:
    Seg_is_Alloc		; Alloc segment
	call Unhighlight_line
	call Move_up
	jmp Highlight_line

Move_highlight_down:
    Seg_is_Alloc		; Alloc segment
	call Unhighlight_line
	call Move_down
	jmp Highlight_line

Do_Home:
    Seg_is_Alloc		; Alloc segment
	call Unhighlight_line
	mov Which_item, 0	; Item 0
	jmp Highlight_line
	
Do_End:
    Seg_is_Alloc		; Alloc segment
	call Unhighlight_line
	mov ax, Last_item	; Final item on menu
	mov Which_item, ax
	jmp Highlight_line

Do_PgUp:
	jmp Do_Home

Do_PgDn:
	jmp Do_End

Do_Up:
	jmp Move_highlight_up

Do_Down:
	jmp Move_highlight_down

Do_return:
;	jmp CHK_bye

CHK_bye:
    Seg_is_Alloc		; Alloc segment
	or cs:MFlags, Bye_bye	; Flag the exit
	ret			; Done here

Highlight_line:
	mov al, Highlight	; Set up to highlight the chars
	jmp SHORT Change_highlighting

Unhighlight_line:
	mov al, Unhighlight	; Set up to unhighlight the chars
;	jmp Change_highlighting

Change_highlighting:
	call New_line
	add di, 3		; Skip over border, point at attribute
    Seg_is_Alloc		; Alloc segment
	mov cx, Item_width	; The number of attributes to hit

HIL_lp:	stosb
	inc di
	loop HIL_lp		; Try for another
	ret

Move_up:
	mov ax, Which_item	; Item we arte pointing to now
	dec ax			; Go up by 1
	or ax, ax		; See if negative
	 jns Move_is_OK		;  No problem

	mov ax, Last_item	; Move to the last item instead
	jmp SHORT Move_is_OK

Move_down:
	mov ax, Which_item	; Item we are pointing to now
	inc ax			; Go up by 1
	cmp ax, Last_item	; Did we go too high?
	 jle Move_is_OK		;  No problem

	sub ax, ax		; Move to the first item instead

Move_is_OK:
	mov Which_item, ax	; Store the new item number
	ret			; That's it

Back_to_normal_ds:
	push ax			; Save reg
	mov ax, Normal_ds	; Pick up addr of DataS segment
	mov ds, ax		; Copy to ds
	 nop
	mov es, ax		;  and es
	pop ax			; Restore reg
	ret			;  and return

    ASSUME ds:DataS, es:DataS

Use_our_own_ds:
	push ax			; Save reg
	mov ax, MAlloc_ds	; Pick up addr of MAlloc segment
	mov ds, ax		; Copy to ds
	 nop
	mov es, ax		;  and es
	pop ax			; Restore reg
	ret			;  and return

Menu	ENDP

Do_mode_line_error PROC

	mov In_menu_mode, 0	; Leave menu mode, if in it
	call Save_mode_line	; Ask for a save
	call PutErr_PC		; Play with mode line
	call Bombed		; Make noise
	sub ah, ah		; Code to read a character
	int Kb			; Wait until user hits key
	call Restore_mode_line	; Ask for a restore
	mov ah, PrStr		; Code to type a string
	mov dx, OFFSET Cr_str	; Simple carriage return
	int Dos			; Type it (force left margin)
	ret			; Done here

Do_mode_line_error ENDP


; Save the mode line for later restoral

Save_mode_line PROC

	push ds			; Save ds
	call Get_address	; Get new ds into ax
	mov ds, ax		; Store the new seg addr
	mov si, (Number_of_rows_PC-1)*2*Number_of_columns ; Offset of line 25
	mov di, OFFSET Saved_mode_line ; Where to copy it
	mov cx, Number_of_columns
	cld			; Forwards
	rep movsw		; Save the line
	pop ds			; Restore ds
	ret

Save_mode_line ENDP


; Restore the mode line

Restore_mode_line PROC

	push es			; Save es
	call Get_address	; Get new es into ax
	mov es, ax		; Store the new seg addr
	mov si, OFFSET Saved_mode_line ; Where copy is
	mov di, (Number_of_rows_PC-1)*2*Number_of_columns ; Offset of line 25
	mov cx, Number_of_columns
	rep movsw		; Restore the line
	pop es			; Restore es
	ret

Restore_mode_line ENDP


; Get address of segment of currently-displayed video page

Get_address PROC

	mov ax, 0B000h		; Mono, maybe
	cmp Which_card, 0 	; Is it?
	 je Go_back		;  Yes

	sub bh, bh		; Zero high half of bx
	mov bl, Which_page	; The page we are on in the color card
	shl bx, 1		; Double for word offset into table
	mov ax, Video_page_addresses[bx] ; Pick up the address for the page

Go_back:
	ret

Get_address ENDP

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.

RSKP	PROC
	pop bp
	add bp,3
	push bp
	ret

RSKP	ENDP

; Jumping here is the same as a ret

R	PROC
	ret
R	ENDP

Code	ENDS

	END

