Real mode Interrupt handling routine not working as expected

后端 未结 1 1954
时光说笑
时光说笑 2021-01-21 03:21

I managed to load a small kernel into memory via a bootloader that performs a far jump to 0x0090:0x0000. The kernel is loaded successfully as I print a character fr

相关标签:
1条回答
  • 2021-01-21 03:38

    Real mode interrupt routines have to be developed as if nothing but the CS register is known (and the interrupt flag is cleared). CS:IP is set via the interrupt vector when we get a hardware interrupt. CS will be the segment we wrote to the interrupt vector table. In your case it was 0x0090 since you did this (with DS=0x0000) to update the interrupt vector table:

    mov [ds:0x86], cs
    

    Since we can't rely on DS being what we want when our interrupt handler is called we can either push DS onto the stack, copy CS to DS and access our memory variables via DS, and then restore DS. Alternatively we can modify our memory operands so they explicitly use the CS register. We could modify the interrupt handler to look like this:

    interrupt21:                ; Keyboard
        push ax
        push bx
        push di                 ; Save DI
        push es                 ; Save ES
    
        mov  ax, VIDEO_ORIGIN
        mov  es, ax             ; Set ES to video memory segment
        mov  di, [cs:videopos]  ; Get last videopos into DI
        in al, 0x60
        test al, 0x80           ; high-bit set = keyup = don't print
        jnz .finish
        xor bh, bh              ; set high byte of BX to zero
        mov bl, al              ; low byte of BX is scancode
        mov al, [cs:keymap + bx]; Reference keymap via CS segment (not DS)
        mov ah, 0x07
        cld                     ; Set the direction flag forward
        stosw
        mov [cs:videopos], di   ; Save current video position
    
    .finish:
        mov al, 0x20
        out 0x20, al
    
        pop es                 ; Restore ES
        pop di                 ; Restore DI
        pop bx
        pop ax
        iret
    

    I've documented the lines I added. But important things are:

    • We can't guarantee that ES will be what we want, so we'll need to set it explicitly to the video memory segment.
    • Registers have to be restored to the same state they were before the interrupt. We will be modifying the ES register and the DI register so we should save them on the stack (and restore them at the end).
    • We can't rely on DI actually being the value we expect, so we have to save its value between interrupt calls so that we can properly advance to the next cell on the screen for writing.
    • The memory operands have been rewritten to use the CS register rather than the DS register. Doing a segment override avoids having to copy CS to DS and save/restore DS in our interrupt handler.
    • We can't rely on the direction flag being what we want. Since you use STOSW in the interrupt handler we want to make sure it is cleared with CLD so that it advances DI forward.
    • Rather than use movzx we can simply clear the upper part of the BX register to zero. movzx is only available on 386 processors. You can keep that instruction if you are on 386+, but if you intend to target 8086/8088/80188/80286 then you can't use it.

    Modify your start routine to be:

    start:
        mov word [videopos], 0x0000 ; Initialize starting video position
        sti
    .progloop:
        hlt
        jmp .progloop
    

    Some emulators don't always do screen refreshes if you do a tight loop with jmp $. A better way to do it is with the HLT instruction. When interrupts are on the CPU will halt the processor until the next interrupt occurs. When one does occur it will be serviced by the interrupt handlers and eventually will fall to the next instruction. In this case we jump back and do the HLT again waiting for the next interrupt.

    Since we added a new variable to keep track of the screen cell we are writing to, we will need to add videopos to you .data segment:

    ; --- DATA --- ;
    drive db 0
    videopos dw 0
    

    These changes do seem to work on Bochs, QEMU, and VirtualBox. If this doesn't work for you then possibly you aren't loading the second stage (4 sectors worth) properly into 0x0090:0x0000? Since I can't see your first stage code I can't really say for certain.

    0 讨论(0)
提交回复
热议问题