读书笔记:汇编语言 第三版 王爽 清华出版社 章六 章七 章八 章九 章十

第六章 包含多个段的程序
    6.0 概述
        合法地通过操作系统取得的空间都是安全的
            操作系统不会让多个程序之间发生空间冲突
        程序向操作系统获得空间的方法
            程序加载时分配
                在程序中定义各种段
            程序运行时分配
                通过指令向操作系统获取
    6.1 在代码中使用数据
        在程序中定义需处理的数据,这些数据会被编译、连接后作为程序的一部分写入可执行文件中
        当可执行文件中的程序被加载至内存时,数据亦被载入,此时也获得了数据所含的空间
        在代码段开头定义数据
            定义数据
                dw,define word
                db,define byte
                dw 1000h, 10001h
            程序入口地址
                当不定义入口地址时
                    (CS) = (DS) + 100H
                    (IP) = 0
                    此时在代码段开头定义数据,会出现程序入口指向异常
                定义地址
                    end label
                    程序入口地址为label
                存储地址
                    可执行文件组成
                        程序,来自于源程序中的汇编指令和数据
                        描述信息,主要是编译连接时,对源程序中伪指令的处理所得
                    保存在描述信息中
                使用地址
                    加载程序从可执行文件中读取地址,并设置CS:IP
        程序框架
            assume cs:code
            code segment
                // data
            start:
                // code
            code ends
            end start
    6.2 在代码段中使用栈
        与在代码段中使用数据类似
        栈关系的是空间,不在乎开辟空间使用的占位符
    6.3 将数据、代码、栈放入不同的段
        将代码、数据、栈放入同一段的问题
            程序混乱
            三者共享一个段的地址空间,会限制三者的应用
        代码、数据、栈放在不同的段中
            定义段
                assume cs:code, ds:data, ss:statck
                code segments
                code ends
                data segments
                data ends
                statck segments
                statck ends
            使用段
                段名,是标号,是地址常量,指向段的开始
                段,是一段地址连续的内存空间,其上存有数据
                段及其数据的类型
                    段及其数据没有类型
                    他是什么,取决于他干了什么
                ss:stack
                    被当做栈用
                    mov ax, stack
                    mov ss, ax
                ds:data
                    被当做数据用
                    mov ax, data
                    mov ss, ds
                cs:code
                    被当做代码用
                    end start
    6.4 编写、调试具有多个段的程序
        程序中段的空间分配
            因为段的首地址是16倍数
            所以段以16个字节为单位进行分配
        link警告
            no stack segment
            不理会,需手动处理SS和SP
            理会,加载程序会帮忙设定SS:SP,根据可执行文件中的描述信息
                assume ss:stack
                stack segment stack
                stack ends
        一般使用bx进行正向循环,配合栈可实现反向循环
        masm611中不能以"c"为标号,否则会报语法错误

第七章 更灵活的定位内存地址的方法
    7.1 and和or指令
        按位与
            二元操作指令,格式类似于add
                AND 寄存器, 数据
                AND 寄存器, 寄存器
                AND 寄存器, 内存
                AND 内存, 寄存器
            有0为0,全1为1
        按位或
            二元操作指令,格式类似于add
                OR 寄存器, 数据
                OR 寄存器, 寄存器
                OR 寄存器, 内存
                OR 内存, 寄存器
            有1为1,全0为0
    7.2 关于ASCII码
        ASCII,一种字符编码方案
        编码方案,一套映射规则,目标对象到数字
    7.3 以字符形式给出的数据
        定义数据
            db 'hello'
        指令常量
            mov al, 'a'
        单个字符会转换成相应的ASCII码(单个字节)进行存储
    7.4 大小写转换的问题
        将字符转换成大写形式
            5号位置0
            与上11011111
        将字符转换成小写形式
            5号位置1
            或上00100000
    7.5 [bx+idata]
        地址的变化度分离
            段地址和偏移地址的分离
                段内的不同存储单元
                    拥有不同的偏移地址,相同的段地址
                    地址不同,属于同一段
            基址和偏移的分离
                数组类的不同存储单元
                    拥有不同的偏移,相同的基址
                    地址不同,属于同一数组
        偏移地址的表示
            常量        [idata]         ((ds)*16+idata)
            变量        [bx]            ((ds)*16+bx)
            变量+常量    [idata+bx]      ((ds)*16+idata+bx)
        格式
            mov ax, [idata+bx]        // idata表示基址
            mov ax, [bx+idata]        // bx表示基址
            mov ax, idata[bx]
            mov ax, [bx].idata
    7.6 用[bx+idata]的方式进行数组的处理
        将第一个字符串转换为大写,第二个字符串转换为小写
        汇编语言
            assume cs:code, ds:data
            data segment
                db 'BaSiC'
                db 'MinIX'
            data ends
            code segment
            start:  mov ax, data
                    mov ds, ax
                    mov bx, 0
                    mov cx, 5
            s:      mov al, [bx]
                    and al, 11011111b
                    mov [bx], al
                    mov al, 5[bx]
                    or  al, 00100000b
                    mov 5[bx], al
                    inc bx
                    loop s

                    mov ax, 4c00h
                    int 21h
            code ends
            end start
        C语言
            char a[5] = "BaSiC";
            char b[5] = "MinIX";

            main()
            {
                int i;
                i = 0;
                do
                {
                    a[i] &= 0xDF;
                    b[i] &= 0x20;
                    ++i;
                }
                while (i<5);
            }
    7.7 SI和DI
        SI和DI寄存器(source和destination)
            可用于表示偏移地址,类似于BX
            不可拆分成两个8位寄存器使用
        例子
            mov ax, [si]            // 相对段地址的单次偏移
                (ax) = ((ds*16)+(si))
            mov ax, [si+idata]      // 相对物理地址的单次偏移
                (ax) = ((ds*16)+(si)+idata)
            mov ax, [di]
            mov ax, [di+idata]
    7.8 [bx+si]和[bx+di]
        mov ax, [bx+si]             // 相对段地址的双次偏移
            (ax) = ((ds*16)+(bx)+(si))
        也写作
            mov ax, [bx][si]
    7.9 [bx+si+idata]和[bx+di+idata]
        mov ax, [bx+si+idata]       // 相对物理地址的双次偏移
            (ax) = ((ds*16)+(bx)+(si)+idata)
        也写作
            mov ax, idata[bx][si]
            mov ax, [bx].idata[si]
            mov ax, [bx][si].idata
    7.10 不同的寻址方式的灵活引用
        [idata]       对指定偏移地址的访问,是点的访问
        [idata+bx]    对偏移序列的访问,是线的访问
            将数据中的单词首字母变为大写形式
                data segment
                    db '1. file123456789'
                    db '1. edit123456789'
                    db '1. view123456789'
                data ends
            步骤
                bx+=16
                    [3+bx]
        [bx+si] 对阵列的访问,是面的访问
            将数据中的单词变为大写形式
                data segment
                    db 'file 12312345678'
                    db 'edit 12312345678'
                    db 'view 12312345678'
                data ends
            步骤
                bx+=1
                    si+=1
                        [bx+si]
            注意
                双重循环时,注意cs的保存,可使用寄存器、普通内存、栈进行保存
                一般来讲,在需要暂存数据的时候,我们应该使用栈
            代码
                stack segment stack
                    db 0,0,0,0,0,0,0,0
                code segment
                    mov ax, data
                    mov ds, ax
                    mov ax, stack
                    mov ss, ax
                    mov sp, 16

                    mov bx, 0
                    mov cx, 3
                s:  push cx
                    mov si, 0
                    mov cx, 4
                s0: mov ax, [dx+si]
                    and al, 11011111b
                    mov [dx+si], al
                    inc si
                    loop s0
                    add bx, 16
                    pop cx
                    loop s

                    mov ax, 4c00h
                    int 21
                code ends
        [bx+si+idata]    对偏移阵列的访问,是面的访问
            将数据中的单词变为大写形式
                data segment
                    db ' file 1212345678'
                    db ' edit 1212345678'
                    db ' view 1212345678'
                data ends
            与[bx+si]类似

第八章 数据处理的两个基本问题
    8.0 概述
        数据处理时的数据定位
            数据的首地址
            数据的长度
        定义描述符号
            reg,表示寄存器
                ax,bx,cx,dx
                ah,al,bh,bl,ch,cl,dh,dl
                bp,si,di
                sp
            sreg,表示段寄存器
                cs,ds,ss,es
    8.1 bx,si,di和bp
        组合形式
            一个
                [bx+idata]
                [bp+idat]
                [si+idat]
                [di+idat]
            两个
                [bx+si+idata]
                [bx+di+idata]
                [bp+si+idata]
                [bp+di+idata]
        缺省段寄存器
            使用了bp,则为ss,否则为ds
    8.2 机器指令处理的数据在什么地方
        CPU内部
            寄存器
            指令缓冲器
        内存
        端口
    8.3 汇编语言中数据位置的表达
        指令缓冲器
            立即数(idata)
            数据在指令中,随同指令一起被读到指令缓冲器
            指令能够自动找到立即数
        寄存器
            给出寄存器编号
        内存
            物理地址
            段地址SA+偏移地址EA(excursion)
            段地址,保存在段寄存器中
                段寄存器
                    可隐式给出
                        偏移地址中使用了bp,则为ss,否则为ds
                    可显示给出
                        sreg:[X]
            偏移地址,通过寄存器和立即数组合而成
    8.4 寻址方式
        直接寻址            固定点(点,无变化)     [idata]
        寄存器间接寻址       变化点(线,一维变化)    [X],X为bx,si,di,bp
        寄存器相对寻址       变化偏移点                [X+idata]或[X].idata(idata相对于X变) 用于结构体
                            (偏移线)            [idata+X]或idata[X](X相对于idata变)     用于数组
                         (一维或二变化)           [X][idata](X和idata相对于段地址变)       用于二维数组
        基址变址寻址        变化线(面,二维变化)      [bx或bp][si或di](X和idata相对于段地址变)  用于二维数组
        相对基址变址寻址    变化偏移线(偏移面,二维变化)[bx或bp].idata[si或di]  用于表格(结构)中的数组项
                                                idata.[bx或bp][si或di]   用于二维数组
    8.5 指令要处理的数据有多长
        间接指定
            通过寄存器名
            如 mov [bx], ax
        直接指定
            word ptr
            byte ptr
            如 mov word ptr [bx], 1
        指令指定
            pop和push操作的是word
    8.6 寻址方式的综合应用
        数据处理
            原数据
                db 'DEC', 'Ken Pslen'
                dw 137, 40
                db 'PDP'
            新数据
                137 -> 38
                40+70
                'PDP' -> 'VAX'
        数据位置的描述
            段      > 数据块     > 数据项     > 数据子项
            段寄存器 > bx或bp    > idata     > si或di
            内存块   > 结构体     > 结构体成员 > 成员分量
        汇编语言
            assume cs:code, ds:data
            data segment
                db 60h dup (0)
                db 'DEC', 'Ken Oslen'
                dw 137, 40
                db 'PDP'
            data ends
            code segment
            start: mov ax, data
                    mov ds, ax
                    mov bx, 60h

                    mov word ptr [bx].12, 38
                    add word ptr [bx].14, 70
                    
                    mov si, 0
                    mov byte ptr [bx].16[si], 'V'
                    inc si
                    mov byte ptr [bx].16[si], 'A'
                    inc si
                    mov byte ptr [bx].16[si], 'X'

                    mov ax, 4c00h
                    int 21h
            code ends
            end start
        C语言
            struct company {
                char cn[3];
                char hn[9];
                int pm;
                int sr;
                char cp[3];
            }
            struct company dec = {"DEC", "Ken Oslen", 137, 40, "PDP"};
            main()
            {
                int i;

                dec.pm = 38;
                dec.sr += 70;
                
                i = 0;
                dec.cp[i] = 'V';
                ++i;
                dec.cp[i] = 'A';
                ++i;
                dec.cp[i] = 'X';

                return 0;
            }
    8.7 div指令
        除数可以在内存中或寄存器中
        除数为8位
            被除数为16位,在AX中
            商在AL
            余数在AH
        除数为16位
            被除数为32位,低16位在AX中,高16位在DX中
            商在AX
            余数在DX
        指令格式
            div reg
            div 内存单元
    8.8 伪指令dd
        dd, define double word, 4字节
        dd等数据定义伪指令,如同汇编指令一样
            可在段内任意地方使用
            都描述了或填充了段空间的数据
                数据定义伪指令,直接使用数据填充
                汇编指令,使用机器码填充
    8.9 伪指令dup
        配合数据定义伪指令使用,表示重复多次执行数据定义伪指令
        如
            db 3 dup (0, 1, 2)
            等价于
            db 0, 1, 2, 0, 1, 2, 0, 1, 2
    8.10 寻址方式在结构化数据访问中的应用
        程序一
            assume cs:code, ds:data, es:table

            data segment
            db '1975', '1976', '1977'
            dd 16, 22, 382
            dw 3, 7, 9
            data ends

            table segment
            db 3 dup ('year summ ne ?? ')
            table ends

            code segment
            start:  mov ax, data
                    mov es, ax
                    mov ax, table
                    mov ds, ax

                    mov cx, 3
                    mov bx, 0
                    mov bp, 0
            year:   mov si, 0
                    mov ax, es:[bp][si]
                    mov [bx].0[si], ax
                    mov si ,2
                    mov ax, es:[bp][si]
                    mov [bx].0[si], ax
                    add bx, 10h
                    add bp, 4
                    loop year

                    mov cx, 3
                    mov bx, 0
                    mov bp, 12
            summ:   mov si, 0
                    mov ax, es:[bp][si]
                    mov [bx].5[si], ax
                    mov si, 2
                    mov ax, es:[bp][si]
                    mov [bx].5[si], ax
                    add bx, 10h
                    add bp, 4
                    loop summ

                    mov cx, 3
                    mov bx, 0
                    mov bp, 24
            emp:    mov ax, es:[bp]
                    mov [bx].10, ax
                    add bx, 10h
                    add bp, 2
                    loop emp

                    mov cx, 3
                    mov bx, 0
            avg:    mov ax, [bx].5
                    mov dx, [bx].7
                    div word ptr [bx].10
                    mov [bx].13, ax
                    add bx, 10h
                    loop avg

                    mov ax, 4c00h
                    int 21h
            code ends

            end start
        程序二
            assume cs:code, ds:data, es:table

            data segment
            db '1975', '1976', '1977'
            dd 16, 22, 382
            dw 3, 7, 9
            data ends

            table segment
            db 3 dup ('year summ ne ?? ')
            table ends

            code segment
            start:  mov ax, data
                    mov es, ax
                    mov ax, table
                    mov ds, ax

                    mov cx, 3
                    mov si, 0
                    mov di, 0
                    mov bx, 0
            s:
                    mov ax, es:[si]
                    mov [bx].0, ax
                    mov ax, es:[2][si]
                    mov [bx].2, ax
                            
                    mov ax, es:[12][si]
                    mov [bx].5, ax
                    mov ax, es:[14][si]
                    mov [bx].7, ax
                    
                    mov ax, es:[24][di]
                    mov [bx].10, ax
                    
                    mov ax, [bx].5
                    mov dx, [bx].7
                    div word ptr [bx].10
                    mov [bx].13, ax

                    add si, 4 
                    add di, 2
                    add bx, 10h
                    loop s

                    mov ax, 4c00h
                    int 21h
            code ends

            end start

第九章 转移指令的原理
    9.0 概述
        转移指令,更新CPU从内存取指令的位置
        转移指令分类
            转移行为
                只修改IP,称为段内转移,远转移
                    IP修改的范围
                        -128~127        短转移
                        -32768~32767    近转移
                同时修改CS和IP,称为段间转移
            转移条件
                无条件转移指令
                条件转移指令
                循环指令
                过程
                中断
    9.1 操作符offset
        伪指令,获取标号的偏移地址
    9.2 jmp指令
        无条件转移指令
        同时支持、近转移、短转移
        指令参数
            绝对位置转移
                段地址和偏移地址
            相对位移转移
                转移位移
    9.3 相对位移转移的jmp指令
        段内短转移
            jmp short 标号
            二字节
        段内近转移
            jmp near ptr 标号
            三字节
        注意,标号用于计算转移位移,实际指令中仅包含位移数据,无标号的偏移地址数据
        行为
            (IP) = 转移指令后首字节的偏移地址 + 位移
            位移 = 标号的偏移地址 - 转移指令后首字节的偏移地址
                编译器计算
                负数,使用补码表示
                    正数,最高位为0,取反加一为负数
                    负数,最高位为1,取反加一后正数
    9.4 立即数的绝对位置转移的jmp指令
        段间远转移
            jmp far ptr 标号
            五字节
        行为
            (CS) = 标号的段地址
            (IP) = 标号的偏移地址
        jmp 标号的处理过程
            编译器中有一个地址计数器(AC)
                根据指令,在编译过程中不断增加
                遇到标号,会做记录
            向前(低地址)转移
                例子
                    s:  ...
                        jmp [short, near ptr, far ptr]s
                编译器先遇到标号,会记录标号的地址
                若转移位移属于[-128, 127]
                    jmp [short, near ptr, far ptr] s
                    转移指令,统一转化为"jmp short s"对应的机器码
                若转移位移属于[-32768, 32767]
                    jmp [short, near ptr, far ptr] s
                    转移指令
                        jmp short s,将报错
                        jmp [near ptr, far ptr]s,原格式编译
                            jmp s,等价于 jmp near ptr s
            向后(高地址)转移
                例子
                        jmp [short, near ptr, far ptr]s
                        ...
                    s:  ...
                编译器会先遇到jmp指令
                    各类转移指令,按原格式编译
                    参数使用nop指令填充
                        nop指令,一字节,无动作
                        jmp short s,共二字节,填充一字节
                        jmp near ptr s,共三字节,填充二字节
                        jmp far ptr s,共五字节,填充四字节
                编译器后遇到标号
                    若转移位移属于[-128, 127]
                        原各类转移指令,统一转化为"jmp short s"对应的机器码
                            未覆盖的空间,为nop,无影响
                    若转移位移属于[-32768, 32767]
                        jmp short s,将报错
                        jmp [near ptr, far ptr]s,在原指令处填充参数
    9.5 寄存器的绝对位置转移的jmp指令
        jmp 16为reg
            段内转移
            (IP) = (16位reg)
    9.6 内存的绝对位置转移的jmp指令
        jmp word ptr 内存单元地址
            段内转移
            (IP) = (内存单元地址)
        jmp dword ptr 内存单元地址
            段间转移
            (IP) = (内存单元地址)
            (CS) = (内存单元地址+2)
    9.7 jcxz指令
        jmp when cx equal zero
        jcxz指令,属于条件转移指令
            条件转移指令,属于短转移
                参数是一字节位移,[-128, 127]
        该指令一共两个字节
        jcxz指令行为
            若cx为0,则转移,否则继续
        jcxz,即有条件的jmp short s
            if (0==cx) jmp short s;
    9.8 loop指令
        该指令,属于条件转移指令
            条件转移指令,属于短转移
        该指令一共两个字节
        该指令行为
            --cx
            若cx不为0,则转移,否则继续
        有条件的jmp short s
            --cx;
            if (0!=cx) jmp short s;
    9.9 根据位移进行转移的意义
        相对关系与绝对关系的变化度分离
        便于程序段在内存中浮动装配
    9.10 编译器对转移位移越界的检查
        如jmp short s,支持的转移范围为[-128, 127],超过就会报错
    9.11 分析一个奇怪的程序
        问题,程序是否能够正常返回
        程序
            assume cs:code
            code segment
                    mov ax, 4c00h
                    int 21h
            start:  mov ax, 0
            s:      nop
                    nop
                    mov di, offset s
                    mov si, offset s2
                    mov ax, cs:[si]
                    mov cs:[di], ax
            s0:     jmp short s
            s1:     mov ax, 0
                    int 21h
                    mov ax, 0
            s2:     jmp short s1
                    nop
            code ends
            end start
        答案
            可以
            jmp short 不是绝对位置转移,而是相对位移转移
    9.12 根据材料编程
        要求
            在屏幕中间分别显示绿色、绿地红色、白底蓝色的字符串'welcome to masm!'
        知识
            80*25彩色字符模式显示缓冲区(简称显示缓冲区)的介绍
                显示缓冲区的
                向显示缓冲区写入数据,写入的内容立即出现在显示器中
                显示缓冲区结构
                    空间,[B8000H~BFFFFH],共32KB
                    一共分为8页,单页4KB,4096B
                    单页,25行,80列,共2000个字符
                    单个字符,两个字节,低字节为ASCII码,高字节为属性码
                    属性码组成
                        7   6   5   4   3   2   1   0
                        BL  BR  BG  BB  I   FR  FG  FB
                        闪烁 ( 背景色 )   高亮(  前景色 )
                一般情况下,显示0号页内容
                    [B8000H, B9000H]
        程序
            11行32列  绿色字符串
                首地址,B8000H+(11*80+32)*2,B8720H
                绿色,00000010,02H
            12行32列  绿底红色字符串
                首地址,B8000H+(12*80+32)*2,B87C0H
                绿底红色,00100100,24H
            13行32列  白底蓝色字符串
                首地址,B8000H+(13*80+32)*2,B8860H
                白底蓝色,01110001,71H
        代码
            assume cs:code, ds:data

            data segment
                    db 'welcome to masm!'
            dl1:    
            data ends

            code segment
            start:  mov ax, data
                    mov ds, ax
                    mov ax, 0b800h
                    mov es, ax

                    mov si, 0
                    mov di, 0
                    mov cx, offset dl1

            s:      mov al, [si]
                    mov es:720h.[di], al
                    mov es:7c0h.[di], al
                    mov es:860h.[di], al
                    inc si
                    inc di

                    mov es:720h.[di], 02h
                    mov es:7c0h.[di], 24h
                    mov es:860h.[di], 71h
                    inc di

                    loop s

                    mov ax, 4c00h
                    int 21h
            code ends

            end start

第十章 CALL和RET指令
    10.0 概述
        使用栈的转移指令
        ret,使用数据
        call,保存数据
    10.1 ret和retf
        ret
            用栈数据,修改IP
            等效指令 POP IP
        retf
            用栈数据,修改IP和CS
            等效指令
                POP IP
                POP CS
    10.2 call
        行为
            下一指令地址压栈
                将当前的IP或CS和IP压栈
            转移
        转移与jmp类似,不支持近转移
    10.3 相对位移转移的call指令
        call 标号
        等效指令
            push ip
            jmp near ptr 标号
    10.4 立即数的绝对位置转移的call指令
        call 标号
        等效指令
            push cs
            push ip
            jmp far ptr 标号
    10.5 寄存器的绝对位置转移的call指令
        call 16位寄存器
        等效指令
            push ip
            jmp 16位寄存器
    10.6 内存的绝对位置转移的call指令
        call word ptr 内存单元地址
            等效指令
                push ip
                jmp word ptr 内存单元地址
        call dword ptr 内存单元地址
            等效指令
                push cs
                push ip
                jmp dword ptr 内存单元地址
    10.7 call和ret的配合使用
        配合使用实现子程序的机制
        具有子程序的源程序框架
            assume cs:code
            code segment:
            main:   ...
                    call sub1
                    ...
                    mov 4c00h,
                    int 21h
            sub1:   ...
                    call sub2
                    ...
                    ret
            sub2:   ...
                    ret
            code ends
            end main
    10.8 mul
        格式
            mul reg
            mul [byte ptr|word ptr] 内存单元地址
        8位乘法
            被乘数,al
            乘数,指令参数,reg或内存单元地址
            结果,ax
        16位乘法
            被乘数,ax
            乘数,指令参数,reg或内存单元地址
            结果,低字ax,高字dx
    10.9 模块化程序设计
        现实的问题比较复杂,常被转化为多个相互联系、不同层次的子问题
        每个子问题,对应了一个子程序
        使用call和ret,可以实现多个相互联系、功能独立的子程序,来解决一个复杂问题
    10.10 参数和结果传递的问题
        使用寄存器来存储参数和结果的内容
            调用者,将参数送入参数寄存器,从结果寄存器获得结果
            子程序,从参数寄存器获得参数,将结果送入结果寄存器
        例子
            assume cs:code, ds:data
            data segment
                dw 1,2,3,4,5,6,7,8
                dd 0,0,0,0,0,0,0,0
            data ends
            code segment
            start:  mov ax, data
                    mov ds, ax

                    mov cx, 8
                    mov si, 0
                    mov di, 16
            s:      mov bx, [si]
                    call cube
                    mov [di], ax
                    mov [di].2, dx
                    add si, 2
                    add di, 4
                    loop s

                    mov ax, 4c00h
                    int 21h
            
            cube:   mov ax, bx
                    mul bx,
                    mul bx
                    ret
            code ends
            end start
    10.11 批量数据的传递
        方法一
            将批量数据转化为简单数据
            传值与传址
            使用寄存器来存储参数和结果的地址
            例子
                assume cs:code, ds:data
                data segment
                    db 'conversation'
                d1:
                data ends
                code segment
                start:  mov ax, data
                        mov ds, ax

                        mov si, 0
                        mov cx, offset d1
                        call capital
                        mov ax, 4c00h
                        int 21h
                
                capital:and byte ptr [si], 11011111b
                        inc si
                        loop capital
                        ret
                code ends
                end start
        方法二,用栈传递参数
            参数,属于临时数据,只需暂存,可以存放在栈中
                栈,可被用于暂存数据
            调用者,将参数压栈
            子程序,从栈中取参数
            例子一
                ;功能:计算(a-b)^3,a和b为字型数据
                ;参数:进入子程序时,栈顶存放IP,后面依次是a和b
                ;结果:(dx:ax)=(a-b)^3

                difcube:push bp
                        mov bp, sp
                        mov ax, [bp+4]
                        sub ax, [bp+6]
                        mov bp, ax
                        mul bp
                        mul bp
                        pop bp
                        ret 4
                ;ret n
                ;等效指令
                ;    pop ip
                ;    sub sp, n

                ;调用
                mov ax, 1
                push ax
                mov ax, 3
                push ax
                call defcube
            例子二
                void add(int, int, int);
                main()
                {
                    int a=1;
                    int b=2;
                    int c=0;
                    add(a, b, c);
                    c++;
                }
                void add(int a, int b, int c)
                {
                    c = a+b;
                }

                编译后的汇编程序
                mov bp, sp
                sub sp, 6
                mov word ptr [bp-6], 1
                mov word ptr [bp-4], 2
                mov word ptr [bp-2], 0
                push [bp-2]
                push [bp-4]
                push [bp-6]
                call ADDR
                add sp, 6
                inc word ptr [bp-2]

                ADDR:    push bp
                        mov bp, sp
                        mov ax, [bp+4]
                        add ax, [bp+6]
                        mov [bp+8], ax
                        mov sp, bp
                        pop bp
                        ret

                调用者,通过栈传入的参数,成为子程序的局部变量
    10.12 寄存器冲突的问题
        问题
            主程序和子程序,使用了相同的寄存器
            主程序的寄存器数据,被子程序覆盖
        方法
            程序块之间,接触寄存器使用的耦合性,不再相互依赖
            子程序中,开始时保存所用寄存器,结束时恢复所用寄存器
            寄存器数据,属于临时暂存数据,可使用栈保存
        子程序的框架
            子程序开始:
                子程序所用寄存器入栈
                子程序内容
                子程序所用寄存器出栈
                返回(ret,retf)
    10.13 编写子程序
        代码
            assume cs:code

            data segment stack
                    db '1975', '1976', '1977'
                    dd 16, 22, 382
                    dw 3, 7, 9
            data_1:
                    db 80 dup (' ')
                    db 0
            data_2:
                    db 65 dup (0)
            data ends

            table segment
            db 3 dup ('year', 0, 'summ ne ?? ')
            table ends

            code segment
            start:  mov ax, data
                    mov es, ax
                    mov ax, table
                    mov ds, ax

                    mov cx, 3
                    mov si, 0
                    mov di, 0
                    mov bx, 0
            s:
                    mov ax, es:[si]
                    mov [bx].0, ax
                    mov ax, es:[2][si]
                    mov [bx].2, ax
                            
                    mov ax, es:[12][si]
                    mov [bx].5, ax
                    mov ax, es:[14][si]
                    mov [bx].7, ax
                    
                    mov ax, es:[24][di]
                    mov [bx].10, ax
                    
                    mov ax, [bx].5
                    mov dx, [bx].7
                    div word ptr [bx].10
                    mov [bx].13, ax

                    add si, 4 
                    add di, 2
                    add bx, 10h
                    loop s

            ; display table
                    mov cx, 3
                    mov bh, 5 ; row index
                    mov di, 0
            main_s:
                    push cx
                    mov cl, 2
            ; erase original  display
                    mov dh, bh
                    mov dl, 0
                    mov ax, data
                    mov ds, ax
                    mov si, offset data_1
                    call show_str
            ; display year
                    mov dl, 1
                    mov ax, table
                    mov ds, ax
                    mov si, di
                    call show_str
            ; display summ
                    mov dl, 10
                    mov ax, data
                    mov ds, ax
                    mov ax, table
                    mov es, ax
                    mov ax, es:[di].5
                    mov dx, es:[di].7
                    mov si, data_2
                    call ddtoc
                    mov dh, bh
                    mov dl, 10
                    call show_str
            ; display ne
                    mov dl, 20
                    mov ax, es:[di].10
                    call dtoc
                    call show_str
            ; display ??
                    mov dl, 30
                    mov ax, es:[di].13
                    call dtoc
                    call show_str

                    pop cx
                    inc bh
                    add di, 16
                    loop main_s


                    mov ax, 4c00h
                    int 21h

            ;===================================================
                    ; parameters
                    ;       dh, row index, 0~24
                    ;       dl, column indexx, 0~79
                    ;       cl, symbol color
                    ;       ds:si, string address
                    ; result
                    ;       void
            show_str:
                    push ax
                    push dx
                    push di
                    push es
                    push cx

                    mov al, 80
                    mul dh
                    mov dh, 0
                    add ax, dx
                    mov di, 2
                    mul di
                    mov di, ax
                    mov ax, 0b800h
                    mov es, ax
                    mov ah, cl
                    mov cx, 0

            show_str_s:
                    mov cl, [si]
                    jcxz show_str_ret

                    mov al, [si]
                    mov es:[di], al
                    inc si
                    inc di

                    mov es:[di], ah
                    inc di

                    jmp show_str_s

            show_str_ret:
                    pop cx
                    pop es
                    pop di
                    pop dx
                    pop ax

                    ret

            ;====================================================
                    ; parameters
                    ;       ax, bei chu shu, low word
                    ;       dx, bei chu shu, high word
                    ;       cx, chu shu
                    ; result
                    ;       ax, shang, low word
                    ;       dx, shang, low word
                    ;       cx, yu shu
            divdw:  push ax
                    mov ax, dx
                    mov dx, 0
                    div cx
                    push ax

                    push bp
                    mov bp, sp
                    mov ax, [bp+4]
                    pop bp
                    div cx
                    mov cx, dx
                    pop dx

                    add sp, 2
                    ret

            ;====================================================
                    ; parameters
                    ;       ax, data will be converted
                    ;       ds:si, space will be store result
                    ; result
                    ; void
            dtoc:
                    push ax
                    push si
                    push bx
                    push dx
                    push cx
                    push di

                    mov bx, 10
                    mov di, 0
            dtoc_next:
                    mov dx, 0         
                    div bx
                    add dx, '0'
                    push dx
                    inc di
                    mov cx, ax
                    jcxz dtoc_ret
                    jmp dtoc_next

            dtoc_ret:
                    mov cx, di
            dtoc_copy:
                    pop [si]
                    inc si
                    loop dtoc_copy
                    mov [si], 0

                    pop di
                    pop cx
                    pop dx
                    pop bx
                    pop si
                    pop ax
                    ret       
            ;====================================================
                    ; parameters
                    ;       ax, data will be converted, low word
                    ;       dx, data will be converted, high word
                    ;       ds:si, space will be store result
                    ; result
                    ; void
            ddtoc:
                    push ax
                    push si
                    push dx
                    push cx
                    push di

                    mov di, 0
            ddtoc_next:
                    mov cx, 10
                    call divdw
                    add cx, '0'
                    push cx
                    inc di
                    mov cx, ax
                    or  cx, dx
                    jcxz ddtoc_ret
                    jmp ddtoc_next

            ddtoc_ret:
                    mov cx, di
            ddtoc_copy:
                    pop [si]
                    inc si
                    loop ddtoc_copy
                    mov [si], 0

                    pop di
                    pop cx
                    pop dx
                    pop si
                    pop ax
                    ret       
            ;====================================================

            code ends

            end start
        注意
            定义stack段时,注意段长要保持为偶数,否则编译器会自动(或前或后)增加字节
                在段头增加字节,可能影响数据的引用

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值