Listing 1 (block.c) Main Interrupt Routine

#include   <dos.h>
#include   "block.h"

/*
 * normalize()
 *
 * normalize() guarantees that the offset portion of a far
 * pointer is as small as possible. A complete 20-bit address on
 * the processor can be calculated as
 *
 *     (segment * 16) + offset
 *
 * thus, the offset can be kept to a value between 0 and 15. I
 * use the FP_SEG and FP_OFF macro's in Microsoft's dos.h to
 * manipulate the segment and offset of the far pointer. If your
 * compiler doesn't support such a facility, see the  _rawscroll
 * routine in RAW.ASM, where I do it in assembly language.
 *
 * The whole point of this is to allow a lot of pointer
 * incrementing, using just the offset, without worrying about
 * wrapping around.
 */

static void normalize(p)
int far     **p;
   {
   offset       = FP_OFF(*p);
   FP_SEG(*p)   = FP_SEG(*p) + (offset >> 4);
   FP_OFF(*p)   = offset & 017;
   }

/*
 * interrupt()
 *
 * interrupt() takes care of the commands as they come in from
 * the request header. Because of the size of the RAM disk
 * buffer, the driver initialization could not be appended to the
 * back of the driver, and is in-line like everything else.
 */

void    interrupt()
   {
   command      = rh->command;
   start        = rh->b18.io.start;
   count        = rh->b18.io.count;
   transfer     = (int far *) rh->b14.transfer;
   switch (command)
       {
       case    0:       /* driver initialization */
          source            = ram_disk;
          FP_SEG(source)    = FP_SEG(source) + 0x1000;
          normalize(&source);
          rh->b14.transfer  = (char far *) source;
          rh->b18.bpb       = bpb_tab;
          rh->data          = 1;
          rh->status        = DONE;
          break;
       case    1:       /* media check */
          rh->b14.media_change_code   = 1;     /* disk has
                                 * not been changed */
          rh->status      = DONE;
          break;
       case    2:      /* build parameter block */
          rh->b18.bpb = &bpb;
          break;
       case    4:      /*  read */
       case    8:      /*  write */
       case    9:      /*  write with verify */

          If (start > MAX_BLK  ½½ count > MAX_BLK ½½
              start + count > MAX_BLK)
              {
              rh->status = BLK_NOT_FOUND ½ ERROR;
              break;
              }
          If (command == 4)
              {
              source = ram_disk;
              normalize(&source);
              source += (BLK_SIZE / sizeof(int)) * start;
              dest   = transfer;
              }
          else
              {
              source = transfer;
              dest   = ram_disk;
              normalize(&dest);
              dest   += (BLK_SIZE / sizeof(int)) * start;
              }
          normalize(&dest);
          normalize(&source);
          for (k1 = 0; k1 < count; k1++)
              for (k2 = 0; k2 < BLK_SIZE / sizeof(int); k2++)
                  *dest++ = *source++;
          rh->status = DONE;
          break;
       case    15:    /*  removable media check */
          rh->status = DONE | BUSY;
          break;
       case    5:     /*  non-destructive read */
       case    6:     /*  input status */
       case    7:     /*  flush input buffers */
       case    10:    /*  output status */
       case    11:    /*  flush output buffers */
       case    13:    /*  device open */
       case    14:    /*  device done */
          rh->status = DONE;
          break;
       case    3:     /*  ioctl read */
       case    12:    /*  ioctl write */
       default;
          rh->status = UNKNOWN_COMMAND | ERROR | DONE;
          break;
       }
    }