汇编语言

物理地址

CPU访问内存单元是要给出内存单元的地址。

8086的寻址能力和cpu的寻址能力不匹配,前者20位后者只有16位。

8086CPU的解决办法:

物理地址=段地址*16+偏移地址(乘16相当于把段地址左移一位)

一个物理地址不能决定段地址和偏移地址

用两个16位的地址相加得到一个20位的物理地址

用分段的方式管理内存

内存并没有分段,段的划分来自于CPU

一个内存段的起始地址(基础地址)为段地址的十六倍

  • 一个段的起始地址一定是十六的倍数
  • 编译地址为十六位,十六位地址的寻址能力为64k,所以一个段的最长大小为64k

eg:数据在21F60H内存单元中,段地址是2000H,说法:

  • 数据存在内存2000:1FH单元中
  • 数据存在2000H段中的1F60H单元中

四个段寄存器:

CS代码寄存器

DS数据寄存器

SS栈段寄存器

ES附加寄存器

debug的使用

借debug观察计算机内部的情况

1.启动debug:

2.R-查看寄存器内容:R 寄存器名, 再进行改变

3.D-查看内存中的内容

​ D 列出预设地址内存处的128个字节的内容

​ D 段地址:偏移地址

4.E-改变内存中的内容:

​ E 段地址:偏移地址 数据1 数据2 数据3……

​ E 段地址:偏移地址 原数据.新数据 原数据.新数据……

5.U命令间隔你存中的机器指令翻译成汇编指令

  • E 段地址:偏移地址 -写入地址(写入的全是对应的机器码)
  • D 地址-查看
  • U 地址-查看代码

6 . A-以汇编指令的格式写入命令:A 地址

7 .T-执行机器指令:CS:IP 处的指令(单步执行)

8 . q退出

CS:IP与代码段

CS:代码寄存器

IP:指令指针寄存器(只有CPU自己能够进行修改,不能用mov)

1.从所指向的内存单元读取指令,读取的指令进入指令缓冲器

2.IP=IP+所读取指令的长度,从而指向下一条指令

3.执行指令,转到步骤1,重复过程

先-r看一哈cs和ip的指向哪里,再修改值,向相应的单元写入数据,再A,再U查看,也可以用D

jmp指令

修改CS,IP 的方法:

1.rcs,rip进行修改,但是不现实,debug是调试手段

2.转移指令jmp:

​ 同时修改cs,ip:jmp 段地址:偏移地址

​ 仅修改ip的内容:jmp 某一合法寄存器(jmp ax=mov ip ax但是后者不能用)

jmp可以用于循环

内存中字的存储

对于8086CPU,16位作为一个字,16位的字在内存中需要两个连续字节存储:

低位字节存放在低地址单元,高位字节存放在高地址单元

eg:4B20H存放在0,1两个单元。20存放在0单元,4B放在1单元

​ 0地址单元中存放的字节数据是20H

​ 0地址字单元存放的字型数据是4E20H

读的时候先读高地址单元再读低地址单元

用DS和[address]实现字的传送

CPU从内存单元中读取数据:

​ 用DS寄存器存放要访问数据的段地址

​ 偏移地址用[……]的形式直接给出

eg:mov bx,1000h

​ mov ds,bx

​ mov al,[0]

以上代码段的意思是将1000:0处的数据读到al中

eg:mov bx,1000h

​ mov ds,bx

​ mov [0],al

以上代码段的意思是将al中的数据写到1000:0中

不支持将数据直接送入段寄存器,要先送入基础寄存器
每次读两个单元,如果为[0],则读的实际数据是[1] [0]

DS与数据段

将哪段内存当作数据段,段地址如何定,在编程时安排

​ 用DS存放数据段的段地址

​ 用相关指令访问数据的具体单元,单元地址有[address]给出

eg:累加数据段中前3个单元的数据:add al,[0] add al,[1] add al,[2]

​ 累加数据段中前3个字型数据:add ax,[0] add ax,[2] add ax,[4]

栈及栈操作的实现

栈是一种只能在一端进行插入或者删除的数据结构

入栈push:

​ push ax 将ax中的数据送入栈中

出栈pop:

​ pop ax 将栈顶的数据送入ax

都是以字为单位进行的操作,栈越往上内存地址越小
CPU如何知道一段内存被当作栈使用?

SS是栈段寄存器:存放栈顶的段地址

SP是栈顶指针寄存器:存放栈顶的偏移地址

任何时刻,SS:SP指向栈顶元素
栈的操作

push ax:

​ SP=SP-2

​ 将ax中的内容送入SS:SP指向的内存单元,指向新栈顶SS: SP

pop ax:

​ 将SS :SP指向的内存单元处的数据送入ax中

​ SP=SP+2

栈顶超界问题

栈顶超界是危险的,在执行push和pop指令都可能出现

CPU不保证栈顶不会超界

段的总结

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

数据段:

段地址放在DS中,与[address]搭配使用,用mov,add,sub等访问时将内存单元中的内容当作数据来访问

代码段:

CS和IP用于指向要寻找的代码段

栈段:

SS和SP,采用push和pop指令,其中SP的加减取决于指令,需要进行栈段的操作是就用这个

三个段地址可以一样

用汇编语言写的源程序

汇编程序——编译器——机器码——计算机执行

汇编代码中的汇编指令称为伪指令

段定义:

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

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

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

eg:段名 segment——段的开始

​ 段名 ends——段的结束

end:

不是ends ,若程序结尾处不加end,编译器在编译程序时不知道程序在何处结束

assume:

假设某一段寄存器和程序中某一个用segment……ends定义的相关段相关联——assume cs:codesg指的是CS寄存器和codesg关联,将定义的codesg当作程序的代码段使用

汇编程序.asm——可执行程序.exe:

1.在debug中直接写入指令:适用于功能简单,短小精悍

2.单独写源程序文件后再进行编译:适用于编写大程序,需要包括汇编指令还需要伪指令,由段构成

如何写一个程序:

编程求2^3:

定义一个段

实现处理任务

指出程序在何处结束

短语寄存器的关联

加上程序返回时的代码

1
2
3
4
5
6
7
8
9
10
11
assume cs:abc
abc segment
mov ax,2
add ax,ax
add ax,ax

mov ax,4c00h
int 21h;表示返回正常
abc ends
end

程序可能发现的错误:

语法错误:编译时被编译器发现

逻辑错误

由源程序到程序运行

编辑源程序:

写好asm文件

编译:

masm xx.asm,得到目标文件xx.obj,或者直接masm,再进行写文件名

连接:

link (obj的名字)

no stack segment可以不管

用debug跟踪程序运行

用法:debug xx.exe

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

这个内存区的前256个字节村PSP(段前缀),DOS用来和程序进行通信,256字节后的空间存放处是程序,CS的值为DS+10H

程序加载后CX中存放代码的长度

-t单步执行

-p类似t命令,但遇到子程序或者中断时,直接执行然后显示结果

[…] (…)

[]——在汇编语法中表示一个内存单元,根据ax和al判断操作单位是字还是字节:[bx]=((ds)*16+(bx))

()——在学习中为了方便做出的约定,表示一个内存单元或寄存器的内容:(ax)=0010H

idata——表示常量

inc :加一指令

loop指令

功能:循环(计数型循环)

CPU执行loop指令是要进行的操作

​ (cx)=(cx)-1

​ 判断CX中的值:不为0则转至表好处执行程序,如果为0则向下执行

要求: cx中要提前存放循环次数,因为(cx)影响这loop指令执行的结果,定义一个标号

1
2
3
4
5
6
7
8
9
10
11
assume cs:code
code segment
mov ax,2
mov cx,11
s:add ax,ax;计算2^12
loop s

mov ax,4c00h
int 21h
code ends
end

用loop实现循环的三个要点:

1.在cx中存放循环次数

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

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

1
2
3
4
5
6
7
8
9
10
11
12
;计算 123*236,用加法解决乘法
assume cs:code
code segment
mov ax,0
mov cx,123
s:add ax 256
loop s

mov ax,4c00h
int 21h
code ends
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;计算ffff:0006字节单元中的数乘以3,结果储存在DX中
assume cs:code
code segment
mov ax,0ffffh;汇编源程序中,数据不能以字母开头,要在ffff前加0
mov ds,ax
mov bx,6
mov al,[bx];将题目中内存单元的数据放入ax中,要求以字节单元
mov ah,0;将高八位置零

mov dx,0
mov cx,3
s:add dx,ax
loop s

mov ax,4c00h
int 21h
code ends
end
;要考虑会不会溢出

段前缀的使用

为了防止在程序段中的mov ax,[0]这种类似的形式在编译时被编译成mov ax ,00被当成了数据而不是偏移地址

对策:在[idata]前显式的加上寄存器:mov ax,ds:[bx]

访问连续的内存单元:loop和[bx]联手

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

对策:取出8位数据,加到16位寄存器

​ mov al,ds:[addr]

​ mov ah,0

​ add dx,ax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;计算ffff:0~ffff:b字节单元中的数据的和,结果储存在dx中?
;采用loop循环
assume cs:code
code segment
mov ax,0ffffh
mov ds,ax

mov bx,0
mov cx,11
mov dx,0
s:mov al,[bx]
mov ah,0
add dx,ax
inc bx
loop s

mov ax,4c00h
int 21h
code ends
end
;当程序段中有多个段的时候,如拷贝,此时段前缀很有用,es称为附加段寄存器,段前缀不一样,偏移量一样

在代码中使用数据

问题:

​ 在程序中直接写地址是危险的

对策:

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

​ 段的类别:数据段,代码段,栈段

​ 各种段中均可以有数据

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
;编程计算八个数据的和,结果存在ax寄存器中、
;dw定义一个字;db定义一个字节;dd定义一个双字
assume cs:code
code segment
dw 0123h,0456h,0789h,0defh,0fedh,0cbah,0987h;将所有数据以字的行书存放在代码段中

mov bx,0
mov ax,0
mov cx,8;用作循环

s:add ax,cs:[bx]
add bx,2
loop s

mov ax,4c00h
int 21h
code ends
end
;该程序前属于数据段,而ip存放的从0开始,所以该代码段存在不足
;要求从代码段开始
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
;改进
assume cs:code
code segment
dw 0123h,0456h,0789h,0defh,0fedh,0cbah,0987h;将所有数据以字的行书存放在代码段中

start:mov bx,0
mov ax,0
mov cx,8;用作循环

s:add ax,cs:[bx]
add bx,2
loop s

mov ax,4c00h
int 21h
code ends
end start
;此时代码的开始从代码段开始

在代码中使用栈

问题:利用栈,将程序中定义的数据逆序存放

对策:依次将八个子单元的数据入栈,在出栈,实现逆序。可以定义空来取得空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
assume cs:code
code segment
dw 0123h,0456h,0789h,0defh,0fedh,0cbah,0987h;将所有数据以字的行书存放在代码段中
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0;栈段,占用32个字节,此时代码段从30h开始,故此时的sp在30h

start:mov ax,cs
mov ss,ax
mov sp,30h

;入栈
mov bx,0
mov cx,8
s0:push cs:[bx]
add bx,2
loop s0

;出栈
mov bx,0
mov cx,8
s1:pop cs:[bx]
add bx,2
loop s0

mov ax,4c00h
int 21h
code ends
end start

将数据,代码,栈放入不同段

特点:三种都在一个段

功能:应用于要处理的数据很少,用到的占空间也小,加上没有多长的代码

对策:遇到很大的放入不同段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
assume cs:code,ds:data,ss:stack
data segmemt
dw 0123h,0456h,0789h,0defh,0fedh,0cbah,0987h;将所有数据以字的行书存放在代码段中
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start:
;初始化各段寄存器,CS是默认的,不需要初始化
mov ax,data
mov ds,ax
mov ax,stack
mov ss,ax
mov so,20h;实际所占内存加一

;入栈
mov bx,0
mov cx,8
s0:push [bx];此时不指明也知道就是ds中的
add bx,2
loop s

;出栈
mov bx,0
mov cx,8
s1:pop [bx]
add bx,2
loop s1

mov ax,4c00h
int 21h
code ends
end start

处理字符问题

汇编程序中,用’……’的方式指明数据是字符串,转化为ASCII码

大写字母的ASCII比小写的小20H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
;解决大小写转换的问题
;首字母大写全部转化为大写,首字母为小写全部转化为小写
assume cs:code,ds:datasg
datasg segment
db 'BasIc'
db 'iNfoRMaTiOn'
datasg ends
code segment
start:mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
s:mov al,[bx]
and al,11011111b;小写字母转化为大写
mov [bx],al
inc bx
loop s

mov bx,5
mov cx,11
s1:mov al,[bx]
or al,00100000b;大写字母转化为小写
mov [bx],al
inc bx
loop s1

mov ax,4c00h
int 21h
code ends
end start

[bx+idata]寻址方式

[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata

mov ax,[bx+200]的含义:

​ 将一个内存单元送入ax

​ 这个内存单元长度为两个字节,存放一个字

​ 内存单元的段地址在ds内,偏移地址为200加上bx中的数值

​ 数学化的描述为:(ax)=((ds)*16+200+(bx))

[bx+200]还能表示为[200+bx],[bx].200,200[bx]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
;用[bx+idata]处理上面的问题
;解决大小写转换的问题
;首字母大写全部转化为大写,首字母为小写全部转化为小写
assume cs:code,ds:datasg
datasg segment
db 'BasIc'
db 'iNfoRMaTiOn'
datasg ends
code segment
start:mov ax,datasg
mov ds,ax
mov bx,0
mov cx,5
s:mov al,[bx]
and al,11011111b;小写字母转化为大写
mov [bx],al

mov al,[bx+5];采用本节内容
or al,00100000b
mov [bx+5],al
inc bx;只在末尾加一次1就可以了
loop s

mov ax,4c00h
int 21h
code ends
end start

变址寄存器SI和DI

si和di是和bx功能相近的寄存器,也能[si+idata],[di+idata]
[bx+si] [bx+di]也可以用来指定地址

[bx+si]表示一个内存单元:偏移地址为bx的数值加上si的数值

bx叫做基址,si叫做变址

[bx+si+idata] [bx+di+idata]也可以用来指定地址

[bx+si+200]的其他写法:[bx+200+si] ,[bx].200[si] ,[bx] [sx].200,200[bx] [si]数值跟在寄存器后面要加点

寻址方式总结

[idata]直接寻址

[bx]寄存器寻址

[bx+idata]寄存器相对寻址

[bx+si]基址变址寻址

[bx+si+idata]相对基址变址寻址

用于内存寻址的寄存器用法

只有idata,bx,bp,si,di可以用于内存单元寻址,可以往方括号[]里面放

[bx+bp] [si+di]不能这样用,是错误的
bp默认的是SS段,bx默认的是DS段,如果指定了段前缀就用指定的段

数据的位置和长度

1.数据的位置

​ idata:称为立即数,数据包含在指令中

​ 寄存器:要处理的数据在寄存器中,给出相应的寄存器名字

​ 内存:段地址SA和偏移地址EA

2.数据的长度

​ 字:word操作,16位,ax

​ 字节:byte操作,8位,al

​ 未知ax或者al:用word ptr或者byte ptr指明(eg:mov word ptr ds:[0],1)

div除法指令

使用div做除法的时候:被除数默认放在AX和DX和AX中,除数放在寄存器或者内存单元中

除数为8位内存或者寄存器则AL放商,AH放余数,除数位16位寄存器或者内存,则AX放商,DX放余数

eg:div bx 被除数:(DX*10000H+AX) 除数:(BX) 商:AX 余数:DX

提前在默认的寄存器中设置好被除数,且默认寄存器不做别的用处,可以把其中的数据装在内存单元或者压栈

dup的功能和用法

功能:dup和db,dw,dd等数据定义伪指令配合使用,用来进行数据的重复

eg:

​ db 3 dup(0) 定义了三个字节,它们的值都是0 相当于db 0,0,0

​ db 3 dup(0,1,2) 定义了九个字节,由0,1,2重复构成

​ db 3 dup(‘abc’,’ABC’) 定义了18个字节,构成‘abcABCabcABCabcABC’,一个字符一个字节

1
2
3
4
;定义一个容量为200字节的栈段
stack segment
db 200 dup(0)
stack ends

操作符offset

用offset获得标号的偏移地址

eg:mov si,offset s

jmp无条件转移

无条件转移,可以只修改IP,也可以修改CS,IP

​ 段间转移(远转移):jmp 2000:0,源程序不能这样,只能在debug中使用

​ 段内短转移:jmp short 标号,八位的位移量是指标号处的地址与jmp后一条指令地址的差,差要在-128~127之间

​ 段内近转移:jmp near ptr 标号,十六位的转移量与上面差不多,但是差值的范围更大

​ 远转移:far ptr指明了跳转到的目的地址,包含了标号的段地址和偏移地址

​ 寄存器:jmp ax,IP=(AX)十六位

​ 内存:段内:jmp word ptr 地址两个字节

​ 段间:jmp dword ptr 地址,段地址在高位,偏移地址在低位四个字节

jcxz指令

格式:jcxz 标号

功能:如果(cx)=0,则转移到目的标号处执行

​ 当(cx)!=0时,则顺序执行

所有有条件的转移均为短转移

call指令和ret指令

调用子程序:call

返回:ret

call:

字面意思:调用子程序

实质:流程转移,与jmp相似

格式:call 标号

操作:

​ 将当前IP或者CS和IP压入栈中

​ 转移到标号处执行指令

​ 16位位移量

​ call far ptr 可以实现段间转移

相当于压栈和jmp的结合体
ret:

功能:用栈中的数据,修改IP的内容,实现金砖一,相当于pop IP

​ 没有call也可以用ret,实际上转移到出栈的数据所代表的位置

retf:可以实现远转移

乘法mul指令

八位乘法:被乘数放在AL中,结果放在AX中

十六位乘法:被乘数放在AX中,结果高十六位放在DX,第十六位放在AX

模块化程序设计

1
2
3
4
5
6
;根据提供的N,计算三次方
;用寄存器来存储出参数和结果是最常用的方法
cube:mov ax,bx;ax和dx用来保存最终的结果
mul bx
mul bx
ret
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
;计算第一段数据的三次方,结果保存在后面一组
assume cs:code,ds:data
data segment
dw 1,2,3,4
dd 0,0,0,0
data ends
codesegment
start:mov ax,data
mov ds,ax
mov si,0
mov di,8

;进行循环处理
mov cx,4
S:mov,bx,[si]
call cube
mov [di],ax;结果的低位放在ax中
mov [di.2,dx;高位放在dx中
add si,2
add,di,4
loop s

cube:mov ax,bx
mul bx
mul bx
ret

mov ax,4c00h
int 21h
code ends
end start
还可以用栈(栈顶sp放ip)和内存单元传递参数

标志寄存器

标志寄存器是按位起作用的,每一位都有专门的含义

8086的1,3,5,12,13,14,15位不具有任何意义

标志寄存器的作用:

​ 用来储存相关指令的某些执行结果

​ 用来为CPU执行相关指令提供行为依据

​ 用来控制CPU的相关工作方式

直接访问标志寄存器的方法:

​ pushf 将标志寄存器的值压入栈

​ popf 从栈中弹出数据,送入标志寄存器

ZF-零标志(zero flag)

ZF=1,表示结果是0

ZF=0,表示结果不是0

PF-奇偶标志(parity flag)

PF=1,1的个数是偶数

PF=0,1的个数是奇数

SF-符号标志(sign flag)

SF=1,结果为负

SF=0,结果为正

CF-进位标志(carry flag)

CF=1,有进位

CF=0,无进位

OF-溢出标志(overflow flag)

OF=1,有溢出

OF=0,无溢出

adc是带进位加法指令

利用了CF位上是否有进位

cmp和条件转移指令

格式:cmp 操作对象1,操作对象2

功能:计算操作对象1-操作对象2

应用:通过cmp指令执行后相关标志位的值,可以看出比较的结果、

1
2
3
4
5
cmp ax,bx
;ZF=1 (AX)=(BX)
;ZF=0 (AX)!=(BX)
;CF=1 (AX)<(BX),产生了借位
;CF=0 (AX)>=(BX),不必借位,当且仅当ZF=1时为等于

条件转移指令中各符号的意思:

j-jump e-equal n-not b-below a-above l-less g-greater s-sign c-carry p-parity o-overflow z-zero

1
2
3
4
5
6
7
;如果(ah)=(bh),则(ah)=(ah)+(ah),否则(ah)=(ah)+(bh)
cmp ah,bh
je s
add ah,bh
jmp short ok
s:add ah,ah
ok:ret

DF标志及串传送指令

DF=0,每次操作后si,di递增,否则递减。递增和递减的大小取决于传送的单位

movsb:以字节单位传送
movsw以字单位传送
rep指令

功能:根据cx的值,重复执行后面的指令

用法: rep movsb=s:movsb

​ loop s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code,ds:data
data segment
db 'welcome to masm!'
db 16 dup(0)
data ends
code segment
start:
mov ax,data
mov ds,ax
mov es,ax
mov si,0;源地址寄存器
mov di,16;目标地址寄存器
cld;将DF设置为0,std将DF设置为1
mov cx,8
rep movsw

mov ax,4c00h
int 21h
code ends
end start

移位指令

逻辑左移SHL

逻辑右移SHR

循环左移ROL

循环右移ROR

算数左移SAL

算术右移SAR

带进位循环左移RCL

带进位循环右移RCR

RO-rotate,C-carry

移位指令+OPR+CNT OPR代表操作数,CNT表述移动数位

用法:左移乘以2,右移除以二

1
2
3
4
mov al,00000001b;(al)=1
shl al,1;(al)=2
mov cl,3
shl al,cl;(al)=16

操作显存数据

屏幕上的数据=显存内的数据

显存空间:128kRAM 其中B8000H-BFFFFH共32k空间是80*25彩色字符模式第0页的显示缓冲区(80列25行)

每一行80个字,160个字节

低位字节放ASCII,高位字节放属性字节

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mov ax,data
mov ds,ax
mov ax,0b800h
mov es,ax
mov si,0
mov di,160*12+80-16

mov cx,16
w:mov al,[si]
mov es:[di],al
inc di
mov al,71h;颜色属性
mov es:[di],al
inc si
inc di
loop w
;直接显示

描述内存单元的标号

代码段中的数据也可以用标号

在code段使用标号但不使用冒号,使用字还是字节看后面,不加冒号的为数据标号,加冒号的为地址标号

数据标号同时描述内存地址和单元长度
地址标号只能在代码段使用
seg的作用是取段地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
assume cs:code
code segment
start:
mov al,60
call showsin
mov ax,4c00h
int 21h

showsin:
jmp shortshow
;字符串偏移地址表
table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180
ag0 db '0',0
……
;预备
push bx
push es
push si

mov bx,0b800h
mov es,bx
;取相应字符串的偏移地址放在bx内
mov ah,0
mov bl,30
div bl;相当于60/30
mov bl,al;结果在al中
mov bh,0
add bx,bx;每一个都是dw类型
mov bx,table[bx]
;显示对应字符串
mov si,160*12+40*2
shows:
mov ah,cs:[bx]
cmp ah,0
je showret
mov es:[si],ah;显示
inc bx
add si,2
jmp shows
;善后
pop di
pop es
pop bx
ret
code ends
end start

直接定址表

问题:以十六进制的形式在屏幕中间娴熟给定的byte型数据

分析:把一个byte的高四位和第四位分开,显示对应的数码字符

利用表,在两个数据集合之间建立一种映射关系,用查表的方法根据给出的数据得到其在另一集合中的数据

ah放功能号,al放属性号

中断及其处理

内中断:CPU内部发生的事件引起的中断

外中断:外部设备发生的事情引起的中断

8086的内中断:

​ 除法错误:div指令产生除法溢出;中断码0

​ 单步执行:中断类型码1

​ 执行into指令:中断类型码4

​ 执行int指令:执行int n指令,立即数为中断类型码

1
2
3
4
5
6
;lea取得相应标号地址,lea a,b 把b的地址放入a
;13,10回车换行
;'$'字符串
;int 21h处理dos信息
;mov ah,9显示字符串
;ds:dx显示的是从这里取了显示
中断处理程序

CPU接收到中断信息执行中断处理程序

中断信息和其处理程序的入口地址之间有某种联系

中断向量表可以由中断类型码查表得到中断处理程序

高位放CS,低位放IP

中断过程

中断过程由CPU的硬件自动完成

IP=4N,CS=4N+2

设置中断向量表

把程序的入口地址,写道中断向量表对应的n号表项中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
;求2*3456^2,写中断7ch的中断例程完成任务
;iret主要用于中断子程序中
assume cs:code
code segment
start:
mov ax,cs
mov ds,ax
mov si,offset sqr;源地址
mov ax,0
mov es,ax
mov di,200h;目的地址
mov cx,offset sqrend-offset sqr;循环次数
cld
rep movsb

mov ax,0
mov es,ax
mov word ptr es:[7ch*4],200h;ip
mov word ptr es:[7ch*4+2],0;cs
mov ax,4c00h
int 21h

sqr:
mul ax
iret
sqrend:nop
code ends
end start

;调用中断例程
code segment
start:
mov ax,3456
int 7ch
add ax,ax
mov ax,4c00h
int 21h
code ends
end start

单步中断

TF(陷阱标志)标志为1,让CPU处在单步中断方式下

IF(中断标志)标志为1,允许CPU相应可屏蔽中断请求,IF=0关闭中断

中断过程:

​ 取得中断类型码N

​ 标志寄存器入栈,TF,IF置为0

​ CS,IP入栈

​ IP=4N CS=4N+2

中断不响应的情况

ss:sp联合指向栈顶,对他们的涉资连续完成,不进行单步执行

由int指令引起的中断

中断过程与上方一致

BIOS基本输入输出系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
;bios中断调用示例
;在屏幕的五行十二列显示三个红底高亮闪烁绿色的'a';
assume cs:code
code segment
mov ah,2;置光标功能
mov bh,0;第0页
mov dh,5;第五行
mov dl,12;十二列
int 10h;bios10号中断例程

mov ah,9;显示字符功能
mov al,'a';
mov bl,11001010b;颜色属性
mov bh,0;第0页
mov cx,3;三遍
int 10h

mov ax,4c00h
int 21h
code ends
end start

端口的读写

in从端口读取数据和out输出数据

设备控制寄存器61h

端口对应除CPU以外的外部设备


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!