boot.c
Skip to line: 5050 - 5100 - 5150 - 5200 - 5250 - 5300 - 5350 - 5400 - 5450 - 5500 - 5550 - 5600 - 5650 - 5700 - 5750 - 5800 - 5850 - 5900 - 5950 - 6000 - 6050 - 6100 - 6150 - 6200 - 6250 - 6300 - 6350 - 6400 - 6450 - 6500 - 6550 - 6600 - 6650 - 6700


Highlighted entries were made in the last day
Select a different time increment to highlight entries
Current GMT time: Mar 10 2010 14:00:18

If you have a comment for boot.c, please click here.
Name: zoxhdgtEmail:  gbilat@myscci.comDate: Oct 03 2009 11:32:57 GMT
Subject:  BcLSxnLDHVcmA
Reponse: ep55Yi dpsuplwxvxau, [url=http://aruhfhqepyva.com/]aruhfhqepyva[/url], [link=http://gmchclxalfdq.com/]gmchclxalfdq[/link], http://ccqaiwqyyxwv.com/
 
Respond to zoxhdgt's comment.
 
 
Name: JohnEmail:  tod@gmail.comDate: Sep 27 2009 03:11:04 GMT
Subject:  serfer
Reponse: hi, good site. 111 [URL=http://www.link222.com]222[/URL] http://www.link333.com
 
Respond to John's comment.
 
 
Name: Egdares FutchEmail:  efutch@gmail.comDate: Jun 21 2006 01:27:36 GMT
Subject:  Change the key used for the default boot option
Reponse: If you need to change the default '=' key to select the default Minix boot image in order to support NLS keyboards, edit the string in line 5834 and change the character directly in the display string.
 
Respond to Egdares Futch's comment.
 
 
Name: christosEmail:  Date: Oct 15 2005 12:41:33 GMT
Subject:  line 5354
Reponse: At the figure of line 5354, string2 should be swapped with string3
 
Respond to christos's comment.
 
 
5000   /*      boot.c - Load and start Minix.             Author: Kees J. Bot
5001    *                                                         27 Dec 1991
5002    *
5003    * Copyright 1998 Kees J. Bot, All rights reserved.
5004    * This package may be freely used and modified except that changes that
5005    * do not increase the functionality or that are incompatible with the
5006    * original may not be released to the public without permission from the
5007    * author.  Use of so called "C beautifiers" is explicitly prohibited.
Expand/Collapse Item5008     */
The key to understanding the boot sequence is understanding the function boot() (line 6605).  boot() is called from boothead.s ; it is the first function from boot.c to be called. boot() initializes the system with calls to initialize(), get_parameters(), and r_super() before processing commands from the bootparams sector (see lines 5848-5864) and commands typed in by the user.

Study the code from boot() before returning to the beginning.

5009
5010 char version[]=         "2.11";
5011
Expand/Collapse Item5012    #define BIOS    (!UNIX)         /* Either uses BIOS or UNIX syscalls. */
boot.c is used for both the boot monitor and edparams, a utility program.  Since the boot monitor runs independently of any operating system, it relies on bios function calls for its input/output (BIOS=TRUE, UNIX=FALSE).  edparams is an application that depends on the kernel; it relies on the operating system for its input/output (UNIX=TRUE, BIOS=FALSE).

In Makefile, edparams.c is compiled with the -D option.  (Note that edparams.c is a link to boot.c.)  The -DUNIX option sets UNIX equal to 1 (TRUE).  boot.c, on the other hand, is not compiled with this option (see line 0126 of Makefile). UNIX is therefore undefined and FALSE.

Different sections of code are parsed depending on whether UNIX or BIOS is TRUE.  For example, if BIOS is TRUE, lines 5030-5031 are parsed and lines 5034-5039 are discarded.  If UNIX is TRUE, lines 5034-5039 are parsed and lines 5030-5031 are discarded.  I do not cover sections of code that are specific to UNIX; for example, I do not cover lines 5143-5268.

5013
Expand/Collapse Item5014  #define nil 0
#define is a preprocessor command.  The preprocessor replaces all occurrences of the first string (in this case, "nil") with the second string (in this case, "0") before compilation.

The string "nil" is used to increase readability.

Expand/Collapse Item5015    #define _POSIX_SOURCE   1
Expand/Collapse Item5016    #define _MINIX          1
_POSIX_SOURCE and _MINIX are macros that are used by library routines.  In fact, nearly all macros that begin with an underscore (_) are specific to library routines.

The POSIX standard was created to improve portability between UNIX systems.  The 12th paragraph of section 2.6.2, lines 01184-011200, and lines 01241-01245 in the minix code in Operating Systems describe the _POSIX_SOURCE and _MINIX macros.

Expand/Collapse Item5017    #include <stddef.h>
A function must be either defined or declared in a file before it can be used.  Header files (files ending in .h) make the task of declaring variables easier.

Before compilation begins, the preprocessor replaces any #include <filename.h> statement with the contents of filename.h.  If the filename is enclosed in < and >, the preprocessor searches for the file in a default directory (typically /usr/include) and other directories specified by the -I option of the compiler (see line 5030).  If the filename is quoted (see line 5041), the preprocessor looks for the include file in the same directory that the source file is found.  (In this case, boot.c is the source file and is found in the /usr/src/boot directory.)

As an example, strlen() (see line 5613) is declared in the file string.h (see line 5022).  string.h is located in the directory /usr/include.

Header files, in addition to containing function declarations, also frequently contain #defines.  For example, RATIO (see line 5130) is #defined in boot.h.  Since boot.h is quoted (see line 5044), the preprocessor searches for boot.h in the same directory as boot.c.  Indeed, both files are in the /usr/src/boot directory.

5018   #include <sys/types.h>
5019   #include <sys/stat.h>
5020   #include <stdlib.h>
5021   #include <limits.h>
5022   #include <string.h>
Expand/Collapse Item5023    #include <errno.h>
errno is declared in errno.h (see line 00230 in the book) as extern.  Memory for a variable can be allocated in only one file (i.e. the variable is "defined" once) but the variable must be declared as extern in every other file that accesses it.  However, memory is not allocated for errno in boothead.s, boot.c, bootimage.c, or rawfs.c.  If you understand how memory is allocated for errno, please submit a comment to the site which will be displayed below.
Name: qlaujmsujuEmail:  nijshe@oaqzqe.comDate: Oct 03 2009 12:19:43 GMT
Subject:  GEUQWVHPrWQs
Reponse: UGdmZ6 atwqlmqeqjgc, [url=http://jqzfydkawbfp.com/]jqzfydkawbfp[/url], [link=http://utdjxfulqmpw.com/]utdjxfulqmpw[/link], http://nucixwaxemng.com/
 
Respond to qlaujmsuju's comment.
 
 
Name: JohnEmail:  teddy@mail.comDate: Sep 27 2009 03:10:52 GMT
Subject:  serfer
Reponse: hi, good site. 111 [URL=http://www.link222.com]222[/URL] http://www.link333.com
 
Respond to John's comment.
 
 
Name: Christos KarayiannisEmail:  christos@kar.forthnet.grDate: Dec 08 2003 15:02:52 GMT
Subject:  Where is errno defined?
Reponse: errno is defined in src/lib/other/errno.c
(see also the comment of line 7013 in bootimage.c)
 
Respond to Christos Karayiannis's comment.
 
 
5024   #include <ibm/partition.h>
5025   #include <minix/config.h>
5026   #include <minix/type.h>
5027   #include <minix/const.h>
5028   #include <minix/minlib.h>
5029   #if BIOS
Expand/Collapse Item5030    #include <kernel/const.h>
If a filename is enclosed in < and >, the preprocessor searches for the file in the default directory (typically /usr/include) and other directories specified by the -I option of the compiler.  In Makefile , the parent directory (..) is specified by the -I option.  Makefile is found in /usr/src/boot; therefore, the parent directory is /usr/src.  After the preprocessor unsuccessfully looks for const.h in the /usr/include/kernel directory, the preprocessor looks for (and finds) const.h in the /usr/src/kernel directory.  Note that the preprocessor can't find const.h in /usr/src/kernel since this directory doesn't exist.
5031   #include <kernel/type.h>
5032   #endif
Expand/Collapse Item5033    #if UNIX
I do not cover sections of code that are specific to UNIX (lines 5034-5040).
5034   #include <stdio.h>
5035   #include <time.h>
5036   #include <unistd.h>
5037   #include <fcntl.h>
5038   #include <signal.h>
5039   #include <termios.h>
5040   #endif
5041   #include "rawfs.h"
Expand/Collapse Item5042    #undef EXTERN
Memory for a variable can be allocated in only one file (i.e. the variable is "defined") but the variable must be declared as extern in every other file that accesses it.  To accomplish this, the macro EXTERN is #defined as the empty string.  This prevents the EXTERN macro from being #defined as extern in boot.h when boot.h is #included in boot.c.  boot.h is also #included in bootimage.c.  Since EXTERN is not #defined (and is therefore undefined), EXTERN is replaced by extern in bootimage.c.  This mechanism ensures that memory for a variable is allocated only once.
5043   #define EXTERN  /* Empty */
5044   #include "boot.h"
5045
Expand/Collapse Item5046  #define arraysize(a)            (sizeof(a) / sizeof((a)[0]))
The sizeof operator returns the size of its argument (in this case, the array a and the first element of a).  For example, if a has 10 elements and each element is 2 bytes (a short):

arraysize(a) = sizeof(a)/sizeof(a[0]) = 20/2 = 10

Expand/Collapse Item5047    #define arraylimit(a)           ((a) + arraysize(a))
arraylimit(a) returns the address of the first byte after the last element of array a.

Let's say we have an array of shorts, short_array[], that has 10 elements.  As the above example shows, arraysize(short_array) = 10.  So if the address of the first byte of short_array[] was, for example, 0x1100000, then arraylimit(a) = a + arraysize(a) = 10 = 0x1100000 + 10 = 0x1100000 + 0xA = 0x110000A.  This is incorrect.

Pointer arithmetic can be confusing to the uninitiated.  The following program:

#include <stdio.h>

#define arraysize(a)    (sizeof(a)/sizeof((a)[0]))
#define arraylimit(a)   ((a) + arraysize(a))

int main()
{
  short short_array[5]= {1,2,3,4,5};
  int size_of_array;
  short *short_ptr;

  size_of_array= arraysize(short_array);
  printf("the size of short_array= %d\n", size_of_array);

  short_ptr= arraylimit(short_array);
  printf("the beginning of short_array is %p\n", short_array);
  printf("short_ptr= %p\n", short_ptr);

}

produces the output:

the size of short_array= 5
the beginning of short_array is 0xbffffc50
short_ptr= 0xbffffc5A

short_ptr equals 0xbffffc5A and not 0xbffffc55.  In other words, 0xbffffc50+5 (in this scenario) equals 0xbffffc5A and not 0xbffffc55.  Any value added to a pointer is first multiplied by the size of the type that the pointer references.  A short is 2 bytes and short_array has 5 elements.  So 0xbffffc50+2*5 = 0xbffffc50+10 = 0xbffffc50+0x0A = 0xbffffc5A.

Expand/Collapse Item5048    #define between(a, c, z)        ((unsigned) ((c) - (a)) <= ((z) - (a)))
between() returns TRUE if c is between a and z.
5049
5050   #if BIOS
Expand/Collapse Item5051    char *bios_err(int err)
The functions in boothead.s (for example, dev_open()) return an error code in ax if something goes wrong.  bios_err() converts this number to a readable string.

For example, on line 6131, the return value of dev_open() is stored in r.  If an error occurred, r will be nonzero.  On line 6133, bios_err() converts this number to a readable string so that printf() (on the next line:  line 6134) prints something meaningful.

5052   /* Translate BIOS error code to a readable string.  (This is a rare trait
5053    * known as error checking and reporting.  Take a good look at it, you
5054    * won't see it often.)
5055    */
5056   {
Expand/Collapse Item5057            static struct errlist {
The static declaration specifies that the variable (in this case, the array errlist[]) remains in existence after the function returns.  Since the contents of errlist[] don't change from one invocation of bios_err() to the next, it is preferable that the array remain in existence.  It saves the time for reinitialization that would otherwise be necessary.

The static declaration has a different meaning for external variables.  The scope of an external static variable is only within the file in which the variable is declared.  Note that "external" variables are variables that are declared outside of all functions.

5058                   int     err;
5059                   char    *what;
5060           } errlist[] = {
5061   #if !DOS
5062            { 0x00, "No error" },
5063            { 0x01, "Invalid command" },
5064            { 0x02, "Address mark not found" },
5065            { 0x03, "Disk write-protected" },
5066            { 0x04, "Sector not found" },
5067            { 0x05, "Reset failed" },
5068            { 0x06, "Floppy disk removed" },
5069            { 0x07, "Bad parameter table" },
5070            { 0x08, "DMA overrun" },
5071            { 0x09, "DMA crossed 64 KB boundary" },
5072            { 0x0A, "Bad sector flag" },
5073            { 0x0B, "Bad track flag" },
5074            { 0x0C, "Media type not found" },
5075            { 0x0D, "Invalid number of sectors on format" },
5076            { 0x0E, "Control data address mark detected" },
5077            { 0x0F, "DMA arbitration level out of range" },
5078            { 0x10, "Uncorrectable CRC or ECC data error" },
5079            { 0x11, "ECC corrected data error" },
5080            { 0x20, "Controller failed" },
5081            { 0x40, "Seek failed" },
5082            { 0x80, "Disk timed-out" },
5083            { 0xAA, "Drive not ready" },
5084            { 0xBB, "Undefined error" },
5085            { 0xCC, "Write fault" },
5086            { 0xE0, "Status register error" },
5087            { 0xFF, "Sense operation failed" }
5088   #else /* DOS */
5089            { 0x00, "No error" },
5090            { 0x01, "Function number invalid" },
5091            { 0x02, "File not found" },
5092            { 0x03, "Path not found" },
5093            { 0x04, "Too many open files" },
5094            { 0x05, "Access denied" },
5095            { 0x06, "Invalid handle" },
5096            { 0x0C, "Access code invalid" },
5097   #endif /* DOS */
5098           };
Expand/Collapse Item5099            struct errlist *errp;
errp is a pointer to a struct errlist (see line 5060).
5100
Expand/Collapse Item5101            for (errp= errlist; errp < arraylimit(errlist); errp++) {
Expand/Collapse Item5102                    if (errp->err == err) return errp->what;
Expand/Collapse Item5103            }
Expand/Collapse Item5104            return "Unknown error";
The for loop compares the argument err with the err field of each element of the array errlist[].  When a match is found, the string what is returned.  If the for loop goes through the entire array errlist[] without finding a match, bios_err() returns the string "Unknown error".
5105   }
5106
5107   char *unix_err(int err)
5108   /* Translate the few errors rawfs can give. */
5109   {
5110           switch (err) {
5111           case ENOENT:    return "No such file or directory";
5112           case ENOTDIR:   return "Not a directory";
5113           default:        return "Unknown error";
5114           }
5115   }
5116
5117   void rwerr(char *rw, off_t sec, int err)
5118   {
5119           printf("\n%s error 0x%02x (%s) at sector %ld absolute\n",
5120                   rw, err, bios_err(err), sec);
5121   }
5122
Expand/Collapse Item5123    void readerr(off_t sec, int err)      { rwerr("Read", sec, err); }
readerr() and writerr() provide convenient interfaces to rwerr() and bios_err().

Notice the order of the functions bios_err(), rwerr(), and readerr()readerr() calls rwerr() which calls bios_err().  A function must be declared or defined before it can be called.  bios_err() is defined (lines 5051-5105) before rwerr() calls it (line 5120) and rwerr() is defined (lines 5117-5121) before readerr() calls it.  The need to carefully order the functions can be avoided if the functions are declared in a header file #included at the beginning of the file.  (In this case, boot.h would be the appropriate header file.)

5124   void writerr(off_t sec, int err)      { rwerr("Write", sec, err); }
5125
Expand/Collapse Item5126    void readblock(off_t blk, char *buf)
readblock() reads blk into the buffer buf.  Note that blk is not an absolute block on the hard drive; blk is the block offset from the beginning of the partition.  The first sector in the partition is lowsec.  (2 sectors = 1 block = 2*512 bytes = 1024 bytes)
5127   /* Read blocks for the rawfs package. */
5128   {
5129           int r;
Expand/Collapse Item5130            u32_t sec= lowsec + blk * RATIO;
sec is the absolute sector address of the first sector of blk.
5131
Expand/Collapse Item5132            if ((r= readsectors(mon2abs(buf), sec, 1 * RATIO)) != 0) {
int readsectors(u32_t bufaddr, u32_t sector, U8_t count) reads count bytes beginning at absolute sector address sector into absolute memory address bufaddr.

u32_t mon2abs(void *ptr) converts the offset memory address ptr to an absolute memory address.

RATIO = 2.  Both sectors of blk are read.

5133                readerr(sec, r); exit(1);
5134           }
5135   }
5136
Expand/Collapse Item5137    #define istty           (1)
Expand/Collapse Item5138    #define alarm(n)        (0)
Expand/Collapse Item5139    #define pause()         (0)
istty, alarm(n), and pause() expand to TRUE, FALSE, and FALSE, respectively.
5140
5141   #endif /* BIOS */
5142
Expand/Collapse Item5143    #if UNIX
I do not cover UNIX-specific sections of code (lines 5143-5268).  I may cover these sections at a later time.
5144
5145   /* The Minix boot block must start with these bytes: */
5146   char boot_magic[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC };
5147
5148   struct biosdev {
5149           char *name;             /* Name of device. */
5150           int device;             /* Device to edit parameters. */
5151   } bootdev;
5152
5153   struct termios termbuf;
5154   int istty;
5155
5156   void quit(int status)
5157   {
5158           if (istty) (void) tcsetattr(0, TCSANOW, &termbuf);
5159           exit(status);
5160   }
5161
5162   #define exit(s) quit(s)
5163
5164   void report(char *label)
5165   /* edparams: label: No such file or directory */
5166   {
5167           fprintf(stderr, "edparams: %s: %s\n", label, strerror(errno));
5168   }
5169
5170   void fatal(char *label)
5171   {
5172           report(label);
5173           exit(1);
5174   }
5175
5176   void *alloc(void *m, size_t n)
5177   {
5178           m= m == nil ? malloc(n) : realloc(m, n);
5179           if (m == nil) fatal("");
5180           return m;
5181   }
5182
5183   #define malloc(n)       alloc(nil, n)
5184   #define realloc(m, n)   alloc(m, n)
5185
5186   #define mon2abs(addr)   ((void *) (addr))
5187
5188   int rwsectors(int rw, void *addr, u32_t sec, int nsec)
5189   {
5190           ssize_t r;
5191           size_t len= nsec * SECTOR_SIZE;
5192
5193           if (lseek(bootdev.device, sec * SECTOR_SIZE, SEEK_SET) == -1)
5194                   return errno;
5195
5196           if (rw == 0) {
5197                   r= read(bootdev.device, (char *) addr, len);
5198           } else {
5199                   r= write(bootdev.device, (char *) addr, len);
5200           }
5201           if (r == -1) return errno;
5202           if (r != len) return EIO;
5203           return 0;
5204   }
5205
5206   #define readsectors(a, s, n)     rwsectors(0, (a), (s), (n))
5207   #define writesectors(a, s, n)    rwsectors(1, (a), (s), (n))
5208   #define readerr(sec, err)       (errno= (err), report(bootdev.name))
5209   #define writerr(sec, err)       (errno= (err), report(bootdev.name))
5210   #define putch(c)                putchar(c)
5211   #define unix_err(err)           strerror(err)
5212
5213   void readblock(off_t blk, char *buf)
5214   /* Read blocks for the rawfs package. */
5215   {
5216           errno= EIO;
5217           if (lseek(bootdev.device, blk * BLOCK_SIZE, SEEK_SET) == -1
5218                   || read(bootdev.device, buf, BLOCK_SIZE) != BLOCK_SIZE)
5219           {
5220                   fatal(bootdev.name);
5221           }
5222   }
5223
5224   int trapsig;
5225
5226   void trap(int sig)
5227   {
5228           trapsig= sig;
5229           signal(sig, trap);
5230   }
5231
5232   int escape(void)
5233   {
5234           if (trapsig == SIGINT) {
5235                   trapsig= 0;
5236                   return 1;
5237           }
5238           return 0;
5239   }
5240
5241   int getch(void)
5242   {
5243           char c;
5244
5245           fflush(stdout);
5246
5247           switch (read(0, &c, 1)) {
5248           case -1:
5249                   if (errno != EINTR) fatal("");
5250                   return(ESC);
5251           case 0:
5252                   if (istty) putch('\n');
5253                   exit(0);
5254           default:
5255                   if (istty && c == termbuf.c_cc[VEOF]) {
5256                           putch('\n');
5257                           exit(0);
5258                   }
5259                   return c & 0xFF;
5260           }
5261   }
5262
5263   #define get_tick()              ((u32_t) time(nil))
5264   #define clear_screen()          printf("[clear]");
5265   #define boot_device(device)     printf("[boot %s]\n", device);
5266   #define bootminix()             (run_trailer() && printf("[boot]\n"))
5267
5268   #endif /* UNIX */
5269
5270   char *readline(void)
5271   /* Read a line including a newline with echoing. */
5272   {
5273           char *line;
5274           size_t i, z;
5275           int c;
5276
5277           i= 0;
5278           z= 20;
Expand/Collapse Item5279            line= malloc(z * sizeof(char));
void *malloc(size_t size) allocates size bytes from the heap and returns a pointer to the first byte of the allocated memory. malloc() is declared in stdlib.h.    (The heap is the region between the stack and the bss.)

Space for 20 char's (20 bytes) is initially allocated.  If this is used up, another 20 bytes are allocated.  If the 40 bytes are used up, 40 additional bytes are allocated.  Each reallocation of memory doubles the total amount of memory allocated.  See lines 5298-5299.

5280
5281           do {
5282                   c= getch();
5283
Expand/Collapse Item5284                    if (strchr("\b\177\25\30", c) != nil) {
char *strchr(char *cs, char c)  returns a pointer to the first occurrence of c in cs or nil (0) if not present.  strchr() is declared in string.h.

ctrl-U and ctrl-X erase the entire line.

5285                           /* Backspace, DEL, ctrl-U, or ctrl-X. */
5286                           do {
5287                                   if (i == 0) break;
Expand/Collapse Item5288                                    printf("\b \b");
"\b \b" backspaces, prints a space, and then backspaces again.  This erases the previous character and backs up one space.  Note that no single ascii character can replace this sequence.
5289                                   i--;
5290                           } while (c == '\25' || c == '\30');
5291                   } else
Expand/Collapse Item5292                    if (c < ' ' && c != '\n') {
Expand/Collapse Item5293                           putch('\7');
5292-5293
'
\7' is the bell.  Backspace, DEL, ctrl-U, ctrl-X and newline are the only allowed control characters.  All other control characters are forbidden.

int putch(int c) prints c at the current cursor position.  If c is the bell, the speaker beeps and the cursor doesn't advance.

5294                   } else {
5295                          putch(c);
5296                           line[i++]= c;
5297                           if (i == z) {
5298                                   z*= 2;
5299                                   line= realloc(line, z * sizeof(char));
5300                           }
5301                   }
Expand/Collapse Item5302            } while (c != '\n');
The line is read until a newline ('\n') is typed.
Expand/Collapse Item5303            line[i]= 0;
A terminating 0 is appended to line.
5304           return line;
5305   }
5306
Expand/Collapse Item5307    int sugar(char *tok)
Commands for the boot monitor must be parsed into "tokens" and placed in a command chain before the commands can be processed.  Here are some sample commands for the boot monitor:

hd2a> rootdev = hd2a
hd2a> delay 5000
hd2a> dos(d, MS-DOS) { boot hd1 }
hd2a> minix(=, MINIX) { boot }
hd2a> main() { trap 5000 minix; menu }

and here is how the commands are broken up into tokens:

token1    token2     token3    token4
rootdev   =          hd2a      \n

token1    token2     token3
delay     5000       \n

token1    token2     token3    token4    token5    token6
dos       (d,MS-DOS) {         boot      hd1       }
token7
\n

token1    token2     token3    token4    token5    token6
minix     (=,MINIX)  {         boot      }         \n

token1    token2     token3    token4    token5    token6
main      ()         {         trap      5000      minix
token7    token8     token9    token10
;         menu       }          \n

These tokens are chained together with the struct token (lines 5356-5359).

Here is the first command (rootdev = hd2a).

Commands are separated not only by newlines ('\n') but also by semicolons(;).  For example, on lines 5861-5862, the tokens ":", "leader", and "main" are separated by semicolons.

Sector 1 (PARAMSEC) of any bootable partition is the "bootparams sector". (Sector 0 of any bootable partition is the bootblock code.) The PARAMSEC sector contains commands that were previously saved.  Before the boot monitor goes into interactive mode (i.e. before the prompt appears), the bootparams sector is tokenized (see lines 5848-5854) and the commands are processed.  Tokens in the bootparams sector are separated by newlines (see line 5920).

5308   /* Recognize special tokens. */
5309   {
Expand/Collapse Item5310            return strchr("=(){};\n", tok[0]) != nil;
char *strchr(char *cs, char c)  returns a pointer to the first occurrence of c in cs or nil (0) if not present.  strchr() is declared in string.h.  Note that only a single character (tok[0]) is being compared and not an entire string.
5311   }
5312
Expand/Collapse Item5313    char *onetoken(char **aline)
onetoken() is called on line 5372.  It is a little difficult (for people inexperienced with pointers) to understand why a pointer to a pointer (char **aline) is the parameter rather than just a pointer.

In the function tokenize() (line 5361), line must be advanced in each call to onetoken().  (Note that this refers to the variable line from tokenize(), not the variable line from onetoken().)

Study the following program:

#include <stdio.h>

int main()
{
  int var1 = 5;
  function1(var1);
  printf("var1 is equal to %d.", var1);
}

int function1(int var)
{
  var=3;
}

The output of this program is:
var1 is equal to 5.

The value of var1 cannot be changed by altering the value of var in function1().  The reason var1 can't be changed here is that function arguments in C are passed "by value."  Called functions are given the value of their arguments in temporary variables rather than the originals.  For this reason, changes to var in function1() are not reflected in var1 from main().

However, there is a way to change the value of var1:

#include <stdio.h>

int main()
{
  int var1 = 5;
  function1(&var1);
  printf("var1 is equal to %d." var1);
}

int function1(int *var)
{
  *var=3;
}

The output of this program is:
var1 is equal to 3.

Note that the value of var1 has been changed by passing in the address of var1 rather than var1 itself.  function1() dereferences var to change the value of var1.

Since the function onetoken() must change the value of line (see line 5372), the address of line is passed in rather than line itself.  Note that line from tokenize() is a pointer to a char; therefore a pointer to a pointer to a char is passed in.

When you see a pointer to a pointer as a parameter to a function, check to see if the function changes the value of a pointer by dereferencing the pointer to a pointer.  For example, onetoken() changes the value of line (the variable line from tokenize(), not onetoken()) by dereferencing aline on line 5350.

I understand that this stuff is confusing.  We'll run into this again; I'll continue to give detailed explanations in case you didn't understand it this time.

5314   /* Returns a string with one token for tokenize. */
5315   {
Expand/Collapse Item5316            char *line= *aline;
line keeps track of the current position in the token.  On line 5350, it's copied to *aline.
5317           size_t n;
Expand/Collapse Item5318            char *tok;
On lines 5345-5346, memory is allocated for tok and the token is copied to the allocated memory.  On line 5351, tok is returned.
5319
Expand/Collapse Item5320            /* Skip spaces and runs of newlines. */
Expand/Collapse Item5321            while (*line == ' '||(*line =='\n'&&line[1] == '\n')) line++;
All spaces are skipped.  If there are several newlines ('\n') in a row, every newline until the last is skipped.  The last newline is tokenized.

On line 5348, newlines are replaced with semicolons (;).  Semicolons and newlines have the same meaning here; they are both command separators.

5322
5323           *aline= line;
5324
5325           /* Don't do odd junk (nor the terminating 0!). */
5326           if ((unsigned) *line < ' ' && *line != '\n') return nil;
5327
Expand/Collapse Item5328            if (*line == '(') {
Study the examples in the comment for line 5307.  These examples will help you to understand what constitutes a single token.  Also, I refer to them in the next lines.

If the first character is a left parenthesis ('('), then the token is something like (d,MS-DOS), (=, MINIX), or () (see comments for line 5307).

5329                   /* Function argument, anything goes but () must match. */
5330                   int depth= 0;
5331
5332                   while ((unsigned) *line >= ' ') {
5333                           if (*line == '(') depth++;
5334                           if (*line++ == ')' && --depth == 0) break;
5335                   }
5336           } else
5337           if (sugar(line)) {
5338                   /* Single character token. */
5339                   line++;
Expand/Collapse Item5340            } else {
These are character strings like boot, minix, 5000, hd2a, etc. (see comments for line 5307).
5341                   /* Multicharacter token. */
5342                   do line++; while ((unsigned) *line > ' ' && !sugar(line));
5343           }
Expand/Collapse Item5344            n= line - *aline;
Expand/Collapse Item5345            tok= malloc((n + 1) * sizeof(char));
Expand/Collapse Item5346            memcpy(tok, *aline, n);
Expand/Collapse Item5347            tok[n]= 0;
Let's look at an example.  Suppose the token is "hd2a". line would have advanced four characters beyond *aline.  So 4 char's would be allocated for "hd2a" and one char for the terminating 0.
Expand/Collapse Item5348            if (tok[0] == '\n') tok[0]= ';';        /* ';' same as '\n' */
Newlines ('\n') and semicolons (;) have the same meaning - they're both command separators.  We standardize by converting newlines to semicolons.
5349
Expand/Collapse Item5350            *aline= line;
This advances *aline to point to the next token.  Since aline points to line (from tokenize(); see line 5372), advancing *aline advances line (from tokenize()).
5351           return tok;
5352   }
5353
Expand/Collapse Item5354    /* Typed commands form strings of tokens. */

5355
5356   typedef struct token {
5357           struct token    *next;  /* Next in a command chain. */
5358           char            *token;
5359   } token;
5360
Expand/Collapse Item5361    token **tokenize(token **acmds, char *line)
tokenize() is called for the first time on line 5854 in get_parameters()(line 5773) when the boot parameters from sector PARAMSEC (=1) are tokenized. cmds (line 5382) is a pointer to the first token in the command chain.  After sector PARAMSEC is tokenized, the string ":;leader;main" is tokenized and appended to the command chain (see lines 5861-5862).

Again, we see a function parameter (token **acmds) that is a pointer to a pointer.  We saw this previously with  onetoken() (line 5313).  And again, we wish to change a pointer variable (for example, cmds on line 5854) so we pass in a pointer to this pointer variable.

The pointer manipulations in tokenize() are extremely tricky.  I hope that this slide show helps.  This slide show assumes that a pointer to cmds is passed into acmds.  This is the case if the command chain is empty.  The command chain is empty before the boot parameters from sector PARAMSEC are tokenized (see line 5854).  The command chain is also empty before the boot monitor reads a line and tokenizes it (see lines 6591-6598).

tokenize slide show

5362  /* Takes a line apart to form tokens.  The tokens are inserted into a
5363    * command chain at *acmds.  Tokenize returns a reference to where
5364    * another line could be added.  Tokenize looks at spaces as token
5365    * separators and recognizes only ';', '=', '{', '}', and '\n' as single
5366    * character tokens.  One token is formed from '(' and ')' with anything
5367    * in between as long as more match */
5368   {
5369           char *tok;
5370           token *newcmd;
5371
5372           while ((tok= onetoken(&line)) != nil) {
5373                   newcmd= malloc(sizeof(*newcmd));
5374                   newcmd->token= tok;
5375                   newcmd->next= *acmds;
5376                   *acmds= newcmd;
5377                   acmds= &newcmd->next;
5378           }
5379           return acmds;
5380   }
5381
Expand/Collapse Item5382    token *cmds;            /* String of commands to execute. */
cmds points to the first token in the command chain.
5383   int err;                /* Set on an error. */
5384
Expand/Collapse Item5385    char *poptoken(void)
poptoken() removes the first token from the command chain and returns the string.  For example, if the command chain is:

token1    token2    token3    token4    token5    token6
rootdev   =         hd2a      ;         menu      ;

poptoken() would remove token1 from the command chain and return a pointer to "rootdev".

In the figure below, note that there are two memory objects, one beginning at address token1 and one beginning at address a (the string).  The two memory objects are independent.  Freeing (i.e. deallocating) the memory object that begins at address token1 does not free the memory object that begins at address a. poptoken() frees the memory for the token.  voidtoken() frees the memory for the token and the string.


5386   /* Pop one token off the command chain. */
5387   {
5388          token *cmd= cmds;
5389           char *tok= cmd->token;
5390
Expand/Collapse Item5391           cmds= cmd->next;
cmds is advanced to the next token.  In the example in the comments for line 5385, cmds would point to token2.
Expand/Collapse Item5392            free(cmd);
Note that the memory that cmd references is freed (i.e. deallocated) but the memory that tok references is not freed.
5393
5394           return tok;
5395   }
5396
Expand/Collapse Item5397    void voidtoken(void)
voidtoken() removes the first token from the command chain and frees the memory of the token's string.  After a command has been processed, voidtoken() removes the command from the command chain (see lines 6379, 6426).
5398   /* Remove one token from the command chain. */
5399   {
5400           free(poptoken());
5401   }
5402
Expand/Collapse Item5403    int interrupt(void)
interrupt() returns 1 (TRUE) if the ESC key has been pressed. interrupt() sets the variable err (line 5383).

Occasionally, the boot monitor needs to check if the ESC (escape) key has been pressed.  The boot monitor allows the delay command (see line 6225) and the menu command (see line 6270) to be escaped (see line 6225).  The delay msec command pauses for msec milliseconds before executing the next command in the command chain.  The menu command shows the selection of kernels that can be booted.  Escaping the menu command sends the user back to the boot monitor prompt.

The command echo \w also calls interrupt(). echo \w waits until the escape key is pressed or a newline is entered (see line 6486).

5404   /* Clean up after an ESC has been typed. */
5405   {
Expand/Collapse Item5406            if (escape()) {
escape() returns 1 (TRUE) if the ESC key has been pressed.
5407                   printf("[ESC]\n");
5408                  err= 1;
5409                   return 1;
5410           }
5411           return 0;
5412   }
5413
5414   #if BIOS
5415
5416   int activate;
5417
Expand/Collapse Item5418    struct biosdev {
Expand/Collapse Item5419            char name[6];
Expand/Collapse Item5420            int device, primary, secondary;
Expand/Collapse Item5421    } bootdev, tmpdev;
bootdev is the partition or drive (in the case of a floppy) from which this boot monitor program originated.  If the user chooses to boot another partition or drive, tmpdev is the chosen partition or drive (see exec_bootstrap() on line 6070).

bootdev is set in initialize() (line 5450). tmpdev is set in name2dev() (line 5979).  name will be something like "fd1" (second floppy drive) or "hd7" (second partition on second hard drive; see line 5549) or "hd3a" (first subpartition on third partition on first hard drive).

device, primary and secondary for "fd1", "hd7", and "hd3a" are:

name= "fd1"
device= 0x01
primary= -1
secondary= -1

name= "hd7"
device= 0x81
primary= 1
secondary= -1

name= "hd3a"
device= 0x80
primary= 2
secondary= 0

5422
Expand/Collapse Item5423    int get_master(char *master, struct part_entry **table, u32_t pos)
The master boot record (MBR) is found in the first sector (sector 0) of a partitioned disk.  If a partition has subpartitions, an MBR will also be found in the first sector of the partition.

An MBR has two parts: the code and the partition table.  The MBR is 1 sector (=512 bytes) long and the partition table starts at byte 0x1BE (=446).  A partition table has 4 entries.  Here is an entry from the partition table:

get_master() copies the entire MBR (code + partition table) from disk into the memory address referenced by master and copies the memory addresses of the 4 partition entries from the partition table into the array referenced by table.  The figure below shows the variables and memory layout after get_parameter() has finished the copy.

get_master() is called on line 5519 and line 6085.  On line 5519, get_master() is called to determine the name of the partition (e.g. "hd2a").  On line 6085, get_master() is called to determine where to find the bootstrap that corresponds to tmpdev (see line 5418).  The code on lines 5519 and 6085 is concerned only with the partition table and ignores the code from the MBR.

5424   /* Read a master boot sector and its partition table. */
5425   {
5426           int r, n;
Expand/Collapse Item5427            struct part_entry *pe, **pt;
struct part_entry is declared in <ibm/partition.h> (see line 04100 in the book).
5428
Expand/Collapse Item5429            if ((r= readsectors(mon2abs(master), pos, 1)) != 0) return r;
int readsectors(u32_t bufaddr, u32_t sector, U8_t count) reads count bytes beginning at absolute sector address sector into absolute memory address bufaddr.

u32_t mon2abs(void *ptr) converts the offset memory address ptr to an absolute memory address.

5430
Expand/Collapse Item5431            pe= (struct part_entry *) (master + PART_TABLE_OFF);
pe is initialized to point to the first partition table entry.

PART_TABLE_OFFSET and NR_PARTITIONS are #defined in <ibm/partition.h>. NR_PARTITIONS = 4.

Expand/Collapse Item5432            for (pt= table; pt < table + NR_PARTITIONS; pt++) *pt= pe++;

The for loop fills in the array at address f (see figure above).

5433
5434    /* DOS has the misguided idea that partition tables must be sorted. */
Expand/Collapse Item5435            if (pos != 0) return 0;         /* But only the primary. */
If a partition is subpartitioned and the partition has a master boot record, the partition table from the master boot record does not need to be sorted.
5436
Expand/Collapse Item5437            n= NR_PARTITIONS;
Here's a review of partition table entries:

The partition table entries are sorted with the bubble sort algorithm.  Here is how the bubble sort algorithm works:

Partition table entry 1 and partition table entry 2 are compared.  If the first entry has a greater lowsec (lowsec is the sector number of the first sector in a partition) or is not being used (sysind=NO_PART), the two entries are swapped.  If the first entry has a lower lowsec and is being used, the first two entries are not swapped.  Entries 2 and 3 are next compared and swapped if entry 2 has a greater lowsec or is not being used.  Entries 3 and 4 are compared and swapped if entry 3 has a greater lowsec or is not being used.  Next, the process starts again and entries 1 and 2 are compared and swapped if appropriate.

Here is a figure that describes the entire sorting process.  Four partitions with lowsec's of 1, 509, 890, and 1500 are sorted.  The partition with a lowsec of 890 is not being used (NP).


5438           do {
5439                   for (pt= table; pt < table + NR_PARTITIONS-1; pt++) {
5440                           if (pt[0]->sysind == NO_PART
5441                                   || (pt[0]->lowsec > pt[1]->lowsec
5442                                          && pt[1]->sysind != NO_PART)) {
5443                                   pe= pt[0]; pt[0]= pt[1]; pt[1]= pe;
5444                           }
5445                   }
5446           } while (--n > 0);
5447           return 0;
5448   }
5449
Expand/Collapse Item5450    void initialize(void)
initialize() is called from boot() on line 6609. initialize() does 3 things:

1)  Relocates the boot monitor to the upper end of low memory.  Low memory is memory that is under 640KB.

2)  Removes the boot monitor from the memory map that is passed to the kernel.  This prevents the kernel from allocating the memory that the boot monitor occupies.

3)  Initializes bootdev (see comments for line 5418).

5451   {
Expand/Collapse Item5452            char master[SECTOR_SIZE];
The master boot record (MBR) is copied into master[] on line 5519.  For a description of the MBR, see the comments for get_master() (line 5423).
Expand/Collapse Item5453            struct part_entry *table[NR_PARTITIONS];
table[] is an array of pointers to partition table entries (struct part_entry).  table[] has NR_PARTITIONS (=4) elements and is filled in by get_master() (see line 5519).
5454           int r, p;
Expand/Collapse Item5455            u32_t masterpos;
masterpos is the absolute sector number of a sector that holds a master boot record (MBR).  The first sector of a partitioned disk always holds an MBR.  If a partition has subpartitions, the first sector of the partition holds an MBR.
5456           static char sub[]= "a";
Expand/Collapse Item5457            char *argp;
argp is specific to DOS.
5458
Expand/Collapse Item5459    /* Copy the boot program to the far end of low memory, this must be
Expand/Collapse Item5460      * done to get out of the way of Minix, and to put the data area
Expand/Collapse Item5461      * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems).
Expand/Collapse Item5462      */
mem[] is declared in boot.h and initialized in boothead.s. mem[] describes the available memory and is the "memory map" that is passed to the kernel.  The boot monitor is removed from the memory map on lines 5482-5488.

caddr, daddr and runsize are also declared in boot.h and initialized in boothead.scaddr is the absolute memory address of the beginning of the boot monitor.  Since the boot monitor is relocated to the upper end of low memory, caddr will change (see line 5473).  daddr is the absolute memory address of the beginning of the data segment of the boot monitor.  runsize is the size (in bytes) of the boot monitor.

5463           u32_t oldaddr= caddr;
5464           u32_t memend= mem[0].base + mem[0].size;
Expand/Collapse Item5465            u32_t newaddr= (memend - runsize) & ~0x0000FL;
The L in ~0x000FL stands for Long.  In Minix, the type long is 32-bits.  ~0x000FL = ~0x0000000F = 0xFFFFFFF0.  Note that ~0x000F (without the L) is equal to 0xFFF0 and not 0xFFFFFFF0.

Since memend and runsize are 32-bit values, newaddr = (memend-runsize) & ~0x000FL will be on a 16-byte boundary.  Since the boot monitor runs in real mode, the beginning of a segment (in this case, the code segment) must be on a 16-byte boundary.

5466   #if !DOS
Expand/Collapse Item5467            u32_t dma64k= (memend - 1) & ~0x0FFFFL;
(memend-1) is the last (upper) byte of low memory.  dma64k = (memend-1) & ~0x0FFFFL rounds down to the nearest 64K boundary.

The figure below demonstrates the scenario that we wish to avoid.

(daddr-caddr) is the size of the code segment.  Since newaddr + (size of code segment) < dma64k (and therefore the data segment crosses a 64KB boundary) in the figure above, an adjustment is necessary. The figure below shows the adjustment.

I don't understand why the data segment of the boot monitor isn't allowed to cross a 64KB boundary.  If you understand why the data segment isn't allowed to cross a 64KB boundary, please submit a comment to the site which will be displayed below.

Name: Christos Basil Karayiannis - Karditsa GREmail:  christos@kar.forthnet.grDate: Feb 15 2004 18:18:36 GMT
Subject:  64KB boundary
Reponse: The Direct Memory Access controller establishes a 'direct' path between the peripherals and main memory.

Lets begin with a short description of the relation between DMA controller and the memory:

The DMA controller holds 16 bit of the memory address (A15-A0). If the DMA didn't use the 'Page Registers' it wouldn't be able to address memory locations higher than 64k.

Until EISA motherboards appeared, there was only one 8-bit page register, used to hold the remaining bits of the address (A23-A16).

The page register allow thus to access any location within 16 Mbytes of memory (2^24). EISA have a better improvement to this scheme (DMA additional page register).

| DMA Page Register | DMA |
---------------------------------------------------------
| A23 - A16 | A15 - A0 |


The DMA controller can't control the page register, thus a 64k page boundary mustn't be crossed in a DMA transfer. This is due to the situation that after bits A0-A15 have reached the maximum, i.e. 0xFFFF (64K), they will be incremented to 0x0000 without the page register being also incremented.

Some of the disk and floppy I/O interrupts in the Boot Monitor, for example 'int 13 ah=2', the 'Read Disk Sectors' interrupt (called by readsectors()) use the DMA. In this interrupt ES:BX is the pointer to buffer that sectors' data are loaded. The advice for using this interrupt is 'be sure ES:BX does not cross a 64K segment boundary or a DMA boundary error will occur'

To avoid this situation the buffers, the Monitor uses, to cross a DMA boundary, the entire data segment of the Monitor is placed between 64K boundaries.
 
Respond to Christos Basil Karayiannis - Karditsa GR's comment.
 
 
5469           /* Check if data segment crosses a 64K boundary. */
5470           if (newaddr + (daddr - caddr) < dma64k) newaddr= dma64k - runsize;
5471   #endif
5472           /* Set the new caddr for relocate. */
5473           caddr= newaddr;
5474
5475           /* Copy code and data. */
Expand/Collapse Item5476            raw_copy(newaddr, oldaddr, runsize);
void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) copies count bytes from absolute memory address srcaddr to absolute memory address dstaddrraw_copy() copies the boot monitor from its current location to the upper end of low memory.
5477
5478           /* Make the copy running. */
Expand/Collapse Item5479            relocate();
relocate() forces the jump to the upper end of low memory. relocate() is an interesting function worthy of careful study.
5480
Expand/Collapse Item5481    #if !DOS
Expand/Collapse Item5482 /* Take the monitor out of the memory map if we have memory to spare,
Expand/Collapse Item5483    * and also keep the BIOS data area safe (1.5K), plus a bit extra for
Expand/Collapse Item5484    * where we may have to put a.out headers for older kernels.
Expand/Collapse Item5485    */
If there is any extended memory (mem[1].size > 0), the boot monitor is taken out of the memory map.  This protects the boot monitor from being overwritten and allows a return to the boot monitor.

If a return to the boot monitor (to either bios13 or int86) from the kernel is made in order to make a bios interrupt call, the bios interrupt vectors must be at memory locations 0x0000-0x03FF and the bios data area  must be at 0x0400-0x04FF.  This accounts for 1.25K.  I don't know about the next .25K and I'm not really sure why .5K is reserved for older a.out headers.  If you understand why the last .75K is reserved, please submit a comment to the site which will be displayed below.

Name: Christos Karayiannis - Karditsa GREmail:  christos@kar.forthnet.grDate: Oct 28 2005 07:08:43 GMT
Subject:  scratch buffer
Reponse: As we see in line 5491 a dev_open() follows. This routine, found also in other places, is implemented in boothead.s. It tries to find the characteristics of the boot device and also if the disk (e.g. a floppy disk) is present, by loading the first sector of the device to the scratch buffer at absolute memory address 0x600. The sector is therefore occupies the area 0x600-0x800. In any case, the practice is to load the Kernel at a 'round' number and the next free is 2K (0x800).
 
Respond to Christos Karayiannis - Karditsa GR's comment.
 
 
5486           if (mem[1].size > 0) mem[0].size = newaddr;
5487          mem[0].base += 2048;
5488           mem[0].size -= 2048;
5489
Expand/Collapse Item5490            /* Set the parameters for the BIOS boot device. */
Expand/Collapse Item5491            (void) dev_open();
devopen() sets sectors and secspcyl for the floppy or hard drive specified by devicesectors is the number of sectors per track of the device. secspcyl is the number of sectors per cylinder of the device.

5492
Expand/Collapse Item5493            /* Find out what the boot device and partition was. */
Lines 5494-5552 fill in bootdev.  Here's a review of 3 possible sets of values for bootdev:

name= "fd1"
device= 0x01
primary= -1
secondary= -1

name= "hd7"
device= 0x81
primary= 1
secondary= -1

name= "hd3a"
device= 0x80
primary= 2
secondary= 0

Expand/Collapse Item5494            bootdev.name[0]= 0;
I'm not sure why name[0] is initialized to 0.  strcpy() on lines 5502 and 5548 doesn't require this initialization.
Expand/Collapse Item5495            bootdev.device= device;
device is set in boothead.s.  The bootstrap (bootblock.s) passes the device number in register dl to the secondary boot (the boot monitor - this program).  The bootstrap also passes the segment:offset address of the partition table entry of the booted partition in es:si.
Expand/Collapse Item5496            bootdev.primary= -1;
Expand/Collapse Item5497            bootdev.secondary= -1;
If device has no partitions (for example, if device is a floppy drive), primary remains -1.  If the hard drive partition is not subpartitioned, secondary remains -1.  (See the examples for the comment for line 5493.)
5498
Expand/Collapse Item5499            if (device < 0x80) {
If device is a floppy drive, name is set to either "fd0" or "fd1".  primary and secondary remain -1.
5500                   /* Floppy. */
5501                   strcpy(bootdev.name, "fd");
Expand/Collapse Item5502                    strcat(bootdev.name, ul2a10(bootdev.device));
char *strcat(s1, s2) concatenates string s2 to the end of string s1 and returns s1strcat() is declared in <string.h>.
5503                   return;
5504           }
5505
5506       /* Get the partition table from the very first sector, and determine
5507        * the partition we booted from using the information from the booted
5508        * partition entry as passed on by the bootstrap (rem_part).  All we
5509        * need from it is the partition offset.
5510        */
Expand/Collapse Item5511            raw_copy(mon2abs(&lowsec),
Expand/Collapse Item5512               vec2abs(&rem_part) + offsetof(struct part_entry, lowsec),
Expand/Collapse Item5513                    sizeof(lowsec));
rem_part is set in boothead.s. rem_part holds the segment:offset address of the partition table entry for the partition being booted.  raw_copy() copies the lowsec field of this partition table entry (see figure below) into the global variable lowsec . vec2abs() converts this segment:offset address into an absolute address.

void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) copies count bytes from absolute memory address srcaddr to absolute memory address dstaddr.

u32_t mon2abs(void *ptr) converts the offset memory address ptr to an absolute memory address.

int offsetof(struct struct1, field) returns the offset of field within struct1part_entry is shown on line 04104 in the book.  Since lowsec is the 9th field after 8 fields of type unsigned char, offsetof(struct part_entry, lowsec) returns 8.  (An unsigned char is 1 byte.)

Since lowsec has type u32_t, sizeof(lowsec) returns 4.

5514
Expand/Collapse Item5515            masterpos= 0;   /* Master bootsector position. */
At this point, it is known that a partition (or subpartition) from hard drive device has been booted and the lowest sector number (lowsec) of this partition (or subpartition) is also known.  However, the partition number (or the subpartition number) is not known.  In other words, it is not known if partition hd3 or subpartition hd2a or subpartition hd4b has been booted.

In order to determine the partition (or subpartition) number, the partition table is copied from the master boot record (MBR) on device's sector 0 and lowsec is compared with the lowsec field of the partition table entries.  If the active partition is subpartitioned, the partition table from the MBR on the first sector of the active partition must also be analyzed.

5516
Expand/Collapse Item5517            for (;;) {
The for loop loops around once if the booted partition is subpartitioned.  If the booted partition is not subpartitioned, the for loop does not loop around.
5518             /* Extract the partition table from the master boot sector. */
Expand/Collapse Item5519                    if ((r= get_master(master, table, masterpos)) != 0) {
int get_master(char *master, struct part_entry **table, u32_t pos) (line 5423) loads the master boot record (MBR) at disk sector number pos into master and loads table[] with pointers to the partition table entries from the partition table in the MBR.  get_master() also sorts the pointers according to the entry's lowsec.  After get_master() returns, the memory layout will be as shown in the figure below.


5520                       readerr(masterpos, r); exit(1);
5521                   }
5522
5523                   /* See if you can find "lowsec" back. */
5524                   for (p= 0; p < NR_PARTITIONS; p++) {
Expand/Collapse Item5525                       if (lowsec - table[p]->lowsec < table[p]->size) break;
If (lowsec - table[p]->lowsec < table[p].size), there are two possibilities (provided there is no error):

1)  lowsec == table[p]->lowsec

The partition or subpartition has been found (see line 5528).

2)  lowsec > table[p]->lowsec

table[p] points to the partition table entry whose partition contains the booted subpartition (i.e. the subpartition with a lowest sector of lowsec).

5526                   }
5527
5528                   if (lowsec == table[p]->lowsec) {       /* Found! */
5529                           if (bootdev.primary < 0)
5530                               bootdev.primary= p;
5531                           else
5532                               bootdev.secondary= p;
5533                           break;
5534                   }
5535
Expand/Collapse Item5536                    if (p == NR_PARTITIONS || bootdev.primary >= 0) {
Something went wrong.  A value of -1 for bootdev.device indicates an error.
5537                    /* The boot partition cannot be named, this only means
5538                     * that "bootdev" doesn't work.
5539                     */
5540                           bootdev.device= -1;
5541                           return;
5542                   }
5543
Expand/Collapse Item5544                    /* See if the primary partition is subpartitioned. */
Unless an error has occurred or will occur, the partition that contains the booted subpartition has been found.  The code loops around to line 5517 to look for the subpartition.
5545                   bootdev.primary= p;
5546                   masterpos= table[p]->lowsec;
5547           }
Expand/Collapse Item5548        strcpy(bootdev.name, "hd");
Expand/Collapse Item5549        strcat(bootdev.name, ul2a10((device - 0x80) * (1 + NR_PARTITIONS)
Expand/Collapse Item5550                                                + 1 + bootdev.primary));
Expand/Collapse Item5551        sub[0]= 'a' + bootdev.secondary;
Expand/Collapse Item5552        if (bootdev.secondary >= 0) strcat(bootdev.name, sub);
Here are 2 examples of (device, primary, secondary) triplets to name conversions:

name= "hd7"
device= 0x81
primary= 1
secondary= -1

name= "hd3a"
device= 0x80
primary= 2
secondary= 0

NR_PARTITIONS (=4) is declared in <ibm/partition.h>.

char *strcat(s1, s2) concatenates string s2 to the end of string s1 and returns s1strcat() is declared in <string.h>.

char *ul2a10(u32_t n) (line 5746) converts n (an unsigned long) to an ascii string (base 10).

5553
Expand/Collapse Item5554    #else /* DOS */
I do not cover DOS-specific sections of code (lines 5555-5593).  I may cover these sections at a later time.
5555      /* Take the monitor out of the memory map if we have memory to spare,
5556       * note that only half our PSP is needed at the new place, the first
5557       * half is to be kept in its place.
5558       */
5559           if (mem[1].size > 0) mem[0].size = newaddr + 0x80 - mem[0].base;
5560
5561           /* Parse the command line. */
5562           argp= PSP + 0x81;
5563           argp[PSP[0x80]]= 0;
5564           while (between('\1', *argp, ' ')) argp++;
5565           vdisk= argp;
5566           while (!between('\0', *argp, ' ')) argp++;
5567           while (between('\1', *argp, ' ')) *argp++= 0;
5568           if (*vdisk == 0) {
5569                   printf("\nUsage: boot <vdisk> [commands ...]\n");
5570                   exit(1);
5571           }
5572           drun= *argp == 0 ? "main" : argp;
5573
5574           if ((r= dev_open()) != 0) {
5575                   printf("\n%s: Error %02x (%s)\n", vdisk, r, bios_err(r));
5576                   exit(1);
5577           }
5578
5579           /* Find the active partition on the virtual disk. */
5580           if ((r= get_master(master, table, 0)) != 0) {
5581                   readerr(0, r); exit(1);
5582           }
5583
5584           strcpy(bootdev.name, "dosd0");
5585           bootdev.primary= -1;
5586           for (p= 0; p < NR_PARTITIONS; p++) {
5587              if (table[p]->bootind != 0 && table[p]->sysind == MINIX_PART) {
5588                           bootdev.primary= p;
5589                           bootdev.name[4]= '1' + p;
5590                           lowsec= table[p]->lowsec;
5591                           break;
5592               }
5593           }
5594   #endif /* DOS */
5595   }
5596
5597   #endif /* BIOS */
5598
Expand/Collapse Item5599    char null[]= "";        /* This kludge saves lots of memory. */
null[] is a single char, '\0'.  This is referred to as the "null string."

In order to signify that a string has no value, the string is set to the null string.  For example, on line 5693, the third argument for b_setenv() is the null string.  The third parameter to b_setenv()(line 5653) is char *arg and is used to pass a function argument.  Since b_setvar() (line 5690) is specifically for variables and for functions without arguments, passing in the null string for char *arg is appropriate.

A null string (i.e. a single char, '\0') could be created each time we wished to signify that a string had no value.  However, as the comment suggests, this would be a significant waste of memory.  On the other hand, care must be taken when memory is freed.  On line 5604, sfree() checks to make sure it's not deallocating the global variable null[], since many variables will undoubtedly be pointed to null[].

5600
Expand/Collapse Item5601    void sfree(char *s)
Expand/Collapse Item5602    /* Free a non-null string. */
For the reasons given for line 5599, care must be taken not to free null[].
5603   {
5604           if (s != nil && s != null) free(s);
5605   }
5606
Expand/Collapse Item5607    char *copystr(char *s)
Expand/Collapse Item5608    /* Copy a non-null string using malloc. */
copystr() allocates memory (using malloc()) for a new string and copies the string s (using strcpy()) to it.  If the string s is a null string, copystr() returns null.
5609   {
5610           char *c;
5611
5612           if (*s == 0) return null;
Expand/Collapse Item5613            c= malloc((strlen(s) + 1) * sizeof(char));
void *malloc(size_t size) allocates size bytes from the heap and returns a pointer to the first byte of the allocated memory. malloc() is declared in stdlib.h.  (The heap is the region between the stack and the bss.)
Expand/Collapse Item5614            strcpy(c, s);
char *strcpy(char *s1,char *s2) copies string s2 to string s1, including '\0' and returns s1.
5615           return c;
5616   }
5617
Expand/Collapse Item5618    int is_default(environment *e)
Lines 5618-5864 are principally concerned with environment variables and functions.  Environment variables and functions for the boot monitor can be broken into 3 categories:

1)  Variables and functions that the minix kernel needs.  Examples are processor, bus, video, and mem[].  These are system parameters that the boot monitor determined that the kernel needs to know.  params2params() in bootimage.c packages the environment variables so that they can be passed as an argument to the minix kernel .

2)  Variables and functions that the boot monitor needs.  These are typically commands that perform a small task.  Examples are leader, main, and trailer (see lines 5825).  The variable image (see line 5826) also falls into this category.

3)  Reserved names.  The names of built-in commands (like boot, menu, and ls) are protected and their function cannot be changed by the set command.

struct environment is declared in boot.h .  The flags field describes the behavior of the environment variable or function.  The following attributes (bits) are the most difficult to understand:

E_RESERVED(=0x04):  Environment variables and functions that have the E_RESERVED bit set cannot be altered.  These are the names of built-in commands (see lines 5837-5846).

E_STICKY(=0x07):  Once the E_STICKY bit is set for a variable or function, the bit cannot be unset.  However, the E_STICKY bit is meaningless otherwise.  The E_STICKY bit is never checked and none of the environment variables or functions set in get_parameters() (line 5773) have the E_STICKY bit set.  The E_STICKY bit was perhaps significant in an earlier version of the boot monitor.

E_SPECIAL(0x01):  An environment variable with the E_SPECIAL bit set cannot be changed to a function and an environment function with the E_SPECIAL bit set cannot be changed to a variable (see lines 5670-5675).  Also, an environment variable or function with the E_SPECIAL bit set "remembers" its initial value.  Before the value field of an E_SPECIAL environment variable or function is changed for the first time, the value field is copied to the defval field.  If the E_SPECIAL environment variable or function is later unset, instead of removing the variable or function from the environment, the defval field is copied back to the value field (see lines 5705-5712).

The defval field of an E_SPECIAL environment variable or function is initially nil and becomes nil again if the field value is restored with its default value (with the unset command).  is_default() can therefore look at the field defval to determine if the environment variable or function has its "default" value.

5619   {
5620           return (e->flags & E_SPECIAL) && e->defval == nil;
5621   }
5622
Expand/Collapse Item5623    environment **searchenv(char *name)
searchenv() searches through the environment variables and functions for name and returns a pointer to a pointer to the corresponding environment.
5624   {
Expand/Collapse Item5625            environment **aenv= &env;
After aenv is initialized to &env, the memory layout is as shown:


5626
Expand/Collapse Item5627            while (*aenv != nil && strcmp((*aenv)->name, name) != 0) {
Here is a slideshow that shows the while loop:
5628                   aenv= &(*aenv)->next;
5629           }
5630
5631           return aenv;
5632   }
5633
Expand/Collapse Item 5634    #define b_getenv(name)  (*searchenv(name))
b_getenv() is a macro that dereferences the return value of searchenv() (line 5623).  The return value of b_getenv() is a pointer to an environment rather than a pointer to a pointer to an environmentb_getenv() is used in the next function, b_value() (line 5637).
xvc
5635/* Return the environment *structure* belonging to name, nil if not found.*/
5636
Expand/Collapse Item5637    char *b_value(char *name)
Expand/Collapse Item5638    /* The value of a variable. */
b_value() returns the value of the environment variable name or nil (0) if it is a function or it doesn't exist.
5639   {
5640           environment *e= b_getenv(name);
5641
5642           return e == nil || !(e->flags & E_VAR) ? nil : e->value;
5643   }
5644
Expand/Collapse Item5645    char *b_body(char *name)
Expand/Collapse Item5646    /* The value of a function. */
b_body() returns the value of the environment function name or nil (0) if it is a variable or it doesn't exist.
5647   {
5648           environment *e= b_getenv(name);
5649
5650           return e == nil || !(e->flags & E_FUNCTION) ? nil : e->value;
5651   }
5652
Expand/Collapse Item5653    int b_setenv(int flags, char *name, char *arg, char *value)
Expand/Collapse Item5654 /* Change the value of an environment variable.  Returns the flags of the
Expand/Collapse Item5655     * variable if you are not allowed to change it, 0 otherwise.
Expand/Collapse Item5656     */
b_setenv() changes the value of an environment variable or function or creates a new environment variable or function.

The flags field of environment describes the behavior of the environment variable or function.  The following attributes (bits) are the most difficult to understand:

E_RESERVED(=0x04):  Environment variables and functions that have the E_RESERVED bit set cannot be altered.  These are the names of built-in commands (see lines 5837-5846).  (See comments for lines 5696-5699).

E_STICKY(=0x07):  Once the E_STICKY bit is set for a variable or function, the bit cannot be unset.  However, the E_STICKY bit is meaningless otherwise.  The E_STICKY bit is never checked and none of the environment variables or functions set in get_parameters() (line 5773) have the E_STICKY bit set.  The E_STICKY bit was perhaps significant in an earlier version of the boot monitor.

E_SPECIAL(0x01):  An environment variable with the E_SPECIAL bit set cannot be changed to a function and an environment function with the E_SPECIAL bit set cannot be changed to a variable (see lines 5670-5675).  Also, an environment variable or function with the E_SPECIAL bit set "remembers" its initial value.  Before the value field of an E_SPECIAL environment variable or function is changed for the first time, the value field is copied to the defval field.  If the E_SPECIAL environment variable or function is later unset, instead of removing the variable or function from the environment, the defval field is copied back to the value field (see lines 5705-5712).

5657   {
5658           environment **aenv, *e;
5659
Expand/Collapse Item5660            if (*(aenv= searchenv(name)) == nil) {
If *aenv==nil, the environment variable name does not exist and must be created.
Expand/Collapse Item5661                    e= malloc(sizeof(*e));
The sizeof operator is flexible.  The argument for sizeof can be a type (like int, char, etc.), a variable, or a dereferenced pointer (in this case, *e).

As noted above, sizeof is an operator and not a function.  What this means is that sizeof is a part of the C language itself, and not a function that needs to be declared in a header file.

Expand/Collapse Item5662                    e->name= copystr(name);
copystr() (line 5607) allocates memory (using malloc()) for a new string and copies the string name (using strcpy()) to it.
5663                   e->flags= flags;
5664                   e->defval= nil;
5665                   e->next= nil;
5666                   *aenv= e;
Expand/Collapse Item5667            } else {
The environment variable name already exists.  Be careful with E_RESERVED and E_SPECIAL environment variables and functions.
5668                   e= *aenv;
5669
Expand/Collapse Item5670                    /* Don't touch reserved names and don't change special
Expand/Collapse Item5671                     * variables to functions or vv.
Expand/Collapse Item5672                     */
E_RESERVED(=0x04):  Environment variables and functions that have the E_RESERVED bit set cannot be altered.  These are the names of built-in commands (see lines 5837-5846).  (See comments for lines 5696-5699).

E_SPECIAL(0x01):  An environment variable with the E_SPECIAL bit set cannot be changed to a function and an environment function with the E_SPECIAL bit set cannot be changed to a variable (see lines 5670-5675).  Also, an environment variable or function with the E_SPECIAL bit set "remembers" its initial value.  Before the value field of an E_SPECIAL environment variable or function is changed for the first time, the value field is copied to the defval field.  If the E_SPECIAL environment variable or function is later unset, instead of removing the variable or function from the environment, the defval field is copied back to the value field (see lines 5705-5712).

5673                   if (e->flags & E_RESERVED || (e->flags & E_SPECIAL
5674                           && (e->flags & E_FUNCTION) != (flags & E_FUNCTION)
5675                   )) return e->flags;
5676
Expand/Collapse Item5677                    e->flags= (e->flags & E_STICKY) | flags;
E_STICKY(=0x07):  Once the E_STICKY bit is set for a variable or function, the bit cannot be unset.  However, the E_STICKY bit is meaningless otherwise.  The E_STICKY bit is never checked and none of the environment variables or functions set in get_parameters() (line 5773) have the E_STICKY bit set.  The E_STICKY bit was perhaps significant in an earlier version of the boot monitor.
5678                   if (is_default(e)) {
Expand/Collapse Item5679                            e->defval= e->value;
As noted above, an environment variable or function with the E_SPECIAL bit set "remembers" its initial value.  Before the value field of an E_SPECIAL environment variable or function is changed for the first time, the value field is copied to the defval field.  If the E_SPECIAL environment variable or function is later unset, instead of removing the variable or function from the environment, the defval field is copied back to the value field (see lines 5705-5712).
5680                   } else {
Expand/Collapse Item5681                           sfree(e->value);
e->value gets its new value on line 5686.
5682                   }
Expand/Collapse Item5683                    sfree(e->arg);
e->arg gets its new value on line 5685.
5684           }
5685           e->arg= copystr(arg);
5686           e->value= copystr(value);
5687           return 0;
5688   }
5689
Expand/Collapse Item5690    int b_setvar(int flags, char *name, char *value)
Expand/Collapse Item5691    /* Set variable or simple function. */
arg for int b_setenv(int flags, char *name, char *arg, char *value) doesn't exist for a variable or a simple function (i.e. a function without arguments).  null is passed in for arg.
5692   {
5693           return b_setenv(flags, name, null, value);
5694   }
5695
Expand/Collapse Item5696    void b_unset(char *name)
Expand/Collapse Item5697 /*Remove a variable from the environment.  A special variable is reset to
Expand/Collapse Item5698     * its default value.
Expand/Collapse Item5699     */
E_RESERVED(=0x04):  Environment variables and functions that have the E_RESERVED bit set cannot be altered.  These are the names of built-in commands (see lines 5837-5846).

E_SPECIAL(0x01):  An environment variable with the E_SPECIAL bit set cannot be changed to a function and an environment function with the E_SPECIAL bit set cannot be changed to a variable (see lines 5670-5675).  Also, an environment variable or function with the E_SPECIAL bit set "remembers" its initial value.  Before the value field of an E_SPECIAL environment variable or function is changed for the first time, the value field is copied to the defval field.  If the E_SPECIAL environment variable or function is later unset, instead of removing the variable or function from the environment, the defval field is copied back to the value field (see lines 5705-5712).

Strangely enough, b_unset() does not check to see if it's unsetting an E_RESERVED environment variable.

hd2a>name1() {echo hello}
hd2a>name1
hello
hd2a>boot() {echo hello}
boot is a reserved word
hd2a>unset boot
hd2a>boot() {echo hello}
hd2a>boot

(system boots)

Since execute() (see line 6535) checks for built-in commands (like "boot") before user-defined commands, the built-in boot takes precedence over the user-defined boot and the system boots instead of echoing "hello".

5700   {
5701           environment **aenv, *e;
5702
5703           if ((e= *(aenv= searchenv(name))) == nil) return;
5704
5705           if (e->flags & E_SPECIAL) {
5706                   if (e->defval != nil) {
5707                          sfree(e->arg);
5708                           e->arg= null;
5709                          sfree(e->value);
5710                           e->value= e->defval;
5711                           e->defval= nil;
5712                   }
5713           } else {
5714                   sfree(e->name);
5715                   sfree(e->arg);
5716                   sfree(e->value);
Expand/Collapse Item5717                    *aenv= e->next;
B_unset slide show
5718                   free(e);
5719           }
5720   }
5721
Expand/Collapse Item5722    long a2l(char *a)
Expand/Collapse Item5723    /* Cheap atol(). */
a2l() converts a string (like "-1023") to a long.

I'm not sure why this function is described as "cheap."  Perhaps atol() (from <stdlib.h>) performs error-checking. a2l() performs no error-checking.

5724   {
5725           int sign= 1;
5726           long n= 0;
5727
5728           if (*a == '-') { sign= -1; a++; }
5729
Expand/Collapse Item5730            while (between('0', *a, '9')) n= n * 10 + (*a++ - '0');
between() (line 5048) returns TRUE if *a (the value at address a) is between '0' and '9'.

*a++ dereferences pointer a and then increments a.  It does not increment the value at address a.

5731
5732           return sign * n;
5733   }
5734
Expand/Collapse Item5735    char *ul2a(u32_t n, unsigned b)
Expand/Collapse Item5736    /* Transform a long number to ascii at base b, (b >= 8). */
ul2a() converts an unsigned long to an ascii string. b specifies the format of the number.  For example, if b=0x10=16, n=23 is converted to "17"; if b=0x08=8, n=23 is converted to "27".

The ascii string is held in num[], which is 12 char's (bytes) (see comments for line 5738).  If b < 8, 12 bytes is not enough to hold the largest possible value of n.

5737   {
Expand/Collapse Item5738            static char num[(CHAR_BIT * sizeof(n) + 2) / 3 + 1];
CHAR_BIT (=8) is declared in <limits.h>.  It is the number of bits in a char.

(CHAR_BIT*sizeof(n)+2)/3 + 1 = (8*4+2)/3 + 1
 = 34/3 + 1 = 12
(Remember, this is integer math - in other words, fractions are discarded.)

An internal static variable retains its value from one invocation of the function to the next.  The static declaration has a different meaning for external variables (see comments for line 5057).

Expand/Collapse Item5739            char *a= arraylimit(num) - 1;
The least significant digit is determined first.  We start at the end of num[] and work our way back.

% is the modulus (remainder) operator.  11%3=2.

*a-- dereferences pointer a and then decrements a. It does not decrement the value at address a.

5740           static char hex[16] = "0123456789ABCDEF";
5741
5742           do *--a = hex[(int) (n % b)]; while ((n/= b) > 0);
5743           return a;
5744   }
5745
5746   char *ul2a10(u32_t n)
5747   /* Transform a long number to ascii at base 10. */
5748   {
5749           return ul2a(n, 10);
5750   }
5751
Expand/Collapse Item5752    unsigned a2x(char *a)
Expand/Collapse Item5753    /* Ascii to hex. */
a2x() converts ascii strings in hexadecimal notation to unsigned's.  The comment "Ascii to hex" is a little confusing.
5754   {
5755           unsigned n= 0;
5756           int c;
5757
5758           for (;;) {
5759                   c= *a;
5760                   if (between('0', c, '9')) c= c - '0' + 0x0;
5761                   else
5762                   if (between('A', c, 'F')) c= c - 'A' + 0xA;
5763                   else
5764                   if (between('a', c, 'f')) c= c - 'a' + 0xa;
5765                   else
5766                           break;
Expand/Collapse Item5767                    n= (n<<4) | c;
Shifting a value to the left 4 bits is the equivalent of multiplying the value by 16.
5768                   a++;
5769           }
5770           return n;
5771   }
5772
Expand/Collapse Item5773    void get_parameters(void)
get_parameters() is called in boot() (see line 6611).  The name get_parameters() is a little misleading since it sets many environment variables and functions in addition to getting parameters (from the bootparams sector - see line 5848).
5774   {
Expand/Collapse Item5775            char params[SECTOR_SIZE + 1];
readsectors() (see line 5849) reads the bootparams sector into params[].  The "+1" is for the terminating '\0'.
Expand/Collapse Item5776            token **acmds;
cmds (line 5382) points to the first token.  acmds points to where another token and another line can be added (see line 5854).
5777           int r;
5778           memory *mp;
Expand/Collapse Item5779            static char bus_type[][4] = {
An internal static variable (like the array bus_type[][]) retains its value from one invocation of the function to the next.  The static declaration has a different meaning for global variables (see comments for line 5057).

bus_type[0][0] = 'x'; bus_type[0][1] = 't'; bus_type[0][2] = '\0'

bus_type could have been declared as bus_type[3][4] instead of bus_type[][4] since there are 3 rows: "xt", "at", and "mca".  Instead, the compiler determines the number of rows and does the work for us.  4 is the number of char's (bytes) that the largest row ("mca") takes up ('m', 'c', 'a', '\0').

5780                   "xt", "at", "mca"
5781           };
5782           static char vid_type[][4] = {
5783                   "mda", "cga", "ega", "ega", "vga", "vga"
5784           };
5785           static char vid_chrome[][6] = {
5786                   "mono", "color"
5787           };
5788
Expand/Collapse Item5789            /* Variables that Minix needs: */
The E_SPECIAL and E_RESERVED variables are set here.  Here's a review:

E_SPECIAL(0x01):  An environment variable with the E_SPECIAL bit set in the flags field (of environment cannot be changed to a function and an environment function with the E_SPECIAL bit set cannot be changed to a variable (see lines 5670-5675).  Also, an environment variable or function with the E_SPECIAL bit set "remembers" its initial value.  Before the value field of an E_SPECIAL environment variable or function is changed for the first time, the value field is copied to the defval field.  If the E_SPECIAL environment variable or function is later unset, instead of removing the variable or function from the environment, the defval field is copied back to the value field (see lines 5705-5712).

E_RESERVED(=0x04):  Environment variables and functions that have the E_RESERVED bit set cannot be altered.  These are the names of built-in commands (see lines 5837-5846).  (See comments for lines 5696-5699).

params2params() in bootimage.c packages the environment variables so that they can be passed as an argument to the minix kernel.

A good description of these environment variables and boot monitor commands can be found in the man page.

5790           b_setvar(E_SPECIAL|E_VAR|E_DEV, "rootdev", "ram");
5791           b_setvar(E_SPECIAL|E_VAR|E_DEV, "ramimagedev", "bootdev");
5792           b_setvar(E_SPECIAL|E_VAR, "ramsize", "0");
5793   #if BIOS
Expand/Collapse Item5794            b_setvar(E_SPECIAL|E_VAR, "processor", ul2a10(getprocessor()));
get_processor() returns 86 for an 8086, 286 for an 80286, etc.

ula10() (line 5746) converts an unsigned long to an ascii string with base 10.

5795           b_setvar(E_SPECIAL|E_VAR, "bus", bus_type[get_bus()]);
5796           b_setvar(E_SPECIAL|E_VAR, "video", vid_type[get_video()]);
5797           b_setvar(E_SPECIAL|E_VAR, "chrome", vid_chrome[get_video() & 1]);
5798           params[0]= 0;
Expand/Collapse Item5799            for (mp= mem; mp < arraylimit(mem); mp++) {
mem[] was set in boothead.s.  The value field of environment for memory will be something like:  "800:7FF00,100000:800000,1000000:400000" (Note that this is a string).  The first of the three (base,size) pairs is for lower memory (memory under 1MB).  The second (base, size) pair is for memory between 1MB and 16MB.  The third (base, size) pair is for memory greater than 16MB.  If the memory of the second and third regions is contiguous, then the memory from the third region is aggregated into the second region.  If there is no memory above 1MB, then there will only be a single (base, size) pair.
5800                   if (mp->size == 0) continue;
5801                   if (params[0] != 0) strcat(params, ",");
5802                   strcat(params, ul2a(mp->base, 0x10));
5803                   strcat(params, ":");
5804                   strcat(params, ul2a(mp->size, 0x10));
5805           }
5806           b_setvar(E_SPECIAL|E_VAR, "memory", params);
Expand/Collapse Item5807    #if DOS
I do not cover DOS-specific sections of code (line 5808).  I may cover these sections at a later time.
5808           b_setvar(E_SPECIAL|E_VAR, "dosd0", vdisk);
5809   #else /* !DOS */
Expand/Collapse Item5810            /* Obsolete memory size variables. */
memory (see line 5806) replaces memsize and emssize.
5811           b_setvar(E_SPECIAL|E_VAR, "memsize",
5812                               ul2a10((mem[0].base + mem[0].size) / 1024));
5813           b_setvar(E_SPECIAL|E_VAR, "emssize", ul2a10(mem[1].size / 1024));
5814   #endif
5815
5816   #endif
Expand/Collapse Item5817    #if UNIX
I do not cover UNIX-specific sections of code (lines 5818-5822).  I may cover these sections at a later time.
5818           b_setvar(E_SPECIAL|E_VAR, "processor", "?");
5819           b_setvar(E_SPECIAL|E_VAR, "bus", "?");
5820           b_setvar(E_SPECIAL|E_VAR, "video", "?");
5821           b_setvar(E_SPECIAL|E_VAR, "chrome", "?");
5822           b_setvar(E_SPECIAL|E_VAR, "memory", "?");
5823   #endif
5824
5825           /* Variables boot needs: */
Expand/Collapse Item5826            b_setvar(E_SPECIAL|E_VAR, "image", "minix");
minix is not an image file but a directory (/minix).  The boot monitor searches through the /minix directory and boots the image file with the most recent modification time.  To boot a specific image, reset image:

hd2a> image = /minix/minix_386_09282000

Expand/Collapse Item5827            b_setvar(E_SPECIAL|E_FUNCTION, "leader",
Expand/Collapse Item5828                    "echo \\cM