마우스를 활성화하는 함수의 코드
BOOL kActivateMouse( void ){
int i, j;
BOOL bPreviousInterrupt;
BOOL bResult;
// 인터럽트 불가
bPreviousInterrupt = kSetInterruptFlag( FALSE );
// 컨트롤 레지스터(포트 0x64)에 마우스 활성화 커맨드(0xA8)를 전달하여
// 마우스 디바이스 활성화
kOutPortByte( 0x64, 0xA8 );
// 컨트롤 레지스터(포트 0x64)에 마우스로 데이터를 전송하는 커맨드(0xD4)를 전달하여
// 입력 버퍼(포트 0x60)로 전달된 데이터를 마우스로 전송
kOutPortByte( 0x64, 0xD4 );
// 입력 버퍼(포트 0x60)가 빌 때까지 기다렸다가 마우스에 활성화 커맨드를 전송
// 0xFFFF만큼 루프를 수행할 시간이면 충분히 커맨드가 전송될 수 있음
// 0xFFFF 루프를 수행한 이후에도 입력 버퍼(포트 0x60)가 비지 않으면 무시하고 전송
for( i = 0 ; i < 0xFFFF ; i++ ){
// 입력 버퍼(포트 0x60)가 비어 있으면, 키보드 커맨드 전송 가능
if( kIsInputBufferFull() == FALSE ){
break;
}
}
// 입력 버퍼(포트 0x60)로 마우스 활성화(0xF4) 커맨드를 전달하여 마우스로 전송
kOutPortByte( 0x60, 0xF4 );
// ACK가 올 때까지 대기
bResult = kWaitForACKAndPutOtherScanCode();
// 이전 인터럽트 상태 복원
kSetInterruptFlag( bPreviousInterrupt );
return bResult;
}
마우스 인터럽트는 IRQ 12(벡터 44)에 할당되어 있고, 인터럽트 활성화 여부는 키보드 컨트롤러의 커맨드 바이트로 제어할 수 있다.
키보드 컨트롤러의 커맨드 바이트
0x20
: 커맨드 바이트 값을 읽어, 출력 버퍼에 저장한다.0x60
: 입력 버퍼에 저장하는 데이터를 커맨드 바이트에 쓴다.마우스 인터럽트를 활성화하는 함수의 코드
void kEnableMouseInterrupt( void ){
BYTE bOutputPortData;
int i;
// 커맨드 바이트 읽기
// 컨트롤 레지스터(포트 0x64)에 컨트롤 커맨드 바이트를 읽는 커맨드(0x20) 전송
kOutPortByte( 0x64, 0x20 );
// 출력 포트의 데이터를 기다렸다가 읽음
for( i = 0 ; i < 0xFFFF ; i++ ){
// 출력 버퍼(포트 0x60)가 차 있으면 데이터를 읽을 수 있음
if( kIsOutputBufferFull() == TRUE ){
break;
}
}
// 출력 포트(포트 0x60)에 수신된 커맨드 바이트 값을 읽음
bOutputPortData = kInPortByte( 0x60 );
// 마우스 인터럽트 비트 활성화한 뒤, 커맨드 바이트 전송
// 마우스 인터럽트 비트(비트 1) 설정
kOutputPortData |= 0x02;
// 커맨드 레지스터(0x64)에 커맨드 바이트를 쓰는 커맨드(0x60)를 전달
kOutPortByte( 0x64, 0x60 )
// 입력 버퍼(포트 0x60)에 데이터가 비어 있으면,
// 출력 포트에 값을 쓰는 커맨드와 커맨드 바이트 전송
for( i = 0 ; i < 0xFFFF ; i++ ){
// 입력 버퍼(포트 0x60)가 비었으면 커맨드 전송 가능
if( kIsInputBufferFull() == FALSE ){
break;
}
}
// 입력 버퍼(0x60)에 마우스 인터럽트 비트가 1로 설정된 값을 전달
kOutPortByte( 0x60, bOutputPortData );
}
마우스 인터럽트 핸들러를 추가하는 예
// 마우스 인터럽트 핸들러 함수
void kMouseHandler( int iVectorNumber )
{
``` 핸들러 함수 내용 ```
}
// ISR에 추가할 내용
extern kMouseHandler
; #44, 마우스 ISR
kISRMouse:
KSAVECONTEXT
; 핸들러에 인터럽트 번호를 삽입하고 핸들러 호출
mov rdi, 44
call kMouseHandler
KLOADCONTEXT
iretq
표준 PS/2 마우스는 마우스 이동 정보를 3byte로 나누어 전달하며, 마우스 버튼 상태와 X축, Y축 이동 거리로 구성된다.
마우스 이동 정보를 저장하는 마우스 자료구조의 코드
typedef struct kMousePacketStruct
{
// 버튼 상태와 X, Y 값에 관련된 플래그
BYTE bButtunStatusAndFlag;
// X축 이동 거리
BYTE bXMovement;
// Y축 이동 거리
BYTE bYMovement;
} MOUSEDATA;
마우스를 관리하는 자료구조의 코드
typedef struct kMouseManagerStruct
{
// 자료구조 동기화를 위한 스핀락
SPINLOCK stSpinLock;
// 현재 수신된 데이터의 개수
// 마우스 데이터가 3개이므로 0~2의 범위를 계속 반복
int iByteCount;
// 현재 수신 중인 마우스 데이터
MOUSEDATA stCurrentData;
} MOUSEMANAGER;
마우스 초기화 함수의 코드
// 마우스 큐의 최대 크기
#define MOUSE_MAXQUEUECOUNT 100
// 마우스 상태를 관리하는 마우스 매니저
static MOUSEMANAGER gs_stMouseManager = { 0, }
// 마우스를 저장하는 큐와 버퍼의 정의
static QUEUE gs_stMouseQueue;
static MOUSEDATA gs_vstMouseQueueBuffer[ MOUSE_MAXQUEUECOUNT ];
// 마우스 초기화
BOOL kInitializeMouse( void )
{
// 큐 초기화
kInitializeQueue( &gs_stMouseQueue, gs_vstMouseQueueBuffer, MOUSE_MAXQUEUECOUNT, sizeof( MOUSEDATA ) );
// 스핀락 초기화
kInitializeSpinLock( &( gs_stMouseManager.stSpinLock ) );
// 마우스 활성화
if( kActivateMouse() == TRUE ){
// 마우스 인터럽트 활성화
kEnableMouseInterrupt();
return TRUE;
}
return FALSE;
}
마우스 데이터를 모아, 마우스 자료구조 단위로 큐에 삽입하는 함수의 코드
BOOL kAccumulateMouseDataAndPutQueue( BYTE bMouseData ){
BOOL bResult;
// 수신된 바이트 수에 따라, 마우스 데이터를 설정
switch( gs_stMouseManager.iByteCount ){
// 바이트 1에 데이터 설정
case 0:
gs_stMouseManager.stCurrentData.bButtonStatusAndFlag = bMouseData;
gs_stMouseManager.iByteCount++;
break;
// 바이트 2에 데이터 설정
case 1:
gs_stMouseManager.stCurrentData.bXMovement = bMouseData;
gs_stMouseManager.iByteCount++;
break;
// 바이트 3에 데이터 설정
case 2:
gs_stMouseManager.stCurrentData.bYMovement = bMouseData;
gs_stMouseManager.iByteCount++;
break;
// 그 외의 경우는 수신된 바이트 수 초기화
default:
gs_stMouseManager.iByteCount = 0;
break;
}
// 3byte가 모두 수신되었으면, 마우스 큐에 삽입하고 수신된 횟수를 초기화
if( gs_stMouseManager.iByteCount >= 3 ){
// 임계 영역 시작
kLockForSpinLock( &( gs_stMouseManager.stSpinLock ) );
// 마우스 큐에 마우스 데이터 삽입
bResult = kPutQueue( &gs_stMouseQueue, &gs_stMouseManager.stCurrentData );
// 임계 영역 끝
kUnlockForSpinLock( &( gs_stMouseManager.stSpinLock ) );
// 수신된 바이트 수 초기화
gs_stMouseManager.iByteCount = 0;
}
return bResult;
}
마우스 큐에서 마우스 데이터를 꺼내는 함수의 코드
BOOL kGetMouseDataFromMouseQueue( BYTE* pbButtonStatus, int* piRelativeX, int* piRelativeY )
{
MOUSEDATA stData;
BOOL bResult;
// 큐기 비어 있으면, 데이터를 꺼낼 수 없음
if( kIsQueueEmpty( &( gs_stMouseQueue ) ) == TRUE ){
return FALSE;
}
// 임계 영역 시작
kLockForSpinLock( &( gs_stMouseManager.stSpinLock ) );
// 큐에서 데이터를 꺼냄
bResult = kGetQueue( &( gs_stMouseQueue ), &stData );
// 임계 영역 끝
kUnlockForSpinLock( &( gs_stMouseManager.stSpinLock ) );
// 데이터를 꺼내지 못했으면 실패
if( bResult == FALSE ){
return FALSE;
}
// 마우스 데이터 분석
// 마우스 버튼 플래그는 첫 번째 바이트의 하위 3비트에 존재함
*pbButtonStatus = stData.bButtonStatusAndFlag & 0x7;
// X, Y의 이동 거리 설정
// X의 부호 비트는 비트 4에 있으며, 1로 설정되어 있으면 음수임
*piRelativeX = stData.bXMovement & 0xFF;
if( stData.bButtonStatusAndFlag & 0x10 )
{
// 음수이므로, 아래 8비트에 X 이동 거리를 설정한 후
// 상위 비트를 모두 1로 만들어 부호 비트를 확장
*piRelativeX |= ( 0xFFFFFF00 );
}
// Y의 부호 비트는 비트 5에 있으며, 1로 설정되었으면 음수
// 아래 방향으로 갈수록 Y 값이 증가하는 화면 좌표와 달리 마우스는 위쪽 방향으로
// 갈수록 값이 증가하므로 계산이 끝난 후 부호를 뒤집음
*piRelativeY = stData.bYMovement & 0xFF;
if( stData.bButtonStatusAndFlag & 0x20 ){
// 음수이므로 아래 8비트에 Y 이동 거리를 설정한 후
// 상위 비트를 모두 1로 만들어 부호 비트를 확장함
*piRelativeY |= ( 0xFFFFFF00 );
}
// 마우스의 Y축 증감 방향은 화면 좌표와 반대이므로 Y이동 거리에 -하여 방향을 바꿈
*piRelativeY = -*piRelativeY;
return TRUE;
}
마우스 인터럽트 핸들러 코드
vvoid kMouseHandler( int iVectorNumber )
{
char vcBuffer[] = "[INT: , ]";
static int g_iMouseInterruptCount = 0;
BYTE bTemp;
int iIRQ;
//==========================================================
// 인터럽트가 발생했음을 알리려고 메시지를 출력하는 부분
// 인터럽트 벡터를 화면 왼쪽 위에 두 자리 정수로 출력
vcBuffer[ 5 ] = '0' + iVectorNumber / 10;
vcBuffer[ 6 ] = '0' + iVectorNumber % 10;
// 발생한 횟수 출력
vcBuffer[ 8 ] = '0' + g_iMouseInterruptCount;
g_iMouseInterruptCount = ( g_iMouseInterruptCount + 1 ) % 10;
kPrintStringXY( 0, 0, vcBuffer );
//==========================================================
// 출력 버퍼(포트 0x60)에 수신된 데이터가 있는지 여부를 확인하여
// 읽은 데이터를 키 큐 또는 마우스 큐에 삽입
if( kIsOutputBufferFull() == TRUE ){
// 마우스 데이터가 아니면 키 큐에 삽입
if( kIsMouseDataInOutputBuffer() == FALSE ){
// 출력 버퍼(포트 0x60)에서 키 스캔 코드를 읽는 용도의 함수지만
// 키보드와 마우스 데이터를 출력 버퍼를 공통으로 사용하므로
// 마우스 데이터를 읽는데도 사용 가능
bTemp = kGetKeyboardScanCode();
// 키 큐에 삽입
kConvertScanCodeAndPutQueue( bTemp );
}
// 마우스 데이터이면, 마우스 큐에 삽입
else
{
// 출력 버퍼(포트 0x60)에서 키 스캔 코드를 읽는 용도의 함수지만
// 키보드와 마우스 데이터는 출력 버퍼를 공통으로 사용하므로
// 마우스 데이터를 읽는데도 사용 가능
bTemp = kGetKeyboardScanCode();
// 마우스 큐에 삽입
kAccumulateMouseDataAndPutQueue( bTemp );
}
}
// 인터럽트 벡터에서 IRQ 번호 추출
iIRQ = iVectorNumber - PIC_IRQSTARTVECTOR;
// EOI 전송
kSendEOI( iIRQ );
// 인터럽트 발생 횟수를 업데이트
kIncreaseInterruptCount( iIRQ );
// 부하 분산 처리
kProcessLoadBalancing( iIRQ );
}