1. New Features 1.1 Directory and documentation - Only common files (SCP and libraries) are in the top level directory. Individual simulator files are in their individual directories. - simh_doc.txt has been split up. simh_doc.txt now documents only SCP. The individual simulators are documented in separate text files in their own directories. - mingw_build.bat is a batch file for the MINGW/gcc environment that will build all the simulators, assuming the root directory structure is at c:\sim. - Makefile is a UNIX make file for the gcc environment that will build all the simulators, assuming the root directory is at c:\sim. 1.2 SCP - DO <file name> executes the SCP commands in the specified file. - Replicated registers in unit structures can now be declared as arrays for examine, modify, save, and restore. Most replicated unit registers (for example, mag tape position registers) have been changed to arrays. - The ADD/REMOVE commands have been replaced by SET unit ONLINE and SET unit OFFLINE, respectively. - Register names that are unique within an entire simulator do not have to be prefaced with the device name. - The ATTACH command can attach files read only, either under user option (-r), or because the attached file is ready only. - The SET/SHOW capabilities have been extended. New forms include: SET <dev> param{=value}{ param ...} SET <unit> param{=value}{ param ...} SHOW <dev> {param param ...} SHOW <unit> {param param ...} - Multiple breakpoints have been implemented. Breakpoints are set/cleared/displayed by: BREAK addr_list{[count]} NOBREAK addr_list SHOW BREAK addr_list 1.3 PDP-11 simulator - Unibus map implemented, with 22b RP controller (URH70) or 18b RP controller (URH11) (in debug). - All DMA peripherals rewritten to use map. - Many peripherals modified for source sharing with VAX. - RQDX3 implemented. - Bugs fixed in RK11 and RL11 write check. 1.4 PDP-10 simulator - ITS 1-proceed implemented. - Bugs fixed in ITS PC sampling and LPMR 1.5 18b PDP simulator - Interrupts split out to multiple levels to allow easier expansion. 1.5 IBM System 3 Simulator - Written by Charles (Dutch) Owen. 1.6 VAX Simulator (in debug) - Simulates MicroVAX 3800 (KA655) with 16MB-64MB memory, RQDX3, RLV12, TSV11, DZV11, LPV11, PCV11. - CDROM capability has been added to the RQDX3, to allow testing with VMS hobbyist images. 1.7 SDS 940 Simulator (not tested) - Simulates SDS 940, 16K-64K memory, fixed and moving head disk, magtape, line printer, console. 1.8 Altair Z80 - Revised from Charles (Dutch) Owen's original by Peter Schorn. - MITS 8080 with full Z80 simulation. - 4K and 8K BASIC packages, Prolog package. 1.9 Interdata The I4 simulator has been withdrawn for major rework. Look for a complete 16b/32b Interdata simulator sometime next year. 2. Release Notes 2.1 SCP SCP now allows replicated registers in unit structures to be modelled as arrays. All replicated register declarations have been replaced by register array declarations. As a result, save files from prior revisions will generate errors after restoring main memory. 2.2 PDP-11 The Unibus map code is in debug. The map was implemented primarily to allow source sharing with the VAX, which requires a DMA map. DMA devices work correctly with the Unibus map disabled. The RQDX3 simulator has run a complete RSTS/E SYSGEN, with multiple drives, and booted the completed system from scratch. 2.3 VAX The VAX simulator will run the boot code up to the >>> prompt. It can successfully process a SHOW DEVICE command. It runs the HCORE instruction diagnostic. It can boot the hobbyist CD through SYSBOOT and through the date/time dialog and restore the hobbyist CD, using standalone backup. On the boot of the restored disk, it gets to the date/time dialog, and then crashes. 2.4 SDS 940 The SDS 940 is untested, awaiting real code. 2.5 GCC Optimization At -O2 and above, GCC does not correctly compile the simulators which use setjmp-longjmp (PDP-11, PDP-10, VAX). A working hypothesis is that optimized state maintained in registers is being used in the setjmp processing routine. On the PDP-11 and PDP-10, all of this state has been either made global, or volatile, to encourage GCC to keep the state up to date in memory. The VAX is still vulnerable. 3. Work list 3.1 SCP - Better ENABLE/DISABLE. 3.2 PDP-11 RQDX3 Software mapped mode, RCT read simulation, VMS debug.
2991 lines
93 KiB
C
2991 lines
93 KiB
C
/* scp.c: simulator control program
|
||
|
||
Copyright (c) 1993-2001, Robert M Supnik
|
||
|
||
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 M SUPNIK 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 M Supnik 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 M Supnik.
|
||
|
||
19-Dec-01 RMS Fixed DO command bug (found by John Dundas)
|
||
07-Dec-01 RMS Implemented breakpoint package
|
||
05-Dec-01 RMS Fixed bug in universal register logic
|
||
03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers
|
||
24-Nov-01 RMS Added unit-based registers
|
||
16-Nov-01 RMS Added DO command
|
||
28-Oct-01 RMS Added relative range addressing
|
||
08-Oct-01 RMS Added SHOW VERSION
|
||
30-Sep-01 RMS Relaxed attach test in BOOT
|
||
27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod
|
||
17-Sep-01 RMS Removed multiple console support
|
||
07-Sep-01 RMS Removed conditional externs on function prototypes
|
||
Added special modifier print
|
||
31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7)
|
||
18-Jul-01 RMS Minor changes for Macintosh port
|
||
12-Jun-01 RMS Fixed bug in big-endian I/O (found by Dave Conroy)
|
||
27-May-01 RMS Added multiple console support
|
||
16-May-01 RMS Added logging
|
||
15-May-01 RMS Added features from Tim Litt
|
||
12-May-01 RMS Fixed missing return in disable_cmd
|
||
25-Mar-01 RMS Added ENABLE/DISABLE
|
||
14-Mar-01 RMS Revised LOAD/DUMP interface (again)
|
||
05-Mar-01 RMS Added clock calibration support
|
||
05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0
|
||
04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine
|
||
21-Jan-01 RMS Added relative time
|
||
22-Dec-00 RMS Fixed find_device for devices ending in numbers
|
||
08-Dec-00 RMS V2.5a changes
|
||
30-Oct-00 RMS Added output file option to examine
|
||
11-Jul-99 RMS V2.5 changes
|
||
13-Apr-99 RMS Fixed handling of 32b addresses
|
||
04-Oct-98 RMS V2.4 changes
|
||
20-Aug-98 RMS Added radix commands
|
||
05-Jun-98 RMS Fixed bug in ^D handling for UNIX
|
||
10-Apr-98 RMS Added switches to all commands
|
||
26-Oct-97 RMS Added search capability
|
||
25-Jan-97 RMS Revised data types
|
||
23-Jan-97 RMS Added bi-endian I/O
|
||
06-Sep-96 RMS Fixed bug in variable length IEXAMINE
|
||
16-Jun-96 RMS Changed interface to parse/print_sym
|
||
06-Apr-96 RMS Added error checking in reset all
|
||
07-Jan-96 RMS Added register buffers in save/restore
|
||
11-Dec-95 RMS Fixed ordering bug in save/restore
|
||
22-May-95 RMS Added symbolic input
|
||
13-Apr-95 RMS Added symbolic printouts
|
||
*/
|
||
|
||
#include "sim_defs.h"
|
||
#include "sim_rev.h"
|
||
#include <signal.h>
|
||
#include <ctype.h>
|
||
|
||
#define EX_D 0 /* deposit */
|
||
#define EX_E 1 /* examine */
|
||
#define EX_I 2 /* interactive */
|
||
#define SCH_OR 0 /* search logicals */
|
||
#define SCH_AND 1
|
||
#define SCH_XOR 2
|
||
#define SCH_E 0 /* search booleans */
|
||
#define SCH_N 1
|
||
#define SCH_G 2
|
||
#define SCH_L 3
|
||
#define SCH_EE 4
|
||
#define SCH_NE 5
|
||
#define SCH_GE 6
|
||
#define SCH_LE 7
|
||
#define SSH_ST 0 /* set */
|
||
#define SSH_SH 1 /* show */
|
||
#define SSH_CL 2 /* clear */
|
||
#define RU_RUN 0 /* run */
|
||
#define RU_GO 1 /* go */
|
||
#define RU_STEP 2 /* step */
|
||
#define RU_CONT 3 /* continue */
|
||
#define RU_BOOT 4 /* boot */
|
||
|
||
#define SWHIDE (1u << 26) /* enable hiding */
|
||
#define SRBSIZ 1024 /* save/restore buffer */
|
||
#define SIM_BRK_INILNT 1024
|
||
#define SIM_BRK_ALLTYP 0xFFFFFFFF
|
||
#define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \
|
||
sim_rtime = sim_rtime + ((uint32) (x - sim_interval)); \
|
||
x = sim_interval
|
||
|
||
struct brktab {
|
||
t_addr addr;
|
||
int32 typ;
|
||
int32 cnt;
|
||
char *act;
|
||
};
|
||
typedef struct brktab BRKTAB;
|
||
|
||
extern char sim_name[];
|
||
extern DEVICE *sim_devices[];
|
||
extern REG *sim_PC;
|
||
extern char *sim_stop_messages[];
|
||
extern t_stat sim_instr (void);
|
||
extern t_stat sim_load (FILE *ptr, char *cptr, char *fnam, int flag);
|
||
extern int32 sim_emax;
|
||
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
|
||
UNIT *uptr, int32 sw);
|
||
extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val,
|
||
int32 sw);
|
||
extern t_stat ttinit (void);
|
||
extern t_stat ttrunstate (void);
|
||
extern t_stat ttcmdstate (void);
|
||
extern t_stat ttclose (void);
|
||
extern t_stat sim_putchar (int32 out);
|
||
extern uint32 sim_os_msec (void);
|
||
UNIT *sim_clock_queue = NULL;
|
||
int32 sim_interval = 0;
|
||
int32 sim_switches = 0;
|
||
int32 sim_is_running = 0;
|
||
uint32 sim_brk_summ = 0;
|
||
uint32 sim_brk_types = 0;
|
||
uint32 sim_brk_dflt = 0;
|
||
BRKTAB *sim_brk_tab = NULL;
|
||
int32 sim_brk_ent = 0;
|
||
int32 sim_brk_lnt = 0;
|
||
int32 sim_brk_ins = 0;
|
||
t_bool sim_brk_pend = FALSE;
|
||
t_addr sim_brk_ploc = 0;
|
||
static double sim_time;
|
||
static uint32 sim_rtime;
|
||
static int32 noqueue_time;
|
||
volatile int32 stop_cpu = 0;
|
||
t_value *sim_eval = NULL;
|
||
int32 sim_end = 1; /* 1 = little */
|
||
FILE *sim_log = NULL; /* log file */
|
||
unsigned char sim_flip[FLIP_SIZE];
|
||
|
||
t_stat sim_brk_init (void);
|
||
t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt);
|
||
t_stat sim_brk_clr (t_addr loc, int32 sw);
|
||
t_stat sim_brk_clrall (int32 sw);
|
||
t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw);
|
||
t_stat sim_brk_showall (FILE *st, int32 sw);
|
||
void sim_brk_npc (void);
|
||
#define print_val(a,b,c,d) fprint_val (stdout, (a), (b), (c), (d))
|
||
#define SZ_D(dp) (size_map[((dp) -> dwidth + CHAR_BIT - 1) / CHAR_BIT])
|
||
#define SZ_R(rp) \
|
||
(size_map[((rp) -> width + (rp) -> offset + CHAR_BIT - 1) / CHAR_BIT])
|
||
#if defined (t_int64)
|
||
#define SZ_LOAD(sz,v,mb,j) \
|
||
if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + j); \
|
||
else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + j); \
|
||
else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + j); \
|
||
else v = *(((t_uint64 *) mb) + j);
|
||
#define SZ_STORE(sz,v,mb,j) \
|
||
if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \
|
||
else if (sz == sizeof (uint16)) *(((uint16 *) mb) + j) = (uint16) v; \
|
||
else if (sz == sizeof (uint32)) *(((uint32 *) mb) + j) = (uint32) v; \
|
||
else *(((t_uint64 *) mb) + j) = v;
|
||
#else
|
||
#define SZ_LOAD(sz,v,mb,j) \
|
||
if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + j); \
|
||
else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + j); \
|
||
else v = *(((uint32 *) mb) + j);
|
||
#define SZ_STORE(sz,v,mb,j) \
|
||
if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \
|
||
else if (sz == sizeof (uint16)) *(((uint16 *) mb) + j) = (uint16) v; \
|
||
else *(((uint32 *) mb) + j) = v;
|
||
#endif
|
||
#define GET_SWITCHES(cp,gb) \
|
||
sim_switches = 0; \
|
||
while (*cp == '-') { \
|
||
int32 lsw; \
|
||
cp = get_glyph (cp, gb, 0); \
|
||
if ((lsw = get_switches (gb)) <= 0) return SCPE_INVSW; \
|
||
sim_switches = sim_switches | lsw; }
|
||
#define GET_RADIX(val,dft) \
|
||
if (sim_switches & SWMASK ('O')) val = 8; \
|
||
else if (sim_switches & SWMASK ('D')) val = 10; \
|
||
else if (sim_switches & SWMASK ('H')) val = 16; \
|
||
else val = dft;
|
||
|
||
t_stat set_glob (CTAB *tab, char *gbuf, DEVICE *dptr, UNIT *uptr);
|
||
t_stat ssh_break (FILE *st, char *cptr, int32 flg);
|
||
t_stat set_radix (DEVICE *dptr, UNIT *uptr, int flag);
|
||
t_stat set_onoff (DEVICE *dptr, UNIT *uptr, int32 flag);
|
||
t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg);
|
||
t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, int32 flag);
|
||
int32 get_switches (char *cptr);
|
||
t_value get_rval (REG *rptr, int idx);
|
||
void put_rval (REG *rptr, int idx, t_value val);
|
||
t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
|
||
t_value strtotv (char *inptr, char **endptr, int radix);
|
||
t_stat fprint_val (FILE *stream, t_value val, int rdx, int wid, int fmt);
|
||
void fprint_stopped (FILE *stream, t_stat r);
|
||
char *read_line (char *ptr, int size, FILE *stream);
|
||
DEVICE *find_dev (char *ptr);
|
||
DEVICE *find_unit (char *ptr, UNIT **uptr);
|
||
DEVICE *find_dev_from_unit (UNIT *uptr);
|
||
REG *find_reg (char *ptr, char **optr, DEVICE *dptr);
|
||
REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr);
|
||
t_bool qdisable (DEVICE *dptr);
|
||
t_stat attach_err (UNIT *uptr, t_stat stat);
|
||
t_stat detach_all (int32 start_device, t_bool shutdown);
|
||
t_stat ex_reg (FILE *ofile, t_value val, int flag, REG *rptr, t_addr idx);
|
||
t_stat dep_reg (int flag, char *cptr, REG *rptr, t_addr idx);
|
||
t_stat ex_addr (FILE *ofile, int flag, t_addr addr, DEVICE *dptr, UNIT *uptr);
|
||
t_stat dep_addr (int flag, char *cptr, t_addr addr, DEVICE *dptr,
|
||
UNIT *uptr, int dfltinc);
|
||
char *get_range (char *cptr, t_addr *lo, t_addr *hi, int rdx,
|
||
t_addr max, char term);
|
||
SCHTAB *get_search (char *cptr, DEVICE *dptr, SCHTAB *schptr);
|
||
int test_search (t_value val, SCHTAB *schptr);
|
||
t_stat step_svc (UNIT *ptr);
|
||
t_stat show_version (FILE *st, int flag);
|
||
|
||
UNIT step_unit = { UDATA (&step_svc, 0, 0) };
|
||
const char save_vercur[] = "V2.6";
|
||
const char save_ver25[] = "V2.5";
|
||
const char *scp_error_messages[] = {
|
||
"Address space exceeded",
|
||
"Unit not attached",
|
||
"I/O error",
|
||
"Checksum error",
|
||
"Format error",
|
||
"Unit not attachable",
|
||
"File open error",
|
||
"Memory exhausted",
|
||
"Invalid argument",
|
||
"Step expired",
|
||
"Unknown command",
|
||
"Read only argument",
|
||
"Command not completed",
|
||
"Simulation stopped",
|
||
"Goodbye",
|
||
"Console input I/O error",
|
||
"Console output I/O error",
|
||
"End of file",
|
||
"Relocation error",
|
||
"No settable parameters",
|
||
"Unit already attached",
|
||
"Hardware timer error",
|
||
"SIGINT handler setup error",
|
||
"Console terminal setup error",
|
||
"Subscript out of range",
|
||
"Command not allowed",
|
||
"Unit disabled",
|
||
"Logging enabled",
|
||
"Logging disabled",
|
||
"Read only operation not allowed",
|
||
"Invalid switch",
|
||
"Missing value",
|
||
"Too few arguments",
|
||
"Too many arguments",
|
||
"Non-existent device",
|
||
"Non-existent unit",
|
||
"Non-existent register",
|
||
"Non-existent parameter",
|
||
"Nested DO commands",
|
||
"Internal error"
|
||
};
|
||
|
||
const size_t size_map[] = { sizeof (int8),
|
||
sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32)
|
||
#if defined (t_int64)
|
||
, sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64)
|
||
#endif
|
||
};
|
||
|
||
const t_value width_mask[] = { 0,
|
||
0x1, 0x3, 0x7, 0xF,
|
||
0x1F, 0x3F, 0x7F, 0xFF,
|
||
0x1FF, 0x3FF, 0x7FF, 0xFFF,
|
||
0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
|
||
0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF,
|
||
0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF,
|
||
0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF,
|
||
0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF
|
||
#if defined (t_int64)
|
||
, 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF,
|
||
0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF,
|
||
0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF,
|
||
0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF,
|
||
0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF,
|
||
0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF,
|
||
0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF,
|
||
0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF,
|
||
0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF,
|
||
0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF
|
||
#endif
|
||
};
|
||
|
||
t_stat reset_cmd (int flag, char *ptr);
|
||
t_stat exdep_cmd (int flag, char *ptr);
|
||
t_stat load_cmd (int flag, char *ptr);
|
||
t_stat run_cmd (int flag, char *ptr);
|
||
t_stat attach_cmd (int flag, char *ptr);
|
||
t_stat detach_cmd (int flag, char *ptr);
|
||
t_stat save_cmd (int flag, char *ptr);
|
||
t_stat restore_cmd (int flag, char *ptr);
|
||
t_stat exit_cmd (int flag, char *ptr);
|
||
t_stat set_cmd (int flag, char *ptr);
|
||
t_stat show_cmd (int flag, char *ptr);
|
||
t_stat enable_cmd (int flag, char *ptr);
|
||
t_stat disable_cmd (int flag, char *ptr);
|
||
t_stat log_cmd (int flag, char *ptr);
|
||
t_stat nolog_cmd (int flag, char *ptr);
|
||
t_stat brk_cmd (int flag, char *ptr);
|
||
t_stat do_cmd (int flag, char *ptr);
|
||
t_stat help_cmd (int flag, char *ptr);
|
||
|
||
static CTAB cmd_table[] = {
|
||
{ "RESET", &reset_cmd, 0 },
|
||
{ "EXAMINE", &exdep_cmd, EX_E },
|
||
{ "IEXAMINE", &exdep_cmd, EX_E+EX_I },
|
||
{ "DEPOSIT", &exdep_cmd, EX_D },
|
||
{ "IDEPOSIT", &exdep_cmd, EX_D+EX_I },
|
||
{ "RUN", &run_cmd, RU_RUN },
|
||
{ "GO", &run_cmd, RU_GO },
|
||
{ "STEP", &run_cmd, RU_STEP },
|
||
{ "CONT", &run_cmd, RU_CONT },
|
||
{ "BOOT", &run_cmd, RU_BOOT },
|
||
{ "ATTACH", &attach_cmd, 0 },
|
||
{ "DETACH", &detach_cmd, 0 },
|
||
{ "SAVE", &save_cmd, 0 },
|
||
{ "RESTORE", &restore_cmd, 0 },
|
||
{ "GET", &restore_cmd, 0 },
|
||
{ "LOAD", &load_cmd, 0 },
|
||
{ "DUMP", &load_cmd, 1 },
|
||
{ "EXIT", &exit_cmd, 0 },
|
||
{ "QUIT", &exit_cmd, 0 },
|
||
{ "BYE", &exit_cmd, 0 },
|
||
{ "SET", &set_cmd, 0 },
|
||
{ "SHOW", &show_cmd, 0 },
|
||
{ "ENABLE", &enable_cmd, 0 },
|
||
{ "DISABLE", &disable_cmd, 0 },
|
||
{ "LOG", &log_cmd, 0 },
|
||
{ "NOLOG", &nolog_cmd, 0 },
|
||
{ "BREAK", &brk_cmd, SSH_ST },
|
||
{ "NOBREAK", &brk_cmd, SSH_CL },
|
||
{ "DO", &do_cmd, 0 },
|
||
{ "HELP", &help_cmd, 0 },
|
||
{ NULL, NULL, 0 } };
|
||
|
||
/* Main command loop */
|
||
|
||
int main (int argc, char *argv[])
|
||
{
|
||
char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr;
|
||
int32 i;
|
||
t_stat stat;
|
||
union {int32 i; char c[sizeof (int32)]; } end_test;
|
||
|
||
#if defined (__MWERKS__) && defined (macintosh)
|
||
argc = ccommand(&argv);
|
||
#endif
|
||
|
||
if ((stat = ttinit ()) != SCPE_OK) {
|
||
printf ("Fatal terminal initialization error\n%s\n",
|
||
scp_error_messages[stat - SCPE_BASE]);
|
||
return 0; }
|
||
printf ("\n");
|
||
show_version (stdout, 0);
|
||
end_test.i = 1; /* test endian-ness */
|
||
sim_end = end_test.c[0];
|
||
stop_cpu = 0;
|
||
sim_interval = 0;
|
||
sim_time = sim_rtime = 0;
|
||
noqueue_time = 0;
|
||
sim_clock_queue = NULL;
|
||
sim_is_running = 0;
|
||
sim_log = NULL;
|
||
if (sim_emax <= 0) sim_emax = 1;
|
||
if ((sim_eval = calloc (sim_emax, sizeof (t_value))) == NULL) {
|
||
printf ("Unable to allocate examine buffer\n");
|
||
return 0; };
|
||
if ((stat = reset_all (0)) != SCPE_OK) {
|
||
printf ("Fatal simulator initialization error\n%s\n",
|
||
scp_error_messages[stat - SCPE_BASE]);
|
||
return 0; }
|
||
if ((stat = sim_brk_init ()) != SCPE_OK) {
|
||
printf ("Fatal breakpoint table initialization error\n%s\n",
|
||
scp_error_messages[stat - SCPE_BASE]);
|
||
return 0; }
|
||
|
||
if ((argc > 1) && argv[1]) { /* cmd file arg? */
|
||
stat = do_cmd (0, argv[1]); /* proc cmd file */
|
||
if (stat == SCPE_OPENERR) fprintf (stderr, /* error? */
|
||
"Can't open file %s\n", argv[1]); }
|
||
|
||
do { printf ("sim> "); /* prompt */
|
||
cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */
|
||
stat = SCPE_UNK;
|
||
if (cptr == NULL) continue; /* ignore EOF */
|
||
if (*cptr == 0) continue; /* ignore blank */
|
||
if (sim_log) fprintf (sim_log, "sim> %s\n", cbuf); /* log cmd */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */
|
||
for (i = 0; cmd_table[i].name != NULL; i++) {
|
||
if (MATCH_CMD (gbuf, cmd_table[i].name) == 0) {
|
||
stat = cmd_table[i].action (cmd_table[i].arg, cptr);
|
||
break; } }
|
||
if (stat >= SCPE_BASE) { /* error? */
|
||
printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);
|
||
if (sim_log) fprintf (sim_log, "%s\n",
|
||
scp_error_messages[stat - SCPE_BASE]); }
|
||
} while (stat != SCPE_EXIT);
|
||
|
||
detach_all (0, TRUE); /* close files */
|
||
nolog_cmd (0, NULL); /* close log */
|
||
ttclose (); /* close console */
|
||
return 0;
|
||
}
|
||
|
||
/* Exit command */
|
||
|
||
t_stat exit_cmd (int flag, char *cptr)
|
||
{
|
||
return SCPE_EXIT;
|
||
}
|
||
|
||
/* Help command */
|
||
|
||
void fprint_help (FILE *st)
|
||
{
|
||
fprintf (st, "r{eset} {ALL|<device>} reset simulator\n");
|
||
fprintf (st, "e{xamine} <list> examine memory or registers\n");
|
||
fprintf (st, "ie{xamine} <list> interactive examine memory or registers\n");
|
||
fprintf (st, "d{eposit} <list> <val> deposit in memory or registers\n");
|
||
fprintf (st, "id{eposit} <list> interactive deposit in memory or registers\n");
|
||
fprintf (st, "l{oad} <file> {<args>} load binary file\n");
|
||
fprintf (st, "du(mp) <file> {<args>} dump binary file\n");
|
||
fprintf (st, "ru{n} {new PC} reset and start simulation\n");
|
||
fprintf (st, "go {new PC} start simulation\n");
|
||
fprintf (st, "c{ont} continue simulation\n");
|
||
fprintf (st, "s{tep} {n} simulate n instructions\n");
|
||
fprintf (st, "b{oot} <unit> bootstrap unit\n");
|
||
fprintf (st, "br{eak} <list> set breakpoints\n");
|
||
fprintf (st, "nobr{eak} <list> clear breakpoints\n");
|
||
fprintf (st, "at{tach} <unit> <file> attach file to simulated unit\n");
|
||
fprintf (st, "det{ach} <unit> detach file from simulated unit\n");
|
||
fprintf (st, "sa{ve} <file> save simulator to file\n");
|
||
fprintf (st, "rest{ore}|ge{t} <file> restore simulator from file\n");
|
||
fprintf (st, "exi{t}|q{uit}|by{e} exit from simulation\n");
|
||
fprintf (st, "set <dev>|<unit> <prm> set device/unit parameter\n");
|
||
fprintf (st, "sh{ow} <dev>|<unit> show device parameters\n");
|
||
fprintf (st, "sh{ow} c{onfiguration} show configuration\n");
|
||
fprintf (st, "sh{ow} d{evices} show devices\n");
|
||
fprintf (st, "sh{ow} m{odifiers} show modifiers\n");
|
||
fprintf (st, "sh{ow} q{ueue} show event queue\n");
|
||
fprintf (st, "sh{ow} t{ime} show simulated time\n");
|
||
fprintf (st, "sh{ow} v{ersion} show simulator version\n");
|
||
fprintf (st, "en{able} <device> enable device\n");
|
||
fprintf (st, "di{sable} <device> disable device\n");
|
||
fprintf (st, "log <file> enable logging to file\n");
|
||
fprintf (st, "nolog disable logging\n");
|
||
fprintf (st, "do <file> process command file\n");
|
||
fprintf (st, "h{elp} type this message\n");
|
||
return;
|
||
}
|
||
|
||
t_stat help_cmd (int flag, char *cptr)
|
||
{
|
||
fprint_help (stdout);
|
||
if (sim_log) fprint_help (sim_log);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Do command */
|
||
|
||
t_stat do_cmd (int flag, char *fcptr)
|
||
{
|
||
char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE];
|
||
FILE *fpin;
|
||
int32 i;
|
||
t_stat stat;
|
||
|
||
if ((fpin = fopen (fcptr, "r")) != NULL) { /* cmd file open? */
|
||
do {
|
||
cptr = read_line (cbuf, CBUFSIZE, fpin); /* get cmd line */
|
||
if (cptr == NULL) break; /* exit on eof */
|
||
if (*cptr == 0) continue; /* ignore blank */
|
||
if (sim_log) fprintf (sim_log, "do> %s\n", cptr);
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */
|
||
if (strcmp (gbuf, "do") == 0) { /* don't recurse */
|
||
fclose (fpin);
|
||
return SCPE_NEST; }
|
||
for (i = 0; cmd_table[i].name != NULL; i++) { /* find cmd */
|
||
if (MATCH_CMD (gbuf, cmd_table[i].name) == 0) {
|
||
stat = cmd_table[i].action (cmd_table[i].arg, cptr);
|
||
break; } }
|
||
if (stat >= SCPE_BASE) /* error? */
|
||
printf ("%s\n", scp_error_messages[stat - SCPE_BASE]);
|
||
} while (stat != SCPE_EXIT);
|
||
fclose (fpin); /* close file */
|
||
return SCPE_OK; } /* end if cmd file */
|
||
return SCPE_OPENERR;
|
||
}
|
||
|
||
/* Set command */
|
||
|
||
t_stat set_cmd (int flag, char *cptr)
|
||
{
|
||
int32 lvl;
|
||
t_stat r;
|
||
char gbuf[CBUFSIZE], *cvptr;
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
MTAB *mptr;
|
||
CTAB *ctbr;
|
||
static CTAB set_dev_tab[] = {
|
||
{ "OCTAL", &set_radix, 8 },
|
||
{ "DECIMAL", &set_radix, 10 },
|
||
{ "HEX", &set_radix, 16 },
|
||
{ NULL, NULL, 0 } };
|
||
static CTAB set_unit_tab[] = {
|
||
{ "ONLINE", &set_onoff, 1 },
|
||
{ "OFFLINE", &set_onoff, 0 },
|
||
{ NULL, NULL, 0 } };
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* get switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get glob/dev/unit */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
|
||
if (dptr = find_dev (gbuf)) { /* device match? */
|
||
uptr = dptr -> units; /* first unit */
|
||
ctbr = set_dev_tab; /* global table */
|
||
lvl = MTAB_VDV; } /* device match */
|
||
else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */
|
||
if (uptr == NULL) return SCPE_NXUN; /* invalid unit */
|
||
ctbr = set_unit_tab; /* global table */
|
||
lvl = MTAB_VUN; } /* unit match */
|
||
else return SCPE_NXDEV; /* no match */
|
||
if (dptr -> modifiers == NULL) return SCPE_NOPARAM; /* any modifiers? */
|
||
|
||
while (*cptr != 0) { /* do all mods */
|
||
cptr = get_glyph (cptr, gbuf, ','); /* get modifier */
|
||
if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */
|
||
if (set_glob (ctbr, gbuf, dptr, uptr) == SCPE_OK) /* global match? */
|
||
return SCPE_OK;
|
||
for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) {
|
||
if ((mptr -> mstring) && /* match string */
|
||
(MATCH_CMD (gbuf, mptr -> mstring) == 0)) { /* matches option? */
|
||
if (mptr -> mask & MTAB_XTD) { /* extended? */
|
||
if ((lvl & mptr -> mask) == 0) return SCPE_ARG;
|
||
if ((lvl & MTAB_VUN) && (uptr -> flags & UNIT_DIS))
|
||
return SCPE_UDIS; /* unit disabled? */
|
||
if (mptr -> valid) { /* validation rtn? */
|
||
r = mptr -> valid (uptr, mptr -> match, cvptr, mptr -> desc);
|
||
if (r != SCPE_OK) return r; }
|
||
else if (!mptr -> desc) break; /* value desc? */
|
||
else if (mptr -> mask & MTAB_VAL) { /* take a value? */
|
||
if (!cvptr) return SCPE_MISVAL; /* none? error */
|
||
r = dep_reg (0, cvptr, (REG *) mptr -> desc, 0);
|
||
if (r != SCPE_OK) return r; }
|
||
else if (cvptr) return SCPE_ARG; /* = value? */
|
||
else *((int32 *) mptr -> desc) = mptr -> match;
|
||
} /* end if xtd */
|
||
else { /* old style */
|
||
if (cvptr) return SCPE_ARG; /* = value? */
|
||
if (uptr -> flags & UNIT_DIS) /* disabled? */
|
||
return SCPE_UDIS;
|
||
if ((mptr -> valid) && ((r = mptr -> valid
|
||
(uptr, mptr -> match, cvptr, mptr -> desc))
|
||
!= SCPE_OK)) return r; /* invalid? */
|
||
uptr -> flags = (uptr -> flags & ~(mptr -> mask)) |
|
||
(mptr -> match & mptr -> mask); /* set new value */
|
||
} /* end else xtd */
|
||
break; /* terminate for */
|
||
} /* end if match */
|
||
} /* end for */
|
||
if (mptr -> mask == 0) return SCPE_NXPAR; /* any match? */
|
||
} /* end while */
|
||
return SCPE_OK; /* done all */
|
||
}
|
||
|
||
t_stat set_glob (CTAB *tab, char *gbuf, DEVICE *dptr, UNIT *uptr)
|
||
{
|
||
for (; tab -> name != NULL; tab++) {
|
||
if (MATCH_CMD (gbuf, tab -> name) == 0)
|
||
return tab -> action (dptr, uptr, tab -> arg); }
|
||
return SCPE_NXPAR;
|
||
}
|
||
|
||
/* Set radix routine */
|
||
|
||
t_stat set_radix (DEVICE *dptr, UNIT *uptr, int32 flag)
|
||
{
|
||
dptr -> dradix = flag & 017;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Set online/offline routine */
|
||
|
||
t_stat set_onoff (DEVICE *dptr, UNIT *uptr, int32 flag)
|
||
{
|
||
if (!(uptr -> flags & UNIT_DISABLE)) return SCPE_NOFNC; /* allowed? */
|
||
if (flag) uptr -> flags = uptr -> flags & ~UNIT_DIS; /* onl? enable */
|
||
else { /* offline? */
|
||
if ((uptr -> flags & UNIT_ATT) || sim_is_active (uptr))
|
||
return SCPE_NOFNC; /* more tests */
|
||
uptr -> flags = uptr -> flags | UNIT_DIS; } /* disable */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show command */
|
||
|
||
t_stat show_cmd (int flag, char *cptr)
|
||
{
|
||
int32 i, lvl;
|
||
t_stat r;
|
||
char gbuf[CBUFSIZE];
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
MTAB *mptr;
|
||
|
||
t_stat show_config (FILE *st, int32 flag);
|
||
t_stat show_queue (FILE *st, int32 flag);
|
||
t_stat show_time (FILE *st, int32 flag);
|
||
t_stat show_mod_names (FILE *st, int32 flag);
|
||
t_stat show_device (FILE *st, DEVICE *dptr, int32 flag);
|
||
t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag);
|
||
|
||
static CTAB show_table[] = {
|
||
{ "CONFIGURATION", &show_config, 0 },
|
||
{ "DEVICES", &show_config, 1 },
|
||
{ "QUEUE", &show_queue, 0 },
|
||
{ "TIME", &show_time, 0 },
|
||
{ "MODIFIERS", &show_mod_names, 0 },
|
||
{ "VERSION", &show_version, 0 },
|
||
{ NULL, NULL, 0 } };
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
for (i = 0; show_table[i].name != NULL; i++) { /* find command */
|
||
if (MATCH_CMD (gbuf, show_table[i].name) == 0) {
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
r = show_table[i].action (stdout, show_table[i].arg);
|
||
if (sim_log) show_table[i].action (sim_log, show_table[i].arg);
|
||
return r; } }
|
||
|
||
if (MATCH_CMD (gbuf, "BREAK") == 0) { /* SHOW BREAK? */
|
||
r = ssh_break (stdout, cptr, 1);
|
||
if (sim_log) ssh_break (sim_log, cptr, SSH_SH);
|
||
return r; }
|
||
if (dptr = find_dev (gbuf)) { /* device match? */
|
||
uptr = dptr -> units; /* first unit */
|
||
lvl = MTAB_VDV; } /* device match */
|
||
else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */
|
||
if (uptr == NULL) return SCPE_NXUN; /* invalid unit */
|
||
if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
|
||
lvl = MTAB_VUN; } /* unit match */
|
||
else return SCPE_NXDEV; /* no match */
|
||
|
||
if (*cptr == 0) { /* now eol? */
|
||
if (lvl == MTAB_VDV) { /* show dev? */
|
||
r = show_device (stdout, dptr, 0);
|
||
if (sim_log) show_device (sim_log, dptr, 0);
|
||
return r; }
|
||
else {
|
||
r = show_unit (stdout, dptr, uptr, -1);
|
||
if (sim_log) show_unit (sim_log, dptr, uptr, -1);
|
||
return r; } }
|
||
if (dptr -> modifiers == NULL) return SCPE_NOPARAM; /* any modifiers? */
|
||
|
||
while (*cptr != 0) { /* do all mods */
|
||
cptr = get_glyph (cptr, gbuf, ','); /* get modifier */
|
||
for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) {
|
||
if (((mptr -> mask & MTAB_XTD)? /* right level? */
|
||
(mptr -> mask & lvl): (MTAB_VUN & lvl)) &&
|
||
((mptr -> disp && mptr -> pstring && /* named disp? */
|
||
(MATCH_CMD (gbuf, mptr -> pstring) == 0)) ||
|
||
(((mptr -> mask & MTAB_XTV) == MTAB_XTV) && /* named value? */
|
||
mptr -> mstring &&
|
||
(MATCH_CMD (gbuf, mptr -> mstring) == 0)))) {
|
||
show_one_mod (stdout, dptr, uptr, mptr, 1);
|
||
if (sim_log) show_one_mod (sim_log, dptr, uptr, mptr, 1);
|
||
break;
|
||
} /* end if */
|
||
} /* end for */
|
||
} /* end while */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show processors */
|
||
|
||
t_stat show_device (FILE *st, DEVICE *dptr, int32 flag)
|
||
{
|
||
int32 j, ucnt;
|
||
UNIT *uptr;
|
||
|
||
fprintf (st, "%s", dptr -> name); /* print dev name */
|
||
if (qdisable (dptr)) { /* disabled? */
|
||
fprintf (st, ", disabled\n");
|
||
return SCPE_OK; }
|
||
for (j = ucnt = 0; j < dptr -> numunits; j++) { /* count units */
|
||
uptr = (dptr -> units) + j;
|
||
if (!(uptr -> flags & UNIT_DIS)) ucnt++; }
|
||
show_all_mods (st, dptr, dptr -> units, MTAB_VDV); /* show dev mods */
|
||
if (dptr -> numunits == 0) fprintf (st, "\n");
|
||
else { if (ucnt == 0) fprintf (st, ", all units disabled\n");
|
||
else if (ucnt > 1) fprintf (st, ", %d units\n", ucnt);
|
||
else if (flag) fprintf (st, "\n"); }
|
||
if (flag) return SCPE_OK; /* dev only? */
|
||
for (j = 0; j < dptr -> numunits; j++) { /* loop thru units */
|
||
uptr = (dptr -> units) + j;
|
||
if ((uptr -> flags & UNIT_DIS) == 0)
|
||
show_unit (st, dptr, uptr, ucnt); }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
|
||
{
|
||
t_addr kval = (uptr -> flags & UNIT_BINK)? 1024: 1000;
|
||
int32 u = uptr - dptr -> units;
|
||
|
||
if (flag > 1) fprintf (st, " %s%d", dptr -> name, u);
|
||
else if (flag < 0) fprintf (st, "%s%d", dptr -> name, u);
|
||
if (uptr -> flags & UNIT_FIX) {
|
||
if (uptr -> capac < kval)
|
||
fprintf (st, ", %d%s", uptr -> capac,
|
||
((dptr -> dwidth / dptr -> aincr) > 8)? "W": "B");
|
||
else fprintf (st, ", %dK%s", uptr -> capac / kval,
|
||
((dptr -> dwidth / dptr -> aincr) > 8)? "W": "B"); }
|
||
if (uptr -> flags & UNIT_ATT) {
|
||
fprintf (st, ", attached to %s", uptr -> filename);
|
||
if (uptr -> flags & UNIT_RO) fprintf (st, ", read only"); }
|
||
else if (uptr -> flags & UNIT_ATTABLE)
|
||
fprintf (st, ", not attached");
|
||
show_all_mods (st, dptr, uptr, MTAB_VUN); /* show unit mods */
|
||
fprintf (st, "\n");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_version (FILE *st, int32 flag)
|
||
{
|
||
int32 vmaj = SIM_MAJOR, vmin = SIM_MINOR, vpat = SIM_PATCH;
|
||
|
||
fprintf (st, "%s simulator V%d.%d-%d\n", sim_name, vmaj, vmin, vpat);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_config (FILE *st, int32 flag)
|
||
{
|
||
int32 i;
|
||
DEVICE *dptr;
|
||
|
||
fprintf (st, "%s simulator configuration\n\n", sim_name);
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
|
||
show_device (st, dptr, flag);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_queue (FILE *st, int32 flag)
|
||
{
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
int32 accum;
|
||
|
||
if (sim_clock_queue == NULL) {
|
||
fprintf (st, "%s event queue empty, time = %-16.0f\n",
|
||
sim_name, sim_time);
|
||
return SCPE_OK; }
|
||
fprintf (st, "%s event queue status, time = %-16.0f\n",
|
||
sim_name, sim_time);
|
||
accum = 0;
|
||
for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr -> next) {
|
||
if (uptr == &step_unit) fprintf (st, " Step timer");
|
||
else if ((dptr = find_dev_from_unit (uptr)) != NULL) {
|
||
fprintf (st, " %s", dptr -> name);
|
||
if (dptr -> numunits > 1) fprintf (st, " unit %d",
|
||
uptr - dptr -> units); }
|
||
else fprintf (st, " Unknown");
|
||
fprintf (st, " at %d\n", accum + uptr -> time);
|
||
accum = accum + uptr -> time; }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_time (FILE *st, int32 flag)
|
||
{
|
||
fprintf (st, "Time: %-16.0f\n", sim_time);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_mod_names (FILE *st, int32 flag)
|
||
{
|
||
int i, any;
|
||
DEVICE *dptr;
|
||
MTAB *mptr;
|
||
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
|
||
if (dptr -> modifiers) {
|
||
any = 0;
|
||
for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) {
|
||
if (mptr -> mstring) {
|
||
if (any++) fprintf (st, ", %s", mptr -> mstring);
|
||
else fprintf (st, "%s\t%s", dptr -> name, mptr -> mstring); } }
|
||
if (any) fprintf (st, "\n"); } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag)
|
||
{
|
||
MTAB *mptr;
|
||
|
||
if (dptr -> modifiers == NULL) return SCPE_OK;
|
||
for (mptr = dptr -> modifiers; mptr -> mask != 0; mptr++) {
|
||
if (mptr -> pstring && ((mptr -> mask & MTAB_XTD)?
|
||
((mptr -> mask & flag) && !(mptr -> mask & MTAB_NMO)):
|
||
((MTAB_VUN & flag) && ((uptr -> flags & mptr -> mask) == mptr -> match)))) {
|
||
fputs (", ", st);
|
||
show_one_mod (st, dptr, uptr, mptr, 0); } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, int32 flag)
|
||
{
|
||
t_value val;
|
||
|
||
if (mptr -> disp) mptr -> disp (st, uptr, mptr -> match, mptr -> desc);
|
||
else if ((mptr -> mask & MTAB_XTV) == MTAB_XTV) {
|
||
REG *rptr = (REG *) mptr -> desc;
|
||
fprintf (st, "%s=", mptr -> pstring);
|
||
val = get_rval (rptr, 0);
|
||
fprint_val (st, val, rptr -> radix, rptr -> width,
|
||
rptr -> flags & REG_FMT); }
|
||
else fputs (mptr -> pstring, st);
|
||
if (flag) fputc ('\n', st);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Breakpoint commands */
|
||
|
||
t_stat brk_cmd (int flg, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
return ssh_break (NULL, cptr, flg); /* call common code */
|
||
}
|
||
|
||
t_stat ssh_break (FILE *st, char *cptr, int32 flg)
|
||
{
|
||
char gbuf[CBUFSIZE], *tptr, *t1ptr;
|
||
DEVICE *dptr = sim_devices[0];
|
||
UNIT *uptr = dptr -> units;
|
||
t_stat r;
|
||
t_addr lo, hi, max = uptr -> capac - dptr -> aincr;
|
||
int32 cnt;
|
||
|
||
if (*cptr == 0) return SCPE_2FARG;
|
||
if (sim_brk_types == 0) return SCPE_NOFNC;
|
||
if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR;
|
||
while (*cptr) {
|
||
cptr = get_glyph (cptr, gbuf, ',');
|
||
tptr = get_range (gbuf, &lo, &hi, dptr -> aradix, max, 0);
|
||
if (tptr == NULL) return SCPE_ARG;
|
||
if (*tptr == '[') {
|
||
errno = 0;
|
||
cnt = strtoul (tptr + 1, &t1ptr, 10);
|
||
if (errno || (tptr == t1ptr) || (*t1ptr != ']') ||
|
||
(flg != SSH_ST)) return SCPE_ARG;
|
||
tptr = t1ptr + 1; }
|
||
else cnt = 0;
|
||
if (*tptr != 0) return SCPE_ARG;
|
||
if ((lo == 0) && (hi == max)) {
|
||
if (flg == SSH_CL) sim_brk_clrall (sim_switches);
|
||
else if (flg == SSH_SH) sim_brk_showall (st, sim_switches);
|
||
else return SCPE_ARG; }
|
||
else {
|
||
for ( ; lo <= hi; lo = lo + dptr -> aincr) {
|
||
if (flg == SSH_ST) r = sim_brk_set (lo, sim_switches, cnt);
|
||
else if (flg == SSH_CL) r = sim_brk_clr (lo, sim_switches);
|
||
else if (flg == SSH_SH) r = sim_brk_show (st, lo, sim_switches);
|
||
else return SCPE_ARG;
|
||
}
|
||
}
|
||
}
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Enable and disable commands and routines
|
||
|
||
en[able] enable device
|
||
di[sable] disable device
|
||
*/
|
||
|
||
t_stat enable_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
DEVICE *dptr;
|
||
REG *rptr;
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
dptr = find_dev (gbuf); /* locate device */
|
||
if (dptr == NULL) return SCPE_NXDEV; /* found it? */
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
rptr = find_reg ("*DEVENB", NULL, dptr); /* locate enable */
|
||
if (rptr == NULL) return SCPE_NOFNC; /* found it? */
|
||
put_rval (rptr, 0, 1); /* enable */
|
||
if (dptr -> reset) dptr -> reset (dptr); /* reset */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat disable_cmd (int flag, char *cptr)
|
||
{
|
||
int32 i;
|
||
char gbuf[CBUFSIZE];
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
REG *rptr;
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
dptr = find_dev (gbuf); /* locate device */
|
||
if (dptr == NULL) return SCPE_NXDEV; /* found it? */
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
rptr = find_reg ("*DEVENB", NULL, dptr); /* locate enable */
|
||
if (rptr == NULL) return SCPE_NOFNC; /* found it? */
|
||
for (i = 0; i < dptr -> numunits; i++) { /* check units */
|
||
uptr = (dptr -> units) + i;
|
||
if (uptr -> flags & UNIT_ATT || sim_is_active (uptr))
|
||
return SCPE_NOFNC; }
|
||
put_rval (rptr, 0, 0); /* disable */
|
||
if (dptr -> reset) dptr -> reset (dptr); /* reset */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Test for disabled device */
|
||
|
||
t_bool qdisable (DEVICE *dptr)
|
||
{
|
||
REG *rptr;
|
||
|
||
rptr = find_reg ("*DEVENB", NULL, dptr); /* locate enable */
|
||
if (rptr == NULL) return FALSE; /* found it? */
|
||
return (get_rval (rptr, 0)? FALSE: TRUE); /* return flag */
|
||
}
|
||
|
||
/* Logging commands
|
||
|
||
log filename open log file
|
||
nolog close log file
|
||
*/
|
||
|
||
t_stat log_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return (sim_log? SCPE_LOGON: SCPE_LOGOFF);
|
||
cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
nolog_cmd (0, NULL); /* close cur log */
|
||
sim_log = fopen (gbuf, "a"); /* open log */
|
||
if (sim_log == NULL) return SCPE_OPENERR; /* error? */
|
||
printf ("Logging to file \"%s\"\n", gbuf); /* start of log */
|
||
fprintf (sim_log, "Logging to file \"%s\"\n", gbuf);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat nolog_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
|
||
if (cptr) {
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr != 0) return SCPE_2MARG; } /* now eol? */
|
||
if (sim_log == NULL) return SCPE_OK; /* no log? */
|
||
printf ("Log file closed\n");
|
||
fprintf (sim_log, "Log file closed\n"); /* close log */
|
||
fclose (sim_log);
|
||
sim_log = NULL;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Reset command and routines
|
||
|
||
re[set] reset all devices
|
||
re[set] all reset all devices
|
||
re[set] device reset specific device
|
||
*/
|
||
|
||
t_stat reset_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
DEVICE *dptr;
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return (reset_all (0)); /* reset(cr) */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
if (strcmp (gbuf, "ALL") == 0) return (reset_all (0));
|
||
dptr = find_dev (gbuf); /* locate device */
|
||
if (dptr == NULL) return SCPE_NXDEV; /* found it? */
|
||
if (dptr -> reset != NULL) return dptr -> reset (dptr);
|
||
else return SCPE_OK;
|
||
}
|
||
|
||
/* Reset devices start..end
|
||
|
||
Inputs:
|
||
start = number of starting device
|
||
Outputs:
|
||
status = error status
|
||
*/
|
||
|
||
t_stat reset_all (int start)
|
||
{
|
||
DEVICE *dptr;
|
||
int32 i;
|
||
t_stat reason;
|
||
|
||
if (start < 0) return SCPE_IERR;
|
||
for (i = 0; i < start; i++) {
|
||
if (sim_devices[i] == NULL) return SCPE_IERR; }
|
||
for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
|
||
if (dptr -> reset != NULL) {
|
||
reason = dptr -> reset (dptr);
|
||
if (reason != SCPE_OK) return reason; } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Load and dump commands
|
||
|
||
lo[ad] filename {arg} load specified file
|
||
du[mp] filename {arg} dump to specified file
|
||
*/
|
||
|
||
t_stat load_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
FILE *loadfile;
|
||
t_stat reason;
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */
|
||
loadfile = fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */
|
||
if (loadfile == NULL) return SCPE_OPENERR;
|
||
reason = sim_load (loadfile, cptr, gbuf, flag); /* load or dump */
|
||
fclose (loadfile);
|
||
return reason;
|
||
}
|
||
|
||
/* Attach command
|
||
|
||
at[tach] unit file attach specified unit to file
|
||
*/
|
||
|
||
t_stat attach_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
if (*cptr == 0) return SCPE_2MARG; /* now eol? */
|
||
dptr = find_unit (gbuf, &uptr); /* locate unit */
|
||
if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
|
||
if (uptr == NULL) return SCPE_NXUN; /* valid unit? */
|
||
if (dptr -> attach != NULL) return dptr -> attach (uptr, cptr);
|
||
return attach_unit (uptr, cptr);
|
||
}
|
||
|
||
t_stat attach_unit (UNIT *uptr, char *cptr)
|
||
{
|
||
DEVICE *dptr;
|
||
t_stat reason;
|
||
|
||
if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
|
||
if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT; /* not attachable? */
|
||
if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT;
|
||
if (uptr -> flags & UNIT_ATT) { /* already attached? */
|
||
reason = detach_unit (uptr);
|
||
if (reason != SCPE_OK) return reason; }
|
||
uptr -> filename = calloc (CBUFSIZE, sizeof (char));
|
||
if (uptr -> filename == NULL) return SCPE_MEM;
|
||
strncpy (uptr -> filename, cptr, CBUFSIZE);
|
||
if (sim_switches & SWMASK ('R')) { /* read only? */
|
||
if ((uptr -> flags & UNIT_ROABLE) == 0) /* allowed? */
|
||
return attach_err (uptr, SCPE_NORO); /* no, error */
|
||
uptr -> fileref = fopen (cptr, "rb"); /* open rd only */
|
||
if (uptr -> fileref == NULL) /* open fail? */
|
||
return attach_err (uptr, SCPE_OPENERR); /* yes, error */
|
||
uptr -> flags = uptr -> flags | UNIT_RO; /* set rd only */
|
||
printf ("%s: unit is read only\n", dptr -> name); }
|
||
else { /* normal */
|
||
uptr -> fileref = fopen (cptr, "rb+"); /* open r/w */
|
||
if (uptr -> fileref == NULL) { /* open fail? */
|
||
if (errno == EROFS) { /* read only? */
|
||
if ((uptr -> flags & UNIT_ROABLE) == 0) /* allowed? */
|
||
return attach_err (uptr, SCPE_NORO); /* no error */
|
||
uptr -> fileref = fopen (cptr, "rb"); /* open rd only */
|
||
if (uptr -> fileref == NULL) /* open fail? */
|
||
return SCPE_OPENERR; /* yes, error */
|
||
uptr -> flags = uptr -> flags | UNIT_RO; /* set rd only */
|
||
printf ("%s: unit is read only\n", dptr -> name); }
|
||
else { /* doesn't exist */
|
||
uptr -> fileref = fopen (cptr, "wb+"); /* open new file */
|
||
if (uptr -> fileref == NULL) /* open fail? */
|
||
return SCPE_OPENERR; /* yes, error */
|
||
printf ("%s: creating new file\n", dptr -> name); }
|
||
} /* end if null */
|
||
} /* end else */
|
||
if (uptr -> flags & UNIT_BUFABLE) { /* buffer? */
|
||
if ((uptr -> filebuf = calloc (uptr -> capac, SZ_D (dptr))) != NULL) {
|
||
printf ("%s: buffering file in memory\n", dptr -> name);
|
||
uptr -> hwmark = fxread (uptr -> filebuf, SZ_D (dptr),
|
||
uptr -> capac, uptr -> fileref);
|
||
uptr -> flags = uptr -> flags | UNIT_BUF; }
|
||
else if (uptr -> flags & UNIT_MUSTBUF) return SCPE_MEM; }
|
||
uptr -> flags = uptr -> flags | UNIT_ATT;
|
||
uptr -> pos = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat attach_err (UNIT *uptr, t_stat stat)
|
||
{
|
||
free (uptr -> filename);
|
||
uptr -> filename = NULL;
|
||
return stat;
|
||
}
|
||
|
||
/* Detach command
|
||
|
||
det[ach] all detach all units
|
||
det[ach] unit detach specified unit
|
||
*/
|
||
|
||
t_stat detach_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
if (strcmp (gbuf, "ALL") == 0) return (detach_all (0, FALSE));
|
||
dptr = find_unit (gbuf, &uptr); /* locate unit */
|
||
if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
|
||
if (uptr == NULL) return SCPE_NXUN; /* valid unit? */
|
||
if (!(uptr -> flags & UNIT_ATTABLE)) return SCPE_NOATT;
|
||
if (dptr -> detach != NULL) return dptr -> detach (uptr);
|
||
return detach_unit (uptr);
|
||
}
|
||
|
||
/* Detach devices start..end
|
||
|
||
Inputs:
|
||
start = number of starting device
|
||
shutdown = TRUE if simulator shutting down
|
||
Outputs:
|
||
status = error status
|
||
*/
|
||
|
||
t_stat detach_all (int32 start, t_bool shutdown)
|
||
{
|
||
int32 i, j;
|
||
t_stat reason;
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
|
||
if ((start < 0) || (start > 1)) return SCPE_IERR;
|
||
for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
|
||
for (j = 0; j < dptr -> numunits; j++) {
|
||
uptr = (dptr -> units) + j;
|
||
if ((uptr -> flags & UNIT_ATTABLE) || shutdown) {
|
||
if (dptr -> detach != NULL) reason = dptr -> detach (uptr);
|
||
else reason = detach_unit (uptr);
|
||
if (reason != SCPE_OK) return reason; } } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat detach_unit (UNIT *uptr)
|
||
{
|
||
DEVICE *dptr;
|
||
|
||
if (uptr == NULL) return SCPE_IERR;
|
||
if (!(uptr -> flags & UNIT_ATT)) return SCPE_OK;
|
||
if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK;
|
||
if (uptr -> flags & UNIT_BUF) {
|
||
if (uptr -> hwmark && ((uptr -> flags & UNIT_RO) == 0)) {
|
||
printf ("%s: writing buffer to file\n", dptr -> name);
|
||
rewind (uptr -> fileref);
|
||
fxwrite (uptr -> filebuf, SZ_D (dptr), uptr -> hwmark, uptr -> fileref);
|
||
if (ferror (uptr -> fileref)) perror ("I/O error"); }
|
||
free (uptr -> filebuf);
|
||
uptr -> flags = uptr -> flags & ~UNIT_BUF;
|
||
uptr -> filebuf = NULL; }
|
||
uptr -> flags = uptr -> flags & ~(UNIT_ATT | UNIT_RO);
|
||
free (uptr -> filename);
|
||
uptr -> filename = NULL;
|
||
return (fclose (uptr -> fileref) == EOF)? SCPE_IOERR: SCPE_OK;
|
||
}
|
||
|
||
/* Save command
|
||
|
||
sa[ve] filename save state to specified file
|
||
*/
|
||
|
||
t_stat save_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
void *mbuf;
|
||
FILE *sfile;
|
||
int32 i, j, l, t;
|
||
t_addr k, high;
|
||
t_value val;
|
||
t_stat r;
|
||
t_bool zeroflg;
|
||
size_t sz;
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
REG *rptr;
|
||
|
||
#define WRITE_I(xx) fxwrite (&(xx), sizeof (xx), 1, sfile)
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
if ((sfile = fopen (cptr, "wb")) == NULL) return SCPE_OPENERR;
|
||
fputs (save_vercur, sfile); /* save format version */
|
||
fputc ('\n', sfile);
|
||
fputs (sim_name, sfile); /* sim name */
|
||
fputc ('\n', sfile);
|
||
WRITE_I (sim_time); /* sim time */
|
||
WRITE_I (sim_rtime); /* sim relative time */
|
||
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */
|
||
fputs (dptr -> name, sfile); /* device name */
|
||
fputc ('\n', sfile);
|
||
for (j = 0; j < dptr -> numunits; j++) {
|
||
uptr = (dptr -> units) + j;
|
||
t = sim_is_active (uptr);
|
||
WRITE_I (j); /* unit number */
|
||
WRITE_I (t); /* activation time */
|
||
WRITE_I (uptr -> u3); /* unit specific */
|
||
WRITE_I (uptr -> u4);
|
||
if (uptr -> flags & UNIT_ATT) fputs (uptr -> filename, sfile);
|
||
fputc ('\n', sfile);
|
||
if (((uptr -> flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) &&
|
||
(dptr -> examine != NULL) &&
|
||
((high = uptr -> capac) != 0)) { /* memory-like unit? */
|
||
WRITE_I (high); /* write size */
|
||
sz = SZ_D (dptr);
|
||
if ((mbuf = calloc (SRBSIZ, sz)) == NULL) {
|
||
fclose (sfile);
|
||
return SCPE_MEM; }
|
||
for (k = 0; k < high; ) {
|
||
zeroflg = TRUE;
|
||
for (l = 0; (l < SRBSIZ) && (k < high);
|
||
l++, k = k + (dptr -> aincr)) {
|
||
r = dptr -> examine (&val, k, uptr, 0);
|
||
if (r != SCPE_OK) return r;
|
||
if (val) zeroflg = FALSE;
|
||
SZ_STORE (sz, val, mbuf, l);
|
||
} /* end for l */
|
||
if (zeroflg) { /* all zero's? */
|
||
l = -l; /* invert block count */
|
||
WRITE_I (l); } /* write only count */
|
||
else {
|
||
WRITE_I (l); /* block count */
|
||
fxwrite (mbuf, l, sz, sfile); }
|
||
} /* end for k */
|
||
free (mbuf); /* dealloc buffer */
|
||
} /* end if mem */
|
||
else { high = 0;
|
||
WRITE_I (high); } /* no memory */
|
||
} /* end unit loop */
|
||
j = -1; /* write marker */
|
||
WRITE_I (j);
|
||
for (rptr = dptr -> registers; /* loop thru regs */
|
||
(rptr != NULL) && (rptr -> name != NULL); rptr++) {
|
||
fputs (rptr -> name, sfile); /* name */
|
||
fputc ('\n', sfile);
|
||
for (j = 0; j < rptr -> depth; j++) { /* loop thru values */
|
||
val = get_rval (rptr, j); /* get value */
|
||
WRITE_I (val); } } /* store */
|
||
fputc ('\n', sfile); } /* end registers */
|
||
fputc ('\n', sfile); /* end devices */
|
||
r = (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */
|
||
fclose (sfile);
|
||
return r;
|
||
}
|
||
|
||
/* Restore command
|
||
|
||
re[store] filename restore state from specified file
|
||
*/
|
||
|
||
t_stat restore_cmd (int flag, char *cptr)
|
||
{
|
||
char buf[CBUFSIZE];
|
||
void *mbuf;
|
||
FILE *rfile;
|
||
int32 i, j, blkcnt, limit, unitno, time;
|
||
t_addr k, high;
|
||
t_value val, mask, vzro = 0;
|
||
t_stat r;
|
||
size_t sz;
|
||
t_bool v26 = FALSE, v25 = FALSE;
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
REG *rptr;
|
||
|
||
#define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \
|
||
{ fclose (rfile); return SCPE_IOERR; }
|
||
#define READ_I(xx) if (fxread (&xx, sizeof (xx), 1, rfile) <= 0) \
|
||
{ fclose (rfile); return SCPE_IOERR; }
|
||
|
||
GET_SWITCHES (cptr, buf); /* test for switches */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
if ((rfile = fopen (cptr, "rb")) == NULL) return SCPE_OPENERR;
|
||
READ_S (buf); /* save ver or sim name */
|
||
if (strcmp (buf, save_vercur) == 0) { /* version 2.6? */
|
||
v26 = v25 = TRUE; /* set flag */
|
||
READ_S (buf); } /* read name */
|
||
else if (strcmp (buf, save_ver25) == 0) { /* version 2.5? */
|
||
v25 = TRUE; /* set flag */
|
||
READ_S (buf); } /* read name */
|
||
if (strcmp (buf, sim_name)) { /* name match? */
|
||
printf ("Wrong system type: %s\n", buf);
|
||
fclose (rfile);
|
||
return SCPE_INCOMP; }
|
||
READ_I (sim_time); /* sim time */
|
||
if (v26) { READ_I (sim_rtime); } /* sim relative time */
|
||
|
||
for ( ;; ) { /* device loop */
|
||
READ_S (buf); /* read device name */
|
||
if (buf[0] == 0) break; /* last? */
|
||
if ((dptr = find_dev (buf)) == NULL) { /* locate device */
|
||
printf ("Invalid device name: %s\n", buf);
|
||
fclose (rfile);
|
||
return SCPE_INCOMP; }
|
||
for ( ;; ) { /* unit loop */
|
||
READ_I (unitno); /* unit number */
|
||
if (unitno < 0) break;
|
||
if (unitno >= dptr -> numunits) {
|
||
printf ("Invalid unit number %s%d\n", dptr -> name,
|
||
unitno);
|
||
fclose (rfile);
|
||
return SCPE_INCOMP; }
|
||
READ_I (time); /* event time */
|
||
uptr = (dptr -> units) + unitno;
|
||
sim_cancel (uptr);
|
||
if (time > 0) sim_activate (uptr, time - 1);
|
||
READ_I (uptr -> u3); /* device specific */
|
||
READ_I (uptr -> u4);
|
||
READ_S (buf); /* attached file */
|
||
if (buf[0] != 0) {
|
||
uptr -> flags = uptr -> flags & ~UNIT_DIS;
|
||
if (dptr -> attach != NULL) r = dptr -> attach (uptr, buf);
|
||
else r = attach_unit (uptr, buf);
|
||
if (r != SCPE_OK) return r; }
|
||
READ_I (high); /* memory capacity */
|
||
if (high > 0) { /* any memory? */
|
||
if (((uptr -> flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) ||
|
||
(high > uptr -> capac) || (dptr -> deposit == NULL)) {
|
||
printf ("Invalid memory bound: %u\n", high);
|
||
fclose (rfile);
|
||
return SCPE_INCOMP; }
|
||
if (v25) {
|
||
sz = SZ_D (dptr);
|
||
if ((mbuf = calloc (SRBSIZ, sz)) == NULL) {
|
||
fclose (rfile);
|
||
return SCPE_MEM; }
|
||
for (k = 0; k < high; ) {
|
||
READ_I (blkcnt);
|
||
if (blkcnt < 0) limit = -blkcnt;
|
||
else limit = fxread (mbuf, sz, blkcnt, rfile);
|
||
if (limit <= 0) {
|
||
fclose (rfile);
|
||
return SCPE_IOERR; }
|
||
for (j = 0; j < limit; j++, k = k + (dptr -> aincr)) {
|
||
if (blkcnt < 0) val = 0;
|
||
else SZ_LOAD (sz, val, mbuf, j);
|
||
r = dptr -> deposit (val, k, uptr, 0);
|
||
if (r != SCPE_OK) return r;
|
||
} /* end for j */
|
||
} /* end for k */
|
||
free (mbuf); /* dealloc buffer */
|
||
} /* end if v25 */
|
||
else { for (k = 0; k < high; k = k + (dptr -> aincr)) {
|
||
READ_I (val);
|
||
if (((t_svalue) val) < 0) {
|
||
for (j = (int32) val + 1; j < 0; j++) {
|
||
r = dptr -> deposit (vzro, k, uptr, 0);
|
||
if (r != SCPE_OK) return r;
|
||
k = k + (dptr -> aincr); }
|
||
val = 0; }
|
||
r = dptr -> deposit (val, k, uptr, 0);
|
||
if (r != SCPE_OK) return r; }
|
||
} /* end else v25 */
|
||
} /* end if high */
|
||
} /* end unit loop */
|
||
for ( ;; ) { /* register loop */
|
||
READ_S (buf); /* read reg name */
|
||
if (buf[0] == 0) break; /* last? */
|
||
if ((rptr = find_reg (buf, NULL, dptr)) == NULL) {
|
||
printf ("Invalid register name: %s\n", buf);
|
||
fclose (rfile);
|
||
return SCPE_INCOMP; }
|
||
mask = width_mask[rptr -> width];
|
||
for (i = 0; i < rptr -> depth; i++) { /* loop thru values */
|
||
READ_I (val); /* read value */
|
||
if (val > mask)
|
||
printf ("Invalid register value: %s\n", buf);
|
||
else put_rval (rptr, i, val); } }
|
||
} /* end device loop */
|
||
fclose (rfile);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Run, go, cont, step commands
|
||
|
||
ru[n] [new PC] reset and start simulation
|
||
go [new PC] start simulation
|
||
co[nt] start simulation
|
||
s[tep] [step limit] start simulation for 'limit' instructions
|
||
b[oot] device bootstrap from device and start simulation
|
||
*/
|
||
|
||
t_stat run_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE];
|
||
int32 i, j, step, unitno;
|
||
t_stat r;
|
||
DEVICE *dptr;
|
||
UNIT *uptr;
|
||
void int_handler (int signal);
|
||
|
||
GET_SWITCHES (cptr, gbuf); /* test for switches */
|
||
step = 0;
|
||
if (((flag == RU_RUN) || (flag == RU_GO)) && (*cptr != 0)) { /* run or go */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
if ((r = dep_reg (0, gbuf, sim_PC, 0)) != SCPE_OK) return r; }
|
||
|
||
if (flag == RU_STEP) { /* step */
|
||
if (*cptr == 0) step = 1;
|
||
else { cptr = get_glyph (cptr, gbuf, 0);
|
||
step = (int32) get_uint (gbuf, 10, INT_MAX, &r);
|
||
if ((r != SCPE_OK) || (step == 0)) return SCPE_ARG; } }
|
||
|
||
if (flag == RU_BOOT) { /* boot */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */
|
||
dptr = find_unit (gbuf, &uptr); /* locate unit */
|
||
if (dptr == NULL) return SCPE_NXDEV; /* found dev? */
|
||
if (uptr == NULL) return SCPE_NXUN; /* valid unit? */
|
||
if (dptr -> boot == NULL) return SCPE_NOFNC; /* can it boot? */
|
||
if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
|
||
if ((uptr -> flags & UNIT_ATTABLE) &&
|
||
!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT;
|
||
unitno = uptr - dptr -> units; /* recover unit# */
|
||
if ((r = dptr -> boot (unitno)) != SCPE_OK) return r; }
|
||
|
||
if (*cptr != 0) return SCPE_2MARG; /* now eol? */
|
||
|
||
if ((flag == RU_RUN) || (flag == RU_BOOT)) { /* run or boot */
|
||
sim_interval = 0; /* reset queue */
|
||
sim_time = sim_rtime = 0;
|
||
noqueue_time = 0;
|
||
sim_clock_queue = NULL;
|
||
if ((r = reset_all (0)) != SCPE_OK) return r; }
|
||
for (i = 1; (dptr = sim_devices[i]) != NULL; i++) {
|
||
for (j = 0; j < dptr -> numunits; j++) {
|
||
uptr = (dptr -> units) + j;
|
||
if ((uptr -> flags & (UNIT_ATT + UNIT_SEQ)) ==
|
||
(UNIT_ATT + UNIT_SEQ))
|
||
fseek (uptr -> fileref, uptr -> pos, SEEK_SET); } }
|
||
stop_cpu = 0;
|
||
if ((int) signal (SIGINT, int_handler) == -1) { /* set WRU */
|
||
return SCPE_SIGERR; }
|
||
if (ttrunstate () != SCPE_OK) { /* set console */
|
||
ttcmdstate ();
|
||
return SCPE_TTYERR; }
|
||
if (step) sim_activate (&step_unit, step); /* set step timer */
|
||
sim_is_running = 1; /* flag running */
|
||
r = sim_instr();
|
||
|
||
sim_is_running = 0; /* flag idle */
|
||
ttcmdstate (); /* restore console */
|
||
signal (SIGINT, SIG_DFL); /* cancel WRU */
|
||
sim_cancel (&step_unit); /* cancel step timer */
|
||
if (sim_clock_queue != NULL) { /* update sim time */
|
||
UPDATE_SIM_TIME (sim_clock_queue -> time); }
|
||
else { UPDATE_SIM_TIME (noqueue_time); }
|
||
#if defined (VMS)
|
||
printf ("\n");
|
||
#endif
|
||
fprint_stopped (stdout, r); /* print msg */
|
||
if (sim_log) fprint_stopped (sim_log, r); /* log if enabled */
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Print stopped message */
|
||
|
||
void fprint_stopped (FILE *stream, t_stat v)
|
||
{
|
||
int32 i;
|
||
t_stat r;
|
||
t_addr k;
|
||
t_value pcval;
|
||
DEVICE *dptr;
|
||
|
||
if (v >= SCPE_BASE) fprintf (stream, "\n%s, %s: ",
|
||
scp_error_messages[v - SCPE_BASE], sim_PC -> name);
|
||
else fprintf (stream, "\n%s, %s: ", sim_stop_messages[v], sim_PC -> name);
|
||
pcval = get_rval (sim_PC, 0);
|
||
fprint_val (stream, pcval, sim_PC -> radix, sim_PC -> width,
|
||
sim_PC -> flags & REG_FMT);
|
||
if (((dptr = sim_devices[0]) != NULL) && (dptr -> examine != NULL)) {
|
||
for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
|
||
for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr -> aincr) {
|
||
if (r = dptr -> examine (&sim_eval[i], k, dptr -> units,
|
||
SWMASK ('V')) != SCPE_OK) break; }
|
||
if ((r == SCPE_OK) || (i > 0)) {
|
||
fprintf (stream, " (");
|
||
if (fprint_sym (stream, (t_addr) pcval, sim_eval, NULL, SWMASK('M')) > 0)
|
||
fprint_val (stream, sim_eval[0], dptr -> dradix,
|
||
dptr -> dwidth, PV_RZRO);
|
||
fprintf (stream, ")"); } }
|
||
fprintf (stream, "\n");
|
||
return;
|
||
}
|
||
|
||
/* Unit service for step timeout, originally scheduled by STEP n command
|
||
|
||
Return step timeout SCP code, will cause simulation to stop
|
||
*/
|
||
|
||
t_stat step_svc (UNIT *uptr)
|
||
{
|
||
return SCPE_STEP;
|
||
}
|
||
|
||
/* Signal handler for ^C signal
|
||
|
||
Set stop simulation flag
|
||
*/
|
||
|
||
void int_handler (int sig)
|
||
{
|
||
stop_cpu = 1;
|
||
return;
|
||
}
|
||
|
||
/* Examine/deposit commands
|
||
|
||
ex[amine] [modifiers] list examine
|
||
de[posit] [modifiers] list val deposit
|
||
ie[xamine] [modifiers] list interactive examine
|
||
id[eposit] [modifiers] list interactive deposit
|
||
|
||
modifiers
|
||
@filename output file
|
||
-letter(s) switches
|
||
devname'n device name and unit number
|
||
[{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification
|
||
|
||
list list of addresses and registers
|
||
addr[:addr|-addr] address range
|
||
ALL all addresses
|
||
register[:register|-register] register range
|
||
STATE all registers
|
||
*/
|
||
|
||
t_stat exdep_cmd (int flag, char *cptr)
|
||
{
|
||
char gbuf[CBUFSIZE], *gptr, *tptr;
|
||
int32 t;
|
||
t_bool exd2f;
|
||
t_addr low, high;
|
||
t_stat reason;
|
||
DEVICE *dptr, *tdptr;
|
||
UNIT *uptr, *tuptr;
|
||
REG *lowr, *highr;
|
||
SCHTAB stab, *schptr;
|
||
FILE *ofile;
|
||
t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int flag, char *ptr,
|
||
t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr);
|
||
t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int flag, char *ptr,
|
||
REG *lowr, REG *highr, t_addr lows, t_addr highs);
|
||
|
||
ofile = NULL; /* no output file */
|
||
exd2f = FALSE;
|
||
sim_switches = 0; /* no switches */
|
||
schptr = NULL; /* no search */
|
||
stab.logic = SCH_OR; /* default search params */
|
||
stab.bool = SCH_GE;
|
||
stab.mask = stab.comp = 0;
|
||
dptr = sim_devices[0]; /* default device, unit */
|
||
uptr = dptr -> units;
|
||
for (;;) { /* loop through modifiers */
|
||
if (*cptr == 0) return SCPE_2FARG; /* must be more */
|
||
if (*cptr == '@') { /* output file spec? */
|
||
if (flag != EX_E) return SCPE_ARG; /* examine only */
|
||
if (exd2f) { /* already got one? */
|
||
fclose (ofile); /* one per customer */
|
||
return SCPE_ARG; }
|
||
cptr = get_glyph_nc (cptr + 1, gbuf, 0);
|
||
ofile = fopen (gbuf, "a"); /* open for append */
|
||
if (ofile == NULL) return SCPE_OPENERR;
|
||
exd2f = TRUE;
|
||
continue; } /* look for more */
|
||
cptr = get_glyph (cptr, gbuf, 0);
|
||
if ((t = get_switches (gbuf)) != 0) { /* try for switches */
|
||
if (t < 0) return SCPE_INVSW; /* err if bad switch */
|
||
sim_switches = sim_switches | t; } /* or in new switches */
|
||
else if (get_search (gbuf, dptr, &stab) != NULL) { /* try for search */
|
||
schptr = &stab; } /* set search */
|
||
else if (((tdptr = find_unit (gbuf, &tuptr)) != NULL) &&
|
||
(tuptr != NULL)) { /* try for unit */
|
||
dptr = tdptr; /* set as default */
|
||
uptr = tuptr; }
|
||
else break; } /* not rec, break out */
|
||
if (uptr == NULL) return SCPE_NXUN; /* got a unit? */
|
||
if ((*cptr == 0) == (flag == 0)) return SCPE_ARG; /* eol if needed? */
|
||
|
||
if (ofile == NULL) ofile = stdout; /* no file? stdout */
|
||
for (gptr = gbuf, reason = SCPE_OK;
|
||
(*gptr != 0) && (reason == SCPE_OK); gptr = tptr) {
|
||
tdptr = dptr; /* working dptr */
|
||
if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) {
|
||
tptr = gptr + strlen ("STATE");
|
||
if (*tptr && (*tptr++ != ',')) return SCPE_ARG;
|
||
if ((lowr = dptr -> registers) == NULL) return SCPE_NXREG;
|
||
for (highr = lowr; highr -> name != NULL; highr++) ;
|
||
sim_switches = sim_switches | SWHIDE;
|
||
reason = exdep_reg_loop (ofile, schptr, flag, cptr,
|
||
lowr, --highr, 0, 0);
|
||
continue; }
|
||
|
||
if ((lowr = find_reg (gptr, &tptr, tdptr)) ||
|
||
(lowr = find_reg_glob (gptr, &tptr, &tdptr))) {
|
||
low = high = 0;
|
||
if ((*tptr == '-') || (*tptr == ':')) {
|
||
highr = find_reg (tptr + 1, &tptr, tdptr);
|
||
if (highr == NULL) return SCPE_NXREG; }
|
||
else { highr = lowr;
|
||
if (*tptr == '[') {
|
||
if (lowr -> depth <= 1) return SCPE_ARG;
|
||
tptr = get_range (tptr + 1, &low, &high,
|
||
10, lowr -> depth - 1, ']');
|
||
if (tptr == NULL) return SCPE_ARG; } }
|
||
if (*tptr && (*tptr++ != ',')) return SCPE_ARG;
|
||
reason = exdep_reg_loop (ofile, schptr, flag, cptr,
|
||
lowr, highr, low, high);
|
||
continue; }
|
||
|
||
tptr = get_range (gptr, &low, &high, dptr -> aradix,
|
||
(((uptr -> capac == 0) || (flag == EX_E))? 0:
|
||
uptr -> capac - dptr -> aincr), 0);
|
||
if (tptr == NULL) return SCPE_ARG;
|
||
if (*tptr && (*tptr++ != ',')) return SCPE_ARG;
|
||
reason = exdep_addr_loop (ofile, schptr, flag, cptr, low, high,
|
||
dptr, uptr);
|
||
} /* end for */
|
||
if (exd2f) fclose (ofile); /* close output file */
|
||
return reason;
|
||
}
|
||
|
||
/* Loop controllers for examine/deposit
|
||
|
||
exdep_reg_loop examine/deposit range of registers
|
||
exdep_addr_loop examine/deposit range of addresses
|
||
*/
|
||
|
||
t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int flag, char *cptr,
|
||
REG *lowr, REG *highr, t_addr lows, t_addr highs)
|
||
{
|
||
t_stat reason;
|
||
t_addr idx;
|
||
t_value val;
|
||
REG *rptr;
|
||
|
||
if ((lowr == NULL) || (highr == NULL)) return SCPE_IERR;
|
||
if (lowr > highr) return SCPE_ARG;
|
||
for (rptr = lowr; rptr <= highr; rptr++) {
|
||
if ((sim_switches & SWHIDE) &&
|
||
(rptr -> flags & REG_HIDDEN)) continue;
|
||
for (idx = lows; idx <= highs; idx++) {
|
||
if (idx >= (t_addr) rptr -> depth) return SCPE_SUB;
|
||
val = get_rval (rptr, idx);
|
||
if (schptr && !test_search (val, schptr)) continue;
|
||
if (flag != EX_D) {
|
||
reason = ex_reg (ofile, val, flag, rptr, idx);
|
||
if (reason != SCPE_OK) return reason;
|
||
if (sim_log && (ofile == stdout))
|
||
ex_reg (sim_log, val, flag, rptr, idx); }
|
||
if (flag != EX_E) {
|
||
reason = dep_reg (flag, cptr, rptr, idx);
|
||
if (reason != SCPE_OK) return reason; } } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int flag, char *cptr,
|
||
t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr)
|
||
{
|
||
t_addr i, mask;
|
||
t_stat reason;
|
||
|
||
if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */
|
||
reason = 0;
|
||
mask = (t_addr) width_mask[dptr -> awidth];
|
||
if ((low > mask) || (high > mask) || (low > high)) return SCPE_ARG;
|
||
for (i = low; i <= high; i = i + (dptr -> aincr)) {
|
||
reason = get_aval (i, dptr, uptr); /* get data */
|
||
if (reason != SCPE_OK) return reason; /* return if error */
|
||
if (schptr && !test_search (sim_eval[0], schptr)) continue;
|
||
if (flag != EX_D) {
|
||
reason = ex_addr (ofile, flag, i, dptr, uptr);
|
||
if (reason > SCPE_OK) return reason;
|
||
if (sim_log && (ofile == stdout))
|
||
ex_addr (sim_log, flag, i, dptr, uptr); }
|
||
if (flag != EX_E) {
|
||
reason = dep_addr (flag, cptr, i, dptr, uptr, reason);
|
||
if (reason > SCPE_OK) return reason; }
|
||
if (reason < SCPE_OK) i = i - (reason * dptr -> aincr); }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Examine register routine
|
||
|
||
Inputs:
|
||
ofile = output stream
|
||
val = current register value
|
||
flag = type of ex/mod command (ex, iex, idep)
|
||
rptr = pointer to register descriptor
|
||
idx = index
|
||
Outputs:
|
||
return = error status
|
||
*/
|
||
|
||
t_stat ex_reg (FILE *ofile, t_value val, int flag, REG *rptr, t_addr idx)
|
||
{
|
||
int32 rdx;
|
||
|
||
if (rptr == NULL) return SCPE_IERR;
|
||
if (rptr -> depth > 1) fprintf (ofile, "%s[%d]: ", rptr -> name, idx);
|
||
else fprintf (ofile, "%s: ", rptr -> name);
|
||
if (!(flag & EX_E)) return SCPE_OK;
|
||
GET_RADIX (rdx, rptr -> radix);
|
||
fprint_val (ofile, val, rdx, rptr -> width, rptr -> flags & REG_FMT);
|
||
if (flag & EX_I) fprintf (ofile, " ");
|
||
else fprintf (ofile, "\n");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Get register value
|
||
|
||
Inputs:
|
||
rptr = pointer to register descriptor
|
||
idx = index
|
||
Outputs:
|
||
return = register value
|
||
*/
|
||
|
||
t_value get_rval (REG *rptr, int idx)
|
||
{
|
||
size_t sz;
|
||
t_value val;
|
||
UNIT *uptr;
|
||
|
||
sz = SZ_R (rptr);
|
||
if ((rptr -> depth > 1) && (rptr -> flags & REG_UNIT)) {
|
||
uptr = ((UNIT *) rptr -> loc) + idx;
|
||
val = *((uint32 *) uptr); }
|
||
else if ((rptr -> depth > 1) && (sz == sizeof (uint8)))
|
||
val = *(((uint8 *) rptr -> loc) + idx);
|
||
else if ((rptr -> depth > 1) && (sz == sizeof (uint16)))
|
||
val = *(((uint16 *) rptr -> loc) + idx);
|
||
#if !defined (t_int64)
|
||
else val = *(((uint32 *) rptr -> loc) + idx);
|
||
#else
|
||
else if (sz <= sizeof (uint32))
|
||
val = *(((uint32 *) rptr -> loc) + idx);
|
||
else val = *(((t_uint64 *) rptr -> loc) + idx);
|
||
#endif
|
||
val = (val >> rptr -> offset) & width_mask[rptr -> width];
|
||
return val;
|
||
}
|
||
|
||
/* Deposit register routine
|
||
|
||
Inputs:
|
||
flag = type of deposit (normal/interactive)
|
||
cptr = pointer to input string
|
||
rptr = pointer to register descriptor
|
||
idx = index
|
||
Outputs:
|
||
return = error status
|
||
*/
|
||
|
||
t_stat dep_reg (int flag, char *cptr, REG *rptr, t_addr idx)
|
||
{
|
||
t_stat r;
|
||
t_value val, mask;
|
||
int32 rdx;
|
||
char gbuf[CBUFSIZE];
|
||
|
||
if ((cptr == NULL) || (rptr == NULL)) return SCPE_IERR;
|
||
if (rptr -> flags & REG_RO) return SCPE_RO;
|
||
if (flag & EX_I) {
|
||
cptr = read_line (gbuf, CBUFSIZE, stdin);
|
||
if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr);
|
||
if (cptr == NULL) return 1; /* force exit */
|
||
if (*cptr == 0) return SCPE_OK; } /* success */
|
||
mask = width_mask[rptr -> width];
|
||
GET_RADIX (rdx, rptr -> radix);
|
||
val = get_uint (cptr, rdx, mask, &r);
|
||
if (r != SCPE_OK) return SCPE_ARG;
|
||
if ((rptr -> flags & REG_NZ) && (val == 0)) return SCPE_ARG;
|
||
put_rval (rptr, idx, val);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Put register value
|
||
|
||
Inputs:
|
||
rptr = pointer to register descriptor
|
||
idx = index
|
||
val = new value
|
||
mask = mask
|
||
Outputs:
|
||
none
|
||
*/
|
||
|
||
void put_rval (REG *rptr, int idx, t_value val)
|
||
{
|
||
size_t sz;
|
||
t_value mask;
|
||
UNIT *uptr;
|
||
|
||
#define PUT_RVAL(sz,rp,id,v,m) \
|
||
*(((sz *) rp -> loc) + id) = \
|
||
(*(((sz *) rp -> loc) + id) & \
|
||
~((m) << (rp) -> offset)) | ((v) << (rp) -> offset)
|
||
|
||
if (rptr == sim_PC) sim_brk_npc ();
|
||
sz = SZ_R (rptr);
|
||
mask = width_mask[rptr -> width];
|
||
if ((rptr -> depth > 1) && (rptr -> flags & REG_UNIT)) {
|
||
uptr = ((UNIT *) rptr -> loc) + idx;
|
||
*((uint32 *) uptr) =
|
||
(*((uint32 *) uptr) &
|
||
~(((uint32) mask) << rptr -> offset)) |
|
||
(((uint32) val) << rptr -> offset); }
|
||
else if ((rptr -> depth > 1) && (sz == sizeof (uint8)))
|
||
PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask);
|
||
else if ((rptr -> depth > 1) && (sz == sizeof (uint16)))
|
||
PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask);
|
||
#if !defined (t_int64)
|
||
else PUT_RVAL (uint32, rptr, idx, val, mask);
|
||
#else
|
||
else if (sz <= sizeof (uint32))
|
||
PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask);
|
||
else PUT_RVAL (t_uint64, rptr, idx, val, mask);
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
/* Examine address routine
|
||
|
||
Inputs: (sim_eval is an implicit argument)
|
||
ofile = output stream
|
||
flag = type of ex/mod command (ex, iex, idep)
|
||
addr = address to examine
|
||
dptr = pointer to device
|
||
uptr = pointer to unit
|
||
Outputs:
|
||
return = if >= 0, error status
|
||
if < 0, number of extra words retired
|
||
*/
|
||
|
||
t_stat ex_addr (FILE *ofile, int flag, t_addr addr, DEVICE *dptr, UNIT *uptr)
|
||
{
|
||
t_stat reason;
|
||
int32 rdx;
|
||
|
||
fprint_val (ofile, addr, dptr -> aradix, dptr -> awidth, PV_LEFT);
|
||
fprintf (ofile, ": ");
|
||
if (!(flag & EX_E)) return SCPE_OK;
|
||
|
||
GET_RADIX (rdx, dptr -> dradix);
|
||
if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0)
|
||
reason = fprint_val (ofile, sim_eval[0], rdx, dptr -> dwidth, PV_RZRO);
|
||
if (flag & EX_I) fprintf (ofile, " ");
|
||
else fprintf (ofile, "\n");
|
||
return reason;
|
||
}
|
||
|
||
/* Get address routine
|
||
|
||
Inputs:
|
||
flag = type of ex/mod command (ex, iex, idep)
|
||
addr = address to examine
|
||
dptr = pointer to device
|
||
uptr = pointer to unit
|
||
Outputs: (sim_eval is an implicit output)
|
||
return = error status
|
||
*/
|
||
|
||
t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr)
|
||
{
|
||
int32 i;
|
||
t_value mask;
|
||
t_addr j, loc;
|
||
t_stat reason;
|
||
size_t sz;
|
||
|
||
if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR;
|
||
mask = width_mask[dptr -> dwidth];
|
||
for (i = 0; i < sim_emax; i++) sim_eval[i] = 0;
|
||
for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr -> aincr) {
|
||
if (dptr -> examine != NULL) {
|
||
reason = dptr -> examine (&sim_eval[i], j, uptr, sim_switches);
|
||
if (reason != SCPE_OK) break; }
|
||
else { if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT;
|
||
if ((uptr -> flags & UNIT_FIX) && (j >= uptr -> capac)) {
|
||
reason = SCPE_NXM;
|
||
break; }
|
||
sz = SZ_D (dptr);
|
||
loc = j / dptr -> aincr;
|
||
if (uptr -> flags & UNIT_BUF) {
|
||
SZ_LOAD (sz, sim_eval[i], uptr -> filebuf, loc); }
|
||
else { fseek (uptr -> fileref, sz * loc, SEEK_SET);
|
||
fxread (&sim_eval[i], sz, 1, uptr -> fileref);
|
||
if ((feof (uptr -> fileref)) &&
|
||
!(uptr -> flags & UNIT_FIX)) {
|
||
reason = SCPE_EOF;
|
||
break; }
|
||
else if (ferror (uptr -> fileref)) {
|
||
clearerr (uptr -> fileref);
|
||
reason = SCPE_IOERR;
|
||
break; } } }
|
||
sim_eval[i] = sim_eval[i] & mask; }
|
||
if ((reason != SCPE_OK) && (i == 0)) return reason;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Deposit address routine
|
||
|
||
Inputs:
|
||
flag = type of deposit (normal/interactive)
|
||
cptr = pointer to input string
|
||
addr = address to examine
|
||
dptr = pointer to device
|
||
uptr = pointer to unit
|
||
dfltinc = value to return on cr input
|
||
Outputs:
|
||
return = if >= 0, error status
|
||
if < 0, number of extra words retired
|
||
*/
|
||
|
||
t_stat dep_addr (int flag, char *cptr, t_addr addr, DEVICE *dptr,
|
||
UNIT *uptr, int dfltinc)
|
||
{
|
||
int32 i, count, rdx;
|
||
t_addr j, loc;
|
||
t_stat r, reason;
|
||
t_value mask;
|
||
size_t sz;
|
||
char gbuf[CBUFSIZE];
|
||
|
||
if (dptr == NULL) return SCPE_IERR;
|
||
if (flag & EX_I) {
|
||
cptr = read_line (gbuf, CBUFSIZE, stdin);
|
||
if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr);
|
||
if (cptr == NULL) return 1; /* force exit */
|
||
if (*cptr == 0) return dfltinc; } /* success */
|
||
if (uptr -> flags & UNIT_RO) return SCPE_RO; /* read only? */
|
||
mask = width_mask[dptr -> dwidth];
|
||
|
||
GET_RADIX (rdx, dptr -> dradix);
|
||
if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) {
|
||
sim_eval[0] = get_uint (cptr, rdx, mask, &reason);
|
||
if (reason != SCPE_OK) return reason; }
|
||
count = 1 - reason;
|
||
|
||
for (i = 0, j = addr; i < count; i++, j = j + dptr -> aincr) {
|
||
sim_eval[i] = sim_eval[i] & mask;
|
||
if (dptr -> deposit != NULL) {
|
||
r = dptr -> deposit (sim_eval[i], j, uptr, sim_switches);
|
||
if (r != SCPE_OK) return r; }
|
||
else { if (!(uptr -> flags & UNIT_ATT)) return SCPE_UNATT;
|
||
if ((uptr -> flags & UNIT_FIX) && (j >= uptr -> capac))
|
||
return SCPE_NXM;
|
||
sz = SZ_D (dptr);
|
||
loc = j / dptr -> aincr;
|
||
if (uptr -> flags & UNIT_BUF) {
|
||
SZ_STORE (sz, sim_eval[i], uptr -> filebuf, loc);
|
||
if (loc >= uptr -> hwmark) uptr -> hwmark = loc + 1; }
|
||
else { fseek (uptr -> fileref, sz * loc, SEEK_SET);
|
||
fxwrite (sim_eval, sz, 1, uptr -> fileref);
|
||
if (ferror (uptr -> fileref)) {
|
||
clearerr (uptr -> fileref);
|
||
return SCPE_IOERR; } } } }
|
||
return reason;
|
||
}
|
||
|
||
/* String processing routines
|
||
|
||
read_line read line
|
||
|
||
Inputs:
|
||
cptr = pointer to buffer
|
||
size = maximum size
|
||
stream = pointer to input stream
|
||
Outputs:
|
||
optr = pointer to first non-blank character
|
||
NULL if EOF
|
||
*/
|
||
|
||
char *read_line (char *cptr, int size, FILE *stream)
|
||
{
|
||
char *tptr;
|
||
|
||
cptr = fgets (cptr, size, stream); /* get cmd line */
|
||
if (cptr == NULL) {
|
||
clearerr (stream); /* clear error */
|
||
return NULL; } /* ignore EOF */
|
||
for (tptr = cptr; tptr < (cptr + size); tptr++) /* remove cr */
|
||
if (*tptr == '\n') *tptr = 0;
|
||
while (isspace (*cptr)) cptr++; /* absorb spaces */
|
||
if (*cptr == ';') *cptr = 0; /* ignore comment */
|
||
return cptr;
|
||
}
|
||
|
||
/* get_glyph get next glyph (force upper case)
|
||
get_glyph_nc get next glyph (no conversion)
|
||
get_glyph_gen get next glyph (general case)
|
||
|
||
Inputs:
|
||
iptr = pointer to input string
|
||
optr = pointer to output string
|
||
mchar = optional end of glyph character
|
||
flag = TRUE for convert to upper case (_gen only)
|
||
Outputs
|
||
result = pointer to next character in input string
|
||
*/
|
||
|
||
char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc)
|
||
{
|
||
while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) {
|
||
if (islower (*iptr) && uc) *optr = toupper (*iptr);
|
||
else *optr = *iptr;
|
||
iptr++; optr++; }
|
||
*optr = 0;
|
||
if (mchar && (*iptr == mchar)) iptr++; /* skip terminator */
|
||
while (isspace (*iptr)) iptr++; /* absorb spaces */
|
||
return iptr;
|
||
}
|
||
|
||
char *get_glyph (char *iptr, char *optr, char mchar)
|
||
{
|
||
return get_glyph_gen (iptr, optr, mchar, TRUE);
|
||
}
|
||
|
||
char *get_glyph_nc (char *iptr, char *optr, char mchar)
|
||
{
|
||
return get_glyph_gen (iptr, optr, mchar, FALSE);
|
||
}
|
||
|
||
/* get_yn yes/no question
|
||
|
||
Inputs:
|
||
cptr = pointer to question
|
||
deflt = default answer
|
||
Outputs:
|
||
result = true if yes, false if no
|
||
*/
|
||
|
||
t_stat get_yn (char *ques, t_stat deflt)
|
||
{
|
||
char cbuf[CBUFSIZE], *cptr;
|
||
|
||
printf ("%s ", ques);
|
||
cptr = read_line (cbuf, CBUFSIZE, stdin);
|
||
if ((cptr == NULL) || (*cptr == 0)) return deflt;
|
||
if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE;
|
||
return FALSE;
|
||
}
|
||
|
||
/* get_uint unsigned number
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
radix = input radix
|
||
max = maximum acceptable value
|
||
*status = pointer to error status
|
||
Outputs:
|
||
val = value
|
||
*/
|
||
|
||
t_value get_uint (char *cptr, int radix, t_value max, t_stat *status)
|
||
{
|
||
t_value val;
|
||
char *tptr;
|
||
|
||
val = strtotv (cptr, &tptr, radix);
|
||
if ((cptr == tptr) || (val > max) || (*tptr != 0)) *status = SCPE_ARG;
|
||
else *status = SCPE_OK;
|
||
return val;
|
||
}
|
||
|
||
/* get_range range specification
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
*lo = pointer to low result
|
||
*hi = pointer to high result
|
||
aradix = address radix
|
||
max = default high value
|
||
term = terminating character, 0 if none
|
||
Outputs:
|
||
tptr = input pointer after processing
|
||
NULL if error
|
||
*/
|
||
|
||
char *get_range (char *cptr, t_addr *lo, t_addr *hi, int rdx,
|
||
t_addr max, char term)
|
||
{
|
||
char *tptr;
|
||
t_addr hb;
|
||
|
||
*lo = *hi = hb = 0;
|
||
if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */
|
||
tptr = cptr + strlen ("ALL");
|
||
*hi = max; }
|
||
else { errno = 0;
|
||
*lo = strtoul (cptr, &tptr, rdx); /* get low */
|
||
if (errno || (cptr == tptr)) return NULL; /* error? */
|
||
if ((*tptr == '-') || (*tptr == ':') || (*tptr == '/')) {
|
||
if (*tptr == '/') hb = *lo; /* relative? */
|
||
cptr = tptr + 1;
|
||
errno = 0;
|
||
*hi = hb + strtoul (cptr, &tptr, rdx); /* get high */
|
||
if (errno || (cptr == tptr)) return NULL;
|
||
if (*lo > *hi) return NULL; }
|
||
else *hi = *lo; }
|
||
if (term && (*tptr++ != term)) return NULL;
|
||
return tptr;
|
||
}
|
||
|
||
/* Find_device find device matching input string
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
Outputs:
|
||
result = pointer to device
|
||
*/
|
||
|
||
DEVICE *find_dev (char *cptr)
|
||
{
|
||
int32 i;
|
||
DEVICE *dptr;
|
||
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
|
||
if (strcmp (cptr, dptr -> name) == 0) return dptr; }
|
||
return NULL;
|
||
}
|
||
|
||
/* Find_unit find unit matching input string
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
uptr = pointer to unit pointer
|
||
Outputs:
|
||
result = pointer to device (null if no dev)
|
||
*iptr = pointer to unit (null if nx unit)
|
||
*/
|
||
|
||
DEVICE *find_unit (char *cptr, UNIT **uptr)
|
||
{
|
||
int32 i, lenn, u;
|
||
t_stat r;
|
||
DEVICE *dptr;
|
||
|
||
if (uptr == NULL) return NULL; /* arg error? */
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* exact match? */
|
||
if (strcmp (cptr, dptr -> name) == 0) {
|
||
if (qdisable (dptr)) return NULL; /* disabled? */
|
||
*uptr = dptr -> units; /* unit 0 */
|
||
return dptr; } }
|
||
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */
|
||
lenn = strlen (dptr -> name);
|
||
if ((dptr -> numunits == 0) || /* no units? */
|
||
(strncmp (cptr, dptr -> name, lenn) != 0)) continue;
|
||
if (qdisable (dptr)) return NULL; /* disabled? */
|
||
cptr = cptr + lenn; /* skip devname */
|
||
u = (int32) get_uint (cptr, 10, dptr -> numunits - 1, &r);
|
||
if (r != SCPE_OK) *uptr = NULL; /* error? */
|
||
else *uptr = dptr -> units + u;
|
||
return dptr; }
|
||
return NULL;
|
||
}
|
||
|
||
/* Find_dev_from_unit find device for unit
|
||
|
||
Inputs:
|
||
uptr = pointer to unit
|
||
Outputs:
|
||
result = pointer to device
|
||
*/
|
||
|
||
DEVICE *find_dev_from_unit (UNIT *uptr)
|
||
{
|
||
DEVICE *dptr;
|
||
int32 i, j;
|
||
|
||
if (uptr == NULL) return NULL;
|
||
for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
|
||
for (j = 0; j < dptr -> numunits; j++) {
|
||
if (uptr == (dptr -> units + j)) return dptr; } }
|
||
return NULL;
|
||
}
|
||
|
||
/* find_reg_glob find globally unique register
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
optr = pointer to output pointer (can be null)
|
||
gdptr = pointer to global device
|
||
Outputs:
|
||
result = pointer to register, NULL if error
|
||
*optr = pointer to next character in input string
|
||
*gdptr = pointer to device where found
|
||
*/
|
||
|
||
REG *find_reg_glob (char *cptr, char **optr, DEVICE **gdptr)
|
||
{
|
||
int32 i;
|
||
DEVICE *dptr;
|
||
REG *rptr, *srptr = NULL;
|
||
|
||
for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */
|
||
if (rptr = find_reg (cptr, optr, dptr)) { /* found? */
|
||
if (srptr) return NULL; /* ambig? err */
|
||
srptr = rptr; /* save reg */
|
||
*gdptr = dptr; } } /* save unit */
|
||
return srptr;
|
||
}
|
||
|
||
/* find_reg find register matching input string
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
optr = pointer to output pointer (can be null)
|
||
dptr = pointer to device
|
||
Outputs:
|
||
result = pointer to register, NULL if error
|
||
*optr = pointer to next character in input string
|
||
*/
|
||
|
||
REG *find_reg (char *cptr, char **optr, DEVICE *dptr)
|
||
{
|
||
char *tptr;
|
||
REG *rptr;
|
||
|
||
if ((cptr == NULL) || (dptr == NULL) ||
|
||
(dptr -> registers == NULL)) return NULL;
|
||
tptr = cptr;
|
||
do { tptr++; }
|
||
while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_'));
|
||
for (rptr = dptr -> registers; rptr -> name != NULL; rptr++) {
|
||
if (strncmp (cptr, rptr -> name, tptr - cptr) == 0) {
|
||
if (optr != NULL) *optr = tptr;
|
||
return rptr; } }
|
||
return NULL;
|
||
}
|
||
|
||
/* get_switches get switches from input string
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
Outputs:
|
||
sw = switch bit mask
|
||
0 if no switches, -1 if error
|
||
*/
|
||
|
||
int32 get_switches (char *cptr)
|
||
{
|
||
int32 sw;
|
||
|
||
if (*cptr != '-') return 0;
|
||
sw = 0;
|
||
for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) {
|
||
if (isalpha (*cptr) == 0) return -1;
|
||
sw = sw | SWMASK (*cptr); }
|
||
return sw;
|
||
}
|
||
|
||
t_bool match_ext (char *fnam, char *ext)
|
||
{
|
||
char *fptr, *eptr;
|
||
|
||
if ((fnam == NULL) || (ext == NULL)) return FALSE;
|
||
fptr = strrchr (fnam, '.');
|
||
if (fptr == NULL) return FALSE;
|
||
for (fptr++, eptr = ext; *fptr; fptr++, eptr++) {
|
||
if (toupper (*fptr) != toupper (*eptr)) return FALSE; }
|
||
if (*eptr) return FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
/* Get search specification
|
||
|
||
Inputs:
|
||
cptr = pointer to input string
|
||
dptr = pointer to device
|
||
schptr = pointer to search table
|
||
Outputs:
|
||
return = NULL if error
|
||
schptr if valid search specification
|
||
*/
|
||
|
||
SCHTAB *get_search (char *cptr, DEVICE *dptr, SCHTAB *schptr)
|
||
{
|
||
int c, logop, cmpop;
|
||
t_value logval, cmpval;
|
||
char *sptr, *tptr;
|
||
const char logstr[] = "|&^", cmpstr[] = "=!><";
|
||
|
||
if (*cptr == 0) return NULL; /* check for clause */
|
||
for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */
|
||
if (sptr = strchr (logstr, c)) { /* check for mask */
|
||
logop = sptr - logstr;
|
||
logval = strtotv (cptr, &tptr, dptr -> dradix);
|
||
if (cptr == tptr) return NULL;
|
||
cptr = tptr; }
|
||
else if (sptr = strchr (cmpstr, c)) { /* check for bool */
|
||
cmpop = sptr - cmpstr;
|
||
if (*cptr == '=') {
|
||
cmpop = cmpop + strlen (cmpstr);
|
||
cptr++; }
|
||
cmpval = strtotv (cptr, &tptr, dptr -> dradix);
|
||
if (cptr == tptr) return NULL;
|
||
cptr = tptr; }
|
||
else return NULL; } /* end while */
|
||
if (logop >= 0) {
|
||
schptr -> logic = logop;
|
||
schptr -> mask = logval; }
|
||
if (cmpop >= 0) {
|
||
schptr -> bool = cmpop;
|
||
schptr -> comp = cmpval; }
|
||
return schptr;
|
||
}
|
||
|
||
/* Test value against search specification
|
||
|
||
Inputs:
|
||
val = value to test
|
||
schptr = pointer to search table
|
||
Outputs:
|
||
return = 1 if value passes search criteria, 0 if not
|
||
*/
|
||
|
||
int test_search (t_value val, SCHTAB *schptr)
|
||
{
|
||
if (schptr == NULL) return 0;
|
||
switch (schptr -> logic) { /* case on logical */
|
||
case SCH_OR:
|
||
val = val | schptr -> mask;
|
||
break;
|
||
case SCH_AND:
|
||
val = val & schptr -> mask;
|
||
break;
|
||
case SCH_XOR:
|
||
val = val ^ schptr -> mask;
|
||
break; }
|
||
switch (schptr -> bool) { /* case on comparison */
|
||
case SCH_E: case SCH_EE:
|
||
return (val == schptr -> comp);
|
||
case SCH_N: case SCH_NE:
|
||
return (val != schptr -> comp);
|
||
case SCH_G:
|
||
return (val > schptr -> comp);
|
||
case SCH_GE:
|
||
return (val >= schptr -> comp);
|
||
case SCH_L:
|
||
return (val < schptr -> comp);
|
||
case SCH_LE:
|
||
return (val <= schptr -> comp); }
|
||
return 0;
|
||
}
|
||
|
||
/* Radix independent input/output package
|
||
|
||
strtotv - general radix input routine
|
||
|
||
Inputs:
|
||
inptr = string to convert
|
||
endptr = pointer to first unconverted character
|
||
radix = radix for input
|
||
Outputs:
|
||
value = converted value
|
||
|
||
On an error, the endptr will equal the inptr.
|
||
*/
|
||
|
||
t_value strtotv (char *inptr, char **endptr, int radix)
|
||
{
|
||
int nodigit;
|
||
t_value val;
|
||
int c, digit;
|
||
|
||
*endptr = inptr; /* assume fails */
|
||
if ((radix < 2) || (radix > 36)) return 0;
|
||
while (isspace (*inptr)) inptr++; /* bypass white space */
|
||
val = 0;
|
||
nodigit = 1;
|
||
for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */
|
||
if (islower (c)) c = toupper (c);
|
||
if (isdigit (c)) digit = c - (int) '0'; /* digit? */
|
||
else digit = c + 10 - (int) 'A'; /* no, letter */
|
||
if (digit >= radix) return 0; /* valid in radix? */
|
||
val = (val * radix) + digit; /* add to value */
|
||
nodigit = 0; }
|
||
if (nodigit) return 0; /* no digits? */
|
||
*endptr = inptr; /* result pointer */
|
||
return val;
|
||
}
|
||
|
||
/* fprint_val - general radix printing routine
|
||
|
||
Inputs:
|
||
stream = stream designator
|
||
val = value to print
|
||
radix = radix to print
|
||
width = width to print
|
||
format = leading zeroes format
|
||
Outputs:
|
||
status = error status
|
||
*/
|
||
|
||
t_stat fprint_val (FILE *stream, t_value val, int radix,
|
||
int width, int format)
|
||
{
|
||
#define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value)))
|
||
t_value owtest, wtest;
|
||
int32 d, digit, ndigits;
|
||
char dbuf[MAX_WIDTH + 1];
|
||
|
||
for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' ';
|
||
dbuf[MAX_WIDTH] = 0;
|
||
d = MAX_WIDTH;
|
||
do { d = d - 1;
|
||
digit = (int32) (val % (unsigned) radix);
|
||
val = val / (unsigned) radix;
|
||
dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10);
|
||
} while ((d > 0) && (val != 0));
|
||
|
||
if (format != PV_LEFT) {
|
||
wtest = owtest = radix;
|
||
ndigits = 1;
|
||
while ((wtest < width_mask[width]) && (wtest >= owtest)) {
|
||
owtest = wtest;
|
||
wtest = wtest * radix;
|
||
ndigits = ndigits + 1; }
|
||
if ((MAX_WIDTH - ndigits) < d) d = MAX_WIDTH - ndigits; }
|
||
if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Event queue package
|
||
|
||
sim_activate add entry to event queue
|
||
sim_cancel remove entry from event queue
|
||
sim_process_event process entries on event queue
|
||
sim_is_active see if entry is on event queue
|
||
sim_atime return absolute time for an entry
|
||
sim_gtime return global time
|
||
sim_qcount return event queue entry count
|
||
|
||
Asynchronous events are set up by queueing a unit data structure
|
||
to the event queue with a timeout (in simulator units, relative
|
||
to the current time). Each simulator 'times' these events by
|
||
counting down interval counter sim_interval. When this reaches
|
||
zero the simulator calls sim_process_event to process the event
|
||
and to see if further events need to be processed, or sim_interval
|
||
reset to count the next one.
|
||
|
||
The event queue is maintained in clock order; entry timeouts are
|
||
RELATIVE to the time in the previous entry.
|
||
|
||
sim_process_event - process event
|
||
|
||
Inputs:
|
||
none
|
||
Outputs:
|
||
reason reason code returned by any event processor,
|
||
or 0 (SCPE_OK) if no exceptions
|
||
*/
|
||
|
||
t_stat sim_process_event (void)
|
||
{
|
||
UNIT *uptr;
|
||
t_stat reason;
|
||
|
||
if (stop_cpu) return SCPE_STOP; /* stop CPU? */
|
||
if (sim_clock_queue == NULL) { /* queue empty? */
|
||
UPDATE_SIM_TIME (noqueue_time); /* update sim time */
|
||
sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
|
||
return SCPE_OK; }
|
||
UPDATE_SIM_TIME (sim_clock_queue -> time); /* update sim time */
|
||
do { uptr = sim_clock_queue; /* get first */
|
||
sim_clock_queue = uptr -> next; /* remove first */
|
||
uptr -> next = NULL; /* hygiene */
|
||
uptr -> time = 0;
|
||
if (sim_clock_queue != NULL) sim_interval = sim_clock_queue -> time;
|
||
else sim_interval = noqueue_time = NOQUEUE_WAIT;
|
||
if (uptr -> action != NULL) reason = uptr -> action (uptr);
|
||
else reason = SCPE_OK;
|
||
} while ((reason == SCPE_OK) && (sim_interval == 0));
|
||
|
||
/* Empty queue forces sim_interval != 0 */
|
||
|
||
return reason;
|
||
}
|
||
|
||
/* sim_activate - activate (queue) event
|
||
|
||
Inputs:
|
||
uptr = pointer to unit
|
||
event_time = relative timeout
|
||
Outputs:
|
||
reason = result (SCPE_OK if ok)
|
||
*/
|
||
|
||
t_stat sim_activate (UNIT *uptr, int32 event_time)
|
||
{
|
||
UNIT *cptr, *prvptr;
|
||
int32 accum;
|
||
|
||
if (event_time < 0) return SCPE_IERR;
|
||
if (sim_is_active (uptr)) return SCPE_OK; /* already active? */
|
||
if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }
|
||
else { UPDATE_SIM_TIME (sim_clock_queue -> time); } /* update sim time */
|
||
|
||
prvptr = NULL;
|
||
accum = 0;
|
||
for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) {
|
||
if (event_time < accum + cptr -> time) break;
|
||
accum = accum + cptr -> time;
|
||
prvptr = cptr; }
|
||
if (prvptr == NULL) { /* insert at head */
|
||
cptr = uptr -> next = sim_clock_queue;
|
||
sim_clock_queue = uptr; }
|
||
else { cptr = uptr -> next = prvptr -> next; /* insert at prvptr */
|
||
prvptr -> next = uptr; }
|
||
uptr -> time = event_time - accum;
|
||
if (cptr != NULL) cptr -> time = cptr -> time - uptr -> time;
|
||
sim_interval = sim_clock_queue -> time;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* sim_cancel - cancel (dequeue) event
|
||
|
||
Inputs:
|
||
uptr = pointer to unit
|
||
Outputs:
|
||
reason = result (SCPE_OK if ok)
|
||
|
||
*/
|
||
|
||
t_stat sim_cancel (UNIT *uptr)
|
||
{
|
||
UNIT *cptr, *nptr;
|
||
|
||
if (sim_clock_queue == NULL) return SCPE_OK;
|
||
UPDATE_SIM_TIME (sim_clock_queue -> time); /* update sim time */
|
||
nptr = NULL;
|
||
if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr -> next;
|
||
else { for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) {
|
||
if (cptr -> next == uptr) {
|
||
nptr = cptr -> next = uptr -> next;
|
||
break; } } } /* end queue scan */
|
||
if (nptr != NULL) nptr -> time = nptr -> time + uptr -> time;
|
||
uptr -> next = NULL; /* hygiene */
|
||
uptr -> time = 0;
|
||
if (sim_clock_queue != NULL) sim_interval = sim_clock_queue -> time;
|
||
else sim_interval = noqueue_time = NOQUEUE_WAIT;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* sim_is_active - test for entry in queue, return activation time
|
||
|
||
Inputs:
|
||
uptr = pointer to unit
|
||
Outputs:
|
||
result = absolute activation time + 1, 0 if inactive
|
||
*/
|
||
|
||
int32 sim_is_active (UNIT *uptr)
|
||
{
|
||
UNIT *cptr;
|
||
int32 accum;
|
||
|
||
accum = 0;
|
||
for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr -> next) {
|
||
accum = accum + cptr -> time;
|
||
if (cptr == uptr) return accum + 1; }
|
||
return 0;
|
||
}
|
||
|
||
/* sim_gtime - return global time
|
||
sim_grtime - return global time with rollover
|
||
|
||
Inputs: none
|
||
Outputs:
|
||
time = global time
|
||
*/
|
||
|
||
double sim_gtime (void)
|
||
{
|
||
if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }
|
||
else { UPDATE_SIM_TIME (sim_clock_queue -> time); }
|
||
return sim_time;
|
||
}
|
||
|
||
uint32 sim_grtime (void)
|
||
{
|
||
if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); }
|
||
else { UPDATE_SIM_TIME (sim_clock_queue -> time); }
|
||
return sim_rtime;
|
||
}
|
||
|
||
/* sim_qcount - return queue entry count
|
||
|
||
Inputs: none
|
||
Outputs:
|
||
count = number of entries on the queue
|
||
*/
|
||
|
||
int32 sim_qcount (void)
|
||
{
|
||
int32 cnt;
|
||
UNIT *uptr;
|
||
|
||
cnt = 0;
|
||
for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr -> next) cnt++;
|
||
return cnt;
|
||
}
|
||
|
||
/* Endian independent binary I/O package
|
||
|
||
For consistency, all binary data read and written by the simulator
|
||
is stored in little endian data order. That is, in a multi-byte
|
||
data item, the bytes are written out right to left, low order byte
|
||
to high order byte. On a big endian host, data is read and written
|
||
from high byte to low byte. Consequently, data written on a little
|
||
endian system must be byte reversed to be usable on a big endian
|
||
system, and vice versa.
|
||
|
||
These routines are analogs of the standard C runtime routines
|
||
fread and fwrite. If the host is little endian, or the data items
|
||
are size char, then the calls are passed directly to fread or
|
||
fwrite. Otherwise, these routines perform the necessary byte swaps
|
||
using an intermediate buffer.
|
||
*/
|
||
|
||
size_t fxread (void *bptr, size_t size, size_t count, FILE *fptr)
|
||
{
|
||
size_t c, j, nelem, nbuf, lcnt, total;
|
||
int32 i, k;
|
||
unsigned char *sptr, *dptr;
|
||
|
||
if (sim_end || (size == sizeof (char)))
|
||
return fread (bptr, size, count, fptr);
|
||
if ((size == 0) || (count == 0)) return 0;
|
||
nelem = FLIP_SIZE / size; /* elements in buffer */
|
||
nbuf = count / nelem; /* number buffers */
|
||
lcnt = count % nelem; /* count in last buf */
|
||
if (lcnt) nbuf = nbuf + 1;
|
||
else lcnt = nelem;
|
||
total = 0;
|
||
dptr = bptr; /* init output ptr */
|
||
for (i = nbuf; i > 0; i--) {
|
||
c = fread (sim_flip, size, (i == 1? lcnt: nelem), fptr);
|
||
if (c == 0) return total;
|
||
total = total + c;
|
||
for (j = 0, sptr = sim_flip; j < c; j++) {
|
||
for (k = size - 1; k >= 0; k--) *(dptr + k) = *sptr++;
|
||
dptr = dptr + size; } }
|
||
return total;
|
||
}
|
||
|
||
size_t fxwrite (void *bptr, size_t size, size_t count, FILE *fptr)
|
||
{
|
||
size_t c, j, nelem, nbuf, lcnt, total;
|
||
int32 i, k;
|
||
unsigned char *sptr, *dptr;
|
||
|
||
if (sim_end || (size == sizeof (char)))
|
||
return fwrite (bptr, size, count, fptr);
|
||
if ((size == 0) || (count == 0)) return 0;
|
||
nelem = FLIP_SIZE / size; /* elements in buffer */
|
||
nbuf = count / nelem; /* number buffers */
|
||
lcnt = count % nelem; /* count in last buf */
|
||
if (lcnt) nbuf = nbuf + 1;
|
||
else lcnt = nelem;
|
||
total = 0;
|
||
sptr = bptr; /* init input ptr */
|
||
for (i = nbuf; i > 0; i--) {
|
||
c = (i == 1)? lcnt: nelem;
|
||
for (j = 0, dptr = sim_flip; j < c; j++) {
|
||
for (k = size - 1; k >= 0; k--) *(dptr + k) = *sptr++;
|
||
dptr = dptr + size; }
|
||
c = fwrite (sim_flip, size, c, fptr);
|
||
if (c == 0) return total;
|
||
total = total + c; }
|
||
return total;
|
||
}
|
||
|
||
/* OS independent clock calibration package
|
||
|
||
sim_rtc_init initialize calibration
|
||
sim_rtc_calb calibrate clock
|
||
*/
|
||
|
||
static int32 rtc_ticks = 0; /* ticks */
|
||
static uint32 rtc_realtime = 0; /* real time */
|
||
static uint32 rtc_virttime = 0; /* virtual time */
|
||
static uint32 rtc_nextintv = 0; /* next interval */
|
||
static int32 rtc_basedelay = 0; /* base delay */
|
||
static int32 rtc_currdelay = 0; /* current delay */
|
||
extern t_bool rtc_avail;
|
||
#define TMAX 500 /* max makeup per sec */
|
||
|
||
int32 sim_rtc_init (int32 time)
|
||
{
|
||
rtc_realtime = sim_os_msec ();
|
||
rtc_virttime = rtc_realtime;
|
||
rtc_nextintv = 1000;
|
||
rtc_ticks = 0;
|
||
rtc_basedelay = time;
|
||
rtc_currdelay = time;
|
||
return rtc_currdelay;
|
||
}
|
||
|
||
int32 sim_rtc_calb (int32 ticksper)
|
||
{
|
||
uint32 new_realtime, delta_realtime;
|
||
int32 delta_virttime;
|
||
|
||
rtc_ticks = rtc_ticks + 1; /* count ticks */
|
||
if (rtc_ticks < ticksper) return rtc_currdelay; /* 1 sec yet? */
|
||
rtc_ticks = 0; /* reset ticks */
|
||
if (!rtc_avail) return rtc_currdelay; /* no timer? */
|
||
new_realtime = sim_os_msec (); /* wall time */
|
||
delta_realtime = new_realtime - rtc_realtime; /* elapsed wtime */
|
||
if (delta_realtime == 0) return rtc_currdelay; /* can't calibr */
|
||
rtc_basedelay = (int32) (((double) rtc_basedelay * (double) rtc_nextintv) /
|
||
((double) delta_realtime)); /* new base rate */
|
||
rtc_realtime = new_realtime; /* adv wall time */
|
||
rtc_virttime = rtc_virttime + 1000; /* adv sim time */
|
||
delta_virttime = rtc_virttime - rtc_realtime; /* gap */
|
||
if (delta_virttime > TMAX) delta_virttime = TMAX; /* limit gap */
|
||
else if (delta_virttime < -TMAX) delta_virttime = -TMAX;
|
||
rtc_nextintv = 1000 + delta_virttime; /* next wtime */
|
||
rtc_currdelay = (int32) (((double) rtc_basedelay * (double) rtc_nextintv) /
|
||
1000.0); /* next delay */
|
||
return rtc_currdelay;
|
||
}
|
||
|
||
/* Breakpoint package. This module replaces the VM-implemented one
|
||
instruction breakpoint capability.
|
||
|
||
Breakpoints are stored in table sim_brk_tab, which is ordered by address for
|
||
efficient binary searching. A breakpoint consists of a four entry structure:
|
||
|
||
addr address of the breakpoint
|
||
type types of breakpoints set on the address
|
||
a bit mask representing letters A-Z
|
||
cnt number of iterations before breakp is taken
|
||
action pointer to list of commands to be executed
|
||
when break is taken
|
||
|
||
sim_brk_summ is a summary of the types of breakpoints that are currently set (it
|
||
is the bitwise OR of all the type fields). A simulator need only check for
|
||
a breakpoint of type X if bit SWMASK('X') is set in sim_brk_sum.
|
||
|
||
The package contains the following public routines:
|
||
|
||
sim_brk_init initialize
|
||
sim_brk_pchg PC change
|
||
sim_brk_set set breakpoint
|
||
sim_brk_clr clear breakpoint
|
||
sim_brk_clrall clear all breakpoints
|
||
sim_brk_show show breakpoint
|
||
sim_brk_showall show all breakpoints
|
||
sim_brk_test test for breakpoint
|
||
sim_brk_npc PC has been changed
|
||
|
||
Initialize breakpoint system. If simulator has filled in sim_brk_types,
|
||
allocate an initial breakpoint table, otherwise, allocate a minimal table.
|
||
*/
|
||
|
||
t_stat sim_brk_init (void)
|
||
{
|
||
if (sim_brk_types) sim_brk_lnt = SIM_BRK_INILNT;
|
||
else sim_brk_lnt = 1;
|
||
sim_brk_tab = calloc (sim_brk_lnt, sizeof (BRKTAB));
|
||
if (sim_brk_tab == NULL) return SCPE_MEM;
|
||
sim_brk_ent = sim_brk_ins = 0;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Search for a breakpoint in the sorted breakpoint table */
|
||
|
||
BRKTAB *sim_brk_fnd (t_addr loc)
|
||
{
|
||
int32 lo, hi, p;
|
||
BRKTAB *bp;
|
||
|
||
if (sim_brk_ent == 0) { /* table empty? */
|
||
sim_brk_ins = 0; /* insrt at head */
|
||
return NULL; } /* sch fails */
|
||
lo = 0; /* initial bounds */
|
||
hi = sim_brk_ent - 1;
|
||
do { p = (lo + hi) >> 1; /* probe */
|
||
bp = sim_brk_tab + p; /* table addr */
|
||
if (loc == bp -> addr) return bp; /* match? */
|
||
else if (loc < bp -> addr) hi = p - 1; /* go down? p is upper */
|
||
else lo = p + 1; } /* go up? p is lower */
|
||
while (lo <= hi);
|
||
if (loc < bp -> addr) sim_brk_ins = p; /* insrt before or */
|
||
else sim_brk_ins = p + 1; /* after last sch */
|
||
return NULL;
|
||
}
|
||
|
||
/* Insert a breakpoint */
|
||
|
||
BRKTAB *sim_brk_new (t_addr loc)
|
||
{
|
||
BRKTAB *bp;
|
||
|
||
if (sim_brk_ins < 0) return NULL;
|
||
if (sim_brk_ent >= sim_brk_lnt) return NULL;
|
||
if (sim_brk_ins != sim_brk_ent) { /* move needed? */
|
||
for (bp = sim_brk_tab + sim_brk_ent;
|
||
bp > sim_brk_tab + sim_brk_ins; bp--)
|
||
*bp = *(bp - 1); }
|
||
bp = sim_brk_tab + sim_brk_ins;
|
||
bp -> addr = loc;
|
||
bp -> typ = 0;
|
||
bp -> cnt = 0;
|
||
bp -> act = NULL;
|
||
sim_brk_ent = sim_brk_ent + 1;
|
||
return bp;
|
||
}
|
||
|
||
/* Set a breakpoint of type sw */
|
||
|
||
t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt)
|
||
{
|
||
BRKTAB *bp;
|
||
|
||
if (sw == 0) sw = sim_brk_dflt;
|
||
if ((sim_brk_types & sw) == 0) return SCPE_NOFNC;
|
||
bp = sim_brk_fnd (loc);
|
||
if (!bp) bp = sim_brk_new (loc);
|
||
if (!bp) return SCPE_MEM;
|
||
bp -> typ = sw;
|
||
bp -> cnt = ncnt;
|
||
sim_brk_summ = sim_brk_summ | sw;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Clear a breakpoint */
|
||
|
||
t_stat sim_brk_clr (t_addr loc, int32 sw)
|
||
{
|
||
BRKTAB *bp = sim_brk_fnd (loc);
|
||
|
||
if (!bp) return SCPE_OK;
|
||
if (sw == 0) sw = SIM_BRK_ALLTYP;
|
||
bp -> typ = bp -> typ & ~sw;
|
||
if (bp -> typ) return SCPE_OK;
|
||
for ( ; bp < (sim_brk_tab + sim_brk_ent - 1); bp++)
|
||
*bp = *(bp + 1);
|
||
sim_brk_ent = sim_brk_ent - 1;
|
||
sim_brk_summ = 0;
|
||
for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++)
|
||
sim_brk_summ = sim_brk_summ | bp -> typ;
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Clear all breakpoints */
|
||
|
||
t_stat sim_brk_clrall (int32 sw)
|
||
{
|
||
BRKTAB *bp;
|
||
|
||
if (sw == 0) sw = SIM_BRK_ALLTYP;
|
||
if ((sim_brk_summ & ~sw) == 0) sim_brk_ent = sim_brk_summ = 0;
|
||
else { for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) {
|
||
if (bp -> typ & sw) sim_brk_clr (bp -> addr, sw); } }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show a breakpoint */
|
||
|
||
t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw)
|
||
{
|
||
BRKTAB *bp = sim_brk_fnd (loc);
|
||
DEVICE *dptr;
|
||
int32 i, any;
|
||
|
||
if (sw == 0) sw = SIM_BRK_ALLTYP;
|
||
if (!bp || (!(bp -> typ & sw))) return SCPE_OK;
|
||
dptr = sim_devices[0];
|
||
if (dptr == NULL) return SCPE_OK;
|
||
fprint_val (st, loc, dptr -> aradix, dptr -> awidth, PV_LEFT);
|
||
fprintf (st, ":\t");
|
||
for (i = any = 0; i < 26; i++) {
|
||
if ((bp -> typ >> i) & 1) {
|
||
if (any) fprintf (st, ", ");
|
||
fputc (i + 'A', st);
|
||
any = 1; } }
|
||
if (bp -> cnt > 0) fprintf (st, " [%d]", bp -> cnt);
|
||
fprintf (st, "\n");
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Show all breakpoints */
|
||
|
||
t_stat sim_brk_showall (FILE *st, int32 sw)
|
||
{
|
||
BRKTAB *bp;
|
||
|
||
if (sw == 0) sw = SIM_BRK_ALLTYP;
|
||
for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) {
|
||
if (bp -> typ & sw) sim_brk_show (st, bp -> addr, sw); }
|
||
return SCPE_OK;
|
||
}
|
||
|
||
/* Test for breakpoint */
|
||
|
||
t_bool sim_brk_test (t_addr loc, int32 btyp)
|
||
{
|
||
BRKTAB *bp;
|
||
|
||
if ((bp = sim_brk_fnd (loc)) &&
|
||
(btyp & bp -> typ) &&
|
||
(!sim_brk_pend || (loc != sim_brk_ploc)) &&
|
||
(--(bp -> cnt) <= 0)) {
|
||
bp -> cnt = 0;
|
||
sim_brk_ploc = loc;
|
||
sim_brk_pend = TRUE;
|
||
return TRUE; }
|
||
sim_brk_pend = FALSE;
|
||
return FALSE;
|
||
}
|
||
|
||
/* New PC */
|
||
|
||
void sim_brk_npc (void)
|
||
{
|
||
sim_brk_pend = FALSE;
|
||
return;
|
||
}
|