如何在裸机 16 位 x86 程序集中休眠?

How to sleep in bare-metal 16 bit x86 assembly?

我想停止执行大约 0.1 秒,而不考虑 CPU 的时钟速度。代码应 运行 直接来自引导设备,因此不应使用 DOS 中断。

我目前正在使用 int 15h,但这似乎与我用 PIT 的通道 2 调制的蜂鸣音冲突。我听说过频道 0,但我不知道如何设置它。

准确性并不那么重要,但是,它应该 运行 在旧计算机和现代计算机上以相同的速度运行。所以只是循环指令不是一个选项。

蜂鸣声和睡眠只是一堆用于改变频率和打开和关闭扬声器的宏。如果我在 beepoff.

之前调用 sleep,哔哔声似乎不会停止

蜂鸣音宏如下:

%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 之外的裸机组装。

可编程间隔定时器是最佳选择。如果您只处理较新的系统,请查看 HPET。对于 PIT,有几件事需要了解。首先,要设置它,您需要使用端口 0x43 作为 control/command 端口来配置通道零计时器。我们要发送的字节有一个位映射字段:

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

频道将被清除为 select 频道零。

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,它在这两个方面都有精彩的文章和示例代码。