home *** CD-ROM | disk | FTP | other *** search
- %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
-