I can determine if you are using assembler NASM (or NASM compatible). I do not know which OS you use to build the bootloader, but I will consider Linux or Windows. Other environments will be somewhat similar.
You must split your bootloader in two to make it easier. One of them is the bootloader, and the second is the second stage of loading 0x1000: 0x0000. This allows us to correctly determine the starting point for our bootloader. It is expected that the bootloader will be loaded at the physical address 0x07c00, and the second stage - 0x10000 ((0x1000 <4 +) + 0). We need assembler to correctly generate addresses for our data and code.
I wrote some StackOverflow answers that describe some of the changes I made to the code. Some of the most important ones are:
- General bootloader tips that contain general recommendations and assumptions that you do not want to make in the bootloader
- Information about errors associated with improper DS setup and garbage collection when accessing memory variables. This refers to the second stage.
If you do not have a proper understanding of segment pairs: offsets, I recommend this article. I talk about this because there is confusion in your question and in your code. You seem to think that the physical memory address 0x1000 is the same as the segment: offset pair 0x1000: 0x0000. In your question you say:
The following code loads the contents of the floppy drive into memory and proceeds to it (starts loading at 0x1000).
In your code, you have this line and comment:
jmp 0x1000:0000 ;Jump to 0x1000, start of second program
If you look at this link, you will find that the: offset segment calculates the physical address by moving the segment to the left by 4 bits (multiply by 16 decimal), and then adding the offset. The equation usually appears as (segment <4) + offset. In your case, 0x1000: 0x0000 is the segment 0x1000 and the offset 0x0000. Using the equation to get the physical address in memory that you received (0x1000 <4) + 0x0000 = 0x10000 (not 0x1000)
From your code, itβs impossible to tell how you are going with NASM. I will give an example of how this can be done, but the important part is the bootloader partition. Suppose we put your bootloader in a file called bootload.asm
:
[bits 16] [ORG 0x7c00]
You should notice that I deleted this line:
mov dl,0x0 ;Drive = 0 (Floppy)
This hardcodes the boot disk to floppy A :. If you boot from a USB drive, hard drive, or floppy disk B: your code will not work because the number of disks will probably not be zero in these cases. The BIOS sends the actual boot disk that was used to boot your bootloader. This value is in the DL register. This is the value you must use for the functions of the BIOS drive. Since DL already contains a boot disk, we simply use it as is.
The second stage can be changed in this way. I will take a file called stage2.asm
:
[BITS 16] [ORG 0x0000]
I did not try to optimize your code. The idea is to show how to add glue to fix your problems. Both files indicate the starting point using the ORG
directive. Boot loaders must be assembled so that they work at memory address 0x07c00. You load the second stage into 0x1000: 0x0000, which maps to the physical address 0x10000. We set the ORG to 0x0000, because FAR JUMP jmp 0x1000:0000
will set CS = 0x1000 and IP = 0x0000. Since the IP address is 0x0000, we want the ORG to match it so that the links to the closest memory are relative to the beginning of our 64k segment.
This will allow the assembler to generate the correct memory references for your variables and code. Since you did this incorrectly in your code, your second step read the wrong memory location for var
and subsequently displayed the wrong character.
Once you split 2 files, you need to collect them using NASM and then put them in a disk image. Unlike your question, I will use DD to create a 720k floppy disk image, and then place the bootloader at the beginning (without cutting the disk), and then place the second stage, starting in the sector immediately after. This can be done as follows:
# Assemble both components as binary images with NASM nasm -f bin bootload.asm -o bootload.bin nasm -f bin stage2.asm -o stage2.bin
You can run such an image using QEMU with something like:
qemu-system-i386 -fda disk.img
If you use Windows and you do not have access to DD, you can use this modification for stage2.asm
:
[BITS 16] [ORG 0x0000]
Then create and create a 720K disk image with the following commands:
nasm -f bin bootload.asm -o bootload.bin nasm -f bin stage2.asm -o stage2.bin copy /b bootload.bin+stage2.bin disk.img
disk.img
will be a 720K disk image to be used by QEMU or Bochs. The final size of disk.img
should be disk.img
bytes.
If you want to move a value from a memory address to a register, you can do it directly without an intermediate register. In your stage2.asm
you have the following:
mov bx, var mov ah, 0x0e mov al, [bx]
It can be written as:
mov ah, 0x0e mov al, [var]
This will move one byte from the var memory area and move it directly to AL. The size is determined by NASM as byte, because the target AL is an 8-bit register.