/* vax820_ka.c: VAX 8200 CPU

   Copyright (c) 2019, 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.

   This module contains the VAX 8200 CPU registers and devices.

   ka0, ka1         KA820 CPU
*/

#include "vax_defs.h"

#define PCSR_RSTH       0x80000000                      /* restart halt */
#define PCSR_LCON       0x40000000                      /* logical console */
#define PCSR_CONEN      0x20000000                      /* console enable */
#define PCSR_BIRST      0x10000000                      /* BI reset */
#define PCSR_BISTF      0x08000000                      /* self test fast/slow */
#define PCSR_ENAPT      0x04000000                      /* APT connection status */
#define PCSR_STPASS     0x02000000                      /* self test pass */
#define PCSR_RUN        0x01000000                      /* pgm mode run */
#define PCSR_WWPE       0x00800000                      /* write wrong parity, even */
#define PCSR_EVLCK      0x00400000                      /* event lock */
#define PCSR_WMEM       0x00200000                      /* write mem status */
#define PCSR_V_EVENT    16
#define PCSR_M_EVENT    0xF
#define PCSR_EVENT      (PCSR_M_EVENT << PCSR_V_EVENT)  /* BI event */
#define PCSR_WWPO       0x00008000                      /* write wrong parity, odd */
#define PCSR_PER        0x00004000                      /* parity error */
#define PCSR_ENPIPE     0x00002000                      /* enable BI pipeline */
#define PCSR_TIMEOUT    0x00001000                      /* timeout */
#define PCSR_RSVD       0x00000800                      /* reserved */
#define PCSR_CONIE      0x00000400                      /* console interrupt enable */
#define PCSR_CONCLR     0x00000200                      /* clear console interrupt */
#define PCSR_V_CONINT   8
#define PCSR_CONINT     (1u << PCSR_V_CONINT)           /* console interrupt req */
#define PCSR_RXIE       0x00000080                      /* RX50 interrupt enable */
#define PCSR_RXCLR      0x00000040                      /* clear RX50 interrupt */
#define PCSR_RXINT      0x00000020                      /* RX50 interrupt request */
#define PCSR_IPCLR      0x00000010                      /* clear IP interrupt */
#define PCSR_V_IPINT    3
#define PCSR_IPINT      (1u << PCSR_V_IPINT)            /* IP interrupt request */
#define PCSR_CRDEN      0x00000004                      /* enable CRD interrupts */
#define PCSR_CRDCLR     0x00000002                      /* clear CRD interrupt */
#define PCSR_CRDINT     0x00000001                      /* CRD interrupt request */
#define PCSR_WR         (PCSR_RUN | PCSR_WWPE | PCSR_WWPO | \
                         PCSR_ENPIPE | PCSR_CONIE | PCSR_RXIE | \
                         PCSR_CRDEN)
#define PCSR_W1C        (PCSR_EVLCK | PCSR_PER | PCSR_TIMEOUT)

int32 rxcd_count = 0;
char rxcd_ibuf[20];
char rxcd_obuf[20];
int32 rxcd_iptr = 0;
int32 rxcd_optr = 0;
char rxcd_char = '\0';
BIIC ka_biic[KA_NUM];
uint32 ka_rxcd[KA_NUM];
uint32 ka_pcsr[KA_NUM];

extern int32 rxcd_int;
extern int32 ipir;
#if defined (VAX_MP)
extern int32 cur_cpu;
#else
int32 cur_cpu;
#endif

t_stat ka_reset (DEVICE *dptr);
t_stat ka_rdreg (int32 *val, int32 pa, int32 mode);
t_stat ka_wrreg (int32 val, int32 pa, int32 mode);
t_stat ka_svc (UNIT *uptr);

#if defined (VAX_MP)
extern void cpu_setreg (int32 cpu, int32 rg, int32 val);
extern void cpu_start (int32 cpu, uint32 addr);
#endif

/* KAx data structures

   kax_dev      KAx device descriptor
   kax_unit     KAx unit
   kax_reg      KAx register list
*/

DIB ka0_dib[] = { { TR_KA0, 0, &ka_rdreg, &ka_wrreg, 0 } };

UNIT ka0_unit = { UDATA (&ka_svc, 0, 0) };

REG ka0_reg[] = {
    { NULL }
    };

MTAB ka0_mod[] = {
    { MTAB_XTD|MTAB_VDV, TR_KA0, "NEXUS", NULL,
      NULL, &show_nexus },
    { 0 }
    };

DIB ka1_dib[] = { { TR_KA1, 0, &ka_rdreg, &ka_wrreg, 0 } };

UNIT ka1_unit = { UDATA (&ka_svc, 0, 0) };

MTAB ka1_mod[] = {
    { MTAB_XTD|MTAB_VDV, TR_KA1, "NEXUS", NULL,
      NULL, &show_nexus },
    { 0 }  };

REG ka1_reg[] = {
    { NULL }
    };

DEVICE ka_dev[] = {
    {
    "KA0", &ka0_unit, ka0_reg, ka0_mod,
    1, 16, 16, 1, 16, 8,
    NULL, NULL, &ka_reset,
    NULL, NULL, NULL,
    &ka0_dib, DEV_NEXUS
    },
    {
    "KA1", &ka1_unit, ka1_reg, ka1_mod,
    1, 16, 16, 1, 16, 8,
    NULL, NULL, &ka_reset,
    NULL, NULL, NULL,
    &ka1_dib, DEV_NEXUS | DEV_DISABLE | DEV_DIS
    }
    };

/* KA read */

t_stat ka_rdreg (int32 *val, int32 pa, int32 lnt)
{
int32 ka, ofs;

ka = NEXUS_GETNEX (pa) - TR_KA0;                        /* get CPU num */
ofs = NEXUS_GETOFS (pa);                                /* get offset */
switch (ofs) {

    case BI_DTYPE:
        *val = DTYPE_KA820;
        break;

    case BI_CSR:
        *val = ka_biic[ka].csr & BICSR_RD;
        break;

    case BI_BER:
        *val = ka_biic[ka].ber & BIBER_RD;
        break;

    case BI_EICR:
        *val = ka_biic[ka].eicr & BIECR_RD;
        break;

    case BI_IDEST:
        *val = ka_biic[ka].idest & BIID_RD;
        break;

    case BI_SA:
    case BI_EA:
        *val = 0;
        break;

    default:
        return SCPE_NXM;
        }

return SCPE_OK;
}

/* KA write */

t_stat ka_wrreg (int32 val, int32 pa, int32 lnt)
{
int32 ka, ofs;

ka = NEXUS_GETNEX (pa) - TR_KA0;                        /* get CPU num */
ofs = NEXUS_GETOFS (pa);                                /* get offset */
switch (ofs) {

    case BI_CSR:
        ka_biic[ka].csr = (ka_biic[ka].csr & ~BICSR_RW) | (val & BICSR_RW);
        break;

    case BI_BER:
        ka_biic[ka].ber = ka_biic[ka].ber & ~(val & BIBER_W1C);
        break;

    case BI_EICR:
        ka_biic[ka].eicr = (ka_biic[ka].eicr & ~BIECR_RW) | (val & BIECR_RW);
        ka_biic[ka].eicr = ka_biic[ka].eicr & ~(val & BIECR_W1C);
        break;

    case BI_IDEST:
        ka_biic[ka].idest = val & BIID_RW;
        break;

    case BI_IMSK:
        break;

    default:
        return SCPE_NXM;
        }

return SCPE_OK;
}

/* KA reset */

t_stat ka_reset (DEVICE *dptr)
{
int32 i;

rxcd_count = 0;
ka_rxcd[0] = 0;
ka_rxcd[1] = 0;
for (i = 0; i < KA_NUM; i++) {
    ka_biic[i].csr = (1u << BICSR_V_IF) | BICSR_STS | ((TR_KA0 + i) & BICSR_NODE);
    ka_biic[i].ber = 0;
    ka_biic[i].eicr = 0;
    ka_biic[i].idest = 0;
    }
ka_pcsr[0] = PCSR_CONEN | PCSR_ENAPT | PCSR_STPASS | PCSR_RUN;
ka_pcsr[1] = PCSR_RSTH | PCSR_LCON | PCSR_CONEN | PCSR_ENAPT | PCSR_STPASS | PCSR_RUN;
sim_cancel (&ka0_unit);
sim_cancel (&ka1_unit);
return SCPE_OK;
}

t_stat ka_svc (UNIT *uptr)
{
if ((rxcd_count > 0) && rxcd_int)
    sim_activate (uptr, 20);
rxcd_int = 1;
return SCPE_OK;
}

int32 rxcd_rd (void)
{
int32 val;

if (rxcd_count) {                                       /* data available? */
    val = rxcd_obuf[rxcd_optr] | (1 << 8);
    rxcd_optr++;
    rxcd_count--;
    if (rxcd_count)
        sim_activate (&ka0_unit, 20);
    }
else {
    val = 0x52584344;                                   /* "RXCD" */
    mxpr_cc_vc |= CC_V;                                 /* set overflow */
    }

return val;
}

void rxcd_wr (int32 val)
{
int32 cpu = (val >> 8) & 7;
int32 ch = val & 0xFF;
int32 rg;
int32 rval;
t_stat r;
char conv[10];

if (ka_rxcd[cpu] & 0x8000) {                            /* busy? */
    mxpr_cc_vc |= CC_V;                                 /* set overflow */
    return;
    }

switch (ch) {

    case 0x0D:                                          /* CR */
        rxcd_ibuf[rxcd_iptr++] = '\0';                  /* terminator */
        printf (">>> %s\n", &rxcd_ibuf[0]);
        if (rxcd_ibuf[0] == 'D') {                      /* DEPOSIT */
            conv[0] = rxcd_ibuf[4];
            conv[1] = '\0';
            rg = (int32)get_uint (conv, 16, 0xF, &r); /* get register number */
            strlcpy (conv, &rxcd_ibuf[6], 9);
            rval = (int32)get_uint (conv, 16, 0xFFFFFFFF, &r); /* get deposit value */
#if defined (VAX_MP)
            cpu_setreg (cpu, rg, rval);
#endif
            rxcd_count = 3;                             /* ready for next cmd */
            sprintf (&rxcd_obuf[0], ">>>");
            rxcd_optr = 0;
            }
        else if (rxcd_ibuf[0] == 'I') {                 /* INIT */
            rxcd_count = 3;                             /* ready for next cmd */
            sprintf (&rxcd_obuf[0], ">>>");
            rxcd_optr = 0;
            }
        else if (rxcd_ibuf[0] == 'S') {                 /* START */
            strlcpy (conv, &rxcd_ibuf[2], 9);
            rval = (int32)get_uint (conv, 16, 0xFFFFFFFF, &r);
#if defined (VAX_MP)
            cpu_start (cpu, rval);
#endif
            }
        rxcd_iptr = 0;
        break;

    case 0x10:                                          /* CTRL-P */
        rxcd_count = 3;                                 /* ready for next cmd */
        sprintf (&rxcd_obuf[0], ">>>");
        rxcd_optr = 0;
        break;

    default:
        rxcd_count = 1;
        rxcd_obuf[0] = ch;                              /* echo characters back */
        rxcd_optr = 0;
        rxcd_ibuf[rxcd_iptr++] = ch;                    /* store incoming charactes */
        break;
        }
if (rxcd_count) {
    if (cpu == 0)
        sim_activate (&ka0_unit, 20);
    else
        sim_activate (&ka1_unit, 20);
    }
return;
}

int32 pcsr_rd (int32 pa)
{
int32 data;
int32 ip_int = (ipir >> cur_cpu) & 0x1;
data = ka_pcsr[cur_cpu] | (rxcd_int << PCSR_V_CONINT) | (ip_int << PCSR_V_IPINT);
return data;
}

void pcsr_wr (int32 pa, int32 val, int32 lnt)
{
ka_pcsr[cur_cpu] &= ~(val & PCSR_W1C);
ka_pcsr[cur_cpu] &= ~(PCSR_WR) | (val & PCSR_WR);
if (val & PCSR_CONCLR)
    rxcd_int = 0;
if (val & PCSR_IPCLR)
    ipir &= ~(1u << cur_cpu);
}
