root/src/dps8/dps8_append.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. selftest_ptwaw
  2. do_ldbr
  3. fetch_dsptw
  4. modify_dsptw
  5. calc_hit_am
  6. fetch_sdw_from_sdwam
  7. fetch_psdw
  8. fetch_nsdw
  9. str_sdw
  10. dump_sdwam
  11. to_be_discarded_am
  12. load_sdwam
  13. fetch_ptw_from_ptwam
  14. fetch_ptw
  15. loadPTWAM
  16. modify_ptw
  17. do_ptw2
  18. str_access_type
  19. str_acv
  20. str_pct
  21. do_append_cycle
  22. dbgLookupAddress

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * SPDX-License-Identifier: Multics
   5  * scspell-id: 4bc3e31c-f62d-11ec-bf00-80ee73e9b8e7
   6  *
   7  * ---------------------------------------------------------------------------
   8  *
   9  * Copyright (c) 2012-2016 Harry Reed
  10  * Copyright (c) 2013-2017 Charles Anthony
  11  * Copyright (c) 2017 Michal Tomek
  12  * Copyright (c) 2021-2022 The DPS8M Development Team
  13  *
  14  * All rights reserved.
  15  *
  16  * This software is made available under the terms of the ICU
  17  * License, version 1.8.1 or later.  For more details, see the
  18  * LICENSE.md file at the top-level directory of this distribution.
  19  *
  20  * ---------------------------------------------------------------------------
  21  *
  22  * This source file may contain code comments that adapt, include, and/or
  23  * incorporate Multics program code and/or documentation distributed under
  24  * the Multics License.  In the event of any discrepancy between code
  25  * comments herein and the original Multics materials, the original Multics
  26  * materials should be considered authoritative unless otherwise noted.
  27  * For more details and historical background, see the LICENSE.md file at
  28  * the top-level directory of this distribution.
  29  *
  30  * ---------------------------------------------------------------------------
  31  */
  32 
  33 #include <stdio.h>
  34 #include "dps8.h"
  35 #include "dps8_sys.h"
  36 #include "dps8_faults.h"
  37 #include "dps8_scu.h"
  38 #include "dps8_iom.h"
  39 #include "dps8_cable.h"
  40 #include "dps8_cpu.h"
  41 #include "dps8_append.h"
  42 #include "dps8_addrmods.h"
  43 #include "dps8_utils.h"
  44 #if defined(THREADZ) || defined(LOCKLESS)
  45 # include "threadz.h"
  46 #endif
  47 
  48 #define DBG_CTR cpu.cycleCnt
  49 
  50 /*
  51  * The appending unit ...
  52  */
  53 
  54 #ifdef TESTING
  55 # define DBG_CTR cpu.cycleCnt
  56 # define DBGAPP(...) sim_debug (DBG_APPENDING, & cpu_dev, __VA_ARGS__)
  57 #else
  58 # define DBGAPP(...)
  59 #endif
  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 #ifdef TESTING
 118 static char *str_sdw (char * buf, sdw_s *SDW);
 119 #endif
 120 
 121 //
 122 //
 123 //  The Use of Bit 29 in the Instruction Word
 124 //  The reader is reminded that there is a preliminary step of loading TPR.CA
 125 //  with the ADDRESS field of the instruction word during instruction decode.
 126 //  If bit 29 of the instruction word is set to 1, modification by pointer
 127 //  register is invoked and the preliminary step is executed as follows:
 128 //  1. The ADDRESS field of the instruction word is interpreted as shown in
 129 //  Figure 6-7 below.
 130 //  2. C(PRn.SNR) -> C(TPR.TSR)
 131 //  3. maximum of ( C(PRn.RNR), C(TPR.TRR), C(PPR.PRR) ) -> C(TPR.TRR)
 132 //  4. C(PRn.WORDNO) + OFFSET -> C(TPR.CA) (NOTE: OFFSET is a signed binary
 133 //  number.)
 134 //  5. C(PRn.BITNO) -> TPR.BITNO
 135 //
 136 
 137 // Define this to do error detection on the PTWAM table.
 138 // Useful if PTWAM reports an error message, but it slows the emulator
 139 // down 50%
 140 
 141 #ifdef do_selftestPTWAM
 142 static void selftest_ptwaw (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 143   {
 144     int usages[N_NAX_WAM_ENTRIES];
 145     for (int i = 0; i < N_MODEL_WAM_ENTRIES; i ++)
 146       usages[i] = -1;
 147 
 148     for (int i = 0; i < N_MODEL_WAM_ENTRIES; i ++)
 149       {
 150         ptw_s * p = cpu.PTWAM + i;
 151         if (p->USE > N_MODEL_WAM_ENTRIES - 1)
 152           sim_printf ("PTWAM[%d].USE is %d; > %d!\n",
 153                       i, p->USE, N_MODEL_WAM_ENTRIES - 1);
 154         if (usages[p->USE] != -1)
 155           sim_printf ("PTWAM[%d].USE is equal to PTWAM[%d].USE; %d\n",
 156                       i, usages[p->USE], p->USE);
 157         usages[p->USE] = i;
 158       }
 159     for (int i = 0; i < N_MODEL_WAM_ENTRIES; i ++)
 160       {
 161         if (usages[i] == -1)
 162           sim_printf ("No PTWAM had a USE of %d\n", i);
 163       }
 164   }
 165 #endif
 166 
 167 /*
 168  * implement ldbr instruction
 169  */
 170 
 171 void do_ldbr (word36 * Ypair)
     /* [previous][next][first][last][top][bottom][index][help] */
 172   {
 173     CPTUR (cptUseDSBR);
 174     if (cpu.tweaks.enable_wam)
 175       {
 176         if (cpu.cu.SD_ON)
 177           {
 178             // If SDWAM is enabled, then
 179             //   0 -> C(SDWAM(i).FULL) for i = 0, 1, ..., 15
 180             //   i -> C(SDWAM(i).USE) for i = 0, 1, ..., 15
 181             for (uint i = 0; i < N_MODEL_WAM_ENTRIES; i ++)
 182               {
 183                 cpu.SDWAM[i].FE = 0;
 184                 L68_ (cpu.SDWAM[i].USE = (word4) i;)
 185                 DPS8M_ (cpu.SDWAM[i].USE = 0;)
 186               }
 187           }
 188 
 189         if (cpu.cu.PT_ON)
 190           {
 191             // If PTWAM is enabled, then
 192             //   0 -> C(PTWAM(i).FULL) for i = 0, 1, ..., 15
 193             //   i -> C(PTWAM(i).USE) for i = 0, 1, ..., 15
 194             for (uint i = 0; i < N_MODEL_WAM_ENTRIES; i ++)
 195               {
 196                 cpu.PTWAM[i].FE = 0;
 197                 L68_ (cpu.PTWAM[i].USE = (word4) i;)
 198                 DPS8M_ (cpu.PTWAM[i].USE = 0;)
 199               }
 200 #ifdef do_selftestPTWAM
 201             selftest_ptwaw ();
 202 #endif
 203           }
 204       }
 205     else
 206       {
 207         cpu.SDW0.FE  = 0;
 208         cpu.SDW0.USE = 0;
 209         cpu.PTW0.FE  = 0;
 210         cpu.PTW0.USE = 0;
 211       }
 212 
 213     // If cache is enabled, reset all cache column and level full flags
 214     // XXX no cache
 215 
 216     // C(Y-pair) 0,23 -> C(DSBR.ADDR)
 217     cpu.DSBR.ADDR  = (Ypair[0] >> (35 - 23)) & PAMASK;
 218 
 219     // C(Y-pair) 37,50 -> C(DSBR.BOUND)
 220     cpu.DSBR.BND   = (Ypair[1] >> (71 - 50)) & 037777;
 221 
 222     // C(Y-pair) 55 -> C(DSBR.U)
 223     cpu.DSBR.U     = (Ypair[1] >> (71 - 55)) & 01;
 224 
 225     // C(Y-pair) 60,71 -> C(DSBR.STACK)
 226     cpu.DSBR.STACK = (Ypair[1] >> (71 - 71)) & 07777;
 227     DBGAPP ("ldbr 0 -> SDWAM/PTWAM[*].F, i -> SDWAM/PTWAM[i].USE, "
 228             "DSBR.ADDR 0%o, DSBR.BND 0%o, DSBR.U 0%o, DSBR.STACK 0%o\n",
 229             cpu.DSBR.ADDR, cpu.DSBR.BND, cpu.DSBR.U, cpu.DSBR.STACK);
 230   }
 231 
 232 /*
 233  * fetch descriptor segment PTW ...
 234  */
 235 
 236 // CANFAULT
 237 
 238 static void fetch_dsptw (word15 segno)
     /* [previous][next][first][last][top][bottom][index][help] */
 239   {
 240     DBGAPP ("%s segno 0%o\n", __func__, segno);
 241     PNL (L68_ (cpu.apu.state |= apu_FDPT;))
 242 
 243     if (2 * segno >= 16 * (cpu.DSBR.BND + 1))
 244       {
 245         DBGAPP ("%s ACV15\n", __func__);
 246         // generate access violation, out of segment bounds fault
 247         PNL (cpu.acvFaults |= ACV15;)
 248         PNL (L68_ (cpu.apu.state |= apu_FLT;))
 249         doFault (FAULT_ACV, fst_acv15,
 250                  "acvFault: fetch_dsptw out of segment bounds fault");
 251       }
 252     set_apu_status (apuStatus_DSPTW);
 253 
 254     //word24 y1 = (2u * segno) % 1024u;
 255     word24 x1 = (2u * segno) / 1024u; // floor
 256 
 257     PNL (cpu.lastPTWOffset = segno;)
 258     PNL (cpu.lastPTWIsDS = true;)
 259 
 260     word36 PTWx1;
 261     core_read ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
 262 
 263     cpu.PTW0.ADDR = GETHI (PTWx1);
 264     cpu.PTW0.U    = TSTBIT (PTWx1, 9);
 265     cpu.PTW0.M    = TSTBIT (PTWx1, 6);
 266     cpu.PTW0.DF   = TSTBIT (PTWx1, 2);
 267     cpu.PTW0.FC   = PTWx1 & 3;
 268 
 269     L68_ (if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 270       add_l68_APU_history (APUH_FDSPTW);)
 271 
 272     DBGAPP ("%s x1 0%o DSBR.ADDR 0%o PTWx1 0%012"PRIo64" "
 273             "PTW0: ADDR 0%o U %o M %o F %o FC %o\n",
 274             __func__, x1, cpu.DSBR.ADDR, PTWx1, cpu.PTW0.ADDR, cpu.PTW0.U,
 275             cpu.PTW0.M, cpu.PTW0.DF, cpu.PTW0.FC);
 276   }
 277 
 278 /*
 279  * modify descriptor segment PTW (Set U=1) ...
 280  */
 281 
 282 // CANFAULT
 283 
 284 static void modify_dsptw (word15 segno)
     /* [previous][next][first][last][top][bottom][index][help] */
 285   {
 286 
 287     PNL (L68_ (cpu.apu.state |= apu_MDPT;))
 288 
 289     set_apu_status (apuStatus_MDSPTW);
 290 
 291     word24 x1 = (2u * segno) / 1024u; // floor
 292 
 293 #ifdef THREADZ
 294     bool lck = get_rmw_lock ();
 295     if (! lck)
 296       lock_rmw ();
 297 #endif
 298 
 299     word36 PTWx1;
 300 #ifdef LOCKLESS
 301     core_read_lock ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
 302     PTWx1 = SETBIT (PTWx1, 9);
 303     core_write_unlock ((cpu.DSBR.ADDR + x1) & PAMASK, PTWx1, __func__);
 304 #else
 305     core_read ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
 306     PTWx1 = SETBIT (PTWx1, 9);
 307     core_write ((cpu.DSBR.ADDR + x1) & PAMASK, PTWx1, __func__);
 308 #endif
 309 
 310 #ifdef THREADZ
 311     if (! lck)
 312       unlock_rmw ();
 313 #endif
 314 
 315     cpu.PTW0.U = 1;
 316     L68_ (if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 317       add_l68_APU_history (APUH_MDSPTW);)
 318   }
 319 
 320 static word6 calc_hit_am (word6 LRU, uint hit_level)
     /* [previous][next][first][last][top][bottom][index][help] */
 321   {
 322     switch (hit_level)
 323       {
 324         case 0:  // hit level A
 325           return (LRU | 070);
 326         case 1:  // hit level B
 327           return ((LRU & 037) | 06);
 328         case 2:  // hit level C
 329           return ((LRU & 053) | 01);
 330         case 3:  // hit level D
 331           return (LRU & 064);
 332         default:
 333           DBGAPP ("%s: Invalid AM level\n", __func__);
 334           return 0;
 335      }
 336   }
 337 
 338 static sdw_s * fetch_sdw_from_sdwam (word15 segno) {
     /* [previous][next][first][last][top][bottom][index][help] */
 339   DBGAPP ("%s(0):segno=%05o\n", __func__, segno);
 340 
 341   if ((! cpu.tweaks.enable_wam || ! cpu.cu.SD_ON)) {
 342     DBGAPP ("%s(0): SDWAM disabled\n", __func__);
 343     return NULL;
 344   }
 345 
 346   if (cpu.tweaks.l68_mode) { // L68
 347     int nwam = N_L68_WAM_ENTRIES;
 348     for (int _n = 0; _n < nwam; _n++) {
 349       // make certain we initialize SDWAM prior to use!!!
 350       if (cpu.SDWAM[_n].FE && segno == cpu.SDWAM[_n].POINTER) {
 351         DBGAPP ("%s(1):found match for segno %05o " "at _n=%d\n", __func__, segno, _n);
 352 
 353         cpu.cu.SDWAMM = 1;
 354         cpu.SDWAMR = (word4) _n;
 355         cpu.SDW = & cpu.SDWAM[_n];
 356 
 357         // If the SDWAM match logic circuitry indicates a hit, all usage
 358         // counts (SDWAM.USE) greater than the usage count of the register
 359         // hit are decremented by one, the usage count of the register hit
 360         // is set to 15, and the contents of the register hit are read out
 361         // into the address preparation circuitry.
 362 
 363         for (int _h = 0; _h < nwam; _h++) {
 364           if (cpu.SDWAM[_h].USE > cpu.SDW->USE)
 365             cpu.SDWAM[_h].USE -= 1;
 366         }
 367         cpu.SDW->USE = N_L68_WAM_ENTRIES - 1;
 368 
 369         char buf[256];
 370         (void)buf;
 371 #ifdef TESTING
 372         DBGAPP ("%s(2):SDWAM[%d]=%s\n", __func__, _n, str_sdw (buf, cpu.SDW));
 373 #endif
 374         return cpu.SDW;
 375       }
 376     }
 377   }
 378 
 379   if (! cpu.tweaks.l68_mode) { // DPS8M
 380     uint setno = segno & 017;
 381     uint toffset;
 382     sdw_s *p;
 383     for (toffset = 0; toffset < 64; toffset += 16) {
 384       p = & cpu.SDWAM[toffset + setno];
 385       if (p->FE && segno == p->POINTER) {
 386         DBGAPP ("%s(1):found match for segno %05o " "at _n=%d\n", __func__, segno, toffset + setno);
 387 
 388         cpu.cu.SDWAMM = 1;
 389         cpu.SDWAMR = (word6) (toffset + setno);
 390         cpu.SDW = p; // export pointer for appending
 391 
 392         word6 u = calc_hit_am (p->USE, toffset >> 4);
 393         for (toffset = 0; toffset < 64; toffset += 16) { // update LRU
 394           p = & cpu.SDWAM[toffset + setno];
 395           if (p->FE)
 396             p->USE = u;
 397         }
 398 
 399         char buf[256];
 400         (void)buf;
 401 #ifdef TESTING
 402         DBGAPP ("%s(2):SDWAM[%d]=%s\n", __func__, toffset + setno, str_sdw (buf, cpu.SDW));
 403 #endif
 404         return cpu.SDW;
 405       }
 406     }
 407   }
 408 #ifdef TESTING
 409   DBGAPP ("%s(3):SDW for segment %05o not found in SDWAM\n", __func__, segno);
 410 #endif
 411   cpu.cu.SDWAMM = 0;
 412   return NULL;    // segment not referenced in SDWAM
 413 }
 414 
 415 /*
 416  * Fetches an SDW from a paged descriptor segment.
 417  */
 418 // CANFAULT
 419 
 420 static void fetch_psdw (word15 segno)
     /* [previous][next][first][last][top][bottom][index][help] */
 421   {
 422     DBGAPP ("%s(0):segno=%05o\n",
 423             __func__, segno);
 424 
 425     PNL (L68_ (cpu.apu.state |= apu_FSDP;))
 426 
 427     set_apu_status (apuStatus_SDWP);
 428     word24 y1 = (2 * segno) % 1024;
 429 
 430     word36 SDWeven, SDWodd;
 431 
 432     core_read2 (((((word24) cpu.PTW0.ADDR & 0777760) << 6) + y1) & PAMASK,
 433                 & SDWeven, & SDWodd, __func__);
 434 
 435     // even word
 436     cpu.SDW0.ADDR  = (SDWeven >> 12) & 077777777;
 437     cpu.SDW0.R1    = (SDWeven >> 9)  & 7;
 438     cpu.SDW0.R2    = (SDWeven >> 6)  & 7;
 439     cpu.SDW0.R3    = (SDWeven >> 3)  & 7;
 440     cpu.SDW0.DF    = TSTBIT (SDWeven, 2);
 441     cpu.SDW0.FC    = SDWeven & 3;
 442 
 443     // odd word
 444     cpu.SDW0.BOUND = (SDWodd >> 21) & 037777;
 445     cpu.SDW0.R     = TSTBIT (SDWodd, 20);
 446     cpu.SDW0.E     = TSTBIT (SDWodd, 19);
 447     cpu.SDW0.W     = TSTBIT (SDWodd, 18);
 448     cpu.SDW0.P     = TSTBIT (SDWodd, 17);
 449     cpu.SDW0.U     = TSTBIT (SDWodd, 16);
 450     cpu.SDW0.G     = TSTBIT (SDWodd, 15);
 451     cpu.SDW0.C     = TSTBIT (SDWodd, 14);
 452     cpu.SDW0.EB    = SDWodd & 037777;
 453 
 454     L68_ (
 455       if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 456         add_l68_APU_history (APUH_FSDWP);
 457      )
 458     DBGAPP ("%s y1 0%o p->ADDR 0%o SDW 0%012"PRIo64" 0%012"PRIo64" "
 459             "ADDR %o R %o%o%o BOUND 0%o REWPUGC %o%o%o%o%o%o%o "
 460             "F %o FC %o FE %o USE %o\n",
 461             __func__, y1, cpu.PTW0.ADDR, SDWeven, SDWodd, cpu.SDW0.ADDR,
 462             cpu.SDW0.R1, cpu.SDW0.R2, cpu.SDW0.R3, cpu.SDW0.BOUND,
 463             cpu.SDW0.R, cpu.SDW0.E, cpu.SDW0.W, cpu.SDW0.P, cpu.SDW0.U,
 464             cpu.SDW0.G, cpu.SDW0.C, cpu.SDW0.DF, cpu.SDW0.FC, cpu.SDW0.FE,
 465             cpu.SDW0.USE);
 466   }
 467 
 468 // Nonpaged SDW Fetch
 469 // Fetches an SDW from an unpaged descriptor segment.
 470 // CANFAULT
 471 
 472 static void fetch_nsdw (word15 segno)
     /* [previous][next][first][last][top][bottom][index][help] */
 473   {
 474     DBGAPP ("%s (0):segno=%05o\n", __func__, segno);
 475 
 476     PNL (L68_ (cpu.apu.state |= apu_FSDN;))
 477 
 478     set_apu_status (apuStatus_SDWNP);
 479 
 480     if (2 * segno >= 16 * (cpu.DSBR.BND + 1))
 481       {
 482         DBGAPP ("%s (1):Access Violation, out of segment bounds for "
 483                 "segno=%05o DSBR.BND=%d\n",
 484                 __func__, segno, cpu.DSBR.BND);
 485         // generate access violation, out of segment bounds fault
 486         PNL (cpu.acvFaults |= ACV15;)
 487         PNL (L68_ (cpu.apu.state |= apu_FLT;))
 488         doFault (FAULT_ACV, fst_acv15,
 489                  "acvFault fetch_dsptw: out of segment bounds fault");
 490       }
 491     DBGAPP ("%s (2):fetching SDW from %05o\n",
 492             __func__, cpu.DSBR.ADDR + 2u * segno);
 493 
 494     word36 SDWeven, SDWodd;
 495     core_read2 ((cpu.DSBR.ADDR + 2u * segno) & PAMASK,
 496                 & SDWeven, & SDWodd, __func__);
 497 
 498     // even word
 499     cpu.SDW0.ADDR = (SDWeven >> 12) & 077777777;
 500     cpu.SDW0.R1   = (SDWeven >> 9)  & 7;
 501     cpu.SDW0.R2   = (SDWeven >> 6)  & 7;
 502     cpu.SDW0.R3   = (SDWeven >> 3)  & 7;
 503     cpu.SDW0.DF   = TSTBIT (SDWeven, 2);
 504     cpu.SDW0.FC   = SDWeven & 3;
 505 
 506     // odd word
 507     cpu.SDW0.BOUND = (SDWodd >> 21) & 037777;
 508     cpu.SDW0.R     = TSTBIT (SDWodd, 20);
 509     cpu.SDW0.E     = TSTBIT (SDWodd, 19);
 510     cpu.SDW0.W     = TSTBIT (SDWodd, 18);
 511     cpu.SDW0.P     = TSTBIT (SDWodd, 17);
 512     cpu.SDW0.U     = TSTBIT (SDWodd, 16);
 513     cpu.SDW0.G     = TSTBIT (SDWodd, 15);
 514     cpu.SDW0.C     = TSTBIT (SDWodd, 14);
 515     cpu.SDW0.EB    = SDWodd & 037777;
 516 
 517     L68_ (
 518       if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 519         add_l68_APU_history (0 /* No fetch no paged bit */);
 520     )
 521 #ifndef SPEED
 522     char buf[256];
 523     (void)buf;
 524     DBGAPP ("%s (2):SDW0=%s\n", __func__, str_SDW0 (buf, & cpu.SDW0));
 525 #endif
 526   }
 527 
 528 #ifdef TESTING
 529 static char *str_sdw (char * buf, sdw_s *SDW)
     /* [previous][next][first][last][top][bottom][index][help] */
 530   {
 531     if (! SDW->FE)
 532       sprintf (buf, "*** SDW Uninitialized ***");
 533     else
 534       sprintf (buf,
 535                "ADDR:%06o R1:%o R2:%o R3:%o BOUND:%o R:%o E:%o W:%o P:%o "
 536                "U:%o G:%o C:%o CL:%o DF:%o FC:%o POINTER=%o USE=%d",
 537                SDW->ADDR,    SDW->R1, SDW->R2, SDW->R3, SDW->BOUND,
 538                SDW->R,       SDW->E,  SDW->W,  SDW->P,  SDW->U,
 539                SDW->G,       SDW->C,  SDW->EB, SDW->DF, SDW->FC,
 540                SDW->POINTER, SDW->USE);
 541     return buf;
 542   }
 543 
 544 /*
 545  * dump SDWAM...
 546  */
 547 
 548 # ifdef TESTING
 549 t_stat dump_sdwam (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 550   {
 551     char buf[256];
 552     (void)buf;
 553     for (int _n = 0; _n < N_MODEL_WAM_ENTRIES; _n++)
 554       {
 555         sdw_s *p = & cpu.SDWAM[_n];
 556 
 557         if (p->FE)
 558           sim_printf ("SDWAM n:%d %s\n", _n, str_sdw (buf, p));
 559       }
 560     return SCPE_OK;
 561   }
 562 # endif
 563 #endif
 564 
 565 static uint to_be_discarded_am (word6 LRU)
     /* [previous][next][first][last][top][bottom][index][help] */
 566   {
 567 
 568 
 569 
 570 
 571 
 572 
 573 
 574 
 575 
 576 
 577 
 578 
 579     if ((LRU & 070) == 070) return 0;
 580     if ((LRU & 046) == 006) return 1;
 581     if ((LRU & 025) == 001) return 2;
 582     return 3;
 583   }
 584 
 585 /*
 586  * load the current in-core SDW0 into the SDWAM ...
 587  */
 588 
 589 static void load_sdwam (word15 segno, bool nomatch)
     /* [previous][next][first][last][top][bottom][index][help] */
 590   {
 591     cpu.SDW0.POINTER = segno;
 592     cpu.SDW0.USE = 0;
 593 
 594     cpu.SDW0.FE = true;     // in use by SDWAM
 595 
 596     cpu.SDW = & cpu.SDW0;
 597 
 598     if (nomatch || (! cpu.tweaks.enable_wam) || (! cpu.cu.SD_ON))
 599       {
 600         DBGAPP ("%s: SDWAM disabled\n", __func__);
 601         return;
 602       }
 603 
 604     if (cpu.tweaks.l68_mode) { // L68
 605       // If the SDWAM match logic does not indicate a hit, the SDW is fetched
 606       // from the descriptor segment in main memory and loaded into the SDWAM
 607       // register with usage count 0 (the oldest), all usage counts are
 608       // decremented by one with the newly loaded register rolling over from 0 to
 609       // 15, and the newly loaded register is read out into the address
 610       // preparation circuitry.
 611 
 612       for (int _n = 0; _n < N_L68_WAM_ENTRIES; _n++) {
 613         sdw_s * p = & cpu.SDWAM[_n];
 614         if (! p->FE || p->USE == 0) {
 615           DBGAPP ("%s(1):SDWAM[%d] FE=0 || USE=0\n", __func__, _n);
 616 
 617           * p = cpu.SDW0;
 618           p->POINTER = segno;
 619           p->USE = 0;
 620           p->FE = true;     // in use by SDWAM
 621 
 622           for (int _h = 0; _h < N_L68_WAM_ENTRIES; _h++) {
 623             sdw_s * q = & cpu.SDWAM[_h];
 624             q->USE -= 1;
 625             q->USE &= N_L68_WAM_MASK;
 626           }
 627 
 628           cpu.SDW = p;
 629 
 630           char buf[256];
 631           (void) buf;
 632 #ifdef TESTING
 633           DBGAPP ("%s(2):SDWAM[%d]=%s\n", __func__, _n, str_sdw (buf, p));
 634 #endif
 635           return;
 636         }
 637       }
 638       // if we reach this, USE is scrambled
 639 #ifdef TESTING
 640       DBGAPP ("%s(3) no USE=0 found for segment=%d\n", __func__, segno);
 641       sim_printf ("%s(%05o): no USE=0 found!\n", __func__, segno);
 642       dump_sdwam ();
 643 #endif
 644     }
 645 
 646     if (! cpu.tweaks.l68_mode) { // DPS8M
 647       uint setno = segno & 017;
 648       uint toffset;
 649       sdw_s *p;
 650       for (toffset = 0; toffset < 64; toffset += 16) {
 651         p = & cpu.SDWAM[toffset + setno];
 652         if (!p->FE)
 653           break;
 654       }
 655       if (toffset == 64) { // all FE==1
 656         toffset = to_be_discarded_am (p->USE) << 4;
 657         p = & cpu.SDWAM[toffset + setno];
 658       }
 659       DBGAPP ("%s(1):SDWAM[%d] FE=0 || LRU\n", __func__, toffset + setno);
 660 
 661       word6 u = calc_hit_am (p->USE, toffset >> 4); // before loading the SDWAM!
 662       * p = cpu.SDW0; // load the SDW
 663       p->POINTER = segno;
 664       p->FE = true;  // in use
 665       cpu.SDW = p; // export pointer for appending
 666 
 667       for (uint toffset1 = 0; toffset1 < 64; toffset1 += 16) { // update LRU
 668         p = & cpu.SDWAM[toffset1 + setno];
 669         if (p->FE)
 670           p->USE = u;
 671       }
 672 
 673       char buf[256];
 674       (void) buf;
 675 #ifdef TESTING
 676       DBGAPP ("%s(2):SDWAM[%d]=%s\n", __func__, toffset + setno, str_sdw (buf, cpu.SDW));
 677 #endif
 678     } // DPS8M
 679   }
 680 
 681 static ptw_s * fetch_ptw_from_ptwam (word15 segno, word18 CA)
     /* [previous][next][first][last][top][bottom][index][help] */
 682   {
 683     if ((! cpu.tweaks.enable_wam) || (! cpu.cu.PT_ON))
 684       {
 685         DBGAPP ("%s: PTWAM disabled\n", __func__);
 686         return NULL;
 687       }
 688 
 689     if (cpu.tweaks.l68_mode) { // L68
 690       int nwam = N_L68_WAM_ENTRIES;
 691       for (int _n = 0; _n < nwam; _n++)
 692         {
 693           if (cpu.PTWAM[_n].FE && ((CA >> 6) & 07760) == cpu.PTWAM[_n].PAGENO &&
 694               cpu.PTWAM[_n].POINTER == segno)   //_initialized
 695             {
 696               DBGAPP ("%s: found match for segno=%o pageno=%o "
 697                       "at _n=%d\n",
 698                       __func__, segno, cpu.PTWAM[_n].PAGENO, _n);
 699               cpu.cu.PTWAMM = 1;
 700               cpu.PTWAMR = (word4) _n;
 701               cpu.PTW = & cpu.PTWAM[_n];
 702 
 703               // If the PTWAM match logic circuitry indicates a hit, all usage
 704               // counts (PTWAM.USE) greater than the usage count of the register
 705               // hit are decremented by one, the usage count of the register hit
 706               // is set to 15, and the contents of the register hit are read out
 707               // into the address preparation circuitry.
 708 
 709               for (int _h = 0; _h < nwam; _h++)
 710                 {
 711                   if (cpu.PTWAM[_h].USE > cpu.PTW->USE)
 712                     cpu.PTWAM[_h].USE -= 1; //PTW->USE -= 1;
 713                 }
 714               cpu.PTW->USE = N_L68_WAM_ENTRIES - 1;
 715 #ifdef do_selftestPTWAM
 716               selftest_ptwaw ();
 717 #endif
 718               DBGAPP ("%s: ADDR 0%o U %o M %o F %o FC %o\n",
 719                       __func__, cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M,
 720                       cpu.PTW->DF, cpu.PTW->FC);
 721               return cpu.PTW;
 722             }
 723         }
 724     }
 725 
 726     DPS8M_ (
 727       uint setno = (CA >> 10) & 017;
 728       uint toffset;
 729       ptw_s *p;
 730       for (toffset = 0; toffset < 64; toffset += 16)
 731         {
 732           p = & cpu.PTWAM[toffset + setno];
 733 
 734           if (p->FE && ((CA >> 6) & 07760) == p->PAGENO && p->POINTER == segno)
 735             {
 736               DBGAPP ("%s: found match for segno=%o pageno=%o "
 737                       "at _n=%d\n",
 738                       __func__, segno, p->PAGENO, toffset + setno);
 739               cpu.cu.PTWAMM = 1;
 740               cpu.PTWAMR = (word6) (toffset + setno);
 741               cpu.PTW = p; // export pointer for appending
 742 
 743               word6 u = calc_hit_am (p->USE, toffset >> 4);
 744               for (toffset = 0; toffset < 64; toffset += 16) // update LRU
 745                 {
 746                   p = & cpu.PTWAM[toffset + setno];
 747                   if (p->FE)
 748                     p->USE = u;
 749                 }
 750 
 751               DBGAPP ("%s: ADDR 0%o U %o M %o F %o FC %o\n",
 752                       __func__, cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M,
 753                       cpu.PTW->DF, cpu.PTW->FC);
 754               return cpu.PTW;
 755             }
 756         }
 757      )
 758     cpu.cu.PTWAMM = 0;
 759     return NULL;    // page not referenced in PTWAM
 760   }
 761 
 762 static void fetch_ptw (sdw_s *sdw, word18 offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 763   {
 764     // AL39 p.5-7
 765     // Fetches a PTW from a page table other than a descriptor segment page
 766     // table and sets the page accessed bit (PTW.U)
 767     PNL (L68_ (cpu.apu.state |= apu_FPTW;))
 768     set_apu_status (apuStatus_PTW);
 769 
 770     //word24 y2 = offset % 1024;
 771     word24 x2 = (offset) / 1024; // floor
 772 
 773     word36 PTWx2;
 774 
 775     DBGAPP ("%s address %08o\n", __func__, sdw->ADDR + x2);
 776 
 777     PNL (cpu.lastPTWOffset = offset;)
 778     PNL (cpu.lastPTWIsDS = false;)
 779 
 780 #ifdef THREADZ
 781     bool lck = get_rmw_lock ();
 782     if (! lck)
 783       lock_rmw ();
 784 #endif
 785 #ifdef LOCKLESS
 786     core_read_lock ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
 787 #else
 788     core_read ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
 789 #endif
 790 
 791     cpu.PTW0.ADDR = GETHI  (PTWx2);
 792     cpu.PTW0.U    = TSTBIT (PTWx2, 9);
 793     cpu.PTW0.M    = TSTBIT (PTWx2, 6);
 794     cpu.PTW0.DF   = TSTBIT (PTWx2, 2);
 795     cpu.PTW0.FC   = PTWx2 & 3;
 796 
 797     // ISOLTS-861 02
 798 #ifndef LOCKLESS
 799     if (! cpu.PTW0.U)
 800 #endif
 801       {
 802         PTWx2 = SETBIT (PTWx2, 9);
 803 #ifdef LOCKLESS
 804         core_write_unlock ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
 805 #else
 806         core_write ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
 807 #endif
 808         cpu.PTW0.U = 1;
 809       }
 810 
 811 #ifdef THREADZ
 812     if (! lck)
 813       unlock_rmw ();
 814 #endif
 815 
 816     L68_ (if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 817       add_l68_APU_history (APUH_FPTW);)
 818 
 819     DBGAPP ("%s x2 0%o sdw->ADDR 0%o PTWx2 0%012"PRIo64" "
 820             "PTW0: ADDR 0%o U %o M %o F %o FC %o\n",
 821             __func__, x2, sdw->ADDR, PTWx2, cpu.PTW0.ADDR, cpu.PTW0.U,
 822             cpu.PTW0.M, cpu.PTW0.DF, cpu.PTW0.FC);
 823   }
 824 
 825 static void loadPTWAM (word15 segno, word18 offset, UNUSED bool nomatch)
     /* [previous][next][first][last][top][bottom][index][help] */
 826   {
 827     cpu.PTW0.PAGENO  = (offset >> 6) & 07760;
 828     cpu.PTW0.POINTER = segno;
 829     cpu.PTW0.USE     = 0;
 830     cpu.PTW0.FE      = true;
 831 
 832     cpu.PTW = & cpu.PTW0;
 833     if (nomatch || (! cpu.tweaks.enable_wam) || (! cpu.cu.PT_ON))
 834       {
 835         DBGAPP ("loadPTWAM: PTWAM disabled\n");
 836         return;
 837       }
 838 
 839     if (cpu.tweaks.l68_mode) { // L68
 840       // If the PTWAM match logic does not indicate a hit, the PTW is fetched
 841       // from main memory and loaded into the PTWAM register with usage count 0
 842       // (the oldest), all usage counts are decremented by one with the newly
 843       // loaded register rolling over from 0 to 15, and the newly loaded register
 844       // is read out into the address preparation circuitry.
 845 
 846       for (int _n = 0; _n < N_L68_WAM_ENTRIES; _n++)
 847         {
 848           ptw_s * p = & cpu.PTWAM[_n];
 849           if (! p->FE || p->USE == 0)
 850             {
 851               DBGAPP ("loadPTWAM(1):PTWAM[%d] FE=0 || USE=0\n", _n);
 852               *p = cpu.PTW0;
 853               p->PAGENO = (offset >> 6) & 07760;
 854               p->POINTER = segno;
 855               p->USE = 0;
 856               p->FE = true;
 857 
 858               for (int _h = 0; _h < N_L68_WAM_ENTRIES; _h++)
 859                 {
 860                   ptw_s * q = & cpu.PTWAM[_h];
 861                   q->USE -= 1;
 862                   q->USE &= N_L68_WAM_MASK;
 863                 }
 864 
 865               cpu.PTW = p;
 866               DBGAPP ("loadPTWAM(2): ADDR 0%o U %o M %o F %o FC %o "
 867                       "POINTER=%o PAGENO=%o USE=%d\n",
 868                       cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M, cpu.PTW->DF,
 869                       cpu.PTW->FC, cpu.PTW->POINTER, cpu.PTW->PAGENO,
 870                       cpu.PTW->USE);
 871 #ifdef do_selftestPTWAM
 872               selftest_ptwaw ();
 873 #endif
 874               return;
 875             }
 876         }
 877       // if we reach this, USE is scrambled
 878       sim_printf ("loadPTWAM(segno=%05o, offset=%012o): no USE=0 found!\n",
 879                   segno, offset);
 880     }
 881 
 882     DPS8M_ (
 883       uint setno = (offset >> 10) & 017;
 884       uint toffset;
 885       ptw_s *p;
 886       for (toffset = 0; toffset < 64; toffset += 16)
 887         {
 888           p = & cpu.PTWAM[toffset + setno];
 889           if (! p->FE)
 890             break;
 891         }
 892       if (toffset == 64) // all FE==1
 893         {
 894           toffset = to_be_discarded_am (p->USE) << 4;
 895           p = & cpu.PTWAM[toffset + setno];
 896         }
 897 
 898       DBGAPP ("loadPTWAM(1):PTWAM[%d] FE=0 || LRU\n",
 899               toffset + setno);
 900 
 901       word6 u = calc_hit_am (p->USE, toffset >> 4); // before loading the PTWAM
 902       * p = cpu.PTW0; // load the PTW
 903       p->PAGENO = (offset >> 6) & 07760;
 904       p->POINTER = segno;
 905       p->FE = true;  // in use
 906       cpu.PTW = p; // export pointer for appending
 907 
 908       for (uint toffset1 = 0; toffset1 < 64; toffset1 += 16) // update LRU
 909         {
 910           p = & cpu.PTWAM[toffset1 + setno];
 911           if (p->FE)
 912             p->USE = u;
 913         }
 914 
 915       DBGAPP ("loadPTWAM(2): ADDR 0%o U %o M %o F %o FC %o POINTER=%o "
 916               "PAGENO=%o USE=%d\n",
 917               cpu.PTW->ADDR, cpu.PTW->U, cpu.PTW->M, cpu.PTW->DF,
 918               cpu.PTW->FC, cpu.PTW->POINTER, cpu.PTW->PAGENO, cpu.PTW->USE);
 919     )
 920   }
 921 
 922 /*
 923  * modify target segment PTW (Set M=1) ...
 924  */
 925 
 926 static void modify_ptw (sdw_s *sdw, word18 offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 927   {
 928     PNL (L68_ (cpu.apu.state |= apu_MPTW;))
 929     //word24 y2 = offset % 1024;
 930     word24 x2 = offset / 1024; // floor
 931 
 932     word36 PTWx2;
 933 
 934     set_apu_status (apuStatus_MPTW);
 935 
 936 #ifdef THREADZ
 937     bool lck = get_rmw_lock ();
 938     if (! lck)
 939       lock_rmw ();
 940 #endif
 941 #ifdef LOCKLESS
 942     core_read_lock ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
 943     PTWx2 = SETBIT (PTWx2, 6);
 944     core_write_unlock ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
 945 #else
 946     core_read ((sdw->ADDR + x2) & PAMASK, & PTWx2, __func__);
 947     PTWx2 = SETBIT (PTWx2, 6);
 948     core_write ((sdw->ADDR + x2) & PAMASK, PTWx2, __func__);
 949 #endif
 950 #ifdef THREADZ
 951     if (! lck)
 952       unlock_rmw ();
 953 #endif
 954     cpu.PTW->M = 1;
 955     L68_ (if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 956       add_l68_APU_history (APUH_MPTW);)
 957   }
 958 
 959 static void do_ptw2 (sdw_s *sdw, word18 offset)
     /* [previous][next][first][last][top][bottom][index][help] */
 960   {
 961     PNL (L68_ (cpu.apu.state |= apu_FPTW2;))
 962     set_apu_status (apuStatus_PTW2);
 963 
 964     //word24 y2 = offset % 1024;
 965     word24 x2 = (offset) / 1024; // floor
 966 
 967     word36 PTWx2n;
 968 
 969     DBGAPP ("%s address %08o\n", __func__, sdw->ADDR + x2 + 1);
 970 
 971     core_read ((sdw->ADDR + x2 + 1) & PAMASK, & PTWx2n, __func__);
 972 
 973     ptw_s PTW2;
 974     PTW2.ADDR = GETHI (PTWx2n);
 975     PTW2.U    = TSTBIT (PTWx2n, 9);
 976     PTW2.M    = TSTBIT (PTWx2n, 6);
 977     PTW2.DF   = TSTBIT (PTWx2n, 2);
 978     PTW2.FC   = PTWx2n & 3;
 979 
 980     L68_ (if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
 981       add_l68_APU_history (APUH_FPTW2);)
 982 
 983     DBGAPP ("%s x2 0%o sdw->ADDR 0%o PTW2 0%012"PRIo64" "
 984             "PTW2: ADDR 0%o U %o M %o F %o FC %o\n",
 985             __func__, x2, sdw->ADDR, PTWx2n, PTW2.ADDR, PTW2.U, PTW2.M,
 986             PTW2.DF, PTW2.FC);
 987 
 988     // check that PTW2 is the next page of the same segment
 989     // ISOLTS 875 02a
 990     if ((PTW2.ADDR & 0777760) == (cpu.PTW->ADDR & 0777760) + 16)
 991        //Is PTW2.F set ON?
 992        if (! PTW2.DF)
 993            // initiate a directed fault
 994            doFault (FAULT_DF0 + PTW2.FC, fst_zero, "PTW2.F == 0");
 995   }
 996 
 997 /*
 998  * Is the instruction a SToRage OPeration ?
 999  */
1000 
1001 #ifndef QUIET_UNUSED
1002 static char *str_access_type (MemoryAccessType accessType)
     /* [previous][next][first][last][top][bottom][index][help] */
1003   {
1004     switch (accessType)
1005       {
1006         case UnknownMAT:        return "Unknown";
1007         case OperandRead:       return "OperandRead";
1008         case OperandWrite:      return "OperandWrite";
1009         default:                return "???";
1010       }
1011   }
1012 #endif
1013 
1014 #ifndef QUIET_UNUSED
1015 static char *str_acv (_fault_subtype acv)
     /* [previous][next][first][last][top][bottom][index][help] */
1016   {
1017     switch (acv)
1018       {
1019         case ACV0:  return "Illegal ring order (ACV0=IRO)";
1020         case ACV1:  return "Not in execute bracket (ACV1=OEB)";
1021         case ACV2:  return "No execute permission (ACV2=E-OFF)";
1022         case ACV3:  return "Not in read bracket (ACV3=ORB)";
1023         case ACV4:  return "No read permission (ACV4=R-OFF)";
1024         case ACV5:  return "Not in write bracket (ACV5=OWB)";
1025         case ACV6:  return "No write permission (ACV6=W-OFF)";
1026         case ACV7:  return "Call limiter fault (ACV7=NO GA)";
1027         case ACV8:  return "Out of call brackets (ACV8=OCB)";
1028         case ACV9:  return "Outward call (ACV9=OCALL)";
1029         case ACV10: return "Bad outward call (ACV10=BOC)";
1030         case ACV11: return "Inward return (ACV11=INRET) XXX ??";
1031         case ACV12: return "Invalid ring crossing (ACV12=CRT)";
1032         case ACV13: return "Ring alarm (ACV13=RALR)";
1033         case ACV14: return "Associative memory error XXX ??";
1034         case ACV15: return "Out of segment bounds (ACV15=OOSB)";
1035         //case ACDF0: return "Directed fault 0";
1036         //case ACDF1: return "Directed fault 1";
1037         //case ACDF2: return "Directed fault 2";
1038         //case ACDF3: return "Directed fault 3";
1039         default:
1040             break;
1041       }
1042   return "unhandled acv in str_acv";
1043   }
1044 #endif
1045 
1046 static char *str_pct (processor_cycle_type t)
     /* [previous][next][first][last][top][bottom][index][help] */
1047   {
1048     switch (t)
1049       {
1050         case UNKNOWN_CYCLE:       return "UNKNOWN_CYCLE";
1051         case OPERAND_STORE:       return "OPERAND_STORE";
1052         case OPERAND_READ:        return "OPERAND_READ";
1053         case INDIRECT_WORD_FETCH: return "INDIRECT_WORD_FETCH";
1054         case RTCD_OPERAND_FETCH:  return "RTCD_OPERAND_FETCH";
1055         case INSTRUCTION_FETCH:   return "INSTRUCTION_FETCH";
1056         case APU_DATA_READ:       return "APU_DATA_READ";
1057         case APU_DATA_STORE:      return "APU_DATA_STORE";
1058         case ABSA_CYCLE:          return "ABSA_CYCLE";
1059 #ifdef LOCKLESS
1060         case OPERAND_RMW:         return "OPERAND_RMW";
1061         case APU_DATA_RMW:        return "APU_DATA_RMW";
1062 #endif
1063 
1064         default:
1065             return "Unhandled processor_cycle_type";
1066       }
1067   }
1068 
1069 /*
1070  * recoding APU functions to more closely match Fig 5,6 & 8 ...
1071  * Returns final address suitable for core_read/write
1072  */
1073 
1074 //
1075 // Phase 1:
1076 //
1077 //     A: Get the SDW
1078 //
1079 //     B: Check the ring
1080 //
1081 // Phase 2:
1082 //
1083 //     B1: If CALL6 operand
1084 //           goto E
1085 //         If instruction fetch or transfer instruction operand
1086 //           goto F
1087 //         If write
1088 //           check write permission
1089 //         else
1090 //           check read permission
1091 //         goto G
1092 //
1093 //     E: -- CALL6 operand handling
1094 //        Check execute and gate bits
1095 //        Get the ring
1096 //        goto G
1097 //
1098 //     F: -- instruction fetch or transfer instruction operand
1099 //        Check execute bit and ring
1100 //        goto D
1101 //
1102 //     D: Check RALR
1103 //        goto G
1104 //
1105 // Phase 3
1106 //
1107 //     G: Check BOUND
1108 //        If not paged
1109 //          goto H
1110 //        Fetch PTW
1111 //        Fetch prepage PTW
1112 //        Goto I
1113 //
1114 //     H: Compute final address
1115 //        Goto HI
1116 //
1117 //     I: If write
1118 //          set PTW.M
1119 //        Compute final address
1120 //        Goto HI
1121 //
1122 // Phase 4
1123 //
1124 //     HI: --
1125 //         If indirect word fetch
1126 //           goto J
1127 //         if rtcd operand fetch
1128 //           goto K
1129 //         if CALL6
1130 //           goto N
1131 //         If instruction fetch or transfer instruction operand
1132 //           goto L
1133 //         APU data movement?
1134 //           load/store APU data
1135 //
1136 //    K: Set PPR.P
1137 //         Goto J
1138 //
1139 //    J: return
1140 //
1141 
1142 // CANFAULT
1143 
1144 word24 do_append_cycle (processor_cycle_type thisCycle, word36 * data,
     /* [previous][next][first][last][top][bottom][index][help] */
1145                       uint nWords)
1146   {
1147     DCDstruct * i = & cpu.currentInstruction;
1148     DBGAPP ("do_append_cycle(Entry) thisCycle=%s\n",
1149             str_pct (thisCycle));
1150     DBGAPP ("do_append_cycle(Entry) lastCycle=%s\n",
1151             str_pct (cpu.apu.lastCycle));
1152     DBGAPP ("do_append_cycle(Entry) CA %06o\n",
1153             cpu.TPR.CA);
1154     DBGAPP ("do_append_cycle(Entry) n=%2u\n",
1155             nWords);
1156     DBGAPP ("do_append_cycle(Entry) PPR.PRR=%o PPR.PSR=%05o\n",
1157             cpu.PPR.PRR, cpu.PPR.PSR);
1158     DBGAPP ("do_append_cycle(Entry) TPR.TRR=%o TPR.TSR=%05o\n",
1159             cpu.TPR.TRR, cpu.TPR.TSR);
1160 
1161     if (i->b29)
1162       {
1163         DBGAPP ("do_append_cycle(Entry) isb29 PRNO %o\n",
1164                 GET_PRN (IWB_IRODD));
1165       }
1166 
1167     bool StrOp = (thisCycle == OPERAND_STORE ||
1168                   thisCycle == APU_DATA_STORE);
1169 
1170     bool nomatch = true;
1171     if (cpu.tweaks.enable_wam)
1172       {
1173         // AL39: The associative memory is ignored (forced to "no match") during
1174         // address preparation.
1175         // lptp,lptr,lsdp,lsdr,sptp,sptr,ssdp,ssdr
1176         // Unfortunately, ISOLTS doesn't try to execute any of these in append mode.
1177         // XXX should this be only for OPERAND_READ and OPERAND_STORE?
1178         nomatch = ((i->opcode == 0232 || i->opcode == 0254  ||
1179                     i->opcode == 0154 || i->opcode == 0173) &&   i->opcodeX) ||
1180                   ((i->opcode == 0557 || i->opcode == 0257) && ! i->opcodeX);
1181       }
1182 
1183     processor_cycle_type lastCycle = cpu.apu.lastCycle;
1184     cpu.apu.lastCycle = thisCycle;
1185 
1186     DBGAPP ("do_append_cycle(Entry) XSF %o\n", cpu.cu.XSF);
1187 
1188     PNL (L68_ (cpu.apu.state = 0;))
1189 
1190     cpu.RSDWH_R1 = 0;
1191 
1192     cpu.acvFaults = 0;
1193 
1194 //#define FMSG(x) x
1195 #define FMSG(x)
1196     FMSG (char * acvFaultsMsg = "<unknown>";)
1197 
1198     word24 finalAddress = (word24) -1;  // not everything requires a final
1199                                         // address
1200 
1201 ////////////////////////////////////////
1202 //
1203 // Sheet 1: "START APPEND"
1204 //
1205 ////////////////////////////////////////
1206 
1207 // START APPEND
1208     word3 n = 0; // PRn to be saved to TSN_PRNO
1209 
1210     // If the rtcd instruction is executed with the processor in absolute
1211     // mode with bit 29 of the instruction word set OFF and without
1212     // indirection through an ITP or ITS pair, then:
1213     //
1214     //   appending mode is entered for address preparation for the
1215     //   rtcd operand and is retained if the instruction executes
1216     //   successfully, and the effective segment number generated for
1217     //   the SDW fetch and subsequent loading into C(TPR.TSR) is equal
1218     //   to C(PPR.PSR) and may be undefined in absolute mode, and the
1219     //   effective ring number loaded into C(TPR.TRR) prior to the SDW
1220     //   fetch is equal to C(PPR.PRR) (which is 0 in absolute mode)
1221     //   implying that control is always transferred into ring 0.
1222     //
1223     if (thisCycle == RTCD_OPERAND_FETCH &&
1224         get_addr_mode() == ABSOLUTE_mode &&
1225         ! (cpu.cu.XSF || cpu.currentInstruction.b29) /*get_went_appending()*/)
1226       {
1227         cpu.TPR.TSR = 0;
1228         DBGAPP ("RTCD_OPERAND_FETCH ABSOLUTE mode set TSR %05o TRR %o\n",
1229                 cpu.TPR.TSR, cpu.TPR.TRR);
1230       }
1231 
1232     goto A;
1233 
1234 ////////////////////////////////////////
1235 //
1236 // Sheet 2: "A"
1237 //
1238 ////////////////////////////////////////
1239 
1240 //
1241 //  A:
1242 //    Get SDW
1243 
1244 A:;
1245 
1246     //PNL (cpu.APUMemAddr = address;)
1247     PNL (cpu.APUMemAddr = cpu.TPR.CA;)
1248 
1249     DBGAPP ("do_append_cycle(A)\n");
1250 
1251     // is SDW for C(TPR.TSR) in SDWAM?
1252     if (nomatch || ! fetch_sdw_from_sdwam (cpu.TPR.TSR))
1253       {
1254         // No
1255         DBGAPP ("do_append_cycle(A):SDW for segment %05o not in SDWAM\n",
1256                  cpu.TPR.TSR);
1257 
1258         DBGAPP ("do_append_cycle(A):DSBR.U=%o\n",
1259                 cpu.DSBR.U);
1260 
1261         if (cpu.DSBR.U == 0)
1262           {
1263             fetch_dsptw (cpu.TPR.TSR);
1264 
1265             if (! cpu.PTW0.DF)
1266               doFault (FAULT_DF0 + cpu.PTW0.FC, fst_zero,
1267                        "do_append_cycle(A): PTW0.F == 0");
1268 
1269             if (! cpu.PTW0.U)
1270               modify_dsptw (cpu.TPR.TSR);
1271 
1272             fetch_psdw (cpu.TPR.TSR);
1273           }
1274         else
1275           fetch_nsdw (cpu.TPR.TSR); // load SDW0 from descriptor segment table.
1276 
1277         if (cpu.SDW0.DF == 0)
1278           {
1279             if (thisCycle != ABSA_CYCLE)
1280               {
1281                 DBGAPP ("do_append_cycle(A): SDW0.F == 0! "
1282                         "Initiating directed fault\n");
1283                 // initiate a directed fault ...
1284                 doFault (FAULT_DF0 + cpu.SDW0.FC, fst_zero, "SDW0.F == 0");
1285               }
1286           }
1287         // load SDWAM .....
1288         load_sdwam (cpu.TPR.TSR, nomatch);
1289       }
1290     DBGAPP ("do_append_cycle(A) R1 %o R2 %o R3 %o E %o\n",
1291             cpu.SDW->R1, cpu.SDW->R2, cpu.SDW->R3, cpu.SDW->E);
1292 
1293     // Yes...
1294     cpu.RSDWH_R1 = cpu.SDW->R1;
1295 
1296 ////////////////////////////////////////
1297 //
1298 // Sheet 3: "B"
1299 //
1300 ////////////////////////////////////////
1301 
1302 //
1303 // B: Check the ring
1304 //
1305 
1306     DBGAPP ("do_append_cycle(B)\n");
1307 
1308     // check ring bracket consistency
1309 
1310     //C(SDW.R1) <= C(SDW.R2) <= C(SDW .R3)?
1311     if (! (cpu.SDW->R1 <= cpu.SDW->R2 && cpu.SDW->R2 <= cpu.SDW->R3))
1312       {
1313         // Set fault ACV0 = IRO
1314         cpu.acvFaults |= ACV0;
1315         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1316         FMSG (acvFaultsMsg = "acvFaults(B) C(SDW.R1) <= C(SDW.R2) <= "
1317                               "C(SDW .R3)";)
1318       }
1319 
1320     // lastCycle == RTCD_OPERAND_FETCH
1321     // if a fault happens between the RTCD_OPERAND_FETCH and the INSTRUCTION_FETCH
1322     // of the next instruction - this happens about 35 time for just booting  and
1323     // shutting down Multics -- a stored lastCycle is useless.
1324     // the opcode is preserved across faults and only replaced as the
1325     // INSTRUCTION_FETCH succeeds.
1326     if (thisCycle == INSTRUCTION_FETCH &&
1327         i->opcode == 0610  && ! i->opcodeX)
1328       goto C;
1329     else if (lastCycle == RTCD_OPERAND_FETCH)
1330       sim_warn ("%s: lastCycle == RTCD_OPERAND_FETCH opcode %0#o\n", __func__, i->opcode);
1331 
1332     //
1333     // B1: The operand is one of: an instruction, data to be read or data to be
1334     //     written
1335     //
1336 
1337     // Is OPCODE call6?
1338     if (thisCycle == OPERAND_READ && (i->info->flags & CALL6_INS))
1339       goto E;
1340 
1341 
1342 
1343 
1344 
1345 
1346 
1347 
1348 
1349 
1350 
1351 
1352 
1353 
1354 
1355 
1356 
1357 
1358 
1359 
1360 
1361 
1362     // Transfer or instruction fetch?
1363     if (thisCycle == INSTRUCTION_FETCH ||
1364         (thisCycle == OPERAND_READ && (i->info->flags & TRANSFER_INS)))
1365       goto F;
1366 
1367     //
1368     // check read bracket for read access
1369     //
1370 
1371 #ifdef LOCKLESS
1372     // PVS-Studio reports ...
1373     // V560 A part of conditional expression is always false: thisCycle == OPERAND_RMW.
1374     // V560 A part of conditional expression is always false: thisCycle == APU_DATA_RMW.
1375     if (!StrOp || thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW) // -V560
1376 #else
1377     if (!StrOp)
1378 #endif
1379       {
1380         DBGAPP ("do_append_cycle(B):!STR-OP\n");
1381 
1382         // No
1383         // C(TPR.TRR) > C(SDW .R2)?
1384         if (cpu.TPR.TRR > cpu.SDW->R2)
1385           {
1386             DBGAPP ("ACV3\n");
1387             DBGAPP ("do_append_cycle(B) ACV3\n");
1388             //Set fault ACV3 = ORB
1389             cpu.acvFaults |= ACV3;
1390             PNL (L68_ (cpu.apu.state |= apu_FLT;))
1391             FMSG (acvFaultsMsg = "acvFaults(B) C(TPR.TRR) > C(SDW .R2)";)
1392           }
1393 
1394         if (cpu.SDW->R == 0)
1395           {
1396             // isolts 870
1397             cpu.TPR.TRR = cpu.PPR.PRR;
1398 
1399             //C(PPR.PSR) = C(TPR.TSR)?
1400             if (cpu.PPR.PSR != cpu.TPR.TSR)
1401               {
1402                 DBGAPP ("ACV4\n");
1403                 DBGAPP ("do_append_cycle(B) ACV4\n");
1404                 //Set fault ACV4 = R-OFF
1405                 cpu.acvFaults |= ACV4;
1406                 PNL (L68_ (cpu.apu.state |= apu_FLT;))
1407                 FMSG (acvFaultsMsg = "acvFaults(B) C(PPR.PSR) = C(TPR.TSR)";)
1408               }
1409 
1410 
1411 
1412 
1413 
1414 
1415           }
1416       }
1417 
1418     //
1419     // check write bracket for write access
1420     //
1421 #ifdef LOCKLESS
1422     if (StrOp || thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW)
1423 #else
1424     if (StrOp)
1425 #endif
1426       {
1427         DBGAPP ("do_append_cycle(B):STR-OP\n");
1428 
1429         // isolts 870
1430         if (cpu.TPR.TSR == cpu.PPR.PSR)
1431             cpu.TPR.TRR = cpu.PPR.PRR;
1432 
1433         // C(TPR.TRR) > C(SDW .R1)? Note typo in AL39, R2 should be R1
1434         if (cpu.TPR.TRR > cpu.SDW->R1)
1435           {
1436             DBGAPP ("ACV5 TRR %o R1 %o\n",
1437                     cpu.TPR.TRR, cpu.SDW->R1);
1438             //Set fault ACV5 = OWB
1439             cpu.acvFaults |= ACV5;
1440             PNL (L68_ (cpu.apu.state |= apu_FLT;))
1441             FMSG (acvFaultsMsg = "acvFaults(B) C(TPR.TRR) > C(SDW .R1)";)
1442           }
1443 
1444         if (! cpu.SDW->W)
1445           {
1446             // isolts 870
1447             cpu.TPR.TRR = cpu.PPR.PRR;
1448 
1449             DBGAPP ("ACV6\n");
1450             // Set fault ACV6 = W-OFF
1451             cpu.acvFaults |= ACV6;
1452             PNL (L68_ (cpu.apu.state |= apu_FLT;))
1453             FMSG (acvFaultsMsg = "acvFaults(B) ACV6 = W-OFF";)
1454           }
1455 
1456       }
1457     goto G;
1458 
1459 ////////////////////////////////////////
1460 //
1461 // Sheet 4: "C" "D"
1462 //
1463 ////////////////////////////////////////
1464 
1465 C:;
1466     DBGAPP ("do_append_cycle(C)\n");
1467 
1468     //
1469     // check ring bracket for instruction fetch after rtcd instruction
1470     //
1471     //   allow outbound transfers (cpu.TPR.TRR >= cpu.PPR.PRR)
1472     //
1473 
1474     // C(TPR.TRR) < C(SDW.R1)?
1475     // C(TPR.TRR) > C(SDW.R2)?
1476     if (cpu.TPR.TRR < cpu.SDW->R1 ||
1477         cpu.TPR.TRR > cpu.SDW->R2)
1478       {
1479         DBGAPP ("ACV1 c\n");
1480         DBGAPP ("acvFaults(C) ACV1 ! ( C(SDW .R1) %o <= C(TPR.TRR) %o <= C(SDW .R2) %o )\n",
1481                 cpu.SDW->R1, cpu.TPR.TRR, cpu.SDW->R2);
1482         //Set fault ACV1 = OEB
1483         cpu.acvFaults |= ACV1;
1484         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1485         FMSG (acvFaultsMsg = "acvFaults(C) C(SDW.R1 > C(TPR.TRR) > C(SDW.R2)";)
1486       }
1487     // SDW.E set ON?
1488     if (! cpu.SDW->E)
1489       {
1490         DBGAPP ("ACV2 a\n");
1491         DBGAPP ("do_append_cycle(C) ACV2\n");
1492         //Set fault ACV2 = E-OFF
1493         cpu.acvFaults |= ACV2;
1494         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1495         FMSG (acvFaultsMsg = "acvFaults(C) SDW.E";)
1496       }
1497     if (cpu.TPR.TRR > cpu.PPR.PRR)
1498       sim_warn ("rtcd: outbound call cpu.TPR.TRR %d cpu.PPR.PRR %d\n",
1499                 cpu.TPR.TRR, cpu.PPR.PRR);
1500     // C(TPR.TRR) >= C(PPR.PRR)
1501     if (cpu.TPR.TRR < cpu.PPR.PRR)
1502       {
1503         DBGAPP ("ACV11\n");
1504         DBGAPP ("do_append_cycle(C) ACV11\n");
1505         //Set fault ACV11 = INRET
1506         cpu.acvFaults |= ACV11;
1507         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1508         FMSG (acvFaultsMsg = "acvFaults(C) TRR>=PRR";)
1509       }
1510 
1511 D:;
1512     DBGAPP ("do_append_cycle(D)\n");
1513 
1514     // transfer or instruction fetch
1515 
1516     // check ring alarm to catch outbound transfers
1517 
1518     if (cpu.rRALR == 0)
1519         goto G;
1520 
1521     // C(PPR.PRR) < RALR?
1522     if (! (cpu.PPR.PRR < cpu.rRALR))
1523       {
1524         DBGAPP ("ACV13\n");
1525         DBGAPP ("acvFaults(D) C(PPR.PRR) %o < RALR %o\n",
1526                 cpu.PPR.PRR, cpu.rRALR);
1527         cpu.acvFaults |= ACV13;
1528         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1529         FMSG (acvFaultsMsg = "acvFaults(D) C(PPR.PRR) < RALR";)
1530       }
1531 
1532     goto G;
1533 
1534 ////////////////////////////////////////
1535 //
1536 // Sheet 5: "E"
1537 //
1538 ////////////////////////////////////////
1539 
1540 E:;
1541 
1542     //
1543     // check ring bracket for instruction fetch after call6 instruction
1544     //   (this is the call6 read operand)
1545     //
1546 
1547     DBGAPP ("do_append_cycle(E): CALL6\n");
1548     DBGAPP ("do_append_cycle(E): E %o G %o PSR %05o TSR %05o CA %06o "
1549             "EB %06o R %o%o%o TRR %o PRR %o\n",
1550             cpu.SDW->E, cpu.SDW->G, cpu.PPR.PSR, cpu.TPR.TSR, cpu.TPR.CA,
1551             cpu.SDW->EB, cpu.SDW->R1, cpu.SDW->R2, cpu.SDW->R3,
1552             cpu.TPR.TRR, cpu.PPR.PRR);
1553 
1554     //SDW.E set ON?
1555     if (! cpu.SDW->E)
1556       {
1557         DBGAPP ("ACV2 b\n");
1558         DBGAPP ("do_append_cycle(E) ACV2\n");
1559         // Set fault ACV2 = E-OFF
1560         cpu.acvFaults |= ACV2;
1561         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1562         FMSG (acvFaultsMsg = "acvFaults(E) SDW .E set OFF";)
1563       }
1564 
1565     //SDW .G set ON?
1566     if (cpu.SDW->G)
1567       goto E1;
1568 
1569     // C(PPR.PSR) = C(TPR.TSR)?
1570     if (cpu.PPR.PSR == cpu.TPR.TSR && ! TST_I_ABS)
1571       goto E1;
1572 
1573     // XXX This doesn't seem right
1574     // EB is word 15; masking address makes no sense; rather 0-extend EB
1575     // Fixes ISOLTS 880-01
1576     if (cpu.TPR.CA >= (word18) cpu.SDW->EB)
1577       {
1578         DBGAPP ("ACV7\n");
1579         DBGAPP ("do_append_cycle(E) ACV7\n");
1580         // Set fault ACV7 = NO GA
1581         cpu.acvFaults |= ACV7;
1582         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1583         FMSG (acvFaultsMsg = "acvFaults(E) TPR.CA4-17 >= SDW.CL";)
1584       }
1585 
1586 E1:
1587     DBGAPP ("do_append_cycle(E1): CALL6 (cont'd)\n");
1588 
1589     // C(TPR.TRR) > SDW.R3?
1590     if (cpu.TPR.TRR > cpu.SDW->R3)
1591       {
1592         DBGAPP ("ACV8\n");
1593         DBGAPP ("do_append_cycle(E) ACV8\n");
1594         //Set fault ACV8 = OCB
1595         cpu.acvFaults |= ACV8;
1596         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1597         FMSG (acvFaultsMsg = "acvFaults(E1) C(TPR.TRR) > SDW.R3";)
1598       }
1599 
1600     // C(TPR.TRR) < SDW.R1?
1601     if (cpu.TPR.TRR < cpu.SDW->R1)
1602       {
1603         DBGAPP ("ACV9\n");
1604         DBGAPP ("do_append_cycle(E) ACV9\n");
1605         // Set fault ACV9 = OCALL
1606         cpu.acvFaults |= ACV9;
1607         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1608         FMSG (acvFaultsMsg = "acvFaults(E1) C(TPR.TRR) < SDW.R1";)
1609       }
1610 
1611     // C(TPR.TRR) > C(PPR.PRR)?
1612     if (cpu.TPR.TRR > cpu.PPR.PRR)
1613       {
1614         // C(PPR.PRR) < SDW.R2?
1615         if (cpu.PPR.PRR < cpu.SDW->R2)
1616           {
1617             DBGAPP ("ACV10\n");
1618             DBGAPP ("do_append_cycle(E) ACV10\n");
1619             // Set fault ACV10 = BOC
1620             cpu.acvFaults |= ACV10;
1621             PNL (L68_ (cpu.apu.state |= apu_FLT;))
1622             FMSG (acvFaultsMsg = "acvFaults(E1) C(TPR.TRR) > C(PPR.PRR) && "
1623                   "C(PPR.PRR) < SDW.R2";)
1624           }
1625       }
1626 
1627     DBGAPP ("do_append_cycle(E1): CALL6 TPR.TRR %o SDW->R2 %o\n",
1628             cpu.TPR.TRR, cpu.SDW->R2);
1629 
1630     // C(TPR.TRR) > SDW.R2?
1631     if (cpu.TPR.TRR > cpu.SDW->R2)
1632       {
1633         // SDW.R2 -> C(TPR.TRR)
1634         cpu.TPR.TRR = cpu.SDW->R2;
1635       }
1636 
1637     DBGAPP ("do_append_cycle(E1): CALL6 TPR.TRR %o\n", cpu.TPR.TRR);
1638 
1639     goto G;
1640 
1641 ////////////////////////////////////////
1642 //
1643 // Sheet 6: "F"
1644 //
1645 ////////////////////////////////////////
1646 
1647 F:;
1648     PNL (L68_ (cpu.apu.state |= apu_PIAU;))
1649     DBGAPP ("do_append_cycle(F): transfer or instruction fetch\n");
1650 
1651     //
1652     // check ring bracket for instruction fetch
1653     //
1654 
1655     // C(TPR.TRR) < C(SDW .R1)?
1656     // C(TPR.TRR) > C(SDW .R2)?
1657     if (cpu.TPR.TRR < cpu.SDW->R1 ||
1658         cpu.TPR.TRR > cpu.SDW->R2)
1659       {
1660         DBGAPP ("ACV1 a/b\n");
1661         DBGAPP ("acvFaults(F) ACV1 !( C(SDW .R1) %o <= C(TPR.TRR) %o <= C(SDW .R2) %o )\n",
1662                 cpu.SDW->R1, cpu.TPR.TRR, cpu.SDW->R2);
1663         cpu.acvFaults |= ACV1;
1664         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1665         FMSG (acvFaultsMsg = "acvFaults(F) C(TPR.TRR) < C(SDW .R1)";)
1666       }
1667     // SDW .E set ON?
1668     if (! cpu.SDW->E)
1669       {
1670         DBGAPP ("ACV2 c \n");
1671         DBGAPP ("do_append_cycle(F) ACV2\n");
1672         cpu.acvFaults |= ACV2;
1673         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1674         FMSG (acvFaultsMsg = "acvFaults(F) SDW .E set OFF";)
1675       }
1676 
1677     // C(PPR.PRR) = C(TPR.TRR)?
1678     if (cpu.PPR.PRR != cpu.TPR.TRR)
1679       {
1680         DBGAPP ("ACV12\n");
1681         DBGAPP ("do_append_cycle(F) ACV12\n");
1682         //Set fault ACV12 = CRT
1683         cpu.acvFaults |= ACV12;
1684         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1685         FMSG (acvFaultsMsg = "acvFaults(F) C(PPR.PRR) != C(TPR.TRR)";)
1686       }
1687 
1688     goto D;
1689 
1690 ////////////////////////////////////////
1691 //
1692 // Sheet 7: "G"
1693 //
1694 ////////////////////////////////////////
1695 
1696 G:;
1697 
1698     DBGAPP ("do_append_cycle(G)\n");
1699 
1700     //C(TPR.CA)0,13 > SDW.BOUND?
1701     if (((cpu.TPR.CA >> 4) & 037777) > cpu.SDW->BOUND)
1702       {
1703         DBGAPP ("ACV15\n");
1704         DBGAPP ("do_append_cycle(G) ACV15\n");
1705         cpu.acvFaults |= ACV15;
1706         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1707         FMSG (acvFaultsMsg = "acvFaults(G) C(TPR.CA)0,13 > SDW.BOUND";)
1708         DBGAPP ("acvFaults(G) C(TPR.CA)0,13 > SDW.BOUND\n"
1709                 "   CA %06o CA>>4 & 037777 %06o SDW->BOUND %06o",
1710                 cpu.TPR.CA, ((cpu.TPR.CA >> 4) & 037777), cpu.SDW->BOUND);
1711       }
1712 
1713     if (cpu.acvFaults)
1714       {
1715         DBGAPP ("do_append_cycle(G) acvFaults\n");
1716         PNL (L68_ (cpu.apu.state |= apu_FLT;))
1717         // Initiate an access violation fault
1718         doFault (FAULT_ACV, (_fault_subtype) {.fault_acv_subtype=cpu.acvFaults},
1719                  "ACV fault");
1720       }
1721 
1722     // is segment C(TPR.TSR) paged?
1723     if (cpu.SDW->U)
1724       goto H; // Not paged
1725 
1726     // Yes. segment is paged ...
1727     // is PTW for C(TPR.CA) in PTWAM?
1728 
1729     DBGAPP ("do_append_cycle(G) CA %06o\n", cpu.TPR.CA);
1730     if (nomatch ||
1731         ! fetch_ptw_from_ptwam (cpu.SDW->POINTER, cpu.TPR.CA))  //TPR.CA))
1732       {
1733         fetch_ptw (cpu.SDW, cpu.TPR.CA);
1734         if (! cpu.PTW0.DF)
1735           {
1736             if (thisCycle != ABSA_CYCLE)
1737               {
1738                 // initiate a directed fault
1739                 doFault (FAULT_DF0 + cpu.PTW0.FC, (_fault_subtype) {.bits=0},
1740                          "PTW0.F == 0");
1741               }
1742           }
1743         loadPTWAM (cpu.SDW->POINTER, cpu.TPR.CA, nomatch); // load PTW0 to PTWAM
1744       }
1745 
1746     // Prepage mode?
1747     // check for "uninterruptible" EIS instruction
1748     // ISOLTS-878 02: mvn,cmpn,mvne,ad3d; obviously also
1749     // ad2/3d,sb2/3d,mp2/3d,dv2/3d
1750     // DH03 p.8-13: probably also mve,btd,dtb
1751     if (i->opcodeX && ((i->opcode & 0770)== 0200|| (i->opcode & 0770) == 0220
1752         || (i->opcode & 0770)== 020|| (i->opcode & 0770) == 0300))
1753       {
1754         do_ptw2 (cpu.SDW, cpu.TPR.CA);
1755       }
1756     goto I;
1757 
1758 ////////////////////////////////////////
1759 //
1760 // Sheet 8: "H", "I"
1761 //
1762 ////////////////////////////////////////
1763 
1764 H:;
1765     DBGAPP ("do_append_cycle(H): FANP\n");
1766 
1767     PNL (L68_ (cpu.apu.state |= apu_FANP;))
1768 
1769 
1770 
1771 
1772 
1773 
1774 
1775 
1776 
1777     set_apu_status (apuStatus_FANP);
1778 
1779     DBGAPP ("do_append_cycle(H): SDW->ADDR=%08o CA=%06o \n",
1780             cpu.SDW->ADDR, cpu.TPR.CA);
1781 
1782     if (thisCycle == RTCD_OPERAND_FETCH &&
1783         get_addr_mode () == ABSOLUTE_mode &&
1784         ! (cpu.cu.XSF || cpu.currentInstruction.b29) /*get_went_appending ()*/)
1785       {
1786         finalAddress = cpu.TPR.CA;
1787       }
1788     else
1789       {
1790         finalAddress = (cpu.SDW->ADDR & 077777760) + cpu.TPR.CA;
1791         finalAddress &= 0xffffff;
1792       }
1793     PNL (cpu.APUMemAddr = finalAddress;)
1794 
1795     DBGAPP ("do_append_cycle(H:FANP): (%05o:%06o) finalAddress=%08o\n",
1796             cpu.TPR.TSR, cpu.TPR.CA, finalAddress);
1797 
1798     //if (thisCycle == ABSA_CYCLE)
1799     //    goto J;
1800     goto HI;
1801 
1802 I:;
1803 
1804 // Set PTW.M
1805 
1806     DBGAPP ("do_append_cycle(I): FAP\n");
1807 #ifdef LOCKLESS
1808     if ((StrOp ||
1809         thisCycle == OPERAND_RMW ||
1810         thisCycle == APU_DATA_RMW) && cpu.PTW->M == 0)  // is this the right way to do this?
1811 #else
1812     if (StrOp && cpu.PTW->M == 0)  // is this the right way to do this?
1813 #endif
1814       {
1815        modify_ptw (cpu.SDW, cpu.TPR.CA);
1816       }
1817 
1818     // final address paged
1819     set_apu_status (apuStatus_FAP);
1820     PNL (L68_ (cpu.apu.state |= apu_FAP;))
1821 
1822     word24 y2 = cpu.TPR.CA % 1024;
1823 
1824     // AL39: The hardware ignores low order bits of the main memory page
1825     // address according to page size
1826     finalAddress = (((word24)cpu.PTW->ADDR & 0777760) << 6) + y2;
1827     finalAddress &= 0xffffff;
1828     PNL (cpu.APUMemAddr = finalAddress;)
1829 
1830     L68_ (if (cpu.MR_cache.emr && cpu.MR_cache.ihr)
1831       add_l68_APU_history (APUH_FAP);)
1832     DBGAPP ("do_append_cycle(H:FAP): (%05o:%06o) finalAddress=%08o\n",
1833             cpu.TPR.TSR, cpu.TPR.CA, finalAddress);
1834 
1835     //if (thisCycle == ABSA_CYCLE)
1836     //    goto J;
1837     goto HI;
1838 
1839 HI:
1840     DBGAPP ("do_append_cycle(HI)\n");
1841 
1842     // isolts 870
1843     if (thisCycle != ABSA_CYCLE)
1844       {
1845         cpu.cu.XSF = 1;
1846         sim_debug (DBG_TRACEEXT, & cpu_dev, "loading of cpu.TPR.TSR sets XSF to 1\n");
1847       }
1848 
1849     if (thisCycle == OPERAND_STORE && cpu.useZone)
1850       {
1851         core_write_zone (finalAddress, * data, str_pct (thisCycle));
1852       }
1853     else if (StrOp)
1854       {
1855         core_writeN (finalAddress, data, nWords, str_pct (thisCycle));
1856       }
1857     else
1858       {
1859 #ifdef LOCKLESS
1860         if ((thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW) && nWords == 1)
1861           {
1862             core_read_lock (finalAddress, data, str_pct (thisCycle));
1863           }
1864         else
1865           {
1866             if (thisCycle == OPERAND_RMW || thisCycle == APU_DATA_RMW)
1867               sim_warn("do_append_cycle: RMW nWords %d !=1\n", nWords);
1868             core_readN (finalAddress, data, nWords, str_pct (thisCycle));
1869           }
1870 #else
1871         if (thisCycle != ABSA_CYCLE)
1872           core_readN (finalAddress, data, nWords, str_pct (thisCycle));
1873         //else sim_printf ("############### bogus absa read\r\n");
1874 #endif
1875       }
1876 
1877     // Was this an indirect word fetch?
1878     if (thisCycle == INDIRECT_WORD_FETCH)
1879       goto J;
1880 
1881     // Was this an rtcd operand fetch?
1882     if (thisCycle == RTCD_OPERAND_FETCH)
1883       goto K;
1884 
1885     // is OPCODE call6?
1886     if (thisCycle == OPERAND_READ && (i->info->flags & CALL6_INS))
1887       goto N;
1888 
1889     // Transfer or instruction fetch?
1890     if (thisCycle == INSTRUCTION_FETCH ||
1891         (thisCycle == OPERAND_READ && (i->info->flags & TRANSFER_INS)))
1892       goto L;
1893 
1894     // APU data movement?
1895     //  handled above
1896    goto Exit;
1897 
1898 ////////////////////////////////////////
1899 //
1900 // Sheet 9: "J"
1901 //
1902 ////////////////////////////////////////
1903 
1904 // Indirect operand fetch
1905 
1906 J:;
1907     DBGAPP ("do_append_cycle(J)\n");
1908 
1909     // ri or ir & TPC.CA even?
1910     word6 tag = GET_TAG (IWB_IRODD);
1911     if ((GET_TM (tag) == TM_IR || GET_TM (tag) == TM_RI) &&
1912         (cpu.TPR.CA & 1) == 0)
1913       {
1914         if (ISITS (* data))
1915           goto O;
1916         if (ISITP (* data))
1917           goto P;
1918       }
1919 
1920     // C(Y) tag == other indirect?
1921     //   TM_R never indirects
1922     //   TM_RI always indirects
1923     //   TM_IR always indirects
1924     //   TM_IT always indirects
1925 
1926 
1927 
1928 
1929 
1930 
1931 
1932 
1933 
1934 
1935 
1936 
1937 
1938 
1939 
1940 
1941 
1942 
1943 
1944 
1945 
1946 
1947 
1948 
1949 
1950 
1951 
1952 
1953     if ((* data) & 060)
1954 
1955       {
1956         // C(Y)0,17 -> C(IWB)0,17
1957         // C(Y)30,35 -> C(IWB)30,35
1958         // 0 -> C(IWB)29
1959         //updateIWB (GET_ADDR (* data), (* data) & MASK6);
1960         //cpu.cu.TSN_PRNO[0] = n;
1961         //cpu.cu.TSN_VALID[0] = 1;
1962 
1963       }
1964 
1965      goto Exit;
1966 
1967 ////////////////////////////////////////
1968 //
1969 // Sheet 10: "K", "L", "M", "N"
1970 //
1971 ////////////////////////////////////////
1972 
1973 K:; // RTCD operand fetch
1974     DBGAPP ("do_append_cycle(K)\n");
1975 
1976     word3 y = GET_ITS_RN (data);
1977 
1978     // C(Y-pair)3,17 -> C(PPR.PSR)
1979     // We set TSR here; TSR will be copied to PSR at KL
1980     cpu.TPR.TSR = GET_ITS_SEGNO (data);
1981 
1982     // Maximum of
1983     // C(Y-pair)18,20; C(TPR.TRR); C(SDW.R1) -> C(PPR.PRR)
1984     // We set TRR here as well
1985     cpu.PPR.PRR = cpu.TPR.TRR = max3 (y, cpu.TPR.TRR, cpu.RSDWH_R1);
1986 
1987     // C(Y-pair)36,53 -> C(PPR.IC)
1988     // We set CA here; copied to IC  at KL
1989     cpu.TPR.CA = GET_ITS_WORDNO (data);
1990 
1991     // If C(PPR.PRR) = 0 then C(SDW.P) -> C(PPR.P);
1992     //     otherwise 0 -> C(PPR.P)
1993     // Done at M
1994 
1995     goto KL;
1996 
1997 L:; // Transfer or instruction fetch
1998 
1999     DBGAPP ("do_append_cycle(L)\n");
2000 
2001     // Is OPCODE tspn?
2002     if (thisCycle == OPERAND_READ && (i->info->flags & TSPN_INS))
2003       {
2004         //word3 n;
2005         if (i->opcode <= 0273)
2006           n = (i->opcode & 3);
2007         else
2008           n = (i->opcode & 3) + 4;
2009 
2010         // C(PPR.PRR) -> C(PRn .RNR)
2011         // C(PPR.PSR) -> C(PRn .SNR)
2012         // C(PPR.IC)  -> C(PRn .WORDNO)
2013         // 000000     -> C(PRn .BITNO)
2014         cpu.PR[n].RNR = cpu.PPR.PRR;
2015 // According the AL39, the PSR is 'undefined' in absolute mode.
2016 // ISOLTS thinks means don't change the operand
2017         if (get_addr_mode () == APPEND_mode)
2018           cpu.PR[n].SNR = cpu.PPR.PSR;
2019         cpu.PR[n].WORDNO = (cpu.PPR.IC + 1) & MASK18;
2020         SET_PR_BITNO (n, 0);
2021 #ifdef TESTING
2022         HDBGRegPRW (n, "app tspn");
2023 #endif
2024       }
2025 
2026     // lastCycle == RTCD_OPERAND_FETCH
2027 
2028     if (thisCycle == INSTRUCTION_FETCH &&
2029         i->opcode == 0610  && ! i->opcodeX)
2030       {
2031         // C(PPR.PRR) -> C(PRn.RNR) for n = (0, 1, ..., 7)
2032         // Use TRR here; PRR not set until KL
2033         CPTUR (cptUsePRn + 0);
2034         CPTUR (cptUsePRn + 1);
2035         CPTUR (cptUsePRn + 2);
2036         CPTUR (cptUsePRn + 3);
2037         CPTUR (cptUsePRn + 4);
2038         CPTUR (cptUsePRn + 5);
2039         CPTUR (cptUsePRn + 6);
2040         CPTUR (cptUsePRn + 7);
2041         cpu.PR[0].RNR =
2042         cpu.PR[1].RNR =
2043         cpu.PR[2].RNR =
2044         cpu.PR[3].RNR =
2045         cpu.PR[4].RNR =
2046         cpu.PR[5].RNR =
2047         cpu.PR[6].RNR =
2048         cpu.PR[7].RNR = cpu.TPR.TRR;
2049 #ifdef TESTING
2050         HDBGRegPRW (0, "app rtcd");
2051         HDBGRegPRW (1, "app rtcd");
2052         HDBGRegPRW (2, "app rtcd");
2053         HDBGRegPRW (3, "app rtcd");
2054         HDBGRegPRW (4, "app rtcd");
2055         HDBGRegPRW (5, "app rtcd");
2056         HDBGRegPRW (6, "app rtcd");
2057         HDBGRegPRW (7, "app rtcd");
2058 #endif
2059       }
2060     goto KL;
2061 
2062 KL:
2063     DBGAPP ("do_append_cycle(KL)\n");
2064 
2065     // C(TPR.TSR) -> C(PPR.PSR)
2066     cpu.PPR.PSR   = cpu.TPR.TSR;
2067     // C(TPR.CA)  -> C(PPR.IC)
2068     cpu.PPR.IC    = cpu.TPR.CA;
2069 
2070     goto M;
2071 
2072 M: // Set P
2073     DBGAPP ("do_append_cycle(M)\n");
2074 
2075     // C(TPR.TRR) = 0?
2076     if (cpu.TPR.TRR == 0)
2077       {
2078         // C(SDW.P) -> C(PPR.P)
2079         cpu.PPR.P = cpu.SDW->P;
2080       }
2081     else
2082       {
2083         // 0 C(PPR.P)
2084         cpu.PPR.P = 0;
2085       }
2086 
2087     goto Exit;
2088 
2089 N: // CALL6
2090     DBGAPP ("do_append_cycle(N)\n");
2091 
2092     // C(TPR.TRR) = C(PPR.PRR)?
2093     if (cpu.TPR.TRR == cpu.PPR.PRR)
2094       {
2095         // C(PR6.SNR) -> C(PR7.SNR)
2096         cpu.PR[7].SNR = cpu.PR[6].SNR;
2097         DBGAPP ("do_append_cycle(N) PR7.SNR = PR6.SNR %05o\n", cpu.PR[7].SNR);
2098       }
2099     else
2100       {
2101         // C(DSBR.STACK) || C(TPR.TRR) -> C(PR7.SNR)
2102         cpu.PR[7].SNR = ((word15) (cpu.DSBR.STACK << 3)) | cpu.TPR.TRR;
2103         DBGAPP ("do_append_cycle(N) STACK %05o TRR %o\n",
2104                 cpu.DSBR.STACK, cpu.TPR.TRR);
2105         DBGAPP ("do_append_cycle(N) PR7.SNR = STACK||TRR  %05o\n", cpu.PR[7].SNR);
2106       }
2107 
2108     // C(TPR.TRR) -> C(PR7.RNR)
2109     cpu.PR[7].RNR = cpu.TPR.TRR;
2110     // 00...0 -> C(PR7.WORDNO)
2111     cpu.PR[7].WORDNO = 0;
2112     // 000000 -> C(PR7.BITNO)
2113     SET_PR_BITNO (7, 0);
2114 #ifdef TESTING
2115     HDBGRegPRW (7, "app call6");
2116 #endif
2117     // C(TPR.TRR) -> C(PPR.PRR)
2118     cpu.PPR.PRR   = cpu.TPR.TRR;
2119     // C(TPR.TSR) -> C(PPR.PSR)
2120     cpu.PPR.PSR   = cpu.TPR.TSR;
2121     // C(TPR.CA)  -> C(PPR.IC)
2122     cpu.PPR.IC    = cpu.TPR.CA;
2123 
2124     goto M;
2125 
2126 ////////////////////////////////////////
2127 //
2128 // Sheet 11: "O", "P"
2129 //
2130 ////////////////////////////////////////
2131 
2132 O:; // ITS, RTCD
2133     DBGAPP ("do_append_cycle(O)\n");
2134     word3 its_RNR = GET_ITS_RN (data);
2135     DBGAPP ("do_append_cycle(O) TRR %o RSDWH.R1 %o ITS.RNR %o\n",
2136             cpu.TPR.TRR, cpu.RSDWH_R1, its_RNR);
2137 
2138     // Maximum of
2139     //  C(Y)18,20;  C(TPR.TRR); C(SDW.R1) -> C(TPR.TRR)
2140     cpu.TPR.TRR = max3 (its_RNR, cpu.TPR.TRR, cpu.RSDWH_R1);
2141     DBGAPP ("do_append_cycle(O) Set TRR to %o\n", cpu.TPR.TRR);
2142 
2143     goto Exit;
2144 
2145 P:; // ITP
2146 
2147     DBGAPP ("do_append_cycle(P)\n");
2148 
2149     n = GET_ITP_PRNUM (data);
2150     DBGAPP ("do_append_cycle(P) TRR %o RSDWH.R1 %o PR[n].RNR %o\n",
2151             cpu.TPR.TRR, cpu.RSDWH_R1, cpu.PR[n].RNR);
2152 
2153     // Maximum of
2154     // cpu.PR[n].RNR;  C(TPR.TRR); C(SDW.R1) -> C(TPR.TRR)
2155     cpu.TPR.TRR = max3 (cpu.PR[n].RNR, cpu.TPR.TRR, cpu.RSDWH_R1);
2156     DBGAPP ("do_append_cycle(P) Set TRR to %o\n", cpu.TPR.TRR);
2157 
2158     goto Exit;
2159 
2160 Exit:;
2161 
2162     PNL (cpu.APUDataBusOffset = cpu.TPR.CA;)
2163     PNL (cpu.APUDataBusAddr = finalAddress;)
2164 
2165     PNL (L68_ (cpu.apu.state |= apu_FA;))
2166 
2167     DBGAPP ("do_append_cycle (Exit) PRR %o PSR %05o P %o IC %06o\n",
2168             cpu.PPR.PRR, cpu.PPR.PSR, cpu.PPR.P, cpu.PPR.IC);
2169     DBGAPP ("do_append_cycle (Exit) TRR %o TSR %05o TBR %02o CA %06o\n",
2170             cpu.TPR.TRR, cpu.TPR.TSR, cpu.TPR.TBR, cpu.TPR.CA);
2171 
2172     return finalAddress;    // or 0 or -1???
2173   }
2174 
2175 // Translate a segno:offset to a absolute address.
2176 // Return 0 if successful.
2177 
2178 #ifdef TESTING
2179 int dbgLookupAddress (word18 segno, word18 offset, word24 * finalAddress,
     /* [previous][next][first][last][top][bottom][index][help] */
2180                       char * * msg)
2181   {
2182     // Local copies so we don't disturb machine state
2183 
2184     ptw_s PTW1;
2185     sdw_s SDW1;
2186 
2187    if (2u * segno >= 16u * (cpu.DSBR.BND + 1u))
2188      {
2189        if (msg)
2190          * msg = "DSBR boundary violation.";
2191        return 1;
2192      }
2193 
2194     if (cpu.DSBR.U == 0)
2195       {
2196         // fetch_dsptw
2197 
2198         word24 y1 = (2 * segno) % 1024;
2199         word24 x1 = (2 * segno) / 1024; // floor
2200 
2201         word36 PTWx1;
2202         core_read ((cpu.DSBR.ADDR + x1) & PAMASK, & PTWx1, __func__);
2203 
2204         PTW1.ADDR = GETHI (PTWx1);
2205         PTW1.U = TSTBIT (PTWx1, 9);
2206         PTW1.M = TSTBIT (PTWx1, 6);
2207         PTW1.DF = TSTBIT (PTWx1, 2);
2208         PTW1.FC = PTWx1 & 3;
2209 
2210         if (! PTW1.DF)
2211           {
2212             if (msg)
2213               * msg = "!PTW0.F";
2214             return 2;
2215           }
2216 
2217         // fetch_psdw
2218 
2219         y1 = (2 * segno) % 1024;
2220 
2221         word36 SDWeven, SDWodd;
2222 
2223         core_read2 (((((word24)PTW1. ADDR & 0777760) << 6) + y1) & PAMASK,
2224                     & SDWeven, & SDWodd, __func__);
2225 
2226         // even word
2227         SDW1.ADDR = (SDWeven >> 12) & 077777777;
2228         SDW1.R1   = (SDWeven >> 9)  & 7;
2229         SDW1.R2   = (SDWeven >> 6)  & 7;
2230         SDW1.R3   = (SDWeven >> 3)  & 7;
2231         SDW1.DF   = TSTBIT (SDWeven, 2);
2232         SDW1.FC   = SDWeven & 3;
2233 
2234         // odd word
2235         SDW1.BOUND = (SDWodd >> 21) & 037777;
2236         SDW1.R     = TSTBIT (SDWodd, 20);
2237         SDW1.E     = TSTBIT (SDWodd, 19);
2238         SDW1.W     = TSTBIT (SDWodd, 18);
2239         SDW1.P     = TSTBIT (SDWodd, 17);
2240         SDW1.U     = TSTBIT (SDWodd, 16);
2241         SDW1.G     = TSTBIT (SDWodd, 15);
2242         SDW1.C     = TSTBIT (SDWodd, 14);
2243         SDW1.EB    = SDWodd & 037777;
2244       }
2245     else // ! DSBR.U
2246       {
2247         // fetch_nsdw
2248 
2249         word36 SDWeven, SDWodd;
2250 
2251         core_read2 ((cpu.DSBR.ADDR + 2 * segno) & PAMASK,
2252                     & SDWeven, & SDWodd, __func__);
2253 
2254         // even word
2255         SDW1.ADDR = (SDWeven >> 12) & 077777777;
2256         SDW1.R1   = (SDWeven >> 9)  & 7;
2257         SDW1.R2   = (SDWeven >> 6)  & 7;
2258         SDW1.R3   = (SDWeven >> 3)  & 7;
2259         SDW1.DF   = TSTBIT (SDWeven, 2);
2260         SDW1.FC   = SDWeven & 3;
2261 
2262         // odd word
2263         SDW1.BOUND = (SDWodd >> 21) & 037777;
2264         SDW1.R     = TSTBIT (SDWodd, 20);
2265         SDW1.E     = TSTBIT (SDWodd, 19);
2266         SDW1.W     = TSTBIT (SDWodd, 18);
2267         SDW1.P     = TSTBIT (SDWodd, 17);
2268         SDW1.U     = TSTBIT (SDWodd, 16);
2269         SDW1.G     = TSTBIT (SDWodd, 15);
2270         SDW1.C     = TSTBIT (SDWodd, 14);
2271         SDW1.EB    = SDWodd & 037777;
2272 
2273       }
2274 
2275     if (SDW1.DF == 0)
2276       {
2277         if (msg)
2278           * msg = "!SDW0.F != 0";
2279         return 3;
2280       }
2281 
2282     if (((offset >> 4) & 037777) > SDW1.BOUND)
2283       {
2284         if (msg)
2285           * msg = "C(TPR.CA)0,13 > SDW.BOUND";
2286         return 4;
2287       }
2288 
2289     // is segment C(TPR.TSR) paged?
2290     if (SDW1.U)
2291       {
2292         * finalAddress = (SDW1.ADDR + offset) & PAMASK;
2293       }
2294     else
2295       {
2296         // fetch_ptw
2297         word24 y2 = offset % 1024;
2298         word24 x2 = (offset) / 1024; // floor
2299 
2300         word36 PTWx2;
2301 
2302         core_read ((SDW1.ADDR + x2) & PAMASK, & PTWx2, __func__);
2303 
2304         PTW1.ADDR = GETHI (PTWx2);
2305         PTW1.U    = TSTBIT (PTWx2, 9);
2306         PTW1.M    = TSTBIT (PTWx2, 6);
2307         PTW1.DF   = TSTBIT (PTWx2, 2);
2308         PTW1.FC   = PTWx2 & 3;
2309 
2310         if (! PTW1.DF)
2311           {
2312             if (msg)
2313               * msg = "!PTW0.F";
2314             return 5;
2315           }
2316 
2317         y2 = offset % 1024;
2318 
2319         * finalAddress = ((((word24)PTW1.ADDR & 0777760) << 6) + y2) & PAMASK;
2320       }
2321     if (msg)
2322       * msg = "";
2323     return 0;
2324   }
2325 #endif

/* [previous][next][first][last][top][bottom][index][help] */