zoukankan      html  css  js  c++  java
  • 汇编语言

    环境搭建

    https://fishc.com.cn/forum.php?mod=viewthread&tid=156177&highlight=MASM
    在dosbox运行,首先需要mount c 文件所在文件夹
    源程序文件夹中需要 LINK.EXE, MASM.EXE, ML.EXE, DOSXNT.EXE

    段寄存器

    8086 CPU有四个段寄存器:
    CS, DS, SS, ES
    段寄存器用来提供段地址

    CS和IP

    CS: 代码段寄存器
    IP: 指令指针寄存器
    修改CS,IP: jmp 段地址:偏移地址
    仅修改IP的内容:
       jmp 某一合法寄存器
       jmp ax 用ax中的值修改IP


    下面的3条指令执行后,cpu几次修改IP?都是在什么时候?最后IP中的值是多少?

    mov ax,bx

    sub ax,ax

    jmp ax

    答:一共修改四次

    第一次:读取mov ax,bx之后

    第二次:读取sub ax,ax之后

    第三次:读取jmp ax之后

    第四次:执行jmp ax修改IP

    最后IP的值为0000H,因为最后ax中的值为0000H,所以IP中的值也为0000H


    debug

    -r: 查看寄存器内容
    -r ax :修改ax的内容
    -t : 执行指令
    -d :查看内存中内容
    -u: 查看内存中内容,将机器指令翻译成汇编指令
    -e : 改写内存中内容(机器指令)
    -a: 以汇编指令的格式在内存中写入一条机器指令

    DS和[address]

    DS寄存器通常用来存放要访问的数据的段地址
    若要读区10000H单元内容到寄存器:

    mov bx,1000H
    mov ds,bx
    mov al, [0]
    

    不可以直接 mov ds,1000H
    8086cpu 不支持直接将数据送入段寄存器

    将al中数据送入内存单元10000H:

    mov bx, 1000H
    mov ds, bx
    mov [0], al
    

    8086cpu有16根数据线,一次性可以传一个字

    mov

    mov 段寄存器,寄存器 是可以的
    但是 mov 寄存器, 段寄存器 可以吗? 答案是可以。

    段寄存器SS: 存放栈顶的段地址
    寄存器SP:存放栈顶的偏移地址
    任意时刻,SS:SP指向栈顶元素

    mov ax, 1000H
    mov ss,ax            // 不能直接mov数据到段寄存器
    mov sp,0010H
    push ax
    push bx
    push ds
    

    第一个程序!

    assume cs:abc
    abc segment
    	mov ax,2
    	add ax,ax
    	add ax,ax
    	
    	mov ax, 4c00H
    	int 21H
    abc ends 
    end
    

    .asm源代码文件,masm后.obj, link后.exe


    可以加一个入口,然后debug,debug.exe要放在文件夹中。

    assume cs:codesg
    
    codesg segment
    
    start:   mov ax,0123H
    	   mov bx,0456H
    	   add ax,bx
    	   add ax,ax 
    	   
    	   mov ax,4c00H
    	   int 21H 
    
    codesg ends
    end start 
    

    shell中输入 debug 2.exe 开始单步调试。int 21H 要用p执行,其他用t。
    debug将程序从可执行文件加载入内存后,cx中存放的是程序的长度。


    程序加载后,ds中存放着程序所在内存区的段地址,偏移地址为0,则程序所在内存区的地址为 ds:0 。这个内存区的前256个字节存放PSP,dos用来和程序进行通信。
    ds和cs相差10H,物理地址相差100H,256B

    [bx]

    若源程序中这样写
    mov ax,[0]
    编译器会认为是 mov ax,0
    所以要这样:
    mov ax,[bx]
    bx中放偏移地址。
    在debug中可以 mov ax,[0]

    loop

    执行loop时

    1. (cx)=(cx)-1
    2. 判断cx中的值,不为0则转至标号处执行,为0则向下执行。
      通常用loop实现循环,cx中存放循环次数。
    mov cx,11
    s: add ax,ax
    loop s
    

    以上代码共执行11次add。


    注意:在汇编源程序中数据不能以字母开头!
    所以 mov ax,0ffffh


    debug时用g命令可以直接跳到想要跳到的ip处。 -g 000B
    也可以在ip在loop那行时执行p命令。

    一段安全的空间

    在一般的PC机中,DOS方式下,DOS和其他合法程序一般都不会用0:200h-0:2ffh的256个字节的空间,所以我们使用这段空间是安全的。


    把ffff:0-ffff:b中的数据放入0020:0-0020:b中:

    assume cs:code
    
    code segment
    	mov ax,0ffffH
    	mov ds,ax
    	mov ax,0020H
    	mov es,ax
    	mov bx,0
    	mov cx,12
    s:	mov dl,[bx]
    	mov es:[bx], dl
    	inc bx
    	loop s
    	
    	mov ax,4c00H 
    	int 21H 
    
    code ends
    end 
    
    

    使用es存目标段地址,避免在循环中重复改变ds

    包含多个段的程序

    在代码段中使用数据

    dw: define word

    assume cs:code
    
    code segment 
    	dw 1h, 2h, 3h, 4h
    	mov bx,0 
    	mov ax,0  
    	mov cx, 4 
    s:  add ax, cs:[bx]
    	add bx,2 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    code ends
    end   
    

    1h 在cs:0处,依次放4个字
    这样的话cpu会把这些字转换成指令,而这些并不是指令,cpu会误读指令,我们可以在第一条需要执行的代码前加start(或其他任意,只要和end后对应):

    assume cs:code
    
    code segment 
    	dw 1h, 2h, 3h, 4h
    start:	mov bx,0 
    	mov ax,0  
    	mov cx, 4 
    s:  add ax, cs:[bx]
    	add bx,2 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    code ends
    end  start 
    

    end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。

    在代码段中使用栈

    利用栈,将程序中定义的数据逆序存放

    assume cs:codesg
    
    codesg segment 
    	dw 0123h,0456h,8976h,0987h,4576h,0345h,0984h,7678h 
    	dw 0,0,0,0,0,0,0,0 ;用dw定义8个字型数据,程序加载后将取得8个字的内存空间,后面把这些空间当作栈来使用。
    
    start:     mov ax,cs
    	   mov ss,ax 
    	   mov sp,32 
    	   
    	   mov bx,0 
    	   mov cx,8 
    s:	   push cs:[bx] 
    	   add bx,2 
    	   loop s 
    	   
    	   mov bx,0 
    	   mov cx,8 
    s1:    pop cs:[bx] 
    	   add bx,2 
    	   loop s1 
    	   
    	   mov ax,4c00h 
    	   int 21h 
    codesg ends 
    end start 
    

    将数据,代码,栈放入不同的段

    assume cs:codesg, ds:data, ss:stack
    
    data segment 
    	dw 0123h,0456h,8976h,0987h,4576h,0345h,0984h,7678h 
    data ends 
    
    stack segment 
    	dw 0,0,0,0,0,0,0,0 
    stack ends
    
    
    codesg segment 
    start:     mov ax,stack 
    	   mov ss,ax 
    	   mov sp, 16 
    	   mov ax,data 
    	   mov ds,ax 
    	   mov cx,8 
    	   mov bx,0 
    s:	   push [bx] 
    	   add bx,2 
    	   loop s 
    	   
    	   mov cx,8 
    	   mov bx,0 
    s1:        pop [bx] 
    	   add bx,2 
    	   loop s1
    	   
    	   mov ax,4c00h 
    	   int 21h 
    codesg ends 
    end start 
    

    不可以 mov ds,data


    对于如下定义的段
    name segment
    ...
    name ends
    如果段中的数据占N个字节,程序加载后,该段实际占有的空间为16*(N/6 +1)


    用push指令将a段中的前8个字型数据逆序存放到b段中:

    assume cs:code 
    
    a segment 
    	dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh 
    a ends 
    
    b segment 
    	dw 0,0,0,0,0,0,0,0
    b ends 
    
    code segment 
    start: 
    		mov ax,a 
    		mov ds,ax 
    		mov ax,b 
    		mov ss,ax 
    		mov sp,16 
    		mov bx,0 
    		mov cx,8 
    s:
    		push [bx]
    		add bx,2 
    		loop s 
    		
    		mov ax,4c00h 
    		int 21h 
    code ends 
    end start 
    

    更灵活的定位内存地址

    大小写转换:
    小写字母的ASCII码比对应的大写字母大32,即100000B.大写->小写,or al,00100000B; 小写->大写, and al,11011111B

    assume cs:code, ds:data 
    
    data segment 
    	db 'BaSiC'
    	db 'iNfOrMaTiOn'
    data ends 
    
    code segment 
    start:
    	mov ax,data 
    	mov ds,ax 
    	mov bx,0 
    	mov cx,5 
    	
    s:  mov al,[bx] 
        and al,11011111B 
    	mov [bx],al    ;小写to大写 
    	inc bx 
    	loop s 
    	
    	mov bx,5 
    	mov cx,11 
    s1: mov al,[bx]
        or al,00100000B 
    	mov [bx],al   ;大写to小写  
    	inc bx 
    	loop s1 
    	
    	mov ax,4c00H 
    	int 21H 
    code ends 
    end start 
    

    [bx+idata]作为偏移地址

    mov ax,[bx+5]
    mov ax,[5+bx]
    mov ax,5[bx]
    mov ax,[bx].5

    assume cs:code, ds:data 
    
    data segment 
    	db 'BaSiC'   ;转换为大写 
    	db 'iNfOr'   ;转换为小写 
    data ends 
    
    code segment 
    start:
    	mov ax,data 
    	mov ds,ax 
    	mov bx,0 
    	mov cx,5 
    	
    s:  mov al,[0+bx] 
    	and al,11011111b  
    	mov [0+bx],al 
    	
    	mov al,[5+bx]
    	or al,00100000b 
    	mov [5+bx],al 
    	inc bx 
    	loop s  
    	
    	mov ax,4c00H 
    	int 21H 
    code ends 
    end start 
    

    SI和DI

    SI和DI不能分成两个8位寄存器使用。

    mov si,0
    mov ax,[si]
    
    mov di,0
    mov ax,[di]
    
    mov di,0
    mov ax,[di+123]
    
    assume cs:code, ds:data 
    
    data segment 
    	db 'welcome to masm!'
    	db '................'
    data ends 
    
    code segment 
    start:
    	mov ax,data 
    	mov ds,ax 
    	mov si,0 
    	mov cx,16 
    	
    s:  mov al,[si]
    	mov [16+si],al 
    	inc si 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    code ends 
    end start 
    

    [bx+si], [bx+di]
    [bx+si+idata], [bx+di+idata]


    练习:

    1. 将data中每个单词变为大写
    assume cs:codesg, ds:datasg 
    
    datasg segment 
    	db 'abc             '
    	db 'def             '
    	db 'ghi             '
    	db 'gkl             '
    datasg ends 
    
    codesg segment 
    start:
    	mov ax,datasg 
    	mov ds,ax 
    	mov bx,0 
    	mov cx,4 
    	
    s:  mov dx,cx 
    	mov cx,3 
    	mov di,0
    	s1:	mov al,[bx+di]
    		and al,11011111b 
    		mov [bx+di],al 
    		inc di 
    		loop s1 
    	add bx,16 
    	mov cx,dx 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    codesg ends 
    end start 
    

    但是用dx暂存cx不合适,dx有可能会用到。
    使用栈:

    assume cs:codesg, ds:datasg, ss:stack 
    
    datasg segment 
    	db 'abc             '
    	db 'def             '
    	db 'ghi             '
    	db 'gkl             '
    datasg ends 
    
    stack segment 
    	dw 0,0,0,0,0,0,0,0
    stack ends
    
    codesg segment 
    start:
    	mov ax,stack 
    	mov ss,ax 
    	mov sp,16  
    	mov ax,datasg 
    	mov ds,ax 
    	mov bx,0 
    	mov cx,4 
    	
    s:  push cx
    	mov cx,3 
    	mov di,0
    	s1:	mov al,[bx+di]
    		and al,11011111b 
    		mov [bx+di],al 
    		inc di 
    		loop s1 
    	add bx,16 
    	pop cx 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    codesg ends 
    end start 
    
    1. 将单词的前四个字母变大写
    assume cs:codesg, ds:datasg, ss:stack 
    
    datasg segment 
    	db '1. abcdisplay   '
    	db '2. defbrows     '
    	db '3. ghikkkkk     '
    	db '4. gkluiui      '
    datasg ends 
    
    stack segment 
    	dw 0,0,0,0,0,0,0,0
    stack ends
    
    codesg segment 
    start:
    	mov ax,stack 
    	mov ss,ax 
    	mov sp,16  
    	mov ax,datasg 
    	mov ds,ax 
    	mov bx,0 
    	mov cx,4 
    	
    s:  push cx
    	mov cx,4 
    	mov si,0
    	s1:	mov al,3[bx+si]
    		and al,11011111b 
    		mov 3[bx+si],al 
    		inc si 
    		loop s1 
    	add bx,16 
    	pop cx 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    codesg ends 
    end start 
    

    数据处理的两个基本问题

    bx,si,di,bp
    只能以四种组合出现:[bx+si],[bx+di],[bp+si],[bp+di]
    错误用法:[bx+bp],[si+di]
    只要在[]中使用bp,而指令中没有显性的给出短地址,段地址就默认在ss中
    可以显性的给出存放段地址的寄存器:
    mov ax,ds:[bp]
    mov ax,es:[bx]
    mov ax,ss:[bx+si]
    mov ax,cs:[bx+si+8]


    寻址方式:

    1. 直接寻址: mov ax,[2]
    2. 寄存器间接寻址: mov ax,[bx]
    3. 寄存器相对寻址: mov ax,[bx+2]
    4. 基址变址寻址: mov ax,[bx+si]
    5. 相对基址变址寻址: mov ax,[bx+si+2]


      在没有寄存器参与的内存单元访问指令中,用word ptr或byte ptr显性的指明访问的内存单元的长度。
    mov word ptr ds:[0],1 
    inc word ptr [bx]
    inc word ptr ds:[0] 
    add word ptr [bx],2
    

    若是字节型,byte ptr
    push只push字型数据
    push ds:[1]

    div指令

    1. 除数8位,被除数16位(AX),商(al),余数(ah)
    2. 除数16位,被除数32位(dx+ax),商(ax),余数(dx)
      div bype ptr ds:[0] (al)=(ax)/((ds)16+0)的商,(ah)=余数
      div word ptr ds:[0] (ax)=[(dx)
      10000h+(ax)]/((ds)*16+0) 的商 ,(dx)=余数

    计算 100001/100
    100001=186a1h

    mov dx,1h
    mov ax,86a1h 
    mov bx,100 
    div bx
    

    执行完成后,(ax)=03e8h(100), (dx)=1


    计算 1001/100

    mov ax,1001
    mov bl,100
    div bl
    

    执行后,(al)=0ah, (ah)=1

    伪指令 dd

    用来定义double word, 32位

    assume cs:code, ds:data 
    
    data segment 
    	dd 100001
    	dw 100
    	dw 0
    data ends 
    
    code segment 
    start: mov ax,data
    	   mov ds,ax 
    	   mov ax,ds:[0]
    	   mov dx,ds:[2]
    	   mov bx,ds:[4]
    	   div bx 
    	   mov ds:[6],ax 
    	   
    	   mov ax,4c00h
    	   int 21h 
    code ends 
    end start
    

    伪指令 dup

    和db,dw,dd配合使用,进行数据的重复。

    • db 3 dup (0)
      定义了3个字节,值都是0
    • db 3 dup (0,1,2)
      相当于 db 0,1,2,0,1,2,0,1,2
    • db 3 dup ('abc','ABC')
      相当于 db 'abcABCabcABCabcABC'
    • 定义200字节大小的栈:
    stack segment
          db 200 dup (0)
    stack ends
    

    实验: 把table填上数据
    assume cs:code,ds:data,es:table
    
    data segment
    db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
    db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
    db '1993','1994','1995'
    ;以上是表示21年的字符串 4 * 21 = 84
    
    dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
    dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
    ;以上是表示21年公司总收入的dword型数据 4 * 21 = 84
    
    dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
    dw 11542,14430,15257,17800
    ;以上是表示21年公司雇员人数的21个word型数据 2 * 21 = 42
    data ends
    
    table segment
    db 21 dup ('year summ ne ?? ') ; 'year summ ne ?? ' 刚好16个字节
    table ends
    
    code segment
    start:
    	mov ax,data 
    	mov ds,ax 
    	mov ax,table 
    	mov es,ax 
    	
    	mov bx,0 ;年和总收入的偏移 
    	mov si,0 ; 人数的偏移 
    	mov di,0 ; 结果的行的偏移 
    	
    	mov cx,21 
    s:  mov ax,ds:[0+bx] ;填年份
    	mov es:[di],ax 
    	mov ax,ds:[2+bx]
    	mov es:[di+2],ax 
    	
    	mov ax,ds:[84+bx] ;填总收入
    	mov es:[di+5],ax
    	mov ax,ds:[86+bx]
    	mov es:[di+7],ax
    	
    	mov ax,ds:[168+si]  ;填人数
    	mov es:[di+10],ax
    	
    	mov ax,ds:[84+bx]   ;计算平均收入
    	mov dx,ds:[86+bx]
    	div word ptr ds:[168+si]
    	mov es:[di+13],ax  ; 填平均收入 
    	
    	add bx,4   ;双字型移4位 
    	add si,2    ; 字型移2位 
    	add di,16 
    	loop s 
    	
    	mov ax,4c00h 
    	int 21h 
    code ends 
    end start 
    

    转移指令

    操作符offset

    是由编译器处理的符号,用来取得标号的偏移地址。

    assume cs:code 
    
    code segment 
    start: mov ax, offset start ;相当于 mov ax,0 
    s:     mov ax, offset s     ; 相当于 mov ax,3  
    code ends 
    end start 
    

    把s处的第一条指令复制到s0处:

    assume cs:code 
    
    code segment 
    s:	mov ax,bx      ; 2 bytes
    	mov si,offset s 
    	mov di, offset s0 
    	
    	mov ax,cs:[si]
    	mov cs:[di],ax 
    s0:     nop           ; 1 byte
    	nop 
    
    code ends 
    end  
    

    jmp指令

    无条件跳转

    • jmp short 标号
      转到标号处执行指令
      这种格式的jmp指令实现段内短转移,对ip的修改范围为 -128~127
    assume cs:code 
    
    code segment 
    start: mov ax,0 
    	   jmp short s 
    	   inc ax 
    	   add ax,ax 
    s:	   inc ax 
    	   
    	   mov ax,4c00h 
    	   int 21h   
    
    code ends 
    end  
    


    jmp对应的机器码 EB03, 读取指令 EB03 进入指令缓冲器后,ip变为 0005, 执行 EB03, IP变为 0008,成功跳转。
    EB后面的8位位移由编译程序在编译时算出,因为是8位,所以范围为 -128-127

    • jmp near ptr 标号
      16位的偏移。
      位移范围: -32768~32767
    • jmp far ptr 标号
      段间转移
      far ptr指明了标号的段地址和偏移地址,修改cs:ip
    assume cs:code 
    
    code segment 
    start:     mov ax,0 
    	   mov bx,0 
    	   jmp far ptr s 
    	   db 256 dup (0)
    s:         add ax,1 
    	   inc ax 
    	   
    	   mov ax,4c00h 
    	   int 21h   
    
    code ends 
    end start
    


    注意到jmp指令对应的机器码,和cs:ip对应

    • jmp 16位寄存器
      寄存器中放的是ip的值。
    • 转移地址在内存中
      • jmp word ptr 内存单元地址
        内存单元中存放的字是转移目的的偏移地址。
        mov ax,0123h 
        mov ds:[0],ax 
        jmp word ptr ds:[0]
        
        跳转到ip 0123h处
      • jmp dword ptr 内存单元地址
        (cs)=(内存单元地址+2)
        (ip)=(内存单元地址)
        mov ax,0123h 
        mov ds:[0],ax 
        mov word ptr ds:[2],0 
        jmp dword ptr ds:[0]
        
        (cs)=0, (ip)=0123h

    jcxz 指令

    有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对ip的修改范围都为 -128~127
    格式: jcxz 标号
    当(cx)=0时,(IP)=(IP)+8位位移
    当(cx)!=0时,程序向下执行

    loop指令

    短转移,在对应的机器码中包含转移的位移,而不是目的地址。
    对ip的修改范围 -128~127

    根据位移进行转移的意义

    jmp short 标号
    jmp near ptr 标号
    jcxz 标号
    loop 标号
    在他们的机器码中不包含转移的目的地址,包含的是到目的地址的位移距离。
    转移范围有限,超出编译时编译器将报错。

    assume cs:code 
    
    code segment 
    start: jmp short s 
    	db 128 dup(0)
    	s:    mov ax,0ffffh 
    
    code ends 
    end start
    

    注意: jmp 2000:1000这样的转移指令实在debug中使用的,汇编编译器并不认识。

    实验8

    assume cs:codesg  	     
    
    codesg segment
    	mov ax,4c00h
    	int 21h
    	
    
    start:      			
    	mov ax,0
    
    s:
    	nop      		;nop指令占一个字节
    	nop
    	
    	mov di,offset s      
    	mov si,offset s2	
    	mov ax,cs:[si]		; (ax)=F6EB, F6h=-10,即往回跳10个字节, 0022->0018
    	mov cs:[di],ax		; s处将存 EBF6 ,即0008处存EBF6
    	
    
    s0:
    	jmp short s	       ;跳到s后执行EBF6,往上跳10个跳到了0000
    	
    
    s1:
    	mov ax,0
    	int 21h
    	mov ax,0
    	
    s2:
    	jmp short s1
    	nop
    	
    codesg ends
    end start
    





    实验9

    assume cs:code, ds:data, ss:stack  
    
    data segment 
    	db 'welcome to masm!'
    	db 02h,24h,71h  
    data ends 
    
    stack segment 
    	db 20 dup (0) 
    stack ends
    
    code segment 
    start:	mov ax,data 
    	mov ds,ax ;源段  
    	mov ax,0B800h  
    	mov es,ax   ;目标段
    	
    	mov ax,stack 
    	mov ss,ax 
    	mov sp,20
    	
    	mov bx,1820   ;目标显示的偏移,每次加160  
    	mov si,0   ;颜色格式,每次加1 
    	mov cx,3 
    	
    s:  mov ah,ds:[16+si] ;颜色  
    	push cx 
    	push si 
    	mov si,0   ;源字符串偏移,每次加1
    	mov di,0    ;目标偏移位置,每次加2 
    	mov cx,16 
    	s1:	mov al,ds:[si]
    		mov es:[bx+di],al 
    		mov es:1[bx+di],ah 
    		inc si 
    		add di,2 
    		loop s1 
    	
    	pop si 
    	pop cx 
    	inc si 
    	add bx,160 
    	loop s 
    
    
    	mov ax,4c00h 
    	int 21h 
    code ends 
    end start 
    
    
    

    Call和Ret指令

    call和ret都是转移指令,他们都修改ip,或者同时修改cs和IP

    ret和retf

    ret指令用栈中的数据,修改ip的内容,从而实现近转移。
    cpu执行ret指令时,进行下面两步操作:

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


      retf 指令用栈中的数据修改cs和ip的内容,从而实现远转移。
    3. (ip)=((ss)*16+(sp))
    4. (sp)=(sp)+2
    5. (cs)=((ss)*16+(sp))
    6. (sp)=(sp)+2

    call

    cpu执行call指令,进行两步操作:

    1. 将当前ip或者cs和ip入栈
    2. 转移(jmp)

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

    • call 标号
      将当前的ip压栈后,转到标号处执行指令。
      cpu操作步骤:
    1. (sp)=(sp)-2
    2. ((ss)*16+(sp))=(IP)
    3. (ip)=(ip)+16位位移
    • call far ptr 标号
      段间转移
      cpu执行此操作时的步骤:
    1. (sp)=(sp)-2
    2. (cs)入栈
    3. (sp)=(sp)-2
    4. (ip)入栈
    5. 跳到标号处
    • call 16位寄存器
      功能:
      ip入栈,跳到ip为寄存器内容处

    • 转移地址在内存中

    1. call word ptr 内存单元地址
      段内
      先push ip再跳到内存中存着的ip

    2. call dword ptr 内存单元地址
      先push cs, push ip, 再跳
      内存中第一个字存着要跳的ip,下一个字存着要跳的cs

    mov sp,10h 
    mov ax,0123h 
    mov ds:[0],ax 
    mov word ptr ds:[2],0 
    call dword ptr ds:[0]
    

    执行后,(cs)=0, (ip)=0123h,(sp)=0ch

    call和ret配合使用

    mul 指令

    mul reg
    mul 内存单元
    用al或者ax中的数据×,都是8位或者都是16位。
    8位的两个数结果存在ax, 16位的两个数结果 高位存在DX,低位存在AX
    计算100*10:

    mov al,100
    mov bl,10 
    mul bl 
    

    结果 (ax)=1000


    计算100*10000:

    mov ax,100
    mov bx,10000
    mul bx
    

    结果 (dx)=000fh, (ax)=4240h

    参数和结果的传递

    编程:计算data段中第一组数据的3次方,结果存在后面一组dword中

    assume cs:code, ds:data 
    
    data segment 
    	dw 1,2,3,4,5,6,7,8 
    	dd 0,0,0,0,0,0,0,0 
    data ends 
    
    code segment 
    start:	mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		mov di,0
    		mov cx,8 
    		
    		s:	mov ax,ds:[si]
    			call cube 
    			mov ds:[16+di],ax 
    			mov ds:[16+di+2],dx 
    			add si,2 
    			add di,4 
    			loop s
    		
    		mov ax,4c00h 
    		int 21h 
    		
    		cube:	mov bx,ax 
    				mul bx 
    				mul bx 
    				ret
    				
    code ends 
    end start
    

    批量数据的传递

    把批量数据放到内存中,然后把他们所在内存空间的首地址放到寄存器。

    《汇编语言第三版》实验10-1

    assume cs:code 
    
    data segment 
    	db 'Welcome to masm!',0 
    data ends 
    
    code segment 
    start:	mov dh,8 
    	    mov dl,3 
    		mov cl,2 
    		mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		call show_str 
    		
    		mov ax,4c00h 
    		int 21h 
    
    show_str:	mov al,160 
    			mul dh 
    			mov di,ax 
    			add dl,dl 
    			mov dh,0 
    			add di,dx 
    			mov ax,0B800h 
    			mov es,ax 
    			mov dl,cl  ;dl存颜色信息 
    			mov ch,0
    			s:	mov cl,[si]
    				jcxz ok 
    				mov es:[di],cl 
    				mov es:[di+1],dl 
    				inc si 
    				add di,2 
    				jmp short s 
    			ok: ret
    			
    code ends 
    end start 
    

    实验10-2

    assume cs:code, ds:data 
    
    data segment 
    	dw 0,0,0,0,0  ;分别存被除数的低16位,高16位,除数; 公式右半部分商,余数  
    data ends 
    
    code segment 
    start:	mov ax,data 
    		mov ds,ax 
    		mov ax,4240h 
    		mov dx,000fh 
    		mov cx,0ah 
    		mov ds:[0],ax 
    		mov ds:[2],dx 
    		mov ds:[4],cx 
    		call divdw 
    		
    		mov cx,4c00h 
    		int 21h 
    		
    divdw:	mov ax,ds:[2]
    		mov dx,0 
    		div cx    ; rem(H/N) 结果在dx  
    		mov ax,ds:[0]
    		div cx   ;公式右半部分结果,商在ax,余数在dx 
    		mov ds:[6],ax 
    		mov ds:[8],dx 
    		mov ax,ds:[2]
    		mov dx,0 
    		div cx  ; int(H/N) 结果在ax 
    		mov dx,ax 
    		mov ax,ds:[6]
    		mov cx,ds:[8]
    		ret 
    
    code ends 
    end start 	
    

    改进:

    assume cs:code
    
    stack segment 
    	dw 0,0,0,0,0 
    stack ends 
    
    code segment 
    start:	mov ax,stack   
    		mov ss,ax    
    		mov sp,10   
    		mov ax,4240h 
    		mov dx,000fh 
    		mov cx,0ah 
    		
    		call divdw 
    		
    		mov cx,4c00h 
    		int 21h 
    		
    divdw:	push ax  
    		mov ax,dx  
    		mov dx,0 
    		div cx   ;int(H/N)--ax,  rem(H/N)--dx  
    		mov bx,ax    ;bx: int(H/N)
    		pop ax 
    		div cx  ; [res(H/N)*65536+L]/N  
    		mov cx,dx 
    		mov dx,bx   
    		
    		ret 
    
    code ends 
    end start 
    	
    

    实验10-3

    assume cs:code 
    
    displaycontent segment 
    	db 10 dup (0)
    displaycontent ends 
    
    tmp segment   ;用于逆序  
    	db 10 dup (0)
    tmp ends  
    
    code segment 
    start:	mov ax,12666
    		mov bx,tmp 
    		mov es,bx 
    		mov bp,0         
    		mov bx,displaycontent 
    		mov ds,bx 
    		mov si,0   
    		call dtoc   ;转换到10进制的字符并存到displaycontent中
    		
    		mov dh,8     ;开始准备显示displaycontent中的内容 
    	    mov dl,3 
    		mov cl,2 
    		mov si,0 
    		call show_str 
    		mov ax,4c00h
    		int 21h
    		
    dtoc:	mov dx,0 
    		mov cx,10 
    		div cx   ; ax:商,dx:余数  
    		add dx,30h  
    		
    		mov es:[bp],dl
    		
    		mov cx,ax  
    		jcxz dtocok 
    		inc bp   
    		jmp short dtoc 
    
    dtocok: 
    			mov al,es:[bp]
    			mov ds:[si],al 
    			mov cx,bp   
    			jcxz ok_1
    			inc si 
    			dec bp     
    			jmp short dtocok    
    			
    		ok_1:  ret		
    		
    
    show_str:	mov al,160 
    			mul dh 
    			mov di,ax 
    			add dl,dl 
    			mov dh,0 
    			add di,dx 
    			mov ax,0B800h 
    			mov es,ax 
    			mov dl,cl  ;dl存颜色信息 
    			mov ch,0
    			s:	mov cl,[si]
    				jcxz ok 
    				mov es:[di],cl 
    				mov es:[di+1],dl 
    				inc si 
    				add di,2 
    				jmp short s 
    			ok: ret
    
    code ends 
    end start
    

    课程设计1

    assume cs:code,ds:data,es:table
    
    data segment
    	db '1975','1976','1977','1978','1979','1980','1981','1982','1983'
    	db '1984','1985','1986','1987','1988','1989','1990','1991','1992'
    	db '1993','1994','1995'
    	;以上是表示21年的字符串 4 * 21 = 84
    
    	dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514
    	dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000
    	;以上是表示21年公司总收入的dword型数据 4 * 21 = 84
    
    	dw 3,7,9,13,28,38,130,220,476,778,1001,1442,2258,2793,4037,5635,8226
    	dw 11542,14430,15257,17800
    	;以上是表示21年公司雇员人数的21个word型数据 2 * 21 = 42
    data ends
    
    table segment
    	db 21 dup ('year summ ne ?? ') ; 'year summ ne ?? ' 刚好16个字节
    table ends
    
    displaycontent segment 
    	db 10 dup (0)
    displaycontent ends 
    
    tmp segment   ;用于逆序  
    	db 10 dup (0)
    tmp ends  
    
    code segment
    start:
    	mov ax,data 
    	mov ds,ax 
    	mov ax,table 
    	mov es,ax 
    	
    	mov bx,0 ;年和总收入的偏移 
    	mov si,0 ; 人数的偏移 
    	mov di,0 ; 结果的行的偏移 
    	
    	mov cx,21 
    s:  mov ax,ds:[0+bx] ;填年份
    	mov es:[di],ax 
    	mov ax,ds:[2+bx]
    	mov es:[di+2],ax 
    	
    	mov ax,ds:[84+bx] ;填总收入
    	mov es:[di+5],ax
    	mov ax,ds:[86+bx]
    	mov es:[di+7],ax
    	
    	mov ax,ds:[168+si]  ;填人数
    	mov es:[di+10],ax
    	
    	mov ax,ds:[84+bx]   ;计算平均收入
    	mov dx,ds:[86+bx]
    	div word ptr ds:[168+si]
    	mov es:[di+13],ax  ; 填平均收入 
    	
    	add bx,4   ;双字型移4位 
    	add si,2    ; 字型移2位 
    	add di,16 
    	loop s 
    	
    	mov ax,table 
    	mov ds,ax      ;ds: table段地址  
    	mov ax,0B800h 
    	mov es,ax 		;es:显存段地址    
    	mov bx,0   ;table的行首偏移地址   
    	mov si,160   ;显存的行首地址  
    	mov cx,21 
    	
    s4:	push cx 
        mov cl,02h
    	
    	mov al,ds:[bx] 
    	mov es:[si],al 
    	mov es:[si+1],cl 
    	mov al,ds:[bx+1]
    	mov es:[si+2],al
        mov es:[si+3],cl  	
    	mov al,ds:[bx+2] 
    	mov es:[si+4],al
    	mov es:[si+5],cl 
    	mov al,ds:[bx+3] 
    	mov es:[si+6],al
    	mov es:[si+7],cl   
    	
    	; 第二列:总工资  
    	mov ax,ds:[bx+5]
    	mov dx,ds:[bx+7]
    	call ddtoc 
    	
    	mov di,si
    	add di,20
    	call showstr
    	
    	; 第三列:人数   
    	mov ax,ds:[bx+10]
    	mov dx,0 
    	call ddtoc 
    	
    	mov di,si
    	add di,50 
    	call showstr
    	
    	; 第四列:平均工资    
    	mov ax,ds:[bx+13]
    	mov dx,0 
    	call ddtoc 
    	
    	mov di,si
    	add di,80 
    	call showstr
    	
    	pop cx 
    	add bx,16 
    	add si,160 
    	loop s4    
    	
    	mov ax,4c00h 
    	int 21h 
    
    ;需要传入 di, es   
    ; 需要用到寄存器: ds, bx , cx   
    showstr: 
    		 push ds 
    		 mov ax,displaycontent 
    		 mov ds,ax 	;ds:displaycontent段地址 
    	 
    		 push bx 
    		 mov bx,0  ; bx: displaycontent的偏移 
    ls:		 mov cl,ds:[bx]
    		 mov ch,0 
    		 jcxz showstr_ok 
    		 mov es:[di],cl 
    		 mov cl,02h  
    		 mov es:[di+1],cl 
    		 add di,2 
    		 inc bx   
    		 jmp short ls   
    showstr_ok: pop bx 
    		    pop ds
    			ret
    	
    	;一个子程序,负责把dword (dx+ax)转变成字符串存到displaycontent 
    	; 此程序需要传入dx,ax   
    	; 需要用到寄存器: bx, es, ds, si, di, cx  
    ddtoc: 
    	push bx
    	push ds 
    	push es 
    	push si 
    	mov bx,displaycontent 
    	mov es,bx    ;es: displaycontent段地址  
    	mov bx,tmp 
    	mov ds,bx     ; ds:tmp段地址  
    	mov si,0   ;暂存的tmp的偏移地址
    	mov di,0    ; 结果displaycontent的偏移地址  
    s0:	mov cx,10  
    	call divdw 
    	add cx,30h 
    	mov ds:[si],cl 
    	mov cx,dx 
    	jcxz s1 
    	inc si
    	jmp short s0
    	s1: mov cx,ax 
    		jcxz s2 
    		inc si 
    		jmp short s0  
    	s2: mov bl,ds:[si]
    		mov es:[di],bl 
    		mov cx,si 
    		jcxz s3 
    		dec si 
    		inc di 
    		jmp short s2    
    	s3: inc di 
    		mov es:[di],0   ;结束标记  
    		
    		pop si 
    		pop es 
    		pop ds 
    		pop bx 
    		ret 
    		
    	
    	; 32位/16位,(dx+ax)/cx, 结果余数cx,商dx+cx   
    	; 需要传入 dx,ax,cx    
    	; 需要用到寄存器: bx   
    divdw:	push ax  
    		mov ax,dx  
    		mov dx,0 
    		div cx   ;int(H/N)--ax,  rem(H/N)--dx  
    		mov bx,ax    ;bx: int(H/N)
    		pop ax 
    		div cx  ; [res(H/N)*65536+L]/N  
    		mov cx,dx 
    		mov dx,bx   
    		
    		ret 
    
    code ends 
    end start 
    

    标志寄存器


    6--ZF
    结果为0,ZF=1
    不为0,ZF=0
    PF
    奇偶标志位,结果的所有的二进制位中1的个数
    为偶数,PF=1
    为奇数,PF=0
    7--SF
    结果为负,SF=1
    为正,SF=0
    当我们将数据当作有符号数,可以通过SF得知结果的正负;
    如果当作无符号数,则SF无意义。


    add,sub,mul,div,inc,or,and等运算指令影响标志寄存器; mov,push,pop等传送指令对标志寄存器无影响

    0--CF
    进位标志位
    无符号数运算产生进位 CF=1





    如果两数相减,产生借位,CF也为1.

    OF
    有符号数运算超过机器所能表示的范围产生溢出。
    例如8位al寄存器范围,如果是有符号数, -128~127
    溢出针对有符号数,进位针对无符号数!

    adc指令
    adc 操作对象1 操作对象2
    功能: 操作对象1+操作对象2+CF

    mov al,98h 
    add al,98h 
    adc ah,0
    

    执行后 (ah)=1

    mov ax,2 
    mov bx,1 
    sub bx,ax ;借位,CF=1 
    adc ax,1
    

    执行后 (ax)=4


    编程计算 1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中:

    assume cs:code  
    
    code segment 
    start:	mov bx,0f000h 
    		mov ax,1eh 
    		add bx,1000h 
    		adc ax,20h 
    		
    		mov ax,4c00h
    		int 21h 
    code ends 
    end start  
    

    adc指令也可能改变CF!
    编程计算 1EF0001000h + 2010001EF0H
    结果放在ax+bx+cx中

    assume cs:code  
    
    code segment 
    start:	mov cx,1000h 
    		mov bx,0f000h 
    		mov ax,1eh 
    		add cx,1ef0h 
    		adc bx,1000h 
    		adc ax,20h 
    				
    		mov ax,4c00h
    		int 21h 
    code ends 
    end start 
    

    计算两个128位数据的和:

    assume cs:code, ds:data 
    
    data segment 
    	db 88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h
    	db 88h,88h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h
    data ends  
    
    code segment 
    start:	mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		mov di,16 
    		mov cx,8 
    		sub ax,ax 
    		call add128 
    		
    				
    		mov ax,4c00h
    		int 21h   
    		
    add128:	mov ax,[si]
    		adc ax,[di]
    		mov [si],ax 
    		inc si    ;必须inc,不可以add,inc不影响CF  
    		inc si 
    		inc di 
    		inc di 
    		loop add128 
    		ret  
    code ends 
    end start  
    

    sbb 借位减法指令
    sbb ax,bx
    (ax)=(ax)-(bx)-CF
    计算 003E1000H - 00202000H

    mov bx, 1000H 
    mov ax, 003EH 
    sub bx,2000H 
    sbb ax,0020H 
    

    cmp指令
    只对标志寄存器产生影响,不保存结果。
    cmp ax,ax
    做(ax)-(ax),结果0,并不在ax中保存,只影响flag相关各位。

    1. 比较无符号数
      ZF=1 说明相等
      CF=1 说明前面小于后面
    2. 比较有符号数
      ZF=1, 两数相等
      SF=1, 前面<后面 ???
      不可以,可能溢出, 比如 (ah)=22h, (bh)=0a0h, cmp ah,bh 产生溢出
      要考虑溢出!!!
      需要同时考虑OF和SF
      1. SF=1,OF=0  前面<后面
      2. SF=1,OF=1   前面>后面
      3. SF=0,OF=1   前面<后面
      4. SF=0,OF=0   前面>=后面

    检测比较结果的条件转移指令

    无符号:

    je 检测的是ZF的值,不管je前面是什么指令,只要cpu执行je指令时ZF=1,则转移。
    其他指令同理。
    计算8的个数:

    assume cs:code, ds:data 
    
    data segment 
    	db 8,11,8,1,8,5,8,38 
    data ends 
    
    code segment 
    start:	mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		mov ax,0 
    		mov cx,8 
    		
    		s:	mov bl,[si]
    			cmp bl,8 
    			jne skip 
    			inc ax  
    			
    		skip:	inc si 
    				loop s  
    			
    			mov ax,4c00h 
    			int 21h  
    
    code ends
    end start  
    

    小于8:

    assume cs:code, ds:data 
    
    data segment 
    	db 8,11,8,1,8,5,8,38 
    data ends 
    
    code segment 
    start:	mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		mov ax,0 
    		mov cx,8 
    		
    		s:	mov bl,[si]
    			cmp bl,8 
    			jnb skip 
    			inc ax  
    			
    		skip:	inc si 
    				loop s  
    			
    			mov ax,4c00h 
    			int 21h  
    
    code ends
    end start  
    

    DF标志和串传送指令

    DF:方向标志位
    在串处理指令中,控制每次操作后si,di的增减

    • DF=0: 每次操作后si,di递增
    • DF=1: 每次操作后si,di递减
    1. movsb
      以字节单位传送
      ((es)x16+(di))=((ds)x16+(si))
    • DF=0: 每次操作后si,di递增
    • DF=1: 每次操作后si,di递减
    1. movsw
      传送一个字
      一般来说movsb,movsw都和rep配合使用,格式:rep movsb
      rep的作用是根据cx的值重复执行后面的串传送指令
      改变DF的值:
    2. cld指令:DF设置为0
    3. std指令:DF设置为1

    把前面的字符串复制到后面:

    assume cs:code, ds:data 
    
    data segment 
    	db 'Welcome to masm!'
    	db 16 dup (0)
    data ends 
    
    code segment 
    start:	mov ax,data 
    		mov ds,ax 
    		mov es,ax 
    		mov si,0 
    		mov di,16
    		
    		mov cx,16 
    		cld 
    		rep movsb  
    		
    		mov ax,4c00h 
    		int 21h  
    
    code ends
    end start 
    

    把F000H段中最后16个字节数据存到data段中:

    assume cs:code, ds:data 
    
    data segment 
    	db 16 dup (0)
    data ends 
    
    code segment 
    start:	mov ax,0F000h
    		mov ds,ax 
    		mov ax,data
    		mov es,ax
    		mov si,0ffffh
    		mov di,15
    		
    		mov cx,16 
    		std  
    		rep movsb  
    		
    		mov ax,4c00h 
    		int 21h  
    
    code ends
    end start  
    

    pushf和popf

    pushf: 将标志寄存器的值入栈
    popf: 从栈中弹出数据,送入标志寄存器

    实验11:将data中数据的小写字母转为大写

    assume cs:codesg
    
    datasg segment 
    
        db "Beginner's All-purpose Symbolic Instruction Code.",0 
    
    datasg ends
    
    codesg segment
    
    start:  mov ax, datasg
            mov ds, ax
            mov si, 0
    
            call letterc
    
            mov ax, 4c00H
            int 21H
    
    letterc:    mov cl,[si]
    			mov ch,0 d 076a:0d
    			jcxz done 
    			cmp cl,97 
    			jb skip 
    			cmp cl,122 
    			ja skip 
    			and cl, 11011111B
    			mov [si],cl 
    		skip:	inc si 
    				jmp short letterc 
    		
    done: 	ret 
    
    codesg ends
    
    end start
    

    内中断

    在中断过程中,寄存器的入栈顺序是标志寄存器,CS,IP,而iret的出栈顺序为IP,CS,标志寄存器,刚好对应。
    中断处理程序的cs和ip存放在0000:0000--0000:03FF 中,即中断向量表。
    一般,0000:0200--0000:02FF的256字节是空的,是一段安全的内存空间。
    ip在低地址,cs在高地址
    N号中断的处理程序的ip在4N,cs在4N+2
    一个中断处理程序占4个字节,前两个字节是ip,后两个是cs

    编程处理0号中断:

    1. 在内存0:0200处安装do0的代码,将0号中断处理程序的入口地址设置为0:0200,即0000:0000为0200,0000:0002为0
    2. do0的代码不在程序执行时执行,只在发生除法溢出时执行
    assume cs:code 
    
    code segment
    start:	mov ax,cs 
    		mov ds,ax 
    		mov si,offset do0 
    		mov ax,0 
    		mov es,ax 
    		mov di,0200h 
    		mov cx,offset do0end - offset do0   ;计算do0程序段的长度  
    		cld 
    		rep movsb  
    		
    		mov ax,0 
    		mov ds,ax 
    		mov dx,0200h 
    		mov ds:[0],dx     ;0号中断处理程序的ip 
    		mov dx,0 
    		mov ds:[2],dx 		; cs   
    		
    		mov ax,4c00h 
    		int 21h  
    		
    do0:   jmp short do0start                      ;此程序将要放到内存0:0200处
    	   db "Not good, 0# INT!",0
    
    do0start:  mov ax,0 
    	   mov ds,ax 
    	   mov ax,0B800h 
    	   mov es,ax 
    	   mov si,0202h 
    	   mov di,7*160+20 
    	   mov ch,0
    s:	   mov cl,[si]
    	   jcxz done 
    	   mov es:[di],cl 
    	   mov cl,02h 
    	   mov es:[di+1],cl    
           inc si  
    	   add di,2  
           jmp short s 	   
    done:  mov ax,4c00h 
    	   int 21h 
    do0end: nop
    	
    code ends 
    end start 
    

    单步中断

    CPU在执行完一条指令后,如果监测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。
    单步中断的中断类型码是1
    引发的中断过程如下:

    1. 取得中断类型码1;
    2. 标志寄存器入栈,TF,IF设为0
    3. CS,IP入栈
    4. (IP)=(4), (cs)=(6)
      如果TF=1,则执行一条指令后cpu就要去执行1号中断处理程序。
      debug提供了单步中断的中断处理程序,功能为显示所有寄存器的内容后等待输入命令。
      在debug中,使用t命令执行指令时,debug将TF设置为1,使得cpu工作于单步中断方式下,则在cpu执行完这条指令后便引发单步中断,执行单步中断的中断处理程序。
      总之,当TF=1时,cpu在执行完一条指令后引发单步中断,转去执行中断处理程序。执行完中断处理程序后,返回原来的位置继续。

    int指令

    cpu执行 int n 指令,相当于引发一个n号中断的中断过程,过程如下:

    1. 取中断类型码
    2. 标志寄存器入栈,IF=0,TF=0
    3. CS,IP入栈
    4. (ip)=(4n),(cs)=(4n+2)

    例1
    编写int 7ch 的中断处理函数并安装
    在中断例程的最后,使用iret指令
    iret指令的功能为:
    pop ip
    pop cs
    popf
    中断程序及安装:

    assume cs:code 
    
    code segment 
    start: mov ax,cs 
    	   mov ds,ax 
    	   mov si,offset sqr  
    	   mov ax,0 
    	   mov es,ax 
    	   mov di,200h 
    	   mov cx, offset sqrend - offset sqr 
    	   cld
    	   rep movsb 
    	   
    	   mov ax,0 
    	   mov ds,ax 
    	   mov ax,200h 
    	   mov ds:[7ch*4],ax    ;修改7ch中断的中断处理函数的ip
    	   mov ax,0
    	   mov ds:[7ch*4+2],ax 
    	   
    	   mov ax,4c00h
    	   int 21h 
    
    sqr:   mul ax 
    	   iret
    	   
    
    sqrend: nop
    
    code ends 
    end start  
    

    测试程序:

    assume cs:code 
    
    code segment 
    start: mov ax,3456
    	   int 7ch 
    	   
    	   add ax,ax 
    	   adc dx,dx 
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start   
    

    例2
    转换字符串为大写
    中断例程及安装:

    assume cs:code 
    
    code segment 
    start: mov ax,cs 
    	   mov ds,ax 
    	   mov si,offset sqr  
    	   mov ax,0 
    	   mov es,ax 
    	   mov di,200h 
    	   mov cx, offset sqrend - offset sqr 
    	   cld
    	   rep movsb 
    	   
    	   mov ax,0 
    	   mov ds,ax 
    	   mov ax,200h 
    	   mov ds:[7ch*4],ax    ;修改7ch中断的中断处理函数的ip
    	   mov ax,0
    	   mov ds:[7ch*4+2],ax 
    	   
    	   mov ax,4c00h
    	   int 21h 
    
    sqr:   mov cl,ds:[si]
    	   mov ch,0 
           jcxz done 
           and cl,11011111b 
    	   mov ds:[si],cl 
    	   inc si 
    	   jmp short sqr 
    	   
    done:  iret	   
    	   
    
    sqrend: nop
    
    code ends 
    end start  
    

    用到中断的程序:

    assume cs:code 
    
    data segment 
    	db 'conversation',0
    data ends 
    
    code segment 
    start: mov ax,data
    	   mov ds,ax 
    	   mov si,0
    	   int 7ch 
    	   
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start   
    

    例3
    用int 7ch实现loop的功能
    中断程序:

    assume cs:code 
    
    code segment 
    start: mov ax,cs 
    	   mov ds,ax 
    	   mov si,offset lp  
    	   mov ax,0 
    	   mov es,ax 
    	   mov di,200h 
    	   mov cx, offset lpend - offset lp 
    	   cld
    	   rep movsb 
    	   
    	   mov ax,0 
    	   mov ds,ax 
    	   mov ax,200h 
    	   mov ds:[7ch*4],ax    ;修改7ch中断的中断处理函数的ip
    	   mov ax,0
    	   mov ds:[7ch*4+2],ax 
    	   
    	   mov ax,4c00h
    	   int 21h 
    
    lp:    push bp 
    	   mov bp,sp 
    	   dec cx 
    	   jcxz done 
    	   add ss:[bp+2],bx  
    done:  pop bp 
           iret 
    
    lpend: nop
    
    code ends 
    end start  
    

    测试程序:

    assume cs:code 
    
    code segment 
    start: mov ax,0b800h 
    	   mov es,ax 
    	   mov di,160*10 
    	   mov bx,offset s - offset se 
    	   mov cx,80 
    s:     mov byte ptr es:[di],'!'
    	   add di,2 
    	   int 7ch     ; int时,压栈的ip是se的位置,此ip+bx可以得到s的位置  
    se:    nop 
    	   
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start   
    

    int 10h

    mov ah,2    ; 2号子程序:放置光标  
    mov bh,0    ; 第0页
    mov dh,5    ; dh中放行号
    mov dl,12   ; dl中放列号 
    int 10h
    

    (ah)=2 表示调用第10h号中断例程的2号子程序,功能为设置光标位置

    mov ah,9 
    mov al,'a'   ;字符
    mov bl,7     ; 颜色属性
    mov bh,0     ; 第0页   
    mov cx,3     ; 重复个数  
    int 10h 
    

    9号子程序,功能为在光标位置显示字符。
    int 21h

    ds:dx 指向字符串   ;要显示的字符串需要用$作为结束符  
    mov ah,9 
    int 21 h
    

    在光标位置显示字符串,可以提供要显示的字符串的地址作为参数。
    实验13_1
    中断程序及安装:

    assume cs:code 
    
    code segment 
    start: 
    		mov ax,cs 
    		mov ds,ax 
    		mov si,offset intcode 
    		mov ax,0 
    		mov es,ax 
    		mov di,0200h 
    		mov cx, offset intcodeend - offset intcode 
    		cld 
    		rep movsb 
    		
    		mov ax,0 
    		mov ds,ax 
    		mov word ptr ds:[7ch*4],0200h
    		mov word ptr ds:[7ch*4+2],0 
    		
    		mov ax,4c00h
    		int 21h 
    
    
    intcode:    mov al,160 
    			mul dh 
    			mov di,ax 
    			add dl,dl 
    			mov dh,0 
    			add di,dx 
    			mov ax,0B800h 
    			mov es,ax 
    			mov dl,cl 
    			mov ch,0 
    			
    s:			mov cl,ds:[si]
    			jcxz done 
    			mov es:[di],cl 
    			mov es:[di+1],dl 
    			inc si 
    			add di,2 
    			jmp short s 
    done:       iret 
    intcodeend:  nop    
    
    code ends 
    end start 
    

    测试中断的程序:

    assume cs:code 
    
    data segment 
    	db "welcome to masm!",0 
    data ends 
    
    code segment 
    start:	mov dh,10 
    	    mov dl,10 
    		mov cl,2 
    		mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		int 7ch 
    		
    		mov ax,4c00h 
    		int 21h 
    code ends 
    end start 
    

    实验13_2
    中断程序及安装:

    assume cs:code 
    
    code segment 
    start: 
    		mov ax,cs 
    		mov ds,ax 
    		mov si,offset intcode 
    		mov ax,0 
    		mov es,ax 
    		mov di,0200h 
    		mov cx, offset intcodeend - offset intcode 
    		cld 
    		rep movsb 
    		
    		mov ax,0 
    		mov ds,ax 
    		mov word ptr ds:[7ch*4],0200h
    		mov word ptr ds:[7ch*4+2],0 
    		
    		mov ax,4c00h
    		int 21h 
    
    
    intcode:    dec cx 
    			jcxz done 
    			mov bp,sp 
    			add ss:[bp],bx 
    done:	    iret
    intcodeend:  nop    
    
    code ends 
    end start 
    

    测试程序:

    assume cs:code 
    
    code segment 
    start: mov ax,0b800h 
    	   mov es,ax 
    	   mov di,160*12 
    	   mov bx, offset s - offset se 
    	   mov cx,80 
    	   
    s:	   mov byte ptr es:[di],'!'
    	   add di,2 
    	   int 7ch 
    se:    nop 
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start 
    

    实验13_3

    assume cs:code
    
    code segment
    
    s1:		db 'Good, better, best,', '$'
    s2:		db 'Never let it rest,', '$'
    s3:		db 'Till good is better,', '$'
    s4:		db 'And better,best.', '$'
    s:		dw offset s1, offset s2, offset s3, offset s4
    row:	db 2, 4, 6, 8
    
    start: mov ax,cs 
    	   mov ds,ax 
    	   mov bx,offset s 
    	   mov si, offset row 
    	   mov cx,4 
    	   
    	ok:mov bh,0    ; 第0页
    	   mov dh,[si]   ; dh中放行号
    	   mov dl,0      ; dl中放列号 
    	   mov ah,2   ;2号子程序:放置光标 
    	   int 10h 
    	   
    	   mov dx,[bx]   ;ds:dx 指向字符串   要显示的字符串需要用$作为结束符
    	   mov ah,9    ;在光标位置显示字符串
    	   int 21h 
    	   inc si 
    	   add bx,2 
    	   loop ok 
    	   
    	   
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start 
    

    端口

    端口的读写

    读写指令只有两条: in 和 out
    in al,60h 从60h号端口读入一个字节
    out 21h, al 向21h端口写al
    注意,只能用al或者ax

    • 对0-255端口进行读写
      in al,20h
      out 20h,al
    • 对256-65535号端口进行读写,端口号放在dx中
    mov dx,3f8h 
    in al,dx 
    out dx,al
    

    CMOS RAM芯片

    包含128个存储单元
    该芯片靠电池供电,断电后仍可继续工作,信息不丢失。
    时钟占用0-0dh单元,其余大部分单元保存系统配置信息,供系统启动时BIOS程序读取。
    该芯片有两个端口,70h和71h,cpu通过这两个端口读写CMOS RAM
    70h为地址端口,存放要访问的CMOS RAM单元的地址;71h为数据端口,存放从选定的CMOS RAM单元中读取的数据,或者要写入的数据。
    读2号单元:

    1. 将2送入端口70h
    2. 从71h读出2号单元的内容
      编程读取CMOS RAM 2号存储单元内容:
    assume cs:code 
    
    code segment 
    start: mov al,2 
    	   out 70h, al 
    	   in al,71h 
    	   
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start  
    

    向 2号存储单元写入0:

    assume cs:code 
    
    code segment 
    start: mov al,2 
    	   out 70h, al 
    	   mov al,0 
    	   out 71h,al
    	   
    	   mov ax,4c00h 
    	   int 21h 
    code ends 
    end start  
    

    shr, shl

    shl 逻辑左移,将一个寄存器或者内存单元中的数据向左移位,最后移出的一位写入CF。

    mov al,01001000b 
    shl al,1
    

    执行后 (al)=10010000B, CF=0
    如果移位位数大于1,必须将位数放在cl中。
    比如:

    mov al,01010001B
    mov cl,3 
    shl al,cl 
    

    执行后 (al)=10001000b, CF=0

    计算 (ax)=(ax)*10

    assume cs:code 
    
    code segment 
    start:  mov bx,ax 
    	    shl ax,1 
    		mov cl,3 
    		shl bx,cl 
    		add ax,bx 
    		
    		mov ax,4c00h 
    		int 21h 
    code ends 
    end start
    

    CMOS RAM 中存储的时间信息

    秒: 00h
    分: 02H
    时: 04H
    日: 07H
    月: 08H
    年: 09H
    存的数据以BCD码格式,00010001 表示 11

    实验14
    显示当前日期和时间

    assume cs:code 
    
    data segment 
    	db 09h,08h,07h,04h,02h,00h 
    	db '// :: '
    data ends
    
    code segment 
    start:  mov ax,0b800H 
    	    mov es, ax 
    		mov di, 160*10+20 
    		mov ax,data 
    		mov ds,ax 
    		mov si,0 
    		mov dl,02h 
    		
    		mov cx,6 
    s:      push cx
    		mov al,ds:[si]
    	    out 70h,al 
    		in al,71h 
    		call show 
    		mov al, ds:[6+si]
    		mov byte ptr es:[di],al   
    		mov es:[di+1],dl 
    		add di,2 
    		inc si 
    		pop cx 
    		loop s 
    		
    		mov ax,4c00h 
    		int 21h 
    		
    		
    show:   mov ah,al 
    		mov cl,4 
    		shr ah,cl 
    		add ah,30h 
    		mov es:[di],ah 
    		mov es:[di+1],dl   
    		add di,2 
    		mov ah,al 
    		shl ah,cl 
    		shr ah,cl 
    		add ah,30h 
    		mov es:[di], ah 
    		mov es:[di+1],dl
    		add di,2 
    		ret 
    code ends 
    end start
    

    外中断

    外中断源有两类:可屏蔽中断和不可屏蔽中断
    可屏蔽中断是cpu可以不响应的外中断。cpu是否响应可屏蔽中断,要看标志寄存器IF位的设置。
    当cpu检测到可屏蔽中断信息时,如果IF=1,则cpu响应中断,引发中断过程; 若IF=0, 则不响应可屏蔽中断。
    可屏蔽中断信息来自于cpu外部,中断类型码是通过数据总线送入cpu的。
    内中断的中断类型码是cpu内部产生的。
    中断过程中将IF设置为0的原因:在进入中断处理程序后,禁止其他的可屏蔽中断。
    如果在中断处理程序中需要处理可屏蔽中断,可以用指令将IF设为1.
    sti: 设置IF=1
    cli: 设置IF=0
    不可屏蔽中断是cpu必须响应的外中断。当cpu检测到不可屏蔽中断信息时,则在执行完当前指令后,立即响应,引发中断过程。
    8086cpu不可屏蔽中断的中断类型码固定为2,中断过程中不需要取中断类型码。
    不可屏蔽中断中断过程:

    1. 标志寄存器入栈, IF=0, TF=0
    2. CS,IP入栈
    3. (ip)=8, (cs)=10

    键盘

    键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关进行扫面。按下一个键时,开关接通,该芯片就产生一个扫描码,扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60h。松开按下的键时,也产生一个扫描码,扫描码说明了松开的键在键盘上的位置。松开按键时产生的扫描码也被送入60h端口中。
    按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。
    扫描码长度为一个字节,通码的第七位为0,断码的第七位为1. 断码=通码+80h

    BIOS提供了int 9中断例程,用于进行基本的键盘输入处理,主要工作如下:

    1. 读出60h端口中的扫描码
    2. 如果是字符键的扫描码,将该扫描码和对应的字符码(ASCII码)送入内存中的BIOS键盘缓冲区。
      键盘的处理过程:
      键盘的输入到达60h端口时,相关芯片向cpu发出中断类型码为9的可屏蔽中断信息
      cpu检测到中断信息后,如果IF=1,则响应中断,去执行int 9中断例程
      如果是控制键(ctrl)和切换键(caps),则将其转变为状态字节,写入内存中存储状态字节的单元
      BIOS键盘缓冲区是系统启动后,BIOS用于存放int 9中断例程所接收的键盘输入的内存区
      该内存区可以存储15个键盘输入,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码
      0040:17 单元存储键盘状态字节,记录了键盘控制键和切换键的状态,

    编写int 9例程,每次按下esc, 改变显示字符颜色:

    assume cs:code 
    
    stack segment 
    	db 128 dup(0) 
    stack ends 
    
    data segment 
    	dw 0,0 
    data ends 
    
    code segment 
    start:  mov ax,stack 
    		mov ss, ax 
    		mov sp, 128 
    		mov ax,data  
    		mov ds,ax        ; ds: data  
    		mov ax,0 
    		mov es, ax       ; es:0
    		push es:[9*4]
    		pop ds:[0] 
    		push es:[9*4+2]
    		pop ds:[2]
    		mov word ptr es:[9*4], offset int9 
    		mov ax,cs 
    		mov es:[9*4+2], ax    
    		
    		
    		mov ax,0b800h 
    		mov es,ax          ;es: 0b800h 
    		mov ah,'a'
    		mov al,02h
    		mov es:[160*10+21],al
    s:		mov es:[160*10+20],ah 
    		call delay 
    		inc ah 
    		cmp ah,'z'
    		jna s 
    		
    		mov ax,0          
    		mov es, ax        ; es: 0
    		push ds:[0]
    		pop es:[9*4]
    		push ds:[2]
    		pop es:[9*4+2]
    		
    		mov ax,4c00h 
    		int 21h 
    	
    delay:  
    		push ax
    		push dx 
    		mov dx,10 
    		mov ax,0
    		
    s1:     sub ax,1 
    		sbb dx,0 
    		cmp ax,0 
    		jne s1 
    		cmp dx,0 
    		jne s1 
    		pop dx 
    		pop ax 
    		ret 
    
    int9:   push es 
    		push ax 
    		push bx 
    		
    		in al,60h 
    		
    		pushf  				; 标志寄存器入栈
    		pushf 
    		pop bx 
    		and bh, 11111100b  ; IF,TF 置为0  
    		push bx 
    		popf 
    		
    		call dword ptr ds:[0]     ; 调用原来的int 9程序,最后会pop IP, pop CS, pop 标志寄存器
    		cmp al,1 
    		jne int9ret 
    		mov ax,0b800h 
    		mov es,ax 					; es: 0b800h 
    		inc byte ptr es:[160*10+20+1]
    		
    int9ret:  pop bx 
    		  pop ax 
    		  pop es 
    		  iret 
    		
    	
    code ends 
    end start  
    		
    

    安装int 9例程,实现按下f1键,改变dos窗口颜色

    assume cs:code 
    
    stack segment 
    	db 128 dup (0)
    stack ends
    
    code segment 
    start: mov ax, 0
    	   mov es, ax       ; es:0
    	   mov di,0204h 
    	   mov ax, cs 
    	   mov ds, ax       ; ds: cs  
    	   mov si, offset int9 
    	   mov cx, offset int9end - offset int9 
    	   cld
    	   rep movsb 
    	   
    	   mov ax,stack 
    	   mov ss,ax 
    	   mov sp,128 
    	   
    	   push es:[9*4]
    	   pop es:[200h]
    	   push es:[9*4+2]
    	   pop es:[202h]
    	   
    	   mov ax, 204h 
    	   mov es:[9*4], ax 
    	   mov ax,0 	
    	   mov es:[9*4+2], ax
    		
    	   mov ax,4c00h 
    	   int 21h 
    	   
    int9:	push ax 
    		push ds  
    		push cx 
    		push bx 
    		
    		in al,60h 
    		pushf 
    		call dword ptr cs:[200h]
    		
    		cmp al,3bh 
    		jne int9ret 
    		mov ax,0b800h 
    		mov es,ax 
    		mov bx,1 
    		mov cx,2000 
    	s:  inc byte ptr es:[bx]
    		add bx,2 
    		loop s   
    		
    int9ret:  pop bx 
    		  pop cx 
    		  pop ds 
    		  pop ax 
    		  iret  
    		
    int9end: nop  
    
    code ends 
    end start
    

    实验15 安装新的int9 中断例程

    assume cs:code 
    
    stack segment 
    	db 128 dup (0)
    stack ends
    
    code segment 
    start: mov ax, 0
    	   mov es, ax       ; es:0
    	   mov di,0204h 
    	   mov ax, cs 
    	   mov ds, ax       ; ds: cs  
    	   mov si, offset int9 
    	   mov cx, offset int9end - offset int9 
    	   cld
    	   rep movsb 
    	   
    	   mov ax,stack 
    	   mov ss,ax 
    	   mov sp,128 
    	   
    	   push es:[9*4]
    	   pop es:[200h]
    	   push es:[9*4+2]
    	   pop es:[202h]
    	   
    	   mov ax, 204h 
    	   mov es:[9*4], ax 
    	   mov ax,0 	
    	   mov es:[9*4+2], ax
    		
    	   mov ax,4c00h 
    	   int 21h 
    	   
    int9:	push ax 
    		push es  
    		push cx 
    		push bx 
    		
    		in al,60h 
    		pushf 
    		call dword ptr cs:[200h]
    		
    		cmp al,9eh 
    		jne int9ret 
    		mov ax,0b800h 
    		mov es,ax 
    		mov bx,0 
    		mov cx,2000 
    	s:  mov byte ptr es:[bx],'A'
    		add bx,2 
    		loop s   
    		
    int9ret:  pop bx 
    		  pop cx 
    		  pop es 
    		  pop ax 
    		  iret  
    		
    int9end: nop  
    
    code ends 
    end start
    
  • 相关阅读:
    汇编语言 第三章 寄存器
    汇编语言 第二章
    实验一 查看CPU和内存,用机器指令和汇编指令教程
    nginx的log、upstream和server
    高并发情况下Linux系统及kernel参数优化
    二进制方式安装docker(非root用户启动docker)
    redis
    redis配置文件详解
    Keepalived+LVS实现LNMP网站的高可用部署
    Nginx location相关配置说明
  • 原文地址:https://www.cnblogs.com/FEIIEF/p/13111042.html
Copyright © 2011-2022 走看看