16位汇编笔记1-6


                             16位汇编1-6

1.基础

1.1 cpu

汇编语言有以下3类指令

  • 汇编指令:有对应机器码,是汇编语言的核心。
  • 伪指令:无机器码,由编译器执行,计算机不执行。
  • 其它符号:编译器识别,无机器码。

cpu工作需要有指令和数据,而指令和数据存储于内存,所以内存的作用仅次于cpu。


CPU读写数据,要和外部器件(芯片)进行三类信息交互:

  • 地址信息:存储单元的地址。
  • 控制信息:器件选择,读或写的命令。
  • 数据信息:读或写的数据。

每种cpu都有自己的汇编指令集。

真正的64位要满足的条件:

  • 64 bit cpu
  • 64 bit os
  • 64 bit app

1.2 总线

cpu不能直接控制外部器件,而是通过总线连接其它芯片,逻辑上分为地址总线,控制总线,数据总线。

地址总线

一个CPU有N根地址线,则说该CPU的地址总线的宽度为N,可寻址2N 个内存单元。

N决定了CPU与外界的数据传输速度。

控制总线

控制总线数量意味着CPU提供了对外部器件的多少种控制,所以控制总线的宽度决定了CPU对外部器件的控制能力。


数据总线宽度决定了数据传送速度;
控制总线宽度决定能控制多少种外部器件;
地址总线宽度决定cpu寻址能力。

指令和数据在存储器中没有区别,都是二进制信息,属于哪种信息取决于所在的总线。

读操作的步骤:

  1. 地址线:cpu --> 内存,传送地址
  2. 控制线:cpu --> 内存,读指令
  3. 数据线:内存 – > cpu,数据

写操作同理。


1.3 内存地址空间

地址总线宽度为n,则可寻址的 2 n 2^n 2n个内存单元构成内存地址空间。

对cpu来说,所有存储器的存储单元都位于一个统一的逻辑存储器中,容量受cpu寻址能力的限制。这个逻辑存储器就是内存地址空间。

1.4 存储

寄存器:可以理解为嵌入到cpu中的内存。

微机存储器容量以字节为最小单元计算,即一个存储单元存储一个字节。

其实计算机的任何部件都有独立存储器,比如显卡的显存,将数据交给gpu,而且gpu的速度比cpu还要快。


存储器按读写属性分类:ram和rom

按功能和连接分类:

  • ram
  • rom with bios
  • ram on the interface card , 比如显存

各种存储器虽然物理上独立,但有两个相同点:

  • 和cpu总线相连
  • 读写操作要通过控制总线

1.6 masm

命令功能
r查看,更改寄存器
d查看内存
e改写内存,写入机器码
a改写内存,写入汇编指令
t执行一条机器指令
u把机器指令翻译为汇编指令
p执行指令。
p命令执行到int 21时,程序退出, 而t命令不会退出
g跳转
q退出

-d fff0:f0可以查看主板rom生产日期,-e无法更改rom。

-d 1000:0 8查看8个字节。

debug的原理:不放弃对cpu的控制。程序加载顺序为cmd->debug->1.exe

dos加载exe文件时,ds存放程序所在内存区的段地址SA,并且系统会创建段前缀psp,dos用它来和程序通信,里面包括了传递给待运行程序的命令行参数、 程序运行结束时返回DOS所需的地址等有用的信息,位于ds:0,占用256字节。从ds:100H开始才是exe程序。

所以,程序的物理地址是:SA*16+0+256 = (SA+16)*16+0

2.寄存器

三条外部总线连接cpu和外部器件;内部总线实现cpu内部各个器件的联系。

8086CPU有14个寄存器:

  • 通用寄存器:ax,bx,cx,dx
  • si,di
  • sp,bp,ip
  • 段寄存器:cs,ss,ds,es
  • psw

它们全是16位的,可分为2个8位寄存器使用,如AX分为AHAL

16位结构代表了:

  • 寄存器最大宽度
  • 运算器一次最多处理16位数据
  • 寄存器和运算器之间的通路是16位的
寄存器解释
AXAccumulator累加器
BXBase Register基地址寄存器
CXCount Register计数寄存器
DXData Register数据寄存器
SIsource index register
DIdestination index register
SPstack pointer
BPbase pointer
IPintruction pointer
CScode segment
SSstack segment
DSdata segment
ESextra segment附加段寄存器
PSWProgram Status Word

2.1 指令

8086可 一次性处理两种尺寸的数据:

  • 字节:byte,8个bit组成。
  • 字:word,高字节和低字节两个字节组成。
汇编指令高级语言描述
mov ax,18ax=18
mov ah,78ah=78
add ax,8ax=ax+8
mov ax,bxax=bx
add ax,bxax=ax+bx

写指令时不区分大小写。

数据传输或运算时,两个操作对象的位数应一致。

add al,al,如果有进位,1不会保存进ah.

add ax,al``,如果有进位,1会保存进ah`.

2.2 物理地址

存储空间时一维线性空间,每一个内存单元在这个空间中都有唯一的地址,即物理地址。

8086有20位地址总线,即1MB寻址能力,但内部为16位结构,表现出来的寻址能力只有64k。8086采用合成两个16位地址的方法形成一个20位物理地址。

物理地址=段地址*16+偏移地址

段地址*16可看作基础地址。

0:200~0:20b等同0020:0~0020:b

2.3 段

错误认识:内存被划分为段

其实,内存没有分段,段的划分来自于cpu。

编程时可根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。用段地址*16定位段的起始地址,用偏移地址定位内存单元。

偏移地址为16位,寻址能力为64k,所以一个段的最大长度为64k。

对于物理地址21F60H

段地址偏移地址
2000H1F60H
2100H0F60H

所以,cpu可以用不同的段地址和偏移地址形成同一个物理地址。

21F60H有两种描述:

  • 2000:1F60单元
  • 2000段的1F60单元

思考,当段地址给定多少时,CPU无论怎么变化偏移地址都无法寻到20000H单元?

20000h=SA*16+EA
SA=(20000h-EA)/16=2000h-EA/16
EA取最大值时,SA=2000h-ffffh/16=1001h,SA为最小值
EA取最小值时,SA=2000h-0h/16=2000h,SA为最大值

所以答案是 1001H 以下和 2000H 以上。

2.4 CS和IP

8086有4个段寄存器:CS,DS,SS,ES.

CS和IP是两个最关键的寄存器。CPU会一直重复以下过程

  1. 将从(cs)*16+(ip)单元开始读取指令并执行
  2. IP=IP+读取指令的长度

8086启动后,CS=FFFFH,IP=0000H,所以FFFF0H是8086主机开机执行的第一条指令。

cpu只认cs:ip单元的内容为指令

2.5 修改CS,IP

不是传送指令mov,而是跳转指令jmp

用法1
jmp 2AE3:3
之后CS=2AE3H,IP=0003H

用法2
AX=1000H,CS=2000H,IP=0003H
jmp ax
之后AX=1000H,CS=2000H,IP=1000H

修改段寄存器:数据–>通用寄存器–>段寄存器

切记,8086cpu不支持将数据直接送入段寄存器。

段的访问:

  • 代码段:cs:ip
  • 数据段:ds:[]
  • 栈段:ss:sp

2.6 内存访问

0地址处存放2000(4E20H)
----------
|   20   | <-- 0
----------
|   4E   | <-- 1
----------
这是小端存储
0地址处的字节型数据为20H
0地址处的字型数据为4E20H

3.寄存器

3.1 字的存储

字单元:存放一个字形数据的内存单元,由两个字节单元组成。

N地址字单元:起始地址为N的字单元。

3.2 DS和[address]

DS用来存储要访问数据的段地址。

[]表示一个内存单元的偏移地址,段地址默认在ds中。

mov bx,1000H
mov ds,bx
mov al,[0]

3.3字的传送

mov bx,1000H
mov ds,bx
mov ax,[0]    ;1000:0处的字型数据传入ax
mov [0],cx    ;cx中的16位数据送到1000:0处

3.4 mov、add、sub指令

mov的3种常用功能:
mov ax,8	; mov 寄存器,数据
mov ax,bx	; mov 寄存器,寄存器
mov ax,[0]	; mov 寄存器,内存单元,实际可能会出问题,因为ax是16位,[0]是8位。

其它:
mov [0],ax	; mov 内存单元,寄存器
mov ds,ax    ; mov 段寄存器,寄存器

mov ax,ds
mov [0],cs
mov ds,[0]
add,sub同mov

3.5数据段

将123B0H~123B9H定义为数据段,现在累加其中前三个单元内的数据
mov ax,123BH
mov ds,ax
mov al,0
add al,[0]
add al,[1]
add al,[2]

如果是累加字型数据,则改为
mov ax,0
add al,[0]
add al,[2]
add al,[4]

3.6栈、SS、SP

操作规则:LIFO

8086的入栈和出栈都是以字为单位进行的。

SS存放栈顶的段地址,SP存放栈顶的偏移地址。任意时刻,SS:SP指向栈顶元素。

push ax由两步完成

  1. SP=SP-2,SS:SP指向当前栈顶前面的单元。
  2. 送入ax,SS:SP为新栈顶。

pop相反,而且pop后数据还存在栈内,再次push会被覆盖。内存不提供删除机制,此处联系文件的删除。

SS=1000H SP=000EH
PUSH ax   ;ax=2266H
1000DH=22  1000CH=66  SP=000CH
---------
|   66  |  <-- 1000C
---------
|   22  |  <-- 1000D
---------

8086不保证栈操作不会出界,所以要合理安排栈大小。出栈时也要确定栈不为空。

mov ax,1000H
mov ds,ax
mov ax,2266H
mov [0],ax
等同以下
mov ax,1000H
mov ss,ax
mov sp,2
mov ax,2266H
push,ax

假设栈空间:10000H-1000FH

  • 栈最底部的字单元地址为1000:000E
  • 栈只有一个元素时,sp指向000EH
  • 栈为空时,sp指向0010H

若栈空间为10000H-1FFFFH,栈为空时,sp指向0000H;栈满时sp也指向0000H,再次push会覆盖。

因为这两个栈指令只修改sp,所以栈顶最大变化范围为0-FFFFH

pushpop可以操作:

  • 通用寄存器
  • 段寄存器
  • 内存单元

栈是为存放临时数据而设计的,早期用来保存函数地址。

4.第一个程序

assume	cs:codesg      ; 将codesg与cs联系起来。

codesg	segment         ;伪指令,段开始。段名称为codesg
		
		mov ax,0123H
		mov bx,0456H
		add ax,bx
		add ax,ax
		
		mov ax,4c00H       ;两条指令用于程序返回,即把cpu控制权交给使该程序运行的程序,如cmd.exe.
		int 21H
		
codesg	ends              ;伪指令,codesg段结束。

end      ;结束编译

一个汇编程序由多个段组成,至少要有一个代码段。
指令,数据,和栈被划分到不同的段里。

伪指令没有对应机器码,不被cpu执行,由编译器执行。编译器根据伪指令进行编译。

汇编需要编辑器,编译器,连接器,调试器。

连接器:

  • 把很大的源程序分为多个源程序文件来编译为目标文件,再连接为可执行文件exe。
  • 程序调用库文件的子程序,也需要库文件和该程序目标文件连接为exe。
  • 存有机器码的目标文件中有内容需要连接程序处理后才能生成exe。

dos系统是单任务系统,中断机制是基础;windows的消息机制是多任务的基础。

编译时使用;可以简化编译步骤,或者直接使用ml.exe

5.[BX]和loop指令

5.1 [bx]

描述一个内存单元需要两种信息:

  • 内存单元地址
  • 内存单元长度

[bx]表示内存单元,它的偏移地址为bx,段地址(段前缀)默认在ds中。例如mov ax,[bx]。也可以显示指定段地址为ds:,cs:,ss:,es:

mov ax,[idata]会被编译为mov ax,idata,所以需要用[bx]或者mov ax,ds:[0]

我们用()表示寄存器或内存单元中的内容,可包含寄存器名,段寄存器名,内存单元物理地址(20位)

(ax),(ds),(20000H).

idata表示常量。


mov ax,[bx]

功能:(ax)=( (ds)*16+(bx) )

mov [bx],ax

功能:( (ds)*16+(bx) )=(ax)


5.2 inc

inc ax即自加1

5.3 loop

loop指令有两步操作:

  1. (cx)=(cx)-1
  2. 判断(cx),不为0则转至标号处执行程序,为0则向下执行。
	mov cx,循环次数
s:
	循环代码段
	loop s

计算2^12
assume	cs:code
code segment
	mov ax,2
	mov cx,11    
s:	add ax,ax	;类似do{}while{}
	loop s
	
	mov ax,4c00H
	int 21H
code ends
end

汇编中,标号实际上是一个地址。上面的s地址处有add ax,ax指令。

计算ffff:0~ffff:b单元中的和
assume	cs:code
code segment
	mov	ax,0ffffH      ;汇编源程序中,数据不能以字母开头,所以要在前面加0. 
	mov	ds,ax
	mov bx,0
	
	mov dx,0
	mov cx,12
	
s:	mov al,[bx]
	mov ah,0	;可以尝试直接mov ax,[bx],看看是否会出错。
	add dx,ax
	inc bx
	loop s
	
	mov ax,4c00H
	int 21h
code ends
end

调试时可以先用u指令查看地址,g直接跳过循环,也可以执行到loop时,p指令结束循环。

上面的题目有两个问题:类型匹配和越界问题。我们用ax寄存器作中介来解决这两个问题。后面还有更好的解决方法

我们经常会碰到用循环处理连续内存单元数据的问题。

5.4 安全空间

dos和其它合法程序一般不使用0:200-0:2ff这256个字节,所以我们可以向这段空间里写入数据。

谨慎起见,先用d指令查看一下。

将ffff:0~ffff:b复制到0:200~0:20b

assume	cs:code
code segment
	mov bx,0
	mov cx,12
	
s:	mov ax,0ffffH
	mov ds,ax
	mov dl,[bx]
	
	mov ax,0020H
	mov ds,ax
	mov es:[bx],dl
	
	inc bx
	loop s
	
	mov ax,4c00H
	int 21H
code ends
end


改进
assume cs:code
code segment
	mov ax,0ffffH
	mov ds,ax
	mov ax,0020H
	mov ds,ax      ;优化后
	
	mov bx,0
	mov cx,12

s:	mov dl,[bx]
	mov es:[bx],dl
	
	inc bx
	loop s
	
	mov ax,4c00H
	int 21H
code ends
end

6.包含多个段的程序

6.1 在代码段中使用数据

assume cs:codesg
codesg segment
		..		..
		数据
		..
start:
		..
		代码
		..
codesg ends
end start

要有start标号表明代码开始地址,end start会告诉编译器开始和结束地址,否则会把数据当作代码执行。

如果没有start,则会从第一个可识别的机器码开始执行。

assume cs:codesg
codesg segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h	;dw:define word,16 bytes
start:
	mov bx,0   ;数据偏移地址即代码段最开始,即0
	mov ax,0
	
	mov cx,8
s:	add ax,cs:[bx]
	add bx,2
	loop s
	
	mov ax,4c00h
	int 21h
	
codesg ends
end start

dw定义的8个字型数据地址分别为:cs:0,cs:2,...,cs:e处。

在代码段中使用栈实现逆序
assume cs:codesg
codesg segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h  
	dw 0,0,0,0,0,0,0,0    ;32 bytes for stack
start:	
	mov ax,cs
	mov ss,ax
	mov sp,20h   ;栈底 ss:sp=cs:30
	
	mov bx,0
	mov cx,8
s:	push cs:[bx]
	add bx,2
	loop s
	
	mov bx,0
	mov cx,8
s0: pop cs:[bx]
	add bx,2
	loop s0

	mov ax,4c00h
	mov 21h
	
codesg ends
end start

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

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,stack      ;段名代表段地址
	mov ss,ax
	mov sp,20h
	
	mov ax,data
	mov ds,ax
	
	mov bx,0
	mov cx,8
s:	push [bx]
	add bx,2
	loop s
	
	mov bx,0
	mov cx,8
s0: pop [bx]
	add bx,2
	loop s0
	
	mov ax,4c00h
	mov 21h
code ends
end start

assume是伪指令,是给编译器看的,cpu并不会将cs,ds,ss指向code,data,stack

如果段中的数据占N个字节,则实际占有的空间为 16 × ( N / 16 + 1 ) 16\times (N/16+1) 16×(N/16+1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值