《汇编语言基础教程》 学习实录


书目:《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章 迭代结构

  1. INVOKE指令破坏寄存器eax、ecx和edx的值,故用ecx计数时需要用到一个内存变量存储计数值

  2. loop指令使用时ecx的值不能为0或负值

  3. 完整代码示例:实现幂函数

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章 逻辑运算指令、移位指令、循环移位指令和堆栈

  1. 逻辑运算:and \ or \ xor A, B

  2. 逻辑移位:左shl \ shr A, B

  3. 测试比特位:test A, B A值不变 || and A, B,A值会变

  4. 算术移位:左sal A, B\ sar A, B 右,salshl完全一样,但sarshr不一样,考虑负数的情况即可

  5. 循环移位:左rol A, B \ ror A, B右,一般移动一整轮使得值复原

  6. 堆栈: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,所以折中考虑方法二

  7. 章节实例:模拟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
    
附:Intel x86汇编环境搭建与配置
  1. 打开VS2017, 选择VC++的“桌面安装向导”,创建项目
    在这里插入图片描述

  2. 勾选“空项目”(有的VS版本还有安全周期检查,若有的话则把它也去掉),确定
    在这里插入图片描述

  3. 右键项目 → \rightarrow 生成依赖项 → \rightarrow 生成自定义
    在这里插入图片描述

  4. 勾选masm,确定
    在这里插入图片描述

  5. 然后在源文件夹下创建如图所示的源文件,后缀名为.asm
    在这里插入图片描述

    1. 右键项目 → \rightarrow 属性 → \rightarrow 弹出属性页,进行如下设置:在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  6. 设置完成,写个示例代码编译调试

    .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
    

    运行结果如下:
    在这里插入图片描述
    设置完成!!!

Guess you like

Origin blog.csdn.net/weixin_42430021/article/details/108384651