【汇编系列-1】汇编语言的基本程序结构及helloworld示例(内含“最简化程序”呈现过程proc和宏macro的基本使用方法)

刚开始学习微机原理课程的时候,对汇编语言编程曾经有种无从下手的感觉。经过一个学期的练习,对汇编语言熟悉了不少,想要整理一篇文章来帮助一下刚入门的汇编小白。

一、编程环境

简单先提一句编程环境的选择,我比较建议VScode,安装相对比较方便,而且功能比较齐全,可以tab补全、代码折叠,也可以方便地查看寄存器内容。而且,VScode软件的启动速度挺快的,所占用的存储空间也很能接受,并不是很笨重的软件!

安装:VScode直接去官网下载就行了https://code.visualstudio.com/,然后安装VScode里面的MASM/TASM插件即可,安装过程可以参考其他文章,这里不再赘述。

二、helloworld示例及运行

新建一个文件,选择assembly(DOS)语言,然后把下面这段代码复制过去:

TITLE HelloWorld

DATA SEGMENT
    DispTEXT           DB  10,'HelloWorld','$'            
DATA ENDS

STACKS SEGMENT
    DW  80  DUP(?)      ;duplicate 80 times,prepare 80Byte for stacks
STACKS ENDS

.486
CODE    SEGMENT    USE16
        ASSUME      CS:CODE,DS:DATA,SS:STACKS
    BEG:
        ;Initialization
        MOV     AX,DATA     
        MOV     DS,AX

        ;where you can add your codes
        MOV     AH,09H
        LEA     DX,DispTEXT
        INT     21H

        ;Ending
        MOV     AX,4C00H
        INT     21H
CODE ENDS
        END BEG

在VScode里面应该是这样的:

然后右键选择“运行当前程序(汇编+链接+运行)”,即可运行程序

运行结果如下图所示:

注:一般而言完整的程序有三个段:数据段、堆栈段和代码段。
数据段中定义变量;
堆栈段中定义堆栈空间(使用“过程”,或者主动PUSH和POP都需要一定堆栈空间)
代码段书写代码:
开头要初始化数据区(标准格式,一般都要写的);
结尾必须以MOV AX,4C00H INT 21H结束(先这样写就行了,后面会慢慢明白意思的);
中间就可以写代码啦,代码较长时建议使用模块化程序设计(利用过程PROC以及宏MACRO,两者区别后文会阐述)。

最前面的TITLE为程序标题(可写可不写,为程序的说明)。
注释用英文分号;开头。

HelloWorld程序用到的为字符串的显示,其中DispTEXT变量定义一开始的“10”为换行符的ASCII码(不使用回车的ASCII码,原因是汇编语言中回车会使字符从一行的最开始进行显示,而非换行显示,会出现覆盖);字符串必须以$结尾,否则程序不知道显示到哪里结束;使用INT21H(DOS)的09号功能,显示以$为结尾的字符串,字符串的首地址预先存在DX寄存器中
(更多常用DOS功能后面不咕咕咕的话可能会出一期)

三、一些排错(如果上一步正确运行了可以不看)

  1. 假如你使用的环境不支持32位寄存器,可以把代码中的“.486”和“USE16”同时删除。 后果是程序中不允许出现EAX、EBX、ECX、EDX等32位寄存器,否则会报错。
  2. doxbox和jsbox的切换:在左侧 “扩展”-MASM/TASM的“扩展设置”中切换。

    一般而言,doxbox和jsbox没有太大的区别,我个人觉得jsbox的显示界面(左右分屏显示)比较舒服,而dosbox一般以弹窗的形式显示运行结果。
    (下图为jsbox的运行界面:

    注:jsbox对于INT15H的86H延时功能好像不支持,如果运行结果有问题可以尝试换成dosbox运行。
		MOV     AH,86H
        MOV     CX,200
        MOV     DX,0   ;CX,DX:延迟时间(微秒)
        INT     15H

四、查看寄存器的debug方法

作为初学者,一般需要通过查看寄存器的方法了解汇编语言的运作方式,偶尔也会通过查看寄存器进行debug。
右键选择“运行当前程序(汇编+链接+调试)”,即可进入调试模式。

选择“View-CPU”即可查看寄存器,如下图所示:

一般用 (Fn+)F8进行调试(会跳过过程)
如果希望进入“过程”进入单步调试,可以使用 (Fn+)F7
结束本次调试 (Fn+)F9
注:假如鼠标被限制在了调试区域无法移动,可以按一下“Windows”按钮就可以让鼠标出来~

五、过程PROC的使用

将HelloWorld程序的主程序代码用“过程”包装后的代码如下:

TITLE HelloWorld(withPROC)

DATA SEGMENT
    DispTEXT           DB  10,'HelloWorld','$'            
DATA ENDS

STACKS SEGMENT
    DW  80  DUP(?)      ;duplicate 80 times,prepare 80Byte for stacks
STACKS ENDS

CODE    SEGMENT 
        ASSUME      CS:CODE,DS:DATA,SS:STACKS
    BEG:
        ;Initialization
        MOV     AX,DATA     
        MOV     DS,AX

        ;where you can add your codes
        CALL    A10DISP     ;call A10DISP to display "HelloWorld"

        ;Ending
        MOV     AX,4C00H
        INT     21H

    ;define a process
    A10DISP     PROC    NEAR
        MOV     AH,09H
        LEA     DX,DispTEXT
        INT     21H
        RET
    A10DISP     ENDP
CODE ENDS
        END BEG

注:

  1. PROC中的代码必须以RET(return)结尾,否则程序运行时无法正常结束、调用多个过程时无法正常返回;
  2. 主程序中通过CALL来调用过程;
  3. 过程的调用需要用到堆栈,所以堆栈错误时(如过程中PUSH了3次,却只POP了2次)会引起过程的调用错误,表现就是“程序跑飞了”,出现各种奇怪的跳转错误;
  4. 过程的命名一般以A10xxx,B10xxx等命名,便于分类整理;
  5. 一般单个程序的过程用NEAR就足够了,多个文件之间互相调用时才需要用到FAR。

六、宏MACRO的使用

将HelloWorld程序的主程序代码用“宏”包装后的代码如下:

TITLE HelloWorld(withMACRO)

;define a macro
DISPhw  MACRO
    MOV     AH,09H
    LEA     DX,DispTEXT
    INT     21H
    ENDM

DATA SEGMENT
    DispTEXT           DB  10,'HelloWorld','$'            
DATA ENDS

STACKS SEGMENT
    DW  80  DUP(?)      ;duplicate 80 times,prepare 80Byte for stacks
STACKS ENDS

CODE    SEGMENT 
        ASSUME      CS:CODE,DS:DATA,SS:STACKS
    BEG:
        ;Initialization
        MOV     AX,DATA     
        MOV     DS,AX

        ;where you can add your codes
        DISPhw

        ;Ending
        MOV     AX,4C00H
        INT     21H
CODE ENDS
        END BEG

注:

  1. 宏的定义一般放在程序的最开始,每个宏的定义以ENDM结束;
  2. 主程序中直接通过宏的名称来调用宏;

宏和过程的区别:

过程
程序汇编时完成展开CALL在程序执行时完成调用
直接传递和接受参数通过“寄存器”、“存储单元”堆栈传递参数
简化源程序,不能简化目标代码,增加内存空间缩短目标代码,节省内存空间
不增加执行时间保护恢复现场,增加时间开销

因此,在子程序本身较短,参数较多的情况下,用指令更有效;
一般而言,可执行文件被调用时,以过程的方式调用。
模块化程序设计时,也建议以过程的方式调用。

所以可知,HelloWorld程序比较合适的包装方式为“过程”而非“宏”。

七、完整程序示例

简单的冒泡排序(降序)并显示

TITLE BUBBLESORT

;==============================================DATAsegment============================================================================================
DATA SEGMENT
    numbers                 DB  78,23,-12,0,-2,99,-11,-67,9,56,44       ;11 numbers
DATA ENDS
;=================================================STACKS SEGMENT=======================================================================
STACKS SEGMENT
    DW  128  DUP(?)      ;duplicate 128 times,准备128Byte的空间
STACKS ENDS

;===================================================CODE SEGMENT=======================================================================
.486
CODE SEGMENT    USE16
        ASSUME      CS:CODE,DS:DATA
    BEG:
        ;Initialization
        MOV     AX,DATA     
        MOV     DS,AX
    A10MAIN     PROC    NEAR
        CALL    A11BUBBLESORT   
        CALL    A12DISPLAY            
        A10end:
        MOV     AX,4C00H
        INT     21H
    A10MAIN     ENDP

    ;===========================================PART1.BUBBLESORT=================================================================================
    A11BUBBLESORT  PROC    NEAR
        MOV     CX,10 ;记录外循环的次数

        L1:
        MOV     DI,CX ;记录当前内循环的次数
        MOV     BX,0 ; 记录当前比较的数字的位置
        L2:
        MOV     AL,numbers[BX]
        CMP     AL,numbers[BX+1]
        JGE     NEXT1
        XCHG    AL,numbers[BX+1]
        MOV     numbers[BX],AL
        NEXT1:
        INC     BX
        DEC     DI
        CMP     DI,0
        JNE     L2
        LOOP    L1 
        RET
    A11BUBBLESORT  ENDP 

    ;===========================================PART2.DISPLAY=================================================================================
    A12DISPLAY     PROC    NEAR
        MOV     CX,11
        MOV     SI,0
        Ldisp:
        MOV     BX,10
        PUSH    BX
        MOV     AX,0
        MOV     AL,numbers[SI] 
        TEST    AL,10000000B
        JZ      LTRANSFORM       ;非负数
        PUSH    AX
        MOV     AH,02H
        MOV     DL,'-'
        INT     21H
        POP     AX
        NEG     AL
        LTRANSFORM:     ;转换数字
        MOV     DX,0
        DIV     BX      ;DX,AX/BX = 商:AX,余数:DX
        PUSH    DX        
        CMP     AX,0
        JNE     LTRANSFORM
        DISP:
        POP     DX
        CMP     DX,BX
        JE      NEXTNUM   ;NEXT NUMBER
        MOV     AH,02H
        ADD     DL,30H
        INT     21H
        JMP     DISP
        NEXTNUM:
        CMP     SI,9
        JA      NEXT3
        MOV     AH,02H
        MOV     DL,','
        INT     21H
        NEXT3:
        INC     SI
        LOOP    Ldisp

        RET
    A12DISPLAY     ENDP
    
CODE ENDS
        END BEG
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值