Page 1 of 1

Boot sector / boot loader pair - addressing problem

Posted: Fri May 30, 2003 11:00 pm
by Ilya Melamed
Guys,

I'm trying to write a boot sector / boot loader pair (2 separate programs) in assembly (I am using NASM). Now the boot sector performs fine (however, it is not migrating itself at this stage), but the boot loader program has some problem accessing it's DATA.
The boot sector loads the boot loader right after itself, and then makes a JMP to 0x07E0:0h. The code of the boot loader EXECUTES, and local JMP instructions within it execute just FINE, which makes me think that CS is set correctly. What I am doing later is to set DS = CS and try to access some data I put inside the code - which fails completely. I get some arbitrary data from elsewhere which is totally unacceptible.

I'm including the source code for both the boot sector and the boot loader, please help.

P.S. I am a *novice* assembly programmer, so don't judge this code for not being optimized ;-)

------------------------------------------------------------------------
-- Make.bat ------------------------------------------------------------
------------------------------------------------------------------------

nasm -f bin boot_sector.asm -o boot_sector.bin
nasm -f bin boot_loader.asm -o boot_loader.bin

copy /B boot_sector.bin +boot_loader.bin diskette.img

------------------------------------------------------------------------
-- Boot sector ---------------------------------------------------------
------------------------------------------------------------------------

[BITS 16] ; 16-bit mode

;[ORG 0x7C00] ; The BIOS load the program at this address
[ORG 0x0000] ; But we trick it with the next instruction

JMP 0x07C0:start_of_boot_code ; Jump to the real BOOT code
; Following this line we will have some tables
; describing the disk (partition) structure

JMP start_of_boot_code

;-----------------------------------------------------------------------------------------------------------------------------
; BOOT Tables
;-----------------------------------------------------------------------------------------------------------------------------

disk_cylinders dd 1 ; Count
disk_heads dd 2 ; Count
disk_sectors dd 9 ; Count

loader_start_abs_sector dd 1 ; Zero-based

loader_number_of_sectors dd 1 ; Count

;-----------------------------------------------------------------------------------------------------------------------------
; BOOT CODE Starts here
;-----------------------------------------------------------------------------------------------------------------------------
start_of_boot_code: ; This is the start of the BOOT code

; The BIOS has set a stack up for us, so we can use it instead of defining
; our own.

PUSH CS ; Save CODE segment
POP DS ; Put it into DATA segment

put_string_on_screen: ; Say hello

mov ax, 1301h ; BIOS function number for write string
mov bx, 0007h ; BL contains attributes
mov cx, 7 ; String length
mov dx, 0000h ; YYXX - position on screen
push ds
pop es ; ES:BP is the string address
mov bp, hello_string
int 10h ; Issue the call

load_boot_loader_code_from_disk:

reset_disks:

xor ah, ah ; ah = 0
mov dl, 80h ; disk and diskette drives
int 13h

init_read:

;
; Used by BIOS function call:
;
; AH - 02h
; AL - number of sectors to read (must be nonzero)
; CH - low eight bits of cylinder number
; CL - sector number 1-63 (bits 0-5)
     ; high two bits of cylinder (bits 6-7, hard disk only)
; DH - head number
; DL - drive number (bit 7 set for hard disk)
; ES:BX - data buffer
;
; BIOS returns:
;
; CF - set on error
; AH - status (see #00234)
; AL - number of sectors transferred (only valid if CF set for some BIOSes)
;

mov word [loader_offset], location_for_boot_loader
read_single_sector:
call indicate_progress ; TODO: Remove or redefine this one

; --------------------------------------
; Compute C/H/S
; --------------------------------------

; 1 / 9 = 0
; 0 * 9 = 0
; 1 - 0 = 1
; ++1   = 2

; --- Compute (S)ector --------------------------------------------------------------------------
;
; [current_sector] = ([loader_start_abs_sector] % [disk_sectors]) + 1  ---- +1 is because sector number is not 0-based
;
mov eax, dword [loader_start_abs_sector]
mov ebx, eax

mov ecx, dword [disk_sectors]

mov edx, 0 ; integer div (4/3 = 1) (EAX = EDX:EAX / [disk_sectors])
div dword [disk_sectors]
mul dword [disk_sectors] ; EDX:EAX = EAX * [disk_sectors]

sub ebx, eax ; substract ebx from eax
inc ebx ; Sector number is 1-based
mov byte [current_sector], bl ; Save the result

; --- Compute (H)ead --------------------------------------------------------------------------
;
; [current_head] = (([loader_start_abs_sector] + 1 - [current_sector]) / [disk_sectors]) % [disk_heads] ---- +1 is because sector number is not 0-based
;
; Check:
;
; C/H/S = 10/2/9
; [loader_start_abs_sector] = 18
;
; [current_sector] = (18 % 9) + 1 = 1
;
; [current_head] = ((18 + 1 - 1) / 9) % 2 = 0
;
xor edx, edx
mov dl, byte [current_sector]

mov eax, dword [loader_start_abs_sector]
inc eax
sub eax, edx ; EAX = [loader_start_abs_sector] + 1 - [current_sector]

xor edx, edx
div dword [disk_sectors] ; EAX = ([loader_start_abs_sector] + 1 - [current_sector]) / [disk_sectors]

mov ebx, eax ; Save EAX
xor edx, edx
div dword [disk_heads]
mul dword [disk_heads]
sub ebx, eax ; EBX = modulus

mov byte [current_head], bl

; --- Compute (C)ylinder --------------------------------------------------------------------------
;
; [current_cylinder] = (([loader_start_abs_sector] + 1 - [current_sector]) / [disk_heads]) / [disk_sectors]
;
xor edx, edx
mov dl, byte [current_sector]

mov eax, dword [loader_start_abs_sector]
inc eax
sub eax, edx ; EAX = [loader_start_abs_sector] + 1 - [current_sector]

xor edx, edx
div dword [disk_heads]
xor edx, edx
div dword [disk_sectors]
mov dword [current_cylinder], eax

; --------------------------------------
; Setup registers for READ SECTOR call
; --------------------------------------
push CS
pop ES ; ES is now the same as CS

mov BX, location_for_boot_loader
add bx, word [loader_offset] ; Set up BX (ES:BX - data buffer)

mov edx, dword [current_cylinder]
shl dh, 6

mov ch, dl ; Cylinder number (low 8 bits)
mov cl, dh
or cl, byte [current_sector] ; 2 is 2nd sector

mov al, 1 ; Num sectors to read
mov dh, 0 ; head num
mov dl, 0 ; drive num (hard drive = bit7 on)
mov ah, 02h
int 13h
jc read_single_sector



add word [loader_offset], 512 ; Add 512 (the size of the sector) to [loader_offset] var

dec dword [loader_number_of_sectors] ; Decrease sector counter by 1
cmp dword [loader_number_of_sectors], 00000000h ; Compare to 0
jnz read_single_sector ; If non-zero - read one more

;;;; JMP location_for_boot_loader
;;;; JMP 0x07E0:location_for_boot_loader

JMP 0x07E0:0h

;;;; JMP 0x07C0:0200h
;;;; JMP 0x07E0:0000h
;;;; db 0eah
;;;; dw 0x0000,0x07E0

;;;; PUSH 0x07C0
;;;; PUSH 0x0000
;;;; db 0CBh ; RET FAR

;
; Our data here
;
current_drive db 0
current_cylinder dd 0
current_head dw 0
current_sector db 0

loader_offset dw 0 ; This is used by the read_single_sector: code
hello_string db 'Loading'


indicate_progress:
mov ah, 0Eh
mov al, '.'
mov bh, 0Fh
mov bl, 0
int 10h
ret

;-----------------------------------------------------------------------------------------------------------------------------
infinite_loop:
HLT
JMP infinite_loop ; Just hang the processor in an infinite loop
;-----------------------------------------------------------------------------------------------------------------------------

;-----------------------------------------------------------------------------------------------------------------------------
; This is necessary for the BIOS to recognize
; this code as the BOOT SECTOR
;
; For it to work correctly, the WORD at address 0x510
; must be set to 55AAh (magic number)
;-----------------------------------------------------------------------------------------------------------------------------
times 510-($-$$) db 0 ; This tells the assembler to fill up the space between the last instruction and 510h with 0s
; It is not a real processor instruction

dw 55AAh ; This is our "magic" number
;-----------------------------------------------------------------------------------------------------------------------------

SEGMENT BOOT_LOADER
location_for_boot_loader:
END

---------------------------------------------------------------------------
-- Boot loader ------------------------------------------------------------
---------------------------------------------------------------------------

[BITS 16]
;[ORG 0x7E00] ; The boot sector loads the code right after itself (0 + 512 = 0x0200)
[ORG 0x0000] ; The boot sector loads the code right after itself (0 + 512 = 0x0200)

;;;JMP 0x07E0:start_of_boot_loader

start_of_boot_loader:
mov ax, cs
mov ds, ax



mov ax, 0xb800
mov es, ax


;mov al, byte [hi_2_string]
mov al, 'T'
mov byte [es:0], al
jmp inf_loop

; The following code will dump the hex values of the nibbles of DX ***backwards***
; to the video memory
;
; DX holds the value to dump
;

;mov dx, ds
;mov dx, hi_2_string

;xor dx,dx
;mov dl, byte [ds:hi_2_string]

mov cx, 4 ; 4 nibbles
mov di, 0 ; ES:DI = 0xB800:DI

show_hex_dx:
mov bx, dx
and bx, 0x000F ; take the rightmost nibble

cmp bl, 0x0A
jl dec_char

hex_char:
sub bx, 0x0A
add bl, 'A'
jmp end_of_loop

dec_char:
add bl, '0'
jmp end_of_loop

end_of_loop:
mov [ES:DI], bl

shr dx, 4
add di, 2
dec cx
jnz show_hex_dx

;;;;;;;;;;;;;;;

inf_loop:
jmp inf_loop;

; -- DATA --

hi_2_string db 'QWERTY'

times 512-($-$$) db 0 ; This tells the assembler to fill up the space between the last instruction and 510h with 0s
; It is not a real processor instruction