新学汇编。刚才debug一个汇编程序,很久之后发现是一个内存地址计算错误。然后意识到那是一个数据段的起始地址,在计算的时候忽略了段起始地址必须是16的倍数(8086CPU, 以两个16位寄存器合成一个20位地址的情况下)。后面会详细解释。 先来看看我的代码:
data segment
;年份
db '1975', '1976', '1977', '1978', '1979', '1980', '1981', '1982', '1983', '1984', '1985'
db '1986', '1987', '1988', '1989', '1990', '1991', '1992', '1993','1994', '1995'
;对应的公司收入
dd 10h,16h,17eh,54ch,956h,1f40h,3e80h,5fa6h,0c391h,17cc7h,22481h,30388h,5477ch,903ebh,0c42cah,120d18h,1c1f38h,2a1958h,2a01e8h,565978h,5a9768h
;对应的公司雇员数
dw 3h,7h,9h,0dh,1ch,26h,82h,0dch,1dch,30ah,3e9h,5a2h,8d2h,0ae9h,0fc5h,1603h,2022h,2d16h,3867h,3b99h,4588h
data ends
table segment
db 21 dup ('year summ ne ?? ')
table ends
这样,后面要使用table段的起始内存地址。 我一开始是这样计算的, 已经把寄存器ds设置为data段的起始地址:
项 | 起始地址 | 地址长度 |
收入 | 0 | 4×21=54h |
雇员数 | 54h | 4×21=54h |
表格 | 54h+54h=d2h | 略 |
这样看来,table的起始地址应该是d2h。可是debug的时候却出了问题。看到正确的table段的起始地址是e0。
为什么?注意到table是单独一个段之后,意识到了问题。既然table是独立的一个段,那它的首地址就必须可以表示为:段地址:0000的形式。直观上,8086CPU是这样计算地址的:段地址×16+偏移地址
表中的XXXX是16进制数
段地址 | x | x | x | x | 0 | |
偏移地址 | + | x | x | x | x | |
合成地址 | x | x` | x` | x` | x |
结合前面说的,作为一个独立的段。table的起始地址必须能够表示成一个段地址:0000的形式。所以向后取离d2最近的e0作为地址了。
或者这样解释:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
把内存看成上面的结构,数字表示绝对物理地址。0是第一个物理单元(第一个byte)……则0可以是一个段的起始,16可以是一个段的起始。32可以是一个段的起始。 如果雇员数据占的最后一个地址在9,那么就选16作为table的起始地址。而把9-15空出来。因为0可以表示为:0000:0000, 16可以表示为0001:0000, 32可以表示为0002:0000.