王爽汇编笔记

 

第一章

内存地址空间的地址段分配

  • 地址:0~7FFFH的32kb空间为主随机存储器的地址空间
  • 地址:8000~9FFFH的8kb空间为显存地址空间
  • 地址:A000~FFFFH的24kb空间为各个rom的地址空间

 

第二章

进入DOS模式

  1. 重新启动计算机,进入DOS模式,此时进入的是实模式的DOS
  2. 在Windows中进入DOS方式,此时进入的是虚拟8086模式的DOS

 

使用命令

  • -R 查看寄存器内容
  1. -R 寄存器 可以修改寄存器的内容
  • -D查看内存中的内容
  1. –D 段地址:偏移地址 输入后会从该物理地址处开始,显示128个内存单元内容,
  2. 使用了一次D命令后,接着使用时,会显示接下来的内存地址中的内容
  3. 指定D命令查看的范围 D 段地址:其实偏移地址   结尾偏移地址
  4. 查看某一内存单元内容  D 段地址:偏移地址 0
  • E命令改写内存中的内容
  1. E 段地址:偏移地址  数据(用空格段隔)
  2. E 段地址:偏移地址 enter 数据 空格 数据… 输入完成回车就可以停止修改内存数据
  3. 不能直接写入汇编指令,但是可以以十六进制的形式写入数据
  • U命令查看内存中机器码的含义 
  1. U 段地址:偏移地址 格式就可以查看内存中的指令
  • T命令执行内存中的机器码
  1. 执行的是当前SC:IP指向的内容,如果想要执行你指定的代码,需要先通过使用R命令来修改寄存器CS、IP中的值
  • A命令写入汇编代码        
  • 命令中使用段寄存器 eg d ds:0 

 

疑问:

  • jmp ax指令

在书上说jmp ax指令相当于是  mov ip,ax 在实际的实践过程中发现的确是这个样子的,当IP改变,也就相当于是地址发生了变化,当前执行的指令就指向了[ax]

  • 下面3条指令执行后,CPU几次修改IP?都在什么时候?最后IP值是多少?
mov ax,bx    ;1,4
sub ax,ax    ;2
jmp ax       ;3

 

要知道CPU修改几次IP就要先明白CPU什么时候会修改IP,CPU是在将指令读取到缓冲区的时候改变IP的,而不是执行一条指令而改变IP的。 所以COU修改4次IP,修改顺序在注释中列出了,最后IP值为0

 

第三章

 

关于栈:

  • LIFO(Last In First Out,先进先出)
  • CS:IP中存放当前指令的段地址和偏移地址
  • SS:SP 任意时刻指向栈顶元素
  • 8086CPU的入栈和出栈都是以字为单位
  • 栈顶从高地址像低地址方向增长
  • 栈空时,SS:SP指向栈空间最高地址单元的下一个单元
  • push、pop等操作指令,修改的只是SP。所以栈顶的变化范围在0000~FFFFH      

 

push ax执行过程

  • SP=SP-2 ,SS:SP 指向当前栈顶前面的单元,以当前栈顶前一个单元为新的栈顶
  • 将ax中的内容送入SS:SP指向的内存单元处

 

pop ax 出栈执行过程

  • 将SS:SP指向的内存单元的数据送入ax
  • SP=SP+2

 

pop/push指令格式

push/pop 寄存器

push/pop  段寄存器

push/pop  内存单元(一个内存字单元)

 

关于段

  • 对于数据段,默认段地址DS,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当做数据来访问
  • 对于代码段,默认段地址CS,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令
  • 对于栈段,默认段地址SS,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push、pop指令等,就将我们定义的栈段当做栈空间来用
  • 一段内存,可以是代码的存储空间,可以是数据的存储空间,还可以是栈空间,也可以什么也不是。

 

一些概念

  • 内存单元--在内存中存储时,由于内存单元是字节单元(一个单元存放一个字节),则一个字要用两个地址连续的内存单元来存放。

地址单元:将起始地址为N的字单元简称为N地址字单元

问题

如果将10000H~1FFFFH这段空间当做栈段,初始状态为空,此时SS=1000H,SP=?

SP = 0 不管怎么样,我们只要记住当栈为空时,栈顶是指向栈的最后一个内存字单元的下一个内存字单元的

 

关于寄存器                      

  • 通用寄存器,ax,bx,cx,dx
  • 段寄存器cs,ds,es,ss,段寄存器不能直接复制,需要通过其他寄存器作为辅助
  • ip指针寄存器,使用Jmp指令可以指定修改他的值 

 

第四章

 

伪指令

  • 定义一个段

段名 segment

段名 ends

  • end

汇编程序的结束标记

  • assume

将有特定用途的段和相关的段寄存器关联起来,比如我们在使用 codesg segment . . . codesg ends 时(指定该段为代码段),需要先用assume来关联一下 eg assume cs:codesg

 

源程序与程序

源程序是指由指令和伪指令构成(伪指令由编译器处理)

程序是指源程序中最终由计算机执行、处理的指令或数据

 

程序返回

在一个程序执行完后,需要将CPU控制权限交还给CPU,不然被暂停了的程序不能被执行

mov ax,4c00H
int 21H

 

编译连接:

使用masm5.0    链接:http://pan.baidu.com/s/1gdg2Bwn 密码:os0e

编译

  1. 进入masm.exe的目录  cd 目录 (运行masm后,会先显示masm的版本信息,然后提示输入将要被编译的源程序文件的名称。如果要编译的文件名的后缀名为.asm,则只需要输入文件名就可以了,如果需要编译的文件的后缀名不是.asm就需要输入完整的文件名+后缀,同时,一定要输入文件的路径。)
  2. Object filename[*.obj]:这里你可以选择直接enter,不修改名字和obj文件的存储路径。当然你也可以修改。如果不修改文件名,默认适合你代码名一样,不修改路径名,默认新生成的obj文件是存储在masm.exe所在的路径中
  3. Source listing [NUL.LIST]: 提示输入列表文件名称,这个是编译器将源程序编译为目标文件的过程中产生的中间结果,可以忽略直接enter
  4. Cross-reference[NUL.CRF]:提示输入交叉引用文件名称,同样也是中间结果,可以直接忽略按enter

因为连接的方法是和编译的步骤一样。需要注意的也是一样,所以就不在重复。

 

提示:

  • 如果出现unable to openfile 错误,最好的解决办法就是将你存放的路径名修改为英文的,简单一点。
  • 在连接的时候会出现一个警告, No stack segment,在这里我们可以不理会这个错误
  • 在编译连接的过程中,因为中间产物都是尅忽略掉的,所以,这里提供了一个小技巧,可以直接忽略掉中间产物,而不需要我们一步一步的输入,就是在命令后面添加;号 eg masm c:/1;

汇编程序从写成到执行过程

编程→1.asm→编译→1.obj→连接→1.exe→加载→内存中的文件→运行

(edit)          masm          link             command              CPU

 

DOS系统中.EXE文件中的程序的加载过程

  • 找到一段起始地址为SA:0000(即起始地址的偏移地址为0的容量足够的空闲内存区
  • 在这段内存区的钱256个字节中,创建一个称为段前缀(PSP)的数据区,DOS利用PSP来和被加载程序进行通信
  • 从这段内存区的256字节处开始(在PSP后面),将程序状图,程序的地址呗设为SA+10H:0(为了区分PSP和程序,DOS将他们划分好不同的段。所以PSP区→SA:0  程序区→SA+10H:0
  • 将该内存区的段地址存入ds中(ds=sa)后,设置cs:ip指向程序入口。

换个角度来看就是知道了DS就可以很好的定位到程序代码段了

 

注意

  • 在使用DOS单步执行的时候,执行 int 21H需要使用P命令
  • 退出debug使用Q命令

 

第五章

[bx]和内存单元

内存单元:

完整描述一个内存单元需要两种信息

  1. 内存单元的地址
  2. 内存单元的长度(类型)

eg:[0]

用[0]表示内存单元,0表示单元的偏移地址,段地址默认在ds中,单元长度(类型)可以由具体指令中的其他指令中的其他操作对象(比如寄存器)指出

[bx]:

同样表示一个内存单元,他的偏移地址在bx中,段地址默认在ds中

 

()符号:

用()表示一个寄存器或内存单元中的内容,()中的元素可以有3中

  1. 寄存器名
  2. 段寄存器名
  3. 内存单元

 

loop符号:

CPU在执行loop指令时,会有两个步骤

  1. (cx)=(cx)-1(简单的说就是用来存放循环次数)
  2. 判断cx中的值是否为0,如果不为0 转至标号处执行程序,如果为0向下继续执行(简单的就是为0 就不循环,不为了就继续循环)

loop实例:

计算2^12

assume cs:codesg
codesg segment
    mov ax,2
    mov cx,11
s:  add ax,ax
    loop s
    
    mov ax,4c00H
    int 21H
codesg ends
end

 

计算ffff:0006单元中的数乘以3 记过存储在dx中  11

assume cs:codesg
 
codesg segment
 
//这里写错了,把[]与()给弄混了,()只是一个描述性符号,在汇编指令中并不存在。[]符号,是表示间接寻址,是表示的偏移地址。需要和段地址来结合使用
 
//  mov  al,[ffff6]  ;因为是ffff:0006单元,一个单元只占一个字节。所以需要用一个8位寄存器来存储。所以这里用到的是al
 
    mov ax,ffff
 
    mov ds,ax
 
    mov ah,0        ;清零ah
 
    mov cx,3
 
    mov al,[6]
 
   mov dx,0
 
s  add dx,al
 
    loop
 
    mov ax,4c00H
 
    int 21h
 
codesg ends
 
end
 

 

 

DeBug和汇编编译器masm对指令的不同处理

指令 Debug 汇编编译器
mov ax,[0] mov ax,[0] mov ax,0

从表中可以看出DeBug将[idata]解释为内存单元,idata就是内存单元的偏移地址,而编译器将[idata]解释为立即数,那么如何实现在汇编编译器中将内存单元中的数据送入寄存器中?

解决方法:

1.将偏移地址送入bx寄存器中,用[bx]的方式来访问内存单元

([bx]:默认偏段地址是ds)

eg:

mov ax,2000h
moc ds,ax
mov bx,0
mov al,[bx]

2.明确给出段地址

mov al,ds:[0]

3.前两者的结合

mov ds:[bx]

 

解决8位寄存器不够用问题

当将12个8位数放入dx中时,因为不能直接给dx赋值,因为运算对象类型不匹配,也不能通过直接用dl累加,因为会越界所以,可以用下面的方法解决这两个问题

mov al,[bx]
mov ah,0
add dx,ax

 

 

注意

  • 在汇编源程序中,数据不能以字母开头,所以如果数据是A000H,则需要写成0A000H
  • 在调试循环的时候,如果循环次数太多,可用p命令一次执行完整个循环。或者也可使用g命令,g命令格式是 g 偏移地址
  • 段前缀: ds,cs,ss,es
  • 运行在CPU实模式下的DOS,没有能力去对硬件系统进行全面、严格的管理
  • DOS方式下,一般情况,0:200~0:2ff空间中没有系统或其他程序的数据或代码

 

第六章 包含多个段的程序

 

注意:

  • 我们若要CPU从何处开始执行程序,只要在源程序中用"end 标号"指明
  • assume cs:code 这样做后并不带表cs就指向了code,assume只是一个伪指令,是有编译器执行的,也是仅在源程序中的信息,CPU并不知道他们。assume只是用来定义的具有一定用途的段和相关寄存器联系起来
  • 对于命名为code的段,它并不是被当初了code,而只是方便我们阅读

 

第七章 更灵活的定位内存地址的方法

 

指令:

  1. and指令,逻辑与指令,安位进行与运算(二同一为1)
  2. or指令,逻辑或指令,按位进行或运算(有1则为1)

 

大小写转换

因为大写字母和小写字母的差别在于第6位,第6位如果为0则是大写,第六位如果为1则是小写,那么大小写转换的时候,就只需要控制好第6位就可以了。

转换成大写,即第六位为0  and 11011111

转换成小写,即第六位为1   or  00100000

 

[bx+idata]

  • [bx+0],[bx+2]…
  • 0[bx],2[bx]

上下两个是等价的

 

si,di

  • si,di是8086CPU中和bx功能相近的寄存器,si,di不能够分成两个8位寄存器来使用。

[bx+si],[bx+di]

  • [bx+si]
  • [bx][si]

以上两条指令等价

 

[bx+si/di+idata]

  • mov ax,[bx+200+si]
  • mov ax,200[bx][si]
  • mov ax,[bx].200[si]
  • mov ax,[bx][si].200

注意:

  • 不存在mov [di],[si]这样的指令,只能通过ax,等来作为中间的桥梁
  • 使用双重循环是,注意cx的值的设置,可以用栈来保存数据,也可以用其他的地址来保存数据,但是最好使用第一种方法

 

第八章 数据处理的两个基本问题

 

reg:ax,bx,cx,dx,ah,al,bh,bl,ch,cl,dh,dl,sp,bp,si,di

sreg:ds,ss,cs,es

 

内存单元寻址

  • 只有bx,bp,si,di这四个寄存器有寻址功能,其他没有
  • bx-bp,si-di是相对应的,良良不能同时使用
  • 只要在[..]中填的是bp,那么段寄存器默认是ss

 

 

汇编指令 指令执行前数据的位置
mov bx,[0] 内存,ds:0单元中
mov bx,ax CPU内部,ax寄存器
mov bx,1 CPU内部,指令缓冲器

 

指令执行前,它将要处理的数据所在位置

  • CPU内部
  • 内存
  • 端口

 

div指令

  • 除数:有8位和16位两种,在一个reg或内存单元中
  • 被除数:默认放在ax或ax和dx中,如果除数为16位,被除数则为32位。在dx,ax中存放,dx存放高16位,ax存低16位 ,如果除数为8位,被除数为16位,默认放在ax中
  • 结果:如果除数为8位,则al存商,ah存余,如果除数为16位,则ax存商,dx存余

 

第九章  转移指令的原理

 

8086CPU的转移指令分类

  • 无条件转移指令(jmp)
  • 条件转移指令
  • 循环指令
  • 过程
  • 中断

 

offset指令:取得标号的偏移地址

jmp指令:无条件跳转指令,可以只修改ip,也可以同时修改ip和cs,在使用jmp的时候,需要给出下面两种信息

  • 转移的目的地址
  • 转移的距离(段间转移、段内短转移、段内近转移)

 

jmp short 标号(转到标号处执行指令)  ip修改范围 -128~127 段内短转移

jmp near ptr 标号 -32768~32767  段内近转移

jmp far ptr 标号 段间转移,又称为远转移

jmp word ptr 内存地址单元   

jmp dword ptr 内存单元地址  cs = (内存单元地址+2),ip=(内存单元地址)

 

jcxz指令:条件转移指令,所有的条件转移指令都是短转移

条件,如果cx=0,则跳转到标号处执行

 

loop指令

循环指令,所有的循环指令都为短指  cx=cx-1

 

实验9

assume cs:code,ds:data
data segment
    db ‘Welcome to asm! ‘
data ends
 
code segment
start:    mov ax,data
        mov ds,ax
        
        
        mov ax,0B800h
        mov es,ax
        mov bx,0
        mov si,0
 
        
 
    s:    mov cx,16
        
        mov al,[bx]
        mov es:[si+720h],al        //注意这里,写720h是因为显示的原因,前面第0行和第1行会被后面的覆盖掉,所以如果从0开始的,我们会看不到效果
        mov al,01000010b
        mov es:[si+721h],al
        
        inc bx
        add si,2
        loop s
        
        mov ax,4c00h
        int 21h    
code ends
end start

 

win7程序能够运行,但是不能显示效果,但是在xp下能够完美的显示

 

第十章 call和ret指令

ret:用栈中的数据,修改ip内容,从而实现近转移

  1. ip =ss*16+sp
  2. sp=sp+2

retf:硬栈中数据,修改cs和ip的内容,从而实现远转移

  1. (ip)=((ss)*16+(sp))
  2. sp=sp+2
  3. cs=ss*16+sp
  4. sp=sp+2

call

  1. 将当前的ip或cs和ip压入栈中
  2. 转移

call指令不能实现短转移,除此之外,call指令实现转移的方法和jmp指令的原理是相同的

 

call 标号

push ip

jmp near ptr 标号

 

call far ptr 标号

push cs

push ip

jmp far ptr 标号

 

转移地址在寄存器中的call指令

push ip

jmp 16位reg

cs:reg

 

换一个地址在内存中的call指令

push ip

jmp word ptr 内存单元地址

 

mul指令

  • 两个乘数,要么都为8位(一个默认放在al中,另外一个放在8位Reg中或内存字节单元中),要么都为16位(一个默认放在ax中,一个默认放在16位Reg中或内存字单元中)
  • 结果,如果是8位乘法,结果默认放在ax中,如果是16位乘法,默认高位放在dx中,低位放在ax中

 

第十一章  标识寄存器

15  14  13 12  11   10  9   8   7   6    5    4   3   2   1    0

        OF DF IF TF SF ZF   AF   PF   CF

 

ZF标志

零标志位,记录相关指令执行后,结果是否为0,如果为0,ZF=1,如果不为0,ZF=0

 

PF标志

奇偶标志位,记录相关指令执行后,其结果的所有bit中1个个数是否为偶数位,如果为偶数,PF=1

 

SF标志

符号标志位,记录相关指令执行后,其结果是否为负,如果为负,SF=19(计算机中通常用补码来表示有符号数据)

我们在将数据当做有符号的数据来运算的时候,可以通过它来得知的值结果的正负

如果我们将数据当做无符号的数据来运算的时候,SF的值就没有意义了,虽然相关指令影响了它的值

 

CF标志

进位标志位,在进行无符号数运算的时候,他记录了运算结果的最高有效位向更高位的进位值,或从更高的借位值,相关执行执行后,进位CF=1,不进位CF=0

  

OF标志

溢出标志位,在有符号运算中,如果超出了寄存器所能存放的范围。

 

adc指令:带位加法指令

adc 操作对象1,操作对象2

操作对象1=操作对象1+操作对象2+CF

 

sbb指令带借位减法指令

sbb 操作对象1,操作对象2

操作对象1=操作对象1-操作对象2-CF

 

cmp指令:

比较指令,功能相当于减法指令,但是不保存结果,但是会影响标志寄存器

 

 

指令 含义 检测的相关标志位
je 等于则转移 ZF=1
jne 不等于则转移 ZF=0
jb 低于则转移 CF=1
jnb 不低于则转移 CF=0
ja 高于则转移 CF=0或ZF=0
jna 不高于则转移 CF=1或ZF=1

 

DF标志和串传送指令

方向标志位,控制每次操作后si,di的自增自减

DF=0 每次操作后,si,di自减

DF=1 每次操作后,si,di自增

 

DF指令配合串操作指令使用的

eg

movsb,将ds:si指向的内存单元中的字节送入es:di中,根据DF位,决定si,di是自增还是自减

 

pushf 和popf

pushf的作用是将标志级传奇的值压栈,而popf的作用是从栈中弹出数据送入标志寄存器

时间: 2024-10-13 10:18:13

王爽汇编笔记的相关文章

王爽汇编10.12

;10.12 assume cs:code,ds:data data segment db 'word',0 db 'unix',0 db 'wind',0 db 'good',0 data ends code segment start : mov ax,data mov ds,ax mov si,0 mov di,16 mov cx,4 s: call cap inc si loop s MOV AX,4C00H INT 21H cap: push cx tip: mov cl,[si] m

王爽汇编实验九

1 ;实验九 2 assume cs:code,ds:data 3 data segment 4 db 'welcome to masm!' 5 data ends 6 7 code segment 8 start : 9 mov ax,data 10 mov ds,ax;定义数据段 11 12 mov ax,0B800h 13 mov es,ax;定义显示段 14 15 mov cx,16 16 mov si,0 17 mov di,10*160+80 ;将输出显示放在第10行中间 18 19

王爽汇编实验十一

;实验11 ;把小写字母换成大写字母 assume cs:code , ds:data data segment db "Beginner's All-purpose Symbolic Instruction Code.",0 data ends code segment start: mov ax,data mov ds,ax mov si,0 call letterc mov ax,4c00h int 21h letterc: push ax push si fun: mov al

王爽汇编实验(三)

王爽汇编浅显易懂,虽然前面两个实验都做完了,但是都是吊儿郎当做完的. 看到第三次实验比较好写心得,就写了. 原码如下,用提到的LINK.EXE和MASM.EXE进行编译链接操作. assume cs:codesgcodesg segmentmov ax,2000Hmov ss,axmov sp,0add sp,10pop axpop bxpush axpush bxpop axpop bx mov ax,4c00Hint 21Hcodesg endsend 首先是用debug 跟踪t1.exe执

跟我学汇编(二)王爽汇编环境搭建

这一次我们来讲解一下如何在window上搭建一个王爽教材中所用的汇编环境.王爽的书中用的是8086CPU,现在的CPU肯定是不一样啦,所以我们使用虚拟机来模拟一下. 一.下载系统 我们采用MS-DOS7.10系统,这里我提供一个免费的下载地址http://download.csdn.net/detail/xingjiarong/9428514,上次我看到了一个哥们的环境搭建过程,搭建好系统下载竟然要5个积分,我一气之下就自己动手搭建一个.这里下载之后是一个DOS的IOS文件,下面我们就来安装这个

王爽 汇编 检测点10.4

一.要求 二.上机调试 1.debug调试开始,首先查看反编译后的汇编代码(假设ob38就是段地址1000) 2.执行第一条指令mov ax,6 3.执行第二条指令call ax,也就是call 6(我们知道,对于call 16位reg格式来说,相当于push ip:jmp 16位reg.) 执行了call ax,会将它的下一条指令的压入栈中也就是5压入栈中,软后跳转到ip=6的地方 此时ip=6 ,sp=fffe,查看栈中的数据 4.执行mov bp,sp,栈顶地址赋值给bp寄存器 5.执行a

王爽 <<汇编 语言>> 13.6 BIOS中断例程应用

1 ;名称:ILOVEU程序 2 ;使用BIOS提供的中断例程 3 assume cs:code 4 code segment 5 main: 6 mov cx,880 ;显示背景22*80 7 mov dh,0 ;dh中放行号 8 mov dl,0 ;dl中放列号 9 bibi: 10 push cx 11 mov ah,2 ;显示光标 12 mov bh,0 ;第0页 13 int 10h 14 15 ;在光标处显示个数自定的字符 16 mov ah,9 ;9为在"在光标处显示字符的功能&q

王爽汇编 实验13

1. assume cs:code data segment db 'welcome to masm! ',0 data ends code segment start: mov ax,cs mov ds,ax mov si,offset capital mov ax,0 mov es,ax mov di,200h mov cx,offset capitalend-offset capital cld rep movsb mov ax,0 mov es,ax mov word ptr es:[7

王爽汇编第三版检测点10.5

(1)下面的程序执行后,ax 中的数值是多少? 1 assume cs:code 2 stack segment 3 dw 8 dup (0) 4 stack ends 5 code segment 6 start: mov ax,stack 7 mov ss,ax 8 mov sp,16 9 mov ds,ax 10 mov ax,0 11 call word ptr ds:[0] 12 inc ax 13 inc ax 14 inc ax 15 mov ax,4c00h 16 int 21h