diff --git a/devices/serial/chario.cpp b/devices/serial/chario.cpp new file mode 100644 index 0000000..dccdd1a --- /dev/null +++ b/devices/serial/chario.cpp @@ -0,0 +1,145 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-22 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +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, either version 3 of the License, or +(at your option) any later version. + +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, see . +*/ + +/** Character I/O backend implementations. */ + +#include +#include + +#include +#include + +bool CharIoNull::rcv_char_available() +{ + return false; +} + +int CharIoNull::xmit_char(uint8_t c) +{ + return 0; +} + +int CharIoNull::rcv_char(uint8_t *c) +{ + *c = 0xFF; + return 0; +} + +//======================== STDIO character I/O backend ======================== +#ifndef _WIN32 + +#include +#include +#include +#include + +struct sigaction old_act, new_act; +struct termios orig_termios; + +void mysig_handler(int signum) +{ + // restore original terminal state + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); + + // restore original signal handler for SIGINT + signal(SIGINT, old_act.sa_handler); + + LOG_F(INFO, "Old terminal state restored, SIG#=%d", signum); + + // re-post signal + raise(signum); +} + +int CharIoStdin::rcv_enable() +{ + if (this->stdio_inited) + return 0; + + // save original terminal state + tcgetattr(STDIN_FILENO, &orig_termios); + + struct termios new_termios = orig_termios; + + new_termios.c_cflag &= ~(CSIZE | PARENB); + new_termios.c_cflag |= CS8; + new_termios.c_lflag &= ~(ECHO | ICANON); + new_termios.c_iflag &= ~(ICRNL); + + tcsetattr(STDIN_FILENO, TCSANOW, &new_termios); + + // save original signal handler for SIGINT + //struct sigaction new_act; + memset(&new_act, 0, sizeof(new_act)); + new_act.sa_handler = mysig_handler; + sigaction(SIGINT, &new_act, &old_act); + + // redirect SIGINT to new handler + //signal(SIGINT, mysig_handler); + + this->stdio_inited = true; + + return 0; +} + +void CharIoStdin::rcv_disable() +{ + if (!this->stdio_inited) + return; + + // restore original terminal state + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); + + // restore original signal handler for SIGINT + signal(SIGINT, old_act.sa_handler); + + this->stdio_inited = false; +} + +bool CharIoStdin::rcv_char_available() +{ + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + fd_set savefds = readfds; + + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + int chr; + + int sel_rv = select(1, &readfds, NULL, NULL, &timeout); + return sel_rv > 0; +} + +int CharIoStdin::xmit_char(uint8_t c) +{ + write(STDOUT_FILENO, &c, 1); + return 0; +} + +int CharIoStdin::rcv_char(uint8_t *c) +{ + read(STDIN_FILENO, c, 1); + return 0; +} + +#endif // _WIN32 diff --git a/devices/serial/chario.h b/devices/serial/chario.h new file mode 100644 index 0000000..118b26f --- /dev/null +++ b/devices/serial/chario.h @@ -0,0 +1,78 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-22 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +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, either version 3 of the License, or +(at your option) any later version. + +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, see . +*/ + +/** Character I/O definitions. */ + +#ifndef CHAR_IO_H +#define CHAR_IO_H + +#include + +#ifndef _WIN32 +#include +#endif + +enum { + CHARIO_BE_NULL = 0, // NULL backend: swallows everything, receives nothing + CHARIO_BE_STDIO = 1, // STDIO backend: uses STDIN for input and STDOUT for output +}; + +/** Interface for character I/O backends. */ +class CharIoBackEnd { +public: + CharIoBackEnd() = default; + virtual ~CharIoBackEnd() = default; + + virtual int rcv_enable() { return 0; }; + virtual void rcv_disable() {}; + virtual bool rcv_char_available() = 0; + virtual int xmit_char(uint8_t c) = 0; + virtual int rcv_char(uint8_t *c) = 0; +}; + +/** Null character I/O backend. */ +class CharIoNull : public CharIoBackEnd { +public: + CharIoNull() = default; + ~CharIoNull() = default; + + bool rcv_char_available(); + int xmit_char(uint8_t c); + int rcv_char(uint8_t *c); +}; + +/** Stdin character I/O backend. */ +class CharIoStdin : public CharIoBackEnd { +public: + CharIoStdin() { this->stdio_inited = false; }; + ~CharIoStdin() = default; + + int rcv_enable(); + void rcv_disable(); + bool rcv_char_available(); + int xmit_char(uint8_t c); + int rcv_char(uint8_t *c); + +private: + bool stdio_inited; +}; + +#endif // CHAR_IO_H diff --git a/devices/serial/escc.cpp b/devices/serial/escc.cpp index a801df5..205167c 100644 --- a/devices/serial/escc.cpp +++ b/devices/serial/escc.cpp @@ -21,11 +21,14 @@ along with this program. If not, see . /** @file Enhanced Serial Communications Controller (ESCC) emulation. */ -#include "escc.h" +#include +#include #include +#include #include #include +#include /** Remap the compatible addressing scheme to MacRISC one. */ const uint8_t compat_to_macrisc[6] = { @@ -36,9 +39,17 @@ const uint8_t compat_to_macrisc[6] = { EsccController::EsccController() { + // allocate channels this->ch_a = std::unique_ptr (new EsccChannel("A")); this->ch_b = std::unique_ptr (new EsccChannel("B")); + // attach backends + std::string backend_name = GET_STR_PROP("serial_backend"); + + this->ch_a->attach_backend( + (backend_name == "stdio") ? CHARIO_BE_STDIO : CHARIO_BE_NULL); + this->ch_b->attach_backend(CHARIO_BE_NULL); + this->reg_ptr = 0; } @@ -144,9 +155,25 @@ void EsccController::write_internal(EsccChannel *ch, uint8_t value) } // ======================== ESCC Channel methods ============================== +void EsccChannel::attach_backend(int id) +{ + switch(id) { + case CHARIO_BE_NULL: + this->chario = std::unique_ptr (new CharIoNull); + break; + case CHARIO_BE_STDIO: + this->chario = std::unique_ptr (new CharIoStdin); + break; + default: + LOG_F(ERROR, "ESCC: unknown backend ID %d, using NULL instead", id); + this->chario = std::unique_ptr (new CharIoNull); + } +} void EsccChannel::reset(bool hw_reset) { + this->chario->rcv_disable(); + this->write_regs[1] &= 0x24; this->write_regs[3] &= 0xFE; this->write_regs[4] |= 0x04; @@ -184,15 +211,22 @@ void EsccChannel::write_reg(int reg_num, uint8_t value) { switch (reg_num) { case 3: - if (value & 0x11) { - if ((this->write_regs[3] ^ value) & 0x10) { - this->write_regs[3] |= 0x10; - this->read_regs[0] |= 0x10; // set SYNC_HUNT flag - LOG_F(9, "ESCC: Hunt mode entered."); - } + if ((this->write_regs[3] ^ value) & 0x10) { + this->write_regs[3] |= 0x10; + this->read_regs[0] |= 0x10; // set SYNC_HUNT flag + LOG_F(9, "ESCC: Hunt mode entered."); + } + if ((this->write_regs[3] ^ value) & 1) { if (value & 1) { this->write_regs[3] |= 0x1; + this->chario->rcv_enable(); LOG_F(9, "ESCC: receiver enabled."); + } else { + this->write_regs[3] ^= 0x1; + this->chario->rcv_disable(); + LOG_F(9, "ESCC: receiver disabled."); + this->write_regs[3] |= 0x10; // enter HUNT mode + this->read_regs[0] |= 0x10; // set SYNC_HUNT flag } } this->write_regs[3] = (this->write_regs[3] & 0x11) | (value & 0xEE); @@ -205,6 +239,8 @@ void EsccChannel::write_reg(int reg_num, uint8_t value) break; case 14: switch (value >> 5) { + case DPLL_NULL_CMD: + break; case DPLL_ENTER_SRC_MODE: this->dpll_active = 1; this->read_regs[10] &= 0x3F; @@ -245,16 +281,28 @@ void EsccChannel::write_reg(int reg_num, uint8_t value) uint8_t EsccChannel::read_reg(int reg_num) { + if (!reg_num) { + if (this->chario->rcv_char_available()) { + this->read_regs[0] |= 1; + } + } return this->read_regs[reg_num]; } void EsccChannel::send_byte(uint8_t value) { - // Put one byte into the Data FIFO + // TODO: put one byte into the Data FIFO + + this->chario->xmit_char(value); } uint8_t EsccChannel::receive_byte() { - // Remove one byte from the Receive FIFO - return 0xFF; + // TODO: remove one byte from the Receive FIFO + + uint8_t c; + + this->chario->rcv_char(&c); + this->read_regs[0] &= ~1; + return c; } diff --git a/devices/serial/escc.h b/devices/serial/escc.h index 99db9eb..44e4ca9 100644 --- a/devices/serial/escc.h +++ b/devices/serial/escc.h @@ -24,6 +24,8 @@ along with this program. If not, see . #ifndef ESCC_H #define ESCC_H +#include + #include #include #include @@ -71,6 +73,7 @@ enum { /** DPLL commands in WR14. */ enum { + DPLL_NULL_CMD = 0, DPLL_ENTER_SRC_MODE = 1, DPLL_RST_MISSING_CLK = 2, DPLL_DISABLE = 3, @@ -91,6 +94,7 @@ public: EsccChannel(std::string name) { this->name = name; }; ~EsccChannel() = default; + void attach_backend(int id); void reset(bool hw_reset); uint8_t read_reg(int reg_num); void write_reg(int reg_num, uint8_t value); @@ -107,6 +111,8 @@ private: uint8_t dpll_clock_src; uint8_t brg_active; uint8_t brg_clock_src; + + std::unique_ptr chario; }; /** ESCC Controller class. */ diff --git a/machines/machinefactory.cpp b/machines/machinefactory.cpp index aba3044..11757b0 100644 --- a/machines/machinefactory.cpp +++ b/machines/machinefactory.cpp @@ -1,6 +1,6 @@ /* DingusPPC - The Experimental PowerPC Macintosh emulator -Copyright (C) 2018-21 divingkatae and maximum +Copyright (C) 2018-22 divingkatae and maximum (theweirdo) spatium (Contact divingkatae#1017 or powermax#2286 on Discord for more info) @@ -69,6 +69,8 @@ static const map> rom_identity = { static const vector WriteToggle = {"ON", "on", "OFF", "off"}; +static const vector CharIoBackends = {"null", "stdio"}; + static const PropMap CatalystSettings = { {"rambank1_size", new IntProperty(16, vector({4, 8, 16, 32, 64, 128}))}, @@ -84,6 +86,7 @@ static const PropMap CatalystSettings = { new StrProperty("")}, {"fdd_img", new StrProperty("")}, + {"serial_backend", new StrProperty("null", CharIoBackends)}, }; static const PropMap GossamerSettings = { @@ -100,6 +103,7 @@ static const PropMap GossamerSettings = { {"fdd_img", new StrProperty("")}, {"fdd_wr_prot", new StrProperty("OFF", WriteToggle)}, + {"serial_backend", new StrProperty("null", CharIoBackends)}, }; /** Monitors supported by the PDM on-board video. */ @@ -121,6 +125,7 @@ static const PropMap PDMSettings = { new StrProperty("")}, {"fdd_wr_prot", new StrProperty("OFF", WriteToggle)}, + {"serial_backend", new StrProperty("null", CharIoBackends)}, }; static const map PropHelp = { @@ -132,6 +137,7 @@ static const map PropHelp = { {"fdd_img", "specifies path to floppy disk image"}, {"fdd_wr_prot", "toggles floppy disks write protection"}, {"mon_id", "specifies which monitor to emulate"}, + {"serial_backend", "specifies the backend for the serial port"}, }; static const map, string>> machines = {