flat assembler
Message board for the users of flat assembler.

Index > OS Construction > [FIXED] FASM/Bochs/INT 10h, AH=13h

Author
Thread Post new topic Reply to topic
RHaden



Joined: 22 Aug 2009
Posts: 6
RHaden
Greetings!

I'm yet another OS-development noob. Right now I'm just doing some prototyping with a custom bootloader. Unfortunately I seem to have gotten stuck on something which should be simple - displaying a string to the screen using the BIOS INT 10h interrupt, subfunction 13h. In desperation, I submit my humble code for you all to review.

Bootloader:

Code:
use16
org 7C00h

    jmp EntryPoint

    ; Store disk-drive parameters here!
    headCount        db 10h
    sectorsPerTrack  db 3Fh
    fatSectors       dw ?    ; Number of sectors in FAT goes here.

EntryPoint:
    ; Clear the screen!
    push dx
    mov ax, 0700h
    mov bh, 07h
    xor cx, cx
    mov dx, 184Fh
    int 10h

    ; Put cursor back at the top left corner!
    xor bx, bx
    xor dx, dx
    mov ah, 02h
    int 10h

    ; Display the 'Hello, World!' string.
    mov ax, 1301h
    mov bl, 07h
    mov bp, HelloWorld
    mov cx, 0Dh
    int 10h

    ; Move cursor to next line.
    xor bx, bx
    mov dh, 01h
    mov ax, 0200h
    int 10h

    ; Translate LBA to CHS.
    pop dx
    mov ax, [fatSectors]
    div [sectorsPerTrack]
    add ah, 02h              ; Assume boot sector is immediately before FAT.
    mov cl, ah               ; Starting sector.
    xor ah, ah
    div [headCount]          ; Get the starting head and cylinder.
    mov ch, al               ; Starting head.
    mov dh, ah               ; Starting cylinder.
    mov bx, 1000h            ; Segment value.
    mov es, bx               ; Can't load value into ES directly!
    xor bx, bx               ; Offset value.

ReadSector:
    mov di, 05h              ; Try loading five times.
    mov ax, 0201h            ; Read one sector from drive.
    int 13h
    jnc StartLoader          ; Start the loader if there's no error.
    xor ax, ax               ; Clear AX register to be able to reset drive.
    int 13h
    dec di
    jnz ReadSector           ; If counter > 0, try again.

StartLoader:
    push es
    push bx
    retf

    ; Data!
    HelloWorld db 'Hello, World!'

    ; Boot signature!
    db (1FEh - ($ - $$)) dup (0)
    dw 0AA55h
    


As you can see, I tested the INT 10h function in my bootloader, and it works fine. For some reason I can't get it to work in my "second-stage loader", which is below.

"Second-stage loader":

Code:
use16

EntryPoint:
    ; Display the 'Available memory: ' string.
    mov ax, 1301h
    mov bx, 0007h
    mov dx, 0100h
    mov cx, AvailableMemory
    mov bp, cx
    mov cx, 18
    int 10h

    ; Get available conventional memory.
    xor ax, ax               ; Is this really necessary? - It is now!
    int 12h                  ; BIOS call to get available conventional memory.
    mov dx, ax               ; Save result in DX - AX will be used for character display.
    mov ah, 0Eh              ; Prepare for INT 10h call.

    ; Display the first hex digit.
    mov ch, 0F0h             ; Used to get the 4 high bits of an 8-bit value.
    mov cl, dh               ; Display high value first.
    and cl, ch               ; Get the upper 4 bits!
    shr cl, 4                ; Put upper 4 bits into lower 4 bits.
    call DisplayHexValue

    ; Display the second hex digit.
    mov ch, 0Fh              ; Used to get the 4 low bits of an 8-bit value.
    mov cl, dh
    and cl, ch
    call DisplayHexValue

    ; Display the third hex digit.
    mov ch, 0F0h
    mov cl, dl
    and cl, ch
    shr cl, 4
    call DisplayHexValue

    ; Display the fourth hex digit.
    mov ch, 0Fh
    mov cl, dl
    and cl, ch
    call DisplayHexValue

KeyboardLoop:
    xor ax, ax
    int 16h
    mov ah, 0Eh
    int 10h
    jmp KeyboardLoop
    nop

DisplayHexValue:
    ; Assume 4-bit value to be displayed is in CL.
    cmp cl, 09h
    jg  .displayHexLetter    ; If the value is greater than 9, it's an ASCII letter.
    jmp .displayHexNumber    ; Otherwise, it's an ASCII number.
.displayHexNumber:
    add cl, 30h
    jmp .displayChar
.displayHexLetter:
    add cl, 37h
.displayChar:
    mov al, cl
    int 10h
    ret

    AvailableMemory db 'Available Memory: '
    


My inkling is that I'm doing something wrong with the ES:BP value in my second piece of code, but TBH I'm not sure what that something wrong is. If this issue has been addressed already, I do apologize - in that case, my Google- and forum-fu need some honing. Otherwise, thanks in advance for any feedback that anyone might have!


Last edited by RHaden on 25 Aug 2009, 01:25; edited 1 time in total
Post 22 Aug 2009, 01:11
View user's profile Send private message Reply with quote
windwakr



Joined: 30 Jun 2004
Posts: 827
Location: Michigan, USA
windwakr
EDIT: Does it do ANYTHING after the boot loader is done?


You don't need to hard-code hex stuff like the 1FEh in "db (1FEh - ($ - $$)) dup (0)", Just put 510, to make it easier to read for everyone.

BTW: WELCOME TO THE FORUM! It's nice to see new members here. Smile



EDIT: Ignore below, I wrote it without even looking at your code.

I didn't look through it, but are ES and DS set to you're second stage segment?

EDIT: Ya, doesn't look like you set DS up.
EDIT: WAIT A SECOND, ds isn't used for that interrupt, WHAT AM I THINKING?? Well, you should set it up anyways.

_________________
----> * <---- My star, won HERE
Post 22 Aug 2009, 01:15
View user's profile Send private message Reply with quote
RHaden



Joined: 22 Aug 2009
Posts: 6
RHaden
Thanks for the welcome!

windwakr wrote:
EDIT: Does it do ANYTHING after the boot loader is done?


Yes... the boot loader transfers control to the "second-stage loader", which is currently meant to find out how much free memory there is and write it out on the screen. Then it just enters an infinite loop where it echoes on the screen each key the user (i.e. me) presses.

Quote:
You don't need to hard-code hex stuff like the 1FEh in "db (1FEh - ($ - $$)) dup (0)", Just put 510, to make it easier to read for everyone.


Fair enough, I guess. Razz


Last edited by RHaden on 22 Aug 2009, 03:26; edited 1 time in total
Post 22 Aug 2009, 03:19
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17715
Location: In your JS exploiting you and your system
revolution
windwakr wrote:
You don't need to hard-code hex stuff like the 1FEh in "db (1FEh - ($ - $$)) dup (0)", Just put 510, to make it easier to read for everyone.
Eh, why is 510 any easier to read than 01feh? A sector is 0x200, so 0x1fe clearly shows a sector less 2 bytes.
Post 22 Aug 2009, 03:24
View user's profile Send private message Visit poster's website Reply with quote
windwakr



Joined: 30 Jun 2004
Posts: 827
Location: Michigan, USA
windwakr
Hmmm, I don't know. Try adding
Code:
mov ax,cs
mov es,ax
    

To the top of the entry point of the second stage.

Also, your ReadSector is flawed. di gets reset to 5 every loop, so if it can't read the floppy it would enter an endless loop and never bail out. Even if it did detect an error, it would still continue on to try to jump to a not loaded area.

I would write it as:
Code:
    mov di,5
ReadSector:
    mov ax, 0201h            ; Read one sector from drive.
    int 13h
    jnc StartLoader          ; Start the loader if there's no error.
    xor ax, ax               ; Clear AX register to be able to reset drive.
    int 13h
    dec di
    jnz ReadSector           ; If counter > 0, try again.
    jmp loaderror

StartLoader:           
    

Have a label called loaderror that displays an error message and halts or something.

_________________
----> * <---- My star, won HERE
Post 22 Aug 2009, 03:43
View user's profile Send private message Reply with quote
sinsi



Joined: 10 Aug 2007
Posts: 713
Location: Adelaide
sinsi
Code:
    mov ax, [fatSectors] 
    div [sectorsPerTrack] 
    

- fatSectors isn't initialised and could be anything
- you should set up a stack too
Post 22 Aug 2009, 03:53
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17715
Location: In your JS exploiting you and your system
revolution
sinsi wrote:
Code:
    mov ax, [fatSectors] 
    div [sectorsPerTrack] 
    

- fatSectors isn't initialised and could be anything
- you should set up a stack too
And what is dx at that point?
Post 22 Aug 2009, 03:56
View user's profile Send private message Visit poster's website Reply with quote
sinsi



Joined: 10 Aug 2007
Posts: 713
Location: Adelaide
sinsi
revolution: sectorsPerTrack is a byte.
Post 22 Aug 2009, 04:22
View user's profile Send private message Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17715
Location: In your JS exploiting you and your system
revolution
Oh, never mind.
Post 22 Aug 2009, 04:37
View user's profile Send private message Visit poster's website Reply with quote
sinsi



Joined: 10 Aug 2007
Posts: 713
Location: Adelaide
sinsi
'div bx' divides DX:AX by BX
but 'div bl' divides AX (AH:AL) by BL
Post 22 Aug 2009, 04:40
View user's profile Send private message Reply with quote
RHaden



Joined: 22 Aug 2009
Posts: 6
RHaden
windwakr wrote:
Hmmm, I don't know. Try adding
Code:
mov ax,cs
mov es,ax
    

To the top of the entry point of the second stage.


I tried that and the same thing happens. The string I want to display does not appear on the screen. What does appear is a string of 18 empty characters.

Quote:
Also, your ReadSector is flawed. di gets reset to 5 every loop, so if it can't read the floppy it would enter an endless loop and never bail out. Even if it did detect an error, it would still continue on to try to jump to a not loaded area.

I would write it as:
Code:
    mov di,5
ReadSector:
    mov ax, 0201h            ; Read one sector from drive.
    int 13h
    jnc StartLoader          ; Start the loader if there's no error.
    xor ax, ax               ; Clear AX register to be able to reset drive.
    int 13h
    dec di
    jnz ReadSector           ; If counter > 0, try again.
    jmp loaderror

StartLoader:           
    

Have a label called loaderror that displays an error message and halts or something.


Ah yes, you're right! Thanks for pointing that out to me. I've fixed the code accordingly.
Post 22 Aug 2009, 19:18
View user's profile Send private message Reply with quote
RHaden



Joined: 22 Aug 2009
Posts: 6
RHaden
sinsi wrote:
Code:
    mov ax, [fatSectors] 
    div [sectorsPerTrack] 
    

- fatSectors isn't initialised and could be anything


Based on looking at the code, you're right. I should've pointed out that I write the number of FAT sectors in that space when I run my disk-format program.

Quote:
- you should set up a stack too


Okay, why is that?
Post 22 Aug 2009, 19:21
View user's profile Send private message Reply with quote
RHaden



Joined: 22 Aug 2009
Posts: 6
RHaden
Okay, I did some more investigating tonight using Bochs' wonderful system-snapshot functionality and HEdit by Yuri Software. It turns out that my "second-stage loader" program was not being read into memory at 1000h:0000h (like I thought it was). How could this be? Well, it turns out that it wasn't be written on a disk-drive sector boundary, because I was not properly calculating the sector-size of the FAT in my disk-formatter program. I've since corrected that program and the "second-stage loader" now works like a charm!

So in a nutshell, this was yet another example of PEBKAC. Razz
Post 25 Aug 2009, 01:24
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2020, Tomasz Grysztar. Also on GitHub, YouTube, Twitter.

Website powered by rwasa.