|
|
1.1 root 1: / $Header: /newbits/conf/boot/RCS/boot.m,v 1.4 91/11/08 13:18:38 bin Exp Locker: bin $
2: / (lgl-
3: / The information contained herein is a trade secret of Mark Williams
4: / Company, and is confidential information. It is provided under a
5: / license agreement, and may be copied or disclosed only under the
6: / terms of that agreement. Any reproduction or disclosure of this
7: / material without the express written authorization of Mark Williams
8: / Company or persuant to the license agreement is unlawful.
9: /
10: / COHERENT Version 3.2.1
11: / Copyright (c) 1982, 1983, 1984, 1991.
12: / An unpublished work by Mark Williams Company, Chicago.
13: / All rights reserved.
14: / -lgl)
15:
16: ////////
17: /
18: / Bootstrap block for the IBM
19: / PC. This code, which lives in sector 1,
20: / track 0 of the floppy (or sector 1 of a hard disk partition)
21: / gets read into location 0x7C00 by the ROM.
22: / The code relocates itself to location 0x2060:0x0000
23: / (128K above the fixed memory used by the ROM). The system image is read
24: / in beginning at paragraph 0x0060.
25: /
26: / $Log: boot.m,v $
27: / * Revision 1.4 91/11/08 13:18:38 bin
28: / * Piggy update for building secondary boot for tboot
29: / *
30: / Revision 1.7 91/11/08 11:31:13 piggy
31: / Attempted to integrate pboot into these sources. Still not working.
32: / Updated secondary boot for use with tertiary boot code.
33: /
34: / Revision 1.4 91/09/17 10:35:59 piggy
35: / Uses 4 byte disk blocks, so can boot from anywhere on disk.
36: / Points ds:si at useful data for tertiary boot.
37: / Loads /tboot by default.
38: /
39: / Revision 1.1 88/03/24 16:44:09 src
40: / Initial revision
41: /
42: / 87/10/04 Allan Cornish /usr/src/sys/i8086/boot/boot.s
43: / Number of heads now defined by NHD macro. Should be 1 for f*d*, 2 for f*a*.
44: /
45: / 85/10/04 Allan Cornish /usr/src/sys/i8086/boot/boot.s
46: / Bootstrap extended to increase max file size from 64 Kbytes to 128 Kbytes.
47: / The private code segment is now loaded, where previously it had to be empty.
48: / File name editing enhanced to help cursor stay in the 14 char entry field,
49: / and to provide a destructive backspace.
50: /
51: / 85/01/04 Allan Cornish
52: / Added backspace editing of file name.
53: /
54: ////////
55:
56: #ifndef NHD
57: #define NHD 1 / num of heads per drive [1 for f9d0].
58: #endif
59:
60: #ifndef NSPT
61: #define NSPT 9 / num of sectors per track on floppy.
62: #define NTRK 40 / num of tracks on floppy.
63: #endif
64:
65:
66: #ifndef CNTRL
67: #define CNTRL 8
68: #endif
69:
70: #ifndef WPCC
71: #define WPCC 0xFFFF
72: #endif
73:
74: NSTK = 256 / num of bytes of stack.
75: BS = 0x08 / BS character.
76: CR = 0x0D / CR character.
77: LF = 0x0A / LF character.
78: SP = 0x20 / Backspace character.
79: BOOTLC = 0x7C00 / Boot location (ROM).
80: BOOTS = 0 / boot segment (ROM).
81: RBOOTS = 0x2060 / Relocated boot segment.
82: BSHIFT = 9 / Shift, bytes to blocks.
83: ROOTINO = 2 / Root inode #
84: INOORG = 2 / First inode block.
85: IBSHIFT = 3 / Shift, inode to blocks
86: IOSHIFT = 6 / Shift, inode to bytes
87: INOMASK = 0x0007 / Mask, inode to offset
88: DIRSIZE = 14 / Directory size.
89: DIRSIZ2 = 7 / Directory size / 2.
90: BUFSIZE = 512 / Block size.
91: HDRSIZE = 44 / L.out header size.
92: HDRSIZ2 = 22 / L.out header size / 2.
93: BUFMSK2 = 0x00FF / Block [word] mask, for reads.
94: DISK = 0x13 / Disk Interrupt
95: KEYBD = 0x16 / Keyboard Interrupt
96: READ1 = 0x0201 / read one sector
97: DADDR = 4 / number of bytes in a disk block number
98: ND = 10 / num of direct blocks.
99: NIND = 128 / num of blocks in indirect block.
100: NINDX2 = 256 / NIND * 2 (number of words in indirect)
101: ISIZE = 8 / Offset of "di_size".
102: IADDR = 12 / Offset of "di_addr".
103: JMPF = 0xEA / Jump far, direct.
104: LMAGIC = 0 / Offset of "l_magic"
105: LFLAG = 2 / Offset of "l_flag"
106: LSHRI = 10 / Offset of "l_ssize[SHRI]"
107: LPRVI = 14 / Offset of "l_ssize[PRVI]"
108: LSHRD = 22 / Offset of "l_ssize[SHRD]"
109: LPRVD = 26 / Offset of "l_ssize[PRVD]"
110: LBSSD = 30 / Offset of "l_ssize[BSSD]"
111: LFMAG = 0x0107 / Magic number.
112: LFSEP = 0x02 / Sep I/D flag bit.
113: SYSBASE = 0x0060 / System load base paragraph.
114: FIRST = 8 / relative start of partition
115:
116: / Hard disk controller port addresses.
117: HF_REG = 0x3F6
118: NSEC_REG= 0x1F2
119: SEC_REG = 0x1F3
120: HDRV_REG= 0x1F6
121: CSR_REG = 0x1F7
122: BSY_ST = 0x80
123: SETPARM_CMD= 0x91
124:
125: ////////
126: / Overall structure of boot.s (the secondary boot):
127: /
128: / boot: relocate secondary to high memory (0x2060:0x0000)
129: / if needed, get hard disk parameters from bios
130: / jump far to high memory (RBOOTS:entry)
131: / entry: open directory "/"
132: / search: while filename not found
133: / walk through "/" looking for filename (in nbuf)
134: / if not found, ask for another name (call input:), and loop to search
135: / found: open file that we just found
136: / read the l.out header (verify magic number, get partition sizes)
137: / load the shared and private code segments as one (call load:)
138: / load the initialized data segment (call load:)
139: / jump far to the newly loaded program
140: /
141: ////////
142:
143: ////////
144: /
145: / Bootstrap mainline. Relocate the
146: / boot to high memory, so it will not be written
147: / over by the system. Read in a file name. Look up
148: / the file name by following out the directory
149: / structure. Read in the image and jump to it.
150: /
151: ////////
152:
153: boot: xor di, di
154: mov ds, di
155: mov si, $BOOTLC / Make DS:SI point at the code
156: / "mov es, 1f(si)" really means "mov es, $RBOOTS". Clever Intel... :-(
157: mov es, 1f(si) / Make ES:DI point at where it goes
158: mov cx, $512 / cx = # of bytes to move
159: cld
160: rep / Move the bootstrap
161: movsb / to high memory.
162:
163: / Check to see if we were invoked by mboot. (That means we are booting
164: / off a hard disk and need to load disk parameters. )
165:
166: / mboot sets bx = sp
167: cmp bx, sp / trick to tell
168: jne 0f / whether or not xt
169:
170: / Assertion: at this point we know we are booting off a hard disk.
171: mov si, bp / set si to partition table
172:
173: movb dl, (si) / get drive number
174: movb ah, $8 / get drive parameters
175: int DISK
176: jc 0f / abort on error
177:
178: movb al, ch / fetch cyl(lo)
179: movb ah, cl / move cyl(hi), sects
180: rolb ah, $1 / shift cylinder high to
181: rolb ah, $1 / the least sig bits
182: andb ah, $3 / mask out cylinder bits
183:
184: mov di, $traks / point to drive
185: stosw / set number of tracks
186:
187: movb al, $0x3F / sector mask
188: andb al,cl / mask sector
189: stosb / set sector
190:
191: movb al, dh / get max head
192: inc ax / change to # of heads [incb al]
193: stosb / set number of heads
194:
195: movsb / set drive
196: add si, $FIRST-1 / point to first block
197: movsw
198: movsw / fetch first block
199:
200: / Set registers.
201:
202: 0: mov bp, $stack+NSTK / stack pointer value
203: mov ax, es
204: mov ds, ax
205: mov ss, ax
206: mov sp, bp
207:
208: / Jump to (relocated) boot.
209:
210: .byte JMPF
211: .word entry
212: 1: .word RBOOTS
213:
214: ////////
215: /
216: / This is the equivalent of "open()",
217: / but takes an inode instead of a pathname.
218: /
219: / Read the inode specified by "ax"
220: / into the external variable "iaddr".
221: /
222: / Makes a list of block numbers in "iaddr"
223: / consisting of the 10 direct block numbers, the 128
224: / block numbers pointed to the singly indirect block,
225: / and the first 128 block numbers pointed to by the
226: / first doubly indirect block.
227: /
228: / Note that "iread" can only access the first 256
229: / block numbers in this list, so the last 10 are
230: / ignored.
231: /
232: / Note that block numbers are actually 32 bits on disk--
233: / this routine only uses the lower 16 bits.
234: / In fact, iaddr is loaded with 16 bit numbers, not 32 bit.
235: /
236: / No checking is done to make sure that
237: / the inumber is in range on the filesystem.
238: / Sets dx and cx to 0.
239: / Mungs si, di, ax.
240: /
241: ////////
242:
243: igrab: dec ax / Make origin 0 and
244: push ax / remember for later use.
245:
246: / We assume that the inode table appears in the first 32 meg of disk.
247: / Look up the inode in the inode table.
248: movb cl, $IBSHIFT / Convert to
249: shr ax, cl / inode block number,
250: inc ax / then to physical block number,
251: inc ax
252: sub bx, bx / Assume inode within first 32 meg.
253: call bread / and read in the data.
254:
255: pop ax / Get i-number back.
256: and ax, $INOMASK / Get inode within block
257: movb cl, $IOSHIFT / and convert to
258: shl ax, cl / a byte offset in the block.
259: add si, ax / si = inode pointer.
260:
261: / Assertion: si now points at the start of the desired
262: / inode structure (see <sys/inode.h>).
263:
264: / Copy out the direct block numbers to iaddr.
265: add si, $IADDR / Point at address field.
266: mov di, $iaddr / Copy out area.
267:
268: / The block numbers in the inode on disk are stored as
269: / 3 bytes: msb, lsw. lsw is lsb, msb. We want to store them
270: / in memory as msw, lsw, just like the inodes in the indirect
271: / blocks on disk. So we need to cast msb as msw.
272: movb cl, $ND / cx = # of direct blocks
273: sub ax, ax / cast byte in al to word in ax
274: 0: lodsb / al = msb for direct byte
275: stosw / store msb cast as a word
276: movsw / store lsw.
277: loop 0b
278:
279: lodsb / Get the msb for the indirect block.
280: xchg bx, ax / (implicit cast of msb to msw)
281: lodsw / Get the lsw for the indirect block.
282: call bread / Read (first) indirect block.
283:
284: / Copy out the singly indirect block numbers to iaddr.
285: / These are stored on disk as 4 byte numbers: msw, lsw
286: / where each word is lsb, msb. This is how we store them
287: / in memory.
288: mov cx, $NINDX2
289: rep
290: movsw
291:
292:
293: / Finish by reading the first block of the file into bbuf.
294: sub dx, dx / Set dh to first block number in iaddr.
295: / jmp iread / Done return through iread.
296:
297: ////////
298: /
299: / This routine reads the virtual
300: / block iaddr[dh] (the dhth block number
301: / in iaddr) into the buffer "bbuf".
302: / It uses the mapping data that
303: / has been provided by a previous call to igrab
304: / On return "si" points at "bbuf".
305: / Holes in files (sparse blocks) are correctly done.
306: / mungs ax, bx, si.
307: / clears cx.
308: /
309: ///////
310:
311: iread:
312: sub bx, bx / Convert from words to blocks.
313: movb bl, dh /
314:
315: / Block numbers are 4 bytes long, stored msw, lsw.
316: shl bx, $2 / Compute index into iaddr table.
317: push iaddr(bx) / fetch msw
318: inc bx
319: inc bx
320: mov ax, iaddr(bx) / fetch lsw
321: pop bx / put msw where it belongs
322: / jmp bread / Yes, return through "bread".
323:
324: ////////
325: /
326: / Read a block from the floppy disk,
327: / drive A:, using the code in the IBM firmware.
328: / The physical block # is in ax and bx.
329: / Register bx is the MSW, and ax is the LSW.
330: /
331: ////////
332:
333: bread: push es / Save registers
334: push di
335: push dx
336:
337: push ds
338: pop es / set ES to the address of the buffer
339:
340: mov di, bp / Blast the buffer contents.
341: mov cx, $BUFSIZE / For block 0, this fills the buffer
342: rep / with zeros.
343: stosb
344:
345: / Block #0 is the sparse block--it means a block of all zeros.
346: test ax, ax / if block 0, return zeroed buffer
347: jnz 3f
348: test bx, bx
349: jz 2f
350:
351: / Translate block number into cylinder, head, and sector.
352: 3: push bx
353: pop dx
354: / xor dx, dx / extend block number
355: add ax, first / add first block
356: adc dx, first+2 / add rest
357:
358: mov bx, ax / save block number
359: movb al, heads / get number of heads
360: movb cl, sects / get number of sectors
361: mulb cl / calculate sectors per cylinder
362: xchg bx,ax / swap block/sectors
363: div bx / calculate track
364: xchg dx, ax / put track in DX
365: divb cl / calculate head/sector
366:
367: movb cl, ah / set sector
368: inc cx / sectors start at 1 [incb cl]
369:
370: cmp dx, traks / check for second side
371: jb 0f
372: sub dx, traks / fold track
373: inc ax / next head [incb al]
374:
375: 0: rorb dh, $1 / rotate track(low) into
376: rorb dh, $1 / msbits of DX
377: orb cl, dh / set track(high)
378: movb ch, dl / set track(low)
379: movb dh, al / set head
380: movb dl, drive / set drive
381: mov bx, bp / set offset [bbuf]
382:
383: mov ax, $READ1 / Read, 1 sector.
384: int DISK / Disk I/O.
385: jnc 2f / Jump if no error.
386: mov ax, $READ1 / try again
387: int DISK
388: jc error
389:
390: 2: mov si, bp / set SI to point to buffer for return
391: sub cx, cx / clear CX here to save space
392:
393: pop dx / restore registers.
394: pop di
395: pop es
396: ret / return.
397:
398: / Prompt with a "?" and read the file name
399: / into "nbuf". No character editing facilites
400: / are provided.
401:
402: error: mov sp, bp / Reset stack
403:
404: input: mov di, $nbuf / di=name buffer pointer
405: mov cx, $DIRSIZE / cx=size of
406: / Preprocessor blows up if we use single-quote question mark.
407: movb al, $0077 / set prompt to ?.
408: 0: call putc
409:
410: 1: movb ah, $0 / Get ASCII opcode.
411: int KEYBD / Read keyboard ROM call.
412:
413: 2: cmpb al, $CR / CR ?
414: je 3f / Yup, do next thing
415:
416: jcxz 1b / skip over too big name
417: stosb / put it into the buffer,
418: dec cx / adjust char count
419: jmp 0b / and continue.
420:
421: 3: movb al, $CR
422: call putc
423: movb al, $LF
424: call putc
425:
426: movb al, $0 / zero out
427: rep
428: stosb / rest of name
429:
430: ////////
431: / This is where we jump after boot has been relocated.
432: /
433: / Open "/"; we are going to search for "autoboot".
434: ////////
435: entry: mov ax, $ROOTINO / ax = current inode
436: call igrab / Read in inode and set dx
437:
438: ////////
439: / Search directory for filename in nbuf.
440: / Assume directory size = 64 Kbytes.
441: / This assumption works because block numbers
442: / from the inode beyond end of file are zero.
443: ////////
444:
445: search: orb dl, dl / On block boundry ?
446: jnz 0f / Nope.
447: call iread / Read block, set si.
448:
449: 0: movb cl, $DIRSIZE / cx = count left
450: mov di, $nbuf / Point at name buffer and
451: lodsw / ax = inumber, this entry.
452: or ax, ax / Empty directory slot ?
453: je 1f / Yes, skip.
454: repe / compare the
455: cmpsb / two file names.
456: je found / If eq, go and get inode.
457:
458: 1: add si, cx / Advance by count remaining.
459: add dx, $DIRSIZ2+1 / Bump [word] seek pointer
460: jg search / Scan up to 64 Kbytes of directory.
461:
462: jmp input / File not found.
463:
464:
465: ////////
466: /
467: / Found the file so read it in.
468: / ax = inode number of file
469: /
470: ////////
471:
472: found: call igrab / read in inode and first block
473: cmp LMAGIC(si),$LFMAG / Check the magic number.
474: jne error / not Ok.
475:
476: / push LBSSD(si) / Push the uninitialized data size.
477:
478: / Get the total size of shared and private data segments.
479: / They are contigious on disk. see <l.out.h>
480: mov ax, LSHRD(si) / Push the sum
481: add ax, LPRVD(si) / of the shared and private
482: push ax / [initialized] data sizes.
483: / If this is not a sep I/D executable, then we
484: / do not want to load the non-existent data segment.
485: andb LFLAG(si), $LFSEP / check image flags.
486: pushf / Push result of test.
487: mov ax, LSHRI(si) / Get the sum of the
488: add ax, LPRVI(si) / shared and private code sizes.
489:
490: / Find the start of the first segment on disk.
491: mov dx, $HDRSIZ2 / Seek after header and
492: add si, $HDRSIZE / set buffer pointer appropriately.
493: mov es, 1f / Set ES:DI to point to the load
494: sub di, di / base of the new system.
495:
496: / Load the code segment.
497: call load / Load code.
498:
499: / Skip loading the data segment for non-sep I/D executables.
500: popf / Pop sep I/D flag test
501: jz 0f / Not sep.
502:
503: / Assertion: at this point we are dealing with a sep I/D
504: / l.out executable.
505:
506: / Calculate load point for data segment.
507: add di, $15 / Round up the system code
508: movb cl, $4 / size to 16 byte
509: shr di, cl / paragraphs.
510: mov ax, es / fetch program base
511: add ax, di / Compute the data base and
512: mov es, ax / set up ES:DI to point
513: sub di, di / at it.
514:
515: 0: pop ax / Pop off initialized data size and
516: / Load the initialized data segment.
517: call load / load the image.
518:
519: mov si, $useful / Point ds:si at the useful data
520: / for the tertiary boot.
521:
522: / pop cx / Pop off uninitialized data size and
523: / rep / clear it.
524: / stosb
525:
526: / Run the program that we just loaded.
527: .byte JMPF / Jump to offset
528: .word 0x0100 / 0x0100 (after base) in system
529: 1: .word SYSBASE / code segment.
530:
531: ////////
532: /
533: / Load a segment from an l.out file into memory.
534: / The "dx" register holds the
535: / seek pointer into the file. The "si" holds
536: / a pointer into the "bbuf". The "es:di" pair
537: / holds the target address. The "ax" holds
538: / the number of bytes to load. On return the
539: / "ax" must equal 0 (the caller assumes this
540: / and uses "al" to clear the BSS).
541: /
542: / Note that since the number of bytes to load is
543: / stored in a 16 bit register, a segment may be no
544: / longer than 64K. For small model this is just fine.
545: ////////
546:
547: load: or ax, ax / Any left ?
548: jz return / Jump if all loaded.
549:
550: mov cx, $bbuf+BUFSIZE / Compute the number of bytes
551: sub cx, si / remaining in the block.
552: jnz 0f / Jump if some.
553:
554: push ax / Save count registers, then
555: call iread / read the next block
556: pop ax / of the file.
557: mov cx, $BUFSIZE / We now have a full block.
558:
559: 0: cmp cx, ax / More than we need ?
560: jbe 1f / Nope.
561: mov cx, ax / Only take what we need.
562:
563: 1: sub ax, cx / Fix up the count
564: shr cx, $1 /
565: add dx, cx / Fix up the seek [word] address, then
566: rep / copy the words from the block
567: movsw / buffer to the load point and
568: jmp load / loop until done.
569:
570: ////////
571: /
572: / Write the character in "al" out to
573: / the display, using routines in the ROM.
574: / Like most calls to the ROM, this routine spends
575: / most of its time saving and restoring the
576: / registers.
577: /
578: / Note: The ZF in the PSW word must be preserved.
579: /
580: ////////
581:
582: putc: push si / Save registers.
583: push di
584: push bp
585:
586: mov bx, $0x0007 / Page 0, white on black
587: movb ah, $0x0E / Write TTY.
588: int 0x10 / Call video I/O in ROM.
589:
590: pop bp / Restore registers.
591: pop di
592: pop si
593: return: ret / Return
594:
595: ////////
596: /
597: / Data. There is some pure data in
598: / the "prvd" segment. This data is moved to the
599: / new memory when the boot is relocated. All buffers
600: / are in the BSS, and are not actually moved, or even
601: / saved in the boot block.
602: /
603: ////////
604:
605: .prvd
606:
607: / Do not rearrange ANY of the following variables. If you
608: / need to add variables, they must come BEFORE or AFTER these.
609: / If you want to teach the tertiary boot about any variables you
610: / add, put them after double word "first".
611:
612:
613: .even
614: useful: / Useful variables for the tertiary boot.
615: nbuf: .ascii "tboot" / Name buffer.
616: .blkb DIRSIZE-5 / rest of buffer (5=sizeof("tboot"))
617:
618: traks: .word NTRK / Number of cylinders on drive we are booting off of.
619: sects: .byte NSPT / Number of sectors per track for our drive.
620: heads: .byte NHD / Number of heads on drive we are booting off of.
621:
622: drive: .byte 0 / Drive our partition resides upon.
623: first: .word 0 / First block of our partition (?)
624: .word 0
625: cntrl: .byte CNTRL / Control byte
626: wpcc: .word WPCC /
627:
628: / This magic pair of bytes must be the last two bytes of
629: / the sector (address 0x1FE), otherwise mboot will refuse
630: / to execute it.
631: / If needed, uncomment the .blkb and adjust the number appropriately.
632:
633: .blkb 0x28 / Padding needed to make magic byte line up
634:
635: .byte 0x55,0xAA
636:
637: .bssd
638: stack: .blkb NSTK / Local Stack and name buffer
639: bbuf: .blkb BUFSIZE / Block buffer [must follow stack].
640: iaddr: .blkw ND+ND+NIND+NIND / Inode map words.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.