home *** CD-ROM | disk | FTP | other *** search
Text File | 1987-06-28 | 60.6 KB | 1,629 lines |
-
-
-
-
-
-
-
-
-
-
-
-
-
- Super File Manager v1.0
-
- PROGRAMMER'S GUIDE
-
-
-
-
- David Steiner
- 2035 J Apt. 6
- Lincoln, NE 68510
- (402)475-0601
-
-
-
-
- Written for:
-
- Capitol PC User Group Inc.
- 1987 Software Programming Contest.
-
-
-
-
- Permission is granted for Capitol PC and other not for
- profit organizations to publish the source and executable
- portions of this program.
-
- >> OVERVIEW <<
-
-
- This documentation file is not a user's manual. It is
- designed to provide helpful information about the
- techniques used in Super File Manager (SFM).
-
- This document is not designed to be a DOS technical
- reference either. I do not claim to be any kind of
- authority on DOS or the other types of system calls used by
- this program. It is entirely possible that information
- presented here is not completely accurate. The only claim
- I can make is that these routines work for me.
-
- Before getting too far into this file I suggest that
- you first take a brief look at the source code for SFM.
- The style used may not be what your used to, but I like
- it. In most cases the comments are kept to a simple
- procedure description in order to avoid clutter.
-
- Several items will be covered in this reference that I
- think you will find useful:
-
- Compiling and running SFM.
- General design of a DOS call routine.
- DOS function $32, non-documented but useful.
- Using absolute disk reads and writes.
- Trapping Turbo errors.
- Designing an interrupt handler.
- Trapping DOS critical errors.
- An overall look at SFM.
- Suggested references.
- Acknowledgments.
-
- This documentation file is being written fairly
- hastily so I will apologize now if it doesn't flow terribly
- well. If I had more time I would have had a friend or two
- help work out the rough spots.
-
-
- Super File Manager - 2 -
- >> COMPILING SFM <<
-
-
- Although the executable code (.COM file) distributed
- with SFM will work on most any system, you will need
- Borland's Turbo Pascal version 3.0 if you wish to customize
- SFM to better suit your needs. This technical
- documentation, however, presents ideas that may be
- generalized to any other language. If you don't have Turbo
- Pascal, but are fairly good at language conversions you
- will also be able to make use of the source code.
-
- It is a requirement stated in the contest rules that I
- must describe all steps necessary to compile and run SFM.
- I apologize to those who find these instructions
- condescending.
-
- To compile SFM you must first make sure that all of
- the include files are present in the current directory.
- These are:
-
- SFM.PAS
- SFMVARS.INC
- SFMOTHER.INC
- SFMSCRN.INC
- SFMDOS.INC
- SFMFUNC.INC
-
- The next thing to do is start Turbo. Assuming this is done
- and that the files above are in the current directory, here
- is the command sequence for compiling to the file SFM.COM:
-
- M ; Main file
- SFM <ENTER> ; file name
- O ; Options menu
- C ; Compile to COM file
- Q ; Quit options menu
- C ; Compile
- Q ; Quit Turbo
- SFM <ENTER> ; Run SFM
-
- The letters shown to the left are typed as shown, the ENTER
- key need only be pressed when explicitly listed. SFM
- should now be up and running, and it is time to refer to
- the User's Guide if you aren't already familiar with SFM.
-
-
- Super File Manager - 3 -
- >> DOS CALLS <<
-
-
- Making a DOS or BIOS call from Turbo Pascal is not all
- that difficult. This section just gives an outline of a
- good method for designing functions that make these calls,
- as well as some specifics about how Turbo handles such
- requests.
-
- For those of you new to system calls a short
- explanation is in order. There exists a vault of functions
- available to you (as a programmer) that is always resident
- in memory. Many of these functions are part of your
- system's hardware (BIOS) as part of the ROM (read-only
- memory). Many more become available when your system loads
- DOS.
-
- In most cases BIOS functions are very low level and
- hard to work with. An example of the more useful BIOS
- functions would be those for controlling the video
- display. They allow you to alter the cursor, change the
- video mode, read a character from the screen... the list is
- a long one.
-
- The DOS functions form something of a buffer between
- you and the BIOS, but they are accessed similarly.
- Naturally, the majority of DOS functions have to do with
- disk access, but there are several other types also. IBM
- suggests that you use the DOS functions whenever possible
- since the BIOS may be different on later models of the PC
- or even PC compatibles.
-
- Calling these functions is accomplished at the
- assembly language level through the use of the INT xx
- (interrupt) instruction, where xx is an interrupt number.
- The BIOS sets aside an area of memory for a table of
- addresses when the system is turned on. Each entry in this
- table is given a number, the interrupt number.
-
- When you issue the INT instruction you are telling the
- system to save its place and basically jump to the address
- associated with that number. After the system routine has
- finished it returns to where the program left off.
-
- Before making such a call you must initialize the
- registers so the interrupt will know exactly what to do and
- how to find the input it requires. If you don't know what
- a register is you should stop here. Find an introductory
- text on assembly language and spend enough time with it to
- get comfortable with the concept of registers before
- continuing.
-
-
-
- Super File Manager - 4 -
- DOS CALLS : continued
-
- How to initialize these registers varies greatly from
- one function to the next, and a comprehensive list is
- beyond the scope of this document. Please see the
- references section below if you are looking for a good
- place to get this information.
-
- Now we will proceed with how to use this information
- from Turbo Pascal. The Turbo procedures that we need to
- know are the following two:
-
- INTR( Interrupt : integer; var Regs : reg_T);
-
- MSDOS( var Regs : reg_T);
-
- They both require an input of type REG_T. Here is the best
- way I know of to define this record:
-
- reg_T = record case boolean of
- true : (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags
- : integer);
- false : (AL,AH,BL,BH,CL,CH,DL,CH : byte);
- end;
-
- This is known as a variant record and allows us to use the
- registers as full or half registers, just like we would
- from assembly language.
-
- The MSDOS procedure is really just a slight variation
- of INTR. Most DOS functions are accessed through interrupt
- number $21 and that is why there is a separate procedure
- for them. I suspect that the MSDOS procedure really looks
- something like this:
-
- procedure MSDOS( Regs : reg_T );
- begin
- Intr( $21, Regs );
- end;
-
- Since this relationship has been pointed out I will only
- talk about the INTR procedure for now. Although redundant,
- the MSDOS procedure does help your code's clarity and
- should be used when appropriate.
-
- INTR acts in the following manner. First it saves the
- current registers and then puts the values you specified in
- the REGS record into the registers. It then issues an INT
- instruction for the interrupt you specified. Upon return
- Turbo puts the new register values into REGS and restores
- the values it saved to the actual registers.
-
- After a call to INTR the REGS record will contain the
- values you would expect from the system call. I think that
- it's time for an example.
-
-
- Super File Manager - 5 -
- DOS CALLS : continued
-
-
- An example of a BIOS call:
-
- The procedure below will ask BIOS what the current
- video mode is. This example shows what is known as a
- subfunction. We will be calling interrupt $10, named
- the BIOS Video I/O interrupt.
-
- This interrupt performs all kinds of video functions,
- and we may tell it which one we want by specifying a
- subfunction. The one we want is number $0F, called
- Get the Current Video Mode. The standard for
- requesting a subfunction is to be put the function's
- number into register AH.
-
- The only input is loading the subfunction number into
- Regs.AH.
-
- Output from this interrupt is contained in the the AX
- and BX registers as follows:
-
- AL = video mode number
- $00 = black & white 40 columns X 25 rows
- $01 = color 40 X 25
- $02 = b&w 80 X 25
- $03 = color 80 X 25
- $07 = monochrome text, 80 X 25
- $04-$06,
- $08-$0F = different graphics modes
-
- AH = Number of columns
-
- BH = Active video page (0 if graphics mode)
-
-
- procedure GetMode(var ModeNum : integer );
- var
- Regs : reg_T;
- begin
- with Regs do
- begin
- AH := $0F;
- Intr( $10, Regs );
- ModeNum := AL;
- end;
- end;
-
-
- Since this procedure is just for getting the mode
- number we don't bother saving the other register
- values.
-
-
- Super File Manager - 6 -
- DOS CALLS : continued
-
-
- A side note: Once you start playing around with interrupts
- you may discover what lies behind some of Turbo's
- procedures and functions. For example Turbo's TEXTMODE
- function apparently calls BIOS interrupt $10, subfunction
- $00 - Set Video Mode. The Turbo constants used
- (bw40,bw80,c40,c80) for this call correspond to the values
- returned in the above GetMode example. This implies that
- you could call TEXTMODE with some other number and actually
- set a graphics mode (e.g. $04 for medium-resolution CGA).
- I must admit that I haven't tried it yet, but it seems
- highly likely.
-
- If you have looked at the SFM source code you may have
- noted that whenever I used the INTR or MSDOS procedures the
- function call was named in a comment to the side. This is
- a good habit that you should adopt. I would have done it
- in the above example but these lines aren't wide enough to
- allow such comments.
-
- For you folks already familiar with using these Turbo
- procedures we're finally getting to something you can use.
- Now we get into those functions that may not always be
- successful. The most common type of functions where this
- can happen are those accessing a disk, the domain of DOS.
-
- When using the MSDOS procedure (or INTR with $21) many
- of the functions will not always work. DOS uses several
- methods for letting us know something went wrong.
-
- Much of the time the carry flag will be set if there
- was a problem executing one of the DOS functions. You can
- check this bit (bit 0 of the Flags register) by ANDing the
- flags register with $01 and comparing this to zero:
-
- if ( (Flags AND $01) <> 0 ) then ERROR else OK;
-
- If there was an error the registers will not contain the
- information you were expecting. Instead DOS will have
- placed an error code in the AX register. This error code
- will correspond to a message explaining what went wrong.
- Hopefully the reference you chose for DOS function calls
- has a list of these messages.
-
- Another method often used is setting the AL register
- equal to $FF if there was an error. I'm just trying to
- give you the general idea here, you will have to check how
- errors are handled for each function that you use.
-
-
-
- Super File Manager - 7 -
- DOS CALLS : continued
-
-
- Well here is the main idea I want to get across for
- this section. When you design a routine to make a system
- call that may not always be successful you should make it a
- Turbo function. This way the function result can pass back
- the error code or at least a boolean value for whether or
- not it was successful.
-
- Then any procedure that calls your function can decide
- what to do with an error on its own. You may have it abort
- the program, ignore the error, or (more likely) print an
- error message and allow the program to continue.
-
- When SFM uses this technique it is sometimes two or
- three levels deep in function calls, but the error code is
- just passed back on through until it gets to a routine that
- is designed for handling output. Then the error message is
- finally printed out. A pretty good example of this is in
- the ChangePath procedure found at the top of SFMFUNC.INC.
-
- Another sample function:
-
- We'll write an example that tries to rename
- "\SFMTECH.TXT" to "\TRASH\BULL.XXX". We need to use
- the MSDOS procedure, subfunction $56 - Rename a File.
-
- You may have noticed that the new name contains a
- different path. We can do that with this DOS
- function. If you look into the SFM source code you
- will see that the move command uses this DOS function.
-
- If you're just learning this system call stuff you're
- probably wondering how we're going to fit those
- strings into the registers. The answer is - we don't.
- All DOS expects is the addresses of these two strings,
- loaded into the DS:DX and ES:DI register pairs
- respectively.
-
- To set these addresses you need to know a little
- about two more Turbo procedures: OFS and SEG. SEG
- returns the "high" portion of the address of a
- variable, or the segment. OFS returns the "low"
- portion, or offset within that segment. That's not a
- very complete description, but it gives you the
- general idea.
-
- There is one more thing you must note when passing
- strings to DOS functions. DOS doesn't recognize
- Turbo's string structure. It expects the first
- character at the address to be the start of the file
- name and that the string be terminated by a NUL ($00)
- character. This is known as an ASCIIZ string.
-
-
- Super File Manager - 8 -
- RENAME EXAMPLE : continued
-
-
- The error codes returned from DOS calls like this
- often have their own small set of error messages.
- Those codes returned by this call (in AX) are:
-
- $02 : File not found
- $03 : Path not found
- $05 : Access denied
- $11 : Tried to rename to a different drive
-
-
- function RenameStuff : integer;
- var
- Regs : reg_T;
- oldstr, newstr : string[80];
- begin
- oldstr := '\SFMTECH.TXT' + #00;
- newstr := '\TRASH\BULL.XXX' + #00;
- with Regs do
- begin
- AH := $56;
- DS := seg( oldstr[1] );
- DX := ofs( oldstr[1] );
- ES := seg( newstr[1] );
- DI := ofs( newstr[1] );
- MsDos( Regs );
- if (Flags AND $01) <> 0 then
- RenameStuff := AX
- else
- RenameStuff := 0;
- end;
- end;
-
-
- Note that the OFS procedure calls are asking for the
- offset to the first character in the string
- (xstr[1]). This index is not necessary for getting
- the segment portion of the address, but is done anyway
- for consistency.
-
- Well that's about it for what I had to say about DOS
- calls. We'll discuss them again a bit later in the section
- for trapping serious DOS errors.
-
-
-
- Super File Manager - 9 -
- >> DOS FUNCTION $32 <<
-
-
- If you already have a reference for MS-DOS calls
- you've probably noticed that there are several subfunction
- numbers that are "reserved for DOS." Ever wonder what
- mystical operations those functions performed? Well we are
- about to unravel one of those mysteries...
-
- Glenn Roberts authored an excellent article in PC Tech
- Journal that covers the $32 DOS function. I must admit
- that SFM would be crippled, slower, and less reliable
- without it. This function request will return an address
- to an immensely valuable table of diskette parameters.
-
- Before going into the actual description, I want to
- make sure you know that this is a NON-DOCUMENTED function.
- This means that it may or may not remain in future versions
- of DOS, MicroSoft doesn't guarantee anything. From the
- information in Roberts' article it is valid for DOS
- versions 2.0 through 3.1 and I can verify that it is still
- there in version 3.2.
-
-
- $32 - Get Device Parameter Table
-
-
- Input:
- AH = $32 for requesting the subfunction
- DL = The drive you want the table for
- (A=1, B=2,...)
-
- Output:
- DS = Segment of parameter table
- BX = Offset of table
-
- Error:
- AL = $FF if the drive was invalid
-
-
-
-
- Super File Manager - 10 -
- FUNCTION $32 : continued
-
-
- Table Contents:
-
- Byte(s) Contents
- ------- ---------------------
- 0 Assigned disk (A=0, B=1,...)
- 1 Same as above, except 0 for RAM disk
- 2-3 Bytes per cluster
- 4 Sectors per cluster minus 1
- 5 Number of heads minus 1
- 6-7 Reserved sectors (bootstrap)
- 8 Number of FAT copies
- 9-10 Maximum number of root directory entries
- 11-12 First usable sector
- 13-14 Total cluster count plus 1
- 15 Sectors per FAT
- 16-17 First root directory sector
- 18-21 Device driver address
- 22-23 Media descriptor
- 24-27 Chain to next disk table
- 28-29 *Cluster of current working directory
- 30-93 *64-byte current working directory
-
- * = valid only for DOS 2.x
-
- If you plan on implementing this table in a Turbo program
- there is a record defined for it in the SFMVARS.INC file.
-
- Those of you paying attention may have noticed the
- above inconsistency for numbering the drives. DOS is
- pretty poor on this count. Notice that when you call
- function $32 you ask for a drive's table according to A=1,
- B=2... When information is returned in the table the drives
- are numbered A=0, B=1... Keep this in mind when using DOS
- calls and be especially careful about specifying which
- drive you want to write to.
-
- When using this table remember that you are looking
- directly into the table that DOS uses. Changing values in
- this table may affect the way DOS accesses the disk, which
- would not be good if it happened to want to do a write
- operation.
-
- The only reason I can think of for wanting to change
- these parameters would be during a format function. SFM
- has no such function, but I did have one started before it
- became too difficult to implement properly. I found that
- in some cases this table had to be altered in order to
- format diskette tracks. I don't feel qualified to give any
- further discussion on formatting (I had a good start
- though, until I tried formatting one of the AT's high
- density drives at work).
-
- Super File Manager - 11 -
- >> ABSOLUTE DISK ACCESSES <<
-
-
- Now I shall go into the Turbo INLINE command a bit.
- It seems that the best way to read from or write directly
- to diskette sectors would be to use the corresponding DOS
- interrupts. There is something of a problem here though.
- For some unknown reason the DOS interrupts $25, Absolute
- Disk Read and $26, Absolute Disk Write both leave a copy of
- the Flags register on the stack after returning from the
- interrupt.
-
- DOS apparently does a no-no. It seems to use the
- wrong type of return from an interrupt handler. Something
- I didn't mention above is exactly how the INT operation
- works. Well, it puts a copy of the Flags register on the
- stack, followed by two words for the return address.
- Whenever an interrupt handler is designed there is supposed
- to be an IRET instruction for returning to the calling
- program. The IRET, of course, loads the return address and
- then restores the Flags register before making the jump
- back.
-
- DOS, however, seems to use the normal RET instruction
- to return from interrupts $25 and $26. This means that the
- Flags don't get popped unless the calling procedure does so
- itself after the INT call.
-
- From the assembly language level this is just an
- annoying little quirk. In Turbo Pascal the quirk develops
- into one of those things people buy Preparation H to
- remedy. The INTR procedure is not designed to handle these
- special cases and those Flags left sitting on the stack
- wreak havoc with the system, often causing a lockup.
-
- The only ways to fix this problem from within Turbo is
- to write an external subroutine or resort to machine
- language using the INLINE command. I prefer INLINE code
- since it doesn't require that you keep track of a separate
- binary file. This command is used in the following manner:
-
- Inline( $xx/$xx ... /$xx );
-
- That's right, I didn't mistype. You must enter the actual
- machine codes (hex numbers) for Turbo to accept INLINE
- input. The only break Turbo offers you is that you can use
- variable names when you need to, so you don't have to know
- their exact address (which isn't possible anyway).
-
- Fortunately there are alternatives to being a machine
- code wizard. There are programs available that take small
- files of pseudo-assembly code and do all of the machine
- code conversions for you. The one I use is listed below in
- the references section.
-
-
- Super File Manager - 12 -
- ABSOLUTE DISK ACCESS : continued
-
- There are still minor drawbacks even with these
- utilities. Because such programs must interface with Turbo
- they often use a variation of assembly language. This
- means learning a new dialect even if you are already
- familiar with assembly. On the brighter side, alterations
- aren't too great and don't take long to master.
-
- An example is not presented here since both interrupts
- that have this problem are already written as part of SFM.
- They are two separate procedures and can be found in the
- SFMDOS.INC file. Their names are LoadSectors and
- WriteSectors.
-
- You may also find examples of INLINE code below when
- we discuss interrupt handlers.
-
-
- Super File Manager - 13 -
- >> TRAPPING TURBO ERRORS <<
-
-
- The method used to trap Turbo run-time errors is
- really quite simple. There is a Turbo variable named
- ERRORPTR that you can change to point to a procedure of
- your own. If your error routine is called AbortOnError you
- can issue this statement
-
- ErrorPtr := ofs( AbortOnError );
-
- near the beginning of your program and when an error occurs
- Turbo jumps there instead of jumping to its normal error
- handler, expecting you to handle the error and then exit
- the program. You won't find this in the Turbo manual,
- instead it was put into a READ.ME file on one of the
- compiler diskettes. I hope I never meet any of the
- manual's authors since I couldn't afford the lawsuit I'd
- get after punching them in the nose.
-
- The restrictions placed on the error handling
- procedure are that it must have two integers as its input
- and that it contain a HALT command since the program must
- be terminated. The following procedure layout is correct:
-
- procedure AbortOnError( ErrNum, Addr : integer );
- begin
- { Your code goes here }
- halt;
- end;
-
- Within this procedure you can do whatever you wish, except
- "end" it normally. You can even call another procedure.
- The input parameters contain the following:
-
- Hi( ErrNum ) = The error type
- $00 = user break
- $01 = I/O error
- $02 = run-time error
- Lo( ErrNum ) = The error code
- Addr = Where the program was when
- the error occurred.
-
- SFM contains just such an error routine. While it
- isn't likely that it will be called, it is still there just
- in case. The procedure is found towards the top of the
- SFMSCRN.INC file (named AbortOnError).
-
- The Addr value may be converted to hex and entered in
- the Find run-time error option from the Turbo Editor. This
- will allow Turbo to place you where the error occurred in
- relation to the source code, and you can take it from
- there.
-
-
- Super File Manager - 14 -
- TRAPPING TURBO ERRORS : continued
-
-
- Note that the main reason that SFM has its own error
- handler is because it must perform several operations to
- return the system to normal before exiting. You see, SFM
- uses custom interrupt handlers and these interrupts must be
- restored to normal or DOS will have a fit. The next
- section will describe these interrupt handlers.
-
- As a final note there is one special situation you may
- not have considered yet. If the program is terminated by a
- heap/stack collision (error $FF) you will not be able to
- call any subprograms from within your error handler. To
- alleviate this problem you can provide your program with a
- means of freeing up some heap space.
-
- First you must declare a variable that can contain a
- pointer address. The one used by SFM is:
-
- HeapStart : ^integer;
-
- It doesn't matter much what type of variable HeapStart
- points to, but a pointer to an integer is a common method.
- Near the beginning of your program you simply issue a MARK
- procedure call:
-
- Mark( HeapStart );
-
- Now HeapStart points at a memory location on the heap.
- Later in the AbortOnError procedure you just issue a
- RELEASE call:
-
- Release( HeapStart );
-
- Once this is done there will be some free space in memory
- again and you can call other procedures if you like. You
- must remember that the call to RELEASE will clear out
- everything put on the heap after the last MARK call. This
- means that any dynamic variables you were using are now
- gone.
-
- In case you were wondering how the program's heap and
- stack are related, here is an explanation. The heap and
- stack are both dynamic in nature. This means that the
- amount of memory used by either can grow or diminish during
- the program execution. It then makes sense that they share
- the same area of memory.
-
-
-
- Super File Manager - 15 -
- TRAPPING TURBO ERRORS : continued
-
-
- Let's take a look at where Turbo puts a program in
- memory when it is loaded. First Turbo loads in the
- program's code segment, containing all of the procedures
- and typed constants defined. Then it sets up the program's
- data segment, which consists of the global variables. All
- of the memory left over becomes a playground for the stack
- and heap.
-
- Now let's look closer at the heap/stack space. Turbo
- decides that the heap should start right after the the data
- segment. Whenever more space is required for the heap
- Turbo designates another chunk of memory to it (NEW or
- GETMEM procedure calls). The stack, on the other hand,
- starts at the outer limit of the program's memory.
- Whenever it needs more memory Turbo takes another chunk
- from that end (procedure calls and their local variables).
-
- When the program is finished with memory on the heap
- or stack it can be returned to the system. This is
- accomplished through DISPOSE or RELEASE procedures for the
- heap space. All that is required to restore stack space is
- the return from the procedure that allocated the memory.
-
- Turbo keeps track of how far out the heap and stack
- are extended by setting a pointer to their outermost memory
- location. Should these pointers ever cross you will
- receive a heap/stack collision error message ($FF). This
- means that the heap and stack were trying to use the same
- memory area for their data at the same time.
-
- One likely cause for such a collision would be to ask
- Turbo to give you a large chunk of memory using a GETMEM
- call. This is the type of error possible with SFM.
- Another, not so obvious cause, is when a procedure or
- function sets up a large array in its local variable
- declarations. A third example would be a recursive routine
- that calls itself many times. Since Turbo must save quite
- a bit of information each time a procedure calls itself you
- may eventually run out of memory.
-
-
- Super File Manager - 16 -
- >> INTERRUPT HANDLERS <<
-
-
- Here is another use for Inline code. You can design
- your own interrupt handlers. This is how SFM traps all of
- the DOS critical errors and avoids the normal DOS message
- "Abort, Retry, or Ignore?" Writing such a routine is no
- small task, but I will give you a general outline.
-
- The INT24 function in the SFMOTHER.INC file is such a
- routine and INT10 from SFMVARS.INC is another example. The
- first routine takes over DOS interrupt $24, the Critical
- Error Handler Address. The second takes over BIOS
- interrupt $10, the Video I/O interrupt.
-
- The INT24 routines were developed by Bela Lubkin and
- published in an article in Programmer's Journal.
-
- The first thing to look at when designing an interrupt
- handler is how the INT and IRET assembly instructions
- work. These are covered well enough above in the absolute
- disk read/write section.
-
- Next we must take Turbo into account. Turbo issues
- the following few lines of code at the beginning of every
- procedure or function:
-
- PUSH BP
- MOV BP, SP
- PUSH SP
-
- These instructions set the BP register to a location that
- can be used to index the variables in the subroutine's
- declaration. Your major concern with these lines should be
- how to put things back in order before returning from your
- interrupt handler:
-
- MOV SP, BP ; code to exit
- POP BP
- IRET
-
- This exit code must be inserted at the end of your
- interrupt handler to successfully return to the calling
- program. You can't let your interrupt routine exit
- normally since the IRET instruction must be used. An
- exception to this rule would be DOS interrupts $25 and $26
- for reasons explained in that section.
-
-
-
- Super File Manager - 17 -
- INTERRUPT HANDLERS : continued
-
- If you were wondering why there are two PUSH
- instructions inserted by Turbo but we only POP once I have
- the answer, though it did have me confused for a bit.
- Loading the SP register into BP effectively saves stack
- pointer right after the PUSH BP instruction. The line
- "MOV SP, BP" restores this value and has the same effect
- as popping SP (and any local variables that were declared)
- from the stack. This means that you must not change the BP
- register, unless you take special precautions.
-
- In addition to this exit code you must save and
- restore all registers that are used by your routines.
- Exceptions to this are those registers that are used for
- output from the interrupt. Before designing an interrupt
- handler you should know exactly which registers these are
- and what their output is supposed to look like.
-
- That covers the basics of the interrupt handling
- routine, now we must examine what is required to make the
- system use that code. SFM's procedures for doing this are
- called INT24ON and INT10ON for the indicated interrupt
- handlers.
-
- Recalling that the BIOS sets up a table in memory for
- the addresses of the interrupt handlers (a vector table),
- it seems obvious that all we need to do is change the
- vector that corresponds to our interrupt. Rather than
- attempting to locate and change this entry ourselves we
- will let DOS do the dirty work for us.
-
- DOS has a pair of subfunctions that will either get or
- set these vectors for us. The function numbers are $35 for
- getting the address and $25 for setting it. The input for
- this function call is:
-
- AH = $35 or $25 as needed
- AL = interrupt to get/set
- DS:DX = new address when setting
-
- output:
- ES:BX = current address when getting
-
- Notice that the ES:BX register pair will contain the
- address that the vector currently points to after function
- $35 is called. There is no output for the $25 function,
- but the vector will have been changed (even if the address
- points to an invalid memory location).
-
-
-
- Super File Manager - 18 -
- INTERRUPT HANDLERS : continued
-
- Before changing the interrupt handler you should save
- the old address. This is done by setting aside a four byte
- space to contain the old vector. That way the interrupt
- may be restored before the program exits.
-
- An example:
-
- This interrupt handler will prevent the video screen
- from being printed while the program is running. It
- is being presented as an entire program so you can see
- everything that is required.
-
-
- program NoPrint;
-
- type
- Reg_T = record case boolean of
- true : (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags : integer);
- false : (AL,AH,BL,BH,CL,CH,DL,DH : byte);
- end;
-
- const
- DataSeg : integer = 0;
- OldInt05 : array[0..1] of integer = (0,0);
-
- procedure Int05;
- begin
- {
- PUSH BP ; Done by Turbo
- MOV BP, SP
- PUSH SP
- }
- Inline(
- $50 { PUSH AX ; Save all regs }
- /$53 { PUSH BX ; so we can use }
- /$51 { PUSH CX ; Turbo code }
- /$52 { PUSH DX }
- /$57 { PUSH DI }
- /$56 { PUSH SI }
- /$06 { PUSH ES }
- /$1E { PUSH DS }
- { ; }
- { Set DS so we can use global }
- { variables and the Turbo }
- { write procedures.} }
- { ; }
- /$2E/$A1/>DATASEG {CS: MOV AX, [>DataSeg] }
- /$8E/$D8 { MOV DS, AX }
- );
-
- writeln( 'Sorry, can''t print the screen right now.' );
- write( #7 );
-
-
- Super File Manager - 19 -
- NOPRINT : continued
-
- InLine(
- $1F { POP DS ; Restore all regs}
- /$07 { POP ES }
- /$5E { POP SI }
- /$5F { POP DI }
- /$5A { POP DX }
- /$59 { POP CX }
- /$5B { POP BX }
- /$58 { POP AX }
- { ; }
- /$89/$EC { MOV SP, BP ; Exit code }
- /$5D { POP BP }
- /$CF { IRET }
- );
- end;
-
- procedure Int05ON;
- var
- Regs : reg_T;
- begin
- DataSeg := Dseg;
- with Regs do
- begin
- AH := $35; { DOS function $35 - Get Interrupt Vector Address }
- AL := $05; { getting $05 - Print Screen }
- MsDos( Regs );
- OldInt05[1] := ES;
- OldInt05[0] := BX;
- AH := $25; { DOS function $25 - Set Interrupt Vector Address }
- AL := $05; { setting $05 - Print Screen }
- DS := Cseg;
- DX := ofs( Int05 );
- MsDos( Regs );
- end;
- end;
-
- procedure Int05OFF;
- var
- Regs : reg_T;
- begin
- with Regs do
- begin
- AH := $25; { DOS function $25 - Set Interrupt Vector Address }
- AL := $05; { setting $05 - Print Screen }
- DS := OldInt05[1];
- DX := OldInt05[0];
- MsDos( Regs );
- end;
- end;
-
-
-
- Super File Manager - 20 -
- NOPRINT : continued
-
- var
- ch : char;
-
- begin
- DataSeg := Dseg;
- Int05ON;
- writeln( 'Hit the SPACE BAR to exit.' );
- writeln;
- repeat
- read( kbd, ch );
- until ch = #32;
- Int05OFF;
- end.
-
-
- As you can see there are three main parts to setting up an
- interrupt handler: the actual handler code, the procedure
- to save the old vector and set yours, and finally the
- procedure to restore the old vector. In addition to this
- you must set aside a bit of space to store the old vector
- and perhaps the data segment also.
-
- In this example the data segment was saved in order to
- allow us to use the Turbo write procedures. I did some
- checking with DEBUG and it seems that Turbo keeps some
- information used by the write procedure in the program's
- data segment. Restoring the DS register also allows your
- program to access the global variables.
-
- Even if you don't fully understand what I have
- presented here you can still used the above example as a
- template to create your own interrupt handlers. All you
- need to do is change all occurrences of "05" into whatever
- interrupt you wish to customize.
-
- Now I must clarify that last statement a bit. You
- must be careful what interrupt vectors you are changing.
- The majority of interrupt routines used by the BIOS or DOS
- have several subfunctions. If you take control of such a
- vector you will either have to emulate all of its functions
- or be selective about which functions you are controlling.
-
- The INT10 procedure found in SFMVARS.INC is one of
- these cases. Emulating all of the subfunctions of the BIOS
- Video Interrupt would take an incredible amount of code and
- essentially be wasted space. For this reason I chose to
- compare the value in the AH register and let the BIOS have
- all function requests except the one that I wanted to
- alter, the $0E function (Write Character as a Teletype).
-
-
- Super File Manager - 21 -
- INTERRUPT HANDLERS : continued
-
- The transfer of control to the BIOS is accomplished by
- making a FAR CALL to the old vector address we have taken
- the precaution of saving. INT10 then issues an IRET right
- away in order to return to the calling procedure.
-
- I won't cover this in any more detail since the
- example can be found in the SFM code if you wish to pursue
- the matter.
-
- Super File Manager - 22 -
- >> TRAPPING DOS CRITICAL ERRORS <<
-
-
- Now that you have some idea of how to design an
- interrupt handler we can continue with a practical use.
- The INT24 interrupt handler mentioned above has become one
- of my most useful procedures and an addition to any program
- of conseqence.
-
- The DOS interrupt $24 is the one that prints that
- nasty "Abort, Retry, Ignore?" message that has blemished so
- many otherwise well designed display screens. By taking
- control of this vector we may stop DOS from printing a
- message and print our own error message at our leisure.
-
- To make use of this routine from Turbo we must first
- take note of a special compiler directive. This is the "I"
- compiler directive used for turning Turbo's DOS error
- checking on, {$I+}, or off, {$I-}. The default is on,
- which allows DOS to print the error message mentioned
- above.
-
- When we turn this error checking off we assume
- responsibility for DOS errors. If we don't do something
- about the error before the next DOS function call the
- program will abort whether or not Turbo was performing
- error checking. This means that even if we intend to
- ignore the error we must at least check the error code
- using INT24RESULT (this also clears the error code).
-
- Using this error checking with the standard Turbo
- function IORESULT will catch most minor errors. Such
- errors include messages like "File does not exist" or "File
- not Open." It does not allow for more critical errors such
- as an unformatted disk or an open diskette drive.
-
- To use the IORESULT function you simply turn off the
- Turbo error checking before an operation that accesses the
- disk and then turn it back on right away. Then you may
- access the IORESULT function to get an error code. If this
- code is zero then no error occurred. Here is a small
- example for changing the current directory:
-
- {$I-}
- chdir( newpath );
- {$I+}
- ErrCode := IOresult;
-
- In this example NEWPATH is a string variable that contains
- the path we are attempting to change to. ERRCODE is an
- integer variable that will hold the code returned by
- IORESULT. This example could become the body of a DOS call
- function that follows the outline given in the first
- section of this documentation.
-
-
- Super File Manager - 23 -
- DOS CRITICAL ERRORS : continued
-
- Now we may extend this error checking to include
- errors that are considered critical to DOS. If you
- implement the INT24 set of procedures you may use
- INT24RESULT in the same manner as you would normally use
- IORESULT. The difference is that the new function result
- will actually contain two error codes. There is one placed
- in both the high and low bytes of the integer result. To
- see how to extract these two codes take a look into the
- ErrorMessage routine found in the SFMOTHER.INC file.
-
- Keeping this in mind we can now take the final step in
- designing a break-proof DOS call function that returns a
- comprehensive error code. I think an example is the best
- way to illustrate this. To avoid setting up an entirely
- new example we will just enhance the renaming function from
- the DOS calls section above:
-
-
- function RenameStuff : integer;
- var
- Regs : reg_T;
- ErrCode : integer;
- oldstr, newstr : string[80];
- begin
- oldstr := '\SFMTECH.TXT' + #00;
- newstr := '\TRASH\BULL.XXX' + #00;
- with Regs do
- begin
- AH := $56;
- DS := seg( oldstr[1] );
- DX := ofs( oldstr[1] );
- ES := seg( newstr[1] );
- DI := ofs( newstr[1] );
- {$I-}
- MsDos( Regs );
- {$I+}
- ErrCode := Int24result;
- if ErrCode <> 0 then
- RenameStuff := ErrCode
- else
- begin
- if (Flags AND $01) <> 0 then
- RenameStuff := (AX SHL 8) OR $8000
- else
- RenameStuff := 0;
- end;
- end;
- end;
-
-
-
-
- Super File Manager - 24 -
- Looking at this code may be a bit confusing, but it
- covers all three levels of DOS error messages. The first
- two are taken care of by the INT24RESULT function which
- combines both Turbo and DOS error messages. The third type
- of error is that mentioned in the DOS calls section. This
- error code has been altered a bit in order to create an
- integer function result that can be sent to the
- ErrorMessage procedure.
-
- The alteration is this: error codes that are returned
- by the function call itself are shifted left so the occupy
- the high byte along with the DOS critical error codes.
- Then the high bit is set to let ErrorMessage know that this
- error code is one returned from a DOS function call rather
- than the DOS critical error handler.
-
- Again, even if you don't fully understand this process
- you can still take the procedures and place them into your
- own program. The only place you will need to make changes
- is in ErrorMessage, in order to make it fit your I/O
- routines and also to restore the error messages that I
- commented out. You may then use INT24RESULT just as you
- would use the standard Turbo IORESULT.
-
-
- Super File Manager - 25 -
- >> SFM OVERVIEW <<
-
-
- Now that we have covered those techniques that I
- thought needed more background, we can cover Super File
- Manager's structure in specific.
-
- The first thing to note about SFM will be how it is
- broken down into include files:
-
- SFM.PAS Naturally this is the main file. It
- contains the initialization routines
- for setting up SFM. It also contains
- the routines for the first level of
- I/O, the command menus.
-
- sfmVARS.inc This file contains all of the types
- that are used by SFM as well as the
- majority of global variables and
- constants. Also included are a few
- routines that needed to be near the
- beginning, but didn't belong in the
- sfmOTHER.inc file.
-
- sfmOTHER.inc Here we have the two sets of routines
- that I borrowed from other sources.
- These are DISPLAY and the INT24
- routines.
-
- sfmSCRN.inc Here is the file for the majority of
- low-level I/O routines and help
- displays. These include everything
- from setting the video mode on up to a
- custom string input function.
-
- sfmDOS.inc This file contains the low-level DOS
- function calls that generally don't
- perform much screen I/O. Most of these
- functions return error codes as
- described in previous sections.
-
- sfmFUNC.inc This last include file contains the
- routines that merge the SCRN, DOS and
- SFM files. In other words, they are
- called from SFM.PAS and use the
- routines in sfmSCRN.inc to set up calls
- to the routines in sfmDOS.inc (which do
- all the actual work).
-
- Of course there are procedures in each file that may belong
- somewhere else, but in some cases this is not possible.
-
- Super File Manager - 26 -
- SFM : continued
-
- Actually, that breakdown gives a pretty good feel for
- how control is passed around within SFM. We always start
- in SFM.PAS and the selection of a command usually sends us
- off to sfmFUNC.inc. From there things jump around
- according to the function.
-
- As mentioned above, nearly all error checking is
- returned through function results and then the codes are
- sent off to the ErrorMessage routine. Exceptions to this
- are warning messages or those errors specific to SFM (such
- as "Windows must have different paths").
-
- Within the sfmSCRN.inc file there are two error
- routines that bear special explanations. These are the
- AbortProgram and AbortOnError routines. Since we are using
- custom interrupt handlers these must be turned off before
- the program is exited, including if SFM happens to be
- halted by an error.
-
- For this reason all exits of the program are routed
- through AbortProgram, with the exception of normal
- termination. This way it is less likely that we will
- forget to turn off an interrupt handler or to turn the
- cursor back on. An example of an error SFM does not handle
- is the case of a bad file allocation table. Rather than
- take the chance of making things worse SFM will quit.
-
- The AbortOnError routine sets up a special error
- message that takes the place of the normal Turbo
- termination message. It displays the same information in a
- slightly different format and also gives us the chance to
- call AbortProgram in order to return the system to normal
- as explained above.
-
- If you want to see AbortOnError in action just go into
- the SFM source code and change the line containing "{$C-}"
- to "{$C+}". Then run SFM normally and type Ctrl-Break.
- You will then see the AbortOnError message screen.
-
- The address displayed here can then be entered in the
- Turbo "Find run-time error" option from the editor. This
- allows you to find out where SFM was when you pressed the
- Ctrl-Break key sequence.
-
-
-
- Super File Manager - 27 -
- COMPILER DIRECTIVES USED BY SFM
-
-
- Super File Manager uses a few of the Turbo compiler
- directives. Setting the "C-" option is done simply to
- speed up screen displays and allow the use of the keyboard
- buffer. The "I-" and "I+" compiler directives are
- described above under the DOS critical errors section.
-
- A less known compiler directive is "K-". This
- directive tells Turbo to stop checking for a heap/stack
- collision whenever a procedure or function is called.
- While this is not always a good idea, it is done here
- because it saves SFM about five kilobytes of code space.
- We only take back about 500 bytes of this when we perform
- our own stack checking where it is needed.
-
- SFM performs stack checking mostly by making a call to
- MemoryAvail to find the amount of free space before issuing
- a GETMEM call. The function MemoryAvail makes a call to
- the standard Turbo function MaxAvail and then uses this
- value to calculate the number of free bytes. Before
- returning this number, SFM subtracts the number of bytes
- specified by the constant MinStack in order to provide a
- safety margin for procedure calls.
-
-
- THE COPY ROUTINES
-
-
- The most frequently used procedure in SFM has to be
- CopyEntries. This is the routine that performs the actual
- copy operations. Therefore it deserves a bit of special
- attention.
-
- The first item of interest here is the BUFFER array.
- This array is made up of a small record that contains
- information about the file being copied. The address field
- is a pointer to the start of the buffer. The next field
- contains the index in the source directory that the buffer
- belongs to. The size is simply how big this buffer is, as
- an unsigned integer. The last field tells whether or not
- the file was too large to fit into one buffer (files over
- 64K) and that there is more to follow.
-
- Using this array of BUFFER records we may now begin a
- copy operation. The first action is to open the file to
- read from and then allocate enough memory for that file.
- The OPENFILE procedure uses the newer DOS method for files,
- known as file handles.
-
-
- Super File Manager - 28 -
- COPYING FILES : continued
-
- If the file is too large for one buffer, or we run out
- of heap space, the MORE flag is set. This is repeated
- until we run out of memory, files, or buffers. Note that
- if we stop reading from a file before all of it is loaded
- the RHANDLE (read-handle) is not reset to zero. This is
- our method for keeping track of whether or not part of a
- file has already been copied when we start the next read
- pass. If a file is completely loaded then we call
- CLOSEFILE with RHANDLE, which also sets it to zero.
-
- After we have read in as much as possible we must then
- start writing back to the destination path. We keep track
- of whether or not part of the file was already written by
- using the same method as with the read-handle. If WHANDLE
- is set to zero then we must use CREATEFILE to make the new
- file. Note that this function will fail if the file name
- already exists with the read-only attribute set, otherwise
- it will overwrite an existing file of the same name. If
- there is an error we allow the user the option of
- continuing with the next marked file or trying again.
-
- If the disk becomes full there are two possibilities.
- If the disk is in a floppy drive we can allow the user to
- continue by making a call to the ChangeCopyDisk procedure,
- otherwise we must abort the function.
-
- After a change of disks SFM will try to continue where
- it left off. There are two cases where it will have to
- back up a bit and reload files that were already read in
- once before the disk change.
-
- One case is when the clear disk option has been used.
- The call to CLEARFAT caused SFM to set up a temporary disk
- transfer area in the program's heap space. This transfer
- area occupies the same memory area as the copy buffers and
- will have overwritten the information there. Therefore we
- must reload the files that were in the buffers. Note that
- ChangeCopyDisk uses the SPLIT flag to indicate that a disk
- has been cleared.
-
- The second case offers insight into why I chose the
- name for the SPLIT flag. This flag keeps an eye on the
- following special case:
-
-
-
- Super File Manager - 29 -
- While we are reading files into memory we hit the
- memory or buffer limit and the current file is split
- between two buffers. Then after a successful write
- pass we return to the read procedure and pick up
- reading that file where we left off. Let's say that
- on the next write pass we run out of diskette space
- while writing that file. In this case the file is
- deleted from the destination and we allow the user to
- change disks. Now we are ready to continue, but we
- don't have the entire file in memory anymore. We can
- tell because the SPLIT flag is still true, so we must
- return to the read pass and start over with that file.
-
- That covers a not so obvious, but dangerous possibility.
-
- As a final step in the WriteTo procedure we must close
- the file handle and set the correct date and time. When we
- close a file that was open for writing, DOS updates the
- time and date to the current system values. To update
- these fields we are required to reopen the file for
- reading, use the appropriate DOS function for the update,
- and then close the file again. The time and date we use is
- retrieved from the entry stored in memory for the source
- directory.
-
-
- MENU TWO NOTES
-
-
- When menu two is entered SFM loads a copy of the
- directory into memory. All of the functions from this menu
- will act on this copy and won't be updated on the disk
- until the update disk function is used.
-
- In addition to a copy of the current directory we also
- keep a copy of the file allocation table. This FAT is only
- used for recovering files when using the undelete
- function. If the disk is updated after an undelete
- operation then this FAT must also be written back to disk.
-
-
- REARRANGING FILES
-
-
- While in menu two the directory may be sorted or
- rearranged using the pick up file function. Both methods
- work only on the copy in memory and don't become permanent
- until updated.
-
- The Sort functions are fairly easy to understand.
- Just for the record, the sort used is an insertion sort.
- The major factors in choosing the type of sort used were
- whether or not it was stable and how difficult it would be
- to alter later.
-
-
- Super File Manager - 30 -
- REARRANGING FILES : continued
-
-
- You may notice the array of real numbers that gets set
- up for a couple of the sort fields. This allows us to set
- up the keys for the directory once and thus avoid
- recalculating the keys every time we need them. The time
- for one such pass is nothing compared to the exponential
- running time of the insertion sort.
-
- As a final note on the sort routines, I'm not sure why
- the character array comparisons work. I thought the
- comparisons would have to be done on Turbo strings, but
- they seem to work fine with arrays of characters. I
- couldn't find anything in the Turbo reference manual to
- either support or deny this.
-
- Now, about the procedures for picking up and dropping
- a directory entry. If you look at them you'll see that I
- basically copied the routines that allow movement around
- the directory windows and modified them to suit this
- function. I don't like this duplication of code, but lack
- the time to fix the problem. Other that that, the
- procedures are fairly easy to understand.
-
-
- SINGLE FLOPPY SYSTEMS
-
-
- Support has been provided for systems having a single
- floppy drive. Persons with such systems may occasionally
- have use for a program such as SFM, but few programs
- provide support for them. SFM does so by trapping the BIOS
- interrupt 10 (Video I/O).
-
- You may ask what this interrupt has to do with DOS and
- the answer is - not much. However, DOS has a neat ability
- to allow a user to use a single drive as two drives. When
- DOS wants access to the disk that is supposed to be in the
- other drive it asks the user to change disks.
-
- This works fine from the DOS command prompt, but not
- so well from within a program. DOS uses the Write
- Character as Teletype BIOS Video function request to print
- its message. When the bottom of the screen is reached this
- function scrolls the entire screen, without paying
- attention to any windows you may have specified. When this
- happens the current display screen is ruined.
-
-
-
- Super File Manager - 31 -
- SINGLE FLOPPY SYSTEMS : continued
-
-
- The INT10 procedure in SFMVARS.INC handles this in a
- unique manner. It takes over the BIOS interrupt 10 and
- when it is asked to write a character it uses the Turbo
- WRITE procedure instead. This forces the message printed
- to conform to the current window specified.
-
- Granted, this is not the ideal way to handle the
- problem, but I don't know the DOS interrupt that handles
- the requests to change disks. For this reason, I had to
- rely on a hunch that DOS used the specified BIOS interrupt
- to write its message. What I ended up with is crude, but
- it works just fine.
-
- Super File Manager - 32 -
- >> SUGGESTED REFERENCES <<
-
-
- Here is a list of the books that I found useful in
- designing SFM. The list isn't in a standard bibliography
- format, but then this isn't a Technical Writing class
- either.
-
-
- Programmer's Reference Manual for IBM Personal Computers
- Steven Armbrust & Ted Forgeron
- 1986: Dow Jones - Irwin
-
- This was by far the most used of my references. It is
- an excellent text that contains no-nonsense facts
- about the majority of DOS and BIOS interrupts. In
- many cases there are short example programs written in
- Assembly Language, Pascal and C (examples in this
- document are not plagiarized, they are original).
-
-
- The Complete Guide to IBM PC AT Assembly Language
- Harley Hahn
- 1987: Scott, Foresman and Company
-
- Not used extensively for SFM, but is by far the best
- Assembly Language book that I have seen. It serves as
- both an introductory text and reference manual. You
- don't have to own an AT to make use of it (my system
- is an XT compatible).
-
-
- The MS-DOS Handbook
- Richard Allen King
- 1986: SYBEX, Inc.
-
- This is a pretty good reference, but I don't use it
- nearly as much as the first book listed above.
-
-
- That about wraps up my favorite references. I have used
- others, but not enough to bother listing.
-
- Super File Manager - 33 -
- >> ACKNOWLEDGMENTS <<
-
-
- Here are some of the other sources that I found
- useful.
-
-
- Finding Disk Parameters
- Glenn F. Roberts
- May 1986: PC Tech Journal
- Article covering the DOS function $32 request.
-
-
- Dipping Into Directories
- Ted Mirecki
- February 1985: PC Tech Journal
- Article on some ideas for loading subdirectories into
- memory and altering them.
-
-
- INLINE Interrupts
- Charles C. Edwards
- December 1985: PC Tech Journal
- Article that contains some information on writing
- interrupt handlers in Turbo Pascal. It clarifies a
- few problems with the Turbo documentation.
-
-
- INT24
- Bela Lubkin
- May/June 1986: Programmer's Journal
- Article that contains the INT24 procedures used in
- SFM.
-
-
- DISPLAY.ARC
- Keith G. Chuvala
- File containing the DISPLAY procedure used in SFM. It
- also includes a documentation file.
-
-
- INLINE.ARC
- L. David Baldwin
- 1985,86
- File containing the inline assembler that I use and
- another utility to turn inline code into its Assembly
- Language equivalent. Documentation is also included.
-