《汇编语言》学习(六)多个段的程序

来源:互联网 发布:傻妹妹雾化器完美数据 编辑:程序博客网 时间:2024/05/12 09:52

    《汇编语言》第六章主要讲述“如何编写包含多个段的汇编源程序”。本章的文字内容不多,主要通过编程实践来理解汇编源程序架构的含义,通篇学下来,我觉得有以下几点需要理解清楚:


1,这里所说的多个段(segment),从汇编“程序”的角度来说,它指的是程序所占用的内存空间的划分,大致分为:data segment、stack segment和code segment。其实际就是指定运行时CPU的DS、SS和CS这三个寄存器的值。

2,但是,从源程序的角度来看,它指的是代码的组织方式。源程序分成多个段,是的代码的层次更清晰。

3,我们通常讲的多个段,更多的还是从源程序的角度来说的。事实上,一个段的源程序,通过设置CPU的寄存器也可以划分程序的内存空间。


4,汇编语言通过在源程序中使用段名(name)来标记各个段。

5,源程序中的段名(name)和标号(label),它们本质上都代表一个地址。在debug下反汇编,我们可以看到,段名和标号都被它们代码的地址值替代了。

6,不同的是,段名代表的是段地址(SA),标号代表的是偏移地址(EA)。


7,源程序通过“end+label”指定入口地址(entry),当然,这个入口地址仅仅是偏移地址。默认为0。

8,这个入口地址的信息被记录在.exe的描述信息中,对应“PSP(程序段前缀)”。

9,程序加载的时候,CS由系统指定(或者说由加载程序指定,shell);加载程序会搜索PSP,根据其中的entry,设置IP的值,默认为0。

10,严格来说,单段的程序只需要根据entry修改IP的值,多段的程序,则需要修改CS的值,CS的值按16位对齐,IP的值为0。

11,IP的值为16位,范围为0-255,它能开辟的空间有限,故单段的程序不适合需要大内存的应用。而CS表示段地址,它的变化范围就比较广了,可以开大得多的内存空间。



    本章的重点在于编程实践,下面我将介绍我的编程实践。

检测点6.1

程序补全

(1)内存复制

assume cs:codesgcodesg segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h start:mov ax,0       mov ds,ax       mov bx,0       mov cx,8     s:mov ax,[bx]       <span style="color:#ff0000;">mov cs:[bx],ax</span>       add bx,2       loop s       mov ax,4c00h       int 21hcodesg endsend start

(2)使用栈进行内存复制

assume cs:codesgcodesg segment dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h dw 0,0,0,0,0,0,0,0,0,0 start:mov ax,cs       mov ss,ax       mov sp,36       mov ax,0       mov ds,ax       mov bx,0       mov cx,8     s:push [bx]       pop cs:[bx]       add bx,2       loop s       mov ax,4c00h       int 21hcodesg endsend start

    以上两个程序都是单段程序,这个不是主流,我就不详细讲解了。


实验5

1,程序调试

问题回答:

1)CPU执行程序,程序返回前,data段中的数据不变;

2)CS=

3)设程序加载后,code段的段地址为X,则data段的段地址为X-16 ,stack段的段地址为X-32



    如上图,重点观察程序执行前后,ds、ss、cs和ip这几个寄存器的值。分析如下:

1)程序加载后,执行前,ds=0cd7h,ss=0ce7h,cs=0ce9h,ip=0000h

    根据第4章的知识可知,ds:0指向程序内存空间起始地址,或PSP首地址,而PSP为256Byte,故SS初始化时为0ce7h,即ss:0 - ds:0 = 256。本来cs的值也应该是0ce7h,但是该程序的源码中用“end+start”改变了入口地址。如果是在单段的程序,仅修改IP的值,但是这是一个多段的程序,直接修改了cs的值。

    cs:0 - ss:0 = 20h。这个20h刚好是data segment和stack segment所占的空间大小。

    需要注意的是:cs的值会保持16位对齐。也就是说,我们在源码中,删除几个(小于16)data segment或stack segment中的元素,重新编译、链接和加载后,在debug下查看,cs的值还是0ce9。至于那段空间,会自动补0,进行填充。

2)程序执行后,ds=0ce7,ss=0ce8,cs=0ce9

    ds和ss的值的变化实际上是程序执行的结果,即是程序员自己编写指令修改它们的。


2,程序调试

    这道程序调试题,与上一道相似,它的重点在问题4。实际上,它要表达的意思是:data segment、stack segment和code segment的段地址会按16位对齐。

    如果一个段中的数据占N个字节,则程序加载后,该段实际占有的空间为“ceil(N/16) * 16”。


3,程序调试

assume cs:code,ds:data,ss:stackcode segment start:mov ax,stack       mov ss,ax       mov sp,16       mov ax,data       mov ds,ax       push ds:[0]       push ds:[2]       pop ds:[2]       pop ds:[0]       mov ax,4c00h       int 21hcode endsdata segment dw 0123h,0456hdata endsstack segment dw 0,0stack endsend start

    这个程序是在上一道程序的基础上,调换源程序中各个段的顺序。debug如下:



如上图,重点观察程序执行前后,ds、ss、cs这几个寄存器的值。分析如下:

1)程序加载后,执行前,ds=0cd2h,ss=0ce6h,cs=0ce2h

    根据第4章的知识可知,ds:0指向程序内存空间起始地址,或PSP首地址,而PSP为256Byte,故CS初始化时为0ce7h,即cs:0 - ds:0 = 256。

    这个程序编译后,cs段提到了第一位。

2)程序执行后,ds=0ce5,ss=0ce6,cs=0ce2

    ds和ss的值的变化实际上是程序执行的结果,即是程序员自己编写指令修改它们的。

这道题的目的是要说明,在编译器看来,段名仅仅只是一种标记,谁排在前谁排在后,由源码的位置决定(编译先后不同)。

3)该程序,即使将“end start”改变为“end”,也不影响编译结果


5,编程题(求和)

assume cs:codea segment db 1,2,3,4,5,6,7,8a endsb segment db 1,2,3,4,5,6,7,8b endsc segment db 0,0,0,0,0,0,0,0c endscode segment start:mov bx,0       mov cx,8     s:mov ax,a       mov ds,ax       mov dx,[bx]       mov ax,b       mov ds,ax       add dx,[bx]       mov ax,c       mov ds,ax       mov [bx],dx       inc bx       loop s       mov ax,4c00h       int 21hcode endsend start

6,编程题(逆序复制)

assume cs:codea segment  dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffha endsb segment  dw 0,0,0,0,0,0,0,0b endscode segment  start:mov ax,a        mov ds,ax        mov ax,b        mov ss,ax        mov sp,10h        mov bx,0        mov cx,8      s:mov ax,[bx]        push ax        add bx,2        loop s        mov ax,4c00h        int 21hcode endsend start



0 0
原创粉丝点击