부트로더 - 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번지로 점프하여 해당 코드를 구동시킨다.
참고 문헌
- 64Bit 멀티코어 OS의 구조 - 한승훈 저
- 한빛출판사 - 64Bit 멀티코어 OS의 구조 첨부 부트로더 코드
업데이트
20240210 : 내용 첨가
This post is licensed under CC BY 4.0 by the author.