diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4d8d2f1..50339cf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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" diff --git a/test/kernel.cpp b/test/nboxkrnl/kernel.cpp similarity index 88% rename from test/kernel.cpp rename to test/nboxkrnl/kernel.cpp index 0005978..7a09819 100644 --- a/test/kernel.cpp +++ b/test/nboxkrnl/kernel.cpp @@ -6,8 +6,8 @@ */ #include -#include "lib86cpu.h" -#include "run.h" +#include "pic.hpp" +#include "..\run.h" #define CONTIGUOUS_MEMORY_BASE 0x80000000 @@ -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; } @@ -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); diff --git a/test/nboxkrnl/pic.cpp b/test/nboxkrnl/pic.cpp new file mode 100644 index 0000000..059a874 --- /dev/null +++ b/test/nboxkrnl/pic.cpp @@ -0,0 +1,314 @@ +/* + * pic barebones implementation + * + * ergo720 Copyright (c) 2023 + */ + +#include "pic.hpp" +#include "..\run.h" +#include + + +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(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(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(opaque)[addr & 1].elcr = data; +} + +uint8_t +pic_elcr_read_handler(uint32_t addr, void *opaque) +{ + return static_cast(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; + } +} diff --git a/test/nboxkrnl/pic.hpp b/test/nboxkrnl/pic.hpp new file mode 100644 index 0000000..f294b50 --- /dev/null +++ b/test/nboxkrnl/pic.hpp @@ -0,0 +1,32 @@ +/* + * pic declarations + * + * ergo720 Copyright (c) 2023 + */ + +#pragma once + +#include + + +struct pic_t { + uint8_t imr, irr, isr, elcr; + uint8_t read_isr, in_init; + uint8_t vector_offset; + uint8_t priority_base; + uint8_t highest_priority_irq_to_send; + uint8_t pin_state; + unsigned icw_idx; +}; + +// 0: master, 1: slave +inline pic_t pic[2]; + +uint8_t pic_read_handler(uint32_t port, void *opaque); +void pic_write_handler(uint32_t addr, const uint8_t data, void *opaque); +uint8_t pic_elcr_read_handler(uint32_t addr, void *opaque); +void pic_elcr_write_handler(uint32_t addr, const uint8_t data, void *opaque); +uint16_t pic_get_interrupt(); +void pic_raise_irq(uint8_t a); +void pic_lower_irq(uint8_t a); +void pic_reset();