汇编笔记(持续更新中)

汇编笔记


寄存器register

​ 学习汇编语言,首先必须了解两个知识点:寄存器和内存模型。

​ 先来看寄存器。CPU 本身只负责运算,不负责储存数据。数据一般都储存在内存之中,CPU 要用的时候就去内存读写数据。但是,CPU 的运算速度远高于内存的读写速度,为了避免被拖慢,CPU 都自带一级缓存和二级缓存。基本上,CPU 缓存可以看作是读写速度较快的内存。

​ 但是,CPU 缓存还是不够快,另外数据在缓存里面的地址是不固定的,CPU 每次读写都要寻址也会拖慢速度。因此,除了缓存之外,CPU 还自带了寄存器(register),用来储存最常用的数据。也就是说,那些最频繁读写的数据(比如循环变量),都会放在寄存器里面,CPU 优先读写寄存器,再由寄存器跟内存交换数据。


内存模型:Heap

  • 程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用malloc命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址0x1000开始给他分配,一直分配到地址0x100A,如果再要求得到22个字节,那么就分配到0x1020。

  • 这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收

img


内存模型: Stack

  • 除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域

  • 系统开始执行main函数时,会为它在内存里面建立一个帧(frame),所有main的内部变量(比如a和b)都保存在这个帧里面。main函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

  • 在函数内部在调用一个函数process()的时候,系统也会为该函数再新建一个帧,一般来说调用栈有多少层,就有多少帧。

  • 当函数运行结束之后,它的帧会被回收,系统回到函数中断执行的地方继续执行上一个帧的流程。通过这个机制,可实现函数的层层调用,且每一层函数都可以使用自己的本地变量

  • Stack是由内存区域的结束地址开始分配,从高位到地位分配

img


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个
如何确定偏移地址的值
  • 与数据有关的十种方式,
    1. 立即寻址
    2. 寄存器寻址
    3. 直接寻址
    4. 寄存器间接寻址
    5. 寄存器相对寻址
    6. 基址变址寻址
    7. 相对基址变址寻址
    8. 比例变址寻址
    9. 基址比例变址寻址
    10. 相对基址比例变址寻址
  • 与转移地址有关的4种寻址方式
    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指令改变

直接寻址方式

  • 指令所要的操作数存放在内存中,在指令中直接给出该操作数的有效地址

  • 物理地址=(DS)×16d+EA

    • MOV AX , [2000H]

    • MOV AX, VALUE

    • MOV AX , [VALUE]

  • 物理地址=(ES)×16d+EA

    • 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 为上文写到的标志)
  • 中断指令

    • 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寄存器中,但不访问该地址处的内存内容。




段地址和偏移地址

​ 段地址*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 相加构成偏移地址

  • 寄存器相加再加上立即数也可以构成合法的偏移地址

img

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,其余位为该数的绝对值的二进制表示。
    2. 反码‌:正数的反码与原码相同,负数的反码是除符号位外其他位取反。
    3. 补码‌:正数的补码与原码相同,负数的补码是其反码加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 [偏移地址]} / {标号}
        

    存储单位

  1. db : define byte 8 位, 1 字节

  2. dw : define word 16 位

  3. dd : define dword 32 位

  4. 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 位置的元素


汇编应用

  1. 输出字符串:
    	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 
  1. 从键盘输入并接受单个字符

        mov ah,01h
        int 21h
        

​ 注意, 这里会把字符串读取并输入到 al 中, al 就是读取的字符的asc码形式


宏定义

  1.      宏定义类型
         宏名 MACRO [参数列表]
             ; 宏体
       ENDM
    
  2. 宏操作符

    1. "&"

      1. 表示连接, 类似c 语言中的 # 宏定义, 直接##连接参数

      2. Leap	MACRO  COND,LAB
        		J&COND LAB
        		ENDM
        

        然后我们这样调用:Leap Z,there
        在展开的过程中,会变成:JZ there

    2. "%"

    3. "!"

      1. 表示转义字符
  3. 宏既可以在所有代码前定义, 也可以在码段中定义, 但必须遵循"先定义后引用"原则

  4. 同一模块不可重复定义宏 , 多模块可重复

  5. 宏定义可嵌套调用

    栈段定义细节

栈的声明过程

  1. 定义栈段。
  2. 分配栈空间。
  3. 在程序的开始处设置栈段寄存器(SS)和栈指针(SP)。

草这里要特别注意, 汇编的call 函数的操作其实也是用栈来实现的, call 会进行压栈存操作数的地址的操作 , ret 会进行读取栈顶操作数跳转并退栈的操作


汇编实战某些细节:

  1. 再进入一个函数时, push 进去的数据一定要在函数返回前全部 pop , 不然的话一部分数据留在栈区就会导致某些函数无法正常的返回, 程序出错

  2. 想要直接写入 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寄存器中。这个值应该与数据缓冲区中的实际数据量相匹配。
posted @   -风间琉璃-  阅读(162)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示