Category : Utilities for DOS and Windows Machines
Archive   : RAMVIEW.ZIP
Filename : RAMVIEW.ASM

 
Output of file : RAMVIEW.ASM contained in archive : RAMVIEW.ZIP
;----------------------------------------------------------------
; RAMVIEW: A memory-resident memory-viewer.
; Frank Dever & David Thomas, WindowDOS Associates
;
;
; Modified: 25-Oct-1991
;
; By James B. Tabor , to support multitask,
; and to perment other foreground tasks to
; run .
;
;----------------------------------------------------------------
; scan codes
;----------------------------------------------------------------
U_ARR EQU 72 ;up arrow
D_ARR EQU 80 ;down arrow
PGUP EQU 73 ;page up
PGDN EQU 81 ;page down
L_ARR EQU 75 ;left arrow
R_ARR EQU 77 ;right arrow
HOME EQU 71 ;home
END_KEY EQU 79 ;end
INT9_BUSY EQU 1
INT10_BUSY EQU 2
INT16_BUSY EQU 4
SHIFT_MASK EQU 8 ;Alt
HOTKEY EQU 13H ;R

CR EQU 0DH
LF EQU 0AH

_TEXT SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT
ORG 100H
START:
JMP RES

INSTALLED DB "RAMVIEW 2.00 (c) 1988, 1991 Ziff Communications Co."
DB CR,LF,"PC Magazine ",254," Frank Dever & David Thomas"
db cr,lf,"Modified by James B. Tabor , for multitask ."
DB CR,LF,"Hotkey is Alt-R",CR,LF,"$",1AH
UNINSTALLED DB "RAMVIEW Uninstalled",CR,LF,"$"

DISABLE DB 0 ;flag for inapropriate de-installation

LABEL0 DB " ABS SEG OFF 0 1 2 3 4 5 6 7"
DB " 8 9 A B C D E F ASCII",0

START_DISPLAY DB " GOTO ADDRESS: 0000:0000",0

COMMANDLINE DB "(G)oto (H)ex Search (A)scii Search (N)ext"
DB " ",24," ",25," PgUp PgDn Home End Esc ",0

CURSOR_POS DW 0
CURSOR_TYPE DW 0607H ;for turning cursor on/off
START_OFFSET DW 0 ;top addr shown on screen
START_SEGMENT DW 0
END_OFFSET DW 0FH ;last segment in search
END_SEGMENT DW 0FFFFH ;fixed to ffff:f
SEARCH_SEGMENTS DW 0 ;# segments to search
SEARCH_BYTES DW 0 ;# bytes left after search
HEX_SIEVE DB " " ;hex with space
ADDR_SIEVE DB "01234567890ABCDEF" ;hex without space
CHAR_SIEVE DB 0 ;all characters
ASCII_SEARCH_PROMPT DB "ASCII Search String: ",0 ;ascii search prompt
HEX_SEARCH_PROMPT DB "HEX Search String: ",0 ;hex search prompt
ASCII_SEARCH_AREA DB 20 DUP (" "),0 ;ascii search string
HEX_SEARCH_AREA DB 20 DUP (" "),0 ;hex search string
LAST_SEARCH_STRING DW 0 ;pointer to last string
LAST_STRING_LENGTH DW 0 ;last string length
OUR_SS DW 0
OUR_SP DW 0
THEIR_SS DW 0
THEIR_SP DW 0
ADDR_INT9H DD 0
ADDR_INT10H DD 0
BUSY DB 0
BIOS_SEG DW 40H
DIFF DW 0 ;# of chars on a line > 80
ROW DW 0 ;display row counter
FLICKER_FREE DB 0 ;snowy monitor flag
;
;
doss_ofs dw 0 ;* PART OF MOD ...
doss_seg dw 0 ;* InDOS FLAG ADDRESS
dos_crit_ofs dw 0 ;* CRIT ERROR FLAG ADDRESS
dos_crit_seg dw 0 ;*
; ;*
int08ip dw 0 ;* INT 08H ADDRESS
int08cp dw 0 ;*
int28ip dw 0 ;* INT 28H
int28cp dw 0 ;*
; ;*
pop_up db 0 ;* NEW HOT KEY POP UP FLAG ...
;
;
; INT 08 CALL
;
;
int_08 proc far

cli
pushf
call cs:dword ptr [int08ip]
cmp cs: byte ptr busy,0
jz norm_int08
iret
norm_int08:
sti
cmp cs: byte ptr pop_up,0ffh
jnz exit_08a
push ds
push es
push bx
push ax
push cs
pop ds
mov cs: byte ptr pop_up,0
les bx,cs:dword ptr [doss_ofs] ;InDOS FLAG ADDRESS
; cmp es: byte ptr [bx],0 ;
; jnz exit_08 ;
cmp es: byte ptr [bx]-1,0 ; SUB ONE FOR CRIT-ERROR
jnz exit_08 ; FLAG ADDRESS ..
or cs: byte ptr busy,01

call adjust_for_video_mode
jc exit_08
call mem
exit_08:
xor cs: byte ptr busy,01
pop ax
pop bx
pop es
pop ds
exit_08a:
iret
int_08 endp
;
;
; PROGRAMMERS PREF. IF YOU WANT TO USE IT HERE IT IS....
;
int_28 proc far
cli
pushf
call cs:dword ptr [int28ip]
cmp cs: byte ptr busy,0
jz norm_int28
iret
norm_int28:
sti
cmp cs: byte ptr pop_up,0ffh
jnz exit_28a
push ds
push es
push bx
push ax
push cs
pop ds
mov cs: byte ptr pop_up,0
les bx,cs:dword ptr [dos_crit_ofs]
cmp es: byte ptr [bx],0
jnz exit_28
or cs: byte ptr busy,01h

call adjust_for_video_mode
jc exit_28
call mem
exit_28:
xor cs: byte ptr busy,01h
pop ax
pop bx
pop es
pop ds
exit_28a:
iret
int_28 endp
;
;-----------------------------------------------------------------
; code relating to residency
;-----------------------------------------------------------------
RES PROC NEAR

CLD
CALL PROGRAM_ALREADY_IN
JNZ NOT_IN ;else, it's ok to install
CALL UNINSTALL
MOV AX,4C01H ;terminate with error code
INT 21H
NOT_IN:
MOV OUR_SS,CS ;set stack seg
MOV OUR_SP,OFFSET LAST_BYTE+4000+256 ; and pointer
CALL INSTALL
MOV DX,OFFSET INSTALLED ;already in
MOV AH,9
INT 21H
;program, screen buf, stack
MOV DX,(OFFSET LAST_BYTE-OFFSET _TEXT+4000+256+15) SHR 4
MOV AX,3100H ;stay resident
INT 21H
RES ENDP
;----------------------------------------------------------------------
INT10H PROC FAR
OR CS:BUSY,INT10_BUSY ;recursion protection
PUSHF
CALL CS:ADDR_INT10H
PUSHF
AND CS:BUSY,NOT(INT10_BUSY)
POPF
RET 2 ;preserve flags
INT10H ENDP
;----------------------------------------------------------------------
INT9H PROC FAR
STI
PUSH AX ;save working register
CMP CS:DISABLE,-1 ;if disabled, do nothing
JE NOT_US
IN AL,60H ;get key from keyboard port
CMP AL,HOTKEY ;is it our hotkey?
JNE NOT_US ;if not, exit
MOV AH,2 ;otherwise
INT 16H ;get shift status
AND AL,0FH
CMP AL,SHIFT_MASK ;test the shift status
JNE NOT_US ;if not shift combo, exit
IN AL,61H ;These instructions reset
MOV AH,AL ; the keyboard.
OR AL,80H
OUT 61H,AL
MOV AL,AH
JMP SHORT $+2 ;I/O delay for fast AT's
OUT 61H,AL
CLI ;Disable interrupts and
MOV AL,20H ;reset the int controller
OUT 20H,AL
STI
mov cs: byte ptr pop_up,0ffh ;* MOD FOR POP UP ...
; CMP CS:BUSY,0 ;recursion protection
; JNE WE_ARE_BUSY ;dont allow re-entrancy
; OR CS:BUSY,INT9_BUSY ;set flag for protection
; CALL ADJUST_FOR_VIDEO_MODE
; JC CANT_POP_UP
; CALL MEM ;call our program
;CANT_POP_UP:
; CLI ;disable kbd momentarily
; AND CS:BUSY,NOT(INT9_BUSY) ;reset protection
;WE_ARE_BUSY:
; POP AX ;restore working register
; IRET ;return to foreground
NOT_US:
POP AX ;restore working register
CLI
JMP CS:ADDR_INT9H ;let ROM code do its work
INT9H ENDP

;-----------------------------------------------------------------
;check for text modes and set offset
;for lines > than 80 characters in length
;-----------------------------------------------------------------
ADJUST_FOR_VIDEO_MODE PROC NEAR

PUSH BX
MOV AH,15 ;get present mode
INT 10H

CMP AH,80
JB BAD_MODE

MOV BYTE PTR CS:DIFF,AH ;calc the # of chars > 80
SUB CS:BYTE PTR DIFF,80 ;on the line & save in diff
CMP AL,7 ;7 is mono
JNE TRY_COLOR
MODE_OK:
CLC
POP BX
RET
TRY_COLOR:
CMP AL,3 ;3 is color 80x25
JBE MODE_OK
BAD_MODE:
STC ;not good mode
POP BX
RET

ADJUST_FOR_VIDEO_MODE ENDP

;-----------------------------------------------------------------
;main routine called by pressing hot key
;-----------------------------------------------------------------
MEM PROC NEAR
CALL SWAPIN ;new stack
MOV AX,CS ;our data segment
MOV DS,AX
CALL FLICKER ;determine presence of snow
CALL SAVE_CURSOR ;save cursor
CALL CURSOR_OFF ;turn cursor off
CALL SAVE_SCREEN ;save screen
CALL CLEAR_SCREEN ;clear screen
CALL PROGRAM ;do our program
CALL RESTORE_SCREEN ;put screen back
CALL CURSOR_ON ;turn cursor on
CALL RESTORE_CURSOR
CALL SWAPOUT ;put stack back
RET ;that's all
MEM ENDP

PROGRAM PROC NEAR
XOR AX,AX ;row 0
MOV BX,AX ;col 0
MOV SI,OFFSET LABEL0 ;bottom line
CALL DISPLAY_STRING ;show header line
NEXT_SCREEN:
CALL BODY ;put hex display up
MOV AX,24 ;last row
XOR BX,BX ;column 0
MOV SI,OFFSET COMMANDLINE ;command line
CALL DISPLAY_STRING ;show it
CALL MENU ;go get keystroke
JNC NEXT_SCREEN ;until esc is pressed

EXIT:
RET
PROGRAM ENDP

;-----------------------------------------------------------------
;cursor routines
;-----------------------------------------------------------------
SAVE_CURSOR PROC NEAR
MOV ES,BIOS_SEG
MOV AX,ES:[60H] ;get present cursor type
MOV CURSOR_TYPE,AX ;save it
CALL GETPOS
MOV CURSOR_POS,DX
RET
SAVE_CURSOR ENDP
;----------------------------------------------------------------------
RESTORE_CURSOR PROC NEAR
MOV DX,CURSOR_POS
CALL SETPOS
RET
RESTORE_CURSOR ENDP
;----------------------------------------------------------------------
SETPOS PROC NEAR
MOV AH,2 ;set cursor position
XOR BH,BH ;active page
INT 10H ;set cursor position to dx
RET
SETPOS ENDP
;----------------------------------------------------------------------
GETPOS PROC NEAR
MOV AH,3 ;get cursor position
XOR BH,BH ;active page
INT 10H ;get cursor position in dx
RET
GETPOS ENDP
;----------------------------------------------------------------------
CURSOR_OFF PROC NEAR
PUSH CX
MOV CX,2B0CH ;turn off cursor value
CALL SETCUR
POP CX
RET
CURSOR_OFF ENDP
;----------------------------------------------------------------------
CURSOR_ON PROC NEAR
PUSH CX
MOV CX,CURSOR_TYPE ;turn on cursor value
CALL SETCUR
POP CX
RET
CURSOR_ON ENDP
;----------------------------------------------------------------------
SETCUR PROC NEAR
PUSH AX
MOV AH,1 ;function to set cursor shape
INT 10H ;do it
POP AX
RET ;that's all
SETCUR ENDP
;-----------------------------------------------------------------
;screen routines
;-----------------------------------------------------------------
SAVE_SCREEN PROC NEAR
CALL TURN_OFF_CRT
PUSH DS ;save data segment
XOR AX,AX
MOV BX,AX
CALL CALC_SCRN_ADDR ;address of (0,0)
MOV SI,OFFSET LAST_BYTE ;buffer is past end of prog
PUSH DS ;exchange
PUSH ES ;ds and es
POP DS
POP ES
XCHG DI,SI ;exchange source,destination
MOV BX,25
SAVE_NEXT_LINE:
MOV CX,80 ;save 2000
REP MOVSW ;words
ADD SI,CS:DIFF ;go to next
ADD SI,CS:DIFF ;line
DEC BX ;25 lines
JNZ SAVE_NEXT_LINE
POP DS ;restore data segment
CALL TURN_ON_CRT
RET
SAVE_SCREEN ENDP
;-----------------------------------------------------------------
CLEAR_SCREEN PROC NEAR
CALL TURN_OFF_CRT
XOR AX,AX
MOV BX,AX
CALL CALC_SCRN_ADDR ;address of (0,0)
MOV AX,0720H ;space with normal attribute
MOV BX,24
CLEAR_NEXT_LINE:
MOV CX,80
REP STOSW ;words
ADD DI,CS:DIFF ;go to next
ADD DI,CS:DIFF ;line
DEC BX ;24 lines
JNZ CLEAR_NEXT_LINE
CALL TURN_ON_CRT
RET
CLEAR_SCREEN ENDP
;-----------------------------------------------------------------
RESTORE_SCREEN PROC NEAR
CALL TURN_OFF_CRT
XOR AX,AX
MOV BX,AX
CALL CALC_SCRN_ADDR ;address of (0,0)
MOV SI,OFFSET LAST_BYTE ;buffer past end of program
MOV BX,25
RESTORE_NEXT_LINE:
MOV CX,80 ;save 2000
REP MOVSW ;words
ADD DI,CS:DIFF ;go to next
ADD DI,CS:DIFF ;line
REP MOVSW ;words
DEC BX ;25 lines
JNZ RESTORE_NEXT_LINE
CALL TURN_ON_CRT
RET
RESTORE_SCREEN ENDP
;-----------------------------------------------------------------
TURN_ON_CRT PROC NEAR
CMP CS:FLICKER_FREE,-1 ;is it flicker free?
JZ NOSNOW ;if so, dont turn crt_on
PUSH AX
PUSH DX
PUSH ES
CALL WAIT_VERT
MOV DX,3D8H
MOV ES,BIOS_SEG
MOV AL,ES:BYTE PTR[65H] ;get crt mode setting
OUT DX,AL ;set crt back to previous mode
POP ES
POP DX
POP AX
NOSNOW:
RET
TURN_ON_CRT ENDP
;-----------------------------------------------------------------
FLICKER PROC NEAR
MOV FLICKER_FREE,0 ;assume snowy monitor
MOV AX,1A00H
INT 10H ;Use PC Magazine's method
CMP AL,1AH ;of determining
JZ FLICKFREE ;vga or mda active
MOV AH,12H
MOV BL,10H
INT 10H
CMP BL,10H
JNZ FLICKFREE ;if bl changed, its ega
MOV AX,40H
MOV ES,AX
TEST ES:BYTE PTR[63H],40H ;check for mda (= no flicker)
JNZ ITS_CGA
FLICKFREE:
MOV FLICKER_FREE,-1
ITS_CGA:
RET
FLICKER ENDP
;-----------------------------------------------------------------
TURN_OFF_CRT PROC NEAR
CMP CS:FLICKER_FREE,-1 ;is it flicker free?
JZ NO_SNOW
PUSH DX
PUSH AX
CALL WAIT_VERT
MOV AL,25H ;mask to turn off crt
MOV DX,3D8H
OUT DX,AL ;turn it off
POP AX
POP DX
NO_SNOW:
RET
TURN_OFF_CRT ENDP
;-----------------------------------------------------------------
WAIT_VERT PROC NEAR
CMP CS:FLICKER_FREE,-1 ;is it flicker free?
JZ NOWAIT
PUSH AX
PUSH DX
MOV DX,3DAH ;color status port
WAIT0:
IN AL,DX
TEST AL,8
JNZ WAIT0 ;wait for vertical off
WAIT1:
IN AL,DX
TEST AL,8
JZ WAIT1 ;wait for verical on
POP DX
POP AX
NOWAIT:
RET
WAIT_VERT ENDP

;-----------------------------------------------------------------
;stack routines
;-----------------------------------------------------------------
RETADDR DW 0
SWAPIN PROC NEAR
POP CS:RETADDR ;save callers address
MOV CS:THEIR_SS,SS ;save their stack
MOV CS:THEIR_SP,SP
MOV SS,CS:OUR_SS ;switch to our stack
MOV SP,CS:OUR_SP
PUSH AX ;save all registers
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH ES
PUSH DS
PUSH BP
JMP CS:RETADDR ;return to caller
SWAPIN ENDP
;-----------------------------------------------------------------
SWAPOUT PROC NEAR
POP CS:RETADDR ;save callers address
POP BP ;restore all registers
POP DS
POP ES
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
MOV SS,CS:THEIR_SS ;restore callers stack
MOV SP,CS:THEIR_SP
JMP CS:RETADDR ;return to caller
SWAPOUT ENDP

;-----------------------------------------------------------------
;clears last line
;-----------------------------------------------------------------
CLEAR_LAST_LINE PROC NEAR
CALL WAIT_VERT
MOV AX,24 ;row 24
XOR BX,BX ;col 0
CALL CALC_SCRN_ADDR ;get es:di for that address
MOV CX,80 ;80 words
MOV AX,0720H ;normal attribute spaces
REP STOSW ;clear it
RET ;that's all
CLEAR_LAST_LINE ENDP

;-----------------------------------------------------------------
;menu accept and key input
;-----------------------------------------------------------------
MENU PROC NEAR
NEXT_MENU_KEY:
CALL GETKEY ;input a key
CMP AH,2 ;if scan code is less than 2
JBE QUIT_EXIT ; the key is Esc
NOT_ESC:
AND AL,0DFH ;make all ascii caps
CMP AL,"G" ;G)o
JNZ NOT_GOTO
CALL GOTO ;get start addr
JMP MENU_EXIT
NOT_GOTO:
CMP AL,"A" ;A)scii search
JNZ NOT_ASCII_SEARCH
CALL ASCII_SEARCH ;find ascii string
JMP MENU_EXIT
NOT_ASCII_SEARCH:
CMP AL,"H" ;H)ex search
JNZ NOT_HEX_SEARCH
CALL HEX_SEARCH ;find hex string
JMP MENU_EXIT
NOT_HEX_SEARCH:
CMP AL,"N" ;N)ext search
JNZ NOT_NEXT_SEARCH
CALL NEXT_SEARCH ;find next string
JMP MENU_EXIT
NOT_NEXT_SEARCH:
CMP AH,U_ARR ;up arrow
JNZ NOT_UP
MOV AX,-10H
JMP SHORT MOVEIT
NOT_UP:
CMP AH,HOME ;go to beginning of memory
JNZ NOT_HOME
MOV START_OFFSET,0
MOV START_SEGMENT,0
JMP SHORT MENU_EXIT
NOT_HOME:
CMP AH,END_KEY ;go to end of memory
JNZ NOT_END
MOV START_OFFSET,0FE90H ;top of last page
MOV START_SEGMENT,0F000H
JMP SHORT MENU_EXIT
NOT_END:
CMP AH,D_ARR ;down arrow
JNZ NOT_DOWN
MOV AX,10H
JMP SHORT MOVEIT
NOT_DOWN:
CMP AH,PGUP ;page up
JNZ NEXT_CHECK
MOV AX,-170H
JMP SHORT MOVEIT
NEXT_CHECK:
CMP AH,PGDN ;page down
MOV AX,170H
JZ MOVEIT
JMP NEXT_MENU_KEY
MOVEIT:
CALL MOVE
MENU_EXIT:
CLC ;clear carry for exit
QUIT_EXIT:
RET ;that's all
MENU ENDP

;-----------------------------------------------------------------
;sets starting segment and offset for display
;-----------------------------------------------------------------
MOVE PROC NEAR
OR AX,AX ;is the movement signed
JNS ADDIT ;no-add to seg:off
NEG AX ;otherwise make positive
SUB START_OFFSET,AX ;subtract from offset
JNC MOVE_EXIT ;if no carry, done
SUB START_SEGMENT,1000H ;else wrap segment
JMP SHORT MOVE_EXIT
ADDIT:
ADD START_OFFSET,AX ;add movement to offset
JNC MOVE_EXIT ;if no carry, done
ADD START_SEGMENT,1000H ;else wrap segment
MOVE_EXIT:
RET
MOVE ENDP

;-----------------------------------------------------------------
;ascii search routine
;-----------------------------------------------------------------
ASCII_SEARCH PROC NEAR
CALL CLEAR_LAST_LINE ;clear line for input
MOV AX,24 ;last row
XOR BX,BX ;first col
MOV SI,OFFSET ASCII_SEARCH_PROMPT ;prompt
CALL DISPLAY_STRING ;show the prompt
CALL STRLEN ;find out how long prompt is
MOV DL,CL ;dl = col after prompt
MOV DH,24 ;dx = row col
MOV SI,OFFSET ASCII_SEARCH_AREA ;
MOV CX,20 ;size of the string
CALL CLEAR_BUF
MOV DI,OFFSET CHAR_SIEVE ;pointer to sieve
MOV BX," " ;get
;dx=row col,cx=size,bl=replace,si=prompt,di=sieve
CALL INPUT
MOV SI,OFFSET ASCII_SEARCH_AREA ;pointer to search string
MOV LAST_SEARCH_STRING,SI ;save ptr to str for next
CALL STRLEN ;how long?
MOV LAST_STRING_LENGTH,CX ;save length for next
JCXZ NO_SEARCH ;if not, dont search
CALL SEARCH_MEM ;cx=length si=string
NO_SEARCH:
RET ;that's all
ASCII_SEARCH ENDP

;-----------------------------------------------------------------
;find next occurrence
;-----------------------------------------------------------------
NEXT_SEARCH PROC NEAR
MOV SI,LAST_SEARCH_STRING ;get address of last string
MOV CX,LAST_STRING_LENGTH
JCXZ NO_NEXT ;dont search
ADD START_OFFSET,1 ;move over one to start
JNC NOS_WRAP ;if it causes wrap,
ADD START_SEGMENT,1000H ;wrap segment
NOS_WRAP:
CALL SEARCH_MEM ;search for next string
NO_NEXT:
RET ;that's all
NEXT_SEARCH ENDP

;-----------------------------------------------------------------
;hex search routine
;-----------------------------------------------------------------
HEX_SEARCH PROC NEAR
CALL CLEAR_LAST_LINE ;clear line for input
MOV SI,OFFSET HEX_SEARCH_PROMPT ;prompt
MOV AX,24 ;last row
XOR BX,BX ;first col
CALL DISPLAY_STRING ;show the prompt
CALL STRLEN ;find out how long prompt is
MOV DL,CL ;dl = col after prompt
MOV DH,24 ;dx = row col
MOV SI,OFFSET HEX_SEARCH_AREA ;
MOV CX,20 ;size of the string
CALL CLEAR_BUF
MOV DI,OFFSET HEX_SIEVE ;pointer to sieve
MOV BX," " ;replacement char
;dx=row col,cx=size,bx=replace,si=prompt,di=sieve
CALL INPUT
MOV SI,OFFSET HEX_SEARCH_AREA ;pointer to search string
CALL STRLEN
CMP CL,1 ;must be at least two digits
JBE NO_SEARCH ;otherwise search fails
MOV LAST_SEARCH_STRING,SI ;save it
MOV LAST_STRING_LENGTH,CX ;save length
CALL ELIMINATE_BLANKS ;eliminate blanks in input
CALL CONVERT_ASCII_HEX_TO_HEX_STRING ;string into hex
MOV LAST_STRING_LENGTH,CX ;save length
CALL SEARCH_MEM ;find it
NOSEARCH:
RET ;that's all
HEX_SEARCH ENDP

;-----------------------------------------------------------------
;clear ds:[si] with (cx) spaces
;-----------------------------------------------------------------
CLEAR_BUF PROC NEAR
PUSH CX
PUSH SI
CLEARIT:
MOV BYTE PTR [SI]," "
INC SI
LOOP CLEARIT
POP SI
POP CX
RET
CLEAR_BUF ENDP

;-----------------------------------------------------------------
;translate ascii hex string to binary string, return size in cx
;-----------------------------------------------------------------
CONVERT_ASCII_HEX_TO_HEX_STRING PROC NEAR
PUSH SI ;save si for use in main
MOV DI,SI ;duplicate si
MOV BX,SI ;for later use
NEXTC:
LODSW ;get two ascii digits
OR AL,AL ;are we at the end
JZ C_EXIT ;if so, string is converted
OR AH,AH ;in case wrong # digits
JZ C_EXIT ;string is converted
CALL ASCII_HEX_TO_HEX ;translate ascii to binary
STOSB ;put back in string
JMP NEXTC ;get next two characters
C_EXIT:
XOR AL,AL ;clear al
STOSB ;put terminator after this
SUB DI,BX ;find out how many bytes
LEA CX,[DI-1] ;we have converted
POP SI ;restore important register
RET ;that's all
CONVERT_ASCII_HEX_TO_HEX_STRING ENDP

;-----------------------------------------------------------------
;close up blanks in a string
;-----------------------------------------------------------------
ELIMINATE_BLANKS PROC NEAR
PUSH SI ;save si for main
PUSH DS ;set ds:si and es:di to same
POP ES
MOV DI,SI
NEXT_SQ:
LODSB ;get character
CMP AL," " ;if space
JZ NEXT_SQ ;skip it
STOSB ;otherwise store it
OR AL,AL ;if zero
JNZ NEXT_SQ ;done whole string
SQUOZE:
POP SI ;restore important register
RET ;that's all
ELIMINATE_BLANKS ENDP

;-----------------------------------------------------------------
;go to command
;-----------------------------------------------------------------
GOTO PROC NEAR
CALL CLEAR_LAST_LINE ;clear line for input
MOV AX,24 ;row
XOR BX,BX ;col
MOV SI,OFFSET START_DISPLAY ;prompt
CALL DISPLAY_STRING ;show it
MOV DX,180FH ;row col
MOV SI,OFFSET START_DISPLAY+15 ;destination
MOV CX,9 ;size
MOV DI,OFFSET ADDR_SIEVE ;sieve
MOV BX,"0" ;replace char
;dx=row col,cx=size,bx=replace,si=prompt,di=sieve
CALL INPUT
MOV SI,OFFSET START_DISPLAY+15 ;ascii address
MOV DI,OFFSET START_SEGMENT ;destination
CALL CONVERT_ADDR ;32 bit ascii address to ptr
CLC
RET
GOTO ENDP

;-----------------------------------------------------------------
;convert ascii to binary segment and offset
; si=ascii di= pointer to destination
;-----------------------------------------------------------------
CONVERT_ADDR PROC NEAR
PUSH DS
POP ES ;set es for stos
INC DI
CALL CONVERT_BYTE ;segment
CALL CONVERT_BYTE
INC SI ;colon
CALL CONVERT_BYTE ;offset
CALL CONVERT_BYTE
RET ;that's all
CONVERT_ADDR ENDP
;-----------------------------------------------------------------
CONVERT_BYTE PROC NEAR
LODSW ;get two chars
CALL ASCII_HEX_TO_HEX ;make hex
MOV [DI],AL ;put in destination
DEC DI ;set up for next character
RET ;that's all
CONVERT_BYTE ENDP

;-----------------------------------------------------------------
;display main screen
;-----------------------------------------------------------------
BODY PROC NEAR
CALL TURN_OFF_CRT ;turn off monitor if necessary
PUSH BP ;use the
MOV BP,SP ;stack
SUB SP,8 ;for temporary variables
PUSH DS ;save ds for main routine
LES BX,DWORD PTR START_OFFSET ;get start pointer
MOV [BP-2],BX ;save on stack
MOV [BP-4],ES ;for later use
CALL NORMALIZE ;change es:bx so 10h <= bx >=0
MOV [BP-6],BX ;save value on stack
MOV [BP-8],ES ;for later use
MOV DS,[BP-4] ;get pointer
MOV SI,[BP-2] ;to memory to display
MOV CS:ROW,0 ;initialize row counter
NEXT_LINE:
INC CS:ROW ;next display row
MOV AX,CS:ROW ;row
XOR BX,BX ;col
CALL CALC_SCRN_ADDR ;get es:di for that address
MOV CX,[BP-8] ;hi part of norm 20 bit desc
CALL WORD_OUT ;print it
INC WORD PTR[BP-8] ;add 1 to seg = 16 bytes
MOV AL,[BP-6] ;get lo part
CALL HEX_TO_ASCII_HEX ;convert to ascii
XCHG AL,AH ;lowest significant nybble
CALL CHAR_OUT ;print lo part of 'ABS' value
CALL SPACE_OUT ;put separator
MOV CX,[BP-4] ;segment being displayed
CALL WORD_OUT ;print it
MOV AL,":" ;followed
CALL CHAR_OUT ;by a colon
MOV CX,[BP-2] ;offset this line
CALL WORD_SPACE ;print offset/space
MOV CX,16 ;# characters to print
MOV DX,SI ;save ptr to present line
HEX_PART:
LODSB ;get character
CALL BYTE_OUT ;print two hex nybbles
CALL SPACE_OUT ;print space
LOOP HEX_PART ;all 16 hex digits
MOV SI,DX ;get ptr to present line
MOV CX,16 ;all 16 hex digits
MOV AL,70H ;reverse the ascii part
ASCII_PART:
MOVSB ;print the ascii part
STOSB ;reverse attribute on it
LOOP ASCII_PART ;all 16 ascii chars
ADD WORD PTR[BP-6],10H ;'ABS' + 16
ADC WORD PTR[BP-8],0 ;
ADD WORD PTR[BP-2],10H ;'SEG:OFF' + 16
JNC NO_WRAP ;if it wraps
ADD WORD PTR[BP-4],1000H ;wrap the segment
MOV DS,[BP-4] ;get the data segment
NO_WRAP:
CMP CS:ROW,23 ;if not completely done
JB NEXT_LINE ;do another
POP DS ;restore ds for main routine
ADD SP,8 ;clean off temporary variables
POP BP ;restore bp
CALL TURN_ON_CRT ;turn on monitor if necessary
RET ;that's all
BODY ENDP

WORD_SPACE LABEL NEAR
CALL WORD_OUT ;output two nybbles from cx
SPACE_OUT LABEL NEAR ;then
MOV AL," " ;output a space
JMP SHORT CHAR_OUT
WORD_OUT LABEL NEAR
MOV AL,CH ;get high nybble
CALL BYTE_OUT ;print it
MOV AL,CL ;get low nybble
BYTE_OUT LABEL NEAR
CALL HEX_TO_ASCII_HEX ;cvt hex to two char nybbles
STOSB ;print highest order nybble
INC DI ;skip attribute
MOV AL,AH ;get low order nybble
CHAR_OUT LABEL NEAR
STOSB ;print it
INC DI ;skip attribute
RET ;that's all

END_MINUS_START PROC NEAR
LES BX,DWORD PTR START_OFFSET ;get end segment
CALL NORMALIZE ;change es:bx so 10h <= bx >=0
MOV AX,ES ;save normalized result
MOV CX,BX ;for later use
LES BX,DWORD PTR END_OFFSET ;get end address
CALL NORMALIZE ;change es:bx so 10h <= bx >=0
MOV DX,ES ;get end segment
SUB DX,AX ;calculate segments to search
MOV SEARCH_SEGMENTS,DX
MOV SEARCH_BYTES,BX ;# bytes in final segment
MOV ES,AX ;set es:bx to
MOV BX,CX ;start address
RET ;that's all
END_MINUS_START ENDP

;-----------------------------------------------------------------
;make 20 bit pointer in es:bx from segment:offset in es:bx
;-----------------------------------------------------------------
NORMALIZE PROC NEAR
PUSH AX ;save these registers
PUSH CX ;for main routine
PUSH DX ;
MOV AX,BX ;get the offset
MOV CL,4 ;make into
SHR AX,CL ;number of paragraphs
MOV DX,ES ;get segment
ADD DX,AX ;add in number of paragraphs
MOV ES,DX ;back into segment
SHL AX,CL ;calc offset into segment

SUB BX,AX ;paras - paras mod 16
POP DX ;retstore registers
POP CX ;for main routine
POP AX
RET ;that's all
NORMALIZE ENDP

;-----------------------------------------------------------------
;si = string cx = string_size
;search for match of string beginning at start_segment
;-----------------------------------------------------------------
SEARCH_MEM PROC NEAR
MOV DI,CX ;save string size
CALL END_MINUS_START ;calculate search length
LOOK_AGAIN:
CMP SEARCH_SEGMENTS,1000H ;more than or equal 64k?
JAE MORE_THAN_ENOUGH ;if so, search 64k
MOV AX,SEARCH_SEGMENTS ;otherwise, get what's left
MOV CL,4 ;
SHL AX,CL ;segs*16 = bytes to search
ADD AX,SEARCH_BYTES ;add in the last few bytes
JMP SHORT LOOK ;and go look
MORE_THAN_ENOUGH:
MOV AX,-1 ;64k search
LOOK:
SUB AX,BX ;subtract initial offset
JC SEARCH_NOT_FOUND ;offset < search size?
SUB AX,DI ;subtract off string size
JBE SEARCH_NOT_FOUND ;less than search size?
CMP AX,DI ;amount left to search
JB SEARCH_NOT_FOUND ;less than search size?
MOV DX,AX ;dx gets search size
MOV CL,4 ;
SHR DX,CL ;number of segments to search
SUB SEARCH_SEGMENTS,DX ;decrease the amount to search
;si = string di = string size
CALL SEARCH ;es:bx=ptr,ax=bytes to search
JZ SEARCH_FOUND ;if zero flag, string is found
ADD AX,1 ;next character after fail
MOV BX,AX ;into es:bx
JNC NOWR ;if offset rolls over
MOV AX,ES ;add 64k
ADD AX,1000H ;to the
MOV ES,AX ;offset
NOWR:
CALL NORMALIZE ;set to start at the top of
JMP LOOK_AGAIN ;a new segment
SEARCH_NOT_FOUND:
XOR AX,AX ;start over
MOV ES,AX
CMP AL,1 ;clear zero flag
SEARCH_FOUND:
MOV START_SEGMENT,ES ;set page
MOV START_OFFSET,AX ;address of found string
RET ;that's all
SEARCH_MEM ENDP

;-----------------------------------------------------------------
;si = string di = string size es:bx = pointer to buffer to search
;ax = number of bytes in buffer to search. Zero flag set if found
;-----------------------------------------------------------------
SEARCH PROC NEAR ;si points at string
PUSH BX
PUSH DI
PUSH SI
XCHG BX,DI ;string size, ptr to data area
MOV CX,AX ;# chars in segment to search
BYTE_ADD:
LODSB ;char for first part of search
NEXT_SRCH:
REPNZ SCASB ;is first char in string in buffer
JNZ NOT_FOUND ;if not, no match
PUSH DI ;save against cmpsb
PUSH SI
PUSH CX
LEA CX,[BX-1] ;# chars in string - 1
JCXZ ONE_CHAR ;if one char search, we have found it
REP CMPSB ;otherwise compare rest of string
ONE_CHAR:
POP CX ;restore for next cmpsb
POP SI
POP DI
JNZ NEXT_SRCH ;if zr = 0 then string not found
NOT_FOUND:
LEA AX,[DI-1] ;ptr to last first character found
POP SI
POP DI
POP BX
RET ;that's all
SEARCH ENDP

;------------------------------------------------------------------
;dx=row:col,cx=max len,bx=replace character,si=prompt,di=sieve
;------------------------------------------------------------------
INPUT PROC NEAR
PUSH BP ;create stack frame
MOV BP,SP
SUB SP,6
CALL CURSOR_ON
MOV [BP-2],CX ;save size of destination
MOV [BP-4],SI
MOV [BP-6],DX
XOR CX,CX ;clear count of chars entered
NEXT_DISPLAY:
PUSH BX ;save replace char
PUSH SI
MOV AL,[BP-5] ;row
CBW ;into ax
MOV BL,[BP-6] ;col
XOR BH,BH ;into bx
MOV SI,[BP-4]
CALL DISPLAY_STRING ;show string at si
POP SI
POP BX ;restore replace char
PUSH DS ;set up string addressing
POP ES
NEXT_KEY:
CMP BYTE PTR[SI],":" ;skip colon in destination
JNZ NOT_COLON
INC CX ;next count
INC DL ;next col
INC SI ;point to next char
NOT_COLON:
CALL SETPOS ;set cursor position
CALL GETKEY ;get next key
CMP AH,1 ;if scan = 1
JZ END_OF_INPUT;then it's esc
CMP AH,1CH ;if scan = 1ch
JZ END_OF_INPUT ;then it's carriage return
CMP AH,L_ARR ;if it's left arrow
JZ BACK_SPACE ;or
CMP AH,0EH ;if it's backspace key
JNZ NOT_BACK_SPACE ;then back up
BACK_SPACE:
JCXZ NEXT_KEY ;if count 0 can't back up
DEC CX ;decrement count
DEC DL ;decrement column
DEC SI ;decrement pointer to chars
CMP BYTE PTR[SI],":" ;if pointer is to colon,
JZ BACK_SPACE ;then back up again
MOV BYTE PTR[SI],BL ;put replace character in
JMP NEXT_DISPLAY ;show line again
NOT_BACK_SPACE:
CMP CX,[BP-2] ;is count = max count
JZ NEXT_KEY ;if so, can't put in buffer
NEXT_INPUT:
CALL VERIFY ;ax = char di = sieve
JNZ NEXT_KEY ;if illegal char get new key
MOV [SI],AL ;otherwize put in buffer
INC CX ;next count
INC SI ;pointer to next char
INC DL ;next column
JMP NEXT_DISPLAY ;show line again
END_OF_INPUT:
CMP BL,"0" ;if replace char is "0"
JZ NO_ZERO ;dont put terminator in
MOV BYTE PTR[SI],0 ;terminate string
NO_ZERO:
CALL CURSOR_OFF
ADD SP,6 ;clean off stack
POP BP ;
RET ;that's all
INPUT ENDP

GETKEY PROC NEAR

LOOP_KEY: ;* TO HELP OTHER PROGRAMS TO RUN IN BACKGROUND.
INT 28H ;* I ADDED THIS PART .
MOV AH,1 ;*
INT 16H ;*
JZ LOOP_KEY ;*
XOR AH,AH ;getkey function number
INT 16H ;go get a key
RET ;return it
GETKEY ENDP

;------------------------------------------------------------------
;ax = row bx = col displays string at ds:si
;------------------------------------------------------------------
DISPLAY_STRING PROC NEAR
PUSH SI ;save si for main routines
PUSH DI
CALL CALC_SCRN_ADDR ;get es:di for row col
MOV AH,7 ;get normal attribute
CALL WAIT_VERT
NEXT_CHAR:
LODSB ;get char from string
OR AL,AL ;test for zero
JZ LAST_CHAR ;if zero, string has been printed
STOSW ;put char and attribute on screen
JMP NEXT_CHAR ;until string has been printed
LAST_CHAR:
POP DI
POP SI ;restore si for main routines
RET ;that's all
DISPLAY_STRING ENDP

;------------------------------------------------------------------
;ax = row bx= col, returns es:di pointing to the screen address
;------------------------------------------------------------------
CALC_SCRN_ADDR PROC NEAR
PUSH CX
PUSH DX ;mul destroys dx
PUSH AX ;save ax for later
MOV ES,CS:BIOS_SEG ;into es
MOV AX,0B800H ;screen seg = b800h
CMP ES:BYTE PTR[49H],7 ;if 40:49 == 7
JNZ COLOR ;then
MOV AH,0B0H ;screen seg = b000h
COLOR:
MOV ES,AX ;into es
POP CX ;get row
MOV AX,160 ;160 bytes to a row of text
ADD AX,CS:DIFF ;# characters > 80
ADD AX,CS:DIFF ;# of attribute bytes > 80
MUL CX ;row * 160 + diff*2
ADD AX,BX ;+col
ADD AX,BX ;row*160 + col*2
MOV DI,AX ;es:di points to right address
POP DX ;restore dx
POP CX
RET ;that's all
CALC_SCRN_ADDR ENDP

;------------------------------------------------------------------
;ax = char di = sieve
;------------------------------------------------------------------
VERIFY PROC NEAR
PUSH CX ;save registers for main routines
PUSH DI
PUSH SI
PUSH DS
POP ES
MOV SI,DI ;si = sieve address
CALL STRLEN ;how many chars in sieve (cx)
JCXZ ANY_KEY ;get key if no chars in sieve
CMP AL,"A" ;if lower case
JB NOT_SMALL ;
AND AL,0DFH ;make upper case
NOT_SMALL:
REPNZ SCASB ;search for char in the sieve
ANY_KEY: ;set zero flag to indicate it
POP SI ;restore registers
POP DI
POP CX
RET ;that's all
VERIFY ENDP
;----------------------------------------------------------------------
STRLEN PROC NEAR
PUSH SI ;save si for main routines
PUSH AX
MOV CX,-1 ;count for string length
NEXTS:
INC CX ;next count
LODSB ;next char
OR AL,AL ;is it a zero
JNZ NEXTS ;if not get next char
POP AX
POP SI ;restore si for main routines
RET ;thats all
STRLEN ENDP

;------------------------------------------------------------------
;input: al = ascii lsb ah = ascii msb
; example: if hex is '3F', ah = '3' al = 'F' result: al = 3Fh
;output: al = hex byte
;------------------------------------------------------------------
ASCII_HEX_TO_HEX PROC NEAR
TEST AH,40H ;if "A" - "F"
JZ AH_EXIT ;
SUB AH,"A"-10 ;make 10-15
AH_EXIT:
TEST AL,40H ;if "A" - "F"
JZ AL_EXIT ;
SUB AL,"A"-10 ;make 10-15
AL_EXIT:
AND AX,0F0FH ;if "0" - "9" make 0-9
SHL AL,1 ;
SHL AL,1 ;
SHL AL,1 ;
SHL AL,1 ;
OR AL,AH ;combine msn and lsn
RET ;that's all
ASCII_HEX_TO_HEX ENDP

;------------------------------------------------------------------
;input: al = char to be translated:
; example: if hex is '3F', al = '3' ah = 'F'
;output: al = msb of translation, ah = lsb of translation
;------------------------------------------------------------------
HEX_TO_ASCII_HEX PROC NEAR
DB 0D4H,10H ;div AL/16-remainder in al
OR AX,3030H ;make ascii
CMP AL,"9"
JBE ALEXIT
ADD AL,7 ;if a - f make "A" - "F"
ALEXIT:
CMP AH,"9"
JBE AHEXIT
ADD AH,7 ;if a - f make "A" - "F"
AHEXIT:
XCHG AL,AH
RET ;that's all
HEX_TO_ASCII_HEX ENDP

LAST_BYTE LABEL BYTE ;marks end of resident code and
;start of memory used to save screen

INSTALLED_SEGMENT DW 0
DISABLED DB CR,LF,"RAMVIEW IS DISABLED",CR,LF,"$"
ENABLED DB CR,LF,"RAMVIEW IS RE-ENABLED",CR,LF,"$"

;------------------------------------------------------------------
;determine if vectors have changed since program was installed
;------------------------------------------------------------------
HOOKED_VECTORS_SAME? PROC NEAR
MOV CX,INSTALLED_SEGMENT ;get executing segment
XOR AX,AX ;interrupt table segment
MOV ES,AX ;into the extra segment
CMP CX,ES:[10H*4+2] ;see if int 10h points at us
JNZ VECTOR_CHANGED
CMP CX,ES:[9*4+2] ;see if int 9 points at us
VECTOR_CHANGED:
RET
HOOKED_VECTORS_SAME? ENDP

;------------------------------------------------------------------
;determine if program is already installed
;------------------------------------------------------------------
PROGRAM_ALREADY_IN PROC NEAR
NOT WORD PTR START ;only srch for active copy
MOV START_SEGMENT,60H ;start after dos
MOV START_OFFSET,0 ;
MOV END_SEGMENT,CS ;stop looking before you
MOV END_OFFSET,0 ; get to this program
MOV SI,OFFSET START ;start at modified byte
MOV CX,25 ;enough of a match
CALL SEARCH_MEM ;use our search
PUSHF ;save zr flag
MOV AX,START_SEGMENT ;get address of find
MOV INSTALLED_SEGMENT,AX ;save in installed address
MOV AX,START_OFFSET
MOV CL,4
SHR AX,CL
SUB AX,10H ;adjust for psp
ADD INSTALLED_SEGMENT,AX
MOV START_SEGMENT,0 ;reset starting address
MOV START_OFFSET,0
MOV END_OFFSET,0FH ;reset ending address
MOV END_SEGMENT,0FFFFH
POPF ;restore flgs from search
RET
PROGRAM_ALREADY_IN ENDP

;------------------------------------------------------------------
;uninstall routines
;------------------------------------------------------------------
UNINSTALL PROC NEAR
CALL HOOKED_VECTORS_SAME? ;if all vectors still hooked
JZ UNINSTALL_OK ;go ahead and dis installed

NOT WORD PTR START
MOV ES,INSTALLED_SEGMENT ;else, change the disable flag
NOT ES:DISABLE ;in the installed program
MOV DX,OFFSET ENABLED ;get the message corresponding
CMP ES:DISABLE,-1 ;to the action that causes
JNZ ITS_DISABLED ;
MOV DX,OFFSET DISABLED ;
ITS_DISABLED: ;
MOV AH,9 ;and display that message
INT 21H
JMP SHORT UNINSTALL_EXIT ;all done here.
UNINSTALL_OK:
MOV ES,INSTALLED_SEGMENT ;get resident prog's psp
MOV DX,ES:WORD PTR ADDR_INT9H ;put back int 9 vector
MOV DS,ES:WORD PTR ADDR_INT9H+2
MOV AH,25H
MOV AL,9
INT 21H
MOV DX,ES:WORD PTR ADDR_INT10H ;restore int 10h vector
MOV DS,ES:WORD PTR ADDR_INT10H+2
MOV AH,25H
MOV AL,10H
INT 21H

MOV DX,ES: WORD PTR INT08IP ;* MORE MOD ...
MOV DS,ES: WORD PTR INT08CP ;*
MOV AX,2508H ;*
INT 21H ;*
; MOV DX,ES: WORD PTR INT28IP ;*
; MOV DS,ES: WORD PTR INT28CP ;*
; MOV AX,2528H ;*
; INT 21H ;*

PUSH ES
MOV ES,ES:[2CH] ;get segment of environment
MOV AH,49H ;belonging to resident program
INT 21H ;free it
POP ES
MOV AH,49H ;free memory block of program
INT 21H
PUSH CS
POP DS ;get back our data segment
MOV DX,OFFSET UNINSTALLED
MOV AH,9
INT 21H ;for whom it may concern
UNINSTALL_EXIT:
RET
UNINSTALL ENDP
;----------------------------------------------------------------------
INSTALL PROC NEAR
MOV CL,9 ;link vector 9
MOV SI,OFFSET ADDR_INT9H
MOV DI,OFFSET INT9H
CALL INSTALL_VECTOR
MOV CL,10H ;link vector 10h
MOV SI,OFFSET ADDR_INT10H
MOV DI,OFFSET INT10H
CALL INSTALL_VECTOR

mov ax,3508h ; MORE MOD...
int 21h ;
mov dx,es ;
mov cs: word ptr int08cp,dx ;
mov cs: word ptr int08ip,bx ;
mov dx,offset int_08 ;
mov ax,2508h ;
int 21h ;
;
; mov ax,3528h ;
; int 21h ;
; mov dx,es ;
; mov cs: word ptr int28cp,dx ;
; mov cs: word ptr int28ip,bx ;
; mov dx,offset int_28 ;
; mov ax,2528h ;
; int 21h ;
;
mov ax,3400h ; GET InDOS FLAG ADDRESS.
int 21h ;
mov dx,es ;
mov cs: word ptr doss_seg,dx ;
mov cs: word ptr doss_ofs,bx ;
;
push ds ;
push si ;
mov ax,5d06h ; GET DOS SWAPABLE DATA AREA .
int 21h ;
mov dx,ds ; DOS CRITICAL ERROR FLAG
mov bx,si ; ADDRESS ....
mov cs: word ptr dos_crit_seg,dx ;
mov cs: word ptr dos_crit_ofs,bx ;
pop si ;
pop ds ;

RET
INSTALL ENDP
;----------------------------------------------------------------------
INSTALL_VECTOR PROC NEAR

MOV AL,CL ;get vector number
MOV AH,35H ;get interrupt vector
INT 21H ;
MOV [SI],BX ;save interrupt vector
MOV [SI+2],ES ;
MOV DX,DI ;get replacement address
MOV AH,25H ;set vector address
MOV AL,CL ;for vector
INT 21H
RET

INSTALL_VECTOR ENDP

_TEXT ENDS
END START


  3 Responses to “Category : Utilities for DOS and Windows Machines
Archive   : RAMVIEW.ZIP
Filename : RAMVIEW.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. 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/