simh-testsetgenerator/PDP11/pdp11_xu.c
Bob Supnik b6393b36b4 Notes For V3.3
RESTRICTION: The HP DS disk is not debugged.  DO NOT enable this
feature for normal operations.
WARNING: Massive changes in the PDP-11 make all previous SAVEd
file obsolete.  Do not attempt to use a PDP-11 SAVE file from a
prior release with V3.3!

1. New Features in 3.3

1.1 SCP

- Added -p (powerup) qualifier to RESET
- Changed SET <unit> ONLINE/OFFLINE to SET <unit> ENABLED/DISABLED
- Moved SET DEBUG under SET CONSOLE hierarchy
- Added optional parameter value to SHOW command
- Added output file option to SHOW command

1.2 PDP-11

- Separated RH Massbus adapter from RP controller
- Added TU tape support
- Added model emulation framework
- Added model details

1.3 VAX

- Separated out CVAX-specific features from core instruction simulator
- Implemented capability for CIS, octaword, compatibility mode instructions
- Added instruction display and parse for compatibility mode
- Changed SET CPU VIRTUAL=n to SHOW CPU VIRTUAL=n
- Added =n optional parameter to SHOW CPU HISTORY

1.4 Unibus/Qbus simulators (PDP-11, VAX, PDP-10)

- Simplified DMA API's
- Modified DMA peripherals to use simplified API's

1.5 HP2100 (all changes from Dave Bryan)

CPU	- moved MP into its own device; added MP option jumpers
	- modified DMA to allow disabling
	- modified SET CPU 2100/2116 to truncate memory > 32K
	- added -F switch to SET CPU to force memory truncation
	- modified WRU to be REG_HRO
	- added BRK and DEL to save console settings

DR	- provided protected tracks and "Writing Enabled" status bit
	- added "parity error" status return on writes for 12606
	- added track origin test for 12606
	- added SCP test for 12606
	- added "Sector Flag" status bit
	- added "Read Inhibit" status bit for 12606
	- added TRACKPROT modifier

LPS	- added SET OFFLINE/ONLINE, POWEROFF/POWERON
	- added fast/realistic timing
	- added debug printouts

LPT	- added SET OFFLINE/ONLINE, POWEROFF/POWERON

PTR	- added paper tape loop mode, DIAG/READER modifiers to PTR
	- added PV_LEFT to PTR TRLLIM register

CLK	- modified CLK to permit disable

1.6 IBM 1401, IBM 1620, Interdata 16b, SDS 940, PDP-10

- Added instruction history

1.7 H316, PDP-15, PDP-8

- Added =n optional value to SHOW CPU HISTORY

2. Bugs Fixed in 3.3

2.1 SCP

- Fixed comma-separated SET options (from Dave Bryan)
- Fixed duplicate HELP displays with user-specified commands

2.2 PDP-10

- Replicated RP register state per drive
- Fixed TU to set FCE on short record
- Fixed TU to return bit<15> in drive type
- Fixed TU format specification, 1:0 are don't cares
- Fixed TU handling of TMK status
- Fixed TU handling of DONE, ATA at end of operation
- Implemented TU write check

2.3 PDP-11

- Replicated RP register state per drive
- Fixed RQ, TQ to report correct controller type and stage 1 configuration
  flags on a Unibus system
- Fixed HK CS2<output_ready> flag

2.4 VAX

- Fixed parsing of indirect displacement modes in instruction input

2.5 HP2100 (all fixes from Dave Bryan)

CPU	- fixed S-register behavior on 2116
	- fixed LIx/MIx behavior for DMA on 2116 and 2100
	- fixed LIx/MIx behavior for empty I/O card slots

DP	- fixed enable/disable from either device
	- fixed ANY ERROR status for 12557A interface
	- fixed unattached drive status for 12557A interface
	- status cmd without prior STC DC now completes (12557A)
	- OTA/OTB CC on 13210A interface also does CLC CC
	- fixed RAR model
	- fixed seek check on 13210 if sector out of range

DQ	- fixed enable/disable from either device
	- shortened xtime from 5 to 3 (drive avg 156KW/second)
	- fixed not ready/any error status
	- fixed RAR model

DR	- fixed enable/disable from either device
	- fixed sector return in status word
	- fixed DMA last word write, incomplete sector fill value
	- fixed 12610 SFC operation
	- fixed current-sector determination

IPL	- fixed enable/disable from either device

LPS	- fixed status returns for error conditions
	- fixed handling of non-printing characters
	- fixed handling of characters after column 80
	- improved timing model accuracy for RTE

LPT	- fixed status returns for error conditions
	- fixed TOF handling so form remains on line 0

SYS	- fixed display of CCA/CCB/CCE instructions

2.5 PDP-15

FPP	- fixed URFST to mask low 9b of fraction
	- fixed exception PC setting
2011-04-15 08:34:40 -07:00

1375 lines
44 KiB
C

/* pdp11_xu.c: DEUNA/DELUA ethernet controller simulator
------------------------------------------------------------------------------
Copyright (c) 2003-2004, David T. Hittner
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the author shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the author.
------------------------------------------------------------------------------
This DEUNA/DELUA simulation is based on:
Digital DELUA Users Guide, Part# EK-DELUA-UG-002
Digital DEUNA Users Guide, Part# EK-DEUNA-UG-001
These manuals can be found online at:
http://www.spies.com/~aek/pdf/dec/unibus
What this BETA version contains:
1) Basic Transmit/Receive packet functionality.
2) Promiscuous/All_Multicast/Multicast/MAC support
Testing performed:
1) Receives/Transmits single packet under custom RSX driver
2) Passes RSTS 10.1 controller diagnostics during boot
Known issues:
1) Transmit/Receive rings have not been thoroughly tested,
particularly when and where the ring pointers get reset.
2) Most auxiliary commands are not implemented yet.
3) System_ID broadcast is not implemented
4) Error/Interrupt signalling is still iffy from merge of FvK and sim_ether
------------------------------------------------------------------------------
Modification history:
16-Jan-04 DTH Added more info to SHOW MOD commands
09-Jan-04 DTH Made XU floating address so that XUB will float correctly
08-Jan-04 DTH Added system_id message
06-Jan-04 DTH Added protection against changing mac and type if attached
05-Jan-04 DTH Moved most of xu_setmac to sim_ether
Implemented auxiliary function 12/13
Added SET/SHOW XU STATS
31-Dec-03 DTH RSTS 10.1 accepts controller during boot tests
Implemented chained buffers in transmit/receive processing
29-Dec-03 DTH Primitive RSX packet sending succeeds
23-Dec-03 DTH Implemented write function
17-Dec-03 DTH Implemented read function
05-May-03 DTH Started XU simulation -
core logic pirated from unreleased FvK PDP10 variant
------------------------------------------------------------------------------
*/
#include "pdp11_xu.h"
extern int32 tmr_poll, clk_tps;
extern FILE *sim_log;
t_stat xu_rd(int32* data, int32 PA, int32 access);
t_stat xu_wr(int32 data, int32 PA, int32 access);
t_stat xu_svc(UNIT * uptr);
t_stat xu_reset (DEVICE * dptr);
t_stat xu_attach (UNIT * uptr, char * cptr);
t_stat xu_detach (UNIT * uptr);
t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc);
int32 xu_int (void);
t_stat xu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat xu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
void xua_read_callback(int status);
void xub_read_callback(int status);
void xua_write_callback(int status);
void xub_write_callback(int status);
void xu_setint (CTLR* xu);
void xu_clrint (CTLR* xu);
void xu_process_receive(CTLR* xu);
DIB xua_dib = { IOBA_XU, IOLN_XU, &xu_rd, &xu_wr,
1, IVCL (XU), VEC_XU, {&xu_int} };
UNIT xua_unit[] = {
{ UDATA (&xu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } /* receive timer */
};
struct xu_device xua = {
xua_read_callback, /* read callback routine */
xua_write_callback, /* write callback routine */
{0x08, 0x00, 0x2B, 0xCC, 0xDD, 0xEE}, /* mac */
XU_T_DELUA /* type */
};
MTAB xu_mod[] = {
#if defined (VM_PDP11)
{ MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL },
#else
{ MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL,
NULL, &show_addr, NULL },
#endif
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL,
NULL, &show_vec, NULL },
{ MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx",
&xu_setmac, &xu_showmac, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH",
NULL, &eth_show, NULL },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS",
&xu_set_stats, &xu_show_stats, NULL },
{ MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEUNA|DELUA}",
&xu_set_type, &xu_show_type, NULL },
{ 0 },
};
REG xua_reg[] = {
{ NULL } };
DEBTAB xu_debug[] = {
{"TRACE", DBG_TRC},
{"WARN", DBG_WRN},
{"REG", DBG_REG},
{"ETH", DBG_ETH},
{0}
};
DEVICE xu_dev = {
"XU", xua_unit, xua_reg, xu_mod,
1, XU_RDX, 8, 1, XU_RDX, 8,
&xu_ex, &xu_dep, &xu_reset,
NULL, &xu_attach, &xu_detach,
&xua_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG,
0, xu_debug
};
/* XUB does not exist in the PDP10 simulation */
#if defined(IOBA_XUB)
DIB xub_dib = { IOBA_XUB, IOLN_XUB, &xu_rd, &xu_wr,
1, IVCL (XU), 0, { &xu_int } };
UNIT xub_unit[] = {
{ UDATA (&xu_svc, UNIT_ATTABLE + UNIT_DISABLE, 0) } /* receive timer */
};
struct xu_device xub = {
xub_read_callback, /* read callback routine */
xub_write_callback, /* write callback routine */
{0x08, 0x00, 0x2B, 0xDD, 0xEE, 0xFF}, /* mac */
XU_T_DELUA /* type */
};
REG xub_reg[] = {
{ NULL } };
DEVICE xub_dev = {
"XUB", xub_unit, xub_reg, xu_mod,
1, XU_RDX, 8, 1, XU_RDX, 8,
&xu_ex, &xu_dep, &xu_reset,
NULL, &xu_attach, &xu_detach,
&xub_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG,
0, xu_debug
};
#define XU_MAX_CONTROLLERS 2
CTLR xu_ctrl[] = {
{&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */
,{&xub_dev, xub_unit, &xub_dib, &xub} /* XUB controller */
};
#else /* IOBA_XUB */
#define XU_MAX_CONTROLLERS 1
CTLR xu_ctrl[] = {
{&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */
};
#endif /* IOBA_XUB */
/*============================================================================*/
/* Multicontroller support */
CTLR* xu_unit2ctlr(UNIT* uptr)
{
int i;
unsigned int j;
for (i=0; i<XU_MAX_CONTROLLERS; i++)
for (j=0; j<xu_ctrl[i].dev->numunits; j++)
if (&xu_ctrl[i].unit[j] == uptr)
return &xu_ctrl[i];
/* not found */
return 0;
}
CTLR* xu_dev2ctlr(DEVICE* dptr)
{
int i;
for (i=0; i<XU_MAX_CONTROLLERS; i++)
if (xu_ctrl[i].dev == dptr)
return &xu_ctrl[i];
/* not found */
return 0;
}
CTLR* xu_pa2ctlr(uint32 PA)
{
int i;
for (i=0; i<XU_MAX_CONTROLLERS; i++)
if ((PA >= xu_ctrl[i].dib->ba) && (PA < (xu_ctrl[i].dib->ba + xu_ctrl[i].dib->lnt)))
return &xu_ctrl[i];
/* not found */
return 0;
}
/*============================================================================*/
/* stop simh from reading non-existant unit data stream */
t_stat xu_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw)
{
return SCPE_NOFNC;
}
/* stop simh from writing non-existant unit data stream */
t_stat xu_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw)
{
return SCPE_NOFNC;
}
t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc)
{
CTLR* xu = xu_unit2ctlr(uptr);
char buffer[20];
eth_mac_fmt((ETH_MAC*)xu->var->mac, buffer);
fprintf(st, "MAC=%s", buffer);
return SCPE_OK;
}
t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc)
{
t_stat status;
CTLR* xu = xu_unit2ctlr(uptr);
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
status = eth_mac_scan(&xu->var->mac, cptr);
return status;
}
t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc)
{
CTLR* xu = xu_unit2ctlr(uptr);
/* set stats to zero, regardless of passed parameter */
memset(&xu->var->stats, 0, sizeof(struct xu_stats));
return SCPE_OK;
}
t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc)
{
char* fmt = " %-24s%d\n";
CTLR* xu = xu_unit2ctlr(uptr);
struct xu_stats* stats = &xu->var->stats;
fprintf(st, "Ethernet statistics:\n");
fprintf(st, fmt, "Seconds since cleared:", stats->secs);
fprintf(st, fmt, "Recv frames:", stats->frecv);
fprintf(st, fmt, "Recv dbytes:", stats->rbytes);
fprintf(st, fmt, "Xmit frames:", stats->ftrans);
fprintf(st, fmt, "Xmit dbytes:", stats->tbytes);
fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv);
fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes);
fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans);
fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes);
return SCPE_OK;
}
t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc)
{
CTLR* xu = xu_unit2ctlr(uptr);
fprintf(st, "type=");
switch (xu->var->type) {
case XU_T_DEUNA: fprintf(st, "DEUNA"); break;
case XU_T_DELUA: fprintf(st, "DELUA"); break;
}
return SCPE_OK;
}
t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc)
{
CTLR* xu = xu_unit2ctlr(uptr);
if (!cptr) return SCPE_IERR;
if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
/* this assumes that the parameter has already been upcased */
if (!strcmp(cptr, "DEUNA")) xu->var->type = XU_T_DEUNA;
else if (!strcmp(cptr, "DELUA")) xu->var->type = XU_T_DELUA;
else return SCPE_ARG;
return SCPE_OK;
}
/*============================================================================*/
void upd_stat16(uint16* stat, uint16 add)
{
*stat += add;
/* did stat roll over? latches at maximum */
if (*stat < add)
*stat = 0xFFFF;
}
void upd_stat32(uint32* stat, uint32 add)
{
*stat += add;
/* did stat roll over? latches at maximum */
if (*stat < add)
*stat = 0xFFFFFFFF;
}
void bit_stat16(uint16* stat, uint16 bits)
{
*stat |= bits;
}
t_stat xu_process_local (CTLR* xu, ETH_PACK* pack)
{
return SCPE_NOFNC; /* not implemented yet */
}
void xu_read_callback(CTLR* xu, int status)
{
/* process any packets locally that can be */
status = xu_process_local (xu, &xu->var->read_buffer);
/* add packet to read queue */
if (status != SCPE_OK)
ethq_insert(&xu->var->ReadQ, 2, &xu->var->read_buffer, 0);
}
void xua_read_callback(int status)
{
xu_read_callback(&xu_ctrl[0], status);
}
void xub_read_callback(int status)
{
xu_read_callback(&xu_ctrl[1], status);
}
t_stat xu_system_id (CTLR* xu, const ETH_MAC dest, uint16 receipt_id)
{
static uint16 receipt = 0;
ETH_PACK system_id;
uint8* const msg = &system_id.msg[0];
t_stat status;
sim_debug(DBG_TRC, xu->dev, "xu_system_id()\n");
memset (&system_id, 0, sizeof(system_id));
memcpy (&msg[0], dest, sizeof(ETH_MAC));
memcpy (&msg[6], xu->var->setup.macs[0], sizeof(ETH_MAC));
msg[12] = 0x60; /* type */
msg[13] = 0x02; /* type */
msg[14] = 0x1C; /* character count */
msg[15] = 0x00; /* character count */
msg[16] = 0x07; /* code */
msg[17] = 0x00; /* zero pad */
if (receipt_id) {
msg[18] = receipt_id & 0xFF; /* receipt number */
msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */
} else {
msg[18] = receipt & 0xFF; /* receipt number */
msg[19] = (receipt++ >> 8) & 0xFF; /* receipt number */
}
/* MOP VERSION */
msg[20] = 0x01; /* type */
msg[21] = 0x00; /* type */
msg[22] = 0x03; /* length */
msg[23] = 0x03; /* version */
msg[24] = 0x00; /* eco */
msg[25] = 0x00; /* user eco */
/* FUNCTION */
msg[26] = 0x02; /* type */
msg[27] = 0x00; /* type */
msg[28] = 0x02; /* length */
msg[29] = 0x05; /* value 1 */
msg[30] = 0x00; /* value 2 */
/* HARDWARE ADDRESS */
msg[31] = 0x07; /* type */
msg[32] = 0x00; /* type */
msg[33] = 0x06; /* length */
memcpy (&msg[34], xu->var->mac, sizeof(ETH_MAC)); /* ROM address */
/* DEVICE TYPE */
msg[40] = 0x64; /* type */
msg[41] = 0x00; /* type */
msg[42] = 0x01; /* length */
if (xu->var->type == XU_T_DEUNA)
msg[43] = 1; /* value (1=DEUNA) */
else
msg[43] = 11; /* value (11=DELUA) */
/* write system id */
system_id.len = 60;
status = eth_write(xu->var->etherface, &system_id, NULL);
return status;
}
t_stat xu_svc(UNIT* uptr)
{
int queue_size;
t_stat status;
CTLR* xu = xu_unit2ctlr(uptr);
const int one_second = clk_tps * tmr_poll; /* recalibrate timer */
const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00};
/* First pump any queued packets into the system */
if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING))
xu_process_receive(xu);
/* Now read and queue packets that have arrived */
/* This is repeated as long as they are available and we have room */
do
{
queue_size = xu->var->ReadQ.count;
/* read a packet from the ethernet - processing is via the callback */
status = eth_read (xu->var->etherface, &xu->var->read_buffer, xu->var->rcallback);
} while (queue_size != xu->var->ReadQ.count);
/* Now pump any still queued packets into the system */
if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING))
xu_process_receive(xu);
/* send identity packet when timer expires */
if (--xu->var->idtmr <= 0) {
if ((xu->var->mode & MODE_DMNT) == 0) /* if maint msg is not disabled */
status = xu_system_id(xu, mop_multicast, 0); /* then send ID packet */
xu->var->idtmr = XU_ID_TIMER_VAL; /* reset timer */
}
/* has one second timer expired? if so, update stats and reset timer */
if (++xu->var->sectmr >= XU_SERVICE_INTERVAL) {
upd_stat16 (&xu->var->stats.secs, 1);
xu->var->sectmr = 0;
}
/* resubmit service timer if controller not halted */
switch (xu->var->pcsr1 & PCSR1_STATE) {
case STATE_READY:
case STATE_RUNNING:
sim_activate(&xu->unit[0], one_second/XU_SERVICE_INTERVAL);
break;
};
return SCPE_OK;
}
void xu_write_callback (CTLR* xu, int status)
{
xu->var->write_buffer.status = status;
}
void xua_write_callback (int status)
{
xu_write_callback(&xu_ctrl[0], status);
}
void xub_write_callback (int status)
{
xu_write_callback(&xu_ctrl[1], status);
}
void xu_setclrint(CTLR* xu, int32 bits)
{
if (xu->var->pcsr0 & 0xFF00) { /* if any interrupt bits on, */
xu->var->pcsr0 |= PCSR0_INTR; /* turn master bit on */
xu_setint(xu); /* and trigger interrupt */
} else {
xu->var->pcsr0 &= ~PCSR0_INTR; /* ... or off */
xu_clrint(xu); /* and clear interrupt if needed*/
}
}
t_stat xu_sw_reset (CTLR* xu)
{
t_stat status;
const int one_second = clk_tps * tmr_poll; /* recalibrate timer */
sim_debug(DBG_TRC, xu->dev, "xu_sw_reset()\n");
/* Clear the registers. */
xu->var->pcsr0 = 0;
xu->var->pcsr1 = STATE_READY;
switch (xu->var->type) {
case XU_T_DELUA: xu->var->pcsr1 |= TYPE_DELUA; break;
case XU_T_DEUNA: xu->var->pcsr1 |= TYPE_DEUNA; break;
}
xu->var->pcsr2 = 0;
xu->var->pcsr3 = 0;
/* Clear the parameters. */
xu->var->mode = 0;
xu->var->pcbb = 0;
xu->var->stat = 0;
/* clear read queue */
ethq_clear(&xu->var->ReadQ);
/* clear setup info */
memset(&xu->var->setup, 0, sizeof(struct xu_setup));
/* clear network statistics */
memset(&xu->var->stats, 0, sizeof(struct xu_stats));
/* reset ethernet interface */
memcpy (xu->var->setup.macs[0], xu->var->mac, sizeof(ETH_MAC));
xu->var->setup.mac_count = 1;
if (xu->var->etherface)
status = eth_filter (xu->var->etherface, xu->var->setup.mac_count,
&xu->var->mac, xu->var->setup.multicast,
xu->var->setup.promiscuous);
/* activate device if not disabled - cancel first, just in case */
if ((xu->dev->flags & DEV_DIS) == 0) {
sim_cancel(&xu->unit[0]);
sim_activate(&xu->unit[0], one_second/XU_SERVICE_INTERVAL);
}
return SCPE_OK;
}
/* Reset device. */
t_stat xu_reset(DEVICE* dptr)
{
t_stat status;
CTLR* xu = xu_dev2ctlr(dptr);
/* init read queue (first time only) */
status = ethq_init (&xu->var->ReadQ, XU_QUE_MAX);
if (status != SCPE_OK)
return status;
/* software reset controller */
xu_sw_reset(xu);
return SCPE_OK;
}
/* Perform one of the defined ancillary functions. */
int32 xu_command(CTLR* xu)
{
uint32 udbb;
int fnc, mtlen;
uint16 value;
t_stat status, rstatus, wstatus, wstatus2, wstatus3;
struct xu_stats* stats = &xu->var->stats;
uint16* udb = xu->var->udb;
static char* command[] = {
"NO-OP",
"Start Microaddress",
"Read Default Physical Address",
"NO-OP",
"Read Physical Address",
"Write Physical Address",
"Read Multicast Address List",
"Write Multicast Address List",
"Read Descriptor Ring Format",
"Write Descriptor Ring Format",
"Read Counters",
"Read/Clear Counters",
"Read Mode Register",
"Write Mode Register",
"Read Status",
"Read/Clear Status",
"Dump Internal Memory",
"Load Internal Memory",
"Read System ID",
"Write System ID",
"Read Load Server Address",
"Write Load Server Address"
};
/* Grab the PCB from the host. */
rstatus = Map_ReadW(xu->var->pcbb, 8, xu->var->pcb);
if (rstatus != SCPE_OK)
return PCSR0_PCEI + 1;
/* High 8 bits are defined as MBZ. */
if (xu->var->pcb[0] & 0177400) return PCSR0_PCEI;
/* Decode the function to be performed. */
fnc = xu->var->pcb[0] & 0377;
sim_debug(DBG_TRC, xu->dev, "xu_command(), Command: %s [0%o]\n", command[fnc], fnc);
switch (fnc) {
case FC_NOOP:
break;
case FC_RDPA: /* read default physical address */
wstatus = Map_WriteW(xu->var->pcbb + 2, 6, (uint16*)xu->var->mac);
if (wstatus != SCPE_OK)
return PCSR0_PCEI + 1;
break;
case FC_RPA: /* read current physical address */
wstatus = Map_WriteW(xu->var->pcbb + 2, 6, (uint16*)&xu->var->setup.macs[0]);
if (wstatus != SCPE_OK)
return PCSR0_PCEI + 1;
break;
case FC_WPA: /* write current physical address */
rstatus = Map_ReadW(xu->var->pcbb + 2, 6, (uint16*)&xu->var->setup.macs[0]);
if (xu->var->pcb[1] & 1) return PCSR0_PCEI;
break;
case FC_WMAL: /* write multicast address list */
mtlen = (xu->var->pcb[2] & 0xFF00) >> 8;
if (mtlen > 10) return PCSR0_PCEI;
udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16);
rstatus = Map_ReadW(udbb, mtlen * 6, (uint16*) &xu->var->setup.macs[1]);
if (rstatus == SCPE_OK) {
xu->var->setup.mac_count = mtlen + 1;
status = eth_filter (xu->var->etherface, xu->var->setup.mac_count,
xu->var->setup.macs, xu->var->setup.multicast,
xu->var->setup.promiscuous);
} else {
xu->var->pcsr0 |= PCSR0_PCEI;
}
break;
case FC_RRF: /* read ring format */
if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374))
return PCSR0_PCEI;
xu->var->udb[0] = xu->var->tdrb & 0177776;
xu->var->udb[1] = (xu->var->telen << 8) + ((xu->var->tdrb >> 16) & 3);
xu->var->udb[2] = xu->var->trlen;
xu->var->udb[3] = xu->var->rdrb & 0177776;
xu->var->udb[4] = (xu->var->relen << 8) + ((xu->var->rdrb >> 16) & 3);
xu->var->udb[5] = xu->var->rrlen;
/* Write UDB to host memory. */
udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);
wstatus = Map_WriteW(udbb, 12, xu->var->pcb);
if (wstatus != SCPE_OK)
return PCSR0_PCEI+1;
break;
case FC_WRF: /* write ring format */
if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374))
return PCSR0_PCEI;
if ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING)
return PCSR0_PCEI;
/* Read UDB into local memory. */
udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);
rstatus = Map_ReadW(udbb, 12, xu->var->udb);
if (rstatus != SCPE_OK)
return PCSR0_PCEI+1;
if ((xu->var->udb[0] & 1) || (xu->var->udb[1] & 0374) ||
(xu->var->udb[3] & 1) || (xu->var->udb[4] & 0374) ||
(xu->var->udb[5] < 2)) {
return PCSR0_PCEI;
}
xu->var->tdrb = ((xu->var->udb[1] & 3) << 16) + (xu->var->udb[0] & 0177776);
xu->var->telen = (xu->var->udb[1] >> 8) & 0377;
xu->var->trlen = xu->var->udb[2];
xu->var->rdrb = ((xu->var->udb[4] & 3) << 16) + (xu->var->udb[3] & 0177776);
xu->var->relen = (xu->var->udb[4] >> 8) & 0377;
xu->var->rrlen = xu->var->udb[5];
xu->var->rxnext = 0;
xu->var->txnext = 0;
break;
case FC_RDCTR: /* read counters */
case FC_RDCLCTR: /* read and clear counters */
/* prepare udb for stats transfer */
memset(xu->var->udb, 0, sizeof(xu->var->udb));
/* place stats in udb */
udb[0] = 68; /* udb length */
udb[1] = stats->secs; /* seconds since zeroed */
udb[2] = stats->frecv & 0xFFFF; /* frames received <15:00> */
udb[3] = stats->frecv >> 16; /* frames received <31:16> */
udb[4] = stats->mfrecv & 0xFFFF; /* multicast frames received <15:00> */
udb[5] = stats->mfrecv >> 16; /* multicast frames received <31:16> */
udb[6] = stats->rxerf; /* receive error status bits */
udb[7] = stats->frecve; /* frames received with error */
udb[8] = stats->rbytes & 0xFFFF; /* data bytes received <15:00> */
udb[9] = stats->rbytes >> 16; /* data bytes received <31:16> */
udb[10] = stats->mrbytes & 0xFFFF; /* multicast data bytes received <15:00> */
udb[11] = stats->mrbytes >> 16; /* multicast data bytes received <31:16> */
udb[12] = stats->rlossi; /* received frames lost - internal buffer */
udb[13] = stats->rlossl; /* received frames lost - local buffer */
udb[14] = stats->ftrans & 0xFFFF; /* frames transmitted <15:00> */
udb[15] = stats->ftrans >> 16; /* frames transmitted <31:16> */
udb[16] = stats->mftrans & 0xFFFF; /* multicast frames transmitted <15:00> */
udb[17] = stats->mftrans >> 16; /* multicast frames transmitted <31:16> */
udb[18] = stats->ftrans3 & 0xFFFF; /* frames transmitted 3+ tries <15:00> */
udb[19] = stats->ftrans3 >> 16; /* frames transmitted 3+ tries <31:16> */
udb[20] = stats->ftrans2 & 0xFFFF; /* frames transmitted 2 tries <15:00> */
udb[21] = stats->ftrans2 >> 16; /* frames transmitted 2 tries <31:16> */
udb[22] = stats->ftransd & 0xFFFF; /* frames transmitted deferred <15:00> */
udb[23] = stats->ftransd >> 16; /* frames transmitted deferred <31:16> */
udb[24] = stats->tbytes & 0xFFFF; /* data bytes transmitted <15:00> */
udb[25] = stats->tbytes >> 16; /* data bytes transmitted <31:16> */
udb[26] = stats->mtbytes & 0xFFFF; /* multicast data bytes transmitted <15:00> */
udb[27] = stats->mtbytes >> 16; /* multicast data bytes transmitted <31:16> */
udb[28] = stats->txerf; /* transmit frame error status bits */
udb[29] = stats->ftransa; /* transmit frames aborted */
udb[30] = stats->txccf; /* transmit collision check failure */
udb[31] = 0; /* MBZ */
udb[32] = stats->porterr; /* port driver error */
udb[33] = stats->bablcnt; /* babble counter */
/* transfer udb to host */
udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16);
wstatus = Map_WriteW(udbb, 68, xu->var->udb);
if (wstatus != SCPE_OK) {
xu->var->pcsr0 |= PCSR0_PCEI;
}
/* if clear function, clear network stats */
if (fnc == FC_RDCLCTR)
memset(stats, 0, sizeof(struct xu_stats));
break;
case FC_RMODE: /* read mode register */
value = xu->var->mode;
wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value);
if (wstatus != SCPE_OK)
return PCSR0_PCEI + 1;
break;
case FC_WMODE: /* write mode register */
value = xu->var->mode;
xu->var->mode = xu->var->pcb[1];
/* set promiscuous and multicast flags */
xu->var->setup.promiscuous = (xu->var->mode & MODE_PROM) ? 1 : 0;
xu->var->setup.multicast = (xu->var->mode & MODE_ENAL) ? 1 : 0;
/* if promiscuous or multicast flags changed, change filter */
if ((value ^ xu->var->mode) & (MODE_PROM | MODE_ENAL))
status = eth_filter (xu->var->etherface, xu->var->setup.mac_count,
&xu->var->mac, xu->var->setup.multicast,
xu->var->setup.promiscuous);
break;
case FC_RSTAT: /* read extended status */
case FC_RCSTAT: /* read and clear extended status */
value = xu->var->stat;
wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value);
value = 10;
wstatus2 = Map_WriteW(xu->var->pcbb+4, 2, &value);
value = 32;
wstatus3 = Map_WriteW(xu->var->pcbb+6, 2, &value);
if ((wstatus != SCPE_OK) || (wstatus2 != SCPE_OK) || (wstatus3 != SCPE_OK))
return PCSR0_PCEI + 1;
if (fnc = FC_RCSTAT)
xu->var->stat &= 0377; /* clear high byte */
break;
default: /* Unknown (unimplemented) command. */
printf("%s: unknown ancilliary command 0%o requested !\n", xu->dev->name, fnc);
return PCSR0_PCEI;
break;
} /* switch */
return PCSR0_DNI;
}
/* Transfer received packets into receive ring. */
void xu_process_receive(CTLR* xu)
{
uint32 segb, ba;
int slen, wlen, off;
t_stat rstatus, wstatus;
ETH_ITEM* item = 0;
int state = xu->var->pcsr1 & PCSR1_STATE;
int no_buffers = xu->var->pcsr0 & PCSR0_RCBI;
sim_debug(DBG_TRC, xu->dev, "xu_process_receive(), buffers: %d\n", xu->var->rrlen);
/* process only when in the running state, and host buffers are available */
if ((state != STATE_RUNNING) || no_buffers)
return;
/* check read queue for buffer loss */
if (xu->var->ReadQ.loss) {
upd_stat16(&xu->var->stats.rlossl, (uint16) xu->var->ReadQ.loss);
xu->var->ReadQ.loss = 0;
}
/* while there are still packets left to process in the queue */
while (xu->var->ReadQ.count > 0) {
/* get next receive buffer */
ba = xu->var->rdrb + (xu->var->relen * 2) * xu->var->rxnext;
rstatus = Map_ReadW (ba, 8, xu->var->rxhdr);
if (rstatus) {
/* tell host bus read failed */
xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG;
xu->var->pcsr0 |= PCSR0_SERI;
break;
}
/* if buffer not owned by controller, exit [at end of ring] */
if (!(xu->var->rxhdr[2] & RXR_OWN)) {
/* tell the host there are no more buffers */
xu->var->pcsr0 |= PCSR0_RCBI;
break;
}
/* set buffer length and address */
slen = xu->var->rxhdr[0];
segb = xu->var->rxhdr[1] + ((xu->var->rxhdr[2] & 3) << 16);
/* get first packet from receive queue */
if (!item) {
item = &xu->var->ReadQ.item[xu->var->ReadQ.head];
/*
* 2.11BSD does not seem to like small packets.
* For example.. an incoming ARP packet is:
* ETH dstaddr [6]
* ETH srcaddr [6]
* ETH type [2]
* ARP arphdr [8]
* ARP dstha [6]
* ARP dstpa [4]
* ARP srcha [6]
* ARP srcpa [4]
*
* for a total of 42 bytes. According to the 2.11BSD
* driver for DEUNA (if_de.c), this is not a legal size,
* and the packet is dropped. Therefore, we pad the
* thing to minimum size here. Stupid runts...
*/
if (item->packet.len < ETH_MIN_PACKET) {
int len = item->packet.len;
memset (&item->packet.msg[len], 0, ETH_MIN_PACKET - len);
item->packet.len = ETH_MIN_PACKET;
}
}
/* is this the start of frame? */
if (item->packet.used == 0) {
xu->var->rxhdr[2] |= RXR_STF;
off = 0;
}
/* figure out chained packet size */
wlen = item->packet.len - item->packet.used;
if (wlen > slen)
wlen = slen;
/* transfer chained packet to host buffer */
wstatus = Map_WriteB (segb, wlen, &item->packet.msg[off]);
if (wstatus) {
/* error during write */
xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG;
xu->var->pcsr0 |= PCSR0_SERI;
break;
}
/* update chained counts */
item->packet.used += wlen;
off += wlen;
/* Is this the end-of-frame? */
if (item->packet.used == item->packet.len) {
/* mark end-of-frame */
xu->var->rxhdr[2] |= RXR_ENF;
/*
* Fill in the Received Message Length field.
* The documenation notes that the DEUNA actually performs
* a full CRC check on the data buffer, and adds this CRC
* value to the data, in the last 4 bytes. The question
* is: does MLEN include these 4 bytes, or not??? --FvK
*
* A quick look at the RSX Process Software driver shows
* that the CRC byte count(4) is added to MLEN, but does
* not show if the DEUNA/DELUA actually transfers the
* CRC bytes to the host buffers, since the driver never
* tries to use them. However, since the host max buffer
* size is only 1514, not 1518, I doubt the CRC is actually
* transferred in normal mode. Maybe CRC is transferred
* and used in Loopback mode.. -- DTH
*/
xu->var->rxhdr[3] &= ~RXR_MLEN;
xu->var->rxhdr[3] |= (item->packet.len + 4/*CRC*/);
/* update stats */
upd_stat32(&xu->var->stats.frecv, 1);
upd_stat32(&xu->var->stats.rbytes, item->packet.len - 14);
if (item->packet.msg[0] & 1) { /* multicast? */
upd_stat32(&xu->var->stats.mfrecv, 1);
upd_stat32(&xu->var->stats.mrbytes, item->packet.len - 14);
}
/* remove processed packet from the receive queue */
ethq_remove (&xu->var->ReadQ);
item = 0;
/* tell host we received a packet */
xu->var->pcsr0 |= PCSR0_RXI;
} /* if end-of-frame */
/* give buffer back to host */
xu->var->rxhdr[2] &= ~RXR_OWN; /* clear ownership flag */
/* update the ring entry in host memory. */
wstatus = Map_WriteW (ba, 8, xu->var->rxhdr);
if (wstatus) {
/* tell host bus write failed */
xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG;
xu->var->pcsr0 |= PCSR0_SERI;
/* if this was end-of-frame, log frame loss */
if (xu->var->rxhdr[2] & RXR_ENF)
upd_stat16(&xu->var->stats.rlossi, 1);
}
/* set to next receive ring buffer */
xu->var->rxnext += 1;
if (xu->var->rxnext == xu->var->rrlen)
xu->var->rxnext = 0;
} /* while */
/* if we failed to finish receiving the frame, flush the packet */
if (item) {
ethq_remove(&xu->var->ReadQ);
upd_stat16(&xu->var->stats.rlossl, 1);
}
/* set or clear interrupt, depending on what happened */
xu_setclrint(xu, 0);
}
void xu_process_transmit(CTLR* xu)
{
uint32 segb, ba;
int slen, wlen, i, off, giant, runt;
t_stat rstatus, wstatus;
const ETH_MAC zeros = {0, 0, 0, 0, 0, 0};
sim_debug(DBG_TRC, xu->dev, "xu_process_transmit()\n");
for (;;) {
/* get next transmit buffer */
ba = xu->var->tdrb + (xu->var->telen * 2) * xu->var->txnext;
rstatus = Map_ReadW (ba, 8, xu->var->txhdr);
if (rstatus) {
/* tell host bus read failed */
xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG;
xu->var->pcsr0 |= PCSR0_SERI;
break;
}
/* if buffer not owned by controller, exit [at end of ring] */
if (!(xu->var->txhdr[2] & TXR_OWN))
break;
/* set buffer length and address */
slen = xu->var->txhdr[0];
segb = xu->var->txhdr[1] + ((xu->var->txhdr[2] & 3) << 16);
wlen = slen;
/* prepare to accumulate transmit information if start of frame */
if (xu->var->txhdr[2] & TXR_STF) {
memset(&xu->var->write_buffer, 0, sizeof(ETH_PACK));
off = giant = runt = 0;
}
/* get packet data from host */
if (xu->var->write_buffer.len + slen > ETH_MAX_PACKET) {
wlen = ETH_MAX_PACKET - xu->var->write_buffer.len;
giant = 1;
}
if (wlen > 0) {
rstatus = Map_ReadB(segb, wlen, &xu->var->write_buffer.msg[off]);
if (rstatus) {
/* tell host bus read failed */
xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG;
xu->var->pcsr0 |= PCSR0_SERI;
break;
}
}
off += wlen;
xu->var->write_buffer.len += wlen;
/* transmit packet when end-of-frame is reached */
if (xu->var->txhdr[2] & TXR_ENF) {
/* make sure packet is minimum length */
if ((xu->var->write_buffer.len < ETH_MIN_PACKET) && (xu->var->mode & MODE_TPAD)) {
xu->var->write_buffer.len = ETH_MIN_PACKET;
runt = 1;
}
/* are we in internal loopback mode ? */
if ((xu->var->mode & MODE_LOOP) && (xu->var->mode & MODE_INTL)) {
/* just put packet in receive buffer */
ethq_insert (&xu->var->ReadQ, 1, &xu->var->write_buffer, 0);
} else {
/* transmit packet synchronously - write callback sets status */
wstatus = eth_write(xu->var->etherface, &xu->var->write_buffer, xu->var->wcallback);
if (wstatus)
xu->var->pcsr0 |= PCSR0_PCEI;
}
/* update transmit status in transmit buffer */
if (xu->var->write_buffer.status != 0) {
/* failure */
const uint16 tdr = 100 + wlen * 8; /* arbitrary value */
xu->var->txhdr[3] |= TXR_RTRY;
xu->var->txhdr[3] |= tdr & TXR_TDR;
xu->var->txhdr[2] |= TXR_ERRS;
}
/* was packet too big or too small? */
if (giant || runt) {
xu->var->txhdr[3] |= TXR_BUFL;
xu->var->txhdr[2] |= TXR_ERRS;
}
/* was packet self-addressed? */
for (i=0; i<XU_FILTER_MAX; i++)
if (memcmp(xu->var->write_buffer.msg, xu->var->setup.macs[i], sizeof(ETH_MAC)) == 0)
xu->var->txhdr[2] |= TXR_MTCH;
/* tell host we transmitted a packet */
xu->var->pcsr0 |= PCSR0_TXI;
/* update stats */
upd_stat32(&xu->var->stats.ftrans, 1);
upd_stat32(&xu->var->stats.tbytes, xu->var->write_buffer.len - 14);
if (xu->var->write_buffer.msg[0] & 1) { /* multicast? */
upd_stat32(&xu->var->stats.mftrans, 1);
upd_stat32(&xu->var->stats.mtbytes, xu->var->write_buffer.len - 14);
}
if (giant)
bit_stat16(&xu->var->stats.txerf, 0x10);
} /* if end-of-frame */
/* give buffer ownership back to host */
xu->var->txhdr[2] &= ~TXR_OWN;
/* update transmit buffer */
wstatus = Map_WriteW (ba, 8, xu->var->txhdr);
if (wstatus) {
/* tell host bus write failed */
xu->var->pcsr0 |= PCSR0_PCEI;
/* update stats */
upd_stat16(&xu->var->stats.ftransa, 1);
break;
}
/* set to next transmit ring buffer */
xu->var->txnext += 1;
if (xu->var->txnext == xu->var->trlen)
xu->var->txnext = 0;
} /* while */
}
void xu_port_command (CTLR* xu)
{
char* msg;
int command = xu->var->pcsr0 & PCSR0_PCMD;
int state = xu->var->pcsr1 & PCSR1_STATE;
int bits;
static char* commands[] = {
"NO-OP",
"GET PCBB",
"GET CMD",
"SELFTEST",
"START",
"BOOT",
"Reserved NO-OP",
"Reserved NO-OP",
"PDMD",
"Reserved NO-OP",
"Reserved NO-OP",
"Reserved NO-OP",
"Reserved NO-OP",
"Reserved NO-OP",
"HALT",
"STOP"
};
sim_debug(DBG_TRC, xu->dev, "xu_port_command(), Command = %s [0%o]\n", commands[command], command);
switch (command) {
case CMD_NOOP: /* NO-OP */
/* NOOP does NOT set DNI */
break;
case CMD_GETPCBB: /* GET PCB-BASE */
xu->var->pcbb = (xu->var->pcsr3 << 16) | xu->var->pcsr2;
xu->var->pcsr0 |= PCSR0_DNI;
break;
case CMD_GETCMD: /* GET COMMAND */
bits = xu_command(xu);
xu->var->pcsr0 |= PCSR0_DNI;
break;
case CMD_SELFTEST: /* SELFTEST */
xu_sw_reset(xu);
xu->var->pcsr0 |= PCSR0_DNI;
break;
case CMD_START: /* START */
if (state == STATE_READY) {
xu->var->pcsr1 &= ~PCSR1_STATE;
xu->var->pcsr1 |= STATE_RUNNING;
xu->var->pcsr0 |= PCSR0_DNI;
/* reset ring pointers */
xu->var->rxnext = 0;
xu->var->txnext = 0;
} else
xu->var->pcsr0 |= PCSR0_PCEI;
break;
case CMD_BOOT: /* BOOT */
/* not implemented */
msg = "%s: BOOT command not implemented!\n";
printf (msg, xu->dev->name);
if (sim_log) fprintf(sim_log, msg, xu->dev->name);
xu->var->pcsr0 |= PCSR0_PCEI;
break;
case CMD_PDMD: /* POLLING DEMAND */
/* process transmit buffers, receive buffers are done in the service timer */
xu_process_transmit(xu);
xu->var->pcsr0 |= PCSR0_DNI;
break;
case CMD_HALT: /* HALT */
if ((state == STATE_READY) || (state == STATE_RUNNING)) {
sim_cancel (&xu->unit[0]); /* cancel service timer */
xu->var->pcsr1 &= ~PCSR1_STATE;
xu->var->pcsr1 |= STATE_HALT;
xu->var->pcsr0 |= PCSR0_DNI;
} else
xu->var->pcsr0 |= PCSR0_PCEI;
break;
case CMD_STOP: /* STOP */
if (state == STATE_RUNNING) {
xu->var->pcsr1 &= ~PCSR1_STATE;
xu->var->pcsr1 |= STATE_READY;
xu->var->pcsr0 |= PCSR0_DNI;
} else
xu->var->pcsr0 |= PCSR0_PCEI;
break;
case CMD_RSV06: /* RESERVED */
case CMD_RSV07: /* RESERVED */
case CMD_RSV11: /* RESERVED */
case CMD_RSV12: /* RESERVED */
case CMD_RSV13: /* RESERVED */
case CMD_RSV14: /* RESERVED */
case CMD_RSV15: /* RESERVED */
xu->var->pcsr0 |= PCSR0_DNI;
break; /* all reserved commands act as a no-op but set DNI */
} /* switch */
/* set interrupt if needed */
xu_setclrint(xu, 0);
}
t_stat xu_rd(int32 *data, int32 PA, int32 access)
{
CTLR* xu = xu_pa2ctlr(PA);
int reg = (PA >> 1) & 03;
sim_debug(DBG_TRC, xu->dev, "xu_rd(), PCSR%d\n", reg);
if (PA & 1)
sim_debug(DBG_WRN, xu->dev, "xu_rd(), Unexpected Odd address access of PCSR%d\n", reg);
switch (reg) {
case 00:
*data = xu->var->pcsr0;
break;
case 01:
*data = xu->var->pcsr1;
break;
case 02:
*data = xu->var->pcsr2;
break;
case 03:
*data = xu->var->pcsr3;
break;
}
return SCPE_OK;
}
t_stat xu_wr(int32 data, int32 PA, int32 access)
{
CTLR* xu = xu_pa2ctlr(PA);
int reg = (PA >> 1) & 03;
sim_debug(DBG_TRC, xu->dev, "xu_wr(), PCSR%d\n", reg);
switch (reg) {
case 00:
if (access == WRITEB) {
data &= 0377;
if (PA & 1) {
/* Handle WriteOneToClear trick. */
xu->var->pcsr0 &= ~((data << 8) & 0177400);
/* set/reset interrupt */
xu_setclrint(xu, 0);
/* Bail out early to avoid PCMD crap. */
break;
}
}
/* RESET function requested? */
if (data & PCSR0_RSET) {
xu_sw_reset(xu);
xu->var->pcsr0 |= PCSR0_DNI;
xu_setclrint(xu, 0);
return SCPE_OK;
}
/* Nope. Handle the INTE interlock thingie. */
if ((xu->var->pcsr0 ^ data) & PCSR0_INTE) {
xu->var->pcsr0 ^= PCSR0_INTE;
xu->var->pcsr0 |= PCSR0_DNI;
} else {
/* Normal write, no interlock. */
xu->var->pcsr0 &= ~PCSR0_PCMD;
xu->var->pcsr0 |= (data & PCSR0_PCMD);
xu_port_command(xu);
}
/* We might have changed the interrupt sys. */
xu_setclrint(xu, 0);
break;
case 01:
sim_debug(DBG_WRN, xu->dev, "xu_wr(), invalid write access on PCSR1!\n");
break;
case 02:
xu->var->pcsr2 = data & 0177776; /* store word, but not MBZ LSB */
break;
case 03:
xu->var->pcsr3 = data & 0000003; /* store significant bits */
break;
}
return SCPE_OK;
}
/* attach device: */
t_stat xu_attach(UNIT* uptr, char* cptr)
{
t_stat status;
char* tptr;
CTLR* xu = xu_unit2ctlr(uptr);
sim_debug(DBG_TRC, xu->dev, "xu_attach(cptr=%s)\n", cptr);
tptr = malloc(strlen(cptr) + 1);
if (tptr == NULL) return SCPE_MEM;
strcpy(tptr, cptr);
xu->var->etherface = malloc(sizeof(ETH_DEV));
if (!xu->var->etherface) return SCPE_MEM;
status = eth_open(xu->var->etherface, cptr, xu->dev, DBG_ETH);
if (status != SCPE_OK) {
free(tptr);
free(xu->var->etherface);
xu->var->etherface = 0;
return status;
}
uptr->filename = tptr;
uptr->flags |= UNIT_ATT;
/* reset the device with the new attach info */
xu_reset(xu->dev);
return SCPE_OK;
}
/* detach device: */
t_stat xu_detach(UNIT* uptr)
{
t_stat status;
CTLR* xu = xu_unit2ctlr(uptr);
sim_debug(DBG_TRC, xu->dev, "xu_detach()\n");
if (uptr->flags & UNIT_ATT) {
status = eth_close (xu->var->etherface);
free(xu->var->etherface);
xu->var->etherface = 0;
free(uptr->filename);
uptr->filename = NULL;
uptr->flags &= ~UNIT_ATT;
}
return SCPE_OK;
}
void xu_setint(CTLR* xu)
{
if (xu->var->pcsr0 & PCSR0_INTE) {
xu->var->irq = 1;
SET_INT(XU);
}
return;
}
void xu_clrint(CTLR* xu)
{
int i;
xu->var->irq = 0; /* set controller irq off */
/* clear master interrupt? */
for (i=0; i<XU_MAX_CONTROLLERS; i++) /* check all controllers.. */
if (xu_ctrl[i].var->irq) { /* if any irqs enabled */
SET_INT(XU); /* set master interrupt on */
return;
}
CLR_INT(XU); /* clear master interrupt */
return;
}
int32 xu_int (void)
{
int i;
for (i=0; i<XU_MAX_CONTROLLERS; i++) {
CTLR* xu = &xu_ctrl[i];
if (xu->var->irq) { /* if interrupt pending */
xu_clrint(xu); /* clear interrupt */
return xu->dib->vec; /* return vector */
}
}
return 0; /* no interrupt request active */
}