2016-10-05 3 views
3

저는 어셈블리를 배우기 시작했고 OS 클래스의 일부로 간단한 부트 로더를 만들고 있습니다. 코드를 좀 더 효율적으로 만들려고합니다. 즉, 지금까지 내가 한 일이 내가 원하는 것을 성취 할 수있는 좋은 방법이라고 생각하지 않습니다. 즉, 나는 이것을 수행하는 가장 효율적인 방법이라고 생각하는 점프/브랜치/룩업 테이블을 문서화하는 온라인 자원을 찾기 위해 고심하고있다.cmp/je를 많이 사용하는 대신 어셈블리 점프/분기/찾아보기 테이블?

내가 달성하고자하는 것을 설명하기 위해 0에서 4까지 dx 레지스터의 값을 반환하는 함수를 호출하고 있습니다. 현재는 cmp 명령어를 사용하여 값을 비교하고 값이 같으면 조건부 je을 점프하십시오. 만약 내가 더 높은 수준의 언어로 이것을 작성했다면, 나는 더 효율적인 switch 문장을 사용하는 대신 본질적으로 복수의 if 문장을 차례대로 수행 할 것이다.

그래서 여기 내가 지금 뭘하는지입니다 :이있다

cmp dx, 1   
je .F_1 
cmp dx, 2 
je .F_2 
cmp dx, 3 
je .F_3 
cmp dx, 4 
je .F_4 
cmp dx, 0 
je .F_5 
jmp RangeError_Handler 

.F1: 
    mov si, msg1 
    jmp F_Exit 
.F2: 
    mov si, msg2 
    jmp F_Exit 
... ; .F3 and .F4 follow the pattern 

.F5:    ; special case 
    mov si, msg_error 
    call PrintLn 
    hlt 

F_Exit: 
    call PrintLn 
    ...   ; and do something else 


msg1: db 'Message 1', 0 
msg2: ... 
... 

이 작업을 수행 할 수있는 더 좋은 방법이 될 수 있습니다. 나의지도 교사는 점프 테이블이 이상적이라고 암시했지만 나에게 그것이 어셈블리에서 어떻게 작동할지에 대한 어떤 종류의 추가 설명을 줄 시간이 없었습니다. 그래서 누군가가 일종의 예제를 제공 할 수 있다면 매우 감사 할 것입니다. 내 상황.

이론적으로 dx의 값을 확인한 다음 5 개의 개별 시간을 확인하는 대신 특정 함수로 이동하는 함수가 하나 있는데 어셈블리에서이를 구현하는 방법을 알 수 없습니다. 문자열에 조회 테이블을 사용하는 것이 더 효율적입니까? 즉, 1의 반환 값은 테이블의 문자열 1을 나타 냅니까?

+0

수정 사항으로 인해 질문과 대답 모두에 버그가 생기고 논리가 매우 단순 해지면서 특별한 경우를 가진 다른 사람들이이 질문과 답변이 해당 사례에 적용되지 않는다고 생각할 수 있습니다. 나는 그것을 다시 정상적인 질문으로 되돌려 놓았다고 생각한다. 나는 당신의 코드가 무엇을하는지보기가 힘들지 않았기 때문에 그것을 되 돌리는 것을 고려했다. (그리고 A20의 내용을 확인했다는 사실은 MichaelPetch가 CS가 설정되었는지 확인하는 데 유용한 관찰을하도록 만들었습니다.) 코드는 따라하기 쉽다면 완전히 일반적인 것일 필요는 없습니다. –

답변

5

대부분의 경우 다른 데이터로 동일한 지침이 있으므로 점프 테이블이 필요하지 않습니다. 문자열 테이블을 사용하면 서로 다른 명령을 실행해야하는 조건에 대해서만 점프하고 다른 데이터에는 동일한 명령을 사용하지 마십시오.

mov si, dx     ; SI can be used in addressing modes, DX can't 
    shl si      ; 16-bit doesn't allow scaled indices, so we can't just do [table + si*2]. And shl sets flags 
    cmp dx, 4 
    ja RangeError_Handler 

    mov si, [F_messages + si] 
     ; call PrintLn could be here, if it preserves DX or SI for us to test after 

    test dx,dx    ; detect the one special case. 
    jnz .F_Exit 

    ;; fall through only in the dx==0 case 
    call PrintLn 
RangeError_Handler: 
    hlt        ; Are interrupts disabled? if not, execution will continue after hlt 

.F_exit 
    call PrintLn 
    ... ; and do whatever else your code needs to do 


F_messages:    # char* F_messages[] 
    dw msg1, 
     msg2 
     ... 

조건부 점프 체인 대신 테이블을 사용하면 매우 광범위하게 적용 할 수 있습니다. 이것이 64 비트 x86 코드 또는 ARM 또는 MIPS 어셈블리 인 경우 로직은 거의 동일합니다. 또는 C 조 (좋은 C 컴파일러는 switch을 점프 테이블 대신 데이터에 대한 테이블 조회로 변환 할 수 있습니다).


당신은 지점의 양쪽 밖으로 call PrintLn을 반영 할 수 있지만, DX 또는 SI 하나를 보존 경우에만 가능합니다. 입력 값을 PUSH/POP하여 다시 테스트 할 수 있다면 가치가 없을 것입니다. 특수한 경우가 DX == 0이므로 (이 답변의 이전 버전과 같이 DX == 5가 아님), 한 CMP에서 FLAGS를 가진 JCC를 모두 수행 할 수 없습니다. 당신이 점프 테이블 만들고 싶어 않은 경우


는 : 대신 문자열 주소의 다음

jmp [jump_table + si] 


jump_table: 
    dw .F_1, .F_2, ... 

및 메모리에 코드 주소 테이블을 만들기 위해 DW를 사용합니다. 각각의 경우가 동일한 크기 (기계 코드 바이트 수)라면 포인터 테이블을 사용하지 않고 첫 번째 주소와 관련된 점프 거리를 계산하면됩니다.


확실히 당신은 CS이 절대 주소를 사용하기 전에 설정되어 알고 있는지 확인합니다. 정상 점프는 상대적이지만 간접 점프/호출은 절대 주소를 사용합니다. @ MichaelPetch의 주석이 지적했듯이, 코드의 어느 지점에서 FAR JMP가 CS를 설정합니다.

+3

하나의 관찰이 있습니다. 두 번째 코드 스 니펫에서와 같이 JMP 테이블을 사용할 경우 보호 모드로 들어가기 전에 부트 로더에서이 작업을 수행하고 있습니다 (A20 라인을 활성화하려고 시도하기 때문에 OP가있을 가능성이 큽니다). _CS_가 예상 한대로 설정되어 있는지 확인해야합니다. 나는이 문제에 관해서 [SO Q & A] (http://stackoverflow.com/questions/34548325/near-call-jump-tables-dont-always-work-in-a-bootloader)를 게시했다. 나는 IRL을 안다. 대부분의 사람들은 _CS_를 명시 적으로 설정하는 FAR JMP를 가지지 않으므로 결국 일부 환경에서는 JMP/CALL 테이블이 실패 할 수 있습니다. –

+0

감사합니다. 매우 유용합니다. 나는 당신이'SI '를 얼마나 멀리 이동 시켰는 지 명시하지 않았기 때문에'shl' 명령에 오타가 있다고 생각합니다. 그렇다면 SI가 왜 처음부터 비트 쉬프트가 되어야만하는지 완전히 이해하지 못합니까? – Jamie4840

+1

@ Jamie4840 : 8086은 단일 비트 시프트 또는 CL에 의한 시프 팅 만 지원합니다. 'shl si, 1'과 동일하지만 순수한 8086 어셈블러가 즉각적인 상수 피연산자를 지원하는지 확실하지 않았습니다. 386 호환 CPU에서 16 비트가 아니라이 8086을 태그했습니다. (일부 명령 집합 링크 및 기타 좋은 것들에 대해서는 [x86 tag wiki] (http://stackoverflow.com/tags/x86/info)를 참조하십시오. 또한 이와 동등합니다 :'add si, si' (하지만 깃발을 다르게 설정합니다). 2 바이트 요소의 테이블로 색인을 만들기 때문에 이동해야합니다. –

관련 문제