2

I have a problem when running an operating system boot loader on VMWare and Bochs.

The boot loader should be displaying a string via int 10h, but it just prints the background color without any character.

The color was set by the value of bx. The code is here:

    org 7c00h
;   org 0100h
    mov ax, cs
    mov ds, ax
    mov es, ax
    call  DispStr
    jmp $

DispStr:
    mov ax, BootMessage
    mov bp, ax
    mov cx, 13
    mov ax, 1301h
    mov bx, 005eh
    mov dh, 3
    mov dl, 0
    int 10h
    ret

BootMessage:        db "Hello, Luser!"
times 510-($-$$)    db 0

dw  0xaa55
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Sherlock
  • 21
  • 1
  • 1
    Try harder formatting your question, please. – Ondrej Tucny May 18 '14 at 15:53
  • For calling a subroutine the return address will be push on the stack and for a stack we need a little peace of ram where the stackpointer(SP) and the stacksegmentregister(SS) is pointing to. – Dirk Wolfgang Glomp May 18 '14 at 17:48
  • to Ondrej Tucny and Dirk Wolfgang Glomp : I am so sorry that I didn't express my question clearly. My question is : when the boot loader ran, the screen just printed the background color without any character.But what I expect is both the string and the background color are printed. What's wrong? Thank you very much!! – Sherlock May 19 '14 at 05:08
  • Please take also a look at this page:[link]http://stackoverflow.com/questions/23716647/why-isnt-the-text-colored-when-using-the-0eh-10h-interrupt[/link]. Maybe it help a little bit. – Dirk Wolfgang Glomp May 20 '14 at 08:17
  • Weird BIOS? It works for me under DosBox. Does it work if you `org 000h`, or set ES to 0? http://stackoverflow.com/questions/6644881/simple-nasm-boot-program-not-accessing-memory-correctly – ninjalj Jun 04 '14 at 21:21
  • @ninjalj : doubtful this problem is a buggy bios, just that the code makes false assumptions about what _CS_ contains. I posted an answer discussing the issue. – Michael Petch Sep 24 '15 at 22:55
  • @MichaelPetch: well, for true compatibility with the original IBM PC, a BIOS should jump to 0000:7c00, not to 07c0:0000. – ninjalj Sep 25 '15 at 00:42
  • Those two memory locations are exactly the same.The original BIOS developers (I wrote boot loaders in the 80s on real hardware) never said that a boot loader should expect a certain segment. It was known that we should expect nothing except the contents of `DL` and that our code would start execution at physical address 0x7C00. Properly designed bootloaders for decades don't make segment assumption.Some Award BIOSes in the early nineties started jumping to 0X07C0:0000 as well. Nothing prevented this. Anyone who assumes the value in any segment register is valid is writing the bootloader wrong. – Michael Petch Sep 25 '15 at 00:52
  • Side note about history of 0x7C00 is that when the IBM BIOS was developed that it had a requirement that an IBM PC BIOS based system would have a minimum of 32k RAM, so they chose the area just below 32K to load the bootloader and that is the way it is today. That has nothing to do with the question at hand, but just for informational purposes. – Michael Petch Sep 25 '15 at 01:02
  • 1
    If you do a disassembly an MS-DOS 1983 floppy you'll observe this behavior. Interrupts turned off, SS:SP set to 0000:7C00, DS=ES=0 (these were explicitly set). Nothing was assumed.The boot loader would move itself to 0000:0600. Rather than doing a near jump (which would have be in range since it is addressable in the current segment) to continue, the bootloader issued a FAR JMP to specifically set _CS_ to zero by jumping to 0000:0600+offset to continue. – Michael Petch Sep 25 '15 at 01:30
  • @MichaelPetch: At least some vendors documented their BIOS as jumping to 0000:7c00, e.g: http://www.seasip.info/AmstradXT/1512tech/section2.html#2.3.16 But of course you're right regarding boot sectors, you shouldn't assume particular values of CS:IP (_be liberal in what you accept_). Conversely, when writing BIOSes or chain-loaders, it would be best to jump to 0000:7c00 (_be conservative in what you send_). – ninjalj Sep 25 '15 at 18:01
  • @ninjalj An interesting foot note to all this is that the only release of IBM-DOS that got this wrong was PC-DOS 1.x. When IBM basically rewrote 2.0 and released it in 1983 they specifically modified their boot loader to not assume anything about how the BIOS got to physical address 0x00007C00. The first time I was made aware of this was either in a Byte or InfoWorld article, but on Compuserve there was a discussion about this very thing. My opinion by the time 1983 rolled around anyone who was doing bootloader work should have been aware of it with some research. – Michael Petch Sep 25 '15 at 18:08
  • 1
    One big problem is that people rely on the internet where such issues get lost or become vague, but if you lived at that time working with real hw you were more likely to be aware of it. There is another interesting thing as well.There was a bug in old 8088 chips that didn't turn off interrupts when you modified SS (if previously enabled it was suppose to enable them after the following instruction) Back then it was recommended that you specifically use CLI/STI around SS/SP updates. That bug has been largely lost but if you ever run on antiquated h/w you could in rare cases be bit by that bug. – Michael Petch Sep 25 '15 at 18:19

2 Answers2

2

Although this answer is late, the question is simple and the code is a good example of a developer assuming that the BIOS jumped to their code with valid or expected values. I have recently answered a number of questions on bootloaders and have begun providing general tips. Tip #1 should apply here:

  1. When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts.

Since you are making BIOS int calls you should ensure you set up your own stack. You can't guarantee SS/SP point to enough stack space or is even valid when the BIOS jumps to your code at physical memory address 0x00007C00 . The only requirement is the BIOS starts executing your code at that location, but it doesn't say what CS and IP should be used to do it. The BIOS will also pass the boot drive in DL.

A single physical memory location can be referenced by many different segment:offset pairs. For example these all point to the boot loader physical address:

0x0000:0x7C00 = (0x0000<<4)+0x7C00 = 0x00007C00
0x0203:0x5BD0 = (0x0203<<4)+0x5BD0 = 0x00007C00
0x07C0:0x0000 = (0x07C0<<4)+0x0000 = 0x00007C00 

As you can see the above examples give you an idea that there is more than one way to address a single memory location with segment:offset pairs. The code the poster has presented assumes the CS segment is going to be 0. One can observe the org 7c00h in the code, which says that all the absolute near memory references will be offset by 0x7C00 when the compiler generates an object file. Since we will be physically loaded at 0x7C00 in memory, our DS has to be 0x000.

How does this relate to the OP's code. Observe that we start with:

mov ax, cs
mov ds, ax
mov es, ax

We blindly move what is in CS and transfer it to DS and ES . Our code specifically needs a DS of 0x0000 to work properly (with origin 0x7C00). I gave some examples of some CS:IP pairs that map to physical location 0x7C00. If the BIOS jumped to our code specifically with a far jump to 0x0000:0x7C00, our CS would be 0x0000 and IP would be 0x7C00. If we moved 0x0000 to DS and ES everything would be fine. What happens though if the BIOS far jumped to 0x07C0:0x0000 ? Our CS would have 0x07C0 in it and IP would be 0x0000. If we move CS to ES and DS all these segments are 0x07C0 but we wrote our code relative to a segment of 0x0000.

With the wrong segment what can go wrong? Assume that the Bootmessage was placed at offset 0x7C50 (0x50 + 0x7C00 origin point) by the assembler. Now execute this code:

mov ax, BootMessage

Since no segment was specified it would have been the same as saying DS:BootMessage . If our DS segment is set to 0x07C0 and the Bootmessage set to 0x7C50 we are then accessing physical address (0x07C0<<4)+0x7C50 = 0xF850 . Now it should be obvious - if CS is not 0 we are loading BootMessage from a memory address where our string doesn't exist.

To fix this is simple. Don't assume CS is what we expect. Code the bootloader to use what we want. We intend to have DS and ES set to segment 0x0000:

xor ax, ax      ; Set AX to 0
mov ds, ax      ; Initialize DS=ES=0
mov es, ax

Some virtual machines and real hardware are known to far jmp to our bootloader using 0x07C0:0x0000 and some others with 0x0000:0x7C00. If you assume the value in CS is what you want then your code may work on some platforms and not others.

Community
  • 1
  • 1
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
-2

In your lines here:

 mov ax, BootMessage
 mov bp, ax

I don't believe one can move an address to AX directly like you have here since there's no instruction available in x86 to query the address pointer of the AX register's per se.

Instead, why not directly move it into BX as follows:

  mov BX, BootMessage
  ; and move the byte pointed to by [BX] into AL for 0x10 video ops
  mov al, [BX] 

not sure if this was already tried ??

N0AGI
  • 1
  • 1
  • 1
    `int 10h/ah=13h` take a segment:offset passed in ES:BP which points to a string made up of characters and attributes and displays it. It doesn't take a single character in `AL` as you suggest. http://www.ctyme.com/intr/rb-0210.htm . `mov ax, BootMessage` `mov bp, ax` could have been simplified to `mov bp, BootMessage` – Michael Petch Jul 30 '23 at 20:00
  • This is NASM syntax. `mov ax, BootMessage` is just `mov reg, imm16`. Any register can be the destination. – Peter Cordes Jul 30 '23 at 22:17