我怎样才能让操作系统在关闭前等待一秒钟(nasm)

Min*_*ICT 0 x86 operating-system kernel nasm x86-16

powerCommand:
    mov si, powerOFF
    call printString
    ;sleep command here
    mov ax, 0x1000
    mov ax, ss
    mov sp, 0xf000
    mov ax, 0x5307
    mov bx, 0x0001
    mov cx, 0x0003
    int 0x15
    ret
Run Code Online (Sandbox Code Playgroud)

我希望程序等待 1 秒然后继续关闭。目前,它在关闭消息后立即关闭。我在 nasm 制作的自定义操作系统上运行它。

ecm*_*ecm 5

假设您的程序由 ROM-BIOS(不是 EFI)加载,并且您在(真实/虚拟)86 模式下运行,并且您启用了中断 ( sti),并且您没有重新配置 PIC 和 PIT,并且也没有更改中断 8(默认 IRQ 0)处理程序,然后您可以使用 0_046Ch(相当于 40h:6Ch)处的双字,该双字保存自午夜以来的计时器滴答声,并且每秒递增约 18.2 次(约 18.2 Hz)由 ROM-BIOS 的中断 8 处理程序处理。

在我的程序中,我通常只检查计数器的较低字更改的频率,这通常足够准确,并且不需要任何特殊的午夜翻转处理。

(简单的方法是获取当前的滴答数并添加您想要等待的滴答数,然后在循环时检查滴答双字是否高于或等于计算值。但是,这需要午夜翻转处理才能正确工作所有情况。)

这是我的项目中一些计时器处理的源代码部分:https://hg.ulukai.org/ecm/ldebug/file/82570f7094b8/source/debug.asm#l1367

.timer:
    push ax
    push dx
    push cx
    push es

    mov dx, 40h
    mov es, dx

    mov cx, word [getline_timer_count]
    mov dx, word [getline_timer_last]

    cmp dx, word [es:6Ch]
    je .timer_next
    mov dx, word [es:6Ch]
    inc cx
    mov al, 18
    mul byte [serial_keep_timeout]
    test ax, ax
    jz .timer_next
    cmp cx, ax
    jb .timer_next

    pop es
    mov dx, msg.serial_no_keep_timer
    jmp .no_keep

.timer_next:
    mov word [getline_timer_count], cx
    mov word [getline_timer_last], dx
    pop es
    pop cx
    pop dx
    pop ax
    retn
Run Code Online (Sandbox Code Playgroud)

这是该计时器的设置:

    xor ax, ax
    mov word [getline_timer_count], ax
    mov word [getline_timer_last], ax
    mov word [getline_timer_func], .timer

    call getline00
Run Code Online (Sandbox Code Playgroud)

word [getline_timer_func]如果输入是从串行端口完成的(每当我们使用此计时器时都会出现这种情况),则getline00 在等待输入时会重复调用函数指针。这是在https://hg.ulukai.org/ecm/ldebug/file/82570f7094b8/source/lineio.asm#l814

    call near word [getline_timer_func]
Run Code Online (Sandbox Code Playgroud)

下面的行通过指向一个空函数(这只是一个retn)来禁用计时器:

    mov word [getline_timer_func], dmycmd
Run Code Online (Sandbox Code Playgroud)

将它们放在一起,这是您的睡眠处理程序:


%assign SLEEP_SECONDS 1

sleep:
        xor cx, cx      ; initialise counter to zero
        xor dx, dx      ; initialise "prior value" to zero
                        ; (any value will do, at worst it is a match to the
                        ;  tick low word and we will wait one tick longer)

        mov ax, 40h
        mov es, ax      ; => ROM-BIOS data area
.loop:
        cmp word [es:6Ch], dx
                        ; still same ?
        je .next        ; yes, just wait for next -->
        mov dx, word [es:6Ch]
                        ; update our last-seen low tick word value
        inc cx          ; increment our counter
        cmp cx, SLEEP_SECONDS * 18
                        ; as many ticks elapsed as we want ?
        jae .end        ; yes, end the loop -->

                        ; (fall through to .next)
.next:
        sti             ; insure interrupts are enabled for the hlt
        hlt             ; idle the machine while waiting for IRQs

        jmp .loop       ; continue the loop -->


.end:
Run Code Online (Sandbox Code Playgroud)

我的程序源代码的更改:

  • 睡眠滴答超时是在汇编时从预处理器定义的秒数计算的,而不是在运行时使用变量。
  • 计数器和最后看到的值不会存储在迭代之间的变量中,而是始终保存在cx和中dx
  • 不使用函数指针框架,因为指针在睡眠处理期间将是常量。
  • 我们不是从计时器函数返回直到再次调用它,而是跳回.loop本地标签。这也意味着我们不必使用push和保留寄存器内容pop
  • 我们没有检查按键(在我的程序中最终也会使机器闲置),而是在这里处于一个紧密的循环中。这hlt使得机器可以空转。

  • 我赞成,但可能值得一提的是,如果您使用的是 AT (286) 或更高版本,则 BIOS 功能 [Int 15h/AH=86h](http://www.ctyme.com/intr/ rb-1525.htm) 可以用作等待。 (4认同)