;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; VIDEO HARDWARE MODULE
;;;; (C) Copyright 1995 Gregory Ercolano
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; INITIALIZE SCREEN DRIVER MODULE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
initvideo proc near
;;; INIT SCROLLBACK
ifdef PROM
;; ON A PROM? USE MOST OF A 64K SEGMENT FOR SCROLL BUFFER
mov [scrbufin],0000h
mov [scrbufinseg],1000h
mov [scrbufstart],0000h
mov [scrbufstartseg],1000h
mov [scrbufend],SCRBUF_SIZE
mov [scrbufendseg],1000h
mov si,1000h
mov di,0000h
mov cx,SCRBUF_SIZE
mov al,0
call memset ;SI:DI filled with value in AL for CX bytes
else
mov [scrbufin], offset scrbuf
mov [scrbufinseg],cs
mov [scrbufstart], offset scrbuf
mov [scrbufstartseg],cs
mov [scrbufend], offset scrbuf+SCRBUF_SIZE
mov [scrbufendseg],cs
mov si,[scrbufinseg]
mov di,offset scrbuf
mov cx,SCRBUF_SIZE
mov al,0
call memset ;SI:DI filled with value in AL for CX bytes
endif
;;; INIT CURSOR'S APPEARANCE
mov ah,0ah ; 6845 register for cursor set
mov cx,000dh ; block cursor (all scanlines on)
call send_6845
;;; INIT MISC VIDEO VARIABLES
mov [phystop],00d ; set physical top line
mov [physbottom],23d ; set physical bottom line
mov [physleft],00d ; set physical left edge
mov [physright],79d ; set physical right edge
call resetscrollregion ; set logical top/bottom/left/right edges
mov [curx],0h
mov [cury],0h ; home cursor
mov [curxsave],0h
mov [curysave],0h
mov [vidbackscroll],0h ; back scroll index (0=in active display)
mov [hexdebug_x],0h ; hex debug byte position on line
mov [vidflags],NO_WRAP ; initial video flags
call curupdate ; update hardware cursor
mov [videsc],0 ; clear esc modes
call clearescbuf ; clear the escape buffer
mov [vidcolor],07h ; normal screen color
mov al,1
call cleartabstops ; reset all tabstops to every 8 chars
mov [vidblank],0 ; no blanked screen
;;; GET SEGMENT ADDRESS OF VIDEO RAM
mov bx,0b800h ; assume graphics card
push ds
mov ax,0040h
mov ds,ax
mov ax,[ds:0010h] ; equipment flag
and ax,30h ; CRT bits
cmp ax,30h ; monochrome?
jne iscga ; no
mov bx,0b000h
iscga:
pop ds
mov [vidseg],bx ; save segment address
ret
initvideo endp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRINT CHARACTER IN AL
;; Handles ANSI modes
;; Handles DEBUG modes
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
printchar proc near
test [vidflags],HEX_DEBUG
jf realprintchar
jmp hexdebug
realprintchar:
push ax
mov al,[bottom]
cmp [cury],al ; ENSURE WE NEVER WRITE BELOW BOTTOM LINE
jle pc_nostat
mov [cury],al ; clamp to bottom line
pc_nostat:
pop ax
cmp [videsc],0 ; first see if this is part of an ESC sequence
je pc_xescmode
call videscsequence ; HANDLE ESCAPE SEQUENCE IN PROGRESS
jmp printchardone
pc_xescmode:
cmp al,20h ; CONTROL CHAR? Do tests to handle it
jl pc_xprintable
printrawchar:
; WRITE THE RAW CHARACTER TO SCREEN, NO MATTER WHAT IT LOOKS LIKE
; Advance the cursor, as necessary.
;
push ax
call vidaddr ; AX returns offset address
mov di,ax
pop ax
mov ah,[vidcolor] ; hi byte is current attribute
stosw ; write the character and attribute
inc [curx] ; adjust cursor
push ax
mov al,[right]
cmp [curx],al ; hit right edge?
pop ax
jg pc_offedge
jmp printchardone
pc_offedge:
test [vidflags],NO_WRAP ; don't wrap when we hit edge?
jt pc_nowrap
push ax
mov al,[left] ; wrap to left edge
mov [curx],al
pop ax
jmp pc_lf ; advance to next line
pc_nowrap:
push ax
mov al,[right]
mov [curx],al ; clamp cursor to right edge
pop ax
jmp printchardone
pc_xprintable:
cmp al,00h ; VT100: 00h - NULL?
jnz pc_x00
jmp printchardone ; take no action (VT100 manual, pp.42)
pc_x00:
cmp al,07h ; VT100: 07h - BELL?
jne pc_x07
; INVERT THE SCREEN FOR 4 TIMER TICKS
call vidsavescrn
call vidinvert
mov cx,2 ; 2 ticks is approx 1/8 sec
call wait_ticks
call vidresscrn ; restore screen/cursor
jmp printchardone
pc_x07:
cmp al,08h ; VT100: 08h - BACKSPACE?
jne pc_x08
push ax
mov al,[left]
cmp [curx],al ; already at or beyond left edge?
jg pc_bs
mov [curx],al ; clamp to left edge
pop ax
jmp printchardone ; do not wrap around (VT100 manual, pp.42)
pc_bs:
pop ax
dec [curx] ; move back
jmp printchardone
pc_x08:
cmp al,09h ; VT100: 09h - TAB?
jne pc_x09
mov ah,0
mov al,[curx]
mov si,offset tabstops
add si,ax ; index into tabstop table
; FIND THE NEXT NEAREST TABSTOP TO THE RIGHT
pc_tabloop:
inc si ; move to right one char
inc al
cmp al,[right] ; hit right edge?
jle pc_xtabhitedge
mov al,[right] ; stop at right edge
jmp pc_tabsetpos
pc_xtabhitedge:
cmp byte ptr [ds:si],1 ; found tabstop yet?
jne pc_tabloop
pc_tabsetpos:
mov [curx],al
jmp printchardone
pc_x09:
cmp al,0ah ; VT100: 0ah - LINEFEED?
jne pc_x0a
pc_lf:
call linefeed
jmp printchardone
pc_x0a:
cmp al,0bh ; VT100: 0bh - VERTICAL TAB?
je pc_lf ; handle as line feed, VT100 Manual, pp.42
cmp al,0ch ; VT100: 0ch - FORM FEED?
je pc_lf ; handle as line feed, VT100 Manual, pp.42
cmp al,0dh ; VT100: 0dh - CARRIAGE RETURN?
jne pc_x0d
push ax
mov al,[left]
mov [curx],al ; force cursor to left edge
pop ax
jmp printchardone
pc_x0d:
cmp al,1bh ; VT100: 1bh - ESC?
jne pc_x1b
call videscsequence
jmp printchardone ; and otherwise ignore.
pc_x1b:
cmp al,7fh ; VT100: 7fh - DEL?
jne pc_x7f
jmp printchardone ; do not print, VT100, pp.42
pc_x7f:
jmp printrawchar ; whatever the char is, it's printable after all
printchardone:
call curupdate ; update hardware cursor, whatever we did to it
ret ; done
printchar endp
;
; HANDLE ESCAPE SEQUENCE CHARACTERS
; Buffered escape sequence includes leading ESC..
;
videscsequence proc near
; BUFFER THE ESC SEQUENCE
push si
mov si,[videsc]
add si,offset escbuf
mov [ds:si],al ; buffer the sequence
inc si
inc [videsc]
cmp si,offset escbufend ; buffer overrun?
pop si
jl ves_xescbufend ; no.
jmp ves_clearesc ; buffer overrun. cancel esc sequence
ves_xescbufend:
cmp [videsc],1 ; first character in ESC sequence?
jnz ves_x0
;;;;; HANDLE FIRST CHARACTER IN ESC SEQUENCE
ret ; do nothing if just the ESC received
ves_x0:
cmp [videsc],2 ; second character in ESC sequence?
je ves_2
jmp ves_x2
ves_2:
;;;;; HANDLE SECOND CHARACTER IN ESC SEQUENCE
cmp al,'[' ; VT100: ESC '['?
jne ves_xbrackchar
ret ; ignore. (part of larger sequence)
ves_xbrackchar:
cmp al,'#' ; VT100: ESC '#'?
jne ves_xpndchar
ret ; ignore. (part of larger sequence)
ves_xpndchar:
cmp al,'Z' ; VT100: ESC 'Z'? (TERMINAL IDENT)
jne ves_xescz
mov si,offset esczresponsemsg
call serialoutstring
jmp ves_clearesc
ves_xescz:
cmp al,'=' ; VT100: ESC '='? (IGNORE: APPL MODE)
jne ves_xescequal
; NOT SUPPORTED: keypad application mode
jmp ves_clearesc
ves_xescequal:
cmp al,'>' ; VT100: ESC '>'? (IGNORE: NUM MODE)
jne ves_xescgt
; NOT SUPPORTED: keypad numeric mode
jmp ves_clearesc
ves_xescgt:
cmp al,'7' ; VT100: ESC '7'? (SAVE CURSOR)
jne ves_xseven
; SAVE CURSOR POSITION
mov al,[curx] ; save cursor position
mov [curxsave],al
mov al,[cury]
mov [curysave],al
jmp ves_clearesc
ves_xseven:
cmp al,'8' ; VT100: ESC '8'? (RESTORE CURSOR)
jne ves_xesc8
; RESTORE PREVIOUS CURSOR POSITION
mov al, [curxsave] ; restore cursor position
mov [curx],al
mov al, [curysave]
mov [cury],al
jmp ves_clearesc
ves_xesc8:
cmp al,'D' ; VT100: ESC 'D'? (LINE FEED)
jne ves_xescd
call linefeed ; handle like a LF (VT100, pp.53)
jmp ves_clearesc
ves_xescd:
cmp al,'E' ; VT100: ESC 'E'? (CRLF)
jne ves_xesce
mov [curx],0
call linefeed ; handle like a CRLF (VT100, pp.53)
jmp ves_clearesc
ves_xesce:
cmp al,'H' ; VT100: ESC 'H'? (SET TAB STOP)
jne ves_xesch
mov si,offset tabstops
mov al,[curx]
mov ah,0
mov byte ptr [ds:si],1 ; set a tab stop at current xpos
jmp ves_clearesc
ves_xesch:
cmp al,'M' ; VT100: ESC 'M'? (REVERSE LF)
jne ves_xesccapm
call revlinefeed ; handle as reverse LF (VT100, pp.53)
jmp ves_clearesc
ves_xesccapm:
cmp al,'c' ; VT100: ESC 'c'? (RESET TERMINAL)
jne ves_xescc
jmp master_init ; 'reset terminal'
ves_xescc:
; UNKNOWN ESC <> SEQUENCE. CANCEL MODE
jmp ves_clearesc
ves_x2:
cmp byte ptr [escbuf+1],'#' ; are we in ESC '#' mode?
je ves_pnd
jmp ves_xpnd
ves_pnd:
cmp [videsc],3 ; third char in sequence?
je ves_pnd3rd
jmp ves_pndx3rd
ves_pnd3rd:
cmp al,'8' ; VT100: ESC # 8? (SCREEN OF 'E'S)
jne ves_xescpnd8
; FILL SCREEN WITH E's
mov ax,0745h
call vidclear
jmp ves_clearesc
ves_xescpnd8:
; UNKNOWN ESC'#'<> SEQUENCE. CANCEL MODE
jmp ves_clearesc
ves_pndx3rd:
; UNKNOWN ESC'#'<><>.. SEQUENCE. CANCEL MODE
jmp ves_clearesc
ves_xpnd:
cmp byte ptr [escbuf+1],'[' ; are we in ESC '[' mode?
je ves_brackmode
jmp ves_xescbrackmode
ves_brackmode:
cmp al,';' ; HANDLE NUMBERIC ###;### ARGS
je ves_brackarg
cmp al,'9'
jg ves_xbrackarg
cmp al,'0'
jl ves_xbrackarg
ves_brackarg:
ret ; simply return, with the arg buffered
ves_xbrackarg:
cmp al,'A' ; VT100: ESC [ Ps A (CURSOR UP)
jne ves_xescbracka
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0100h ; default
call escparseint
mov al,[cury]
sub al,ah
ves_ywrap:
cmp al,0
jge ves_escbracka_ywrap
mov al,0
ves_escbracka_ywrap:
cmp al,[physbottom]
jle ves_escbracka_ywrap2
mov al,[physbottom]
ves_escbracka_ywrap2:
mov [cury],al
call curupdate
jmp ves_clearesc
ves_xescbracka:
cmp al,'B' ; VT100: ESC [ Ps B (CURSOR DOWN)
jne ves_xescbrackb
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0100h ; default
call escparseint
mov al,[cury]
add al,ah
jmp ves_ywrap
ves_xescbrackb:
cmp al,'C' ; VT100: ESC [ Ps C (CURSOR RIGHT)
jne ves_xescbrackc
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0100h ; default
call escparseint
mov al,[curx]
add al,ah
ves_xwrap:
cmp al,0
jge ves_escbracka_xwrap
mov al,0
ves_escbracka_xwrap:
cmp al,[physright]
jle ves_escbracka_xwrap2
mov al,[physright]
ves_escbracka_xwrap2:
mov [curx],al
call curupdate
jmp ves_clearesc
ves_xescbrackc:
cmp al,'D' ; VT100: ESC [ Ps D (CURSOR LEFT)
jne ves_xescbrackd
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0100h ; default
call escparseint
mov al,[curx]
sub al,ah
jmp ves_xwrap
ves_xescbrackd:
cmp al,'K' ; VT100: ESC [ Ps K (CLEOL)
jne ves_xescbrackk
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0000h ; default
call escparseint
;
; 00 - clear to end of line
; 01 - clear to start of line
; 02 - clear line
;
cmp ah,00d
jne ves_escbrackk_x00
push ax ; VT100: ESC [ 0 K (CLEAR END OF LINE)
push bx
mov al,[curx]
mov ah,[cury]
mov bl,[physright]
mov bh,[cury]
mov dx,CLEAR_SCRN_WORD
call clearxy2xy
pop bx
pop ax
jmp ves_clearesc
ves_escbrackk_x00:
cmp ah,01d
jne ves_escbrackk_x01
push ax ; VT100: ESC [ 1 K (CLEAR START OF LINE)
push bx
mov al,[physleft]
mov ah,[cury]
mov bl,[curx]
mov bh,[cury]
mov dx,CLEAR_SCRN_WORD
call clearxy2xy
pop bx
pop ax
jmp ves_clearesc
ves_escbrackk_x01:
cmp ah,02d
jne ves_escbrackk_x01
push ax ; VT100: ESC [ 2 K (CLEAR CURRENT LINE)
push bx
mov al,[physleft]
mov ah,[cury]
mov bl,[physright]
mov bh,[cury]
mov dx,CLEAR_SCRN_WORD
call clearxy2xy
pop bx
pop ax
ves_escbrackk_x02:
jmp ves_clearesc
ves_xescbrackk:
cmp al,'J' ; VT100: ESC [ Ps J (CLEAR TO EOS)
je ves_escbrackj
jmp ves_xescbrackj
ves_escbrackj:
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0000h ; default
call escparseint
;
; 00 - erase from cursor to EOS inclusive (DEFAULT)
; 01 - erase from cursor to TOS inclusive
; 02 - erase entire screen
;
cmp ah,00d
jne ves_xescbrackj00
push ax ; VT100: ESC [ 0 J (CLEAR TO EOS)
push bx
mov al,[curx]
mov ah,[cury]
mov bl,[physright]
mov bh,[physbottom]
mov dx,CLEAR_SCRN_WORD
call clearxy2xy
pop bx
pop ax
jmp ves_clearesc
ves_xescbrackj00:
cmp ah,01d
jne ves_xescbrackj01
push ax ; VT100: ESC [ 1 J (CLEAR TO TOS)
push bx
mov al,[physleft]
mov ah,[phystop]
mov bl,[curx]
mov bh,[cury]
mov dx,CLEAR_SCRN_WORD
call clearxy2xy
pop bx
pop ax
jmp ves_clearesc
ves_xescbrackj01:
cmp ah,02d
jne ves_xescbrackj02
push ax ; VT100: ESC [ 2 J (CLEAR SCREEN)
push bx
mov al,[physleft]
mov ah,[phystop]
mov bl,[physright]
mov bh,[physbottom]
mov dx,CLEAR_SCRN_WORD
call clearxy2xy
pop bx
pop ax
jmp ves_clearesc
ves_xescbrackj02:
jmp ves_clearesc
ves_xescbrackj:
cmp al,'f' ; VT100: ESC [ Pn;Pn f (CURSOR POSN)
je ves_escbrackh ; same as ESC [ Pn; Pn H
cmp al,'H' ; VT100: ESC [ Pn;Pn H
je ves_escbrackh
jmp ves_xescbrackh
ves_escbrackh:
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0101h ; default if numbers unspecified
call escparseint ; AL=X pos, AH=Y pos
call setcurpos ; handle 1 based #s, ORIGIN mode, etc
jmp ves_clearesc
ves_xescbrackh:
cmp al,'g' ; VT100: ESC [ Pv g (CLEAR TAB STOPS)
jne ves_xescbrackg ; (TAB STOPS)
mov si,offset escbuf
add si,2
mov al,0 ; assume clear all tabs to _nothing_
cmp byte ptr [ds:si],'3' ; Pv = 3? clear ALL tabs
jne ves_escgx3
mov al,1 ; use default tabstops every 8
ves_escgx3:
call cleartabstops ; AL=0 clear tabs, AL=1 default tabstops
jmp ves_clearesc
ves_xescbrackg:
;
; I YAM HERE:
; ENSURE WE SUPPORT MULTIPLE 'Pv' SPECS WITHIN ONE ESC [ SEQUENCE!
;
cmp al,'m' ; VT100: ESC [ Pv m (SET COLOR)
je ves_escm
jmp ves_xescbrackm
ves_escm:
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0000h ; default is reset to normal
call escparseint ; AH=subfunction index
mov al,ah
cmp al,00h ; VT100: ESC [ 0 m (RESET COLOR)
jne ves_escm_x0
mov [vidcolor],07h ; 'normal' video
jmp ves_escm_done
ves_escm_x0:
cmp al,01h ; VT100: ESC [ 1 m (BOLD COLOR)
jne ves_escm_x1
or byte ptr [vidcolor],00001000b ; set bold bit
jmp ves_escm_done
ves_escm_x1:
cmp al,02h ; VT100: ESC [ 2 m (HALF INTENS)
jne ves_escm_x2
and byte ptr [vidcolor],11110111b ; clear bold bit
jmp ves_escm_done
ves_escm_x2:
cmp al,04h ; VT100: ESC [ 4 m (UNDERLINE)
jne ves_escm_x4 ;
or byte ptr [vidcolor],00000001b; set underline bit (on MDA only)
jmp ves_escm_done
ves_escm_x4:
cmp al,05h ; VT100: ESC [ 5 m (BLINK)
jne ves_escm_x5
or byte ptr [vidcolor],10000000b; set blink bit
jmp ves_escm_done
ves_escm_x5:
cmp al,07h ; VT100: ESC [ 7 m (REVERSE VIDEO)
jne ves_escm_x7
mov al,[vidcolor]
mov ah,al
and al,01110111b ; get RGB bits only
and ah,10001000b ; get intensity/blink bits only
rol al,1 ; swap RGB fg with bg
rol al,1
rol al,1
rol al,1
or al,ah
mov [vidcolor],al
jmp ves_escm_done
ves_escm_x7:
cmp al,30d ; VT100: ESC [ 30 m thru ESC [ 37 m
jl ves_escm_x30 ; 30-37=black,red,grn,yel,
cmp al,37d ; blu,mag,cya,wht respectively
jg ves_escm_x30
mov ah,0
mov si,ax
sub si,30d
add si,offset textcolortable
mov al,[vidcolor]
and al,11111000b
or al,[ds:si]
mov [vidcolor],al
jmp ves_escm_done
ves_escm_x30:
cmp al,40d ; VT100: ESC [ 40 m thru ESC [ 47 m
jl ves_escm_x40 ; 40-47=black,red,grn,yel,
cmp al,47d ; blu,mag,cya,wht respectively
jg ves_escm_x40
mov ah,0
mov si,ax
sub si,40d
add si,offset pagecolortable
mov al,[vidcolor]
and al,10001111b
or al,[ds:si]
mov [vidcolor],al
jmp ves_escm_done
ves_escm_x40:
ves_escm_done:
jmp ves_clearesc
ves_xescbrackm:
cmp al,'%' ; DEBUG
jne ves_xdebug
mov si,offset escbuf
add si,2 ; point to Ps
mov ax,0809h ; default if numbers unspecified
call escparseint
int 3
ves_xdebug:
; UNKNOWN ESC '[' SEQUENCE. CANCEL MODE.
jmp ves_clearesc
ves_xescbrackmode:
; UNKNOWN ESC SEQUENCE. CANCEL MODE.
jmp ves_clearesc
; CLEAR ESCAPE SEQUENCE, DONE
ves_clearesc:
call clearescbuf
jmp printchardone
videscsequence endp
;
; CLEAR ANY PENDING ESCAPE MODES
;
clearescbuf proc near
mov [videsc],0
; ZERO OUT ESCAPE BUFFER
push cx
push si
push di
mov si,offset escbuf
push cs
pop di
mov cx,ESCBUF_SIZE
mov al,0
call memset
pop di
pop si
pop cx
ret
clearescbuf endp
; CREATE A VIDEO ADDRESS BASED ON CURRENT X/Y LOCATION
;
; AX returns address offset into video memory
; All other regs preserved.
;
vidaddr proc near
mov al,[curx]
mov ah,[cury]
;
; CREATE A VIDEO ADDRESS BASED ON X/Y IN AL/AH RESPECTIVELY
;
; AL=X
; AH=Y
;
vidaddrax:
push bx
push ds
push ax
mov ax,0040h
mov ds,ax
pop ax
mov bx,ax
mov al,ah
mul byte ptr [ds:004ah] ;BIOS: width of hardware screen
mov bh,0
add ax,bx
sal ax,1
pop ds
pop bx
ret
vidaddr endp
; SEND WORD TO 6845 VIDEO CONTROLLER
;
; AH=6845 register number to send word
; CX=word to send
; All other regs preserved
;
send_6845 proc near
push dx
push ds
push ax
mov ax,0040h ; BIOS area
mov ds,ax
mov dx,[ds:0063h] ; BIOS: 6845 base address for video card
pop ax
pop ds
mov al,ah ; reg#
out dx,al ; send it
inc dx
mov al,ch ; hi byte of data sent first
out dx,al
dec dx
mov al,ah ; reg#+1
inc al
out dx,al
inc dx
mov al,cl ; lo byte of data sent last
out dx,al
pop dx
ret
send_6845 endp
; SCROLL THE ENTIRE SCREEN UP ONE LINE
; All registers preserved
;
scrollup proc near
push ax
push bx
push cx
push dx
push si
push di
call savetopline ; save the top line before scrolling
;;; LET'S CHEAT, AND USE THE BIOS FOR SCROLLING
mov cl,[left]
mov ch,[top]
mov dl,[right]
mov dh,[bottom]
mov bh,[vidcolor]
mov al,1
mov ah,6
int 10h
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
scrollup endp
; SCROLL THE ENTIRE SCREEN DOWN ONE LINE
; All registers preserved
;
scrolldown proc near
push ax
push bx
push cx
push dx
push si
push di
;;; LET'S CHEAT, AND USE THE BIOS FOR SCROLLING
mov cl,[left]
mov ch,[top]
mov dl,[right]
mov dh,[bottom]
mov bh,[vidcolor]
mov al,1
mov ah,7
int 10h
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
scrolldown endp
;
; CLEAR THE ENTIRE PHYSICAL SCREEN INCLUDING STATUS LINE
; Used for screen blanking
;
; ax contains characters to use
; Preserves all registers, except AX
;
vidclearall proc near
push ax
push bx
push dx
mov dx,ax
mov ah,[phystop]
mov al,[physleft]
mov bh,[physbottom]
mov bl,[physright]
add bh,1 ; status line
call clearxy2xy
pop dx
pop bx
pop ax
ret
vidclearall endp
; CLEAR THE USER SCREEN
; (status line not altered)
;
; ax contains characters to use
; Preserves all registers
;
vidclear proc near
push ax
push bx
push dx
mov dx,ax
mov ah,[phystop]
mov al,[physleft]
mov bh,[physbottom]
mov bl,[physright]
call clearxy2xy
pop dx
pop bx
pop ax
ret
vidclear endp
;
; INVERT THE ENTIRE SCREEN
; Used by screen flash routine
;
vidinvert proc near
push ax
push bx
mov ah,[phystop]
mov al,[physleft]
mov bh,[physbottom]
mov bl,[physright]
inc bh ; get status line too
call invertxy2xy
pop bx
pop ax
ret
vidinvert endp
; DO A LINE FEED, SCROLL AS NECESSARY
;
linefeed proc near
inc [cury] ; move cursor down one line
push ax
mov al,[bottom]
cmp [cury],al ; hit bottom of logical screen?
jle lf_done
mov [cury],al ; keep cursor at bottom edge
call scrollup ; scroll up one line, done
lf_done:
pop ax
ret
linefeed endp
; DO A REVERSE LINE FEED, SCROLL AS NECESSARY
;
revlinefeed proc near
mov al,[top]
dec [cury] ; move cursor up one line
cmp [cury],al ; hit top?
jge rlf_done ; no
mov [cury],al ; clamp cursor to top
call scrolldown ; scroll down one line
rlf_done:
ret
revlinefeed endp
vidstatusline proc near
push es
push di
push cx
mov al,0cdh ; double line
mov ah,07h
mov es,[vidseg]
mov di,80d*2*24d ; PUT STATUS ON LAST PHYSICAL LINE (line #25)
mov cx,80d ; #words to write
cld
rep stosw
pop cx
pop di
pop es
ret
vidstatusline endp
; PRINT CHARACTER IN AL BOTH AS A HEX DIGIT AND RAW ASCII CHARACTER
hexdebug proc near
; PRINT CHARACTER AS RAW ASCII
mov bl,[curx]
mov [curxsave],bl
push ax
mov al,3*17d ; calc index for raw characters on right
add al,[hexdebug_x] ; byte # on debug line
mov [curx],al ; set curpos for raw characters
pop ax
push ax
call printrawchar
mov bl,[curxsave]
mov [curx],bl
pop ax
push ax
; PRINT HEX MSN
mov cl,4
shr al,cl
call hd_printnib ; MSN
pop ax
; PRINT HEX LSN
call hd_printnib
; PRINT SPACE FOLLOWING HEX BYTE
mov al,' '
call printrawchar
; ADVANCE HEX DEBUG POSITION ON LINE
inc [hexdebug_x]
cmp [hexdebug_x],16d
jl hd_done
mov [hexdebug_x],0
mov [curx],0
call linefeed
hd_done:
call curupdate
ret
hd_printnib:
and al,0fh
cmp al,09h
jle hd_phn
sub al,0ah
add al,'A'
jmp printrawchar
hd_phn: add al,'0'
jmp printrawchar
hexdebug endp
; PRINT A NULL TERMINATED STRING TO THE SCREEN
; SI contains offset address of message to print.
; Use full ANSI compatible character out routines.
;
printstring proc near
mov al,[ds:si]
cmp al,0
je pb_done
push si
call printchar
pop si
inc si
jmp printstring
pb_done:
ret
printstring endp
; SAVE THE TOP LINE OF SCREEN INTO BACKSCROLL BUFFER
; Used just before a line is to be scrolled off the top of the screen.
; Top line is pushed into a large 'ring buffer' that can traverse
; segments..
;
savetopline proc near
push bx
push cx
push si
push di
push es
push ds
mov bx,[scrbufend] ; (save for ringwrap test)
mov di,[scrbufin]
mov es,[scrbufinseg]
; COMPUTE SI - TOP LINE
push ax
mov al,[left]
mov ah,[top]
call vidaddrax
mov si,ax
pop ax
mov ds,[vidseg]
mov cx,80d ; #words to move
cld
stl_loop:
movsw ; save character and attribute
cmp di,bx ; ring wrap?
jc stl_noringwrap
sub di,SCRBUF_SIZE ; ring wrap back to beginning
stl_noringwrap:
loop stl_loop
pop ds
push ds
mov [scrbufin],di ; update in point
pop ds
pop es
pop di
pop si
pop cx
pop bx
ret
savetopline endp
; SAVE THE ENTIRE ACTIVE VIDEO SCREEN IN A 4K BUFFER
; Used when viewing backscroll screens
;
vidsavescrn proc near
push ax
push cx
push si
push di
push es
push ds
mov al,[curx] ; save cursor position too
mov [curxsave],al
mov al,[cury]
mov [curysave],al
mov si,0000h ; top of screen
mov di,offset scrsavebuf ; 4k buffer
mov ax,ds
mov es,ax ; dest is buffer
mov ds,[vidseg] ; source is video scrn
mov cx,80d*25d ; #words to move
cld
rep movsw
pop ds
pop es
pop di
pop si
pop cx
pop ax
ret
vidsavescrn endp
; RESTORE THE ENTIRE ACTIVE VIDEO SCREEN FROM 4K BUFFER
; Used when restoring active screen from a backscroll
;
vidresscrn proc near
push ax
push cx
push ds
push es
push si
push di
mov al,[curxsave] ; restore cursor position
mov [curx],al
mov al,[curysave]
mov [cury],al
call curupdate
mov si,offset scrsavebuf
mov di,0000h ; top of screen
mov es,[vidseg] ; dest is screen
mov ax,cs
mov ds,ax ; source is buffer
mov cx,80d*25d ; #words to move
cld
rep movsw
pop di
pop si
pop es
pop ds
pop cx
pop ax
ret
vidresscrn endp
; TOGGLE THE DEBUG MODE
; Resets the debug mode cursor position
;
vidtoggledebug proc near
xor [vidflags],HEX_DEBUG ; toggle debug mode
mov [hexdebug_x],0h ; hex debug byte position zeroed
; NEW LINE
mov al,0dh
call realprintchar
mov al,0ah
call realprintchar
ret
vidtoggledebug endp
; UPDATE THE BACKSCROLL BUFFER TO THE DISPLAY
; Use the vidbackscroll value to figure out how many lines
; back into the buffer we want to display.
;
vidupdatebackscroll proc near
push ax
push bx
push cx
push dx
push si
push es
push ds
; COMPUTE STARTING LOCATION
mov cx,[vidbackscroll] ; number of lines to scroll back
mov si,[scrbufin]
mov dx,si ; save [scrbufin] address
vubs_compute:
sub si,80d*2
cmp [scrbufstart],si
jz vubs_nowrap
jc vubs_nowrap
add si,SCRBUF_SIZE ; wrap around
vubs_nowrap:
loop vubs_compute
; SI NOW CONTAINS STARTING ADDRESS IN BACKSCROLL BUFFER FOR TOP LINE
mov bx,[scrbufend] ; (save end offset for ringwrap check)
mov di,0000h ; top of screen
mov es,[vidseg]
mov ds,[scrbufinseg]
mov cx,80d*24d ;# words to move
cld
vubs_loop:
movsw
cmp bx,0001h ; switched?
jz vubs_noringwrap ; yes, no ring wrap checking
cmp si,dx ; wrapped into display screen?
jne vubs_nosw
mov si,offset scrsavebuf ; switch to displaying saved screen
mov bx,0001h ; flag successive loops
jmp vubs_noringwrap
vubs_nosw:
cmp si,bx ; ring wrap?
jc vubs_noringwrap
sub si,SCRBUF_SIZE ; backup
vubs_noringwrap:
loop vubs_loop
pop ds
pop es
pop si
pop dx
pop cx
pop bx
pop ax
ret
vidupdatebackscroll endp
; CLEAR MEMORY TO A CERTAIN VALUE
; Similar to C's memset() function
; si:di filled with value in AL for CX bytes
; All regs preserved
;
memset proc near
push cx
push si
push di
push es
push si
pop es
cld
rep stosb
pop es
pop di
pop si
pop cx
ret
memset endp
; CONVERT ASCII STRING(S) AT [ds:si] TO INTEGER IN AX
; si returns pointing to next non-numeric character
atoi proc near
push bx
push cx
push dx
push di
; FIND END OF ASCII INTEGER STRING
mov cx,0
mov bx,si
atoi_loop:
mov al,[ds:bx]
cmp al,'0'
jl atoi_nan
cmp al,'9'
jg atoi_nan
inc bx
inc cx
cmp cx,5 ; no more than 5 digits can be parsed
jle atoi_loop
atoi_nan:
cmp bx,si ; no digits found?
jnz atoi_parsenum
mov si,bx ; return char position in si
mov ax,0ffffh ; AX=0
atoi_done:
pop di
pop dx
pop cx
pop bx
ret
atoi_parsenum:
push bx ; save char position
mov dx,0000h
mov di,offset dectable
; PARSE THE DIGITS IN ASCENDING ORDER (RT TO LEFT)
atoi_parseloop:
dec bx
mov al,[ds:bx] ; get next ascii digit
and al,0fh ; 31->01, 39->09
mul byte ptr [ds:di] ; apply digit position
add dx,ax ; compile result in dx
add di,2
loop atoi_parseloop
mov ax,dx ; return result
pop si ; restore char position
jmp atoi_done
atoi endp
; CONVERT INTEGER IN AX TO AN ASCII STRING AT [ds:di]
;
itoa proc near
push ax
push cx
push dx
push si
push di
mov si,10d
xor cx,cx
itoa_xzero:
xor dx,dx
div si
push dx
inc cx
or ax,ax
jne itoa_xzero
itoa_loop:
pop ax
or al,30h ; convert to printable int
mov [ds:di],al ; save it
inc di
loop itoa_loop
mov byte ptr [ds:di],0 ; terminate string
pop di
pop si
pop dx
pop cx
pop ax
ret
itoa endp
; PARSE TWO ';' SEPARATED INTEGERS AT [ds:si], RETURN LHS->AH, RHS->AL
; AH preserved if LHS not specified
; AL preserved if RHS not specified
;
; ; ; AH=<old-AH>, AL=<old-AL>
; 01; ; AH=01, AL=<old-AL>
; ;02 ; AH=<old-AH>, AL=02
; 01;02 ; AH=01, AL=02
;
escparseint proc near
push bx
mov bx,ax ; save previous al/ah
call atoi ; AL will be FF if LHS not specified
mov ah,al
cmp ah,0ffh ; LHS?
jne epi_xalff ; yes, handle it
mov ah,bh ; no LHS, maintain previous ah
epi_xalff:
cmp byte ptr [ds:si],';' ; RHS?
je epi_2 ; yes, handle it
mov al,bl ; no RHS, maintain previous al
pop bx
ret
epi_2:
mov bh,ah ; save mods so far
inc si
call atoi
mov ah,bh
cmp al,0ffh
jne epi_xahff
mov al,bl
epi_xahff:
pop bx
ret
; CONVERT ASCII STRING(S) AT [ds:si] TO INTEGER IN AX
escparseint endp
; CLEAR ALL TABSTOPS
;
; AL=0 - clear all tabstops to nothing(!)
; AL=1 - clear all tabstops to every 8 chars
;
cleartabstops proc near
push cx
push si
mov cx,0
mov si,offset tabstops
cts_loop:
push cx
and cx,0111b
cmp cx,0111b
pop cx
jne cts_notabstop
mov [ds:si],al ; tab stop every 8 chars
jmp cts_tabstop
cts_notabstop:
mov byte ptr [ds:si],0
cts_tabstop:
inc si
inc cx
cmp cx,TABSTOP_SIZE
jl cts_loop ; (jle because 1 based)
pop si
pop cx
ret
cleartabstops endp
;
; REVERT LOGICAL SCROLL WINDOW TO PHYSICAL DEFAULTS
;
resetscrollregion proc near
push ax
mov al,[phystop]
mov [top],al
mov al,[physbottom]
mov [bottom],al
mov al,[physleft]
mov [left],al
mov al,[physright]
mov [right],al
pop ax
ret
resetscrollregion endp
;
; SET CURSOR POSITION, AL=X, AH=Y (1 based!!)
;
; Take into account ORIGIN mode, ie. absolute vs relative cursor
; addressing with respect to logical screen edges.
; If AL is -1, cursor is disabled.
;
setcurpos proc near
push ax
cmp al,-1
jz scp_save
test [vidflags],RELATIVE_MODE
jf scp_norelative
add al,[left] ; apply offsets
add ah,[top]
scp_norelative:
cmp al,0
jz scp_alzero
dec al ; 1 base -> 0 base
scp_alzero:
cmp ah,0
jz scp_ahzero
dec ah
scp_ahzero:
scp_save:
mov [curx],al ; save curpos
mov [cury],ah
call curupdate ; update hardware cursor
pop ax
ret
setcurpos endp
; UPDATE THE HARDWARE CURSOR POSITION
; All regs saved
; Physical limits are enforced
; If [curx] == -1, cursor is disabled.
;
curupdate proc near
push ax
cmp [curx],-1
jne cu_nm1
push cx
mov cx,80d*26d ; move cursor off the screen
mov ah,0eh
call send_6845
pop cx
pop ax
ret
cu_nm1:
mov al,[physright]
cmp [curx],al ; enforce physical screen limits
jle cu_okx
mov [curx],al
cu_okx: mov al,[physbottom]
cmp [cury],al
jle cu_oky
mov [cury],al
cu_oky: push cx
call vidaddr ; get video memory address offset
mov cx,ax
sar cx,1 ; /2 for character offset
mov ah,0eh ; 6845 register# to set cursor position
call send_6845
cu_done:
pop cx
pop ax
ret
curupdate endp
;
; HANDLE SCROLL BACK CHANGES
; AX=#lines to scroll back (neg #s scroll down)
;
scrollbackadjust proc near
cmp byte ptr [vidbackscroll],0
jnz sba_add ; already in backscroll mode? skip
test ax,10000000b ; negative number (scroll down)?
jt sba_done ; nowhere to go
call vidsavescrn ; transition -> backscroll mode
push ax
mov al,-1
mov ah,-1
call setcurpos
pop ax
sba_add:
add [vidbackscroll],ax
cmp [vidbackscroll],0
jg sba_noactive
mov [vidbackscroll],0 ; down too far? (gone below zero)
call vidresscrn
sba_done:
ret
sba_noactive:
cmp [vidbackscroll],SCRBUF_LINES ; up too high?
jl sba_xoverrun
mov [vidbackscroll],SCRBUF_LINES ; clamp to limit
sba_xoverrun:
call vidupdatebackscroll ; update backscroll
ret
scrollbackadjust endp
blankupdate proc near
test byte ptr [vidblank],1 ; blank the screen?
jt bu_blank
jmp vidresscrn ; restore screen/cursor
bu_blank:
cmp byte ptr [vidbackscroll],0
jz bu_nobackscroll
mov byte ptr [vidbackscroll],0 ; ensure not in backscroll mode
call scrollbackadjust
bu_nobackscroll:
call vidsavescrn ; save screen in present state
mov ax,0000h ; clear to blanks
call vidclearall
mov [curx],0ffh ; move cursor off the screen
call curupdate
ret
blankupdate endp
;
; CLEAR A REGION OF THE SCREEN
;
; al/ah - X/Y upper left screen to clear (inclusive)
; bl/bh - X/Y lower right screen to clear (inclusive)
; dl/dh - character/attribute to use for clear
;
clearxy2xy proc near
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
call vidaddrax
mov di,ax ; upper left address -> DI
mov ax,bx
call vidaddrax
mov cx,ax ; lower right address -> CX
sub cx,di ; convert CX to a count of bytes
shr cx,1 ; divide by 2 for byte -> word convert
add cx,1 ; handle 'inclusive' requirement
mov es,[vidseg]
mov ax,dx ; char/attrib pair to use for clear
cld
rep stosw ; clear the region
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
clearxy2xy endp
;
; INVERT A REGION OF THE SCREEN
;
; al/ah - X/Y upper left screen to invert (inclusive)
; bl/bh - X/Y lower right screen to invert (inclusive)
;
invertxy2xy proc near
push ax
push bx
push cx
push dx
push si
push di
push ds
push es
call vidaddrax
mov di,ax ; upper left address -> DI
mov si,ax
mov ax,bx
call vidaddrax
mov cx,ax ; lower right address -> CX
sub cx,di ; convert CX to a count of bytes
shr cx,1 ; range of bytes -> words
add cx,1 ; handle 'inclusive' requirement
mov es,[vidseg]
mov ax,[vidseg]
mov ds,ax
inc si ; address the attributes only
inc di
cld
invloop:
lodsb ; get attrib
mov ah,al
shl al,1 ; swap the hi and low 4 bits to do invert
shl al,1
shl al,1
shl al,1
shr ah,1
shr ah,1
shr ah,1
shr ah,1
or al,ah
stosb
inc si
inc di
loop invloop
pop es
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
invertxy2xy endp
;
; al is number of characters to delete
; bl = X
; bh = Y
;
deletechar proc near
rep movsb
deletechar endp
; WAIT SO MANY TICKS OF THE 18-TICK-PER-SECOND CLOCK
; CX is number of ticks to wait
; NOTE: first tick will be short..
;
wait_ticks proc near
push ax
push bx
push ds
mov ax,0040h ;; bios
mov ds,ax
wt_nexttick:
mov bx,[ds:006ch] ;; timer low counter
wt_wait:
mov ax,[ds:006ch]
cmp ax,bx ;; see if it changed
je wt_wait
loop wt_nexttick ;; it did, count down tick counter
pop ds
pop bx
pop ax
ret
wait_ticks endp