Page 1 of 1

My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 10:58 am
by streamholder
Hey everyone,
I'm very proud to announce the stage 1 bootloader for UselessOS, a (prospected) Useless OS for x86 processors that will be able to do pretty much nothing. :D

As you will see, it is comprised of very many lines of overly-commented NASM code.

Its functionality is pretty basic: it checks whether it is running from a floppy disk or a hard disk drive, and in the first case it uses CHS addressing to load 1 to 17 sectors of data, ensuring retro-compatibility with unextended INT 0x13.
In the second case, for now, it doesn't really do anything, as I've not implemented extended INT 0x13 loading yet. :lol:

Anyway, I'm posting it because I'd like you to completely destroy it. I know this is very basic stuff, I also know my code is not particularly good, and I'd really like to improve it.

Here is the code:

Code: Select all

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Stage 1 bootloader for UselessOS ;
; -------------------------------- ;
; Determines if it was loaded from ;
;  a floppy disk or an hard disk   ;
;  drive, and then loads stage 2   ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;
; Assembler directives ;
;;;;;;;;;;;;;;;;;;;;;;;;

; tells the assembler that the program will be loaded at 0x7C00
; this is done by the BIOS
org 0x7C00

; we are targeting (x86) 16-bit real mode
bits 16

;;;;;;;;;;;;;;;;;
; Jump to start ;
;;;;;;;;;;;;;;;;;
jmp start

;;;;;;;;
; Data ;
;;;;;;;;
; fdd geometry & options
fddsamt  db 8        ; how many sectors to load
fddretr  db 5        ; max retries for fdd operations
fddcretr db 0        ; current retries left

; misc strings
welcome1 db "Welcome to the UselessOS Stage 1 bootloader.", 13, 10, 0
disktype db "Drive type: ", 0
diskfdd  db "FDD", 13, 10, 0
diskhdd  db "HDD", 13, 10, 0
loaded   db "Data loaded!", 13, 10, 0

; errors
fdderes  db "FDD reset failed.", 13, 10, 0
fddeload db "FDD read failed.", 13, 10, 0

; storage
disknum  db 0

;;;;;;;;;;;
; Program ;
;;;;;;;;;;;

start:
	xor ax, ax             ; set up segment registers to segment 0 since
	mov ds, ax             ; our addresses are already relative to 0x7C00
	mov es, ax

	mov [disknum], dl      ; save disk number to memory

	mov ah, 0x01           ; set cursor shape
	mov cx, 0x0100         ; hide cursor by setting ch = 1 and cl = 0x00
	int 0x10               ; video interrupt

	mov ah, 0x08           ; read page number into bh
	int 0x10

	mov si, welcome1       ; print welcome
	call printstr

	mov si, disktype       ; print first part of disk type
	call printstr

	mov dl, [disknum]      ; restore disk number - should not be
	                       ; strictly necessary but you never know
	and dl, 0x80           ; sets zf if disk is floppy
	jz fddload

hddload:
	mov si, diskhdd        ; print disk type
	call printstr

	jmp halt               ; not implemented!

fddload:
	mov si, diskfdd        ; print disk type
	call printstr

fddload_onto_reset:
	mov ah, [fddretr]     ; load max retries in memory
	mov [fddcretr], ah
fddload_reset:
	mov si, fdderes        ; load error message pointer
	dec byte [fddcretr]    ; decrement the retries counter
	jz fddload_err         ; if it is 0, we stop trying

	mov ah, 0x00           ; otherwise, reset function (int 0x13)
	int 0x13
	jc fddload_reset       ; if jc (error), we try again

fddload_onto_load:
	mov ah, [fddretr]      ; reset retries counter
	mov [fddcretr], ah
	mov ax, 0x1000         ; need to stay within real mode limits
	mov es, ax
fddload_load:              ; loads 512*fddsamt bytes from sector 2 on.
	mov si, fddeload
	dec byte [fddcretr]
	jz fddload_err

	mov dh, 0              ; head 0
	mov ch, 0              ; cyl/track 0
	mov cl, 2              ; start sector
	mov bx, 0              ; memory location
	mov al, [fddsamt]      ; how many sectors to read
	mov ah, 0x02           ; read function (int 0x13)
	int 0x13
	jc fddload_load        ; if jc (error), we try again
	cmp al, [fddsamt]      ; also if al is not 1, we have a problem
	jnz fddload_load

fddload_done:
	mov si, loaded         ; we have successfully loaded the data
	call printstr
	jmp halt               ; this will be jmp 0x1000:0x0000

fddload_err:
	call printstr          ; print
	jmp halt               ; and die

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; printstr routine, prints the string pointed by si using int 0x10 ;
; sets the direction flag to 0                                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
printstr:
	cld                    ; clear df flag - lodsb increments si
printstr_loop:
	lodsb                  ; load next character into al, increment si
	or al, al              ; sets zf if al is 0x00
	jz printstr_end
	mov ah, 0x0E           ; teletype output (int 0x10)
	int 0x10               ; print character
	jmp printstr_loop
printstr_end:
	ret                    ; return to caller address


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; halt routine - infinite loop ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
halt:
	cli
	jmp halt

;;;;;;;;;;;
; Padding ;
;;;;;;;;;;;
; $ is the address of the current line, $$ is the base address for
; this program.
; the expression is expanded to 510 - ($ - 0x7C00), or
; 510 + 0x7C00 - $, which is, in other words, the number of bytes
; before the address 510 + 0x7C00 (= 0x7DFD), where the 0xAA55
; signature shall be put.
times 510 - ($ - $$) db 0x00

;;;;;;;;;;;;;;;;;;
; BIOS signature ;
;;;;;;;;;;;;;;;;;;
dw 0xAA55
Thank you!

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 11:50 am
by MichaelFarthing
If it works ok that's all you need!
My only comment is that you've believed your load happened because your routine says it has.
I suggest the next step is to put a string into the load part of your floppy and add a print of this string to your boot code (just to be sure it has arrived in tact in the right place).

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 11:53 am
by crunch
Your code works fine. I got it to load and execute code from sector 2. The only quibbles I had were some overly complicated floppy disk loading code.
An easy way to test this out is to just start writing your sector 2 code right after the boot signature

Code: Select all

times 510 - ($ - $$) db 0x00
;;;;;;;;;;;;;;;;;;
; BIOS signature ;
;;;;;;;;;;;;;;;;;;
dw 0xAA55

stage2
   mov si, msg
   call printstr
msg db 'Hello World', 10, 0
Then you can load sector two into memory at 0x7E00 (which is 512 bytes after 0x7C00), and immediately start executing code. You can call functions from stage1 in your stage2 code. That's how I set up my "Stage 1.5" loader. It gives you more room to set up protected mode, parse file systems, select video modes/memory map, etc.
Using the extended AH=42 function is very very easy as well.

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 11:56 am
by streamholder
MichaelFarthing wrote:My only comment is that you've believed your load happened because your routine says it has.
I suggest the next step is to put a string into the load part of your floppy and add a print of this string to your boot code (just to be sure it has arrived in tact in the right place).
You're absolutely right!
Thank you for your feedback.

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 12:07 pm
by Schol-R-LEA
My first piece of advice is, if you don't have an account with a VCS hosting service such as GitHub or Cloudforge yet, get one, and put your code there. Aside from all of the advantages this give regarding securing and versioning your code, it also makes sharing it a matter of simply giving us a link to it, which can be very useful when the code starts to get bigger.

Second, I would suggest keeping floppy and hard disk boot loaders separate, and handling selection of the appropriate one at installation time. It isn't as if the disk type is going to change, after all. If you write an installer script, you might even figure a way to have it auto-patch the drive information into a header at install time, eliminating several tests.

Third, I would like to ask: have you given any consideration to the file or document format yet? If you decide to use FAT-12 for floppies - which is pretty much a no-brainer unless you prefer SFS - then you will need to include a BIOS Parameter Block starting at byte 0x00B of the boot sector.

I'll take a closer look later, but that should be enough for you to think about for now. Oh, and read the wiki, too, if you haven't done so already.

Getting Started
How To Ask Questions
FAQ

Required Knowledge
Beginner Mistakes (the "deadlines" section in particular)
What order should I make things in
Code Management

How kernel, compiler, and C library work together
Using Programming Languages other than C

Real Mode, especially the section on memory addressing, and Segmentation
Memory Map, Detecting Memory and A20 Line
BIOS, and Boot Sequence
Interrupts
Bootloader and Rolling Your Own Bootloader
FAT and SFS

While this is a lot of reading, it simply reflects the due diligence that any OS-devver needs to go through in order to get anywhere. OS development, even as a simple project, is not amenable to the Stack Overflow cut-and-paste model of software development; you really need to understand a fair amount of the concepts and principles before writing any code, and the examples given in tutorials and forum posts generally are exactly that. Copying an existing code snippet without at least a basic idea of what it is doing simply won't do. While learning itself is an iterative process - you learn one thing, try it out, see what worked and what didn't, read some more, etc. - in this case a basic foundation is needed at the start. Without a solid understanding of at least some of the core ideas before starting, you simply can't get very far in OS dev.

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 12:53 pm
by streamholder
Schol-R-LEA wrote:My first piece of advice is, if you don't have an account with a VCS hosting service such as GitHub or Cloudforge yet, get one, and put your code there. Aside from all of the advantages this give regarding securing and versioning your code, it also makes sharing it a matter of simply giving us a link to it, which can be very useful when the code starts to get bigger.
I already have my own public Git repository system on one of my servers, I will surely upload the code there as soon as I have a working Makefile build system (which should be pretty soon! :D ).
Schol-R-LEA wrote:Second, I would suggest keeping floppy and hard disk boot loaders separate, and handling selection of the appropriate one at installation time. It isn't as if the disk type is going to change, after all. If you write an installer script, you might even figure a way to have it auto-patch the drive information into a header at install time, eliminating several tests.
Yeah, I think I'm definitely going for this. It doesn't make all that much sense to put all that effort in 512 bytes -- especially if there's no need for it. For now, I will keep everything floppy-only while leaving enough flexibility in the build system to allow to add new part and interchange them with a configuration file/script of some kind.
Schol-R-LEA wrote:Third, I would like to ask: have you given any consideration to the file or document format yet? If you decide to use FAT-12 for floppies - which is pretty much a no-brainer unless you prefer SFS - then you will need to include a BIOS Parameter Block starting at byte 0x00B of the boot sector.
I have, but I haven't concluded much just yet. While I can't really fit that much on a FDD, FAT12 really seems like a pretty lazy choice. Parsing FAT12, even in Assembly language, doesn't pose that much challenge, and kind of defies what I'm trying to achieve with this project. SFS, which I didn't know before (I wasn't around yet when the Amiga was discontinued :lol: ), definitely seems like a better choice on this matter.
Schol-R-LEA wrote:I'll take a closer look later, but that should be enough for you to think about for now. Oh, and read the wiki, too, if you haven't done so already.
Thank you. I've read many of the precious articles on the Wiki already, I will make sure to keep that up.
Schol-R-LEA wrote:While this is a lot of reading, it simply reflects the due diligence that any OS-devver needs to go through in order to get anywhere. OS development, even as a simple project, is not amenable to the Stack Overflow cut-and-paste model of software development; you really need to understand a fair amount of the concepts and principles before writing any code, and the examples given in tutorials and forum posts generally are exactly that. Copying an existing code snippet without at least a basic idea of what it is doing simply won't do. While learning itself is an iterative process - you learn one thing, try it out, see what worked and what didn't, read some more, etc. - in this case a basic foundation is needed at the start. Without a solid understanding of at least some of the core ideas before starting, you simply can't get very far in OS dev.
I understand that, and I'll proudly say that that's exactly what I'm here for.

Thank you so much for your help.

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 12:56 pm
by MichaelPetch
In addition to other comments, you should consider setting up your own SS:SP. You don't really know where the BIOS placed its own stack and if you end up reading data (like a disk sector) on top of the stack area the BIOS set then you can end up with a whole lot of hurt.

If you are intending to possibly boot on real hardware with a USB flash drive using USB-FDD mode you will probably want to start your code with a JMP and space for an OEM Name and minimum DOS 3.2 BIOS Parameter Block.

If targeting USB-HDD you'll want to set up a partition table with one active partition (even if you aren't using partitions or a file system). In this situation without a BPB it is also a good idea to start your bootloader with something like 'xor ax, ax'. Some BIOSes will attempt to detect USB-HDD bootability by looking for certain signature instructions at the beginning of the boot sector. Some BIOSes can can be very picky and failure to do this may result in the BIOS refusing to identify your bootsector as being bootable.

I have heard of some BIOSes that may actually require an identifiable volume boot record with USB-HDD, although I have never encountered it myself. It is possible you may need not only an active partition entry in the MBR, but something that appears to be a VBR (with a BPB) at the beginning of that active partition.

Re: My Useless NASM stage 1 bootloader

Posted: Sun Sep 04, 2016 1:48 pm
by Octocontrabass
MichaelPetch wrote:You'll also want to set up at least a partition table with one active partition (even if your aren't using partitions or a file system).
You're correct about everything except this. If you want to boot in floppy disk mode, you must not include a partition table. Conversely, if you want to boot in hard disk mode, you must include a valid partition table with one active partition, and you must not include a BPB.

Re: My Useless NASM stage 1 bootloader

Posted: Wed Sep 07, 2016 6:00 pm
by azblue
When you do implement LBA, what happens if you run on an old computer that doesn't support it?

I've recently starting rewriting my bootloader. My new code doesn't bother to check if it's a floppy or hard drive; instead it attempts to use LBA, and if it's not supported it reverts to CHS. That should work for floppies and for old BIOSes.

Re: My Useless NASM stage 1 bootloader

Posted: Wed Sep 07, 2016 7:57 pm
by Kazinsal
azblue wrote:When you do implement LBA, what happens if you run on an old computer that doesn't support it?

I've recently starting rewriting my bootloader. My new code doesn't bother to check if it's a floppy or hard drive; instead it attempts to use LBA, and if it's not supported it reverts to CHS. That should work for floppies and for old BIOSes.
Why support something that positively archaic anyways? 28-bit LBA has been around since 1994, and 48-bit since 2003.

Re: My Useless NASM stage 1 bootloader

Posted: Wed Sep 07, 2016 10:58 pm
by Brendan
Hi,
Kazinsal wrote:Why support something that positively archaic anyways? 28-bit LBA has been around since 1994, and 48-bit since 2003.
I'd assume that streamholder wanted to use the same boot loader for floppy disk (which still use CHS today, even for the "floppy emulation El Torito boot CD" case) and hard disk. Of course using the same boot loader for both floppy disk and hard disk is a mistake (already mentioned by MichaelPetch).


Cheers,

Brendan

Re: My Useless NASM stage 1 bootloader

Posted: Thu Sep 08, 2016 3:28 am
by MichaelFarthing
My comment would be that a bootloader is really something outside the operating system and to me it is simply an easy way of my getting my computer to load my code in the way I want it to. On that halcyon day when my operating system becomes sought after by users around the globe then I shall return to this relatively straightforward issue and give it a little attention - but, hey - I won't need to because those good old chappies at grub will probably do it for me! Worry about making the product better before getting concerned about the marketing.

Matthew 6:34

I suppose in these godless days* I should supply details:
"Sufficient unto the day in the evil thereof"

*For the avoidance of doubt I am among the godless.