I finally did it...

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Schol-R-LEA

I finally did it...

Post by Schol-R-LEA »

(This has already been posted to Mega-Tokyo, and I know that these two groups overlap a lot, but I thought I'd mention it here for those who didn't see it there.)

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 [i]obvious[/i] 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. I'll post the existing code in a followup for anyone who wants it.
Schol-R-LEA

Code part 1: the boot loader

Post by Schol-R-LEA »

; 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
Schol-R-LEA

Code Part 2: second stage

Post by Schol-R-LEA »

;;;;;;;;;;;;;;;;;
;; 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
; '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
Schol-R-LEA

er, uh oh

Post by Schol-R-LEA »

Apparently, the board software doesn't like tabs. :-\ Still, it mostly readable without the formatting, so ir's not as bad as all that.
Chase

RE:I finally did it...

Post by Chase »

Congrats!

> 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'll usually do a HLT inside an infinite loop just on the theory that it keeps the cpu cooler. Probably wishful thinking but still....
Post Reply