home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Garbo
/
Garbo.cdr
/
pc
/
progrmng
/
dosfiles.lzh
/
dosfiles
/
msdos.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-11-29
|
13KB
|
486 lines
/*
** 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);
}