Over this past weekend, I finally decided to bite the bullet and get some coding done; to wit, a boot loader of the sort that seems to be giving so many others so much trouble. It seemed rather hypocritical, after all, to be giving advice on something I had not succeeded in myself.
Well, it took me four days (not counting the previous ten years of casual study on the subject) of cursing, sweat, frustration, more cursing, pawing through reference works, googling through the online material, still more cursing when I found the obvious bug that had stalled me, awkward learning experiences, moments of enlightenment followed by dazed disillusionment, puzzled looks at some of the odder qualities of the Wintel platform, but I finally got the damn thing to work as I designed it. What bothers me is that there are still some places where I am not entirely sure why what I have down now works when the same thing didn't elsewhere.
BTW, my development environment was NASM-Edit under Win98, while my 'test machine' was Bochs 1.3 on the same platform (I have not tested this on a real machine yet). I used a 1.44M empty file to simulate the disk, and partcopy to load the two files into the disk file's 'sectors'.
I had some very weird experiences trying to get the ES:BX settings right when calling the INT13 AL2 service. For some reason, I couldn't get BX to load anything that wasn't a multiple of 0x100! Even stranger, the problem simply vanished when I rewrote the code. Anyone have any insight into that probem?
Even though I've 'solved' it, it still bugs me, as I don't know what was really happening.
I also found out why it is a bad idea to use the HLT instruction instead of a final infinite loop: in order for it to actually halt the processor, you have to shut off interrupts, which in turn means that you cannot reset the system, but have to cycle power to get it going again.
I've still got to go back and comment it better, and add some small bit of documentation so I don't loss what I've gained at after all this work. But for now, I'm happy I'm done. In the next message I'll post the code in it's current form for anyone wants it.
I can't believe I finally did it...
code part 1: boot loader
The first file is the boot loader, which reads the second sector of the disk into ES:BX, transfers control to the second stage, which prints a notice of success and returns control to the boot loader, which shuts down. There is some redundancy between the two, and the code is written for clarity, not optimal performance.
----------------------------------------------------------------
; BootLoader - simple test boot loader for PCs
; The boot loader performs the following functions:
;???- sets the segments for use in the boot process.
;???- loads and verifies the boot image, then transfers control to it.
; v 0.01 Joseph Osako 3 June 2002
stage2_entry???equ 0x1000???; the segment:offset to load the second stage into
stage2_offset???equ 0x0000???
stack_top???equ 0x9000
VBIOS???equ 0x10???; BIOS interrupt vector for video services
ttype???equ 0x0E???; insert character in AL as if screen were teletype
EOL???equ 0x00???;end of string marker
CR???equ 0x0D
LF???equ 0x0A
DBIOS???equ 0x13???; BIOS interrupt vector for disk services
disk_reset???equ 0x00???; disk reset service
disk_read???equ 0x02???; disk read service
cyl???equ 0x00???; cylinder to read from
head???equ 0x00???; head to read from
startsector???equ 0x02???; sector to start reading at
numsectors???equ 0x01???; number of sectors to read
%define zero(x) xor x, x
%macro write 1
???mov si, %1
???call printstr
%endmacro
[bits 16]
[org 0x7C00]
segment .text
;***************************************************
; start
; This is the real begining of the code. The first order of
; business is clearing the interrupts, then setting the
; segment registers and the stack pointer.
start:
???cli
???mov ax, stack_top
???mov ss, ax??????; and the stack at an arbitrarily high point past ES.
???zero (ax)
???mov sp, ax ??????; put the stack pointer to the top of SS
???mov ax, cs
???mov ds, ax??????; set DS == CS
???sti??????; reset ints so BIOS calls can be used
???write testnumber???
???mov [bootdrv], dl???; save boot drive info for later use
???write reset
; reset disk to read from
reset_disk:
???mov dl, [bootdrv]
???zero (ah)
???mov al, disk_reset
???int DBIOS
???jnc read_disk
???write no_reset
???call shutdown
; read in the data from disk and load it to ES:BX (already initalized)
read_disk:
???write loading
mov ax, stage2_entry
mov es, ax
???mov dl, [bootdrv]
???mov ch, cyl??????; cylinder
???mov dh, head??????; head
???mov cl, startsector???; first sector
???mov al, numsectors???; number of sectors to load???
???mov ah, disk_read
mov bx, stage2_offset
???int DBIOS
???jnc done_reading
???write disk_fail
???call shutdown
done_reading:
???write done
; set up fake return frame for code returning from second stage
mov ax, cs
push ax
mov ax, reenter
push ax
; fake a jump to the second stage entry point
mov ax, stage2_entry
mov es, ax
mov bx, stage2_offset
???push es
push bx
???write snd_stage
retf
reenter:
mov ax, cs
mov ds, ax
write returned
shutdown:
???write exit
???jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Auxilliary functions
;; printstr - prints the string point to by SI
printstr:
???push ax
???mov ah, ttype ???; set function to 'teletype mode'
.loop:???
???mov al, [si]???; update byte to print
???cmp al, EOL???; test that it isn't EOL
???jz .endstr
???int VBIOS???; put character in AL at next cursor position
???inc si
???jmp short .loop
.endstr:
???pop ax
???ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; data
testnumber???db 'Test #126', CR, LF, EOL
reset???db 'Resetting disk drive.', CR, LF, EOL
loading???db 'Loading stage two... ', EOL
done???db 'done.', CR, LF, EOL
snd_stage???db 'Second stage loaded, proceeding to switch context.', CR, LF, EOL
returned db 'Control returned to first stage, ', EOL
no_reset???db 'Could not reset drive,', EOL
disk_fail???db 'could not read second stage, ', EOL
exit???db 'system halted.', EOL
bootdrv???resb 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pad out to 510, and then add the last two bytes needed for a boot disk
space???times (0x0200 - 2) - ($-$$) db 0
bootsig???dw 0xAA55
----------------------------------------------------------------
; BootLoader - simple test boot loader for PCs
; The boot loader performs the following functions:
;???- sets the segments for use in the boot process.
;???- loads and verifies the boot image, then transfers control to it.
; v 0.01 Joseph Osako 3 June 2002
stage2_entry???equ 0x1000???; the segment:offset to load the second stage into
stage2_offset???equ 0x0000???
stack_top???equ 0x9000
VBIOS???equ 0x10???; BIOS interrupt vector for video services
ttype???equ 0x0E???; insert character in AL as if screen were teletype
EOL???equ 0x00???;end of string marker
CR???equ 0x0D
LF???equ 0x0A
DBIOS???equ 0x13???; BIOS interrupt vector for disk services
disk_reset???equ 0x00???; disk reset service
disk_read???equ 0x02???; disk read service
cyl???equ 0x00???; cylinder to read from
head???equ 0x00???; head to read from
startsector???equ 0x02???; sector to start reading at
numsectors???equ 0x01???; number of sectors to read
%define zero(x) xor x, x
%macro write 1
???mov si, %1
???call printstr
%endmacro
[bits 16]
[org 0x7C00]
segment .text
;***************************************************
; start
; This is the real begining of the code. The first order of
; business is clearing the interrupts, then setting the
; segment registers and the stack pointer.
start:
???cli
???mov ax, stack_top
???mov ss, ax??????; and the stack at an arbitrarily high point past ES.
???zero (ax)
???mov sp, ax ??????; put the stack pointer to the top of SS
???mov ax, cs
???mov ds, ax??????; set DS == CS
???sti??????; reset ints so BIOS calls can be used
???write testnumber???
???mov [bootdrv], dl???; save boot drive info for later use
???write reset
; reset disk to read from
reset_disk:
???mov dl, [bootdrv]
???zero (ah)
???mov al, disk_reset
???int DBIOS
???jnc read_disk
???write no_reset
???call shutdown
; read in the data from disk and load it to ES:BX (already initalized)
read_disk:
???write loading
mov ax, stage2_entry
mov es, ax
???mov dl, [bootdrv]
???mov ch, cyl??????; cylinder
???mov dh, head??????; head
???mov cl, startsector???; first sector
???mov al, numsectors???; number of sectors to load???
???mov ah, disk_read
mov bx, stage2_offset
???int DBIOS
???jnc done_reading
???write disk_fail
???call shutdown
done_reading:
???write done
; set up fake return frame for code returning from second stage
mov ax, cs
push ax
mov ax, reenter
push ax
; fake a jump to the second stage entry point
mov ax, stage2_entry
mov es, ax
mov bx, stage2_offset
???push es
push bx
???write snd_stage
retf
reenter:
mov ax, cs
mov ds, ax
write returned
shutdown:
???write exit
???jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Auxilliary functions
;; printstr - prints the string point to by SI
printstr:
???push ax
???mov ah, ttype ???; set function to 'teletype mode'
.loop:???
???mov al, [si]???; update byte to print
???cmp al, EOL???; test that it isn't EOL
???jz .endstr
???int VBIOS???; put character in AL at next cursor position
???inc si
???jmp short .loop
.endstr:
???pop ax
???ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; data
testnumber???db 'Test #126', CR, LF, EOL
reset???db 'Resetting disk drive.', CR, LF, EOL
loading???db 'Loading stage two... ', EOL
done???db 'done.', CR, LF, EOL
snd_stage???db 'Second stage loaded, proceeding to switch context.', CR, LF, EOL
returned db 'Control returned to first stage, ', EOL
no_reset???db 'Could not reset drive,', EOL
disk_fail???db 'could not read second stage, ', EOL
exit???db 'system halted.', EOL
bootdrv???resb 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pad out to 510, and then add the last two bytes needed for a boot disk
space???times (0x0200 - 2) - ($-$$) db 0
bootsig???dw 0xAA55
code part 2: second stage
This second file simply prints a message and returns to the
boot loader.
--------------------------------------------------------------
;;;;;;;;;;;;;;;;;
;; testsecond.asm - test simple second stage boot loader
;;
;; v 0.01 Joseph Osako 3 June 2002
VBIOS???equ 0x10???; BIOS interrupt vector for video services
ttype???equ 0x0E???; insert character in AL as if screen were teletype
EOL???equ 0???;end of string marker
CR???equ 0x0D
LF???equ 0x0A
%macro write 1
???mov si, %1
???call printstr
%endmacro
entry:
???mov ax, cs
???mov ds, ax
???write success
; jmp $
; 'return' to the first stage via a faked call frame set up earlier
retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Auxilliary functions
;; printstr - prints the string point to by SI
printstr:
???push ax
???mov ah, ttype ???; set function to 'teletype mode'
.loop:???
???mov al, [si]???; update byte to print
???cmp al, EOL???; test that it isn't EOL
???jz .endstr
???int VBIOS???; put character in AL at next cursor position
???inc si
???jmp short .loop
.endstr:
???pop ax
???ret
;;;;;;;;;;;;;;;;;;;;;;;;;
;; data
success???db 'Control successfully transferred to second stage.', CR, LF, EOL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pad out to 512
space???times 0x0200 - ($-$$) db 0
boot loader.
--------------------------------------------------------------
;;;;;;;;;;;;;;;;;
;; testsecond.asm - test simple second stage boot loader
;;
;; v 0.01 Joseph Osako 3 June 2002
VBIOS???equ 0x10???; BIOS interrupt vector for video services
ttype???equ 0x0E???; insert character in AL as if screen were teletype
EOL???equ 0???;end of string marker
CR???equ 0x0D
LF???equ 0x0A
%macro write 1
???mov si, %1
???call printstr
%endmacro
entry:
???mov ax, cs
???mov ds, ax
???write success
; jmp $
; 'return' to the first stage via a faked call frame set up earlier
retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Auxilliary functions
;; printstr - prints the string point to by SI
printstr:
???push ax
???mov ah, ttype ???; set function to 'teletype mode'
.loop:???
???mov al, [si]???; update byte to print
???cmp al, EOL???; test that it isn't EOL
???jz .endstr
???int VBIOS???; put character in AL at next cursor position
???inc si
???jmp short .loop
.endstr:
???pop ax
???ret
;;;;;;;;;;;;;;;;;;;;;;;;;
;; data
success???db 'Control successfully transferred to second stage.', CR, LF, EOL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; pad out to 512
space???times 0x0200 - ($-$$) db 0
Re:I can't believe I finally did it...
congratulations on your victory.
I assembled this on my machine and booted it from a floppy. NASM squawked about 'uninitialized data in text segment', but otherwise it ran beautifully.
Unless you will be linking, the segment directive is unnecessary.
welcome, brother!
I assembled this on my machine and booted it from a floppy. NASM squawked about 'uninitialized data in text segment', but otherwise it ran beautifully.
Unless you will be linking, the segment directive is unnecessary.
welcome, brother!
Re:I can't believe I finally did it...
Congrats Schol-R-LEA on starting the coding of your OS(starting to code is always the hardest thing to do it seems).
K.J.
K.J.