#include "rs232.h"

#define TRSDOS 0 /* Model 3 TRSDOS 1.3 */
#define LDOS5 1 /* Model 3 LDOS 5.3.1 */
#define LDOS6 0 /* Model 4 LDOS 6.3.1 */

#define MODEM_STATUS     0xe8
#define RESET            0xe8
#define BAUD             0xe9
#define STATUS           0xea
#define CONTROL          0xea
#define RECV_REG         0xeb
#define XMIT_REG         0xeb

#define XMIT_REG_EMPTY   0x40

#define WRINTMASK        0xe0

#define MASK_XMIT_INT    0x10
#define MASK_RECV_INT    0x20
#define MASK_ERR_INT     0x40

#if TRSDOS
#define WRINTMASK_SHADOW 0x4213
#define XMIT_INT_VECTOR  0x4207
#define RECV_INT_VECTOR  0x420a
#define VECTOR_DEFAULT   0x35fa
#endif

#if LDOS5
#define WRINTMASK_SHADOW 0x4474
#define XMIT_INT_VECTOR  0x447d
#define RECV_INT_VECTOR  0x447f
#define VECTOR_DEFAULT   0x44c2
#endif

#if LDOS6
#define WRINTMASK_SHADOW 0x003d
#define XMIT_INT_VECTOR  0x0046
#define RECV_INT_VECTOR  0x0048
#define VECTOR_DEFAULT   0x1c48
#endif

/* Buffer sizes must be <=256 */
#define RECVBUF_N 256
#define XMITBUF_N 256

char rxbuf[RECVBUF_N];
unsigned char rxi = 0, rxo = 0;

char txbuf[XMITBUF_N];
unsigned char txi = 0, txo = 0;

/* rs232 receive interrupt service - regular function because trs80
   interrupt handler in rom stacks the machine state before call. */

void rs232_recvisr()
{
  rxbuf[rxi++] = inport(RECV_REG);
#if RECVBUF_N < 256
  if (rxi == RECVBUF_N) rxi = 0;
#endif
}

/* rs232 transmit interrupt service - regular function because trs80
   interrupt handler in rom stacks the machine state before call.
   If using rom handler, must first test if transmit interrupts are
   really enabled, if not then jump back to rom. */

void rs232_xmitisr()
{
#if TRSDOS
#asm
        LD      B,A                     ;save interrupt request
        LD      A,(4213H)               ;get enabled interrupt mask
;        OUT     (0E0H),A                ;not needed if shadow is handled properly
        AND     10H                     ;test if xmit interrupts enabled
        JR      NZ,$?0                  ;continue xmit isr if enabled
        LD      A,B                     ;restore interrupt request
        JP      35E4H                   ;resume processing in rom
$?0:
#endasm
#endif
  outport(XMIT_REG, txbuf[txo++]);
#if XMITBUF_N < 256
  if (txo == XMITBUF_N) txo = 0;
#endif
  if (txo == txi) outport(WRINTMASK, *(char *)WRINTMASK_SHADOW &= ~MASK_XMIT_INT);
}

/* Get a character from the receive buffer.
   Return 0 if success, -1 if fail. */

char rs232_get(c)
char *c;
{
  if (rxo != rxi) {
    *c=rxbuf[rxo++];
#if RECVBUF_N < 256
    if (rxo == RECVBUF_N) rxo = 0;
#endif
    return 0;
  } else
  return -1;
}

/* Put a character in the transmit buffer with wait if needed. */

void rs232_put(c)
char c;
{
  unsigned char tmptxi;

#asm
        EI
#endasm
  tmptxi = txi;
  txbuf[tmptxi++] = c;
#if XMITBUF_N < 256
  if (tmptxi == XMITBUF_N) tmptxi = 0;
#endif
  while (tmptxi == txo) ;
  /* There are some subtle timing issues compounded by the somewhat
     broken trs80 interrupts.  Better to do the following instructions
     with interrupts disabled. */
#asm
        DI
#endasm
  txi = tmptxi;
  outport(WRINTMASK, *(char *)WRINTMASK_SHADOW |= MASK_XMIT_INT);
#asm
        EI
#endasm
}

/* Initialize the serial port.  Refer to model3 manual or technical
   reference manual for control and baud encodings.  Install interrupt
   vectors and enable interrupts. */

void rs232_init(ctrl, baud)
char ctrl, baud;
{
  outport(RESET, 0);
  outport(CONTROL, ctrl);
  outport(BAUD, baud);
  *(int *) XMIT_INT_VECTOR = rs232_xmitisr;
  *(int *) RECV_INT_VECTOR = rs232_recvisr;
  *(char *) WRINTMASK_SHADOW &= ~(MASK_ERR_INT | MASK_XMIT_INT);
  outport(WRINTMASK, *(char *) WRINTMASK_SHADOW |= MASK_RECV_INT);
}

void rs232_done()
{
  outport(WRINTMASK, *(char *) WRINTMASK_SHADOW &= ~(MASK_ERR_INT | MASK_RECV_INT | MASK_XMIT_INT));
  *(int *) XMIT_INT_VECTOR = VECTOR_DEFAULT;
  *(int *) RECV_INT_VECTOR = VECTOR_DEFAULT;
}
