๐๐ป miniOS-Text-2์์๋ ํค๋ณด๋์ ํ์ดํ ํ๋ ๊ธ์๋ฅผ ํฐ๋ฏธ๋์ ํํํฉ๋๋ค.
In miniOS-Text-2, the characters typed on the keyboard are displayed in the terminal.
๐๐ป ๊ธฐ์กด์ ์ด์
๋ธ๋ฌ์ฝ๋๋ ๋์ผํ๊ณ kernel.c์ฝ๋๋ง ์์ ๋์์ต๋๋ค.
The existing assembler code remains the same; only the kernel.c code has been modified.
๐ kernel.c
โ๏ธ ๋ฐ์ดํฐ ํ์ ์ ์ / Data Type Definition
typedef unsigned char byte;
typedef unsigned short word;
1)byte
-1 ๋ฐ์ดํธ ํฌ๊ธฐ์ ๋ฐ์ดํฐ ํ์
(unsigned char)์ผ๋ก ์ ์
Defined as a 1-byte-sized data type (unsigned char)
-ํ๋์จ์ด์์ ์ฝ์ด์ค๋ ๋ฐ์ดํฐ๋ ํฌํธ ์ํ๊ฐ์ ๋ด์ ๋ ์ฌ์ฉํจ.
Used for storing data read from hardware or the status values โโof ports.
2)word
-2๋ฐ์ดํธ(16๋นํธ) ํฌ๊ธฐ์ ๋ฐ์ดํฐ ํ์
(unsigned short)์ผ๋ก ์ ์
Defined as a 2-byte (16-bit) data type (unsigned short)
-x86 ์ํคํ
์ฒ์ I/O ํฌํธ ์ฃผ์ ๋ฒ์(0 ~ 65535)๋ฅผ ์์ ํ๊ฒ ๋ด๊ธฐ ์ํด ์ฌ์ฉํจ.
Used to safely accommodate the I/O port address range (0โ65535) of the x86 architecture.
โ๏ธ I/O ํฌํธ์์ 1๋ฐ์ดํธ๋ฅผ ์ฝ์ด์ค๋ ํจ์
Read 1 byte from I/O port
static inline byte inb(word port) {
byte ret;
__asm__ __volatile__("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret;
}
1)ํจ์์ static ํค์๋๊ฐ ๋ถ์ด ์์ผ๋ฉด ์ด ํ์ผ ๋ด๋ถ์ฉ์ผ๋ก ์ฌ์ฉ๋๋ ํจ์๋ผ๋ ์๋ฏธ์
๋๋ค. ๊ทธ๋์ ๋ค๋ฅธ ํ์ผ์์๋ static ํค์๋๊ฐ ๋ถ์ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์์.
When a function is marked with the static keyword, it means the function is intended for use only within that file. Consequently, such a function cannot be accessed from other files.
์๋ฐ๋ ์ฝํ๋ฆฐ ๊ฐ์ ๊ฒฝ์ฐ staticํค์๋๋ ํด๋์ค์ ๋ฌ๋ฆฌ ์ปดํ์ผ ์์ ์ ํจ์๋ฅผ ์คํ์์ผ์ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ๋ ๋ฐฉ์์ ์๋ฏธํจ.
In languages โโlike Java or Kotlin, the static keyword signifies that a function is executed and loaded into memory at compile time, rather than being associated with an instance of a class.
ํด๋์ค๋ ์ธ์คํด์ค๊ฐ ์์ฑ๋ ๋ ๋ฉ๋ชจ๋ฆฌ์ ํจ์๋ฅผ ์คํ๋ฐฉ์.
A class functions by executing its methods in memory when an instance is created.
๊ฐ์ static ํค์๋๋ฅผ ์ฌ์ฉํด๋ c์ธ์ด์ static์ ๊ฐ๋
๊ณผ ์๋ฐ,์ฝํ๋ฆฐ์์์ static์ ๊ฐ๋
์ ๋ค๋ฆ.
Although the same static keyword is used, the concept of static in C differs from its concept in Java or Kotlin.
2)ํจ์์ inline ํค์๋๊ฐ ๋ถ์ด ์์ผ๋ฉด, ๋ค๋ฅธ ๊ณณ์์ ์ด ํจ์๋ฅผ ํธ์ถํ ๋ ์ ํ(call)ํ์ง ๋ง๊ณ , ์ด ํจ์์ ๋ด๋ถ ์ฝ๋๋ฅผ ํธ์ถํ ๊ทธ ์๋ฆฌ์ ๊ทธ๋๋ก ๋ณต์ฌํด์ ๋ถ์ฌ๋ฃ์ผ๋ผ๋ ์๋ฏธ๋ก ์ฑ๋ฅ ์์ค์ ์์ ๋ ์ต์ ํ ๊ธฐ๋ฒ์
๋๋ค.
When a function is marked with the inline keyword, it serves as an optimization technique that eliminates performance overhead: instead of performing a jump (a function call) when the function is invoked elsewhere, the code inside the function is copied and pasted directly into the location where it is called.
1.์ผ๋ฐ์ ํ ๋ฐฉ์์ ์ฝ๋
// ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ 0x1000 ๋ฒ์ง / memory address 0x1000
void say_hello() {
print("์๋
ํ์ธ์/Hello!");
}
// ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ 0x2000 ๋ฒ์ง (main ํจ์) / Memory address 0x2000 (main function)
int main() {
// ์ฌ๊ธฐ์ CPU๋ 0x1000๋ฒ์ง๋ก '์ ํ(call)'ํ๋ค๊ฐ ๋์์ต๋๋ค.
// Here, the CPU 'jumps' (calls) to address 0x1000 and then returns.
say_hello();
print("๋/End!");
}
2.inline ํจ์/function
// ์ปดํ์ผ๋ฌ๊ฐ ์ง์ say_hello() ๋ด๋ถ์ ์๋งน์ด๋ฅผ ๋ณต์ฌํด์ main ํจ์ ์์ ๊ทธ ์๋ฆฌ์ ๊ทธ๋๋ก ๋ผ์ ๋ฃ์ต๋๋ค.
// The compiler directly copies the contents of say_hello() and inserts them right into that spot within the main function.
// ์ปดํ์ผ ํ ์ค์ ๋ฉ๋ชจ๋ฆฌ์ ๋ง๋ค์ด์ง๋ ๋ชจ์ (์ ํ๊ฐ ์ฌ๋ผ์ง)
// How it appears in actual memory after compilation (jumps are removed)
int main() {
// say_hello()์ ๋ด์ฉ์ด ๊ทธ ์๋ฆฌ์ ํต์งธ๋ก ๋ฐํ
// The contents of say_hello() are inserted in their entirety at that spot.
print("์๋
ํ์ธ์/Hello!");
print("๋/End!");
}
3)__asm__ ์ C์ฝ๋๊ฐ ์๋ ์ด์
๋ธ๋ฌ๋ฅผ ์คํํ๋ค๋ ์๋ฏธํจ.__asm__ indicates that the code following it is assembler, not C code.
4)__volatile__์ C์ธ์ด์ volatile ํค์๋์ ๊ฐ์ ๋งฅ๋ฝ์ผ๋ก C์ธ์ด๋ ๋๊ฐ์ ์ฝ๋๊ฐ ๋ฐ๋ณต๋๋ฉด ์ด์ ๊ฒฐ๊ณผ๊ฐ์ ์ฌ์ฌ์ฉํ๊ฑฐ๋ ์ฝ๋ ์์๋ฅผ ๋ฐ๊ฟ ์ ์์.__volatile__ follows the same logic as the volatile keyword in the C language; in C, when the same code is repeated, the compiler may reuse a previously calculated result or reorder the code.
ํ๋์จ์ด ํฌํธ(0x60 ๋ฑ)๋ ์ฐ๋ฆฌ๊ฐ ์ฝ์ ๋๋ง๋ค ์ํ๊ฐ ์ค์๊ฐ์ผ๋ก ๋ณํ ์ ์์ด์ ์ฝ๋ ์ฌ์ฌ์ฉ์ด๋ ์ฝ๋์์ ๋ฐ๊พธ๊ธฐ๋ฅผ ๊ธ์งํ๋ ํค์๋
Hardware ports (such as 0x60) are locations where the state can change in real time whenever they are read; therefore, a specific keyword is used to prohibit code reuse or the reordering of code operations.
5)(“inb %1, %0″ : “=a”(ret) : “Nd”(port))
A.”inb %1, %0″%1๋ฒ ํฌํธ์์ 1๋ฐ์ดํธ๋ฅผ ์ฝ์ด์ %0์ ์ง์ด๋ฃ๊ธฐ (inb = Input Byte).
Read 1 byte from port 1 and store it in %0
B. : “=a”(ret)
์ถ๋ ฅ๋ถ๋ก inb ๋ช
๋ น์ด๊ฐ ๋๋ ๋ค ํ๋์จ์ด ๊ท์น์ ๋ฐ๋ผ AL(๋๋ EAX) ๋ ์ง์คํฐ์ ๋ด๊ธด ๊ฒฐ๊ณผ๊ฐ์ C์ธ์ด ๋ณ์ ret์ ์ง์ด๋ฃ๊ธฐ(=a๋ ๋ ์ง์คํฐ ์ง์ ์ฝ๋)
After the inb instruction completes, the resultโstored in the AL (or EAX) register according to hardware conventionsโis assigned to the C variable ret (where =a is a code specifying the register to be used).
C. : “Nd”(port)
์
๋ ฅ๋ถ๋ก C์ธ์ด ๋ณ์ port์ ๋ค์ด ์๋ ํฌํธ ๋ฒํธ(์: 0x60)๋ฅผ ์ด์
๋ธ๋ฆฌ ๋ช
๋ น์ด๊ฐ ์ธ ์ ์๋๋ก ์ ์ ํ ๋ ์ง์คํฐ๋ ์์๋ก ์ ๋ฌํ๊ธฐ
Pass the port number (e.g., 0x60) contained in the C variable port to an appropriate register or constant so that it can be used by assembly instructions.
D. ๊ฒฐ๊ตญ ์์ ์ฝ๋๋ ์ด์
๋ธ๋ฌ inb๋ช
๋ น์ด๋ก port์์ ๊ฐ์ ์ฝ์ด์์ ret๋ณ์์ ์ ์ฅํ๋ ์ญํ ์ ํจ.
Ultimately, the code above performs the task of reading a value from a port using the assembler inb instruction and storing it in the ret variable.
E.%0,%1์ ์์๋๋ก 0๋ฒ๋ถํฐ ๋ฒํธ๋ฅผ ๋งค๊ธฐ๊ธฐ ์์ํจ.
%0 and %1 are numbered sequentially, starting from 0.
โ๏ธ ํค๋ณด๋์์ ์
๋ ฅ๋ฐ์ ์ค์บ์ฝ๋๋ฅผ ๊ธฐ๋ณธ ASCII ๋ฌธ์๋ก ๋ณํํ๊ธฐ
Converting scan codes received from the keyboard into standard ASCII characters
char scancode_to_ascii(byte scancode) {
// ํค๋ณด๋ Set 1 ์ฃผ์ ํค ๋งคํ (์๋ฌธ ์๋ฌธ์, ์ซ์, ์ผ๋ถ ํน์๋ฌธ์)
// Key mappings for Keyboard Set 1 (lowercase English letters, numbers, and select special characters)
char table[128] = {
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0,
'*', 0, ' '
};
if (scancode < 128) {
return table[scancode];
}
return 0;
}
1)ํค๋ณด๋์์ a๋ฅผ ๋๋ฅธ๊ฒฝ์ฐ
If you press ‘a’ on the keyboard
A.ํค๋ณด๋ ‘a’๋ 0x1E(์ญ์ง์๋ก 30)์
๋๋ค.
The keyboard key ‘a’ is 0x1E (30 in decimal).
B.’a’๋ ๋ฐฐ์ด 30๋ฒ์ ์์ต๋๋ค.
‘a’ is at index 30 of the array.
C.ASCII๋ฌธ์๋ผ๊ณ ํ๋ ๊ท์น์ ์ํ๋ฉด 30๋ฒ์ ‘a’๋ผ๋ ๋ฌธ์์
๋๋ค.
According to the rules known as ASCII, the number 30 corresponds to the character ‘a’.
D.๊ทธ๋์ ๋ฐฐ์ด 30๋ฒ์์ ๋ฌธ์๋ฅผ ์ฐพ์ผ๋ฉด ‘a’๋ผ๋ ๋ฌธ์๋ฅผ ์ฐพ์ ์ ์๊ณ ์ด ๊ฐ์ ๋ฆฌํดํด ์ค๋๋ค.
So, if you look for a character at index 30 of the array, you will find the character ‘a’, and this value is then returned.
2)\b๋ ๋ฐฑ์คํ์ด์ค(Backspace), \t๋ ํญ(Tab), \n์ ์ํฐ(Enter) ํค๋ฅผ ์๋ฏธํฉ๋๋ค.
\b represents the Backspace key, \t represents the Tab key, and \n represents the Enter key.
3)์ค๊ฐ์ 0์ด ๋ค์ด ์๋ ๋ถ๋ถ์ ๋ฌธ์๋ก ์ถ๋ ฅ๋์ง ์๋ ํน์ ํค(์: Shift, Ctrl, Fํค ๋ฑ)์ด๊ฑฐ๋ ์์ง ๋งคํ๋์ง ์์ ์ฝ๋์ฌ์ ๋ฌด์ํ๋๋ก ์ฒ๋ฆฌํ ๊ฒ์
๋๋ค.
Entries containing a 0 have been ignored because they either represent special keys that do not produce printable characters (such as Shift, Ctrl, or F-keys) or correspond to codes that have not yet been mapped.
4)ํค๋ณด๋์์ ํค๋ฅผ ๋๋ฅผ ๋ ๋ฐ์ํ๋ ์ค์บ์ฝ๋๋ 0๋ถํฐ 127 ์ฌ์ด์ ๊ฐ์
๋๋ค. (ํค๋ฅผ ๋ ๋๋ ์ด๋ณด๋ค 128์ด ๋ ํฐ ๊ฐ์ด ๋ค์ด์ต๋๋ค.)
The scan code generated when a key is pressed on a keyboard is a value between 0 and 127. (When the key is released, a value 128 greater than this is received.)
5)์
๋ ฅ๋ฐ์ ์ค์บ์ฝ๋๊ฐ 128๋ณด๋ค ์๋ค๋ฉด, ๋ฐฐ์ด์์ ๊ณง๋ฐ๋ก ๋ฌธ์๋ฅผ ์ฐพ์ ๋ฐํ(table[scancode])ํฉ๋๋ค.
If the input scancode is less than 128, the corresponding character is retrieved directly from the array and returned (table[scancode]).
6)๋ฒ์๋ฅผ ๋ฒ์ด๋๊ฑฐ๋ ์๋ชป๋ ๊ฐ์ด๋ฉด 0(Null)์ ๋ฐํํ์ฌ ๋ฌธ์๋ก ์ฒ๋ฆฌํ์ง ์์ต๋๋ค.
If a value is out of range or incorrect, it returns 0 (Null) and is not treated as a character.
โ๏ธ ํค๋ณด๋ ์ ๋ ฅ ๊ฐ์ง / Detect keyboard input
while (1) {
byte status = inb(0x64);
... ...
}
1)๋ฌดํ๋ฃจํ๋ก ํค๋ณด๋ ์
๋ ฅ(0x64๋ฒ ํฌํธ)์ ๊ฐ์ง
Detect keyboard input (port 0x64) in an infinite loop
2)0x64๋ฒ ํฌํธ์ ๊ฐ(status)์ 1๋ฐ์ดํธ ์ฝ์ด์์ status๋ณ์์ ์ ์ฅ
Read a 1-byte value (status) from port 0x64 and store it in the variable ‘status’.
โ๏ธ ์ฝ์ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ์ธํ๊ธฐ
Check if there is data to read
1)0x64๋ฒ ํค๋ณด๋ ์ํ ํฌํธ์์ ์ฝ์ด์จ ๊ฐ์ ๊ฐ์ฅ ์ฐ์ธก ๊ฐ์ด 1๋ฒ์ธ์ง ํ์ธ
Check whether the rightmost bit of the value read from the keyboard status port (0x64) is 1.
if (status & 0x01) { ... }
1)&์ ๋นํธ ์ฐ์ฐ์ &&๋ ๋
ผ๋ฆฌ์ฐ์ฐ์
& is a bitwise operator, while && is a logical operator.
2)status์ ๊ฐ์ด 0x25 (์ด์ง์๋ก 0010 0101)์ธ๊ฒฝ์ฐ ๋นํธ ์ฐ์ฐํ๋ฉด 1์ด๋จ(AND์ฐ์ฐ์ด๋ฏ๋ก ๋ ๊ฐ๋ชจ๋ 1์ธ ๊ฒฝ์ฐ๋ง 1์ด๋จ.)
If the value of status is 0x25 (0010 0101 in binary), performing a bitwise operation results in 1 (since it is an AND operation, the result is 1 only if both values โโare 1).
0010 0101 & 0000 0001 = 0000 0001
4)๊ฐ์ฅ ์ฐ์ธก ๊ฐ์ด 1์ธ๊ฑด ์ถ๋ ฅ ๋ฒํผ๊ฐ ๊ฝ ์ฐผ๋์ง ์๋ ค์ฃผ๋ OBF ๋นํธ๋ฅผ ํ์ธํ๋ ์ฝ๋
The value 1 in the rightmost position is for checking the OBF bit, which indicates whether the output buffer is full.
5)์ฆ CPU์์ ๊ฐ์ ธ๊ฐ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ ์๋ฏธ
In other words, it means there is data for the CPU to retrieve.
6)ํค๋ณด๋๋ก ํ์ดํํ๋ ์๊ฐ ์ถ๋ ฅ๋ฒํผ์ ์ค์บ์ฝ๋๊ฐ ์ฑ์์ง.
The moment you type on the keyboard, the output buffer is filled with scan codes.
โ๏ธ ๋ฐ์ดํฐ ์ฝ์ด์ค๊ธฐ / Reading data
byte scancode = inb(0x60);
1)๋ฐ์ดํฐ ํฌํธ(0x60)์์ ์ค์บ์ฝ๋๋ฅผ ์ฝ์ด์ค๊ธฐ
Read a scan code from the data port (0x60)
2)์ด๋ ์ถ๋ ฅ๋ฒํผ๊ฐ ๋น์์ง๊ณ ์ํ ํฌํธ(0x64)์ OBF(Output Buffer Full) ๋นํธ๋ ๋ค์ 0์ด๋จ.
At this point, the output buffer is cleared, and the OBF (Output Buffer Full) bit in the status port (0x64) is also reset to 0.
โ๏ธ ํ๋ฉด์ ๊ธ์ ์ถ๋ ฅ / Display text on the screen
if (!(scancode & 0x80)) {
char c = scancode_to_ascii(scancode);
if (c != 0) {
// ํ๋ฉด์ ๊ธ์ ์ถ๋ ฅ / Display text on the screen
vram[cursor * 2] = c;
vram[cursor * 2 + 1] = 0x0F; // ํฐ์ ๊ธ์ / White text
cursor++;
}
}
1)!(scancode & 0x80)
์ง๊ธ ๋ค์ด์จ ์ค์บ์ฝ๋๊ฐ ์์ ๋ผ์ ๋ฐ์ํ ์ ํธ๊ฐ ์๋๋ผ๋ฉด(์ฆ, ๋๋ฅธ ์ ํธ๊ฐ ๋ง๋ค๋ฉด) ๋ค์ ๋ก์ง์ ์คํ
If the scan code received is not a signal generated by releasing the hand (i.e., if it is a pressed signal), execute the next logic.
ํค๋ฅผ ๋๋ฅผ๋์ ์์๋๋ผ ๋ค๋ฅธ ์ค์บ์ฝ๋๋ฅผ ๋ณด๋
Different scan codes are sent when a key is pressed and when it is released.
0x80์ ์ด์ง์๋ก 1000 0000 ์ต์์๋นํธ(๊ฐ์ฅ ์ข์ธก ๊ฐ)๋ง 1 ๋๋จธ์ง๋ ๋ชจ๋ 0
0x80 is 1000 0000 in binary, where only the most significant bit (the leftmost bit) is 1, and all other bits are 0.
์๋๋ ‘a'(0x1E)ํค๋ฅผ ๋๋ฅผ ๋์ ๊ณผ์
Below is the process when pressing the ‘a’ (0x1E) key
A.ํค๋ฅผ ๋๋ฅผ ๋ / When pressing a key
0001 1110 (0x1E, a ํค ๋๋ฅผ ๋ ์ค์บ์ฝ๋ / Scan code when the 'a' key is pressed)
& 1000 0000 (0x80, ํํฐ / filter)
------------
0000 0000 (๊ฒฐ๊ณผ / result: 0)
a)(scancode & 0x80) ์ด ๊ฐ์ด 0์ฆ false๊ฐ ๋จ.
The value of (scancode & 0x80) becomes 0, which is false.
b)์ค์ ์ฝ๋์์๋ !(scancode & 0x80) ์ด๋ฏ๋ก true๊ฐ ๋จ.
In the actual code, it evaluates to true because of !(scancode & 0x80).
B.ํค์์ ์์ ๋ผ ๋
When you take your hand off the key
1001 1110 (0x9E, a ํค ๋ ๋ ์ค์บ์ฝ๋ / Scan code when the 'a' key is released)
& 1000 0000 (0x80, ํํฐ / filter)
------------
1000 0000 (๊ฒฐ๊ณผ/result : 0x80, ์ฆ 0์ด ์๋ ๊ฐ / In other words, a non-zero value)
a) ๊ฒฐ๊ณผ๊ฐ 0์ด ์๋๊ฐ์ ๋ชจ๋ true๊ฐ ๋จ.
Any result with a non-zero value is considered true.
b)์ค์ ์ฝ๋์์๋ !(scancode & 0x80) ์ด๋ฏ๋ก false๊ฐ ๋จ.
In the actual code, !(scancode & 0x80) evaluates to false.
2)ํค๋ณด๋ ์
๋ ฅ๊ฐ๊ณผ ํํฐ๊ฐ(0x80,)์ AND์ฐ์ฐ ๊ฒฐ๊ณผ ๊ฐ์ผ๋ก true,false๋ฅผ ๊ฒฐ์
Determine true or false based on the result of an AND operation between the keyboard input value and the filter value (0x80).
3)์์ ๊ฒฐ๊ณผ ๊ฐ์ผ๋ก ํค๋ณด๋๋ฅผ ๋๋ฅธ๊ฒฝ์ฐ ์ฝ๋ ์คํ,์์ ๋ผ๋ฉด ์ฝ๋๋ฅผ ์คํํ์ง ์์
Based on the results above, the code executes when a key is pressed, and does not execute when the key is released.
4)ํค๋ณด๋์ ํค๋ฅผ ๋๋ฅธ๊ฒฝ์ฐ ํ๋ฉด์ ๋ฌธ์ ์ถ๋ ฅ
Characters are displayed on the screen when a keyboard key is pressed.
char c = scancode_to_ascii(scancode);
if (c != 0) {
// ํ๋ฉด์ ๊ธ์ ์ถ๋ ฅ / Display text on the screen
vram[cursor * 2] = c;
vram[cursor * 2 + 1] = 0x0F; // ํฐ์ ๊ธ์ / White text
cursor++;
}
A. ์ง์๋ฐฐ์ด์ ๋ฌธ์์ ASCII ์ฝ๋ ๊ฐ ์
๋ ฅ
Store the ASCII code values โโof characters in the even-indexed positions of the array.
B.ํ์๋ฐฐ์ด์๋ ๊ธ์์ ์์ ๋ฐ ์์ฑ ์ ๋ณด๊ฐ ์
๋ ฅ๋จ
The odd-indexed arrays contain information regarding character color and attributes.
C.0x0F๋ ๋ฐ์ดํธ๋ฅผ ๋ฐ์ผ๋ก ๋๋์ด ์์์ ํํ
0x0F divides the byte in half to represent the color.
์์ 0์ ๋ฐฐ๊ฒฝ์ (๊ฒ์์),๋ค์ F๋ ๊ธ์์ (๋ฐ์ ํฐ์)
The ‘0’ at the beginning represents the background color (black), and the ‘F’ at the end represents the text color (bright white).
โ๏ธ ์ปดํ์ผ / Compile
— ์์คํฌ๋ฆฝํธ๋ก build.shํ์ผ์ ๋ง๋ค์ด์ ์ปดํ์ผํ๋ฉด ๋งค์ฐ ํธํฉ๋๋ค.
Creating a build.sh shell script to handle compilation makes things very convenient.
1)build.sh
#!/bin/bash
# 1. ๊ธฐ์กด ๋น๋ ํ์ผ ๋ฐ ์ด๋ฏธ์ง ์ ๋ฆฌ
# Clean up existing build files and images
echo "[*] Cleaning old build files..."
rm -f *.bin *.o *.img temp.bin sector2.bin
# 2. ๋ถํธ๋ก๋ ์ปดํ์ผ
# Bootloader compilation
echo "[*] Compiling boot.asm..."
nasm -f bin boot.asm -o boot.bin
# 3. ์ด์
๋ธ๋ฆฌ ์คํ
์ปดํ์ผ (ELF32)
# Assembly Stub Compilation (ELF32)
echo "[*] Compiling sector2.asm..."
nasm -f elf32 sector2.asm -o sector2.o
# 4. C ์ปค๋ ์ปดํ์ผ
# C Kernel Compilation
echo "[*] Compiling kernel.c..."
i686-elf-gcc -m32 -c kernel.c -o kernel.o -nostdlib -fno-builtin -fno-stack-protector -fno-pic -fno-PIE
# 5. ๋ง์ปค ์คํ
# Run linker
echo "[*] Linking objects..."
i686-elf-ld -m elf_i386 -Ttext 0x8000 -e start -o sector2.bin sector2.o kernel.o --oformat binary
# 6. ๋ฐ์ด๋๋ฆฌ ๋ณํฉ ๋ฐ ๋์คํฌ ์ด๋ฏธ์ง ์์ฑ
# Binary Merging and Disk Image Creation
echo "[*] Creating floppy disk image..."
cat boot.bin sector2.bin > temp.bin
dd if=temp.bin of=os.img bs=512 count=2880 conv=sync
# 7. QEMU ์คํ
# Running QEMU
echo "[*] Running QEMU..."
qemu-system-x86_64 -drive file=os.img,format=raw,if=floppy -boot a -display cocoa
2)์์คํฌ๋ฆฝํธ ์คํ
chmod +x build.sh
./build.sh
๐ ํ ์คํธ / Test

๋งํ๋ณต์ 21์ฅ 22์ / Matthew 21: 22
“๋ํฌ๊ฐ ๊ธฐ๋ํ ๋์ ๋ฌด์์ด๋ ์ง ๋ฏฟ๊ณ ๊ตฌํ๋ ๊ฒ์ ๋ค ๋ฐ์ผ๋ฆฌ๋ผ ํ์๋๋ผ”
“And everything you ask in prayer, believing, you will receive.”