从 BIOS (8086) 读取刻度需要多少?

How much of this is needed to read ticks from the BIOS (8086)?

提问人:Benjamin Penney 提问时间:11/16/2023 最后编辑:Peter CordesBenjamin Penney 更新时间:11/20/2023 访问量:88

问:

我有以下函数(带汇编的 C)可以读取 BIOS 滴答声:

static clock_t clock(void);
#pragma aux clock = \
"pushf" \
"push ds" \
"cli" \
"mov ax, 0x0040" \
"mov ds, ax" \
"mov bx, 0x006C" \
"mov ax, [bx]" \
"sti" \
"pop ds" \
"popf" \
parm [ ax ] \
modify [ ax bx ];

这可行,但其中有多少是必要的?有人可以帮我编写一下高尔夫代码吗?我正在使用 8086(微型内存模型),我不确定我是否需要和/或和读取该值之前。如果可以减少操作次数,我也将不胜感激。我对远指针一无所知,并把它捏在一起(第一次)。pushfpopfclisti

C 程序集 x86-16 DOS

评论

4赞 Jester 11/16/2023
显然,您不需要通过寄存器进行寻址,您可以这样做.如果编译器支持段寄存器的子句,则不需要 save(或 )。由于您只有单个内存读取,因此禁用中断不会做任何有用的事情,因此您可以摆脱整个 /// 业务。此外,由于这只是内存读取,因此您不需要内联 asm,只需执行(使用编译器提供的任何方式创建远指针)。mov ax, [0x6c]modifydsespushfclistipopfreturn *(volatile clock_t*)MAKE_FAR(0x40, 0x6c);
0赞 Michael Petch 11/16/2023
我从语法中收集到这是 Watcom/Openwatcom C/C++
5赞 Jester 11/16/2023
因为否则,如果您重复读取时钟(您可能会这样做),编译器可能会决定优化额外的访问,认为内存中的值没有改变。它还可能会根据您可能想要计时的代码重新排序时钟读数。
1赞 ecm 11/17/2023
@Jester 据推测,禁用中断应该是为了防止在两次内存访问碰巧读取整个 32 位定时器滴答值时发生撕裂。在兼容 8086 的真实/虚拟 86 模式下,可能还有另一种选择,即使用(或 ,并使用任何 GPR 目的地)。这可能不是原子性的,但可能足以避免硬件中断 (IRQ) 干扰。但这不适用于保护模式代码,或者应该能够在保护模式下工作的双峰代码(就像我的大多数调试器一样)。clilds ax, [6Ch]les
1赞 Peter Cordes 11/20/2023
@ecm:在需要 386 保护模式的代码中,您只需将 4 字节加载到 4 字节整数寄存器中即可。作为一条指令,这就是原子wrt。中断。(例如,与 m68k 不同,x86 指令在中断之前完全发生或根本不发生。如果你的意思是 286 保护模式,那么是的,les/lds 将是一个问题。如果您有 x87,/ 可以作为单个指令执行 4 字节加载,但如果 x87 访问是异步的,则 IDK。CPU 中断,它可能比真正的 286 + 硬件 FPU 慢得多,更不用说软 FP 仿真了。fild dwordfistp dwordclisti
1赞 Peter Cordes 11/20/2023
汇编指令运行时中断它 re:英特尔文档支持单个指令是原子 wrt 这一事实。中断。(因此,原子 wrt. 单处理器机器上的其他线程,以及 wrt. 任何中断处理程序。 在这种情况下,仅对与设备交互有用;UniProcess 上的线程是 CMPXCHG带锁的用例lock

答:

2赞 fuz 11/20/2023 #1

您只需要从地址中读取一个单词即可获得滴答计数器的低 16 位。执行此操作的一种简单方法是使用远易失性指针:0000:046c

#include <i86.h>

#define ticks (*(volatile unsigned short __far *)MK_FP(0x0000, 0x046c))

也可以使用其他等效地址。0040:006c

然后,您可以按如下方式访问即时报价计数器:

int now;

now = ticks;

请注意,这只会给您低 2 个字节。如果你想要所有字节,你必须禁用中断来对整个计数器进行原子读取(或循环直到收敛)。

您也可以使用预定义的功能或手册中的指示来实现此目的。clock_bios_timeofday