/* MAP.COM

   Copyright (c)1995 Gert de Boom

   History (DD-MM-YY)
   xx-07-95    First version (beta)
   25-01-98    Translated comments to English
	         Please note this is an early beta version and not
  	         final version which was distributed (for some reason
		   this one seems to be disappeared from my harddisk)

   To do :  - Read/Write IDs handle in good way + set in workArea
              (oldMSX, Extended)
            - Include MAK 3.0 support
            - Get name from 1st directory sector and not from bootsector
            - Set extra bits in last fhm-fields (extended)
            - Execute Flush drive before deleting from workArea
            - Check for not disabeling boot partition
            - Do not map in not supported partitions
*/

#define SetWD3393       0x7f83
#define TermAct         0x7f86
#define RdSecDir        0x7f89
#define WrSecDir        0x7f8c
#define ReqSense        0x7f8f
#define Inquiry         0x7f92
#define ReadCap         0x7f95
#define ModeSense       0x7f98
#define ModeSel         0x7f9b
#define FmtUnit         0x7f9e
#define TstUnitRdy      0x7fa1
#define Initialise      0x7fa4
#define InsWork         0x7fa7
#define ClrEndLn        0x7faa
#define Verify          0x7fad
#define StrtStpUn       0x7fb0
#define SndDiag         0x7fb3
#define RdDefect        0x7fbf
#define GetWrk		0x7fc2
#define PartInfo        0x7fc5
#define GetUnitsOn      0x7fc8
#define SetHOST_ID      0x7fcb
#define SetTargetID     0x7fce
#define GetTargetID     0x7fd1
#define GetHOST_ID      0x7fd4
#define GetSense        0x7fd7
#define MedRemoval      0x7fda

#define UNKNOWN    0
#define MAK	   1  /* Mak 3.0 , max. 8 partitions */
#define EXTENDED   2  /* Extended, max. 15 partitions */
#define OLDPC	   3  /* Old PC, max. 4 partitions */
#define OLDMSX	   4  /* Old MSX, max. 6 partitions */

#define off 0
#define on  1         /* Message on; request of Jurgen (shells) */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <msxbios.h>
#include <bdosfunc.h>

int slot;         /* slot no., known after initScsi */
int message;      /* Message on/off */
int lPartition;   /* no. of last partition */

struct PARTITION
{
 char pfxtar;         /* P---FXTAR, see SCSI manual      */
 TINY sa, sb, sc;     /* Start sector in TINY's          */
 TINY ea, eb;         /* Length in TINY's                */
 char wnnn;           /* W-----NNN, see manual           */
 char fhm;            /* F------HM, see manual           */
 char volumename[12]; /* Volumename out of first sector  */
 int type;            /* kind of partition               */
} partitions[15];     /* 15: yet the max. no. partitions */

int getHostId()
{
 REGS reg;
 calslt((TINY)slot, GetHOST_ID, &reg);
 return ((int)reg.a);
}

int isUnitReady(id)
int id;
{
 REGS reg;
 if (id == getHostId()) return 2;
 reg.a = (TINY)id;
 calslt((TINY)slot, TstUnitRdy, &reg);
 return (reg.de/0xFF != 0x02);
}

int getType(bsect)
char *bsect;
{
 int c1, c2, i, j, result;
 c1 = (int)bsect[0x1fe] % 0xFF00;
 c2 = (int)bsect[0x1ff] % 0xFF00;
 result = 0;
 if (c1==0xEB && c2 == 0xEB)
  result = MAK;
 if (!result && c1==0x55 && c2 == 0xAA)
 {
  for (i=1; i<=4; i++)
  {
   j = 0x1be + (i-1)*0x10;
   if (((int)bsect[j+0x04] % 0xFF00) == 0x05)
    result = EXTENDED;
  }
  if (!result)
  {
   for (i=1; i<=2; i++)
   {
    j = 0x1be - i * 0x10;
    if (((int)bsect[j+0x04] % 0xFF00) != 0x00)
     result = OLDMSX;
   }
   if (!result) result = OLDPC;
  }
 }
 return result;
}

int getTargetId()
{
 REGS reg;
 calslt((TINY)slot, GetTargetID, &reg);
 return ((int)reg.a);
}

int getLogDrives(logdrives)
char *logdrives;
{
 TINY slt;
 int i, j, nr, total = 0;
 for(i=0; i<8; i++)
  logdrives[i] = 'X';
 for(i=0; i<4; i++)
 {
  slt = (*(TINY *)(0xfb22+i*2));
  if (slt == (TINY)slot)
  {
   nr = (int)(*(TINY *)(0xfb21+i*2));
   for (j=0; j<nr; j++)
    logdrives[total+j] = 'S';
   total += nr;
  }
  else
  {
   if ((int)slt != 0 && (int)slt != 255)
   {
    nr = (int)(*(TINY *)(0xfb21+i*2));
    for (j=0; j<nr; j++)
     logdrives[total+j] = 'N';
    total += nr;
   }
  }
 }
 return(total);
}

int readSect(bsect, id, a,b,c)
char *bsect;
int id;
int a;
int b;
int c;
{
 REGS reg;
 reg.a	= (TINY)id;
 reg.bc = 0x0100 + a;
 reg.de = b * 0x0100 + c;
 reg.hl = (NAT)bsect;
 calslt((TINY)slot, RdSecDir, &reg);
 return ((int)reg.a);
}

int fillMak(bsect, id)
char *bsect;
int id;
{
 printf("\nNot yet implemented\n");
 lPartition = -1;
 return 1;
}

int prPar()
{
 int i;
 char volSect[512], *ptr;
 unsigned size;
 for(i=0; i<=lPartition; i++)
 {
  printf("%2.2d. ", i+1);

  readSect(volSect, (int)partitions[i].pfxtar,\
		    (int)partitions[i].sa % 0xFF00,\
		    (int)partitions[i].sb % 0xFF00,\
		    (int)partitions[i].sc % 0xFF00);

  volSect[0x0B]='\0';
  ptr = volSect; ptr=ptr+3;
  printf(" %s  ", ptr);
  size = (((int)partitions[i].ea % 0xFF00) * 0x0100) +\
	 ((int)partitions[i].eb % 0xFF00);
  if ((size-((size/2048) * 2048))*10 / 2048 >= 5)
   printf("%2.2u MB  ", size/2048 + 1);
  else
   printf("%2.2u MB  ", size/2048);
  switch((int)partitions[i].type % 0xFF00)
  {
   case 0x00 : printf("NOT USED        "); break;
   case 0x01 : printf("MS(X)DOS FAT12  "); break;
   case 0x02 : printf("XENIX USR       "); break;
   case 0x03 : printf("XENIX ROOT      "); break;
   case 0x04 : printf("MS DOS (<=32MB) "); break;
   case 0x05 : printf("EXTENDED        "); break;
   case 0x06 : printf("MS DOS (>32 MB) "); break;
   default   : printf("Other           "); break;
  }
 printf("\n");
 }
}

int fillExtended(bsect, id)
char *bsect;
int id;
{
 int j;
 int a, b, c, olda, oldb, oldc, gdb, teller;
 olda = 0; oldb = 0; oldc = 0; gdb = 0x05; teller = 1;
 while(gdb == 0x05)
 {
  j = 0x1be;
  partitions[teller-1].pfxtar = (char)id;
  partitions[teller-1].type = (int)bsect[j+0x04] % 0xFF00;
  partitions[teller-1].sa = (TINY)(olda + (int)bsect[j+0x0a] % 0xFF00);
  partitions[teller-1].sb = (TINY)(oldb + (int)bsect[j+0x09] % 0xFF00);
  partitions[teller-1].sc = (TINY)(oldc + (int)bsect[j+0x08] % 0xFF00);
  partitions[teller-1].ea = (TINY)((int)bsect[j+0x0c] % 0xFF00);
  partitions[teller-1].eb = (TINY)((int)bsect[j+0x0d] % 0xFF00);
  j = 0x1be + 1*0x10;
  gdb = (int)bsect[j+0x04] % 0xFF00;
  a = (int)bsect[j+0x0a] % 0xFF00;
  b = (int)bsect[j+0x09] % 0xFF00;
  c = (int)bsect[j+0x08] % 0xFF00;
  readSect(bsect, id, a + olda, b + oldb, c + oldc);
  olda = a + olda;
  oldb = b + oldb;
  oldc = c + oldc;
  lPartition = teller - 1;
  teller++;
 }
 return 1;
}

int fillOld(bsect, id, type)
char *bsect;
int id;
int type;
{
 int i,j, start;

 if (type == OLDMSX)
  start = 1;
 else
  start = 3;
 for (i=start; i<=6; i++)
 {
  j = 0x1be + (i-3)*0x10;
  if ((int)bsect[j+0x04] % 0xFF00 == 0x00) break;
  partitions[i-start].pfxtar = (char)id;
  partitions[i-start].type = (int)bsect[j+0x04] % 0xFF00;
  partitions[i-start].sa = (TINY)((int)bsect[j+0x0a] % 0xFF00);
  partitions[i-start].sb = (TINY)((int)bsect[j+0x09] % 0xFF00);
  partitions[i-start].sc = (TINY)((int)bsect[j+0x08] % 0xFF00);
  partitions[i-start].ea = (TINY)((int)bsect[j+0x0c] % 0xFF00);
  partitions[i-start].eb = (TINY)((int)bsect[j+0x0d] % 0xFF00);
  lPartition = i-start;
 }
 return 1;
}

int printPartitions(id)
int id;
{
 char bsect[512];  /* Sectorbuffer */
 int i, type;
 type = 0;
 for(i=0; i<512; i++) bsect[i] = '0';
 readSect(bsect, id, 0, 0, 0);
 type = getType(bsect);

 switch(type)
 {
  case MAK      : fillMak(bsect, id); break;
  case EXTENDED : fillExtended(bsect, id); break;
  case OLDPC    : fillOld(bsect, id, OLDPC); break;
  case OLDMSX   : fillOld(bsect, id, OLDMSX); break;
 }

 return type;
}

int initSCSI()
{
 int i, result;
 TINY slt;
 result = -1;
 for(i=0; i<4; i++)
 {
  slt = (*(TINY *)(0xfb22+i*2));
  if (slt!=0)
  {
   if (rdslt(slt, 0x7ff0)=='K')
    if (rdslt(slt, 0x7ff1)=='M')
     if (rdslt(slt, 0x7ff2)=='c')
      if (rdslt(slt, 0x7ff3)=='s')
      {
       result = (int)slt;
       break;
      }
  }
 }
 return (result);
}

VOID printUsage()
{
 if (message == on)
 {
  printf("MAP.COM - partition mapper for GOUDA SCSI INTERFACE\n");
  printf("(c)1995 MSX Club Gouda/Gert de Boom - version 0.9 beta\n\n");
  printf("Usage : MAP [-<s|S>] <drive> <targetID> <partitionnumber>\n");
  printf("          -s|S     : silent mode\n          drive    : A...H\n");
  printf("          targetID : 0...7\n          part.no. : 1...15\n\n");
  printf("        MAP -<l|L> [targetID]\n          -l|L     : list partitions\n");
  printf("          targetID : 0...7\n");
 }
 exit(1);
}

VOID listParts(argc, argv)
int argc;
char **argv;
{
 int id, type;
 if (strcmp(argv[1], "-l") && strcmp(argv[1], "-L"))
  printUsage();

 if (argc==2)
   id = getTargetId();
 else
  id = atoi(argv[2]);

 switch(isUnitReady(id))
 {
  case 1 :  printf("List of partitions on targetID %d\n\n", id );
	    type = printPartitions(id);
	    switch(type)
	    {
	     case MAK	   : printf("Partitietype : MAK 3.0\n\n"); break;
	     case EXTENDED : printf("Partitietype : Extended\n\n"); break;
	     case OLDPC    : printf("Partitietype : Old PC\n\n"); break;
	     case OLDMSX   : printf("Partitietype : Old MSX\n\n"); break;
	     default  : printf("Error : partition table not valid"); exit(1);
	    }
            printf("No.  Label     Size   Type\n\n");
	    prPar();
            break;
  case 2 :  if (message != off) printf("HostID is not a valid target ID");
            exit(1);
  case 0 :  if (message != off) printf("Invalid target ID");
            exit(1);
 }
}

VOID mapMain(argc, argv)
int argc;
char **argv;
{
 int id, i, result, nrdrives, argnr, partno, count;
 char logdrives[8], buf[5], *workArea;
 REGS reg;

 argnr = 0; count = 0;
 if (argc == 4) argnr = 1;
 if (argc == 5)
 {
  if (strcmp(argv[1], "-s") && strcmp(argv[1], "-S"))
  {
   message = off;
   printUsage();
  }
 }

 id = atoi(argv[argnr+1]);
 result = isUnitReady(id);

 if (result)
 {
  if (result != 1)
  {
   if (message != off)
    printf("HostID is not a valid target ID");
   exit(1);
  }
 }
 else
 {
  if (message != off)
   printf("Invalid target ID");
  exit(1);
 }

 nrdrives = getLogDrives(logdrives);
 result = -1;
 for (i=0; i<nrdrives; i++)
 {
  sprintf(buf, "%c", i+'A');
  if (!strcmp(buf, argv[argnr]))
  {
   if (logdrives[i]=='S')
    result = i;
  }
  else
  {
   sprintf(buf, "%c", i+'a');
   if (!strcmp(buf, argv[argnr]))
    if (logdrives[i]=='S')
     result = i;
  }
 }
 if (result==-1)
 {
  if (message != off)
   printf("Drive %s is not a valid MAP-drive", argv[argnr]);
  exit(1);
 }

 if (atoi(argv[argnr+2])<1 || atoi(argv[argnr+2])>15)
 {
  if (message != off)
   printf("Invalid partitionnumber");
  exit(1);
 }
 else
  partno = atoi(argv[argnr+2]);

 /* NOW REALLY MAP	       */
 /* ==============             */
 /* id	   = target id	       */
 /* result = no. of log. drive */
 /* partno = partition no.     */
 /* count  = no. of SCSI drive */
 printPartitions(id);
 if (lPartition+1 < partno)
 {
  if (message != off)
   printf("Partition %d does not exist on targetID %d.", partno, id);
  exit(1);
 }
 calslt((TINY)slot, GetWrk, &reg);
 workArea = (char *)reg.hl;
 for(i=0; i<result; i++)
  if (logdrives[i]=='S') count++;
 workArea = workArea + count * 8;
 *workArea = (TINY)partitions[partno-1].pfxtar; workArea++;
 *workArea = (TINY)partitions[partno-1].sc; workArea++;
 *workArea = (TINY)partitions[partno-1].sb; workArea++;
 *workArea = (TINY)partitions[partno-1].sa; workArea++;
 *workArea = (TINY)partitions[partno-1].ea; workArea++;
 *workArea = (TINY)partitions[partno-1].eb;
}


int main(argc, argv)
int argc;
char **argv;
{
 message = on;
 lPartition = 0;
 slot = initSCSI();
 if (slot==-1)
 {
  printf("SCSI BIOS not found, not installed or wrong version\n");
  exit(1);
 }
 if (argc != 4 && argc != 5)
 {
  if (argc != 3 && argc != 2)
   printUsage();
  listParts(argc, argv);
 }
 else
  mapMain(argc, argv);
 return 0;
}


