Real mode Interrupt handling routine not working a

2019-02-25 05:51发布

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 from there to test it and it works properly.

I wanted to remap interrupts 0x08->0x0F and 0x70->0x77 to interrupts 0x20->0x2F, so the exception/reserved interrupts are not overlapped. So far, I am only handling a keyboard press and attempting to print it to the screen.
I went over it a bunch of times and for some reason, I just don't know why but nothing happens when I press a key.
The keymap is just an array of the scancodes to their respected ASCII value.

If this is of any help: I tested running a loop and printing a character then HLTing, and once 2 characters were printed, it hanged. I made sure to enable interrupts.
(I'm loading 4 sectors (from sector 2 to sector 5) for the bootloader which is why I'm padding this to make it 2048 bytes in size).

By the way, I don't have to CLI in my interrupt procedures since it's done for me, right? I remember reading this but I am not too sure.

BITS 16
ORG 0x0000

; Setup Segments ;
cli
cld
mov ax, cs
mov ds, ax              ; this program was far-jumped to (0x0090:0x0000) so ds = cs = 0x0090
mov ax, VIDEO_ORIGIN
mov es, ax

; Remap PIC Interrupt Vector Offsets to 0x20 -> 0x35 ;
remapInterrupts:
    ; Send Initialization Command (expecting ICW4)
    mov al, 0x11
    out 0x20, al
    out 0xA0, al

    ; Remap Vector Offsets (ICW2)
    mov al, 0x20        ; Master IRQ lines mapped to 0x20 -> 0x27
    out 0x21, al
    mov al, 0x28        ; Slave IRQ lines mapped to 0x28 -> 0x2F
    out 0xA1, al

    ; Set Cascade Lines between Master and Slave PICs (ICW3)
    mov al, 0x04        ; 00000100 (line 2)
    out 0x21, al
    mov al, 0x02        ; 00000010 (line 2 in binary, cascade identity)
    out 0xA1, al

    ; Set 80x86 Mode (ICW4)
    mov al, 0x01
    out 0x21, al
    out 0xA1, al

    ; Set Masks
    mov al, 0xFD        ; 11111101 (keyboard)
    out 0x21, al
    mov al, 0xFF        ; 11111111
    out 0xA1, al

setInterrupts:
    push ds
    mov ax, 0x0000
    mov ds, ax
    mov [ds:0x84], word interrupt21     ; 0x84 = 0x21 * 4
    mov [ds:0x86], cs
    pop ds
    jmp start

    interrupt20:                ; Programmable Interval Timer
        ; NOT SUPPORTED, place holder
        push ax
        mov al, 0x20
        out 0x20, al
        pop ax
        iret
    interrupt21:                ; Keyboard
        push ax
        push bx
        in al, 0x60
        test al, 0x80           ; high-bit set = keyup = don't print
        jnz .finish
        movzx bx, al
        mov al, [keymap + bx]
        mov ah, 0x07
        stosw

        .finish:
        mov al, 0x20
        out 0x20, al

        pop bx
        pop ax
        iret
    interrupt22:                ; Slave Cascade
    interrupt23:                ; COM2 / COM4
    interrupt24:                ; COM1 / COM3
    interrupt25:                ; LPT2
    interrupt26:                ; Floppy controller
    interrupt27:                ; LPT1
        ; NOT SUPPORTED, place holder
        push ax
        mov al, 0x20
        out 0x20, al
        pop ax
        iret
    interrupt28:                ; RTC
    interrupt29:                ; Unassigned
    interrupt2A:                ; Unassigned
    interrupt2B:                ; Unassigned
    interrupt2C:                ; Mouse Controller
    interrupt2D:                ; Math Coprocessor
    interrupt2E:                ; Hard Disk Controller 1
    interrupt2F:                ; Hard Disk Controller 2
        ; NOT SUPPORTED, place holder
        push ax
        mov al, 0x20
        out 0xA0, al
        out 0x20, al
        pop ax
        iret

start:
    sti
    xor di, di
    jmp $

; --- CONSTANTS --- ;
VIDEO_ORIGIN EQU 0xB800

; --- DATA --- ;
drive db 0
keymap:
    db 00h, 1Bh, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 08h, 09h
    db 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', 00h, 00h
    db 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', "'", '`', 00h, '\'
    db 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 00h, 00h, 00h, ' ', 00h,
    db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h,
    db '-', 00h, 00h, 00h, '+', 00h, 00h, 00h, 00h, 00h

times 2048 - ($ - $$) db 0

1条回答
Explosion°爆炸
2楼-- · 2019-02-25 06:11

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.

查看更多
登录 后发表回答