Boot: Load Sector | Index | Boot: Data Calculations

Write Your Own OS from Scratch - Chapter 1. Boot

Boot: Root Calculations

We start off by adding a new subroutine. This one calculates a logical block address to a Cylinder, Head, Sector value.

;-------------------------------------------------------
; Calculate CHS
; Input:
;     AX:DX = Logical Sector
; Output:
;     sector
;     head
;     track/cylinder
;-------------------------------------------------------
; sector = (lba % sectors per track) + 1
; head = lba / sectors per track % number of heads
; track = lba / sectors per track / number of heads
calcchs:
    cmp     dx, [secpertrack]                           ; Check within track
    jnc     calcchsfail                                 ; if not fail
    div     word [secpertrack]                          ; LBA / secpertrack
    inc     dl                                          ; Sector = lba % secpertrack + 1
    mov     [sector], dl                                ; Save in sector
    xor     dx, dx                                      ; Clear the modulous
    div     word [heads]                                ; lba / secpertrack / heads
    mov     [currenthead], dl                           ; currenthead = lba / secpertrack % heads
    mov     [track], ax                                 ; Track = lba / secpertrack / heads
    clc                                                 ; Clear carry to report success
    ret                                                 ; Return from subroutine
calcchsfail:
    stc                                                 ; Set carry to report failure
    ret                                                 ; Return from subroutine
		

Next we need someplace to store the lba for the root directory calculations.

root:           dw      0                               ; lba for root
rootmod:        dw      0                               ; lba modulous for root
		

Now we calculate the logical block address of the root directory.

    mov     al, [fats]                                  ; fats * fatsecs + hiddensecs + ressectors
    mul     word [fatsecs]
    add     ax, [hiddensecs]
    adc     dx, [hiddensecs+2]
    add     ax, [ressectors]
    adc     dx, byte 0                                  ; if carry increment modulous
    mov     [root], ax                                  ; Save result in root
    mov     [rootmod], dx                               ; Save modulous in rootmod
		

Next we remove the following code because we no longer wish to load sector 2 we want to load the root directory sector that we calculated.

    mov     [track], ax                                 ; Zero track
    mov     [currenthead], al                           ; Zero currenthead
    mov     al, 2                                       ; Sector 2
    mov     [sector], al
		

Now it's time to get the CHS values from the LBA we calculated for the root directory.

    call    calcchs                                     ; AX = root, Calculate CHS
    jc      loaderror                                   ; If there is an issue show load error
		

boot.asm

;=======================================================
; Boot Sector - boot.asm
; -----------------------------------------------------
; Written in NASM
; Written by Marcus Kelly
;=======================================================

    org     07C00h                                      ; Assemble code for this offset

    jmp     short start                                 ; Jump to boot sector start
    nop                                                 ; place holder

oemname:        db      "DEMO OS "                      ; Operating system name
bytespersec:    dw      512                             ; Bytes per sector
secperclust:    db      1                               ; Sectors per cluster
ressectors:     dw      1                               ; Reserved Sectors
fats:           db      2                               ; Number of FATs on disk
rootdirents:    dw      224                             ; Number of directory entries
sectors:        dw      2880                            ; Sectors per disk
media:          db      0F0h                            ; Always F0 hex for floppy
fatsecs:        dw      9                               ; Sectors per FAT
secpertrack:    dw      18                              ; Sectors per track
heads:          dw      2                               ; Number of heads
hiddensecs:     dd      0                               ; Hidden sectors
hugesectors:    dd      0                               ; Huge sectors
drivenumber:    db      000h                            ; Drive number 000h for floppy
currenthead:    db      0                               ; Reserved byte we are using for disk read
bootsignature:  db      029h                            ; Boot signature
volumeid:       dd      02394C726h                      ; Serial Number
vlumelabel:     db      "DEMO OS DSK"                   ; Disk label
filesystype:    db      "FAT12   "                      ; File system
track:          dw      0                               ; Track for disk read
sector:         db      0                               ; Sector for disk read
root:           dw      0                               ; lba for root
rootmod:        dw      0                               ; lba modulous for root

start:
    cli                                                 ; Turn off interrupts
    cld							; Make sure we are going forward
    xor     ax, ax                                      ; Zero the ax register
    mov     ds, ax                                      ; Make sure data segment is zero
    mov     es, ax                                      ; Make sure extra segment is zero
    mov     ss, ax                                      ; Make sure stack segment is zero
    mov     sp, 07C00h                                  ; Place stack ptr before boot sector
    sti                                                 ; Turn on interrupts

    int     013h                                        ; AH = 0, Reset disk system

    mov     al, [fats]                                  ; fats * fatsecs + hiddensecs + ressectors
    mul     word [fatsecs]
    add     ax, [hiddensecs]
    adc     dx, [hiddensecs+2]
    add     ax, [ressectors]
    adc     dx, byte 0                                  ; if carry increment modulous
    mov     [root], ax                                  ; Save result in root
    mov     [rootmod], dx                               ; Save modulous in rootmod

    mov     bx, 00700h                                  ; Load second sector at 00000h:00700h

    call    calcchs                                     ; AX = root, Calculate CHS
    jc      loaderror                                   ; If there is an issue show load error
    call    readsec                                     ; Read Sector
    jc      loaderror

    jmp     00070h:00000h                               ; Jump to code from sector 2


loaderror:
    mov     si, message                                 ; si is set to offset Message
    call    strout                                      ; call our string output routine

die:
    jmp     die                                         ; This is our die loop so the
                                                        ;  computer doesn't run random
                                                        ;  code

;-------------------------------------------------------
; String Output
; Input:
;    DS:SI = Message offset
; Output:
;    AX, BX, SI are destroyed
;-------------------------------------------------------
strout:
    lodsb                                               ; Get character from string
    or      al, al                                      ; Check if our character is zero
    jz      stroutdone                                  ; if it is zero we have finished
    mov     ah, 0Eh                                     ; BIOS print call
    mov     bx, 00007h                                  ; Character attribute
    int     010h                                        ; Call the video BIOS
    jmp     strout                                      ; Get next character
stroutdone:
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Calculate CHS
; Input:
;     AX:DX = Logical Sector
; Output:
;     sector
;     head
;     track/cylinder
;-------------------------------------------------------
; sector = (lba % sectors per track) + 1
; head = lba / sectors per track % number of heads
; track = lba / sectors per track / number of heads
calcchs:
    cmp     dx, [secpertrack]                           ; Check within track
    jnc     calcchsfail                                 ; if not fail
    div     word [secpertrack]                          ; LBA / secpertrack
    inc     dl                                          ; LBA % secpertrack + 1
    mov     [sector], dl                                ; Save in sector
    xor     dx, dx                                      ; Clear the modulous
    div     word [heads]                                ; lba / secpertrack / heads
    mov     [currenthead], dl                           ; lba / secpertrack % heads
    mov     [track], ax                                 ; lba / secpertrack / heads
    clc                                                 ; Clear carry to report success
    ret                                                 ; Return from subroutine
calcchsfail:
    stc                                                 ; Set carry to report failure
    ret                                                 ; Return from subroutine


;-------------------------------------------------------
; Read Sector
; Input:
;    bsTrack = Track
;    bsSector = Sector
;    bsHead = Head
;    ES:BX = Location to load sector at
; Output:
;       AX, BX, CX, DX are destroyed
;-------------------------------------------------------
readsec:
    mov     dx, [track]	                                ; Get the track
    mov     cl, 006h                                    ; Rotate the bits 8 and 9 into position
    shl     dh, cl
    or      dh, [sector]                                ; Get the sector with out changing high bits
    mov     cx, dx                                      ; Place in the correct register
    xchg    ch, cl                                      ; Swap bytes to complete the process
    mov     dl, [drivenumber]                           ; Drive number
    mov     dh, [currenthead]                           ; Current Head
    mov     ax, 00201h                                  ; BIOS Disk Read 1 Sector
    int     013h
    ret                                                 ; Return from subroutine

;-------------------------------------------------------
; Data Area
;-------------------------------------------------------

message:        db      00Dh, 00Ah, "Error Loading"     ; Message to be printed
                db      00Dh, 00Ah, 000h

                times   510-($-$$) db 0                 ; inserts zeros from this address
                                                        ;   to address 510
                dw      0AA55h                          ; Boot signature at 510 / 511
		

New test code

Again we modify our base program to signify the directory we have loaded. A simple change of the message string is all that is needed here. Open demoos2.asm and save it as rootdir.asm. Then modify the following:

message:        db      00Dh, 00Ah, "Sector 2"          ; Message to be printed
                db      00Dh, 00Ah, 000h
		

to

message:        db      00Dh, 00Ah, "Root Directory"    ; Message to be printed
                db      00Dh, 00Ah, 000h
		

rootdir.asm

;=======================================================
; Demo OS - rootdir.asm
; -----------------------------------------------------
; Written in NASM
; Written by Marcus Kelly
;=======================================================

    org     00000h                                      ; Assemble code for this offset

start:
    cli                                                 ; Turn off interrupts
    cld							; Make sure we are going forward
    xor     ax, ax                                      ; Zero the ax register
    mov     ds, ax                                      ; Make sure data segment is zero
    mov     ss, ax                                      ; Make sure stack segment is zero
    mov     sp, 07C00h                                  ; Place stack ptr before boot sector
    sti                                                 ; Turn on interrupts

    push    cs                                          ; Make sure data segment = code segment
    pop     ds

    mov     si, message                                 ; si is set to offset Message
    call    strout                                      ; call our string output routine

die:                                                    ; This is our die loop so the
    jmp     die                                         ;  computer doesn't run random
                                                        ;  code

;-------------------------------------------------------
; String Output
; Input:
;    DS:SI = Message offset
; Output:
;    AX, BX, SI are destroyed
;-------------------------------------------------------
strout:
    lodsb                                               ; Get character from string
    or      al, al                                      ; Check if our character is zero
    jz      stroutdone                                  ; if it is zero we have finished
    mov     ah, 00Eh                                    ; BIOS print call
    mov     bx, 00007h                                  ; Character attribute
    int     010h                                        ; Call the video BIOS
    jmp     strout                                      ; Get next character
stroutdone:
    ret                                                 ; return from subroutine

;-------------------------------------------------------
; Data Area
;-------------------------------------------------------

message:        db      00Dh, 00Ah, "Root Directory"    ; Message to be printed
                db      00Dh, 00Ah, 000h

                times   510-($-$$) db 0                 ; inserts zeros from this address
                                                        ;   to address 510
                dw      0AA55h                          ; Boot signature at 510 / 511
		

Modify the disk image

Add the following lines to your osdisk.asm file to add rootdir.bin to your disk image.

                times 9728-($-$$) db 0                  ; Place our file at 9728
incbin "rootdir.bin"                                    ; root = fats(2) * fatsecs(9) + ressecs(1)
                                                        ; 9728 = 19 * 512
		

Now lets build our disk. If you are running the previous tutorial in VirtualBox Remember to remove the disk or close virtual box before assembling the disk image.

> nasm boot.asm -o boot.bin
> nasm rootdir.asm -o rootdir.bin
> nasm osdisk.asm -o osdisk.img
		

Your osdisk.img can now be loaded with VirtualBox. The result should be as follows:


Root Directory
		
Boot: Load Sector | Index | Boot: Data Calculations

Support This Project On Patreon

Copyright © 2021 Marcus Kelly