 /*
  * UAE - The Un*x Amiga Emulator
  *
  * AutoConfig devices
  *
  * (c) 1995 Bernd Schmidt
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include "config.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "disk.h"
#include "xwin.h"
#include "autoconf.h"

static int opencount = 0;
static int uaedevfd;
int numtracks = 512;

static ULONG hardfile_init(void)
{
    ULONG tmp1, devicenode, bootnode;
    int have36 = 1;
    ULONG retval = regs.d[0];

    filesys_init();

    return retval;
}

static ULONG hardfile_open(void)
{
    CPTR tmp1 = regs.a[1]; /* IOReq */

    /* Check unit number */
    if (regs.d[0] == 0) {
	opencount++;
	put_word (regs.a[6]+32, get_word (regs.a[6]+32) + 1);
	put_long (tmp1+24, 0); /* io_Unit */
	put_byte (tmp1+31, 0); /* io_Error */
	put_byte (tmp1+8, 7); /* ln_type = NT_REPLYMSG */
	return 0;
    }

    put_long (tmp1+20, (ULONG)-1);
    put_byte (tmp1+31, (UBYTE)-1);
    return (ULONG)-1;
}

static ULONG hardfile_close(void)
{
    opencount--;
    put_word (regs.a[6]+32, get_word (regs.a[6]+32) - 1);

    return regs.d[0];
}

static ULONG hardfile_expunge(void)
{
    return 0; /* Simply ignore this one... */
}

static ULONG hardfile_beginio(void)
{
    ULONG tmp1, tmp2, dataptr, offset;
    ULONG retval = regs.d[0];

    tmp1 = regs.a[1];
    put_byte (tmp1+8, 5); /* set ln_type to NT_MESSAGE */
    put_byte (tmp1+31, 0); /* no error yet */
    tmp2 = get_word (tmp1+28); /* io_Command */
    switch (tmp2) {
     case 2: /* Read */
	dataptr = get_long (tmp1 + 40);
	if (dataptr & 1)
	    goto bad_command;
	offset = get_long (tmp1 + 44);
	if (offset & 511)
	    goto bad_command;
	tmp2 = get_long (tmp1 + 36); /* io_Length */
	if (tmp2 & 511)
	    goto bad_command;
	if (tmp2 + offset > (ULONG)numtracks * 32 * 512)
	    goto bad_command;

	put_long (tmp1 + 32, tmp2); /* set io_Actual */
	lseek (uaedevfd, offset, SEEK_SET);
	while (tmp2) {
	    int i;
	    char buffer[512];
	    read (uaedevfd, buffer, 512);
	    for (i = 0; i < 512; i++, dataptr++)
		put_byte(dataptr, buffer[i]);
	    tmp2 -= 512;
	}
	break;

     case 3: /* Write */
     case 11: /* Format */
	dataptr = get_long (tmp1 + 40);
	if (dataptr & 1)
	    goto bad_command;
	offset = get_long (tmp1 + 44);
	if (offset & 511)
	    goto bad_command;
	tmp2 = get_long (tmp1 + 36); /* io_Length */
	if (tmp2 & 511)
	    goto bad_command;
	if (tmp2 + offset > (ULONG)numtracks * 32 * 512)
	    goto bad_command;

	put_long (tmp1 + 32, tmp2); /* set io_Actual */
	lseek (uaedevfd, offset, SEEK_SET);
	while (tmp2) {
	    char buffer[512];
	    int i;
	    for (i=0; i < 512; i++, dataptr++)
		buffer[i] = get_byte(dataptr);
	    write (uaedevfd, buffer, 512);
	    tmp2 -= 512;
	}
	break;

	bad_command:
	break;

     case 18: /* GetDriveType */
	put_long (tmp1 + 32, 1); /* not exactly a 3.5" drive, but... */
	break;

     case 19: /* GetNumTracks */
	put_long (tmp1 + 32, numtracks);
	break;

	/* Some commands that just do nothing and return zero */
     case 4: /* Update */
     case 5: /* Clear */
     case 9: /* Motor */
     case 10: /* Seek */
     case 12: /* Remove */
     case 13: /* ChangeNum */
     case 14: /* ChangeStatus */
     case 15: /* ProtStatus */
     case 20: /* AddChangeInt */
     case 21: /* RemChangeInt */
	put_long (tmp1+32, 0); /* io_Actual */
	retval = 0;
	break;

     default:
	/* Command not understood. */
	put_byte (tmp1+31, (UBYTE)-3); /* io_Error */
	retval = 0;
	break;
    }
    if ((get_byte (tmp1+30) & 1) == 0) {
	/* Not IOF_QUICK -- need to ReplyMsg */
	regs.a[1] = tmp1;
	CallLib (get_long(4), -378);
    }
    return retval;
}

static ULONG hardfile_abortio(void)
{
    return (ULONG)-3;
}

void hardfile_install(void)
{
    ULONG functable, datatable, inittable;
    ULONG initcode, openfunc, closefunc, expungefunc;
    ULONG nullfunc, beginiofunc, abortiofunc;

    uaedevfd = open ("hardfile", O_RDWR | O_BINARY);

    ROM_hardfile_resname = ds("hardfile.device");
    ROM_hardfile_resid = ds("UAE hardfile.device 0.2");

    numtracks = hardfile_size / 16384;

    /* initcode */
    initcode = here();
    calltrap(deftrap(hardfile_init)); dw(RTS);

    /* Open */
    openfunc = here();
    calltrap(deftrap(hardfile_open)); dw(RTS);

    /* Close */
    closefunc = here();
    calltrap(deftrap(hardfile_close)); dw(RTS);

    /* Expunge */
    expungefunc = here();
    calltrap(deftrap(hardfile_expunge)); dw(RTS);

    /* Null */
    nullfunc = here();
    dw(0x7000); /* return 0; */
    dw(RTS);

    /* BeginIO */
    beginiofunc = here();
    calltrap(deftrap(hardfile_beginio)); dw(RTS);

    /* AbortIO */
    abortiofunc = here();
    calltrap(deftrap(hardfile_abortio)); dw(RTS);

    /* FuncTable */
    functable = here();
    dl(openfunc); /* Open */
    dl(closefunc); /* Close */
    dl(expungefunc); /* Expunge */
    dl(nullfunc); /* Null */
    dl(beginiofunc); /* BeginIO */
    dl(abortiofunc); /* AbortIO */
    dl(0xFFFFFFFF); /* end of table */

    /* DataTable */
    datatable = here();
    dw(0xE000); /* INITBYTE */
    dw(0x0008); /* LN_TYPE */
    dw(0x0300); /* NT_DEVICE */
    dw(0xC000); /* INITLONG */
    dw(0x000A); /* LN_NAME */
    dl(ROM_hardfile_resname);
    dw(0xE000); /* INITBYTE */
    dw(0x000E); /* LIB_FLAGS */
    dw(0x0600); /* LIBF_SUMUSED | LIBF_CHANGED */
    dw(0xD000); /* INITWORD */
    dw(0x0014); /* LIB_VERSION */
    dw(0x0004); /* 0.4 */
    dw(0xD000);
    dw(0x0016); /* LIB_REVISION */
    dw(0x0000);
    dw(0xC000);
    dw(0x0018); /* LIB_IDSTRING */
    dl(ROM_hardfile_resid);
    dw(0x0000); /* end of table */

    ROM_hardfile_init = here();
    dl(0x00000100); /* ??? */
    dl(functable);
    dl(datatable);
    dl(initcode);

    if (uaedevfd >= 0) {
	CPTR initfunc;
	
	add_filesys_unit(NULL, "hardfile", 1);
    }
}
