org 0x0000 fdc: ; FDC Descriptor as per ECMA-107 jmp _startup times (0x03 - ($-$$)) nop .oem: db "2B" ; creating system identifier times (0x0B - ($-$$)) db " " ; space-padded .ss: dw 0x200 ; sector size .sc: db 2 ; sectors per cluster .rsc: dw 1 ; reserved sector count .fn: db 2 ; number of file allocation tables .rde: dw 0x70 ; number of root directory entries .ts: dw 720 ; total number of sectors .mi: ; medium identifier db 0xFD ; 5.25-inch Double sided, 40 tracks per side, 9 sectors per track (360 KB) .sf: ; sectors per fat dw 2 .spt: dw 9 ; sectors per track .nos: dw 2 ; number of sides (heads) .po: dd 0 ; partition offset (in LBA blocks) .lrgts: dw 0 .drv: db 0 ; drive number db 0 db 0x29 ; efdc signature .vid: dd 0 ; volume id .vlabel: db "2B" times (54 - ($-$$)) db " " .fstype: db "FAT12" times (62 - ($-$$)) db " " _startup: xor ax, ax ; setup stack area growing down from 0x10000 mov ss, ax mov sp, ax ; backup important values push bx push cx push dx ; adjust CS jmp 0x07C0:main ; non-fdc variables rootdir: .start: dw 0 .length: dw 0 main: ; setup buffer area directly at 4k mov ax, 0x0100 mov ds, ax mov es, ax mov [cs:fdc.drv], dl ; backup drive number call fix_chs call calc_sectors call find_first_cluster xor bx, bx call load_cluster ; restore important variables xor ax, ax mov bp, ax mov si, ax mov di, ax pop dx pop cx pop bx jmp 0x0100:0000 ; Write AX, a string and bail out ; Return pointer is the string start ; String must be 0 suffixed error: call print16 push cs pop ds pop si mov bx, 0x0000 mov al, 0x20 .loop: mov ah, 0x0e int 0x10 lodsb cmp al, 0x00 jne .loop .hlt: hlt jmp .hlt ; Read CHS data from BIOS and fix fdc values fix_chs: mov ah, 0x08 int 0x13 jc .end ; skip if function does not exist inc dh mov [cs:fdc.nos], dh mov ax, cx and ax, 0x003F mov [cs:fdc.spt], ax ; no adjustment because sectors are 1-indexed mov ax, cx xchg al, ah mov cl,6 shr ah,cl inc ax ; convert from maximum number (0-based) to total number (1-based) of cylinders mul word [cs:fdc.nos] ; number of tracks = number of cylinders * heads mul word [cs:fdc.spt] ; number of sectors = number of tracks * sectors per track mov [cs:fdc.ts], ax .end: ret calc_sectors: mov bx, [cs:fdc.ss] ; bytes per sector mov cl, 5 shr bx, cl ; div by 2^5, 32 bytes per entry mov ax, [cs:fdc.rde] ; number of entries xor dx, dx div bx mov [cs:rootdir.length], ax mov ax, [cs:fdc.sf] ; sectors per fat mul byte [cs:fdc.fn] ; number of fats add ax, [cs:fdc.rsc] ; reserved sectors mov [cs:rootdir.start], ax ret ; Load the root directory find_first_cluster: mov ax, [cs:rootdir.start] mov cx, [cs:rootdir.length] xor bx, bx .blkloop: call loadblk add bx, [cs:fdc.ss] add ax, 1 loop .blkloop push cs pop es mov cx, [cs:fdc.rde] xor bx, bx .dirloop: push cx mov cx, 0x000B mov si, bx mov di, bootfilename repe cmpsb pop cx jz .found add bx, 0x0020 ; 32 bytes per directory entry loop .dirloop call error db "NOT FOUND", 0 .found: push ds pop es add bx, 26 mov ax, [bx] ret ; Load a single FAT cluster into memory ; in ax cluster number ; es:bx buffer ; load_cluster: xor ch, ch mov cl, [cs:fdc.sc] ; sectors per cluster xor dx, dx dec ax ; somehow 2 is first cluster div word cx add ax, [cs:rootdir.start] add ax, [cs:rootdir.length] .loop: call loadblk inc ax add bx, [cs:fdc.ss] loop .loop ret ; Load a single block into memory ; Does not return on error ; in ax sector number ; es:bx buffer loadblk: push ax push cx push dx xor dx, dx div word [cs:fdc.spt] ; ax:temp = (lba / spt) inc dx ; dx:sector = (lba % spt) + 1 mov cl, dl ; sector number xor dx, dx div word [cs:fdc.nos] ; ax:cylinder = (tmp / heads) ; dx:head = (tmp % heads) mov ch, al ; cylinder number mov dh, dl ; head number mov dl, [cs:fdc.drv] ; driver number mov ax, 0x0201 ; ah=0x02 al=0x01 int 0x13 jc .error pop dx pop cx pop ax ret .error: xchg al, ah call error db "DISK ERROR", 0 %include "print.asm" times (0x1F0 - ($-$$)) db 0 bootfilename: db "BOOT" times (0x1F8 - ($-$$)) db " " db "BIN" times (0x1FE - ($-$$)) db 0 dw 0xAA55