/*
	SetCPU V1.60
	by Dave Haynie, April 13, 1990
	Released to the Public Domain

	MISC.C MODULE
	
	This module is responsible for managing ROM image patchs.
*/

#include "setcpu.h"


/* ====================================================================== */

/* This replaces the Lattice "stricmp()" function, plus it's a better form
   for my needs here. */
   
LONG striequ(s1,s2)
char *s1,*s2;
{
   LONG aok = FALSE;
   
   while (*s1 && *s2 && (aok = (*s1++ & 0xdf) == (*s2++ & 0xdf)));
   return (LONG) (!*s1 && !*s2 && aok);
}

LONG strniequ(s1,s2,n)
char *s1,*s2;
unsigned n;
{
   LONG aok = FALSE;
   
   while (n-- && *s1 && *s2 && (aok = (*s1++ & 0xdf) == (*s2++ & 0xdf)));
   return aok;
}

/* ====================================================================== */

/* The device I/O functions. */

/* This routine turns off the motor. */

void MotorOff(req)
struct IOStdReq *req;
{
   req->io_Length = 0L;
   req->io_Command = TD_MOTOR;
   (void)DoIO((struct IORequest *)req);
}

/* This function fills the buffer "buf" with the contents of the given
   sector number.  Returns 0 if there's no error.  */

BYTE ReadBuf(buf,sect,req)
char *buf;
LONG sect;
struct IOStdReq *req;
{
   req->io_Length = 512L;
   req->io_Data = (APTR) buf;
   req->io_Command = CMD_READ;
   req->io_Offset = (512L * sect);
   (void)DoIO((struct IORequest *)req);
   return req->io_Error;
}

/* This function takes in a DOS name, and returns either the 
   trackdisk.device unit that it corresponds to, or -1L. */
   
LONG CheckTDDev(name)
char *name;
{
   char *devname;
   struct DosLibrary *dl;
   struct RootNode *dr;
   struct DosInfo *di;
   struct DeviceNode *dev;
   struct FileSysStartupMsg *start;
   struct DosEnvec *env;

   dl = (struct DosLibrary *)OpenLibrary("dos.library",0L);
   dr = (struct RootNode *)dl->dl_Root;
   di = (struct DosInfo *)BADDR(dr->rn_Info);
   dev = (struct DeviceNode *)BADDR(di->di_DevInfo);
   CloseLibrary((struct Library *)dl);

  /* Next we find the device */
   if (name[strlen(name)-1] == ':') name[strlen(name)-1] = '\0';

   for (; dev != NULL; dev = (struct DeviceNode *)BADDR(dev->dn_Next)) {
      if (dev->dn_Type != DLT_DEVICE) continue;
      devname = (char *)BADDR(dev->dn_Name);
      if (strniequ(name,devname+1,(unsigned)devname[0])) break;
   }

  /* Is it a valid trackdisk.device? */
   if (!dev) return -1L;
   if (!(start = (struct FileSysStartupMsg *)BADDR(dev->dn_Startup))) return -1L;
   env = (struct DosEnvec *)BADDR(start->fssm_Environ);
   if (env->de_BlocksPerTrack != 11L || env->de_LowCyl != 0L) return -1L;
   devname = (char *)BADDR(start->fssm_Device);
   if (!strniequ(devname+1,"trackdisk.device",16)) return -1L;

   return start->fssm_Unit;
}

/* ====================================================================== */

/* The patch manager stuff. */

/* The "JSR address" opcode used for my patches. */

#define JSR_OPCODE	0x4eb9

/* These are for some string patches */

static char DiskS[] = "SALV to recover it! ";

/* These are the V1.3 patches */

struct pitem PL_345[] = {
   { PT_KEYBOARD,0,0x2528a,  0L, NULL },		/* Keyboard... */
   { PT_STRING,  0,0x3fe19, 20L, (UWORD *)DiskS },	/* "DiskDoctor" name */
   { PT_END,     0,      0,  0L, NULL }
};

struct pitem PL_33180[] = {
   { PT_KEYBOARD,0,0x2572a,  0L, NULL },		/* Keyboard... */
   { PT_STRING,  0,0x3fe11, 20L, (UWORD *)DiskS },	/* "DiskDoctor" name */
   { PT_END,     0,      0,  0L, NULL }
};

/* This is main system patch list. */

struct patch SystemPatch[] = {
   { &SystemPatch[1], &PL_345[0],34,5 },
   { NULL, &PL_33180[0],33,180 }
};

/* This is a pointer to the base of the last applied patch, for making
   patch links.  The structure of any applied allocated patch, such as
   a JSR patch, is a link pointer, the actual patch size, and then the
   patch code.  This lets any version of SetCPU remove allocated patches
   from any other version, providing it can locate the tag. */

struct MemChunk *lastpatch = NULL;

/* This routine applys the patch */

LONG AddPatch(rom,lst,tag)
ULONG rom;
struct patch *lst;
struct systag *tag;
{
   UWORD *kickver = (UWORD *)(0x100000C - ( * (ULONG *) 0xFFFFEC ));
   UWORD *addr, *base;
   struct pitem *item;
   struct MemChunk *chunk;
   LONG any = FALSE;

   while (lst) {
      if (lst->Version == kickver[0] && lst->Revision == kickver[1]) {
         for (item = &lst->list[0]; item->Type != PT_END; ++item) {
            any = TRUE;
            switch (item->Type) {
               case PT_IGNORE :
                  break;
               case PT_STRING :
                  MemCopy(item->Code,(char *)(rom+item->Offset),item->Length);
                  ++tag->patches;
                  break;
               case PT_JSR    :
                  chunk = (struct MemChunk *)AllocMem(item->Length+14L,0L);
                  chunk->mc_Next = lastpatch;
                  lastpatch = chunk;
                  chunk->mc_Bytes = item->Length+14L;
                  addr = (UWORD *)(((ULONG)chunk)+8L);
                  base = (UWORD *)(rom+item->Offset);
                  MemCopy(base,addr,6L);
                  base[0] = (UWORD)JSR_OPCODE;
                  base[1] = (UWORD)(((ULONG)addr)>>16L);
                  base[2] = (UWORD)(((ULONG)addr)&0x0ffff);
                  MemCopy(item->Code,&addr[3],item->Length);
                  ++tag->patches;
                  break;
            }
         }
      }
      lst = lst->next;
   }
   return any;
}
























