汇编基本代码与debug的使用

32位基本汇编程序

首先,理解一下简单汇编语言程序

;程序的顶部省略了一些声明
;程序执行两个数相加,并将结果保存在寄存器中
main PROC
    mov eax, 5               ;将数字 5 送入 eax 寄存器
    add eax, 6               ;eax 寄存器加 6

    INVOKE ExitProcess, 0    ;程序结束(调用 Windows 服务(也被称为函数)ExitProcess 停止程序,并将控制权交还给操作系统)
main ENDP                    ;主程序结束的标记

为了将加法运算的结果保存在变量 sum 中,需要增加一些标记,或声明,用来标识程序的代码和数据区

伪指令:
是嵌入源代码中的命令,由汇编器识别和执行。伪指令不在运行时执行,但是它们可以定义变量、宏和子程序;为内存段分配名称,执行许多其他与汇编器相关的日常任务

.data                          ;此为数据区
sum DWORD 0                    ;定义名为sum的变量(DWORD 伪指令告诉汇编器在程序中为一个双字变量保留空间)

.code                          ;此为代码区
main PROC
    mov eax,5                  ;将数字5送入而eax寄存器
    add eax,6                  ;eax寄存器加6
    mov sum,eax

    INVOKE ExitProcess,0       ;结束程序
main ENDP

;.code 和 .data 伪指令标记的代码和数据区,被称为段

为了使其能运行,要添加必要的声明:

; AddTwo.asm -两个 32 位整数相加

.386                                ;这是 .386 伪指令,它表示这是一个 32 位程序,能访问 32 位寄存器和地址
.model flat,stdcall                 ;选择了程序的内存模式(flat),并确定了子程序的调用规范(称为 stdcall,32 位 Windows 服务要求使用 stdcall 规范)
.stack 4096                         ;为运行时堆栈保留了 4096 字节的存储空间,每个程序都必须有
ExitProcess PROTO, dwExitCode:DWORD ;声明了 ExitProcess 函数的原型,可以将其看作为给 Windows 操作系统的返回值
                                    ;返回值为零,则表示程序执行成功;而任何其他的整数值都表示了一个错误代码

.code
main PROC
mov  eax,5                          ;将数字5送入eax寄存器
add  eax,6                          ;eax寄存器加6

INVOKE ExitProcess,0
main ENDP
END main                            ;用 end 伪指令来标记汇编的最后一行

引入变量sum后:

;AddTowSum.asm

.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.data
sum DWORD 0

.code
main PROC
    mov eax,5
    add eax,6
    mov sum,eax

    INVOKE ExitProcess,0
main ENDP
END main

但现在大多都是64位系统,那这个代码岂不凉了?

64位基本汇编程序

HelloWorld

;完整段的Hello World程序
					 
STACKS  SEGMENT      
    db 100 dup(?)    
STACKS  ENDS	   

DATAS  SEGMENT		
     STRING  DB  'Hello World!',13,10,'$'
DATAS  ENDS			 

CODES  SEGMENT		
     ASSUME    CS:CODES,DS:DATAS,SS:STACKS	
     
START:				 
     MOV  AX,DATAS
     MOV  DS,AX		   
     LEA  DX,STRING  
     
     MOV  AH,9		
     INT  21H		
   
     MOV  AH,4CH	 
     INT  21H		
CODES  ENDS			 
    END   START	

因为初学,给出详细注释:

					 ;完整段的Hello World程序			 
STACKS  SEGMENT      ;定义堆栈段
    db 100 dup(?)    ;堆栈段长100字节, db即字节,如果是用dw就是字
STACKS  ENDS	     ;堆栈段结束

DATAS  SEGMENT		 ;定义数据段
     STRING  DB  'Hello World!',13,10,'$'
     
    				 ;STRING  DB  'Hello World!',0dh,0ah,24h
    				 ;13,10分别是回车键,换行键的ASCII码
     				 ;0dh,0ah分别是回车键,换行键的ASCII码
     				 ;'$'是DOS功能调用INT 21H中9号功能要求的要显示字符串的结束标志(24h也可以,因为24h就是'$')
  
DATAS  ENDS			 ;数据段结束

CODES  SEGMENT		 ;定义代码段
     ASSUME    CS:CODES,DS:DATAS,SS:STACKS	;确定cs,ds,ss,指向的逻辑段
     
START:				 ;程序入口
     MOV  AX,DATAS
     MOV  DS,AX		 ;将段地址DATAS送入DS中
     
     LEA  DX,STRING  ;将字符串地址送人DX中
     
    				 ;mov DX,offset STRING 
    				 ;取得STRING的偏移地址,也就是在DS段中的偏移地址
    				
     MOV  AH,9		 ;AH中的9号功能表示要显示一行字符串
     INT  21H		 ;调用INT 21H的9号中断
   
     MOV  AH,4CH	 
     INT  21H		 ;两个连用表示程序结束
CODES  ENDS			 ;定义代码段结束
    END   START		 ;程序结束

解释:
在这里插入图片描述
MOV AX,DATAS
MOV DS,AX
分两步的原因:

将伪段地址放入AX中,DATAS不是指令,而是伪指令,实际上是一个动态的内存地址,要想运行,必须先把DATAS放入到DS中,但是80X86中规定,内存数不可以直接装入段寄存器,所以才会有这样的一次中转,mov ds,ax就是将段地址装入段寄存器,最终达到了段地址装入段寄存器的目的

assume

要用assume把逻辑段跟段寄存器(ds,cs,ss等)对应起来,因为原来的DOS找到的空闲内存的地址不是固定的,无法找到一个地址在任何时候都是空闲的。于是DOS需要可以重定位的程序,而当时的定位方式就是设置段寄存器的值,使该程序能在可分配(空闲)的内存中可用。那就需要知道某个段被重定位的时候,需要修改哪个段寄存器的值才能正确执行。

assume提供这种段和重定位代码时需要对应修改的寄存器的关系给编译器,编译器再这个信息写到二进制文件中去,声明格式一般是:

assume ds:data(数据段名称,可任意),cs:code(代码段名称,可任意),ss:stuck(堆栈段名称,可任意)

lea dx,string

把string的偏移地址存到dx,这里的 lea 是load effective address的缩写

int 21h

dos功能调用,含有近100个功能,提供了应用程序所需要的大多数服务,包括打开文件、关闭文件、读文件、写文件、读键盘输入、写显示屏、读取或设置系统日期和时间,以及一大堆控制变量,通过给AH寄存器赋值,然后调用INT 21H指令,计算机就会根据AH寄存器中的值执行相应的操作,之后查表即可,给出链接:DOS系统功能调用表(INT 21H)

当然,HelloWorld也可以简化一下:

;简化段的Hello World程序
.MODEL SMALL
.DATA
     STRING  DB  'Hello World!',13,10,'$'
.STACK
.CODE
.STARTUP
     LEA  DX,STRING
     MOV  AH,9
     INT  21H
.EXIT
     END

解释:

汇编程序通常有tiny、small、huge等模式

(1)tiny模式通常和内存映像文件(com)文件对应,代码、数据同段且不超过64k
(2)small模式通常代码段、数据段均不超过64k,代码段中的跳转和call均为near模式,即使用段偏移就能定位
(3)huge模式代码、数据段等不受64k限制,跳转、call指令允许使用far模式

STARTUP和.EXIT是汇编程序MASM中提供的二组简化的代码伪指令,用法可看:程序开始和结束伪操作,在此不赘述

求带符号数绝对值

编写程序段,求AX中存放的带符号数的绝对值,结果存RES单元

data segment
	res dw ?
data ends

code segment
	;确定cs,ds指向的逻辑段
	assume cs:code,ds:data 
start:
    mov ax,data
    mov ds,ax   ;将段地址DATAS送入DS中
    
    mov ax,9a00H
    cmp ax,0
    jge isp     ;大于或等于转移指令
    neg ax
isp:
	mov res,ax
	
    mov ah,4ch  ;带返回码结束
    int 21h
    
code ends
	end start

求两数的和

;完整段的求3+5的和
STACKS  SEGMENT
      DB  128 DUP (?)
STACKS  ENDS

DATAS  SEGMENT
    FIVE  DB  5
DATAS  ENDS

CODES  SEGMENT
     ASSUME    CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    MOV AL,FIVE
    ADD AL,3
    ADD AL,30H
    MOV DL,AL
    MOV AH,2
    INT 21H
    
    MOV AH,4CH
    INT 21H
CODES  ENDS
    END  START

加上注释:

;完整段的求3+5的和
STACKS  SEGMENT
      DB  128 DUP (?)
STACKS  ENDS

DATAS  SEGMENT
    FIVE  DB  5
DATAS  ENDS

CODES  SEGMENT
     ASSUME    CS:CODES,DS:DATAS,SS:STACKS
START:
    MOV AX,DATAS
    MOV DS,AX
    MOV AL,FIVE ;将字节变量5存入到寄存器中,也可MOV AL,5
    ADD AL,3	;将寄存器中的值取出,加上3后放回
    ADD AL,30H	;需要转化成ASCII码才能进行显示,8对应ASCII码为38H,故加上30H
    MOV DL,AL	;将待输出字符的ASCII码传到DL中去
    MOV AH,2	;02命令放入AH中,表示输出DL
    INT 21H		;DOS系统调用放入AH的命令
    
    MOV AH,4CH	;结束本程序,返回 DOS 操作系统
    INT 21H		;DOS系统调用放入AH的命令
CODES  ENDS
    END  START

也可以简化成这样:

;简化段的求3+5的和
.MODEL SMALL
.DATA
    FIVE  DB    5

.STACK
      DB  128 DUP (?)
.CODE

.STARTUP
    MOV AL,FIVE
    ADD AL,3
    ADD AL,30H
    MOV DL,AL
    MOV AH,2
    INT 21H
    
.EXIT 0
    END 

计算1+2+3+…+10,将结果显示在屏幕上

code segment
	;段间远调用: 调用程序和子程序不在同一代码段中,可以被相同或不同代码段的程序调用
	main proc far
	assume cs:code;确定cs指向的逻辑段
start:
	;push寄存器:将一个寄存器中的数据入栈
	;pop寄存器:出栈用一个寄存器接收数据
	push ds    
	sub ax,ax   ;初值0
	push ax
	mov bx,0ah  ;初值10
	mov cx, 0ah ;初值10
sum1:
	add ax,bx   ;10加到1
	dec bx
	;loop指令用来实现循环功能,cx(寄存器)存放循环次数,CPU执行loop指令的时候
	;先cx=cx-1,然后判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行
	loop sum1
printit:
	;需要将ax中的结果以十进制形式输出
	;将结果的两位数分别存于低位和高位中
	mov bl,10
	div bl
	
	;AL是商
	mov ch,ah   ;将余数AH保存到al中
	add al,30h
	mov dl,al
	mov ah,2
	int 21h
	
	;AH是余数
	add ch,30h  ;需要转化成ASCII码才能进行显示,故加上30H
	mov dl,ch
	mov ah,2    ;显示输出,且dl=输出字符
	int 21h
	
	;子程序定义结束
	ret
	main endp
	code ends
end

main proc far

proc是定义子程序的伪指令,位置在子程序的开始处,它和endp分别表示子程序定义的开始和结束两者必须成对出现。

far是该子程序的属性,决定调用程序和子程序是否在同一代码段
如下:为子程序定义及说明,

子程序名 PROC NEAR (FAR )
……
ret
子程序名 ENDP

子程序名为符合语法的标识符
NEAR属性(段内近调用): 调用程序和子程序在同一代码段中,只能被相同代码段的其他程序调用;
FAR属性(段间远调用): 调用程序和子程序不在同一代码段中,可以被相同或不同代码段的程序调用.

div:

1:除数为8位或者16位(即字节型或字型),在寄存器或内存单元中
2: 被除数在AX 或者 AX和DX中( 注意,后面是AX和DX,AX存放低16位,DX存放高16位)

除数 被除数
8161632(AXDX)

与此对应的,当除数为8位时,商存放在AL寄存器中,余数存放在AH寄存器中,当除数为16位时,商在AX中,余数在DX中

3:指令格式

div reg //reg表示一个寄存器
div 内存单元

如:

mov ax,17 
mov bl,3    
div bl

求分段函数值

已知变量x为16位带符号数,分段函数的值要求保存到字单元y中,函数定义如下:当x=0时,y=0,当x大于0时,y=1,当x小于0的时候,y=-1

data	segment   ;数据段定义
	x  dw  -128
	y  dw  ?
data	ends

code	segment   ;代码段定义
	assume	cs:code,ds:data
	
start:	
	mov  ax,data
    mov  ds,ax    ;将段地址datas送入ds中
    
	mov  ax,x
    cmp  ax,0
    jg  ispn      ;jg:大于转移指令
	jz	iszn      ;jz(jUMP IF zERO)是此前的运算结果为0时跳转,反之不跳转
    mov  y,-1
    jmp  finish
ispn:	
	mov  y,1
    jmp  finish
iszn:   
	mov  y,0    
finish: 
	mov  ah,4ch
    int  21h
code    ends
        end  start

绘制矩形

CODES SEGMENT
   ASSUME CS:CODES
START:
  mov ah,00
  mov al,06h
  int 10h ;设置640*48016色彩色分辨率
  mov dx,50
back_1:
  mov cx,100
back_2:
  mov ah,0ch
  mov al,71h ;白底蓝色图 
  mov bh,0
  int 10h
  inc cx
  cmp cx,200
  jnz back_2
  inc dx
  cmp dx,150
  jnz back_1
CODES ENDS
  END START

给出注释:

CODES SEGMENT
   ASSUME CS:CODES
START:
  mov ah,00     ;是用来设定显示模式的服务程序
  mov al,06h	;设置图形模式
  int 10h 	    ;设置640*48016色彩色分辨率
  
  mov dx,50		;y坐标
back_1:
  mov cx,100	;x坐标
back_2:
  mov ah,0ch	;设定显示模式
  mov al,71h 	;设置图形模式,白底蓝色图 
  mov bh,0		;设定终止x坐标
  int 10h
  
  inc cx		;x++
  cmp cx,200
  jnz back_2	;如果cx没到200,就接着back_2,x++,画横线(x从100->200)
  
  inc dx		;如果cx等于200,y++,往下走一行接着画横线
  cmp dx,150	
  jnz back_1	;如果dx没到150,就到back_1让x=100,接着back_2了
  				;如果y轴到了150,矩形画完了,程序结束
CODES ENDS
  END START

解释:

首先得理解一下汇编语言中的标志位,可看这个:汇编语言中的标志位,然后了解一下cmp比较指令对标志寄存器的影响,再看一下这个:cmp比较指令对标志寄存器的影响,而且对条件转移指令要有一定了解,还可以看这个:条件转移指令详解,之后再读跳转部分的代码就会好很多了😅,其实难点就在cmp和jnz的结合上

debug基本操作

debug下最常用的调试指令为六个

(1)R :查看更改cpu寄存器内容
(2)D:查看内存中内容
(3)E:改写内存中内容
(4)U:将内存中机器指令翻译成汇编指令
(5)T:执行一条机器指令
(6)A:以汇编格式在内存中写入一条指令

具体可看:常用命令的使用

对照 求两数的和 部分的代码,来演示一下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
大概就这样,部分寄存器的变化原理没有搞懂,以后深入研究吧

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值