汇编版hello world
Wednesday, October 26, 2022
本文共1087字
3分钟阅读时长
⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/tricks/%E6%B1%87%E7%BC%96%E7%89%88hello-world/。商业转载请联系作者获得授权,非商业转载请注明出处!
Prejudice is a burden that confuses the past, threatens the future and renders the present inaccessible.
— Maya Angelou
一个非常简易的向用户读取输入姓名,将其显示在屏幕上,同时用hello world向其问候的程序在C语言中极其简便,但是在汇编中我们不得不去手动安排栈段与数据段,我们还得时刻注意栈的变化,防止从函数返回时无法返回到正确的位置
源代码
assume cs:code, ds:data, ss:stack
data segment
StringToPrint db "Hello World!", 13, 10, "$"
StringForRead db "Please Input Your Name:", "$"
Input db 0ah, 20 dup("$")
data ends
stack segment
db 128 dup (0)
stack ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 128
mov bp, sp
mov ax, offset StringForRead
push ax
call read
mov ax, offset StringToPrint
push ax
call puts
mov ah,4ch
int 21h
puts:
pop si
pop dx
push si
push bp
mov bp, sp
mov ah, 09h
int 21h
mov sp, bp
pop bp
ret
read:
pop si
pop ax
push si
push bp
mov bp, sp
push ax
call puts
mov di, offset Input + 1
mov bx, 17
get:
mov cx, bx
jcxz ok
mov ah, 01h
int 21h
mov ch, 0
mov cl, al
sub cl, 0dh
jcxz ok
mov ds:[di], al
inc di
dec bx
jmp get
ok:
show:
mov al, ","
mov ds:[di], al
mov ax, offset INPUT
push ax
call puts
mov sp, bp
pop bp
ret
code ends
end start
输出结果:
分析
在数据段中我们定义了我们需要用到的各种数据,同时还有一段固定长度用于保存姓名的空间(在实际上的C程序中,局部变量是放在栈中的,通过将rsp减一定的值,在rbp和rsp之间空出一段空间来存储变量)
data segment
StringToPrint db "Hello World!", 13, 10, "$"
StringForRead db "Please Input Your Name:", "$"
Input db 0ah, 20 dup("$") ;用来保存输入
data ends
stack segment
db 128 dup (0) ;栈的总大小为128个字节
stack ends
然后我们定义了类似C语言中的main函数:
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
mov sp, 128
mov bp, sp ;备份sp
mov ax, offset StringForRead ;将需要的数据入栈
push ax
call read ;调用read函数
mov ax, offset StringToPrint
push ax
mov bp, sp
call puts
mov ah,4ch
int 21h
然后我们去实现这两个函数
PUTS:
puts:
pop si ;call的时候会将下一条指令地址进栈
pop dx ;取出参数
;实际上64位系统的参数保存至寄存器中,
;而32位系统的参数保存在栈中,
;详情见前面的笔记
push si ;重新进栈让ret指令能够返回
push bp ;备份bp
mov bp, sp ;备份sp
mov ah, 09h ;09h告诉dos系统打印一个字符串
int 21h ;打印
mov sp, bp ;恢复sp的值
pop bp ;恢复bp的值
;这两句话相当于leave指令
ret ;相当于pop rip
;返回call的下一条指令的地址
READ:
read:
pop si
pop ax ;取出参数
push si
push bp ;备份
mov bp, sp
push ax ;要调用函数的话还要将参数进栈
;这里如果不先出栈再进栈
;那么参数会变成call下一条指令地址
call puts
mov di, offset Input + 1 ;越过开头的换行符
mov bx, 17 ;通过bx控制最多能输入的字符数
;(去掉前后两个换行符,终止符)
get:
mov cx, bx ;将bx的值传给cx
jcxz ok ;如果cx=0就跳到ok
;相当于如果bx=0就跳出
mov ah, 01h ;01h代表读取单个字符
int 21h
mov ch, 0 ;将cx高位设为0
mov cl, al
sub cl, 0dh ;如果输入回车符cx就为0
jcxz ok ;所以输入回车就结束
mov ds:[di], al ;将字符保存在变量空间中
inc di ;相当于增加数组指针
dec bx ;减少bx,直至0
jmp get ;继续读取输入
ok:
show:
mov al, "," ;在名字后加逗号
mov ds:[di], al
mov ax, offset INPUT
push ax ;将要puts打印的字符串地址入栈
call puts
mov sp, bp ;恢复sp
pop bp ;恢复bp
ret ;返回
扫码阅读此文章
点击按钮复制分享信息
点击订阅