表述了单元长度的标号
assume cs:code
code segment
a dw 1,2,3,4,5,6,7,8 ;a代表了code段的首地址code:00,由于dw,也代表了a开始的这一小段内存都是按照字单元存储的。
;从code:[0]~code:[15],也就是,a[0]~a[15];a[si]代表了从code:00开始的偏移量为si的内存单元。
;此内存单元是字单元,故si的偏移量是2个字节。
b dd 0 ;b代表了code段中紧邻a段地址的code:[16](首地址),一个双字的单元,它存储位置是code:[16]~code:[19],也就是b[0]~b[3];
;剩下的空间都是真正的CPU执行的代码了。
start:mov si,0
mov cx,8
s:mov ax,a[si] ;mov ax, a[si]代码含义:将偏移量为si的段地址(或内存段首地址)为a的内存单元按字单元送入到ax中。
add word ptr b[0],ax ;add word ptr b[0], ax,代码含义:将ax值与b标号的低16位做加法,结果存储在b标号的低16位中;
;此时b代表的是双字单元,如果add b, ax;编译器肯定报错,因为它们的类型不匹配。
;此处必须指定b[0]是按照字单元作为存储结构的。
adc word ptr b[2],0 ;adc word ptr b[2], 0;代码含义:带进位加法,将0与上一指令中的CF值相加,结果存储在b的高16位单元中。
;目的:防止低16位值产生进位。同理:此处必须指定b[2]是按照字单元作为存储结构的。这个实例就是高位不溢出加法的套路。
;对于无符号数加法它的最大允许范围是(0000 0000H~FFFF FFFFH):0~4294967295
add si,2 ;add si, 2;代码含义:si的偏移量,由于a的偏移量是按照字单元偏移,故si为2个字节的偏移
loop s
mov ax,4c00h
int 21h
code ends
end start
在其他段中使用数据标号
assume cs:code, ds:data
data segment ;即使在assume这个伪指令中将ds和data联系在一起(C语言的全局声明意思)。
;如果没有将data的段地址赋值给ds,那么[si]寻址的内存单元是错误的,并不是ds:[si]或a[si]。
a db 1,2,3,4,5,6,7,8
b dw 0
data ends
code segment
start: mov ax,data ;如果在assume中没有ds:data,那么在code段中使用a和b是不可能的。因为a和b在code段是不可以见的。
mov ds,ax
mov si,0
mov cx, 8
s: mov al, a[si] ;a[si]代表的是一个字节(它已经代表地址和单元长度了),赋值给ax是错误的,应该赋值给al,别犯这样的低级错误。
;(ah)=0,是防止高位不为零,al+ah组合成数据就是(ax)。
mov ah, 0
add b, ax
inc si
loop s
mov ax,4c00h
int 21h
code ends
end start
直接定址表
1. 自定义(映射)表
自定义(映射)表是一种巧妙的程序设计方法。在程序设计时,我们可以利用各种自定义(映射)表,在两个数据集合之间建立一种映射关系,使得我们可以利用查表的方法,根据给出的数据(输入)得到其在另一个集合中的对应数据(输出)。这种设计一般来说有以下3中目的:
- 为了算法的清晰和简洁;
- 为了加快运算速度;
- 为了使程序易于扩充。
2. 数据表
- 一个字节可以用两个16进制字符表示,分别表示高4位和低4位的值;
- 一个16进制的值的范围为“0 - 15”,符号范围为“0 - F”,建立如下一张字符表:
table db '0123456789ABCDEF'
在这张字符表(字符串)中,它的下标(index)和对应的字符(value),两者之间有一个简单的线性映射关系。我们通过下标运算(查表),就可以快速地从给定的数获取对应的字符。
3. 地址表
table dw ag0,ag30,ag60,ag90,ag120,ag150,ag180 ;字符串偏移地址表
ag0 db '0',0 ;利用mov bx,table[bx]做为table的偏移,取得对应的字符串的偏移地址,放入bx中
ag30 db '0.5',0
ag60 db '0.866',0
ag90 db '1',0
ag120 db '0.866',0
ag150 db '0.5',0
ag180 db '0',0