지난번까지 QEMU 환경으로 변환했고, 하드디스크를 구현할 차례이다.
#interrupt.c
__asm__ __volatile__("mov eax, %0"::"r"(&idtr));
__asm__ __volatile__("lidt [eax]");
__asm__ __volatile__
(
"mov al, 0x00;"
"out 0xA1, al;"
"mov al, 0x00;"
"out 0x21, al;"
);
__asm__ __volatile__("sti");
return;
}
mov al,, 0x00
out 0xA1, al
- 슬레이브 PIC의 모든 인터럽트를 열어둔다.
mov al,, 0x00
out 0x21, al
- 마스터 PIC의 모든 인터럽트를 열어둔다.
이렇게 모든 인터럽트를 해방하고 나서 하드디스크의 인터럽트(자신에게 떨어진 명령을 수행한 후 CPU에게 날리는 인터럽트)를 받을 수 있음.
#function.c - HDDstatus()~
unsigned char HDDstatus()
{
unsigned char value;
__asm__ __volatile__
(
"mov dx,0x1F7;"
"in al, dx;"
);
__asm__ __volatile__("mov %0, al;" :"=r"(value));
return value;
}
int HDD_BSY()
{
unsigned char status = HDDstatus();
if ( (status & 0x80) == 0x80) return 1;
else return 0;
}
int HDD_DRDY()
{
unsigned char status = HDDstatus();
if ((status & 0x40) == 0x40) return 1;
else return 0;
}
int HDD_DRQ()
{
unsigned char status = HDDstatus();
if ((status & 0x08) == 0x08) return 1;
else return 0;
}
int HDD_ERR()
{
unsigned char status = HDDstatus();
if ((status & 0x01) == 0x01) return 1;
else return 0;
}
- 하드디스크의 현재 상태를 읽어오는 함수 구현
- HDD_BSY() : if문 : busy 상태
- HDD_DRDY() : if문 : 준비 상태
- HDD_DRQ() : if문 내부에서 데이터 리퀘스트. 아닐 경우 논 데이터 리퀘스트.
- HDD_ERR() : if문 진입시 에러상태.
#function.c - HDDread()
void HDDread(unsigned int sector, char* buffer)
{
unsigned char LBA_a = sector & 0xFF;
unsigned char LBA_b = ( sector >> 8 ) & 0xFF;
unsigned char LBA_c = ( sector >> 16) & 0xFF;
unsigned char LBA_d = ( ( sector >> 24) & 0x0F ) | 0xE0 ;
/*
* sector 변수를 분해. 각 포트별로 비트를 분해해서 넣기 위함
*
* LBA_a : sector의 [7:0] 부분의 비트 추출
* LBA_b : sector의 [15:8] 비트 추출
* LBA_c : sector의 [23:16] 비트 추출
* LBA_d : sector의 [27:24] 비트 추출
*/
// HDD INT 활성화
__asm__ __volatile__
(
"mov al, 0;"
"mov dx, 0x3F6;"
"out dx, al;"
);
while ( HDD_BSY() == 1); // HDD가 busy 하다면 계속 대기
// ========== 하드디스크 Setting
// 각 포트가 요구하는 값에 맞게 값 저장
// 드라이브/헤드 레지스터 초기화 + LBA 주소 [27:24] 4비트
__asm__ __volatile__
(
"mov al, %0;"
"mov dx, 0x1F6;"
"out dx, al;"::"r"(LBA_d)
);
__asm__ __volatile__
(
"mov al, 0x01;"
"mov dx,0x1F2;"
"out dx, al;"
); // 섹터 1개 읽어오기
__asm__ __volatile__
(
"mov al, %0;"
"mov dx,0x1F3;"
"out dx, al;" ::"r"(LBA_a)
);
__asm__ __volatile__
(
"mov al, %0;"
"mov dx,0x1F4;"
"out dx, al;" ::"r"(LBA_b)
);
__asm__ __volatile__
(
"mov al, %0;"
"mov dx,0x1F5;"
"out dx, al;" ::"r"(LBA_c)
);
// 하드디스크 드라이버가 명령을 받을 수 있는지 체크
while ((HDD_BSY() ==1 )|| (HDD_DRDY()==0));
// 0x20 : 읽기 명령
__asm__ __volatile__
(
"mov al, 0x20;"
"mov dx,0x1F7;"
"out dx, al;"
);
// order 후 error 발생 시 read 중단.
if (HDD_ERR() == 1)
{
kprintf("Error!!", videomaxline - 1, 0);
return;
}
while (HDD_DRQ() == 0); // 데이터를 다 읽을 때까지 대기
// 읽기가 성공 -> Buffer에 데이터를 512바이트만큼 옮김
__asm__ __volatile__("mov dx,0x1F0;");
__asm__ __volatile__("mov edi, %0;" : : "r"(buffer));
__asm__ __volatile__("mov ecx, 256");
__asm__ __volatile__("rep insw");
// ds:edi 주소에 dx포트의 값을 2바이트 읽는 명령어. 한번 실행 시 0x1F0에 담긴 정보를 buffer에 저장하게 됨.
}
하드웨어 구현이 끝났지만 이대로는 제대로 구현이 되었는지 어렵기 때문에 hdd.img에 각 섹터의 시작 부분에 여기가 어떤 섹터인지 알리는 데이터를 저장한다.
- 0x000 : "This is sector 0"
- 0x200 : "This is sector 1"
이 문자열을 확인하기 위해 shell 명령어를 하나 추가한다.
#shell.c - sh_HDDread()
void sh_HDDread()
{
HDDread(0, diskbuffer);
kprintf(diskbuffer, ++curline, 0);
HDDread(1, diskbuffer);
kprintf(diskbuffer, ++curline, 0);
}
- diskbuffer를 data.h에 unsigned char diskbuffer[512]; 로 수정
#main.c - translate_shell()
void translate_shell()
{
if (keyboard[0] == 0) { return; }
if (kstrcmp(keyboard, "clear")) { sh_clear(); return; }
if (kstrcmp(keyboard, "version")) { sh_version(); return; }
if (kstrcmp(keyboard, "read")) { sh_HDDread(); return; }
kprintf("There is no such command.",++curline, 0);
}
- 방금 추가한 sh_HDDread() 함수를 실행할 수 있도록 read 명령어를 추가해준다.
컴파일 후 실행할 경우, 다음과 같이 정상적으로 실행한다.