141 lines
5.2 KiB
C
141 lines
5.2 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
|
│ │
|
|
│ 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; version 2 of the License. │
|
|
│ │
|
|
│ 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, write to the Free Software │
|
|
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
|
│ 02110-1301 USA │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "ape/lib/pc.h"
|
|
|
|
#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
|
|
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
|
|
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
|
|
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
|
|
#define ICW1_INIT 0x10 /* Initialization - required! */
|
|
|
|
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
|
|
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
|
|
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
|
|
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
|
|
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
|
|
|
|
static inline void io_wait(void) {
|
|
/* Magic technique from Linux, according to:
|
|
* wiki.osdev.org/index.php?title=Inline_Assembly/Examples&oldid=23541
|
|
*/
|
|
outb(0x80, 0);
|
|
}
|
|
|
|
void PIC_sendEOI(unsigned char irq) {
|
|
if (irq >= 8) outb(PIC2_CMD, PIC_EOI);
|
|
outb(PIC1_CMD, PIC_EOI);
|
|
}
|
|
|
|
bool AreInterruptsEnabled() {
|
|
return (eflags() & kInterruptFlag) == kInterruptFlag;
|
|
}
|
|
|
|
nodiscard forceinline unsigned long irqdisable(void) {
|
|
unsigned long eflags;
|
|
asm("pushf\n\t"
|
|
"cli\n\t"
|
|
"pop\t%0"
|
|
: "=r"(eflags)
|
|
: /* no inputs */
|
|
: "cc");
|
|
return eflags;
|
|
}
|
|
|
|
forceinline void irqrestore(unsigned long eflags) {
|
|
asm volatile(
|
|
"push\t%0\n\t"
|
|
"popf"
|
|
: /* no outputs */
|
|
: "rm"(eflags)
|
|
: "cc");
|
|
}
|
|
|
|
/**
|
|
* @param offset1 is vector offset for master PIC
|
|
* vectors on the master become offset1..offset1+7
|
|
* @param offset2 is same for slave PIC: offset2..offset2+7
|
|
**/
|
|
void PIC_remap(int offset1, int offset2) {
|
|
unsigned char a1, a2;
|
|
a1 = inb(PIC1_DATA); // save masks
|
|
a2 = inb(PIC2_DATA);
|
|
outb(PIC1_CMD,
|
|
ICW1_INIT |
|
|
ICW1_ICW4); // starts the initialization sequence (in cascade mode)
|
|
io_wait();
|
|
outb(PIC2_CMD, ICW1_INIT | ICW1_ICW4);
|
|
io_wait();
|
|
outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset
|
|
io_wait();
|
|
outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset
|
|
io_wait();
|
|
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at
|
|
// IRQ2 (0000 0100)
|
|
io_wait();
|
|
outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010)
|
|
io_wait();
|
|
outb(PIC1_DATA, ICW4_8086);
|
|
io_wait();
|
|
outb(PIC2_DATA, ICW4_8086);
|
|
io_wait();
|
|
outb(PIC1_DATA, a1); // restore saved masks.
|
|
outb(PIC2_DATA, a2);
|
|
}
|
|
|
|
void IRQ_set_mask(unsigned char IRQline) {
|
|
uint16_t port;
|
|
uint8_t value;
|
|
if (IRQline < 8) {
|
|
port = PIC1_DATA;
|
|
} else {
|
|
port = PIC2_DATA;
|
|
IRQline -= 8;
|
|
}
|
|
value = inb(port) | (1 << IRQline);
|
|
outb(port, value);
|
|
}
|
|
|
|
void IRQ_clear_mask(unsigned char IRQline) {
|
|
uint16_t port;
|
|
uint8_t value;
|
|
if (IRQline < 8) {
|
|
port = PIC1_DATA;
|
|
} else {
|
|
port = PIC2_DATA;
|
|
IRQline -= 8;
|
|
}
|
|
value = inb(port) & ~(1 << IRQline);
|
|
outb(port, value);
|
|
}
|
|
|
|
static uint16_t __pic_get_irq_reg(int ocw3) {
|
|
/* OCW3 to PIC CMD to get the register values. PIC2 is chained, and
|
|
* represents IRQs 8-15. PIC1 is IRQs 0-7, with 2 being the chain */
|
|
outb(PIC1_CMD, ocw3);
|
|
outb(PIC2_CMD, ocw3);
|
|
return (inb(PIC2_CMD) << 8) | inb(PIC1_CMD);
|
|
}
|
|
|
|
/* Returns the combined value of the cascaded PICs irq request register */
|
|
uint16_t pic_get_irr(void) { return __pic_get_irq_reg(PIC_READ_IRR); }
|
|
|
|
/* Returns the combined value of the cascaded PICs in-service register */
|
|
uint16_t pic_get_isr(void) { return __pic_get_irq_reg(PIC_READ_ISR); }
|