2

I have looked into making a small OS for the sake of learning, and am on the bootloader right now. I want to be able to use int 0x13 to read sectors from a floppy drive, put them into memory, then jump to that code. Here is what I have so far:

org 0x7c00
bits 16

main:
    call setup_segments

    mov ah, 2      ; function
    mov al, 1      ; num of sectors
    mov ch, 1      ; cylinder
    mov cl, 2      ; sector
    mov dh, 0      ; head
    mov dl, 0      ; drive
    mov bx, 0x1000 ;
    mov es, bx     ; dest (segment)
    mov bx, 0      ; dest (offset)
    int 0x13       ; BIOS Drive Interrupt

    jmp 0x1000:0   ; jump to loaded code

times 510 - ($-$$) db 0 ; fluff up program to 510 B
dw 0xAA55               ; boot loader signature




LoadTarget: ; Print Message, Get Key Press, Reboot

jmp new_main

Greeting: db "Hello, welcome to the bestest bootloader there ever was!", 0
Prompt:   db "Press any key to reboot...", 0

Println:
    lodsb ; al <-- [ds:si], si++

    or al, al    ; needed for jump ?
    jz PrintNwl  ; if null is found print '\r\n'
    mov ah, 0x0e ; function
    mov bh, 0    ; page number ?
    mov bl, 7    ; text attribute ?
    int 0x10     ; BIOS Interrupt
    jmp Println

PrintNwl: ; print \r\n
    ; print \r
    mov ah, 0x0e ; function
    mov al, 13   ; char (carriage return)
    mov bh, 0    ; page number ?
    mov bl, 7    ; text attribute ?
    int 0x10

    ; print \n
    mov ah, 0x0e ; function
    mov al, 20   ; char (line feed)
    mov bh, 0    ; page number ?
    mov bl, 7    ; text attribute ?
    int 0x10

    ret          ; return

GetKeyPress:
    mov si, Prompt ; load prompt
    call Println   ; print prompt

    xor ah, ah     ; clear ah
    int 0x16       ; BIOS Keyboard Service

    ret            ; return

setup_segments:
    cli ;Clear interrupts
    ;Setup stack segments
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    sti ;Enable interrupts

    ret

new_main:
    call setup_segments

    mov si, Greeting ; load greeting
    call Println     ; print greeting

    call GetKeyPress ; wait for key press

    jmp 0xffff:0     ; jump to reboot address

times 1024 - ($-$$) db 0 ; fluff up sector

I want to load the sector after LoadTarget into the address 0x1000:0, then jump to it. So far I just get a blank screen. I feel like the bug is somewhere between main and the line times 510 - ($-$$) db 0. Maybe I'm just not getting the values of the register right? Please help! Thanks

Jester
  • 56,577
  • 4
  • 81
  • 125
Jerfov2
  • 5,264
  • 5
  • 30
  • 52
  • 1
    Since `setup_segments` is already the second sector, how do you expect it to work if it's not even loaded yet? Also, setting `ss` without `sp` is very bad practice, you have no way of knowing where your stack is going to be. – Jester Jul 10 '15 at 15:33
  • @Jester oh yeah didn't think about that ;). Also where should I set `sp` to? – Jerfov2 Jul 10 '15 at 16:46
  • http://stackoverflow.com/questions/2065370/how-to-load-second-stage-boot-loader-from-first-stage || http://stackoverflow.com/questions/19381434/cannot-read-disk-sectors-in-assembly-language – Ciro Santilli OurBigBook.com Oct 03 '15 at 21:46

2 Answers2

5

You should substitute the first call setup_segments with the actual instructions that do the job. Also as Jester pointed out always update the SP register when changing the SS register.

Currently you are reading from cylinder 1. It should be cylinder 0.

The code for linefeed is 10 (not 20 as you wrote).

Both BIOS calls in PrintNwl don't need the BL register since both CR and LF are non-displayable ascii's.

Fifoernik
  • 9,779
  • 1
  • 21
  • 27
  • 2
    Wow I can't believe I messed up the '\n' character! I knew it was 10, I don't know why I put 20. I'll fix the other bugs too, thanks! – Jerfov2 Jul 10 '15 at 16:43
  • Thanks for helping, but it still shows a blank screen. I am using a floppy disk on Virtualbox. Is this any different from a real floppy, usage wise? – Jerfov2 Jul 10 '15 at 16:51
  • Sorry, never had the pleasure of working with Virtualbox. – Fifoernik Jul 10 '15 at 16:53
  • Thats ok, my program sorta works now, it reboots, but doesn't display anything. I think it may have something to do with `sp`. I don't know what I should set it to. Should I just copy `ss` into `sp`? – Jerfov2 Jul 10 '15 at 16:56
  • @TheTromboneWilly have you properly set the origin with `ORG`? Should n't it be `0` for the second stage? –  Jul 10 '15 at 17:18
  • @knm241 If I try to set the `org` again it gives me `error: program origin redefined` – Jerfov2 Jul 10 '15 at 17:32
  • Nasm allows only one `org` per file. One way to deal with that is `section kernel vstart=0` (any arbitrary name - doesn't have to be "kernel") - would have the effect of `org 0`. Another way would be to assemble the file separately, and `%incbin "kernel.bin"` after your boot sig. Another way would be to assemble the two files separately and combine them with `cat` or `copy /b`. Could write them separately to floppy or fake floppy, but probably easier to combine them and write once. osdev.org is your friend. – Frank Kotler Jul 10 '15 at 18:03
  • @FrankKotler Could I also use a segmented address for `vstart` like `section LoadTarget vstart=0x1000:0`? – Jerfov2 Jul 10 '15 at 19:37
  • @FrankKotler nvm i fixed it – Jerfov2 Jul 10 '15 at 21:23
  • Glad you fixed it. No, you can't use a segmented address for `vstart`. I'm tempted to say "of course". Back in the "good old days" we had no choice but to learn segmented memory model... and DOS. You young guys need to learn it for bootsectors and the transition to pmode - but it isn't good for much else. That's life. – Frank Kotler Jul 10 '15 at 23:48
3

The fact that you are using ORG 0x7C00 implies that you expect the CS:IP pair or registers to hold 0000h:7C00h. Remember that BIOS has already put the first 512 bytes of your 1024 bytes long program at linear address 00007C00h.

Setting up the other segment registers is just a matter of copying CS to DS, ES, and SS. But very importantly every time you change SS you must also change SP to maintain a coherent SS:SP pair of registers. In your program a convenient place for the stack would be below the program and so I setup the SP register with 7C00h. You cannot fiddle with SS:SP in a subroutine (like you did) because the RET at the end would not know where to return to!

When you load the 2nd sector from the floppy it has to come from cylinder 0. In the CHS representation Cylinder and Head start at 0 and Sector starts at 1.
It would have been nice if you checked the CF for a successfull operation.

Jumping to the loaded code cannot be done through jmp 0x1000:0 because the code that sits there by now is part of a program that was compiled with the ORG 0x7C00 directive. You need to compensate for this!

  1. Compensate for the ORG 0x7C00 directive. Subtract 07C0h from the segment part, add 7C00h to the offset part. => jmp 0840h:7C00h
  2. Compensate for the fact that your LoadTarget label is at offset 512 within the program. Subtract 0020h from the segment part, add 0200h to the offset part. => jmp 0820h:7E00h

Here's what the initial code could look like

 org 7C00h
 bits 16
main:
 mov ax,cs
 mov ds,ax  <-- Not necessary at this stage in this program
 mov es,ax  <-- (keep them should you display a message on error)
 cli
 mov ss,ax
 mov sp,7C00h
 sti
 mov ax,0201h
 mov cx,0002h
 mov dx,0000h
 mov bx,1000h
 mov es,bx
 mov bx,0000h
 int 13h
 jc  Error
 jmp 0820h:7E00h
Error:
 hlt

Once the second stage is running you only need to setup the DS, and ES registers. CS:IP was setup via the JMP and SS:SP just continues.

 ...
new_main:
 push cs
 pop  ds
 push cs
 pop  es
 mov  si,Greeting
 call Println
 ...
Sep Roland
  • 33,889
  • 7
  • 43
  • 76