/*******************************
 *       BigBoot V1.0          *
 *                             *
 * Schreibt Assemblerfiles bis *
 * zu 11k Länge zusammen mit   *
 * einer Laderoutine in den    *
 * Boot-track (Block 0-21)     *
 * Compiler: Aztec C3.40a      *
 * Aufruf:   cc bb.c -s        *
 *           ln bb.o -lc       *
 * Warnungen beim compilieren  *
 * können ignoriert werden!    *
 *                             *
 * Der Code wird vom Lader an  *
 * "TRACKLOAD" geladen und mit *
 * GFXBASE in A5 und EXECBASE  *
 * in A6 angesprungen. Die     *
 * Registerverwendung ist frei.*
 * Achtung: Der Code muß auf   *
 * jeden Fall zum Lader zurück-*
 * springen und darf diesen    *
 * keinesfalls überschreiben!  *
 *******************************/

#include <libraries/dos.h>
#include <devices/trackdisk.h>
#include <exec/memory.h>

/* Definition von globalen Variablen
   und Funktion */
struct MsgPort  *dp,*CreatePort();
struct IOExtTD  *dr,*CreateExtIO();
long AllocMem(),Examine(),Open(),Lock();

/* Diese Funktion öffnet das Trackdisk
   für das Gerät "drive". Port & IOReq
   werden automatisch angelegt, die
   Variabeln dp (Port) und dr (Req)
   müßen global verfügbar sein! Bei einem
   Fehler wird ein Wert != 0 zurückgegeben! */
int tdopen(drive)
register int drive;
{
 if (dp=CreatePort(0L,0L)) {
  if (dr=CreateExtIO(dp,(LONG)sizeof (struct IOExtTD)))
   return(OpenDevice(TD_NAME,(LONG)drive,dr,0L));
 DeletePort(dp); }
 return(1);
}

/* Schaltet den Laufwerksmotor ab und schliesst das TD.
   Port und IOReq (dp und dr) werden gelöscht */
void tdclose()
{
 dr->iotd_Req.io_Command  = TD_MOTOR;
 dr->iotd_Req.io_Length   = 0L;
 DoIO(dr);
 CloseDevice(dr);
 DeleteExtIO(dr,(LONG)sizeof (struct IOExtTD));
 DeletePort(dp);
}

/* Stellt die Länge eines Files fest
   (0 = Fäler) */
long length(file)
register char *file;
{
 register struct FileInfoBlock *fib;
 register long l;

 fib=AllocMem((LONG)sizeof(struct FileInfoBlock),0L);
 if (!(l=Lock(file)))
  return(0);
 Examine(l,fib); UnLock(l);
 l=fib->fib_Size;
 FreeMem(fib,(LONG)sizeof(struct FileInfoBlock));
 return(l);
}

/* Berechnet den Wert einer übergebenen Zeichenkette.
   Dabei wird zwischen Hex (beginnt mit $) und Dezimal
   unterschieden */
long calc(wert)
register char *wert;
{
 register char c,*ziff="0123456789abcdef";
 register long erg=0;

 if (*wert!='$') return(atol(wert));
 while (*++wert) {
  for(c=0; c<16; c++)
   if (tolower(*wert)==ziff[c]) {
    erg=(erg<<4)+c; break; }
   if (c==16) return(0);
 }
 return(erg);
}

main(argc,argv)
int argc;
char *argv[];
{
 register unsigned long file,flen,*tbuff,*fbuff,load=0x30000,code=0,chk;
 register int offs=0,c,breq,drive,ret=20,*bb;
 register char *note=":bigboot:bootstrap_module:V3REV1:(W)OLIVER_WAGNER:",
               *np;

/* USAGE ausgeben */
 printf("\2333mBigBoot V1.0 \2330mWritten 1987 by O.Wagner\n");
 if (argc==2 && *argv[1]=='?') { printf("USAGE: BB dfx: file [options]\n\
possible options are: -S (36 bytes SEKA-Obj-format offset)\n\
                      -A (32 bytes Metacomco-Obj-format offset)\n\
                      -Oxxx (set offset to xxx)\n\
                      -Lxxx (specify track load adress, def is $30000)\n\
                      -Cxxx (encode the track with xxx)\n\
                      -Ntext (write \"text\" in block 0 as a note)\n");
 exit(0); }
 if (argc<3) { printf("Bad args\n"); exit(20); }

/* DRIVE feststellen */
 if (tolower(argv[1][0])!='d' || tolower(argv[1][1])!='f' ||
 (drive=argv[1][2]-'0')>3 || drive<0) {
  printf("Illegal drive specification \"\2331m%s\2330m\".\n",argv[1]);
  exit(20); }

/* Optionen auswerten */
 for (c=3; c<argc; c++) {
  if (*argv[c]!='-') {
   printf("Option expected: \"\2331m%s\2330m\".\n",argv[c]); exit(20); }
  switch(tolower(*++argv[c])) {
  case 's': offs=36; break;
  case 'a': offs=32; break;
  case 'o': offs=(SHORT)calc(++argv[c]); break;
  case 'l': load=calc(++argv[c]); break;
  case 'c': code=calc(++argv[c]); break;
  case 'n': note=++argv[c]; break;
  default:
  printf("Invalid option: \"\2331m%s\2330m\".\n",argv[c]-1);
  exit(20);
  }
 }

/* Inputfile analysieren und die Bufferspeicher
   reservieren */
 if (!(flen=length(argv[2]))) {
  printf("Can´t open \"\2331m%s\2330m\".\n",argv[2]); exit(20); }

/* File zu lang für den track */
 if (flen>10752) {
  printf("File (%ld bytes) is too \2331mlarge\2330m for one track!\n",flen);
  exit(20); }

/* Ist offset > filelänge, dann fehler */
 if (offs>flen) { printf("\2331mOffset\2330m (%d) out of range!\n",offs);
  exit(20); }

/* Wieviele Blocks werden benötigt ? */
 breq=(flen-=offs)/512; if (breq*512!=flen) breq++;

/* Buffer für File reservieren */
 if (!(fbuff=AllocMem(flen,MEMF_CLEAR))) {
  printf("Can´t allocate \2331mfile buffer!\2330m\n"); exit(20); }

/* Informationen über das File und die eingestellten Parameter
   ausgeben */
 printf("Reading file \"\2331m%s\2330m\"\nFile length: \2331m%ld\2330m\
 bytes, offset: \2331m%d\2330m bytes, effective length:\
 \2331m%ld\2330m bytes\nBlock requirements: from block 0 to block\
 \2331m%d\2330m.\nTrack load adress is \2331m$%lx\2330m.\n",
  argv[2],flen+offs,offs,flen,breq,load);
 if (load<0xa000 || load>0x7ffff)
  printf("\2331mWARNING: Track load adress in invalid range!!!\2330m\n"); 
 if (code)
  printf("The track will be encoded by \2331m$%lx\2330m.\n",code);

/* File lesen */
 file=Open(argv[2],MODE_OLDFILE); Seek(file,(LONG)offs,0L);
 Read(file,fbuff,flen); Close(file);
 printf("Insert disk to write to in drive \2331mDF%d:\2330m and press\
 RETURN!\nEnter Q/RETURN to quit!",drive);
 if (tolower(getchar())=='q') goto cleanup;

/* Trackdisk öffnen */
 if (tdopen(drive)) {
  printf("Can´t access \"trackdisk.device\" for \2331mDF%d:\2330m\n",drive);
  goto cleanup; }

/* Trackbuffer reservieren */
 if (!(tbuff=AllocMem(11264L,MEMF_CHIP))) {
  printf("Can´t allocate \2331mtrack buffer!\2330m\n"); goto clean1; }

/* Track 0 lesen */
 dr->iotd_Req.io_Length  = 11264L;
 dr->iotd_Req.io_Data    = tbuff;
 dr->iotd_Req.io_Command = CMD_READ;
 dr->iotd_Req.io_Offset  = 0L;
 DoIO(dr);

/* File vom File-Buffer in den Track-Buffer kopieren
   und dabei kodieren */
 for (c=1; c<128; c++) tbuff[c]=0L;
 for (c=0; c<flen/4; c++) tbuff[c+128]=fbuff[c]^code;
 bb=tbuff; np=tbuff+40;

/* Lader in den Block schreiben */
 tbuff[0]=0x444f5300; tbuff[3]=0x48e77f3e;
 tbuff[4]=0x337c0002; tbuff[5]=0x001c237c;
 tbuff[6]=(LONG)breq*512; tbuff[7]=0x0024237c;
 tbuff[8]=load; tbuff[9]=0x0028237c;
 tbuff[10]=0x00000200; tbuff[11]=0x002c4eae;
 tbuff[12]=0xfe384a80; tbuff[13]=0x662441f9;
 tbuff[14]=load; bb[30]=0x303c; bb[31]=(flen+3)/4;
 bb[32]=0x0a98; bb[33]=code>>16;
 bb[34]=code; bb[35]=0x51c8;
 tbuff[18]=0xfff843fa; tbuff[19]=0x00304eae;
 tbuff[20]=0xfe682a40; bb[42]=0x4eb9;
 bb[43]=load>>16; bb[44]=load;
 bb[45]=0x4cdf;
 tbuff[23]=0x7cfe43fa; tbuff[24]=0x00104eae;
 tbuff[25]=0xffa02040; tbuff[26]=0x20680016;
 tbuff[27]=0x70004e75; tbuff[28]=0x646f732e;
 tbuff[29]=0x6c696272; tbuff[30]=0x61727900;
 tbuff[31]=0x67726170; tbuff[32]=0x68696373;
 tbuff[33]=0x2e6c6962; tbuff[34]=0x72617279;

/* "Note"-Text kopieren */
 for(c=0; (*np++=note[c++]) && c<441;) ;

/* Checksum berechnen */
 for(c=0,code=0; c<256; c++) {
  chk=code+tbuff[c];
  if (chk<code) chk++;
  code=chk; }
 tbuff[1]=0xffffffff-chk;

/* Track zurückschreiben */
 dr->iotd_Req.io_Command = CMD_WRITE;
 DoIO(dr);
 dr->iotd_Req.io_Command = CMD_UPDATE;
 DoIO(dr);
 ret=0;

/* "Aufräumen" */
 clean2:
 FreeMem(tbuff,11264L);
 clean1:
 tdclose(); 
 cleanup:
 FreeMem(fbuff,flen);
 exit(ret);
} 
