You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实模式下开发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 15:20:01