home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1993 May
/
SIMTEL_0593.ISO
/
msdos
/
screen
/
wbios.asm
< prev
next >
Wrap
Assembly Source File
|
1984-06-17
|
23KB
|
1,065 lines
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