home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / turbopas / sfmsrc.arc / SFMTECH.TXT < prev    next >
Text File  |  1987-06-28  |  62KB  |  1,629 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.                    Super File Manager v1.0
  15.  
  16.                      PROGRAMMER'S GUIDE
  17.  
  18.  
  19.  
  20.  
  21.                         David Steiner
  22.                         2035 J Apt. 6
  23.                         Lincoln, NE  68510
  24.                         (402)475-0601
  25.  
  26.  
  27.  
  28.  
  29.      Written for:
  30.  
  31.                  Capitol PC User Group Inc.
  32.              1987 Software Programming Contest.
  33.  
  34.  
  35.  
  36.  
  37.      Permission is granted for Capitol PC and other not for 
  38. profit organizations to publish the source and executable 
  39. portions of this program.
  40.  
  41.                        >> OVERVIEW <<
  42.  
  43.  
  44.      This documentation file is not a user's manual.  It is 
  45. designed to provide helpful information about the 
  46. techniques used in Super File Manager (SFM).
  47.  
  48.      This document is not designed to be a DOS technical 
  49. reference either.  I do not claim to be any kind of 
  50. authority on DOS or the other types of system calls used by 
  51. this program.  It is entirely possible that information 
  52. presented here is not completely accurate.  The only claim 
  53. I can make is that these routines work for me.
  54.  
  55.      Before getting too far into this file I suggest that 
  56. you first take a brief look at the source code for SFM.  
  57. The style used may not be what your used to, but I like 
  58. it.  In most cases the comments are kept to a simple 
  59. procedure description in order to avoid clutter.
  60.  
  61.      Several items will be covered in this reference that I 
  62. think you will find useful:
  63.  
  64.           Compiling and running SFM.
  65.           General design of a DOS call routine.
  66.           DOS function $32, non-documented but useful.
  67.           Using absolute disk reads and writes.
  68.           Trapping Turbo errors.
  69.           Designing an interrupt handler.
  70.           Trapping DOS critical errors.
  71.           An overall look at SFM.
  72.           Suggested references.
  73.           Acknowledgments.
  74.  
  75.      This documentation file is being written fairly 
  76. hastily so I will apologize now if it doesn't flow terribly 
  77. well.  If I had more time I would have had a friend or two 
  78. help work out the rough spots.
  79.  
  80.  
  81.                   Super File Manager - 2 -
  82.                      >> COMPILING SFM <<
  83.  
  84.  
  85.      Although the executable code (.COM file) distributed 
  86. with SFM will work on most any system, you will need 
  87. Borland's Turbo Pascal version 3.0 if you wish to customize 
  88. SFM to better suit your needs.  This technical 
  89. documentation, however, presents ideas that may be 
  90. generalized to any other language.  If you don't have Turbo 
  91. Pascal, but are fairly good at language conversions you 
  92. will also be able to make use of the source code.
  93.  
  94.      It is a requirement stated in the contest rules that I 
  95. must describe all steps necessary to compile and run SFM.  
  96. I apologize to those who find these instructions 
  97. condescending.
  98.  
  99.      To compile SFM you must first make sure that all of 
  100. the include files are present in the current directory.  
  101. These are:
  102.  
  103.                SFM.PAS
  104.                SFMVARS.INC
  105.                SFMOTHER.INC
  106.                SFMSCRN.INC
  107.                SFMDOS.INC
  108.                SFMFUNC.INC
  109.  
  110. The next thing to do is start Turbo.  Assuming this is done 
  111. and that the files above are in the current directory, here 
  112. is the command sequence for compiling to the file SFM.COM:
  113.  
  114.           M             ; Main file
  115.           SFM <ENTER>   ; file name
  116.           O             ; Options menu
  117.           C             ; Compile to COM file
  118.           Q             ; Quit options menu
  119.           C             ; Compile
  120.           Q             ; Quit Turbo
  121.           SFM <ENTER>   ; Run SFM
  122.  
  123. The letters shown to the left are typed as shown, the ENTER 
  124. key need only be pressed when explicitly listed.  SFM 
  125. should now be up and running, and it is time to refer to 
  126. the User's Guide if you aren't already familiar with SFM.
  127.  
  128.  
  129.                   Super File Manager - 3 -
  130.                        >> DOS CALLS <<
  131.  
  132.  
  133.      Making a DOS or BIOS call from Turbo Pascal is not all 
  134. that difficult.  This section just gives an outline of a 
  135. good method for designing functions that make these calls, 
  136. as well as some specifics about how Turbo handles such 
  137. requests.
  138.  
  139.      For those of you new to system calls a short 
  140. explanation is in order.  There exists a vault of functions 
  141. available to you (as a programmer) that is always resident 
  142. in memory.  Many of these functions are part of your 
  143. system's hardware (BIOS) as part of the ROM (read-only 
  144. memory).  Many more become available when your system loads 
  145. DOS.
  146.  
  147.      In most cases BIOS functions are very low level and 
  148. hard to work with.  An example of the more useful BIOS 
  149. functions would be those for controlling the video 
  150. display.  They allow you to alter the cursor, change the 
  151. video mode, read a character from the screen... the list is 
  152. a long one.
  153.  
  154.      The DOS functions form something of a buffer between 
  155. you and the BIOS, but they are accessed similarly.  
  156. Naturally, the majority of DOS functions have to do with 
  157. disk access, but there are several other types also.  IBM 
  158. suggests that you use the DOS functions whenever possible 
  159. since the BIOS may be different on later models of the PC 
  160. or even PC compatibles.
  161.  
  162.      Calling these functions is accomplished at the 
  163. assembly language level through the use of the INT xx 
  164. (interrupt) instruction, where xx is an interrupt number.  
  165. The BIOS sets aside an area of memory for a table of 
  166. addresses when the system is turned on.  Each entry in this 
  167. table is given a number, the interrupt number.
  168.  
  169.      When you issue the INT instruction you are telling the 
  170. system to save its place and basically jump to the address 
  171. associated with that number.  After the system routine has 
  172. finished it returns to where the program left off.
  173.  
  174.      Before making such a call you must initialize the 
  175. registers so the interrupt will know exactly what to do and 
  176. how to find the input it requires.  If you don't know what 
  177. a register is you should stop here.  Find an introductory 
  178. text on assembly language and spend enough time with it to 
  179. get comfortable with the concept of registers before 
  180. continuing.
  181.  
  182.  
  183.  
  184.                   Super File Manager - 4 -
  185. DOS CALLS : continued
  186.  
  187.      How to initialize these registers varies greatly from 
  188. one function to the next, and a comprehensive list is 
  189. beyond the scope of this document.  Please see the 
  190. references section below if you are looking for a good 
  191. place to get this information.
  192.  
  193.      Now we will proceed with how to use this information 
  194. from Turbo Pascal.  The Turbo procedures that we need to 
  195. know are the following two:
  196.  
  197.           INTR( Interrupt : integer; var Regs : reg_T);
  198.           
  199.           MSDOS( var Regs : reg_T);
  200.  
  201. They both require an input of type REG_T.  Here is the best 
  202. way I know of to define this record:
  203.  
  204.      reg_T = record case boolean of
  205.                true  : (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags
  206.                                                 : integer);
  207.                false : (AL,AH,BL,BH,CL,CH,DL,CH : byte);
  208.              end;
  209.  
  210. This is known as a variant record and allows us to use the 
  211. registers as full or half registers, just like we would 
  212. from assembly language.
  213.  
  214.      The MSDOS procedure is really just a slight variation 
  215. of INTR.  Most DOS functions are accessed through interrupt 
  216. number $21 and that is why there is a separate procedure 
  217. for them.  I suspect that the MSDOS procedure really looks 
  218. something like this:
  219.  
  220.           procedure MSDOS( Regs : reg_T );
  221.           begin
  222.             Intr( $21, Regs );
  223.           end;
  224.  
  225. Since this relationship has been pointed out I will only 
  226. talk about the INTR procedure for now.  Although redundant, 
  227. the MSDOS procedure does help your code's clarity and 
  228. should be used when appropriate.
  229.  
  230.      INTR acts in the following manner.  First it saves the 
  231. current registers and then puts the values you specified in 
  232. the REGS record into the registers.  It then issues an INT 
  233. instruction for the interrupt you specified.  Upon return 
  234. Turbo puts the new register values into REGS and restores 
  235. the values it saved to the actual registers.
  236.  
  237.      After a call to INTR the REGS record will contain the 
  238. values you would expect from the system call.  I think that 
  239. it's time for an example.
  240.  
  241.  
  242.                   Super File Manager - 5 -
  243. DOS CALLS : continued
  244.  
  245.  
  246. An example of a BIOS call:
  247.  
  248.      The procedure below will ask BIOS what the current 
  249.      video mode is.  This example shows what is known as a 
  250.      subfunction.  We will be calling interrupt $10, named 
  251.      the BIOS Video I/O interrupt.
  252.      
  253.      This interrupt performs all kinds of video functions, 
  254.      and we may tell it which one we want by specifying a 
  255.      subfunction.  The one we want is number $0F, called 
  256.      Get the Current Video Mode.  The standard for 
  257.      requesting a subfunction is to be put the function's 
  258.      number into register AH.
  259.      
  260.      The only input is loading the subfunction number into 
  261.      Regs.AH.
  262.      
  263.      Output from this interrupt is contained in the the AX 
  264.      and BX registers as follows:
  265.      
  266.           AL = video mode number
  267.                  $00   = black & white 40 columns X 25 rows
  268.                  $01   = color 40 X 25
  269.                  $02   = b&w   80 X 25
  270.                  $03   = color 80 X 25
  271.                  $07   = monochrome text, 80 X 25
  272.                $04-$06,
  273.                $08-$0F = different graphics modes
  274.                
  275.           AH = Number of columns
  276.           
  277.           BH = Active video page (0 if graphics mode)
  278.      
  279.      
  280.      procedure GetMode(var ModeNum : integer );
  281.      var
  282.        Regs : reg_T;
  283.      begin
  284.        with Regs do
  285.        begin
  286.          AH := $0F;
  287.          Intr( $10, Regs );
  288.          ModeNum := AL;
  289.        end;
  290.      end;
  291.      
  292.      
  293.      Since this procedure is just for getting the mode 
  294.      number we don't bother saving the other register 
  295.      values.
  296.  
  297.  
  298.                   Super File Manager - 6 -
  299. DOS CALLS : continued
  300.  
  301.  
  302. A side note:  Once you start playing around with interrupts 
  303. you may discover what lies behind some of Turbo's 
  304. procedures and functions.  For example Turbo's TEXTMODE 
  305. function apparently calls BIOS interrupt $10, subfunction 
  306. $00 - Set Video Mode.  The Turbo constants used 
  307. (bw40,bw80,c40,c80) for this call correspond to the values 
  308. returned in the above GetMode example.  This implies that 
  309. you could call TEXTMODE with some other number and actually 
  310. set a graphics mode (e.g. $04 for medium-resolution CGA).  
  311. I must admit that I haven't tried it yet, but it seems 
  312. highly likely.
  313.  
  314.      If you have looked at the SFM source code you may have 
  315. noted that whenever I used the INTR or MSDOS procedures the 
  316. function call was named in a comment to the side.  This is 
  317. a good habit that you should adopt.  I would have done it 
  318. in the above example but these lines aren't wide enough to 
  319. allow such comments.
  320.  
  321.      For you folks already familiar with using these Turbo 
  322. procedures we're finally getting to something you can use.  
  323. Now we get into those functions that may not always be 
  324. successful.  The most common type of functions where this 
  325. can happen are those accessing a disk, the domain of DOS.
  326.  
  327.      When using the MSDOS procedure (or INTR with $21) many 
  328. of the functions will not always work.  DOS uses several 
  329. methods for letting us know something went wrong.
  330.  
  331.      Much of the time the carry flag will be set if there 
  332. was a problem executing one of the DOS functions.  You can 
  333. check this bit (bit 0 of the Flags register) by ANDing the 
  334. flags register with $01 and comparing this to zero:
  335.  
  336.      if ( (Flags AND $01) <> 0 ) then ERROR else OK;
  337.  
  338. If there was an error the registers will not contain the 
  339. information you were expecting.  Instead DOS will have 
  340. placed an error code in the AX register.  This error code 
  341. will correspond to a message explaining what went wrong.  
  342. Hopefully the reference you chose for DOS function calls 
  343. has a list of these messages.
  344.  
  345.      Another method often used is setting the AL register 
  346. equal to $FF if there was an error.  I'm just trying to 
  347. give you the general idea here, you will have to check how 
  348. errors are handled for each function that you use.
  349.  
  350.  
  351.  
  352.                   Super File Manager - 7 -
  353. DOS CALLS : continued
  354.  
  355.  
  356.      Well here is the main idea I want to get across for 
  357. this section.  When you design a routine to make a system 
  358. call that may not always be successful you should make it a 
  359. Turbo function.  This way the function result can pass back 
  360. the error code or at least a boolean value for whether or 
  361. not it was successful.
  362.  
  363.      Then any procedure that calls your function can decide 
  364. what to do with an error on its own.  You may have it abort 
  365. the program, ignore the error, or (more likely) print an 
  366. error message and allow the program to continue.
  367.  
  368.      When SFM uses this technique it is sometimes two or 
  369. three levels deep in function calls, but the error code is 
  370. just passed back on through until it gets to a routine that 
  371. is designed for handling output.  Then the error message is 
  372. finally printed out.  A pretty good example of this is in 
  373. the ChangePath procedure found at the top of SFMFUNC.INC.
  374.  
  375. Another sample function:
  376.  
  377.      We'll write an example that tries to rename 
  378.      "\SFMTECH.TXT" to "\TRASH\BULL.XXX".  We need to use 
  379.      the MSDOS procedure, subfunction $56 - Rename a File.
  380.      
  381.      You may have noticed that the new name contains a 
  382.      different path.  We can do that with this DOS 
  383.      function.  If you look into the SFM source code you 
  384.      will see that the move command uses this DOS function.
  385.      
  386.      If you're just learning this system call stuff you're 
  387.      probably wondering how we're going to fit those 
  388.      strings into the registers.  The answer is - we don't. 
  389.      All DOS expects is the addresses of these two strings, 
  390.      loaded into the DS:DX and ES:DI register pairs 
  391.      respectively.
  392.      
  393.           To set these addresses you need to know a little 
  394.      about two more Turbo procedures: OFS and SEG.  SEG 
  395.      returns the "high" portion of the address of a 
  396.      variable, or the segment.  OFS returns the "low" 
  397.      portion, or offset within that segment.  That's not a 
  398.      very complete description, but it gives you the 
  399.      general idea.
  400.      
  401.      There is one more thing you must note when passing 
  402.      strings to DOS functions.  DOS doesn't recognize 
  403.      Turbo's string structure.  It expects the first 
  404.      character at the address to be the start of the file 
  405.      name and that the string be terminated by a NUL ($00) 
  406.      character.  This is known as an ASCIIZ string.
  407.      
  408.  
  409.                   Super File Manager - 8 -
  410. RENAME EXAMPLE : continued
  411.  
  412.  
  413.      The error codes returned from DOS calls like this 
  414.      often have their own small set of error messages.  
  415.      Those codes returned by this call (in AX) are:
  416.      
  417.           $02 : File not found
  418.           $03 : Path not found
  419.           $05 : Access denied
  420.           $11 : Tried to rename to a different drive
  421.      
  422.      
  423.      function RenameStuff : integer;
  424.      var
  425.        Regs           : reg_T;
  426.        oldstr, newstr : string[80];
  427.      begin
  428.        oldstr := '\SFMTECH.TXT'    + #00;
  429.        newstr := '\TRASH\BULL.XXX' + #00;
  430.        with Regs do
  431.        begin
  432.          AH := $56;
  433.          DS := seg( oldstr[1] );
  434.          DX := ofs( oldstr[1] );
  435.          ES := seg( newstr[1] );
  436.          DI := ofs( newstr[1] );
  437.          MsDos( Regs );
  438.          if (Flags AND $01) <> 0 then
  439.            RenameStuff := AX
  440.          else
  441.            RenameStuff := 0;
  442.        end;
  443.      end;
  444.      
  445.      
  446.      Note that the OFS procedure calls are asking for the 
  447.      offset to the first character in the string 
  448.      (xstr[1]).  This index is not necessary for getting 
  449.      the segment portion of the address, but is done anyway 
  450.      for consistency.
  451.  
  452. Well that's about it for what I had to say about DOS 
  453. calls.  We'll discuss them again a bit later in the section 
  454. for trapping serious DOS errors.
  455.  
  456.  
  457.  
  458.                   Super File Manager - 9 -
  459.                    >> DOS FUNCTION $32 <<
  460.  
  461.  
  462.      If you already have a reference for MS-DOS calls 
  463. you've probably noticed that there are several subfunction 
  464. numbers that are "reserved for DOS."  Ever wonder what 
  465. mystical operations those functions performed?  Well we are 
  466. about to unravel one of those mysteries...
  467.  
  468.      Glenn Roberts authored an excellent article in PC Tech 
  469. Journal that covers the $32 DOS function.  I must admit 
  470. that SFM would be crippled, slower, and less reliable 
  471. without it.  This function request will return an address 
  472. to an immensely valuable table of diskette parameters.
  473.  
  474.      Before going into the actual description, I want to 
  475. make sure you know that this is a NON-DOCUMENTED function.  
  476. This means that it may or may not remain in future versions 
  477. of DOS, MicroSoft doesn't guarantee anything.  From the 
  478. information in Roberts' article it is valid for DOS 
  479. versions 2.0 through 3.1 and I can verify that it is still 
  480. there in version 3.2.
  481.  
  482.  
  483.               $32 - Get Device Parameter Table
  484.  
  485.  
  486. Input:
  487.           AH = $32 for requesting the subfunction
  488.           DL = The drive you want the table for
  489.                  (A=1, B=2,...)
  490.  
  491. Output:
  492.           DS = Segment of parameter table
  493.           BX = Offset of table
  494.  
  495. Error:
  496.           AL = $FF if the drive was invalid
  497.  
  498.  
  499.  
  500.  
  501.                   Super File Manager - 10 -
  502. FUNCTION $32 : continued
  503.  
  504.  
  505. Table Contents:
  506.  
  507.      Byte(s)   Contents
  508.      -------   ---------------------
  509.         0      Assigned disk (A=0, B=1,...)
  510.         1      Same as above, except 0 for RAM disk
  511.        2-3     Bytes per cluster
  512.         4      Sectors per cluster minus 1
  513.         5      Number of heads minus 1
  514.        6-7     Reserved sectors (bootstrap)
  515.         8      Number of FAT copies
  516.        9-10    Maximum number of root directory entries
  517.       11-12    First usable sector
  518.       13-14    Total cluster count plus 1
  519.        15      Sectors per FAT
  520.       16-17    First root directory sector
  521.       18-21    Device driver address
  522.       22-23    Media descriptor
  523.       24-27    Chain to next disk table
  524.       28-29    *Cluster of current working directory
  525.       30-93    *64-byte current working directory
  526.      
  527.                * = valid only for DOS 2.x
  528.  
  529. If you plan on implementing this table in a Turbo program 
  530. there is a record defined for it in the SFMVARS.INC file.
  531.  
  532.      Those of you paying attention may have noticed the 
  533. above inconsistency for numbering the drives.  DOS is 
  534. pretty poor on this count.  Notice that when you call 
  535. function $32 you ask for a drive's table according to A=1, 
  536. B=2... When information is returned in the table the drives 
  537. are numbered A=0, B=1...  Keep this in mind when using DOS 
  538. calls and be especially careful about specifying which 
  539. drive you want to write to.
  540.  
  541.      When using this table remember that you are looking 
  542. directly into the table that DOS uses.  Changing values in 
  543. this table may affect the way DOS accesses the disk, which 
  544. would not be good if it happened to want to do a write 
  545. operation.
  546.  
  547.      The only reason I can think of for wanting to change 
  548. these parameters would be during a format function.  SFM 
  549. has no such function, but I did have one started before it 
  550. became too difficult to implement properly.  I found that 
  551. in some cases this table had to be altered in order to 
  552. format diskette tracks.  I don't feel qualified to give any 
  553. further discussion on formatting (I had a good start 
  554. though, until I tried formatting one of the AT's high 
  555. density drives at work).
  556.  
  557.                   Super File Manager - 11 -
  558.                 >> ABSOLUTE DISK ACCESSES <<
  559.  
  560.  
  561.      Now I shall go into the Turbo INLINE command a bit.  
  562. It seems that the best way to read from or write directly 
  563. to diskette sectors would be to use the corresponding DOS 
  564. interrupts.  There is something of a problem here though.  
  565. For some unknown reason the DOS interrupts $25, Absolute 
  566. Disk Read and $26, Absolute Disk Write both leave a copy of 
  567. the Flags register on the stack after returning from the 
  568. interrupt.
  569.  
  570.      DOS apparently does a no-no.  It seems to use the 
  571. wrong type of return from an interrupt handler.  Something 
  572. I didn't mention above is exactly how the INT operation 
  573. works.  Well, it puts a copy of the Flags register on the 
  574. stack, followed by two words for the return address.  
  575. Whenever an interrupt handler is designed there is supposed 
  576. to be an IRET instruction for returning to the calling 
  577. program.  The IRET, of course, loads the return address and 
  578. then restores the Flags register before making the jump 
  579. back.
  580.  
  581.      DOS, however, seems to use the normal RET instruction 
  582. to return from interrupts $25 and $26.  This means that the 
  583. Flags don't get popped unless the calling procedure does so 
  584. itself after the INT call.
  585.  
  586.      From the assembly language level this is just an 
  587. annoying little quirk.  In Turbo Pascal the quirk develops 
  588. into one of those things people buy Preparation H to 
  589. remedy.  The INTR procedure is not designed to handle these 
  590. special cases and those Flags left sitting on the stack 
  591. wreak havoc with the system, often causing a lockup.
  592.  
  593.      The only ways to fix this problem from within Turbo is 
  594. to write an external subroutine or resort to machine 
  595. language using the INLINE command.  I prefer INLINE code 
  596. since it doesn't require that you keep track of a separate 
  597. binary file.  This command is used in the following manner:
  598.  
  599.      Inline( $xx/$xx ... /$xx );
  600.  
  601. That's right, I didn't mistype.  You must enter the actual 
  602. machine codes (hex numbers) for Turbo to accept INLINE 
  603. input.  The only break Turbo offers you is that you can use 
  604. variable names when you need to, so you don't have to know 
  605. their exact address (which isn't possible anyway).
  606.  
  607.      Fortunately there are alternatives to being a machine 
  608. code wizard.  There are programs available that take small 
  609. files of pseudo-assembly code and do all of the machine 
  610. code conversions for you.  The one I use is listed below in 
  611. the references section.
  612.  
  613.  
  614.                   Super File Manager - 12 -
  615. ABSOLUTE DISK ACCESS : continued
  616.  
  617.      There are still minor drawbacks even with these 
  618. utilities.  Because such programs must interface with Turbo 
  619. they often use a variation of assembly language.  This 
  620. means learning a new dialect even if you are already 
  621. familiar with assembly.  On the brighter side, alterations 
  622. aren't too great and don't take long to master.
  623.  
  624.      An example is not presented here since both interrupts 
  625. that have this problem are already written as part of SFM.  
  626. They are two separate procedures and can be found in the 
  627. SFMDOS.INC file.  Their names are LoadSectors and 
  628. WriteSectors.
  629.  
  630.      You may also find examples of INLINE code below when 
  631. we discuss interrupt handlers.
  632.  
  633.  
  634.                   Super File Manager - 13 -
  635.                  >> TRAPPING TURBO ERRORS <<
  636.  
  637.  
  638.      The method used to trap Turbo run-time errors is 
  639. really quite simple.  There is a Turbo variable named 
  640. ERRORPTR that you can change to point to a procedure of 
  641. your own.  If your error routine is called AbortOnError you 
  642. can issue this statement
  643.  
  644.           ErrorPtr := ofs( AbortOnError );
  645.  
  646. near the beginning of your program and when an error occurs 
  647. Turbo jumps there instead of jumping to its normal error 
  648. handler, expecting you to handle the error and then exit 
  649. the program.  You won't find this in the Turbo manual, 
  650. instead it was put into a READ.ME file on one of the 
  651. compiler diskettes.  I hope I never meet any of the 
  652. manual's authors since I couldn't afford the lawsuit I'd 
  653. get after punching them in the nose.
  654.  
  655.      The restrictions placed on the error handling 
  656. procedure are that it must have two integers as its input 
  657. and that it contain a HALT command since the program must 
  658. be terminated.  The following procedure layout is correct:
  659.  
  660.           procedure AbortOnError( ErrNum, Addr : integer );
  661.           begin
  662.             { Your code goes here }
  663.             halt;
  664.           end;
  665.  
  666. Within this procedure you can do whatever you wish, except 
  667. "end" it normally.  You can even call another procedure.  
  668. The input parameters contain the following:
  669.  
  670.           Hi( ErrNum ) = The error type
  671.                            $00 = user break
  672.                            $01 = I/O error
  673.                            $02 = run-time error
  674.           Lo( ErrNum ) = The error code
  675.           Addr         = Where the program was when
  676.                            the error occurred.
  677.  
  678.      SFM contains just such an error routine.  While it 
  679. isn't likely that it will be called, it is still there just 
  680. in case.  The procedure is found towards the top of the 
  681. SFMSCRN.INC file (named AbortOnError).
  682.  
  683.      The Addr value may be converted to hex and entered in 
  684. the Find run-time error option from the Turbo Editor.  This 
  685. will allow Turbo to place you where the error occurred in 
  686. relation to the source code, and you can take it from 
  687. there.
  688.  
  689.  
  690.                   Super File Manager - 14 -
  691. TRAPPING TURBO ERRORS : continued
  692.  
  693.  
  694.      Note that the main reason that SFM has its own error 
  695. handler is because it must perform several operations to 
  696. return the system to normal before exiting.  You see, SFM 
  697. uses custom interrupt handlers and these interrupts must be 
  698. restored to normal or DOS will have a fit.  The next 
  699. section will describe these interrupt handlers.
  700.  
  701.      As a final note there is one special situation you may 
  702. not have considered yet.  If the program is terminated by a 
  703. heap/stack collision (error $FF) you will not be able to 
  704. call any subprograms from within your error handler.  To 
  705. alleviate this problem you can provide your program with a 
  706. means of freeing up some heap space.
  707.  
  708.      First you must declare a variable that can contain a 
  709. pointer address.  The one used by SFM is:
  710.  
  711.           HeapStart : ^integer;
  712.  
  713. It doesn't matter much what type of variable HeapStart 
  714. points to, but a pointer to an integer is a common method.  
  715. Near the beginning of your program you simply issue a MARK 
  716. procedure call:
  717.  
  718.           Mark( HeapStart );
  719.  
  720. Now HeapStart points at a memory location on the heap.  
  721. Later in the AbortOnError procedure you just issue a 
  722. RELEASE call:
  723.  
  724.           Release( HeapStart );
  725.  
  726. Once this is done there will be some free space in memory 
  727. again and you can call other procedures if you like.  You 
  728. must remember that the call to RELEASE will clear out 
  729. everything put on the heap after the last MARK call.  This 
  730. means that any dynamic variables you were using are now 
  731. gone.
  732.  
  733.      In case you were wondering how the program's heap and 
  734. stack are related, here is an explanation.  The heap and 
  735. stack are both dynamic in nature.  This means that the 
  736. amount of memory used by either can grow or diminish during 
  737. the program execution.  It then makes sense that they share 
  738. the same area of memory.
  739.  
  740.  
  741.  
  742.                   Super File Manager - 15 -
  743. TRAPPING TURBO ERRORS : continued
  744.  
  745.  
  746.      Let's take a look at where Turbo puts a program in 
  747. memory when it is loaded.  First Turbo loads in the 
  748. program's code segment, containing all of the procedures 
  749. and typed constants defined.  Then it sets up the program's 
  750. data segment, which consists of the global variables.  All 
  751. of the memory left over becomes a playground for the stack 
  752. and heap.
  753.  
  754.      Now let's look closer at the heap/stack space.  Turbo 
  755. decides that the heap should start right after the the data 
  756. segment.  Whenever more space is required for the heap 
  757. Turbo designates another chunk of memory to it (NEW or 
  758. GETMEM procedure calls).  The stack, on the other hand, 
  759. starts at the outer limit of the program's memory.  
  760. Whenever it needs more memory Turbo takes another chunk 
  761. from that end (procedure calls and their local variables).
  762.  
  763.      When the program is finished with memory on the heap 
  764. or stack it can be returned to the system.  This is 
  765. accomplished through DISPOSE or RELEASE procedures for the 
  766. heap space.  All that is required to restore stack space is 
  767. the return from the procedure that allocated the memory.
  768.  
  769.      Turbo keeps track of how far out the heap and stack 
  770. are extended by setting a pointer to their outermost memory 
  771. location.  Should these pointers ever cross you will 
  772. receive a heap/stack collision error message ($FF).  This 
  773. means that the heap and stack were trying to use the same 
  774. memory area for their data at the same time.
  775.  
  776.      One likely cause for such a collision would be to ask 
  777. Turbo to give you a large chunk of memory using a GETMEM 
  778. call.  This is the type of error possible with SFM.  
  779. Another, not so obvious cause, is when a procedure or 
  780. function sets up a large array in its local variable 
  781. declarations.  A third example would be a recursive routine 
  782. that calls itself many times.  Since Turbo must save quite 
  783. a bit of information each time a procedure calls itself you 
  784. may eventually run out of memory.
  785.  
  786.  
  787.                   Super File Manager - 16 -
  788.                   >> INTERRUPT HANDLERS <<
  789.  
  790.  
  791.      Here is another use for Inline code.  You can design 
  792. your own interrupt handlers.  This is how SFM traps all of 
  793. the DOS critical errors and avoids the normal DOS message 
  794. "Abort, Retry, or Ignore?"  Writing such a routine is no 
  795. small task, but I will give you a general outline.
  796.  
  797.      The INT24 function in the SFMOTHER.INC file is such a 
  798. routine and INT10 from SFMVARS.INC is another example.  The 
  799. first routine takes over DOS interrupt $24, the Critical 
  800. Error Handler Address.  The second takes over BIOS 
  801. interrupt $10, the Video I/O interrupt.
  802.  
  803.      The INT24 routines were developed by Bela Lubkin and 
  804. published in an article in Programmer's Journal.
  805.  
  806.      The first thing to look at when designing an interrupt 
  807. handler is how the INT and IRET assembly instructions 
  808. work.  These are covered well enough above in the absolute 
  809. disk read/write section.
  810.  
  811.      Next we must take Turbo into account.  Turbo issues 
  812. the following few lines of code at the beginning of every 
  813. procedure or function:
  814.  
  815.           PUSH    BP
  816.           MOV     BP, SP
  817.           PUSH    SP
  818.  
  819. These instructions set the BP register to a location that 
  820. can be used to index the variables in the subroutine's 
  821. declaration.  Your major concern with these lines should be 
  822. how to put things back in order before returning from your 
  823. interrupt handler:
  824.  
  825.           MOV     SP, BP  ; code to exit
  826.           POP     BP
  827.           IRET
  828.  
  829.      This exit code must be inserted at the end of your 
  830. interrupt handler to successfully return to the calling 
  831. program.  You can't let your interrupt routine exit 
  832. normally since the IRET instruction must be used.  An 
  833. exception to this rule would be DOS interrupts $25 and $26 
  834. for reasons explained in that section.
  835.  
  836.  
  837.  
  838.                   Super File Manager - 17 -
  839. INTERRUPT HANDLERS : continued
  840.  
  841.      If you were wondering why there are two PUSH 
  842. instructions inserted by Turbo but we only POP once I have 
  843. the answer, though it did have me confused for a bit.  
  844. Loading the SP register into BP effectively saves stack 
  845. pointer right after the PUSH BP instruction.  The line 
  846. "MOV  SP, BP" restores this value and has the same effect 
  847. as popping SP (and any local variables that were declared) 
  848. from the stack.  This means that you must not change the BP 
  849. register, unless you take special precautions.
  850.  
  851.      In addition to this exit code you must save and 
  852. restore all registers that are used by your routines.  
  853. Exceptions to this are those registers that are used for 
  854. output from the interrupt.  Before designing an interrupt 
  855. handler you should know exactly which registers these are 
  856. and what their output is supposed to look like.
  857.  
  858.      That covers the basics of the interrupt handling 
  859. routine, now we must examine what is required to make the 
  860. system use that code.  SFM's procedures for doing this are 
  861. called INT24ON and INT10ON for the indicated interrupt 
  862. handlers.
  863.  
  864.      Recalling that the BIOS sets up a table in memory for 
  865. the addresses of the interrupt handlers (a vector table), 
  866. it seems obvious that all we need to do is change the 
  867. vector that corresponds to our interrupt.  Rather than 
  868. attempting to locate and change this entry ourselves we 
  869. will let DOS do the dirty work for us.
  870.  
  871.      DOS has a pair of subfunctions that will either get or 
  872. set these vectors for us.  The function numbers are $35 for 
  873. getting the address and $25 for setting it.  The input for 
  874. this function call is:
  875.  
  876.           AH     = $35 or $25 as needed
  877.           AL     = interrupt to get/set
  878.           DS:DX  = new address when setting
  879.           
  880.      output:
  881.           ES:BX  = current address when getting
  882.  
  883. Notice that the ES:BX register pair will contain the 
  884. address that the vector currently points to after function 
  885. $35 is called.  There is no output for the $25 function, 
  886. but the vector will have been changed (even if the address 
  887. points to an invalid memory location).
  888.  
  889.  
  890.  
  891.                   Super File Manager - 18 -
  892. INTERRUPT HANDLERS : continued
  893.  
  894.      Before changing the interrupt handler you should save 
  895. the old address.  This is done by setting aside a four byte 
  896. space to contain the old vector.  That way the interrupt 
  897. may be restored before the program exits.
  898.  
  899. An example:
  900.  
  901.      This interrupt handler will prevent the video screen 
  902.      from being printed while the program is running.  It 
  903.      is being presented as an entire program so you can see 
  904.      everything that is required.
  905.  
  906.  
  907. program NoPrint;
  908.  
  909. type
  910.   Reg_T    = record case boolean of
  911.                true  : (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags : integer);
  912.                false : (AL,AH,BL,BH,CL,CH,DL,DH          : byte);
  913.              end;
  914.  
  915. const
  916.   DataSeg     : integer  =  0;
  917.   OldInt05    : array[0..1] of integer  = (0,0);
  918.  
  919. procedure Int05;
  920. begin
  921.   {
  922.      PUSH    BP      ; Done by Turbo
  923.      MOV     BP, SP
  924.      PUSH    SP
  925.   }
  926.   Inline(
  927.     $50                    {        PUSH    AX  ; Save all regs   }
  928.     /$53                   {        PUSH    BX  ;   so we can use }
  929.     /$51                   {        PUSH    CX  ;   Turbo code    }
  930.     /$52                   {        PUSH    DX                    }
  931.     /$57                   {        PUSH    DI                    }
  932.     /$56                   {        PUSH    SI                    }
  933.     /$06                   {        PUSH    ES                    }
  934.     /$1E                   {        PUSH    DS                    }
  935.                            {        ;                             }
  936.                            {        Set DS so we can use global   }
  937.                            {          variables and the Turbo     }
  938.                            {          write procedures.}          }
  939.                            {        ;                             }
  940.     /$2E/$A1/>DATASEG      {CS:     MOV     AX, [>DataSeg]        }
  941.     /$8E/$D8               {        MOV     DS, AX                }
  942.   );
  943.  
  944.   writeln( 'Sorry, can''t print the screen right now.' );
  945.   write( #7 );
  946.  
  947.  
  948.                   Super File Manager - 19 -
  949. NOPRINT : continued
  950.  
  951.   InLine(
  952.     $1F                    {        POP     DS  ; Restore all regs}
  953.     /$07                   {        POP     ES                    }
  954.     /$5E                   {        POP     SI                    }
  955.     /$5F                   {        POP     DI                    }
  956.     /$5A                   {        POP     DX                    }
  957.     /$59                   {        POP     CX                    }
  958.     /$5B                   {        POP     BX                    }
  959.     /$58                   {        POP     AX                    }
  960.                            {        ;                             }
  961.     /$89/$EC               {        MOV     SP, BP   ; Exit code  }
  962.     /$5D                   {        POP     BP                    }
  963.     /$CF                   {        IRET                          }
  964.   );
  965. end;
  966.  
  967. procedure Int05ON;
  968. var
  969.   Regs : reg_T;
  970. begin
  971.   DataSeg := Dseg;
  972.   with Regs do
  973.   begin
  974.     AH := $35;    { DOS function $35 - Get Interrupt Vector Address }
  975.     AL := $05;    {      getting $05 - Print Screen }
  976.     MsDos( Regs );
  977.     OldInt05[1] := ES;
  978.     OldInt05[0] := BX;
  979.     AH := $25;    { DOS function $25 - Set Interrupt Vector Address }
  980.     AL := $05;    {      setting $05 - Print Screen }
  981.     DS := Cseg;
  982.     DX := ofs( Int05 );
  983.     MsDos( Regs );
  984.   end;
  985. end;
  986.  
  987. procedure Int05OFF;
  988. var
  989.   Regs : reg_T;
  990. begin
  991.   with Regs do
  992.   begin
  993.     AH := $25;    { DOS function $25 - Set Interrupt Vector Address }
  994.     AL := $05;    {      setting $05 - Print Screen }
  995.     DS := OldInt05[1];
  996.     DX := OldInt05[0];
  997.     MsDos( Regs );
  998.   end;
  999. end;
  1000.  
  1001.  
  1002.  
  1003.                   Super File Manager - 20 -
  1004. NOPRINT : continued
  1005.  
  1006. var
  1007.   ch : char;
  1008.  
  1009. begin
  1010.   DataSeg := Dseg;
  1011.   Int05ON;
  1012.   writeln( 'Hit the SPACE BAR to exit.' );
  1013.   writeln;
  1014.   repeat
  1015.     read( kbd, ch );
  1016.   until ch = #32;
  1017.   Int05OFF;
  1018. end.
  1019.  
  1020.  
  1021. As you can see there are three main parts to setting up an 
  1022. interrupt handler:  the actual handler code, the procedure 
  1023. to save the old vector and set yours, and finally the 
  1024. procedure to restore the old vector.  In addition to this 
  1025. you must set aside a bit of space to store the old vector 
  1026. and perhaps the data segment also.
  1027.  
  1028.      In this example the data segment was saved in order to 
  1029. allow us to use the Turbo write procedures.  I did some 
  1030. checking with DEBUG and it seems that Turbo keeps some 
  1031. information used by the write procedure in the program's 
  1032. data segment.  Restoring the DS register also allows your 
  1033. program to access the global variables.
  1034.  
  1035.      Even if you don't fully understand what I have 
  1036. presented here you can still used the above example as a 
  1037. template to create your own interrupt handlers.  All you 
  1038. need to do is change all occurrences of "05" into whatever 
  1039. interrupt you wish to customize.
  1040.  
  1041.      Now I must clarify that last statement a bit.  You 
  1042. must be careful what interrupt vectors you are changing.  
  1043. The majority of interrupt routines used by the BIOS or DOS 
  1044. have several subfunctions.  If you take control of such a 
  1045. vector you will either have to emulate all of its functions 
  1046. or be selective about which functions you are controlling.
  1047.  
  1048.      The INT10 procedure found in SFMVARS.INC is one of 
  1049. these cases.  Emulating all of the subfunctions of the BIOS 
  1050. Video Interrupt would take an incredible amount of code and 
  1051. essentially be wasted space.  For this reason I chose to 
  1052. compare the value in the AH register and let the BIOS have 
  1053. all function requests except the one that I wanted to 
  1054. alter, the $0E function (Write Character as a Teletype).
  1055.  
  1056.  
  1057.                   Super File Manager - 21 -
  1058. INTERRUPT HANDLERS : continued
  1059.  
  1060.      The transfer of control to the BIOS is accomplished by 
  1061. making a FAR CALL to the old vector address we have taken 
  1062. the precaution of saving.  INT10 then issues an IRET right 
  1063. away in order to return to the calling procedure.
  1064.  
  1065.      I won't cover this in any more detail since the 
  1066. example can be found in the SFM code if you wish to pursue 
  1067. the matter.
  1068.  
  1069.                   Super File Manager - 22 -
  1070.              >> TRAPPING DOS CRITICAL ERRORS <<
  1071.  
  1072.  
  1073.      Now that you have some idea of how to design an 
  1074. interrupt handler we can continue with a practical use.  
  1075. The INT24 interrupt handler mentioned above has become one 
  1076. of my most useful procedures and an addition to any program 
  1077. of conseqence.
  1078.  
  1079.      The DOS interrupt $24 is the one that prints that 
  1080. nasty "Abort, Retry, Ignore?" message that has blemished so 
  1081. many otherwise well designed display screens.  By taking 
  1082. control of this vector we may stop DOS from printing a 
  1083. message and print our own error message at our leisure.
  1084.  
  1085.      To make use of this routine from Turbo we must first 
  1086. take note of a special compiler directive.  This is the "I" 
  1087. compiler directive used for turning Turbo's DOS error 
  1088. checking on, {$I+}, or off, {$I-}.  The default is on, 
  1089. which allows DOS to print the error message mentioned 
  1090. above.
  1091.  
  1092.      When we turn this error checking off we assume 
  1093. responsibility for DOS errors.  If we don't do something 
  1094. about the error before the next DOS function call the 
  1095. program will abort whether or not Turbo was performing 
  1096. error checking.  This means that even if we intend to 
  1097. ignore the error we must at least check the error code 
  1098. using INT24RESULT (this also clears the error code).
  1099.  
  1100.      Using this error checking with the standard Turbo 
  1101. function IORESULT will catch most minor errors.  Such 
  1102. errors include messages like "File does not exist" or "File 
  1103. not Open."  It does not allow for more critical errors such 
  1104. as an unformatted disk or an open diskette drive.
  1105.  
  1106.      To use the IORESULT function you simply turn off the 
  1107. Turbo error checking before an operation that accesses the 
  1108. disk and then turn it back on right away.  Then you may 
  1109. access the IORESULT function to get an error code.  If this 
  1110. code is zero then no error occurred.  Here is a small 
  1111. example for changing the current directory:
  1112.  
  1113.           {$I-}
  1114.           chdir( newpath );
  1115.           {$I+}
  1116.           ErrCode := IOresult;
  1117.  
  1118. In this example NEWPATH is a string variable that contains 
  1119. the path we are attempting to change to.  ERRCODE is an 
  1120. integer variable that will hold the code returned by 
  1121. IORESULT.  This example could become the body of a DOS call 
  1122. function that follows the outline given in the first 
  1123. section of this documentation.
  1124.  
  1125.  
  1126.                   Super File Manager - 23 -
  1127. DOS CRITICAL ERRORS : continued
  1128.  
  1129.      Now we may extend this error checking to include 
  1130. errors that are considered critical to DOS.  If you 
  1131. implement the INT24 set of procedures you may use 
  1132. INT24RESULT in the same manner as you would normally use 
  1133. IORESULT.  The difference is that the new function result 
  1134. will actually contain two error codes.  There is one placed 
  1135. in both the high and low bytes of the integer result.  To 
  1136. see how to extract these two codes take a look into the 
  1137. ErrorMessage routine found in the SFMOTHER.INC file.
  1138.  
  1139.      Keeping this in mind we can now take the final step in 
  1140. designing a break-proof DOS call function that returns a 
  1141. comprehensive error code.  I think an example is the best 
  1142. way to illustrate this.  To avoid setting up an entirely 
  1143. new example we will just enhance the renaming function from 
  1144. the DOS calls section above:
  1145.  
  1146.  
  1147.      function RenameStuff : integer;
  1148.      var
  1149.        Regs           : reg_T;
  1150.        ErrCode        : integer;
  1151.        oldstr, newstr : string[80];
  1152.      begin
  1153.        oldstr := '\SFMTECH.TXT'    + #00;
  1154.        newstr := '\TRASH\BULL.XXX' + #00;
  1155.        with Regs do
  1156.        begin
  1157.          AH := $56;
  1158.          DS := seg( oldstr[1] );
  1159.          DX := ofs( oldstr[1] );
  1160.          ES := seg( newstr[1] );
  1161.          DI := ofs( newstr[1] );
  1162.          {$I-}
  1163.          MsDos( Regs );
  1164.          {$I+}
  1165.          ErrCode := Int24result;
  1166.          if ErrCode <> 0 then
  1167.            RenameStuff := ErrCode
  1168.          else
  1169.          begin
  1170.            if (Flags AND $01) <> 0 then
  1171.              RenameStuff := (AX SHL 8) OR $8000
  1172.            else
  1173.              RenameStuff := 0;
  1174.          end;
  1175.        end;
  1176.      end;
  1177.  
  1178.  
  1179.  
  1180.  
  1181.                   Super File Manager - 24 -
  1182.      Looking at this code may be a bit confusing, but it 
  1183. covers all three levels of DOS error messages.  The first 
  1184. two are taken care of by the INT24RESULT function which 
  1185. combines both Turbo and DOS error messages.  The third type 
  1186. of error is that mentioned in the DOS calls section.  This 
  1187. error code has been altered a bit in order to create an 
  1188. integer function result that can be sent to the 
  1189. ErrorMessage procedure.
  1190.  
  1191.      The alteration is this:  error codes that are returned 
  1192. by the function call itself are shifted left so the occupy 
  1193. the high byte along with the DOS critical error codes.  
  1194. Then the high bit is set to let ErrorMessage know that this 
  1195. error code is one returned from a DOS function call rather 
  1196. than the DOS critical error handler.
  1197.  
  1198.      Again, even if you don't fully understand this process 
  1199. you can still take the procedures and place them into your 
  1200. own program.  The only place you will need to make changes 
  1201. is in ErrorMessage, in order to make it fit your I/O 
  1202. routines and also to restore the error messages that I 
  1203. commented out.  You may then use INT24RESULT just as you 
  1204. would use the standard Turbo IORESULT.
  1205.  
  1206.  
  1207.                   Super File Manager - 25 -
  1208.                      >> SFM OVERVIEW <<
  1209.  
  1210.  
  1211.      Now that we have covered those techniques that I 
  1212. thought needed more background, we can cover Super File 
  1213. Manager's structure in specific.
  1214.  
  1215.      The first thing to note about SFM will be how it is 
  1216. broken down into include files:
  1217.  
  1218.      SFM.PAS        Naturally this is the main file.  It 
  1219.                     contains the initialization routines 
  1220.                     for setting up SFM.  It also contains 
  1221.                     the routines for the first level of 
  1222.                     I/O, the command menus.
  1223.                       
  1224.      sfmVARS.inc    This file contains all of the types 
  1225.                     that are used by SFM as well as the 
  1226.                     majority of global variables and 
  1227.                     constants.  Also included are a few 
  1228.                     routines that needed to be near the 
  1229.                     beginning, but didn't belong in the 
  1230.                     sfmOTHER.inc file.
  1231.                       
  1232.      sfmOTHER.inc   Here we have the two sets of routines 
  1233.                     that I borrowed from other sources.  
  1234.                     These are DISPLAY and the INT24 
  1235.                     routines.
  1236.                       
  1237.      sfmSCRN.inc    Here is the file for the majority of 
  1238.                     low-level I/O routines and help 
  1239.                     displays.  These include everything 
  1240.                     from setting the video mode on up to a 
  1241.                     custom string input function.
  1242.                       
  1243.      sfmDOS.inc     This file contains the low-level DOS 
  1244.                     function calls that generally don't 
  1245.                     perform much screen I/O.  Most of these 
  1246.                     functions return error codes as 
  1247.                     described in previous sections.
  1248.                       
  1249.      sfmFUNC.inc    This last include file contains the 
  1250.                     routines that merge the SCRN, DOS and 
  1251.                     SFM files.  In other words, they are 
  1252.                     called from SFM.PAS and use the 
  1253.                     routines in sfmSCRN.inc to set up calls 
  1254.                     to the routines in sfmDOS.inc (which do 
  1255.                     all the actual work).
  1256.  
  1257. Of course there are procedures in each file that may belong 
  1258. somewhere else, but in some cases this is not possible.
  1259.  
  1260.                   Super File Manager - 26 -
  1261. SFM : continued
  1262.  
  1263.      Actually, that breakdown gives a pretty good feel for 
  1264. how control is passed around within SFM.  We always start 
  1265. in SFM.PAS and the selection of a command usually sends us 
  1266. off to sfmFUNC.inc.  From there things jump around 
  1267. according to the function.
  1268.  
  1269.      As mentioned above, nearly all error checking is 
  1270. returned through function results and then the codes are 
  1271. sent off to the ErrorMessage routine.  Exceptions to this 
  1272. are warning messages or those errors specific to SFM (such 
  1273. as "Windows must have different paths").
  1274.  
  1275.      Within the sfmSCRN.inc file there are two error 
  1276. routines that bear special explanations.  These are the 
  1277. AbortProgram and AbortOnError routines.  Since we are using 
  1278. custom interrupt handlers these must be turned off before 
  1279. the program is exited, including if SFM happens to be 
  1280. halted by an error.
  1281.  
  1282.      For this reason all exits of the program are routed 
  1283. through AbortProgram, with the exception of normal 
  1284. termination.  This way it is less likely that we will 
  1285. forget to turn off an interrupt handler or to turn the 
  1286. cursor back on.  An example of an error SFM does not handle 
  1287. is the case of a bad file allocation table.  Rather than 
  1288. take the chance of making things worse SFM will quit.
  1289.  
  1290.      The AbortOnError routine sets up a special error 
  1291. message that takes the place of the normal Turbo 
  1292. termination message.  It displays the same information in a 
  1293. slightly different format and also gives us the chance to 
  1294. call AbortProgram in order to return the system to normal 
  1295. as explained above.
  1296.  
  1297.      If you want to see AbortOnError in action just go into 
  1298. the SFM source code and change the line containing "{$C-}" 
  1299. to "{$C+}".  Then run SFM normally and type Ctrl-Break.  
  1300. You will then see the AbortOnError message screen.
  1301.  
  1302.      The address displayed here can then be entered in the 
  1303. Turbo "Find run-time error" option from the editor.  This 
  1304. allows you to find out where SFM was when you pressed the 
  1305. Ctrl-Break key sequence.
  1306.  
  1307.  
  1308.  
  1309.                   Super File Manager - 27 -
  1310.                COMPILER DIRECTIVES USED BY SFM
  1311.  
  1312.  
  1313.      Super File Manager uses a few of the Turbo compiler 
  1314. directives.  Setting the "C-" option is done simply to 
  1315. speed up screen displays and allow the use of the keyboard 
  1316. buffer.  The "I-" and "I+" compiler directives are 
  1317. described above under the DOS critical errors section.
  1318.  
  1319.      A less known compiler directive is "K-".  This 
  1320. directive tells Turbo to stop checking for a heap/stack 
  1321. collision whenever a procedure or function is called.  
  1322. While this is not always a good idea, it is done here 
  1323. because it saves SFM about five kilobytes of code space.  
  1324. We only take back about 500 bytes of this when we perform 
  1325. our own stack checking where it is needed.
  1326.  
  1327.      SFM performs stack checking mostly by making a call to 
  1328. MemoryAvail to find the amount of free space before issuing 
  1329. a GETMEM call.  The function MemoryAvail makes a call to 
  1330. the standard Turbo function MaxAvail and then uses this 
  1331. value to calculate the number of free bytes.  Before 
  1332. returning this number, SFM subtracts the number of bytes 
  1333. specified by the constant MinStack in order to provide a 
  1334. safety margin for procedure calls.
  1335.  
  1336.  
  1337.                       THE COPY ROUTINES
  1338.  
  1339.  
  1340.      The most frequently used procedure in SFM has to be 
  1341. CopyEntries.  This is the routine that performs the actual 
  1342. copy operations.  Therefore it deserves a bit of special 
  1343. attention.
  1344.  
  1345.      The first item of interest here is the BUFFER array.  
  1346. This array is made up of a small record that contains 
  1347. information about the file being copied.  The address field 
  1348. is a pointer to the start of the buffer.  The next field 
  1349. contains the index in the source directory that the buffer 
  1350. belongs to.  The size is simply how big this buffer is, as 
  1351. an unsigned integer.  The last field tells whether or not 
  1352. the file was too large to fit into one buffer (files over 
  1353. 64K) and that there is more to follow.
  1354.  
  1355.      Using this array of BUFFER records we may now begin a 
  1356. copy operation.  The first action is to open the file to 
  1357. read from and then allocate enough memory for that file.  
  1358. The OPENFILE procedure uses the newer DOS method for files, 
  1359. known as file handles.
  1360.  
  1361.  
  1362.                   Super File Manager - 28 -
  1363. COPYING FILES : continued
  1364.  
  1365.      If the file is too large for one buffer, or we run out 
  1366. of heap space, the MORE flag is set.  This is repeated 
  1367. until we run out of memory, files, or buffers.  Note that 
  1368. if we stop reading from a file before all of it is loaded 
  1369. the RHANDLE (read-handle) is not reset to zero.  This is 
  1370. our method for keeping track of whether or not part of a 
  1371. file has already been copied when we start the next read 
  1372. pass.  If a file is completely loaded then we call 
  1373. CLOSEFILE with RHANDLE, which also sets it to zero.
  1374.  
  1375.      After we have read in as much as possible we must then 
  1376. start writing back to the destination path.  We keep track 
  1377. of whether or not part of the file was already written by 
  1378. using the same method as with the read-handle.  If WHANDLE 
  1379. is set to zero then we must use CREATEFILE to make the new 
  1380. file.  Note that this function will fail if the file name 
  1381. already exists with the read-only attribute set, otherwise 
  1382. it will overwrite an existing file of the same name. If 
  1383. there is an error we allow the user the option of 
  1384. continuing with the next marked file or trying again.
  1385.  
  1386.      If the disk becomes full there are two possibilities.  
  1387. If the disk is in a floppy drive we can allow the user to 
  1388. continue by making a call to the ChangeCopyDisk procedure, 
  1389. otherwise we must abort the function.
  1390.  
  1391.      After a change of disks SFM will try to continue where 
  1392. it left off.  There are two cases where it will have to 
  1393. back up a bit and reload files that were already read in 
  1394. once before the disk change.
  1395.  
  1396.      One case is when the clear disk option has been used.  
  1397. The call to CLEARFAT caused SFM to set up a temporary disk 
  1398. transfer area in the program's heap space.  This transfer 
  1399. area occupies the same memory area as the copy buffers and 
  1400. will have overwritten the information there.  Therefore we 
  1401. must reload the files that were in the buffers.  Note that 
  1402. ChangeCopyDisk uses the SPLIT flag to indicate that a disk 
  1403. has been cleared.
  1404.  
  1405.      The second case offers insight into why I chose the 
  1406. name for the SPLIT flag.  This flag keeps an eye on the 
  1407. following special case:  
  1408.  
  1409.  
  1410.  
  1411.                   Super File Manager - 29 -
  1412.      While we are reading files into memory we hit the 
  1413.      memory or buffer limit and the current file is split 
  1414.      between two buffers.  Then after a successful write 
  1415.      pass we return to the read procedure and pick up 
  1416.      reading that file where we left off.  Let's say that 
  1417.      on the next write pass we run out of diskette space 
  1418.      while writing that file.  In this case the file is 
  1419.      deleted from the destination and we allow the user to 
  1420.      change disks.  Now we are ready to continue, but we 
  1421.      don't have the entire file in memory anymore.  We can 
  1422.      tell because the SPLIT flag is still true, so we must 
  1423.      return to the read pass and start over with that file.
  1424.  
  1425. That covers a not so obvious, but dangerous possibility.
  1426.  
  1427.      As a final step in the WriteTo procedure we must close 
  1428. the file handle and set the correct date and time.  When we 
  1429. close a file that was open for writing, DOS updates the 
  1430. time and date to the current system values.  To update 
  1431. these fields we are required to reopen the file for 
  1432. reading, use the appropriate DOS function for the update, 
  1433. and then close the file again.  The time and date we use is 
  1434. retrieved from the entry stored in memory for the source 
  1435. directory.
  1436.  
  1437.  
  1438.                        MENU TWO NOTES
  1439.  
  1440.  
  1441.      When menu two is entered SFM loads a copy of the 
  1442. directory into memory.  All of the functions from this menu 
  1443. will act on this copy and won't be updated on the disk 
  1444. until the update disk function is used.
  1445.  
  1446.      In addition to a copy of the current directory we also 
  1447. keep a copy of the file allocation table.  This FAT is only 
  1448. used for recovering files when using the undelete 
  1449. function.  If the disk is updated after an undelete 
  1450. operation then this FAT must also be written back to disk.
  1451.  
  1452.  
  1453.                       REARRANGING FILES
  1454.  
  1455.  
  1456.      While in menu two the directory may be sorted or 
  1457. rearranged using the pick up file function.  Both methods 
  1458. work only on the copy in memory and don't become permanent 
  1459. until updated.
  1460.  
  1461.      The Sort functions are fairly easy to understand.  
  1462. Just for the record, the sort used is an insertion sort.  
  1463. The major factors in choosing the type of sort used were 
  1464. whether or not it was stable and how difficult it would be 
  1465. to alter later.
  1466.  
  1467.  
  1468.                   Super File Manager - 30 -
  1469. REARRANGING FILES : continued
  1470.  
  1471.  
  1472.      You may notice the array of real numbers that gets set 
  1473. up for a couple of the sort fields.  This allows us to set 
  1474. up the keys for the directory once and thus avoid 
  1475. recalculating the keys every time we need them.  The time 
  1476. for one such pass is nothing compared to the exponential 
  1477. running time of the insertion sort.
  1478.  
  1479.      As a final note on the sort routines, I'm not sure why 
  1480. the character array comparisons work.  I thought the 
  1481. comparisons would have to be done on Turbo strings, but 
  1482. they seem to work fine with arrays of characters.  I 
  1483. couldn't find anything in the Turbo reference manual to 
  1484. either support or deny this.
  1485.  
  1486.      Now, about the procedures for picking up and dropping 
  1487. a directory entry.  If you look at them you'll see that I 
  1488. basically copied the routines that allow movement around 
  1489. the directory windows and modified them to suit this 
  1490. function.  I don't like this duplication of code, but lack 
  1491. the time to fix the problem.  Other that that, the 
  1492. procedures are fairly easy to understand.
  1493.  
  1494.  
  1495.                     SINGLE FLOPPY SYSTEMS
  1496.  
  1497.  
  1498.      Support has been provided for systems having a single 
  1499. floppy drive.  Persons with such systems may occasionally 
  1500. have use for a program such as SFM, but few programs 
  1501. provide support for them.  SFM does so by trapping the BIOS 
  1502. interrupt 10 (Video I/O).
  1503.  
  1504.      You may ask what this interrupt has to do with DOS and 
  1505. the answer is - not much.  However, DOS has a neat ability 
  1506. to allow a user to use a single drive as two drives.  When 
  1507. DOS wants access to the disk that is supposed to be in the 
  1508. other drive it asks the user to change disks.
  1509.  
  1510.      This works fine from the DOS command prompt, but not 
  1511. so well from within a program.  DOS uses the Write 
  1512. Character as Teletype BIOS Video function request to print 
  1513. its message.  When the bottom of the screen is reached this 
  1514. function scrolls the entire screen, without paying 
  1515. attention to any windows you may have specified.  When this 
  1516. happens the current display screen is ruined.
  1517.  
  1518.  
  1519.  
  1520.                   Super File Manager - 31 -
  1521. SINGLE FLOPPY SYSTEMS : continued
  1522.  
  1523.  
  1524.      The INT10 procedure in SFMVARS.INC handles this in a 
  1525. unique manner.  It takes over the BIOS interrupt 10 and 
  1526. when it is asked to write a character it uses the Turbo 
  1527. WRITE procedure instead.  This forces the message printed 
  1528. to conform to the current window specified.
  1529.  
  1530.      Granted, this is not the ideal way to handle the 
  1531. problem, but I don't know the DOS interrupt that handles 
  1532. the requests to change disks.  For this reason, I had to 
  1533. rely on a hunch that DOS used the specified BIOS interrupt 
  1534. to write its message.  What I ended up with is crude, but 
  1535. it works just fine.
  1536.  
  1537.                   Super File Manager - 32 -
  1538.                  >> SUGGESTED REFERENCES <<
  1539.  
  1540.  
  1541.      Here is a list of the books that I found useful in 
  1542. designing SFM.  The list isn't in a standard bibliography 
  1543. format, but then this isn't a Technical Writing class 
  1544. either.
  1545.  
  1546.  
  1547. Programmer's Reference Manual for IBM Personal Computers
  1548.      Steven Armbrust & Ted Forgeron
  1549.      1986: Dow Jones - Irwin
  1550.      
  1551.      This was by far the most used of my references.  It is 
  1552.      an excellent text that contains no-nonsense facts 
  1553.      about the majority of DOS and BIOS interrupts.  In 
  1554.      many cases there are short example programs written in 
  1555.      Assembly Language, Pascal and C (examples in this 
  1556.      document are not plagiarized, they are original).
  1557.  
  1558.  
  1559. The Complete Guide to IBM PC AT Assembly Language
  1560.      Harley Hahn
  1561.      1987: Scott, Foresman and Company
  1562.      
  1563.      Not used extensively for SFM, but is by far the best 
  1564.      Assembly Language book that I have seen.  It serves as 
  1565.      both an introductory text and reference manual.  You 
  1566.      don't have to own an AT to make use of it (my system 
  1567.      is an XT compatible).
  1568.  
  1569.  
  1570. The MS-DOS Handbook
  1571.      Richard Allen King
  1572.      1986: SYBEX, Inc.
  1573.      
  1574.      This is a pretty good reference, but I don't use it 
  1575.      nearly as much as the first book listed above.
  1576.  
  1577.  
  1578. That about wraps up my favorite references.  I have used 
  1579. others, but not enough to bother listing.
  1580.  
  1581.                   Super File Manager - 33 -
  1582.                     >> ACKNOWLEDGMENTS <<
  1583.  
  1584.  
  1585.      Here are some of the other sources that I found 
  1586. useful.
  1587.  
  1588.  
  1589. Finding Disk Parameters
  1590.      Glenn F. Roberts
  1591.      May 1986: PC Tech Journal
  1592.      Article covering the DOS function $32 request.
  1593.  
  1594.  
  1595. Dipping Into Directories
  1596.      Ted Mirecki
  1597.      February 1985: PC Tech Journal
  1598.      Article on some ideas for loading subdirectories into 
  1599.      memory and altering them.
  1600.  
  1601.  
  1602. INLINE Interrupts
  1603.      Charles C. Edwards
  1604.      December 1985: PC Tech Journal
  1605.      Article that contains some information on writing 
  1606.      interrupt handlers in Turbo Pascal.  It clarifies a 
  1607.      few problems with the Turbo documentation.
  1608.  
  1609.  
  1610. INT24
  1611.      Bela Lubkin
  1612.      May/June 1986: Programmer's Journal
  1613.      Article that contains the INT24 procedures used in 
  1614.      SFM.
  1615.  
  1616.  
  1617. DISPLAY.ARC
  1618.      Keith G. Chuvala
  1619.      File containing the DISPLAY procedure used in SFM.  It 
  1620.      also includes a documentation file.
  1621.  
  1622.  
  1623. INLINE.ARC
  1624.      L. David Baldwin
  1625.      1985,86
  1626.      File containing the inline assembler that I use and 
  1627.      another utility to turn inline code into its Assembly 
  1628.      Language equivalent.  Documentation is also included.
  1629.