汇编_将数据、代码和栈放入不同的段

数据放在哪里

之前的程序,只有一个代码段,需要运算的数据直接编码在代码里,例如: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之间我迷糊了一段时间,一度搞不清楚一个字节是几位数据了。折腾了几天,程序开始像模像样了。

posted @ 2023-04-25 15:40  柠檬水请加冰  阅读(115)  评论(0编辑  收藏  举报