描述性符号“()”

描述性符号()来表示一个寄存器或者一个内存单元中的!!内容!!

只能用来描述罢了

1
2
3
4
5
6
7
8
9
10
11
12
13
举个例子:
1.ax中的内容是0010H则
(ax)=0010H
2.2000:1000处的内容为0010H则
(21000H)=0010H
3.对于mov ax,[2]的功能,我们可以这样来描述:
(ax)=((ds)*16+2)
4.对于push ax
(sp)=(sp-2) ; 一定要注意是先改掉sp的位置在入栈
((ss)*16+(sp))=(ax) ;这个就是把ax中的值入栈
5.对于pop
(ax)=((ss)*16+(sp))
(sp)=(sp)+2

约定符号idata表示常量

1
2
3
4
5
6
7
8
9
比如说 mov ax,[idata] 就代表 mov ax,[1] mov ax,[2] 

在这里回顾一个知识点,段寄存器比如ds
不能使用
mov ds,[1]
所以在这里段寄存器就不能使用
mov ds,[idata]
因为段寄存器只能通过其他的寄存器来赋予
mov ds,[ax]

1.[bx]

[bx]和内存单元的描述

1.[bx]是什么呢?

​ 和[0]有些类似【这个[0]是在之前的ds:[]中有遇到的数据的段地址】[0]表示内存单元,它的偏移地址是0

1
2
mov ax,[0] ;这个是以字为大小的
mov al,[0] ;这个是以字节为大小的

2.我们要完整地描述一个内存单元,需要俩种信息:

1
2
1.内存单元的地址
2.内存单元的长度【类型】

3.我们在使用[0]表示一个内存单元的时候 , 0表示单元的偏移地址,段地址默认是ds!!单元的长度【类型】是由指令中的其他操作对象所指出的【寄存器,ax ,al】

4.[bx]同样也表示一个内存单元,它的偏移地址在bx中

1
2
3
4
5
6
7
8
9
10
11
12
13
mov ax,[bx]
mov al,[bx]

为什么要这么写呢,而不是直接 mov ax,[0]?
是因为:编译器和debug的认知不一样,在debug中它认为 mov ax,[0]就是把偏移地址=0的内存内容放进ax存储器中,但是!!在编译器中它会认为是mov ax,0 而不是mov ax,[0],
所以在使用编译器写的时候,需要使用到[bx]这样

例如:
mov bx,[0]
mov ax,[bx]
这样写就和在debug中
mov ax,[0]
是一样的

5.下面来简单的看一串代码分析分析最后的样子应该是什么样子的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mov ax,2000H	;在内存中21000H 是BE 21001H 是00
mov ds,ax
mov bx,1000H
mov ax,[bx] ;这里是把ds:[bs]中的内容放入ax中 也就是段地址:偏移地址==2000H:1000H也就是物理地址等
inc bx 于2000H*16+1000H ==>21000H
inc bx ;这个inc就是自加1就和++一样
mov [bx],ax
inc bx
inc bx
mov [bx],ax
inc bx
mov [bx],al
inc bx
mov [bx],al

一定要记住:高存高,低存低

最后得到应该是

1
2
3
4
5
6
7
8
21000H	BE
21001H 00
21002H BE
21003H 00
21004H BE
21005H BE
21006H BE
21007H

2.Loop指令

1.英文单词loop由循环的意思,显然这个指令就是和循环由关系

1
2
3
俩步走
1.CX=CX-1【这里是CX哦 而不是CS】【CX是一个新的通配符】
2.判断cx中的值,不为0则跳转到标号处,如果是0则执行吓一条指令

2.这说明,CX中的值影响着LOOP指令的执行结果

3.通常下,我们用loop指令来实现循环功能CX中存放着循环的次数

小小的练习1

1.编程计算:2^12

1
2
3
4
5
6
7
8
9
10
11
assume cs:code
code segment
mov ax,2
mov cx,11 ;这里来定义一个11次循环
s:add ax,ax ;这里的s就是标号【可以随便定义】
loop s ;这里就类似于jmp 跳到了s去运行add ax,ax

mov ax,4c00H
int 21h
code ends
end

这里有一个标号: s 【这个东西是可以随便定义的】

image-20230410193828528

注意看这里 LOOP 0006 这个0006其实就是 s 【标号】 也就是IP的值 也就是CS:IP的偏移地址,所以这个标号是可以随便定义的【名字】,它总会跳到上这个标号的的偏移地址的位置

小小的总结

1.在CX中存放循环次数【只能是用CX 不能使用其他的!!】

2.loop指令中的标号所表示前面

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

1
2
3
	mov cx,循环次数
s:循环执行的程序段
loop s

小小的练习2

1.用加法计算123×236,结果放在ax中

2.分析一下咯

1
2
我有点蠢 就想到了将123加236次
设置ax,0 然后循环 add, ax,ax 236次

写代码咯

1
2
3
4
5
6
7
8
9
10
11
assume cs:code
code segment
mov ax,0
mov cx,236
s:add ax,123
loop s

mov ax,4c00H
int 21h
code ends
end

3.卧槽傻逼了

1
可以用236  +123次
1
2
3
4
5
6
7
8
9
10
11
assume cs:code
code segment
mov ax,0
mov cx,123
s:add ax,236
loop s

mov ax,4c00H
int 21h
code ends
end

3.在Debug中跟踪用loop指令实现的循环语句

1.将ffff:0006单元中的值*3后赋值给dx

补充一点知识点:

1
2
3
4
5
ffff:0006单元的中值 一个字节单元的值,也就是【XXH】但是通用寄存器dx的内存空间大小是字【XXXXH】

这里要知道:10和0010虽然那个不一样大,但是值是一样大的,所以这里要使用dl而不是,如果使用了dx,则dx中存放的值就是ffff:0006和ffff:0007一起组合在一起的字


ok,现在来分析咯

1
2
也就是说:我们不能直接使用dx 【这里视频里面用的ax,所以我们也用ax】
所以我们应该令(ah)=0,(al)=(ffff6H)

哈哈哈,写代码咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assume cs:code
code segment
mov ax,0ffffH ;在汇编源程序中,数据是不能以字母开头的,所以要+0
mov ds,ax
mov bx,6
mov al,[bx]
mov ah,0
mov dx,0

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

mov ax,4c00H
int 21H
code ends
end

问题来咯,如果是要成1000次,这不得+1000次,在用debug跟踪的时候不得t1000次,这不得把我累死

于是乎我们有了G命令来解决

1
2
g 命令是debug中的命令 
用法:g 偏移地址 ; 这样它就会跳到你所指向的偏移地址的地方而且中间的代码都会运行完全,循环也会指向完全

实操一下咯

1
用 u CS:IP来查看一下咯

image-20230410203021347

emmmm 这个dx的值看着有点奇怪,

image-20230410203359405

卧槽,这个好像不对耶,我左思右想………会不会是al溢出了哦【哈哈哈,一看就是溢出了】

这种情况咋解决呢?【书上和视频里面没有,全靠我自己咯】

tmd 我是傻逼 这个地方是123不是123H

所以代码没有问题

补充一点

1
2
3
4
5
我在调试的时候,发现
mov al,0
不能写成 mov al,00
至于为什么
我也不知道

4.Debug和汇编编译器Masm对指令的不同处理

1.debug和编译器中的不同理解

1
2
3
4
5
6
7
8
9
10
11
1.mov ax,[0]
这个在编译器里面写会被理解成
mov ax,0 这样就会犯错
但是在debug中就可以这么写

2.在debug中,默认的就是16进制
但是在编译器中16进制一定要写H

但是如果我们就是想在编译中写入[0]这样的形式呢?
那就需要写上段地址
mov ax,ds:[0]

2.在debug中的一些命令

1
2
3
4
5
6
7
r 查看
t 运行一步
u 把机器码转成汇编语言
a 以汇编的形式写入
a cs:ip 回车
d 查看
d 段地址:偏移地址

5.loop和[bx]的联合应用

1.在计算处的结果存放在dx中,要稍微的判断一下这个值,是否能完美的装入dx中国,会不会溢出?

2.在计算ffff:0~ffff:b单元的数据和,不能将数据直接累加到dx中,因为没一个单元中的数据都是8位的,不能直接加到16位寄存器dx中

1
2
那是否可以加到dl中呢?
同样的问题,不能丢失数据

3.所以这里有2个问题

1
2
1.类型匹配的问题
2.结果越界的问题

4.解:

1
我们将内存单元的8位数据赋值到一个16位寄存器ax中,在将ax中的数据累加到dx中,从而使得运算对象的类型匹配,并且不会越界

.在计算ffff:0~ffff:b单元的数据和

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume cs:code
code segment
mov ax,0ffffH
mov ds,ax
mov bx,0

mov dx,0

mov cx,12

s: mov al,[bx] ;这里就是把ds:[bx]中的值放入al中
mov ah,0 ;我感觉这个地方有点浪费了,但是我也不知道如何改进,
add dx,ax
inc bx ;inc 就是自增
loop s

mov ax,4c00H
int 21h

code ends
end

在这里只能用变量 bx 可以用来自增, 不能用常量哦

mov al,[bx]中的bx就可以看作一个代表内存单元地址的变量,我们可以不写新的指令,仅仅通过改变bx中的数值,改变指令访问的内存单元

6.段前缀

1.指令mov ax,[bx]中,内存单元的偏移地址由bx给处,而段地址默认是ds中

2.我们可以在访问内存单元的指令中显示地给出内存单元的段地址所在的段寄存器

3.这些出现在访问内存单元中的指令中,用于显示地指明内存单元的段地址的ds,cs,ss,es,在汇编语言中就把这些称为段前缀

1
在没有用段前缀的时候,就会默认使用ds

7.一段安全的空间

我们应该是自由,直接的用汇编语言去操作真实的硬件,了解那些早已被层层系统软件掩盖的真相

在dos下,一般在0:200这里开始就可以操作

8.段前缀的使用

考虑到一个问题:

1
将内存ffff:0~ffff:b单元中的数据拷贝到0:200~0:20b单元中

分析一下

1
2
3
4
5
6
7
8
1.0:200~0:20b单元等同于0020:0~0020:b单元,他们描述的是同一段内存空间
2.拷贝过程应用用循环实现:
初始化 : X=0
循环12次
将ffff:X单元中的数据送入0020:X(需要用一个寄存器中转一下咯)
X=X+1
3.在循环中,源单元中ffff:X和目标单元的0020:X的偏移地址X是变量。我们用bx来存放
4.我们用将0:200~0:20b用0020:0~0020:b描述,就是为了使目标单元的偏移地址和源始单元的偏移地址从同一数值0开始

写代码咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code
code segment
mov bx,0 ;(bx)=0,偏移地址从0开始
mov cx,12 ;(cx)=12,循环12次

s: mov ax,0ffffH
mov ds,ax ;(ds)=0ffffh
mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl中
mov ax,0020H
mov ds,ax ;(ds)=0020h
mov [bx],dl ((ds)*16+(bx))=(dl),将dl的数据送入0020:bx中
inc bx ;(bx)=(bx)+1
loop s

mov ax,4c00H
int 21h
code ends
end

分析一下代码:

1
2
3
4
1.因为原单元ffff:X和目标单元0020:X相聚大于64KB,在不同的64KB段里,程序中每次都是设置俩次的ds,
这样的效率不高
2.所以,我们可以使用2个段寄存器分别存放原单元ffff:x和目标单元0020:x的段地址,这样就可以省略循环中需要重复做的12次设置ds的程序段
这里的俩个段段寄存器,一个是ds 一个是es

改进的代码来咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code
code segment
mov ax,0ffffH
mov ds,ax ;(ds)=0ffffH
mov ax,0020H
mov es,ax ;(es)=0020H , 这里的es是以多余的段,需要的时候就可以拿来用
mov bx,0 ;(bx)=0,此时ds:bx指向ffff:0,es:bx指向0020:0
mov cx,12 ;(cx)=12,循环12次

s: mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据送入dl
mov es:[bx],dl ;((es)*16+(bx))=(dl),将dl的数据收纳柜如0020:bx
inc bx ;(bx)=(bx)+1
loop s

mov ax,4c00H
int 21h
code ends
end

实验咯

实验4

(1)向内存0:2000:23f依次送入数据03f

分析一下:

1
我首先想到的是用栈段依次依次的送入,但是栈中的push都是以16个字节为单位的。不能满足条件,题目是8个字节,也就是一个内存单元来放入数据。放弃这个栈段的思路
1
2
这里送入的数据是0~64也就是一步一步自增
所以,首先找到这个内存地址,用ds:bx定位,然后用一个low寄存器从0开始依次送入,然后再递增

尝试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code
code segment
mov ax,0
mov ds,ax ;这里把段寄存器设置成0
mov bx,200H ;这里把偏移地址设置成200,下面在实现送入数据的同时自曾1
mov al,0 ;这里是把就是向把al中的数据送入ds:bx中,因为是从0开始,所以设置成0【感觉也不需要,因为上面的ax,0】

mov cx,64
s: mov [bx],al ;把al中的数据送入ds:bx中
inc bx
inc al
loop s

mov ax,4c00H
int 21h

ends
end

image-20230411102853483

(2)程序中只能使用9条指令,其中包括mov ax,4c00hint 21h

分析一下:

1
也就是说,这里地方就是要把上面的代码进行优化,使他只有9行

emmm,看了答案才知道咋搞…它就是说要把bx和al合并一下

1
也就是说内存是:0:200-0:23F,也就是物理地址是00200-0023f,可以把这个理解成0020:0~00

写一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
assume cs:code
code segment

mov ax,20h
mov ds,ax;设置ds=0

mov cx,64;循环64次
mov al,0;传送的数据

s: mov [al],al
inc al
loop s

mov ax,4c00h
int 21h

code ends
end

抄的答案不对??

image-20230411105420647

emmm

使用寄存器间接寻址时,只可以使用 BX, BP, SI, DI 这四个寄存器中的一个,不可以使用其它寄存器。

也就是说这里的[al]是不行的

长知识了,间接寻址的时候,不能用al,只能用BX BP SI DI

重新来一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;又找到了一个答案,看看是怎么个费事
;向内存`0:200-0:23F`依次传送数据0~63(3FH)
assume cs:code
code segment

mov bx,0h
mov ds,bx ;这里ds就是0
mov ax,200h ;这一段是纯属没用的,要不要都可以

mov cx,64
s: mov [bx+200h],bx ;???在汇编里面可以用+号吗?
inc bx
loop s

mov ax,4c00h
int 21h

code ends
end

image-20230411110203257

image-20230411110743607

实践处真道理,可以用+号

这里用debug测试了一下可以用加号

image-20230411110928917

(3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume cs:code
code segment

mov ax,___
mov ds,ax ;ds的地址为ax
mov ax,0020h
mov es,ax ;es是20h
mov bx,0
mov cx,___

s: mov al,[bx] ;将ds;bx的值赋值给al
mov es:[bx],al ;将al的值赋值给es[bx]
inc bx ;bx++
loop s

mov ax,4c00h
int 21h

code ends
end

这串代码是将 “mov ax, 4c00H”之前的指令复制到内存0:200处,补全程序,上级调试

1
首先,在解决两个内存之前的命令的复制的时候,需要用到2个寄存器,一个存放的是目标地址,一个存放的是原地址

分析这个问题

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
首先,我们知道的是:我们要把mov ax,4c00H之前的指令复制到内存0:200处

mov ax,___
mov ds,ax ;ds的地址为ax

看到这里,我们所最后所希望的是什么?
s: mov al,[bx] ;将ds:bx的值赋值给al,这里的ds-->ax
mov es:[bx],al ;将al的值赋值给es[bx]
inc bx ;bx++
loop s

这个应该是我们所循环,也就是把命令复制过去的一个执行过程。仔细阅读

也就是说,把这个命令中的内容是放入al中,然后通过al 放入0:200处【物理地址是00200】【当然我们这个地方,也可以理解成 0020:0处】【这2中写法所得到的物理地址是一样的】

mov ax,___
mov ds,ax ;ds的地址为ax
mov ax,0020h
mov es,ax ;es是20h
mov bx,0
mov cx,___


因为我们所需要的是:把mov ax,4c00H中的东西放到al中 然后通过al放入0:200处【也就是我们写的es:[bx]中】
然后这里,ds:[bx]中的ds是由 ax决定 ax由我决定,所以这里ax=cs 就可以定位到ax,4c00H之前的内容

问题1.复制的是什么?

1
2
复制的是,你所写的代码,也就是上面给出的命令, 要将这些代码复制到0:200处
因为复制的是命令,所以要用cs:ip

问题2.复制的是什么?有多少个字节?你如何知道要复制的字节的数量?

答:

1
2
第一个题是 cs
第二个空是 17H

image-20230411122659855

哈哈哈【不算太理解第二个空耶】