diff --git a/devices/serial/escc.cpp b/devices/serial/escc.cpp
new file mode 100644
index 0000000..15a3acf
--- /dev/null
+++ b/devices/serial/escc.cpp
@@ -0,0 +1,122 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-21 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 .
+*/
+
+/** @file Enhanced Serial Communications Controller (ESCC) emulation. */
+
+#include "escc.h"
+#include
+
+#include
+#include
+
+EsccController::EsccController()
+{
+ this->ch_a = std::unique_ptr (new EsccChannel("A"));
+ this->ch_b = std::unique_ptr (new EsccChannel("B"));
+
+ this->reg_ptr = 0;
+}
+
+uint8_t EsccController::read(uint8_t reg_offset)
+{
+ switch(reg_offset) {
+ case EsccReg::Port_B_Cmd:
+ LOG_F(9, "ESCC: reading Port B register RR%d", this->reg_ptr);
+ this->reg_ptr = 0;
+ break;
+ case EsccReg::Port_A_Cmd:
+ LOG_F(9, "ESCC: reading Port A register RR%d", this->reg_ptr);
+ this->reg_ptr = 0;
+ break;
+ default:
+ LOG_F(INFO, "ESCC: reading register %d", reg_offset);
+ }
+
+ return 0;
+}
+
+void EsccController::write(uint8_t reg_offset, uint8_t value)
+{
+ switch(reg_offset) {
+ case EsccReg::Port_B_Cmd:
+ this->write_internal(this->ch_b.get(), value);
+ break;
+ case EsccReg::Port_A_Cmd:
+ this->write_internal(this->ch_a.get(), value);
+ break;
+ default:
+ LOG_F(INFO, "ESCC: writing 0x%X to register %d", value, reg_offset);
+ }
+}
+
+uint8_t EsccController::read_compat(uint8_t reg_offset)
+{
+ switch(reg_offset) {
+ case Compat::EsccReg::Port_B_Cmd:
+ LOG_F(9, "ESCC: reading Port B register RR%d", this->reg_ptr);
+ this->reg_ptr = 0;
+ break;
+ case Compat::EsccReg::Port_A_Cmd:
+ LOG_F(9, "ESCC: reading Port A register RR%d", this->reg_ptr);
+ this->reg_ptr = 0;
+ break;
+ default:
+ LOG_F(INFO, "ESCC: reading register %d", reg_offset);
+ }
+
+ return 0;
+}
+
+void EsccController::write_compat(uint8_t reg_offset, uint8_t value)
+{
+ switch(reg_offset) {
+ case Compat::EsccReg::Port_B_Cmd:
+ this->write_internal(this->ch_b.get(), value);
+ break;
+ case Compat::EsccReg::Port_A_Cmd:
+ this->write_internal(this->ch_a.get(), value);
+ break;
+ default:
+ LOG_F(INFO, "ESCC: writing 0x%X to register %d", value, reg_offset);
+ }
+}
+
+void EsccController::write_internal(EsccChannel *ch, uint8_t value)
+{
+ if (this->reg_ptr) {
+ ch->write_reg(this->reg_ptr, value);
+ this->reg_ptr = 0;
+ } else {
+ this->reg_ptr = value & 7;
+ switch(value >> 3) {
+ case WR0Cmd::Point_High:
+ this->reg_ptr |= 8;
+ break;
+ }
+ }
+}
+
+void EsccChannel::write_reg(int reg_num, uint8_t value)
+{
+ this->write_regs[reg_num] = value;
+ LOG_F(9, "ESCC: writing 0x%X to Channel %s WR%d", value, this->name.c_str(),
+ reg_num);
+}
diff --git a/devices/serial/escc.h b/devices/serial/escc.h
new file mode 100644
index 0000000..323a0f8
--- /dev/null
+++ b/devices/serial/escc.h
@@ -0,0 +1,101 @@
+/*
+DingusPPC - The Experimental PowerPC Macintosh emulator
+Copyright (C) 2018-21 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 .
+*/
+
+/** @file Enhanced Serial Communications Controller (ESCC) definitions. */
+
+#ifndef ESCC_H
+#define ESCC_H
+
+#include
+#include
+#include
+
+/** ESCC compatible (aka Macintosh legacy) register addressing */
+namespace Compat {
+ enum EsccReg : uint8_t {
+ Port_B_Cmd = 0,
+ Port_A_Cmd = 1,
+ Port_B_Data = 2,
+ Port_A_Data = 3,
+ Enh_Reg_B = 4,
+ Enh_Reg_A = 5,
+ };
+};
+
+/** ESCC MacRISC style register addressing */
+enum EsccReg : uint8_t {
+ Port_B_Cmd = 0,
+ Port_B_Data = 1,
+ Port_A_Cmd = 2,
+ Port_A_Data = 3,
+ Enh_Reg_B = 4,
+ Enh_Reg_A = 5,
+};
+
+/** LocalTalk registers (same in both addressing schemes) */
+enum LocalTalkReg : uint8_t {
+ Rec_Count = 8,
+ Start_A = 9,
+ Start_B = 0xA,
+ Detect_AB = 0xB,
+};
+
+enum WR0Cmd : uint8_t {
+ Point_High = 1,
+};
+
+/** ESCC Channel class. */
+class EsccChannel {
+public:
+ EsccChannel(std::string name) { this->name = name; };
+ ~EsccChannel() = default;
+
+ void write_reg(int reg_num, uint8_t value);
+
+private:
+ std::string name;
+ uint8_t write_regs[16];
+};
+
+/** ESCC Controller class. */
+class EsccController {
+public:
+ EsccController();
+ ~EsccController() = default;
+
+ // ESCC MacRISC registers access
+ uint8_t read(uint8_t reg_offset);
+ void write(uint8_t reg_offset, uint8_t value);
+
+ // ESCC compatible registers access
+ uint8_t read_compat(uint8_t reg_offset);
+ void write_compat(uint8_t reg_offset, uint8_t value);
+
+private:
+ void write_internal(EsccChannel* ch, uint8_t value);
+
+ std::unique_ptr ch_a;
+ std::unique_ptr ch_b;
+
+ int reg_ptr; // register pointer for reading/writing (same for both channels)
+};
+
+#endif // ESCC_H