汇编_将数据、代码和栈放入不同的段
数据放在哪里
之前的程序,只有一个代码段,需要运算的数据直接编码在代码里,例如:mov ax,1。如果我们想计算多个数求和,不能也傻乎乎的add多次,因为多个数可能是不一样,循环都不可以用。
我们需要一段安全的空间用来存放数据。它更像是一个数组,占据一段连续的内存空间,通过[bx++]方式,可以对内存值索引。
在操作系统的环境中,合法地通过操作系统申请的空间都是安全的。加载程序时,操作系统会分配一段内存空间,操作系统会保证这段内存的安全性。我们需要在程序中定义这段内存空间,这样加载时就会自动纳入程序的一部分。
代码段中定义数据
assume cs:codesg ;假设某一段寄存器和某一个程序段相联系
codesg segment ;定义一个段
dw 0123h,0456h
mov ax,cs:[0]
mov bx,cs:[2]
add ax,bx
mov ax,4c00h
int 21h
codesg ends
end ;标识程序结束
在程序的开头定义了2个双字节的数据,这四个字节就放在cs代码段的开始位置。可是程序默认执行的位置cs:ip,默认指向开始的数据,翻译成指令,程序就不对了,所以我们只能手动修改ip指向执行。
虽然奇怪了一点,但是我们有了放置数据的地方。
多个段
数据直接定义在代码段中,会让cs:ip指向不正确,执行的时候还需要手动改一下代码段开始位置。一种容易想到的思路,代码和数据分开,代码段和数据段。
assume cs:codesg ;假设某一段寄存器和某一个程序段相联系
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 0,0,0,0,0,0,0,0
b ends
codesg segment ;定义一个段
start:
mov ax,a
mov ds,ax
mov ax,b
mov es,ax
mov bx,0
mov cx,4
s0:
mov ax,[bx]
mov es:[bx],ax
add bx,2
loop s0
mov ax,4c00h
int 21h
codesg ends
end start ;标识程序结束
这里定义了3个段,a b数据段和代码段,代码段这里加了start标识,标识程序从这里执行,不用我们再手动修改cs:ip指向。然后,mov ax,a,这里的a指的是数据段a的首地址,之后[bx]就可以取到数据段的值。
上一篇介绍了程序加载进内存时,存在256字节的程序段前缀(PSP),这里ds地址是075a,a的起始地址是076a。这表示a段是放在程序最开始的位置的。
有一点需要注意的是,db定义的单字节数据,但是mov ax,[bx],ax的值是0201。因为ax是一个16位寄存器,默认读取两字节数据。所以,bx设置为2,同样cx设置为4就可以读取全部数据。
实践
将a段和b段中的数据依次相加存入c段中
assume cs:codesg ;假设某一段寄存器和某一个程序段相联系
a segment
db 1,2,3,4,5,6,7,8
a ends
b segment
db 1,2,3,4,5,6,7,8
b ends
c segment
db 0,0,0,0,0,0,0,0
c ends
codesg segment ;定义一个段
start:
mov ax,c
mov ds,ax
mov cx,4
mov bx,0
s0:
mov ax,a
mov es,ax
mov ax,es:[bx]
mov [bx],ax
mov ax,b
mov es,ax
mov ax,[bx]
add ax,es:[bx]
mov [bx],ax
add bx,2
loop s0
mov ax,4c00h
int 21h
codesg ends
end start ;标识程序结束
代码的复杂性主要体现在段寄存器之间不能在一条指令上操作,思路很简单,就是借用了es完成数值中转。
使用push指令将a段中的前八个字节,逆序保存到b段中
assume cs:codesg ;假设某一段寄存器和某一个程序段相联系
a segment
dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends
b segment
dw 0,0,0,0,0,0,0,0
b ends
codesg segment ;定义一个段
start:
mov ax,a
mov ds,ax
mov ax,b
mov ss,ax
mov sp,16
mov bx,0
mov cx,8
s0:
push [bx]
add bx,2
loop s0
mov ax,4c00h
int 21h
codesg ends
end start ;标识程序结束
这里将b段当成一个栈段来使用,难点在合理的设置栈顶数值。因为debug时,cs:ip的值也会写入栈中,所以数据看起来有点奇怪。只关心栈顶元素,sp指向栈顶的前一位,定位查看对应的内存值。这里定义数据使用的是dw,每次push会让sp-2,需要用心体会。
写到这里的时候,ss和sp的设值有点忘记了,贴一下栈操作图示。
总结
这一章解决了一个大问题,程序可以读取数据了。这里学习下来,感觉es ds这些段寄存器之间切换,栈段的sp设值还是有点绕的,甚至dw和db之间我迷糊了一段时间,一度搞不清楚一个字节是几位数据了。折腾了几天,程序开始像模像样了。