• 第16章 直接定址表


    引言

    这一章,我们讨论如何有效合理地组织数据,以及相关地编程技术。

    本章中,我们要用到这种标号,先进行如下介绍

    前面地课程中,我们一直在代码段中使用标号来标记指令、数据、段的起始地址。

    比如:下面的程序将code段中的a标号处的8个数据累加,结果存储到b标号处的字中。

     程序中,code、a、b、start、s都是标号。这些标号仅仅表示了内存单元的地址。

    但是,我们还可以使用一种标号,这种标号不但表示内存单元的地址,还表示了内存单元的长度,即表示在此标号处的单元,是一个字节单元,还是字单元,还是双字单元。

    上面的程序我们还可以写成这样:

     我们在code段中使用的标号a、b后面没有:,他们是同时描述内存地址和单元长度的标号。

    标号a,描述了地址code:0,和从这个地址开始,以后的内存单元都是字节单元;

    而标号b描述了地址code:8,和从这个地址开始,以后的内存单元都是字单元。

    因为这种标号包含了对单元长度的描述,所以,在指令中,它可以代表一个段中的内存单元。

    比如,对于程序中的b  dw  0

    指令  :mov  ax,b

    相当于:mov  ax,cs:[8]

    指令:mov  b,2

    相当于:mov word ptr cs:[8],2

    指令:inc  b

    相当于: inc word  ptr cs:[8]

    在这些指令中,标号b代表了一个内存单元,地址为code:8,长度为2字节。

    下面的指令会引起编译错误:

    mov  al,b

    因为b代表的内存单元是字单元,而al是8位寄存器。

    如果我们将程序中的指令:add  b,ax,写为add  b,al

    将出现同样的编译错误。

     可见,使用这种包含单元长度的标号,可以使我们以简洁的形式访问内存中的数据。

    以后,我们将这种标号称为数据标号。

    它标记了存储数据的单元的地址和长度。

    它不同于仅仅表示地址的地址标号。

    16.2  在其他段中使用数据标号

    一般来说,我们不再代码段中定义数据,而是将数据定义到其他段中。

    在其他段中,我们也可以使用数据标号来描述存储数据的单元的地址和长度。

    注意:在后面加有:的地址标号,只能在代码段中使用,不能在其他段中使用。

    注意,如果想在代码段中,直接用数据标号访问数据,则需要用伪指令assume将标号所在的段和一个段寄存器联系起来。

    否则编译器在编译的时候,无法确定标号的段地址在哪一个寄存器中。

    当然,这种联系是编译器需要的,但绝对不是说,我们因为编译器的工作需要,用assume指令将段寄存器和某个段相联系,段寄存器中就会真的存放该段的地址。

    我们在程序中还要使用指令对段寄存器进行设置。

    比如:在上面的程序中,我们要在断码段code中用data段中的数据标号a、b访问数据,则必须用assume将一个寄存器和data段相联。

    在程序中,我们用ds寄存器和data段相联,则编译器对相关指令的编译如下:

    指令:mov  al,a[si]

    编译为:mov al,[si+0]

    指令:add  b,ax

    编译为: add  [8],ax

    因为这些实际编译出的指令,都默认所访问单元的段地址在ds中,而实际要访问的段为data,所以,若要访问正确,在这些指令执行前,ds中必须为data段的段地址。

     我们可以将标号当作数据来定义,此时,编译器将标号所表示的地址当作数据的值。

    比如:

    data  segment

    a  db 1,2,3,4,5,6,7,8

    b  dw 0

    c  dw a,b

    data  ends

    数据标号c处存储的两个字型数据为标号a、b的偏移地址。

     

     数据标号c处存储的两个双字型数据为标号a的偏移地址和段地址、标号b的偏移地址和段地址。

     16.3  直接定址表

    现在,我们讨论用查表得方法编写相关程序得技巧。

    编写子程序 ,以十六进制得形式在屏幕中间显示给定得byte型数据。

    分析:

    一个字节需要两个十六进制数码来表示,所以,子程序需要在屏幕上显示两个ASCII字符。

     我们可以将一个byte得高4位和低4位分开,分别用他们得值得到对应的数码字符。

    比如2bh,我们可以得到高4位的值为2,低4位的值为11.

    那么我们如何用这两个数值得到对应的数码字符2和b呢

     

     更简洁的做法是:

    我们建立一张表,表中依次存储字符0~f,我们可以通过数值0~15直接查找到对应的字符。

    assume cs:code
    code segment
    start:  mov al,0eh
    
            call showbyte
    
            mov ax,4c00h
            int 21h
    
    ;子程序:
    ;用al传送要显示的数据
    
    showbyte:
            jmp short show
    
            table db '0123456789ABCDEF'    ;字符表
    
    show:   push bx
            push es
    
            mov ah,al
            shr ah,1           
            shr ah,1
            shr ah,1
            shr ah,1            ;右移4位,ah中得到高4位的值
            and al,00001111b        ;al中为低4位的值
    
            mov bl,ah
            mov bh,0
            mov ah,table[bx]        ;用高4位的值作为相对于table的偏移,取得对应的字符
    
            mov bx,0b800h
            mov es,bx
            mov es:[160*12+40*2],ah
    
            mov bl,al
            mov bh,0
            mov al,table[bx]        ;用低4位的值作为相对于table的偏移,取得对应的字符
            
            mov es:[160*12+40*2+2],al
    
            pop es
            pop bx
            ret
    
    code ends
    end start

    利用表,在两个数据集合之间建立一种映射关系,使我们可以用查表得方法根据给出得数据得到其在另一集合中得对应数据。

    这样做得目的一般来说有三个:

    1)为了算法得清晰和简洁

    2)为了加快运算速度

    3)为了使程序易于扩充

    上面得子程序中,体现得更多的是算法得清晰和简洁,下面这个例子是为了加快运算速度而采用的查表得方法。

    assume cs:code
    code segment
    start:  mov al,60
    
            call showsin
    
            mov ax,4c00h
            int 21h
    
    showsin:
            jmp short show
            table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180    ;字符串偏移地址表
            ag0      db '0',0            ;sin(0)对应的字符串“0”
            ag30     db '0.5',0            ;sin(0)对应的字符串“0.5”
            ag60     db '0.866',0            ;sin(0)对应的字符串“0.866”
            ag90     db '1',0            ;sin(0)对应的字符串“1”
            ag120    db '0.866',0            ;sin(0)对应的字符串“0.866”
            ag150    db '0.5',0            ;sin(0)对应的字符串“0.5”
            ag180    db '0',0            ;sin(0)对应的字符串“0”
    show:   push bx
            push es
            push si
    
            mov bx,0b800h
            mov es,bx
    
    ;以下用角度值/30 作为相对于table的偏移量,取得对应的字符串的偏移地址,放在bx中
            mov ah,0
            mov bl,30
            div bl
            mov bl,al
            mov bh,0
            add bx,bx
            mov bx,table[bx]
    
    ;以下显示sin(x)对应的字符串
            mov si,160*12+40*2
    shows:  mov ah,cs:[bx]
            cmp ah,0
            je showret
            mov es:[si],ah
            inc bx
            add si,2
            jmp shows
    
    showret:
            pop si
            pop es
            pop bx
            ret
    
    code ends
    end start

     

    下面,我们讨论一下各种功能如何实现:

    1)清屏:将显存中当前屏幕中的字符设为空格符。

    2)设置前景色:设置显存中当前屏幕中处于奇地址的属性字节的第0、1、2位

    3)设置背景色:设置显存中当前屏幕中处于奇地址的属性字节的第4、5、6位。

    4)向上滚动一行:依次将第n+1行的内容复制到第n行处:最后一行为空。

    ;编程:实现一个子程序setscreen,为显示输出提供如下功能:
    ;(1) 清屏。
    ;(2) 设置前景色。
    ;(3) 设置背景色。
    ;(4) 向上滚动一行。
    ;
    ;入口参数说明:
    ;(1) 用 ah 寄存器传递功能号:0 表示清屏,1表示设置前景色,2 表示设置背景色,3 表示向上滚动一行;
    ;(2) 对于2、3号功能,用 al 传送颜色值,(al) ∈{0,1,2,3,4,5,6,7}
    
    setscreen: jmp short set
    
        table  dw sub1,sub2,sub3,sub4
    
    set:    push bx
        
        cmp ah,3        ;判断传递的是否大于 3
        ja sret
        mov bl,ah
        mov bh,0
        add bx,bx        ;根据ah中的功能号计算对应子程序的地址在table表中的偏移
        
        call word ptr table[bx]    ;调用对应的功能子程序
    
    sret:    pop bx    
        iret
    
    ;功能子程序1:清屏
    sub1:   push bx
        push cx
            push es
            mov bx,0b800h
            mov es,bx
            mov bx,0
            mov cx,2000
    sub1s:  mov byte ptr es:[bx],' '
            add bx,2
            loop sub1s
            pop es
            pop cx
            pop bx
        ret ;sub1 ends
    
    ;功能子程序2:设置前景色
    sub2:    push bx
        push cx
        push es
        mov bx,0b800h
        mov es,bx
        mov bx,1
        mov cx,2000
    sub2s:    and byte ptr es:[bx],11111000b    
        or es:[bx],al 
        add bx,2
        loop sub2s
    
        pop es
        pop cx
        pop bx
        ret ;sub2 ends
    
    ;功能子程序3:设置背景色
    sub3:    push bx
        push cx
        push es
        mov cl,4
        shl al,cl
        mov bx,0b800h
        mov es,bx
        mov bx,1
        mov cx,2000
    sub3s:    and byte ptr es:[bx],10001111b
        or es:[bx],al 
        add bx,2
        loop sub2s
    
        pop es
        pop cx
        pop bx
        ret ; sub3 ends
    
    ;功能子程序4:向上滚动一行
    sub4:    push cx
        push si
        push di
        push es
        push ds
    
        mov si,0b800h
        mov es,si
        mov ds,si
        mov si,160            ;ds:si指向第n+1行
        mov di,0            ;es:di指向第n行
        cld
        mov cx,24;共复制24行
    
    sub4s:    push cx
        mov cx,160
        rep movsb             ;复制
          pop cx
        loop sub4s
    
        mov cx,80    
        mov si,0
    sub4s1: mov byte ptr es:[160*24+si],' '        ;最后一行清空
        add si,2
        loop sub4s1
    
        pop ds
        pop es
        pop di
        pop si
        pop cx
        ret ;sub4 ends
  • 相关阅读:
    HTML5 Input 类型
    Html5 web 储存
    解决json日期格式问题的3种方法(转载)
    Json格式串处理
    全局图片防盗链处理
    我的博客开张了
    iPhone手机屏幕分辨率
    通过CSS3伪类,美化Radio按钮样式
    测试用例 相关
    MongoDB基本命令
  • 原文地址:https://www.cnblogs.com/fate-/p/12989641.html
Copyright © 2020-2023  润新知