FreeDOS/kernel/main.c
2004-07-25 09:55:58 +00:00

710 lines
20 KiB
C

/****************************************************************/
/* */
/* main.c */
/* DOS-C */
/* */
/* Main Kernel Functions */
/* */
/* Copyright (c) 1995, 1996 */
/* 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, Inc., */
/* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */
/****************************************************************/
#include "portab.h"
#include "init-mod.h"
#include "dyndata.h"
#ifdef VERSION_STRINGS
static BYTE *mainRcsId =
"$Id$";
#endif
static char copyright[] =
"(C) Copyright 1995-2004 Pasquale J. Villani and The FreeDOS Project.\n"
"All Rights Reserved. This is free software and comes with ABSOLUTELY NO\n"
"WARRANTY; you can redistribute it and/or modify it under the terms of the\n"
"GNU General Public License as published by the Free Software Foundation;\n"
"either version 2, or (at your option) any later version.\n";
struct _KernelConfig InitKernelConfig BSS_INIT({0});
STATIC VOID init_internal_devices(void);
STATIC VOID update_dcb(struct dhdr FAR *);
STATIC VOID init_kernel(VOID);
STATIC VOID signon(VOID);
STATIC VOID init_shell(VOID);
STATIC VOID FsConfig(VOID);
STATIC VOID InitPrinters(VOID);
STATIC VOID InitSerialPorts(VOID);
STATIC void CheckContinueBootFromHarddisk(void);
STATIC void setup_int_vectors(void);
#ifdef _MSC_VER
BYTE _acrtused = 0;
__segment DosDataSeg = 0; /* serves for all references to the DOS DATA segment
necessary for MSC+our funny linking model
*/
__segment DosTextSeg = 0;
#endif
struct lol FAR *LoL = &DATASTART;
void ASMCFUNC FreeDOSmain(void)
{
#ifdef _MSC_VER
extern FAR prn_dev;
DosDataSeg = (__segment) & DATASTART;
DosTextSeg = (__segment) & prn_dev;
#endif
/* clear the Init BSS area (what normally the RTL does */
memset(_ib_start, 0, _ib_end - _ib_start);
/* if the kernel has been UPX'ed,
CONFIG info is stored at 50:e2 ..fc
and the bootdrive (passed from BIOS)
at 50:e0
*/
{
UBYTE drv;
UBYTE FAR *p = MK_PTR(UBYTE, 0, 0x5e2);
if (fmemcmp(p, "CONFIG", 6) == 0) /* UPXed */
drv = p[-2]; /* stored there by stub from exeflat.c */
/* !!! stub, added by exeflat.c for UPXed kernel, should store
boot drive# in the CONFIG-block, not outside (below) it. --avb */
else
{
drv = LoL->BootDrive;
/* !!! kernel.asm should store boot drive# from BL into
LowKernelConfig instead LoL->BootDrive. --avb */
p[-2] = drv; /* used in initdisk.c */
/* !!! initdisk.c:ReadAllPartitionTables() should get boot drive#
from InitKernelConfig, not at fixed address 0:5e0. --avb */
p = (UBYTE FAR*)&LowKernelConfig;
}
/* !!! boot drive# should be get from InitKernelConfig after
copying there FAR memory (from 0:5e0 or LowKernelConfig). --avb */
drv++; /* A:=1, B:=2 */
if (drv > 0x80)
drv = 3; /* C: */
LoL->BootDrive = drv;
fmemcpy(&InitKernelConfig, p, sizeof InitKernelConfig);
}
setup_int_vectors();
CheckContinueBootFromHarddisk();
signon();
init_kernel();
init_shell();
init_call_p_0(&Config); /* execute process 0 (the shell) */
}
/*
InitializeAllBPBs()
or MakeNortonDiskEditorHappy()
it has been determined, that FDOS's BPB tables are initialized,
only when used (like DIR H:).
at least one known utility (norton DE) seems to access them directly.
ok, so we access for all drives, that the stuff gets build
*/
void InitializeAllBPBs(VOID)
{
static char filename[] = "A:-@JUNK@-.TMP";
int drive, fileno;
for (drive = 'C'; drive < 'A' + LoL->nblkdev; drive++)
{
filename[0] = drive;
if ((fileno = open(filename, O_RDONLY)) >= 0)
close(fileno);
}
}
STATIC void PSPInit(void)
{
psp _seg *p = MK_SEG_PTR(psp, DOS_PSP);
fmemset(p, 0, sizeof(psp)); /* Clear out new psp first */
/* initialize all entries and exits */
p->ps_exit = 0x20cd; /* CP/M-like exit point: */
/* INT 20 opcode */
/* CP/M-like entry point: */
p->ps_farcall = 0x9a; /* FAR CALL opcode... */
p->ps_reentry = MK_FP(0, 0x30 * 4); /* ...entry address */
p->ps_unix[0] = 0xcd; /* unix style call: */
p->ps_unix[1] = 0x21; /* INT 21/RETF opcodes */
p->ps_unix[2] = 0xcb;
/* parent-child relationships */
/*p->ps_parent = 0;*/ /* parent psp segment */
p->ps_prevpsp = (VFP)-1l; /* previous psp address */
/* Environment and memory useage parameters */
/*p->ps_size = 0;*/ /* segment of memory beyond */
/* memory allocated to program */
/*p->ps_environ = 0;*/ /* environment paragraph */
/*p->ps_isv22 = NULL;*/ /* terminate handler */
/*p->ps_isv23 = NULL;*/ /* break handler */
/*p->ps_isv24 = NULL;*/ /* critical error handler */
/*p->ps_stack = NULL;*/ /* user stack pointer - int 21 */
/* File System parameters */
p->ps_maxfiles = sizeof p->ps_files; /* size of file table */
fmemset(p->ps_filetab = p->ps_files, 0xff, sizeof p->ps_files);
/*p->ps_fcb1.fcb_drive = 0;*/ /* 1st command line argument */
/*fmemset(p->ps_fcb1.fcb_fname, ' ', FNAME_SIZE + FEXT_SIZE);*/
/*p->ps_fcb2.fcb_drive = 0;*/ /* 2nd command line argument */
/*fmemset(p->ps_fcb2.fcb_fname, ' ', FNAME_SIZE + FEXT_SIZE);*/
/* this area reused for master environment */
/*p->ps_cmd.ctCount = 0;*/ /* local command line */
/*p->ps_cmd.ctBuffer[0] = '\r';*/ /* command tail */
/* !!! dirty hack: because bug in old FreeCOM, which wrongly
process empty environment in MS-DOS style, garbage empty
environment by dummy variable: --avb
*/
fmemcpy(&p->ps_cmd, "PATH=.", 6/*strlen("PATH=.")*/);
}
#ifndef __WATCOMC__
/* for WATCOMC we can use the ones in task.c */
intvec getvec(unsigned char intno)
{
intvec iv;
disable();
iv = *(intvec FAR *)MK_FP(0,4 * (intno));
enable();
return iv;
}
void setvec(unsigned char intno, intvec vector)
{
disable();
*(intvec FAR *)MK_FP(0,4 * intno) = vector;
enable();
}
#endif
STATIC void setup_int_vectors(void)
{
static struct vec
{
unsigned char intno;
size_t handleroff;
} vectors[] =
{
/* all of these are in the DOS DS */
{ 0x0, FP_OFF(int0_handler) }, /* zero divide */
{ 0x1, FP_OFF(empty_handler) }, /* single step */
{ 0x3, FP_OFF(empty_handler) }, /* debug breakpoint */
{ 0x6, FP_OFF(int6_handler) }, /* invalid opcode */
{ 0x20, FP_OFF(int20_handler) },
{ 0x21, FP_OFF(int21_handler) },
{ 0x22, FP_OFF(int22_handler) },
{ 0x24, FP_OFF(int24_handler) },
{ 0x25, FP_OFF(low_int25_handler) },
{ 0x26, FP_OFF(low_int26_handler) },
{ 0x27, FP_OFF(int27_handler) },
{ 0x28, FP_OFF(int28_handler) },
{ 0x2a, FP_OFF(int2a_handler) },
{ 0x2f, FP_OFF(int2f_handler) }
};
struct vec *pvec;
int i;
for (i = 0x23; i <= 0x3f; i++)
setvec(i, empty_handler);
for (pvec = vectors; pvec < ENDOF(vectors); pvec++)
setvec(pvec->intno, (intvec)MK_FP(FP_SEG(empty_handler), pvec->handleroff));
pokeb(0, 0x30 * 4, 0xea);
pokel(0, 0x30 * 4 + 1, (ULONG)cpm_entry);
/* these two are in the device driver area LOWTEXT (0x70) */
setvec(0x1b, got_cbreak);
setvec(0x29, int29_handler); /* required for printf! */
}
STATIC void init_kernel(void)
{
COUNT i;
LoL->os_setver_major = LoL->os_major = MAJOR_RELEASE;
LoL->os_setver_minor = LoL->os_minor = MINOR_RELEASE;
/* move kernel to high conventional RAM, just below the init code */
#ifdef __WATCOMC__
lpTop = MK_FP(_CS, 0);
#else
lpTop = MK_FP(_CS - (FP_OFF(_HMATextEnd) + 15) / 16, 0);
#endif
MoveKernel(FP_SEG(lpTop));
/* lpTop should be para-aligned */
lpTop = MK_FP(FP_SEG(lpTop) - 0xfff, 0xfff0);
/* Initialize IO subsystem */
init_internal_devices();
InitPrinters();
InitSerialPorts();
set_DTA(MK_FP(DOS_PSP, 0x80));
PSPInit();
init_PSPSet(DOS_PSP);
Init_clk_driver();
/* Do first initialization of system variable buffers so that */
/* we can read config.sys later. */
LoL->lastdrive = Config.cfgLastdrive;
blk_dev.dh_name[0] = dsk_init();
PreConfig();
/* Number of units */
if (blk_dev.dh_name[0] > 0)
update_dcb(&blk_dev);
/* Now config the temporary file system */
FsConfig();
/* Now process CONFIG.SYS */
DoConfig();
/* Close all (device) files */
for (i = 0; i < 20; i++)
close(i);
/* and do final buffer allocation. */
PostConfig();
/* Init the file system one more time */
FsConfig();
configDone();
InitializeAllBPBs();
DoInstall();
}
STATIC VOID FsConfig(VOID)
{
struct dpb FAR *dpb = LoL->DPBp;
int i;
/* Initialize the current directory structures */
for (i = 0; i < LoL->lastdrive; i++)
{
struct cds FAR *pcds_table = &LoL->CDSp[i];
pcds_table->cdsCurrentPath[0] = 'A' + i;
pcds_table->cdsCurrentPath[1] = ':';
pcds_table->cdsCurrentPath[2] = '\\';
pcds_table->cdsCurrentPath[3] = '\0';
pcds_table->cdsFlags = 0;
if (i < LoL->nblkdev && (LONG) dpb != -1l)
{
pcds_table->cdsDpb = dpb;
pcds_table->cdsFlags = CDSPHYSDRV;
dpb = dpb->dpb_next;
}
pcds_table->cdsStrtClst = 0xffff;
pcds_table->cdsParam = 0xffff;
pcds_table->cdsStoreUData = 0xffff;
pcds_table->cdsJoinOffset = 2;
}
/* Log-in the default drive. */
init_setdrive(LoL->BootDrive - 1);
/* The system file tables need special handling and are "hand */
/* built. Included is the stdin, stdout, stdaux and stdprn. */
/* a little bit of shuffling is necessary for compatibility */
/* sft_idx=0 is /dev/aux */
open("AUX", O_RDWR);
/* handle 1, sft_idx=1 is /dev/con (stdout) */
open("CON", O_RDWR);
/* 3 is /dev/aux */
dup2(STDIN, STDAUX);
/* 0 is /dev/con (stdin) */
dup2(STDOUT, STDIN);
/* 2 is /dev/con (stdin) */
dup2(STDOUT, STDERR);
/* 4 is /dev/prn */
open("PRN", O_WRONLY);
/* Initialize the disk buffer management functions */
/* init_call_init_buffers(); done from CONFIG.C */
}
STATIC VOID signon()
{
printf("\r%S"
"Kernel compatibility %d.%d - "
#if defined(__BORLANDC__)
"BORLANDC"
#elif defined(__TURBOC__)
"TURBOC"
#elif defined(_MSC_VER)
"MSC"
#elif defined(__WATCOMC__)
"WATCOMC"
#elif defined(__GNUC__)
"GNUC" /* this is hypothetical only */
#else
#error Unknown compiler
generate some bullshit error here, as the compiler should be known
#endif
#if defined (I386)
" - 80386 CPU required"
#elif defined (I186)
" - 80186 CPU required"
#endif
#ifdef WITHFAT32
" - FAT32 support"
#endif
"\n\n%s",
MK_FP(FP_SEG(LoL), FP_OFF(LoL->os_release)),
MAJOR_RELEASE, MINOR_RELEASE, copyright);
}
STATIC void init_shell()
{
/* if stepping CONFIG.SYS (F5/F8), tell COMMAND.COM about it */
/* (insert /D, /Y as first argument) */
if (askCommand & (ASK_TRACE | ASK_SKIPALL))
{
PStr p = Config.cfgShell - 1; /* find end of command name */
/* too long line -> truncate it to make space for "/Y \0" */
Config.cfgShell[sizeof Config.cfgShell - 4] = '\0';
do p++; while ((UBYTE)*p > ' ' && *p != '/');
if (*p == ' ' || *p == '\t')
p++; /* place option after space */
{
PStr q = p;
while (*q++); /* find end of command line */
/* shift tail to right by 3 to make room for option */
do
{
q--;
q[3] = q[0];
} while (q > p);
}
p[0] = '/', p[1] = 'Y', p[2] = ' '; /* single step AUTOEXEC */
if (askCommand & ASK_SKIPALL)
p[1] = 'D'; /* disable AUTOEXEC */
}
}
/* check for a block device and update device control block */
STATIC VOID update_dcb(struct dhdr FAR * dhp)
{
int nunits = dhp->dh_name[0];
struct dpb FAR *dpb = LoL->DPBp;
if (LoL->nblkdev)
{
while ((LONG) dpb->dpb_next != -1l)
dpb = dpb->dpb_next;
dpb = dpb->dpb_next =
KernelAlloc(nunits * sizeof(struct dpb), 'E', Config.cfgDosDataUmb);
}
{
int i = 0;
do
{
dpb->dpb_next = dpb + 1;
dpb->dpb_unit = LoL->nblkdev;
if (LoL->nblkdev < LoL->lastdrive && LoL->CDSp)
{
LoL->CDSp[LoL->nblkdev].cdsDpb = dpb;
LoL->CDSp[LoL->nblkdev].cdsFlags = CDSPHYSDRV;
}
LoL->nblkdev++;
dpb->dpb_subunit = i;
dpb->dpb_device = dhp;
dpb->dpb_flags = M_CHANGED;
dpb++;
i++;
} while (i < nunits);
}
(dpb - 1)->dpb_next = (VFP)-1l;
}
/* If r_top is NULL, this is an internal driver */
BOOL init_device(struct dhdr FAR * dhp, PCStr cmdLine, int mode, VFP *r_top)
{
request rq;
rq.r_unit = 0;
rq.r_status = 0;
rq.r_command = C_INIT;
rq.r_length = sizeof(request);
rq.r_endaddr = r_top ? *r_top : lpTop;
rq.r_bpbptr = (VFP)cmdLine;
rq.r_firstunit = LoL->nblkdev;
execrh(&rq, dhp);
/*
* Added needed Error handle
*/
if ((rq.r_status & (S_ERROR | S_DONE)) == S_ERROR)
return TRUE;
if (r_top)
{
/* Don't link in device drivers which do not take up memory */
if (rq.r_endaddr == (BYTE FAR *) dhp)
return TRUE;
/* Fix for multisegmented device drivers: */
/* If there are multiple device drivers in a single driver file, */
/* only the END ADDRESS returned by the last INIT call should be */
/* the used. It is recommended that all the device drivers in */
/* the file return the same address */
if (FP_OFF(dhp->dh_next) == 0xffff)
{
char name[8];
PCStr q;
{
UBYTE ch;
PCStr p = cmdLine;
q = p; /* position after path */
do /* find driver name after path */
{
ch = *p;
p++;
if (ch == ':' || ch == '\\' || ch == '/')
q = p;
} while (ch > ' ');
}
{
int i = 0;
do /* extract driver name */
{
UBYTE ch = *q;
if (ch <= ' ' || ch == '.')
ch = '\0'; /* copy name, without extension */
name[i] = ch;
if (ch == '\0')
break;
i++, q++;
} while (i < sizeof name);
}
KernelAllocPara(FP_SEG(alignNextPara(rq.r_endaddr)) - FP_SEG(dhp),
'D', name, mode);
}
/* Another fix for multisegmented device drivers: */
/* To help emulate the functionallity experienced with other DOS */
/* operating systems when calling multiple device drivers in a */
/* single driver file, save the end address returned from the */
/* last INIT call which will then be passed as the end address */
/* for the next INIT call. */
*r_top = rq.r_endaddr;
}
if (!(dhp->dh_attr & ATTR_CHAR) && (rq.r_nunits != 0))
{
dhp->dh_name[0] = rq.r_nunits;
update_dcb(dhp);
}
if (dhp->dh_attr & ATTR_CONIN)
LoL->syscon = dhp;
else if (dhp->dh_attr & ATTR_CLOCK)
LoL->clock = dhp;
return FALSE;
}
STATIC void init_internal_devices(void)
{
struct dhdr far *device = &LoL->nul_dev;
/* Initialize driver chain */
do {
/* ??? is cmdLine "\n" for internal devices required? --avb */
init_device(device, "\n", 0, NULL);
device = device->dh_next;
}
while (FP_OFF(device) != 0xffff);
}
/*
Initialize all printers
this should work. IMHO, this might also be done on first use
of printer, as I never liked the noise by a resetting printer, and
I usually much more often reset my system, then I print :-)
*/
STATIC VOID InitPrinters(VOID)
{
iregs r;
int num_printers, i;
init_call_intr(0x11, &r); /* get equipment list */
num_printers = (r.a.x >> 14) & 3; /* bits 15-14 */
for (i = 0; i < num_printers; i++)
{
r.a.x = 0x0100; /* initialize printer */
r.d.x = i;
init_call_intr(0x17, &r);
}
}
STATIC VOID InitSerialPorts(VOID)
{
iregs r;
int serial_ports, i;
init_call_intr(0x11, &r); /* get equipment list */
serial_ports = (r.a.x >> 9) & 7; /* bits 11-9 */
for (i = 0; i < serial_ports; i++)
{
r.a.x = 0xA3; /* initialize serial port to 2400,n,8,1 */
r.d.x = i;
init_call_intr(0x14, &r);
}
}
/*****************************************************************
if kernel.config.BootHarddiskSeconds is set,
the default is to boot from harddisk, because
the user is assumed to just have forgotten to
remove the floppy/bootable CD from the drive.
user has some seconds to hit ANY key to continue
to boot from floppy/cd, else the system is
booted from HD
*/
static int EmulatedDriveStatus(int drive,char statusOnly)
{
iregs r;
char buffer[0x13];
buffer[0] = 0x13;
r.AH = 0x4b; /* bootable CDROM - get status */
r.AL = statusOnly;
r.DL = (char)drive;
r.SI = (int)buffer;
init_call_intr(0x13, &r);
return r.FLAGS & 1; /* carry flag */
}
STATIC void CheckContinueBootFromHarddisk(void)
{
char *bootedFrom = "Floppy/CD";
if (InitKernelConfig.BootHarddiskSeconds <= 0)
return;
if (LoL->BootDrive >= 3)
{
#if 0
if (EmulatedDriveStatus(0x80,1))
#endif
/* already booted from HD */
return;
}
else
{
#if 0
if (EmulatedDriveStatus(0x00,1))
#endif
bootedFrom = "Floppy";
}
printf("\n"
"\n"
"\n"
" Hit any key within %d seconds to continue booot from %s\n"
" Hit 'H' or wait %d seconds to boot from Harddisk\n",
InitKernelConfig.BootHarddiskSeconds,
bootedFrom,
InitKernelConfig.BootHarddiskSeconds);
if (GetBiosKey(InitKernelConfig.BootHarddiskSeconds))
{
unsigned key = GetBiosKey(-1); /* remove key from buffer */
if ((UBYTE)key != 'h' && (UBYTE)key != 'H')
/* user has hit a key, continue to boot from floppy/CD */
return;
}
/* reboot from harddisk */
EmulatedDriveStatus(0x00,0);
EmulatedDriveStatus(0x80,0);
{
iregs r;
r.AX = 0x0201;
r.CX = 0x0001;
r.DX = 0x0080;
r.BX = 0x7c00;
r.ES = 0;
init_call_intr(0x13, &r);
}
/* now jump and run */
((void (far*)(void)) MK_FP(0,0x7c00))(); /* jump to boot sector */
}