汇编学习笔记
- 作者: chaihahaha
- 时间:
- 分类: 计算机技术笔记
- 评论
masm汇编程序格式:; (cs) ~ code (ds) ~ data (ss) ~ stack
assume cs:code, ds:data, ss:stack
; 编译完后:
; ds:0 ~ ds+10h:0存放程序段前缀(PSP)
; cs:ip初始化为地址标号main
; data段从ds+10h:0开始,注意此时ds不指向data段
data segment ; d...
展开阅读
masm汇编程序格式:
; (cs) ~ code (ds) ~ data (ss) ~ stack
assume cs:code, ds:data, ss:stack
; 编译完后:
; ds:0 ~ ds+10h:0存放程序段前缀(PSP)
; cs:ip初始化为地址标号main
; data段从ds+10h:0开始,注意此时ds不指向data段
data segment ; data是一个地址标号
; db: define byte dw: define word dd: define double word
str db 'Hello World$' ; str是一个数据标号
; ((cs)<<4 + 0) ~ ((cs)<<4 + 11) = 'Hello World$'
arr dw 0123h, 0234h, 0345h, 0456h
; ((cs)<<4 + 12) = 0123h
; ((cs)<<4 + 14) = 0234h
; ((cs)<<4 + 16) = 0345h
; ((cs)<<4 + 18) = 0456h
; 数据的直接定址表
addr dd str, arr
; offset操作符取标号的偏移地址,seg操作符取标号的段地址
; 等价于addr dw offset str, seg a, offset b, seg b
table dw main, sub ; 子程序的直接定址表
data ends
stack segment
; 初始值为0的容量为256个字单元的栈段,初始栈顶(sp)=100h
dw 256 dup (0)
stack ends
code segment
main:
;;; 初始化栈段
mov ax, stack
mov ss, ax
mov sp, 100h
; 栈操作
push ax ; (sp)=(sp)-2 ((ss)<<4 + (sp)) = (ax)
pop bx ; (ax) = ((ss)<<4 + (sp)) (sp)=(sp)+2
pushf ; push flags
popf ; pop flags 可以用于修改标志寄存器
;;; 初始化数据段
mov ax, data
mov ds, ax
mov bx, 0
;;; 使用assume和以上初始化代码设置好数据段后可以使用数据标号访问数据,不能使用地址标号访问另一个段的数据
mov al, str[si] ; mov al, ds:[si]
add arr, ax ; add ds:[12], ax 代表单元arr可指示单元大小
;;; 常见运算
add ax, 1 ; (ax) += 1
add al, bl
adc ah, bh ; 带进位加法,这两条指令等价于add ax,bx
sub ax, 1 ; (ax) -= 1
sub al, bl
sbb ah, bh ; 带借位减法,这两条指令等价于sub ax,bx
and ax, 011b ; (ax) &= 011b
or ax, 011b ; (ax) |= 011b
inc ax ; (ax) = (ax) + 1
dec ax ; (ax) = (ax) - 1
div byte ptr [bx] ; (al) = (ax)//((ds)<<4 + (bx)) (ah) = (ax)%((ds)<<4 + (bx))
div word ptr [bx] ; (ax) = ((dx)*10000h + (ax))//((ds)<<4 + (bx)) (ax) = ((dx)*10000h + (ax))%((ds)<<4 + (bx))
mul byte ptr [bx] ; (ax) *= ((ds)<<4 + (bx))
mul word ptr [bx] ; (dx)*10000h + (ax) = (ax) * ((ds)<<4 + (bx))
; 逻辑移位
mov al,01010001b
mov cl,3 ; 必须用cl设置移位位数
shr al,cl ; 右移,左移是shl
;;; 内存访问使用ds和bx
mov ax, [0ffffh] ; 警告:含义不明(ax) = ffffh 首位为f时必须加0
mov al, ds:[0] ; (al) = ((ds)<<4 + 0) 直接寻址
mov al, [bx] ; 同上
mov al, ds:[bx] ; 同上
mov al, [bp] ; (al) = ((ss)<<4 + (bp)) 栈段寻址用bp
mov [bx], al ; ((ds)<<4 + (bx)) = (al) 寄存器间接寻址
mov [bx], ax ; ((ds)<<4 + (bx)) = (al) ((ds)<<4 + (bx) + 1) = (ah) 小心高位覆盖
mov ax, [2+bx] ; (ax) = ((ds)<<4 + (bx) + 2) 寄存器相对寻址(结构体[bx].idata,数组idata[si])
mov ax, 2[bx] ; 同上
mov ax, [bx].2 ; 同上
mov ax, [bx+si] ; (ax) = ((ds)<<4 + (bx) + (si)) 基址变址寻址(二维数组[bx][si])
mov ax, [bx][si] ; 同上
mov ax, [bx+si+2] ; (ax) = ((ds)<<4 + (bx) + (si) + 2) 相对基址变址寻址(表格中的数组[bx].idata[si],二维数组idata[bx][si]
mov ax, 2[bx][si] ; 同上
mov ax, [bx].2[si] ; 同上
mov ax, [bx][si].2 ; 同上
;;; 标号偏移地址可进行立即数运算
mov ax, offset main - offset data ; main, data都是地址标号
;;; 串传送指令
cld ; (df) = 0 向右传送, clear direction
std ; (df) = 1 向左传送, set direction
mov cx, 8 ; 根据(df)从(ds)段复制8个字节到(es)段,(df)=0时(si),(di)++,(df)=1时--
rep movsb ; s:movsb, loop s
mov cx, 8 ; 根据(df)从(ds)段复制8个字到(es)段,(df)=0时(si),(di)++,(df)=1时--
rep movsw ; s:movsw, loop s
;;; 循环
mov cx, 10 ; 使用cx设定循环次数
s: ; 循环体
inc ax
loop s ; (cx) -= 1 if (cx) == 0: then (cs) = seg s (ip) = offset s
;;; 无条件跳转jmp, 有条件跳转jcxz(jmp if cx is zero)
jmp short main ; 8位转移 (ip) += main - (ip_next)
jmp near main ; 16位段内转移
jmp far main ; 段间转移,指令机器码包含段地址和偏移地址 (cs)=seg main (ip)=offset main
jmp ax ; (ip) = (ax)
jmp word ptr [bx] ; (ip) = ((ds)<<4 + (bx))
jmp dword ptr [bx] ; (ip) = ((ds)<<4 + (bx)) (cs) = ((ds)<<4 + (bx) + 2)
jcxz main ; if (cx)==0: then jmp short main
cmp ax, bx ; 计算(ax)-(bx),比较结果用(zf)和(cf)表示
; 以下跳转指令检测无符号数cmp指令比较结果
je main ; (ax)==(bx)
jne main ; (ax)!=(bx)
jb main ; (ax)<(bx)
jnb main ; (ax)>=(bx)
ja main ; (ax)>(bx)
jna main ; (ax)<=(bx)
;;; 调用子程序call
call sub ; push IP, jmp near ptr sub
; call far ptr sub ; push CS, push IP, jmp far ptr sub
call ax ; push IP, jmp ax
call word ptr [bx] ; push IP, jmp word ptr [bx]
call dword ptr [bx] ; push CS, push IP, jmp dword [bx]
;;; 设置中断向量
mov ax, 0 ; 中断向量表的段为0
mov es, ax
; 字单元 0:n*4 保存n号中断向量偏移地址
mov word ptr es:[0*4], offset sub
; 字单元 0:n*4 + 2 保存n号中断向量段地址
mov word ptr es:[0*4+2], seg sub
;;; 可屏蔽外中断
sti ; (if)=1
cli ; (if)=0
;;; 从CMOS RAM时钟信息中读取当前分钟
mov al, 2 ; 选中2号CMOS RAM
out 70h, al ; 在CMOS端口写入2
in al, 71h ; 从CMOS端口读出数据(BCD码),0号对应秒,2号对应分,4号对应时,7号对应日,8号对应月,9号对应年
;;; 打印字符串
mov ax,data
mov ds,ax ; 设置数据段
mov dx,0 ; 设置打印起始位置偏移地址
mov ah,9 ; 参数9对应打印的中断子程序
int 21h ; 触发中断,打印到'$'为止
mov ah, 4ch ; 参数4ch对应程序返回的中断子程序
mov al, 0 ; 返回值
int 21h ; 引发21h号中断,返回dos
; 子程序保存恢复现场, 返回ret ref
sub:
push cx ; 保存寄存器,入栈
nop ; 1B的指令,什么也不干
pop cx ; 恢复寄存器,出栈
ret ; pop IP
; retf ; pop IP, pop CS
; iret ; 中断程序返回pop IP, pop CS, popf
code ends ; end segment
end main ; end main表示程序入口为main段
编译
打开dosbox,输入以下命令
mount c E:\learn_asm
c:
masm filename;
link filename;
debug filename.exe
debug
? ; 显示帮助信息
r ; 显示寄存器内容
r ax ; 修改寄存器ax内容
d ds:0 8 ; 从((ds)<<4+0)开始打印内存内容,打印到8
e ds:0 12 23 ; 从((ds)<<4+0)开始修改内存内容,输入12 23(16进制)
t ; 运行一步
p ; 运行一步(不进入)
g cs:0 8 ; 从代码段((cs)<<4+0)开始运行,运行到8
h ax bx ; 输出16进制加减((ax)+(bx))和((ax)-(bx))
u cs:0 8 ; 反编译,从((cs)<<4+0)到8
a cs:0 ; 从((cs)<<4 + 0) 开始输入汇编指令,并写入内存
q ; 退出
; 标志寄存器表示
of(OV, NV)
sf(NG, PL)
zf(ZR, NZ)
pf(PE, PO)
cf(CY, NC)
df(DN, UP)
寄存器及其功能:
寄存器(reg):ax,bx,cx,dx,ah,al,bh,hl,ch,cl,dh,dl,sp,bp,si,di
段寄存器(sreg):ds,ss,cs,es
标志寄存器(flags):zf(zero flag), pf(parity flag), sf(sign flag), cf(carry flag无符号数), of(overflow flag有符号数), df(direction flag), 单步中断(类型码为1)标志tf(trap flag), 可屏蔽外中断使能标志if(interrupt enable flag)
ds:数据段寄存器; ss:栈段寄存器;es,ds和ss只能用通用寄存器赋值; cs:代码段寄存器,只能用jmp修改; ip:代码偏移地址寄存器,不能赋值,只能用loop修改; sp:栈顶偏移地址寄存器,可以用立即数赋值; cx:loop指令的计数器; bx,si,di:数据段偏移地址; bp:栈段偏移地址; si,di:偏移地址增量
32位汇编示例程序
新建文件upper_lower.asm
,内容如下
include \masm32\include\masm32rt.inc
assume cs:codesg,ds:datasg
datasg segment
a db 'BaSiC',10,0
b db 'iNfOrMaTiOn',10,0
datasg ends
codesg segment
start:
mov ebx, offset a ; first string
mov ecx,5
s:
mov al,[ebx]
and al,11011111b ; lower to upper
mov [ebx],al
inc ebx
loop s
mov ebx, offset b ; second string
mov ecx,11
s0:
mov al,[ebx]
or al,00100000b ; upper to lower
mov [ebx],al
inc ebx
loop s0
call main
exit
main proc
cls
print offset a
print offset b
ret
main endp
codesg ends
end start
32位汇编编译环境
在www.masm32.com上下载编译器并安装,新建文件makeit.bat
,内容如下:
@echo off
if exist "upper_lower.obj" del "upper_lower.obj"
if exist "upper_lower.exe" del "upper_lower.exe"
\masm32\bin\ml /c /coff "upper_lower.asm"
if errorlevel 1 goto errasm
\masm32\bin\PoLink /SUBSYSTEM:CONSOLE "upper_lower.obj"
if errorlevel 1 goto errlink
dir "upper_lower.*"
goto TheEnd
:errlink
echo _
echo Link error
goto TheEnd
:errasm
echo _
echo Assembly Error
goto TheEnd
:TheEnd
pause
双击文件即可编译,在工作目录下Ctrl+L,输入cmd,输入upper_lower回车即可看到运行结果
生成C程序的汇编列表文件
gcc -Wa,-adhln -g source_code.c > assembly_list.lst
参数说明:
-g: 生成debug信息
-Wa,option: 把`option`作为参数传给汇编器
-adhln:
a: 生成列表
d: 忽略debug指令
n: 忽略列表处理
h: 包含高级语言代码include high-level source
l: 包含汇编代码