2474 lines
87 KiB
C
2474 lines
87 KiB
C
/* kl10_fe.c: KL-10 front end (console terminal) simulator
|
||
|
||
Copyright (c) 2019-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"
|
||
#include "sim_sock.h"
|
||
#include "sim_tmxr.h"
|
||
#include <ctype.h>
|
||
|
||
#if KL
|
||
#define UNIT_DUMMY (1 << UNIT_V_UF)
|
||
|
||
#define DTE_DEVNUM 0200
|
||
#define DEV_V_OS (DEV_V_UF + 1) /* Type RSX10/RSX20 */
|
||
#define DEV_M_OS (1 << DEV_V_OS)
|
||
#define TYPE_RSX10 (0 << DEV_V_OS)
|
||
#define TYPE_RSX20 (1 << DEV_V_OS)
|
||
|
||
|
||
|
||
/* DTE10 CONI bits */
|
||
|
||
#define DTE_RM 00100000 /* Restricted mode */
|
||
#define DTE_D11 00040000 /* Dead-11 */
|
||
#define DTE_11DB 00020000 /* TO11 Door bell request */
|
||
#define DTE_10DB 00001000 /* TO10 Door bell request */
|
||
#define DTE_11ER 00000400 /* Error during TO11 transfer */
|
||
#define DTE_11DN 00000100 /* TO 11 transfer done */
|
||
#define DTE_10DN 00000040 /* TO 10 transfer done */
|
||
#define DTE_10ER 00000020 /* Error during TO10 transfer */
|
||
#define DTE_PIE 00000010 /* PIO enabled */
|
||
#define DTE_PIA 00000007 /* PI channel assigment */
|
||
|
||
/* internal flags */
|
||
#define DTE_11RELD 01000000 /* Reload 11. */
|
||
#define DTE_TO11 02000000 /* Transfer to 11 */
|
||
#define DTE_SEC 04000000 /* In secondary protocol */
|
||
#define DTE_IND 010000000 /* Next transfer will be indirect */
|
||
#define DTE_SIND 020000000 /* Send indirect data next */
|
||
|
||
/* DTE CONO bits */
|
||
#define DTE_CO11DB 0020000 /* Set TO11 Door bell */
|
||
#define DTE_CO11CR 0010000 /* Clear reload 11 button */
|
||
#define DTE_CO11SR 0004000 /* Set reload 11 button */
|
||
#define DTE_CO10DB 0001000 /* Clear TO10 Door bell */
|
||
#define DTE_CO11CL 0000100 /* Clear TO11 done and error */
|
||
#define DTE_CO10CL 0000040 /* Clear TO10 done and error */
|
||
#define DTE_PIENB 0000020 /* Load PI and enable bit */
|
||
|
||
/* DTE DATAO */
|
||
#define DTE_TO10IB 010000 /* Interrupt after transfer */
|
||
#define DTE_TO10BC 007777 /* Byte count for transfer */
|
||
|
||
/* Secondary protocol addresses */
|
||
#define SEC_DTFLG 0444 /* Operation complete flag */
|
||
#define SEC_DTCLK 0445 /* Clock interrupt flag */
|
||
#define SEC_DTCI 0446 /* Clock interrupt instruction */
|
||
#define SEC_DTT11 0447 /* 10 to 11 argument */
|
||
#define SEC_DTF11 0450 /* 10 from 11 argument */
|
||
#define SEC_DTCMD 0451 /* To 11 command word */
|
||
#define SEC_DTSEQ 0452 /* Operation sequence number */
|
||
#define SEC_DTOPR 0453 /* Operational DTE # */
|
||
#define SEC_DTCHR 0454 /* Last typed character */
|
||
#define SEC_DTMTD 0455 /* Monitor tty output complete flag */
|
||
#define SEC_DTMTI 0456 /* Monitor tty input flag */
|
||
#define SEC_DTSWR 0457 /* 10 switch register */
|
||
|
||
#define SEC_PGMCTL 00400
|
||
#define SEC_ENDPASS 00404
|
||
#define SEC_LOOKUP 00406
|
||
#define SEC_RDWRD 00407
|
||
#define SEC_RDBYT 00414
|
||
#define SEC_ESEC 00440
|
||
#define SEC_EPRI 00500
|
||
#define SEC_ERTM 00540
|
||
#define SEC_CLKCTL 01000
|
||
#define SEC_CLKOFF 01000
|
||
#define SEC_CLKON 01001
|
||
#define SEC_CLKWT 01002
|
||
#define SEC_CLKRD 01003
|
||
#define SEC_RDSW 01400
|
||
#define SEC_CLRDDT 03000
|
||
#define SEC_SETDDT 03400
|
||
#define SEC_MONO 04000
|
||
#define SEC_MONON 04400
|
||
#define SEC_SETPRI 05000
|
||
#define SEC_RTM 05400
|
||
#define SEC_CMDMSK 07400
|
||
#define DTE_MON 00000001 /* Save in unit1 STATUS */
|
||
#define SEC_CLK 00000002 /* Clock enabled */
|
||
#define ITS_ON 00000004 /* ITS Is alive */
|
||
|
||
/* Primary or Queued protocol addresses */
|
||
#define PRI_CMTW_0 0
|
||
#define PRI_CMTW_PPT 1 /* Pointer to com region */
|
||
#define PRI_CMTW_STS 2 /* Status word */
|
||
#define PRI_CMT_PWF SMASK /* Power failure */
|
||
#define PRI_CMT_L11 BIT1 /* Load 11 */
|
||
#define PRI_CMT_INI BIT2 /* Init */
|
||
#define PRI_CMT_TST BIT3 /* Valid examine bit */
|
||
#define PRI_CMT_QP 020000000LL /* Do Queued protocol */
|
||
#define PRI_CMT_FWD 001000000LL /* Do full word transfers */
|
||
#define PRI_CMT_IP RSIGN /* Indirect transfer */
|
||
#define PRI_CMT_TOT 0200000LL /* TOIT bit */
|
||
#define PRI_CMT_10IC 0177400LL /* TO10 IC for queued transfers */
|
||
#define PRI_CMT_11IC 0000377LL /* TO11 IC for queued transfers */
|
||
#define PRI_CMTW_CNT 3 /* Queue Count */
|
||
#define PRI_CMTW_KAC 5 /* Keep alive count */
|
||
#define PRI_IND_FLG 0100000 /* Flag function as indirect */
|
||
|
||
#define PRI_EM2EI 001 /* Initial message to 11 */
|
||
#define PRI_EM2TI 002 /* Replay to initial message. */
|
||
#define PRI_EMSTR 003 /* String data */
|
||
#define PRI_EMLNC 004 /* Line-Char */
|
||
#define PRI_EMRDS 005 /* Request device status */
|
||
#define PRI_EMOPS 006
|
||
#define PRI_EMHDS 007 /* Here is device status */
|
||
#define PRI_EMRDT 011 /* Request Date/Time */
|
||
#define PRI_EMHDR 012 /* Here is date and time */
|
||
#define PRI_EMFLO 013 /* Flush output */
|
||
#define PRI_EMSNA 014 /* Send all (ttys) */
|
||
#define PRI_EMDSC 015 /* Dataset connect */
|
||
#define PRI_EMHUD 016 /* Hang up dataset */
|
||
#define PRI_EMLBE 017 /* Acknowledge line */
|
||
#define PRI_EMXOF 020 /* XOFF line */
|
||
#define PRI_EMXON 021 /* XON line */
|
||
#define PRI_EMHLS 022 /* Here is line speeds */
|
||
#define PRI_EMHLA 023 /* Here is line allocation */
|
||
#define PRI_EMRBI 024 /* Reboot information */
|
||
#define PRI_EMAKA 025 /* Ack ALL */
|
||
#define PRI_EMTDO 026 /* Turn device On/Off */
|
||
#define PRI_EMEDR 027 /* Enable/Disable line */
|
||
#define PRI_EMLDR 030 /* Load LP RAM */
|
||
#define PRI_EMLDV 031 /* Load LP VFU */
|
||
|
||
#define PRI_EMCTY 001 /* Device code for CTY */
|
||
#define PRI_EMDL1 002 /* DL11 */
|
||
#define PRI_EMDH1 003 /* DH11 #1 */
|
||
#define PRI_EMDLS 004 /* DLS (all ttys combined) */
|
||
#define PRI_EMLPT 005 /* Front end LPT */
|
||
#define PRI_EMCDR 006 /* CDR */
|
||
#define PRI_EMCLK 007 /* Clock */
|
||
#define PRI_EMFED 010 /* Front end device */
|
||
#define PRI_CTYDV 000 /* Line number for CTY */
|
||
#define NUM_DLS 5 /* Number of first DH Line */
|
||
|
||
#if KL_ITS
|
||
/* ITS Timesharing protocol locations */
|
||
#define ITS_DTEVER 0400 /* Protocol version and number of devices */
|
||
#define ITS_DTECHK 0401 /* Increment at 60Hz. Ten setom 2 times per second */
|
||
#define ITS_DTEINP 0402 /* Input from 10 to 11. Line #, Count */
|
||
#define ITS_DTEOUT 0403 /* Output from 10 to 11 Line #, Count */
|
||
#define ITS_DTELSP 0404 /* Line # to set speed of */
|
||
#define ITS_DTELPR 0405 /* Parameter */
|
||
#define ITS_DTEOST 0406 /* Line # to start output on */
|
||
#define ITS_DTETYI 0410 /* Received char (Line #, char) */
|
||
#define ITS_DTEODN 0411 /* Output done (Line #, buffer size) */
|
||
#define ITS_DTEHNG 0412 /* Hangup/dialup */
|
||
#endif
|
||
|
||
extern int32 tmxr_poll;
|
||
t_stat dte_devio(uint32 dev, uint64 *data);
|
||
t_addr dte_devirq(uint32 dev, t_addr addr);
|
||
void dte_second(UNIT *uptr);
|
||
void dte_primary(UNIT *uptr);
|
||
#if KL_ITS
|
||
void dte_its(UNIT *uptr);
|
||
#endif
|
||
void dte_transfer(UNIT *uptr);
|
||
void dte_function(UNIT *uptr);
|
||
void dte_input();
|
||
int dte_start(UNIT *uptr);
|
||
int dte_queue(int func, int dev, int dcnt, uint16 *data);
|
||
t_stat dtei_svc (UNIT *uptr);
|
||
t_stat dte_svc (UNIT *uptr);
|
||
t_stat dteo_svc (UNIT *uptr);
|
||
t_stat dtertc_srv(UNIT * uptr);
|
||
t_stat dte_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat dte_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||
t_stat dte_reset (DEVICE *dptr);
|
||
t_stat dte_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 dte_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
||
const char *dte_description (DEVICE *dptr);
|
||
extern uint64 SW; /* Switch register */
|
||
|
||
CONST char *pri_name[] = { "(0)", "EM2EI", "EM2TI", "EMSTR", "EMLNC", "EMRDS", "(6)",
|
||
"EMHDS", "(10)", "EMRDT", "EMHDR", "EMFLO", "EMSNA", "EMDSC", "EMHUD",
|
||
"EMLBE", "EMXOF", "EMXON", "EMHLS", "EMHLA", "EMRBI", "EMAKA", "EMTDO",
|
||
"EMEDR", "EMLDR", "EMLDV" };
|
||
|
||
#if KL_ITS
|
||
#define QITS (cpu_unit[0].flags & UNIT_ITSPAGE)
|
||
#else
|
||
#define QITS 0
|
||
#endif
|
||
|
||
#define STATUS u3
|
||
#define CNT u4
|
||
|
||
extern uint32 eb_ptr;
|
||
static int32 rtc_tps = 60;
|
||
uint16 rtc_tick;
|
||
uint16 rtc_wait = 0;
|
||
|
||
struct _dte_queue {
|
||
int dptr; /* Pointer to working item */
|
||
uint16 cnt; /* Number of bytes in packet */
|
||
uint16 func; /* Function code */
|
||
uint16 dev; /* Dev code */
|
||
uint16 spare; /* Dev code */
|
||
uint16 dcnt; /* Data count */
|
||
uint16 data[258]; /* Data packet */
|
||
uint16 sdev; /* Secondary device code */
|
||
uint16 sz; /* Byte size */
|
||
} dte_in[32], dte_out[32];
|
||
|
||
int32 dte_in_ptr;
|
||
int32 dte_in_cmd;
|
||
int32 dte_out_ptr;
|
||
int32 dte_out_res;
|
||
int32 dte_base; /* Base */
|
||
int32 dte_off; /* Our offset */
|
||
int32 dte_dt10_off; /* Offset to 10 deposit region */
|
||
int32 dte_et10_off; /* Offset to 10 examine region */
|
||
int32 dte_et11_off; /* Offset to 11 examine region */
|
||
int32 dte_proc_num; /* Our processor number */
|
||
|
||
struct _buffer {
|
||
int in_ptr; /* Insert pointer */
|
||
int out_ptr; /* Remove pointer */
|
||
char buff[256]; /* Buffer */
|
||
} cty_in, cty_out;
|
||
int32 cty_done;
|
||
|
||
#define full(q) ((((q)->in_ptr + 1) & 0xff) == (q)->out_ptr)
|
||
#define empty(q) ((q)->in_ptr == (q)->out_ptr)
|
||
#define not_empty(q) ((q)->in_ptr != (q)->out_ptr)
|
||
#define inco(q) (q)->out_ptr = ((q)->out_ptr + 1) & 0xff
|
||
#define inci(q) (q)->in_ptr = ((q)->in_ptr + 1) & 0xff
|
||
|
||
DIB dte_dib[] = {
|
||
{ DTE_DEVNUM|000, 1, dte_devio, dte_devirq},
|
||
};
|
||
|
||
MTAB dte_mod[] = {
|
||
{ UNIT_DUMMY, 0, NULL, "STOP", &dte_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 },
|
||
{MTAB_XTD|MTAB_VDV, TYPE_RSX10, NULL, "RSX10", &dte_set_type, NULL,
|
||
NULL, "Sets DTE to RSX10 mode"},
|
||
{MTAB_XTD|MTAB_VDV, TYPE_RSX20, "RSX20", "RSX20", &dte_set_type, &dte_show_type,
|
||
NULL, "Sets DTE to RSX20 mode"},
|
||
{ 0 }
|
||
};
|
||
|
||
UNIT dte_unit[] = {
|
||
{ UDATA (&dte_svc, TT_MODE_7B, 0), 100},
|
||
{ UDATA (&dteo_svc, TT_MODE_7B, 0), 100},
|
||
{ UDATA (&dtei_svc, TT_MODE_7B|UNIT_DIS, 0), 1000 },
|
||
{ UDATA (&dtertc_srv, UNIT_IDLE|UNIT_DIS, 0), 1000 }
|
||
};
|
||
|
||
REG dte_reg[] = {
|
||
{SAVEDATA(IN, dte_in) },
|
||
{SAVEDATA(OUT, dte_out) },
|
||
{HRDATA(IN_PTR, dte_in_ptr, 32), REG_HRO},
|
||
{HRDATA(IN_CMD, dte_in_cmd, 32), REG_HRO},
|
||
{HRDATA(OUT_PTR, dte_out_ptr, 32), REG_HRO},
|
||
{HRDATA(OUT_RES, dte_out_res, 32), REG_HRO},
|
||
{HRDATA(BASE, dte_base, 32), REG_HRO},
|
||
{HRDATA(OFF, dte_off, 32), REG_HRO},
|
||
{HRDATA(DTOFF, dte_dt10_off, 32), REG_HRO},
|
||
{HRDATA(ETOFF, dte_et10_off, 32), REG_HRO},
|
||
{HRDATA(E1OFF, dte_et11_off, 32), REG_HRO},
|
||
{HRDATA(PROC, dte_proc_num, 32), REG_HRO},
|
||
{SAVEDATA(CTYIN, cty_in) },
|
||
{SAVEDATA(CTYOUT, cty_out) },
|
||
{HRDATA(DONE, cty_done, 8), REG_HRO},
|
||
{HRDATAD(WRU, sim_int_char, 8, "interrupt character") },
|
||
{ 0 },
|
||
};
|
||
|
||
|
||
DEVICE dte_dev = {
|
||
"CTY", dte_unit, dte_reg, dte_mod,
|
||
4, 10, 31, 1, 8, 8,
|
||
NULL, NULL, &dte_reset,
|
||
NULL, NULL, NULL, &dte_dib, DEV_DEBUG, 0, dev_debug,
|
||
NULL, NULL, &dte_help, NULL, NULL, &dte_description
|
||
};
|
||
|
||
|
||
|
||
#ifndef NUM_DEVS_LP20
|
||
#define NUM_DEVS_LP20 0
|
||
#endif
|
||
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
|
||
#define COL u4
|
||
#define POS u5
|
||
#define LINE u6
|
||
#define LPST us9
|
||
#define LPCNT us10
|
||
|
||
#define EOFFLG 001 /* Tops 20 wants EOF */
|
||
#define HDSFLG 002 /* Tell Tops 20 The current device status */
|
||
#define ACKFLG 004 /* Post an acknowwledge message */
|
||
#define INTFLG 010 /* Send interrupt */
|
||
#define DELFLG 020 /* Previous character was delimiter */
|
||
|
||
#define MARGIN 6
|
||
|
||
#define UNIT_V_CT (UNIT_V_UF + 0)
|
||
#define UNIT_UC (1 << UNIT_V_CT)
|
||
#define UNIT_CT (3 << UNIT_V_CT)
|
||
|
||
|
||
|
||
t_stat lp20_svc (UNIT *uptr);
|
||
t_stat lp20_reset (DEVICE *dptr);
|
||
t_stat lp20_attach (UNIT *uptr, CONST char *cptr);
|
||
t_stat lp20_detach (UNIT *uptr);
|
||
t_stat lp20_setlpp(UNIT *, int32, CONST char *, void *);
|
||
t_stat lp20_getlpp(FILE *, UNIT *, int32, CONST void *);
|
||
t_stat lp20_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
||
const char *cptr);
|
||
const char *lp20_description (DEVICE *dptr);
|
||
|
||
char lp20_buffer[134 * 3];
|
||
|
||
#define LP20_RAM_RAP 010000 /* RAM Parity */
|
||
#define LP20_RAM_INT 04000 /* Interrrupt bit */
|
||
#define LP20_RAM_DEL 02000 /* Delimiter bit */
|
||
#define LP20_RAM_TRN 01000 /* Translation bite */
|
||
#define LP20_RAM_PI 00400 /* Paper Instruction */
|
||
#define LP20_RAM_CHR 00377 /* Character translation */
|
||
|
||
uint16 lp20_vfu[256];
|
||
uint16 lp20_ram[256];
|
||
uint16 lp20_dvfu[] = { /* Default VFU */
|
||
/* 66 line page with 6 line margin */
|
||
00377, /* Line 0 8 7 6 5 4 3 2 1 */
|
||
00220, /* Line 1 8 5 */
|
||
00224, /* Line 2 8 5 3 */
|
||
00230, /* Line 3 8 5 4 */
|
||
00224, /* Line 4 8 5 3 */
|
||
00220, /* Line 5 8 5 */
|
||
00234, /* Line 6 8 5 4 3 */
|
||
00220, /* Line 7 8 5 */
|
||
00224, /* Line 8 8 5 3 */
|
||
00230, /* Line 9 8 5 4 */
|
||
00264, /* Line 10 8 6 5 3 */
|
||
00220, /* Line 11 8 5 */
|
||
00234, /* Line 12 8 5 4 3 */
|
||
00220, /* Line 13 8 5 */
|
||
00224, /* Line 14 8 5 3 */
|
||
00230, /* Line 15 8 5 4 */
|
||
00224, /* Line 16 8 5 3 */
|
||
00220, /* Line 17 8 5 */
|
||
00234, /* Line 18 8 5 4 3 */
|
||
00220, /* Line 19 8 5 */
|
||
00364, /* Line 20 8 7 6 5 3 */
|
||
00230, /* Line 21 8 5 4 */
|
||
00224, /* Line 22 8 5 3 */
|
||
00220, /* Line 23 8 5 */
|
||
00234, /* Line 24 8 5 4 3 */
|
||
00220, /* Line 25 8 5 */
|
||
00224, /* Line 26 8 5 3 */
|
||
00230, /* Line 27 8 5 4 */
|
||
00224, /* Line 28 8 5 3 */
|
||
00220, /* Line 29 8 5 */
|
||
00276, /* Line 30 8 6 5 4 3 2 */
|
||
00220, /* Line 31 8 5 */
|
||
00224, /* Line 32 8 5 3 */
|
||
00230, /* Line 33 8 5 4 */
|
||
00224, /* Line 34 8 5 3 */
|
||
00220, /* Line 35 8 5 */
|
||
00234, /* Line 36 8 5 4 3 */
|
||
00220, /* Line 37 8 5 */
|
||
00224, /* Line 38 8 5 3 */
|
||
00230, /* Line 39 8 5 4 */
|
||
00364, /* Line 40 8 7 6 5 3 */
|
||
00220, /* Line 41 8 5 */
|
||
00234, /* Line 42 8 5 4 3 */
|
||
00220, /* Line 43 8 5 */
|
||
00224, /* Line 44 8 5 3 */
|
||
00230, /* Line 45 8 5 4 */
|
||
00224, /* Line 46 8 5 3 */
|
||
00220, /* Line 47 8 5 */
|
||
00234, /* Line 48 8 5 4 3 */
|
||
00220, /* Line 49 8 5 */
|
||
00264, /* Line 50 8 6 5 3 */
|
||
00230, /* Line 51 8 5 4 */
|
||
00224, /* Line 52 8 5 3 */
|
||
00220, /* Line 53 8 5 */
|
||
00234, /* Line 54 8 5 4 3 */
|
||
00220, /* Line 55 8 5 */
|
||
00224, /* Line 56 8 5 3 */
|
||
00230, /* Line 57 8 5 4 */
|
||
00224, /* Line 58 8 5 3 */
|
||
00220, /* Line 59 8 5 */
|
||
00020, /* Line 60 5 */
|
||
00020, /* Line 61 5 */
|
||
00020, /* Line 62 5 */
|
||
00020, /* Line 63 5 */
|
||
00020, /* Line 64 5 */
|
||
04020, /* Line 65 12 5 */
|
||
010000, /* End of form */
|
||
};
|
||
|
||
struct _buffer lp20_queue;
|
||
|
||
/* LPT data structures
|
||
|
||
lp20_dev LPT device descriptor
|
||
lp20_unit LPT unit descriptor
|
||
lp20_reg LPT register list
|
||
*/
|
||
|
||
UNIT lp20_unit = {
|
||
UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 66), 100
|
||
};
|
||
|
||
REG lp20_reg[] = {
|
||
{BRDATA(BUFFER, lp20_buffer, 16, 8, sizeof(lp20_buffer)), REG_HRO},
|
||
{BRDATA(VFU, lp20_vfu, 16, 16, (sizeof(lp20_vfu)/sizeof(uint16))), REG_HRO},
|
||
{BRDATA(RAM, lp20_ram, 16, 16, (sizeof(lp20_ram)/sizeof(uint16))), REG_HRO},
|
||
{SAVEDATA(QUEUE, lp20_queue) },
|
||
{ NULL }
|
||
};
|
||
|
||
MTAB lp20_mod[] = {
|
||
{UNIT_CT, 0, "Lower case", "LC", NULL},
|
||
{UNIT_CT, UNIT_UC, "Upper case", "UC", NULL},
|
||
{MTAB_XTD|MTAB_VUN|MTAB_VALR, 0, "LINESPERPAGE", "LINESPERPAGE",
|
||
&lp20_setlpp, &lp20_getlpp, NULL, "Number of lines per page"},
|
||
{ 0 }
|
||
};
|
||
|
||
DEVICE lp20_dev = {
|
||
"LP20", &lp20_unit, lp20_reg, lp20_mod,
|
||
1, 10, 31, 1, 8, 8,
|
||
NULL, NULL, &lp20_reset,
|
||
NULL, &lp20_attach, &lp20_detach,
|
||
NULL, DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
|
||
NULL, NULL, &lp20_help, NULL, NULL, &lp20_description
|
||
};
|
||
#endif
|
||
|
||
#ifndef NUM_DEVS_TTY
|
||
#define NUM_DEVS_TTY 0
|
||
#endif
|
||
|
||
#if (NUM_DEVS_TTY > 0)
|
||
|
||
struct _buffer tty_out[NUM_LINES_TTY], tty_in[NUM_LINES_TTY];
|
||
TMLN tty_ldsc[NUM_LINES_TTY] = { 0 }; /* Line descriptors */
|
||
TMXR tty_desc = { NUM_LINES_TTY, 0, 0, tty_ldsc };
|
||
int32 tty_connect[NUM_LINES_TTY];
|
||
int32 tty_done[NUM_LINES_TTY];
|
||
int tty_enable = 0;
|
||
extern int32 tmxr_poll;
|
||
|
||
t_stat ttyi_svc (UNIT *uptr);
|
||
t_stat ttyo_svc (UNIT *uptr);
|
||
t_stat tty_reset (DEVICE *dptr);
|
||
t_stat tty_set_modem (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat tty_show_modem (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||
t_stat tty_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat tty_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat tty_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
||
t_stat tty_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
||
t_stat tty_attach (UNIT *uptr, CONST char *cptr);
|
||
t_stat tty_detach (UNIT *uptr);
|
||
t_stat tty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
||
const char *cptr);
|
||
const char *tty_description (DEVICE *dptr);
|
||
|
||
/* TTY data structures
|
||
|
||
tty_dev TTY device descriptor
|
||
tty_unit TTY unit descriptor
|
||
tty_reg TTY register list
|
||
*/
|
||
|
||
UNIT tty_unit[] = {
|
||
{ UDATA (&ttyi_svc, TT_MODE_7B+UNIT_IDLE+UNIT_DISABLE+UNIT_ATTABLE, 0), KBD_POLL_WAIT},
|
||
{ UDATA (&ttyo_svc, TT_MODE_7B+UNIT_IDLE+UNIT_DIS, 0), KBD_POLL_WAIT},
|
||
};
|
||
|
||
REG tty_reg[] = {
|
||
{ DRDATA (TIME, tty_unit[0].wait, 24), REG_NZ + PV_LEFT },
|
||
{ SAVEDATA (OUT, tty_out) },
|
||
{ SAVEDATA (IN, tty_in) },
|
||
{ BRDATA (CONN, tty_connect, 8, 32, sizeof(tty_connect)/sizeof(int32)), REG_HRO },
|
||
{ BRDATA (DONE, tty_done, 8, 32, sizeof(tty_done)/sizeof(int32)), REG_HRO },
|
||
{ ORDATA (EN, tty_enable, 1), REG_HRO },
|
||
{ 0 }
|
||
};
|
||
|
||
MTAB tty_mod[] = {
|
||
{ TT_MODE, TT_MODE_KSR, "KSR", "KSR", NULL },
|
||
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
|
||
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
|
||
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT",
|
||
&tmxr_dscln, NULL, &tty_desc, "Disconnect a specific line" },
|
||
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
|
||
NULL, &tmxr_show_summ, (void *) &tty_desc, "Display a summary of line states" },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
|
||
NULL, &tmxr_show_cstat, (void *) &tty_desc, "Display current connections" },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
|
||
NULL, &tmxr_show_cstat, (void *) &tty_desc, "Display multiplexer statistics" },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
|
||
&tty_setnl, &tmxr_show_lines, (void *) &tty_desc, "Set number of lines" },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file",
|
||
&tty_set_log, NULL, (void *)&tty_desc },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG",
|
||
&tty_set_nolog, NULL, (void *)&tty_desc, "Disable logging on designated line" },
|
||
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL,
|
||
NULL, &tty_show_log, (void *)&tty_desc, "Display logging for all lines" },
|
||
{ 0 }
|
||
};
|
||
|
||
DEVICE tty_dev = {
|
||
"TTY", tty_unit, tty_reg, tty_mod,
|
||
2, 10, 31, 1, 8, 8,
|
||
&tmxr_ex, &tmxr_dep, &tty_reset,
|
||
NULL, &tty_attach, &tty_detach,
|
||
NULL, DEV_MUX | DEV_DISABLE | DEV_DEBUG, 0, dev_debug,
|
||
NULL, NULL, &tty_help, NULL, NULL, &tty_description
|
||
};
|
||
#endif
|
||
|
||
|
||
t_stat dte_devio(uint32 dev, uint64 *data) {
|
||
uint32 res;
|
||
switch(dev & 3) {
|
||
case CONI:
|
||
*data = (uint64)(dte_unit[0].STATUS) & RMASK;
|
||
sim_debug(DEBUG_CONI, &dte_dev, "CTY %03o CONI %06o\n", dev, (uint32)*data);
|
||
break;
|
||
case CONO:
|
||
res = (uint32)(*data & RMASK);
|
||
clr_interrupt(dev);
|
||
if (res & DTE_PIENB) {
|
||
dte_unit[0].STATUS &= ~(DTE_PIA|DTE_PIE);
|
||
dte_unit[0].STATUS |= res & (DTE_PIA|DTE_PIE);
|
||
}
|
||
if (res & DTE_CO11CL)
|
||
dte_unit[0].STATUS &= ~(DTE_11DN|DTE_11ER);
|
||
if (res & DTE_CO10CL) {
|
||
dte_unit[0].STATUS &= ~(DTE_10DN|DTE_10ER);
|
||
dte_start(&dte_unit[0]);
|
||
}
|
||
if (res & DTE_CO10DB)
|
||
dte_unit[0].STATUS &= ~(DTE_10DB);
|
||
if (res & DTE_CO11CR)
|
||
dte_unit[0].STATUS &= ~(DTE_11RELD);
|
||
if (res & DTE_CO11SR)
|
||
dte_unit[0].STATUS |= (DTE_11RELD);
|
||
if (res & DTE_CO11DB) {
|
||
sim_debug(DEBUG_CONO, &dte_dev, "CTY Ring 11 DB\n");
|
||
dte_unit[0].STATUS |= DTE_11DB;
|
||
sim_activate(&dte_unit[0], 200);
|
||
}
|
||
if (dte_unit[0].STATUS & (DTE_10DB|DTE_11DN|DTE_10DN|DTE_11ER|DTE_10ER))
|
||
set_interrupt(dev, dte_unit[0].STATUS);
|
||
sim_debug(DEBUG_CONO, &dte_dev, "CTY %03o CONO %06o %06o\n", dev,
|
||
(uint32)*data, PC);
|
||
break;
|
||
case DATAI:
|
||
sim_debug(DEBUG_DATAIO, &dte_dev, "CTY %03o DATAI %06o\n", dev,
|
||
(uint32)*data);
|
||
break;
|
||
case DATAO:
|
||
sim_debug(DEBUG_DATAIO, &dte_dev, "CTY %03o DATAO %06o\n", dev,
|
||
(uint32)*data);
|
||
if (*data == 01365) {
|
||
dte_unit[0].STATUS |= DTE_SEC|DTE_10ER;
|
||
dte_unit[0].STATUS &= ~(DTE_10DB|DTE_IND|DTE_11DB);
|
||
break;
|
||
}
|
||
dte_unit[0].CNT = (*data & (DTE_TO10IB|DTE_TO10BC));
|
||
dte_unit[0].STATUS |= DTE_TO11;
|
||
sim_activate(&dte_unit[0], 10);
|
||
break;
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Handle KL style interrupt vectors */
|
||
t_addr
|
||
dte_devirq(uint32 dev, t_addr addr) {
|
||
return 0142;
|
||
}
|
||
|
||
/* Handle TO11 interrupts */
|
||
t_stat dte_svc (UNIT *uptr)
|
||
{
|
||
/* Did the 10 knock? */
|
||
if (uptr->STATUS & DTE_11DB) {
|
||
/* If in secondary mode, do that protocol */
|
||
if (uptr->STATUS & DTE_SEC)
|
||
dte_second(uptr);
|
||
else
|
||
dte_primary(uptr); /* Retrieve data */
|
||
} else if (uptr->STATUS & DTE_TO11) {
|
||
/* Does 10 want us to send it what we have? */
|
||
dte_transfer(uptr);
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Handle secondary protocol */
|
||
void dte_second(UNIT *uptr) {
|
||
uint64 word;
|
||
int32 ch;
|
||
uint32 base = 0;
|
||
|
||
#if KI_22BIT
|
||
#if KL_ITS
|
||
if (!QITS)
|
||
#endif
|
||
base = eb_ptr;
|
||
#endif
|
||
/* read command */
|
||
word = M[SEC_DTCMD + base];
|
||
#if KL_ITS
|
||
if (word == 0 && QITS && (uptr->STATUS & ITS_ON) != 0) {
|
||
dte_its(uptr);
|
||
uptr->STATUS &= ~DTE_11DB;
|
||
return;
|
||
}
|
||
#endif
|
||
/* Do it */
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY secondary %012llo\n", word);
|
||
switch(word & SEC_CMDMSK) {
|
||
default:
|
||
case SEC_MONO: /* Ouput character in monitor mode */
|
||
ch = (int32)(word & 0177);
|
||
if (full(&cty_out)) {
|
||
sim_activate(uptr, 200);
|
||
return;
|
||
}
|
||
if (ch != 0) {
|
||
cty_out.buff[cty_out.in_ptr] = ch & 0x7f;
|
||
inci(&cty_out);
|
||
sim_activate(&dte_unit[1], 200);
|
||
}
|
||
M[SEC_DTCHR + base] = ch;
|
||
M[SEC_DTMTD + base] = FMASK;
|
||
break;
|
||
|
||
case SEC_SETPRI:
|
||
enter_pri:
|
||
if (Mem_examine_word(0, 0, &word))
|
||
break;
|
||
dte_proc_num = (word >> 24) & 037;
|
||
dte_base = dte_proc_num + 1;
|
||
dte_off = dte_base + (word & 0177777);
|
||
dte_dt10_off = 16;
|
||
dte_et10_off = dte_dt10_off + 16;
|
||
dte_et11_off = dte_base + 16;
|
||
uptr->STATUS &= ~DTE_SEC;
|
||
dte_in_ptr = dte_out_ptr = 0;
|
||
dte_in_cmd = dte_out_res = 0;
|
||
cty_done = 0;
|
||
/* Start input process */
|
||
M[SEC_DTCMD + base] = 0;
|
||
M[SEC_DTFLG + base] = FMASK;
|
||
uptr->STATUS &= ~DTE_11DB;
|
||
return;
|
||
|
||
case SEC_SETDDT: /* Read character from console */
|
||
if (empty(&cty_in)) {
|
||
M[SEC_DTF11 + base] = 0;
|
||
M[SEC_DTMTI + base] = FMASK;
|
||
break;
|
||
}
|
||
ch = cty_in.buff[cty_in.out_ptr];
|
||
inco(&cty_in);
|
||
M[SEC_DTF11 + base] = 0177 & ch;
|
||
M[SEC_DTMTI + base] = FMASK;
|
||
break;
|
||
|
||
case SEC_CLRDDT: /* Clear DDT input mode */
|
||
uptr->STATUS &= ~DTE_MON;
|
||
break;
|
||
|
||
case SEC_MONON:
|
||
uptr->STATUS |= DTE_MON;
|
||
break;
|
||
|
||
case SEC_RDSW: /* Read switch register */
|
||
M[SEC_DTSWR + base] = SW;
|
||
M[SEC_DTF11 + base] = SW;
|
||
break;
|
||
|
||
case SEC_PGMCTL: /* Program control: Used by KLDCP */
|
||
switch(word) {
|
||
case SEC_ENDPASS:
|
||
case SEC_LOOKUP:
|
||
case SEC_RDWRD:
|
||
case SEC_RDBYT:
|
||
break;
|
||
case SEC_ESEC:
|
||
goto enter_pri;
|
||
case SEC_EPRI:
|
||
case SEC_ERTM:
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case SEC_CLKCTL: /* Clock control: Used by KLDCP */
|
||
switch(word) {
|
||
case SEC_CLKOFF:
|
||
dte_unit[3].STATUS &= ~SEC_CLK;
|
||
break;
|
||
case SEC_CLKWT:
|
||
rtc_wait = (uint16)(M[SEC_DTT11 + base] & 0177777);
|
||
/* Fall Through */
|
||
|
||
case SEC_CLKON:
|
||
dte_unit[3].STATUS |= SEC_CLK;
|
||
rtc_tick = 0;
|
||
break;
|
||
case SEC_CLKRD:
|
||
M[SEC_DTF11+base] = rtc_tick;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
/* Acknowledge command */
|
||
M[SEC_DTCMD + base] = 0;
|
||
M[SEC_DTFLG + base] = FMASK;
|
||
uptr->STATUS &= ~DTE_11DB;
|
||
if (dte_dev.flags & TYPE_RSX20) {
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, dte_unit[0].STATUS);
|
||
}
|
||
}
|
||
|
||
#if KL_ITS
|
||
/* Process ITS Ioeleven locations */
|
||
void dte_its(UNIT *uptr) {
|
||
uint64 word;
|
||
char ch;
|
||
uint16 data;
|
||
int cnt;
|
||
int ln;
|
||
|
||
/* Check for input Start */
|
||
word = M[ITS_DTEINP];
|
||
if ((word & SMASK) == 0) {
|
||
M[ITS_DTEINP] = FMASK;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEINP = %012llo\n", word);
|
||
}
|
||
/* Check for output Start */
|
||
word = M[ITS_DTEOUT];
|
||
if ((word & SMASK) == 0) {
|
||
cnt = word & 017777;
|
||
ln = ((word >> 18) & 077) - 1;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEOUT = %012llo\n", word);
|
||
while (cnt > 0) {
|
||
if (ln < 0) {
|
||
if (full(&cty_out))
|
||
return;
|
||
if (!Mem_read_byte(0, &data, 1))
|
||
return;
|
||
ch = data & 0177;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY queue %x\n", ch);
|
||
cty_out.buff[cty_out.in_ptr] = ch;
|
||
inci(&cty_out);
|
||
cnt--;
|
||
if (! sim_is_active(&dte_unit[1]))
|
||
sim_activate(&dte_unit[1], 50);
|
||
#if (NUM_DEVS_TTY > 0)
|
||
} else {
|
||
struct _buffer *otty = &tty_out[ln];
|
||
if (full(otty))
|
||
return;
|
||
if (!Mem_read_byte(0, &data, 1))
|
||
return;
|
||
ch = data & 0177;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "TTY queue %x %d\n", ch, ln);
|
||
otty->buff[otty->in_ptr] = ch;
|
||
inci(otty);
|
||
cnt--;
|
||
#endif
|
||
}
|
||
}
|
||
M[ITS_DTEOUT] = FMASK;
|
||
uptr->STATUS |= DTE_11DN;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEOUT = %012llo\n", word);
|
||
}
|
||
/* Check for line speed */
|
||
word = M[ITS_DTELSP];
|
||
if ((word & SMASK) == 0) { /* Ready? */
|
||
M[ITS_DTELSP] = FMASK;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTELSP = %012llo %012llo\n", word, M[ITS_DTELPR]);
|
||
}
|
||
dte_input();
|
||
/* Check for output Start */
|
||
word = M[ITS_DTEOST];
|
||
if ((word & SMASK) == 0) {
|
||
if (word == 0)
|
||
cty_done++;
|
||
#if (NUM_DEVS_TTY > 0)
|
||
else if (word > 0 && word < (uint64)tty_desc.lines) {
|
||
tty_done[word-1] = 1;
|
||
}
|
||
#endif
|
||
M[ITS_DTEOST] = FMASK;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEOST = %012llo\n", word);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
/* Handle primary protocol */
|
||
void dte_primary(UNIT *uptr) {
|
||
uint64 word, iword;
|
||
int s;
|
||
int cnt;
|
||
struct _dte_queue *in;
|
||
uint16 data1, *dp;
|
||
|
||
if ((uptr->STATUS & DTE_11DB) == 0)
|
||
return;
|
||
|
||
/* Check if there is room for another packet */
|
||
if (((dte_in_ptr + 1) & 0x1f) == dte_in_cmd) {
|
||
/* If not reschedule ourselves */
|
||
sim_activate(uptr, 100);
|
||
return;
|
||
}
|
||
uptr->STATUS &= ~(DTE_11DB);
|
||
clr_interrupt(DTE_DEVNUM);
|
||
/* Check status word to see if valid */
|
||
if (Mem_examine_word(0, dte_et11_off + PRI_CMTW_STS, &word)) {
|
||
uint32 base;
|
||
error:
|
||
base = 0;
|
||
#if KI_22BIT
|
||
#if KL_ITS
|
||
if (!QITS)
|
||
#endif
|
||
base = eb_ptr;
|
||
#endif
|
||
/* If we can't read it, go back to secondary */
|
||
M[SEC_DTFLG + base] = FMASK;
|
||
uptr->STATUS |= DTE_SEC;
|
||
uptr->STATUS &= ~DTE_11DB;
|
||
if (dte_dev.flags & TYPE_RSX20) {
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, dte_unit[0].STATUS);
|
||
}
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "DTE: error %012llo\n", word);
|
||
return;
|
||
}
|
||
|
||
if ((word & PRI_CMT_QP) == 0) {
|
||
goto error;
|
||
}
|
||
in = &dte_in[dte_in_ptr];
|
||
/* Check if indirect */
|
||
if ((word & PRI_CMT_IP) != 0) {
|
||
/* Transfer from 10 */
|
||
if ((uptr->STATUS & DTE_IND) == 0) {
|
||
fprintf(stderr, "DTE out of sync\n\r");
|
||
return;
|
||
}
|
||
/* Get size of transfer */
|
||
if (Mem_examine_word(0, dte_et11_off + PRI_CMTW_CNT, &iword))
|
||
goto error;
|
||
sim_debug(DEBUG_EXP, &dte_dev, "DTE: count: %012llo\n", iword);
|
||
in->dcnt = (uint16)(iword & 0177777);
|
||
/* Read in data */
|
||
dp = &in->data[0];
|
||
for (cnt = in->dcnt; cnt > 0; cnt --) {
|
||
/* Read in data */
|
||
s = Mem_read_byte(0, dp, 0);
|
||
if (s == 0)
|
||
goto error;
|
||
in->sz = s;
|
||
sim_debug(DEBUG_DATA, &dte_dev,
|
||
"DTE: Read Idata: %06o %03o %03o %06o cnt=%o\n",
|
||
*dp, *dp >> 8, *dp & 0377,
|
||
((*dp & 0377) << 8) | ((*dp >> 8) & 0377), cnt);
|
||
dp++;
|
||
if (s <= 8)
|
||
cnt--;
|
||
}
|
||
uptr->STATUS &= ~DTE_IND;
|
||
dte_in_ptr = (dte_in_ptr + 1) & 0x1f;
|
||
} else {
|
||
/* Transfer from 10 */
|
||
in->dptr = 0;
|
||
in->dcnt = 0;
|
||
/* Read in count */
|
||
if (!Mem_read_byte(0, &data1, 0))
|
||
goto error;
|
||
in->cnt = data1;
|
||
cnt = in->cnt-2;
|
||
if (!Mem_read_byte(0, &data1, 0))
|
||
goto error;
|
||
in->func = data1;
|
||
cnt -= 2;
|
||
if (!Mem_read_byte(0, &data1, 0))
|
||
goto error;
|
||
in->dev = data1;
|
||
cnt -= 2;
|
||
if (!Mem_read_byte(0, &data1, 0))
|
||
goto error;
|
||
in->spare = data1;
|
||
cnt -= 2;
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: Read CMD: %o c=%o f=%o %s d=%o\n",
|
||
dte_in_ptr, in->cnt, in->func,
|
||
((in->func & 0377) > PRI_EMLDV)?"***":
|
||
pri_name[in->func & 0377], in->dev);
|
||
dp = &in->data[0];
|
||
for (; cnt > 0; cnt -=2) {
|
||
/* Read in data */
|
||
if (!Mem_read_byte(0, dp, 0))
|
||
goto error;
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: Read data: %06o %03o %03o\n",
|
||
*dp, *dp >> 8, *dp & 0377);
|
||
dp++;
|
||
in->dcnt += 2;
|
||
}
|
||
if (in->func & PRI_IND_FLG) {
|
||
uptr->STATUS |= DTE_IND;
|
||
in->dcnt = in->data[0];
|
||
in->sdev = (in->dcnt >> 8) & 0377;
|
||
in->dcnt &= 0377;
|
||
word |= PRI_CMT_TOT;
|
||
if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word))
|
||
goto error;
|
||
} else {
|
||
dte_in_ptr = (dte_in_ptr + 1) & 0x1f;
|
||
}
|
||
}
|
||
word &= ~PRI_CMT_TOT;
|
||
if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word))
|
||
goto error;
|
||
uptr->STATUS |= DTE_11DN;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
dte_function(uptr);
|
||
}
|
||
|
||
/* Process primary protocol packets */
|
||
void
|
||
dte_function(UNIT *uptr)
|
||
{
|
||
uint16 data1[32];
|
||
int32 ch;
|
||
struct _dte_queue *cmd;
|
||
int func;
|
||
int dev;
|
||
|
||
/* Check if queue is empty */
|
||
while (dte_in_cmd != dte_in_ptr) {
|
||
if (((dte_out_res + 1) & 0x1f) == dte_out_ptr) {
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: func out full %d %d\n",
|
||
dte_out_res, dte_out_ptr);
|
||
return;
|
||
}
|
||
cmd = &dte_in[dte_in_cmd];
|
||
dev = cmd->dev & 0377;
|
||
func = cmd->func & 0377;
|
||
sim_debug(DEBUG_DATA, &dte_dev,
|
||
"DTE: func %o %02o %s dev %o cnt %d dcnt %d\n",
|
||
dte_in_cmd, func, (func > PRI_EMLDV) ? "***" : pri_name[func],
|
||
cmd->dev, cmd->dcnt, cmd->dptr );
|
||
switch (func) {
|
||
case PRI_EM2EI: /* Initial message to 11 */
|
||
data1[0] = PRI_CTYDV;
|
||
if (dte_queue(PRI_EM2TI, PRI_EMCTY, 1, data1) == 0)
|
||
return;
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
data1[0] = 140;
|
||
if (dte_queue(PRI_EMHLA, PRI_EMLPT, 1, data1) == 0)
|
||
return;
|
||
#endif
|
||
data1[0] = 0;
|
||
if (dte_queue(PRI_EMAKA, PRI_EMCLK, 1, data1) == 0)
|
||
return;
|
||
break;
|
||
|
||
case PRI_EM2TI: /* Replay to initial message. */
|
||
case PRI_EMLBE: /* Acknowledge line */
|
||
/* Should never get these */
|
||
break;
|
||
|
||
case PRI_EMHDR: /* Here is date and time */
|
||
/* Ignore this function */
|
||
break;
|
||
|
||
case PRI_EMRDT: /* Request Date/Time */
|
||
{
|
||
time_t t = sim_get_time(NULL);
|
||
struct tm *tm = localtime(&t);
|
||
int yr = tm->tm_year + 1900;
|
||
int tim = (((tm->tm_hour * 60) + tm->tm_min) * 60) +
|
||
tm->tm_sec;
|
||
data1[0] = 0177777;
|
||
data1[1] = ((yr & 0377) << 8) | ((yr >> 8) & 0377);
|
||
data1[2] = (tm->tm_mon) + ((tm->tm_mday - 1) << 8);
|
||
data1[3] = (((tm->tm_wday + 6) % 7)) +
|
||
(tm->tm_isdst ? 0200 << 8 : 0);
|
||
tim >>= 1;
|
||
data1[4] = ((tim & 0377) << 8) | ((tim >> 8) & 0377);
|
||
if (dte_queue(PRI_EMHDR | PRI_IND_FLG, PRI_EMCLK, 6, data1) == 0)
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case PRI_EMSTR: /* String data */
|
||
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
/* Handle printer data */
|
||
if (dev == PRI_EMLPT) {
|
||
uptr->LPST &= ~(EOFFLG);
|
||
if (!sim_is_active(&lp20_unit))
|
||
sim_activate(&lp20_unit, 1000);
|
||
while (cmd->dptr < cmd->dcnt) {
|
||
ch = (int32)(cmd->data[cmd->dptr >> 1]);
|
||
if ((cmd->dptr & 1) == 0)
|
||
ch >>= 8;
|
||
ch &= 0177;
|
||
if (full(&lp20_queue))
|
||
return;
|
||
lp20_queue.buff[lp20_queue.in_ptr] = ch;
|
||
inci(&lp20_queue);
|
||
cmd->dptr++;
|
||
}
|
||
if (cmd->dptr != cmd->dcnt)
|
||
return;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "LP20 done\n");
|
||
break;
|
||
}
|
||
#endif
|
||
|
||
/* Handle terminal data */
|
||
if (dev == PRI_EMDLS) {
|
||
int ln = cmd->sdev;
|
||
struct _buffer *otty;
|
||
if (ln == PRI_CTYDV)
|
||
goto cty;
|
||
#if (NUM_DEVS_TTY > 0)
|
||
ln -= NUM_DLS;
|
||
if (ln < 0 || ln >= tty_desc.lines)
|
||
break;
|
||
otty = &tty_out[ln];
|
||
if (cmd->sz > 8)
|
||
cmd->dcnt += cmd->dcnt;
|
||
while (cmd->dptr < cmd->dcnt) {
|
||
ch = (int32)(cmd->data[cmd->dptr >> 1]);
|
||
if ((cmd->dptr & 1) == 0)
|
||
ch >>= 8;
|
||
ch &= 0177;
|
||
if (ch != 0) {
|
||
if (full(otty))
|
||
return;
|
||
otty->buff[otty->in_ptr] = ch;
|
||
inci(otty);
|
||
sim_debug(DEBUG_DATA, &dte_dev, "TTY queue %o %d\n",
|
||
ch, ln);
|
||
}
|
||
cmd->dptr++;
|
||
}
|
||
if (cmd->dptr != cmd->dcnt)
|
||
return;
|
||
#endif
|
||
break;
|
||
}
|
||
|
||
if (dev == PRI_EMCTY) {
|
||
cty:
|
||
sim_activate(&dte_unit[1], 100);
|
||
data1[0] = 0;
|
||
if (cmd->sz > 8)
|
||
cmd->dcnt += cmd->dcnt;
|
||
while (cmd->dptr < cmd->dcnt) {
|
||
ch = (int32)(cmd->data[cmd->dptr >> 1]);
|
||
if ((cmd->dptr & 1) == 0)
|
||
ch >>= 8;
|
||
ch &= 0177;
|
||
if (ch != 0) {
|
||
ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags));
|
||
if (full(&cty_out))
|
||
return;
|
||
cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff);
|
||
inci(&cty_out);
|
||
sim_debug(DEBUG_DATA, &dte_dev,"CTY queue %o\n",ch);
|
||
}
|
||
cmd->dptr++;
|
||
}
|
||
if (cmd->dptr != cmd->dcnt)
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case PRI_EMSNA: /* Send all (ttys) */
|
||
/* Handle terminal data */
|
||
if (dev == PRI_EMDLS || dev == PRI_EMCTY) {
|
||
struct _buffer *otty;
|
||
int ln;
|
||
while (cmd->dptr < cmd->dcnt) {
|
||
ch = (int32)(cmd->data[cmd->dptr >> 1]);
|
||
if ((cmd->dptr & 1) == 0)
|
||
ch >>= 8;
|
||
ch &= 0177;
|
||
if (ch != 0) {
|
||
sim_debug(DEBUG_DATA, &dte_dev, "SNA queue %o\n", ch);
|
||
ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags));
|
||
if (!(full(&cty_out))) {
|
||
cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff);
|
||
inci(&cty_out);
|
||
}
|
||
#if (NUM_DEVS_TTY > 0)
|
||
for(ln = 0; ln <= tty_desc.lines; ln++) {
|
||
otty = &tty_out[ln];
|
||
if (!(full(otty))) {
|
||
otty->buff[otty->in_ptr] = ch;
|
||
inci(otty);
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
cmd->dptr++;
|
||
}
|
||
if (cmd->dptr != cmd->dcnt)
|
||
return;
|
||
data1[0] = 0;
|
||
}
|
||
break;
|
||
|
||
case PRI_EMLNC: /* Line-Char */
|
||
if (dev == PRI_EMDLS) {
|
||
sim_activate(&dte_unit[1], 100);
|
||
while (cmd->dptr < cmd->dcnt) {
|
||
int ln;
|
||
ch = (int32)(cmd->data[cmd->dptr >> 1]);
|
||
ln = (ch >> 8);
|
||
ch &= 0177;
|
||
if (ch != 0 && ln == PRI_CTYDV) {
|
||
ch = sim_tt_outcvt( ch, TT_GET_MODE(uptr->flags));
|
||
cty_out.buff[cty_out.in_ptr] = (char)(ch & 0xff);
|
||
inci(&cty_out);
|
||
if (((cty_out.in_ptr + 1) & 0xff) == cty_out.out_ptr)
|
||
return;
|
||
sim_debug(DEBUG_DATA, &dte_dev, "CTY queue %o\n", ch);
|
||
} else
|
||
if (ch != 0 && ln >= NUM_DLS && ln <= tty_desc.lines) {
|
||
struct _buffer *otty;
|
||
ln -= NUM_DLS;
|
||
otty = &tty_out[ln];
|
||
if (full(otty))
|
||
return;
|
||
otty->buff[otty->in_ptr] = ch;
|
||
inci(otty);
|
||
sim_debug(DEBUG_DATA, &dte_dev, "TTY queue %o %d\n", ch, ln);
|
||
}
|
||
cmd->dptr+=2;
|
||
}
|
||
if (cmd->dptr != cmd->dcnt)
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case PRI_EMOPS:
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
if (dev == PRI_EMLPT) {
|
||
lp20_unit.LINE = 0;
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PRI_EMRDS: /* Request device status */
|
||
if (dev == PRI_EMLPT) {
|
||
if (cmd->data[0] != 0) {
|
||
data1[0] = 2 << 8;
|
||
data1[1] = 0;
|
||
data1[2] = 0;
|
||
if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT,
|
||
3, data1) == 0)
|
||
return;
|
||
} else {
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
lp20_unit.LPST |= HDSFLG;
|
||
if (!sim_is_active(&lp20_unit))
|
||
sim_activate(&lp20_unit, 1000);
|
||
#else
|
||
data1[0] = 2 << 8;
|
||
data1[1] = 0;
|
||
data1[2] = 0;
|
||
if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT,
|
||
3, data1) == 0)
|
||
return;
|
||
#endif
|
||
}
|
||
}
|
||
if (dev == PRI_EMCTY) {
|
||
data1[0] = 0;
|
||
data1[1] = 0;
|
||
if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMCTY,
|
||
3, data1) == 0)
|
||
return;
|
||
}
|
||
if (dev == PRI_EMDH1) {
|
||
data1[0] = 0;
|
||
data1[1] = 0;
|
||
if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMDH1,
|
||
3, data1) == 0)
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case PRI_EMHDS: /* Here is device status */
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
if (dev == PRI_EMLPT) {
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "LPT HDS %06o %06o %06o\n",
|
||
cmd->data[0], cmd->data[1], cmd->data[2]);
|
||
if (cmd->data[0] & 040) {
|
||
lp20_unit.LPST |= EOFFLG;
|
||
lp20_unit.LPCNT = 0;
|
||
}
|
||
lp20_unit.LPST |= HDSFLG;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "LPT HDS %06o \n",
|
||
lp20_unit.LPST);
|
||
if (!sim_is_active(&lp20_unit))
|
||
sim_activate(&lp20_unit, 1000);
|
||
}
|
||
#endif
|
||
break;
|
||
case PRI_EMLDV: /* Load LP VFU */
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
if (dev == PRI_EMLPT) {
|
||
int ln = lp20_unit.LPCNT;
|
||
while (cmd->dptr < cmd->dcnt) {
|
||
uint16 d = cmd->data[cmd->dptr++];
|
||
if (d == (0357 << 8))
|
||
lp20_vfu[ln++] = 010000; /* Signal end of page */
|
||
else
|
||
lp20_vfu[ln++] = ((d >> 8) & 077)|((d <<6) & 07700);
|
||
}
|
||
lp20_unit.LPCNT = ln;
|
||
for (ln = 0; ln < 256; ln++)
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
||
"LP20 VFU %02d => %04o\n", ln, lp20_vfu[ln]);
|
||
data1[0] = 0;
|
||
if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0)
|
||
sim_activate(uptr, 1000);
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PRI_EMLDR: /* Load LP RAM */
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
if (dev == PRI_EMLPT) {
|
||
int ln = lp20_unit.LPCNT;
|
||
for (;cmd->dptr < cmd->dcnt; cmd->dptr++, ln++) {
|
||
if (ln < 256)
|
||
lp20_ram[ln] = cmd->data[cmd->dptr];
|
||
}
|
||
lp20_unit.LPCNT = ln;
|
||
for (ln = 0; ln < 256; ln++)
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
||
"LP20 RAM %02x => %04x\n", ln, lp20_ram[ln]);
|
||
data1[0] = 0;
|
||
if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0)
|
||
sim_activate(uptr, 1000);
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
|
||
case PRI_EMFLO: /* Flush output */
|
||
#if (NUM_DEVS_TTY > 0)
|
||
if (dev == PRI_EMDLS) {
|
||
int ln = cmd->data[0] - NUM_DLS;
|
||
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "Flush out %d %o\n",
|
||
ln, cmd->data[0]);
|
||
if (ln == (NUM_DLS - PRI_CTYDV))
|
||
cty_out.in_ptr = cty_out.out_ptr = 0;
|
||
else
|
||
tty_out[ln].in_ptr = tty_out[ln].out_ptr = 0;
|
||
data1[0] = (ln + NUM_DLS) | (PRI_EMDLS << 8);
|
||
if (dte_queue(PRI_EMLBE, PRI_EMDLS, 1, data1) == 0)
|
||
return;
|
||
}
|
||
#endif
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
if ((cmd->dev & 0377) == PRI_EMLPT) {
|
||
data1[0] = cmd->data[0];
|
||
if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0)
|
||
return;
|
||
}
|
||
#endif
|
||
break;
|
||
|
||
case PRI_EMDSC: /* Dataset connect */
|
||
break;
|
||
|
||
case PRI_EMHUD: /* Hang up dataset */
|
||
#if (NUM_DEVS_TTY > 0)
|
||
if (dev == PRI_EMDLS) {
|
||
int ln = cmd->sdev - NUM_DLS;
|
||
if (ln >= 0) {
|
||
TMLN *lp = &tty_ldsc[ln];
|
||
tmxr_linemsg (lp, "\r\nLine Hangup\r\n");
|
||
tmxr_reset_ln(lp);
|
||
tty_connect[ln] = 0;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PRI_EMXOF: /* XOFF line */
|
||
if (dev == PRI_EMDLS) {
|
||
int ln = cmd->sdev - NUM_DLS;
|
||
if (ln >= 0) {
|
||
tty_ldsc[ln].rcve = 0;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PRI_EMXON: /* XON line */
|
||
if (dev == PRI_EMDLS) {
|
||
int ln = cmd->sdev - NUM_DLS;
|
||
if (ln >= 0) {
|
||
tty_ldsc[ln].rcve = 1;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PRI_EMHLS: /* Here is line speeds */
|
||
if (dev == PRI_EMDLS) {
|
||
int ln = cmd->sdev - NUM_DLS;
|
||
sim_debug(DEBUG_DETAIL, &tty_dev, "HDL %o o=%d i=%d %o\n",
|
||
ln, cmd->data[0], cmd->data[1], cmd->data[2]);
|
||
}
|
||
break;
|
||
|
||
case PRI_EMHLA: /* Here is line allocation */
|
||
case PRI_EMRBI: /* Reboot information */
|
||
case PRI_EMAKA: /* Ack ALL */
|
||
case PRI_EMTDO: /* Turn device On/Off */
|
||
break;
|
||
|
||
case PRI_EMEDR: /* Enable/Disable line */
|
||
if (cmd->dev == PRI_EMDH1) {
|
||
/* Zero means enable, no-zero means disable */
|
||
tty_enable = !((cmd->data[0] >> 8) & 0xff);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY enable %x\n",
|
||
tty_enable);
|
||
if (tty_enable) {
|
||
sim_activate(&tty_unit[0], 1000);
|
||
sim_activate(&tty_unit[1], 1000);
|
||
} else {
|
||
sim_cancel(&tty_unit[0]);
|
||
sim_cancel(&tty_unit[1]);
|
||
}
|
||
}
|
||
break;
|
||
#endif
|
||
default:
|
||
break;
|
||
}
|
||
/* Mark command as finished */
|
||
cmd->cnt = 0;
|
||
dte_in_cmd = (dte_in_cmd + 1) & 0x1F;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Handle primary protocol,
|
||
* Send to 10 when requested.
|
||
*/
|
||
void dte_transfer(UNIT *uptr) {
|
||
uint16 cnt;
|
||
uint16 scnt;
|
||
struct _dte_queue *out;
|
||
uint16 *dp;
|
||
|
||
/* Check if Queue empty */
|
||
if (dte_out_res == dte_out_ptr)
|
||
return;
|
||
|
||
out = &dte_out[dte_out_ptr];
|
||
uptr->STATUS &= ~DTE_TO11;
|
||
clr_interrupt(DTE_DEVNUM);
|
||
|
||
/* Compute how much 10 wants us to send */
|
||
scnt = ((uptr->CNT ^ DTE_TO10BC) + 1) & DTE_TO10BC;
|
||
/* Check if indirect */
|
||
if ((uptr->STATUS & DTE_SIND) != 0) {
|
||
/* Transfer indirect */
|
||
cnt = out->dcnt;
|
||
dp = &out->data[0];
|
||
if (cnt > scnt) /* Only send as much as we are allowed */
|
||
cnt = scnt;
|
||
for (; cnt > 0; cnt -= 2) {
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: Send Idata: %06o %03o %03o\n",
|
||
*dp, *dp >> 8, *dp & 0377);
|
||
if (Mem_write_byte(0, dp) == 0)
|
||
goto error;
|
||
dp++;
|
||
}
|
||
uptr->STATUS &= ~DTE_SIND;
|
||
} else {
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d send CMD: [%o] %o %o %o\n",
|
||
dte_out_ptr, dte_out_res, scnt, out->cnt, out->func, out->dev);
|
||
/* Get size of packet */
|
||
cnt = out->cnt;
|
||
if ((out->func & PRI_IND_FLG) == 0)
|
||
cnt += out->dcnt;
|
||
/* If it will not fit, request indirect */
|
||
if (cnt > scnt) { /* If not enough space request indirect */
|
||
out->func |= PRI_IND_FLG;
|
||
cnt = scnt;
|
||
}
|
||
/* Write out header */
|
||
if (!Mem_write_byte(0, &cnt))
|
||
goto error;
|
||
if (!Mem_write_byte(0, &out->func))
|
||
goto error;
|
||
cnt -= 2;
|
||
if (!Mem_write_byte(0, &out->dev))
|
||
goto error;
|
||
cnt -= 2;
|
||
if (!Mem_write_byte(0, &out->spare))
|
||
goto error;
|
||
cnt -= 2;
|
||
if (out->func & PRI_IND_FLG) {
|
||
uint16 dwrd = out->dcnt;
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: Indirect %o %o\n", cnt,
|
||
out->dcnt);
|
||
dwrd |= (out->sdev << 8);
|
||
if (!Mem_write_byte(0, &dwrd))
|
||
goto error;
|
||
uptr->STATUS |= DTE_SIND;
|
||
goto done;
|
||
}
|
||
cnt -= 2;
|
||
dp = &out->data[0];
|
||
for (; cnt > 0; cnt -= 2) {
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: Send data: %06o %03o %03o\n",
|
||
*dp, *dp >> 8, *dp & 0377);
|
||
if (!Mem_write_byte(0, dp))
|
||
goto error;
|
||
dp++;
|
||
}
|
||
}
|
||
out->cnt = 0;
|
||
dte_out_ptr = (dte_out_ptr + 1) & 0x1f;
|
||
done:
|
||
uptr->STATUS |= DTE_10DN;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
error:
|
||
return;
|
||
}
|
||
|
||
/* Process input from CTY and TTY's to 10. */
|
||
void
|
||
dte_input()
|
||
{
|
||
uint16 data1;
|
||
uint16 dataq[32];
|
||
int n;
|
||
int ln;
|
||
int save_ptr;
|
||
char ch;
|
||
UNIT *uptr = &dte_unit[0];
|
||
|
||
#if KL_ITS
|
||
if (QITS && (uptr->STATUS & ITS_ON) != 0) {
|
||
uint64 word;
|
||
word = M[ITS_DTEODN];
|
||
/* Check if ready for output done */
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEODN = %012llo %d\n", word,
|
||
cty_done);
|
||
if ((word & SMASK) != 0) {
|
||
if (cty_done) {
|
||
word = 64LL;
|
||
cty_done--;
|
||
#if (NUM_DEVS_TTY > 0)
|
||
} else {
|
||
for (ln = 0; ln < tty_desc.lines; ln++) {
|
||
if (tty_done[ln]) {
|
||
word = (((uint64)ln + 1) << 18);
|
||
word |=(tty_connect[ln])? 64: 1;
|
||
tty_done[ln] = 0;
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
if ((word & SMASK) == 0) {
|
||
M[ITS_DTEODN] = word;
|
||
/* Tell 10 something is ready */
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEODN = %012llo\n",
|
||
word);
|
||
}
|
||
}
|
||
/* Check if ready for any input */
|
||
word = M[ITS_DTETYI];
|
||
if ((word & SMASK) != 0) {
|
||
/* CTY first. */
|
||
if (not_empty(&cty_in)) {
|
||
ch = cty_in.buff[cty_in.out_ptr];
|
||
inco(&cty_in);
|
||
word = (uint64)ch;
|
||
#if (NUM_DEVS_TTY > 0)
|
||
} else {
|
||
ln = uptr->CNT;
|
||
while ((word & SMASK) != 0) {
|
||
if (not_empty(&tty_in[ln])) {
|
||
ch = tty_in[ln].buff[tty_in[ln].out_ptr];
|
||
inco(&tty_in[ln]);
|
||
word = ((uint64)(ln+1) << 18) | (uint64)ch;
|
||
}
|
||
ln++;
|
||
if (ln >= tty_desc.lines)
|
||
ln = 0;
|
||
if (ln == uptr->CNT)
|
||
break;
|
||
}
|
||
uptr->CNT = ln;
|
||
#endif
|
||
}
|
||
if ((word & SMASK) == 0) {
|
||
M[ITS_DTETYI] = word;
|
||
/* Tell 10 something is ready */
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTETYI = %012llo\n",
|
||
word);
|
||
}
|
||
}
|
||
#if (NUM_DEVS_TTY > 0)
|
||
/* Check ready for hang up message */
|
||
word = M[ITS_DTEHNG];
|
||
if ((word & SMASK) != 0) {
|
||
for (ln = 0; ln < tty_desc.lines; ln++) {
|
||
if (tty_connect[ln] != tty_ldsc[ln].conn) {
|
||
if (tty_ldsc[ln].conn)
|
||
word = 015500 + ln + 1;
|
||
else
|
||
word = ln + 1;
|
||
tty_connect[ln] = tty_ldsc[ln].conn;
|
||
tty_done[ln] = tty_ldsc[ln].conn;
|
||
break;
|
||
}
|
||
}
|
||
/* Tell 10 something is ready */
|
||
if ((word & SMASK) == 0) {
|
||
M[ITS_DTEHNG] = word;
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS DTEHNG = %012llo\n",
|
||
word);
|
||
}
|
||
}
|
||
#endif
|
||
} else
|
||
#endif
|
||
if ((uptr->STATUS & DTE_SEC) == 0) {
|
||
/* Check if CTY done with input */
|
||
if (cty_done) {
|
||
data1 = PRI_CTYDV;
|
||
if (dte_queue(PRI_EMLBE, PRI_EMDLS, 1, &data1) == 0)
|
||
return;
|
||
cty_done--;
|
||
}
|
||
/* Grab a chunck of input from CTY if any */
|
||
n = 0;
|
||
save_ptr = cty_in.out_ptr;
|
||
while (not_empty(&cty_in) && n < 32) {
|
||
ch = cty_in.buff[cty_in.out_ptr];
|
||
inco(&cty_in);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY recieve %02x\n", ch);
|
||
dataq[n++] = (PRI_CTYDV << 8) | ch;
|
||
}
|
||
if (n > 0 && dte_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0) {
|
||
/* Restore the input pointer */
|
||
cty_in.out_ptr = save_ptr;
|
||
return;
|
||
}
|
||
#if (NUM_DEVS_TTY > 0)
|
||
n = 0;
|
||
/* While we have room for one more packet,
|
||
* grab as much input as we can */
|
||
for (ln = 0; ln < tty_desc.lines &&
|
||
((dte_out_res + 1) & 0x1f) != dte_out_ptr; ln++) {
|
||
struct _buffer *itty = &tty_in[ln];
|
||
while (not_empty(itty)) {
|
||
ch = itty->buff[itty->out_ptr];
|
||
inco(itty);
|
||
dataq[n++] = ((ln + NUM_DLS) << 8) | ch;
|
||
if (n == 32) {
|
||
if (dte_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0)
|
||
return;
|
||
n = 0;
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
if (n > 0 && dte_queue(PRI_EMLNC, PRI_EMDLS, n, dataq) == 0)
|
||
return;
|
||
n = 0;
|
||
for (ln = 0; ln < tty_desc.lines; ln++) {
|
||
data1 = (ln + NUM_DLS) | (PRI_EMDLS << 8);
|
||
if (tty_connect[ln] != tty_ldsc[ln].conn) {
|
||
if (tty_ldsc[ln].conn)
|
||
n = PRI_EMDSC;
|
||
else
|
||
n = PRI_EMHUD;
|
||
if (dte_queue(n, PRI_EMDLS, 1, &data1) == 0)
|
||
return;
|
||
tty_connect[ln] = tty_ldsc[ln].conn;
|
||
}
|
||
if (tty_done[ln]) {
|
||
if (dte_queue(PRI_EMLBE, PRI_EMDLS, 1, &data1) == 0)
|
||
return;
|
||
tty_done[ln] = 0;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Queue up a packet to send to 10.
|
||
*/
|
||
int
|
||
dte_queue(int func, int dev, int dcnt, uint16 *data)
|
||
{
|
||
uint16 *dp;
|
||
struct _dte_queue *out;
|
||
|
||
/* Check if room in queue for this packet. */
|
||
if (((dte_out_res + 1) & 0x1f) == dte_out_ptr) {
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d out full\n", dte_out_res, dte_out_ptr);
|
||
return 0;
|
||
}
|
||
out = &dte_out[dte_out_res];
|
||
out->cnt = 10;
|
||
out->func = func;
|
||
out->dev = dev;
|
||
out->dcnt = (dcnt-1)*2;
|
||
out->spare = 0;
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: %d %d queue resp: %o (%o) f=%o %s d=%o\n",
|
||
dte_out_ptr, dte_out_res, out->cnt, out->dcnt, out->func,
|
||
(out->func > PRI_EMLDV)? "***":pri_name[out->func], out->dev);
|
||
for (dp = &out->data[0]; dcnt > 0; dcnt--) {
|
||
*dp++ = *data++;
|
||
}
|
||
/* Advance pointer to next function */
|
||
dte_out_res = (dte_out_res + 1) & 0x1f;
|
||
return 1;
|
||
}
|
||
|
||
|
||
/*
|
||
* If anything in queue, start a transfer, if one is not already
|
||
* pending.
|
||
*/
|
||
int
|
||
dte_start(UNIT *uptr)
|
||
{
|
||
uint64 word;
|
||
int dcnt;
|
||
|
||
/* Check if queue empty */
|
||
if (dte_out_ptr == dte_out_res)
|
||
return 1;
|
||
|
||
/* If there is interrupt pending, just return */
|
||
if ((uptr->STATUS & (DTE_IND|DTE_10DB|DTE_11DB)) != 0)
|
||
return 1;
|
||
if (Mem_examine_word(0, dte_et11_off + PRI_CMTW_STS, &word)) {
|
||
error:
|
||
/* If we can't read it, go back to secondary */
|
||
uptr->STATUS |= DTE_SEC|DTE_10ER;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
return 0;
|
||
}
|
||
/* Bump count of messages sent */
|
||
word = (word & ~(PRI_CMT_10IC|PRI_CMT_IP)) | ((word + 0400) & PRI_CMT_10IC);
|
||
word &= ~PRI_CMT_FWD;
|
||
if ((uptr->STATUS & DTE_SIND) != 0)
|
||
word |= PRI_CMT_IP;
|
||
if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_STS, &word))
|
||
goto error;
|
||
dcnt = dte_out[dte_out_ptr].cnt;
|
||
if ((dte_out[dte_out_ptr].func & PRI_IND_FLG) == 0)
|
||
dcnt += dte_out[dte_out_ptr].dcnt;
|
||
/* Tell 10 something is ready */
|
||
if ((uptr->STATUS & DTE_SIND) != 0) {
|
||
dcnt = dte_out[dte_out_ptr].dcnt;
|
||
}
|
||
sim_debug(DEBUG_DATA, &dte_dev, "DTE: start: %012llo %o\n", word, dcnt);
|
||
word = (uint64)dcnt;
|
||
if (Mem_deposit_word(0, dte_dt10_off + PRI_CMTW_CNT, &word))
|
||
goto error;
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, uptr->STATUS);
|
||
return 1;
|
||
}
|
||
|
||
|
||
/* Check for input from CTY and put on queue. */
|
||
t_stat dtei_svc (UNIT *uptr)
|
||
{
|
||
int32 ch;
|
||
uint32 base = 0;
|
||
UNIT *optr = &dte_unit[0];
|
||
|
||
#if KI_22BIT
|
||
#if KL_ITS
|
||
if (!QITS)
|
||
#endif
|
||
base = eb_ptr;
|
||
#endif
|
||
sim_clock_coschedule (uptr, tmxr_poll);
|
||
dte_input();
|
||
if ((optr->STATUS & (DTE_SEC)) == 0) {
|
||
dte_function(uptr); /* Process queue */
|
||
dte_start(optr);
|
||
}
|
||
|
||
|
||
/* If we have room see if any new lines */
|
||
while (!full(&cty_in)) {
|
||
ch = sim_poll_kbd ();
|
||
if (ch & SCPE_KFLAG) {
|
||
ch = 0177 & sim_tt_inpcvt(ch, TT_GET_MODE (uptr->flags));
|
||
cty_in.buff[cty_in.in_ptr] =ch & 0377;
|
||
inci(&cty_in);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY char %o '%c'\n", ch,
|
||
((ch > 040 && ch < 0177)? ch: '.'));
|
||
} else
|
||
break;
|
||
}
|
||
|
||
/* If Monitor input, place in buffer */
|
||
if ((optr->STATUS & (DTE_SEC|DTE_MON)) == (DTE_SEC|DTE_MON) &&
|
||
not_empty(&cty_in) && M[SEC_DTMTI + base] == 0) {
|
||
ch = cty_in.buff[cty_in.out_ptr];
|
||
inco(&cty_in);
|
||
M[SEC_DTF11 + base] = ch;
|
||
M[SEC_DTMTI + base] = FMASK;
|
||
if (dte_dev.flags & TYPE_RSX20) {
|
||
uptr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, dte_unit[0].STATUS);
|
||
}
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Handle output of characters to CTY. Started whenever there is output pending */
|
||
t_stat dteo_svc (UNIT *uptr)
|
||
{
|
||
/* Flush out any pending CTY output */
|
||
while(not_empty(&cty_out)) {
|
||
char ch = cty_out.buff[cty_out.out_ptr];
|
||
if (ch != 0) {
|
||
if (sim_putchar_s(ch) != SCPE_OK) {
|
||
sim_activate(uptr, 1000);
|
||
return SCPE_OK;;
|
||
}
|
||
}
|
||
inco(&cty_out);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY outch %o '%c'\n", ch,
|
||
((ch > 040 && ch < 0177)? ch: '.'));
|
||
}
|
||
cty_done++;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
/* Handle FE timer interrupts. And keepalive counts */
|
||
t_stat
|
||
dtertc_srv(UNIT * uptr)
|
||
{
|
||
UNIT *optr = &dte_unit[0];
|
||
|
||
sim_activate_after(uptr, 1000000/rtc_tps);
|
||
/* Check if clock requested */
|
||
if (uptr->STATUS & SEC_CLK) {
|
||
rtc_tick++;
|
||
if (rtc_wait != 0) {
|
||
rtc_wait--;
|
||
} else {
|
||
UNIT *optr = &dte_unit[0];
|
||
uint32 base = 0;
|
||
#if KI_22BIT
|
||
base = eb_ptr;
|
||
#endif
|
||
/* Set timer flag */
|
||
M[SEC_DTCLK + base] = FMASK;
|
||
optr->STATUS |= DTE_10DB;
|
||
set_interrupt(DTE_DEVNUM, optr->STATUS);
|
||
sim_debug(DEBUG_EXP, &dte_dev, "CTY tick %x %x %06o\n",
|
||
rtc_tick, rtc_wait, optr->STATUS);
|
||
}
|
||
}
|
||
#if KL_ITS
|
||
/* Check if Timesharing is running */
|
||
if (QITS) {
|
||
uint64 word;
|
||
|
||
word = (M[ITS_DTECHK] + 1) & FMASK;
|
||
if (word == 0) {
|
||
optr->STATUS |= ITS_ON;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS ON\n");
|
||
sim_activate(&tty_unit[0], 1000);
|
||
sim_activate(&tty_unit[1], 1000);
|
||
} else if (word >= (15 * 60)) {
|
||
optr->STATUS &= ~ITS_ON;
|
||
word = 15 * 60;
|
||
sim_cancel(&tty_unit[0]);
|
||
sim_cancel(&tty_unit[1]);
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "CTY ITS OFF\n");
|
||
}
|
||
M[ITS_DTECHK] = word;
|
||
} else
|
||
#endif
|
||
|
||
/* Update out keep alive timer if in secondary protocol */
|
||
if ((optr->STATUS & DTE_SEC) == 0) {
|
||
int addr = 0144 + eb_ptr;
|
||
uint64 word;
|
||
|
||
(void)Mem_examine_word(0, dte_et11_off + PRI_CMTW_STS, &word);
|
||
addr = (M[addr+1] + dte_off + PRI_CMTW_KAC) & RMASK;
|
||
word = M[addr];
|
||
word = (word + 1) & FMASK;
|
||
M[addr] = word;
|
||
sim_debug(DEBUG_EXP, &dte_dev, "CTY keepalive %06o %012llo %06o\n",
|
||
addr, word, optr->STATUS);
|
||
}
|
||
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
t_stat dte_reset (DEVICE *dptr)
|
||
{
|
||
dte_unit[0].STATUS = DTE_SEC;
|
||
dte_unit[1].STATUS = 0;
|
||
dte_unit[2].STATUS = 0;
|
||
dte_unit[3].STATUS = 0;
|
||
cty_done = 0;
|
||
sim_activate(&dte_unit[3], 1000);
|
||
sim_activate(&dte_unit[2], 1000);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
t_stat
|
||
dte_set_type(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
DEVICE *dptr;
|
||
dptr = find_dev_from_unit (uptr);
|
||
if (dptr == NULL)
|
||
return SCPE_IERR;
|
||
dptr->flags &= ~DEV_M_OS;
|
||
dptr->flags |= val;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
dte_show_type (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||
{
|
||
DEVICE *dptr;
|
||
|
||
if (uptr == NULL)
|
||
return SCPE_IERR;
|
||
|
||
dptr = find_dev_from_unit(uptr);
|
||
if (dptr == NULL)
|
||
return SCPE_IERR;
|
||
fprintf (st, "%s", (dptr->flags & TYPE_RSX20) ? "RSX20" : "RSX10");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
/* Stop operating system */
|
||
|
||
t_stat dte_stop_os (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
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)
|
||
{
|
||
dte_unit[0].flags = (dte_unit[0].flags & ~TT_MODE) | val;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat dte_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");
|
||
fprintf (st, "This will write a 1 to location %03o, causing TOPS10 to stop\n\n", CTY_SWITCH);
|
||
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, &dte_dev);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *dte_description (DEVICE *dptr)
|
||
{
|
||
return "Console TTY Line";
|
||
}
|
||
|
||
|
||
#if (NUM_DEVS_LP20 > 0)
|
||
|
||
void
|
||
lp20_printline(UNIT *uptr, int nl) {
|
||
int trim = 0;
|
||
|
||
/* Trim off trailing blanks */
|
||
while (uptr->COL >= 0 && lp20_buffer[uptr->COL - 1] == ' ') {
|
||
uptr->COL--;
|
||
trim = 1;
|
||
}
|
||
lp20_buffer[uptr->COL] = '\0';
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP output %d %d [%s]\n", uptr->COL, nl,
|
||
lp20_buffer);
|
||
/* Stick a carraige return and linefeed as needed */
|
||
if (uptr->COL != 0 || trim)
|
||
lp20_buffer[uptr->COL++] = '\r';
|
||
if (nl != 0) {
|
||
lp20_buffer[uptr->COL++] = '\n';
|
||
uptr->LINE++;
|
||
}
|
||
if (nl > 0 && lp20_vfu[uptr->LINE] == 010000) {
|
||
lp20_buffer[uptr->COL++] = '\f';
|
||
uptr->LINE = 1;
|
||
} else if (nl < 0 && uptr->LINE >= (int32)uptr->capac) {
|
||
uptr->LINE = 1;
|
||
}
|
||
|
||
sim_fwrite(&lp20_buffer, 1, uptr->COL, uptr->fileref);
|
||
uptr->pos += uptr->COL;
|
||
uptr->COL = 0;
|
||
return;
|
||
}
|
||
|
||
|
||
/* Unit service */
|
||
void
|
||
lp20_output(UNIT *uptr, char c) {
|
||
|
||
if (c == 0)
|
||
return;
|
||
if (uptr->COL == 132)
|
||
lp20_printline(uptr, 1);
|
||
if ((uptr->flags & UNIT_UC) && (c & 0140) == 0140)
|
||
c &= 0137;
|
||
else if (c >= 040 && c < 0177) { /* If printable */
|
||
lp20_buffer[uptr->COL++] = c;
|
||
} if (c == 011) { /* Tab */
|
||
lp20_buffer[uptr->COL++] = ' ';
|
||
while ((uptr->COL & 07) != 0)
|
||
lp20_buffer[uptr->COL++] = ' ';
|
||
}
|
||
return;
|
||
}
|
||
|
||
t_stat lp20_svc (UNIT *uptr)
|
||
{
|
||
char ch;
|
||
uint16 ram_ch;
|
||
uint16 data1[5];
|
||
|
||
if ((uptr->flags & UNIT_ATT) == 0)
|
||
return SCPE_OK;
|
||
if (dte_dev.flags & TYPE_RSX20 && uptr->LPST & HDSFLG) {
|
||
data1[0] = 0;
|
||
|
||
data1[1] = (uptr->LINE == 1) ? 01<<8: 0;
|
||
sim_debug(DEBUG_DETAIL, &dte_dev, "LPT status %06o \n", uptr->LPST);
|
||
if (uptr->LPST & EOFFLG) {
|
||
data1[0] |= 040 << 8;
|
||
uptr->LPCNT = 0;
|
||
}
|
||
if (uptr->LPST & INTFLG) {
|
||
data1[1] |= 02 << 8;
|
||
uptr->LPCNT = 0;
|
||
}
|
||
data1[2] = 0110200;
|
||
if (dte_queue(PRI_EMHDS+PRI_IND_FLG, PRI_EMLPT, 4, data1) == 0)
|
||
sim_activate(uptr, 1000);
|
||
uptr->LPST &= ~(HDSFLG);
|
||
}
|
||
|
||
if (empty(&lp20_queue))
|
||
return SCPE_OK;
|
||
while (not_empty(&lp20_queue)) {
|
||
ch = lp20_queue.buff[lp20_queue.out_ptr];
|
||
inco(&lp20_queue);
|
||
ram_ch = lp20_ram[(int)ch];
|
||
|
||
/* If previous was delimiter or translation do it */
|
||
if (uptr->LPST & DELFLG || (ram_ch &(LP20_RAM_DEL|LP20_RAM_TRN)) != 0) {
|
||
ch = ram_ch & LP20_RAM_CHR;
|
||
uptr->LPST &= ~DELFLG;
|
||
if (ram_ch & LP20_RAM_DEL)
|
||
uptr->LPST |= DELFLG;
|
||
}
|
||
/* Flag if interrupt set */
|
||
if (ram_ch & LP20_RAM_INT)
|
||
uptr->LPST |= HDSFLG|INTFLG;
|
||
/* Check if paper motion */
|
||
if (ram_ch & LP20_RAM_PI) {
|
||
int lines = 0; /* Number of new lines to output */
|
||
/* Print any buffered line */
|
||
lp20_printline(uptr, (ram_ch & 037) != 020);
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP deque %02x %04x\n",
|
||
ch, ram_ch);
|
||
if ((ram_ch & 020) == 0) { /* Find channel mark in output */
|
||
while ((lp20_vfu[uptr->LINE] & (1 << (ram_ch & 017))) == 0) {
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
||
"LP skip chan %04x %04x %d\n",
|
||
lp20_vfu[uptr->LINE], ram_ch, uptr->LINE);
|
||
if (lp20_vfu[uptr->LINE] & 010000) { /* Hit bottom of form */
|
||
sim_fwrite("\014", 1, 1, uptr->fileref);
|
||
uptr->pos++;
|
||
lines = 0;
|
||
uptr->LINE = 1;
|
||
break;
|
||
}
|
||
lines++;
|
||
uptr->LINE++;
|
||
}
|
||
} else {
|
||
while ((ram_ch & 017) != 0) {
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev,
|
||
"LP skip line %04x %04x %d\n",
|
||
lp20_vfu[uptr->LINE], ram_ch, uptr->LINE);
|
||
if (lp20_vfu[uptr->LINE] & 010000) { /* Hit bottom of form */
|
||
sim_fwrite("\014", 1, 1, uptr->fileref);
|
||
uptr->pos++;
|
||
lines = 0;
|
||
uptr->LINE = 1;
|
||
}
|
||
lines++;
|
||
uptr->LINE++;
|
||
ram_ch--;
|
||
}
|
||
}
|
||
for(;lines > 0; lines--) {
|
||
sim_fwrite("\r\n", 1, 2, uptr->fileref);
|
||
uptr->pos+=2;
|
||
}
|
||
} else if (ch != 0) {
|
||
sim_debug(DEBUG_DETAIL, &lp20_dev, "LP deque %02x '%c' %04x\n",
|
||
ch, ch, ram_ch);
|
||
lp20_output(uptr, ch);
|
||
}
|
||
}
|
||
if (empty(&lp20_queue)) {
|
||
data1[0] = 0;
|
||
if (dte_queue(PRI_EMLBE, PRI_EMLPT, 1, data1) == 0)
|
||
sim_activate(uptr, 1000);
|
||
if (dte_dev.flags & TYPE_RSX20) {
|
||
if (uptr->LINE == 0) {
|
||
uptr->LPST |= HDSFLG;
|
||
sim_activate(uptr, 1000);
|
||
}
|
||
}
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat lp20_reset (DEVICE *dptr)
|
||
{
|
||
UNIT *uptr = &lp20_unit;
|
||
int i;
|
||
uptr->POS = 0;
|
||
uptr->COL = 0;
|
||
uptr->LINE = 1;
|
||
/* Clear RAM & VFU */
|
||
for (i = 0; i < 256; i++) {
|
||
lp20_ram[i] = 0;
|
||
lp20_vfu[i] = 0;
|
||
}
|
||
|
||
/* Load default VFU into VFU */
|
||
memcpy(&lp20_vfu, lp20_dvfu, sizeof(lp20_dvfu));
|
||
lp20_ram[012] = LP20_RAM_TRN|LP20_RAM_PI|7; /* Line feed, print line, space one line */
|
||
lp20_ram[013] = LP20_RAM_TRN|LP20_RAM_PI|6; /* Vertical tab, Skip mod 20 */
|
||
lp20_ram[014] = LP20_RAM_TRN|LP20_RAM_PI|0; /* Form feed, skip to top of page */
|
||
lp20_ram[015] = LP20_RAM_TRN|LP20_RAM_PI|020; /* Carrage return */
|
||
lp20_ram[020] = LP20_RAM_TRN|LP20_RAM_PI|1; /* Skip half page */
|
||
lp20_ram[021] = LP20_RAM_TRN|LP20_RAM_PI|2; /* Skip even lines */
|
||
lp20_ram[022] = LP20_RAM_TRN|LP20_RAM_PI|3; /* Skip triple lines */
|
||
lp20_ram[023] = LP20_RAM_TRN|LP20_RAM_PI|4; /* Skip one line */
|
||
lp20_ram[024] = LP20_RAM_TRN|LP20_RAM_PI|5;
|
||
sim_cancel (&lp20_unit); /* deactivate unit */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Attach routine */
|
||
|
||
t_stat lp20_attach (UNIT *uptr, CONST char *cptr)
|
||
{
|
||
sim_switches |= SWMASK ('A'); /* Position to EOF */
|
||
return attach_unit (uptr, cptr);
|
||
}
|
||
|
||
/* Detach routine */
|
||
|
||
t_stat lp20_detach (UNIT *uptr)
|
||
{
|
||
return detach_unit (uptr);
|
||
}
|
||
|
||
/*
|
||
* Line printer routines
|
||
*/
|
||
|
||
t_stat
|
||
lp20_setlpp(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
t_value i;
|
||
t_stat r;
|
||
if (cptr == NULL)
|
||
return SCPE_ARG;
|
||
if (uptr == NULL)
|
||
return SCPE_IERR;
|
||
i = get_uint (cptr, 10, 100, &r);
|
||
if (r != SCPE_OK)
|
||
return SCPE_ARG;
|
||
uptr->capac = (t_addr)i;
|
||
uptr->LINE = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat
|
||
lp20_getlpp(FILE *st, UNIT *uptr, int32 v, CONST void *desc)
|
||
{
|
||
if (uptr == NULL)
|
||
return SCPE_IERR;
|
||
fprintf(st, "linesperpage=%d", uptr->capac);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat lp20_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag,
|
||
const char *cptr)
|
||
{
|
||
fprintf (st, "Line Printer (LPT)\n\n");
|
||
fprintf (st, "The line printer (LPT) writes data to a disk file. The POS register specifies\n");
|
||
fprintf (st, "the number of the next data item to be written. Thus, by changing POS, the\n");
|
||
fprintf (st, "user can backspace or advance the printer.\n");
|
||
fprintf (st, "The Line printer can be configured to any number of lines per page with the:\n");
|
||
fprintf (st, " sim> SET %s0 LINESPERPAGE=n\n\n", dptr->name);
|
||
fprintf (st, "The default is 66 lines per page.\n\n");
|
||
fprintf (st, "The device address of the Line printer can be changed\n");
|
||
fprintf (st, " sim> SET %s0 DEV=n\n\n", dptr->name);
|
||
fprint_set_help (st, dptr);
|
||
fprint_show_help (st, dptr);
|
||
fprint_reg_help (st, dptr);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *lp20_description (DEVICE *dptr)
|
||
{
|
||
return "LP20 line printer" ;
|
||
}
|
||
|
||
#endif
|
||
|
||
#if (NUM_DEVS_TTY > 0)
|
||
|
||
/* Unit service */
|
||
t_stat ttyi_svc (UNIT *uptr)
|
||
{
|
||
int32 ln;
|
||
TMLN *lp;
|
||
int flg;
|
||
|
||
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
|
||
return SCPE_OK;
|
||
|
||
sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */
|
||
|
||
/* If we have room see if any new lines */
|
||
ln = tmxr_poll_conn (&tty_desc); /* look for connect */
|
||
if (ln >= 0) {
|
||
tty_ldsc[ln].rcve = 1;
|
||
sim_debug(DEBUG_DETAIL, &tty_dev, "TTY line connect %d\n", ln);
|
||
}
|
||
|
||
tmxr_poll_tx(&tty_desc);
|
||
tmxr_poll_rx(&tty_desc);
|
||
|
||
/* Scan each line for input */
|
||
for (ln = 0; ln < tty_desc.lines; ln++) {
|
||
struct _buffer *iptr = &tty_in[ln];
|
||
lp = &tty_ldsc[ln];
|
||
if (lp->conn == 0)
|
||
continue;
|
||
flg = 1;
|
||
while (flg && !full(iptr)) {
|
||
/* Spool up as much as we have room for */
|
||
int32 ch = tmxr_getc_ln(lp);
|
||
if ((ch & TMXR_VALID) != 0) {
|
||
ch = sim_tt_inpcvt (ch,
|
||
TT_GET_MODE(tty_unit[0].flags) | TTUF_KSR);
|
||
iptr->buff[iptr->in_ptr] = ch & 0377;
|
||
inci(iptr);
|
||
sim_debug(DEBUG_DETAIL, &tty_dev, "TTY recieve %d: %o\n",
|
||
ln, ch);
|
||
} else
|
||
flg = 0;
|
||
}
|
||
}
|
||
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Output whatever we can */
|
||
t_stat ttyo_svc (UNIT *uptr)
|
||
{
|
||
t_stat r;
|
||
int32 ln;
|
||
TMLN *lp;
|
||
|
||
if ((tty_unit[0].flags & UNIT_ATT) == 0) /* attached? */
|
||
return SCPE_OK;
|
||
|
||
sim_clock_coschedule(uptr, tmxr_poll); /* continue poll */
|
||
|
||
for (ln = 0; ln < tty_desc.lines; ln++) {
|
||
struct _buffer *optr = &tty_out[ln];
|
||
lp = &tty_ldsc[ln];
|
||
if (lp->conn == 0) {
|
||
if (not_empty(optr)) {
|
||
optr->out_ptr = optr->in_ptr = 0;
|
||
tty_done[ln] = 1;
|
||
}
|
||
continue;
|
||
}
|
||
if (empty(optr))
|
||
continue;
|
||
while (not_empty(optr)) {
|
||
int32 ch = optr->buff[optr->out_ptr];
|
||
ch = sim_tt_outcvt(ch, TT_GET_MODE (tty_unit[0].flags) | TTUF_KSR);
|
||
sim_debug(DEBUG_DATA, &tty_dev, "TTY: %d output %o\n", ln, ch);
|
||
r = tmxr_putc_ln (lp, ch);
|
||
if (r == SCPE_OK)
|
||
inco(optr);
|
||
else if (r == SCPE_LOST) {
|
||
optr->out_ptr = optr->in_ptr = 0;
|
||
continue;
|
||
} else
|
||
continue;
|
||
}
|
||
tty_done[ln] = 1;
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Reset routine */
|
||
|
||
t_stat tty_reset (DEVICE *dptr)
|
||
{
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
/* SET LINES processor */
|
||
|
||
t_stat tty_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
int32 newln, i, t;
|
||
t_stat r;
|
||
|
||
if (cptr == NULL)
|
||
return SCPE_ARG;
|
||
newln = (int32) get_uint (cptr, 10, NUM_LINES_TTY, &r);
|
||
if ((r != SCPE_OK) || (newln == tty_desc.lines))
|
||
return r;
|
||
if ((newln == 0) || (newln >= NUM_LINES_TTY) || (newln % 16) != 0)
|
||
return SCPE_ARG;
|
||
if (newln < tty_desc.lines) {
|
||
for (i = newln, t = 0; i < tty_desc.lines; i++)
|
||
t = t | tty_ldsc[i].conn;
|
||
if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
|
||
return SCPE_OK;
|
||
for (i = newln; i < tty_desc.lines; i++) {
|
||
if (tty_ldsc[i].conn) {
|
||
tmxr_linemsg (&tty_ldsc[i], "\r\nOperator disconnected line\r\n");
|
||
tmxr_send_buffered_data (&tty_ldsc[i]);
|
||
}
|
||
tmxr_detach_ln (&tty_ldsc[i]); /* completely reset line */
|
||
}
|
||
}
|
||
if (tty_desc.lines < newln)
|
||
memset (tty_ldsc + tty_desc.lines, 0,
|
||
sizeof(*tty_ldsc)*(newln-tty_desc.lines));
|
||
tty_desc.lines = newln;
|
||
return tty_reset (&tty_dev); /* setup lines and auto config */
|
||
}
|
||
|
||
/* SET LOG processor */
|
||
|
||
t_stat tty_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
t_stat r;
|
||
char gbuf[CBUFSIZE];
|
||
int32 ln;
|
||
|
||
if (cptr == NULL)
|
||
return SCPE_ARG;
|
||
cptr = get_glyph (cptr, gbuf, '=');
|
||
if ((cptr == NULL) || (*cptr == 0) || (gbuf[0] == 0))
|
||
return SCPE_ARG;
|
||
ln = (int32) get_uint (gbuf, 10, tty_desc.lines, &r);
|
||
if ((r != SCPE_OK) || (ln >= tty_desc.lines))
|
||
return SCPE_ARG;
|
||
return tmxr_set_log (NULL, ln, cptr, desc);
|
||
}
|
||
|
||
/* SET NOLOG processor */
|
||
|
||
t_stat tty_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
||
{
|
||
t_stat r;
|
||
int32 ln;
|
||
|
||
if (cptr == NULL)
|
||
return SCPE_ARG;
|
||
ln = (int32) get_uint (cptr, 10, tty_desc.lines, &r);
|
||
if ((r != SCPE_OK) || (ln >= tty_desc.lines))
|
||
return SCPE_ARG;
|
||
return tmxr_set_nolog (NULL, ln, NULL, desc);
|
||
}
|
||
|
||
/* SHOW LOG processor */
|
||
|
||
t_stat tty_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
||
{
|
||
int32 i;
|
||
|
||
for (i = 0; i < tty_desc.lines; i++) {
|
||
fprintf (st, "line %d: ", i);
|
||
tmxr_show_log (st, NULL, i, desc);
|
||
fprintf (st, "\n");
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
|
||
/* Attach routine */
|
||
|
||
t_stat tty_attach (UNIT *uptr, CONST char *cptr)
|
||
{
|
||
t_stat reason;
|
||
|
||
reason = tmxr_attach (&tty_desc, uptr, cptr);
|
||
if (reason != SCPE_OK)
|
||
return reason;
|
||
sim_activate (uptr, tmxr_poll);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Detach routine */
|
||
|
||
t_stat tty_detach (UNIT *uptr)
|
||
{
|
||
int32 i;
|
||
t_stat reason;
|
||
sim_cancel (uptr);
|
||
reason = tmxr_detach (&tty_desc, uptr);
|
||
for (i = 0; i < tty_desc.lines; i++)
|
||
tty_ldsc[i].rcve = 0;
|
||
return reason;
|
||
}
|
||
|
||
t_stat tty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
||
{
|
||
fprintf (st, "FE Terminal Interfaces\n\n");
|
||
fprintf (st, "The FE terminal could support up to 256 lines, in groups of 16\n");
|
||
fprintf (st, "lines. The number of lines is specified with a SET command:\n\n");
|
||
fprintf (st, " sim> SET TTY LINES=n set number of additional lines to n [8-32]\n\n");
|
||
fprintf (st, "Lines must be set in multiples of 8.\n");
|
||
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
|
||
tmxr_attach_help (st, dptr, uptr, flag, cptr);
|
||
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.\n");
|
||
fprintf (st, "Finally, each line supports output logging. The SET TTYn LOG command enables\n");
|
||
fprintf (st, "logging on a line:\n\n");
|
||
fprintf (st, " sim> SET TTYn LOG=filename log output of line n to filename\n\n");
|
||
fprintf (st, "The SET TTYn NOLOG command disables logging and closes the open log file,\n");
|
||
fprintf (st, "if any.\n\n");
|
||
fprintf (st, "Once TTY is attached and the simulator is running, the terminals listen for\n");
|
||
fprintf (st, "connections on the specified port. They assume that the incoming connections\n");
|
||
fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n");
|
||
fprintf (st, "by the Telnet client, a SET TTY DISCONNECT command, or a DETACH TTY command.\n\n");
|
||
fprintf (st, "Other special commands:\n\n");
|
||
fprintf (st, " sim> SHOW TTY CONNECTIONS show current connections\n");
|
||
fprintf (st, " sim> SHOW TTY STATISTICS show statistics for active connections\n");
|
||
fprintf (st, " sim> SET TTYn DISCONNECT disconnects the specified line.\n");
|
||
fprint_reg_help (st, &tty_dev);
|
||
fprintf (st, "\nThe additional terminals do not support save and restore. All open connections\n");
|
||
fprintf (st, "are lost when the simulator shuts down or TTY is detached.\n");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
const char *tty_description (DEVICE *dptr)
|
||
{
|
||
return "FE asynchronous line interface";
|
||
}
|
||
|
||
#endif
|
||
#endif
|