实模式下开发Pong游戏OS:定时与清屏方法咨询
实模式下的定时与清屏方案(附保护模式C实现)
实模式定时:BIOS INT 1Ah实现1秒等待
实模式下可直接用BIOS中断INT 1Ah(系统时间服务)实现定时。PC系统时钟以约18.2065Hz频率更新,每秒产生18次左右计时滴答,通过读取当前滴答数并等待其增加18次,即可实现近似1秒的延迟。
Intel汇编代码示例:
; 等待约1秒的函数 wait_one_second: mov ah, 00h ; INT 1Ah功能00h:获取当前系统滴答数(存储在cx:dx) int 1Ah mov bx, dx ; 保存当前滴答数的低16位 wait_loop: int 1Ah ; 再次读取当前滴答数 cmp dx, bx jl wait_loop ; 处理滴答数溢出(短时间可忽略但建议保留) sub dx, bx cmp dx, 18 ; 等待18个滴答(≈1秒) jl wait_loop ret
该方案在qemu-system-i386实模式环境下完全兼容,无需切换保护模式。
实模式清屏方案
实模式下有两种可靠的清屏方式:
方法1:BIOS INT 10h屏幕滚动功能
利用INT 10h的06h功能(上滚屏幕),将整个屏幕区域滚动0行(即清空),同时重置光标到左上角:
clear_screen: mov ah, 06h ; INT 10h功能06h:上滚屏幕 mov al, 00h ; 滚动行数为0(清屏) mov bh, 07h ; 文本属性:前景白色,背景黑色 mov ch, 00h ; 滚动区域左上角行号(0-based) mov cl, 00h ; 滚动区域左上角列号 mov dh, 18h ; 滚动区域右下角行号(24行,0-based) mov dl, 4Fh ; 滚动区域右下角列号(79列,0-based) int 10h ; 将光标移到屏幕左上角 mov ah, 02h ; INT 10h功能02h:设置光标位置 mov bh, 00h ; 页码0 mov dh, 00h ; 目标行号 mov dl, 00h ; 目标列号 int 10h ret
方法2:直接写入文本模式显存
实模式下文本模式显存起始地址为0xB8000,每个字符占用2字节(ASCII码+属性字节)。通过批量写入空格字符(ASCII 0x20)+黑底白字属性(0x07),直接清空显存:
clear_screen_mem: mov ax, 0B800h mov es, ax ; 将ES段寄存器指向显存段 mov di, 0 ; 显存起始偏移 mov cx, 2000 ; 80列×25行=2000个字符 mov ax, 0720h ; 0x20是空格ASCII,0x07是黑底白字属性 rep stosw ; 重复写入CX次AX的值,清空整个显存 ; 重置光标到左上角(同方法1) mov ah, 02h mov bh, 00h mov dh, 00h mov dl, 00h int 10h ret
保护模式下C语言实现定时(若必须切换)
若后续需切换到保护模式,可通过编程PIT(可编程间隔定时器)实现精确定时。PIT基础频率为1.193182MHz,可通过设置分频值实现每秒1次的定时。
步骤1:实现端口读写函数(汇编)
保护模式下无法直接使用BIOS中断,需通过汇编实现inb/outb函数访问硬件端口:
; 32位Intel语法汇编,供C调用 global inb inb: mov dx, [esp+4] ; 获取端口号参数 in al, dx ; 读取端口值到AL ret global outb outb: mov dx, [esp+4] ; 获取端口号 mov al, [esp+8] ; 获取要写入的数据 out dx, al ; 写入端口 ret
步骤2:C语言定时实现
#include <stdint.h> // 声明端口读写函数 unsigned char inb(unsigned short port); void outb(unsigned short port, unsigned char data); // PIT硬件端口定义 #define PIT_CMD_PORT 0x43 #define PIT_CH0_PORT 0x40 // PIT基础频率 #define PIT_BASE_FREQ 1193182 // 目标频率:1Hz(每秒1次) #define TARGET_FREQ 1 // 初始化PIT为方波发生器模式 void pit_init() { // 命令字:通道0,低8位+高8位,模式3(方波发生器) outb(PIT_CMD_PORT, 0x36); // 计算分频值 uint16_t divisor = PIT_BASE_FREQ / TARGET_FREQ; // 写入低8位和高8位 outb(PIT_CH0_PORT, divisor & 0xFF); outb(PIT_CH0_PORT, divisor >> 8); } // 等待1秒的函数 void wait_one_second() { // 锁存通道0的当前计数器值 outb(PIT_CMD_PORT, 0x00); uint16_t start = inb(PIT_CH0_PORT); start |= inb(PIT_CH0_PORT) << 8; // PIT计数器递减,计算目标值(处理溢出) uint16_t target = start - PIT_BASE_FREQ; if (target > start) { // 溢出情况:等待计数器从0递减到target while (1) { outb(PIT_CMD_PORT, 0x00); uint16_t current = inb(PIT_CH0_PORT); current |= inb(PIT_CH0_PORT) << 8; if (current <= target) break; } } else { // 非溢出情况:等待计数器递减到target while (1) { outb(PIT_CMD_PORT, 0x00); uint16_t current = inb(PIT_CH0_PORT); current |= inb(PIT_CH0_PORT) << 8; if (current <= target) break; } } }
内容的提问来源于stack exchange,提问作者DominikSzasz
相关产品推荐
相关产品推荐

