我如何才能让操作系统在关机前等待一秒钟(nasm)

时间:2019-08-21 16:44:54

标签: 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

我希望程序等待1秒钟,然后继续关闭。此刻,它会在关闭消息后立即关闭。我正在用nasm定制的操作系统运行它。

1 个答案:

答案 0 :(得分:4)

假设您的程序是通过ROM-BIOS(不是EFI)加载的,并且您正在(真实/虚拟)86模式下运行,并且启用了中断(sti),而您没有重新配置PIC和PIT,并且也没有更改中断8(默认IRQ 0)处理程序,那么您可以使用0_046Ch(相当于40h:6Ch)处的dword,它自午夜以来一直保持计时器滴答,并在大约ROM-BIOS的中断8处理程序每​​秒显示18.2次(大约为18.2 Hz)。

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

(幼稚的方法是获取当前的滴答声计数并添加要等待的任意多个滴答声,然后在循环时检查tick dword是否高于或等于计算值。但是,这需要午夜翻转处理在任何情况下都能正常工作。)

这是我的项目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

这是该计时器的设置:

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

    call getline00

getline00在等待输入的过程中反复调用word [getline_timer_func]中的函数指针(如果输入是通过串行端口完成的(每次使用此计时器时都是这种情况))。那是https://hg.ulukai.org/ecm/ldebug/file/82570f7094b8/source/lineio.asm#l814

    call near word [getline_timer_func]

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

    mov word [getline_timer_func], dmycmd

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


%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:

程序源代码中的更改:

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