BIOS 与 MS - DOS 编程深度解析
1. BIOS 级编程概述
在 BIOS 级别进行编程能够比在 MS - DOS 级别对计算机的输入输出设备实现更精细的控制。下面将详细介绍在 BIOS 级别对键盘、视频显示和鼠标的编程方法。
1.1 键盘编程
INT 16h 中断在读取扩展键盘按键(如功能键和光标箭头键)时非常有用。键盘硬件与 INT 9h、INT 16h 和 INT 21h 处理程序协同工作,为程序提供键盘输入。以下是一个示例程序,它会不断轮询键盘,当按下 Esc 键时跳出循环:
; 此处可编写轮询键盘并在按下 Esc 键时跳出循环的代码
1.2 视频显示编程
视频显示器通过原色的加法合成来产生颜色,颜色位会映射到视频属性字节。INT 10h 提供了广泛且实用的功能,可在 BIOS 级别控制视频显示。
-
滚动彩色窗口示例
:有一个示例程序可以滚动彩色窗口,并在窗口中间写入文本。
-
绘制彩色图形
:可以使用 INT 10h 绘制彩色图形,有两个示例程序展示了具体实现方法,还可以用一个简单公式将笛卡尔坐标转换为屏幕坐标(像素位置)。
-
高速彩色图形绘制
:通过直接写入视频内存,一个带有详细文档的示例程序展示了如何绘制高速彩色图形。
1.3 鼠标编程
INT 33h 有众多函数可用于操作和读取鼠标信息。一个示例程序可以同时跟踪鼠标的移动和鼠标按钮的点击。
2. 编程挑战与练习
以下是一些需要在实地址模式下完成的编程练习:
| 练习名称 | 具体要求 |
|---|---|
| ASCII 表显示 | 使用 INT 10h 显示 IBM 扩展 ASCII 字符集中的所有 256 个字符,每行显示 32 列,每个字符后加一个空格。 |
| 滚动文本窗口 | 定义一个约为视频显示大小四分之三的文本窗口,按顺序执行以下操作:在窗口顶行绘制一串随机字符;将窗口向下滚动一行;程序暂停约 200 毫秒;再绘制一行随机文本;持续滚动和绘制,直到显示 50 行。 |
| 滚动彩色列 | 以滚动文本窗口练习为基础,随机字符串仅在第 0、3、6、9……78 列有字符,其他列留空,每列颜色不同。 |
| 不同方向滚动列 | 以滚动文本窗口练习为基础,在循环开始前随机选择每列是向上还是向下滚动,程序运行期间保持同一方向。提示:将每列定义为单独的滚动窗口。 |
| 使用 INT 10h 绘制矩形 | 创建一个名为 DrawRectangle 的过程,接收指定左上角和右下角位置以及颜色的输入参数,编写一个简短的测试程序绘制不同大小和颜色的多个矩形。 |
| 使用 INT 10h 绘制函数曲线 | 使用 INT 10h 的像素绘制功能,绘制由方程 Y = 2(X^2) 确定的直线。 |
| 模式 13 单垂直线 | 修改内存映射图形程序,使其绘制一条单垂直线。 |
| 模式 13 多垂直线 | 修改内存映射图形程序,使其绘制一系列 10 条不同颜色的垂直线。 |
| 绘制框程序 | 编写一个过程,在屏幕任意位置绘制单线框,使用特定的扩展 ASCII 代码,过程的唯一输入参数是指向 FRAME 结构的指针。 |
graph LR
A[开始] --> B[显示 ASCII 表]
B --> C[滚动文本窗口]
C --> D[滚动彩色列]
D --> E[不同方向滚动列]
E --> F[绘制矩形]
F --> G[绘制函数曲线]
G --> H[绘制单垂直线]
H --> I[绘制多垂直线]
I --> J[绘制框程序]
J --> K[结束]
3. 专家级 MS - DOS 编程
对于计划从事英特尔处理器硬件级工作的工程师,或者想了解 MS - DOS 专家如何利用有限资源实现强大功能的人来说,这部分内容非常有价值。
3.1 定义段
早期的 MASM 程序需要为代码、数据和堆栈段创建复杂的定义。简化段指令(如 .model、.code、.stack 等)的出现让编程变得更简单,但专家级程序员可能更倾向于传统的显式段定义方式。
3.1.1 简化段指令
- 数据段 :使用 .MODEL 指令时,汇编器会自动为近数据段定义 DGROUP。.DATA 和 .DATA? 指令会创建近数据段,在实地址模式下最大为 64Kb,放置在 DGROUP 组中。.FARDATA 和 .FARDATA? 在小和中等内存模型中会创建远数据段。
- 变量段识别 :可以使用 SEG 运算符将段地址分配给段寄存器,例如:
mov ax, SEG farvar
mov ds, ax
- 代码段 :.CODE 指令定义代码段。在小内存模型程序中,会生成名为 _TEXT 的段;在中、大、巨型模型程序中,每个源模块会分配不同的段名。也可以在同一模块中声明多个代码段,如:
.code MyCode
以下是一个包含两个代码段的示例程序:
TITLE Multiple Code Segments (MultCode.asm)
; This small model program contains multiple
; code segments.
.model small,stdcall
.stack 100h
WriteString PROTO
.data
msg1 db "First Message",0dh,0ah,0
msg2 db "Second Message",0dh,0ah,"$"
.code
main PROC
mov ax,@data
mov ds,ax
mov dx,OFFSET msg1
call WriteString
; NEAR call
call Display
; FAR call
.exit
main ENDP
.code OtherCode
Display PROC FAR
mov ah,9
mov dx,offset msg2
int 21h
ret
Display ENDP
END main
3.1.2 显式段定义
在某些情况下,可能需要显式定义段,例如定义多个带有额外内存缓冲区的数据段,或者链接使用专有段名的对象库,或者编写供不使用微软段名的高级语言编译器调用的过程。
显式段定义的语法如下:
name SEGMENT [align] [combine] ['class']
statement - list
name ENDS
- align 类型 :决定段的起始地址对齐方式,包括 BYTE、WORD、DWORD、PARA 和 PAGE。默认是 PARA,即段必须从 16 字节边界开始。
- combine 类型 :告诉链接器如何合并同名段,包括 PRIVATE、PUBLIC、STACK、COMMON、MEMORY 和 AT address。
- class 类型 :用于合并不同名称的段,是区分大小写的字符串,如标准类型 ‘CODE’ 用于包含指令的段。
以下是一个定义段的示例:
ExtraData SEGMENT PARA PUBLIC 'DATA'
var1 BYTE 1
var2 WORD 2
ExtraData ENDS
ASSUME 指令 :该指令告诉汇编器在汇编时如何计算代码和数据标签的偏移量,通常放在代码段的 SEGMENT 指令之后。例如:
ASSUME ds:data1
ASSUME cs:myCode, ss:myStack
以下是一个包含两个数据段的示例程序:
TITLE Multiple Data Segments (MultData.asm)
; This program shows how to explicitly declare
; multiple data segments.
cseg SEGMENT 'CODE'
ASSUME cs:cseg, ds:data1, es:data2, ss:mystack
main PROC
mov ax,data1
; point DS to data1 segment
mov ds,ax
mov ax,SEG val2
; point ES to data2 segment
mov es,ax
mov ax,val1
; data1 segment assumed
mov bx,val2
; data2 segment assumed
mov ax,4C00h
; exit program
int 21h
main ENDP
cseg ENDS
data1 SEGMENT 'DATA'
val1 WORD 1001h
data1 ENDS
data2 SEGMENT 'DATA'
val2 WORD 1002h
data2 ENDS
mystack SEGMENT para STACK 'STACK'
BYTE 100h dup('S')
mystack ENDS
END main
通过以上内容的学习,我们可以在 BIOS 级别和 MS - DOS 环境下进行更深入、更灵活的编程。无论是对输入输出设备的控制,还是对程序段的精细定义,都有了更清晰的认识和实践方法。在后续的学习和实践中,我们可以根据具体需求选择合适的编程方式和技巧,实现更复杂、更强大的功能。
BIOS 与 MS - DOS 编程深度解析
3.2 运行时程序结构
了解程序在运行时的结构有助于我们更好地理解程序的执行过程和内存使用情况。
3.2.1 程序段前缀(PSP)
程序段前缀是 MS - DOS 为每个程序分配的一块内存区域,位于程序加载地址的起始处。它包含了程序运行所需的一些重要信息,如环境字符串、命令行参数等。
3.2.2 COM 程序
COM 程序是一种简单的可执行文件格式,其代码、数据和堆栈都位于同一个 64KB 的段中。COM 程序的入口点是文件的起始地址,它没有复杂的头部信息,结构较为紧凑。
3.2.3 EXE 程序
EXE 程序是一种更灵活的可执行文件格式,它可以包含多个段,如代码段、数据段和堆栈段。EXE 文件有一个头部,包含了程序的加载信息和段的描述。在加载 EXE 程序时,MS - DOS 会根据头部信息将各个段加载到内存中,并初始化相应的段寄存器。
| 程序类型 | 特点 |
|---|---|
| COM 程序 | 代码、数据和堆栈在同一 64KB 段,无复杂头部,入口为文件起始地址 |
| EXE 程序 | 可包含多个段,有头部信息,加载时根据头部初始化段寄存器 |
3.3 中断处理
中断处理是 MS - DOS 编程中的重要部分,它允许程序在特定事件发生时暂停当前执行,转去执行相应的中断处理程序。
3.3.1 硬件中断
硬件中断是由外部硬件设备触发的中断,如键盘、鼠标、定时器等。每个硬件中断都有一个对应的中断请求(IRQ)级别,Intel 8259 可编程中断控制器(PIC)负责管理这些中断请求。
| IRQ 级别 | 常见设备 |
|---|---|
| 0 | 系统定时器 |
| 1 | 键盘 |
| 2 | 级联 8259 控制器 |
| 3 | 串口 2 |
| 4 | 串口 1 |
| 5 | 并行口 2 |
| 6 | 软盘驱动器 |
| 7 | 并行口 1 |
3.3.2 中断控制指令
在汇编语言中,有一些指令用于控制中断的开启和关闭,如 CLI(清除中断标志,关闭中断)和 STI(设置中断标志,开启中断)。
3.3.3 编写自定义中断处理程序
可以编写自己的中断处理程序来替换系统默认的中断处理程序。以下是一个简单的步骤:
1. 保存原中断向量:将原中断向量(中断处理程序的入口地址)保存到一个变量中。
2. 设置新中断向量:将自定义中断处理程序的入口地址设置到中断向量表中。
3. 编写中断处理程序:实现自定义的中断处理逻辑。
4. 恢复原中断向量:在程序结束时,将原中断向量恢复到中断向量表中。
; 示例:替换 Ctrl - Break 中断处理程序
OLD_INT23 DD ? ; 保存原 INT 23h 中断向量
; 保存原中断向量
MOV AX, 3523h
INT 21h
MOV WORD PTR OLD_INT23, BX
MOV WORD PTR OLD_INT23 + 2, ES
; 设置新中断向量
MOV AX, 2523h
MOV DX, OFFSET NEW_INT23
INT 21h
; 自定义中断处理程序
NEW_INT23 PROC
; 自定义处理逻辑
IRET
NEW_INT23 ENDP
; 恢复原中断向量
MOV AX, 2523h
MOV DX, WORD PTR OLD_INT23
MOV ES, WORD PTR OLD_INT23 + 2
INT 21h
3.3.4 终止并驻留程序(TSR)
终止并驻留程序是一种特殊的程序,它在执行完部分任务后,将自己的一部分代码和数据驻留在内存中,继续监听特定的事件。例如,一个 TSR 程序可以拦截 Ctrl - Alt - Del 键组合。
; 示例:拦截 Ctrl - Alt - Del 键组合的 TSR 程序
OLD_INT9 DD ? ; 保存原 INT 9h 中断向量
; 保存原中断向量
MOV AX, 3509h
INT 21h
MOV WORD PTR OLD_INT9, BX
MOV WORD PTR OLD_INT9 + 2, ES
; 设置新中断向量
MOV AX, 2509h
MOV DX, OFFSET NEW_INT9
INT 21h
; 自定义中断处理程序
NEW_INT9 PROC
; 检查是否按下 Ctrl - Alt - Del
; 自定义处理逻辑
JMP DWORD PTR OLD_INT9 ; 调用原中断处理程序
NEW_INT9 ENDP
; 驻留程序
MOV AX, 3100h
MOV DX, (END_OF_PROGRAM - START_OF_PROGRAM) / 16 ; 驻留大小
INT 21h
START_OF_PROGRAM:
; 程序代码
END_OF_PROGRAM:
3.4 硬件控制使用 I/O 端口
I/O 端口是计算机与外部硬件设备进行通信的接口,通过读写 I/O 端口可以控制硬件设备的行为,读取硬件设备的状态。
3.4.1 输入 - 输出端口
每个硬件设备都有一个或多个对应的 I/O 端口地址,通过 IN 和 OUT 指令可以对这些端口进行读写操作。例如,读取键盘状态可以通过读取特定的 I/O 端口。
3.4.2 PC 声音程序
可以通过控制 PC 扬声器的 I/O 端口来产生声音。以下是一个简单的示例程序:
; 示例:PC 声音程序
MOV AL, 182 ; 选择定时器 2
OUT 43h, AL ; 写入控制字
MOV AX, 1193 ; 频率值
OUT 42h, AL ; 写入低字节
MOV AL, AH
OUT 42h, AL ; 写入高字节
IN AL, 61h ; 读取扬声器控制端口
OR AL, 03h ; 开启扬声器
OUT 61h, AL ; 写入新的控制值
; 延时一段时间
MOV CX, 10000
DELAY_LOOP:
LOOP DELAY_LOOP
IN AL, 61h ; 读取扬声器控制端口
AND AL, 0FCh ; 关闭扬声器
OUT 61h, AL ; 写入新的控制值
graph LR
A[开始] --> B[保存原中断向量]
B --> C[设置新中断向量]
C --> D[执行主程序]
D --> E{是否有中断发生}
E -- 是 --> F[执行自定义中断处理程序]
F --> E
E -- 否 --> G[恢复原中断向量]
G --> H[结束]
通过对以上内容的学习,我们可以在 MS - DOS 环境下进行更高级的编程,包括对程序段的精细控制、中断处理和硬件设备的操作。这些知识和技能对于深入理解计算机系统的工作原理和实现复杂的应用程序具有重要意义。在实际编程中,我们可以根据具体需求灵活运用这些技术,实现各种功能。
超级会员免费看
1430

被折叠的 条评论
为什么被折叠?



