Files
PTV_Archive/PM5639/Comdrv/TPCRT.ASM
2022-02-21 12:42:04 +01:00

1597 lines
42 KiB
NASM
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;******************************************************
; TPCRT.ASM 5.08
; Low level CRT routines
; Copyright (c) TurboPower Software 1987.
; Portions copyright (c) Sunny Hill Software 1985, 1986
; and used under license to TurboPower Software
; All rights reserved.
;******************************************************
INCLUDE TPCOMMON.ASM
;****************************************************** Equates
;structure of a Turbo FIB (File Interface Block)
FHandle EQU (WORD PTR 0)
FMode EQU (WORD PTR 2)
FBufSize EQU (WORD PTR 4)
FPrivate EQU (WORD PTR 6)
FBufPos EQU (WORD PTR 8)
FBufEnd EQU (WORD PTR 10)
FBufPtr EQU (DWORD PTR 12)
FOpenProc EQU (DWORD PTR 16)
FInOutProc EQU (DWORD PTR 20)
FFlushProc EQU (DWORD PTR 24)
FCloseProc EQU (DWORD PTR 28)
FUserData EQU (BYTE PTR 32)
FName EQU (BYTE PTR 48)
FBuffer EQU (BYTE PTR 128)
;FMode constants
FMClosed = 0D7B0h
FMInput = 0D7B1h
FMOutput = 0D7B2h
FMInOut = 0D7B3h
;equates for referencing pointers to FIB's on the stack
TextFile EQU DWORD PTR SS:[BX+4]
TextFileBP EQU [BP+6]
TFPtr EQU ES:[DI]
;keys / control characters
Null = 0
CtrlA = 1
CtrlC = 3
CtrlD = 4
CtrlF = 6
Bell = 7
BkSp = 8
LineFeed = 10
Return = 13
CR = 13
CtrlR = 18
CtrlS = 19
CtrlX = 24
CtrlZ = 26
Escape = 27
CtrlBkSp = 127
;****************************************************** Macros
Write MACRO Char
MOV AL, Char ;Al has character to write
CALL WriteChar ;Write it
ENDM
WriteLn MACRO
Write CR ;Write the carriage return
Write LineFeed ;Write the line feed
ENDM
;****************************************************** Data
DATA SEGMENT BYTE PUBLIC
;Pascal variables
EXTRN CheckBreak : BYTE ;Check for ^Break?
EXTRN CheckEof : BYTE ;Check for ^Z on Read/Ln?
EXTRN DirectVideo : BYTE ;If false, use BIOS
EXTRN WindMin : WORD ;Min. XY coordinates
EXTRN WindMax : WORD ;Max. XY coordinates
EXTRN LastMode : WORD ;Current video mode
EXTRN CurrentMode : BYTE ;Current video mode
EXTRN CurrentPage : BYTE ;Current video page
EXTRN ScreenWidth : BYTE ;Current width of display
EXTRN VirtualWidth : BYTE ;Current width of virtual display
EXTRN ScreenHeight : BYTE ;Current height of display
EXTRN CurrentHeight : BYTE ;Current height of display-1
EXTRN CurrentDisplay : BYTE ;Current display type
EXTRN EnhancedDisplay : BYTE ;Type of enhanced display
;installed, if any
EXTRN WhichHerc : BYTE ;Type of Herc card installed
EXTRN InTextMode : BYTE ;False if in graphics mode
EXTRN TextAttr : BYTE ;Current video attribute
EXTRN TextChar : BYTE ;background character
EXTRN NormalAttr : BYTE; ;Attribute for NormVideo
EXTRN CheckSnow : BYTE ;If true, check for retrace
EXTRN CtrlBreakFlag : BYTE ;True if ^Break pressed
EXTRN VideoSegment : WORD ;Segment of Video Memory
EXTRN VirtualSegment : WORD ;Segment of Video Memory--alt
EXTRN BufLen : WORD ;Max length of string for Read
EXTRN ExitProc : DWORD ;Turbo's ExitProc pointer
EXTRN Save1B : DWORD ;Previous INT 1Bh handler
EXTRN DetectMultitasking : BYTE ;If True, automatically checks
; for multitasker
EXTRN MultitaskingOn : BYTE ;True if running under
; multitasker
EXTRN IsCompaq : BYTE ;True if system is a COMPAQ
EXTRN BiosScroll : BYTE ;If False, use special scroll
; routine
EXTRN DisplayOverride : BYTE ;override auto-detection
EXTRN NextChar : BYTE ;Used by KeyPressed/ReadKey
EXTRN OneMS : WORD ;Loop count for 1 ms delay
EXTRN SaveExitProc :DWORD ;Saves old ExitProc
DATA ENDS
;****************************************************** Code
CODE SEGMENT BYTE PUBLIC
ASSUME CS:CODE, DS:DATA
;Pascal routines
EXTRN WhichHercCard : NEAR ;Herc detection routine
;standard CRT unit routines
PUBLIC ReadKey, InitCrt, AssignCrt
;extensions
PUBLIC ReInitCrt
;low-level routines
PUBLIC ReadCursorPrim, SetCursorPrim, GetCursorPrim, GetCrtModePrim
PUBLIC ScrollUpPrim, ScrollDownPrim, AdapterCheck, DelayMS
PUBLIC GetCharAttr, SetWindowPrim, FullWindow, GetAttribute
;data in code segment
OurDS DW DATA ;Init'd by EXE loader to value of DS
AdapterTable1 LABEL BYTE
DB UnknownD ;0 = no display
DB Mono ;1 = mono
DB CGA ;2 = CGA
DB UnknownD ;3 = reserved
DB EGA ;4 = EGA w/color
DB EGA ;5 = EGA w/mono
DB PGC ;6 = Professional Graphics controller
DB VGA ;7 = VGA w/mono
DB VGA ;8 = VGA w/color
DB UnknownD ;9 = reserved
DB MCGA ;A = MCGA w/digital color
DB MCGA ;B = MCGA w/mono
DB MCGA ;C = MCGA w/analog color
AdapterTable2 LABEL BYTE
DB Unenhanced ;0 = not enhanced
DB Unenhanced ;1 = "
DB Unenhanced ;2 = "
DB Unenhanced ;3 = "
DB EGA ;4 = EGA w/color
DB EGA ;5 = EGA w/mono
DB PGC ;6 = Professional Graphics controller
DB VGA ;7 = VGA w/mono
DB VGA ;8 = VGA w/color
DB Unenhanced ;9 = not enhanced
DB MCGA ;A = MCGA w/digital color
DB MCGA ;B = MCGA w/mono
DB MCGA ;C = MCGA w/analog color
;****************************************************** AdapterCheck
JunkValue = 0FFFFh
AdapterCheck PROC NEAR
MOV WP ScreenHeight,25 ;Assume 25-line mode
MOV WP CurrentHeight,24
AND WP LastMode,00FFh ;Clear high byte of LastMode
MOV AL,DisplayOverride ;check for override
CMP AL,-1 ;-1 means no override
JE ACauto ;if -1, use auto detect
CMP AL,VGA ;is it in Mono..VGA?
JA ACauto ;if not, use auto detect
MOV CurrentDisplay,AL ;use the override
MOV EnhancedDisplay,AL
CMP AL,MCGA ;is MCGA or higher indicated?
JAE ACgetheight ;if so, check height
MOV EnhancedDisplay,0 ;not enhanced
MOV CurrentDisplay,CGA ;assume CGA
CMP CurrentMode,7 ;Current mode = 7?
JNE ACexit1 ;if not, done
MOV CurrentDisplay,Mono ;else, mono
ACexit1:
RET ;done
ACgetHeight:
JMP SHORT ACheight
;test for VGA, MCGA
ACauto:
VidCallAX 1A00h ;Video display combination
CMP AL,1Ah ;AL = $1A signals valid call
JNE ACega ;Check for EGA if invalid
;get current display type
MOV DX,BX ;move result from BX into DX
CMP DL,0Ch ;DL should be in range 1..C
JA ACega
MOV AL,DL ;translate into our terms
MOV BX,Offset AdapterTable1
XLAT BYTE PTR CS:[0]
CMP AL,UnknownD ;check for unknown adapter
JE ACega ;use EGA test after all
MOV CurrentDisplay,AL ;save result
;get enhanced display type
MOV AL,DL ;assume one display
OR DH,DH ;DH of 0 means one display
JZ ACone
CMP DH,0Ch ;else DH should be in range 1..C
JA ACone
CMP DH,DL ;if two, enhanced has higher value
JB ACone
MOV AL,DH
ACone: MOV BX,Offset AdapterTable2
XLAT BYTE PTR CS:[0]
MOV EnhancedDisplay,AL ;save result
JMP SHORT ACHeight ;Check the height
;test for EGA
ACega: MOV CurrentDisplay,EGA ;Assume EGA
MOV EnhancedDisplay,EGA
MOV BX,0FF10h ;Return EGA information
MOV CX,JunkValue ;Load CX with junk value
VidCallAX 1200h ;Alternate function select
SetZero AL ;AL = 0
CMP CX,JunkValue ;CX unchanged?
JE ACnotEnhanced ;If so, not an enhanced display
CMP BH,1 ;BH should be 0 or 1
JA ACnotEnhanced ;Mono or CGA if it isn't
;See if EGA/VGA is the active monitor
CMP BH,1 ;mono display?
JE ACMono ;If so, use mono check
CMP CurrentMode,7 ;In mono mode?
JE ACPlain ;If so, we're not on the EGA/VGA/MCGA
JMP SHORT ACHeight ;Else check height
ACMono:
CMP CurrentMode,7 ;Current mode = 7?
JNE ACPlain ;Exit if not
;check the height of the display
ACHeight:
MOV CX,40h ;CX = $40
MOV ES,CX ;ES = $40
MOV CL,ES:[84h] ;CL = Number of rows - 1
MOV CurrentHeight,CL ;Set CurrentHeight and ScreenHeight
INC CL
MOV ScreenHeight,CL
CMP CL,25 ;Is it 25-line mode?
JE ACexit ;If so, done
OR WP LastMode,Font8x8 ;Set Font8x8 bit
CMP CL,43 ;Is it 43-line mode?
JE ACexit ;if so, done
CMP CL,50 ;Is it 50-line mode?
JE ACexit ;if so, done
AND WP LastMode,00FFh ;Clear Font8x8 bit of LastMode
ACexit:
RET
ACnotEnhanced:
MOV EnhancedDisplay,0 ;No enhanced display installed
ACPlain:
CMP CurrentDisplay,EGA ;Set to EGA?
JNE ACExit ;Exit if not
MOV CurrentDisplay,CGA ;Assume CGA
CMP CurrentMode,7 ;Mono mode
JNE ACExit ;Done if not
MOV CurrentDisplay,Mono ;Else, Mono
RET
AdapterCheck ENDP
;****************************************************** BreakCheck
;Check for ^Break
BreakCheck PROC NEAR
CMP CtrlBreakFlag,False ;Break flag set?
JE BreakExit ;Done if not
MOV CtrlBreakFlag,False ;Reset break flag
FlushKbdBuffer:
KbdCall 1 ;Key pressed function
JZ BreakCtrlC ;If no keypress, continue
KbdCall 0 ;Read next character
JMP SHORT FlushKbdBuffer ;Repeat
BreakCtrlC:
Write '^' ;Write the caret
Write 'C' ;Write the 'C'
WriteLn ;Write a CR/LF
INT 23h ;Abort
BreakExit:
RET
BreakCheck ENDP
;****************************************************** ReadKey
;function ReadKey : Char;
;Read a character from the keyboard
ReadKey PROC FAR
MOV AL,NextChar ;AL = NextChar
MOV NextChar,0 ;NextChar = 0
OR AL,AL ;See if NextChar = 0
JNZ ReadKeyExit ;If not, we're done
ReadKeyStart:
INT 28h ;Help keep popups from locking out
;other TSR's
KbdCall 1 ;Key pressed function
JZ ReadKeyStart ;If no keypress, loop
KbdCall 0 ;Read next char service
OR AL,AL ;Check for AL = 0
JNZ ReadKeyExit ;Exit if AL <> 0
MOV NextChar,AH ;Save extended scan code
OR AH,AH ;Check for scan code of 0 (^Break)
JNZ ReadKeyExit ;Exit if AH <> 0
MOV AL,CtrlC ;Else, AL = ^C
ReadKeyExit:
CALL BreakCheck ;Check for break if necessary
RET
ReadKey ENDP
;****************************************************** SetCursorPrim
;Primitive routine to set cursor coordinates
;On entry, DH has row (Y), DL has column (X)
SetCursorPrim PROC NEAR
CMP DirectVideo,False ;Direct video flag on?
JE SCNotDirect ;If not, use BIOS call
CMP MultitaskingOn,True ;Multitasking flag on?
JE SCNotDirect ;If so, use BIOS call
;Set cursor position in BIOS data area
MOV BX,0040h ;BX = $40
MOV ES,BX ;ES = $40
MOV BL,CurrentPage ;BL = CurrentPage, BH = 0
SHL BX,1 ;Multiply by 2
MOV ES:50h[BX],DX ;DX has cursor position
;program the 6845
SetZero AH
MOV AL,DH ;AL = Row
MUL ScreenWidth ;AX = Row * width of screen
SetZero DH ;DX = Column (in DL, DH is 0)
ADD AX,DX ;AX = (Row * width) + Column
SHL AX,1 ;Multiply by 2
ADD AX,ES:[4Eh] ;DX has starting offset of video memory
SHR AX,1 ;Divide by 2
MOV BX,AX ;BX = (Row * width) + Column
MOV DX,ES:[63h] ;DX has address of 6845 ($03D4/$03B4)
MOV AL,14 ;Select cursor address (high) register
OUT DX,AL ;Write the byte
NullJump ;Wait
MOV AL,BH ;Get high byte of word
INC DX ;Next register ($03D5/$03B5)
OUT DX,AL ;Write the byte
NullJump ;Wait
DEC DX ;Previous register ($03D4/$03B4)
MOV AL,15 ;Select cursor address (low) register
OUT DX,AL ;Write the byte
NullJump ;Wait
MOV AL,BL ;Get low byte of word
INC DX ;Next register ($03D5/$03B5)
OUT DX,AL ;Write the byte
NullJump ;Wait
RET
SCNotDirect:
;Set cursor position with BIOS call
MOV BH,CurrentPage ;BH = current display page
VideoCall 2 ;Set cursor service
RET
SetCursorPrim ENDP
;****************************************************** GetCursorPrim
;Primitive routine to get cursor coordinates
;On exit, DH has row, DL has column, CH has starting scan line, CL has ending
GetCursorPrim PROC NEAR
CMP DirectVideo,False ;Direct video flag on?
JE GCNotDirect ;If not, use BIOS call
CMP MultitaskingOn,True ;Multitasking flag on?
JE GCNotDirect ;If so, use BIOS call
;get cursor position from BIOS data area
MOV BX,0040h ;BX = $40
MOV ES,BX ;ES = $40
MOV BL,CurrentPage ;BL = CurrentPage, BH = 0
SHL BX,1 ;Multiply by 2
MOV DX,ES:50h[BX] ;DX has cursor position for CurrentPage
MOV CX,ES:60h ;CX has cursor shape
RET
GCNotDirect:
;get cursor position with BIOS call
MOV BH,CurrentPage ;BH = current display page
VideoCall 3 ;Read cursor service
RET
GetCursorPrim ENDP
;****************************************************** ReadCursorPrim
;Primitive routine to get cursor size and position
;On exit, DH has row, DL has column, AH has starting scan line, AL has ending
ReadCursorPrim PROC NEAR
CALL GetCursorPrim
MOV AX,CX ;Scan lines from CX into AX
RET
ReadCursorPrim ENDP
;****************************************************** DelayMS
;Delay one millisecond
DelayMS PROC NEAR
MOV BX,4
DMSdec: DEC BX
JNZ DMSdec
CMP AL,ES:[DI]
LOOPE DelayMS
RET
DelayMS ENDP
;****************************************************** CrtExitProc
;Invoked when a program is ending. Restore INT 1B vector.
CrtExitProc PROC FAR
GetPtr SaveExitProc ;ES:DI points to old ExitProc
SetPtr ExitProc, ES, DI ;Restore old ExitProc
MOV CtrlBreakFlag,False ;Reset CtrlBreakFlag
PUSH DS ;Save DS
LDS DX,Save1B ;DS:DX has saved vector
MOV AX,251Bh ;AH = Set interrupt vector service
;AL = interrupt vector
INT 21h ;Call DOS
POP DS ;Restore DS
RET
CrtExitProc ENDP
;****************************************************** Int1B
;Handles INT 1Bh, ^Break interrupt
Int1B PROC NEAR
PUSH DS ;Save DS
PUSH AX ;Save AX
MOV DS,CS:OurDS ;Restore our DS
MOV AL,CheckBreak ;If CheckBreak is True...
MOV CtrlBreakFlag,AL ;CtrlBreakFlag will be True also
POP AX ;Restore AX
POP DS ;Restore DS
IRET
Int1B ENDP
;****************************************************** GetCharAttr
;Get current character and attribute
;Returns attribute in AH, character in AL
GetCharAttr PROC NEAR
MOV BH,CurrentPage ;BH = current video page
VideoCall 8 ;Read char and attribute service
RET
GetCharAttr ENDP
;****************************************************** GetAttribute
;Get current attribute at cursor
GetAttribute PROC NEAR
CALL GetCharAttr ;Get char and attribute
AND AH,01111111b ;Mask out the blink bit
MOV TextAttr,AH ;Attribute at cursor is in AH
MOV NormalAttr,AH ;Attribute for NormVideo
RET
GetAttribute ENDP
;****************************************************** GetCrtModePrim
;Return the current video mode in AL, initialize most global screen variables
GetCrtModePrim PROC NEAR
VideoCall 0Fh ;Get video mode service
AND AL,7Fh ;mask out high bit, sometimes set on EGA
MOV CurrentMode,AL ;Save video mode
MOV ScreenWidth,AH ;Save width
MOV VirtualWidth,AH
MOV CurrentPage,BH ;Save page
MOV InTextMode,True ;Assume text mode
SetZero DX ;DX = 0
MOV CX,0B000h ;Assume mono card
CMP AL,7 ;In monochrome mode?
JE GetCrtExit ;If so, exit
;get the segment of video memory for the current page
MOV CX,40h ;CX = $40
MOV ES,CX ;ES = $40
MOV CX,ES:[4Eh] ;CX = MemW[$40:$4E]
SHR CX,1 ;Convert offset to paragraph
SHR CX,1
SHR CX,1
SHR CX,1
ADD CX,0B800h ;Color card
;make sure we're not in graphics mode
CMP AL,3 ;In mode 0-3?
JNA GetCrtExit ;If so, in text mode
MOV InTextMode,False ;Else, graphics mode
GetCrtExit:
MOV VideoSegment,CX ;Save video segment
MOV VirtualSegment,CX
CMP DetectMultitasking,True ;Check for multitasker?
JE GCcheck ;If so, do the check
MOV MultitaskingOn,False ;Else, turn the flag off
RET
;check for multitasker
GCcheck:
MOV ES,CX ;Put video segment in ES
SetZero DI ;DI = 0
PUSH BP ;Save BP
VidCallAX 0FE00h ;Get segment of virtual screen
POP BP ;Restore BP
SetZero AL ;AL = False
MOV BX,ES ;Return value into BX
CMP BX,VideoSegment ;Same as before?
JE GCno ;If so, not multitasking
MOV VideoSegment,BX ;Save segment
MOV VirtualSegment,BX
INC AL ;AL = True
GCno:
MOV MultitaskingOn,AL ;Save multitasking flag
MOV AL,CurrentMode ;Return current mode in AL
RET
GetCrtModePrim ENDP
;****************************************************** SetWindowPrim
;Primitive routine to set current window coordinates
;On entry, the new Y coordinates must be in CX, X coordinates in DX
SetWindowPrim PROC NEAR
MOV WindMin.XLow,DL ;Set XLow
MOV WindMax.XHigh,DH ;Set XHigh
MOV WindMin.YLow,CL ;Set YLow
MOV WindMax.YHigh,CH ;Set YHigh
RET
SetWindowPrim ENDP
;****************************************************** FullWindow
;Makes the entire screen the window
FullWindow PROC NEAR
SetZero DL ;XLow = 0
MOV CL,DL ;YLow = 0
MOV DH,ScreenWidth ;DH = ScreenWidth-1
DEC DH
MOV CH,CurrentHeight ;CH = ScreenHeight-1 = CurrentHeight
CALL SetWindowPrim ;Call primitive routine
RET
FullWindow ENDP
;****************************************************** ScrollUpPrim
;Primitive routine to scroll a window up, with optional snow/flicker prevention
COMMENT |
On entry:
BH has the video attribute to use as filler
CH has top row of window
CL has left column
DH has bottom row
DL has right column
AL has number of lines to scroll, AL = 0 means clear the whole window
|
SPcharsPerRow EQU BYTE PTR [BP-1] ;# of char/attr pairs per window row
SProwsToMove EQU BYTE PTR [BP-2] ;# of rows to move
SProwsToFill EQU BYTE PTR [BP-3] ;# of rows to fill
SPattr EQU BYTE PTR [BP-4] ;attribute to fill with
SPchar EQU BYTE PTR [BP-5] ;char to fill with
ScrollUpPrim PROC NEAR
CMP BiosScroll,False ;Use special scroll routine?
JE SUspecial ;If not, use BIOS
CMP TextChar,' ' ;Use ours if TextChar <> ' '
JNE SUspecial
VideoCall 06h ;Scroll window up service
RET
SUspecial:
CLD ;Go forward
PUSH DS ;Save DS
StackFrameBP
SUB SP,5 ;Make room for locals
MOV SPattr,BH ;Save attribute
MOV AH,TextChar ;Save character
MOV SPchar,AH
MOV TextChar,' ' ;Reset TextChar
;Get rows in window into AH
MOV AH,DH ;AH = bottom row
SUB AH,CH ;AH = bottom row - top row
INC AH ;AH = Succ(bottom row - top row)
;Get rows to scroll into AL
CMP AL,AH ;AL > AH?
JNA SUcheckZero ;If not, check for AL = 0
MOV AL,AH ;Else, AL = AH
JMP SHORT SUnotWhole ;Continue
SUcheckZero:
OR AL,AL ;Is AL 0?
JNZ SUnotWhole ;If not, use actual value
MOV AL,AH ;Else, scroll entire window
SUnotWhole:
MOV SProwsToFill,AL ;Save # of rows to fill
SUB AH,AL ;AH = # of rows to move
MOV SProwsToMove,AH ;Save # of rows to move
;Get # of chars per row
SetZero AH ;AH = 0
MOV AL,DL ;AL = right column
SUB AL,CL ;AL = right column - left column
INC AL ;AL = Succ(right column - left column)
MOV SPcharsPerRow,AL ;Save # of char/attr pairs per row
MOV SI,AX ;Save in SI too
;Get starting value for DI
MOV BX,WP ScreenWidth ;BX = Chars per screen row
MOV AL,CH ;AX = top row
MUL BX ;AX = row * current width
SetZero CH ;CH = 0, CX = LeftCol
ADD AX,CX ;AX = (row * current width) + col
SHL AX,1 ;account for attribute bytes
MOV DI,AX ;DI has starting offset for top left
; corner of window
SHL SI,1 ;SI = bytes per window row
SHL BX,1 ;BX = Video words per screen row
MOV CX,BX ;CX = Video words per screen row
SUB BX,SI ;BX has delta to add when looping
SetZero AH ;AH = 0
MOV AL,SProwsToFill ;AX = # of rows to fill
MUL CX ;AX = rows to fill * words/screen row
MOV SI,DI ;SI = DI
ADD SI,AX ;Point SI to first row to move
MOV AL,CheckSnow ;Grab this before changing DS
MOV DX,VideoSegment ;DX = VideoSegment
MOV ES,DX ;ES = VideoSegment
MOV DS,DX ;DS = VideoSegment
SetZero CH ;CH = 0
SHR AL,1 ;Snow checking on?
JNC SUfullSpeed ;Skip next check if not
CMP DH,0B8h ;See if we're moving/filling CGA memory
JNE SUfullSpeed ;If not, don't wait for retrace
;Move screen data (with snow prevention)
MOV DX,03DAh ;Point DX to CGA status port
CMP SProwsToMove,0 ;Any rows to move?
JE SUstartFillWait ;If not, start filling
SUmoveWait:
MOV CL,SPcharsPerRow ;CX = # of char/attr pairs to move
WordMoveNoSnow ;Move CX words, preventing snow
ADD DI,BX ;Point DI to next row
ADD SI,BX ;Point SI to next row
DEC SProwsToMove ;Decrement # of rows to move
JNZ SUmoveWait ;If not zero, repeat
;Fill with blanks (with snow prevention)
SUstartFillWait:
MOV SI,BX ;Delta into SI
MOV BH,SPattr ;Attribute into BH
MOV BL,SPchar ;Filler into BL
SUfillWait:
MOV CL,SPcharsPerRow ;CX = # of char/attr pairs to fill
SUfillNext:
WaitForRetrace ;Wait for opportunity to write
WordToCGA BX ;Move the word
LOOP SUfillNext ;Fill next video word
ADD DI,SI ;Point DI to next row
DEC SProwsToFill ;Decrement # of rows to fill
JNZ SUfillWait ;Repeat if not zero
JMP SHORT SUExit ;All done
;Move screen data (no snow prevention)
SUfullSpeed:
MOV DL,SPcharsPerRow ;DL = # of char/attr pairs to move
MOV DH,SProwsToMove ;DH = # of rows to move
MOV AH,SPattr ;Attribute into AH
MOV AL,SPchar ;Filler into AL
OR DH,DH ;Any rows to move?
JZ SUstartFillNoWait ;If not, start filling
SUmoveNoWait:
MOV CL,DL ;CX = # of char/attr pairs to move
REP MOVSW ;Move 'em
ADD DI,BX ;Point DI to next row
ADD SI,BX ;Point SI to next row
DEC DH ;Decrement # of rows to move
JNZ SUmoveNoWait ;If not zero, repeat
;Fill with blanks (no snow prevention)
SUstartFillNoWait:
MOV DH,SProwsToFill ;DH = # of rows to fill
SUfillNoWait:
MOV CL,DL ;CX = # of char/attr pairs to move
REP STOSW ;Fill this row
ADD DI,BX ;Point DI to next row
DEC DH ;Decrement # of rows to fill
JNZ SUfillNoWait ;Repeat if not zero
SUExit:
MOV SP,BP ;Restore SP
POP BP ;Restore BP
POP DS ;Restore DS
RET ;Return
ScrollUpPrim ENDP
;****************************************************** ScrollDownPrim
;Primitive routine to scroll a window down, with optional snow/flicker prevention
COMMENT |
On entry:
BH has the video attribute to use as filler
CH has top row of window
CL has left column
DH has bottom row
DL has right column
AL has number of lines to scroll, AL = 0 means clear the whole window
|
ScrollDownPrim PROC NEAR
CMP BiosScroll,False ;Use special scroll routine?
JE SDspecial ;If not, use BIOS
CMP TextChar,' ' ;Use ours if TextChar <> ' '
JNE SDspecial
VideoCall 07h ;Scroll window down service
RET
SDspecial:
CLD ;Go forward
PUSH DS ;Save DS
StackFrameBP
SUB SP,5 ;Make room for locals
MOV SPattr,BH ;Save attribute
MOV AH,TextChar ;Save character
MOV SPchar,AH
MOV TextChar,' ' ;Reset TextChar
;Get rows in window into BL
MOV BL,DH ;BL = bottom row
SUB BL,CH ;BL = bottom row - top row
INC BL ;BL = Succ(bottom row - top row)
;Get rows to scroll into AL
CMP AL,BL ;AL > BL?
JNA SDcheckZero ;If not, check for AL = 0
MOV AL,BL ;Else, AL = AH
JMP SHORT SDnotWhole ;Continue
SDcheckZero:
OR AL,AL ;Is AL 0?
JNZ SDnotWhole ;If not, use actual value
MOV AL,BL ;Else, scroll entire window
SDnotWhole:
MOV SProwsToFill,AL ;Save # of rows to fill
SUB BL,AL ;BL = # of rows to move
MOV SProwsToMove,BL ;Save # of rows to move
;Get # of chars per row
SetZero AH ;AH = 0
MOV AL,DL ;AL = right column
SUB AL,CL ;AL = right column - left column
INC AL ;AL = Succ(right column - left column)
MOV SPcharsPerRow,AL ;Save # of char/attr pairs per row
MOV SI,AX ;Save in SI too
;Get starting value for SI
MOV AL,DH ;AX = bottom row
MOV BX,WP ScreenWidth ;BX = Chars per screen row
MUL BX ;AX = row * current width
SetZero CH ;CH = 0, CX = LeftCol
ADD AX,CX ;AX = (row * current width) + col
SHL AX,1 ;account for attribute bytes
MOV DI,AX ;SI has starting offset for last row
SHL SI,1 ;SI = bytes per window row
SHL BX,1 ;BX = Video words per screen row
MOV CX,BX ;CX = Video words per screen row
ADD BX,SI ;BX has delta to subtract when looping
SetZero AH ;AH = 0
MOV AL,SProwsToFill ;AX = # of rows to fill
MUL CX ;AX = rows to fill * words/screen row
MOV SI,DI ;SI = DI
SUB SI,AX ;Point SI to first row to move
MOV AL,CheckSnow ;Grab this before changing DS
MOV DX,VideoSegment ;DX = VideoSegment
MOV ES,DX ;ES = VideoSegment
MOV DS,DX ;DS = VideoSegment
SetZero CH ;CH = 0
SHR AL,1 ;Snow checking on?
JNC SDfullSpeed ;Skip next check if not
CMP DH,0B8h ;See if we're moving/filling CGA memory
JNE SDfullSpeed ;If not, don't wait for retrace
;Move screen data (with snow prevention)
MOV DX,03DAh ;Point DX to CGA status port
CMP SProwsToMove,0 ;Any rows to move?
JE SDstartFillWait ;If not, start filling
SDmoveWait:
MOV CL,SPcharsPerRow ;CX = # of char/attr pairs to move
WordMoveNoSnow ;Move CX words, preventing snow
SUB DI,BX ;Point DI to previous row
SUB SI,BX ;Point SI to previous row
DEC SProwsToMove ;Decrement # of rows to move
JNZ SDmoveWait ;If not zero, repeat
;Fill with blanks (with snow prevention)
SDstartFillWait:
MOV SI,BX ;Delta into SI
MOV BH,SPattr ;Attribute into BH
MOV BL,SPchar ;Filler into BL
SDfillWait:
MOV CL,SPcharsPerRow ;CX = # of char/attr pairs to fill
SDfillNext:
WaitForRetrace ;Wait for opportunity to write
MOV AX,BX ;Get word back into AX
STOSW ;Store the word
STI ;Allow interrupts
LOOP SDfillNext ;Fill next video word
SUB DI,SI ;Point DI to previous row
DEC SProwsToFill ;Decrement # of rows to fill
JNZ SDfillWait ;Repeat if not zero
JMP SHORT SDExit ;All done
;Move screen data (no snow prevention)
SDfullSpeed:
MOV DL,SPcharsPerRow ;DL = # of char/attr pairs to move
MOV DH,SProwsToMove ;DH = # of rows to move
MOV AH,SPattr ;Attribute into AH
MOV AL,SPchar ;Filler into AL
OR DH,DH ;Any rows to move?
JZ SDstartFillNoWait ;If not, start filling
SDmoveNoWait:
MOV CL,DL ;CX = # of char/attr pairs to move
REP MOVSW ;Move 'em
SUB DI,BX ;Point DI to previous row
SUB SI,BX ;Point SI to previous row
DEC DH ;Decrement # of rows to move
JNZ SDmoveNoWait ;If not zero, repeat
;Fill with blanks (no snow prevention)
SDstartFillNoWait:
MOV DH,SProwsToFill ;DH = # of rows to fill
SDfillNoWait:
MOV CL,DL ;CX = # of char/attr pairs to move
REP STOSW ;Fill this row
SUB DI,BX ;Point DI to previous row
DEC DH ;Decrement # of rows to fill
JNZ SDfillNoWait ;Repeat if not zero
SDExit:
MOV SP,BP ;Restore SP
POP BP ;Restore BP
POP DS ;Restore DS
RET ;Return
ScrollDownPrim ENDP
;****************************************************** CrtNop
;Do-nothing file handling procedure
;On entry, a pointer to a FileRec is on the stack
CrtNop PROC FAR
SetZero AX ;Signal AOK
RET 4
CrtNop ENDP
;****************************************************** OutputChar
;On entry, AL has character to write
OutputChar PROC NEAR
PUSH DI ;Save DI
MOV CL,AL ;CL has character
MOV CH,TextAttr ;CH has TextAttr
CMP DirectVideo,True ;Write directly to video memory?
JNE BiosOutput ;If not, use BIOS
CMP InTextMode,True ;In text mode?
JNE BiosOutput ;If not, use BIOS
CMP MultitaskingOn,True ;Multitasking?
JNE OCgetCursor ;if not, don't need BIOS call
;get cursor position with BIOS call
PUSH CX ;Save CX
MOV BH,CurrentPage ;BH = current display page
VideoCall 3 ;Read cursor service
POP CX ;Restore CX
JMP SHORT OCcalcAddress ;Ready to calculate address
;get cursor position from BIOS data area into DX
OCgetCursor:
MOV BX,0040h ;BX = $40
MOV ES,BX ;ES = $40
MOV BL,CurrentPage ;BL = CurrentPage, BH = 0
SHL BX,1 ;Multiply by 2
MOV DX,ES:50h[BX] ;DX has cursor position for CurrentPage
;calculate address in video memory
OCcalcAddress:
MOV AL,DH ;AL = row
SetZero DH ;DH = 0 (DX = column)
MUL ScreenWidth ;AL = (row * width)
MOV DI,AX ;DI = (row * width)
ADD DI,DX ;DI = (row * width) + column
SHL DI,1 ;Account for attribute bytes
MOV ES,VideoSegment ;ES has segment of video memory
;determine which method of output to use
CMP CheckSnow,False ;Snow checking off?
JE OutputStore ;If so, go
;Use the following only for color cards when retrace checking is on
MOV DX,03DAh ;Point DX to CGA status port
WaitForRetrace ;Wait for an opportunity to write
OutputStore:
WordToCGA CX ;Write the word
JMP SHORT OutputExit ;Return
;Write with a BIOS call
BiosOutput:
MOV AL,CL ;AL = character
MOV BL,CH ;BL = Video attribute
MOV BH,CurrentPage ;BH = Current video page
MOV CX,1 ;CX = number of chars to repeat
VideoCall 9 ;Write character and attribute service
OutputExit:
POP DI ;Restore DI
RET
OutputChar ENDP
;****************************************************** DoLineFeed
DoLineFeed PROC NEAR
INC DH ;Next row
CMP DH,WindMax.YHigh ;Still inside window?
JBE DLFexit ;If so, go
DEC DH ;Back up to current row
PUSH BX ;save registers
PUSH CX
PUSH DI
PUSH SI
PUSH ES
PUSH DX
MOV BH,TextAttr ;Filler character
MOV CX,WindMin ;Top row and left column
MOV DX,WindMax ;Bottom row and right column
MOV AL,1 ;AL = 1 (lines to scroll)
CALL ScrollUpPrim ;Call scroll window up primitive
POP DX
POP ES ;Restore registers
POP SI
POP DI
POP CX
POP BX
DLFexit:
RET
DoLineFeed ENDP
;****************************************************** WriteChar
;Write one character
;On entry, AL has character to write
WriteChar PROC NEAR
;Save registers
PUSH DI
PUSH SI
PUSH BX
PUSH CX
PUSH DX
PUSH ES
;Get current position of cursor
PUSH AX
CALL GetCursorPrim
POP AX
;Check for special characters
CMP AL,CR ;Carriage return?
JE CarriageReturn
CMP AL,LineFeed ;Line feed?
JE WCLineFeed
CMP AL,BkSp ;Backspace?
JE BackSpace
CMP AL,Bell ;Bell (^G)?
JE RingBell
;Normal character
PUSH DX ;Save DX
CALL OutputChar ;Write the character
POP DX ;Restore DX
;Now move the cursor
INC DL ;Cursor to next column
CMP DL,WindMax.XHigh ;Still inside window?
JBE WCSetCursor ;If so, move cursor and exit
MOV DL,WindMin.XLow ;Else, do a 'carriage return'...
JMP SHORT WCLineFeed ;... and 'line feed'
RingBell:
VideoCall 0Eh ;Write character as TTY
JMP SHORT WCExit ;Done
BackSpace:
CMP DL,WindMin.XLow ;Cursor at start of this column?
JE WCExit ;If so, do nothing
DEC DL ;Else, previous column
JMP SHORT WCSetCursor ;Move cursor and exit
CarriageReturn:
MOV DL,WindMin.XLow ;First column in window
JMP SHORT WCSetCursor ;Move cursor and exit
WCLineFeed:
CALL DoLineFeed ;generate a line feed
WCSetCursor:
CALL SetCursorPrim
WCExit:
POP ES ;Restore registers
POP DX
POP CX
POP BX
POP SI
POP DI
RET
WriteChar ENDP
;****************************************************** FlushString
FlushString PROC NEAR
PUSH CX ;save CX
MOV CX,DI ;get number of characters to write
SUB CX,SI
JCXZ FSexit ;done if CX = 0
PUSH DX ;save registers used
PUSH DI
PUSH DS
PUSH ES
;calculate address in video memory
MOV AL,BH ;AL = row
SetZero BH ;BH = 0 (BX = column)
MUL ScreenWidth ;AL = (row * width)
MOV DI,AX ;DI = (row * width)
ADD DI,BX ;DI = (row * width) + column
SHL DI,1 ;Account for attribute bytes
MOV AH,TextAttr ;AH has attribute
MOV AL,CheckSnow ;AL = CheckSnow
MOV BX,VideoSegment ;BX = VideoSegment
PUSH ES ;DS:SI points to string
POP DS
MOV ES,BX ;ES has segment of video memory
;determine which method of output to use
CLD ;go forward
OR AL,AL ;Snow checking off?
JZ FSfast ;If so, go fast
MOV DX,03DAh ;Point DX to CGA status port
;Use the following only for color cards when retrace checking is on
FSgetNext:
LODSB ;Load next character into AL
MOV BX,AX ;Store video word in BX
WaitForRetrace ;Wait for an opportunity to write
WordToCGA BX ;Move the word
LOOP FSgetNext ;Get next character
JMP SHORT FSdone ;Done
FSfast:
LODSB ;load next character
STOSW ;write it
LOOP FSfast ;get next character
FSdone:
POP ES ;restore registers
POP DS
POP DI
POP DX
FSexit:
POP CX
RET
FlushString ENDP
;****************************************************** WriteString
;on entry: ES:DI points to start of string, CX has its length
WriteString PROC NEAR
PUSH ES
PUSH DI
PUSH CX
CALL GetCursorPrim ;Get cursor coordinates into DX
POP CX
POP DI
POP ES
MOV BX,DX ;save them in BX
MOV SI,DI ;ES:SI points to start of string
WSnext:
MOV AL,ES:[DI] ;next character into AL
CMP AL,CR ;check for control characters
JA WSplain
JE WScr
CMP AL,LineFeed
JE WSlinefeed
CMP AL,BkSp
JE WSbksp
CMP AL,Bell
JE WSbell
WSplain:
INC DI ;point to next character
INC DL ;next column
CMP DL,WindMax.XHigh ;past end of window?
JBE WSskip ;if not, skip to next character
CALL FlushString ;else, flush string
CALL DoLineFeed ;do a line feed
MOV DL,WindMin.XLow ;DL has first column
JMP SHORT WSnoAdvance
WSbell:
CALL FlushString ;flush the string
Write Bell ;sound the bell
JMP SHORT WSadvance
WSbksp:
CALL FlushString ;flush the string
CMP DL,WindMin.XLow ;already at first column?
JE WSadvance ;if so, don't back up
DEC DL ;else, previous column
JMP SHORT WSadvance
WSlinefeed:
CALL FlushString ;flush the string
CALL DoLineFeed ;do a line feed
JMP SHORT WSadvance
WScr:
CALL FlushString ;flush the string
MOV DL,WindMin.XLow ;DL has first column
WSadvance:
INC DI ;point to next character
WSnoAdvance:
MOV SI,DI ;SI = DI
MOV BX,DX ;save cursor position
WSskip:
LOOP WSnext ;do it again
CALL FlushString ;flush the string
CALL SetCursorPrim ;position the cursor
RET
WriteString ENDP
;****************************************************** WriteCrt
;On entry, a pointer to an FIB is on the stack
WriteCrt PROC FAR
StackFrame
GetPtr TextFile ;ES:DI points to file
MOV CX,TFPtr.FBufPos ;CX = number of chars to write
JCXZ WCrtExit ;Exit if string empty
SUB TFPtr.FBufPos,CX ;Back up pointer to start of string
GetPtr TFPtr.FBufPtr ;Point ES:DI to the start of the buffer
MOV TextChar,' ' ;Force TextChar to ' '
CMP DirectVideo,True ;Write directly to screen?
JNE WriteNext ;nope, write character at a time
CMP InTextMode,True
JNE WriteNext
CALL WriteString ;else, write string at a time
JMP SHORT WCrtExit ;we're done
WriteNext:
Write TFPtr ;Write the next character
INC DI ;point to next char
LOOP WriteNext ;Next character
WCrtExit:
CALL BreakCheck ;Check for ^Break
SetZero AX ;Signal AOK
RET 4
WriteCrt ENDP
;****************************************************** ReadCrt
;On entry, a pointer to an FIB is on the stack
;constants
CrLf = 0A0Dh
COMMENT |
Register usage:
BX always points to the next available slot in the text buffer
CX contains a loop count (0 or 1). When CX = 0, LOOP instructions
set CX to FFFFh, so the loop gets executed until some other
condition is met.
|
ReadCrt PROC FAR
StackFrameBP
GetPtr TextFileBP ;ES:DI points to FileRec
MOV SI,TFPtr.FBufPos ;SI = BufPos
MOV CX,TFPtr.FBufSize ;CX = BufSize
DEC CX
DEC CX ;CX = 126
PUSH CX ;Save this for later
SetZero BX ;Start at beginning of the buffer
GetPtr TFPtr.FBufPtr ;ES:DI points to BufPtr^
MOV AX,BufLen ;AX = BufLen
OR AX,AX ;AX = 0?
JZ ResetBufLen ;If so, set it to the default
CMP AX,CX ;BufLen > BufSize?
JNA ReadNext ;If not, proceed
ResetBufLen:
MOV Buflen,CX ;BufLen = 126
ReadNext:
MOV NextChar,Null ;Filter keys with extended scan codes
CALL ReadKey ;Get next character into AL
MOV CX,1 ;Loop counter = 1
CMP AL,BkSp ;Backspace?
JE ReadBkSp
CMP AL,CtrlS ;^S? (= Backspace)
JE ReadBkSp
CMP AL,CtrlD ;^D? (Restore one character)
JE ReadRestore
DEC CX ;BkSp loop counter = 0
CMP AL,Escape ;Escape? (Delete line)
JE ReadBkSp
CMP AL,CtrlBkSp ;^BkSp? (Delete line)
JE ReadBkSp
CMP AL,CtrlA ;^A? (Delete line)
JE ReadBkSp
CMP AL,CtrlF ;^F? (Restore whole line)
JE ReadRestore
CMP AL,CtrlZ ;^Z? (close Input?)
JE ReadEofCheck ;If so, see if we should close shop
CMP AL,Return ;Enter key?
JE ReadCrlf ;If so, write a CrLf and stop
CMP AL,' ' ;Regular ASCII character?
JB ReadNext ;If not, continue
;Process the character
CMP BX,BufLen ;Line full?
JE ReadNext ;If so, don't add to it
MOV TFPtr[BX],AL ;Add character to end of buffer
CALL WriteChar ;Write the character
INC BX ;Increment the buffer pointer
CMP BX,SI ;BX > BufPos?
JNA ReadNext ;If not, continue
MOV SI,BX ;Else set BufPos to BX
JMP SHORT ReadNext
ReadExit:
GetPtr TextFileBP ;ES:DI points to FileRec
POP CX ;Get back default Buflen setting
MOV BufLen,CX ;Reset BufLen to default
SetZero AX ;No I/O error
MOV TFPtr.FBufPos,AX ;Reset BufPos to 0 for next read
MOV TFPtr.FBufEnd,BX ;FBufEnd has length of the string
ExitCode 4
ReadRestore:
CMP BX,SI ;Does BX point to end of buffer?
JE ReadNext ;If so, do nothing
MOV AL,TFPtr[BX] ;Next character into AL
CMP AL,' ' ;Is it a control character?
JB ReadNext ;If so, do nothing
CALL WriteChar ;Write it
INC BX ;Point to next character in buffer
LOOP ReadRestore ;Repeat if CX was 0 on entry
ReadJump:
JMP SHORT ReadNext
ReadEofCheck:
CMP CheckEof,False ;CheckEof false?
JE ReadNext ;If so, continue
MOV TFPtr[BX],AL ;Add character to end of buffer
INC BX ;Increment BX
JMP SHORT ReadExit ;And exit
ReadBkSp:
OR BX,BX ;BX = 0?
JZ ReadJump ;If so, don't back up
Write BkSp ;Move the cursor back
Write ' ' ;Overwrite the last character
Write BkSp ;Move the cursor back again
DEC BX ;Decrement buffer pointer
LOOP ReadBkSp ;Repeat if CX was 0 on entry
JMP ReadNext ;Continue
ReadCrlf:
WriteLn ;Write a CR/LF
MOV WP TFPtr[BX],CrLf ;Add the CrLf to the buffer
INC BX
INC BX
JMP SHORT ReadExit ;And exit
ReadCrt ENDP
;****************************************************** OpenCrt
;Open input or output files
;Parameter is a pointer to an FIB
COMMENT |
Register usage:
AX has offset of the Read/Write procedure
BX Flush
CX Close
|
OpenCrt PROC FAR
StackFrame
GetPtr TextFile ;ES:DI points to FileRec
;assume we're opening for input
MOV CX,OFFSET CrtNop ;CX = Ofs(CrtNop)
MOV BX,CX ;BX = Ofs(CrtNop)
MOV AX,OFFSET ReadCrt ;AX = Ofs(ReadCrt)
CMP TFPtr.FMode,FMInput ;Opening for input?
JE OpenIt ;If so, AX-BX-CX are correct
;nope, opening for output
MOV TFPtr.FMode,FMOutput ;Open for output
MOV AX,OFFSET WriteCrt ;AX = Ofs(WriteCrt)
MOV BX,AX ;BX = Ofs(WriteCrt) (Flush with write)
OpenIt:
SetPtr TFPtr.FInOutProc, CS, AX ;Set up Read/Write routine
SetPtr TFPtr.FFlushProc, CS, BX ;Set up Flush routine
SetPtr TFPtr.FCloseProc, CS, CX ;Do nothing with a Close()
SetZero AX ;Signal success
RET 4
OpenCrt ENDP
;****************************************************** AssignCrt
;procedure AssignCrt(var F : Text);
;Parameter is a pointer to an FIB
AssignCrt PROC FAR
StackFrame
GetPtr TextFile ;ES:DI points to F
MOV BX, OFFSET OpenCrt ;BX has offset of OpenCrt
SetPtr TFPtr.FOpenProc, CS, BX ;Open with OpenCrt
MOV TFPtr.FMode,FMClosed ;File is closed
MOV TFPtr.FName,0 ;No filename
LEA AX,TFPtr.FBuffer ;Set up file buffer
MOV TFPtr.FBufPtr.Segm,ES ;Initialize record
MOV TFPtr.FBufPtr.Ofst,AX
MOV AX,128 ;AX = Default buffersize
MOV TFPtr.FBufSize,AX ;Store Buffer size
DEC AX
DEC AX ;AX = Buffer size - 2
MOV BufLen,AX ;BufLen = Buffer size - 2
RET 4
AssignCrt ENDP
;****************************************************** ReInitCrt
;procedure ReInitCrt;
;Reinitialize CRT unit's internal variables. For TSR's or programs with
;DOS shells.
ReInitCrt PROC FAR
;save current video mode
CALL GetCrtModePrim ;Current video mode into AL
;check for EGA/VGA/etc.
CALL AdapterCheck ;Check for EGA
CALL WhichHercCard ;Check for Herc card type
MOV WhichHerc,AL ;save it in WhichHerc
;set retrace checking variable
MOV CheckSnow,False ;Assume false
CMP IsCompaq,True ;Is it a Compaq?
JE SetWindow ;If so, won't need to wait
CMP CurrentDisplay,CGA ;CGA installed?
JNE SetWindow ;If not, won't need to wait
INC CheckSnow ;Else, may have to wait when writing
; to the color card
SetWindow:
CALL FullWindow ;set window coordinates
RET
ReInitCrt ENDP
;****************************************************** InitCrt
;procedure InitCrt;
;Initialization stuff
InitCrt PROC NEAR
;Run test to see if DirectVideo will cause problems
MOV DirectVideo,False ;try it with DirectVideo false
CALL GetCursorPrim ;Get current coordinates into DX
PUSH DX ;save result on stack
MOV DirectVideo,True ;try it with DirectVideo on
CALL GetCursorPrim ;Get current coordinates into DX
POP AX ;first result into AX
CMP AX,DX ;results the same?
JE DirectVideoOK ;if so, DirectVideo=True presumably OK
MOV DirectVideo,False ;else, force it to false
DirectVideoOK:
SetZero AL ;AL = 0
MOV NextChar,AL ;NextChar = 0
MOV CtrlBreakFlag,AL ;CtrlBreakFlag = False
MOV WP ScreenWidth,80 ;ScreenWidth = 80
MOV WP VirtualWidth,80
CALL ReinitCrt ;Init code shared w/ ReInitCrt
CALL GetAttribute ;get current attribute at cursor
;set up our ExitProc routine
MOV AX, OFFSET CrtExitProc ;AX has offset of CrtExitProc
GetPtr ExitProc ;ES:DI points to ExitProc^
SetPtr SaveExitProc, ES, DI ;Save current value of ExitProc
SetPtr ExitProc, CS, AX ;Replace with address of CrtExitProc
;take over INT 1Bh
MOV AX,351Bh ;AH = Get interrupt vector service
;AL = interrupt vector
INT 21h ;Call DOS
SetPtr Save1B, ES, BX ;Save pointer returned in ES:BX
MOV DX,OFFSET Int1B ;DX = Ofs(Int1B)
PUSH DS ;Save DS
MOV AX,CS ;AX = CS
MOV DS,AX ;DS = CS
MOV AX,251Bh ;AH = Set interrupt vector service
;AL = interrupt vector
INT 21h ;Call DOS
POP DS ;Restore DS
;set up delay count
MOV AX,40h ;AX = $40
MOV ES,AX ;ES = $40
MOV DI,6Ch ;ES:DI => low word of BIOS timer count
MOV AL,ES:[DI] ;AL has first byte there
STI
WaitForChange:
CMP AL,ES:[DI] ;wait for the byte to change
JZ WaitForChange
MOV AL,ES:[DI] ;see how many times DelayMS has to
MOV CX,0FFFFh ;loop before the timer count changes
CALL DelayMS
MOV AX,55 ;now calculate OneMS
XCHG CX,AX
NOT AX
XOR DX,DX
DIV CX
MOV OneMS,AX ;AX has OneMS
RET
InitCrt ENDP
CODE ENDS
END