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