2016-11-30 6 views
1

현재 하드 드라이브의 부트 섹터에서 실행할 수있는 약간의 게임을하고 있습니다. 이것은 내 프로그램이 16 비트 리얼 모드로 실행된다는 것을 의미하며 컴파일러 플래그를 설정하여 순수한 i386 코드를 생성합니다. 저는 C++로 게임을 작성하고 있지만 인터럽트 호출을 통해 BIOS와 대화하기 위해 많은 인라인 어셈블리가 필요합니다. 이러한 호출 중 일부는 32 비트 정수를 반환하지만 두 개의 16 비트 레지스터에 저장됩니다. 현재 내가 어셈블리에서 내 전화 번호를 얻기 위해 다음과 같은 일을 해요 :인라인 어셈블리의 두 레지스터에서 값의 상위 및 하위 부분을 검색하려면 어떻게합니까?

auto getTicks = [](){ 
    uint16_t ticksL{ 0 }, ticksH{ 0 }; 
    asm volatile("int $0x1a" : "=c"(ticksH), "=d"(ticksL) : "a"(0x0)); 
    return static_cast<uint32_t>((ticksH << 16) | ticksL); 
}; 

이것은 내가 틱 수를 반환 this interrupt function를 호출하는 데 사용하는 람다 함수입니다. 나는 시간 데이터를 얻는 더 좋은 방법이 있다는 것을 알고 있으며, 자정인지 확인하기 위해 AL에 대한 점검을 구현하지 않았지만, 그것은 또 다른 주제입니다.

두 개의 16 비트 값을 사용하고 레지스터 값을 별도로 구한 다음 return 문에서 볼 수있는대로 32 비트 숫자로 결합해야합니다.

코드에서 해당 데이터를 하나의 32 비트 숫자로 가져 와서 shift 및 bitwise-or를 피할 수있는 방법이 있습니까? 필자가 액세스하는 16 비트 레지스터는 실제 32 비트 레지스터의 상위 16 비트에 불과하지만 전체 32 비트 레지스터에 액세스하는 방법을 알지 못합니다.

+3

'CX'와 'DX'는 서로 다른 레지스터의 하위 16 비트이므로 32 비트 레지스터에 이들을 넣으려면 'shift'와 'or'와 같은 트위 들링을해야합니다. C 코드를 사용 했으므로 컴파일러에서 코드를 작성하는 가장 좋은 방법을 찾아 내도록하십시오. – Jester

+0

아, 그것들은 다른 레지스터입니다. 나는 그들이 같은 레지스터의 상위 16 비트보다 하위 16 비트라고 생각했기 때문에 이것을보다 효율적인 방식으로 작동시킬 수 있다고 생각했습니다. 그것들이 실제로 다른 레지스터라면, 나는이 작업들을 피할 수 없을 것입니다. – adam10603

+0

상위 16 비트는 직접 접근 할 수 없습니다. – Jester

답변

4

필자가 액세스하고있는 16 비트 레지스터는 32 비트 레지스터의 실제 상위 32 비트 레지스터와 하위 16 비트 레지스터이지만 실제 32 비트 레지스터에 액세스하는 방법은 알지 못합니다. 전체적으로 등록하십시오.

Jester가 이미 지적했듯이 이들은 사실 2 개의 별도 레지스터이므로 "원래 32 비트 레지스터"를 검색 할 수있는 방법은 없습니다.

다른 한 점 : 그 인터럽트는 ax 레지스터를 수정합니다 ('지난 자정'플래그를 반환 함). 그러나 asm은 gcc에 ax를 변경한다고 알리지 않습니다. midnight 또한 uint16_t 것을

asm volatile("int $0x1a" : "=c"(ticksH), "=d"(ticksL), "=a"(midnight) : "a"(0x0)); 

참고 :이 같은 제안 수 있습니다.

+0

입/출력에' "+ a"(자정)'제약 조건을 사용할 수 있으며 어셈블러 템플릿 전에 자정 변수를 0으로 설정할 수 있습니다. –

0

dx : ax 레지스터 쌍을 double-wide 값으로 참조하는 'A'제약 조건을 찾고 있습니다. x86 in the gcc documentation에 대해 정의 된 전체 제약 조건 집합을 볼 수 있습니다. 불행히도 다른 레지스터 쌍에는 아무런 제약이 없으므로 두 값으로 가져 와서 shift 나 and로 재구성해야합니다.

+0

내 데이터가'CX'와'DX'에 있고,''A'' 제약 조건이 가리키는 것이 아닙니다. 그렇기 때문에 런타임에 이들 데이터를 합칠 필요가있는 것처럼 보입니다. – adam10603

2

다른 답변으로 DX와 CX를 32 비트 레지스터에 직접로드 할 수 없다는 것이 좋습니다. 당신이 제안한대로 그들을 결합해야합니다.


이 경우 대안이 있습니다. INT 1Ah/AH=0h을 사용하는 대신 32 비트 DWORD 값에 대해 메모리가 부족한 BIOS Data Area (BDA)을 읽고 32 비트 레지스터에로드 할 수 있습니다. 이것은 i386 프로세서의 리얼 모드에서 허용됩니다. 관심의 두 개의 메모리 주소 :

40:6C dword Daily timer counter, equal to zero at midnight; 
       incremented by INT 8; read/set by INT 1A 
40:70 byte Clock rollover flag, set when 40:6C exceeds 24hrs 

이 두 개의 메모리 주소는 segment:offset 형식으로되어 있지만, 실제 주소 0x0046C 및 0x00470에 해당 될 것이다.

는 C/C++ 포인터를 사용하여 낮은 메모리에서 값을 검색 일시적으로 (이전 값을 저장)을 DS 레지스터를 0으로 설정해야 할 CLI와 인터럽트를 끌 것 모두, 다시 활성화 STI으로 인터럽트하고 DS을 이전에 저장된 값으로 복원하십시오. 물론 이것은 부팅 섹터에서 INT 1Ah/AH = 0h을 사용하는 것과 비교하여 오버 헤드가 가중되지만 BIOS가 사용자를 대신하여 읽고 쓰는 메모리 주소에 직접 액세스 할 수 있습니다.

참고 : DS이 이미 0으로 설정되어 있으면이를 저장/설정/복원 할 필요가 없습니다. C++ 코드를 호출하기 전에 환경을 설정하는 코드가 표시되지 않기 때문에 기본 세그먼트 값이 무엇인지 알 수 없습니다. 롤오버 값과 타이머 값을 모두 가져올 필요가 없으며 개별적으로 가져오고 자한다면 CLI/STI을 제거 할 수 있습니다.

+0

이것에 대해 확실합니까? 내가 ([여기]를 읽고 있었다 https://books.google.com/books?id=WKaekAHjQ1EC&pg=PA345&lpg=PA345&dq=read+time+from+bios+data+area+cli&source=bl&ots=A2RR15gn4f&sig=wznVw6ygHSTr8O_LPln6wQ6B4bc&hl=en&sa=X&ved = 0ahUKEwiR-Ozi6NTQAhVS_mMKHenRCsoQ6AEILzAD # v = 한 페이지 및 q = 상태 바이트에 대한 업데이트를보아야 만했던 ("진짜 시간 클럭"에 대한 단락으로 스크롤) 20 % 20 % 20 % 20 % 20 % 20 % 20 % 읽는 것이 언제 안전한지 알아 내기 위해. cli를 사용하면 아마도 시계가 느려질 수 있습니까? –

+0

@DavidWohlferd :이 메서드는 실제로 하드웨어 시계를 쿼리하지 않습니다. 언급 한 2 가지 값은 기본 BIOS Int 8h (IRQ0) 처리기로 실제로 업데이트됩니다. 그러나 IRQ0 인터럽트 처리기는 실시간 클럭 하드웨어에 액세스 할 수 있지만 BIOS는 처리 후에이 두 값을 사용할 수있게합니다. 데이터 포트를 통해 RTC를 쿼리한다면 타이밍에 대해 걱정해야합니다. –