BIOS 级编程:深入计算机底层的奥秘
一、引言
在计算机发展的早期,当第一台 IBM - PC 问世时,众多程序员都渴望深入了解计算机硬件并与之直接交互。像 Peter Norton 这样的先驱者发现了许多有用且神秘的信息,IBM 也慷慨地公布了 IBM PC/XT BIOS 的汇编语言源代码。早期的游戏设计师如 Michael Abrash 利用对 PC 硬件的了解,优化了图形和声音软件。如今,你也有机会加入这个优秀的群体,在 BIOS(基本输入 - 输出系统)层面开展工作。
为确保与本章中的程序完全兼容,建议安装早期版本的 Windows,如 Windows 95 或 Windows 98。你可以使用软件工具在计算机上创建虚拟机,以便进行相关软件的实验。这些程序均为 16 位实模式应用程序,可在 Windows XP 及更早版本的 Microsoft Windows 中开发和运行。通过学习,你将掌握以下实用技能:
1. 了解按下键盘按键时的具体过程以及字符的最终去向。
2. 学会检查键盘缓冲区是否有字符等待处理,并清除旧的按键记录。
3. 掌握读取非 ASCII 键盘按键(如功能键和光标箭头)的方法。
4. 懂得如何显示彩色文本以及颜色基于视频显示的 RGB 颜色混合系统的原理。
5. 学会将屏幕划分为彩色面板并分别滚动。
6. 掌握绘制 256 色位图图形的方法。
7. 学会检测鼠标移动和鼠标点击。
二、BIOS 数据区
BIOS 数据区包含了 ROM BIOS 服务例程使用的系统数据,部分内容如下表所示:
| 十六进制偏移量 | 描述 |
| ---- | ---- |
| 0000 – 0007 | 端口地址,COM1 – COM4 |
| 0008 – 000F | 端口地址,LPT1 – LPT4 |
| 0010 – 0011 | 已安装硬件列表 |
| 0012 | 初始化标志 |
| 0013 – 0014 | 内存大小(以千字节为单位) |
| 0015 – 0016 | I/O 通道中的内存 |
| 0017 – 0018 | 键盘状态标志 |
| 0019 | 备用键输入存储 |
| 001A – 001B | 键盘缓冲区指针(头) |
| 001C – 001D | 键盘缓冲区指针(尾) |
| 001E – 003D | 键盘预输入缓冲区 |
| 003E – 0048 | 软盘数据区 |
| 0049 | 当前视频模式 |
| 004A – 004B | 屏幕列数 |
| 004C – 004D | 再生(视频)缓冲区长度(以字节为单位) |
| 004E – 004F | 再生(视频)缓冲区起始偏移量 |
| 0050 – 005F | 视频页面 1 – 8 的光标位置 |
| 0060 | 光标结束行 |
| 0061 | 光标起始行 |
| 0062 | 当前显示的视频页面编号 |
| 0063 – 0064 | 活动显示基地址 |
| 0065 | CRT 模式寄存器 |
| 0066 | 彩色图形适配器寄存器 |
| 0067 – 006B | 磁带数据区 |
| 006C – 0070 | 定时器数据区 |
例如,键盘预输入缓冲区(偏移量为 001Eh)包含了等待 BIOS 处理的按键的 ASCII 码和键盘扫描码。
三、使用 INT 16h 进行键盘输入
3.1 键盘工作原理
键盘输入从键盘控制器芯片开始,最终将字符放置在键盘预输入缓冲区中。由于每个按键产生 2 字节(ASCII 码 + 扫描码),该缓冲区最多可容纳 15 个按键。当用户按下按键时,会发生以下事件:
1. 键盘控制器芯片向 PC 的键盘输入端口发送 8 位数字扫描码(sc)。
2. 输入端口触发中断,这是向 CPU 发送的预定义信号,表示输入 - 输出设备需要关注。CPU 响应并执行 INT 9h 服务例程。
3. INT 9h 服务例程从输入端口获取键盘扫描码(sc),并查找相应的 ASCII 码(ac)(如果有)。它将扫描码和 ASCII 码都插入到键盘预输入缓冲区中。如果扫描码没有匹配的 ASCII 码,预输入缓冲区中该键的 ASCII 码为零或 E0h。
在 MS - Windows 下,当按下扩展键时,其 ASCII 码为 00h 或 E0h,具体如下表所示:
| 按键 | ASCII 码 |
| ---- | ---- |
| Ins, Del, PageUp, PageDown, Home, End, Up arrow, Down arrow, Left arrow, Right arrow | E0h |
| 功能键(F1 – F12) | 00h |
一旦扫描码和 ASCII 码安全地存储在预输入缓冲区中,它们将一直保留,直到当前运行的程序检索它们。在实模式应用程序中,有两种检索方式:
1. 调用使用 INT 16h 的 BIOS 级函数,从键盘预输入缓冲区中检索扫描码和 ASCII 码。这在处理功能键和光标箭头等扩展键时非常有用,因为这些键没有 ASCII 码。
2. 调用使用 INT 21h 的 MS - DOS 级函数,从输入缓冲区中检索 ASCII 码。如果按下了扩展键,则必须再次调用 INT 21h 以检索扫描码。
3.2 INT 16h 函数
INT 16h 在键盘处理方面比 INT 21h 具有明显优势。它可以在一步中同时检索扫描码和 ASCII 码,并且具有设置按键重复率和检索键盘标志状态等额外操作。当不确定用户将按下普通键还是扩展键时,通常最好调用 INT 16h。
以下是 INT 16h 的一些常用函数:
1.
设置按键重复率(03h)
INT 16h 功能 03h 可用于设置键盘按键的重复率。当按住按键时,按键开始重复之前会有 250 到 1000 毫秒的延迟。重复率可以在 1Fh(最慢)到 0(最快)之间。
| 描述 | 接收参数 | 返回值 | 示例调用 |
| ---- | ---- | ---- | ---- |
| 设置按键重复率 | AH = 3
AL = 5
BH = 重复延迟(0 = 250 ms; 1 = 500 ms; 2 = 750 ms; 3 = 1000 ms)
BL = 重复率:0 = 最快(30/秒),1Fh = 最慢(2/秒) | 无 | mov ax,0305h
mov bh,1 ; 500 ms 重复延迟
mov bl,0Fh ; 重复率
int 16h |
-
将按键推入键盘缓冲区(05h)
INT 16h 功能 05h 可将按键推入键盘预输入缓冲区。按键由两个 8 位整数组成:ASCII 码和键盘扫描码。
| 描述 | 接收参数 | 返回值 | 示例调用 |
| ---- | ---- | ---- | ---- |
| 将按键推入键盘缓冲区 | AH = 5
CH = 扫描码
CL = ASCII 码 | 如果预输入缓冲区已满,CF = 1 且 AL = 1;否则,CF = 0,AL = 0。 | mov ah,5
mov ch,3Bh ; F1 键的扫描码
mov cl,0 ; ASCII 码
int 16h | -
等待按键(10h)
INT 16h 功能 10h 可从键盘预输入缓冲区中移除下一个可用的按键。如果没有按键等待,键盘处理程序将等待用户按下按键。
| 描述 | 接收参数 | 返回值 | 示例调用 |
| ---- | ---- | ---- | ---- |
| 等待按键并扫描键盘 | AH = 10h | AH = 键盘扫描码
AL = ASCII 码 | mov ah,10h
int 16h
mov scanCode,ah
mov ASCIICode,al |
以下是一个使用 INT 16h 输入按键并显示每个按键的 ASCII 码和扫描码的示例程序:
TITLE Keyboard Display (Keybd.asm)
; This program displays keyboard scan codes
; and ASCII codes, using INT 16h.
INCLUDE Irvine16.inc
.code
main PROC
mov ax,@data
mov ds,ax
call ClrScr ; clear screen
L1:
mov ah,10h ; keyboard input
int 16h ; using BIOS
call DumpRegs ; AH = scan, AL = ASCII
cmp al,1Bh ; ESC key pressed?
jne L1 ; no: repeat the loop
call ClrScr ; clear screen
exit
main ENDP
END main
-
检查键盘缓冲区(11h)
INT 16h 功能 11h 可查看键盘预输入缓冲区中是否有按键等待。它返回下一个可用按键的 ASCII 码和扫描码(如果有),但不会从缓冲区中移除该按键。
| 描述 | 接收参数 | 返回值 | 示例调用 |
| ---- | ---- | ---- | ---- |
| 检查键盘缓冲区 | AH = 11h | 如果有按键等待,ZF = 0,AH = 扫描码,AL = ASCII 码;否则,ZF = 1。 | mov ah,11h
int 16h
jz NoKeyWaiting ; no key in buffer
mov scanCode,ah
mov ASCIICode,al | -
获取键盘标志(12h)
INT 16h 功能 12h 返回有关键盘标志当前状态的有价值信息。键盘标志位于 BIOS 数据区的地址 00417h – 00418h 处。
| 描述 | 接收参数 | 返回值 | 示例调用 |
| ---- | ---- | ---- | ---- |
| 获取键盘标志 | AH = 12h | AX = 键盘标志的副本 | mov ah,12h
int 16h
mov keyFlags,ax |
键盘标志非常有用,因为它们能告诉你用户使用键盘的很多信息。每个位在其对应的按键被按下或切换打开(如 Caps lock、Scroll lock、Num lock 和 Insert)时为 1。在 Windows 95 和 98 下,也可以通过直接读取段 0040h、偏移量 17h - 18h 处的内存来获取键盘标志字节。
3.3 清除键盘缓冲区
程序通常有一个处理循环,只能被预先安排的按键中断。例如,基于 DOS 的游戏程序经常检查键盘缓冲区,以查看是否按下了箭头键和其他特殊按键,同时显示图形图像。用户可能会按下许多无关的按键,这些按键只会填满键盘预输入缓冲区,但当按下正确的按键时,程序应立即响应命令。
使用 INT 16h 函数,我们可以检查键盘缓冲区中是否有按键等待(功能 11h),并从缓冲区中移除按键(功能 10h)。以下程序演示了一个名为 ClearKeyboard 的过程,它使用循环清除键盘缓冲区,同时检查特定的键盘扫描码:
TITLE Testing ClearKeyboard (ClearKbd.asm)
; This program shows how to clear the keyboard
; buffer while waiting for a particular key.
; To test it, rapidly press random keys to fill
; up the buffer. When you press Esc, the program
; ends immediately.
INCLUDE Irvine16.inc
ClearKeyboard PROTO, scanCode:BYTE
ESC_key = 1 ; scan code
.code
main PROC
L1:
; Display a dot, to show program's progress
mov ah,2
mov dl,'.'
int 21h
mov eax,300 ; delay for 300 ms
call Delay
INVOKE ClearKeyboard, ESC_key ; check for Esc key
jnz L1 ; continue loop if ZF=0
quit:
call Clrscr
exit
main ENDP
;---------------------------------------------------
ClearKeyboard PROC,
scanCode:BYTE
;
; Clears the keyboard while checking for a
; particular scan code.
; Receives: keyboard scan code
; Returns: Zero flag set if the ASCII code is
; found; otherwise, Zero flag is clear.
;---------------------------------------------------
push ax
L1:
mov ah,11h ; check keyboard buffer
int 16h ; any key pressed?
jz noKey ; no: exit now
mov ah,10h ; yes: remove from buffer
int 16h
cmp ah,scanCode ; was it the exit key?
je quit ; yes: exit now (ZF=1)
jmp L1 ; no: check buffer again
noKey:
; no key pressed
or al,1 ; clear zero flag
quit:
pop ax
ret
ClearKeyboard ENDP
END main
3.4 章节回顾
-
哪个中断(16h 或 21h)最适合读取包含功能键和其他扩展键的用户输入?
答案:INT 16h 更适合读取包含功能键和其他扩展键的用户输入,因为它可以在一步中同时检索扫描码和 ASCII 码。 -
键盘输入字符在等待应用程序处理时存储在内存的哪个位置?
答案:存储在键盘预输入缓冲区,其在 BIOS 数据区的偏移量为 001Eh。 -
INT 9h 服务例程执行哪些操作?
答案:INT 9h 服务例程从输入端口获取键盘扫描码,并查找相应的 ASCII 码(如果有),然后将扫描码和 ASCII 码插入到键盘预输入缓冲区中。 -
哪个 INT 16h 函数将按键推入键盘缓冲区?
答案:INT 16h 功能 05h 可将按键推入键盘缓冲区。 -
哪个 INT 16h 函数从键盘缓冲区中移除下一个可用的按键?
答案:INT 16h 功能 10h 可从键盘缓冲区中移除下一个可用的按键。 -
哪个 INT 16h 函数检查键盘缓冲区并返回第一个可用输入的扫描码和 ASCII 码?
答案:INT 16h 功能 11h 可检查键盘缓冲区并返回第一个可用输入的扫描码和 ASCII 码。 -
INT 16h 功能 11h 是否从键盘缓冲区中移除字符?
答案:否,INT 16h 功能 11h 不会从键盘缓冲区中移除字符。 -
哪个 INT 16h 函数提供键盘标志字节的值?
答案:INT 16h 功能 12h 可返回键盘标志字节的值。 -
键盘标志字节中的哪一位表示 ScrollLock 键已被按下?
答案:需要根据键盘标志字节的具体定义来确定 ScrollLock 键对应的位。 -
编写输入键盘标志字节并重复循环直到按下 Ctrl 键的语句。
答案:可以使用 INT 16h 功能 12h 输入键盘标志字节,然后在循环中检查 Ctrl 键对应的位。示例代码如下:
INCLUDE Irvine16.inc
.code
main PROC
L1:
mov ah,12h
int 16h
mov keyFlags,ax
; 假设 Ctrl 键对应的位为第 n 位
test ax, (1 << n)
jz L1 ; 如果 Ctrl 键未按下,继续循环
exit
main ENDP
END main
-
挑战:第 16.2.2 节中的 ClearKeyboard 过程仅检查单个键盘扫描码。假设你的程序需要检查多个扫描码(例如四个光标箭头)。请展示对该过程进行代码修改的示例,使其成为可能。
答案:可以修改 ClearKeyboard 过程,使用一个数组来存储多个扫描码,然后在循环中检查每个扫描码。示例代码如下:
TITLE Testing ClearKeyboard (ClearKbd.asm)
; This program shows how to clear the keyboard
; buffer while waiting for a particular key.
; To test it, rapidly press random keys to fill
; up the buffer. When you press a specific key, the program
; ends immediately.
INCLUDE Irvine16.inc
ClearKeyboard PROTO, scanCodes:DWORD, numCodes:DWORD
; 定义四个光标箭头的扫描码
SCAN_CODES EQU <1, 2, 3, 4> ; 这里的扫描码仅为示例
NUM_CODES EQU 4
.code
main PROC
L1:
; Display a dot, to show program's progress
mov ah,2
mov dl,'.'
int 21h
mov eax,300 ; delay for 300 ms
call Delay
INVOKE ClearKeyboard, ADDR SCAN_CODES, NUM_CODES ; check for multiple keys
jnz L1 ; continue loop if ZF=0
quit:
call Clrscr
exit
main ENDP
;---------------------------------------------------
ClearKeyboard PROC,
scanCodes:DWORD, numCodes:DWORD
;
; Clears the keyboard while checking for multiple scan codes.
; Receives: address of an array of keyboard scan codes, number of scan codes
; Returns: Zero flag set if one of the ASCII codes is
; found; otherwise, Zero flag is clear.
;---------------------------------------------------
push ax
push cx
L1:
mov ah,11h ; check keyboard buffer
int 16h ; any key pressed?
jz noKey ; no: exit now
mov ah,10h ; yes: remove from buffer
int 16h
mov cx, numCodes
mov esi, scanCodes
L2:
cmp ah, [esi]
je quit ; yes: exit now (ZF=1)
add esi, 4
loop L2
jmp L1 ; no: check buffer again
noKey:
; no key pressed
or al,1 ; clear zero flag
quit:
pop cx
pop ax
ret
ClearKeyboard ENDP
END main
四、使用 INT 10h 进行视频编程
4.1 基本背景
当应用程序需要在文本模式下在屏幕上写入字符时,可以选择以下三种输出类型:
1.
MS - DOS 级访问
任何运行或模拟 MS - DOS 的计算机都可以使用 INT 21h 将文本写入视频显示器。输入/输出可以轻松重定向到其他设备(如打印机或磁盘)。但输出速度较慢,并且无法控制文本颜色。
2.
BIOS 级访问
使用 INT 10h 函数(即 BIOS 服务)输出字符。它们的执行速度比 INT 21h 快,并且可以指定文本颜色。在填充大的屏幕区域时,通常可以检测到轻微的延迟。输出不能重定向。
3.
直接视频内存访问
将字符直接移动到视频 RAM 中,因此执行是即时的。输出不能重定向。在 MS - DOS 时代,文字处理器和电子表格程序都使用这种方法。在 Windows NT、2000、XP 及更高版本中,此方法仅限于全屏模式。
应用程序会根据自身需求选择不同的访问级别。那些对性能要求最高的应用程序选择直接视频访问;其他应用程序选择 BIOS 级访问。当输出可能需要重定向或屏幕需要与其他程序共享时,使用 MS - DOS 级访问。需要注意的是,MS - DOS 中断使用 BIOS 级例程来完成其工作,而 BIOS 例程使用直接视频访问来产生输出。
4.2 全屏模式下运行程序
使用视频 BIOS 绘制图形的程序应在以下环境之一中执行:
1. 纯 MS - DOS
2. Linux 下的 DOS 模拟器
3. 在 MS - Windows 下的全屏模式
在 MS - Windows 中,有几种方法可以切换到全屏模式:
1. 在 Windows XP 中,为程序的 EXE 文件创建快捷方式。然后打开快捷方式的属性对话框,选择“选项”,并在“显示选项”组中选择“全屏模式”。(注意:Windows Vista 不支持在全屏模式下运行 16 位 EXE 程序。)
2. 从“开始”菜单打开命令窗口,然后按 Alt - Enter 切换到全屏模式。使用 CD(更改目录)命令导航到 EXE 文件的目录,并通过输入其名称来运行程序。Alt - Enter 是一个切换键,再次按下它将使程序返回窗口模式。
4.3 理解视频文本
基于 Intel 的系统有两种基本视频模式:文本模式和图形模式。程序只能在一种模式下运行,不能同时在两种模式下运行:
1.
文本模式
程序在文本模式下将 ASCII 字符写入屏幕。BIOS 中的内置字符生成器为每个字符生成位图图像。在文本模式下,程序无法绘制任意线条和形状。
2.
图形模式
程序在图形模式下控制每个屏幕像素的外观。操作相对原始,因为没有内置的线条和形状绘制函数。可以使用内置函数在图形模式下在屏幕上写入文本,并且可以用不同的字体替换内置字体。MS - Windows 提供了一组用于在图形模式下绘制形状和线条的函数。
当计算机在 MS - DOS 下启动时,视频控制器设置为视频模式 3(彩色文本,默认 80 列×25 行)。在文本模式下,行从屏幕顶部开始编号,行号为 0。每一行的高度是当前活动字体的字符单元格高度。列从屏幕左侧开始编号,列号为 0。每一列的宽度是字符单元格的宽度。
4.4 字体
字符由内存驻留的字符字体表生成。BIOS 允许程序在运行时重写字符表,因此可以显示自定义字体。
通过以上内容,我们深入了解了 BIOS 级编程中的键盘输入和视频编程相关知识,掌握了如何在底层与计算机硬件进行交互,为进一步开发高性能的应用程序打下了坚实的基础。
BIOS 级编程:深入计算机底层的奥秘
五、使用 INT 10h 绘制图形
5.1 INT 10h 与像素相关的函数
INT 10h 提供了一些与像素操作相关的函数,这些函数可以帮助我们在屏幕上进行图形绘制。通过这些函数,我们能够精确地控制每个像素的显示,从而实现各种复杂的图形效果。这些函数可以直接操作屏幕上的像素,实现像素的设置、读取等操作,为图形绘制提供了基础。
| 功能描述 | 调用参数 | 返回值 |
|---|---|---|
| 设置像素颜色 | AH = 0Ch,AL = 颜色值,CX = 列坐标,DX = 行坐标 | 无 |
| 读取像素颜色 | AH = 0Dh,CX = 列坐标,DX = 行坐标 | AL = 像素颜色值 |
以下是一个简单的示例代码,用于设置屏幕上指定位置的像素颜色:
TITLE SetPixelExample (SetPixel.asm)
INCLUDE Irvine16.inc
.code
main PROC
mov ax, @data
mov ds, ax
mov ah, 0Ch ; 设置像素颜色功能
mov al, 1 ; 颜色值
mov cx, 100 ; 列坐标
mov dx, 50 ; 行坐标
int 10h
exit
main ENDP
END main
5.2 DrawLine 程序
DrawLine 程序用于在屏幕上绘制直线。其基本原理是根据直线的起点和终点坐标,通过一定的算法计算出直线上的各个像素点,并依次设置这些像素点的颜色。以下是一个简化的 DrawLine 程序示例:
TITLE DrawLineExample (DrawLine.asm)
INCLUDE Irvine16.inc
DrawLine PROC, x1:WORD, y1:WORD, x2:WORD, y2:WORD, color:BYTE
; 实现直线绘制算法
; 这里省略具体的算法实现,可使用 Bresenham 算法等
ret
DrawLine ENDP
.code
main PROC
mov ax, @data
mov ds, ax
INVOKE DrawLine, 10, 10, 100, 100, 2 ; 绘制直线
exit
main ENDP
END main
5.3 Cartesian Coordinates Program
Cartesian Coordinates Program 用于处理笛卡尔坐标。在图形编程中,我们常常需要将笛卡尔坐标转换为屏幕坐标,以便在屏幕上正确显示图形。该程序可以实现笛卡尔坐标与屏幕坐标之间的转换,使得我们可以更方便地进行图形绘制。以下是一个简单的示例:
TITLE CartesianCoordinatesExample (Cartesian.asm)
INCLUDE Irvine16.inc
; 定义笛卡尔坐标转换函数
ConvertToScreenCoordinates PROC, x:WORD, y:WORD, screenX:DWORD, screenY:DWORD
; 实现坐标转换算法
; 这里省略具体的算法实现
ret
ConvertToScreenCoordinates ENDP
.code
main PROC
mov ax, @data
mov ds, ax
mov ax, 50 ; 笛卡尔 x 坐标
mov bx, 50 ; 笛卡尔 y 坐标
INVOKE ConvertToScreenCoordinates, ax, bx, ADDR screenX, ADDR screenY
exit
main ENDP
END main
5.4 转换笛卡尔坐标为屏幕坐标
在图形编程中,我们需要将笛卡尔坐标转换为屏幕坐标。其转换过程可以通过以下步骤实现:
1. 确定屏幕的原点位置,通常屏幕的左上角为原点 (0, 0)。
2. 根据笛卡尔坐标的范围和屏幕的分辨率,计算出坐标的缩放比例。
3. 将笛卡尔坐标乘以缩放比例,并加上原点的偏移量,得到屏幕坐标。
以下是一个简单的转换函数示例:
ConvertToScreen PROC, x:WORD, y:WORD, screenX:DWORD, screenY:DWORD
; 假设屏幕分辨率为 640x480
mov ax, x
mov bx, 640 ; 屏幕宽度
mul bx ; 乘以屏幕宽度
mov dx, ax ; 保存结果
mov ax, y
mov bx, 480 ; 屏幕高度
mul bx ; 乘以屏幕高度
mov [screenX], dx ; 保存屏幕 x 坐标
mov [screenY], ax ; 保存屏幕 y 坐标
ret
ConvertToScreen ENDP
5.5 章节回顾
-
INT 10h 中用于设置像素颜色的功能号是多少?
答案:功能号为 0Ch。 -
DrawLine 程序的主要作用是什么?
答案:用于在屏幕上绘制直线。 -
如何将笛卡尔坐标转换为屏幕坐标?
答案:通过确定屏幕原点、计算缩放比例,并根据坐标范围进行转换。
六、内存映射图形
6.1 Mode 13h:320×200,256 颜色
Mode 13h 是一种常见的图形模式,其分辨率为 320×200 像素,支持 256 种颜色。在这种模式下,屏幕上的每个像素对应视频内存中的一个字节,通过直接操作视频内存,可以快速地实现图形的绘制。以下是进入 Mode 13h 模式的示例代码:
TITLE EnterMode13hExample (Mode13h.asm)
INCLUDE Irvine16.inc
.code
main PROC
mov ax, 0013h ; 设置视频模式为 Mode 13h
int 10h
exit
main ENDP
END main
6.2 内存映射图形程序
内存映射图形程序通过直接访问视频内存来绘制图形。在 Mode 13h 模式下,视频内存的起始地址为 A000:0000。以下是一个简单的内存映射图形程序示例,用于在屏幕上绘制一个彩色矩形:
TITLE MemoryMappedGraphicsExample (MemoryMap.asm)
INCLUDE Irvine16.inc
.code
main PROC
mov ax, 0013h ; 设置视频模式为 Mode 13h
int 10h
mov ax, 0A000h ; 视频内存段地址
mov es, ax
mov di, 0 ; 视频内存偏移地址
mov cx, 320 * 200 ; 像素总数
mov al, 1 ; 颜色值
L1:
mov [es:di], al ; 设置像素颜色
inc di
loop L1
exit
main ENDP
END main
6.3 章节回顾
-
Mode 13h 的分辨率和颜色数分别是多少?
答案:分辨率为 320×200 像素,支持 256 种颜色。 -
内存映射图形程序的基本原理是什么?
答案:通过直接访问视频内存来绘制图形。
七、鼠标编程
7.1 鼠标 INT 33h 函数
INT 33h 提供了一系列用于鼠标操作的函数,包括初始化鼠标、获取鼠标位置、检测鼠标点击等。以下是一些常用的 INT 33h 函数:
| 功能描述 | 调用参数 | 返回值 |
| ---- | ---- | ---- |
| 初始化鼠标 | AX = 0000h | CX = 最大水平位置,DX = 最大垂直位置 |
| 获取鼠标位置和按钮状态 | AX = 0003h | BX = 按钮状态,CX = 水平位置,DX = 垂直位置 |
以下是一个初始化鼠标的示例代码:
TITLE MouseInitializationExample (MouseInit.asm)
INCLUDE Irvine16.inc
.code
main PROC
mov ax, 0000h ; 初始化鼠标功能
int 33h
exit
main ENDP
END main
7.2 鼠标跟踪程序
鼠标跟踪程序用于实时跟踪鼠标的位置和按钮状态。以下是一个简单的鼠标跟踪程序示例:
TITLE MouseTrackingExample (MouseTrack.asm)
INCLUDE Irvine16.inc
.code
main PROC
mov ax, 0000h ; 初始化鼠标
int 33h
L1:
mov ax, 0003h ; 获取鼠标位置和按钮状态
int 33h
; 处理鼠标位置和按钮状态
jmp L1
exit
main ENDP
END main
7.3 章节回顾
-
INT 33h 中用于初始化鼠标的功能号是多少?
答案:功能号为 0000h。 -
鼠标跟踪程序的主要作用是什么?
答案:实时跟踪鼠标的位置和按钮状态。
八、总结
通过对 BIOS 级编程的学习,我们深入了解了计算机底层的输入输出机制,包括键盘输入、视频编程、图形绘制、内存映射图形以及鼠标编程等方面。掌握这些知识可以帮助我们开发出高性能、低延迟的应用程序,特别是在嵌入式系统开发等领域具有重要的应用价值。同时,我们也学习了如何使用 INT 16h、INT 10h 和 INT 33h 等中断来实现各种功能,以及如何处理不同的输入输出设备。在未来的学习和开发中,我们可以进一步深入研究这些技术,不断提升自己的编程能力。
以下是整个 BIOS 级编程的流程 mermaid 图:
graph LR
A[引言] --> B[BIOS 数据区]
B --> C[键盘输入(INT 16h)]
C --> D[视频编程(INT 10h)]
D --> E[绘制图形(INT 10h)]
E --> F[内存映射图形]
F --> G[鼠标编程(INT 33h)]
G --> H[总结]
通过这个流程图,我们可以清晰地看到 BIOS 级编程各个部分之间的关系和学习顺序。希望大家通过本文的学习,能够对 BIOS 级编程有更深入的理解和掌握。
超级会员免费看
74

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



