Why does my conversion from LBA to CHS not work?

I’m developing a bootloader for an x86 BIOS. In my first-stage bootloader (MBR), I need to read 2880 sectors (or more) from the disk, and then jump to the second-stage bootloader placed in the second sector of the disk. The second stage will then load a kernel file using FAT16, which I’ll implement later.

The function works for LBA values lower than 65, which is sufficient for loading the second stage written in C.

I have defined BPB_SecPerTrk as 18 and BPB_NumHeads as 2

Here is my function code in assembly:

; convert LBA to CHS
; input: si = LBA
; output: ch = cylinder, dh = head, cl = sector
lba_to_chs:
    push bx                         ; save bx
    mov ax, si                      ; load LBA address into ax

    ; Calculate sectors (CL)
    xor dx, dx                      ; clear dx
    div word [BPB_SecPerTrk]        ; ax = LBA / SPT, dx = LBA % SPT
    mov cl, dl                      ; cl = (LBA % SPT) + 1 (sector)
    inc cl                          ; increment cl by 1

    ; Calculate head (DH)
    xor dx, dx                      ; clear dx
    div word [BPB_NumHeads]         ; ax = LBA / (SPT * NumHeads), dx = (LBA / SPT) % NumHeads
    mov dh, dl                      ; dh = (LBA / SPT) % NumHeads (head)

    ; Calculate cylinder (CH)
    mov ch, al                      ; ch = ax (cylinder number, lower 8 bits)
    mov al, ah                      ; al = ah (upper 8 bits of cylinder number)
    shl al, 6                       ; shift upper 2 bits of cylinder to higher bits
    or ch, al                       ; combine them with lower 8 bits of ch

    pop bx                          ; restore bx
    ret

and also here is function responsible for disk init.

disk_init_lba:
    pusha
    ; check if lba extension is supperted
    mov ah, 0x41                    ; check extensions
    mov bx, 0x55AA                  ; magic number
    mov dl, 0x80                    ; disk number
    int 0x13                        ; call BIOS
    stc                             ; DEBUG: implicitly disable reading disk using int 0x13 extensions
    jc .lba_ext_not_sup             ; if carry flag is set, jump to error handler
    jmp .read_lba_ext               ; if not, jump to read disk using LBA
.read_lba_ext:
    mov si, DAPACK                  ; load DAP address to si
    mov ah, 0x42                    ; extended read function
    mov dl, 0x80                    ; disk number
    int 0x13                        ; call BIOS
    jc .fail                        ; if carry flag is set, jump to error handler
    jmp .ok                         ; if not, jump to success handler
.lba_ext_not_sup:
    call print_disk_lba_sup_fail    ; print failure message
    jmp .read_lba_via_chs           ; jump to read disk using CHS
.read_lba_via_chs:
    clc                             ; clear carry flag if for some reason it was set
    xor si, si                      ; LBA = 0
    xor di, di                      ; set di to 0
    mov bx, START_STAGE1            ; buffer for sector
    jmp .loop                       ; jump to loop
.loop:
    inc si                          ; increment LBA
    add bx, 0x200                   ; next sector buffer
    call lba_to_chs                 ; convert LBA to CHS
    mov ah, 0x02                    ; read disk BIOS function
    mov al, 0x01                    ; number of sectors to read
    mov dl, 0x80                    ; disk number 0
    int 0x13                        ; call BIOS
    jc .retry                       ; if carry flag is set, jump to error handler
    ; FIXME: reading LBAs above 65
    ; TODO: read up to 1.44 MB (2879 sectors)
    cmp si, 65                      ; check if we read enough sectors to fill 1.44 MB
    jle .loop                       ; if true read next sector
    jmp .ok                         ; if not, jump to success handler
.retry:
    inc di                          ; increment di
    cmp di, 3                       ; check if we tried 3 times
    jne .loop                       ; if not, retry
    jmp .fail                       ; if yes, jump to error handler
.fail:
    call print_disk_read_fail       ; print failure message
    jmp .exit                       ; jump to exit
.ok:
    call print_disk_read_ok         ; print success message
    jmp .exit                       ; jump to exit
.exit:
    popa
    ret

I think it is related to value overflow some where in the code.

2

The error in lba_to_chs

As found by @ecm the 2 most significant bits for the cylinder number belong to the CL register (bits 6 and 7), so change or ch, al into or cl, al, or else consider using next shorter version of the conversion code:

; IN (si) OUT (cx,dh) MOD (ax,dl)
lba_to_chs:
    mov  ax, si                      ; LBA
    xor  dx, dx
    div  word [BPB_SecPerTrk]
    mov  cx, dx
    inc  cx                          ; Sector
    cwd
    div  word [BPB_NumHeads]
    mov  dh, dl                      ; Head
    shl  ah, 6
    xchg al, ah
    or   cx, ax                      ; Cylinder
    ret

The errors in disk_init_lba

It is not unusual for disk operations to need being repeated. That’s why you have that retry count of 3 in the DI register. What you did however is allow 3 retries for all the sectors together where you should actually be allowing a couple of retries per sector.
But it is even worse than that, you are not retrying on the same sector at all! The “increment LBA” and “next sector buffer” operations must not intervene while retrying.

clc                             ; clear carry flag if for some reason it was set
xor si, si                      ; LBA = 0

No need for this clc, the xor si, si that follows clears the carry flag anyway.

disk_init_lba:
    pusha
    ...
.read_lba_via_chs:
    xor  si, si                      ; LBA = 0
    mov  bx, START_STAGE1            ; buffer for sector
.NextSector:
    inc  si                          ; increment LBA
    add  bx, 0x200                   ; next sector buffer
    mov  di, 3                       ; Tries per sector
.NextTry:
    call lba_to_chs                  ; convert LBA to CHS
    mov  ax, 0x0201                  ; read 1 sector
    mov  dl, 0x80                    ; disk number
    int  0x13                        ; call BIOS
    jc   .retry
    ; FIXME: reading LBAs above 65
    ; TODO: read up to 1.44 MB (2879 sectors)
    cmp  si, 65                      ; check if we read enough sectors
    jbe  .NextSector
    jmp  .ok                         ; if not, jump to success handler
.retry:
    dec  di                          ; more tries?
    jnz  .NextTry                    ; yes
.fail:
    call print_disk_read_fail        ; print failure message
    jmp  .exit
.ok:
    call print_disk_read_ok          ; print success message
.exit:
    popa
    ret

; FIXME: reading LBAs above 65

Instead of modifying BX, keep it fixed and advance the ES segment register by 32 (512 / 16).

disk_init_lba:
    pusha
    push es
    ...
.read_lba_via_chs:
    xor  si, si                      ; LBA = 0
    mov  bx, START_STAGE1            ; buffer for sector
.NextSector:
    inc  si                          ; increment LBA
    mov  ax, es                      ; next sector buffer ES:BX
    add  ax, 32
    mov  es, ax
    mov  di, 3                       ; Tries per sector
.NextTry:
    call lba_to_chs                  ; convert LBA to CHS
    mov  ax, 0x0201                  ; read 1 sector
    mov  dl, 0x80                    ; disk number
    int  0x13                        ; call BIOS
    jc   .retry
    cmp  si, 65                      ; check if we read enough sectors
    jbe  .NextSector
    jmp  .ok
.retry:
    dec  di                          ; more tries?
    jnz  .NextTry                    ; yes
.fail:
    call print_disk_read_fail        ; print failure message
    jmp  .exit
.ok:
    call print_disk_read_ok          ; print success message
.exit:
    pop  es
    popa
    ret

; TODO: read up to 1.44 MB (2879 sectors)

You can’t hope to read that many bytes. Your present code is confined to using the conventional memory, so at most a little less than 655360 bytes would be feasible…
More than enough for your second stage I would say.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật