[PS64] 1. 운영체제 제작 프로젝트, psboot 분석하기 (1)

Getting Deeper/Operating System | 2018.01.15 23:23 | P.노우렛지

사실 Enigma-Pi 이전에 운영체제를 만들겠다고 까불던(?) 것이 있었기에 이것을 더욱 중점적으로 탐구해보고자 한다.


훌륭한 개발자가 되려면 필요한 것들이 여러가지가 있겠지만, 그중에서 제일 필요한 지식은 운영체제가 아닐까 싶다. 하드웨어(컴퓨터)와 사용자 사이에서 중개인의 역할을 충실히 수행하는 운영체제는 소프트웨어의 작동에 매우 깊이 관여하고 있으므로, 운영체제를 이해한다는 것은 곧 컴퓨터 시스템이 어떻게 작동하는 가를 이해한다는 말과 같다고 볼 수 있지 않을까.


나는 Philosopher's Stone(이하 PS64)이라는 이름으로 운영체제를 만들어 보려고 하고 있다. 비록 책을 보며 예제를 하나하나 따라하는 수준이지만, 곧 책에서 제시한 틀에서 벗어나 POSIX 규격을 만족하는 운영체제를 하나 만들어보는 것이 최종 목표이다.


이번 포스팅에서는 PS64의 부트로더, 즉 psboot을 분석해볼까 한다. psboot은 저번달에 완성해놓은 것이다. 책의 예제를 참고해서 일부 변형하였으나, 운영체제 이미지를 불러오는 부분은 책에서 제시한 예제와 완벽하게 같다. psboot 외에도 모든 어셈블리 소스코드는 nasm을 이용하여 어셈블 되고, VirtualBox 위에서 테스트 되었다.


이제 psboot의 어셈블리 코드를 살펴보도록 하자. 원본


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Filename : psldr.asm                           ;
; Author : P.Knowledge, 2016-                    ;
; Data since : 20171208 15:43, KST               ;
; Purpose : PS64 16-bit Realmode Bootloader      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

[ORG 0x00]
[BITS 16]

시작 지점 0x00, 16비트로 구동되는 어셈블리 코드이다. 

따라서 다음 줄인 jmp 0x07C0:BEGIN 는 메모리 상에서 0x0000 위에 놓이게 된다.

사실 이 부트로더의 원래 이름은 psldr이었다. 하지만 뭔가 ntldr 스러워서 psboot으로 바꾸었지만, Github 에는 아직 psldr.asm이라는 이름으로 남아있다. 나중에 고쳐야지.

 

    
SECTION     .text
            jmp 0x07C0:BEGIN

여기서부터 부트로더가 실행된다. 0x07C0:BEGIN 번지로 점프하라는 이야기이다.

왜 하필이면 0x07C0일까. 대략 알아보니 PS64의 타겟 시스템인 IBM 호환 PC의 리얼모드에서는 다음과 같이 메모리 배치가 이루어 진다고 한다.[각주:1]

이렇듯 시스템 구동에 필요한 정보들을 적절한 위치에 배치하므로, 아무데나 부트로더를 위치시켰다가든 시스템이 맛이 가버리기 때문이다.


따라서 IBM PC 호환 규격에서는 부트로더의 시작 지점을 0x07C0:0000로 정했던 것이다.


더욱 자세한 내용과 이 다음 내용은 psboot 분석하기(2) 에서 다루어 보도록 한다.



남은 부분: 

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Environment Values                             ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

TOTALSECTORCOUNT: dw 1024

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Bootloader Procedure                           ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


BEGIN:      
            mov ah, 2
            mov bh, 1
            mov dh, 1
            mov dl, 8

            int 10h

            ; initialize data segment register with bootloader begin address
            mov ax, 0x07C0
            mov ds, ax
            mov ax, 0xB800
            mov es, ax

            ; create 64Kilobytes stack
            mov ax, 0x0000
            mov ss, ax
            mov sp, 0xFFFE
            mov bp, 0xFFFE

INITSI:     mov  si, 0
CLEARSCR:   mov  BYTE[es:si], 0

            add  si, 2
 
            cmp  si, 80 * 25 * 2
            jl   CLEARSCR

LOADOSIMG:  mov  ax, 0
            ; Drive #
            mov  dl, 0 
            int  0x13
            jc   DISKERR

            ; PS64 starts from 0x1000
            mov  si, 0x1000
            mov  es, si
            mov  bx, 0x0000

            mov  di, word[TOTALSECTORCOUNT]

READ:       cmp  di, 0
            je   LOAD
            sub  di, 0x1

            ; BIOS service 2, read 1 sector
            mov  ax, 0x21
            mov  ch, byte[TRACKNO]
            mov  cl, byte[SECTORNO]
            mov  dh, byte[HEADNO]
            mov  dl, 0x00
            int  0x13
            jc   DISKERR2

            add  si, 0x0020
            mov  es, si

            mov  al, byte[SECTORNO]
            add  al, 0x01
            mov  byte[SECTORNO], al
            cmp  al, 19
            jl   READ

            xor  byte[HEADNO], 0x01
            mov  byte[SECTORNO], 0x01

            cmp  byte[HEADNO], 0x00
            jne  READ

            add  byte[TRACKNO], 0x01
            jmp  READ

            
LOAD:       push DONE
            call TYPESTR

            ; Start PS64
            jmp 0x1000:0x0000

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Functions                                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DISKERR()                        ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DISKERR:    push 0x0C    
            push DISKERRMSG
            push 1
            push 1
            call PRINTSTR
            jmp  $

DISKERR2:   push 0x0C
            push DISKREADERR
            push 0
            push 0
            call PRINTSTR
            jmp  $

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PRINTSTR(color, msg, xpos, ypos) ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

PRINTSTR:   push bp
            mov  bp, sp

            push es
            push si
            push di
            push ax
            push cx
            push dx

            mov  ax, 0xB800
            mov  es, ax

            mov  ax, word[bp + 4]   ; ypos
            mov  si, 160
            mul  si
            mov  di, ax
 
            mov  ax, word[bp + 6]   ; xpos
            mov  si, 2
            mul  si
            add  di, ax
 
            mov  si, word[bp + 8]   ; msg
            mov  al, byte[bp + 10]  ; color            
 
MSGLOOP:    mov  cl, byte[si]
            cmp  cl, 0
            je   CLEAR
 
            cmp  cl, 0x10
            je   LINEFEED
 
            mov  byte[es:di], cl
            mov  byte[es:di+1], al
 
            add  si, 1
            add  di, 2

            jmp  MSGLOOP
 
LINEFEED:   add  si, 1
            add  di, 160
            jmp  MSGLOOP
 
CLEAR:      pop  dx
            pop  cx
            pop  ax
            pop  di
            pop  si
            pop  es
            pop  bp
            ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; TYPESTR(msg)                     ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

TYPESTR:    push bp
            mov  bp, sp

            push es
            push si
            push di
            push ax
            push cx
            push dx

            mov ah, 0x0E

            mov si, DONE
TYPELOOP:   mov cl, byte[si]
            cmp cl, 0
            je ENDTYPE

            mov al, cl
            int 0x10
            add si, 1
            jmp TYPELOOP

ENDTYPE:    pop  dx
            pop  cx
            pop  ax
            pop  di
            pop  si
            pop  es
            pop  bp
            ret

        
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Data                                           ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

DONE:       db 'LOAD DONE, STARTING...', 0x00
DISKERRMSG: db 'DISK INIT ERROR!', 0
DISKREADERR:db 'DISK READ EEROR!', 0

SECTORNO:   db 0x02
HEADNO:     db 0x00
TRACKNO:    db 0x00

NOTATION:   times 510 - ( $ - $$ ) db 0xFF
            dw 0xAA55

참고로 IBM 호환 PC의 부트로더는 항상 마지막 바이트가 0xAA,0x55로 끝난다.

따라서 510 바이트까지 남는 부분을 0xFF로 채우고 남은 2 바이트를 0xAA,0x55 로 채울 필요가 있다.

  1. http://www.tuner.tw/OMEGA%20CD/zsection/MEM__MAP.PDF [본문으로]