#!/usr/bin/perl -w
# $Id: tmuconv 1089 2018-12-19 10:45:41Z mueller $
#
# Copyright 2008-2018 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 3, 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
# 2018-12-18  1089   1.1.3  add and use bailout
# 2018-10-05  1053   1.1.2  use 'ca.*' instead of 'sy.*' fields
# 2015-11-01   712   1.1.1  BUGFIX: fix '.' handling for br/sob instructions
#                           BUGFIX: correct xor (now r,dst, and not src,r)
#                           br/sob offsets now octal; assume --t_id if no opts
# 2015-07-03   697   1.1    adapt to new DM_STAT_(SY|VM); add rhrp vector
# 2010-10-22   334   1.0.9  adapt to ibus V2 signals: req,we,dip->aval,re,we,rmw
# 2010-06-26   309   1.0.8  add ibimres.cacc/racc handling
# 2010-04-26   284   1.0.7  add error check for GetOptions
# 2009-09-19   240   1.0.6  add more VFETCH addr defs; add 2nd DL11 defs
# 2009-06-04   223   1.0.5  add IIST and PC11 defs
# 2009-05-03   212   1.0.4  add defs for mmu par/pdr's and some unibus dev's
# 2008-12-14   177   1.0.3  add -t_ru; use dp_ireg_we_last; add ibus names
# 2008-11-30   174   1.0.2  SPUSH and VFETCH tags for em cycles; psw in id lines
# 2008-04-25   138   1.0.1  show ccc/scc for code 000257/000277 in disassembler
# 2008-04-19   137   1.0    Initial version
#
# Current fields in tmu_ofile:
#   clkcycle:d
#   cpu:o
#   dp.pc:o
#   dp.psw:o
#   dp.ireg:o
#   dp.ireg_we:b
#   dp.ireg_we_last:b
#   dp.dsrc:o
#   dp.ddst:o
#   dp.dtmp:o
#   dp.dres:o
#   dp.gpr_adst:o
#   dp.gpr_mode:o
#   dp.gpr_bytop:b
#   dp.gpr_we:b
#   vm.ibmreq.aval:b
#   vm.ibmreq.re:b
#   vm.ibmreq.we:b
#   vm.ibmreq.rmw:b
#   vm.ibmreq.be0:b
#   vm.ibmreq.be1:b
#   vm.ibmreq.cacc:b
#   vm.ibmreq.racc:b
#   vm.ibmreq.addr:o
#   vm.ibmreq.din:o
#   vm.ibsres.ack:b
#   vm.ibsres.busy:b
#   vm.ibsres.dout:o
#   vm.emmreq.req:b
#   vm.emmreq.we:b
#   vm.emmreq.be:b
#   vm.emmreq.cancel:b
#   vm.emmreq.addr:o
#   vm.emmreq.din:o
#   vm.emsres.ack_r:b
#   vm.emsres.ack_w:b
#   vm.emsres.dout:o
#   co.cpugo:b
#   co.cpususp:b
#   co.suspint:b
#   co.suspext:b
#   ca.rd:b
#   ca.wr:b
#   ca.rdhit:b
#   ca.wrhit:b
#   ca.rdmem:b
#   ca.wrmem:b
#   ca.rdwait:b
#   ca.wrwait:b
#

use 5.14.0;                                 # require Perl 5.14 or higher
use strict;                                 # require strict checking
use FileHandle;

use Getopt::Long;

my %opts = ();

GetOptions(\%opts, "help", "dump", "cdump",
	   "t_id", "t_ru", "t_em", "t_ib")
  or bailout("bad command options");

my @var_name;
my @var_type;
my @var_dec;
my @var_oct;
my %name;

my @val_curr_text;
my @val_curr;
my @val_last;

my @reg_05 = ("------","------","------","------","------","------",   # set 0
              "------","------","------","------","------","------",); # set 1
my @reg_sp = ("------","------","------","------");	   # ksp,ssp,???,usp

my $ind_dp_pc;
my $ind_dp_psw;
my $ind_dp_ireg;
my $ind_dp_ireg_we;
my $ind_dp_ireg_we_last;
my $ind_dp_dres;
my $ind_dp_gpr_adst;
my $ind_dp_gpr_mode;
my $ind_dp_gpr_bytop;
my $ind_dp_gpr_we;

my $ind_vm_ibmreq_aval;
my $ind_vm_ibmreq_re;
my $ind_vm_ibmreq_we;
my $ind_vm_ibmreq_rmw;
my $ind_vm_ibmreq_be0;
my $ind_vm_ibmreq_be1;
my $ind_vm_ibmreq_cacc;
my $ind_vm_ibmreq_racc;
my $ind_vm_ibmreq_addr;
my $ind_vm_ibmreq_din;
my $ind_vm_ibsres_ack;
my $ind_vm_ibsres_busy;
my $ind_vm_ibsres_dout;

my $ind_vm_emmreq_req;
my $ind_vm_emmreq_we;
my $ind_vm_emmreq_be;
my $ind_vm_emmreq_cancel;
my $ind_vm_emmreq_addr;
my $ind_vm_emmreq_din;
my $ind_vm_emsres_ack_r;
my $ind_vm_emsres_ack_w;
my $ind_vm_emsres_dout;

my $ind_ca_rd;
my $ind_ca_wr;
my $ind_ca_rdhit;
my $ind_ca_wrhit;
my $ind_ca_rdmem;
my $ind_ca_wrmem;
my $ind_ca_rdwait;
my $ind_ca_wrwait;

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=>"swab", 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=>"rsrc"},
    {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=>"rsrc"},
    {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"}
  );

my %pdp11_regs = (			    # use simh naming convention
     177776=> "psw",
     177774=> "stklim",
     177772=> "pirq",
     177770=> "mbrk",
     177766=> "cpuerr",
     177764=> "sysid",
     177600=> "uipdr0",
     177602=> "uipdr1",
     177604=> "uipdr2",
     177606=> "uipdr3",
     177610=> "uipdr4",
     177612=> "uipdr5",
     177614=> "uipdr6",
     177616=> "uipdr7",
     177620=> "udpdr0",
     177622=> "udpdr1",
     177624=> "udpdr2",
     177626=> "udpdr3",
     177630=> "udpdr4",
     177632=> "udpdr5",
     177634=> "udpdr6",
     177636=> "udpdr7",
     177640=> "uipar0",
     177642=> "uipar1",
     177644=> "uipar2",
     177646=> "uipar3",
     177650=> "uipar4",
     177652=> "uipar5",
     177654=> "uipar6",
     177656=> "uipar7",
     177660=> "udpar0",
     177662=> "udpar1",
     177664=> "udpar2",
     177666=> "udpar3",
     177670=> "udpar4",
     177672=> "udpar5",
     177674=> "udpar6",
     177676=> "udpar7",
     177576=> "mmr2",
     177574=> "mmr1",
     177572=> "mmr0",
     177570=> "sdreg",			    # not a simh name !!
     177560=> "tia.csr",
     177562=> "tia.buf",
     177564=> "toa.csr",
     177566=> "toa.buf",
     177550=> "pr.csr",
     177552=> "pr.buf",
     177554=> "pp.csr",
     177556=> "pp.buf",
     177546=> "kl.csr",
     177514=> "lp.csr",
     177516=> "lp.buf",
     177500=> "ii.acr",
     177502=> "ii.adr",
     177400=> "rk.ds ",
     177402=> "rk.er ",
     177404=> "rk.cs ",
     177406=> "rk.wc ",
     177410=> "rk.ba ",
     177412=> "rk.da ",
     177414=> "rk.mr ",
     177416=> "rk.db ",
     177060=> "xor.cs",                     # XOR Tester
     176700=> "rp.cs1",
     176702=> "rp.wc ",
     176704=> "rp.ba ",
     176706=> "rp.da ",
     176710=> "rp.cs2",
     176712=> "rp.ds ",
     176714=> "rp.er1",
     176716=> "rp.as ",
     176720=> "rp.la ",
     176722=> "rp.db ",
     176724=> "rp.mr1",
     176726=> "rp.dt ",
     176730=> "rp.sn ",
     176732=> "rp.of ",
     176734=> "rp.dc ",
     176736=> "rp.m13",
     176740=> "rp.m14",
     176742=> "rp.m15",
     176744=> "rp.ec1",
     176746=> "rp.ec2",
     176750=> "rp.bae",
     176752=> "rp.cs3",
     176500=> "tib.cs",
     176502=> "tib.bu",
     176504=> "tob.cs",
     176506=> "tob.bu",
     174400=> "rl.cs ",
     174402=> "rl.ba ",
     174404=> "rl.da ",
     174406=> "rl.mp ",
     172540=> "kp.csr",
     172542=> "kp.buf",
     172544=> "kp.cnt",
     172520=> "tm.sr",
     172522=> "tm.cr",
     172524=> "tm.bc",
     172526=> "tm.ba",
     172530=> "tm.db",
     172532=> "tm.rl",
     172516=> "mmr3",
     172200=> "sipdr0",
     172202=> "sipdr1",
     172204=> "sipdr2",
     172206=> "sipdr3",
     172210=> "sipdr4",
     172212=> "sipdr5",
     172214=> "sipdr6",
     172216=> "sipdr7",
     172220=> "sdpdr0",
     172222=> "sdpdr1",
     172224=> "sdpdr2",
     172226=> "sdpdr3",
     172230=> "sdpdr4",
     172232=> "sdpdr5",
     172234=> "sdpdr6",
     172236=> "sdpdr7",
     172240=> "sipar0",
     172242=> "sipar1",
     172244=> "sipar2",
     172246=> "sipar3",
     172250=> "sipar4",
     172252=> "sipar5",
     172254=> "sipar6",
     172256=> "sipar7",
     172260=> "sdpar0",
     172262=> "sdpar1",
     172264=> "sdpar2",
     172266=> "sdpar3",
     172270=> "sdpar4",
     172272=> "sdpar5",
     172274=> "sdpar6",
     172276=> "sdpar7",
     172300=> "kipdr0",
     172302=> "kipdr1",
     172304=> "kipdr2",
     172306=> "kipdr3",
     172310=> "kipdr4",
     172312=> "kipdr5",
     172314=> "kipdr6",
     172316=> "kipdr7",
     172320=> "kdpdr0",
     172322=> "kdpdr1",
     172324=> "kdpdr2",
     172326=> "kdpdr3",
     172330=> "kdpdr4",
     172332=> "kdpdr5",
     172334=> "kdpdr6",
     172336=> "kdpdr7",
     172340=> "kipar0",
     172342=> "kipar1",
     172344=> "kipar2",
     172346=> "kipar3",
     172350=> "kipar4",
     172352=> "kipar5",
     172354=> "kipar6",
     172356=> "kipar7",
     172360=> "kdpar0",
     172362=> "kdpar1",
     172364=> "kdpar2",
     172366=> "kdpar3",
     172370=> "kdpar4",
     172372=> "kdpar5",
     172374=> "kdpar6",
     172376=> "kdpar7",
     160100=> "dz.csr",
     160102=> "dz.mp2",
     160104=> "dz.tcr",
     160106=> "dz.mp6"
);

autoflush STDOUT 1 if (-p STDOUT);          # autoflush if output into pipe

if (exists $opts{help}) {
  print_help();
  exit 0;
}

my $nopts = 0;                              # count options
$nopts += 1 if $opts{dump};
$nopts += 1 if $opts{cdump};
$nopts += 1 if $opts{t_id};
$nopts += 1 if $opts{t_ru};
$nopts += 1 if $opts{t_em};
$nopts += 1 if $opts{t_ib};

$opts{t_id} = 1 if $nopts == 0;             # if no opts, assume t_id

foreach my $file (@ARGV) {
  do_file($file);
}


#-------------------------------------------------------------------------------

sub do_file {
  my ($file) = @_;

  open IFILE,"<$file" or bailout("failed to open '$file': $!");

  my $idec_cyc = 0;
  my $change_cyc = 0;
  my $emreq_cyc = 0;
  my $emreq_str = "";
  my $ibreq_cyc = 0;
  my $ibreq_typ = "";
  my $ibreq_str = "";
  my $ibreq_nam = "";

  my $emcurr_we   = 0;			    # curr em write enable (or undef)
  my $emcurr_addr = undef;		    # curr em address
  my $emlast_we   = 0;			    # prev em write enable (or undef)
  my $emlast_addr = undef;		    # prev em address

  while (<IFILE>) {
    chomp;
    if (/^#\s+/) {
      @var_name = ();
      @var_type = ();
      my $dsc_str  = $';
      my @dsc_list = split /\s+/,$dsc_str;
      foreach my $dsc (@dsc_list) {
	if ($dsc =~ /^(.*):([bdo])$/) {
	  my $ind = scalar(@var_name);
	  $name{$1} = {ind=>$ind,
		       typ=>$2};
	  push @var_name, $1;
	  push @var_type, $2;
	  push @var_dec, $ind if $2 eq "d";
	  push @var_oct, $ind if $2 eq "o";
	} else {
	  print "tmuconv-E: bad descriptor $dsc\n";
	}
      }

      $ind_dp_pc            = $name{'dp.pc'}->{ind};
      $ind_dp_psw           = $name{'dp.psw'}->{ind};
      $ind_dp_ireg          = $name{'dp.ireg'}->{ind};
      $ind_dp_ireg_we       = $name{'dp.ireg_we'}->{ind};
      $ind_dp_ireg_we_last  = $name{'dp.ireg_we_last'}->{ind};
      $ind_dp_dres          = $name{'dp.dres'}->{ind};
      $ind_dp_gpr_adst      = $name{'dp.gpr_adst'}->{ind};
      $ind_dp_gpr_mode      = $name{'dp.gpr_mode'}->{ind};
      $ind_dp_gpr_bytop     = $name{'dp.gpr_bytop'}->{ind};
      $ind_dp_gpr_we        = $name{'dp.gpr_we'}->{ind};

      $ind_vm_ibmreq_aval   = $name{'vm.ibmreq.aval'}->{ind};
      $ind_vm_ibmreq_re     = $name{'vm.ibmreq.re'}->{ind};
      $ind_vm_ibmreq_we     = $name{'vm.ibmreq.we'}->{ind};
      $ind_vm_ibmreq_rmw    = $name{'vm.ibmreq.rmw'}->{ind};
      $ind_vm_ibmreq_be0    = $name{'vm.ibmreq.be0'}->{ind};
      $ind_vm_ibmreq_be1    = $name{'vm.ibmreq.be1'}->{ind};
      $ind_vm_ibmreq_cacc   = $name{'vm.ibmreq.cacc'}->{ind};
      $ind_vm_ibmreq_racc   = $name{'vm.ibmreq.racc'}->{ind};
      $ind_vm_ibmreq_addr   = $name{'vm.ibmreq.addr'}->{ind};
      $ind_vm_ibmreq_din    = $name{'vm.ibmreq.din'}->{ind};
      $ind_vm_ibsres_ack    = $name{'vm.ibsres.ack'}->{ind};
      $ind_vm_ibsres_busy   = $name{'vm.ibsres.busy'}->{ind};
      $ind_vm_ibsres_dout   = $name{'vm.ibsres.dout'}->{ind};

      $ind_vm_emmreq_req    = $name{'vm.emmreq.req'}->{ind};
      $ind_vm_emmreq_we     = $name{'vm.emmreq.we'}->{ind};
      $ind_vm_emmreq_be     = $name{'vm.emmreq.be'}->{ind};
      $ind_vm_emmreq_cancel = $name{'vm.emmreq.cancel'}->{ind};
      $ind_vm_emmreq_addr   = $name{'vm.emmreq.addr'}->{ind};
      $ind_vm_emmreq_din    = $name{'vm.emmreq.din'}->{ind};
      $ind_vm_emsres_ack_r  = $name{'vm.emsres.ack_r'}->{ind};
      $ind_vm_emsres_ack_w  = $name{'vm.emsres.ack_w'}->{ind};
      $ind_vm_emsres_dout   = $name{'vm.emsres.dout'}->{ind};

      $ind_ca_rd            = $name{'ca.rd'}->{ind};
      $ind_ca_wr            = $name{'ca.wr'}->{ind};
      $ind_ca_rdhit         = $name{'ca.rdhit'}->{ind};
      $ind_ca_wrhit         = $name{'ca.wrhit'}->{ind};
      $ind_ca_rdmem         = $name{'ca.rdmem'}->{ind};
      $ind_ca_wrmem         = $name{'ca.wrmem'}->{ind};
      $ind_ca_rdwait        = $name{'ca.rdwait'}->{ind};
      $ind_ca_wrwait        = $name{'ca.wrwait'}->{ind};

    } else {
      @val_last = @val_curr;
      my $notfirst = scalar(@val_last) > 0;

      $_ =~ s/^\s*//;
      $_ =~ s/\s*$//;
      @val_curr = split /\s+/,$_;
      if (scalar(@val_curr) != scalar(@var_name)) {
	printf "tmuconv-E: value list length mismatch, seen %d, expected %d\n",
	  scalar(@val_curr), scalar(@var_name);
	for (my $i=0; $i<scalar(@val_curr); $i++) {
	  printf "%3d: %s\n", $i,$val_curr[$i];
	}
	next;
      }

      @val_curr_text = @val_curr  if exists $opts{dump} || exists $opts{cdump};

      my $cyc_curr = int $val_curr[0];
      my $cyc_str  = sprintf "%8d", $cyc_curr;

      foreach my $ind (@var_dec) {
	$val_curr[$ind] = int ($val_curr[$ind]);
      }
      foreach my $ind (@var_oct) {
	$val_curr[$ind] = oct ($val_curr[$ind]);
      }

      my $id_str = "";
      my $ru_str = "";
      my $emres_str = "";
      my $emtyp_str = "";
      my $ibres_str = "";
      my $ibreq_we  = 0;
      my $ibreq_act = 0;

      if (exists $opts{dump} || exists $opts{cdump}) {
	my @val_change;
	my $any_change;

	for (my $i=1; $i<scalar(@val_curr); $i++) {
	  my $change = (not $notfirst) || ($val_curr[$i] != $val_last[$i]);
	  $val_change[$i] = $change;
	  $any_change |= $change;
	}

	if (exists $opts{dump} || $any_change) {
	  printf "cycle $cyc_str %s", "-" x 32;
	  if ($notfirst && exists $opts{cdump}) {
	    printf " (%d)",$cyc_curr-$change_cyc;
	  }
	  print "\n";

	  for (my $i=1; $i<scalar(@val_curr); $i++) {
	    my $oper = $val_change[$i] ? "<=" : " =";
	    if (exists $opts{dump} || $val_change[$i]) {
	      printf "   %-16s:%s %s %s\n", $var_name[$i], $var_type[$i],
		                            $oper, $val_curr_text[$i];
	    }
	  }
	  $change_cyc = $cyc_curr;
	}
      }
#
# handle t_id
#   uses cycles with dp_ireg_we = '1'
#
      if (exists $opts{t_id} and $notfirst) {
	if ($val_curr[$ind_dp_ireg_we_last]) {
	  my $pc   = $val_curr[$ind_dp_pc] - 2;
	  my $psw  = $val_curr[$ind_dp_psw];
	  my $ireg = $val_curr[$ind_dp_ireg];
	  my $code = code2mnemo($ireg);
	  $id_str = sprintf "       %6.6o %6.6o %6.6o  %s",
	                    $pc, $psw, $ireg, $code;
	  $id_str .= " " x (22-length($code));
	  $id_str .= sprintf " (%d)",$cyc_curr-$idec_cyc;
	  $idec_cyc = $cyc_curr;
	}
      }
#

#    1706 ru  0 06   000002 000002 000002 000002 000002 000002 000002  ksp
#    1694 id         002012 000340 010036  mov  r0,@(sp)+       (8)


# handle t_ru
#   uses cycles with dp_gpr_we = '1'
#
      if (exists $opts{t_ru}) {
	if ($val_curr[$ind_dp_gpr_we]) {
	  my $adst  = $val_curr[$ind_dp_gpr_adst];
	  my $mode  = $val_curr[$ind_dp_gpr_mode];
	  my $bytop = $val_curr[$ind_dp_gpr_bytop];
	  my $psw   = $val_curr[$ind_dp_psw];
	  my $dres  = $val_curr[$ind_dp_dres];
	  my $rset  = $psw>>11 & 01;
	  $ru_str  = sprintf "%o %o%o   %6.6o", $bytop, $rset, $adst, $dres;
          $ru_str .= " ";
	  if ($adst eq "7") {
	    $ru_str .= "pc";
	  } elsif ($adst eq "6") {
	    $reg_sp[$mode] = sprintf "%6.6o",$dres;
	    $ru_str .= $reg_sp[0];
	    $ru_str .= ($mode == 0) ? "*" : " ";
	    $ru_str .= $reg_sp[1];
	    $ru_str .= ($mode == 1) ? "*" : " ";
	    $ru_str .= $reg_sp[3];
	    $ru_str .= ($mode == 3) ? "*" : " ";
	    $ru_str .= " ksp" if $mode eq "0";
	    $ru_str .= " ssp" if $mode eq "1";
	    $ru_str .= " usp" if $mode eq "3";
	  } else {
	    my $rbase = ($rset==0) ? 0 : 6;
	    $reg_05[$rbase+$adst] = sprintf "%6.6o",$dres;
	    for (my $i=0; $i<6; $i++) {
	      $ru_str .= $reg_05[$rbase+$i];
	      $ru_str .= ($adst==$i) ? "*" : " ";
	    }
	    $ru_str .= sprintf " r%o%o", $rset, $adst;
	  }
	}
      }
#
# handle t_em
#   uses cycles with vm_emmreq_req = '1'
#                    vm_emsres_ack_r = '1'
#                    vm_emsres_ack_w = '1'
#                    vm_emsreq_cancel = '1'
#
      if (exists $opts{t_em}) {
	if ($val_curr[$ind_vm_emmreq_req]) {
	  $emreq_cyc = $cyc_curr;
	  $emreq_str = sprintf "%s %s %8.8o",
	                 ($val_curr[$ind_vm_emmreq_we] ? "w" : "r"),
	                 $val_curr[$ind_vm_emmreq_be],
	                 $val_curr[$ind_vm_emmreq_addr];
	  $emcurr_we   = $val_curr[$ind_vm_emmreq_we];
	  $emcurr_addr = $val_curr[$ind_vm_emmreq_addr];
	  if ($emcurr_we) {
	    $emreq_str .= sprintf " %6.6o", $val_curr[$ind_vm_emmreq_din];
	  } else {
	    $emreq_str .= " " x 7;
	  }
	}
	if ($val_curr[$ind_vm_emsres_ack_r] ||
	    $val_curr[$ind_vm_emsres_ack_w] ||
	    $val_curr[$ind_vm_emmreq_cancel]) {
	  $emres_str = sprintf "%s%s%s%s",
	    $val_curr[$ind_vm_emmreq_cancel],
	    $val_curr[$ind_vm_emsres_ack_r],
	    $val_curr[$ind_vm_emsres_ack_w],
	    $val_curr[$ind_ca_rdhit];
	  if ($val_curr[$ind_vm_emmreq_cancel]) {
	    $emreq_str .= " cancel";
	    $emcurr_we = undef;
	  } else {
	    if ($val_curr[$ind_vm_emsres_ack_r]) {
	      $emreq_str .= sprintf " %6.6o", $val_curr[$ind_vm_emsres_dout];
	    } else {
	      $emreq_str .= " " x 7;
	    }
	    if (defined $emlast_we && $emcurr_we == $emlast_we) {
	      if ($emcurr_we && $emcurr_addr == $emlast_addr-2) {
		$emtyp_str = "SPUSH";
	      } elsif ((not $emcurr_we) && $emcurr_addr == $emlast_addr+2 &&
		      $emcurr_addr < 0400 && ($emcurr_addr % 04) == 02) {
		$emtyp_str = "VFETCH";
		$emtyp_str .= " 004 ill.inst"   if ($emlast_addr == 0004);
		$emtyp_str .= " 010 res.inst"   if ($emlast_addr == 0010);
		$emtyp_str .= " 014 BPT"        if ($emlast_addr == 0014);
		$emtyp_str .= " 020 IOT"        if ($emlast_addr == 0020);
		$emtyp_str .= " 030 EMT"        if ($emlast_addr == 0030);
		$emtyp_str .= " 034 TRAP"       if ($emlast_addr == 0034);
		$emtyp_str .= " 060 DL11-TTI"   if ($emlast_addr == 0060);
		$emtyp_str .= " 064 DL11-TTO"   if ($emlast_addr == 0064);
		$emtyp_str .= " 070 PC11-PTR"   if ($emlast_addr == 0070);
		$emtyp_str .= " 074 PC11-PTP"   if ($emlast_addr == 0074);
		$emtyp_str .= " 100 KW11-L"     if ($emlast_addr == 0100);
		$emtyp_str .= " 104 KW11-P"     if ($emlast_addr == 0104);
		$emtyp_str .= " 160 RL11"       if ($emlast_addr == 0160);
		$emtyp_str .= " 200 LP11"       if ($emlast_addr == 0200);
		$emtyp_str .= " 220 RK11"       if ($emlast_addr == 0220);
		$emtyp_str .= " 224 TM11"       if ($emlast_addr == 0224);
		$emtyp_str .= " 240 PIRQ"       if ($emlast_addr == 0240);
		$emtyp_str .= " 244 FPP exp"    if ($emlast_addr == 0244);
		$emtyp_str .= " 250 MMU trap"   if ($emlast_addr == 0250);
		$emtyp_str .= " 254 RHRP"       if ($emlast_addr == 0254);
		$emtyp_str .= " 260 IIST"       if ($emlast_addr == 0260);
		$emtyp_str .= " 300 DL11-2-TTI" if ($emlast_addr == 0300);
		$emtyp_str .= " 304 DL11-2-TTO" if ($emlast_addr == 0304);
	      }
	    }
	  }
	  $emlast_we   = $emcurr_we;
	  $emlast_addr = $emcurr_addr;
	}
      }
#
# handle t_ib
#   uses cycles with sy_ibmreq_re = '1' or sy_ibmreq_we = '1'
#                    sy_ibsres_ack = '1'
#                    vm_ibsres_busy '1' -> '0' transition
#
      if (exists $opts{t_ib}) {
	if ($val_curr[$ind_vm_ibmreq_re] || $val_curr[$ind_vm_ibmreq_we]) {
	  my $addr_str = sprintf "%6.6o", $val_curr[$ind_vm_ibmreq_addr];
	  $ibreq_cyc = $cyc_curr;
	  $ibreq_typ = sprintf "%s%s",
	                 ($val_curr[$ind_vm_ibmreq_cacc] ? "c" : "-"),
	                 ($val_curr[$ind_vm_ibmreq_racc] ? "r" : "-");
	  $ibreq_str = sprintf "%s%s%s%s   %s",
	                 ($val_curr[$ind_vm_ibmreq_we]  ? "w" : "r"),
                         ($val_curr[$ind_vm_ibmreq_rmw] ? "m" : " "),
	                 $val_curr[$ind_vm_ibmreq_be1],
	                 $val_curr[$ind_vm_ibmreq_be0],
	                 $addr_str;
	  $ibreq_we  = $val_curr[$ind_vm_ibmreq_we];
	  $ibreq_act = 1;
	  if ($ibreq_we) {
	    $ibreq_str .= sprintf " %6.6o", $val_curr[$ind_vm_ibmreq_din];
	  } else {
	    $ibreq_str .= " " x 7;
	  }
	  $ibreq_nam = $pdp11_regs{$addr_str};
	  $ibreq_nam = "" if not defined $ibreq_nam;
	}

	if ($val_curr[$ind_vm_ibsres_ack]) {
	  $ibreq_act = 0;
	  $ibres_str .= sprintf "   %s", $val_curr[$ind_vm_ibsres_ack];
	  if (not $ibreq_we) {
	    $ibreq_str .= sprintf " %6.6o", $val_curr[$ind_vm_ibsres_dout];
	  } else {
	    $ibreq_str .= " " x 7;
	  }
	}

	if ($ibreq_act && $val_curr[$ind_vm_ibsres_busy]==0) {
	  $ibres_str .= "no ACK, no BUSY";
	  $ibreq_act = 0;
	}
      }

      print "$cyc_str id    $id_str\n"     if $id_str;
      print "$cyc_str ru    $ru_str\n"     if $ru_str;
      if ($emres_str) {
	printf "$cyc_str em    $emreq_str  $emres_str (%d) $emtyp_str\n",
	  $cyc_curr-$emreq_cyc;
      }
      if ($ibres_str) {
	printf "$cyc_str ib %s $ibreq_str  $ibres_str (%d) $ibreq_nam\n",
	  $ibreq_typ, $cyc_curr-$ibreq_cyc;
      }
    }
  }

  close IFILE;
}

#-------------------------------------------------------------------------------

sub code2mnemo {
  my ($code) = @_;

  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;

      } elsif ($type eq "1arg" or $type eq "1fpp") {
	my $dst = $code & 077;
	my $dst_str = regmod($dst);
	return "$name $dst_str";

      } elsif ($type eq "2arg") {
	my $src = ($code>>6) & 077;
	my $dst = $code & 077;
	my $src_str = regmod($src);
	my $dst_str = regmod($dst);
	return "$name $src_str,$dst_str";

      } elsif ($type eq "rdst") {
	my $reg = ($code>>6) & 07;
	my $src = $code & 077;
	my $src_str = regmod($src);
	return "$name $src_str,r$reg";

      } 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";

      } elsif ($type eq "br")   {
        # Note: in MACRO-11 syntax . refers to the address of the instruction
        #       the opcode has offset relative to PC after instruction fetch
        #       so   000776  --> br .-2
        #            000777  --> br .
        #            000400  --> br .+2
        #
	my $off  = $code & 0177;
	my $sign = "?";
	if ($code & 0200) {                 # negative offsets
	  $sign = "-";
	  $off  = ((~$off) & 0177)+1;
          $off  = $off - 1;                   # refer to address of instruction
	} else {                            # positive offsets
	  $sign = "+";
          $off  = $off + 1;                   # refer to address of instruction
        }
	return sprintf "$name .%s%o", $sign, abs(2*$off);

      } elsif ($type eq "sob")  {
        # Note: like in br type instructions, asm syntax and opcode differ by one
	my $reg = ($code>>6) & 07;
	my $off = $code & 077;
	return sprintf "$name r%d,.-%o", $reg, 2*($off-1);

      } elsif ($type eq "trap") {
	my $off = $code & 0377;
	return sprintf "$name %3.3o", $off;

      } elsif ($type eq "spl")  {
	my $off = $code & 07;
	return sprintf "$name %d", $off;

      } elsif ($type eq "ccop")  {
	my $cc = $code & 017;
	return "nop" if ($cc == 0);
	return "ccc" if ($code == 0257);
	return "scc" 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;

      } elsif ($type eq "rsrc")  {
	my $reg = ($code>>6) & 07;
	my $dst = $code & 077;
	my $dst_str = regmod($dst);
	return "$name r$reg,$dst_str";

      } elsif ($type eq "mark") {
	my $off = $code & 077;
	return sprintf "$name %3.3o", $off;

      } elsif ($type eq "rfpp") {
	my $reg = ($code>>6) & 03;
	my $dst = $code & 077;
	my $dst_str = regmod($dst,"f");
	return "$name f$reg,$dst_str";

      } else {
	return "?type?";
      }
    }
  }
  return "=inval=";
}

#-------------------------------------------------------------------------------
sub regmod {
  my ($regmod,$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;
  } elsif ($mod == 1) {	  	        # mode 1:    (Rx)
    return "($reg_str)";
  } elsif ($mod == 2 || $mod == 3) {    # mode 2/3:  (Rx)+   @(Rx)+
    my $ind = ($mod == 3) ? "@" : "";
    if ($reg != 7) {		          # if reg != pc
      return "$ind($reg_str)+";
    } else {			          # if reg == pc
      my $str = sprintf "$ind#nnn";     # 27 -> #nnn;  37 -> @#nnn
      return $str;
    }
  } elsif ($mod == 4 || $mod == 5) {    # mode 4/5:  -(Rx)   @-(Rx)
    my $ind = ($mod == 5) ? "@" : "";
    return "$ind-($reg_str)";
  } elsif ($mod == 6 || $mod == 7) {    # mode 6/7:  nn(Rx)  @nn(Rx)
    my $ind = ($mod == 7) ? "@" : "";
    return "${ind}nnn($reg_str)";
  }
}

#-------------------------------------------------------------------------------

sub bailout {
  my ($msg) = @_;
  print STDERR "tmuconv-F: $msg\n";
  exit 1;
}

#-------------------------------------------------------------------------------

sub print_help {
  print "usage: tmuconf <command> file\n";
  print "  --help           this message\n";
  print "  --dump           dump all information\n";
  print "  --cdump          dump only changes relative to prev cycle\n";
  print "  --t_id           trace instruction decodes\n";
  print "  --t_ru           trace register updates\n";
  print "  --t_em           trace em transactions\n";
  print "  --t_ib           trace ib transactions\n";
}
