home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast2.iso
/
sprint
/
spfonts4.zip
/
SPFONT.ASM
next >
Wrap
Assembly Source File
|
1989-06-27
|
32KB
|
1,400 lines
; SPFONT - software fonts for Sprint
;
; Copyright (c) 1989 Andrew D. Morrow
;
; =======================================================================
;
; Edit History:
;
; jan89, v0.1; called HERC, released to Borland for comments
; 6mar89, v0.2; renamed SPFONT, supports HERC and CGA, implemented as TSR
; 13mar89, v0.2a; support video function 14 (WriteTTY); @StringInput uses it.
; 29mar89, v0.2b; experiment with loading font file from command line
; 29mar89, v0.2c; forget 2b. install font table with SPFONTLD
; 31mar89, v0.2d; detect Hercules card
; 1apr89, v0.2e; expand fonttable to 8 fonts
; add word-underline attribute
; 6apr89, v0.2f; if /p, fonts 1-7 will print reverse video
;--- first Compuserve release
; 17apr89, v0.2g; fix scroll-down bug (FirstScanTable needed sentinel entry)
; 29apr89, v0.3; support different character heights (up to 16 dots)
; 3may89, v0.3a; detect SPFONT already installed, add /u uninstall option
; 9may89, v0.3b; add /b non-blinking cursor option
; add blink & huge cursor video functions
; 10may89, v0.3c; /a option makes CGA cursor sizing look ok with large chars
; 17may89, v0.3d; don't display cursor when positioned off the screen
; 24jun89, v0.3e; /k option for block cursor at startup
; remove chars 0-31 from each font (since Sprint can't
; use them) -- a savings of 2K-3K
; don't display characters when cursor is off-screen either
; 25jun89, v0.3f; jump to video functions through an address table
; blink & huge cursor routines return original value
;--- second Compuserve release
;
; =======================================================================
;
; Future Enhancements:
;
; - /s option prevents snow on CGA
; - /m# user-defined video mode number (if SPFONTMODE chokes other TSRs)
; - /1-/7 options to choose fewer fonts in memory (/1=/p; missing fonts
; would be replaced by reverse video)
; - EGA/VGA support
;
;
; =======================================================================
;
DOSSEG
.MODEL TINY
; ***********************************************************************
; * *
; * Equates *
; * *
; ***********************************************************************
SPFONTMODE EQU 255 ; a unique video mode number
PREVIOUSMODE EQU 254
MAXFONTS EQU 8 ; number of fonts supported
CHARSPERFONT EQU 224 ; from 20h to FFh
; The following equates identify the differences between HERC and CGA
; video memory. Hopefully they will adequately describe EGA and VGA as well.
;
IFDEF HERC
VIDEOBASE EQU 0b000h ; video page 0
MAXBANKS EQU 4
BANKSIZE EQU 02000h
MAXSCANLINES EQU 348
MAXCOLUMNS EQU 90
ELSEIFDEF CGA
VIDEOBASE EQU 0b800h
MAXBANKS EQU 2
BANKSIZE EQU 02000h
MAXSCANLINES EQU 200
MAXCOLUMNS EQU 80
ELSE
ERR missing /dHERC or /dCGA on TASM command line!
ENDIF
MAXROWS EQU (MAXSCANLINES/CHARHEIGHT)
; Values for Attribute bits. The bit-mapping bears no relation to standard
; IBM video attribute bytes. The only file that has to know about this
; mapping is MAIN.SPL which maps typestyle to video attribute. Unfortunately,
; if Sprint is started without SPFONT in memory, plain characters will
; be "inivsible". The unused bits (4,3,2) are available for future fonts.
;
RV EQU 80h ; reverse video
UN EQU 40h ; underline
WU EQU 20h ; word underline
SO EQU 10h ; strikeout
ATTRIBMASK EQU 07h ; up to MAXFONTS fonts
; Bios interrupts called
;
VIDEOINT EQU 10h
TIMERINT EQU 1ch
ENDINT EQU 20h
DOSINT EQU 21h
TSRINT EQU 27h
; Video functions supported. Internally, Sprint only calls functions 2,6,7 & 9.
; The init & reset strings in the terminal driver call function 0. Functions
; 1,3 & 15 have been implemented to support "hardware" macros that try to
; detect a Mono or Color monitor, or that want to manipulate the cursor shape.
; Functions 14 (and indirectly, 8) are used by @StringInput who uses DOS
; calls for character i/o so that SPFMT is as DOS-compatible as possible.
; Two non-standard cursor control functions are also supported; from a Sprint
; "hardware" string, cursor blink can be turned on & off and the cursor can
; be turned into a huge "cross" for better visibility.
;
SETVID EQU 0
SETCSIZE EQU 1
SETCPOS EQU 2
GETCINFO EQU 3 ; returns cx,dx
SCROLLUP EQU 6
SCROLLDN EQU 7
GETCHAR EQU 8 ; returns ax
WRTCHAR EQU 9
WRITETTY EQU 14
GETVID EQU 15 ; returns ax,bx
SETBLINK EQU 16
SETCROSS EQU 17
; DOS functions called (by the initialization code only!)
PUTSTR EQU 9
GETVECT EQU 35h
SETVECT EQU 25h
DEALLOC EQU 49h
; States of the simulated cursor. The CBUSY state protects the cursor
; from being manipulated by a timer interrupt when a video function is
; in progress.
COFF EQU 0
CON EQU 1
CBUSY EQU 2
; ***********************************************************************
; * *
; * Entry point *
; * *
; ***********************************************************************
.CODE
ORG 100h
Start: jmp Init
DW FontAnchor ; so that SPFONTLD can find table
; ***********************************************************************
; * *
; * Data Variables *
; * *
; ***********************************************************************
; Note: Some of the Cursor variables (Top,Bot,Row,Col) are words
; even though only the bottom byte is significant. Doing this
; makes the DrawCharacter and CursorToggle routines smaller and
; faster. Routines that update these variables are careful to leave
; the high-order byte untouched.
PlainFontOnly DB 0 ; assume all fonts wanted
OldVideoMode DB ?
VideoActive DB 0 ; initially inactive
OldVideoInt DD ?
OldTimerInt DD ?
CursorState DB COFF
CursorCount DB 0 ; cursor initially disabled
CursorBlink DB 1
CursorCross DB 0
CursorAdjust DB 0
CursorTop DW CHARHEIGHT-2
CursorBot DW CHARHEIGHT-1
CursorRow DW 0
CursorCol DW 0
TempCol DW ?
TempTop DW ?
TempBot DW ?
ScrollNLines DW ?
ScrollFill DW ?
ScrollUpper DW ?
ScrollLower DW ?
ScrollRange DW ?
; ***********************************************************************
; * *
; * Timer Handler & Cursor Routines *
; * *
; ***********************************************************************
; Turning the cursor off and on every time we write a character is a
; waste of precious CPU time. So turning on the cursor just sets a
; flag so that the timer interrupt will truly restore the cursor
; 50-100ms later. If more video functions are called in the meantime
; the timer will keep being deferred until the screen is "idle".
; The only problem with this scheme is that holding the up- or down-
; arrow to scroll can cause the cursor to apparently "disappear" until
; you take your finger off the key!
;
; Another advantage of hooking the timer into the cursor routines is that
; a blinking cursor can be simulated. The blink rate could be made
; programmable (or disabled for a non-blinking cursor), and the on/off
; cycle could be made "uneven".
CursorOff:
cmp [CursorState],CON ; if it's on ...
jne @@cf1
call CursorToggle ; ... turn it off
@@cf1: mov [CursorState],COFF ; ... and note the fact
ret
CursorOn:
mov [CursorCount],2 ; reset the timer
ret
; Timer interrupts are generated by hardware so all registers are precious.
; Pushing the registers is done in two steps to that the expensive "big"
; push is done only when needed.
;
; The CursorCount variable is only zero when SPFONT mode is inactive.
; When the driver is active, a decrement to zero causes the cursor to be
; toggled and the variable reset to a non-zero value. The only exception
; to this is when the timer interrupt occurs when a video function is
; executing. CursorOff (via CursorToggle) has marked the cursor BUSY,
; and the timer interrupt will return with CursorCount at zero. When
; the video function is finished, CursorOn will set CursorCount non-zero
; and the timer will gain control again some time later.
;
NewTimerHandler:
push ax
push ds
mov ax,cs
mov ds,ax
cmp [CursorCount],0 ; SPFONT active?
je TimerDone ; no, don't do anything!
dec [CursorCount] ; time to toggle the cursor?
jnz TimerDone ; not yet
push es
push di
push bx
push cx
push dx
cmp [CursorState],COFF ; if the cursor's off ...
jne @@tm1
call CursorToggle ; ... turn it on ...
mov [CursorState],CON
mov [CursorCount],4 ; for about half a second
jmp @@tm2
@@tm1:
cmp [CursorState],CON ; if it's on ...
jne @@tm2
cmp [CursorBlink],0 ; ... and blinking is wanted ...
je @@tm2
call CursorToggle ; ... do the opposite
mov [CursorState],COFF
mov [CursorCount],4
@@tm2:
pop dx
pop cx
pop bx
pop di
pop es
TimerDone:
pop ds
pop ax
jmp cs:[OldTimerInt] ; service other timers
CursorToggle:
mov [CursorState],CBUSY ; so a timer interrupt won't interfere
;
cmp [CursorCross],0 ; is a huge cursor wanted?
je @@ct9
;
xor bx,bx ; yes - toggle the whole row ...
mov cx,MAXCOLUMNS
@@ct1: push cx
push bx
mov ax,[CursorRow]
xor cx,cx
mov dx,CHARHEIGHT-1
call DisplayCursor
pop bx
inc bx
pop cx
loop @@ct1
;
xor ax,ax ; ... and the whole column ...
mov cx,MAXROWS
@@ct2: push cx
push ax
mov bx,[CursorCol]
xor cx,cx
mov dx,CHARHEIGHT-1
call DisplayCursor
pop ax
inc ax
pop cx
loop @@ct2
;
@@ct9: mov ax,[CursorRow] ; ... and/or just the exact position
mov bx,[CursorCol]
mov cx,[CursorTop]
mov dx,[CursorBot]
jmp DisplayCursor
; ax=row, bx=col, cx=top, dx=bot
DisplayCursor:
cmp ax,MAXROWS ; don't display cursor off the screen
jge @@dcdone
cmp bx,MAXCOLUMNS
jge @@dcdone
;
mov [TempCol],bx
mov [TempTop],cx
mov [TempBot],dx
;
IF CHARHEIGHT EQ 8
shl ax,1 ; convert row -> ScanTable index in bx
shl ax,1
shl ax,1
ELSE
mov ah,CHARHEIGHT
mul ah
ENDIF
add ax,[TempTop]
shl ax,1
mov bx,ax
;
mov ax,VIDEOBASE
mov es,ax
;
mov cx,[TempBot]
sub cx,[TempTop]
inc cx
@@dcloop:
mov dx,[ScanTable+bx]
add dx,[TempCol]
mov di,dx
mov al,es:[di]
xor al,0ffh
mov es:[di],al
add bx,2
loop @@dcloop
@@dcdone:
ret
; ***********************************************************************
; * *
; * Video Handler *
; * *
; ***********************************************************************
; Sprint is written in TurboC (I would hope!) and the manuals state
; that ax..dx and es are fair game, so we only have to save ds,si & di.
; Unfortunately, some TSR programs have been found that do not adequately
; save their registers during video calls, so in the interests of safety,
; all registers are saved.
;
; The low level routines DrawCharacter and CursorToggle destroy most
; registers so it is up to the video function to save any important
; registers.
;
NewVideoHandler:
cmp cs:[VideoActive],0ffh ; are we in SPFONT mode?
je ExecFunction ; yes
cmp ax,(SETVID*256)+SPFONTMODE ; switching to SPFONT mode?
je ExecFunction ; yes
jmp cs:[OldVideoInt] ; else pass onto old handler
ExecFunction:
push ds ; save important registers
push si
push di
mov si,cs
mov ds,si
;
push es ; save everything else
push dx
push cx
push bx
push ax
;
@@exec:
push ax
mov al,ah
xor ah,ah
sal ax,1
mov si,ax
pop ax
cmp si,ExecTableLen
jae DoNothing
jmp [ExecTable+si]
DoNothing:
jmp ReturnVOID ; video function not supported
ExecTable LABEL WORD
dw OFFSET DoSetVideoMode
dw OFFSET DoSetCursorSize
dw OFFSET DoSetCursorPos
dw OFFSET DoGetCursorInfo
dw OFFSET DoNothing
dw OFFSET DoNothing
dw OFFSET DoScrollUp
dw OFFSET DoScrollDown
dw OFFSET DoGetCharInfo
dw OFFSET DoWriteChar
dw OFFSET DoNothing
dw OFFSET DoNothing
dw OFFSET DoNothing
dw OFFSET DoNothing
dw OFFSET DoWriteTTY
dw OFFSET DoGetVideoParms
dw OFFSET DoSetCursorBlink
dw OFFSET DoSetCursorCross
ExecTableLen EQU $-ExecTable
DoSetVideoMode:
cmp al,SPFONTMODE ; switch to SPFONT mode?
jne @@sv1
mov ah,GETVID ; yes, remember current video mode
pushf
call cs:[OldVideoInt]
mov [OldVideoMode],al ; (should we also save video page?)
call EnterGraphicsMode
mov [CursorRow],0
mov [CursorCol],0
mov [CursorState],COFF
mov [CursorCount],1 ; next timer will toggle cursor on
mov cs:[VideoActive],0ffh ; SPFONT active!
jmp ReturnVOID
@@sv1: cmp al,PREVIOUSMODE ; revert to previous mode?
jne @@sv2
mov al,[OldVideoMode] ; yes (ah is still SETVID)
@@sv2: mov [CursorCount],0 ; disable the software cursor
mov cs:[VideoActive],0 ; no longer in SPFONT
pushf
call cs:[OldVideoInt] ; let the old handler do the work
jmp ReturnVOID
DoSetCursorSize:
push cx
call CursorOff
pop cx
cmp ch,CHARHEIGHT-1 ; make sure top is 0..CHARHEIGHT-1
jle @@sz1
mov ch,CHARHEIGHT-1
@@sz1:
cmp cl,CHARHEIGHT-1 ; make sure bottom is 0..CHARHEIGHT-1
jle @@sz2
mov ch,CHARHEIGHT-1
@@sz2:
cmp ch,cl ; make sure top <= bottom
jle @@sz3
xchg ch,cl
@@sz3:
IF CHARHEIGHT NE 8
cmp [CursorAdjust],0 ; if cursor size adjusting wanted ...
je @@sz9
cmp cl,7 ; ... & changing CGA bottom line ...
jne @@sz9
cmp ch,0 ; ... then leave top line as is ...
je @@sz8
sub cl,ch
mov ch,CHARHEIGHT-1
sub ch,cl ; ... else top = (CHARHEIGHT-1)-(bottom-top)
@@sz8: mov cl,CHARHEIGHT-1 ; ... and set new bottom line
@@sz9:
ENDIF
mov BYTE PTR [CursorTop],ch
mov BYTE PTR [CursorBot],cl
call CursorOn
jmp ReturnVOID
DoSetCursorPos:
push dx
call CursorOff
pop dx
mov BYTE PTR [CursorRow],dh
mov BYTE PTR [CursorCol],dl
call CursorOn
jmp ReturnVOID
DoGetCursorInfo:
mov ch,BYTE PTR [CursorTop]
mov cl,BYTE PTR [CursorBot]
mov dh,BYTE PTR [CursorRow]
mov dl,BYTE PTR [CursorCol]
jmp ReturnCXDX
; To properly determine the character/attribute under the cursor
; would require a lot of computation matching through the font tables.
; Returning just a "plain-space" seems to keep DOS perfectly happy.
;
DoScrollUp:
call ScrollPrep
call CursorOff
call ScrollUp
call CursorOn
jmp ReturnVOID
DoScrollDown:
call ScrollPrep
call CursorOff
call ScrollDown
call CursorOn
jmp ReturnVOID
DoGetCharInfo:
mov ax,' '*256+0 ; return char=space, attrib=plain
jmp ReturnAX
DoWriteChar:
push cx
push bx
push ax
call CursorOff
pop ax
pop bx
pop cx
@@wc1:
push cx
push bx
push ax
call DrawCharacter
pop ax
pop bx
pop cx
inc BYTE PTR [CursorCol]
loop @@wc1
call CursorOn
jmp ReturnVOID
; Write a character as TTY. @StringInput uses it so I must support it.
; Fortunately for me, only 65 characters of input is allowed (so line
; wrap should never occur) and backspace is the only editing character.
; CR & LF codes are sent when Enter is pressed, but these can be ignored
; since the screen driver repositions the cursor anyways. Characters are
; always written in the "plain" attribute.
;
; entry: al=character
DoWriteTTY:
cmp al,0dh ; cr?
je @@ttyend
cmp al,0ah ; lf?
je @@ttyend
push ax
call CursorOff
pop ax
cmp al,08h ; backspace?
jne @@tty1
cmp BYTE PTR [CursorCol],0 ; if at left edge ...
je @@ttydone ; ... do nothing
dec BYTE PTR [CursorCol] ; ... else just move cursor
jmp @@ttydone
@@tty1: mov bl,0 ; plain attribute
call DrawCharacter ; print the char ...
inc BYTE PTR [CursorCol] ; ... and move the cursor
@@ttydone: ; common wrap-up
call CursorOn
@@ttyend:
jmp ReturnVOID
; will macros with hardware strings interpret SPFONTMODE as 'Mono' or 'Color'?
DoGetVideoParms:
mov ax,(MAXCOLUMNS*256)+SPFONTMODE
xor bh,bh ; always video page 0
jmp ReturnAXBX
DoSetCursorBlink:
push ax
call CursorOff
pop ax
mov ah,[CursorBlink] ; remember old value
cmp al,2 ; if al >= 2 ...
jl @@cbl1
mov al,ah ; ... then leave unchanged
@@cbl1: mov [CursorBlink],al ; store new value ...
push ax
call CursorOn
pop ax
mov al,ah ; ... and return old one
xor ah,ah
jmp ReturnAX
DoSetCursorCross:
push ax
call CursorOff
pop ax
mov ah,[CursorCross]
cmp al,2
jl @@ccr1
mov al,ah
@@ccr1: mov [CursorCross],al
push ax
call CursorOn
pop ax
mov al,ah
xor ah,ah
jmp ReturnAX
ReturnAX:
pop es ; throw away saved ax
pop bx
pop cx
pop dx
pop es
jmp VideoDone
ReturnAXBX:
pop es ; throw away saved ax
pop es ; and bx
pop cx
pop dx
pop es
jmp VideoDone
ReturnCXDX:
pop ax
pop bx
pop es ; throw away saved cx
pop es ; and dx
pop es
jmp VideoDone
ReturnVOID:
pop ax
pop bx
pop cx
pop dx
pop es
VideoDone:
pop di
pop si
pop ds
iret
; ***********************************************************************
; * *
; * Video Subroutines *
; * *
; ***********************************************************************
; Scroll Up. Although the function is defined to work on any rectangular
; area of the screen, Sprint only scrolls whole lines so the column numbers
; in cl & dl can be ignored.
;
; The technique used here is to compute the source and destination
; offsets for the first bank and the length of the block in that bank,
; then the data is moved in each of the banks. Note that the computations
; have to take into account how many of the CHARHEIGHT scan lines of a character
; reside in a bank. We also divide the length by 2 since we want to use
; a movsw command to move words instead of bytes.
;
; entry: al=Nlines, bh=fill attrib, ch=Upper row, dh=Lower row
;
ScrollPrep:
xor ah,ah
mov [ScrollNLines],ax
mov al,ch
mov [ScrollUpper],ax
mov al,dh
mov [ScrollLower],ax
sub ax,[ScrollUpper]
inc ax
mov [ScrollRange],ax
cmp [ScrollNLines],0
je @@su4 ; if Nlines = 0 ...
cmp ax,[ScrollNLines]
jge @@su5 ; ... or Range < Nlines ...
@@su4: mov [ScrollNLines],ax ; then Nlines = Range
@@su5:
xor dx,dx ; fill with zeros
test bh,RV
jz @@su2
dec dx ; fill with ones
@@su2: mov [ScrollFill],dx
ret
ScrollUp:
cmp ax,[ScrollNLines] ; if scrolling whole range ...
je @@sufill ; ... then bypass move
; move data in each bank
; compute source, destination & length for first bank
mov ax,[ScrollUpper]
add ax,[ScrollNLines]
shl ax,1
mov bx,ax
mov si,[ScanFirstTable+bx] ; si = U+N
mov ax,[ScrollUpper]
shl ax,1
mov bx,ax
mov di,[ScanFirstTable+bx] ; di = U
mov ax,[ScrollRange]
sub ax,[ScrollNLines]
shl ax,1
mov bx,ax
mov cx,[Rows2Words+bx] ; cx = (L-U+1)-N
mov ax,VIDEOBASE
mov es,ax
push ds
mov ds,ax
cld
mov bx,MAXBANKS
@@su1:
push cx
push si
push di
rep movsw
pop di
pop si
pop cx
add si,BANKSIZE
add di,BANKSIZE
dec bx
jnz @@su1
pop ds
; fill empty region depending on attribute
; compute source & length for first bank
@@sufill:
mov ax,[ScrollLower]
sub ax,[ScrollNLines]
inc ax
shl ax,1 ; since ScanFirst is a word table
mov bx,ax
mov di,[ScanFirstTable+bx] ; si = L-N+1
mov ax,[ScrollNLines]
shl ax,1
mov bx,ax
mov cx,[Rows2Words+bx] ; cx = N
mov ax,[ScrollFill]
; es and df are unchanged from above
mov bx,MAXBANKS
@@su3:
push cx
push di
rep stosw
pop di
pop cx
add di,BANKSIZE
dec bx
jnz @@su3
ret
ScrollDown:
cmp ax,[ScrollNLines] ; if scrolling whole range ...
je @@sdfill ; ... then bypass move
; move data in each bank
; compute source, destination & length for first bank
mov ax,[ScrollRange]
sub ax,[ScrollNLines]
add ax,[ScrollUpper]
shl ax,1
mov bx,ax
mov si,[ScanFirstTable+bx] ; si = U+(R-N)
dec si
dec si
mov ax,[ScrollLower]
inc ax
shl ax,1
mov bx,ax
mov di,[ScanFirstTable+bx] ; di = L+1
dec di
dec di
mov ax,[ScrollRange]
sub ax,[ScrollNLines]
shl ax,1
mov bx,ax
mov cx,[Rows2Words+bx] ; cx = R-N
mov ax,VIDEOBASE
mov es,ax
push ds
mov ds,ax
std
mov bx,MAXBANKS
@@sd1:
push cx
push si
push di
rep movsw
pop di
pop si
pop cx
add si,BANKSIZE
add di,BANKSIZE
dec bx
jnz @@sd1
pop ds
; fill empty region depending on attribute
; compute source & length for first bank
@@sdfill:
mov ax,[ScrollUpper]
add ax,[ScrollNLines]
shl ax,1 ; since ScanFirst is a word table
mov bx,ax
mov di,[ScanFirstTable+bx] ; si = U+N
dec di
dec di
mov ax,[ScrollNLines]
shl ax,1
mov bx,ax
mov cx,[Rows2Words+bx] ; cx = N
mov ax,[ScrollFill]
; es and df are unchanged from above
mov bx,MAXBANKS
@@sd3:
push cx
push di
rep stosw
pop di
pop cx
add di,BANKSIZE
dec bx
jnz @@sd3
ret
; The critical DrawCharacter routine. Almost any technique that make this
; routine faster is worth the effort. The reverse, underline and strikeout
; attributes could be coded into font tables but all the combinations of 5
; attribute bits would require (2^5)*2K = 64K of font tables! This is
; obviously too much.
;
; The code of the "inner loop" has been implemented as a macro loop
; (producing replicated code) for efficiency.
;
; Draw a character at the current row & column
; entry: al = character, bl=attribute
; uses: ax,bx,cx,dx, si,di,es
;
DrawCharacter:
test bl,WU ; word underline wanted?
jz @@dc1
or bl,UN ; yes - assume underline ...
cmp al,' '
jne @@dc1
and bl,NOT UN ; ... except for spaces
@@dc1:
cmp [PlainFontOnly],0ffh ; if only plain font in memory ...
jne @@dc2
test bl,ATTRIBMASK ; ... and another font is wanted ...
jz @@dc2
and bl,NOT ATTRIBMASK ; ... then print in plain font ...
or bl,RV ; ... with reverse video
@@dc2:
sub al,' ' ; adjust out unprintable characters
jnb @@dc3
mov al,0 ; unprintable characters print as space
@@dc3:
IF CHARHEIGHT EQ 8
xor ah,ah ; convert char -> FontTable index in si
shl ax,1 ; (*CHARHEIGHT bytes per entry)
shl ax,1
shl ax,1
ELSE
mov ah,CHARHEIGHT
mul ah
ENDIF
add ax,OFFSET FontTable
mov si,ax
mov ax,bx ; get attribute
and ax,ATTRIBMASK ; isolate font# bits
mov cx,(CHARSPERFONT*CHARHEIGHT)
mul cx ; (*?K per font)
add si,ax ; index FontTable to desired font
mov cx,bx ; attribute in cl
;
mov ax,[CursorCol]
cmp ax,MAXCOLUMNS
jge @@nodraw
mov ax,[CursorRow]
cmp ax,MAXROWS
jge @@nodraw
jmp @@dc4
@@nodraw: ret ; cursor is off-screen
@@dc4: ; convert row -> ScanTable index in bx
IF CHARHEIGHT EQ 8
shl ax,1
shl ax,1
shl ax,1
ELSE
mov bx,CHARHEIGHT
mul bx
ENDIF
shl ax,1
mov bx,ax
mov ax,VIDEOBASE ; es -> video memory
mov es,ax
cld
lup = 0
REPT CHARHEIGHT
mov dx,[ScanTable+bx]
add dx,[CursorCol]
mov di,dx
lodsb
IF lup EQ (CHARHEIGHT/2)+1
test cl,SO
jz @@dc&lup&st
mov al,0ffh
@@dc&lup&st:
ENDIF
IF lup EQ (CHARHEIGHT-1)
test cl,UN
jz @@dc&lup&un
mov al,0ffh
@@dc&lup&un:
ENDIF
test cl,RV
; jz @@dc&lup&rv
jz $+4 ; since lup is not local to REPT
xor al,0ffh
;@@dc&lup&rv:
stosb
add bx,2
lup = lup + 1
ENDM
ret
ClearVideo:
mov cx,(MAXBANKS*BANKSIZE)/2
mov ax,VIDEOBASE
mov es,ax
xor di,di
xor ax,ax
cld
rep stosw
ret
; ***********************************************************************
; * *
; * HERC Particulars (resident) *
; * *
; ***********************************************************************
IFDEF HERC
CONFIGPORT EQU 3bfh
HALFCONFIG EQU 1 ; all that SPFONT requires
MODEPORT EQU 3b8h
TEXTMODE EQU 00h
GRAPHMODE EQU 02h
SCREENOFF EQU 00h
SCREENON EQU 08h
BLINKOFF EQU 00h
BLINKON EQU 20h
GPAGE0 EQU 00h
GPAGE1 EQU 80h
M6845PORT EQU 3b4h
STATUSPORT EQU 3bah
graphicsreg DB 35h,2dh,2eh,7h,5bh,2h,57h,57h,2h,3h,0,0
EnterGraphicsMode:
call ClearVideo ; clear display first to avoid ugly "flash"
mov al,HALFCONFIG
mov dx,CONFIGPORT
out dx,al
mov al,GRAPHMODE+GPAGE0+SCREENON
mov dx,MODEPORT
out dx,al
xor ah,ah ; starting with register 0
mov cx,12 ; for 12 registers
mov dx,M6845PORT
mov si,OFFSET graphicsreg
cld
cli
@@rv1:
mov al,ah
out dx,al ; select register
inc dx
lodsb
out dx,al ; write value
inc ah
dec dx
loop @@rv1
sti
mov al,GRAPHMODE+GPAGE0+SCREENON
mov dx,MODEPORT
out dx,al
ret
ENDIF
; ***********************************************************************
; * *
; * CGA Particulars *
; * *
; ***********************************************************************
IFDEF CGA
EnterGraphicsMode:
mov ax,(SETVID*256)+6 ; enter 640x200 b/w mode
pushf
call cs:[OldVideoInt]
ret
ENDIF
; ***********************************************************************
; * *
; * Tables *
; * *
; ***********************************************************************
; This table gives the offset from VIDEOBASE for the start of each
; scan line. The increase in speed by avoiding multiplications
; is worth the space.
ScanTable LABEL WORD
scan = 0
rept MAXSCANLINES/MAXBANKS
bank = 0
rept MAXBANKS
DW (bank*BANKSIZE)+(scan*MAXCOLUMNS)
bank = bank+1
endm
scan = scan+1
endm
ScanFirstTable LABEL WORD
scan = 0
rept MAXROWS+1
DW scan*MAXCOLUMNS*(CHARHEIGHT/MAXBANKS)
scan = scan+1
endm
Rows2Words LABEL WORD
nrows = 0
rept MAXROWS
DW (nrows*MAXCOLUMNS*(CHARHEIGHT/MAXBANKS))/2
nrows = nrows+1
endm
; The font tables. Currently there are eight fonts implemented (plain,
; italic, bold, bold-italic, large, subscript, superscript & other).
;
; FontTable MUST be the last table before the installation code! If the
; /p option is given then all but the first (plain) font are discarded
; when the TSR loads. This is for the benefit of programmers who are
; more interested in 43 lines of text than in fonts and who want to
; sacrifice as little ram as possible.
;
; FontAnchor is used by SPFONTLD to assure that it is really patching a
; copy of SPFONT and not scribbling over just any file.
;
FontAnchor DB '<SPFONT>' ; validation string
FontTableSize DW (MAXFONTS*CHARSPERFONT*CHARHEIGHT)
FontValid DB 0 ; set to ff by SPFONTLD
FontTable LABEL BYTE
DB (CHARSPERFONT*CHARHEIGHT) DUP (0)
EndPlainFonts LABEL BYTE
DB ((MAXFONTS-1)*(CHARSPERFONT*CHARHEIGHT)) DUP (0)
EndAllFonts LABEL BYTE
; ***********************************************************************
; * *
; * Installation *
; * *
; ***********************************************************************
InstallWanted DB 0 ; assume installation not wanted
Herald DB 'SPFONT for '
IFDEF HERC
DB 'Hercules'
ENDIF
IFDEF CGA
DB 'CGA'
ENDIF
DB ' (v0.3f) by Andrew D. Morrow','$'
InstallMsg DB ' - Installed','$'
UninstallMsg DB ' - Uninstalled','$'
PlainMsg DB ' (Plain font only)','$'
CrLfMsg DB 0dh,0ah,'$'
HelpMsg DB 0dh,0ah
DB 'Syntax: SPFONT [/options]',0dh,0ah
DB 0dh,0ah
DB '/i Install',0dh,0ah
DB '/p load Plain font only (no bold,italic,etc.)',0dh,0ah
DB '/a Adjust cursor size commands relative to CGA 0-7',0dh,0ah
DB '/b start with nonBlinking cursor',0dh,0dh
DB '/k start with blocK cursor',0dh,0ah
DB '/u Uninstall',0dh,0ah
DB '$'
FontMsg DB 'Font Table not loaded. Run SPFONTLD.',0dh,0ah,'$'
InMemMsg DB 'SPFONT already installed',0dh,0ah,'$'
CantUnMsg DB 'Cannot Uninstall',0dh,0ah,'$'
IFDEF HERC
NoHercMsg DB 'Cannot detect Hercules Graphics Card',0dh,0ah,'$'
ENDIF
JUMPS ; I don't care how inefficient the init code is!
Init:
; print herald
mov dx,OFFSET Herald
mov ah,PUTSTR
int DOSINT
; parse command tail
mov bx,0080h ; first 'inc bx' will skip tail length
CmdLup: inc bx
cmp BYTE PTR [bx],' ' ; deblank
je CmdLup
cmp BYTE PTR [bx],0dh ; all done?
je EndCmd
cmp BYTE PTR [bx],'/' ; there had better be an option
jnz DisplayHelpMsg
inc bx ; look at which option
mov al,BYTE PTR [bx]
and al,01011111b ; quick-and-dirty capitalization
cmp al,'I'
jz SelectInstall
cmp al,'P'
jz PlainFonts
cmp al,'B'
jz BlinkOff
cmp al,'K'
jz BlockCursor
cmp al,'A'
jz AdjustOn
cmp al,'U'
jz DoUninstall
jmp DisplayHelpMsg
SelectInstall:
mov [InstallWanted],1
jmp CmdLup
PlainFonts:
mov [PlainFontOnly],0ffh
jmp CmdLup
BlinkOff:
mov [CursorBlink],0
jmp CmdLup
BlockCursor:
mov [CursorTop],0
mov [CursorBot],CHARHEIGHT-1
jmp CmdLup
AdjustOn:
mov [CursorAdjust],0ffh
jmp CmdLup
EndCmd:
cmp [InstallWanted],0 ; if installation was not requested
je DisplayHelpMsg ; then assume that user needs help
mov ax,(GETVECT*256)+VIDEOINT
int DOSINT
cmp bx,OFFSET NewVideoHandler
mov dx,OFFSET InMemMsg
je DisplayErrorMsg
cmp [FontValid],0
mov dx,OFFSET FontMsg
je DisplayErrorMsg
IFDEF HERC
call DetectHercules
mov dx,OFFSET NoHercMsg
jz DisplayErrorMsg
ENDIF
; replace 18.2Hz timer
mov ax,(GETVECT*256)+TIMERINT
int DOSINT
mov WORD PTR cs:[OldTimerInt],bx
mov bx,es
mov WORD PTR cs:[OldTimerInt+2],bx
mov dx,OFFSET NewTimerHandler
push ds
mov ax,cs
mov ds,ax
mov ax,(SETVECT*256)+TIMERINT
int DOSINT
pop ds
; replace video driver
mov ax,(GETVECT*256)+VIDEOINT
int DOSINT
mov WORD PTR cs:[OldVideoInt],bx
mov bx,es
mov WORD PTR cs:[OldVideoInt+2],bx
mov dx,OFFSET NewVideoHandler
push ds
mov ax,cs
mov ds,ax
mov ax,(SETVECT*256)+VIDEOINT
int DOSINT
pop ds
; deallocate environment segment
mov ax,ds:[02ch] ; get env segment from PSP
mov es,ax
mov ah,DEALLOC
int DOSINT
; acknowledge installation
mov dx,OFFSET InstallMsg
mov ah,PUTSTR
int DOSINT
cmp [PlainFontOnly],0ffh
jne @@ai1
mov dx,OFFSET PlainMsg
mov ah,PUTSTR
int DOSINT
@@ai1:
; terminate & stay resident
mov dx,OFFSET EndPlainFonts
cmp [PlainFontOnly],0ffh
je @@tsr
mov dx,OFFSET EndAllFonts
@@tsr: int TSRINT
DoUninstall:
mov ax,(GETVECT*256)+VIDEOINT ; who owns video interrupt?
int DOSINT
cmp bx,OFFSET NewVideoHandler ; another copy of SPFONT?
mov dx,OFFSET CantUnMsg
jne DisplayErrorMsg ; no - sorry
mov ax,(GETVECT*256)+TIMERINT ; who owns timer interrupt?
int DOSINT
cmp bx,OFFSET NewTimerHandler ; another copy of SPFONT?
mov dx,OFFSET CantUnMsg
jne DisplayErrorMsg ; no - sorry
; at this point, es = segment address of original SPFONT
; restore video
mov dx,WORD PTR es:[OldVideoInt]
push ds
mov ax,WORD PTR es:[OldVideoInt+2]
mov ds,ax
mov ax,(SETVECT*256)+VIDEOINT
push es
int DOSINT
pop es
pop ds
; restore timer
mov dx,WORD PTR es:[OldTimerInt]
push ds
mov ax,WORD PTR es:[OldTimerInt+2]
mov ds,ax
mov ax,(SETVECT*256)+TIMERINT
push es
int DOSINT
pop es
pop ds
; deallocate original copy of SPFONT
mov ah,DEALLOC
int DOSINT
; acknowledge uninstallation
mov dx,OFFSET UninstallMsg
mov ah,PUTSTR
int DOSINT
int ENDINT
DisplayHelpMsg:
mov dx,OFFSET HelpMsg
DisplayErrorMsg:
push dx
mov dx,OFFSET CrLfMsg
mov ah,PUTSTR ; since herald doesn't crlf
int DOSINT
pop dx
mov ah,PUTSTR ; print diagnostic
int DOSINT
int ENDINT ; and quit without doing anything
; ***********************************************************************
; * *
; * HERC Particulars (transient) *
; * *
; ***********************************************************************
IFDEF HERC
; returns ax=ffff if HGC found, else 0 (Zflag set appropriately)
DetectHercules:
mov ah,GETVID
int VIDEOINT
cmp al,7 ; in mono text mode?
jne @@dh8 ; no
mov dx,STATUSPORT
in al,dx
and al,80h ; get current vertical retrace state
mov ah,al ; and store it away
mov cx,0ffffh ; wait a long time
@@dh1: in al,dx
and al,80h ; get vertical retrace state again
cmp ah,al ; any change?
jne @@dh9 ; yes - it's an HGC!
loop @@dh1 ; no - keep testing
@@dh8: xor ax,ax ; return failure
ret
@@dh9: or ax,0ffffh ; return success
ret
ENDIF
END Start