内存寻址方式

处理字符问题

在处理汇编语言程序中,用'…….'的方式指明数据是以字符的形式给出的 编译器将他们转化为他们对应的ascii码 通过ascii码表我们可以发现同一个大写和小写字符相差20h

assume cs:code,ds:data
data segment
db 'unIX'
db 'foRX'
data ends
code segment
start:mov al,'a'
mov bl,'b'
mov ax,4c00h
int 21h
code ends
end start
C++

经过我们编译之后 我们可以看到数据区 一共256(100H)字节程序段前缀(PSP),作为数据区 所以需要在ds的基础上+100h 如果我们观察ascii表中'a'和'A' 换成为二进制表示
'a' = 01100010B 'A' = 01000010 二进制下同一字符的第六位不一样 小写是1 大写是0 所以我们就可以运用与运算实现大小写转换

assume cs:code,ds:data
data segment
db 'Love'
db 'pAce'
data ens
cs segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s:mov al,ds:[bx]
and al,11011111b
mov ds:[bx],al
inc bx
loop s
mov bx,4
mov cx,4
s:mov al,ds:[bx]
or al,00100000b
mov ds:[bx],al
inc bx
loop s0
mov ax,4c00h
int 21h
code ens
end start
C++

第一次循环通过位运算符与and 将第六位的位值补0 实现了小写转大写
通过该指令可将操作对象的相应位设为0,其他位保持不变
第二次循环通过位运算符或or 将第六位的位值置1 实现了大写转小写
通过该指令可将操作对象的相应位设为1,其他位保持不变
最后通过指令查看 数据段中的内容可以看到字符已经被转换

[BX+200]

[bx+idata]表示一个内存单元 他的偏移地址为(bx)+idata
mov ax,[bx+200]的含义

  • 将一个内存单元的内容送入ax
  • 这个内存单元的长度为2字节
  • 段地址在ds中 偏移地址为200+bx
  • (ax) = ((ds)*16+200+(bx))
    我们通过修改之前大小写转换的程序可以看一下这个寻址方式的应用
assume cs:code,ds:data
data segment
db 'Love'
db 'pAce'
data ens
cs segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s:mov al,ds:[bx]
and al,11011111b
mov ds:[bx],al
mov al,ds:[5+bx]
or al,00100000b
mov ds:[5+bx],al
inc bx
loop s
mov ax,4c00h
int 21h
code ens
end start
C++

这里通过寻址方式同时处理了 数据段的两个数据的大小写转换 转换成C代码应该是

include <stdio>
int main()
{
			int i = 0;
		char str1[4]="Love";
		char str2[4]="pAce";
	do
	{
str1[i] = str1[i]&0xDF;
str2[i] = str2[i]&0x20;
i++;
		/* code */
	} while (i<4);
}
C++

寻址方式辨析

内存寻址方式
上图是一些寻址方式 SI 和DI是变址寄存器

assume cs:code,ds:data
data segment
db '1.file'
db '2.edit'
db '3.search'
db '4.view'
db '5.options'
db '6.help'
data ends
code segment
start:
...
mov 4c00h
int 21h
code ends
end start
C++
  • 问题1将data段的字符的第一个字母改成大写字母
    我们该用哪一个寻址更方便 答案是寄存器相对寻址 因为每一个首字母都是第三个字符 所以可以用[bx+3]方式改写
    代码如下
mov ax,data
mov ds,ax
mov bx,0
mov cx,6
s:
mov al,[bx+3]
and al,11011111b
mov [bx+3],al
add bx,16
loop s
C++

问题2 将data段的每个单词改为大写字母

assume cs:code,ds:data
data segment
db 'ibm'
db 'dec'
db 'dos'
db 'vax'
data ends
code segment
start:
...
mov 4c00h
int 21h
code ends
end start
C++

代码如下
方法1

mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s:mov dx,cx
mov si,0
mov cx,3
s0:
mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s0
add bx,16
mov cx,dx
loop s
C++

因为一个循环需要控制行 另一个循环需要控制改写每个字母的大小写 通过保存最外层循环的cx 保证每一行依次修改
方法2
寄存器可能被占用 因此我们可以用数据段的一个内存地址保存cx的值 代替dx寄存器

mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s:mov ds:[30h],cx
mov si,0
mov cx,3
s0:
mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s0
add bx,16
mov cx,ds:[30h]
loop s
C++

方法3:
也可以用栈保存cx的数据

assume ss:stack
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
mov ax,stack
mov ss,stack
mov sp,30
mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s:push cx
mov si,0
mov cx,3
s0:
mov al,[bx+si]
and al,11011111b
mov [bx+si],al
inc si
loop s0
add bx,16
pop cx
loop s
C++

用于内存寻址的寄存器

只有bx bp si di可以用在对内存单元([])的寻址
错误的指令:
mov ax,[cx]
mov ax,[ax]
mov ax,[dx]
mov ax,[ds]
一般来说bx和bp作为基址寄存器 si和di作为变址寄存器 bp默认为ss段 bx默认为ds段
内存寻址方式

内存地址定位(where, how long)

  1. 立即数 直接包含在机器指令中的数据 称为立即数(idata) 数据包含在指令中
    mov ax,1
    add bx,2000h
    or bx,00010000b
    mov al,'a'
  2. 寄存器
    mov ax,bx
  3. 段地址和偏移地址
    前面说到的内存寻址的几种方式都可以
    指令处理的数据长度
  • 字操作
    直接用16位的寄存器就可以表示
  • 字节操作
    直接用8位的寄存器就可以表示
    如果一个指令中没有指定的寄存器 那么我们可以用如下的方式来指导数据的长度
  • word prt或者byte ptr
    如mov word ptr ds:[0],1
    inc word prt [bx]
    inc word ptr ds:[0]
    add word ptr [bx],2
    以上数据传输的长度是一个字
    mov byte ptr ds:[0],1
    inc byte ptr [bx]
    inc byte ptr ds:[0]
    add byte ptr [bx],2
    以上数据传输的长度是一个字节

内存寻址综合应用

如何通过编程修改指定内存中的数据 我们之前已经学习过很多种内存寻址的方式,包括直接寻址 寄存器间接寻址 相对寻址等寻址方式。
内存寻址方式
可以看到0c 0e和10数据发生了改变 汇编语言该如何实现这个效果 可以看下代码实现

mov ax,seg
moc dx,ax
mov bx,60h
mov word ptr [bx+0ch],11
mov word ptr [bx+0eh],13
mov si,0
mov byte ptr [bx+10+si],'H'
inc si
mov byte ptr [bx+10+si],'O'
inc si
mov byte ptr [bx+10+si],'U'
C++

然后我们可以看一下C语言是如何描述这段代码的 因为我们学习汇编的目的是为了看懂别人计算语言转成的汇编代码。

struct Player{
char name[3];
char birthday[9];
int num;
int ppg;
char team[3];
};
struct Player Yao{"Yao","19800912",15,32,"SSH"};
int main()
{
int i;
yao.num = 11;
yao.ppg = 13;
i = 0;
yao.team[i]='H';
i++;
yao.team[i]='O';
i++;
yao.team[i]='U';
return 0;
}
C++

我们可以对比一下二组代码的相似之处
首先yao表示了这个结构体的开头地址 team等信息指明了结构体不同的地址 而team[i]则可以定位这些地址中的字符。汇编语言中,bx就表示了基地址 又用idata表示了结构体中的不同数据项 同时si变址寄存器又为定位数组元素提供了方便。通过[bx+idata+si]的寻址方式 我们可以对结构化的数据进行处理和对待

div除法

div是除法指令 使用div除法的时候,被除数 默认放在AX或DX和AX中
格式:div 寄存器 或者指令单元
可以用来说明一下
被除数 ax 除数8位寄存器 商 AL 余数 AH
被除数ax或者dx 除数16位寄存器 商 AX 余数DX
div bl 被除数(ax) 除数(bl) 商(al) 余数(ah)
div byte ptr ds:[0] 被除数(ax) 除数((ds)*16+0) 商(al) 余数(ah)
div byte prt [bx+si+08] 被除数(ax) 除数((ds)*16+(bx)+(si)+08))
div bx 被除数((dx)*10000+(ax)) 除数(bx) 商(ax) 余数(dx)
div word ptr es:[0] 被除数(dx)*10000+(ax) 除数((es)*16+0) 商(ax) 余数(dx)
div word ptr [bx+si+8]被除数(dx)*10000+(ax) 除数(ds)*16+(bx)+(si)+8
编程举例01
利用除法指令计算100001/100
被除数是100001D十六进制为 1 86A1H 20位长度 所以被除数需要存放在ax和dx中 除数可以放在bx中
内存寻址方式
可以看到商存放在ax中 余数放在dx中
编程举例02
利用除法计算1001/100
进行8位除法即可 1001D=3E9H 在ax寄存器中存放被除数 bx存放除数64h 可以在Dosbox看一下代码运行
内存寻址方式
编程举例03
db 表示定义字节数据(8位) dw定义字单元数据(16位) dd定义双字单元数据(32位)
使用div计算data段中的第一个数据除以第二个数据后的结果 商存放在第三个数据的存储单元中。
首先第一个数据是32位 分别存放在ax和dx中 mov ax,ds:[0] mov dx,ds:[2] 这俩指令将ds段前32位数据分别赋值给ax和dx 根据寄存器可以看出第二行数据是位于ds:[4] 第三行数据为ds:[6] 代码如下

data segment
dd 100001
dw 100
dw 0
data ends
mov ax,data
mov ds,ax
mov ax,ds:[0]
mov dx,ds:[2]
div word ptr ds:[4]
mov ds:[6],ax
C++

商存放在ax中 没有寄存器的情况下 要表示内存数据可以用倒数第二个指令

dup功能和用法

dup和db dw dd等数据定义伪指令配合使用 用来进行数据的重复
db 3 dup(0) 定义三个字节且值都为0 = db 0,0,0,
db 3 dup(1,2,3) 定义了9个字节 由123重复三次构成 = db 1,2,3,1,2,3,1,2,3
db 3 dup('abc','ABC') 定义了六个字符串 长度为18个字节
db&&dw&&dd 定义长度 dup(data)
举例

assume cs:code,ds:data
data segment
db 3 dup(0)
db 3 dup(0,1,2)
db 80 dup(0)
db 3 dup("abc","ABC")
data ends
code segment
mov ax,data
mov ds,ax
mov ax,4c00h
int 21h
code ens
end
C++
汇编语言编程学习

汇编语言_汇编语言程序

2024-11-15 11:16:19

汇编语言编程学习

转移指令

2024-11-15 11:42:54

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索

Powered by atecplugins.com