I am building an operating system and am experiencing a problem where the bootloader successfully prints its message, but the kernel's message isn't displayed. Here's a breakdown:
Assembler and Environment:
push ax
push bx
mov bl, dl ; This is to preserve dl
push bx
This code snippet is part of a bootloader, and it might seem confusing initially.
The requirement here is to preserve the dl register throughout the function. You might ask:
push dl
onto the stack?
push dx
?
dh
serves as a return value in this context.To work around these constraints, I took this approach:
dl into the lower bits of bx
(i.e., bl).now holding the value of dl
) onto the stack again.mov dl, bl
.bootloader.asm
[org 0x7c00]
[bits 16]
jmp main
_message_read_err: db 'Error in reading floppy!', 0
_message_boot: db 'Booting SSoS...', 0
_sectors_per_track: dw 18
_head_count: dw 2
_kernel_start_LBA: dw 1
_kernel_size: dw 1
_stack_ptr_addr: dw 0x7c00
_es_start: dw 0x7c0
; Arguments:
; None
; Returns:
; None
main:
mov ax, 0 ; can't write to ds and ss directly
mov ds, ax
mov ss, ax
mov sp, [_stack_ptr_addr] ; stack pointer
mov ax, [_es_start] ; address * 16 = real address -> 0x7c0 * 16 (dec) = 0x7c00
mov es, ax
mov si, _message_boot
call puts
; Converting LBA to CHS
;; Parameters
mov si, 1
call LBA_to_CHS ; after this call, CH, CL, and DH will have cylinder, sector, and head values
mov bx, 0x200
mov al, [_kernel_size] ; number of sectors to read
call read_disk
jmp 0:7e00h
; Arguments:
; si - Points to the string to be printed
; Returns:
; None
puts:
push ax
.begin:
mov ah, 0x0E
lodsb
cmp al, 0
je .done
int 10h
jmp .begin
.done:
pop ax
ret
; Arguments:
; si - LBA adress
; Returns:
; ch, dh, cl - CHS adress
;; dx and cx will have to be modified
LBA_to_CHS:
push ax
push bx
mov bl, dl ; This is to preserve dl
push bx
mov ax, [_head_count]
mul word [_sectors_per_track]
mov bx, ax
mov ax, si
mul word bx
mov ch, al ; Put lower bits of ax as the cylinder address
mov ax, si
div word [_sectors_per_track] ; result in dx:ax
div word [_head_count] ; result also in dx:ax (dx is remainder)
mov dh, dl ; since dx is composed of dh (higher bits) and dl (lower bits) we want to move the lower bits into dh
push dx
mov ax, si
div word [_sectors_per_track] ; remainder in dx
inc dx
mov cl, dl
pop dx
pop bx
mov dl, bl
pop bx
pop ax
ret
; Arguments:
; bx - Address to load the data
; ch, cl, dh - CHS values
; Returns:
; None
read_disk:
push ax
push si
mov si, 4
.retry:
dec si
cmp si, 0
je .read_error
mov ah, 02h
mov al, [_kernel_size]
int 13h
jc .retry
pop si
pop ax
ret
.read_error:
mov si, _message_read_err
call puts
cli
hlt
times 510-($-$$) db 0
dw 0aa55h
kernel.asm
[org 0x7E00]
[bits 16]
jmp main
_message: db 'Hello from kernel!', 0
main:
mov si, _message
call puts
cli
hlt
puts:
mov ah, 0x0E
.begin:
lodsb
cmp al, 0
je .done
int 10h
jmp .begin
.done:
ret
Makefile
BUILD_DIR = build
SRC_DIR = src
IMG_NAME = dev
dev: image
qemu-system-i386 -fda $(BUILD_DIR)/$(IMG_NAME).img
image: $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin
@dd if=$(BUILD_DIR)/bootloader.bin of=$(BUILD_DIR)/$(IMG_NAME).img bs=512
@dd if=$(BUILD_DIR)/kernel.bin of=$(BUILD_DIR)/$(IMG_NAME).img bs=512 seek=1
@truncate -s 1440k $(BUILD_DIR)/$(IMG_NAME).img
@hexdump -C $(BUILD_DIR)/$(IMG_NAME).img
@objdump -b binary -m i8086 -M intel -D build/dev.img
clean:
rm -f $(BUILD_DIR)/bootloader.bin $(BUILD_DIR)/kernel.bin
$(BUILD_DIR)/%.bin: $(SRC_DIR)/%.asm
nasm $^ -f bin -o $@
Upon booting, the message "Booting SSoS..." is displayed, but the kernel's "Hello from kernel!" message isn't.
Someone also had the same problem here. I tried removing the mov dl, 0
in the read_disk function but it still didin't work.
I suspect the issue might be in the read_disk function within the bootloader.
Even though no error message displays, the qemu-system-i386 command seems unusual.
Constants like _sectors_per_track and drive number (register dl in read_disk function) seem accurate. However, even when I attempted different disk-reading methods, the problem was still occurring.
I know this is a complex and low-level topic which requires a deep understanding of operating systems. That's why I thank everyone who will try to help me. Thanks a lot!
I tried different methods of reading the floppy disk (like reading directly with CHS adressing) however none of them worked. I also double-checked my constants and looked on the disassebly of the image but everything seems to be right. I looked into others code but mine seems (almost except it's not that perfect) identical.
Update:
Below is Your code:
xor dx,dx
here and theremul word bx
should be div word bx
0x7e00
.push
and pop
instructions do nothing special. You can remove them.Source:
https://datacadamia.com/io/drive/lba
https://en.wikipedia.org/wiki/Logical_block_addressing
But code seems to work fine!
Result is:
Bootloader.asm
[org 0x7c00]
[bits 16]
jmp main
_message_read_err: db 'Error in reading floppy!', 0
_message_boot: db 'Booting SSoS...', 0
_sectors_per_track: dw 18
_head_count: dw 2
_kernel_start_LBA: dw 1
_kernel_size: dw 1
_stack_ptr_addr: dw 0x7c00 ;prev value 0x7b00
_es_start: dw 0x7c0
; Arguments:
; None
; Returns:
; None
main:
mov ax, 0 ; can't write to ds and ss directly
mov ds, ax
mov ss, ax
mov sp, [_stack_ptr_addr] ; stack pointer
mov ax, [_es_start] ; address * 16 = real address -> 0x7c0 * 16 (dec) = 0x7c00
mov es, ax
mov si, _message_boot
call puts
; Converting LBA to CHS
;; Parameters
;(* 1 *)
;; before call to LBA_to_CHS
;; ax = 0x07c0
;; bx = unknown value, could be anything from 16-bit range (0x0000 - 0xFFFF)
;; cx = same cx, not initialized
;; dx = same here
;; unknown values could cause unexpected results ;)
;; Inside LBA_to_CHS You push ax / push bx / mov bl, dl / push bx
;; and before return from LBA_to_CHS You pop those values
;; So after procedure:
;; ax = popped value 0x07c0, then al = _kernel_size = 1, ah = 0x07 changed inside read_disk to 0x02
;; bx = unknown value but set later to 0x0200
;; cx = ch - cylinder, cl - sector
;; dx = dh - head, dl - popped value, are you sure this is drive number (0 = A:)
mov si, 1 ;LBA = si = 1
call LBA_to_CHS ; after this call, CH, CL, and DH will have cylinder, sector, and head values
mov bx, 0x200
mov al, [_kernel_size] ; number of sectors to read
call read_disk
jmp 0:7e00h
; Arguments:
; si - Points to the string to be printed
; Returns:
; None
puts:
push ax
.begin:
mov ah, 0x0E
lodsb
cmp al, 0
je .done
int 10h
jmp .begin
.done:
pop ax
ret
; Arguments:
; si - LBA adress
; Returns:
; ch, dh, cl - CHS adress
;; dx and cx will have to be modified
LBA_to_CHS:
push ax ; push ax and push bx won't destroy any necessary data
push bx
mov bl, dl ; dl = trash values
push bx ; delete this too
; C = LBA ÷ (HPC × SPT)
mov ax, [_head_count] ; ax = 2
mul word [_sectors_per_track] ; ax = 2 * 18 = 36
mov bx, ax ; bx = 36
mov ax, si ; ax = 1
xor dx,dx ; dx = 0
;(* 2 *)
; here You have mul word bx so I changed this to div word bx
; we divide LBA mod (Heads * Sectors) not mul
;mul word bx
;should be div word bx
div word bx ; ax = 0 dx = 1
mov ch, al ; Put lower bits of ax as the cylinder address
;(* 3 *)
; I used xor dx, dx before div instructions because in 16-bit division dividend is in dx:ax
; so we have to clear dx or our result maybe wrong value
; H = (LBA ÷ SPT) mod HPC
xor dx, dx ; dx = 0
mov ax, si ; ax = 1
div word [_sectors_per_track] ; 18 ; result in dx:ax, ax = 0 dx = 1
xor dx, dx
div word [_head_count] ; 2 ; result also in dx:ax (dx is remainder) ax = 0 dx = 0
mov dh, dl ; since dx is composed of dh (higher bits) and dl (lower bits) we want to move the lower bits into dh
push dx ; save dx
; S = (LBA mod SPT) + 1
xor dx, dx ; dx = 0
mov ax, si ; ax = 1
div word [_sectors_per_track] ; 18 ; remainder in dx, ax = 0 dx = 1
inc dx ; dx = 2
mov cl, dl
pop dx
pop bx ; delete
mov dl, bl ; dl = trash
pop bx ; delete
pop ax ; delete
ret
; Arguments:
; bx - Address to load the data
; ch, cl, dh - CHS values
; Returns:
; None
read_disk:
push ax
push si
mov si, 4
.retry:
dec si
cmp si, 0
je .read_error
mov ah, 02h
mov al, [_kernel_size]
int 13h
jc .retry
pop si
pop ax
ret
.read_error:
mov si, _message_read_err
call puts
cli
hlt
times 510-($-$$) db 0
dw 0aa55h
Kernel.asm
[org 0x7e00]
[bits 16]
jmp main
_message: db 'Hello from kernel!', 0
main:
mov si, _message
call puts
cli
hlt
puts:
mov ah, 0x0E
.begin:
lodsb
cmp al, 0
je .done
int 10h
jmp .begin
.done:
ret