	page	56,132
	title	WBIOS   Window BIOS extension
	.sall
;
; Written 1984 By J. Eric Roskos; public domain.
; No support is provided by the author; you must make any desired
; revisions yourself.
;
; This program provides an extension to the BIOS function calls,
; functions 40H-43H.  For a description of the calls, see the Turbo
; Pascal program WDEMO.PAS, which also shows the preferred way of
; using the functions.  Note, however, that this BIOS extension is
; independent of any particular language, and will work with any
; program that does its I/O through the ROM BIOS's "WRITE_TTY"
; function (the plain MS-DOS display driver does this, for instance).
;
; There are a few enhancements to the IBM BIOS call: TABs are handled
; properly, BELs produce a more bell-like sound, and there is a "Raw"
; mode (although you'll have to add the function call to set it your-
; self) in which all characters are displayed as-is, without translation
; for CR, LF, BEL, etc.  There is also an option to turn end-of-line
; wraps on or off, although again you must add the function call.
; To turn these modes on, set the variables "wrap" and "raw" as
; explained below in the comments.  (These features were included in
; the program for future expansion, but have never been tested, and
; aren't guaranteed to work).
;

;
; Macros
;

;
; wto: write message to display.  Append cr/lf unless crsup=nocr
;

wto	macro	msg,crsup
	local	msgstr,around

	jmp	around

msgstr	db	msg
	ifb	<crsup>
	db	0DH,0AH
	endif
	db	'$'

around:	push	ax
	push	bx
	push	si
	push	di
	push	bp

	mov	si,offset msgstr
	call	putc

	pop	bp
	pop	di
	pop	si
	pop	bx
	pop	ax

	endm

prtreg	macro	rg,msg	; put register rg with message msg on display

	push	ax

	mov	ax,rg
	call	prtax
	wto	msg,nocr

	pop	ax
	endm


;
; Code Segment
;

cseg		segment para public 'code'
		assume	cs:cseg,ds:cseg,ss:cseg,es:nothing

		org	100H
;
; COM program startup
;
cpstart		proc	far
		jmp	near ptr start
cpstart		endp

;
; Local Procedures
;
lcpos		dw	0E60H

putc		proc	near	; write a string to display W/O DOS intervention
		push	es
		push	ax
		mov	ax,crt_seg
		mov	es,ax
		mov	di,cs:lcpos
putc1:
		mov	al,cs:[si]
		cmp	al,'$'
		je	putc2
		cmp	al,0dH
		jne	putc3
		mov	cs:lcpos,0E60H
		mov	di,0E60H
		jmp	putc4
putc3:
		mov	es:[di],al
		inc	di
		inc	di
putc4:
		inc	si
		jmp	putc1

putc2:
		mov	cs:lcpos,di
		pop	ax
		pop	es
		ret

putc		endp

prtnum		proc	near	; print half a byte on screen
		push	ds	; this procedure is used by prtax below
		push	es

		push	cs
		pop	ds

		push	bx
		mov	bx,crt_seg
		mov	es,bx
		mov	bx,offset xltab
		xlatb
		mov	bx,cs:lcpos
		mov	es:[bx],al
		inc	cs:lcpos
		inc	cs:lcpos

		pop	bx
		pop	es
		pop	ds
		ret
xltab		db	'0123456789ABCDEF'
prtnum		endp

prtax		proc	near	; print contents of ax register on screen
		push	cx	; all registers are preserved
		push	ax
		mov	al,ah
		mov	cl,4
		shr	al,cl
		call	prtnum
		pop	ax
		push	ax
		mov	al,ah
		and	al,0Fh
		call	prtnum
		pop	ax
		push	ax
		mov	cl,4
		shr	al,cl
		call	prtnum
		pop	ax
		push	ax
		and	al,0Fh
		call	prtnum
		pop	ax
		pop	cx
		ret
prtax		endp

;
; BIOS call dispatcher
;

;
; data area
;

;
; vector to ROM BIOS
;

romoff		dw	?
romseg		dw	?

;
; display variables
;

active_page	db	?	; parameters for wtty (window I/O) proc.
crt_mode	db	?
color		db	7
rt_edge		db	79
bot_edge	db	24
lf_edge		db	0
top_edge	db	0
wd_width	db	79	; window width and height (really the coords of
wd_height	db	24	; bottom corner relative to strt of window)
wrap		db	0	; 0=don't CRLF at right margin, 1=do
raw		db	0	; 0=xlate cr, lf, etc, 1=display w/o xlation
crt_seg		dw	?	; segment for CRT display RAM

;
; clock
;

tick		dw	0	; 1/18th second counter
tock		dw	0	; every-5-seconds counter (18.2 Hz adjust)
clk_oset	dw	0	; save area for displaced clock int handler
clk_seg		dw	0

;
; window stack:
;   0[wsp] = left edge of window
;   1[wsp] = top edge of window
;   2[wsp] = right edge of window
;   3[wsp] = bottom edge of window
;   4[wsp] = cursor address (2 bytes)
;   6[wsp] = offset of window save area (2 bytes)
;   8[wsp] = segment of window save area (2 bytes)
;  10[wsp] = number of frames (1 byte)
;

wssize		equ	13	     ; number of bytes in window stack frame
wsframes	equ	15	     ; number of windows that can be stacked

wsp		dw	offset wsend ; display window stack pointer
wstack		db	wssize*wsframes dup (?) ; display window save stack
wsend 		equ	this word    ; bottom of stack
		db	wssize dup (?) ; extra frame in case of user error

;
; window queue: save area for windows
;

wqp		dw	offset qbase ; next avail byte in window queue
wqsize		equ	8192	     ; size of window queue - ok to tune
qlim		dw	?	     ; max address in window queue

;
;

temp		dw	?	     ; used when fixing up stack for return

;
; jump table for our BIOS functions
;

jmptab		label	word
		dw	offset	setwindow
		dw	offset	pushwindow
		dw	offset	rstwindow
		dw	offset	frame
tablen		equ	$-jmptab


;
; the dispatcher
;

disp		proc	far

		;
		; see if it's one of our functions
		;

		cmp	ah,14		; write TTY function
		jne	tryrcp
		jmp	ourfn
tryrcp:		cmp	ah,3		; read cursor position
		jne	tryscp
		jmp	readcsr
tryscp:		cmp	ah,2		; set cursor position
		je	adjxy		; have to adjust coordinates
		cmp	ah,6		; scroll screen up
		jne	trysd		; have to adjust corners
		jmp	adjsc
trysd:		cmp	ah,7		; scroll screen down
		jne	tryus		; adjust corners
		jmp	adjsc
tryus:		cmp	ah,40H		; one of our new functions?
		jl	callbios	; no, call the BIOS
		jmp	ourfn

callbios:
		push	cs:romseg	; not one of ours; call BIOS
		push	cs:romoff
		ret

;
; adjust x and y coordinates for set-cursor-position
;
adjxy:
		push	dx		; save caller's dx

		add	dh,cs:top_edge	; adjust the coordinates
		cmp	dh,cs:bot_edge
		jle	adjxy1

		; if below the bottom edge, scroll up a line

		push	ax		; save caller's registers
		push	bx
		push	cx
		push	dx
		push	si
		push	di
		mov	ax,0601H	; scroll up one line
		mov	cx,0		; in the current window
		mov	dh,cs:wd_height
		mov	dl,cs:wd_width
		mov	bh,cs:color
		int	10H
		pop	di		; restore caller's registers
		pop	si
		pop	dx
		pop	cx
		pop	bx
		pop	ax

		mov	dh,cs:bot_edge		; now set cursor to bottom

adjxy1:		add	dl,cs:lf_edge
		cmp	dl,cs:rt_edge
		jle	adjxy2
		mov	dl,cs:rt_edge

		; fix up stack so BIOS's IRET will return to
		; our adjxy3 label

adjxy2:		mov	cs:temp,ax	; save ax (can't push it!)
		push	ax		; a dummy flag register save
		push	cs		; return cs
		mov	ax,offset adjxy3 ; return ip
		push	ax
		mov	ax,cs:temp	; restore saved ax
		jmp	callbios	; and call the BIOS

adjxy3:
		pop	dx		; back from BIOS now: restore caller's
		iret			; dx, and return.

;
; adjust corners for scroll
;
adjsc:
		push	bx
		add	ch,cs:top_edge	; adjust row posn of upper left
		cmp	ch,cs:bot_edge
		jle	adjsc1
		mov	ch,cs:bot_edge
adjsc1:		add	cl,cs:lf_edge	; adjust column posn of upper left
		cmp	cl,cs:rt_edge
		jle	adjsc2
		mov	cl,cs:rt_edge
adjsc2:		add	dh,cs:top_edge	; adjust row posn of lower right
		cmp	dh,cs:bot_edge
		jle	adjsc3
		mov	dh,cs:bot_edge
adjsc3:		add	dl,cs:lf_edge	; adjust column posn of lower right
		cmp	dl,cs:rt_edge
		jle	adjsc4
		mov	dl,cs:rt_edge
adjsc4:		mov	bl,cs:bot_edge	; adjust number of lines to scroll
		sub	bl,cs:top_edge
		cmp	al,bl
		jle	adjsc5
		mov	al,bl
adjsc5:		pop	bx
		jmp	callbios

readcsr:				; read cursor position
		push	ds		; save registers that must be kept
		push	bx

		mov	ax,40H		; switch to ROM BIOS data segment
		mov	ds,ax
		mov	bl,bh		; get param page number into bx
		xor	bh,bh
		sal	bx,1		; convert to cursor table offset
		mov	dx,[bx+50H]	; read csr posn from cursor table
		mov	cx,word ptr 60H ; read csr mode from mode word
		push	cs		; switch to our data segment
		pop	ds
		sub	dh,top_edge	; adjust position for window corner
		sub	dl,lf_edge

		; make sure cursor address is in range -- adjust if not

		cmp	dh,0		; check not above first row
		jge	lrowok
		mov	dh,0
lrowok:		cmp	dh,wd_height	; check not below last row
		jle	hrowok
		mov	dh,wd_height
hrowok:		cmp	dl,0		; check not left of first column
		jge	lcolok
		mov	dl,0
lcolok:		cmp	dl,wd_width	; check not right of last column
		jle	hcolok
		mov	dl,wd_width
hcolok:
		pop	bx		; restore bx register
		pop	ds		; switch to caller's data segment
		iret			; and return


ourfn:
		;
		; Process one of our functions.
		;

		cld		; save all registers except ax
		push	es
		push	ds
		push	dx
		push	cx
		push	bx
		push	si
		push	di

		push	cs	; switch to our data/code segment
		push	cs
		pop	ds
		pop	es

		; find what routine they are invoking and call it

		cmp	ah,14
		jne	newfns

		call	wtty
		jmp	ourret

newfns:
		mov	al,ah
		xor	ah,ah
		sub	al,40H
		sal	ax,1
		mov	si,ax
		cmp	ax,tablen
		jb	validjmp
		jmp	ourret
validjmp:
		call	word ptr [si+offset jmptab]

ourret:
		pop	di
		pop	si
		pop	bx
		pop	cx
		pop	dx
		pop	ds
		pop	es
		clc
		cmp	ax,0	; did we return an error code?
		je	ourretok ; no, leave carry cleared
		stc		; set carry
ourretok:
		ret	2	; discard flags from interrupt

disp		endp

;
; setwindow - set corners of the current display window.
; (ch,cl) = (row,column) of upper left corner
; (dh,dl) = (row,column) of lower right corner
;

setwindow	proc	near

	mov	lf_edge,cl
	mov	top_edge,ch
	mov	rt_edge,dl
	mov	bot_edge,dh
	mov	al,dh		; compute relative bottom corner of window
	sub	al,ch
	mov	wd_height,al
	mov	al,dl
	sub	al,cl
	mov	wd_width,al
	mov	ax,0		; always return successful
	ret

setwindow	endp

;
; setcolor - set attribute byte for display to al
;

setcolor	proc	near

	mov	color,al
	ret

setcolor	endp

;
; cls - clear the current window
;

cls	proc	near

	mov	cx,0
	mov	dh,bot_edge
	sub	dh,top_edge
	mov	dl,rt_edge
	sub	dl,lf_edge
	mov	ax,0600H
	mov	bh,color
	int	10H
	ret

cls	endp

;
; savqcalc - compute parameters for the save/restore window operation
; No input parameters.
; results: cx = number of words to xfer
;          bl = number of words to transfer per line
;	   bh = zero, used in counting words xferred on this line
;	   dx = amount to add to screen pointer to wrap to next line
;	   ax = same as cx
;
savqcalc	proc	near

	mov	al,bot_edge	; get number of words to xfer
	sub	al,top_edge	; compute number of lines in al
	inc	al
	mov	bl,rt_edge	; compute number of words per line in bl
	sub	bl,lf_edge
	inc	bl
	mov	dl,80		; get wraparound increment in dx = 80-bl
	xor	dh,dh
	sub	dl,bl		; compute word increment
	shl	dx,1		; convert to byte increment
	mul	bl		; multiply ax = al*bl
	mov	cx,ax		; gives word count to xfer, store in cx
				; bl still contains # words to xfer per line
	xor	bh,bh		; zero out bh to count up to bl before wrapping
	ret

savqcalc	endp
;
; pushwindow - push the current window state onto the window stack
;

pushwindow	proc	near

	; first, save the new corners on stack

	push	cx
	push	dx

	; now see if there's room in the window queue:
	; compute ax = (endrow-strtrow+1)*(endcol-strtcol+1)*2
	; add ax to queue pointer and see if it overflows qlim

	mov	al,dh		; get ending row
	sub	al,ch		; subtract starting row
	inc	al		; add 1

	mov	bl,dl		; get ending column
	sub	bl,cl		; subtract starting column
	inc	bl		; add 1

	mul	bl		; multiply the dimensions
	shl	ax,1		; and mult by 2 to convert to byte count

	add	ax,wqp		; add the byte count to the queue pointer
	cmp	ax,qlim		; compare against end of queue
	jl	pushok		; if less, go ahead

	pop	dx		; no room.  get back dx and cx
	pop	cx
	mov	ax,1		; set error code in ax
	jmp	nosav		; and don't do the save


	; save window corners and cursor position
pushok:
	sub	wsp,wssize	; adjust the stack pointer down a frame
	mov	bx,wsp
	mov	al,lf_edge
	mov	[bx],al
	mov	al,top_edge
	mov	1[bx],al
	mov	al,rt_edge
	mov	2[bx],al
	mov	al,bot_edge
	mov	3[bx],al
	mov	ah,3
	mov	bh,0
	int	10H		; get current cursor position
	mov	bx,wsp		; get window stack pointer back
	mov	4[bx],dx	; save current cursor position

	; set up the new window

	pop	dx		; get back the new window corners
	pop	cx

	call	setwindow	; set the window corners

	; now, save the area occupied by new window

	; compute area to be saved

	mov	al,top_edge	; get starting address in CRT segment
	mov	bl,160
	mul	bl
	add	al,lf_edge	; ax = top_edge*160 * lf_edge*2
	adc	ah,0
	add	al,lf_edge
	adc	ah,0
	mov	si,ax		; store it in the si

	call	savqcalc	; compute size of save area, etc.

	mov	di,wqp		; set di to next byte in window queue

	push	ds		; save ds for switch to screen segment
	mov	ax,crt_seg	; switch ds to screen segment
	mov	ds,ax

;;;	assume	ds:0B000H

	cld
				; save address of screen save area on stack
	push	di		; so we can get it back later

pshmov:
	movsw			; move a word
	inc	bh		; inc count of words moved
	cmp	bh,bl		; done with this line?
	jge	pshnxt		; yes, go move to next line
	loop	pshmov
	jmp	pshdon
pshnxt:
	add	si,dx		; increment si by wraparound increment
	xor	bh,bh		; zero out the words-per-line counter
	loop	pshmov		; and go move next word

pshdon:				; here when finished saving
	mov	ax,di		; remember next free mem address

	pop	di		; get back the starting address of save area
	pop	ds		; get back our data segment

;;;	assume	ds:dseg

	mov	wqp,ax		; now set wqp to next free mem address
	mov	bx,wsp		; get window stack pointer back again
	mov	6[bx],di	; save screen save area address on stack
	mov	8[bx],es	; (this word is currently always our ds)

	mov	ax,0		; set no-error code
nosav:
	ret

pushwindow	endp


;
; rstwindow - restore most recent window state from stack
;
rstwindow	proc	near

	; first make sure there's a window on the stack

	cmp	wsp,offset wsend
	jl	rstok
	mov	ax,1		; nothing on stack. set error code in ax
	jmp	norst		; and don't restore

rstok:
	; restore screen contents

	mov	bx,wsp		; get window stack pointer
	mov	al,10[bx]	; delete the frames around the window
	sub	lf_edge,al
	sub	top_edge,al
	add	rt_edge,al
	add	bot_edge,al
	shl	al,1
	add	wd_width,al
	add	wd_height,al
	mov	byte ptr 10[bx],0 ; now there are no frames around the window

	push	es
	mov	di,crt_seg	; get address of screen segment into es
	mov	es,di

;;;	assume	es:0B000H

	; compute area to be restored

	mov	al,top_edge	; get starting address in CRT segment
	mov	bl,160
	mul	bl
	add	al,lf_edge	; ax = top_edge*160 + lf_edge*2
	adc	ah,0
	add	al,lf_edge
	adc	ah,0
	mov	di,ax		; store it in the di

	mov	bx,wsp		; get back our window stack pointer
	mov	si,6[bx]	; get address of window save area
	mov	wqp,si		; delete save area while we've got the address

	call	savqcalc

	cld
rstmov:
	movsw			; move a word
	inc	bh		; inc count of words moved
	cmp	bh,bl		; done with this line?
	jge	rstnxt		; yes, go move to next line
	loop	rstmov
	jmp	rstdon
rstnxt:
	add	di,dx		; increment si by wraparound increment
	xor	bh,bh		; zero out the words-per-line counter
	loop	rstmov		; and go move next word

rstdon:				; here when finished saving
	pop	es		; get our es back

;;;	assume	es:dseg

	; restore the screen corners and cursor position

	mov	bx,wsp		; get stack frame
	mov	al,[bx]		; restore corners
	mov	lf_edge,al
	mov	al,1[bx]
	mov	top_edge,al
	mov	al,2[bx]
	mov	rt_edge,al
	mov	al,3[bx]
	mov	bot_edge,al

	mov	al,bot_edge	; compute relative window edges
	sub	al,top_edge
	mov	wd_height,al
	mov	al,rt_edge
	sub	al,lf_edge
	mov	wd_width,al

	mov	dx,4[bx]	; set cursor position
	mov	ah,2
	mov	bh,0
	int	10H


	add	wsp,wssize	; delete this stack frame
	mov	ax,0		; return no-error code
norst:
	ret			; and return

rstwindow	endp

;
; frame - draw a frame around the current window
;

frame	proc	near

	push	es		; switch es to crt segment
	mov	ax,crt_seg
	mov	es,ax

	mov	al,top_edge	; find starting address of window
	mov	bl,160		; ax = top_edge*160 + lf_edge*2
	mul	bl
	mov	bl,lf_edge
	shl	bl,1
	add	al,bl
	adc	ah,0
	mov	si,ax		; store it in the si for horizontals
	mov	di,ax		; and in di for drawing verticals

	mov	al,wd_height	; find distance to bottom line of window
	mov	bl,160		; ax = wd_height*160
	mul	bl
	mov	bx,ax		; store it in the bx

	mov	byte ptr es:[si],0daH ; put on the top and bottom corners
	mov	byte ptr es:[si+bx],0c0H
	inc	si
	mov	ah,color	; fill in attributes for corners
	mov	byte ptr es:[si],ah
	mov	byte ptr es:[si+bx],ah
	inc	si

	mov	cl,wd_width	; get width of window, minus 2
	xor	ch,ch
	dec	cx
	mov	al,0c4H		; get horizontal line into ax
	mov	ah,color

horiz:				; draw the 2 horizontals
	mov	word ptr es:[si],ax
	mov	word ptr es:[si+bx],ax
	add	si,2
	loop	horiz

	mov	al,0bfH		; top right corner
	mov	word ptr es:[si],ax
	mov	al,0d9H		; bottom right corner
	mov	word ptr es:[si+bx],ax

	mov	si,di		; get back starting address of window
	mov	bl,wd_width	; get width of window minus 1 into bx
	xor	bh,bh
	shl	bx,1
	add	si,160		; move down a line (don't overwrite corners)
	mov	cl,wd_height	; get height of window, minus 2, into cx
	xor	ch,ch
	dec	cx
	mov	al,0B3H		; get vertical line into al
	mov	ah,color	; get attribute into ah

vert:				; draw the two verticals
	mov	word ptr es:[si],ax
	mov	word ptr es:[si+bx],ax
	add	si,160
	loop	vert

	inc	lf_edge		; shrink the window
	inc	top_edge
	dec	rt_edge
	dec	bot_edge
	sub	wd_width,2
	sub	wd_height,2
	mov	bx,wsp
	inc	byte ptr 10[bx] ; increment count of frames

	pop	es
	ret

frame	endp


;
; wtty - same as function 14 in ROM BIOS, but uses windows.
;
wtty	proc	near

	; get CRT mode into ah and crt_mode
	; get active page into active_page

	push	es
	push	bx
	mov	bx,40H
	mov	es,bx
	mov	bx,62H		; 62H = offset active page
	mov	ah,es:[bx]
	mov	active_page,ah
	mov	bx,49H		; 49H = offset CRT mode
	mov	ah,es:[bx]
	mov	crt_mode,ah
	pop	bx
	pop	es

	push	ax		; save registers
	push	ax		; save char to write

	; read cursor position (inline for speed)

	push	ds		; save registers that must be kept
	push	bx

	mov	ax,40H		; switch to ROM BIOS data segment
	mov	ds,ax
	mov	bl,bh		; get param page number into bx
	xor	bh,bh
	sal	bx,1		; convert to cursor table offset
	mov	dx,[bx+50H]	; read csr posn from cursor table
	mov	cx,word ptr 60H ; read csr mode from mode word
	push	cs		; switch to our data segment
	pop	ds
	sub	dh,top_edge	; adjust position for window corner
	sub	dl,lf_edge

	pop	bx		; restore bx register
	pop	ds		; switch to caller's data segment

	; end of read-cursor-position

	pop	ax		; get back character

	cmp	raw,0		; raw I/O mode?
	jne	rawio		; yes, no special char xlation

	cmp	al,8		; BS?
	je	dobsp
	cmp	al,0dH		; CR?
	je	docr
	cmp	al,0aH		; LF?
	je	dolf
	cmp	al,07H		; BEL?
	je	dobel
	cmp	al,09H		; TAB?
	je	dotab

rawio:
	mov	bh,active_page	; set active page into bh which we got above
	mov	bl,color	; get color also (attribute byte)

	mov	ah,9		; write character/attribute BIOS call
	mov	cx,1		; write only once
	int	10H

	inc	dl		; advance cursor one position
	cmp	wrap,0		; wrap-around on?
	je	scrpos		; no, don't wrap
	cmp	dl,wd_width	; at the right edge of the screen?
	jnz	scrpos		; no, don't wrap
	mov	dl,0		; yes, move it to left edge
	cmp	dh,wd_height	; at bottom of screen?
	jnz	down1		; no, just move it down a line

sclreq:				; have to scroll
	mov	ah,2		; jmp here to set cursor position also
	mov	bh,0
	int	10H

	mov	al,crt_mode	; get the crt mode
	cmp	al,4
	jc	gcolor
	cmp	al,7
	mov	bh,color 	; fill with proper fg/bg 
	jne	scrl1

gcolor:
	mov	bh,color	; get filler fg/bg

scrl1:
	mov	ax,601H		; scroll one line
	mov	cx,0		; get current screen corners
	mov	dh,wd_height
	mov	dl,wd_width
xint10:
	int	10h		; do scroll - here also for gen'l int 10 call
wtret1: 			; jmp here to return from wtty
	pop	ax		; restore the character we wrote
	ret

down1:				; move cursor down a line
	inc	dh
scrpos:				; jmp here to do set-cursor call
	mov	ah,2
	jmp	xint10

dobsp:				; backspace
	cmp	dl,0		; do nothing if already at left edge
	je	scrpos
	dec	dl		; move back one position
	jmp	scrpos

docr:				; carriage return
	mov	dl,0
	jmp	scrpos

dolf:				; linefeed
	cmp	dh,wd_height
	jl	down1		; just move down
	jmp	sclreq		; scroll (*** fix- makes redundant set csr call!)

dobel:				; bell
	mov	bl,2
	call	beep
	jmp	wtret1

dotab:				; tab
	mov	ah,3
	mov	bh,0		; get cursor position
	int	10H
	add	dl,8		; increment it by 8
	and	dl,0F8H		; mask it down
	mov	ah,2		; set cursor position
	int	10H
	jmp	wtret1		; and return from call

wtty	endp

;
; beep the speaker for desired interval.  This is how IBM does it -- kind
; of a mystery why they didn't put a counter in the clk int routine to turn
; the beeper off to avoid busy-waiting...
; This beeper is set up to sound like the Z19 beeper, which I especially like.
;

beep	proc	near
	mov	al,10110110B	; command to timer - generate square wave
	out	43H,al
	mov	ax,0700H	; count to produce beep frequency
	out	42H,al
	mov	al,ah
	out	42H,al
	in	al,61H		; get orig. value in PPI with spkr enable bit
	mov	ah,al
	or	al,03		; turn on speaker enable, starting beep
	out	61H,al
	mov	cx,2000H	; this determines beep duration
bpwt:	loop	bpwt
	mov	al,ah		; turn speaker enable off, stopping beep
	out	61H,al
	ret
beep	endp

;
; everything below this point will be deleted from memory once the initial
; command exits
;

qbase		equ	this word
start		proc	near

		; determine what kind of CRT is in use

		int	11H
		and	ax,0030H
		cmp	ax,0030H
		jne	iscolor
		mov	crt_seg,0B000H
		jmp	setvec
iscolor:
		mov	crt_seg,0B800H

setvec:
		; set up the BIOS vectors

		mov	ax,0
		mov	es,ax
		mov	ax,word ptr es:40H
		mov	romoff,ax
		mov	ax,word ptr es:42H
		mov	romseg,ax

		mov	ax,offset disp
		mov	word ptr es:40H,ax
		mov	ax,cs
		mov	word ptr es:42H,ax

		mov	dx,offset qbase ; get starting address for window queue
		add	dx,wqsize	; allocate space for window queue
		mov	qlim,dx		; save end of queue to test later

		int	27H

start		endp

cseg		ends

		end	cpstart
