《汇编语言基础教程》 学习实录
书目:《Guide to assembly language. A concise introduction》 by James T.Streib
时间紧迫,这里仅记录我学习过程中写的一些示例代码,每个示例代码都有对应C语言版本。长时间不用难免忘记,希望能通过一些简单示例快速拾起intel汇编语法。
IDE:VS2017
Intel x86汇编环境搭建与配置
第3章 算术运算指令
COMMENT @
#include<stdio.h>
int main(){
int volts, ohms, amperes;
printf("\n%s","Enter the number of volts: ");
scanf("%d", &volts);
printf("%s","Enter the number of ohms: ");
scanf("%d", &ohms);
amperes = volts / ohms;
printf("\n%s%d\n\n", "The number of amperes is:", amperes);
}
@
.386
.model flat, C
.stack 100h
includelib msvcrt.lib
printf proto arg1:Ptr Byte, printlist:VARARG
scanf proto arg1:Ptr Byte, printlist:VARARG
.data
volts SDWORD ?
ohms SDWORD ?
amperes SDWORD ?
in1fmt Byte "%d",0
msg1fmt Byte 0Ah,"%s",0
msg2fmt Byte "%s",0
msg3fmt Byte 0Ah,"%s%d",0Ah,0Ah,0
msg1 Byte "Enter the number of volts: ",0
msg2 Byte "Enter the number of ohms: ",0
msg3 Byte "The number of amperes is:",0
.code
start1:
INVOKE printf, ADDR msg1fmt, ADDR msg1
INVOKE scanf, ADDR in1fmt, ADDR volts
INVOKE printf, ADDR msg2fmt, ADDR msg2
INVOKE scanf, ADDR in1fmt, ADDR ohms
mov eax, volts
cdq
idiv ohms
mov amperes, eax
INVOKE printf, ADDR msg3fmt,ADDR msg3, amperes
ret
end start1
第4章 选择结构
COMMENT @ C语言表述
#include<stdio.h>
int main(){
int voltage;
printf("%s", "Enter an AC Voltage: ");
scanf("%d", &voltage);
if (voltage >= 110 && voltage <= 120)
printf("\n%s\n", "Voltage is Acceptable");
else {
printf("\n%s\n", "Warning!");
if(voltage < 110)
printf("%s\n", "Voltage too Low");
else
printf("%s\n", "Voltage too High");
}
if(voltage >=380)
printf("%s", "Generator power off...");
printf("\n");
return 0;
}
@
; 以下是上述C代码的汇编语言表述
; MASM32 intelx86 Debugger
; =============================================
.listall
.386
.model flat, c
.stack 100h
includelib msvcrt.lib
scanf PROTO :PTR Byte, :VARARG
printf PROTO :PTR Byte, :VARARG
.data
in1fmt Byte "%d",0
msg1fmt Byte "%s",0
msg1 Byte "Enter an AC Voltage: ",0
msg2fmt Byte 0Ah,"%s",0Ah,0
msg2 Byte "Voltage is Acceptable",0
msg3 Byte "Warning!",0
msg3fmt Byte "%s",0Ah,0
msg4 Byte "Voltage too Low",0
msg5 Byte "Voltage too High",0
msg4fmt Byte 0Ah,0
msg6 Byte "Generator power off...",0
voltage SDWORD ?
.code
main PROC
INVOKE printf, ADDR msg1fmt, ADDR msg1
INVOKE scanf, ADDR in1fmt, ADDR voltage
if01: cmp voltage, 110
jl else01
cmp voltage, 120
jg else01
then01: INVOKE printf, ADDR msg2fmt, ADDR msg2
jmp endif01
else01: nop
INVOKE printf, ADDR msg2fmt, ADDR msg3
if02: cmp voltage, 110
jge else02
then02: INVOKE printf, ADDR msg3fmt, ADDR msg4
jmp endif02
else02: INVOKE printf, ADDR msg3fmt, ADDR msg5
endif02: nop
endif01: nop
.if voltage >= 380
INVOKE printf, ADDR msg1fmt, ADDR msg6
nop ;这里要有一个补位的,否则会抛出未知异常
.endif
INVOKE printf, ADDR msg4fmt
ret
main ENDP
END main
第5章 迭代结构
-
INVOKE指令破坏寄存器eax、ecx和edx的值,故用ecx计数时需要用到一个内存变量存储计数值
-
loop指令使用时ecx的值不能为0或负值
-
完整代码示例:实现幂函数
COMMENT @
#include<stdio.h>
int main() {
int x, n, i, ans;
printf("%s", "Enter x: ");
scanf_s("%d", &x);
printf("%s", "Enter n: ");
scanf_s("%d", &n);
if (x < 0 || n < 0)
printf("\n%s\n\n", "Error: Negative x and/or y");
else
if (x == 0 && n == 0)
printf("\n%s\n\n", "Error: Undefined answer");
else {
i = 1;
ans = 1;
while (i <= n) {
ans = ans * x;
++i;
}
printf("\n%s%d\n\n", "The answer is: ", ans);
}
return 0;
}
@
.listall
.386
.model flat, c
.stack 100h
includelib msvcrt.lib
printf PROTO :Ptr Byte, :VARARG
scanf PROTO :Ptr Byte, :VARARG
.data
x SDWORD ?
n SDWORD ?
i SDWORD ?
ans SDWORD ?
msg1fmt Byte "%s",0
msg2fmt Byte 0Ah,"%s",0Ah,0Ah,0
msg3fmt Byte 0Ah,"%s%d",0Ah,0Ah,0
in1fmt Byte "%d",0
msg1 Byte "Enter x: ",0
msg2 Byte "Enter n: ",0
msg3 Byte "Error: Negative x and/or y",0
msg4 Byte "Error: Undefined answer",0
msg5 Byte "The answer is: ",0
.code
main PROC
INVOKE printf, ADDR msg1fmt, ADDR msg1
INVOKE scanf, ADDR in1fmt, ADDR x
INVOKE printf, ADDR msg1fmt, ADDR msg2
INVOKE scanf, ADDR in1fmt, ADDR n
if01: cmp x, 0
jge else01
OR01: cmp n, 0
jge else01
then01: nop
INVOKE printf, ADDR msg2fmt, ADDR msg3
else01: nop
if02: cmp x, 0
jne else02
cmp n, 0
jne else02
then02: nop
INVOKE printf, ADDR msg2fmt, ADDR msg4
else02: nop
mov i, 1
mov ans, 1
mov ecx, i
w01: cmp ecx, n
jg endw01
mov eax, ans
imul x
mov ans, eax
inc ecx
jmp w01
endw01: nop
mov i, ecx
INVOKE printf, ADDR msg3fmt, ADDR msg5, ans
endif02: nop
endif01:nop
ret
main ENDP
END main
第6章 逻辑运算指令、移位指令、循环移位指令和堆栈
-
逻辑运算:
and
\or
\xor A, B
-
逻辑移位:左
shl
\shr A, B
右 -
测试比特位:
test A, B
A值不变 ||and A, B
,A值会变 -
算术移位:左
sal A, B
\sar A, B
右,sal
与shl
完全一样,但sar
与shr
不一样,考虑负数的情况即可 -
循环移位:左
rol A, B
\ror A, B
右,一般移动一整轮使得值复原 -
堆栈:
push reg\mem\imm
;pop reg\mem
; 注意:①只能16位或32位寄存器/内存空间可使用;②pop
不能取立即数堆栈应用:排序算法中的交换值
方法一 (较慢) 方法二: 中等 方法三:较快push num1 mov eax, num1 mov eax, num1 push num2 xchg eax, num2 mov edx, num2 pop num1 mov num1, eax mov num1, edx pop num2 mov num2, eax
原因:指令执行速度 mov > xchg > push/pop,但从方法一到方法三,使用的寄存器数分别为0、1、2,所以折中考虑方法二
-
章节实例:模拟OCR设备,检测文档状态字节(每一位都代表一个状态)
COMMENT @ bit ERROR STATE 0 SHORT DOCUMENT 1 LONG DOCUMENT 2 CLOSE FEED 3 MULTIPLE FEED 4 EXCESSIVE SKEW 5 DOCUMENT MISFEED 6 DOCUMENT JAM 7 UNSPECIFIED ERROR @ .listall .386 .model flat,stdcall .stack 100h includelib msvcrt.lib printf PROTO C :PTR Byte, :VARARG scanf PROTO C :PTR Byte, :VARARG .data msg1fmt Byte "%s",0 in1fmt Byte "%x",0 msg2fmt Byte "%s%x",0Ah,0Ah,0 msg1 Byte 0Ah,"Enter a hexadecimal number: ",0 msg2 Byte "The hexadecimal number is: ",0 msgshort Byte "SHORT DOCUMENT",0Ah,0 msglong Byte "LONG DOCUMENT",0Ah,0 msgclose Byte "CLOSE FEED",0Ah,0 msgmult Byte "MULTIPLE FEED",0Ah,0 msgskew Byte "EXCESSIVE SKEW",0Ah,0 msgfeed Byte "DOCUMENT MISFEED",0Ah,0 msgjam Byte "DOCUMENT JAM",0Ah,0 msgerror Byte "UNSPECIFIED ERROR",0Ah,0 dsb DWORD ? .code main PROC INVOKE printf, ADDR msg1fmt, ADDR msg1 INVOKE scanf, ADDR in1fmt, ADDR dsb INVOKE printf, ADDR msg2fmt, ADDR msg2, dsb while01: cmp dsb, 0ffh ; .while dsb<=0ffh jg endwhile01 test dsb, 00000001b if01: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgshort endif01: .endif test dsb, 00000010b if02: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msglong endif02: .endif test dsb, 00000100b if03: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgclose endif03: .endif test dsb, 00001000b if04: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgmult endif04: .endif test dsb, 00010000b if05: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgskew endif05: .endif test dsb, 00100000b if06: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgfeed endif06: .endif test dsb, 01000000b if07: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgjam endif07: .endif test dsb, 10000000b if08: .if !ZERO? INVOKE printf, ADDR msg1fmt, ADDR msgerror endif08: .endif INVOKE printf, ADDR msg1fmt, ADDR msg1 INVOKE scanf, ADDR in1fmt, ADDR dsb INVOKE printf, ADDR msg2fmt, ADDR msg2, dsb endwhile01: nop ;.endw ret main ENDP END main
-
打开VS2017, 选择VC++的“桌面安装向导”,创建项目
-
勾选“空项目”(有的VS版本还有安全周期检查,若有的话则把它也去掉),确定
-
右键项目 → \rightarrow →生成依赖项 → \rightarrow →生成自定义
-
勾选masm,确定
-
然后在源文件夹下创建如图所示的源文件,后缀名为.asm
- 右键项目
→
\rightarrow
→属性
→
\rightarrow
→弹出属性页,进行如下设置:
- 右键项目
→
\rightarrow
→属性
→
\rightarrow
→弹出属性页,进行如下设置:
-
设置完成,写个示例代码编译调试
.386 .model flat, c .stack 100h includelib msvcrt.lib ;输入输出函数所在的库 printf PROTO arg1:Ptr Byte, printlist:VARARG .data msg1fmt Byte "%s%d", 0 msg1 Byte "Hello World! ",0Ah, 0 num1 SDWORD ? .code main PROC mov num1, 7777 INVOKE printf, ADDR msg1fmt, ADDR msg1, num1 ret main ENDP END main
运行结果如下:
设置完成!!!