mirror of
https://github.com/VARCem/PTV_Archive.git
synced 2026-07-08 18:16:20 +00:00
1597 lines
42 KiB
NASM
1597 lines
42 KiB
NASM
;******************************************************
|
||
; 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
|
||
|
||
|