将四进制转换为八进制。 ASM 8086
Converting quaternary to octal. ASM 8086
我必须为 8086 处理器准备将四进制数转换为八进制数的程序。
我的想法:
将每个数字乘以 4 的指数并添加到寄存器中。稍后检查 8 的最高指数不高于第一步的总和。
除以 8 的指数,直到余数为 0。每一个除法结果都是一位八进制数。
但是对于 16 位数字,4 的最后一个指数是 4^15。我想这不是最佳算法。
还有其他办法吗?也许二进制和 3 位分组。
事实证明,您确实可以一次处理 3 位数的值。通过这种方式,您可以处理任意长度的字符串,而不受寄存器大小的限制。不确定你为什么需要这样做,除非外星人试图使用具有巨大长度的四进制数字的 ascii 字符串与我们交流。可能会发生。
可以任意方式进行翻译(从右到左或从左到右)。但是,两者都有一点挑战:
如果你正在处理 RtL,你需要在开始之前知道输出字符串的长度(这样你就知道在计算数字时把数字写在哪里)。这是可行的,但有点棘手。用最简单的术语来说,长度是 ((strlen(Q) + 2) / 3) * 2。 almost 得到它。但是,在许多情况下,开头可能会出现空白 space。 “1”和“10”将给出空格 space。 “20”不会。可以计算出正确的值,但是很烦人
同样,处理LtR也有类似的问题。您没有弄清楚在哪里写数字的问题,但请考虑:如果将字符串转换为“123”,那么转换很简单(八进制 33)。但是,如果您开始处理,并且完整的字符串是“1231”(八进制 155)怎么办?在那种情况下,您需要像“001231”(01 55)一样处理它。 IOW,数字可以以 3 为一组进行处理,但是您需要处理初始情况,即数字的数量不能被 3 整除。
张贴家庭作业的答案通常是我避免的事情。然而,我怀疑你是否会把它作为你的 'solution,' 提交,并且 google 可能会(几乎)可能派人来这里需要类似的东西。
注意几点:
- 此代码旨在使用 Microsoft 的 fastcall(它使测试更容易)从 C 调用并使用 masm 编译。
- 虽然它是用32位写的(我的环境),但是里面没有什么特别需要32位的。既然你说你的目标是 8086,我就尽量避免任何 'advanced' 指令。转换为 16 位甚至 64 位应该不会有太大挑战。
- 它从左到右处理。
- 与任何编写良好的例程一样,它会验证其参数。它在错误时输出零长度字符串,例如输入字符串中的无效数字。
- 如果输出缓冲区为NULL,它会崩溃。我想我可以 return 一个错误的布尔值(目前 return 无效),但是,好吧,我没有。
- 我确定代码可以更紧凑(不能总是这样吗?),但对于 "homework project quality," 来说似乎是合理的。
除此之外,注释应该解释代码。
.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)
@PrintOct@8 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
@PrintOct@8 ENDP
end
我 运行 一个字符串,其中包含 1,000,000,000 个四进制数字以及 0-4,294,967,295 之间的所有值。似乎有效。
我欢迎我们新的 4 位数外星霸主。
我必须为 8086 处理器准备将四进制数转换为八进制数的程序。
我的想法:
将每个数字乘以 4 的指数并添加到寄存器中。稍后检查 8 的最高指数不高于第一步的总和。 除以 8 的指数,直到余数为 0。每一个除法结果都是一位八进制数。 但是对于 16 位数字,4 的最后一个指数是 4^15。我想这不是最佳算法。
还有其他办法吗?也许二进制和 3 位分组。
事实证明,您确实可以一次处理 3 位数的值。通过这种方式,您可以处理任意长度的字符串,而不受寄存器大小的限制。不确定你为什么需要这样做,除非外星人试图使用具有巨大长度的四进制数字的 ascii 字符串与我们交流。可能会发生。
可以任意方式进行翻译(从右到左或从左到右)。但是,两者都有一点挑战:
如果你正在处理 RtL,你需要在开始之前知道输出字符串的长度(这样你就知道在计算数字时把数字写在哪里)。这是可行的,但有点棘手。用最简单的术语来说,长度是 ((strlen(Q) + 2) / 3) * 2。 almost 得到它。但是,在许多情况下,开头可能会出现空白 space。 “1”和“10”将给出空格 space。 “20”不会。可以计算出正确的值,但是很烦人
同样,处理LtR也有类似的问题。您没有弄清楚在哪里写数字的问题,但请考虑:如果将字符串转换为“123”,那么转换很简单(八进制 33)。但是,如果您开始处理,并且完整的字符串是“1231”(八进制 155)怎么办?在那种情况下,您需要像“001231”(01 55)一样处理它。 IOW,数字可以以 3 为一组进行处理,但是您需要处理初始情况,即数字的数量不能被 3 整除。
张贴家庭作业的答案通常是我避免的事情。然而,我怀疑你是否会把它作为你的 'solution,' 提交,并且 google 可能会(几乎)可能派人来这里需要类似的东西。
注意几点:
- 此代码旨在使用 Microsoft 的 fastcall(它使测试更容易)从 C 调用并使用 masm 编译。
- 虽然它是用32位写的(我的环境),但是里面没有什么特别需要32位的。既然你说你的目标是 8086,我就尽量避免任何 'advanced' 指令。转换为 16 位甚至 64 位应该不会有太大挑战。
- 它从左到右处理。
- 与任何编写良好的例程一样,它会验证其参数。它在错误时输出零长度字符串,例如输入字符串中的无效数字。
- 如果输出缓冲区为NULL,它会崩溃。我想我可以 return 一个错误的布尔值(目前 return 无效),但是,好吧,我没有。
- 我确定代码可以更紧凑(不能总是这样吗?),但对于 "homework project quality," 来说似乎是合理的。
除此之外,注释应该解释代码。
.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)
@PrintOct@8 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
@PrintOct@8 ENDP
end
我 运行 一个字符串,其中包含 1,000,000,000 个四进制数字以及 0-4,294,967,295 之间的所有值。似乎有效。
我欢迎我们新的 4 位数外星霸主。