556 lines
18 KiB
C
556 lines
18 KiB
C
/* ka10_ch.c: CH10 Chaosnet interface.
|
|
------------------------------------------------------------------------------
|
|
|
|
Copyright (c) 2019, Richard Cornwell, original by Lars Brinkhoff.
|
|
|
|
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.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "kx10_defs.h"
|
|
#include "sim_tmxr.h"
|
|
|
|
#ifndef NUM_DEVS_CH10
|
|
#define NUM_DEVS_CH10 0
|
|
#endif
|
|
|
|
#if (NUM_DEVS_CH10 > 0)
|
|
#define CH_DEVNUM 0470
|
|
|
|
/* CSR bits */
|
|
#define PIA 00000007LL /* Interrupt channel */
|
|
#define TXIE 00000010LL /* Transmit interrupt enable */
|
|
#define RXIE 00000020LL /* Receive interrupt enable */
|
|
#define SPY 00000040LL /* Spy */
|
|
#define LOOP 00000100LL /* Loop back */
|
|
#define SWAP 00000200LL /* Swap bytes */
|
|
#define HALF 00000400LL /* Halfword output */
|
|
#define TXD 00001000LL /* Transmit done */
|
|
#define RXD 00002000LL /* Receive done */
|
|
#define TXA 00004000LL /* Transmit abort */
|
|
#define CTX 00004000LL /* Clear transmitter */
|
|
#define LOST 00170000LL /* Lost count */
|
|
#define RESET 00010000LL /* Reset */
|
|
#define CRC 00200000LL /* CRC error */
|
|
#define WLE 00400000LL /* Word length error */
|
|
#define PLE 01000000LL /* Packet length error */
|
|
#define OVER 02000000LL /* Overrun */
|
|
|
|
#define STATUS_BITS (PIA|TXIE|RXIE|SPY|LOOP|SWAP|HALF|TXA|LOST|CRC|WLE|PLE|OVER)
|
|
|
|
BITFIELD ch10_csr_bits[] = {
|
|
BIT(PIA),
|
|
BIT(TXIE),
|
|
BIT(RXIE),
|
|
BIT(SPY),
|
|
BIT(LOOP),
|
|
BIT(SWAP),
|
|
BIT(HALF),
|
|
BIT(TXD),
|
|
BIT(RXD),
|
|
BIT(TXA),
|
|
BIT(CTX),
|
|
BITF(LOST,4),
|
|
BIT(CRC),
|
|
BIT(WLE),
|
|
BIT(PLE),
|
|
BIT(OVER),
|
|
ENDBITS
|
|
};
|
|
|
|
#define CHUDP_HEADER 4
|
|
#define IOLN_CH 020
|
|
#define DBG_TRC 0x0001
|
|
#define DBG_REG 0x0002
|
|
#define DBG_PKT 0x0004
|
|
#define DBG_DAT 0x0008
|
|
#define DBG_INT 0x0010
|
|
#define DBG_ERR 0x0020
|
|
|
|
t_stat ch10_svc(UNIT *);
|
|
t_stat ch10_reset (DEVICE *);
|
|
t_stat ch10_attach (UNIT *, CONST char *);
|
|
t_stat ch10_detach (UNIT *);
|
|
t_stat ch10_devio(uint32 dev, uint64 *data);
|
|
t_stat ch10_show_peer (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
|
|
t_stat ch10_set_peer (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
|
|
t_stat ch10_show_node (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
|
|
t_stat ch10_set_node (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
|
|
t_stat ch10_help (FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
t_stat ch10_help_attach (FILE *, DEVICE *, UNIT *, int32, const char *);
|
|
const char *ch10_description (DEVICE *);
|
|
|
|
static char peer[256];
|
|
int address;
|
|
static uint64 ch10_status;
|
|
static int rx_count;
|
|
static int rx_pos;
|
|
static int tx_count;
|
|
static uint8 rx_buffer[514+100];
|
|
static uint8 tx_buffer[514+100];
|
|
|
|
TMLN ch10_lines[1] = { {0} };
|
|
TMXR ch10_tmxr = { 1, NULL, 0, ch10_lines};
|
|
|
|
UNIT ch10_unit[] = {
|
|
{UDATA (&ch10_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },
|
|
};
|
|
|
|
REG ch10_reg[] = {
|
|
{ GRDATADF(CSR, ch10_status, 16, 16, 0, "Control and status", ch10_csr_bits), REG_FIT },
|
|
{ GRDATAD(RXCNT, rx_count, 16, 16, 0, "Receive word count"), REG_FIT|REG_RO},
|
|
{ GRDATAD(TXCNT, tx_count, 16, 16, 0, "Transmit word count"), REG_FIT|REG_RO},
|
|
{ BRDATAD(RXBUF, rx_buffer, 16, 8, sizeof rx_buffer, "Receive packet buffer"), REG_FIT},
|
|
{ BRDATAD(TXBUF, tx_buffer, 16, 8, sizeof tx_buffer, "Transmit packet buffer"), REG_FIT},
|
|
{ BRDATAD(PEER, peer, 16, 8, sizeof peer, "Network peer"), REG_HRO},
|
|
{ GRDATAD(NODE, address, 16, 16, 0, "Node address"), REG_HRO},
|
|
{ NULL } };
|
|
|
|
MTAB ch10_mod[] = {
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "PEER", "PEER",
|
|
&ch10_set_peer, &ch10_show_peer, NULL, "Remote host name and port" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "NODE", "NODE",
|
|
&ch10_set_node, &ch10_show_node, NULL, "Chaosnet node address" },
|
|
{ 0 },
|
|
};
|
|
|
|
DIB ch10_dib = {CH_DEVNUM, 1, &ch10_devio, NULL};
|
|
|
|
DEBTAB ch10_debug[] = {
|
|
{ "TRC", DBG_TRC, "Detailed trace" },
|
|
{ "REG", DBG_REG, "Hardware registers" },
|
|
{ "PKT", DBG_PKT, "Packets" },
|
|
{ "DAT", DBG_DAT, "Packet data" },
|
|
{ "INT", DBG_INT, "Interrupts" },
|
|
{ "ERR", DBG_ERR, "Error conditions" },
|
|
{ 0 }
|
|
};
|
|
|
|
DEVICE ch10_dev = {
|
|
"CH", ch10_unit, ch10_reg, ch10_mod,
|
|
1, 8, 16, 1, 8, 16,
|
|
NULL, NULL, &ch10_reset,
|
|
NULL, &ch10_attach, &ch10_detach,
|
|
&ch10_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX,
|
|
0, ch10_debug, NULL, NULL, &ch10_help, &ch10_help_attach, NULL,
|
|
&ch10_description
|
|
};
|
|
|
|
uint16 ch10_checksum (const uint8 *p, int count)
|
|
{
|
|
int32 sum = 0;
|
|
|
|
while (count > 1) {
|
|
sum += (p[0]<<8) | p[1];
|
|
p += 2;
|
|
count -= 2;
|
|
}
|
|
|
|
if ( count > 0)
|
|
sum += p[0];
|
|
|
|
while (sum >> 16)
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
|
|
return (~sum) & 0xffff;
|
|
}
|
|
|
|
|
|
int ch10_test_int (void)
|
|
{
|
|
if ((ch10_status & (RXD|RXIE)) == (RXD|RXIE) ||
|
|
(ch10_status & (TXD|TXIE)) == (TXD|TXIE)) {
|
|
sim_debug (DBG_INT, &ch10_dev, "%s %s Interrupt\n",
|
|
ch10_status & RXD ? "RX" : "",
|
|
ch10_status & TXD ? "TX" : "");
|
|
set_interrupt(CH_DEVNUM, ch10_status & PIA);
|
|
return 1;
|
|
} else {
|
|
clr_interrupt(CH_DEVNUM);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void ch10_validate (const uint8 *p, int count)
|
|
{
|
|
uint16 chksum;
|
|
int size;
|
|
|
|
sim_debug (DBG_TRC, &ch10_dev, "Packet opcode: %02x\n", p[0]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "MBZ: %02x\n", p[1]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Forwarding count: %02x\n", p[2] >> 4);
|
|
size = ((p[2] & 0xF) << 8) + p[3];
|
|
sim_debug (DBG_TRC, &ch10_dev, "Packet size: %03x\n", size);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Destination address: %o\n", (p[4] << 8) + p[5]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Destination index: %02x\n", (p[6] << 8) + p[7]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Source address: %o\n", (p[8] << 8) + p[9]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Source index: %02x\n", (p[10] << 8) + p[11]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Packet number: %02x\n", (p[12] << 8) + p[13]);
|
|
sim_debug (DBG_TRC, &ch10_dev, "Acknowledgement: %02x\n", (p[14] << 8) + p[15]);
|
|
|
|
if (p[1] != 0)
|
|
sim_debug (DBG_ERR, &ch10_dev, "Bad packet\n");
|
|
|
|
chksum = ch10_checksum (p, count);
|
|
if (chksum != 0) {
|
|
sim_debug (DBG_ERR, &ch10_dev, "Checksum error: %04x\n", chksum);
|
|
ch10_status |= CRC;
|
|
} else
|
|
sim_debug (DBG_TRC, &ch10_dev, "Checksum: %05o\n", chksum);
|
|
}
|
|
|
|
t_stat ch10_transmit ()
|
|
{
|
|
size_t len;
|
|
t_stat r;
|
|
int i = CHUDP_HEADER + tx_count;
|
|
uint16 chk;
|
|
|
|
if (tx_count > (514 - CHUDP_HEADER)) {
|
|
sim_debug (DBG_PKT, &ch10_dev, "Pack size failed, %d bytes.\n", (int)tx_count);
|
|
ch10_status |= PLE;
|
|
return SCPE_INCOMP;
|
|
}
|
|
tx_buffer[i] = tx_buffer[8+CHUDP_HEADER];
|
|
tx_buffer[i+1] = tx_buffer[9+CHUDP_HEADER];
|
|
tx_count += 2;
|
|
chk = ch10_checksum(tx_buffer + CHUDP_HEADER, tx_count);
|
|
tx_buffer[i+2] = (chk >> 8) & 0xff;
|
|
tx_buffer[i+3] = chk & 0xff;
|
|
tx_count += 2;
|
|
|
|
tmxr_poll_tx (&ch10_tmxr);
|
|
len = CHUDP_HEADER + (size_t)tx_count;
|
|
r = tmxr_put_packet_ln (&ch10_lines[0], (const uint8 *)&tx_buffer, len);
|
|
if (r == SCPE_OK) {
|
|
sim_debug (DBG_PKT, &ch10_dev, "Sent UDP packet, %d bytes. %04x checksum\n", (int)len, chk);
|
|
tmxr_poll_tx (&ch10_tmxr);
|
|
} else {
|
|
sim_debug (DBG_ERR, &ch10_dev, "Sending UDP failed: %d.\n", r);
|
|
ch10_status |= OVER;
|
|
}
|
|
tx_count = 0;
|
|
ch10_status |= TXD;
|
|
ch10_test_int ();
|
|
return SCPE_OK;
|
|
}
|
|
|
|
int ch10_receive (void)
|
|
{
|
|
size_t count;
|
|
const uint8 *p;
|
|
uint16 dest;
|
|
|
|
tmxr_poll_rx (&ch10_tmxr);
|
|
if (tmxr_get_packet_ln (&ch10_lines[0], &p, &count) != SCPE_OK) {
|
|
sim_debug (DBG_ERR, &ch10_dev, "TMXR error receiving packet\n");
|
|
return 0;
|
|
}
|
|
if (p == NULL)
|
|
return 0;
|
|
dest = ((p[4+CHUDP_HEADER] & 0xff) << 8) + (p[5+CHUDP_HEADER] & 0xff);
|
|
|
|
sim_debug (DBG_PKT, &ch10_dev, "Received UDP packet, %d bytes for: %o\n", (int)count, dest);
|
|
/* Check if packet for us. */
|
|
if (dest != address && dest != 0 && (ch10_status & SPY) == 0)
|
|
return 1;
|
|
|
|
if ((RXD & ch10_status) == 0) {
|
|
count = (count + 1) & 01776;
|
|
rx_count = count;
|
|
memcpy (rx_buffer, p, rx_count);
|
|
rx_pos = 0;
|
|
sim_debug (DBG_TRC, &ch10_dev, "Rx count, %d\n", rx_count);
|
|
ch10_validate (rx_buffer + CHUDP_HEADER, rx_count - CHUDP_HEADER);
|
|
ch10_status |= RXD;
|
|
ch10_lines[0].rcve = FALSE;
|
|
sim_debug (DBG_TRC, &ch10_dev, "Rx off\n");
|
|
ch10_test_int ();
|
|
} else {
|
|
sim_debug (DBG_ERR, &ch10_dev, "Lost packet\n");
|
|
if ((ch10_status & LOST) < LOST)
|
|
ch10_status += 01000;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void ch10_clear (void)
|
|
{
|
|
ch10_status = TXD;
|
|
rx_count = 0;
|
|
tx_count = 0;
|
|
rx_pos = 0;
|
|
|
|
tx_buffer[0] = 1; /* CHUDP header */
|
|
tx_buffer[1] = 1;
|
|
tx_buffer[2] = 0;
|
|
tx_buffer[3] = 0;
|
|
ch10_lines[0].rcve = TRUE;
|
|
|
|
ch10_test_int ();
|
|
}
|
|
|
|
void ch10_command (uint32 data)
|
|
{
|
|
if (data & RXD) {
|
|
sim_debug (DBG_REG, &ch10_dev, "Clear RX\n");
|
|
ch10_status &= ~RXD;
|
|
rx_count = 0;
|
|
ch10_lines[0].rcve = TRUE;
|
|
rx_count = 0;
|
|
}
|
|
if (data & RESET) {
|
|
/* Do this first so other bits can do their things. */
|
|
sim_debug (DBG_REG, &ch10_dev, "Reset\n");
|
|
ch10_clear ();
|
|
}
|
|
if (data & CTX) {
|
|
sim_debug (DBG_REG, &ch10_dev, "Clear TX\n");
|
|
tx_count = 0;
|
|
ch10_status |= TXD;
|
|
ch10_status &= ~TXA;
|
|
}
|
|
if (data & TXD) {
|
|
sim_debug (DBG_REG, &ch10_dev, "XMIT TX\n");
|
|
ch10_transmit();
|
|
ch10_status &= ~TXA;
|
|
}
|
|
}
|
|
|
|
t_stat ch10_devio(uint32 dev, uint64 *data)
|
|
{
|
|
switch(dev & 07) {
|
|
case CONO:
|
|
sim_debug (DBG_REG, &ch10_dev, "CONO %012llo %012llo \n", *data, ch10_status);
|
|
ch10_command ((uint32)(*data & RMASK));
|
|
ch10_status &= ~STATUS_BITS;
|
|
ch10_status |= *data & STATUS_BITS;
|
|
ch10_test_int ();
|
|
break;
|
|
case CONI:
|
|
*data = ch10_status & (STATUS_BITS|TXD|RXD);
|
|
*data |= (uint64)address << 20;
|
|
break;
|
|
case DATAO:
|
|
ch10_status &= ~TXD;
|
|
if (tx_count < 514) {
|
|
int i = CHUDP_HEADER + tx_count;
|
|
if (ch10_status & SWAP) {
|
|
tx_buffer[i] = (*data >> 20) & 0xff;
|
|
tx_buffer[i+1] = (*data >> 28) & 0xff;
|
|
} else {
|
|
tx_buffer[i] = (*data >> 28) & 0xff;
|
|
tx_buffer[i+1] = (*data >> 20) & 0xff;
|
|
}
|
|
tx_count+=2;
|
|
if ((ch10_status & HALF) == 0) {
|
|
if (ch10_status & SWAP) {
|
|
tx_buffer[i+2] = (*data >> 4) & 0xff;
|
|
tx_buffer[i+3] = (*data >> 12) & 0xff;
|
|
} else {
|
|
tx_buffer[i+2] = (*data >> 12) & 0xff;
|
|
tx_buffer[i+3] = (*data >> 4) & 0xff;
|
|
}
|
|
tx_count+=2;
|
|
}
|
|
sim_debug (DBG_DAT, &ch10_dev, "Write buffer word %d:%02x %02x %02x %02x %012llo %012llo\n",
|
|
tx_count, tx_buffer[i], tx_buffer[i+1], tx_buffer[i+2], tx_buffer[i+3], *data, ch10_status);
|
|
return SCPE_OK;
|
|
} else {
|
|
sim_debug (DBG_ERR, &ch10_dev, "Write buffer overflow\n");
|
|
ch10_status |= PLE;
|
|
return SCPE_OK;
|
|
}
|
|
case DATAI:
|
|
if (rx_count == 0) {
|
|
*data = 0;
|
|
sim_debug (DBG_ERR, &ch10_dev, "Read empty buffer\n");
|
|
} else {
|
|
ch10_status &= ~RXD;
|
|
if (ch10_status & SWAP) {
|
|
*data = ((uint64)(rx_buffer[rx_pos]) & 0xff) << 20;
|
|
*data |= ((uint64)(rx_buffer[rx_pos+1]) & 0xff) << 28;
|
|
*data |= ((uint64)(rx_buffer[rx_pos+2]) & 0xff) << 4;
|
|
*data |= ((uint64)(rx_buffer[rx_pos+3]) & 0xff) << 12;
|
|
} else {
|
|
*data = ((uint64)(rx_buffer[rx_pos]) & 0xff) << 28;
|
|
*data |= ((uint64)(rx_buffer[rx_pos+1]) & 0xff) << 20;
|
|
*data |= ((uint64)(rx_buffer[rx_pos+2]) & 0xff) << 12;
|
|
*data |= ((uint64)(rx_buffer[rx_pos+3]) & 0xff) << 4;
|
|
}
|
|
rx_count-=4;
|
|
sim_debug (DBG_DAT, &ch10_dev, "Read buffer word %d:%02x %02x %02x %02x %012llo %012llo\n",
|
|
rx_count, rx_buffer[rx_pos], rx_buffer[rx_pos+1], rx_buffer[rx_pos+2], rx_buffer[rx_pos+3],
|
|
*data, ch10_status);
|
|
rx_pos+=4;
|
|
}
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_svc(UNIT *uptr)
|
|
{
|
|
if (ch10_lines[0].conn) {
|
|
if (ch10_receive ()) {
|
|
sim_activate_after (uptr, 300);
|
|
return SCPE_OK;
|
|
}
|
|
} else {
|
|
(void)tmxr_poll_conn (&ch10_tmxr);
|
|
}
|
|
sim_clock_coschedule (uptr, 1000);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
char linkinfo[256];
|
|
t_stat r;
|
|
|
|
ch10_dev.dctrl |= 0xF77F0000;
|
|
if (address == -1)
|
|
return sim_messagef (SCPE_2FARG, "Must set Chaosnet NODE address first \"SET CH NODE=val\"\n");
|
|
if (peer[0] == '\0')
|
|
return sim_messagef (SCPE_2FARG, "Must set Chaosnet PEER \"SET CH PEER=host:port\"\n");
|
|
|
|
snprintf (linkinfo, sizeof(linkinfo), "Buffer=%d,UDP,%s,PACKET,Connect=%.*s,Line=0",
|
|
(int)sizeof tx_buffer, cptr, (int)(sizeof(linkinfo) - (45 + strlen(cptr))), peer);
|
|
r = tmxr_attach (&ch10_tmxr, uptr, linkinfo);
|
|
if (r != SCPE_OK) {
|
|
sim_debug (DBG_ERR, &ch10_dev, "TMXR error opening master\n");
|
|
return sim_messagef (r, "Error Opening: %s\n", peer);
|
|
}
|
|
|
|
uptr->filename = (char *)realloc (uptr->filename, 1 + strlen (cptr));
|
|
strcpy (uptr->filename, cptr);
|
|
sim_activate (uptr, 1000);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_detach (UNIT *uptr)
|
|
{
|
|
sim_cancel (uptr);
|
|
tmxr_detach (&ch10_tmxr, uptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_reset (DEVICE *dptr)
|
|
{
|
|
ch10_clear ();
|
|
if (ch10_unit[0].flags & UNIT_ATT)
|
|
sim_activate (&ch10_unit[0], 100);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_show_peer (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
fprintf (st, "peer=%s", peer[0] ? peer : "unspecified");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_set_peer (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
char host[256], port[256];
|
|
|
|
if ((cptr == NULL) || (*cptr == 0))
|
|
return SCPE_ARG;
|
|
if (uptr->flags & UNIT_ATT)
|
|
return SCPE_ALATT;
|
|
if (sim_parse_addr (cptr, host, sizeof host, NULL, port, sizeof port, NULL, NULL))
|
|
return SCPE_ARG;
|
|
if (host[0] == '\0')
|
|
return SCPE_ARG;
|
|
|
|
strncpy (peer, cptr, sizeof(peer) - 1);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_show_node (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
if (address == -1)
|
|
fprintf (st, "node=unspecified");
|
|
else
|
|
fprintf (st, "node=%o", address);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_set_node (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
t_stat r;
|
|
int x;
|
|
|
|
if ((cptr == NULL) || (*cptr == 0))
|
|
return SCPE_ARG;
|
|
if (uptr->flags & UNIT_ATT)
|
|
return SCPE_ALATT;
|
|
|
|
x = (int)get_uint (cptr, 8, 0177777, &r);
|
|
if (r != SCPE_OK)
|
|
return SCPE_ARG;
|
|
|
|
address = x;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
const char *ch10_description (DEVICE *dptr)
|
|
{
|
|
return "CH11 Chaosnet interface";
|
|
}
|
|
|
|
t_stat ch10_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
fprintf (st, "CH10 Chaosnet interface\n\n");
|
|
fprintf (st, "It's a network interface for MIT's Chaosnet. Options allow\n");
|
|
fprintf (st, "control of the node address and network peer. The node address must\n");
|
|
fprintf (st, "be a 16-bit octal number.\n");
|
|
fprint_set_help (st, dptr);
|
|
fprintf (st, "\nConfigured options and controller state can be displayed with:\n");
|
|
fprint_show_help (st, dptr);
|
|
fprintf (st, "\nThe CH10 simulation will encapsulate Chaosnet packets in UDP or TCP.\n");
|
|
fprintf (st, "To access the network, the simulated Chaosnet interface must be attached\n");
|
|
fprintf (st, "to a network peer.\n\n");
|
|
ch10_help_attach (st, dptr, uptr, flag, cptr);
|
|
fprintf (st, "Software that runs on SIMH that supports this device include:\n");
|
|
fprintf (st, " - ITS, the PDP-10 Incompatible Timesharing System\n");
|
|
fprintf (st, "Outside SIMH, there's KLH10 and Lisp machine simulators. Various\n");
|
|
fprintf (st, "encapsulating transport mechanisms exist: UDP, IP, Ethernet.\n\n");
|
|
fprintf (st, "Documentation:\n");
|
|
fprintf (st, "https://lm-3.github.io/amber.html#Hardware-Programming-Documentation\n\n");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat ch10_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
fprintf (st, "To configure CH10, first set the local Chaosnet node address, and\n");
|
|
fprintf (st, "the peer:\n\n");
|
|
fprintf (st, " sim> SET CH NODE=<octal address>\n");
|
|
fprintf (st, " sim> SET CH PEER=<remote host>:<remote port>\n\n");
|
|
fprintf (st, "Then, attach a local port. By default UDP is used:\n\n");
|
|
fprintf (st, " sim> ATTACH CH <local port>\n\n");
|
|
fprintf (st, "If TCP is desired, add \"TCP\":\n\n");
|
|
fprintf (st, " sim> ATTACH CH <local port>,TCP\n\n");
|
|
return SCPE_OK;
|
|
}
|
|
#endif
|