BIOS 级编程与鼠标编程详解
1. 像素颜色设置与绘制
在某些编程场景中,我们需要对屏幕像素的颜色进行设置并绘制像素。以下是一段相关的代码示例:
; Sets individual palette colors and draws
; several pixels.
;------------------------------------------------
; Change the color at index 1 to white (63,63,63).
mov dx,VIDEO_PALLETE_PORT
mov al,1
; set palette index 1
out dx,al
mov dx,COLOR_SELECTION_PORT
mov al,63
; red
out dx,al
mov al,63
; green
out dx,al
mov al,63
; blue
out dx,al
; Calculate the video buffer offset of the first pixel.
; Method is specific to mode 13h, which is 320 X 200.
mov xVal,160
; middle of screen
mov yVal,100
mov ax,320
; 320 for video mode 13h
mul yVal
; y-coordinate
add ax,xVAl
; x-coordinate
; Place the color index into the video buffer.
mov cx,10
; draw 10 pixels
mov di,ax
; AX contains buffer offset
; Draw the pixels now. By default, the assembler assumes
; DI is an offset from the segment address in DS. The
; segment override ES:[DI] tells the CPU to use the segment
; address in ES instead. ES currently points to VRAM.
DP1:
mov BYTE PTR es:[di],COLOR_INDEX
add di,5
; move 5 pixels to the right
loop DP1
ret
Draw_Some_Pixels ENDP
END main
这段代码的主要步骤如下:
1.
颜色设置
:将调色板索引 1 的颜色设置为白色(RGB 值均为 63)。
2.
计算像素偏移
:针对 320x200 的 13h 视频模式,计算第一个像素在视频缓冲区的偏移量。
3.
绘制像素
:在视频缓冲区中绘制 10 个像素,每个像素向右移动 5 个位置。
如果要绘制垂直线,可以每次给
DI
的值加上 320 来移动到下一行像素;绘制斜率为 1 的对角线,则给
DI
加上 321。而绘制任意两点之间的直线,推荐使用 Bresenham 算法,该算法在很多网站上都有详细解释。
2. 视频模式 13h 相关问题
以下是关于视频模式 13h 的一些常见问题及解答:
1.
判断对错
:视频模式 13h 将屏幕像素映射为二维字节数组,每个字节对应两个像素。(答案:错误)
2.
判断对错
:在视频模式 13h 中,每个屏幕行使用 320 字节的存储空间。(答案:正确)
3.
像素颜色设置方式
:在视频模式 13h 中,通过设置调色板的索引和对应的 RGB 值来设置像素颜色。
4.
颜色索引的使用
:颜色索引用于在调色板中选择特定的颜色,将其放入视频缓冲区以显示相应颜色的像素。
5.
调色板元素内容
:在视频模式 13h 中,调色板的每个元素包含一个 RGB 颜色值。
6.
深灰色的 RGB 值
:(16, 16, 16)
7.
白色的 RGB 值
:(63, 63, 63)
8.
亮红色的 RGB 值
:(63, 0, 0)
9.
挑战:将视频模式 13h 的屏幕背景颜色设置为绿色
:
; 假设调色板索引 0 用于背景颜色
mov dx,VIDEO_PALLETE_PORT
mov al,0
out dx,al
mov dx,COLOR_SELECTION_PORT
mov al,0
; red
out dx,al
mov al,63
; green
out dx,al
mov al,0
; blue
out dx,al
- 挑战:将视频模式 13h 的屏幕背景颜色设置为白色 :
; 假设调色板索引 0 用于背景颜色
mov dx,VIDEO_PALLETE_PORT
mov al,0
out dx,al
mov dx,COLOR_SELECTION_PORT
mov al,63
; red
out dx,al
mov al,63
; green
out dx,al
mov al,63
; blue
out dx,al
3. 鼠标编程基础
鼠标通常通过 PS - 2 鼠标端口、RS - 232 串行端口、USB 端口或无线连接与计算机主板相连。在检测鼠标之前,MS - DOS 需要安装设备驱动程序,MS - Windows 也有内置的鼠标驱动程序,这里我们主要关注 MS - DOS 提供的功能。
鼠标的移动以“米基(mickeys)”为单位进行跟踪,1 米基大约代表鼠标实际移动 1/200 英寸。鼠标的米基与像素的比例可以设置,默认情况下,每 8 个水平像素对应 8 米基,每 8 个垂直像素对应 16 米基。此外,还有一个双速阈值,默认值为每秒 64 米基。
4. 鼠标 INT 33h 函数
INT 33h 提供了关于鼠标的各种信息,包括当前位置、最后点击的按钮、速度等,还可以用于显示或隐藏鼠标光标。以下是一些重要的 INT 33h 函数:
| 函数编号 | 描述 | 接收参数 | 返回参数 | 示例调用 |
| ---- | ---- | ---- | ---- | ---- |
| 0 | 重置鼠标并获取状态 | AX = 0 | 如果支持鼠标,AX = FFFFh 且 BX = 鼠标按钮数量;否则,AX = 0 |
mov ax,0
int 33h
cmp ax,0
je MouseNotAvailable
mov numberOfButtons,bx
|
| 1 | 显示鼠标指针 | AX = 1 | 无 |
mov ax,1
int 33h
|
| 2 | 隐藏鼠标指针 | AX = 2 | 无 |
mov ax,2
int 33h
|
| 3 | 获取鼠标位置和状态 | AX = 3 | BX = 鼠标按钮状态
CX = X 坐标(像素)
DX = Y 坐标(像素) |
mov ax,3
int 33h
test bx,1
jne Left_Button_Down
test bx,2
jne Right_Button_Down
test bx,4
jne Center_Button_Down
mov Xcoord,cx
mov yCoord,dx
|
| 4 | 设置鼠标位置 | AX = 4
CX = X 坐标(像素)
DX = Y 坐标(像素) | 无 |
mov ax,4
mov cx,200
mov dx,100
int 33h
|
| 5 | 获取鼠标按钮按下信息 | AX = 5
BX = 按钮 ID(0 = 左键,1 = 右键,2 = 中键) | AX = 按钮状态
BX = 按钮按下计数器
CX = 最后一次按钮按下的 X 坐标
DX = 最后一次按钮按下的 Y 坐标 |
mov ax,5
mov bx,0
int 33h
test ax,1
jz skip
mov X_coord,cx
mov Y_coord,dx
|
| 6 | 获取鼠标按钮释放信息 | AX = 6
BX = 按钮 ID(0 = 左键,1 = 右键,2 = 中键) | AX = 按钮状态
BX = 按钮释放计数器
CX = 最后一次按钮释放的 X 坐标
DX = 最后一次按钮释放的 Y 坐标 |
mov ax,6
mov bx,0
int 33h
test ax,1
jz skip
mov X_coord,cx
mov Y_coord,dx
|
| 7 | 设置鼠标水平移动范围 | AX = 7
CX = 最小 X 坐标(像素)
DX = 最大 X 坐标(像素) | 无 |
mov ax,7
mov cx,100
mov dx,700
int 33h
|
| 8 | 设置鼠标垂直移动范围 | AX = 8
CX = 最小 Y 坐标(像素)
DX = 最大 Y 坐标(像素) | 无 |
mov ax,8
mov cx,100
mov dx,500
int 33h
|
5. 坐标转换
在鼠标编程中,经常需要进行像素坐标和字符坐标的转换:
-
像素坐标转字符坐标
:标准的 MS - DOS 文本字体宽度为 8 像素,高度为 16 像素。可以使用公式
C = int(P / D)
将像素坐标
P
转换为字符坐标
C
,其中
D
为字符尺寸。例如,若字符宽度为 8 像素,INT 33 函数 3 返回的 X 坐标为 100 像素,则对应的字符位置为
int(100 / 8) = 12
。
-
字符坐标转像素坐标
:使用公式
P = C * D
进行转换,其中
C
为字符坐标,
P
为像素坐标,
D
为字符尺寸。在水平方向上,
P
是字符单元格左侧的像素坐标;在垂直方向上,
P
是字符单元格顶部的像素坐标。例如,若字符宽度为 8 像素,要将鼠标置于字符单元格 12,则该单元格最左侧像素的 X 坐标为
12 * 8 = 96
。
以下是一个简单的流程表示坐标转换:
graph LR
A[像素坐标] -->|P / D| B[字符坐标]
C[字符坐标] -->|C * D| D[像素坐标]
6. 其他鼠标函数
除了上述常见的鼠标函数外,还有一些其他的 INT 33h 函数用于配置鼠标和控制其行为,如下表所示:
| 函数编号 | 描述 | 输入/输出参数 |
| ---- | ---- | ---- |
| AX = 0Fh | 设置水平和垂直鼠标移动每 8 像素对应的米基数 | 接收:CX = 水平米基数,DX = 垂直米基数。默认值为 CX = 8,DX = 16 |
| AX = 10h | 设置鼠标排除区域(防止鼠标进入矩形区域) | 接收:CX, DX = 左上角的 X, Y 坐标。SI, DI = 右下角的 X, Y 坐标 |
| AX = 13h | 设置双速阈值 | 接收:DX = 阈值速度(米基/秒),默认值为 64 |
| AX = 1Ah | 设置鼠标灵敏度和阈值 | 接收:BX = 水平速度(米基/秒),CX = 垂直速度(米基/秒),DX = 双速阈值(米基/秒) |
| AX = 1Bh | 获取鼠标灵敏度和阈值 | 返回:BX = 水平速度,CX = 垂直速度,DX = 双速阈值 |
| AX = 1Fh | 禁用鼠标驱动程序 | 返回:若失败,AX = FFFFh |
| AX = 20h | 启用鼠标驱动程序 | 无 |
| AX = 24h | 获取鼠标信息 | 错误时返回 FFFFh;否则,返回:BH = 主版本号,BL = 次版本号,CH = 鼠标类型(1 = 总线,2 = 串行,3 = InPort,4 = PS/2,5 = HP);CL = IRQ 号(PS/2 鼠标为 0) |
7. 鼠标跟踪程序
这里有一个简单的鼠标跟踪程序,它可以跟踪文本鼠标光标的移动。X 和 Y 坐标会在屏幕右下角不断更新,当用户按下左键时,鼠标的位置会显示在屏幕左下角。以下是源代码:
TITLE Tracking the Mouse (mouse.asm)
; Demonstrates basic mouse functions available via INT 33h.
; In Standard DOS mode, each character position in the DOS
; window equals 8 mouse units.
INCLUDE Irvine16.inc
GET_MOUSE_STATUS = 0
SHOW_MOUSE_POINTER = 1
HIDE_MOUSE_POINTER = 2
GET_CURSOR_SIZE = 3
GET_BUTTON_PRESS_INFO = 5
GET_MOUSE_POSITION_AND_STATUS = 3
ESCkey = 1Bh
.data
greeting BYTE "[Mouse.exe] Press Esc to quit",0
statusLine BYTE "Left button: "
BYTE "Mouse position: ",0
blanks BYTE " ",0
xCoord WORD 0
; current X-coordinate
yCoord WORD 0
; current Y-coordinate
xPress WORD 0
; X-coord of last button press
yPress WORD 0
; Y-coord of last button press
; Display coordinates.
statusRow BYTE ?
statusCol BYTE 15
buttonPressCol BYTE 20
statusCol2 BYTE 60
coordCol BYTE 65
.code
main PROC
mov
ax,@data
mov
ds,ax
call
Clrscr
; Get the screen X/Y coordinates.
call
GetMaxXY
; DH = rows, DL = columns
dec
dh
; calculate status row value
mov
statusRow,dh
; Hide the text cursor and display the mouse.
call
HideCursor
mov
dx,OFFSET greeting
call
WriteString
call
ShowMousePointer
; Display status information on the bottom screen line.
mov
dh,statusRow
mov
dl,0
call
Gotoxy
mov
dx,OFFSET statusLine
call
Writestring
; Loop: show mouse coordinates, check for left mouse
; button press or keypress (Esc key).
L1:
call
ShowMousePosition
call
LeftButtonPress
; check for button press
mov
ah,11h
; key pressed already?
int
16h
jz
L2
; no, continue the loop
mov
ah,10h
; remove key from buffer
int
16h
cmp
al,ESCkey
; yes. Is it the ESC key?
je
quit
; yes, quit the program
L2:
jmp
L1
; no, continue the loop
; Hide the mouse, restore the text cursor, clear
; the screen, and wait for a key press.
quit:
call
HideMousePointer
call
ShowCursor
call
Clrscr
call
WaitMsg
exit
main ENDP
;---------------------------------------------------------
GetMousePosition PROC USES ax
;
; Gets the current mouse position and button status.
; Receives: nothing
; Returns: BX = button status (0 = left button down,
; (1 = right button down, 2 = center button down)
; CX = X-coordinate
; DX = Y-coordinate
;---------------------------------------------------------
mov
ax,GET_MOUSE_POSITION_AND_STATUS
int
33h
ret
GetMousePosition ENDP
;---------------------------------------------------------
HideCursor PROC USES ax cx
;
; Hide the text cursor by setting its top line
; value to an illegal value.
; Receives: nothing. Returns: nothing
;---------------------------------------------------------
mov
ah,GET_CURSOR_SIZE
int
10h
or
ch,30h
; set upper row to illegal value
mov
ah,1
; set cursor size
int
10h
ret
HideCursor ENDP
;---------------------------------------------------------
ShowCursor PROC USES ax cx
;
; Show the text cursor by setting size to default.
; Receives: nothing. Returns: nothing
;---------------------------------------------------------
mov
ah,GET_CURSOR_SIZE
int
10h
mov
ah,1
; set cursor size
mov
cx,0607h
; default size
int
10h
ret
ShowCursor ENDP
;---------------------------------------------------------
HideMousePointer PROC USES ax
;
; Hides the mouse pointer.
; Receives: nothing. Returns: nothing
;---------------------------------------------------------
mov
ax,HIDE_MOUSE_POINTER
int
33h
ret
HideMousePointer ENDP
;---------------------------------------------------------
ShowMousePointer PROC USES ax
;
; Makes the mouse pointer visible.
; Receives: nothing. Returns: nothing
;---------------------------------------------------------
mov
ax,SHOW_MOUSE_POINTER ; make mouse cursor visible
int
33h
ret
ShowMousePointer ENDP
;---------------------------------------------------------
LeftButtonPress PROC
;
; Checks for the most recent left mouse button press
; and displays the mouse location.
; Receives: nothing. Returns: nothing
;---------------------------------------------------------
pusha
mov
ax,GET_BUTTON_PRESS_INFO
mov
bx,0
; specify the left button
int
33h
; Exit proc if the coordinates have not changed.
cmp
cx,xPress
; same X coordinate?
jne
L1
; no: continue
cmp
dx,yPress
; same Y coordinate?
je
L2
; yes: exit
; Coordinates have changed, so save them.
L1:
mov
xPress,cx
mov
yPress,dx
; Position the cursor, clear the old numbers.
mov
dh,statusRow
; screen row
mov
dl,statusCol
; screen column
call
Gotoxy
push
dx
mov
dx,OFFSET blanks
call
WriteString
pop
dx
; Show coordinates where mouse button was pressed.
call
Gotoxy
mov
ax,xCoord
call
WriteDec
mov
dl,buttonPressCol
call
Gotoxy
mov
ax,yCoord
call
WriteDec
L2:
popa
ret
LeftButtonPress ENDP
;---------------------------------------------------------
SetMousePosition PROC
;
; Set the mouse's position on the screen.
; Receives: CX = X-coordinate
; DX = Y-coordinate
; Returns: nothing
;---------------------------------------------------------
mov
ax,4
int
33h
ret
SetMousePosition ENDP
;---------------------------------------------------------
ShowMousePosition PROC
;
; Get and show the mouse coordinates at the
; bottom of the screen.
; Receives: nothing
; Returns: nothing
;---------------------------------------------------------
pusha
call
GetMousePosition
; Exit proc if the coordinates have not changed.
cmp
cx,xCoord
; same X coordinate?
jne
L1
; no: continue
cmp
dx,yCoord
; same Y coordinate?
je
L2
; yes: exit
; Save the new X and Y coordinates.
L1:
mov
xCoord,cx
mov
yCoord,dx
; Position the cursor, clear the old numbers.
mov
dh,statusRow
; screen row
mov
dl,statusCol2
; screen column
call
Gotoxy
push
dx
mov
dx,OFFSET blanks
call
WriteString
pop
dx
; Show the mouse coordinates.
call
Gotoxy
mov
ax,xCoord
call
WriteDec
mov
dl,coordCol
; screen column
call
Gotoxy
mov
ax,yCoord
call
WriteDec
L2:
popa
ret
ShowMousePosition ENDP
END main
该程序的主要流程如下:
1. 初始化数据段和屏幕,隐藏文本光标并显示鼠标指针。
2. 在屏幕底部显示状态信息。
3. 进入循环,不断显示鼠标坐标,检查左键按下或按键(Esc 键)情况。
4. 如果按下 Esc 键,退出程序,隐藏鼠标指针,恢复文本光标,清屏并等待按键。
8. 程序行为变化
程序的行为会根据两个因素有所变化:
1. 所运行的 MS - Windows 版本。
2. 是在控制台窗口还是全屏模式下运行。
例如,在 Windows XP 及更高版本中:
-
全屏模式
:鼠标光标是一个实心块,其坐标似乎每次变化一个像素,而只有当水平移动 8 个像素或垂直移动 16 个像素时,鼠标光标才会从一个字符跳到下一个字符。
-
控制台窗口模式
:鼠标光标是一个指针,其坐标水平和垂直方向每次变化 8 个像素。
9. 鼠标编程相关问题解答
以下是关于鼠标编程的一些常见问题及解答:
1.
哪个 INT 33h 函数用于重置鼠标并获取鼠标状态?
- 答案:INT 33h 函数 0。
2.
编写重置鼠标并获取鼠标状态的 ASM 语句。
asm
mov ax,0
int 33h
cmp ax,0
je MouseNotAvailable
mov numberOfButtons,bx
3.
哪个 INT 33h 函数用于显示和隐藏鼠标指针?
- 答案:显示鼠标指针用 INT 33h 函数 1;隐藏鼠标指针用 INT 33h 函数 2。
4.
编写隐藏鼠标指针的 ASM 语句。
asm
mov ax,2
int 33h
5.
哪个 INT 33h 函数用于获取鼠标位置和状态?
- 答案:INT 33h 函数 3。
6.
编写获取鼠标位置并将其存储在变量
mouseX
和
mouseY
中的 ASM 语句。
asm
mov ax,3
int 33h
mov mouseX,cx
mov mouseY,dx
7.
哪个 INT 33h 函数用于设置鼠标位置?
- 答案:INT 33h 函数 4。
8.
编写将鼠标指针设置到 X = 100 和 Y = 400 的 ASM 语句。
asm
mov ax,4
mov cx,100
mov dx,400
int 33h
9.
哪个 INT 33h 函数用于获取鼠标按钮按下信息?
- 答案:INT 33h 函数 5。
10.
编写当左键按下时跳转到标签
Button1
的 ASM 语句。
asm
mov ax,5
mov bx,0
int 33h
test ax,1
jne Button1
11.
哪个 INT 33h 函数用于获取鼠标按钮释放信息?
- 答案:INT 33h 函数 6。
12.
编写获取右键释放时鼠标位置并将其存储在变量
mouseX
和
mouseY
中的 ASM 语句。
asm
mov ax,6
mov bx,1
int 33h
mov mouseX,cx
mov mouseY,dx
13.
编写将鼠标垂直移动范围设置为 200 和 400 的 ASM 语句。
asm
mov ax,8
mov cx,200
mov dx,400
int 33h
14.
编写将鼠标水平移动范围设置为 300 和 600 的 ASM 语句。
asm
mov ax,7
mov cx,300
mov dx,600
int 33h
通过以上内容,我们对 BIOS 级编程中的像素绘制、视频模式 13h、鼠标编程等方面有了较为全面的了解,掌握了相关的编程技巧和常见问题的解决方法。在实际应用中,可以根据具体需求灵活运用这些知识。
超级会员免费看
53

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



