【问题标题】:Timer interrupt in x86 Assemblyx86 程序集中的定时器中断
【发布时间】:2017-02-08 01:48:16
【问题描述】:

我正在尝试编写一个使用INT 08h 停止执行n*55ms 的过程,但到目前为止我还没有找到任何有用的东西。
如何使用此中断?

【问题讨论】:

    标签: assembly x86 interrupt x86-16


    【解决方案1】:

    基本想法是获取此中断的现有 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 那样简单(因为它不是)。
    【解决方案2】:

    您可以通过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而不是在开始时编写额外的循环吗?
    • @Tommylee2k 在技术上不是(cx=0 的行为不同),实际上是的。实际上,实际上我认为“0到n * 55ms”延迟比“至少n * 55ms”更需要,所以对于我自己的代码,我会完全删除第一个循环......但话又说回来,在我所有的DOS中我宁愿同步到 Vsync 的应用程序,18.2/s 的代码对我来说用处不大,因为我通常处理图形,在 60Hz 显示下 55ms 太长了。
    • 从逻辑上讲,除了一个被告知“睡眠”0 x 55ms 到不睡眠 3.6 秒的函数(在这种情况下,我会使用 0xffff),所以这个更改甚至是一个修复; ) 但是,wait() 是我从不使用的函数;需要睡眠的 Imo 程序是糟糕的设计(这不是冒犯,同步不是在睡眠:-P)
    • @Tommylee2k 好的,听起来一切都有效,我可以同意(然后,关于 SO 的许多问题再次带有“糟糕的设计”的味道,但没有必要争论,有时他们可能有正当理由) .至少我对源代码进行了一些更新,以明确第一部分是可选的。 :)
    • 原问题涉及“在n*55ms后停止执行其他代码”。像这样在轮询循环中旋转不能用于“在 n*55ms 后停止执行其他代码” - 一旦开始等待,您就会停止执行其他代码。
    【解决方案3】:

    您可以参考Ralph Browns Interrupt List获取INT 08h的相关信息。

    根据您的使用场景,您可以选择两种选择:

    无论您的使用场景是什么,这都很好地解释了它。

    因此,INT 08h 的发生有两种可能性 - 硬件或软件中断。目前尚不清楚您的问题所指的是哪一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 1970-01-01
      • 2016-03-15
      • 2011-05-08
      • 2015-09-20
      • 1970-01-01
      • 2012-03-05
      相关资源
      最近更新 更多