汇编语言:在代码段中使用栈

问题:完成下面的程序,利用栈,将程序中定义的数据逆序存放

程序运行时,定义的8个数据存放在CS:0~CS:F单元中,依次将这8个字单元中的数据入栈,然后再依次出栈,从而实现数据的逆序存放。

23CS:0
01CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F

这是代码段中定义的8个字数据


既然需要栈,栈在内存中需要内存空间,所以,用dw定义“空”数据来开辟栈的内存空间,代码如下

内存空间开辟的空间如下

23CS:0
01CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
0CS:20
0CS:21
0CS:22
0CS:23
0CS:24
0CS:25
0CS:26
0CS:27
0CS:28
0CS:29
0CS:2A
0CS:2B
0CS:2C
0CS:2D
0CS:2E
0CS:2F

下面开始进行栈的位置定义,如下

mov ax,cs  将代码CS段地址复制并移动到寄存器AX中

mov ss,ax  栈段开始的位置就是CS:0

mov sp,30H  栈顶指针是CS:30

23CS:0
01CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
0CS:20
0CS:21
0CS:22
0CS:23
0CS:24
0CS:25
0CS:26
0CS:27
0CS:28
0CS:29
0CS:2A
0CS:2B
0CS:2C
0CS:2D
0CS:2E
0CS:2F
CS:30这是栈顶指针

再下一步,进行入栈的操作,代码如下

mov bx,0   代表取内存地址处的数据,也就是第一步取CS:0处的数据,取16位

mov cx,8  循环8次

push cs:[bx]  将cs:bx处的数据压入栈,而栈是向下生长的,所以第一次cs:bx处的数据为0123压入到CS:2E中,如下所示

23CS:0
01CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
0CS:20
0CS:21
0CS:22
0CS:23
0CS:24
0CS:25
0CS:26
0CS:27
0CS:28
0CS:29
0CS:2A
0CS:2B
0CS:2C
0CS:2D
23CS:2E第一次循环从栈顶压入
01CS:2F
CS:30栈顶指针

add bx,2   偏移地址+2,cs:bx就变为了cs:2

loop s 开始循环->push cs:[bx] ,此时地址为cs:2,取CS:2地址处的数据压入栈,如下

23CS:0
01CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
0CS:20
0CS:21
0CS:22
0CS:23
0CS:24
0CS:25
0CS:26
0CS:27
0CS:28
0CS:29
0CS:2A
0CS:2B
56CS:2C第二次循环压入栈
04CS:2D
23CS:2E第一次循环压入栈
01CS:2F
CS:30栈顶指针

如此循环8次后,如果如下

23CS:0
01CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
87CS:20到此入栈完毕,栈指针停在此处
09CS:21
BACS:22
0CCS:23
EDCS:24
0FCS:25
EFCS:26
0DCS:27
BCCS:28
0ACS:29
89CS:2A
07CS:2B
56CS:2C
04CS:2D
23CS:2E
01CS:2F
CS:30

可以看出来,指定好栈的范围后,入栈操作就是从栈顶开始向下生长。


pop指令格式:

pop + destination  :destination是操作数,是指从当前栈指针指向的内存地址处读取数据,同时增加栈指针的值 ,将数据从栈中弹出后要存放的位置 ,而destination就是要存放的位置 。

push指令格式:

push + source :source是操作数,是指从栈的顶部向下移动,同时减小栈指针的值,将指定的数据压入栈中,而source就是指定的要压入栈中的数据来源位置。


了解了pop和push的命令后,我们知道 ,通过上述的push命令循环之后,目前的栈指针指向的是地址CS:20处,下面,使用出栈pop指令将栈指针处的数据弹出并存放在destination处。

mov bx,0  偏移地址重新定义为0,即cs:0

mov cx,8 循环8次

pop cs:[bx]   目前的栈指针是指向的CS:20处,弹出数据后,将数据存放在cs:[bx]处,也就是是CS:0处,栈指针增加,循环第一次的结果如下

87CS:0第一次循环后,数据存放在此处
09CS:1
56CS:2
04CS:3
89CS:4
07CS:5
BCCS:6
0ACS:7
EFCS:8
0DCS:9
EDCS:A
0FCS:B
BACS:C
0CCS:D
87CS:E
09CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
87CS:20
09CS:21
BACS:22栈指针自增后,指针指向此处
0CCS:23
EDCS:24
0FCS:25
EFCS:26
0DCS:27
BCCS:28
0ACS:29
89CS:2A
07CS:2B
56CS:2C
04CS:2D
23CS:2E
01CS:2F
CS:30

如此循环8次后,结果如下

87CS:0
09CS:1
BACS:2
0CCS:3
EDCS:4
0FCS:5
EFCS:6
0DCS:7
BCCS:8
0ACS:9
89CS:A
07CS:B
56CS:C
04CS:D
23CS:E
01CS:F
0CS:10
0CS:11
0CS:12
0CS:13
0CS:14
0CS:15
0CS:16
0CS:17
0CS:18
0CS:19
0CS:1A
0CS:1B
0CS:1C
0CS:1D
0CS:1E
0CS:1F
87CS:20
09CS:21
BACS:22
0CCS:23
EDCS:24
0FCS:25
EFCS:26
0DCS:27
BCCS:28
0ACS:29
89CS:2A
07CS:2B
56CS:2C
04CS:2D
23CS:2E
01CS:2F
CS:30

DOSBOX-X的实操

将上述代码存储 为p6-3.asm,并在DOSBOX-X中进行编译

IP(Instruction Pointer)指令指针寄存器:

可以看到IP=0030,说明指令执行是从上面的CS:30地址处开始执行的。


assume cs:code
code segment
    ; 第一块:8个字型数据(dw=define word,每个占2字节,共16字节)
    dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H  
    ; 第二块:16个0(作为栈空间,共32字节)
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  

start:  ; 程序入口
    ; 栈初始化:栈段=代码段,栈顶指针sp=30H(十进制48)
    mov ax,cs
    mov ss,ax
    mov sp,30H  

    ; 第一步:将前8个数据依次压入栈
    mov bx,0       ; 偏移地址初始化为0(指向第一个数据0123H)
    mov cx,8       ; 循环次数=8(对应8个数据)
s:  push cs:[bx]   ; 将cs:bx指向的数据压栈
    add bx,2       ; bx+2(指向下一个字型数据)
    loop s         ; cx--,cx≠0则跳回s

    ; 第二步:将栈中数据依次弹出,写回原数据区
    mov bx,0       ; 偏移地址重置为0
    mov cx,8       ; 循环次数重置为8
s0: pop cs:[bx]    ; 栈顶数据弹出到cs:bx指向的内存
    add bx,2       ; bx+2
    loop s0        ; cx--,cx≠0则跳回s0

    ; 程序退出
    mov ax,4c00h
    int 21h
code ends
end start

以上是对整体代码的总结解读。

可以看出来,数据、栈和代码都是在一个段中的,程序显得混乱,尤其是程序更加庞大的时候,编程和阅读时都要注意何处是数据,何处是栈,何处是代码,数据量太大了,非常混乱。上面这种程序只适用于处理数据很少的情况,用到的栈也很小,代码没有多长。

现实中的高级语言中,都是将数据、代码、栈存放在不同的段中。

将p6-4.asm 放入DOSBOX-X中进行编译并运行

IP=0000,可以看出来,代码执行是从0E29:0000处开始执行的。

posted @ 2025-12-05 09:25  chenlight  阅读(1)  评论(0)    收藏  举报  来源