王爽 汇编语言
第二章
寄存器
✅
8086cpu有14个寄存器:AX BX CX DS SI DI SP BP IP CS SS DS ES PSW。
8086cpu寄存器都是16位的。
ABCD-X通常用来存放一般性数据,被成为通用寄存器。
寄存器?X可被分为?H和?L,如AX分为AH AL,即分为AHigh ALow,前者是高八位,后者是第八位。
所谓高位和数字的高位联想,都是左边是高位。
✅
几条汇编指令:mov add,用法一样。add ax,18H; add ax,8H。
数字默认16进制。
mov ax,bx; mov ax,7。
两个操作对象位数一致,第一个位置要是变量,如mov ah,bl; mov bh, AA。
✅
物理地址即内存在统一的逻辑存储器中的地址。
内存地址空间:cpu将系统中各类存储器看作一个逻辑存储器。
物理地址=段地址*16+偏移地址,通常写作 段地址:偏移地址,如cpu指令读取开始地址CS:IP。
由于十六进制,故其实即 “段地址0+偏移地址”,由一个地址加法器合成。
段的概念:段地址固定,偏移地址连续的内存空间。故其最大范围0000~FFFF。实践发现,0-1会自动变成FFFF,FFFF+1变成0000。
✅
8086有四个段寄存器:CS DS SS ES。
CS和IP:CS叫代码 段寄存器,IP 叫指令指针寄存器,CS存段地址IP存偏移量。
cpu将从CS:IP单元开始读取一条指令并执行。
取指执行过程:从CS:IP读取指令,该指令进入指令缓冲器;IP=IP+该指令长度,从而指向下一指令首单元;执行指令;从CS:IP读取指令…….
修改CS IP命令——jmp jmp 段地址:偏移地址 段地址修改CS 偏移地址修改IP jmp “四位数” 仅修改IP jmp 某合法寄存器 仅修改IP,IP=某合法寄存器的值
第三章
寄存器(内存访问)
✅
- (⊙﹏⊙)
第四章
第一个程序
✅
- 源程序写出到执行简要过程,后文有实例:
- 编写源程序 —— 写一个文本文件 —— 使用edit、笔记本等。
- 编译连接 —— 用汇编语言编译程序对文本文件编译产生目标文件;再用连接程序对目标文件进行连接,生成可执行文件。
- 在操作系统中执行文件。
✅
- 源程序简单结构
assume 寄存器:某segment段, 寄存器:某segment段, 寄存器:某segment段
XXX segment
:
:
:
XXX ends
YYY segment
:
mov ax,4c00H
int 21H
YYY ends
end- 伪指令:提供给编译器的,编译器根据伪指令进行编译工作。上面代码中assume,XXX segment……XXX ends 和 end。
- 标号,如segment段的名称 上面代码中的XXX、YYY,一个标号指代一个地址。
- 程序返回:将CPU的控制权还给 使他得以运行的程序(shell)。代码中的 mov ax,4C00H \n int 21H 。
✅
实例
编写源程序 svg
桌面右键新建txt命名123,记事本里写好源程序。
编译 svg
- win+R 输入command打开命令提示符,输入“cd masm”(masm.exe所在目录),输入“masm”启动masm.exe。
- 提示我们输入源文件名,txt文件放在了c:\asm,且不是asm文件,输入c:\asm\123.txt。(注意此处的斜杠是和\n\t一样的)
- 询问生成obj文件名,我们选择默认,回车即可。
- 提示输入列表文件名,该文件为编译为目标文件的中间结果,我们不生成,回车即可。
- 提示输入交叉引用文件名,也是中间结果,不生成,回车即可。
- 无错误则masm.exe所在目录下生成123.obj
连接 svg
- 进入DOS方式(win+R 输command),进入c:/masm (Overlay Linker3.60,link.exe所在目录),输入link启动link.exe。
- 提示输入目标文件名,[.OBJ]提示默认扩展名为obj,正合我意,输入123即可。
- 提示生成的exe文件名,默认名就行,回车即可。
- 提示输入映像文件名,提示输入库文件名,我们都回车即可。
- 无error则当前目录下生成123.exe。
执行,命令行输入文件名就行了,masm.exe 或 masm 123.exe 或 123 。
debug跟踪程序的运行过程:debug 可以将程序载入内存,设置CS:IP指向程序的入口,但debug不放弃cpu的控制权
- 具体方法:debug 123.exe svg
- 运用第二章的debug的使用方法
- R命令查看、改变寄存器内容
- D查看内存中内容
- E改写内存内容
- U将内存中机器指令(01…)翻译以为汇编命令(mov…)
- T执行一条机器指令
- A以汇编指令格式往内存写入机器指令
- P正常结束程序。
- 实操如下图: svg
✅
- exe执行过程与shell
- DOS中,123.exe若要运行,要有一个正在运行的程序p2将其载入内存,123.exe运行完后即将cpu控制权交还程序p2。 p2即command.com,被成为命令解释器,即DOS系统的shell。输入cd、dir、type等都由command执行。
- 提示符"c:\masm“后输入123.exe,此时command.com运行。 输入123.3exe后,command根据文件名找到该可执行文件,将程序载入内存,设置CS:IP指向程序入口,随后执行程序。 执行完回到command。
- dos中.exe的加载过程(上面第二步——载入内存) 找到SA:0000空闲内存区——>前256即偏移地址0-255创程序段前缀(PSP),DOS要用它和.exe通信——>后面装入程序,即从SA+10H:0000——>CS:IP=SA+10H:0000 加载.svg
✅
- 习题 习题.svg
第五、六章
☝内存单元 、"()"、 loop 和 idata
[bx]
类似[0],[bx]表示 ds:bx上的字节单元或字单元的内存内容,bx内的4位作为偏移地址
[0] [bx] mov ax, [0] 将地址为ds:0 的内存字单元存入ax mov ax, [bx] 将地址为ds:bx的内存单元存入ax mov al, [0] 将地址为ds:0 的内存半字单元存入al mov al, [bx] 将地址为ds:bx的内存半字单元存入al - 实例
问题5.1.svg
忘了要写啥了“()",机组中“(地址)”——描述某地址上的内容
()中的元素有三种类型: 寄存器名,段寄存器名 和 五位十六进制的物理地址
如 (ax)、(al)、(ds)、(20000H)、**((ds)*16+(bx))**注意里面的ds、bx都有括号
ax=10000H,则(ax)=(10000H)即地址10000H上的字节单元或字单元
loop 类似“goto”
| |
首先(cx)=(cx)-1,然后判断(cx)是否等于0,如果等于0则执行下一指令,如果不等于0则跳转到s处执行。
| loop 可能情境 |
|---|
| cx初始化8,循环8次后继续执行 |
| cx为人为初始化,然后为0,(cx)=(cx)-1变成大数(如65535) |
| cx设置指令在循环体内,死循环 |
idata
约定符号好idata表示常量,mov ax,[idata]代表mov ax,[1]、mov ax,[2]等。
又如 mov ds,idata 都是非法指令
☝实验四 程序
编程,向内存0:2000:23F依次传送数据063(3FH),程序中只能使用9条指令,9条指令中包括‘mov ax,4c00h’和‘int 21h’。
在常见的x86汇编语言(如MASM、NASM)中,分号 ; 是标准的行注释符号。
assume cs:codesg
codesg segment
mov ax,020H
mov ds,ax ; 初始化ds,不能直接将数字传入段寄存器
mov bx,0H ; 初始化bx因为从0
mov cx,40H ; cx=40H即循环64次存入数据0~63
s: ; 标记loop跳转位置
mov [bx],bl ; 内存0:200~0:23F是40H个字节数据,要存入字节数据,bl存入cs:bx的字节单元,因为要对应bl的大小是1字节
inc bx ; 把bx+=1 移动偏移地址
loop s ; 减小cx, 检查判断是否跳转
mov ax,4c00H
int 21H ; 结束
codesg ends
end☝实验五倒二题
编写code段中的代码,将a段和b段中的数据依次相加,将结果保存到c段中。
assume cs:code
a segment
db 1,2,3,4,5,6,7,8 ; a 原始数据
a ends
b segment
db 1,2,3,4,5,6,7,8 ; b 原始数据
b ends
c segment
db 0,0,0,0,0,0,0,0 ; c 结果存储区
c ends
code segment
start:
mov bx,0 ; 初始化偏移,从0开始
mov cx,8 ; 设置好循环次数
s:
mov dx,a
mov ds,dx ; ds=a
mov al,[bx] ; al=a[bx]
mov dx,b
mov ds,dx ; ds=b
add al,[bx] ; al=
mov dx,c
mov ds,dx ; ds=c
mov [bx],al ; c[bx]=al=a[bx]+b[bx]
inc bx ;bx++
loop s ;跳转
mov ax,4c00h
int 21h ; 程序退出
code ends
end start # 指明程序的开始结尾,start处设置cs=start- 创建txt,将上述代码写入,保存。
- win+R进入DOS,输入"cd masm5",再输入"masm"启动。
- 源文件输入 “file_name.txt” 然后四个回车
- 没有error后,输入"link",obj文件输入file_name,然后回车回车回车
- 输入"debug file_name.exe",进入对该exe的debug模式
- 输入"u"查看当前能看到的指令,看不到"mov ax,4c00H"就输入t执行一条指令,输入个三四个再输入"u"查看,总之看到结束指令的偏移地址bx
- 输入"g bx",直接执行到bx处
- 输入"r",查看DS,因为它是c段的首地址,因为最后一次设置DS就是用的c
- 实验中ds=0c25,输入“d 0c25:0"便能查看c处的值是不是2 4 6 8…,或者输入"d 0c23:0"能对比a、b、c段的内容。
- db、dw:定义数据,db即define bytes,dw即define words,
☝实验五最后一题
编写code段代码,用push指令将a段中的前8个字型数据,逆序存储到b段。
assume cs:code
a segment ; 数据段a
dw 1,2,3,4,5,6,7,8,0ah,0bh,0ch,0dh,0fh,0ffh
a ends
b segment ; 数据段b
dw 0,0,0,0,0,0,0,0
b ends
code segment
start:
mov ax, a ; 赋值ds为a段的段地址
mov ds, ax
mov ax, b ; 赋值堆栈段ss为b段的段地址
mov ss, ax
mov sp,10h ;偏移到堆栈最后的字型数据后,8个字,16个字节,0c24:0~0c24:0f,ss:sp指向0c24:10,即前者加一。
mov bx, 0 ; 初始化偏移量
mov cx, 8 ; 循环次数
s:
push [bx] ; 将a段第i个字型数据存入b段倒数第i个字单元内
add bx, 2h ; 按字 (word) 处理数据,故+2
loop s
mov ax, 4c00h ; 退出程序
int 21h
code ends
end start读代码代码后思考:各种段是如何安排的,怎么让CPU会把dode段当作代码段
❌
code data stack等段名如何设置只是为了方便阅读,不能起到修改cs ds ss的作用
assume cs:code,ds:data,ss:stack 也不能
✅
程序末尾end start可以设置程序入口CS:IP
✅
mov ax,stack
mov ss,ax
mov sp,20h
设置堆栈段,stacK是你设置的作为堆栈段的段名,可以是a、c what ever you want 符合一定命名规范即可。
✅
mov ax,data
mov ds,ax
设置数据段
每个段势必占据N行,即:
前一个段最后一个字节单元为所在行被前一个段占有,当前段从下一行开始,每行16字节。
前一个段最后一个字节单元 a:b,则当前段的第一个字节单元为a+[b/10h]+1:0,[ ]取整数,10h十六个字节
a:b=2:34h a+[34h/10h]+1:0=2+3+1:0=6:0
第七、八章
assume cs:codesg
data segment
db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
db '1984','1985','1986','1987','1988','1989'
dd 16,22,345,4567,6475,4567,12345,34526,65734,465436,234234
dd 123345,463654,867887,1567689
dw 4,13,76,89,99,345,567,789,890,1234,5467
dw 6789,12345,14357,16776
data ends
table segment
db 15 dup ('year summ ne ?? ')
table ends
codesg segment
start:
;set sreg (segment register)
mov ax,data
mov ds,ax
mov ax,table
mov es,ax
;set reg (register)
mov bx,0 ;bx add 4 bytes
mov si,0 ;si add 2 bytes
mov bp,0 ;bp add 16 bytes
mov cx,0fh
s:
;year
mov ax,[bx] ;30th row
mov es:[bp+0],ax
mov ax,2[bx]
mov es:[bp+2],ax
;sum of money
mov ax,60[bx] ;low -> ax
mov es:[bp+5],ax
mov dx,62[bx] ;high -> dx
mov es:[bp+7],dx
;n of employee
mov di,120[si]
mov es:[bp+10],di
;?? —— average money
div di
mov es:[bp+13],ax
add bx,4h
add bp,10h
add si,2h
loop s
mov ax,4c00h
int 21h
codesg ends
end start第九章(转移指令原理)
offset
获得标号的偏移地址
大小为一个字
性质相当于表示IP的idata
jmp
标号
- jmp short s 短转移
- jmp near s 近转移
- jmp far s 远转移
寄存器/idata
jmp reg/一字idata
jmp sreg:reg/一字idata:一字idata
内存单元
jmp word ptr 内存地址
jmp dword ptr 内存地址
jcxz
jump if (cx) == zero
等价于if((cx)==0) jmp short 标号 短转移
loop
短转移类型
(cx)–
if((cx)!=0)jmp short 标号
实验8
实验9
第十章 CALL 和 RET指令
ret、retf
ret —— return retf —— return far
ret 出栈字单元 放入IP
相当于
pop IPretf 第一次出栈 字单元 放入 IP,第二次出栈 字单元 放入CS,相当于
pop IPpop CS栈里,CS总在IP底下
| | | IP | | CS | |——-|
结合上地址来说,CS总在IP的右边(高地址),因为sp压栈会从高到低走。
IP CS ↑ sp
出栈、压栈总是一个字单元
实现从1000:0000处开始执行指令,谁知道1000:0000处有啥。
assume cs:code stack segment db 16 dup (0) stack ends code segment start: mov ax, stack mov ss, ax mov sp, 16 mov ax, 1000h push ax mov ax, 00h push ax retf code ends
call
标号型
call s (s是标号)(位移转移),将当前IP压入栈,然后近转移到s,相当于
push IPjmp near ptr s(标号)1000:3处指令为
call s,s处内容为s:pop ax执行完这两条指令后,ax数值为 3
call far ptr s(标号),先将CS压入栈,再将IP压栈,然后远转移到s,相当于
push CSpush IPjmp far ptr s(标号)
寄存器型
call 16位reg,压栈IP,IP=(reg)
push IP
jmp 16位reg
内存单元型
call word ptr 内存单元地址,地址取出内容大小为字单元,仅压栈IP,相当于
push IPjmp word ptr 如es:[bx]call dword ptr 内存单元地址,地址取出内容大小为双字,先压栈CS再压栈IP,相当于
push CSpush IPjmp dword ptr 如es:[bx]
call和ret实现模块化
assume cs:code
stack segment
db 8 dup (0)
db 8 dup (0)
stack ends
code segment
start:
mov ax, stack
mov ss, ax
mov sp, 16
mov ax, 1000h
call s
mov ax, 4c00h
int 21h
s:
add ax,ax
ret
code ends
end startmul指令
实验10
1.显示字符串
我的错误❌:
- 外部的dl为3表示第三列,对应字节时要乘二,第6个字符
- jcxz结合jmp使用,不要结合loop使用,loop会改变cx!,先减cx1再判断cx值
assume cs:code
data segment
db 'Welcome to masm!',0
data ends
code segment
start:
mov dh, 8h
mov dl, 3h
mov cl, 2h
mov ax, data
mov ds, ax
mov si, 0
call show_str
mov ax, 4c00h
int 21h
show_str:
;--------------------以上为给定,以下为作答-------------------
;===set print sreg===
mov ax, 0b800h
mov es, ax
;===set print reg===
;di=a0h*dh+dl*2
;a0h*dh
mov al, 0a0h
mul dh
;+2*dl
mov dh, 00h
add ax, dx
add ax, dx
mov di, ax
;===print to es:di===
;save cl
mov bl, cl
s:
;check if end
mov cl, ds:[si]
mov ch, 0h
jcxz fin
mov es:[di], cl
mov es:[di+1], bl
add di, 02h
add si, 01h
jmp s
fin:
ret
code ends
end start