62、BIOS 级编程与鼠标编程详解

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
  1. 挑战:将视频模式 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、鼠标编程等方面有了较为全面的了解,掌握了相关的编程技巧和常见问题的解决方法。在实际应用中,可以根据具体需求灵活运用这些知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值