Search code examples
assemblyx86qemubootloaderbios

Why BIOS interrupt 8 (timer) not working on qemu-system-x86_64


I suppose BIOS interrupt 8 (timer) should come 18.2 times per second, but it doesn't on qemu. See the following example:

$ cat c.asm

init:
    .segments:
        mov ax, 0x07C0
        mov ds, ax
        mov ax, 0
        mov fs, ax
    .interrupt:
        mov [fs:0x08*4], word timer
        mov [fs:0x08*4+2], ds

main:
    hlt
    jmp main

timer:
    mov ah, 0x0e
    mov al, 0x41
    int 0x10
    iret

times 510-($-$$) db 0
dw 0xaa55
$ nasm -f bin c.asm -o c.bin && qemu-system-x86_64 c.bin

Qemu window appears, and only one 'A' displayed, not continuously.

What's wrong with my code, if I hope interrupt 8 comings again and again.

I use nasm 1.14.02, qemu 4.2.1 and ubuntu 20.04.


Solution

  • The crucial change to make it display 'A' repeatedly is sending an End Of Interrupt signal to the PIC on port 20h. If you use interrupt 1Ch or chain to another interrupt 08h handler this is not needed in your code. If you replace the interrupt 08h handler entirely though, it is. The PIC won't send another IRQ #0 until the prior one gets an EOI. Because of this I was able to reproduce your problem.

    The other changes I did were to insure that the interrupt flag is set before entering the main loop (with a sti instruction), and preserving all registers across the interrupt 08h handler (this is optional if your code is the only thing running on the machine).

    init:
        .segments:
            mov ax, 0x07C0
            mov ds, ax
            mov ax, 0
            mov fs, ax
        .interrupt:
            mov [fs:0x08*4], word timer
            mov [fs:0x08*4+2], ds
    
            sti
    main:
        hlt
        jmp main
    
    timer:
            push ax
            push bx
            push bp
        mov ah, 0x0e
        mov al, 0x41
        int 0x10
            mov al, 20h
            out 20h, al
            pop bp
            pop bx
            pop ax
        iret
    
    times 510-($-$$) db 0
    dw 0xaa55
    

    Run like so:

    $ nasm test.asm
    $ timeout 10 qemu-system-x86_64 test -curses