/* * MDCFS: Minimal Dos Compatible File System * * These routines provide the bare minimum needed to read and write * files on an MS-DOS format floppy disk. You could use them with a hard * disk as well, however since only 12 bit FAT's are supported, you are * limited to a total of 4096 clusters, and total drive space is limited * to 32MB due to 16 bit sector numbers (assuming 512 byte sectors). * * The functions were written for use in embedded systems (where memory * is often limited), and therefore provide only the basic open, read/write, * close and delete operations. I have documented the functions which * manipulate the directory and FAT, and it should be fairly easy to add * other features if you need them (directory display etc.). Only access to * files in the ROOT directory is provided, subdirectories are NOT supported. * * For simplicity and memory conservation, these functions buffer only 1 * sector (512 bytes) in memory. This makes them run quite slowly, but is * adaquate for reading/writing setup information and occational data logging. * If you have lots of memory and need extra speed, you could modify the * functions to read/write multiple sectors (a cluster would be easy). * You can also experiment with different interleave factors, to obtain * optimim performance with the existing I/O functions. * * As they stand, the functions really support access to only one drive * at a time. You can use multiple drives if you call "open_drive()" between * disk operations to the separate drives. This "switches" the active drive * to the specified one. Note however, that the selected drive will seek to * track zero each time this function is called, so performing many small * operations on more than one drive gets VERY inefficent. DO NOT read or * write to an open file located on any drive other than the currently * selected one! Call "open_drive()" first! * * Concurrent access to multiple files (on the same drive) is supported, * however since only one "work" buffer is used for directory/FAT access, * the drive may have to perform extra read/write operation when switching * from one file to the other. For this reason, it is best to try and do * as many reads or writes as possible on one file before accessing others. * Avoid many small operations to multiple files. * * At present, only the first copy of the disk File Allocation Table * (FAT) is used by these functions. * * Functions read/write data in RAW (binary) form, without regard for * NEWLINE characters etc. If you want to read/write ASCII text, you will * have to write "wrapper" functions to drop RETURN (0x0D) characters on * reading, and to add them before NEWLINE (0x0A) when writing. * * Due to the use of 'C' structures, Version 3.0 (or later) of MICRO-C * is REQUIRED! It should not be difficult to compile with a different * compiler, but I have not attempted to do so. * * You can compile this DEMO program to run under DOS with the MICRO-C * DOS compiler (available from our BBS: 613-256-6289). To use the functions * without DOS, replace the I/O routines (at the end of this file) with the * ones from the file DK86IO.C, and compile it with our stand-alone * 8086 Developers Kit. * * Copyright 1993-2000 Dave Dunfield * All rights reserved. * * Permission granted for personal (non-commercial) use only. */ /* Required definitions from MICRO-C stdio.h (not part of MDCFS) */ extern register printf(); #define FILE unsigned /* Misc fixed parameters */ #define SECTOR_SIZE 512 /* Size of a disk sector */ #define BPB_SIZE 17 /* Number of bytes in BIOS Parm Block */ #define EMPTY 0xE5 /* Signals empty directory */ #define EOF -1 /* End of file indicator */ #define ERROR -2 /* Report error in file */ #define READ 0 /* File opened for READ */ #define WRITE 1 /* File opened for WRITE */ /* * Structure of MSDOS directory entry */ struct Dentry { unsigned char Dname[11]; /* Filename + extension */ unsigned char Dattr; /* File attributes */ unsigned char Dreserved[10]; /* Reserved area */ unsigned Dtime; /* Time last modified */ unsigned Ddata; /* Date last modified */ unsigned Dcluster; /* First cluster number */ unsigned Dsizel; /* File size (LOW) */ unsigned Dsizeh; } ; /* File size (HIGH) */ /* * Structure of internal file control block */ struct Fblock { unsigned char Fattr; /* Open attributes */ unsigned char Fsector; /* Sector within cluster */ struct Dentry *Fdirptr; /* Pointer to directory entry */ unsigned Fdirsec; /* Directory sector */ unsigned Ffirstcls; /* First cluster in file */ unsigned Flastcls; /* Last cluster read/written */ unsigned Fnextcls; /* Next cluster to read/write */ unsigned Foffset; /* Read/Write offset */ unsigned Fsizel; /* File size (LOW) */ unsigned Fsizeh; /* File size (HIGH) */ unsigned char Fbuffer[]; } ; /* Data transfer buffer */ /* Internal "work" sector variables */ unsigned wrkdrv = 0, /* Current work drive number */ wrksec = -1; /* Current work sector number */ char wrkchg = 0; /* Indicates work sector changed */ unsigned char wrkbuff[SECTOR_SIZE]; /* Work sector buffer */ /* Active drive information (other than contained in BPB) */ char active_drive = -1; /* Open disk drive number */ unsigned dirsec = 5, /* First sector of directory */ datasec = 12; /* First sector of data area */ /* Disk information (from BIOS Parameter Block) */ unsigned int bytsec = 512; /* Bytes / sector */ unsigned char seccls = 2; /* Sectors / cluster */ unsigned int ressec = 1; /* # reserved sectors */ unsigned char numfat = 2; /* Number of FAT's */ unsigned int dirent = 112; /* Number of directory entries */ unsigned int sectors = 720; /* Sectors on disk */ unsigned char mediaid = 0xFD; /* Media ID byte */ unsigned int secfat = 2; /* Sectors / fat */ unsigned int sectrk = 9; /* Sectors / track */ unsigned int numhead = 2; /* Number of heads */ /* * Function Prototypes */ extern struct Dentry *lookup(), *create_file(); /* * File accessing functions: * * open_drive(drive) - Initialize a drive for file access * drive - Drive id (0=A, 1=B ...) * * open_file(name, attrs) - Open file for read or write * name - Name of file to open * attrs - Open attributes: READ or WRITE * returns : Pointer to file structure, or 0 if failure * * close_file(fp) - Close an open file * fp - Pointer to open file structure * * read_byte(fp) - Read a byte from an open file * fp - Pointer to open file structure * returns : Value read (0-255), -1 if EOF, -2 if not open for read * * write_byte(byte, fp) - Write a byte to a file * byte - Value to write to file (0-255) * returns : Value written (0-255) or -2 if error * * delete_file(name) - Erases the named file * name - Name of file to erase * returns : 0 if Success, -1 if failure (file not found) */ /* * Open a disk drive and set up control information * * THIS FUNCTION MUST BE CALLED BEFORE ACCESSING ANY FILES, * AND ANYTIME YOU SWITCH TO ACCESS A DIFFERENT DRIVE. */ open_drive(drive) char drive; { read_work(active_drive = drive, 0); memcpy(&bytsec, wrkbuff+11, BPB_SIZE); dirsec = (numfat * secfat) + ressec; datasec = ((((dirent * sizeof(struct Dentry)) + bytsec) - 1) / bytsec) + dirsec; } /* * Open a file & return a pointer to an allocated file structure */ struct Fblock *open_file(name, attrs) char *name; int attrs; { struct Dentry *dirptr; struct Fblock *fp; if(dirptr = lookup(name)) { /* File already exists */ if(attrs == WRITE) /* Zero size on write */ dirptr->Dsizel = dirptr->Dsizeh = 0; } else { if(attrs != WRITE) /* Not writing file */ return 0; if(!(dirptr = create_file(name, 0))) /* Unable to create */ return 0; } /* Allocate buffer for file control block */ if(!(fp = malloc(bytsec+sizeof(struct Fblock)))) return 0; /* Fill in file control block from directory information */ fp->Fdirsec = wrksec; fp->Fdirptr = dirptr; fp->Fattr = attrs; fp->Fnextcls = fp->Ffirstcls = dirptr->Dcluster; fp->Fsizel = dirptr->Dsizel; fp->Fsizeh = dirptr->Dsizeh; fp->Fsector = fp->Foffset = fp->Flastcls = 0; return fp; } /* * Close an open file */ close_file(fp) struct Fblock *fp; { struct Dentry *dirptr; unsigned sizeh, sizel; /* Special actions to be taken when writing */ if(fp->Fattr == WRITE) { sizel = fp->Fsizel; sizeh = fp->Fsizeh; /* If there is a partial last sector, fill it with DOS EOF */ /* characters, which also causes it to be written out */ while(fp->Foffset) write_byte(0x1A, fp); /* Release remaining clusters in file. If any sectors in the */ /* current cluster are used, begin with the next one. */ release(fp->Fsector ? get_fat(fp->Fnextcls) : fp->Fnextcls); /* Update size entry in file directory */ read_work(active_drive, fp->Fdirsec); dirptr = fp->Fdirptr; dirptr->Dcluster= fp->Ffirstcls; dirptr->Dsizel = sizel; dirptr->Dsizeh = sizeh; /* Set directory date of modification here - if you wish */ wrkchg = -1; } free(fp); update_work(); /* Insure disk is in sync */ } /* * Delete a file from the disk */ delete_file(name) char *name; { struct Dentry *dirptr; if(dirptr = lookup(name)) { *dirptr->Dname = EMPTY; wrkchg = -1; release(dirptr->Dcluster); update_work(); return 0; } return -1; } /* * Read next byte from open file */ int read_byte(fp) struct Fblock *fp; { unsigned cluster, sector; /* If all data read, return EOF */ if(!(fp->Fsizel || fp->Fsizeh)) { return EOF; } /* Decrement file size (32 bit) */ if((--fp->Fsizel) == -1) --fp->Fsizeh; /* If no data buffered ... read next sector */ if((fp->Foffset >= bytsec) || !fp->Foffset) { /* If not open for READ, return ERROR */ if(fp->Fattr != READ) return ERROR; /* If past last cluster, return EOF */ if(((cluster = fp->Fnextcls) >= 0xFF8) || !cluster) return EOF; /* Read the sector into memory */ read_sector(active_drive, (sector = fp->Fsector) + ((cluster-2)*seccls) + datasec, fp->Fbuffer); /* Advance sector in cluster, if past end, get next cluster number */ if(++sector >= seccls) { fp->Fnextcls = get_fat(cluster); sector = 0; } /* Update file control block information */ fp->Flastcls = cluster; fp->Fsector = sector; fp->Foffset = 0; } /* Read character, and advance circular buffer pointer */ return fp->Fbuffer[fp->Foffset++]; } /* * Write a byte to an open file */ int write_byte(c, fp) unsigned c; struct Fblock *fp; { unsigned cluster, sector; /* Test for writable file */ if(fp->Fattr != WRITE) return ERROR; /* Advance file size */ if(!++fp->Fsizel) ++fp->Fsizeh; /* Write character to buffer */ fp->Fbuffer[fp->Foffset++] = c; /* If buffer is full, write it */ if(fp->Foffset >= bytsec) { /* If no sector allocated, allocate one */ if(!fp->Fnextcls) fp->Ffirstcls = fp->Fnextcls = allocate(0); else if(fp->Fnextcls >= 0xFF8) fp->Fnextcls = allocate(fp->Flastcls); if(!(cluster = fp->Fnextcls)) return ERROR; /* Write the data to the drive */ write_sector(active_drive, (sector = fp->Fsector) + ((cluster-2)*seccls) + datasec, fp->Fbuffer); /* Advance to next sector in cluster */ if(++sector >= seccls) { fp->Fnextcls = get_fat(cluster); sector = 0; } /* Update file control block information */ fp->Flastcls = cluster; fp->Fsector = sector; fp->Foffset = 0; } return c; } /* * FAT manipulation functions: * * allocate(cluster) - Allocate a free cluster * cluster - Cluster to link this one to (o if none). * returns : Cluster number allocated, 0 if failure * * release(cluster) - Release a cluster chain (if allocated) * cluster - Beginnig cluster to release * * get_fat(cluster) - Get FAT entry for cluster * cluster - Cluster number to obtain entry for * returns : Cluster number linked (0 if free, 0xFF8+ for EOF) * * set_fat(cluster, link) - Set the FAT entry for a cluster * cluster - Cluster number to set link for * link - Cluster number to link to (0=free, 0xFFF = EOF) */ /* * Allocate a free cluster on disk, and cross connect FAT if necessary * mark cluster as used & end of file. */ int allocate(cluster) int cluster; { int begin, end, i; /* Calculate start and end clusters for search */ /* If we have a "FAT" sector loaded, begin searching from there, */ /* To attempt to keep allocated sectors in same "FAT" sector */ begin = ((i = wrksec-1) && (i <= secfat)) ? ((i * (SECTOR_SIZE*2)) / 3) + 1 : 2; end = (sectors - datasec) / seccls; do { #ifdef DEBUG printf("Allocate(%u) : Wrk=%u Begin=%u End=%u\n", cluster, wrksec, begin, end); #endif for(i = begin; i < end; ++i) if(!get_fat(i)) { #ifdef DEBUG printf("Allocated %u\n", i); #endif set_fat(i, 0xFFF); if(cluster) set_fat(cluster, i); return i; } /* Didn't find, reset to start cluster, and try again */ end = begin; begin = 2; } while(end != 2); #ifdef DEBUG printf("Failed!\n"); #endif return 0; } /* * Release a chain of allocated clusters */ release(cluster) int cluster; { unsigned i; while(cluster && (cluster < 0xFF8)) { i = get_fat(cluster); set_fat(cluster, 0); cluster = i; } } /* * Get a FAT entry for a given cluster number */ int get_fat(cluster) unsigned cluster; { unsigned sec, fat; #ifdef DEBUG printf("Get FAT for %u - ", cluster); #endif sec = (cluster *= 3)/2; read_work(active_drive, (sec / bytsec) + 1); fat = wrkbuff[sec % bytsec]; read_work(active_drive, (++sec / bytsec) + 1); fat |= wrkbuff[sec % bytsec] << 8; if(cluster & 1) fat >>= 4; #ifdef DEBUG printf("%u\n", fat & 0xFFF); #endif return fat & 0xfff; } /* * Set a FAT entry for a given cluster number */ set_fat(cluster, value) unsigned cluster, value; { unsigned sec, x; #ifdef DEBUG printf("Set FAT for %u - %u\n", cluster, value); #endif sec = (cluster *= 3)/2; /* Set LOW byte of FAT */ read_work(active_drive, (sec / bytsec) + 1); x = sec % bytsec; if(cluster & 1) wrkbuff[x] = (wrkbuff[x] & 0x0F) | (value << 4); else wrkbuff[x] = value; wrkchg = -1; /* Set high byte of FAT */ read_work(active_drive, (++sec / bytsec) + 1); x = sec % bytsec; if(cluster & 1) wrkbuff[x] = value >> 4; else wrkbuff[x] = (wrkbuff[x] & 0xF0) | ((value >> 8) & 0x0F); wrkchg = -1; } /* * Directory manipulation functions: * * lookup(file) - Locate a files directory entry * file - Name of file to locate * returns : Pointer to directory entry, 0 if failure. * * create_file(file,attrs) - Create a mew file * file - Name of file to create * attrs - Attributes for file (0 = normal) * returns : Pointer to directory entry, 0 if failure. * * parse_filename(buffer,name) - Get filename in directory format * buffer - Buffer for directory format filename (13 bytes) * name - ASCII filename to convert * * memcmp(ptr1, ptr2, size) - Block memory compare * ptr1 - Pointer to first block * ptr2 - Pointer to second block * size - Number of bytes to compare * returns : -1 of match, 0 if different. */ /* * Lookup a directory entry & return pointer to it */ struct Dentry *lookup(file) char *file; { unsigned i, j, k; char fbuffer[12]; parse_filename(fbuffer, file); #ifdef DEBUG printf("Lookup: '%s' ", fbuffer); #endif j = bytsec; k = dirsec; for(i=0; i < dirent; ++i) { if(j >= bytsec) { read_work(active_drive, k++); j = 0; } if(memcmp(wrkbuff+j, fbuffer, 11)) { #ifdef DEBUG printf("Found - Cluster: %u\n", *(int*)(wrkbuff+j+0x1A)); #endif return wrkbuff+j; } j += sizeof(struct Dentry); } #ifdef DEBUG printf("Not found\n"); #endif return 0; } /* * Create a file with the specified name * NOTE: Does NOT check for duplicates. It is assumed that "lookup()" * has been called first, to verify that the file does not already * exist on the disk. */ struct Dentry *create_file(file, attrs) char *file; int attrs; { int i, j, k; char fbuffer[12]; struct Dentry *dirptr; parse_filename(fbuffer, file); #ifdef DEBUG printf("Create '%s' ", fbuffer); #endif j = bytsec; k = dirsec; for(i=0; i < dirent; ++i) { if(j >= bytsec) { read_work(active_drive, k++); j = 0; } if((*(dirptr = wrkbuff+j)->Dname == EMPTY) || !*dirptr->Dname) { #ifdef DEBUG printf("Dir entry #%u\n", i); #endif memset(dirptr, 0, sizeof(struct Dentry)); strcpy(dirptr, fbuffer); dirptr->Dattr = attrs; /* Set directory date of creation here - if you wish */ wrkchg = -1; return wrkbuff+j; } j += sizeof(struct Dentry); } #ifdef DEBUG printf("No directory!\n"); #endif return 0; } /* * Parse a filename into directory format (12 characters, space filled) */ parse_filename(buffer, name) char buffer[], *name; { int i; i = 0; while(i < 8) buffer[i++] = (*name && (*name != '.')) ? toupper(*name++) : ' '; if(*name == '.') ++name; while(i < 11) buffer[i++] = *name ? toupper(*name++) : ' '; buffer[i] = 0; } /* * Memory to memory compare */ int memcmp(ptr1, ptr2, size) char *ptr1, *ptr2; unsigned size; { while(size--) if(*ptr1++ != *ptr2++) return 0; return -1; } /* * Internal "work" sector manipulation functions: * * read_work(sector) - Read sector into work buffer (if necessary) * sector - Sector number to read. * * update_work() - Write work sector back to disk if changed */ /* * Read a work sector into memory if it is not already in memory. */ read_work(drive, sector) char drive; int sector; { if((wrkdrv != drive) || (wrksec != sector)) { update_work(); read_sector(wrkdrv = drive, wrksec = sector, wrkbuff); } } /* * Write the work sector if it has been marked as modified */ update_work() { if(wrkchg) { write_sector(wrkdrv, wrksec, wrkbuff); wrkchg = 0; } } /* * Low level disk I/O functions: * * disk_error(code) - Called if unrecoverable disk error * code - Disk failure code (system depandant) * * read_sector(d, s, b) - Read a sector from the disk * d - Drive to read (0=A, 1=B ...) * s - Sector number to read (1-n) * b - Pointer to buffer to receive data * * write_sector(d, s, b) - Write a sector to the disk * d - Drive to write (0=A, 1=B ...) * s - Sector number to write (1-n) * b - Pointer to buffer containing the data * * These functions make use of the IBM PC BIOS, and are compatible * with Microsoft MASM assembler. Use these if you are compiling the * demo program with the MICRO-C DOS compiler. */ /* * Report a disk error */ disk_error(code) int code; { printf("Disk error - Code: %04x\n", code); exit(-1); } /* * Read a sector from the disk drive */ read_sector(drive, sector, buffer) asm { PUSH DS ; Save DS POP ES ; Set ES MOV BX,4[BP] ; Get buffer MOV AX,6[BP] ; Get sector XOR DX,DX ; Zero high DIV WORD PTR _sectrk; Calculate track MOV CL,DL ; CL = sector INC CL ; 1- XOR DX,DX ; Zero high DIV WORD PTR _numhead; Compute head MOV CH,AL ; CH = cylinder MOV DH,DL ; DH = head MOV DL,8[BP] ; DL = drive MOV DI,3 ; Try three times read1: MOV AX,0201h ; Read 1 sector INT 13h ; Call BIOS JNC read2 ; Success DEC DI ; Reduce count JNZ read1 ; Keep trying PUSH AX ; Pass parameter CALL _disk_error ; Report an error POP AX ; Clean stack read2: XOR AX,AX ; Zero return } /* * Write a sector to the disk drive */ write_sector(drive, sector, buffer) asm { PUSH DS ; Save DS POP ES ; Set ES MOV BX,4[BP] ; Get buffer MOV AX,6[BP] ; Get sector XOR DX,DX ; Zero high DIV WORD PTR _sectrk; Calculate track MOV CL,DL ; CL = sector INC CL ; 1- XOR DX,DX ; Zero high DIV WORD PTR _numhead; Compute head MOV CH,AL ; CH = cylinder MOV DH,DL ; DH = head MOV DL,8[BP] ; DL = drive MOV DI,3 ; Try three times write1: MOV AX,0301h ; Write 1 sector INT 13h ; Call BIOS JNC write2 ; Success DEC DI ; Reduce count JNZ write1 ; Keep trying PUSH AX ; Pass parameter CALL _disk_error ; Report an error POP AX ; Clean stack write2: XOR AX,AX ; Zero return } /* * Sample Main program (Not part of MDCFS) to demonstrate reading, * writing and deleteing files. This also uses the standard file * I/O of the MICRO-C DOS compiler, and will not run stand-alone. * * Demo command syntax: * MCDFS W <file> - Copy file from current DOS dir to floppy * MCDFS R <file> - Copy file from floppy to current DOS dir * MCDFS D <file> - Delete file from floppy. * * All accesss to the floppy are performed via MCDFS. */ main(argc, argv) int argc; char *argv[]; { int c; struct Fblock *fp; FILE *xfp; open_drive(0); /* Change to (1) for drive B: */ switch((argc > 2) ? toupper(*argv[1]) : 0) { case 'R' : /* Read file from floppy drive */ if(!(fp = open_file(argv[2], READ))) abort("Couldn't read MDCFS file"); xfp = fopen(argv[2], "wbvq"); while((c = read_byte(fp)) >= 0) putc(c, xfp); close_file(fp); fclose(xfp); c = 0; /* Disk has not been modified */ break; case 'W' : /* Write file to floppy drive */ xfp = fopen(argv[2], "rvbq"); if(!(fp = open_file(argv[2], WRITE))) abort("Couldn't write MDCFS file"); while((c = getc(xfp)) >= 0) write_byte(c, fp); fclose(xfp); close_file(fp); c = -1; /* Note that disk may have been written */ break; case 'D' : /* Delete file from floppy drive */ delete_file(argv[2]); c = -1; /* Note that disk may have been written */ break; default: abort("Use: MDCFS (Read/Write/Delete) <filename>"); } if(c) { printf("\nNote: Since MDCFS bypasses DOS completely, DOS will be unaware\n"); printf("of any changes to the disk... Due to DOS buffering, files written\n"); printf("may not appear in DOS 'dir' command, or may have their sizes\n"); printf("reported incorrectly... Press CONTROL-C to force DOS to flush\n"); printf("its buffers before accessing the floppy disk"); } }
Interested:
file: /Techref/com/dunfield/ftp/embedpc/MDCFS_C.htm, 95KB, , updated: 2002/12/17 20:50, local time: 2024/12/25 08:28,
18.191.171.43:LOG IN
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/techref/com/dunfield/ftp/embedpc/MDCFS_C.htm"> Colorized Source Code</A> |
Did you find what you needed? |