汇编程序语言设计的一些小点

汇编学的时间好短,尽力速成一下程序设计,其他就靠背了

DOS功能调用

 1 - 键盘输入
- 2 - 屏幕输出 
- 3 - 辅助输入
- 4 - 辅助输出
- 5 - 打印器输出
- 6 - 直接控制台输入/输出
- 7 - 直接控制台输入,不回显
- 8 - 读取键盘不回显
- 9 - 显示字符串

- 0Ah -Buffered键盘输入

- 0Bh - 检查键盘输入

-4ch-结束正在运行的程序直接返回DOS

 当作一个模块来做

mov ah, 2    ; 功能号 2 - 输出字符
mov dl, al
int 21h      ; 输出

 

 1. mov ah, 2   设置ah寄存器为2,这对应DOS中断INT 21h的输出字符功能号。

2. mov dl, al   将al寄存器中的字符移动到dl寄存器中。DOS 21h号中断的输出字符功能需要将要输出的ASCII字符存放在dl寄存器中。

3. int 21h     触发DOS中断INT 21h,调用ah寄存器所指定的输出字符功能,将dl寄存器中的ASCII字符输出到屏幕上。

简单记一下就好

我们记住以上的模块就可以做相应的输入输出

PS:当我们输出字符串的时候,需要有$作为结尾,否则会超量输出

简单代码阅读

 

复制代码
.486

DATA SEGMENT USE16

    MESG1 db 'PLEASE INPUT YOUR USER: $'  
    MESG2 db 'PLEASE INPUT YOUR PASSWORD: $'
    U db 15  
    db ?
    db 15 dup(?)     
    PWD db 15 
    db ?
    db 15 dup(?)                  
    USER db '123'
    ULEN equ $-USER        
    PASSWORD db '0'
    MLEN equ $-PASSWORD   

    WELCOME db '-----------WELCOME!-----------$' 
    ERROR db '------ERROR!------$'

DATA ends

CODE SEGMENT USE16
assume CS:CODE, DS:DATA  

START:
    mov ax, DATA
    mov ds, ax

PRINT_USERNAME:  
    mov ah, 9    
    mov dx, offset MESG1
    int 21h

GET_USERNAME:   
    mov ah, 0Ah
    mov dx, offset U  
    int 21h
          
PRINT_NEWLINE:             ;换行
    mov ah, 2
    mov dl, 0ah      
    int 21h

PRINT_PWD:
    mov ah, 9
    mov dx, offset MESG2   
    int 21h   

GET_PASSWORD:
    mov ah, 0Ah
    mov dx, offset PWD  
    int 21h

CHECK_USER_PWD:

                    ;检查用户名
mov bx, offset USER  
mov si, offset U+2
mov cx, 3

check_user:
mov al, [bx]
cmp al, [si]  
jne PRINT_ERROR

inc bx
inc si
dec cx       ; 减少循环计数器
jnz check_user ; 如果cx不为零,则继续循环

;检查密码
mov bx, offset PASSWORD
mov si, offset PWD+2
mov cx, 1
check_pwd:
mov al, [bx]
cmp al, [si]
jne PRINT_ERROR
inc bx  
inc si
dec cx       ; 减少循环计数器
jnz check_pwd ; 如果cx不为零,则继续循环
;检查完成,用户名密码正确  
jmp welcomeTo
PRINT_ERROR: 
mov ah, 2    ; 功能号 2 - 输出字符
mov dl, 10   ; ASCII码值为10,表示换行
int 21h      ; 输出换行字符

mov ah, 9
mov dx, offset ERROR
int 21h

mov ah, 2    ; 功能号 2 - 输出字符
mov dl, 10   ; ASCII码值为10,表示换行
int 21h      ; 输出换行字符
jmp START
welcomeTo:
mov ah, 2    ; 功能号 2 - 输出字符
mov dl, 10   ; ASCII码值为10,表示换行
int 21h      ; 输出换行字符

mov ah, 9
mov dx, offset WELCOME  
int 21h

EXIT: 
mov ah, 4Ch
int 21h

CODE ends
end START
复制代码

 

以上这段代码没有使用ULEN和MLEN但进行了定义,这是对用户名长度和密码长度进行比较的,这段代码没有加入这部分

复制代码
DATA SEGMENT USE16
   ; 数据定义
DATA ends

CODE SEGMENT
  assume CS:CODE, DS:DATA
; 代码主体
CODE ends

end START
复制代码

 

我们首先看一下以上这段代码的结构或者框架,如上

.486就不说了

然后就是

DATA SEGMENT 
   ; 数据定义
DATA ends

这段数据定义后接着是代码段

CODE SEGMENT
  ; 代码主体
CODE ends

在代码段之后,我们就要指定整个程序的入口点

也就是

end START

从我刚才的描述可以发现,这里的START是指定的,它叫什么都无所谓,也可以叫常规的BEG,也可以叫abcd

因为我们迟早会指定一个标签

刚才的整段代码段分成下面的部分

复制代码
 ; 打印用户名段
    PRINT_USERNAME:
       ; 打印提示用户名的消息 

    ; 获取用户名段   
    GET_USERNAME:
      ; 调用DOS获取用户名存入缓冲区

    ; 打印密码段
    PRINT_PWD:
      ; 打印提示密码的消息
    
    ; 获取密码段
    GET_PASSWORD:
       ; 调用DOS获取密码存入缓冲区

    ; 检查验证段  
    CHECK_USER_PWD:
       ; 检查用户名密码是否匹配

    ; 输出结果段  
    PRINT_RESULT:
       ; 根据检查结果打印成功或失败消息 

    ; 结束段
    EXIT:
      ; 程序退出前的收尾工作
复制代码

 

当我们将这些分开之后,相关的功能就可以分开解决了

这些都是标签名,并不影响代码的运行,反而是大家都爱用的NEXT等等这些会可读性很差

初始化的问题

START:
    mov ax, DATA
    mov ds, ax

 

这里的就是将DATA段地址转入ax,再用ax初始化ds

不要直接转入ds

 

键盘输入与输出

阅读了我刚才的代码,可以发现我在DATA提供了

U db 15  
    db ?
    db 15 dup(?)

 

这是用以键盘输入的一个结构

mov ah, 0Ah
    mov dx, offset U  
    int 21h

 

这里对U进行输入

但字符串是存储在U+2的

所以我们在使用的时候是用

mov si, offset U+2

 

输出就以之前说的就好

cmp的一些问题

首先cmp自己有特定的使用要求

mov al, [bx]
cmp al, [si]

 

 在比较的时候不要使用两个[偏移地址]进行比较

循环的一些小问题

1. 循环计数循环:
mov cx, 5 ;循环次数

loop:
   ;循环体 
   dec cx
   jnz loop

 

2.利用cx寄存器作为计数器,执行循环体,dec指令递减cx,jnz判断cx是否为0,不为0则跳转继续循环。2. 标签循环:
begin:
   ;循环体
   jmp begin ;无条件跳转到begin标签

 

3.通过无条件跳转begin实现循环,begin标签标记循环位置。3. 比较循环:
mov cx, 0

cmp_loop:
  cmp cx, 5 ;比较计数器cx和5
  jl cmp_loop ;小于则跳转继续循环
  
  ;循环体
  inc cx ;计数器++

 

4.利用cmp和条件跳转jl实现计数器判断,控制循环执行。4. REP指令:
mov cx,5 ;循环次数

repeat:
  ;循环体

  rep repeat ;重复执行repeat循环

 

 REP前缀可以重复执行循环体,通过CX控制次数。

这里的标签依然是之前所说的,无所谓什么名字

甚至这里的循环都是通过指令实现的罢了并非坚固不可摧

剩下的就是一些简答输入输出问题

我们可以发现跟我们写一个普通程序一样,使用大脑的地方并不多

但剩下的地方需要你不断完善以合适用户真正的使用流程,这段代码比较用户名和密码的部分并不多

复制代码
;检查用户名
mov bx, offset USER  
mov si, offset U+2
mov cx, 3

check_user:
mov al, [bx]
cmp al, [si]  
jne PRINT_ERROR

inc bx
inc si
dec cx       ; 减少循环计数器
jnz check_user ; 如果cx不为零,则继续循环

;检查密码
mov bx, offset PASSWORD
mov si, offset PWD+2
mov cx, 1;这里可以改成
MLEN
 check_pwd: mov al, [bx] cmp al, [si] jne PRINT_ERROR inc bx inc si dec cx ; 减少循环计数器 jnz check_pwd ; 如果cx不为零,则继续循环 ;检查完成,用户名密码正确  jmp welcomeTo
复制代码

 

就这些部分罢了,其他都是满足用户流程

第二段代码

复制代码
.486
DATA  SEGMENT USE16
  BUF db 'ABC6DED55BV99CADE77ABD5AERS2AV1BA$' ; 保存字符串的缓冲区
  count dw 0 ; 统计结果存放变量
DATA    ENDS
CODE   SEGMENT         USE16
         ASSUME CS:CODE,   DS:DATA
START:  
        MOV AX,                 DATA
        MOV DS,                  AX
        MOV DH,                 0H
        MOV BX,                 OFFSET BUF
        CMP BYTE PTR [BX], 42H
        JB   NEXT
        CMP BYTE PTR [BX], 45H
        JA  NEXT
        INC DH    ;首先判断第一个字符是否满足条件,满足了就DH加1
LAST:   
        INC BX    ;接着通过加BX继续判断后面的字符
        CMP BYTE PTR [BX], 42H
        JB   NEXT
        CMP BYTE PTR [BX], 45H
        JA  NEXT
        INC DH
NEXT:   CMP BYTE PTR [BX], 24H ;判断'$',结尾
        JNE LAST
        MOV CX,8
XUN:    ROL DH,1    ;开始进行左移位,循环判断输出0、1
        MOV DL,                 30H
        JNC CON
        INC DL
CON:    MOV AH,                 02H
        INT 21H
        LOOP    XUN
        MOV AH,                 4CH
        INT 21H
CODE    ENDS
         END START
复制代码

 

简单解释几个代码

CMP BYTE PTR [BX], 42H
        JB   NEXT

 

这是一段比较的代码,下面的NEXT可以做到循环判断,不过也可以让地址+1在直接跳转到自己的标签位置,不需要使用两个标签做循环

汇编的代码相当自由

复制代码
XUN:    ROL DH,1    ;开始进行左移位,循环判断输出0、1
        MOV DL,                 30H
        JNC CON
        INC DL
CON:    MOV AH,                 02H
        INT 21H
        LOOP    XUN
        MOV AH,                 4CH
        INT 21H
复制代码

 

这里的输出没有像我之前说的那么模块化,少用了寄存器,这样当然更有优势

可读性上和写起来都要多想一层

1. XUN标签处是循环体,ROL DH,1实现了DH寄存器左移1位的逻辑运算。

2. JNC CON判断左移后最高位是否为1,如果是则不跳转,否则跳转到CON。

3. INC DL指令将DL寄存器的值+1,这里用于统计左移中1的个数。

4. CON标签输出当前DH的最低位,MOV DL,30H将DL预置为0,然后判断左移后最高位,如果为1则DL加1,记录1的个数。

5. 调用INT 21H中断打印DL的值,0或1。

6. LOOP XUN实现循环控制。

7. 最后调用INT 21H和INT 4CH结束程序。

 

 

这篇文章只是简单写了几个小点,并且很多地方并没有写的很细,只是为了着半个学期的课程做个小努力

posted @   逆世混沌  阅读(31)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 狂迪 卢广仲
狂迪 - 卢广仲
00:00 / 00:00
An audio error has occurred.

作词 : 卢广仲/討海人/黄少雍

作曲 : 卢广仲

在我眼前消失不见

今天的什么都不对

楼下的店听见熟悉的音乐

谁离开我谁爱着我

剩下一半的小火锅

别乱牵拖直到你出现

满天星光月亮出来了

神魂颠倒这是真的吗

管不住我自己的步伐

戒不掉你致命的魔法

谁教我蜿蜒的弹跳

谁教我崎岖的舞蹈

我爱你你知不知道

OH Dónde estás Bongria

想要见面有点抱歉

下午才跟你晚上约

请放轻松看着公园的落叶

摇摇晃晃我的形状

一边海洋一边天堂

你拉着我这一步叫做永远

满天星光月亮出来了

神魂颠倒这是真的吗

管不住我自己的步伐

戒不掉你致命的魔法

谁教我蜿蜒的弹跳

谁教我崎岖的舞蹈

我爱你你知不知道

OH Dónde estás Bongria

我丢掉太多的不必要

朝着有你的方向跑

跑到你眼前逗你笑

听到电影里的配乐响起

全场只为了等你说一句

说欸你要不要跟我一起

去教堂

Yes I do希望你也一样

先说好一起永保安康

每一天蜿蜒的弹跳

每一天崎岖的舞蹈

我爱你你知不知道 OH

如果爱我让我看见你

的脚