home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frostbyte's 1980s DOS Shareware Collection
/
floppyshareware.zip
/
floppyshareware
/
WLAX
/
ZAVT11.ZIP
/
ZAVT.ASM
< prev
next >
Wrap
Assembly Source File
|
1990-05-28
|
25KB
|
948 lines
page 58,132
;--- zavt.asm ----------------------------------------------------------
; Zephyr Avatar terminal driver.
; Copyright (C) 1989-1990, Luns Tee, Toronto Ontario
; Based on original code for ZANSI by Thomas Hanlin III, Alexandria VA.
; and original code for NANSI by Daniel Kegel, Pasadena CA.
;
; Revision History:
;------------------------------------------------------------------------
; Luns Tee, Toronto Ontario
; 19 Dec 1988: Replaced code to set 43 lines with code to check
; number of lines on screen through bios and then
; set internal paramaters accordingly
;
; 8 Mar 1989: Removed forementioned code completely and placed it
; with the code that determines screen size and video
; mode at each write. Also added routines for automatic
; support of different video pages and tidied up the
; TTY scroll
;
; 24 Mar 1989: Added code to enable/disable pseudocursor. Similar to
; enabling/disabling end of line wrap but with 45 instead
; i.e. esc[45l disables pseudocursor, esc[45h reenables
;
; 15 Apr 1989: Removed all the JMP $+2 from the cursor set routines as
; nobody could give a reason for their being there aside
; from them being artifacts from compiling a higher level
; language
;
; 17 Aug 1989; Added code to allow backspace across left margin as
; is required for upcoming (?) command line editor
; enhancement TSR
;
; 23 Aug 1989; Put in faster *10 code in parsing, fixed bug involving
; backspace after wraparound on screens of width >128
; and general maniacal cleaning up
;
; 26 Aug 1989; Fixed minor bug in beep routine as addressed in NANSI 22c
; and made the darned bell shorter
;
; 20 Sep 1989; added restriction that DSR paramater must be 6 (as
; mentioned inconsistently between sources) and centralized
; graphics mode checking to balance Mono and Color system
; speeds
;
; 29 Sep 1989; made max_x one based and saved 10 bytes, reversed
; strategy for HVP_SET and changed BYTEOUT to support
; screens up to 255x255 in size
;
; 16 Nov 1989; removed redundant code between getchar/peekchar as well as
; the interrupt functions, word aligned everything relevant,
; made check for non EGA screens part of init rather than
; part of normally resident interrupt routine, tightened
; backspace, reordered code to make for more jmp short rather
; than jmp near, rewrote parse routines so a colon not
; preceeded by a number acts as predicted, removed all KKR and
; kb remapping artifacts, fixed bug involving tabs over the
; right margin on screens of widths not a multiple of 8,
; referred byte requests for 0 to dh instead of a constant,
; put everything not needed as a word variable into equates,
; put register saving and compilation paramaters into macros
; in an include file ZANSI_D.ASM and rewrote SM/RM
;
; 26 Nov 1989; Modified scroll routine to better handle situations
; where screen lengths change leaving cursor off screen
; - formerly scrolled screen while writing outside of it - now
; scrolls region from start of screen to cursor bringing
; active screen area into view
;
; 27 Dec 1989; Added support for AVATAR/0 except for ^Y RLE (being planned)
; and added switches in ZANSI_D for AVATAR, direct scroll
; override (for back-scroll users), and left margin wrap
;
; 4 Feb 1990; Finished off RLE support, commented all code not in ZANSI12
; and renamed to ZAVT.
;
; 21 Feb 1990; Put 'hold's into cursor set routine for driver compiled for
; 286 as it is on the faster machines (wish I had one) that
; they are needed to give the system time to catch up
;
; 28 Feb 1990; Took code from PC-Mag's DOS-EDIT as grounds for a full-screen
; editor for DOS input. Effect is the similar, but code is now
; where it belongs saving some TSR conflicts
;
; 23 May 1990; Dug up NANSI code and replace keyboard remapping. Also
; reversed keybuf logic - strings are no longer stored
; inverted - to simplify code
;------------------------------------------------------------------------
include zavt_d.asm ; get equates
; from zavt_p.asm
extrn f_escape:near, f_in_escape:near
if avatar
extrn avt_cls:near, f_avatar:near
extrn dle:near, rle:near
endif
; from zavt_i.asm
extrn dosfn0:near
if fullscreen
extrn linebufend:word
endif
; to zavt_p.asm
public f_loopdone
public f_control
public f_looploop
public cur_parm_ptr
public escvector, string_term
if avatar
public f_nctl
public f_loop_nocheck
public gmode_flag
public putchar
endif
; to both zavt_p.asm and zavt_f.asm
public cur_x, cur_y, max_x, max_y, cur_attrib,string_attrib
; to zavt_f.asm
public pseudo_flag
public wrap_flag
public gmode_flag
public xy_to_regs, get_blank_attrib
public port_6845
public cur_coords, saved_coords
public max_coords
public cpr_esc, cpr_buf, cprseq
public video_mode
public screen_len
public cur_page
public video_off
; to zavt_i.asm
public hdrseg
public req_ptr, break_handler
public int_29
; from zavt_k.asm
extrn screenedit:near
extrn getchar:near, peekchar:near
; to zavt_k.asm
if fullscreen
public scrnbuf, pseudocursor
endif
if xlate
public xlatseq
endif
public fnkey, fnkeybuf
CODE segment byte public 'CODE'
assume cs:code, ds:code
; Device Driver Header
org 0
if two_handlers
dw header2
else
dw -1
endif
hdrseg dw -1 ; next device
dw 8013h ; attributes
dw strategy ; request header pointer entry
dw interrupt ; request entry point
db 'CON', 5 dup(' ') ; device name (8 char)
header2:
if two_handlers
dd -1 ; next device
dw 8013h ; attributes
dw strategy ; request header pointer entry
dw interrupt ; request entry point
db 'KEYB', 4 dup(' ') ; device name (8 char)
endif
; following three keybufs hold information about input
; Storage order determines priority- since the characters making up a function
; key code must never be separated (say, by a Control-Break), they have the
; highest priority, and so on. Keyboard keys (except ctrl-break) have the
; lowest priority.
fnkey keybuf <0, fnkeybuf> ; fn key string (0 followed by scan code)
cprseq keybuf <0> ; CPR string (ESC [ y;x R)
brkkey keybuf <0, brkkeybuf> ; ^C
if fullscreen
scrnbuf keybuf <0> ; line from full-screen editor
endif
if xlate
xlatseq keybuf <0> ; keyboard reassignment string
endif
;------- dos_fn_tab -------------
; This table is used in "interrupt" to call the routine that handles
; the requested function.
max_cmd equ 12
dos_fn_tab:
dw dosfn0, nopcmd, nopcmd, badcmd, dosfn4, dosfn5, dosfn6
dw dosfn7, dosfn8, dosfn8, nopcmd, nopcmd
;----- variable area --------------------
req_ptr label dword
req_off dw ?
req_seg dw ?
escvector dw 0 ; state vector of ESCape sequencer
pseudo_flag db 1 ; 1 = simulate cursor in graphics modes
wrap_flag db 1 ; 0 = no wrap past line end
video_mode db 3 ; ROM BIOS video mode (2=BW, 3=color)
gmode_flag db 0 ; 0 = text mode
max_coords label word
max_y db 24
max_cur_x label word ; used to get both max & cur at once
max_x db 80 ; line width (80 for 80x25 modes)
cur_coords label word
cur_x db 0 ; cursor position (0 = left edge)
cur_y db 0 ; (0 = top edge)
saved_coords dw ? ; holds XY after a SCP escape sequence
video_off dw 0 ; offset of current page into video buffer
screen_len dw ? ; bytes on screen (w attributes)
f_cptr_seg dw ? ; part of fastout write buffer pointer
cur_parm_ptr dw ? ; last byte of parm area now used
port_6845 dw ? ; port address of 6845 card
string_attrib label word ; to get the attribute pronto
string_term db 0 ; either escape or double quote
cur_attrib db 7 ; current char attributes
cur_page db 0 ; current display page
brkkeybuf db 3 ; control C
fnkeybuf db ? ; holds second byte of fn key codes
cpr_esc db 27,'[' ; descending buffer for cpr function
cpr_buf db 9 dup (?)
;------ xy_to_regs --------------------------------------------
; on entry: x in cur_x, y in cur_y
; on exit: dx = chars left on line, di = address
; Alters ax, bx.
xy_to_regs proc near
; Find number of chars til end of line, keep in DX
mov ax,max_cur_x
xor bx,bx ; BX = cur_x
xchg bl,ah ; AX = max_x
mov dx,ax
sub dx,bx ; DX is # of chars till EOL
; Calculate DI = current address in text buffer
mul cur_y
add ax,bx ; AX is # of chars into buffer
shl ax,1
add ax,[video_off]
mov di,ax ; DI is now offset of cursor.
ret
xy_to_regs endp
;------- strategy ----------------------------------------------------
; DOS calls strategy with a request which is to be executed later.
; Strategy just saves the request.
strategy proc far
mov cs:req_off,BX
mov cs:req_seg,ES
ret
strategy endp
;------ interrupt -----------------------------------------------------
; This is where the request handed us during "strategy" is
; actually carried out.
; Calls one of 12 subroutines depending on the function requested.
; Each subroutine returns with exit status in AX.
interrupt proc far
sti
push_all
; Read requested function information into registers
lds bx,cs:req_ptr
mov al,2[BX] ; al = function code
les si,14[BX] ; ES:SI = input/output buffer addr
mov cx,18[BX] ; cx = input/output byte count
cmp al,max_cmd
ja unk_command ; too big, exit with error code
mov bx,ax
shl bx,1 ; form index to table of words
mov ax,cs
mov ds,ax
call word ptr dos_fn_tab[bx]
int_done:
lds bx,cs:req_ptr ; report status
or ax,100h ; (always set done bit upon exit)
mov 3[bx],ax
pop_all ; restore caller's registers
ret ; return to DOS.
unk_command:
call badcmd
jmp int_done
interrupt endp
;----- BIOS break handler -----------------------------------------
; Called by BIOS when Control-Break is hit (vector was set up in Init).
; Simply notes that a break was hit. Flag is checked during input calls.
break_handler proc
mov cs:brkkey.len, 1
iret
break_handler endp
;------ badcmd -------------------------------------------------------
; Invalid function request by DOS.
badcmd proc near
mov ax, 813h ; return "Error: invalid cmd"
ret
badcmd endp
;------- dos function #4 -----------------------------------------------
; Reads CX characters from the keyboard, places them in buffer at ES:SI.
dosfn4 proc near
jcxz nopcmd
mov di,si
dos4lp: push cx
call getchar
pop cx
stosb
loop dos4lp
dosfn4 endp
;------ nopcmd -------------------------------------------------------
; Unimplemented or dummy function request by DOS.
; also used as a central not-busy exit for othere functions
nopcmd proc near
xor ax, ax ; No error, not busy.
ret
nopcmd endp
;-------- dos function #5: non-destructive input, no wait ------
; One-character lookahead into the keyboard buffer.
; If no characters in buffer, return BUSY; otherwise, get value of first
; character of buffer, stuff into request header, return DONE.
dosfn5 proc near
call peekchar
jz busy
lds bx,req_ptr
mov [bx+0Dh], al
jmp nopcmd ; No error, not busy.
dosfn5 endp
;-------- dos function #6: input status --------------------------
; Returns "busy" if no characters waiting to be read.
dosfn6 proc near
call peekchar
jnz nopcmd ; No error, not busy.
dosfn6 endp
;------ busy --------------------------------------------------------
; corollary to nopcmd
busy proc near
mov ax, 200h ; No error, busy.
ret
busy endp
;-------- dos function #7: flush input buffer --------------------
; Clears the IBM keyboard input buffer. Since it is a circular
; queue, we can do this without knowing the beginning and end
; of the buffer; all we need to do is set the tail of the queue
; equal to the head (as if we had read the entire queue contents).
; Also resets all the device driver's stuffahead buffers.
dosfn7 proc near
mov ax, abs40
mov es, ax
mov ax, es:buffer_head ; clear queue by making the tail
mov es:buffer_tail, ax ; equal to the head
xor ax, ax ; No error, not busy
mov fnkey.len, ax ; Reset the stuffahead buffers.
mov cprseq.len, ax
mov brkkey.len, ax
if fullscreen
mov scrnbuf.len, ax
endif
ret
dosfn7 endp
;------ int_29 ----------------------------------------------
; Int 29 handles DOS quick-access putchar.
; Last device loaded with attribute bit 4 set gets accessed for
; single-character writes via int 29h instead of via interrupt.
; Must preserve all registers.
; Installed as int 29h by dosfn0 (init).
int_29_buf db ?
int_29 proc near
sti
push_all
mov cx,1
mov bx,cs
mov es,bx
mov ds,bx
mov si,offset int_29_buf
mov [si],al
call dosfn8
pop_all
iret
int_29 endp
;------ dosfn8 -------------------------------------------------------
; Handles writes to the device (with or without verify).
; Called with
; CX = number of bytes to write
; ES:SI = transfer buffer
; DS = CS, so we can access local variables.
dosfn8 proc near
mov f_cptr_seg, es ; save segment of char ptr
; Read the BIOS buffer address/cursor position variables.
mov ax,abs40
mov ds,ax
assume ds:abs40
; Find current video mode and screen size.
mov ax, crt_len
mov cs:screen_len, ax
mov ax,word ptr crt_mode ; al = crt mode; ah = # of columns
mov cs:video_mode, al
mov al, crt_rows
mov cs:max_coords,ax ; one based
; Find current cursor coordinates.
mov al,active_page
mov cs:cur_page,al
cbw
shl ax,1
mov bx,ax
mov ax,cursor_posn[bx]
mov cs:cur_coords,ax
; Find video buffer segment address; adjust so ofs is 0; return in AX.
mov ax,crt_start
mov cs:video_off,ax
mov ax,addr_6845 ; 6845 address
mov dx,cs
mov ds,dx
assume ds:code
mov port_6845,ax
call xy_to_regs ; Set DX, DI according to cur_coords.
; | If in graphics mode, clear old pseudocursor
; and set graphics mode flag
mov bx,0B800h ; segment for colour text
mov al,video_mode
cbw
cmp al, 4
jb d8_no_cp
mov bh,0B0h ; if text, it's a monochrome card..
cmp al, 7
jz d8_no_cp
call pseudocursor ; write block in xor, make al nonzero
d8_no_cp:
mov es,bx
mov word ptr video_mode,ax ; store zero if text mode
mov ax, string_attrib
mov ds, f_cptr_seg ; get segment of char ptr
assume ds:nothing
cld ; make sure we'll increment
; Get a character, put it on the screen, repeat 'til end of line
; or no more characters.
jcxz f_loopdone ; if count = 0, we're already done.
cmp cs:escvector, 0 ; If in middle of an escape sequence,
jnz f_in_escapex ; jump to escape sequence handler.
f_tloop:
; If not in graphics mode, jump to alternate loop
; What a massive kludge! A better approach would have been
; to collect characters for a "write n chars" routine
; which would handle both text and graphics modes.
cmp cs:gmode_flag,dh
jnz f_g_cloop
f_t_cloop:
lodsb ; get char! (al = ds:[si++])
cmp al,28 ; is it a control char?
jb f_control ; maybe...
f_t_nctl:
stosw ; Put Char! (es:[di++] = ax)
dec dx ; count down to end of line
loopnz f_t_cloop ; and go back for more.
jnz f_loopdone
f_at_eol: ; at end of line; maybe do a crlf.
;----- Handle overrunning right end of screen -------
; cx++; compensate for double loop
; if (!wrap_flag) { dx++; di-=2; }
; else do_crlf;
inc cx
cmp cs:wrap_flag, dh
jz reverse
feol_wrap:
; dx=max_x; set bx= chars left in line
; di -= 2*(max_x);
; do_lf
mov dl, cs:max_x
sub di, dx
sub di, dx
jmp f_lf
f_g_cloop:
lodsb ; get char! (al = ds:[si++])
cmp al,28 ; is it a control char?
jb f_control ; maybe...
f_g_nctl:
call putchar
dec dx ; count down to end of line
loopnz f_g_cloop ; and go back for more.
jmp short f_t_at_eol
f_looploop:
or dx,dx
f_loop_nocheck: ; in case we switched into
loopnz f_tloop ; a graphics mode
f_t_at_eol:
jz f_at_eol
f_loopdone:
;--------- All done with write request -----------
; DI is cursor address; cursor position in cur_y, dl.
mov ax, cs
mov ds, ax ; get our segment back
assume ds:code
; Restore cur_x = max_x - dx
mov al, max_x
sub al, dl
mov cur_x, al
; Set cursor position; cursor adr in DI; cursor pos in cur_x,cur_y
call set_pseudocursor
; Return to DOS.
xor ax, ax ; No error, not busy.
ret
f_in_escapex:
jmp f_in_escape
f_bs: ;----- Handle backspace -----------------
; Moves cursor back one space without erasing. No wraparound.
cmp dl, cs:max_x ; wrap around to previous line?
jb reverse ; if not, back up
if wrap_left
fbs_wrap: ; else do some checks
cmp cs:wrap_flag,dh ; do we want a wrap?
jz f_looploop ; no; leave
cmp cs:cur_y,dh ; top left corner?
jz f_looploop ; if so, ditto
dec cs:cur_y
mov dl,dh ; else 0 char left on line
else
jmp f_looploop
endif
reverse:
dec di ; back up one char & attrib
dec di
inc dx ; and note one more char left on line.
jmp f_loop_nocheck
assume ds:nothing
;---- handle control characters ----
; Note: cur_x is not kept updated in memory, but can be
; computed from max_x and dx.
; Cur_y is kept updated in memory.
f_control:
cmp al,13 ; carriage return?
jz f_cr
cmp al,10 ; line feed?
jz f_lf
cmp al,27 ; Is it an escape?
jz f_escapex
cmp al,8 ; backspace?
jz f_bs
cmp al,9 ; tab?
jz f_tabx
cmp al,7 ; bell?
jz f_bell
if avatar
cmp al,12 ; AVT clearscreen?
jz avt_clsx
cmp al,22 ; AVT escape?
jz f_avatarx
cmp al,16 ; DLE?
jz dlex
cmp al,25 ; RLE?
jz rlex
f_nctl:
endif
; not a control char
cmp cs:gmode_flag,dh
jnz f_g_nctl
jmp f_t_nctl
f_escapex:
jmp f_escape
if avatar
avt_clsx:
jmp avt_cls
f_avatarx:
jmp f_avatar
dlex:
jmp dle
rlex:
jmp rle
endif
f_bell: ;----- Handle bell ----------------------
call beep ; >DING<
jmp f_looploop ; Let main loop decrement cx.
f_cr: ;----- Handle carriage return -----------
; di -= cur_x<<1; set di= address of start of line
; dx=max_x; set bx= chars left in line
mov al, cs:max_x
mov ah,dh
sub ax,dx ; Get cur_x into ax.
sub di,ax
sub di,ax
add dx,ax
mov ax,cs:string_attrib ; restore current attribute
jmp f_loop_nocheck ; and let main loop decrement cx
f_tabx: jmp f_tab
f_lf: ;----- Handle line feed -----------------
; if (cur_y >= max_y) scroll; scroll screen up if needed
; else { cur_y++; di += max_x; else increment Y
mov al,cs:Cur_y
sub al,cs:Max_y ; do we need to scroll screen?
jb flf_noscroll ; yes, do it
sub cs:cur_y,al
inc ax
push cx
push dx
call get_blank_attrib ; ah is attribute to use
mov bh,ah ; color to use on new blank areas
xor cx,cx
if direct_scroll
cmp cs:gmode_flag,dh
jnz flf_scrollit
mov ah,dh ; ax has count of lines to scroll
mov cl,cs:max_x ; cx
shl cx,1 ; counted in bytes
mul cx ; multiply it by the overflow
mov dx,cx ; save our screen width
sub di,ax ; bring back pointer scrolled distance
add di,dx ; then add one blank line
xchg bx,ax ; and save it for later
push ds
push si
push di
mov cx,es
mov ds,cx ; set DS to video segment
mov di,cs:[video_off] ; and SI and DI to start of screen
mov si,di
add si,bx
mov cx,cs:[screen_len] ; get the length of the screen
sub cx,dx ; less however many lines
shr cx,1 ; in words
rep movsw ; scroll it
mov cx,bx ; CX is chars per line
shr cx,1
mov al," " ; AH still blank attribute
rep stosw ; clear the bottom line
pop di
pop si
pop ds
else
jmp short flf_scrollit
endif
flf_scroll_done:
pop dx
pop cx
mov ax,cs:string_attrib ; restore current attribute
jmp f_looploop ; and let main loop decrement cx
flf_scrollit:
mov dl,al
add dx,cs:max_coords
dec dx
xchg dl,dh
dec dx
mov ah,06 ; BIOS scroll al lines.
int 10h ; call BIOS to scroll a rectangle.
jmp short flf_scroll_done
flf_noscroll:
inc cs:cur_y
mov al,cs:Max_x
mov ah,dh
shl ax,1
add di,ax
mov ax,cs:string_attrib ; restore current attribute
jmp f_looploop ; and let main loop decrement cx
f_tab: ;----- Handle tab expansion -------------
push cx ; save cx
; Calculate number of spaces to output.
mov cx,dx
sub cl,cs:max_x ; cx=0-cur_x
dec cx ; raise floor and ceiling
and cx,7 ; 0-7
inc cx ; 1-8
; ah is still current attribute. Move CX spaces to the screen.
mov al, ' '
cmp cs:gmode_flag,dh
jnz f_tp_lp
sub dx, cx ; update chars-to-eol, maybe set z
jae nochop ; in case width is not a multiple of 8
add cx,dx ; chop the tab so we don't get a neg DX
xor dx,dx
nochop: rep stosw
pop cx ; restore cx
jmp f_loop_nocheck ; Let main loop decrement cx.
;--------------- graphics mode support -----------------------
f_tp_lp: ; graphics mode- call putc to put the char
call putchar
dec dx ; go to next cursor position
loopnz f_tp_lp
pop cx
jmp f_loop_nocheck
;---- set_pseudocursor ------------
; If in graphics mode, set pseudocursor, else set real cursor.
; Destroys DS!!!!
assume ds:code
set_pseudocursor proc near
cmp gmode_flag,dh
jnz pseudocursor
SET_CURS: ; Write directly to 6845 cursor address register.
mov bx,di
shr bx,1 ; convert word index to byte index
mov dx,Port_6845
mov al,0Eh
out dx,al
hold
inc dx
mov al, bh
out dx, al
hold
dec dx
mov al, 0fh
out dx, al
hold
inc dx
mov al, bl
out dx, al
mov al,cur_page
cbw
shl ax,1
mov bx,ax
mov ax,cur_coords
; Set cursor position in low memory.
mov dx, abs40
mov ds, dx
assume ds:abs40
mov cursor_posn[bx],ax
ret
set_pseudocursor endp
assume ds:code
;---- pseudocursor --------------------------------------------------
; If pseudo_flag is true, writes a color 15 block in XOR at the
; current cursor location, and sets cursor position.
; Otherwise, just sets cursor position.
pseudocursor proc near
cmp pseudo_flag, 0
jnz psc_pseudo
push dx ; flag off - don't draw
mov dx, cur_coords ; get X & Y into DX
mov bh, cur_page ; supposed to be zero in graph modes?
mov ah, 2 ; chose "Set Cursor Position"
int 10h ; call ROM BIOS
pop dx
ret
psc_pseudo:
mov ax, 8F16h ; xor, color 15, ^V (small block)
; fall through to putchar
pseudocursor endp
;---- putchar ------------------------------------------------
; Writes char AL, attribute AH to screen at (max_x+1-dl), cur_y.
; On entry, registers set up as per xy_to_regs.
; Preserves all registers.
assume ds:nothing
putchar proc near
push ax
push bx
push cx
push dx
; 1. Set cursor position.
sub dl, cs:max_x
neg dx
mov dh, cs:cur_y ; get X & Y into DX
mov bh, cs:cur_page ; choose page
mov bl,ah ; attribute in BL for part 2.
mov ah, 2 ; chose "Set Cursor Position"
int 10h ; call ROM BIOS
; 2. Write char & attribute.
mov cx,1
mov ah,9
int 10h
pop dx
pop cx
pop bx
pop ax
ret
putchar endp
;--------------- end of graphics mode support --------------------
dosfn8 endp
;--- get_blank_attrib ------------------------------------------------
; Determine new attribute and character for a new blank region.
; Use current attribute, just disallow blink and underline.
; (Pretty strange way to do it. Might want to disallow rev vid, too.)
; Returns result in AH, preserves all other registers.
get_blank_attrib proc near
xor ah,ah
cmp cs:gmode_flag,ah
jnz gb_aok ; if graphics mode, 0 is bkgnd
mov ah, cs:cur_attrib
and ah,7fh ; disallow blink
cmp cs:video_mode,7 ; monochrome?
jne gb_aok
cmp ah,1 ; underline?
jne gb_aok
mov ah,7 ; yep- set it to normal.
gb_aok: ret
get_blank_attrib endp
;---- beep ------------------------------------------------------
; Beep speaker; period given by beep_div, duration by beep_len.
; Preserves CX and DX
beep_div equ 1300 ; fairly close to IBM beep
beep_len equ 2 ; 2/18 sec- shorter than IBM
beep proc near
push cx
push dx
mov al,10110110b ; select 8253
mov dx,43h ; control port address
out dx,al
dec dx ; timer 2 address
mov ax, beep_div
out dx,al ; low byte of divisor
mov al,ah
out dx,al ; high byte of divisor
mov dx,61h
in al,dx ; get current value of control bits
push ax
or al, 3
out dx,al ; turn speaker on
; Wait for desired duration by monitoring time-of-day 18 Hz clock
push ds
mov ax,abs40
mov ds,ax
assume ds:abs40
mov bx, timer_low
mov cx, -1 ; emergency, in case clock dead
beeplp: mov ax, timer_low
sub ax,bx
cmp ax, beep_len
jg beepover
loop beeplp
beepover:
pop ds
assume ds:nothing
pop ax
and al, not 3 ; turn speaker off
out dx,al
pop dx
pop cx
ret
beep endp
CODE ends
end