ESCC: connect NULL and STDIO backends.

This commit is contained in:
Maxim Poliakovski 2022-05-07 21:38:27 +02:00
parent da25e72668
commit 4c9001901e
5 changed files with 294 additions and 11 deletions

145
devices/serial/chario.cpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
/** Character I/O backend implementations. */
#include <devices/serial/chario.h>
#include <loguru.hpp>
#include <cinttypes>
#include <memory>
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<signal.h>
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
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

78
devices/serial/chario.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
/** Character I/O definitions. */
#ifndef CHAR_IO_H
#define CHAR_IO_H
#include <cinttypes>
#ifndef _WIN32
#include <termios.h>
#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

View File

@ -21,11 +21,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
/** @file Enhanced Serial Communications Controller (ESCC) emulation. */ /** @file Enhanced Serial Communications Controller (ESCC) emulation. */
#include "escc.h" #include <devices/serial/chario.h>
#include <devices/serial/escc.h>
#include <loguru.hpp> #include <loguru.hpp>
#include <machines/machineproperties.h>
#include <cinttypes> #include <cinttypes>
#include <memory> #include <memory>
#include <string>
/** Remap the compatible addressing scheme to MacRISC one. */ /** Remap the compatible addressing scheme to MacRISC one. */
const uint8_t compat_to_macrisc[6] = { const uint8_t compat_to_macrisc[6] = {
@ -36,9 +39,17 @@ const uint8_t compat_to_macrisc[6] = {
EsccController::EsccController() EsccController::EsccController()
{ {
// allocate channels
this->ch_a = std::unique_ptr<EsccChannel> (new EsccChannel("A")); this->ch_a = std::unique_ptr<EsccChannel> (new EsccChannel("A"));
this->ch_b = std::unique_ptr<EsccChannel> (new EsccChannel("B")); this->ch_b = std::unique_ptr<EsccChannel> (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; this->reg_ptr = 0;
} }
@ -144,9 +155,25 @@ void EsccController::write_internal(EsccChannel *ch, uint8_t value)
} }
// ======================== ESCC Channel methods ============================== // ======================== ESCC Channel methods ==============================
void EsccChannel::attach_backend(int id)
{
switch(id) {
case CHARIO_BE_NULL:
this->chario = std::unique_ptr<CharIoBackEnd> (new CharIoNull);
break;
case CHARIO_BE_STDIO:
this->chario = std::unique_ptr<CharIoBackEnd> (new CharIoStdin);
break;
default:
LOG_F(ERROR, "ESCC: unknown backend ID %d, using NULL instead", id);
this->chario = std::unique_ptr<CharIoBackEnd> (new CharIoNull);
}
}
void EsccChannel::reset(bool hw_reset) void EsccChannel::reset(bool hw_reset)
{ {
this->chario->rcv_disable();
this->write_regs[1] &= 0x24; this->write_regs[1] &= 0x24;
this->write_regs[3] &= 0xFE; this->write_regs[3] &= 0xFE;
this->write_regs[4] |= 0x04; this->write_regs[4] |= 0x04;
@ -184,15 +211,22 @@ void EsccChannel::write_reg(int reg_num, uint8_t value)
{ {
switch (reg_num) { switch (reg_num) {
case 3: case 3:
if (value & 0x11) {
if ((this->write_regs[3] ^ value) & 0x10) { if ((this->write_regs[3] ^ value) & 0x10) {
this->write_regs[3] |= 0x10; this->write_regs[3] |= 0x10;
this->read_regs[0] |= 0x10; // set SYNC_HUNT flag this->read_regs[0] |= 0x10; // set SYNC_HUNT flag
LOG_F(9, "ESCC: Hunt mode entered."); LOG_F(9, "ESCC: Hunt mode entered.");
} }
if ((this->write_regs[3] ^ value) & 1) {
if (value & 1) { if (value & 1) {
this->write_regs[3] |= 0x1; this->write_regs[3] |= 0x1;
this->chario->rcv_enable();
LOG_F(9, "ESCC: receiver enabled."); 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); 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; break;
case 14: case 14:
switch (value >> 5) { switch (value >> 5) {
case DPLL_NULL_CMD:
break;
case DPLL_ENTER_SRC_MODE: case DPLL_ENTER_SRC_MODE:
this->dpll_active = 1; this->dpll_active = 1;
this->read_regs[10] &= 0x3F; 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) 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]; return this->read_regs[reg_num];
} }
void EsccChannel::send_byte(uint8_t value) 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() uint8_t EsccChannel::receive_byte()
{ {
// Remove one byte from the Receive FIFO // TODO: remove one byte from the Receive FIFO
return 0xFF;
uint8_t c;
this->chario->rcv_char(&c);
this->read_regs[0] &= ~1;
return c;
} }

View File

@ -24,6 +24,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifndef ESCC_H #ifndef ESCC_H
#define ESCC_H #define ESCC_H
#include <devices/serial/chario.h>
#include <cinttypes> #include <cinttypes>
#include <memory> #include <memory>
#include <string> #include <string>
@ -71,6 +73,7 @@ enum {
/** DPLL commands in WR14. */ /** DPLL commands in WR14. */
enum { enum {
DPLL_NULL_CMD = 0,
DPLL_ENTER_SRC_MODE = 1, DPLL_ENTER_SRC_MODE = 1,
DPLL_RST_MISSING_CLK = 2, DPLL_RST_MISSING_CLK = 2,
DPLL_DISABLE = 3, DPLL_DISABLE = 3,
@ -91,6 +94,7 @@ public:
EsccChannel(std::string name) { this->name = name; }; EsccChannel(std::string name) { this->name = name; };
~EsccChannel() = default; ~EsccChannel() = default;
void attach_backend(int id);
void reset(bool hw_reset); void reset(bool hw_reset);
uint8_t read_reg(int reg_num); uint8_t read_reg(int reg_num);
void write_reg(int reg_num, uint8_t value); void write_reg(int reg_num, uint8_t value);
@ -107,6 +111,8 @@ private:
uint8_t dpll_clock_src; uint8_t dpll_clock_src;
uint8_t brg_active; uint8_t brg_active;
uint8_t brg_clock_src; uint8_t brg_clock_src;
std::unique_ptr<CharIoBackEnd> chario;
}; };
/** ESCC Controller class. */ /** ESCC Controller class. */

View File

@ -1,6 +1,6 @@
/* /*
DingusPPC - The Experimental PowerPC Macintosh emulator DingusPPC - The Experimental PowerPC Macintosh emulator
Copyright (C) 2018-21 divingkatae and maximum Copyright (C) 2018-22 divingkatae and maximum
(theweirdo) spatium (theweirdo) spatium
(Contact divingkatae#1017 or powermax#2286 on Discord for more info) (Contact divingkatae#1017 or powermax#2286 on Discord for more info)
@ -69,6 +69,8 @@ static const map<uint32_t, std::tuple<string, const char*>> rom_identity = {
static const vector<string> WriteToggle = {"ON", "on", "OFF", "off"}; static const vector<string> WriteToggle = {"ON", "on", "OFF", "off"};
static const vector<string> CharIoBackends = {"null", "stdio"};
static const PropMap CatalystSettings = { static const PropMap CatalystSettings = {
{"rambank1_size", {"rambank1_size",
new IntProperty(16, vector<uint32_t>({4, 8, 16, 32, 64, 128}))}, new IntProperty(16, vector<uint32_t>({4, 8, 16, 32, 64, 128}))},
@ -84,6 +86,7 @@ static const PropMap CatalystSettings = {
new StrProperty("")}, new StrProperty("")},
{"fdd_img", {"fdd_img",
new StrProperty("")}, new StrProperty("")},
{"serial_backend", new StrProperty("null", CharIoBackends)},
}; };
static const PropMap GossamerSettings = { static const PropMap GossamerSettings = {
@ -100,6 +103,7 @@ static const PropMap GossamerSettings = {
{"fdd_img", {"fdd_img",
new StrProperty("")}, new StrProperty("")},
{"fdd_wr_prot", new StrProperty("OFF", WriteToggle)}, {"fdd_wr_prot", new StrProperty("OFF", WriteToggle)},
{"serial_backend", new StrProperty("null", CharIoBackends)},
}; };
/** Monitors supported by the PDM on-board video. */ /** Monitors supported by the PDM on-board video. */
@ -121,6 +125,7 @@ static const PropMap PDMSettings = {
new StrProperty("")}, new StrProperty("")},
{"fdd_wr_prot", {"fdd_wr_prot",
new StrProperty("OFF", WriteToggle)}, new StrProperty("OFF", WriteToggle)},
{"serial_backend", new StrProperty("null", CharIoBackends)},
}; };
static const map<string, string> PropHelp = { static const map<string, string> PropHelp = {
@ -132,6 +137,7 @@ static const map<string, string> PropHelp = {
{"fdd_img", "specifies path to floppy disk image"}, {"fdd_img", "specifies path to floppy disk image"},
{"fdd_wr_prot", "toggles floppy disks write protection"}, {"fdd_wr_prot", "toggles floppy disks write protection"},
{"mon_id", "specifies which monitor to emulate"}, {"mon_id", "specifies which monitor to emulate"},
{"serial_backend", "specifies the backend for the serial port"},
}; };
static const map<string, tuple<PropMap, function<int(string&)>, string>> machines = { static const map<string, tuple<PropMap, function<int(string&)>, string>> machines = {