王爽的汇编语言,有这样一道题:
检测点2.2
(2) 有一数据存放在内存20000H单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为 , 最大为 。
提示,反过来思考一下,当段地址给定为多少,CPU无论怎么变化偏移地址都无法寻到20000H单元?
我的思考过程是,关键是看“物理地址=段地址(SA)*10H+偏移地址(EA)”这句话,容易得到段地址=(物理地址-偏移地址)/10H(10H也可以写成16)。当偏移地址为最大时,能求得最小的段地址;否则求得最大的段地址。而偏移地址是16位的,也就是说寻址范围是0~FFFFH。那么,顺着这个想法很容易得到“答案”——当偏移地址为最大(FFFFH)时段地址最小;当偏移地址最小(0)时段地址最大。
但是,经过计算发现,“段地址=(物理地址-偏移地址)/16”对应的式子“SA=(20000H-EA)/16”,其数学上等价的另一条分配律得到的式子“SA=20000H/16-EA/16”,在计算SA最大值的时候所计算的结果却不一样!(前者是20000H-FFFFH得到10001H,之后10001H除以10H,得到的结果是1000;后者是20000H/10H先得到2000H,再FFFFH/10H得到FFFH,最后2000H-FFFH,得到的结果是1001)
为什么完全相等的两条数学运算,其结果竟然不同!
先想一想*10H(*16)的本质:
从一个例子来观察(后缀B表示二进制、后缀D表示十进制、后缀H表示十六进制):
10B( 2D、 2H),左移1位
为 100B( 4D、 4H),左移1位
为 1000B( 8D、 8H),左移1位
为 10000B(16D、10H),左移1位
为100000B(32D、20H)
得到 1) 10B左移1位,其十进制从2D变成4D,其十六进制从2H变成4H; 2) 10B左移4位,其十进制从2D变成32D,其十六进制从2H变成20H
所以有以下的结论: 1) 一个数据的二进制左移1位,相当于其十进制乘上2,也相当于其十六进制乘上2; 2) 一个数据的二进制左移N位,相当于该数据乘以2的N次方
所以,一个数据乘上10H(乘上16)其本质是该数据以二进制形式左移4位
对应地,一个数据除以10H(除以16)其本质就是该数据以二进制形式右移4位
回到这道题上面,为什么完全相同的两个数学式子,结果会不同?
这就是因为题目这句话:段地址*10H必须是16的倍数。段地址SA为2000H,乘上10H(暂时称段地址*10H的结果为基础地址,并没有这个概念),本质上是以二进制形式左移4位(2的4次方,幂就是左移的位数),这在十六进制中表现为其末尾为0,所以其基础地址的末尾必为0,而不是20001H或20002H,而是20000H。
接着,基础地址加上偏移地址就得到了物理地址,则基础地址=物理地址-偏移地址,即SA*16=20000H-EA
如果EA取理论最大值FFFFH(因为偏移地址为4位,即可变化范围为0~164-1,十六进制为0~FFFFH),那么其基础地址为:20000H-FFFFH=10001H,可以发现这个基础地址是错误的。若要保证其基础地址必须为16的倍数(也就是其十六进制形式的末尾为0),那么偏移地址的最大值只能为FFF0H,这个FFF0H末尾的0是为了保证与基础地址20000H末尾的0对应,从而保证段地址*10H的结果是16的倍数
而这两条数学式子:SA=(20000-EA)/16和SA=20000/16-EA/16,虽然从数学角度来说是相等的,但其本身表示的意义就不同了,前者先执行(20000-EA)运算,最大的问题就是无法保证其表示的意义——基础地址的末尾必为0;而先执行20000/16就能保证执行运算时先排除末尾的干扰,无论其末尾是什么,通过除以16(右移4位)得到了保护,最终能使得基础地址的末位为0不变
所以这就是SA=(20000-EA)/16和SA=2000-EA/16运算结果不想等的原因
正确的算法应该是SA=2000-EA/16才能排除算法的逻辑错误
(这是我在学习汇编语言的过程中得到的心得写的第一篇笔记,如果我的表述有错误,欢迎指出,打扰各位了~)