/* HzN: 18.04.2003: 32 Bit
        Alle const xx=wert in #define umgesetzt, da Typen fehlten
        Dabei entstandene Doubletten umbenamst
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "swis.h"

#define max(a,b) a>b ? (a) : (b)

#ifndef uint
#define uint unsigned int
#endif

#ifndef boolean
#define boolean char
#endif

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

typedef struct
 {
  int addresses[127];
  char hardwaredata[0X1BF];
  uint defcount,datacount;
 } BootBlockData;

typedef struct
 {
  char *mapblock;
  uint allocun,sparebits,sectsize,idlen;
 } MapData;

/* Externe Assembler-Routinen */
extern uint zonecheck(char *,uint);
extern uint bbchecksum(char *,uint);
extern uint dirchecksum(uint);
extern void deflistchecksum(char *,char *);
extern void dircheckset(void);

/* Externe Teile des Wimp-Programmes */
extern void stringfrommem(char *,char *);
extern _kernel_oserror *swi(int,_kernel_swi_regs *);
extern int drive,ssize,zonspa,idlen;
extern _kernel_swi_regs regs;


void create_fragment(MapData,uint,uint,uint);
_kernel_oserror *write_disc(uint,char *,uint,uint);
_kernel_oserror *read_disc(uint,char *,uint,uint);
void reset_bits(char *,uint,uint);
void set_bits(char *,uint,uint);
void i_crosscheck(MapData,uint);
void i_del_dirtree(char *,uint);
uint ld(uint);

char mapblock[10240];
_kernel_oserror *prepare_partition(uint drive,uint ntracks,uint sectptrack,uint nheads,uint ldsectsize,uint skew,uint lowsector,uint density,uint partoffset,uint allocun,char *discname,BootBlockData *bbdata);

_kernel_oserror *pc_init(char *name)
{
_kernel_oserror *e;
char init_name[10];
uint Tracks,Offset,SectPTrack,Heads,LdSectSize,Skew,LowSector,Density,
     five_byte[2];
BootBlockData bbdata;
Tracks=80;
SectPTrack=18;
Heads=2;
LdSectSize=9;
Offset=0;

regs.r[0]=14;
regs.r[1]=(int) five_byte;
five_byte[0]=3;
e=_kernel_swi(OS_Word,&regs,&regs);if (e!=NULL) return e;

regs.r[0]=(int) five_byte;
regs.r[1]=(int) init_name;
regs.r[2]=10;
regs.r[3]=(int) "%24-%MI-%W3";
e=_kernel_swi(OS_ConvertDateAndTime,&regs,&regs);if (e!=NULL) return e;

if (name==NULL || *name==0) name=init_name;

Skew=0;
LowSector=1;
Density=4;

bbdata.defcount=0; /* Keine Defekte */
bbdata.datacount=0; /* Keine Hardwareinfo */
e=prepare_partition(drive,Tracks,SectPTrack,Heads,LdSectSize,Skew,LowSector,Density,Offset,64,name,&bbdata);

return e;

}

_kernel_oserror *prepare_partition(uint drive,uint ntracks,uint sectptrack,uint nheads,uint ldsectsize,uint skew,uint lowsector,uint density,uint partoffset,uint allocun,char *discname,BootBlockData *bbdata)
{

/* const dreclen=60U,bootblockadr=0X0C00U;*/
#define dreclen 60U
#define bootblockadr 0X0C00U

static _kernel_oserror e_b;
_kernel_oserror *e=&e_b; // War falsch, zeigte ins Nirwana
MapData mapdata;
uint sectsize,discsize,nzones,sparebits,idlen,rootadr,cycleid,midzone,mapadr,mapsize,oddbits,*ip;
int maxfrags,i,j;
char *mapblock,*fragptr,bootblock[0X200],*p,*discrecord=bootblock+0X1C0,
     dirblock[2048];

for(p=bootblock;p<=bootblock+0X1FF;*p++=0);

sectsize=1<<ldsectsize;
idlen=max(15,ldsectsize+3);
discsize=ntracks*sectptrack*nheads*sectsize;

maxfrags=discsize;

sparebits=32;
do
{
oddbits=(discsize/allocun+dreclen*8) % (sectsize*8-sparebits);
sparebits+=1;
}
while (oddbits>0 && oddbits<=15);
sparebits-=1;

nzones=1;
maxfrags-=(sectsize-sparebits/8-dreclen)*allocun*8;

while (maxfrags>0)
 {
  nzones+=1;
  maxfrags-=(sectsize-sparebits/8)*allocun*8;
 }

rootadr=(2<<8)+2*nzones+1;
cycleid=rand();
/* Bootblock fr die Partition vorbereiten */

discrecord[0]=ldsectsize;
discrecord[1]=sectptrack;
discrecord[2]=nheads;
discrecord[3]=density;
discrecord[4]=idlen;
discrecord[5]=ld(allocun);
discrecord[6]=skew;
discrecord[7]=0;
discrecord[8]=lowsector;
discrecord[9]=nzones;
discrecord[10]=sparebits % 256;
discrecord[11]=sparebits / 256;
*(int*)(discrecord+12)=rootadr;
*(int*)(discrecord+16)=discsize;

ip=(uint *)bootblock;

for (i=1;i<=bbdata->defcount;i++)
 {
  if ((j=bbdata->addresses[i-1]-partoffset)>=0 && j<discsize)
   {
    *ip++=j;
   }
 }

deflistchecksum(bootblock,(char *) ip);

p=bootblock+0X01BF;
for (i=1;i<=bbdata->datacount;i++)
 *p--=bbdata->hardwaredata[i-1];

bootblock[0X01FF]=bbchecksum(bootblock,512);

if ((e=write_disc(partoffset+bootblockadr,bootblock,512,drive))!=NULL)
 return e;

/* Map vorbereiten */

*(int*)(discrecord+20)=cycleid;
strcpy(discrecord+22,discname);

midzone=nzones/2;
if (nzones>1)
 mapadr=(midzone*(sectsize*8-sparebits)-dreclen*8)*allocun;
else
 mapadr=0;

if ((mapblock=malloc(mapsize=nzones*sectsize))==NULL)
 {
  strcpy(e->errmess,"Kein Speicher verfgbar");
  e->errnum=0;
  return(e);
 }
for(p=mapblock;p<mapblock+mapsize;*p++=0);

mapdata.mapblock=mapblock;
mapdata.sectsize=sectsize;
mapdata.allocun=allocun;
mapdata.sparebits=sparebits;
mapdata.idlen=idlen;

p=mapblock;
for(i=1;i<=nzones;i++)
 {
  if (i==1)
   {
    memcpy(p+4,discrecord,60);
    fragptr=p+64;
   }
  else
   {
    fragptr=p+4;
   }

  j=(fragptr-p-1)*8;
  p[1]=j % 256;
  p[2]=j / 256 | 0X80;
  /* FreeLink initialisieren */
  reset_bits(p,(fragptr-p)*8,sectsize*8-sparebits+32-(fragptr-p)*8);
   /* Alle Fragmente zunchst zurcksetzen */
  set_bits(p,sectsize*8-sparebits+31,1);
   /* Schlubit */

  p+=sectsize;
 }

  /* Bootblock aus der Map streichen */
 create_fragment(mapdata,2,0,4096);
  /* Map + Verzeichnisbaum */
 create_fragment(mapdata,2,mapadr,mapsize*2+2048);
  /* Unntze Map */
 create_fragment(mapdata,1,discsize,(uint)(((sectsize*8-sparebits)*nzones-dreclen*8)*allocun)-discsize);
/* Bekannte Defekte aus der Map streichen */
for (i=1;i<=bbdata->defcount;i++)
 {
  if ((j=bbdata->addresses[i-1]-partoffset)>=0 && j<discsize)
   create_fragment(mapdata,1,j,sectsize);
 }

i_crosscheck(mapdata,nzones);

e=write_disc(partoffset+mapadr,mapblock,mapsize,drive);
 if (e!=NULL) return e;
e=write_disc(partoffset+mapadr+mapsize,mapblock,mapsize,drive);
 if (e!=NULL) return e;
/* Zwei Kopien der Map schreiben */
i_del_dirtree(dirblock,rootadr);
e=write_disc(partoffset+mapadr+2*mapsize,dirblock,2048,drive);
 if (e!=NULL) return e;

free (mapblock);
return NULL;

}

void reset_bits(char *point,uint bitoffset,uint bitcount)
{
 char *byte;
 uint i,bit;

 for (i=bitoffset;i<bitoffset+bitcount;i++)
  {
   byte=point+i/8;
   bit=i % 8;
   *byte&=~(1<<bit);
  }
}

void set_bits(char *point,uint bitoffset,uint bitcount)
{
 char *byte;
 uint i,bit;

 for (i=bitoffset;i<bitoffset+bitcount;i++)
  {
   byte=point+i/8;
   bit=i % 8;
   *byte|=1<<bit;
  }
}

void create_fragment(MapData mapdata,uint fragid,uint discadr,uint size)
{
 /* Discadr wird als gltige Adresse angenommen*/
 uint zonebytes1,zonebytes,q,i,frextent;
 char *p=mapdata.mapblock;
 zonebytes=(mapdata.sectsize*8-mapdata.sparebits)*mapdata.allocun;
 zonebytes1=zonebytes-60*8*mapdata.allocun;

 if (discadr>=zonebytes1)
  {
   discadr-=zonebytes1;
   p+=mapdata.sectsize;
  }
 while (discadr>=zonebytes)
  {
   discadr-=zonebytes;
   p+=mapdata.sectsize;
  }
  q=discadr/mapdata.allocun+32+(p==mapdata.mapblock ? 480 : 0);
  if (discadr!=0) set_bits(p,q-1,1); /* Block zuvor beenden */
  set_bits(p,q+fragid-1,1); /* FragId-Nummer schreiben */
  frextent=size/mapdata.allocun+ ((size % mapdata.allocun) ? 1 : 0);
  frextent=max(frextent,mapdata.idlen+1);
  reset_bits(p,q+fragid,frextent-fragid);
  set_bits(p,q+frextent-1,1);
  q+=frextent;
  if (discadr==0)
   {
    p[1]=(q-8) % 256; /* Offset von FreeLink=MapStart+8 Bits*/
    p[2]=(q-8) / 256 | 0X80;
   } /* FreeLink setzen */
  else if (q<mapdata.sectsize*8-mapdata.sparebits+32)
   {
    i=8+p[1]+256*(p[2] & 0X7F);
    p[i/8]=(q-i) % 256;
    p[i/8+1]|=(q-i) / 256;
   }

}

void i_crosscheck(MapData mapdata,uint nzones)
{
/* Berechnet die Gesamtprfsumme der Map und die Einzelprfsumme
   fr Map-Blcke */
uint i;
char *p,j=0,c[256]/*Max. Zonenzahl*/;
for (i=1;i<nzones;i++)
 {
  c[i-1]=rand()/RAND_MAX*255;
  j^=c[i-1];
 }
c[nzones-1]=255 ^ j;
p=mapdata.mapblock;
for (i=0;i<=nzones-1;i++)
 {
  p[3]=c[i];
  *p=zonecheck(p,mapdata.sectsize);
  p+=mapdata.sectsize;
 }
}

void i_del_dirtree(char *block,uint rootadr)
{

/* Verzeichnisbaum lschen */
char *e=block+2048,*f;
int check;

dircheckset();
for(f=block;f<e;*f++=0);

strcpy(block+1,"Nick");
*(e-38)=rootadr % 256;
*(e-37)=rootadr / 256;
*(e-35)='$';
*(e-16)='$';
strcpy(e-5,"Nick");
dirchecksum(*(uint*)block);
dirchecksum(block[4]);
f=e-40;

while((uint)f & 3)
 dirchecksum(*f++);

while(f<e-4)
 {
  check=dirchecksum(*(uint*)f);
  f+=4;
 }
check^=check>>16;
check^=check>>8;
check&=255;
e[-1]=check;

}
void del_dirtree(char *block)
{

/*Verzeichnisbaum lschen */
char *i;
int d[]={ 9,2,0,0X4C,0X65,0X65,0X72,0X0D,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
          0X4C,0X65,0X65,0X72,0X0D,0,0,0,0,0,0,0X4E,0X69,0X63,0X6B,0X9C };

block[0]=0;
block[1]=0X4E;
block[2]=0X69;
block[3]=0X63;
block[4]=0X6B;

for (i=block+5;i<=block+2009;*i++=0);
for (i=block+2010;i<=block+2047;i++)
 *i=d[i-block-2010];

}

void freemap(char *FreeLink,char *FragId)
{

struct fragdata
 {
  char *start;
  char *end;
 };

unsigned int a,f,i,ext;
char *d,*o,*endmb;
struct fragdata *z
 =calloc(ext=(ssize*8-zonspa)/(idlen+1)+1,sizeof(struct fragdata));
boolean coherent=FALSE;

for(i=0;i<ext;z[i++].end=NULL);  /* Zeiger im Array lschen */

d=FragId;
f=0;                      /* Zhler fr Fragmente */

do
{
 a=d[0]+256*d[1];         /* Fragment id auf relevante Bits beschrnken */
 i=a & (1 << idlen)-1;

 if (i==1 || i==2)
  {
  z[f].end=d-1;           /* Der lschbare Block endet vor dieser Adresse. */
  coherent=FALSE;         /* Block ignorieren (Map, Defect list etc. */
  }                       /* erhalten) und Flag zurcksetzen */
 else
  {
  if (!coherent)          /* Wurde zuvor kein lschbarer Block gefunden ? */
   {
    coherent=TRUE;        /* Flag setzen (nachfolgende Blcke als zugehrig*/
    z[++f].start=d;       /* betrachten); Block merken und Zhler erhhen */
   }
  }

 d+=(idlen+1)/8;          /* frhestmgliche nchste Fragment id */
 if ((a & 0X8000)==0)
  while (*d++==0);        /* Nullbits berspringen */
}
while (d<=(endmb=FreeLink+ssize+2-zonspa/8)); /* Ende des Map block */

if (z[f].end==NULL)       /* Ende des letzen Fragmentblocks unbekannt ? */
 z[f].end=endmb;          /* Dann ist's das Ende des Map block. */


for(i=1;i<=f;i++)         /* Alle gefundenen Fragmentblcke */
 {
  for(d=z[i].start;d<z[i].end;d++)
   *d=0;                  /* Smtliche Bits im Fragmentblock lschen */
  *d=0X80;                /* und Schlubit setzen */
 }

if (f>0)                  /* Ist berhaupt ein Block lschbar ? */

 {
  o=z[f].start;           /* Alte (letzte) Fragment id holen */
  a=o[0]+256*o[1];        /* Relevante Bits der Fragment id ausblenden */
  a&=~((1<<idlen)-1);     /* und null hineinschreiben (letzten freien Block*/
                          /* markieren) */
  o[0]=a & 255;
  o[1]=a / 256;

  if (f>1)                /* Mehr als ein lschbarer Block ? */
   {                      /* -> Ketten erstellen */
    for (i=f-1;i>=1;i--)
    {
     d=z[i].start;
     a=d[0]+256*d[1];     /* Relevante Bits der Fragment id ausblenden */
     a&=~((1<<idlen)-1);  /* und verbinden mit nchstem freien Block */
     a|=(o-d) * 8;
     d[0]=a & 255;
     d[1]=a / 256;        /* neu nach alt */
     o=d;
    }
   }
  }

                          /* FreeLink mit erstem freien Block verbinden */
i=(o-FreeLink)*8;
FreeLink[0]=i & 255;
FreeLink[1]=i / 256 | 0X80;

                          /* ... und ZoneCheck neu berechnen */
i=zonecheck(FreeLink-1,ssize);
FreeLink[-1]=i;           /* FreeLink-1=MapStart */

free(z);                  /* Abschlieend das Feld wieder freigeben */

}

void crosscheck(char *block)
{

unsigned int i,c[4];
c[0]=rand()/RAND_MAX*255;
c[1]=rand()/RAND_MAX*255;
c[2]=rand()/RAND_MAX*255;
c[3]=255 ^ c[0] ^ c[1] ^ c[2];
for (i=0;i<=3;i++)
 block[i*ssize]=c[i];

}

void crosscheck2(char *block)
{

unsigned int i,c[6];
c[0]=rand()/RAND_MAX*255;
c[1]=rand()/RAND_MAX*255;
c[2]=rand()/RAND_MAX*255;
c[3]=rand()/RAND_MAX*255;
c[4]=rand()/RAND_MAX*255;
c[5]=255 ^ c[0] ^ c[1] ^ c[2] ^ c[3] ^ c[4];
for (i=0;i<=5;i++)
 block[i*ssize]=c[i];

}

_kernel_oserror *dd(char *name)
{

/* const mapadr=0X00000000;      */
/* const blocksize=4*0X00000400; */
#define mapadr_dd 0X00000000
#define blocksize_dd (4*0X00000400)
_kernel_oserror *result;

 regs.r[1]=1;
 regs.r[2]=mapadr_dd+(drive<<29);
 regs.r[3]=(int) mapblock;
 regs.r[4]=blocksize_dd;
swi(ADFS_DiscOp,&regs);

if (name!=NULL && *name!=0)
 strcpy(mapblock+26,name);

freemap(mapblock+1,mapblock+64);
del_dirtree(mapblock+2048);
 regs.r[1]=2;
 regs.r[2]=mapadr_dd+(drive<<29);
 regs.r[3]=(int) mapblock;
 regs.r[4]=1024;
if ((result=_kernel_swi(ADFS_DiscOp,&regs,&regs))==NULL)
{
  regs.r[1]=2;
  regs.r[2]=mapadr_dd+1024+(drive<<29);
  regs.r[3]=(int) mapblock;
  regs.r[4]=1024;
 swi(ADFS_DiscOp,&regs);
  regs.r[1]=2;
  regs.r[2]=mapadr_dd+2048+(drive<<29);
  regs.r[3]=(int) mapblock+2048;
  regs.r[4]=2048;
 swi(ADFS_DiscOp,&regs);
}
 return(result);

}

_kernel_oserror *pc_clean(char *name)
{

/* const mapadr=0X000B7C00;      */
/* const blocksize=5*0X00000400; */
#define mapadr_pc 0X000B7C00
#define blocksize_pc (5*0X00000400)

_kernel_oserror *result;
 regs.r[1]=1;
 regs.r[2]=mapadr_pc+(drive<<29);
 regs.r[3]=(int) mapblock;
 regs.r[4]=blocksize_pc;
swi(ADFS_DiscOp,&regs);

if (name!=NULL && *name!=0)
 strcpy(mapblock+26,name);

crosscheck2(mapblock+3);
freemap(mapblock+1,mapblock+64);
freemap(mapblock+0X0201,mapblock+0X0204);
freemap(mapblock+0X0401,mapblock+0X0404);
freemap(mapblock+0X0601,mapblock+0X0604);
freemap(mapblock+0X0801,mapblock+0X0804);
freemap(mapblock+0X0A01,mapblock+0X0A04);
i_del_dirtree(mapblock+3072,0X020D);
 regs.r[1]=2;
 regs.r[2]=mapadr_pc+(drive<<29);
 regs.r[3]=(int) mapblock;
 regs.r[4]=3072;
if ((result=_kernel_swi(ADFS_DiscOp,&regs,&regs))==NULL)
{ /* Fahre fort, wenn der Schreibzugriff erfolgreich war */
  regs.r[1]=2;
  regs.r[2]=mapadr_pc-0XC00+(drive<<29);
  regs.r[3]=(int) mapblock;
  regs.r[4]=3072;
 swi(ADFS_DiscOp,&regs);
  regs.r[1]=2;
  regs.r[2]=mapadr_pc+0XC00+(drive<<29);
  regs.r[3]=(int) mapblock+3072;
  regs.r[4]=2048;
 swi(ADFS_DiscOp,&regs);
}
 return(result);

}

_kernel_oserror *hd(char *name)
{

/* const mapadr=0X000C7800;      */
/* const blocksize=6*0X00000400; */
#define mapadr_hd 0X000C7800
#define blocksize_hd (6*0X00000400)

_kernel_oserror *result;
 regs.r[1]=1;
 regs.r[2]=mapadr_hd+(drive<<29);
 regs.r[3]=(int) mapblock;
 regs.r[4]=blocksize_hd;
swi(ADFS_DiscOp,&regs);

if (name!=NULL && *name!=0)
 strcpy(mapblock+26,name);

crosscheck(mapblock+3);
freemap(mapblock+1,mapblock+64);
freemap(mapblock+0X0401,mapblock+0X0404);
freemap(mapblock+0X0801,mapblock+0X0804);
freemap(mapblock+0X0C01,mapblock+0X0C04);
del_dirtree(mapblock+4096);
 regs.r[1]=2;
 regs.r[2]=mapadr_hd+(drive<<29);
 regs.r[3]=(int) mapblock;
 regs.r[4]=4096;
if ((result=_kernel_swi(ADFS_DiscOp,&regs,&regs))==NULL)
{ /* Fahre fort, wenn der Schreibzugriff erfolgreich war */
  regs.r[1]=2;
  regs.r[2]=mapadr_hd-0X1000+(drive<<29);
  regs.r[3]=(int) mapblock;
  regs.r[4]=4096;
 swi(ADFS_DiscOp,&regs);
  regs.r[1]=2;
  regs.r[2]=mapadr_hd+0X1000+(drive<<29);
  regs.r[3]=(int) mapblock+4096;
  regs.r[4]=2048;
 swi(ADFS_DiscOp,&regs);
}
 return(result);

}

_kernel_oserror *write_disc(uint adr,char *mem,uint len,uint drive)
{
 _kernel_swi_regs regs;
 regs.r[1]=2;
 regs.r[2]=adr;
 regs.r[2]+=drive<<29;
 regs.r[3]=(int) mem;
 regs.r[4]=len;
 return (_kernel_swi(ADFS_DiscOp,&regs,&regs));
}

_kernel_oserror *read_disc(uint adr,char *mem,uint len,uint drive)
{
 _kernel_swi_regs regs;
 regs.r[1]=1;
 regs.r[2]=adr;
 regs.r[2]+=drive<<29;
 regs.r[3]=(int) mem;
 regs.r[4]=len;
 return (_kernel_swi(ADFS_DiscOp,&regs,&regs));
}

uint ld(uint pot)
{
  uint e=0;
  while(pot>=1<<++e);
  return e-1;
}
