URL
https://opencores.org/ocsvn/s80186/s80186/trunk
Subversion Repositories s80186
[/] [s80186/] [trunk/] [sim/] [PIC.h] - Rev 2
Compare with Previous | Blame | View Log
// Copyright Jamie Iles, 2017 // // This file is part of s80x86. // // s80x86 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. // // s80x86 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 s80x86. If not, see <http://www.gnu.org/licenses/>. #pragma once #include <cassert> #include <iostream> #include <boost/serialization/string.hpp> #include <boost/serialization/version.hpp> #include "CPU.h" class PIC : public IOPorts { private: enum State { STATE_IDLE, STATE_ICW2, STATE_ICW4 }; enum CommandReadMode { STATE_READ_IRR, STATE_READ_ISR }; public: explicit PIC(SimCPU *cpu) : IOPorts(0x0020, 1), cpu(cpu), state(STATE_IDLE), command_read_mode(STATE_READ_IRR), mask(0), isr(0), irr(0) { cpu->set_inta_handler( [this](int irq_num) { this->inta_handler(irq_num); }); } void write8(uint16_t __unused port_num, unsigned offs, uint8_t v) { if (offs == 0) write_command(v); else write_data(v); } uint8_t read8(uint16_t __unused port_num, unsigned offs) { if (offs == 0) return read_command(); else return read_data(); } void raise_irq(int irq_num) { if (mask & (1 << irq_num)) return; irr |= (1 << irq_num); for (int i = 0; i < 8; ++i) { if ((1 << i) & isr) return; if ((1 << i) & irr) { cpu->raise_irq(vector_address + i); return; } } } private: void inta_handler(int irq_num) { isr |= (1 << (irq_num - vector_address)); irr &= ~(1 << (irq_num - vector_address)); } void write_command(uint8_t v) { if (is_icw1(v)) icw1(v); else if (is_ocw2(v)) ocw2(v); else if (is_ocw3(v)) ocw3(v); } uint8_t read_command() { if (command_read_mode == STATE_READ_IRR) return irr; else if (command_read_mode == STATE_READ_ISR) return isr; return 0; } uint8_t read_data() { return mask; } void write_data(uint8_t v) { switch (state) { case STATE_ICW2: vector_address = v & 0xf8; state = STATE_ICW4; return; case STATE_ICW4: assert(v == 0x01); state = STATE_IDLE; return; default: set_mask(v); } } bool is_icw1(uint8_t v) { return v & (1 << 4); } bool is_ocw2(uint8_t v) { return (v & (3 << 3)) == 0; } bool is_ocw3(uint8_t v) { return (v & (0x80 | (3 << 3))) == 0x08; } void icw1(uint8_t __unused v) { state = STATE_ICW2; // Only single PIC in edge triggered, 8086 mode supported assert((v & 0x0f) == 0x3); } void ocw2(uint8_t v) { if (v >> 5 == 1) { for (int i = 0; i < 8; ++i) { if (isr & (1 << i)) { isr &= ~(1 << i); return; } } } else if (v >> 5 == 3) { isr &= ~(1 << (v & 0x7)); } } void ocw3(uint8_t v) { // No poll supported assert(!(v & 0x04)); // No special mask assert(!(v & 0x60)); if ((v & 0x3) == 2) command_read_mode = STATE_READ_IRR; else if ((v & 0x3) == 3) command_read_mode = STATE_READ_ISR; } void set_mask(uint8_t v) { mask = v; } friend class boost::serialization::access; template <class Archive> void serialize(Archive &ar, const unsigned int __unused version) { // clang-format off ar & state; ar & command_read_mode; ar & vector_address; ar & mask; ar & isr; ar & irr; // clang-format on } SimCPU *cpu; State state; CommandReadMode command_read_mode; uint8_t vector_address; uint8_t mask; uint8_t isr; uint8_t irr; };