summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/mc6854.c
diff options
context:
space:
mode:
author Curt Coder <curtcoder@mail.com>2013-03-07 15:28:25 +0000
committer Curt Coder <curtcoder@mail.com>2013-03-07 15:28:25 +0000
commit76ac08a0b805820f12be6dac3daee17f8eb26953 (patch)
tree74a1a81d8e2a6cf242d498978b3573015c4d7a0c /src/emu/machine/mc6854.c
parent7e0b63efa9978e8611337c4ac80ffc293bb527ee (diff)
Moved some files to where they belong. (nw)
Diffstat (limited to 'src/emu/machine/mc6854.c')
-rw-r--r--src/emu/machine/mc6854.c1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/src/emu/machine/mc6854.c b/src/emu/machine/mc6854.c
new file mode 100644
index 00000000000..f453647b60b
--- /dev/null
+++ b/src/emu/machine/mc6854.c
@@ -0,0 +1,1072 @@
+/**********************************************************************
+
+ Copyright (C) Antoine Mine' 2006
+
+ Motorola 6854 emulation.
+
+ The MC6854 chip is an Advanced Data-Link Controller (ADLC).
+ It provides a high-level network interface that can transimit frames with
+ arbitrary data and address length, and is compatible with the following
+ standards:
+ - ADCCP (Advanced Data Communication Control Procedure)
+ - HDLC (High-Level Data-Link Control)
+ - SDLC (Synchronous Data-Link Control)
+ It is designed to be interfaced with a M6800-family CPU.
+
+ It is used in the "Nano-network" extension of the Thomson computers to
+ link up to 32 computers at 500 Kbps.
+ Many networks involving one PC server and several MO5 or TO7/70 computers
+ were build in French schools in the 1980's to teach computer science.
+
+ TODO:
+ - CRC
+ - DMA mode
+ - loop mode
+ - status prioritization
+ - NRZI vs. NRZ coding
+ - FD output
+
+**********************************************************************/
+
+
+#include "emu.h"
+#include "mc6854.h"
+
+
+
+/******************* parameters ******************/
+
+
+
+#define VERBOSE 0
+
+
+#define MAX_FRAME_LENGTH 65536
+/* arbitrary value, you may need to enlarge it if you get truncated frames */
+
+#define FIFO_SIZE 3
+/* hardcoded size of the 6854 FIFO (this is a hardware limit) */
+
+#define FLAG 0x7e
+/* flag value, as defined by HDLC protocol: 01111110 */
+
+#define BIT_LENGTH attotime::from_hz( 500000 )
+
+
+
+/******************* internal chip data structure ******************/
+
+
+struct mc6854_t
+{
+ devcb_resolved_write_line out_irq_func;
+ devcb_resolved_read_line in_rxd_func;
+ devcb_resolved_write_line out_txd_func;
+ devcb_resolved_write_line out_rts_func;
+ devcb_resolved_write_line out_dtr_func;
+
+ /* interface */
+ const mc6854_interface* iface;
+
+ /* registers */
+ UINT8 cr1, cr2, cr3, cr4; /* control registers */
+ UINT8 sr1, sr2; /* status registers */
+
+ UINT8 cts, dcd;
+
+ /* transmit state */
+ UINT8 tstate;
+ UINT16 tfifo[FIFO_SIZE]; /* X x 8-bit FIFO + full & last marker bits */
+ UINT8 tones; /* counter for zero-insertion */
+ emu_timer *ttimer; /* when to ask for more data */
+
+ /* receive state */
+ UINT8 rstate;
+ UINT32 rreg; /* shift register */
+ UINT8 rones; /* count '1 bits */
+ UINT8 rsize; /* bits in the shift register */
+ UINT16 rfifo[FIFO_SIZE]; /* X x 8-bit FIFO + full & addr marker bits */
+
+ /* frame-based interface*/
+ UINT8 frame[MAX_FRAME_LENGTH];
+ UINT32 flen, fpos;
+
+};
+
+/* meaning of tstate / rtate:
+ 0 = idle / waiting for frame flag
+ 1 = flag sync
+ 2 = 8-bit address field(s)
+ 3-4 = 8-bit control field(s)
+ 5 = 8-bit logical control field(s)
+ 6 = variable-length data field(s)
+*/
+
+
+
+/******************* utility function and macros ********************/
+
+
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+
+/* control register 1 */
+
+#define AC ( mc6854->cr1 & 1 )
+#define FCTDRA ( mc6854->cr2 & 8 )
+/* extra register select bits */
+
+#define RRESET ( mc6854->cr1 & 0x40 )
+#define TRESET ( mc6854->cr1 & 0x80 )
+/* transmit / reset condition */
+
+#define RIE ( mc6854->cr1 & 2 )
+#define TIE ( mc6854->cr1 & 4 )
+/* interrupt enable */
+
+#define DISCONTINUE ( mc6854->cr1 & 0x20 )
+/* discontinue received frame */
+
+
+
+/* control register 2 */
+
+#define PSE ( mc6854->cr2 & 1 )
+/* prioritize status bits (TODO) */
+
+#define TWOBYTES ( mc6854->cr2 & 2 )
+/* two-bytes mode */
+
+#define FMIDLE ( mc6854->cr2 & 4 )
+/* flag time fill (vs. mark idle) */
+
+#define TLAST ( mc6854->cr2 & 0x10 )
+/* transmit last byte of frame */
+
+#define RTS ( mc6854->cr2 & 0x80 )
+/* request-to-send */
+
+
+
+/* control register 3 */
+
+#define LCF ( mc6854->cr3 & 1 )
+/* logical control field select */
+
+#define CEX ( mc6854->cr3 & 2 )
+/* control field is 16 bits instead of 8 */
+
+#define AEX ( mc6854->cr3 & 4 )
+/* extended address mode (vs normal 8-bit address mode) */
+
+#define IDL0 ( mc6854->cr3 & 8 )
+/* idle condition begins with a '0' instead of a '1" */
+
+#define FDSE ( mc6854->cr3 & 0x10 )
+/* enable the flag detect status in SR1 */
+
+#define LOOP ( mc6854->cr3 & 0x20 )
+/* loop mode */
+
+#define TST ( mc6854->cr3 & 0x40 )
+/* test mode (or go active on poll) */
+
+#define DTR ( mc6854->cr3 & 0x80 )
+/* data-transmit-ready (or loop on-line control) */
+
+
+
+/* control register 4 */
+
+#define TWOINTER ( mc6854->cr4 & 1 )
+/* both an openning and a closing inter-frame are sent */
+
+static const int word_length[4] = { 5, 6, 7, 8 };
+#define TWL word_length[ ( mc6854->cr4 >> 1 ) & 3 ]
+#define RWL word_length[ ( mc6854->cr4 >> 3 ) & 3 ]
+/* transmit / receive word length */
+
+#define ABT ( mc6854->cr4 & 0x20 )
+/* aborts */
+
+#define ABTEX ( mc6854->cr4 & 0x40 )
+/* abort generates 16 '1' bits instead of 8 */
+
+#define NRZ ( mc6854->cr4 & 0x80 )
+/* zero complement / non-zero complement data format */
+
+
+
+/* status register 1 */
+#define RDA 0x01 /* receiver data available */
+#define S2RQ 0x02 /* status register #2 read request */
+#define FD 0x04 /* flag detect */
+#define CTS 0x10 /* clear-to-send */
+#define TU 0x20 /* transmitter underrun */
+#define TDRA 0x40 /* transmitter data register available */
+#define IRQ 0x80 /* interrupt request */
+
+
+/* status register 2 */
+#define AP 0x01 /* address present */
+#define FV 0x02 /* frame valid */
+#define RIDLE 0x04 /* receiver idle */
+#define RABT 0x08 /* receiver abort */
+#define ERR 0x10 /* invalid frame error */
+#define DCD 0x20 /* data carrier detect (ignored) */
+#define OVRN 0x40 /* receiver overrun */
+#define RDA2 0x80 /* copy of RDA */
+
+
+
+INLINE mc6854_t* get_safe_token( device_t *device )
+{
+ assert( device != NULL );
+ assert( device->type() == MC6854 );
+ return (mc6854_t*) downcast<mc6854_device *>(device)->token();
+}
+
+
+/*********************** transmit ***********************/
+
+
+
+/* MC6854 fills bit queue */
+static void mc6854_send_bits( device_t *device, UINT32 data, int len, int zi )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ attotime expire;
+ int i;
+ if ( zi )
+ {
+ /* zero-insertion mode */
+ UINT32 d = 0;
+ int l = 0;
+ for ( i = 0; i < len; i++, data >>= 1, l++ )
+ {
+ if ( data & 1 )
+ {
+ d |= 1 << l;
+ mc6854->tones++;
+ if ( mc6854->tones == 5 )
+ {
+ /* insert a '0' after 5 consecutive '1" */
+ mc6854->tones = 0;
+ l++;
+ }
+ }
+ else
+ mc6854->tones = 0;
+ }
+ data = d;
+ len = l;
+ }
+ else
+ mc6854->tones = 0;
+
+ /* send bits */
+ if ( !mc6854->out_txd_func.isnull() )
+ {
+ for ( i = 0; i < len; i++, data >>= 1 )
+ mc6854->out_txd_func( data & 1 );
+ }
+
+ /* schedule when to ask the MC6854 for more bits */
+ expire = mc6854->ttimer ->remaining( );
+ if ( expire== attotime::never )
+ expire = attotime::zero;
+ mc6854->ttimer->reset( expire + (BIT_LENGTH * len));
+}
+
+
+
+/* CPU push -> tfifo[0] -> ... -> tfifo[FIFO_SIZE-1] -> pop */
+static void mc6854_tfifo_push( device_t *device, UINT8 data )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ int i;
+
+ if ( TRESET )
+ return;
+
+ /* push towards the rightmost free entry */
+ for ( i = FIFO_SIZE - 1; i >= 0; i-- )
+ {
+ if ( ! ( mc6854->tfifo[ i ] & 0x100 ) )
+ break;
+ }
+
+ if ( i >= 0 )
+ mc6854->tfifo[ i ] = data | 0x100;
+ else
+ logerror( "%f mc6854_tfifo_push: FIFO overrun\n", device->machine().time().as_double() );
+
+ /* start frame, if needed */
+ if ( ! mc6854->tstate )
+ {
+ LOG(( "%f mc6854_tfifo_push: start frame\n", device->machine().time().as_double() ));
+ mc6854->tstate = 2;
+ mc6854_send_bits( device, FLAG, 8, 0 );
+ }
+}
+
+
+
+/* CPU asks for normal frame termination */
+static void mc6854_tfifo_terminate( device_t *device )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+
+ /* mark most recently pushed byte as the last one of the frame */
+ int i;
+ for ( i = 0; i < FIFO_SIZE; i++ )
+ {
+ if ( mc6854->tfifo[ i ] & 0x100 )
+ {
+ mc6854->tfifo[ i ] |= 0x200;
+ break;
+ }
+ }
+}
+
+
+
+/* call-back to refill the bit-stream from the FIFO */
+static TIMER_CALLBACK(mc6854_tfifo_cb)
+{
+ device_t* device = (device_t*) ptr;
+ mc6854_t* mc6854 = get_safe_token( device );
+ int i, data = mc6854->tfifo[ FIFO_SIZE - 1 ];
+
+ if ( ! mc6854->tstate )
+ return;
+
+ /* shift FIFO to the right */
+ for ( i = FIFO_SIZE - 1; i > 0; i-- )
+ mc6854->tfifo[ i ] = mc6854->tfifo[ i - 1 ];
+ mc6854->tfifo[ 0 ] = 0;
+
+ if ( data & 0x100 )
+ {
+ /* got data */
+
+ int blen = 8;
+
+ switch ( mc6854->tstate )
+ {
+ case 2: /* 8-bit address field */
+ if ( ( data & 1 ) || ( ! AEX ) )
+ mc6854->tstate = 3;
+ LOG(( "%f mc6854_tfifo_cb: address field $%02X\n", machine.time().as_double(), data & 0xff ));
+ break;
+
+ case 3: /* 8-bit control field */
+ if ( CEX )
+ mc6854->tstate = 4;
+ else if ( LCF )
+ mc6854->tstate = 5;
+ else
+ mc6854->tstate = 6;
+ LOG(( "%f mc6854_tfifo_cb: control field $%02X\n", machine.time().as_double(), data & 0xff ));
+ break;
+
+ case 4: /* 8-bit extended control field (optional) */
+ if ( LCF )
+ mc6854->tstate = 5;
+ else
+ mc6854->tstate = 6;
+ LOG(( "%f mc6854_tfifo_cb: control field $%02X\n", machine.time().as_double(), data & 0xff ));
+ break;
+
+ case 5: /* 8-bit logical control (optional) */
+ if ( ! ( data & 0x80 ) )
+ mc6854->tstate = 6;
+ LOG(( "%f mc6854_tfifo_cb: logical control field $%02X\n", machine.time().as_double(), data & 0xff ));
+ break;
+
+ case 6: /* variable-length data */
+ blen = TWL;
+ LOG(( "%f mc6854_tfifo_cb: data field $%02X, %i bits\n", machine.time().as_double(), data & 0xff, blen ));
+ break;
+
+ default:
+ LOG(( "%f mc6854_tfifo_cb: state=%i\n", machine.time().as_double(), mc6854->tstate));
+ }
+
+ if ( mc6854->flen < MAX_FRAME_LENGTH )
+ mc6854->frame[ mc6854->flen++ ] = data;
+ else
+ logerror( "mc6854_tfifo_cb: truncated frame, max=%i\n", MAX_FRAME_LENGTH );
+
+ mc6854_send_bits( device, data, blen, 1 );
+ }
+ else
+ {
+ /* data underrun => abort */
+ logerror( "%f mc6854_tfifo_cb: FIFO underrun\n", machine.time().as_double() );
+ mc6854->sr1 |= TU;
+ mc6854->tstate = 0;
+ mc6854_send_bits( device, 0xffff, ABTEX ? 16 : 8, 0 );
+ mc6854->flen = 0;
+ }
+
+ /* close frame, if needed */
+ if ( data & 0x200 )
+ {
+ int len = mc6854->flen;
+
+ LOG(( "%f mc6854_tfifo_cb: end frame\n", machine.time().as_double() ));
+ mc6854_send_bits( device, 0xdeadbeef, 16, 1 ); /* send check-sum: TODO */
+ mc6854_send_bits( device, FLAG, 8, 0 ); /* send closing flag */
+
+ if ( mc6854->tfifo[ FIFO_SIZE - 1 ] & 0x100 )
+ {
+ /* re-open frame asap */
+ LOG(( "%f mc6854_tfifo_cb: start frame\n", machine.time().as_double() ));
+ if ( TWOINTER )
+ mc6854_send_bits( device, FLAG, 8, 0 );
+ }
+ else
+ mc6854->tstate = 0;
+
+ mc6854->flen = 0;
+ if ( mc6854->iface->out_frame )
+ mc6854->iface->out_frame( device, mc6854->frame, len );
+ }
+}
+
+
+
+static void mc6854_tfifo_clear( device_t *device )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ memset( mc6854->tfifo, 0, sizeof( mc6854->tfifo ) );
+ mc6854->tstate = 0;
+ mc6854->flen = 0;
+ mc6854->ttimer->reset( );
+}
+
+
+
+/*********************** receive ***********************/
+
+
+
+/* MC6854 pushes a field in the FIFO */
+static void mc6854_rfifo_push( device_t *device, UINT8 d )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ int i, blen = 8;
+ unsigned data = d;
+
+ switch ( mc6854->rstate )
+ {
+ case 0:
+ case 1:
+ case 2: /* 8-bit address field */
+ if ( ( data & 1 ) || ( ! AEX ) )
+ mc6854->rstate = 3;
+ else
+ mc6854->rstate = 2;
+ LOG(( "%f mc6854_rfifo_push: address field $%02X\n", device->machine().time().as_double(), data ));
+ data |= 0x400; /* address marker */
+ break;
+
+ case 3: /* 8-bit control field */
+ if ( CEX )
+ mc6854->rstate = 4;
+ else if ( LCF )
+ mc6854->rstate = 5;
+ else
+ mc6854->rstate = 6;
+ LOG(( "%f mc6854_rfifo_push: control field $%02X\n", device->machine().time().as_double(), data ));
+ break;
+
+ case 4: /* 8-bit extended control field (optional) */
+ if ( LCF )
+ mc6854->rstate = 5;
+ else
+ mc6854->rstate = 6;
+ LOG(( "%f mc6854_rfifo_push: control field $%02X\n", device->machine().time().as_double(), data ));
+ break;
+
+ case 5: /* 8-bit logical control (optional) */
+ if ( ! ( data & 0x80 ) )
+ mc6854->rstate = 6;
+ LOG(( "%f mc6854_rfifo_push: logical control field $%02X\n", device->machine().time().as_double(), data ));
+ break;
+
+ case 6: /* variable-length data */
+ blen = RWL;
+ data >>= 8 - blen;
+ LOG(( "%f mc6854_rfifo_push: data field $%02X, %i bits\n", device->machine().time().as_double(), data, blen ));
+ break;
+ }
+
+ /* no further FIFO fill until FV is cleared! */
+ if ( mc6854->sr2 & FV )
+ {
+ LOG(( "%f mc6854_rfifo_push: field not pushed\n", device->machine().time().as_double() ));
+ return;
+ }
+
+ data |= 0x100; /* entry full marker */
+
+ /* push towards the rightmost free entry */
+ for ( i = FIFO_SIZE - 1; i >= 0; i-- )
+ {
+ if ( ! ( mc6854->rfifo[ i ] & 0x100 ) )
+ break;
+ }
+
+ if ( i >= 0 )
+ mc6854->rfifo[ i ] = data | 0x100;
+ else
+ {
+ /* FIFO full */
+ mc6854->sr2 |= OVRN;
+ mc6854->rfifo[ 0 ] = data;
+ logerror( "%f mc6854_rfifo_push: FIFO overrun\n", device->machine().time().as_double() );
+ }
+
+ mc6854->rsize -= blen;
+}
+
+
+
+static void mc6854_rfifo_terminate( device_t *device )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ /* mark most recently pushed byte as the last one of the frame */
+ int i;
+ for ( i = 0; i < FIFO_SIZE; i++ )
+ {
+ if ( mc6854->rfifo[ i ] & 0x100 )
+ {
+ mc6854->tfifo[ i ] |= 0x200;
+ break;
+ }
+
+ }
+
+ mc6854->flen = 0;
+ mc6854->rstate = 1;
+}
+
+
+
+/* CPU pops the FIFO */
+static UINT8 mc6854_rfifo_pop( device_t *device )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ int i, data = mc6854->rfifo[ FIFO_SIZE - 1 ];
+
+ /* shift FIFO to the right */
+ for ( i = FIFO_SIZE - 1; i > 0; i -- )
+ mc6854->rfifo[ i ] = mc6854->rfifo[ i - 1 ];
+ mc6854->rfifo[ 0 ] = 0;
+
+ if ( mc6854->rfifo[ FIFO_SIZE - 1 ] & 0x200 )
+ {
+ /* last byte in frame */
+ mc6854->sr2 |= FV; /* TODO: check CRC & set ERR instead of FV if error*/
+ }
+
+ /* auto-refill in frame mode */
+ if ( mc6854->flen > 0 )
+ {
+ mc6854_rfifo_push( device, mc6854->frame[ mc6854->fpos++ ] );
+ if ( mc6854->fpos == mc6854->flen )
+ mc6854_rfifo_terminate( device );
+ }
+
+ return data;
+}
+
+
+/* MC6854 makes fields from bits */
+WRITE_LINE_DEVICE_HANDLER( mc6854_set_rx )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ int fieldlen = ( mc6854->rstate < 6 ) ? 8 : RWL;
+
+ if ( RRESET || (mc6854->sr2 & DCD) )
+ return;
+
+ if ( state )
+ {
+ mc6854->rones++;
+ mc6854->rreg = (mc6854->rreg >> 1) | 0x80000000;
+ if ( mc6854->rones >= 8 )
+ {
+ /* abort */
+ mc6854->rstate = 0;
+ mc6854->rsize = 0;
+ if ( mc6854->rstate > 1 )
+ {
+ /* only in-frame abort */
+ mc6854->sr2 |= RABT;
+ LOG(( "%f mc6854_receive_bit: abort\n", device->machine().time().as_double() ));
+ }
+ }
+ else
+ {
+ mc6854->rsize++;
+ if ( mc6854->rstate && mc6854->rsize >= fieldlen + 24 )
+ mc6854_rfifo_push( device, mc6854->rreg );
+ }
+ }
+ else if ( mc6854->rones == 5 )
+ {
+ /* discards '0' inserted after 5 '1' */
+ mc6854->rones = 0;
+ return;
+ }
+ else if ( mc6854->rones == 6 )
+ {
+ /* flag */
+ if ( FDSE )
+ mc6854->sr1 |= FD;
+
+ if ( mc6854->rstate > 1 )
+ {
+ /* end of frame */
+ mc6854->rreg >>= 1;
+ mc6854->rsize++;
+ if ( mc6854->rsize >= fieldlen + 24 ) /* last field */
+ mc6854_rfifo_push( device, mc6854->rreg );
+ mc6854_rfifo_terminate( device );
+ LOG(( "%f mc6854_receive_bit: end of frame\n", device->machine().time().as_double() ));
+ }
+ mc6854->rones = 0;
+ mc6854->rstate = 1;
+ mc6854->rsize = 0;
+ } else
+ {
+ mc6854->rones = 0;
+ mc6854->rreg >>= 1;
+ mc6854->rsize++;
+ if ( mc6854->rstate && mc6854->rsize >= fieldlen + 24 )
+ mc6854_rfifo_push( device, mc6854->rreg );
+ }
+}
+
+
+
+static void mc6854_rfifo_clear( device_t *device )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ memset( mc6854->rfifo, 0, sizeof( mc6854->rfifo ) );
+ mc6854->rstate = 0;
+ mc6854->rreg = 0;
+ mc6854->rsize = 0;
+ mc6854->rones = 0;
+ mc6854->flen = 0;
+}
+
+
+
+int mc6854_send_frame( device_t *device, UINT8* data, int len )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ if ( mc6854->rstate > 1 || mc6854->tstate > 1 || RTS )
+ return -1; /* busy */
+
+ if ( len > MAX_FRAME_LENGTH )
+ {
+ logerror( "mc6854_send_frame: truncated frame, size=%i, max=%i\n", len, MAX_FRAME_LENGTH );
+ len = MAX_FRAME_LENGTH;
+ }
+ else if ( len < 2 )
+ {
+ logerror( "mc6854_send_frame: frame too short, size=%i, min=2\n", len );
+ len = 2;
+ }
+ memcpy( mc6854->frame, data, len );
+ if ( FDSE )
+ mc6854->sr1 |= FD;
+ mc6854->flen = len;
+ mc6854->fpos = 0;
+ mc6854_rfifo_push( device, mc6854->frame[ mc6854->fpos++ ] );
+ mc6854_rfifo_push( device, mc6854->frame[ mc6854->fpos++ ] );
+ if ( mc6854->fpos == mc6854->flen )
+ mc6854_rfifo_terminate( device );
+ return 0;
+}
+
+
+
+/************************** CPU interface ****************************/
+
+
+
+WRITE_LINE_DEVICE_HANDLER( mc6854_set_cts )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ if ( ! mc6854->cts && state )
+ mc6854->sr1 |= CTS;
+ mc6854->cts = state;
+
+ if ( mc6854->cts )
+ mc6854->sr1 |= CTS;
+ else
+ mc6854->sr1 &= ~CTS;
+}
+
+
+
+WRITE_LINE_DEVICE_HANDLER( mc6854_set_dcd )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ if ( ! mc6854->dcd && state )
+ {
+ mc6854->sr2 |= DCD;
+ /* partial reset */
+ mc6854->rstate = 0;
+ mc6854->rreg = 0;
+ mc6854->rsize = 0;
+ mc6854->rones = 0;
+ }
+ mc6854->dcd = state;
+}
+
+
+
+static void mc6854_update_sr2( mc6854_t* mc6854 )
+{
+ /* update RDA */
+ mc6854->sr2 |= RDA2;
+ if ( ! (mc6854->rfifo[ FIFO_SIZE - 1 ] & 0x100) )
+ mc6854->sr2 &= ~RDA2;
+ else if ( TWOBYTES && ! (mc6854->tfifo[ FIFO_SIZE - 2 ] & 0x100) )
+ mc6854->sr2 &= ~RDA2;
+
+ /* update AP */
+ if ( mc6854->rfifo[ FIFO_SIZE - 1 ] & 0x400 )
+ mc6854->sr2 |= AP;
+ else
+ mc6854->sr2 &= ~AP;
+}
+
+
+
+static void mc6854_update_sr1( mc6854_t* mc6854 )
+{
+ mc6854_update_sr2( mc6854 );
+
+ /* update S2RQ */
+ if ( mc6854->sr2 & 0x7f )
+ mc6854->sr1 |= S2RQ;
+ else
+ mc6854->sr1 &= ~S2RQ;
+
+ /* update TRDA (always prioritized by CTS) */
+ if ( TRESET || ( mc6854->sr1 & CTS ) )
+ mc6854->sr1 &= ~TDRA;
+ else
+ {
+ mc6854->sr1 |= TDRA;
+ if ( mc6854->tfifo[ 0 ] & 0x100 )
+ mc6854->sr1 &= ~TDRA;
+ else if ( TWOBYTES && (mc6854->tfifo[ 1 ] & 0x100) )
+ mc6854->sr1 &= ~TDRA;
+ }
+
+ /* update RDA */
+ if ( mc6854->sr2 & RDA2 )
+ mc6854->sr1 |= RDA;
+ else
+ mc6854->sr1 &= ~RDA;
+
+ /* update IRQ */
+ mc6854->sr1 &= ~IRQ;
+ if ( RIE && (mc6854->sr1 & (TU | TDRA) ) )
+ mc6854->sr1 |= IRQ;
+ if ( TIE )
+ {
+ if ( mc6854->sr1 & (S2RQ | RDA | CTS) )
+ mc6854->sr1 |= IRQ;
+ if ( mc6854->sr2 & (ERR | FV | DCD | OVRN | RABT | RIDLE | AP) )
+ mc6854->sr1 |= IRQ;
+ }
+
+ mc6854->out_irq_func((mc6854->sr1 & IRQ) ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+
+READ8_DEVICE_HANDLER ( mc6854_r )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ switch ( offset )
+ {
+ case 0: /* status register 1 */
+ mc6854_update_sr1( mc6854 );
+ LOG(( "%f $%04x mc6854_r: get SR1=$%02X (rda=%i,s2rq=%i,fd=%i,cts=%i,tu=%i,tdra=%i,irq=%i)\n",
+ space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->sr1,
+ ( mc6854->sr1 & RDA) ? 1 : 0, ( mc6854->sr1 & S2RQ) ? 1 : 0,
+ ( mc6854->sr1 & FD ) ? 1 : 0, ( mc6854->sr1 & CTS ) ? 1 : 0,
+ ( mc6854->sr1 & TU ) ? 1 : 0, ( mc6854->sr1 & TDRA) ? 1 : 0,
+ ( mc6854->sr1 & IRQ) ? 1 : 0 ));
+ return mc6854->sr1;
+
+ case 1: /* status register 2 */
+ mc6854_update_sr2( mc6854 );
+ LOG(( "%f $%04x mc6854_r: get SR2=$%02X (ap=%i,fv=%i,ridle=%i,rabt=%i,err=%i,dcd=%i,ovrn=%i,rda2=%i)\n",
+ space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->sr2,
+ ( mc6854->sr2 & AP ) ? 1 : 0, ( mc6854->sr2 & FV ) ? 1 : 0,
+ ( mc6854->sr2 & RIDLE) ? 1 : 0, ( mc6854->sr2 & RABT) ? 1 : 0,
+ ( mc6854->sr2 & ERR ) ? 1 : 0, ( mc6854->sr2 & DCD ) ? 1 : 0,
+ ( mc6854->sr2 & OVRN ) ? 1 : 0, ( mc6854->sr2 & RDA2) ? 1 : 0 ));
+ return mc6854->sr2;
+
+ case 2: /* receiver data register */
+ case 3:
+ {
+ UINT8 data = mc6854_rfifo_pop( device );
+ LOG(( "%f $%04x mc6854_r: get data $%02X\n",
+ space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data ));
+ return data;
+ }
+
+ default:
+ logerror( "$%04x mc6854 invalid read offset %i\n", space.machine().firstcpu->pcbase( ), offset );
+ }
+ return 0;
+}
+
+
+
+WRITE8_DEVICE_HANDLER ( mc6854_w )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ switch ( offset )
+ {
+ case 0: /* control register 1 */
+ mc6854->cr1 = data;
+ LOG(( "%f $%04x mc6854_w: set CR1=$%02X (ac=%i,irq=%c%c,%sreset=%c%c)\n",
+ space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr1,
+ AC ? 1 : 0,
+ RIE ? 'r' : '-', TIE ? 't' : '-',
+ DISCONTINUE ? "discontinue," : "",
+ RRESET ? 'r' : '-', TRESET ? 't' : '-'
+ ));
+ if ( mc6854->cr1 & 0xc )
+ logerror( "$%04x mc6854 DMA not handled (CR1=$%02X)\n",
+ space.machine().firstcpu->pcbase( ), mc6854->cr1 );
+ if ( DISCONTINUE )
+ {
+ /* abort receive FIFO but keeps shift register & synchro */
+ mc6854->rstate = 0;
+ memset( mc6854->rfifo, 0, sizeof( mc6854->rfifo ) );
+ }
+ if ( RRESET )
+ {
+ /* abort FIFO & synchro */
+ mc6854_rfifo_clear( device );
+ mc6854->sr1 &= ~FD;
+ mc6854->sr2 &= ~(AP | FV | RIDLE | RABT | ERR | OVRN | DCD);
+ if ( mc6854->dcd ) mc6854->sr2 |= DCD;
+ }
+ if ( TRESET )
+ {
+ mc6854_tfifo_clear( device );
+ mc6854->sr1 &= ~(TU | TDRA | CTS);
+ if ( mc6854->cts ) mc6854->sr1 |= CTS;
+ }
+ break;
+
+ case 1:
+ if ( AC )
+ {
+ /* control register 3 */
+ mc6854->cr3 = data;
+ LOG(( "%f $%04x mc6854_w: set CR3=$%02X (lcf=%i,aex=%i,idl=%i,fdse=%i,loop=%i,tst=%i,dtr=%i)\n",
+ space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr3,
+ LCF ? (CEX ? 16 : 8) : 0, AEX ? 1 : 0,
+ IDL0 ? 0 : 1, FDSE ? 1 : 0, LOOP ? 1 : 0,
+ TST ? 1 : 0, DTR ? 1 : 0
+ ));
+ if ( LOOP )
+ logerror( "$%04x mc6854 loop mode not handled (CR3=$%02X)\n", space.machine().firstcpu->pcbase( ), mc6854->cr3 );
+ if ( TST )
+ logerror( "$%04x mc6854 test mode not handled (CR3=$%02X)\n", space.machine().firstcpu->pcbase( ), mc6854->cr3 );
+
+ mc6854->out_dtr_func( DTR ? 1 : 0 );
+
+ }
+ else
+ {
+ /* control register 2 */
+ mc6854->cr2 = data;
+ LOG(( "%f $%04x mc6854_w: set CR2=$%02X (pse=%i,bytes=%i,fmidle=%i,%s,tlast=%i,clr=%c%c,rts=%i)\n",
+ space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr2,
+ PSE ? 1 : 0, TWOBYTES ? 2 : 1, FMIDLE ? 1 : 0,
+ FCTDRA ? "fc" : "tdra", TLAST ? 1 : 0,
+ data & 0x20 ? 'r' : '-', data & 0x40 ? 't' : '-',
+ RTS ? 1 : 0 ));
+ if ( PSE )
+ logerror( "$%04x mc6854 status prioritization not handled (CR2=$%02X)\n", space.machine().firstcpu->pcbase( ), mc6854->cr2 );
+ if ( TLAST )
+ mc6854_tfifo_terminate( device );
+ if ( data & 0x20 )
+ {
+ /* clear receiver status */
+ mc6854->sr1 &= ~FD;
+ mc6854->sr2 &= ~(AP | FV | RIDLE | RABT | ERR | OVRN | DCD);
+ if ( mc6854->dcd )
+ mc6854->sr2 |= DCD;
+ }
+ if ( data & 0x40 )
+ {
+ /* clear transmitter status */
+ mc6854->sr1 &= ~(TU | TDRA | CTS);
+ if ( mc6854->cts )
+ mc6854->sr1 |= CTS;
+ }
+
+ mc6854->out_rts_func( RTS ? 1 : 0 );
+ }
+ break;
+
+ case 2: /* transmitter data: continue data */
+ LOG(( "%f $%04xmc6854_w: push data=$%02X\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data ));
+ mc6854_tfifo_push( device, data );
+ break;
+
+ case 3:
+ if ( AC )
+ {
+ /* control register 4 */
+ mc6854->cr4 = data;
+ LOG(( "%f $%04x mc6854_w: set CR4=$%02X (interframe=%i,tlen=%i,rlen=%i,%s%s)\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), mc6854->cr4,
+ TWOINTER ? 2 : 1,
+ TWL, RWL,
+ ABT ? ( ABTEX ? "abort-ext," : "abort,") : "",
+ NRZ ? "nrz" : "nrzi" ));
+ if ( ABT )
+ {
+ mc6854->tstate = 0;
+ mc6854_send_bits( device, 0xffff, ABTEX ? 16 : 8, 0 );
+ mc6854->flen = 0;
+ }
+ }
+ else
+ {
+ /* transmitter data: last data */
+ LOG(( "%f $%04x mc6854_w: push last-data=$%02X\n", space.machine().time().as_double(), space.machine().firstcpu->pcbase( ), data ));
+ mc6854_tfifo_push( device, data );
+ mc6854_tfifo_terminate( device );
+ }
+ break;
+
+ default:
+ logerror( "$%04x mc6854 invalid write offset %i (data=$%02X)\n", space.machine().firstcpu->pcbase( ), offset, data );
+ }
+}
+
+WRITE_LINE_DEVICE_HANDLER( mc6854_rxc_w )
+{
+ // TODO
+}
+
+WRITE_LINE_DEVICE_HANDLER( mc6854_txc_w )
+{
+ // TODO
+}
+
+/************************ reset *****************************/
+
+static DEVICE_RESET( mc6854 )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+ LOG (( "mc6854 reset\n" ));
+ mc6854->cr1 = 0xc0; /* reset condition */
+ mc6854->cr2 = 0;
+ mc6854->cr3 = 0;
+ mc6854->cr4 = 0;
+ mc6854->sr1 = 0;
+ mc6854->sr2 = 0;
+ mc6854->cts = 0;
+ mc6854->dcd = 0;
+ mc6854_tfifo_clear( device );
+ mc6854_rfifo_clear( device );
+}
+
+
+
+/************************ start *****************************/
+
+static DEVICE_START( mc6854 )
+{
+ mc6854_t* mc6854 = get_safe_token( device );
+
+ mc6854->iface = (const mc6854_interface*)device->static_config();
+ mc6854->out_irq_func.resolve(mc6854->iface->out_irq_func, *device);
+ mc6854->in_rxd_func.resolve(mc6854->iface->in_rxd_func, *device);
+ mc6854->out_txd_func.resolve(mc6854->iface->out_txd_func, *device);
+ mc6854->out_rts_func.resolve(mc6854->iface->out_rts_func, *device);
+ mc6854->out_dtr_func.resolve(mc6854->iface->out_dtr_func, *device);
+
+ mc6854->ttimer = device->machine().scheduler().timer_alloc(FUNC(mc6854_tfifo_cb), (void*) device );
+
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr1 );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr2 );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr3 );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cr4 );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->sr1 );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->sr2 );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->cts );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->dcd );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->tstate );
+ state_save_register_item_array( device->machine(), "mc6854", device->tag(), 0, mc6854->tfifo );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->tones );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rstate );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rreg );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rones );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->rsize );
+ state_save_register_item_array( device->machine(), "mc6854", device->tag(), 0, mc6854->rfifo );
+ state_save_register_item_array( device->machine(), "mc6854", device->tag(), 0, mc6854->frame );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->flen );
+ state_save_register_item( device->machine(), "mc6854", device->tag(), 0, mc6854->fpos );
+}
+
+
+
+const device_type MC6854 = &device_creator<mc6854_device>;
+
+mc6854_device::mc6854_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MC6854, "Motorola MC6854 ADLC", tag, owner, clock)
+{
+ m_token = global_alloc_clear(mc6854_t);
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void mc6854_device::device_config_complete()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mc6854_device::device_start()
+{
+ DEVICE_START_NAME( mc6854 )(this);
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mc6854_device::device_reset()
+{
+ DEVICE_RESET_NAME( mc6854 )(this);
+}