This source file includes following definitions.
- mtp_show_nunits
- mtp_set_nunits
- mtp_show_boot_drive
- mtp_set_boot_drive
- mtp_show_device_name
- mtp_set_device_name
- mtp_reset
- mt_rewind
- mt_show_nunits
- mt_set_nunits
- mt_show_device_name
- mt_set_device_name
- mt_show_tape_path
- mt_set_tape_path
- mt_add_tape_search_path
- mt_show_tape_search_paths
- mt_set_capac
- signal_tape
- tape_set_ready
- mt_reset
- deterimeFullTapeFileName
- loadTape
- unloadTape
- mt_init
- mt_exit
- mtReadRecord
- mtReadCtrlMainMem
- mtInitRdMem
- mtWRCtrlRegs
- mtInitWrMem
- mtMTPWr
- mtWriteRecord
- surveyDevices
- mt_iom_cmd
- simh_tape_msg
- attachTape
- mount_tape
- detachTape
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 
  11 
  12 
  13 
  14 
  15 
  16 
  17 
  18 
  19 
  20 
  21 
  22 
  23 
  24 
  25 
  26 
  27 
  28 
  29 
  30 
  31 
  32 
  33 
  34 
  35 
  36 #include <stdio.h>
  37 #include <signal.h>
  38 #include <ctype.h>
  39 #include <dirent.h>
  40 
  41 #include "dps8.h"
  42 #include "dps8_sys.h"
  43 #include "dps8_faults.h"
  44 #include "dps8_scu.h"
  45 #include "dps8_iom.h"
  46 #include "dps8_cable.h"
  47 #include "dps8_cpu.h"
  48 #include "dps8_utils.h"
  49 #include "dps8_mt.h"
  50 
  51 #define DBG_CTR 1
  52 
  53 #ifdef TESTING
  54 # undef FREE
  55 # define FREE(p) free(p)
  56 #endif 
  57 
  58 
  59 
  60 
  61 
  62 
  63 
  64 
  65 
  66 
  67 
  68 
  69 
  70 
  71 
  72 
  73 
  74 
  75 
  76 
  77 
  78 
  79 
  80 
  81 
  82 
  83 
  84 
  85 
  86 
  87 
  88 
  89 
  90 
  91 
  92 
  93 
  94 
  95 
  96 
  97 
  98 
  99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 
 111 
 112 
 113 
 114 
 115 
 116 
 117 
 118 
 119 
 120 
 121 
 122 
 123 
 124 
 125 #include "sim_tape.h"
 126 
 127 static DEBTAB mt_dt [] =
 128   {
 129     { "NOTIFY", DBG_NOTIFY, NULL },
 130     { "INFO",   DBG_INFO,   NULL },
 131     { "ERR",    DBG_ERR,    NULL },
 132     { "WARN",   DBG_WARN,   NULL },
 133     { "DEBUG",  DBG_DEBUG,  NULL },
 134     { "TRACE",  DBG_TRACE,  NULL },
 135     { "ALL",    DBG_ALL,    NULL }, 
 136     { NULL,     0,          NULL }
 137   };
 138 
 139 
 140 
 141 
 142 
 143 
 144 
 145 #define MTP_UNIT_IDX(uptr) ((uptr) - mtp_unit)
 146 #define N_MTP_UNITS 1 
 147 
 148 static struct mtp_state_s
 149   {
 150     uint boot_drive;
 151     char device_name [MAX_DEV_NAME_LEN];
 152   } mtp_state [N_MTP_UNITS_MAX];
 153 
 154 static t_stat mtp_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     
 155                                UNUSED int val, UNUSED const void * desc)
 156   {
 157     sim_printf ("Number of MTP controllers in the system is %d\n",
 158                 mtp_dev.numunits);
 159     return SCPE_OK;
 160   }
 161 
 162 static t_stat mtp_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     
 163                               const char * cptr, UNUSED void * desc)
 164   {
 165     if (! cptr)
 166       return SCPE_ARG;
 167     int n = atoi (cptr);
 168     if (n < 0 || n > N_MTP_UNITS_MAX)
 169       return SCPE_ARG;
 170     mtp_dev.numunits = (uint32) n;
 171     return SCPE_OK;
 172   }
 173 
 174 static t_stat mtp_show_boot_drive (UNUSED FILE * st, UNIT * uptr,
     
 175                                    UNUSED int val, UNUSED const void * desc)
 176   {
 177     long mtp_unit_idx = MTP_UNIT_IDX (uptr);
 178     if (mtp_unit_idx < 0 || mtp_unit_idx >= N_MTP_UNITS_MAX)
 179       {
 180         sim_printf ("Controller unit number out of range\n");
 181         return SCPE_ARG;
 182       }
 183     sim_printf ("boot     : %u",
 184                 mtp_state[mtp_unit_idx].boot_drive);
 185     return SCPE_OK;
 186   }
 187 
 188 static t_stat mtp_set_boot_drive (UNIT * uptr, UNUSED int32 value,
     
 189                                  const char * cptr, UNUSED void * desc)
 190   {
 191     long mtp_unit_idx = MTP_UNIT_IDX (uptr);
 192     if (mtp_unit_idx < 0 || mtp_unit_idx >= N_MTP_UNITS_MAX)
 193       {
 194         sim_printf ("Controller unit number out of range\n");
 195         return SCPE_ARG;
 196       }
 197     if (! cptr)
 198       return SCPE_ARG;
 199     int n = (int) atoi (cptr);
 200     if (n < 0 || n >= N_DEV_CODES)
 201       return SCPE_ARG;
 202     mtp_state[mtp_unit_idx].boot_drive = (uint) n;
 203     return SCPE_OK;
 204   }
 205 
 206 UNIT mtp_unit [N_MTP_UNITS_MAX] = {
 207 #ifdef NO_C_ELLIPSIS
 208   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 209   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 210   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 211   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 212   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 213   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 214   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 215   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 216   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 217   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 218   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 219   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 220   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 221   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 222   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 223   { UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 224 #else
 225   [0 ... N_MTP_UNITS_MAX-1] = {
 226     UDATA (NULL, 0, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 227   }
 228 #endif
 229 };
 230 
 231 static t_stat mtp_show_device_name (UNUSED FILE * st, UNIT * uptr,
     
 232                                     UNUSED int val, UNUSED const void * desc)
 233   {
 234     int n = (int) MTP_UNIT_IDX (uptr);
 235     if (n < 0 || n >= N_MTP_UNITS_MAX)
 236       return SCPE_ARG;
 237     sim_printf("name     : %s", mtp_state [n].device_name);
 238     return SCPE_OK;
 239   }
 240 
 241 static t_stat mtp_set_device_name (UNIT * uptr, UNUSED int32 value,
     
 242                                    const char * cptr, UNUSED void * desc)
 243   {
 244     int n = (int) MTP_UNIT_IDX (uptr);
 245     if (n < 0 || n >= N_MTP_UNITS_MAX)
 246       return SCPE_ARG;
 247     if (cptr)
 248       {
 249         strncpy (mtp_state[n].device_name, cptr, MAX_DEV_NAME_LEN-1);
 250         mtp_state[n].device_name[MAX_DEV_NAME_LEN-1] = 0;
 251       }
 252     else
 253       mtp_state[n].device_name[0] = 0;
 254     return SCPE_OK;
 255   }
 256 
 257 static MTAB mtp_mod [] =
 258   {
 259     {
 260       MTAB_XTD | MTAB_VDV | \
 261       MTAB_NMO | MTAB_VALR,                 
 262       0,                                    
 263       "NUNITS",                             
 264       "NUNITS",                             
 265       mtp_set_nunits,                       
 266       mtp_show_nunits,                      
 267       "Number of TAPE units in the system", 
 268       NULL                                  
 269     },
 270     {
 271       MTAB_XTD | MTAB_VUN | MTAB_VALR,      
 272       0,                                    
 273       "BOOT_DRIVE",                         
 274       "BOOT_DRIVE",                         
 275       mtp_set_boot_drive,                   
 276       mtp_show_boot_drive,                  
 277       "Select the boot drive",              
 278       NULL                                  
 279     },
 280     {
 281       MTAB_XTD | MTAB_VUN | \
 282       MTAB_VALR | MTAB_NC,                  
 283       0,                                    
 284       "NAME",                               
 285       "NAME",                               
 286       mtp_set_device_name,                  
 287       mtp_show_device_name,                 
 288       "Set the device name",                
 289       NULL                                  
 290     },
 291     { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
 292   };
 293 
 294 static t_stat mtp_reset (UNUSED DEVICE * dptr)
     
 295   {
 296     return SCPE_OK;
 297   }
 298 
 299 DEVICE mtp_dev =
 300   {
 301     "MTP",            
 302     mtp_unit,         
 303     NULL,             
 304     mtp_mod,          
 305     N_MTP_UNITS,      
 306     10,               
 307     31,               
 308     1,                
 309     8,                
 310     9,                
 311     NULL,             
 312     NULL,             
 313     mtp_reset,        
 314     NULL,             
 315     NULL,             
 316     NULL,             
 317     NULL,             
 318     DEV_DEBUG,        
 319     0,                
 320     mt_dt,            
 321     NULL,             
 322     NULL,             
 323     NULL,             
 324     NULL,             
 325     NULL,             
 326     NULL,             
 327     NULL
 328   };
 329 
 330 
 331 
 332 
 333 
 334 
 335 
 336 #define MT_UNIT_NUM(uptr) ((uptr) - mt_unit)
 337 
 338 struct tape_state tape_states [N_MT_UNITS_MAX];
 339 static const char * simh_tape_msg (int code); 
 340 
 341 static char tape_path_prefix [PATH_MAX+2];
 342 
 343 
 344 #define LABEL_MAX 32
 345 
 346 #define N_MT_UNITS 1 
 347 
 348 UNIT mt_unit [N_MT_UNITS_MAX] = {
 349     
 350     
 351     
 352     
 353     
 354     
 355     
 356 
 357 #ifdef NO_C_ELLIPSIS
 358   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 359   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 360   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 361   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 362   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 363   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 364   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 365   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 366   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 367   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 368   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 369   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 370   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 371   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 372   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 373   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 374   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 375   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 376   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 377   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 378   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 379   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 380   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 381   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 382   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 383   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 384   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 385   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 386   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 387   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 388   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 389   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 390   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL },
 391   { UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL }
 392 #else
 393   [0 ... N_MT_UNITS_MAX-1] = {
 394     UDATA (NULL, UNIT_ATTABLE |  UNIT_ROABLE | UNIT_DISABLE | UNIT_IDLE, 0), 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL
 395   }
 396 #endif
 397 };
 398 
 399 #define UNIT_WATCH (1 << MTUF_V_UF)
 400 
 401 static t_stat mt_rewind (UNIT * uptr, UNUSED int32 value,
     
 402                          UNUSED const char * cptr, UNUSED void * desc)
 403   {
 404     return sim_tape_rewind (uptr);
 405   }
 406 
 407 static t_stat mt_show_nunits (UNUSED FILE * st, UNUSED UNIT * uptr,
     
 408                               UNUSED int val, UNUSED const void * desc)
 409   {
 410     sim_printf("Number of TAPE units in system is %d\n", tape_dev . numunits);
 411     return SCPE_OK;
 412   }
 413 
 414 static t_stat mt_set_nunits (UNUSED UNIT * uptr, UNUSED int32 value,
     
 415                              const char * cptr, UNUSED void * desc)
 416   {
 417     if (! cptr)
 418       return SCPE_ARG;
 419     int n = atoi (cptr);
 420     if (n < 1 || n > N_MT_UNITS_MAX)
 421       return SCPE_ARG;
 422     tape_dev . numunits = (uint32) n;
 423     return SCPE_OK;
 424   }
 425 
 426 static t_stat mt_show_device_name (UNUSED FILE * st, UNIT * uptr,
     
 427                                    UNUSED int val, UNUSED const void * desc)
 428   {
 429     int n = (int) MT_UNIT_NUM (uptr);
 430     if (n < 0 || n >= N_MT_UNITS_MAX)
 431       return SCPE_ARG;
 432     if (tape_states[n].device_name[1] == 0)
 433       sim_printf("name     : default");
 434     else
 435       sim_printf("name     : %s", tape_states[n].device_name);
 436     return SCPE_OK;
 437   }
 438 
 439 static t_stat mt_set_device_name (UNUSED UNIT * uptr, UNUSED int32 value,
     
 440                                   UNUSED const char * cptr, UNUSED void * desc)
 441   {
 442     int n = (int) MT_UNIT_NUM (uptr);
 443     if (n < 0 || n >= N_MT_UNITS_MAX)
 444       return SCPE_ARG;
 445     if (cptr)
 446       {
 447         strncpy (tape_states [n] . device_name, cptr, MAX_DEV_NAME_LEN - 1);
 448         tape_states [n] . device_name [MAX_DEV_NAME_LEN - 1] = 0;
 449       }
 450     else
 451       tape_states [n] . device_name [0] = 0;
 452     return SCPE_OK;
 453   }
 454 
 455 static t_stat mt_show_tape_path (UNUSED FILE * st, UNUSED UNIT * uptr,
     
 456                                  UNUSED int val, UNUSED const void * desc)
 457   {
 458     sim_printf("TAPE DEFAULT_PATH: %s\n", tape_path_prefix);
 459     return SCPE_OK;
 460   }
 461 
 462 typedef struct path_node PATH_ENTRY;
 463 
 464 struct path_node
 465   {
 466       size_t prefix_len;
 467       char label_prefix[LABEL_MAX + 1];
 468       char dir[PATH_MAX + 1];
 469       PATH_ENTRY *next_entry;
 470   };
 471 
 472 static PATH_ENTRY *search_list_head = NULL;
 473 static PATH_ENTRY *search_list_tail = NULL;
 474 
 475 static t_stat mt_set_tape_path (UNUSED UNIT * uptr, UNUSED int32 value,
     
 476                              const char * cptr, UNUSED void * desc)
 477   {
 478     if (! cptr)
 479       return SCPE_ARG;
 480 
 481     size_t len = strlen(cptr);
 482 
 483     
 484     if (len >= (sizeof(tape_path_prefix) - (LABEL_MAX + 2)))
 485       return SCPE_ARG;
 486 
 487     
 488     PATH_ENTRY *current_entry = search_list_head;
 489     while (current_entry != NULL)
 490       {
 491         PATH_ENTRY *old_entry = current_entry;
 492         current_entry = current_entry->next_entry;
 493         FREE(old_entry);
 494       }
 495 
 496     search_list_head = NULL;
 497     search_list_tail = NULL;
 498 
 499     
 500     DIR * dp;
 501     dp = opendir (cptr);
 502     if (! dp)
 503       {
 504         sim_warn ("\rInvalid '%s' ", cptr);
 505         perror ("opendir");
 506         sim_warn ("\r\n");
 507         return SCPE_ARG;
 508       }
 509 
 510     closedir(dp);
 511 
 512     
 513     strncpy (tape_path_prefix, cptr, (sizeof(tape_path_prefix)-1));
 514     if (len > 0)
 515       {
 516         if (tape_path_prefix[len - 1] != '/')
 517           {
 518             tape_path_prefix[len++] = '/';
 519             tape_path_prefix[len] = 0;
 520           }
 521       }
 522 
 523     return SCPE_OK;
 524   }
 525 
 526 static t_stat mt_add_tape_search_path(UNUSED UNIT * uptr, UNUSED int32 value,
     
 527                              const char * cptr, UNUSED void * desc)
 528   {
 529     if (! cptr)
 530       return SCPE_ARG;
 531 
 532     if (!tape_path_prefix[0])
 533       {
 534         sim_print("ERROR: DEFAULT_PATH must be set before ADD_PATH is used.\n");
 535         return SCPE_ARG;
 536       }
 537 
 538     size_t len = strlen(cptr);
 539 
 540     char buffer[len + 2];
 541     char prefix[len + 2];
 542     char dir[len + 2];
 543 
 544     strcpy(buffer, cptr);
 545 
 546     
 547     char *token = strtok(buffer, "=");
 548     if (token == NULL)
 549       {
 550         return SCPE_ARG;
 551       }
 552 
 553     strcpy(prefix, token);
 554 
 555     token = strtok(NULL, "=");
 556     if (token == NULL)
 557       {
 558         sim_print("ERROR: ADD_PATH parameter must be specified as [prefix]=[dir]\n");
 559         sim_print("   i.e. SET TAPE ADD_PATH=BK=./tapes/backups\n");
 560         return SCPE_ARG;
 561       }
 562 
 563     strcpy(dir, token);
 564 
 565     if (strtok(NULL, "=") != NULL)
 566       {
 567         return SCPE_ARG;
 568       }
 569 
 570     size_t prefix_len = strlen(prefix);
 571     if ((prefix_len > LABEL_MAX) || (prefix_len < 1))
 572       {
 573         return SCPE_ARG;
 574       }
 575 
 576     size_t dir_len = strlen(dir);
 577 
 578     
 579     if (dir_len > (PATH_MAX - 1))
 580       {
 581         return SCPE_ARG;
 582       }
 583 
 584     
 585     DIR * dp;
 586     dp = opendir (dir);
 587     if (! dp)
 588       {
 589         sim_warn ("\rInvalid '%s' ", dir);
 590         perror ("opendir");
 591         sim_warn ("\r\n");
 592         return SCPE_ARG;
 593       }
 594 
 595     closedir(dp);
 596 
 597     PATH_ENTRY *new_entry = malloc(sizeof(PATH_ENTRY));
 598     if (!new_entry)
 599       {
 600         fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 601                  __func__, __FILE__, __LINE__);
 602 #if defined(USE_BACKTRACE)
 603 # ifdef SIGUSR2
 604         (void)raise(SIGUSR2);
 605          
 606 # endif 
 607 #endif 
 608         abort();
 609       }
 610     new_entry->prefix_len = prefix_len;
 611     strcpy(new_entry->label_prefix, prefix);
 612     strcpy(new_entry->dir, dir);
 613 
 614     if (new_entry->dir[dir_len - 1] != '/')
 615       {
 616         new_entry->dir[dir_len++] = '/';
 617         new_entry->dir[dir_len] = 0;
 618       }
 619 
 620     new_entry->next_entry = NULL;
 621     if (search_list_tail == NULL)
 622       {
 623         search_list_head = new_entry;
 624         search_list_tail = new_entry;
 625       }
 626     else
 627       {
 628         search_list_tail->next_entry = new_entry;
 629         search_list_tail = new_entry;
 630       }
 631 
 632     return SCPE_OK;
 633   }
 634 
 635 static t_stat mt_show_tape_search_paths(UNUSED FILE * st, UNUSED UNIT * uptr,
     
 636                                  UNUSED int val, UNUSED const void * desc)
 637   {
 638     sim_print("Tape directory search paths:\n");
 639     sim_printf("%-32s %s\n", "Prefix", "Directory");
 640     sim_printf("%-32s %s\n", "------", "---------");
 641     PATH_ENTRY *current_entry = search_list_head;
 642     while (current_entry != NULL)
 643       {
 644           sim_printf("%-32s %s\n", current_entry->label_prefix, current_entry->dir);
 645           current_entry = current_entry->next_entry;
 646       }
 647 
 648     sim_printf("%-32s %s\n", "[default]", tape_path_prefix);
 649 
 650     return SCPE_OK;
 651   }
 652 
 653 static t_stat mt_set_capac (UNUSED UNIT * uptr, UNUSED int32 value,
     
 654                              const char * cptr, UNUSED void * desc)
 655   {
 656     if (! cptr)
 657       return SCPE_ARG;
 658     t_stat rc;
 659     int i;
 660     
 661     
 662     for (i = 1; i < N_MT_UNITS_MAX; i ++)
 663       {
 664         rc = sim_tape_set_capac (mt_unit + i, value, cptr, desc);
 665         if (rc != SCPE_OK)
 666           return rc;
 667       }
 668     return SCPE_OK;
 669   }
 670 
 671 t_stat signal_tape (uint tap_unit_idx, word8 status0, word8 status1) {
     
 672 
 673   
 674   
 675   
 676   
 677   
 678   
 679   
 680   
 681   
 682   
 683       
 684       
 685 
 686   if (! sim_is_running)
 687     return SCPE_OK;
 688 
 689   uint ctlr_unit_idx = cables->tape_to_mtp[tap_unit_idx].ctlr_unit_idx;
 690 
 691 
 692 
 693 
 694 
 695 
 696 
 697 
 698 
 699 
 700 
 701 
 702 
 703 
 704 
 705 
 706 
 707 
 708 
 709 
 710 
 711 
 712 
 713 
 714   for (uint ctlr_port_num = 0; ctlr_port_num < MAX_CTLR_PORTS; ctlr_port_num ++) {
 715     if (cables->mtp_to_iom[ctlr_unit_idx][ctlr_port_num].in_use) {
 716       uint iom_unit_idx = cables->mtp_to_iom[ctlr_unit_idx][ctlr_port_num].iom_unit_idx;
 717       uint chan_num = cables->mtp_to_iom[ctlr_unit_idx][ctlr_port_num].chan_num;
 718       uint dev_code = cables->tape_to_mtp[tap_unit_idx].dev_code;
 719       send_special_interrupt (iom_unit_idx, chan_num, dev_code, 0, 020 );
 720       return SCPE_OK;
 721     }
 722   }
 723   sim_warn ("loadTape can't find controller; dropping interrupt\n");
 724   return SCPE_ARG;
 725 
 726 }
 727 
 728 static t_stat tape_set_ready (UNIT * uptr, UNUSED int32 value,
     
 729                               UNUSED const char * cptr,
 730                               UNUSED void * desc)
 731   {
 732     long tape_unit_idx = MT_UNIT_NUM (uptr);
 733     if (tape_unit_idx >= N_MT_UNITS_MAX)
 734       {
 735         sim_debug (DBG_ERR, & tape_dev,
 736                    "Tape set ready: Invalid unit number %ld\n", (long) tape_unit_idx);
 737         sim_printf ("error: Invalid unit number %ld\n", (long) tape_unit_idx);
 738         return SCPE_ARG;
 739       }
 740     return signal_tape ((unsigned int) tape_unit_idx, 0, 020 );
 741   }
 742 
 743 static MTAB mt_mod [] =
 744   {
 745 #ifndef SPEED
 746     { UNIT_WATCH, UNIT_WATCH, "WATCH",   "WATCH",   NULL, NULL, NULL, NULL },
 747     { UNIT_WATCH, 0,          "NOWATCH", "NOWATCH", NULL, NULL, NULL, NULL },
 748 #endif
 749     {
 750        MTAB_XTD | MTAB_VUN | \
 751        MTAB_NC,                                  
 752       0,                                         
 753       NULL,                                      
 754       "REWIND",                                  
 755       mt_rewind,                                 
 756       NULL,                                      
 757       NULL,                                      
 758       NULL                                       
 759     },
 760     {
 761       MTAB_XTD | MTAB_VDV | \
 762       MTAB_NMO | MTAB_VALR,                      
 763       0,                                         
 764       "NUNITS",                                  
 765       "NUNITS",                                  
 766       mt_set_nunits,                             
 767       mt_show_nunits,                            
 768       "Number of TAPE units in the system",      
 769       NULL                                       
 770     },
 771     {
 772       MTAB_XTD | MTAB_VUN | \
 773       MTAB_VALR | MTAB_NC,                       
 774       0,                                         
 775       "NAME",                                    
 776       "NAME",                                    
 777       mt_set_device_name,                        
 778       mt_show_device_name,                       
 779       "Set the device name",                     
 780       NULL                                       
 781     },
 782     {
 783       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 784       MTAB_VALR | MTAB_NC,                       
 785       0,                                         
 786       "DEFAULT_PATH",                            
 787       "DEFAULT_PATH",                            
 788       mt_set_tape_path,                          
 789       mt_show_tape_path,                         
 790       "Set the default path to the directory containing tape images (also clear search paths)",
 791       NULL
 792     },
 793     {
 794       MTAB_XTD | MTAB_VDV | MTAB_NMO | \
 795       MTAB_VALR | MTAB_NC,                       
 796       0,                                         
 797       "ADD_PATH",                                
 798       "ADD_PATH",                                
 799       mt_add_tape_search_path,                   
 800       mt_show_tape_search_paths,                 
 801       "Add a search path for tape directories",  
 802       NULL                                       
 803     },
 804     {
 805       MTAB_XTD | MTAB_VUN,                       
 806       0,                                         
 807       "CAPACITY",                                
 808       "CAPACITY",                                
 809       sim_tape_set_capac,                        
 810       sim_tape_show_capac,                       
 811       "Set the device capacity",                 
 812       NULL                                       
 813     },
 814     {
 815       MTAB_XTD | MTAB_VDV | \
 816       MTAB_NMO | MTAB_VALR,                      
 817       0,                                         
 818       "CAPACITY_ALL",                            
 819       "CAPACITY_ALL",                            
 820       mt_set_capac,                              
 821       NULL,                                      
 822       "Set the tape capacity of all drives",     
 823       NULL                                       
 824     },
 825     {
 826       MTAB_XTD | MTAB_VUN | \
 827       MTAB_NMO | MTAB_VALR,                      
 828       0,                                         
 829       "READY",                                   
 830       "READY",                                   
 831       tape_set_ready,                            
 832       NULL,                                      
 833       NULL,                                      
 834       NULL                                       
 835     },
 836     { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
 837   };
 838 
 839 static t_stat mt_reset (DEVICE * dptr)
     
 840   {
 841     for (int i = 0; i < (int) dptr -> numunits; i ++)
 842       {
 843         sim_tape_reset (& mt_unit [i]);
 844         
 845       }
 846     return SCPE_OK;
 847   }
 848 
 849 DEVICE tape_dev =
 850   {
 851     "TAPE",            
 852     mt_unit,           
 853     NULL,              
 854     mt_mod,            
 855     N_MT_UNITS,        
 856     10,                
 857     31,                
 858     1,                 
 859     8,                 
 860     9,                 
 861     NULL,              
 862     NULL,              
 863     mt_reset,          
 864     NULL,              
 865     &sim_tape_attach,  
 866     &sim_tape_detach,  
 867     NULL,              
 868     DEV_DEBUG,         
 869     0,                 
 870     mt_dt,             
 871     NULL,              
 872     NULL,              
 873     NULL,              
 874     NULL,              
 875     NULL,              
 876     NULL,              
 877     NULL               
 878   };
 879 
 880 static void deterimeFullTapeFileName(char * tapeFileName, char * buffer, int bufferLength)
     
 881   {
 882     
 883     if (!tape_path_prefix[0])
 884       {
 885           strncpy(buffer, tapeFileName, (unsigned long)bufferLength);
 886           buffer[bufferLength - 1] = 0;
 887           return;
 888       }
 889 
 890     
 891     PATH_ENTRY *current_entry = search_list_head;
 892     while (current_entry != NULL)
 893       {
 894         if (strncmp(current_entry->label_prefix, tapeFileName, current_entry->prefix_len) == 0)
 895           {
 896               break;
 897           }
 898 
 899         current_entry = current_entry->next_entry;
 900       }
 901 
 902     char *selected_path = tape_path_prefix;     
 903     if (current_entry != NULL)
 904       {
 905         selected_path = current_entry->dir;
 906       }
 907 
 908     
 909     if ((size_t) bufferLength < (strlen(selected_path) + strlen(tapeFileName) + 1))
 910       {
 911           
 912           
 913           strncpy(buffer, tapeFileName, (unsigned long)bufferLength);
 914           buffer[bufferLength - 1] = 0;
 915           return;
 916       }
 917 
 918     
 919     int buffWrote;
 920     buffWrote = snprintf(buffer,
 921                     ((int)((strlen(selected_path)+strlen(tapeFileName))+1)),
 922                         "%s%s", selected_path, tapeFileName);
 923     if (buffWrote < 0)
 924       sim_warn("%s snprintf problem, returned %d\n", __func__, buffWrote);
 925   }
 926 
 927 t_stat loadTape (uint driveNumber, char * tapeFilename, bool ro)
     
 928   {
 929     char full_tape_file_name[PATH_MAX + 1];
 930 
 931     if (ro)
 932       mt_unit [driveNumber] . flags |= MTUF_WRP;
 933     else
 934       mt_unit [driveNumber] . flags &= ~ MTUF_WRP;
 935 
 936     deterimeFullTapeFileName(tapeFilename, full_tape_file_name, (sizeof(full_tape_file_name)-1));
 937 
 938     sim_printf("loadTape Attaching drive %u to file %s\n", driveNumber, full_tape_file_name);
 939     t_stat stat = sim_tape_attach (& mt_unit [driveNumber], full_tape_file_name);
 940     if (stat != SCPE_OK)
 941       {
 942         sim_printf ("%s sim_tape_attach returned %d\n", __func__, stat);
 943         return stat;
 944       }
 945     return signal_tape (driveNumber, 0, 020 );
 946   }
 947 
 948 t_stat unloadTape (uint driveNumber)
     
 949   {
 950     if (mt_unit [driveNumber] . flags & UNIT_ATT)
 951       {
 952         t_stat stat = sim_tape_detach (& mt_unit [driveNumber]);
 953         if (stat != SCPE_OK)
 954           {
 955             sim_warn ("%s sim_tape_detach returned %d\n", __func__, stat);
 956             return stat;
 957           }
 958       }
 959     return signal_tape (driveNumber, 0, 040 );
 960   }
 961 
 962 void mt_init(void)
     
 963   {
 964     memset(tape_states, 0, sizeof(tape_states));
 965     for (int i = 0; i < N_MT_UNITS_MAX; i ++)
 966       {
 967         mt_unit [i] . capac = 40000000;
 968       }
 969   }
 970 
 971 void mt_exit (void) {
     
 972   
 973   PATH_ENTRY * current_entry = search_list_head;
 974   while (current_entry != NULL) {
 975     PATH_ENTRY * old_entry = current_entry;
 976     current_entry = current_entry->next_entry;
 977     FREE (old_entry);
 978   }
 979 }
 980 
 981 static iom_cmd_rc_t mtReadRecord (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
 982   {
 983 
 984 
 985 
 986     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
 987     UNIT * unitp = & mt_unit[devUnitIdx];
 988     struct tape_state * tape_statep = & tape_states[devUnitIdx];
 989 
 990     
 991     sim_debug (DBG_DEBUG, & tape_dev, "%s: Read %s record\n", __func__,
 992                tape_statep->is9 ? "9" : "binary");
 993     
 994     tape_statep->tbc = 0;
 995     if (! (unitp -> flags & UNIT_ATT))
 996       {
 997         p->stati = 04104;
 998         return IOM_CMD_ERROR;
 999       }
1000     t_stat rc = sim_tape_rdrecf (unitp, & tape_statep -> buf [0], & tape_statep -> tbc,
1001                                BUFSZ);
1002     sim_debug (DBG_DEBUG, & tape_dev, "%s: sim_tape_rdrecf returned %d, with tbc %d\n",
1003             __func__, rc, tape_statep -> tbc);
1004     if (rc == MTSE_TMK)
1005        {
1006          tape_statep -> rec_num ++;
1007          sim_debug (DBG_NOTIFY, & tape_dev,
1008                     "%s: EOF: %s\n", __func__, simh_tape_msg (rc));
1009         p -> stati = 04423; 
1010         if (tape_statep -> tbc != 0)
1011           {
1012             sim_warn ("%s: Read %d bytes with EOF.\n",
1013                         __func__, tape_statep -> tbc);
1014           }
1015         tape_statep -> tbc = 0;
1016         
1017         return IOM_CMD_ERROR;
1018       }
1019     if (rc == MTSE_EOM)
1020       {
1021         sim_debug (DBG_NOTIFY, & tape_dev,
1022                     "%s: EOM: %s\n", __func__, simh_tape_msg (rc));
1023         
1024         if (sim_tape_bot (unitp))
1025           p -> stati = 04302; 
1026         else
1027           p -> stati = 04340; 
1028         if (tape_statep -> tbc != 0)
1029           {
1030             sim_warn ("%s: Read %d bytes with EOM.\n",
1031                         __func__, tape_statep -> tbc);
1032             
1033           }
1034         tape_statep -> tbc = 0;
1035         
1036         return IOM_CMD_PROCEED;
1037       }
1038     if (rc != 0)
1039       {
1040         sim_debug (DBG_ERR, & tape_dev,
1041                    "%s: Cannot read tape: %d - %s\n",
1042                    __func__, rc, simh_tape_msg (rc));
1043         sim_debug (DBG_ERR, & tape_dev,
1044                    "%s: Returning arbitrary error code\n",
1045                    __func__);
1046         p -> stati      = 05001; 
1047         p -> chanStatus = chanStatParityErrPeriph;
1048         return IOM_CMD_ERROR;
1049       }
1050     p -> stati = 04000;
1051     if (sim_tape_wrp (unitp))
1052       p -> stati |= 1;
1053     tape_statep -> rec_num ++;
1054 
1055     tape_statep -> words_processed = 0;
1056     if (unitp->flags & UNIT_WATCH)
1057       sim_printf ("Tape %ld reads record %d\n",
1058                   (long) MT_UNIT_NUM (unitp), tape_statep -> rec_num);
1059 
1060     uint tally = p -> DDCW_TALLY;
1061     if (tally == 0)
1062       {
1063         sim_debug (DBG_DEBUG, & tape_dev,
1064                    "%s: Tally of zero interpreted as 010000(4096)\n",
1065                    __func__);
1066         tally = 4096;
1067       }
1068 
1069     sim_debug (DBG_DEBUG, & tape_dev,
1070                "%s: Tally %d (%o)\n", __func__, tally, tally);
1071 
1072     word36 buffer [tally];
1073     uint i;
1074     int rc2;
1075     for (i = 0; i < tally; i ++)
1076       {
1077         if (tape_statep -> is9)
1078           rc2 = extractASCII36FromBuffer (tape_statep -> buf,
1079                   tape_statep -> tbc, & tape_statep -> words_processed, buffer + i);
1080         else
1081           rc2 = extractWord36FromBuffer (tape_statep -> buf,
1082                   tape_statep -> tbc, & tape_statep -> words_processed, buffer + i);
1083         if (rc2)
1084           {
1085              break;
1086           }
1087       }
1088 
1089 
1090 
1091 
1092 
1093 
1094 
1095 
1096 
1097 
1098 
1099 
1100 
1101 
1102 
1103     iom_indirect_data_service (iomUnitIdx, chan, buffer,
1104                                     & tape_statep -> words_processed, true);
1105     if (p -> tallyResidue)
1106       {
1107         sim_debug (DBG_WARN, & tape_dev,
1108                    "%s: Read buffer exhausted on channel %d\n",
1109                        __func__, chan);
1110 
1111       }
1112 
1113     if (tape_statep -> is9)
1114       p -> charPos = tape_statep -> tbc % 4;
1115     else
1116       p -> charPos = (tape_statep -> tbc * 8) / 9 % 4;
1117     return IOM_CMD_PROCEED;
1118   }
1119 
1120 static void mtReadCtrlMainMem (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
1121   {
1122     struct tape_state * tape_statep = & tape_states[devUnitIdx];
1123     word36 control;
1124     uint count;
1125     iom_indirect_data_service (iomUnitIdx, chan, & control, &count, false);
1126     if (count != 1)
1127       sim_warn ("%s: count %d not 1\n", __func__, count);
1128     tape_statep -> cntlrAddress = getbits36_16 (control, 0);
1129     tape_statep -> cntlrTally   = getbits36_16 (control, 16);
1130   }
1131 
1132 static void mtInitRdMem (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
1133   {
1134     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1135 
1136     uint tally = p -> DDCW_TALLY;
1137     if (tally != 04000)
1138       {
1139         sim_warn ("%s: tape controller read memory expected tally of 04000\n", __func__);
1140         p -> stati      = 04501;
1141         p -> chanStatus = chanStatIncorrectDCW;
1142         return;
1143       }
1144     uint16 mem [04000 * 2];
1145     memset (mem, 0, sizeof (mem));
1146 
1147     const uint charTableOS = 0xE0; 
1148 
1149 
1150 
1151 
1152 
1153 
1154 
1155 
1156 
1157 
1158 
1159 
1160 
1161 
1162 
1163 
1164 
1165 
1166 
1167 
1168 
1169 
1170 
1171 
1172     mem [charTableOS + 0]  = 4096;          
1173     mem [charTableOS + 1]  = 0;             
1174 
1175     
1176     mem [charTableOS + 2]  = 04000 + 0123;  
1177     mem [charTableOS + 3]  = 0;             
1178     mem [charTableOS + 4]  = 0;             
1179     mem [charTableOS + 5]  = 04000 + 0234;  
1180     mem [charTableOS + 6]  = 04000 + 0345;  
1181     mem [charTableOS + 7]  = 04000 + 0456;  
1182     mem [charTableOS + 8]  = 012345;        
1183 
1184     
1185     mem [charTableOS + 9]  = 0x0013;        
1186 
1187     mem [charTableOS + 10] = 0x1025;        
1188 
1189     word36 buf [tally];
1190     
1191     memset (buf, 0, sizeof (word36) * tally);
1192     for (uint i = 0; i < tally; i ++)
1193       {
1194         putbits36_18 (buf + i,  0, mem [i * 2]);
1195         putbits36_18 (buf + i, 18, mem [i * 2 + 1]);
1196       }
1197     iom_indirect_data_service (iomUnitIdx, chan, buf, & tally, true);
1198     p -> stati = 04000;
1199   }
1200 
1201 static void mtWRCtrlRegs (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
1202   {
1203     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1204     
1205     p->initiate = false;
1206     return;
1207   }
1208 
1209 static void mtInitWrMem (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
1210   {
1211     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1212     
1213     p->initiate = false;
1214     return;
1215   }
1216 
1217 static void mtMTPWr (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
1218   {
1219     iom_chan_data_t * p = & iom_chan_data[iomUnitIdx][chan];
1220     struct tape_state * tape_statep = & tape_states [devUnitIdx];
1221 
1222     word36 control;
1223     uint count;
1224     iom_indirect_data_service (iomUnitIdx, chan, & control, &count, false);
1225     if (count != 1)
1226       sim_warn ("%s: count %d not 1\n", __func__, count);
1227     tape_statep -> cntlrAddress = getbits36_16 (control, 0);
1228     tape_statep -> cntlrTally = getbits36_16 (control, 16);
1229     p -> stati = 04000;
1230   }
1231 
1232 static int mtWriteRecord (uint devUnitIdx, uint iomUnitIdx, uint chan)
     
1233   {
1234 
1235 
1236 
1237     iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1238     UNIT * unitp = & mt_unit [devUnitIdx];
1239     struct tape_state * tape_statep = & tape_states [devUnitIdx];
1240 
1241     tape_statep -> is9 = p -> IDCW_DEV_CMD == 013;
1242     sim_debug (DBG_DEBUG, & tape_dev, "%s: Write %s record\n", __func__,
1243                tape_statep -> is9 ? "9" : "binary");
1244 
1245     p -> isRead = false;
1246 
1247     uint tally = p -> DDCW_TALLY;
1248     if (tally == 0)
1249       {
1250         sim_debug (DBG_DEBUG, & tape_dev,
1251                    "%s: Tally of zero interpreted as 010000(4096)\n",
1252                    __func__);
1253         tally = 4096;
1254       }
1255 
1256     sim_debug (DBG_DEBUG, & tape_dev,
1257                "%s: Tally %d (%o)\n", __func__, tally, tally);
1258 
1259     
1260 
1261     tape_statep -> words_processed = 0;
1262     word36 buffer [tally];
1263 
1264     iom_indirect_data_service (iomUnitIdx, chan, buffer,
1265                             & tape_statep -> words_processed, false);
1266 
1267 
1268 
1269 
1270 
1271 
1272 
1273 
1274 
1275 
1276 
1277 
1278 
1279 
1280 
1281 
1282 
1283     if (tape_statep -> is9)
1284       tape_statep -> tbc = tape_statep -> words_processed * 4;
1285     else
1286       tape_statep -> tbc = (tape_statep -> words_processed * 9 + 1) / 2;
1287 
1288     
1289 
1290     tape_statep -> words_processed = 0;
1291     uint i;
1292     for (i = 0; i < tally; i ++)
1293       {
1294         int rc2;
1295         if (tape_statep -> is9)
1296           {
1297             rc2 = insertASCII36toBuffer (tape_statep -> buf,
1298                                          tape_statep -> tbc,
1299                                          & tape_statep -> words_processed,
1300                                          buffer [i]);
1301           }
1302         else
1303           {
1304             rc2 = insertWord36toBuffer (tape_statep -> buf,
1305                                         tape_statep -> tbc,
1306                                         & tape_statep -> words_processed,
1307                                         buffer [i]);
1308             }
1309         if (rc2)
1310           {
1311             p -> stati = 04000;
1312             if (sim_tape_wrp (unitp))
1313               p -> stati |= 1;
1314             sim_debug (DBG_WARN, & tape_dev,
1315                        "%s: Write buffer exhausted on channel %d\n",
1316                        __func__, chan);
1317             break;
1318           }
1319       }
1320     p -> tallyResidue = (word12) (tally - i);
1321 
1322 
1323     if (tape_statep -> is9)
1324       p -> charPos = tape_statep -> tbc % 4;
1325     else
1326       p -> charPos = (tape_statep -> tbc * 8) / 9 % 4;
1327 
1328     
1329 
1330     if (! (unitp -> flags & UNIT_ATT))
1331       return MTSE_UNATT;
1332 
1333     int ret = sim_tape_wrrecf (unitp, tape_statep -> buf, tape_statep -> tbc);
1334     sim_debug (DBG_DEBUG, & tape_dev, "%s: sim_tape_wrrecf returned %d, with tbc %d\n",
1335                __func__, ret, tape_statep -> tbc);
1336 
1337     if (unitp->io_flush)
1338       unitp->io_flush (unitp);                              
1339     
1340 
1341     if (ret != 0)
1342       {
1343         
1344         if (ret == MTSE_EOM)
1345           {
1346             sim_debug (DBG_NOTIFY, & tape_dev,
1347                         "%s: EOM: %s\n", __func__, simh_tape_msg (ret));
1348             p -> stati = 04340; 
1349             if (tape_statep -> tbc != 0)
1350               {
1351                 sim_warn ("%s: Wrote %d bytes with EOM.\n",
1352                            __func__, tape_statep -> tbc);
1353               }
1354             return 0;
1355           }
1356         sim_warn ("%s: Cannot write tape: %d - %s\n",
1357                    __func__, ret, simh_tape_msg(ret));
1358         sim_warn ("%s: Returning arbitrary error code\n",
1359                    __func__);
1360         p -> stati = 05001; 
1361         p -> chanStatus = chanStatParityErrPeriph;
1362         return IOM_CMD_ERROR;
1363       }
1364     tape_statep -> rec_num ++;
1365     if (unitp->flags & UNIT_WATCH)
1366       sim_printf ("Tape %ld writes record %d\n",
1367                   (long) MT_UNIT_NUM (unitp), tape_statep -> rec_num);
1368 
1369     sim_tape_wreom (unitp);
1370     if (unitp->io_flush)
1371       unitp->io_flush (unitp);                              
1372 
1373     p -> stati = 04000;
1374     if (sim_tape_wrp (unitp))
1375       p -> stati |= 1;
1376     if (sim_tape_eot (unitp))
1377       p -> stati |= 04340;
1378 
1379     sim_debug (DBG_INFO, & tape_dev,
1380                "%s: Wrote %d bytes to simulated tape; status %04o\n",
1381                __func__, (int) tape_statep -> tbc, p -> stati);
1382 
1383     return 0;
1384   }
1385 
1386 
1387 
1388 static int surveyDevices (uint iomUnitIdx, uint chan)
     
1389   {
1390     iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1391 
1392 
1393 
1394 
1395 
1396 
1397 
1398 
1399 
1400 
1401 
1402 
1403 
1404 
1405     sim_debug (DBG_DEBUG, & tape_dev,
1406                "%s: Survey devices\n", __func__);
1407     p -> stati = 04000; 
1408 
1409     uint bufsz = 8;
1410     word36 buffer [bufsz];
1411     uint cnt = 0;
1412     for (uint i = 0; i < bufsz; i ++)
1413       buffer [i] = 0;
1414 
1415     uint ctlr_idx = get_ctlr_idx (iomUnitIdx, chan);
1416     
1417     for (uint dev_code = 0; dev_code < N_DEV_CODES; dev_code ++)
1418       {
1419        if (cnt / 2 >= bufsz)
1420           break;
1421         
1422         struct ctlr_to_dev_s * p = & cables->mtp_to_tape[ctlr_idx][dev_code];
1423         if (! p -> in_use)
1424           continue;
1425         uint unit_idx = p->unit_idx;
1426 
1427         word18 handler = 0;
1428         handler |= 0100000; 
1429         if (mt_unit [unit_idx].filename)
1430           {
1431             handler |= 0040000; 
1432           }
1433         handler |= ((word18) dev_code & 037) << 9; 
1434         handler |= 0000040; 
1435         handler |= 0000020; 
1436         handler |= 0000007; 
1437         sim_debug (DBG_DEBUG, & tape_dev,
1438                    "%s: unit %d handler %06o\n", __func__, unit_idx, handler);
1439         if (cnt % 2 == 0)
1440           {
1441             buffer [cnt / 2] = ((word36) handler) << 18;
1442           }
1443         else
1444           {
1445             buffer [cnt / 2] |= handler;
1446           }
1447         cnt ++;
1448       }
1449     iom_indirect_data_service (iomUnitIdx, chan, buffer, & bufsz, true);
1450     p -> stati = 04000; 
1451     return 0;
1452   }
1453 
1454 
1455 
1456 
1457 
1458 
1459 
1460 
1461 
1462 
1463 
1464 
1465 
1466 
1467 
1468 
1469 iom_cmd_rc_t mt_iom_cmd (uint iomUnitIdx, uint chan) {
     
1470   iom_chan_data_t * p = & iom_chan_data [iomUnitIdx] [chan];
1471 #ifdef TESTING
1472   if_sim_debug (DBG_TRACE, & tape_dev) dumpDCW (p->DCW, 0);
1473 #endif
1474 
1475 
1476 
1477 
1478 
1479 
1480 
1481 
1482 
1483 
1484 
1485 
1486   uint ctlr_unit_idx = get_ctlr_idx (iomUnitIdx, chan);
1487   uint dev_code = p->IDCW_DEV_CODE;
1488 #ifdef TESTING
1489   if_sim_debug (DBG_TRACE, & tape_dev) dumpDCW (p->DCW, 0);
1490 #endif
1491   if (p->IDCW_DEV_CODE == 0)
1492     dev_code = mtp_state[ctlr_unit_idx].boot_drive;
1493 
1494   sim_debug (DBG_DEBUG, & tape_dev, "%s: Tape %c%02o_%02o\n", __func__, iomChar (iomUnitIdx), chan, dev_code);
1495 
1496   uint devUnitIdx = cables->mtp_to_tape[ctlr_unit_idx][dev_code].unit_idx;
1497   UNIT * unitp = & mt_unit [devUnitIdx];
1498   struct tape_state * tape_statep = & tape_states [devUnitIdx];
1499 
1500   iom_cmd_rc_t rc = IOM_CMD_PROCEED;
1501 
1502   
1503   if (IS_IDCW (p)) {
1504     
1505 
1506     
1507     
1508     
1509     
1510 
1511     
1512     
1513     
1514     
1515     
1516     
1517     
1518     
1519     
1520 
1521     tape_statep->io_mode = tape_no_mode;
1522     sim_debug (DBG_DEBUG, & tape_dev, "%s: IDCW_DEV_CMD %oo %d.\n", __func__, p->IDCW_DEV_CMD, p->IDCW_DEV_CMD);
1523     switch (p->IDCW_DEV_CMD) {
1524 
1525       case 000: { 
1526           if_sim_debug (DBG_TRACE, & tape_dev) {
1527             sim_printf ("// Tape Request Status\r\n");
1528           }
1529           
1530           if (p->IDCW_CHAN_CMD == 040) {
1531             sim_debug (DBG_DEBUG, & tape_dev, "%s: controller suspend\n", __func__);
1532             send_special_interrupt (iomUnitIdx, chan, p->IDCW_DEV_CODE, 01, 0 );
1533             p->stati = 04000;
1534           } else {
1535             p->stati = 04000;
1536           }
1537           sim_debug (DBG_DEBUG, & tape_dev, "%s: Request status: %04o control %0o chan_cmd %02o\n", __func__,
1538                      p->stati, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
1539         }
1540         break;
1541 
1542 
1543 
1544 
1545 
1546 
1547 
1548 
1549 
1550 
1551 
1552 
1553 
1554 
1555 
1556 
1557 
1558 
1559 
1560 
1561 
1562 
1563 
1564 
1565 
1566 
1567 
1568 
1569 
1570 
1571 
1572 
1573 
1574 
1575 
1576 
1577 
1578 
1579 
1580 
1581 
1582 
1583 
1584 
1585 
1586 
1587 
1588 
1589 
1590 
1591 
1592 
1593 
1594 
1595       case 002:               
1596         if_sim_debug (DBG_TRACE, & tape_dev) {
1597           sim_printf ("// Tape Read Controller Main Memory\r\n");
1598         }
1599         sim_debug (DBG_DEBUG, & tape_dev, "%s: Read controller main memory\n", __func__);
1600 
1601         tape_statep->io_mode = tape_rd_ctlr;
1602         p->stati = 04000;
1603         break;
1604 
1605       case 003: 
1606         if_sim_debug (DBG_TRACE, & tape_dev) {
1607           sim_printf ("// Tape Read 9 Record\r\n");
1608         }
1609         sim_debug (DBG_DEBUG, & tape_dev, "%s: Read 9 record\n", __func__);
1610         tape_statep->io_mode = tape_rd_9;
1611         tape_statep->is9 = true;
1612         if (! (unitp->flags & UNIT_ATT)) {
1613           p->stati = 04104;
1614           return IOM_CMD_ERROR;
1615         }
1616         p->stati = 04000;
1617         break;
1618 
1619 
1620 
1621       case 005: 
1622         if_sim_debug (DBG_TRACE, & tape_dev) {
1623           sim_printf ("// Tape Read Binary Record\r\n");
1624         }
1625         sim_debug (DBG_DEBUG, & tape_dev, "%s: Read binary record\n", __func__);
1626         tape_statep->io_mode = tape_rd_bin;
1627         tape_statep->is9 = false;
1628         if (! (unitp->flags & UNIT_ATT)) {
1629           p->stati = 04104;
1630           return IOM_CMD_ERROR;
1631         }
1632         p->stati = 04000;
1633         break;
1634 
1635 
1636 
1637 
1638 
1639 
1640 
1641 
1642 
1643 
1644 
1645 
1646 
1647 
1648 
1649 
1650 
1651 
1652 
1653 
1654 
1655       case 006:               
1656         if_sim_debug (DBG_TRACE, & tape_dev) {
1657           sim_printf ("// Tape Initiate Read Data Transfer\r\n");
1658         }
1659         sim_debug (DBG_DEBUG, & tape_dev, "%s: initiate read data transfer\n", __func__);
1660         tape_statep->io_mode = tape_initiate_rd_mem;
1661         p->stati = 04000;
1662         break;
1663 
1664 
1665 
1666 
1667 
1668 
1669 
1670 
1671 
1672       case 013: 
1673         if_sim_debug (DBG_TRACE, & tape_dev) {
1674           sim_printf ("// Tape Write 9 Record\r\n");
1675         }
1676         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write 9 record\n", __func__);
1677         tape_statep->io_mode = tape_wr_9;
1678         if (! (unitp->flags & UNIT_ATT)) {
1679           p->stati = 04104;
1680           return IOM_CMD_ERROR;
1681         }
1682         p->stati = 04000;
1683         break;
1684 
1685 
1686 
1687       case 015: 
1688         if_sim_debug (DBG_TRACE, & tape_dev) {
1689           sim_printf ("// Tape Write Binary Record\r\n");
1690         }
1691         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write binary record\n", __func__);
1692         tape_statep->io_mode = tape_wr_bin;
1693         if (! (unitp->flags & UNIT_ATT)) {
1694           p->stati = 04104;
1695           return IOM_CMD_ERROR;
1696         }
1697         p->stati = 04000;
1698         break;
1699 
1700       case 016:               
1701         if_sim_debug (DBG_TRACE, & tape_dev) {
1702           sim_printf ("// Tape Write Control Registers\r\n");
1703         }
1704         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write Control Registers\n", __func__);
1705         tape_statep->io_mode = tape_wr_ctrl_regs;
1706         p->stati = 04000;
1707         break;
1708 
1709 
1710 
1711       case 020:  { 
1712           if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Release Controller\r\n"); }
1713           if (p->IDCW_CHAN_CMD == 040) { 
1714             sim_debug (DBG_DEBUG, & tape_dev, "%s: Release controller\n", __func__);
1715             p->stati = 04000; 
1716             sim_debug (DBG_DEBUG, & tape_dev, "%s: Release status: %04o control %0o chan_cmd %02o\n",
1717                        __func__, p->stati, p->IDCW_CHAN_CTRL, p->IDCW_CHAN_CMD);
1718             send_special_interrupt (iomUnitIdx, chan, p->IDCW_DEV_CODE, 02, 0 );
1719           } else {
1720             p->stati = 04501;
1721             p->chanStatus = chanStatIncorrectDCW;
1722             sim_warn ("%s: Unknown command %02o\n", __func__, p->IDCW_DEV_CMD);
1723           }
1724         }
1725         break;
1726 
1727 
1728 
1729 
1730 
1731 
1732 
1733 
1734 
1735 
1736 
1737 
1738 
1739 
1740 
1741 
1742 
1743 
1744 
1745 
1746 
1747       case 032: 
1748                 
1749         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Write Main Memory\r\n"); }
1750         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write controller main memory\n", __func__);
1751         tape_statep->io_mode = tape_MTP_wr;
1752         p->stati = 04000;
1753         break;
1754 
1755 
1756 
1757 
1758 
1759 
1760 
1761 
1762 
1763 
1764 
1765       case 040:               
1766         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Reset Status\r\n"); }
1767         sim_debug (DBG_DEBUG, & tape_dev, "%s Reset status\n", __func__);
1768         p->stati = 04000;
1769         p->isRead = false;
1770         
1771         if (dev_code == 077) {
1772           p->stati = 04502; 
1773           return IOM_CMD_DISCONNECT;
1774         }
1775         if (sim_tape_wrp (unitp))
1776           p->stati |= 1;
1777         if (sim_tape_bot (unitp))
1778           p->stati |= 2;
1779         
1780           
1781         break;
1782 
1783       case 041:              
1784         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 6259 CPI\r\n"); }
1785         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 6250 cpi\n", __func__);
1786         p->stati = 04000;
1787         if (sim_tape_wrp (unitp))
1788           p->stati |= 1;
1789         if (sim_tape_bot (unitp))
1790           p->stati |= 2;
1791         
1792           
1793         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 800 bpi\n", __func__);
1794         break;
1795 
1796       case 042:              
1797       case 060:              
1798         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 800 BPI\r\n"); }
1799         p->stati = 04000;
1800         if (sim_tape_wrp (unitp))
1801           p->stati |= 1;
1802         if (sim_tape_bot (unitp))
1803           p->stati |= 2;
1804         
1805           
1806         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 800 bpi\n", __func__);
1807         break;
1808 
1809       case 043:              
1810       case 061:              
1811         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 556 BPI\r\n"); }
1812         p->stati = 04000;
1813         if (sim_tape_wrp (unitp))
1814           p->stati |= 1;
1815         if (sim_tape_bot (unitp))
1816           p->stati |= 2;
1817         
1818           
1819         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 556 bpi\n", __func__);
1820         break;
1821 
1822       case 044: { 
1823         if_sim_debug (DBG_TRACE, & tape_dev) {
1824           sim_printf ("// Tape Forward Skip One Record\r\n");
1825           sim_printf ("//    pos before %d\r\n", unitp->pos);
1826         }
1827 #ifdef TESTING
1828         hdbgNote ("tape", "Tape Forward Skip One Record pos before %d", unitp->pos);
1829 #endif
1830         if (! (unitp->flags & UNIT_ATT)) {
1831           p->stati = 04104;
1832           return IOM_CMD_ERROR;
1833         }
1834         uint tally = p->IDCW_COUNT;
1835         if (tally == 0) {
1836           sim_debug (DBG_DEBUG, & tape_dev, "%s: Tally of zero interpreted as 64\n", __func__);
1837           tally = 64;
1838         }
1839         sim_debug (DBG_DEBUG, & tape_dev, "%s: Forward skip record %d\n", __func__, tally);
1840 
1841 
1842 
1843 
1844 
1845 
1846         uint32 skipped = 0;
1847         t_stat ret = MTSE_OK;
1848         while (skipped < tally) {
1849           ret = sim_tape_sprecf (unitp, & tape_statep->tbc);
1850           if (ret != MTSE_OK && ret != MTSE_TMK)
1851             break;
1852           skipped = skipped + 1;
1853         }
1854 
1855         if (ret != MTSE_OK && ret != MTSE_TMK && ret != MTSE_EOM) {
1856           break;
1857         }
1858         if (skipped != tally) {
1859           sim_warn ("skipped %d != tally %d\n", skipped, tally);
1860         }
1861 
1862         tape_statep->rec_num += (int) skipped;
1863         if (unitp->flags & UNIT_WATCH)
1864           sim_printf ("Tape %ld forward skips to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
1865 
1866         p->tallyResidue = (word12) (tally - skipped);
1867 
1868         p->stati = 04000;
1869         if (sim_tape_wrp (unitp))
1870           p->stati |= 1;
1871         if (sim_tape_bot (unitp))
1872           p->stati |= 2;
1873         
1874           
1875         if_sim_debug (DBG_TRACE, & tape_dev) {
1876           sim_printf ("//    pos after %d\r\n", unitp->pos);
1877         }
1878 #ifdef TESTING
1879         hdbgNote ("tape", "Tape Forward Skip One Record pos after %d", unitp->pos);
1880 #endif
1881       }
1882       break;
1883 
1884     case 045: { 
1885         if_sim_debug (DBG_TRACE, & tape_dev) {
1886           sim_printf ("// Tape Forward Skip One File\r\n");
1887           sim_printf ("//    pos before %d\r\n", unitp->pos);
1888         }
1889 #ifdef TESTING
1890         hdbgNote ("tape", "Tape Forward Skip One File pos before %d", unitp->pos);
1891 #endif
1892         sim_debug (DBG_DEBUG, & tape_dev, "%s:: Forward Skip File\n", __func__);
1893         if (! (unitp->flags & UNIT_ATT)) {
1894           p->stati = 04104;
1895           return IOM_CMD_ERROR;
1896         }
1897         uint tally = 1;
1898 
1899         uint32 skipped, recsskipped;
1900         t_stat ret = sim_tape_spfilebyrecf (unitp, tally, & skipped, & recsskipped, false);
1901         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// sim_tape_spfilebyrecf ret %d skipped %d recsskipped %d\r\n", ret, skipped, recsskipped); }
1902         if (ret != MTSE_OK && ret != MTSE_TMK && ret != MTSE_LEOT) {
1903           sim_warn ("sim_tape_spfilebyrecf returned %d\n", ret);
1904           break;
1905         }
1906         if (skipped != tally) {
1907           sim_warn ("skipped %d != tally %d\n", skipped, tally);
1908         }
1909 
1910         tape_statep->rec_num += (int) recsskipped;
1911         if (unitp->flags & UNIT_WATCH)
1912           sim_printf ("Tape %ld forward skips to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
1913 
1914         p->tallyResidue = (word12) (tally - skipped);
1915         sim_debug (DBG_NOTIFY, & tape_dev, "%s: Forward space %d files\n", __func__, tally);
1916 
1917         p->stati = 04000;
1918         if (sim_tape_wrp (unitp))
1919           p->stati |= 1;
1920         if (sim_tape_bot (unitp))
1921           p->stati |= 2;
1922         
1923           
1924         }
1925         if_sim_debug (DBG_TRACE, & tape_dev) {
1926           sim_printf ("//    pos after %d\r\n", unitp->pos);
1927         }
1928 #ifdef TESTING
1929         hdbgNote ("tape", "Tape Forward Skip One File pos after %d", unitp->pos);
1930 #endif
1931         break;
1932 
1933       case 046: { 
1934         if_sim_debug (DBG_TRACE, & tape_dev) {
1935           sim_printf ("// Tape Backspace One Record\r\n");
1936           sim_printf ("//    pos before %d\r\n", unitp->pos);
1937         }
1938 #ifdef TESTING
1939         hdbgNote ("tape", "Tape Backspace One Record pos before %d", unitp->pos);
1940 #endif
1941         sim_debug (DBG_DEBUG, & tape_dev, "%s: Backspace Record\n", __func__);
1942         if (! (unitp->flags & UNIT_ATT)) {
1943           p->stati = 04104;
1944           return IOM_CMD_ERROR;
1945         }
1946 
1947         uint tally = p->IDCW_COUNT;
1948         if (tally == 0) {
1949           sim_debug (DBG_DEBUG, & tape_dev, "%s: Tally of zero interpreted as 64\n", __func__);
1950           tally = 64;
1951         }
1952 
1953         sim_debug (DBG_DEBUG, & tape_dev, "%s: Backspace record tally %d\n", __func__, tally);
1954 
1955         
1956 
1957 
1958 
1959 
1960 
1961 
1962 
1963 
1964         uint32 skipped = 0;
1965         while (skipped < tally) {
1966           t_stat ret = sim_tape_sprecr (unitp, & tape_statep->tbc);
1967           if (ret != MTSE_OK && ret != MTSE_TMK)
1968             break;
1969           skipped ++;
1970         }
1971 
1972         if (skipped != tally) {
1973           sim_warn ("skipped %d != tally %d\n", skipped, tally);
1974         }
1975         tape_statep->rec_num -= (int) skipped;
1976         if (unitp->flags & UNIT_WATCH)
1977           sim_printf ("Tape %ld skip back to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
1978 
1979         p->tallyResidue = (word12) (tally - skipped);
1980 
1981         sim_debug (DBG_NOTIFY, & tape_dev, "%s: Backspace %d records\n", __func__, skipped);
1982 
1983         p->stati = 04000;
1984         if (sim_tape_wrp (unitp))
1985           p->stati |= 1;
1986         if (sim_tape_bot (unitp))
1987           p->stati |= 2;
1988         
1989           
1990         }
1991         if_sim_debug (DBG_TRACE, & tape_dev) {
1992           sim_printf ("//    pos after %d\r\n", unitp->pos);
1993         }
1994 #ifdef TESTING
1995         hdbgNote ("tape", "Tape Backspace One Record pos after %d", unitp->pos);
1996 #endif
1997         break;
1998 
1999       case 047: { 
2000         if_sim_debug (DBG_TRACE, & tape_dev) {
2001           sim_printf ("// Tape Backspace One File\r\n");
2002           sim_printf ("//    pos before %d\r\n", unitp->pos);
2003         }
2004         sim_debug (DBG_DEBUG, & tape_dev, "%s: Backspace File\n", __func__);
2005 #ifdef TESTING
2006         hdbgNote ("tape", "Tape Backspace One File pos before %d", unitp->pos);
2007 #endif
2008         if (! (unitp->flags & UNIT_ATT)) {
2009           p->stati = 04104;
2010           return IOM_CMD_ERROR;
2011         }
2012         uint tally = 1;
2013 
2014 
2015 
2016 
2017 
2018 
2019 
2020 
2021 
2022 
2023 
2024 
2025 
2026 
2027 
2028 
2029 
2030 
2031 
2032 
2033         uint32 skipped, recsskipped;
2034         t_stat ret = sim_tape_spfilebyrecr (unitp, tally, & skipped, & recsskipped);
2035         if (ret != MTSE_OK && ret != MTSE_TMK && ret != MTSE_BOT) {
2036           sim_warn ("sim_tape_spfilebyrecr returned %d\n", ret);
2037           break;
2038         }
2039         if (skipped != tally) {
2040           sim_warn ("skipped %d != tally %d\n", skipped, tally);
2041         }
2042 
2043         tape_statep->rec_num -= (int) recsskipped;
2044         if (unitp->flags & UNIT_WATCH)
2045           sim_printf ("Tape %ld backward skips to record %d\n", (long) MT_UNIT_NUM (unitp), tape_statep->rec_num);
2046 
2047         p->tallyResidue = (word12) (tally - skipped);
2048         sim_debug (DBG_NOTIFY, & tape_dev, "%s: Backspace %d records\n", __func__, tally);
2049 
2050 
2051         p->stati = 04000;
2052         if (sim_tape_wrp (unitp))
2053           p->stati |= 1;
2054         if (sim_tape_bot (unitp))
2055           p->stati |= 2;
2056         
2057           
2058         }
2059         if_sim_debug (DBG_TRACE, & tape_dev) {
2060           sim_printf ("//    pos after %d\r\n", unitp->pos);
2061         }
2062 #ifdef TESTING
2063         hdbgNote ("tape", "Tape Backspace One File pos after %d", unitp->pos);
2064 #endif
2065         break;
2066 
2067       case 050: {              
2068           if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Request Device Status\r\n"); }
2069           p->stati = 04000;
2070           if (sim_tape_wrp (unitp))
2071             p->stati |= 1;
2072           if (sim_tape_bot (unitp))
2073             p->stati |= 2;
2074           
2075             
2076           sim_debug (DBG_DEBUG, & tape_dev, "%s: Request device status: %o\n", __func__, p->stati);
2077         }
2078         break;
2079 
2080 
2081 
2082 
2083 
2084 
2085 
2086 
2087       case 051: {              
2088           if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Reset Device Status\r\n"); }
2089           if (p->isPCW) {
2090             p->stati = 04501; 
2091             p->chanStatus = chanStatIncorrectDCW;
2092             return IOM_CMD_ERROR;
2093           }
2094           p->stati = 04000;
2095           if (sim_tape_wrp (unitp))
2096             p->stati |= 1;
2097           if (sim_tape_bot (unitp))
2098             p->stati |= 2;
2099           
2100             
2101           sim_debug (DBG_DEBUG, & tape_dev, "%s: Reset device status: %o\n", __func__, p->stati);
2102         }
2103         break;
2104 
2105 
2106 
2107 
2108 
2109 
2110 
2111       case 055: 
2112         if_sim_debug (DBG_TRACE, & tape_dev) {
2113           sim_printf ("// Tape Write EOF\r\n");
2114           sim_printf ("//    pos before %d\r\n", unitp->pos);
2115         }
2116         sim_debug (DBG_DEBUG, & tape_dev, "%s: Write tape mark\n", __func__);
2117 #ifdef TESTING
2118         hdbgNote ("tape", "Tape Write EOF pos before %d", unitp->pos);
2119 #endif
2120 
2121         if (! (unitp->flags & UNIT_ATT)) {
2122           p->stati = 04104;
2123           return IOM_CMD_ERROR;
2124         }
2125         int ret;
2126         ret = sim_tape_wrtmk (unitp);
2127         sim_debug (DBG_DEBUG, & tape_dev, "%s: returned %d\n", __func__, ret);
2128         if (unitp->io_flush)
2129           unitp->io_flush (unitp);                              
2130         if (ret != 0) {
2131           if (ret == MTSE_EOM) {
2132             sim_debug (DBG_NOTIFY, & tape_dev, "%s: EOM: %s\n", __func__, simh_tape_msg (ret));
2133             p->stati = 04340; 
2134             sim_warn ("%s: Wrote tape mark with EOM.\n", __func__);
2135             break;
2136           }
2137           sim_warn ("%s: Cannot write tape mark: %d - %s\n", __func__, ret, simh_tape_msg(ret));
2138           sim_warn ("%s: Returning arbitrary error code\n", __func__);
2139           p->stati = 05001; 
2140           p->chanStatus = chanStatParityErrPeriph;
2141           break;
2142         }
2143 
2144         sim_tape_wreom (unitp);
2145         if (unitp->io_flush)
2146           unitp->io_flush (unitp);                              
2147 
2148         tape_statep->rec_num ++;
2149         if (unitp->flags & UNIT_WATCH)
2150           sim_printf ("Tape %ld writes tape mark %ld\n", (long) MT_UNIT_NUM (unitp), (long) tape_statep->rec_num);
2151 
2152         p->stati = 04000;
2153         if (sim_tape_eot (unitp))
2154           p->stati = 04340;
2155 
2156         sim_debug (DBG_INFO, & tape_dev, "%s: Wrote tape mark; status %04o\n", __func__, p->stati);
2157         if_sim_debug (DBG_TRACE, & tape_dev) {
2158           sim_printf ("//    pos after %d\r\n", unitp->pos);
2159         }
2160 #ifdef TESTING
2161         hdbgNote ("tape", "Tape Write EOF pos after %d", unitp->pos);
2162 #endif
2163         break;
2164 
2165 
2166 
2167       case 057:               
2168         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Survey Devices\r\n"); }
2169         sim_debug (DBG_DEBUG, & tape_dev, "%s: survey_devices\n", __func__);
2170         tape_statep->io_mode = tape_survey;
2171         p->stati = 04000;
2172         break;
2173 
2174 
2175 
2176 
2177 
2178 
2179 
2180       case 063:              
2181         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set File Permit\r\n"); }
2182         sim_debug (DBG_WARN, & tape_dev, "%s: Set file permit\n", __func__);
2183         p->stati = 04000;
2184         if (sim_tape_wrp (unitp))
2185           p->stati |= 1;
2186         if (sim_tape_bot (unitp))
2187           p->stati |= 2;
2188         break;
2189 
2190       case 064:              
2191         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 200 BPI\r\n"); }
2192         p->stati = 04000;
2193         if (sim_tape_wrp (unitp))
2194           p->stati |= 1;
2195         if (sim_tape_bot (unitp))
2196           p->stati |= 2;
2197         
2198           
2199         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 200 bpi\n", __func__);
2200         break;
2201 
2202       case 065:              
2203         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Set 1600 CPI\r\n"); }
2204         p->stati = 04000;
2205         if (sim_tape_wrp (unitp))
2206           p->stati |= 1;
2207         if (sim_tape_bot (unitp))
2208           p->stati |= 2;
2209         
2210           
2211         sim_debug (DBG_DEBUG, & tape_dev, "%s: Set 1600 CPI\n", __func__);
2212         break;
2213 
2214 
2215 
2216 
2217 
2218       case 070:              
2219         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Rewind\r\n"); }
2220         sim_debug (DBG_DEBUG, & tape_dev, "%s: Rewind\n", __func__);
2221         if (! (unitp->flags & UNIT_ATT)) {
2222           p->stati = 04104;
2223           return IOM_CMD_ERROR;
2224         }
2225         sim_tape_rewind (unitp);
2226 
2227         tape_statep->rec_num = 0;
2228         if (unitp->flags & UNIT_WATCH)
2229           sim_printf ("Tape %ld rewinds\n", (long) MT_UNIT_NUM (unitp));
2230 
2231         p->stati = 04000;
2232         if (sim_tape_wrp (unitp))
2233           p->stati |= 1;
2234         if (sim_tape_bot (unitp))
2235           p->stati |= 2;
2236         
2237           
2238         send_special_interrupt (iomUnitIdx, chan, dev_code, 0, 0100 );
2239         break;
2240 
2241 
2242 
2243       case 072:              
2244         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Rewind/Unload\r\n"); }
2245         if ((unitp->flags & UNIT_ATT)) {
2246           if (unitp->flags & UNIT_WATCH)
2247             sim_printf ("Tape %ld unloads\n", (long) MT_UNIT_NUM (unitp));
2248           sim_debug (DBG_DEBUG, & tape_dev, "%s: Rewind/unload\n", __func__);
2249           sim_tape_detach (unitp);
2250         }
2251         p->stati = 04000;
2252         
2253         
2254         
2255         
2256         
2257         
2258         break;
2259 
2260 
2261 
2262 
2263 
2264 
2265 
2266 
2267 
2268 
2269 
2270       default:
2271         if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape Unknown %02o\r\n", p->IDCW_DEV_CMD); }
2272         p->stati = 04501;
2273         p->chanStatus = chanStatIncorrectDCW;
2274         sim_warn ("%s: tape unrecognized device command  %02o\n", __func__, p->IDCW_DEV_CMD);
2275         return IOM_CMD_ERROR;
2276 
2277     } 
2278 
2279     sim_debug (DBG_DEBUG, & tape_dev, "%s: stati %04o\n", __func__, p->stati);
2280     return rc;
2281   } 
2282 
2283   
2284   switch (tape_statep->io_mode) {
2285     case tape_no_mode:
2286 
2287 
2288 
2289 
2290 
2291 
2292       
2293       
2294       break;
2295 
2296     case tape_rd_9:
2297     case tape_rd_bin: {
2298         if_sim_debug (DBG_TRACE, & tape_dev) {
2299           sim_printf ("// Tape IOT Read\r\n");
2300           sim_printf ("//    pos before %d\r\n", unitp->pos);
2301         }
2302 #ifdef TESTING
2303         hdbgNote ("tape", "Tape IOT Read pos before %d", unitp->pos);
2304 #endif
2305         int rc = mtReadRecord (devUnitIdx, iomUnitIdx, chan);
2306         if (rc)
2307           return IOM_CMD_ERROR;
2308         }
2309         if_sim_debug (DBG_TRACE, & tape_dev) {
2310           sim_printf ("//    pos after %d\r\n", unitp->pos);
2311         }
2312 #ifdef TESTING
2313         hdbgNote ("tape", "Tape IOT Read pos after %d", unitp->pos);
2314 #endif
2315         break;
2316 
2317     case tape_wr_9:
2318     case tape_wr_bin: {
2319         if_sim_debug (DBG_TRACE, & tape_dev) {
2320           sim_printf ("// Tape IOT Write\r\n");
2321           sim_printf ("//    pos before %d\r\n", unitp->pos);
2322         }
2323 #ifdef TESTING
2324         hdbgNote ("tape", "Tape IOT write pos before %d", unitp->pos);
2325 #endif
2326         int rc = mtWriteRecord (devUnitIdx, iomUnitIdx, chan);
2327         if_sim_debug (DBG_TRACE, & tape_dev) {
2328           sim_printf ("//    pos after %d\r\n", unitp->pos);
2329         }
2330 #ifdef TESTING
2331         hdbgNote ("tape", "Tape IOT write pos after %d", unitp->pos);
2332 #endif
2333         if (rc)
2334           return IOM_CMD_ERROR;
2335       }
2336       break;
2337 
2338     case tape_rd_ctlr:
2339       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Read Memory\r\n"); }
2340       mtReadCtrlMainMem (devUnitIdx, iomUnitIdx, chan);
2341       break;
2342 
2343     case tape_initiate_rd_mem:
2344       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Write Memory\r\n"); }
2345       mtInitRdMem (devUnitIdx, iomUnitIdx, chan);
2346       break;
2347 
2348     case tape_initiate_wr_mem:
2349       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Initiate Write\r\n"); }
2350       mtInitWrMem (devUnitIdx, iomUnitIdx, chan);
2351       break;
2352 
2353     case tape_MTP_wr:
2354       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT MTP Write\r\n"); }
2355       mtMTPWr (devUnitIdx, iomUnitIdx, chan);
2356       break;
2357 
2358     case tape_wr_ctrl_regs:
2359       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Wr Ctrl Regs\r\n"); }
2360       mtWRCtrlRegs (devUnitIdx, iomUnitIdx, chan);
2361       break;
2362 
2363     case tape_survey:
2364       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT Survey\r\n"); }
2365       surveyDevices (iomUnitIdx, chan);
2366       break;
2367 
2368     default:
2369       if_sim_debug (DBG_TRACE, & tape_dev) { sim_printf ("// Tape IOT unknown %d\r\n", tape_statep->io_mode); }
2370       sim_warn ("%s: Unrecognized io_mode %d\n", __func__, tape_statep->io_mode);
2371       return IOM_CMD_ERROR;
2372   }
2373 
2374   return rc;
2375 } 
2376 
2377 
2378 
2379 
2380 
2381 
2382 
2383 
2384 
2385 static const char *simh_tape_msg(int code)
     
2386 {
2387     
2388     
2389     
2390     
2391     if (code == MTSE_OK)
2392         return "OK";
2393     else if (code == MTSE_UNATT)
2394         return "Unit not attached to a file";
2395     else if (code == MTSE_FMT)
2396         return "Unit specifies an unsupported tape file format";
2397     else if (code == MTSE_IOERR)
2398         return "Host OS I/O error";
2399     else if (code == MTSE_INVRL)
2400         return "Invalid record length (exceeds maximum allowed)";
2401     else if (code == MTSE_RECE)
2402         return "Record header contains error flag";
2403     else if (code == MTSE_TMK)
2404         return "Tape mark encountered";
2405     else if (code == MTSE_BOT)
2406         return "BOT encountered during reverse operation";
2407     else if (code == MTSE_EOM)
2408         return "End of Medium encountered";
2409     else if (code == MTSE_WRP)
2410         return "Write protected unit during write operation";
2411     else
2412         return "Unknown tape error";
2413   }
2414 
2415 t_stat attachTape (char * label, bool withring, char * drive)
     
2416   {
2417     
2418     int i;
2419     for (i = 0; i < N_MT_UNITS_MAX; i ++)
2420       {
2421         if (strcmp (drive, tape_states [i] . device_name) == 0)
2422           break;
2423       }
2424     if (i >= N_MT_UNITS_MAX)
2425       {
2426         sim_printf ("can't find device named %s\n", drive);
2427         return SCPE_ARG;
2428       }
2429     sim_printf ("attachTape selected unit %d\n", i);
2430     loadTape ((uint) i, label, ! withring);
2431     return SCPE_OK;
2432   }
2433 
2434 
2435 
2436 t_stat mount_tape (UNUSED int32 arg, const char * buf)
     
2437   {
2438     size_t bufl = strlen (buf) + 1;
2439     char fname [bufl];
2440     char ring [bufl];
2441     char drive [bufl];
2442     int nParams = sscanf (buf, "%s %s %s", fname, ring, drive);
2443     if (nParams != 3)
2444       goto usage;
2445     size_t ringl = strlen (ring);
2446     bool withring;
2447     if (strncasecmp ("noring", ring, ringl) == 0)
2448       withring = false;
2449     else if (strncasecmp ("ring", ring, ringl) == 0)
2450       withring = true;
2451     else
2452       goto usage;
2453     return attachTape (fname, withring, drive);
2454 
2455 usage:
2456      sim_printf ("mount <image.tap> ring|noring <drive>\n");
2457      return SCPE_ARG;
2458   }
2459 
2460 #ifndef QUIET_UNUSED
2461 t_stat detachTape (char * drive)
     
2462   {
2463     
2464     int i;
2465     for (i = 0; i < N_MT_UNITS_MAX; i ++)
2466       {
2467         if (strcmp (drive, tape_states [i] . device_name) == 0)
2468           break;
2469       }
2470     if (i >= N_MT_UNITS_MAX)
2471       {
2472         sim_printf ("can't find device named %s\n", drive);
2473         return SCPE_ARG;
2474       }
2475     unloadTape ((uint) i);
2476     return SCPE_OK;
2477   }
2478 #endif