web-dev-qa-db-ja.com

アセンブリNASMで数値を印刷する方法は?

レジスタに整数があるとしたら、どうすればそれを出力できますか?簡単なサンプルコードを見せてもらえますか?

「hello、world」などの文字列を出力する方法はすでに知っています。

私はLinuxで開発しています。

17
AR89

すでにLinuxを使用している場合は、自分で変換を行う必要はありません。代わりに printf を使用してください:

;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf

main:

  mov eax, 0xDEADBEEF
  Push eax
  Push message
  call printf
  add esp, 8
  ret

message db "Register = %08X", 10, 0

printfcdecl呼び出し規約 を使用するため、後でスタックポインターを復元する必要があることに注意してください。つまり、関数に渡されるパラメーターごとに4バイトを追加します。

12
Martin

文字列に変換する必要があります。 16進数について話している場合、それは非常に簡単です。この方法で任意の数を表すことができます。

0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3

したがって、この番号がある場合は、私が示したように分割する必要があり、すべての「セクション」をASCII同等のものに変換します。
4つの部分の取得は、ビットマジックを使用して簡単に実行できます。特に、最初の4ビットに関心のある部分を右に移動してから、結果を0xfでANDし、残りの部分から分離します。これが私が意味することです(私たちが3を使いたいとは思っていません):

0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003

これで、単一の数値が得られたので、その数値をASCII値に変換する必要があります。数値が9以下の場合は、0を追加できますASCII値(0x30)、それが9より大きい場合、aのASCII値(0x61)を使用する必要があります。
ここにあります。今、それをコーディングする必要があります:

    mov si, ???         ; si points to the target buffer
    mov ax, 0a31fh      ; ax contains the number we want to convert
    mov bx, ax          ; store a copy in bx
    xor dx, dx          ; dx will contain the result
    mov cx, 3           ; cx's our counter

convert_loop:
    mov ax, bx          ; load the number into ax
    and ax, 0fh         ; we want the first 4 bits
    cmp ax, 9h          ; check what we should add
    ja  greater_than_9
    add ax, 30h         ; 0x30 ('0')
    jmp converted

greater_than_9:
    add ax, 61h         ; or 0x61 ('a')

converted:
    xchg    al, ah      ; put a null terminator after it
    mov [si], ax        ; (will be overwritten unless this
    inc si              ; is the last one)

    shr bx, 4           ; get the next part
    dec cx              ; one less to do
    jnz convert_loop

    sub di, 4           ; di still points to the target buffer

PS:私はこれが16ビットコードであることを知っていますが、それでも古いTASMを使用しています:P

PPS:これはIntel構文ですが、AT&T構文への変換は難しくありません。 ここ を参照してください。

12
BlackBear

Linux x86-64とprintf

main.asm

_default rel            ; make [rel format] the default, you always want this.
extern printf, exit    ; NASM requires declarations of external symbols, unlike GAS
section .rodata
    format db "%#x", 10, 0   ; C 0-terminated string: "%#x\n" 
section .text
global main
main:
    sub   rsp, 8             ; re-align the stack to 16 before calling another function

    ; Call printf.
    mov   esi, 0x12345678    ; "%x" takes a 32-bit unsigned int
    lea   rdi, [rel format]
    xor   eax, eax           ; AL=0  no FP args in XMM regs
    call  printf

    ; Return from main.
    xor   eax, eax
    add   rsp, 8
    ret
_

GitHubアップストリーム

次に:

_nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out
_

出力:

_0x12345678
_

ノート:

Cライブラリなしで16進数が必要な場合: 16進数をアセンブリで印刷

Ubuntu 18.10、NASM 2.13.03でテスト済み。

使用しているアーキテクチャ/環境によって異なります。

たとえば、Linuxで数値を表示したい場合、ASMコードはWindowsで使用するものとは異なります。

編集:

変換の例については [〜#〜] this [〜#〜] を参照してください。

1
AlQafir

私はアセンブリに比較的慣れていないので、これは明らかに最良の解決策ではありませんが、機能しています。主な関数は_iprintで、最初にeaxの数値が負かどうかをチェックし、負の場合はマイナス記号を出力します。その後、すべての数字に対して関数_dprintを呼び出して個々の数値を出力します。アイデアは次のとおりです。512が等しい場合、512 =(5 * 10 + 1)* 10 + 2 = Q * 10 + Rなので、次のように除算することにより、数値の最後の桁を見つけることができます。 10、そしてリマインダーRを取得しますが、ループでそれを行う場合、数字は逆の順序になるため、スタックを使用してプッシュし、その後、それらをstdoutに書き込むときに、正しい順序でポップアウトされます。

; Build        : nasm -f elf -o baz.o baz.asm
;                ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
    pushad        ; Push registers
    mov [c], eax  ; store ascii value at c
    mov eax, 0x04 ; sys_write
    mov ebx, 1    ; stdout
    mov ecx, c    ; copy c to ecx
    mov edx, 1    ; one character
    int 0x80      ; syscall
    popad         ; pop registers
    ret           ; bye
; writes a digit stored in eax to stdout 
_dprint:
    pushad        ; Push registers
    add eax, '0'  ; get digit's ascii code
    mov [c], eax  ; store it at c
    mov eax, 0x04 ; sys_write
    mov ebx, 1    ; stdout
    mov ecx, c    ; pass the address of c to ecx
    mov edx, 1    ; one character
    int 0x80      ; syscall
    popad         ; pop registers
    ret           ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
    pushad       ; Push registers
    cmp eax, 0   ; check if eax is negative
    jge Pos      ; if not proceed in the usual manner
    Push eax     ; store eax
    mov eax, '-' ; print minus sign
    call _cprint ; call character printing function 
    pop eax      ; restore eax
    neg eax      ; make eax positive
Pos:
    mov ebx, 10 ; base
    mov ecx, 1  ; number of digits counter
Cycle1:
    mov edx, 0  ; set edx to zero before dividing otherwise the
    ; program gives an error: SIGFPE arithmetic exception
    div ebx     ; divide eax with ebx now eax holds the
    ; quotent and edx the reminder
    Push edx    ; digits we have to write are in reverse order
    cmp eax, 0  ; exit loop condition
    jz EndLoop1 ; we are done
    inc ecx     ; increment number of digits counter
    jmp Cycle1  ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
    pop eax      ; pop up the digits we have stored
    call _dprint ; and print them to stdout
    dec ecx      ; decrement number of digits counter
    jz EndLoop2  ; if it's zero we are done
    jmp Cycle2   ; loop back
EndLoop2:   
    popad ; pop registers
    ret   ; bye
global _start
_start:
    nop           ; gdb break point
    mov eax, -345 ;
    call _iprint  ; 
    mov eax, 0x01 ; sys_exit
    mov ebx, 0    ; error code
    int 0x80      ; край
0
baz

数値表現については言わなかったので、基数が無ければ(もちろん大きすぎない)符号なし数値用に次のコードを書いたので、それを使用できます。

BITS 32
global _start

section .text
_start:

mov eax, 762002099 ; unsigned number to print
mov ebx, 36        ; base to represent the number, do not set it too big
call print

;exit
mov eax, 1
xor ebx, ebx
int 0x80

print:
mov ecx, esp
sub esp, 36   ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.

mov edi, 1
dec ecx
mov [ecx], byte 10

print_loop:

xor edx, edx
div ebx
cmp dl, 9     ; if reminder>9 go to use_letter
jg use_letter

add dl, '0'
jmp after_use_letter

use_letter:
add dl, 'W'   ; letters from 'a' to ... in ascii code

after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop

; system call to print, ecx is a pointer on the string
mov eax, 4    ; system call number (sys_write)
mov ebx, 1    ; file descriptor (stdout)
mov edx, edi  ; length of the string
int 0x80

add esp, 36   ; release space for the number string

ret

2の累乗の基数を持つ数値に対して最適化されておらず、printflibcを使用しません。

関数printは、数値を改行して出力します。数値文字列はスタック上に形成されます。 nasmでコンパイルします。

出力:

clockz

https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-Assembly-nasm

0
TigerTV.ru