2017-03-12 1 views
2

현재 문제가 있습니다. 프로그램에 문제가 있는지 없는지 잘 모르겠습니다 만, 이것이 제가 100 %가 아니기 때문에 사용할 것입니다. 학습 기회로.'AL'바이트를 C 함수에 올바르게 푸시하는 방법은 무엇입니까?

키보드에서 스캔 코드를 읽으려면 다음 안내를 따르십시오 : in al, 0x60. 이 스캔 코드를 C로 작성된 함수로 보내려고합니다. C 함수 선언은 다음과 같습니다 : void cFunction(unsigned int scancode).

목표는 C 함수에 같은 값을 얻을 수 있습니다
in al, 0x60 
movzx EAX, AL 
push EAX 
call Cfunction 

: 0x10으로는 Q을 의미 누르면되었으며, 0x11을 그러니까 기본적으로, 여기

내가 뭘하는지입니다

    : W 누른, 0x12로는 ... 등등

    질문 E이며,3210

  • 함수에 올바른 값을 전달하는지 여부를 묻는 메시지가 표시됩니다.
  • EAX 대신 AX만을 입력하면 결과가 달라 집니까?
  • 단지 바이트AL이 필요하지만, 분명히 나는 ​​push AL이 될 수 없기 때문에, EAX으로 확장했습니다. 그래서 Q을 눌렀을 때 다음과 같이 비교했다고 가정 해 봅시다. if(scancode == 0x10)EAXAX이 푸시 되었더라도이 값이 올바르게 해석됩니까? 또는 스캔 코드에 AL의 값만 입력하면됩니까? 그렇지 않다면 어떻게 함수에 AL을 가져갈 수 있습니까?
+2

일반적으로 4 바이트를 푸시해야하기 때문에'axx' (어셈블러에서 허용되는 경우)를 원하지 않습니다.이 경우 C 함수는 4 바이트 부호없는 int를 기대합니다. 당신이 한 일은 옳았습니다. – Jester

+0

최신 OS가 있거나 USB 키보드를 사용하는 경우 작동하지 않습니다. – Olaf

+0

AL을 EAX로 확장하는 것이므로 RAX = EAX = AX = AL = scancode에 해당하는 가치가 있습니다. –

답변

2

답변은 calling convention에 따라 달라집니다. 표준 인 경우 cdecl 예보다 일반적으로 올바르게 처리합니다.

일부 노트 :

  1. 이가 해결되지 않은 크기 int보다 uint32_t처럼 바이트 정확한 크기로 C 데이터 유형을 사용하는 것이 좋습니다. 당신이 AX 대신 EAX을 사용하려면이 데이터 유형은 AL 이후
    void cFunction(uint16_t scancode).
  2. AX (및 EAX)의 일부가 낫다는 당신이 당신의 함수를 정의해야합니다, stdint.h

  3. 에 정의되어 있습니다 만 읽은 후 MOVZX으로 연장보다 키 스캔 코드를 읽기 전에 AX (또는 EAX) 소거.조립에 대한 일반적인 방법 :

    XOR은

    XOR EAX, EAX 
    

    자체에 등록 레지스터

    MOV EAX, 0 
    

    제로 이동도 정확하지만 XOR은 일반적으로 조금 더 빠른 (그리고 레지스터를 사용하여 지우기는 지금 일종의 전통입니다)

0

그래서 몇 가지가 있습니다 여기에 대해 이야기 해. 한 번에 하나씩 커버 해 봅시다.

먼저 OS가 없거나 (예 : 나만의 OS 만들기) 또는 I/O 방식으로 실행되지 않는 MS-DOS와 같은 OS에서 실행 중이라고 가정합니다. Olaf가 올바르게 지적한 것처럼 in al, 0x60은 최신 보호 모드 OS에서 작동하지 않습니다. 고전적인 PS/2 키보드를 제공하는 것으로 보이는 가상 머신이나 에뮬레이터에서 실행하지 않는 한 USB 키보드에서도 작동하지 않습니다.

둘째, 저는 32 비트 CPU에서 프로그래밍 중이라고 가정합니다. C ABI (응용 프로그램 바이너리 인터페이스)는 16 비트 CPU, 32 비트 CPU 및 64 비트 CPU에서 서로 다르므로 사용중인 CPU에 따라 코드가 다르게 읽혀집니다.

셋째, 포트 60h는 이상한 짐승이며, 제가 운전 기사를 작성한 이후로 오랜 시간이 걸렸습니다. 읽은 값은 많은 시간에 읽을 것으로 생각되는 값이 아니며 E0h 확장 코드가 있으며 Pause 키의 동작이 있습니다. 버그없는 키보드 드라이버를 작성하는 것은 보이는 것보다 훨씬 어렵습니다. 그러나이 질문에 대한 모든 것을 무시합시다.

그런데 OS가없고 32 비트 CPU이고 가장 기본적인 키 스트로크 만 있다고 가정 해 보겠습니다. 어떻게 키보드에서 C 함수로 데이터를 전달하겠습니까? 꽤 많이 했어.

in al, 0x60 
movzx eax, al 
push eax 
call cFunction 

왜이 부분이 맞습니까?

음, 첫번째 줄은 키보드로 8 비트 레지스터를로드합니다. 그리고는 8 비트 레지스터이므로 in에 쓸 수 있기 때문에 al이어야합니다.

32 비트 C ABI는 함수의 매개 변수가 역 호출 순서로 스택에 푸시 될 것으로 기대합니다. 그래서 C 함수를 호출하기 전에 그 앞에 push 명령이 있어야합니다. 그러나 32 비트 모드에서 모든 push 명령어는 32 비트 크기이므로 eax, ebx, esi, edi 등을 누를 수 있습니다. al을 직접 푸시 할 수 없습니다. 32 비트 모드에서 푸시 된 모든 항목은 4 바이트 경계에 정렬되어야하기 때문에 직접 스택 쓰기를 사용하여 기술적으로 정렬 할 수는 있습니다. 따라서 값이 먼저 8 비트에서 32 비트로 승격되며, movzx이이를 훌륭하게 수행합니다.

그럴 가치가있는 다른 방법이 있습니다.당신은 in 전에 eax을 취소 할 수 :

xor eax, eax 
in al, 0x60 
push eax 
call cFunction 

이 솔루션은 성능을 원래의 솔루션보다 조금 더 나쁘다; 부분 등록 스톨의 비용이 있습니다. 프로세서는 내부적으로 을 eax의 일부로 유지하지 않고 별도의 레지스터로 유지합니다. 레지스터 크기가 다른 여러 개의 하위 피스를 함께 섞으려는 시도는 프로세서가 스누핑을 수행 할 수 있기 전에 수행해야합니다. push eax 여기에서 프로세서는 al이 이전 명령에 의해 돌연변이가 발생하고 클럭 사이클이 빨리 al의 비트를 eax에 매시 했으므로 여전히 al 인 것처럼 보입니다. 실제로는 eax의 일부였습니다.

그것은 당신이 고전적인 16 비트 모드 (8086, 또는 '286 보호 모드)에 있다면, 호출 순서가 약간 다른 것을 지적 가치가

:

in al, 0x60 
movzx ax, al 
push ax 
call cFunction 

이 경우, int는 16 비트 크기이므로 16 비트로 모든 것을 정확합니다. 또한, 64 비트 모드로, 대신 rax를 사용해야합니다 :

in al, 0x60 
movzx rax, al 
push rax 
call cFunction 

cFunction는 int는 32 비트, 64 비트 모드 명령에서 스택 정렬 요구되는 컴파일되었을 수도 있지만있는 64 비트 값이 푸시됩니다. C 함수는 64 비트 값을 32 비트 값으로 올바르게 읽지 만 64 비트로만 푸시 할 수 있습니다.

그래서 당신은 그것을 가지고 있습니다. CPU 및 환경에 따라 포트 데이터를 사용자의 기능으로 가져 오기 위해 C ABI와 상호 작용하는 다양한 방법.

관련 문제