/* h316_imp.c- BBN ARPAnet IMP/TIP Specific Hardware | |
Based on the SIMH simulator package written by Robert M Supnik. | |
Copyright (c) 2013 Robert Armstrong, bob@jfcl.com. | |
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 | |
ROBERT ARMSTRONG 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 Robert Armstrong shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Robert Armstrong. | |
tks task switch device | |
mlc multiline controller (aka TIP) | |
21-May-13 RLA New file | |
OVERVIEW | |
This module implements the IMP pseudo device - this hack takes care of two | |
custom devices in the IMP hardware - device 041, which implements task | |
switching and the RDIMPN instruction, and device 42, which implements the | |
AMIMLC ("am I a multiline controller") instruction. This module also contains | |
a few miscellaneous routines which are used by the IMP support in general. | |
IMP state is maintained in a set of state variables: | |
MLC always zero (TIP flag) | |
IEN task interrupt enabled | |
IRQ task interrupt pending | |
TODO | |
*/ | |
#ifdef VM_IMPTIP | |
#include "h316_defs.h" // H316 emulator definitions | |
#include "h316_imp.h" // ARPAnet IMP/TIP definitions | |
// Locals ... | |
uint16 imp_station = IMP_STATION; // IMP number (or address) | |
uint16 imp_ismlc = 0; // 1 for MLC (not yet implemented!) | |
// Externals from other parts of simh ... | |
extern uint16 dev_ext_int, dev_ext_enb; // current IRQ and IEN bit vectors | |
extern int32 PC; // current PC (for debug messages) | |
extern int32 stop_inst; // needed by IOBADFNC() | |
// Forward declarations ... | |
int32 imp_io (int32 inst, int32 fnc, int32 dat, int32 dev); | |
t_stat imp_service (UNIT *uptr); | |
t_stat imp_reset (DEVICE *dptr); | |
t_stat imp_show_station (FILE *st, UNIT *uptr, int32 val, void *dp); | |
t_stat io_show_int (FILE *st, UNIT *uptr, int32 val, void *dp); | |
t_stat imp_set_station (UNIT *uptr, int32 val, char *cptr, void *dp); | |
t_stat io_set_int (UNIT *uptr, int32 val, char *cptr, void *dp); | |
//////////////////////////////////////////////////////////////////////////////// | |
////////////////////// D A T A S T R U C T U R E S ////////////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// IMP device information block ... | |
DIB imp_dib = { IMP, 2, IOBUS, IOBUS, INT_V_TASK, INT_V_NONE, &imp_io, 0 }; | |
// IMP unit data (we have only one!) ... | |
UNIT imp_unit = { UDATA (&imp_service, 0, 0) }; | |
// IMP device registers (for "EXAMINE IMP STATE") ... | |
REG imp_reg[] = { | |
{ FLDATA (MLC, imp_ismlc, 0), REG_RO }, | |
{ FLDATA (IEN, dev_ext_enb, INT_V_TASK-INT_V_EXTD) }, | |
{ FLDATA (IRQ, dev_ext_int, INT_V_TASK-INT_V_EXTD) }, | |
{ NULL } | |
}; | |
// IMP device modifiers (for "SET/SHOW IMP xxx") ... | |
MTAB imp_mod[] = { | |
{ MTAB_XTD|MTAB_VDV, 0, "NUM", "NUM", &imp_set_station, &imp_show_station, NULL }, | |
{ 0 } | |
}; | |
// IMP debugging flags (for "SET IMP DEBUG=xxx") ... | |
DEBTAB imp_debug[] = { | |
{"WARN", IMP_DBG_WARN}, | |
{"IO", IMP_DBG_IOT}, | |
{0} | |
}; | |
// And finally tie it all together ... | |
DEVICE imp_dev = { | |
"IMP", &imp_unit, imp_reg, imp_mod, | |
1, 0, 0, 0, 0, 0, | |
NULL, NULL, &imp_reset, NULL, NULL, NULL, | |
&imp_dib, DEV_DIS|DEV_DISABLE|DEV_DEBUG, 0, imp_debug, NULL, NULL | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
////////// I M P I / O A N D S E R V I C E R O U T I N E S ////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// Set and clear the TASK IRQ and IEN ... | |
#define SET_TASK_IRQ() SET_EXT_INT((1u << (imp_dib.inum - INT_V_EXTD))) | |
#define CLR_TASK_IRQ() CLR_EXT_INT((1u << (imp_dib.inum - INT_V_EXTD))) | |
#define CLR_TASK_IEN() CLR_EXT_ENB((1u << (imp_dib.inum - INT_V_EXTD))) | |
// IMP I/O routine ... | |
int32 imp_io (int32 inst, int32 fnc, int32 dat, int32 dev) | |
{ | |
if (dev == IMP) { | |
if ((inst == ioOCP) && (fnc == 000)) { | |
// TASK - just set the task interrupt request bit ... | |
sim_debug(IMP_DBG_IOT, &imp_dev, "request task interrupt (PC=%06o)\n", PC-1); | |
SET_TASK_IRQ(); return dat; | |
} else if ((inst == ioINA) && ((fnc == 010) || (fnc == 000))) { | |
// RDIMPN - return the IMP address and always skip ... | |
sim_debug(IMP_DBG_IOT, &imp_dev, "read address (PC=%06o)\n", PC-1); | |
return IOSKIP(imp_station); | |
} | |
} else if (dev == IMP+1) { | |
if ((inst == ioSKS) && (fnc == 000)) { | |
// AMIMLC - skip if this machine is an MLC ... | |
sim_debug(IMP_DBG_IOT, &imp_dev, "skip on MLC (PC=%06o %s)\n", PC-1, imp_ismlc ? "SKIP" : "NOSKIP"); | |
if (imp_ismlc != 0) return IOSKIP(dat); else return dat; | |
} | |
} | |
// Anything else is an error... | |
sim_debug(IMP_DBG_WARN, &imp_dev, "UNIMPLEMENTED I/O (PC=%06o, instruction=%o, function=%02o)\n", PC-1, inst, fnc); | |
return IOBADFNC(dat); | |
} | |
// Unit service ... | |
t_stat imp_service (UNIT *uptr) | |
{ | |
return SCPE_OK; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
/////////////// D E V I C E A C T I O N C O M M A N D S //////////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// Reset routine ... | |
t_stat imp_reset (DEVICE *dptr) | |
{ | |
// The simh RESET command clears both the interrupt request and enable... | |
CLR_TASK_IRQ(); CLR_TASK_IEN(); | |
return SCPE_OK; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
///////// D E V I C E S E T A N D S H O W C O M M A N D S ////////// | |
//////////////////////////////////////////////////////////////////////////////// | |
// Show the station number ... | |
t_stat imp_show_station (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
fprintf(st,"station=%d", imp_station); | |
return SCPE_OK; | |
} | |
// Set the station number ... | |
t_stat imp_set_station (UNIT *uptr, int32 val, char *cptr, void *dp) | |
{ | |
uint32 newnum; t_stat sts; | |
if (cptr == NULL) return SCPE_ARG; | |
newnum = get_uint (cptr, 10, 9999, &sts); | |
if (newnum == 0) return SCPE_ARG; | |
imp_station = newnum; | |
return SCPE_OK; | |
} | |
#endif // #ifdef VM_IMPTIP from the very top |