0

I've created a string and turned it into an array. Looping through each index and moving to the al register so it can print out to the vga. The problem is, it prints the size of the string with no problem, but the characters in gibberish. Can you please help me figure out what the problem is in the code. It will be highly appreciated.

 org 0
 bits 16

 section .text
   global _start

  _start:

    
    mov si, msg
    loop:
    inc si
    mov ah, 0x0e
    mov al, [si]
    or al, al
    jz end
    mov bh, 0x00
    int 0x10
    jmp loop
    end:
    jmp .done

.done:
jmp $


msg  db  'Hello, world!',0xa
len  equ  $ - msg

TIMES 510 - ($ - $$) db 0
DW 0xAA55

bootloader code

ORG 0x7c00
BITS  16

boot:
mov ah, 0x02
mov al, 0x01
mov ch, 0x00
mov cl, 0x02
mov dh, 0x00
mov dl, 0x00
mov bx, 0x1000
mov es, bx
int 0x13
jmp 0x1000:0x00

times 510 - ($ - $$) db 0
dw 0xAA55
  • 1
    You didn't set DS to match your ORG setting. Google for `site:stackoverflow.com bootloader print gibberish` found multiple duplicates. – Peter Cordes Oct 05 '22 at 15:31
  • The code above is for the kernel. I have a boot loader already made, the kernel is being called by the boot loader at the address "org 0". – MastersProgression Oct 05 '22 at 19:15
  • 1
    The code in your question is for an MBR bootloader. It ends with `TIMES 510 - ($ - $$) db 0` / `DW 0xAA55` to make a 512-byte file with that 2-byte signature at the end. You could of course load it from another bootloader, but if DS:0 isn't `_start` at that point, `ds:si` won't be accessing the bytes of `msg`. (i.e. it's DS that matters, not CS.) But if you know CS is set correctly to match the ORG, and might *not* be `07C0h`, you can `mov ax, cs` / `mov ds, cs` or push/pop. In an actual bootloader you should use the numeric value. – Peter Cordes Oct 06 '22 at 01:02
  • You need to compensate for the pre-increment on SI (`inc si`) by writing `mov si, msg - 1`. Your current code would skip the 'H' character. And instead of relying on that vast block of zeroes, better zero-terminate the message individually: `msg db 'Hello, world!', 13, 10, 0`. Please note that newline for BIOS is the combo 13 and 10. – Sep Roland Oct 06 '22 at 16:33
  • "the kernel is being called by the boot loader at the address `org 0`". This is very confusing! You might consider posting your bootloader so we can actually see what you mean by this. That is, if you want us to figure out what the problem is in the code... ps. downvotes can always get reverted once the question becomes a good one. – Sep Roland Oct 06 '22 at 16:41
  • 1
    Posted the boot loader above – MastersProgression Oct 07 '22 at 19:32
  • @PeterCordes It's a pity you had to close this question as a duplicate. Now that they have added their bootloader code, more issues arise. Perhaps other things than the linked posts deal with... and more than the OP can chew? – Sep Roland Oct 08 '22 at 10:18
  • @SepRoland: When I closed it, there was every reason to believe it was just a normal MBR bootloader, since it has an MBR signature. That was before the first comment saying it was a "kernel". And the solution is still to set segment registers properly for wherever they loaded it. Until they posted a [mcve] of what they were actually doing, leaving the question closed was the right choice. (Turns out they never set DS anywhere :/) Now that they have, the question could maybe be reopened if there isn't another duplicate about setting `org` and `ds` for static storage in a "kernel". – Peter Cordes Oct 08 '22 at 13:09
  • @PeterCordes The missing setup for DS in their kernel is certainly part of the problem, but my answer would have been about how they don't actually jump to the kernel and yet get extremely lucky that the kernel's code does get executed in the end. – Sep Roland Oct 08 '22 at 13:46
  • @SepRoland: Oh, wow, yeah they load it to `0x1000:0x1000`, then jump 0x1000 bytes earlier and presumably execute a boatload of `00 00 add [bx+si], al` instructions until it reaches CS:IP=0x1000:0x1000. (Or if there are any non-zero bytes in there, e.g. if this was stack memory, they aren't jumps out of the region). Yeah, we can reopen this if you want to write an answer. Seems to me just another variation on segmentation basics, and failure to use a debugger to see what's going on, and we have existing Q&As with correct examples of loading and jumping to a bootloader. – Peter Cordes Oct 08 '22 at 13:53
  • @SepRoland: e.g. [Boot loader doesn't jump to kernel code](https://stackoverflow.com/q/32701854) has some useful stuff. I didn't immediately find a duplicate about having DS set up correctly for static data in the "kernel", other than [Why doesn't lodsb work after bootloading into the kernel?](https://stackoverflow.com/q/69068198) where the self-answer is very incomplete, still a lot of happens-to-work. Let me know if you want to reopen and write an answer; I notice there are no reopen votes on the question. – Peter Cordes Oct 08 '22 at 13:59
  • @PeterCordes It is not that I am desperate to answer this question, but I do feel a bit responsible after having encouraged the OP to include their bootloader so we could figure out what the problem is in the code. Then again most everything that is wrong in the code has been pointed out in these comments. I expressly did not vote to reopen because I seem to remember that it could complicate removing the close vote as the community might not agree with it. (3 reopen votes are needed, isn't it) – Sep Roland Oct 08 '22 at 14:25
  • @SepRoland: If you want a question reopened, vote to reopen. It takes 3 total votes, or 1 from a gold badge holder. So yeah, I guess I'll just reopen if if the only reason you didn't vote to reopen was that it wouldn't instantly work, and pinged me in a comment instead. – Peter Cordes Oct 08 '22 at 14:27
  • @PeterCordes That's indeed the only reasson. Could you please retract the close vote? – Sep Roland Oct 08 '22 at 14:36
  • 1
    @SepRoland: I voted to reopen moments after my previous comment. The question's been open for 10 minutes. (You don't "retract" a vote after it's already triggered the thing. I had to cast a new reopen vote, but since I have a gold tag badge, only one vote was needed, like for closing.) – Peter Cordes Oct 08 '22 at 14:40

1 Answers1

3

The bootloader

Before tackling the kernel code, let's look at the bootloader that brings the kernel in memory.
You have written a very minimalistic version of a bootloader, one that omits much of the usual stuff like setting up segment registers, but thanks to its reduced nature that's not really a problem.

What could be a problem is that you wrote mov dl, 0x00, hardcoding a zero to select the first floppy as your bootdisk. No problem if this is indeed the case, but it would be much better to just use whatever value the BIOS preloaded the DL register with. That's the ID for the disk that holds your bootloader and kernel.

What is a problem is that you load the kernel to the segmented address 0x1000:0x1000 and then later jump to the segmented address 0x1000:0x0000 which is 4096 bytes short of the kernel. You got lucky that the kernel code did run in the end, thanks to the memory between these two addresses most probably being filled with zero-bytes that (two by two) translate into the instruction add [bx+si], al. Because you omitted setting up the DS segment register, we don't know what unlucky byte got overwritten so many times. Let's hope it was not an important byte...

mov bx, 0x1000
mov es, bx
xor bx, bx          <== You forgot to write this instruction!
int 0x13
jmp 0x1000:0x0000

What is a problem is that you ignore the possibility of encountering troubles when loading a sector from the disk. At the very least you should inspect the carry flag that the BIOS.ReadSector function 02h reports and if the flag is set you could abort cleanly. A more sophisticated approach would also retry a limited number of times, say 3 times.

ORG   0x7C00
BITS  16

; IN (dl)
mov dh, 0x00     ; DL is bootdrive
mov cx, 0x0002
mov bx, 0x1000
mov es, bx
xor bx, bx
mov ax, 0x0201   ; BIOS.ReadSector
int 0x13         ; -> AH CF
jc  ERR
jmp 0x1000:0x0000

ERR:
cli
hlt
jmp ERR

times 510 - ($ - $$) db 0
dw 0xAA55

The kernel

After the jmp 0x1000:0x0000 instruction has brought you to the first instruction of your kernel, the CS code segment register holds the value 0x1000. None of the other segment registers did change, and since you did not setup any of them in the bootloader, we still don't know what any of them contain. However in order to retrieve the bytes from the message at msg with the mov al, [si] instruction, we need a correct value for the DS data segment register. In accordance with the ORG 0 directive, the correct value is the one we already have in CS. Just two 1-byte instructions are needed: push cs pop ds.

There's more to be said about the kernel code:

  • The printing loop uses a pre-increment on the pointer in the SI register. Because of this the first character of the string will not get displayed. You could compensate for this via mov si, msg - 1.
  • The printing loop processes a zero-terminating string. You don't need to prepare that len equate. What you do need is an explicit zero byte that terminates the string. You should not rely on that large number of zero bytes thattimes produced. In some future version of the code there might be no zero byte at all!
  • You (think you) have included a newline (0xa) in the string. For the BIOS.Teletype function 0Eh, this is merely a linefeed that moves down on the screen. To obtain a newline, you need to include both carriage return (13) and linefeed (10).
  • There's no reason for your kernel code to have the bootsector signature bytes at offset 510. Depending on how you get this code to the disk, it might be necessary to pad the code up to (a multiple of) 512, so keep times 512 - ($ - $$) db 0.

The kernel:

ORG   0
BITS  16

section .text
    global _start

_start:
    push cs
    pop  ds
    mov  si, msg
    mov  bx, 0x0007    ; DisplayPage=0, GraphicsColor=7 (White)
    jmp  BeginLoop
PrintLoop:
    mov  ah, 0x0E      ; BIOS.Teletype
    int  0x10
BeginLoop:
    mov  al, [si]
    inc  si
    test al, al
    jnz  PrintLoop


    cli
    hlt
    jmp  $-2

msg db   'Hello, world!', 13, 10, 0

TIMES 512 - ($ - $$) db 0
Sep Roland
  • 33,889
  • 7
  • 43
  • 76