汇编语言笔记03内存访问
汇编语言笔记03内存访问
0x00 字的储存
cpu中,用16位寄存器来储存一个字,高8位存放高位字节,低8位存放低位字节,该种储存方式称为小端模式。 例如存放 4e20h和0012h
内存单元是一个字节,而字单元是连续的两个内存单元,存放一个字的两个字节。
0x01 DS和[address]
cpu要读写一个内存单元时,必须先给出这个内存单元的地址,在8086cpu中由段地址和偏移地址组成。ds寄存器,用来存放要访问数据的段地址,比如要读取10000h的内容
mov bx,1000h
mov ds,bx
mov al,[0]
即将10000h中的内容放在al中 mov可以 1将数据直接送入寄存器 2 将寄存器送入寄存器 3 将内存送入寄存器 反过来也行,mov al,[0]时自动从ds中读取段地址
8086不支持直接将数据送入段寄存器cs ds es ss 都不行 所以才先送到ax或者bx 再由ax或者bx送到段寄存器
mov bx,1000h
mov ds,bx
mov [0],al
0x02 字的传送
mov bx,1000h
mov ds,bx
mov ax,[0]
mov [0],cx
由于是ax和cx是16位寄存器,因此从内存中读写的时候也是取出或者写入2个字节一个字
0x03 mov add sub指令
mov有以下几种形式
反过来mov 寄存器,段寄存器 mov 内存单元,段寄存器 mov 段寄存器,内存单元都行
但是add和sub 不能对段寄存器使用
0x04 数据段
在编程时,可以根据需要将一组连续的内存单元定义为一个段。我们可以将一组长度为n(n<64kb) 地址连续,起始地址为16的倍数(主要是为了让偏移地址能从0开始)的内存单元当做专门储存数据的内存空间,从而定义了一个数据段。
比如用123b0h-123b9h这段空间存放数据,可以认为这段空间为一个数据段,段地址为123b,
段的长度为10个字节,那么访问这个数据段的代码如下
mov ax,123bh
mov ds,ax
mov al,0
add,al,[0]
add,al,[1]
add,al,[2]
上述代码累加数据段的前三个字节,下面代码累加数据段的前三个字型
mov ax,123bh
mov ds,ax
mov al,0
add,al,[0]
add,al,[2]
add,al,[4]
该题解题思路 ds为1那么段的起始地址为10h(要*16),所以后面就很简单了,主要注意的是最高位进位会被舍弃,例如 add al,bl时 al为0e6h bl为26h 相加为10ch,超过了8位 1被丢弃了 也不在ah上,所以ax为0ch
0x05 栈
在数据结构里,栈是一种先入后出的线性数据结构,而在内存里,栈是一种有特殊访问方式的储存空间,同样是最后进这个空间的数据,最先出去
这种规则称为LIFO(LAST IN FIRST OUT) 队列是FIFO
每一种cpu中都有栈的设计,8086也有,提供相关的指令来以栈的方式访问内存空间,push和pop push 寄存器 pop寄存器 (push和pop都是一次传输一个字,而不是一个字节)
上面的图描述了以下代码的工作流程
mov ax,0123h
push ax
mov bx,2266h
push bx
mov cx,1122h
push cx
pop ax
pop bx
pop cx
值得注意的是,往栈中存放数据,是由高地址向低地址进行的,每次一个字,然而字的储存方式仍然是小端模式,即低位字节对应低地址
问题:cpu如何知道某一段内存空间被当做栈地址来使用? push pop 在执行的时候如何知道哪个是栈顶单元?
这不禁让我们想到讨论过的另外一个问题,就是cpu如何知道当前要执行的指令所在位置?,用cs ip来存放段地址和偏移地址,那栈也是一样的 由两个寄存器 栈的段寄存器 ss 段的栈顶寄存器sp 任意时刻ss:sp执行栈顶元素
push ax 的执行实际是
1sp=sp-2,即ss:sp指向当前栈顶前面的单元,以其为新的栈顶
2将ax送入栈顶单元处,ss:sp指向新栈顶
pop ax反过来
1将栈顶单元处的字送入ax
2sp=sp+2,即ss:sp指向原来栈顶后面的单元,以其为新的栈顶
栈空时没有元素,也就不存在栈顶元素,所以ss:sp指向最底部单元下面的单元,该单元的偏移地址为最底部的字单元的偏移地址+2,栈最底部字单元的地址为1000:000e(不是1000:000f,以为一个字占2字节,最后一个字的起始地址是1000:000e),所以栈空时,sp=0010h
0x06 栈顶越界问题
8086cpu并没有记录栈的大小的寄存器,从而也没有检测push pop 时会不会越界的机制,所以我们编程时,要自己操心栈顶是否越界,要根据可能用到的最大栈空间,来安排栈的大小。
0x07 push pop指令
push和pop是可以在寄存器和内存(栈也是内存空间,只不过访问的方式比较特殊)之间传送数据
push 寄存器/段寄存器 pop 寄存器/段寄存器
实际上也可以在内存和内存之间传送数据
push 内存单元 pop 内存单元
如 mov ax,1000h
mov ds,ax ;内存单元的段地址放在ds里
push [0]
pop [2]
即通过push来将数据存入内存10000h处,那么该内存字单元必须设置成栈,那么ss为1000h栈顶指针-2要为0,所以sp为 02h
0x08 栈段
在编程时,可以根据需要,将一组连续的内存单元定义为一个段,我们将长度为n(n<64kb)的一组地址连续、其实地址为16的倍数的内存单元,当做栈空间使用,从而定义了一个栈段。
比如我们将10010h-1001fh这个段长度为16字节的内存空间当做栈来使用,以栈的方式访问,那么就可以称为一个栈段,栈地址为1001h,大小为16字节
这仅仅是我们自己的安排,要执行push和pop时自动访问我们定义的栈段当做栈空间,需要哦们将ss:sp指向我们定义的栈段
当只有一个元素时,栈顶指针指向0fffeh(因为栈的储存单元是字,因此一个元素的起始地址是0fffeh),所以出栈后栈顶指针sp+2=0000h(注意进位被丢掉了,只有进位标志被记录了)
0x08 检测题
要逆序复制,由已给出的部分可以看出是用栈段,而且是将1000h:0看做数据段 2000h:0看做栈段,那么只需要设置栈顶指针就行了,因为ss是段寄存器,不能直接用数据赋值,所以需要先mov到ax里,再由ax mov到ss
第二问发现是pop到2000h:0里,也就是2000h:0是数据段 ,1000h:0是栈段,那么也就是认为元素已经push进去了需要一个一个pop出来,所以ss设置为1000h,栈顶指针应该在最前面的元素那里,所以为0h,而不是在最底下(因为初始为满)