부트로더 #2
이제 커널을 로드 해서 커널 실행코드로 넘겨주면 될 것 같다. ㅋ 이 두려운 어셈블리어에서 빨리 벗어나고픈 생각 뿐이다.
이제 파일을 로드 하는 방법에 대해서 검색질 해봐야 겠다. 파일을 읽는 방법은 int 13h 인터럽트를 사용하면 된다고 한다. 근데 사용 방법이 거의 이해하기 어려운 수준이다. 섹터, 트렉, 헤드.. 많이 들어본 용어긴 하지만 그냥 언어적 이해만 할 뿐이지, 실제 데이터 읽고, 쓰는 방법에 대해선 전혀 모른다. 이것 먼저 공부해야 한다. 휴~ 겁나 귀찮고 어렵고 지루하다.
INT 13h 를 사용하여 데이터를 읽는 방법은 다음과 같다.
파라미터
AH | 0x02 읽기 ( 0x03 : 쓰기) |
AL | 읽을 섹터 수. |
CH | 트랙 번호 |
CL | 읽을 섹터 번호 |
DH | 헤드번호 |
DL | 드라이브 번호 |
ES:BX | 읽은 데이터를 저장할 위치 |
결과
CF | 에러시 플래그가 설정됨. |
AH | 리턴 코드 |
AL | 읽은 섹터 갯수 |
그러면 트랙, 섹터, 헤드 가 뭔지를 알아보자.
인터넷에서 검색하다가 찾은 그림이다. 그림을 보니 이해가 된다.
트랙 | 디스크판에서 원형으로 줄 그어 놓은 영역 |
섹터 | 트랙을 부채꼴 모양으로 줄 그어 나눈 구간 |
헤드 | 디스크 윗면과 밑면을 읽는 구분. |
각각 트랙과 헤드는 0 부터 시작하고 섹터는 1부터 시작한다고 한다. 컴퓨터가 부트로드 영역을 찾을 수 있었던 건 0 트랙, 1섹터에 접근해서 그냥 1섹터를 읽기 때문이기 때문이군..뭐 대충 이해는 간다. 근데 왜이리 복잡한 방법으로 구현해 놓은거냐!~~흠..
나의 커널 데이터는 0트랙 2섹터에 저장해 놓고 이걸 읽으면 되겠군. 아싸 드뎌 진도가 조금 나갔다!!!
헉 프로그래밍 하다 보니까 int 13h 에서 쓰는 DL 레지스터 값 드라이브 번호는 어떻게 적어줘야 하냐 ㅡㅡ;;; 문제에 봉착했다. 검색질 해보니 0x00 은 플로피 디스크 , 0x80 부터는 하드디스크 번호라고 하는데… 나의 이미지 파일은 하드 디스크 이므로 0x80 이라고 주어줘도 된다. 근데 만약 플로피 인지 하드디스크 인지 알고 싶으면??? 검색해보자!! 검색 할 단어 조차도 모르겠다 ㅠㅠ 여튼 부트로더 로 검색 해봐야지~~~ 영어에 일본어에 모르는 글자 들 ㅠㅠ 어렵다…어려워
에휴! 컴퓨터가 부트로더 올리고 DX의 DL 의 부팅한 번호를 기록해서 부트로더로 점프한다고 한다. dl 레지스터가 변경되지 않은 한 dl에 부팅 드라이브 번호가 기록된단다.. 검색하느라 겁나 오래 걸렸다. 아무튼 변수 하나 만들어서 이것도 기록해 놔야 겠다.
ORG 0x7C00 BITS 16 cli ; 인터럽트 발생안되게 설정 xor ax,ax ; ax 0으로 초기화 한다. ax xor ax 어차피 0이다. mov ds, ax ; ds 세그먼트 레지스터를 0 으로 초기화 한다. mov es, ax ; es 세그먼트 레지스터를 0 으로 초기화 한다. sti ; 인터럽트 발생 설정 mov [DRIVE_NUM], dl ; 부팅한 드라이브 번호를 DRIVE_NUM 변수에 저장. lea si, [HELLO_MSG] ; HELLO_MSG 위치 주소 call PRINT ; PRINT 함수 호출 call READ_SECTOR ; 커널 이미지를 읽어라 jmp 0x9000 ; 메모리 0x9000으로 이동해라. READ_SECTOR: pusha ; 모든 레지스터를 스택에 넣는다. mov ah, 0x02 ; 디스크 읽기 모드 mov al, 1 ; 1 섹터만 읽는다. mov ch, 0 ; 트랙은 0번 트랙 mov cl, 2 ; 읽을 섹터는 2번째 섹터 mov dh, 0 ; 헤드는 0번 헤드 mov dl, [DRIVE_NUM] ; 읽을 드라이브 번호 mov bx, 0x9000 ; 0x9000 메모리에 커널 이미지 올린다. int 0x13 ; 읽어라! popa ret PRINT: pusha ; 모든 레지스터를 스택에 넣는다. PRINT_LOOP: mov al, [si] ; al에 글자 하나 저장한다. inc si ; si 주소를 증가한다. or al, al ; or 연산. jz PRINT_END ; 문자열 끝이 or 연산이 0 이라면. 종료 mov ah,0x0E ; ah = 0xE int 0x10 ; 인터럽트 0x10h jmp PRINT_LOOP ; 다음 문자 출력 루프 PRINT_END: popa ;모든 레지스터 스택에서 뺀다. ret ;호출 한곳으로 리턴. HELLO_MSG: db "HELLO MY BOOTLOADER", 0x0D,0x0A,0x00 DRIVE_NUM db 0x00 times 510-($-$$) db 0x00 dw 0xAA55 |
자, 부트로더는 이렇게 작성해 놓고…커널 이미지를 만들면 된다. 음.. 동작 되는지 확인 할겸 화면에 문자열을 출력하는 기능만 갖도록 하자. 완전 흥분된다!
ORG 0x9000 ; 0x9000 메모리에서 실행되므로 0x9000 으로 주소정렬 BITS 16 cli ; 인터럽트 발생안되게 설정 xor ax,ax ; ax 0으로 초기화 한다. ax xor ax 어차피 0이다. mov ds, ax ; ds 세그먼트 레지스터를 0 으로 초기화 한다. mov es, ax ; es 세그먼트 레지스터를 0 으로 초기화 한다. sti ; 인터럽트 발생 설정 lea si, [LOAD_MSG] ; LOAD_MSG 위치 주소 call PRINT ; PRINT 함수 호출 jmp $ PRINT: pusha ; 모든 레지스터를 스택에 넣는다. PRINT_LOOP: mov al, [si] ; al에 글자 하나 저장한다. inc si ; si 주소를 증가한다. or al, al ; or 연산. jz PRINT_END ; 문자열 끝이 or 연산이 0 이라면. 종료 mov ah,0x0E ; ah = 0xE int 0x10 ; 인터럽트 0x10h jmp PRINT_LOOP ; 다음 문자 출력 루프 PRINT_END: popa ;모든 레지스터 스택에서 뺀다. ret ;호출 한곳으로 리턴. LOAD_MSG: db "LOAD MY KERNEL", 0x00 times 510-($-$$) db 0x00 dw 0xAA55 |
자 컴파일..
nasm –fbin –o bootloader bootloader.asm
nasm –fbin –o kernel kernel.asm
깔끔하게 컴파일 된다 ^^ 자~ bootloader 하고 kernel 을 Visual studio에서 헥사뷰어로 로드 하고 … 가상 이미지 파일도 로드 하고 512 바이트씩 복사해서 붙여보자…
자~~ 가상 컴퓨터 ~~~
HELLO MY BOOTLOADER
LOAD MY KERNEL
우하하하!! 된다!!! ㅋㅋㅋ
이거 은근히 가슴이 벅차다.. 이제 커널까지 로드 했으니, 본격적으로 커널 만들어서 띄우면 되겠다 ! ^^ 우선, 부트로더 랑 커널을 이미지 파일에 넣는 거 불편하니, 프로그램 하나 만들어 주고!!~ 가만 커널이 커지면 부트로더 에서 섹터 읽어 오는 부분을 변경해줘야 하네!! 흠…지금은 512바이트만 읽어오는 구조니 커널 먼저 만들고 크기 계산해서 부트로더 섹터 읽어 오는 갯수를 늘려줘야 하는 구조네…음…
그냥 파일 크기 알아서 오는 방법은 없나?? 흠.. 커널 만들어서 그냥 복사 해주는 방식으로.. 흠.. 아~~악, 내가 만든 부트로더랑 커널은 파일 시스템이 전혀 고려 안된 구조다!!!
그냥 부트로더 만든다고 나머지 설렁설렁 봤더니 , ㅠㅠ 파일시스템을 고려 했어야 했는데!!! 다시 파일시스템을 고려한 부트로드를 만들어야 한다. (아놔! 어셈블리어 또 …ㅠㅠ) 아, 지친다.. 하기 싫다.. 파일 시스템은 만들어야 하나, 있던거 써야 하나 .. 리눅스 ext2?? 윈도우 FAT??? 이거 뭘 써야 하나~
'프로그래밍 > OS 만들기' 카테고리의 다른 글
OS 만들기 #6 - MBR (0) | 2013.08.14 |
---|---|
OS 만들기 #5 - 부트로더 (0) | 2013.08.14 |
OS 만들기 #3 - 부트로더 (1) | 2013.08.14 |
OS 만들기 #2 (0) | 2013.08.14 |
OS 만들기 #1 (0) | 2013.08.14 |