Author Topic: 2 Stage Bootloader  (Read 66226 times)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #15 on: July 22, 2014, 09:05:35 PM »
Yeah, that's about right. "%include" essentially does a   "cut and paste" of the included file - right where the "%include" occurs - and Nasm assembles it as if it were all one file. You already use this. If we'd done "%include 'stage1.asm'" and "%include 'stage2.asm'" Nasm would have complained about duplicate symbols. (I didn't even try this - "experience" is the ability to recognize mistakes when you make 'em again). But "incbin" (equivalent to "%incbin" I think) pastes a file in "as is" without any attempt to assemble it. Since I had the two files already assembled, I figured it would work better here. (you can specify a starting point and how much to include, too - doesn't have to be the whole file)

What got me started thinking about it was the references in the VirtualBox forum about using "/dev/zero" to fill a file with zeros. Nasm will do that without "/dev/zero" - we already use it to pad the bootsector out to 510 bytes before the signature. (incidentally, that signature isn't "supposed" to be required for a floppy boot sector - only for a hard drive - but some "buggy" BIOSes look for it, so we have to put it in).

After reading a little more about VirtualBox, I didn't see where we tell it "boot from this". Glad to hear you got it to work!

If, by any chance, you have further problems, feel free to ask!

Best,
Frank


Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #16 on: July 23, 2014, 05:53:14 AM »
I don't know if its better to make another topic for this or not so I will be posting here I have gone through and created the first stage with a FAT32 file system but the thing is when it boots it does nothing except print the welcome message I have the full code right here:

Code: [Select]

BITS 16

ORG 0x0

%DEFINE RED 0x04
%DEFINE PURPLE 0x05
%DEFINE BLUE 0x03


Start:
jmp main
;BIOS Paramater Block for the File Allocation Table(F.A.T. For short)
OEM_ID                db "OS-Z"
BytesPerSector        dw 0x0200
SectorsPerCluster     db 0x08
ReservedSectors       dw 0x0020
TotalFATs             db 0x02
MaxRootEntries        dw 0x0000
NumberOfSectors       dw 0x0000
MediaDescriptor       db 0xF8
SectorsPerFAT         dw 0x0000
SectorsPerTrack       dw 0x003D
SectorsPerHead        dw 0x0002
HiddenSectors         dd 0x00000000
TotalSectors         dd 0x00FE3B1F
BigSectorsPerFAT      dd 0x00000778
FSVersion             dw 0x0000
RootDirectoryStart    dd 0x00000002
FSInfoSector          dw 0x0001
BackupBootSector      dw 0x0006
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TIMES 13 DB 0 ;jumping to next offset

DriveNumber           db 0x00
Signature             db 0x29
VolumeID              dd 0xFFFFFFFF
VolumeLabel           db "OS-Z BIN"
SystemID              db "FAT32   "

sPrint:                   ; Routine: output string in SI to screen


 .repeat:
  ;Paramaters for Input
    mov ah, 09h             ; Must be 9 to print color
  xor bh, bh ;Must be set to zero
    mov bl, BLUE ;The Color
    mov cx, 01h ;The counter I guess QQ
   
    lodsb                   ; Get character from string
    test al, al ;If end of string jump to done
    je .done                ; If char is zero, end of string
    int 10h                 ; Otherwise, print it
    mov ah, 02h
    mov bh, 00h
    inc dl    ;Cursor Position maybe?????
    int 10h ;takes ah and bh and acts on their values
    jmp .repeat

 .done:
    ret



     ;*************************************************************************
     ; PROCEDURE ClusterLBA
     ; convert FAT cluster into LBA addressing scheme
     ; FileStartSector = ((X ? 2) * SectorsPerCluster(0x08))
     ;*************************************************************************
Cluster_TO_LBA:
sub ax, 0x02
xor cx, cx
mov cl, BYTE[SectorsPerCluster]; move the Sectors per cluster into cl to multiply by
mul cx;convert to LBA
add ax, WORD[datasector];add the offset to the register ax
ret


;*************************************************************************
; PROCEDURE LBACHS
; convert ?ax? LBA addressing scheme to CHS addressing scheme
; absolute sector = (logical sector / sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)
;*************************************************************************

;ax == logical sector
LBA_TO_CHS:
xor cx, cx;clear out cx
xor dx, dx;clear out dx for arithmetic
mov cx, ax;mov the logical sector into cx

Absolute_Sector:
;calculate the absolute sector
div WORD[SectorsPerTrack]
inc ax
mov WORD[absoluteSector], ax

xor ax, ax
mov ax, cx
xor dx, dx

Absolute_Head:
;calculate the absolute Head
div WORD[SectorsPerTrack]
div WORD[SectorsPerHead]
mov WORD[absoluteHead], dx


xor ax, ax
xor bx, bx
xor dx, dx

Absolute_Track:
;calculate the absolute track
mov ax, WORD[SectorsPerTrack]
mov bx, WORD[SectorsPerHead]
mul bx

xor bx, bx
xor dx, dx
mov bx, ax

xor ax, ax
mov ax,cx

div bx
mov WORD[absoluteTrack], ax


ret

Read_Sector:
.MAIN:
     mov     di, 0x0005                          ; five retries for error
.SECTORLOOP:
push ax
push bx
push cx
call LBA_TO_CHS
mov ah, 0x02
mov al, 0x01
mov ch, BYTE[absoluteTrack]; Track = Cylinder
mov cl, BYTE[absoluteSector];load in sector
mov dh, BYTE[absoluteHead]; Load in the head
mov dl, BYTE[DriveNumber];load in the drive number
int 0x13 ; access the bios

jnc .SUCCESS ; check to see if there was an overflow(ERROR)

xor     ax, ax                              ; BIOS reset disk
    int     0x13                                ; invoke BIOS
    dec     di                                  ; decrement error counter
    pop     cx ;resets everything
    pop     bx
    pop     ax
    jnz     .SECTORLOOP                         ; attempt to read again
   
    int     0x18 ; Do not know what this does

    .SUCCESS:
   
    mov si, MSG_Progress
    call sPrint
    ;restore counter and other registers
    pop cx
    pop bx
    pop ax

    add     bx, WORD [BytesPerSector]           ; queue next buffer
    inc     ax                                      ; queue next sector

    test cx, cx
    jnz .MAIN; loops through 8 times


ret
main:
;----------------------------------------------------
; code located at 0000:7C00, adjust segment registers
;----------------------------------------------------
     
          cli           ; disable interrupts
          mov     ax, 0x07C0        ; setup registers to point to our segment
          mov     ds, ax
          mov     es, ax
          mov     fs, ax
          mov     gs, ax

     ;----------------------------------------------------
     ; create stack
     ;----------------------------------------------------
     
          mov     ax, 0x0000        ; set the stack
          mov     ss, ax
          mov     sp, 0xFFFF
          sti           ; restore interrupts
          mov si, MSG
  call sPrint


  ;Calculate The Start of the datasector
  mov al, BYTE[TotalFATs];moves the start of the Directory into ax
  mul WORD[BigSectorsPerFAT]
  add ax, WORD[ReservedSectors]
  mov WORD[datasector], ax;Start of datasector into datasector

  LOAD_ROOT:
  mov ax, WORD[RootDirectoryStart]
  call Cluster_TO_LBA

  mov bx, Sector_Size
  call Read_Sector;Read in the first sector


  mov di, Sector_Size + 0x20; adds the offset to the sector size to point the destination index register to the first File

  mov     dx, WORD [di + 0x001A]; point the dx register to where all the information in that file is stored
          mov     WORD [cluster], dx ;mov the information into cluster
          LOAD_STAGE2:
          ;Set up the segments where the Second Stage needs to be loaded

            mov ax, 0100h       ; set ES:BX = 0100:0000
            mov es, ax         
            mov bx, 0


            ;Read the cluster which contains the Second Stage
            mov cx, 0x0008
            mov ax, WORD[cluster]
            call Cluster_TO_LBA
            call Read_Sector   

            Stage2:;jumps to stage2 of the boot loader

            push    WORD 0x0100
          push    WORD 0x0000
          retf


            Failed:
            ;An error has occured if this part is executed
          mov     si, FAILURE
          call    sPrint
          mov     ah, 0x00
          int     0x16                                ; await keypress
          int     0x19                                ; warm boot computer
     







MSG: db "Welcome to OS-Z!",0
absoluteSector db 0x00
absoluteHead    db 0x00
absoluteTrack  db 0x00
cluster      dw 0x0000
datasector  dw 0x0000
Sector_Size dw 0x0200   
MSG_Progress: db ".", 0
FAILURE: db "The Bootloader has Failed to load...........", 0


TIMES 510 - ($-$$) DB 0

DW 0xAA55



Here is the stage 2 bootloader
Code: [Select]
BITS  16
ORG 0x0100


jmp main
%DEFINE RED 0x04
%DEFINE PURPLE 0x05
%DEFINE BLUE 0x03

sPrint:                   ; Routine: output string in SI to screen


 .repeat:
  ;Paramaters for Input
    mov ah, 09h             ; Must be 9 to print color
  xor bh, bh ;Must be set to zero
    mov bl, BLUE ;The Color
    mov cx, 01h ;The counter I guess QQ
   
    lodsb                   ; Get character from string
    test al, al ;If end of string jump to done
    je .done                ; If char is zero, end of string
    int 10h                 ; Otherwise, print it
    mov ah, 02h
    mov bh, 00h
    inc dl    ;Cursor Position maybe?????
    int 10h ;takes ah and bh and acts on their values
    jmp .repeat

 .done:
    ret


main:


cli ; clear interrupts
xor ax, ax ; null segments
mov ds, ax
mov es, ax
mov ax, 0x9000 ; stack begins at 0x9000-0xffff
mov ss, ax
mov sp, 0xFFFF
sti ; enable interrupts

;-------------------------------;
;   Print loading message ;
;-------------------------------;

mov si, LoadingMsg
call sPrint



LoadingMsg: db "Preparing to load Kernel.....", 0
And here is what I use to boot with both of the files
Code: [Select]
[BITS 16]


incbin "Stage1.bin"
incbin "Stage2.bin"

times (512 * 2880) - ($ - $$) db 0
I think the problem is when I start to load the root directory and read the first sector because its supposed to print the progress message if it does read it though there are no errors , I tried to do this without a tutorial and figure it out on my own and it has really helped me in understanding how to read the sector and converting to LBA and from LBA to CHS and using the segment offset model used in the 16 bit part of the bootloader , that being said there may be a bunch of errors but like you said " "experience" is the ability to recognize mistakes when you make 'em again ". And I have little to no experience with bootloaders but I have been at assembly for about a month now.
« Last Edit: July 23, 2014, 06:00:21 AM by Anonymous »
Thanks in advance, Anonymous

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #17 on: July 23, 2014, 08:19:49 AM »
Wow! Big step! This is going to take some studying, and I'll probably try it. Let me take a quick guess at what "might" be wrong. You seem to be loading your stage2 at segment 0x100 offset 0, and jumping to segment 0x100 offset 0, but your stage2 says "org 0x100". This will (I think) cause stage2 to fail to find its data. Changing that to "org 0" would be an easy thing to try, to see if it helps any...

Best,
Frank


Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #18 on: July 23, 2014, 06:23:49 PM »
I have changed the function LBA_TO_CHS because apparently I was doing it wrong still trying to figure out what I did wrong when converting but any way now that I fixed that (I think) it now gives me an INT18 BOOT FAILURE message after my welcome pops up.
Code: [Select]

LBA_TO_CHS:
   xor     dx, dx                              ; prepare dx:ax for operation
   div     WORD [SectorsPerTrack]              ; calculate
   inc     dl                                  ; adjust for sector 0
   mov     BYTE [absoluteSector], dl
   xor     dx, dx                              ; prepare dx:ax for operation
   div     WORD [SectorsPerHead]                     ; calculate
   mov     BYTE [absoluteHead], dl
   mov     BYTE [absoluteTrack], al
ret

« Last Edit: July 23, 2014, 06:27:04 PM by Anonymous »
Thanks in advance, Anonymous

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #19 on: July 24, 2014, 12:42:47 AM »
Hi Anonymous,

I didn't get far with this before realizing that your numbers for FAT32 obviously aren't going to work for my floppy drive. I'm not sure what it's doing for you, either. I can see you're reading the root directory into memory (where?), but from what? Do you have a FAT32 drive to read from?

Anyway, starting from "main:" (not that ".MAIN:", the other "main:"), 0xFFFF is a horrible alignment for your stack. Zero is a fine number for sp (if you want the whole segment) - it'll wrap around to 0xFFFE on the first push or call. Or start at 0xFFFE if that makes you nervous.

Then you print the message using a different sPrint. The int 10h/9 looks okay, but the int 10h/2 looks like it's setting the cursor to a random dx - starts off at zero (boot drive in dl... might be 80h?) so okay the first time. As I recall, to use int 10h/9, we need to "get cursor" first (int 10h/3?), then inc dl and do the int 10h/2. There's an int 10h/13h that will do colored text and advance the cursor (or not) - unusual parameters, the message goes in es:bp. It looks like you're "guessing" on these BIOS interrupts. Get RBIL (Ralf Brown's Interrupt List). The online versions are okay for interrupts, but if you download the whole thing you get ports.lst and memory.lst (plus other goodies) which will come in handy later. The int 18h loads ROM-BASIC, which isn't likely to be there unless you've got a genuine IBM... and probably not then, these days.

In any case, you return from sPrint with ah non-zero, then load al with [TotalFATs] and multiply by a word. This isn't going to give you a useful value. A little later, you do "mov bx, Sector_Size". This is the address of Sector_Size, not the [contents]. Is that what you intend? It seems to be the buffer into which Read_Sector reads sectors. The very first thing this is going to do is overwrite your "progress dot", so that may be why you don't see them. I get confused after that...

You may need to back up and take smaller steps, although I applaud your trying to figure out the LBA_TO_CHS thing - you'll probably need it eventually.

If I'm feeling ambitious I may try to rework your stage1 and see if I can get it to load stage2 from floppy again.  I never worked much with FAT32. As I recall, we know the (maximum) size of the root directory for FAT12 and FAT16, but for FAT32 we don't. This may not be a problem from a bootloader since all the memory is "yours" anyway. My brain is turning to mush. Thunderstorm coming and the lights are beginning to flicker.

Later,
Frank


Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #20 on: July 24, 2014, 01:09:42 AM »
I made changes to the print function so here it is and Now I actually get how to move the cursor around and which registers are the row column and page and I actually started over well not over I am re-using the old code because now that I look back it's a little messy here it is :
Code: [Select]
BITS   16

ORG  0x00





Start:


jmp main

;Colors for text
%DEFINE TEAL 0x03
%DEFINE RED 0x04
%DEFINE PURPLE 0x05

;macro for print
%macro Print 2

xor bx, bx
mov bl, %2
mov si, %1
call cPrint

mov ah, 0x02;moves to a new line in the middle of the screen
mov dh, 0x01
xor bh, bh
mov dl, 22
int 10h
%endmacro

;*************************************;
;                                     ;
;         OEM PARAMETER BLOCK         ;
;                                     ;
;                                     ;
;*************************************;

BPB_OEM      DB "Zeroth OS   " ;Name of the OS
BPB_BytesPerSector:    DW 512   ;Size of the Sector
BPB_SectorsPerCluster:    DB 1
BPB_ReservedSectors:    DW 1
BPB_NumberOfFATs:  DB 2
BPB_RootEntries:    DW 224
BPB_TotalSectors:  DW 2880
BPB_Media:      DB 0xf0  ;; 0xF1
BPB_SectorsPerFAT:    DW 9
BPB_SectorsPerTrack:    DW 18
BPB_HeadsPerCylinder:  DW 2
BPB_HiddenSectors:    DD 0
BPB_TotalSectorsBig:      DD 0
BS_DriveNumber:          DB 0
BS_Unused:      DB 0
BS_ExtBootSignature:    DB 0x29
BS_SerialNumber:          DD 0xa0a1a2a3
BS_VolumeLabel:          DB "Z-OS FLOPPY "
BS_FileSystem:            DB "FAT32   "



cPrint:                   ; Routine: output string in SI to screen


 .top:
  ;Paramaters for Input
    mov ah, 09h             ; Must be 9 to print color
    mov cx, 01h ;x position
    lodsb                   ; Get character from string
    test al, al
    je .done                ; If char is zero, end of string
    int 0x10                 ; Otherwise, print it

    mov ah, 0x02 ;set cursor position
    mov bh, 0x00 ;page
    inc dl    ;column
    int 0x10 ;changes the cursor position so the next char can be written at the new location
    jmp .top

 .done:
    ret


 clear:
    mov ah, 0x0F            ;get current video mode
    mov al, 0x00            ;reset register
    int 0x10                ;get video mode
    mov ah, 0x00            ;set video mode
    int 0x10                ;reset screen
    mov ah, 0x02            ;set cursor pos
    mov bh, 0x00            ;page 00
    mov dh, 0x00            ;row 00
    mov dl, 0x00            ;col. 00
    int 0x10                ;set pos
ret




main:

cli; disable interrupts
mov ax, 0x07c0 ;adjust the segment registers
mov ds, ax
mov gs, ax
mov fs, ax


Create_Stack:
xor ax, ax
mov es, ax
mov ss, ax
mov sp ,0x0FFFE
sti ; enable interrupts

call clear

;move the cursor to the middle of the screen
mov ah, 0x02
xor dh, dh
xor bh, bh
mov dl, 22
int 0x10

Print W_MSG, TEAL

LOAD_STAGE2:





W_MSG: db "Welcome to the Zeroth-OS!", 0
Progress_MSG: db ".",0
TIMES 510 - ($-$$) DB 0
DW 0xAA55

EDIT: I know this may be ambitious and maybe your right that I should be taking it slow or one thing at a time but I just realized that if I continue to create this bootloader then make it really complex I would never actually be able to test it on real hardware because ... well I don't have a floppy or anything that uses a floppy. So I wen't around tying to find even some code on how to write boot off of CD and I found some and it is in AT&T syntax T_T, now I have to go translate that, and I think that may change the way I approach the file system am I right?
« Last Edit: July 24, 2014, 07:57:21 AM by Anonymous »
Thanks in advance, Anonymous

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #21 on: July 24, 2014, 09:02:40 AM »
AT&T syntax shouldn't make much difference - looks wildly different, but should do the same thing. I think CDs have bigger sectors - 2k? - but there's a way to boot off CD "as if" it were a floppy. I don't know how to do it. I vaguely recall something about "El Torito spec"? Max mentions something about booting off a USB device to test his "painter" bootsector (in the Examples section). He might give us some clues if we asked him. That would have the advantage that it could be reused. http://www.osdev.org has a lot of info about this kind of thing. If you get advanced enough you could dedicate a hard drive to it...

Haven't tried your latest code yet...

Best,
Frank


Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #22 on: July 24, 2014, 11:29:14 AM »
AT&T code like this, you mean? No idea if it's right. It assembles.
Code: [Select]
;/* ISO-9660 boot sector
 ;* --------------------
 ;*
 ;* Copyright (c) 2008-2009 Frédéric Feret
 ;* All rights reserved.
 ;*
 ;* Features:
 ;*      - supports ISO-9660 filesystem ("El Torito")
 ;*      - supports multiples burning session
 ;*      - no floppy emulation
 ;*
 ;* Limitations:
 ;*      - supports only Joliet format
 ;*/
BITS  16
ORG 0x00

;/*
; * This is the main function of the boot sector, which loads the OS image
 ;* into memory at 1000:0000.
 ;*
 ;* Parameters:
 ;*      DL = boot disk number.
 ;*/
;MISMATCH: "        .global _start"
;_start:
        cli

 ;       /* The BIOS has loaded our boot sector at 0000:7C00. We move the code
  ;         of this boot sector at 9000:0000. */
        xor     ax,ax
        mov     ds,ax
        mov     si,07C00h                        ; /* DS:SI = 0000:7C00. */
        mov     ax,09000h
        mov     es,ax
xor     di,di                            ; /* ES:DI = 9000:0000. */
        mov     cx,00800h                        ; /* 2048 bytes. */
        rep movsb                                ; /* do the relocation. */

        ;/* Jump to the new address. */
;MISMATCH: "        ljmp    $0x9000, $1f"

jmp 0x9000:here

here:

 ;       /* Setup segment registers. */
        mov     ax,cs
        mov     ds,ax                                  ; /* DS = CS = 9000. */
        xor     ax,ax
        mov     es,ax
        mov     ss,ax                                   ;/* SS = ES = 0000. */
        mov     sp,07C00h                               ;/* SS:SP = 0000:7C00. */

       ; /* Save the boot disk number. */
        mov     [drive_number],dl

   ;     /* Clear the screen. */
        mov     ax,003h
        int     010h

        mov     si, msg_loading
        call    print_string

        sti

  ;      /* Check if the processor is a 386. */
        pushf
        pop     ax                                    ;  /* FLAGS -> AX */
        or      ax,03000h                              ; /* try to set IOPL bits. */
        push    ax
        popf                                            ;/* AX -> FLAGS */

        pushf
        pop     ax                                      ;/* FLAGS -> AX */
        test    ax,03000h                               ;/* check if IOPL bits are always set */
        jnz     short .l1                               ;/* it's a 386, or a higher model. */

        ;/* The processor is a 286 or even older model. We display an error message on the
         ;  screen and then prompts the user to press a key on the keyboard to restart
          ; the computer. */
        mov     si, msg_no_386
        call    print_string
        jmp     reboot

.l1:
        ;/* Get the size in bytes of a sector from the BIOS. Normally, the size of
         ;  a sector on a CD-ROM is 2048 bytes. */
        mov     word [disk_packet],01Ah
        mov     ah,048h
        xor     al,al
        mov     si, disk_packet
        mov     dl, [drive_number]
        int     013h
        jc      assume_2k_sector

        mov    ax, disk_packet + 0x18
        mov     [bytes_per_sect],ax
        jmp     .l1

assume_2k_sector:
        ;/* An error occured while retrieving the size of a sector. We will display
         ;  a warning message on the screen, then we assume that a sector on the
          ; disk makes 2 kilobytes. */
        mov     word [bytes_per_sect],00800h
        mov     si, msg_assume_2k_sector
        call    print_string

.l1:
        ;/* To load the OS image (which is located at the root directory of the disk),
         ;  we have to look at all sessions of the disk. We will retrieve information
          ; on the root directory (its size and location) of each session, load the
           ;directory into memory and see if the OS image filename is in this directory.
           ;If the file isn't found, we move on to the next session. If all sessions
           ;were covered and that the filename wasn't found, we conclude that the OS
           ;image isn't on the disk. The first volume descriptor is the 17th sector of
           ;the disk. */
        mov     eax,16

get_next_desc:
        ;/* Read the sector in memory at 0000:1000. */
        mov     [desc_sector],eax
        mov     bx,01000h
        mov     cx,1
        call    read_sectors

        ;/* Check for the signature "\2CD001" at the beginning of the descriptor. */
        mov     si, cd_signature
        mov     di,01000h
        mov     cx,6
        cmpsb
        je      found_desc

        ;/* Check if we have looked in all the descriptors of volume. */
        cmp     byte [es:0x1000],0FFh
        jne     next_desc

        ;/* We looked in all sessions of the disk, and the OS image wasn't found.
        ;   We display an error message on the screen and then prompts the user to
         ;  press a key to restart the computer. */
        mov     si, msg_file_not_found
        call    print_string
        jmp     reboot

next_desc:
        ;/* Compute the next sector number to load. */
        mov     eax, [desc_sector]
        inc     eax
        jmp     get_next_desc

found_desc:
        ;/* We have to load a volume descriptor of a session in memory. We will check
         ;  if the session supports the Joliet format for storing filenames and
          ; directories. Otherwise, the session is unknown. */
        mov     di,01058h
        mov     si, joliet_signature
        mov     cx,3
        cmpsb
        jne     next_desc

        ;/* We found a session that supports Joliet format. We can find the size and
         ;  the location of the root directory. */
        mov     eax, [es:0x109E]
        mov     [root_dir_start],eax
        mov     eax, [es:0x10A6]
        mov     [root_dir_size],eax

        ;/* Compute the number of sectors to load. */
        movzx   ebx,word [bytes_per_sect]
        div     ebx
        cmp     edx,0
        je      .l1
        inc     eax
.l1:
        mov     [root_dir_sectors],ax

        ;/* Read the root directory in memory at 0000:1000. */
        mov     eax, [root_dir_start]
        mov     bx,01000h
        mov     cx, [root_dir_sectors]
        call    read_sectors

        ;/* We will look into the root directory the OS image filename. If the file has
         ;  been found, we save the sector number where that file ans its size in bytes.
          ; Otherwise, we move to the next session. */
        mov     di,01000h

search_file:
        add     di,25

        ;/* Check if this entry refers to a file. */
        cmp     byte [es:+di],0
        jne     next_entry

        push    di
        add     di,8
        mov     si, osimage
        mov     cx,14
        cmpsb
        pop     di
        je      found_file                            ;  /* file found? */

next_entry:
        add     di,7
        movzx   ax,byte [es:+di]
        add     di,ax

.l1:
        inc     di
        cmp     byte [es:+di],0
        je      1b

        ;/* Check if we have check all the entries of the root directory. */
        mov     eax, [root_dir_size]
        add     eax,01000h
        cmp     di,ax
        jb      search_file

        ;/* The OS image wasn't found in the root directory. We go to the next
         ;  session of the disk. */
        jmp     next_desc

found_file:
        sub     di,25

        ;/* Get the location of this file. */
        mov     eax, [es:2+di]
        mov     [file_start],eax

        ;/* Get the size of this file. */
        mov     eax, [es:10+di]
        mov     [file_size],eax

        ;/* Compute the number of sectors to load. */
        movzx   ebx,word [bytes_per_sect]
        div     ebx
        cmp     edx,0
        je      .l1
        inc     eax
.l1:
        mov     [file_sectors],ax

        ;/* Read the OS image in memory at 1000:0000. */
        mov     eax, [file_start]
        mov     bx,01000h
        mov     es,bx
        xor     bx,bx
        mov     cx, [file_sectors]
        call    read_sectors

        mov     dl, [drive_number]
        xor     si,si

        ;/* Run the OS loader... */
        jmp    0x1000:0x0000

;/*
; * This function loads one or more sectors in memory.
; *
; * Parmaeters:
; *      EAX             first sector to load.
; *      CX              number of sectors to load.
; *      ES:BX           destination address.
; */
read_sectors:
;        /* To request the BIOS to load the sectors, we need to build a data
 ;          packet that contains the number or sectors to load, the first
  ;         logical sector to load and destination address. The address of
   ;        this packet will then be given to the BIOS, which loads these
    ;       sectors into memory. */
        mov     byte [disk_packet],010h

     ;   /* We don't read one single sector at a time. */
         mov [disk_packet + 2], word 0x01

       ; /* Write the destination address. */
         mov     [disk_packet + 4], bx
         mov     [disk_packet + 6], es

        ;/* Write the logical sector to load. */
        mov     [disk_packet + 8], eax
        mov     [disk_packet + 12], dword 0x0

read_one_sector:
        ;/* Read the sector into memory. */
        mov     ah,042h
        xor     al,al
        mov     si, disk_packet
        mov     dl, [drive_number]
        int     013h
        jnc     short .l1                              ; /* read error? */

        mov     si, msg_read_error
        call    print_string
        jmp     reboot

.l1:
      ;  /* Updates the next sector to load. */
       inc    dword [disk_packet + 8]

       ; /* Updates the destination address. Rather than incrementing the offset, we
        ;   will increase the segment. */
        mov     ax, [bytes_per_sect]
        shr     ax,4
        add [disk_packet + 6], ax
        loop    read_one_sector

        ret

;/*
 ;* This function displays a string to the screen, at the current cursor
 ;* position.
 ;*
 ;* Parameters:
 ;*      DS:SI           null-terminated string.
 ;*/
print_string:
  ;      /* Read a character. */
        repe lodsb

   ;     /* Check if we reached the end of the string. */
        cmp     al,0
        je      short .l1

    ;    /* Displays the character on screen. */
        mov     ah,00Eh
        mov     bx,007h
        int     010h

        jmp     print_string

.l1:
        ret

;/*
; * This function reboots the computer.
; */
reboot:
        mov     si, msg_reboot
        call    print_string

 ;       /* Wait for a key. */
        xor     ax,ax
        int     016h

  ;      /* Rebooting... */
        int     019h

;/*
; * Messages
; */
msg_loading:            db     `\n\rLoading...\n\n\r`, 0
msg_no_386:             db     `Error: 386 or higher processor required.\n\r`, 0
msg_assume_2k_sector:   db     `Error: failed to get sector size, assume 2 Kb.\n\r`, 0
msg_file_not_found:     db     `Error: OS image not found.\n\r`, 0
msg_read_error:         db     `Error: cannot read on disk.\n\r`, 0
msg_reboot:             db     `Press any key to restart your computer.\n\r`, 0

;/*
; * Datas
; */
cd_signature:           db    002h
                        db     'CD001'
joliet_signature:       db    025h,02Fh,045h
osimage:                db    0, 'o', 0, 's', 0, 'i', 0, 'm', 0, 'a', 0, 'g', 0, 'e'
drive_number:           db    0
bytes_per_sect:         dw    0
root_dir_size:          dd    0
root_dir_sectors:       dw    0
root_dir_start:         dd    0
file_size:              dd    0
file_sectors:           dw    0
file_start:             dd    0
desc_sector:            dd    0
disk_packet:            times   020h db 0

;/*
 ;* Boot sector signature
 ;*/
; ORG 2046
times 2046 - ($ - $$) db 0


dw    0AA55h

Best,
Frank



Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #23 on: July 24, 2014, 05:44:57 PM »
Thanks frank It does assemble but when I try to put it into virtual box it says format not recognized and gives me an error message I think its because of what the Developer stated at the top:
Code: [Select]
Limitations:
 ;*      - supports only Joliet format
I have been studying it and apparently I will have to Put The Descriptors for the Master Boot record into the first sector of the CD_ROM (I think) Each sector is still 512 bytes and the size of an ISO is 2 kilo-bytes or 2,048 bytes. This is as far As I have gotten Not much but I guess I have the structure right so far:
Code: [Select]
BITS   16

ORG  0x00

Start: jmp main


;Colors for text
%DEFINE TEAL 0x03
%DEFINE RED 0x04
%DEFINE PURPLE 0x05

;macro for print
%macro Print 2

xor bx, bx
mov bl, %2
mov si, %1
call cPrint

mov ah, 0x02;moves to a new line in the middle of the screen
mov dh, 0x01
xor bh, bh
mov dl, 22
int 10h
%endmacro


cPrint:                   ; Routine: output string in SI to screen


 .top:
  ;Paramaters for Input
    mov ah, 09h             ; Must be 9 to print color
    mov cx, 01h ;x position
    lodsb                   ; Get character from string
    test al, al
    je .done                ; If char is zero, end of string
    int 0x10                 ; Otherwise, print it

    mov ah, 0x02 ;set cursor position
    mov bh, 0x00 ;page
    inc dl    ;column
    int 0x10 ;changes the cursor position so the next char can be written at the new location
    jmp .top

 .done:
    ret

;clears the screen and sets the cursor position to the top left
 clear:
    mov ah, 0x0F            ;get current video mode
    mov al, 0x00            ;reset register
    int 0x10                ;get video mode
    mov ah, 0x00            ;set video mode
    int 0x10                ;reset screen
    mov ah, 0x02            ;set cursor pos
    mov bh, 0x00            ;page 00
    mov dh, 0x00            ;row 00
    mov dl, 0x00            ;col. 00
    int 0x10                ;set pos
ret




main:

cli; disable interrupts
mov ax, 0x07c0 ;adjust the segment registers
mov ds, ax
mov gs, ax
mov fs, ax


Create_Stack:
xor ax, ax
mov es, ax
mov ss, ax
mov sp ,0x0FFFE
sti ; enable interrupts

call clear

;move the cursor to the middle of the screen
mov ah, 0x02
xor dh, dh
xor bh, bh
mov dl, 22
int 0x10

Print W_MSG, TEAL;prints the welcome message in teal



INIT_MBR:













cd_signature: db "CD001"
cd_Version:   db 0x01



W_MSG: db "Welcome to the Zeroth-OS!", 0
Progress_MSG: db ".",0
times 2048 - ($ - $$) db 0; padd out the rest of the file to 0
DW 0xAA55


« Last Edit: July 24, 2014, 08:03:20 PM by Anonymous »
Thanks in advance, Anonymous

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #24 on: July 24, 2014, 09:13:12 PM »
Code: [Select]
Limitations:
 ;*      - supports only Joliet format
Quote
Tell me the trouble
Tell once to my ear
Turn, turn, turn again
Joliet prison
And ninety-nine years
Turn, turn to the rain
And the wind
- Bob Dylan

After "What's a Joliet format, mama?", my next question would have been "How do I write stage1.bin and stage2.bin to CD in the right places?". Will dd do it, if I can figure out "/dev/***", or do I have to make an iso and use "cdrecord"? There are answers to these questions, of course. I'd start looking around osdev.org and follow on from there. In your case, the VirtualBox docs may be more to the point. I thought the idea of a CD bootsector was to get away from VirtualBox. In my case, a USB device is probably what I want to boot from, failing a real floppy.

Once when my buddy was out of town, I grabbed what I thought was a "pen drive" off his desk. I could recognise the device, but couldn't read or write anything to it. Turned out what I had was a "card reader" - I didn't notice the little door on the side. I've never gotten around to trying an actual storage device. That's the extent of my experience with USB devices. I'm a floppy kinda guy. :)

Best,
Frank


Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #25 on: July 24, 2014, 09:43:14 PM »
Well It looks like I am so close to actually jumping to the next stage correctly
here is my Code:
Code: [Select]
BITS   16

ORG  0x00

Start: jmp main


;Colors for text
%DEFINE TEAL 0x03
%DEFINE RED 0x04
%DEFINE PURPLE 0x05
COL: db 0
ROW:  db 0

;macro for print
%macro Print 2
xor dx, dx
mov dh, BYTE[ROW];puts the row into the dh register
xor bx, bx
mov bl, %2
mov si, %1
call cPrint

             
    mov ah, 0x02            ;set cursor pos
    mov bh, 0x00            ;page 00
    inc dh            ;row 00
    mov dl, 0x00            ;col. 00   
int 0x10

mov BYTE[ROW], dh;saves the rows for the next time we need to print
%endmacro


cPrint:                   ; Routine: output string in SI to screen


 .top:
  ;Paramaters for Input
    mov ah, 09h             ; Must be 9 to print color
    mov cx, 0x01 ;x position
    lodsb                   ; Get character from string
    test al, al
    je .done                ; If char is zero, end of string
    int 0x10                 ; Otherwise, print it

    mov ah, 0x02 ;set cursor position
    mov bh, 0x00 ;page
    inc dl ;column
    int 0x10 ;changes the cursor position so the next char can be written at the new location
    jmp .top

 .done:
    ret

;clears the screen and sets the cursor position to the top left
 clear:
    mov ah, 0x0F            ;get current video mode
    mov al, 0x00            ;reset register
    int 0x10                ;get video mode
    mov ah, 0x00            ;set video mode
    int 0x10                ;reset screen
    mov ah, 0x02            ;set cursor pos
    mov bh, 0x00            ;page 00
    mov dh, 0x00            ;row 00
    mov dl, 0x00            ;col. 00
    int 0x10                ;set pos
ret




Read_Extended_Sector:
pusha
xor ax, ax
xor dx, dx
xor bx, bx
;read in the sector


.ForLoop:
MOV DL,BYTE[CDDriveNumber]              ; Set it up again
MOV AH,42h                          ; Read from drive function
MOV SI,DiskAddressPacket            ; Load SI with address of the Disk Address Packet
INT 13h   
jnc .Success


Print Read_Sector_Error_MSG, PURPLE
cli
hlt

.Success:
Print READ_SUCCESS, TEAL
cmp ah, Stage2
jz .DONE
Print FILE_NOT_FOUND, RED
cli
hlt
.DONE:
popa
ret


main:

cli; disable interrupts
mov ax, 0x07c0 ;adjust the segment registers
mov ds, ax
mov gs, ax
mov fs, ax


Create_Stack:
xor ax, ax
mov es, ax
mov ss, ax
mov sp ,0x0FFFE
sti ; enable interrupts

call clear


Print W_MSG, TEAL;prints the loading message in colour


call Read_Extended_Sector; Reads the first sector of the drive
;the read in data is stored in AH














cd_signature: db "CD001"
cd_Version:   db 0x01
CDDriveNumber: db 80h

;Disk Address Packet


DiskAddressPacket:          db 16,0
.SectorsToRead:             dw 1                              ; Number of sectors to read (read size of OS)
.Offset:                    dw 0                              ; Offset :0000
.Segment:                   dw 0200h                          ; Segment 0200
.End:                       dq 16                             ; Sector 16 or 10h on CD-ROM



W_MSG: db "Loading Z-Boot...........", 0
Stage2: db "STAGE2 BIN"
Read_Sector_Error_MSG: db "Failed to read sector ......",0
READ_SUCCESS: db "Reading the first sector was a success .......",0
FILE_NOT_FOUND: db "Error, File not found......."
times 2046 - ($ - $$) db 0; padd out the rest of the file to 0
DW 0xAA55


I am still unsure that this will work but I know I am going in the Right direction I started reading the documentation on int 0x13 here is the link :

http://en.wikipedia.org/wiki/INT_13H#INT_13h_AH.3D42h%3a_Extended_Read_Sectors_From_Drive
I now can assemble using mkisofs which copies it to a .iso file which I can probably burn to a disk if I wanted to now all i have to figure out is how to actually read the sector because the carry flag was set after I ran this so it printed the error message
EDIT:


just fixed it I realized that upon setting up the segments I have to save the CD_Drive number and put it correctly into the function here
Code: [Select]
    Create_Stack:
    xor ax, ax
    mov es, ax
    mov ss, ax
    mov sp ,0x0FFFE
    sti
        mov [CDDrivenumber], dl
« Last Edit: July 25, 2014, 04:43:55 AM by Anonymous »
Thanks in advance, Anonymous

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #26 on: July 25, 2014, 01:00:05 PM »
Does "byte value exceeds bounds" ring any bells? It isn't clear to me what you expect to be in ah in Read_Extended_Sector. You compare it with "Stage2" which is the address of a variable containing "STAGE2  BIN", which looks like how the name would be displayed in a (FAT) directory entry... but I think CD directory names are in UTF16. :(

In any case, you pusha/popa around Read_Extended_Sector, so ah is going to be whatever it was going into it - 2 from the print routine would be my guess... It is the carry-flag that will tell you if the interrupt suceeded. If it did, whatever you read from... sector 16(?)... should be at 200h:0. You might want to look for it there before blindly jumping.

I like your version better, though. It doesn't say "all rights reserved"... :)

Best,
Frank


Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #27 on: July 25, 2014, 03:34:16 PM »
I am trying now to find the Volume descriptor and it is proving to be a rial PITA it's supposed to be in the 16th sector but there are only 4 sectors on the CD so I am at a loss
here it is
Code: [Select]
BITS   16

ORG  0x00

Start: jmp main


;Colors for text
%DEFINE TEAL 0x03
%DEFINE RED 0x04
%DEFINE PURPLE 0x05
COL: db 0
ROW:  db 0

;macro for print
%macro Print 2
pusha
xor ax, ax
xor dx, dx
mov dh, BYTE[ROW];puts the row into the dh register
mov dl, BYTE[COL]
xor bx, bx
mov bl, %2
mov si, %1
call cPrint
mov BYTE[COL], dl
 ;saves the rows for the next time we need to print
popa
%endmacro

Print_ln:

pusha   
mov dh, BYTE[ROW]         
    mov ah, 0x02            ;set cursor pos
    mov bh, 0x00            ;page 00
    inc dh            ;row 00
    mov dl, 0x00            ;col. 00   
int 0x10
mov BYTE[ROW], dh
mov BYTE[COL], 0
popa


ret

cPrint:                   ; Routine: output string in SI to screen


 .top:
  ;Paramaters for Input
    mov ah, 09h             ; Must be 9 to print color
    mov cx, 0x01 ;x position
    lodsb                   ; Get character from string
    test al, al
    je .done                ; If char is zero, end of string
    int 0x10                 ; Otherwise, print it

    mov ah, 0x02 ;set cursor position
    mov bh, 0x00 ;page
    inc dl ;column
    int 0x10 ;changes the cursor position so the next char can be written at the new location
    jmp .top

 .done:
    ret

;clears the screen and sets the cursor position to the top left
 clear:
    mov ah, 0x0F            ;get current video mode
    mov al, 0x00            ;reset register
    int 0x10                ;get video mode
    mov ah, 0x00            ;set video mode
    int 0x10                ;reset screen
    mov ah, 0x02            ;set cursor pos
    mov bh, 0x00            ;page 00
    mov dh, 0x00            ;row 00
    mov dl, 0x00            ;col. 00
    int 0x10                ;set pos
ret




Read_Sectors: 
        ;/* Read the sector into memory. */
       
.ForLoop:
mov     ah,042h
xor     al,al
mov     si, DiskAddressPacket
mov     dl, [CDDriveNumber]
int     013h
        jnc    .Success ; /* read error? */

        Print Read_Sector_Error_MSG, RED

cli
hlt

.Success:
Print Progress_MSG , PURPLE
inc WORD[DiskAddressPacket.SectorsToRead]

        loop    .ForLoop

call Print_ln
ret


main:

cli
mov ax, 0x07c0 ;adjust the segment registers
mov ds, ax
mov gs, ax
mov fs, ax


Create_Stack:
xor ax, ax
mov es, ax
mov ss, ax
mov sp ,0x0FFFE
sti

mov     [CDDriveNumber],dl
call clear


Print W_MSG, TEAL;prints the loading message in colour
call Print_ln

LOAD_ROOT:
;first calculate the start of the Root directory
;mov ax, WORD[DiskAddressPacket.End]
mov WORD[DiskAddressPacket.SectorsToRead],16
;xor ax, ax
mov cx, 0x01
call Read_Sectors

Print READ_SUCCESS, TEAL
call Print_ln




 














Sector_Size: dw 512
CDDriveNumber: db 80h
CD_bytes_per_sect:         dw    0
CD_root_dir_size:          dd    0
CD_root_dir_sectors:       dw    0
CD_root_dir_start:         dd    0
CD_file_size:              dd    0
CD_file_sectors:           dw    0
CD_file_start:             dd    0
CD_desc_sector:            dd    0
CD_Signature:    db "CD001"
CD_FILE_VER:    db 0x01

;Disk Address Packet


DiskAddressPacket:          db 16,0
.SectorsToRead:             dw 1                              ; Number of sectors to read (read size of OS)
.Offset:                    dw 0                              ; Offset :0000
.Segment:                   dw 0200h                          ; Segment 0200
.End:                       dq 16                             ; Sector 16 or 10h on CD-ROM

RETURN: DB 0

W_MSG: db "Loading Z-Boot", 0
Stage2: db "STAGE2 BIN"
Read_Sector_Error_MSG: db "Error, failed to read sector",0
READ_SUCCESS: db "Sectors read correctly",0
Progress_MSG: db ".",0
FILE_NOT_FOUND: db "Error, file not found",0
FOUND: db "Found", 0
times 2046 - ($ - $$) db 0; padd out the rest of the file to 0
DW 0xAA55


Without the volume descriptor I cannot find the root directory which without it I can't really do anything any Ideas?
Also I am trying to register to OS Dev but it won't send me the activation email any idea on how to contact their admins?
« Last Edit: July 25, 2014, 04:13:05 PM by Anonymous »
Thanks in advance, Anonymous

Offline Anonymous

  • Jr. Member
  • *
  • Posts: 78
  • Country: us
Re: 2 Stage Bootloader
« Reply #28 on: July 25, 2014, 05:53:00 PM »
How would I read in whats at the address of 0x0200:0 to I just use a JMP instruction to jump there and read in whats at ES and SI? I think I am getting it I have put the segment and offset of the DAP into ES:SI and now I am trying to print it to see if its there but no luck nothing printed here is what I tried right under my read sector code
Code: [Select]
mov es, WORD[DiskAddressPacket.Segment]
mov si, WORD[DiskAddressPacket.Offset]

mov dl, BYTE[ES:SI]
mov ah, 0xE
int 0x010
« Last Edit: July 25, 2014, 06:25:44 PM by Anonymous »
Thanks in advance, Anonymous

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: 2 Stage Bootloader
« Reply #29 on: July 25, 2014, 06:37:46 PM »
al not dl, but otherwise looks about right.

Best,
Frank