diff options
Diffstat (limited to 'boot0/boot0.asm')
| -rw-r--r-- | boot0/boot0.asm | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/boot0/boot0.asm b/boot0/boot0.asm new file mode 100644 index 0000000..e31f126 --- /dev/null +++ b/boot0/boot0.asm @@ -0,0 +1,347 @@ +; +; This boot sector was based on VnutZ's example available online +; at http://www.geocities.com/mvea/bootstrap.htm. It also made use +; of info at http://support.microsoft.com/support/kb/articles/Q140/4/18.asp +; and the source code for geekos. +; +; It searches the root directory of a floppy for two files: boot.com and +; kernel.bin. If either is missing the boot fails. If they are found, they +; are loaded into memory at BOOT1SEG:BOOT1OFF and KERNSEG:KERNOFF. Then +; control is passed to the next stage bootloader, boot.com. +; +%include "../inc/defs.asm" + +[BITS 16] +[ORG 0] + +START: + jmp AROUND_DATA + +szOEM_ID db "SCOTTOS", 0 ; 8 bytes, null not required +wBytesPerSector dw 0x0200 ; bytes per sector +bSectorsPerCluster db 0x01 ; sectors per cluster +wReservedSectors dw 0x0001 ; reserved sectors (boot sector) +bTotalFATs db 0x02 ; num copies of the FAT +wMaxRootEntries dw 0x00E0 ; max num entries in root directory +wTotalSectorsSmall dw 0x0B40 ; +bMediaDescriptor db 0xF0 ; +wSectorsPerFAT dw 0x0009 ; length of single FAT copy in sectors +wSectorsPerTrack dw 0x0012 ; sectors per track +wNumHeads dw 0x0002 ; num heads +dwHiddenSectors dd 0x00000000 ; num hidden sectors +dwTotalSectorsLarge dd 0x00000000 ; +bDriveNumber db 0x00 ; 0x00=A: 0x01=B: 0x80=C: ... +bFlags db 0x00 ; +bSignature db 0x29 ; +dwVolumeID dd 0xFFFFFFFF ; +szVolumeLabel db "SCOTT BOOT", 0 ; 11 bytes, null not required +szSystemID db "FAT12 " ; + +AROUND_DATA: +;;; setup segment registers, this code is located at 7C00:0000 + mov dx, BOOT0SEG ; 7C00 + mov ds, dx + mov es, dx + +;;; create a temporary stack at the top of segment 7C00 + mov ss, dx + mov sp, 0xFFFF ; top of this segment + +LOAD_ROOT_DIRECTORY: +;;; +;;; Compute the length (in number of disk sectors) of the floppy's +;;; root directory information: +;;; +;;; wNumRootDirectorySectors = sizeof(directory_entry) * +;;; wMaxRootEntries / wBytesPerSector +;;; +;;; Store this in cx and in the wNumRootDirectorySectors identifier. +;;; + mov ax, 0x0020 ; sizeof(directory_entry) + mul WORD [wMaxRootEntries] + div WORD [wBytesPerSector] + mov cx, ax ; count of num sectors to read for dir + +;;; +;;; Compute the starting location of the root directory (sector number): +;;; +;;; wRootDirectoryStartSector = wReservedSectors + +;;; (bTotalFATs * wSectorsPerFAT) +;;; + mov al, BYTE [bTotalFATs] + mul WORD [wSectorsPerFAT] + add ax, WORD [wReservedSectors] + +;;; +;;; Compute the first sector after the root directory -- this is the +;;; sector where real data starts on the floppy. +;;; + mov WORD [wDriveDataStartSector], ax + add WORD [wDriveDataStartSector], cx + +;;; +;;; Read the floppy's root directory into memory at BOOT0SEG:0200 by +;;; calling ReadSectors. This function reads cx count (wNumRootDirSectors) +;;; starting at sector ax (wRootDirStartSector) into memory at es:bx +;;; + mov bx, 0x0200 + call ReadSectors + +SEARCH_ROOT_DIRECTORY: +;;; +;;; Search the root directory for the stage two loader and the kernel. +;;; + mov cx, WORD [wMaxRootEntries] + mov di, 0x0200 ; offset of first directory entry +.LOOP: + push cx ; save current entry count + mov cx, 0x000B ; 11 char name + mov si, szStage2Image ; name we are seeking + push di ; save current entry ptr + rep cmpsb ; test for match + pop di ; restore entry ptr + jne .TRY_KERN ; match? if not see if it matches the kernel + mov dx, WORD [di+1Ah] ; match. save the starting cluster + mov WORD [wStage2Cluster], dx +.TRY_KERN: + mov cx, 0x000B ; 11 char name + mov si, szKernelImage ; name we are seeking + push di ; save current entry ptr + rep cmpsb ; test for match + pop di ; restore entry ptr + jne .NEXT ; match? if not next entry + mov dx, WORD [di+1Ah] ; match. save the starting cluster + mov WORD [wKernelCluster], dx +.NEXT: + pop cx ; cx = entry count again + add di, 0x0020 ; next directory entry (+32 bytes later) + loop .LOOP ; cx -= 1, jmp .LOOP if cx != 0 + +.DONE: +;;; +;;; Did we find both the kernel and the second stage boot loader? If not +;;; give them a "missing operating system" message. +;;; + mov ax, [wStage2Cluster] + test ax, 0xFFFF + jz .NOOS + mov ax, [wKernelCluster] + test ax, 0xFFFF + jnz LOAD_FAT +.NOOS: + mov si, szNoOperatingSystem + call Write +FAIL: mov ah, 0x00 ; wait for a keystroke and reboot + int 0x16 + int 0x19 + +LOAD_FAT: +;;; +;;; Prepare the load the FAT into memory. Compute the size of the FAT (in +;;; number of sectors) and store in cx for a call to ReadSectors. +;;; + xor ax, ax + mov al, BYTE [bTotalFATs] + mul WORD [wSectorsPerFAT] + mov cx, ax + +;;; +;;; Compute location of FAT and store in ax +;;; + mov ax, WORD [wReservedSectors] + +;;; +;;; Read the FAT into memory at BOOT0SEG:0200. This overwrites the root +;;; directory information but we already have the cluster number for the +;;; stage two boot loader and the kernel. +;;; + mov bx, 0x0200 + call ReadSectors + +;;; +;;; Prepare to read the stage two boot loader into memory at +;;; BOOT1SEG:BOOT1OFF +;;; + ;; point es:bx at where we want the stage two loader read into memory + mov ax, BOOT1SEG + mov es, ax ; destination for image + mov bx, BOOT1OFF ; destination for image + + ;; use the starting cluster we found by reading the directory + mov ax, WORD [wStage2Cluster] + mov WORD [wCluster], ax + + ;; call LOAD_IMAGE to read in the file + call LOAD_IMAGE + +;;; +;;; Prepare to read the kernel into memory at KERNSEG:KERNOFF +;;; + ;; point es:bx at where we want the kernel read into memory + mov ax, KERNSEG + mov es, ax + mov bx, KERNOFF + + ;; use the starting cluster we found by reading the directory + mov ax, WORD [wKernelCluster] + mov WORD [wCluster], ax + + ;; call LOAD_IMAGE to read in the file + call LOAD_IMAGE + +DONE: +;;; +;;; We're done, transfer control to the second stage loader which we +;;; put at BOOT1SEG:BOOT1OFF. +;;; + push WORD BOOT1SEG + push WORD BOOT1OFF + retf + +;; ************************************************************************* +;; PROCEDURE ClusterLBA +;; convert FAT cluster into LBA addressing scheme +;; LBA = (cluster - 2) * sectors per cluster +;; ************************************************************************* +ClusterLBA: + sub ax, 0x0002 ; cluster number is zero-based + xor cx, cx + mov cl, BYTE [bSectorsPerCluster]; convert byte to word + mul cx + add ax, WORD [wDriveDataStartSector]; base data sector + ret + +;;; ************************************************************************* +;;; PROCEDURE LBACHS +;;; +;;; convert ax LBA addressing scheme to CHS addressing scheme using these +;;; formulas: +;;; +;;; absolute sector = (logical sector / sectors per track) + 1 +;;; absolute head = (logical sector / sectors per track) MOD number of heads +;;; absolute track = logical sector / (sectors per track * number of heads) +;;; ************************************************************************* +LBACHS: + xor dx, dx + div WORD [wSectorsPerTrack] + inc dl ; adjust for sector 0 + mov BYTE [bAbsoluteSector], dl + xor dx, dx ; prepare dx:ax for operation + div WORD [wNumHeads] + mov BYTE [bAbsoluteHead], dl + mov BYTE [bAbsoluteTrack], al + ret + +;;; ************************************************************************* +;;; PROCEDURE ReadSectors +;;; +;;; reads disk sectors ax..ax+cx from the disk into memory at es:bx +;;; +;;; ************************************************************************* +ReadSectors: +.MAIN + mov di, 5 ; five retries for disk errors +.SECTORLOOP + push ax + push bx + push cx + call LBACHS ; convert LBA sector in ax into absolute CHT + mov ah, 0x02 ; BIOS read sector + mov al, 0x01 ; read one sector + mov ch, BYTE [bAbsoluteTrack] + mov cl, BYTE [bAbsoluteSector] + mov dh, BYTE [bAbsoluteHead] + mov dl, BYTE [bDriveNumber] + int 0x13 + jnc .SUCCESS ; CF=1 on read error + + ;; read error, reset the disk and try again + xor ax, ax ; BIOS reset disk + int 0x13 + dec di ; error counter -= 1 + pop cx ; restore registers + pop bx + pop ax + jnz .SECTORLOOP ; if error counter != 0, try to read again + + ;; give up + mov si, szReadError + call Write + jmp FAIL + + ;; sector read succeeded +.SUCCESS + pop cx ; restore registers + pop bx + pop ax + inc ax ; next sector + + ;;; bx += wBytesPerSector + add bx, WORD [wBytesPerSector] + loop .MAIN ; cx -= 1, jmp .MAIN if cx != 0 + ret + +;;; ************************************************************************* +;;; PROCEDURE Write +;;; +;;; display ASCIIZ string at ds:si via BIOS +;;; ************************************************************************* +Write: + cld ; make sure direction flag is correct + lodsb ; load char to al, increment si + or al, al ; see if it's null + jz .DONE ; if so, we're finished + mov ah, 0x0E ; service 0x0E: output one char + mov bh, 0x00 ; display page 0 + mov bl, 0x07 ; char text attrib (white on black) + int 0x10 ; invoke bios + jmp Write ; next char +.DONE: + ret + +LOAD_IMAGE: + mov ax, WORD [wCluster] ; cluster to read + call ClusterLBA ; convert cluster to LBA + xor cx, cx + mov cl, BYTE [bSectorsPerCluster] + call ReadSectors ; this increments bx + + ;; compute next cluster + mov ax, WORD [wCluster] ; identify current cluster + mov cx, ax ; copy current cluster + mov dx, ax ; copy current cluster + shr dx, 0x0001 ; dx = wCluster / 2 + add cx, dx ; cx = wCluster + (wCluster / 2) + push bx + mov bx, 0x0200 ; location of FAT in memory + add bx, cx ; index into FAT + mov dx, WORD [bx] ; read two bytes from FAT + pop bx + test ax, 0x0001 ; is the cluster even or odd? + jnz .ODD +.EVEN: + and dx, 0x0FFF ; take low twelve bits + jmp .DONE +.ODD: + shr dx, 0x0004 ; take high twelve bits +.DONE: + mov WORD [wCluster], dx ; store new cluster + cmp dx, 0x0FF0 ; test for end of file + jb LOAD_IMAGE ; if not, go get the next one + ret + +wDriveDataStartSector dw 0x0000 +szStage2Image db "BOOT COM", 0 +wStage2Cluster dw 0x0000 +szKernelImage db "KERNEL BIN", 0 +wKernelCluster dw 0x0000 +szNoOperatingSystem db "Missing OS File", 13, 10, 0 +szReadError db "Disk read error", 13, 10, 0 +wCluster dw 0x0000 +bAbsoluteSector db 0x00 +bAbsoluteHead db 0x00 +bAbsoluteTrack db 0x00 +szProgress db ".", 0 + + TIMES 510-($-$$) DB 0 ; 0 padding + DW 0xAA55 ; bootsector signature |
