Category : Files from Magazines
Archive   : VOL7N12.ZIP
Filename : FSB.ASM
; FSB.ASM -- "File Search and Browse" for OS/2
; (c) 1988, Ziff Communications Co.
; PC Magazine * Charles Petzold, 3/88
;------------------------------------------------
.286P
;-----------------------------------------------------------
; Macro to call OS/2 functions without declaring them first
;-----------------------------------------------------------
OS2Call MACRO fnctname
IFNDEF fnctname
EXTRN fnctname:FAR
ENDIF
Call Far Ptr fnctname
ENDM
;-----------------------------------
; Structures used in OS/2 functions
;-----------------------------------
FileFindBufStr STRUC
create_date dw ?
create_time dw ?
access_date dw ?
access_time dw ?
write_date dw ?
write_time dw ?
file_size dd ?
falloc_size dd ?
attributes dw ?
string_len db ?
file_name db 13 dup (?)
FileFindBufStr ENDS
KeyDataStruc STRUC
char_code db ?
scan_code db ?
status db ?
nls_shift db ?
shift_state dw ?
time dd ?
KeyDataStruc ENDS
ModeDataStruc STRUC
md_length dw ?
md_type db ?
color db ?
col dw ?
row dw ?
hres dw ?
vres dw ?
ModeDataStruc ENDS
;--------------------------------
; Define all segments and DGROUP
;--------------------------------
_TEXT SEGMENT WORD PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
_BSS ENDS
STACK SEGMENT PARA STACK 'STACK'
dw 1024 dup (?)
STACK ENDS
DGROUP GROUP _DATA, _BSS, STACK
ASSUME CS:_TEXT, DS:DGROUP, SS:DGROUP, ES:DGROUP
;--------------------------
; Initialized Data Segment
;--------------------------
_DATA SEGMENT
SyntaxMsg db 13, 10, "Syntax: FSB filespec"
db 13, 10
db 13, 10, "File Search and Browse "
db "(c) 1988, Ziff Communications Co."
db 13, 10, "PC Magazine ", 254," Charles Petzold, 3/88"
CRLF db 13, 10, 0
DriveError db "FSB: Invalid disk drive", 0
StartDirError db "FSB: Invalid directory", 0
FileFindError db "FSB: Invalid file spec", 0
PipeError db "FSB: Cannot not create pipe", 0
ThreadError db "FSB: Cannot not create second thread", 0
FileOpenError db 9, "FSB: Cannot open file for reading", 13, 10, 0
VideoModeError db 9, "FSB: Unsupported video mode", 13, 10, 0
RootDir db "\", 0
Delimiters db 9, ' ,;=', 0
DoBrowse db 1
_DATA ENDS
;----------------------------
; Uninitialized Data Segment
;----------------------------
_BSS SEGMENT
FileSpec dw ?, ?
LastBackSlash dw ?
PipeReadHandle dw ?
PipeWriteHandle dw ?
ThreadID dw ?
ThreadStack dw 1024 dup (?)
FullFileName db 80 dup (?)
BytesRead dw ?
BytesWritten dw ?
_BSS ENDS
;--------------
; Code Segment
;--------------
_TEXT SEGMENT
;----------------------------------------------
; Parse command line to get file specification
;----------------------------------------------
Entry: Push DS ; Data segment selector
Pop ES ; Transfer it to ES
Mov DS, AX ; DS = Environment selector
Mov SI, BX ; SI = Start of command line
SkipProgName: Lodsb ; Pull a command line byte
Or AL, AL ; Check if it's zero
Jnz SkipProgName ; If not, continue
SkipDelim: Lodsb ; Get command line byte
Or AL, AL ; See if it's zero
Jnz CheckDelim ; If not, check for delimiter
Jmp DoSyntaxMsg ; If so, display message
CheckDelim: Mov CX, 5 ; Five delimiters
Mov DI, Offset Delimiters ; pointer to them
Repne Scasb ; scan for delimiter
Jz SkipDelim ; if delimiter, loop around
Cmp Byte Ptr [SI], ':' ; Check if a drive present
Jnz NoDiskDrive ; If not, skip section
And AL, 0DFh ; Capitalize it
Sub AL, '@' ; Change from A to 1, etc
Sub AH, AH ; Zero out top byte
Push AX ; Change to that drive
OS2Call DosSelectDisk ; by calling OS/2
Mov DX, Offset DriveError
Or AX, AX ; If AX not zero,
Jnz ErrorExit ; display message and exit
Add SI, 2 ; Skip past drive
NoDiskDrive: Dec SI ; First character of rest
Mov ES:[FileSpec], SI ; Save the address
Mov ES:[FileSpec + 2], DS
FindEnd: Lodsb ; Get a byte
Cmp AL, '\' ; Check if it's a backslash
Jnz NotBackSlash ; If not, continue
Mov ES:[LastBackSlash], SI ; If so, save the address
NotBackSlash: Mov CX, 6 ; now six delimiters
Mov DI, Offset Delimiters ; pointer to delimiters
Repne Scasb ; scan them
Jnz FindEnd ; if not delimiter, loop around
Mov Byte Ptr [SI-1], 0 ; terminate with zero
DoChangeDir: Mov SI, ES:[LastBackSlash] ; Address of last back slash
Or SI, SI ; See if any at all
Jz NoChangeDir ; If none, skip this section
Dec SI ; Points to last backslash now
Cmp SI, ES:[FileSpec] ; Check if at beginning
Jnz ChangeToDir ; If not, use directory
Inc Word Ptr ES:[FileSpec] ; Skip past initial backslash
Push DGROUP ; Segment of new dir
Push Offset RootDir ; Offset of new dir
Jmp Short CallChangeDir ; Change to root directory
ChangeToDir: Mov Byte Ptr [SI], 0 ; Terinate dir name with zero
Push Word Ptr ES:[FileSpec + 2] ; This is segment
Push Word Ptr ES:[FileSpec] ; This is offset
Inc SI ; Save address of filename
Mov ES:[FileSpec], SI ; in FileSpec
CallChangeDir: Push 0 ; Reserved bytes
Push 0
OS2Call DosChdir ; Change directory
Mov DX, Offset StartDirError
Or AX, AX ; If non-zero return code,
Jnz ErrorExit ; display message and leave
NoChangeDir: Push ES ; Set DS to data segment
Pop DS
Call MainRoutine ; Go!
Push 1 ; All threads to terminate
Push 0 ; Return code
OS2Call DosExit ; End the program
;-------------------------------------
; Error Exit: DX is pointer to string
;-------------------------------------
DoSyntaxMsg: Mov DX, Offset SyntaxMsg
ErrorExit: Mov AX, DGROUP ; Set DS to data segment
Mov DS, AX
Mov BX, 2 ; Standard Error handle
Call StringLen ; Get length of DX string
Mov CX, AX ; Move it to CX
Call MyDosWrite ; Display error message
Push 1 ; All threads to terminate
Push 1 ; Error return code
OS2Call DosExit ; End the program
;----------------------------------------------------------------
; Main Routine, create pipe, start second thread, and do browses
;----------------------------------------------------------------
MainRoutine: Push DS ; Address for read handle
Push Offset PipeReadHandle
Push DS ; Address for write handle
Push Offset PipeWriteHandle
Push 16384 ; Size of pipe
OS2Call DosMakePipe ; Create it
Mov DX, Offset PipeError
Or AX, AX ; If non-zero return code,
Jnz ErrorExit ; depart from program
Push CS ; Address of second thread
Push Offset SecondThread
Push DS ; Address for thread ID
Push Offset ThreadID
Push DS ; Top of thread stack
Push Offset ThreadStack + 2048
OS2Call DosCreateThread ; Create the thread
Mov DX, Offset ThreadError
Or AX, AX ; Again, if error code,
Jnz ErrorExit ; get out of this place
StartPipeRead: Mov CX, 80 ; Read 80 chars from pipe
Mov DX, Offset FullFileName ; Store it here
ReadPipe: Mov BX, [PipeReadHandle] ; Pipe handle
Call MyDosRead ; Read it
Cmp [BytesRead], 0 ; Check if read no bytes
Jz NothingLeft ; If so, pipe is empty
Add DX, [BytesRead] ; Increment destination ptr
Sub CX, [BytesRead] ; Decrement counter
Jnz ReadPipe ; If not 80 bytes, read again
Mov DX, Offset FileFindError
Cmp [FullFileName], 0 ; See if file name present
Jz ErrorExit ; If not, thread had problem
NoErrFromThread:Mov BX, 1 ; Standard output handle
Mov DX, Offset FullFileName ; Full file name
Call StringLen ; Get its length
Mov CX, AX ; Store in CX
Call MyDosWrite ; Write to standard output
Mov CX, 2 ; Two bytes
Mov DX, Offset CRLF ; for carriage ret/line feed
Call MyDosWrite ; Write that out also
Cmp [DoBrowse], 1 ; See if still doing browse
Jnz StartPipeRead ; If not, skip the call
Call Browse ; Browse the file
Or AX, AX ; If zero returned, continue
Jz StartPipeRead
Cmp AX, -1 ; If -1, Esc pressed -->
Jz NothingLeft ; terminate gracefully
Cmp AX, Offset VideoModeError
Jnz ShowBrowseErr ; If video mode error,
Mov [DoBrowse], 0 ; don't try browse again
ShowBrowseErr: Mov BX, 2 ; Standard error output
Mov DX, AX ; Address of error message
Call StringLen ; Find length
Mov CX, AX ; and save in CX
Call MyDosWrite ; Dispay the string
Jmp StartPipeRead ; And get another file name
NothingLeft: Ret ; Return when all done
;--------------------------------------------------------------------------
; DosRead and DosWrite routines (BX is handle, DX is buffer, CX is length)
;--------------------------------------------------------------------------
MyDosRead: Push BX ; Input handle
Push DS ; Segment of buffer
Push DX ; Offset of buffer
Push CX ; Number of bytes to write
Push DS ; Segment for bytes read
Push Offset BytesRead ; Offset for bytes read
OS2Call DosRead ; Read
Ret
MyDosWrite: Push BX ; Output handle
Push DS ; Segment of buffer
Push DX ; Offset of buffer
Push CX ; Number of bytes to write
Push DS ; Segment for bytes written
Push Offset BytesWritten ; Offset for bytes written
OS2Call DosWrite ; Write
Ret
;------------------------------------------------------------
; StringLen routine (DS:DX points to string, AX is returned)
;------------------------------------------------------------
StringLen: Push ES ; Save some registers
Push DI
Push CX
Push DS
Pop ES ; Set ES to DS
Mov DI, DX ; Set DI to address of string
Mov CX, -1 ; Initialize CX to big number
Sub AL, AL ; Search for zero
Repnz Scasb ; Do the scan
Not CX ; Invert CX
Dec CX ; Take away one
Mov AX, CX ; That's the length
Pop CX ; Restore saved registers
Pop DI
Pop ES
Ret
_TEXT ENDS
;-----------------------------------------------------------------------
;-----------------------------------------------------------------------
; SECOND THREAD section to search for files and write to pipe
;-----------------------------------------------------------------------
;-----------------------------------------------------------------------
_DATA SEGMENT
BackDir db "..", 0
StarDotStar db "*.*", 0
_DATA ENDS
_BSS SEGMENT
FileFindBuf FileFindBufStr <>
FileFindBufLen equ $ - FileFindBuf
FullPathName Label Byte
CurrentDisk db ?, ?, ?
CurrentDir db 64 dup (?)
CurrDirLen dw ?
Zeroes db 80 dup (?)
DriveMap dd ?
_BSS ENDS
;---------------------------------------------------------
; Second Thread -- Calls FindThem, closes pipe, and exits
;---------------------------------------------------------
_TEXT SEGMENT
SecondThread: Push DS ; Variable to receive
Push Offset CurrentDisk ; current drive
Push DS ; Variable to receive
Push Offset DriveMap ; drive map
OS2Call DosQCurDisk
Add [CurrentDisk], '@' ; Convert to letter
Mov [CurrentDisk + 1], ':' ; Follow by colon
Mov [CurrentDisk + 2], '\' ; and backslash
Call FindThem ; Do the finds
TerminateThread:Push [PipeWriteHandle] ; Close the pipe for writing
OS2Call DosClose
Push 0 ; Terminate this thread only
Push 0 ; Result code (ignored)
OS2Call DosExit
SearchError: Mov BX, [PipeWriteHandle] ; If error,
Mov DX, Offset Zeroes ; write zeroes to
Mov CX, 80 ; pipe
Call MyDosWrite
Jmp TerminateThread ; and terminate
;----------------------------------------------------------------
; Find Them -- Recursive routine to find files fitting file spec
;----------------------------------------------------------------
FindThem: Enter 4, 0 ; Dir Handle is [BP - 2]
; Search Count is [BP - 4]
;------------------------------------
; Get current directory for printing
;------------------------------------
Mov [CurrDirLen], 64 ; Len for current dir
Push 0 ; Current disk drive
Push DS
Push Offset CurrentDir ; Space for current dir
Push DS
Push Offset CurrDirLen
OS2Call DosQCurDir ; Get current dir
Mov DX, Offset FullPathName ; Find length of it
Call StringLen
Cmp AX, 3 ; See if root
Jz NoMoreSlashes ; If so, skip next part
Mov SI, DX ; Add a slash
Add SI, AX ; at end of directory
Mov Byte Ptr [SI], '\'
Mov Byte Ptr [SI + 1], 0
Inc AX
NoMoreSlashes: Mov [CurrDirLen], AX ; Save total length
;----------------------
; Search for the files
;----------------------
Mov Word Ptr [BP - 2], -1 ; Initial directory Handle
Mov Word Ptr [BP - 4], 1 ; Search for one file
Push [FileSpec + 2] ; segment of find file name
Push [FileSpec] ; offset of find file name
Push SS ; segment of directory handle
Lea AX, [BP - 2] ; offset of directory handle
Push AX
Push 07h ; attribute
Push DS ; segment of buffer
Push Offset FileFindBuf ; offset of buffer
Push FileFindBufLen ; length of buffer
Push DS ; segment of search count
Lea AX, [BP - 4] ; offset of search count
Push AX
Push 0 ; Reserved
Push 0
OS2Call DosFindFirst ; Find first file
FindResult1: Or AX, AX ; See if error
Jz NoFindError1 ; If not skip next
Cmp AX, 18 ; See if no more files
Jz FindAllDone1 ; If so, done with search
Jmp SearchError ; Process error
NoFindError1: Cmp Word Ptr [BP - 4], 0 ; See if zero files found
Jz FindAllDone1 ; If so, done with search
Mov BX, [PipeWriteHandle] ; Write drive and
Mov DX, Offset FullPathName ; directory path
Mov CX, [CurrDirLen] ; to pipe
Mov SI, CX
Call MyDosWrite
Mov DX, Offset FileFindBuf.file_name
Mov CL, [FileFindBuf.string_len] ; Write file name
Sub CH, CH ; to pipe
Add SI, CX
Call MyDosWrite
Mov DX, Offset Zeroes ; Pad with zeroes
Mov CX, 80 ; written to pipe
Sub CX, SI
Call MyDosWrite
Push [BP - 2] ; Directory Handle
Push DS ; segment of buffer
Push Offset FileFindBuf ; offset of buffer
Push FileFindBufLen ; length of buffer
Push DS ; segment of count
Lea AX, [BP - 4] ; offset of count
Push AX
OS2Call DosFindNext ; Find next file
Jmp FindResult1 ; Loop around
FindAllDone1: Push [BP - 2] ; directory handle
OS2Call DosFindClose ; close it
;-------------------------------
; Now search for subdirectories
;-------------------------------
Mov Word Ptr [BP - 2], -1 ; Initial directory handle
Mov Word Ptr [BP - 4], 1 ; Search for one directory
Push DS ; segment of spec
Push Offset StarDotStar ; offset of spec
Push SS ; segment of handle
Lea AX, [BP - 2] ; offset of handle
Push AX
Push 10h ; attribute (dirs only)
Push DS ; segment of buffer
Push Offset FileFindBuf ; offset of buffer
Push FileFindBufLen ; length of buffer
Push DS ; segment of count
Lea AX, [BP - 4] ; offset of count
Push AX
Push 0 ; reserved
Push 0
OS2Call DosFindFirst ; Find first dir
FindResult2: Or AX, AX ; Check if error
Jz NoFindError2
Cmp AX, 18 ; Check if all finished
Jz FindAllDone2
Jmp SearchError
NoFindError2: Cmp Word Ptr [BP - 4], 0 ; Check if no dirs found
Jz FindAllDone2
Test [FileFindBuf.attributes], 10h ; See if directory
Jz FindTheNext
Cmp [FileFindBuf.file_name], '.' ; Exclude '.' and '..'
Jz FindTheNext
Push DS ; Change the directory
Push Offset FileFindBuf.file_name
Push 0
Push 0
OS2Call DosChdir
Call FindThem ; Recursive call
Push DS ; Change to '..' directory
Push Offset BackDir
Push 0
Push 0
OS2Call DosChdir
FindTheNext: Push [BP - 2] ; search handle
Push DS ; segment of buffer
Push Offset FileFindBuf ; offset of buffer
Push FileFindBufLen ; length of buffer
Push DS ; segment of count
Lea AX, [BP - 4] ; offset of count
Push AX
OS2Call DosFindNext ; Find next directory
Jmp FindResult2
FindAllDone2: Push [BP - 2] ; directory handle
OS2Call DosFindClose ; close it
Leave
Ret
_TEXT ENDS
;-----------------------------------------------------------------------
;-----------------------------------------------------------------------
; BROWSE section to display files after names are read from pipe
;-----------------------------------------------------------------------
;-----------------------------------------------------------------------
_DATA SEGMENT
db 'ATTR1='
Attribute1 db 1Eh ; Attribute for file text
db 'ATTR2='
Attribute2 db 4Fh ; Attribute for file name
db 'SHIFT='
ShiftHoriz db 8 ; Horizontal shift screen default
FileOffset dw -1, -1
Dispatch dw Home, Up, PgUp, Dummy, Left
dw Dummy, Right, Dummy, EndKey, Down, PgDn
_DATA ENDS
_BSS SEGMENT
ModeData ModeDataStruc <>
KeyData KeyDataStruc <>
Buffer db 16384 dup (?)
BufferMid db 16384 dup (?)
BufferEnd Label Byte
ScreenSize dw ?
ScreenStart dw ?
EndOfFile dw ?
HorizOffset dw ?
RightMargin dw ?
ScreenAddr Label DWord
ScreenOff dw ?
ScreenSeg dw ?
FileHandle dw ?
OpenAction dw ?
NewPointer dd ?
ScreenSaveSel dw ?
_BSS ENDS
;---------------------------
; Open the File for reading
;---------------------------
_TEXT SEGMENT
Browse: Mov [FileOffset], -1 ; Initialize these pointers
Mov [FileOffset + 2], -1
Push DS ; segment of name
Push Offset FullFileName ; offset of name
Push DS ; segment for handle
Push Offset FileHandle ; offset for handle
Push DS ; segment for 'action'
Push Offset OpenAction ; offset for 'acton'
Sub AX, AX
Push AX ; high size (ignored)
Push AX ; low size (ignored)
Push AX ; attribute (ignored)
Push 1 ; open if file exists
Push 0A0h ; read only / deny write
Push AX ; reserved
Push AX ; reserved
OS2Call DosOpen ; Open File
Or AX, AX ; Check if error
Jz GotTheFile ; If not, continue
Mov AX, Offset FileOpenError
Ret
GotTheFile:
;-----------------------------
; Get Screen Mode Information
;-----------------------------
Mov [ModeData.md_length], 12 ; length of structure
Push DS ; segment of structure
Push Offset ModeData ; offset of structure
Push 0 ; reserved
OS2Call VioGetMode ; get video mode
Or AX, AX ; See if error (only if
Jz NotDetached ; program is detached)
VideoError: Mov AX, Offset VideoModeError
Ret
NotDetached: Test [ModeData.md_type], 2 ; See if graphics mode
Jnz VideoError ; If so, do not continue
Mov AX, [ModeData.col] ; character columns
Mul [ModeData.row] ; character rows
Jc VideoError ; Leave if exceeds 64K
Add AX, AX ; Ditto here
Jc VideoError
Mov [ScreenSize], AX ; Save screen size in bytes
Push DS ; Segment for address
Push Offset ScreenAddr ; Offset for address
Push DS ; Segment for size
Push Offset ScreenSize ; Offset for size
Push 0 ; Reserved
OS2Call VioGetBuf ; Get logical video buffer
Or AX, AX ; Leave if an error
Jnz VideoError
;---------------------------------------
; Allocate memory and save screen in it
;---------------------------------------
Push [ScreenSize] ; Size of segment
Push DS ; Segment for selector
Push Offset ScreenSaveSel ; Offset for selector
Push 0 ; No sharing
OS2Call DosAllocSeg ; Allocate segment
Or AX, AX ; If error, leave
Jnz VideoError
Push [ScreenSaveSel] ; Segment for destination
Sub AX, AX
Push AX ; Offset for destination
Push DS ; Segment of screen size
Push Offset ScreenSize ; Offset of screen size
Push AX ; Starting row
Push AX ; Starting column
Push AX ; Reserved
OS2Call VioReadCellStr ; Save the screen
Call SetExitList ; Will restore on exit
;---------------------------------------
; Get keyboard key and decide on action
;---------------------------------------
Call Home ; Read file in
Mov [ScreenStart],SI ; Set buffer address
KeyLoop: Push ThreadID ; Don't let the search
OS2Call DosSuspendThread ; slow down the update
Call UpDateScreen ; Update the screen
Push ThreadID
OS2Call DosResumeThread ; Let the search resume
GetKey: Push DS ; Segment of structure
Push Offset KeyData ; Offset of structure
Push 0 ; Wait for key
Push 0 ; Reserved
OS2Call KbdCharIn ; Read key
Mov AL, [KeyData.char_code] ; Get character code
Cmp AL, ' ' ; A space means do next
Je SpaceLeave ; file
Cmp AL,27 ; Check if ESC
Je EscLeave ; If so, terminate
Cmp AL, 0E0h ; E0 for enhanced keyboard
Jz ScanCode ; extended keys
Or AL, AL ; Check if extended
Jnz GetKey ; If not, try again
ScanCode: Mov AL, [KeyData.scan_code] ; Get scan code
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
;--------------------------------------------
SpaceLeave: Call ExitRoutine ; Restore screen
Call UnsetExitList ; Take away exit list
Sub AX, AX ; 0 means continue with files
Ret ; Return
EscLeave: Call ExitRoutine ; Restore screen
Call UnsetExitList ; Take away exit list
Mov AX, -1 ; -1 means stop program
Ret ; Return
;-------------------------------------
; Exit List Processing for Ctrl-Break
;-------------------------------------
SetExitList: Push 1 ; Set an exit list
Push CS ; Segment of routine
Push Offset ExitList ; Offset of routine
OS2Call DosExitList
Ret
UnsetExitList: Push 2 ; Unset an exit list
Push CS ; Segment of routine
Push Offset ExitList ; Offset of routine
OS2Call DosExitList
Ret
ExitRoutine: Push [ScreenSaveSel] ; Segment of saved screen
Push 0 ; Offset of saved screen
Push [ScreenSize] ; Length of saved screen
Push 0 ; Starting row
Push 0 ; Starting column
Push 0 ; Reserved
OS2Call VioWrtCellStr ; Restore screen
Push [FileHandle] ; File handle
OS2Call DosClose ; Close it
Ret
ExitList: Call ExitRoutine ; Do exit routine
Push 3 ; Then continue exiting
Push 0
Push 0
OS2Call DosExitList
;---------------------
; Cursor Key Routines
;---------------------
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: 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 [ModeData.row] ; Number of lines
Dec CX ; less title line
PgUpLoop: Call Up ; Do UP that many times
Loop PgUpLoop
Ret
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
EndKey: Mov BX,SI ; Save buffer pointer
Call PgDn ; Go page down
Cmp BX,SI ; Check if we did so
Jnz EndKey ; 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 [ModeData.row] ; Number of lines
Dec CX ; less title line
PgDnLoop: Call Down ; Do DOWN that many times
Loop PgDnLoop
Ret
;---------------
; Update Screen
;---------------
UpdateScreen: Push ES
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,[Attribute1] ; With screen attribute
Rep Stosw ; Blank it
Mov CX, [ModeData.col] ; Number of character cols
Mov DI, [ScreenOff] ; Offset of screen
Mov SI, Offset FullFileName ; File name
DisplayName: Lodsb ; Get character
Or AL, AL ; See if end
Jz EndOfName
Mov AH, [Attribute2] ; Use second attribute
Stosw ; Display it
Loop DisplayName
EndOfName: Mov SI,[ScreenStart] ; Address of data in buffer
Mov DL, 1 ; Start with second line
Mov AL, Byte Ptr [ModeData.col] ; Length of row
Sub AH,AH
Add AX,[HorizOffset] ; Add Horizontal Offset
Mov [RightMargin],AX ; That's right display margin
LineLoop: Sub BX,BX ; Column Number
Mov AL, Byte Ptr [ModeData.col] ; 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
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,[Attribute1] ; Attribute for display
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,Byte Ptr [ModeData.row] ; See if down at bottom
Jb LineLoop ; If not, continue
EndOfScreen: Push 0 ; Offset of buffer
Push [ScreenSize] ; Size of buffer
Push 0 ; Reserved
OS2Call VioShowBuf ; Update the screen
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
And AL, 7Fh
Stc
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
Push ES
Push DS
Pop ES
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 ES
Pop DX ; Get back registers
Pop CX
CanGetPrev: Dec SI ; Move pointer back
Mov AL,[SI] ; Get the character
And AL, 7Fh ; Wipe out high byte
Stc ; CY flag reset for success
AtTopAlready: Cmc ; CY flag set for no more
Ret
;--------------------------------------------
; Read CX bytes from the file into DX buffer
;--------------------------------------------
FileRead: Push AX ; Save some registers
Push BX
Mov [EndOfFile], -1 ; Initialize this
Push [FileHandle] ; File handle
Push [FileOffset + 2] ; New pointer (high)
Push [FileOffset] ; New pointer (low)
Push 0 ; Action
Push DS ; Segment for new pointer
Push Offset NewPointer ; Offset for new pointer
OS2Call DosChgFilePtr
Mov BX, [FileHandle] ; Read from the file
Call MyDosRead
Or AX, AX ; See if error
Mov AX, [BytesRead]
Jz 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
_TEXT ENDS
END Entry
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/