dos_compilers/Microsoft QuickC v2/SAMPLES/MISC.ASM
2024-07-02 06:32:02 -07:00

1066 lines
31 KiB
NASM

.MODEL small, c
INCLUDE demo.inc
.CODE
;* DisableCGA - Disables CGA video by reprogramming the control register.
;*
;* Shows: Instructions - cli sti loopz loopnz
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: None
;*
;* Return: None
DisableCGA PROC \
USES ax cx dx ; Preserve registers
mov cx, -1 ; Set maximum loop count
mov dx, 03DAh ; Address of status register
wait1: in al, dx ; Get video status
test al, 8 ; Vertical retrace active?
loopnz wait1 ; Yes? Wait for end/timeout
cli ; Disallow interruptions
mov cx, -1 ; Reset loop count
wait2: in al, dx ; Get video status
test al, 8 ; Start of vertical retrace?
loopz wait2 ; No? Wait for start/timeout
sub dx, 2 ; DX = address of control reg
mov al, 1 ; Value to disable CGA video
out dx, al ; Disable video
sti ; Reenable interrupts
ret
DisableCGA ENDP
;* EnableCGA - Enables CGA video by reprogramming the control register.
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: None
;*
;* Return: None
EnableCGA PROC \
USES ax dx ; Preserve registers
mov dx, 03D8h ; Address of control register
mov al, vconfig.CGAvalue ; Reprogram with proper value
out dx, al ; Enable video
ret
EnableCGA ENDP
;* WinOpen - Saves portion of screen to allocated memory, then opens a window
;* with specified fill attribute. See also the WinClose procedure.
;*
;* Shows: DOS Function - 48h (Allocate Memory Block)
;* BIOS Interrupt - 10h, Function 6 (Initialize or Scroll Up Window)
;* Instructions - movsw stosw rep
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: row1 - Row at top of window
;* col1 - Column at left edge of window
;* row2 - Row at bottom of window
;* col2 - Column at right edge of window
;* attr - Fill attribute for window
;*
;* Return: Short integer with segment address of allocated buffer, or
;* 0 if unable to allocate memory
WinOpen PROC \
USES ds di si, \
row1:WORD, col1:WORD, row2:WORD, col2:WORD, attr:WORD
GetVidOffset row1, col1 ; Get offset in video segment
mov si, ax ; SI = video offset for window
mov bx, row2
sub bx, row1
inc bx ; BX = number of window rows
mov cx, col2
sub cx, col1
inc cx ; CX = number of columns
mov ax, cx ; Compute number of video
mul bl ; cells in window
add ax, 3 ; Plus 3 additional entries
shr ax, 1 ; Shift right 3 times to
shr ax, 1 ; multiply by 2 bytes/cell,
shr ax, 1 ; divide by 16 bytes/para
inc ax ; Add a paragraph
push bx ; Save number of rows
mov bx, ax ; BX = number of paragraphs
mov ah, 48h ; Request DOS Function 48h
int 21h ; Allocate Memory Block
pop bx
jnc @F ; If successful, continue
sub ax, ax ; Else return null pointer
jmp SHORT exit
@@: mov es, ax ; Point ES:DI to allocated
sub di, di ; buffer
mov ax, si
stosw ; Copy video offset to buffer
mov ax, bx
stosw ; Number of rows to buffer
mov ax, cx
stosw ; Number of cols to buffer
mov ax, 160 ; Number of video cells/row
mov ds, vconfig.sgmnt ; DS = video segment
loop1: push si ; Save ptr to start of line
push cx ; and number of columns
cmp vconfig.adapter, CGA ; CGA adapter?
jne @F ; No? Skip video disable
; For CGA adapters, WinOpen avoids screen "snow" by disabling the video prior
; to block memory moves, then reenabling it. Although this technique can
; result in brief flickering, it demonstrates the fastest way to access a
; block in the CGA video buffer without causing display snow. See also the
; StrWrite procedure for another solution to the problem of CGA snow.
call DisableCGA ; Yes? Disable video
@@: rep movsw ; Copy one row to buffer
cmp vconfig.adapter, CGA
jne @F
call EnableCGA ; Reenable CGA video
@@: pop cx ; Recover number of columns
pop si ; and start of line
add si, ax ; Point to start of next line
dec bx ; Decrement row counter
jnz loop1 ; Loop while rows remain
; Screen contents (including display attributes) are now copied to buffer.
; Next open window, overwriting the screen portion just saved.
mov ax, 0600h ; Scroll service
mov bh, BYTE PTR attr ; Fill attribute
mov cx, col1 ; CX = row/col for upper left
mov ch, BYTE PTR row1
mov dx, col2 ; DX = row/col for lower right
mov dh, BYTE PTR row2
int 10h ; Blank window area on screen
mov ax, es ; Return address of allocated
exit: ret ; segment
WinOpen ENDP
;* WinClose - "Closes" a window previously opened by the WinOpen procedure.
;* See also the WinOpen procedure.
;*
;* Shows: DOS Function - 49h (Release Memory Block)
;* Instructions - lodsw
;* Operators - : (segment override) SEG
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: addr - Segment address of buffer that holds screen contents
;* saved in WinOpen procedure
;*
;* Return: None
WinClose PROC \
USES ds di si, \
addr:WORD
mov ds, addr ; DS:SI points to buffer
sub si, si
lodsw
mov di, ax ; DI = video offset of window
lodsw
mov bx, ax ; BX = number of window rows
lodsw
mov cx, ax ; CX = number of columns
mov ax, SEG vconfig.sgmnt
mov es, ax ; Point ES to data segment
push es:vconfig.sgmnt
pop es ; ES = video segment
mov ax, 160 ; Number of video cells/row
loop1: push di ; Save ptr to start of line
push cx ; and number of columns
cmp vconfig.adapter, CGA ; CGA adapter?
jne @F ; No? Skip video disable
; Disable CGA video prior to memory move to avoid screen snow. (See the
; WinOpen and StrWrite procedures for further discussions on CGA snow.)
call DisableCGA ; Yes? Disable video
@@: rep movsw ; Copy one row to buffer
cmp vconfig.adapter, CGA
jne @F
call EnableCGA ; Reenable CGA video
@@: pop cx ; Recover number of columns
pop di ; and start of line
add di, ax ; Point to start of next line
dec bx ; Decrement row counter
jnz loop1 ; Loop while rows remain
mov ah, 49h ; Request DOS Function 49h
mov es, addr
int 21h ; Release Memory Block
ret
WinClose ENDP
;* SetCurSize - Sets cursor size.
;*
;* Shows: BIOS Interrupt - 10h, Function 1 (Set Cursor Type)
;*
;* Params: scan1 - Starting scan line
;* scan2 - Ending scan line
;*
;* Return: None
SetCurSize PROC \
scan1:WORD, scan2:WORD
mov cx, scan2 ; CL = ending scan line
mov ch, BYTE PTR scan1 ; CH = starting scan line
mov ah, 1 ; Function 1
int 10h ; Set Cursor Type
ret
SetCurSize ENDP
;* GetCurSize - Gets current cursor size.
;*
;* Shows: BIOS Interrupt - 10h, Function 3 (Get Cursor Position)
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: None
;*
;* Return: Short integer with high byte = top scan line,
;* low byte = bottom scan line
GetCurSize PROC
mov ah, 3 ; Function 3
mov bh, vconfig.dpage
int 10h ; Get Cursor Position
mov ax, cx ; Return cursor size
ret
GetCurSize ENDP
;* GetCurPos - Gets current cursor position.
;*
;* Shows: BIOS Interrupt - 10h, Function 3 (Get Cursor Position)
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: None
;*
;* Return: Short integer with high byte = row, low byte = column
GetCurPos PROC
mov ah, 3 ; Function 3
mov bh, vconfig.dpage
int 10h ; Get cursor position
mov ax, dx ; Return cursor row/column
ret
GetCurPos ENDP
;* GetShift - Gets current shift status. Checks for extended keyboard,
;* and if available returns additional shift information.
;*
;* Shows: BIOS Interrupt - 16h, Functions 2 and 12h (Get Keyboard Flags)
;*
;* Params: None
;*
;* Return: Long integer
;* high word = 0 for non-extended keyboard
;* 1 for extended keyboard
;* low word has following bits set when indicated keys are pressed:
;* 0 - Right shift 8 - Left Ctrl
;* 1 - Left shift 9 - Left Alt
;* 2 - Ctrl 10 - Right Ctrl
;* 3 - Alt 11 - Right Alt
;* 4 - Scroll Lock active 12 - Scroll Lock pressed
;* 5 - Num Lock active 13 - Num Lock pressed
;* 6 - Caps Lock active 14 - Caps Lock pressed
;* 7 - Insert toggled 15 - Sys Req pressed
GetShift PROC
sub dx, dx ; Assume non-extended keyboard
mov ah, 2 ; and use Function 2
mov es, dx ; Point ES to low memory
test BYTE PTR es:[496h], 16 ; Extended keyboard installed?
jz @F ; No? Leave AH as Function 2
inc dx ; Yes? Set high word of return code
mov ah, 12h ; and use Function 12h
@@: int 16h ; Get Keyboard Flags
ret
GetShift ENDP
;* GetKeyClock - Waits for keypress while updating time at specified location
;* on screen.
;*
;* Shows: BIOS Interrupt - 16h, Functions 0 and 10h (Read Character)
;* 16h, Functions 1 and 11h (Get Keyboard Status)
;* DOS Functions - 2Ah (Get Date)
;* 2Ch (Get Time)
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: row - Screen row for clock display
;* col - Screen column for clock display
;*
;* Return: Short integer with key scan code in high byte and ASCII
;* character code in low byte. Low byte is 0 for special
;* keys (such as the "F" keys) which don't generate characters.
.DATA
PUBLIC datestr
datestr DB ' - - : : ', 0 ; Date/time string
.CODE
EXTRN StrWrite:PROC
GetKeyClock PROC \
row:WORD, col:WORD
LOCAL service:BYTE
call GetShift ; Check for extended keyboard
mov service, 11h ; Request for Function 11h
cmp dx, 1 ; Extended keyboard available?
je key1 ; Yes? Set AH appropriately
mov service, 1 ; No? Set AH for Function 1
key1: mov ah, service
int 16h ; Get Keyboard Status
jnz exit ; Ready? Exit procedure
; Not ready? Check text mode
cmp vconfig.mode, 7 ; Monochrome text mode?
je @F ; Yes? Continue
cmp vconfig.mode, 3 ; Color text mode?
je @F ; Yes? Continue
cmp vconfig.mode, 2 ; Black/white?
jne key1 ; No? Skip clock update and
; poll keyboard again
; If 80-column text, get date and time from DOS before again polling keyboard,
; and display at upper right corner of screen.
@@: mov ah, 2Ch ; Request time
int 21h ; Get Time
mov dl, dh
push dx ; Save seconds,
push cx ; minutes,
mov cl, ch ; and
push cx ; hours
mov ah, 2Ah ; Request date
int 21h ; Get Date
sub cx, 1900 ; Subtract century, CL = year
push cx ; Save year,
push dx ; day,
mov dl, dh ; and
push dx ; month
mov cx, 6
sub bx, bx
loop1: pop ax ; Recover all 6 numbers in AL
aam ; Convert to unpacked BCD
xchg al, ah ; Switch bytes for word move
or ax, '00' ; Make ASCII numerals
mov WORD PTR datestr[bx], ax; Copy to string
add bx, 3 ; at every third byte
loop loop1
DispText row, col, <OFFSET datestr>
jmp key1 ; Loop again for keypress
exit: mov ah, service ; 1 or 11h, depending on keybd
dec ah ; Set AH to 0 or 10h
int 16h ; Get key to remove it from
ret ; keyboard buffer
GetKeyClock ENDP
;* GetPSP - Gets address of Program Segment Prefix. For DOS 3.0 or higher.
;*
;* Shows: DOS Function - 62h (Get PSP Address)
;* Instruction - call (See the MISCDEMO.ASM example program
;* for an example of a call dispatch table)
;*
;* Params: None
;*
;* Return: Short integer with PSP segment address
;* or 0 if DOS version below 3.0
EXTRN GetVer:PROC
GetPSP PROC
call GetVer ; Get DOS version number
cmp ax, 300 ; Version 3.0 or higher?
jb dos2 ; No? Return error code
mov ah, 62h ; Yes? Query DOS for PSP
int 21h ; Get PSP Address
mov ax, bx ; Put in AX
jmp SHORT exit ; Exit
dos2: sub ax, ax ; For version 2, return 0
exit: ret
GetPSP ENDP
;* GetMem - Gets total size of memory and determines the largest amount of
;* unallocated memory available. GetMem invokes DOS Function 48h (Allocate
;* Memory) to request an impossibly large memory block. DOS denies the re-
;* quest, but returns instead the size of the largest block available. This
;* is the amount that GetMem returns to the calling program. See the WinOpen
;* procedure for an example of calling Function 48h to allocate unused memory.
;*
;* Shows: BIOS Interrupt - 12h (Get Conventional Memory Size)
;*
;* Params: None
;*
;* Return: Long integer, high word = total memory in kilobytes (KB)
;* low word = largest block of available memory (KB)
GetMem PROC
int 12h ; Get total memory in K
push ax ; Save size of memory
mov ah, 48h ; Request memory allocation
mov bx, 0FFFFh ; Ensure request is denied for
; impossibly large block
int 21h ; Get largest available block in BX
mov ax, bx ; Copy to AX
mov cl, 6 ; Convert paragraphs to kilobytes by
shr ax, cl ; dividing by 64
pop dx ; Recover total in DX
ret ; Return long integer DX:AX
GetMem ENDP
;* VeriPrint - Checks if LPT1 (PRN) is available.
;*
;* Shows: BIOS Interrupt - 17h (Parallel Port Printer Driver)
;*
;* Params: None
;*
;* Return: Short integer, 1 for yes or 0 for no
VeriPrint PROC
mov ah, 2 ; Check printer status for
sub dx, dx ; parallel printer (port 0)
int 17h
xchg dx, ax ; Put 0 (for error) in AX
test dh, 00101001b ; Are any error bits on?
jne exit ; Yes? Leave 0
test dh, 10010000b ; Are both operation bits on?
jz exit ; No? Leave 0
inc ax ; Yes? Return 1
exit: ret
VeriPrint ENDP
;* IntToAsc - Converts integer to ASCII string. This procedure is useful
;* only for assembly language, and is not intended to be C-callable.
;*
;* Shows: Instructions - cwd aam xchg
;*
;* Entry: AX = integer (9999 max)
;*
;* Return: DX:AX = 4-digit ASCII number
IntToAsc PROC
cwd ; Zero DX register
mov cx, 100 ; Divide AX by 100, yields
div cx ; AX=quotient, DX=remainder
aam ; Make digits unpacked BCD
or ax, '00' ; Convert to ASCII
xchg ax, dx ; Do same thing for DX
aam
or ax, '00'
ret ; Return DX:AX = ASCII number
IntToAsc ENDP
;* VeriAnsi - Checks for ANSI driver by writing ANSI sequence to report
;* cursor position. If report compares with position returned from
;* GetCurPos procedure, then ANSI driver is operating.
;*
;* Shows: DOS Functions - 06h (Direct Console I/O)
;* 0Ch (Flush Input Buffer and then Input)
;*
;* Params: None
;*
;* Return: Short integer, 1 for yes or 0 for no
.DATA
PUBLIC report
report DB ESCAPE, '[6n$' ; ANSI Report Cursor sequence
.CODE
VeriAnsi PROC
call GetCurPos ; Cursor position from BIOS
mov cx, ax ; Save it in CX
mov dx, OFFSET report ; ANSI string to get position
mov ah, 9 ; Request DOS String Output
int 21h ; Write ANSI escape sequence
mov ah, 6 ; Skip Esc character in
mov dl, 0FFh ; keyboard buffer
int 21h
jz e_exit ; If no key, ANSI not loaded
mov ah, 6 ; Skip '[' character
int 21h
jz e_exit ; If no key, ANSI not loaded
mov ah, 6 ; Get 1st digit of cursor row
int 21h
jz e_exit ; If no key, ANSI not loaded
mov bh, al ; Store in BH
mov ah, 6 ; Get 2nd digit of cursor row
int 21h
jz e_exit ; If no key, ANSI not loaded
mov bl, al ; Store in BL
mov al, ch ; Get original row # in AL
cbw ; AX = row # from GetCurPos
inc ax ; Add 1 to it
call IntToAsc ; Make ASCII digits
cmp ax, bx ; ANSI and BIOS reports match?
jne e_exit ; No? Then ANSI not loaded
mov ax, 0C06h ; Flush remaining ANSI keys
mov dl, 0FFh ; from buffer
int 21h
mov ax, 1 ; Set 1 for true
jmp SHORT exit ; and exit
e_exit: sub ax, ax ; Set 0 return code if no
exit: ret ; ANSI driver installed
VeriAnsi ENDP
;* VeriCop - Checks for coprocessor.
;*
;* Shows: BIOS Interrupt - 11h (Get Equipment Configuration)
;*
;* Params: None
;*
;* Return: Short integer, 1 for yes or 0 for no
VeriCop PROC
int 11h ; Check peripherals
test al, 2 ; Coprocessor?
mov ax, 0 ; Assume no, don't alter flags
jz exit ; No? Done
inc ax ; Yes? Set to 1
exit: ret
VeriCop ENDP
;* SetLineMode - Sets line mode for EGA or VGA.
;*
;* Shows: BIOS Interrupt - 10h, Function 11h (Character Generator Interface)
;* 10h, Function 12h (Video Subsystem Configuration)
;* Instruction - cmp
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: line - Requested line mode (25, 43, or 50)
;*
;* Return: Short integer with error code
;* 0 if successful
;* 1 if error
SetLineMode PROC \
line:WORD
cmp vconfig.adapter, EGA ; EGA or VGA?
jge @F ; Yes? Continue
jmp e_exit ; No? Exit with error
@@: mov ax, line ; Check for valid parameter
cmp al, 25
je line25
cmp al, 43
je line43
cmp al, 50
je line50
jmp SHORT e_exit ; If not 25, 43, or 50, exit w/ error
line25: mov al, 11h ; Set for EGA 25-line mode
cmp vconfig.adapter, EGA ; EGA?
je lmode ; Yes? Continue
mov ax, 1202h ; No? Function 12h for VGA
mov bl, 30h ; AL = 2 for 400 scan lines
int 10h ; Reset to 400 scan lines
mov ax, 0003 ; Reset mode (Function 0)
int 10h ; to mode 3 (80-col text)
mov al, 14h ; Request 8x16 char matrix
jmp SHORT lmode
line43: mov al, 12h ; Set for EGA 43-line mode
cmp vconfig.adapter, EGA ; EGA?
je lmode ; Yes? Continue
mov ax, 1201h ; No? Function 12h for VGA
mov bl, 30h ; AL = 1 for 350 scan lines
int 10h ; Reset to 350 scan lines
mov ax, 0003 ; Reset mode (Function 0)
int 10h ; to mode 3 (80-col text)
mov al, 12h ; Request 8x8 character matrix
jmp SHORT lmode
line50: cmp vconfig.adapter, VGA ; VGA?
jne e_exit ; No? Exit with error
mov ax, 1202h ; Yes? Function 12h
mov bl, 30h ; AL = 2 for 400 scan lines
int 10h ; Reset to 400 scan lines
mov ax, 0003 ; Reset mode (Function 0)
int 10h ; to mode 3 (80-col text)
mov al, 12h ; Request 8x8 character matrix
lmode: sub bl, bl ; Use table 0
mov ah, 11h ; Request Function 11h
int 10h ; Set new line mode
mov ah, 12h ; Select alternate print
mov bl, 20h ; screen for EGA and VGA
int 10h
cmp vconfig.adapter, VGA ; VGA?
je exit ; Yes? Then exit
cmp line, 12h ; If EGA 43-line mode, set
je port ; cursor through port to
; avoid cursor emulation bug
mov al, 7 ; Else use BIOS to set cursor
push ax ; Pass bottom scan line
mov al, 6
push ax ; Pass top scan line
call SetCurSize ; Set normal cursor
add sp, 4 ; Clean stack
jmp SHORT exit ; Exit
port: mov dx, 03D4h ; Video controller address
mov ax, 060Ah ; Set AH = 06h (cursor start)
; AL = 0Ah (register #)
out dx, ax ; Update port
mov ax, 000Bh ; Set AH = 00h (cursor end)
; AL = 0Bh (register #)
out dx, ax ; Update port
jmp SHORT exit ; Normal exit
e_exit: mov ax, 1 ; Set error code
jmp SHORT @F
exit: sub ax, ax ; Clear error code
@@: ret
SetLineMode ENDP
;* Pause - Waits for specified number of clocks to elapse, then returns.
;*
;* Shows: BIOS Interrupt - 1Ah, Function 0 (Real-Time Clock Driver)
;* Operators - LOCAL []
;*
;* Params: duration - Desired duration in clocks, where
;* 18 clocks = approx 1 second
;*
;* Return: None
Pause PROC \
duration:WORD
LOCAL time:DWORD
sub ah, ah
int 1Ah ; Get Clock Count in CX:DX
add dx, duration ; Add pause time to it
adc cx, 0
mov WORD PTR time[0], dx ; Result is target time;
mov WORD PTR time[2], cx ; keep in local variable
loop1: int 1AH ; Now repeatedly poll clock
cmp dx, WORD PTR time[0] ; count until the target
jb loop1 ; time is reached
cmp cx, WORD PTR time[2]
jb loop1
ret
Pause ENDP
;* Sound - Sounds speaker with specified frequency and duration.
;*
;* Shows: Instructions - in out
;*
;* Params: freq - Desired frequency of sound in Hertz
;* duration - Desired duration in clocks, where
;* 18 clocks = approx 1 second
;*
;* Return: None
Sound PROC \
freq:WORD, duration:WORD
mov al, 0B6h ; Initialize channel 2 of
out 43h, al ; timer chip
mov dx, 12h ; Divide 1,193,182 Hertz
mov ax, 34DEh ; (clock frequency) by
div freq ; desired frequency
; Result is timer clock count
out 42h, al ; Low byte of count to timer
mov al, ah
out 42h, al ; High byte of count to timer
in al, 61h ; Read value from port 61h
or al, 3 ; Set first two bits
out 61h, al ; Turn speaker on
push duration
call Pause ; Pause for specified time
add sp, 2 ; Clean stack on return
in al, 61h ; Get port value
xor al, 3 ; Kill bits 0-1 to turn
out 61h, al ; speaker off
ret
Sound ENDP
;* WriteTTY - Displays ASCIIZ string at cursor position, in either text
;* or graphics mode.
;*
;* Shows: BIOS Interrupt - 10h, Function 0Eh (Write Character in TTY Mode)
;* Instruction - loop
;*
;* Uses: vconfig - Video configuration structure, declared in the
;* DEMO.INC include file. The structure must first be
;* initialized by calling the GetVidConfig procedure.
;*
;* Params: str - Pointer to ASCIIZ string
;* icolor - Color index (for graphics mode only)
;*
;* Return: None
WriteTTY PROC \
USES ds si, \
str:PTR BYTE, icolor:WORD
mov bx, icolor ; BL = color index
mov bh, vconfig.dpage ; BH = current display page
LoadPtr ds, si, str
mov cx, -1 ; Set loop counter to maximum
mov ah, 14 ; Function 14
loop1: lodsb ; Get character from string
or al, al ; NULL string terminator?
jz exit ; Yes? Exit
int 10h ; No? Display, advance cursor
loop loop1 ; Loop to get next character
exit: ret
WriteTTY ENDP
;* Colors - Alters screen colors within a specified area by using bit
;* or move operations on display attribute bytes in video memory.
;*
;* Shows: Instructions - not rol ror and xor or
;*
;* Params: logic - Code number, 0 = NOT 2 = ROR 4 = XOR 6 = MOV
;* 1 = ROL 3 = AND 5 = OR
;* attr - Attribute mask
;* row1 - Row at top of window
;* col1 - Column at left edge of window
;* row2 - Row at bottom of window
;* col2 - Column at right edge of window
;*
;* Return: None
Colors PROC \
USES ds si, \
logic:WORD, attr:WORD, row1:WORD, col1:WORD, row2:WORD, col2:WORD
GetVidOffset row1, col1 ; Get offset in video segment
inc ax
mov si, ax ; SI = offset for 1st attr byte
mov bx, row2
sub bx, row1
inc bx ; BX = number of window rows
mov cx, col2
sub cx, col1
inc cx ; CX = number of columns
mov ds, vconfig.sgmnt ; DS = video segment
mov ax, attr ; AL = mask for and, xor, and or
loop1: push si ; Save ptr to start of line
push cx ; and number of columns
cmp vconfig.adapter, CGA ; CGA adapter?
jne @F ; No? Skip video disable
; Disable CGA video prior to memory access to avoid screen snow. (See the
; WinOpen and StrWrite procedures for further discussions on CGA snow.)
call DisableCGA ; Yes? Disable video
@@: cmp logic, 1 ; Rotate left?
jl c_not ; If less, do NOT
je c_rol ; If equal, do ROL
cmp logic, 3 ; And?
jl c_ror ; If less, do ROR
je c_and ; If equal, do AND
cmp logic, 5 ; Or?
jl c_xor ; If less, do XOR
je c_or ; If equal, do OR
; Otherwise, do MOV
c_mov: mov BYTE PTR [si], al ; MOV attr parameter
add si, 2 ; into attribute byte
loop c_mov
jmp SHORT c_done
c_or: or BYTE PTR [si], al ; OR with attr parameter
add si, 2
loop c_or
jmp SHORT c_done
c_xor: xor BYTE PTR [si], al ; XOR with attr parameter
add si, 2
loop c_xor
jmp SHORT c_done
c_and: and BYTE PTR [si], al ; AND with attr parameter
add si, 2
loop c_and
jmp SHORT c_done
c_ror: ror BYTE PTR [si], 1 ; Rotate right 1 bit
add si, 2
loop c_ror
jmp SHORT c_done
c_rol: rol BYTE PTR [si], 1 ; Rotate left 1 bit
add si, 2
loop c_rol
jmp SHORT c_done
c_not: not BYTE PTR [si] ; Flip bits
add si, 2
loop c_not
c_done: cmp vconfig.adapter, CGA
jne @F
call EnableCGA ; Reenable CGA video
@@: pop cx ; Recover number of columns
pop si ; Recover offset for start of line
add si, 160 ; Point to start of next line
dec bx ; Decrement row counter
jnz loop1 ; Loop while rows remain
ret ; Exit when all lines complete
Colors ENDP
;* Exec - Executes a child process. Exec handles the usual chores associated
;* with spawning a process: (1) parsing the command line tail and loading the
;* FCBs with the first two arguments; (2) setting and restoring the vectors
;* for Interrupts 1Bh, 23h, and 24h; and (3) querying DOS for the child's
;* return code.
;*
;* Shows: DOS Functions - 29h (Parse Filename)
;* 25h (Set Interrupt Vector)
;* 35h (Get Interrupt Vector)
;* 4Bh (Execute Program)
;* 4Dh (Get Return Code)
;*
;* Params: spec - Pointer to ASCIIZ specification for program file
;* (must include .COM or .EXE extension)
;* block - Pointer to parameter block structure
;* break - Pointer to new Ctrl-Break (Interrupt 1Bh) handler
;* ctrlc - Pointer to new Ctrl-C (Interrupt 23h) handler
;* criterr - Pointer to new Critical Error (Interrupt 24h) handler
;*
;* Return: Short integer with child return code, or -1 for EXEC error
Exec PROC \
USES ds si di, \
spec:PTR BYTE, block:PTR parmblk, break:PTR BYTE, \
ctrlc:PTR BYTE, criterr:PTR BYTE
jmp SHORT @F ; Jump over data area
old_1Bh DD WORD PTR ? ; Keep vectors for Interrupts
old_23h DD WORD PTR ? ; 1Bh, 23h, and 24h in
old_24h DD WORD PTR ? ; code segment
old_stk DD WORD PTR ? ; Keep stack pointer
@@: Vector 1Bh, cs:old_1Bh, break ; Save, replace Int 1Bh vector
Vector 23h, cs:old_23h, ctrlc ; Save, replace Int 23h vector
Vector 24h, cs:old_24h, criterr ; Save, replace Int 24h vector
LoadPtr ds, bx, block ; Point DS:BX to parameter block
push ds ; Save segment address
les di, [bx].fcb1 ; Point ES:DI to first FCB
lds si, [bx].taddr ; Point DS:SI to command line tail
inc si ; Skip over count byte
mov ax, 2901h ; Set AH to request Function 29h
; AL = flag to skip leading blanks
int 21h ; Parse command-line into first FCB
pop es ; Recover seg addr of parameter block
les di, es:[bx].fcb2 ; Point ES:DI to second FCB
mov ax, 2901h ; Request DOS Function #29h again
int 21h ; Parse command-line into second FCB
push bp ; Save only important register
mov WORD PTR cs:old_stk[0], sp
mov WORD PTR cs:old_stk[2], ss
LoadPtr es, bx, block ; ES:BX points to param block
LoadPtr ds, dx, spec ; DS:DX points to path spec
mov ax, 4B00h ; AH = DOS Function 4Bh
; AL = 0 for load and execute
int 21h ; Execute Program
mov sp, WORD PTR cs:old_stk[0] ; Reset stack pointers
mov ss, WORD PTR cs:old_stk[2]
pop bp ; Recover saved register
; Restore vectors for Interrupts 1Bh, 23h, and 24h.
mov ax, 251Bh ; AH = DOS Function 25h
; AL = interrupt number
lds dx, cs:old_1Bh ; DS:DX = original vector
int 21h ; Set Interrupt 1Bh Vector
mov al, 23h ; AL = interrupt number
lds dx, cs:old_23h ; DS:DX = original vector
int 21h ; Set Interrupt 23h Vector
mov al, 24h ; AL = interrupt number
lds dx, cs:old_24h ; DS:DX = original vector
int 21h ; Set Interrupt 24h Vector
mov ax, -1 ; Set error code
jc exit ; If EXEC error, exit
mov ah, 4Dh ; Else request child's code
int 21h ; Get Return Code
sub ah, ah ; Make short integer
exit: ret
Exec ENDP
;* BinToHex - Converts binary word to 6-byte hexadecimal number in
;* ASCIIZ string. String is right-justified and includes "h" radix.
;*
;* Shows: Instruction - xlat
;*
;* Params: num - Number to convert to hex string
;* str - Pointer to 6-byte string
;*
;* Return: None
.DATA
hex DB '0123456789ABCDEF' ; String of hex numbers
.CODE
BinToHex PROC \
USES di, \
num:WORD, str:PTR BYTE
LoadPtr es, di, str ; Point ES:DI to 6-byte string
mov bx, OFFSET hex ; Point DS:BX to hex numbers
mov ax, num ; Number in AX
mov cx, 2 ; Loop twice for two bytes
loop1: xchg ah, al ; Switch bytes
push ax ; Save number
shr al, 1 ; Shift high nibble to low
shr al, 1
shr al, 1
shr al, 1
xlat ; Get equivalent ASCII number in AL
stosb ; Copy to 6-byte string, increment DI
pop ax ; Recover number
push ax ; Save it again
and al, 00001111b ; Mask out high nibble
xlat ; Get equivalent ASCII number in AL
stosb ; Copy to 6-byte string, increment DI
pop ax ; Recover number
loop loop1 ; Do next byte
mov ax, 'h' ; Put null, 'h' radix in AX
stosw ; Copy to last two bytes in string
ret
BinToHex ENDP
;* NewBlockSize - Adjusts size of allocated memory block.
;*
;* Shows: DOS Function - 4Ah (Resize Memory Block)
;*
;* Params: addr - Segment address of block
;* resize - Requested block size in paragraphs
;*
;* Return: Short integer error code
;* 0 if successful
;* 1 if error
NewBlockSize PROC \
addr:WORD, resize:WORD
mov ax, addr ; Get block address
mov es, ax ; Point ES to block
mov bx, resize ; New block size
mov ah, 4Ah ; Function number
int 21h ; Resize Memory Block
ret
NewBlockSize ENDP
;* Initialize - Initializes global variables _psp and _env, which are defined
;* in the DEMO.INC include file. If used with a DOS version less than 3.0,
;* this procedure will not produce valid results unless it is called before
;* changing the ES register. This is because at program entry ES points to
;* the Program Segment Prefix (PSP).
;*
;* Params: None
;*
;* Return: None
Initialize PROC
call GetPSP ; Get segment address of PSP
or ax, ax ; DOS version less than 3.0?
jz @F ; Yes? Assume ES points to PSP
mov es, ax ; No? Reload ES with PSP address
@@: mov _psp, es ; Initialize variable with PSP address
mov ax, es:[2Ch] ; Get environment seg from PSP
mov _env, ax ; Store it
ret
Initialize ENDP
END