Skip to content

Commit

Permalink
Added minimal pic implementation to the kernel test
Browse files Browse the repository at this point in the history
  • Loading branch information
ergo720 committed Oct 4, 2023
1 parent b016c03 commit d197025
Show file tree
Hide file tree
Showing 4 changed files with 369 additions and 4 deletions.
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ set(TEST_RUN86_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})

file (GLOB HEADERS
"${TEST_RUN86_ROOT_DIR}/run.h"
"${TEST_RUN86_ROOT_DIR}/nboxkrnl/pic.hpp"
)

file (GLOB SOURCES
"${TEST_RUN86_ROOT_DIR}/debug.cpp"
"${TEST_RUN86_ROOT_DIR}/hook.cpp"
"${TEST_RUN86_ROOT_DIR}/kernel.cpp"
"${TEST_RUN86_ROOT_DIR}/nboxkrnl/kernel.cpp"
"${TEST_RUN86_ROOT_DIR}/nboxkrnl/pic.cpp"
"${TEST_RUN86_ROOT_DIR}/run.cpp"
"${TEST_RUN86_ROOT_DIR}/test386.cpp"
"${TEST_RUN86_ROOT_DIR}/test80186.cpp"
Expand Down
23 changes: 20 additions & 3 deletions test/kernel.cpp → test/nboxkrnl/kernel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/

#include <fstream>
#include "lib86cpu.h"
#include "run.h"
#include "pic.hpp"
#include "..\run.h"

#define CONTIGUOUS_MEMORY_BASE 0x80000000

Expand Down Expand Up @@ -218,7 +218,7 @@ gen_nboxkrnl_test(const std::string &executable)
}

// Init lib86cpu
if (!LC86_SUCCESS(cpu_new(ramsize, cpu, nullptr, "nboxkrnl"))) {
if (!LC86_SUCCESS(cpu_new(ramsize, cpu, pic_get_interrupt, "nboxkrnl"))) {
std::printf("Failed to create cpu!\n");
return false;
}
Expand All @@ -238,6 +238,23 @@ gen_nboxkrnl_test(const std::string &executable)
return false;
}

if (!LC86_SUCCESS(mem_init_region_io(cpu, 0x20, 2, true, io_handlers_t{ .fnr8 = pic_read_handler, .fnw8 = pic_write_handler }, &pic[0]))) {
std::printf("Failed to initialize master pic i/o ports!\n");
return false;
}

if (!LC86_SUCCESS(mem_init_region_io(cpu, 0xA0, 2, true, io_handlers_t{ .fnr8 = pic_read_handler, .fnw8 = pic_write_handler }, &pic[1]))) {
std::printf("Failed to initialize slave pic i/o ports!\n");
return false;
}

if (!LC86_SUCCESS(mem_init_region_io(cpu, 0x4D0, 2, true, io_handlers_t{ .fnr8 = pic_elcr_read_handler, .fnw8 = pic_elcr_write_handler }, pic))) {
std::printf("Failed to initialize elcr i/o ports!\n");
return false;
}

pic_reset();

// Load kernel exe into ram
uint8_t *ram = get_ram_ptr(cpu);
std::memcpy(&ram[peHeader->OptionalHeader.ImageBase - CONTIGUOUS_MEMORY_BASE], dosHeader, peHeader->OptionalHeader.SizeOfHeaders);
Expand Down
314 changes: 314 additions & 0 deletions test/nboxkrnl/pic.cpp
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;
}
}
Loading

0 comments on commit d197025

Please sign in to comment.