mirror of https://github.com/FDOS/kernel.git
2068 lines
62 KiB
C
2068 lines
62 KiB
C
/***************************************************************
|
|
|
|
sys.c
|
|
DOS-C
|
|
|
|
sys utility for DOS-C
|
|
|
|
Copyright (c) 1991
|
|
Pasquale J. Villani
|
|
All Rights Reserved
|
|
|
|
This file is part of DOS-C.
|
|
|
|
DOS-C is free software; you can redistribute it and/or modify it under the
|
|
terms of the GNU General Public License as published by the Free Software
|
|
Foundation; either version 2, or (at your option) any later version.
|
|
|
|
DOS-C is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
details.
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
DOS-C; see the file COPYING. If not, write to the Free Software Foundation,
|
|
675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************/
|
|
|
|
/* #define DEBUG */ /* to display extra information */
|
|
/* #define DDEBUG */ /* to enable display of sector dumps */
|
|
/* #define WITHOEMCOMPATBS */ /* include support for OEM MS/PC DOS 3.??-6.x */
|
|
#define FDCONFIG /* include support to configure FD kernel */
|
|
|
|
#define SYS_VERSION "v3.6f"
|
|
#define SYS_NAME "DOS System Installer "
|
|
|
|
#include <stdlib.h>
|
|
#ifndef __GNUC__
|
|
#include <dos.h>
|
|
#endif
|
|
#include <ctype.h>
|
|
#ifndef __GNUC__
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#ifdef __TURBOC__
|
|
#include <mem.h>
|
|
#else
|
|
#include <memory.h>
|
|
#endif
|
|
#endif
|
|
#include <string.h>
|
|
#ifdef __TURBOC__
|
|
#include <dir.h>
|
|
#endif
|
|
#define SYS_MAXPATH 260
|
|
#include "portab.h"
|
|
#include "algnbyte.h"
|
|
#include "device.h"
|
|
#include "dcb.h"
|
|
#include "xstructs.h"
|
|
#include "ddate.h"
|
|
#include "dtime.h"
|
|
#include "fat.h"
|
|
|
|
/* These definitions deliberately put here instead of
|
|
* #including <stdio.h> to make executable MUCH smaller
|
|
* using [s]printf from prf.c!
|
|
*/
|
|
extern int VA_CDECL printf(CONST char * fmt, ...);
|
|
extern int VA_CDECL sprintf(char * buff, CONST char * fmt, ...);
|
|
|
|
#include "fat12com.h"
|
|
#include "fat16com.h"
|
|
#ifdef WITHFAT32
|
|
#include "fat32chs.h"
|
|
#include "fat32lba.h"
|
|
#endif
|
|
#ifdef WITHOEMCOMPATBS
|
|
#include "oemfat12.h"
|
|
#include "oemfat16.h"
|
|
#endif
|
|
|
|
#ifndef __WATCOMC__
|
|
|
|
#ifdef __GNUC__
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#define O_BINARY 0
|
|
#define stricmp strcasecmp
|
|
#define memicmp strncasecmp
|
|
union REGS {
|
|
struct {
|
|
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
|
|
} h;
|
|
struct {
|
|
unsigned short ax, bx, cx, dx, si, di, cflag;
|
|
} x;
|
|
};
|
|
struct SREGS {
|
|
unsigned short ds, es;
|
|
};
|
|
struct _diskfree_t {
|
|
unsigned short avail_clusters, sectors_per_cluster, bytes_per_sector;
|
|
};
|
|
|
|
int int86(int ivec, union REGS *in, union REGS *out)
|
|
{
|
|
/* must save sp for int25/26 */
|
|
asm("mov %5, (1f+1); jmp 0f; 0:push %%ds; mov %%di, %%dx; mov %%sp, %%di;"
|
|
"push %%di; push %%di;"
|
|
/* push twice to work both for int 25h/26h and int 21h */
|
|
"1:int $0x00; pop %%di; pop %%di;"
|
|
/* second pop always reads the correct SP value.
|
|
the first pop may read the FL left on stack. */
|
|
"mov %%di, %%sp; pop %%ds; sbb %0, %0" :
|
|
"=r"(out->x.cflag),
|
|
"=a"(out->x.ax), "=b"(out->x.bx), "=c"(out->x.cx), "=d"(out->x.dx) :
|
|
"q"((unsigned char)ivec), "a"(in->x.ax), "b"(in->x.bx),
|
|
"c"(in->x.cx), "D"(in->x.dx), "S"(in->x.si) :
|
|
"cc", "memory");
|
|
return out->x.ax;
|
|
}
|
|
|
|
int intdos(union REGS *in, union REGS *out)
|
|
{
|
|
return int86(0x21, in, out);
|
|
}
|
|
|
|
int intdosx(union REGS *in, union REGS *out, struct SREGS *s)
|
|
{
|
|
asm("push %%ds; mov %%bx, %%ds; int $0x21; pop %%ds; sbb %0, %0":
|
|
"=r"(out->x.cflag), "=a"(out->x.ax) :
|
|
"a"(in->x.ax), "c"(in->x.cx), "d"(in->x.dx),
|
|
"D"(in->x.di), "S"(in->x.si), "b"(s->ds), "e"(s->es) :
|
|
"cc", "memory");
|
|
return out->x.ax;
|
|
}
|
|
|
|
unsigned _dos_allocmem(unsigned size, unsigned *seg)
|
|
{
|
|
union REGS in, out;
|
|
in.h.ah = 0x48;
|
|
in.x.bx = size;
|
|
unsigned ret = intdos(&in, &out);
|
|
if (!out.x.cflag)
|
|
{
|
|
*seg = ret;
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
unsigned _dos_freemem(unsigned seg)
|
|
{
|
|
union REGS in, out;
|
|
struct SREGS s;
|
|
in.h.ah = 0x49;
|
|
s.es = seg;
|
|
return intdosx(&in, &out, &s);
|
|
}
|
|
|
|
unsigned int _dos_getdiskfree(unsigned int drive,
|
|
struct _diskfree_t *diskspace)
|
|
{
|
|
union REGS in, out;
|
|
in.x.ax = 0x3600;
|
|
in.x.dx = drive;
|
|
unsigned ret = intdos(&in, &out);
|
|
diskspace->avail_clusters = out.x.bx;
|
|
diskspace->sectors_per_cluster = out.x.dx;
|
|
diskspace->bytes_per_sector = out.x.cx;
|
|
return ret;
|
|
}
|
|
|
|
long filelength(int fhandle)
|
|
{
|
|
long ret = lseek(fhandle, 0, SEEK_END);
|
|
lseek(fhandle, 0, SEEK_SET);
|
|
return ret;
|
|
}
|
|
|
|
struct find_t {
|
|
char reserved[21];
|
|
unsigned char attrib;
|
|
unsigned short wr_time;
|
|
unsigned short wr_date;
|
|
unsigned long size;
|
|
char filename[13];
|
|
};
|
|
#define _A_NORMAL 0x00
|
|
#define _A_HIDDEN 0x02
|
|
#define _A_SYSTEM 0x04
|
|
|
|
int _dos_findfirst(const char *file_name, unsigned int attr,
|
|
struct find_t *find_tbuf)
|
|
{
|
|
union REGS in, out;
|
|
in.h.ah = 0x4e;
|
|
in.x.dx = FP_OFF(file_name);
|
|
in.x.cx = attr;
|
|
intdos(&in, &out);
|
|
if (out.x.cflag)
|
|
return out.x.ax;
|
|
memcpy(find_tbuf, (void *)0x80, sizeof(*find_tbuf));
|
|
return 0;
|
|
}
|
|
#else
|
|
#include <io.h>
|
|
#endif
|
|
|
|
/* returns current DOS drive, A=0, B=1,C=2, ... */
|
|
#ifdef __TURBOC__
|
|
#define getcurdrive (unsigned)getdisk
|
|
#else
|
|
unsigned getcurdrive(void)
|
|
{
|
|
union REGS regs;
|
|
regs.h.ah = 0x19;
|
|
int86(0x21, ®s, ®s);
|
|
return regs.h.al;
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
|
|
/* returns current DOS drive, A=0, B=1,C=2, ... */
|
|
unsigned getcurdrive(void);
|
|
#pragma aux getcurdrive = \
|
|
"mov ah, 0x19" \
|
|
"int 0x21" \
|
|
"xor ah, ah" \
|
|
__value [__ax];
|
|
|
|
|
|
long filelength(int __handle);
|
|
#pragma aux filelength = \
|
|
"mov ax, 0x4202" \
|
|
"xor cx, cx" \
|
|
"xor dx, dx" \
|
|
"int 0x21" \
|
|
"push ax" \
|
|
"push dx" \
|
|
"mov ax, 0x4200" \
|
|
"xor cx, cx" \
|
|
"xor dx, dx" \
|
|
"int 0x21" \
|
|
"pop dx" \
|
|
"pop ax" \
|
|
__parm [__bx] \
|
|
__modify [__cx] \
|
|
__value [__dx __ax];
|
|
|
|
extern int unlink(const char *pathname);
|
|
|
|
/* some non-conforming functions to make the executable smaller */
|
|
int open(const char *pathname, int flags, ...)
|
|
{
|
|
int handle;
|
|
int result = (flags & O_CREAT ?
|
|
_dos_creat(pathname, _A_NORMAL, &handle) :
|
|
_dos_open(pathname, flags & (O_RDONLY | O_WRONLY | O_RDWR),
|
|
&handle));
|
|
|
|
return (result == 0 ? handle : -1);
|
|
}
|
|
|
|
int read(int fd, void *buf, unsigned count)
|
|
{
|
|
unsigned bytes;
|
|
int result = _dos_read(fd, buf, count, &bytes);
|
|
|
|
return (result == 0 ? bytes : -1);
|
|
}
|
|
|
|
int write(int fd, const void *buf, unsigned count)
|
|
{
|
|
unsigned bytes;
|
|
int result = _dos_write(fd, buf, count, &bytes);
|
|
|
|
return (result == 0 ? bytes : -1);
|
|
}
|
|
|
|
#define close _dos_close
|
|
#endif
|
|
|
|
#if defined(__WATCOMC__) || defined(__GNUC__)
|
|
int stat(const char *file_name, struct stat *statbuf)
|
|
{
|
|
struct find_t find_tbuf;
|
|
|
|
int ret = _dos_findfirst(file_name, _A_NORMAL | _A_HIDDEN | _A_SYSTEM, &find_tbuf);
|
|
statbuf->st_size = (off_t)find_tbuf.size;
|
|
/* statbuf->st_attr = (ULONG)find_tbuf.attrib; */
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef __WATCOMC__
|
|
/* WATCOM's getenv is case-insensitive which wastes a lot of space
|
|
for our purposes. So here's a simple case-sensitive one */
|
|
char *getenv(const char *name)
|
|
{
|
|
char **envp, *ep;
|
|
const char *np;
|
|
char ec, nc;
|
|
|
|
for (envp = environ; (ep = *envp) != NULL; envp++) {
|
|
np = name;
|
|
do {
|
|
ec = *ep++;
|
|
nc = *np++;
|
|
if (nc == 0) {
|
|
if (ec == '=')
|
|
return ep;
|
|
break;
|
|
}
|
|
} while (ec == nc);
|
|
}
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
BYTE pgm[] = "SYS";
|
|
|
|
#define SEC_SIZE 512
|
|
#define COPY_SIZE 0x4000
|
|
|
|
struct bootsectortype {
|
|
UBYTE bsJump[3];
|
|
char OemName[8];
|
|
UWORD bsBytesPerSec;
|
|
UBYTE bsSecPerClust;
|
|
UWORD bsResSectors;
|
|
UBYTE bsFATs;
|
|
UWORD bsRootDirEnts;
|
|
UWORD bsSectors;
|
|
UBYTE bsMedia;
|
|
UWORD bsFATsecs;
|
|
UWORD bsSecPerTrack;
|
|
UWORD bsHeads;
|
|
ULONG bsHiddenSecs;
|
|
ULONG bsHugeSectors;
|
|
UBYTE bsDriveNumber;
|
|
UBYTE bsReserved1;
|
|
UBYTE bsBootSignature;
|
|
ULONG bsVolumeID;
|
|
char bsVolumeLabel[11];
|
|
char bsFileSysType[8];
|
|
};
|
|
|
|
struct bootsectortype32 {
|
|
UBYTE bsJump[3];
|
|
char OemName[8];
|
|
UWORD bsBytesPerSec;
|
|
UBYTE bsSecPerClust;
|
|
UWORD bsResSectors;
|
|
UBYTE bsFATs;
|
|
UWORD bsRootDirEnts;
|
|
UWORD bsSectors;
|
|
UBYTE bsMedia;
|
|
UWORD bsFATsecs;
|
|
UWORD bsSecPerTrack;
|
|
UWORD bsHeads;
|
|
ULONG bsHiddenSecs;
|
|
ULONG bsHugeSectors;
|
|
ULONG bsBigFatSize;
|
|
UBYTE bsFlags;
|
|
UBYTE bsMajorVersion;
|
|
UWORD bsMinorVersion;
|
|
ULONG bsRootCluster;
|
|
UWORD bsFSInfoSector;
|
|
UWORD bsBackupBoot;
|
|
ULONG bsReserved2[3];
|
|
UBYTE bsDriveNumber;
|
|
UBYTE bsReserved3;
|
|
UBYTE bsExtendedSignature;
|
|
ULONG bsSerialNumber;
|
|
char bsVolumeLabel[11];
|
|
char bsFileSystemID[8];
|
|
};
|
|
|
|
/*
|
|
* globals needed by put_boot & check_space
|
|
*/
|
|
enum {FAT12 = 12, FAT16 = 16, FAT32 = 32} fs; /* file system type */
|
|
unsigned smallfat32;
|
|
/* static */ struct xfreespace x; /* we make this static to be 0 by default -
|
|
this avoids FAT misdetections */
|
|
|
|
#define SBOFFSET 11
|
|
#define SBSIZE (sizeof(struct bootsectortype) - SBOFFSET)
|
|
#define SBSIZE32 (sizeof(struct bootsectortype32) - SBOFFSET)
|
|
|
|
/* essentially - verify alignment on byte boundaries at compile time */
|
|
struct VerifyBootSectorSize {
|
|
char failure1[sizeof(struct bootsectortype) == 62 ? 1 : -1];
|
|
char failure2[sizeof(struct bootsectortype) == 62 ? 1 : 0];
|
|
/* (Watcom has a nice warning for this, by the way) */
|
|
};
|
|
|
|
#ifdef FDCONFIG
|
|
int FDKrnConfigMain(int argc, char **argv);
|
|
#endif
|
|
|
|
/* FreeDOS sys, we default to our kernel and load segment, but
|
|
if not found (or explicitly given) support OEM DOS variants
|
|
(such as DR-DOS or a FreeDOS kernel mimicing other DOSes).
|
|
Note: other (especially older) DOS versions expect the boot
|
|
loader to perform particular steps, which we may not do;
|
|
older PC/MS DOS variants may work with the OEM compatible
|
|
boot sector (optionally included).
|
|
*/
|
|
typedef struct DOSBootFiles {
|
|
const char * kernel; /* filename boot sector loads and chains to */
|
|
const char * dos; /* optional secondary file for OS */
|
|
WORD loadaddr; /* segment kernel file expects to start at for stdbs */
|
|
/* or offset to jump into kernel for oem compat bs */
|
|
BOOL stdbs; /* use FD boot sector (T) or oem compat one (F) */
|
|
LONG minsize; /* smallest dos file can be and be valid, 0=existance optional */
|
|
} DOSBootFiles;
|
|
#define FREEDOS_FILES { "KERNEL.SYS", NULL, 0x60/*:0*/, 1, 0 },
|
|
DOSBootFiles bootFiles[] = {
|
|
/* Note: This order is the order OEM:AUTO uses to determine DOS flavor. */
|
|
/* FreeDOS */ FREEDOS_FILES
|
|
/* EDR-DOS */ { "DRBIO.SYS", "DRDOS.SYS", 0x70, 1, 1 },
|
|
/* EDR-DOS */ { "EDRPACK.SYS", NULL, 0x60, 1, 0 },
|
|
/* EDR-DOS */ { "EDRDOS.COM", NULL, 0x60, 1, 0 },
|
|
/* OSS MS-DOS */ { "LMSPACK.SYS", NULL, 0x60, 1, 0 },
|
|
/* OSS MS-DOS */ { "LMSDOS.COM", NULL, 0x60, 1, 0 },
|
|
/* OpenDOS */ { "IBMBIO.COM", "IBMDOS.COM", /*0x70:*/0x70, 1, 1 },
|
|
#ifdef WITHOEMCOMPATBS
|
|
/* PC-DOS */ { "IBMBIO.COM", "IBMDOS.COM", /*0x70:*/0x0, 0, 6138 },
|
|
/* MS-DOS */ { "IO.SYS", "MSDOS.SYS", /*0x70:*/0x0, 0, 10240 },
|
|
/* W9x-DOS */ { "IO.SYS", "MSDOS.SYS", /*0x70:*/0x0200, 0, 0 },
|
|
/* Rx-DOS */ { "RXDOSBIO.SYS", "RXDOS.SYS", /*0x70:*/0x0, 0, 1 },
|
|
#endif
|
|
};
|
|
#define DOSFLAVORS (sizeof(bootFiles) / sizeof(*bootFiles))
|
|
|
|
/* associate friendly name with index into bootFiles array */
|
|
#define OEM_AUTO (-1) /* attempt to guess DOS on source drive */
|
|
#define OEM_FD 0 /* standard FreeDOS mode */
|
|
#define OEM_EDR 1 /* DRBIO, DRDOS version of EDR kernel */
|
|
#define OEM_LEDRPACK 2 /* lDOS drload version of EDR kernel */
|
|
#define OEM_LEDR 3 /* lDOS iniload version of EDR kernel */
|
|
#define OEM_LMSPACK 4 /* lDOS drload version of MS-DOS kernel */
|
|
#define OEM_LMS 5 /* lDOS iniload version of MS-DOS kernel */
|
|
#define OEM_OPENDOS 6 /* old IBMBIO, IBMDOS compatible DR-DOS versions */
|
|
#ifdef WITHOEMCOMPATBS
|
|
#define OEM_PC 7 /* use PC-DOS compatible boot sector and names */
|
|
#define OEM_MS 8 /* use PC-DOS compatible BS with MS names */
|
|
#define OEM_W9x 9 /* use PC-DOS compatible BS with MS names */
|
|
#define OEM_RX 10 /* use PC-DOS compatible BS with Rx names */
|
|
#endif
|
|
|
|
CONST char * msgDOS[DOSFLAVORS] = { /* order should match above items */
|
|
"\n", /* In standard FreeDOS mode, don't print anything special */
|
|
"Enhanced DR-DOS 7.01.07+ mode (DRBIO.SYS and DRDOS.SYS)\n",
|
|
"Enhanced DR-DOS mode (EDRPACK.SYS, lDOS drload)\n",
|
|
"Enhanced DR-DOS mode (EDRDOS.COM, lDOS iniload)\n",
|
|
"OSS MS-DOS mode (LMSPACK.SYS, lDOS drload)\n",
|
|
"OSS MS-DOS mode (LMSDOS.COM, lDOS iniload)\n",
|
|
"Novell DOS 7, Caldera OpenDOS, DR-DOS 7.02 - 7.03, 7.01.01 - 7.01.06 mode\n",
|
|
#ifdef WITHOEMCOMPATBS
|
|
"PC-DOS, DR DOS 5 - Novell DOS 7 mode\n",
|
|
"MS-DOS mode\n",
|
|
"Win9x DOS mode\n",
|
|
"RxDOS mode\n",
|
|
#endif
|
|
};
|
|
|
|
typedef struct SYSOptions {
|
|
BYTE srcDrive[SYS_MAXPATH]; /* source drive:[path], root assumed if no path */
|
|
BYTE dstDrive; /* destination drive [STD SYS option] */
|
|
int flavor; /* DOS variant we want to boot, default is AUTO/FD */
|
|
DOSBootFiles kernel; /* file name(s) and relevant data for kernel */
|
|
BYTE defBootDrive; /* value stored in boot sector for drive, eg 0x0=A, 0x80=C */
|
|
BOOL ignoreBIOS; /* true to NOP out boot sector code to get drive# from BIOS */
|
|
BOOL skipBakBSCopy; /* true to not copy boot sector to backup boot sector */
|
|
BOOL copyKernel; /* true to copy kernel files */
|
|
BOOL copyShell; /* true to copy command interpreter */
|
|
BOOL writeBS; /* true to write boot sector to drive/partition LBA 0 */
|
|
BYTE *bsFile; /* file name & path to save bs to when saving to file */
|
|
BYTE *bsFileOrig; /* file name & path to save original bs when backing up */
|
|
BYTE *fnKernel; /* optional override to source kernel filename (src only) */
|
|
BYTE *fnCmd; /* optional override to cmd interpreter filename (src & dest) */
|
|
enum {AUTO=0,LBA,CHS} force; /* optional force boot sector to only use LBA or CHS */
|
|
BOOL verbose; /* show extra (DEBUG) output */
|
|
} SYSOptions;
|
|
|
|
void dumpBS(const char *, int);
|
|
void restoreBS(const char *, int);
|
|
void put_boot(SYSOptions *opts);
|
|
BOOL check_space(COUNT, ULONG);
|
|
BOOL copy(SYSOptions *opts, const BYTE *source, COUNT drive, const BYTE * filename);
|
|
|
|
void showHelpAndExit(void)
|
|
{
|
|
printf(
|
|
"Usage: %s [source] drive: [bootsect] [{option}]\n"
|
|
" source = A:,B:,C:\\DOS\\,etc., or current directory if not given\n"
|
|
" drive = A,B,etc.\n"
|
|
" bootsect = name of 512-byte boot sector file image for drive:\n"
|
|
" to write to *instead* of real boot sector\n"
|
|
" {option} is one or more of the following:\n"
|
|
" /BOTH : write to *both* the real boot sector and the image file\n"
|
|
" /BOOTONLY: do *not* copy kernel / shell, only update boot sector or image\n"
|
|
" /UPDATE : copy kernel and update boot sector (do *not* copy shell)\n"
|
|
" /OEM : indicates boot sector, filenames, and load segment to use\n"
|
|
" /OEM:FD FreeDOS settings\n"
|
|
" /OEM:EDR Enhanced DR-DOS (DRBIO.SYS and DRDOS.SYS)\n"
|
|
" /OEM:LEDRPACK Enhanced DR-DOS (EDRPACK.SYS, lDOS drload)\n"
|
|
" /OEM:LEDR Enhanced DR-DOS (EDRDOS.COM, lDOS iniload)\n"
|
|
" /OEM:LMSPACK OSS MS-DOS (LMSPACK.SYS, lDOS drload)\n"
|
|
" /OEM:LMS OSS MS-DOS (LMSDOS.COM, lDOS iniload)\n"
|
|
" /OEM:OPENDOS Caldera OpenDOS 7.01 (and Novell DOS 7),\n"
|
|
" DR-DOS 7.02 - 7.03, 7.01.01 - 7.01.06\n"
|
|
#ifdef WITHOEMCOMPATBS
|
|
" /OEM:PC PC-DOS, DR DOS 5 - Novell DOS 7\n"
|
|
" /OEM:MS MS-DOS\n"
|
|
" /OEM:W9x MS Win9x DOS\n"
|
|
#endif
|
|
" default is /OEM[:AUTO], select DOS based on existing files\n"
|
|
" /K name : name of kernel to use in boot sector instead of %s\n"
|
|
" /L segm : hex load segment to use in boot sector instead of %02x\n"
|
|
" /B btdrv : hex BIOS # of boot drive set in bs, 0=A:, 80=1st hd,...\n"
|
|
" /FORCE : override automatic selection of BIOS related settings\n"
|
|
" /FORCE:BSDRV use boot drive # set in bootsector\n"
|
|
" /FORCE:BIOSDRV use boot drive # provided by BIOS\n"
|
|
" /NOBAKBS : skips copying boot sector to backup bs, FAT32 only else ignored\n"
|
|
#ifdef FDCONFIG
|
|
"%s CONFIG /help\n"
|
|
#endif
|
|
/*SYS, KERNEL.SYS/DRBIO.SYS 0x60/0x70*/
|
|
, pgm, bootFiles[0].kernel, bootFiles[0].loadaddr
|
|
#ifdef FDCONFIG
|
|
, pgm
|
|
#endif
|
|
);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* get and validate arguments */
|
|
void initOptions(int argc, char *argv[], SYSOptions *opts)
|
|
{
|
|
int argno;
|
|
int drivearg = 0; /* drive argument, position of 1st or 2nd non option */
|
|
int srcarg = 0; /* nonzero if optional source argument */
|
|
BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file [to copy] */
|
|
struct stat fstatbuf;
|
|
|
|
/* initialize to defaults */
|
|
memset(opts, 0, sizeof(SYSOptions));
|
|
/* set srcDrive and dstDrive after processing args */
|
|
opts->flavor = OEM_AUTO; /* attempt to detect DOS user wants to boot */
|
|
opts->copyKernel = 1; /* actually copy the kernel and cmd interpreter to dstDrive */
|
|
opts->copyShell = 1;
|
|
|
|
/* cycle through processing cmd line arguments */
|
|
for(argno = 1; argno < argc; argno++)
|
|
{
|
|
char *argp = argv[argno];
|
|
|
|
if (argp[0] == '/') /* optional switch */
|
|
{
|
|
argp++; /* skip past the '/' character */
|
|
|
|
/* explicit request for base help/usage */
|
|
if ((*argp == '?') || (memicmp(argp, "HELP", 4) == 0))
|
|
{
|
|
showHelpAndExit();
|
|
}
|
|
/* enable extra (DEBUG) output */
|
|
else if (memicmp(argp, "VERBOSE", 7) == 0)
|
|
{
|
|
opts->verbose = 1;
|
|
}
|
|
/* write to *both* the real boot sector and the image file */
|
|
else if (memicmp(argp, "BOTH", 4) == 0)
|
|
{
|
|
opts->writeBS = 1; /* note: if bs file omitted, then same as omitting /BOTH */
|
|
}
|
|
/* do *not* copy kernel / shell, only update boot sector or image */
|
|
else if (memicmp(argp, "BOOTONLY", 8) == 0)
|
|
{
|
|
opts->copyKernel = 0;
|
|
opts->copyShell = 0;
|
|
}
|
|
/* copy kernel and update boot sector (do *not* copy shell) */
|
|
else if (memicmp(argp, "UPDATE", 6) == 0)
|
|
{
|
|
opts->copyKernel = 1;
|
|
opts->copyShell = 0;
|
|
}
|
|
/* indicates compatibility mode, fs, filenames, and load segment to use */
|
|
else if (memicmp(argp, "OEM", 3) == 0)
|
|
{
|
|
argp += 3;
|
|
if (!*argp)
|
|
opts->flavor = OEM_AUTO;
|
|
else if (*argp == ':')
|
|
{
|
|
argp++; /* point to DR/PC/MS that follows */
|
|
if (memicmp(argp, "AUTO", 4) == 0)
|
|
opts->flavor = OEM_AUTO;
|
|
else if (memicmp(argp, "LEDRPACK", 8) == 0)
|
|
opts->flavor = OEM_LEDRPACK;
|
|
else if (memicmp(argp, "LEDR", 4) == 0)
|
|
opts->flavor = OEM_LEDR;
|
|
else if (memicmp(argp, "EDR", 3) == 0)
|
|
opts->flavor = OEM_EDR;
|
|
else if (memicmp(argp, "LMSPACK", 7) == 0)
|
|
opts->flavor = OEM_LMSPACK;
|
|
else if (memicmp(argp, "LMS", 3) == 0)
|
|
opts->flavor = OEM_LMS;
|
|
else if (memicmp(argp, "OPENDOS", 7) == 0)
|
|
opts->flavor = OEM_OPENDOS;
|
|
#ifdef WITHOEMCOMPATBS
|
|
else if (memicmp(argp, "PC", 2) == 0)
|
|
opts->flavor = OEM_PC;
|
|
else if (memicmp(argp, "MS", 2) == 0)
|
|
opts->flavor = OEM_MS;
|
|
else if (memicmp(argp, "W9", 2) == 0)
|
|
opts->flavor = OEM_W9x;
|
|
else if (memicmp(argp, "RX", 2) == 0)
|
|
opts->flavor = OEM_RX;
|
|
#endif
|
|
else if (memicmp(argp, "FD", 2) == 0)
|
|
opts->flavor = OEM_FD;
|
|
else
|
|
{
|
|
printf("%s: unknown OEM qualifier %s\n", pgm, argp);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("%s: unknown OEM qualifier %s\n", pgm, argp);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
/* override auto options */
|
|
else if (memicmp(argp, "FORCE", 5) == 0)
|
|
{
|
|
argp += 5;
|
|
if (*argp == ':')
|
|
{
|
|
argp++; /* point to CHS/LBA/... that follows */
|
|
|
|
/* specify which BIOS access mode to use */
|
|
if (memicmp(argp, "AUTO", 4) == 0) /* default */
|
|
opts->force = AUTO;
|
|
else if (memicmp(argp, "CHS", 3) == 0)
|
|
opts->force = CHS;
|
|
else if (memicmp(argp, "LBA", 3) == 0)
|
|
opts->force = LBA;
|
|
|
|
/* specify if BIOS or BOOTSECTOR provided boot drive # is to be used */
|
|
else if (memicmp(argp, "BSDRV", 5) == 0) /* same as FORCEDRV */
|
|
opts->ignoreBIOS = 1;
|
|
else if (memicmp(argp, "BIOSDRV", 7) == 0) /* always use BIOS passed */
|
|
opts->ignoreBIOS = -1;
|
|
else
|
|
{
|
|
printf("%s: invalid FORCE qualifier %s\n", pgm, argp);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
else if (memicmp(argp, "DRV", 3) == 0) /* FORCEDRV */
|
|
{
|
|
/* force use of drive # set in bs instead of BIOS boot value */
|
|
/* deprecated, use FORCE:BSDRV */
|
|
opts->ignoreBIOS = 1;
|
|
}
|
|
else
|
|
{
|
|
printf("%s: invalid FORCE qualifier %s\n", pgm, argp);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
/* skips copying boot sector to backup bs, FAT32 only else ignored */
|
|
else if (memicmp(argp, "NOBAKBS", 7) == 0)
|
|
{
|
|
opts->skipBakBSCopy = 1;
|
|
}
|
|
else if (argno + 1 < argc) /* two part options, /SWITCH VALUE */
|
|
{
|
|
argno++;
|
|
if (toupper(*argp) == 'K') /* set Kernel name */
|
|
{
|
|
opts->kernel.kernel = argv[argno];
|
|
}
|
|
else if (toupper(*argp) == 'L') /* set Load segment */
|
|
{
|
|
opts->kernel.loadaddr = (WORD)strtol(argv[argno], NULL, 16);
|
|
}
|
|
else if (memicmp(argp, "B", 2) == 0) /* set boot drive # */
|
|
{
|
|
opts->defBootDrive = (BYTE)strtol(argv[argno], NULL, 16);
|
|
}
|
|
/* options not documented by showHelpAndExit() */
|
|
else if (memicmp(argp, "SKFN", 4) == 0) /* set KERNEL.SYS input file and /OEM:FD */
|
|
{
|
|
opts->flavor = OEM_FD;
|
|
opts->fnKernel = argv[argno];
|
|
}
|
|
else if (memicmp(argp, "SCFN", 4) == 0) /* sets COMMAND.COM input file */
|
|
{
|
|
opts->fnCmd = argv[argno];
|
|
}
|
|
else if (memicmp(argp, "BACKUPBS", 8) == 0) /* save current bs before overwriting */
|
|
{
|
|
opts->bsFileOrig = argv[argno];
|
|
}
|
|
else if (memicmp(argp, "DUMPBS", 6) == 0) /* save current bs and exit */
|
|
{
|
|
if (drivearg)
|
|
dumpBS(argv[argno], (BYTE)(toupper(*(argv[drivearg])) - 'A'));
|
|
else
|
|
printf("%s: unspecified drive, unable to obtain boot sector\n", pgm);
|
|
exit(1);
|
|
}
|
|
else if (memicmp(argp, "RESTORBS", 8) == 0) /* overwrite bs and exit */
|
|
{
|
|
if (drivearg)
|
|
restoreBS(argv[argno], (BYTE)(toupper(*(argv[drivearg])) - 'A'));
|
|
else
|
|
printf("%s: unspecified drive, unable to restore boot sector\n", pgm);
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
printf("%s: unknown option, %s\n", pgm, argv[argno]);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("%s: unknown option or missing parameter, %s\n", pgm, argv[argno]);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
else if (!drivearg)
|
|
{
|
|
drivearg = argno; /* either source or destination drive */
|
|
}
|
|
else if (!srcarg /* && drivearg */ && !opts->bsFile)
|
|
{
|
|
/* need to determine is user specified [source] dest or dest [bootfile] (or [source] dest [bootfile])
|
|
- dest must be either X or X: as only a drive specifier without any path is valid -
|
|
if 1st arg not drive and 2nd is then [source] dest form
|
|
if 1st arg drive and 2nd is not drive then dest [bootfile] form
|
|
if both 1st arg and 2nd are not drives then invalid arguments
|
|
if both 1st arg and 2nd are drives then assume [source] dest form (use ./X form is single letter used)
|
|
*/
|
|
if (!argv[drivearg][1] || (argv[drivearg][1]==':' && !argv[drivearg][2])) /* if 1st arg drive */
|
|
{
|
|
if (!argv[argno][1] || (argv[argno][1]==':' && !argv[argno][2])) /* if 2nd arg drive */
|
|
{
|
|
srcarg = drivearg; /* set source path */
|
|
drivearg = argno; /* set destination drive */
|
|
}
|
|
else
|
|
{
|
|
opts->bsFile = argv[argno];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!argv[argno][1] || (argv[argno][1]==':' && !argv[argno][2])) /* if 2nd arg drive */
|
|
{
|
|
srcarg = drivearg; /* set source path */
|
|
drivearg = argno; /* set destination drive */
|
|
}
|
|
else
|
|
{
|
|
goto EXITBADARG;
|
|
}
|
|
}
|
|
}
|
|
else if (!opts->bsFile /* && srcarg && drivearg */)
|
|
{
|
|
opts->bsFile = argv[argno];
|
|
}
|
|
else /* if (opts->bsFile && srcarg && drivearg) */
|
|
{
|
|
EXITBADARG:
|
|
printf("%s: invalid argument %s\n", pgm, argv[argno]);
|
|
showHelpAndExit();
|
|
}
|
|
}
|
|
|
|
/* if neither BOTH nor a boot sector file specified, then write to boot record */
|
|
if (!opts->bsFile)
|
|
opts->writeBS = 1;
|
|
|
|
/* set dest path */
|
|
if (!drivearg)
|
|
showHelpAndExit();
|
|
opts->dstDrive = (BYTE)(toupper(*(argv[drivearg])) - 'A');
|
|
if (/* (opts->dstDrive < 0) || */ (opts->dstDrive >= 26))
|
|
{
|
|
printf("%s: drive %c must be A:..Z:\n", pgm, *(argv[drivearg]));
|
|
exit(1);
|
|
}
|
|
|
|
/* set source path, default to current drive */
|
|
sprintf(opts->srcDrive, "%c:", 'A' + getcurdrive());
|
|
if (srcarg)
|
|
{
|
|
int slen;
|
|
/* set source path, reserving room to append filename */
|
|
if ( (argv[srcarg][1] == ':') || ((argv[srcarg][0]=='\\') && (argv[srcarg][1] == '\\')) )
|
|
strncpy(opts->srcDrive, argv[srcarg], SYS_MAXPATH-13);
|
|
else if (argv[srcarg][1] == '\0') /* assume 1 char is drive not path specifier */
|
|
sprintf(opts->srcDrive, "%c:", toupper(*(argv[srcarg])));
|
|
else /* only path provided, append to default drive */
|
|
strncat(opts->srcDrive, argv[srcarg], SYS_MAXPATH-15);
|
|
slen = strlen(opts->srcDrive);
|
|
/* if path follows drive, ensure ends in a slash, ie X:-->X: or X:.\mypath-->X:.\mypath\ */
|
|
if ((slen>2) && (opts->srcDrive[slen-1] != '\\') && (opts->srcDrive[slen-1] != '/'))
|
|
strcat(opts->srcDrive, "\\");
|
|
}
|
|
/* source path is now in form of just a drive, "X:"
|
|
or form of drive + path + directory separator, "X:\path\" or "\\path\"
|
|
If just drive we try current path then root, else just indicated path.
|
|
*/
|
|
|
|
|
|
/* if source and dest are same drive, then source should not be root,
|
|
so if is same drive and not explicit path, force only current
|
|
Note: actual copy routine prevents overwriting self when src=dst
|
|
*/
|
|
if ( (opts->dstDrive == (toupper(*(opts->srcDrive))-'A')) && (!opts->srcDrive[2]) )
|
|
strcat(opts->srcDrive, ".\\");
|
|
|
|
|
|
/* attempt to detect compatibility settings user needs */
|
|
if (opts->flavor == OEM_AUTO)
|
|
{
|
|
/* 1st loop checking current just source path provided */
|
|
for (argno = 0; argno < DOSFLAVORS; argno++)
|
|
{
|
|
/* look for existing file matching kernel filename */
|
|
sprintf(srcFile, "%s%s", opts->srcDrive, bootFiles[argno].kernel);
|
|
if (stat(srcFile, &fstatbuf)) continue; /* if !exists() try again */
|
|
if (!fstatbuf.st_size) continue; /* file must not be empty */
|
|
|
|
/* now check if secondary file exists and of minimal size */
|
|
if (bootFiles[argno].minsize)
|
|
{
|
|
sprintf(srcFile, "%s%s", opts->srcDrive, bootFiles[argno].dos);
|
|
if (stat(srcFile, &fstatbuf)) continue;
|
|
if (fstatbuf.st_size < bootFiles[argno].minsize) continue;
|
|
}
|
|
|
|
/* above criteria succeeded, so default to corresponding DOS */
|
|
opts->flavor = argno;
|
|
break;
|
|
}
|
|
|
|
/* if no match, and source just drive, try root */
|
|
if ( (opts->flavor == OEM_AUTO) && (!opts->srcDrive[2]) )
|
|
{
|
|
for (argno = 0; argno < DOSFLAVORS; argno++)
|
|
{
|
|
/* look for existing file matching kernel filename */
|
|
sprintf(srcFile, "%s\\%s", opts->srcDrive, bootFiles[argno].kernel);
|
|
if (stat(srcFile, &fstatbuf)) continue; /* if !exists() try again */
|
|
if (!fstatbuf.st_size) continue; /* file must not be empty */
|
|
|
|
/* now check if secondary file exists and of minimal size */
|
|
if (bootFiles[argno].minsize)
|
|
{
|
|
sprintf(srcFile, "%s\\%s", opts->srcDrive, bootFiles[argno].dos);
|
|
if (stat(srcFile, &fstatbuf)) continue;
|
|
if (fstatbuf.st_size < bootFiles[argno].minsize) continue;
|
|
}
|
|
|
|
/* above criteria succeeded, so default to corresponding DOS */
|
|
opts->flavor = argno;
|
|
strcat(opts->srcDrive, "\\"); /* indicate to use root from now on */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if unable to determine DOS, assume FreeDOS */
|
|
if (opts->flavor == OEM_AUTO) opts->flavor = OEM_FD;
|
|
|
|
/* set compatibility settings not explicitly set */
|
|
if (!opts->kernel.kernel) opts->kernel.kernel = bootFiles[opts->flavor].kernel;
|
|
if (!opts->kernel.dos) opts->kernel.dos = bootFiles[opts->flavor].dos;
|
|
if (!opts->kernel.loadaddr) opts->kernel.loadaddr = bootFiles[opts->flavor].loadaddr;
|
|
opts->kernel.stdbs = bootFiles[opts->flavor].stdbs;
|
|
opts->kernel.minsize = bootFiles[opts->flavor].minsize;
|
|
|
|
|
|
/* did user insist on always using BIOS provided drive # */
|
|
if (opts->ignoreBIOS == -1)
|
|
opts->ignoreBIOS = 0; /* its really a boolean value in rest of code */
|
|
/* if destination is floppy (A: or B:) then use drive # stored in boot sector */
|
|
else if (opts->dstDrive < 2)
|
|
opts->ignoreBIOS = 1;
|
|
|
|
/* if bios drive to store in boot sector not set and not floppy set to 1st hd */
|
|
if (!opts->defBootDrive && (opts->dstDrive >= 2))
|
|
opts->defBootDrive = 0x80;
|
|
/* else opts->defBootDrive = 0x0; the 1st floppy */
|
|
|
|
|
|
/* unless we are only setting boot sector, verify kernel file exists */
|
|
if (opts->copyKernel)
|
|
{
|
|
/* check kernel (primary file) 1st */
|
|
sprintf(srcFile, "%s%s", opts->srcDrive, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel);
|
|
if (stat(srcFile, &fstatbuf)) /* if !exists() */
|
|
{
|
|
/* check root path as well if src is drive only */
|
|
sprintf(srcFile, "%s\\%s", opts->srcDrive, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel);
|
|
if (opts->srcDrive[2] || stat(srcFile, &fstatbuf))
|
|
{
|
|
printf("%s: failed to find kernel file %s\n", pgm, (opts->fnKernel)?opts->fnKernel:opts->kernel.kernel);
|
|
exit(1);
|
|
}
|
|
/* else found, but in root, so force to always use root */
|
|
strcat(opts->srcDrive, "\\");
|
|
}
|
|
|
|
/* now check for secondary file */
|
|
if (opts->kernel.dos && opts->kernel.minsize)
|
|
{
|
|
sprintf(srcFile, "%s%s", opts->srcDrive, opts->kernel.dos);
|
|
if (stat(srcFile, &fstatbuf))
|
|
{
|
|
printf("%s: failed to find source file %s\n", pgm, opts->kernel.dos);
|
|
exit(1);
|
|
}
|
|
if (fstatbuf.st_size < opts->kernel.minsize)
|
|
{
|
|
printf("%s: source file %s appears corrupt, invalid size\n", pgm, opts->kernel.dos);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if updating or only setting bootsector then skip this check */
|
|
if (opts->copyShell)
|
|
{
|
|
/* lastly check for command interpreter */
|
|
sprintf(srcFile, "%s%s", opts->srcDrive, (opts->fnCmd)?opts->fnCmd:"COMMAND.COM");
|
|
if (stat(srcFile, &fstatbuf)) /* if !exists() */
|
|
{
|
|
char *comspec = getenv("COMSPEC");
|
|
if (opts->fnCmd || (comspec == NULL) || stat(comspec, &fstatbuf))
|
|
{
|
|
printf("%s: failed to find command interpreter (shell) file %s\n", pgm, srcFile);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
SYSOptions opts; /* boot options and other flags */
|
|
BYTE srcFile[SYS_MAXPATH]; /* full path+name of [kernel] file [to copy] */
|
|
|
|
#ifdef FDCONFIG
|
|
if (argc > 1 && memicmp(argv[1], "CONFIG", 6) == 0)
|
|
{
|
|
exit(FDKrnConfigMain(argc, argv));
|
|
}
|
|
#endif
|
|
|
|
initOptions(argc, argv, &opts);
|
|
|
|
if (opts.verbose) {
|
|
printf(SYS_NAME SYS_VERSION ", " __DATE__ "\n");
|
|
printf(msgDOS[opts.flavor]);
|
|
}
|
|
|
|
if (opts.verbose)
|
|
printf("Processing boot sector...\n");
|
|
put_boot(&opts);
|
|
|
|
if (opts.copyKernel)
|
|
{
|
|
if (opts.verbose)
|
|
printf("Now copying system files...\n");
|
|
|
|
sprintf(srcFile, "%s%s", opts.srcDrive, (opts.fnKernel)?opts.fnKernel:opts.kernel.kernel);
|
|
if (!copy(&opts, srcFile, opts.dstDrive, opts.kernel.kernel))
|
|
{
|
|
printf("%s: cannot copy \"%s\"\n", pgm, srcFile);
|
|
exit(1);
|
|
} /* copy kernel */
|
|
|
|
if (opts.kernel.dos)
|
|
{
|
|
sprintf(srcFile, "%s%s", opts.srcDrive, opts.kernel.dos);
|
|
if (!copy(&opts, srcFile, opts.dstDrive, opts.kernel.dos) && opts.kernel.minsize)
|
|
{
|
|
printf("%s: cannot copy \"%s\"\n", pgm, srcFile);
|
|
exit(1);
|
|
} /* copy secondary file (DOS) */
|
|
}
|
|
}
|
|
|
|
if (opts.copyShell)
|
|
{
|
|
if (opts.verbose)
|
|
printf("Copying shell (command interpreter)...\n");
|
|
|
|
/* copy command.com, 1st try source path, then try %COMSPEC% */
|
|
sprintf(srcFile, "%s%s", opts.srcDrive, (opts.fnCmd)?opts.fnCmd:"COMMAND.COM");
|
|
if (!copy(&opts, srcFile, opts.dstDrive, "COMMAND.COM"))
|
|
{
|
|
char *comspec = getenv("COMSPEC");
|
|
if (!opts.fnCmd && (comspec != NULL))
|
|
printf("%s: Trying shell from %%COMSPEC%%=\"%s\"\n", pgm, comspec);
|
|
if (opts.fnCmd || (comspec == NULL) || !copy(&opts, comspec, opts.dstDrive, "COMMAND.COM"))
|
|
{
|
|
printf("\n%s: failed to find command interpreter (shell) file %s\n", pgm, (opts.fnCmd)?opts.fnCmd:"COMMAND.COM");
|
|
exit(1);
|
|
}
|
|
} /* copy shell */
|
|
}
|
|
|
|
if (opts.verbose)
|
|
printf("\n");
|
|
printf("System transferred.\n\n");
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DDEBUG
|
|
VOID dump_sector(unsigned char far * sec)
|
|
{
|
|
COUNT x, y;
|
|
char c;
|
|
|
|
for (x = 0; x < 32; x++)
|
|
{
|
|
printf("%03X ", x * 16);
|
|
for (y = 0; y < 16; y++)
|
|
{
|
|
printf("%02X ", sec[x * 16 + y]);
|
|
}
|
|
for (y = 0; y < 16; y++)
|
|
{
|
|
c = sec[x * 16 + y];
|
|
if (isprint(c))
|
|
printf("%c", c);
|
|
else
|
|
printf(".");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#ifdef __WATCOMC__
|
|
|
|
int absread(int DosDrive, int nsects, int foo, void *diskReadPacket);
|
|
#pragma aux absread = \
|
|
"push bp" \
|
|
"int 0x25" \
|
|
"sbb ax, ax" \
|
|
"popf" \
|
|
"pop bp" \
|
|
__parm [__ax] [__cx] [__dx] [__bx] \
|
|
__modify [__si __di] \
|
|
__value [__ax];
|
|
|
|
int abswrite(int DosDrive, int nsects, int foo, void *diskReadPacket);
|
|
#pragma aux abswrite = \
|
|
"push bp" \
|
|
"int 0x26" \
|
|
"sbb ax, ax" \
|
|
"popf" \
|
|
"pop bp" \
|
|
__parm [__ax] [__cx] [__dx] [__bx] \
|
|
__modify [__si __di] \
|
|
__value [__ax];
|
|
|
|
int fat32readwrite(int DosDrive, void *diskReadPacket, unsigned intno);
|
|
#pragma aux fat32readwrite = \
|
|
"mov ax, 0x7305" \
|
|
"mov cx, 0xffff" \
|
|
"int 0x21" \
|
|
"sbb ax, ax" \
|
|
__parm [__dx] [__bx] [__si] \
|
|
__modify [__cx __dx __si] \
|
|
__value [__ax];
|
|
|
|
void reset_drive(int DosDrive);
|
|
#pragma aux reset_drive = \
|
|
"push ds" \
|
|
"inc dx" \
|
|
"mov ah, 0xd" \
|
|
"int 0x21" \
|
|
"mov ah,0x32" \
|
|
"int 0x21" \
|
|
"pop ds" \
|
|
__parm [__dx] \
|
|
__modify [__ax __bx];
|
|
|
|
void truename(char far *dest, const char *src);
|
|
#pragma aux truename = \
|
|
"mov ah,0x60" \
|
|
"int 0x21" \
|
|
__parm [__es __di] [__si];
|
|
|
|
int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par);
|
|
#pragma aux generic_block_ioctl = \
|
|
"mov ax, 0x440d" \
|
|
"int 0x21" \
|
|
"sbb ax, ax" \
|
|
__value [__ax] \
|
|
__parm [__bx] [__cx] [__dx]; /* BH must be 0 for lock! */
|
|
|
|
#else
|
|
|
|
#ifndef __TURBOC__
|
|
|
|
int int2526readwrite(int DosDrive, void *diskReadPacket, unsigned intno)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.h.al = (BYTE) DosDrive;
|
|
regs.x.bx = (short)diskReadPacket;
|
|
regs.x.cx = 0xffff;
|
|
|
|
int86(intno, ®s, ®s);
|
|
|
|
return regs.x.cflag;
|
|
}
|
|
|
|
#define absread(DosDrive, foo, cx, diskReadPacket) \
|
|
int2526readwrite(DosDrive, diskReadPacket, 0x25)
|
|
|
|
#define abswrite(DosDrive, foo, cx, diskReadPacket) \
|
|
int2526readwrite(DosDrive, diskReadPacket, 0x26)
|
|
|
|
#endif
|
|
|
|
int fat32readwrite(int DosDrive, void *diskReadPacket, unsigned intno)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.x.ax = 0x7305;
|
|
regs.h.dl = DosDrive;
|
|
regs.x.bx = (short)diskReadPacket;
|
|
regs.x.cx = 0xffff;
|
|
regs.x.si = intno;
|
|
intdos(®s, ®s);
|
|
|
|
return regs.x.cflag;
|
|
} /* fat32readwrite */
|
|
|
|
void reset_drive(int DosDrive)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.h.ah = 0xd;
|
|
intdos(®s, ®s);
|
|
regs.h.ah = 0x32;
|
|
regs.h.dl = DosDrive + 1;
|
|
intdos(®s, ®s);
|
|
} /* reset_drive */
|
|
|
|
int generic_block_ioctl(unsigned drive, unsigned cx, unsigned char *par)
|
|
{
|
|
union REGS regs;
|
|
|
|
regs.x.ax = 0x440d;
|
|
regs.x.cx = cx;
|
|
regs.x.dx = (unsigned)par;
|
|
regs.x.bx = drive; /* BH must be 0 for lock! */
|
|
intdos(®s, ®s);
|
|
return regs.x.cflag;
|
|
} /* generic_block_ioctl */
|
|
|
|
void truename(char *dest, const char *src)
|
|
{
|
|
union REGS regs;
|
|
struct SREGS sregs;
|
|
|
|
regs.h.ah = 0x60;
|
|
sregs.es = FP_SEG(dest);
|
|
regs.x.di = FP_OFF(dest);
|
|
sregs.ds = FP_SEG(src);
|
|
regs.x.si = FP_OFF(src);
|
|
intdosx(®s, ®s, &sregs);
|
|
} /* truename */
|
|
|
|
#endif
|
|
|
|
int MyAbsReadWrite(int DosDrive, int count, ULONG sector, void *buffer,
|
|
int write)
|
|
{
|
|
struct {
|
|
unsigned long sectorNumber;
|
|
unsigned short count;
|
|
void far *address;
|
|
} diskReadPacket;
|
|
|
|
diskReadPacket.sectorNumber = sector;
|
|
diskReadPacket.count = count;
|
|
diskReadPacket.address = buffer;
|
|
|
|
if ((!write && absread(DosDrive, -1, -1, &diskReadPacket) == -1)
|
|
|| (write && abswrite(DosDrive, -1, -1, &diskReadPacket) == -1))
|
|
{
|
|
#ifdef WITHFAT32
|
|
return fat32readwrite(DosDrive + 1, &diskReadPacket, write);
|
|
#else
|
|
return 0xff;
|
|
#endif
|
|
}
|
|
return 0;
|
|
} /* MyAbsReadWrite */
|
|
|
|
#ifdef __WATCOMC__
|
|
|
|
unsigned getextdrivespace(void far *drivename, void *buf, unsigned buf_size);
|
|
#pragma aux getextdrivespace = \
|
|
"mov ax, 0x7303" \
|
|
"stc" \
|
|
"int 0x21" \
|
|
"sbb ax, ax" \
|
|
__parm [__es __dx] [__di] [__cx] \
|
|
__value [__ax];
|
|
|
|
#else /* !defined __WATCOMC__ */
|
|
|
|
unsigned getextdrivespace(void *drivename, void *buf, unsigned buf_size)
|
|
{
|
|
union REGS regs;
|
|
struct SREGS sregs;
|
|
|
|
regs.x.ax = 0x7303; /* get extended drive free space */
|
|
|
|
sregs.es = FP_SEG(buf);
|
|
regs.x.di = FP_OFF(buf);
|
|
sregs.ds = FP_SEG(drivename);
|
|
regs.x.dx = FP_OFF(drivename);
|
|
|
|
regs.x.cx = buf_size;
|
|
|
|
intdosx(®s, ®s, &sregs);
|
|
return regs.x.ax == 0x7300 || regs.x.cflag;
|
|
} /* getextdrivespace */
|
|
|
|
#endif /* defined __WATCOMC__ */
|
|
|
|
#ifdef __WATCOMC__
|
|
/*
|
|
* If BIOS has got LBA extensions, after the Int 13h call BX will be 0xAA55.
|
|
* If extended disk access functions are supported, bit 0 of CX will be set.
|
|
*/
|
|
BOOL haveLBA(void); /* return TRUE if we have LBA BIOS, FALSE otherwise */
|
|
#pragma aux haveLBA = \
|
|
"mov ax, 0x4100" /* IBM/MS Int 13h Extensions - installation check */ \
|
|
"mov bx, 0x55AA" \
|
|
"mov dl, 0x80" \
|
|
"int 0x13" \
|
|
"xor ax, ax" \
|
|
"cmp bx, 0xAA55" \
|
|
"jne quit" \
|
|
"and cx, 1" \
|
|
"xchg cx, ax" \
|
|
"quit:" \
|
|
__modify [__bx __cx __dx] \
|
|
__value [__ax];
|
|
#else
|
|
|
|
BOOL haveLBA(void)
|
|
{
|
|
union REGS r;
|
|
r.x.ax = 0x4100;
|
|
r.x.bx = 0x55AA;
|
|
r.h.dl = 0x80;
|
|
int86(0x13, &r, &r);
|
|
return r.x.bx == 0xAA55 && r.x.cx & 1;
|
|
}
|
|
#endif
|
|
|
|
void correct_bpb(struct bootsectortype *default_bpb,
|
|
struct bootsectortype *oldboot, BOOL verbose)
|
|
{
|
|
/* don't touch partitions (floppies most likely) that don't have hidden
|
|
sectors */
|
|
if (default_bpb->bsHiddenSecs == 0)
|
|
return;
|
|
|
|
if (verbose)
|
|
{
|
|
printf("Old boot sector values: sectors/track: %u, heads: %u, hidden: %lu\n",
|
|
oldboot->bsSecPerTrack, oldboot->bsHeads, oldboot->bsHiddenSecs);
|
|
printf("Default and new boot sector values: sectors/track: %u, heads: %u, "
|
|
"hidden: %lu\n", default_bpb->bsSecPerTrack, default_bpb->bsHeads,
|
|
default_bpb->bsHiddenSecs);
|
|
}
|
|
|
|
oldboot->bsSecPerTrack = default_bpb->bsSecPerTrack;
|
|
oldboot->bsHeads = default_bpb->bsHeads;
|
|
oldboot->bsHiddenSecs = default_bpb->bsHiddenSecs;
|
|
}
|
|
|
|
|
|
/* reads in boot sector (1st SEC_SIZE bytes) from file */
|
|
void readBS(const char *bsFile, UBYTE *bootsector)
|
|
{
|
|
if (bsFile != NULL)
|
|
{
|
|
int fd;
|
|
|
|
#ifdef DEBUG
|
|
printf("reading bootsector from file %s\n", bsFile);
|
|
#endif
|
|
|
|
/* open boot sector file, it must exists, then overwrite
|
|
drive with 1st SEC_SIZE bytes from the [image] file
|
|
*/
|
|
if ((fd = open(bsFile, O_RDONLY | O_BINARY)) < 0)
|
|
{
|
|
printf("%s: can't open\"%s\"\nDOS errnum %d", pgm, bsFile, errno);
|
|
exit(1);
|
|
}
|
|
if (read(fd, bootsector, SEC_SIZE) != SEC_SIZE)
|
|
{
|
|
printf("%s: failed to read %u bytes from %s\n", pgm, SEC_SIZE, bsFile);
|
|
close(fd);
|
|
exit(1);
|
|
}
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
/* write bs in bsFile to drive's boot record unmodified */
|
|
void restoreBS(const char *bsFile, int drive)
|
|
{
|
|
UBYTE bootsector[SEC_SIZE];
|
|
|
|
if (bsFile == NULL)
|
|
{
|
|
printf("%s: missing filename of boot sector to restore\n", pgm);
|
|
exit(1);
|
|
}
|
|
|
|
readBS(bsFile, bootsector);
|
|
|
|
/* lock drive */
|
|
generic_block_ioctl(drive + 1, 0x84a, NULL);
|
|
|
|
reset_drive(drive);
|
|
|
|
/* write bootsector to drive */
|
|
if (MyAbsReadWrite(drive, 1, 0, bootsector, 1) != 0)
|
|
{
|
|
printf("%s: failed to write boot sector to drive %c:\n", pgm, drive + 'A');
|
|
exit(1);
|
|
}
|
|
|
|
reset_drive(drive);
|
|
|
|
/* unlock_drive */
|
|
generic_block_ioctl(drive + 1, 0x86a, NULL);
|
|
}
|
|
|
|
/* write bootsector to file bsFile */
|
|
void saveBS(const char *bsFile, UBYTE *bootsector)
|
|
{
|
|
if (bsFile != NULL)
|
|
{
|
|
int fd;
|
|
|
|
#ifdef DEBUG
|
|
printf("writing bootsector to file %s\n", bsFile);
|
|
#endif
|
|
|
|
/* open boot sector file, create it if not exists,
|
|
but don't truncate if exists so we can replace
|
|
1st SEC_SIZE bytes of an image file
|
|
*/
|
|
if ((fd = open(bsFile, O_WRONLY | O_CREAT | O_BINARY,
|
|
S_IREAD | S_IWRITE)) < 0)
|
|
{
|
|
printf("%s: can't create\"%s\"\nDOS errnum %d", pgm, bsFile, errno);
|
|
exit(1);
|
|
}
|
|
if (write(fd, bootsector, SEC_SIZE) != SEC_SIZE)
|
|
{
|
|
printf("%s: failed to write %u bytes to %s\n", pgm, SEC_SIZE, bsFile);
|
|
close(fd);
|
|
/* unlink(bsFile); don't delete in case was image */
|
|
exit(1);
|
|
}
|
|
close(fd);
|
|
} /* if write boot sector file */
|
|
}
|
|
|
|
/* write drive's boot record unmodified to bsFile */
|
|
void dumpBS(const char *bsFile, int drive)
|
|
{
|
|
UBYTE bootsector[SEC_SIZE];
|
|
|
|
if (bsFile == NULL)
|
|
{
|
|
printf("%s: missing filename to dump boot sector to\n", pgm);
|
|
exit(1);
|
|
}
|
|
|
|
/* lock drive */
|
|
generic_block_ioctl(drive + 1, 0x84a, NULL);
|
|
|
|
reset_drive(drive);
|
|
|
|
/* suggestion: allow reading from a boot sector or image file here */
|
|
if (MyAbsReadWrite(drive, 1, 0, bootsector, 0) != 0)
|
|
{
|
|
printf("%s: failed to read boot sector for drive %c:\n", pgm, drive + 'A');
|
|
exit(1);
|
|
}
|
|
|
|
reset_drive(drive);
|
|
|
|
/* unlock_drive */
|
|
generic_block_ioctl(drive + 1, 0x86a, NULL);
|
|
|
|
saveBS(bsFile, bootsector);
|
|
}
|
|
|
|
|
|
|
|
void put_boot(SYSOptions *opts)
|
|
{
|
|
struct bootsectortype32 *bs32;
|
|
struct bootsectortype *bs;
|
|
UBYTE oldboot[SEC_SIZE], newboot[SEC_SIZE];
|
|
UBYTE default_bpb[0x5c];
|
|
int bsBiosMovOff; /* offset in bs to mov [drive],dl that we NOP out */
|
|
|
|
if (opts->verbose)
|
|
{
|
|
printf("Reading old bootsector from drive %c:\n", opts->dstDrive + 'A');
|
|
}
|
|
|
|
/* lock drive */
|
|
generic_block_ioctl(opts->dstDrive + 1, 0x84a, NULL);
|
|
|
|
reset_drive(opts->dstDrive);
|
|
|
|
/* suggestion: allow reading from a boot sector or image file here */
|
|
if (MyAbsReadWrite(opts->dstDrive, 1, 0, oldboot, 0) != 0)
|
|
{
|
|
printf("%s: can't read old boot sector for drive %c:\n", pgm, opts->dstDrive + 'A');
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef DDEBUG
|
|
printf("Old Boot Sector:\n");
|
|
dump_sector(oldboot);
|
|
#endif
|
|
|
|
/* backup original boot sector when requested */
|
|
if (opts->bsFileOrig)
|
|
{
|
|
printf("Backing up original boot sector to %s\n", opts->bsFileOrig);
|
|
saveBS(opts->bsFileOrig, oldboot);
|
|
}
|
|
|
|
bs = (struct bootsectortype *)oldboot;
|
|
|
|
if (bs->bsBytesPerSec != SEC_SIZE)
|
|
{
|
|
printf("Sector size is not 512 but %d bytes - not currently supported!\n",
|
|
bs->bsBytesPerSec);
|
|
exit(1); /* Japan?! */
|
|
}
|
|
|
|
{
|
|
/* see "FAT: General Overview of On-Disk Format" v1.02, 5.V.1999
|
|
* (http://www.nondot.org/sabre/os/files/FileSystems/FatFormat.pdf)
|
|
*/
|
|
ULONG fatSize, totalSectors, dataSectors, clusters;
|
|
UCOUNT rootDirSectors;
|
|
|
|
bs32 = (struct bootsectortype32 *)bs;
|
|
rootDirSectors = (bs->bsRootDirEnts * DIRENT_SIZE /* 32 */
|
|
+ bs32->bsBytesPerSec - 1) / bs32->bsBytesPerSec;
|
|
fatSize = bs32->bsFATsecs ? bs32->bsFATsecs : bs32->bsBigFatSize;
|
|
totalSectors = bs32->bsSectors ? bs32->bsSectors : bs32->bsHugeSectors;
|
|
dataSectors = totalSectors
|
|
- bs32->bsResSectors - (bs32->bsFATs * fatSize) - rootDirSectors;
|
|
clusters = dataSectors / (((bs32->bsSecPerClust - 1) & 0xFF) + 1);
|
|
|
|
if (bs32->bsFATsecs == 0) {
|
|
if (clusters >= 0xFFFfff5UL) { /* FAT32 has 28 significant bits */
|
|
printf("Too many clusters (%lXh) for FAT32 file system!\n", clusters);
|
|
exit(1);
|
|
}
|
|
fs = FAT32;
|
|
if (clusters < FAT_MAGIC16)
|
|
smallfat32 = 1;
|
|
} else {
|
|
if (clusters < FAT_MAGIC) /* < 4085 */
|
|
fs = FAT12;
|
|
else if (clusters < FAT_MAGIC16) /* < 65525 */
|
|
fs = FAT16;
|
|
else {
|
|
printf("Too many clusters (%lXh) for non-FAT32 file system!\n", clusters);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* bit 0 set if function to use current BPB, clear if Device
|
|
BIOS Parameter Block field contains new default BPB
|
|
bit 1 set if function to use track layout fields only
|
|
must be clear if CL=60h
|
|
bit 2 set if all sectors in track same size (should be set) (RBIL) */
|
|
default_bpb[0] = 4;
|
|
|
|
if (fs == FAT32)
|
|
{
|
|
if (opts->verbose)
|
|
printf("FAT type: FAT32%s\n", smallfat32 ? " (small)" : "");
|
|
/* get default bpb (but not for floppies) */
|
|
if (opts->dstDrive >= 2 &&
|
|
generic_block_ioctl(opts->dstDrive + 1, 0x4860, default_bpb) == 0)
|
|
correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs, opts->verbose);
|
|
|
|
#ifdef WITHFAT32 /* copy one of the FAT32 boot sectors */
|
|
if (!opts->kernel.stdbs) /* MS/PC DOS compatible BS requested */
|
|
{
|
|
printf("%s: FAT32 versions of PC/MS DOS compatible boot sectors\n"
|
|
"are not supported.\n", pgm);
|
|
exit(1);
|
|
}
|
|
|
|
/* user may force explicity lba or chs, otherwise base on if LBA available */
|
|
if ((opts->force==LBA) || ((opts->force==AUTO) && haveLBA()))
|
|
memcpy(newboot, fat32lba, SEC_SIZE);
|
|
else /* either auto mode & no LBA detected or forced CHS */
|
|
memcpy(newboot, fat32chs, SEC_SIZE);
|
|
#else
|
|
printf("SYS hasn't been compiled with FAT32 support.\n"
|
|
"Consider using -DWITHFAT32 option.\n");
|
|
exit(1);
|
|
#endif
|
|
}
|
|
else
|
|
{ /* copy the FAT12/16 CHS+LBA boot sector */
|
|
if (opts->verbose)
|
|
printf("FAT type: FAT1%c\n", fs + '0' - 10);
|
|
if (opts->dstDrive >= 2 &&
|
|
generic_block_ioctl(opts->dstDrive + 1, 0x860, default_bpb) == 0)
|
|
correct_bpb((struct bootsectortype *)(default_bpb + 7 - 11), bs, opts->verbose);
|
|
|
|
if (opts->kernel.stdbs)
|
|
{
|
|
/* copy over appropriate boot sector, FAT12 or FAT16 */
|
|
memcpy(newboot, (fs == FAT16) ? fat16com : fat12com, SEC_SIZE);
|
|
|
|
/* !!! if boot sector changes then update these locations !!! */
|
|
{
|
|
/* magic offset: LBA detection */
|
|
unsigned offset;
|
|
offset = (fs == FAT16) ? 0x178 : 0x17B;
|
|
|
|
if ( (newboot[offset]==0x84) && (newboot[offset+1]==0xD2) ) /* test dl,dl */
|
|
{
|
|
/* if always use LBA then NOP out conditional jmp over LBA logic if A: */
|
|
if (opts->force==LBA)
|
|
{
|
|
offset+=2; /* jz */
|
|
newboot[offset] = 0x90; /* NOP */ ++offset;
|
|
newboot[offset] = 0x90; /* NOP */
|
|
}
|
|
else if (opts->force==CHS) /* if force CHS then always skip LBA logic */
|
|
{
|
|
newboot[offset] = 0x30; /* XOR */
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("%s: Internal error: FAT1%c LBA detect unexpected content\n",
|
|
pgm, fs == FAT12 ? '2' : '6');
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef WITHOEMCOMPATBS
|
|
printf("Using OEM (PC/MS-DOS) compatible boot sector.\n");
|
|
memcpy(newboot, (fs == FAT16) ? oemfat16 : oemfat12, SEC_SIZE);
|
|
#else
|
|
printf("Internal Error: no OEM compatible boot sector!\n");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Copy disk parameter from old sector to new sector */
|
|
#ifdef WITHFAT32
|
|
if (fs == FAT32)
|
|
memcpy(&newboot[SBOFFSET], &oldboot[SBOFFSET], SBSIZE32);
|
|
else
|
|
#endif
|
|
memcpy(&newboot[SBOFFSET], &oldboot[SBOFFSET], SBSIZE);
|
|
|
|
bs = (struct bootsectortype *)newboot;
|
|
|
|
/* originally OemName was "FreeDOS", changed for better compatibility */
|
|
memcpy(bs->OemName, "FRDOS5.1", 8); /* Win9x seems to require
|
|
5 uppercase letters,
|
|
digit(4 or 5) dot digit */
|
|
|
|
#ifdef WITHFAT32
|
|
if (fs == FAT32)
|
|
{
|
|
bs32 = (struct bootsectortype32 *)newboot;
|
|
/* ensure appears valid, if not then force valid */
|
|
if ((bs32->bsBackupBoot < 1) || (bs32->bsBackupBoot > bs32->bsResSectors))
|
|
{
|
|
if (opts->verbose)
|
|
printf("BPB appears to have invalid backup boot sector #, forcing to default.\n");
|
|
bs32->bsBackupBoot = 0x6; /* ensure set, 6 is MS defined bs size */
|
|
}
|
|
bs32->bsDriveNumber = opts->defBootDrive;
|
|
|
|
/* the location of the "0060" segment portion of the far pointer
|
|
in the boot sector is just before cont: in boot*.asm.
|
|
This happens to be offset 0x78 for FAT32 and offset 0x5c for FAT16
|
|
|
|
force use of value stored in bs by NOPping out mov [drive], dl
|
|
0x82: 88h,56h,40h for fat32 chs & lba boot sectors
|
|
|
|
i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!!
|
|
*/
|
|
if (opts->kernel.stdbs)
|
|
{
|
|
/* magic offset: loadsegoff_60 */
|
|
int defaultload = *(int *)(&newboot[0x78]);
|
|
if (defaultload != 0x60 && defaultload != 0x70) {
|
|
printf("%s: Internal error: FAT32 load seg unexpected content\n", pgm);
|
|
exit(1);
|
|
}
|
|
*(int *)(&newboot[0x78]) = opts->kernel.loadaddr;
|
|
bsBiosMovOff = 0x82; /* magic offset: mov byte [bp + 40h], dl */
|
|
}
|
|
else /* compatible bs */
|
|
{
|
|
printf("%s: INTERNAL ERROR: how did you get here?\n", pgm);
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
printf(" FAT starts at sector %lx + %x\n",
|
|
bs32->bsHiddenSecs, bs32->bsResSectors);
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
|
|
/* establish default BIOS drive # set in boot sector */
|
|
bs->bsDriveNumber = opts->defBootDrive;
|
|
|
|
/* the location of the "0060" segment portion of the far pointer
|
|
in the boot sector is just before cont: in boot*.asm.
|
|
This happens to be offset 0x78 for FAT32 and offset 0x5c for FAT16
|
|
The oem boot sectors do not have/need this value for patching.
|
|
|
|
the location of the jmp address (patching from
|
|
EA00007000 [jmp 0x0070:0000] to EA00207000 [jmp 0x0070:0200])
|
|
0x11b: for fat12 oem boot sector
|
|
0x118: for fat16 oem boot sector
|
|
The standard boot sectors do not have/need this value patched.
|
|
|
|
force use of value stored in bs by NOPping out mov [drive], dl
|
|
0x66: 88h,56h,24h for fat16 and fat12 boot sectors
|
|
0x4F: 88h,56h,24h for oem compatible fat16 and fat12 boot sectors
|
|
|
|
i.e. BE CAREFUL WHEN YOU CHANGE THE BOOT SECTORS !!!
|
|
*/
|
|
if (opts->kernel.stdbs)
|
|
{
|
|
/* magic offset: loadsegoff_60 */
|
|
int defaultload = *(int *)(&newboot[0x5C]);
|
|
if (defaultload != 0x60 && defaultload != 0x70) {
|
|
printf("%s: Internal error: FAT1%c load seg unexpected content\n",
|
|
pgm, fs == FAT12 ? '2' : '6');
|
|
exit(1);
|
|
}
|
|
/* this sets the segment we load the kernel to, default is 0x60:0 */
|
|
*(int *)(&newboot[0x5C]) = opts->kernel.loadaddr;
|
|
bsBiosMovOff = 0x66; /* magic offset: mov byte [bp + 24h], dl */
|
|
}
|
|
else
|
|
{
|
|
int defaultload;
|
|
/* load segment hard coded to 0x70 in oem compatible boot sector, */
|
|
/* this however changes the offset jumped to default 0x70:0 */
|
|
if (fs == FAT12) {
|
|
/* magic offset: jmp LOADSEG:xxxxh */
|
|
defaultload = *(int *)(&newboot[0x11A]);
|
|
*(int *)(&newboot[0x11A]) = opts->kernel.loadaddr;
|
|
} else {
|
|
/* magic offset: jmp LOADSEG:xxxxh */
|
|
defaultload = *(int *)(&newboot[0x118]);
|
|
*(int *)(&newboot[0x118]) = opts->kernel.loadaddr;
|
|
}
|
|
if (defaultload != 0x0 && defaultload != 0x200) {
|
|
printf("%s: Internal error: OEM FAT1%c load ofs unexpected content\n",
|
|
pgm, fs == FAT12 ? '2' : '6');
|
|
exit(1);
|
|
}
|
|
bsBiosMovOff = 0x4F; /* magic offset: mov byte [bp + 24h], dl */
|
|
}
|
|
}
|
|
|
|
if ( (newboot[bsBiosMovOff]==0x88) && (newboot[bsBiosMovOff+1]==0x56) )
|
|
{
|
|
if (opts->ignoreBIOS)
|
|
{
|
|
newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff;
|
|
newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff;
|
|
newboot[bsBiosMovOff] = 0x90; /* NOP */ ++bsBiosMovOff;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("%s: Internal error: Unit save unexpected content\n", pgm);
|
|
exit(1);
|
|
}
|
|
|
|
if (opts->verbose) /* display information about filesystem */
|
|
{
|
|
printf("Root dir entries = %u\n", bs->bsRootDirEnts);
|
|
|
|
printf("FAT starts at sector (%lu + %u)\n",
|
|
bs->bsHiddenSecs, bs->bsResSectors);
|
|
printf("Root directory starts at sector (PREVIOUS + %u * %u)\n",
|
|
bs->bsFATsecs, bs->bsFATs);
|
|
}
|
|
|
|
{
|
|
int i = 0;
|
|
/* magic offset: (first) kernel filename */
|
|
memset(&newboot[0x1f1], ' ', 11);
|
|
while (opts->kernel.kernel[i] && opts->kernel.kernel[i] != '.')
|
|
{
|
|
if (i < 8)
|
|
newboot[0x1f1+i] = toupper(opts->kernel.kernel[i]);
|
|
i++;
|
|
}
|
|
if (opts->kernel.kernel[i] == '.')
|
|
{
|
|
/* copy extension */
|
|
int j = 0;
|
|
i++;
|
|
while (opts->kernel.kernel[i+j] && j < 3)
|
|
{
|
|
newboot[0x1f9+j] = toupper(opts->kernel.kernel[i+j]);
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (opts->verbose)
|
|
{
|
|
/* there's a zero past the kernel name in all boot sectors */
|
|
printf("Boot sector kernel name set to %s\n", &newboot[0x1f1]);
|
|
if (opts->kernel.stdbs)
|
|
printf("Boot sector kernel load segment set to %X:0h\n", opts->kernel.loadaddr);
|
|
else
|
|
printf("Boot sector kernel jmp address set to 70:%Xh\n", opts->kernel.loadaddr);
|
|
}
|
|
|
|
#ifdef DDEBUG
|
|
printf("\nNew Boot Sector:\n");
|
|
dump_sector(newboot);
|
|
#endif
|
|
|
|
if (opts->writeBS)
|
|
{
|
|
#ifdef DEBUG
|
|
printf("Writing new bootsector to drive %c:\n", opts->dstDrive + 'A');
|
|
#endif
|
|
|
|
/* write newboot to a drive */
|
|
if (MyAbsReadWrite(opts->dstDrive, 1, 0, newboot, 1) != 0)
|
|
{
|
|
printf("Can't write new boot sector to drive %c:\n", opts->dstDrive + 'A');
|
|
exit(1);
|
|
}
|
|
|
|
/* for FAT32, we need to update the backup copy as well */
|
|
/* unless user has asked us not to, eg for better dual boot support */
|
|
/* Note: assuming sectors 1-5 & 7-11 (FSINFO+additional boot code)
|
|
are properly setup by prior format and need no modification
|
|
[technically freespace, etc. should be updated]
|
|
*/
|
|
if ((fs == FAT32) && !opts->skipBakBSCopy)
|
|
{
|
|
bs32 = (struct bootsectortype32 *)newboot;
|
|
#ifdef DEBUG
|
|
printf("writing backup bootsector to sector %d\n", bs32->bsBackupBoot);
|
|
#endif
|
|
if (MyAbsReadWrite(opts->dstDrive, 1, bs32->bsBackupBoot, newboot, 1) != 0)
|
|
{
|
|
printf("Can't write backup boot sector to drive %c:\n", opts->dstDrive + 'A');
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
} /* if write boot sector to boot record*/
|
|
|
|
if (opts->bsFile != NULL)
|
|
{
|
|
if (opts->verbose)
|
|
printf("writing new bootsector to file %s\n", opts->bsFile);
|
|
|
|
saveBS(opts->bsFile, newboot);
|
|
} /* if write boot sector to file*/
|
|
|
|
reset_drive(opts->dstDrive);
|
|
|
|
/* unlock_drive */
|
|
generic_block_ioctl(opts->dstDrive + 1, 0x86a, NULL);
|
|
} /* put_boot */
|
|
|
|
|
|
/*
|
|
* Returns TRUE if `drive` has at least `bytes` free space, FALSE otherwise.
|
|
* put_sector() must have been already called to determine file system type.
|
|
*/
|
|
BOOL check_space(COUNT drive, ULONG bytes)
|
|
{
|
|
#ifdef WITHFAT32
|
|
if (fs == FAT32)
|
|
{
|
|
char *drivename = "A:\\";
|
|
drivename[0] = 'A' + drive;
|
|
getextdrivespace(drivename, &x, sizeof(x));
|
|
return x.xfs_freeclusters > (bytes / (x.xfs_clussize * x.xfs_secsize));
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef __TURBOC__
|
|
struct dfree df;
|
|
getdfree(drive + 1, &df);
|
|
return (ULONG)df.df_avail * df.df_sclus * df.df_bsec >= bytes;
|
|
#else
|
|
struct _diskfree_t df;
|
|
_dos_getdiskfree(drive + 1, &df);
|
|
return (ULONG)df.avail_clusters * df.sectors_per_cluster
|
|
* df.bytes_per_sector >= bytes;
|
|
#endif
|
|
}
|
|
} /* check_space */
|
|
|
|
|
|
BYTE copybuffer[COPY_SIZE];
|
|
|
|
/* allocate memory from DOS, return 0 on success, nonzero otherwise */
|
|
int alloc_dos_mem(ULONG memsize, UWORD *theseg)
|
|
{
|
|
unsigned dseg;
|
|
#ifdef __TURBOC__
|
|
if (allocmem((unsigned)((memsize+15)>>4), &dseg)!=-1)
|
|
#else
|
|
if (_dos_allocmem((unsigned)((memsize+15)>>4), &dseg)!=0)
|
|
#endif
|
|
return -1; /* failed to allocate memory */
|
|
|
|
*theseg = (UWORD)dseg;
|
|
return 0; /* success */
|
|
}
|
|
#ifdef __TURBOC__
|
|
#define dos_freemem freemem
|
|
#else
|
|
#define dos_freemem _dos_freemem
|
|
#endif
|
|
|
|
/* copies file (path+filename specified by srcFile) to drive:\filename */
|
|
BOOL copy(SYSOptions *opts, const BYTE *source, COUNT drive, const BYTE * filename)
|
|
{
|
|
static BYTE src[SYS_MAXPATH];
|
|
static BYTE dest[SYS_MAXPATH];
|
|
unsigned ret;
|
|
int fdin, fdout;
|
|
ULONG copied = 0;
|
|
|
|
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
|
|
#if defined(__WATCOMC__) && __WATCOMC__ < 1280
|
|
unsigned short date, time;
|
|
#else
|
|
unsigned date, time;
|
|
#endif
|
|
#elif defined __TURBOC__
|
|
struct ftime ftime;
|
|
#endif
|
|
|
|
if (opts->verbose)
|
|
printf("Copying %s...\n", source);
|
|
|
|
truename(src, source);
|
|
sprintf(dest, "%c:\\%s", 'A' + drive, filename);
|
|
if (stricmp(src, dest) == 0)
|
|
{
|
|
if (opts->verbose)
|
|
printf("%s: source and destination are identical: skipping \"%s\"\n",
|
|
pgm, source);
|
|
return TRUE;
|
|
}
|
|
|
|
if ((fdin = open(source, O_RDONLY | O_BINARY)) < 0)
|
|
{
|
|
printf("%s: failed to open \"%s\"\n", pgm, source);
|
|
return FALSE;
|
|
}
|
|
|
|
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
|
|
_dos_getftime(fdin, &date, &time);
|
|
#elif defined __TURBOC__
|
|
getftime(fdin, &ftime);
|
|
#endif
|
|
|
|
if (!check_space(drive, filelength(fdin)))
|
|
{
|
|
printf("%s: Not enough space to transfer %s\n", pgm, filename);
|
|
close(fdin);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((fdout =
|
|
open(dest, O_RDWR | O_TRUNC | O_CREAT | O_BINARY,
|
|
S_IREAD | S_IWRITE)) < 0)
|
|
{
|
|
printf(" %s: can't create\"%s\"\nDOS errnum %d\n", pgm, dest, errno);
|
|
close(fdin);
|
|
return FALSE;
|
|
}
|
|
|
|
#if 0 /* simple copy loop, read chunk then write chunk, repeat until all data copied */
|
|
while ((ret = read(fdin, copybuffer, COPY_SIZE)) > 0)
|
|
{
|
|
if (write(fdout, copybuffer, ret) != ret)
|
|
{
|
|
printf("Can't write %u bytes to %s\n", ret, dest);
|
|
close(fdout);
|
|
unlink(dest);
|
|
return FALSE;
|
|
}
|
|
copied += ret;
|
|
}
|
|
#else /* read in whole file, then write out whole file */
|
|
{
|
|
ULONG filesize;
|
|
UWORD theseg;
|
|
BYTE far *buffer; BYTE far *bufptr;
|
|
UWORD offs;
|
|
unsigned chunk_size;
|
|
|
|
/* get length of file to copy, then allocate enough memory for whole file */
|
|
filesize = filelength(fdin);
|
|
if (alloc_dos_mem(filesize, &theseg)!=0)
|
|
{
|
|
printf("Not enough memory to buffer %lu bytes for %s\n", filesize, source);
|
|
return FALSE;
|
|
}
|
|
bufptr = buffer = MK_FP(theseg, 0);
|
|
|
|
/* read in whole file, a chunk at a time; adjust size of last chunk to match remaining bytes */
|
|
chunk_size = (COPY_SIZE < filesize)?COPY_SIZE:(unsigned)filesize;
|
|
while ((ret = read(fdin, copybuffer, chunk_size)) > 0)
|
|
{
|
|
for (offs = 0; offs < ret; offs++)
|
|
{
|
|
*bufptr = copybuffer[offs];
|
|
bufptr++;
|
|
if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */
|
|
{
|
|
bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000);
|
|
}
|
|
}
|
|
/* keep track of how much read in, and only read in filesize bytes */
|
|
copied += ret;
|
|
chunk_size = (COPY_SIZE < (filesize-copied))?COPY_SIZE:(unsigned)(filesize-copied);
|
|
}
|
|
|
|
/* write out file, a chunk at a time; adjust size of last chunk to match remaining bytes */
|
|
bufptr = buffer;
|
|
copied = 0;
|
|
do
|
|
{
|
|
/* keep track of how much read in, and only read in filesize bytes */
|
|
chunk_size = (COPY_SIZE < (filesize-copied))?COPY_SIZE:(unsigned)(filesize-copied);
|
|
copied += chunk_size;
|
|
|
|
/* setup chunk of data to be written out */
|
|
for (offs = 0; offs < chunk_size; offs++)
|
|
{
|
|
copybuffer[offs] = *bufptr;
|
|
bufptr++;
|
|
if (FP_OFF(bufptr) > 0x7777) /* watcom needs this in tiny model */
|
|
{
|
|
bufptr = MK_FP(FP_SEG(bufptr)+0x700, FP_OFF(bufptr)-0x7000);
|
|
}
|
|
}
|
|
|
|
/* write the data to disk, abort on any error */
|
|
if (write(fdout, copybuffer, chunk_size) != chunk_size)
|
|
{
|
|
printf("Can't write %u bytes to %s\n", ret, dest);
|
|
close(fdout);
|
|
unlink(dest);
|
|
return FALSE;
|
|
}
|
|
} while (copied < filesize);
|
|
|
|
dos_freemem(theseg);
|
|
}
|
|
#endif
|
|
|
|
#if defined __WATCOMC__ || defined _MSC_VER /* || defined __BORLANDC__ */
|
|
_dos_setftime(fdout, date, time);
|
|
#elif defined __TURBOC__
|
|
setftime(fdout, &ftime);
|
|
#endif
|
|
|
|
/* reduce disk swap on single drives, close file on drive last accessed 1st */
|
|
close(fdout);
|
|
|
|
#ifdef __SOME_OTHER_COMPILER__
|
|
{
|
|
#include <utime.h>
|
|
struct utimbuf utimb;
|
|
|
|
utimb.actime = /* access time */
|
|
utimb.modtime = fstatbuf.st_mtime; /* modification time */
|
|
utime(dest, &utimb);
|
|
};
|
|
#endif
|
|
|
|
/* and close input file, usually same drive as next action will access */
|
|
close(fdin);
|
|
|
|
if (opts->verbose)
|
|
printf("%lu Bytes transferred\n", copied);
|
|
|
|
return TRUE;
|
|
} /* copy */
|
|
|