汇编笔记(持续更新中)
汇编笔记
寄存器register
学习汇编语言,首先必须了解两个知识点:寄存器和内存模型。
先来看寄存器。CPU 本身只负责运算,不负责储存数据。数据一般都储存在内存之中,CPU 要用的时候就去内存读写数据。但是,CPU 的运算速度远高于内存的读写速度,为了避免被拖慢,CPU 都自带一级缓存和二级缓存。基本上,CPU 缓存可以看作是读写速度较快的内存。
但是,CPU 缓存还是不够快,另外数据在缓存里面的地址是不固定的,CPU 每次读写都要寻址也会拖慢速度。因此,除了缓存之外,CPU 还自带了寄存器(register),用来储存最常用的数据。也就是说,那些最频繁读写的数据(比如循环变量),都会放在寄存器里面,CPU 优先读写寄存器,再由寄存器跟内存交换数据。
内存模型:Heap
-
程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用malloc命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址0x1000开始给他分配,一直分配到地址0x100A,如果再要求得到22个字节,那么就分配到0x1020。
-
这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。
的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收
内存模型: Stack
-
除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域。
-
系统开始执行main函数时,会为它在内存里面建立一个帧(frame),所有main的内部变量(比如a和b)都保存在这个帧里面。main函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。
-
在函数内部在调用一个函数process()的时候,系统也会为该函数再新建一个帧,一般来说调用栈有多少层,就有多少帧。
-
当函数运行结束之后,它的帧会被回收,系统回到函数中断执行的地方继续执行上一个帧的流程。通过这个机制,可实现函数的层层调用,且每一层函数都可以使用自己的本地变量
-
Stack是由内存区域的结束地址开始分配,从高位到地位分配
CPU指令
使用 g++ file.cpp -S out.s
或者gcc file.cpp -S out.s
,可用C/C++生成相应的汇编代码
#include<stdio.h>
void hello()
{
int a=1,b=2;
a=a+b;
printf("hello world\n");
}
int main()
{
int a=1,b=2;
a=a+b;
hello() ;
return 0;
}
.LC0:
.string "hello world"
hello():
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
mov eax, DWORD PTR [rbp-8]
add DWORD PTR [rbp-4], eax
mov edi, OFFSET FLAT:.LC0
call puts
nop
leave
ret
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov DWORD PTR [rbp-8], 2
mov eax, DWORD PTR [rbp-8]
add DWORD PTR [rbp-4], eax
call hello()
mov eax, 0
leave
ret
push 指令
push rbp
push是CPU指令,rbp是该指令的算子.一个CPU指令可以有0到多个算子
寄存器
IA-32体系结构中有10个32位和6个16位处理器寄存器。寄存器分三类:
-
通用寄存器
-
数据寄存器
-
指针寄存器
-
索引寄存器
-
-
控制寄存器
-
段寄存器
指令系统
80x86寻址方式
-
寻址:寻找操作数的地址。
-
寻址方式:寻找操作数的方式
80x86指令格式
- 指令助记符 +操作数1,+操作数2,+操作数3
- 指令的操作数个数可以是0,1,2,3个
如何确定偏移地址的值
- 与数据有关的十种方式,
- 立即寻址
- 寄存器寻址
- 直接寻址
- 寄存器间接寻址
- 寄存器相对寻址
- 基址变址寻址
- 相对基址变址寻址
- 比例变址寻址
- 基址比例变址寻址
- 相对基址比例变址寻址
- 与转移地址有关的4种寻址方式
- 段内直接寻址
- 段内间接寻址
- 段间直接寻址
- 段间间接寻址
寻址方式
立即寻址方式
MOV AX, 5
MOV AX, 05H
把十六进制数05H 移动到通用寄存器AX上
寄存器寻址方式(无EA)
- 指令所要的操作数已存储在寄存器中或者把目标数存入寄存器
MOV AX , BX
- 指令中可以引用的寄存器及其符号名称:
8位寄存器 | AH、AL、BH、BL、CH、CL、DH、DL |
8位寄存器 | AX、BX、CX、DX、SI、DI、SP、BP和段寄存器等 |
8位寄存器 | EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP。 |
-
DST 和SRC的字长一致
-
CS不能用MOV指令改变
直接寻址方式
-
指令所要的操作数存放在内存中,在指令中直接给出该操作数的有效地址
-
物理地址=
-
MOV AX , [2000H]
-
MOV AX, VALUE
-
MOV AX , [VALUE]
-
-
物理地址=
-
MOV AX , ES:[2000H]
-
MOV AX, ES:VALUE
-
MOV AX , ES:[VALUE]
-
在通常情况下,操作数存放在数据段中,所以,其物理地址将有数据段寄存器DS和指令中的有效地址直接形成但如果使用段超越前缀,那么操作数可存放在其他段中。如:
MOV ES:[1000H], AX
-
立即寻址:1234H
-
直接寻址;[1234H]
-
寄存器间接寻址方式→EA基址/变址
- MOV AX,[BX]或MOV AX,ES:[BX]
寄存器相对寻址方式→EA=基址/变址+位移量
-
MOV AX,COUNT[SI] 或 MOV AX,3000H[SI]
-
MOV AX,[COUNT+SI] 或 MOV AX,[3000H+SI]
-
MOV AX,ES:COUNT[SI] 或 MOV AX,ES:[COUNT+SI]
-
操作数在存储器中,其有效地址是一个基址寄存器(BX、BP)或变址寄存器(SI、DI的内容和指令中的8位/16位偏移量之和。
基址变址寻址方式→EA=基址+变址
-
操作数在存储器中,其有效地址是一个基址寄存器(BX、BP)和一个变址寄存器(SI、DI)的内容之和。
- MOV AX,[BX][SI] 或 MOV AX,[BX+SI]
- MOV AX,ES: [BX] [SI] 或 MOV AX,ES:[BX+SI]
相对基址变址寻址方式→EA=基址+变址+位移量
- MOV AX,COUNT [BX] [SI] 或MOV AX,-46H [BX] [SI]
- MOV AX,COUNT[BX+SI] 或MOVAX,0246H[BX+SI]
- MOV AX,[COUNT+BX+SI] 或 MOV AX,[-56H+BX+SI]
- MOV AX,ES:COUNT [BX] [SI] 或 MOV AX,ES:[0246H+BX+SI]
相对比例变址寻址→EA=变址×比例因子+位移量
-
MOV EBX,[EAX][EDX*8] 或 MOV EBX,
-
[EAX+EDX*8] 或 MOV EBX,[ESP] [EAX*2]
-
MOV EAX,TABLE [EBP] [EDI*4] 或 MOV EAX,[TABLE+EBP+EDI*4]
8位/16位/32位的位移量
注意1:比例因子只能与32位变址寄存器:EAX、EBX、ECX、EDX、EBP、ESI、
EDI联用;且比例因子只能为1、2、4或8;
注意2: 8)、9)、10)只能是32位寻址,没有16位寻址;
-
基址比例变址寻址→EA=基址+变址×比例因子
-
相对基址比例变址寻址→EA=基址+变址×比例因子+位移
汇编程序
-
段结束:
xxx ends
(xxx 即为段名)
-
程序结束:
end
-
程序返回:
-
MOV AX 4C00H INT 21
-
汇编debug指令
在DOSbox中输入debug 即可进入debug模式
-
-d xxxx:xxxx
即可读取相应的内存的信息。 (dump) -
-e xxxx:xxxx aa bb cc dd
把aa bb cc dd写入对应的地址中。 -
-e 0000:0000 12.AB 34.CD
把内存对应的 12 修改成 AB, 34 修改成 CD 。 -
-a
输入汇编指令 -
-t 执行 -a输入的汇编指令。
.......回头再补吧,debug 指令先放一下。
汇编基本指令
常用指令
-
mov
-
基本指令 mov ax , 0fffh 把16进制数赋值给ax
-
mov ax ,01110011B 把二进制数赋值给 ax
-
mov ax ,0110O 把八进制数赋值给 ax
-
-
add
add ax ,10
, 意思就是ax +=10。
-
sub
sub ax ,10
意思就是 ax-=10。
-
mul
- 两个相乘的数,要么都是八位,要么都是十六位.
- 如果是8位,一个默认放在AL中 ,另一个放在8位reg 或内存字节单元中。
- 如果是16位,一个默认放在AX中,另一个放在16位reg 或内存字节单元中。
- 两个相乘的数,要么都是八位,要么都是十六位.
-
div
- 除数:有8位和16位两种,存放在一个寄存器或内存单元中。
-
被除数:默认放在AX或(DX和AX)中,如果除数为8位,被除数则为16位,默认放在AX中;如果除数为16位,那么被除数就为32位,存放在DX和AX两个寄存器中,高16位存放在DX,低16位存放在AX。
-
两数相除结果:如果除数是8位,则除法操作的商存放在AL,除法操作的余数存放在AH;如果除数为16位,则商存放在AX,余数存放在DX。
-
这里注意除数必须放在寄存器或者内存单元中, 不能使用立即数, 因为立即数无法推出除数的大小
-
shl
shl al ,1
左移一位。
-
shr
shr al ,1
右移一位。SHR
是一个指令,代表 “Shift Right”(逻辑右移)。这个指令用于将操作数中的位向右移动指定的次数。
-
rol
rol al ,1 循环左移。
-
ror
ror al ,1
循环右移
-
rcr
rcr al ,1
带进位的循环右移。
-
rcl
rcl al ,1
带进位的循环左移。
-
inc
inc ax
自增。
-
dec
dec ax
自减。
-
xchg
xchg ax, bx
交换。
-
mov bx , word ptr [0]
- 位拓展。
-
offset
- offset 获取函数(标志)地址指令
- mov ax , offset s (s 为上文写到的标志)
- offset 获取函数(标志)地址指令
-
中断指令
-
int 0
- 除法除以0 的中断
-
MOV AX , 4C00H INT 21H
- 退出程序
-
-
jmp 跳转指令
-
jmp short {ptr [偏移地址]} /
-
jmp far {ptr [偏移地址]} /
-
jmp near {ptr [偏移地址]} /
jmp 存储的是跳转的距离, 往上跳转的距离
-
jmp 1000:0003
EA 1000: 0003
就是jmp的内存中显示的格式,代表跳转到 1000:0003 -
jmp word ptr 内存单元地址
- 读取内存中存在的一个字,字即是转移的目的偏移地址
-
jmp dword ptr 内存单元地址
- 读取内存中的两个字 , 并跳转到该两个字指向的地址
- 例如 jmp dword ptr ds:[0] , ds[0] 开始的一个字作为 IP , ds[2] 开始的一个字作为 CS, 高地址为 IP , 低地址为 CS .
-
-
cmp 指令 , 判断指令
-
JE ;等于则跳转
-
JNE ;不等于则跳转
-
JZ ;为 0 则跳转
-
JNZ ;不为 0 则跳转
-
JS ;为负则跳转
-
JNS ;不为负则跳转
-
JC ;进位则跳转
-
JNC ;不进位则跳转
-
JO ;溢出则跳转
-
JNO ;不溢出则跳转
-
JA ;无符号大于则跳转
-
JNA ;无符号不大于则跳转
-
JAE ;无符号大于等于则跳转
-
JNAE ;无符号不大于等于则跳转
-
JG ;有符号大于则跳转
-
JNG ;有符号不大于则跳转
-
JGE ;有符号大于等于则跳转
-
JNGE ;有符号不大于等于则跳转
-
JB ;无符号小于则跳转
-
JNB ;无符号不小于则跳转
-
JBE ;无符号小于等于则跳转
-
JNBE ;无符号不小于等于则跳转
-
JL ;有符号小于则跳转
-
JNL ;有符号不小于则跳转
-
JLE ;有符号小于等于则跳转
-
JNLE ;有符号不小于等于则跳转
-
JP ;奇偶位置位则跳转
-
JNP ;奇偶位清除则跳转
-
JPE ;奇偶位相等则跳转
-
JPO ;奇偶位不等则跳转
-
push ax :
-
注意 若 ax =1234H ,则它在内存里面的储存方式是: 34 12 , 从低到高的顺序, 内部顺序不变
-
栈的储存顺序是从后往前依次储存的.
-
-
pop ax :
- 将栈顶元素弹出并赋值给ax
-
adc : 带进位加法指令
- 即adc 会把低位的进位加到高位中.
- ADC AX , BX : (ax) = (ax)+(bx) +(CF)
-
sbb 带进位减法
- sbb ax , bx 意思是: (ax) = (ax) -(bx) -(CF)
adc 在利用2个 dw 来表示一个32位数的时候有实际用处
-
type 指令, 可获取数组元素的大小
-
例如
-
data segment
arr db 00,
data ends
mov ax , type arr
-
结果 ax = 0001 H , 因为 db 是一个字节大小的, 如果是 dw , 那么ax = 0002H , dw 是2个字节大小
-
-
-
lea
- LEA指令的格式为:
LEA 目的, 源
- 与MOV指令相比,LEA指令的主要区别在于:MOV指令用于在寄存器或内存之间传输数据,而LEA指令仅用于传输地址的有效地址,不涉及数据的传输。例如,
MOV AX, [BX]
会将内存地址[BX]处的值加载到AX寄存器中,而LEA AX, [BX+SI]
则会计算地址的有效值并加载到AX寄存器中,但不访问该地址处的内存内容。
- LEA指令的格式为:
段地址和偏移地址
段地址*16 +偏移地址 = 物理地址
0000:0100
:前面的是段地址 , : 后面的是偏移地址。
DS 寄存器的使用
mov AX, [60]
相当于把 DS : 60 处的两个字节存入AX 中
MOV DS , BX
可以把寄存器里的值赋给DS ,但不能把立即数赋值给DS。
MOV DS , 1000 (该操作是不被允许的)
- 未声明段地址的情况下默认段地址就是 DS。
声明变量
DATA SEGMENT
_DATA DB 100(?)
;100 就是多少个? ,?代表待定
DATA ENDS
寄存器的种类及作用
-
8086 CPU 有14 个寄存器 , 他们的名称为: AX , BX , CX , DX , SI , DI ,SP , BP , IP , CS , SS , DS , ES , PSW , 所有的的寄存器都是16位的,可以放两个字节。
-
AX: 累加器。
与mul 和div 作用有关。
-
BX : 基地址寄存器,可以储存地址并访问
- 地址表达方式 : 240B:1001
-
CX : 与 loop 指令有关, 每次 loop , CX -- , CX 为0 ,loop 结束
-
AX ,BX ,CX ,DX 都属于通用寄存器,即可以把一个16位寄存器拆成两个8位寄存器使用。
段寄存器 CS : IP
- CS:IP 即为可执行汇编指令的储存的位置。
- 汇编语言中代码和数据是不加区分的,都存在于内存里面.
栈寄存器 SS: SP
任意时刻 , SS:SP
指向栈顶元素
-
压栈 sp -=2
-
弹出 sp+=2
-
当栈已空的时候, 如果继续弹出, 计算机会继续进行 把sp指向的内存弹出并进行sp+=2的操作 .
-
栈的容量
-
空栈:在进行堆栈操作前,为空栈。此时SP应预置一个初值。该值为堆栈空间的大小
-
SP初值=堆栈空间的最大容量
例:SP=0008H。则最大容量为8个字节。
SP指向当前的栈顶。
-
如何避免栈顶超界??
只能通过人为注意栈顶是否超界的问题
偏移地址 [1(立即数)] ,[BX + SI] , [BX +DI] [BX + SI + 1(立即数)]
-
形式 [BX + SI/ DI + 立即数]
-
立即数可单独作为偏移地址
-
BX , SI 和 DI 都可单独作为偏移地址 , BX 可与立即数, SI , DI 相加构成偏移地址
-
寄存器相加再加上立即数也可以构成合法的偏移地址
BP 寄存器
类似BX 的一种替代,但是在某些细节存在区别, 不可以和BX 同时存在[]中寻址.
- 如果指令中没有显示地给出段地址, 段地址就默认在ss中,即在堆区.
ES 寄存器
- 额外附加段寄存器
特殊标志位
ZF:
- (zero flag): 计算结果 AX 为 0 , zf =1 , 否则 zf -0 .
PF:
- 所有计算结果 AX 的二进制表达中1的个数是否为偶数, 是偶数 PF =1 ,否则 PF =0
SF :
- flag 的第七位是SF , 是符号位 , 如果计算结果为负 ,SF =1 , 否则SF =0 .
CF
- flag 的第0 位是 CF , 进位标志位, 记录了在进行无符号数运算时,运算结果向更高位的进位值或者借位值
OF
-
有符号数运算的结果是否发生了溢出, 溢出 , OF =1 ,无溢出 , OF =0 .
原码、反码和补码的定义和转换规则
- 原码:用最高位表示数值的符号,右边各位表示数值的绝对值。正数的原码与反码、补码相同,负数的原码符号位为1,其余位为该数的绝对值的二进制表示。
- 反码:正数的反码与原码相同,负数的反码是除符号位外其他位取反。
- 补码:正数的补码与原码相同,负数的补码是其反码加1。
函数与封装
-
函数定义:
-
S : mov ax ,1000H RET
-
-
函数调用:
call {ptr [偏移地址]} / {标号}
call far {ptr [偏移地址]} / {标号}
: 与 retf 搭配使用
-
ret 与 retf 的区别
-
ret 利用栈区的内存, 修改了 IP , 实现了近转移
-
retf 利用栈区的数据修改了CS 与 IP , 实现了远转移.
-
区别则是指令内部跳转内存是用了一个16位数表示还是2个16位数表示.ret 只会把下一条指令的 IP 信息进行压栈, retf 则会将 下一条指令的CS 与 IP 的信息都进行压栈 . retf 压栈先压 IP , 再压 CS .
-
CALL 相当于:
-
pop IP jmp 标号
-
-
call far {ptr [偏移地址]} / {标号} 相当于:
-
push CS push IP jmp far {ptr [偏移地址]} / {标号}
-
存储单位
-
-
db : define byte 8 位, 1 字节
-
dw : define word 16 位
-
dd : define dword 32 位
-
dq : define Qword 64 位
1 字节 = 8 byte , 1 字 = 2 字节
- 在code 段定义数据可能会将data段当成代码段来执行产生奇怪的指令.
- 所以写 start 可以直接让 CS : IP 指向可执行代码的位置,从而避免经过 data 段
将数据,代码, 栈放入不同的段
assume cs : code , ds : data , ss:stack
- assume 最典型的用法:是指明变量与段寄存器的联系,比如 assume ds:data,它是告诉编译器以后所有在data段中定义的变量寻址时,使用ds作为段地址,但是**它不对程序作任何事,比如你必须自己对ds赋值 **.
- 想要直接用数据标号访问数据 , 必须用 assume 把标号所在的段和一个段寄存器连接起来
注意: 需要提前声明 code , data , stack 内存段
数据段声明方式:
- dup 指令
data segment
arr(别名) dw 10 dup (0) ; 声明 10 个 0000 的dw 内存
arr2 db 10 dup (1,2,3); 声明了10个01,02,03 ,也就是30个db内存
data ends
-
注意字符串在内存中的储存方式是一个 db , 也就是一个字节 , 只能用 db 来声明, 如果用 dw 来声明会报错
-
输出字符时, 在 string segment 的时候在字符串后面加一个 '$'符号表示终止符
-
data segment string db "hello world",'$' data ends
-
输出字符的时候先 把offset string 赋值给 dx , 然后再 把 ah 赋值为 09H ,然后int 21h 即可输出字符
-
mov dx , offset string mov ax ,900H ; 输出字符串 int 21h mov ax ,4C00H ; 退出程序 INT 21H
-
data segment string db "hello world",0dh ,0ah,'$' data ends ; odh 是回车的asc码 ; 0ah 是换行的asc码
-
-
这里注意 , 如果定义
data segment
str db "helloworld",
db "hello world",
db "hello world",
db "hello world",
data ends
实际上定义的内存还是线性连接的 , str[5] [6] 表示的并不是第6行第7列的元素, 而相当于 str[5+6] =str[11], 代表线性排在第12 位置的元素
汇编应用
- 输出字符串:
mov dx , offset _string ; 传入输出字符串的地址 mov ah ,9 int 21h
然后利用这个和之前的逻辑函数就可以写一些简单的代码,如把大写字母转化成小写字母
data segment
string db "SURE, HERE iS THE PARAGRAPH IN UPPERCASE:",0dh,0ah,'$'
data ends
code segment
assume cs: code ,ds :data
begin:
mov ax , data
mov ds , ax
mov cx , 1000
mov bx , 0
s:
cmp string[bx] , '$'
je e
cmp string[bx] , 'A'
jb break
cmp string[bx] , 'Z'
ja break
add string[bx], 020H
break :
inc bx
loop s
e :
mov dx ,offset string
mov ah , 09H
int 21h
mov ax ,4C00H
int 21h
code ends
end begin
- 从键盘输入并接受单个字符
mov ah,01h
int 21h
注意, 这里会把字符串读取并输入到 al 中, al 就是读取的字符的asc码形式
宏定义
-
宏定义类型 宏名 MACRO [参数列表] ; 宏体 ENDM
-
宏操作符
-
"&"
-
表示连接, 类似c 语言中的 # 宏定义, 直接##连接参数
-
Leap MACRO COND,LAB J&COND LAB ENDM
然后我们这样调用:Leap Z,there
在展开的过程中,会变成:JZ there
-
-
"%"
-
"!"
- 表示转义字符
-
-
宏既可以在所有代码前定义, 也可以在码段中定义, 但必须遵循"先定义后引用"原则
-
同一模块不可重复定义宏 , 多模块可重复
-
宏定义可嵌套调用
栈段定义细节
栈的声明过程
- 定义栈段。
- 分配栈空间。
- 在程序的开始处设置栈段寄存器(SS)和栈指针(SP)。
草这里要特别注意, 汇编的call 函数的操作其实也是用栈来实现的, call 会进行压栈存操作数的地址的操作 , ret 会进行读取栈顶操作数跳转并退栈的操作
汇编实战某些细节:
-
再进入一个函数时, push 进去的数据一定要在函数返回前全部 pop , 不然的话一部分数据留在栈区就会导致某些函数无法正常的返回, 程序出错
-
想要直接写入 heap 区数据, 可以使用 如下方法写入
push bx mov bx , 02h mov [bx], ax pop bx
或者直接:
mov ax ,[string] ; string 为变量别名
对于add / sub 指令
add [num1] ,ax add [num1] ,01h add ax , [num1]
但是注意, 以下操作是不被允许的:
add [num2],[num1]
可以这样写入数据:
mov [num1] ,ax mov ax ,[num1]
对于 mul 指令以下操作是合法的:
mul [num1]
汇编变量
在数据段定义数据的时候可在定义前加上一个别名.
data segment
num dw 0ffffh
arr1 db 100 dup(?)
arr2 db 100 dup(?)
data ends
其中 mov ax , num
等价于 mov ax, [num]
都是指 num 对应偏移地址指向的内存
中断指令
退出程序
mov ax ,4C00H
int 21h
读入单个键盘输入到al
mov ah , 09h
int 21h
读取字符串到内存
lea dx, buffer; 内存的别名
mov ah , 0ah
int 21h
但是注意这个读入的字符串格式是 0ah data前后都有一个 $ + 0ah 换行
会持续读取字符串知道遇到回车
创建文件
mov ah , 3ch
lea dx , file1
int 21h
mov f1, ax
注意每次读入文件后会生成一个句柄并赋值给 ax , 该句柄应该保存好, file1 是文件路径
dosbox 每次挂载工作区都会清空文件夹, 所以如果要使用文件操作应该先创建文件
读入文本
mov ax , 3d00h
lea dx , file ; dx 储存文本字符串
int 21h ; 表示打开 dx 指向的文件
mov ax , 3fh
int 21h ; ax 储存文本句柄
写入文件
在DOS中断
INT 21H
中,功能号40H
通常用于写文件或设备。为了执行这一操作,需要设置一系列参数。以下是对这些参数的详细解释:1. 功能号(AH寄存器)
- 值:
40H
- 含义:指定执行写文件或设备的操作。
2. 文件句柄(BX寄存器)
- 用途:标识要写入的文件或设备。
- 设置:在执行
INT 21H
之前,需要将文件句柄加载到BX寄存器中。文件句柄通常是通过之前打开文件或设备的操作获得的。3. 数据缓冲区指针(DX寄存器)
- 用途:指向包含要写入数据的数据缓冲区。
- 设置:使用
LEA
(加载有效地址)或其他指令将数据缓冲区的起始地址加载到DX寄存器中。注意,这里的地址应该是数据缓冲区中实际数据的起始地址,而不是缓冲区的整体起始地址(如果缓冲区有头部或预留空间)。4. 写入字节数(CX寄存器)
- 用途:指定要写入文件或设备的字节数。
- 设置:在执行
INT 21H
之前,需要将要写入的字节数加载到CX寄存器中。这个值应该与数据缓冲区中的实际数据量相匹配。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了