镇楼图
Pixiv:岸Yasuri
〇、BX寄存器的特性
通用寄存器中也有一类特殊的,BX寄存器可以像数据段地址的变量一样来使用
/*比如*/
mov [0],1234h
mov [2],5678h
/*为数据段ds:0~ds:3赋值*/
mov bx,0
mov ax,[bx]
add bx,2
mov ax,[bx]
其他大部分寄存器都无法做到这样,而bx寄存器却可以使用这样的语法
基于bx寄存器的特性,我们不能再简单地将bx寄存器理解为一个通用寄存器,它还常常作为数据段的一个地址变量
一、inc指令、loop指令与CX寄存器
inc
这个指令类似于C语言的++
inc bx
等效于add bx,1
loop
这个指令允许我们使用循环结构
比如求解\(2^{15}\)(目前还没学乘除,汇编语言也没有pow这样一个函数)
assume cs:code
code segment
mov ax,1
mov cx,15
s: mov ax,2
add ax,ax
loop s
/*这是一个循环结构*/
mov ax,4c00h
int 21h
code ends
end
标号
你在某一代码处设置标号s,在下面再写上loop <标号>
即可设置一个循环段
从s的这一行代码开始到loop结束为要循环的代码
这里不断循环的代码是
mov ax,2
add ax,ax
循环次数如何确定?
在8086汇编语言中,次数完全由CX来定,我们已经知道CX寄存器可以用来计算一个汇编程序的代码行数,但同时CX寄存器还担当了作为循环次数的一个作用
你设置CX的值为多少就循环多少次
mov cx,15/*设置次数为15*/
...
loop s/*会隐含地让cx-1*/
/*当cx为0时则退出循环
因此你设置cx为多少就循环就执行多少次
*/
循环结构框架
mov cx,次数
循环体
loop 标号
二、debug与masm的一个不同的细微的处理方式
debug是用来调试代码
masm是用来编译代码
在处理某一方面不太一样
assume cs:code
code segment
mov ax,2000
mov al,[0]
code ends
end
我们首先看一下从test.asm到test.obj再到test.exe经过masm编译器运行后的代码
再对比一下我在debug上写的
可以发现debug会处理成[0]
而masm会处理成0
[0]
的含义是偏移地址
而0
的含义是一个内存单元
就效率而言[0]
更高,我们可以将代码mov al,[0]
改成mov al,ds:[0]
这样可以达到与debug一样的处理方式,效率也更高
三、一个完整的程序
在汇编程序中你要详细地设置好各个段以及作用,一个段最多只能容纳64KB的内存
我们利用assume设置了代码段,同理我们也可以设置栈段和数据段
设置段
assume cs:code,ss:stack,ds:data
stack segment
...
stack ends
data segment
...
data ends
code segment
...
code ends
end
这里我定义code与cs链接起来,ss与stack链接起来,ds与data链接起来
这些段寄存器的作用不用多说了
其内存空间是由系统自动分配好的
我们无需再关心它到底是在哪一段,只需要知道其偏移地址即可,我们可以用BX寄存器索引数据段,sp索引栈段,ip索引代码段。
不过一般情况下我们只会去索引数据段
设置程序从哪开始
当设置的段多起来后我们需要一个标号来确定程序从哪开始,结束的是哪一段
assume cs:code,ss:stack,ds:data
stack segment
...
stack ends
data segment
...
data ends
code segment
start: ...
...
code ends
end start
这里我设置标号start,同时在下面的end伪指令表明我结束的是从start开始的那一段代码
dw指令——定义字型数据
dw指令类似于C语言里面的定义变量,不过有些不一样
在汇编里是定义一个整数或者更准确点你是定义一个4位十六进制数据
dw是定义一个字型数据(也就是定义一个占2字节的数据)
dw 1234
/*
定义字型数据1234H
类似于C语言的int a = 0x1234;
*/
dw 12
/*定义字型数据1200H*/
习惯上会在栈段和代码段那一块专门定义数据
四、现在能做点什么?
虽然现在学得有限,但还是能从内存角度实现一些功能
①内存数据COPY
下面代码能将data段的数据copy至stack段
assume cs:code,ds:data,ss:sg
data segment
dw 3031h,3233h,3435h,3637h,3839h
/*注意加上h,否则会默认为十进制的3031*/
data ends
sg segment stack
dt 1 dup (0)
/*这块的语法再下一节会有讲
可以忽略这行代码
*/
sg ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,10
s:
push ds:[bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
同理依据内存操作,你还能完成其他的操作,比如清空内存、数据交换等
LAST、关于寄存器
我们发现通用寄存器并不普通,似乎每个寄存器都具有特殊功能一样,但总体上就分为段寄存器、偏移地址寄存器、通用寄存器和标志寄存器(后续会讲)。
这三类具有一个最基本的功能,通用寄存器就是用来存储数据,段寄存器就是用来指向段,偏移地址寄存器就是指向段的偏移地址的内存
其余的功能如果你不使用你就可以只用它的基本功能
如果你在某一个块不用循环结构,那么CX就可以用来存储数据而不是专门去存储循环次数
如果在某一个程序不需要使用栈段,你就可以把SS指向的内存当作一个普通的内存,可以当成一个代码段也可以当成一个数据段