home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
dskutl
/
pdtimprk.arc
/
PDTIMPRK.ASM
next >
Wrap
Assembly Source File
|
1987-09-02
|
22KB
|
532 lines
PAGE ,132 ; Corrected version, 9/2/87, DF
TITLE PDTIMPRK -- Public domain software by Dick Flanagan
;----------------------------------------------------------------------------+
; |
; Placed in the PUBLIC DOMAIN without warranty, guarantee, or |
; assumption of liability. All rights under copyright law are |
; unconditionally waived by the author. |
; |
; Written by Dick Flanagan, Ben Lomond, California, August 1987. |
; |
;----------------------------------------------------------------------------+
comment *
PPPPPP DDDDDD TTTTTTT III M M PPPPPP RRRRRR K K
P P D D T I MM MM P P R R K K
P P D D T I M M M M P P R R K K
PPPPPP D D T I M M M M PPPPPP RRRRRR KK
P D D T I M M M P R R K K
P D D T I M M P R R K K
P DDDDDD T III M M P R R K K
*
;----------------------------------------------------------------------------
;
; The purpose of this program is to automatically park fixed disk
; drive heads after a predetermined period of inactivity has passed.
;
; While there are several other programs available to accomplish
; this task, almost all are either copyrighted or have evolved from
; copyrighted material. Some of these programs are in the ignorent
; position of ostensibly being both copyrighted and in the public
; domain, or with restrictions on their "public domainness." Until
; their authors realize they can't have it both ways, these programs
; are assumed to be copyrighted.
;
; Program Syntax: PDTIMPRK <minutes>
;
; The <minutes> parameter is a single digit in the range of 1 to 9.
; It represents the number of minutes of inactivity that are to be
; allowed before the heads are to be automatically parked. Some
; people might like a slightly lower floor on this value, but
; (and since I can't imagine anyone realistically wanting a higher
; ceiling) I have retained this convention which is common in
; other programs.
;
; Only BIOS I/O functions are used, so the program should be
; usable across a broad spectrum of IBM-compatible computers.
;
; Theory of Operation:
;
; Every time a software interrupt 13 is issued it is inter-
; cepted by this program which checks if the request is for
; a fixed or floppy disk. If it is for a fixed disk, a
; count-down timer is reset and a flag is cleared to indicate
; that the disk heads are probably no longer parked.
;
; Every time a hardware timer interrupt 8 is detected it is
; also intercepted and, if the disks are not already parked
; and we are not in the process of parking them, the timer is
; decremented and checked to see if the interval has expired.
; If it has, a flag is set indicating that parking is in pro-
; gress, the disks are "seeked" to their innermost cylinder,
; the parking-in-progress flag is cleared, the heads-are-parked
; flag is set, and our task is done until the next interrupt
; 13 for one of the fixed disks sets our timer running again.
;
; The hardware timer interrupt 8 is used instead of the more
; conservative approach of using the bios-generated software
; interrupt 1C. The length of time required to park the disk
; heads requires that at least the timer interrupts be enabled
; during the parking, and the parking operation itself requires
; that disk interrupts be enabled. These effectivly dictate
; that we operate with all interrupts enabled.
;
; To enable interrupts from within the interrupt 1C logic would
; require that an EOI (End-Of-Interrupt) code be sent to the
; 8259A PIC (Programmable Interrupt Controller). The problem
; with this is that the Time Of Day interrupt handler that issued
; the interrupt 1C in the first place will issue another EOI to
; the 8259A as soon as we return. This second EOI can wreak
; havok with lower level interrupt handlers that may have been
; interrupted by the original timer interrupt.
;
; Instead, we intercept timer interrupt 8 and immediately pass
; it off to the bios Time Of Day routine. When we get control
; back, the EOI will already have been issued and we can proceed
; with a clear conscience.
;
; Author's Notes:
;
; Because I hate TSR's, I try to make them as small as possible.
; This one is about as small as I've seen with equivalent
; functionality.
;
; I have attempted to comment the code in such a way that its
; operation should be clearly evident and modifications or
; (horrors!) bug fixes should be easily implemented. I also
; hope the comments will help remove some of the mystery that
; surrounds assembly language in general and interrupt handlers
; in particular for many people.
;
; Because this program was a quick weekend project, its level of
; parameter checking and error recovery is minimal, but I feel it
; to be adequate for the task at hand. For example:
;
; Limitations:
;
; o Only the first non-blank, non-zero character on the command
; line is examined. If 20 is entered, the 2 will be accepted
; and the 0 will be ignored.
;
; o If one drive in a two-drive system is kept busy frequently
; enough so as not to be parked, the other drive, regardless
; of how idle it might be, will not be parked until the other
; one finally is (the old two-drive-one-timer problem).
;
; o No attempt is made to determine if the program has already
; been installed. Simple tests are easily confused and complex
; ones aren't worth the trouble for a program as small and
; benign as this.
;
; o No error recovery is attempted.
;
;----------------------------------------------------------------------------
CODE SEGMENT PARA
ASSUME CS:CODE, DS:NOTHING, ES:NOTHING, SS:NOTHING
ORG 0100H ; reserve space for psp
START: JMP INIT ; jump to initialization code
INT08VECT LABEL DWORD ; pre-existing interrupt 08 vector
INT08OFF DW 0 ;
INT08SEG DW 0 ;
INT13VECT LABEL DWORD ; pre-existing interrupt 13 vector
INT13OFF DW 0 ;
INT13SEG DW 0 ;
DRV0CYL DW 0 ; cylinder to park drive 0
DRV1CYL DW 0 ; cylinder to park drive 1
PARKED DB 0 ; non-zero = drives are parked
BUSY DB 0 ; non-zero = parking in progress
TIMER DW 0 ; decremented-to-zero timer counter
TICKS DW 0 ; timer reset value
;----------------------------------------------------------------------------
;
; all disk i/o via interrupt 13 is intercepted here. while all
; int 13 activity does not necessarily result in the disk drives
; becoming 'unparked,' the penalty in so assuming is reasonably
; small.
;
;----------------------------------------------------------------------------
INT13 PROC
;
; the less time spent with interrupts disabled the better, so
; reenable them right away
;
STI ; enable interrupts
;
; check if this request is for a diskette drive instead of for
; a hard disk. if it is, we aren't interested in it.
;
TEST DL,080H ; check if this is for hard disk
JZ INT13EXIT ; exit if not
;
; if we are in the process of parking the drives, don't bother
; resetting anything
;
CMP CS:BUSY,1 ; if actively parking the drives,
JE INT13EXIT ; don't reset anything.
;
; reset timer counter and flag the disks as not being parked
;
PUSH CS:TICKS ; this is for hard disk, so reset
POP CS:TIMER ; time counter and indicate that
MOV CS:PARKED,0 ; disks are no longer parked
;
; jump to normal int 13 bios routine to process the interrupt
;
INT13EXIT:
JMP CS:[INT13VECT] ; jump to original vector
INT13 ENDP
;----------------------------------------------------------------------------
;
; this routine is entered approximately 18.2 times per second on
; the heals of a hardware timer interrupt. the purpose of this
; routine is to determine if the drives need to be parked. if so,
; the parking is done before the routine returns.
;
;----------------------------------------------------------------------------
INT08 PROC
;
; the hardware interrupt that got us here disabled interrupts.
; since we aren't doing anything critical, we'll reenable them
; first thing and then go take care of the time-of-day processing.
;
STI ; enable interrupts
PUSHF ; simulate int by pushing flags
CALL CS:[INT08VECT] ; and then calling tod routine
;
; if the drives are already parked, we needn't look any farther
;
CMP CS:PARKED,1 ; check if drives already parked
JE INT08EXIT ; exit if so
;
; if this interrupt caught us in the process of parking the drives,
; exit so we don't totally confuse the issue
;
CMP CS:BUSY,1 ; are we the task being interupted?
JE INT08EXIT ; exit if so
;
; decrement the parking timer and check if it is time to park
; the drives
;
DEC CS:TIMER ; reduce time-to-go-until-park
JG INT08EXIT ; exit if not yet time to park 'em
;
; set busy flag so we don't subsequently re-enter ourself when
; the next timer interrupt comes by (we'll probably be here
; through several of them), and also so we can detect when int13
; activity is coming from us and not from some application task.
;
MOV CS:BUSY,1 ; set flag to indicate we are active
;
; save all of the registers we are going to use
;
PUSH AX ; push registers on the stack
PUSH CX ;
PUSH DX ;
;
; park drive 0 by seeking to the last cylinder on that drive
;
; (full-platter head movement can take a long, long time. this
; operation can take anywhere from a few to hundreds of milli-
; seconds to accomplish.)
;
MOV AX,0C01H ; seek op code, dummy sector count
MOV CX,CS:DRV0CYL ; parking cylinder number
MOV DX,0080H ; set head to 0, drive to 0
INT 013H ; seek to highest cylinder
; (ignore errors)
;
; park drive 1 by seeking to the last cylinder on that drive
;
; (if drive 1 doesn't exist, this will simply return an error
; which we ignore anyway. if this causes problems with some
; bios's, the contents of DRV1CYL could be checked first--a
; zero value means the drive was not detected during our
; initialization)
;
MOV AX,0C01H ; seek op code, dummy sector count
MOV CX,CS:DRV1CYL ; parking cylinder number
MOV DX,0081H ; set head to 0, drive to 1
INT 013H ; seek to highest cylinder
; (ignore errors)
;
; restore the registers we used
;
POP DX ; restore registers from the stack
POP CX ;
POP AX ;
;
; flag that the drives are now parked and we are now longer
; busy doing it
;
MOV CS:PARKED,1 ; flag that drives are parked
MOV CS:BUSY,0 ; clear the we-are-active flag
;
; return to the routine that was originally interrupted by the
; hardware timer going off
;
INT08EXIT:
IRET ; return to interrupted routine
INT08 ENDP
END_RESIDENT LABEL BYTE ; end of resident code
;----------------------------------------------------------------------------
;
; display initialization error messages and exit
;
; these message routines are placed in this rather asthetically
; displeasing location because the references to these routines
; that follow can access them here via relative jumps. at the
; end of the initialization code, where I originally placed these
; routines, non-relative jumps were required which were even more
; offensive.
;
;----------------------------------------------------------------------------
;
; no hard disks were detected
;
INI_DRIVE:
MOV DX,OFFSET NODRIVE ; advise there are no hard disks
JMP SHORT INI_ERROR ; jump to display message and exit
;
; no or invalid parameter was found
;
INI_USAGE:
MOV DX,OFFSET USAGE ; advise what our usage is
;
; display the error message and terminate without doing anything
; further
;
INI_ERROR:
MOV AH,9 ; send message to standard output
INT 021H ;
MOV AX,04C01H ; terminate and return error
INT 021H ; indication
;----------------------------------------------------------------------------
;
; initialization consists of three basic steps:
;
; o process user-provided parameter to determine delay interval
;
; o ascertain the cylinder that is to be used to park each drive
;
; o change interrupt vectors to insert us into the interrupt 13
; and interrupt 08 processing flow
;
;----------------------------------------------------------------------------
ASSUME CS:CODE, DS:CODE, ES:CODE, SS:CODE
INIT PROC
;
; check if a parameter was passed on the command line
;
MOV BX,080H ; set (bx) to psp parameter area
MOV AL,[BX] ; parameter length to (al)
CMP AL,0 ; check if no parameter
JE INI_USAGE ; take error exit if so
;
; ignore any leading blanks or zeros before the parameter
;
INI_LOOP:
INC BX ; increment index and get
MOV AL,[BX] ; next character
CMP AL,' ' ; check if blank
JE INI_LOOP ; loop back if so
CMP AL,'0' ; check if zero
JE INI_LOOP ; loop back if so
;
; we found a non-blank, non-zero character, make sure it is not
; just the terminating carriage return
;
CMP AL,0DH ; check for <cr>
JE INI_USAGE ; exit if so
;
; we found a parameter, so check if it is a digit between 1 and 9
;
CMP AL,'1' ; check if less than ascii 1
JB INI_USAGE ; exit if so
CMP AL,'9' ; check if greater than ascii 9
JA INI_USAGE ; exit if so
;
; we found an acceptable parameter, so multiply it by the number
; of timer ticks in a minute and use it as our timer value (at
; 18.2 ticks per second, there are 1092 of them per minute)
;
MOV TIMSG,AL ; save number in install message
XOR AH,AH ; clear (ah)
SUB AL,'0' ; extract binary number of minutes
MOV DX,1092 ; number of ticks per minute
MUL DX ; compute tick-count for wait loop
MOV TICKS,AX ; save the value and then use it
MOV TIMER,AX ; to initialize the timer
;
; determine if we have any hard disks installed
;
MOV AH,8 ; get disktable parameters
MOV DL,080H ; for drive 0
INT 013H ;
CMP DL,0 ; check number of hard disks
JE INI_DRIVE ; jump if none installed
;
; the disktable parameters contain the maximum 'seekable'
; cylinder number in (ch), with the high-order two bits of
; the cylinder number found in the high two bits of (cl). we
; want to increase this cylinder number by one and use that
; next/last cylinder as our parking spot.
;
INC CH ; increment max cylinder number
JNC INI_0NC ; jump if no carry into high 2 bits
ADD CL,040H ; incr high two bits of cylinder
INI_0NC:
MOV DRV0CYL,CX ; save drive 0 parking cylinder
;
; check if we have a second hard disk
;
CMP DL,1 ; check only one hard disk
JE INI_VECT ; jump if only one
;
; we need to extract the maximum cylinder number for the second
; hard disk in the same way we just did for drive 0
;
MOV AH,8 ; get disktable parameters
MOV DL,081H ; for drive 1
INT 013H ;
INC CH ; increment max cylinder number
JNC INI_1NC ; jump if no carry into high 2 bits
ADD CL,040H ; incr high two bits of cylinder
INI_1NC:
MOV DRV1CYL,CX ; save drive 1 parking cylinder
;
; save current int 13 vector
;
INI_VECT:
MOV AX,03513H ; request int 13 vector
INT 021H ;
MOV INT13SEG,ES ; save it for later use
MOV INT13OFF,BX ;
;
; replace vector with a pointer to our own int 13 routine
;
MOV AX,02513H ; set new int 13 vector
MOV DX,OFFSET INT13 ;
INT 021H ;
;
; save current int 08 vector
;
MOV AX,03508H ; request int 08 vector
INT 021H ;
MOV INT08SEG,ES ; save it for later use
MOV INT08OFF,BX ;
;
; replace vector with a pointer to our own int 08 routine
;
MOV AX,02508H ; set new int 08 vector
MOV DX,OFFSET INT08 ;
INT 021H ;
;
; output message stating we are installed
;
MOV DX,OFFSET INSTALL ; installation message
MOV AH,9 ; send it to standard output
INT 021H ;
;
; release memory allocated to our environment segment
;
MOV ES,CS:[2CH] ; environment memory segment
MOV AH,049H ; give it back to system
INT 021H ;
;
; calculate our resident size rounded-up to the next paragraph
;
MOV DX,OFFSET END_RESIDENT + 15 ; length of resident code
MOV CL,4 ; set (cl) to divide by
SHR DX,CL ; 16 to get para count
;
; terminate and leave our resident routines in place
;
MOV AX,03100H ; tsr op and return code
INT 021H ; terminate
INIT ENDP
INSTALL DB 0DH, 0AH
DB 'Automatic Disk Parking Utility Installed'
DB 0DH, 0AH
DB 'Disk drive head(s) will automatically park after '
TIMSG DB 'x'
DB ' minute(s) of inactivity'
DB 0DH, 0AH, '$'
USAGE DB 'Usage: PDTIMPRK <minutes> (minutes = 1-9)'
DB 7, 0DH, 0AH, '$'
NODRIVE DB 'No hard drives detected'
DB 7, 0DH, 0AH, '$'
AUTHOR DB 'Placed in the public domain by Dick Flanagan, August 1987'
CODE ENDS
END START