如何有效合理地组织数据,以及相关的编程技术。

描述了单元长度的标号

assume cs:code

code segment

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

b:dw 0

start:   mov si,offset a

mov bx, offset b

mov cx,8

s:   mov al,cs:[si]

mov ah,0

add cs:[bx],ax

inc si

loop s

mov ax,4c00h

int 21h

code ends

end start

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

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

比如:

assume cs:code

code segment

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

b dw 0

start:    mov si,offset a

mov bx, offset b

mov cx,8

s:   mov al,cs:[si]

mov ah,0

add cs:[bx],ax

inc si

loop s

mov ax,4c00h

int 21h

code ends

end start

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

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

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

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

指令:mov ax,b

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

指令:mob 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,将出现同样的编译错误。

对于程序中的a db 1,2,3,4,5,6,7,8:

指令:mov al,a[si]

相当于:mov al,cs:0[si]                            ;这种语法比较像高级语言中的数组语法。

相当于:mov al,cs:[si+0]

相当于:mov al,cs:[si].0

指令:mov al,a[3]

相当于:mov al,cs:0[3]

指令:mov al,a[bx+si+3]

相当于:mov al,cs:0[bx+si+3]

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

以后,我们将这种标号称为数据标号,它标记了存储数据的单元的地址和长度。它不同于仅仅表示地址的地址标号。

检测点16.1

下面的程序将code段中a处的8个数据累加,结果存储到b处的dword中,补全程序。

assume cs:code

code segment

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

b dd 0

start:    mov si,0

mov cs,8

s: mov ax,a[si]

add word ptr b,ax

adc word ptr b[2],0   ;双字单元的高字单元用来存放进位数值。

add si, 2     ;内存单元为字节单元。

loop s

mov ax,4c00h

int 21h

code ends

end start

在其他段中使用数据标号

一般来说,我们不在代码段中定义数据,而是将数据定义到其他段中。在其他段中,我们也可以使用数据标号来描述存储数据的单元的地址和长度。

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

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

assume cs:code, ds:data

data segment

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

b dw 0

data ends

code segment

start:    mov ax,data

mov ds,ax

mov si,0

mov ex,8

s: mov al,a[si]

mov ah,0

add b,ax

inc si

loop s

mov 4c00h

int 21h

code ends

end start

注意,如果想在代码段中,直接用数据标号访问数据,则需要用伪指令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段的段地址。则,我们在程序中使用指令:

mov ax,data

mov ds,ax

设置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的偏移地址。相当于:

data segment

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

b dw 0

c dw offset a, offset b

data ends

再比如:

data segment

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

b dw 0

c dd a,b

data ends

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

data segment

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

b dw 0

c dw offset a, seg a, offset b, seg b

data ends

seg操作符,功能为取得某一标号的段地址。

检测点16.2

下面的程序将data段中a处的8个数据累加,结果存储到b处的字中。补全程序。

assume cs:code,es:data

data segment

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

b dw 0

data ends

code segment

start:    mov ax,data

mov ds,ax

mov si,0

mov cx,8

s: mov al,a[si]

mov ah,0

add b,ax

inc si

loop s

mov ax,4c00h

int 21h

end start

直接定址表

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

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

分析:一个字节需要用两个十六进制数码来表示,所以,子程序需要在屏幕上显示两个ASCII字符。用“0”、“1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9”、“A”、“B”、“C”、“D”、“E”、“F”这16个字符来显示十六进制数码。

我们可以将一个byte的高4位和低4位分开,分别用它们的值得到对应的数码字符。比如2Bh,我们可以得到高4位的值为2,低4位的值为11,那么我们如何用这两个数值得到对应的数码字符“2”和“B”呢?

最简单的办法就是一个一个地比较,如下:

如果数值为0,则显示“0”;

如果数值为1,则显示“1”;

如果数值为11,则显示“B”;

这样做,程序中要使用多条比较、转移指令。程序将比较长,混乱。

显示,我们希望能够在数值0~15和字符“0”~“F”之间找到一种映射关系。这样我们用0~15间的任何数值,都可以通过这种映射关系直接得到“0”~“F”中对应的字符。

数值0~9和字符“0”~“9”之间的映射关系是很明显的,即:

数值+30h = 对应字符的ASCII值。

但是,10~15和“A”~“F”之间的映射关系是:

数值+37h = 对应字符的ASCII值。

可见,我们可以利用数值和字符之间的这种原本存在的映射关系,通过高4位和低4位值得到对应的字符码。但是由于映射关系的不同,我们在程序中必须进行一些比较,对于大于9的数值,我们要用不同的计算方法。

这样做,虽然使程序得到了简化。但是,如果我们希望用更简捷的算法,就要考虑用同一种映射关系从数值得到字符码。所以,我们就不能利用0~9和“0”~“9”之间与10~15和“A”~“F”之间原有的映射关系。

因为数值0~15和字符“0”~“F”之间没有一致的映射关系存在,所以,我们应该在它们之间建立新的映射关系。

具体的做法是,我们建立一张表,表中依次存储字符“0”~“F”,我们可以通过数值0~15直接查找到对应的字符。

子程序如下:

;用al传送要显示的数据

showtyte:       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            ;显示高4位的十六进制字符码

mov bl,al

mov bh,0

mov al,table[bx]            ;用低4位的值作为相对于table的偏移,对得对应的字符。

mov es:[160*12+40*2+2],al ;显示低4位的十六进制字符码

pop es

pop bx

ret

可以看出,在子程序中,我们在数值0~15和字符“0”~“F”之间建立的映射关系为:

以数值N为table青史的偏移,可以找到对应的字符。

利用表,在两个数据集合之间建立一种映射关系,使我们可以用查表现方法根据给出的数据得到其在另一个集合中的对应数据。这样做的目的一般来说有三个:

1)为了算法的清晰和简洁;

2)为了加快运算速度;

3)为了使程序易于扩充。

在上面的子程序中,我们更多的是为了算法的清晰和简洁,而采用了查表的方法。

编程的时候要注意程序的容错性,即对于错误的输入要有处理能力。

上面的例子,我们通给出的数据进行计算或比较而得到结果的问题,转化为用给出的数据作为查表的依据,通过查表得到结果的问题。具体的查表方法,是用查表的依据数据,直接计算出所要查找的元素在表中的位置。像这种可以通过依据数据,直接计算出所要找的元素的位置的表,我们称其为:直接定址表

程序入口地址的直接定址表

我们可以在直接定址表中存储子程序的地址,从而方便地实现不同子程序的调用。

实现一个子程序setscreen,为显示输出提供如下功能:

1)清屏;

2)设置前景色;

3)设置背景色;

4)向上滚动一行。

入口参数说明:

1)用ah寄存器传递功能号:0表示清屏,1表示设置前景色,2表示设置背景色,3表示向上滚动一行;

2)对于2、3号功能,用al传送颜色值,(al)∈{0,1,2,3,4,5,6,7}

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

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

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

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

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

我们将这4个功能分别写为4个子程序:

;清屏

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

;设置前景色

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

;设置背景色

sub3:       push bx

push cx

push es

mov cl,4

shr al,cl

mov bx,0b800h

mov es,bx

mov bx,1

sub3s: and byte ptr es:[bx],10001111b

shl al,1

shl al,1

shl al,1

shl al,1

or es:[bx],al

add bx,2

loop sub3s

pop es

pop cx

pop bx

pop bx

ret

;向上滚动一行

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

sub4s: push cx

mov cx,160           ;一行的长度为160个字节。

rep movsb             ;一次复制一行

pop cx

loop sub4s

mov cx,80

mov si,0

sub4s1:mov byte ptr [160*24+si],’ ‘   ;最后一行清空

add si,2

loop sub4s1

pop ds

pop es

pop di

pop si

pop cx

ret

我们可以将这些功能子程序的入口地址存储在一个表中,它们在表中的位置和功能号相对应。对应关系为:功能号*2=对应的功能子程序在地址表中的偏移。程序如下:

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

ret

当然,我们也可以将子程序setscreen如下实现:

setscreen:       cmp ah,0

je do1

cmp ah,1

je do2

cmp ah,2

je do3

cmp ah,3

je do4

jmp short sret

do1:     call sub1

jmp short sret

do2:     call sub2

jmp short sret

do3:     call sub3

jmp short sret

do4:     call sub4

sret:     ret

显然,用通过比较功能号进行转移的方法,程序结构比较混乱,不得功能的扩充。比如说,在setscreen中再加入一个功能,则需要修改程序的逻辑,加入新的比较、转移指令。

用根据功能号查找地址表的方法,程序的结构清晰,便于扩充。如果加入一个新的功能子程序,那么只需要在地址表中加入它的入口地址就可以了

汇编学习--7.16--直接定址表相关推荐

  1. 直接定址表---汇编学习笔记

    直接定址表 16.1 描述了单元长度的标号 很简单,自己看. 检测点 16.1 下面的程序将code段中a处的8个数据累加,结果存储到b处的双字中,补全程序. assume cs:code code ...

  2. [汇编学习笔记][第十六章直接定址表]

    第十六章 直接定址表 16.1 描述了单元长度的标号 格式 code segmenta db 1,2,3,4,5,6,7,8,b dw 0 功能 此时标号a,b 不仅代表了内存单元,还代表了内存长度 ...

  3. 汇编语言随笔(14)-直接定址表、检测点16和实验16

    标号 1,关于地址标号,a:,带有冒号的形式,之前我们一直使用.这种标号被用来标记指令,数据的起始地址,表示内存单元的起始地址.       后面带有冒号的地址标号,只能在代码段中使用,不能在其他段中 ...

  4. 直接定址表03 - 零基础入门学习汇编语言74

    第十六章:直接定址表03 让编程改变世界 Change the world by program 程序入口地址的直接定址表 我们看下面的问题: 实现一个子程序setscreen ,为显示输出提供如下功 ...

  5. 汇编访问计算机端口,汇编总结(2):中断、端口、直接定址表

    1.中断 CPU具有一种能力,可以在执行完当前指令之后,检测从内部产生或外部发来的特殊信息,并立即对其进行处理,这种特殊的信息,叫做中断信息. 中断即CPU不再接着刚执行完的指令向下执行,而是转去处理 ...

  6. 寄存器---汇编学习笔记

    第二章 寄存器 2.0 寄存器的绪论 一个典型的CPU由运算器.控制器.寄存器(CPU工作原理)等器件构成.内部总线实现 CPU 内部各个器件之间的联系,外部总线实现CPU和主板其他器件的联系. 在C ...

  7. 15 计算机底层——二进制到汇编学习

    计算机底层--二进制到汇编学习 1.概述 语言 机制 进制如何计算 二进制 数据宽度 有符号和无符号数 原码和反码 位运算 位运算计算 汇编 寄存器 内存 汇编指令 内存复制 堆栈的指令 汇编如何写函 ...

  8. Fasm---Win32汇编学习7

    Fasm---Win32汇编学习7 学习更多关于"绘制"文本串的知识 我们将做更多的实践去了解有关文本的诸多属性如字体和颜色等. 理论: Windows 的颜色系统是用RGB值来表 ...

  9. linux web高级编程,寒假学习 第16.17天 (linux 高级编程)

    寒假学习 第16.17天 (linux 高级编程) 笔记 总结 一.进程的基本控制(进程的同步) 1.进程的常见控制函数 pause   sleep/usleep atexit   on_exit i ...

最新文章

  1. 电脑ip地址设置_关于电脑的远程开机(唤醒)
  2. 【Android 事件分发】ItemTouchHelper 实现侧滑删除
  3. php9宫格抽奖程序_php抽奖算法(适用于九宫格、大转盘)
  4. 代码提示级别设置 inspection
  5. 设计模式之_Strategy_06
  6. webstorm 2018 破解
  7. oracle sql条件语句,谁能介绍下Oraclesql之条件语句?
  8. opencv的两个错误
  9. bzoj 2705: [SDOI2012]Longge的问题(欧拉函数)
  10. 安装完固态硬盘后计算机里没显示,安装了双硬盘电脑却不显示新硬盘怎么办
  11. python爬取qq音乐下载歌曲
  12. “ 作业帮 “ (Servlet)
  13. 【自动驾驶】碰撞检测算法
  14. 经典 90 坦克大战 Python 版实现(支持单双人模式)
  15. 使用个人股票量化接口做股票投资靠谱吗?
  16. 北京公务员计算机试题,北京公务员补录一级计算机练习题计算机一级计算机基础及+MS+Office应用(选择题)模拟试卷190:钢铁是怎样炼成的读后感...
  17. 基于YOLOv3的口罩佩戴检测
  18. 小游戏渠道大盘点,不可错过的流量入口
  19. [茗洋]程序员常用不常见很难得的地址大全,博主很辛苦
  20. 无锡职业技术学院计算机好,无锡职业技术学院王牌专业有哪些及专业排名

热门文章

  1. 音频audio/sound声卡驱动分析
  2. 单链表反转(递归和非递归)
  3. ios build fdk-aac
  4. iOS 使用FFmpeg
  5. tensorflow之relu
  6. Educoder 机器学习 SMO进行优化 第1关:SMO高效优化算法
  7. Java特点是支持引用类型_Java四种引用类型:强、软、弱、虚
  8. kalilinux装到u盘上的弊端_你有一个 U 盘制作多系统安装盘的需求吗,YUMI 帮你秒实现!...
  9. swagger 返回json字符串_[Swagger] Swagger Codegen 高效开发客户端对接服务端代码
  10. python步骤切片_python中的切片操作