[BITS 16] [ORG 0x7C00] jmp main ; Reads sectors ; bx == Low 16 bits of addr ; cx == High 12 bits of addr ; dx == Output buffer ; di == Number of sectors ; Reference: http://www.osdever.net/tutorials/lba.php readSectors: .start: ; Save output buffer push dx ; outb(0x1F1, 0) ; Setup mov dx, 0x1F1 xor ax, ax out dx, al ; outb(0x1F2, 1) ; Send sector count to port 1F2 inc dx mov ax, di out dx, al .sendAddr: ; outb(0x1F3, addr&0xFF) ; Send low byte of the address mov al, bl inc dx out dx, al ; outb(0x1F4, (addr>>8)&0xFF) mov al, bh inc dx out dx, al ; outb(0x1F5, (addr>>16)&0xFF) mov al, cl inc dx out dx, al ; outb(0x1F6, 0xE0 | (drive << 4) | ((addr >> 24) & 0x0F)) ; Send high 4 bits, drive ID, and magic fs mov ax, [driveID] or ax, 0xE0 or al, ch inc dx out dx, al .sendCmd: ; outb(0x1F7, 0x20) mov al, 0x20 inc dx out dx, al .waitForCompletion: ; al = inb(0x1F7) in al, dx ; while((al & 0x08) == 0) and al, 0x08 test al, al jz .waitForCompletion .setupRead: pop bx mov ax, 0x200 mul di mov cx, ax add cx, bx mov dx, 0x1F0 .readResp: ; ax = inw(0x1F0) in ax, dx mov word [bx], ax inc bx inc bx cmp bx, cx jl .readResp .return: ret ; Finds attribute by type ; si == FILE record offset ; cx == Low 16 bits of attr type ; Returns attribute offset or 0 (in si) findAttrByType: .getAttrList: add si, [si+14h] ; Offset to sequence of attributes .iterate: mov ax, 0FFFFh xor ax, [si] ; End of attr list? jnz .checkType mov ax, 0FFFFh xor ax, [si+2] ; End of attr list? jz .notFound .checkType: mov ax, cx xor ax, [si] ; Low 16 bits of attr type jz .return .next: add si, [si+4] ; Attr len jmp .iterate .return: ret .notFound: xor si, si ret ; Get MFT entry ; ax = File entry num ; bx = Output buffer getMFTEntry: .getEntry: fs mul word [sectorsPerMFTRecord] fs add ax, [mftBaseLow] mov cx, dx fs adc cx, [mftBaseHigh] mov dx, bx mov bx, ax fs mov di, [sectorsPerMFTRecord] jmp readSectors ; Check file ; ax = File entry num checkFile: .getEntry: mov bx, 0400h call getMFTEntry mov si, 0400h mov ax, [si] xor ax, 0FFFFh jz $ ; Not found .getFilename: mov cx, 30h call findAttrByType .checkParent: cmp word [si+18h], 5 ; Is it in the root directory? jne .wrong .compare: mov bp, secondStageName add si, 5Ah .iterate: fs mov bl, [bp] test bl, bl jz .right inc bp mov cl, [si] add si, 2 xor bl, cl jz .iterate .wrong: xor bx, bx ret .right: mov bx, 1 ret ; Loads second stage bootloader ; ax == File entry num loadFile: .getEntry: xor bx, bx call getMFTEntry .getData: xor si, si mov cx, 80h call findAttrByType .nonResident: mov bx, [si+18h] ; Last VCN sub bx, [si+10h] ; First VCN push bx add si, [si+20h] ; Runlist offset .readRun: xor bx, bx xor ax, ax fs mov al, [sectorsPerCluster] mov bl, [si+1] ; Length push bx ; Length in clusters mul bl mov di, ax xor cx, cx mov cl, [si] ; Size byte inc si mov ax, cx and al, 0Fh ; Length size (bleh) add si, ax shr cx, 4 ; Offset size push cx ; Offset size in bytes sal cx, 3 mov bx, [si] mov ax, 0FFFFh sal ax, cl not ax and bx, ax fs add [partStartLow], bx mov bp, [si+2] sub cx, 16 mov ax, 0FFFFh sal ax, cl not ax and bp, ax fs add [partStartHigh], bp pop bp ; Offset size in bytes add si, bp pop bp ; Length in clusters pop ax ; VCN left sub ax, bp push ax ; VCN left fs mov bx, [partStartLow] fs mov cx, [partStartHigh] mov dx, 1000h push di ; Number of sectors call readSectors .copy: pop bp ; WTF? Why does this actually work?! pop ax ; Number of sectors mov cx, 512 mul cx mov cx, ax push si mov si, 1000h fs mov di, [curOff] fs add [curOff], cx cld rep movsb pop si pop ax push ax test ax, ax jnz .readRun .run: jmp 6000h:0 main: .setupStack: mov ax, 4000h mov ss, ax mov sp, 0FFFCh .setupTemp: mov ax, ds mov fs, ax mov ax, 3000h mov ds, ax mov ax, 6000h mov es, ax .getPartTable: xor bx, bx ; Low 16 bits of addr xor cx, cx ; High 12 bits of addr xor dx, dx ; Output buffer mov di, 1 ; Number of sectors call readSectors .getFirstPart: mov bx, [0x1C6] ; Load low 16 bits of partition start mov cx, [0x1C8] ; Load high 12 bits of partition start fs mov [partStartLow], bx fs mov [partStartHigh], cx .getBPB: xor dx, dx ; Output buffer mov di, 1 ; Number of sectors call readSectors xor bx, bx mov bl, [0x0D] ; Sectors per cluster fs mov [sectorsPerCluster], bl mov al, [0x40] ; Clusters per MFT record mul bl fs mov [sectorsPerMFTRecord], ax mov ax, [0x30] ; First word of $MFT cluster num mul bx mov cx, ax mov di, dx mov ax, [0x32] ; Second word of $MFS cluster num mul bx fs add cx, [partStartLow] fs adc ax, [partStartHigh] fs mov [mftBaseLow], cx fs mov [mftBaseHigh], ax .walkMFT: xor ax, ax .iterate: push ax call checkFile pop ax test bx, bx jnz .found inc ax jmp .iterate .found: jmp loadFile sectorsPerCluster db 1 sectorsPerMFTRecord dw 2 mftBaseLow dw 4FD5h mftBaseHigh dw 0005h partStartLow dw 003Fh partStartHigh dw 0 curOff dw 0 driveID dw 1<<4 secondStageName db "CBLdr.boot", 0 times 510-($-$$) db 90h dw 0xAA55