Category : Files from Magazines
Archive   : PCTV1N4.ZIP
Filename : TRANSFER.ASM

 
Output of file : TRANSFER.ASM contained in archive : PCTV1N4.ZIP
%TITLE "transfer.asm"

;** Resident data-transfer utility. (c) 1990 by Tom Swan.

IDEAL
JUMPS

;---------------------------------------------------------------
; ---- Equates ----
;---------------------------------------------------------------

CR equ 13 ; ASCII carriage return
LF equ 10 ; ASCII line feed
TSRINT equ 64h ; TSR's interrupt number
STACK_SIZE equ 2048 ; TSR loader's stack size
BUF_SIZE equ 512 ; Size of storage buffer

;----- Function numbers

FN_GETBLOCK equ 1 ; Get stored data from TSR
FN_PUTBLOCK equ 2 ; Save data in TSR
FN_CLEARBLOCK equ 3 ; Clear data stored in TSR
FN_STATUS equ 4 ; Retrieve TSR's status

;----- Error codes

ERR_BADFUNCTION equ 1 ; Bad function number in AH

;----- Equates for "stuffing" register values on the stack

SETCX equ [word bp - 6] ; Location of pushed CX
SETDX equ [word bp - 8] ; Location of pushed DX
SETDS equ [word bp - 14] ; Location of pushed DS
SETFLAGS equ [word bp + 6] ; Location of pushed flags
SETCF equ 01h ; Value to set pushed cf flag
RESETCF equ not SETCF ; Value to reset pushed cf flag
SETZF equ 40h ; Value to set pushed zf flag
RESETZF equ not SETZF ; Value to reset pushed zf flag

;---------------------------------------------------------------
; ---- Resident Portion ----
;---------------------------------------------------------------

;----- The TSR's code segment

SEGMENT TSR_code 'TSRCODE'

;---------------------------------------------------------------
; TSR_isr The TSR's Interrupt Service Routine (ISR)
;---------------------------------------------------------------
; Input:
; ah = function code (1, 2, 3, or 4)
; Note: See individual functions for register requirements
; Note: Uses 00 bytes of caller's stack
; Output:
; cf = 0 (no error)
; cf = 1 (error: use function 4 to determine cause)
; zf = 0 (not in progress)
; zf = 1 (in progress--retry)
; Note: cf and zf are meaningless for function 4
; Registers:
; cf, zf, cx, dx (see individual functions for details)
;---------------------------------------------------------------
PROC TSR_isr far

ASSUME cs:TSR_code, ds:TSR_data

sti ; Enable interrupts
push bp ; Prepare bp for addressing stack
mov bp, sp
push ax bx cx dx si di ds es ; Save other registers
mov bx, TSR_data ; Prepare ds for
mov ds, bx ; addressing TSR's data seg

;----- Execute reentrant routines (may be called recursively)

cmp ah, FN_STATUS ; Do Status function
je Status

;----- Check if TSR is already running

and SETFLAGS, RESETZF ; Reset pushed zf to 0
cmp [inProgress], 0 ; Check inProgress flag
je TSR_inactive ; Continue if flag = 0
or SETFLAGS, SETZF ; Set pushed zf to 1
jmp TSR_quit2 ; Quit TSR without resetting
; the in-progress flag

;----- Execute non-reentrant routines. The next instruction should
; be the first to write to the TSR's data segment.

TSR_inactive:
inc [inProgress] ; Set TSR inProgress flag
cmp ah, FN_GETBLOCK ; Do GetBlock function
je GetBlock
cmp ah, FN_PUTBLOCK ; Do PutBlock function
je PutBlock
cmp ah, FN_CLEARBLOCK ; Do ClearBlock function
je ClearBlock
mov al, ERR_BADFUNCTION ; Report bad function number

;----- All errors except in progress exit from here

TSR_errExit:
mov [errorCode], al ; Save error code
or SETFLAGS, SETCF ; Set pushed cf bit to 1
jmp short TSR_quit ; Jump to skip next section

;----- All nonerrors except status request exit from here

TSR_exit:
mov [errorCode], 0 ; Reset error code
and SETFLAGS, RESETCF ; Reset pushed cf bit to 0
TSR_quit:
mov [inProgress], 0 ; Reset inProgress flag

;----- Status request and in-progress error exit from here

TSR_quit2:
pop es ds di si dx cx bx ax bp
iret

ENDP TSR_isr

;---------------------------------------------------------------
; GetBlock Get stored data from TSR
;---------------------------------------------------------------
; Input:
; ah = 1 (FN_GETBLOCK)
; cx = size of destination block
; es:di = destination address
; Note: Destination must be large enough to hold at
; least cx bytes. In no case will more than cx
; bytes be transferred to the destination.
; Note: Contents of stored data are not disturbed.
; Output:
; cf = 0 (no error)
; cf = 1 (error: use function 4 to determine cause)
; cx = number of bytes actually transferred
; Note: If cx = 0, then buffer was empty, and no data
; was transferred to the destination.
; Registers:
; cx
;---------------------------------------------------------------
PROC GetBlock

mov ax, [bufcount] ; Get buffer count
cmp cx, ax ; Is cx <= bufcount?
jbe GetBlock_10 ; Jump if yes
mov cx, ax ; Else limit cx to bufcount
GetBlock_10:
mov ax, cx ; Save transfer count
jcxz GetBlock_99 ; Exit if cx = 0
mov si, offset buffer ; ds:si = source address
cld ; Move in forward direction
rep movsb ; Transfer
GetBlock_99:
mov SETCX, ax ; Set cx = bytes transferred
jmp TSR_exit ; Normal exit--no errors

ENDP GetBlock

;---------------------------------------------------------------
; PutBlock Save data in TSR
;---------------------------------------------------------------
; Input:
; ah = 2 (FN_PUTBLOCK)
; cx = number of bytes to transfer
; dl = data type code (0=untyped)
; ds:si = source address
; Note: Copies cx bytes from source to internal
; buffer. Value in cx must be <= buffer
; size. If cx = 0, the buffer is erased.
; Output:
; cf = 0 (no error)
; cx = number of bytes actually saved
; Registers:
; cx
;---------------------------------------------------------------
PROC PutBlock

mov [buftype], dl ; Save data type code
cmp cx, BUF_SIZE ; Is cx <= max buffer size?
jbe PutBlock_10 ; If yes, continue
mov cx, BUF_SIZE ; Else limit cx to max
PutBlock_10:
push ds ; Set es to TSR's
pop es ; data segment
mov di, offset buffer ; es:di = destination address
mov [bufcount], cx ; Save buffer count
mov SETCX, cx ; Save count in stack too
mov ax, SETDS ; Get saved DS from stack
mov ds, ax ; ds:si = source address
jcxz PutBlock_99 ; Exit if count = 0
cld ; Move in forward direction
rep movsb ; Transfer
PutBlock_99:
push es ; Restore TSR's data segment
pop ds ; from es to ds
jmp TSR_exit ; Normal exit--no errors

ENDP PutBlock

;---------------------------------------------------------------
; ClearBlock Clear data stored in TSR
;---------------------------------------------------------------
; Input:
; ah = 3 (FN_CLEARBLOCK)
; Note: Erases internal buffer.
; Output:
; cf = 0 (no error)
; Registers:
; none
;---------------------------------------------------------------
PROC ClearBlock

mov [bufcount], 0 ; Reset buffer counter
mov [buftype], 0 ; Reset buffer data type code
jmp TSR_exit ; Normal exit--no errors

ENDP ClearBlock

;---------------------------------------------------------------
; Status Retrieve TSR's status
;---------------------------------------------------------------
; Input:
; ah = 4 (FN_STATUS)
; Note: This routine does not write to the TSR's
; data segment; therefore, it may be called
; recursively (i.e. this function is reentrant.)
; Output:
; dh = error code from previous operation
; dl = data type code
; cx = number of bytes stored in buffer
; Registers:
; cx, dx
;---------------------------------------------------------------
PROC Status

mov ax, [bufcount] ; Get count of bytes in buffer
mov SETCX, ax ; Stuff into pushed CX on stack
mov ax, [errNtype] ; Get error and type codes
mov SETDX, ax ; Stuff into pushed DX on stack
jmp TSR_quit2 ; Alternate exit

ENDP Status

ENDS TSR_code

;----- The TSR's data segment

SEGMENT TSR_data 'TSRDATA'

DOSversion dw 0 ; Major and minor version numbers
inProgress db 0 ; 0=TSR not active; 1=TSR active
db 0 ; Keep data word aligned

;----- Note: Order of next two bytes is critical. Don't move!

LABEL errNtype word
buftype db 0 ; Buffer data-type code
errorCode db 0 ; Previous operation's error code

bufcount dw 0 ; Bytes stored in buffer

;----- The storage buffer

buffer db BUF_SIZE dup(0)

ENDS TSR_data

;---------------------------------------------------------------
; ---- Transient Portion ----
;---------------------------------------------------------------

;----- The TSR loader's code segment

SEGMENT LOADER_code 'CODE'

PROC Load_TSR

ASSUME cs:LOADER_code, ds:TSR_data

mov ax, TSR_data ; Initialize ds to address
mov ds, ax ; the TSR's data segment

ASSUME ds:TSR_data

call CheckVersion ; Abort if DOS version = 1.x
jnc LTSR_10 ; Jump if cf = 0 (no error)
mov al, 1 ; Select error message #1
jmp ErrorExit ; End program if DOS 1.x
LTSR_10:
push es ; Save PSP address on stack
push ds ; Save TSR data segment

;----- Install interrupt service routine

mov al, TSRINT ; Get current vector for
mov ah, 35h ; the TSR's interrupt number
int 21h ; using DOS function 35h.
mov bx, es ; Copy segment address to bx
or bx, bx ; and test if bx = 0.
jz LTSR_20 ; Jump if vector is not used
pop ds ; Restore TSR data seg to ds
pop es ; Restore PSP address to es
mov al, 2 ; Set error code number
jmp ErrorExit ; And exit with error message
LTSR_20:
mov ax, TSR_code ; Set ds to TSR's code
mov ds, ax ; segment.

ASSUME ds:TSR_code

mov dx, offset TSR_isr ; Set dx to TSR's int service
mov al, TSRINT ; routine, and set the
mov ah, 25h ; interrupt vector for TSRINT
int 21h ; with DOS function 25h.
pop ds ; Restore TSR data segment

;----- Terminate and stay resident

mov ax, LOADER_data ; Initialize ds to loader's
mov ds, ax ; data segment

ASSUME ds:LOADER_data

mov dx, offset doneMsg ; Display "TSR Loaded" message
mov ah, 09h ; by calling DOS print-
int 21h ; string function.
pop ax ; Restore PSP seg addr to ax
mov dx, cs ; dx <- Transient start addr
sub dx, ax ; dx <- Resident size
mov ax, 3100h ; DOS terminate function
int 21h ; Terminate, stay resident
; al = 0 (return code)
ENDP LOAD_TSR

;---------------------------------------------------------------
; ErrorExit Exit with error message and code in al
;---------------------------------------------------------------
; Input:
; al = error code 1..n
; ds = address of TSR's data segment
; es = psp segment address (DOS 1.x only)
; Output:
; none. program halted.
; Registers:
; none preserved
;---------------------------------------------------------------
PROC ErrorExit near

ASSUME ds:TSR_data

push [DOSversion] ; Save DOS version on stack
push ax ; Save error code on stack
mov ax, LOADER_data ; Initialize ds to loader's
mov ds, ax ; data segment

ASSUME ds:LOADER_data

mov dx, offset errorMsg ; Address "ERROR: " string
mov ah, 09h ; DOS print-string function
int 21h ; Display error lead-in
pop ax ; Restore error code to al
push ax ; Save code again
cmp al, 1 ; Does error code = 1?
jne test2 ; If not, check next code
mov dx, offset errmsg1 ; Address error message 1
jmp Exit ; Display message and exit
test2:
cmp al, 2 ; Error code 2
jne test3
mov dx, offset errmsg2
jmp Exit

;----- Other error codes or default

test3:
mov dx, offset defaultmsg ; Default error message

;----- Display message and exit. Error code still in al.

Exit:
mov ah, 09h ; DOS print-string function
int 21h ; Display error message
pop ax ; Restore error code to al
pop bx ; Restore DOS version to bx
cmp bl, 2 ; Is it ver. 2.x or higher?
jb ExitDOS1x ; Jump for versions 1.x

;----- End program for DOS 2.x and higher

mov ah, 4ch ; DOS terminate with code
int 21h ; End with error code in al

;----- End program for DOS 1.x

ExitDOS1x:
push es ; Push psp segment onto stack
xor ax,ax ; Set ax to 0000
push ax ; Push 0000 (stack=es:0000)
retf ; Far return exits program

ENDP ErrorExit

;---------------------------------------------------------------
; CheckVersion Test DOS version
;---------------------------------------------------------------
; Input:
; ds = address of TSR's data segment
; Output:
; DOSversion = version number
; ax = version number
; cf = 0 = DOS version 2.x or higher
; cf = 1 = DOS version 1.x
; Registers:
; ax
;---------------------------------------------------------------
PROC CheckVersion near

ASSUME ds:TSR_data

mov ah, 30h ; DOS get-version function
int 21h ; Get DOS version
mov [word DOSversion], ax ; Save in TSR data seg
cmp al, 02h ; Test major revision number
ret ; cf = 0 if al >= 2
; cf = 1 if al < 2
ENDP CheckVersion

ENDS LOADER_code

;----- TSR loader's data segment

SEGMENT LOADER_data 'DATA'

doneMsg db CR,LF,'Data Transfer Utility'
db CR,LF,'(c) 1990 by Tom Swan'
db CR,LF,'TSR Loaded',CR,LF,'$'
errorMsg db CR,LF,'ERROR: ', '$'
errmsg1 db 'Requires DOS 2.0 or later',CR,LF,'$'
errmsg2 db 'Interrupt vector in use',CR,LF,'$'
defaultmsg db 'Unknown cause',CR,LF,'$'

ENDS LOADER_data

;----- The TSR loader's stack segment

SEGMENT LOADER_stack stack 'STACK'
db STACK_SIZE dup(?)
ENDS LOADER_stack

END Load_TSR


  3 Responses to “Category : Files from Magazines
Archive   : PCTV1N4.ZIP
Filename : TRANSFER.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/