/* kx10_cty.c: PDP6, KA-10 and KI-10 front end (console terminal) simulator

   Copyright (c) 2013-2020, Richard Cornwell

   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
   RICHARD CORNWELL 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 Richard Cornwell shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Richard Cornwell

*/

#include "kx10_defs.h"
#if PDP6 | KA | KI
#define UNIT_DUMMY      (1 << UNIT_V_UF)

extern int32 tmxr_poll;
t_stat ctyi_svc (UNIT *uptr);
t_stat ctyo_svc (UNIT *uptr);
t_stat cty_reset (DEVICE *dptr);
t_stat cty_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
const char *cty_description (DEVICE *dptr);

/* CTY data structures

   cty_dev       CTY device descriptor
   cty_unit      CTY unit descriptor
   cty_reg       CTY register list
*/
#define TEL_RDY           0010
#define TEL_BSY           0020
#define KEY_RDY           0040
#define KEY_BSY           0100
#define KEY_TST          04000
#define CTY_DEVNUM        0120

#define STATUS            u3
#define DATA              u4
#define PIA               u5

t_stat cty_devio(uint32 dev, uint64 *data);

DIB cty_dib = { CTY_DEVNUM, 1, cty_devio, NULL};

UNIT cty_unit[] = {
    { UDATA (&ctyo_svc, TT_MODE_7B, 0), 10000 },
    { UDATA (&ctyi_svc, TT_MODE_7B|UNIT_IDLE, 0), 0 },
    };


MTAB cty_mod[] = {
    { UNIT_DUMMY, 0, NULL, "STOP", &cty_stop_os },
    { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode },
    { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode },
    { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode },
    { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode },
    { 0 }
    };

REG cty_reg[] = {
    { HRDATAD (WRU, sim_int_char, 8, "interrupt character") },
    { 0 }
    };

DEVICE cty_dev = {
    "CTY", cty_unit, cty_reg, cty_mod,
    2, 10, 31, 1, 8, 8,
    NULL, NULL, &cty_reset,
    NULL, NULL, NULL, &cty_dib, DEV_DEBUG, 0, dev_debug,
    NULL, NULL, &cty_help, NULL, NULL, &cty_description
    };

t_stat cty_devio(uint32 dev, uint64 *data) {
     uint64     res;
     switch(dev & 3) {
     case CONI:
        res = cty_unit[0].PIA | (cty_unit[0].STATUS & (TEL_RDY | TEL_BSY));
        res |= cty_unit[1].STATUS & (KEY_RDY | KEY_BSY);
        res |= cty_unit[0].STATUS & KEY_TST;
        *data = res;
        sim_debug(DEBUG_CONI, &cty_dev, "CTY %03o CONI %06o\n", dev, (uint32)*data);
        break;
     case CONO:
         res = *data;
         cty_unit[0].PIA = res & 07;
         cty_unit[1].PIA = res & 07;
         cty_unit[0].PIA &= ~(KEY_TST);
         cty_unit[0].STATUS &= ~((res >> 4) & (TEL_RDY | TEL_BSY));
         cty_unit[0].STATUS |= (res & (TEL_RDY | TEL_BSY | KEY_TST));
         cty_unit[1].STATUS &= ~((res >> 4) & (KEY_RDY | KEY_BSY));
         cty_unit[1].STATUS |= (res & (KEY_RDY | KEY_BSY));
         if ((cty_unit[0].STATUS & TEL_RDY) || (cty_unit[1].STATUS & KEY_RDY))
             set_interrupt(dev, cty_unit[0].PIA);
         else
             clr_interrupt(dev);
         sim_debug(DEBUG_CONO, &cty_dev, "CTY %03o CONO %06o\n", dev, (uint32)*data);
         break;
     case DATAI:
         res = cty_unit[1].DATA & 0xff;
         cty_unit[1].STATUS &= ~KEY_RDY;
         if ((cty_unit[0].STATUS & TEL_RDY) == 0)
             clr_interrupt(dev);
         *data = res;
         sim_debug(DEBUG_DATAIO, &cty_dev, "CTY %03o DATAI %06o\n", dev, (uint32)*data);
         break;
    case DATAO:
         cty_unit[0].DATA = *data & 0x7f;
         cty_unit[0].STATUS &= ~TEL_RDY;
         cty_unit[0].STATUS |= TEL_BSY;
         if ((cty_unit[1].STATUS & KEY_RDY) == 0)
             clr_interrupt(dev);
         sim_activate(&cty_unit[0], cty_unit[0].wait);
         sim_debug(DEBUG_DATAIO, &cty_dev, "CTY %03o DATAO %06o\n", dev, (uint32)*data);
         break;
    }
    return SCPE_OK;
}



t_stat ctyo_svc (UNIT *uptr)
{
    t_stat  r;
    int32   ch;

    if (uptr->DATA != 0) {
        ch = sim_tt_outcvt ( uptr->DATA, TT_GET_MODE (uptr->flags)) ;
        if ((r = sim_putchar_s (ch)) != SCPE_OK) {   /* output; error? */
            sim_activate (uptr, uptr->wait);               /* try again */
            return ((r == SCPE_STALL)? SCPE_OK: r);        /* !stall? report */
        }
    }
    uptr->STATUS &= ~TEL_BSY;
    uptr->STATUS |= TEL_RDY;
    set_interrupt(CTY_DEVNUM, uptr->PIA);
    return SCPE_OK;
}

t_stat ctyi_svc (UNIT *uptr)
{
    int32 ch;

    sim_clock_coschedule (uptr, tmxr_poll);
                                                       /* continue poll */
    if (uptr->STATUS & KEY_RDY)
        return SCPE_OK;
    if ((ch = sim_poll_kbd ()) < SCPE_KFLAG)           /* no char or error? */
        return ch;
    if (ch & SCPE_BREAK)                               /* ignore break */
        return SCPE_OK;
    uptr->DATA = 0177 & sim_tt_inpcvt(ch, TT_GET_MODE (uptr->flags));
    uptr->DATA = ch & 0177;
    uptr->STATUS |= KEY_RDY;
    set_interrupt(CTY_DEVNUM, uptr->PIA);
    return SCPE_OK;
}

/* Reset */

t_stat cty_reset (DEVICE *dptr)
{
    cty_unit[0].STATUS &= ~(TEL_RDY | TEL_BSY);
    cty_unit[1].STATUS &= ~(KEY_RDY | KEY_BSY);
    clr_interrupt(CTY_DEVNUM);
    sim_clock_coschedule (&cty_unit[1], tmxr_poll);

    return SCPE_OK;
}

/* Stop operating system */

t_stat cty_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
#if ITS
    if (cpu_unit[0].flags & UNIT_ITSPAGE)
        M[037] = FMASK;
    else
#endif
    M[CTY_SWITCH] = 1;                                 /* tell OS to stop */
    return SCPE_OK;
}

t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    cty_unit[0].flags = (cty_unit[0].flags & ~TT_MODE) | val;
    cty_unit[1].flags = (cty_unit[1].flags & ~TT_MODE) | val;
    return SCPE_OK;
}

t_stat cty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "To stop the cpu use the command:\n\n");
fprintf (st, "    sim> SET CTY STOP\n\n");
#if ITS
fprintf (st, "If the CPU is in standard mode, this will write 1 to location\n\n");
fprintf (st, "%03o, causing TOPS10 to stop.  If the CPU is in ITS mode, this\n\n", CTY_SWITCH);
fprintf (st, "will write -1 to location 037, causing ITS to stop.\n\n");
#else
fprintf (st, "This will write a 1 to location %03o, causing TOPS10 to stop\n\n", CTY_SWITCH);
#endif
fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n");
fprintf (st, "  mode  input characters        output characters\n\n");
fprintf (st, "  UC    lower case converted    lower case converted to upper case,\n");
fprintf (st, "        to upper case,          high-order bit cleared,\n");
fprintf (st, "        high-order bit cleared  non-printing characters suppressed\n");
fprintf (st, "  7P    high-order bit cleared  high-order bit cleared,\n");
fprintf (st, "                                non-printing characters suppressed\n");
fprintf (st, "  7B    high-order bit cleared  high-order bit cleared\n");
fprintf (st, "  8B    no changes              no changes\n\n");
fprintf (st, "The default mode is 7P.  In addition, each line can be configured to\n");
fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n");
fprint_reg_help (st, &cty_dev);
return SCPE_OK;
}

const char *cty_description (DEVICE *dptr)
{
    return "Console TTY Line";
}
#endif
