博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在屏幕中间显示,按ESC键后改变字符的颜色
阅读量:6161 次
发布时间:2019-06-21

本文共 4085 字,大约阅读时间需要 13 分钟。

程序功能:编写程序在屏幕中间显示“a”~“z”,并可以让人看清,这个任务比较好实现。

(1)在b800:[ 160*12+40*2]处存入a的ASCII码、(2)在循环中使用一个100000000000H次的循环空转达到延迟效果、(3)按键盘引发int9中断改变颜色
那么如何实现,按下Esc 键后,改变显示的颜色呢?
键盘输入到达60h 端口后,就会引发9号中断,CPU 则转去执行int 9中断例程。
我们可以编写int 9中断例程,功能如下:
(1)从60h 端口读出键盘的输入;
(2)调用BIOS 的int 9 中断例程,处理其他硬件细节;
(3)判断是否为Esc的扫描码,如果是,改变显示的颜色后返回;如果不是则直接返回。
我们对这些功能的实现一一进行分析
1、从端口60h读出键盘的输入使用:in al,60h
2、调用BIOS的int 9中断例程
有一点要注意的是,我们写的中断处理程序要成为新的int 9中断例程,主程序必须要将中断向量表中的int 9中断例程的入口地址改为我们写的中断处理程序的入口地址。那么在新的中断处理程序中调用原来的int 9中断例程时,中断向量表中的int 9中断例程的入口地址却不是原来的int 9 中断例程的地址。所以我们不能使用int 指令直接调用。要在我们写的新中断例程中调用原来的中断例程,就必须在将中断向量表中的中断例程的入口地址改为新地址之前,将原来的入口地址保存起来。这样,在需要调用的时候,我们才能找到原来的中断例程的入口。对于我们现在的问题,假设我们将原来int 9中断例程的偏移地址和段地址保存在ds:[0]和ds:[2]单元中。那么我们在需要调用原来的int 9中断例程时候,就可以在ds:[0]、ds:[2] 单元中找到它的入口地址。那么,有了入口地址后,我们如何进行调用呢?
当然不能使用指令int 9来调用。我们可以用别的指令来对int指令进行一些模拟,从而实现对中断例程的调用。我们来看,int 指令在执行的时候,CPU 进行下面的工作:
(1)取中断类型码n;
(2)标志寄存器入栈;
(3)IF=0,TF=0;
(4)CS 、IP 入栈;
(5)设置(IP)=(n*4),(CS=(n*4+2)。
取中断类型码是为了定位中断例程的入口地址,在我们的问题中,中断例程的入口地址已经知道。所以,我们用别的指令模拟int指令时候,不需要做第(1)步。在假设要调用的中断例程的入口地址在ds:0和ds:2单元中的前提下,我们将int 过程用下面几步模拟:
(1)标志寄存器入栈;
(2)IF=0,TF=0;
(3)CS、IP入栈;
(4)(IP)=((ds)*16+0),(CS)=((ds)*16+2)。
可以注意到第(3)、(4)步和call dword ptr ds:[0]的功能一样。call dword ptr ds:[0]的功能也是:(1)CS 、IP 入栈;(2)(IP)=((ds)*16+0),(CS)=((ds)*16+2)。
说明:如果还有疑问,复习10.6节的内容。
所以int 过程的模拟过程变为:
(1)标志寄存器入栈;
(2)IF=0,TF=0;
(3)call dword ptr ds:[0]
对于(1),可用pushf来实现。
对于(2),可用and和popf实现,如下面的指令实现。
实现IF=0,TF=0步骤:
pushf
pop ax
and ah,11111100b ;IF和OF为标志寄存器的第9位和第8位
push ax
popf
这样,模拟int指令的调用功能,调用入口地址在ds:0、ds:2中的中断例程的程序如下
pushf ;标志寄存器入栈
pushf;实现IF=0,TF=0的功能
pop ax
and ah,11111100b ;IF和OF为标志寄存器的第9位和第8位
push ax
popf ;IF=0、TF=0
call dword ptr ds:[0];call功能: ①CS、IP入栈,②;(IP)=((ds)*16+0),③;(CS)=((ds)*16+2)
3、如果是Esc键的扫描码,改变显示的颜色后返回,如何改变显示的颜色?
显示的位置是屏幕的中问,即第12行40列,显存中的偏移地址为:160*12+40* 2。所以字符的ASCII码要送入b800:160*12+40*2处。而b800:160*12+40*2+1处是字符的属性,我们只要改变此处的数据就可以改变在b800:160*12+40*2处显示的字符的颜色了。
该程序的最后一个问题是,要在程序返回前,将中断向量表中的ini 9中断例程的入口地址恢复为原来的地址。否则程序返回后,别的程序将无法使用键盘。
注意,本章中所有关于键盘的程序,因要直接访问真实的硬件,则必须在DOS实模式下运行。在Windows 2000 的DOS 方式下运行,会出现一些和硬件工作原理不符合的现象。
开发int9中断例程架构:
①在主程序中把原来的int9的原始程序入口保存到data段中,并把自己写的int9中断例程入口地址替换到中断向量表的9号中断地址,对应的是IP是0: [9*4]和CS是0[9*4+2]
②等待外部中断自动调用int9
③程序运行完后还原int9原来的中断例程入口
在int9中断例程内部结构
1.保存用到的通用寄存器
2.接收60h端口的数据
3.修改IF和TF的值为0
4.处理数据
5.使用call模拟调用int9的系统中断例程
6.还原通用寄存器
说明:由于中断例程使用的是iret返回,而iret的过程是①从栈中还原IP和CS,②从栈中还原寄存器状态;这里使用了call的远跳转(地址在data段中),而用call过程是先把CS和IP保存进栈,跳转到指定地址执行完再通过retf返回,调用完成后再从栈中还原IP和CS;而这里我们调用的是中断例程,是用iret返回的,retf和iret返回的IP和CS顺序相同,而iret比retf多一步还原寄存器状态,所以我们要构造供iret的返回的栈数据:就是在call前先保存寄存器状态,然后就可以使用iret的形式还原程序的IP,CS,标志寄存器。我们只要在自己编写的中断例程中处理完自己的数据后再调用BIOS 的int 9中断例程就可以了。

;程序功能:在屏幕中间同一点显示a-z的所有字符;        1.使用cpu循环空运行实现延迟;        2.按ESC键改变正在循环显示的字符的颜色;        3.程序完成时,再次还原int9中断向量表assume cs:codedata segmentdb 100 dup(0)data endscode segmentstart:     ;init ds,es    mov ax,data    mov ds,ax    mov ax,0    mov es,ax        ; save int9 IP and CS    mov ax,es:[9*4]    mov ds:[0],ax    mov ax,es:[9*4+2]    mov ds:[2],ax    mov es:[9*4],offset int9    mov es:[9*4+2],cs        ;循环显示a-z的所有字符    mov ax,0b800h    mov es,ax    mov ah,'a'    show: mov es:[160*12+40*2],ah    call delay    ;调用延迟    inc ah    cmp ah,'z'    jna show        ;还原int9中断向量表    mov ax,0    mov es,ax    push ds:[0]    pop es:[9*4]    push ds:[2]    pop es:[9*4+2]        mov ax,4c00h    int 21h;使用cup循环空运行,实现延迟时间的作用;使用32位的借位减法实现 1000 0000 0000h 次数的循环delay:    push ax    push dx    mov dx,1000h    mov ax,0    s: sub ax,1    sbb dx,0    cmp ax,0    jne s    cmp dx,0    jne s    pop dx    pop ax    ret;实现int9中断例程;pushf是16位,pop是16位int9:        push ax    ;保存临时变量    in al,60h        pushf    ;用于call模拟int9后的popf操作    pushf    ;用于修改IF=0、TF=0 操作    pop bx    and bh,11111100b    push bx    popf    call dword ptr ds:[0]        ;调用完这个模拟的int9后,会执行iret操作    ;会先popf出第一次pushf的值        cmp al,1    jne int9ret    add byte ptr es:[160*12+40*2+1],11h    int9ret:    pop ax    iretcode endsend start

程序简化:

1)在键盘按键的时候触发int9中断例程,这时候IF和TF都已经设置成0了,使用我们就没必要设置了

2)原始的int9可以保存到0:200中,在程序中使用中断调用

存在的问题:

在上面的程序,如果设置中断向量表的时候没有全部完成,就是重定位中断向量表地址的时候出现了键盘的按键中断,在CUP会跳到一个错误的地址执行,如何解决呢?                            提示:使用sti和cli命令

转载地址:http://ikefa.baihongyu.com/

你可能感兴趣的文章
eclipse decompiler
查看>>
记一个搜索网盘资源的网站
查看>>
jdk1.7和jdk1.8的String的getByte方法的差异
查看>>
java父子进程通信
查看>>
Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
查看>>
Olap学习笔记
查看>>
Codeforces Round #431 (Div. 1)
查看>>
如何进行数组去重
查看>>
将标题空格替换为 '_' , 并自动复制到剪切板上
查看>>
List Collections sort
查看>>
Mysql -- You can't specify target table 'address' for update in FROM clause
查看>>
使用局部标准差实现图像的局部对比度增强算法。
查看>>
2017-2018-1 20165313 《信息安全系统设计基础》第八周学习总结
查看>>
《代码敲不队》第四次作业:项目需求调研与分析
查看>>
菜鸡互啄队—— 团队合作
查看>>
HttpWebRequest的GetResponse或GetRequestStream偶尔超时 + 总结各种超时死掉的可能和相应的解决办法...
查看>>
SparseArray
查看>>
第二章
查看>>
android背景选择器selector用法汇总
查看>>
[转]Paul Adams:为社交设计
查看>>