动态线条
动态线条end

汇编语言程序设计

用汇编语言写源程序

伪指令

没有对应的机器码的指令,最终不被CPU所执行。伪指令是由编译器来执行的指令,编译器根据伪指令来进行相关的编译工作。

常见的汇编程序格式如下:

assume cs:codesg
codesg segment
	mov ax,0123H
	mov bx,0456H
	add ax,bx
	add ax,ax
	
	mov ax,4c00H
	int 21H
codesg ends
end

将上述代码拆解

伪指令部分如下:

assume cs:codesg
codesg segment
	....
codesg ends
end

程序返回部分如下:

....
	....
	
	mov ax,4c00H
	int 21H
....

汇编指令部分如下:

....
	mov ax,0123H
	mov bx,0456H
	add ax,bx
	add ax,ax
....

程序中的三种伪指令

段定义

1)一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或者当做栈空间使用。

2)一个有意义的汇编程序中至少要有一个段,这个段用来存放代码。

3)定义程序中的段:每个段都需要段名

;分号为汇编语言的注释符,与C语言的 //和/**/同理
段名 segment				;段的开始
...						 ;汇编代码
段名 ends					;段的结束

end(不是ends)

汇编程序的结束标记。若程序结尾处不加end,编辑器在编译程序时,无法知道程序在何处结束。

assume(假设)

含义是假设某一段寄存器和程序中的某一个用segment ... ends定义的段相关联 -- assume cs:codesg指cs寄存器与codesg相关联,将定义的codesg当做程序的代码段使用。

编程求23

1)定义一个段

2)实现处理任务

3)指出程序在何处结束

4)段与段寄存器关联

5)加上程序返回的代码

代码实现如下

assume cs:code
code segment
	mov ax,2
	add ax,ax
	add ax,ax
	
	mov ax,4C00H
	int 21H
code ends
end

将原文件变成可执行文件

masm 文件名.mas	编译程序
link 文件名.ojb	链接程序
文件名				运行程序

Debug跟踪程序的执行

debug跟踪test程序

image-20220108094153450

如图可以看到数据段从075AH开始,代码段从076AH开始

小结:

1)程序加载后,DS中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,这程序所在的内存区的地址为DS:0

2)这个内存区的前256个字节村PSP,DOS用来和程序进行通信

3)从256个字节处向后的空间存放的是程序,CS的值为DS + 10H

4)程序加载后,CX中存放代码的长度(字节)

[ ] 和 ( )

中括号:汇编语法的规定,表示一个内存单元

小括号:为了方便学习而做出的约定,表示了一个内存单元或寄存器中的内容

Loop指令

功能:实现循环

CPU执行loop指令进行的操作

1)(cx) = (cx) - 1

2)判断cx中的值

     不为0则转至标号处执行程序

     如果为0则向下执行

实例

assume cs:code
code segment
	mov ax,2
	mov cx,11
	s:add ax,ax
	loop s
	
	mov ax,4c00H
	int 21H
code ends
end

用cx和loop指令向配合实现循环功能的三个要点:

1)在cx中存放循环此时

2)用标号指定循环开始的位置

3)在标号和loop指令的中间,写上要循环执行的程序段(循环体)

实现123*236

assume cs:code
code segment
	mov ax,0
	mov cx,123
	
	l:add ax,256
	loop l
	
	mov ax,4c00H
	int 21H
code ends
end

计算FFFF:0006字节单元中的数乘以3,结果存储在dx中(连加三次,等同乘三)

assume cs:code
code segment
	mov ax,0ffffH
	mov ds,ax
	mov bx,6
	mov al,[bx]		;ax低位寄存器初始化
	mov ah,0		;ax高位寄存器初始化
	
	mov dx,0
	mov cx,3
	
	l:add dx,ax
	loop l
	
	mov ax,4c00H
	int 21H
code ends
end

段前缀的使用

观察如下代码

assume cs:code
code segment
	mov ax,2000H
	mov ds,ax
	
	mov al,[0]
	mov bl,[1]
	mov cl,[2]
	mov dl,[3]
	
	mov ax,4c00H
	int 21H
code ends
end

代码中[ ]的含义是将段地址为2000偏移地址为[ ]中数据位置的值赋值给左侧的寄存器,但是在真正编译链接后,通过debug进行查看会发现,编译器只是将0赋值给al,1赋值给bl,以此类推。与想象中编译的结果不同。因此正确的书写格式如下:

assume cs:code
code segment
	mov ax,2000H
	mov ds,ax
	
	mov al,ds:[0]	;添加段前缀
	mov bl,ds:[1]
	mov cl,ds:[2]
	mov dl,ds:[3]
	
	mov ax,4c00H
	int 21H
code ends
end

计算ffff:0 -- ffff:b 字节单元中的数据的和,结果存储在dx中

assume cs:code
code segment
	mov ax,0ffff
	mov ds,ax		;为数据段地址赋值
	mov dx,0		;初始化为零,用于记录总和
	mov ax,0		;利用al记录字节数据
	mov cx,12		;循环次数
	mov bx,0		;作为地址的偏移
	
  l:mov al,ds:[bx]
  	inc bx			;bx自行加一
  	add dx,ax		;记录总和
	loop l
	
	mov ax,c400H
	int 21H
code ends
end

将内存ffff:0 -- ffff:b中的数据拷贝到 0020:0 -- 0020:000b单元中

代码一:

assume cs:code
code segment
	mov cx,12		;需要循环十二次复制操作
	mov bx,0		;偏移量
	mov dx,0		;当做中间变量
	
  l:mov ax,0ffff
	mov ds,ax
	mov dl,ds:[bx]	;从ffff段位置取出一个数据
	
	mov ax,20
	mov ds,ax
	mov ds:[bx],dl	;将中间变量赋值给20段地址的空间
	
	inc bx			;偏移地址加一
	
	loop l			;跳转到循环的开始,继续下一个空间的复制
	mov 4c00H
	int 21H
code ends
end

但是上述操作过于繁琐,每次复制一个字节空间,都需要对段地址进行更换两次,因此进行代码二的优化

代码二:

assume cs:code
code segment
	mov ax,0ffff
	mov ds,ax		;为第一个段寄存器赋值
	
	mov ax,20
	mov ex,ax		;为第二个段寄存器赋值
	
	mov bx,0		;初始化偏移量为0
	mov cx,12		;设置循环次数
	mov ax,0		;存储中间变量
	
  l:mov al,ds:[bx]	;取出字节中数据
	mov ex:[bx],al	;将al中数据存储到指定地址
	inc bx			;偏移量自行加一
	loop l
	
	
	mov ax,4c00H
	int 21H
code ends
end

在代码段中使用的数据

在上述代码二中的代码,存在一定的危险性

由于ds与ex寄存器被直接指定了物理地址,如果该物理地址恰好是其他程序运行所需要的空间,那么如果进行数据的更改,造成的结果是无法预见的。

因此衍生出来的对策

1)在程序的段中存放数据,运行时由操作系统分配空间

2)段的类别:数据段、代码段、栈段

3)各种段中均可以有数据

4)可以在单个的段中安置,也可以将数据、代码、栈放入不同的段中

应用案例

编程计算以下八个数据的和,结果存放在ax寄存器中

0123H	0456H	0789H	0ABCH	0DEFH	0FEDH	0CBAH	0987H

数据的定义

字型数据:dw(define word)

字节型数据:db(define byte)

双字型数据:dd(define double)

在代码段中定义字形数据(define word),代码如下:(下述代码存在问题)

assume cs:code
code segment
	
	dw 0123H,0456H,0789H,0ABCH,0DEFH,0FEDH,0CBAH,0987H
	
	mov bx,0		;初始化偏移量为0
	mov ax,0		;在ax中记录总和,将ax初始化为0
	mov cx,8		;初始化循环次数
	
  l:add ax,cs:[bx]
  	add bx,2
  	loop l
  	
	mov ax,4c00H
	int 21H
code ends
end

image-20220108181707094

可以观察到,下一条执行的指令不是mov bx,0,而是将定义的字型数据当做了指令来执行,那么逻辑上与我们构想的程序不相符,而程序执行的位置由cs:ip来决定,因此ip的位置如果跳过前八个双字的数据,直接指向执行命令的话,那么程序运行的过程与我们逻辑上设想的是一样的。

代码做如下改进

assume cs:code
code segment
	
	dw 0123H,0456H,0789H,0ABCH,0DEFH,0FEDH,0CBAH,0987H
	
start:				;表示程序的开始
	mov bx,0		;初始化偏移量为0
	mov ax,0		;在ax中记录总和,将ax初始化为0
	mov cx,8		;初始化循环次数
	
  l:add ax,cs:[bx]
  	add bx,2
  	loop l
  	
	mov ax,4c00H
	int 21H
code ends
end start

image-20220108183116657

在代码段中使用栈

利用栈,将下述八个数据逆序存放

0123H	0456H	0789H	0ABCH	0DEFH	0FEDH	0CBAH	0987H

代码实现如下

assume cs:code
code segment
	dw 0123H,0456H,0789H,0ABCH,0DEFH,0FEDH,0CBAH,0987H	;cs:0至cs:F
	dw 0,0,0,0,0,0,0,0	;定义栈空间cs:10至cs:1F
start:					;定义程序的开始
	mov ax,cx
	mov ss,ax			;为栈段寄存器赋初值
	mov sp,20			;定义栈顶指针
	
	mov bx,0			;初始化偏移量
	mov cx,8			;定义循环次数
pushstack:
    push cs:[bx]		;入栈操作
    add bx,2			;由于数据是字型,因此偏移量一次加二
    loop pushstack		;进行循环

	mov bx,0			;将偏移量重新置零
	mov cx,8			;设置循环次数
popstack:
	pop cs:[bx]			;将出栈的值赋值到指定地址空间
	add bx,2
	loop popstack		;进行循环

	mov ax,4c00H
	int 21H
code ends
end start

将数据、代码、栈放入不同的段

代码如下

assume cs:code,ds:data,ss:stack
data segment
	dw 0123H,0456H,0789H,0ABCH,0DEFH,0FEDH,0CBAH,0987H
data ends

stack segment
	dw 0,0,0,0,0,0,0,0
stack ends

code segment
start:					;定义程序的开始
	mov ax,cx
	mov ss,ax			;为栈段寄存器赋初值
	mov sp,20			;定义栈顶指针
	
	mov ax,data
	mov ds,ax			;初始化数据段
	
	mov bx,0			;初始化偏移量
	mov cx,8			;定义循环次数
pushstack:
    push cs:[bx]		;入栈操作
    add bx,2			;由于数据是字型,因此偏移量一次加二
    loop pushstack		;进行循环

	mov bx,0			;将偏移量重新置零
	mov cx,8			;设置循环次数
popstack:
	pop cs:[bx]			;将出栈的值赋值到指定地址空间
	add bx,2
	loop popstack		;进行循环

	mov ax,4c00H
	int 21H
code ends
posted @ 2022-01-16 14:34  v1v1v1  阅读(388)  评论(0)    收藏  举报