2016-12-14 25 views
1

4 진수를 8 진수로 변환하는 8086 프로세서 용 프로그램을 준비해야합니다.4 진수를 8 진수로 변환합니다. ASM 8086

내 생각 :

4의 지수로 모든 숫자를 곱 및 등록을 추가 할 수 있습니다. 나중에 첫 번째 단계에서 합계보다 높은 8의 가장 높은 지수를 확인합니다. 나머지가 0이 될 때까지 8의 지수로 나눕니다. 나눗셈의 모든 결과는 8 진수 중 하나의 숫자입니다. 그러나 16 자리 숫자의 경우 4의 마지막 지수는 4^15입니다. 나는 그것이 최적의 알고리즘이 아니라고 생각한다.

다른 방법이 있습니까? 어쩌면 2 진수로 3 자리 숫자로 그룹화 할 수 있습니다.

+0

여기 의도가 명확하지 않습니다. 4 진수를 문자열로 입력하려고하고 8 진수 문자열로 인쇄해야합니까? 나는 이것이 어떤 유형의 학교 프로젝트라고 가정한다. (왜 다른 것을 asm으로 작성 하는가?) 그래서 itoa와 같은 함수를 사용하는 것은 의문의 여지가 없다. –

+0

예 .exactly 사용자는 4 진 문자열을 입력해야하며 프로그램은 8 진수 문자열을 인쇄합니다. –

+0

"16 자리 숫자의 경우"- 16 비트 8086이 단일 레지스터에서 보유 할 수있는 최대 값은 (십진수) 65535 (5 자리)입니다. 이것은 (4 차) 33333333 (8 자리) 및 (8 진수) 177777 (6 자리)입니다. 16 자리 숫자를 지원하려면이 작업이 흥미로운 프로젝트가 될 것입니다. 4 진수에서 직접 8 진수를 계산하는 간단한 방법이있을 수 있지만 그것을 볼 수는 없습니다. 각 (3 비트) 8 진수는 해당하는 (2 비트) 4 진수와 그 옆에있는 숫자의 일부를 포함합니다. 전체 quanternary 숫자를 reg에로드하고 구문 분석하는 것이 더 간단해야합니다. –

답변

1

실제로 한 번에 3 자리 값을 처리 할 수 ​​있습니다. 이렇게하면 레지스터 크기에 제한받지 않고 임의의 길이의 문자열을 처리 할 수 ​​있습니다. 왜 외계인이 거대한 길이의 4 진수의 ascii 문자열을 사용하여 우리와 통신하려고 시도하지 않는 한, 왜 그렇게해야하는지 확신 할 수 없습니다. 일어날 수 있습니다.

어느 방향으로나 (오른쪽에서 왼쪽 또는 왼쪽에서 오른쪽으로) 번역 할 수 있습니다. 그러나 다음 중 하나의 문제가 있습니다.

RtL을 처리하는 경우 시작하기 전에 출력 문자열의 길이를 알아야합니다 (계산할 때 숫자를 쓸 수 있도록). 이것은 할 수 있지만 조금 까다 롭습니다. 가장 간단한 용어로, 길이는 ((strlen (Q) + 2)/3) * 2. 거의입니다. 그러나 처음에는 많은 경우에 공백으로 끝날 수 있습니다. "1"뿐만 아니라 "10"은 공백을 줄 것입니다. "20"은하지 ​​않습니다. 올바른 값을 계산할 수 있지만 성가시다.

마찬가지로 LtR 처리도 비슷한 문제가 있습니다. 숫자를 쓰는 위치를 알아내는 문제는 없지만 고려해보십시오. 문자열을 "123"으로 변환하면 변환이 간단합니다 (33 진수). 그러나 처리를 시작하면 전체 문자열은 "1231"(155 진수)입니까? 이 경우 "0"(01 55)처럼 처리해야합니다. IOW, 자릿수는 3 개 그룹으로 처리 될 수 있지만 자릿수가 3으로 균등하게 나눠지지 않는 초기 사례를 처리해야합니다.

일반적으로 숙제를 게시하는 것은 피하는 방법입니다. 그러나 나는 당신이 당신의 '해결책'으로 이것을 돌릴 것이라는 점을 의심하고 있으며, 구글이 비슷한 것을 필요로하는 누군가를 이곳으로 보낼 수도있다. 주의 할

몇 가지가 :

  1. 이 코드는 (쉽게 테스트를했다) 마이크로 소프트의 fastcall를 사용하여 C에서 호출 할 의도 MASM로 컴파일됩니다.
  2. 32 비트 (내 환경)로 작성되었지만 특별히 32 비트가 필요한 것은 없습니다. 당신이 8086을 목표로 삼고 있다고 말했기 때문에 나는 '진보 된'지시를 피하려고 노력했습니다. 16 비트 또는 64 비트로 변환해도 많은 문제가 발생하지 않습니다.
  3. 왼쪽에서 오른쪽으로 처리됩니다.
  4. 잘 작성된 루틴과 마찬가지로 매개 변수의 유효성을 검사합니다. 입력 문자열의 유효하지 않은 숫자와 같은 오류가있는 길이가 0 인 문자열을 출력합니다.
  5. 출력 버퍼가 NULL이면 오류가 발생합니다. 오류가있을 때 bool을 반환 할 수 있다고 가정합니다 (현재 void를 반환합니다).
  6. 코드가 더 엄격해질 수 있다고 확신하지만 (항상 그렇지는 않습니까?) "숙제 프로젝트 품질"에 대해서는 합리적인 것처럼 보입니다.

그 밖의 주석은 코드를 설명해야합니다.

.386 
.model flat 
.code 

; Call from C via: 
; extern "C" void __fastcall PrintOct(const char *pQuat, char *pOct); 

; On Entry: 
; ecx: pQuat 
; edx: pOct 

; On Exit: 
; eax, ecx, edx clobbered 
; all others preserved 
; If pOct is zero bytes long, an error occurred (probably invalid digits) 

@[email protected] PROC 

; ----------------------- 
; If pOct is NULL, there's nothing we can do 
    test edx, edx 
    jz Failed 

; ----------------------- 
; Save the registers we modify (except for 
; eax, edx and ecx which we treat as scratch). 
    push esi 
    push ebx 
    push edi 

    mov esi, ecx 
    mov edi, edx 
    xor ebx, ebx 

; ----------------------- 
; esi: pQuat 
; edi: pOct 
; ebx: zero (because we use lea) 
; ecx: temp pointer to pQuat 

; Reject NULL pQuat 
    test esi, esi 
    jz WriteNull 

; ----------------------- 
; Reject 0 length pQuat 
    mov bl, BYTE PTR [esi] 
    test bl, bl 
    jz WriteNull 

; ----------------------- 
; How many chars in pQuat? 
    mov dl, bl ; bl is first digit as ascii. Preserve it. 

CountLoop: 
    inc ecx ; One more valid char 

; While we're counting, check for invalid digits 
    cmp dl, '0' 
    jl WriteNull 
    cmp dl, '3' 
    jg WriteNull 

    mov dl, BYTE PTR [ecx] ; Read the next char 
    test dl, dl ; End of string? 
    jnz CountLoop 

    sub ecx, esi 

; ----------------------- 
; At this point, there is at least 1 valid digit, and 
; ecx contains # digits 
; bl still contains first digit as ascii 

; Normally we process 3 digits at a time. But the number of 
; digits to process might not be an even multiple of 3. 

; This code finds the 'remainder' when dividing ecx by 3. 
; It might seem like you could just use 'div' (and you can), 
; but 'div' is so insanely expensive, that doing all these 
; lines is *still* cheaper than a single div. 
    mov eax, ecx 
    mov edx, 0AAAAAAABh 
    mul edx 
    shr edx, 1 
    lea edx, [edx+edx*2] 
    sub ecx, edx ; This gives us the remainder (0-2). 

; If the remainder is zero, use the normal 3 digit load 
    jz LoadTriplet 

; ----------------------- 
; Build a triplet from however many leading 'odd' digits 
; there are (1 or 2). Result is in al. 

    lea eax, DWORD PTR [ebx-48] ; This get us the first digit 

; If there was only 1 digit, don't try to load 2 
    cmp cl, 1 
    je OneDigit 

; Load the other digit 

    shl al, 2 
    mov bl, BYTE PTR [esi+1] 
    sub bl, 48 
    or al, bl 

OneDigit: 

    add esi, ecx ; Update our pQuat pointer 
    jmp ProcessDigits 

; ----------------------- 
; Build a triplet from the next 3 digits. 
; Result is in al. 

; bl contains the first digit as ascii 
LoadTriplet: 

    lea eax, DWORD PTR [ebx-48] 

    shl al, 4 ; Make room for the other 2 digits. 

    ; Second digit 
    mov cl, BYTE PTR [esi+1] 
    sub cl, '0' 
    shl cl, 2 
    or al, cl 

    ; Third digit 
    mov bl, BYTE PTR [esi+2] 
    sub bl, '0' 
    or al, bl 

    add esi, 3 ; Update our pQuat pointer 

; ----------------------- 
; At this point 
; al: Triplet 
; ch: DigitWritten (initially zeroed when computing remainder) 
ProcessDigits: 

    mov dl, al 
    shr al, 3 ; left digit 
    and dl, 7 ; right digit 

    ; If we haven't written any digits, and we are 
    ; about to write a zero, skip it. This deals 
    ; with both "000123" and "2" (due to OneDigit, 
    ; the 'left digit' might be zero). 

    ; If we haven't written any digits yet (ch == 0), and the 
    ; value we are are about to write is zero (al == 0), skip 
    ; the write. 
    or ch, al 
    jz Skip1 

    add al, '0' ; Convert to ascii 
    mov BYTE PTR [edi], al ; Write a digit 
    inc edi ; Update pointer to output buffer 

    jmp Skip1a ; No need to check again 

Skip1: 
    or ch, dl ; Both check and update DigitWritten 
    jz Skip2 

Skip1a: 

    add dl, '0' ; Convert to ascii 
    mov BYTE PTR [edi], dl ; Write a digit 
    inc edi ; Update pointer to output buffer 

Skip2: 

; Load the next digit. 
    mov bl, BYTE PTR [esi] 
    test bl, bl 
    jnz LoadTriplet 

; ----------------------- 
; All digits processed. We know there is at least 1 valid digit 
; (checked on entry), so if we never wrote anything, the value 
; must have been zero. Since we skipped it to avoid 
; unnecessary preceding zeros, deal with it now. 

    test ch, ch 
    jne WriteNull 
    mov BYTE PTR [edi], '0' 
    inc edi 

; ----------------------- 
; Write the trailing NULL. Note that if the returned string is 
; 0 bytes long, an error occurred (probably invalid digits). 
WriteNull: 
    mov BYTE PTR [edi], 0 

; ----------------------- 
; Cleanup 
    pop edi 
    pop ebx 
    pop esi 

Failed: 
    ret 

@[email protected] ENDP 

end 

나는 0-4,294,967,295에서 모든 값뿐만 아니라 그것을 통해 1,000,000,000 급 숫자로 된 문자열을 실행했습니다. 일하는 것 같습니다.

우리의 새로운 4 자 외계인 군주를 환영합니다.