跳至内容
王爽 汇编语言

王爽 汇编语言

第二章

寄存器

✅

  • 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=某合法寄存器的值

第三章

寄存器(内存访问)

✅

  • (⊙﹏⊙)

第四章

第一个程序

✅

  • 源程序写出到执行简要过程,后文有实例:
    1. 编写源程序 —— 写一个文本文件 —— 使用edit、笔记本等。
    2. 编译连接 —— 用汇编语言编译程序对文本文件编译产生目标文件;再用连接程序对目标文件进行连接,生成可执行文件。
    3. 在操作系统中执行文件。

✅

  • 源程序简单结构
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

      1. win+R 输入command打开命令提示符,输入“cd masm”(masm.exe所在目录),输入“masm”启动masm.exe。
      2. 提示我们输入源文件名,txt文件放在了c:\asm,且不是asm文件,输入c:\asm\123.txt。(注意此处的斜杠是和\n\t一样的)
      3. 询问生成obj文件名,我们选择默认,回车即可。
      4. 提示输入列表文件名,该文件为编译为目标文件的中间结果,我们不生成,回车即可。
      5. 提示输入交叉引用文件名,也是中间结果,不生成,回车即可。
      6. 无错误则masm.exe所在目录下生成123.obj
    • 连接 svg

      1. 进入DOS方式(win+R 输command),进入c:/masm (Overlay Linker3.60,link.exe所在目录),输入link启动link.exe。
      2. 提示输入目标文件名,[.OBJ]提示默认扩展名为obj,正合我意,输入123即可。
      3. 提示生成的exe文件名,默认名就行,回车即可。
      4. 提示输入映像文件名,提示输入库文件名,我们都回车即可。
      5. 无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]

    1. 类似[0],[bx]表示 ds:bx上的字节单元或字单元的内存内容,bx内的4位作为偏移地址

    2. [0][bx]
      mov ax, [0] 将地址为ds:0 的内存字单元存入axmov ax, [bx] 将地址为ds:bx的内存单元存入ax
      mov al, [0] 将地址为ds:0 的内存半字单元存入almov al, [bx] 将地址为ds:bx的内存半字单元存入al
      1. 实例

问题5.1.svg

忘了要写啥了
  • “()",机组中“(地址)”——描述某地址上的内容

    ()中的元素有三种类型: 寄存器名,段寄存器名 和 五位十六进制的物理地址

    如 (ax)、(al)、(ds)、(20000H)、**((ds)*16+(bx))**注意里面的ds、bx都有括号

    ax=10000H,则(ax)=(10000H)即地址10000H上的字节单元或字单元

  • loop 类似“goto”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
assume cs:code
code segment
	cx=8
s:	ax=0
	loop s
	
	mov ax,4c00h
	int 21
code ends
end

​ 首先(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
  1. 创建txt,将上述代码写入,保存。
  2. win+R进入DOS,输入"cd masm5",再输入"masm"启动。
  3. 源文件输入 “file_name.txt” 然后四个回车
  4. 没有error后,输入"link",obj文件输入file_name,然后回车回车回车
  5. 输入"debug file_name.exe",进入对该exe的debug模式
  6. 输入"u"查看当前能看到的指令,看不到"mov ax,4c00H"就输入t执行一条指令,输入个三四个再输入"u"查看,总之看到结束指令的偏移地址bx
  7. 输入"g bx",直接执行到bx处
  8. 输入"r",查看DS,因为它是c段的首地址,因为最后一次设置DS就是用的c
  9. 实验中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 IP

  • retf 第一次出栈 字单元 放入 IP,第二次出栈 字单元 放入CS,相当于

    pop IP

    pop CS

  • 栈里,CS总在IP底下

    | | | IP | | CS | |——-|

    结合上地址来说,CS总在IP的右边(高地址),因为sp压栈会从高到低走。

    IPCS

    ​ ↑ ​ 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 IP

    jmp near ptr s(标号)

  • 1000:3处指令为call s,s处内容为s:pop ax

    执行完这两条指令后,ax数值为 3

  • call far ptr s(标号),先将CS压入栈,再将IP压栈,然后远转移到s,相当于

    push CS

    push IP

    jmp far ptr s(标号)

寄存器型

call 16位reg,压栈IP,IP=(reg)

push IP

jmp 16位reg

内存单元型

  • call word ptr 内存单元地址,地址取出内容大小为字单元,仅压栈IP,相当于

    push IP

    jmp word ptr 如es:[bx]

  • call dword ptr 内存单元地址,地址取出内容大小为双字,先压栈CS再压栈IP,相当于

    push CS

    push IP

    jmp 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 start

mul指令

实验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
最后更新于