通过汇编实现在屏幕中间输入字符,enter结束,退格键删除字符

俗话说,高级语言,yyds,小手指一敲,就会在屏幕上显示出一串"hello world!"

但背后的原理是什么,是无数个中断指令,寄存器为之服务.接下来让我们揭开潘多拉的魔盒.

构思:

我们现在想在屏幕上输入字符,首先就是要在键盘上按下按键,就在按键的一瞬间,中断指令就触发了

我们的程序就对应的执行了,

下面我们写一段能够让我们在屏幕中间输入东西的程序.

前提:有一定的汇编基础,能够了解中断指令,这里我为你理清思路

 

第一步:

我们当然是写入数据,数据段写入,

assume cs:code,ds:data                


data segment

                db 32  dup(0)

data ends


定义的数据段

code segment
start:
        mov ax,data     ;数据段导入
        mov ds,ax
        mov si,0    ;栈空间偏移地址
        mov dh,12   ;显存第12行    
        mov dl,20   ;显存第二十列
        call getstr   ;开始操作
return: mov ax,4c00h
        int 21h         ;结束操作


将数据段导入到ds里,设置显存的位置

然后call 开始执行操作

第二步:

我们调用指令了,现在就是判断键盘的指令,进而进行相应的操作

getstr:push ax   ;常规操作,需要用到ax,保护ax 不会因为每次的使用而改变数值

     

 响应键盘操作

        

 dis:  pop ax

        ret               

第三步:

我们现在设法实现相应的键盘操作的内容

我们要读取按键指令,就要执行中断指令   int 16h

这样键盘就会一直读取键盘缓冲区的内容,并且把 Ascll 码存放在寄存器al 里面,我们对应着把相应的内容写到显存上面就行了.

我们现在要实现三种按键的功能,

那用什么来存放字符呢?最好的办法采用栈

① 按键类型是字符的,


可以将字符入栈,

然后显示栈中的字符

 

②退格键


将栈中的字符出栈

然后显示剩余的字符

同时将出栈的字符存到al里面(这样我们可以看到是哪个字符被删了)

③回车按键

将零入栈,

显示栈中的字符包括0,

然后结束运行

第四步:

经过以上步骤我们就实现了字符的写入,字符的删除,和程序的结束

但是我们会发现这三个键中实现功能的同时,有很多类似的功能

比如,将字符入栈和出栈,  显示栈中的字符,  这三个功能

我们所以先将这三个功能定义一下,一会儿再去具体实现, 

charpush    charpop   charshow

   入栈            出栈      显示栈中的字符

第五步:

现在我们的功能就大体实现了,具体框架就是第三步 , 然后框架中的细节就是第四部实现的 , 我们现在把第三步用函数具体去实现

扫描按键,根据具体的按键去实现对应的功能:

因为我们按键的Ascll存放在 寄存器 al 里面,

小于20h就不是字符,那就跳转到非字符处理

  cmp al,20h      ;判断是什么类型的按键信息
  jb nochar       ;如果小于20h就不是字符,
                        ;接着跳转到非字符处理


不跳转就是字符,我们开始实现①


call charpush             ;字符入栈

call   charshow           ;显示字符

跳转回去,继续判断输入按键信息

jmp   响应键盘操作

nochar:

不是字符则跳转执行,判断是否是退格键   回车键

        cmp ah,0eh             ;这是退格键的扫描码,直接就存放在了ax高位,低位是Ascll码,

        je backspace            ;是退格键,则跳转到退格键的功能



        cmp ah,1ch              ;扫描是否是回车键
        je enter                     ;是则执行对应功能



        jmp getstrs             ;不是以上按键,则仍未结束,接着执行int16h,
                                        ;等待输入
                                        ;接下来是退格键和回车键的处理

 

现在正式实现②退格键和③回车键的功能

 


; 退格键

backspace:                        
  call charpop            ;将字符出栈,

call   charshow        ;将剩余字符显示                

 

;回车键盘

enter:          ;这里是回车的功能
        mov al,48        ;这里是要结束,并且在栈里添加一个0,48是零的Ascll 码
                                ;现在存在al里面
           
        call charpop            ;字符入栈
      
        call charshow         ;显示栈中的字符


        jmp  dis            ;这里应该跳转结束

dis:    pop ax              ;寄存器出栈
          ret                     ;执行结束

这里功能都大致实现了,下面的细节就是,小功能的实现了

字符的入栈 charpop,

出栈 charpush,

显示 charshow

首先我们要知道,我们能够将字符任意的存放,删除,都是依托内存的,我们之前定义了内存data,

然后要利用栈的指针优势,来实现数据的增删, 但是如果直接利用栈段的话,我们把栈内容写入到显存的时候就麻烦了,

先进后出,我们刚开始输入的字符,进去了,最后输入的字符也进去了,那出来的时候就,是最后进去的先出来,这样出来的顺序就和进去的顺序颠倒了

我们现在要用到栈的指针来管理数据,又想让数据出来的时候按照进去的数据一样的顺序输出.

那我们就需要将数据段增添栈顶指针,然后栈顶指针是从0开始的,每增加一个数据,栈顶指针就会加一,以此来控制数据的增删,并且输出的时候也可以操作数据段来输出数据.

866c1119f1a840d09e91e9b7536f0e67.png

 

那我们首先就把栈空间构建好

说白了,栈空间还是一段空间,我们刚开始就把他定义在了ds里面了,其中,bx是此空间的段偏移地址,

然后我们这里是为了使用栈的优势,所以在数据段的开始定义了一个top 参数,来代替栈顶指针的功能,从0开始,每增加一个数据,top加一,删除一个数据的话,top减一 . 然后把整段数据输出的话,还可以从开始写入显存,当偏移地址和top 一样时,退出.

 

top dw 0

 

用top 来存放栈空间指针,从零开始
 

;字符入栈功能
charpush:
        mov bx,top[0]      ;将栈顶的指针给bx
        mov [si][bx],al ;然后将要输入的数字的阿斯克码,
                        ;放在栈空间的栈顶
        inc top[0]         ;当放入字符后,栈顶加一,这是我们自己做的栈                
        jmp sret  


这里看此图:

         要将字符加入到内存段里面,位置实际上就是top的位置,然后top 加一,就可以了

top就是下一个数据的偏移地址,  把al里的字符的Asccl码放到下一个字符的地址就可以了

此时,数据段是ds, 栈空间的开始的偏移地址是si, 栈段的偏移指针就是top 里的数值

这里是基址变址寻址

所以下一个数据的位置就是 ds段,加上栈空间的偏移地址si , 加上栈段的偏移地址top

 

 

48d4f28ab9e04629888475caee05ec50.png

 

;字符出栈
charpop:
        cmp top[0],0       ;打狗看主人,看看栈里有没有数据,


        je sret                 ;没有数据就出来


        dec top[0]          ;有数据就接着,将栈顶减一 ,


                                  ;此时栈顶对应出栈的字符       


        mov bx,top[0]         ;然后将栈顶交给bx,目的是传数据


        mov al,[si][bx]        ;将要出栈的数据送到al,
                                       ;这里注意top每次都比数据的偏移地址多1


        jmp sret                ;跳转结束
                                ;这里在第(dh)行,第(dl)列显示字符


这里出栈,也是一个意思,这里把要出栈的数据的内容送到寄存器 al 里面 , 然后top 指向要出栈的数据,对应的top 里内容减一 

显示字符

charshow:
        mov bx,0b800h
        mov es,bx
        mov al,160
        mov ah,0
        mov dh,12
        mul dh
        mov di,ax
        mov dl,20
        add dl,dl
        mov dh,0
        add di,dx
        
        mov bx,0


这里是把显存的内容送到 0b800h  ,  显示的位置是 第 dh 行,  第  dl  列

 

charshows:
        cmp bx,top
        jne noempty
        mov byte ptr es:[di],' '        ;让下一个字符变成 空白
        jmp sret

noempty:
        mov al,[si][bx]
        mov es:[di],al
        mov byte ptr es:[di+2],' '        让下一个字符变成空白 ,优化观感
        inc bx
        add di,2
        jmp charshows

这里小功能实现了,的确,我们又发现了一个问题,

我们次程序的调用需要频繁调用小功能,并且不容易拓展,所以我们可以把这几个功能,放在表里,然后通过寻址来调用相应的功能:

;接下来就是重磅的功能实现了,把小程序细分了,通过表调用
;这里是通过调用charstack 表,然后根据ah里的数值,来寻找表
;里的偏移地址,然后实现对应的功能的
charstack:
        jmp short charstart        
        table dw  charpush,charpop,charshow     
         
        top dw 0
         
charstart:
        push dx                        ;常规的入栈
        push di
        push bx
        push es
        ;实现各个功能
        cmp ah,2        ;首先查表  
        ja  sret        ;大于二就跳转结束
        mov bl,ah       ;功能交给bl                ;ah里面存放表里的数字
        mov bh,0        ;功能嫁接成bx        
        add bx,bx       ;因为占用两个字节,所以双倍寻址
        jmp word ptr table[bx]                ;通过表,来调用功能

 ;字符入栈

charpush:

;字符出栈                                        ;这里就把之前我们的小功能放在这里就可以了.

charpop:

;显示字符

charshow: 

sret:   pop es
        pop dx
        pop di
        pop bx
        ret

 

前面对应的功能调用,变成

mov ah,0        ;字符入栈
        call charstack

 

 mov ah,2
        call charstack  ;显示栈中的字符

        mov ah,2
        call charstack  ;再显示剩余的栈中的字符

 说到这里,大致就完成了,下面我把源码给出:

assume cs:code,ds:data
data segment
        db 32 dup(0)
data ends

code segment
start:
        mov ax,data     ;数据段导入
        mov ds,ax
        mov si,0    ;栈空间偏移地址
        mov dh,12   ;显存第12行    
        mov dl,20   ;显存第二十列
        call getstr   ;开始操作
return: mov ax,4c00h
        int 21h         ;结束操作

getstr: push ax         ;常规操作
getstrs:mov ah,0  
        int 16h         ;执行中断,等待读取按键信息

        cmp al,20h      ;判断是什么类型的按键信息
        jb nochar       ;如果小于20h就不是字符,
                        ;接着跳转到非字符处理
       ; mov al,0h 
        mov ah,0        ;字符入栈
        call charstack
        mov ah,2
        call charstack  ;显示栈中的字符
        jmp getstrs     ;继续执行键盘指令

nochar:                 ;这里是处理非字符
        cmp ah,0eh      ;扫描判断是否是退格键
        je backspace    ;是退格键,则跳转到退格键的功能
        cmp ah,1ch      ;扫描是否是回车键
        je enter        ;是则执行对应功能
        jmp getstrs     ;不是以上按键,则仍未结束,接着执行int16h,
                        ;等待输入
;接下来是退格键和回车键的处理
backspace:
        mov ah,1        ;首先完成退格功能,效果就是出栈,然后显示
        call charstack
                        ;然后完成,将字符出栈
        mov ah,2
        call charstack  ;再显示剩余的栈中的字符
        jmp getstrs     ;执行完之后,接着返回等待输入

enter:          ;这里是回车的功能
        mov al,48        ;这里是要结束,并且在栈里添加一个0,
                        ;现在存在al里面
        mov ah,0        ;字符入栈
        call charstack
        mov ah,2
        call charstack  ;显示栈中的字符
        jmp  dis            ;这里应该跳转吧,存疑
dis:    pop ax  ;寄存器入栈
        ret     ;执行结束
;接下来就是重磅的功能实现了,把小程序细分了,通过表调用
;这里是通过调用charstack 表,然后根据ah里的数值,来寻找表
;里的偏移地址,然后实现对应的功能的
charstack:
        jmp short charstart        
        table dw  charpush,charpop,charshow     
         
        top dw 0
         
charstart:
        push dx                        ;常规的入栈
        push di
        push bx
        push es
        ;实现各个功能
        cmp ah,2        ;首先查表  
        ja  sret        ;大于二就跳转结束
        mov bl,ah       ;功能交给bl
        mov bh,0        ;功能嫁接成bx        
        add bx,bx       ;因为占用两个字节,所以双倍寻址
        jmp word ptr table[bx]
;字符入栈功能
charpush:
        mov bx,top[0]      ;将栈顶的指针给bx
        mov [si][bx],al ;然后将要输入的数字的阿斯克码,
                        ;放在栈空间的栈顶
        inc top[0]         ;当放入字符后,栈顶加一,这是我们自己做的栈                
        jmp sret        
;字符出栈
charpop:
        cmp top[0],0       ;打狗看主人,看看栈里有没有数据,
        je sret         ;没有数据就出来
        dec top[0]         ;有数据就接着,将栈顶减一 ,
                        ;此时栈顶对应出栈的字符       
        mov bx,top[0]      ;然后将栈顶交给bx,目的是传数据
        mov al,[si][bx];将要出栈的数据送到al,
                        ;这里注意top每次都比数据的偏移地址多1
        jmp sret        ;跳转结束
;这里在第(dh)行,第(dl)列显示字符
charshow:
        mov bx,0b800h
        mov es,bx
        mov al,160
        mov ah,0
        mov dh,12
        mul dh
        mov di,ax
        mov dl,20
        add dl,dl
        mov dh,0
        add di,dx
        
        mov bx,0

charshows:
        cmp bx,top
        jne noempty
        mov byte ptr es:[di],' '
        jmp sret

noempty:
        mov al,[si][bx]
        mov es:[di],al
        mov byte ptr es:[di+2],' '
        inc bx
        add di,2
        jmp charshows


sret:   pop es
        pop dx
        pop di
        pop bx
        ret
code ends
end start

 

 

 

 

 

 

 

 

 

 

 

 

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值