Writing a boot loader in Assembly and C - Part 2

Reading the contents of a floppy disk using BIOS interrupts and Services.

Introduction

In the previous article, I tried to brief about booting, how to write a bootable code in C and assembly, and how to embed assembly statements inside a
C program as well.
We also tried to write a few programs to examine if our code injected into the boot sector of a device works or not. In this article, I will try to explain about segmentation
and reading data from a floppy disk during booting and displaying it onto the screen. We will try writing
a few programs
and examine if we can read the data and try displaying it onto the screen.

What is the scope of the article?

I will limit the scope of the article onto how to write a program code in assembly, and how to copy it to the boot sector of a 3.5 inches floppy disk image,
and then how to test the floppy disk to boot it with the code we have written using an x86 emulator like bochs. I will take the help of BIOS services to achieve the task
of reading data from a floppy disk. By doing so we can explore more about BIOS routines and feel comfortable playing around in Real Mode.

Breakdown of topics

Introduction to segmentation

Programming environment

Reading data from RAM-Memory

Introduction to storage devices

Architecture of a floppy disk

Interaction with a floppy disk

Introduction to Segmentation

Before I proceed writing some examples on how to read a floppy disk, I wanted to refresh the topic of segmentation and its need as below.

What is
Segmentation?

The main memory is divided into segments that are indexed by few special registers called as segment registers (CS, DS, SS and ES).

What is the use of
Segmentation?

When we specify a 16-bit address, the CPU automatically calculates the start address of the respective segment. However, it is the duty of the programmer
to specify the start address of each segment especially when writing a program like boot loader.

What are the different types of segments?

I will mention only four types for now, as they are important for us to understand.

Code Segment

Data Segment

Stack Segment

Extended Segment

Code Segment

It is one of the sections of a program in memory that contains the executable instructions.
If you refer to my previous article, you will see the label .text where under which we intend to place the instructions to execute. When the program is loaded into memory,
the instructions under section .text are placed into code segment.
In CPU, we use CS register to refer to the code segment in memory.

Data Segment

It is one of the sections of a program in memory that contains variables both static and global by the programmer. We use DS register to refer to the data segment in memory.

Stack Segment

A programmer can use registers to store, modify and retrieve data during the scope of the program
that he has written. As there are only a few registers available for a programmer to use during the run time of the program, there is always a chance that the program logic
might get complicated, as there are only a few registers available for temporary use. Due to this, the programmer might always feel the need for a bigger place, which is more flexible
in terms of storing, processing and retrieving data. The CPU designers have come up with a special segment called the stack segment.
In order to store and retrieve data on stack segment, the programmer uses push and pop instructions. We use push instructions to pass arguments to functions as well.
We use SS register to refer to the stack segment in memory. Also remember that stack grows downwards.

Extended Segment:

The extended segment is normally used to load data that is much bigger than the size of the data that is stored in data segment. You will further see that I will try to load the data
from the floppy on Extended segment. We use ES register to refer to the Extended Segment in memory.

How to set Segment registers?

The programmer does not have the freedom to directly set any of the segment registers but instead we follow this way.

movw $0x07c0, %ax
movw %ax, %ds

What does the above step means?

Copy the data to a general purpose register.

Then assign it to the segment register.

We are setting AX register to 0x07c0

And when we are copying the contents of AX to DS. The absolute address is calculated as below.

DS = 16 * AX
So DS = 0x7c00

We further use offset to traverse from here on. To reach to a location in Data segment, we use offsets.

Programming environment

Operating system (GNU Linux)

Assembler (GNU Assembler)

Compiler (GNU GCC)

Linker (GNU linker ld)

An x86 emulator used for our testing purposes(bochs).

Reading data from RAM

Note

Now, if you observe BIOS loads our program at 0x7c00 and starts executing it and then our program starts printing values one by one. We access the data on RAM by directly specifying
the offset and setting the data segment to 0x7c00.

Example1

Once our program is loaded by BIOS at 0x7c00, let us try to read data from offset 3 and 4 and then print them onto the screen.

If you compile the program and open the binary in hexadecimal editor, you may see the string as “This is boot loader” onto output.

Introduction to storage devices

What is a storage device?

It is a device used for information storage, retrieval. It can also be used as a bootable media.

Why does a computer require storage devices?

We use computers mainly to store information, retrieve the information and process it so as part of storing and retrieval
of information, the manufactures came up with a new device called a storage device with various types.

What are the various types of storage devices available?

Depending on the size of the data, I will try to list them as below.

Floppy disk

Hard disk

USB Disk

And more…

What is a floppy disk?

It is a device used for information storage, retrieval. It is also used as a bootable media.

A floppy disk is designed to store small amounts of data whose maximum size
could be limited to few Mega Bytes.

What is a Mega Byte?

In computing, the size of the data is measured in one of the following ways:

Where is boot sector located on a floppy disk?

It is located on the first sector of the disk.

Interaction with a floppy disk

How to read data from a floppy disk?

As our mission in this article is to read data from a floppy disk, the only choice left to us as of now is to use BIOS Services in our program as during the boot time we are in Real Mode to interact with the floppy disk. We need to use BIOS Interrupts to achieve our task.

We call the macro to read sector2 and then display its content and then again we call the macro to read sector 3 and then display its content.

_freeze: #infinite loopjmp _freeze #

After displaying the contents of the sectors, we go into one infinite loop to hang our program.

_failure: #
mPrintString msgFail #write error message and then
jmp _freeze #jump to the freezing point

We defined this section to jump to this label incase of any erroneous situations and then hang the program again.

. = _start + 510 #mov to 510th byte from 0 pos
.byte 0x55 #append first part of the boot signature
.byte 0xAA #append last part of the boot signature

We move to the 510th byte of the sector and then append the boot signature which is a mandatory for a floppy disk to be identified as a bootable device,
else the system throws an error as invalid disk.

_sector2: #second sector of the floppy disk
.asciz "Sector: 2\n\r" #write data to the begining of the sector
. = _sector2 + 512 #move to the end of the second sector
_sector3: #third sector of the floppy disk
.asciz "Sector: 3\n\r" #write data to the begining of the sector
. = _sector3 + 512 #move to the end of the third sector

The above step performs appending a string at the beginning of the sector 2 and 3.

That’s all for this article

Have fun and try to explore reading floppy disk in real mode and embed the functionality into your bootloader.

In the following articles I will try to brief about File systems and their importance. We will also write a minimal bootloader to parse a fat12 formatted floppy disk,
how to read and write to it and also write a second stage bootloader and its importance.

Share

About the Author

Ashakiran is from Hyderabad, India and currently working as a Software Engineer in USA. He is a hobbyist programmer and enjoys writing code in C/C++ and Assembly. His biggest passion is into the world of Operating Systems Design and Development.

Comments and Discussions

Many thanks to your tutorial I have learned how to create a custom boot loader. But now I want to use it in my machine so that after my boot loaer displays the intended message ie. hello world, it proceeds to load the OS installed on the machine. How can this be achieved??

Hi. I have doubled checked everything. I'm getting an "No bootable device" error from bochs when I load. When I look at the boot.bin using hexedit, it does not display the XZ in position offset 3, and 4. I only see the ELF string.

Hi Ashakiran. I had previously completed Part1 of your series. I had not run into any issues using Bochs or the GNU Assemblier. I followed the steps you posted for Example 1 of this part 2 series.
I am using Ubuntu 12 64bit guest on a Windows 7 64 bit host using VM Virtualbox.
Please advise
Thanks again! Great articles.

do all these GNU tools you have mentioned come with the ubuntu linux? Sorry for this stupid question as I have only been doing java an objective C in Mac.
I have theoretical knowledge about operating systems such as OSX and Windows and have never done any practical work. After reading your article I think linux is the best environment for following and practicing the subjects in your articles.
I am going to install linux on a second hand pc which I recently bought only because of your fantastic articles that inspired me so much. Do I get all of these tools after installing ubuntu on my pc?
Please forgive me if my questions sound a bit stupid.