실제로 한 번에 3 자리 값을 처리 할 수 있습니다. 이렇게하면 레지스터 크기에 제한받지 않고 임의의 길이의 문자열을 처리 할 수 있습니다. 왜 외계인이 거대한 길이의 4 진수의 ascii 문자열을 사용하여 우리와 통신하려고 시도하지 않는 한, 왜 그렇게해야하는지 확신 할 수 없습니다. 일어날 수 있습니다.
어느 방향으로나 (오른쪽에서 왼쪽 또는 왼쪽에서 오른쪽으로) 번역 할 수 있습니다. 그러나 다음 중 하나의 문제가 있습니다.
RtL을 처리하는 경우 시작하기 전에 출력 문자열의 길이를 알아야합니다 (계산할 때 숫자를 쓸 수 있도록). 이것은 할 수 있지만 조금 까다 롭습니다. 가장 간단한 용어로, 길이는 ((strlen (Q) + 2)/3) * 2. 거의입니다. 그러나 처음에는 많은 경우에 공백으로 끝날 수 있습니다. "1"뿐만 아니라 "10"은 공백을 줄 것입니다. "20"은하지 않습니다. 올바른 값을 계산할 수 있지만 성가시다.
마찬가지로 LtR 처리도 비슷한 문제가 있습니다. 숫자를 쓰는 위치를 알아내는 문제는 없지만 고려해보십시오. 문자열을 "123"으로 변환하면 변환이 간단합니다 (33 진수). 그러나 처리를 시작하면 전체 문자열은 "1231"(155 진수)입니까? 이 경우 "0"(01 55)처럼 처리해야합니다. IOW, 자릿수는 3 개 그룹으로 처리 될 수 있지만 자릿수가 3으로 균등하게 나눠지지 않는 초기 사례를 처리해야합니다.
일반적으로 숙제를 게시하는 것은 피하는 방법입니다. 그러나 나는 당신이 당신의 '해결책'으로 이것을 돌릴 것이라는 점을 의심하고 있으며, 구글이 비슷한 것을 필요로하는 누군가를 이곳으로 보낼 수도있다. 주의 할
몇 가지가 :
- 이 코드는 (쉽게 테스트를했다) 마이크로 소프트의 fastcall를 사용하여 C에서 호출 할 의도 MASM로 컴파일됩니다.
- 32 비트 (내 환경)로 작성되었지만 특별히 32 비트가 필요한 것은 없습니다. 당신이 8086을 목표로 삼고 있다고 말했기 때문에 나는 '진보 된'지시를 피하려고 노력했습니다. 16 비트 또는 64 비트로 변환해도 많은 문제가 발생하지 않습니다.
- 왼쪽에서 오른쪽으로 처리됩니다.
- 잘 작성된 루틴과 마찬가지로 매개 변수의 유효성을 검사합니다. 입력 문자열의 유효하지 않은 숫자와 같은 오류가있는 길이가 0 인 문자열을 출력합니다.
- 출력 버퍼가 NULL이면 오류가 발생합니다. 오류가있을 때 bool을 반환 할 수 있다고 가정합니다 (현재 void를 반환합니다).
- 코드가 더 엄격해질 수 있다고 확신하지만 (항상 그렇지는 않습니까?) "숙제 프로젝트 품질"에 대해서는 합리적인 것처럼 보입니다.
그 밖의 주석은 코드를 설명해야합니다.
.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 자 외계인 군주를 환영합니다.
여기 의도가 명확하지 않습니다. 4 진수를 문자열로 입력하려고하고 8 진수 문자열로 인쇄해야합니까? 나는 이것이 어떤 유형의 학교 프로젝트라고 가정한다. (왜 다른 것을 asm으로 작성 하는가?) 그래서 itoa와 같은 함수를 사용하는 것은 의문의 여지가 없다. –
예 .exactly 사용자는 4 진 문자열을 입력해야하며 프로그램은 8 진수 문자열을 인쇄합니다. –
"16 자리 숫자의 경우"- 16 비트 8086이 단일 레지스터에서 보유 할 수있는 최대 값은 (십진수) 65535 (5 자리)입니다. 이것은 (4 차) 33333333 (8 자리) 및 (8 진수) 177777 (6 자리)입니다. 16 자리 숫자를 지원하려면이 작업이 흥미로운 프로젝트가 될 것입니다. 4 진수에서 직접 8 진수를 계산하는 간단한 방법이있을 수 있지만 그것을 볼 수는 없습니다. 각 (3 비트) 8 진수는 해당하는 (2 비트) 4 진수와 그 옆에있는 숫자의 일부를 포함합니다. 전체 quanternary 숫자를 reg에로드하고 구문 분석하는 것이 더 간단해야합니다. –