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 = {