📄 loaderc.c
字号:
/*****************************************************************************
xxx - support RDSK and ELF files
xxx - load RDSK files into extended memory if too little conventional memory
xxx - let user change "Physical load address" and "Store boot data in BSS"
xxx - fat_mount() should get hard drive geometry -- somehow
INT 13h AH=08h should be OK for hard disk (not for floppy, though)
(later)
xxx - let user browse different disks/partitions
xxx - use XMS to access extended memory if HIMEM.SYS loaded
xxx - use VCPI to switch to pmode if EMM386 loaded
xxx - support DOS .EXE files, DOS .COM files, and chainloaded bootsectors
xxx - support binary kernel (let user change load adr, entry point, etc.)
xxx - support ext2 filesystem
*****************************************************************************/
/* portability! These assume sizeof(short)==2 and sizeof(long)==4
They also assume a little-endian CPU like x86.
Big-endian CPUs will need byte-swapping functions here. */
#define read_le16(X) *(unsigned short *)(X)
#define read_le32(X) *(unsigned long *)(X)
/* some functions return -1 when I don't know what else to return */
#define ERR_EOF -2 /* end of file */
#define ERR_IO -3 /* disk read error */
#define ERR_MEM -4 /* out of memory */
#define ERR_SW -5 /* software error ("can't happen") */
#define ERR_FILE -6 /* invalid file format */
#define ERR_FILESYSTEM -7
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
/* "An inode is an [unique] integer associated with a file on the filesystem."
For FAT, we use the first cluster of the file as the inode */
typedef unsigned short inode_t;
typedef unsigned long sector_t;
typedef unsigned long pos_t;
typedef struct
{
unsigned char int13_dev;
unsigned short sects, heads, bytes_per_sector;
} dev_t;
typedef struct
{
pos_t pos;
struct _mount *mount;
inode_t inode;
unsigned long file_size;
unsigned is_dir : 1;
char name[16]; /* xxx - 16 is arbitrary...maybe use malloc()? */
} file_t;
typedef struct
{
int (*open_root)(file_t *root_dir, struct _mount *mount);
int (*readdir)(file_t *dir, file_t *file);
int (*read)(file_t *file, unsigned char *buf, unsigned len);
/* void *info; */
unsigned char info[64]; /* xxx - use malloc() */
} fsinfo_t;
typedef struct _mount
{
dev_t *dev;
fsinfo_t fsinfo;
sector_t partition_start;
inode_t curr_dir;
} mount_t;
/* non-standard (not ANSI nor UNIX) I/O functions, but they were
eaiser to write than open(), readdir(), etc. (and more efficient,
since they deal directly with inodes, rather than path names) */
static int my_seek(file_t *file, long offset, int whence);
static int my_read(file_t *file, void *buf, unsigned len);
static int my_close(file_t *file);
static void cprintf(const char *fmt, ...);
static mount_t _mount;
/*////////////////////////////////////////////////////////////////////////////
READ-ONLY DISK CODE AND CACHE
////////////////////////////////////////////////////////////////////////////*/
#include <bios.h> /* biosdisk() */
#define MAX_CACHE 32 /* 32 sectors == 16K */
#define BPS 512 /* bytes per sector */
typedef struct
{
sector_t sector;
unsigned char blk[BPS];
} cache_t;
/*****************************************************************************
*****************************************************************************/
static int read_sector(dev_t *dev, sector_t sector, unsigned char **blk)
{
static unsigned char init, evict;
static cache_t cache[MAX_CACHE];
/* */
unsigned short c, h, s, temp;
unsigned char tries;
if(!init)
{
init = 1;
for(temp = 0; temp < MAX_CACHE; temp++)
cache[temp].sector = -1uL;
}
/* see if this sector is cached */
for(temp = 0; temp < MAX_CACHE; temp++)
{
if(cache[temp].sector == sector)
{
(*blk) = cache[temp].blk;
return 0;
}
}
/* not cached, find a free buffer for it */
for(temp = 0; temp < MAX_CACHE; temp++)
{
if(cache[temp].sector == -1uL)
break;
}
/* no free buffer, kick out someone else */
if(temp >= MAX_CACHE)
{
temp = evict;
evict++;
if(evict >= MAX_CACHE)
evict = 0;
}
/* load it */
cache[temp].sector = sector;
(*blk) = cache[temp].blk;
/* we can load sector 0 even if we don't know the disk geometry
(which is good, because FAT uses sector 0 to store floppy geometry info) */
if(sector == 0)
{
s = 1;
h = 0;
c = 0;
}
else
{
s = sector % dev->sects + 1;
h = (sector / dev->sects) % dev->heads;
c = (sector / dev->sects) / dev->heads;
}
/* make 3 attempts */
for(tries = 3; tries != 0; tries--)
{
temp = biosdisk(/* _DISK_READ */2, dev->int13_dev,
h, c, s, 1, *blk);
/* biosdisk() does not return what the Turbo C online help says.
It returns the AH value from INT 13h, not AX
temp >>= 8; */
if(temp == 0 || temp == 0x11)
return 0;
/* reset FDC if error */
(void)biosdisk(/* _DISK_RESET */0, dev->int13_dev,
0, 0, 0, 0, 0);
}
cprintf("\n\rread_sector: INT 13h disk error 0x%02X, CHS=%u:%u:%u, "
"dev=0x%02X, *blk=%p\n\r", temp, c, h, s,
dev->int13_dev, *blk);
return ERR_IO;
}
/*///////////////////////////////////////////////////////////////////////////
FAT12/16 FILESYSTEM
////////////////////////////////////////////////////////////////////////////*/
/* NULL, memset, memcmp, memcpy, strchr, strlen, strcpy, strupr, strchr */
#include <string.h>
#define min(a,b) (((a) < (b)) ? (a) : (b))
/* bytes per FAT directory entry */
#define FAT_DIRENT_LEN 32
/* FAT entries 0 and 1 are reserved: */
#define MAGIC_FAT_CLUSTER 0
#define MAGIC_ROOT_CLUSTER 1
typedef enum
{
FAT12, FAT16, FAT32
} fat_type_t;
typedef struct
{
unsigned short max_cluster;
unsigned char sectors_per_cluster;
sector_t fat_start, root_start, data_start;
fat_type_t fat_type;
} fat_t;
/*****************************************************************************
e.g. "foo.i" -> "FOO I "
'dst' must be >=12 bytes
*****************************************************************************/
static int fat_convert_name_to_fat(char *dst, char *src)
{
unsigned len;
char *dot;
/* put src in FAT format */
memset(dst, ' ', 11);
dst[11] = '\0';
dot = strchr(src, '.');
/* xxx - why? trouble with Turbo C 2.0 installation? */
#define NULL 0
/* there is an extension */
if(dot != NULL)
{
/* long filenames not supported */
len = dot - src;
if(len > 8)
return ERR_FILESYSTEM;
/* copy filename */
memcpy(dst, src, len);
dst[len] = '\0';
/* long extension not supported */
len = strlen(dot) - 1;
if(len > 3)
return ERR_FILESYSTEM;
/* copy extension */
memcpy(dst + 8, dot + 1, len);
}
/* no extension */
else
{
/* long filenames not supported */
len = strlen(src);
if(len > 8)
return ERR_FILESYSTEM;
/* copy filename */
strcpy(dst, src);
}
/* make it upper case */
strupr(dst);
return 0;
}
/*****************************************************************************
e.g. "README TXT" -> "readme.txt"
'dst' must be >=12 bytes
*****************************************************************************/
static void fat_convert_name_from_fat(char *dst, char *src)
{
unsigned char i;
char *d = dst;
for(i = 0; i < 8; i++)
{
if(src[i] == ' ')
break;
*d = src[i];
d++;
}
if(src[8] != ' ')
{
*d = '.';
d++;
for(i = 8; i < 11; i++)
{
if(src[i] == ' ')
break;
*d = src[i];
d++;
}
}
*d = '\0';
/* make it lower case */
strlwr(dst);
}
/*****************************************************************************
*****************************************************************************/
static int fat_open_root(file_t *root_dir, mount_t *mount)
{
root_dir->pos = 0;
root_dir->mount = mount;
root_dir->inode = MAGIC_ROOT_CLUSTER;
root_dir->file_size = -1uL; /* xxx - get root dir size */
root_dir->is_dir = 1;
strcpy(root_dir->name, "/");
return 0;
}
/*****************************************************************************
FAT file attribute bits:
Bit(s) Description (Table 01401)
0 read-only
1 hidden
2 system
3 volume label or LFN
4 directory
5 archive
6 ?
7 if set, file is shareable under Novell NetWare
*****************************************************************************/
static int fat_readdir(file_t *dir, file_t *file)
{
char dirent[FAT_DIRENT_LEN];
int err;
while(1)
{
/* read one 32-byte FAT directory entry */
err = my_read(dir, dirent, FAT_DIRENT_LEN);
if(err != FAT_DIRENT_LEN)
{
/* short read means end of dir */
if(err >= 0)
return ERR_EOF;
return err;
}
/* "virgin" dir entry means end of dir, for both root dir and subdir */
if(dirent[0] == 0)
return ERR_EOF;
/* deleted file */
if(dirent[0] == 5 || dirent[0] == '\xE5')
continue;
/* volume label or LFN */
if((dirent[11] & 0x08) == 0)
break;
}
/* found it! */
file->pos = 0;
file->mount = dir->mount;
file->inode = read_le16(dirent + 26);
file->file_size = read_le32(dirent + 28);
if(dirent[11] & 0x10)
file->is_dir = 1;
else
file->is_dir = 0;
fat_convert_name_from_fat(file->name, dirent);
/* inode==0 is actually a pointer to the root directory */
if(file->inode == 0)
{
if(file->is_dir)
file->inode = MAGIC_ROOT_CLUSTER;
/* ...but only for directories */
else
return -1;
}
/* xxx - corrupt filesystem; not software error */
else if(file->inode < 2)
return ERR_SW;
/* I guess FAT doesn't store the correct size of subdirectories
in the directory entry */
if(file->file_size == 0)
{
if(file->is_dir)
file->file_size = -1uL;
/* else it's a zero-length file, which is cool */
}
return 0;
}
/*****************************************************************************
convert 'cluster' to 'sector', then advance to next cluster in FAT chain
and store next cluster at 'cluster'
*****************************************************************************/
static int fat_walk(file_t *file, sector_t *sector, unsigned short *cluster)
{
unsigned char buf[2];
unsigned short entry;
file_t the_fat;
sector_t temp;
fat_t *fat;
int err;
/* xxx - init the_fat properly */
fat = (fat_t *)(file->mount->fsinfo.info);
the_fat.mount = file->mount;
the_fat.inode = MAGIC_FAT_CLUSTER;
the_fat.file_size = -1uL;
/* must be cluster within data area of disk */
temp = (*cluster);
if(temp < 2)
return ERR_SW; /* can't (shouldn't) happen */
temp -= 2;
/* convert cluster to sector */
temp *= fat->sectors_per_cluster;
temp += fat->data_start;
(*sector) = temp;
/* now convert cluster to byte offset into FAT */
temp = (*cluster);
if(fat->fat_type == FAT12)
{
/* 12 bits per FAT entry, so byte offset into FAT is 3/2 * temp */
temp *= 3;
the_fat.pos = temp >> 1;
/* read 2-byte entry */
err = my_read(&the_fat, buf, 2);
if(err < 0)
return err;
entry = read_le16(buf);
/* top 12 bits or bottom 12 bits? */
if(temp & 1)
entry >>= 4;
else
entry &= 0x0FFF;
}
else if(fat->fat_type == FAT16)
{
/* 16 bits per FAT entry */
temp <<= 1;
the_fat.pos = temp;
/* read 2-byte entry */
err = my_read(&the_fat, buf, 2);
if(err < 0)
return err;
entry = read_le16(buf);
}
else
{
cprintf("fat_walk: FAT32 not yet supported\n\r");
return ERR_SW;
}
/* that's what we want! */
(*cluster) = entry;
return 0;
}
/*****************************************************************************
*****************************************************************************/
static int fat_read_sector(file_t *file, sector_t rel_sector,
unsigned char **blk)
{
unsigned short rel_cluster, abs_cluster;
sector_t abs_sector;
fsinfo_t *fsinfo;
mount_t *mount;
dev_t *dev;
fat_t *fat;
int err;
mount = file->mount;
dev = mount->dev;
fsinfo = &mount->fsinfo;
fat = (fat_t *)fsinfo->info;
abs_cluster = file->inode;
/* starting cluster == 0: read from FAT */
if(abs_cluster == MAGIC_FAT_CLUSTER)
{
abs_sector = fat->fat_start + rel_sector;
if(abs_sector >= fat->root_start)
return ERR_EOF;
}
/* starting cluster == 1: read from root directory */
else if(abs_cluster == MAGIC_ROOT_CLUSTER)
{
abs_sector = fat->root_start + rel_sector;
if(abs_sector >= fat->data_start)
return ERR_EOF;
}
/* starting cluster >= 2: normal read from data area of disk */
else
{
/* cluster within file */
rel_cluster = rel_sector / fat->sectors_per_cluster;
/* sector within cluster */
rel_sector %= fat->sectors_per_cluster;
/* chase clusters, so we go from relative cluster (cluster-within-file)... */
for(;;)
{
if(abs_cluster > fat->max_cluster)
return ERR_EOF;
err = fat_walk(file, &abs_sector, &abs_cluster);
if(err != 0)
return err;
if(rel_cluster == 0)
break;
rel_cluster--;
}
/* ...to absolute cluster (cluster-within-disk). fat_walk() also
converted cluster to sector, so add things together to get the
absolute sector number (finally!) */
abs_sector += rel_sector;
}
/* load it */
return read_sector(dev, mount->partition_start + abs_sector, blk);
}
/*****************************************************************************
*****************************************************************************/
static int fat_read(file_t *file, unsigned char *buf, unsigned want)
{
unsigned short byte_in_sector;
sector_t rel_sector;
unsigned got, count;
unsigned char *blk;
mount_t *mount;
dev_t *dev;
int err;
if(want == 0)
return 0;
mount = file->mount;
dev = mount->dev;
count = 0;
do
{
rel_sector = file->pos;
/* byte within sector */
byte_in_sector = rel_sector % dev->bytes_per_sector;
/* sector within file */
rel_sector /= dev->bytes_per_sector;
/* read the sector */
err = fat_read_sector(file, rel_sector, &blk);
if(err < 0)
return err;
/* how many bytes can we read from this sector? */
got = dev->bytes_per_sector - byte_in_sector;
/* nearing end of file? */
if(file->pos + got > file->file_size)
got = file->file_size - file->pos;
if(got == 0)
break;
/* how many will we actually read from it? */
got = min(got, want);
/* read them */
memcpy(buf, blk + byte_in_sector, got);
/* done with this sector
maybe I will need a function like this later?
uncache_sector(file, sector, blk); */
/* advance pointers */
file->pos += got;
buf += got;
want -= got;
count += got;
/* done? */
} while(want != 0);
return count;
}
/*****************************************************************************
*****************************************************************************/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -