汇编入门
模板:
.386
data segment use16
data ends
code segment use16
assume cs:code,ds:data
main:
mov ah, 4Ch
mov al, 0
int 21h
code ends
end main
数据的表示与运算¶
-
数据的表示
-
有符号数与无符号数
-
有符号数:具有符号位,用于表示正数和负数。用补码存储。
表示范围:\([-2^{n-1},2^{n-1}-1]\),其中非负数范围为 \([0,2^{n-1}-1]\),负数范围为 \([-2^{n-1},-1]\)。
补码
-
补码定义:\(x_{补码}=\begin{cases}x&(x\geq 0)\\2^n-|x|&(x<0)\end{cases}\)
Info
-
原码:最高位为符号位,其余为数值位。
-
反码:正数反码 = 原码;负数反码 = 原码符号位不变、数值位取反。
\(x_{反码}=\begin{cases}x&(x\geq 0)\\2^n-1-|x|&(x<0)\end{cases}\)
-
补码:正数补码 = 原码;负数补码 = 反码 + 1。
\(x_{补码}=\begin{cases}x&(x\geq 0)\\2^n-|x|&(x<0)\end{cases}\)
性质:设 \(a,b>0\),\(a_{补码}+(-b)_{补码}=a+(2^n-b)\equiv a-b\pmod {2^n}\)。
因此补码不仅统一了 \(+0\) 与 \(-0\),也实现了加法与减法的统一。
-
-
由补码求真值:\(x=\begin{cases}x_{补码}&(0\leq x_{补码}<2^{n-1})\\x_{补码}-2^n&(2^{n-1}\leq x_{补码}<2^n)\end{cases}\)
-
-
无符号数:无符号位,均为数值位。
表示范围:\([0,2^n-1]\)。
-
-
十六进制常数:后缀
h或H,且若十六进制常数恰好以字母开头,需要添加0前缀(如0FFFFh相当于 C 语言的0xFFFF)。二进制常数:后缀
B。
-
-
运算
-
算术运算:
-
+:add ax,bx表示ax+=bx -
-:sub ax,bx -
*:imul(有符号),mul(无符号)单操作数(
mul,imul):数据宽度 被乘数(隐含) 乘数(显式操作) 结果 8 位乘法 AL(8 位)8 位 AX(16 位,AH:AL)16 位乘法 AX(16 位)16 位 DX:AX(32 位)32 位乘法 EAX(32 位)32 位 EDX:EAX(64 位)64 位乘法(x86-64) RAX(64 位)64 位 RDX:RAX(128 位)双操作数(
imul):imul ax,bx数据宽度 被乘数 乘数 结果 16 位 16 位寄存器 16 位 目标寄存器(16 位) 32 位 32 位寄存器 32 位 目标寄存器(32 位) 64 位(x86-64) 64 位寄存器 64 位 目标寄存器(64 位) -
/:idiv(有符号),div(无符号)数据宽度 被除数(隐含) 除数(显式操作) 商(结果) 余数(结果) 8 位除法 AX(16 位)8 位 AL(8 位)AH(8 位)16 位除法 DX:AX(32 位)16 位 AX(16 位)DX(16 位)32 位除法 EDX:EAX(64 位)32 位 EAX(32 位)EDX(32 位)64 位除法(x86-64) RDX:RAX(128 位)64 位 RAX(64 位)RDX(64 位)
-
-
关系运算:
==:je!=:jne>:有符号数jg,无符号数ja<:有符号数jl,无符号数jb>=:有符号数jge,无符号数jae<=:有符号数jle,无符号数jbe
-
逻辑运算:
&&:用两个嵌套的 if 实现。||:用两个并列的 if 实现。!
-
位运算:
&:and al,bl|:or al,bl^:xor al,bl~:not ax-
<<:shl al,1。最后一个被移动的位会被放到 CF 标志中。(与
jc配合:若 CF=1,jc会跳转) -
>>:shr al,1(无符号),sar al,1(有符号)。最后一个被移动的位会被放到 CF 标志中。 - 循环左移
rol,循环右移ror。最后一个被移动的位会在被放到另一端的同时放到 CF 标志中。
-
int21h 中断¶
| AH | 功能 | 调用参数 | 返回参数 |
|---|---|---|---|
| 00 | 程序终止 | CS=程序段前缀 | |
| 01 | 键盘输入并回显 | AL=输入字符 | |
| 02 | 显示输出 | DL=输出字符(\r → 0Dh,\n → 0Ah) | |
| 09 | 显示字符串 | DS:DX=字符串地址 | |
| 4C | 带返回码结束 | AL=返回码 |
堆栈¶
push ax:堆栈指针 -=2,再把 ax 存放到新指针指向的内存中
pop cx:把当前堆栈指针指向的内容取出保存到 cx,再堆栈指针 +=2
分支与循环¶
用跳转实现。
do-while
函数¶
写在 main 前面,用 call 调用,用 ret 返回
CPU、内存和端口¶
-
逻辑地址(段地址 : 偏移地址)
-
在 8086 中,可以采用逻辑地址间接访问物理地址。物理地址 = 段地址 × 16 + 偏移地址。其中,段地址存储在 16 位段寄存器(如
cs、ds)中,偏移地址为 16 位值。一个段可以表示的内存为 64KB,且段首地址的物理地址的 16 进制表示的个位必然是 0(段首地址 = 段地址 × 16)。
-
引用变量:
段寄存器:[偏移地址](段寄存器:[常数],段寄存器:[寄存器],段寄存器:[寄存器+常数])。段地址只能用段寄存器表示,不能用常数表示。 seg s获取变量 s 所在段的段地址,offset s获取 s 在段内的偏移地址。bx、bp、si、di均可用作偏移地址。
-
-
宽度修饰
宽度修饰词 限定变量的宽度 byte ptr8 位 word ptr16 位 dword ptr32 位
数组¶
-
数组的定义
-
字节(byte)类型:db(8 位)
char a[100] = {'\0'};→a db 100 dup(0)(dup:duplicate 重复。dup 可嵌套使用)char a[3] = {1, 2, 3};→a db 1, 2, 3char a[100];→a db 100 dup(?)(编译时默认 0)char a[] = "Hello";→a db "Hello", 0(需手动补 '\0')char a[] = "Hello\0World";→a db "Hello", 0, "World", 0char a[] = "Hello\n";→a db "Hello", 0Dh, 0Ah, 0或a db "Hello", 13, 10, 0或a db "Hello", 15Q, 12Q, 0(Q 表示八进制数)或a db "Hello", 00001101B, 00001010B, 0 -
字(word)类型:dw(16 位)
short int a[3]={0x1234, 0x5678, 0xABCD};→a dw 1234h, 5678h, 0ABCDh -
双字(double word)类型:dd(32 位)
long int a[3] = {0x100, 0x200, 0x300};→a dd 100h, 200h, 300h
-
-
数组的引用
-
地址计算规则
汇编中指针 q 的 q+i 表示:(unsigned int)q + i(偏移量直接加 i,不乘元素大小)。
b dw 1234h, 5678h, 0ABCDh,定义了 3 个字元素,每个元素占 2 字节。按小端序存储,每个元素的低字节在低地址、高字节在高地址。值 12h 34h 56h 78h 0ABh 0CDh 地址 b+1 b b+3 b+2 b+5 b+4 mov ax,b[2],b[2] 表示地址 b+2,又 ax 是 16 位寄存器,会访问 b+2(低字节),b+3(高字节),所以 ax 的值是 5678h。同理,mov ax,b[1],ax 的值是 7812h。 -
段寄存器设置
assume cs:code, ds:datamov ax, data,mov ds, ax -
元素访问
一条指令不能同时访问两个变量(如
add c[0], c[1]非法,需先将c[1]存入寄存器)。
-
显卡内存¶
-
文本模式下,显卡内存的段地址固定为 0B800h,整个屏幕的显示内容都存储在该段内。
每个字符占 2 字节:低地址字节存储字符的 ASCII 码(8 位),高地址字节存储字符的颜色属性(8 位,其中低 4 位为前景色,高 4 位为背景色。4 位表示 3 位 RGB 编码和 1 位亮度位)。
25 行 × 80 列,屏幕上的每个位置对应的显存偏移地址计算公式为:偏移地址 = (行号 × 80 + 列号) × 2。
int16h 中断¶
| AH | 功能 | 输入字符 | 返回参数 |
|---|---|---|---|
| 00 | 读取键盘输入 | 可打印字符 | AL=按键的 ASCII 码、AH=按键扫描码 |
| 上方向键 | AL=00h、AH=48h | ||
| 下方向键 | AL=00h、AH=50h | ||
| 左方向键 | AL=00h、AH=4Bh | ||
| 右方向键 | AL=00h、AH=4Dh |