Boot Loader: boot0 |
| ; |
| ; A small boot sector program written in x86 assembly whose only |
| ; responsibility is to locate the active partition, load the |
| ; partition booter into memory, and jump to the booter's entry point. |
| ; It leaves the boot drive in DL and a pointer to the partition entry in SI. |
| ; |
| ; This boot loader must be placed in the Master Boot Record. |
| ; |
| ; In order to coexist with a fdisk partition table (64 bytes), and |
| ; leave room for a two byte signature (0xAA55) in the end, boot0 is |
| ; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to |
| ; live in the MBR, then we would have 510 bytes to work with. |
| ; |
| ; boot0 is always loaded by the BIOS or another booter to 0:7C00h. |
| ; |
| ; This code is written for the NASM assembler. |
| ; nasm boot0.s -o boot0 |
| |
| ; |
| ; This version of boot0 implements hybrid GUID/MBR partition scheme support |
| ; |
| ; Written by Tamás Kosárszky on 2008-03-10 and JrCs on 2013-05-08. |
| ; |
| ; Turbo added EFI System Partition boot support |
| ; |
| ; Added KillerJK's switchPass2 modifications |
| ; |
| ; JrCs added FAT32/exFAT System Partition boot support on GPT pure partition scheme |
| ; |
| |
| ; |
| ; boot0af and boot0ss share the same code except. |
| ; The ACTIVEFIRST macro is used to select the right code |
| ; boot0af - define ACTIVEFIRST |
| ; boot0ss - do not define ACTIVEFIRST |
| ; |
| |
| ; |
| ; Set to 1 to enable obscure debug messages. |
| ; |
| DEBUG EQU 0 |
| |
| ; |
| ; Set to 1 to enable verbose mode |
| ; |
| VERBOSE EQU 0 |
| |
| ; |
| ; Various constants. |
| ; |
| kBoot0Segment EQU 0x0000 |
| kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer |
| kBoot0LoadAddr EQU 0x7C00 ; boot0 load address |
| kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address |
| |
| kMBRBuffer EQU 0x1000 ; MBR buffer address |
| kLBA1Buffer EQU 0x1200 ; LBA1 - GPT Partition Table Header buffer address |
| kGPTABuffer EQU 0x1400 ; GUID Partition Entry Array buffer address |
| |
| kPartTableOffset EQU 0x1be |
| kMBRPartTable EQU kMBRBuffer + kPartTableOffset |
| |
| kSectorBytes EQU 512 ; sector size in bytes |
| kBootSignature EQU 0xAA55 ; boot sector signature |
| kHFSPSignature EQU 'H+' ; HFS+ volume signature |
| kHFSPCaseSignature EQU 'HX' ; HFS+ volume case-sensitive signature |
| kEXFATSignature EQU 'EX' ; exFAT volume signature |
| kFAT32BootCodeOffset EQU 0x5a ; offset of boot code in FAT32 boot sector |
| kBoot1FAT32Magic EQU 'BO' ; Magic string to detect our boot1f32 code |
| |
| |
| kGPTSignatureLow EQU 'EFI ' ; GUID Partition Table Header Signature |
| kGPTSignatureHigh EQU 'PART' |
| kGUIDLastDwordOffs EQU 12 ; last 4 byte offset of a GUID |
| |
| kPartCount EQU 4 ; number of paritions per table |
| kPartTypeEXFAT EQU 0x07 ; exFAT Filesystem type |
| kPartTypeFAT32 EQU 0x0c ; FAT32 Filesystem type |
| kPartTypeHFS EQU 0xaf ; HFS+ Filesystem type |
| kPartTypePMBR EQU 0xee ; On all GUID Partition Table disks a Protective MBR (PMBR) |
| ; in LBA 0 (that is, the first block) precedes the |
| ; GUID Partition Table Header to maintain compatibility |
| ; with existing tools that do not understand GPT partition structures. |
| ; The Protective MBR has the same format as a legacy MBR |
| ; and contains one partition entry with an OSType set to 0xEE |
| ; reserving the entire space used on the disk by the GPT partitions, |
| ; including all headers. |
| |
| kPartActive EQU 0x80 ; active flag enabled |
| kPartInactive EQU 0x00 ; active flag disabled |
| kAppleGUID EQU 0xACEC4365 ; last 4 bytes of Apple type GUIDs. |
| kEFISystemGUID EQU 0x3BC93EC9 ; last 4 bytes of EFI System Partition Type GUID: |
| ; C12A7328-F81F-11D2-BA4B-00A0C93EC93B |
| kBasicDataGUID EQU 0xC79926B7 ; last 4 bytes of Basic Data System Partition Type GUID: |
| ; EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 |
| |
| %ifdef FLOPPY |
| kDriveNumber EQU 0x00 |
| %else |
| kDriveNumber EQU 0x80 |
| %endif |
| |
| ; |
| ; Format of fdisk partition entry. |
| ; |
| ; The symbol 'part_size' is automatically defined as an `EQU' |
| ; giving the size of the structure. |
| ; |
| struc part |
| .bootid resb 1 ; bootable or not |
| .head resb 1 ; starting head, sector, cylinder |
| .sect resb 1 ; |
| .cyl resb 1 ; |
| .type resb 1 ; partition type |
| .endhead resb 1 ; ending head, sector, cylinder |
| .endsect resb 1 ; |
| .endcyl resb 1 ; |
| .lba resd 1 ; starting lba |
| .sectors resd 1 ; size in sectors |
| endstruc |
| |
| ; |
| ; Format of GPT Partition Table Header |
| ; |
| struc gpth |
| .Signature resb 8 |
| .Revision resb 4 |
| .HeaderSize resb 4 |
| .HeaderCRC32 resb 4 |
| .Reserved resb 4 |
| .MyLBA resb 8 |
| .AlternateLBA resb 8 |
| .FirstUsableLBA resb 8 |
| .LastUsableLBA resb 8 |
| .DiskGUID resb 16 |
| .PartitionEntryLBA resb 8 |
| .NumberOfPartitionEntries resb 4 |
| .SizeOfPartitionEntry resb 4 |
| .PartitionEntryArrayCRC32 resb 4 |
| endstruc |
| |
| ; |
| ; Format of GUID Partition Entry Array |
| ; |
| struc gpta |
| .PartitionTypeGUID resb 16 |
| .UniquePartitionGUID resb 16 |
| .StartingLBA resb 8 |
| .EndingLBA resb 8 |
| .Attributes resb 8 |
| .PartitionName resb 72 |
| endstruc |
| |
| ; |
| ; Macros. |
| ; |
| %macro DebugCharMacro 1 |
| mov al, %1 |
| call print_char |
| %endmacro |
| |
| %macro LogString 1 |
| mov di, %1 |
| call log_string |
| %endmacro |
| |
| %if DEBUG |
| %define DebugChar(x) DebugCharMacro x |
| %else |
| %define DebugChar(x) |
| %endif |
| |
| ;-------------------------------------------------------------------------- |
| ; Start of text segment. |
| |
| SEGMENT .text |
| |
| ORG kBoot0RelocAddr |
| |
| ;-------------------------------------------------------------------------- |
| ; Boot code is loaded at 0:7C00h. |
| ; |
| start: |
| ; |
| ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack. |
| ; Interrupts should be off while the stack is being manipulated. |
| ; |
| cli ; interrupts off |
| xor ax, ax ; zero ax |
| mov ss, ax ; ss <- 0="" span="">-> |
| mov sp, kBoot0Stack ; sp <- of="" span="" stack="" top="">-> |
| sti ; reenable interrupts |
| |
| mov es, ax ; es <- 0="" span="">-> |
| mov ds, ax ; ds <- 0="" span="">-> |
| |
| ; |
| ; Relocate boot0 code. |
| ; |
| mov si, kBoot0LoadAddr ; si <- source="" span="">-> |
| mov di, kBoot0RelocAddr ; di <- destination="" span="">-> |
| cld ; auto-increment SI and/or DI registers |
| mov cx, kSectorBytes/2 ; copy 256 words |
| repnz movsw ; repeat string move (word) operation |
| |
| ; |
| ; Code relocated, jump to start_reloc in relocated location. |
| ; |
| jmp kBoot0Segment:start_reloc |
| |
| ;-------------------------------------------------------------------------- |
| ; Start execution from the relocated location. |
| ; |
| start_reloc: |
| |
| DebugChar('>') |
| |
| %if DEBUG |
| mov al, dl |
| call print_hex |
| %endif |
| |
| ; |
| ; Since this code may not always reside in the MBR, always start by |
| ; loading the MBR to kMBRBuffer and LBA1 to kGPTBuffer. |
| ; |
| |
| xor eax, eax |
| mov [my_lba], eax ; store LBA sector 0 for read_lba function |
| mov al, 2 ; load two sectors: MBR and LBA1 |
| mov bx, kMBRBuffer ; MBR load address |
| call load |
| jc error ; MBR load error |
| |
| ; |
| ; Look for the booter partition in the MBR partition table, |
| ; which is at offset kMBRPartTable. |
| ; |
| mov si, kMBRPartTable ; pointer to partition table |
| call find_boot ; will not return on success |
| |
| error: |
| LogString(boot_error_str) |
| |
| hang: |
| hlt |
| jmp hang |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; Find the active (boot) partition and load the booter from the partition. |
| ; |
| ; Arguments: |
| ; DL = drive number (0x80 + unit number) |
| ; SI = pointer to fdisk partition table. |
| ; |
| ; Clobber list: |
| ; EAX, BX, EBP |
| ; |
| find_boot: |
| |
| ; |
| ; Check for boot block signature 0xAA55 following the 4 partition |
| ; entries. |
| ; |
| cmp WORD [si + part_size * kPartCount], kBootSignature |
| jne .exit ; boot signature not found. |
| |
| xor bx, bx ; BL will be set to 1 later in case of |
| ; Protective MBR has been found |
| |
| inc bh ; BH = 1. Giving a chance for a second pass |
| ; to boot an inactive but boot1h aware HFS+ partition |
| ; by scanning the MBR partition entries again. |
| |
| .start_scan: |
| mov cx, kPartCount ; number of partition entries per table |
| |
| .loop: |
| |
| ; |
| ; First scan through the partition table looking for the active |
| ; partition. |
| ; |
| %if DEBUG |
| mov al, [si + part.type] ; print partition type |
| call print_hex |
| %endif |
| |
| mov eax, [si + part.lba] ; save starting LBA of current |
| mov [my_lba], eax ; MBR partition entry for read_lba function |
| cmp BYTE [si + part.type], 0 ; unused partition? |
| je .continue ; skip to next entry |
| cmp BYTE [si + part.type], kPartTypePMBR ; check for Protective MBR |
| jne .testPass |
| |
| mov BYTE [si + part.bootid], kPartInactive ; found Protective MBR |
| ; clear active flag to make sure this protective |
| ; partition won't be used as a bootable partition. |
| mov bl, 1 ; Assume we can deal with GPT but try to scan |
| ; later if not found any other bootable partitions. |
| |
| .testPass: |
| cmp bh, 1 |
| jne .Pass2 |
| |
| .Pass1: |
| %ifdef ACTIVEFIRST |
| jmp SHORT .tryToBootIfActive |
| %else |
| jmp SHORT .tryToBootSupportedFS |
| %endif |
| |
| |
| .Pass2: |
| %ifdef ACTIVEFIRST |
| jmp SHORT .tryToBootSupportedFS |
| %endif |
| |
| .tryToBootIfActive: |
| ; We're going to try to boot a partition if it is active |
| cmp BYTE [si + part.bootid], kPartActive |
| jne .continue |
| |
| xor dh, dh ; Argument for loadBootSector to skip file system signature check. |
| jmp SHORT .tryToBoot |
| |
| .tryToBootSupportedFS: |
| ; We're going to try to boot a partition with a supported filesystem |
| ; equipped with boot1x in its boot record regardless if it's active or not. |
| |
| mov dh, 1 ; Argument for loadBootSector to check file system signature. |
| |
| cmp BYTE [si + part.type], kPartTypeHFS |
| je .tryToBoot |
| |
| cmp BYTE [si + part.type], kPartTypeFAT32 |
| je .tryToBoot |
| |
| cmp BYTE [si + part.type], kPartTypeEXFAT |
| jne .continue |
| |
| .tryToBoot: |
| ; |
| ; Found boot partition, read boot sector to memory. |
| ; |
| |
| call loadBootSector |
| jne .continue |
| jmp SHORT initBootLoader |
| |
| .continue: |
| add si, BYTE part_size ; advance SI to next partition entry |
| loop .loop ; loop through all partition entries |
| |
| ; |
| ; Scanned all partitions but not found any with active flag enabled |
| ; Anyway if we found a protective MBR before we still have a chance |
| ; for a possible GPT Header at LBA 1 |
| ; |
| dec bl |
| jnz .switchPass2 ; didn't find Protective MBR before |
| call checkGPT |
| |
| .switchPass2: |
| ; |
| ; Switching to Pass 2 |
| ; try to find a boot1h aware HFS+ MBR partition |
| ; |
| dec bh |
| mov si, kMBRPartTable ; set SI to first entry of MBR Partition table |
| jz .start_scan ; scan again |
| |
| .exit: |
| ret ; Giving up. |
| |
| |
| ; |
| ; Jump to partition booter. The drive number is already in register DL. |
| ; SI is pointing to the modified partition entry. |
| ; |
| initBootLoader: |
| |
| DebugChar('J') |
| |
| %if VERBOSE |
| LogString(done_str) |
| %endif |
| |
| jmp kBoot0LoadAddr |
| |
| ; |
| ; Found Protective MBR Partition Type: 0xEE |
| ; Check for 'EFI PART' string at the beginning |
| ; of LBA1 for possible GPT Table Header |
| ; |
| checkGPT: |
| push bx |
| |
| mov di, kLBA1Buffer ; address of GUID Partition Table Header |
| cmp DWORD [di], kGPTSignatureLow ; looking for 'EFI ' |
| jne .exit ; not found. Giving up. |
| cmp DWORD [di + 4], kGPTSignatureHigh ; looking for 'PART' |
| jne .exit ; not found. Giving up indeed. |
| mov si, di |
| |
| ; |
| ; Loading GUID Partition Table Array |
| ; |
| mov eax, [si + gpth.PartitionEntryLBA] ; starting LBA of GPT Array |
| mov [my_lba], eax ; save starting LBA for read_lba function |
| mov cx, [si + gpth.NumberOfPartitionEntries] ; number of GUID Partition Array entries |
| mov bx, [si + gpth.SizeOfPartitionEntry] ; size of GUID Partition Array entry |
| |
| push bx ; push size of GUID Partition entry |
| |
| ; |
| ; Calculating number of sectors we need to read for loading a GPT Array |
| ; |
| ; push dx ; preserve DX (DL = BIOS drive unit number) |
| ; mov ax, cx ; AX * BX = number of entries * size of one entry |
| ; mul bx ; AX = total byte size of GPT Array |
| ; pop dx ; restore DX |
| ; shr ax, 9 ; convert to sectors |
| |
| ; |
| ; ... or: |
| ; Current GPT Arrays uses 128 partition entries each 128 bytes long |
| ; 128 entries * 128 bytes long GPT Array entries / 512 bytes per sector = 32 sectors |
| ; |
| mov al, 32 ; maximum sector size of GPT Array (hardcoded method) |
| |
| mov bx, kGPTABuffer |
| push bx ; push address of GPT Array |
| call load ; read GPT Array |
| pop si ; SI = address of GPT Array |
| pop bx ; BX = size of GUID Partition Array entry |
| jc error |
| |
| ; |
| ; Walk through GUID Partition Table Array |
| ; and load boot record from first supported partition. |
| ; |
| ; If it has boot signature (0xAA55) then jump to it |
| ; otherwise skip to next partition. |
| ; |
| |
| %if VERBOSE |
| LogString(gpt_str) |
| %endif |
| |
| .gpt_loop: |
| |
| mov eax, [si + gpta.PartitionTypeGUID + kGUIDLastDwordOffs] |
| |
| cmp eax, kAppleGUID ; check current GUID Partition for Apple's GUID type |
| je .gpt_ok |
| |
| ; |
| ; Turbo - also try EFI System Partition |
| ; |
| cmp eax, kEFISystemGUID ; check current GUID Partition for EFI System Partition GUID type |
| je .gpt_ok |
| |
| ; |
| ; JrCs - also try FAT2 System Partition |
| ; |
| cmp eax, kBasicDataGUID ; check current GUID Partition for Basic Data Partition GUID type |
| jne .gpt_continue |
| |
| .gpt_ok: |
| ; |
| ; Found a possible good partition try to boot it |
| ; |
| |
| mov eax, [si + gpta.StartingLBA] ; load boot sector from StartingLBA |
| mov [my_lba], eax |
| mov dh, 1 ; Argument for loadBootSector to check file system signature. |
| call loadBootSector |
| jne .gpt_continue ; no boot loader signature |
| |
| mov si, kMBRPartTable ; fake the current GUID Partition |
| mov [si + part.lba], eax ; as MBR style partition for boot1h |
| mov BYTE [si + part.type], kPartTypeHFS ; with HFS+ filesystem type (0xAF) |
| jmp SHORT initBootLoader |
| |
| .gpt_continue: |
| |
| add si, bx ; advance SI to next partition entry |
| loop .gpt_loop ; loop through all partition entries |
| |
| .exit: |
| pop bx |
| ret ; no more GUID partitions. Giving up. |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; loadBootSector - Load boot sector |
| ; |
| ; Arguments: |
| ; DL = drive number (0x80 + unit number) |
| ; DH = 0 skip file system signature checking |
| ; 1 enable file system signature checking |
| ; [my_lba] = starting LBA. |
| ; |
| ; Returns: |
| ; ZF = 0 if boot sector hasn't kBootSignature |
| ; 1 if boot sector has kBootSignature |
| ; |
| loadBootSector: |
| pusha |
| |
| mov al, 3 |
| mov bx, kBoot0LoadAddr |
| call load |
| jc error |
| |
| or dh, dh |
| jz .checkBootSignature |
| |
| .checkHFSSignature: |
| |
| %if VERBOSE |
| LogString(test_str) |
| %endif |
| |
| ; |
| ; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature. |
| ; |
| mov ax, [kBoot0LoadAddr + 2 * kSectorBytes] |
| cmp ax, kHFSPSignature ; 'H+' |
| je .checkBootSignature |
| cmp ax, kHFSPCaseSignature ; 'HX' |
| je .checkBootSignature |
| |
| ; |
| ; Looking for exFAT signature |
| ; |
| mov ax, [kBoot0LoadAddr + 3] |
| cmp ax, kEXFATSignature ; 'EX' |
| je .checkBootSignature |
| |
| ; |
| ; Looking for boot1f32 magic string. |
| ; |
| mov ax, [kBoot0LoadAddr + kFAT32BootCodeOffset] |
| cmp ax, kBoot1FAT32Magic |
| jne .exit |
| |
| .checkBootSignature: |
| ; |
| ; Check for boot block signature 0xAA55 |
| ; |
| cmp WORD [kBoot0LoadAddr + kSectorBytes - 2], kBootSignature |
| |
| .exit: |
| |
| popa |
| |
| ret |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; load - Load one or more sectors from a partition. |
| ; |
| ; Arguments: |
| ; AL = number of 512-byte sectors to read. |
| ; ES:BX = pointer to where the sectors should be stored. |
| ; DL = drive number (0x80 + unit number) |
| ; [my_lba] = starting LBA. |
| ; |
| ; Returns: |
| ; CF = 0 success |
| ; 1 error |
| ; |
| load: |
| push cx |
| |
| .ebios: |
| mov cx, 5 ; load retry count |
| .ebios_loop: |
| call read_lba ; use INT13/F42 |
| jnc .exit |
| loop .ebios_loop |
| |
| .exit: |
| pop cx |
| ret |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; read_lba - Read sectors from a partition using LBA addressing. |
| ; |
| ; Arguments: |
| ; AL = number of 512-byte sectors to read (valid from 1-127). |
| ; ES:BX = pointer to where the sectors should be stored. |
| ; DL = drive number (0x80 + unit number) |
| ; [my_lba] = starting LBA. |
| ; |
| ; Returns: |
| ; CF = 0 success |
| ; 1 error |
| ; |
| read_lba: |
| pushad ; save all registers |
| mov bp, sp ; save current SP |
| |
| ; |
| ; Create the Disk Address Packet structure for the |
| ; INT13/F42 (Extended Read Sectors) on the stack. |
| ; |
| |
| ; push DWORD 0 ; offset 12, upper 32-bit LBA |
| push ds ; For sake of saving memory, |
| push ds ; push DS register, which is 0. |
| mov ecx, [my_lba] ; offset 8, lower 32-bit LBA |
| push ecx |
| push es ; offset 6, memory segment |
| push bx ; offset 4, memory offset |
| xor ah, ah ; offset 3, must be 0 |
| push ax ; offset 2, number of sectors |
| |
| ; It pushes 2 bytes with a smaller opcode than if WORD was used |
| push BYTE 16 ; offset 0-1, packet size |
| |
| DebugChar('<') |
| %if DEBUG |
| mov eax, ecx |
| call print_hex |
| %endif |
| |
| ; |
| ; INT13 Func 42 - Extended Read Sectors |
| ; |
| ; Arguments: |
| ; AH = 0x42 |
| ; DL = drive number (80h + drive unit) |
| ; DS:SI = pointer to Disk Address Packet |
| ; |
| ; Returns: |
| ; AH = return status (sucess is 0) |
| ; carry = 0 success |
| ; 1 error |
| ; |
| ; Packet offset 2 indicates the number of sectors read |
| ; successfully. |
| ; |
| mov si, sp |
| mov ah, 0x42 |
| int 0x13 |
| |
| jnc .exit |
| |
| DebugChar('R') ; indicate INT13/F42 error |
| |
| ; |
| ; Issue a disk reset on error. |
| ; Should this be changed to Func 0xD to skip the diskette controller |
| ; reset? |
| ; |
| xor ax, ax ; Func 0 |
| int 0x13 ; INT 13 |
| stc ; set carry to indicate error |
| |
| .exit: |
| mov sp, bp ; restore SP |
| popad |
| ret |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; Write a string with 'boot0: ' prefix to the console. |
| ; |
| ; Arguments: |
| ; ES:DI pointer to a NULL terminated string. |
| ; |
| ; Clobber list: |
| ; DI |
| ; |
| log_string: |
| pusha |
| |
| push di |
| mov si, log_title_str |
| call print_string |
| |
| pop si |
| call print_string |
| |
| popa |
| |
| ret |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; Write a string to the console. |
| ; |
| ; Arguments: |
| ; DS:SI pointer to a NULL terminated string. |
| ; |
| ; Clobber list: |
| ; AX, BX, SI |
| ; |
| print_string: |
| mov bx, 1 ; BH=0, BL=1 (blue) |
| cld ; increment SI after each lodsb call |
| .loop: |
| lodsb ; load a byte from DS:SI into AL |
| cmp al, 0 ; Is it a NULL? |
| je .exit ; yes, all done |
| mov ah, 0xE ; INT10 Func 0xE |
| int 0x10 ; display byte in tty mode |
| jmp short .loop |
| .exit: |
| ret |
| |
| |
| %if DEBUG |
| |
| ;-------------------------------------------------------------------------- |
| ; Write a ASCII character to the console. |
| ; |
| ; Arguments: |
| ; AL = ASCII character. |
| ; |
| print_char: |
| pusha |
| mov bx, 1 ; BH=0, BL=1 (blue) |
| mov ah, 0x0e ; bios INT 10, Function 0xE |
| int 0x10 ; display byte in tty mode |
| popa |
| ret |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; Write the 4-byte value to the console in hex. |
| ; |
| ; Arguments: |
| ; EAX = Value to be displayed in hex. |
| ; |
| print_hex: |
| pushad |
| mov cx, WORD 4 |
| bswap eax |
| .loop: |
| push ax |
| ror al, 4 |
| call print_nibble ; display upper nibble |
| pop ax |
| call print_nibble ; display lower nibble |
| ror eax, 8 |
| loop .loop |
| |
| mov al, 10 ; carriage return |
| call print_char |
| mov al, 13 |
| call print_char |
| |
| popad |
| ret |
| |
| print_nibble: |
| and al, 0x0f |
| add al, '0' |
| cmp al, '9' |
| jna .print_ascii |
| add al, 'A' - '9' - 1 |
| .print_ascii: |
| call print_char |
| ret |
| |
| getc: |
| pusha |
| mov ah, 0 |
| int 0x16 |
| popa |
| ret |
| %endif ;DEBUG |
| |
| |
| ;-------------------------------------------------------------------------- |
| ; NULL terminated strings. |
| ; |
| |
| %if VERBOSE |
| gpt_str db 'GPT', 0 |
| test_str db 'test', 0 |
| done_str db 'done', 0 |
| %endif |
| |
| boot_error_str db 'error', 0 |
| |
| ;-------------------------------------------------------------------------- |
| ; Pad the rest of the 512 byte sized booter with zeroes. The last |
| ; two bytes is the mandatory boot sector signature. |
| ; |
| ; If the booter code becomes too large, then nasm will complain |
| ; that the 'times' argument is negative. |
| |
| ; |
| ; According to EFI specification, maximum boot code size is 440 bytes |
| ; |
| |
| pad_boot: |
| times 428-($-$$) db 0 ; 428 = 440 - len(log_title_str) |
| |
| log_title_str: |
| %ifdef ACTIVEFIRST |
| db 10, 13, 'boot0af: ', 0 ; can be use as signature |
| %else |
| db 10, 13, 'boot0ss: ', 0 ; can be use as signature |
| %endif |
| |
| pad_table_and_sig: |
| times 510-($-$$) db 0 |
| dw kBootSignature |
| |
| |
| ABSOLUTE 0xE400 |
| |
| ; |
| ; In memory variables. |
| ; |
| my_lba resd 1 ; Starting LBA for read_lba function |
| |
| ; END
https://github.com/Clover-EFI-Bootloader/clover/blob/master/BootHFS/boot0.s
|