The actual interrupt handling mode is not working properly

I managed to load a small kernel into memory through the bootloader, which performs a quick jump to 0x0090:0x0000 . The kernel loads successfully when I print a character from there to check it and it works correctly.

I wanted to reassign interrupts 0x08->0x0F and 0x70->0x77 to interrupts 0x20->0x2F , so interrupts with exception / reserved do not overlap. So far, I only deal with pressing the keyboard and try to print it on the screen.
I looked through this several times, and for some reason I just don't know why, but nothing happens when I press a key.
The key card is just an array of scancodes to their respected ASCII value.

If this helps: I tested the start of the loop and printing the character, then HLT ing, and after 2 characters were printed, it hanged itself. I turned on interrupts. (I load 4 sectors (from sector 2 to sector 5) for the bootloader, so I populate this to make it 2048 bytes in size).

By the way, I don't need a CLI in my interrupt routines, since this is done for me, right? I remember reading this, but I'm not 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 
+3
source share
1 answer

Real-time interrupt routines should be designed as if only the CS register is known (and the interrupt flag is cleared). CS: IP is set via the interrupt vector when we receive a hardware interrupt. CS will be the segment that we wrote in the interrupt vector table. In your case, it was 0x0090, since you did it (with DS = 0x0000) to update the interrupt vector table:

 mov [ds:0x86], cs 

Since we cannot rely on what we need when we call the interrupt handler, 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 that 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 documented the lines I added. But the important things are:

  • We cannot guarantee that the ES will be what we want, so we will need to explicitly point it to the video memory segment.
  • The registers must be restored to the same state that they were in before the interruption. We will change the ES register and the DI register, so we must save them on the stack (and restore them at the end).
  • We cannot rely on the fact that DI is really the expected value, so we need to save its value between interrupt calls so that we can correctly move to the next cell on the screen for recording.
  • The memory operands were rewritten to use the CS register, not the DS register. Performing segment redefinition avoids copying CS to DS and saving / restoring DS in our interrupt handler.
  • We cannot rely on the direction flag we want. Since you use STOSW in the interrupt handler, we want to make sure that it is cleared by the CLD so that it pushes the DI forward.
  • Instead of using movzx we can simply clear the top of the BX register to zero. movzx is only available on 386 processors. You can save this instruction if you are 386+, but if you intend to target 8086/8088/80188/80286, you cannot use it.

Change your initial procedure as follows:

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

Some emulators do not always refresh the screen if you are executing a compressed loop with jmp $ . The best way to do this is with an HLT instruction. When interrupts are on the processor, the processor stops until the next interrupt occurs. When this happens, it will be serviced by interrupt handlers and will eventually fall to the next instruction. In this case, we go back and perform HLT again, waiting for the next interrupt.

Since we added a new variable for tracking the screen cell that we are writing, we need to add videopos to you .data segment:

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

These changes seem to work on Bochs, QEMU, and VirtualBox. If this does not work for you, maybe you will not load the second stage (4 sectors) properly in 0x0090: 0x0000? Since I cannot see your code for the first stage, I cannot say for sure.

+2
source

Source: https://habr.com/ru/post/1013774/


All Articles