home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pctchnqs
/
1990
/
number4
/
transfer.asm
< prev
next >
Wrap
Assembly Source File
|
1990-07-28
|
17KB
|
455 lines
%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