Hello , 我是小恒不会java。今天写写x86相关底层的东西
寄存器
8086由BIU和EU组成
8088/8086寄存器有14个。8通用,4段,1指针,1标志
8个通用寄存器:这些寄存器可以用来存储任意类型的数据,包括整数、地址等。8086有8个通用寄存器,分别是:
AX(Accumulator):累加器,用于存储计算结果和中间结果。
BX(Base Register):基址寄存器,用于存储基址。
CX(Count Register):计数寄存器,用于存储循环次数和计数。
DX(Data Register):数据寄存器,用于存储乘法和除法操作的数据。
SI(Source Index):源索引寄存器,用于存储源操作数的地址。
DI(Destination Index):目标索引寄存器,用于存储目标操作数的地址。
BP(Base Pointer):基址指针寄存器,用于存储基址指针。
SP(Stack Pointer):堆栈指针寄存器,用于存储堆栈顶部的地址。
4个段寄存器:这些寄存器用于存储内存段的地址。8086有4个段寄存器,分别是:
CS(Code Segment):代码段寄存器,用于存储代码段的地址。
DS(Data Segment):数据段寄存器,用于存储数据段的地址。
SS(Stack Segment):堆栈段寄存器,用于存储堆栈段的地址。
ES(Extra Segment):附加段寄存器,用于存储附加段的地址。
1个指针寄存器:
IP(Instruction Pointer):指令指针寄存器,用于存储下一条指令的地址。
1个标志寄存器:
FLAGS:标志寄存器,用于存储指令执行过程中产生的状态信息,如进位、溢出、零等。
寄存器具体隐式用法我在此不多赘述
物理地址
物理地址 (PA) = (CS << 4) + IP
常见指令
数据传输指令:
mov:移动数据,从一个寄存器到另一个寄存器,或从内存到寄存器,反之亦然。
push:将数据压入栈。 pop:从栈中弹出数据。
xchg:交换两个寄存器或一个寄存器和内存位置的内容。
lea:加载有效地址,通常用于计算内存偏移。
算术和逻辑指令:add:加法。
sub:减法。
mul:无符号乘法。
imul:有符号乘法。
div:无符号除法。
idiv:有符号除法。
inc:递增寄存器或内存位置的内容。
dec:递减寄存器或内存位置的内容。
neg:取负数。
and:按位与。
or:按位或。
xor:按位异或。
not:按位非。
shl/sal:左移。
shr/sar:右移(逻辑右移/算术右移)。
控制流指令:
jmp:无条件跳转。
call:调用子程序。
ret:从子程序返回。
je/jz:如果相等/为零则跳转。
jne/jnz:如果不等于/不为零则跳转。
jg/jnle:如果大于/不小于等于则跳转。
jge/jnl:如果大于等于/不小于则跳转。
jl/jnge:如果小于/不大于等于则跳转。
jle/jng:如果小于等于/不大于则跳转。
test:测试寄存器或内存位置的内容,常用于条件跳转。
字符串处理指令:
lods:从字符串中加载字节/字/双字。
stos:存储字节/字/双字到字符串。
cmps:比较字符串。 scas:扫描字符串。
输入/输出指令:
in:从端口读取数据。
out:向端口写入数据。 系统指令:
syscall:执行系统调用。
int:触发中断。
iret:从中断返回。
MOV 指令
mov
是“Move”的缩写,用于在汇编语言中执行数据传输操作,即从一个源(source)复制数据到一个目标(destination)。其基本语法是:
mov dst, src
其中:
dst
是目标操作数,可以是寄存器、内存位置(通过地址表达式指定)或其他可寻址的数据对象。src
是源操作数,同样可以是寄存器、内存位置、立即数(常数值)等。
mov
指令的主要功能是将 src
中的值直接拷贝到 dst
,实现数据的移动或赋值。例如:
mov eax, ebx ; 将 ebx 寄存器的值复制到 eax 寄存器
mov [esp], 0x1234 ; 将立即数 0x1234 存储到 esp 指向的内存位置
mov eax, [0x1000] ; 从内存地址 0x1000 处读取数据并存入 eax 寄存器
LEA 指令
lea
是“Load Effective Address”的缩写,它的功能不是直接传送数据,而是计算并获取源操作数所代表的内存地址,并将这个地址(而非地址处的数据)存放到目标寄存器中。其基本语法是:
lea dst, src
这里的 dst
仍然是目标寄存器,而 src
是一个地址表达式,通常由一个基址寄存器加上一个偏移量(可以是常数或寄存器)构成。lea
指令并不访问内存,它仅仅计算地址表达式的值并将其存入目标寄存器。
使用 lea
的典型场景包括:
- 快速计算数组元素的地址,无需额外的算术运算。
- 在不实际加载数据的情况下获取变量或数据结构成员的地址。
- 作为某种形式的算术运算(如简单的加法或乘法)的替代,特别是在地址计算中。
例如:
lea eax, [ebx + 4*ecx] ; 计算 ebx 加上 4 倍 ecx 的值所对应的地址,并将该地址存入 eax
lea edi, [ebp - 8] ; 获取当前栈帧中偏移量为 -8 的局部变量的地址,存入 edi
mov
指令用于数据的直接复制,而 lea
指令则用于获取和存储内存地址
代码片段
基本结构
DATAS SEGMENT
; 数据定义
DATAS ENDS
CODES SEGMENT
ASSUME CS:CODES, DS:DATAS
; 主程序和子程序
CODES ENDS
END START
常用结构
.MODEL SMALL
; 定义堆栈段
.STACK
; 数据段声明
.DATA
; 代码段声明
.CODE
; 程序入口点
.STARTUP
; 结束程序执行
.EXIT
; 定义子程序PRINT_STRING
; 程序结束标记
END
定义程序模式:练习的话用小内存模式
.MODEL SMALL
; 使用`MODEL SMALL`定义程序模式,指定小模式内存模型,程序段(代码和数据)不超过64KB。
定义字符串类型数据
STRING DB 'Hello World!',13,10,'$';
; 声明一个字符串变量STRING,内容为'Hello World!\r\n$'
; 其中,'\r'代表回车(Carriage Return,ASCII码13),'\n'代表换行(Line Feed,ASCII码10)
; '$'字符作为字符串结束符,某些情况下用于标识字符串的结尾
创建子程序
NAME PROC
; 子程序主体代码
; ...
; 子程序返回
RET
NAME ENDP
子程序调用
CALL NAME
计算两个数的和:
MOV AX, NUM1
ADD AX, NUM2
MOV SUM, AX
比较两个数:
MOV AX, NUM1
CMP AX, NUM2
JG GREATER
JL LESS
JE EQUAL
输出字符串到屏幕:
LEA DX, STRING;STRING为变量名
MOV AH, 9
INT 21H
读取字符:
MOV AH, 1
INT 21H
输出字符:
MOV AH, 2
MOV DL, 'A'
INT 21H
打印字符串实例
; 使用`MODEL SMALL`定义程序模式,指定小模式内存模型,程序段(代码和数据)不超过64KB。
.MODEL SMALL
; 定义堆栈段
.STACK
; 数据段声明
.DATA
; 声明一个字符串变量STRING,内容为'Hello World!\r\n$'
; 其中,'\r'代表回车(Carriage Return,ASCII码13),'\n'代表换行(Line Feed,ASCII码10)
; '$'字符作为字符串结束符,某些情况下用于标识字符串的结尾
STRING DB 'Hello World!',13,10,'$'
; 代码段声明
.CODE
; 程序入口点
.STARTUP
; 调用子程序PRINT_STRING,输出字符串
CALL PRINT_STRING
; 结束程序执行
.EXIT
; 定义子程序PRINT_STRING
PRINT_STRING PROC
; LEA (Load Effective Address)指令将STRING变量的地址加载到DX寄存器
LEA DX, STRING
; 将AH寄存器设置为9,这是DOS中断INT 21H的功能号,表示要执行"显示字符串"操作
MOV AH, 9
; 调用DOS中断INT 21H,执行显示字符串功能。此时DX寄存器保存了待显示字符串的地址
INT 21H
; 子程序返回
RET
PRINT_STRING ENDP
; 程序结束标记
END
子程序创建实例
; 使用`MODEL SMALL`定义程序模式,指定小模式内存模型,程序段(代码和数据)不超过64KB。
.MODEL SMALL
; 定义堆栈段
.STACK
; 数据段声明
.DATA
; 声明一个字符串变量STRING,内容为'Hello World!\r\n$'
; 其中,'\r'代表回车(Carriage Return,ASCII码13),'\n'代表换行(Line Feed,ASCII码10)
; '$'字符作为字符串结束符,某些情况下用于标识字符串的结尾
STRING DB 'Hello World!',13,10,'$'
; 代码段声明
.CODE
; 程序入口点
.STARTUP
; 调用子程序PRINT_STRING,输出字符串
CALL PRINT_STRING
; 结束程序执行
.EXIT
; 定义子程序PRINT_STRING
PRINT_STRING PROC
; LEA (Load Effective Address)指令将STRING变量的地址加载到DX寄存器
LEA DX, STRING
; 将AH寄存器设置为9,这是DOS中断INT 21H的功能号,表示要执行"显示字符串"操作
MOV AH, 9
; 调用DOS中断INT 21H,执行显示字符串功能。此时DX寄存器保存了待显示字符串的地址
INT 21H
; 子程序返回
RET
PRINT_STRING ENDP
; 程序结束标记
END
AH实例
AH 寄存器是 8086 汇编语言中的一个 8 位寄存器,属于 16 位寄存器 AX 的高 8 位部分
在 DOS 操作系统中,通过调用中断 21H 可以执行各种功能。这些功能由 AH 寄存器的值确定。例如,AH 寄存器的值设置为 9 表示调用 DOS 的输出字符串功能,值设置为 4CH 表示调用 DOS 的退出功能。
以下是一个简单的汇编程序示例,演示了如何使用 AH 寄存器调用 DOS 的输出字符功能:
.MODEL SMALL
.STACK
.DATA
CHAR DB 'A'
.CODE
.STARTUP
MOV AH,2 ; 设置 AH 寄存器的值为 2,表示调用 DOS 的输出字符功能
MOV DL,CHAR ; 将 CHAR 变量的值加载到 DL 寄存器
INT 21H ; 调用 DOS 中断
.EXIT
END
在这个示例中,程序首先将 AH 寄存器的值设置为 2,表示调用 DOS 的输出字符功能。然后,将 CHAR 变量的值加载到 DL 寄存器,最后使用 INT 21H 指令调用 DOS 中断。这将在屏幕上输出字符 ‘A’。
求和案例
;完整段的求3+5的和
DATAS SEGMENT
FIVE DB 5
DATAS ENDS
STACKS SEGMENT
DB 128 DUP (?)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
MOV AL,FIVE
ADD AL,3
ADD AL,30H
MOV DL,AL
MOV AH,2
INT 21H
MOV AH,4CH
INT 21H
CODES ENDS
END START
调用宏库案例
INCLUDE MACROOUT.LIB;调用库
DATAS SEGMENT
STRING DB 'Hello World',13,10,'$';定义字符串数据
DATAS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS
START:
MOV AX,DATAS
MOV DS,AX
OUTPUT STRING;使用output库内方法
;使用寄存器中断程序
MOV AH,4CH
INT 21H
CODES ENDS
END START