0%

微机原理

(꒪⌓꒪)

1字节 = 8位

字节是最小的寻单位!

1字 = 2字节 = 16位

WORD 字 16位

DWORD 双字 32位

Intel x86微处理器的寄存器结构

image-20221005205008112通用寄存器:主要用于算术运算数据传送

image-20221008210013032

AX通常称为累加器

BX基地址寄存器,作为存储器指针来用

CX计数寄存器

DX数据寄存器

BP常用来寻址堆栈而不是数据段

标志位:CF(进位)、ZF(零)、SF(符号)、OF(溢出)、AF(辅助进位)、PF(奇偶)

16位系统寻址方式

1.立即寻址

1
2
3
MOV AX, im  ; im:立即数   MOV AX, 43H
MOV AX, @DATA ; 在编译时返回整数常量值
MOV SI, OFFSET ARRAY ;??

2.直接寻址

1
2
3
MOV AX, [ARRAY]     ;加上方括号
SUB AX, [ARRAY+2]
MOV AX, DS:[2000H] ;设DS=3000H 物理地址:3000H×16(即左移4位变为20位)+2000H 偏移地址2000H由指令直接给出 不加方括号立即寻址 加上直接寻址

3.寄存器寻址

1
MOV DS, AX

4.寄存器间接寻址

操作数放在存储器中,操作数的16位段内偏移地址存放在SI、DI、BP、BX这四个寄存器之一中

  • 若以SI、DI、BX间接寻址,则操作数存放在现行数据段中,此时,数据段寄存器DS的内容加上SI、DI、BX中的16位段内偏移地址,即得操作数地址

  • 若以BP间接寻址,操作数存放在堆栈段区域中,此时,堆栈段寄存器SS的内容加上BP中的16位段内偏移地址,即得操作数地址

1
2
MOV AX, [SI]  ;设DS=2000H,SI=1000H  则操作数地址=21000H
MOV AX, [BP] ;设SS=3000H,BP=2000H 则操作数地址=32000H

DS、SS需左移四位

5.寄存器相对寻址

操作数存放在存储器中,操作数地址是由段寄存器内容(SI、DI、BX对应DS,BP对应SS)加上SI、DI、BX、BP其中之一的内容,再加上由指令中所指出的8位或16位带符号相对地址偏移量而得到的

1
2
3
MOV AX, DISP[SI]  ;操作数地址为DISP+SI+DS(DS需左移4位)
ADD AX, ARRAY[SI]
MOV AX, [BX+6]

6.基址变址寻址

在8086/8088中,通常把BX和BP作为基址寄存器,而把SI、DI作为变址寄存器,将这两种寄存器联合起来进行的寻址就称为基址+变址寻址。

此时,操作数的地址为段寄存器的内容(DS或SS BX对应DS BP对应SS)加上基址寄存器的内容(BX或BP),再加上变址寄存器内容(SI或DI)

1
MOV AX, [BX][SI]  ;操作数地址为BX+SI+DS(DS<<4)

7.基址变址相对寻址

1
MOV AX, DISP[BX][SI]  ;BX+SI+DISP+DS(DS<<4)

8.PC相对寻址

1
LOOP L1   ; PC相对寻址

9.隐含寻址

在有些指令中,不仅包含有操作码的信息,而且还隐含了操作数地址的信息。

汇编语言

大小写不敏感 标识符不能以数字开头

R 寄存器

M 内存

AL 寄存器低八位

h 十六进制 b 二进制 q/o八进制 r 编码实数 d 十进制

注意:0A3h

一、定义数据

BYTE 8位无符号整数 SBYTE 8位有符号整数

WORD 16位无符号整数 SWORD 16位有符号整数

DWORD 32位无符号整数 SDWORD 32位有符号整数

FWORD 48位整数

QWORD 64位整数

db 字节 8位

dw 字 = 2字节 16位

dd 双字 = 4字节

小端存储

image-20221014234431129

符号常量不占用任何实际的存储空间

1
2
3
4
5
6
7
8
9
; 计算数组和字符串的大小
list1 BYTE 10,20,30,40
List1Size = ($ - list1) ; $代表当前偏移地址
myString BYTE "This is a long string,"
BYTE " Containing any number"
BYTE " of characters",0dh,0ah
MyString_len = ($ - myString)
list2 WORD 1000h,2000h,3000h,4000h
List2Size = ($ - list2)/2

二、和数据相关的操作符和伪指令

预定义符号$

  • 表示当前这条指令在代码段中的偏移量

  • 表示字符串结束 0dh,0ah换行

OFFSET操作符

返回数据标号的偏移地址(标号据数据段开始的距离,以字节为单位)

1
2
3
4
5
6
7
8
9
10
11
12
13
.data
bVal BYTE ? ; 假设bVal位于00303000h处
wVal WORD ?
dVal1 DWORD ?
dVal2 DWORD ?
.code
……
mov esi,OFFSET bVal ; ESI = 00303000
mov esi,OFFSET wVal ; ESI = 00303001
mov esi,OFFSET dVal1 ; ESI = 00303003
mov esi,OFFSET dVal2 ; ESI = 00303007
mov esi,OFFSET bVal + 1
...

PTR操作符

用来重载操作数的默认尺寸

必须和以下标准数据类型联合使用:BYTE,SBYTE,WORD,SWORD,DWORD,SDWORD,FWORD,QWORD,TBYTE

1
2
3
4
5
6
7
.data
myDouble DWORD 12345678h ;小端存储
.code
mov ax, myDouble ; 错误!
mov ax, WORD PTR myDouble ; ax = 5678h
mov ax, WORD PTR [myDouble+2] ; ax = 1234h
mov bl, BYTE PTR myDouble ; bl = 78h

TYPE操作符

返回按字节计算的变量的单个元素的大小

DUP伪指令

duplicate 重复初始化数据

1
str1 db 10 dup('!@#')  ;十个'!@#'

LENGTHOF操作符

计算数组中元素的个数

1
2
3
4
5
6
.data
byte1 BYTE 10,20,30 ; LENGTHOF byte1 = 3
array1 WORD 30 DUP(?),0,0 ; 30+2
array2 WORD 5 DUP(3 DUP(?)) ; 5×3
arrar3 DWORD 1,2,3,4 ; 4
digitStr BYTE "12345678",0 ; 9

SIZEOF操作符

SIZEOF返回值 = LENGTHOF返回值 × TYPR返回值

INVOKE伪指令

自动在堆栈上压入参数并调用程序

1
INVOKE Swap,ADDR Array,ADDR [Array+4]

PROTO伪指令

为一个已存在的过程创建原型。

原型声明了过程的名字和参数列表,允许一个过程在被定义之前就可以在其他地方被调用。

三、数据传送指令

操作数类型

立即操作数(immediate)

  • imm:8、16或32位立即数

  • imm8:8位立即数(字节)

  • imm16:16位立即数(字)

  • imm32: 32位立即数(双字)

寄存器操作数(register)

  • reg:任意的通用寄存器

  • sreg:16位段寄存器CS、DS、SS、ES、FS、GS

  • r8:AH、AL、BH、BL、CH、CL、DH、DL

  • 16:AX、BX、CX、DX、SI、DI、SP、BP

  • r32:EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP

内存操作数(memory)

  • mem:8、16或32位内存操作数

直接内存操作数

1
2
3
4
5
6
7
8
9
10
.data
...
var1 BYTE 55h
.code
mov ax, @data
mov ds, ax
...
mov al, [1040h] ;设var1位于偏移1040h处
mov al, var1 ;等价于 mov al, [var1]
mov al, [var1+2] ;直接偏移操作数

MOV指令

  • 两个操作数的尺寸必须一致。
  • 两个操作数不能同时为内存操作数。
  • 目的操作数不能是CS,EIP和IP。
  • 立即数不能直接送至段寄存器。

image-20221005231757119

XCHG指令

交换两个操作数内容

1
2
3
4
5
6
7
8
9
;格式 不能直接交换两个内存操作数
xchg reg, reg
xchg reg, mem
xchg mem, reg

;交换两个内存操作数 利用寄存器
mov ax, val1
xchg ax, val2
mov val1, ax

四、布尔和比较指令

1.AND指令

按位与 两操作数大小必须相同 用于特定位置清0

将结果放在目标操作数中

1
2
3
4
5
6
7
8
9
10
; 允许操作数形式:
AND reg,reg
AND reg,mem
AND mem,reg
AND reg,imm
AND mem,imm

; 大小写ASCII码
'a' 61h 01100001
'A' 41h 01000001
  • 总是清除OF (溢出)和 CF(进位)
  • 根据结果修改SF(符号)、ZF(零)、PF(奇偶)

2.OR指令

按位或 用于特定位置置1

1
2
3
4
5
; 将0-9间整数转换为对应ASCII码数字
MOV dl,5 ;二进制值
OR dl,30h ;00110000
;或 add dl,30h
;或 add dl,'0'

3.XOR指令

按位异或

4.NOT指令

数据位按位取反,结果为反码

不影响任何标志位

1
2
3
;格式
NOT reg
NOT mem

5.TEST指令

格式同AND指令

两操作数按位相与,根据结果设置标志位,但不修改目的操作数

用于测试操作数某一位是0还是1

影响标志位:清除OF、CF 修改SF、ZF、PF

6.CMP指令

1
2
3
4
5
6
7
;格式同AND指令
cmp 目的操作数, 源操作数

;无符号操作数比较
目的<源 ZF=0 CF=1 SF≠OF
目的>源 ZF=0 CF=0 SF=OF
目的=源 ZF=1 CF=0

执行目的操作数-源操作数,但不回送结果,只影响标志位

根据相减结果修改OF、SF、ZF、CF、AF、PF

7.设置和清除单个CPU标志

五、算术运算

INCDNC指令

increment 即 +1

decrement 即 -1

1
2
3
4
5
;格式
inc reg/mem
dec reg/mem

inc BYTE PTR [si]

INC和DEC指令不影响进位标志

ADD指令

格式同MOV指令

两个操作数不能同时为内存操作数

1
2
3
4
5
6
.data
var1 DWORD 10000h
var2 DWORD 20000h
.code
mov eax,var1
add eax,var2 ; 30000h

SUB指令

格式同MOV指令

1
2
3
4
5
6
.data
var1 DWORD 30000h
var2 DWORD 10000h
.code
mov eax,var1
sub eax,var2 ; 20000h

影响标志位:CF(进位)、ZF(零)、SF(符号)、OF(溢出)、AF(辅助进位)、PF(奇偶)

NEG指令

negate 求补 即求相反数

将操作数按位取反、末位加1

1
2
3
;格式
neg reg
neg mem

影响标志位同上

mul乘法

乘数与被乘数大小必须保持一致

被乘数AL(8位) -> 积AX(16位)

被乘数AX(16位) -> 积DX:AX

1
2
3
4
5
6
7
;8位无符号数乘法
mov al, 5h ;被乘数放al寄存器
mov bl 10h ;乘数任一寄存器 但不能直接乘立即数
mul bl ;cf = 0 积在AX中,50h

;有符号乘法
imul

div除法

1
2
3
4
5
6
7
8
;无符号数 8003h/100h
mov dx,0 ;除数8位,则被除数16位放在AX,dx必须初始化
mov ax,8003h
mov cx,100h
div cx ;AX=0080h(商) DX=0003h(余数)

;有符号除法
idiv

8位无符号除法 被除数在AX(高8位清零) 除数8位 商在AL 余数在AH

16位无符号除法 被除数在DX:AX(DX清零) 除数16位 商在AX 余数在DX

六、控制转移 JMPLOOP指令

控制转移分为:

  • 无条件转移:JMP
  • 条件转移:LOOP

LOOP指令

ECX用作循环计数器,实地址模式下CX用作循环计数器

1
2
3
4
    mov  ax,0
mov ecx,5
L1: inc ax
loop L1

七、条件跳转指令

基于特定CPU标志值的跳转指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
JZ/JE    ;为0则跳转 ZF=1
JNZ/JNE ;不为0则跳转 ZF=0

JC ;设进位标志则跳转CF=1
JNC ;未设 CF=0

JO ;设溢出标志则跳转OF=1
JNO ;未设OF=0

JS ;设符号标志则跳转SF=1
JNS ;未设SF=0

JP ;设奇偶标志则跳转PF=1
JNP ;未设PF=0

基于CMP指令的跳转

1
2
3
4
JE     ;相等则跳转
JNE ;不等则跳转
JCXZ ;CX=0则跳转
JECXZ ;ECX=0则跳转

无符号整数:

1
2
3
4
JA  ;大于则跳转 同JNBE不小于或等于则跳转
JAE ;大于等于则跳转 同JNB不小于则跳转
JB ;小于则跳转 同JNAE不大于或等于则跳转
JBE ;小于或等于则跳转 同JNA不大于则跳转

有符号整数:

1
2
3
4
JG  ;大于则跳转 同JNLE
JGE ;大于等于 同JNL
JL ;小于 同JNGE
JLE ;小于或等于 同JNG

LOOPZLOOPE

LOOPZ与LOOPE等价

执行逻辑:

​ ECX=ECX-1

​ if ECX>0 and ZF=1, jump to destination

LOOPNZLOOPNE

等价

执行逻辑:

​ ECX=ECX-1

​ if ECX>0 and ZF=0, jump to destination

八、移位指令

1
2
3
4
5
SHL/SHR    ;逻辑左/右移
SAL/SAR ;算数左/右移
ROL/ROR ;循环左/右移
RCL/RCR ;带进位的循环左/右移
;SHLD/SHRD 双精度左/右移

影响OF、CF

逻辑右移:最高位补0,最低位移入CF

算术右移:最高位保持,最低位移入CF

逻辑左移/算术左移:最高位移入CF,最低位补0

循环左移:最高位移入CF和最低位

循环右移:最低位移入CF和最高位

九、过程

PROCENDP伪指令来声明

对程序进行逻辑划分为过程

1
2
3
4
5
6
7
8
9
10
11
12
; 启动过程
main PROC
...
call MySub
...
exit
main ENDP
; 其他过程
MySub PROC
...
ret
MySub ENDP

局部标号和全局标号

十、字符串和数组

基本指令

指令 描述
MOVSB, MOVSW, MOVSD 移动字符串:拷贝DS:(E)SI寻址的内存操作数至ES:(E)DI
CMPSB, CMPSW, CMPSD 比较字符串:比较内存中由DS:(E)SI寻址和ES:(E)DI寻址的字符串
SCASB, SCASW, SCASD 扫描字符串:扫描ES:(E)DI指向的内存字符串查找与累加器匹配的值
STOSB, STOSW, STOSD 存储字符串:将累加器内容存储到由ES:(E)DI寻址的内存中
LODSB, LODSW, LODSD 将字符串数据装入累加器:将由DS:(E)SI寻址的内存单元装入累加其中
  • SI是DS段中的偏移
  • DI是ES段中的偏移

使用重复前缀

  • 字符串操作指令每次只能处理一个内存值。

  • 通过增加一个重复前缀,字符串指令就会使用ECX作为计数器进行重复,实现用一条指令处理整个数组。

前缀 描述
REP ECX>0时重复
REPZ, REPE ZF=1&&ECX>0
REPNZ, REPNE ZF=0&&ECX>0
1
2
3
4
5
6
7
8
9
10
main PROC
mov ax,@data ;get addr of data seg
mov ds,ax ;initialize DS
mov es,ax ;initialize ES

cld ; clear direction flag
mov si,OFFSET string1 ;SI points to source
mov di,OFFSET string2 ;DI points to target
mov cx,10 ; set counter to 10
rep movsb ; move 10 bytes

方向标志:简单字符串指令使用方向标志来决定ESI和EDI是自动增加还是自动减少:

  • 方向标志位DF=0:ESI、EDI自动增加

  • DF=1:ESI、EDI自动减少

    1
    2
    CLD  ;清除方向标志
    STD ;设置方向标志

十一、结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;结构的定义
Employee STRUCT
IdNum BYTE "000000000"
LastName BYTE 30 DUP(0)
Years WORD 0
SalaryHistory DWORD 0,0,0,0
Employee ENDS

;声明
.data
worker Employee <>
person1 Employee <"555223333">
person2 Employee <,"Jones">
person3 Employee <,,,2 DUP(20000)>

;引用
.code
mov dx,worker.Years
mov worker.SalaryHistory+4,30000 ; second salary
mov edx,OFFSET worker.LastName
mov esi,OFFSET worker
mov ax,(Employee PTR [esi]).Years

十二、宏

命名的汇编语句块。调用时直接拷贝插入程序中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;宏的定义
mWriteStr MACRO string
push edx
mov edx,OFFSET string
call WriteString
pop edx
ENDM

;调用
.data
msg1 BYTE "This is message 1.",0Dh,0Ah,’$’
msg2 BYTE "This is message 2.",0Dh,0Ah,’$’
msg3 BYTE "This is message 3.",0Dh,0Ah,’$’
.code
mWriteStr msg1
mWriteStr msg2
mWriteStr msg3

编程题举例

数组间接寻址

16位汇编:三个字相加

1
2
3
4
5
6
7
8
9
10
11
.data
arrayW WORD 1000h,2000h,3000h
.code
mov ax,@data
mov ds,ax
mov si,OFFSET arrayW
mov ax,[si]
add si,2
add ax,[si]
add si,2
add ax,[si]

P106 3.8 在DATA为首地址的主存区域中存放100个无符号8位数,试编写程序找出其中最大的数,并将其放在KVFF存储单元中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
		MOV BX, SEG DATA
MOV DS, BX
MOV BX, OFFSET DATA

;初始化
MOV AL, [BX]
INC BX
MOV CX, 99
AGIN: CMP AL, [BX]
JAE NEXT
MOV AL, [BX]
NEXT: INC BX
LOOP AGIN
MOV KVFF, AL
...

P107 3.14 试编写8086汇编程序,统计由主存40000H开始的16K个单元中所存放的字符“A”的个数,并将结果存放在DX中。

1
2
3
4
5
6
7
8
9
10
11
12
START:	MOV DX, 40000H
MOV DS, DX
MOV CX, 16*1024
MOV SI, 0
MOV DX, 0
FIND: MOV AL, [SI]
CMP AL, 'A'
JNE NEXT ;或JNZ
INC DX
NEXT: INC SI
LOOP FIND
...

https://godbolt.org