Real Mode Simple OS difficulty

492 views Asked by At

I am writing a simple OS in real mode ASM for the fun of it. I have recently decided to move on to the filesystem. I assembled the code with

nasm -f bin -o boot.o boot.asm
nasm -f bin -o kernel.o kernel.asm
nasm -f bin -o fs.o fs.asm

dd if=boot.o bs=512 of=os.img
dd if=kernel.o bs=512 of=os.img seek=1
dd if=fs.o bs=512 of=os.img seek=2

In my bootloader I load the filesystem at the address 0x1000:0000, and my kernel at 0x2000:0000. Each are 512 bytes (so far) are fairly small. So, to test out my new filesystem, I wrote my kernel to print out the first letter of the name of the first file in the table. It puts the value 0x2000 into si, moves the byte at the address in si into al. It then passes 0x0e into ah and calls int 0x10. It then halts. However, when I boot the os into qemu, it simply shows me the bios information, says booting from floppy, then does nothing. No letter. Nothing. Here is the relevent code:

relevent bootloader code

    ;;the part that loads the file system.
    ;;The part for the kernel is identical, except that cl is 02 and bx is 0x2000
    mov ah, 02
    mov al, 01
    mov ch, 00
    mov cl, 03
    mov dh, 00
    mov dl, 00
    mov bx, 0x1000
    mov es, bx
    xor bx, bx
    int 0x13
    ;;jumps to address 0x2000 (where the kernel is)
    mov ax, 0x2000
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    xor ax, ax
    jmp 0x2000
    ;;halts
    hlt

relevent kernel code

    ;;gets address
    mov si, 0x1000
    ;;loads al
    mov al, [si]
    ;;prints
    mov ah, 0x0e
    int 0x10
    ;;halts
    hlt

relevent filesystem code

    ;;declares first file (it is hard coded for testing purposes)
    ;;format: [name], [sector number], [number of sectors to load]
    db 'file.bin', 4, 1

If I have done something wring in posting this, please forgive me, as this is my first post.

2

There are 2 answers

4
user35443 On

There are several problems in your code.

  1. You're loading your filesystem on address 0x1000:0000. Translated to linear address (which is the most important form) it is 0x10000, or simply 64KiB.
  2. You're loading your kernel on address 0x2000:0000 (0x20000, 128KiB). The jump you're doing

    jmp 0x2000
    

    is IP-relative jump, so you don't get to position you want. Execution of this jump is performed by adding immediate value passed as instruction parameter to value of IP register, which holds (in that time) address of next instruction (your hlt). What you need to do is far jump. It jumps to absolute address. Form of far jump is following:

    jmp segment:offset
    

    Replace the jump you have with this one:

    jmp 0x2000:0000
    
  3. Kernel code that should print character is using SI register uses segment-relative addressing, as you're executing your code in real mode. In difference to IP-relative jumps, you can't pass signed value as offset (although it would be cool and sometimes useful). Change value of DS segment, or use segment override to make everything faster. In your case, I would recommend using fs segment for all filesystem manipulations, so there would be no need for saving and restoring value of ds.

    mov ax, 0x1000
    mov fs, ax
    xor si, si
    mov al, byte [fs:si] ;or simply [fs:si] without byte
    

    Remember that ds is default segment for all addressing except BP usage. You should use it for main purposes of kernel (it will save some bytes and again, it will surely be faster). es, fs and gs are here for you for other memory operations. You can use es for processing destinations, fs for filesystem and gs for video memory pointing. Everything is up to you.

Note that some instructions (movs, cmps, scas...) have pre-assigned combinations of registers and segments (and sometimes only one of them can be overridden), so be wise when chossing them, and attentive when using them.

0
Frank Kotler On

You've loaded your fs to 1000h:0. Before jumping to your kernel, you load segregs with 2000h. So far, so good. But mov al, si gets 2000h:1000h. Try something like:

push ds ; save kernel ds
push 1000h
pop ds ; set ds to filesystem's segment
mov si, 0
mov al, [si]
pop ds ; get kernel's segment back
; now print it...

I think that should work - untested! Ah, the joys of segmented memory! Happy bootin'!