ncloader  0.1
 모두 데이타 구조 파일들 함수 변수 타입정의 열거형 타입 열거형 멤버 매크로 그룹들 페이지들
Disk I/O
+ Disk I/O에 대한 협력 다이어그램:

앞에서 Disk I/O 에 대한 이야기를 자주 했다. 하지만, 어떻게 할 수 있을까?

막상 I/O 를 하려고 하면 막막해진다.

H/W 를 제어하기 위해서 우리는 BIOS 가 제공하는 Service 들을 이용한다. BIOS Software 는 Embedded System 에서의 Firmware 와 같은 것이다. 이 Software 에는 다양한 서비스 함수들이 구현되어 있기 때문에, 우리는 가져다 쓰기만 하면 된다. 물론, Real mode(16 bits) 에서만 동작하는 것이므로, Protected mode (32 bits) 에서 사용하기는 어렵다.

주의
32 Bits mode 에서 16 Bits BIOS Service Routine 에 접근할 수 있으나, 직접 호출 해서 사용하는 방법은 독자가 직접 찾아 보기 바란다. 한참 Disk Access 방법을 찾다가 Protected Mode 에서 Real Mode BIOS Service Routine 을 사용하는 방법에 대한 글을 읽은 기억은 있지만, 실제 해보질 않아서 설명하기 어렵다. 그러나 방법은 있을 듯 하니, 관심있는 독자라면 한 번쯤 찾아 보는 것도 많은 도움이 될 것이다.

일단 전체 BIOS Service Routine 에 대한 목록을 정리하면 다음과 같다.

http://www.bioscentral.com/misc/biosservices.htm

InterruptAddressTypeDescription
05h0000:0014hSoftwarePrint screen
10h0000:0040hSoftwareVideo service routine
11h0000:0044hSoftwareEquipment list service routine
12h0000:0048HSoftwareMemory size service routine
13h0000:004ChSoftwareHard disk drive service
14h0000:0050hSoftwareSerial communications service routines
15h0000:0054hSoftwareSystem services support routines
AHALDescription
0x88Get Extended Memory Size
0xE80x01Get Extended Memory Size. Gives results for memory size above 64 MB
0xE80x20Query System Address Map. The information returned from E820 supersedes what is returned from the older AX=0xE801 and AH=0x88 interfaces Memory Range Descriptors
16h0000:0058hSoftwareKeyboard support service routines
17h0000:005ChSoftwareParallel printer support services
18h0000:0060hSoftwareLoad and run ROM BASIC
19h0000:0064hSoftwareDOS loading routine
1Ah0000:0068hSoftwareReal time clock service routines
1Bh0000:006ChSoftwareCRTL - BREAK service routines
1Ch0000:0070hSoftwareUser timer service routine
1Dh00000074hSoftwareVideo control parameter table
1Eh0000:0078hSoftwareFloppy disk parameter routine
1Fh0000:007ChSoftwareVideo graphics character routine
20h-3Fh0000:0080f - 0000:00FChSoftwareDOS interrupt points
40h0000:0100hSoftwareFloppy disk revector routine
41h0000:0104hSoftwarehard disk drive C: parameter table
42h0000:0108hSoftwareEGA default video driver
43h0000:010ChSoftwareVideo graphics characters
44h0000:0110hSoftwareNovel Netware API
45h0000:0114hSoftwareNot used
46h0000:0118hSoftwareHard disk drive D: parameter table
47h0000:011Ch -SoftwareNot used
48hSoftwareNot used
49h0000:0124hSoftwareNot used
4Ah0000:0128hSoftwareUser alarm
4Bh-63h0000:012Ch - SoftwareNot used
64hSoftwareNovel Netware IPX
65h-66hSoftwareNot used
67hSoftwareEMS support routines
68h-6Fh0000:01BChSoftwareNot used
7AhSoftwareNovell Netware API
78h-FFh0000:03FChSoftwareNot used
주의
이 장에서는 간략히 목록만 나열하고, 자세한 사항은 실제 필요한 시점에 다시 설명하기로 한다.

현재 우리가 필요한 것은 Disk 에 저장되어 있는 Stage 2 Boot Loader 를 메모리에 적재하는 일이다. Disk 를 읽기 위해서는, 위에 나열된 서비스 루틴들 중에, Low level disk services 를 이용하면 된다.

InsturctionInt 13h
BIOS operationDiskette service
ParametersAx, es:bx, cx, dx

Int 13h 함수는 여러가지의 서로 다른 저 수준 디스크 서비스를 PC 프로그램을 위해 제공한다. 디스켓 시스템을 초기화 시키거나 디스켓의 상태를 가져오는 것, 원하는 섹터를 읽어 들이는 것, 섹터를 쓰는 것, 섹터를 검사하는 것, 디스크 트랙을 포맷하는 것 등 많은 기능을 제공한다. 이 인터럽트 루틴은 그 동안 많은 변화가 있었던 것 중 하나이다. 처음 이 루틴이 개발 되었을 때, 10 MB 의 하드 디스크는 매우 큰 용량이었다. 하지만, 요즘은 게임만 수백메가가 되기도 한다.

AHInput parametersOutput parametersDescription
0DL : driver (0..7h is floppy, 80h..FFh is hardAH: status(0 and carry clear if no error, error code if error)Resets the specified disk dirver. Resetting a hard disk also resets the floppy drives.
1DL: drive (as above)AH: 0, AL: status of previous disk oeprationThis call returns the following status values in AL 0: no error 1: invalid command 2: address mark not found 3: disk write protected 4: couldn’t find sector 5: reset error 6: removed media 7: bad parameter table 8: DMA overrun 9: DMA operation crossed 64K boundary 10: illegal sector flag 11: illegal track flag 12: illegal media 13: invalid # of sectors 14: control data address mark encountered 15: DMA error 16: CRC data error 17: ECC corrected data error 32: disk controller failed 64: seek error 128: timeout error 170: drive not ready 187: undefined rror 204: write error 224: status error 255: sense failure
2AL: # of sectors to read ES:BX: buffer address CL: bits 0..5 sector # CL: bits 6/7 track bits CL: bits 8/9 CH: track bits 0..7 DL: drive # DH: bits 0..5: head # DH: bits 6/7: track bits 10/11 AH: return status AL: burst error length Carry 0: success Carry 1: error Reads the specified number of 512 bytes sectors from the disk. Data read must be 64 Kbytes or less
3Same as (2) aboveSame as (2) aboveWrites the specified number of 512 byte sectors to the disk. Data written must not exceed 64 Kbytes in length
4Same as (2) above except there is no need for a bufferSame as (2) aboveVerifies the data in the specified number of 512 bytes sectors on disk
0xchSame as (4) above except there is no need for a sector #Same as (4) aboveSends the disk head to the specified track on the disk
0x0dhDL: drive # (80h or 81h)AH: return statusCarry 0: no error Carry 1: error Reset the hard disk controller

이 인터럽트 서비스 루틴을 이용하면 Boot loader 에서 디스크 엑세스를 직접할 수 있다. 하지만, 디스크를 엑세스 하기 위해서는 디스크의 구조에 대한 이해가 선행되어야 한다. 디스켓에는 Head 와 Track, Sector 가 있다. Track 은 Cylinder 라고 불리우기도 한다.

하나의 Track 에는 18개 (BPB에 기록됨)의 Sector 가 있으며, Head 에는 2개(BPB에 기록된)가 있다. Head 0, Track 0 번 다음에는 Head 1, Track 0번의 순서로 Sector 를 읽어야 한다. 즉 0 번 트랙에는 Head 0 과 Head 1 의 두 가지 경우를 포함하여 총 36 개의 Sector 가 존재하는 것이다. 예를 들어 20 번째 Sector 에 접근해야 한다면, Head 는 1번 Track 은 0 번 Sector 는 2번이 되어야 하는 것이다. 한가지 주의할 점은 인터럽트 핸들러를 이용한 섹터 접근시 시작 섹터가 0번이 아닌 1번이라는 것이다. 원하는 위치의 섹터를 읽어 들이는 함수를 구현하는 것은 커널 이미지 적재를 위해서 반드시 필요하다. 읽어 들이고 싶은 섹터 번호를 넘겨주면, 해당 번호를 이용해 Head 번화 Track 번호를 구해내는 함수라면 매우 유용할 것이다. 이 함수를 구현하기 위해서는 다음과 같은 순서를 따르면 된다.

인터럽트 호출이 끝나면 Carry flag 를 이용해 성공했는지를 판단해 주어야 한다. Carry 가 발생하지 않았다면 읽기에 성공한 것이다.

Functionread_sector(segment, offset, sector)
Brief지정된 섹터를 메모리로 읽어 들인다.
Param[in] segment - 적재할 대상 메모리 세그먼트
Param[in] offset - 세그먼트 내에서의 오프셋
Param[in] sector - 읽어들일 섹터 번호
Returnvoid
Code

read_sector:
push bp
mov bp, sp
pusha
mov ax, [bp+4]
mov bl, TWO_TRACKS
div bl ; qutient(al) == track
mov ch, al ; # of tracks
shr ax, 8 ; To divide remainder of the previous division
sub si, BOOT
add si, [RELT]
mov bl, [si] ; Dividen
div bl ; quotient(al) == head
mov dh, al ; # of head
mov cl, ah ; sector starting number
inc cl
mov si, bootdrive
sub si, BOOT
add si, [RELT]
mov dl, [si]
mov ax, 0x0201 ; read a(1) sector
mov es, [bp+8]
mov bx, [bp+6]
int 0x13
popa
pop bp
ret
주의
이 코드에서 주의할 것으 [] 로 표현된 부분들이다. 실제로 필자는 sector_per_track 의 값을 메모리의 특정 번지에 저장해서 사용하고 있었다. BPB 에 정의된 값이라서 그렇게 한 것인데, 한참을 해맸었다. 디버깅도 어렵게 했다. sector_per_track dw 18 메모리에 접근하는 경우(C 언어의 포인터 연산을 생각하라)에, 특히 그 번지의 값을 필요로 하는 경우에는 반드시 대가로를 사용해야 한다. 물론 필자는 이 사실을 알고 시작했었지만, sector_per_track 이 메모리 번지로 해석이 되어야 한다는 사실을 마치 변수에 값을 넣는 것으로 착각해 버렸던 것이다. 이 글을 읽는 독자는 같은 실수로 고생하지 말았으면 한다.