/* DOSSWAP.C - Functions to manage DOS swap areas */

#include <stdlib.h>
#include <dos.h>
#include <memory.h>
#include "tsr.h"

#define GET_DOSSWAP3       0x5d06
#define GET_DOSSWAP4       0x5d0b

#define SWAP_LIST_LIMIT    20     

struct  swap_list    /* format of DOS 4+ SDA list */
{
    void    far*    swap_ptr;
            int     swap_size;
};

/* variables for 3.x swap work */
static   char  far *  swap_ptr;  /* pointer to dos swap area */
static   char  far *  swap_save; /* pointer to our local save area */
static   int   swap_size_indos;
static   int   swap_size_always;
static   int   size;

/* variables for 4.x swap work */
static int swap_count;      /* count of swappable areas */
static struct swap_list swp_list[SWAP_LIST_LIMIT]; /*list of swap areas*/
static char far *swp_save[SWAP_LIST_LIMIT];   /* out save area */
static int swp_flag[SWAP_LIST_LIMIT];   /* flags if has been swapped */

static int dos_level;    /* for level dependent code */
int dos_critical;       /* in critical section, can't swap */

/*****
Function: InitDosSwap
Initialize pointers and sizes of DOS swap area. Return zero if success
*****/
int InitDosSwap(void)
{ 
    union  REGS regs;
    struct SREGS segregs;

    if ((_osmajor == 3) && (_osminor >= 10))
        dos_level = 3;
    else if (_osmajor >= 4)
        dos_level = 4;
    else
        dos_level = 0;
    
    if (dos_level == 3)    /* use 215D06 */
    {
        regs.x.ax = GET_DOSSWAP3;   
        intdosx(&regs,&regs,&segregs);
        /* pointer to swap area is returned in DS:SI */
        FP_SEG(swap_ptr) = segregs.ds;
        FP_OFF(swap_ptr) = regs.x.si;
   
        swap_size_indos = regs.x.cx;
        swap_size_always= regs.x.dx;

        size = 0;  /* initialize for later */
        return ((swap_save = malloc(swap_size_indos)) == 0);
    }
/*
    NOTE: Next line changed from version in UNDOCUMENTED DOS, p. 322
    (first printing), where we tested DOS version number with == and
    thereby lose the opportunity to run in DOS 5. This was just the
    sort of thing we had warned against in Chapter 2 of the book!
*/
    else if (dos_level >= 4)  /* use 5d0b */
    {
        struct swap_list far *ptr;
        int far *iptr;
        int i;
        regs.x.ax = GET_DOSSWAP4;   
        intdosx(&regs,&regs,&segregs);
        /* pointer to swap list is returned in DS:SI */
        FP_SEG(iptr) = segregs.ds;
        FP_OFF(iptr) = regs.x.si;
        swap_count = *iptr;                   /* get size of list */
        iptr++;
        ptr = (struct swap_list far *) iptr;  /* create point to list */

        if (swap_count > SWAP_LIST_LIMIT)     /* too many data areas */
            return 2;

        /* get pointers and sizes of data areas */
        for (i = 0; i < swap_count; i++)
        {
            swp_list[i].swap_ptr = ptr->swap_ptr;
            swp_list[i].swap_size= ptr->swap_size;
            if (! (swp_save[i] = malloc(swp_list[i].swap_size & 0x7fff)))
                return 3;   /* out of memory */
            swp_flag[i] = 0;
            ptr++;   /* point to next entry in the list */
        }
        return 0;
    }
    else
        return 1;   /* unsupported DOS */
}

/*****
Function: SaveDosSwap
This function will save the dos swap area to a local buffer
It returns zero on success, non-zero meaning can't swap 
*****/ 
int SaveDosSwap(void)
{
    if (dos_level == 3)
    {
        if (swap_ptr && !dos_critical)   
        {
            /* if INDOS flag is zero, use smaller swap size */
            size = (*indos_ptr) ? swap_size_indos : swap_size_always;
              
            movedata(FP_SEG(swap_ptr),  FP_OFF(swap_ptr), 
                     FP_SEG(swap_save), FP_OFF(swap_save),
                     size);
        }
        else       /* can't swap it */
            return 1;
    }
    else if (dos_level == 4)
    {
        /* loop through pointer list and swap appropriate items */
        int i;
        for (i = 0; i < swap_count; i++)
        {
            if (swp_list[i].swap_size & 0x8000)  /* swap always */
            {
                movedata(FP_SEG(swp_list[i].swap_ptr), 
                         FP_OFF(swp_list[i].swap_ptr),
                         FP_SEG(swp_save[i]),          
                         FP_OFF(swp_save[i]),
                         swp_list[i].swap_size & 0x7fff);
            }
            else if (*indos_ptr)     /* swap only if dos busy */
            {
                movedata(FP_SEG(swp_list[i].swap_ptr), 
                         FP_OFF(swp_list[i].swap_ptr),
                         FP_SEG(swp_save[i]),          
                         FP_OFF(swp_save[i]),
                         swp_list[i].swap_size);
            }
        }
    }
    else 
        return 1;
   
    return 0;
}

/*****
Function: RestoreDosSwap
This function will restore a previously swapped dos data area
*****/
void RestoreDosSwap(void)
{
    if (dos_level == 3)
    {
        /* make sure its already saved and we have a good ptr */
        if (size && swap_ptr)
        {
            movedata(FP_SEG(swap_save), FP_OFF(swap_save), 
                     FP_SEG(swap_ptr),  FP_OFF(swap_ptr), size);
            size = 0;
        }
    }
    else if (dos_level == 4)
    {
        int i;
        for (i = 0; i < swap_count; i++)
        {
            movedata(FP_SEG(swp_save[i]),          
                     FP_OFF(swp_save[i]),
                     FP_SEG(swp_list[i].swap_ptr), 
                     FP_OFF(swp_list[i].swap_ptr),
                     swp_list[i].swap_size);
           swp_flag[i] = 0;   /* clear flag */
        }
    }
}   
