| ; -*- fundamental -*- (asm-mode sucks) |
| ; $Id$ |
| ; ----------------------------------------------------------------------- |
| ; |
| ; NOT Copyright 2000 H. Peter Anvin |
| ; |
| ; This file is in the public domain. Enjoy. However, I would |
| ; appreciate it if modified versions were marked as such, and |
| ; please don't bug me about any version not released by me. |
| ; |
| ; ----------------------------------------------------------------------- |
| |
| ; |
| ; mbr.asm |
| ; |
| ; Simple Master Boot Record, including support for EBIOS extensions. |
| ; |
| ; The MBR lives in front of the boot sector, and is responsible for |
| ; loading the boot sector of the active partition. The EBIOS support |
| ; is needed if the active partition starts beyond cylinder 1024. |
| ; |
| ; This MBR determines all geometry info at runtime. It uses only the |
| ; linear block field in the partition table. It does, however, pass |
| ; the partition table information unchanged to the target OS. |
| ; |
| ; This MBR should be "8086-clean", i.e. not require a 386. |
| ; |
| |
| ; |
| ; Note: The MBR is actually loaded at 0:7C00h, but we quickly move it down to |
| ; 0600h. |
| ; |
| cpu 8086 |
| |
| org 0600h |
| _start: cli |
| xor ax,ax |
| mov ds,ax |
| mov es,ax |
| mov ss,ax |
| mov sp,7C00h |
| sti |
| cld |
| mov si,sp ; Start address |
| mov di,0600h ; Destination address |
| mov cx,512/2 |
| rep movsw |
| |
| ; |
| ; Now, jump to the copy at 0600h so we can load the boot sector at 7C00h. |
| ; Since some BIOSes seem to think 0000:7C00h and 07C0:0000h are the same |
| ; thing, use a far jump to canonicalize the address. This also makes |
| ; sure that it is a code speculation barrier. |
| ; |
| |
| jmp 0:next ; Jump to copy at 0600h |
| |
| next: |
| mov [DriveNo], dl ; Drive number stored in DL |
| ; |
| ; Check for CHS parameters. This doesn't work on floppy disks, |
| ; but for an MBR we don't care. |
| ; |
| mov ah,08h ; Get drive parameters |
| int 13h |
| xor ax,ax |
| mov al,dh |
| inc ax ; From 0-based to count |
| mov [Heads],ax |
| and cl,3Fh ; Max sector number |
| mov [Sectors],cl |
| ; Note: we actually don't care about the number of |
| ; cylinders, since that's the highest-order division |
| |
| ; |
| ; Now look for one (and only one) active partition. |
| ; |
| mov si,PartitionTable |
| xor ax,ax |
| mov cx,4 |
| checkpartloop: |
| test byte [si],80h |
| jz .notactive |
| inc ax |
| mov di,si |
| .notactive: add si,byte 16 |
| loop checkpartloop |
| |
| cmp ax,byte 1 ; Better be only one |
| jnz not_one_partition |
| |
| ; |
| ; Now we have the active partition partition information in DS:DI. |
| ; Check to see if we support EBIOS. |
| ; |
| mov dl,[DriveNo] |
| mov ax,4100h |
| mov bx,055AAh |
| xor cx,cx |
| xor dh,dh |
| stc |
| int 13h |
| jc no_ebios |
| cmp bx,0AA55h |
| jne no_ebios |
| test cl,1 ; LBA device access |
| jz no_ebios |
| ; |
| ; We have EBIOS. Load the boot sector using LBA. |
| ; |
| push di |
| mov si,dapa |
| mov bx,[di+8] ; Copy the block address |
| mov [si+8],bx |
| mov bx,[di+10] |
| mov [si+10],bx |
| mov dl,[DriveNo] |
| mov ah,42h ; Extended Read |
| jmp short common_tail |
| ; |
| ; No EBIOS. Load the boot sector using CHS. |
| ; |
| no_ebios: |
| push di |
| mov ax,[di+8] |
| mov dx,[di+10] |
| div word [Sectors] |
| inc dx |
| mov cx,dx ; Sector # |
| xor dx,dx |
| div word [Heads] |
| ; DX = head #, AX = cylinder # |
| mov ch,al |
| shr ax,1 |
| shr ax,1 |
| and al,0C0h |
| or cl,al |
| mov dh,dl ; Head # |
| mov dl,[DriveNo] |
| mov bx,7C00h |
| mov ax,0201h ; Read one sector |
| common_tail: |
| int 13h |
| jc disk_error |
| pop si ; DS:SI -> partition table entry |
| ; |
| ; Verify that we have a boot sector, jump |
| ; |
| cmp word [7C00h+510],0AA55h |
| jne missing_os |
| cli |
| jmp 0:7C00h ; Jump to boot sector; far |
| ; jump is speculation barrier |
| ; (Probably not neecessary, but |
| ; there is plenty of space.) |
| |
| not_one_partition: |
| ja too_many_os |
| missing_os: |
| mov si,missing_os_msg |
| jmp short die |
| too_many_os: |
| disk_error: |
| mov si,bad_disk_msg |
| die: |
| .msgloop: |
| lodsb |
| xor al,al |
| jz .now |
| mov ah,0Eh ; TTY output |
| mov bx,0007h |
| int 10h |
| jmp short .msgloop |
| .now: |
| jmp short .now |
| |
| align 4, db 0 ; Begin data area |
| |
| ; |
| ; EBIOS disk address packet |
| ; |
| dapa: |
| dw 16 ; Packet size |
| .count: dw 1 ; Block count |
| .off: dw 7C00h ; Offset of buffer |
| .seg: dw 0 ; Segment of buffer |
| .lba: dd 0 ; LBA (LSW) |
| dd 0 ; LBA (MSW) |
| |
| ; CHS information |
| Heads: dw 0 |
| Sectors: dw 0 |
| |
| ; Error messages |
| missing_os_msg db 'Missing operating system', 13, 10, 0 |
| bad_disk_msg db 'Operating system loading error', 13, 10, 0 |
| |
| ; |
| ; Maximum MBR size: 446 bytes; end-of-boot-sector signature also needed. |
| ; Note that some operating systems (NT, DR-DOS) put additional stuff at |
| ; the end of the MBR, so shorter is better. |
| ; |
| |
| PartitionTable equ $$+446 ; Start of partition table |
| |
| ; |
| ; BSS data; put at 800h |
| ; |
| DriveNo equ 0800h |