【发布时间】:2017-02-08 01:48:16
【问题描述】:
我正在尝试编写一个使用INT 08h 停止执行n*55ms 的过程,但到目前为止我还没有找到任何有用的东西。
如何使用此中断?
【问题讨论】:
标签: assembly x86 interrupt x86-16
我正在尝试编写一个使用INT 08h 停止执行n*55ms 的过程,但到目前为止我还没有找到任何有用的东西。
如何使用此中断?
【问题讨论】:
标签: assembly x86 interrupt x86-16
基本想法是获取此中断的现有 IVT 条目(0x000:0x0020 处的四个字节)并将它们保存在某处,然后将这四个字节替换为中断处理程序的段和偏移量。您的中断处理程序每秒将被调用 18.2 次,并且您的中断处理程序应该“跳远”到旧的中断处理程序(使用您最初保存的四个字节)。
完成后(例如,如果/当您的程序退出到 DOS 或其他情况时)您将在 0x000:0x0020 处恢复原始的四个字节。
对于其中的n*55 ms 部分,您需要将一个全局变量设置为n+1,并且您的中断处理程序会递减该全局变量。当全局变量减为 0 时,您知道经过的时间量介于 n*55 ms 和 (n+1)*55 ms 之间。
请注意,这种缺乏精度是由于安装中断处理程序和第一个 IRQ 发生之间的时间变化(例如,计时器的 IRQ 可能在您安装中断处理程序后立即发生,或者在安装后最多 55 毫秒发生你的中断处理程序)。如果您等待第一个计时器 IRQ 发生,然后让您的代码为 n*55 ms 运行,您可以在“确切地”n*55 ms 之后停止您的代码。
另外,请确保您的中断处理程序保存它使用的所有寄存器(包括段寄存器)并在执行“jmp far”之前恢复它们。可以在不使用任何寄存器的情况下减少一个值并与零进行比较(因此可以避免保存和恢复您使用的寄存器,因为您没有使用任何寄存器)。例如(NASM):
interruptHandler:
sub word [cs:globalCounter],1
je .counterIsZero
jmp far [cs:oldInterruptHandler]
.counterIsZero:
【讨论】:
cli/sti 以及安装自己的 IVT 值是值得的。只是为了确保听起来不像将两个值写入 RAM 那样简单(因为它不是)。
您可以通过INT 1Ah, AH=0服务或直接在内存地址0040h:006C读取int 08h的输出。
; cx = "n" to wait "at least n*55ms", will modify cx
DelayProcedure:
push ax
push ds
mov ax,40h
mov ds,ax
; make sure that the delay will take "at least n*55ms"
; by waiting for first incomplete (<= 55ms) tick
mov ax,[6Ch]
.waitFirstForOneTickHappen:
nop
cmp [6Ch],ax
je .waitFirstForOneTickHappen
; remove the loop above to get "at most n*55ms" behaviour
; (which may be more practical for some situations, like animation syncing)
; wait "at most n*55ms" ticks
.waitNticks:
mov ax,[6Ch]
.waitForTick:
nop
cmp [6Ch],ax
je .waitForTick
loop .waitNticks
; restore ds, ax and return
pop ds
pop ax
ret
(我没有调试它,只是从头开始写的,所以不能保证它会起作用)
另外,将几个 nop 放入这些循环中可能会很好(从功率使用的角度来看)......比如 4-8 个(而不是 cmp+je 很可能与 nop on 一样低功耗)现代 x86)。
【讨论】:
inc cx而不是在开始时编写额外的循环吗?
您可以参考Ralph Browns Interrupt List获取INT 08h的相关信息。
根据您的使用场景,您可以选择两种选择:
无论您的使用场景是什么,这都很好地解释了它。
因此,INT 08h 的发生有两种可能性 - 硬件或软件中断。目前尚不清楚您的问题所指的是哪一个。
【讨论】: