; pcboot - bootable PC demo/game kernel ; Copyright (C) 2018-2023 John Tsiombikas ; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY, without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . section .data align 4 dw 0 idtr_desc: lim: dw 0 addr: dd 0 section .text ; void set_idt(uint32_t addr, uint16_t limit) global set_idt set_idt: mov eax, [esp + 4] mov [addr], eax mov ax, [esp + 8] mov [lim], ax lidt [idtr_desc] ret ; int get_intr_flag() global get_intr_flag get_intr_flag: pushf pop eax ; bit 9 of eflags is IF shr eax, 9 and eax, 1 ret ; void set_intr_flag(int onoff) global set_intr_flag set_intr_flag: cmp dword [esp + 4], 0 jz .off sti ret .off: cli ret ; interrupt entry with error code macro ; this macro generates an interrupt entry point for the ; exceptions which include error codes in the stack frame %macro ientry_err 2 global intr_entry_%2 intr_entry_%2: push dword %1 jmp intr_entry_common %endmacro ; interrupt entry without error code macro ; this macro generates an interrupt entry point for the interrupts ; and exceptions which do not include error codes in the stack frame ; it pushes a dummy error code (0), to make the stack frame identical %macro ientry_noerr 2 global intr_entry_%2 intr_entry_%2: push dword 0 push dword %1 jmp intr_entry_common %endmacro ; common code used by all entry points. calls dispatch_intr() ; defined in intr.c extern dispatch_intr intr_entry_common: ; save general purpose registers pusha call dispatch_intr intr_ret_local: ; restore general purpose registers popa ; remove error code and intr num from stack add esp, 8 iret ; special case for the timer interrupt, to avoid all the overhead of ; going through the C interrupt dispatcher 250 times each second ; extern nticks ; global intr_entry_fast_timer ;intr_entry_fast_timer: ; inc dword [nticks] ; ; signal end of interrupt ; push eax ; mov al, 20h ; out 20h, al ; pop eax ; iret ; special case for IRQ 7 and IRQ 15, to catch spurious interrupts PIC1_CMD equ 0x20 PIC2_CMD equ 0xa0 OCW2_EOI equ 0x20 OCW3_ISR equ 0x0b global irq7_entry_check_spurious irq7_entry_check_spurious: push eax mov al, OCW3_ISR out PIC1_CMD, al in al, PIC1_CMD and al, 80h pop eax jnz intr_entry_irq7 iret global irq15_entry_check_spurious irq15_entry_check_spurious: push eax mov al, OCW3_ISR out PIC2_CMD, al in al, PIC2_CMD and al, 80h jnz .legit ; it was spurious, send EOI to master PIC and iret mov al, OCW2_EOI out PIC1_CMD, al pop eax iret .legit: pop eax jmp intr_entry_irq15 ; XXX not necessary for now, just leaving it in in case it's useful ; down the road. ; ; intr_ret is called by context_switch to return from the kernel ; to userspace. The argument is a properly formed intr_frame ; structure with the saved context of the new task. ; ; First thing to do is remove the return address pointing back ; to context_switch, which then leaves us with a proper interrupt ; stack frame, so we can jump right in the middle of the regular ; interrupt return code above. global intr_ret intr_ret: add esp, $4 jmp intr_ret_local ; by including interrupts.h with ASM defined, the macros above ; are expanded to generate all required interrupt entry points %define INTR_ENTRY_EC(n, name) ientry_err n, name %define INTR_ENTRY_NOEC(n, name) ientry_noerr n, name %include 'intrtab.h' ; vi:set ts=8 sts=8 sw=8 ft=nasm: