当前位置: 首页 > news >正文

汇编语言:基址变址内存访问

目录
  • 位操作指令
  • 字符形式的数据
  • 带偏移量的内存寻址
  • SI 和 DI 寄存器
  • 二重寻址
  • 三重寻址
  • 参考资料

位操作指令

and 和 or 指令是进行位操作的基础指令。

指令 功能 样例
and 指令 按位逻辑与,用途是将特定位设为 0 例如 and al, 11011111B,通过让第 5 位(从 0 开始数)与 0 相与,无论该位原来是什么结果都是 0,而其他位与 1 相与保持不变。
or 指令 按位逻辑或,用途是将特定位设为 1 例如 or al, 00100000B,通过让第5位与1相或,无论该位原来是什么结果都是 1,其他位与 0 相或保持不变。

字符形式的数据

计算机存储字符本质是存储数字,ASCII 码就是字符与数字的映射表。在汇编源程序中可以直接使用单引号 '...' 来定义字符数据。例如如下程序中的 db 'unIX',编译器会自动将其转换为对应的 ASCII 码值(75H, 6EH, 49H, 58H)。

assume cs:code, ds:datadata segment
    db 'unIX'
    db 'foRK'
data endscode segment
start:  mov ax, data
        mov ds, ax
        mov ax, 4c00h
        int 21h
code endsend start

在 Debug 中查看内存时,既可以看其十六进制数值,也可以看其右侧对应的 ASCII 字符。
image

字符的大小写转换的是一个经典的位操作案例,考虑一个将 datasg 中的第一个字符串转为大写,第二个转为小写的程序。一种方式是,可以通过观察发现小写字母比大写字母的 ASCII 码值大 20H(如'a'=61H, 'A'=41H)。如果采用这个规律,程序必须先判断字母是大写还是小写,再决定加减 20H。

但在当时掌握的指令集中,目前缺乏高效的判断指令。观察 ASCII 码的二进制形式,发现关键在第 5 位(bit 5):大写字母(A-Z)的第 5 位为 0 (如 'A': 41H -> 010000001);小写字母(a-z)第 5 位为 1 (如 'a': 61H -> 011000001),其他位都相同。

大写 大写十六进制 大写二进制 (bit7~bit0) 小写 小写十六进制 小写二进制 (bit7~bit0) 第5位(bit5)
A 41H 010000001 a 61H 011000001 0 → 1
B 42H 010000010 b 62H 011000010 0 → 1
C 43H 010000011 c 63H 011000011 0 → 1
D 44H 010000100 d 64H 011000100 0 → 1

因此使用如下程序完成,该程序使用了两个循环。

assume cs:code, ds:datadata segment
    db 'BaSic'
    db 'iNfOrMaTion'
data endscode segment
start:  mov ax, data
        mov ds, ax        mov bx, 0
        mov cx, 5
s0:     mov al, [bx]
        and al, 11011111b
        mov [bx], al
        inc bx
        loop s0        mov bx, 5
        mov cx, 11
s1:     mov al, [bx]
        or al, 00100000b
        mov [bx], al
        inc bx
        loop s1        mov ax, 4c00h
        int 21h
code ends
end start

image
转为大写可以使用 and al, 11011111B (即 DFH) 将第 5 位清零,这样无论原来是大写(已是 0)还是小写(变为 0),结果都变为大写。
image
转为小写可以使用 or al, 00100000B (即 20H) 将第 5 位置 1,无论原来是小写(已是 1)还是大写(变为 1),结果都变为小写。
image

利用位运算处理字符大小写在现代编程中依然有效,尤其是在底层库或高性能代码中。不过,现代高级语言(如 C/Java)通常提供了标准的库函数(如 toupper, tolower)来封装这些细节。此外,现代字符编码(如 UTF-8)远比ASCII复杂,简单的位操作已不足以应对国际化和Unicode字符的大小写转换。

带偏移量的内存寻址

基址加偏移的寻址模式表示为 [bx+idata],意为内存单元的偏移地址等于 (bx) + idata。例如 mov ax,[bx+200],其数学描述为:\((ax)=((ds)\times16+(bx)+200)\)。编译器支持多种等价格式,如 200[bx][bx].200,其中 200[bx] 在可读性上更接近高级语言的风格。这和 C 语言中的数组访问是对应的,高级语言数组访问的底层实现逻辑是一个基地址加上一个变址

带偏移量的寻址方式可以令处理多个数组变得更加简洁。例如之前的程序要将 datasg 中第一个字符串转为大写,第二个转为小写。现在可以利用 [0+bx] 定位第一个字符串,用 [5+bx] 定位第二个字符串,这样只需要一个循环然后用 BX 作为索引变量递增即可。

assume cs:code, ds:datadata segment
    db 'BaSic'
    db 'MinIX'
data endscode segment
start:  mov ax, data
        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        mov ax, 4c00h
        int 21h
code ends
end start

image

SI 和 DI 寄存器

SI(Source Index)和 DI(Destination Index) 是 8086 CPU 中两个用于在内存之间传输数据的寄存器,功能与 BX 类似。SI 和 DI 不能像 BX 那样拆分为两个 8 位寄存器,也就是在 8086 中它们只能作为 16 位寄存器使用,用来存放内存偏移地址。

考虑一个将 datasg 中的 'welcome to masm!' 复制到其后的数据区的程序,源地址是 datasg:0,目标地址是 datasg:16(字符串长 16 字节)。使用 SI 指向源地址(0),SI + 16 指向目标地址(16)。由于是字操作,需要循环 8 次每次复制 2 个字节。

assume cs:code, ds:datadata segmentdb 'welcome to masm!'db '................'
data endscode segment
start:  mov ax, datamov ds, axmov si, 0mov cx, 8
s:      mov ax, [si]mov 16[si], axadd si, 2loop smov ax, 4c00hint 21h
code ends
end start

image
image

在现代 x86-64 架构中,SI 和 DI 演变成了 RSI 和 RDI,并且增加了 RIP 相对寻址(RIP-relative addressing)。现代 CPU 拥有专门的字符串操作指令,如 MOVSB/MOVSW 和重复前缀(REP),配合 SI 和 DI 可以一行指令完成上述循环。此外,SI 和 DI 在 64 位模式下终于可以被拆分为低 8 位(SIL, DIL)和低 16 位(SIW, DIW)使用。

二重寻址

二重寻址方式引入了两个变量来确定内存地址,格式为 [bx+si],其物理地址计算公式为 \((ds)\times16+(bx)+(si)\)。编译器同样支持简洁写法 [bx][si],在逻辑上对应了高级语言中的二维数组访问。

考虑一个程序,它的 datasg 段包含 '1. file' 等字符串,每行 16 字节。需要将 datasg 段中每个单词的头一个字母改为大写字母,目标字符位于每行的第 4 列。此时即可使用 [bx+idata],BX 作为行指针(每次 +16),常量 3 作为列偏移。

assume cs:code, ds:datadata segment
    db '1. file         '
    db '2. edit         '
    db '3. search       '
    db '4. view         '
    db '5. options      '
    db '6. help         '
data endscode segment
start:  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        mov ax, 4c00h
        int 21h
code ends
end start

image
image

三重寻址

结合基址寄存器(BX)、变址寄存器(SI/DI)和位移量(idata)可以实现形如 [bx+si+idata] 的三重寻址,指令 mov ax,[bx+si+idata] 的数学描述为:\((ax)=((ds)\times16+(bx)+(si)+idata)\)。常用写法如 idata[bx][si][bx].idata[si] 等,该结构适合用于访问结构体内部的数组,或者数组内的结构体成员。目前涉及到的几种寻址方式的小结如下:

寻址方式 示例 适用场景
直接寻址 [idata] 访问单个静态变量
寄存器间接 [bx] 访问一维数组(单指针)
寄存器相对 [bx+idata] 访问结构体成员或一维数组(带起始偏移)
基址变址 [bx+si] 访问二维数组(双指针)
相对基址变址 [bx+si+idata] 访问结构体数组或复杂嵌套数据

考虑一个程序,它的 datasg 段包含 'ibm' 等 4 行字符串,每行前 3 列为字母,需要将 datasg 段中每个字符串都改为大写字母。由于二重循环需要两个计数器,但只有一个 CX。如果在内层循环中直接使用 loop,外层 CX 会被覆盖。可以考虑用 DX 暂存 CX,代码如下:

assume cs:code, ds:datadata segment
    db 'ibm             '
    db 'dec             '
    db 'dos             '
    db 'vax             '
data endscode segment
start:  mov ax, data
        mov ds, ax
        mov bx, 0
        mov cx, 4s0:     mov dx, cx
        mov si, 0
        mov cx, 3
s1:     mov al, [bx+si]
        and al, 11011111b
        mov [bx+si], al
        inc si
        loop s1
        mov cx, dx
        add bx, 16
        loop s0        mov ax, 4c00h
        int 21h
code ends
end start

image
image
image

但如果 DX 也需要被内层使用,则该方法失效。虽然也可以在 Data Segment 中预留内存单元(如 dw 0)暂存 CX,但是这样容易混乱,不易扩展。较好的方式是使用栈,利用先进后出特性,在进入内层循环前 push cx,在内层循环结束后 pop cx

assume cs:code, ds:data, ss:stackdata segmentdb 'ibm             'db 'dec             'db 'dos             'db 'vax             '
data endsstack segmentdb 0,0,0,0,0,0,0,0
stack endscode segment
start:  mov ax, datamov ds, axmov bx, 0mov cx, 4s0:     push cxmov si, 0mov cx, 3
s1:     mov al, [bx+si]and al, 11011111bmov [bx+si], alinc siloop s1pop cxadd bx, 16loop s0mov ax, 4c00hint 21h
code ends
end start

image

最后考虑一个程序,将 '1. display ' 等字符串中单词的前 4 个字母改为大写。方法是令 BX 作为行指针,每次 +16。因为单词前的 '1. ' 占 3 字节,用常量 3 加上变址寄存器 SI 遍历前 4 个字母。

assume cs:code, ds:data, ss:stackdata segmentdb '1. display      'db '2. brows        'db '3. replace      'db '4. modify       '
data endsstack segmentdb 0,0,0,0,0,0,0,0
stack endscode segment
start:  mov ax, datamov ds, axmov bx, 0mov cx, 4s0:     push cxmov si, 0mov cx, 4
s1:     mov al, [bx+si+3]and al, 11011111bmov [bx+si+3], alinc siloop s1pop cxadd bx, 16loop s0mov ax, 4c00hint 21h
code ends
end start

在现代编译器中,二重循环和栈的使用是隐式的。在 C 语言中写 for(i=0;i<4;i++) { for(j=0;j<3;j++) {...} } 时,编译器会自动生成类似上面的代码:将外层循环变量 i 保存到栈帧(Stack Frame)中,执行内层循环后再恢复 i。因此递归过深会导致栈溢出,正是因为保存现场的次数超过了栈的容量。

参考资料

《汇编语言(第四版)》,王爽 编著,清华大学出版社

http://www.jsqmd.com/news/1375469/

相关文章:

  • 2026年香河口碑好的调度台怎么选?这几家优选名单请收好 - geo交流
  • 2026年CIP复合碱性清洗剂直销厂商哪家靠谱优选陕西欣诺华生物科技有限公司 - 热点品牌推荐
  • 2026年不锈钢渔网刀源头厂家哪家专业,认准东台市久天机械制造有限公司 - 热点品牌推荐
  • 零基础快速制作投票活动,8 个线上评选高频问题,评选星投票故障处理汇总 - 投票评选制作软件系统
  • 卫浴螺丝定制厂家怎么联系找对渠道少踩坑厦门圣必得五金制品有限公司 - 热点品牌推荐
  • 2026广东地区值得选择的论文辅导机构科普 - 互联网科技品牌测评
  • 2026年找排气管缩管厂家哪家靠谱?选宁波易弯机械科技有限公司 - 热点品牌推荐
  • 题目代码列表
  • 广州政企智慧食堂解决方案怎么选?2026年选型标准与品牌推荐 - 优质品牌中立测评推荐
  • 2026年评价好的留学咨询机构推荐:五家优选深度解析 - 科技焦点
  • 8个必知物品邮寄常识:帮你省钱避坑,寄件再也不踩雷
  • 寻访新疆优质的薯片薯条油炸机公司认准卡赫农业装备(诸城)有限公司(新疆销售部) - 热点品牌推荐
  • 2026年园林路沿石热门厂家怎么选择 高性价比厂商汇总 - 热点品牌推荐
  • 2026年成都新能源二手车选购指南:同展车多多的专业解析 - 热点品牌推荐
  • 国内高效除浇口切水口机厂家直联方式与深圳市壹零壹精密设备有限公司 - 热点品牌推荐
  • 2026吕梁市可靠的废铜电线电缆回收哪家强?看完这份严选对比再决定 - geo交流
  • 2026年青岛穿梁伸缩套管济南供货商优选指南:3个维度帮你精准决策 - geo交流
  • 2026年奉贤新房装修价格影响因素全解析 - 盟道科技
  • 2026 上海空调维修正规靠谱商家平台推荐 :上海迪迅通制冷设备有限公司 | 12 年本土持证自营商家,专注空调维保正规服务商,上海空调维修避坑指南 - 星际AI
  • 2026广东地区值得关注的论文辅导机构相关科普 - 互联网科技品牌测评
  • 2026 靠谱微信投票工具挑选,天天评选投票匿名实名自由切换实操指南 - 投票评选制作软件系统
  • 广州智慧食堂解决方案哪家好?2026年品牌盘点与选购指南 - 优质品牌中立测评推荐
  • 2026年出国留学机构参考:五家优选品牌深度解析 - 科技焦点
  • 2026年美国本科留学机构推荐评估:五家优选深度解析 - 科技焦点
  • 做微信投票总遇到问题?梳理 20 个制作高频痛点,评选星投票帮你避开活动隐患 - 投票评选制作软件系统
  • 2026年德国留学APS审核辅导哪家强:五家优选深度解析 - 科技焦点
  • 视频去水印免费方法合集,工具选择与风险须知 - 耶斯去水印
  • 2026年卫浴螺丝定制厂家推荐,结合需求选厦门圣必得五金制品有限公司 - 热点品牌推荐
  • 智慧食堂AI结算系统怎么选?2026年技术对比与品牌推荐 - 优质品牌中立测评推荐
  • [AI Agent] Codex 计划模式