 /*
  * UAE - The Un*x Amiga Emulator
  *
  * Hardfile emulation
  *
  * Copyright 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 "autoconf.h"
#include "filesys.h"
#include "execlib.h"

static int opencount = 0;

static uae_u32 hardfile_open (void)
{
    uaecptr tmp1 = m68k_areg(regs, 1); /* IOReq */

    /* Check unit number */
    if (get_hardfile_data (m68k_dreg (regs, 0))) {
	opencount++;
	put_word (m68k_areg(regs, 6)+32, get_word (m68k_areg(regs, 6)+32) + 1);
	put_long (tmp1 + 24, m68k_dreg (regs, 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, (uae_u32)-1);
    put_byte (tmp1 + 31, (uae_u8)-1);
    return (uae_u32)-1;
}

static uae_u32 hardfile_close (void)
{
    opencount--;
    put_word (m68k_areg(regs, 6) + 32, get_word (m68k_areg(regs, 6) + 32) - 1);

    return 0;
}

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

static uae_u32 hardfile_beginio (void)
{
    uae_u32 tmp1, tmp2, dataptr, offset;
    uae_u32 retval = m68k_dreg(regs, 0);
    int unit;
    struct hardfiledata *hfd;

    tmp1 = m68k_areg(regs, 1);
    unit = get_long (tmp1 + 24);
#undef DEBUGME
#ifdef DEBUGME
    printf ("hardfile: unit = %d\n", unit);
    printf ("hardfile: tmp1 = %08lx\n", (unsigned long)tmp1);
    printf ("hardfile: cmd  = %d\n", (int)get_word(tmp1+28));
#endif
    hfd = get_hardfile_data (unit);

    put_byte (tmp1+8, NT_MESSAGE);
    put_byte (tmp1+31, 0); /* no error yet */
    tmp2 = get_word (tmp1+28); /* io_Command */
/*    put_byte (tmp1 + 30, get_byte (tmp1 + 30) & ~1);*/
    switch (tmp2) {
     case CMD_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 > (uae_u32)hfd->size)
	    goto bad_command;

	put_long (tmp1 + 32, tmp2); /* set io_Actual */
	fseek (hfd->fd, offset, SEEK_SET);
	while (tmp2) {
	    int i;
	    char buffer[512];
	    fread (buffer, 1, 512, hfd->fd);
	    for (i = 0; i < 512; i++, dataptr++)
		put_byte(dataptr, buffer[i]);
	    tmp2 -= 512;
	}
	break;

     case CMD_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 > (uae_u32)hfd->size)
	    goto bad_command;

	put_long (tmp1 + 32, tmp2); /* set io_Actual */
	fseek (hfd->fd, offset, SEEK_SET);
	while (tmp2) {
	    char buffer[512];
	    int i;
	    for (i=0; i < 512; i++, dataptr++)
		buffer[i] = get_byte(dataptr);
	    fwrite (buffer, 1, 512, hfd->fd);
	    tmp2 -= 512;
	}
	break;

	bad_command:
	break;

     case 18: /* GetDriveType */
	printf ("Shouldn't happen\n");
	put_long (tmp1 + 32, 1); /* not exactly a 3.5" drive, but... */
	break;

     case 19: /* GetNumTracks */
	printf ("Shouldn't happen 2\n");
	put_long (tmp1 + 32, 0);
	break;

	/* Some commands that just do nothing and return zero */
     case CMD_UPDATE:
     case CMD_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, (uae_u8)-3); /* io_Error */
	retval = 0;
	break;
    }
#if 0
    if ((get_byte (tmp1+30) & 1) == 0) {
	/* Not IOF_QUICK -- need to ReplyMsg */
	m68k_areg(regs, 1) = tmp1;
	CallLib (get_long(4), -378);
    }
#endif
    return retval;
}

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

void hardfile_install (void)
{
    uae_u32 functable, datatable;
    uae_u32 initcode, openfunc, closefunc, expungefunc;
    uae_u32 beginiofunc, abortiofunc;

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

    /* initcode */
#if 0
    initcode = here ();
    calltrap (deftrap (hardfile_init)); dw (RTS);
#else
    initcode = filesys_initcode;
#endif
    /* 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);

    /* BeginIO */
    beginiofunc = here ();
    calltrap (deftrap (hardfile_beginio));
    dw (0x48E7); dw (0x8002); /* movem.l d0/a6,-(a7) */
    dw (0x0829); dw (0); dw (30); /* btst #0,30(a1) */
    dw (0x6608); /* bne.b +8 */
    dw (0x2C78); dw (0x0004); /* move.l 4,a6 */
    dw (0x4EAE); dw (-378); /* jsr ReplyMsg(a6) */
    dw (0x4CDF); dw (0x4001); /* movem.l (a7)+,d0/a6 */
    dw (RTS);

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

    /* FuncTable */
    functable = here ();
    dl (openfunc); /* Open */
    dl (closefunc); /* Close */
    dl (expungefunc); /* Expunge */
    dl (EXPANSION_nullfunc); /* Null */
    dl (beginiofunc); /* BeginIO */
    dl (abortiofunc); /* AbortIO */
    dl (0xFFFFFFFFul); /* 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);
}
