Improve ESCC stub to bypass LocalTalk.

This commit is contained in:
Maxim Poliakovski 2022-02-26 10:55:30 +01:00
parent 205b5a4956
commit 4de10898ea
2 changed files with 141 additions and 17 deletions

View File

@ -46,22 +46,37 @@ void EsccController::reset()
uint8_t EsccController::read(uint8_t reg_offset)
{
uint8_t result = 0;
switch(reg_offset) {
case EsccReg::Port_B_Cmd:
LOG_F(9, "ESCC: reading Port B register RR%d", this->reg_ptr);
if (this->reg_ptr == 2) {
// TODO: implement interrupt vector modifications
result = this->int_vec;
} else {
result = this->ch_b->read_reg(this->reg_ptr);
}
this->reg_ptr = 0;
return this->ch_b->read_reg(reg_offset);
break;
case EsccReg::Port_A_Cmd:
LOG_F(9, "ESCC: reading Port A register RR%d", this->reg_ptr);
if (this->reg_ptr == 2) {
return this->int_vec;
} else {
return this->ch_a->read_reg(this->reg_ptr);
}
this->reg_ptr = 0;
return this->ch_a->read_reg(reg_offset);
break;
case EsccReg::Port_B_Data:
return this->ch_b->receive_byte();
case EsccReg::Port_A_Data:
return this->ch_a->receive_byte();
default:
LOG_F(INFO, "ESCC: reading register %d", reg_offset);
LOG_F(9, "ESCC: reading from unimplemented register %d", reg_offset);
}
return 0;
return result;
}
void EsccController::write(uint8_t reg_offset, uint8_t value)
@ -73,8 +88,14 @@ void EsccController::write(uint8_t reg_offset, uint8_t value)
case EsccReg::Port_A_Cmd:
this->write_internal(this->ch_a.get(), value);
break;
case EsccReg::Port_B_Data:
this->ch_b->send_byte(value);
break;
case EsccReg::Port_A_Data:
this->ch_a->send_byte(value);
break;
default:
LOG_F(INFO, "ESCC: writing 0x%X to register %d", value, reg_offset);
LOG_F(9, "ESCC: writing 0x%X to unimplemented register %d", value, reg_offset);
}
}
@ -131,6 +152,15 @@ void EsccChannel::reset(bool hw_reset)
this->read_regs[3] = 0x00;
this->read_regs[10] = 0x00;
// initialize DPLL
this->dpll_active = 0;
this->dpll_mode = DpllMode::NRZI;
this->dpll_clock_src = 0;
// initialize Baud Rate Generator (BRG)
this->brg_active = 0;
this->brg_clock_src = 0;
if (hw_reset) {
this->write_regs[10] = 0;
this->write_regs[11] = 8;
@ -145,14 +175,79 @@ void EsccChannel::reset(bool hw_reset)
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 (value & 1) {
this->write_regs[3] |= 0x1;
LOG_F(9, "ESCC: receiver enabled.");
}
}
this->write_regs[3] = (this->write_regs[3] & 0x11) | (value & 0xEE);
return;
case 7:
if (this->write_regs[15] & 1) {
this->wr7_enh = value;
return;
}
break;
case 14:
switch (value >> 5) {
case DPLL_ENTER_SRC_MODE:
this->dpll_active = 1;
this->read_regs[10] &= 0x3F;
break;
case DPLL_DISABLE:
this->dpll_active = 0;
// fallthrough
case DPLL_RST_MISSING_CLK:
this->read_regs[10] &= 0x3F;
break;
case DPLL_SET_SRC_BGR:
this->dpll_clock_src = 0;
break;
case DPLL_SET_SRC_RTXC:
this->dpll_clock_src = 1;
break;
case DPLL_SET_FM_MODE:
this->dpll_mode = DpllMode::FM;
break;
case DPLL_SET_NRZI_MODE:
this->dpll_mode = DpllMode::NRZI;
break;
default:
LOG_F(WARNING, "ESCC: unimplemented DPLL command %d", value >> 5);
}
if (value & 0x1C) { // Local Loopback, Auto Echo DTR/REQ bits set
LOG_F(WARNING, "ESCC: unexpected value in WR14 = 0x%X", value);
}
if (this->brg_active ^ (value & 1)) {
this->brg_active = value & 1;
LOG_F(9, "ESCC: BRG %s", this->brg_active ? "enabled" : "disabled");
}
return;
}
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);
}
uint8_t EsccChannel::read_reg(int reg_num)
{
return 0;
return this->read_regs[reg_num];
}
void EsccChannel::send_byte(uint8_t value)
{
// Put one byte into the Data FIFO
}
uint8_t EsccChannel::receive_byte()
{
// Remove one byte from the Receive FIFO
return 0xFF;
}

View File

@ -28,17 +28,21 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <memory>
#include <string>
/** ESCC register addresses */
/** ESCC register positions */
/* Please note that the registers below are provided
by Apple I/O controllers for accessing ESCC in a
more convenient way. Actual physical addresses
are controller dependent. */
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,
Port_A_Cmd = 1,
Port_B_Data = 2, // direct access to WR8/RR8
Port_A_Data = 3, // direct access to WR8/RR8
Enh_Reg_B = 4, // undocumented Apple extension
Enh_Reg_A = 5, // undocumented Apple extension
};
/** LocalTalk LTPC registers */
/** LocalTalk LTPC registers provided by a MacIO controller. */
enum LocalTalkReg : uint8_t {
Rec_Count = 8,
Start_A = 9,
@ -57,6 +61,22 @@ enum {
RESET_CH_B = 0x40
};
/** DPLL commands in WR14. */
enum {
DPLL_ENTER_SRC_MODE = 1,
DPLL_RST_MISSING_CLK = 2,
DPLL_DISABLE = 3,
DPLL_SET_SRC_BGR = 4,
DPLL_SET_SRC_RTXC = 5,
DPLL_SET_FM_MODE = 6,
DPLL_SET_NRZI_MODE = 7
};
enum DpllMode : uint8_t {
NRZI = 0,
FM = 1
};
/** ESCC Channel class. */
class EsccChannel {
public:
@ -66,11 +86,19 @@ public:
void reset(bool hw_reset);
uint8_t read_reg(int reg_num);
void write_reg(int reg_num, uint8_t value);
void send_byte(uint8_t value);
uint8_t receive_byte();
private:
std::string name;
uint8_t read_regs[16];
uint8_t write_regs[16];
uint8_t wr7_enh;
uint8_t dpll_active;
uint8_t dpll_mode;
uint8_t dpll_clock_src;
uint8_t brg_active;
uint8_t brg_clock_src;
};
/** ESCC Controller class. */
@ -91,6 +119,7 @@ private:
std::unique_ptr<EsccChannel> ch_b;
int reg_ptr; // register pointer for reading/writing (same for both channels)
uint8_t master_int_cntrl;
uint8_t int_vec;
};