This article was written for people who are always interested to know how different things work. For those developers who usually write their programs at a high level, C, C ++ or Java is not important, but they are faced with the need to do something at a low level. We will consider low-level programming using the example of the bootloader.
We describe what happens after the computer is turned on and how the system boots. As a practical example, consider how you can write your own bootloader, which is actually the starting point for booting the system.
What is Boot Loader
Boot loader is a program that is recorded on the first sector of a hard disk. The BIOS automatically reads the entire contents of the first sector into memory, immediately after power on. The first sector is also called the master boot record. In fact, it is not necessary for the first sector of the hard disk to boot something. This name was historically established, as the developers used such a mechanism to load the operating system.
')
Be prepared to dive deeper.
In this section, I will talk about what knowledge and tools are needed to develop your own bootloader, and also remind you of some useful information about booting the system.
And so, what language do you need to know to write a boot loader
In the first stage, when a computer is running, the hardware is monitored primarily through BIOS functions, known as “interrupts”. You can call an interrupt only in assembly language - it would be great if you are at least a little familiar with this language. But this is not a prerequisite. Why? We will use the "mixed code" technology, where you can combine high-level constructions with low-level commands. This does not simplify our task much.
This article will mainly use the C ++ language. But if you know C, it will be easy for you to recognize the necessary C ++ elements. In general, even knowledge of the C language will be enough, but then you will have to change the source code of the examples.
If you know Java or C #, then unfortunately this does not help for our task. The point is that Java and C # code that is produced after compilation is intermediate. A special virtual machine is used for further processing (Java machine for Java and .NET for C #), converting the intermediate code into instructions for the processor. After conversion, it can be executed. This architecture makes it impossible to use mixed code technology - but we will use it to make our lives easier, so Java and C # will not help here.
And so, to develop a simple bootloader, you must know C or C ++, and it would not be bad if you know a little Assembly language.
Which compiler do you need
To use mixed code technology, you need at least two compilers: for the assembler and for C / C ++, and a linker that will merge the object files (.obj) into one executable file.
Now, let's talk about some special moments. There are two modes of operation of the processor: real and protected mode. Real mode is 16-bit and has some limitations. Protected mode is 32-bit and is fully utilized by the operating system. When the computer is just starting, the processor runs in 16-bit mode. Thus, in order to write a program and get an executable file, you need a compiler and linker for the assembler for 16-bit mode. For C / C ++, you only need a compiler that can create object files for 16-bit mode.
Modern compilers are made for 32-bit applications, so we cannot use them.
I tried several free and commercial compilers for 16-bit mode and chose a product from Microsoft. The compiler together with the linker for the assembler, C and C ++ are included in Microsoft Visual Studio 1.52, you can download it from the official website of the company. Some details about the compilers we need are given below.
ML 6.15 - Microsoft's assembler compiler for 16-bit mode.
LINK 5.16 is a linker that can create COM files for 16-bit mode.
CL - C, C ++ compiler for 16-bit mode.
You can also use several alternatives.
DMC is a free compiler for compiling assembler, C, C ++ for 16-bit and 32-bit Digital Mars.
LINK is a free linker for the DMC compiler.
There are also some products from Borland.
BCC 3,5 - C, C ++ compiler that can create files for 16-bit mode.
TASM - assembler compiler for 16-bit mode.
TLINK is a linker that can create COM files for 16-bit mode.
All code samples in this article have been developed with tools from Microsoft.
How the system boots
In order to solve our problem, we must remember how the system loads.
Let us briefly consider how the components of the system interact when the system boots.

After control has been transferred to the address 0000: 7C00, the Master Boot Record (MBR) starts its work and starts the loading of the operating system.
Let's go to coding
In the following sections, we will be directly involved in low-level programming - we will write our own loader.
Program architecture
We are developing a bootloader for ourselves. His tasks are only the following:
- Correct loading into memory at 0000: 7 C00.
- Calling the BootMain function we wrote in a high-level language.
- Display the phrase - “Hello, world ...", from low-level.
The architecture of the program.

The first object is StartPoint, which is written exclusively in assembly language, since there are no instructions we need in high-level languages. This tells the compiler what type of memory should be used, and the address of the command in RAM that should be executed after reading it. It also corrects the registers of the processor and transfers control to the BootMain function, which is written in a high level language.
The next object, BootMain, is analogous to main, which, in turn, is the main function in which all program functions are concentrated.
Classes CDisplay and CString take care of the functional part of the program and displays a message on the screen. As you can see in the previous picture, the CDisplay class uses the CString class in its work.
Development environment
Here I use standard Microsoft Visual Studio 2005 or 2008 development environments. You can use any other tools, but I'm sure that these two, with some settings, compile and work easily and conveniently.
First we need to create a project Makefile Project, where the main work will be done.
File-> New \ Project-> General \ Makefile Project
BIOS interrupts and clears the screen
To display a message on the screen, we need to clear it to start. We will use special BIOS interrupts for this purpose.
The BIOS offers a series of interrupts for working with hardware, such as a video card, keyboard, system disk. Each interrupt has the following structure:
int [number_of_interrupt];
Where "number_of_inter" is the interrupt number.
Each interrupt has a number of parameters that must be set before it is called. The processor register is ah, always responsible for the number of functions for the current interrupt, and other registers are usually used for other parameters of the current operation. Let's see how the work of interrupt number 10h is done in assembler. We will use the 00-function, it changes the video mode and clears the screen:
mov al, 02h; 80x25 () mov ah, 00h; int 10h;
We will consider only those interrupts and functions that will be used in our application. We will need:
int 10h, function 00h – ; int 10h, function 01h – ; int 10h, function 13h – ;
"Mixed Code"
The C ++ compiler supports inline assembly, that is, when writing code in a high level language, you can also use a low level language. Assembler instructions that are used at a high level are also called asm inserts. They consist of the keyword "__asm" and a block of assembler instructions:
__asm ; , ASM { ; … ; } ;
To demonstrate an example of mixed code, we will use the previously mentioned assembly code that cleans up the screen and combine it with code written in C ++.
void ClearScreen() { __asm { mov al, 02h; 80x25 () mov ah, 00h; int 10h; } }
CString implementation
The CString class is designed to work with strings. It includes the Strlen () method, which takes a pointer to a string as a parameter and returns the number of characters in that string.
CDisplay class is designed to work with the screen. It includes several methods:
- TextOut () - displays a string on the screen.
- ShowCursor () - controls the on-screen view cursor: show, hide.
- ClearScreen () - changes the video mode and thus clears the screen.
CDisplay - implementation
Types.h - implementation
Types.h is a header file that includes definitions of data types and macros.
BootMain.cpp - implementation
BootMain () is the main function of the program, which is the first entry point (analogous to main ()). The main work is carried out here.
StartPoint.asm - implementation
;------------------------------------------------------------ .286 ; CPU type ;------------------------------------------------------------ .model TINY ; memory of model ;---------------------- EXTERNS ----------------------------- extrn _BootMain:near ; prototype of C func ;------------------------------------------------------------ ;------------------------------------------------------------ .code org 07c00h ; for BootSector main: jmp short start ; go to main nop ;----------------------- CODE SEGMENT ----------------------- start: cli mov ax,cs ; Setup segment registers mov ds,ax ; Make DS correct mov es,ax ; Make ES correct mov ss,ax ; Make SS correct mov bp,7c00h mov sp,7c00h ; Setup a stack sti ; start the program call _BootMain ret END main ; End of program
Let's put it all together
Creating a COM file
Now that the code has been developed, we need to convert it to a file for 16-bit OS. These files are .COM files. We can run the compiler from the command line, passing the necessary parameters, as a result we get several object files. Next, we run the linker to convert all .COM files into one executable file with the extension. Com. This is a workable option but not very easy.
Let's better automate this process. To do this, we need to create a .bat file and write the necessary commands in it with the necessary parameters.
Place compilers and linker in the project directory. In the same directory, we create a batch file and fill it in accordance with the example (you can use any directory instead of VC152, the main thing is for the compilers and linker to be in it) .:
.\VC152\CL.EXE /AT /G2 /Gs /Gx /c /Zl *.cpp .\VC152\ML.EXE /AT /c *.asm .\VC152\LINK.EXE /T /NOD StartPoint.obj bootmain.obj cdisplay.obj cstring.obj del *.obj
Assembly - Automation
As a final step in this section, we describe how to turn Microsoft Visual Studio 2005, 2008 into a development environment with the support of any compiler. To do this, go to the project properties:
Project-> Properties-> Configuration Properties \ General-> Configuration Type .
The
Configuration Properties tab includes three items:
General ,
Debugging, and
NMake . Select NMake and specify the path to “build.bat” in
Build Command Line and
Rebuild Command Lin .
If done correctly, then you can compile by pressing F7 or Ctrl + F7. In this case, all related information will be displayed in the output window. The main advantage here is not only assembly automation, but also monitoring errors in the code if they occur.
Testing and demonstration
This section will tell you how to see the boot loader in action, how to test and debug it.
How to check the bootloader
You can check the bootloader on real hardware or using VMware virtual machines designed for this purpose. Testing on real hardware gives you more confidence that it works just like a virtual machine. Of course, we can say that VmWare is a great way to test and debug. We will consider both methods.
First of all, we need a tool to write our bootloader to a virtual or physical disk. As far as I know, there are several free and commercial consoles and GUI applications. I used Disk Explorer for NTFS 3.66 (version for FAT, called Disk Explorer for FAT) for working in Windows OS and Norton Disk Editor 2002 for working in MS-DOS.
I will describe only Disk Explorer for NTFS 3.66 because it is the easiest way and most suitable for our purposes.
Testing with the VmWare virtual machine
Create a virtual machine
We will need VmWare software version 5.0, 6.0 or higher. To test the bootloader, we will create a new virtual machine with a minimum disk size, for example, 1 Gb. Format it to the NTFS file system. Now we need to display the formatted hard disk on VmWare as a virtual disk. To do this, select:
File-> Map or Disconnect Virtual Disks ...After that, a window will appear. There you must click the "Map" button. In the next window that appears, you must specify the path to the disk. Now you can also select a drive letter.
Do not forget to uncheck the box "Open file in read-only mode (recommended)". After you have completed all of the above indications, the disk must be read-only to avoid data corruption.
After that, we can work with the virtual machine disk as with a regular logical disk in Windows. Now we have to use Disk Explorer for NTFS 3.66 to write the boot record from position 0.
Work with Disk Explorer for NTFS
After starting the program we go to our disk (File-> Drive). In the window that appears, go to the partition logical drives and select our created disk (in my case it is Z).
Now we select the menu item View as Hex commands. In this window that appears, we can see the disk information in a 16-bit representation, divided into sectors. Now we only have 0, since the disk is empty, for now.
Now we need to write our bootloader in the first sector. We set the marker to position 00, as shown in the previous image. To copy the bootloader we use the menu item
Edit-> Paste from file command . In the window that opens, specify the path to the file and click
Open . After that, the contents of the first sector should change and look like it is shown in the picture - if you, of course, have not changed anything in the code.
You should also record the 55AAh signature on the 1FE position from the beginning of the sector. If you do not do this, the BIOS will check the last two bytes, and if it does not find the specified signature, it will assume that this sector is not bootable and will not load it into memory.
To switch to edit mode, press the
F2 key and write the necessary numbers - 55AAh signature. To exit edit mode, press
ESC .
Now we need to confirm the recorded data.
To apply the recorded we go to
Tools-> Options , now we go to the
Mode item and select the write method -
Virtual Write and click the
Write button.
Most of the routine actions have finally been completed, and now you can see that we have developed from the very beginning of this article. Let's go back to VwWare to disconnect the virtual disk (File-> Map or Disconnect Virtual Disks ... and click Disconnect).
Let's run the virtual machine. We see now how familiar lines appear from the depths of the realm of machine codes - “Hello World ...”, from low-level ... ".
Testing on real equipment
Testing on real hardware is almost the same as on a virtual machine, except that if something does not work, you will need much more time to restore it than to create a new virtual machine. To test the bootloader without having the opportunity to lose data (anything can happen), I suggest using a flash drive, but first you need to restart your computer, go to the BIOS and make sure that it supports booting from the flash drive. If he supports him, then everything is in order. If not, then you should limit testing on a virtual test machine.
The process of writing a bootloader to a flash disk in Disk Explorer for NTFS 3.66 is the same as for a virtual machine. You just have to select the hard disk itself instead of your logical partition.
Conclusion
In this article, we looked at what the boot loader is, how the BIOS works, and how the system components interact when the system boots. The practical part gave us an understanding of how you can develop your own, simple downloader. We demonstrated the technology of mixed code and assembly automation using Microsoft Visual Studio 2005, 2008.
Of course, this is only a small part compared to the huge topic of low-level programming, but if this article was interesting to you, then this is cool.
UPD:
link to the source