//////// / / Patched 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: /newbits/conf/boot/RCS/pboot.m,v $ * Revision 1.2 91/05/29 09:42:16 bin * bob h modified due to commented lines screwing the makefile * / * Revision 1.1 91/05/29 08:30:45 bin / * Initial revision / * / 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. / //////// //////// / This is a patched version for machines with incorrect CMOS disk parameters. / The values in traks, sects, heads, cntrl and wpcc are assumed correct. / This BIOS is not interrogated to find the disk parameters. / The controller is reset with the appropriate values. //////// #ifndef NHD #define NHD 1 / heads per drive [1 for f9d0]. #endif #ifndef NSPT #define NSPT 9 / sectors per track on floppy. #define NTRK 40 / tracks on floppy. #endif #ifndef CNTRL #define CNTRL 8 #endif #ifndef WPCC #define WPCC 0xFFFF #endif NSTK = 256 / 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 ND = 10 / # of direct blocks. NIND = 128 / # of blocks in indirect block. 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 //////// / / Bootstrap mainline. Relocate the / boot to high memory, so it wont 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) / 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. mov si, bp / set si to partition mov di, $drive / point to drive movsb / set drive add si, $FIRST-1 / point to first block movsw movsw / fetch first block / Bang on disk controller. / See at.c/atreset(). #if 1 movb al, $4 mov dx, $HF_REG outb dx, al / outb (HF_REG, 4); mov cx, $-1 0: loop 0b / delay movb al, cntrl andb al, $0x0F outb dx, al / outb (HF_REG, ctrl & 0xF); / call atbsyw / Wait for AT controller to become not busy. / From atas.s. / atbsyw: mov cx, $-1 mov dx, $CSR_REG 0: inb al, dx testb al, $BSY_ST loopne 0b mov ax, cx / ret #endif movb al, cntrl mov dx, $HF_REG outb dx, al / outb(HF_REG, cntrl); movb al, sects mov dx, $NSEC_REG outb dx, al / outb(NSEC_REG, sects); / The following should have drive << 4 added in, this assumes drive 0. / Out of space to do it right now. movb al, drive sal ax, $4 addb al, $0x9F addb al, heads mov dx, $HDRV_REG outb dx, al / outb(HDRV_REG, 0xA0+(drive<<4)+heads-1); movb al, $SETPARM_CMD mov dx, $CSR_REG outb dx, al / outb(CSR_REG, SETPARM_CMD) nop nop / filler to make 512 byte bootstrap / Set registers. 0: mov bp, $stack+NSTK / stack pointer value mov ax, es mov ds, ax mov ss, ax mov sp, bp / call login / print signon message / Jump to (relocated) boot. .byte JMPF .word entry 1: .word RBOOTS //////// / / Read the inode specified by "ax" / into the external variable "iaddr". / 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. movb cl, $IBSHIFT / Convert to shr ax, cl / inode block number, inc ax / then to physical block number, inc ax 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. add si, $IADDR / Point at address field. mov di, $iaddr / Copy out area. movb cl, $ND / cx = # of direct blocks. 0: inc si / Skip 0th byte. movsw / Move 1st (lsb) and 2nd (msb) loop 0b / Do them all. inc si / Skip 0th byte and lodsw / grab block # of (first) indirect. inc si / Skip 0th byte and push (si) / remember block # of double indirect. call bread / Read (first) indirect block. movb cl, $NIND / cx = # of indirect maps (ch=0) 1: lodsw / Skip hi half (canon long) movsw / and move low half. loop 1b / Do them all. pop ax call bread / Read double indirect block. lodsw lodsw call bread / Read (second) indirect block. movb cl, $NIND / cx = # of indirect maps (ch=0) 2: lodsw / Skip hi half (canon long) movsw / and move low half. loop 2b / Do them all. sub dx, dx / sets dx to first word / jmp iread / Done return through iread. //////// / / This routine reads the virtual / block described by the ofs in "dx" 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 are correctly done. / mungs ax, bx, si. / clears cx. / /////// iread: sub bx, bx / Convert from words to blocks. movb bl, dh / shl bx, $1 / Get 2 * block number into mov ax, iaddr(bx) / physical block from the table. / 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". / //////// bread: push es / Save registers push di push dx push ds pop es / set ES to the address of the buffer mov di, bp / if block 0, clear buffer mov cx, $BUFSIZE rep stosb test ax, ax / if block 0, return zeroed buffer jz 2f 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. //////// / / Print signon message / //////// / login: mov si, $msg00 / point si to message print: lodsb / al=byte cmpb al, $LF / check for end of message call putc / Type it and jne print / go back for more. ret / Prompt with a "?" and read the file name / into the "nbuf". No character editing facilities / are provided. error: mov sp, bp / Reset stack / call login / print boot message input: mov di, $nbuf / di=name buffer pointer mov cx, $DIRSIZE / cx=size of movb al, $0x3F / set prompt to ? 0: call putc 1: movb ah, $0 / Get ASCII opcode. int KEYBD / Read keyboard ROM call. #if 0 / Conditionalized out to make more space cmpb al, $BS / BS ? jne 2f / cmpb cl, $DIRSIZE / At start of buffer? je 1b / Yup, ignore BS call putc / Output destructive backspace movb al, $SP call putc movb al, $BS dec di / Adjust pointer inc cx / and char count jmp 0b / and continue. #endif 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: mov si, $crlf / Echo LF followed by CR. call print movb al, $0 / zero out rep stosb / rest of name entry: mov ax, $ROOTINO / ax = current inode call igrab / Read in inode and set dx / Search directory. / Assume directory size < 64 Kbytes. 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. mov ax, LSHRD(si) / Push the sum add ax, LPRVD(si) / of the shared and private push ax / [initialized] data sizes. 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. 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. call load / Load code. popf / Pop sep I/D flag test jz 0f / Not sep. 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 call load / load the image. / pop cx / Pop off uninitialized data size and / rep / clear it. / stosb .byte JMPF / Jump to offset .word 0x0100 / 0x0100 (after base) in system 1: .word SYSBASE / code segment. //////// / / Load a segment. The "dx" 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). / //////// 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 nbuf: .ascii "autoboot" / Name buffer. .blkb DIRSIZE-8 / rest of buffer traks: .word NTRK sects: .byte NSPT heads: .byte NHD cntrl: .byte CNTRL wpcc: .word WPCC / not used currently drive: .byte 0 first: .word 0 .word 0 / msg00: .ascii "Boot" crlf: .byte CR, LF .byte 0x55,0xAA .bssd stack: .blkb NSTK / Local Stack and name buffer bbuf: .blkb BUFSIZE / Block buffer [must follow stack]. iaddr: .blkw ND+NIND+NIND / Inode map words.