/*****************************************************************************
 *                                                                           *
 *    CQM UDOS file generation for P8000 floppies                            *
 *    generates UDOS disk from files given as program arguments              *
 *    calling without file arguments creates empty, formatted UDOS disk      *
 *    Copyright (C) Matt Knoth, 2008, <p8000@earthlink.net>                  *
 *                                                                           *
 *    This program has been derived in part from from LIBDSK 1.2.1           *
 *    LIBDSK: General floppy and diskimage access library                    *
 *    Copyright (C) 2001-2,2005  John Elliott <jce@seasip.demon.co.uk>       *
 *    Credits also to Per Ola Ingvarsson and Roger Plant                     *
 *                                                                           *
 *    This library is free software; you can redistribute it and/or          *
 *    modify it under the terms of the GNU Library General Public            *
 *    License as published by the Free Software Foundation; either           *
 *    version 2 of the License, or (at your option) any later version.       *
 *                                                                           *
 *    This code is distributed in the hope that it will be useful,           *
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
 *    Library General Public License for more details.                       *
 *                                                                           *
 *    You should have received a copy of the GNU Library General Public      *
 *    License along with this library; if not, write to the Free             *
 *    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,        *
 *    MA 02111-1307, USA                                                     *
 *                                                                           *
 *****************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<dirent.h>
#include<string.h>
#include<time.h>
#include <sys/timeb.h>

unsigned char* get_wr_ptr(unsigned short sec_id);

unsigned char* disk=NULL;
unsigned char* allocs;
int            freeblocks=0;

/* Automatically generated CRC table */
/* polynomial: 0x104C11DB7, bit reverse algorithm */
const unsigned long crc32r_table[256] = {
    0x00000000UL,0x77073096UL,0xEE0E612CUL,0x990951BAUL, 0x076DC419UL,0x706AF48FUL,0xE963A535UL,0x9E6495A3UL, 0x0EDB8832UL,0x79DCB8A4UL,0xE0D5E91EUL,0x97D2D988UL,
    0x09B64C2BUL,0x7EB17CBDUL,0xE7B82D07UL,0x90BF1D91UL, 0x1DB71064UL,0x6AB020F2UL,0xF3B97148UL,0x84BE41DEUL, 0x1ADAD47DUL,0x6DDDE4EBUL,0xF4D4B551UL,0x83D385C7UL,
    0x136C9856UL,0x646BA8C0UL,0xFD62F97AUL,0x8A65C9ECUL, 0x14015C4FUL,0x63066CD9UL,0xFA0F3D63UL,0x8D080DF5UL, 0x3B6E20C8UL,0x4C69105EUL,0xD56041E4UL,0xA2677172UL,
    0x3C03E4D1UL,0x4B04D447UL,0xD20D85FDUL,0xA50AB56BUL, 0x35B5A8FAUL,0x42B2986CUL,0xDBBBC9D6UL,0xACBCF940UL, 0x32D86CE3UL,0x45DF5C75UL,0xDCD60DCFUL,0xABD13D59UL,
    0x26D930ACUL,0x51DE003AUL,0xC8D75180UL,0xBFD06116UL, 0x21B4F4B5UL,0x56B3C423UL,0xCFBA9599UL,0xB8BDA50FUL, 0x2802B89EUL,0x5F058808UL,0xC60CD9B2UL,0xB10BE924UL,
    0x2F6F7C87UL,0x58684C11UL,0xC1611DABUL,0xB6662D3DUL, 0x76DC4190UL,0x01DB7106UL,0x98D220BCUL,0xEFD5102AUL, 0x71B18589UL,0x06B6B51FUL,0x9FBFE4A5UL,0xE8B8D433UL,
    0x7807C9A2UL,0x0F00F934UL,0x9609A88EUL,0xE10E9818UL, 0x7F6A0DBBUL,0x086D3D2DUL,0x91646C97UL,0xE6635C01UL, 0x6B6B51F4UL,0x1C6C6162UL,0x856530D8UL,0xF262004EUL,
    0x6C0695EDUL,0x1B01A57BUL,0x8208F4C1UL,0xF50FC457UL, 0x65B0D9C6UL,0x12B7E950UL,0x8BBEB8EAUL,0xFCB9887CUL, 0x62DD1DDFUL,0x15DA2D49UL,0x8CD37CF3UL,0xFBD44C65UL,
    0x4DB26158UL,0x3AB551CEUL,0xA3BC0074UL,0xD4BB30E2UL, 0x4ADFA541UL,0x3DD895D7UL,0xA4D1C46DUL,0xD3D6F4FBUL, 0x4369E96AUL,0x346ED9FCUL,0xAD678846UL,0xDA60B8D0UL,
    0x44042D73UL,0x33031DE5UL,0xAA0A4C5FUL,0xDD0D7CC9UL, 0x5005713CUL,0x270241AAUL,0xBE0B1010UL,0xC90C2086UL, 0x5768B525UL,0x206F85B3UL,0xB966D409UL,0xCE61E49FUL,
    0x5EDEF90EUL,0x29D9C998UL,0xB0D09822UL,0xC7D7A8B4UL, 0x59B33D17UL,0x2EB40D81UL,0xB7BD5C3BUL,0xC0BA6CADUL, 0xEDB88320UL,0x9ABFB3B6UL,0x03B6E20CUL,0x74B1D29AUL,
    0xEAD54739UL,0x9DD277AFUL,0x04DB2615UL,0x73DC1683UL, 0xE3630B12UL,0x94643B84UL,0x0D6D6A3EUL,0x7A6A5AA8UL, 0xE40ECF0BUL,0x9309FF9DUL,0x0A00AE27UL,0x7D079EB1UL,
    0xF00F9344UL,0x8708A3D2UL,0x1E01F268UL,0x6906C2FEUL, 0xF762575DUL,0x806567CBUL,0x196C3671UL,0x6E6B06E7UL, 0xFED41B76UL,0x89D32BE0UL,0x10DA7A5AUL,0x67DD4ACCUL,
    0xF9B9DF6FUL,0x8EBEEFF9UL,0x17B7BE43UL,0x60B08ED5UL, 0xD6D6A3E8UL,0xA1D1937EUL,0x38D8C2C4UL,0x4FDFF252UL, 0xD1BB67F1UL,0xA6BC5767UL,0x3FB506DDUL,0x48B2364BUL,
    0xD80D2BDAUL,0xAF0A1B4CUL,0x36034AF6UL,0x41047A60UL, 0xDF60EFC3UL,0xA867DF55UL,0x316E8EEFUL,0x4669BE79UL, 0xCB61B38CUL,0xBC66831AUL,0x256FD2A0UL,0x5268E236UL,
    0xCC0C7795UL,0xBB0B4703UL,0x220216B9UL,0x5505262FUL, 0xC5BA3BBEUL,0xB2BD0B28UL,0x2BB45A92UL,0x5CB36A04UL, 0xC2D7FFA7UL,0xB5D0CF31UL,0x2CD99E8BUL,0x5BDEAE1DUL,
    0x9B64C2B0UL,0xEC63F226UL,0x756AA39CUL,0x026D930AUL, 0x9C0906A9UL,0xEB0E363FUL,0x72076785UL,0x05005713UL, 0x95BF4A82UL,0xE2B87A14UL,0x7BB12BAEUL,0x0CB61B38UL,
    0x92D28E9BUL,0xE5D5BE0DUL,0x7CDCEFB7UL,0x0BDBDF21UL, 0x86D3D2D4UL,0xF1D4E242UL,0x68DDB3F8UL,0x1FDA836EUL, 0x81BE16CDUL,0xF6B9265BUL,0x6FB077E1UL,0x18B74777UL,
    0x88085AE6UL,0xFF0F6A70UL,0x66063BCAUL,0x11010B5CUL, 0x8F659EFFUL,0xF862AE69UL,0x616BFFD3UL,0x166CCF45UL, 0xA00AE278UL,0xD70DD2EEUL,0x4E048354UL,0x3903B3C2UL,
    0xA7672661UL,0xD06016F7UL,0x4969474DUL,0x3E6E77DBUL, 0xAED16A4AUL,0xD9D65ADCUL,0x40DF0B66UL,0x37D83BF0UL, 0xA9BCAE53UL,0xDEBB9EC5UL,0x47B2CF7FUL,0x30B5FFE9UL,
    0xBDBDF21CUL,0xCABAC28AUL,0x53B39330UL,0x24B4A3A6UL, 0xBAD03605UL,0xCDD70693UL,0x54DE5729UL,0x23D967BFUL, 0xB3667A2EUL,0xC4614AB8UL,0x5D681B02UL,0x2A6F2B94UL,
    0xB40BBE37UL,0xC30C8EA1UL,0x5A05DF1BUL,0x2D02EF8DUL,
    };


static void drv_qm_update_crc( unsigned long* crc, unsigned char byte )
{
    /* Note that there is a bug in the CopyQM CRC calculation  */
    /* When indexing in this table, they shift the crc ^ data  */
    /* 2 bits up to address longwords, but they do that in an  */
    /* eight bit register, so that the top 2 bits are lost,    */
    /* thus the anding with 0x3f                               */
    *crc = crc32r_table[(byte ^ (unsigned char)*crc) & 0x3f ] ^ (*crc >> 8);
}

void drv_qm_dump_compressed(FILE* ofp,unsigned long* pcrc,unsigned char* rd_ptr, int sector_size)
{
   unsigned char* p;
   unsigned char  a;
   int i,l,len;

   for(i=0; i<sector_size; i++) drv_qm_update_crc(pcrc, rd_ptr[i]);   // warming up cache

   for(p=rd_ptr, i=0, l=0, len=sector_size-4; l < len; ) {

       a=p[i];
       if((a==p[i+1]) && (a==p[i+2]) && (a==p[i+3])) {  // equals break even after 3, minimum 4 required

          if(i) {                                       // flush out previous non-equals
             fwrite(&i,1,2,ofp);                        // positive len
             while(i--) fwrite(p++,1,1,ofp);            // runlen data
          }

          for(a=*p, i=0; (l<sector_size) && (a==*p);) { // find true length of equals
                i++; l++; p++;
          }
          i*=-1;
          fwrite(&i,1,2,ofp);                           // negative len
          fwrite(&a,1,1,ofp);                           // runlen data
          i=0;

       } else {
          i++;
          l++;
       }
   }

   if(i || (l<sector_size)) {                          // dump remaining buffer after end of block
      i+=sector_size-l;
      fwrite(&i,1,2,ofp);                              // rest of block
      while(i--) fwrite(p++,1,1,ofp);                  // runlen data
   }
}

void qm_image_save( char* fname )
{
    int rd_cyl,rd_head,rd_sec,rd_track;
    unsigned char* rd_ptr;
    unsigned char buf[256];
    unsigned long crc;
    FILE* ofp;
    int tmp;

    if((ofp=fopen(fname,"wb"))==NULL) {
        fprintf(stderr,"ERROR: unable to open file %s for writing\n",fname);
        return;
    }

    memset(buf,0x00,150);

    buf[0x00]= 'C';
    buf[0x01]= 'Q';
    buf[0x02]= 0x14;
    buf[0x03]= (unsigned char)256;
    buf[0x04]= (unsigned char)(256>>8);

    tmp = 80 * 2 * 16;
    buf[0x0b]= (unsigned char)tmp;
    buf[0x0c]= (unsigned char)(tmp>>8);

    buf[0x10]= (unsigned char)16;
    buf[0x11]= (unsigned char)(16>>8);
    buf[0x12]= (unsigned char)2;

    tmp = (80 * 2 * 16 * 256)>>10;
    sprintf(&buf[0x1c],"%dK MAME emulation floppy",tmp);

    buf[0x5a]= (unsigned char)80;
    buf[0x5b]= (unsigned char)80;

    sprintf(&buf[0x60],"** NONE **"); // volume label  0x60

    fwrite(buf,1,133,ofp);   // write header preliminary

    rd_cyl = 0;
    rd_head = 0;
    rd_sec = 1;
    rd_track = 0;

    crc = 0l;
    do {
         rd_ptr = get_wr_ptr( ((rd_head & 0x01)<<12) | (((rd_sec-1) & 0x0f)<<8) | (rd_cyl & 0xff));
         drv_qm_dump_compressed( ofp, &crc, rd_ptr, 256);

         rd_sec++;
         if(rd_sec > 16) {
            rd_sec=1;
            rd_track++;

            rd_cyl = (rd_track / 2);
            rd_head = (rd_track % 2);
         }
   } while((rd_cyl < 80) && (rd_head < 2) && (rd_sec < (1 + 16)));

   buf[0x5c]= (unsigned char)crc;
   buf[0x5d]= (unsigned char)(crc>>8);
   buf[0x5e]= (unsigned char)(crc>>16);
   buf[0x5f]= (unsigned char)(crc>>24);

   crc = 0l;
   for(tmp=0; tmp<132; tmp++) crc+=buf[tmp];

   crc = - crc;
   buf[0x84]= (unsigned char)crc;

   fseek(ofp,0l,SEEK_SET);
   fwrite(buf,1,133,ofp);   // write header final

   fclose(ofp);
}

void dump_sector_hex(FILE* ofp,int cyl, int head, int sector)
{
   int m,n;
   unsigned char* pSec;

   fprintf(ofp,"ndrv_qm_read(cylinder=%d,head=%d,sector=%d)\n",cyl,head,sector+1);

   pSec = disk + (256*(32*cyl + 16*head + sector));

   for (n = 0; n < 256; n = ((n / 16)+1)*16)
   {
       int base = (n/16)*16;
       int c = 0;

       fprintf(ofp,"%04x: ", n);
       for (m = 0; m < (n%16); m++)
       {
          fputc(' ',ofp);
          fputc(' ',ofp);
          if ((c & 3) == 3) fputc(' ',ofp);
          c++;
       }
       for (m = (n % 16); m < 16; m++)
       {
          fprintf(ofp,"%02x", pSec[base+m]);
          if ((c & 3) == 3) fputc(' ',ofp);
          c++;
          if ((n + m + 1) >= 256) break;
       }
       for (; c < 16; c++)
       {
          fputc(' ',ofp);
          fputc(' ',ofp);
          if ((c & 3) == 3) fputc(' ',ofp);
       }
       fprintf(ofp," *");
       for (m = 0; m < (n%16); m++) fputc(' ',ofp);

       for (m = (n % 16); m < 16; m++)
       {
          if (isprint(pSec[base+m]))
          fputc(pSec[base+m],ofp);
          else    fputc('.',ofp);
          if ((n + m + 1) >= 256) break;
       }
       fputc('*',ofp);
       fputc('\n',ofp);
  }
}

int sec_alloc(unsigned short sec_id)
{
    int sector, head, cylinder;
    int idx_totl, idx_byte;
    unsigned char idx_bits;

    cylinder= sec_id & 0xff;
    head = (sec_id & 0x1000)>>12;
    sector= (sec_id & 0x0f00)>>8;

    idx_totl = 2*16*cylinder + 2*head + sector;
    idx_byte = idx_totl>>3;
    idx_bits = 0x80>>(idx_totl & 0x07);

    allocs[idx_byte] |= idx_bits;
    freeblocks--;
}

unsigned short alloc_free_sector(void)
{
  int i,n,m,c,h,s;

  for(i=0; (i<320) && (allocs[i]==0xff); i++);        // find first empty sector in alloc string

  if(i>=320) return 0xffff;                           // disk full

  for(n=i<<3, m=0x80; (m & allocs[i]); n++, m=m>>1);  // find first empty sector in this alloc byte

  allocs[i] |= m;      // allocate this sector
  freeblocks--;        // one more sector used

  c=n/32;              // cylinders, given 32 sectors/cylinder
  h=((n%32)>15)? 1:0;  // head, given 16 sectors/side
  s=(n%32) & 0x0f;     // sector, given 16 sectors/side

  return (((h & 0x01)<<12) | ((s & 0x0f)<<8) | (c & 0xff));  // sector id
}

unsigned char* get_wr_ptr(unsigned short sec_id)
{
    int sector, head, cylinder;

    cylinder= sec_id & 0xff;
    head = (sec_id & 0x1000)>>12;
    sector= (sec_id & 0x0f00)>>8;

    return disk + (256*(32*cylinder + 16*head + sector));
}

unsigned short update_directory(unsigned short dscr_blk, char* fname, int order)
{
    unsigned short ptr_blk, dir_blk;
    unsigned char* dsc_ptr,* ptr_ptr,* dir_ptr;
    char buf[40], *p;
    int n,i,j;

    p=strrchr(fname,'\\');         // DOS type directory separator
    if(!p) p=strrchr(fname,'/');   // UNIX type directory separator
    if(p)  p++;                    // set to file name
    else   p=fname;                // no hierarchy separator found

    n=strlen(p);
    if(n>32) {
       sprintf(buf,"D%02d%s",order & 0x3f,p+n-29);
       fprintf(stderr,"WARNING: file name %s length > 32 - shorten to %s\n",p,buf);
       n=32;
    } else {
       strcpy(buf,p);
    }
    printf("add file %s\n",buf);
       
    dsc_ptr=get_wr_ptr(0x0016);                                // get directory descriptor, for P8000 in 0x0016
    ptr_blk=(dsc_ptr[0x80]<<8) | dsc_ptr[0x81];                // get ptr sector (only one available assumed)
    ptr_ptr=get_wr_ptr(ptr_blk);                               // get ptr sector pointer

    for(i=2; i<=ptr_ptr[0xfa]; i+=2) {                         // go through all allocated directory data sectors
        dir_blk=(ptr_ptr[i]<<8) | ptr_ptr[i+1];                // current directory data pointer
        dir_ptr=get_wr_ptr(dir_blk);                           // translate to read address
        for(j=0; (j<256) && (dir_ptr[j]!=0xff); j++);          // find 0xff entry end marker
        if((j>=256) || ((j+n+3)>=255)) continue;               // this sector full or defective
        dir_ptr[j]=(unsigned char)n;                           // set name length
        strcpy(&dir_ptr[j+1],buf);                             // set name
        dir_ptr[j+n+1]=(unsigned char)(dscr_blk>>8);           // set descriptor head/sector
        dir_ptr[j+n+2]=(unsigned char)(dscr_blk);              // set descriptor cylinder
        dir_ptr[j+n+3]=0xff;                                   // set new end marker
        break;                                                 // all done, go no further
    }
    if(i>ptr_ptr[0xfa]) return(0xffff);                        // all entries taken

    return(dir_blk);                                           // this directory sector
}

unsigned short fill_data_sectors(FILE* ifp, unsigned short dscr_blk, int ptr_sectors, int rem_bytes, int sectors)
{
    unsigned short ptr_blk,  base_blk,  nxt_blk;
    unsigned char* ptr_ptr,* base_ptr,* nxt_ptr,* dscr_ptr;
    unsigned char buf[256];
    int i,j;

    dscr_ptr=get_wr_ptr(dscr_blk);                            // get descriptor accessor
    ptr_blk=(dscr_ptr[0x80]<<8) | dscr_ptr[0x81];             // first pointer sector
    ptr_ptr=get_wr_ptr(ptr_blk);                              // first pointer pointer
    memset(ptr_ptr,0,256);                                    // clean up pointer sector
    ptr_ptr[0x00]=(unsigned char)(dscr_blk>>8);               // enter descriptor head/sector
    ptr_ptr[0x01]=(unsigned char)dscr_blk;                    // enter descriptor cylinder
    ptr_ptr[0x02]=dscr_ptr[0x08];                             // first data block
    ptr_ptr[0x03]=dscr_ptr[0x09];                             // first data block
    ptr_ptr[0xfa]=0x02;                                       // last entry for now
    ptr_ptr[0xfc]=(unsigned char)(dscr_blk>>8);               // enter descriptor head/sector
    ptr_ptr[0xfd]=(unsigned char)dscr_blk;                    // enter descriptor cylinder
    ptr_ptr[0xfe]=0xff;                                       // mark as last pointer sector
    ptr_ptr[0xff]=0xff;                                       // mark as last pointer sector

    if(sectors==0) {                                          // empty files
       nxt_blk=(ptr_ptr[0x02]<<8) | ptr_ptr[0x03];            // last and only data sector
       nxt_ptr=get_wr_ptr(nxt_blk);                           // get write pointer
       memset(nxt_ptr,0,256);                                 // clean up segment
       return(nxt_blk);                                       // last data block
    }

    // allocate all pointer sectors /////////////////////////////////////////////////////////////////////////////
    base_blk=ptr_blk;                                         // initialize base 
    base_ptr=ptr_ptr;                                         // initialize base pointer
    for(i=1; i<ptr_sectors; i++) {                            // allocate all pointer sectors
        nxt_blk=alloc_free_sector();                          // allocate next pointer segment
        nxt_ptr=get_wr_ptr(nxt_blk);                          // get write pointer
        memset(nxt_ptr,0,256);                                // clean up segment
        nxt_ptr[0xfc]=(unsigned char)(base_blk>>8);           // mark previous sector head/sector
        nxt_ptr[0xfd]=(unsigned char)base_blk;                // mark previous sector cylinder
        nxt_ptr[0xfe]=0xff;                                   // mark end of sectors
        nxt_ptr[0xff]=0xff;                                   // mark end of sectors
        base_ptr[0xfe]=(unsigned char)(nxt_blk>>8);           // link in new block head/sector
        base_ptr[0xff]=(unsigned char)nxt_blk;                // link in new block cylinder
        base_blk=nxt_blk;                                     // advance base
        base_ptr=nxt_ptr;                                     // advance base
    }

    // allocate and fill all data sectors ////////////////////////////////////////////////////////////////////////
    base_blk=ptr_blk;                                         // initialize base 
    base_ptr=ptr_ptr;                                         // initialize base pointer
    nxt_blk=(ptr_ptr[0x02]<<8)|ptr_ptr[0x03];                 // first data block
    nxt_ptr=get_wr_ptr(nxt_blk);                              // get write pointer
    for(i=0, j=1; i<sectors; i++) {                           // over all required data sectors
        if(i) {                                               // sector zero already allocated
           nxt_blk=alloc_free_sector();                       // alloc new data sector
           nxt_ptr=get_wr_ptr(nxt_blk);                       // get write pointer
        }
        base_ptr[j<<1]=(unsigned char)(nxt_blk>>8);           // set new data sector head/sector
        base_ptr[(j<<1)+1]=(unsigned char)nxt_blk;            // set new data sector cylinder
        base_ptr[0xfa]=j<<1;                                  // set last pointer in this segment
        base_ptr[0xfb]=0x00;                                  // set last pointer in this segment

        memset(buf,0x00,256);                                 // initialize for last sector trash tail
        fread(buf,1,256,ifp);                                 // get file data
        memcpy(nxt_ptr,buf,256);                              // copy data, last sector tails trash

        j++;
        if(j>=125) {                                          // this pointer sector full
           j=0;                                               // reset entry index
           base_blk=(base_ptr[0xfe]<<8) | base_ptr[0xff];     // get next block pointer
           base_ptr=get_wr_ptr(base_blk);                     // get write pointer
           if(base_blk==0xffff) {                             // should not happen tho
              fprintf(stderr,"ERROR: pointer list error - abort\n");
              exit (-1);
           }
        }
    }

    return(nxt_blk);
}

int create_empty_disk(void)
{
    struct timeb timeb;
    struct tm    tm_creat;
    unsigned char* wptr;
    int i;

    // create empty disk and allocation table /////////////////////////////////////////////
    if(disk) free(disk);                 // if previously allocated
    disk=malloc(80*16*2*256);            // allocate 640K disk
    memset(disk,0xe5,80*16*2*256);       // format disk
    allocs=get_wr_ptr(0x0017);           // disk allocation table (cyl=23,head=0,sector=0)
    memset(allocs,0,512);                // clear both allocation table sectors
    sprintf(allocs,"P8000emu - makeUDOSdisk"); // !! relies on 23byte string
    allocs[23]=0x0d;                     // fill disk label (relies on 23bytes before)
    memset(allocs+24,0,320);             // clear allocation table
    memset(allocs+0x15c,0x77,32);        // set UDOS magic - don't know what this is
    allocs+=24;                          // set to allocation table
    freeblocks=2560;                     // initialize free block counter
    sec_alloc(0x0017);                   // now allocate table sector 1
    sec_alloc(0x0117);                   // now allocate table sector 2

    ftime(&timeb);                       // get creation time
    localtime_r(&timeb.time, &tm_creat); // translate to ascii

    // create initial directory sectors /////////////////////////////
    sec_alloc(0x0516);             // Directory 1st sector
    wptr=get_wr_ptr(0x0516);       // get Directory wr pointer
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0x89;               // set DIRECTORY string length
    sprintf(&wptr[1],"DIRECTORY"); // name this sector
    wptr[10]=0x00;                 // descriptor - head/sector
    wptr[11]=0x16;                 // descriptor - cylinder
    wptr[12]=0xff;                 // mark this as last entry
    sec_alloc(0x0a16);             // 2nd directory data sector
    wptr=get_wr_ptr(0x0a16);       // 
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0xff;               // 
    sec_alloc(0x0116);             // 3rd directory data sector
    wptr=get_wr_ptr(0x0116);       // 
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0xff;               // 
    sec_alloc(0x0116);             // 3rd directory data sector
    wptr=get_wr_ptr(0x0116);       // 
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0xff;               // 
    sec_alloc(0x0616);             // 4th directory data sector
    wptr=get_wr_ptr(0x0616);       // 
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0xff;               // 
    sec_alloc(0x0b16);             // 5th directory data sector
    wptr=get_wr_ptr(0x0b16);       // 
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0xff;               // 

    // create initial descriptor sector /////////////////////////////
    sec_alloc(0x0016);             // Directory Descriptor
    wptr=get_wr_ptr(0x0016);       // get Descriptor wr pointer
    memset(wptr,0,256);            // clear sector
    wptr[0x06]=0x05;               // return ptr to directory
    wptr[0x07]=0x16;
    wptr[0x08]=0x05;               // pre to first data sector
    wptr[0x09]=0x16;
    wptr[0x0a]=0x0b;               // pre to last data sector
    wptr[0x0b]=0x16;
    wptr[0x0c]=0x40;               // file type D
    wptr[0x0d]=0x05;               // number of data sectors in file
    wptr[0x0e]=0x00;
    wptr[0x0f]=0x00;               // sector size
    wptr[0x10]=0x01;
    wptr[0x11]=0x00;               // sector size
    wptr[0x12]=0x01;
    wptr[0x13]=0xf0;               // file properties WELS
    sprintf(&wptr[0x18],"%02d%02d%02d%c",tm_creat.tm_year-100,tm_creat.tm_mon,tm_creat.tm_mday,0xff);
    sprintf(&wptr[0x20],"%02d%02d%02d%c",tm_creat.tm_year-100,tm_creat.tm_mon,tm_creat.tm_mday,0xff);
    wptr[0x80]=0x02;               // id of first pointer sector
    wptr[0x81]=0x16;

    // create initial data pointer sector /////////////////////////////
    sec_alloc(0x0216);             // Directory Data Pointer Sector
    wptr=get_wr_ptr(0x0216);       // get Descriptor wr pointer
    memset(wptr,0,256);            // clear sector
    wptr[0x00]=0x00;               // descriptor pointer
    wptr[0x01]=0x16;
    wptr[0x02]=0x05;               // first data pointer for directory
    wptr[0x03]=0x16;
    wptr[0x04]=0x0a;               // second data pointer for directory
    wptr[0x05]=0x16;
    wptr[0x06]=0x01;               // 3rd data pointer for directory
    wptr[0x07]=0x16;
    wptr[0x08]=0x06;               // 4th data pointer for directory
    wptr[0x09]=0x16;
    wptr[0x0a]=0x0b;               // 5th data pointer for directory
    wptr[0x0b]=0x16;
    wptr[0x0c]=0xff;               // end marker
    wptr[0x0d]=0xff;

    wptr[0xfa]=0x0a;               // last data pointer for directory
    wptr[0xfb]=0x00;
    wptr[0xfc]=0x00;               // previous ptr - descriptor
    wptr[0xfd]=0x16;
    wptr[0xfe]=0xff;               // no next pointer
    wptr[0xff]=0xff;
}

int main(int argc, char** argv)
{
   FILE* ifp, *ofp;
   int i,c,s,h;

   unsigned short dscr_blk, ptr_blk, data_blk, last_blk, dir_blk;
   unsigned char* dscr_ptr;

   struct timeb timeb;
   struct stat fstatus;
   struct tm   tm_creat;

   create_empty_disk();

   for(i=1; i<argc; i++) {                 // fill in any file from command line

       if((ifp=fopen(argv[i],"rb"))!=NULL) {

           fstat(fileno(ifp), &fstatus);        // get file size
           ftime(&timeb);                       // get creation time
           localtime_r(&timeb.time, &tm_creat); // translate to ascii
           s=fstatus.st_size>>8;                // sector count
           h=fstatus.st_size & 0xff;            // last sector remainder
           if(h) s++;                           // true sector count
           c=(1+s)/125;                         // initial data/dscr pointer sectors
           if((1+s)%125) c++;                   // correct number of data/dscr sectors

           if((s+c)>freeblocks) {
               fprintf(stderr,"ERROR: not enough space for file %s (%d bytes) - omitted\n",argv[i],fstatus.st_size);
               fclose(ifp);
               continue;
           }

           dscr_blk=alloc_free_sector();                            // alloc descriptor block
           dscr_ptr=get_wr_ptr(dscr_blk);                           // get descriptor write handle
           ptr_blk=alloc_free_sector();                             // alloc first pointer block
           data_blk=alloc_free_sector();                            // alloc first data pointer block
           dir_blk=update_directory(dscr_blk,argv[i],i);            // create directory entry

           if(dir_blk==0xffff) {          // P8000 UDOS allocates 5 initial directory sectors, we leave it by that for now
              fprintf(stderr,"ERROR: Directory space overflow - omit file %s and beyond\n",argv[i]);
              fclose(ifp);
              break;
           }

           // fill in file descriptor ////////////////////////////////////////////////////////////////////////////
           memset(dscr_ptr,0,256);                                  // erase block
           dscr_ptr[0x06]=(unsigned char)(dir_blk>>8);              // head/sector directory
           dscr_ptr[0x07]=(unsigned char)dir_blk ;                  // cylinder directory
           dscr_ptr[0x08]=(unsigned char)(data_blk>>8);             // head/sector directory
           dscr_ptr[0x09]=(unsigned char)data_blk ;                 // cylinder directory
           dscr_ptr[0x0c]=0x20;                                     // data type ASCII
           dscr_ptr[0x0d]=(unsigned char)s;                         // used data sectors lo
           dscr_ptr[0x0e]=(unsigned char)(s>>8);                    // used data sectors hi
           dscr_ptr[0x10]=0x01;                                     // sector length 256
           dscr_ptr[0x12]=0x01;                                     // sector length 256
           dscr_ptr[0x13]=0x08;                                     // type R random access
           dscr_ptr[0x16]=(unsigned char)h;                         // bytes in last sector
           sprintf(&dscr_ptr[0x18],"%02d%02d%02d%c",tm_creat.tm_year-100,tm_creat.tm_mon,tm_creat.tm_mday,0xff);
           sprintf(&dscr_ptr[0x20],"%02d%02d%02d%c",tm_creat.tm_year-100,tm_creat.tm_mon,tm_creat.tm_mday,0xff);
           dscr_ptr[0x80]=(unsigned char)(ptr_blk>>8);              // first ptr sector - head/sector
           dscr_ptr[0x81]=(unsigned char)(ptr_blk);                 // first ptr sector - cylinder

           last_blk=fill_data_sectors(ifp,dscr_blk,c,h,s);          // store all data, fill ptr sectors
           dscr_ptr[0x0a]=(unsigned char)(last_blk>>8);             // last data sector, head/sector
           dscr_ptr[0x0b]=(unsigned char)(last_blk);                // last data sector, cylinder

           fclose(ifp);                                             // all done.

       } else {
           fprintf(stderr,"ERROR: unable to open file %s - omitted\n",argv[i]);
       }
   }

   dscr_ptr=get_wr_ptr(0x0117);
   dscr_ptr[0x77]=(unsigned char)(2560-freeblocks-1);               // used blocks low
   dscr_ptr[0x78]=(unsigned char)((2560-freeblocks-1)>>8);          // used blocks high
   dscr_ptr[0x79]=0x01;                                             // UDOS magic - don't know
   dscr_ptr[0x7a]=0x20;                                             // UDOS magic - don't know
   dscr_ptr[0x7b]=0x50;                                             // UDOS magic - don't know
   dscr_ptr[0x7c]=(unsigned char)(1+freeblocks);                    // free blocks low
   dscr_ptr[0x7d]=(unsigned char)((1+freeblocks)>>8);               // free blocks high

   qm_image_save("udosdsk.cqm");

   if((ofp=fopen("udosdsk.hex","wb"))!=NULL) {
       c=h=s=0;
       while(1) {
           dump_sector_hex(ofp,c,h,s);
           s++;
           if(s>=16) {
              if(h>=1) {
                 c++;
                 if(c<80) {
                    h=0;
                    s=0;
                 } else 
                    break;
              } else {
                 h=1;
                 s=0;
              }
           }
       }
       fclose(ofp);
   }

   printf("create debug image: ... udosdsk.hex\n");
   printf("create disk image: ... udosdsk.cqm\n");
   printf("Done.\n");

   return 0;
}


