Table of Content
Loading Program in Boot Sector Code
We can now load and execute a second program in the boot sector code. Below include examples that continue explore this, in particular, how we may load and execute programs with subroutines and how we may dynamically address data or instructions in the second programs.
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 1 Loading and Executing Programs with Subroutine
The following programs include subroutines that you shall find immediately follows the program below
[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 ; push es, bx twice; one set for retf, and one set for the main
mov es, bx
push es
mov bx, 0x9000
push bx
push es
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:
pop cx
pop ds
mov ah, 0x0e
mov al, 0x0d
int 0x10
mov ah, 0x0e
mov al, 0x0a
int 0x10
mov ax, ANNOUNCEMENT
sub ax, main
add ax, cx
mov bx, ax
call main_print_msg
jmp $
ANNOUNCEMENT:
db "New program starts...", 0
%include "main_print_msg.asm"
times 1024-($-$$) db 0
times 512 db 0
Required Functions
;
; 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
;
; 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
;
; 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
;
; 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
;
; print_space.asm
;
print_space:
pusha
mov ah, 0x0e
mov al, ' '
int 0x10
popa
ret
;
; 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
;
; main_print_msg.asm
;
; we implement a function with interface
; void main_print_msg(char* msg)
; where we pass the argument msg via ds:bx
main_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