๐๏ธ 32๋นํธ ๋ณดํธ๋ชจ๋์์ ํ๋ฉด์ ๊ธ์์ฐ๋ ์ฝ๋์
๋๋ค.(๊ทธ๋ํฝ ๋ชจ๋ ์ ์ง์ํ)
This is code for writing text to the screen in 32-bit protected mode (while maintaining graphics mode).
๐๏ธ ํ์ผ / files
— kernel.cํ์ผ์ ์ ์ธํ๊ณค ๊ธฐ์กด์ ๋ด์ฉ๊ณผ ๋์ผํฉ๋๋ค.
Except for the kernel.c file, the contents are identical to the original.
boot.asm kernel.c sector2.asm
๐๏ธ ์ฝ๋ / Code
โ๏ธ kernel.c
typedef unsigned char byte;
// ------------------------------------------------------------------------
// 1. ๊ธฐ๋ณธ ๊ทธ๋ํฝ ํจ์ (volatile ์ ์ฉ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ๊ฐ์ ์ฐ๊ธฐ)
// Basic graphics functions (forced memory write via volatile)
// ------------------------------------------------------------------------
void draw_pixel(int x, int y, byte color) {
volatile byte *vram = (volatile byte *)0xA0000;
vram[(y * 320) + x] = color;
}
void clear_screen(byte color) {
for (int y = 0; y < 200; y++) {
for (int x = 0; x < 320; x++) {
draw_pixel(x, y, color);
}
}
}
// ------------------------------------------------------------------------
// 2. ๊ธ์ ์ถ๋ ฅ ํจ์ (ํฐํธ ๋ฐ์ดํฐ๋ฅผ ํจ์ ๋ด๋ถ๋ก ์ด๋ํ์ฌ ๋งํน ์ค๋ฅ ํด๊ฒฐ)
// Character output function (resolved linking errors by moving font data inside the function)
// ------------------------------------------------------------------------
void draw_char(int start_x, int start_y, int font_index, byte color) {
// ๋ง์ปค ์ค๋ฅ๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด ํฐํธ ๋ฐ์ดํฐ๋ฅผ ํจ์ ๋ด๋ถ ์ง์ญ ๋ณ์๋ก ์ ์ธํฉ๋๋ค.
// To prevent linker errors, declare the font data as a local variable within the function.
byte font_8x8[6][8] = {
{0x18, 0x24, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00}, // A (index 0)
{0x7C, 0x42, 0x42, 0x7C, 0x42, 0x42, 0x7C, 0x00}, // B (index 1)
{0x3C, 0x42, 0x40, 0x40, 0x40, 0x42, 0x3C, 0x00}, // C (index 2)
{0x78, 0x44, 0x42, 0x42, 0x42, 0x44, 0x78, 0x00}, // D (index 3)
{0x7E, 0x40, 0x40, 0x78, 0x40, 0x40, 0x7E, 0x00}, // E (index 4)
{0x7E, 0x40, 0x40, 0x78, 0x40, 0x40, 0x40, 0x00} // F (index 5)
};
for (int y = 0; y < 8; y++) {
byte row_data = font_8x8[font_index][y];
for (int x = 0; x < 8; x++) {
// ๋นํธ ๋ง์คํน ์ฐ์ฐ์ผ๋ก 1์ธ ๋ถ๋ถ๋ง ์ ์ ์ฐ์
// Plot points only at positions corresponding to 1s using bitmasking operations.
if ((row_data & (0x80 >> x)) != 0) {
draw_pixel(start_x + x, start_y + y, color);
}
}
}
}
// ------------------------------------------------------------------------
// 3. ์ปค๋ ๋ฉ์ธ ํจ์
// Kernel main function
// ------------------------------------------------------------------------
void kernel_main(void) {
// ๋ฐฐ๊ฒฝ์ ํ๋์(1)์ผ๋ก ์ด๊ธฐํ
// Reset the background to blue (1).
clear_screen(1);
// ํ๋ฉด ์ค์(X=100, Y=80) ๊ทผ์ฒ์ ๋ค๋ฅธ ์์์ผ๋ก A, B, C, D, E, F ์ถ๋ ฅ
// Display A, B, C, D, E, and F in different colors near the center of the screen (X=100, Y=80)
draw_char(100, 80, 0, 15); // ํฐ์/white 'A'
draw_char(112, 80, 1, 14); // ๋
ธ๋์/yellow 'B'
draw_char(124, 80, 2, 10); // ์ด๋ก์/green 'C'
draw_char(136, 80, 3, 12); // ๋นจ๊ฐ์/red 'D'
draw_char(148, 80, 4, 13); // ์์/Purple 'E'
draw_char(160, 80, 5, 11); // ์ฒญ๋ก์/blue-green 'F'
while (1) {
__asm__ __volatile__("hlt");
}
}
๐๏ธ ์ฝ๋์ค๋ช / Code Explanation
โ๏ธ byte ํ์ ์ ์ธ / byte type declaration
typedef unsigned char byte;
— typedef
1)”Type Definition”์ ์ค์๋ง๋ก, ๊ธฐ์กด์ ์กด์ฌํ๋ ๋ฐ์ดํฐ ํ์
์ ์๋ก์ด ๋ณ๋ช
(๋ณ์นญ)์ ๋ถ์ฌ์ฃผ๋ ํค์๋์
๋๋ค.
Short for “Type Definition,” this is a keyword used to assign a new alias to an existing data type.
2)๊ธด ์ด๋ฆ์ ์งง๊ฒ ์ค์ฌ์ ์ฌ์ฉ ํ ์ ์๊ฒ ํด์ค๋๋ค.
It allows you to shorten long names for use.
3)typeof๋ฌธ๋ฒ์ ์๋์ฒ๋ฆ ์ฌ์ฉ๋ฉ๋๋ค.
The typeof syntax is used as shown below.
typedef [๊ธฐ์กด ์๋ฃํ/Existing data type] [์๋ก์ด ๋ณ๋ช
/New alias];
4)typeof๋ฅผ ์ฌ์ฉํ๋ฉด ์๋์ฒ๋ผ ๋ฐ์ดํฐ ํ์
์ ์ค์ฌ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
By using typeof, you can use a shortened form for the data type, as shown below.
# ๋ณ์นญ์ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ
# When no alias is used
volatile unsigned char *vram = (volatile unsigned char *)0xA0000;
# ๋ณ์นญ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
# When using aliases
volatile byte *vram = (volatile byte *)0xA0000;
— unsigned char
1)C์ธ์ด์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ๊ฐ์ฅ ์์ ๋จ์์ ๋ฐ์ดํฐ ์๋ฃํ์
๋๋ค.
It is the smallest unit of data type provided by default in the C language.
2)char๋ ๊ธฐ๋ณธ์ ์ผ๋ก 1๋ฐ์ดํธ(8๋นํธ) ํฌ๊ธฐ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ง๋๋ค.
A char typically occupies 1 byte (8 bits) of memory.
3)unsigned๋ ‘๋ถํธ๊ฐ ์๋ค’๋ ๋ป์
๋๋ค. ๋ง์ด๋์ค((-)) ์์๋ฅผ ํํํ์ง ์๊ฒ ๋ค๋ ์๋ฏธ์
๋๋ค.
‘Unsigned’ means ‘without a sign.’ It signifies that negative numbers (indicated by a minus sign) are not represented.
4)์ผ๋ฐ์ ์ธ char๋ (-128 ~ +127)๊น์ง ํํํ์ง๋ง, ๋ถํธ๋ฅผ ์์ค unsigned char๋ 8๋นํธ ์ ์ฒด๋ฅผ ์์๋ก๋ง ์ฌ์ฉํ์ฌ (0 ~ 255)๊น์ง ํํํ ์ ์์ต๋๋ค.
While a standard char represents values โโfrom -128 to +127, an unsigned charโwhich removes the sign bitโuses all 8 bits for positive values, allowing it to represent a range of 0 to 255.
โ๏ธ draw_pixelํจ์
draw_pixel function
— ํน์ ์ขํ ((x, y))์ ์ํ๋ ์์์ ์ (ํฝ์
) ํ๋๋ฅผ ์ฐ๋ ํจ์์
๋๋ค.
This is a function that plots a single point (pixel) of a desired color at specific coordinates (x, y).
— unsigned char
1)C์ธ์ด์์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ๊ฐ์ฅ ์์ ๋จ์์ ๋ฐ์ดํฐ ์๋ฃํ์
๋๋ค.
It is the smallest unit of data type provided by default in the C language.
2)char๋ ๊ธฐ๋ณธ์ ์ผ๋ก 1๋ฐ์ดํธ(8๋นํธ) ํฌ๊ธฐ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ง๋๋ค.
A char typically occupies 1 byte (8 bits) of memory.
3)unsigned๋ ‘๋ถํธ๊ฐ ์๋ค’๋ ๋ป์
๋๋ค. ๋ง์ด๋์ค((-)) ์์๋ฅผ ํํํ์ง ์๊ฒ ๋ค๋ ์๋ฏธ์
๋๋ค.
‘Unsigned’ means ‘without a sign.’ It signifies that negative numbers (indicated by a minus sign) are not represented.
4)์ผ๋ฐ์ ์ธ char๋ (-128 ~ +127)๊น์ง ํํํ์ง๋ง, ๋ถํธ๋ฅผ ์์ค unsigned char๋ 8๋นํธ ์ ์ฒด๋ฅผ ์์๋ก๋ง ์ฌ์ฉํ์ฌ (0 ~ 255)๊น์ง ํํํ ์ ์์ต๋๋ค.
While a standard char represents values โโfrom -128 to +127, an unsigned charโwhich removes the sign bitโuses all 8 bits for positive values, allowing it to represent a range of 0 to 255.
โ๏ธ draw_pixelํจ์
draw_pixel function
— ํน์ ์ขํ ((x, y))์ ์ํ๋ ์์์ ์ (ํฝ์
) ํ๋๋ฅผ ์ฐ๋ ํจ์์
๋๋ค.
This is a function that plots a single point (pixel) of a desired color at specific coordinates (x, y).
void draw_pixel(int x, int y, byte color) {
volatile byte *vram = (volatile byte *)0xA0000;
vram[(y * 320) + x] = color;
}
–draw_pixelํจ์ ํ๋ผ๋ฉํฐ
draw_pixel function parameters
1)x ๋ ๊ฐ๋ก์ขํ,y๋ ์ธ๋ก์ขํ์ด๋ฉฐ ๊ฐ๋ก๋ 319๋ฒ,์ธ๋ก 199๋ฒ๊น์ง ๊ฐ๋ฅํฉ๋๋ค.(ํด์๋ 300 x 200)
x represents the horizontal coordinate and y the vertical coordinate; the horizontal range is 0 to 319, and the vertical range is 0 to 199 (resolution: 300 x 200).
2)color๋ 256์์ ์ฌ์ฉํ๋ฏ๋ก 256๋ฒ๊น์ง ์ฌ์ฉ๊ฐ๋ฅํฉ๋๋ค.
Since the color setting uses a 256-color palette, values โโup to 256 can be used.
–0xA0000
1)IBM PC ํธํ ์ปดํจํฐ์์ VGA ๊ทธ๋ํฝ ์นด๋์ ํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ์์๋๋ ๋ฌผ๋ฆฌ์ ์ฃผ์์
๋๋ค. ์ด ์ฃผ์์ ๋ฐ์ดํฐ๋ฅผ ์ฐ๋ฉด ๋ชจ๋ํฐ ํ๋ฉด์ ๊ทธ๋๋ก ๋ํ๋ฉ๋๋ค.
This is the physical address where the screen memory of a VGA graphics card begins on an IBM PC-compatible computer. Writing data to this address causes it to appear directly on the monitor screen.
–volatile
1)์ปดํ์ผ๋ฌ๋ ์๋๋ฅผ ๋์ด๊ธฐ ์ํด์ ํ๋ฒ ์ฝ์ด ์จ ๊ฐ์ cpu๋ ์ง์คํฐ์ ์ ์ฅํด๋๊ณ ์ฌ์ฉํฉ๋๋ค.
To increase speed, the compiler stores a value it has read into a CPU register and uses it from there.
2)volatileํค์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ์ง์คํฐ์ ๊ฐ์ ์ฐธ์กฐํ์ง ์๊ณ ๋ฐ๋ก ๋ฉ๋ชจ๋ฆฌ์์ ๊ฐ์ ๊ฐ์ ธ์ต๋๋ค.
When the volatile keyword is used, the value is retrieved directly from memory instead of referencing the value in a register.
— ๋ฌธ์์ ์์น ์ค์
Setting the text position
vram[(y * 320) + x] = color;
1)vram๋ฐฐ์ด์ ๋จ์ ๊ฐ์ ๋์ด์ด๊ณ ๋์ค์ ์ค์ ๋ก X,Y์์น๋ฅผ ์ก๊ธฐ์ํด ๋ฐ์ดํฐ ์
ํ
ํฉ๋๋ค.
The VRAM array is simply a sequence of values; the data is configured later to actually determine the X and Y positions.
— ํ๋ฉด์ ๋ฐฐ๊ฒฝ์์ ์ง์ ํฉ๋๋ค.
Specifies the background color of the screen.
void clear_screen(byte color) {
for (int y = 0; y < 200; y++) {
for (int x = 0; x < 320; x++) {
draw_pixel(x, y, color);
}
}
}
1)kernel_mainํจ์์์ clear_screen(1)์ ์ง์ ํด์ ํ๋ฉด์ ํ๋์์ผ๋ก ์
ํ
ํฉ๋๋ค.
In the kernel_main function, specify clear_screen(1) to set the screen color to blue.
void kernel_main(void) {
// ๋ฐฐ๊ฒฝ์ ํ๋์(1)์ผ๋ก ์ด๊ธฐํ
// Reset the background to blue (1).
clear_screen(1);
...
}
โ๏ธ draw_charํจ์
draw_char function
— ‘A’์ ๋ฐ์ดํฐ ์๊ฐํ
Data visualization of ‘A’
1){0x18, 0x24, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00}์ 2์ง์๋ก ๋ฐ๊พธ์ด 8×8 ๋ชจ๋์ข
์ด์ ์ฑ์ฐ๋ฉด ๋ค์๊ณผ ๊ฐ์ ๋ชจ์์ด ๋์ต๋๋ค.
If you convert {0x18, 0x24, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x00} into binary and fill in an 8×8 grid, the following shape is formed.
| ํ/Col (Y) | 16์ง์/Hexadecimal | 2์ง์ ๋ณํ (8๋นํธ) Binary Conversion (8-bit) | ์ค์ ๊ธ์ ๋ชจ์ Actual character shape |
| 0๋ฒ์งธ/0th | 0x18 | 00011000 | โโโโโโโโ |
| 1๋ฒ์งธ/1st | 0x24 | 00100100 | โโโโโโโโ |
| 2๋ฒ์งธ/2nd | 0x42 | 01000010 | โโโโโโโโ |
| 3๋ฒ์งธ/3rd | 0x7E | 01111110 | โโโโโโโโ |
| 4๋ฒ์งธ/4th | 0x42 | 01000010 | โโโโโโโโ |
| 5๋ฒ์งธ/5th | 0x42 | 01000010 | โโโโโโโโ |
| 6๋ฒ์งธ/6th | 0x42 | 01000010 | โโโโโโโโ |
| 7๋ฒ์งธ/7th | 0x00 | 00000000 | โโโโโโโโ |
— ํ๋ฉด์ ๊ธ์ ๊ทธ๋ฆฌ๊ธฐ / Drawing text on the screen
for (int y = 0; y < 8; y++) {
byte row_data = font_8x8[font_index][y];
for (int x = 0; x < 8; x++) {
// ๋นํธ ๋ง์คํน ์ฐ์ฐ์ผ๋ก 1์ธ ๋ถ๋ถ๋ง ์ ์ ์ฐ์
// Plot points only at positions corresponding to 1s using bitmasking operations.
if ((row_data & (0x80 >> x)) != 0) {
draw_pixel(start_x + x, start_y + y, color);
}
}
}
1)๋ฐ๋ณต๋ฌธ์ด ์คํ๋๋ฉด์ font_8x8 ๋ฐฐ์ด์์ ํด๋นํ๋ ๋ฌธ์์ ๊ฐ์ ํ๋์ฉ ๊บผ๋
๋๋ค.
As the loop executes, it retrieves the values โโfor the corresponding characters from the font_8x8 array one by one.
byte row_data = font_8x8[font_index][y];
2)0x80 >> x
2-1) 16์ง์ 0x80์ ์ด์ง์๋ก 10000000๊ฐ์ด ๋ฉ๋๋ค.
The hexadecimal value 0x80 corresponds to the binary value 10000000.
2-2) ๋ฐ๋ณต๋ฌธ์ด ์คํ๋๋ฉด x = 0 ์ผ ๋: 10000000,x = 1 ์ผ ๋: 01000000,x = 2 ์ผ ๋: 00100000 ์ด๋ ๊ฒ ๋นํธ๊ฐ ์ด๋ํฉ๋๋ค.
When the loop executes, the bits shift as follows: 10000000 when x = 0, 01000000 when x = 1, and 00100000 when x = 2.
3) row_data & (โฆ)
3-1) ์ค๋ ์ฐ์ฐ์ผ๋ก row_data์ 0x80 >> x๊ฐ์ ํ์ธํฉ๋๋ค.
Check row_data and the value of 0x80 >> x using an AND operation.
3-2)row_data๊ฐ A๋ฌธ์๋ฅผ ๊ทธ๋ฆฌ๊ณ 0x80 >> 0 ์ธ๊ฒฝ์ฐ๋ก ์ค๋ ์ฐ์ฐ์ ๊ฒฐ๊ณผ๊ฐ 00000000 ์ด๋ฏ๋ก ์ ์ ์ฐ์ง ์์ต๋๋ค.
If row_data represents the character ‘A’ and the operation is 0x80 >> 0, the result of the AND operation is 00000000, so no dot is drawn.
00011000 (row_data: 'A'์ ์ฒซ ์ค / row_data: First line of 'A')
& 10000000 (0x80 >> 0)
------------
00000000
3-3) x=3์ผ๋ 4๋ฒ์งธ ๋นํธ๊ฐ 1์ด ๋ฉ๋๋ค.
00011000 (row_data: 'A'์ ์ฒซ ์ค / row_data: First line of 'A')
& 00010000 (0x80 >> 3)
------------
00010000
3-4)๋นํธ๊ฐ 1์ธ ๋ถ๋ถ์ draw_pixelํจ์๋ฅผ ์ฌ์ฉํด์ ์ ์ ์ฐ์ต๋๋ค.
For parts where the bit is 1, a dot is drawn using the draw_pixel function.
draw_pixel(start_x + x, start_y + y, color);
3-5) ์ ๋ฆฌํ๋ฉด ์ฒซ๋ฒ์งธ์ค๋ถํฐ 8๋ฒ์งธ ์ค๊น์ง ์ข์ธก์์ ์ฐ์ธก์ผ๋ก 8๋นํธ๋ฅผ ์์๋๋ก ํ์ธํด์ ์ ์ ์ฐ์ด์ ๋ฌธ์๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
To summarize, the process involves checking 8 bits in order from left to right across the first eight lines and outputting characters by plotting points.
โ๏ธ ์ ์ ๋ฐ ๋๊ธฐ์ํ / Power Saving and Standby Modes
1)__asm_ ์ด์
๋ธ๋ฌ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ํค์๋์
๋๋ค.__asm is a keyword used for assembler code.
2)__volatile__ hlt๋ช
๋ น์ด๋ฅผ ์ปดํ์ผ๋ฌ๊ฐ ์ญ์ ํ์ง ๋ชปํ๊ฒ ๋ง๋ ๊ธฐ๋ฅ์
๋๋ค.__volatile__ prevents the compiler from removing the hlt instruction.
3)hlt๋ ๋ค์ ์ ํธ (์ธํฐ๋ฝํธ) ์ฌ๋ ๊น์ง ๋๊ธฐ์ํ๋ก ์ค์ ํฉ๋๋ค.
The HLT instruction sets the processor to a waiting state until the next signal (interrupt) arrives.
while (1) {
__asm__ __volatile__("hlt");
}
๐๏ธ ์ปดํ์ผ
# 1. ๋ถํธ์นํฐ ๋ฐ ๋ณดํธ๋ชจ๋ ์ง์
๋ถ ์ปดํ์ผ
nasm -f bin boot.asm -o boot.bin
nasm -f elf32 sector2.asm -o sector2.o
# 2. ์๋ก ์์ ํ kernel.c ์ปดํ์ผ
gcc -m32 -c kernel.c -o kernel.o -nostdlib -fno-builtin -fno-stack-protector -fno-pic -fno-PIE
# 3. ํ๋์ ๋ฐ์ด๋๋ฆฌ๋ก ๊ฒฐํฉ
ld -m elf_i386 -Ttext 0x8000 -e start -o sector2.bin sector2.o kernel.o --oformat binary
# 4. ์ด๋ฏธ์ง ์์ฑ ๋ฐ ์คํ
cat boot.bin sector2.bin > temp.bin
dd if=temp.bin of=os.img bs=512 count=2880 conv=sync
qemu-system-x86_64 -drive file=os.img,format=raw,if=floppy -boot a
