home *** CD-ROM | disk | FTP | other *** search
- /*
- ** M S - D O S d i s k e t t e f i l e s y s t e m
- */
-
- /*
- ** Current limitations:
- ** Only one file may be "open" at a time - either for read or write
- ** There is no provision if the diskette becomes full when writing
- */
-
- #include "io.h"
- #include "msdos.h"
-
- /* Macros for converting two bytes to word and vice versa */
- #define GET_WORD(i,a) ((unsigned int)(a[i] | (a[(i)+1]<<8)))
- #define SET_WORD(i,a,v) (a[i]=(v), a[(i)+1]=(v)>>8)
-
- /* Same for converting four bytes to long and vice versa */
- #define C(a) ((char *)&(a))
- #define L(i,a) ((unsigned long)(C(a)[i]))
-
- #define GET_LONG(l) (L(0,l) | (L(1,l)<<8) |\
- (L(2,l)<<16) | (L(3,l)<<24))
- #define SET_LONG(l,v) ((C(l)[0]=(v)), (C(l)[1]=(v)>>8),\
- (C(l)[2]=(v)>>16), (C(l)[3]=(v)>>24))
-
- /* Error marking routines */
- #define READ_ERROR(sector,status) general_error("read",sector,status)
- #define WRITE_ERROR(sector,status) general_error("write",sector,status)
-
- /* Some markers */
- unsigned int sector_size=SECTORSIZE,sectors_per_fat,reserved_sectors,file_area;
- unsigned int fats_per_disk,file_slots,sectors_per_cluster,hidden_sectors;
-
- /* Single directory sector entry */
- unsigned int dirsector_number=-1;
- unsigned char dir_sector[SECTORSIZE];
-
- /* FAT buffer (largest floppy FAT is 9 sectors, on a 1.44M) */
- unsigned char fat[9*SECTORSIZE];
-
- /* Cluster buffer (largest floppy cluster is 2 sectors) */
- unsigned char cluster[2*SECTORSIZE];
-
- /* Some function prototypes */
- void read_cluster(unsigned int number);
- void write_cluster(unsigned int number);
-
- void write_fat();
- unsigned int get_fat_entry(unsigned int cluster);
- void set_fat_entry(unsigned int cluster,unsigned int value);
-
- void general_error(char *dowhat, unsigned int sector,unsigned int status);
-
- /*
- ** Read sector zero, set values appropriately; read FAT into the buffer
- */
- void disk_open() {
- register int i,j;
-
- if(i=read_sector(0,cluster)) READ_ERROR(0,i);
-
- sector_size = GET_WORD(0x0B,cluster);
- sectors_per_cluster = cluster[0x0D];
- reserved_sectors = GET_WORD(0x0E,cluster);
- fats_per_disk = cluster[0x10];
- file_slots = GET_WORD(0x11,cluster);
- sectors_per_fat = GET_WORD(0x16,cluster);
- sectors_per_track = GET_WORD(0x18,cluster);
- number_of_heads = GET_WORD(0x1A,cluster);
- hidden_sectors = GET_WORD(0x1C,cluster);
- file_area = reserved_sectors + hidden_sectors +
- sectors_per_fat*fats_per_disk +
- (file_slots*sizeof(directory))/sector_size;
-
- Db( printf(
- "\ndrive %d\n"
- "media OEM signature \"%.8s\"\n"
- "bytes/sector=%u\n"
- "sectors/cluster=%u\n"
- "reserved=%u\n"
- "FATs=%u\n"
- "root directory file slots=%u\n"
- "total sectors=%u\n"
- "media descriptor=%xh\n"
- "sectors/FAT=%u\n"
- "sectors/track=%u\n"
- "heads=%u\n"
- "hidden sectors=%u\n"
- "boot signature=%02X%02X\n"
- "file area begins at sector %d\n\n",
- drive_number,
- cluster+3,
- sector_size,
- sectors_per_cluster,
- reserved_sectors,
- fats_per_disk,
- file_slots,
- GET_WORD(0x13,cluster),
- cluster[0x15],
- sectors_per_fat,
- sectors_per_track,
- number_of_heads,
- hidden_sectors,
- cluster[sector_size-2],cluster[sector_size-1],
- file_area);
- )
-
- /* Can't read FAT as multiple sectors; FAT can span tracks */
- for(j=0; j<sectors_per_fat; j++)
- if(i=read_sector(reserved_sectors+j,fat+j*sector_size))
- READ_ERROR(reserved_sectors,i);
- }
-
-
- /*
- ** Search root directory for given filename (or free entry if NULL given),
- ** return pointer to it's entry or NULL if no find
- */
- directory *find_dir(filename)
- char *filename; {
- register int i,j;
- directory *dp = (directory *)dir_sector+sector_size;
-
- dirsector_number = reserved_sectors + hidden_sectors +
- sectors_per_fat * fats_per_disk - 1;
-
- for(j=0; j<file_slots; j++, dp++) {
- if(dp >= (directory *)dir_sector+sector_size) {
- if(i=read_sector(++dirsector_number,dir_sector))
- READ_ERROR(dirsector_number,i);
- dp = (directory *)dir_sector;
- }
- if(filename) {
- /* Name must match and must not be volabel, dir etc. */
- if(!(strncmp(dp->filename,filename,8+3) ||
- (dp->attribute & ATTR_SPECIAL)) )
- return(dp);
- } else {
- /* Must be either unused or deleted entry */
- if(!(i=dp->filename[0]) || i==0xE5)
- return(dp);
- }
- }
-
- return(NULL);
- }
-
-
- /*
- ** "Open" a file for read, return true if file cannot be read (zero length)
- */
- static unsigned int current_cluster;
- static unsigned long int bytes_in_file;
- static unsigned char *next_char;
-
- int file_open(dir_item)
- register directory *dir_item; {
- current_cluster = dir_item->cluster;
- bytes_in_file = GET_LONG(dir_item->filesize);
- next_char = cluster+sector_size*sectors_per_cluster;
-
- Db( printf("Opened file, size %ld bytes\n",bytes_in_file); )
-
- return(!current_cluster);
- }
-
-
- /*
- ** Get next character from file previously opened with file_open
- */
- int file_getc() {
- if(!bytes_in_file) return(EOF);
- if(next_char >= cluster+sector_size*sectors_per_cluster) {
- next_char = cluster;
- read_cluster(current_cluster);
- current_cluster = get_fat_entry(current_cluster);
- }
- bytes_in_file--;
- return(*next_char++);
- }
-
-
- /*
- ** "Open" filename for writing, return pointer to directory item if
- ** successful; return NULL if old file can't be overwritten or there
- ** is no room in the root directory for a new file
- */
- directory *file_create(filename)
- char *filename; {
- register int i;
- directory *dp;
-
- /* If file already exists, truncate it to zero length */
- if(dp=find_dir(filename)) {
- /* Can't truncate if file is readonly */
- if(dp->attribute & ATTR_READONLY) return(NULL);
-
- /* Remove FAT chain */
- if(current_cluster = dp->cluster)
- do {
- current_cluster =
- get_fat_entry(i=current_cluster);
- set_fat_entry(i,0);
- Db( printf("Freeing cluster %03x\n",i); )
- } while(current_cluster < 0xFF8);
- /* Then rewrite FAT */
- write_fat();
- } else
- /* Allocate a unused or deleted directory entry, copy name */
- if(dp=find_dir(NULL))
- for(i=0; i<8+3; i++) dp->filename[i]=filename[i];
- else
- return(NULL);
-
- /* Then clear directory entry, filesize and cluster ptr */
- dp->filesize = 0L;
- dp->cluster = 0;
- dp->attribute = ATTR_ARCHIVE; /* File archive bit set (modified) */
- dp->date = 0x0000; /* You may insert anything appropriate here */
- dp->time = 0x0000; /* These values mean "no date" for MS-DOS */
- if(i=write_sector(dirsector_number,dir_sector))
- WRITE_ERROR(dirsector_number,i);
-
- /* Clear indicators for file_putc() */
- current_cluster = 0;
- bytes_in_file = 0L;
- next_char = cluster+sector_size*sectors_per_cluster;
-
- return(dp);
- }
-
-
- /*
- ** Put next character to the file previously opened with file_create
- */
- int file_putc(dp,c)
- directory *dp;
- int c; {
- register int i;
-
- /* Note that there is no provision for running out of FAT */
- if(next_char >= cluster+sector_size*sectors_per_cluster) {
- if(current_cluster)
- write_cluster(current_cluster);
- for(i=current_cluster+1; get_fat_entry(i); i++);
-
- if(current_cluster)
- set_fat_entry(current_cluster,i);
- else
- dp->cluster = i;
-
- Db( printf("Allocated cluster %03x\n",i); )
- current_cluster = i;
- next_char = cluster;
- }
- *next_char++ = c;
- bytes_in_file++;
- return(0);
- }
-
-
- /*
- ** Close the file written to by file_putc
- ** Rewrite directory item and FAT, write disk buffer
- */
- void file_close(dp)
- directory *dp; {
- register int i;
-
- Db( printf("Closing file, size %ld bytes\n",bytes_in_file); )
-
- /* Do nothing if file hasn't actually been written to */
- if(!current_cluster) return;
-
- /* Update file size */
- SET_LONG(dp->filesize,bytes_in_file);
-
- Db( printf("Closing file, size %ld bytes\n",dp->filesize); )
-
- if(i=write_sector(dirsector_number,dir_sector))
- WRITE_ERROR(dirsector_number,i);
-
- /* Write cluster buffer to disk */
- write_cluster(current_cluster);
- /* Mark as end-of-file to FAT */
- set_fat_entry(current_cluster,0xFFF);
- /* Rewrite FAT to disk */
- write_fat();
- }
-
-
- /*
- ** Erase disk's logical structure, create standard MS-DOS disk;
- ** this basically does a logical format to the target disk -- nothing
- ** is done physically. THIS WILL TOTALLY ERASE THE WHOLE DISKETTE !
- ** Idea is that one does not have to do disk_open once this is done
- **
- ** Note: if Atari ST 720k diskette support is wanted, the media
- ** descriptor byte for type 2 should be set to 0xF7.
- */
-
- static struct {
- unsigned char tracks,sectors,heads,media,fat,files,cluster;
- } disk_data[4] = {
- /* tracks sectors heads media FAT files cluster */
- { 40, 9, 2, 0xFD, 2, 7, 2 }, /* 360k */
- { 80, 15, 2, 0xF9, 7, 14, 1 }, /* 1.2M */
- { 80, 9, 2, 0xF9, 3, 7, 2 }, /* 720k */
- { 80, 18, 2, 0xF0, 9, 14, 1 }, /* 1.44M */
- };
- static unsigned char volume_label[8+3]="My Diskette";
-
- void disk_clear(disktype)
- int disktype; {
- register int i;
- directory *dp;
-
- /* First, make logical sector 0 */
- for(i=0; i<SECTORSIZE; i++) cluster[i]='\0';
-
- sectors_per_track = disk_data[disktype].sectors;
- number_of_heads = disk_data[disktype].heads;
-
- sector_size = SECTORSIZE;
- sectors_per_fat = disk_data[disktype].fat;
- reserved_sectors = 1;
- fats_per_disk = 2;
- file_slots = sector_size*disk_data[disktype].files/sizeof(directory);
- sectors_per_cluster = disk_data[disktype].cluster;
- hidden_sectors = 0;
-
- file_area = reserved_sectors + hidden_sectors +
- sectors_per_fat*fats_per_disk +
- disk_data[disktype].files;
-
- /*
- ** Seems Messy-Dos can't read a 3.5" diskette unless these
- ** are here (it has no problems reading 5.25" diskettes !)
- ** they actually are the instructions JMP 003E and NOP for
- ** the iAPX86 family microprocessors - talk about a kludge!
- */
- cluster[0]=0xEB; cluster[1]=0x3C; cluster[2]=0x90;
-
- /* Diskette creator (OEM) signature - that's me, folks! */
- for(i=0; i<8; i++) cluster[i+3]="Mycroft*"[i];
-
- SET_WORD(0x0B,cluster,sector_size);
- cluster[0x0D] = sectors_per_cluster;
- SET_WORD(0x0E,cluster,reserved_sectors);
- cluster[0x10] = fats_per_disk;
- SET_WORD(0x11,cluster,file_slots);
- SET_WORD(0x13,cluster,
- disk_data[disktype].tracks*sectors_per_track*number_of_heads);
- cluster[0x15] = disk_data[disktype].media;
- SET_WORD(0x16,cluster,sectors_per_fat);
- SET_WORD(0x18,cluster,sectors_per_track);
- SET_WORD(0x1A,cluster,number_of_heads);
- SET_WORD(0x1C,cluster,hidden_sectors);
-
- /*
- ** This volume label is actually extraneous, since we haven't
- ** inserted the MS-DOS 4.0 signature byte 0x29 at offset 0x26
- ** (as we'd have to figure a volume serial number for the disk)
- */
- for(i=0; i<8+3; i++) cluster[i+0x2B]=volume_label[i];
-
- if(i=write_sector(0,cluster))
- WRITE_ERROR(0,i);
-
- /* Then create empty FAT */
- fat[0] = disk_data[disktype].media;
- fat[1] = 0xFF;
- fat[2] = 0xFF;
- for(i=3; i<sector_size*sectors_per_fat; i++) fat[i]=0;
- write_fat();
-
- /* Lastly, clear directory area (except for volume label) */
- for(i<0; i<SECTORSIZE; i++) dir_sector[i]='\0';
- dp = (directory *)dir_sector;
- for(i=0; i<8+3; i++) dp->filename[i]=volume_label[i];
- dp->attribute = ATTR_VOLABEL;
-
- dirsector_number = reserved_sectors + hidden_sectors +
- sectors_per_fat * fats_per_disk;
-
- if(i=write_sector(dirsector_number,dir_sector))
- WRITE_ERROR(dirsector_number,i);
-
- for(i=0; i<11; i++) dp->filename[i]='\0';
- dp->attribute = 0;
-
- while(++dirsector_number < disk_data[disktype].files)
- if(i=write_sector(dirsector_number,dir_sector))
- WRITE_ERROR(dirsector_number,i);
-
- }
-
-
- /*
- ** Get the requested cluster to the cluster buffer
- */
- static void read_cluster(number)
- register unsigned int number; {
- register int i,j;
- unsigned char *cp=cluster;
-
- Db( printf("Read cluster %03x\n",number); )
-
- number=CLUSTER_TO_SECTOR(number);
- for(j=0; j<sectors_per_cluster; j++, number++, cp+=sector_size)
- if(i=read_sector(number,cp))
- READ_ERROR(number,i);
- }
-
-
- /*
- ** Put the requested cluster from the cluster buffer
- */
- static void write_cluster(number)
- register unsigned int number; {
- register int i,j;
- unsigned char *cp=cluster;
-
- Db( printf("Wrote cluster %03x\n",number); )
-
- number=CLUSTER_TO_SECTOR(number);
- for(j=0; j<sectors_per_cluster; j++, number++, cp+=sector_size)
- if(i=write_sector(number,cp))
- WRITE_ERROR(number,i);
- }
-
-
- /*
- ** Write modified FAT back to disk (both copies)
- */
- static void write_fat() {
- register int i,j;
-
- for(j=0; j<sectors_per_fat; j++) {
- if(i=write_sector(reserved_sectors+j,fat+j*sector_size))
- WRITE_ERROR(reserved_sectors+j,i);
- if(i=write_sector(reserved_sectors+sectors_per_fat+j,
- fat+j*sector_size))
- WRITE_ERROR(reserved_sectors+sectors_per_fat+j,i);
- }
- }
-
-
- /*
- ** Get FAT entry for specified cluster
- */
- static unsigned int get_fat_entry(cluster)
- unsigned int cluster; {
- register unsigned int i=GET_WORD(3*cluster/2,fat);
-
- return (cluster&1)? (i>>4): (i&0x0FFF);
- }
-
-
- /*
- ** Set FAT entry for specified cluster
- */
- static void set_fat_entry(cluster,value)
- unsigned int cluster,value; {
- register unsigned int i=GET_WORD(3*cluster/2,fat);
-
- i = (cluster&1)?
- ((value<<4) | (i & 0x000F)):
- ((value & 0x0FFF) | (i & 0xF000));
-
- SET_WORD(3*cluster/2,fat,i);
- }
-
-
- /*
- ** Report error and terminate
- */
- static void general_error(dowhat,sector,status)
- char *dowhat;
- unsigned int sector,status; {
- printf("Can't %s logical sector %d, status=%02x\n",
- dowhat,sector,status);
- exit(1);
- }
-