            
/*
 * Parts of http://www.diskwarez.com/disklib.htm combined:
 * dos\gcc\ dosmem.h dosmem.c read.c write.c - just the parts
 * that we need for DPMI raw DOS disk access. Cut and paste by
 * Eric Auer 2002 ... Imre Leber mails:
 *
 * Notice that i we have special written confirmation from Gregg
 * Jennings that anybody in the FreeDOS project is allowed to use
 * the disklib for anything.
 *
 * This file was part of the BETA version of DISKLIB
 * Copyright (C) 1998, Gregg Jennings
 *
 * See README.TXT for information about re-distribution.
 * See DISKLIB.TXT for information about usage.
 *
 */

#include <dos.h>
#include <go32.h>
#include <dpmi.h>
#include <sys/movedata.h>

#include "types.h" /* __{u,s}{8,16,32,64} */

#include <stdio.h>
#include <signal.h>

#pragma pack(1) /* needed for the following struct */

struct DCB {
    __u32  sector;
    __u16  number;
    void  *buffer;
}; /* control block for the > 32 MB disk access with "int" 25/26h */

#pragma pack()

#define DISK_OK 0
#define DOS_ERR (-1)
#define MEM_ERR (-3)

#define secsize 512

/* ************************************************************** */

#define dosmalloc(siz,sel)	  __dpmi_allocate_dos_memory((siz+15)>>4,sel)
/* dosmalloc returns a seg, -1 if memory exhausted */
#define setdosmem(b,siz,sel)  _movedatab(_my_ds(),(unsigned int)b,sel,0,siz)
#define getdosmem(b,siz,sel)  _movedatab(sel,0,_my_ds(),(unsigned int)b,siz)
#define dosfree(sel)		  __dpmi_free_dos_memory(sel)
#define _MK_FP(seg) 		  (void *)(seg << 16)

/* ************************************************************** */

/*
 * from: dos\gcc\read.c       absolute disk read
 */

/*
 * disk_read        absolute disk read (INT 25h)
 */

extern int disk_read(int disk, long sector, void *buffer, int nsecs)
{
__dpmi_regs regs;
int seg,sel;

    if ((seg = dosmalloc(secsize*nsecs,&sel)) == -1)
        return MEM_ERR;

    regs.x.ax = disk;
    regs.x.dx = sector;
    regs.x.cx = nsecs;
    regs.x.ds = seg;
    regs.x.bx = 0;

    __dpmi_int(0x25,&regs); /* READ */

    /* This is read specific: */
    getdosmem(buffer,secsize*nsecs,sel);

    dosfree(sel);

    if (regs.x.flags & 1) { return regs.h.al; }
    return DISK_OK;
}

/*
 * disk_read_ext    absolute disk read (INT 25h), > 32MB
 */

extern int disk_read_ext(int disk, long sector, void *buffer, int nsecs)
{
__dpmi_regs regs;
struct DCB dcb;
int seg,sel;
int bseg,bsel;

    if ((seg = dosmalloc(sizeof(struct DCB),&sel)) == -1)
        return MEM_ERR;

    if ((bseg = dosmalloc(secsize*nsecs,&bsel)) == -1) {
        dosfree(sel);
        return MEM_ERR;
    }

    regs.x.ax = disk;
    regs.x.cx = 0xffff;
    dcb.sector = sector;
    dcb.number = nsecs;
    dcb.buffer = _MK_FP(bseg);
    setdosmem((void *)&dcb,sizeof(struct DCB),sel);
    regs.x.ds = seg;
    regs.x.bx = 0;

    __dpmi_int(0x25,&regs); /* READ */

    /* This is read specific: */
    getdosmem(buffer,secsize*nsecs,bsel);

    dosfree(sel);
    dosfree(bsel);

    if (regs.x.flags & 1) { return regs.h.al; }
    return DISK_OK;
}

/*
 * disk_read32      absolute disk read FAT12/16/32
 *
 * Note: This function will fail without setting the carry flag
 *       under MS-DOS and Windows NT 4.0 !?!?
 *       This function fails under Windows 95.
 *       This function reads all drives under Windows 98.
 */

extern int disk_read32(int disk, long sec, void *buf, int nsecs)
{
__dpmi_regs regs;
int seg,sel;
struct DCB Dcb;
struct DCB *dcb = &Dcb;


    if ((seg = dosmalloc(secsize*nsecs,&sel)) == -1)
        return MEM_ERR;

    /* could zero buffer here: buf, nsecs*secsize */

    dcb->sector = sec;
    dcb->number = (unsigned short)nsecs;
    dcb->buffer = buf;
    regs.x.ax = 0x7305; /* DOS_EXT_ABS_READ_WRITE */
    regs.x.dx = disk+1;
    regs.x.cx = (unsigned short)-1;
    regs.x.bx = 0;
    regs.x.ds = (int)dcb;

    /* dangerous function! SI:0 MUST BE 0 for READ, 1 for WRITE */
    regs.x.si = 0;

    __dpmi_int(0x21,&regs); /* FAT32 */

    if (regs.x.ax == 0x7300) {          /* fail indication DOS/NT */
        regs.x.flags = 1;
        setdoserror(1);
    }

    /* This is read specific: */
    getdosmem(buf,secsize*nsecs,sel);

    dosfree(sel);

    if (regs.x.flags & 1) { return DOS_ERR; }
    return DISK_OK;
}

/* ************************************************************** */

/*
 * from: dos\gcc\write.c      absolute disk write
 */

/*
 * disk_write       absolute disk write (INT 26h)
 */

extern int disk_write(int disk, long sector, void *buffer, int nsecs)
{
int seg,sel;
__dpmi_regs regs;

    if ((seg = dosmalloc(secsize*nsecs,&sel)) == -1)
        return MEM_ERR;

    /* This is write specific: */
    setdosmem(buffer,secsize*nsecs,sel);

    regs.x.ax = disk;
    regs.x.dx = sector;
    regs.x.cx = nsecs;
    regs.x.ds = seg;
    regs.x.bx = 0;

    __dpmi_int(0x26,&regs); /* WRITE */

    dosfree(sel);

    if (regs.x.flags & 1) { return regs.h.al; }
    return DISK_OK;
}

/*
 * disk_write_ext   absolute disk write (INT 26h), > 32MB
 */

extern int disk_write_ext(int disk, long sector, void *buffer, int nsecs)
{
int seg,sel;
int bseg,bsel;
struct DCB dcb;
__dpmi_regs regs;

    if ((seg = dosmalloc(sizeof(struct DCB),&sel)) == -1)
        return MEM_ERR;

    if ((bseg = dosmalloc(secsize*nsecs,&bsel)) == -1) {
        dosfree(sel);
        return MEM_ERR;
    }

    /* This is write specific: */
    setdosmem((void *)buffer,secsize*nsecs,bsel);

    regs.x.ax = disk;
    regs.x.cx = 0xffff;
    dcb.sector = sector;
    dcb.number = nsecs;
    dcb.buffer = _MK_FP(bseg);
    setdosmem((void *)&dcb,sizeof(struct DCB),sel);
    regs.x.ds = seg;
    regs.x.bx = 0;

    __dpmi_int(0x26,&regs); /* WRITE */

    dosfree(sel);
    dosfree(bsel);

    if (regs.x.flags & 1) { return regs.h.al; }
    return DISK_OK;
}

/*
 * disk_write32      absolute disk write FAT12/16/32
 *
 * Note: This function will fail without setting the carry flag
 *       under MS-DOS and Windows NT 4.0 !?!?
 *       This function fails under Windows 95.
 *       This function writes all drives under Windows 98. ???
 */

extern int disk_write32(int disk, long sec, void *buf, int nsecs)
{
int seg,sel;
__dpmi_regs regs;
struct DCB dcb;

    if ((seg = dosmalloc(secsize*nsecs,&sel)) == -1)
        return MEM_ERR;

    /* This is write specific: */
    /* seems to have been wrong in disklib! */
    setdosmem((void *)buf,secsize*nsecs,sel);

    dcb.sector = sec;
    dcb.number = (unsigned short)nsecs;
    dcb.buffer = buf;
    regs.x.ax = 0x7305; /* DOS_EXT_ABS_READ_WRITE */
    regs.x.dx = disk+1;
    regs.x.cx = (unsigned short)-1;
    regs.x.bx = 0;
    regs.x.ds = (int)&dcb;

    /* dangerous function! SI:0 MUST BE 0 for READ, 1 for WRITE */
    regs.x.si = 1;

    __dpmi_int(0x21,&regs); /* FAT32 */

    if (regs.x.ax == 0x7300) {          /* fail indication DOS/NT */
        regs.x.flags = 1;
        setdoserror(1);
    }

    dosfree(sel);

    if (regs.x.flags & 1) { return DOS_ERR; }
    return DISK_OK;
}
