-
[OS] 05. Boot Sector FunctionOS/OS from Scratch 2021. 6. 20. 16:25
서론
이번 강의에서는 가장 기본적인 Assembly에서 함수와 같은 역할을 하는 코드를 만들어 보겠습니다. 해당 Github 강의는 다음과 같습니다.
https://github.com/cfenollosa/os-tutorial/tree/master/05-bootsector-functions-strings
cfenollosa/os-tutorial
How to create an OS from scratch. Contribute to cfenollosa/os-tutorial development by creating an account on GitHub.
github.com
이론
Assembly pusha, popa
pusha는 모든 레지스터에 저장된 값을 Stack에 저장, popa는 Stack에 저장됐던 모든 값을 원래 레지스터 값에 옮겨 원래 상태로 돌려놓는 기능을 합니다. 이와 같은 기능은 현재 상태에서 다른 코드의 기능을 사용하고 싶을 때 활용됩니다.
Assembly 문자열
Assembly에서 문자열은 다음과 같이 사용됩니다.
HELLO: db 'Hello World',0x00
이때 문자열의 끝에는 0x00 Null 문자를 삽입해야 합니다. Assembly에서 newline은 0x0a (Newline) + 0x0d (Carriage Return)으로 이루어집니다. 따라서 이를 출력하기 위한 코드는 다음과 같이 됩니다.
mov ah, 0x0e ; newline 문자 mov al, 0x0a int 0x10 ; carriage return 문자 mov al, 0x0d int 0x10
Assembly에서 다른 파일 사용
Assembly 코드에서 다른 파일의 코드를 실행시키기 위해서는 다음 코드를 작성합니다.
%include “파일명.asm”
이때 코드에 포함되어 있는 Label의 위치를 주의하며 파일을 불러와야 합니다.
Assembly ror
0x1234을 ror 연산하면 값은 0x4123로 변환되고 ror 연산을 반복하면 0x3412, 0x2341, 0x1234로 됩니다.
코드
boot_sect_print_function.asm
문자열을 출력하는 기능, Newline을 출력하는 기능으로 구성되어 있습니다. 먼저 caller에서 print label을 불렀을 때 기존의 상태를 유지하기 위해 상태를 pusha로 저장해 놓습니다. caller 에서 bx에 문자열을 저장해서 넘겨주므로 bx을 시작으로 문자열의 끝인 Null 문자를 만날 때까지 al레지스터에 문자를 옮기고 Interrupt을 발생시키면 됩니다. 이 작업을 반복하는 것이 start label입니다. Null 문자를 만나면 al레지스터에 0x00이 옮겨지므로 done label으로 jump 하고 caller에서 불렀을 때의 상태로 popa 하고 return 합니다.
; 문자열 출력 print: pusha start: ; bx가 문자열 시작 주소 mov al, [bx] cmp al, 0 je done mov ah, 0x0e int 0x10 add bx, 1 jmp start done: popa ret ; 줄 바꾸기 print_nl: pusha mov ah, 0x0e ; newline 문자 mov al, 0x0a int 0x10 ; carriage return 문자 mov al, 0x0d int 0x10 popa ret
boot_sect_print_function_hex.asm
16진수를 출력하는 기능을 가지고 있습니다. print_hex의 경우 caller에서 dx레지스터에 출력할 값을 넘겨줍니다. 이때 16진수의 마지막 문자를 가지고 와서 숫자일 경우 ASCII Code 값을 맞춰주기 위해 0x30만큼 더해주고, 알파벳인 경우 0x30을 더해준 후 7을 또다시 더해줍니다. 이는 9의 ASCII 값이 0x39이고 'A'의 값은 0x41이기 때문입니다. 출력할 문자열을 bx레지스터에 저장하고, 현재 문자열을 끝에서부터 처리하므로 해당 인덱스를 계산하고 계산된 위치에 값을 넘깁니다. 그런 다음 ror 연산을 통해 문자열을 한 칸씩 당기고 위 과정을 반복합니다.
print_hex: pusha ; cx loop의 인덱스 mov cx, 0 hex_loop: cmp cx, 4 je end ; dx에 입력 받음 mov ax, dx ; 마지막 문자만 남김 and ax, 0x000f ; 0의 ASCII 값은 0x30, 0x30 더해야 함 add al, 0x30 ; 'A'의 ASCII 값은 0x41 cmp al, 0x39 jle step2 ; 9 이상의 값보다 크면 7더해야 함 add al, 7 step2: ; 뒤에서 부터 채워나가기 때문 mov bx, HEX_OUT+5 ; 문자의 인덱스 계산 sub bx, cx ; bx가 가리키는 곳에 al에 저장된 문자 넘김 mov [bx], al ; ex 0x1234 -> 0x4123 (한칸씩 순환) ror dx, 4 add cx, 1 jmp hex_loop end ; boot_sect_print_function의 print함수 사용 mov bx, HEX_OUT call print popa ret HEX_OUT: db '0x0000', 0
boot_sect_main.asm
main역할을 하는 코드입니다.
[org 0x7c00] mov bx, HELLO call print call print_nl mov bx, BYE call print call print_nl mov dx, 0x12fe call print_hex call print_nl jmp $ %include "boot_sect_print_function.asm" %include "boot_sect_print_function_hex.asm" HELLO: db 'Hello World',0x00 BYE: bye db 'Good bye world',0x00 times 510-($-$$) db 0 dw 0xaa55
실행 결과
위 코드를 실행 bin파일로 컴파일하고, qemu로 실행시키면 다음과 같은 화면이 나옵니다. 저장된 문자열 'Hello World', 'Good bye world'와 0x12FE의 16진수 표기가 출력되는 것을 확인할 수 있습니다.
실행 결과 'OS > OS from Scratch' 카테고리의 다른 글
[OS] 07. Boot Sector Disk (0) 2021.06.20 [OS] 06. Boot Sector Segmentation (0) 2021.06.20 [OS] 04. Boot Sector Stack (0) 2021.06.20 [OS] 03. Boot Sector Memory (0) 2021.06.20 [OS] 02. Boot Sector Print (0) 2021.06.20