将四元数转换为八进制。 ASM 8086

时间:2016-12-14 06:57:19

标签: algorithm x86-16 octal number-systems

我必须为8086处理器准备程序,将四元数转换为八进制数。

我的想法:

将每个数字乘以4的指数并添加到寄存器。稍后检查最高指数8不高于第一步的总和。 除以8的指数直到余数等于0.除法的每个结果都是八进制的一位数。 但对于16位数字,最后一个指数为4是4 ^ 15。我认为这不是最佳算法。

还有其他方法吗?也许二进制和分组3位数。

1 个答案:

答案 0 :(得分:1)

事实证明,您确实可以一次处理3位数值。通过这种方式,您可以处理任意长度的字符串,而不受寄存器大小的限制。不确定为什么你可能需要,除非外星人尝试使用ascii字符串的四元数字与巨大的长度沟通。可能会发生。

可以以任何方式进行翻译(从右到左或从左到右)。然而,这两方面都有一些挑战:

如果您正在处理RtL,则需要在开始之前知道输出字符串的长度(以便在计算数字时知道在哪里写入数字)。这是可行的,但有点棘手。简单来说,长度是((strlen(Q)+ 2)/ 3)* 2. 几乎得到它。但是,对于许多情况,您最初可能会在开头有一个空格。 “1”以及“10”将给出空白区域。 “20”不会。可以计算出正确的值,但这很烦人。

同样,处理LtR也有类似的问题。你没有弄清楚在哪里写数字的问题,但考虑:如果要转换它的字符串“123”,那么转换很简单(33八进制)。但是,如果你开始处理,完整的字符串是“1231”(155八进制)怎么办?在这种情况下,你需要像“001231”(01 55)那样处理它。 IOW,数字可以以3为一组进行处理,但是您需要处理数字位数不均匀分为3的初始情况。

发布家庭作业的解决方案通常是我避免的事情。但是我怀疑你是否会把它作为你的'解决方案',并且谷歌可能会在这里发送需要类似东西的人(几乎不可能)。

有几点需要注意:

  1. 此代码旨在使用Microsoft的fastcall从C调用(它使测试更容易)并使用masm编译。
  2. 虽然它是用32位(我的环境)编写的,但没有什么特别需要32位的。既然你说你的目标是8086,我就试图避免任何“高级”指令。转换为16位甚至64位不应该是一个很大的挑战。
  3. 从左到右处理。
  4. 与任何编写良好的例程一样,它会验证其参数。它会在出错时输出零长度字符串,例如输入字符串中的无效数字。
  5. 如果输出缓冲区为NULL,它将崩溃。我想我可以在错误时返回一个bool(当前返回void),但是,我没有。
  6. 我确信代码可能更严格(不能总是这样吗?),但对于“家庭作业项目质量”,这似乎是合理的。
  7. 除此之外,该评论应解释代码。

    .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个外星人的外星人。