如何在裸机 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,它在这两个方面都有精彩的文章和示例代码。
我想停止执行大约 0.1 秒,而不考虑 CPU 的时钟速度。代码应 运行 直接来自引导设备,因此不应使用 DOS 中断。
我目前正在使用 int 15h
,但这似乎与我用 PIT 的通道 2 调制的蜂鸣音冲突。我听说过频道 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 之外的裸机组装。
可编程间隔定时器是最佳选择。如果您只处理较新的系统,请查看 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,它在这两个方面都有精彩的文章和示例代码。