-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added minimal pic implementation to the kernel test
- Loading branch information
Showing
4 changed files
with
369 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,314 @@ | ||
/* | ||
* pic barebones implementation | ||
* | ||
* ergo720 Copyright (c) 2023 | ||
*/ | ||
|
||
#include "pic.hpp" | ||
#include "..\run.h" | ||
#include <bit> | ||
|
||
|
||
static inline bool | ||
pic_is_master(pic_t *pic1) | ||
{ | ||
return pic1 == &pic[0]; | ||
} | ||
|
||
static uint8_t | ||
pic_get_interrupt(pic_t *pic1) | ||
{ | ||
uint8_t irq = pic1->highest_priority_irq_to_send, irq_mask = 1 << irq; | ||
if ((pic1->irr & irq_mask) == 0) { | ||
// Generate a spurious IRQ7 if the interrupt is no longer pending | ||
return pic1->vector_offset | 7; | ||
} | ||
|
||
// If edge triggered, then clear irr | ||
if ((pic->elcr & irq_mask) == 0) { | ||
pic1->irr &= ~irq_mask; | ||
} | ||
|
||
pic1->isr |= irq_mask; | ||
|
||
if (pic_is_master(pic1) && irq == 2) { | ||
return pic_get_interrupt(&pic[1]); | ||
} | ||
|
||
return pic1->vector_offset + irq; | ||
} | ||
|
||
uint16_t | ||
pic_get_interrupt() | ||
{ | ||
cpu_lower_hw_int_line(cpu); | ||
return pic_get_interrupt(&pic[0]); | ||
} | ||
|
||
static void | ||
pic_update_state(pic_t *pic) | ||
{ | ||
uint8_t unmasked, isr; | ||
|
||
if (!(unmasked = pic->irr & ~pic->imr)) { | ||
// All interrupts masked, nothing to do | ||
return; | ||
} | ||
|
||
// Left rotate IRR and ISR so that the interrupts are located in decreasing priority | ||
unmasked = std::rotl(unmasked, pic->priority_base ^ 7); | ||
isr = std::rotl(pic->isr, pic->priority_base ^ 7); | ||
|
||
for (unsigned i = 0; i < 8; ++i) { | ||
uint8_t mask = 1 << i; | ||
if (isr & mask) { | ||
return; | ||
} | ||
|
||
if (unmasked & (1 << i)) { | ||
pic->highest_priority_irq_to_send = (pic->priority_base + 1 + i) & 7; | ||
|
||
if (pic_is_master(pic)) { | ||
cpu_raise_hw_int_line(cpu); | ||
} | ||
else { | ||
pic_lower_irq(2); | ||
pic_raise_irq(2); | ||
} | ||
|
||
return; | ||
} | ||
} | ||
} | ||
|
||
static void | ||
pic_raise_irq(pic_t *pic, uint8_t irq) | ||
{ | ||
uint8_t mask = 1 << irq; | ||
pic->pin_state |= mask; | ||
|
||
if (pic->elcr & mask) { | ||
// level triggered | ||
pic->irr |= mask; | ||
pic_update_state(pic); | ||
} | ||
else { | ||
// edge triggered | ||
if ((pic->pin_state & mask) == 0) { | ||
pic->irr |= mask; | ||
pic_update_state(pic); | ||
} | ||
} | ||
} | ||
|
||
static void | ||
pic_lower_irq(pic_t *pic, uint8_t irq) | ||
{ | ||
uint8_t mask = 1 << irq; | ||
pic->pin_state &= ~mask; | ||
pic->irr &= ~mask; | ||
|
||
if (!pic_is_master(pic) && !pic->irr) { | ||
pic_lower_irq(2); | ||
} | ||
} | ||
|
||
void | ||
pic_raise_irq(uint8_t a) | ||
{ | ||
pic_raise_irq(&pic[a > 7 ? 1 : 0], a & 7); | ||
} | ||
|
||
void | ||
pic_lower_irq(uint8_t a) | ||
{ | ||
pic_lower_irq(&pic[a > 7 ? 1 : 0], a & 7); | ||
} | ||
|
||
static void | ||
pic_write_ocw(pic_t *pic, unsigned idx, uint8_t value) | ||
{ | ||
switch (idx) | ||
{ | ||
case 1: | ||
pic->imr = value; | ||
pic_update_state(pic); | ||
break; | ||
|
||
case 2: { | ||
uint8_t rotate = value & 0x80, specific = value & 0x40, eoi = value & 0x20, irq = value & 7; | ||
if (eoi) { | ||
if (specific) { | ||
pic->isr &= ~(1 << irq); | ||
if (rotate) { | ||
pic->priority_base = irq; | ||
} | ||
} | ||
else { | ||
// Clear the highest priority irq | ||
uint8_t highest = (pic->priority_base + 1) & 7; | ||
for (unsigned i = 0; i < 8; ++i) { | ||
uint8_t mask = 1 << ((highest + i) & 7); | ||
if (pic->isr & mask) { | ||
pic->isr &= ~mask; | ||
break; | ||
} | ||
} | ||
if (rotate) { | ||
pic->priority_base = irq; | ||
} | ||
} | ||
pic_update_state(pic); | ||
} | ||
else { | ||
if (specific) { | ||
if (rotate) { | ||
pic->priority_base = irq; | ||
} | ||
} | ||
else { | ||
std::printf("Automatic rotation of IRQ priorities is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
} | ||
} | ||
break; | ||
|
||
case 3: { | ||
if (value & 2) { | ||
pic->read_isr = value & 1; | ||
} | ||
else if (value & 0x44) { | ||
std::printf("Unknown feature: %02X\n", value); | ||
cpu_exit(cpu); | ||
} | ||
} | ||
} | ||
} | ||
|
||
static void | ||
pic_write_icw(pic_t *pic, unsigned idx, uint8_t value) | ||
{ | ||
switch (idx) | ||
{ | ||
case 1: | ||
if ((value & 1) == 0) { | ||
std::printf("Configuration with no icw4 is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
else if (value & 2) { | ||
std::printf("Single pic configuration is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
|
||
pic->in_init = 1; | ||
pic->imr = 0; | ||
pic->isr = 0; | ||
pic->irr = 0; | ||
pic->priority_base = 7; | ||
pic->icw_idx = 2; | ||
break; | ||
|
||
case 2: | ||
pic->vector_offset = value & ~7; | ||
pic->icw_idx = 3; | ||
break; | ||
|
||
case 3: | ||
pic->icw_idx = 4; | ||
break; | ||
|
||
case 4: | ||
if ((value & 1) == 0) { | ||
std::printf("MCS-80/85 mode is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
else if (value & 2) { | ||
std::printf("Auto-eoi mode is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
else if (value & 8) { | ||
std::printf("Buffered mode is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
else if (value & 16) { | ||
std::printf("Special fully nested mode is not supported\n"); | ||
cpu_exit(cpu); | ||
} | ||
|
||
pic->in_init = 0; | ||
pic->icw_idx = 5; | ||
break; | ||
|
||
default: | ||
std::printf("Unknown icw specified, idx was %d\n", idx); | ||
cpu_exit(cpu); | ||
} | ||
} | ||
|
||
void | ||
pic_write_handler(uint32_t addr, const uint8_t data, void *opaque) | ||
{ | ||
pic_t *pic = static_cast<pic_t *>(opaque); | ||
if ((addr & 1) == 0) { | ||
switch (data >> 3 & 3) | ||
{ | ||
case 0: | ||
pic_write_ocw(pic, 2, data); | ||
break; | ||
|
||
case 1: | ||
pic_write_ocw(pic, 3, data); | ||
break; | ||
|
||
default: | ||
cpu_lower_hw_int_line(cpu); | ||
pic_write_icw(pic, 1, data); | ||
} | ||
} | ||
else { | ||
if (pic->in_init) { | ||
pic_write_icw(pic, pic->icw_idx, data); | ||
} | ||
else { | ||
pic_write_ocw(pic, 1, data); | ||
} | ||
} | ||
} | ||
|
||
uint8_t | ||
pic_read_handler(uint32_t port, void *opaque) | ||
{ | ||
pic_t *pic = static_cast<pic_t *>(opaque); | ||
if (port & 1) { | ||
return pic->imr; | ||
} | ||
else { | ||
return pic->read_isr ? pic->isr : pic->irr; | ||
} | ||
} | ||
|
||
void | ||
pic_elcr_write_handler(uint32_t addr, const uint8_t data, void *opaque) | ||
{ | ||
static_cast<pic_t *>(opaque)[addr & 1].elcr = data; | ||
} | ||
|
||
uint8_t | ||
pic_elcr_read_handler(uint32_t addr, void *opaque) | ||
{ | ||
return static_cast<pic_t *>(opaque)[addr & 1].elcr; | ||
} | ||
|
||
void | ||
pic_reset() | ||
{ | ||
for (auto &pic1 : pic) { | ||
pic1.vector_offset = 0; | ||
pic1.imr = 0xFF; | ||
pic1.irr = 0; | ||
pic1.isr = 0; | ||
pic1.in_init = 0; | ||
pic1.read_isr = 0; | ||
} | ||
} |
Oops, something went wrong.