📄 汇008.txt
字号:
例8.13 编写一个创建子目录的程序,具体要求如下:
1)、用键盘输入一个目录路径名,若输入的字符串为空,则程序运行结束;
2)、若目录创建成功,显示成功信息,否则,显示创建失败信息。
解:显示解答
例8.14 编写一个类似TYPE命令的程序,其要求如下:
1)、用键盘输入文件名(可包含路径),若输入的字符串为空,则程序运行结束;
2)、若输入的文件存在,则显示其内容,否则,显示文件不存在的信息。
解:显示解答
8.3.7 内存管理的中断功能
在C/C++语言环境中,若事先不知道数据的容量,那么,可通过动态申请空间的方法来解决数据的存储问题,这种动态数据结构可用中断21H所提供的存储管理功能来实现。
中断21H在内存管理方面所提供的主要功能如下:
48H——分配内存块
49H——释放内存块
4AH——重定义内存块的大小
58H——读取/设置内存分配策略,其最基本的分配策略有三种:第一满足、最好满足和最后满足
有关中断功能的详细描述和调用参数在此从略,需要查阅者可参阅附录3之内存分配功能。
有关存储分配策略的含义可参阅《操作系统》课程中“内存管理”部分的介绍。
8.3.8 读取和设置中断向量
中断向量是系统用来存放中断服务程序或系统参数的入口地址。在通常情况下,程序员不需要对中断向量表作任何操作,但在开发程序时,若要为某种特殊需要而提供新的中断处理程序时,则其就必须要操作中断向量表。
1、读取中断向量
(1)、DOS功能调用的方法
DOS提供了用系统调用的方法来读取中断向量,其中断21H之功能35H就能读取指定中断号的入口地址。其使用参数如下:
入口参数:AH=35H,AL=中断号
出口参数:ES:BX=中断处理程序的入口地址
例8.15 用功能调用的方法把中断n的入口地址保存到双字变量OldAddr中。
解:
……
OldAddr DD ?
……
MOV AL, n ;这里的n要用具体的中断号来定
MOV AH, 35H
INT 21H
MOV word ptr OldAddr, BX
MOV word ptr OldAddr+2, ES ;把其入口地址保存在存储单元中
……
(2)、直接访问存储单元的方法
由前面的图8.2不难看出:中断向量表是存储在内存的第0段。若中断号为n,那么,其入口地址在表内的偏移量为4n。
例8.16 用直接访问存储单元的方法把中断n的入口地址保存到双字变量OldAddr中。
解:
……
OldAddr DD ?
……
MOV AX, 0H
MOV ES, AX ;中断向量表存储在第0段内
MOV BX, 4*n
MOV AX, ES:[BX] ;读取中断入口地址的偏移量
MOV word ptr OldAddr, AX ;保存中断入口地址的偏移量
MOV AX, ES:[BX+2] ;读取中断入口地址的段地址
MOV word ptr OldAddr+2, ES ;保存中断入口地址的段地址
……
2、设置中断向量
(1)、DOS功能调用的方法
中断21H之功能25H可为指定的中断号设置新的入口地址。其使用方法如下:
入口参数:AH=中断号,DS:DX=中断处理程序的入口地址
出口参数:无
例8.17 用功能调用的方法把子程序NewFunc设置为中断n的中断处理程序。
解:
……
NewFunc PROC
……
IRET ;注意其返回指令
NewFunc ENDP
……
MOV AX, SEG NewFunc
MOV DS, AX ;设置段地址寄存器
MOV DX, OFFSET NewFunc ;设置偏移量
MOV AL, n ;这里的n要用具体的中断号来定
MOV AH, 25H
INT 21H
……
(2)、直接访问存储单元的方法
例8.18 用直接访问存储单元的方法把子程序NewFunc设置为中断n的中断处理程序。
解:
……
NewFunc PROC
NewFunc ……
IRET ;注意其返回指令
ENDP
……
MOV AX, 0H
MOV DS, AX
MOV BX, 4*n
CLI
MOV word ptr [BX], OFFSET NewFunc ;设置中断处理程序的偏移量
MOV word ptr [BX+2], SEG NewFunc ;设置中断处理程序的段地址
STI
……
在上面程序段中,指令CLI是一条值得注意的指令,它用来确保随后二条MOV指令被连续执行而不被打断。这是因为执行其第一条MOV指令后,原中断向量表中的入口地址就被破坏了,这时,该入口地址既不是指向原处理程序,也不指向新处理程序。如果此刻正巧发生了该类型的中断请求,那么,系统将转向一个非法的位置。如果程序员能确信此时不会发生该类型的中断请求,当然也就不必插入关中断指令。
后面的指令STI是开中断指令,它允许CPU响应其后的中断请求。
3、修改中断向量的步骤
前面,分别介绍了读取和设置中断入口地址的方法。在编程的实际过程中,若要修改某个具体的中断处理程序时,一般需按下面几步来完成:
1)、读取指定中断的中断处理程序入口地址,并把它保存在存储单元内;
2)、把用户编写的程序段设置为指定中断的新处理程序;
3)、在用户程序结束之前(或不需要新的处理程序时),把步骤1保存的入口地址恢复成处理该中断的入口地址。
程序员按以上三步来编程,可保证:在其程序运行过程中,该指定的中断将按新的处理程序来处理,程序结束后,中断系统又恢复成原来的处理方式。所以,这种中断向量的改变对其它程序或使用者来说是透明的,当然也就不会影响它们的正常运作。
以上三步具体化的形式如下:
……
INTNO EQU 40H ;假设被修改的中断号
OldAddr DD ?
……
NewFunc PROC ;新的中断处理程序
NewFunc ……
IRET
ENDP
……
MOV AL, INTNO
MOV AH, 35H
INT 21H
MOV word ptr OldAddr, BX
MOV word ptr OldAddr+2, ES ;步骤1:保存原入口地址
MOV DX, OFFSET NewFunc
MOV AX, SEG NewFunc
MOV DS, AX
MOV AL, INTNO
MOV AH, 25H
INT 21H ;步骤2:设置新的入口地址
……
;用户编写的主要程序
……
MOV DX, word ptr OldAddr
MOV DS, word ptr OldAddr+2 ;本指令与上一条指令能交换吗?
MOV AL, INTNO
MOV AH, 25H
INT 21H ;步骤3:恢复原入口地址
……
END
8.4 习题
8.1、简述中断和子程序调用之间的主要区别?
8.2、为什么要区分IRET指令与RET指令?
8.3、编写一个子程序,它可显示以0结尾的字符串。子程序的入口参数DS:DX为待输出字符串的首地址。
8.4、编写一个子程序,用来读入一个键,并在屏幕上按十六进制的形式显示按键的扩展ASCII码,如果按键为普通字符,则不显示。
8.5、编写一个程序,在屏幕的右下角闪烁显示编程者自己的姓名,显示颜色自定。
8.6、编写一个把屏幕上显示的字母经过大小写转换后再显示的程序。
8.7、编写一个程序,它把屏幕上的数字改位蓝绿色背景的红字。
8.8、编写一个控制光标位置和形状的程序,该程序具有以下功能:
1)、可用光标移动键↑、↓、←和→来移动光标;
2)、当光标已在第0列,且按'←'键时,光标定在上一行的最后一列;若已在屏幕的左上角,则光标不动,且给出响铃;按'→'键时的边界处理类似;
3)、当光标在第0行,且按'↑'键时,则光标不动,且给出响铃;按'↓'键时的边界处理类似;
4)、按Home或End键,则光标移到当前行的行首或行尾;
5)、若按下数字或字母键,则把该字符从当前位置依次显示到屏幕顶(在新位置显示字符时,原位置的符号被抹去);
6)、按Esc键,程序结束。
8.9、交换例8.5和8.6的画线方法,编写相应的子程序VLine和HLine,并验证之。
8.10、假设显示器的显示模式设定为12H,编写实现下列功能的程序:
1)、在屏幕中间从上到下显示一条明亮的蓝色线,线宽为1个像素;
2)、在屏幕底下横向画一条绿色线,线宽为2个像素;
3)、在屏幕上垂直显示16种颜色,每种颜色宽40个像素;
4)、设定屏幕背景为白色,在屏幕中间画一条青色线,线宽为10个像素。
8.11、对于256色,320X200的显示模式需要多少个字节的内存存放一屏信息?
8.12、为什么在显示数据时必须关闭鼠标?
8.13、编写程序,检测计算机是否已安装了鼠标,并以显示Yes/No来表示检测结果。
8.14、编写程序,显示鼠标的按键,若按左键,显示"Left",若按右键,显示"Right",按Esc键,程序结束。
8.15、编写一个程序,在图形模式下,打开鼠标,并显示鼠标的位置。
8.16、编写一个程序,显示驱动器C根目录下所有EXE文件。提示:用通配符"*.exe"来查找与此匹配的文件名,找到后,显示之。
8.17、编写一个程序,显示驱动器C根目录下的所有子目录。
8.18、编写一个程序,设置指定目录下的所有文件为只读文件。
8.19、编写一个程序,创建一个隐含文件Data.TXT。
8.20、编写一个程序,把键盘上输入写入文件File.dat中,类似以下DOS命令:
COPY con File.dat
8.21、使用哪个DOS功能可读取中断向量地址,哪个DOS功能可把新的中断服务程序地址设置为指定中断的入口地址。
8.22、编写一个程序,在程序开始运行时,把中断向量表保存到程序的数据段中,在程序结束前把数据段中的备份恢复到系统中去。
8.23、把下列C语言的子程序改写成汇编语言子程序。
1)、取当前光标的位置,并分别存入变量x和y中
get_cursor(int *x, int *y)
{union REGS in, out;
in.h.ah = 3; in.h.bh = 0;
int86(0x10, &in, &out);
*x = out.h.dh; *y = out.h.dl;
}
2)、把光标设置在(row, col)位置
locate(int row, int col)
{union REGS in, out;
in.h.ah = 2; in.h.bh = 0;
in.h.dh = row; in.h.dl = col;
int86(0x10, &in, &out);
}
3)、在(row, col)位置显示字符串string
disp_string(int row, int col, char string[])
{struct REGPACK in, out;
int x, y;
get_cursor(&x, &y);
locate(row, col);
in.r_ds = FP_SEG(string);
in.r_dx = FP_OFF(string);
in.r_ax = 0x900;
intr(0x21, &in);
locate(x, y);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -