home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 422_02 / misc / mdcfs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-20  |  22.0 KB  |  811 lines

  1. /*
  2.  * MDCFS: Minimal Dos Compatible File System
  3.  *
  4.  *   These routines provide the bare minimum needed to read and write
  5.  * files on an MS-DOS format floppy disk. You could use them with a hard
  6.  * disk as well, however since only 12 bit FAT's are supported, you are
  7.  * limited to a total of 4096 clusters, and total drive space is limited
  8.  * to 32MB due to 16 bit sector numbers (assuming 512 byte sectors).
  9.  *
  10.  *   The functions were written for use in embedded systems (where memory
  11.  * is often limited), and therefore provide only the basic open, read/write,
  12.  * close and delete operations. I have documented the functions which
  13.  * manipulate the directory and FAT, and it should be fairly easy to add
  14.  * other features if you need them (directory display etc.). Only access to
  15.  * files in the ROOT directory is provided, subdirectories are NOT supported.
  16.  *
  17.  *   For simplicity and memory conservation, these functions buffer only 1
  18.  * sector (512 bytes) in memory. This makes them run quite slowly, but is
  19.  * adaquate for reading/writing setup information and occational data logging.
  20.  * If you have lots of memory and need extra speed, you could modify the
  21.  * functions to read/write multiple sectors (a cluster would be easy).
  22.  * You can also experiment with different interleave factors, to obtain
  23.  * optimim performance with the existing I/O functions.
  24.  *
  25.  *   As they stand, the functions really support access to only one drive
  26.  * at a time. You can use multiple drives if you call "open_drive()" between
  27.  * disk operations to the separate drives. This "switches" the active drive
  28.  * to the specified one. Note however, that the selected drive will seek to
  29.  * track zero each time this function is called, so performing many small
  30.  * operations on more than one drive gets VERY inefficent. DO NOT read or
  31.  * write to an open file located on any drive other than the currently
  32.  * selected one! Call "open_drive()" first!
  33.  *
  34.  *   Concurrent access to multiple files (on the same drive) is supported,
  35.  * however since only one "work" buffer is used for directory/FAT access,
  36.  * the drive may have to perform extra read/write operation when switching
  37.  * from one file to the other. For this reason, it is best to try and do
  38.  * as many reads or writes as possible on one file before accessing others.
  39.  * Avoid many small operations to multiple files.
  40.  *
  41.  *   At present, only the first copy of the disk File Allocation Table
  42.  * (FAT) is used by these functions.
  43.  *
  44.  *   Functions read/write data in RAW (binary) form, without regard for
  45.  * NEWLINE characters etc. If you want to read/write ASCII text, you will
  46.  * have to write "wrapper" functions to drop RETURN (0x0D) characters on
  47.  * reading, and to add them before NEWLINE (0x0A) when writing.
  48.  *
  49.  *   Due to the use of 'C' structures, Version 3.0 (or later) of MICRO-C
  50.  * is REQUIRED! It should not be difficult to compile with a different
  51.  * compiler, but I have not attempted to do so.
  52.  *
  53.  * Copyright 1993-1994 Dave Dunfield
  54.  * All rights reserved.
  55.  *
  56.  * Permission granted for personal (non-commercial) use only.
  57.  */
  58.  
  59.  
  60. /* Required definitions from MICRO-C stdio.h (not part of MDCFS) */
  61. extern register printf();
  62. #define    FILE    unsigned
  63.  
  64.  
  65. /* Misc fixed parameters */
  66. #define    SECTOR_SIZE    512        /* Size of a disk sector */
  67. #define    BPB_SIZE    17        /* Number of bytes in BIOS Parm Block */
  68. #define    EMPTY        0xE5    /* Signals empty directory */
  69. #define    EOF            -1        /* End of file indicator */
  70. #define    ERROR        -2        /* Report error in file */
  71. #define    READ        0        /* File opened for READ */
  72. #define    WRITE        1        /* File opened for WRITE */
  73.  
  74.  
  75. /*
  76.  * Structure of MSDOS directory entry
  77.  */
  78. struct Dentry {
  79.     unsigned char    Dname[11];        /* Filename + extension */
  80.     unsigned char    Dattr;            /* File attributes */
  81.     unsigned char    Dreserved[10];    /* Reserved area */
  82.     unsigned        Dtime;            /* Time last modified */
  83.     unsigned        Ddata;            /* Date last modified */
  84.     unsigned        Dcluster;        /* First cluster number */
  85.     unsigned        Dsizel;            /* File size (LOW) */
  86.     unsigned        Dsizeh; } ;        /* File size (HIGH) */
  87.  
  88. /*
  89.  * Structure of internal file control block
  90.  */
  91. struct Fblock {
  92.     unsigned char     Fattr;            /* Open attributes */
  93.     unsigned char     Fsector;        /* Sector within cluster */
  94.     struct Dentry    *Fdirptr;        /* Pointer to directory entry */
  95.     unsigned        Fdirsec;        /* Directory sector */
  96.     unsigned        Ffirstcls;        /* First cluster in file */
  97.     unsigned        Flastcls;        /* Last cluster read/written */
  98.     unsigned        Fnextcls;        /* Next cluster to read/write */
  99.     unsigned        Foffset;        /* Read/Write offset */
  100.     unsigned        Fsizel;            /* File size (LOW) */
  101.     unsigned        Fsizeh;            /* File size (HIGH) */
  102.     unsigned char    Fbuffer[]; } ;    /* Data transfer buffer */
  103.  
  104.  
  105. /* Internal "work" sector variables */
  106. unsigned    wrkdrv = 0,                /* Current work drive number */
  107.              wrksec = -1;            /* Current work sector number */
  108. char        wrkchg = 0;                /* Indicates work sector changed */
  109. unsigned char wrkbuff[SECTOR_SIZE];    /* Work sector buffer */
  110.  
  111. /* Active drive information (other than contained in BPB) */
  112. char        active_drive = -1;        /* Open disk drive number */
  113. unsigned    dirsec = 5,                /* First sector of directory */
  114.             datasec = 12;            /* First sector of data area */
  115.  
  116. /* Disk information (from BIOS Parameter Block) */
  117. unsigned int    bytsec    = 512;        /* Bytes / sector */
  118. unsigned char    seccls    = 2;        /* Sectors / cluster */
  119. unsigned int    ressec    = 1;        /* # reserved sectors */
  120. unsigned char    numfat    = 2;        /* Number of FAT's */
  121. unsigned int    dirent    = 112;        /* Number of directory entries */
  122. unsigned int    sectors    = 720;        /* Sectors on disk */
  123. unsigned char    mediaid    = 0xFD;        /* Media ID byte */
  124. unsigned int    secfat    = 2;        /* Sectors / fat */
  125. unsigned int    sectrk    = 9;        /* Sectors / track */
  126. unsigned int    numhead    = 2;        /* Number of heads */
  127.  
  128.  
  129. /*
  130.  * Function Prototypes
  131.  */
  132. extern struct Dentry *lookup(), *create_file();
  133.  
  134.  
  135. /*
  136.  * File accessing functions:
  137.  *
  138.  *    open_drive(drive)            - Initialize a drive for file access
  139.  *        drive    - Drive id (0=A, 1=B ...)
  140.  *
  141.  *    open_file(name, attrs)        - Open file for read or write
  142.  *        name    - Name of file to open
  143.  *        attrs    - Open attributes: READ or WRITE
  144.  *        returns : Pointer to file structure, or 0 if failure
  145.  *
  146.  *    close_file(fp)                - Close an open file
  147.  *        fp        - Pointer to open file structure
  148.  *
  149.  *    read_byte(fp)                - Read a byte from an open file
  150.  *        fp        - Pointer to open file structure
  151.  *        returns : Value read (0-255), -1 if EOF, -2 if not open for read
  152.  *
  153.  *    write_byte(byte, fp)        - Write a byte to a file
  154.  *        byte    - Value to write to file (0-255)
  155.  *        returns : Value written (0-255) or -2 if error
  156.  *
  157.  *    delete_file(name)            - Erases the named file
  158.  *        name    - Name of file to erase
  159.  *        returns : 0 if Success, -1 if failure (file not found)
  160.  */
  161.  
  162. /*
  163.  * Open a disk drive and set up control information
  164.  *
  165.  * THIS FUNCTION MUST BE CALLED BEFORE ACCESSING ANY FILES,
  166.  * AND ANYTIME YOU SWITCH TO ACCESS A DIFFERENT DRIVE.
  167.  */
  168. open_drive(drive)
  169.     char drive;
  170. {
  171.     read_work(active_drive = drive, 0);
  172.     memcpy(&bytsec, wrkbuff+11, BPB_SIZE);
  173.     dirsec    = (numfat * secfat) + ressec;
  174.     datasec    = ((((dirent * sizeof(struct Dentry)) + bytsec) - 1) / bytsec) + dirsec;
  175. }
  176.  
  177. /*
  178.  * Open a file & return a pointer to an allocated file structure
  179.  */
  180. struct Fblock *open_file(name, attrs)
  181.     char *name;
  182.     int attrs;
  183. {
  184.     struct Dentry *dirptr;
  185.     struct Fblock *fp;
  186.  
  187.     if(dirptr = lookup(name)) {                    /* File already exists */
  188.         if(attrs == WRITE)                        /* Zero size on write */
  189.             dirptr->Dsizel = dirptr->Dsizeh = 0; }
  190.     else {
  191.         if(attrs != WRITE)                        /* Not writing file */
  192.             return 0;
  193.         if(!(dirptr = create_file(name, 0)))    /* Unable to create */
  194.             return 0; }
  195.  
  196.     /* Allocate buffer for file control block */
  197.     if(!(fp = malloc(bytsec+sizeof(struct Fblock))))
  198.         return 0;
  199.  
  200.     /* Fill in file control block from directory information */
  201.     fp->Fdirsec        = wrksec;
  202.     fp->Fdirptr        = dirptr;
  203.     fp->Fattr        = attrs;
  204.     fp->Fnextcls    = fp->Ffirstcls = dirptr->Dcluster;
  205.     fp->Fsizel        = dirptr->Dsizel;
  206.     fp->Fsizeh        = dirptr->Dsizeh;
  207.     fp->Fsector        = fp->Foffset = fp->Flastcls = 0;
  208.  
  209.     return fp;
  210.  
  211. }
  212.  
  213. /*
  214.  * Close an open file
  215.  */
  216. close_file(fp)
  217.     struct Fblock *fp;
  218. {
  219.     struct Dentry *dirptr;
  220.     unsigned sizeh, sizel;
  221.  
  222.     /* Special actions to be taken when writing */
  223.     if(fp->Fattr == WRITE) {
  224.         sizel = fp->Fsizel;
  225.         sizeh = fp->Fsizeh;
  226.         /* If there is a partial last sector, fill it with DOS EOF */
  227.         /* characters, which also causes it to be written out */
  228.         while(fp->Foffset)
  229.             write_byte(0x1A, fp);
  230.         /* Release remaining clusters in file. If any sectors in the */
  231.         /* current cluster are used, begin with the next one. */
  232.         release(fp->Fsector ? get_fat(fp->Fnextcls) : fp->Fnextcls);
  233.         /* Update size entry in file directory */
  234.         read_work(active_drive, fp->Fdirsec);
  235.         dirptr = fp->Fdirptr;
  236.         dirptr->Dcluster= fp->Ffirstcls;
  237.         dirptr->Dsizel    = sizel;
  238.         dirptr->Dsizeh    = sizeh;
  239.         /* Set directory date of modification here - if you wish */
  240.         wrkchg = -1; }
  241.  
  242.     free(fp);
  243.  
  244.     update_work();        /* Insure disk is in sync */
  245. }
  246.  
  247. /*
  248.  * Delete a file from the disk
  249.  */
  250. delete_file(name)
  251.     char *name;
  252. {
  253.     struct Dentry *dirptr;
  254.  
  255.     if(dirptr = lookup(name)) {
  256.         *dirptr->Dname = EMPTY;
  257.         wrkchg = -1;
  258.         release(dirptr->Dcluster);
  259.         update_work();
  260.         return 0; }
  261.     return -1;
  262. }
  263.  
  264. /*
  265.  * Read next byte from open file
  266.  */
  267. int read_byte(fp)
  268.     struct Fblock *fp;
  269. {
  270.     unsigned cluster, sector;
  271.  
  272.     /* If all data read, return EOF */
  273.     if(!(fp->Fsizel || fp->Fsizeh)) {
  274.         return EOF; }
  275.  
  276.     /* Decrement file size (32 bit) */
  277.     if((--fp->Fsizel) == -1)
  278.         --fp->Fsizeh;
  279.  
  280.     /* If no data buffered ... read next sector */
  281.     if((fp->Foffset >= bytsec) || !fp->Foffset) {
  282.         /* If not open for READ, return ERROR */
  283.         if(fp->Fattr != READ)
  284.             return ERROR;
  285.  
  286.         /* If past last cluster, return EOF */
  287.         if(((cluster = fp->Fnextcls) >= 0xFF8) || !cluster)
  288.             return EOF;
  289.  
  290.         /* Read the sector into memory */
  291.         read_sector(active_drive,
  292.             (sector = fp->Fsector) + ((cluster-2)*seccls) + datasec,
  293.             fp->Fbuffer);
  294.  
  295.         /* Advance sector in cluster, if past end, get next cluster number */
  296.         if(++sector >= seccls) {
  297.             fp->Fnextcls = get_fat(cluster);
  298.             sector = 0; }
  299.  
  300.         /* Update file control block information */
  301.         fp->Flastcls    = cluster;
  302.         fp->Fsector        = sector;
  303.         fp->Foffset        = 0; }
  304.  
  305.     /* Read character, and advance circular buffer pointer */
  306.     return fp->Fbuffer[fp->Foffset++];
  307. }
  308.  
  309. /*
  310.  * Write a byte to an open file
  311.  */
  312. int write_byte(c, fp)
  313.     unsigned c;
  314.     struct Fblock *fp;
  315. {
  316.     unsigned cluster, sector;
  317.  
  318.     /* Test for writable file */
  319.     if(fp->Fattr != WRITE)
  320.         return ERROR;
  321.  
  322.     /* Advance file size */
  323.     if(!++fp->Fsizel)
  324.         ++fp->Fsizeh;
  325.  
  326.     /* Write character to buffer */
  327.     fp->Fbuffer[fp->Foffset++] = c;
  328.  
  329.     /* If buffer is full, write it */
  330.     if(fp->Foffset >= bytsec) {
  331.         /* If no sector allocated, allocate one */
  332.         if(!fp->Fnextcls)
  333.             fp->Ffirstcls = fp->Fnextcls = allocate(0);
  334.         else if(fp->Fnextcls >= 0xFF8)
  335.             fp->Fnextcls = allocate(fp->Flastcls);
  336.  
  337.         if(!(cluster = fp->Fnextcls))
  338.             return ERROR;
  339.  
  340.         /* Write the data to the drive */    
  341.         write_sector(active_drive,
  342.             (sector = fp->Fsector) + ((cluster-2)*seccls) + datasec,
  343.             fp->Fbuffer);
  344.  
  345.         /* Advance to next sector in cluster */
  346.         if(++sector >= seccls) {
  347.             fp->Fnextcls = get_fat(cluster);
  348.             sector = 0; }
  349.  
  350.         /* Update file control block information */
  351.         fp->Flastcls    = cluster;
  352.         fp->Fsector        = sector;
  353.         fp->Foffset        = 0; }
  354.  
  355.     return c;
  356. }
  357.  
  358.  
  359. /*
  360.  * FAT manipulation functions:
  361.  *
  362.  *    allocate(cluster)            - Allocate a free cluster
  363.  *        cluster    - Cluster to link this one to (o if none).
  364.  *        returns : Cluster number allocated, 0 if failure
  365.  *
  366.  *    release(cluster)            - Release a cluster chain (if allocated)
  367.  *        cluster    - Beginnig cluster to release
  368.  *
  369.  *    get_fat(cluster)            - Get FAT entry for cluster
  370.  *        cluster    - Cluster number to obtain entry for
  371.  *        returns : Cluster number linked (0 if free, 0xFF8+ for EOF)
  372.  *
  373.  *    set_fat(cluster, link)        - Set the FAT entry for a cluster
  374.  *        cluster    - Cluster number to set link for
  375.  *        link    - Cluster number to link to (0=free, 0xFFF = EOF)
  376.  */
  377.  
  378. /*
  379.  * Allocate a free cluster on disk, and cross connect FAT if necessary
  380.  * mark cluster as used & end of file.
  381.  */
  382. int allocate(cluster)
  383.     int cluster;
  384. {
  385.     int begin, end, i;
  386.  
  387.     /* Calculate start and end clusters for search */
  388.     /* If we have a "FAT" sector loaded, begin searching from there, */
  389.     /* To attempt to keep allocated sectors in same "FAT" sector */
  390.  
  391.     begin = ((i = wrksec-1) && (i <= secfat)) ?
  392.         ((i * (SECTOR_SIZE*2)) / 3) + 1 : 2;
  393.     end = (sectors - datasec) / seccls;
  394.  
  395.     do {
  396. #ifdef    DEBUG
  397.         printf("Allocate(%u) : Wrk=%u Begin=%u End=%u\n", cluster, wrksec, begin, end);
  398. #endif
  399.         for(i = begin; i < end; ++i)
  400.             if(!get_fat(i)) {
  401. #ifdef    DEBUG
  402.             printf("Allocated %u\n", i);
  403. #endif
  404.                 set_fat(i, 0xFFF);
  405.                 if(cluster)
  406.                     set_fat(cluster, i);
  407.                 return i; }
  408.         /* Didn't find, reset to start cluster, and try again */
  409.         end = begin;
  410.         begin = 2; }
  411.     while(end != 2);
  412.  
  413. #ifdef    DEBUG
  414.     printf("Failed!\n");
  415. #endif
  416.     return 0;
  417. }
  418.  
  419. /*
  420.  * Release a chain of allocated clusters
  421.  */
  422. release(cluster)
  423.     int cluster;
  424. {
  425.     unsigned i;
  426.     while(cluster && (cluster < 0xFF8)) {
  427.         i = get_fat(cluster);
  428.         set_fat(cluster, 0);
  429.         cluster = i; }
  430. }
  431.  
  432. /*
  433.  * Get a FAT entry for a given cluster number
  434.  */
  435. int get_fat(cluster)
  436.     unsigned cluster;
  437. {
  438.     unsigned sec, fat;
  439.  
  440. #ifdef    DEBUG
  441.     printf("Get FAT for %u - ", cluster);
  442. #endif
  443.  
  444.     sec = (cluster *= 3)/2;
  445.     read_work(active_drive, (sec / bytsec) + 1);
  446.     fat = wrkbuff[sec % bytsec];
  447.     read_work(active_drive, (++sec / bytsec) + 1);
  448.     fat |= wrkbuff[sec % bytsec] << 8;
  449.     if(cluster & 1)
  450.         fat >>= 4;
  451.  
  452. #ifdef    DEBUG
  453.     printf("%u\n", fat & 0xFFF);
  454. #endif
  455.  
  456.     return fat & 0xfff;
  457. }
  458.  
  459. /*
  460.  * Set a FAT entry for a given cluster number
  461.  */
  462. set_fat(cluster, value)
  463.     unsigned cluster, value;
  464. {
  465.     unsigned sec, x;
  466.  
  467. #ifdef    DEBUG
  468.     printf("Set FAT for %u - %u\n", cluster, value);
  469. #endif
  470.  
  471.     sec = (cluster *= 3)/2;
  472.  
  473.     /* Set LOW byte of FAT */
  474.     read_work(active_drive, (sec / bytsec) + 1);
  475.     x = sec % bytsec;
  476.     if(cluster & 1)
  477.         wrkbuff[x] = (wrkbuff[x] & 0x0F) | (value << 4);
  478.     else
  479.         wrkbuff[x] = value;
  480.     wrkchg = -1;
  481.  
  482.     /* Set high byte of FAT */
  483.     read_work(active_drive, (++sec / bytsec) + 1);
  484.     x = sec % bytsec;
  485.     if(cluster & 1)
  486.         wrkbuff[x] = value >> 4;
  487.     else
  488.         wrkbuff[x] = (wrkbuff[x] & 0xF0) | ((value >> 8) & 0x0F);
  489.     wrkchg = -1;
  490. }
  491.  
  492.  
  493. /*
  494.  * Directory manipulation functions:
  495.  *
  496.  *    lookup(file)                - Locate a files directory entry
  497.  *        file    - Name of file to locate
  498.  *        returns : Pointer to directory entry, 0 if failure.
  499.  *
  500.  *    create_file(file,attrs)     - Create a mew file
  501.  *        file    - Name of file to create
  502.  *        attrs    - Attributes for file (0 = normal)
  503.  *        returns : Pointer to directory entry, 0 if failure.
  504.  *
  505.  *    parse_filename(buffer,name)    - Get filename in directory format
  506.  *        buffer    - Buffer for directory format filename (13 bytes)
  507.  *        name    - ASCII filename to convert
  508.  *
  509.  *    memcmp(ptr1, ptr2, size)    - Block memory compare
  510.  *        ptr1    - Pointer to first block
  511.  *        ptr2    - Pointer to second block
  512.  *        size    - Number of bytes to compare
  513.  *        returns    : -1 of match, 0 if different.
  514.  */
  515.  
  516. /*
  517.  * Lookup a directory entry & return pointer to it
  518.  */
  519. struct Dentry *lookup(file)
  520.     char *file;
  521. {
  522.     unsigned i, j, k;
  523.     char fbuffer[12];
  524.  
  525.     parse_filename(fbuffer, file);
  526.  
  527. #ifdef    DEBUG
  528.     printf("Lookup: '%s' ", fbuffer);
  529. #endif
  530.  
  531.     j = bytsec;
  532.     k = dirsec;
  533.     for(i=0; i < dirent; ++i) {
  534.         if(j >= bytsec) {
  535.             read_work(active_drive, k++);
  536.             j = 0; }
  537.         if(memcmp(wrkbuff+j, fbuffer, 11)) {
  538. #ifdef    DEBUG
  539.     printf("Found - Cluster: %u\n", *(int*)(wrkbuff+j+0x1A));
  540. #endif
  541.             return wrkbuff+j; }
  542.         j += sizeof(struct Dentry); }
  543.  
  544. #ifdef    DEBUG
  545.     printf("Not found\n");
  546. #endif
  547.     return 0;
  548. }
  549.  
  550. /*
  551.  * Create a file with the specified name
  552.  * NOTE: Does NOT check for duplicates. It is assumed that "lookup()"
  553.  * has been called first, to verify that the file does not already
  554.  * exist on the disk.
  555.  */
  556. struct Dentry *create_file(file, attrs)
  557.     char *file;
  558.     int attrs;
  559. {
  560.     int i, j, k;
  561.     char fbuffer[12];
  562.     struct Dentry *dirptr;
  563.  
  564.     parse_filename(fbuffer, file);
  565.  
  566. #ifdef    DEBUG
  567.     printf("Create '%s' ", fbuffer);
  568. #endif
  569.  
  570.     j = bytsec;
  571.     k = dirsec;
  572.     for(i=0; i < dirent; ++i) {
  573.         if(j >= bytsec) {
  574.             read_work(active_drive, k++);
  575.             j = 0; }
  576.         if((*(dirptr = wrkbuff+j)->Dname == EMPTY) || !*dirptr->Dname) {
  577. #ifdef    DEBUG
  578.     printf("Dir entry #%u\n", i);
  579. #endif
  580.             memset(dirptr, 0, sizeof(struct Dentry));
  581.             strcpy(dirptr, fbuffer);
  582.             dirptr->Dattr = attrs;
  583.             /* Set directory date of creation here - if you wish */
  584.             wrkchg = -1;
  585.             return wrkbuff+j; }
  586.     j += sizeof(struct Dentry); }
  587.  
  588. #ifdef    DEBUG
  589.     printf("No directory!\n");
  590. #endif
  591.  
  592.     return 0;
  593. }
  594.  
  595. /*
  596.  * Parse a filename into directory format (12 characters, space filled)
  597.  */
  598. parse_filename(buffer, name)
  599.     char buffer[], *name;
  600. {
  601.     int i;
  602.  
  603.     i = 0;
  604.     while(i < 8)
  605.         buffer[i++] = (*name && (*name != '.')) ? toupper(*name++) : ' ';
  606.     if(*name == '.')
  607.         ++name;
  608.     while(i < 11)
  609.         buffer[i++] = *name ? toupper(*name++) : ' ';
  610.     buffer[i] = 0;
  611. }
  612.  
  613. /*
  614.  * Memory to memory compare
  615.  */
  616. int memcmp(ptr1, ptr2, size)
  617.     char *ptr1, *ptr2;
  618.     unsigned size;
  619. {
  620.     while(size--)
  621.         if(*ptr1++ != *ptr2++)
  622.             return 0;
  623.     return -1;
  624. }
  625.  
  626.  
  627. /*
  628.  * Intermal "work" sector manipulation functions:
  629.  *
  630.  *    read_work(sector)        - Read sector into work buffer (if necessary)
  631.  *        sector    - Sector number to read.
  632.  *
  633.  *    update_work()            - Write work sector back to disk if changed
  634.  */
  635.  
  636. /*
  637.  * Read a work sector into memory if it is not already in memory.
  638.  */
  639. read_work(drive, sector)
  640.     char drive;
  641.     int sector;
  642. {
  643.     if((wrkdrv != drive) || (wrksec != sector)) {
  644.         update_work();
  645.         read_sector(wrkdrv = drive, wrksec = sector, wrkbuff); }
  646. }
  647.  
  648. /*
  649.  * Write the work sector if it has been marked as modified
  650.  */
  651. update_work()
  652. {
  653.     if(wrkchg) {
  654.         write_sector(wrkdrv, wrksec, wrkbuff);
  655.         wrkchg = 0; }
  656. }
  657.  
  658.  
  659. /*
  660.  * Low level disk I/O functions:
  661.  *
  662.  *    disk_error(code)        - Called if unrecoverable disk error
  663.  *        code    - Disk failure code (system depandant)
  664.  *
  665.  *    read_sector(d, s, b)    - Read a sector from the disk
  666.  *        d        - Drive to read (0=A, 1=B ...)
  667.  *        s        - Sector number to read (1-n)
  668.  *        b        - Pointer to buffer to receive data
  669.  *
  670.  *    write_sector(d, s, b)    - Write a sector to the disk
  671.  *        d        - Drive to write (0=A, 1=B ...)
  672.  *        s        - Sector number to write (1-n)
  673.  *        b        - Pointer to buffer containing the data
  674.  *
  675.  * These functions make use of the IBM PC BIOS, and are compatible
  676.  * with Microsoft MASM assembler. Use these if you are compiling the
  677.  * demo program with the MICRO-C DOS compiler.
  678.  */
  679.  
  680. /*
  681.  * Report a disk error
  682.  */
  683. disk_error(code)
  684.     int code;
  685. {
  686.     printf("Disk error - Code: %04x\n", code);
  687.     exit(-1);
  688. }
  689.  
  690. /*
  691.  * Read a sector from the disk drive
  692.  */
  693. read_sector(drive, sector, buffer) asm
  694. {
  695.         PUSH    DS            ; Save DS
  696.         POP        ES            ; Set ES
  697.         MOV        BX,4[BP]    ; Get buffer
  698.         MOV        AX,6[BP]    ; Get sector
  699.         XOR        DX,DX        ; Zero high
  700.         DIV        WORD PTR _sectrk; Calculate track
  701.         MOV        CL,DL        ; CL = sector
  702.         INC        CL            ; 1-
  703.         XOR        DX,DX        ; Zero high
  704.         DIV        WORD PTR _numhead; Compute head
  705.         MOV        CH,AL        ; CH = cylinder
  706.         MOV        DH,DL        ; DH = head
  707.         MOV        DL,8[BP]    ; DL = drive
  708.         MOV        DI,3        ; Try three times
  709. read1:    MOV        AX,0201h    ; Read 1 sector
  710.         INT        13h            ; Call BIOS
  711.         JNC        read2        ; Success
  712.         DEC        DI            ; Reduce count
  713.         JNZ        read1        ; Keep trying
  714.         PUSH    AX            ; Pass parameter
  715.         CALL    _disk_error    ; Report an error
  716.         POP        AX            ; Clean stack
  717. read2:    XOR        AX,AX        ; Zero return
  718. }
  719.  
  720. /*
  721.  * Write a sector to the disk drive
  722.  */
  723. write_sector(drive, sector, buffer) asm
  724. {
  725.         PUSH    DS            ; Save DS
  726.         POP        ES            ; Set ES
  727.         MOV        BX,4[BP]    ; Get buffer
  728.         MOV        AX,6[BP]    ; Get sector
  729.         XOR        DX,DX        ; Zero high
  730.         DIV        WORD PTR _sectrk; Calculate track
  731.         MOV        CL,DL        ; CL = sector
  732.         INC        CL            ; 1-
  733.         XOR        DX,DX        ; Zero high
  734.         DIV        WORD PTR _numhead; Compute head
  735.         MOV        CH,AL        ; CH = cylinder
  736.         MOV        DH,DL        ; DH = head
  737.         MOV        DL,8[BP]    ; DL = drive
  738.         MOV        DI,3        ; Try three times
  739. write1:    MOV        AX,0301h    ; Write 1 sector
  740.         INT        13h            ; Call BIOS
  741.         JNC        write2        ; Success
  742.         DEC        DI            ; Reduce count
  743.         JNZ        write1        ; Keep trying
  744.         PUSH    AX            ; Pass parameter
  745.         CALL    _disk_error    ; Report an error
  746.         POP        AX            ; Clean stack
  747. write2:    XOR        AX,AX        ; Zero return
  748. }
  749.  
  750.  
  751. /*
  752.  * Sample Main program (Not part of MDCFS) to demonstrate reading,
  753.  * writing and deleteing files. This also uses the standard file
  754.  * I/O of the MICRO-C DOS compiler, and will not run stand-alone.
  755.  *
  756.  * Demo command syntax:
  757.  *   MCDFS W <file>        - Copy file from current DOS dir to floppy
  758.  *   MCDFS R <file>     - Copy file from floppy to current DOS dir
  759.  *   MCDFS D <file>     - Delete file from floppy.
  760.  *
  761.  * All accesss to the floppy are performed via MCDFS.
  762.  */
  763. main(argc, argv)
  764.     int argc;
  765.     char *argv[];
  766. {
  767.     int c;
  768.     struct Fblock *fp;
  769.     FILE *xfp;
  770.  
  771.     open_drive(0);        /* Change to (1) for drive B: */
  772.     switch((argc > 2) ? toupper(*argv[1]) : 0) {
  773.  
  774.         case 'R' :        /* Read file from floppy drive */
  775.             if(!(fp = open_file(argv[2], READ)))
  776.                 abort("Couldn't read MDCFS file");
  777.             xfp = fopen(argv[2], "wbvq");
  778.             while((c = read_byte(fp)) >= 0)
  779.                 putc(c, xfp);
  780.             close_file(fp);
  781.             fclose(xfp);
  782.             c = 0;        /* Disk has not been modified */
  783.             break;
  784.  
  785.         case 'W' :        /* Write file to floppy drive */
  786.             xfp = fopen(argv[2], "rvbq");
  787.             if(!(fp = open_file(argv[2], WRITE)))
  788.                 abort("Couldn't write MDCFS file");
  789.             while((c = getc(xfp)) >= 0)
  790.                 write_byte(c, fp);
  791.             fclose(xfp);
  792.             close_file(fp);
  793.             c = -1;        /* Note that disk may have been written */
  794.             break;
  795.  
  796.         case 'D' :        /* Delete file from floppy drive */
  797.             delete_file(argv[2]);
  798.             c = -1;        /* Note that disk may have been written */
  799.             break;
  800.  
  801.         default:
  802.             abort("Use: MDCFS (Read/Write/Delete) <filename>"); }
  803.  
  804.     if(c) {
  805.         printf("\nNote: Since MDCFS bypasses DOS completely, DOS will be unaware\n");
  806.         printf("of any changes to the disk... Due to DOS buffering, files written\n");
  807.         printf("may not appear in DOS 'dir' command, or may have their sizes\n");
  808.         printf("reported incorrectly... Press CONTROL-C to force DOS to flush\n");
  809.         printf("its buffers before accessing the floppy disk"); }
  810. }
  811.