/ $Header: /newbits/conf/boot/RCS/boot.m,v 1.4 91/11/08 13:18:38 bin Exp Locker: bin $ / (lgl- / The information contained herein is a trade secret of Mark Williams / Company, and is confidential information. It is provided under a / license agreement, and may be copied or disclosed only under the / terms of that agreement. Any reproduction or disclosure of this / material without the express written authorization of Mark Williams / Company or persuant to the license agreement is unlawful. / / COHERENT Version 3.2.1 / Copyright (c) 1982, 1983, 1984, 1991. / An unpublished work by Mark Williams Company, Chicago. / All rights reserved. / -lgl) //////// / / Bootstrap block for the IBM / PC. This code, which lives in sector 1, / track 0 of the floppy (or sector 1 of a hard disk partition) / gets read into location 0x7C00 by the ROM. / The code relocates itself to location 0x2060:0x0000 / (128K above the fixed memory used by the ROM). The system image is read / in beginning at paragraph 0x0060. / / $Log: boot.m,v $ / * Revision 1.4 91/11/08 13:18:38 bin / * Piggy update for building secondary boot for tboot / * / Revision 1.7 91/11/08 11:31:13 piggy / Attempted to integrate pboot into these sources. Still not working. / Updated secondary boot for use with tertiary boot code. / / Revision 1.4 91/09/17 10:35:59 piggy / Uses 4 byte disk blocks, so can boot from anywhere on disk. / Points ds:si at useful data for tertiary boot. / Loads /tboot by default. / / Revision 1.1 88/03/24 16:44:09 src / Initial revision / / 87/10/04 Allan Cornish /usr/src/sys/i8086/boot/boot.s / Number of heads now defined by NHD macro. Should be 1 for f*d*, 2 for f*a*. / / 85/10/04 Allan Cornish /usr/src/sys/i8086/boot/boot.s / Bootstrap extended to increase max file size from 64 Kbytes to 128 Kbytes. / The private code segment is now loaded, where previously it had to be empty. / File name editing enhanced to help cursor stay in the 14 char entry field, / and to provide a destructive backspace. / / 85/01/04 Allan Cornish / Added backspace editing of file name. / //////// #ifndef NHD #define NHD 1 / num of heads per drive [1 for f9d0]. #endif #ifndef NSPT #define NSPT 9 / num of sectors per track on floppy. #define NTRK 40 / num of tracks on floppy. #endif #ifndef CNTRL #define CNTRL 8 #endif #ifndef WPCC #define WPCC 0xFFFF #endif NSTK = 256 / num of bytes of stack. BS = 0x08 / BS character. CR = 0x0D / CR character. LF = 0x0A / LF character. SP = 0x20 / Backspace character. BOOTLC = 0x7C00 / Boot location (ROM). BOOTS = 0 / boot segment (ROM). RBOOTS = 0x2060 / Relocated boot segment. BSHIFT = 9 / Shift, bytes to blocks. ROOTINO = 2 / Root inode # INOORG = 2 / First inode block. IBSHIFT = 3 / Shift, inode to blocks IOSHIFT = 6 / Shift, inode to bytes INOMASK = 0x0007 / Mask, inode to offset DIRSIZE = 14 / Directory size. DIRSIZ2 = 7 / Directory size / 2. BUFSIZE = 512 / Block size. HDRSIZE = 44 / L.out header size. HDRSIZ2 = 22 / L.out header size / 2. BUFMSK2 = 0x00FF / Block [word] mask, for reads. DISK = 0x13 / Disk Interrupt KEYBD = 0x16 / Keyboard Interrupt READ1 = 0x0201 / read one sector DADDR = 4 / number of bytes in a disk block number ND = 10 / num of direct blocks. NIND = 128 / num of blocks in indirect block. NINDX2 = 256 / NIND * 2 (number of words in indirect) ISIZE = 8 / Offset of "di_size". IADDR = 12 / Offset of "di_addr". JMPF = 0xEA / Jump far, direct. LMAGIC = 0 / Offset of "l_magic" LFLAG = 2 / Offset of "l_flag" LSHRI = 10 / Offset of "l_ssize[SHRI]" LPRVI = 14 / Offset of "l_ssize[PRVI]" LSHRD = 22 / Offset of "l_ssize[SHRD]" LPRVD = 26 / Offset of "l_ssize[PRVD]" LBSSD = 30 / Offset of "l_ssize[BSSD]" LFMAG = 0x0107 / Magic number. LFSEP = 0x02 / Sep I/D flag bit. SYSBASE = 0x0060 / System load base paragraph. FIRST = 8 / relative start of partition / Hard disk controller port addresses. HF_REG = 0x3F6 NSEC_REG= 0x1F2 SEC_REG = 0x1F3 HDRV_REG= 0x1F6 CSR_REG = 0x1F7 BSY_ST = 0x80 SETPARM_CMD= 0x91 //////// / Overall structure of boot.s (the secondary boot): / / boot: relocate secondary to high memory (0x2060:0x0000) / if needed, get hard disk parameters from bios / jump far to high memory (RBOOTS:entry) / entry: open directory "/" / search: while filename not found / walk through "/" looking for filename (in nbuf) / if not found, ask for another name (call input:), and loop to search / found: open file that we just found / read the l.out header (verify magic number, get partition sizes) / load the shared and private code segments as one (call load:) / load the initialized data segment (call load:) / jump far to the newly loaded program / //////// //////// / / Bootstrap mainline. Relocate the / boot to high memory, so it will not be written / over by the system. Read in a file name. Look up / the file name by following out the directory / structure. Read in the image and jump to it. / //////// boot: xor di, di mov ds, di mov si, $BOOTLC / Make DS:SI point at the code / "mov es, 1f(si)" really means "mov es, $RBOOTS". Clever Intel... :-( mov es, 1f(si) / Make ES:DI point at where it goes mov cx, $512 / cx = # of bytes to move cld rep / Move the bootstrap movsb / to high memory. / Check to see if we were invoked by mboot. (That means we are booting / off a hard disk and need to load disk parameters. ) / mboot sets bx = sp cmp bx, sp / trick to tell jne 0f / whether or not xt / Assertion: at this point we know we are booting off a hard disk. mov si, bp / set si to partition table movb dl, (si) / get drive number movb ah, $8 / get drive parameters int DISK jc 0f / abort on error movb al, ch / fetch cyl(lo) movb ah, cl / move cyl(hi), sects rolb ah, $1 / shift cylinder high to rolb ah, $1 / the least sig bits andb ah, $3 / mask out cylinder bits mov di, $traks / point to drive stosw / set number of tracks movb al, $0x3F / sector mask andb al,cl / mask sector stosb / set sector movb al, dh / get max head inc ax / change to # of heads [incb al] stosb / set number of heads movsb / set drive add si, $FIRST-1 / point to first block movsw movsw / fetch first block / Set registers. 0: mov bp, $stack+NSTK / stack pointer value mov ax, es mov ds, ax mov ss, ax mov sp, bp / Jump to (relocated) boot. .byte JMPF .word entry 1: .word RBOOTS //////// / / This is the equivalent of "open()", / but takes an inode instead of a pathname. / / Read the inode specified by "ax" / into the external variable "iaddr". / / Makes a list of block numbers in "iaddr" / consisting of the 10 direct block numbers, the 128 / block numbers pointed to the singly indirect block, / and the first 128 block numbers pointed to by the / first doubly indirect block. / / Note that "iread" can only access the first 256 / block numbers in this list, so the last 10 are / ignored. / / Note that block numbers are actually 32 bits on disk-- / this routine only uses the lower 16 bits. / In fact, iaddr is loaded with 16 bit numbers, not 32 bit. / / No checking is done to make sure that / the inumber is in range on the filesystem. / Sets dx and cx to 0. / Mungs si, di, ax. / //////// igrab: dec ax / Make origin 0 and push ax / remember for later use. / We assume that the inode table appears in the first 32 meg of disk. / Look up the inode in the inode table. movb cl, $IBSHIFT / Convert to shr ax, cl / inode block number, inc ax / then to physical block number, inc ax sub bx, bx / Assume inode within first 32 meg. call bread / and read in the data. pop ax / Get i-number back. and ax, $INOMASK / Get inode within block movb cl, $IOSHIFT / and convert to shl ax, cl / a byte offset in the block. add si, ax / si = inode pointer. / Assertion: si now points at the start of the desired / inode structure (see <sys/inode.h>). / Copy out the direct block numbers to iaddr. add si, $IADDR / Point at address field. mov di, $iaddr / Copy out area. / The block numbers in the inode on disk are stored as / 3 bytes: msb, lsw. lsw is lsb, msb. We want to store them / in memory as msw, lsw, just like the inodes in the indirect / blocks on disk. So we need to cast msb as msw. movb cl, $ND / cx = # of direct blocks sub ax, ax / cast byte in al to word in ax 0: lodsb / al = msb for direct byte stosw / store msb cast as a word movsw / store lsw. loop 0b lodsb / Get the msb for the indirect block. xchg bx, ax / (implicit cast of msb to msw) lodsw / Get the lsw for the indirect block. call bread / Read (first) indirect block. / Copy out the singly indirect block numbers to iaddr. / These are stored on disk as 4 byte numbers: msw, lsw / where each word is lsb, msb. This is how we store them / in memory. mov cx, $NINDX2 rep movsw / Finish by reading the first block of the file into bbuf. sub dx, dx / Set dh to first block number in iaddr. / jmp iread / Done return through iread. //////// / / This routine reads the virtual / block iaddr[dh] (the dhth block number / in iaddr) into the buffer "bbuf". / It uses the mapping data that / has been provided by a previous call to igrab / On return "si" points at "bbuf". / Holes in files (sparse blocks) are correctly done. / mungs ax, bx, si. / clears cx. / /////// iread: sub bx, bx / Convert from words to blocks. movb bl, dh / / Block numbers are 4 bytes long, stored msw, lsw. shl bx, $2 / Compute index into iaddr table. push iaddr(bx) / fetch msw inc bx inc bx mov ax, iaddr(bx) / fetch lsw pop bx / put msw where it belongs / jmp bread / Yes, return through "bread". //////// / / Read a block from the floppy disk, / drive A:, using the code in the IBM firmware. / The physical block # is in ax and bx. / Register bx is the MSW, and ax is the LSW. / //////// bread: push es / Save registers push di push dx push ds pop es / set ES to the address of the buffer mov di, bp / Blast the buffer contents. mov cx, $BUFSIZE / For block 0, this fills the buffer rep / with zeros. stosb / Block #0 is the sparse block--it means a block of all zeros. test ax, ax / if block 0, return zeroed buffer jnz 3f test bx, bx jz 2f / Translate block number into cylinder, head, and sector. 3: push bx pop dx / xor dx, dx / extend block number add ax, first / add first block adc dx, first+2 / add rest mov bx, ax / save block number movb al, heads / get number of heads movb cl, sects / get number of sectors mulb cl / calculate sectors per cylinder xchg bx,ax / swap block/sectors div bx / calculate track xchg dx, ax / put track in DX divb cl / calculate head/sector movb cl, ah / set sector inc cx / sectors start at 1 [incb cl] cmp dx, traks / check for second side jb 0f sub dx, traks / fold track inc ax / next head [incb al] 0: rorb dh, $1 / rotate track(low) into rorb dh, $1 / msbits of DX orb cl, dh / set track(high) movb ch, dl / set track(low) movb dh, al / set head movb dl, drive / set drive mov bx, bp / set offset [bbuf] mov ax, $READ1 / Read, 1 sector. int DISK / Disk I/O. jnc 2f / Jump if no error. mov ax, $READ1 / try again int DISK jc error 2: mov si, bp / set SI to point to buffer for return sub cx, cx / clear CX here to save space pop dx / restore registers. pop di pop es ret / return. / Prompt with a "?" and read the file name / into "nbuf". No character editing facilites / are provided. error: mov sp, bp / Reset stack input: mov di, $nbuf / di=name buffer pointer mov cx, $DIRSIZE / cx=size of / Preprocessor blows up if we use single-quote question mark. movb al, $0077 / set prompt to ?. 0: call putc 1: movb ah, $0 / Get ASCII opcode. int KEYBD / Read keyboard ROM call. 2: cmpb al, $CR / CR ? je 3f / Yup, do next thing jcxz 1b / skip over too big name stosb / put it into the buffer, dec cx / adjust char count jmp 0b / and continue. 3: movb al, $CR call putc movb al, $LF call putc movb al, $0 / zero out rep stosb / rest of name //////// / This is where we jump after boot has been relocated. / / Open "/"; we are going to search for "autoboot". //////// entry: mov ax, $ROOTINO / ax = current inode call igrab / Read in inode and set dx //////// / Search directory for filename in nbuf. / Assume directory size = 64 Kbytes. / This assumption works because block numbers / from the inode beyond end of file are zero. //////// search: orb dl, dl / On block boundry ? jnz 0f / Nope. call iread / Read block, set si. 0: movb cl, $DIRSIZE / cx = count left mov di, $nbuf / Point at name buffer and lodsw / ax = inumber, this entry. or ax, ax / Empty directory slot ? je 1f / Yes, skip. repe / compare the cmpsb / two file names. je found / If eq, go and get inode. 1: add si, cx / Advance by count remaining. add dx, $DIRSIZ2+1 / Bump [word] seek pointer jg search / Scan up to 64 Kbytes of directory. jmp input / File not found. //////// / / Found the file so read it in. / ax = inode number of file / //////// found: call igrab / read in inode and first block cmp LMAGIC(si),$LFMAG / Check the magic number. jne error / not Ok. / push LBSSD(si) / Push the uninitialized data size. / Get the total size of shared and private data segments. / They are contigious on disk. see <l.out.h> mov ax, LSHRD(si) / Push the sum add ax, LPRVD(si) / of the shared and private push ax / [initialized] data sizes. / If this is not a sep I/D executable, then we / do not want to load the non-existent data segment. andb LFLAG(si), $LFSEP / check image flags. pushf / Push result of test. mov ax, LSHRI(si) / Get the sum of the add ax, LPRVI(si) / shared and private code sizes. / Find the start of the first segment on disk. mov dx, $HDRSIZ2 / Seek after header and add si, $HDRSIZE / set buffer pointer appropriately. mov es, 1f / Set ES:DI to point to the load sub di, di / base of the new system. / Load the code segment. call load / Load code. / Skip loading the data segment for non-sep I/D executables. popf / Pop sep I/D flag test jz 0f / Not sep. / Assertion: at this point we are dealing with a sep I/D / l.out executable. / Calculate load point for data segment. add di, $15 / Round up the system code movb cl, $4 / size to 16 byte shr di, cl / paragraphs. mov ax, es / fetch program base add ax, di / Compute the data base and mov es, ax / set up ES:DI to point sub di, di / at it. 0: pop ax / Pop off initialized data size and / Load the initialized data segment. call load / load the image. mov si, $useful / Point ds:si at the useful data / for the tertiary boot. / pop cx / Pop off uninitialized data size and / rep / clear it. / stosb / Run the program that we just loaded. .byte JMPF / Jump to offset .word 0x0100 / 0x0100 (after base) in system 1: .word SYSBASE / code segment. //////// / / Load a segment from an l.out file into memory. / The "dx" register holds the / seek pointer into the file. The "si" holds / a pointer into the "bbuf". The "es:di" pair / holds the target address. The "ax" holds / the number of bytes to load. On return the / "ax" must equal 0 (the caller assumes this / and uses "al" to clear the BSS). / / Note that since the number of bytes to load is / stored in a 16 bit register, a segment may be no / longer than 64K. For small model this is just fine. //////// load: or ax, ax / Any left ? jz return / Jump if all loaded. mov cx, $bbuf+BUFSIZE / Compute the number of bytes sub cx, si / remaining in the block. jnz 0f / Jump if some. push ax / Save count registers, then call iread / read the next block pop ax / of the file. mov cx, $BUFSIZE / We now have a full block. 0: cmp cx, ax / More than we need ? jbe 1f / Nope. mov cx, ax / Only take what we need. 1: sub ax, cx / Fix up the count shr cx, $1 / add dx, cx / Fix up the seek [word] address, then rep / copy the words from the block movsw / buffer to the load point and jmp load / loop until done. //////// / / Write the character in "al" out to / the display, using routines in the ROM. / Like most calls to the ROM, this routine spends / most of its time saving and restoring the / registers. / / Note: The ZF in the PSW word must be preserved. / //////// putc: push si / Save registers. push di push bp mov bx, $0x0007 / Page 0, white on black movb ah, $0x0E / Write TTY. int 0x10 / Call video I/O in ROM. pop bp / Restore registers. pop di pop si return: ret / Return //////// / / Data. There is some pure data in / the "prvd" segment. This data is moved to the / new memory when the boot is relocated. All buffers / are in the BSS, and are not actually moved, or even / saved in the boot block. / //////// .prvd / Do not rearrange ANY of the following variables. If you / need to add variables, they must come BEFORE or AFTER these. / If you want to teach the tertiary boot about any variables you / add, put them after double word "first". .even useful: / Useful variables for the tertiary boot. nbuf: .ascii "tboot" / Name buffer. .blkb DIRSIZE-5 / rest of buffer (5=sizeof("tboot")) traks: .word NTRK / Number of cylinders on drive we are booting off of. sects: .byte NSPT / Number of sectors per track for our drive. heads: .byte NHD / Number of heads on drive we are booting off of. drive: .byte 0 / Drive our partition resides upon. first: .word 0 / First block of our partition (?) .word 0 cntrl: .byte CNTRL / Control byte wpcc: .word WPCC / / This magic pair of bytes must be the last two bytes of / the sector (address 0x1FE), otherwise mboot will refuse / to execute it. / If needed, uncomment the .blkb and adjust the number appropriately. .blkb 0x28 / Padding needed to make magic byte line up .byte 0x55,0xAA .bssd stack: .blkb NSTK / Local Stack and name buffer bbuf: .blkb BUFSIZE / Block buffer [must follow stack]. iaddr: .blkw ND+ND+NIND+NIND / Inode map words.