URL
https://opencores.org/ocsvn/w11/w11/trunk
Subversion Repositories w11
[/] [w11/] [tags/] [w11a_V0.5/] [tools/] [bin/] [pi_rri] - Rev 17
Go to most recent revision | Compare with Previous | Blame | View Log
#!/usr/bin/perl -w
# $Id: pi_rri 314 2010-07-09 17:38:41Z mueller $
#
# Copyright 2007-2010 by Walter F.J. Mueller <W.F.J.Mueller@gsi.de>
#
# This program is free software; you may redistribute and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 2, or at your option any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for complete details.
#
# Revision History:
# Date Rev Version Comment
# 2010-06-27 310 1.6.2 fix autoflush for fh_log; duplicate exec err to log
# 2010-06-18 306 1.6.1 PDPCP_ADDR_IBRB now 020, PDPCP_ADDR_IBR now 0200;
# ibrbase now just drops the 6 lsb's; pdpcp mode:
# rename librb -> wibrb; finished cp/rri overhaul
# 2010-06-13 305 1.6 change PDPCP_ADDR_ assignments, add PDPCP_FUNC_
# constants; adapt pdpcp mode to changed rri addr and
# stim file format; emulate old 'sta' behaviour with
# new 'stapc' command; rename lal,lah -> wal,wah and
# implement locally; adapt serv11 mode to new rri
# 2010-06-11 303 1.5.1 add cmd_inter; flush{"line"} after interactive cmd
# added readline support.
# 2010-06-07 302 1.5 use sop/eop framing instead of soc+chaining
# 2010-06-06 301 1.4.18 rename .rpmon->.rbmon; NCOMM=4 (new eop,nak commas)
# 2010-06-03 299 1.4.17 adapt .reset to new rbus init decode
# 2010-05-05 288 1.4.16 first preliminary implementation of eval based
# perl macro's via @@pmac; add ^C signal handler;
# added optional min/max check for cget_gdat() and
# cget_tagval_gdat().
# 2010-05-02 287 1.4.15 enable hardware flow control in termios
# 2010-05-01 286 1.4.14 add .sinit command for rri and pdpcp modes;
# add 500k,1M,2M,3M baudrates, check value now
# 2010-04-26 284 1.4.13 add error check for GetOptions
# 2010-04-25 283 1.4.12 raw_rcv9_to: handle undef as return value correctly
# 2009-10-11 244 1.4.11 support > 115kbaud speed; new stat output in log;
# use nxbuf_max=8 as default;
# 2009-09-27 242 1.4.10 add "ERR --" messages in log file; fix usage of
# uninitialized vars in serv11_exec_probe();
# 2009-09-25 241 1.4.9 BUGFIX: serv11_server_attn_get() now or's attn bits
# 2009-09-20 240 1.4.8 serv11_rri_uset cache under $ctl, not in unittbl
# 2009-09-13 238 1.4.7 add ctlname for DL11 log entries; only TTA output
# written to stdout when no terminal attached.
# 2009-07-26 236 1.4.6 fix cpraw mode and .mode error handling;
# 2009-07-12 233 1.4.5 add attach/detach support for term; telnet support;
# 2009-07-05 232 1.4.4 add cchk_number(), cget_regrange(); rename _atdt_ to
# _attdet_; serv11: add exa/dep command; removed
# rr[0-7], wr[0-7], rmem, and wmem commands; new probe
# handling, use {probe_[ir]val} and {probemask};
# serv11: command parser supports abbeviations;
# serv11: add set/sho command, reorganize ls* cmds;
# serv11: add {trace} parameter; rri_sideband() flush;
# 2009-07-04 231 1.4.3 first reglist definitions; add CPU in _ctltbl;
# add serv11_probe_cpu();
# 2009-06-28 230 1.4.2 use serv11_atdt_pc11(), remove atdt via ucb hack;
# add serv11_rri_uset(); pc11 now fully supported;
# attn log message now gives device; use -e as file
# test; add ptape boot mode support; lsconf output
# sorted by ibus address;
# 2009-06-21 228 1.4.1 BUGFIX: correct typo in RK6 ucb; reorganize init
# handling, introduce usethdl; reorganize attach;
# add atdthdl at ctl or ucb level; add det command;
# 2009-06-20 227 1.4 nxbuf_min,max,inc now a ctl property;
# 2009-06-14 226 1.3.31 add very preliminary lp11 device handling
# 2009-06-11 225 1.3.30 quick hack to add dl11 log files.
# 2009-05-30 220 1.3.29 add papertape boot code;
# 2009-05-24 219 1.3.28 add papertape hook as dev "PC" units "PTR" and "PTP"
# 2009-05-21 217 1.3.27 rk11: now error message when init not done
# 2009-05-17 216 1.3.26 BUGFIX:rk11: fix read/write logic for short sectors;
# BUGFIX:rk11: re-work the seek complete handling
# add read/write check support; add PGE error support;
# 2009-05-13 215 1.3.25 dl11: drop parity bit in transmit path;
# rk11: add read/write format; set SOK at init time;
# 2009-05-10 214 1.3.24 BUGFIX: in serv11_attn_rk11() RKER was written
# instead of RKMR for RKCS_DRESET, _WLOCK, _SEEK
# 2009-04-11 206 1.3.23 add --int to force interactive mode
# fix handling of odd length records in lsabs
# 2008-12-14 177 1.3.22 correct DL11_BASE_B to 176500 (was RL address)
# 2008-11-28 173 1.3.21 serv11_rri_lalh(): allow now mode =0,1,2 and 3;
# serv11_rdma_rk11(): use mode=3 in rri_lalh, thus
# enable 22bit and ubmap simultaneously.
# add proper MEX update for RKCS in rdma_rk11.
# add proper DRSEL propagation for RKDA in rdma_rk11.
# 2008-05-30 151 1.3.20 BUGFIX: corrected @que_rcv logic in serv11 input
# loop, TT0 output will not longer hang sometimes;
# disable the attn+ioto(16 or 63 ms) hack;
# 2008-05-23 150 1.3.19 tio[89b] messages: to $fh_log now, add delta-time;
# fixes in disk extend logic and messages; add the
# (^c form of ( command; add adaptive read vector
# in serv11_attn_dl11;
# 2008-05-22 149 1.3.18 add term_tios_print, fixes for --term under cygwin;
# 2008-05-22 148 1.3.17 add (,< short-cut commands
# 2008-05-18 147 1.3.16 ATTN comma drop now warning, -W (was -I);
# 2008-05-12 145 1.3.15 rename ldpta -> ldabs
# 2008-05-09 144 1.3.14 disable call in attn_dispatch(1) (needs busy logic)
# 2008-05-01 142 1.3.13 serv11: add stop,cont,reset; add $force for attn
# handlers; use attn+ioto(16ms) to avoid TTO hangs
# 2008-05-01 141 1.3.12 write TTO char-by-char now to STDOUT
# 2008-04-25 138 1.3.11 show ccc/scc for code 000257/000277 in disassembler
# 2008-04-19 137 1.3.10 minor fix disassembler: use f reg prefix for f4,f5
# 2008-04-18 136 1.3.9 hack in a poor man's output to console...
# 2008-04-13 135 1.3.8 substitute environment variables in cmd file names
# 2008-04-11 134 1.3.7 allow line comments starting with ";" (for simh)
# 2008-04-06 133 1.3.6 fix file check in serv11_cexec_ldpta; fix wrong
# opcode for mfps; fixed bug in disassembling mode=77
# add -a for lsmem;
# 2008-04-04 132 1.3.5 add in disassembler non-11/70 and fpp codes;
# add (>|>>)file option for lsmem (redirect to file)
# 2008-03-39 131 1.3.4 lsmem -m: use now wide (3 word) symbolic dump format
# add ldpta command (load paper tape abs format)
# add start and step command
# 2008-03-24 129 1.3.3 allow comments when in serv11 server mode.
# check attach file size; zero-fill after partial
# block write; reorganize dsk file and rdma handling
# leading blanks now ignored in commands
# 2008-03-22 128 1.3.2 fully functional server mode (todo: TT <-> telnet)
# 2008-03-19 127 1.3.1 very preliminary server mode now working
# 2008-03-09 124 1.3 add serv11 mode; add PDP11 disamssebler
# 2008-03-02 121 1.2.5 the default .sdef now checks for hard errors.
# the _open handlers for rri and pdpcp setup default
# Add .cerr, .merr as ignored directives for pdpcp
# 2008-02-24 119 1.2.4 added lah,rps,wps command in .pdpcp mode
# 2008-02-16 116 1.2.3 add librb,[rw]ibr,and wtlam to pdpcp command set
# 2007-12-25 105 1.2.2 for rri mode add .dbas[io] (set base for data vals)
# add ${par}, ${par:=val}, ${par:-val} substitution
# allow parameter definition via par=val lines
# add $[..perl code..] escape to embed perl code
# allow @file(arg1,arg2,arg3,...); print .wtlam wait
# 2007-11-24 98 1.2.1 adapt to new rri internal init handling
# 2007-11-18 96 1.2 add 'read before write' logic to avoid deadlocks
# under cygwin broken fifo (size=1 !) implementation
# 2007-10-12 88 1.1.4 fix some -w issues
# 2007-09-23 84 1.1.3 .reset command in pdpcp mode; keep-alive in --fifo
# 2007-09-16 83 1.1.2 add --cmax; full --term implemented
# 2007-09-09 80 1.1.1 add --run; modularize I/O handling; initial --term
# proper return code / retry loop for sysread/write
# 2007-09-09 80 1.1 new non-blocking/blocking handling; ignore IDLE's
# and unexpected ATTN commas; add <mode>_flush; add
# data check handling, command chaining, 'pdpcp' mode.
# 2007-09-02 79 1.0.1 implement 'rri' mode
# 2007-09-01 78 1.0 Initial version
use 5.005; # require Perl 5.005 or higher
use strict; # require strict checking
use FileHandle;
use POSIX qw(mkfifo isatty :termios_h);
use Fcntl qw(O_WRONLY O_RDONLY O_NOCTTY);
use Errno qw(EINTR);
use Time::HiRes qw(gettimeofday);
use Socket;
use Term::ReadLine;
use constant CPREF => 0x80;
use constant NCOMM => 4;
use constant CESC => CPREF|0x0f ;
use constant CEN1 => (~CPREF)&0xf0;
use constant D9IDLE => 0x100;
use constant D9SOP => 0x101;
use constant D9EOP => 0x102;
use constant D9NAK => 0x103;
use constant D9ATTN => 0x104;
use constant PDPCP_ADDR_CONF => 000;
use constant PDPCP_ADDR_CNTL => 001;
use constant PDPCP_ADDR_STAT => 002;
use constant PDPCP_ADDR_PSW => 003;
use constant PDPCP_ADDR_AL => 004;
use constant PDPCP_ADDR_AH => 005;
use constant PDPCP_ADDR_MEM => 006;
use constant PDPCP_ADDR_MEMI => 007;
use constant PDPCP_ADDR_R0 => 010;
use constant PDPCP_ADDR_PC => 017;
use constant PDPCP_ADDR_IBRB => 020;
use constant PDPCP_ADDR_IBR => 0200;
use constant PDPCP_FUNC_NOOP => 000;
use constant PDPCP_FUNC_STA => 001;
use constant PDPCP_FUNC_STO => 002;
use constant PDPCP_FUNC_CONT => 003;
use constant PDPCP_FUNC_STEP => 004;
use constant PDPCP_FUNC_RST => 017;
use constant LINUX_B57600 => 0010001; # B57600 not part of POSIX package !
use constant LINUX_B115200 => 0010002; # in linux these values are in
use constant LINUX_B230400 => 0010003; # termios.h, specifically in
use constant LINUX_B460800 => 0010004; # /usr/include/bits/termios.h
use constant LINUX_B500000 => 0010005;
use constant LINUX_B576000 => 0010006;
use constant LINUX_B921600 => 0010007;
use constant LINUX_B1000000 => 0010010;
use constant LINUX_B2000000 => 0010013;
use constant LINUX_B3000000 => 0010015;
use constant LINUX_CRTSCTS => 020000000000; # ! Not part of POSIX !!
use Getopt::Long;
my %opts = ();
GetOptions(\%opts, "help", "int", "trace",
"tio8", "tio9", "tiob",
"dserv", "tserv", "log:s",
"fifo:s", "term:s",
"timeout=f", "cmax=i",
"run=s",
)
or die "bad options";
sub init_regtbl; # initialize regtbl from reglist
sub get_command;
sub do_command;
sub read_command;
sub setpar_command;
sub nomode_open;
sub nomode_flush;
sub nomode_close;
sub nomode_cexec;
sub cpraw_open;
sub cpraw_flush;
sub cpraw_close;
sub cpraw_cexec;
sub do_cprx;
sub do_cptx;
sub cpraw_tx_match_now;
sub cpraw_tx_match;
sub rri_open;
sub rri_flush;
sub rri_close;
sub rri_cexec;
sub rri_cget_stat;
sub rri_cget_addr;
sub rri_cget_nblk;
sub rri_sideband;
sub rri_cmdlist_do;
sub rri_cmdlist_dump;
sub rri_cmdlist_exec;
sub rri_cmdlist_check_stat;
sub rri_cmdlist_get_rval;
sub rri_cmdlist_conv_rval;
sub rri_ref_check; # check reference data (1=err)
sub pdpcp_open;
sub pdpcp_flush;
sub pdpcp_close;
sub pdpcp_cexec;
sub pdpcp_cmd_rreg;
sub pdpcp_cmd_wreg;
sub serv11_open;
sub serv11_flush;
sub serv11_close;
sub serv11_cexec;
sub serv11_cexec_shoreg;
sub serv11_cexec_shommu_ssrx;
sub serv11_cexec_shommu_sadr;
sub serv11_cexec_ldabs;
sub serv11_cexec_shoconf;
sub serv11_cexec_shoatt;
sub serv11_cexec_attdet;
sub serv11_cexec_boot;
sub serv11_cexec_exa;
sub serv11_cexec_dep;
sub serv11_config;
sub serv11_init_dispatch;
sub serv11_server;
sub serv11_server_attn_get;
sub serv11_server_attn_dispatch;
sub serv11_probe_gen; # generic probe handler
sub serv11_init_gen; # generic controller init handler
sub serv11_detach_gen; # generic detach handler
sub serv11_attdet_disk; # generic disk att/det handler
sub serv11_attdet_ronly; # generic in only att/det handler
sub serv11_attdet_wonly; # generic out only att/det handler
sub serv11_attdet_term; # generic term att/det handler
sub serv11_probe_cpu; # cpu: probe handler
sub serv11_attn_cpu; # cpu: attention handler
sub serv11_exadep_cpu; # cpu: exa/dep handler
sub serv11_ichr_dl11;
sub serv11_attn_dl11;
sub serv11_uset_lp11;
sub serv11_attn_lp11;
sub serv11_uset_pc11;
sub serv11_attdet_pc11;
sub serv11_attn_pc11;
sub serv11_uset_rk11;
sub serv11_attn_rk11;
sub serv11_attn_rk11_logerr;
sub serv11_rdma_rk11;
sub serv11_icb_disk_read; # read one dsk file block
sub serv11_icb_disk_write; # write one dsk file block
sub serv11_rri_init; # issue rri init command
sub serv11_rri_attn; # issue rri attn command
sub serv11_rri_stat; # issue rri stat command
sub serv11_rri_rreg; # issue rri rreg command
sub serv11_rri_wreg; # issue rri wreg command
sub serv11_rri_rblk; # issue rri rblk command
sub serv11_rri_wblk; # issue rri wblk command
sub serv11_rri_lalh; # issue pdpcp lal and lah commands
sub serv11_rri_ibrb; # issue rbus set base address
sub serv11_rri_ribr; # issue rbus read
sub serv11_rri_wibr; # issue rbus write
sub serv11_rri_clear;
sub serv11_rri_exec;
sub serv11_rri_uset; # issue rbus uset writes
sub serv11_exec_rblk;
sub serv11_exec_wblk;
sub serv11_exec_probe;
sub next_nxbuf; # calculate next nxbuf value
sub telnet_readhdl; # telnet: socket read handler
sub telnet_writehdl; # telnet: write handler
sub pdp11_disassemble; # simple PDP11 disassembler
sub pdp11_disassemble_regmod; # helper
sub file_seek; # fseek wrapper
sub file_read; # fread wrapper
sub file_seek_read; # fseek+fread wrapper
sub file_write; # fwrite wrapper
sub file_seek_write; # fseek+fwrite wrapper
sub raw_get9_crc_16bit; # read 16 bit value
sub raw_get9_crc_8bit; # read 8bit value
sub raw_get9_crc_check; # get 9bit, block, crc, ref value
sub raw_get9_check; # get 9bit, block, expect ref value
sub raw_get9_checksop; # get 9bit, block, expect 'sop'
sub raw_get9_checkeop; # get 9bit, block, expect 'eop'
sub raw_get9_crc; # get 9bit, block, update crc
sub raw_get9; # get 9bit, block
sub raw_snd9_crc; # put 9bit to RX, update crc
sub raw_snd9; # put 9bit to RX
sub raw_snd8; # put 8bit to RX
sub raw_rcv9; # get 9bit from TX, non-blocking
sub raw_rcv8; # get 8bit from TX, non-blocking
sub raw_rcv9_to; # get 9bit from TX, expl. time-out
sub raw_rcv8_to; # get 8bit from TX, expl. time-out
sub wait_sel_filercv; # poll/wait for RCV to be ready
sub fifo_open; # chan fifo: open handler
sub fifo_close; # chan fifo: close handler
sub term_open; # chan term: open handler
sub term_close; # chan term: close handler
sub term_tios_print; # chan term: print termios state
sub genio_read; # generic io: read handler
sub genio_write; # generic io: write handler
sub cget_chkblank; # check for unused chars in cmd line
sub cget_tagval2_gdat; # get tag=v1[,v2], generic base
sub cget_tagval_gdat; # get tag=val, generic base
sub cget_gdat; # get generic base value
sub cget_name; # get name \w+
sub cget_bool; # get boolean [01]
sub cget_file; # get filename [\w\/.]+
sub cget_ucb; # get ucb (read name, return ucb)
sub cget_opt; # get option
sub cget_optset; # get option set
sub cget_regrange; # get register/memory range
sub cchk_number; # check for number. any gdat value
sub sget_bdat; # convert 01 string -> binary value
sub conv_etime; # generate timestamp string
sub conv_dat9;
sub conv_dat8;
sub conv_str2bytes; # string to bytelist; handle \n
sub conv_buf2wlist; # string buffer -> word list
sub conv_wlist2buf; # word list -> string buffer
sub conv_byte2ascii2; # byte -> 2 charcter ASCII display
sub gconv_dat16;
sub hdl_sigint; # SIGINT handler
sub get_time;
sub get_timestamp;
sub filename_expand; # expand $nnn in name
sub print_fatal;
sub print_help;
my %stat_tab = ( obyte => 0.,
oesc => 0.,
osop => 0.,
ibyte => 0.,
iesc => 0.,
att => 0.,
xreg => 0.,
xblk => 0.,
rdisk => 0.,
wdisk => 0.);
my %stat_tab_last = %stat_tab;
my %mode_tab = (nomode => {open => \&nomode_open,
flush => \&nomode_flush,
close => \&nomode_close,
cmd => \&nomode_cexec},
cpraw => {open => \&cpraw_open,
flush => \&cpraw_flush,
close => \&cpraw_close,
cmd => \&cpraw_cexec},
rri => {open => \&rri_open,
flush => \&rri_flush,
close => \&rri_close,
cmd => \&rri_cexec},
pdpcp => {open => \&pdpcp_open,
flush => \&pdpcp_flush,
close => \&pdpcp_close,
cmd => \&pdpcp_cexec},
serv11 => {open => \&serv11_open,
flush => \&serv11_flush,
close => \&serv11_close,
cmd => \&serv11_cexec}
);
my %chan_tab = (fifo => {open => \&fifo_open,
close => \&fifo_close,
read => \&genio_read,
write => \&genio_write},
term => {open => \&term_open,
close => \&term_close,
read => \&genio_read,
write => \&genio_write}
);
my $curmode = "nomode";
my $curcmd = \&nomode_cexec;
my $curchan = undef;
my @cmdfh;
my @cmdfn;
my @cmdargs;
my $time0 = -1;
my $tlast_tio8 = 0;
my $tlast_tio9 = 0;
my $tlast_tiob = 0;
my @que_rcv;
my @que_snd;
my @cpraw_tx_read;
my @cpraw_tx_expt;
my $fh_log = *STDOUT;
my $fh_snd;
my $fh_rcv;
my $fdset_filercv;
my $fifo_keep;
my $term_oldtios;
my $raw_rcv_esc = 0;
my $raw_timeout = 1.;
my $cmax = 16;
my $cmd_line;
my $cmd_rest;
my $cmd_bad;
my $cmd_inter; # interactive cmd flag
my $term;
if (-t STDIN) {
$term = new Term::ReadLine 'pi_rri';
}
my %par; # params for command line substitution
my $sigint_count = 0; # SIGINT counter
use constant TELNET_CODE_NULL => 0;
use constant TELNET_CODE_LF => 10;
use constant TELNET_CODE_CR => 13;
use constant TELNET_CODE_ESC => 27;
use constant TELNET_CODE_SE => 240;
use constant TELNET_CODE_NOP => 241;
use constant TELNET_CODE_IP => 244;
use constant TELNET_CODE_GA => 249;
use constant TELNET_CODE_SB => 250;
use constant TELNET_CODE_WILL => 251;
use constant TELNET_CODE_WONT => 252;
use constant TELNET_CODE_DO => 253;
use constant TELNET_CODE_DONT => 254;
use constant TELNET_CODE_IAC => 255;
use constant TELNET_OPT_BIN => 0;
use constant TELNET_OPT_ECHO => 1;
use constant TELNET_OPT_SGA => 3;
use constant TELNET_OPT_TTYP => 24;
use constant TELNET_OPT_LINE => 34;
use constant TELNET_STATE_LISTEN => -1;
use constant TELNET_STATE_STREAM => 0;
use constant TELNET_STATE_IAC => 1;
use constant TELNET_STATE_CMD => 2;
use constant TELNET_STATE_SUBNEG => 3;
use constant TELNET_STATE_SUBIAC => 4;
#
# %telnettbl->{snum} --> telnet session table, hash of hashes, key'ed by port
# -> {port} port number (int)
# -> {state} state: (_LISTEN|_STREAM|_IAC|_CMD|_SUBNEG|_SUBIAC)
# -> {fh_port} file handle of port socket (for listen)
# -> {fh_data} file handle of data socket
# -> {ucb} ucb the port is attached to
#
my %telnettbl;
my $rri_ref_sdef = 0x00; # by default check for 'hard' errors
my $rri_msk_sdef = 0xf0; # ignore the status bits + attn flag
my %rri_amtbl;
my @rri_cmdlist;
my $rri_rvalcnt = 0;
my $rri_ncmdmax = undef;
my $rri_dbasi = 2; # default input base
my $rri_dbaso = 8; # default output base
my $rri_nodfill = " " x 5; # filler string for "d=-" stanzas
my %rri_cname2cmd = (rreg => 0, # c_rri_cmd_rreg : slv3 := "000";
rblk => 1, # c_rri_cmd_rblk : slv3 := "001";
wreg => 2, # c_rri_cmd_wreg : slv3 := "010";
wblk => 3, # c_rri_cmd_wblk : slv3 := "011";
stat => 4, # c_rri_cmd_stat : slv3 := "100";
attn => 5, # c_rri_cmd_attn : slv3 := "101";
init => 6); # c_rri_cmd_init : slv3 := "110";
my @crc8_tbl = ( 0, 29, 58, 39, 116, 105, 78, 83, # from gen_crc8_tbl
232, 245, 210, 207, 156, 129, 166, 187,
205, 208, 247, 234, 185, 164, 131, 158,
37, 56, 31, 2, 81, 76, 107, 118,
135, 154, 189, 160, 243, 238, 201, 212,
111, 114, 85, 72, 27, 6, 33, 60,
74, 87, 112, 109, 62, 35, 4, 25,
162, 191, 152, 133, 214, 203, 236, 241,
19, 14, 41, 52, 103, 122, 93, 64,
251, 230, 193, 220, 143, 146, 181, 168,
222, 195, 228, 249, 170, 183, 144, 141,
54, 43, 12, 17, 66, 95, 120, 101,
148, 137, 174, 179, 224, 253, 218, 199,
124, 97, 70, 91, 8, 21, 50, 47,
89, 68, 99, 126, 45, 48, 23, 10,
177, 172, 139, 150, 197, 216, 255, 226,
38, 59, 28, 1, 82, 79, 104, 117,
206, 211, 244, 233, 186, 167, 128, 157,
235, 246, 209, 204, 159, 130, 165, 184,
3, 30, 57, 36, 119, 106, 77, 80,
161, 188, 155, 134, 213, 200, 239, 242,
73, 84, 115, 110, 61, 32, 7, 26,
108, 113, 86, 75, 24, 5, 34, 63,
132, 153, 190, 163, 240, 237, 202, 215,
53, 40, 15, 18, 65, 92, 123, 102,
221, 192, 231, 250, 169, 180, 147, 142,
248, 229, 194, 223, 140, 145, 182, 171,
16, 13, 42, 55, 100, 121, 94, 67,
178, 175, 136, 149, 198, 219, 252, 225,
90, 71, 96, 125, 46, 51, 20, 9,
127, 98, 69, 88, 11, 22, 49, 44,
151, 138, 173, 176, 227, 254, 217, 196);
my $ocrc = 0;
my $icrc = 0;
my $kpid = -1;
my @pdp11_opcode_tbl = (
{code=>0000000, mask=>0000000, name=>"halt", type=>"0arg"},
{code=>0000001, mask=>0000000, name=>"wait", type=>"0arg"},
{code=>0000002, mask=>0000000, name=>"rti ", type=>"0arg"},
{code=>0000003, mask=>0000000, name=>"bpt ", type=>"0arg"},
{code=>0000004, mask=>0000000, name=>"iot ", type=>"0arg"},
{code=>0000005, mask=>0000000, name=>"reset",type=>"0arg"},
{code=>0000006, mask=>0000000, name=>"rtt ", type=>"0arg"},
{code=>0000007, mask=>0000000, name=>"!!mfpt", type=>"0arg"},
{code=>0000100, mask=>0000077, name=>"jmp ", type=>"1arg"},
{code=>0000200, mask=>0000007, name=>"rts ", type=>"1reg"},
{code=>0000230, mask=>0000007, name=>"spl ", type=>"spl"},
{code=>0000240, mask=>0000017, name=>"cl", type=>"ccop"},
{code=>0000260, mask=>0000017, name=>"se", type=>"ccop"},
{code=>0000300, mask=>0000077, name=>"swap", type=>"1arg"},
{code=>0000400, mask=>0000377, name=>"br ", type=>"br"},
{code=>0001000, mask=>0000377, name=>"bne ", type=>"br"},
{code=>0001400, mask=>0000377, name=>"beq ", type=>"br"},
{code=>0002000, mask=>0000377, name=>"bge ", type=>"br"},
{code=>0002400, mask=>0000377, name=>"blt ", type=>"br"},
{code=>0003000, mask=>0000377, name=>"bgt ", type=>"br"},
{code=>0003400, mask=>0000377, name=>"ble ", type=>"br"},
{code=>0004000, mask=>0000777, name=>"jsr ", type=>"jsr"},
{code=>0005000, mask=>0000077, name=>"clr ", type=>"1arg"},
{code=>0005100, mask=>0000077, name=>"com ", type=>"1arg"},
{code=>0005200, mask=>0000077, name=>"inc ", type=>"1arg"},
{code=>0005300, mask=>0000077, name=>"dec ", type=>"1arg"},
{code=>0005400, mask=>0000077, name=>"neg ", type=>"1arg"},
{code=>0005500, mask=>0000077, name=>"adc ", type=>"1arg"},
{code=>0005600, mask=>0000077, name=>"sbc ", type=>"1arg"},
{code=>0005700, mask=>0000077, name=>"tst ", type=>"1arg"},
{code=>0006000, mask=>0000077, name=>"ror ", type=>"1arg"},
{code=>0006100, mask=>0000077, name=>"rol ", type=>"1arg"},
{code=>0006200, mask=>0000077, name=>"asr ", type=>"1arg"},
{code=>0006300, mask=>0000077, name=>"asl ", type=>"1arg"},
{code=>0006400, mask=>0000077, name=>"mark", type=>"mark"},
{code=>0006500, mask=>0000077, name=>"mfpi", type=>"1arg"},
{code=>0006600, mask=>0000077, name=>"mtpi", type=>"1arg"},
{code=>0006700, mask=>0000077, name=>"sxt ", type=>"1arg"},
{code=>0007000, mask=>0000077, name=>"!!csm", type=>"1arg"},
{code=>0007200, mask=>0000077, name=>"!!tstset",type=>"1arg"},
{code=>0007300, mask=>0000077, name=>"!!wrtlck",type=>"1arg"},
{code=>0010000, mask=>0007777, name=>"mov ", type=>"2arg"},
{code=>0020000, mask=>0007777, name=>"cmp ", type=>"2arg"},
{code=>0030000, mask=>0007777, name=>"bit ", type=>"2arg"},
{code=>0040000, mask=>0007777, name=>"bic ", type=>"2arg"},
{code=>0050000, mask=>0007777, name=>"bis ", type=>"2arg"},
{code=>0060000, mask=>0007777, name=>"add ", type=>"2arg"},
{code=>0070000, mask=>0000777, name=>"mul ", type=>"rdst"},
{code=>0071000, mask=>0000777, name=>"div ", type=>"rdst"},
{code=>0072000, mask=>0000777, name=>"ash ", type=>"rdst"},
{code=>0073000, mask=>0000777, name=>"ashc", type=>"rdst"},
{code=>0074000, mask=>0000777, name=>"xor ", type=>"rdst"},
{code=>0077000, mask=>0000777, name=>"sob ", type=>"sob"},
{code=>0100000, mask=>0000377, name=>"bpl ", type=>"br"},
{code=>0100400, mask=>0000377, name=>"bmi ", type=>"br"},
{code=>0101000, mask=>0000377, name=>"bhi ", type=>"br"},
{code=>0101400, mask=>0000377, name=>"blos", type=>"br"},
{code=>0102000, mask=>0000377, name=>"bvc ", type=>"br"},
{code=>0102400, mask=>0000377, name=>"bvs ", type=>"br"},
{code=>0103000, mask=>0000377, name=>"bcc ", type=>"br"},
{code=>0103400, mask=>0000377, name=>"bcs ", type=>"br"},
{code=>0104000, mask=>0000377, name=>"emt ", type=>"trap"},
{code=>0104400, mask=>0000377, name=>"trap", type=>"trap"},
{code=>0105000, mask=>0000077, name=>"clrb", type=>"1arg"},
{code=>0105100, mask=>0000077, name=>"comb", type=>"1arg"},
{code=>0105200, mask=>0000077, name=>"incb", type=>"1arg"},
{code=>0105300, mask=>0000077, name=>"decb", type=>"1arg"},
{code=>0105400, mask=>0000077, name=>"negb", type=>"1arg"},
{code=>0105500, mask=>0000077, name=>"adcb", type=>"1arg"},
{code=>0105600, mask=>0000077, name=>"sbcb", type=>"1arg"},
{code=>0105700, mask=>0000077, name=>"tstb", type=>"1arg"},
{code=>0106000, mask=>0000077, name=>"rorb", type=>"1arg"},
{code=>0106100, mask=>0000077, name=>"rolb", type=>"1arg"},
{code=>0106200, mask=>0000077, name=>"asrb", type=>"1arg"},
{code=>0106300, mask=>0000077, name=>"aslb", type=>"1arg"},
{code=>0106400, mask=>0000077, name=>"!!mtps", type=>"1arg"},
{code=>0106500, mask=>0000077, name=>"mfpd", type=>"1arg"},
{code=>0106600, mask=>0000077, name=>"mtpd", type=>"1arg"},
{code=>0106700, mask=>0000077, name=>"!!mfps", type=>"1arg"},
{code=>0110000, mask=>0007777, name=>"movb", type=>"2arg"},
{code=>0120000, mask=>0007777, name=>"cmpb", type=>"2arg"},
{code=>0130000, mask=>0007777, name=>"bitb", type=>"2arg"},
{code=>0140000, mask=>0007777, name=>"bicb", type=>"2arg"},
{code=>0150000, mask=>0007777, name=>"bisb", type=>"2arg"},
{code=>0160000, mask=>0007777, name=>"sub ", type=>"2arg"},
{code=>0170000, mask=>0000000, name=>"!!cfcc", type=>"0arg"},
{code=>0170001, mask=>0000000, name=>"!!setf", type=>"0arg"},
{code=>0170011, mask=>0000000, name=>"!!setd", type=>"0arg"},
{code=>0170002, mask=>0000000, name=>"!!seti", type=>"0arg"},
{code=>0170012, mask=>0000000, name=>"!!setl", type=>"0arg"},
{code=>0170100, mask=>0000077, name=>"!!ldfps",type=>"1fpp"},
{code=>0170200, mask=>0000077, name=>"!!stfps",type=>"1fpp"},
{code=>0170300, mask=>0000077, name=>"!!stst", type=>"1fpp"},
{code=>0170400, mask=>0000077, name=>"!!clrf", type=>"1fpp"},
{code=>0170500, mask=>0000077, name=>"!!tstf", type=>"1fpp"},
{code=>0170600, mask=>0000077, name=>"!!absf", type=>"1fpp"},
{code=>0170700, mask=>0000077, name=>"!!negf", type=>"1fpp"},
{code=>0171000, mask=>0000377, name=>"!!mulf", type=>"rfpp"},
{code=>0171400, mask=>0000377, name=>"!!modf", type=>"rfpp"},
{code=>0172000, mask=>0000377, name=>"!!addf", type=>"rfpp"},
{code=>0172400, mask=>0000377, name=>"!!ldf", type=>"rfpp"},
{code=>0173000, mask=>0000377, name=>"!!subf", type=>"rfpp"},
{code=>0173400, mask=>0000377, name=>"!!cmpf", type=>"rfpp"},
{code=>0174000, mask=>0000377, name=>"!!stf", type=>"rfpp"},
{code=>0174400, mask=>0000377, name=>"!!divf", type=>"rfpp"},
{code=>0175000, mask=>0000377, name=>"!!stexp",type=>"rfpp"},
{code=>0175400, mask=>0000377, name=>"!!stcif",type=>"rfpp"},
{code=>0176000, mask=>0000377, name=>"!!stcfd",type=>"rfpp"},
{code=>0176400, mask=>0000377, name=>"!!ldexp",type=>"rfpp"},
{code=>0177000, mask=>0000377, name=>"!!ldcif",type=>"rfpp"},
{code=>0177400, mask=>0000377, name=>"!!ldcdf",type=>"rfpp"}
);
use constant BIT00 => 0000001;
use constant BIT01 => 0000002;
use constant BIT02 => 0000004;
use constant BIT03 => 0000010;
use constant BIT04 => 0000020;
use constant BIT05 => 0000040;
use constant BIT06 => 0000100;
use constant BIT07 => 0000200;
use constant BIT08 => 0000400;
use constant BIT09 => 0001000;
use constant BIT10 => 0002000;
use constant BIT11 => 0004000;
use constant BIT12 => 0010000;
use constant BIT13 => 0020000;
use constant BIT14 => 0040000;
use constant BIT15 => 0100000;
use constant REGATTR_RBMBOX => 0000001; # rbus is mailbox, skip on exa loop
use constant REGATTR_RBRD => 0000002; # by default read on rbus
use constant REGATTR_RBWR => 0000004; # by default write on rbus
use constant REGATTR_IBMBOX => 0000010; # ibus is mailbox, skip on exa loop
# some common defs
my @partbl_nxbuf = ( nxbuf_min => { type => "hval:d" },
nxbuf_inc => { type => "hval:d" },
nxbuf_max => { type => "hval:d" },
nxbuf => { type => "hval:d" } );
# CPU general defs
use constant CPU_MMR3 => 0172516;
use constant CPU_SDREG => 0177570;
use constant CPU_MMR0 => 0177572;
use constant CPU_MMR1 => 0177574;
use constant CPU_MMR2 => 0177576;
use constant CPU_LOSIZE => 0177760;
use constant CPU_HISIZE => 0177762;
use constant CPU_SYSID => 0177764;
use constant CPU_CPUERR => 0177766;
use constant CPU_MBRK => 0177770;
use constant CPU_PIRQ => 0177772;
use constant CPU_STKLIM => 0177774;
use constant CPU_PSW => 0177776;
# DL11 general defs
use constant DL11_BASE_A => 0177560;
use constant DL11_BASE_B => 0176500;
# DL11 address offsets
use constant DL11_RCSR => 00;
use constant DL11_RBUF => 02;
use constant DL11_XCSR => 04;
use constant DL11_XBUF => 06;
# DL11 register defs
use constant DL11_RCSR_M_RDONE => BIT07;
use constant DL11_XCSR_M_XRDY => BIT07;
use constant DL11_XBUF_M_RRDY => BIT09;
use constant DL11_XBUF_M_XVAL => BIT08;
use constant DL11_XBUF_M_XBUF => 0377;
my @reglist_dl11 = ({name => "rcsr",
offset => DL11_RCSR},
{name => "rbuf",
offset => DL11_RBUF,
attr => REGATTR_IBMBOX},
{name => "xcsr",
offset => DL11_XCSR},
{name => "xbuf",
offset => DL11_XBUF,
attr => REGATTR_RBMBOX});
my %partbl_dl11 = ( trace => { type => "hval:b" },
@partbl_nxbuf );
# LP11 general defs
use constant LP11_BASE => 0177514;
# LP11 address offsets
use constant LP11_CSR => 00;
use constant LP11_BUF => 02;
# LP11 register defs
use constant LP11_CSR_M_ERR => BIT15;
use constant LP11_BUF_M_VAL => BIT08;
use constant LP11_BUF_M_BUF => 0177;
my @reglist_lp11 = ({name => "csr",
offset => LP11_CSR},
{name => "buf",
offset => LP11_BUF,
attr => REGATTR_RBMBOX});
my %partbl_lp11 = ( trace => { type => "hval:b" },
@partbl_nxbuf );
# PC11 address offsets
use constant PC11_RCSR => 00;
use constant PC11_RBUF => 02;
use constant PC11_PCSR => 04;
use constant PC11_PBUF => 06;
# PC11 register defs
use constant PC11_RCSR_M_ERR => BIT15;
use constant PC11_PCSR_M_ERR => BIT15;
use constant PC11_PBUF_M_RBUSY => BIT09;
use constant PC11_PBUF_M_PVAL => BIT08;
use constant PC11_PBUF_M_PBUF => 0377;
my @reglist_pc11 = ({name => "rcsr",
offset => PC11_RCSR},
{name => "rbuf",
offset => PC11_RBUF,
attr => REGATTR_IBMBOX},
{name => "pcsr",
offset => PC11_PCSR},
{name => "pbuf",
offset => PC11_PBUF,
attr => REGATTR_RBMBOX});
my %partbl_pc11 = ( trace => { type => "hval:b" },
@partbl_nxbuf );
# RK11 general defs
use constant RK11_BASE => 0177400;
use constant RK11_NUMSE => 12; # number of sectors
use constant RK11_NUMHD => 2; # number of heads
use constant RK11_NUMCY => 203; # number of cylinders
use constant RK11_NUMDR => 8; # number of drives
use constant RK11_NUMBL => RK11_NUMSE * RK11_NUMHD * RK11_NUMCY;
use constant RK11_BLKSIZE => 512; # disk block size
use constant RK11_VOLSIZE => RK11_BLKSIZE * RK11_NUMBL; # disk volume size
# RK11 address offsets
use constant RK11_RKDS => 00;
use constant RK11_RKER => 02;
use constant RK11_RKCS => 04;
use constant RK11_RKWC => 06;
use constant RK11_RKBA => 010;
use constant RK11_RKDA => 012;
use constant RK11_RKMR => 014;
# RK11 register defs
use constant RKDS_M_ID => 0160000; # ID: drive number
use constant RKDS_V_ID => 13;
use constant RKDS_B_ID => 0007;
use constant RKDS_M_HDEN => BIT11; # HDEN: high density drive (rk05)
use constant RKDS_M_DRU => BIT10; # DRU: drive unsafe
use constant RKDS_M_SIN => BIT09; # SIN: seek incomplete
use constant RKDS_M_SOK => BIT08; # SOK: sector counter OK
use constant RKDS_M_DRY => BIT07; # DRY: drive ready
use constant RKDS_M_ADRY => BIT06; # ADRY: access ready
use constant RKDS_M_WPS => BIT05; # WPS: write protect
use constant RKDS_B_SC => 0017; # SC: sector counter
use constant RKER_M_DRE => BIT15; # DRE: drive error
use constant RKER_M_OVR => BIT14; # OVR: overrun
use constant RKER_M_WLO => BIT13; # WLO: write lock violation
use constant RKER_M_PGE => BIT11; # PGE: programming error
use constant RKER_M_NXM => BIT10; # NXM: non existent memory
use constant RKER_M_NXD => BIT07; # NXD: non existent drive
use constant RKER_M_NXC => BIT06; # NXC: non existent cylinder
use constant RKER_M_NXS => BIT05; # NXS: non existent sector
use constant RKER_M_CSE => BIT01; # CSE: check sum error
use constant RKER_M_WCE => BIT00; # WCE: write check error
use constant RKCS_M_MAINT => BIT12; # MAINT: maintenance mode
use constant RKCS_M_IBA => BIT11; # IBA: inhibit increment RKBA
use constant RKCS_M_FMT => BIT10; # FMT: format
use constant RKCS_M_RWA => BIT09; # RWA: read-write all
use constant RKCS_M_SSE => BIT08; # SSE: stop on soft errors
use constant RKCS_M_MEX => 0000060; # MEX: memory extension
use constant RKCS_V_MEX => 4;
use constant RKCS_B_MEX => 0003;
use constant RKCS_V_FUNC => 1; # FUNC: function
use constant RKCS_B_FUNC => 0007;
use constant RKCS_CRESET => 0;
use constant RKCS_WRITE => 1;
use constant RKCS_READ => 2;
use constant RKCS_WCHK => 3;
use constant RKCS_SEEK => 4;
use constant RKCS_RCHK => 5;
use constant RKCS_DRESET => 6;
use constant RKCS_WLOCK => 7;
use constant RKCS_M_GO => BIT00; # GO: go bit
use constant RKDA_M_DRSEL => 0160000; # DRSEL: drive number
use constant RKDA_V_DRSEL => 13;
use constant RKDA_B_DRSEL => 0007;
use constant RKDA_M_CYL => 0017740; # CYL: cyclinder address
use constant RKDA_V_CYL => 5;
use constant RKDA_B_CYL => 0377;
use constant RKDA_M_SUR => 0000020; # SUR: surface
use constant RKDA_V_SUR => 4;
use constant RKDA_B_SUR => 0001;
use constant RKDA_B_SC => 0017; # SC: sector address
use constant RKMR_M_RID => 0160000; # RID: drive id for RKDS RB read
use constant RKMR_V_RID => 13;
use constant RKMR_V_CRDONE => 11; # CRDONE: control reset done
use constant RKMR_V_SBCLR => 10; # SBCLR: clear SBUSY's with SDONE
use constant RKMR_V_CRESET => 9; # CRESET: initiate control reset
use constant RKMR_V_FDONE => 8; # FDONE: initiate function done
my @reglist_rk11 = ({name => "rkds",
offset => RK11_RKDS},
{name => "rker",
offset => RK11_RKER},
{name => "rkcs",
offset => RK11_RKCS},
{name => "rkwc",
offset => RK11_RKWC},
{name => "rkba",
offset => RK11_RKBA},
{name => "rkda",
offset => RK11_RKDA},
{name => "rkmr",
offset => RK11_RKMR});
my %partbl_rk11 = ( trace => { type => "hval:b" } );
# KWP general defs
my @reglist_kwp = ({name => "csr",
offset => 0},
{name => "csb",
offset => 2},
{name => "ctr",
offset => 4});
# KWL general defs
my @reglist_kwl = ({name => "csr",
offset => 0});
# IIST general defs
my @reglist_iist = ({name => "acr",
offset => 0},
{name => "adr",
offset => 2});
use constant BOOT_START => 02000;
my $serv11_fds_update = 1;
my $serv11_config_done = 0;
my $serv11_init_pending = 1;
my $serv11_rdma_chunk = 256;
my $serv11_init_anena = 0x8000; # enable attn
#my $serv11_init_anena = 0xC03f; # enable attn+ioto(63ms)
#
# %serv11_ctltbl->{dev} --> controller table; is hash of hashes
# -> {ctlname} controller name
# -> {ctltype} controller type (e.g. DL11)
# -> {devname} device name
# -> {type} device type: term, lpr, ptap, disk, tape, eth
# -> {units} array of unit names
# -> {base} controller base address
# -> {ibrb} controller base mapping for remote ib access
# -> {csroff} csr offset from base (for probing)
# -> {lam} lam number used by controller
# -> {nxbuf_min} nxbuf: minimal vector size
# -> {nxbuf_inc} nxbuf: vector size increment
# -> {nxbuf_max} nxbuf: maximal vector size
# -> {probehdl} address of probe handler
# -> {probemask} sides to be tested (set to "ir" if missing)
# -> {ichrhdl} address of input character handler
# -> {inithdl} address of controler init handler
# -> {usethdl} address of unit setup handler
# -> {attdethdl} address of attach/detach handler
# -> {attnhdl} address og attention handler
# -> {reglist} register list (array of regdsc's)
# -> {regtbl} register table (by name, created by ...)
# -> {partbl} parameter table (array of pardsc's)
# -> {blksize} block size (in bytes) {for disk}
# -> {volsize} volume size (in bytes) {for disk}
# -> {boot_mode} boot mode: "ptape" undef
# -> {boot_base} base address for boot code if not BOOT_START {for ptap}
# -> {boot_mode} boot mode: "ptape" undef
# -> {boot_entry} boot code entry point, relative to BOOT_START
# -> {boot_unit} offset from BOOT_START where unit number is stored
# -> {boot_code} array with boot loader code
#
# -> {memsize} memory size {for cpu}
#
# -> {nxbuf} nxbuf: current value
# -> {probe_ival} defined if cpu side probing ok
# -> {probe_rval} defined if rem side probing ok
# -> {probe_ok} true if required sides available (see probe_mask)
# -> {probe_text} text for "sho conf" generated during probe
my %serv11_ctltbl = (
CPU =>
{ ctlname => "CPU",
ctltype => "W11A",
type => "cpu",
base => CPU_PSW, # use psw to get it to top of list
lam => 0,
probehdl => \&serv11_probe_cpu,
probemask => "i",
attnhdl => \&serv11_attn_cpu,
reglist => [ ],
partbl => { }
},
TTA =>
{ ctlname => "TTA",
ctltype => "DL11",
devname => "TT",
type => "term",
units => ["TT0"],
base => DL11_BASE_A,
ibrb => DL11_BASE_A & ~(077),
csroff => 0,
lam => 1,
nxbuf_min => 2, # to disable nxbuf mechanism use
nxbuf_inc => 2, # min=1, inc=0, max=1
nxbuf_max => 8, # otherwise: min=2,inc=2,max=8
trace => 1,
probehdl => \&serv11_probe_gen,
ichrhdl => \&serv11_ichr_dl11,
attdethdl => \&serv11_attdet_term,
attnhdl => \&serv11_attn_dl11,
reglist => [ @reglist_dl11 ],
partbl => { %partbl_dl11 }
},
TTB =>
{ ctlname => "TTB",
ctltype => "DL11",
devname => "TT",
type => "term",
units => ["TT1"],
base => DL11_BASE_B,
ibrb => DL11_BASE_B & ~(077),
csroff => 0,
lam => 2,
nxbuf_min => 2,
nxbuf_inc => 2,
nxbuf_max => 8,
trace => 1,
probehdl => \&serv11_probe_gen,
ichrhdl => \&serv11_ichr_dl11,
attdethdl => \&serv11_attdet_term,
attnhdl => \&serv11_attn_dl11,
reglist => [ @reglist_dl11 ],
partbl => { %partbl_dl11 }
},
DZ =>
{ ctlname => "DZ",
ctltype => "DZ11",
devname => "DZ",
type => "term",
units => ["DZ0","DZ1","DZ2","DZ3","DZ4","DZ5","DZ6","DZ7"],
base => 0160100,
ibrb => 0160100 & ~(077),
csroff => 0,
lam => 3,
probehdl => \&serv11_probe_gen
},
LP =>
{ ctlname => "LP",
ctltype => "LP11",
devname => "LP",
type => "lpr",
units => ["LP0"],
base => 0177514,
ibrb => 0177514 & ~(077),
csroff => 0,
lam => 8,
nxbuf_min => 2, # to disable nxbuf mechanism use
nxbuf_inc => 2, # min=1, inc=0, max=1
nxbuf_max => 8, # otherwise: min=2,inc=2,max=8
trace => 1,
probehdl => \&serv11_probe_gen,
inithdl => \&serv11_init_gen,
usethdl => \&serv11_uset_lp11,
attdethdl => \&serv11_attdet_wonly,
attnhdl => \&serv11_attn_lp11,
reglist => [ @reglist_lp11 ],
partbl => { %partbl_lp11 }
},
PC =>
{ ctlname => "PC",
ctltype => "PC11",
devname => "PC",
type => "ptap",
units => ["PTR","PTP"],
base => 0177550,
ibrb => 0177550 & ~(077),
csroff => 0,
lam => 10,
nxbuf_min => 2,
nxbuf_inc => 2,
nxbuf_max => 8,
trace => 1,
probehdl => \&serv11_probe_gen,
usethdl => \&serv11_uset_pc11,
attdethdl => \&serv11_attdet_pc11,
attnhdl => \&serv11_attn_pc11,
reglist => [ @reglist_pc11 ],
partbl => { %partbl_pc11 },
boot_mode => "ptape",
boot_base => 0017476,
boot_entry=> 0017500,
boot_code => [ # papertape lda loader, from dec-11-l2pc-po
0000000, # C000: halt
0010706, # astart: mov pc,sp
0024646, # cmp -(sp),-(sp)
0010705, # mov pc,r5
0062705, 0000112, # add #000112,r5
0005001, # clr r1
0013716, 0177570, # B000: mov @#cp.dsr,(sp)
0006016, # ror (sp)
0103402, # bcs B001
0005016, # clr (sp)
0000403, # br B002
0006316, # B001: asl (sp)
0001001, # bne B002
0010116, # mov r1,(sp)
0005000, # B002: clr r0
0004715, # jsr pc,(r5)
0105303, # decb r3
0001374, # bne B002
0004715, # jsr pc,(r5)
0004767, 0000074, # jsr pc,R000
0010402, # mov r4,r2
0162702, 0000004, # sub #000004,r2
0022702, 0000002, # cmp #000002,r2
0001441, # beq B007
0004767, 0000054, # jsr pc,R000
0061604, # add (sp),r4
0010401, # mov r4,r1
0004715, # B003: jsr pc,(r5)
0002004, # bge B005
0105700, # tstb r0
0001753, # beq B002
0000000, # B004: halt
0000751, # br B002
0110321, # B005: movb r3,(r1)+
0000770, # br B003
0016703, 0000152, # ldchr: mov p.prcs,r3
0105213, # incb (r3)
0105713, # B006: tstb (r3)
0100376, # bpl B006
0116303, 0000002, # movb 000002(r3),r3
0060300, # add r3,r0
0042703, 0177400, # bic #177400,r3
0005302, # dec r2
0000207, # rts pc
0012667, 0000046, # R000: mov (sp)+,D000
0004715, # jsr pc,(r5)
0010304, # mov r3,r4
0004715, # jsr pc,(r5)
0000303, # swap r3
0050304, # bis r3,r4
0016707, 0000030, # mov D000,pc
0004767, 0177752, # B007: jsr pc,R000
0004715, # jsr pc,(r5)
0105700, # tstb r0
0001342, # bne B004
0006204, # asr r4
0103002, # bcc B008
0000000, # halt
0000700, # br B000
0006304, # B008: asl r4
0061604, # add (sp),r4
0000114, # jmp (r4)
0000000, # D000: .word 000000
0012767, 0000352, 0000020, # L000: mov #000352,B009+2
0012767, 0000765, 0000034, # mov #000765,D001
0000167, 0177532, # jmp C000
0016701, 0000026, # bstart: mov p.prcs,r1
0012702, 0000352, # B009: mov #000352,r2
0005211, # inc (r1)
0105711, # B010: tstb (r1)
0100376, # bpl B010
0116162, 0000002, 0157400, # movb 000002(r1),157400(r2)
0005267, 0177756, # inc B009+2
0000765, # D001: br B009
0177550 # p.prcs: .word 177550
]
},
RK =>
{ ctlname => "RK",
ctltype => "RK11/RK05",
devname => "RK",
type => "disk",
units => ["RK0","RK1","RK2","RK3","RK4","RK5","RK6","RK7"],
base => RK11_BASE,
ibrb => RK11_BASE & ~(077),
csroff => 4,
lam => 4,
trace => 1,
probehdl => \&serv11_probe_gen,
inithdl => \&serv11_init_gen,
usethdl => \&serv11_uset_rk11,
attdethdl => \&serv11_attdet_disk,
attnhdl => \&serv11_attn_rk11,
reglist => [ @reglist_rk11 ],
partbl => { %partbl_rk11 },
blksize => RK11_BLKSIZE,
volsize => RK11_VOLSIZE,
boot_entry=> BOOT_START + 002,
boot_unit => BOOT_START + 010,
boot_code => [ # rk05 boot loader - from simh pdp11_rk.c
0042113, # "KD"
0012706, BOOT_START, # MOV #boot_start, SP
0012700, 0000000, # MOV #unit, R0 ; unit number
0010003, # MOV R0, R3
0000303, # SWAB R3
0006303, # ASL R3
0006303, # ASL R3
0006303, # ASL R3
0006303, # ASL R3
0006303, # ASL R3
0012701, 0177412, # MOV #RKDA, R1 ; rkda
0010311, # MOV R3, (R1) ; load da
0005041, # CLR -(R1) ; clear ba
0012741, 0177000, # MOV #-256.*2, -(R1) ; load wc
0012741, 0000005, # MOV #READ+GO, -(R1) ; read & go
0005002, # CLR R2
0005003, # CLR R3
0012704, BOOT_START+020, # MOV #START+20, R4
0005005, # CLR R5
0105711, # TSTB (R1)
0100376, # BPL .-2
0105011, # CLRB (R1)
0005007 # CLR PC (5007)
]
},
RL =>
{ ctlname => "RL",
ctltype => "RL11/RL02",
devname => "RL",
type => "disk",
units => ["RL0","RL1","RL2","RL3"],
base => 0174400,
ibrb => 0174400 & ~(077),
csroff => 0, # ???CHECK-ME???
lam => 5,
probehdl => \&serv11_probe_gen,
boot_entry=> BOOT_START + 002,
boot_unit => BOOT_START + 010,
boot_code => [ # rl02 boot loader - from simh pdp11_rl.c
0042114, # "LD"
0012706, BOOT_START, # MOV #boot_start, SP
0012700, 0000000, # MOV #unit, R0
0010003, # MOV R0, R3
0000303, # SWAB R3
0012701, 0174400, # MOV #RLCS, R1 ; csr
0012761, 0000013, 0000004, # MOV #13, 4(R1) ; clr err
0052703, 0000004, # BIS #4, R3 ; unit+gstat
0010311, # MOV R3, (R1) ; issue cmd
0105711, # TSTB (R1) ; wait
0100376, # BPL .-2
0105003, # CLRB R3
0052703, 0000010, # BIS #10, R3 ; unit+rdhdr
0010311, # MOV R3, (R1) ; issue cmd
0105711, # TSTB (R1) ; wait
0100376, # BPL .-2
0016102, 0000006, # MOV 6(R1), R2 ; get hdr
0042702, 0000077, # BIC #77, R2 ; clr sector
0005202, # INC R2 ; magic bit
0010261, 0000004, # MOV R2, 4(R1) ; seek to 0
0105003, # CLRB R3
0052703, 0000006, # BIS #6, R3 ; unit+seek
0010311, # MOV R3, (R1) ; issue cmd
0105711, # TSTB (R1) ; wait
0100376, # BPL .-2
0005061, 0000002, # CLR 2(R1) ; clr ba
0005061, 0000004, # CLR 4(R1) ; clr da
0012761, 0177000, 0000006, # MOV #-512., 6(R1) ; set wc
0105003, # CLRB R3
0052703, 0000014, # BIS #14, R3 ; unit+read
0010311, # MOV R3, (R1) ; issue cmd
0105711, # TSTB (R1) ; wait
0100376, # BPL .-2
0042711, 0000377, # BIC #377, (R1)
0005002, # CLR R2
0005003, # CLR R3
0012704, BOOT_START+020, # MOV #START+20, R4
0005005, # CLR R5
0005007 # CLR PC
]
},
RP =>
{ ctlname => "RP",
ctltype => "RH70/RP06",
devname => "RP",
type => "disk",
units => ["RP0","RP1","RP2","RP3"],
base => 0176700,
ibrb => 0176700 & ~(077),
csroff => 0, # ???CHECK-ME???
lam => 6,
probehdl => \&serv11_probe_gen,
boot_entry=> BOOT_START + 002,
boot_unit => BOOT_START + 010,
boot_code => [ # rp/rm boot loader - from simh pdp11_rp.c
0042102, # "BD"
0012706, BOOT_START, # mov #boot_start, sp
0012700, 0000000, # mov #unit, r0
0012701, 0176700, # mov #RPCS1, r1
0012761, 0000040, 0000010, # mov #CS2_CLR, 10(r1) ; reset
0010061, 0000010, # mov r0, 10(r1) ; set unit
0012711, 0000021, # mov #RIP+GO, (r1) ; pack ack
0012761, 0010000, 0000032, # mov #FMT16B, 32(r1) ; 16b mode
0012761, 0177000, 0000002, # mov #-512., 2(r1) ; set wc
0005061, 0000004, # clr 4(r1) ; clr ba
0005061, 0000006, # clr 6(r1) ; clr da
0005061, 0000034, # clr 34(r1) ; clr cyl
0012711, 0000071, # mov #READ+GO, (r1) ; read
0105711, # tstb (r1) ; wait
0100376, # bpl .-2
0005002, # clr R2
0005003, # clr R3
0012704, BOOT_START+020, # mov #start+020, r4
0005005, # clr R5
0105011, # clrb (r1)
0005007 # clr PC
]
},
TM =>
{ ctlname => "TM",
ctltype => "TM11",
devname => "TM",
type => "tape",
units => ["TM0","TM1","TM2","TM3","TM4","TM5","TM6","TM7"],
base => 0172520,
ibrb => 0172520 & ~(077),
csroff => 2,
lam => 7,
probehdl => \&serv11_probe_gen,
boot_entry=> BOOT_START + 002,
boot_unit => BOOT_START + 010,
boot_code => [ # tm11 boot2 (skip 1st record) - from simh pdp11_tm.c
0046524, # boot_start: "TM"
0012706, BOOT_START, # mov #boot_start, sp
0012700, 0000000, # mov #unit_num, r0
0012701, 0172526, # mov #172526, r1 ; mtcma
0005011, # clr (r1)
0012741, 0177777, # mov #-1, -(r1) ; mtbrc
0010002, # mov r0,r2
0000302, # swab r2
0062702, 0060011, # add #60011, r2
0010241, # mov r2, -(r1) ; space + go
0105711, # tstb (r1) ; mtc
0100376, # bpl .-2
0010002, # mov r0,r2
0000302, # swab r2
0062702, 0060003, # add #60003, r2
0010211, # mov r2, (r1) ; read + go
0105711, # tstb (r1) ; mtc
0100376, # bpl .-2
0005002, # clr r2
0005003, # clr r3
0012704, BOOT_START+020, # mov #boot_start+20, r4
0005005, # clr r5
0005007 # clr r7
]
},
XU =>
{ ctlname => "XU",
ctltype => "DENUA",
devname => "XU",
type => "eth",
units => ["XU0"],
base => 0174510,
ibrb => 0174510 & ~(077),
csroff => 0,
lam => 9,
probehdl => \&serv11_probe_gen
},
KWP =>
{ ctlname => "KWP",
ctltype => "KW11-P",
devname => "--",
type => "misc",
base => 0172540,
probehdl => \&serv11_probe_gen,
probemask => "i",
reglist => [ @reglist_kwp ]
},
KWL =>
{ ctlname => "KWL",
ctltype => "KW11-L",
devname => "--",
type => "misc",
base => 0177546,
probehdl => \&serv11_probe_gen,
probemask => "i",
reglist => [ @reglist_kwl ]
},
IIS =>
{ ctlname => "IIS",
ctltype => "IIST",
devname => "--",
type => "misc",
base => 0177500,
probehdl => \&serv11_probe_gen,
probemask => "i",
reglist => [ @reglist_iist ]
}
);
#
# %serv11_unittbl->{unit} --> unit table; is hash of hashes
# -> {unitname} unit name
# -> {ctlname} controller name
# -> {ctlunit} unit number of controller {ctlname}
# -> {devunit} device number for device $ucb->{ctlname}->{devname}
# -> {rcvque} receive queue {for term}
# -> {sndque} send queue {for term}
# -> {rcv7bit} use only 7 bits in receive {for term}
# -> {logfile} name of logfile
# -> {logfh} file handle for logfile
#
my %serv11_unittbl = (
TT0 => { unitname => "TT0",
ctlname => "TTA",
ctlunit => 0,
devunit => 0,
rcvque => [],
sndque => [],
rcv7bit => 1,
logfile => "pi_tt0.log",
logfh => undef
},
TT1 => { unitname => "TT1",
ctlname => "TTB",
ctlunit => 0,
devunit => 1,
rcvque => [],
sndque => [],
rcv7bit => 1,
logfile => "pi_tt1.log",
logfh => undef
},
DZ0 => { unitname => "DZ0",
ctlname => "DZ",
ctlunit => 0,
devunit => 0,
rcvque => [],
sndque => []
},
DZ1 => { unitname => "DZ1",
ctlname => "DZ",
ctlunit => 1,
devunit => 1,
rcvque => [],
sndque => []
},
DZ2 => { unitname => "DZ2",
ctlname => "DZ",
ctlunit => 2,
devunit => 2,
rcvque => [],
sndque => []
},
DZ3 => { unitname => "DZ3",
ctlname => "DZ",
ctlunit => 3,
devunit => 3,
rcvque => [],
sndque => []
},
DZ4 => { unitname => "DZ4",
ctlname => "DZ",
ctlunit => 4,
devunit => 4,
rcvque => [],
sndque => []
},
DZ5 => { unitname => "DZ5",
ctlname => "DZ",
ctlunit => 5,
devunit => 5,
rcvque => [],
sndque => []
},
DZ6 => { unitname => "DZ6",
ctlname => "DZ",
ctlunit => 6,
devunit => 6,
rcvque => [],
sndque => []
},
DZ7 => { unitname => "DZ7",
ctlname => "DZ",
ctlunit => 7,
devunit => 7,
rcvque => [],
sndque => []
},
LP0 => { unitname => "LP0",
ctlname => "LP",
ctlunit => 0,
devunit => 0,
logfile => "pi_lp0.log",
logfh => undef
},
PTR => { unitname => "PTR",
ctlname => "PC",
ctlunit => 0,
devunit => 0
},
PTP => { unitname => "PTP",
ctlname => "PC",
ctlunit => 1,
devunit => 1
},
RK0 => { unitname => "RK0",
ctlname => "RK",
ctlunit => 0,
devunit => 0
},
RK1 => { unitname => "RK1",
ctlname => "RK",
ctlunit => 1,
devunit => 1
},
RK2 => { unitname => "RK2",
ctlname => "RK",
ctlunit => 2,
devunit => 2
},
RK3 => { unitname => "RK3",
ctlname => "RK",
ctlunit => 3,
devunit => 3
},
RK4 => { unitname => "RK4",
ctlname => "RK",
ctlunit => 4,
devunit => 4
},
RK5 => { unitname => "RK5",
ctlname => "RK",
ctlunit => 5,
devunit => 5
},
RK6 => { unitname => "RK6",
ctlname => "RK",
ctlunit => 6,
devunit => 6
},
RK7 => { unitname => "RK7",
ctlname => "RK",
ctlunit => 7,
devunit => 7
},
RL0 => { unitname => "RL0",
ctlname => "RL",
ctlunit => 0,
devunit => 0
},
RL1 => { unitname => "RL1",
ctlname => "RL",
ctlunit => 1,
devunit => 1
},
RL2 => { unitname => "RL2",
ctlname => "RL",
ctlunit => 2,
devunit => 2
},
RL3 => { unitname => "RL3",
ctlname => "RL",
ctlunit => 3,
devunit => 3
},
RP0 => { unitname => "RP0",
ctlname => "RP",
ctlunit => 0,
devunit => 0
},
RP1 => { unitname => "RP1",
ctlname => "RP",
ctlunit => 1,
devunit => 1
},
RP2 => { unitname => "RP2",
ctlname => "RP",
ctlunit => 2,
devunit => 2
},
RP3 => { unitname => "RP3",
ctlname => "RP",
ctlunit => 3,
devunit => 3
},
TM0 => { unitname => "TM0",
ctlname => "TM",
ctlunit => 0,
devunit => 0
},
TM1 => { unitname => "TM1",
ctlname => "TM",
ctlunit => 1,
devunit => 1
},
TM2 => { unitname => "TM2",
ctlname => "TM",
ctlunit => 2,
devunit => 2
},
TM3 => { unitname => "TM3",
ctlname => "TM",
ctlunit => 3,
devunit => 3
},
TM4 => { unitname => "TM4",
ctlname => "TM",
ctlunit => 4,
devunit => 4
},
TM5 => { unitname => "TM5",
ctlname => "TM",
ctlunit => 5,
devunit => 5
},
TM6 => { unitname => "TM6",
ctlname => "TM",
ctlunit => 6,
devunit => 6
},
TM7 => { unitname => "TM7",
ctlname => "TM",
ctlunit => 7,
devunit => 7
},
XU0 => { unitname => "XU0",
ctlname => "XU",
ctlunit => 0,
devunit => 0
}
);
my @serv11_attntbl;
my $serv11_active = 0;
my $serv11_attn_mask = 0;
my $serv11_attn_seen = 0;
my @serv11_icbque = ();
my $only_argv = 0;
$only_argv = 1 if scalar(@ARGV) > 0;
$only_argv = 0 if exists $opts{int};
#
# -- Main program starts here ------------------------------------------------
#
autoflush STDOUT 1 if (-p STDOUT); # autoflush if output into pipe
autoflush STDOUT 1 if (-t STDOUT); # autoflush if output into term
if (exists $opts{help}) {
print_help();
exit 0;
}
$SIG{INT} = 'hdl_sigint'; # install ^C (SIGINT) handler
if (exists $opts{log} && $opts{log} ne "") {
my $fh = new FileHandle;
my $filename = $opts{log};
$fh->open(">$filename") or die "couldn't open log file";
$fh_log = $fh;
autoflush $fh_log if (-t $fh);
printf $fh_log "==== opened log file on %s\n", get_timestamp();
}
$raw_timeout = $opts{timeout} if exists $opts{timeout};
$cmax = $opts{cmax} if exists $opts{cmax};
if (exists $opts{run}) {
if (not defined ($kpid=fork())) {
die "cannot fork: $!";
} elsif ($kpid == 0) { # in child
exec "/bin/sh", "-c", $opts{run};
die "failed to exec /bin/sh -c $opts{run}: $!";
} else { # in parent
}
}
fifo_open($opts{fifo}) if (exists $opts{fifo});
$time0 = get_time(); # do T0 after fifo open
term_open($opts{term}) if (exists $opts{term});
while(1) {
my $cmd = get_command();
if (defined $cmd) {
do_command($cmd);
} else {
do_command(".mode nomode");
last;
}
}
if ($curchan) {
&{$chan_tab{$curchan}{write}}(); # flush write queue before close
&{$chan_tab{$curchan}{close}}();
}
if (exists $opts{run}) {
waitpid($kpid, 0);
print "pi_rri($curmode)-I: exit status: $?\n" if $?;
}
0;
#-------------------------------------------------------------------------------
sub init_regtbl { # initialize regtbl from reglist
foreach my $ctlname (sort keys %serv11_ctltbl) {
my $ctl = $serv11_ctltbl{$ctlname};
next unless defined $ctl->{reglist};
$ctl->{regtbl} = {};
my $nregs = scalar (@{$ctl->{reglist}});
for (my $i = 0; $i<$nregs; $i++) {
my $name = $ctl->{reglist}->[$i]->{name};
$ctl->{regtbl}->{$name} = $i;
$ctl->{reglist}->[$i]->{rank} = $i;
##print "+++ 1a $ctl->{ctlname} $name $i\n";
}
}
}
#-------------------------------------------------------------------------------
sub get_command {
my $cmd;
while (1) {
$cmd = read_command;
return $cmd if (not defined $cmd); # quit if EOF
print "$cmd\n" if exists $opts{trace};
if ($cmd =~ m/^C/) { # ignore, but print "C ..." lines
&{$mode_tab{$curmode}{flush}}("comm");
print "$cmd\n" unless exists $opts{trace};
next;
}
$cmd =~ s{^\s*}{}; # remove leading blanks
next if $cmd =~ m/^#/; # ignore "# ...." lines
next if $cmd =~ m/^;/; # ignore "; ...." lines
$cmd =~ s{--.*}{}; # remove comments after --
$cmd =~ s{\s*$}{}; # remove trailing blanks
next if $cmd eq ""; # ignore empty lines
return $cmd;
}
}
#-------------------------------------------------------------------------------
sub do_command {
my ($cmd) = @_;
if ($cmd =~ /^\.mode\s*(\w*)/) { # .mode command
if (exists $mode_tab{$1}) {
&{$mode_tab{$curmode}{flush}}("mode");
&{$mode_tab{$curmode}{close}}();
print "pi_rri($curmode)-I: closed mode\n" unless $curmode eq "nomode";
$curmode = $1;
$curcmd = $mode_tab{$curmode}{cmd};
print "pi_rri($curmode)-I: open mode\n" unless $curmode eq "nomode";
&{$mode_tab{$curmode}{open}}();
} else {
printf "pi_rri($curmode)-E: mode '%s' doesn't exist\n", $1;
printf "pi_rri($curmode)-E: use %s\n", join ",", (sort keys %mode_tab);
}
} else { # any other command
$sigint_count = 0; # clear pending ^C's
&$curcmd($cmd);
&{$mode_tab{$curmode}{flush}}("line") if $cmd_inter;
}
}
#-------------------------------------------------------------------------------
sub read_command {
my $cmd;
$cmd_inter = 0;
while (1) {
# read command line
if (scalar(@cmdfh)==0 && scalar(@ARGV)>0) {
$cmd = shift @ARGV;
} else {
if (scalar(@cmdfh)) {
my $fh = $cmdfh[$#cmdfh];
$cmd = <$fh>;
chomp $cmd if defined $cmd;
if (defined $cmd && $cmd =~ /\\$/) { # continuation line ?
$cmd = $`;
my $cline = <$fh>;
chomp $cline;
$cmd .= $cline if defined $cline;
}
unless (defined $cmd) {
$fh->close();
pop @cmdfh;
print "pi_rri($curmode)-I: close " . pop(@cmdfn) . "\n";
&{$mode_tab{$curmode}{flush}}("file");
pop @cmdargs;
setpar_command($cmdargs[-1]) if scalar(@cmdargs);
next;
}
} else {
return undef if $only_argv;
if (defined $term) {
$cmd = $term->readline('>');
} else {
$cmd = <STDIN>;
}
if (-t STDIN && -t STDOUT) {
$cmd_inter = 1; # signal that cmd interactive
}
chomp $cmd if defined $cmd;
return undef if not defined $cmd;
if (defined $cmd && $cmd =~ /\\$/) { # continuation line ?
$cmd = $`;
my $cline = <STDIN>;
chomp $cline;
$cmd .= $cline if defined $cline;
}
}
}
# preprocess command line
# handle substitutions
while ($cmd =~ /\$\{(\w*):([-=])(.*?)\}/) { # ${name:[-=]val} seen
my $name = $1;
my $typ = $2;
my $val = $3;
if (exists $par{$name}) {
$cmd = $` . $par{$name} . $';
} else {
$cmd = $` . $val . $';
$par{$name} = $val if ($typ eq "=");
}
}
while ($cmd =~ /\$\{(\w*)\}/) { # ${name} seen
my $name = $1;
if (exists $par{$name}) {
$cmd = $` . $par{$name} . $';
} else {
print "pi_rri($curmode)-E: variable \"$name\" not defined\n";
$cmd = $` . "\$?$name?" . $';
}
}
while ($cmd =~ /\$\[(.*)\]/) { # $[name] seen
my $evalstr = $1;
my $evalval = eval $evalstr;
if ($@) {
print "pi_rri($curmode)-E: eval error for \"$evalstr\"\n";
print "pi_rri($curmode)-E: $@\n";
$cmd = $` . "\$?$evalstr?" . $';
} else {
$evalval = "" unless defined $evalval;
$cmd = $` . $evalval . $';
}
}
# handle asignments
if ($cmd =~ /^(\w*)=/) {
my $name = $1;
my $val = $';
$val =~ s/--.*$//;
$val =~ s/\s*$//;
$par{$name} = $val;
next;
}
# handle @@xxx lines (pmac perl macros)
if ($cmd =~ /^\s*\@\@(\S*)\s*(.*)$/) { # is it a "@@xxx" macro call ?
my $file = $1;
my $args = $2;
my $fileexp = filename_expand($file);
print_fatal "pmac file $fileexp not found" unless -r $fileexp;
open (PMACFILE, "<$fileexp") or die "failed to open $fileexp: $!";
my @code = <PMACFILE>;
close PMACFILE;
my $code = join "", @code;
##printf "+++1 code to execute from $fileexp:\n$code---\n";
$cmd_line = $cmd;
$cmd_rest = $args;
$cmd_bad = 0;
$sigint_count = 0; # clear pending ^C's
{ eval $code; }
if ($@) {
print STDERR "pi_rri-E: compile error in $fileexp:\n";
print STDERR $@;
}
next;
}
# handle @xxx lines (pcmd command lists)
if ($cmd =~ /^\s*\@(.*)$/) { # is it a "@xxx" command ?
my $file = $1;
my $args = "";
if ($file =~ /\((.*)\)$/) { # is it a "@xxx(args)" command ?
$file = $`;
$args = $1;
}
my $fileexp = filename_expand($file);
print_fatal "pcmd file $fileexp not found" unless -r $fileexp;
my $fh = new FileHandle;
$fh->open("<$fileexp") or die "failed to open $fileexp: $!";
print "pi_rri($curmode)-I: open $fileexp\n";
push @cmdfh, $fh;
push @cmdfn, $fileexp;
push @cmdargs, $args;
setpar_command($args);
} else {
return $cmd;
}
}
}
#-------------------------------------------------------------------------------
sub setpar_command {
my ($args) = @_;
my @arglist = split /,/,$args;
for (my $i=scalar(@arglist); $i<8; $i++) {
$arglist[$i] = "";
}
for (my $i=0; $i<scalar(@arglist); $i++) {
my $name = $i+1;
$par{"$name"} = $arglist[$i];
}
}
#-------------------------------------------------------------------------------
sub nomode_open {
}
#-------------------------------------------------------------------------------
sub nomode_flush {
my ($case) = @_;
}
#-------------------------------------------------------------------------------
sub nomode_close {
}
#-------------------------------------------------------------------------------
sub nomode_cexec {
my ($cmd) = @_;
print "pi_rri($curmode)-E: unknown command \"$cmd\"\n";
}
#-------------------------------------------------------------------------------
sub cpraw_open {
}
#-------------------------------------------------------------------------------
sub cpraw_flush {
my ($case) = @_;
cpraw_tx_match_now unless $case eq "line";
}
#-------------------------------------------------------------------------------
sub cpraw_close {
}
#-------------------------------------------------------------------------------
# cprx 0 11110000
# cptx 0 11110000
sub cpraw_cexec {
my ($cmd) = @_;
my $dat;
if ($cmd =~ /^(cp[rt]x)\s+([01])\s+([01]{8})\s*/) {
print "pi_rri($curmode)-E: extra data ignored: \"$'\"\n" if $';
} else {
print "pi_rri($curmode)-E: unknown cpraw command: \"$cmd\"\n";
return;
}
$dat = vec(pack("B8",$3), 0,8);
$dat += 0x100 if $2 eq "1";
if ($1 eq "cprx") {
do_cprx($dat);
} else {
do_cptx($dat);
}
}
#-------------------------------------------------------------------------------
sub do_cprx {
my ($dat) = @_;
raw_snd9($dat);
cpraw_tx_match;
}
#-------------------------------------------------------------------------------
sub do_cptx {
my ($dat) = @_;
push @cpraw_tx_expt, $dat;
if ($dat == D9ATTN) { # attn comma ?
print conv_etime(), ".wtlam\n";
cpraw_tx_match_now; # if yes, force match now
} else {
cpraw_tx_match; # otherwise just queue
}
}
#-------------------------------------------------------------------------------
sub cpraw_tx_match_now {
my $nexpt = scalar(@cpraw_tx_expt);
while (scalar(@cpraw_tx_expt)) {
if (wait_sel_filercv(1.)) {
cpraw_tx_match;
} else {
print "pi_rri($curmode)-I: time out waiting for cptx response\n";
last;
}
}
}
#-------------------------------------------------------------------------------
sub cpraw_tx_match {
while (1) {
my $dat = raw_rcv9();
last unless defined $dat;
push @cpraw_tx_read, $dat;
}
while (scalar(@cpraw_tx_expt)>0 &&
scalar(@cpraw_tx_read)>0) {
my $dat_e = shift @cpraw_tx_expt;
my $dat_r = shift @cpraw_tx_read;
print conv_etime(), "cptx ", conv_dat9($dat_r), " CHECK ";
if ($dat_e == $dat_r) {
print "OK";
} else {
print "FAIL exp=", conv_dat9($dat_e);
}
print "\n";
}
}
#-------------------------------------------------------------------------------
sub rri_open {
$rri_ref_sdef = 0x00; # by default check for 'hard' errors
$rri_msk_sdef = 0xf0; # ignore the status bits + attn flag
}
#-------------------------------------------------------------------------------
sub rri_flush {
my ($case) = @_;
rri_cmdlist_do();
}
#-------------------------------------------------------------------------------
sub rri_close {
}
#-------------------------------------------------------------------------------
# .cpmon 0|1
# .rbmon 0|1
# .scntl n 0|1
# .sinit g8 g16
# .sdef [s=g8]
# .amclr
# .amdef name g8
# .reset
# .wait n
# .wtlam n
# .cclst
# rreg <addr> [d=g16] [s=g8]
# rblk <addr> n [s=g8]
# followed by n d=g16 data check values
# wreg <addr> g16 [s=g8]
# wblk <addr> n [s=g8]
# followed by n g16 data values
# stat [d=g16] [s=d8]
# attn [d=g16] [s=d8]
# init <addr> g16 [s=g8]
sub rri_cexec {
my ($cmd) = @_;
$cmd_line = $cmd;
$cmd_rest = "";
$cmd_bad = 0;
if ($cmd =~ /^(\.cpmon|\.rbmon)\s+([01])/) { # .cpmon, .rbmon -------------
my $ind = ($1 eq ".cpmon") ? 15 : 14;
$cmd_rest = $';
rri_sideband(0x00, ($ind<<8) + $2);
} elsif ($cmd =~ /^\.scntl\s+(\d+)\s([01])/) { # .scntl ------------------
$cmd_rest = $';
rri_sideband(0x00, ($1<<8) + $2);
} elsif ($cmd =~ /^\.sinit/) { # .sinit ------------------
$cmd_rest = $';
my $addr = cget_gdat(8,$rri_dbasi);
my $data = cget_gdat(16,$rri_dbasi);
rri_sideband($addr, $data) if (not $cmd_bad);
} elsif ($cmd =~ /^\.sdef/) { # .sdef -------------------------
$cmd_rest = $';
($rri_ref_sdef,$rri_msk_sdef) = cget_tagval2_gdat("s",8,2);
} elsif ($cmd =~ /^\.amclr/) { # .amclr ------------------------
$cmd_rest = $';
%rri_amtbl = ();
} elsif ($cmd =~ /^\.amdef\s+([a-zA-Z][a-zA-Z0-9]*)/) {# .amdef ------------
$cmd_rest = $';
my $name = $1;
my $addr = cget_gdat(8,2);
if (defined $addr) {
$rri_amtbl{$name} = $addr;
} else {
$cmd_bad = 1;
}
} elsif ($cmd =~ /^\.dbasi\s+(\d+)/) { # .dbasi -----------------------
$cmd_rest = $';
my $dbase = int $1;
$rri_dbasi = $dbase;
} elsif ($cmd =~ /^\.dbaso\s+(\d+)/) { # .dbaso -----------------------
$cmd_rest = $';
my $dbase = int $1;
$rri_dbaso = $dbase;
if ($rri_dbaso == 2) {
$rri_nodfill = " " x 15;
} elsif ($rri_dbaso == 8) {
$rri_nodfill = " " x 5;
} elsif ($rri_dbaso == 16) {
$rri_nodfill = " " x 3;
} else {
$rri_nodfill = "???";
}
} elsif ($cmd =~ /^\.reset/) { # .reset ------------------------
$cmd_rest = $';
print "pi_rri($curmode)-I: $cmd currently ignored\n";
} elsif ($cmd =~ /^\.wait\s+(\d+)/) { # .wait ------------------------
$cmd_rest = $';
my $delay = int $1;
rri_cmdlist_do(); # flush before waiting
for (my $i = 0; $i < $delay; $i++) {
raw_snd9(D9IDLE);
}
} elsif ($cmd =~ /^\.wtlam\s+(\d+)/) { # .wtlam ------------------------
$cmd_rest = $';
rri_cmdlist_do(); # flush before wait for ATTN
my $tstart = get_time();
raw_get9_check(D9ATTN, "wtlam"); # ???FIXME this is a hack...
printf "-- .wtlam # wait for %7.3f sec\n", get_time()-$tstart;
} elsif ($cmd =~ /^\.cclst/) { # .cclst ------------------------
$cmd_rest = $';
$rri_ncmdmax = scalar(@rri_cmdlist) + 1; # force exec after next cmd
} elsif ($cmd =~ /^rreg/) { # rreg --------------------------
$cmd_rest = $';
my $addr = rri_cget_addr;
my ($ref_data, $msk_data) = cget_tagval2_gdat("d",16,$rri_dbasi);
my ($ref_stat, $msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "rreg",
addr => $addr,
ref_data => $ref_data,
msk_data => $msk_data,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^rblk/) { # rblk --------------------------
$cmd_rest = $';
my $addr = rri_cget_addr;
my $nblk = rri_cget_nblk;
my ($ref_stat, $msk_stat) = rri_cget_stat;
my @ref_rblk;
my @msk_rblk;
my $i;
cget_chkblank();
for ($i = 0; $i < $nblk; $i++) {
$cmd_rest = get_command() if ($cmd_rest eq "");
$cmd_rest =~ s/^\s*//;
my ($ref,$msk) = cget_tagval2_gdat("d",16,$rri_dbasi);
push @ref_rblk, $ref;
push @msk_rblk, $msk;
}
cget_chkblank();
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "rblk",
addr => $addr,
nblk => $nblk,
ref_rblk => [@ref_rblk],
msk_rblk => [@msk_rblk],
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^wreg/) { # wreg --------------------------
$cmd_rest = $';
my $addr = rri_cget_addr;
my $data = cget_gdat(16,$rri_dbasi);
my ($ref_stat, $msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "wreg",
addr => $addr,
data => $data,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^wblk/) { # wblk --------------------------
$cmd_rest = $';
my $addr = rri_cget_addr;
my $nblk = rri_cget_nblk;
my ($ref_stat, $msk_stat) = rri_cget_stat;
my @dat_wblk;
my $i;
cget_chkblank();
for ($i = 0; $i < $nblk; $i++) {
$cmd_rest = get_command() if ($cmd_rest eq "");
$cmd_rest =~ s/^\s*//;
push @dat_wblk, cget_gdat(16,$rri_dbasi);
}
cget_chkblank();
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "wblk",
addr => $addr,
nblk => $nblk,
dat_wblk => [@dat_wblk],
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^stat/) { # stat --------------------------
$cmd_rest = $';
my ($ref_data, $msk_data) = cget_tagval_gdat("d",16,2);
my ($ref_stat, $msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "stat",
ref_data => $ref_data,
msk_data => $msk_data,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^attn/) { # attn --------------------------
$cmd_rest = $';
my ($ref_data, $msk_data) = cget_tagval_gdat("d",16,$rri_dbasi);
my ($ref_stat, $msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "attn",
ref_data => $ref_data,
msk_data => $msk_data,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^init/) { # init --------------------------
$cmd_rest = $';
my $addr = rri_cget_addr;
my $data = cget_gdat(16,$rri_dbasi);
my ($ref_stat, $msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "init",
addr => $addr,
data => $data,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} else {
print "pi_rri($curmode)-E: unknown command: \"$cmd_line\"\n";
}
cget_chkblank() unless $cmd_bad;
if ($cmd_bad) {
print "pi_rri($curmode)-E: parse error, command ignored: \"$cmd_line\"\n";
} else {
if (scalar(@rri_cmdlist) >= $cmax ||
($rri_ncmdmax && scalar(@rri_cmdlist) >= $rri_ncmdmax)) {
$rri_ncmdmax = undef;
rri_cmdlist_do();
}
}
}
#-------------------------------------------------------------------------------
sub rri_cget_stat {
my ($dat, $msk) = cget_tagval2_gdat("s",8,2);
if (defined $dat) {
return ($dat, $msk);
} else {
return ($rri_ref_sdef, $rri_msk_sdef);
}
}
#-------------------------------------------------------------------------------
sub rri_cget_addr {
my $odat;
$cmd_rest =~ s/^\s*//;
if ($cmd_rest =~ /^\.([a-zA-Z][a-zA-Z0-9]*)/) {
$cmd_rest = $';
if (exists $rri_amtbl{$1}) {
$odat = $rri_amtbl{$1};
if ($cmd_rest =~ /^\|/) {
$cmd_rest = $';
$odat |= cget_gdat(8,2);
}
} else {
print "pi_rri($curmode)-E: undefined address mnemo: \"$1\"\n";
$cmd_bad = 1;
}
} else {
$odat = cget_gdat(8,2);
}
return $odat;
}
#-------------------------------------------------------------------------------
sub rri_cget_nblk {
my $odat;
$cmd_rest =~ s/^\s*//;
if ($cmd_rest =~ /^(\d*)/) {
$cmd_rest = $';
$odat = int $1;
if ($odat <= 0 || $odat > 256) {
print "pi_rri($curmode)-E: block length <0 or >256\n";
$cmd_bad = 1;
}
} else {
$cmd_bad = 1;
}
return $odat;
}
#-------------------------------------------------------------------------------
sub rri_cmdlist_dump {
my ($href,$dblk,$fh) = @_;
my $fh_old;
$fh_old = select($fh) if defined $fh;
foreach my $ele (@$href) {
printf "-- %-4s",$ele->{cname};
printf " %-7s","[$ele->{aname}]" if exists $ele->{aname};
printf " c=%1.1x%1d%1d", $ele->{cmd}>>4, ($ele->{cmd}>>3)&0x1,
$ele->{cmd}&0x7 if exists $ele->{cmd};
printf " a=%s",conv_dat8($ele->{addr}) if exists $ele->{addr};
printf " n=%d", $ele->{nblk} if exists $ele->{nblk};
printf " d=%s", gconv_dat16($ele->{data},$rri_dbaso) if exists $ele->{data};
if (exists $ele->{ref_data}) {
if ((defined $ele->{msk_data} && $ele->{msk_data} == 0xffff)
|| not defined $ele->{ref_data}) {
printf " d=-%s", $rri_nodfill;
} else {
printf " d=%s", gconv_dat16($ele->{ref_data},$rri_dbaso);
printf ",%s", gconv_dat16($ele->{msk_data},$rri_dbaso) if $ele->{msk_data};
}
}
if (defined $ele->{rcv_data}) {
printf " D=%s%s", gconv_dat16($ele->{rcv_data},$rri_dbaso),
($ele->{err_data} ? "(#)" : " ");
}
if (exists $ele->{ref_stat}) {
if ((defined $ele->{msk_stat} && $ele->{msk_stat} == 0xffff)
|| not defined $ele->{ref_stat}) {
printf " s=-";
} else {
printf " s=%s", conv_dat8($ele->{ref_stat});
printf ",%s", conv_dat8($ele->{msk_stat}) if $ele->{msk_stat};
}
}
if (defined $ele->{rcv_stat}) {
printf " S=%s%s", conv_dat8($ele->{rcv_stat}),
($ele->{err_stat} ? "(#)" : " ");
}
if (exists $ele->{ok}) {
print ($ele->{ok} ? " OK" : "FAIL");
} else {
print " PEND";
}
if (exists $ele->{dat_wblk} && $dblk) {
my $i = 0;
foreach ( @{$ele->{dat_wblk}} ) {
printf "\n-- " if ($i % 8 == 0);
printf " %s", gconv_dat16($_,$rri_dbaso);
$i += 1;
}
}
if (exists $ele->{ref_rblk} && $dblk && scalar(@{$ele->{ref_rblk}}) ) {
my $i;
my $nblk = $ele->{nblk};
for ($i = 0; $i < $nblk; $i++) {
printf "\n-- " if ($i % 4 == 0);
if ((defined $ele->{msk_rblk}[$i] && $ele->{msk_rblk}[$i] == 0xffff)
|| not defined $ele->{ref_rblk}[$i]){
printf " d=-%s %s", $rri_nodfill, $rri_nodfill;
} else {
printf " d=%s", gconv_dat16($ele->{ref_rblk}[$i],$rri_dbaso);
if ($ele->{msk_rblk}[$i]) {
printf ",%s", gconv_dat16($ele->{msk_rblk}[$i],$rri_dbaso);
} else {
print " ";
}
}
}
}
if (exists $ele->{rcv_rblk} && $dblk) {
my $i;
my $nblk = $ele->{nblk};
for ($i = 0; $i < $nblk; $i++) {
printf "\n-- " if ($i % 4 == 0);
printf " D=%s%s ", gconv_dat16($ele->{rcv_rblk}[$i],$rri_dbaso),
($ele->{err_rblk}[$i] ? "(#)" : " ");
}
}
printf "\n";
}
select($fh_old) if defined $fh_old;
}
#-------------------------------------------------------------------------------
sub rri_sideband {
my ($addr,$data) = @_;
my $dl = $data & 0xff;
my $dh = ($data>>8) & 0xff;
rri_cmdlist_do();
raw_snd8(CESC);
raw_snd8(CESC);
raw_snd8($addr); # ADDR
raw_snd8($dl); # DL
raw_snd8($dh); # DH
&{$chan_tab{$curchan}{write}}(); # flush write queue
}
#-------------------------------------------------------------------------------
sub rri_cmdlist_do {
if (scalar(@rri_cmdlist)) {
rri_cmdlist_exec(\@rri_cmdlist);
rri_cmdlist_dump(\@rri_cmdlist, 1);
@rri_cmdlist = ();
}
}
#-------------------------------------------------------------------------------
sub rri_cmdlist_exec {
my ($href) = @_;
my $seq = 0;
my $nele = scalar(@$href);
return unless $nele;
$ocrc = 0;
$icrc = 0;
raw_snd9(D9SOP);
foreach my $ele (@$href) {
my $cname = $ele->{cname};
my $cmd;
$cmd = $rri_cname2cmd{$cname};
$cmd |= 0x08 if $seq < $nele-1; # set chain bit
$cmd |= ($seq & 0xf) << 4; # set sequence number field
$ele->{cmd} = $cmd;
raw_snd9_crc($cmd);
$seq += 1;
if ($cname eq "rreg") {
$stat_tab{xreg} += 1;
raw_snd9_crc($ele->{addr});
raw_snd9($ocrc);
} elsif ($cname eq "rblk") {
$stat_tab{xblk} += 1;
raw_snd9_crc($ele->{addr});
raw_snd9_crc($ele->{nblk}-1);
raw_snd9($ocrc);
} elsif ($cname eq "wreg") {
$stat_tab{xreg} += 1;
raw_snd9_crc($ele->{addr});
raw_snd9_crc( $ele->{data} & 0xff);
raw_snd9_crc(($ele->{data}>>8) & 0xff);
raw_snd9($ocrc);
} elsif ($cname eq "wblk") {
$stat_tab{xblk} += 1;
raw_snd9_crc($ele->{addr});
raw_snd9_crc($ele->{nblk}-1);
raw_snd9($ocrc);
foreach ( @{$ele->{dat_wblk}} ) {
raw_snd9_crc( $_ & 0xff);
raw_snd9_crc(($_>>8) & 0xff);
}
raw_snd9($ocrc);
} elsif ($cname eq "stat") {
raw_snd9($ocrc);
} elsif ($cname eq "attn") {
raw_snd9($ocrc);
} elsif ($cname eq "init") {
raw_snd9_crc($ele->{addr});
raw_snd9_crc( $ele->{data} & 0xff);
raw_snd9_crc(($ele->{data}>>8) & 0xff);
raw_snd9($ocrc);
}
}
raw_snd9(D9EOP);
raw_get9_checksop() or return 0;
foreach my $ele (@$href) {
my $cname = $ele->{cname};
my $idat;
my $ok = 1;
raw_get9_crc_check($ele->{cmd}, "cmd") or return 0;
if ($cname eq "rreg") {
raw_get9_crc_16bit(\$ele->{rcv_data}) or return 0;
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
} elsif ($cname eq "rblk") {
raw_get9_crc_check($ele->{nblk}-1, "nblk") or return 0;
for (my $i=0; $i<$ele->{nblk}; $i++) {
my $data;
my $err;
raw_get9_crc_16bit(\$data) or return 0;
push @{$ele->{rcv_rblk}}, $data;
$err = rri_ref_check($data, $ele->{ref_rblk}[$i], $ele->{msk_rblk}[$i]);
push @{$ele->{err_rblk}}, $err;
$ok = 0 if $err;
}
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
} elsif ($cname eq "wreg") {
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
} elsif ($cname eq "wblk") {
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
} elsif ($cname eq "stat") {
raw_get9_crc_8bit(\$ele->{rcv_ccmd}) or return 0;
raw_get9_crc_16bit(\$ele->{rcv_data}) or return 0;
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
} elsif ($cname eq "attn") {
raw_get9_crc_16bit(\$ele->{rcv_data}) or return 0;
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
} elsif ($cname eq "init") {
raw_get9_crc_8bit(\$ele->{rcv_stat}) or return 0;
raw_get9_check($icrc, "crc") or return 0;
}
if (defined $ele->{rcv_data}) {
$ele->{err_data} = rri_ref_check($ele->{rcv_data},
$ele->{ref_data}, $ele->{msk_data});
$ok = 0 if $ele->{err_data};
}
if (defined $ele->{rcv_stat}) {
$ele->{err_stat} = rri_ref_check($ele->{rcv_stat},
$ele->{ref_stat}, $ele->{msk_stat});
$ok = 0 if $ele->{err_stat};
}
$ele->{ok} = $ok;
}
raw_get9_checkeop() or return 0;
return 1;
}
#-------------------------------------------------------------------------------
sub rri_cmdlist_check_stat {
my ($href) = @_;
foreach my $ele (@$href) {
return 1 if not exists $ele->{rcv_stat};
return 1 if $ele->{err_stat};
}
return 0;
}
#-------------------------------------------------------------------------------
sub rri_cmdlist_get_rval {
my ($href,$ind) = @_;
my $nele = scalar(@$href);
return (undef, "#ind?#") if ($ind >= $nele);
return (undef, "#sta?#") if not exists $$href[$ind]->{rcv_stat};
return (undef, sprintf "#s=%2.2x#",$$href[$ind]->{rcv_stat})
if $$href[$ind]->{err_stat};
return (undef, "#dat?#") if (not exists $$href[$ind]->{rcv_data});
return ($$href[$ind]->{rcv_data}, sprintf "%6.6o",$$href[$ind]->{rcv_data});
}
#-------------------------------------------------------------------------------
sub rri_cmdlist_conv_rval {
my ($href,$ind) = @_;
my ($val,$str) = rri_cmdlist_get_rval($href, $ind);
return $str;
}
#-------------------------------------------------------------------------------
sub rri_ref_check { # check reference data (1=err)
my ($rcv,$ref,$msk) = @_;
if (defined $ref) {
my $mask = (defined $msk) ? $msk : 0;
my $mrcv = $rcv | $mask;
my $mref = $ref | $mask;
return 1 if $mrcv != $mref;
}
return 0;
}
#-------------------------------------------------------------------------------
sub pdpcp_open {
$rri_ref_sdef = 0x00; # by default check for 'hard' errors
$rri_msk_sdef = 0x70; # ignore cpuhalt,cpugo and attn
}
#-------------------------------------------------------------------------------
sub pdpcp_flush {
my ($case) = @_;
rri_cmdlist_do();
}
#-------------------------------------------------------------------------------
sub pdpcp_close {
}
#-------------------------------------------------------------------------------
# .tocmd n
# .tostp n
# .togo n
# .anena 0|1
# .cpmon 0|1
# .rbmon 0|1
# .scntl n 0|1
# .sinit g8 g16
# .sdef [s=g8]
# .cerr 0|1
# .merr 0|1
# .reset
# .wait n
# .cclst
# rr* [d=g16] [s=g8]
# wr* g16 [s=g8]
# brm n [s=g8]
# followed by n d=g16 data check values
# bwm n [s=g8]
# followed by n g16 data values
# wal g16 [s=g8]
# wah g16 [s=g8]
# rps [d=g16] [s=g8]
# wps g16 [s=g8]
# rm [d=g16] [s=g8]
# rmi [d=g16] [s=g8]
# wm g16 [s=g8]
# wmi g16 [s=g8]
# stapc g16 [s=g8]
# sta [s=g8]
# sto [s=g8]
# cont [s=g8]
# step [s=g8]
# rst [s=g8]
# wibrb g16
# ribr g6 [d=g16] [s=g8]
# wibr g6 g16
# wtgo
# wtlam [d=g16]
#
sub pdpcp_cexec {
my ($cmd) = @_;
my $cclast;
my $aname;
if ($cmd =~ /^([a-z0-9]*)/) {
$aname = $1;
}
$cmd =~ s/^rsp/rr6/; # rsp -> rr6
$cmd =~ s/^rpc/rr7/; # rsp -> rr7
$cmd =~ s/^wsp/wr6/; # wsp -> wr6
$cmd =~ s/^wpc/wr7/; # wsp -> wr7
$cmd_line = $cmd;
$cmd_rest = "";
$cmd_bad = 0;
if ($cmd =~ /^\.to(cmd|stp|go)\s+(\d*)/) {# .tocmd, .tostp, .togo
$cmd_rest = $';
print "pi_rri($curmode)-I: $cmd currently ignored\n";
} elsif ($cmd =~ /^\.anena\s+([01])/) { # .anena ------------------------
$cmd_rest = $';
my $ena = int $1;
my $ena_data = ($ena==0) ? 0x0000 : 0x8000;
rri_cmdlist_do();
push @rri_cmdlist, {cname => "init",
aname => ".anena",
addr => 0xff,
data => $ena_data};
rri_cmdlist_do();
} elsif ($cmd =~ /^(\.cpmon|\.rbmon)\s+([01])/) { # .cpmon, .rbmon ---------
$cmd_rest = $';
my $ind = ($1 eq ".cpmon") ? 15 : 14;
$cmd_rest = $';
rri_sideband(0x00, ($ind<<8) + $2);
} elsif ($cmd =~ /^\.scntl\s+(\d+)\s([01])/) { # .scntl ------------------
$cmd_rest = $';
rri_sideband(0x00, ($1<<8) + $2);
} elsif ($cmd =~ /^\.sinit/) { # .sinit ------------------
$cmd_rest = $';
my $addr = cget_gdat(8,$rri_dbasi);
my $data = cget_gdat(16,$rri_dbasi);
rri_sideband($addr, $data) if (not $cmd_bad);
} elsif ($cmd =~ /^\.sdef/) { # .sdef -------------------------
$cmd_rest = $';
($rri_ref_sdef,$rri_msk_sdef) = cget_tagval2_gdat("s",8,2);
} elsif ($cmd =~ /^\.[cm]err\s*[01]/) { # .[cm]err
# ignore, no action
} elsif ($cmd =~ /^\.reset/) { # .reset ------------------------
$cmd_rest = $';
rri_cmdlist_do(); # flush before reset
push @rri_cmdlist, {cname => "init",
aname => ".reset",
addr => 0x00,
data => 0x01};
rri_cmdlist_do(); # flush after reset
} elsif ($cmd =~ /^\.wait\s+(\d+)/) { # .wait ------------------------
$cmd_rest = $';
my $delay = int $1;
rri_cmdlist_do(); # flush before waiting
for (my $i = 0; $i < $delay; $i++) {
raw_snd9(D9IDLE);
}
} elsif ($cmd =~ /^\.cclst/) { # .cclst ------------------------
$cmd_rest = $';
$rri_ncmdmax = scalar(@rri_cmdlist) + 1; # force exec after next cmd
} elsif ($cmd =~ /^rr([0-7])/) { # rr* ---------------------------
$cmd_rest = $';
my $rnum = int $1;
pdpcp_cmd_rreg($aname, PDPCP_ADDR_R0+$rnum);
} elsif ($cmd =~ /^wr([0-7])/) { # wr* ---------------------------
$cmd_rest = $';
my $rnum = int $1;
pdpcp_cmd_wreg($aname, PDPCP_ADDR_R0+$rnum);
} elsif ($cmd =~ /^brm/) { # brm ---------------------------
$cmd_rest = $';
my $addr = PDPCP_ADDR_MEMI;
my $nblk = rri_cget_nblk;
my ($ref_stat, $msk_stat) = rri_cget_stat;
my @ref_rblk;
my @msk_rblk;
my $i;
cget_chkblank();
for ($i = 0; $i < $nblk; $i++) {
$cmd_rest = get_command() if ($cmd_rest eq "");
$cmd_rest =~ s/^\s*//;
my ($ref,$msk) = cget_tagval2_gdat("d",16,8);
push @ref_rblk, $ref;
push @msk_rblk, $msk;
}
cget_chkblank();
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "rblk",
aname => $aname,
addr => $addr,
nblk => $nblk,
ref_rblk => [@ref_rblk],
msk_rblk => [@msk_rblk],
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^bwm/) { # bwm ---------------------------
$cmd_rest = $';
my $addr = PDPCP_ADDR_MEMI;
my $nblk = rri_cget_nblk;
my ($ref_stat, $msk_stat) = rri_cget_stat;
my @dat_wblk;
my $i;
cget_chkblank();
for ($i = 0; $i < $nblk; $i++) {
$cmd_rest = get_command() if ($cmd_rest eq "");
$cmd_rest =~ s/^\s*//;
push @dat_wblk, cget_gdat(16,8);
}
cget_chkblank();
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "wblk",
aname => $aname,
addr => $addr,
nblk => $nblk,
dat_wblk => [@dat_wblk],
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
} elsif ($cmd =~ /^wal/) { # wal ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_AL);
} elsif ($cmd =~ /^wah/) { # wah ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_AH);
} elsif ($cmd =~ /^rps/) { # rps ---------------------------
$cmd_rest = $';
pdpcp_cmd_rreg($aname, PDPCP_ADDR_PSW);
} elsif ($cmd =~ /^wps/) { # wps ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_PSW);
} elsif ($cmd =~ /^rmi/) { # rmi ---------------------------
$cmd_rest = $';
pdpcp_cmd_rreg($aname, PDPCP_ADDR_MEMI);
} elsif ($cmd =~ /^rm/) { # rm ----------------------------
$cmd_rest = $';
pdpcp_cmd_rreg($aname, PDPCP_ADDR_MEM);
} elsif ($cmd =~ /^wmi/) { # wmi ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_MEMI);
} elsif ($cmd =~ /^wm/) { # wm ----------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_MEM);
} elsif ($cmd =~ /^stapc/) { # stapc -------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_PC);
pdpcp_cmd_wreg($aname, PDPCP_ADDR_CNTL, PDPCP_FUNC_STA);
} elsif ($cmd =~ /^sta/) { # sta ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_CNTL, PDPCP_FUNC_STA);
} elsif ($cmd =~ /^sto/) { # sto ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_CNTL, PDPCP_FUNC_STO);
} elsif ($cmd =~ /^cont/) { # cont --------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_CNTL, PDPCP_FUNC_CONT);
} elsif ($cmd =~ /^step/) { # step --------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_CNTL, PDPCP_FUNC_STEP);
} elsif ($cmd =~ /^rst/) { # rst ---------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_CNTL, PDPCP_FUNC_RST);
} elsif ($cmd =~ /^wibrb/) { # wibrb -------------------------
$cmd_rest = $';
pdpcp_cmd_wreg($aname, PDPCP_ADDR_IBRB);
} elsif ($cmd =~ /^ribr/) { # ribr --------------------------
$cmd_rest = $';
my $off = cget_gdat(6,8);
pdpcp_cmd_rreg($aname, PDPCP_ADDR_IBR+int($off/2));
} elsif ($cmd =~ /^wibr/) { # wibr --------------------------
$cmd_rest = $';
my $off = cget_gdat(6,8);
pdpcp_cmd_wreg($aname, PDPCP_ADDR_IBR+int($off/2));
} elsif ($cmd =~ /^wtgo/) { # wtgo --------------------------
$cmd_rest = $';
rri_cmdlist_do();
my $tstart = get_time();
raw_get9_check(D9ATTN, "wtgo"); # ???FIXME this is a hack...
printf "-- wtgo # wait for %7.3f sec\n", get_time()-$tstart;
push @rri_cmdlist, {cname => "attn",
aname => ".wtgo"};
} elsif ($cmd =~ /^wtlam/) { # wtlam -------------------------
$cmd_rest = $';
my ($ref_data, $msk_data) = cget_tagval2_gdat("d",16,8);
rri_cmdlist_do();
my $tstart = get_time();
raw_get9_check(D9ATTN, "wtgo"); # ???FIXME this is a hack...
printf "-- wtlam # wait for %7.3f sec\n", get_time()-$tstart;
push @rri_cmdlist, {cname => "attn",
aname => ".wtlam",
ref_data => $ref_data,
msk_data => $msk_data};
} else {
print "pi_rri($curmode)-E: unknown command: \"$cmd_line\"\n";
}
cget_chkblank() unless $cmd_bad;
if ($cmd_bad) {
print "pi_rri($curmode)-E: parse error, command ignored: \"$cmd_line\"\n";
} else {
if (scalar(@rri_cmdlist) >= $cmax || $cclast ||
($rri_ncmdmax && scalar(@rri_cmdlist) >= $rri_ncmdmax)) {
$rri_ncmdmax = undef;
rri_cmdlist_do();
}
}
}
#-------------------------------------------------------------------------------
sub pdpcp_cmd_rreg {
my ($aname,$addr) = @_;
my ($ref_data,$msk_data) = cget_tagval2_gdat("d",16,8);
my ($ref_stat,$msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "rreg",
aname => $aname,
addr => $addr,
ref_data => $ref_data,
msk_data => $msk_data,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
}
#-------------------------------------------------------------------------------
sub pdpcp_cmd_wreg {
my ($aname,$addr,$data) = @_;
my $ldata = (defined $data) ? $data : cget_gdat(16,8);
my ($ref_stat,$msk_stat) = rri_cget_stat;
if (not $cmd_bad) {
push @rri_cmdlist, {cname => "wreg",
aname => $aname,
addr => $addr,
data => $ldata,
ref_stat => $ref_stat,
msk_stat => $msk_stat};
}
}
#-------------------------------------------------------------------------------
sub serv11_open {
$rri_ref_sdef = 0x00; # by default check for 'hard' errors
$rri_msk_sdef = 0x70; # ignore cpuhalt,cpugo and attn
serv11_config() unless $serv11_config_done;
}
#-------------------------------------------------------------------------------
sub serv11_flush {
my ($case) = @_;
}
#-------------------------------------------------------------------------------
sub serv11_close {
}
#-------------------------------------------------------------------------------
# <string
# (string
# lspc
# lsmem {-m|-a} g16{(:g16|nddd)} {(>|>>)file}
# ldabs {-s} file
# exa <rrange>
# dep <rrange> g16
# set ...
# sho conf
# sho att
# sho regs
# sho mmu
# sho ubm[ap]
# wtt <unit> "string"
# attn
# att <unit> file
# det <unit>|all
# init
# boot <unit>
# start g16
# step
# stop
# cont
# reset
# server
#
sub serv11_cexec {
my ($cmd) = @_;
$cmd_line = $cmd;
$cmd_rest = "";
$cmd_bad = 0;
#
# First handle 'special syntax commands: ( and <
#
if ($cmd =~ /^([<(])/) { # < and ( short hands -----------
my $str = $';
my $ucb = cget_ucb("term", "tt0");
return if $cmd_bad or cget_chkblank();
my @bytes;
if ($1 eq "<") { # < command
conv_str2bytes($str, \@bytes);
push @bytes, 0015;
} else { # ( command
if ($str =~ /^\\([0-7]{3})$/) { # (\ooo escape
push @bytes, oct $1;
} elsif ($str =~ /^\\\^(.)$/) { # (^c escape
my $byt = ord($1); # to byte value
$byt -= 040 if ($byt >= 040); # map to control char
$byt -= 040 if ($byt >= 040);
$byt -= 040 if ($byt >= 040);
push @bytes, $byt;
} else {
conv_str2bytes($str, \@bytes, 1);
}
}
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
&{$ctl->{ichrhdl}}($ucb, \@bytes);
return;
}
#
# Now prepare normal syntax commands
#
if ($cmd =~ /^(\w+)\b/) { # get command name
$cmd = $1;
$cmd_rest = $';
$cmd_rest =~ s/^\s*//;
}
if ($cmd =~ /^lspc/) { # dump pc/ps -----------------
return if cget_chkblank();
serv11_cexec_shoreg(0);
} elsif ($cmd =~ /^lsmem/) { # dump memory --------------------
my $opt_m = cget_opt("-m");
my $opt_a = cget_opt("-a");
my $abeg = cget_gdat(22, 8);
return if $cmd_bad;
my $aend = $abeg+64;
my $fh = *STDOUT;
my $redi = 0;
if ($cmd_rest =~ /^:n(\d*)/) {
$cmd_rest = $';
$aend = $abeg + 2*(int $1);
} elsif ($cmd_rest =~ /^:/) {
$cmd_rest = $';
$aend = cget_gdat(22, 8);
return if $cmd_bad;
}
if ($cmd_rest =~ /^\s*(>{1,2})([\w\/.-]+)/) {
$cmd_rest = $';
my $oper = $1;
my $file = $2;
my $fh_new = new FileHandle;
if ($fh_new->open("$oper$file")) {
$fh = $fh_new;
$redi = 1;
} else {
print "pi_rri($curmode)-E: failed to open output file $file\n";
}
}
$abeg &= 0xfffffffe;
$aend &= 0xfffffffe;
my $nword = int (($aend - $abeg)/2);
$nword = 1 if $nword <= 1;
return if cget_chkblank();
my @data;
my $rc = serv11_exec_rblk($abeg, 1, \@data, $nword);
my $inst_nw = 0;
my $inst_str = "";
printf $fh "Memory %8.8o:%8.8o:\n", $abeg, $aend;
for (my $i=0; $i<$nword; $i++) {
if ($opt_m) {
($inst_str, $inst_nw) =
pdp11_disassemble($abeg+2*$i, $data[$i],$data[$i+1],$data[$i+2]);
printf $fh " %6.6o :", $abeg+2*$i;
for (my $j=0; $j<3; $j++) {
if ($j < $inst_nw) {
printf $fh " %6.6o", (defined $data[$i+$j]) ? $data[$i+$j] : 0;
} else {
print $fh " ";
}
}
printf $fh " # %s\n", $inst_str;
$i += $inst_nw-1;
} elsif ($opt_a) {
my $nline = $nword - $i;
my $ascbuf;
$nline = 4 if $nline > 4;
printf $fh " %6.6o :", $abeg+2*$i;
for (my $j=0; $j<$nline; $j++) {
my $word = $data[$i+$j];
my $bl = $word & 0377;
my $bh = ($word>>8) & 0377;
printf $fh " %3.3o %3.3o", $bl, $bh;
$ascbuf .= " " . conv_byte2ascii2($bl);
$ascbuf .= " " . conv_byte2ascii2($bh);
}
print $fh " " x (8*(4-$nline)+4);
print $fh $ascbuf;
print $fh "\n";
$i += $nline-1;
} else {
printf $fh " %6.6o : %6.6o\n", $abeg+2*$i, $data[$i];
}
}
$fh->close() if $redi;
} elsif ($cmd =~ /^ldabs/) { # load absolute loader format ----
my $opt_s = cget_opt("-s");
my $file = cget_file();
return if cget_chkblank();
serv11_cexec_ldabs($file, $opt_s);
} elsif ($cmd =~ /^exa/) { # examine register or memory -----
my $optset = cget_optset("ir");
my ($ctl, $beg, $end) = cget_regrange();
return if cget_chkblank();
serv11_cexec_exa($optset, $ctl, $beg, $end);
} elsif ($cmd =~ /^dep/) { # deposit register or memory -----
my $optset = cget_optset("ir");
my ($ctl, $beg, $end) = cget_regrange();
my $data = cget_gdat(16, 8);
return if cget_chkblank();
serv11_cexec_dep($optset, $ctl, $beg, $end, $data);
} elsif ($cmd =~ /^set/) { # set parameter ------------------
my $what = cget_name();
return if $cmd_bad;
if ($what =~ /^sim/) { # set sim[ulator] ------
my $pnam = cget_name();
my $val = cget_bool();
return if $cmd_bad or cget_chkblank();
my $ind;
$ind = 15 if $pnam eq "cpmon";
$ind = 14 if $pnam eq "rbmon";
$ind = 13 if $pnam eq "tmu";
if (defined $ind) {
rri_sideband(0x00, ($ind<<8) + $val);
} else {
printf "pi_rri($curmode)-E: Invalid parameter '$pnam' for set sim\n";
}
} else { # set <device> ---------
my $ctl = $serv11_ctltbl{uc($what)};
if (defined $ctl) {
my $partbl = $ctl->{partbl};
if (defined $partbl) {
my $pnam = cget_name();
return if $cmd_bad;
my $pdsc = $partbl->{$pnam};
if (defined $pdsc) {
my $type = $pdsc->{type};
if ($type =~ /^hval:([bdos])$/) {
my $cnv = $1;
my $val;
if ($cnv eq "b") {
$val = cget_bool();
} elsif ($cnv eq "d") {
$val = cget_gdat(32, 10);
} elsif ($cnv eq "o") {
$val = cget_gdat(32, 8);
} elsif ($cnv eq "s") {
$val = $cmd_rest;
$val =~ s/^\s*//;
$val =~ s/\s*$//;
$cmd_rest = "";
}
return if $cmd_bad or cget_chkblank();
$ctl->{$pnam} = $val;
} else {
print "pi_rri($curmode)-E: unexpected type $type in partbl\n";
}
} else {
print "pi_rri($curmode)-E: '$pnam' not valid for 'set $what'\n";
}
} else {
print "pi_rri($curmode)-I: nothing to set for '$what'\n";
}
} else {
print "pi_rri($curmode)-E: unknown entity for 'set': \"$what\"\n";
}
}
} elsif ($cmd =~ /^sho/) { # show parameters ----------------
my $what = cget_name();
return if $cmd_bad;
if ($what =~ /^conf/) { # sho conf[iguration] --
return if cget_chkblank();
serv11_cexec_shoconf();
} elsif ($what =~ /^att/) { # sho att --------------
return if cget_chkblank();
serv11_cexec_shoatt();
} elsif ($what =~ /^regs/) { # sho regs -------------
return if cget_chkblank();
serv11_cexec_shoreg(1);
} elsif ($what =~ /^mmu/) { # sho mmu --------------
return if cget_chkblank();
serv11_cexec_shommu_ssrx;
serv11_cexec_shommu_sadr(0172300, "KM");
serv11_cexec_shommu_sadr(0172200, "SM");
serv11_cexec_shommu_sadr(0177600, "UM");
} elsif ($what =~ /^ubm/) { # sho ubmap ------------
return if cget_chkblank();
my @data;
my $rc = serv11_exec_rblk(0170200, 0, \@data, 64);
print "UNIBUS mapping registers:\n";
for (my $i=0; $i<32; $i++) {
printf " [%2d]: %2.2o,%6.6o\n", $i, $data[2*$i+1], $data[2*$i];
}
} else { # sho <device> ---------
my $ctl = $serv11_ctltbl{uc($what)};
if (defined $ctl) {
my $partbl = $ctl->{partbl};
if (defined $partbl) {
foreach my $pnam (sort keys %{$partbl}) {
my $pdsc = $partbl->{$pnam};
my $type = $pdsc->{type};
if ($type =~ /^hval:([bdos])$/) {
my $cnv = $1;
my $val = $ctl->{$pnam};
my $val_str = $val;
if (defined $val) {
$val_str = ($val) ? "1 (yes)" : "0 (no)" if $cnv eq "b";
$val_str = sprintf("%6d.", $val) if $cnv eq "d";
$val_str = sprintf("%6.6o", $val) if $cnv eq "o";
} else {
$val_str = "<undef>";
}
printf "%4s %10s : %s\n", uc($what), $pnam, $val_str;
} else {
print "pi_rri($curmode)-E: unexpected type $type in partbl\n";
}
}
} else {
print "pi_rri($curmode)-I: nothing to show for '$what'\n";
}
} else {
print "pi_rri($curmode)-E: unknown entity for 'sho': \"$what\"\n";
}
}
} elsif ($cmd =~ /^wtt/) { # write to TT decives -------------
my $ucb = cget_ucb("term");
my $str = "\\n";
if ($cmd_rest =~ /^\s*"(.*)"\s*/) {
$cmd_rest = $';
$str = $1;
}
return if $cmd_bad or cget_chkblank();
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
my @bytes;
conv_str2bytes($str, \@bytes, 1);
&{$ctl->{ichrhdl}}($ucb, \@bytes)
} elsif ($cmd =~ /^attn/) { # attn --------------------------
return if cget_chkblank();
serv11_server_attn_get();
} elsif ($cmd =~ /^att/) { # attach ------------------------
my $ucb = cget_ucb();
return if $cmd_bad;
serv11_cexec_attdet(0,$ucb);
} elsif ($cmd =~ /^det/) { # detach ------------------------
my $ucb = cget_ucb();
return if cget_chkblank();
serv11_cexec_attdet(1,$ucb);
} elsif ($cmd =~ /^init/) { # init --------------------------
return if cget_chkblank();
serv11_init_dispatch() if $serv11_init_pending;
} elsif ($cmd =~ /^boot/) { # boot --------------------------
my $ucb = cget_ucb();
return if $cmd_bad or cget_chkblank();
serv11_cexec_boot($ucb);
} elsif ($cmd =~ /^start/) { # start --------------------------
my $addr = cget_gdat(16, 8);
return if cget_chkblank();
my @rval;
my $rc;
serv11_rri_init(".anena", 0xff, $serv11_init_anena);# enable attn+ioto
serv11_rri_attn("attn"); # discard old attn's
serv11_rri_wreg("wpc", PDPCP_ADDR_PC, $addr);
serv11_rri_wreg("sta", PDPCP_ADDR_CNTL, PDPCP_FUNC_STA);
$rc = serv11_rri_exec(\@rval);
} elsif ($cmd =~ /^step/) { # step --------------------------
my @rval;
my $rc;
serv11_rri_wreg("sta", PDPCP_ADDR_CNTL, PDPCP_FUNC_STEP);
$rc = serv11_rri_exec(\@rval);
serv11_cexec_shoreg(1);
} elsif ($cmd =~ /^stop/) { # stop --------------------------
my @rval;
my $rc;
serv11_rri_wreg("sto", PDPCP_ADDR_CNTL, PDPCP_FUNC_STO);
$rc = serv11_rri_exec(\@rval);
serv11_cexec_shoreg(1);
} elsif ($cmd =~ /^cont/) { # cont --------------------------
my @rval;
my $rc;
serv11_rri_wreg("sto", PDPCP_ADDR_CNTL, PDPCP_FUNC_CONT);
$rc = serv11_rri_exec(\@rval);
} elsif ($cmd =~ /^reset/) { # reset -------------------------
my @rval;
my $rc;
serv11_rri_wreg("rst", PDPCP_ADDR_CNTL, PDPCP_FUNC_RST);
$rc = serv11_rri_exec(\@rval);
} elsif ($cmd =~ /^server/) { # enter server mode --------------
return if cget_chkblank();
serv11_server();
} else {
print "pi_rri($curmode)-E: unknown command: \"$cmd_line\"\n";
}
}
#-------------------------------------------------------------------------------
sub serv11_cexec_shoreg {
my ($mode) = @_;
my $ipc;
my $ips;
my @rval;
if ($mode > 0) {
serv11_rri_rreg("rr0", PDPCP_ADDR_R0+0);
serv11_rri_rreg("rr1", PDPCP_ADDR_R0+1);
serv11_rri_rreg("rr2", PDPCP_ADDR_R0+2);
serv11_rri_rreg("rr3", PDPCP_ADDR_R0+3);
serv11_rri_rreg("rr4", PDPCP_ADDR_R0+4);
serv11_rri_rreg("rr5", PDPCP_ADDR_R0+5);
serv11_rri_rreg("rr6", PDPCP_ADDR_R0+6);
}
$ipc = serv11_rri_rreg("rr7", PDPCP_ADDR_R0+7);
$ips = serv11_rri_rreg("rps", PDPCP_ADDR_PSW);
my $rc = serv11_rri_exec(\@rval);
print "Processor registers and status:\n" if ($mode > 0);
my $ps_bin = gconv_dat16($rval[$ips],2);
printf " PC: %6.6o ", $rval[$ipc] if ($mode == 0);
printf " PS: %6.6o", $rval[$ips];
printf " cmo=%s",substr($ps_bin,0,2); # bit 15:14 -> 0,2
printf " pmo=%s",substr($ps_bin,2,2); # bit 13:12 -> 2,2
printf " set=%s",substr($ps_bin,4,1); # bit 11 -> 4
printf " pri=%d",($rval[$ips]>>5)&0x7; # bit 07:05
printf " t=%s", substr($ps_bin,11,1); # bit 04 -> 11,1
printf " NZVC=%s", substr($ps_bin,12,4); # bit 03:00 -> 12,4
print "\n";
if ($mode > 0) {
printf " R0: %6.6o", $rval[0];
printf " R1: %6.6o", $rval[1];
printf " R2: %6.6o", $rval[2];
printf " R3: %6.6o\n", $rval[3];
printf " R4: %6.6o", $rval[4];
printf " R5: %6.6o", $rval[5];
printf " SP: %6.6o", $rval[6];
printf " PC: %6.6o\n", $rval[$ipc];
}
}
#-------------------------------------------------------------------------------
# ssr0 177572
# ssr1 177574
# ssr2 177576
# ssr3 172516
sub serv11_cexec_shommu_ssrx {
my @rval;
serv11_rri_wreg("wal", PDPCP_ADDR_AL, 0177572);
my $issr0 = serv11_rri_rreg("rmi", PDPCP_ADDR_MEMI);
my $issr1 = serv11_rri_rreg("rmi", PDPCP_ADDR_MEMI);
my $issr2 = serv11_rri_rreg("rmi", PDPCP_ADDR_MEMI);
serv11_rri_wreg("lal", PDPCP_ADDR_AL, 0172516);
my $issr3 = serv11_rri_rreg("rmi", PDPCP_ADDR_MEMI);
my $rc = serv11_rri_exec(\@rval);
print "MMU registers:\n";
printf " SSR0: %6.6o\n", $rval[$issr0];
printf " SSR1: %6.6o\n", $rval[$issr1];
printf " SSR2: %6.6o\n", $rval[$issr2];
printf " SSR3: %6.6o\n", $rval[$issr3];
}
#-------------------------------------------------------------------------------
#
# Note: The ptape maindec's have even size records, except possibly for the
# last one, and always start at an even address.
# The lda's extracted with UPD2 PIP from the xxdp22 disk have often
# records with 503 byte payload, starting at even and odd addresses.
# Since blkw only handles even sized transfers on even addresses some
# magic with the %oddbyt hash is needed to handle this correctly.
#
sub serv11_cexec_ldabs {
my ($file,$opt_s) = @_;
if (not -r $file) {
print "pi_rri($curmode)-E: file $file not found or readable\n";
return;
}
my $fh = new FileHandle;
$fh->open("<$file") or die "unexpected open failure";
my $chrnum = 0; # char number in block
my $blknum = 0; # block number
my $bytcnt = 0; # byte count
my $ldaddr = 0; # load address
my $chksum = 0; # check sum
my $addr = 0; # current address
my @data; # data array for transfer
my %oddbyt; # odd byte cache
my $word;
while (1) {
my $buf;
my $rc = $fh->read($buf,1);
if ($rc == 0) {
print "pi_rri($curmode)-E: unexpected EOF in $file\n" unless $chrnum == 0;
return;
}
return if $rc != 1;
my $byt = ord($buf);
$chksum = ($chksum + $byt) & 0377;
if ($chrnum == 0) { # in blank tape
if ($byt == 0) {
next;
} elsif ($byt == 1) {
$chrnum += 1;
} else {
printf "pi_rri($curmode)-E: unexpected start-of-block %3.3o in $file\n",
$byt;
return;
}
} elsif ($chrnum == 1) { # 001 frame seen
if ($byt == 0) {
$chrnum += 1;
} else {
printf "pi_rri($curmode)-E: unexpected 2nd char %3.3o in $file\n",
$byt;
return;
}
} elsif ($chrnum == 2) { # byte count low
$bytcnt = $byt & 0377;
$chrnum += 1;
} elsif ($chrnum == 3) { # byte count high
$bytcnt |= ($byt & 0377)<<8;
$chrnum += 1;
} elsif ($chrnum == 4) { # load address low
$ldaddr = $byt & 0377;
$chrnum += 1;
} elsif ($chrnum == 5) { # load address high
$ldaddr |= ($byt & 0377)<<8;
$chrnum += 1;
printf "pi_rri($curmode)-I: block %3d, length %5d byte,".
" address %6.6o:%6.6o\n",
$blknum, ($bytcnt-6), $ldaddr, $ldaddr+($bytcnt-6)-1;
$addr = $ldaddr; # setup current address
$word = 0;
if (($addr & 01) == 1 && $bytcnt > 6) { # setup even byte if known...
$word = $oddbyt{sprintf("%6.6o",$addr)};
if (not defined $word) {
printf "pi_rri($curmode)-W: no low byte data for %6.6o\n", $addr;
$word = 0;
}
}
} elsif ($chrnum == $bytcnt) { # check sum byte
if ($chksum != 0) {
printf "pi_rri($curmode)-E: check sum error %3.3o in $file\n",
$chksum;
return;
}
if ($chrnum == 6) {
printf "pi_rri($curmode)-I: start address %6.6o\n", $ldaddr;
return;
} else {
if (($addr & 01) == 1) { # high byte not yet seen
push @data, $word; # zero fill high byte
$oddbyt{sprintf("%6.6o",$addr)} = $word; # store even byte for later
# note that address is odd here
}
serv11_exec_wblk($ldaddr, 0, \@data);
@data = ();
}
$chrnum = 0;
$blknum += 1;
} else { # in data
if (($addr & 01) == 0) { # low byte
$word = $byt & 0377;
$addr += 1;
} else { # high byte
$word |= ($byt & 0377)<<8;
push @data, $word;
$addr += 1;
}
$chrnum += 1;
}
}
$fh->close();
}
#-------------------------------------------------------------------------------
# sadr format:
# offset 0: DR[0] I space
# offset 20: DR[0] D space
# offset 40: AR[0] I space
# offset 60: AR[0] D space
#
sub serv11_cexec_shommu_sadr {
my ($base,$mode) = @_;
my @data;
my $rc = serv11_exec_rblk($base, 0, \@data, 32);
for (my $i=0; $i<16; $i++) {
my $space = ($i<8) ? "I" : "D";
my $ind = $i%8;
my $dr = $data[$i];
my $ar = $data[$i+16];
my $dr_bin = gconv_dat16($dr,2);
my $dr_acf = $dr&0xf; # bit 3:0
printf " %s-%s[%d]: %6.6o,%6.6o", $mode,$space,$ind, $dr, $ar;
printf " slf=%3d", ($dr>>8)&0xff;
printf " aib=%s", substr($dr_bin,8,2); # bit 7:6 -> 8,2
printf " acf=%d", $dr_acf;
print "\n";
}
}
#-------------------------------------------------------------------------------
sub serv11_cexec_shoconf {
foreach my $ctlname (sort { $serv11_ctltbl{$b}->{base} <=>
$serv11_ctltbl{$a}->{base} }
keys %serv11_ctltbl) {
my $ctl = $serv11_ctltbl{$ctlname};
my $mask = $ctl->{probemask};
my $ival = $ctl->{probe_ival};
my $rval = $ctl->{probe_rval};
my $ib_str = ($mask =~ /i/) ? ( (defined $ival) ? "y" : "n" ) : "-";
my $rb_str = ($mask =~ /r/) ? ( (defined $rval) ? "y" : "n" ) : "-";
printf "%-3s %-9s %4s: %s ib=%s rb=%s lam=%s boot=%s", $ctlname,
$ctl->{ctltype}, $ctl->{type},
($ctl->{base} ? sprintf("%6.6o", $ctl->{base}) : "......"),
$ib_str, $rb_str,
(exists $ctl->{lam} ? sprintf("%2d",$ctl->{lam}) : " -"),
(exists $ctl->{boot_code} ? "y" : "n");
printf " %s",$ctl->{probe_text} if $ctl->{probe_text};
print "\n";
}
}
#-------------------------------------------------------------------------------
sub serv11_cexec_shoatt {
foreach my $unitname (sort keys %serv11_unittbl) {
my $ucb = $serv11_unittbl{$unitname};
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
next unless $ctl->{probe_ok};
next unless $ucb->{att_ok};
printf "%-3s : ", $unitname;
if ($ctl->{type} eq "disk") {
printf "nblk=%6d wp=%s file=%s",
$ucb->{att_nblk},
($ucb->{att_wpro} ? "y" : "n"),
$ucb->{att_file};
} elsif ($ctl->{type} eq "tape") {
printf "wp=%s file=%s",
($ucb->{att_wpro} ? "y" : "n"),
$ucb->{att_file};
} elsif ($ctl->{type} eq "term") {
printf "port=%s",
$ucb->{att_port};
}
print "\n";
}
}
#-------------------------------------------------------------------------------
sub serv11_cexec_attdet {
my ($det,$ucb) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
my $attdethdl = $ctl->{attdethdl};
if (not defined $attdethdl) {
printf "pi_rri($curmode)-E: attach/detach not supported for %s\n",
$ucb->{unitname};
return;
}
&{$attdethdl}($det, $ucb); # call handler
}
#-------------------------------------------------------------------------------
sub serv11_cexec_boot {
my ($ucb) = @_;
my @rval;
my $rc;
my $unitname = $ucb->{unitname};
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if (not exists $ctl->{boot_code}) {
print "pi_rri($curmode)-E: device $unitname not bootable\n";
return;
}
serv11_init_dispatch() if $serv11_init_pending;
my @boot_code = @{$ctl->{boot_code}};
my $boot_length = scalar(@boot_code);
my $boot_mode = $ctl->{boot_mode};
my $boot_entry = $ctl->{boot_entry};
$boot_mode = "disk" unless defined $boot_mode;
if ($boot_mode eq "disk") {
my $boot_unit = $ctl->{boot_unit};
$boot_code[int (($boot_unit-(BOOT_START))/2)] =
$ucb->{ctlunit}; # patch in unit num
$rc = serv11_exec_wblk(BOOT_START, 0, \@boot_code, $boot_length);
} elsif ($boot_mode eq "ptape") {
my $boot_base = $ctl->{boot_base};
my $memsize = 56 * 1024; # FIXME: check memtop !!!
$memsize = 56*1024 if ($memsize > 56*1024);
my $nblk8k = $memsize/020000;
my $offset = ($nblk8k-1) * 020000;
$boot_base += $offset;
$boot_entry += $offset;
$rc = serv11_exec_wblk($boot_base, 0, \@boot_code, $boot_length);
} else {
print_fatal("unsupported boot mode '$boot_mode' in serv11_cexec_boot");
}
serv11_rri_init(".anena", 0xff, $serv11_init_anena); # enable attn+ioto
serv11_rri_attn("attn"); # discard old attn's
serv11_rri_wreg("wpc", PDPCP_ADDR_PC, $boot_entry);
serv11_rri_wreg("sta", PDPCP_ADDR_CNTL, PDPCP_FUNC_STA);
$rc = serv11_rri_exec(\@rval);
}
#-------------------------------------------------------------------------------
sub serv11_cexec_exa {
my ($optset,$ctl,$beg,$end) = @_;
if (not defined $ctl) { # numerical address
for (my $addr=$beg; $addr<=$end; $addr+=2) {
my @rval;
serv11_rri_wreg("wal", PDPCP_ADDR_AL, $addr);
serv11_rri_rreg("rm", PDPCP_ADDR_MEM);
my $rc = serv11_rri_exec(\@rval);
printf "mem %6.6o : %6.6o\n", $addr, $rval[0];
}
} else {
my $reglist = $ctl->{reglist};
for (my $i=$beg; $i<=$end; $i++) {
my $dsc = $reglist->[$i];
last if not defined $reglist->[$i];
my $name = $dsc->{name};
my $addr = $dsc->{addr};
my $offset = $dsc->{offset};
my $attr = $dsc->{attr};
my $val;
my $addr_str = "......";
my $acs_str = "ib";
my $val_str = "......";
my $com_str = "";
$addr = $ctl->{base} + $offset if defined $offset;
$attr = 0 unless defined $attr;
$acs_str = "rb" if ($attr & REGATTR_RBRD);
$acs_str = "ib" if $optset =~ /i/;
$acs_str = "rb" if $optset =~ /r/;
if ($end > $beg &&
( ( ($attr & REGATTR_IBMBOX) && $acs_str eq "ib" ) ||
( ($attr & REGATTR_RBMBOX) && $acs_str eq "rb" )
) ) {
$com_str = "mailbox skipped";
} else {
my $exadethdl = $dsc->{hdl};
if (defined $dsc->{hdl}) {
$acs_str = " ";
$val = &{$dsc->{hdl}}(0, $dsc);
} else {
if ($acs_str eq "rb") {
my $ibrbase = $addr & ~(077);
my $ibroff = $addr - $ibrbase;
serv11_rri_wreg("wibrb", PDPCP_ADDR_IBRB, $ibrbase);
serv11_rri_rreg("ribr", PDPCP_ADDR_IBR + int($ibroff/2));
$acs_str = "rb";
} else {
serv11_rri_wreg("wal", PDPCP_ADDR_AL, $addr);
serv11_rri_rreg("rm", PDPCP_ADDR_MEM);
$acs_str = "ib";
}
my @rval;
my $rc = serv11_rri_exec(\@rval);
$val = $rval[0];
}
}
$addr_str = sprintf("%6.6o", $addr) if defined $addr;
$val_str = sprintf("%6.6o", $val) if defined $val;
printf "%4s %6s %2s %6s : %6s", $ctl->{ctlname}, $name,
$acs_str, $addr_str, $val_str;
print " $com_str" if defined $com_str;
print "\n";
}
}
}
#-------------------------------------------------------------------------------
sub serv11_cexec_dep {
my ($optset,$ctl,$beg,$end,$data) = @_;
if (not defined $ctl) { # numerical address
for (my $addr=$beg; $addr<=$end; $addr+=2) {
my @rval;
serv11_rri_wreg("wal", PDPCP_ADDR_AL, $addr);
serv11_rri_wreg("wm", PDPCP_ADDR_MEM, $data);
my $rc = serv11_rri_exec(\@rval);
}
} else {
my $reglist = $ctl->{reglist};
for (my $i=$beg; $i<=$end; $i++) {
my $dsc = $reglist->[$i];
last if not defined $reglist->[$i];
my $name = $dsc->{name};
my $addr = $dsc->{addr};
my $offset = $dsc->{offset};
my $attr = $dsc->{attr};
my $acs_str = "ib";
$addr = $ctl->{base} + $offset if defined $offset;
$attr = 0 unless defined $attr;
$acs_str = "rb" if ($attr & REGATTR_RBWR);
$acs_str = "ib" if $optset =~ /i/;
$acs_str = "rb" if $optset =~ /r/;
my $exadethdl = $dsc->{hdl};
if (defined $dsc->{hdl}) {
$acs_str = " ";
&{$dsc->{hdl}}(1, $dsc, $data);
} else {
if ($acs_str eq "rb") {
my $ibrbase = $addr & ~(077);
my $ibroff = $addr - $ibrbase;
serv11_rri_wreg("wibrb", PDPCP_ADDR_IBRB, $ibrbase);
serv11_rri_wreg("wibr", PDPCP_ADDR_IBR + int($ibroff/2), $data);
$acs_str = "rb";
} else {
serv11_rri_wreg("wal", PDPCP_ADDR_AL, $addr);
serv11_rri_wreg("wm", PDPCP_ADDR_MEM, $data);
$acs_str = "ib";
}
my @rval;
my $rc = serv11_rri_exec(\@rval);
}
}
}
}
#-------------------------------------------------------------------------------
# config is called once on the first entering of serv11 mode
#
sub serv11_config {
$serv11_config_done = 1;
foreach my $ctlname (sort keys %serv11_ctltbl) {
my $ctl = $serv11_ctltbl{$ctlname};
$ctl->{probemask} = "ir" unless defined $ctl->{probemask};
&{$ctl->{probehdl}}($ctl) if exists $ctl->{probehdl};
}
if (not $serv11_ctltbl{CPU}->{probe_ok}) {
print_fatal("probe of CPU failed in serv11_open()");
}
init_regtbl();
serv11_cexec_shoconf();
}
#-------------------------------------------------------------------------------
sub serv11_init_dispatch {
foreach my $ctlname (sort keys %serv11_ctltbl) {
my $ctl = $serv11_ctltbl{$ctlname};
&{$ctl->{inithdl}}($ctl) if (exists $ctl->{inithdl} && $ctl->{probe_ok});
}
$serv11_init_pending = 0;
}
#-------------------------------------------------------------------------------
sub serv11_server {
my $old_timeout = $raw_timeout;
my $nfound;
my $fds_rd_act = "";
my $fds_rd;
my $stat_delta = 10.;
my $stat_count = 0;
my $fno_rcv = fileno($fh_rcv);
my $fno_stdin = fileno(STDIN);
my @telfno2dsc;
@serv11_attntbl = ();
foreach my $ctlname (sort keys %serv11_ctltbl) {
my $ctl = $serv11_ctltbl{$ctlname};
if ($ctl->{probe_ok} && exists $ctl->{lam} && exists $ctl->{attnhdl}) {
push @serv11_attntbl, [1<<($ctl->{lam}), $ctl->{attnhdl}, $ctl];
}
}
$raw_timeout = 30.;
$serv11_active = 1;
print "pi_rri($curmode)-I: entering server mode\n";
my $time_stat = get_time() + $stat_delta;
while ($serv11_active) {
my $time_now = get_time();
if ($time_now >= $time_stat) {
##serv11_server_attn_dispatch(1);
if ($stat_count % 20 == 0) {
printf $fh_log "stat -- ";
printf $fh_log " obyte oesc osop ibyte iesc att";
printf $fh_log " xreg xblk rdisk wdisk";
printf $fh_log "\n";
}
$stat_count += 1;
my $dt = $stat_delta;
printf $fh_log "stat -- %s", get_timestamp();
printf $fh_log " %6.0f", ($stat_tab{obyte} - $stat_tab_last{obyte})/$dt;
printf $fh_log " %4.0f", ($stat_tab{oesc} - $stat_tab_last{oesc})/$dt;
printf $fh_log " %4.0f", ($stat_tab{osop} - $stat_tab_last{osop})/$dt;
printf $fh_log " %6.0f", ($stat_tab{ibyte} - $stat_tab_last{ibyte})/$dt;
printf $fh_log " %4.0f", ($stat_tab{iesc} - $stat_tab_last{iesc})/$dt;
printf $fh_log " %3.0f", ($stat_tab{att} - $stat_tab_last{att})/$dt;
printf $fh_log " %5.0f", ($stat_tab{xreg} - $stat_tab_last{xreg})/$dt;
printf $fh_log " %4.0f", ($stat_tab{xblk} - $stat_tab_last{xblk})/$dt;
printf $fh_log " %6.0f", ($stat_tab{rdisk} - $stat_tab_last{rdisk})/$dt;
printf $fh_log " %6.0f", ($stat_tab{wdisk} - $stat_tab_last{wdisk})/$dt;
printf $fh_log "\n";
%stat_tab_last = %stat_tab;
while ($time_stat < $time_now) {
$time_stat += $stat_delta;
}
}
my $timeout = $time_stat - $time_now;
# set timeout=0 if some unfinished business is still pending
$timeout = 0. if $serv11_attn_mask != 0; # attn mask not yet worked down
$timeout = 0. if scalar(@serv11_icbque); # icb queue non empty
$timeout = 0. if scalar(@que_rcv); # still input chars in buffer
if ($serv11_fds_update) {
$fds_rd_act = "";
vec($fds_rd_act, $fno_rcv, 1) = 1;
vec($fds_rd_act, $fno_stdin, 1) = 1;
@telfno2dsc = ();
foreach my $port_str (keys %telnettbl) {
my $teldsc = $telnettbl{$port_str};
my $fno;
if ($teldsc->{state} == TELNET_STATE_LISTEN) {
$fno = fileno($teldsc->{fh_port});
} else {
$fno = fileno($teldsc->{fh_data});
}
vec($fds_rd_act, $fno, 1) = 1;
push @telfno2dsc, [$fno, $teldsc];
}
$serv11_fds_update = 0;
}
##printf $fh_log "+++1 select $timeout, rcvq=%d\n", scalar(@que_rcv);
$nfound = select($fds_rd=$fds_rd_act, undef, undef, $timeout);
##printf $fh_log "+++2 select $nfound\n";
if (vec($fds_rd, $fno_stdin, 1)) {
my $cmd = <STDIN>;
if (defined $cmd) {
chomp $cmd;
$cmd = "lspc" unless $cmd ne "";
$cmd =~ s{^\s*}{}; # remove leading blanks
$cmd =~ s{--.*}{}; # remove comments after --
$cmd =~ s{\s*$}{}; # remove trailing blanks
if ($cmd eq "quit") {
$serv11_active = 0;
} else {
if ($cmd =~ m/^C/) { # ignore, but log "C ..." lines
print $fh_log "$cmd\n";
} elsif ($cmd =~ m/^#/) { # ignore "# ...." lines
} elsif ($cmd =~ m/^;/) { # ignore "; ...." lines
} else { # otherwise execute
serv11_cexec($cmd);
}
}
} else { # handle ^D
$serv11_active = 0;
}
}
# process next input char if read will not block (either fd ready for
# input, or still chars in queue).
if (vec($fds_rd, $fno_rcv, 1) || scalar(@que_rcv)) {
my $dat = raw_rcv9_to(0.);
if (not defined $dat) {
print "pi_rri($curmode)-I: spurious select on rcv channel\n";
next;
} elsif ($dat == D9IDLE) {
next;
} elsif ($dat == D9ATTN) {
serv11_server_attn_get();
} else {
printf "pi_rri($curmode)-I: spurious char on server wait: %3.3x\n",
$dat;
next;
}
}
# process telnet sessions
foreach (@telfno2dsc) {
my $fno = $_->[0];
if (vec($fds_rd, $fno, 1)) {
my $teldsc = $_->[1];
telnet_readhdl($teldsc);
}
}
if ($serv11_attn_mask != 0) {
serv11_server_attn_dispatch(0);
}
if (scalar(@serv11_icbque)) {
my $icb = shift @serv11_icbque;
&{$icb->{rdmahdl}}($icb);
}
if ($serv11_attn_mask == 0 && $serv11_attn_seen) {
$serv11_attn_seen = 0;
serv11_server_attn_get();
}
}
$raw_timeout = $old_timeout;
$serv11_active = 0;
print "pi_rri($curmode)-I: leaving server mode\n";
}
#-------------------------------------------------------------------------------
sub serv11_server_attn_get {
my @rval;
my $rc;
serv11_rri_attn("attn");
$rc = serv11_rri_exec(\@rval);
my $mask_old = $serv11_attn_mask;
$serv11_attn_mask |= $rval[0]; # or-in new attn flags
if (exists $opts{tserv}) {
printf $fh_log "serv -- attn %s :", gconv_dat16($serv11_attn_mask, 2);
foreach my $adsc (@serv11_attntbl) {
my $msk = $adsc->[0];
my $ctl = $adsc->[2];
if ($serv11_attn_mask & $msk) {
my $pref = "";
my $suff = "";
if ($mask_old & $msk) { # old flags are in ()
$pref = "(";
$suff = ")";
}
printf $fh_log " %s%s%s", $pref, $ctl->{ctlname}, $suff;
}
}
printf $fh_log "\n";
}
}
#-------------------------------------------------------------------------------
sub serv11_server_attn_dispatch {
my ($force) = @_;
foreach my $adsc (@serv11_attntbl) {
my $msk = $adsc->[0];
my $hdl = $adsc->[1];
my $ctl = $adsc->[2];
if (($serv11_attn_mask & $msk) || $force) {
$serv11_attn_mask &= ~$msk;
&{$hdl}($ctl,$force);
}
}
}
#-------------------------------------------------------------------------------
sub serv11_probe_gen { # generic probe handler
my ($ctl) = @_;
my $mask = $ctl->{probemask};
my $addr = $ctl->{base};
$addr += $ctl->{csroff} if defined $ctl->{csroff};
my ($ival,$rval) = serv11_exec_probe($addr, $mask);
$ctl->{probe_ival} = $ival;
$ctl->{probe_rval} = $rval;
$ctl->{probe_ok} = 1;
$ctl->{probe_ok} = 0 if ($mask =~ /i/ && ! defined $ival);
$ctl->{probe_ok} = 0 if ($mask =~ /r/ && ! defined $rval);
}
#-------------------------------------------------------------------------------
sub serv11_init_gen { # generic controller init handler
my ($ctl) = @_;
if (exists $ctl->{usethdl}) {
foreach my $unitname (@{$ctl->{units}}) {
my $ucb = $serv11_unittbl{$unitname};
&{$ctl->{usethdl}}($ucb);
}
} else {
printf "pi_rri($curmode)-E: usethdl not defined for %s\n", $ctl->{ctlname};
}
}
#-------------------------------------------------------------------------------
sub serv11_detach_gen { # generic detach handler
my ($ucb) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if ($ucb->{att_ok}) {
my $fh = $ucb->{att_fh};
$fh->close() or die "Unexpected close error";
$ucb->{att_ok} = 0;
delete $ucb->{att_file};
delete $ucb->{att_nbyt};
delete $ucb->{att_nblk};
delete $ucb->{att_wpro};
delete $ucb->{att_fh};
delete $ucb->{att_eof};
&{$ctl->{usethdl}}($ucb); # setup unit registers
} else {
printf "pi_rri($curmode)-E: no file attached for %s\n", $ucb->{unitname};
}
}
#-------------------------------------------------------------------------------
sub serv11_attdet_disk { # generic disk att/det handler
my ($det,$ucb) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if ($det) { # detach handling
serv11_detach_gen($ucb);
} else { # attach handling
if (cget_opt("-w")) { # -w remount
return if $cmd_bad or cget_chkblank();
my $fh = $ucb->{att_fh};
if ($fh) { # mounted and open ?
if (-w $fh) { # file writable ?
$ucb->{att_wpro} = 0; # remove write protect
&{$ctl->{usethdl}}($ucb); # setup unit registers
} else {
printf "pi_rri($curmode)-E: file %s (for %s) is write protected\n",
$ucb->{att_file}, $ucb->{unitname};
return;
}
} else {
printf "pi_rri($curmode)-E: no file attached for %s\n",
$ucb->{unitname};
return;
}
} else { # normal (non -w) handling
my $opt_r = cget_opt("-r");
my $filename = cget_file();
return if $cmd_bad or cget_chkblank();
if (not -e $filename) {
print "pi_rri($curmode)-E: file $filename not found\n";
return;
}
if (not -r $filename) {
print "pi_rri($curmode)-E: file $filename is not readable\n";
return;
}
my $wpro = $opt_r;
if (! $wpro && ! -w $filename) {
print "pi_rri($curmode)-I: file $filename is write protected\n";
$wpro = 1;
}
my $filesize = -s $filename;
if (defined $ctl->{volsize}) {
if ($filesize < $ctl->{volsize}) {
printf "pi_rri($curmode)-W: dsk file too small, %s requires %d".
" file $filename has %d bytes\n",
$ucb->{unitname}, $ctl->{volsize}, $filesize;
}
}
my $fh = new FileHandle;
sysopen ($fh, $filename, $wpro ? O_RDONLY : O_RDWR)
or die "Unexpected sysopen error";
$ucb->{att_ok} = 1;
$ucb->{att_file} = $filename;
$ucb->{att_nbyt} = $filesize;
$ucb->{att_wpro} = $wpro;
$ucb->{att_fh} = $fh;
$ucb->{att_nblk} = int ($ucb->{att_nbyt}/512);
if ($ucb->{att_nbyt}%512 != 0) {
print "pi_rri($curmode)-I: size $filename not multiple of 512\n";
}
&{$ctl->{usethdl}}($ucb); # setup unit registers
}
}
}
#-------------------------------------------------------------------------------
sub serv11_attdet_ronly { # generic in only att/det handler
my ($det,$ucb) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if ($det) { # detach handling
serv11_detach_gen($ucb);
} else { # attach handling
my $filename = cget_file();
return if $cmd_bad or cget_chkblank();
if (not -e $filename) {
print "pi_rri($curmode)-E: file $filename not found\n";
return;
}
if (not -r $filename) {
print "pi_rri($curmode)-E: file $filename is not readable\n";
return;
}
my $fh = new FileHandle;
my $rc = $fh->open("<$filename");
if (not $rc) {
print "pi_rri($curmode)-E: failed to open file $filename\n";
return;
}
$ucb->{att_ok} = 1;
$ucb->{att_file} = $filename;
$ucb->{att_fh} = $fh;
delete $ucb->{att_eof};
&{$ctl->{usethdl}}($ucb); # setup unit registers
}
}
#-------------------------------------------------------------------------------
sub serv11_attdet_wonly { # generic out only att/det handler
my ($det,$ucb) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if ($det) { # detach handling
serv11_detach_gen($ucb);
} else { # attach handling
my $filename = cget_file();
return if $cmd_bad or cget_chkblank();
if (not -e $filename) {
print STDERR "pi_rri($curmode)-I: file $filename will be created\n";
} elsif (not -w $filename) {
print STDERR "pi_rri($curmode)-E: file $filename is not writeable\n";
return;
}
my $fh = new FileHandle;
my $rc = $fh->open(">$filename");
if (not $rc) {
print STDERR "pi_rri($curmode)-E: failed to open file $filename\n";
return;
}
autoflush $fh;
$ucb->{att_ok} = 1;
$ucb->{att_file} = $filename;
$ucb->{att_fh} = $fh;
delete $ucb->{att_eof};
&{$ctl->{usethdl}}($ucb); # setup unit registers
}
}
#-------------------------------------------------------------------------------
sub serv11_attdet_term { # generic term att/det handler
my ($det,$ucb) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if ($det) { # detach handling
my $port_str = $ucb->{att_port};
my $teldsc = $telnettbl{$port_str};
close $teldsc->{fh_data} if defined $teldsc->{fh_data};
close $teldsc->{fh_port} if defined $teldsc->{fh_port};
delete $telnettbl{$port_str};
delete $ucb->{att_port};
$ucb->{att_ok} = 0;
$serv11_fds_update = 1; # request update of select mask
} else { # attach handling
my $port = cget_gdat(16, 10);
return if $cmd_bad or cget_chkblank();
my $port_str = sprintf("%6.6d", $port);
if (exists $telnettbl{$port_str}) {
printf STDERR "pi_rri($curmode)-E: port %d already attached\n", $port;
return;
}
my $fh_port = new FileHandle;
my $proto = getprotobyname('tcp');
if (not socket($fh_port, PF_INET, SOCK_STREAM, $proto)) {
printf STDERR "pi_rri($curmode)-E: error in socket(): $!\n";
return;
}
if (not setsockopt($fh_port, SOL_SOCKET, SO_REUSEADDR, 1)) {
printf STDERR "pi_rri($curmode)-E: error in setsocketopt(): $!\n";
return;
}
my $host = pack('C4', 0,0,0,0);
my $addr = pack('S n a4 x8', 2, $port, $host);
if (not bind($fh_port, $addr)) {
printf STDERR "pi_rri($curmode)-E: error in bind(): $!\n";
return;
}
if (not listen($fh_port, 1)) {
printf STDERR "pi_rri($curmode)-E: error in listen(): $!\n";
return;
}
$telnettbl{$port_str} = {};
$telnettbl{$port_str}->{port} = $port;
$telnettbl{$port_str}->{state} = TELNET_STATE_LISTEN;
$telnettbl{$port_str}->{fh_port} = $fh_port;
$telnettbl{$port_str}->{ucb} = $ucb;
$ucb->{att_ok} = 1;
$ucb->{att_port} = $port_str;
$serv11_fds_update = 1; # request update of select mask
}
}
#-------------------------------------------------------------------------------
sub serv11_probe_cpu { # cpu: probe handler
my ($ctl) = @_;
serv11_probe_gen($ctl);
return unless $ctl->{probe_ok};
my $reglist = $ctl->{reglist};
my $partbl = $ctl->{partbl};
my $text = "";
my $exadep = \&serv11_exadep_cpu;
push @{$reglist}, {name => "r0", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "r1", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "r2", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "r3", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "r4", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "r5", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "sp", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "pc", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "psw", hdl => \&serv11_exadep_cpu};
push @{$reglist}, {name => "stklim", addr => CPU_STKLIM};
push @{$reglist}, {name => "pirq" , addr => CPU_PIRQ};
push @{$reglist}, {name => "mbrk" , addr => CPU_MBRK};
push @{$reglist}, {name => "cpuerr", addr => CPU_CPUERR};
push @{$reglist}, {name => "hisize", addr => CPU_HISIZE};
push @{$reglist}, {name => "losize", addr => CPU_LOSIZE};
my ($ival,$rval) = serv11_exec_probe(CPU_SDREG, "ir");
if (defined $ival && defined $rval) {
push @{$reglist}, {name => "sr", addr => CPU_SDREG, attr => REGATTR_RBWR};
push @{$reglist}, {name => "dr", addr => CPU_SDREG, attr => REGATTR_RBRD};
}
push @{$reglist}, {name => "mmr0" , addr => CPU_MMR0};
push @{$reglist}, {name => "mmr1" , addr => CPU_MMR1};
push @{$reglist}, {name => "mmr2" , addr => CPU_MMR2};
push @{$reglist}, {name => "mmr3" , addr => CPU_MMR3};
my @rval;
serv11_rri_wreg("wal", PDPCP_ADDR_AL, CPU_LOSIZE); # i/o page in 16 bit mode
serv11_rri_rreg("rm", PDPCP_ADDR_MEM);
my $rc = serv11_rri_exec(\@rval);
my $memsize = ($rval[0]+1)<<6; # memsize in bytes
$ctl->{memsize} = $memsize;
$text .= ($text)?";":"" . sprintf("mem=%dkb",$memsize/1024.);
$ctl->{probe_text} = $text;
}
#-------------------------------------------------------------------------------
sub serv11_attn_cpu { # cpu: attention handler
my ($ctl,$force) = @_;
return if $force;
print "CPU halted\n";
$serv11_active = 0;
serv11_cexec_shoreg(1);
}
#-------------------------------------------------------------------------------
sub serv11_exadep_cpu { # cpu: exa/dep handler
my ($dep,$dsc,$val) = @_;
my $name = $dsc->{name};
my $rrireg;
$name =~ s/^sp$/r6/;
$name =~ s/^pc$/r7/;
if ($dep) {
if ($name =~ /^r([0-7])$/) {
$rrireg = PDPCP_ADDR_R0 + int $1;
} elsif ($name eq "psw") {
$rrireg = PDPCP_ADDR_PSW;
} else {
print_fatal("serv11_exadep_cpu() called with bad name '$name'");
}
my @rval;
serv11_rri_wreg("r$name", $rrireg, $val);
my $rc = serv11_rri_exec(\@rval);
return;
} else {
if ($name =~ /^r([0-7])$/) {
$rrireg = PDPCP_ADDR_R0 + int $1;
} elsif ($name eq "psw") {
$rrireg = PDPCP_ADDR_PSW;
} else {
print_fatal("serv11_exadep_cpu() called with bad name '$name'");
}
my @rval;
serv11_rri_rreg("r$name", $rrireg);
my $rc = serv11_rri_exec(\@rval);
return $rval[0];
}
}
#-------------------------------------------------------------------------------
sub serv11_ichr_dl11 {
my ($ucb,$dref) = @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
my @rval;
my $rc;
my $que_old = scalar( @{$ucb->{rcvque}} );
push @{$ucb->{rcvque}}, @$dref;
my $que_new = scalar( @{$ucb->{rcvque}} );
if ($que_old == 0 && $que_new > 0) {
serv11_rri_ibrb($ctl);
serv11_rri_ribr("RCSR", $ctl, DL11_RCSR);
$rc = serv11_rri_exec(\@rval);
if (($rval[0] & DL11_RCSR_M_RDONE) == 0) { # RBUF not full
my $data = shift @{$ucb->{rcvque}};
serv11_rri_wibr("RBUF", $ctl, DL11_RBUF, $data & 0377);
$rc = serv11_rri_exec(\@rval);
}
}
}
#-------------------------------------------------------------------------------
sub serv11_attn_dl11 {
my ($ctl,$force) = @_;
my $ucb = $serv11_unittbl{$ctl->{units}[0]};
my @rval;
my $rc;
my $nxbuf = $ctl->{nxbuf_min};
my $nxbuf_val = 0;
$nxbuf = $ctl->{nxbuf} if defined $ctl->{nxbuf};
serv11_rri_ibrb($ctl);
for (my $i=0; $i<$nxbuf; $i++) {
serv11_rri_ribr("XBUF", $ctl, DL11_XBUF);
}
# select(undef, undef, undef, 5.0); # ! hack
# printf $fh_log "HACK -- wait on DL11 attn\n"; # ! hack
$rc = serv11_rri_exec(\@rval);
my $rrdy;
for (my $i=0; $i<$nxbuf; $i++) {
my $ochr = $rval[$i] & DL11_XBUF_M_XBUF;
my $xval = $rval[$i] & DL11_XBUF_M_XVAL;
$rrdy = $rval[$i] & DL11_XBUF_M_RRDY;
$ochr = $ochr & 0177 if $ucb->{rcv7bit}; # drop parity bit
my $chr = chr($ochr);
my $str = ($ochr>=040 && $ochr<0177) ? "$chr" : sprintf "\\%3.3o",$ochr;
if (exists $opts{tserv} && $ctl->{trace} &&
($xval || not $force)) {
printf $fh_log
"serv -- DL11.%s xbuf=%6.6o xval=%s rrdy=%s rcvq=%3d sndq=%3d",
$ctl->{ctlname}, $rval[$i],
($xval ? "y" : "n"), ($rrdy ? "y" : "n"),
scalar( @{$ucb->{rcvque}} ), scalar( @{$ucb->{sndque}} );
printf $fh_log " char=\"%s\"", $str if $xval;
print $fh_log "\n";
}
if ($xval) {
$nxbuf_val += 1;
my $sndqueref = $ucb->{sndque};
my $ochr_last = 0;
$ochr_last = $$sndqueref[-1] if scalar(@$sndqueref) > 0;
push @{$ucb->{sndque}}, $ochr;
if ($ucb->{att_ok}) {
telnet_writehdl($ucb);
} else {
if ($ctl->{ctlname} eq "TTA") { # for console
while (scalar( @{$ucb->{sndque}} )) {
my $byte = shift @{$ucb->{sndque}};
my $str = "";
if ($byte>=040 && $byte<0177) {
$str = chr($byte);
} elsif ($byte==011) {
$str = "\t";
} elsif ($byte==012) {
$str = "\n";
} elsif ($byte==015) {
$str = "\r";
} else {
$str = sprintf "<%3.3o>", $byte if $byte!=000;
}
print $str;
}
}
if ($ucb->{logfile}) {
my $fh = $ucb->{logfh};
if (not defined $ucb->{logfh}) {
my $logfile = $ucb->{logfile};
my $rc;
$fh = $ucb->{logfh} = new FileHandle;
$rc = $ucb->{logfh}->open(">$logfile");
if (not $rc) {
printf STDERR "pi_rri-E: failed to open $logfile for write\n";
$fh = undef;
} else {
autoflush $fh;
}
}
print $fh $str if $fh;
}
}
# if ($ochr_last == 015 && $ochr == 012) {
# while (scalar( @{$ucb->{sndque}} )) {
# my $byte = shift @{$ucb->{sndque}};
# my $chr = chr($byte);
# if ($byte>=040 && $byte <=177) {
# print $chr
# } else {
# if ($byte != 000 && $byte != 012 && $byte != 015) {
# printf "<%3.3o>", $byte
# }
# }
# }
# print "\n";
# }
}
}
if ($rrdy && scalar( @{$ucb->{rcvque}} ) ) {
my $data = shift @{$ucb->{rcvque}};
serv11_rri_wibr("RBUF", $ctl, DL11_RBUF, $data & 0377);
$rc = serv11_rri_exec(\@rval);
}
$ctl->{nxbuf} = next_nxbuf($ctl, $nxbuf, $nxbuf_val);
}
#-------------------------------------------------------------------------------
sub serv11_uset_lp11 {
my ($ucb) = @_;
my @rval;
my $lpcs = ($ucb->{att_ok}) ? 0 : LP11_CSR_M_ERR;
serv11_rri_uset($ucb, "LPCS", LP11_CSR, $lpcs);
my $rc = serv11_rri_exec(\@rval);
}
#-------------------------------------------------------------------------------
sub serv11_attn_lp11 {
my ($ctl,$force) = @_;
my $ucb = $serv11_unittbl{$ctl->{units}[0]};
my @rval;
my $rc;
my $nxbuf = $ctl->{nxbuf_min};
my $nxbuf_val = 0;
$nxbuf = $ctl->{nxbuf} if defined $ctl->{nxbuf};
serv11_rri_ibrb($ctl);
for (my $i=0; $i<$nxbuf; $i++) {
serv11_rri_ribr("LPBU", $ctl, LP11_BUF);
}
$rc = serv11_rri_exec(\@rval);
for (my $i=0; $i<$nxbuf; $i++) {
my $ochr = $rval[$i] & LP11_BUF_M_BUF;
my $oval = $rval[$i] & LP11_BUF_M_VAL;
my $chr = chr($ochr);
my $str = "$chr";
if (exists $opts{tserv} && $ctl->{trace} &&
($oval || not $force)) {
printf $fh_log
"serv -- LP11 buf=%6.6o val=%s ",
$rval[$i], ($oval ? "y" : "n");
printf $fh_log " char=\"%s\"", $str if $oval;
print $fh_log "\n";
}
if ($oval) {
$nxbuf_val += 1;
my $fh = $ucb->{att_fh};
if ($fh) {
print $fh $str;
} else {
printf STDERR "pi_rri($curmode)-E: spurious output '%s' for %s\n",
$str, $ucb->{unitname};
}
}
}
$ctl->{nxbuf} = next_nxbuf($ctl, $nxbuf, $nxbuf_val);
}
#-------------------------------------------------------------------------------
sub serv11_uset_pc11 {
my ($ucb) = @_;
my @rval;
my $text;
my $addr;
my $data;
if ($ucb->{unitname} eq "PTR") { # if reader
$text = "PRCS";
$addr = PC11_RCSR;
$data = ($ucb->{att_ok}) ? 0 : PC11_RCSR_M_ERR;
} else { # if puncher
$text = "PPCS";
$addr = PC11_PCSR;
$data = ($ucb->{att_ok}) ? 0 : PC11_PCSR_M_ERR;
}
serv11_rri_uset($ucb, $text, $addr, $data);
my $rc = serv11_rri_exec(\@rval);
}
#-------------------------------------------------------------------------------
sub serv11_attdet_pc11 { # pc11 att/det handler
my ($det,$ucb) = @_;
if ($ucb->{unitname} eq "PTR") { # if reader
serv11_attdet_ronly($det, $ucb); # use read-only file
} else { # if puncher
serv11_attdet_wonly($det, $ucb); # use write-only file
}
}
#-------------------------------------------------------------------------------
sub serv11_attn_pc11 {
my ($ctl,$force) = @_;
my $ucb_ptr = $serv11_unittbl{$ctl->{units}[0]};
my $ucb_ptp = $serv11_unittbl{$ctl->{units}[1]};
my @rval;
my $rc;
my $nxbuf = $ctl->{nxbuf_min};
my $nxbuf_val = 0;
$nxbuf = $ctl->{nxbuf} if defined $ctl->{nxbuf};
serv11_rri_ibrb($ctl);
for (my $i=0; $i<$nxbuf; $i++) {
serv11_rri_ribr("PPBUF", $ctl, PC11_PBUF);
}
$rc = serv11_rri_exec(\@rval);
my $rrdy;
for (my $i=0; $i<$nxbuf; $i++) {
my $ochr = $rval[$i] & PC11_PBUF_M_PBUF;
my $pval = $rval[$i] & PC11_PBUF_M_PVAL;
$rrdy = $rval[$i] & PC11_PBUF_M_RBUSY;
if (exists $opts{tserv} && $ctl->{trace} &&
($pval || not $force)) {
printf $fh_log
"serv -- PC11 pbuf=%6.6o pval=%s rrdy=%s \n",
$rval[$i], ($pval ? "y" : "n"), ($rrdy ? "y" : "n");
}
if ($pval) {
$nxbuf_val += 1;
my $fh = $ucb_ptp->{att_fh};
if ($fh) {
print $fh chr($ochr);
} else {
printf STDERR "pi_rri($curmode)-E: spurious output '%3.3o' for %s\n",
$ochr, $ucb_ptp->{unitname};
}
}
}
if ($rrdy) {
my $fh = $ucb_ptr->{att_fh};
if ($fh && (not $ucb_ptr->{att_eof}) ) {
my $char = getc($fh);
if (defined $char) {
serv11_rri_wibr("PRBUF", $ctl, PC11_RBUF, ord($char) & 0377);
$rc = serv11_rri_exec(\@rval);
} else {
serv11_rri_uset($ucb_ptr, "PRCS", PC11_RCSR, PC11_RCSR_M_ERR);
$rc = serv11_rri_exec(\@rval);
$ucb_ptr->{att_eof} = 1;
}
} else {
printf STDERR "pi_rri($curmode)-E: spurious reader busy for %s\n",
$ucb_ptr->{unitname};
}
}
$ctl->{nxbuf} = next_nxbuf($ctl, $nxbuf, $nxbuf_val);
}
#-------------------------------------------------------------------------------
sub serv11_uset_rk11 {
my ($ucb) = @_;
my @rval;
my $rkds = 0;
$rkds = $ucb->{ctlunit}<<(RKDS_V_ID);
if ($ucb->{att_ok}) { # drive available
$rkds |= RKDS_M_HDEN; # always high density
$rkds |= RKDS_M_SOK; # always sector counter OK ?FIXME?
$rkds |= RKDS_M_DRY; # drive available
$rkds |= RKDS_M_ADRY; # access available
$rkds |= RKDS_M_WPS if $ucb->{att_wpro}; # in case write protected
}
$ucb->{rkds} = $rkds;
serv11_rri_uset($ucb, "RKDS", RK11_RKDS, $rkds);
my $rc = serv11_rri_exec(\@rval);
}
#-------------------------------------------------------------------------------
# geometry: c=203;h=2;s=12 ==> 4872 blocks ==> 2 494 464 bytes
#
# several error conditions are only approximately handled:
# OVR: when detected, no transfer done (should trim size)
sub serv11_attn_rk11 {
my ($ctl,$force) = @_;
my @rval;
my $blksize = $ctl->{blksize};
serv11_rri_ibrb($ctl);
serv11_rri_ribr("RKWC", $ctl, RK11_RKWC);
serv11_rri_ribr("RKBA", $ctl, RK11_RKBA);
serv11_rri_ribr("RKDA", $ctl, RK11_RKDA);
serv11_rri_ribr("RKMR", $ctl, RK11_RKMR); # read to monitor CRDONE
serv11_rri_ribr("RKCS", $ctl, RK11_RKCS);
my $rc = serv11_rri_exec(\@rval);
my $rkwc = $rval[0];
my $rkba = $rval[1];
my $rkda = $rval[2];
my $rkmr = $rval[3];
my $rkcs = $rval[4];
my $se = $rkda & RKDA_B_SC;
my $hd = ($rkda>>RKDA_V_SUR ) & RKDA_B_SUR;
my $cy = ($rkda>>RKDA_V_CYL ) & RKDA_B_CYL;
my $dr = ($rkda>>RKDA_V_DRSEL) & RKDA_B_DRSEL;
my $go = ($rkcs & RKCS_M_GO) != 0;
my $fu = ($rkcs>>RKCS_V_FUNC) & RKCS_B_FUNC;
my $mex = ($rkcs>>RKCS_V_MEX ) & RKCS_B_MEX;
my $nwrd = ((~$rkwc) & 0xffff) + 1; # transfer size in words
my $nbyt = 2*$nwrd; # transfer size in bytes
my $nblk = int (($nbyt+$blksize-1)/$blksize);# transfer size in blocks
my $addr = $mex<<16 | $rkba; # 18 bit memory address
my $lbn = $se + RK11_NUMSE*$hd + RK11_NUMSE*RK11_NUMHD*$cy;
my $ucb = $serv11_unittbl{$ctl->{units}[$dr]};
my $rkds = $ucb->{rkds};
if (not defined $rkds) {
printf $fh_log
"serv -- RK11 ERROR: no rri device init, assume ds=0 for drive %d\n", $dr;
$rkds = $ucb->{rkds} = $rkds = 0;
}
if ($go == 0) { # quit here if no go bit set
if (exists $opts{tserv} && $ctl->{trace}) {
if (not $force) {
printf $fh_log "serv -- RK11 cs=%6.6o go=0, spurious attn\n", $rkcs;
}
}
return;
}
my $rker = 0;
my $msg = "";
if ($fu != RKCS_CRESET && # function not control reset
(not $ucb->{att_ok})) { # and drive not attached
$rker = RKER_M_NXD; # --> abort with NXD error
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
if ($fu == RKCS_SEEK || $fu == RKCS_DRESET) {
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_SBCLR) | 1<<($dr));
}
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} elsif ($fu != RKCS_WRITE && # function neither read
$fu != RKCS_READ && # nor write
($rkcs & RKCS_M_FMT)) { # and FMT set
$rker = RKER_M_PGE; # --> abort with PGE error
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
if ($fu == RKCS_SEEK || $fu == RKCS_DRESET) {
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_SBCLR) | 1<<($dr));
}
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} else {
if ($fu == RKCS_CRESET) { # Control reset -------------------
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_CRESET));
} elsif ($fu == RKCS_WRITE) { # Write ---------------------------
# Note: WRITE+FMT is just like WRITE
$rker |= RKER_M_NXS if $se >= RK11_NUMSE;
$rker |= RKER_M_NXC if $cy >= RK11_NUMCY;
$rker |= RKER_M_WLO if $ucb->{att_wpro};
$rker |= RKER_M_DRE if $rkcs & RKCS_M_IBA; # not yet supported ! FIXME !
$rker |= RKER_M_DRE if $rkcs & RKCS_M_RWA; # will never be supported
if ($rker) {
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} else {
my $icb = {rdmahdl => \&serv11_rdma_rk11,
func => "write",
ctl => $ctl,
ucb => $ucb,
lbn => $lbn,
nblk => $nblk,
nwrd => $nwrd,
addr => $addr,
nwdone => 0,
rkcs => $rkcs, # later needed for MEX update
rkda => $rkda # later needed in RKDA update
};
push @serv11_icbque, $icb;
}
} elsif ($fu == RKCS_READ) { # Read ----------------------------
$rker |= RKER_M_NXS if ($se >= RK11_NUMSE);
$rker |= RKER_M_NXC if ($cy >= RK11_NUMCY);
$rker |= RKER_M_DRE if $rkcs & RKCS_M_IBA; # not yet supported ! FIXME !
$rker |= RKER_M_DRE if $rkcs & RKCS_M_RWA; # will never be supported
if ($rker) {
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} else {
if ($rkcs & RKCS_M_FMT) {
my $icb = {rdmahdl => \&serv11_rdma_rk11,
func => "rdfmt",
ctl => $ctl,
ucb => $ucb,
lbn => $lbn,
nblk => $nwrd, # #blocks == #words for RD FMT !!
nwrd => $nwrd,
addr => $addr,
nwdone => 0,
rkcs => $rkcs, # later needed for MEX update
rkda => $rkda # later needed in RKDA update
};
push @serv11_icbque, $icb;
} else {
my $icb = {rdmahdl => \&serv11_rdma_rk11,
func => "read",
ctl => $ctl,
ucb => $ucb,
lbn => $lbn,
nblk => $nblk,
nwrd => $nwrd,
addr => $addr,
nwdone => 0,
rkcs => $rkcs, # later needed for MEX update
rkda => $rkda # later needed in RKDA update
};
push @serv11_icbque, $icb;
}
}
} elsif ($fu == RKCS_WCHK) { # Write Check ---------------------
$rker |= RKER_M_NXS if $se >= RK11_NUMSE;
$rker |= RKER_M_NXC if $cy >= RK11_NUMCY;
$rker |= RKER_M_DRE if $rkcs & RKCS_M_IBA; # not yet supported ! FIXME !
$rker |= RKER_M_DRE if $rkcs & RKCS_M_RWA; # will never be supported
if ($rker) {
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} else {
my $icb = {rdmahdl => \&serv11_rdma_rk11,
func => "wrcheck",
ctl => $ctl,
ucb => $ucb,
lbn => $lbn,
nblk => $nblk,
nwrd => $nwrd,
addr => $addr,
nwdone => 0,
rkcs => $rkcs, # later needed for MEX update
rkda => $rkda # later needed in RKDA update
};
push @serv11_icbque, $icb;
}
} elsif ($fu == RKCS_SEEK) { # Seek ----------------------------
$rker |= RKER_M_NXS if ($se >= RK11_NUMSE);
$rker |= RKER_M_NXC if ($cy >= RK11_NUMCY);
if ($rker) {
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_SBCLR) | 1<<($dr));
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} else {
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
$rkds &= ~(RKDS_B_SC); # replace current sector number
$rkds |= $se;
$ucb->{rkds} = $rkds;
serv11_rri_wibr("RKDS", $ctl, RK11_RKDS, $rkds);
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<($dr)); # issue seek done
}
} elsif ($fu == RKCS_RCHK) { # Read Check ----------------------
$rker |= RKER_M_NXS if $se >= RK11_NUMSE;
$rker |= RKER_M_NXC if $cy >= RK11_NUMCY;
$rker |= RKER_M_DRE if $rkcs & RKCS_M_IBA; # not yet supported ! FIXME !
$rker |= RKER_M_DRE if $rkcs & RKCS_M_RWA; # will never be supported
if ($rker) {
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker);
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker);
} else {
my $icb = {rdmahdl => \&serv11_rdma_rk11,
func => "rdcheck",
ctl => $ctl,
ucb => $ucb,
lbn => $lbn,
nblk => $nblk,
nwrd => $nwrd,
addr => $addr,
nwdone => 0,
rkcs => $rkcs, # later needed for MEX update
rkda => $rkda # later needed in RKDA update
};
push @serv11_icbque, $icb;
}
} elsif ($fu == RKCS_DRESET) { # Drive Reset ---------------------
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<($dr)); # issue seek done
} elsif ($fu == RKCS_WLOCK) { # Write Lock ----------------------
$ucb->{rkds} |= RKDS_M_WPS; # set RKDS write protect flag
$ucb->{att_wpro} = 1; # set UCB write protect flag
serv11_rri_wibr("RKDS", $ctl, RK11_RKDS, $ucb->{rkds});
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
}
}
if (exists $opts{tserv} && $ctl->{trace}) {
printf $fh_log "serv -- RK11 cs=%6.6o da=%6.6o wc=%6.6o",
$rkcs, $rkda, $rkwc;
printf $fh_log " ad=%6.6o", $addr;
printf $fh_log " fu=%d dchs=%d,%3d,%d,%2d", $fu, $dr, $cy, $hd, $se;
printf $fh_log " lbn=%4d nw,nb=%5d,%2d", $lbn, $nwrd, $nblk;
print $fh_log "\n";
}
$rc = serv11_rri_exec(\@rval);
}
#-------------------------------------------------------------------------------
sub serv11_attn_rk11_logerr {
my ($ctl,$rker) = @_;
if (exists $opts{tserv}) {
printf $fh_log "serv -- RK11 er=%6.6o ERROR ABORT\n", $rker;
}
}
#-------------------------------------------------------------------------------
sub serv11_rdma_rk11 {
my ($icb) = @_;
my $ctl = $icb->{ctl};
my $ucb = $icb->{ucb};
my $addr = $icb->{addr};
my $blksize = $ctl->{blksize};
my @rval;
my $rc = 1; # default ok, make code below shorter FIXME
my $rker = 0;
# printf "+++x1 func=%5s addr=%6.6o nblk=%2d nwdone=%3d\n",
# $icb->{func}, $addr, $icb->{nblk}, $icb->{nwdone};
if ($icb->{func} eq "read") { # --- read function --------------
if ($icb->{nwdone} == 0) { # new block ?
$rker |= RKER_M_OVR if $icb->{lbn} >= RK11_NUMBL;
$rc = serv11_icb_disk_read($icb) if not $rker;
$rker |= RKER_M_CSE if not $rc; # forward disk I/O error
}
if (not $rker) {
my $nwdma = int($blksize/2) - $icb->{nwdone};
$nwdma = $icb->{nwrd} if $nwdma > $icb->{nwrd};
$nwdma = $serv11_rdma_chunk if $nwdma > $serv11_rdma_chunk;
my $beg = $icb->{nwdone};
my $end = $beg + $nwdma - 1;
my $buf = $icb->{buf};
serv11_rri_lalh($icb->{addr}, 3);
serv11_rri_wblk($nwdma, [ @$buf[$beg..$end] ]);
$rc = serv11_rri_exec(\@rval); # FIXME: handle dma I/O error
$stat_tab{rdisk} += 2*$nwdma;
$icb->{nwdone} += $nwdma;
$icb->{nwrd} -= $nwdma;
$icb->{addr} += 2*$nwdma;
if ((not $rker) && # no error and
($icb->{nwdone} == int($blksize/2) || # block done or
$icb->{nwrd} == 0) ) { # all done
$icb->{nwdone} = 0;
$icb->{lbn} += 1;
$icb->{nblk} -= 1;
}
}
if ((not $rker) && $icb->{nwrd}) { # if no error found and not done yet
push @serv11_icbque, $icb; # requeue
return;
}
} elsif ($icb->{func} eq "rdfmt") { # --- read format function -------
$rker |= RKER_M_OVR if $icb->{lbn} >= RK11_NUMBL;
if (not $rker) {
my $cy = $icb->{lbn}/(RK11_NUMHD*RK11_NUMSE);
my $da = $cy<<(RKDA_V_CYL);
my @buf = ($da);
serv11_rri_lalh($icb->{addr}, 3);
serv11_rri_wblk(1, [ @buf ]);
$rc = serv11_rri_exec(\@rval); # FIXME: handle dma I/O error
$stat_tab{rdisk} += 2;
$icb->{nwrd} -= 1;
$icb->{addr} += 2;
$icb->{lbn} += 1;
$icb->{nblk} -= 1;
}
if ((not $rker) && $icb->{nwrd}) { # if no error found and not done yet
push @serv11_icbque, $icb; # requeue
return;
}
} elsif ($icb->{func} eq "write") { # --- write function -------------
$icb->{buf} = [] if $icb->{nwdone} == 0;
my $nwdma = int($blksize/2) - $icb->{nwdone};
$nwdma = $icb->{nwrd} if $nwdma > $icb->{nwrd};
$nwdma = $serv11_rdma_chunk if $nwdma > $serv11_rdma_chunk;
serv11_rri_lalh($icb->{addr}, 3);
serv11_rri_rblk($nwdma);
$rc = serv11_rri_exec(\@rval); # FIXME: handle dma I/O error
$stat_tab{wdisk} += 2*$nwdma;
$icb->{nwdone} += $nwdma;
$icb->{nwrd} -= $nwdma;
$icb->{addr} += 2*$nwdma;
push @{$icb->{buf}}, @{$rval[0]};
if ((not $rker) && # no error and
($icb->{nwdone} == int($blksize/2) || # block done or
$icb->{nwrd} == 0) ) { # all done
$rc = serv11_icb_disk_write($icb); # FIXME: handle file I/O error
$icb->{nwdone} = 0;
$icb->{lbn} += 1;
$icb->{nblk} -= 1;
$rker |= RKER_M_OVR if $icb->{nblk} && $icb->{lbn} >= RK11_NUMBL;
}
if ((not $rker) && $icb->{nwrd}) { # if no error found and not done yet
push @serv11_icbque, $icb; # requeue
return;
}
} elsif ($icb->{func} eq "wrcheck") { # --- write check function -------
if ($icb->{nwdone} == 0) { # new block ?
$rker |= RKER_M_OVR if $icb->{lbn} >= RK11_NUMBL;
$rc = serv11_icb_disk_read($icb) if not $rker;
$rker |= RKER_M_CSE if not $rc; # forward disk I/O error
if ((not $rker)) {
$icb->{bufdsk} = $icb->{buf};
$icb->{buf} = [];
}
}
my $nwdma = int($blksize/2) - $icb->{nwdone};
$nwdma = $icb->{nwrd} if $nwdma > $icb->{nwrd};
$nwdma = $serv11_rdma_chunk if $nwdma > $serv11_rdma_chunk;
serv11_rri_lalh($icb->{addr}, 3);
serv11_rri_rblk($nwdma);
$rc = serv11_rri_exec(\@rval); # FIXME: handle dma I/O error
$stat_tab{wdisk} += 2*$nwdma;
$icb->{nwdone} += $nwdma;
$icb->{nwrd} -= $nwdma;
$icb->{addr} += 2*$nwdma;
push @{$icb->{buf}}, @{$rval[0]};
if ((not $rker) && # no error and
($icb->{nwdone} == int($blksize/2) || # block done or
$icb->{nwrd} == 0)) { # all done
my $bufdsk = $icb->{bufdsk};
my $bufmem = $icb->{buf};
my $nwmem = scalar(@{$bufmem});
for (my $i=0; $i<$nwmem; $i++) {
$rker |= RKER_M_WCE if $bufdsk->[$i] != $bufmem->[$i];
}
$icb->{nwdone} = 0;
$icb->{lbn} += 1;
$icb->{nblk} -= 1;
$rker |= RKER_M_OVR if $icb->{nblk} && $icb->{lbn} >= RK11_NUMBL;
}
my $stop = ($rker & ~RKER_M_WCE) != 0 || # any hard error
(($rker & RKER_M_WCE) && $icb->{rkcs} & RKCS_M_SSE);
if ((not $stop) && $icb->{nwrd}) { # if no error found and not done yet
push @serv11_icbque, $icb; # requeue
return;
}
} elsif ($icb->{func} eq "rdcheck") { # --- read check function --------
$rker |= RKER_M_OVR if $icb->{lbn} >= RK11_NUMBL;
if (not $rker) {
my $nwdma = int($blksize/2);
$nwdma = $icb->{nwrd} if $nwdma > $icb->{nwrd};
# Note: rkwc is decremented; rkba is untouched, no DMA transfer done
$icb->{nwrd} -= $nwdma;
$icb->{lbn} += 1;
$icb->{nblk} -= 1;
}
if ((not $rker) && $icb->{nwrd}) { # if no error found and not done yet
push @serv11_icbque, $icb; # requeue
return;
}
} else { # --- unkown function ------------
printf "pi_rri-E: unknown func=%s for serv11_rdma_rk11\n", $icb->{func};
}
# common handling for dma transfer completion
my $ba = $icb->{addr} &0177776; # get lower 16 bits
my $mex = ($icb->{addr} >> 16) & 03; # get upper 2 bits
my $lbn = $icb->{lbn};
my $nwrd = $icb->{nwrd};
my $end = $lbn;
my $se = $end % RK11_NUMSE;
$end = int ($end / RK11_NUMSE);
my $hd = $end % RK11_NUMHD;
$end = int ($end / RK11_NUMHD);
my $cy = $end;
my $da = ($icb->{rkda} & RKDA_M_DRSEL) |
$se | $hd<<(RKDA_V_SUR) | $cy<<(RKDA_V_CYL);
my $cs = ($icb->{rkcs} & (~RKCS_M_MEX)) | ($mex << RKCS_V_MEX);
serv11_rri_ibrb($ctl);
serv11_rri_wibr("RKER", $ctl, RK11_RKER, $rker) if $rker;
serv11_rri_wibr("RKWC", $ctl, RK11_RKWC, (-$nwrd)&0177777);
serv11_rri_wibr("RKBA", $ctl, RK11_RKBA, $ba);
serv11_rri_wibr("RKDA", $ctl, RK11_RKDA, $da);
serv11_rri_wibr("RKCS", $ctl, RK11_RKCS, $cs) if ($cs != $icb->{rkcs});
serv11_rri_wibr("RKMR", $ctl, RK11_RKMR, 1<<(RKMR_V_FDONE));
serv11_attn_rk11_logerr($ctl, $rker) if $rker;
$rc = serv11_rri_exec(\@rval);
}
#-------------------------------------------------------------------------------
# read one disk block at lbn, returns $icb->{buf}
sub serv11_icb_disk_read { # read one dsk file block
my ($icb) = @_;
my $ucb = $icb->{ucb};
my $ctl = $icb->{ctl};
my $fh = $ucb->{att_fh};
my $fsize = $ucb->{att_nbyt};
my $lbn = $icb->{lbn};
my $blksize = $ctl->{blksize};
my $seekpos = $lbn*$blksize;
my $sysbuf;
my $msg = "";
my $rc = 0;
$icb->{buf} = undef;
if ($seekpos < $fsize) {
($rc,$sysbuf) = file_seek_read($fh, $seekpos, $blksize);
$icb->{buf} = conv_buf2wlist($sysbuf);
} else {
$rc = $blksize; # setup good rc
$msg = " past eof zero buf";
$icb->{buf} = [];
while ($blksize > 0) {
push @{$icb->{buf}}, 0;
$blksize -= 2;
}
}
if (exists $opts{tserv} && $ctl->{trace}) {
printf $fh_log "disk -- %3s read lbn=%5d rc=%d%s\n",
$ucb->{unitname}, $lbn, $rc, $msg;
}
return $rc;
}
#-------------------------------------------------------------------------------
# write one disk block at lbn, takes data from $icb->{buf}
sub serv11_icb_disk_write { # write one dsk file block
my ($icb) = @_;
my $ucb = $icb->{ucb};
my $ctl = $icb->{ctl};
my $fh = $ucb->{att_fh};
my $fsize = $ucb->{att_nbyt};
my $lbn = $icb->{lbn};
my $blksize = $ctl->{blksize};
my $seekpos = $lbn*$blksize;
my $sysbuf = "";
my $rc;
if (scalar(@{$icb->{buf}}) > int($blksize/2)) {
print_fatal "serv11_icb_disk_write: buf too long";
}
while (scalar(@{$icb->{buf}}) < int($blksize/2)) { # zero pad to block size
push @{$icb->{buf}}, 0;
}
if ($fsize <= $seekpos) { # extend dsk file ?
my $zerobuf = chr(0) x $blksize;
my $cnt = 0;
file_seek($fh, $fsize);
while ($fsize <= $seekpos) {
file_write($fh, $zerobuf);
$fsize += $blksize;
$cnt += 1;
}
if (exists $opts{tserv} && $ctl->{trace}) {
printf $fh_log "disk -- %3s extended by %d blocks\n",
$ucb->{unitname}, $cnt;
}
$ucb->{att_nbyt} = $fsize;
}
$sysbuf = conv_wlist2buf($icb->{buf});
$rc = file_seek_write($fh, $seekpos, $sysbuf);
if (exists $opts{tserv} && $ctl->{trace}) {
printf $fh_log "disk -- %3s write lbn=%5d rc=%d\n",
$ucb->{unitname}, $lbn, $rc;
}
return $rc;
}
#-------------------------------------------------------------------------------
sub serv11_rri_init { # issue rri init command
my ($aname,$addr,$data) = @_;
push @rri_cmdlist, {cname => "init",
aname => $aname,
addr => $addr,
data => $data,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
return undef;
}
#-------------------------------------------------------------------------------
sub serv11_rri_attn { # issue rri attn command
my ($aname) = @_;
push @rri_cmdlist, {cname => "attn",
aname => $aname,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
$rri_cmdlist[$#rri_cmdlist]->{get_data} = 1;
return $rri_rvalcnt++;
}
#-------------------------------------------------------------------------------
sub serv11_rri_stat { # issue rri stat command
my ($aname) = @_;
push @rri_cmdlist, {cname => "stat",
aname => $aname,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
$rri_cmdlist[$#rri_cmdlist]->{get_data} = 1;
return $rri_rvalcnt++;
}
#-------------------------------------------------------------------------------
sub serv11_rri_rreg { # issue rri rreg command
my ($aname,$addr) = @_;
push @rri_cmdlist, {cname => "rreg",
aname => $aname,
addr => $addr,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
$rri_cmdlist[$#rri_cmdlist]->{get_data} = 1;
return $rri_rvalcnt++;
}
#-------------------------------------------------------------------------------
sub serv11_rri_wreg { # issue rri wreg command
my ($aname,$addr,$data) = @_;
push @rri_cmdlist, {cname => "wreg",
aname => $aname,
addr => $addr,
data => $data,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
return undef;
}
#-------------------------------------------------------------------------------
sub serv11_rri_rblk { # issue rri rblk command
my ($nblk) = @_;
push @rri_cmdlist, {cname => "rblk",
aname => "brm",
addr => PDPCP_ADDR_MEMI,
nblk => $nblk,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
$rri_cmdlist[$#rri_cmdlist]->{get_rblk} = 1;
return $rri_rvalcnt++;
}
#-------------------------------------------------------------------------------
sub serv11_rri_wblk { # issue rri wblk command
my ($nblk,$dref) = @_;
push @rri_cmdlist, {cname => "wblk",
aname => "bwm",
addr => PDPCP_ADDR_MEMI,
nblk => $nblk,
dat_wblk => $dref,
ref_stat => $rri_ref_sdef,
msk_stat => $rri_msk_sdef};
return undef;
}
#-------------------------------------------------------------------------------
sub serv11_rri_lalh { # issue pdpcp lal and lah commands
my ($addr,$mode) = @_;
serv11_rri_wreg("wal", PDPCP_ADDR_AL, $addr&0xffff); # lower 16 bits
if ($mode>=1 and $mode<=3) {
my $data = ($addr>>16)&0x3f | $mode<<6;
serv11_rri_wreg("wah", PDPCP_ADDR_AH, $data); # upper 6 bits
}
if ($mode<0 or $mode>3) {
print STDERR "pi_rri($curmode)-E: bad mode $mode in serv11_exec_rblk()\n";
}
}
#-------------------------------------------------------------------------------
sub serv11_rri_ibrb { # issue rbus set base address
my ($ctl) = @_;
serv11_rri_wreg("wibrb", PDPCP_ADDR_IBRB, $ctl->{ibrb});
return undef;
}
#-------------------------------------------------------------------------------
sub serv11_rri_ribr { # issue rbus read
my ($aname,$ctl,$off) = @_;
my $ibroff = $ctl->{base} + $off - $ctl->{ibrb};
return serv11_rri_rreg($aname, PDPCP_ADDR_IBR+int($ibroff/2));
}
#-------------------------------------------------------------------------------
sub serv11_rri_wibr { # issue rbus write
my ($aname,$ctl,$off,$data) = @_;
my $ibroff = $ctl->{base} + $off - $ctl->{ibrb};
return serv11_rri_wreg($aname, PDPCP_ADDR_IBR+int($ibroff/2), $data);
}
#-------------------------------------------------------------------------------
sub serv11_rri_clear {
@rri_cmdlist = ();
$rri_rvalcnt = 0;
}
#-------------------------------------------------------------------------------
sub serv11_rri_exec {
my ($dref) = @_;
my $rc = 0;
return $rc if scalar(@rri_cmdlist) == 0;
rri_cmdlist_exec(\@rri_cmdlist);
$rc = rri_cmdlist_check_stat(\@rri_cmdlist);
if ($rc) {
print "pi_rri($curmode)-E: serv11_rri_exec error - dump follows\n";
if (exists $opts{log} && $opts{log} ne "") {
print $fh_log "pi_rri($curmode)-E: serv11_rri_exec error - dump follows\n";
}
}
if ($rc || exists $opts{dserv}) {
rri_cmdlist_dump(\@rri_cmdlist, 0, $fh_log);
}
@{$dref} = ();
foreach my $ele (@rri_cmdlist) {
push @{$dref}, $ele->{rcv_data} if $ele->{get_data};
push @{$dref}, $ele->{rcv_rblk} if $ele->{get_rblk};
}
@rri_cmdlist = ();
$rri_rvalcnt = 0;
return $rc;
}
#-------------------------------------------------------------------------------
sub serv11_rri_uset { # issue rbus uset writes
my $ucb = shift @_;
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
my $first = 1;
while (scalar(@_)) {
my $text = shift @_;
my $addr = shift @_;
my $data = shift @_;
my $key = "uset_" . $text;
if ((not defined $ctl->{$key}) || $ctl->{$key} != $data) {
serv11_rri_ibrb($ctl) if $first;
$first = 0;
serv11_rri_wibr($text, $ctl, $addr, $data);
$ctl->{$key} = $data;
if (exists $opts{tserv} && $ctl->{trace}) {
printf $fh_log "uset -- %s %s %6.6o\n",
$ctl->{ctltype}, $ucb->{unitname}, $data;
}
}
}
}
#-------------------------------------------------------------------------------
sub serv11_exec_rblk {
my ($addr,$mode,$dref,$nword) = @_;
my @rval;
serv11_rri_lalh($addr,$mode);
while ($nword>0) {
my $nblk = $nword;
$nblk = 256 if $nblk > 256;
$nword -= $nblk;
my $idref = serv11_rri_rblk($nblk);
my $rc = serv11_rri_exec(\@rval);
return $rc if $rc;
push @$dref, @{$rval[$idref]};
}
return 0;
}
#-------------------------------------------------------------------------------
sub serv11_exec_wblk {
my ($addr,$mode,$dref) = @_;
my @rval;
serv11_rri_lalh($addr,$mode);
my $nword = scalar(@$dref);
my $offset = 0;
if ($nword == 0) {
print "pi_rri($curmode)-W: spurious serv11_exec_wblk() with 0 data length\n";
return;
}
while ($nword>0) {
my $nblk = $nword;
$nblk = 256 if $nblk > 256;
my $beg = $offset;
my $end = $offset+$nblk-1;
serv11_rri_wblk($nblk, [ @$dref[$beg..$end] ]);
$nword -= $nblk;
$offset += $nblk;
my $rc = serv11_rri_exec(\@rval);
return $rc if $rc;
}
return 0;
}
#-------------------------------------------------------------------------------
sub serv11_exec_probe {
my ($addr,$mode) = @_;
my $iib;
my $irb;
if ($mode =~ /i/) {
serv11_rri_wreg("wal", PDPCP_ADDR_AL, $addr); # i/o page in 16 bit mode
serv11_rri_rreg("rm", PDPCP_ADDR_MEM);
$iib = $#rri_cmdlist;
}
if ($mode =~ /r/) {
my $ibrbase = $addr & ~(077); # ibr-base => drop last 6 bits
my $ibroff = $addr & (077); # ibr-offset => take last 6 bits
serv11_rri_wreg("wibrb", PDPCP_ADDR_IBRB, $ibrbase);
serv11_rri_rreg("ribr", PDPCP_ADDR_IBR + int($ibroff/2));
$irb = $#rri_cmdlist;
}
rri_cmdlist_exec(\@rri_cmdlist);
rri_cmdlist_dump(\@rri_cmdlist, 0) if exists $opts{dserv};
my $ival;
my $rval;
if (defined $iib) {
$ival =$rri_cmdlist[$iib]->{rcv_data} if not $rri_cmdlist[$iib]->{err_stat};
}
if (defined $irb) {
$rval =$rri_cmdlist[$irb]->{rcv_data} if not $rri_cmdlist[$irb]->{err_stat};
}
serv11_rri_clear();
return ($ival, $rval);
}
#-------------------------------------------------------------------------------
sub next_nxbuf { # calculate next nxbuf value
my ($ctl,$nxbuf,$nxbuf_val) = @_;
if ($nxbuf_val <= $nxbuf/2) {
$nxbuf -= $ctl->{nxbuf_inc};
} else {
$nxbuf += $ctl->{nxbuf_inc};
}
$nxbuf = $ctl->{nxbuf_min} if $nxbuf < $ctl->{nxbuf_min};
$nxbuf = $ctl->{nxbuf_max} if $nxbuf > $ctl->{nxbuf_max};
return $nxbuf;
}
#-------------------------------------------------------------------------------
sub telnet_readhdl { # telnet: socket read handler
my ($teldsc) = @_;
my $ucb = $teldsc->{ucb};
if ($teldsc->{state} == TELNET_STATE_LISTEN) {
my $fh_data = new FileHandle;
if (not accept($fh_data, $teldsc->{fh_port})) {
printf STDERR "pi_rri($curmode)-E: erro in accept(): $!\n";
return; # FIXME: error handling ??
}
printf "connect on port %s for %s\n", $teldsc->{port}, $ucb->{unitname};
my $buf;
my $rc;
$buf = pack("C3", TELNET_CODE_IAC, TELNET_CODE_WILL, TELNET_OPT_LINE);
$rc = syswrite($fh_data, $buf, length($buf));
$buf = pack("C3", TELNET_CODE_IAC, TELNET_CODE_WILL, TELNET_OPT_SGA);
$rc = syswrite($fh_data, $buf, length($buf));
$buf = pack("C3", TELNET_CODE_IAC, TELNET_CODE_WILL, TELNET_OPT_ECHO);
$rc = syswrite($fh_data, $buf, length($buf));
$buf = pack("C3", TELNET_CODE_IAC, TELNET_CODE_WILL, TELNET_OPT_BIN);
$rc = syswrite($fh_data, $buf, length($buf));
$buf = pack("C3", TELNET_CODE_IAC, TELNET_CODE_DO, TELNET_OPT_BIN);
$rc = syswrite($fh_data, $buf, length($buf));
$teldsc->{state} = TELNET_STATE_STREAM;
$teldsc->{fh_data} = $fh_data;
$buf = sprintf("\r\nconnect on port %s for %s\r\n\r\n",
$teldsc->{port}, $ucb->{unitname});
$rc = syswrite($fh_data, $buf, length($buf));
telnet_writehdl($ucb);
$serv11_fds_update = 1;
} else {
my $buf;
my $rc;
$rc = sysread($teldsc->{fh_data}, $buf, 64);
if ($rc == 0) {
printf "disconnect on port %s for %s\n", $teldsc->{port}, $ucb->{unitname};
close ($teldsc->{fh_data});
delete $teldsc->{fh_data};
$teldsc->{state} = TELNET_STATE_LISTEN;
$serv11_fds_update = 1;
} else {
my @int = unpack("C*", $buf);
foreach my $byt (@int) {
if ($teldsc->{state} == TELNET_STATE_STREAM) { # state: stream
if ($byt == TELNET_CODE_IAC) {
$teldsc->{state} = TELNET_STATE_IAC;
} else {
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
my @bytes;
push @bytes, $byt;
&{$ctl->{ichrhdl}}($ucb, \@bytes); # call ichr handler
}
} elsif ($teldsc->{state} == TELNET_STATE_IAC) { # state: IAC seen
if ($byt == TELNET_CODE_WILL ||
$byt == TELNET_CODE_WONT ||
$byt == TELNET_CODE_DO ||
$byt == TELNET_CODE_DONT) {
$teldsc->{state} = TELNET_STATE_CMD;
} elsif ($byt == TELNET_CODE_SB) {
$teldsc->{state} = TELNET_STATE_SUBNEG;
} else {
$teldsc->{state} = TELNET_STATE_STREAM;
}
} elsif ($teldsc->{state} == TELNET_STATE_CMD) { # state: cmd seen
$teldsc->{state} = 0;
} elsif ($teldsc->{state} == TELNET_STATE_SUBNEG) { # state: subneg
if ($byt == TELNET_CODE_IAC) {
$teldsc->{state} = TELNET_STATE_SUBIAC;
}
} elsif ($teldsc->{state} == TELNET_STATE_SUBIAC) { # state: subneg+IAC
$teldsc->{state} = TELNET_STATE_STREAM;
}
}
}
}
}
#-------------------------------------------------------------------------------
sub telnet_writehdl { # telnet: write handler
my ($ucb) = @_;
my $teldsc = $telnettbl{$ucb->{att_port}};
return if $teldsc->{state} == TELNET_STATE_LISTEN;
while (scalar( @{$ucb->{sndque}} )) {
my $byte = shift @{$ucb->{sndque}};
syswrite($teldsc->{fh_data}, pack("C1",$byte), 1);
## FIXME: escape IAC !!
## if ($byte == TELNET_CODE_CR) {
## syswrite($teldsc->{fh_data}, pack("C1",TELNET_CODE_LF), 1);
## }
}
}
#-------------------------------------------------------------------------------
sub pdp11_disassemble {
my ($pc,$d0,$d1,$d2) = @_;
my @mem = ($d0,0,0);
$mem[1] = $d1 if defined $d1;
$mem[2] = $d2 if defined $d2;
my $code = shift @mem;
foreach my $ele (@pdp11_opcode_tbl) {
if (($code & (~($ele->{mask})) ) == $ele->{code}) {
my $name = $ele->{name};
my $type = $ele->{type};
my $str = $name;
if ($type eq "0arg") {
return ($name,1);
} elsif ($type eq "1arg" or $type eq "1fpp") {
my $dst = $code & 077;
my $pref = ($type eq "1fpp") ? "f" : "r";
my ($dst_str,$dst_nw,$dst_ta) =
pdp11_disassemble_regmod($dst, $mem[0], $pc+2, $pref);
shift @mem if ($dst_nw);
$str = "$name $dst_str";
if ($dst_ta) {
$str .= " " x (27-length($str)) if length($str)<27;
$str .= "; $dst_ta";
}
return ($str, 1+$dst_nw);
} elsif ($type eq "2arg") {
my $src = ($code>>6) & 077;
my $dst = $code & 077;
my ($src_str,$src_nw,$src_ta) =
pdp11_disassemble_regmod($src, $mem[0], $pc+2);
shift @mem if ($src_nw);
my ($dst_str,$dst_nw,$dst_ta) =
pdp11_disassemble_regmod($dst, $mem[0], $pc+2+2*$src_nw);
shift @mem if ($dst_nw);
$str = "$name $src_str,$dst_str";
if ($src_ta or $dst_ta) {
$str .= " " x (27-length($str)) if length($str)<27;
$str .= ";";
$str .= " $src_ta" if $src_ta;
$str .= " $dst_ta" if $dst_ta;
}
return ($str, 1+$src_nw+$dst_nw);
} elsif ($type eq "rdst") {
my $reg = ($code>>6) & 07;
my $src = $code & 077;
my ($src_str,$src_nw,$src_ta) =
pdp11_disassemble_regmod($src, $mem[0], $pc+2);
shift @mem if ($src_nw);
$str = "$name $src_str,r$reg";
if ($src_ta) {
$str .= " " x (27-length($str)) if length($str)<27;
$str .= "; $src_ta";
}
return ($str, 1+$src_nw);
} elsif ($type eq "1reg") {
my $reg = $code & 07;
my $reg_str = "r$reg";
$reg_str = "sp" if $reg == 6;
$reg_str = "pc" if $reg == 7;
return ("$name $reg_str", 1);
} elsif ($type eq "br") {
my $off = $code & 0177;
my $sign = "+";
if ($code & 0200) {
$off = -(((~$off) & 0177)+1);
$sign = "-";
}
my $str = sprintf "$name .%s%d.", $sign, abs(2*$off);
$str .= " " x (27-length($str)) if length($str)<27;
$str .= sprintf "; -> %6.6o", (($pc+2)+2*$off)&0177777;
return ($str, 1);
} elsif ($type eq "sob") {
my $reg = ($code>>6) & 07;
my $off = $code & 077;
my $str = sprintf "$name r%d,.-%d.", $reg, 2*$off;
$str .= " " x (27-length($str)) if length($str)<27;
$str .= sprintf "; -> %6.6o", ($pc+2)-2*$off;
return ($str, 1);
} elsif ($type eq "trap") {
my $off = $code & 0377;
my $str = sprintf "$name %3.3o", $off;
return ($str, 1);
} elsif ($type eq "spl") {
my $off = $code & 07;
my $str = sprintf "$name %d", $off;
return ($str, 1);
} elsif ($type eq "ccop") {
my $cc = $code & 017;
return ("nop",1) if ($cc == 0);
return ("ccc",1) if ($code == 0257);
return ("scc",1) if ($code == 0277);
my $str = "";
my $del = "";
if ($code & 010) { $str .= $del . $name . "n", $del = "+" }
if ($code & 004) { $str .= $del . $name . "z", $del = "+" }
if ($code & 002) { $str .= $del . $name . "v", $del = "+" }
if ($code & 001) { $str .= $del . $name . "c", $del = "+" }
return ($str, 1);
} elsif ($type eq "jsr") {
my $reg = ($code>>6) & 07;
my $dst = $code & 077;
my ($dst_str,$dst_nw,$dst_ta) =
pdp11_disassemble_regmod($dst, $mem[0], $pc+2);
shift @mem if ($dst_nw);
$str = "$name r$reg,$dst_str";
if ($dst_ta) {
$str .= " " x (27-length($str)) if length($str)<27;
$str .= "; $dst_ta";
}
return ($str, 1+$dst_nw);
} elsif ($type eq "mark") {
my $off = $code & 077;
my $str = sprintf "$name %3.3o", $off;
return ($str, 1);
} elsif ($type eq "rfpp") {
my $reg = ($code>>6) & 03;
my $dst = $code & 077;
my ($dst_str,$dst_nw,$dst_ta) =
pdp11_disassemble_regmod($dst, $mem[0], $pc+2, "f");
shift @mem if ($dst_nw);
$str = "$name f$reg,$dst_str";
if ($dst_ta) {
$str .= " " x (27-length($str)) if length($str)<27;
$str .= "; $dst_ta";
}
return ($str, 1+$dst_nw);
} else {
return ("?type?",1);
}
}
}
return ("=inval=",1);
}
#-------------------------------------------------------------------------------
sub pdp11_disassemble_regmod {
my ($regmod,$data,$pc,$pref) = @_;
my $mod = ($regmod>>3) & 07;
my $reg = $regmod & 07;
$pref = "r" if not defined $pref or $reg>5;
my $reg_str = "r$reg";
$reg_str = "sp" if $reg == 6;
$reg_str = "pc" if $reg == 7;
if ($mod == 0) { # mode 0: Rx { Fx for float }
$reg_str = "f$reg" if defined $pref && $pref eq "f" && $reg<=5;
return ($reg_str, 0, "");
} elsif ($mod == 1) { # mode 1: (Rx)
return ("($reg_str)", 0, "");
} elsif ($mod == 2 || $mod == 3) { # mode 2/3: (Rx)+ @(Rx)+
my $ind = ($mod == 3) ? "@" : "";
if ($reg != 7) { # if reg != pc
return ("$ind($reg_str)+", 0, "");
} else { # if reg == pc
my $str = sprintf "$ind#%6.6o", $data; # 27 -> #nnn; 37 -> @#nnn
return ($str, 1, "");
}
} elsif ($mod == 4 || $mod == 5) { # mode 4/5: -(Rx) @-(Rx)
my $ind = ($mod == 5) ? "@" : "";
return ("$ind-($reg_str)", 0, "");
} elsif ($mod == 6 || $mod == 7) { # mode 6/7: nn(Rx) @nn(Rx)
my $ind = ($mod == 7) ? "@" : "";
my $data_str = sprintf "%o", $data;
my $ta_str = "";
$ta_str = sprintf "%6.6o",($pc+2+$data)&0177777 if ($reg==7);
return ("$ind$data_str($reg_str)", 1, $ta_str);
}
}
#-------------------------------------------------------------------------------
sub file_seek { # fseek wrapper
my ($fh,$pos) = @_;
my $rc;
my $offset = $pos;
my $whence = 0;
if ($pos < 0) { # if offset<0 -> seek to EOF
$offset = 0;
$whence = 2;
}
$rc = $fh->seek($offset, $whence);
if (not $rc) {
print "pi_rri($curmode)-E: file_seek failed\n";
$rc = 0;
}
return $rc;
}
#-------------------------------------------------------------------------------
sub file_read { # fread wrapper
my ($fh,$nbyte) = @_;
my $rc;
my $buf = "";
$rc = $fh->read($buf, $nbyte);
if ($rc != $nbyte) {
print "pi_rri($curmode)-E: file_read failed, got $rc, expectd $nbyte\n";
$rc = 0;
}
return ($rc, $buf);
}
#-------------------------------------------------------------------------------
sub file_seek_read { # fseek+fread wrapper
my ($fh,$pos,$nbyte) = @_;
my $rc;
my $buf;
$rc = file_seek($fh, $pos);
($rc,$buf) = file_read($fh, $nbyte) if $rc;
return ($rc, $buf);
}
#-------------------------------------------------------------------------------
sub file_write { # fwrite wrapper
my ($fh,$buf) = @_;
my $rc;
$rc = print $fh $buf;
if (not $rc) {
print "pi_rri($curmode)-E: file_write failed\n";
$rc = 0;
}
return $rc;
}
#-------------------------------------------------------------------------------
sub file_seek_write { # fseek+fwrite wrapper
my ($fh,$pos,$buf) = @_;
my $rc;
$rc = file_seek($fh, $pos);
$rc = file_write($fh, $buf) if $rc;
return $rc;
}
#-------------------------------------------------------------------------------
sub raw_get9_crc_16bit { # read 16 bit value
my ($dref) = @_;
my $idl = raw_get9_crc();
my $idh = undef;
$idh = raw_get9_crc() if defined $idl;
if (defined $idh) {
my $idat = $idl | ($idh<<8);
$$dref = $idat;
return 1;
}
print "pi_rri($curmode)-E: receive time out\n";
print $fh_log "ERR -- receive time out in raw_get9_crc_16bit\n";
return 0;
}
#-------------------------------------------------------------------------------
sub raw_get9_crc_8bit { # read 8bit value
my ($dref) = @_;
my $idat = raw_get9_crc();
if (defined $idat) {
$$dref = $idat;
return 1;
}
return 0;
}
#-------------------------------------------------------------------------------
sub raw_get9_crc_check { # get 9bit, block, crc, ref value
my ($ref,$case) = @_;
my $dat = raw_get9_crc();
if (defined $dat) {
return 1 if ($dat == $ref);
printf "pi_rri($curmode)-E: receive $case mismatch" .
" found=0x%3.3x expect=0x%3.3x\n",
$dat, $ref;
return 0;
}
return 0;
}
#-------------------------------------------------------------------------------
sub raw_get9_check { # get 9bit, block, expect ref value
my ($ref,$case) = @_;
my $dat = raw_get9();
if (defined $dat) {
return 1 if ($dat == $ref);
printf "pi_rri($curmode)-E: receive $case mismatch" .
" found=0x%3.3x expect=0x%3.3x\n",
$dat, $ref;
return 0;
}
return 0;
}
#-------------------------------------------------------------------------------
sub raw_get9_checksop { # get 9bit, block, expect SOP
my $dat;
while(1) {
$dat = raw_get9();
last unless defined $dat;
last if ($dat != D9ATTN);
if ($serv11_active) {
$serv11_attn_seen = 1;
} else {
printf "pi_rri($curmode)-W: unexpected ATTN comma dropped\n";
}
}
if (defined $dat) {
return 1 if ($dat == D9SOP);
printf "pi_rri($curmode)-E: expect sop, but found=0x%3.3x\n", $dat;
}
return 0;
}
#-------------------------------------------------------------------------------
sub raw_get9_checkeop { # get 9bit, block, expect EOP
my $dat;
$dat = raw_get9();
if (defined $dat) {
return 1 if ($dat == D9EOP);
printf "pi_rri($curmode)-E: expect eop, but found=0x%3.3x\n", $dat;
}
return 0;
}
#-------------------------------------------------------------------------------
sub raw_get9_crc { # get 9bit, block, update crc
my $dat = raw_get9();
$icrc = $crc8_tbl[$icrc ^ $dat] if (defined $dat && $dat < 0x100);
return $dat;
}
#-------------------------------------------------------------------------------
sub raw_get9 { # get 9bit, block
my $nidle = 0;
my $dat = undef;
while (1) {
$dat = raw_rcv9_to($raw_timeout);
last unless defined $dat;
last if $dat != D9IDLE;
$nidle += 1;
}
## print "pi_rri($curmode)-I: dropped $nidle idle commas\n" if $nidle;
print "pi_rri($curmode)-E: receive time out\n" unless defined $dat;
print $fh_log "ERR -- receive time out in raw_get9\n" unless defined $dat;
return $dat;
}
#-------------------------------------------------------------------------------
sub raw_snd9_crc { # put 9bit to RX, update crc
my ($dat) = @_;
raw_snd9($dat);
$ocrc = $crc8_tbl[$ocrc ^ $dat] if ($dat < 0x100);
}
#-------------------------------------------------------------------------------
sub raw_snd9 { # put 9bit to RX
my ($dat) = @_;
if (exists $opts{tio9}) {
print $fh_log conv_etime(\$tlast_tio9),
"[$curchan] snd9 ", conv_dat9($dat);
printf $fh_log " sndq=%3d", scalar(@que_snd);
print $fh_log " -- idle" if $dat == D9IDLE;
print $fh_log " -- sop " if $dat == D9SOP;
print $fh_log " -- eop " if $dat == D9EOP;
print $fh_log " -- nak " if $dat == D9NAK;
print $fh_log " -- attn" if $dat == D9ATTN;
print $fh_log "\n";
}
$stat_tab{osop} += 1 if $dat == D9SOP;
if ($dat >= 0x100) {
raw_snd8(CPREF | ($dat & 0x0f));
} else {
if ( $dat == CESC ||
($dat >= CPREF && $dat <= (CPREF+NCOMM)) ) {
raw_snd8(CESC);
raw_snd8(CEN1 | ($dat & 0x0f));
$stat_tab{oesc} += 1;
} else {
raw_snd8($dat);
}
}
}
#-------------------------------------------------------------------------------
sub raw_snd8 { # put 8bit to RX
my ($dat) = @_;
if (exists $opts{tio8}) {
print $fh_log conv_etime(\$tlast_tio8),
"[$curchan] snd8 ", conv_dat8($dat),"\n";
}
$stat_tab{obyte} += 1;
push @que_snd, int $dat;
}
#-------------------------------------------------------------------------------
sub raw_rcv9 { # get 9bit from TX, non-blocking
return raw_rcv9_to(0.);
}
#-------------------------------------------------------------------------------
sub raw_rcv8 { # get 8bit from TX, non-blocking
return raw_rcv8_to(0.);
}
#-------------------------------------------------------------------------------
sub raw_rcv9_to {
my ($timeout) = @_;
my $dat8 = raw_rcv8_to($timeout);
my $dat9 = undef;
if (defined $dat8) {
if ($dat8 == CESC) {
$stat_tab{iesc} += 1;
$raw_rcv_esc = 1;
$dat8 = raw_rcv8_to($timeout);
return $dat8 unless defined $dat8;
}
if ($raw_rcv_esc) {
$dat9 = CPREF | ($dat8 & 0x0f);
$raw_rcv_esc = 0;
} else {
if ($dat8>= CPREF && $dat8<=(CPREF+NCOMM) ) {
$dat9 = 0x100 | ($dat8 & 0x0f);
} else {
$dat9 = $dat8;
}
}
}
if (defined $dat9) {
$stat_tab{att} += 1 if $dat9 == D9ATTN;
##print "+++9 attn seen\n" if $dat9==D9ATTN;
if (exists $opts{tio9}) {
print $fh_log conv_etime(\$tlast_tio9),
"[$curchan] rcv9 ", conv_dat9($dat9);
printf $fh_log " rcvq=%3d", scalar(@que_rcv);
print $fh_log " -- idle" if $dat9 == D9IDLE;
print $fh_log " -- sop " if $dat9 == D9SOP;
print $fh_log " -- eop " if $dat9 == D9EOP;
print $fh_log " -- nak " if $dat9 == D9NAK;
print $fh_log " -- attn" if $dat9 == D9ATTN;
print $fh_log "\n";
}
}
return $dat9;
}
#-------------------------------------------------------------------------------
sub raw_rcv8_to { # get 8bit from TX, expl. time-out
my ($timeout) = @_;
my $buf;
my $dat;
&{$chan_tab{$curchan}{write}}(); # flush write queue before read
&{$chan_tab{$curchan}{read}}($timeout) unless @que_rcv;
$dat = shift @que_rcv;
if (exists $opts{tio8} and defined $dat) {
print $fh_log conv_etime(\$tlast_tio8),
"[$curchan] rcv8 ", conv_dat8($dat),"\n";
}
$stat_tab{ibyte} += 1;
return $dat;
}
#-------------------------------------------------------------------------------
sub wait_sel_filercv { # poll/wait for TX to be ready
my ($timeout) = @_;
my $nfound=-1;
my $fds_rd;
while ($nfound<0) {
$nfound = select($fds_rd=$fdset_filercv, undef, undef, $timeout);
next if ($nfound == -1) and $! == EINTR;
die "select error: $!" unless $nfound >= 0;
}
return $nfound;
}
#-------------------------------------------------------------------------------
sub fifo_open { # chan fifo: open handler
my ($arg) = @_;
my ($file,$keep) = split /,/,$arg;
my $file_base = $file ? $file : "tb_rriext_fifo";
my $file_snd = $file_base . "_rx";
my $file_rcv = $file_base . "_tx";
$fifo_keep = $keep;
$fdset_filercv = "";
print_fatal("I/O mode already set to --$curchan") if ($curchan);
if (-e $file_snd) {
print_fatal("$file_snd exists but is not a pipe") unless (-p $file_snd);
} else {
mkfifo($file_snd, 0666) || die "can't mkfifo $file_snd: $!";
print "pi_rri[fifo]-I: created fifo $file_snd\n";
}
if (-e $file_rcv) {
print_fatal("$file_rcv exists but is not a pipe") unless (-p $file_rcv);
} else {
mkfifo($file_rcv, 0666) || die "can't mkfifo $file_rcv: $!";
print "pi_rri[fifo]-I: created fifo $file_rcv\n";
}
$fh_snd = new FileHandle;
$fh_rcv = new FileHandle;
print "pi_rri[fifo]-I: wait to connect to $file_snd\n";
sysopen ($fh_snd, $file_snd, O_WRONLY) || die "can't open $file_snd: $!";
print "pi_rri[fifo]-I: connected to $file_snd\n";
sysopen ($fh_rcv, $file_rcv, O_RDONLY) || die "can't open $file_rcv: $!";
print "pi_rri[fifo]-I: connected to $file_rcv\n";
vec($fdset_filercv, fileno($fh_rcv), 1) = 1;
$curchan = "fifo";
}
#-------------------------------------------------------------------------------
sub fifo_close { # chan fifo: close handler
if ($fifo_keep) {
print "pi_rri[fifo]-I: signal 'keep-alive' to tb\n";
raw_snd8(CESC);
raw_snd8(CESC);
&{$chan_tab{$curchan}{write}}();
}
close $fh_snd;
close $fh_rcv;
$fh_snd = undef;
$fh_rcv = undef;
$curchan = undef;
}
#-------------------------------------------------------------------------------
sub term_open { # term fifo: open handler
my ($arg) = @_;
my ($dev,$baud,$break) = split /,/,$arg;
$dev = "/dev/ttyS0" unless $dev;
$baud = 115200 unless $baud;
$break = 0 unless $break;
$fdset_filercv = "";
print_fatal("I/O mode already set to --$curchan") if ($curchan);
$fh_snd = new FileHandle;
$fh_rcv = $fh_snd; # same file handle for read and write
sysopen ($fh_snd, $dev, O_RDWR|O_NOCTTY) || # read/write, not control TTY
die "can't open $dev: $!";
my $fd = fileno($fh_snd);
vec($fdset_filercv, $fd, 1) = 1;
$curchan = "term";
print_fatal("$dev is not a TTY") unless isatty($fd);
$term_oldtios = new POSIX::Termios;
$term_oldtios->getattr($fd) || die "getattr failed: $!";
## term_tios_print($term_oldtios);
my $newtios = new POSIX::Termios;
$newtios->getattr($fd) || die "getattr failed: $!"; ## hack for cygwin !!
my $c_iflag = &POSIX::BRKINT; # ignore parity errors
my $c_oflag = 0;
my $c_cflag = &POSIX::CS8 | # 8 bit chars
&POSIX::CSTOPB | # 2 stop bits
&POSIX::CREAD | # enable receiver
&POSIX::CLOCAL | # ignore modem control
LINUX_CRTSCTS; # enable hardware flow control
my $c_lflag = 0;
my $speed = 0;
$speed = &POSIX::B9600 if $baud == 9600;
$speed = &POSIX::B19200 if $baud == 19200;
$speed = &POSIX::B38400 if $baud == 38400;
$speed = LINUX_B57600 if $baud == 57600; # hack, only for linux
$speed = LINUX_B115200 if $baud == 115200; # hack, only for linux
$speed = LINUX_B230400 if $baud == 230400; # hack, only for linux
$speed = LINUX_B460800 if $baud == 460800; # hack, only for linux
$speed = LINUX_B500000 if $baud == 500000; # hack, only for linux
$speed = LINUX_B921600 if $baud == 921600; # hack, only for linux
$speed = LINUX_B1000000 if $baud ==1000000; # hack, only for linux
$speed = LINUX_B2000000 if $baud ==2000000; # hack, only for linux
$speed = LINUX_B3000000 if $baud ==3000000; # hack, only for linux
print_fatal("speed $baud not supported") unless $speed != 0;
$c_cflag |= $speed;
$newtios->setiflag($c_iflag);
$newtios->setoflag($c_oflag);
$newtios->setcflag($c_cflag);
$newtios->setlflag($c_lflag);
$newtios->setcc(&POSIX::VEOF, 0); # undef
$newtios->setcc(&POSIX::VEOL, 0); # undef
$newtios->setcc(&POSIX::VERASE, 0); # undef
$newtios->setcc(&POSIX::VINTR, 0); # undef
$newtios->setcc(&POSIX::VKILL, 0); # undef
$newtios->setcc(&POSIX::VQUIT, 0); # undef
$newtios->setcc(&POSIX::VSUSP, 0); # undef
$newtios->setcc(&POSIX::VSTART, 0); # undef
$newtios->setcc(&POSIX::VSTOP, 0); # undef
$newtios->setcc(&POSIX::VMIN, 1); # wait for 1 char
$newtios->setcc(&POSIX::VTIME, 0); #
## term_tios_print($newtios);
$newtios->setattr($fd) || die "setattr failed: $!";
if ($break) {
tcsendbreak($fd, 0) || die "tcsendbreak failed: $!";
raw_snd8 (0x80);
&{$chan_tab{$curchan}{write}}(); # write 10000000 for autobaud
}
}
#-------------------------------------------------------------------------------
sub term_close { # chan term: close handler
$term_oldtios->setattr(fileno($fh_snd)) || die "setattr failed: $!";
close $fh_snd;
$fh_snd = undef;
$fh_rcv = undef;
$curchan = undef;
}
#-------------------------------------------------------------------------------
sub term_tios_print {
my ($tios) = @_;
my $iflag = $tios->getiflag;
my $oflag = $tios->getoflag;
my $cflag = $tios->getcflag;
my $lflag = $tios->getlflag;
printf "iflag = %8.8x:", $iflag;
print " BRKINT" if $iflag & &POSIX::BRKINT;
print " ICRNL " if $iflag & &POSIX::ICRNL;
print " IGNBRK" if $iflag & &POSIX::IGNBRK;
print " IGNCR " if $iflag & &POSIX::IGNCR;
print " IGNPAR" if $iflag & &POSIX::IGNPAR;
print " INLCR " if $iflag & &POSIX::INLCR;
print " INPCK " if $iflag & &POSIX::INPCK;
print " ISTRIP" if $iflag & &POSIX::ISTRIP;
print " IXOFF " if $iflag & &POSIX::IXOFF;
print " IXON " if $iflag & &POSIX::IXON;
print " PARMRK" if $iflag & &POSIX::PARMRK;
print "\n";
printf "oflag = %8.8x:", $oflag;
print " OPOST " if $oflag & &POSIX::OPOST;
print "\n";
printf "cflag = %8.8x:", $cflag;
print " CLOCAL" if $cflag & &POSIX::CLOCAL;
print " CREAD " if $cflag & &POSIX::CREAD;
print " CS5 " if ($cflag & &POSIX::CSIZE) == &POSIX::CS5;
print " CS6 " if ($cflag & &POSIX::CSIZE) == &POSIX::CS6;
print " CS7 " if ($cflag & &POSIX::CSIZE) == &POSIX::CS7;
print " CS8 " if ($cflag & &POSIX::CSIZE) == &POSIX::CS8;
print " CSTOPB" if $cflag & &POSIX::CSTOPB;
print " HUPCL " if $cflag & &POSIX::HUPCL;
print " PARENB" if $cflag & &POSIX::PARENB;
print " PARODD" if $cflag & &POSIX::PARODD;
my $sbits = &POSIX::B50 | &POSIX::B75 | &POSIX::B110 | &POSIX::B134 |
&POSIX::B150 | &POSIX::B200 | &POSIX::B300 | &POSIX::B600 |
&POSIX::B1200 | &POSIX::B1800 | &POSIX::B2400 | &POSIX::B4800 |
&POSIX::B9600 | &POSIX::B19200 | &POSIX::B38400;
print " B0 " if ($cflag & $sbits) == &POSIX::B0;
print " B50 " if ($cflag & $sbits) == &POSIX::B50;
print " B75 " if ($cflag & $sbits) == &POSIX::B75;
print " B110 " if ($cflag & $sbits) == &POSIX::B110;
print " B134 " if ($cflag & $sbits) == &POSIX::B134;
print " B150 " if ($cflag & $sbits) == &POSIX::B150;
print " B200 " if ($cflag & $sbits) == &POSIX::B200;
print " B300 " if ($cflag & $sbits) == &POSIX::B300;
print " B600 " if ($cflag & $sbits) == &POSIX::B600;
print " B1200 " if ($cflag & $sbits) == &POSIX::B1200;
print " B1800 " if ($cflag & $sbits) == &POSIX::B1800;
print " B2400 " if ($cflag & $sbits) == &POSIX::B2400;
print " B4800 " if ($cflag & $sbits) == &POSIX::B4800;
print " B9600 " if ($cflag & $sbits) == &POSIX::B9600;
print " B19200" if ($cflag & $sbits) == &POSIX::B19200;
print " B38400" if ($cflag & $sbits) == &POSIX::B38400;
print "\n";
printf "lflag = %8.8x:", $lflag;
print " ECHO " if $lflag & &POSIX::ECHO;
print " ECHOE " if $lflag & &POSIX::ECHOE;
print " ECHOK " if $lflag & &POSIX::ECHOK;
print " ECHONL" if $lflag & &POSIX::ECHONL;
print " ICANON" if $lflag & &POSIX::ICANON;
print " IEXTEN" if $lflag & &POSIX::IEXTEN;
print " ISIG " if $lflag & &POSIX::ISIG;
print " NOFLSH" if $lflag & &POSIX::NOFLSH;
print " TOSTOP" if $lflag & &POSIX::TOSTOP;
print "\n";
printf "cc(VEOF) = %3.3o\n", $tios->getcc(&POSIX::VEOF);
printf "cc(VEOL) = %3.3o\n", $tios->getcc(&POSIX::VEOL);
printf "cc(VERASE)= %3.3o\n", $tios->getcc(&POSIX::VERASE);
printf "cc(VINTR) = %3.3o\n", $tios->getcc(&POSIX::VINTR);
printf "cc(VKILL) = %3.3o\n", $tios->getcc(&POSIX::VKILL);
printf "cc(VQUIT) = %3.3o\n", $tios->getcc(&POSIX::VQUIT);
printf "cc(VSUSP) = %3.3o\n", $tios->getcc(&POSIX::VSUSP);
printf "cc(VSTART)= %3.3o\n", $tios->getcc(&POSIX::VSTART);
printf "cc(VSTOP) = %3.3o\n", $tios->getcc(&POSIX::VSTOP);
printf "cc(VMIN) = %3.3o\n", $tios->getcc(&POSIX::VMIN);
printf "cc(VTIME) = %3.3o\n", $tios->getcc(&POSIX::VTIME);
# printf "cc(NCCS) = %3.3o\n", $tios->getcc(&POSIX::NCCS);
}
#-------------------------------------------------------------------------------
sub genio_read { # generic io: read handler
my ($timeout) = @_;
my $tstart;
my $rc;
$tstart = get_time() if exists $opts{tiob};
if (wait_sel_filercv($timeout)) {
my $buf;
while (not defined $rc) {
$rc = sysread($fh_rcv, $buf, 64);
next if (not defined $rc) and $! == EINTR;
die "sysread fifo error: $!" unless defined $rc;
}
if (exists $opts{tiob}) {
printf $fh_log "%s[$curchan] read %3d bytes in %8.6f sec\n",
conv_etime(\$tlast_tiob), $rc, get_time()-$tstart;
}
if ($rc) {
push @que_rcv, unpack("C*", $buf);
}
}
### if (defined $rc) {
### printf "+++1 _read $timeout rc=%d\n", $rc;
### } else {
### printf "+++1 _read $timeout rc=undef\n";
### }
return $rc;
}
#-------------------------------------------------------------------------------
sub genio_write { # generic io: write handler
## printf "+++2 _write q=%d\n", scalar @que_snd;
if (scalar @que_snd) {
my $buf = pack("C*", @que_snd);
while (length($buf)) {
while(1) { # read rcv fifo before writing
my $rc = genio_read(0.); # to avoid blocking under cygwin
last unless defined $rc and $rc > 0;
}
my $nwrite = length($buf);
### $nwrite = 1; # <-- when is this really needed ???
### printf "+++2a _write nw=%d\n", $nwrite;
my $rc = syswrite($fh_snd, $buf, $nwrite);
next if (not defined $rc) and $! == EINTR;
die "syswrite fifo error: $!" unless defined $rc;
if (exists $opts{tiob}) {
printf $fh_log "%s[$curchan] write %3d bytes", conv_etime(\$tlast_tiob), $rc;
printf $fh_log " of %3d in queue", length($buf) if $rc < length($buf);
print $fh_log "\n";
}
last if $rc == length($buf);
$buf = substr($buf, $rc);
}
@que_snd = ();
}
}
#-------------------------------------------------------------------------------
sub cget_chkblank { # check for unused chars in cmd line
$cmd_rest =~ s/^\s*//;
if ($cmd_rest ne "") {
print "pi_rri($curmode)-E: extra data ignored: \"$cmd_rest\"\n";
print " for command: \"$cmd_line\"\n";
$cmd_bad = 1;
}
return $cmd_bad;
}
#-------------------------------------------------------------------------------
sub cget_tagval2_gdat { # get tag=v1[,v2], generic base
my ($tag,$nbit,$dbase) = @_;
my $dat;
my $msk = undef;
$cmd_rest =~ s/^\s*//;
### print "+++2 |$cmd_rest|$tag|$nbit|$dbase|\n";
if ($cmd_rest =~ /^$tag=/) {
$cmd_rest = $';
if ($cmd_rest =~ /^-/) {
$cmd_rest = $';
return (0,0xffff);
} else {
$dat = cget_gdat($nbit, $dbase);
if ($cmd_rest =~ /^,/) {
$cmd_rest = $';
$msk = cget_gdat($nbit, $dbase);
}
return ($dat, $msk);
}
}
return (undef, undef);
}
#-------------------------------------------------------------------------------
sub cget_tagval_gdat { # get tag=val, generic base
my ($tag,$nbit,$dbase,$min,$max) = @_;
$cmd_rest =~ s/^\s*//;
if ($cmd_rest =~ /^$tag=/) {
$cmd_rest = $';
return cget_gdat($nbit, $dbase,$min,$max);
}
return undef;
}
#-------------------------------------------------------------------------------
sub cget_gdat { # get generic base value
my ($nbit,$dbase,$min,$max) = @_;
my $dat;
$cmd_rest =~ s/^\s*//;
### print "+++1 |$nbit|$dbase|$cmd_rest|\n";
if ($cmd_rest =~ /^[xXoObBdD]"/) {
if ($cmd_rest =~ /^[xX]"([0-9a-fA-F]+)"/) {
$cmd_rest = $';
$dat = hex $1;
} elsif ($cmd_rest =~ /^[oO]"([0-7]+)"/) {
$cmd_rest = $';
$dat = oct $1;
} elsif ($cmd_rest =~ /^[bB]"([01]+)"/) {
$cmd_rest = $';
my $odat = sget_bdat($nbit, $1);
$dat = $odat if defined $odat;
} elsif ($cmd_rest =~ /^[dD]"([+-]?[0-9]+)"/) {
$cmd_rest = $';
my $odat = (int $1) & ((1<<$nbit)-1);
$dat = $odat;
}
} else {
if ($cmd_rest =~ /^([+-]?[0-9]+)\./) {
$cmd_rest = $';
my $odat = (int $1) & ((1<<$nbit)-1);
$dat = $odat;
} elsif ($dbase == 16 && $cmd_rest =~ /^([0-9a-fA-F]+)/) {
$cmd_rest = $';
$dat = hex $1;
} elsif ($dbase == 8 && $cmd_rest =~ /^([0-7]+)/) {
$cmd_rest = $';
$dat = oct $1;
} elsif ($dbase == 2 && $cmd_rest =~ /^([01]+)/) {
$cmd_rest = $';
my $odat = sget_bdat($nbit, $1);
$dat = $odat if defined $odat;
} elsif ($dbase == 10 && $cmd_rest =~ /^([0-9]+)/) {
$cmd_rest = $';
$dat = int $1;
}
}
if (not defined $dat) {
$cmd_bad = 1;
print "pi_rri($curmode)-E: cget_gdat error in \"$cmd_rest\" (base=$dbase)\n";
return undef;
}
if (defined $min && $dat < $min) {
$cmd_bad = 1;
print "pi_rri($curmode)-E: cget_gdat range error, $dat < $min\n";
return undef;
}
if (defined $max && $dat > $max) {
$cmd_bad = 1;
print "pi_rri($curmode)-E: cget_gdat range error, $dat > $max\n";
return undef;
}
return $dat;
}
#-------------------------------------------------------------------------------
sub cget_name { # get name \w+
$cmd_rest =~ s/^\s*//;
if ($cmd_rest =~ /^(\w+)/) {
$cmd_rest = $';
return $1;
}
$cmd_bad = 1;
print "pi_rri($curmode)-E: cget_name error in \"$cmd_rest\"\n";
return undef;
}
#-------------------------------------------------------------------------------
sub cget_bool { # get boolean [01]
$cmd_rest =~ s/^\s*//;
if ($cmd_rest =~ /^([01])/) {
$cmd_rest = $';
return int($1);
}
$cmd_bad = 1;
print "pi_rri($curmode)-E: cget_name error in \"$cmd_rest\"\n";
return undef;
}
#-------------------------------------------------------------------------------
sub cget_file { # get filename [\w\/.]+
$cmd_rest =~ s/^\s*//;
if ($cmd_rest =~ /^([\w\/.-]+)/) {
$cmd_rest = $';
return $1;
}
$cmd_bad = 1;
print "pi_rri($curmode)-E: cget_file error in \"$cmd_rest\"\n";
return undef;
}
#-------------------------------------------------------------------------------
sub cget_ucb { # get ucb (read name, return ucb)
my ($type,$name) = @_;
$name = cget_name() unless defined $name;
return undef if not defined $name;
$name = uc($name);
$name .= "0" if length($name)==2;
if (not exists $serv11_unittbl{$name}) {
$cmd_bad = 1;
print "pi_rri($curmode)-E: unknown device unit $name\n";
return undef;
}
my $ucb = $serv11_unittbl{$name};
my $ctl = $serv11_ctltbl{$ucb->{ctlname}};
if (not $ctl->{probe_ok}) {
$cmd_bad = 1;
print "pi_rri($curmode)-E: device controller $name not available\n";
return undef;
}
if (defined $type) {
if ($ctl->{type} ne $type) {
$cmd_bad = 1;
print "pi_rri($curmode)-E: $name is not type=$type\n";
return undef;
}
}
return $ucb;
}
#-------------------------------------------------------------------------------
sub cget_opt { # get option
my ($opt) = @_;
if ($cmd_rest =~ /^\s*$opt\b/) { # opt found, followed by non \w
$cmd_rest = $';
return 1;
}
return 0;
}
#-------------------------------------------------------------------------------
sub cget_optset { # get option set
my ($optset) = @_;
my $optout = "";
while ($cmd_rest =~ /\s*-([a-zA-Z])\b/) { # any -x found
$cmd_rest = $';
my $optchar = $1;
if ($optset =~ /$optchar/) { # char in optset ?
$optout .= $optchar;
} else {
$cmd_bad = 1;
print "pi_rri($curmode)-E: unexpected option -$optchar\n";
}
}
return $optout;
}
#-------------------------------------------------------------------------------
sub cget_regrange { # get register/memory range
my $ctl;
my $beg;
my $end;
if (cchk_number()) { # numerical address
$beg = cget_gdat(22,8);
$end = $beg;
if ($cmd_rest =~ m{^:}) {
$cmd_rest =~ s{^:}{};
$end = cget_gdat(22,8);
} elsif ($cmd_rest =~ m{^/}) {
$cmd_rest =~ s{^/}{};
$end = $beg + cget_gdat(22,8) - 2;
}
} else { # symbolical address
my $regtbl;
my $ctlnam = uc(cget_name());
my $begnam = lc($ctlnam);
my $endnam;
if (exists $serv11_ctltbl{CPU}->{regtbl}->{$begnam}) {
$ctlnam = "CPU";
$regtbl = $serv11_ctltbl{CPU}->{regtbl};
} elsif (exists $serv11_ctltbl{$ctlnam}->{regtbl}) {
$regtbl = $serv11_ctltbl{$ctlnam}->{regtbl};
$begnam = lc(cget_name());
} else {
print "pi_rri($curmode)-E: '$begnam' neither controller nor" .
" cpu register name\n";
$cmd_bad = 1;
return (undef, undef, undef);
}
$ctl = $serv11_ctltbl{$ctlnam};
if (not $ctl->{probe_ok}) {
print "pi_rri($curmode)-E: '$ctlnam' not available\n";
$cmd_bad = 1;
return (undef, undef, undef);
}
my $reglist = $ctl->{reglist};
$beg = 0;
$end = scalar @{$reglist}-1;
if ($begnam ne "state") {
$endnam = $begnam;
if ($cmd_rest =~ m{^:}) {
$cmd_rest =~ s{^:}{};
$endnam = lc(cget_name());
}
if (not exists $regtbl->{$begnam}) {
print "pi_rri($curmode)-E: '$begnam' not register in '$ctlnam'\n";
$cmd_bad = 1;
return (undef, undef, undef);
}
if (not exists $regtbl->{$endnam}) {
print "pi_rri($curmode)-E: '$endnam' not register in '$ctlnam'\n";
$cmd_bad = 1;
return (undef, undef, undef);
}
$beg = $regtbl->{$begnam};
$end = $regtbl->{$endnam};
}
}
if (defined $beg && defined $end && $beg > $end) {
my $tmp = $beg;
$beg = $end;
$end = $tmp;
}
return ($ctl, $beg, $end);
}
#-------------------------------------------------------------------------------
sub cchk_number { # check for number. any gdat value
# except for plain hex (e.g. 'dead')
return 1 if $cmd_rest =~ /^\s*([0-9]+)/;
return 1 if $cmd_rest =~ /^\s*([+-]?[0-9]+)\./;
return 1 if $cmd_rest =~ /^\s*[xX]"([0-9a-fA-F]+)"/;
return 1 if $cmd_rest =~ /^\s*[oO]"([0-9]+)"/;
return 1 if $cmd_rest =~ /^\s*[bB]"([01]+)"/;
return 1 if $cmd_rest =~ /^\s*[dD]"([+-]?[0-9]+)"/;
return 0;
}
#-------------------------------------------------------------------------------
sub sget_bdat { # convert 01 string -> binary value
my ($nbit,$str) = @_;
my $nchar = length($str);
my $odat = 0;
my $i;
return undef if ($nchar != $nbit);
for ($i = 0; $i < $nchar; $i++) {
$odat *= 2;
$odat += 1 if substr($str, $i, 1) eq "1";
}
return $odat;
}
#-------------------------------------------------------------------------------
sub conv_etime { # generate timestamp string
my ($ref_elast) = @_;
my $etime = get_time()-$time0;
my $str = sprintf "%12.6f ", $etime;
if (defined $ref_elast) {
my $dt = $etime - $$ref_elast;
$$ref_elast = $etime;
$str .= sprintf "(%10.6f) ", $dt;
}
return $str;
}
#-------------------------------------------------------------------------------
sub conv_dat9 {
my ($dat9) = @_;
return (($dat9 & 0x100) ? "1" : "0") . " " . conv_dat8($dat9);
}
#-------------------------------------------------------------------------------
sub conv_dat8 {
my ($dat8) = @_;
my $buf = "";
vec($buf,0,8) = int $dat8;
return unpack("B8",$buf);
}
#-------------------------------------------------------------------------------
sub conv_str2bytes { # string to bytelist; handle \n\r
my ($str,$dref,$esc) = @_;
while (length($str)) {
if ($esc && $str =~ /^\\n/) {
push @{$dref}, 0015; # send CR
$str = $';
} elsif ($esc && $str =~ /^\\r/) {
push @{$dref}, 0013; # send LF
$str = $';
} else {
my $chr = substr($str,0,1);
push @{$dref}, ord($chr);
$str = substr($str,1);
}
}
}
#-------------------------------------------------------------------------------
sub conv_buf2wlist { # string buffer -> word list
my ($buf) = @_;
my @sysbyt;
my $nw = int(length($buf)/2);
my $dref = [];
my $i;
push @sysbyt, unpack("C*", $buf);
for ($i=0; $i<$nw; $i++) {
my $bl = shift @sysbyt; # lsb is first
my $bh = shift @sysbyt;
push @{$dref}, 256*$bh + $bl;
}
return $dref;
}
#-------------------------------------------------------------------------------
sub conv_wlist2buf { # word list -> string buffer
my ($dref) = @_;
my @sysbyt;
my $buf;
foreach my $word (@{$dref}) {
my $bl = $word & 0xff;
my $bh = ($word>>8) & 0xff;
push @sysbyt, $bl; # lsb is first
push @sysbyt, $bh;
}
$buf = pack("C*", @sysbyt);
return $buf;
}
#-------------------------------------------------------------------------------
sub conv_byte2ascii2 { # byte -> 2 charcter ASCII display
my ($byte) = @_;
if ($byte >= 32 && $byte < 128) {
return chr($byte) . " ";
} else {
my $str = "..";
$str = "\\0" if $byte == 000; # NUL 000 -> \0
$str = "\\a" if $byte == 007; # BEL 007 -> \a
$str = "\\b" if $byte == 010; # BS 010 -> \b
$str = "\\t" if $byte == 011; # TAB 011 -> \t
$str = "\\n" if $byte == 012; # LF 012 -> \n
$str = "\\v" if $byte == 013; # VT 013 -> \v
$str = "\\f" if $byte == 014; # FF 014 -> \f
$str = "\\r" if $byte == 015; # CR 015 -> \r
return $str;
}
}
#-------------------------------------------------------------------------------
sub gconv_dat16 {
my ($dat,$dbase) = @_;
if ($dbase == 2) {
my $bufl = "";
my $bufh = "";
vec($bufl,0,8) = int ($dat & 0xff);
vec($bufh,0,8) = int (($dat>>8) & 0xff);
return unpack("B8",$bufh) . unpack("B8",$bufl);
} elsif ($dbase == 8) {
return sprintf "%6.6o", int $dat;
} elsif ($dbase == 16) {
return sprintf "%4.4x", int $dat;
} else {
return "??dbase??";
}
}
#-------------------------------------------------------------------------------
sub hdl_sigint { # SIGINT handler
if ($sigint_count == 1) {
print STDERR "\a"; # send beep
} elsif ($sigint_count == 2) {
print STDERR "pi_rri($curmode)-W: not responding on ^C, next will abort\n";
} elsif ($sigint_count == 3) {
print STDERR "pi_rri($curmode)-E: ^C abort\n";
exit(1);
}
$sigint_count += 1;
}
#-------------------------------------------------------------------------------
sub get_time {
my ($sec, $usec) = gettimeofday();
return $sec + 1.e-6 * $usec;
}
#-------------------------------------------------------------------------------
sub get_timestamp {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
return sprintf "%2.2d:%2.2d:%2.2d", $hour, $min, $sec;
}
#-------------------------------------------------------------------------------
sub filename_expand { # expand $nnn in name
my ($file) = @_;
my $fileexp = $file;
while($fileexp =~ /\$(\w+)/) {
if (exists $ENV{$1}) {
$fileexp = $` . $ENV{$1} . $';
} else {
printf "pi_rri-E: environment variable \$%s not defined\n", $1;
$fileexp = $` . "\$?" . $1 . "?" . $';
}
}
return $fileexp;
}
#-------------------------------------------------------------------------------
sub print_fatal {
my ($msg) = @_;
print STDERR "pi_rri($curmode)-F: $msg\n";
exit 1;
}
#-------------------------------------------------------------------------------
sub print_help {
print "usage: pi_rri\n";
print " --help this message\n";
print " --int force interactive mode\n";
print " --trace trace\n";
printf "CPREF %2.2x\n", CPREF;
printf "NCOMM %2.2x\n", NCOMM;
printf "CESC %2.2x\n", CESC;
printf "CEN1 %2.2x\n", CEN1;
}
Go to most recent revision | Compare with Previous | Blame | View Log