자!! FAT32 파일 시스템 분석해서 파일 찾아서 읽어 오는 것까지 다 했다.!! 이제 부트로더에서 파일 찾아서 메모리에 로딩 시키는 일만 남았다. 참 험난한 길이었다. 블로그에 글로 적는 것도 험난했고, 공부하는 것도 험난했다. 뭐 이제 코딩만 남았으니~~

부트로더는 또 어떻게 만드냐!~~~ 아~ 험난하다!!

흠… OS 하나 만들기 정말 힘들다.

우선, 부트로더는 NASM 으로 컴파일 하고 나머지 커널은 Visual Studio C++ 2010에서 컴파일 할 생각이다. 부트로더에서 바로 KERNEL.SYS 파일을 읽어 0x10000에 로딩하고 실행시킬 생각이다.

하지만, 안 된단다.. Visual studio 2010에서 커널 작성 컴파일 방법을 검색 하던 중 부트로더 => KERNEL.SYS (Visual Studio C++)로 실행 시킬 수 없다고 한다!!!!!!!!!


왜!!!!!!!!!! 왜 왜! ??? 왜 안되냐고!~~~~~~~~~~~~~~~~~~


OS 개발 안 해!!!!

ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

에고고고고..


실은 부트로더 에서 KERNEL.SYS를 호출 해서 실행 시킬 수는 있단다. 다만 리얼 모드 에서 보호 모드로 전환 하고 실행 해야 한단다. 리얼 모드는 뭐고 보호 모드가 뭐길래? 안 된다는 것이냐??? 뭐 쏼라 쏼라 영어를 보니 16bit , 32bit가 언급된다. 아마 리얼 모드는 16비트이고 보호 모드는 32비트 인 모양이다. Visual Studio C++ 은 컴파일러가 32비트만 컴파일 된다고 한다. 16비트 컴파일 하려면 아~~~주 낮은 고대 유물과 비슷한 버전의 Visual Studio C++를 사용 하라고 한다.

아놔!~~~

OS만들려다 사람 잡겄다!!!

에고.. 보호모드 로 전환하고 KERNEL.SYS 호출하지 모… 근데 보호모드 예제 코드들을 보니..라인수가 장난이 아니다. 부트로더 512byte로는 어림도 없겠다. 아~~~~~ 미치겠다. VC++에서 인라인 어셈으로 처리해서 KERNEL.SYS 엔트리 포인터 위치에 보호모드 코드 작성 후 실제 커널 코드로 들어가는 부분 방법으로 작성해야 겠다.

아~~ 이 방법도 쉽지 않다. 쉽지 않아. 어렵다!!! 어렵다!!! 모르고 시작 할땐 의욕이 넘쳤는데 이젠 점점 하기가 싫어진다.

그냥 세 분류로 작성 하련다… 생각대로 하기에는 지친다..어렵고..

..

부트로더, 커널로더, 커널  이렇게 세 분류로 계획했다.


메모리 영역

구분

파일

역활

0x7C00

부트로더

BOOTLOADER

- 커널로더를 로드 하고 실행한다.

0x8000

커널로더

OS_LDR.SYS

- 커널 로드한다.
- 보호모드로 전환한다.
- 커널을 실행한다.

0x10000

커널

BARAM.SYS - 커널 실제 동작


꺼림직한 구조지만, 그냥 이렇게 타협?을 봤다. 커널로더에서 공부 할게 많지만 우선은 부트로더가 제일 문제다. ㅠㅠ 아직까지 부트로더에서 헤매고 있다니 ㅠㅠ




- FAT32 부트로더


음…기존 코드에서 BPB 영역(FAT32 정보) 부분만 더 붙여 주고 OS_LDR.SYS 파일 찾아서 로드하고 실행 하는 코드를 만들어야 한다. Baram 이미지 파일내의 0x10000 에서 각 값을 구해서 직접 입력했다.


org 0x7C00
bits 16

BOOTLOADER_ENTRY:
	;; start로 이동한다. (BPB의 영역의 3바이트는 점프 명령 코드로 이용한다. )
	jmp BOOTLOADER_MAIN
	nop ; 코드에서 2바이트 밖에 안되서 nop 1바이트 추가.

;; FAT32 파일시스템 구조
BPB_OEM: db "EOS Boot"				; 8 바이트 OEM 이름..
BPB_BYTE_PER_SECTOR:	dw 512		; 2바이트 섹터당 바이트 크기.
BPB_SECTOR_PER_CLUSTER: db 0x02		; 클러스터당 섹터 갯수
BPB_RESERVED_SECTOR:	dw 0x186E   ;  예약 섹터 갯수
BPB_FAT_NUM:			db 0x02		; FAT 갯수 (FAT1, FAT2 의 카피본을 두므로 보통 2개)
BPB_ROOT_ENTRY_NUMBER:	dw 0x0000	; 루트 디렉토리에 최대 디렉토리 포함할 수 있는 갯수
BPB_FAT16_SECTORS:		dw 0x0000	; FAT16일 때 총 섹터 갯수
BPB_MEDIA_TYPE:			db 0xF8		; 장치 타입 (0xF8 는 하드디스크)
BPB_FAT16_SIZE:			dw 0x0000	; FAT16일때 FAT테이블 크기
BPB_SECTOR_PER_TRACK:	dw 0x003F	; 트랙당 섹터 갯수
BPB_HEADS:				dw 0x00FF 	; 헤드 갯수
BPB_HIDDEN_SECTOR:		dd 0x00000080	; 숨겨진 섹터 갯수
BPB_FAT32_SECTORS:		dd 0x0003E800	; FAT32 파일시스템일 때 총 섹터 갯수
BPB_FAT16_SIZE:			dd 0x000003C9	; FAT32일때 FAT테이블 크기
BPB_EXTENSION_FLAGS:	dw 0x0000		; 확장 플래그 ( 0-3 : 활성화 FAT )
BPB_FILESYSTEM_VER:		dw 0x0000		; FAT32의 버젼
BPB_ROOT_ENTRY_CLUSTER: dd 0x00000002	; 데이터 시작 클러스터 위치
BPB_FILESYSTEM_INFO:	dw 0x0001		; 파일시스템 정보 위치
BPB_BACKUP_BOOT_SECTOR: dw 0x0006		; 백업 부트 섹터 위치
BPB_RESERVED:							; 예약된 정보 
						dd 0x00
						dd 0x00
						dd 0x00
BS_DRIVE:							; 바이오스콜(0x13) 드라이브 정보
						db 0x80
BS_RESERVED1:						; 예약
						db 0x00
BS_BOOT_SIGNATURE:					; 부트 서명 (0x29)
						db 0x29		;
BS_VOLUME_ID:						; 볼륨 시리얼 번호
						dd 0xC1C286C
BS_VOLUME_LABEL:					; 볼륨 레이블명
						dd 0x4E4F204E
						dd 0x414D4520
						dw 0x2020
						db 0x20
BS_FILESYSTEM_TYPE:					; 파일 시스템 이름
						dd 0x46415433
						dd 0x32202020

BOOTLOADER_MAIN:
	cli
	;; CS, DS, ES, & SS을 초기화 하자.
	xor eax, eax
	mov ds, ax
	mov ss, ax
	mov es, ax


제길… 탭을 넣어서 보기 좋게 정리 했더니 여기에 붙여 넣기 하니 저리 되네.. 아 모르겠다.. 그냥 넘어가자.

스택 위치를 잡아야 하는데 0x8000에 커널 로더 OS_LDR.SYS를 로딩한다. 그러니까 0x8000으로 스택 주소를 잡는다. 뭐 스택은 아래로 감소하면서 포인터가 위치하니까 문제는 없을 것 같다. 그리고 부트로드 드라이버도 변수 하나 생성해서 저장해 주자!!


mov sp, 0x8000     ; 스택을 잡자
mov bp, sp           ; bp 도 0x8000으로 잡자.
mov [DRIVE], dl    ; 현재 드라이브 저장하자.


음…이제 FAT32 접근해야 하는데…..


1. 루트 디렉토리 엔트리 위치를 구하자.
2. FAT 테이블 위치를 구하자.
3. 클러스터당 몇 바이트 인지 구하자.
4. 현재 클러스터 위치를 저장하자.
5. 루트 디렉토리 엔트리 가서 파일명을 읽어서 OS_LDR.SYS를 찾는다.
6. OS_LDR.SYS의 데이터 클러스터로 이동해서 읽는다.
7. FAT 테이블 위치로 가서 OS_LDR.SYS 데이터가 더 있는지 확인한다.
8. 6,7번을 반복한다.
9. OS_LDR.SYS를 실행한다. (이동)


위와 같이 한번 설계해 봤다. 이 구조로 한번 코딩 해 봐야지~


	; 루트 디렉토리 엔트리 위치를 구하자.
	; 루트 디렉토리 엔트리 = (FAT 갯수 * FAT 테이블크기) + 예약 섹터 + 히든 섹터
	mov al, [BPB_FAT_NUM]	
	mov ebx, [BPB_FAT32_SIZE]
	mul ebx		; AL * EBX = EAX
	xor ebx, ebx ; EBX를 0으로 초기화 하자.
	mov bx, [BPB_RESERVED_SECTOR] ; 예약 섹터를 bx 저장.
	add eax, ebx	; EAX = EAX + EBX
	mov ebx, [BPB_HIDDEN_SECTOR] ; 히든 섹터를 ebx로 저장.
	add eax, ebx	; EAX = EAX + EBX
	mov [ROOT_ENTRY], eax

	; FAT1 위치를 찾자.
	; FAT1 = 예약 섹터 + 히든 섹터
	xor eax, eax	; eax는 0으로 초기화 한다.
	mov ax, [BPB_RESERVED_SECTOR] ; 예약 섹터를 ax 저장.
	add eax, ebx	; ebx 는 이미 히든 섹터가 저장되어 있다.
	mov [FAT1], eax

	; 클러스터를 바이트로 변환하자.
	; 클러스터 총 바이트 = (섹터당 바이트 * 클러스터당 섹터 갯수)
	xor ebx, ebx
	mov ax, [BPB_BYTE_PER_SECTOR]
	mov bl, [BPB_SECTOR_PER_CLUSTER]
	mul bx	; EAX = AX * BX  총 바이트를 구했다.
	mov [CLUSTER_BYTES], eax
	
	; 현재 위치 클러스터를 저장하자.
	mov eax, [BPB_ROOT_ENTRY_CLUSTER]
	mov [CURRENT_CLUSTER], eax

; 변수를 선언하자.
DRIVE		db 0x00
ROOT_ENTRY	dd 0x00
FAT1		dd 0x00
CLUSTER_BYTES	dd 0x00
CURRENT_CLUSTER dd 0x00


1,2,3,4번을 구현했다. 여기까지는 순조롭다.

이제 루트 디렉토리 엔트리 위치를 로딩해서 OS_LDR.SYS를 찾아야 한다. 파일 로딩하는 함수를 만들어야 하는데~~


READ_SECTOR:
	pusha				; 모든 레지스터를 스택에 넣는다.
	mov ah, 0x02		; 디스크 읽기 모드
	mov al, 5			; 1 섹터만 읽는다.
	mov ch, 0			; 트랙은 0번 트랙
	mov cl, 2			; 읽을 섹터는 2번째 섹터
	mov dh, 0			; 헤드는 0번 헤드
	mov dl, [DRIVE_NUM] ; 읽을 드라이브 번호
	mov bx, 0x1000		; 0x9000 메모리에 커널 이미지 올린다.
	mov es, bx
	mov bx, 0x0000
	int 0x13			; 읽어라!

	popa
	ret	


루트 디렉토리 엔트리 위치는 섹터 번호로 기록되어 있다. 이걸 트랙 하고 헤드,섹터 이렇게 다시 재 계산 해야 한다.

트랙, 헤드를 구하는 식은 검색해 보니 이렇게 한다고 한다.


트랙 = 총섹터 / (헤드 갯수 * 트랙당 섹터수)
헤드 = (총섹터/트랙 당 섹터수) % 헤드 갯수
섹터 = (총섹터 % 트랙 당 섹터) + 1


식을 보면서 생각해 보니 대충 그림이 그려진다. 그렇다고 이걸 어셈으로 바꾸긴 힘들었다. 이해와 구현의 괴리감…인터넷에 있는 걸로 만들었다.^^


	; 헤드, 트랙, 섹터로 재 계산해서 저장하자.
	; 트랙 = 총섹터 / (헤드 갯수 * 트랙당 섹터수)
	; 헤드 = (총섹터/트랙 당 섹터수) % 헤드 갯수
	; 섹터 = (총섹터 % 트랙 당 섹터) + 1
	; EAX 는 총섹터 값이 있다.
CONVERT_CHS:
	xor dx, dx	; dx를 0으로 초기화 하자.
	mov cx, [BPB_SECTOR_PER_TRACK]
	div cx	; AX / CX = AX , AX % CX = DX

	inc dl	; 나머지 + 1 = 섹터다.
	mov [SECTOR], dl

	xor dx, dx	; dx를 0으로 초기화 하자. ax는 총섹터/트랙 당 섹터수 값이 있다.
	mov cx, [BPB_HEADS]
	div cx	; (총섹터/트랙 당 섹터수) / 헤드수 = 트랙, (총섹터/트랙 당 섹터수) % 헤드수 = 헤드

	mov [HEAD], dl
	mov [TRACK], al
	ret


위와 같이 만들었다. 이제 섹터 읽는 부분을 구현해야 겠다~


READ_SECTOR:
	pusha				; 모든 레지스터를 스택에 넣는다.
	mov ah, 0x02		; 디스크 읽기 모드
	mov al, [READ_SIZE]	; 읽을 섹터
	mov ch, [TRACK]		; 트랙
	mov cl, [SECTOR]	; 섹터
	mov dh, [HEAD]		; 헤드
	mov dl, [DRIVE_NUM] ; 읽을 드라이브 번호
	mov bx, [READ_MEMORY]	; 읽는 데이터 저장될 메모리
	mov es, bx
	mov bx, 0x0000
	int 0x13			; 읽어라!

	popa
	ret	


아주 쉽게 구현되었다. ㅎㅎ

이제 5번을 구현해 봐야지!~


FILE_FIND:
	; 파일이름을 찾자. 
	mov ax, [0x9000]	; 0x9000에 루트 디렉토리 엔트리 데이터 존재.

FIND_LOOP:
	add ax, 0x20		; 첫 데이터는 C:\ 데이터므로 넘어간다.

	// 파일 이름을 비교한다.
	mov si, ax	
	mov di, OS_LDR_FILENAME
	mov cx, 11
	repe cmpsb

	jnz FIND_LOOP

	; 파일을 찾았다.!!! ^^

	; 데이터 클러스터 위치를 구하자.
	mov ax, [si+9]	; 클러스터 상위 2바이트
	push ax
	mov ax, [si+15]	; 클러스터 하위 2바이트
	push ax
	pop eax
	mov [CURRENT_CLUSTER], eax


어셈 문법 찾아보면서 하려니 너무 진도가 더디다. 좀 쉬어야 겠다… 머리 쥐나겠다.

….

;; creator : cyj (mangg@manggong.org)
;; date : 2012. 4. 17
;; desc : BPB bootloader on FAT32
;;        1. FAT32를 분석한다.
;;        2. OS_LDR.sys파일을 FAT32 파일 시스템에서 찾아서 0x8000 번지로 로드한다.
;;        3. OS_LDR.sys데이터가 있는 0x8000으로 이동한다.

org 0x7C00
bits 16

BOOTLOADER_ENTRY:
	;; start로 이동한다. (BPB의 영역의 3바이트는 점프 명령 코드로 이용한다. )
	jmp BOOTLOADER_MAIN
	nop ; 코드에서 2바이트 밖에 안되서 nop 1바이트 추가.

;; FAT32 파일시스템 구조
BPB_OEM: db "EOS Boot"				; 8 바이트 OEM 이름..
BPB_BYTE_PER_SECTOR:	dw 512		; 섹터당 바이트 크기.
BPB_SECTOR_PER_CLUSTER: db 0x02		; 클러스터당 섹터 갯수
BPB_RESERVED_SECTOR:	dw 0x186E   ;  예약 섹터 갯수
BPB_FAT_NUM:			db 0x02		; FAT 갯수 (FAT1, FAT2 의 카피본을 두므로 보통 2개)
BPB_ROOT_ENTRY_NUMBER:	dw 0x0000	; 루트 디렉토리에 최대 디렉토리 포함할 수 있는 갯수
BPB_FAT16_SECTORS:		dw 0x0000	; FAT16일 때 총 섹터 갯수
BPB_MEDIA_TYPE:			db 0xF8		; 장치 타입 (0xF8 는 하드디스크)
BPB_FAT16_SIZE:			dw 0x0000	; FAT16일때 FAT테이블 크기
BPB_SECTOR_PER_TRACK:	dw 0x003F	; 트랙당 섹터 갯수
BPB_HEADS:				dw 0x00FF 	; 헤드 갯수
BPB_HIDDEN_SECTOR:		dd 0x00000080	; 숨겨진 섹터 갯수
BPB_FAT32_SECTORS:		dd 0x0003E800	; FAT32 파일시스템일 때 총 섹터 갯수
BPB_FAT32_SIZE:			dd 0x000003C9	; FAT32일때 FAT테이블 크기
BPB_EXTENSION_FLAGS:	dw 0x0000		; 확장 플래그 ( 0-3 : 활성화 FAT )
BPB_FILESYSTEM_VER:		dw 0x0000		; FAT32의 버젼
BPB_ROOT_ENTRY_CLUSTER: dd 0x00000002	; 데이터 시작 클러스터 위치
BPB_FILESYSTEM_INFO:	dw 0x0001		; 파일시스템 정보 위치
BPB_BACKUP_BOOT_SECTOR: dw 0x0006		; 백업 부트 섹터 위치
BPB_RESERVED:							; 예약된 정보 
						dd 0x00
						dd 0x00
						dd 0x00
BS_DRIVE:							; 바이오스콜(0x13) 드라이브 정보
						db 0x80
BS_RESERVED1:						; 예약
						db 0x00
BS_BOOT_SIGNATURE:					; 부트 서명 (0x29)
						db 0x29		;
BS_VOLUME_ID:						; 볼륨 시리얼 번호
						dd 0xC1C286C
BS_VOLUME_LABEL:					; 볼륨 레이블명
						dd 0x4E4F204E
						dd 0x414D4520
						dw 0x2020
						db 0x20
BS_FILESYSTEM_TYPE:					; 파일 시스템 이름
						dd 0x46415433
						dd 0x32202020

BOOTLOADER_MAIN:
	cli
	;; CS, DS, ES, & SS을 초기화 하자.
	xor eax, eax
	mov ds, ax
	mov ss, ax
	mov es, ax

	mov sp, 0x8000  ; 스택포인터를 잡자.
	mov bp, sp		; bp도 0x8000으로 잡자.
	sti

	mov [DRIVE], dl	; 현재 드라이브를 저장하자.

	; 루트 디렉토리 엔트리 위치를 구하자.
	; 루트 디렉토리 엔트리 = (FAT 갯수 * FAT 테이블크기) + 예약 섹터 + 히든 섹터
	mov al, [BPB_FAT_NUM]	
	mov ebx, [BPB_FAT32_SIZE]
	mul ebx		; AL * EBX = EAX
	xor ebx, ebx ; EBX를 0으로 초기화 하자.
	mov bx, [BPB_RESERVED_SECTOR] ; 예약 섹터를 bx 저장.
	add eax, ebx	; EAX = EAX + EBX
	mov ebx, [BPB_HIDDEN_SECTOR] ; 히든 섹터를 ebx로 저장.
	add eax, ebx	; EAX = EAX + EBX
	mov [ROOT_ENTRY], eax

	; FAT1 위치를 찾자.
	; FAT1 = 예약 섹터 + 히든 섹터
	xor eax, eax	; eax는 0으로 초기화 한다.
	mov ax, [BPB_RESERVED_SECTOR] ; 예약 섹터를 ax 저장.
	add eax, ebx	; ebx 는 이미 히든 섹터가 저장되어 있다.
	mov [FAT1], eax

	; 클러스터를 바이트로 변환하자.
	; 클러스터 총 바이트 = (섹터당 바이트 * 클러스터당 섹터 갯수)
	xor ebx, ebx
	mov ax, [BPB_BYTE_PER_SECTOR]
	mov bl, [BPB_SECTOR_PER_CLUSTER]
	mul bx	; EAX = AX * BX  총 바이트를 구했다.
	mov [CLUSTER_BYTES], eax

	mov eax, 0x0800
	mov [LDR_MEMORY], eax
		
	; 현재 위치 클러스터를 저장하자.
	mov eax, [BPB_ROOT_ENTRY_CLUSTER]
	mov [CURRENT_CLUSTER], eax

	; 0x9000 에 임시로 루트 디렉토리 엔트리를 읽어서 저장해 놓자.
	mov ax, 0x0900	; ES 세그먼트
	mov [READ_MEMORY], ax

FILE_LIST:
	mov ebx, [ROOT_ENTRY]	; 루트 디렉토리 섹터 위치.
	mov eax, [CURRENT_CLUSTER]
	sub eax, 2
	add eax, ebx

	call CONVERT_CHS	; 헤드, 트랙, 섹터로 변환한다.
	call READ_SECTOR	; 데이터를 읽는다.

FILE_FIND:
	; 파일이름을 찾자. 
	mov ax, [0x9000]	; 0x9000에 루트 디렉토리 엔트리 데이터 존재.

FIND_LOOP:
	add ax, 0x20		; 첫 데이터는 C:\ 데이터므로 넘어간다.

	; 파일 이름을 비교한다.
	mov si, ax	
	mov di, OS_LDR_FILENAME
	mov cx, 11
	repe cmpsb

	mov ebx, [CLUSTER_BYTES]
	add ebx, 0x9000
	cmp eax, ebx
	je NO_FIND

	jnz FIND_LOOP

	; 파일을 찾았다.!!! ^^

	; 데이터 클러스터 위치를 구하자.
	mov ax, [si+9]	; 클러스터 상위 2바이트
	push ax
	mov ax, [si+15]	; 클러스터 하위 2바이트
	push ax
	pop eax
	mov [CURRENT_CLUSTER], eax

LOOP_DATA_READ:
	call DATA_READ	; 데이터 클러스터에서 데이터 읽기
	call FAT_SEARCH	; FAT 테이블에서 다음 클러스터를 찾는다.
	cmp al, 0x01	; al에 다음 클러스터가 있으면 0x00, 없으면 0x01 
	je RUN_LDR		; 다음 클러스터가 없으면 OS_LDR 실행하러 가자.
	jmp LOOP_DATA_READ	; 다음 데이터 클러스터 읽으로 가자.

RUN_LDR:
	jmp 0x8000		; 0x8000(OS_LDR.SYS) 실행하자.

	; 루트 디렉토리 엔트리에서 OS_LDR.SYS를 못 찾으면
	; 루트 디렉토리 엔트리의 다음 클러스터를 찾아서
	; 파일 목록을 비교 하러간다.
NO_FIND:
	call FAT_SEARCH	; FAT 테이블에서 다음 클러스터를 찾자.
	cmp al, 0x00	; 다음 클러스터를 찾으면 0x00 
	je FILE_FIND	; 다시 파일이름 찾으로 가자.
	jmp $			; 다음 클러스터가 없으면 무한루프.

	; 데이터 클러스터에서 데이터를 읽어서
	; LDR_MEMORY에 로딩하자.
DATA_READ:
	mov ax, [LDR_MEMORY]	
	mov [READ_MEMORY], ax	; READ_MEMORY에 LDR_MEMORY 복사하자. (세그먼트임을 기억하자)
	mov al, [BPB_SECTOR_PER_CLUSTER]
	mov [READ_SIZE], al	; 클러스터(섹터 2) 만큼 읽자.
	mov ebx, [ROOT_ENTRY]	; 루트 디렉토리 섹터 위치.
	mov eax, [CURRENT_CLUSTER] ; 현재 클러스터.
	sub eax, 2		; 클러스터가 2부터 시작한다고 하니 2를 뺀다.
	add eax, ebx	; EAX = EAX-EBX. EAX는 루트디렉토리부터 현재 클러스터(-2) 더한값.

	call CONVERT_CHS	; 헤드, 트랙, 섹터로 변환한다.
	call READ_SECTOR	; 데이터를 읽는다.

	mov ax, [LDR_MEMORY]	; 데이터를 읽었으니까 데이터 주소를
	mov ebx, [CLUSTER_BYTES]	; 읽은 만큼 증가시켜준다.(클러스터의 바이트수)
	shr ebx, 4	; 세그먼트 계산법에 의해 4바이트 빼 버린다.
	add ax, bx	
	mov [LDR_MEMORY], ax	; 데이터 읽을만큼 LDR_MEMORY를 증가.

	ret

	; FAT 테이블에서 다음 클러스터를 찾자.
FAT_SEARCH:
	; FAT 리스트
	; 0x9000 에 임시로 FAT테이블을 읽어서 저장해 놓자.
	mov ax, 0x0900	; ES 세그먼트
	mov [READ_MEMORY], ax

	; FAT 테이블의 클러스터는 4바이트 이루어져 있으므로
	; 512 / 4 = 128 . 한 섹터당 128개의 클러스터로 이루어져 있다.
	; 현재 클러스터의 FAT 테이블에서 위치를 찾아보자.
	; 현재클러스터 / 128 은 FAT에서 클러스터 위치.
	mov al, 1
	mov [READ_SIZE], al	; FAT 테이블은 섹터 하나만 읽자.
	mov eax, [CURRENT_CLUSTER]
	mov bx, [BPB_BYTE_PER_SECTOR]
	shr bx, 2	; 4로 나누자.
	xor edx, edx
	div ebx	; 128로 AX를 나누자.
	push edx	; AX를 128로 나눈 나머지는 DX로.
	add eax, [FAT1] ; 나눈 몫에 FAT테이블 위치를 더한다.

	; FAT 테이블을 읽자.
	call CONVERT_CHS	; 헤드, 트랙, 섹터로 변환한다.
	call READ_SECTOR	; 데이터를 읽는다.

	pop edx	; 나눠서 나머지 스택에서 빼내고.
	mov esi, 0x9000	;
	sal edx, 2	; 클러스터 X 4 = 클러스터 메모리 위치.
	add esi, edx	; 0x9000 + 클러스터 메모리 위치.
	mov eax, [esi]	; eax에 현재 클러스터의 다음 클러스터값을 읽자.

	cmp eax, 0x0FFFFFFF	; 끝이라면
	jz FAT_END			; al에 0x01 실패!
	
	mov [CURRENT_CLUSTER], eax	; 다음 클러스터를 저장.
	mov al, 0			; al에 0x00 성공!
	ret

FAT_END:
	mov al, 1
	ret

	; 헤드, 트랙, 섹터로 재 계산해서 저장하자.
	; 트랙 = 총섹터 / (헤드 갯수 * 트랙당 섹터수)
	; 헤드 = (총섹터/트랙 당 섹터수) % 헤드 갯수
	; 섹터 = (총섹터 % 트랙 당 섹터) + 1
	; EAX 는 총섹터 값이 있다.
CONVERT_CHS:
	xor dx, dx	; dx를 0으로 초기화 하자.
	mov cx, [BPB_SECTOR_PER_TRACK]
	div cx	; AX / CX = AX , AX % CX = DX

	inc dl	; 나머지 + 1 = 섹터다.
	mov [SECTOR], dl

	xor dx, dx	; dx를 0으로 초기화 하자. ax는 총섹터/트랙 당 섹터수 값이 있다.
	mov cx, [BPB_HEADS]
	div cx	; (총섹터/트랙 당 섹터수) / 헤드수 = 트랙, (총섹터/트랙 당 섹터수) % 헤드수 = 헤드

	mov [HEAD], dl
	mov [TRACK], al
	ret
	
READ_SECTOR:
	pusha				; 모든 레지스터를 스택에 넣는다.
	mov ah, 0x02		; 디스크 읽기 모드
	mov al, [READ_SIZE]	; 읽을 섹터 수
	mov ch, [TRACK]		; 트랙
	mov cl, [SECTOR]	; 섹터
	mov dh, [HEAD]		; 헤드
	mov dl, [DRIVE] ; 읽을 드라이브 번호
	mov bx, [READ_MEMORY]	; 읽는 데이터 저장될 메모리
	mov es, bx
	mov bx, 0x0000
	int 0x13			; 읽어라!

	popa
	ret	

; 변수를 선언하자.
DRIVE		db 0x00
ROOT_ENTRY	dd 0x00
FAT1		dd 0x00
CLUSTER_BYTES	dd 0x00
CURRENT_CLUSTER dd 0x00
READ_MEMORY		dw 0x00
READ_SIZE		db 0x00
SECTOR			db 0x00
TRACK			db 0x00
HEAD			db 0x00
LDR_MEMORY		dw 0x00

OS_LDR_FILENAME db "OS_LDR  SYS",0

	times 510-($-$$) db 0x00
	dw	0xAA55


쉬는 동안 6,7,8,9 구현해 봤다. 하고 보니 완전 길다~  컴파일 될지도 모르겠고 동작 되는지도 모르겠다. 그냥 말 그대로 그냥 구현했다.

error : TIMES value –13 is negative


제길 … 컴파일 안 된다. 요건 왜 이러냐..


510-($-$$) 요거 때문에 생기는 에러란다. (현재위치 – 첫 위치) 가 510 바이트를 넘는다는 거지~~ 512… 아니…이것보다는 적은 공간에 작성하려니 너무 적다. 아 … 어떻게 해야 하나…


변수를 저렇게 잡지 않고 메모리 영역 하나 잡아서 선언하는 방법을 인터넷에서 검색하다가 찾았다.


DRIVE		equ 0x500
ROOT_ENTRY	equ 0x501
FAT1		equ 0x505
CLUSTER_BYTES	equ 0x509
CURRENT_CLUSTER	equ 0x50D
READ_MEMORY		equ 0x511
READ_SIZE		equ 0x513
SECTOR			equ 0x514
TRACK			equ 0x515
HEAD			equ 0x516
LDR_MEMORY		equ 0x517


요렇게 하고 컴파일 해보니 호~~~ 된다. 동작은 될련지는 미지수다…내가 만든 코드지만 솔직히 못 믿겠다. 문법도 맞는지도….그냥 막코딩!!!!

검증을 하기 위해서 전번에 만들어 두었던 코드로 테스트 해봐야 겠다.


ORG 0x8000			; 0x8000 메모리에서 실행되므로 0x8000 으로 주소정렬
BITS 16

	cli				; 인터럽트 발생안되게 설정
	xor ax,ax		; ax 0으로 초기화 한다. ax xor ax 어차피 0이다.
	mov ds, ax		; ds 세그먼트 레지스터를 0 으로 초기화 한다.
	mov es, ax		; es 세그먼트 레지스터를 0 으로 초기화 한다.
	mov ss,ax
	mov sp,0xffff
	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


요걸 OS_LDR.SYS 이름으로 컴파일 한다.

nasm –fbin –o OS_LDR.SYS OS_LDR.ASM


파티션 부트로더에 컴파일 한 코드 붙여 넣고, 이걸 다시 [제어판][관리 도구][컴퓨터관리] 가서 VHD 연결 해서 드라이브로 연결해서 OS_LDR.SYS를 넣었다. 잘 될까?

 

'프로그래밍 > OS 만들기' 카테고리의 다른 글

OS 만들기 #10  (0) 2013.08.14
OS 만들기 #9 - 부트로더  (0) 2013.08.14
OS 만들기 #7 - FAT32  (1) 2013.08.14
OS 만들기 #6 - MBR  (0) 2013.08.14
OS 만들기 #5 - 부트로더  (0) 2013.08.14

+ Recent posts