mkmsbr 1.0.1

Clean-room library + CLI for Microsoft-compatible MBR and FAT32/NTFS boot records.
; mbr_xp.asm — mkmsbr Master Boot Record, Windows 2000/XP/2003 variant.
;
; Spec-compliance map (clean-room — see docs/PROVENANCE.md):
;   - INT 13h fn 0x42 (LBA): Phoenix BIOS Interface Reference
;   - Partition table layout: winioctl.h / MBR convention
;   - Relocation 0x7C00 -> 0x600: standard MBR pattern (any BIOS textbook)
;
; Differences vs the future mbr_win7.asm:
;   - No GPT-protective-MBR safety check (XP predates GPT support)
;   - No disk-signature verification (Win 7+ tracks the 4-byte signature
;     at offset 0x1B8 of the MBR to detect drive moves; XP doesn't)
;
; Loaded by BIOS at 0000:7C00 in real mode. The BIOS hands us:
;   DL = boot drive number (e.g. 0x80 for first hard disk / USB stick)
;
; Job:
;   1. Relocate self from 0000:7C00 to 0000:0600 (so we can load the
;      target PBR at 0000:7C00 without overwriting ourselves).
;   2. Find the active primary partition in the partition table at
;      offset 0x1BE..0x1FE of this sector.
;   3. Read its first sector (the PBR) to 0000:7C00 using INT 13h
;      extended read (function 42h, LBA addressing).
;   4. Pass DL = boot drive unchanged to the PBR.
;   5. Far-jump to 0000:7C00 to chain-load the PBR.
;
; Output: exactly 512 bytes. Bytes 446..509 are the partition table
; (zeroed by nasm; written by mkmsbr at install time). Bytes
; 510..511 = 0x55 0xAA.

BITS 16
ORG 0x7C00

start:
    cli
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x7C00          ; stack just below the load address
    sti

    ; Save boot drive number; we'll need it for INT 13h reads and to
    ; pass to the PBR.
    mov [boot_drive], dl

    ; Relocate ourselves to 0000:0600 so the loaded PBR can occupy 0000:7C00.
    cld
    mov si, 0x7C00
    mov di, 0x0600
    mov cx, 256             ; 256 words = 512 bytes
    rep movsw
    ; Far-jump into the RELOCATED copy. NASM resolves `relocated` to its
    ; ORG-base address (~0x7C22); we subtract 0x7C00 and add 0x0600 to
    ; redirect into the new copy. Without this delta, execution continues
    ; in the original at 0x7C2x while the code thinks it's at 0x062x —
    ; absolute addresses (partition table at 0x07BE, message strings)
    ; then point into BIOS data area, the active-partition scan misses,
    ; and the MBR halts silently with "No active partition" on BIOS
    ; teletype (invisible on serial).
    jmp 0x0000:(relocated - 0x7C00 + 0x0600)

relocated:
    ; Find the active partition entry. Partition table starts at
    ; relocated_base + 0x1BE = 0x600 + 0x1BE = 0x07BE.
    mov si, 0x07BE
    mov cx, 4               ; four 16-byte primary partition entries
.scan:
    cmp byte [si], 0x80     ; active flag
    je .found
    add si, 16
    loop .scan

    ; No active partition: print and halt.
    mov si, msg_no_active
    call print
    jmp halt

.found:
    ; SI -> active partition entry. Bytes 8..11 = LBA start (little-endian).
    ; Build a disk address packet for INT 13h extended read.
    push si
    mov si, dap
    mov word [si + 0], 0x10         ; packet size
    mov word [si + 2], 1            ; sectors to read = 1 (just the PBR)
    mov word [si + 4], 0x7C00       ; dest offset
    mov word [si + 6], 0x0000       ; dest segment
    pop bx                          ; partition entry pointer
    mov ax, [bx + 8]
    mov [si + 8], ax                ; LBA low 16
    mov ax, [bx + 10]
    mov [si + 10], ax               ; LBA next 16
    mov word [si + 12], 0           ; LBA bits 32..47
    mov word [si + 14], 0           ; LBA bits 48..63

    mov dl, [boot_drive]
    mov ah, 0x42                    ; extended read
    int 0x13
    jc .io_error

    ; Check the loaded sector's boot signature.
    cmp word [0x7C00 + 510], 0xAA55
    jne .bad_signature

    ; Hand off to the PBR. DL still holds the boot drive number.
    mov dl, [boot_drive]
    jmp 0x0000:0x7C00

.io_error:
    mov si, msg_io_err
    call print
    jmp halt

.bad_signature:
    mov si, msg_no_sig
    call print
    jmp halt

; print: SI -> NUL-terminated string. Uses BIOS teletype (INT 10h, AH=0Eh).
print:
.loop:
    lodsb
    or al, al
    jz .done
    mov ah, 0x0E
    mov bx, 0x0007          ; page 0, attribute 7
    int 0x10
    jmp .loop
.done:
    ret

halt:
    cli
.h:
    hlt
    jmp .h

; Data
boot_drive:   db 0
msg_no_active: db 'No active partition', 13, 10, 0
msg_io_err:    db 'IO error reading PBR', 13, 10, 0
msg_no_sig:    db 'Bad boot signature', 13, 10, 0

dap_pad:
    ; Disk address packet lives at the end of the code area, before the
    ; partition table. We assemble it here so it doesn't collide with
    ; anything else.

dap:
    times 16 db 0

; Pad to the partition table location (offset 0x1BE = 446).
    times 446 - ($ - $$) db 0

; Partition table: 4 × 16-byte entries, zeroed. mkmsbr writes the
; real partition entries during pipeline execution.
    times 64 db 0

; Boot signature.
    dw 0xAA55