汇编学习笔记(自用)

学习与此

程序员进阶之汇编语言从0到1---up:胖薯code

视频中的讲义

仅用于自我总结


一,寄存器的命名

1.x86

四个通用寄存器**ax bx cx dx ,**可以高版本兼容低版本

================ rax (64 bits)

======== eax (32 bits)

==== ax (16 bits)

== ah (8 bits)

== al (8 bits)
mov ax,2000h ;h结尾表示16进制,不区分大小写

;b结尾表示2进制,不区分大小写

;没有字母表示10进制

2.ARM

31个通用寄存器x0--x30

================ xn (64 bits)

======== wn (64 bits)64位寄存器中的低32位

======== rn (32 bits)
mov r0,#0x20202020A ;使用0x开头表示16进制,#是固定写法

3.MIPS

32个通用寄存器**¥0--¥31**

二,内存单元和地址

最大运算表示位数=min{cpu运算能力,地址总线宽度}

物理地址=段地址*16+偏移地址

三,栈和队列

栈和队列都属于数据存储结构,数据结构大致分为一下几种存储结构

1.线性表:顺序表,链表,栈和队列

2.树结构:普通树,二叉树,线索二叉树

3.图存储结构

  • 队列结构:先进先出
  • 栈存储结构:先进后出

栈作用:用于存储临时数据,对数据进行暂时性保护,不被复写

四,搭建x86汇编环境emu8086

EMU8086 v4.08 安装教程

五,x86汇编语法

1.注释

2.变量取值和赋值(传送指令)

;赋值

mov ax,2900h

mov bx, 4904h

;取值

mov cx,ax

mov dx,0FFFFh

16进制数据不能以字母开头,需要在前面加上0

3.函数声明

结构

函数名:

结构体

ret ;结束标志

示例:

void:

mov ax,2000h

ret

4.函数调用

call printf

注意把函数写在下面

5.字符串的定义

伪指令

db-->define byte 定义字节

dw-->define word 定义字=2字节

示例,好像单引号或是双引号都可以

db 'hello'

dw 'hello'

dw定义应该是偶数字符,否则会对应添加.在末尾

6.字符串的获取

因为bd是伪指令,不想让程序给转换成指令,所以用"end"表示程序的开始,其中end是标志,start这种是开始的自定义字符

str db 'hello'

start:

mov ax,str

end start

别名中存放的是偏移地址,还需要段地址,**段地址*16+偏移地址=实际物理地址,**别名默认从ds

(data segment)寄存器中读取段地址,但是我们没有给ds赋值过,这就导致我们无法获取正确的数据,因为我们不知道正确的段地址是多少

字符串的段地址的获取:

  • 方法一:直接从内存中找(仅限于调试,实际开发不可以)
  • 方法二:使用段进行包裹,段能给我们提供一个段地址

data segment ;data可以自定义命名

str db 'hello' ;伪指令

data ends

start2: ;程序入口,提示是指令开始的位置

mov ax,data

mov ds,ax

mov ax,str

end start2

7.对内存中的数据进行读写操作

data segment

str dw 'hello' ;如果定义多个数据,用逗号进行隔开

data ends

start:

mov ax,data

mov ds,ax

mov ax,str ;如果从内存中读取数据,是根据寄存器的大小来读取,16位的寄存器一 次性读取16位数据

end start

注意需要寄存器的位数和地址的位数匹配

ax---dw

al(16位中的低八位)---b.str //db

内存数据的读写是从低往高进行读写

比如:

\

我们写下一个简单的汇编代码

我们看到,在寄存器ax中低段位储存的是34,高段位储存的是12(H和L)

而在内存中也是低段位储存的是34,高段位储存的是12(从左向右)

如果我们想要从指定的内存地址中写入或者读数据的话,需要借助段寄存器进行实现

ds,cs,ss,es

需要通用寄存器进行中转

  • 如何从指定内存中读取数据

start:

mov ax,0710h

mov ds,ax

mov ax,ds:[0] ;实际物理地址 段地址+偏移地址==> ds:[xxx] 表示从该地址取数据

end start

  • 如何往指定内存中写入数据

start:

mov ax,0710h

mov ds,ax

mov ax,1234h

mov ds:[0],ax

end start
data segment

str dw 'he'

data ends

start :

mov ax,data

mov ds,ax

mov ax,ds:str ;str-==>[xx] ds:[xxx] 和一开始的mov ax,str是等价的

mov ds:[0],ax

end start

别名数组读写的简便方式

;将第二个字符串的o替换为e

data segment

str dw 'hello '

newstr dw 'wowowo'

data ends

code segment

start:

mov ax,data

mov ds,ax

mov al,b.str+1 ;b. 相当于一个字节,同理,w. 相当于一个字,两个字节

mov b.newstr+5,ax

end start

code ends
data segment

str dw 'hello '

newstr dw 'wowowo'

data ends

code segment

start:

mov ax,data

mov ds,ax

mov al,b.str[1] ;str 相当于str[0]

mov b.newstr[5],al

end start

code ends

8.字符串的修改和替换

data segment

str dw "he"

newstr dw "wo"

data ends

start:

mov ax,data

mov ds,ax

mov ax,ds:str

mov ds:newstr,ax

end start
data segment

str dw "hello "

newstr dw "wowowo"

data ends

start:

mov ax,data

mov ds,ax

mov ax,str

mov ds:newstr,ax

mov ax,str+2

mov ds:newstr+2,ax

mov ax,str+4

mov ds:newstr+4,ax

end start

分段写法:

data segment

str dw "hello "

data end

newdata segment

newstr dw "wowowo"

newdata end

code segment

start:

mov ax,data

mov ds,ax

mov ax,newdata

mov es,ax

mov ax,ds:str

mov es:newstr,ax

mov ax,ds:str+2

mov es:newstr+2,ax

mov ax,ds:str+4

mov es:newstr+4,ax

end code

end start

9.Loop循环指令

data segment

dw 'hello '

dw 'wowowo'

data end

start:

mov ax,data

mov ds,ax

mov cx,3 ;对应循环次数

mov bx,0

pp:

mov ax,ds:[bx]

mov ds:[bx+6],ax

add bx,2 ;bx=bx+2

loop pp

end start

进阶:

data segment

①dw 'aaaaaaa' 需要修改bx的初始值,或者当有str dw 'hello' 时,使用offset指令

在赋值的时候把str变成对应的偏移地址,而不是对应地址所存储的数据

dw 'hello '

dw 'wowowo'

data end

start:

mov ax,data

mov ds,ax

mov cx,3

mov bx,0

①mov bx,7

或者 mov bx,offset str

pp:

mov ax,ds:[bx]

mov ds:[bx+6],ax

add bx,2

loop pp

end start

加减指令运算add/sub

注意不能内存之间的修改数据

错误写法: add/sub ds:[0],ds:[6]

10.中断

相当于主线任务和支线任务

mov ah,01h

int 21h

例如: INT21h 系统功能调用 在AH中储存功能号,在AL中储存输入的数据

中断码与功能号的查询

11.字符串的输出

data segment

str db "hello world!$"

data ends

code segment

start:

mov ax,data

mov ds,ax

mov dx,offset str

mov ah,09h

int 21h

end start

code ends
data segment

str db "hello world!$"

data ends

code segment

start:

call print ;调用函数

mov al,0h

mov ah,4ch ;结束程序带返回值al

int 21h

print: ;函数定义

mov ax,data

mov ds,ax

mov dx,offset str

mov ah,09h

int 21h

ret

end start

code ends

12.除法指令DIV

被除数/除数==商......余数

  • 被除数:高16位放在DX寄存器中,低16位放在AX寄存器中
  • 除数 div 除数,一般放在BX或CX寄存器中
  • 商存放在AX寄存器中
  • 余数存放在DX寄存器中

mov dx,20 ;定义被除数高16位

mov ax,2000 ;定义被除数低16位

mov bx,300 ;定义除数

div bx

mov ds:[0],ax ;在内存中存放商

mov ds:[2],dx ;在内存中存放余数

13.乘法指令MUL

乘数X乘数=积

mul 乘数

  • AX存放第一个乘数
  • 调用mul指令 第二个乘数
  • 积的高16位存放在DX寄存器中
  • 积的低16位存放在AX寄存器中

mov ax,20

mov bx,3

mul bx

mov ds:[0],ax

mov ds:[2],dx

六.段寄存器

1.数据和指令

用段寄存器标记是指令还是数据

2.数据DS-->data segment 偏移地址bx

指令CS-->code segment 偏移地址ip

栈空间SS-->stack segment 偏移地址sp

ES-->extra segment 一般用于DS的替代

七.栈空间的操作

正常的排列方式是数据从低地址往高地址进行偏移存储,读取数据也是从低到高

栈是写入数据从高到低进行写入,读取是从低到高进行

栈存储的特点

  • 一次读写两个字节的数据
  • 数据是从高地址往低地址逆序偏移存放

栈空间的声明

data segment

str db 8,7,6,5,4,3,2,1

data ends

code segment

start:

mov ax,data

mov ss,ax

mov sp,8

code ends

end start

往栈空间中写入数据

push指令

data segment

str db 8,7,6,5,4,3,2,1

data ends

code segment

start:

mov ax,data

mov ss,ax

mov sp,8

push 2000h

code ends

end start

data segment

str db 8,7,6,5,4,3,2,1

data ends

code segment

start:

mov ax,data

mov ss,ax

mov sp,8

push 2000h

push 5522h

code ends

end start

从栈空间读取数据

POP指令

data segment

str db 8,7,6,5,4,3,2,1

data ends

code segment

start:

mov ax,data

mov ss,ax

mov sp,8

push 2000h

push 5522h

pop bx

code ends

end start

八.操控显存输出字符串

在8086的内存地址结构中,B8000h~BFFFFh 这部分的内存区域为显存区域,一旦向这个地址空间写入数据,cpu就会从0号偏移地址读取数据让后显示输出(每写入一次数据就从0号地址开始读取一次)

每个字符站两个字节的空间,前面的字节表示字符,后面的字节表示颜色

0 0 0 0 0 0 0 0 ;用8个2进制位表示字符颜色属性

从高位往地位数:

  • 第一位表示是否显示闪烁痕迹
  • 第234位代表字符背景颜色RGB
  • 第5位显示是否高亮
  • 第678位代表字符颜色RGB

字符串打印

data segment

str db 'hello world!'

endstr db ''

data ends

code segment

start:

mov ax,data

mov ds,ax

mov ax,0B800h ;只赋子段地址B800,而且因为是字母开头,前面要加0

mov es,ax

mov cx,offset endstr-str ;一种获取字符串长度的方法

mov bx,0

mov si,0

print:

mov al,ds:[si] ;注意使用al,获取8位一字节的数据

mov es:[bx],al

add bx,2

inc si ;相当于 add si,1

loop print

code ends

end start

借助字符不断刷新显示的特性,可以让字符动画显示

;让字符从左往右显示

code segment

start:

mov ax,0B800h

mov es,ax

mov cx,30

mov bx,2

print:

mov al,' '

mov es:[bx-2],al

mov al,'a'

mov es:[bx],al

add bx,2

loop print

code ends

end start

九.使用键盘控制字符的移动

  • int 16中断,键盘输入字符,

  • cmp 函数,jne和je配合使用

:用键盘控制字符移动,a61代表左移,d64代表右移,w77代表上移,:s73代表下移

code segment

start:

mov ax,0B800h

mov ds,ax

mov bx,0

scanf1:

mov ah,0h

int 16h

cmp al,61h :判断值是否相等

jne scanf2 ;jmp not epual 如果值不相等,跳转ssanf2,否则往下执行

call left

jmp scanf1

scanf2:

cmp al,64h

jne scanf3

call right

jmp scanf1

scanf3:

cmp al,77h

jne scanf4

call up

jmp scanf1

scanf4:

cmp al,73h

jne scanf1

call down

jmp scanf1

left:

mov ds:[bx],' '

mov ds:[bx-2],'a'

sub bx,2

ret

right:

mov ds:[bx],' '

mov ds:[bx+2],'a'

add bx,2

ret

up:

mov ds:[bx],' '

mov ds:[bx-160],'a'

sub bx,160

ret

down:

mov ds:[bx],' '

mov ds:[bx+160],'a'

add bx,160

ret

code ends

end start

十.转移指令

1.jmp

jmp 0100:0008h

mov ax,2222h

mov bx,3333h
code segment

jmp 0005h

mov ax,2222h

mov bx,3333h

code ends
code segment

jmp b

mov ax,2222h

mov ax,2222h

mov ax,2222h

mov ax,2222h

mov ax,2222h

b:

mov bx,3333h

code ends

2.jcxz

当cx的值是0的时候跳转,当非零的时候不跳转

code segment

jcxz b

mov ax,2222h

mov ax,2222h

mov ax,2222h

mov ax,2222h

mov ax,2222h

b:

mov bx,3333h

code ends

3.retf

retf需要配合栈进行使用,当程序执行到retf这条指令时,会连续从栈中pop两次数据,第一次的数据赋值给CS,第二次的数据赋值给IP,那么如果我们想要跳转到指定的指令,需要将该指令的段地址和偏移地址分别push进栈中

stack segment

dw 1234h

stack ends

code segment

start:

mov ax,stack

mov ss,ax

mov ax,0710H ;指定段地址

push ax

mov ax,0000H ;指定偏移地址

push ax

retf ;程序跳转到0710:0000这个位置

code ends

end start

十一.call和ret进阶

1.call和ret指令

call在执行时会先将下一条指令所对应的ip地址入栈,让后修改ip的值实现跳转,ret指令执行的时候,将ip地址pop出来进行跳转

call 偏移地址(或者是函数名,标号)

2.call Far ptr和retf指令

call Far ptr 执行时会将下一条指令做对应的cs和ip\都入栈,retf指令执行的时候,将ip和cs的值pop出来进行跳转,进行段之间的跳转

  • ret和call配套使用
  • retf和call Far ptr 配套使用

call Far ptr 偏移地址

3.call word ptr和call dword ptr

直接从内存中获取ip地址然后跳转

call word ptr ds:[0] ;ds:[0]存放ip值

直接从内存中获取cs和ip地址然后跳转

call dword ptr ds:[0] ;ds:[0]存放的是ip值, ds:[2]存放的是cs值 ;这种方式同样会将cs和ip入栈 可以配合retf使用

call指令和jmp指令的区别

  • jmp指令仅仅只是修改了cs:ip的值
  • call指令除了修改cs:ip的值之外,还将下一条指令的ip值入栈,方便ret指令跳转调用
相关推荐
月印千江6716 分钟前
从密码学原理与应用新方向到移动身份认证与实践
经验分享·笔记·其他·网络安全·密码学
bohu8324 分钟前
opencv笔记2
人工智能·笔记·opencv
万事可爱^28 分钟前
算法入门(九)—— 无监督学习介绍与K-Means实战(内附Kaggle实战源码与数据集)
人工智能·学习·算法·机器学习·kmeans
Pandaconda35 分钟前
【新人系列】Python 入门(二十七):Python 库
开发语言·笔记·后端·python·面试··python库
小菜鸟博士42 分钟前
大模型学习笔记 - 第一期 - Milvus向量数据库
数据库·笔记·学习·算法·milvus
索然无味io1 小时前
PHP基础--流程控制
前端·笔记·后端·学习·web安全·网络安全·php
Pandaconda1 小时前
【Golang 面试题】每日 3 题(三十六)
开发语言·经验分享·笔记·后端·面试·golang·go
不是吧这都有重名1 小时前
[Datawheel学习]用Llama-index创建Agent、数据库对话Agent和RAG接入Agent
数据库·学习·llama
黑客老陈1 小时前
漏洞挖掘 | Swagger UI 目录枚举小总结
运维·服务器·网络·学习·ui·php
LuckyLay2 小时前
Golang学习笔记_27——单例模式
笔记·学习·golang·单例·singleton