[OS]miniOS-Text-2

๐Ÿ‘‰๐Ÿป 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

ScreenShot

๋งˆํƒœ๋ณต์Œ 21์žฅ 22์ ˆ / Matthew 21: 22

“๋„ˆํฌ๊ฐ€ ๊ธฐ๋„ํ•  ๋•Œ์— ๋ฌด์—‡์ด๋“ ์ง€ ๋ฏฟ๊ณ  ๊ตฌํ•˜๋Š” ๊ฒƒ์€ ๋‹ค ๋ฐ›์œผ๋ฆฌ๋ผ ํ•˜์‹œ๋‹ˆ๋ผ”

“And everything you ask in prayer, believing, you will receive.”

Leave a Reply