MalwareSourceCode/MSDOS/Virus.MSDOS.Unknown.browse.asm

454 lines
15 KiB
NASM
Raw Normal View History

2021-01-12 23:38:47 +00:00
; 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