blob: 079e283003b95eec5e340affb7ad2bc438aaebd1 [file] [log] [blame] [raw]
/* 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;
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;
ctx->ists |= 0x2;
}
else {
if (ctx->port[PORT_A].put_char != NULL)
ctx->port[PORT_A].put_char ((uint8)data);
}
ua2681_update_txi (ctx);
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;
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;
ctx->ists |= 0x20;
}
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;
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;
ctx->ists &= ~ISTS_RBI;
ua2681_update_rxi (ctx);
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;
ctx->ists |= ISTS_RAI;
}
else {
ctx->port[PORT_A].sts &= ~STS_RXR;
ctx->ists &= ~ISTS_RAI;
}
}
}
else {
ctx->port[PORT_A].sts &= ~STS_RXR;
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;
ctx->ists |= ISTS_RBI;
}
else {
ctx->port[PORT_B].sts &= ~STS_RXR;
ctx->ists &= ~ISTS_RBI;
}
}
}
else {
ctx->port[PORT_B].sts &= ~STS_RXR;
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;
}
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;
}
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;
}
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;
}
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;
}