Post

부트로더 - 7

이전 코드에 이어서 계속해서 코드 분석에 들어가도록 하겠다. 부트 로더 파트는 계속해서 포스팅이 업데이트가 될 예정이니 언제 업데이트가 되었는지는 하단에 업데이트 날짜와 내역에 대해서 기재할 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 화면을 모두 지우고, 속성값을 녹색으로 설정
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov si,    0                    ; SI 레지스터(문자열 원본 인덱스 레지스터)를 초기화
	
.SCREENCLEARLOOP:                   ; 화면을 지우는 루프
    mov byte [ es: si ], 0          ; 비디오 메모리의 문자가 위치하는 어드레스에
                                    ; 0을 복사하여 문자를 삭제
    mov byte [ es: si + 1 ], 0x0A   ; 비디오 메모리의 속성이 위치하는 어드레스에
                                    ; 0x0A(검은 바탕에 밝은 녹색)을 복사


    add si, 2                       ; 문자와 속성을 설정했으므로 다음 위치로 이동

    cmp si, 80 * 25 * 2     ; 화면의 전체 크기는 80 문자 * 25 라인임
                            ; 출력한 문자의 수를 의미하는 SI 레지스터와 비교
    jl .SCREENCLEARLOOP     ; SI 레지스터가 80 * 25 * 2보다 작다면 아직 지우지 
                            ; 못한 영역이 있으므로 .SCREENCLEARLOOP 레이블로 이동

바이오스에서 출력하는 로그를 모두 삭제하는 함수이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 화면 상단에 시작 메시지를 출력
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    push MESSAGE1               ; 출력할 메시지의 어드레스를 스택에 삽입
    push 0                      ; 화면 Y 좌표(0)를 스택에 삽입
    push 0                      ; 화면 X 좌표(0)를 스택에 삽입
    call PRINTMESSAGE           ; PRINTMESSAGE 함수 호출
    add  sp, 6                  ; 삽입한 파라미터 제거
	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; OS 이미지를 로딩한다는 메시지 출력
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    push IMAGELOADINGMESSAGE    ; 출력할 메시지의 어드레스를 스택에 삽입           
    push 1                      ; 화면 Y 좌표(1)를 스택에 삽입                     
    push 0                      ; 화면 X 좌표(0)를 스택에 삽입                     
    call PRINTMESSAGE           ; PRINTMESSAGE 함수 호출                           
    add  sp, 6                  ; 삽입한 파라미터 제거                            

MESSAGE1과 IMAGELOADINGMESSAGE는 코드의 가장 하단에 정의되어있으며 메세지를 출력하는 PRINTMESSAGE 라는 함수 역시 좀 더 코드 아래쪽으로 내려가야 등장한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 디스크에서 OS 이미지를 로딩
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 디스크를 읽기 전에 먼저 리셋
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
RESETDISK:                          ; 디스크를 리셋하는 코드의 시작
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; BIOS Reset Function 호출
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 서비스 번호 0, 드라이브 번호(0=Floppy)
	mov	ax, 0
	mov	dl, 0              
	int	0x13     
	; 에러가 발생하면 에러 처리로 이동
	jc	HANDLEDISKERROR
        
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 디스크에서 섹터를 읽음
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; 디스크의 내용을 메모리로 복사할 어드레스(ES:BX)를 0x10000으로 설정
    mov si, 0x1000                  ; OS 이미지를 복사할 어드레스(0x10000)를 
                                    ; 세그먼트 레지스터 값으로 변환
    mov es, si                      ; ES 세그먼트 레지스터에 값 설정
    mov bx, 0x0000                  ; BX 레지스터에 0x0000을 설정하여 복사할 
                                    ; 어드레스를 0x1000:0000(0x10000)으로 최종 설정

    mov di, word [ TOTALSECTORCOUNT ] ; 복사할 OS 이미지의 섹터 수를 DI 레지스터에 설정
        
READDATA:                           ; 디스크를 읽는 코드의 시작
	; 모든 섹터를 다 읽었는지 확인
    cmp di, 0               ; 복사할 OS 이미지의 섹터 수를 0과 비교
    je  READEND             ; 복사할 섹터 수가 0이라면 다 복사 했으므로 READEND로 이동
    sub di, 0x1             ; 복사할 섹터 수를 1 감소

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; BIOS Read Function 호출
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    mov ah, 0x02                        ; BIOS 서비스 번호 2(Read Sector)
    mov al, 0x1                         ; 읽을 섹터 수는 1
    mov ch, byte [ TRACKNUMBER ]        ; 읽을 트랙 번호 설정
    mov cl, byte [ SECTORNUMBER ]       ; 읽을 섹터 번호 설정
    mov dh, byte [ HEADNUMBER ]         ; 읽을 헤드 번호 설정
    mov dl, 0x00                        ; 읽을 드라이브 번호(0=Floppy) 설정
    int 0x13                            ; 인터럽트 서비스 수행
    jc HANDLEDISKERROR                  ; 에러가 발생했다면 HANDLEDISKERROR로 이동
	
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; 복사할 어드레스와 트랙, 헤드, 섹터 어드레스 계산
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    add si, 0x0020      ; 512(0x200)바이트만큼 읽었으므로, 이를 세그먼트 레지스터
                        ; 값으로 변환
    mov es, si          ; ES 세그먼트 레지스터에 더해서 어드레스를 한 섹터 만큼 증가
	
	; 한 섹터를 읽었으므로 섹터 번호를 증가시키고 마지막 섹터(18)까지 읽었는지 판단
	; 마지막 섹터가 아니면 섹터 읽기로 이동해서 다시 섹터 읽기 수행
    mov al, byte [ SECTORNUMBER ]       ; 섹터 번호를 AL 레지스터에 설정
    add al, 0x01                        ; 섹터 번호를 1 증가
    mov byte [ SECTORNUMBER ], al       ; 증가시킨 섹터 번호를 SECTORNUMBER에 다시 설정
    cmp al, 19                          ; 증가시킨 섹터 번호를 19와 비교
    jl READDATA                         ; 섹터 번호가 19 미만이라면 READDATA로 이동
    
    ; 마지막 섹터까지 읽었으면(섹터 번호가 19이면) 헤드를 토글(0->1, 1->0)하고, 
    ; 섹터 번호를 1로 설정
    xor byte [ HEADNUMBER ], 0x01       ; 헤드 번호를 0x01과 XOR하여 토글(0->1, 1->1)
    mov byte [ SECTORNUMBER ], 0x01     ; 섹터 번호를 다시 1로 설정
	
	; 만약 헤드가 1->0로 바뀌었으면 양쪽 헤드를 모두 읽은 것이므로 아래로 이동하여
	; 트랙 번호를 1 증가
    cmp byte [ HEADNUMBER ], 0x00       ; 헤드 번호를 0x00과 비교
    jne READDATA                        ; 헤드 번호가 0이 아니면 READDATA로 이동
	
	; 트랙을 1 증가시킨 후, 다시 섹터 읽기로 이동
    add byte [ TRACKNUMBER ], 0x01      ; 트랙 번호를 1 증가
    jmp READDATA                        ; READDATA로 이동
READEND:

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; OS 이미지가 완료되었다는 메시지를 출력
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    push LOADINGCOMPLETEMESSAGE     ; 출력할 메시지의 어드레스를 스택에 삽입
    push 1                          ; 화면 Y 좌표(1)를 스택에 삽입
    push 20                         ; 화면 X 좌표(20)를 스택에 삽입
    call PRINTMESSAGE               ; PRINTMESSAGE 함수 호출
    add  sp, 6                      ; 삽입한 파라미터 제거

	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	; 로딩한 가상 OS 이미지 실행	
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	jmp	0x1000:0x0000
	

DISK의 데이터를 읽는 코드이다. FLOPPY DISK를 기준으로 짜여있으며 1개씩 데이터를 읽어서 옮기되 총 섹터의 개수만큼 loop를 도는 식으로 전체 데이터를 읽어온다. 원래는 한번에 여러개의 섹터를 읽을 수 있는 코드가 있지만, 책에서 QEMU라는 가상 머신에서 해당 코드를 구동할수가 없어서 이런식으로 구현했다고 했다. 하지만 Vmware에서는 그런 일이 없으므로 코드를 바꿔서 구동할 수 있다. 만약에 해당 코드를 플로피 디스크가 아닌 HDD에서 구동하고 싶다면 코드를 바꿔야한다.

이렇게 읽어온 데이터를 메모리에 쓴 뒤 해당 메모리 번지로 점프한다. 여기서는 0x10000 번지부터 해당 섹터의 크기만큼 옮긴뒤 0X10000번지로 점프하여 해당 코드를 구동시킨다.

참고 문헌

  1. 64Bit 멀티코어 OS의 구조 - 한승훈 저
  2. 한빛출판사 - 64Bit 멀티코어 OS의 구조 첨부 부트로더 코드

업데이트

20240210 : 내용 첨가

This post is licensed under CC BY 4.0 by the author.