概述
俗话说,高级语言,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开始的,每增加一个数据,栈顶指针就会加一,以此来控制数据的增删,并且输出的时候也可以操作数据段来输出数据.
那我们首先就把栈空间构建好
说白了,栈空间还是一段空间,我们刚开始就把他定义在了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
;字符出栈
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 sretnoempty:
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
最后
以上就是怕孤单小猫咪为你收集整理的通过汇编实现在屏幕中间输入字符,enter结束,退格键删除字符的全部内容,希望文章能够帮你解决通过汇编实现在屏幕中间输入字符,enter结束,退格键删除字符所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复