现在,我们讨论用查表的方法编写相关程序的技巧。编写子程序,以十六进制的形式在屏幕中间显示给定的字节型数据。
分析:
一个字节需要用两个十六进制数码来表示,所以,子程序需要在屏幕上显示两个“ASCII字符。我们当然要用“0"、 “1”、“2”、“3”、"4”、“5”、“6”、“7”、“8”、“9”、"A”、"B’,"c’、“D"、"e’,"F这16个字符来显示十六进制数码。
我们可以将一个字节的高4位和低4位分开,分别用它们的值得到对应的数码字符。比如2Bh,可以得到高4位的值为2,低4位的值为11,那么如何用这两个数值得到对应的数码字符“2”和“B”呢?
最简单的办法就是一个一个地比较,如下:
如果数值为0,则显示“0"; 如果数值为1,则显示“1"; . 如果数值为11,则显示“B": .
我们可以看出,这样做,程序中要使用多条比较、转移指令。程序将比较长,混乱。
显然,我们希望能够在数值0-15和字符“0”-“F”之间找到一种映射关系。这样用O}ls间的任何数值,都可以通过这种映射关系直接得到“0”-“F”中对应的字符。
数值0~9和字符“0”-“9”之间的映射关系是很明显的,即:
教佰+30h=对应字件的ASCII值
0+30h= "0”的ASCII值
1+30h=“1”的ASCII值
2+30h= "2”的ASCII值
但是,10-15和“A”-"F”之间的映射关系是:
数值+37h=对应字符的ASCII值
10+37h= "A”的ASCII值
11+37h= "B”的ASCII值
12+37h= "C”的ASCII值
可见,我们可以利用数值和字符之间的这种原本存在的映射关系,通过高4位和低4位值得到对应的字符码。但是由于映射关系的不同,我们在程序中必须进行一些比较,对于大于9的数值,我们要用不同的计算方法。
这样做,虽然使程序得到了简化。但是,如果我们希望用更简捷的算法,就要考虑用同一种映射关系从数值得到字符码。所以,我们就不能利用0-9和“0”-“9”之间与
10-15和“A”-“F”之间原有的映射关系。
因为数值0-15和字符“0”-“F”之间没有一致的映射关系存在,所以,我们应该在它们之间建立新的映射关系。
具体的做法是,建立一张表,表中依次存储字符“0”-“F",我们可以通过数值0-15直接查找到对应的字符。
子程序如下。
;用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 bl,ah mov bh,0 mov al,table[bx] ;用低4位的值作为相对于table的偏移,取得对应的字符 mov es:[160*12+40*2+2],al pop es pop bx ret
可以看出,在子程序中,我们在数值0-15和字符“0”-“F”之间建立的映射关系
为:以数值N为table表中的偏移,可以找到对应的字符。
利用表,在两个数据集合之间建立一种映射关系,使我们可以用查表的方法根据给出
的数据得到其在另一集合中的对应数据。这样做的目的一般来说有以下3个。
(t)为了算法的清晰和简洁;
(2)为了加快运算速度;
(3)为了使程序易于扩充。
在上面的子程序中,我们更多的是为了算法的清晰和简洁,而采用了查表的方法。下
面我们来看一下,为了加快运算速度而采用查表的方法的情况。
编写一个子程序,计算sin(x)} x E {00,300,600,900,1200,i500,180}并在屏幕中间显示计算结果。比如sin(30)的结果显示为“0.5 "。
我们可以利用麦克劳林公式来计算sin{x), x为角度,麦克劳林公式中需要代入弧度,则:
可以看出,计算sin(x)需要进行多次乘法和除法。乘除是非常费时的运算,它们的执行时间大约是加法、比较等指令的5倍。如何才能够不做乘除而计算sin(x)呢?我们看一下需要计算的sin(x)的结果:
sin(0)=0 sin(30)=0.5 sin(60)=0.866 sin(90)=1 sin(120)=0.866 sin(150)=0.5 sin(180)=0
我们可以看出,其实用不着计算,计算的sin(x)的结果都存储到一张表中;可以占用一些内存空间来换取运算的速度。将所要然后用角度值来查表,找到对应的sin(x)的值。
用ax向子程序传递角度,程序如下:
assume cs:code code segment start:mov al, 30 mov ah, 0 call showSin mov ax, 4c00h int 21h showSin:jmp short show table dw arg0, arg30, arg60, arg90, arg120, arg150, arg180 ;字符串偏移地址 arg0 db '0', 0 arg30 db '0.5', 0 arg60 db '0.866', 0 arg90 db '1', 0 arg120 db '0.866', 0 arg150 db '0.5', 0 arg180 db '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 short shows showret:pop si pop es pop bx ret code ends end start
在上面的子程序中,我们在角度值x和表示sin(x)的字符串集合table之间建立的映射关系为:以角度值/30
为table表中的偏移,可以找到对应的字符串的首地址。
编程的时候要注意程序的容错性,即对于错误的输入要有处理能力。在上面的子程序中,我们还应该再加上对提供的角度值是否超范围的检测。如果提供的角度值不在合法的集合中,程序将定位不到正确的字符串,出现错误。对于角度值的检测,请读者自行完成。
上面的两个子程序中,我们将通过给出的数据进行计算或比较而得到结果的问题,转化为用给出的数据作为查表的依据,通过查表得到结果的问题。具体的查表方法,是用查表的依据数据,直接计算出所要查找的元素在表中的位置。像这种可以通过依据数据,直接计算出所要找的元素的位置的表,我们称其为直接定址表。