[Intel汇编-MASM]数据标号和直接定址表

1. 数据标号:

    1) 往往有这种需求,想在db/dw/dd等数据定义之前加个标号以便于对定义的数据区的访问,但是汇编语法不支持这样做(会直接报错的!),但MASM提供了一种更加便捷的方式来实现这种内存的访问方式,并且比想象中的功能还要强大;

    2) 以上的这种标号在MASM中叫做数据标号,和普通地址标号不同的地方在于定义它不需要使用冒号:,而是直接在db/dw/dd之前写标号:tag db/dw/dd .....

a dw 1, 2, 3
b db 10 dup(?)
*但是数据标号也只能用在db/dw/dd之前以定义数据区,不能用在其它地方;

    3) 数据标号的双重功能:

        i. 支持索引式的随机访问:比如tag[index]就表示以tag开头的低index字节处的内存单元(索引以0开始,索引为0就代表tag处的内存单元),并且[index]的形式和之前讲过的内存访问方式相同,比如tag[bx]、tag[bx + si + 5]等,注意!立即数不要在作为前缀了,但是也支持tag[bx][si][5]的写法;

        ii. 包含有类型信息:可以根据dX判断数据区中元素的类型(是字节、字还是双字),这在访问内存空间的时候可以体现

a db 10 dup(?)

mov		a[2], 5 ; 正确!a本身包含了元素是字节的类型信息,因此可以正常执行
add		al, a[1] ; 正确!al也是字节类型的,对号入座
and		ax, a[5] ; 错误!ax是字类型的,和a[5]的类型冲突!
        iii. 数据标号本身也可以表示内存单元,tag就表示tag[0]:mov a, 5; add al, a等都是正确的!

    4) 要想正确使用数据标号进行内存访问的大前提——使用assume和mov定位数据标号所在的段:

        i. 数据标号的地址:在使用数据标号进行内存访问时汇编器实际上在背后将该标号翻译成其所在段中的偏移地址,比如访问a[2]的时候就是翻译成[2 + offset a];

        ii. 针对i.问题就来了,那么其段地址是什么呢?理所当然了,肯定就是a所在段的段基咯!比如在以下情况中:

data segment
	a db 10 dup(?)
data ends
a的段基肯定就是data了,因此访问a[2]的时候必然是完整地翻译成data:[2 + offset a]了!

!注意:但是汇编编译器并不会做出这样的自动推导,MASM比较死板,规定数据标号的段地址必须由一个段寄存器给出,因此a[2]会被翻译成"某段寄存器:[2 + offset a]",而这个段寄存器应该选哪一个呢?MASM规定,该段寄存器默认就是使用assume中和数据标号所在段关联的那个寄存器了!是不是直到现在才明白assume伪指令的真正用途了吧!

!但是之前讲过,即使使用assume进行关联,也不会真正使上面的ds = data(除了cs之外),也就是说想正确地使用数据标号来访问内存还必须执行将段地址data传送进ds的mov指令了?对的,必须非常麻烦地执行之一步,必须手工执行完这一步之后才能无忧地使用数据标号访问内存!

?那为什么不直接在assume阶段就直接将关联的段地址赋给相应的段寄存器(比如在assume阶段直接ds = data)呢?那是因为CPU的寄存器资源非常有限,除了cs之外的其它段寄存器供不应求,除了给数据标号访问内存提供段基之外还可能需要应付其它大量的需求,因此编译器就给了这些段寄存器极大的自由而不在assume阶段直接将段地址赋值给它们,而仅仅是用于关联,这可以让程序员自由安排这些段寄存器如何使用;

        *小结:在使用数据标号访问内存之前必须要手工地将标号所在段的段基赋值给assume关联的寄存器中!当然,使用代码段中的数据标号可以不必这样做,因为assume会对cs直接赋值的!

    5) !!!数据标号的索引对类型不敏感!:虽然数据标号可以识别内存单元的类型,但是索引在自增后自减的时候并不伴随元素类型,即不管是db、dw还是dd,索引值+1或-1,则访问的地址也是+1字节或-1字节,并不会因为使用dw定义a[1]和a[2]的地址相差2个字节,永远都是1个字节的!


2. 直接定址表:

    1) 可以将数据标号、普通标号作为dw/dd中定义的内容,如果是dw这些标号就会被翻译成偏移地址(16位),如果是dd就会被翻译成标号的段地址和偏移地址(高16位是段地址、低16位是偏移地址),比如:dw a, b就会被翻译成(a、b可以是数据标号也可以是普通标号,因此统称为标号)dw offset a, offset b,而dd a会被翻译成dd offset a, seg a;

    2) 直接定址表:就是用数据标号定义一个数据区,该数据区内存放的都是地址,一般这些地址要么是其它数据区的地址(比如某个字符串的地址)要么就是一些子程序的地址,这样就定义了一张地址表,可以通过数据标号的随机访问特性快速定位想访问的内容(就表中存放的地址),在C语言中就相当于一个指针数组;

    3) 利用数据标号打表的手段比比皆是,这里先演示一个普通的表(不是直接定址表):

以十六进制的形式在屏幕上输出给定的字节型数据

assume cs:code, ds:data

data segment
	char_table db '0123456789ABCDEF'
	input db 1AH
	output db 0, 0, '$'
data ends

code segment

start:
	mov		ax, seg data
	mov		ds, ax

	mov		al, input
	mov		ah, al
	mov		cl, 4
	shr		ah, cl
	and		al, 1111B

	mov		bx, 0

	mov		bl, ah
	mov		dl, char_table[bx]
	mov		output[0], dl

	mov		bl, al
	mov		dl, char_table[bx]
	mov		output[1], dl

	mov		dx, offset output
	mov		ah, 9
	int		21H

	mov		ax, 4C00H
	int		21H

code ends

end start
    4) 利用直接定址表计算sin(x),其中x只取0, 30, 60, 90, 120, 150, 180这几个值,输入的x存放在ax中,结果直接输出到屏幕上:

assume cs:code

code segment
start:
	mov		ax, 30
	call	sin
	
	mov		ax, 4C00H
	int		21H

sin:
	jmp		short _sin_start

	table	dw ag0, ag30, ag60, ag90, ag120, ag150, ag180
	ag0		db '0', '$'
	ag30	db '0.5', '$'
	ag60	db '0.866', '$'
	ag90	db '1', '$'
	ag120	db '0.866', '$'
	ag150	db '0.5', '$'
	ag180	db '0', '$'
_sin_start:
	mov		bl, 30
	div		bl
	mov		ah, 0
	mov		bx, ax
	add		bx, bx
	
	mov		ax, seg table
	mov		ds, ax
	mov		dx, table[bx] 
	mov		ah, 9
	int		21H

	ret

code ends

end start


你可能感兴趣的:(masm,intel汇编,直接定址表,数据标号)