ShuangChenYue ShuangChenYue
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
首页
  • Cpp之旅
  • Cpp专栏
  • Effective_CPP
  • muduo网络库
  • Unix环境高级编程
  • Cpp提高编程
  • 计算机网络
  • 操作系统
  • 数据结构
  • Linux
  • 算法
  • 基础篇
  • MySql
  • Redis
  • 电子嵌入式通信协议
  • 深入浅出SSD
  • 文件系统
  • 汇编语言
  • STM32
  • 随笔(持续更新)
  • Git知识总结
  • Git 创建删除远程分支
  • nvm使用小结
  • 虚拟机固定 IP 地址
  • Shell 脚本学习笔记
  • VScode 插件 CodeGeeX 使用教程
  • KylinV10 将项目上传至 Github教程
  • KylinV10 安装 MySQL 教程(可防踩雷)
  • kylinV10-SP1 安装 QT
  • 高并发内存池
  • USBGUARD 项目编译环境配置
  • Power_Destory 项目
  • U 盘清除工具编译教程
  • 个人博客代码推送教程
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • MFC编程随记
  • MFC实现ini配置文件的读取
  • MFC实现点击列表头排序
  • 贴图法美化Button按钮
  • 如何高效阅读嵌入式项目代码
  • NAND Flash
  • ARM 处理器
  • 嵌入式基础知识-存储器
  • 闪存存储和制造技术概述
  • 芯片IO驱动力
  • 主流先进封装技术介绍
  • 虎牙C++技术面经
  • 金山一面复习
  • 完美世界秋招 C++ 游戏开发面经(Cpp部分)
  • 博客搭建
  • 网站收藏箱
  • 电子嵌入式通信协议

  • 深入浅出SSD

  • 文件系统

  • 汇编语言

    • 第1章 基础知识
    • 第2章 寄存器
    • 第3章 寄存器(内存访问)
    • 第4章 第一个程序
    • 第5章 [BX]和loop指令
    • 第6章 包含多个段的程序
    • 第7章 更灵活的定位内存地址的方法
      • 7.1 and 和 or 指令
        • 7.1.1 and 指令
        • 7.1.2 or 指令
      • 7.2 以字符的形式给出数据
      • 7.3 大小写转换的问题
      • 7.4 [bx+idata]
      • 7.5 SI 和 DI
      • 7.6 [bx+si] 和 [bx+di]
      • 7.7 [bx+si+idata] 和 [bx+di+idata]
      • 7.8 不同的寻址方式的灵活应用
    • 第8章 数据处理的两个基本问题
  • STM32

  • 嵌入式软件开发
  • 汇编语言
霜晨月
2024-07-24
目录

第7章 更灵活的定位内存地址的方法

# 第7章 更灵活的定位内存地址的方法

# 7.1 and 和 or 指令

# 7.1.1 and 指令

and 指令:逻辑与指令,只有全为1,才为1

mov al,01100011B
and  al,   00111011B
------------------------
结果 al = 00100011B
1
2
3
4

通过该指令可将操作对象的相应位设为0,其他位不变。

例如:

  • 将 al 的第 6 位设为 0 的指令是:and al,10111111B
  • 将 al 的第 7 位设为 0 的指令是:and al,01111111B
  • 将 al 的第 0 位设为 0 的指令是:and al,11111110B

# 7.1.2 or 指令

or 指令:逻辑或指令,只用全为 0,才为 0

mov al,01100011B
or     al,00111011B
---------------------
结果al=01111011B
1
2
3
4

通过该指令可将操作对象的相应位设为1,其他位不变。

例如:

  • 将 al 的第 6 位设为 1 的指令是:or al,10111111B
  • 将 al 的第 7 位设为 1 的指令是:or al,01111111B
  • 将 al 的第 0 位设为 1 的指令是:or al,11111110B

# 7.2 以字符的形式给出数据

在汇编程序中,可用‘......’的方式指明数据是以字符的形式给出的,编辑器会将它们转化为相应的 ASCII 码。

assume cs:code,ds:data
data segment
	db 'unIX'
	db 'foRK'
data ends
code segment
	start:mov al,'a'
		mov bl,'b'
		mov ax,4c00h
		int 21h
code ends
end start
1
2
3
4
5
6
7
8
9
10
11
12
  • db 'unIX' 相当于 db 75H,6EH,49H,58H,u 、n、 I、 X 的 ASCII 码分别为 75H、6EH、49H、58H;
  • db 'foRK' 相当于 db 66H,6FH,52H,4BH,f 、o、 R、 K 的 ASCII 码分别为 66H、6FH、52H、4BH;
  • mov al,'a' 相当于 mov al,61H,a 的 ASCII 码为 61H;
  • mov bl,'b' 相当于 mov al,62H,b 的 ASCII 码分别为 62H;

# 7.3 大小写转换的问题

考虑如下问题:在 codesg 中填写代码,将 datasg 中的第一个字符串转化为大写,第二个字符串转化为小写。

assume cs:codesg,ds:datasg
datasg sement
	db 'BaSiC'
	db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:
codesg ends
end start
1
2
3
4
5
6
7
8
9

思路:要改变一个字母的大小,实际就是改变它所对应的 ASCII 码。找到 ASCII 码规律,发现小写字母比大写字母的 ASCII 码值大 20H。所以,将 "a" 的 ASCII 码值减去 20H,就可以得到 "A",反之亦然。要注意的是,对于 'BaSiC' 字符串,只需对小写进行减 20H 处理,其他的大写字母不进行处理。对于 'iNfOrMaTiOn' 字符串则反之。

image

但是,程序还有一个前提,必须能够判断一个字母是大写还是小写。但是根据目前所学,还没有接触到可以实现这个功能的指令,所以继续观察上图,找 ASCII 码值的规律,可发现,大写字母和小写字母的其他位都一样,只是大写字母的 ASCII 码值第 5 位为 0,小写字母为 1。所以,我们只需将字母的 ASCII 码值第 5 位 置为0,则变成大写字母,置为1,则变成小写字母。

这个操作则刚好可以使用我们前面所学的 or and 指令。

assume cs:codesg,ds:datasg
datasg segment
	db 'BaSiC'
	db 'iNfOrMaTiOn'
datasg ends
codesg segment
start:mov ax,datasg
	mov ds,ax			;设置 ds 指向 datasg 段
	mov bx,0			;设置(bx)=0,ds:bx指向 'BaSiC' 的第一个字母
	mov cx,5			;设置循环次数5,因为 'BaSiC' 有5个字母
s:mov al,[bx]			    ;将 ASCII 码从 ds:bx 所指向的单元中取出
	and al,11011111B	 ;将 al 中的 ASCII 码的第5位置为0,变为大写字母
	mov [bx],al			  ;将转变后的 ASCII 码协会原单元
	inc bx				;(bx)加1,ds:bx 指向下一个字母
	loop s
	mov bx,5			;设置(bx)=5,ds:bx 指向 iNfOrMaTiOn' 的第一个字母
	mov cx,11			;置循环次数11,因为 'iNfOrMaTiOn' 有11个字母
s0:mov al,[bx]
	or al,00100000B		   ;将 al 中的 ASCII 码的第5位置为1,变为小写字母
	mov [bx],al
	inc bx
	loop s0
	mov ax,4c00h
	int 21h
codesg ends
end start
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

# 7.4 [bx+idata]

[bx+idata] 表示一个内存单元,它的偏移地址为(bx) + idata 。

mov ax,[bx+200] 的含义:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为 bx 中的数值加上200,段地址在 ds 中。

数字描述为:(ax) = ((bx)*16 + (bx) + 200)

也可写成如下格式(常用):

mov ax,[200+bx]
mov ax,200[bx]
mov ax,[bx].200
1
2
3

用 [bx+idata] 的方式重新实现大小写转换的问题,因为 datasg 段中的两个字符串一个起始地址为0,另一个起始地址为5。将这两个字符串视为两个数组,一个从0地址开始,一个从5开始存放。那么就可以用 [0+bx] 和 [5+bx] 或者 0[bx] 和 5[bx] 来表示。

改进程序如下:

	mov ax,datasg
	mov ds,ax
	mov bx,0
	mov cx,5
s:	  mov al,[bx]		;定位第一个字符串中的字符
	and al,11011111b
	mov [bx],al
	mov al,[5+bx]	    ;定位第二个字符串中的字符
	or al,00100000b
	mov [5+bx],al
	inc bx
	loop s
1
2
3
4
5
6
7
8
9
10
11
12

或者如下:

	mov ax,datasg
	mov ds,ax
	mov bx,0
	mov cx,5
s:	  mov al,0[bx]		;定位第一个字符串中的字符
	and al,11011111b
	mov 0[bx],al
	mov al,5[bx]	    ;定位第二个字符串中的字符
	or al,00100000b
	mov [5+bx],al
	inc bx
	loop s
1
2
3
4
5
6
7
8
9
10
11
12

如果使用高级语言,比如C语言,大致如下:

char a[5] = "BaSiC"
char b[5] = "MinIX";
main()
{
    int i;
    i = 0;
    do {
        a[i] = a[i] & 0xDF;
        b[i] = b[i] | 0x20;
        i++;
    } while(i < 5);
}
1
2
3
4
5
6
7
8
9
10
11
12

# 7.5 SI 和 DI

si 和 di 是与 bx 功能相近的寄存器,si 和di 不能够分成两个8为寄存器来使用,下面是3组指令实现了相同的功能

image

问题 7.2:用 si 和 di 实现将字符串 'welcome to masm!' 复制到它后面的数据区中

assume cs:codesg,ds:datasg
datasg segment
	db 'welcome to masm!'
	db '................'
datasg ends
1
2
3
4
5

用 ds:si 指向要复制的源始字符串,ds:di 指向复制的目的空间

codesg segment
start: mov ax,datasg
	 mov si,0
	 mov di,16
  s:     mov ax,[si]
  	   mov [di],ax
  	   add si,2
  	   add di,2
  	   loop s
  	   mov ax,4c00h
  	   int 21h
codesg ends
end start
1
2
3
4
5
6
7
8
9
10
11
12
13

在程序中,用 16 位寄存器进行内存单元之间的数据传送,一次复制2个字节,一共循环8次

用更少的代码实现:利用 [bx(si或di)+idata] 的方式

codesg segment
start: mov ax,datasg
	 mov ds,ax
	 mov si,0
	 mov cx,8
  s:     mov ax,0[si]
  	   mov 16[si],ax
  	   add si,2
  	   loop s
  	   mov ax,4c00h
  	   int 21h
codesg ends
end start
1
2
3
4
5
6
7
8
9
10
11
12
13

# 7.6 [bx+si] 和 [bx+di]

[bx+si] 表示一个内存单元,偏移地址为 (bx)+(si)即 bx 中的数值加上 si 中的数值。

mov ax,[bx+si] 的含义如下:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为 bx 中的数值加上 si 中的数值,段地址在 ds 中。

数学化描述为:(ax) = ((ds)*16+(bx)+(si))

也可写成如下格式(常用):mov ax,[bx][si]

# 7.7 [bx+si+idata] 和 [bx+di+idata]

[bx+si+idata] 表示一个内存单元,偏移地址为 (bx)+(si)+idata 即 bx 中的数值加上 si 中的数值再加上idata。

mov ax,[bx+si+idata] 的含义如下:将一个内存单元的内容送入 ax,这个内存单元的长度为2个字节(字单元),存放一个字,偏移地址为 bx 中的数值加上 si 中的数值再加上idata,段地址在 ds 中。

数学化描述为:(ax) = ((ds)*16+(bx)+(si)+idata)

也可写成如下格式(常用):

  • mov ax,[bx+200+si]
  • mov ax,[200+bx+si]
  • mov ax,200[bx][si]
  • mov ax,[bx].200[si]
  • mov ax,[bx][si].200

# 7.8 不同的寻址方式的灵活应用

  1. [idata] 用一个常量表示地址,可用于直接定位一个内存单元;
  2. [bx] 用一个变量表示地址,可用于直接定位一个内存单元;
  3. [bx+idata] 用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;
  4. [bx+si] 用两个变量表示地址;
  5. [bx+si+idata]用两个变量和一个常量表示地址。

问题 7.6

image

6个字符串,每个长度均为16个字节,因为是连续存放的,可将这6个字符串看成一个6行16列的二维数组。

需要修改每一个单词的第一个字母,即二维数组的每一行的第4列(相对于行首的偏移地址为3)

image

	mov ax,datasg
	mov ds,ax
	mov bx,0
	mov cx,6
s:	  mov al,[bx+3]
	and al11011111b
	mov [bx+3],al
	add bx,16
	loop s
1
2
3
4
5
6
7
8
9
第6章 包含多个段的程序
第8章 数据处理的两个基本问题

← 第6章 包含多个段的程序 第8章 数据处理的两个基本问题→

Theme by Vdoing | Copyright © 2023-2024 霜晨月
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式