Skip to line: 1050 - 1100 - 1150 - 1200 - 1250 - 1281

Highlighted entries were made in the last day
Select a different time increment to highlight entries
Current GMT time: Apr 24 2024 15:52:59

If you have a comment for masterboot.s, please click here.
Name: faissalEmail:  Date: Oct 27 2009 00:47:27 GMT
Subject:  maybe a typo?
Reponse: first of all, what a great job guys! a nice complement of the book well done. secondely may be the last line in the comment on line 1080 sould be "the testb instruction will clear the Z flagand the jump is..." because the jump instruction is jz not jns.
Respond to faissal's comment.
Name: mnx_bootEmail:  Date: Feb 03 2009 21:22:27 GMT
Subject:  sorting partition table entry
Reponse: hey guys,
great job here.. i just wanted to know one thing.. why are we sorting the partition table entries before going through them. Any technical reasons as to why it should be done that way?.. i mean what if i have 4 partitions, use 2 of them with the other 2 unused in the order USED-UNU-USED-UNU and i want to be able to choose the third partition??.. anything against this?
Respond to mnx_boot's comment.
Name: Darren SpurdensEmail:  Dspurdens@hotmail.comDate: Sep 12 2008 16:08:10 GMT
Subject:  Master boot sector HD5
Reponse: I just done a scan and it came up as Parameter incorrect, what does this mean and how can i Correct it, Please reply by email, Thank you
Respond to Darren Spurdens's comment.
Name: RajaEmail:  raja.btl@gmail.comDate: Aug 10 2008 18:33:34 GMT
Subject:  Why use tricks when calling print
Reponse: Why use tricks when calling print?. Is there a significant advantage to having a data byte defined at the return IP pushed by the call instruction and using that value instead of having to explicitly push the parameter ( the string address in this case ) and then invoke call?
Respond to Raja's comment.
Name: sushil MayengbamEmail:  memsana.sushil@gmail.comDate: Jul 21 2008 18:13:31 GMT
Subject:  1193:1194
Reponse: we need to reset lowsec(absolute sector number to be read) because this would be the MBR and from its partition table, we'll read the first sector from the sub-partition (which was pushed into the stack before coming into this code)
Respond to sushil Mayengbam's comment.
Name: sushil MayengbamEmail:  memsana.sushil@gmail.comDate: Jul 20 2008 18:55:33 GMT
Subject:  line 1065:1066
Reponse: We push the si because when we ret after loading the bootblock, the eip will be pointing to LOADOFF at the same time the sp will also be pointing to it automatically.
Respond to sushil Mayengbam's comment.
Name: leeEmail: Oct 10 2006 03:34:43 GMT
Subject:  my problem
Reponse: This is my first time to touch minix,It is hard for me to understand. But your explainations help me a lot. Thank you very much.
If it is convienient for you,can I ask you some questions in the coming day if i have some.
Respond to lee's comment.
1000   !       masterboot 1.9 - Master bootblock code         Author: Kees J. Bot
1001   !       modified to support drives on 2nd IDE port -- asw 2000-01-23
Expand/Collapse Item1002   !
An exclamation mark (!) indicates a comment.  The assembler ignores everything to the right of an exclamation point.

Before going any further, read the first 4 paragraphs in section 2.5.2 and read the entire 2.6.5 section of Operating Systems by Andrew Tanenbaum and Albert Woodhull.  Haven't bought the book?  You won't be able to get through this web site without buying the book - so buy the book.  I won't be going over things that the book already discusses.

Expand/Collapse Item1003   ! This code may be placed in the first sector(the boot sector) of a floppy,
Expand/Collapse Item1004   ! hard disk or hard disk primary partition. There it will perform the
Expand/Collapse Item1005   ! following actions at boot time:
The code below checks to see if this code was loaded from a floppy; however, I don't understand why a master boot record would ever be put on a floppy.  You typically do not partition a floppy.  In the third paragraph of section 2.6.5, Tanenbaum and Woodhull discuss a circumstance in which a floppy might be partitioned but indicate that even under this circumstance, a master boot record would not exist on the floppy.  Also, the comment on line 1151 suggests the same.  If anyone can see a point to having a master boot record on a floppy, please submit a comment to the site which will be displayed below.
Name: Jeff ZhaoEmail:  jeff_z@126.comDate: Jul 27 2006 14:26:25 GMT
Subject:  Thanx!
Reponse: Hello, guys! I am really appreciate that the webmaster give us such a fabulance thing. I hope more people who are crazy about Minix could join us and share their idea with us. I'll pay more attention to this site and give my comments if my assignment could be done in time.
Respond to Jeff Zhao's comment.
Name: Steven DimitrovEmail:  stf_dimitrov@mail.bgDate: Jul 02 2004 14:13:52 GMT
Subject:  MBR on floppy
Reponse: We could put MBR /masterboot.s/ on floppy to preserve our original MBR on the hard disk. It does not matter that floppy does not have partitions because this code never checks Partition Table in the case it came from floppy and variable "fix" is 0.
In this scenario the first sector from the second floppy will be loaded /eventually bootblock.s/. because "load0" procedure will be called with dl=1 !!! Follow the code and you will see how the value of dl-register is used.

Name: sushil MayengbamEmail:  memsana.sushil@gmail.comDate: Jul 20 2008 18:37:20 GMT
Subject: Re: MBR on floppy
Reponse: I like to believe that this code (masterboot.s) can also come from the floppy. When the BIOS loads this code from say, floppy, with dl = 0, it'll load the bootblock.s from the next floppy-drive (by dl = 1).This is shown in the nextdisk: (1171) when this code is loaded from the floppy disk.
 Continue discussion.
1006   !
1007   ! - If the ALT key is held down, then '/dev/hd?' is typed and you are
Expand/Collapse Item1008   !   expected to type a number key (0 - 9) to select the device to boot
Expand/Collapse Item1009   !   on primary IDE controller, or an alpha key (a - j) to select a boot
Expand/Collapse Item1010   !   device on the secondary controller (a=hd10, b=hd11, ... f=hd15, etc).
hd0 is the entire first hard drive, hd1 is the first partition on that hard drive, hd2 is the second, hd3 is the third and hd4 is the fourth.  hd5 is the entire second hard drive and hd6-hd9 are the partitions on that drive.  hd10 is the third hard drive and hd11-hd14 are the partitions on that drive and hd15 is the fourth hard drive and hd16-hd19 are the partitions on that drive.  Within each partition there can be four further subpartitions.  hd1a is the first subpartition within hd1, hd1b is the second subpartition, hd1c is the third and hd1d is the fourth.

If a drive is partitioned, there must be 4 partitions.  If a partition is subpartitioned, there must be 4 subpartitions.  Any partition or subpartition may have a size of 0 but there must be 4 partitions or subpartitions.

Note that a partition can be specified after hitting the ALT key but a subpartition cannot be specified.  For example, if subpartitions hd3b and hd3c are both bootable, there's no way of specifying either one after hitting the ALT key.  (You could use the installboot utility program to mark one of the subpartitions as active but that takes a little more effort.)

1011   !
Expand/Collapse Item1012   ! - If locked into booting a certain partition, then do so.
See line 1053 for an explanation of this comment.
1013   !
1014   ! - If the booted device is a hard disk and one of the partitions is active
1015   !   then the active partition is booted.
1016   !
1017   ! - Otherwise the next floppy or hard disk device is booted, trying them one
1018   !   by one.
1019   !
1020   ! To make things a little clearer, the boot path might be:
Expand/Collapse Item1021   !       /dev/fd0        - Floppy disk containing data, tries fd1 then hd0
Older machines are likely to halt the process until you remove the floppy.
1022   !       [/dev/fd1]      - Drive empty
1023   !       /dev/hd0        - Master boot block, selects active partition 3
1024   !       /dev/hd3        - Submaster, selects active subpartition 1
Expand/Collapse Item1025   !       /dev/hd3a       - Minix bootblock, reads secondary boot code /boot
The code found in the bootblock.s file is called the bootstrap.
Expand/Collapse Item1026   !       Minix           - Started by secondary boot from /minix
If a hard drive partition (as the book describes in section 2.6.5) is booted, the sequence is master boot (this code), bootstrap, boot monitor and finally the minix operating system.  If a hard drive subpartition is booted, the sequence is master boot, master boot (again), bootstrap, boot monitor and finally the minix operating system.
Expand/Collapse Item1027
The "0x" indicates that the value is in hexadecimal notation.  If you are unfamiliar with the term "hexadecimal," look at this site.

The first instruction of this code (the jmp instruction on line 1052) is loaded at an offset of 0x7C00 into memory.

The figure below shows the memory layout.  This code is initially loaded at memory address LOADOFF (0x7C00) but then copied to memory address BUFFER (0x0600).  The code jmp 's there to make room for the next block we'll load, which will either be another master boot block or a bootstrap (as found in bootblock.s).

Although you won't find it explicitly in this code or in bootblock.s, both the master boot blocks and boot blocks on disk have the magic number at an offset of 510.  How'd they get there?  The installboot utility is responsible for patching in the values.  Go to this link  for the master boot and this link  for the boot block.  These links show where SIGNATURE (0xAA55, the magic number) is patched into the master boot blocks and boot blocks at MAGIC (which is the same value as installboot's SIGPOS).  Don't spend too much time studying the installboot.c code.  It is only important that you begin to understand the role that the installboot utility plays.

1028           LOADOFF    =    0x7C00  ! 0x0000:LOADOFF is where this code is loaded
1029           BUFFER     =    0x0600  ! First free memory
1030           PART_TABLE =       446  ! Location of partition table within this code
1031           PENTRYSIZE =        16  ! Size of one partition table entry
1032           MAGIC      =       510  ! Location of the AA55 magic number
Expand/Collapse Item1034  ! <ibm/partition.h>:
Expand/Collapse Item1035            bootind    =         0
Expand/Collapse Item1036            sysind     =         4
Expand/Collapse Item1037            lowsec     =         8
Expand/Collapse Item1040    .define begtext, begdata, begbss, endtext, enddata, endbss, _main
Expand/Collapse Item1041    .data
Expand/Collapse Item1042    begdata:
Expand/Collapse Item1043    .bss
Expand/Collapse Item1044    begbss:
Expand/Collapse Item1045    .text
Expand/Collapse Item1046    begtext:
Expand/Collapse Item1047    _main:
There are 3 main assembler files in the boot sequence (masterboot.s, bootblock.s, and boothead.s ) and only 2 of the files .define begtext, begdata, and begbss and only masterboot.s (this file).defines endtext, enddata, endbss, and _main.  The convention used in bootblock.s  is the simplest - bootblock.s begins text (i.e. code) sections with .text, data sections with .data, and (if it had a bss section) bss sections with .bss but does not .define begtext,begdata, begbss, endtext, enddata, endbss, and _main.

If anyone sees a point to the extra .defines (begtext, begdata, etc.), please submit a comment to the site which will be displayed below.

(The bss section holds uninitialized global variables; the bss section is discussed later.)

1049   ! Find active (sub)partition, load its first sector, run it.
1051   master:
1052           jmp     over
Expand/Collapse Item1053    fix:    .data1  0                       ! If 1-9 then always boot that device
.data1 reserves 1 byte of memory at the given address (in this case, fix).  This memory address is initialized to a value of zero (0).  If this value is anything other than 0, we are "locked" into booting the specific partition.  (See line 1012 above).  How do we change the value of fix? Again, this is a responsibility of the installboot utility.

Why does data immediately follow the first instruction?  Directly after the first instruction is a convenient place to stick the data.  You'll see this trick elsewhere also.  Look at line 06051 in the book.  This is the first instruction of the minix kernel.

1054   over:
Expand/Collapse Item1055            xor     ax, ax
Expand/Collapse Item1056            mov     ds, ax
Expand/Collapse Item1057            mov     es, ax
The first instruction zeroes the ax register (any number xor'ed with itself is zero).  This is a pretty common practice.  The instruction

mov ax, #0

is slower and takes up 3 bytes compared with xor's 2 bytes.

One thing that initially confuses people with assembler is the order of the operands.  The syntax of the mov instruction is:

mov destination, source

This seems a little strange to me (probably because this is not consistent with the syntax of the Unix cp command) but most assemblers use this syntax. 

Expand/Collapse Item1058            cli
Expand/Collapse Item1059            mov     ss, ax                  ! ds = es = ss = Vector segment
Expand/Collapse Item1060            mov     sp, #LOADOFF
Expand/Collapse Item1061            sti
If you mov a value into the stack register (ss) or the stack pointer (sp), disable the interrupts first.  The ss and sp registers hold the address to which the interrupt will return after its completion.  If the ss and sp register are in flux, you have no idea where the code will return.

The interrupts are disabled with the cli (clear interrupts) instruction and reenabled with the sti (set interrupts) instruction.

What's the pound sign (#) all about?  The pound sign indicates that the value of LOADOFF rather than the contents of the memory location LOADOFF is mov'ed into the register.

Expand/Collapse Item1062            mov     bp, #BUFFER+PART_TABLE  ! Often used address
bp is used here as a general purpose register.  The value of bp doesn't change in this code.
Expand/Collapse Item1064    ! Copy this code to safety, then jump to it.
The figure in the comments of line 1027 describes copying this code from LOADOFF to BUFFER and then jmp'ing there.  This is accomplished in lines 1065-1073.
1065           mov     si, sp                  ! si = start of this code
Expand/Collapse Item1066            push    si                      ! Also its return address
This line is important later in the code.  The ret instruction on lines 1161, 1183, and 1243  jumps to LOADOFF, which is where either another master boot block or a boot block will have been loaded.
1067           mov     di, #BUFFER             ! Buffer area
Expand/Collapse Item1068            mov     cx, #512/2              ! One sector
Since the rep movs instruction moves cx words (not bytes) from ds:si (which is 0:LOADOFF) to es:di (which is 0:BUFFER), the number of bytes is divided by 2.  (The size of a word is architecture dependent.  On a 16-bit architecture - like real-mode on the 80x86's - 2 bytes=1 word and on a 32-bit architecture - like protected mode on the 80x86's - 4 bytes=1 word.  Since the processor is in real mode during the boot sequence and switches to protected mode (if the minix kernel has been compiled for protected mode) immediately before jumping to the kernel, 2 bytes=1 word.  Note that word is sometimes used to mean 2 bytes, regardless of the architecture.  )
Expand/Collapse Item1069            cld
cld (clear direction flag) specifies that the rep movs instruction copies the bytes from 0:LOADOFF to 0:LOADOFF+512, not the bytes from 0:LOADOFF to 0:LOADOFF-512.  If the latter is desired, use std (set direction flag) instead of cld.
1070           rep
1071           movs
Expand/Collapse Item1072            jmpf    BUFFER+migrate, 0       ! To safety
jmpf (far jump) obtains a new segment (in this case 0) and a new offset (BUFFER+migrate).  Even though movb ah, #0x02 is the next instruction executed, this instruction is located LOADOFF-BUFFER bytes lower in memory.
1073   migrate:
Expand/Collapse Item1075    ! ALT key pressed to override active device boot?
Expand/Collapse Item1076    key:
Expand/Collapse Item1077            movb    ah, #0x02               ! Keyboard shift status
Expand/Collapse Item1078            int     0x16
Expand/Collapse Item1079            testb   al, #0x08               ! Bit 3 = ALT key
Expand/Collapse Item1080            jz      noalt                   ! No ALT key pressed
int 0x16 is the keyboard BIOS function call.  If ah=2, int 0x16 tests the status of the control, shift and ALT keys.  If the third bit from the right (0x08 = 00000100) in the return value (al) is high, the ALT key has been pressed.

The "b" in movb and testb indicates that the instructions operate on bytes rather than words.

The difference between the test and the and instruction is that the test instruction doesn't change the destination operand (in this case al).  It only affects the flag register.  In this case we're concerned with the Z (zero) flag.  If al's third bit from the right isn't 1 (the alt key wasn't pressed), the testb instruction will set the Z flag and the jump is made in the next instruction.

noalt is on line 1098.

Expand/Collapse Item1081            call    print
Expand/Collapse Item1082            .data2  BUFFER+devhd
.data2 reserves 2 bytes for data.  Study print (line 1256) to see how this data is used.  Also look at memory location choice on line 1272

BUFFER+devhd is the string's beginning offset address in memory.

Expand/Collapse Item1083    getkey: xorb    ah, ah                  ! Wait for keypress
Expand/Collapse Item1084            int     0x16
Expand/Collapse Item1085            movb    BUFFER+choice, al
Expand/Collapse Item1086            subb    al, #0x30               ! al -= '0'
Expand/Collapse Item1087            cmpb    al, #10
Expand/Collapse Item1088            jl      keyok                   ! key in 0 - 9 range
Expand/Collapse Item1089            andb    al,#0x1f                ! ignore alpha case
Expand/Collapse Item1090            subb    al,#7                   ! correction for alpha keys
Expand/Collapse Item1091            cmpb    al,#20
Expand/Collapse Item1092            jae     getkey                  ! Key not in 0 - 19 range
At this point, the user has hit the ALT key and the code is waiting for the user to specify a partition (0-9, a-j).  When the user types in a value, the ascii value of the pressed key is placed in choice (line 1272), overwriting the initial '0'.  This ascii value is then converted to its corresponding integer value.  The ascii values for '0' through '9' are converted to the integers 0 through 9 and the ascii values for 'a' or 'A' through 'j' or 'J' are converted to the integers 10 through 19.  For a little help, here's an ascii chart.

 cmp is a subtraction that only affects the flag register.  In this way, cmp is similar to test.  The following jae jumps if the first operand in cmp is above or equal to the second operand.

Expand/Collapse Item1093    keyok:  push    ax
Since print uses ax, ax is pushed onto the stack; if ax isn't pushed (and then later popped (line 1096)), its value will be lost.  This is commonly done. For example, it's also done on line 1109.
1094           call    print                   ! Show the key typed
1095           .data2  BUFFER+choice
1096           pop     ax
1097           jmp     override
Expand/Collapse Item1098    noalt:
The ALT key wasn't pressed.  If fix is 0, a jump to findactive is made and the partition tables on the hard drives are searched for a non-zero bootind field.

A partition table entry with the non-zero bootind field indicates a bootable partition.

1099           movb    al, BUFFER+fix          ! Always boot a certain partition?
1100           testb   al, al
1101           jz      findactive              ! No, boot the active partition
Expand/Collapse Item1102    override:
A value between 0 and 19 is in al; this is the partition that will be booted.  Another master boot block from another device must be loaded if the partition isn't on the current device (remember that the current device is in dl).
1103           cbw                             ! ax = partition choice
1104           movb    dl, #5
Expand/Collapse Item1105           divb    dl                      ! al = disk, ah = partition within disk
For divb, ax is divided by dl (in this case) and the quotient is placed in al and the remainder in ah. al will have the value of the hard drive (0-3) and ah will have the value of the partition (0-4).
1106           movb    dl, #0x80
Expand/Collapse Item1107           addb    dl, al                  ! dl = disk
The BIOS uses the values 0x80, 0x81, 0x82, and 0x83 to indicate the 1st, 2nd, 3rd, and 4th hard drives.
1108           movb    al, ah                  ! al = partition within disk
Expand/Collapse Item1109           push    ax                      ! Save partition choice
The ax register is used by load0; since ax is needed on line 1113, its value is pushed and then popped (see line 1112).
Expand/Collapse Item1110           call    load0                   ! Get sector 0
load0  loads the first sector of the hard drive specified by dl.  Keep in mind that this master boot record may be the same as the master boot record that is currently executing.  We could have checked for this and saved the time it takes to load the master boot record but it would have added complexity to the code.

If the partition is 0, 5, 10, or 15, then the master boot record of the appropriate hard drive is loaded and executed (line 1114).  If the partition is not 0, 5, 10, or 15, the partition table from the master boot record that has just been loaded is sorted (line 1121) and the first sector from the desired partition is loaded (line 1146).  This sector either has another master boot record (in which case there are subpartitions on the partition that was chosen) or a bootstrap that will load the boot monitor from that partition.

1111           jb      error0                  ! Unable to read it
1112           pop     ax                      ! Restore partition choice
1113           subb    al, #1                  ! Was it 0 mod 5?
1114           jl      bootstrap               ! Jump to the master bootstrap
Expand/Collapse Item1115           mov     si, #LOADOFF+PART_TABLE ! si = new partition table
On lines 1115-1119, the partition table from the master boot record that is currently executing is overwritten by the partition table from the master boot record that was just loaded (line 1110).
1116           mov     di, bp                  ! To buffer area
Expand/Collapse Item1117           mov     cx, #4*PENTRYSIZE/2
The number of bytes that are transferred is divided by 2 since movs moves words, not bytes.
1118           rep
Expand/Collapse Item1119           movs
movs moves a word from ds:si to es:di.  Since this instruction is prefixed by rep, it is executed cx times; si and di will increment each word.
1120           addb    cl, #4                  ! Four times is enough to sort
Expand/Collapse Item1121    sort:   mov     si, bp                  ! First table entry
Lines 1121-1146 sort a partition table and then load the first sector of the desired partition.

Here's the structure of the partition table again:

Suppose a hard drive has a partition table with (lowsec, sysind) pairs (1,1), (1000,1), (200,0), and (350,1).  (Remember that a sysind value of 0 indicates that the partition is unused.)  This is the order of the partition table entries in the master boot record on the hard drive.  After the partition entries are sorted in memory, the order is (1,1), (350,1), (1000,1), and (200,0).  All partitions in use are sorted by their lowsec values and all unused partitions are at the end.  If the second partition is specified (by hitting the ALT key), the partition with a lowsec of 350, the partition with the second lowest lowsec value currently in use, is booted.

Keep in mind that the partition table entries are sorted in memory but the partition table on the hard drive is not affected.

Expand/Collapse Item1122    bubble: lea     di, PENTRYSIZE(si)      ! Next entry
The lea instruction loads the register specified by the first operand with the offset address of the data specified by the second operand.

This is the first time that we've seen the OFFSET(register) notation.  It's also the first time that the lea instruction has been used.

OFFSET(register) is the value register + OFFSET.  In this case, register is si and OFFSET is PENTRYSIZE.

The lea (load effective address) instruction loads this value into di.  (mov di, PENTRYSIZE(si) would move the contents of memory address PENTRYSIZE+si into di.)

Expand/Collapse Item1123            cmpb    sysind(si), ch          ! Partition type, nonzero when in use
Just to make sure you get the hang of it, we'll look again at the OFFSET(register) notation.  In this case, register is si and OFFSET is sysind (it doesn't matter that sysind is not capitalized; what matters is that sysind is a constant - sysind is set on line 1036).  The byte (cmpb compares bytes; cmp compares words) at memory location sysind+si is compared with the contents of ch.
1124           jz      exchg                   ! Unused entries sort to the end
Expand/Collapse Item1125    inuse:  mov     bx, lowsec+0(di)
Computing di->lowsec - si->lowsec is complicated by the fact that lowsec is 4 bytes and the subtract instructions (sub and sbb) operate on 2 byte operands.
1126           sub     bx, lowsec+0(si)        ! Compute di->lowsec - si->lowsec
1127           mov     bx, lowsec+2(di)
Expand/Collapse Item1128            sbb     bx, lowsec+2(si)
sbb subtracts the second operand (lowsec+2(si)) from the first (bx) and then subtracts an additional 1 if the carry flag was set by sub on line 1126.  It then places the result in the first operand (bx).

Notice that the result from sbb overwrites the result from sub (they both place their results in bx).  We don't care since we are only concerned whether the result is negative or positive.  If the result is negative, the carry flag will be set and the jnb instruction will jump to order.

1129           jnb     order                   ! In order if si->lowsec <= di->lowsec
Expand/Collapse Item1130    exchg:  movb    ah, (si)
If the code arrives here, the partition entries (each entry is 16 bytes) of 2 adjacent entries are exchanged.
1131           xchgb   ah, PENTRYSIZE(si)      ! Exchange entries byte by byte
1132           movb    (si), ah
1133           inc     si
1134           cmp     si, di
1135           jb      exchg
1136   order:  mov     si, di
1137           cmp     si, #BUFFER+PART_TABLE+3*PENTRYSIZE
1138           jb      bubble
1139           loop    sort
Expand/Collapse Item1140            mov     si, bp                  ! si = sorted table
At this point, the partition table is sorted.  The partition that has been chosen must have a nonzero sysind value (in other words, it must be in use - see line 1144).
1141           movb    ah, #PENTRYSIZE
1142           mulb    ah                      ! ax = al * PENTRYSIZE
1143           add     si, ax                  ! si = address of partition entry
1144           cmpb    sysind(si), #1          ! Should be in use
1145           jb      error0
1146           jmp     loadpart                ! Get the partition bootstrap
1148   ! Find the active partition
Expand/Collapse Item1149    findactive:
A jump from line 1102 was made for the code to arrive here.  The value at address fix (see line 1053) is still 0 and has not been changed with the installboot utility program.  The boot sequence has also not been interrupted by holding down the ALT key.

The partition entries are searched for the active partition.  The active partition is the partition that is booted by default.  The 7th bit (0-indexed) in the bootind entry is set for the active partition (see line 1155).

Expand/Collapse Item1150            testb   dl, dl
Expand/Collapse Item1151            jge     nextdisk                ! No partitions on floppies
As I said in the beginning, I'm not sure why we need to check whether this code came from a floppy.  Floppies can't be partitioned and therefore can't have a master boot record.  However, it certainly doesn't hurt anything to check.

testb sets the sign flag if the value in dl is negative.  When is the value negative?  In two's complement notation, if the rightmost bit is a 1 then the value is negative.  So for a one byte value, anything greater than 0x80 is negative.  Remember that 0x00 and 0x01 correspond to the first and second floppy drives and 0x80, 0x81, 0x82, and 0x83 correspond to hard drives 1-4.

The jge instruction jumps to nextdisk if the value in dl is positive.  In other words, the floppy drives are skipped.

Expand/Collapse Item1152            mov     si, bp
bp points to the partition table.
Expand/Collapse Item1153    find:   cmpb    sysind(si), #0          ! Partition type, nonzero when in use
If the value of the sysind entry is 0, the partition is not being used and cannot be active.
1154           jz      nextpart
1155           testb   bootind(si), #0x80      ! Active partition flag in bit 7
1156           jz      nextpart                ! It's not active
Expand/Collapse Item1157    loadpart:
At this point, dl has the hard drive and si has the address of the partition table entry of the partition that will be booted.
1158           call    load                    ! Load partition bootstrap
Expand/Collapse Item1159    error0: jb      error1                  ! Not supposed to fail
I'm not sure why we don't jump directly to error if there's an error instead of first jumping to error1.
Expand/Collapse Item1160    bootstrap:
The ret instruction removes 2 bytes from the stack and places them into the ip (instruction pointer) register.  What 2 bytes are these?  On line 1066 the value LOADOFF was pushed onto the stack.  This is the address of the code that was just loaded.  Remember that master boot, the bootstrap and the boot monitor are all originally loaded at LOADOFF (0x7C00) and then migrate to BUFFER (0x0600).
1161           ret                             ! Jump to the master bootstrap
1162   nextpart:
1163           add     si, #PENTRYSIZE
Expand/Collapse Item1164            cmp     si, #BUFFER+PART_TABLE+4*PENTRYSIZE
If si points to the 5th partition, the search for an active partition in the partition table is done since a hard drive can only have 4 partitions.  The partition table on the next hard drive must be searched.
1165           jb      find
1166   ! No active partition, tell 'em
Expand/Collapse Item1167            call    print
If there isn't an active partition on the device specified by dl , then a message is printed and the partition table on the next hard drive is searched.
1168           .data2  BUFFER+noactive
1170   ! There are no active partitions on this drive, try the next drive.
Expand/Collapse Item1171    nextdisk:
Most of the complexity of the next few lines (1172-1186) is for floppies.  Again, I don't understand why we need to worry about floppies in the master boot code.
1172           incb    dl                      ! Increment dl for the next drive
Expand/Collapse Item1173            testb   dl, dl
Expand/Collapse Item1174            jl      nexthd                  ! Hard disk if negative
To determine if dl specifies a hard drive, the same test used on lines 1150-1151 is used.
Expand/Collapse Item1175            int     0x11                    ! Get equipment configuration
int 0x11 returns a lot of information but the only important information (in this case) is found in bits 6-7.  Bits 6-7 hold the number of floppy drives on the system.
1176           shl     ax, #1                  ! Highest floppy drive # in bits 6-7
1177           shl     ax, #1                  ! Now in bits 0-1 of ah
1178           andb    ah, #0x03               ! Extract bits
1179           cmpb    dl, ah                  ! Must be dl <= ah for drive to exist
Expand/Collapse Item1180            ja      nextdisk                ! Otherwise try hd0 eventually
After all the floppies (0x00 and possibly 0x01) on the system have been searched, the code increments ah and jumps to nextdisk until ah equals 0x80.  This is not extremely efficient but it doesn't matter since (once again, I contend) lines 1175-1183 are never executed since floppies have no relevance in the master boot code.
1181           call    load0                   ! Read the next floppy bootstrap
1182           jb      nextdisk                ! It failed, next disk please
1183           ret                             ! Jump to the next master bootstrap
1184   nexthd: call    load0                   ! Read the hard disk bootstrap
1185   error1: jb      error                   ! No disk?
Expand/Collapse Item1186            ret
As described on line 1160, this jumps to the code that has just been loaded.
1189   ! Load sector 0 from the current device.  It's either a floppy bootstrap or
Expand/Collapse Item1190    ! a hard disk master bootstrap.
If sector 0 must be loaded from the current device (remember, the value of the current device is in dl) , a jump to load0 is made.  For all other sectors, a jump to load is made.

There's a little bit of a trick here.  Since load loads the first sector for the drive specified by dl and the partition pointed to by si , the lowsec value of the entry that si currently points to is overwritten with a 0.  Is that a problem?  No, since in this code the partition table is never written back to disk.

1191 load0:
1192           mov     si, bp
Expand/Collapse Item1193            mov     lowsec+0(si), ds        ! Create an entry with a zero lowsec
Expand/Collapse Item1194            mov     lowsec+2(si), ds
lowsec , set on line 1037, is the offset within a partition table entry to the lowsec field.  The lowsec field is the lowest sector of a partition.
1195           !jmp    load
1197   ! Load sector lowsec(si) from the current device.  The obvious head, sector,
1198   ! and cylinder numbers are ignored in favour of the more trustworthy absolute
1199   ! start of partition.
Expand/Collapse Item1200  load:
lowsec(si) is the absolute sector number that will be loaded.  Unfortunately, the absolute sector number must be converted to a cylinder number, head number and sector number.

The first call to int 0x13 (line 1206) queries the hard drive for its maximum sector number and the maximum head number.  With these values, the sectors per cylinder can be determined and the absolute sector number can be converted to a cylinder number, head number and a sector number.  Once these values are known, the desired sector can be loaded with the second int 0x13 call (line 1230).
Expand/Collapse Item1201            mov     di, #3          ! Three retries for floppy spinup
Somebody please tell me how we can load a sector from a floppy in this scenario because I don't believe it can happen.  Line 1106 ensures that any overriding value (whether typed in or gotten from memory location fix ) will be a hard drive.  Also, if this code is from a hard drive (which, according to the book, it must be) and there is no overriding value then we will either load a sector from the same hard drive or the next hard drive.  If you think I am wrong, please submit a comment to the site which will be displayed below.
Name: Steven DimitrovEmail:  stf_dimitrov@mail.bgDate: Jul 02 2004 14:31:34 GMT
Subject:  MBR on floppy
Reponse: I thing you are wrong! This scenario is possible if the following three conditions are met:
1.This code came from floppy,
2.The value of "fix" variable is 0
and 3.The user do not use the ALT key.

There is nothing wrong to have MBR /masterboot.s/ on floppy - this way we preserve our original MBR on the hard disk.

If BIOS was loaded MBR from floppy /suppose fisrt floppy/the value of DL register will be 0.
If the user do not hit the ALT key and "fix"=0 the code jumps to "nextdisk" with dl=0. Here dl is incremented and then "load0" is called with dl=1!!!
So the first sector from the second floppy will be loaded at LOADOFF and executed. This sector can be the "bootblock.s" who in turn will load the Minix Monitor code.
Respond to Steven Dimitrov's comment.
1202   retry:  push    dx              ! Save drive code
1203           push    es
1204           push    di              ! Next call destroys es and di
1205           movb    ah, #0x08       ! Code for drive parameters
Expand/Collapse Item1206            int     0x13
int 0x13 , ah =0x08 returns the device geometry of the drive specified by dldl is 0x80 for the first drive, 0x81 for the second, 0x82 for the third and 0x83 for the fourth.  The call returns the maximum sector number in bits 0-6 of cl and the maximum head number in dh .  Adding to the confusion, the value that int 0x13 returns for the maximum head number has a 0-origin.  This means that if int 0x13 returns a 15 for the maximum head number, there are actually 16 heads.

Let's work through an example.  Assume that we want to load the 5232nd sector of a hard drive with 2000 cylinders, 16 heads, and 25 sectors/track.  512 bytes/sector * 25 sectors/track * 16 tracks/cylinder * 2000 cylinders  = 400MB hard drive.  The int 0x13 , ah =0x08 call returns 25 in cl and 15 in dh for this hard drive.

1 is added to dh at line 1210 to move to a 1-origin.

1207           pop     di
1208           pop     es
1209           andb    cl, #0x3F       ! cl = max sector number (1-origin)
1210           incb    dh              ! dh = 1 + max head number (0-origin)
1211           movb    al, cl          ! al = cl = sectors per track
Expand/Collapse Item1212            mulb    dh              ! dh = heads, ax = heads * sectors
Continuing with our example, al is multiplied (note that mul multiplies ax ; mulb multiplies al ) by dh .  The result is placed in ax and then moved to bx (line 1213).  So bx = 25 * 16 = 400 (sectors/cylinder).
1213           mov     bx, ax          ! bx = sectors per cylinder = heads * sectors
1214           mov     ax, lowsec+0(si)
1215           mov     dx, lowsec+2(si)! dx:ax = sector within drive
Expand/Collapse Item1216            div     bx              ! ax = cylinder, dx = sector within cylinder
The absolute sector number (which is stored in dx-ax and for our example is 5232) is now divided by bx (which is 400 in our example).  The div instruction divides dx-ax by the operand (in our example bx ) and puts the quotient in ax and the remainder in dx .  So ax =13 and dx =32.  So we know we have the 13th cylinder and the 32 sector within that cylinder.  However, our work is not done.  We still have to break up the 32 into a track number and a sector number.  So 32 is divided by cl , the sectors/track, on line 1219.  divb divides ax by the operand (in this case cl ) and puts the quotient into al and the remainder into ah.  So al =1 and ah =7.  The value in ah is a 0-origin value; on line 1225, 1 is added to this value to get a 1-origin value.

At this point, the absolute sector number 5232 has been broken up into a cylinder number (dx =13), a track number (al =1) and a sector number (ah =7, 0-origin).  Lines 1218 through 1229 move and shift the bits around to the positions that int 0x13 , ah =0x02 (line 1230) expects.  I'll leave it to you to finish the example.

1217           xchg    ax, dx          ! ax = sector within cylinder, dx = cylinder
1218           movb    ch, dl          ! ch = low 8 bits of cylinder
1219           divb    cl              ! al = head, ah = sector (0-origin)
1220           xorb    dl, dl          ! About to shift bits 8-9 of cylinder into dl
1221           shr     dx, #1
1222           shr     dx, #1          ! dl[6..7] = high cylinder
1223           orb     dl, ah          ! dl[0..5] = sector (0-origin)
1224           movb    cl, dl          ! cl[0..5] = sector, cl[6..7] = high cyl
1225           incb    cl              ! cl[0..5] = sector (1-origin)
1226           pop     dx              ! Restore drive code in dl
1227           movb    dh, al          ! dh = al = head
1228           mov     bx, #LOADOFF    ! es:bx = where sector is loaded
1229           mov     ax, #0x0201     ! Code for read, just one sector
Expand/Collapse Item1230            int     0x13            ! Call the BIOS for a read
int 0x13 , ah =0x02 copies sectors from a hard drive or floppy (specified by dl ) to memory.  al specifies how many sectors to copy, bits 0-5 of cl specify the sector number, dh specifies the head number and ch specifies the low 8 bits of the cylinder number and bits 6-7 of cl specify the high bits of the cylinder number.  es:bx specifies where in memory we want to load the sectors.  If the int 0x13 , ah =0x02 call fails, the carry (C) flag will be set.  If the carry flag is set, the jnb instruction will not jump to memory location ok.
1231           jnb     ok              ! Read succeeded
1232           cmpb    ah, #0x80       ! Disk timed out?  (Floppy drive empty)
1233           je      bad
1234           dec     di
1235           jl      bad             ! Retry count expired
1236           xorb    ah, ah
Expand/Collapse Item1237            int     0x13           ! Reset
int 0x13 , ah =0x00 resets the hard drive.  Another attempt to read the drive is made unless 3 attempts have already been made.
1238           jnb     retry           ! Try again
Expand/Collapse Item1239    bad:    stc                     ! Set carry flag
It's common practice for a function (like load ) to set the carry flag if something goes wrong.  The code that called the function should check the carry flag after the function returns (see lines 1159 and 1185).
1240           ret
Expand/Collapse Item1241    ok:     cmp   LOADOFF+MAGIC, #0xAA55
Since LOADOFF+MAGIC doesn't have a pound (#) sign in front of it, the value at memory address LOADOFF+MAGIC is compared rather than the value of LOADOFF+MAGIC .  The value at memory address LOADOFF+MAGIC is 'AA55' if the code that was just loaded is a master boot block or a bootblock.
1242           jne     nosig           ! Error if signature wrong
1243           ret                     ! Return with carry still clear
1244   nosig:  call    print
1245           .data2  BUFFER+noboot
1246           jmp     hang
1248   ! A read error occurred, complain and hang
Expand/Collapse Item1249    error:
If the code arrives here, neither a master boot block nor a bootstrap was found.  Note that after calling print , the code falls through to hang.
1250           call    print
1251           .data2  BUFFER+readerr
1253   ! Hang forever waiting for CTRL-ALT-DEL
1254   hang:   jmp     hang
Expand/Collapse Item1256  print:  pop     si                      ! return address
Look at line 1081.  A return from print cannot return to line 1082  since this contains non-executable data.  A return to line 1083 is necessary since this line contains an executable instruction.  Line 1258 replaces the former return address on the stack with the correct return address.
Expand/Collapse Item1257            lods                            ! ax = *si++ = word after 'call print'
lods loads whatever si points to into ax and increments si by 2.  lodsb increments si by 1.

The next few lines are tricky.  At this point, ax holds the value at the memory address that si initially pointed to (before the increment).  si initially pointed to the address after the print instruction.  Look at line 1081-1082 again.  The value at the address after the print instruction is another address, BUFFER+devhd .  So ax holds the value BUFFER+devhd On line 1259, this value is loaded into si so that the following lodsb instruction loads the first character of the devhd (line 1271) string.

1258           push    si                      ! new return address
1259           mov     si, ax
1260   prnext: lodsb                           ! al = *si++ is char to be printed
Expand/Collapse Item1261            testb   al, al
Expand/Collapse Item1262            jz      prdone                  ! Null marks end
As soon as the null character ('\0' ) is reached, print is done.  Be aware that there are a couple of tricks on lines 1271-1272.
1263           movb    ah, #14                 ! 14 = print char
1264           mov     bx, #0x0001             ! Page 0, foreground color
Expand/Collapse Item1265            int     0x10                    ! Call BIOS VIDEO_IO
int 0x10 , ah =14 prints the character in al to the current cursor position and advances the cursor.  bh holds the active page and bl holds the foreground color.
1266           jmp     prnext
1267   prdone: ret
1270 .data
Expand/Collapse Item1271    devhd:          .ascii  "/dev/hd?\b"
Expand/Collapse Item1272    choice:         .ascii  "\0\r\n\0"
The compiler translates .ascii strings to their ascii equivalent.  The next two lines are equivalent:

.ascii    "go"
.data2    0x676F

(The ascii representation of 'g' is 0x67 and the ascii representation of 'o' is 0x6F.)

Since devhd doesn't have a terminating '\0 ', how does print (line 1256) know when to stop?  The answer is that it stops when it runs into the first '\0 ' from choice .  Note that the strings are in consecutive memory addresses.  The last character of the devhd string and the first character of the choice string are consecutive memory addresses.

Won't print return immediately after the first character when printing choice ?  The answer is no.  The value of the first character is modified (line 1085) before calling print (line 1094).

"\r\n " is a return.  '\r ' moves the cursor to the leftmost position and '\n ' advances the cursor to the next line.  '\b ' is the bell.

1273   noactive:       .ascii  "None active\r\n\0"
1274   readerr:        .ascii  "Read error \0"
1275   noboot:         .ascii  "Not bootable \0"
1276   .text
1277   endtext:
1278   .data
1279   enddata:
1280   .bss
1281   endbss:
end of fileback up