6

アセンブラーで整数を出力する方法を探しています (私が使用しているコンパイラーは Linux の NASM です) が、いくつかの調査を行った後、本当に実行可能な解決策を見つけることができませんでした。この目的に役立つ基本的なアルゴリズムの説明を見つけることができ、それに基づいて次のコードを開発しました。

global _start

section .bss
digit: resb 16
count: resb 16
i: resb 16

section .data

section .text

_start:
mov             dword[i], 108eh         ; i = 4238
mov             dword[count], 1
L01:
mov             eax, dword[i]
cdq
mov             ecx, 0Ah
div             ecx  
mov             dword[digit], edx

add             dword[digit], 30h       ; add 48 to digit to make it an ASCII char
call            write_digit

inc             dword[count]

mov             eax, dword[i]
cdq
mov             ecx, 0Ah
div             ecx  
mov             dword[i], eax 
cmp             dword[i], 0Ah  
jg              L01

add             dword[i], 48            ; add 48 to i to make it an ASCII char
mov             eax, 4                  ; system call #4 = sys_write
mov             ebx, 1                  ; file descriptor 1 = stdout
mov             ecx, i                  ; store *address* of i into ecx
mov             edx, 16                 ; byte size of 16
int             80h

jmp             exit

exit:
mov             eax, 01h                ; exit()
xor             ebx, ebx                ; errno
int             80h

write_digit:
mov             eax, 4                  ; system call #4 = sys_write
mov             ebx, 1                  ; file descriptor 1 = stdout
mov             ecx, digit              ; store *address* of digit into ecx
mov             edx, 16                 ; byte size of 16
int             80h
ret

私が達成したいもののC#バージョン(明確にするため):

static string int2string(int i)
{
    Stack<char> stack = new Stack<char>();
    string s = "";

    do
    {
        stack.Push((char)((i % 10) + 48));
        i = i / 10;
    } while (i > 10);

    stack.Push((char)(i + 48));

    foreach (char c in stack)
    {
        s += c;
    }

    return s;
}

問題は、文字を逆に出力することです。そのため4238、出力は8324. 最初は、x86スタックを使用してこの問題を解決し、数字を押し込んでポップアウトし、最後に出力できると思っていましたが、その機能を実装しようとすると失敗し、取得できなくなりました出力。

その結果、整数を出力するという目標を達成するために、このアルゴリズムにスタックを実装する方法について少し当惑しています。利用可能な場合は、よりシンプルで優れたソリューションにも興味があります (これは私の最初のアセンブラー プログラムの 1 つであるため)。

4

4 に答える 4

7

1つのアプローチは、再帰を使用することです。この場合、数値を10で割って(商と余りを取得)、表示する数値として商を使用して自分自身を呼び出します。次に、余りに対応する桁を表示します。

この例は次のとおりです。

;Input
; eax = number to display

    section .data
const10:    dd 10
    section .text

printNumber:
    push eax
    push edx
    xor edx,edx          ;edx:eax = number
    div dword [const10]  ;eax = quotient, edx = remainder
    test eax,eax         ;Is quotient zero?
    je .l1               ; yes, don't display it
    call printNumber     ;Display the quotient
.l1:
    lea eax,[edx+'0']
    call printCharacter  ;Display the remainder
    pop edx
    pop eax
    ret

もう1つのアプローチは、除数を変更して再帰を回避することです。この例は次のとおりです。

;Input
; eax = number to display

    section .data
divisorTable:
    dd 1000000000
    dd 100000000
    dd 10000000
    dd 1000000
    dd 100000
    dd 10000
    dd 1000
    dd 100
    dd 10
    dd 1
    dd 0
    section .text

printNumber:
    push eax
    push ebx
    push edx
    mov ebx,divisorTable
.nextDigit:
    xor edx,edx          ;edx:eax = number
    div dword [ebx]      ;eax = quotient, edx = remainder
    add eax,'0'
    call printCharacter  ;Display the quotient
    mov eax,edx          ;eax = remainder
    add ebx,4            ;ebx = address of next divisor
    cmp dword [ebx],0    ;Have all divisors been done?
    jne .nextDigit
    pop edx
    pop ebx
    pop eax
    ret

この例では先行ゼロは抑制されませんが、簡単に追加できます。

于 2012-11-23T05:49:24.690 に答える
1

たぶん、スタックを実装することはこれを行うための最良の方法ではないと思います(そして、どのようにそれを行うかを理解できると本当にpop思いmovますsp、したがって、メモリを割り当てて、レジスタの1つを新しい「スタックポインタ」として設定するだけで、好きな場所にスタックを実際にセットアップできます)。Cスタイルのヌル区切り文字列に実際にメモリを割り当て、使用するのと同じアルゴリズムでintを文字列に変換する関数を作成し、結果を別の関数に渡すと、このコードはより明確でモジュール化できると思いますこれらの文字列を印刷できます。これは、あなたが苦しんでいるスパゲッティ コード シンドロームの一部を回避し、問題を解決して起動します。私に実演してもらいたい場合は、聞いてください。ただし、上記のことを書いた場合は、より分割されたプロセスで方法を理解できると思います.

于 2012-11-23T05:46:48.880 に答える
0
; eax = number to stringify/output
; edi = location of buffer

intToString:
    push  edx
    push  ecx
    push  edi
    push  ebp
    mov   ebp, esp
    mov   ecx, 10

 .pushDigits:
    xor   edx, edx        ; zero-extend eax
    div   ecx             ; divide by 10; now edx = next digit
    add   edx, 30h        ; decimal value + 30h => ascii digit
    push  edx             ; push the whole dword, cause that's how x86 rolls
    test  eax, eax        ; leading zeros suck
    jnz   .pushDigits

 .popDigits:
    pop   eax
    stosb                 ; don't write the whole dword, just the low byte
    cmp   esp, ebp        ; if esp==ebp, we've popped all the digits
    jne   .popDigits

    xor   eax, eax        ; add trailing nul
    stosb

    mov   eax, edi
    pop   ebp
    pop   edi
    pop   ecx
    pop   edx
    sub   eax, edi        ; return number of bytes written
    ret
于 2012-11-23T07:57:11.003 に答える