Table of Content
- Loading Program in Boot Sector Code
Loading Program in Boot Sector Code
A boot sector has maximally 512 bytes, and cannot store a larger program. For an operating system, the boot sector contains a small program, e.g., MBR that is part of the operating system and whose job is almost solely to load a large program. This larger program is typically either the operating system kernel, or a rather sophisticated boot loader (or boot manager) program that is responsible to load the operating system kernel. Nevertheless, the boot sector code must be able to load and execute another program on the disk. Since the operating system has not loaded any file systems code, often a large chunk of code, the system cannot easily load and read a file.
For convenience, we call the program that the boot sector code is to load and execute the second program. In this section, we devise a very simple method for the boot sector code to locate, load, and execute the second program, i.e., we place the code of the second program immediately following the boot sector.
Experiment and Programming Environment
Refer to the bootstrap tutorial for the experiment and programming environment, and for how to compile and run the programs here.
Example 0 Memory Segment and Segment Registers
;
; load0.asm
;
mov ax, 0x7c0
mov ds, ax
mov bx, MSG
mov ah, 0x0e
LOOP:
mov al,[ds:bx]
cmp al, 0
je DONE_PRINT
int 0x10
inc bx
jmp LOOP
DONE_PRINT:
jmp $
MSG:
db "Hello, Segment!", 0
times 510-($-$$) db 0
dw 0xaa55
Example 1 Print Message Function Revisited
;
; load1.asm
;
mov ax, 0x7c0
mov ds, ax
mov ax, HELLO_MSG ; 1st variable
mov cx, WORLD_MSG ; 2nd variable
mov bx, ax ; pass argument via bx and ds
call print_msg ; call the function
mov bx, cx ; pass argument via bx and ds
call print_msg ; call the function
mov bx, ax
call print_msg
mov bx, cx
call print_msg
jmp $
%include "print_msg.asm"
HELLO_MSG :
db 'Hello!', 0 ;
WORLD_MSG :
db 'World!', 0 ;
times 510-($-$$) db 0
dw 0xaa55
and
;
; print_msg.asm
;
; we implement a function with interface
; void print_msg(char* msg)
; where we pass the argument msg via ds:bx
print_msg:
pusha ; push all registers to stack
mov ah, 0x0e
LOOP:
mov al, [ds:bx]
cmp al, 0
je DONE_PRINT_MSG
int 0x10
inc bx
jmp LOOP
DONE_PRINT_MSG:
popa ; pop all registers to stack
ret
Example 2 Querying Disk Drive Paramegers
[org 0x7c00]
; query disk drive parameters
;
; load2.asm
;
; using PC BIOS's 0x13 interrupt
; See
; https://en.wikipedia.org/wiki/INT_13H#INT_13h_AH=08h:_Read_Drive_Parameters
READ_DISK:
mov ah, 0x08
mov dl, 0x80 ; 1st HDD
mov ax, 0
mov es, ax
mov di, ax
int 0x13
jc .DISK_ERROR ; using local label. local labels start from "."
push cx
push bx
mov bx, DISK_NO
call print_msg
mov cl, dl
call print_byte
pop bx
call print_line
push bx
mov bx, HEAD_IDX
call print_msg
mov cl, dh
call print_byte
pop bx
call print_line
pop cx
push cx
push bx
mov bx, SECTOR_NUM
call print_msg
pop bx
mov cl, ch
call print_byte
call print_space
pop cx
call print_byte
pop cx
jmp $
.DISK_ERROR:
mov bx, DISK_ERROR_MSG
call print_msg
jmp $
%include "print_byte.asm"
%include "print_msg.asm"
%include "print_line.asm"
%include "print_space.asm"
DISK_ERROR_MSG:
db "Disk error", 0
DISK_NO:
db "DISK # (hex): ", 0
HEAD_IDX:
db "HEAD INDEX (hex): ", 0
SECTOR_NUM:
db "CYL/SEC NUMBERS (hex): ", 0
times 510-($-$$) db 0
dw 0xaa55
times 512 db 0
times 512 db 0
Example 3 Reading Data from Disks
;
; load3.asm
;
[org 0x7c00]
START:
mov [BOOT_DRIVE], dl
mov bp, 0x8000
mov sp, bp
mov ah, 0x00 ; resetting disk
int 0x13
; pass arguments
mov ah, 0x02 ; int 13h function 02
mov al, 0x02 ; read 2 sectors
push ax
mov ch, 0x00 ; cylinder no.
mov cl, 0x02 ; sector no. (sector 1 is boot sector)
push cx
mov dh, 0x00 ; head no.
mov dl, [BOOT_DRIVE] ; drive no.
push dx
push es ; read disk data to es:bx
mov bx, 0x9000
push bx
call read_disk ; read disk data
mov cl, [0x9000]
call print_byte
call print_space
mov cl, [0x9000+512]
call print_byte
jmp $
%include "read_disk.asm"
%include "print_byte.asm"
%include "print_mem.asm"
%include "print_msg.asm"
%include "print_space.asm"
%include "print_line.asm"
BOOT_DRIVE:
db 0
times 510 -($-$$) db 0
dw 0xaa55
times 512 db 0xab
times 512 db 0xba
Example 4 Loading Program Code from Disk
;
; load4.asm
;
[org 0x7c00]
START:
mov [BOOT_DRIVE], dl
mov bp, 0x8000
mov sp, bp
mov ah, 0x00 ; resetting disk
int 0x13
; pass arguments
mov ah, 0x02 ; int 13h function 02
mov al, 0x02 ; read 2 sectors
push ax
mov ch, 0x00 ; cylinder no.
mov cl, 0x02 ; sector no. (sector 1 is boot sector)
push cx
mov dh, 0x00 ; head no.
mov dl, [BOOT_DRIVE] ; drive no.
push dx
mov bx, 0x0000 ; read disk data to es:bx
mov es, bx
push es
mov bx, 0x9000
push bx
call read_disk ; read disk data
mov bx, READ_DISK
call print_msg
call print_line
mov cl, [0x9000]
call print_byte
call print_space
mov cl, [0x9001]
call print_byte
call print_space
mov cl, [0x9002]
call print_byte
call print_space
mov cl, [0x9003]
call print_byte
call print_space
mov bx, 0x0000
mov es, bx
push es
mov bx, 0x9000
push bx
retf
%include "read_disk.asm"
%include "print_byte.asm"
%include "print_mem.asm"
%include "print_msg.asm"
%include "print_space.asm"
%include "print_line.asm"
BOOT_DRIVE:
db 0
READ_DISK:
db "Read new program from disk sectors.", 0
times 510 -($-$$) db 0
dw 0xaa55
main:
mov ah, 0x0e
mov al, 0x0d
int 0x10
mov ah, 0x0e
mov al, 0x0a
int 0x10
mov ah, 0x0e
mov al, 'S'
int 0x10
mov ah, 0x0e
mov al, 't'
int 0x10
mov ah, 0x0e
mov al, 'a'
int 0x10
mov ah, 0x0e
mov al, 'r'
int 0x10
mov ah, 0x0e
mov al, 't'
int 0x10
mov ah, 0x0e
mov al, 'e'
int 0x10
mov ah, 0x0e
mov al, 'd'
int 0x10
mov ah, 0x0e
mov al, ' '
int 0x10
mov ah, 0x0e
mov al, 'n'
int 0x10
mov ah, 0x0e
mov al, 'e'
int 0x10
mov ah, 0x0e
mov al, 'w'
int 0x10
mov ah, 0x0e
mov al, ' '
int 0x10
mov ah, 0x0e
mov al, 'p'
int 0x10
mov ah, 0x0e
mov al, 'r'
int 0x10
mov ah, 0x0e
mov al, 'o'
int 0x10
mov ah, 0x0e
mov al, 'g'
int 0x10
mov ah, 0x0e
mov al, 'r'
int 0x10
mov ah, 0x0e
mov al, 'a'
int 0x10
mov ah, 0x0e
mov al, 'm'
int 0x10
mov ah, 0x0e
mov al, '.'
int 0x10
jmp $
ANNOUNCEMENT:
db "New program starts...", 0
times 1024-($-$$) db 0
times 512 db 0
Required Subroutines
The above example programs reference to a few subroutines.
Printing a Byte in Hexidecimal
;
; print_byte.asm
;
; print a byte in register cl to hex assuming ASCII encoding
; void * print_byte(char c)
print_byte:
pusha ; Push AX, CX, DX, BX, original SP, BP, SI, and DI.
mov ch, cl
shr ch, 4
and ch, 0x0f
push cx
mov cl, ch
call print_low_4bits
pop cx
mov ch, cl
and ch, 0x0f
push cx
mov cl, ch
call print_low_4bits
pop cx
popa
ret
; print lower 4 bits in register cl to hex assuming ASCII encoding
; void * print_byte(char c)
print_low_4bits:
pusha
mov ah, 0x0e
mov ch, cl
and ch, 0x0f
cmp ch, 0x09
jg A_TO_F
add ch, '0'
jmp PRINT_CHAR
A_TO_F:
sub ch, 0x0a
add ch, 'A'
jmp PRINT_CHAR
PRINT_CHAR:
mov al, ch
int 0x10
popa
ret
Printing Link Break
;
; print_line.asm
;
print_line:
pusha
; line feed
mov ah, 0x0e
mov al, 0x0a
int 0x10
; carriage return
mov ah, 0x0e
mov al, 0x0d
int 0x10
popa
ret
Printing Memory Buffer in Hexadecimals
;
; print_mem.asm
;
; print memory in hex byte by byte separated by a space
; void print_mem(void *, short num);
; the parameter is passed via registers ds:bx and cl
print_mem:
pusha
mov ah, 0x0e
cmp cl, 0
je NOTHING_TO_PRINT
xor ch, ch
LOOP:
push cx
mov cl, [ds:bx]
call print_byte
pop cx
inc bx
dec cl
inc ch
cmp cl, 0
je NOTHING_TO_PRINT
and ch, 0x0f
cmp ch, 0
jnz DO_PRINT_SPACE
call print_line
jmp LOOP
DO_PRINT_SPACE:
call print_space
jmp LOOP
NOTHING_TO_PRINT:
popa
ret
Printing String
;
; print_msg.asm
;
; we implement a function with interface
; void print_msg(char* msg)
; where we pass the argument msg via ds:bx
print_msg:
pusha ; push all registers to stack
mov ah, 0x0e
.LOOP:
mov al, [ds:bx]
cmp al, 0
je DONE_PRINT_MSG
int 0x10
inc bx
jmp .LOOP
DONE_PRINT_MSG:
popa ; pop all registers to stack
ret
Printing Space
;
; print_space.asm
;
print_space:
pusha
mov ah, 0x0e
mov al, ' '
int 0x10
popa
ret
Reading Disk Sectors
;
; read_disk.asm
;
; read disk sectors with prototype
; uint16 read_disk(
; uint16 offset,
; uint16 segment,
; uint8 sector_count,
; uint8 cylinder,
; uint8 sector,
; uint8 head,
; uint8 drive)
; the subroutine expects the parameters passed in stack
; offset
; segment
; cylinder sector
; head rive
; 0000 0010b count
;
; return value is also in stack
; uint16
; the subroutine reads disk via PC BIOS int13h
; See https://en.wikipedia.org/wiki/INT_13H
;
read_disk:
; for near call
; ip on the top of the stack
push ax
push cx
push dx
push bp
; parameters are at sp+10
mov bp, sp
mov bx, [bp+10] ; disk data to es:bx
mov ax, [bp+12]
mov es, ax
mov dx, [bp+14] ; dh dl are head no. and drive no.
mov cx, [bp+16] ; ch cl are cylinder and sector no.'s
mov ax, [bp+18]
mov ah, 0x02
push ax
int 0x13
pop dx
jc .DISK_ERROR
cmp dh, al
jne .DISK_SHORT_READ
pop bp
pop dx
pop cx
pop ax
sub bp, 2*5
ret
.DISK_ERROR:
mov bx, DISK_ERROR_MSG
call print_msg
jmp $
.DISK_SHORT_READ:
mov bx, SHORT_READ_MSG
call print_msg
jmp $
DISK_ERROR_MSG:
db "Disk read error!", 0
SHORT_READ_MSG:
db "Disk read error, short read!", 0