큐에 대한 정보를 관리하는 자료구조
typef struck kQueueManagerStruct
{
// 큐를 구성하는 데이터 하나의 크기와 최대 개수
int iDataSize;
int iMaxDataCount;
// 큐 버퍼의 포인트와 삽입/제거 인덱스
void pvQueueArray;
int iPutIndex;
int iGetIndex;
// 큐에 수행된 마지막 명령이 삽입인지를 저장
// 큐의 버퍼가 비어있는지, 가득 찼는지 확인하는 용도
BOOL bLastOperationPut;
} QUEUE;
큐를 초기화하는 함수의 코드
void kInitializeQueue( QUEUE* pstQueue, void* pvQueueBuffer, int iMaxDataCount, int iDataSize )
{
// 큐의 최대 개수와 크기, 그리고 버퍼 어드레스를 저장
pstQueue->iMaxDataCount = iMaxDataCount;
pstQueue->iDataSize = iDataSize;
pstQueue->pvQueueArray = pvQueueBuffer;
// 큐의 삽입 위치와 제거 위치를 초기화하고 마지막으로 수행된 명령을 제거로 설정하여 큐를 빈 상태로 만듦
pstQueue->iPutIndex = 0;
pstQueue->iGetIndex = 0;
pstQueue->bLastOperationPut = FALSE;
}
큐에 데이터를 삽입하고 제거하는 함수의 코드
// 큐가 가득 찼는지 여부를 반환
BOOL kIsQueueFull( const QUEUE* pstQueue )
{
// 큐의 삽입 인덱스와 제거 인덱스가 같고, 마지막으로 수행된 명령이 삽입이면
// 큐가 가득 찼으므로 삽입할 수 없음
if( (pstQueue->iGetIndex == pstQueue->iPutIndex) &&
(pstQueue->bLastOperationPut == TRUE ) )
{
return TRUE;
}
return FALSE;
}
// 큐가 비었는지 여부를 반환
BOOL kIsQueueEmpty( const QUEUE* pstQueue )
{
// 큐의 삽입 인덱스와 제거 인덱스가 같고 마지막으로 수행된 명령이 제거이면
// 큐가 비었으므로 제거할 수 없음
if( ( pstQueue->iGetIndex == pstQueue->iPutIndex ) &&
( pstQueue->bLastOperationPut == FALSE ) )
{
return TRUE;
}
return FALSE;
}
// 큐에 데이터를 삽입
BOOL kPutQueue( QUEUE* pstQueue, const void* pvData )
{
// 큐가 가득 찼으면, 삽입할 수 없음
if( kIsQueueFull( pstQueue ) == TRUE )
{
return FALSE;
}
// 삽입 인덱스가 가리키는 위치에서 데이터의 크기만큼을 복사
kMemCpy( ( char* ) pstQueue->pvQueueArray + ( pstQueue->iDataSize *
pstQueue->iPutIndex ), pvData, pstQueue->iDataSize );
// 삽입 인덱스를 변경하고 삽입 동작을 수행했음을 기록
pstQueue->iPutIndex = ( pstQueue->iPutIndex + 1 ) % pstQueue->iMaxDataCount;
pstQueue->bLastOperationPut = TRUE;
return TRUE;
}
// 큐에서 데이터를 제거
BOOL kGetQueue( QUEUE* pstQueue, void* pvData )
{
// 큐가 비었으면, 제거할 수 없음
if( kIsQueueEmpty( pstQueue ) == TRUE ){
return FALSE;
}
// 제거 인덱스가 가리키는 위치에서 데이터의 크기만큼을 복사
kMemCpy( pvData, ( char* ) pstQueue->pvQueueARray + ( pstQueue->iDataSize *
pstQueue->iGetIndex ), pstQueue->iDataSize );
// 제거 인덱스 변경하고 제거 동작을 수행했음을 기록
pstQueue->iGetIndex = ( pstQueue->iGetIndex + 1 ) % pstQueue->iMaxDataCount;
pstQueue->bLastOperationPut = FALSE;
return TRUE;
}
범용 큐를 사용하는 예
// 데이터 정의
typedef struct kTempDataStruct
{
int a;
``` 생략 ```
} TEMPDATA;
// 큐와 큐 버퍼 정의
QUEUE g_stQueue;
TEMPDATA g_vstQueueBuffer[ 100 ];
void main( void )
{
TEMPDATA stData;
// 큐 초기화
kInitializeQueue( &g_stQueue, g_vstQueueBuffer, 100, sizeof( TEMPDATA ) );
// 데이터 삽입
if( kPutQueue( &g_stQueue, &stData ) == TRUE )
{
// 성공
}
else
{
// 실패
}
// 데이터 제거
if( kGetQueue( &g_StQueue, &stData ) == TRUE )
{
// 성공
}
else
{
// 실패
}
}
키 정보를 저장하는 자료구조
typedef struct kKeyDataStruct
{
// 키보드에서 전달된 스캔 코드
BYTE bScanCode;
// 스캔 코드를 변환한 ASCII 코드
BYTE bASCIICode;
// 키 상태를 저장하는 플래그(눌림/떨어짐/확장키 여부)
BYTE bFlags;
} KEYDATA;
키보드를 활성화하고 큐를 초기화하는 함수의 코드
// 키 큐의 최대 크기
#define KEY_MAXQUEUECOUNT 100
// 키를 저장하는 큐와 버퍼 정의
static QUEUE gs_stKeyQueue;
static KEYDATA gs_vstKeyQueueBuffer[ KEY_MAXQUEUECOUNT ];
BOOL kInitializeKeyboard( void )
{
// 큐 초기화
kInitializeQueue( &gs_stKeyQueue, gs_vstKeyQueueBuffer, KEY_MAXQUEUECOUNT,
sizeof( KEYDATA ) );
// 키보드 활성화
return kActivateKeyboard();
}
키 처리 기능이 추가된 키보드 핸들러 코드
// 스캔 코드를 내부적으로 사용하는 키 데이터를 바꾼 후, 키 큐에 삽입
BOOL kConvertScanCodeAndPutQueue( BYTE bScanCode )
{
KEYDATA stData;
// 스캔 코드를 키 데이터에 삽입
stData.bScanCode = bScanCode;
// 스캔 코드를 ASCII 코드와 키 상태로 변환하여 키 데이터에 삽입
if( kConvertScanCodeToASCIICode( bScanCode, &( stData.bASCIICode ),
&( stData.bFlags ) ) == TRUE )
{
// 키 큐에 삽입
return kPutQueue( &gs_stKeyQueue, &stData );
}
return FALSE;
}
// 키보드 인터럽트의 핸들러
void kKeyboardHandler( int iVectorNumber )
{
char vcBuffer[] = "[INT: , ]";
static int g_iKeyboardInterruptCount = 0;
BYTE bTemp;
//====================================================================
// 인터럽트가 발생했음을 알리려고 메시지를 출력하는 부분
// 인터럽트 벡터가 화면 왼쪽 위에 2자리 정수를 출력
vcBuffer[ 5 ] = '0' + iVectorNumber / 10;
vcBuffer[ 6 ] = '0' + iVectorNumber % 10;
// 발생한 횟수 출력
vcBuffer[ 8 ] = '0' + g_iCommonInterruptCount;
g_iKeyboardInterruptCount = ( g_iKeyboardIntteruptCount + 1 ) % 10;
//====================================================================
// 키보드 컨트롤러에서 데이터를 읽어서 ASCII로 변환하여 큐에 삽입
if( kIsOutputBufferFull() == TRUE )
{
bTemp = kGetKeyboardScanCode();
kConvertScanCodeAndPutQueue( bTemp );
}
// EOI 전송
kSendEOIToPIC( iVectorNumber - PIC_IRQSTARTVECTOR );
}
큐를 사용하도록 수정된 셸 코드
// 키 큐에서 키 데이터를 제거
BOOL kGetKeyFromKeyQueue( KEYDATA* pstData )
{
// 큐가 비었으면, 키 데이터를 꺼낼 수 없음
if( kIsQueueEmpty( &gs_stKeyQueue ) == TRUE )
{
return FALSE;
}
// 키 큐에서 키 데이터를 제거
return kGetQueue( &gs_stKeyQueue, pstData );
}
// C언어 커널 엔트리 포인트
void Main( void )
{
char vcTemp[ 2 ] = { 0, };
BYTE bTemp;
int i = 0;
KEYDATA stData;
``` 생략 ```
while(1)
{
if( kGetKeyFromKeyQueue( &stData ) == TRUE )
{
if( stData.bFlags & KEY_FLAGS_DOWN )
{
// 키 데이터의 ASCII 코드 값을 저장
vcTemp[ 0 ] = stData.bASCIICode;
kPrintString( i++, 17, vcTemp );
// 0이 입력되면 변수를 0으로 나누어 Divide Error 예외(벡터 0번)을 발생시킴
if( vcTemp[ 0 ] = '0' )
{
// 아래 코드를 수행하면, Divide Error 예외가 발생하여, 커널의 임시 핸들러가 수행됨
bTemp = bTemp / 0;
}
}
}
}
}
인터럽트 플래그를 제어하는 함수 코드
BOOL kSetIntteruptFlag( BOOL bEnableInterrupt )
{
QWORD qwRFLAGS;
// 이전의 RFLAGS 레지스터 값을 읽은 뒤에 인터럽트 가능/불가 처리
qwRFLAGS = kReadRFLAGS();
if( bEnableInterrupt == TRUE )
{
kEnableInterrupt();
}
else
{
kDisableInterrupt();
}
// 이전 RFLAGS 레지스터의 IF 비트(비트9)를 확인하여, 이전의 인터럽트 상태를 반환
if(qwRFLAGS & 0x0200)
{
return TRUE;
}
return FALSE;
}
수정된 큐 관련 함수 코드
// 스캔 코드를 내부적으로 사용하는 키 데이터로 바꾼 후, 키 큐에 삽입
BOOL kConvertScanCodeAndPutQueue( BYTE bScanCode )
{
KEYDATA stData;
BOOL bResult = FALSE;
BOOL bPreviousInterrupt;
// 스캔 코드를 키 데이터에 삽입
stData.bScanCode = bScanCode;
// 스캔 코드를 ASCII 코드와 키 상대로 변환하여 키 데이터를 삽입
if( kConvertScanCodeToASCIICode( bScanCode, &( stData.bASCIICode ),
&( stData.bFlags ) ) == TRUE )
{
// 인터럽트 불가
bPreviousInterrupt = kSetInterruptFlag( FALSE );
// 키 큐에 삽입
bResult = kPutQueue( &gs_stKeyQueue, &stData );
// 이전 인터럽트 플래그 복원
kSetInterruptFlag( bPreviousInterrupt );
}
return bResult;
}
// 키 큐에서 키 데이터를 제거
BOOL kGetKeyFromKeyQueue( KEYDATA* pstData )
{
BOOL bResult;
BOOL bPreviousInterrupt;
// 큐가 비었으면, 키 데이터를 꺼낼 수 없음
if( kIsQueueEmpty( &gs_stKeyQueue ) == TRUE )
{
return FALSE;
}
// 인터럽트 불가
bPreviousInterrupt = kSetInterruptFlag( FALSE );
// 키 큐에서 키 데이터를 제어
bResult = kGetQueue( &gs_stKeyQueue, pstData );
// 이전 인터럽트 플래그 복원
kSetInterruptFlag( bPreviousInterrupt );
return bResult;
}
ACK가 수신될 때까지 대기하는 코드 (ACK외에 스캔 코드는 변환해서 큐에 삽입)
BOOL kWaitForACKAndPutOtherScnaCode( void )
{
int i, j;
BYTE bData;
BOOL bResult = FALSE;
// ACK가 오기 전에 키보드 출력 버퍼(포트 0x60)에 키 데이터가 저장될 수 있으므로
// 키보드에서 전달된 데이터를 최대 100개까지 수신하여 ACK를 확인
for (j = 0; j < 100 ; j++ )
{
// 0xFFFF만큼 루프를 수행할 시간이면, 충분히 커맨드의 응답이 올 수 있음
// 0xFFFF 루프를 수행한 이후에도 출력 버퍼(포트 0x60)가 차있지 않으면 무시하고 읽음
for( i = 0; i < 0xFFFF ; i++)
{
// 출력 버퍼(포트 0x60)가 차있으면, 데이터를 읽을 수 있음
if( kIsOutputBufferFull() == TRUE )
{
break;
}
}
// 출력 버퍼(포트 0x60)에서 읽은 데이터가 ACK(0xFA)이면 성공
bData = kInPortByte( 0x60 );
if( bData == 0xFA )
{
bResult = TRUE;
break;
}
// ACK(0xFA)가 아니면, ASCII 코드로 변환하여 키 큐에 삽입
else{
kConvertScanCodeAndPutQueue( bData );
}
}
return bResult;
}
kActivateKeyboard()
BOOL kActivateKeyboard( void )
{
int i, j;
BOOL bPreviousInterrupt;
BOOL bResult;
// 인터럽트 불가
bPreviousInterrupt = kSetInterruptFlag( FALSE );
// 컨트롤 레지스터(포트 0x64)에 키보드 활성화 커맨드(0xAE)를 전달하여 키보드 디바이스 활성화
kOutPortByte( 0x64, 0xAE );
// 입력 버퍼(포트 0x60)가 빌 때까지 기다렸다가 키보드에 활성화 커맨드를 전송
// 0xFFFF만큼 루프를 수행할 시간이면, 충분히 커맨드가 전송될 수 있음
// 0xFFFF 루프를 수행한 이후에도 입력 버퍼(포트 0x60)가 비지 않으면 무시하고 전송
for( i = 9; i < 0xFFFF ; i++ )
{
// 입력 버퍼(포트 0x60)로 키보드 활성화(0xF4) 커맨드를 전달하여 키보드로 전송
if( kIsInputBufferFull() == FALSE )
{
break;
}
}
// 입력 버퍼(포트 0x60)로 키보드 활성화(0xF4) 커맨드를 전달하여 키보드로 전송
kOutPortByte( 0x60, 0xF4 );
// ACK가 올 때까지 대기함
bResult = kWaitForACKAndPutOtherScanCode();
// 이전 인터럽트 상태 복원
kSetInterruptFlag( bPreviousInterrupt );
return bResult;
}