page	64,132
title	screen - IBM Display Enhancement  Dec 1983
.radix 10
;***************************************************************
; Typed in by Greg Glass - gjg@cmu-cs-cad from Byte - Nov. 1983
; NOTE: if you have a color card this will shift over to it
;	(even if the monitor is turned off)
;      hit Alt F7 to switch to the Mono monitor.
;   To build this program:
;	1>  masm screen;	/* assemble the code */
;	2>  link screen;	/* link it - will get no stack seg. message */
;	3>  exe2bin screen      /* convert the format */
;	4>  rename screen.bin screen.com   /* make the new format executable*/
;	5>  del screen.exe	/* don't need this anymore */
;
;  To use program:  just run it or place in autoexec.bat
;
;  COMMANDS:
;	<alt>F1  - increment the text color
;	<alt>F3  - increment the background color
;	<alt>F5 -  toggle modes co80/co40
;	<alt>F7 -  toggle between color and mono display
;	<alt>F9 -  redraw display - reset colors
;***************************************************************

;***************************************************************
;
;  Define interrupt vectors for both keyboard interrupt 16H and
;	screen interrupt 10H.  Both in segment 0.
;
;***************************************************************

keyvect	segment	at 0		; define keyboard interrupt vector
	org	16h*4
KEYINT	label	dword
keyvect	ends

scrvect	segment	at 0		; define screen interrupt vector
	org	10h*4
SCRINT	label	dword
scrvect	ends

;***************************************************************
;
;	define constants
;
;***************************************************************

bw_val	   equ	07h		; standard b&w attibute sent to monitor
equip_flag equ	410h		; area in RAM that contains equipment status
chk_mode   equ	15		; screen interrupt function to check mode
mono_mode  equ	7		; screen mode of 7 indicates monochrome
color_adpt equ	3		;modes from 0 to 3 are non-graphics color

;***************************************************************
;
;	start code area
;
;***************************************************************

code	segment para
	assume	cs:code
	org	100h		; start code offset 100h from starting segment
				; (this leaves room for DOS's work areas)

KEY	proc	far
START:
	; Initialization code...used only once, on system startup

	jmp	init_code	; call initialization routine
even
	validchk db 'FCP!'	; used by INSTALL to check for valid SCREEN
				; program

;***************************************************************
;
;	Define storage areas and data structures
;
;***************************************************************

	; define keystroke scan codes for the five SCREEN functions

fore_inc  dw	6800h		; foreground increment
back_inc  dw	6a00h		; background increment
c80_40    dw	6c00h		; 80x25 to 40x25 flip-flop key
col_mon   dw	6e00h		; color/mono flip-flop key
repaint   dw	7000h		; repaint screen using current mode

cur_mode  dw	col80_area	; Initialize starting mode
mono_set  dw	mono_area	; pointer to monochrome area
color_set dw	col80_area	; pointer to "active" color area

scrn_attr db	70h		; current screen attribute
scrn_mode db	255		; saves current screen mode

	; define structure used to contain information about 40 and 80
	; column color modes as well as monochrome mode.

s	struc
	corner	dw	0	; defines COL/ROW count of characters for mon
	bf	dw	0	; Colors of fore and back
	equip	dw	0	; equipment settings
	mode	dw	0	; AX value for setting mode of monitor
s	ends

	; Now, set up three screen structures with default conditions

col80_area	s	<5019h,0107h,20h,3>	; 80x25, white FORE blue BACK

col40_area	s	<2819h,0107h,10h,1>	; 40x25, Brown FORE black BACK

mono_area	s	<5019h,0007h,30h,7>	; monochrome, reverse video

;NOTE:	The standard BIOS ROM KEYBOARD interrupt routine is
;	executed as a subroutine ( using CALL DWORD PTR) if
;	the interrupt was invoked to return a keystroke.  Any 
;	other execution KEYBOARD_IO can be called as a simple
;	inline FAR JMP instruction.  NOTE: the CALL instruction
;	(see just after INT_LOOP label below) uses the 
;	address stored here at KEY_CALL to KEYBOARD_IO.

KEY_CALL :
	db	0eah		; far JMP address to KEYBOARD interrupt
	dw	0,0		;

;***************************************************************
;
;	procedure KEY_RTNE - Intercepts keyboard interrupt and
;	determines if the keystroke is one of the five SCREEN ones.
;
;***************************************************************

KEY_RTNE:
	assume	ds:code
	sti			; turn on interrupts
	cmp	ah,0		; call as subroutine if keyfetch
	jne	KEY_CALL	; jump to KEYBOARD_IO if not
	push	ds		; save ds and bx from destruction
	push	bx		;
	push	cx
	push	dx
	push	es
	push	di
	mov	bx,cs		; move cs segment into ds
	mov	ds,bx		;

INT_LOOP:
	pushf			; IBM keyboard proc exports interrupt call
	mov	bx,offset KEY_CALL + 1	;Get address to ROM code for keyboard
	call	dword ptr[bx]	; call keyboard routine
	mov	bx, cur_mode	; get current mode address
	cmp	ax, col_mon	; see if COLOR<->MONO flip-flop key
	jne	TEST_FORE	; exit if not
	; otherwise, flip-flop screen mode
	cmp	bx, mono_set	; are we looking at monochrome?
	je	set_color	; swap in color if yes
	cmp	mono_set,0	; see if monochrome monitor enabled
	je	next_key	; ignore command if not
	mov	bx,mono_set	; otherwise set up monochrome
	jmp	short DO_CHG	;

SET_COLOR:
	cmp	color_set,0	; see if COLOR monitor enabled
	je	NEXT_KEY	; skip if not
	mov	bx,color_set	; set up for color

DO_CHG:
	call	SCREEN_CHG	; implement screen change

NEXT_KEY:
	mov	ah,0		; set up to fetch keystroke
	jmp	INT_LOOP	; fetch next key input

TEST_FORE:
	push	ax		; save registers.
	push	bx		; see if in graphics mode
	mov	ah,chk_mode	;
	int	10h		;
	pop	bx		; restore bx register
	cmp	al,color_adpt	; if between 0 and 3, not graphics mode
	jle	NOT_GRAF	;
	cmp	al,mono_mode	; monochrome mode
	jge	NOT_GRAF	; restore stack
	pop	ax		; if color-graphics mode, do not change
	jmp	DONE		; modes

NOT_GRAF:
	pop	ax		; restore ax
	cmp	ax,fore_inc	; is this key to increment FORE ?
	jne	TEST_BACK	; skip if not
	cmp	bx,color_set	; see if currently using color
	jne	BW_FLOP		; if not, go deal with B & W
	mov	ax,[bx].bf	; gets BACK in al, FORE in ah

EQ_FORE:
	inc	al		; increment FOREGROUND color
	and	al,7		; keep it within bounds (chg. for more colors)
	cmp	al,ah		; see if same as background
	je	EQ_FORE		; increment againt if yes
	mov	[bx].bf,ax	; save back to structure
	jmp	DO_CHG		; redraw display

TEST_BACK:
	cmp	ax,back_inc	; is this key to increment BACK?
	jne	TEST_REPAINT	; skip if not
	cmp	bx,color_set	; see if currently using color
	jne	BW_FLOP		; if not, go deal with B&W
	mov	ax,[bx].bf	; gets BACK in al, FORE in ah

EQ_BACK:
	inc	ah		; increment background color
	and	ah,7		; keep in bounds
	cmp	ah,al		; see if same as foreground
	je	EQ_BACK		; increment again if yes
	mov	[bx].bf,ax	; save back to structure
	jmp	DO_CHG		; redraw the screen

BW_FLOP:		; flip-flop B&W screen
	mov	ax,[bx].bf	; BACK in ah, FORE in al
	xchg	ah,al		; swap
	mov	[bx].bf,ax	; save back to structure
	jmp	DO_CHG		; redraw the screen

TEST_REPAINT:
	cmp	ax,repaint	; is this the key to redraw the screen ?
	je	DO_CHG		; if yes then repaint

TEST_80_40:
	cmp	ax,c80_40	; is this the key to flop 80 / 40 ?
	jne	DONE		; exit if not
	cmp	bx,offset col40_area	; is current pointer area 40x25 ?
	jne	TST80		; skip if not
	mov	bx,offset col80_area	; otherwise flip to 80x25
	jmp	short SAVE_COL	; save to color_set

TST80:
	cmp	bx,offset col80_area	; is current 80x25 color ?
	jne	NEXT_KEY		; ignore key if not
	mov	bx,offset col40_area	;

SAVE_COL:
	mov	color_set,bx	; sat to color_set
	jmp	SET_COLOR	; implement

DONE:
	pop	di
	pop	es
	pop	dx
	pop	cx
	pop	bx
	pop	ds
	iret			; return from interrupt

KEY endp		; done with main routine !

;***************************************************************
;
;	SCREEN_CHG - Changes current monitor screen mode
;
;	  Inputs: bx points to current monitor structure
;
;***************************************************************

SCREEN_CHG	proc	near
	mov	ax,0		; get segment address to equip_flag
	mov	es,ax		;   in RAM memory
	mov	ax,es:equip_flag ; get set of equip flag
	and	al,0CFh		; get rid of current monitor flag
	or	ax,[bx].equip	; set up new monitor flag
	mov	es:equip_flag,ax ; save back in RAM
	mov	cur_mode,bx	; indicate new mode

	;  now, set up attribute for background and foreground

	mov	dx,[bx].bf	; get both FORE and BACK in dx
	mov	cl,4		; shift count
	shl	dh,cl		; shift BACK into upper nibble
	or	dh,dl		; move FORE into lower nibble
	mov	scrn_attr,dh	; save result

	;  see if we need to reset monitor (switching to new monitor?)

	mov	ax,[bx].mode	; get mode
	cmp	al,scrn_mode	; compare with current mode
	je	SET_ATTR	; skip if same
	mov	scrn_mode,al	; otherwise, save current mode
	int	10h		; and reset to new monitor

SET_ATTR:	; change attributes of current screen
	call	CH_ATTR		; changes attributes
	ret

SCREEN_CHG	endp

;***************************************************************
;
;	CH_ATTR - repaints acctive screen so that every character on
;		current screen is displayed with the new attributes
;
;	Inputs: bx points to current monitor structure
;
;***************************************************************

CH_ATTR	proc	near
	; see if we need to draw in border for color mode
	cmp	bx,offset mono_set	; in color?
	je	NO_BORDER		; do not worry about border if not
	push	ax			; save registers
	push	bx			;
	mov	bx,[bx].bf		; get background color in bx
	mov	bl,bh			;
	mov	bh,0			; select border color
	mov	ah,11			; interface to set color palette
	int	10h
	pop	bx			; restore registers
	pop	ax

NO_BORDER:
	mov	ax,[bx].corner		; get COL and ROW for current
	mov	cornr,ax		; save in temporary
	mov	ah,chk_mode		; get page number
	int	10h			;
	; bh contains active page
	mov	ah,3			; save current cursor posn
	int	10h
	push	dx			; save position in stack
	xor	dx,dx			; load dx with 0
	mov	cx,1			; set up replication count
	mov	bl,scrn_attr		; get current attribute

REP_ATTR:
	mov	ah,2			; set cursor position
	int	10h
	mov	ah,8			; read next character
	int	10h
	; ah contains current character attribute
	and	ah,88h			; get intensity bit
	and 	bl,77h			; make sore attribute intensity off
	or	bl,ah			; combine to get current attribute
	mov	ah,9			; write out char with new attribute
	int	10h			;
	inc	dl			;
	cmp	dl,tcol			; are we done with this column?
	jle	REP_ATTR
	xor	dl,dl			; otherwise zero out DL
	inc	dh			; move to next row
	cmp	dh,trow			; done with screen ?
	jle	REP_ATTR		; loop until done

	pop	dx			; restore original cursor position
	mov	ah,2
	int	10h
	ret
 
	cornr	label	word
	trow	db	0	; temporary store for ROW
	tcol	db	0	; temp store for COL

CH_ATTR	endp

;***************************************************************
;
;	SCR_RTNE - Replaces SCREEN interrupt so that it can intercept
;		B&W character writes and change attributes
;
;***************************************************************

SCR_RTNE proc	near
	assume	ds: code
	sti
	push	ds		; save data segment register
	push	cs		; move CS segment into DS
	pop	ds		;     this is a funny way to do that (gjg)
	cmp	ah,6		; Spot SCROLL UP and SCROLL DOWN calls
	jl	NORMAL_SCR	;
	cmp	ah,6
	jg	NOT_SCROLL	;

SCROLL:
	call	GET_CH		; for scrolling, update attribute
	jmp	NORMAL_SCR	; now, execute scroll

NOT_SCROLL:
	cmp	ah,9		; check for "WRITE ATTRIBUTE/CHAR" cmd
	jne	NORMAL_SCR	; send out any other command as normal
	xchg	bh,bl		; get attribute for command
	call	GET_CH		; update attribute for command
	xchg	bh,bl		; move attribute back to bh for cmd

NORMAL_SCR:
	pop	ds		; restore ds segment register
	;
	; NOTE:	We are now ready to invoke the BIOS screen interrupt.
	;	Since the ROM code includes an IRET interrupt return call,
	;	all we need to do is to jump to the start of the ROM code
	;	and all will be well.  Since the initialization code set
	;	up the address to the screen interrupt code below, we can
	;	set up a forced jump to that address.

JMP_SCR:			; Address to SCREEN interrupt
	db	0EAh		; force a FAR JMP but do not set up dest-
	dw	0,0		;   ination address at assembly time.
				;   (INIT routine will set this address)

SCR_RTNE  endp


;***************************************************************
;
;	GET_CH - subroutine replaces B&W character with current replacement
;			attributes and allows for intensity bit setting
;
;		Inputs : bh contains attribute to be modified
;
;***************************************************************

GET_CH	proc	near
	mov	savech,bh	; save character
	and	bh,77h		; Remove intensity and blink bits
	cmp	bh,bw_val	; see if currently defined B&W value
	mov	bh,savech	; otherwise, modify to current attribute
	jne	OUT		; exit if not
	and	bh,88h		; get rid of B&W part
	or	bh,scrn_attr	; move in current attribute part

OUT:
	ret			; done

	savech	db	0	; temporary character store
GET_CH	endp

LASTONE:	; all code after this label is freed to DOS use after
		; initialization of the program.

;***************************************************************
;
;	INIT_CODE - Code to load and initialize the SCREEN program..
;		sets up DOS to keep all code before "LASTONE" label
;		safe from overlaying during system operation.
;
;***************************************************************

COPYRT:	db  'SCREEN  Version 1.20  Copyright 1983 Tim Field',13,10,'$'

INIT_CODE proc  near

	; Initialize KEYBOARD intercept code

	assume	es:keyvect		;'vectors' is interrupt segment 0
	
	mov	ax,keyvect		; get address to interrupt vector
	mov	es,ax			; save in es
	mov	ax,es:keyint		; get address to interrupt
	mov	bx,offset key_call+1	; address to place to save vector
	mov	[bx],ax			; save interrupt address
	mov	ax,es:keyint[2]		; get interrupt segment for rtne
	mov	[bx+2],ax		; save it too
	mov	es:keyint,offset key_rtne ; now replace with own address
	mov	ax,cs			; save segment in interrupt vector
	mov	es:keyint[2],ax		;

	; initialize SCREEN intercept code
	assume	es:scrvect		;'vectors' is interrupt segment 0

	mov	ax,scrvect		; get address to interrupt vector
	mov	es,ax			; save in es
	mov	ax,es:scrint		; get address to interrupt
	mov	bx,offset jmp_scr+1	; address to place to save vector
	mov	[bx],ax			; save interrupt address
	mov	ax,es:scrint[2]		; get interrupt segment for rtne
	mov	[bx+2],ax		; save it too
	mov	es:scrint,offset scr_rtne ; now replace with own address
	mov	ax,cs			; save segment in interrupt vector
	mov	es:scrint[2],ax		;

	; Initialize screen
	mov	bx,cur_mode		; set up initial mode
	call	SCREEN_CHG		; initialize

	; Now print out acknowledgement to user monitor and exit

	mov	ax,cs			; set up segment to routine
	mov	ds,ax
	mov	dx,offset copyrt	; now print out copyright message
	mov	ah,9			; DOS function to print string
	int	21h			; execute function interrupt
	mov	dx,offset lastone	; save all code up to "LASTONE" label
	int	27h			; no return needed

INIT_CODE endp
code ends
end start
