[英]Why doesn't this simple assembly program return a number?
為什么這個簡單的匯編程序不返回數字?
我正在嘗試使它打印12
進制,但其打印亂碼。
global _start
section .text
_start:
mov eax, 0x4
mov ebx, 0x1
mov ecx, var1
mov edx, 2
int 0x80
;exit the program gracefully
mov eax, 0x1
mov ebx, 0x5
int 0x80
section .data
var1: db 0x12
您遇到的主要絆腳石試圖將0x12
(ASCII 18)的值寫入stdout
。 快速檢查ASCII圖表將發現此值不可打印。 在匯編中,您只能將字符寫入stdout
。 這就是說,當面對一個數字值時 ,您必須將該值分成數字,然后將數字轉換為ASCII表示形式(通過將'0'
或0x30
或48
位十進制加)。
這與以C或任何其他語言將數字手動轉換為字符串沒有什么不同。 您基本上可以將原始數字重復除以10
,每次將剩余的數字作為數字保存到緩沖區中,然后以相反的順序寫出緩沖區,以將數字的ASCII表示形式寫到stdout
(SO上有很多示例)。
在看一個將所有數字分隔到緩沖區並將它們寫入stdout
的示例之前,讓我們僅介紹示例中的一些基礎知識並使其打印任何內容。 您需要牢記在腦中的匯編的第一個概念是,匯編中的所有標簽(變量)都指向一個內存地址,而不是數字值。 在裝配中分配標簽時,例如:
var1 db 0x12
您將 在 var1
指向的存儲位置中存儲一個單字節數據,其值為12-hex
。 var1
是指向該內存位置的指針。 在nasm中,如果要在該位置上操作/或引用該值 ,則必須通過將指針括在[ ]
來將指針解除引用到地址(就像在C中使用*var1
解除引用指針一樣)。
鞏固的下一個概念是sys_write (syscall 4)
期望在ecx
存儲的起始內存地址而不是值。 然后,它將edx
數據字節寫入ebx
(1- stdout
)中存儲的文件描述符中。 mov
荷蘭國際集團的地址var1
到edx
將使我們能夠在解引用該地址的值通過對操作[edx]
要將單個數字轉換為ASCII值,請在其上添加'0'
(或0x30),但是如果僅在[edx]
添加'0'
,結果將是什么? (當前值0x12
(18)+ 0x30
(48)= 0x42
(66)-恰好是ASCII'B 'B'
)
(我們還將作弊,並在var1
的末尾添加一個newline
( 0xa
),並將其0xa
2個字節,這樣我們就不必再為單獨的調用弄亂了。)
將其與您的示例放在一起將導致:
section .text
global _start
_start:
mov eax, 4 ; linux sys_write
mov ebx, 1 ; stdout
mov ecx, var1 ; mov address of var1 to ecx
add byte [ecx], 0x30 ; add '0' to first byte of var1
mov edx, 2 ; number of chars to print
int 0x80 ; syscall
mov eax, 0x1 ; __NR_exit 1
xor ebx, ebx ; exit code of 0
int 0x80
section .data
var1 db 0x12, 0xa ; ASCII 18 (non-printable) with newline
編譯並運行該程序將導致:
$ ./convert
B
現在我們來看一個完整的示例,它將存儲在var1
處的值的每個數字分開,然后將0x12
(十進制18)的每個數字打印到stdout
然后newline
。 (輸出轉換為十六進制表示形式的工作就交給您了-您可以在網上搜索幾個示例, 然后就會想到int 80h.org 。)
nasm中的一個簡單示例是:
section .text
global _start
_start:
mov edi, result ; address of buffer in destination index
xor eax, eax ; zero out eax
mov al, [var1] ; put the value of var1 in al to divide
mov bl, 10 ; base 10 divisor to find remainder
; separate remainder digits into result buffer
remloop:
div bl ; divide current value by 10
mov [edi], ah ; move the remainder to result
cmp al, 0 ; is the quotient zero?
je printchar ; if it is we are done
xor ah, ah
inc edi ; move offset in result string (note digits
jmp remloop ; of answer are stored in reverse order)
printchar:
add byte [edi], 0x30 ; add ascii '0' to digit to get printable char
mov eax, 4 ; linux sys_write
mov ebx, 1 ; stdout
mov ecx, edi ; point to current digit
mov edx, 1 ; number of chars to print
int 0x80 ; syscall
dec edi ; point to next digit
cmp edi, result ; are we past the final digit?
jge printchar ; if not, keep printing to stdout
; print newline
mov eax, 4 ; linux sys_write
mov ebx, 1 ; stdout
mov ecx, newline ; address of newline
mov edx, 1 ; number of chars to print
int 0x80 ; syscall
; exit the program gracefully
mov eax, 0x1 ; __NR_exit 1
mov ebx, 0x5 ; exit code of 5
int 0x80
section .data
var1 db 0x12 ; ASCII 18 (non-printable)
result times 8 db 0 ; 8 byte buffer for result
newline db 0xa ; newline character
如果您構建代碼:
nasm -f elf -o convert.o convert.asm
ld -m elf_i386 -o convert convert.o
然后,您可以顯示0x12
或18
ASCII的ASCII輸出:
$ ./convert
18
有關匯編的出色Web參考,請參見匯編語言編程的藝術 。 閱讀。 所有的。 就是那樣 盡管它主要是為8086編寫的,但所有原理都可100%適用於當前的匯編編程。 唯一的區別是x86_64的寄存器大小,調用約定和系統調用號。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.