1

4 進数を 8 進数に変換する 8086 プロセッサ用のプログラムを準備する必要があります。

私の考え:

すべての桁に 4 の指数を掛けて、レジスタに追加します。後で、最初のステップの合計よりも大きくない 8 の最高指数を確認します。剰余が 0 になるまで 8 の指数で割ります。除算の結果はすべて 8 進数で 1 桁です。ただし、16 桁の数値の場合、4 の最後の指数は 4^15 です。最適なアルゴリズムではないと思います。

他に方法はありますか?たぶん、2進数にして3桁でグループ化します。

4

1 に答える 1

1

一度に 3 桁の値を実際に処理できることがわかりました。このようにすると、レジスタのサイズに制限されることなく、任意の長さの文字列を処理できます。エイリアンが巨大な長さの 4 進数の ASCII 文字列を使用して私たちと通信しようとしない限り、なぜ必要なのかわかりません。発生する可能性があります。

どちらの方法でも変換できます (右から左または左から右)。ただし、次のいずれかを行うには少し問題があります。

RtL を処理している場合は、開始する前に出力文字列の長さを知る必要があります (計算時に数字をどこに書き込むかがわかるようにするため)。これは実行可能ですが、少し注意が必要です。最も簡単に言えば、長さは (( strlen (Q) + 2) / 3) * 2です。ただし、多くの場合、先頭に空白が残ることがあります。「1」も「10」も空白になります。「20」はしません。正しい値を計算できますが、面倒です。

同様に、LtR の処理にも同様の問題があります。数字をどこに書き込むかという問題はありませんが、次のことを考慮してください。変換する文字列が「123」の場合、変換は簡単です (33 8 進数)。しかし、処理を開始し、完全な文字列が "1231" (155 8 進数) だったらどうなるでしょうか。その場合、「001231」(01 55)のように処理する必要があります。IOW、数字は 3 のグループで処理できますが、数字の数が 3 で均等に分割されない最初のケースを処理する必要があります。

宿題の解決策を投稿することは、通常、私が避けていることです。しかし、あなたがこれをあなたの「解決策」として提出しようとしているとは思えませんし、Google が似たようなものを必要とする誰かをここに送る可能性は (ほとんど) ありません。

注意すべき点がいくつかあります。

  1. このコードは、Microsoft の fastcall (テストが容易になった) を使用して C から呼び出され、masm でコンパイルされることを意図しています。
  2. 32bit(私の環境)で書いていますが、特に32bitを必要とするものはありません。あなたが 8086 をターゲットにしていると言ったので、私は「高度な」指示を避けようとしました。16 ビットまたは 64 ビットに変換することは、それほど難しいことではありません。
  3. 左から右に処理します。
  4. 適切に作成されたルーチンと同様に、パラメーターを検証します。入力文字列に無効な数字が含まれているなど、エラーが発生した場合は長さゼロの文字列を出力します。
  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)

@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 の 4 進数と、0 ~ 4,294,967,295 のすべての値を含む文字列を実行しました。うまくいくようです。

私は、新しい 4 桁のエイリアンの支配者を歓迎します。

于 2016-12-18T07:03:24.090 に答える