首页 文章

如何在裸机16位x86组件中睡觉?

提问于
浏览
5

无论CPU的时钟速度如何,我都希望暂停执行大约0.1秒 . 代码应该直接从引导设备运行,因此不应该使用DOS中断 .

我目前正在使用 int 15h ,但这似乎与我正在用PIT的通道2进行调制的嘟嘟声相冲突 . 我听说过 Channels 0,但我不清楚如何设置它 .

准确性并不重要,但它应该以相同的速度在旧的和现代的计算机上运行 . 因此,循环指令不是一种选择 .

哔声代码和睡眠只是一堆用于改变频率和打开和关闭扬声器的宏 . 如果我在 beepoff 之前呼叫睡眠,哔声似乎不会停止 .

以下是蜂鸣声宏:

%macro beepinit 0
    mov al, 182
    out 43h, al
%endmacro

%macro beepfreq 0
    out 42h, al
    mov al, ah
    out 42h, al
%endmacro

%macro beepon 0
    in al, 61h
    or al, 00000011b
    out 61h, al
%endmacro

%macro beepoff 0
    in al, 61h
    and al, 11111100b
    out 61h, al
%endmacro

和睡眠一:

%macro sleep 2
    push dx
    mov ah, 86h
    mov cx, %1
    mov dx, %2
    int 15h
    pop dx
%endmacro

我正在使用NASM汇编程序 .

这不是How can I create a sleep function in 16bit MASM Assembly x86?的副本,因为这个是用于Windows或DOS之外的裸机装配 .

1 回答

  • 3

    可编程间隔定时器是可行的方法 . 如果您只处理较新的系统,请查看HPET . 对于PIT,有几点需要了解 . 首先,要设置它,您需要使用端口0x43作为控制/命令端口来配置通道零计时器 . 我们要发送的字节是位映射字段:

    7  |  6  |  5  |  4  |  3  |  2  |  1  |  0 
    +----------------------------------------------+
    | Channel  |  RW Mode  |   Channel Mode  | BCD?|
    +----------------------------------------------+
    

    通道将被清除以选择通道零 .

    RW模型可以是1-LSB,2-MSB或3-LSB,然后是MSB . 我们希望两个位都打开(位模式为3,011),因为我们需要发送一个16位值(LSB然后是MSB)

    对于通道模式,我们需要方波 . 这是3(011)的位模式

    我们想为计数器发送一个16位除数,而不是BCD值,因此清除最低位 .

    这给了我们:000110110二进制或0x36十六进制 . 现在我们设置它:

    mov al, 0x36    ; 0x36 from our work above
    out 0x43, al    ; Send that byte to the control port
    
    mov ax, 11931   ; The timer ticks at  1193182. 100hz would be 1193182/11931
    out 0x40, al    ; send low byte
    out 0x40, ah    ; send high byte
    

    此时,您需要决定是否要对中断做出反应(IRQ 0)或只是想读取定时器 . 我将指向OSDev上的this excellent reference,它在示例代码上都有很好的写作 .

相关问题