mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 04:15:26 +00:00
454 lines
15 KiB
NASM
454 lines
15 KiB
NASM
; BROWSE.ASM -- Full Screen File Pager
|
||
; ====================================
|
||
|
||
CSEG Segment
|
||
Assume CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
|
||
Org 0080h
|
||
Parameter Label Byte
|
||
Org 0100h
|
||
Entry: Jmp Begin
|
||
|
||
; All Data
|
||
; --------
|
||
|
||
db 'ATTR='
|
||
Attribute db 0 ; Current screen attribute
|
||
db 'SHIFT='
|
||
ShiftHoriz db 8 ; Horizontal shift screen default
|
||
DosVersionFail db 'Requires DOS 2.0 or above$'
|
||
NoSpaceFail db 'Not enough memory$'
|
||
FileFail db 'File Not Found$'
|
||
ScreenFail db 'Unsupported video mode$'
|
||
Delimiters db 9,' ,;=/' ; Delimiters in parameter
|
||
FileHandle dw ? ; Use for saving file handle
|
||
WSMode db 0FFh ; AND value for non-WordStar mode
|
||
LineLength db ? ; Length of line (from BIOS)
|
||
NumberLines db 25,0 ; Number of lines (check EGA BIOS)
|
||
ScreenSize dw ? ; Size of screen in bytes
|
||
CheckRetrace db 1 ; Flag zero if EGA or MONO used
|
||
Addr6845 dw ? ; Could use for retrace check
|
||
ScreenAddr Label DWord ; Address of screen
|
||
ScreenOff dw 0 ; Higher for non-page 0
|
||
ScreenSeg dw 0B800h ; Set to B000h for Mono Mode 7
|
||
ScreenStart dw ? ; Points within buffer
|
||
EndOfFile dw ? ; Points within buffer
|
||
FileOffset dw -1, -1 ; Address within file of buffer data
|
||
HorizOffset dw 0 ; Horizontal offset for display
|
||
RightMargin dw 0 ; Right margin for offset display
|
||
Dispatch dw Home, Up, PgUp, Dummy, Left
|
||
dw Dummy, Right, Dummy, End, Down, PgDn
|
||
|
||
; Check DOS Version for 2.0 or above
|
||
; ----------------------------------
|
||
|
||
Begin: Cld ; All string directions forward
|
||
Mov AH,30h
|
||
Int 21h ; Get DOS Version Number
|
||
Cmp AL,2 ; Check for 2.0 or later
|
||
Jae DOSVerOK
|
||
Mov DX,Offset DOSVersionFail
|
||
ErrorExit: Mov AH,9 ; Write error message
|
||
Int 21h
|
||
Int 20h
|
||
|
||
; Parse Command Line to get File Name and WordStar flag
|
||
; -----------------------------------------------------
|
||
|
||
DOSVerOK: Mov SI,1 + Offset Parameter ; Points to parameter
|
||
NameSearch: Lodsb ; Get byte
|
||
Cmp AL,13 ; Check if carriage return
|
||
Jz NoFileFound ; If so, no file name
|
||
Mov DI,Offset Delimiters ; String of delimiters
|
||
Mov CX,5 ; Number of delimiters (no /)
|
||
Repne Scasb ; See if a match
|
||
Je NameSearch ; If a delimiter, keep looking
|
||
Mov DX,SI ; Otherwise found file name
|
||
Dec DX ; Points to beginning of it
|
||
EndSearch: Lodsb ; Get next byte
|
||
Cmp AL,13 ; See if carriage return
|
||
Je GotFileEnd ; If so, we're all done
|
||
Mov DI,Offset Delimiters ; String of delimiters
|
||
Mov CX,6 ; Number (including /)
|
||
Repne Scasb ; See if a match
|
||
Jne EndSearch ; If not, still in file name
|
||
Mov Byte Ptr [SI - 1],0 ; If so, mark end of file name
|
||
Jcxz GotFlag ; If slash, check for W
|
||
Jmp EndSearch ; Or continue flag search
|
||
GotFlag: Lodsb ; Get byte after / flag
|
||
Or AL,20h ; Uncapitalize
|
||
Cmp AL,'w' ; See if w for WordStar mode
|
||
Jnz GotFileEnd ; If not, just ignore it
|
||
Mov [WSMode],7Fh ; AND value for WordStar
|
||
|
||
; Open the File
|
||
; -------------
|
||
|
||
GotFileEnd: Mov Byte Ptr [SI - 1],0 ; Mark end of file name
|
||
; DX still points to name
|
||
Mov AX,3D00h ; Open file for reading
|
||
Int 21h ; by calling DOS
|
||
Jnc GotTheFile ; If no error, continue
|
||
NoFileFound: Mov DX,Offset FileFail ; Otherwise print a message
|
||
Jmp ErrorExit
|
||
GotTheFile: Mov [FileHandle],AX ; Save the file handle
|
||
|
||
; Get Screen Mode Information from BIOS Data Area
|
||
; -----------------------------------------------
|
||
|
||
Push ES ; Save register
|
||
Sub AX,AX
|
||
Mov ES,AX ; Set ES to 0 (BIOS Data)
|
||
Mov AL,ES:[0449h] ; Current Video Mode
|
||
Cmp AL,3 ; Check if Color Alpha
|
||
Jbe DisplayOK ; Continue if so
|
||
Cmp AL,7 ; Check if monochrome display
|
||
Je Monochrome ; If so, branch
|
||
Mov DX,Offset ScreenFail ; We can't handle graphics
|
||
Jmp ErrorExit ; So print an error message
|
||
Monochrome: Mov [ScreenSeg],0B000h ; Use Monochrome Segment
|
||
Mov [CheckRetrace],0 ; Don't have to check retrace
|
||
DisplayOK: Mov AL,ES:[044Ah] ; Number of Columns
|
||
Mov [LineLength],AL ; Save it
|
||
Mov AX,ES:[044Eh] ; Offset into screen buffer
|
||
Mov [ScreenOff],AX ; Save it
|
||
Mov AX,ES:[0463h] ; Address of 6845 Regsiter
|
||
Mov [Addr6845],AX ; Save it
|
||
Push ES
|
||
Sub DL,DL ; Set Rows to zero first
|
||
Sub BH,BH
|
||
Mov AX,1130h ; EGA BIOS: Get Information
|
||
Int 10h
|
||
Pop ES
|
||
Or DL,DL ; Check if DL is still zero
|
||
Jz NoEGA ; If so, skip rest of stuff
|
||
Inc DL
|
||
Mov [NumberLines],DL ; Save Number of Lines
|
||
Test Byte Ptr ES:[0487h],4 ; Check if must check retrace
|
||
Jnz NoEGA
|
||
Mov [CheckRetrace],0 ; EGA says we don't have to
|
||
NoEGA: Mov BH,ES:[0462h] ; Get Current Page (use later)
|
||
Pop ES
|
||
Mov AL,[LineLength] ; Length of each line
|
||
Mul [NumberLines] ; Total chars on screen
|
||
Add AX,AX ; Double for attributes
|
||
Mov [ScreenSize],AX ; And Save it
|
||
|
||
; See if enough memory is left
|
||
; ----------------------------
|
||
|
||
Add AX,Offset ScreenHold ; Add ScreenSize to code end
|
||
Add AX,256 ; Add a little stack room
|
||
Cmp AX,SP ; Check against stack pointer
|
||
Jbe GotEnufMemory ; Continue if OK
|
||
Mov DX,Offset NoSpaceFail ; Otherwise end program
|
||
Jmp ErrorExit ; with error messae
|
||
|
||
; Get Current Screen Attribute
|
||
; ----------------------------
|
||
|
||
GotEnufMemory: Cmp [Attribute],0 ; Check if attribute pre-set
|
||
Jnz GotAttribute ; If so, move on
|
||
Mov DL,' ' ; Write out a byte
|
||
Mov AH,2 ; using DOS
|
||
Int 21h
|
||
Mov AL,8 ; Now backspace
|
||
Mov AH,14 ; using BIOS call
|
||
Int 10h
|
||
Mov AH,8 ; Read character & attribute
|
||
Int 10h ; using BIOS call (BH = pg)
|
||
Mov [Attribute],AH ; And save attribute
|
||
|
||
; Save Current Screen
|
||
; -------------------
|
||
|
||
GotAttribute: Mov DX,Offset Terminate ; Set Ctrl-Break exit
|
||
Mov AX,2523h ; to terminate that way
|
||
Int 21h
|
||
Mov DI,Offset ScreenHold ; Destination of screen
|
||
Mov CX,[ScreenSize] ; Size of screen
|
||
Push DS ; Save Source Segment
|
||
Lds SI,[ScreenAddr] ; Get screen address
|
||
Rep Movsb ; Move in the bytes
|
||
Pop DS ; Restore Source Segment
|
||
|
||
; Get Keyboard Key and Decide on Action
|
||
; -------------------------------------
|
||
|
||
Call Home ; Read file in
|
||
Mov [ScreenStart],SI ; Set buffer address
|
||
KeyLoop: Call UpDateScreen ; Write file to screen
|
||
GetKey: Mov AH,8 ; Get key
|
||
Int 21h ; by calling DOS
|
||
Cmp AL,27 ; Check if ESC
|
||
Je Terminate ; If so, terminate
|
||
Cmp AL,0 ; Check if extended
|
||
Jnz GetKey ; If not, try again
|
||
Mov AH,8 ; Get extended code
|
||
Int 21h ; by calling DOS
|
||
Sub AL,71 ; Subtract Home key value
|
||
Jb GetKey ; If below that, not valid
|
||
Cmp AL,(81 - 71) ; Check if above PgDn
|
||
Ja GetKey ; If so, ignore it
|
||
Sub AH,AH ; Zero out top byte
|
||
Add AX,AX ; Double for word access
|
||
Mov BX,AX ; Offset in dispatch table
|
||
Mov SI,[ScreenStart] ; Set current buffer pointer
|
||
Call [Dispatch + BX] ; Do the call
|
||
Mov [ScreenStart],SI ; Set new buffer pointer
|
||
Jmp KeyLoop ; And update the screen
|
||
|
||
; Terminate -- Restore screen and close file
|
||
; ------------------------------------------
|
||
|
||
Terminate: Mov SI,Offset ScreenHold ; Address of Saved Screen
|
||
Les DI,[ScreenAddr] ; Address of Display
|
||
Mov CX,[ScreenSize] ; Number of characters
|
||
Rep Movsb ; Move them back
|
||
Mov BX,[FileHandle] ; Get File Handle
|
||
Mov AH,3Eh ; Close File
|
||
Int 21h
|
||
Int 20h ; Terminate
|
||
|
||
; Cursor Key Routines -- Home Key
|
||
; -------------------------------
|
||
|
||
Home: Sub BX,BX ; For zeroing out values
|
||
Mov AX,[FileOffset] ; Check if read in file
|
||
Or AX,[FileOffset + 2]
|
||
Mov [FileOffset],BX ; Zero out file address
|
||
Mov [FileOffset + 2],BX
|
||
Mov [HorizOffset],BX ; Zero out horizontal offset
|
||
Mov SI,Offset Buffer ; Reset buffer pointer
|
||
Jz Dummy ; Skip file read if in already
|
||
Mov DX,Offset Buffer ; Area to read file in
|
||
Mov CX,32768 ; Number of bytes to read
|
||
Call FileRead ; Read in file
|
||
Dummy: Ret
|
||
|
||
; Up and PgUp Keys
|
||
; ----------------
|
||
|
||
Up: Call GetPrevChar ; Get previous char in buffer
|
||
Jc UpDone ; If none available, finish
|
||
UpLoop: Call GetPrevChar ; Get previous char again
|
||
Jc UpDone ; if none, we're done
|
||
Cmp AL,10 ; Check if line feed
|
||
Jnz UpLoop ; If not, try again
|
||
Call GetNextChar ; Get char after line feed
|
||
UpDone: Ret
|
||
|
||
PgUp: Mov CX,Word Ptr [NumberLines] ; Number of lines
|
||
PgUpLoop: Call Up ; Do UP that many times
|
||
Loop PgUpLoop
|
||
Ret
|
||
|
||
; Left and Right Keys
|
||
; -------------------
|
||
|
||
Left: Mov [HorizOffset],0 ; Reset Horizontal Offset
|
||
Ret
|
||
|
||
Right: Mov AL,[ShiftHoriz] ; Get places to shift
|
||
Sub AH,AH
|
||
Add [HorizOffset],AX ; Move that many right
|
||
Ret
|
||
|
||
; End, Down, and PgDn Keys
|
||
; ------------------------
|
||
|
||
End: Mov BX,SI ; Save buffer pointer
|
||
Call PgDn ; Go page down
|
||
Cmp BX,SI ; Check if we did so
|
||
Jnz End ; If so, do it again
|
||
Ret
|
||
|
||
Down: Call GetNextChar ; Get next character
|
||
Jc NoMoreDown ; If no more, we're done
|
||
DownLoop: Call GetNextChar ; Get one again
|
||
Jc UpLoop ; If no more, find prev LF
|
||
Cmp AL,10 ; See if line feed
|
||
Jnz DownLoop ; If not, continue
|
||
NoMoreDown: Ret
|
||
|
||
PgDn: Mov CX,Word Ptr [NumberLines] ; Number of lines
|
||
PgDnLoop: Call Down ; Do DOWN that many times
|
||
Loop PgDnLoop
|
||
Ret
|
||
|
||
; Update Screen
|
||
; -------------
|
||
|
||
UpdateScreen: Push ES
|
||
Mov SI,[ScreenStart] ; Address of data in buffer
|
||
Les DI,[ScreenAddr] ; Address of display
|
||
Mov CX,ScreenSize ; Number of bytes in screen
|
||
Shr CX,1 ; Half for number of chars
|
||
Mov AL,' ' ; Will blank screen
|
||
Mov AH,[Attribute] ; With screen attribute
|
||
Rep Stosw ; Blank it
|
||
Mov AL,[LineLength] ; Length of display line
|
||
Sub AH,AH
|
||
Add AX,[HorizOffset] ; Add Horizontal Offset
|
||
Mov [RightMargin],AX ; That's right display margin
|
||
Sub DL,DL ; Line Number
|
||
LineLoop: Sub BX,BX ; Column Number
|
||
Mov AL,[LineLength] ; Use Line Length
|
||
Mul DL ; and Line Number
|
||
Add AX,AX ; to recalculate
|
||
Mov DI,AX ; display destination
|
||
Add DI,[ScreenOff] ; Add beginning address
|
||
CharLoop: Call GetNextChar ; Get next character
|
||
Jc EndOfScreen ; If no more, we're done
|
||
And AL,[WSMode] ; Will be 7Fh for WordStar
|
||
Cmp AL,13 ; Check for carriage return
|
||
Je CharLoop ; Do nothing if so
|
||
Cmp AL,10 ; Check for line feed
|
||
Je LineFeed ; Do routine if so
|
||
Cmp AL,9 ; Check for tab
|
||
Je Tab ; Do routine if so
|
||
Mov CX,1 ; Just 1 char to display
|
||
PrintChar: Cmp BX,[HorizOffset] ; See if we can print it
|
||
Jb NoPrint
|
||
Cmp BX,[RightMargin] ; See if within margin
|
||
Jae NoPrint
|
||
Mov AH,[Attribute] ; Attribute for display
|
||
Cmp [CheckRetrace],0 ; See if must stop snow
|
||
Jz WriteIt ; If not, skip retrace wait
|
||
Push BX
|
||
Push DX
|
||
Mov BX,AX ; Save character and attribute
|
||
Mov DX,[Addr6845] ; Set up I/O address
|
||
Add DX,6
|
||
RetraceWait1: In AL,DX ; Check until
|
||
Shr AL,1 ; vertical retrace
|
||
Jc RetraceWait1 ; ends
|
||
Cli ; Clear interrupts
|
||
RetraceWait2: In AL,DX ; Check until
|
||
Shr AL,1 ; vertical retrace
|
||
Jnc RetraceWait2 ; begins
|
||
Mov AX,BX ; Get back character & attr
|
||
Stosw ; Write to display
|
||
Sti ; Enable interrupts again
|
||
Pop DX
|
||
Pop BX
|
||
Jmp Short NoPrint ; Skip around "no snow" write
|
||
WriteIt: Stosw ; Write without retrace wait
|
||
NoPrint: Inc BX ; Bump up line counter
|
||
Loop PrintChar ; Do it CX times
|
||
Jmp CharLoop ; Then go back to top
|
||
Tab: Mov AX,BX ; Current column number
|
||
And AX,07h ; Take lower three bits
|
||
Mov CX,8
|
||
Sub CX,AX ; Subtract from 8
|
||
Mov AL,' ' ; Will print CX blanks
|
||
Jmp PrintChar
|
||
LineFeed: Inc DL ; Next line
|
||
Cmp DL,[NumberLines] ; See if down at bottom
|
||
Jb LineLoop ; If not, continue
|
||
EndOfScreen: Pop ES ; All done -- leave
|
||
Ret
|
||
|
||
; Get Next Character from buffer
|
||
; ------------------------------
|
||
; (Input is SI pointing to buffer, Returns AL, CY if no more)
|
||
|
||
GetNextChar: Cmp SI,[EndOfFile] ; See if at end of file
|
||
Jae NoMoreNext ; If so, no more chars
|
||
Cmp SI,Offset BufferEnd ; See if at end of buffer
|
||
Jb CanGetNext ; If not, just get character
|
||
Push CX ; Otherwise save registers
|
||
Push DX
|
||
Push DI
|
||
Push ES
|
||
Push DS ; Set ES to DS
|
||
Pop ES ; (could be different)
|
||
Mov SI,Offset BufferMid ; Move 2nd buffer half
|
||
Mov DI,Offset Buffer ; to 1st buffer half
|
||
Mov CX,16384
|
||
Sub [ScreenStart],CX ; New buffer pointer
|
||
Rep Movsb ; Move them
|
||
Mov SI,DI ; SI also buffer pointer
|
||
Add [FileOffset],32768 ; Adjust file addr to read
|
||
Adc [FileOffset + 2],0
|
||
Mov DX,Offset BufferMid ; Place to read file
|
||
Mov CX,16384 ; Number of bytes
|
||
Call FileRead ; Read the file
|
||
Sub [FileOffset],16384 ; Now adjust so reflects
|
||
Sbb [FileOffset + 2],0 ; 1st half of buffer
|
||
Pop ES ; Get back registers
|
||
Pop DI
|
||
Pop DX
|
||
Pop CX
|
||
Jmp GetNextChar ; And try again to get char
|
||
CanGetNext: Lodsb ; Get the character
|
||
NoMoreNext: Cmc ; So CY set if no more
|
||
Ret
|
||
|
||
; Get Previous Character from buffer
|
||
; ----------------------------------
|
||
|
||
GetPrevChar: Cmp SI,Offset Buffer ; See if at top of buffer
|
||
Ja CanGetPrev ; If not, just get character
|
||
Mov AX,[FileOffset] ; See if at top of file
|
||
Or AX,[FileOffset + 2]
|
||
Jz AtTopAlready ; If so, can't get anymore
|
||
Push CX ; Save some registers
|
||
Push DX
|
||
Mov SI,Offset Buffer ; Move 1st half of buffer
|
||
Mov DI,Offset BufferMid ; to 2nd half of buffer
|
||
Mov CX,16384
|
||
Add [ScreenStart],CX ; New buffer pointer
|
||
Rep Movsb ; Do the move
|
||
Sub [FileOffset],16384 ; Adjust file addr for read
|
||
Sbb [FileOffset + 2],0
|
||
Mov DX,Offset Buffer ; Area to read file into
|
||
Mov CX,16384 ; Number of bytes
|
||
Call FileRead ; Read the file
|
||
Pop DX ; Get back registers
|
||
Pop CX
|
||
Jmp Short CanGetPrev ; Now get character
|
||
AtTopAlready: Stc ; CY flag set for no more
|
||
Ret
|
||
CanGetPrev: Dec SI ; Move pointer back
|
||
Mov AL,[SI] ; Get the character
|
||
Clc ; CY flag reset for success
|
||
Ret
|
||
|
||
; Read CX bytes from the file into DX buffer
|
||
; ------------------------------------------
|
||
|
||
FileRead: Push AX ; Save some registers
|
||
Push BX
|
||
Push CX
|
||
Push DX
|
||
Mov [EndOfFile],-1 ; Initialize this
|
||
Mov DX,[FileOffset] ; Get file address to read
|
||
Mov CX,[FileOffset + 2]
|
||
Mov BX,[FileHandle] ; Get file Handle
|
||
Sub AL,AL ; Do LSEEK from beginning
|
||
Mov AH,42h ; LSEEK call
|
||
Int 21h
|
||
Pop DX ; Get back destination
|
||
Pop CX ; Get back count
|
||
Mov AH,3Fh ; Read file function call
|
||
Int 21h
|
||
Jnc NoReadError ; If no error, continue
|
||
Sub AX,AX ; Otherwise read zero bytes
|
||
NoReadError: Cmp AX,CX ; See if 32K has been read
|
||
Je GotItAll ; If so, we're home free
|
||
Add AX,DX ; Otherwise add to buffer addr
|
||
Mov [EndOfFile],AX ; And save as end of file
|
||
GotItAll: Pop BX
|
||
Pop AX
|
||
Ret
|
||
|
||
; File Buffer and Screen Hold Areas
|
||
; ---------------------------------
|
||
|
||
Buffer Label Byte ; Area for file reads
|
||
BufferMid equ Buffer + 16384 ; Halfway through it
|
||
BufferEnd equ BufferMid + 16384 ; At end of it
|
||
ScreenHold equ BufferEnd ; Area for holding screen
|
||
CSEG EndS ; End of segment
|
||
End Entry ; Denotes entry point
|
||
|