[Last time] Replaced "Hello, World! In assembly language" in 1 with C language. The sample program is here.
The development environment is the same as Last time.
When you turn on your PC, software called BIOS starts. When the device initialization is completed, this BIOS loads the MBR (Master Boot Record) of the boot device (FDD, HDD, etc.) into the memory and transfers control to the program in the MBR area. The program in this MBR area is called the bootstrap loader.
code16gcc.h
tells GCC that the program is in 16BIT mode.
code16gcc.h
#ifndef _CODE16GCC_H_
#define _CODE16GCC_H_
__asm__(".code16gcc\n");
#endif
Using the inline assembler, create a program that outputs "Hello, World!" In the same way as Last time.
hello.c
#include"code16gcc.h"
__asm__("jmp main");
#define TEXT_COLOR_WHITE 0x07
void print(const char *s)
{
while(*s) {
//Call the BIOS function and output one character on the screen
__asm__ __volatile__ ("int 0x10" : : "a"(0x0E00 | *s), "b"(TEXT_COLOR_WHITE));
s++;
}
}
void main(void) {
print("Hello, World!");
while(1) {
//Stop CPU operation
__asm__ __volatile__("hlt");
}
}
The BIOS loads the MBR at address 0x7C00 in memory. Therefore, the start position of the program is 0x7C00. The boot signature (0xAA55) is like a signature that the MBR is valid, and without the boot signature the MBR is treated as invalid. The boot signature is located in the MBR 510-511 bytes. The address in memory of the boot signature is address 0x7DFE (0x7C00 + 510 bytes = 0x7DFE).
linker.ld
ENTRY(main);
SECTIONS
{
/*Program start position*/
. = 0x7C00;
.data : { hello.o; }
/*Boot signature*/
. = 0x7DFE;
.sig : { SHORT(0xaa55); }
}
Create an object file (hello.o) with the standard library link information (debugging information, etc.) removed.
#compile
gcc -m32 -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o hello.o hello.c
Create a binary file (hello.bin) from an object file (hello.o) using a linker.
#Creating a binary file
ld -m elf_i386 -s -static -Tlinker.ld -nostdlib -nmagic --oformat binary -o hello.bin hello.o
Write the binary file to the floppy disk image in the same way as Last time. If you run QEMU and the output "Hello, World!" Is output, it is successful.
#Launch and connect to Vagrant
host$ vagrant up
host$ vagrant ssh
#Execution of sample program
vagrant$ cd /vagrant/hello-c
vagrant$ rake
Rakefile
OBJECT_FILE = 'hello.o'
BINARY_FILE = 'hello.bin'
IMAGE_FILE = 'floppy.img'
LINKER_FILE = 'linker.ld'
task :default => :run
task :run => [ BINARY_FILE, IMAGE_FILE ] do
sh "dd status=noxfer conv=notrunc if=#{BINARY_FILE} of=#{IMAGE_FILE}"
sh "qemu -boot a -fda #{IMAGE_FILE} -curses -monitor stdio"
end
file BINARY_FILE => [ LINKER_FILE, OBJECT_FILE ] do
sh "ld -m elf_i386 -s -static -T#{LINKER_FILE} -nostdlib -nmagic --oformat binary -o #{BINARY_FILE} #{OBJECT_FILE}"
end
file IMAGE_FILE do
sh "qemu-img create -f raw #{IMAGE_FILE} 1440K"
end
rule '.o' => '.c' do |t|
sh "gcc -masm=intel -m32 -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o #{t.name} #{t.source}"
end
rule '.s' => '.c' do |t|
sh "gcc -S -masm=intel -m32 -ffreestanding -fno-common -fno-builtin -fomit-frame-pointer -O2 -c -o #{t.name} #{t.source}"
end
require 'rake/clean'
CLEAN.include([ '*.bin', '*.img', '*.o' ])
Let's convert C language to assembly language and compare it with the program of Last time.
The following command creates hello.s
from hello.c
.
cd /vagrant/hello-c-optimization
rake hello.s
The following program removes unnecessary directives (.xxxx) for readability. The syntax is slightly different between the assembly language output by NASM and GCC.
/vagrant/hello-c-optimization/hello.s
.file "hello.c"
.intel_syntax noprefix
.code16gcc
jmp main
print:
push ebx
mov edx, eax //Set the start address of the string in the EDX register
movzx eax, BYTE PTR [eax] //Get one character from a string and set it in the EAX register
test al, al
je .L1
mov ebx, 7 //Text color (white)(0x07)
.L4:
movsx eax, al
or ah, 14 //Tell the BIOS a single character display(0x0E)
int 0x10 //Call a function in the BIOS. Call video interrupt.
add edx, 1 //Increment the EDX register
movzx eax, BYTE PTR [edx] //Get one character from a string and set it in the EAX register
test al, al
jne .L4
.L1:
pop ebx
ret
.LC0:
.string "Hello, World!"
main:
mov eax, OFFSET FLAT:.LC0 //Set the start address of the character string in the EAX register
call print
.L8:
hlt
jmp .L8
Recommended Posts