[bx+idata]的寻址方式、SI和DI寄存器、[bx+si]和[bx+di]方式寻址、[bx+si+idata]和[bx+di+idata]方式寻址、数据位置的表达、div指令、dup设置内存空间
[bx+idata]的含义解读
[bx+idata]表示一个内存单元,它的偏移地址为(bx)+idata(也就是bx中的数值+idata值)
比如:mov ax,[bx+200],它的含义如下:
1、将一个内存单元的内容关入AX寄存器中
2、这个内存单元的长度为2个字节(字单元),存放一个字
3、内存单元的段地址在ds中,偏移地址为200+bx中存放的数据值
4、数学化的描述为:(ax)=((ds)*16+200+(bx))
mov ax,[bx+200]还有其它写法,如下:
1、mov ax,[200+bx]
2、mov ax,200[bx]
3、mov ax,[bx].200
与C语言中的数组的数据排列有点类似。
SI和DI常执行与地址有关的操作
SI:source index ,源变址寄存器
DI:destination index ,目标变址寄存器
SI和DI是8086CPU中的和BX功能相近的寄存器,前后两者的区别是啥呢?
区别就是SI和DI不能够分成两个8位寄存器来使用。
问题:用寄存 器SI和DI实现将字符串‘welcom to masm’复制到它后面的数据区中。

; ==============================
; 8086汇编 内存块复制程序 - 完整分析版
; 功能:将data段中"welcome to masm!"复制到后续16字节占位区
; 作者:汇编分析
; 日期:2025
; ==============================
; 1. 段关联伪指令:告知编译器段寄存器与段名的对应关系(仅编译提示,无执行逻辑)
assume cs:code,ds:data
; 2. 数据段定义:存放源字符串和目标占位区
data segment
; 源数据:16字节字符串(w e l c o m e t o m a s m !)
db 'welcome to masm!'
; 目标数据:16字节占位符(初始为点,用于接收复制结果)
db '................'
data ends ; 数据段结束(总长度32字节)
; 3. 代码段定义:存放程序执行逻辑
code segment
start : ; 程序入口标签(end start 指定从此处执行)
; --------------------------
; 步骤1:初始化DS寄存器,指向data段
; --------------------------
mov ax,data ; 8086规则:段寄存器不能直接赋值,先将data段地址送入AX
mov ds,ax ; 将AX的值送入DS,使DS指向data段(后续[si]/[di]默认用DS段)
; --------------------------
; 步骤2:初始化循环相关寄存器
; --------------------------
mov si,0 ; SI = 0 → 源数据偏移地址(指向字符串第一个字符'w')
mov di,16 ; DI = 16 → 目标数据偏移地址(指向占位区第一个点)
mov cx,8 ; CX = 8 → 循环计数器(每次复制2字节,16字节需8次)
; --------------------------
; 步骤3:循环复制核心逻辑(2字节/次)
; --------------------------
s: ; 循环体标签(自定义名称,可替换为任意合法标签)
mov ax,[si] ; 取DS:SI指向的2字节数据 → AX(如第一次:偏移0/1的'w'+'e')
mov [di],ax ; 将AX中的2字节写入DS:DI指向的地址(第一次:偏移16/17)
add si,2 ; 源偏移+2 → 指向下一个2字节
add di,2 ; 目标偏移+2 → 指向下一个2字节
loop s ; 循环指令:CX -= 1;若CX≠0则跳回s,否则退出循环
; --------------------------
; 步骤4:程序正常退出(DOS中断)
; --------------------------
mov ax,4c00h ; AX = 4C00H → DOS中断功能号(4C=程序退出,00=返回码)
int 21h ; 调用21H中断,退出程序返回命令行
code ends ; 代码段结束
end start ; 汇编结束指令:1. 标记汇编结束;2. 指定程序入口为start
; ==============================
; 【关键分析】
; ==============================
; 1. 内存布局变化(执行前→执行后):
; 偏移0~15:welcome to masm! → 不变
; 偏移16~31:................ → welcome to masm!
;
; 2. 循环执行过程(8次循环):
; 次数 | CX值 | SI值 | DI值 | 复制的2字节
; 1 | 8 | 0 | 16 | 'w'+'e'
; 2 | 7 | 2 | 18 | 'l'+'c'
; 3 | 6 | 4 | 20 | 'o'+'m'
; 4 | 5 | 6 | 22 | 'e'+' '
; 5 | 4 | 8 | 24 | 't'+'o'
; 6 | 3 | 10 | 26 | ' '+'m'
; 7 | 2 | 12 | 28 | 'a'+'s'
; 8 | 1 | 14 | 30 | 'm'+'!'
;
; 3. 扩展优化(逐字节复制,兼容任意长度):
; 若需复制非2倍数长度的字符串,可修改循环逻辑:
; mov cx,16 ; 16字节→循环16次
; s:
; mov al,[si] ; 单字节送入AL(代替AX)
; mov [di],al ; 单字节写入目标
; inc si ; 源偏移+1(代替add si,2)
; inc di ; 目标偏移+1(代替add di,2)
; loop s
;
; 4. 核心知识点:
; - 段寄存器初始化:DS必须指向数据段,否则内存寻址错误;
; - loop指令:依赖CX计数器,CX=0时退出循环;
; - 16位数据传输:AX作为2字节中转寄存器,效率高于单字节(AL);
; - 物理地址计算:8086物理地址 = 段寄存器值 × 16 + 偏移地址。
[bx+si]和[bx+di]方式寻址
1、偏移地址为(bx)+(si),也就是说用bx中的数值+si中的数值
指令mov ax,[bx+si]的含义解读
1、将一个内存单元的数据或内容送入ax寄存器中
2、这个内存单元的长度为2个字节
3、偏移地址为bx中的数个+si中的数值
4、段地址默认在ds中
5、mov ax,[bx][si]是另一种写法
[bx+si+idata]和[bx+di+idata]方式寻址
[bx+si+idata]表示一个内存单元,偏移地址为(bx)+(si)+idata,即bx中的数值+si中的数值+idata;
其含义解读与上面的类似
内存寻址方式
| 寻址方式 | 格式示例 | 寻址逻辑(物理地址计算) | 适用场景 | 关键说明 | |
|---|---|---|---|---|---|
| [idata] | 直接寻址 | MOV AX, [1000H] | PA = DS × 16 + 1000H | 访问固定内存单元(如全局变量) | 1. 方括号内为16 位偏移量(直接给出有效地址 EA);2. 默认段寄存器为 DS,可显式修改(如MOV AX, ES:[1000H]);3. 偏移量范围:0~FFFFH。 |
| [bx] | 寄存器间接寻址 | MOV AX, [BX] | PA = DS × 16 + BX(EA=BX) | 访问数组 / 缓冲区(通过指针) | 1. 仅允许 BX/SI/DI/BP 作为间址寄存器;2. 用 BP 时默认段寄存器为 SS(栈段),其余默认 DS;3. 无偏移量,纯寄存器存放 EA。 |
| [bx+idata] | 寄存器相对寻址 | MOV AX, [BX+100H] | PA = DS × 16 + BX + 100H(EA=BX+100H) | 访问数组元素(基址 + 偏移) | 1. 格式可简写:[BX+100H]/100H[BX]/[BX]+100H;2. 相对偏移量可为 8 位 / 16 位立即数;3. 间址寄存器仍为 BX/SI/DI/BP。 |
| [bx+si] | 基址变址寻址 | MOV AX, [BX+SI] | PA = DS × 16 + BX + SI(EA=BX+SI) | 访问二维数组 / 表格 | 1. 基址寄存器(BX/BP)+ 变址寄存器(SI/DI)组合;2. BP 为基址时默认 SS 段,BX 为基址时默认 DS 段;3. 无额外立即数偏移。 |
| [bx+si+idata] | 相对基址变址寻址 | MOV AX, [BX+SI+100H] | PA = DS × 16 + BX + SI + 100H | 访问二维数组(带偏移) | 1. 基址 + 变址 + 立即数偏移的组合;2. 偏移量可为 8 位 / 16 位;3. 最灵活的寻址方式,覆盖复杂内存访问场景。 |
| 隐含寻址(变体) | PUSH AX / POP BX | PA = SS × 16 + SP(栈操作隐含寻址) | 栈操作、字符串指令 | 1. 无需显式指定地址,由指令隐含确定;2. 字符串指令(如 MOVSB)隐含用 SI(源)、DI(目的)、DS/ES 段。 | |
| 串寻址(变体) | MOVSB / CMPSB | 源:DS×16+SI;目的:ES×16+DI | 字符串批量操作 | 1. 专用字符串指令的寻址方式;2. 执行后 SI/DI 自动 ±1(字节)/±2(字)(由 DF 标志控制)。 |
问题:编程将每个字母转换为大写字母
;将每个单词的每个字母更改为大写
assume cs:code,ds:data
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
code segment
start :
mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s0:mov si,0
mov cx,3
s:mov al,[bx+si]
and al,11011111B
mov [bx+si],al
inc si
loop s
add bx,16
loop s0
mov ax,4c00h
int 21h
code ends
end start
上述代码有个关键错误:内层循环和外层循环共用了 cx 寄存器,导致外层循环的计数被内层循环覆盖,程序逻辑混乱,无法正确执行。
修正代码如下:

增加了一个DX寄存器,做为数据的临时存放,进行中转使用。
但是,修改后的代码有个缺点,就是寄存器未保护:
- 代码直接修改
bx、cx、dx、si、al等寄存器,但未遵循 DOS 汇编的 “寄存器调用约定”(如bx、si、dx属于 “非易失性寄存器”,应先入栈保存,用完恢复)。 - 若这段代码作为子程序被其他代码调用,会破坏调用方的寄存器值,引发兼容性问题。
再修改完善,如下
assume cs:code,ds:data
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
code segment
start :
mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s0:mov ds:[40H],cx
mov si,0
mov cx,3
s:mov al,[bx+si]
and al,11011111B
mov [bx+si],al
inc si
loop s
add bx,16
mov cx,ds:[40H]
loop s0
mov ax,4c00h
int 21h
code ends
end start
上述代码,用固定的内存空间保存数据
但是,还是存在问题,比如内存操作原子性缺失:
暂存 / 恢复cx的过程拆分为mov ds:[40H],cx和mov cx,ds:[40H]两个独立指令:
- 若程序被中断(如时钟中断、键盘中断),中断处理程序可能修改
ds:[40H]的值,导致恢复cx时拿到错误数值,外层循环次数错乱(如原本循环 4 次,可能变成随机数),程序逻辑完全失控。而上一个版本用dx寄存器暂存,寄存器操作是原子的(不会被中断打断),稳定性远高于内存暂存。
如果结合寄存器的原子性,又能安全地保护数据呢
assume cs:code,ds:data
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
data segment
db 'ibm '
db 'dec '
db 'dos '
db 'vax '
data ends
code segment
start :
mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
mov bx,0
mov cx,4
s0:push,cx
mov si,0
mov cx,3
s:mov al,[bx+si]
and al,11011111B
mov [bx+si],al
inc si
loop s
add bx,16
pop cx
loop s0
mov ax,4c00h
int 21h
code ends
end start
核心前提:loop 指令的作用
loop 标号 指令的执行逻辑是:
- 将
cx = cx - 1; - 若
cx ≠ 0,跳转到标号处;若cx = 0,执行后续指令。
因此,外层循环的 cx 会从初始的 4,每次循环后减 1,直到变为 0 时退出。
完整执行流程(关键步骤)
初始:mov cx,4 → cx=4;
第 1 次执行 s0::
push cx → 栈中压入4;
执行内层循环(cx=3),处理第 1 个字符串(ibm→IBM);
pop cx → 栈中弹出 4,cx 恢复为 4;
loop s0 → cx=4-1=3,跳回s0;
第 2 次执行 s0::
push cx → 栈中压入3;
执行内层循环(cx=3),处理第 2 个字符串(dec→DEC);
pop cx → 栈中弹出 3,cx 恢复为 3;
loop s0 → cx=3-1=2,跳回s0;
第 3 次执行 s0::
push cx → 栈中压入2;
执行内层循环(cx=3),处理第 3 个字符串(dos→DOS);
pop cx → 栈中弹出 2,cx 恢复为 2;
loop s0 → cx=2-1=1,跳回s0;
第 4 次执行 s0::
push cx → 栈中压入1;
执行内层循环(cx=3),处理第 4 个字符串(vax→VAX);
pop cx → 栈中弹出 1,cx 恢复为 1;
loop s0 → cx=1-1=0,不跳转,退出外层循环。
汇编语言中数据位置的表达
| 1、立即数 | 2、寄存器 | 内存:段地址(SA)和偏移地址(EA) |
|
对于直接包含在机器指令中的数据,称为立即数idata,数据包含在指令中 mov ax,1 add bx,2000H or bx,00010000B mov al,'a' |
指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。 mov ax,bx mov ds,ax push bx mov ds:[0],bx mov ss,ax mov sp,ax |
指令要处理的数据在内存中,由SA:EA确定内存单元。 mov ax,[0] mov ax,[di] 段地址默认在ds中 mov ax,[bp] mov ax,[bp+si] 段地址默认在ss中 |
指令要处理的数据有多长
| 字word操作 | 字节byte操作 | 用word ptr 或byte ptr 指明数据长度 |
|
mov ax,1 mov bx,ds:[0] mov ds,ax mov ds:[0],ax inc ax add ax,1000 |
mov al,1 mov al,bl mov al,ds:[0] mov ds:[0],al inc al add al,100 |
mov word ptr ds:[0],1 inc word ptr [bx] inc word ptr ds:[0] add word ptr [bx],2 |
div指令
div是除尘指令,使用div做除尘的时候
被除数:(默认)放在AX或DX和AX中
除数:8位或16位,在寄存器或内存单元中
DIV指令格式
div 寄存器 或是
div 内存单元
| 被除数 | AX | DX和AX |
| 除数 | 8位内存或寄存器 | 16位内存或寄存器 |
| 商 | AL | AX |
| 余数 | AH | DX |
dup功能和用法
dup和db、dw、dd等数据定义伪指令配合使用,用来进行数据的重复
| 指令 | 功能 | 相当于 |
| db 3 dup(0) | 定义了3个字节,它们的值都是0 | db 0,0,0 |
| db 3 dup(0,1,2) | 定义了9个字节,由0,1,2重复3次构成 | db 0,1,2,0,1,2,0,1,2 |
| db 3 dup('abc','ABC') | 定义了18个字节,构成‘abcABCabcABCabcABC’ | db 'abcABCabcABCabcABC' |

浙公网安备 33010602011771号