477 lines
15 KiB
C
477 lines
15 KiB
C
/* vax_2681.c: 2681 DUART Simulator
|
|
|
|
Copyright (c) 2011-2013, Matt Burke
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of the author shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from the author.
|
|
|
|
09-Aug-2013 MB Fixed reporting of status bits in receiver buffer
|
|
11-Jun-2013 MB First version
|
|
*/
|
|
|
|
#include "vax_2681.h"
|
|
|
|
#define CMD_ERX 0x0001 /* Enable receiver */
|
|
#define CMD_DRX 0x0002 /* Disable receiver */
|
|
#define CMD_ETX 0x0004 /* Enable transmitter */
|
|
#define CMD_DTX 0x0008 /* Disable transmitter */
|
|
#define CMD_V_CMD 4 /* Command */
|
|
#define CMD_M_CMD 0x7
|
|
|
|
#define STS_RXR 0x0001 /* Receiver ready */
|
|
#define STS_FFL 0x0002 /* FIFO full */
|
|
#define STS_TXR 0x0004 /* Transmitter ready */
|
|
#define STS_TXE 0x0008 /* Transmitter empty */
|
|
#define STS_OER 0x0010 /* Overrun error */
|
|
#define STS_PER 0x0020 /* Parity error */
|
|
#define STS_FER 0x0040 /* Framing error */
|
|
#define STS_RXB 0x0080 /* Received break */
|
|
|
|
#define ISTS_TAI 0x0001 /* Transmitter ready A */
|
|
#define ISTS_RAI 0x0002 /* Receiver ready A */
|
|
#define ISTS_CBA 0x0004 /* Change in break A */
|
|
#define ISTS_CRI 0x0008 /* Counter ready */
|
|
#define ISTS_TBI 0x0010 /* Transmitter ready B */
|
|
#define ISTS_RBI 0x0020 /* Receiver ready B */
|
|
#define ISTS_CBB 0x0040 /* Change in break B */
|
|
#define ISTS_IPC 0x0080 /* Interrupt port change */
|
|
|
|
#define MODE_V_CHM 6 /* Channel mode */
|
|
#define MODE_M_CHM 0x3
|
|
|
|
#define PORT_A 0
|
|
#define PORT_B 1
|
|
|
|
void ua2681_update_rxi (UART2681 *ctx);
|
|
void ua2681_update_txi (UART2681 *ctx);
|
|
uint8 ua2681_oport (UART2681 *ctx);
|
|
|
|
|
|
void ua2681_wr (UART2681 *ctx, uint32 rg, uint32 data)
|
|
{
|
|
uint32 mp;
|
|
|
|
switch (rg) {
|
|
|
|
case 0: /* mode 1A,2A */
|
|
mp = ctx->port[PORT_A].mode_ptr;
|
|
ctx->port[PORT_A].mode[mp++] = data & 0xFF;
|
|
if (mp >= 2) mp = 0;
|
|
ctx->port[PORT_A].mode_ptr = mp;
|
|
break;
|
|
|
|
case 1: /* status/clock A */
|
|
/* Used to set baud rate - NI */
|
|
break;
|
|
|
|
case 2: /* command A */
|
|
if (data & CMD_ETX) /* enable transmitter */
|
|
ctx->port[PORT_A].cmd |= CMD_ETX;
|
|
else if (data & CMD_DTX) /* disable transmitter */
|
|
ctx->port[PORT_A].cmd &= ~CMD_ETX;
|
|
|
|
if (data & CMD_ERX) /* enable receiver */
|
|
ctx->port[PORT_A].cmd |= CMD_ERX;
|
|
else if (data & CMD_DRX) /* disable receiver */
|
|
ctx->port[PORT_A].cmd &= ~CMD_ERX;
|
|
|
|
switch ((data >> CMD_V_CMD) & CMD_M_CMD) {
|
|
case 1:
|
|
ctx->port[PORT_A].mode_ptr = 0;
|
|
break;
|
|
|
|
case 2:
|
|
ctx->port[PORT_A].cmd &= ~CMD_ERX;
|
|
ctx->port[PORT_A].sts &= ~(STS_RXR | STS_FFL);
|
|
break;
|
|
|
|
case 3:
|
|
ctx->port[PORT_A].sts &= ~STS_TXR;
|
|
break;
|
|
|
|
case 4:
|
|
ctx->port[PORT_A].sts &= ~(STS_FER | STS_PER | STS_OER);
|
|
break;
|
|
}
|
|
ua2681_update_rxi (ctx);
|
|
ua2681_update_txi (ctx);
|
|
break;
|
|
|
|
case 3: /* tx/rx buf A */
|
|
if (((ctx->port[PORT_A].mode[1] >> MODE_V_CHM) & MODE_M_CHM) == 0x2) { /* Maint */
|
|
ctx->port[PORT_A].buf = data & 0xFF;
|
|
ctx->port[PORT_A].sts |= (STS_RXR | STS_FFL);
|
|
ctx->ists |= ISTS_RAI;
|
|
}
|
|
else {
|
|
if (ctx->port[PORT_A].put_char != NULL)
|
|
ctx->port[PORT_A].put_char ((uint8)data);
|
|
}
|
|
ua2681_update_txi (ctx);
|
|
break;
|
|
|
|
case 4: /* auxiliary control */
|
|
ctx->acr = data & 0xFF;
|
|
break;
|
|
|
|
case 5: /* interrupt status/mask */
|
|
ctx->imask = data & 0xFF;
|
|
break;
|
|
|
|
case 8: /* mode 1B,2B */
|
|
mp = ctx->port[PORT_B].mode_ptr;
|
|
ctx->port[PORT_B].mode[mp++] = data & 0xFF;
|
|
if (mp >= 2) mp = 0;
|
|
ctx->port[PORT_B].mode_ptr = mp;
|
|
break;
|
|
|
|
case 9: /* status/clock B */
|
|
/* Used to set baud rate - NI */
|
|
break;
|
|
|
|
case 10: /* command B */
|
|
if (data & CMD_ETX) /* enable transmitter */
|
|
ctx->port[PORT_B].cmd |= CMD_ETX;
|
|
else if (data & CMD_DTX) /* disable transmitter */
|
|
ctx->port[PORT_B].cmd &= ~CMD_ETX;
|
|
|
|
if (data & CMD_ERX) /* enable receiver */
|
|
ctx->port[PORT_B].cmd |= CMD_ERX;
|
|
else if (data & CMD_DRX) /* disable receiver */
|
|
ctx->port[PORT_B].cmd &= ~CMD_ERX;
|
|
|
|
switch ((data >> CMD_V_CMD) & CMD_M_CMD) {
|
|
case 1: /* reset mode pointer */
|
|
ctx->port[PORT_B].mode_ptr = 0;
|
|
break;
|
|
|
|
case 2:
|
|
ctx->port[PORT_B].cmd &= ~CMD_ERX;
|
|
ctx->port[PORT_B].sts &= ~(STS_RXR | STS_FFL);
|
|
break;
|
|
|
|
case 3:
|
|
ctx->port[PORT_B].sts &= ~STS_TXR;
|
|
break;
|
|
|
|
case 4:
|
|
ctx->port[PORT_B].sts &= ~(STS_FER | STS_PER | STS_OER);
|
|
break;
|
|
}
|
|
ua2681_update_rxi (ctx);
|
|
ua2681_update_txi (ctx);
|
|
break;
|
|
|
|
case 11: /* tx/rx buf B (mouse) */
|
|
if (((ctx->port[PORT_B].mode[1] >> MODE_V_CHM) & MODE_M_CHM) == 0x2) { /* Maint */
|
|
ctx->port[PORT_B].buf = data & 0xFF;
|
|
ctx->port[PORT_B].sts |= (STS_RXR | STS_FFL);
|
|
ctx->ists |= ISTS_RBI;
|
|
}
|
|
else {
|
|
if (ctx->port[PORT_B].put_char != NULL)
|
|
ctx->port[PORT_B].put_char ((uint8)data);
|
|
}
|
|
ua2681_update_txi (ctx);
|
|
break;
|
|
|
|
case 13:
|
|
ctx->opcr = data;
|
|
break;
|
|
|
|
case 14:
|
|
ctx->oport |= data;
|
|
ctx->output_port (ua2681_oport (ctx));
|
|
break;
|
|
|
|
case 15:
|
|
ctx->oport &= ~data;
|
|
ctx->output_port (ua2681_oport (ctx));
|
|
break;
|
|
|
|
default: /* NI */
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint32 ua2681_rd(UART2681 *ctx, uint32 rg)
|
|
{
|
|
uint32 data;
|
|
|
|
switch (rg) {
|
|
|
|
case 0: /* mode 1A,2A */
|
|
data = ctx->port[PORT_A].mode[ctx->port[PORT_A].mode_ptr];
|
|
ctx->port[PORT_A].mode_ptr++;
|
|
if (ctx->port[PORT_A].mode_ptr >= 2) ctx->port[PORT_A].mode_ptr = 0;
|
|
break;
|
|
|
|
case 1: /* status/clock A */
|
|
data = ctx->port[PORT_A].sts;
|
|
break;
|
|
|
|
case 3: /* tx/rx buf A */
|
|
data = ctx->port[PORT_A].buf | (ctx->port[PORT_A].sts << 8);
|
|
ctx->port[PORT_A].sts &= ~(STS_RXR | STS_FFL);
|
|
ctx->ists &= ~ISTS_RAI;
|
|
ua2681_update_rxi (ctx);
|
|
break;
|
|
|
|
case 4: /* input port change */
|
|
data = ctx->ipcr;
|
|
ctx->ipcr &= 0x0f;
|
|
ctx->ists &= ~ISTS_IPC;
|
|
ua2681_update_rxi (ctx);
|
|
break;
|
|
|
|
case 5: /* interrupt status/mask */
|
|
data = ctx->ists;
|
|
break;
|
|
|
|
case 8: /* mode 1B,2B */
|
|
data = ctx->port[PORT_B].mode[ctx->port[PORT_B].mode_ptr];
|
|
ctx->port[PORT_B].mode_ptr++;
|
|
if (ctx->port[PORT_B].mode_ptr >= 2) ctx->port[PORT_B].mode_ptr = 0;
|
|
break;
|
|
|
|
case 9: /* status/clock B */
|
|
data = ctx->port[PORT_B].sts;
|
|
break;
|
|
|
|
case 11: /* tx/rx buf B */
|
|
data = ctx->port[PORT_B].buf | (ctx->port[PORT_B].sts << 8);
|
|
ctx->port[PORT_B].sts &= ~(STS_RXR | STS_FFL);
|
|
ctx->ists &= ~ISTS_RBI;
|
|
ua2681_update_rxi (ctx);
|
|
break;
|
|
|
|
case 13: /* input port */
|
|
data = ctx->iport;
|
|
break;
|
|
|
|
default: /* NI */
|
|
data = 0;
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
void ua2681_update_txi (UART2681 *ctx)
|
|
{
|
|
if (ctx->port[PORT_A].cmd & CMD_ETX) { /* Transmitter A enabled? */
|
|
ctx->port[PORT_A].sts |= STS_TXR; /* ready */
|
|
ctx->port[PORT_A].sts |= STS_TXE; /* empty */
|
|
ctx->ists |= ISTS_TAI; /* set int */
|
|
}
|
|
else {
|
|
ctx->port[PORT_A].sts &= ~STS_TXR; /* clear ready */
|
|
ctx->port[PORT_A].sts &= ~STS_TXE; /* clear empty */
|
|
ctx->ists &= ~ISTS_TAI; /* clear int */
|
|
}
|
|
if (ctx->port[PORT_B].cmd & CMD_ETX) { /* Transmitter B enabled? */
|
|
ctx->port[PORT_B].sts |= STS_TXR; /* ready */
|
|
ctx->port[PORT_B].sts |= STS_TXE; /* empty */
|
|
ctx->ists |= ISTS_TBI; /* set int */
|
|
}
|
|
else {
|
|
ctx->port[PORT_B].sts &= ~STS_TXR; /* clear ready */
|
|
ctx->port[PORT_B].sts &= ~STS_TXE; /* clear empty */
|
|
ctx->ists &= ~ISTS_TBI; /* clear int */
|
|
}
|
|
|
|
if ((ctx->ists & ctx->imask) > 0) /* unmasked ints? */
|
|
ctx->set_int (1);
|
|
else ctx->set_int (0);
|
|
|
|
if (ctx->opcr & 0xc0)
|
|
ctx->output_port (ua2681_oport (ctx));
|
|
|
|
}
|
|
|
|
void ua2681_update_rxi (UART2681 *ctx)
|
|
{
|
|
uint8 c;
|
|
t_stat r;
|
|
|
|
if (ctx->port[PORT_A].cmd & CMD_ERX) {
|
|
if (((ctx->port[PORT_A].sts & STS_RXR) == 0) &&
|
|
(ctx->port[PORT_A].get_char != NULL)) {
|
|
r = ctx->port[PORT_A].get_char (&c);
|
|
if (r == SCPE_OK) {
|
|
ctx->port[PORT_A].buf = c;
|
|
ctx->port[PORT_A].sts |= (STS_RXR | STS_FFL);
|
|
ctx->ists |= ISTS_RAI;
|
|
}
|
|
else {
|
|
ctx->port[PORT_A].sts &= ~(STS_RXR | STS_FFL);
|
|
ctx->ists &= ~ISTS_RAI;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ctx->port[PORT_A].sts &= ~(STS_RXR | STS_FFL);
|
|
ctx->ists &= ~ISTS_RAI;
|
|
}
|
|
|
|
if (ctx->port[PORT_B].cmd & CMD_ERX) {
|
|
if (((ctx->port[PORT_B].sts & STS_RXR) == 0) &&
|
|
(ctx->port[PORT_B].get_char != NULL)) {
|
|
r = ctx->port[PORT_B].get_char (&c);
|
|
if (r == SCPE_OK) {
|
|
ctx->port[PORT_B].buf = c;
|
|
ctx->port[PORT_B].sts |= (STS_RXR | STS_FFL);
|
|
ctx->ists |= ISTS_RBI;
|
|
}
|
|
else {
|
|
ctx->port[PORT_B].sts &= ~(STS_RXR | STS_FFL);
|
|
ctx->ists &= ~ISTS_RBI;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ctx->port[PORT_B].sts &= ~(STS_RXR | STS_FFL);
|
|
ctx->ists &= ~ISTS_RBI;
|
|
}
|
|
|
|
if ((ctx->ists & ctx->imask) > 0)
|
|
ctx->set_int (1);
|
|
else ctx->set_int (0);
|
|
|
|
if (ctx->opcr & 0x30)
|
|
ctx->output_port (ua2681_oport (ctx));
|
|
|
|
}
|
|
|
|
uint8 ua2681_oport (UART2681 *ctx)
|
|
{
|
|
uint8 t = ctx->oport;
|
|
|
|
if (ctx->opcr & 0x80) {
|
|
t &= ~0x80;
|
|
if (ctx->ists & ISTS_TBI) t |= 0x80;
|
|
}
|
|
if (ctx->opcr & 0x40) {
|
|
t &= ~0x40;
|
|
if (ctx->ists & ISTS_TAI) t |= 0x40;
|
|
}
|
|
if (ctx->opcr & 0x20) {
|
|
t &= ~0x20;
|
|
if (ctx->ists & ISTS_RBI) t |= 0x20;
|
|
}
|
|
if (ctx->opcr & 0x10) {
|
|
t &= ~0x10;
|
|
if (ctx->ists & ISTS_RAI) t |= 0x10;
|
|
}
|
|
|
|
return t ^ 0xff;
|
|
}
|
|
|
|
/* input ports */
|
|
|
|
void ua2681_ip0_wr (UART2681 *ctx, uint32 set)
|
|
{
|
|
uint8 new_val = (ctx->iport & ~1) | (set ? 1 : 0);
|
|
|
|
if (new_val != ctx->iport) {
|
|
ctx->ipcr &= ~0x0f;
|
|
ctx->ipcr |= (new_val & 0x0f);
|
|
ctx->ipcr |= 0x10;
|
|
if (ctx->acr & 0x01)
|
|
ctx->ists |= ISTS_IPC;
|
|
}
|
|
|
|
ctx->iport = new_val;
|
|
}
|
|
|
|
void ua2681_ip1_wr (UART2681 *ctx, uint32 set)
|
|
{
|
|
uint8 new_val = (ctx->iport & ~2) | (set ? 2 : 0);
|
|
|
|
if (new_val != ctx->iport) {
|
|
ctx->ipcr &= ~0x0f;
|
|
ctx->ipcr |= (new_val & 0x0f);
|
|
ctx->ipcr |= 0x20;
|
|
if (ctx->acr & 0x02)
|
|
ctx->ists |= ISTS_IPC;
|
|
}
|
|
|
|
ctx->iport = new_val;
|
|
}
|
|
|
|
void ua2681_ip2_wr (UART2681 *ctx, uint32 set)
|
|
{
|
|
uint8 new_val = (ctx->iport & ~4) | (set ? 4 : 0);
|
|
|
|
if (new_val != ctx->iport) {
|
|
ctx->ipcr &= ~0x0f;
|
|
ctx->ipcr |= (new_val & 0x0f);
|
|
ctx->ipcr |= 0x40;
|
|
if (ctx->acr & 0x04)
|
|
ctx->ists |= ISTS_IPC;
|
|
}
|
|
|
|
ctx->iport = new_val;
|
|
}
|
|
|
|
void ua2681_ip3_wr (UART2681 *ctx, uint32 set)
|
|
{
|
|
uint8 new_val = (ctx->iport & ~8) | (set ? 8 : 0);
|
|
|
|
if (new_val != ctx->iport) {
|
|
ctx->ipcr &= ~0x0f;
|
|
ctx->ipcr |= (new_val & 0x0f);
|
|
ctx->ipcr |= 0x80;
|
|
if (ctx->acr & 0x08)
|
|
ctx->ists |= ISTS_IPC;
|
|
}
|
|
|
|
ctx->iport = new_val;
|
|
}
|
|
|
|
/**/
|
|
|
|
t_stat ua2681_svc (UART2681 *ctx)
|
|
{
|
|
ua2681_update_rxi (ctx);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ua2681_reset (UART2681 *ctx)
|
|
{
|
|
ctx->ists = 0;
|
|
ctx->imask = 0;
|
|
ctx->iport = ctx->ipcr = 0;
|
|
ctx->oport = 0;
|
|
|
|
ctx->port[PORT_A].sts = 0;
|
|
ctx->port[PORT_A].mode[0] = 0;
|
|
ctx->port[PORT_A].mode[1] = 0;
|
|
ctx->port[PORT_A].mode_ptr = 0;
|
|
ctx->port[PORT_A].buf = 0;
|
|
|
|
ctx->port[PORT_B].sts = 0;
|
|
ctx->port[PORT_B].mode[0] = 0;
|
|
ctx->port[PORT_B].mode[1] = 0;
|
|
ctx->port[PORT_B].mode_ptr = 0;
|
|
ctx->port[PORT_B].buf = 0;
|
|
return SCPE_OK;
|
|
}
|