/*
 *  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;
        }
    }

