PiDP-8/I Software

Check-in [d413ed639f]
Log In

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:merge Steve Tockey's branch cycle-realistic into the pi5-ils2-bookworm branch
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | pi5-ils2-bworm-cyclerealistic
Files: files | file ages | folders
SHA1: d413ed639febfeee72cfa3d5800e9222f33f97c5
User & Date: HBEggenstein 2024-06-15 21:58:20
Context
2024-06-20
00:24
fixed ILS tweaking check-in: b0f1224117 user: HBEggenstein tags: pi5-ils2-bworm-cyclerealistic
00:21
fixed ILS tweaking check-in: 7b450b94dc user: HBEggenstein tags: pi5-ils2-bworm-cyclerealistic
2024-06-15
21:58
merge Steve Tockey's branch cycle-realistic into the pi5-ils2-bookworm branch check-in: d413ed639f user: HBEggenstein tags: pi5-ils2-bworm-cyclerealistic
2024-06-14
23:42
revert pinctrl chaching optim., not worth it. Moved ILS/NLS switching logic from compile time to runtime / configuration file check-in: ccedb853c9 user: HBEggenstein tags: pi5-ils2-bookworm
2021-12-20
17:21
Merged CONTRIBUTING.md changes in Leaf check-in: 6c073bdde3 user: tangent tags: cycle-realistic
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/SIMH/PDP8/pdp8_cpu.c.

21
22
23
24
25
26
27
28



29
30
31
32
33
34
35

   Except as contained in this notice, the name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   ----------------------------------------------------------------------------

   Portions copyright © 2015 by Oscar Vermeulen, © 2016-2018 by Warren Young




   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:







|
>
>
>







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

   Except as contained in this notice, the name of Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   ----------------------------------------------------------------------------

   Portions copyright © 2015 by Oscar Vermeulen
                      © 2016-2018 by Warren Young
                      © 2021 by HB Eggenstein
                      © 2021 by Steve Tockey

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:
89
90
91
92
93
94
95



96
97
98
99
100
101
102

   The register state for the PDP-8 is:

   AC<0:11>             accumulator
   MQ<0:11>             multiplier-quotient
   L                    link flag
   PC<0:11>             program counter



   IF<0:2>              instruction field
   IB<0:2>              instruction buffer
   DF<0:2>              data field
   UF                   user flag
   UB                   user buffer
   SF<0:6>              interrupt save field








>
>
>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

   The register state for the PDP-8 is:

   AC<0:11>             accumulator
   MQ<0:11>             multiplier-quotient
   L                    link flag
   PC<0:11>             program counter
   MA<0:11>             memory address
   MB<0:11>             memory buffer
   Major_State<0:1>     major state register
   IF<0:2>              instruction field
   IB<0:2>              instruction buffer
   DF<0:2>              data field
   UF                   user flag
   UB                   user buffer
   SF<0:6>              interrupt save field

254
255
256
257
258
259
260



261
262
263
264
265
266
267
268
269
    int16               mq;
    } InstHistory;

uint16 M[MAXMEMSIZE] = { 0 };                           /* main memory */
int32 saved_LAC = 0;                                    /* saved L'AC */
int32 saved_MQ = 0;                                     /* saved MQ */
int32 saved_PC = 0;                                     /* saved IF'PC */



int32 saved_DF = 0;                                     /* saved Data Field */
int32 IB = 0;                                           /* Instruction Buffer */
int32 SF = 0;                                           /* Save Field */
int32 emode = 0;                                        /* EAE mode */
int32 gtf = 0;                                          /* EAE gtf flag */
int32 SC = 0;                                           /* EAE shift count */
int32 UB = 0;                                           /* User mode Buffer */
int32 UF = 0;                                           /* User mode Flag */
int32 SR = 0;                                           /* Switch Register */







>
>
>

|







260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
    int16               mq;
    } InstHistory;

uint16 M[MAXMEMSIZE] = { 0 };                           /* main memory */
int32 saved_LAC = 0;                                    /* saved L'AC */
int32 saved_MQ = 0;                                     /* saved MQ */
int32 saved_PC = 0;                                     /* saved IF'PC */
int32 saved_MA = 0;                                     /* saved MA */
int32 saved_IR = 0;                                     /* saved IR */
int16 saved_Major_State = 1;                            /* saved Major State */
int32 saved_DF = 0;                                     /* saved Data Field */
int32 IB = -1;                                          /* Instruction Buffer */
int32 SF = 0;                                           /* Save Field */
int32 emode = 0;                                        /* EAE mode */
int32 gtf = 0;                                          /* EAE gtf flag */
int32 SC = 0;                                           /* EAE shift count */
int32 UB = 0;                                           /* User mode Buffer */
int32 UF = 0;                                           /* User mode Flag */
int32 SR = 0;                                           /* Switch Register */
300
301
302
303
304
305
306


307
308
309
310
311
312
313
   cpu_mod      CPU modifier list
*/

UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) };

REG cpu_reg[] = {
    { ORDATAD (PC, saved_PC, 15, "program counter") },


    { ORDATAD (AC, saved_LAC, 12, "accumulator") },
    { FLDATAD (L, saved_LAC, 12, "link") },
    { ORDATAD (MQ, saved_MQ, 12, "multiplier-quotient") },
    { ORDATAD (SR, SR, 12, "front panel switches") },
    { GRDATAD (IF, saved_PC, 8, 3, 12, "instruction field") },
    { GRDATAD (DF, saved_DF, 8, 3, 12, "data field") },
    { GRDATAD (IB, IB, 8, 3, 12, "instruction field buffter") },







>
>







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
   cpu_mod      CPU modifier list
*/

UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) };

REG cpu_reg[] = {
    { ORDATAD (PC, saved_PC, 15, "program counter") },
    { ORDATAD (MA, saved_MA, 12, "memory address") },
    { ORDATAD (next_Major_State, saved_Major_State, 2, "major state") },
    { ORDATAD (AC, saved_LAC, 12, "accumulator") },
    { FLDATAD (L, saved_LAC, 12, "link") },
    { ORDATAD (MQ, saved_MQ, 12, "multiplier-quotient") },
    { ORDATAD (SR, SR, 12, "front panel switches") },
    { GRDATAD (IF, saved_PC, 8, 3, 12, "instruction field") },
    { GRDATAD (DF, saved_DF, 8, 3, 12, "data field") },
    { GRDATAD (IB, IB, 8, 3, 12, "instruction field buffter") },
353
354
355
356
357
358
359









360
361
362
363
364

365
366
367
368
369
370
371
372



373
374
375
376
377
378
379















380
381
382
383
384
385
386
387
388
389
390
391


392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416


417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432

433

434
435
436
437
438
439
440

441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459

460
461
462
463

464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490



491
492
493
494
495
496
497
498
499
500
501



502







503
504
505
506
507
508
509
510
511
512






513





514
515
516
517
518
519
520
521
522


523


524
525
526
527
528
529
530



531
532
533
534
535
536
537

538
539
540
541
542
543

544
545




546
547
548
549
550
551
552


553







554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
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
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738



739
740
741
742
743
744
745
746
747
748
749
750
751
752
753

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830



831



832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855

856
857
858


859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876

877



878




879
880
881
882
883
884
885

886

887
888
889
890

891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908

909
910

911
912
913
914
915
916
917
918
919



920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935



936

937
938
939
940
941
942
943
944
945
946
947


948
949
950
951
952

953
954
955
956
957
958
959
960


961
962

963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981

982

983
984


985
986
987

988
989
990

991

992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010

1011

1012
1013



1014

1015
1016
1017
1018

1019
1020
1021
1022

1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039

1040
1041
1042
1043
1044
1045
1046

1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080

1081
1082
1083

1084
1085













































1086




1087











































































1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108








1109
1110
1111
1112




1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132

1133

1134




1135
1136
1137
1138
1139
1140
1141
DEVICE cpu_dev = {
    "CPU", &cpu_unit, cpu_reg, cpu_mod,
    1, 8, 15, 1, 8, 12,
    &cpu_ex, &cpu_dep, &cpu_reset,
    NULL, NULL, NULL,
    NULL, 0
    };










t_stat sim_instr (void)
{
int32 IR, MB, IF, DF, LAC, MQ;
uint32 PC, MA;

int32 device, pulse, temp, iot_data;
t_stat reason;

/* Restore register state */

if (build_dev_tab ())                                   /* build dev_tab */
    return SCPE_STOP;
PC = saved_PC & 007777;                                 /* load local copies */



IF = saved_PC & 070000;
DF = saved_DF & 070000;
LAC = saved_LAC & 017777;
MQ = saved_MQ & 07777;
int_req = INT_UPDATE;
reason = 0;

















/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
// A copy of MA = PC | IR, held steady for the benefit of the PiDP-8/I
// display update calls, unlike the simulator's MA register which has
// the incorrect value at some of the set_pidp8i_leds() calls below.
uint32 SteadyMA;

// PiDP-8/I specific flag, set when the last instruction was an IOT
// instruction to a real device.  SIMH doesn't track this, but the front
// panel needs it.
int Pause = 0;



// Set our initial IPS value from the throttle, if given.
static time_t last_update = 0;
static size_t max_skips = 0;
static const size_t pidp8i_updates_per_sec = 10000;
max_skips = get_pidp8i_initial_max_skips (pidp8i_updates_per_sec);
srand48 (time (&last_update));

// Reset display info in case we're re-entering the simulator from Ctrl-E
extern display display_bufs[2];
memset (display_bufs, 0, sizeof(display_bufs));
static size_t skip_count, dither, inst_count;
skip_count = dither = inst_count = 0;

// Copy a global flag set by main() to control whether we run the GPIO
// stuff based on what name this program was called by.  We reference it
// this way to clue the compiler into the fact that it doesn't change
// once set: tests based on a stack constant are easier to optimize than
// those involving a non-const imported from another module.
//
// This flag can also be disabled if the GPIO thread is started but it
// fails to attach the resources it needs.  No point doing any of the
// work to feed the GPIO thread if it failed to start.
extern int use_pidp8i_extensions;
const int pidp8i_gpio = use_pidp8i_extensions && pidp8i_gpio_present;


#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */


/* Main instruction fetch/decode loop */

while (reason == 0) {                                   /* loop until halted */

    // Allow clean exit to SCP: https://github.com/simh/simh/issues/387
    if (cpu_astop != 0) {
        cpu_astop = 0;
        reason = SCPE_STOP;
        break;
        }

    if (sim_interval <= 0) {                            /* check clock queue */

        if ((reason = sim_process_event ())) {

/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
            // We're about to leave the instruction decode loop, so
            // pause the display driver thread and set it up so that it
            // will resume correctly if user says "cont".
            extern int resumeFromInstructionLoopExit, swStop, swSingInst;
            resumeFromInstructionLoopExit = swStop = swSingInst = 1;


            // Repaint one last time in case the simulator is pausing —
            // e.g. due to Ctrl-E — rather than about to exit.  Without
            // this, the front panel won't show the correct state, a
            // serious problem since it'll be stuck in that state for
            // the user to study until the user gives a "cont" command.
            //
            // We're passing IR for the MB line on purpose.  MB doesn't
            // have the correct value at this point.
            if (pidp8i_gpio) {
                set_pidp8i_leds (PC, SteadyMA, IR, IR, LAC, MQ, IF, DF,
                    SC, int_req, Pause);

                // Also copy SR hardware value to software register in
                // case the user tries poking at it from the sim> prompt.
                SR = get_switch_register();
                }
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */

            break;
            }
        }


/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
    switch (pidp8i_gpio ? handle_flow_control_switches(M, &PC, &SteadyMA,
            &MB, &LAC, &IF, &DF, &int_req) : pft_normal) {
        case pft_stop:
            // Tell the SIMH event queue to keep running even though
            // we're stopped.  Without this, it will ignore Ctrl-E
            // until the simulator is back in free-running mode.
            sim_interval = sim_interval - 1;

            // Have to keep display updated while stopped.  This does
            // mean if the software starts with the STOP switch held
            // down, we'll put garbage onto the display for MA, MB, and
            // IR, but that's what the real hardware does, too.  See
            // https://github.com/simh/simh/issues/386
            set_pidp8i_leds (PC, SteadyMA, MB, IR, LAC, MQ, IF, DF, SC,
                    int_req, Pause);

            // Go no further in STOP mode.  In particular, fetch no more
            // instructions, and do not touch PC!  Limit call rate in this
            // mode; no point burning host-side CPU on this.
            sleep_ms (10);   
            continue;

        case pft_halt:
            // Clear all registers and halt simulator
            PC  = saved_PC  = 0;



            IF  = saved_PC  = 0;
            DF  = saved_DF  = 0;
            LAC = saved_LAC = 0;
            MQ  = saved_MQ  = 0;
            int_req = 0;
            reason = STOP_HALT;
            continue;

        case pft_normal:
            // execute normally
            break;



    }







#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */

    if (int_req > INT_PENDING) {                        /* interrupt? */
        int_req = int_req & ~INT_ION;                   /* interrupts off */
        SF = (UF << 6) | (IF >> 9) | (DF >> 12);        /* form save field */
        PCQ_ENTRY (IF | PC);                            /* save old PC with IF */
        IF = IB = DF = UF = UB = 0;                     /* clear mem ext */
        M[0] = PC;                                      /* save PC in 0 */
        PC = 1;                                         /* fetch next from 1 */






        }






    MA = IF | PC;                                       /* form PC */
/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
    SteadyMA = MA;                                      /* latch it for PiDP-8/I display */
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */
    if (sim_brk_summ && 
        sim_brk_test (MA, (1u << SIM_BKPT_V_SPC) | SWMASK ('E'))) { /* breakpoint? */


        reason = STOP_IBKPT;                            /* stop simulation */


        break;
        }

    IR = M[MA];                                         /* fetch instruction */
    if (sim_brk_summ && 
        sim_brk_test (IR, (2u << SIM_BKPT_V_SPC) | SWMASK ('I'))) { /* breakpoint? */
        reason = STOP_OPBKPT;                            /* stop simulation */



        break;
        }
    PC = (PC + 1) & 07777;                              /* increment PC */
    int_req = int_req | INT_NO_ION_PENDING;             /* clear ION delay */
    sim_interval = sim_interval - 1;

/* Instruction decoding.


   The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>)
   are decoded together.  This produces 32 decode points, four per
   major opcode.  For IOT, the extra decode points are not useful;
   for OPR, only the group flag (IR<3>) is used.


   AND, TAD, ISZ, DCA calculate a full 15b effective address.
   JMS, JMP calculate a 12b field-relative effective address.





   Autoindex calculations always occur within the same field as the
   instruction fetch.  The field must exist; otherwise, the instruction
   fetched would be 0000, and indirect addressing could not occur.

   Note that MA contains IF'PC.
*/










    if (hst_lnt) {                                      /* history enabled? */
        int32 ea;

        hst_p = (hst_p + 1);                            /* next entry */
        if (hst_p >= hst_lnt)
            hst_p = 0;
        hst[hst_p].pc = MA | HIST_PC;                   /* save PC, IR, LAC, MQ */
        hst[hst_p].ir = IR;
        hst[hst_p].lac = LAC;
        hst[hst_p].mq = MQ;
        if (IR < 06000) {                               /* mem ref? */
            if (IR & 0200)
                ea = (MA & 077600) | (IR & 0177);
            else ea = IF | (IR & 0177);                 /* direct addr */
            if (IR & 0400) {                            /* indirect? */
                if (IR < 04000) {                       /* mem operand? */
                    if ((ea & 07770) != 00010)
                        ea = DF | M[ea];
                    else ea = DF | ((M[ea] + 1) & 07777);
                    }
                else {                                  /* no, jms/jmp */
                    if ((ea & 07770) != 00010)
                        ea = IB | M[ea];
                    else ea = IB | ((M[ea] + 1) & 07777);
                    }
                }
            hst[hst_p].ea = ea;                         /* save eff addr */
            hst[hst_p].opnd = M[ea];                    /* save operand */
            }
        }

switch ((IR >> 7) & 037) {                              /* decode IR<0:4> */

/* Opcode 0, AND */

    case 000:                                           /* AND, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        LAC = LAC & (M[MA] | 010000);
        break;

    case 001:                                           /* AND, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        LAC = LAC & (M[MA] | 010000);
        break;

    case 002:                                           /* AND, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = LAC & (M[MA] | 010000);
        break;

    case 003:                                           /* AND, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = LAC & (M[MA] | 010000);
        break;

/* Opcode 1, TAD */

    case 004:                                           /* TAD, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        LAC = (LAC + M[MA]) & 017777;
        break;

    case 005:                                           /* TAD, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        LAC = (LAC + M[MA]) & 017777;
        break;

    case 006:                                           /* TAD, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = (LAC + M[MA]) & 017777;
        break;

    case 007:                                           /* TAD, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        LAC = (LAC + M[MA]) & 017777;
        break;

/* Opcode 2, ISZ */

    case 010:                                           /* ISZ, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        M[MA] = MB = (M[MA] + 1) & 07777;               /* field must exist */
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

    case 011:                                           /* ISZ, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        M[MA] = MB = (M[MA] + 1) & 07777;               /* field must exist */
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

    case 012:                                           /* ISZ, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        MB = (M[MA] + 1) & 07777;
        if (MEM_ADDR_OK (MA))
            M[MA] = MB;
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

    case 013:                                           /* ISZ, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        MB = (M[MA] + 1) & 07777;
        if (MEM_ADDR_OK (MA))
            M[MA] = MB;
        if (MB == 0)
            PC = (PC + 1) & 07777;
        break;

/* Opcode 3, DCA */

    case 014:                                           /* DCA, dir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

    case 015:                                           /* DCA, dir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

    case 016:                                           /* DCA, indir, zero */
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        if (MEM_ADDR_OK (MA))
            M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

    case 017:                                           /* DCA, indir, curr */
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = DF | M[MA];
        else MA = DF | (M[MA] = (M[MA] + 1) & 07777);   /* incr before use */
        if (MEM_ADDR_OK (MA))
            M[MA] = LAC & 07777;
        LAC = LAC & 010000;
        break;

/* Opcode 4, JMS.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF
   flag is cleared. The address of the JMS instruction is loaded into the ERTB
   register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the
   target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF,
   clearing the interrupt inhibit flag, storing of the return address in the first
   word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed
   as usual. */

    case 020:                                           /* JMS, dir, zero */
        PCQ_ENTRY (MA);
        MA = IR & 0177;                                 /* dir addr, page zero */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */



            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

    case 021:                                           /* JMS, dir, curr */
        PCQ_ENTRY (MA);
        MA = (MA & 007600) | (IR & 0177);               /* dir addr, curr page */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */

            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

    case 022:                                           /* JMS, indir, zero */
        PCQ_ENTRY (MA);
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

    case 023:                                           /* JMS, indir, curr */
        PCQ_ENTRY (MA);
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            }
        if (UF && tsc_enb) {                            /* user mode, TSC enab? */
            tsc_pc = (PC - 1) & 07777;                  /* save PC */
            int_req = int_req | INT_TSC;                /* request intr */
            }
        else {                                          /* normal */
            IF = IB;                                    /* change IF */
            UF = UB;                                    /* change UF */
            int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
            MA = IF | MA;
            if (MEM_ADDR_OK (MA))
                M[MA] = PC;
            }
        PC = (MA + 1) & 07777;
        break;

/* Opcode 5, JMP.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF
   flag is cleared. The address of the JMP instruction is loaded into the ERTB
   register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual
   (including the setting of IF, UF and clearing the interrupt inhibit flag). */


    case 024:                                           /* JMP, dir, zero */
        PCQ_ENTRY (MA);



        MA = IR & 0177;                                 /* dir addr, page zero */



        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */
                }
            }
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

/* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */

    case 025:                                           /* JMP, dir, curr */
        PCQ_ENTRY (MA);
        MA = (MA & 007600) | (IR & 0177);               /* dir addr, curr page */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */

                int_req = int_req | INT_TSC;            /* request intr */
                }
            }


        if (sim_idle_enab &&                            /* idling enabled? */
            (IF == IB)) {                               /* to same bank? */
            if (MA == ((PC - 2) & 07777)) {             /* 1) JMP *-1? */
                if (!(int_req & (INT_ION|INT_TTI)) &&   /*    iof, TTI flag off? */
                    (M[IB|((PC - 2) & 07777)] == OP_KSF)) /*  next is KSF? */
                    sim_idle (TMR_CLK, FALSE);          /* we're idle */
                }                                       /* end JMP *-1 */
            else if (MA == ((PC - 1) & 07777)) {        /* 2) JMP *? */
                if (!(int_req & INT_ION))               /*    iof? */
                    reason = STOP_LOOP;                 /* then infinite loop */
                else if (!(int_req & INT_ALL))          /*    ion, not intr? */
                    sim_idle (TMR_CLK, FALSE);          /* we're idle */
                }                                       /* end JMP */
            }                                           /* end idle enabled */
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;

        break;








    case 026:                                           /* JMP, indir, zero */
        PCQ_ENTRY (MA);
        MA = IF | (IR & 0177);                          /* dir addr, page zero */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */

            tsc_ir = IR;                                /* save instruction */

            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */
                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */

                }
            }
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

    case 027:                                           /* JMP, indir, curr */
        PCQ_ENTRY (MA);
        MA = (MA & 077600) | (IR & 0177);               /* dir addr, curr page */
        if ((MA & 07770) != 00010)                      /* indirect; autoinc? */
            MA = M[MA];
        else MA = (M[MA] = (M[MA] + 1) & 07777);        /* incr before use */
        if (UF) {                                       /* user mode? */
            tsc_ir = IR;                                /* save instruction */
            tsc_cdf = 0;                                /* clear flag */
            if (tsc_enb) {                              /* TSC8 enabled? */

                tsc_pc = (PC - 1) & 07777;              /* save PC */
                int_req = int_req | INT_TSC;            /* request intr */

                }
            }
        IF = IB;                                        /* change IF */
        UF = UB;                                        /* change UF */
        int_req = int_req | INT_NO_CIF_PENDING;         /* clr intr inhibit */
        PC = MA;
        break;

/* Opcode 7, OPR group 1 */




    case 034:case 035:                                  /* OPR, group 1 */
        switch ((IR >> 4) & 017) {                      /* decode IR<4:7> */
        case 0:                                         /* nop */
            break;
        case 1:                                         /* CML */
            LAC = LAC ^ 010000;
            break;
        case 2:                                         /* CMA */
            LAC = LAC ^ 07777;
            break;
        case 3:                                         /* CMA CML */
            LAC = LAC ^ 017777;
            break;
        case 4:                                         /* CLL */
            LAC = LAC & 07777;



            break;

        case 5:                                         /* CLL CML = STL */
            LAC = LAC | 010000;
            break;
        case 6:                                         /* CLL CMA */
            LAC = (LAC ^ 07777) & 07777;
            break;
        case 7:                                         /* CLL CMA CML */
            LAC = (LAC ^ 07777) | 010000;
            break;
        case 010:                                       /* CLA */
            LAC = LAC & 010000;


            break;
        case 011:                                       /* CLA CML */
            LAC = (LAC & 010000) ^ 010000;
            break;
        case 012:                                       /* CLA CMA = STA */

            LAC = LAC | 07777;
            break;
        case 013:                                       /* CLA CMA CML */
            LAC = (LAC | 07777) ^ 010000;
            break;
        case 014:                                       /* CLA CLL */
            LAC = 0;
            break;


        case 015:                                       /* CLA CLL CML */
            LAC = 010000;

            break;
        case 016:                                       /* CLA CLL CMA */
            LAC = 07777;
            break;
        case 017:                                       /* CLA CLL CMA CML */
            LAC = 017777;
            break;
            }                                           /* end switch opers */

        if (IR & 01)                                    /* IAC */
            LAC = (LAC + 1) & 017777;
        switch ((IR >> 1) & 07) {                       /* decode IR<8:10> */
        case 0:                                         /* nop */
            break;
        case 1:                                         /* BSW */
            LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6);
            break;
        case 2:                                         /* RAL */
            LAC = ((LAC << 1) | (LAC >> 12)) & 017777;

            break;

        case 3:                                         /* RTL */
            LAC = ((LAC << 2) | (LAC >> 11)) & 017777;


            break;
        case 4:                                         /* RAR */
            LAC = ((LAC >> 1) | (LAC << 12)) & 017777;

            break;
        case 5:                                         /* RTR */
            LAC = ((LAC >> 2) | (LAC << 11)) & 017777;

            break;

        case 6:                                         /* RAL RAR - undef */
            LAC = LAC & (IR | 010000);                  /* uses AND path */
            break;
        case 7:                                         /* RTL RTR - undef */
            LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177);
            break;                                      /* uses address path */
            }                                           /* end switch shifts */
        break;                                          /* end group 1 */

/* OPR group 2.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with
   HLT and OSR: Additional to raising a user mode interrupt, the current OPR
   opcode is moved to the ERIOT register and the ECDF flag is cleared. */

    case 036:case 037:                                  /* OPR, groups 2, 3 */
        if ((IR & 01) == 0) {                           /* group 2 */
            switch ((IR >> 3) & 017) {                  /* decode IR<6:8> */
            case 0:                                     /* nop */

                break;

            case 1:                                     /* SKP */
                PC = (PC + 1) & 07777;



                break;

            case 2:                                     /* SNL */
                if (LAC >= 010000)
                    PC = (PC + 1) & 07777;
                break;

            case 3:                                     /* SZL */
                if (LAC < 010000)
                    PC = (PC + 1) & 07777;
                break;

            case 4:                                     /* SZA */
                if ((LAC & 07777) == 0)
                    PC = (PC + 1) & 07777;
                break;
            case 5:                                     /* SNA */
                if ((LAC & 07777)
                    != 0) PC = (PC + 1) & 07777;
                break;
            case 6:                                     /* SZA | SNL */
                if ((LAC == 0) || (LAC >= 010000))
                    PC = (PC + 1) & 07777;
                break;
            case 7:                                     /* SNA & SZL */
                if ((LAC != 0) && (LAC < 010000))
                    PC = (PC + 1) & 07777;
                break;
            case 010:                                   /* SMA */

                if ((LAC & 04000) != 0)
                    PC = (PC + 1) & 07777;
                break;
            case 011:                                   /* SPA */
                if ((LAC & 04000) == 0)
                    PC = (PC + 1) & 07777;
                break;

            case 012:                                   /* SMA | SNL */
                if (LAC >= 04000)
                    PC = (PC + 1) & 07777;
                break;
            case 013:                                   /* SPA & SZL */
                if (LAC < 04000)
                    PC = (PC + 1) & 07777;
                break;
            case 014:                                   /* SMA | SZA */
                if (((LAC & 04000) != 0) || ((LAC & 07777) == 0))
                    PC = (PC + 1) & 07777;
                break;
            case 015:                                   /* SPA & SNA */
                if (((LAC & 04000) == 0) && ((LAC & 07777) != 0))
                    PC = (PC + 1) & 07777;
                break;
            case 016:                                   /* SMA | SZA | SNL */
                if ((LAC >= 04000) || (LAC == 0))
                    PC = (PC + 1) & 07777;
                break;
            case 017:                                   /* SPA & SNA & SZL */
                if ((LAC < 04000) && (LAC != 0))
                    PC = (PC + 1) & 07777;
                break;
                }                                       /* end switch skips */
            if (IR & 0200)                              /* CLA */
                LAC = LAC & 010000;
            if ((IR & 06) && UF) {                      /* user mode? */
                int_req = int_req | INT_UF;             /* request intr */
                tsc_ir = IR;                            /* save instruction */
                tsc_cdf = 0;                            /* clear flag */
                }
            else {
                if (IR & 04) {                          /* OSR */

/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
                    if (pidp8i_gpio) SR = get_switch_register();  /* get current SR */

#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */













































                    LAC = LAC | SR;




                    }











































































                if (IR & 02) {                          /* HLT */
//--- PiDP change-----------------------------------------------------------------------
#ifdef PIDP8I
                    if (pidp8i_gpio) {
                        // We've got a front panel, so treat HLT the
                        // same as pressing the STOP key: CONT resumes.
                        extern int swStop;
                        swStop = 1;
                        }
                    else {
                        // Fall back to pure SIMH behavior: drop to sim>
                        // prompt where user can poke at the simulator
                        // and resume with a "cont" command.
                        reason = STOP_HALT;
                        }
                    }
#else
                    reason = STOP_HALT;
                    }
#endif
//--- end of PiDP change----------------------------------------------------------------








                }
            break;
            }                                           /* end if group 2 */





/* OPR group 3 standard

   MQA!MQL exchanges AC and MQ, as follows:

        temp = MQ;
        MQ = LAC & 07777;
        LAC = LAC & 010000 | temp;
*/

        temp = MQ;                                      /* group 3 */
        if (IR & 0200)                                  /* CLA */
            LAC = LAC & 010000;
        if (IR & 0020) {                                /* MQL */
            MQ = LAC & 07777;
            LAC = LAC & 010000;
            }
        if (IR & 0100)                                  /* MQA */
            LAC = LAC | temp;
        if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) {
            reason = stop_inst;                         /* EAE not present */

            break;

            }





/* OPR group 3 EAE

   The EAE operates in two modes:

        Mode A, PDP-8/I compatible
        Mode B, extended capability







>
>
>
>
>
>
>
>
>





>








>
>
>







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



<
<
<
<
<




>
>











|
|












>
>
















>

>





<
|
>






<
<
<

|
|







>




>


|
|
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|

>
>
>








|
|
|
>
>
>
|
>
>
>
>
>
>
>
|
<

<
<
<
<
<
<
<
>
>
>
>
>
>
|
>
>
>
>
>

<
<
<
|
<
<
|
|
>
>
|
>
>
|
|

|
|
<
<
>
>
>
|
|
<
<
<
|
|
>

<
|
|
<

>
|
|
>
>
>
>
|
<
<
<

<
|
>
>

>
>
>
>
>
>
>
|
|
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
|
<
|
<
<
<
<
<
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
>
>
>
|
<
<
<
<
<
<
<
<
<
<
<
|
<
<
>
|
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|







<
<
|
>
>
>
|
>
>
>
|
|
|
|
|
|
|
|
<
<
<
<
|
|
|
|
|
<
|
|
<
<
<
|
>
|
<
<
>
>
<
<
<
<
<
<
|
<
<
<
<
<
|
<
|
|
|
|
>
|
>
>
>

>
>
>
>
|
<
<
<
<
<
|
>
|
>
|
<
|
<
>
|
<
|
|
|
<
<
|
|
<
<
<
<
<
|
|
<
|
>
|
|
>
|
<
<
|
|
<
|

<
>
>
>

<
<
|
|
<
<
<
<
|
|
|
<
<
|
|
>
>
>
|
>
|
|
<
<
|
<
<
|
<
<
|
>
>
|
|
<
<
|
>
|
|
|
<
<
|
|
|
>
>
|
|
>
|
|
<
|
|
|
|
|

|
|
<
<
|
|
<
<
|
|
>
|
>
|
<
>
>
|
|
|
>
|
|
<
>
|
>
|
|
|
|
<
|
<
<
|
<
|
<
<
<

<
<
<
|
>
|
>
|
|
>
>
>
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
|
<
<
|
|
|
|
|
|
|
|
<
|
>
|
<
<
|
<
<
|
>
|
|
|
|
|
<
<
<
|
<
|
|
|
<
<
|
<
<
<
|
<
<
<
|
|
|
<
<
<
<
<
|
<
|
>


|
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|


|
|
|
<
|
|
|
|
|
|
|
|
|

|
|


>
>
>
>
>
>
>
>
|
<
<
|
>
>
>
>
|

|

|
|
|
|
<
|
|
|
|
|
|
|
|
|
|
|
>
|
>
|
>
>
>
>







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421





422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478

479
480
481
482
483
484
485
486



487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506





507














508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536

537







538
539
540
541
542
543
544
545
546
547
548
549
550



551


552
553
554
555
556
557
558
559
560
561
562
563


564
565
566
567
568



569
570
571
572

573
574

575
576
577
578
579
580
581
582
583



584

585
586
587
588
589
590
591
592
593
594
595
596
597

598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624


























































































































625









626

627







628

629










630
631
632
633











634


635
636





637
638








639











640











641
642























643
644
645
646
647
648
649
650
651


652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667




668
669
670
671
672

673
674



675
676
677


678
679






680





681

682
683
684
685
686
687
688
689
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
715
716
717


718
719

720
721

722
723
724
725


726
727




728
729
730


731
732
733
734
735
736
737
738
739


740


741


742
743
744
745
746


747
748
749
750
751


752
753
754
755
756
757
758
759
760
761

762
763
764
765
766
767
768
769


770
771


772
773
774
775
776
777

778
779
780
781
782
783
784
785

786
787
788
789
790
791
792

793


794

795



796



797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822


823
824
825
826
827
828
829
830

831
832
833


834


835
836
837
838
839
840
841



842

843
844
845


846



847



848
849
850





851

852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991

992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014


1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027

1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
DEVICE cpu_dev = {
    "CPU", &cpu_unit, cpu_reg, cpu_mod,
    1, 8, 15, 1, 8, 12,
    &cpu_ex, &cpu_dep, &cpu_reset,
    NULL, NULL, NULL,
    NULL, 0
    };

// These definitions support simulation of Fetch, Defer, and Execute cpu major states
// so that the Sing Step switch can behave as on a real pdp-8. The major states are
// detailed in, for example, the 1973 small computer handbook on pages 3-18 to 3-22.
// http://bitsavers.informatik.uni-stuttgart.de/pdf/dec/pdp8/handbooks/Small_Computer_Handbook_1973.pdf
#define FETCH_state     1
#define DEFER_state     2
#define EXECUTE_state   3


t_stat sim_instr (void)
{
int32 IR, MB, IF, DF, LAC, MQ;
uint32 PC, MA;
uint16 this_Major_State, next_Major_State;
int32 device, pulse, temp, iot_data;
t_stat reason;

/* Restore register state */

if (build_dev_tab ())                                   /* build dev_tab */
    return SCPE_STOP;
PC = saved_PC & 007777;                                 /* load local copies */
MA = saved_MA & 007777;
IR = saved_IR & 007777;
this_Major_State = next_Major_State = saved_Major_State;
IF = saved_PC & 070000;
DF = saved_DF & 070000;
LAC = saved_LAC & 017777;
MQ = saved_MQ & 07777;
int_req = INT_UPDATE;
reason = 0;

////////////////////////////////////////////////////////////////////////////////////
// For some strange reason, there are times when IB can be essentially uninitialized.
// It seems harmless most of the time, but it's deadly on TSS-8 startup which is at
// address 24200. Then, at 24204 is a JMS 0060. The JMS code for the EXECUTE major
// state, below, necessarily uses IB to give correct behavior following a CIF. The
// killer problem is that when--without this if () test--that TSS-8 JMS executes,
// IB is 0 not 2 so it is interpreted as a JMS to 00060 instead of the necessary
// JMS to 20060. Having this test forces IB to be set to IF the first time through
// so it's no longer "uninitialized" with respect to the simulated execution. See
// the TBD FIX in the code for the Start switch in main.c.in. When that is fixed,
// this can be removed.
if (IB = -1)
    IB = IF;
////////////////////////////////////////////////////////////////////////////////////


/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I





// PiDP-8/I specific flag, set when the last instruction was an IOT
// instruction to a real device.  SIMH doesn't track this, but the front
// panel needs it.
int Pause = 0;

int op_code = 0;

// Set our initial IPS value from the throttle, if given.
static time_t last_update = 0;
static size_t max_skips = 0;
static const size_t pidp8i_updates_per_sec = 10000;
max_skips = get_pidp8i_initial_max_skips (pidp8i_updates_per_sec);
srand48 (time (&last_update));

// Reset display info in case we're re-entering the simulator from Ctrl-E
extern display display_bufs[2];
memset (display_bufs, 0, sizeof(display_bufs));
static size_t skip_count, dither, cycle_count;
skip_count = dither = cycle_count = 0;

// Copy a global flag set by main() to control whether we run the GPIO
// stuff based on what name this program was called by.  We reference it
// this way to clue the compiler into the fact that it doesn't change
// once set: tests based on a stack constant are easier to optimize than
// those involving a non-const imported from another module.
//
// This flag can also be disabled if the GPIO thread is started but it
// fails to attach the resources it needs.  No point doing any of the
// work to feed the GPIO thread if it failed to start.
extern int use_pidp8i_extensions;
const int pidp8i_gpio = use_pidp8i_extensions && pidp8i_gpio_present;

extern int resumeFromInstructionLoopExit, cpuRun, swSingStep, swSingInst;
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */


/* Main instruction fetch/decode loop */

while (reason == 0) {                                   /* loop until halted */

    // Allow clean exit to SCP: https://github.com/simh/simh/issues/387
    if (cpu_astop != 0) {
        cpu_astop = 0;
        reason = SCPE_STOP;
        break;
        }

    if (sim_interval <= 0) {                            /* check clock queue */

        if ((reason = sim_process_event ())) {

/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
            // We're about to leave the instruction decode loop, so
            // pause the display driver thread and set it up so that it
            // will resume correctly if user says "cont".

            resumeFromInstructionLoopExit = 1;
            cpuRun = 0;

            // Repaint one last time in case the simulator is pausing —
            // e.g. due to Ctrl-E — rather than about to exit.  Without
            // this, the front panel won't show the correct state, a
            // serious problem since it'll be stuck in that state for
            // the user to study until the user gives a "cont" command.



            if (pidp8i_gpio) {
                set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF,
                    SC, int_req, this_Major_State, Pause);

                // Also copy SR hardware value to software register in
                // case the user tries poking at it from the sim> prompt.
                SR = get_switch_register();
                }
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */

            break;
            }
        }


/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
    switch (pidp8i_gpio ? handle_flow_control_switches(M, MEMSIZE, &PC, &MA,
            &MB, &LAC, &IF, &DF, &next_Major_State, &int_req) : pft_running) {




















        case pft_exit:
            // Exiting SIMH altogether, clear all registers and halt the simulator
            PC  = saved_PC  = 0;
            MA  = saved_MA  = 0;
            IR  = saved_IR  = 0;
            this_Major_State = next_Major_State = saved_Major_State;
            IF  = saved_PC  = 0;
            DF  = saved_DF  = 0;
            LAC = saved_LAC = 0;
            MQ  = saved_MQ  = 0;
            int_req = 0;
            reason = STOP_HALT;
            continue;

        case pft_stopped:
            // the cpu is stopped, nothing to do here
            // Tell the SIMH event queue to keep running even though
            // we're now stopped.  Without this, it will ignore Ctrl-E
            // until the simulator is back in free-running mode.
            sim_interval = sim_interval - 1;

            // Have to keep display updated while stopped.  This does
            // mean if the software starts with the STOP switch held
            // down, we'll put garbage onto the display for MA, MB, and
            // IR, but that's what the real hardware does, too.  See
            // https://github.com/simh/simh/issues/386
            set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
                             int_req, this_Major_State, Pause);
            continue;









        case pft_running:
            // the CPU is executing (i.e., running) normally, check if it needs to stop. 
            // SingStep stops at the beginning of the next major state
            // SingInst stops at the beginning of the next instruction, i.e.,
            //    the next major state is Fetch
            if ((swSingStep == 1) || ((swSingInst == 1) && (next_Major_State == FETCH_state))) {

                // The CPU needs to stop
                // Tell the SIMH event queue to keep running even though
                // we're now stopped.  Without this, it will ignore Ctrl-E
                // until the simulator is back in free-running mode.
                sim_interval = sim_interval - 1;




                // Have to keep display updated while stopped.  This does


                // mean if the software starts with the STOP switch held
                // down, we'll put garbage onto the display for MA, MB, and
                // IR, but that's what the real hardware does, too.  See
                // https://github.com/simh/simh/issues/386
                cpuRun = 0;
                set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
                                 int_req, this_Major_State, Pause);
                continue;
                }

            break;                                                  /* continue running */



        case pft_go:
            // a (re-) start on swStart or swCont has to skip the above check for
            // swSingStep and swSingInst to be sure that at least one cycle executes
            break;




    }
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */


    this_Major_State = next_Major_State;
    switch (this_Major_State) {


        case FETCH_state:
            // fetch state for all instructions, regardless of op code
            MA = IF | PC & 07777;                                   /* form PC */
            if (sim_brk_summ && 
                sim_brk_test (MA, (1u << SIM_BKPT_V_SPC) | SWMASK ('E'))) { /* breakpoint? */
                reason = STOP_IBKPT;                                /* stop simulation */
                break;
                }





            PC = (PC + 1) & 07777;                                  /* increment PC */
            int_req = int_req | INT_NO_ION_PENDING;                 /* clear ION delay */
            sim_interval = sim_interval - 1;

            IR = MB = M[MA];                                        /* fetch instruction */
            if (sim_brk_summ && 
                sim_brk_test (IR, (2u << SIM_BKPT_V_SPC) | SWMASK ('I'))) { /* breakpoint? */
                reason = STOP_OPBKPT;                               /* stop simulation */
                break;
                }

            if (hst_lnt) {                                          /* history enabled? */
                int32 ea;

                hst_p = (hst_p + 1);                                /* next entry */
                if (hst_p >= hst_lnt)
                    hst_p = 0;
                hst[hst_p].pc = MA | HIST_PC;                       /* save PC, IR, LAC, MQ */
                hst[hst_p].ir = IR;
                hst[hst_p].lac = LAC;
                hst[hst_p].mq = MQ;
                if (IR < 06000) {                                   /* mem ref? */
                    if (IR & 0200)
                        ea = (MA & 077600) | (IR & 0177);
                    else ea = IF | (IR & 0177);                     /* direct addr */
                    if (IR & 0400) {                                /* indirect? */
                        if (IR < 04000) {                           /* mem operand? */
                            if ((ea & 07770) != 00010)
                                ea = DF | M[ea];
                            else ea = DF | ((M[ea] + 1) & 07777);
                            }
                        else {                                      /* no, jms/jmp */
                            if ((ea & 07770) != 00010)
                                ea = IB | M[ea];
                            else ea = IB | ((M[ea] + 1) & 07777);
                            }
                        }
                    hst[hst_p].ea = ea;                             /* save eff addr */
                    hst[hst_p].opnd = M[ea];                        /* save operand */
                    }
                }




































































































































            op_code = (IR >> 9) & 07;

            switch (op_code) {







                case 4: 

                    PCQ_ENTRY (MA);










                    // intentional fall-through for JMS
                case 0:case 1:case 2:case 3:
                    // Fetch state for MRIs: AND, TAD, ISZ, DCA, JMS
                    if (IR & 0200)                                  /* current page or zero page? */











                        MA = (MA & 007600) | (IR & 0177);           /* current page */


                    else
                        MA = IR & 0177;                             /* zero page */





                    if (IR & 0400)                                  /* indirect or direct? */
                        next_Major_State = DEFER_state;             /* indirect */








                    else











                        next_Major_State = EXECUTE_state;           /* direct */











                    break;
                    // end of case op_code 0..4: AND, TAD, ISZ, DCA, JMS























                case 5:
                    // Fetch state for JMP
/* Opcode 5, JMP.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF
   flag is cleared. The address of the JMP instruction is loaded into the ERTB
   register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual
   (including the setting of IF, UF and clearing the interrupt inhibit flag). */



                    PCQ_ENTRY (MA);
                    if (IR & 0200)                                  /* current page or zero page? */
                        MA = (MA & 077600) | (IR & 0177);           /* current page */
                    else
                        MA = IF | (IR & 0177);                      /* zero page */
                    if (IR & 0400)                                  /* direct or indirect? */
                        next_Major_State = DEFER_state;             /* indirect JMP */
                    else {
                        if (UF) {                                   /* direct, user mode? */
                            tsc_ir = IR;                            /* save instruction */
                            tsc_cdf = 0;                            /* clear flag */
                            if (tsc_enb) {                          /* TSC8 enabled? */
                                tsc_pc = (PC - 1) & 07777;          /* save PC */
                                int_req = int_req | INT_TSC;        /* request intr */
                            }
                        }




                        if ((IR & 0200 == 0) &&  sim_idle_enab &&   /* current page? idling enabled? */
                            (IF == IB)) {                           /* to same bank? */
                            if (MA == ((PC - 2) & 07777)) {         /* 1) JMP *-1? */
                                if (!(int_req & (INT_ION|INT_TTI)) &&     /*    iof, TTI flag off? */
                                    (M[IB|((PC - 2) & 07777)] == OP_KSF)) /*  next is KSF? */

                                    sim_idle (TMR_CLK, FALSE);      /* we're idle */
                                }                                   /* end 1) JMP *-1 */



                            else if (MA == ((PC - 1) & 07777)) {    /* 2) JMP *? */
                                    if (!(int_req & INT_ION))       /*    iof? */
                                        reason = STOP_LOOP;         /* then infinite loop */


                                    else if (!(int_req & INT_ALL))  /*    ion, not intr? */
                                        sim_idle (TMR_CLK, FALSE);  /* we're idle */






                                     }                              /* end 2) JMP */





                            }                                       /* end current page, idle enabled, same bank */

                        IF = IB;                                    /* change IF */
                        UF = UB;                                    /* change UF */
                        int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
                        PC = MA;
                        }                                           /* end direct JMP */
                    break;
                    // end of case op_code 5: JMP
                case 6:
                    // Fetch state for IOTs

/* From Bernhard Baehr's description of the TSC8-75:
   (In user mode) Additional to raising a user mode interrupt, the current IOT
   opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1),
   the ECDF flag is set, otherwise it is cleared. */






                    if (UF) {                                       /* privileged? */
                        int_req = int_req | INT_UF;                 /* request intr */
                        tsc_ir = IR;                                /* save instruction */
                        if ((IR & 07707) == 06201)                  /* set/clear flag */
                            tsc_cdf = 1;

                        else tsc_cdf = 0;

                        break;
                        }

                    device = (IR >> 3) & 077;                       /* device = IR<3:8> */
                    pulse = IR & 07;                                /* pulse = IR<9:11> */
                    iot_data = LAC & 07777;                         /* AC unchanged */


                    switch (device) {                               /* decode IR<3:8> */
                    case 000:                                       /* CPU control */





                        switch (pulse) {                            /* decode IR<9:11> */


                        case 0:                                     /* SKON */
                            if (int_req & INT_ION)
                                PC = (PC + 1) & 07777;
                            int_req = int_req & ~INT_ION;
                            break;



                        case 1:                                     /* ION */
                            int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING;

                            break;


                        case 2:                                     /* IOF */
                            int_req = int_req & ~INT_ION;
                            break;



                        case 3:                                     /* SRQ */
                            if (int_req & INT_ALL)




                                PC = (PC + 1) & 07777;
                            break;



                        case 4:                                     /* GTF */
                            LAC = (LAC & 010000) |
                                  ((LAC & 010000) >> 1) | (gtf << 10) |
                                  (((int_req & INT_ALL) != 0) << 9) |
                                  (((int_req & INT_ION) != 0) << 7) | SF;
                            break;

                        case 5:                                     /* RTF */
                            gtf = ((LAC & 02000) >> 10);


                            UB = (LAC & 0100) >> 6;


                            IB = (LAC & 0070) << 9;


                            DF = (LAC & 0007) << 12;
                            LAC = ((LAC & 04000) << 1) | iot_data;
                            int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING;
                            break;



                        case 6:                                     /* SGT */
                            if (gtf)
                                PC = (PC + 1) & 07777;
                            break;



                        case 7:                                     /* CAF */
                            gtf = 0;
                            emode = 0;
                            int_req = int_req & INT_NO_CIF_PENDING;
                            dev_done = 0;
                            int_enable = INT_INIT_ENABLE;
                            LAC = 0;
                            reset_all (1);                          /* reset all dev */
                            break;
                            }                                       /* end switch pulse */

                        break;

                    case 020:case 021:case 022:case 023:
                    case 024:case 025:case 026:case 027:            /* memory extension */
                        switch (pulse) {                            /* decode IR<9:11> */

                        case 1:                                     /* CDF */
                            DF = (IR & 0070) << 9;


                            break;



                        case 2:                                     /* CIF */
                            IB = (IR & 0070) << 9;
                            int_req = int_req & ~INT_NO_CIF_PENDING;
                            break;

                        case 3:                                     /* CDF CIF */

                            DF = IB = (IR & 0070) << 9;
                            int_req = int_req & ~INT_NO_CIF_PENDING;
                            break;

                        case 4:
                            switch (device & 07) {                  /* decode IR<6:8> */

                            case 0:                                 /* CINT */

                                int_req = int_req & ~INT_UF;
                                break;

                            case 1:                                 /* RDF */
                                LAC = LAC | (DF >> 9);
                                    break;


                            case 2:                                 /* RIF */


                                LAC = LAC | (IF >> 9);

                                break;







                            case 3:                                 /* RIB */
                                LAC = LAC | SF;
                                break;

                            case 4:                                 /* RMF */
                                UB = (SF & 0100) >> 6;
                                IB = (SF & 0070) << 9;
                                DF = (SF & 0007) << 12;
                                int_req = int_req & ~INT_NO_CIF_PENDING;
                                break;

                            case 5:                                 /* SINT */
                                if (int_req & INT_UF)
                                    PC = (PC + 1) & 07777;
                                break;

                            case 6:                                 /* CUF */
                                UB = 0;
                                int_req = int_req & ~INT_NO_CIF_PENDING;
                                break;

                            case 7:                                 /* SUF */
                                UB = 1;
                                int_req = int_req & ~INT_NO_CIF_PENDING;
                                break;
                                }                                   /* end switch device */


                            break;
            
                        default:
                            reason = stop_inst;
                            break;
                            }                                       /* end switch pulse */
                        break;                                      /* end case 20-27 */


                    case 010:                                       /* power fail */
                        switch (pulse) {                            /* decode IR<9:11> */



                        case 1:                                     /* SBE */


                            break;

                        case 2:                                     /* SPL */
                            if (int_req & INT_PWR)
                                PC = (PC + 1) & 07777;
                            break;




                        case 3:                                     /* CAL */

                            int_req = int_req & ~INT_PWR;
                            break;



                        default:



                            reason = stop_inst;



                            break;
                            }                                       /* end switch pulse */
                        break;                                      /* end case 10 */







                    default:                                        /* I/O device */
                        if (dev_tab[device]) {                      /* dev present? */
/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
                            // Any other device will trigger IOP, so light pause
                            Pause = 1;
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */
                            iot_data = dev_tab[device] (IR, iot_data);
                            LAC = (LAC & 010000) | (iot_data & 07777);
                            if (iot_data & IOT_SKP)
                                PC = (PC + 1) & 07777;
                            if (iot_data >= IOT_REASON)
                                reason = iot_data >> IOT_V_REASON;
                            }
                        else reason = stop_inst;                    /* stop on flag */
                        break;
                        }                                           /* end switch device */
                    break;
                    // end of case op_code 6: IOT
                case 7:
                    // Fetch state for OPRs
                    if (!(IR & 00400)) {
                        /* OPR group 1 */
                        if (IR & 0200)
                            LAC = LAC & 010000;                     /* CLA is sequence 1 */
                        if (IR & 0100)
                            LAC = LAC & 007777;                     /* CLL is sequence 1 */
                        if (IR & 0040)
                            LAC = LAC ^ 007777;                     /* CMA is sequence 2 */
                        if (IR & 0020)
                            LAC = LAC ^ 010000;                     /* CML is sequence 2 */
                        if (IR & 0001)
                            LAC = (LAC + 1) & 017777;               /* IAC is sequence 3 */
                        switch (IR & 00016) {                       /* rotates are sequence 4 */
                            case 0000:
                                break;
                            case 0002:                              /* BSW */
                                LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6);
                                break;
                            case 0004:                              /* RAL */
                                LAC = ((LAC << 1) | (LAC >> 12)) & 017777;
                                break;
                            case 0006:                              /* RTL */
                                LAC = ((LAC << 2) | (LAC >> 11)) & 017777;
                                break;
                            case 0010:                              /* RAR */
                                LAC = ((LAC >> 1) | (LAC << 12)) & 017777;
                                break;
                            case 0012:                              /* RTR */
                                LAC = ((LAC >> 2) | (LAC << 11)) & 017777;
                                break;
                            case 0014:                              /* RAL RAR - undef */
                                LAC = LAC & (IR | 010000);          /* uses AND path */
                                break;
                            case 0016:                              /* RTL RTR - undef */
                                LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); /* uses address path */
                                break;
                            }
                        }
                        /* end of OPR group 1 */
                    else if (IR & 00400 && !(IR & 00001)) {
                        /* OPR group 2 */
                        switch (IR & 00170) {
                            /* skips are sequence 1 */
                            case 0010:                               /* SKP */
                                PC = (PC + 1) & 07777;
                                break;
                            case 0020:                               /* SNL */
                                if (LAC >= 010000)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0030:                               /* SZL */
                                if (LAC < 010000)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0040:                               /* SZA */
                                if ((LAC & 07777) == 0)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0050:                               /* SNA */
                                if ((LAC & 07777) != 0 )
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0060:                               /* SZA SNL */
                                if ((LAC == 0) || (LAC >= 010000))
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0070:                               /* SNA SZL */
                                if ((LAC != 0) && (LAC < 010000))
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0100:                               /* SMA */
                                if ((LAC & 04000) != 0)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0110:                               /* SPA */
                                if ((LAC & 04000) == 0)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0120:                               /* SMA SNL */
                                if (LAC >= 04000)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0130:                               /* SPA SZL */
                                if (LAC < 04000)
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0140:                               /* SMA SZA */
                                if (((LAC & 04000) != 0) || ((LAC & 07777) == 0))
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0150:                               /* SPA SNA */
                                if (((LAC & 04000) == 0) && ((LAC & 07777) != 0))
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0160:                               /* SMA SZA SNL */
                                if ((LAC >= 04000) || (LAC == 0))
                                    PC = (PC + 1) & 07777;
                                break;
                            case 0170:                               /* SPA SNA SZL */
                                if ((LAC < 04000) && (LAC != 0))
                                    PC = (PC + 1) & 07777;
                                break;
                            } // end of switch (IR & 00176)
                            if (IR & 0200)
                                LAC = LAC & 010000;                 /* CLA is sequence 2 */
                            if (IR & 06) {                          /* HLT, OSR are sequence 3 */
                                if (UF) {                           /* user mode? */
                                    int_req = int_req | INT_UF;     /* request intr */
                                    tsc_ir = IR;                    /* save instruction */
                                    tsc_cdf = 0;                    /* clear flag */
                                    }
                                else {
                                    if (IR & 02) {                   /* HLT */
//--- PiDP change-----------------------------------------------------------------------
#ifdef PIDP8I
                                        if (pidp8i_gpio) {
                                            // We've got a front panel, so treat HLT the
                                            // same as pressing the STOP key: CONT resumes.

                                            cpuRun = 0;
                                            }
                                        else {
                                            // Fall back to pure SIMH behavior: drop to sim>
                                            // prompt where user can poke at the simulator
                                            // and resume with a "cont" command.
                                            reason = STOP_HALT;
                                           }
                                        }
#else
                                        reason = STOP_HALT;
                                        }
#endif
//--- end of PiDP change----------------------------------------------------------------
                                    else {                           /* OSR */
/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
                                        if (pidp8i_gpio)
                                            SR = get_switch_register();  /* get current SR */
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */
                                        LAC = LAC | SR;
                                        }


                                    }
                                }
                            }
                        /* end of OPR group 2 */
                    else {
                        /* OPR group 3, standard

                               MQA!MQL exchanges AC and MQ, as follows:

                               temp = MQ;
                               MQ = LAC & 07777;
                               LAC = LAC & 010000 | temp;
                        */

                        temp = MQ;                                  /* group 3 */
                        if (IR & 0200)                              /* CLA */
                            LAC = LAC & 010000;
                        if (IR & 0020) {                            /* MQL */
                            MQ = LAC & 07777;
                            LAC = LAC & 010000;
                            }
                        if (IR & 0100)                              /* MQA */
                           LAC = LAC | temp;
                        if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) {
                           reason = stop_inst;                      /* EAE not present */
                            }

/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

All EAE code below remains indented/formatted as in the original file as it fits better on the page

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */


/* OPR group 3 EAE

   The EAE operates in two modes:

        Mode A, PDP-8/I compatible
        Mode B, extended capability
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376

1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413

1414
1415
1416
1417
1418
1419
1420


1421
1422
1423

1424
1425

1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436

1437
1438
1439
1440
1441
1442



1443
1444

1445
1446
1447
1448
1449

1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482

1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536


1537
1538
1539

1540
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
                temp = 0;
            else temp = temp >> SC;                     /* <=24? shift AC:MQ */
            LAC = (temp >> 12) & 07777;
            MQ = temp & 07777;
            PC = (PC + 1) & 07777;
            SC = emode? 037: 0;                         /* SC = 0 if mode A */
            break;
            }                                           /* end switch */
        break;                                          /* end case 7 */

/* Opcode 6, IOT.  From Bernhard Baehr's description of the TSC8-75:

   (In user mode) Additional to raising a user mode interrupt, the current IOT
   opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1),
   the ECDF flag is set, otherwise it is cleared. */

    case 030:case 031:case 032:case 033:                /* IOT */
        if (UF) {                                       /* privileged? */
            int_req = int_req | INT_UF;                 /* request intr */
            tsc_ir = IR;                                /* save instruction */
            if ((IR & 07707) == 06201)                  /* set/clear flag */
                tsc_cdf = 1;
            else tsc_cdf = 0;
            break;

            }
        device = (IR >> 3) & 077;                       /* device = IR<3:8> */
        pulse = IR & 07;                                /* pulse = IR<9:11> */
        iot_data = LAC & 07777;                         /* AC unchanged */
        switch (device) {                               /* decode IR<3:8> */

        case 000:                                       /* CPU control */
            switch (pulse) {                            /* decode IR<9:11> */

            case 0:                                     /* SKON */
                if (int_req & INT_ION)
                    PC = (PC + 1) & 07777;
                int_req = int_req & ~INT_ION;
                break;

            case 1:                                     /* ION */
                int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING;
                break;

            case 2:                                     /* IOF */
                int_req = int_req & ~INT_ION;
                break;

            case 3:                                     /* SRQ */
                if (int_req & INT_ALL)
                    PC = (PC + 1) & 07777;
                break;

            case 4:                                     /* GTF */
                LAC = (LAC & 010000) |
                      ((LAC & 010000) >> 1) | (gtf << 10) |
                      (((int_req & INT_ALL) != 0) << 9) |
                      (((int_req & INT_ION) != 0) << 7) | SF;
                break;

            case 5:                                     /* RTF */
                gtf = ((LAC & 02000) >> 10);

                UB = (LAC & 0100) >> 6;
                IB = (LAC & 0070) << 9;
                DF = (LAC & 0007) << 12;
                LAC = ((LAC & 04000) << 1) | iot_data;
                int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING;
                break;



            case 6:                                     /* SGT */
                if (gtf)
                    PC = (PC + 1) & 07777;

                break;


            case 7:                                     /* CAF */
                gtf = 0;
                emode = 0;
                int_req = int_req & INT_NO_CIF_PENDING;
                dev_done = 0;
                int_enable = INT_INIT_ENABLE;
                LAC = 0;
                reset_all (1);                          /* reset all dev */
                break;
                }                                       /* end switch pulse */
            break;                                      /* end case 0 */


        case 020:case 021:case 022:case 023:
        case 024:case 025:case 026:case 027:            /* memory extension */
            switch (pulse) {                            /* decode IR<9:11> */

            case 1:                                     /* CDF */



                DF = (IR & 0070) << 9;
                break;


            case 2:                                     /* CIF */
                IB = (IR & 0070) << 9;
                int_req = int_req & ~INT_NO_CIF_PENDING;
                break;


            case 3:                                     /* CDF CIF */
                DF = IB = (IR & 0070) << 9;
                int_req = int_req & ~INT_NO_CIF_PENDING;
                break;

            case 4:
                switch (device & 07) {                  /* decode IR<6:8> */

                case 0:                                 /* CINT */
                    int_req = int_req & ~INT_UF;
                    break;

                case 1:                                 /* RDF */
                    LAC = LAC | (DF >> 9);
                        break;

                case 2:                                 /* RIF */
                    LAC = LAC | (IF >> 9);
                    break;

                case 3:                                 /* RIB */
                    LAC = LAC | SF;
                    break;

                case 4:                                 /* RMF */
                    UB = (SF & 0100) >> 6;
                    IB = (SF & 0070) << 9;
                    DF = (SF & 0007) << 12;
                    int_req = int_req & ~INT_NO_CIF_PENDING;
                    break;

                case 5:                                 /* SINT */

                    if (int_req & INT_UF)
                        PC = (PC + 1) & 07777;
                    break;

                case 6:                                 /* CUF */
                    UB = 0;
                    int_req = int_req & ~INT_NO_CIF_PENDING;
                    break;

                case 7:                                 /* SUF */
                    UB = 1;
                    int_req = int_req & ~INT_NO_CIF_PENDING;
                    break;
                    }                                   /* end switch device */
                break;
            
            default:
                reason = stop_inst;
                break;
                }                                       /* end switch pulse */
            break;                                      /* end case 20-27 */

        case 010:                                       /* power fail */
            switch (pulse) {                            /* decode IR<9:11> */

            case 1:                                     /* SBE */
                break;

            case 2:                                     /* SPL */
                if (int_req & INT_PWR)
                    PC = (PC + 1) & 07777;
                break;

            case 3:                                     /* CAL */
                int_req = int_req & ~INT_PWR;
                break;

            default:
                reason = stop_inst;
                break;
                }                                       /* end switch pulse */
            break;                                      /* end case 10 */

        default:                                        /* I/O device */
            if (dev_tab[device]) {                      /* dev present? */
/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
                // Any other device will trigger IOP, so light pause
                Pause = 1;
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */
                iot_data = dev_tab[device] (IR, iot_data);
                LAC = (LAC & 010000) | (iot_data & 07777);
                if (iot_data & IOT_SKP)


                    PC = (PC + 1) & 07777;
                if (iot_data >= IOT_REASON)
                    reason = iot_data >> IOT_V_REASON;

                }
            else reason = stop_inst;                    /* stop on flag */

            break;
            }                                           /* end switch device */
        break;                                          /* end case IOT */

        }                                               /* end switch opcode */

/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
    // Update the front panel with this instruction's final state.
    //
    // There's no point saving *every* LED "on" count.  We just need a
    // suitable amount of oversampling.  We can skip this if we called
    // set_pidp8i_leds recently enough, avoiding all the expensive bit
    // shift and memory update work it does.
    //
    // The trick here is figuring out what "recently enough" means
    // without making expensive OS timer calls.  These timers aren't
    // hopelessly slow (http://stackoverflow.com/a/13096917/142454) but
    // we still don't want to be taking dozens of cycles per instruction
    // just to keep our update estimate current in the face of system
    // load changes and SET THROTTLE updates.
    //
    // Instead, we maintain a model of the current IPS value — seeded
    // with the initial "SET THROTTLE" value, if any — to figure out
    // how many calls we can skip while still meeting our other goals.
    // This involves a bit of math, but when paid only once a second,
    // it amortizes much nicer than estimating the skip count directly
    // based on a more accurate time source which is more expensive







|
|

|

|
<
<

<
<
<
<
<
<
<
<
>
|
<
<
<
<
|
<
<
|
<
<
<
<
|
|
<
<
<

<
<
<
|
<
<
<


|
<
<
<
<
<

|
<
>
|
<
<
<
<
<
|
>
>
|
<
<
>
|
|
>
|
<
<
<
<
<
<
<
<
|
|
>
|
<
<
<
|
<
>
>
>
|
<
>
|
<
<
<
|
>

<
<
<
<

|
|
|
|
<
|
|
<
<
<
|
<
<
<
|
|
|
|
<
|
<
<
|
<
|
<
|
>
|
|
|
<
|
|
<
<
|
<
<
<
|
|
<
|
<
<
|
|
|
<
<
|
|
<
<
|
<
<
<
<
|
<
|
<
|
<
<
<
<
<
<
|
|
|
|
<
<
<
<
<
|
|
>
>
|
<
<
>

<
>

|
|
>
|



|









|
|
|







1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277


1278








1279
1280




1281


1282




1283
1284



1285



1286



1287
1288
1289





1290
1291

1292
1293





1294
1295
1296
1297


1298
1299
1300
1301
1302








1303
1304
1305
1306



1307

1308
1309
1310
1311

1312
1313



1314
1315
1316




1317
1318
1319
1320
1321

1322
1323



1324



1325
1326
1327
1328

1329


1330

1331

1332
1333
1334
1335
1336

1337
1338


1339



1340
1341

1342


1343
1344
1345


1346
1347


1348




1349

1350

1351






1352
1353
1354
1355





1356
1357
1358
1359
1360


1361
1362

1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
                temp = 0;
            else temp = temp >> SC;                     /* <=24? shift AC:MQ */
            LAC = (temp >> 12) & 07777;
            MQ = temp & 07777;
            PC = (PC + 1) & 07777;
            SC = emode? 037: 0;                         /* SC = 0 if mode A */
            break;

            }                                           /* end of OPR group 3 */

/* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

All EAE code above remains indented/formatted as in the original file as it fits better on the page











xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx */





                        }


                        /* end of OPR group 3 */




                    break;
                    // end of case op_code 7







                } // end of switch (op_code)



                break;

            // end of case FETCH_state






        case DEFER_state:

            MA = IF | MA;                                   /* defer major state uses IF */
            MB = M[MA];





            if ((MA & 07770) == 00010)                      /* autoincrement needed? */
                M[MA] = ++MB & 07777;                       /* yes, do the autoincrement */
            MA =  MB;                                       /* get the target address */
            if (((IR >> 9) & 07) != 5)                      /* MRI or JMP? */


                next_Major_State = EXECUTE_state;           /* it's a MRI */
            else {
                if (UF) {                                   /* it's a JMP, user mode? */
                    tsc_ir = IR;                            /* save instruction */
                    tsc_cdf = 0;                            /* clear flag */








                    if (tsc_enb) {                          /* TSC8 enabled? */
                        tsc_pc = (PC - 1) & 07777;          /* save PC */
                        int_req = int_req | INT_TSC;        /* request intr */
                    }



                }

                IF = IB;                                    /* change IF */
                UF = UB;                                    /* change UF */
                int_req = int_req | INT_NO_CIF_PENDING;     /* clr intr inhibit */
                PC = MA;

                next_Major_State = FETCH_state;
            }



            break;
            // end of case DEFER_state






        case EXECUTE_state:
            if (((IR >> 9) & 07) < 4) {                     /* AND .. DCA, or is it JMS? */
                if (IR & 00400)                             /* it is AND .. DCA, direct or indirect? */
                    MA = DF | (MA & 07777);                 /* indirect, use DF */

                else
                    MA = IF | (MA & 07777);                 /* direct, use IF */



                MB = M[MA];                                 /* get the data word */



                switch ((IR >> 9) & 07) {
                    case 0:                                 /* AND */
                        LAC = LAC & (MB | 010000);
                        break;

                    case 1:                                 /* TAD */


                        LAC = (LAC + MB) & 017777;

                        break;

                    case 2:                                 /* ISZ */
                        M[MA] = MB = (MB + 1) & 07777;
                        if (MB == 0)
                            PC = (PC + 1) & 07777;
                        break;

                    case 3:                                 /* DCA */
                        M[MA] = MB = LAC & 07777;


                        LAC = LAC & 010000;



                        break;
                    }  // end of switch ((IR >> 9) & 07)

                }


            else {
                if (UF) {                                   /* JMS, user mode? */
                    tsc_ir = IR;                            /* save instruction */


                    tsc_cdf = 0;                            /* clear flag */
                    }


                if (UF && tsc_enb) {                        /* user mode, TSC enab? */




                    tsc_pc = (PC - 1) & 07777;              /* save PC */

                    int_req = int_req | INT_TSC;            /* request intr */

                    }






                else {                                      /* normal JMS */
                    IF = IB;                                /* change IF */
                    UF = UB;                                /* change UF */
                    int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */





                    MA = IF | (MA & 07777);
                    if (MEM_ADDR_OK (MA))
                        M[MA] = PC;                         /* write the return address */
                    }
                MB = MA & 07777;


                PC = (MA + 1) & 07777;                      /* set the PC to entry + 1 */
                }

            next_Major_State = FETCH_state;
            break;
            // end of case EXECUTE_state

        }  // end of switch (Major_State)


/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
    // Update the front panel with this Major State's ending status.
    //
    // There's no point saving *every* LED "on" count.  We just need a
    // suitable amount of oversampling.  We can skip this if we called
    // set_pidp8i_leds recently enough, avoiding all the expensive bit
    // shift and memory update work it does.
    //
    // The trick here is figuring out what "recently enough" means
    // without making expensive OS timer calls.  These timers aren't
    // hopelessly slow (http://stackoverflow.com/a/13096917/142454) but
    // we still don't want to be taking dozens of our cpu cycles per
    // PiDP-8/I major state just to keep our update estimate current
    // in the face of system load changes and SET THROTTLE updates.
    //
    // Instead, we maintain a model of the current IPS value — seeded
    // with the initial "SET THROTTLE" value, if any — to figure out
    // how many calls we can skip while still meeting our other goals.
    // This involves a bit of math, but when paid only once a second,
    // it amortizes much nicer than estimating the skip count directly
    // based on a more accurate time source which is more expensive
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617













1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636



1637
1638
1639
1640
1641

1642
1643
1644
1645
1646
1647
1648
    // You might think to move this code to the top of set_pidp8i_leds,
    // but the function call itself is a nontrivial hit.  In fact, you
    // don't even want to move all of this to a function here in this
    // module and try to get GCC to inline it: that's good for a 1 MIPS
    // speed hit in my testing!  (GCC 4.9.2, Raspbian Jessie on Pi 3B.)

    if (pidp8i_gpio && (++skip_count + dither >= max_skips )) {
        // Save skips to inst counter and reset
        inst_count += skip_count;
        skip_count = 0;

        // We need to update the LED data again.  Unlike above, circa
        // line 444, we pass the final MB value, not a copy of IR, as
        // MB is settled by this point.
        set_pidp8i_leds (PC, SteadyMA, MB, IR, LAC, MQ, IF, DF, SC,
                 int_req, Pause);
 
        // Has it been ~1s since we updated our max_skips value?
        time_t now;
        if (time(&now) > last_update) {
            // Yep; simulator IPS may have changed, so freshen it.
            last_update = now;
            max_skips = inst_count / pidp8i_updates_per_sec;
            //printf("Inst./repaint: %zu - %zu; %.2f MIPS\r\n",
            //        max_skips, dither, inst_count / 1e6);
            inst_count = 0;
            }
        dither = lrand48() % (( max_skips > 32 ) ? max_skips >> 3 : 4); // 12.5%
        }
    Pause = 0;      // it's set outside the "if", so it must be *reset* outside
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */













    }                                                   /* end while */

/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
// If we're leaving the simulator's CPU instruction execution loop for
// the last time, during program shutdown, also clear all of the LEDs,
// else we'll leave them solidly lit.
//
// If instead we're leaving for a simulator pause, as with a Ctrl-E
// escape, leave the LEDs alone, so user can see the CPU's paused state.
if (pidp8i_gpio && (reason == SCPE_STOP) && pidp8i_gpio_present) {
    turn_off_pidp8i_leds ();
    }
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */

/* Simulation halted */

saved_PC = IF | (PC & 07777);                           /* save copies */



saved_DF = DF & 070000;
saved_LAC = LAC & 017777;
saved_MQ = MQ & 07777;
pcq_r->qptr = pcq_p;                                    /* update pc q ptr */
return reason;

}                                                       /* end sim_instr */

/*
 * This sequence of instructions is a mix that hopefully
 * represents a resonable instruction set that is a close 
 * estimate to the normal calibrated result.
 */







|
|


|
<
<
|
|
|





|

|
|






>
>
>
>
>
>
>
>
>
>
>
>
>
|




|













>
>
>





>







1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420


1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
    // You might think to move this code to the top of set_pidp8i_leds,
    // but the function call itself is a nontrivial hit.  In fact, you
    // don't even want to move all of this to a function here in this
    // module and try to get GCC to inline it: that's good for a 1 MIPS
    // speed hit in my testing!  (GCC 4.9.2, Raspbian Jessie on Pi 3B.)

    if (pidp8i_gpio && (++skip_count + dither >= max_skips )) {
        // Save skips to cycle counter and reset
        cycle_count += skip_count;
        skip_count = 0;

        // We need to update the LED data again.


        set_pidp8i_leds (PC, MA, MB, IR, LAC, MQ, IF, DF, SC,
                int_req, this_Major_State, Pause);

        // Has it been ~1s since we updated our max_skips value?
        time_t now;
        if (time(&now) > last_update) {
            // Yep; simulator IPS may have changed, so freshen it.
            last_update = now;
            max_skips = cycle_count / pidp8i_updates_per_sec;
            //printf("Inst./repaint: %zu - %zu; %.2f MIPS\r\n",
            //        max_skips, dither, cycle_count / 1e6);
            cycle_count = 0;
            }
        dither = lrand48() % (( max_skips > 32 ) ? max_skips >> 3 : 4); // 12.5%
        }
    Pause = 0;      // it's set outside the "if", so it must be *reset* outside
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */


    // at the end of a complete instruction cycle (i.e., next major state is now Fetch)
    // check for an interrupt request and handle it if it occurred with ION
    if (next_Major_State == FETCH_state && int_req > INT_PENDING) {
        int_req = int_req & ~INT_ION;                   /* occurred, so interrupts off */
        SF = (UF << 6) | (IF >> 9) | (DF >> 12);        /* form save field */
        PCQ_ENTRY (IF | PC);                            /* save old PC with IF */
        IF = IB = DF = UF = UB = 0;                     /* clear mem ext */
        M[0] = PC;                                      /* save PC in 0 */
        PC = 1;                                         /* fetch next from 1 */
        }

    }                                                   /* end while (reason == 0) */

/* ---PiDP add--------------------------------------------------------------------------------------------- */
#ifdef PIDP8I
// If we're leaving the simulator's CPU instruction execution loop for
// the last time, during program shutdown, also clears all of the LEDs,
// else we'll leave them solidly lit.
//
// If instead we're leaving for a simulator pause, as with a Ctrl-E
// escape, leave the LEDs alone, so user can see the CPU's paused state.
if (pidp8i_gpio && (reason == SCPE_STOP) && pidp8i_gpio_present) {
    turn_off_pidp8i_leds ();
    }
#endif
/* ---PiDP end---------------------------------------------------------------------------------------------- */

/* Simulation halted */

saved_PC = IF | (PC & 07777);                           /* save copies */
saved_MA = MA & 007777;
saved_IR = IR & 007777;
saved_Major_State = next_Major_State;
saved_DF = DF & 070000;
saved_LAC = LAC & 017777;
saved_MQ = MQ & 07777;
pcq_r->qptr = pcq_p;                                    /* update pc q ptr */
return reason;

}                                                       /* end sim_instr */

/*
 * This sequence of instructions is a mix that hopefully
 * represents a resonable instruction set that is a close 
 * estimate to the normal calibrated result.
 */
1657
1658
1659
1660
1661
1662
1663

1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683

1684
1685
1686

1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697

1698
1699
1700
1701
1702
1703
1704
1705
1706
1707

1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726

1727
1728
1729
1730
1731
1732
1733
    NULL};

/* Reset routine */

t_stat cpu_reset (DEVICE *dptr)
{
saved_LAC = 0;

int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING;
saved_DF = IB = saved_PC & 070000;
UF = UB = gtf = emode = 0;
pcq_r = find_reg ("PCQ", NULL, dptr);
if (pcq_r)
    pcq_r->qptr = 0;
else 
    return SCPE_IERR;
sim_clock_precalibrate_commands = pdp8_clock_precalibrate_commands;
sim_vm_initial_ips = 10 * SIM_INITIAL_IPS;
sim_brk_types = SWMASK ('E') | SWMASK('I');
sim_brk_dflt = SWMASK ('E');
return SCPE_OK;
}

/* Set PC for boot (PC<14:12> will typically be 0) */

void cpu_set_bootpc (int32 pc)
{
saved_PC = pc;                                          /* set PC, IF */

saved_DF = IB = pc & 070000;                            /* set IB, DF */
return;
}


/* Memory examine */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
    return SCPE_NXM;
if (vptr != NULL)
    *vptr = M[addr] & 07777;
return SCPE_OK;
}


/* Memory deposit */

t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
    return SCPE_NXM;
M[addr] = val & 07777;
return SCPE_OK;
}


/* Memory size change */

t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 mc = 0;
uint32 i;

if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))
    return SCPE_ARG;
for (i = val; i < MEMSIZE; i++)
    mc = mc | M[i];
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
    return SCPE_OK;
MEMSIZE = val;
for (i = MEMSIZE; i < MAXMEMSIZE; i++)
    M[i] = 0;
return SCPE_OK;
}


/* Change device number for a device */

t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;







>






|










|

|
>
|


>











>










>



















>







1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
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
    NULL};

/* Reset routine */

t_stat cpu_reset (DEVICE *dptr)
{
saved_LAC = 0;
saved_Major_State = FETCH_state;
int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING;
saved_DF = IB = saved_PC & 070000;
UF = UB = gtf = emode = 0;
pcq_r = find_reg ("PCQ", NULL, dptr);
if (pcq_r)
    pcq_r->qptr = 0;
else
    return SCPE_IERR;
sim_clock_precalibrate_commands = pdp8_clock_precalibrate_commands;
sim_vm_initial_ips = 10 * SIM_INITIAL_IPS;
sim_brk_types = SWMASK ('E') | SWMASK('I');
sim_brk_dflt = SWMASK ('E');
return SCPE_OK;
}

/* Set PC for boot (PC<14:12> will typically be 0) */

void cpu_set_bootpc (int32 PC)
{
saved_PC = PC;                                          /* set PC, IF */
saved_Major_State = FETCH_state;
saved_DF = IB = PC & 070000;                            /* set IB, DF */
return;
}


/* Memory examine */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
    return SCPE_NXM;
if (vptr != NULL)
    *vptr = M[addr] & 07777;
return SCPE_OK;
}


/* Memory deposit */

t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (addr >= MEMSIZE)
    return SCPE_NXM;
M[addr] = val & 07777;
return SCPE_OK;
}


/* Memory size change */

t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 mc = 0;
uint32 i;

if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0))
    return SCPE_ARG;
for (i = val; i < MEMSIZE; i++)
    mc = mc | M[i];
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
    return SCPE_OK;
MEMSIZE = val;
for (i = MEMSIZE; i < MAXMEMSIZE; i++)
    M[i] = 0;
return SCPE_OK;
}


/* Change device number for a device */

t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
DEVICE *dptr;
DIB *dibp;
1746
1747
1748
1749
1750
1751
1752

1753
1754
1755
1756
1757
1758
1759
    return SCPE_IERR;
newdev = get_uint (cptr, 8, DEV_MAX - 1, &r);           /* get new */
if ((r != SCPE_OK) || (newdev == dibp->dev))
    return r;
dibp->dev = newdev;                                     /* store */
return SCPE_OK;
}


/* Show device number for a device */

t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
DEVICE *dptr;
DIB *dibp;







>







1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
    return SCPE_IERR;
newdev = get_uint (cptr, 8, DEV_MAX - 1, &r);           /* get new */
if ((r != SCPE_OK) || (newdev == dibp->dev))
    return r;
dibp->dev = newdev;                                     /* store */
return SCPE_OK;
}


/* Show device number for a device */

t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
DEVICE *dptr;
DIB *dibp;
1767
1768
1769
1770
1771
1772
1773

1774
1775
1776
1777
1778
1779
1780
if (dibp == NULL)
    return SCPE_IERR;
fprintf (st, "devno=%02o", dibp->dev);
if (dibp->num > 1)
    fprintf (st, "-%2o", dibp->dev + dibp->num - 1);
return SCPE_OK;
}


/* CPU device handler - should never get here! */

int32 bad_dev (int32 IR, int32 AC)
{
return (SCPE_IERR << IOT_V_REASON) | AC;                /* broken! */
}







>







1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
if (dibp == NULL)
    return SCPE_IERR;
fprintf (st, "devno=%02o", dibp->dev);
if (dibp->num > 1)
    fprintf (st, "-%2o", dibp->dev + dibp->num - 1);
return SCPE_OK;
}


/* CPU device handler - should never get here! */

int32 bad_dev (int32 IR, int32 AC)
{
return (SCPE_IERR << IOT_V_REASON) | AC;                /* broken! */
}
1822
1823
1824
1825
1826
1827
1828

1829
1830
1831
1832
1833
1834
1835
                    }                                   /* end if dsp */
                }                                       /* end for j */
            }                                           /* end else */
        }                                               /* end if enb */
    }                                                   /* end for i */
return FALSE;
}


/* Set history */

t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i, lnt;
t_stat r;







>







1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
                    }                                   /* end if dsp */
                }                                       /* end for j */
            }                                           /* end else */
        }                                               /* end if enb */
    }                                                   /* end for i */
return FALSE;
}


/* Set history */

t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i, lnt;
t_stat r;
1853
1854
1855
1856
1857
1858
1859

1860
1861
1862
1863
1864
1865
1866
    hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
    if (hst == NULL)
        return SCPE_MEM;
    hst_lnt = lnt;
    }
return SCPE_OK;
}


/* Show history */

t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 l, k, di, lnt;
const char *cptr = (const char *) desc;







>







1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
    hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
    if (hst == NULL)
        return SCPE_MEM;
    hst_lnt = lnt;
    }
return SCPE_OK;
}


/* Show history */

t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 l, k, di, lnt;
const char *cptr = (const char *) desc;
1892
1893
1894
1895
1896
1897
1898
1899

            fprintf (st, "(undefined) %04o", h->ir);
        if (h->ir < 04000)
            fprintf (st, "  [%04o]", h->opnd);
        fputc ('\n', st);                               /* end line */
        }                                               /* end else instruction */
    }                                                   /* end for */
return SCPE_OK;
}








|
>
1740
1741
1742
1743
1744
1745
1746
1747
1748
            fprintf (st, "(undefined) %04o", h->ir);
        if (h->ir < 04000)
            fprintf (st, "  [%04o]", h->opnd);
        fputc ('\n', st);                               /* end line */
        }                                               /* end else instruction */
    }                                                   /* end for */
return SCPE_OK;

}

Changes to src/pidp8i/gpio-common.c.in.

1
2
3
4

5
6
7
8
9
10
11
/*
 * gpio-common.c: functions common to both gpio.c and gpio-nls.c
 *
 * Copyright © 2015 Oscar Vermeulen, © 2016-2019 by Warren Young

 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:



|
>







1
2
3
4
5
6
7
8
9
10
11
12
/*
 * gpio-common.c: functions common to both gpio.c and gpio-nls.c
 *
 * Copyright © 2015 Oscar Vermeulen, © 2016-2018 by Warren Young
 *           © 2021 Steve Tockey
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 * by the other gpio-* modules, from the GPIO thread.
*/

#include "pidp8i.h"

#include <config.h>

#if defined(HAVE_BCM_HOST_H)
#   include <bcm_host.h>
#endif

#include <pthread.h>
#include <sys/file.h>
#include <sys/time.h>

#include <ctype.h>
#include <errno.h>
#include <signal.h>







<
<
<
<







35
36
37
38
39
40
41




42
43
44
45
46
47
48
 * by the other gpio-* modules, from the GPIO thread.
*/

#include "pidp8i.h"

#include <config.h>





#include <pthread.h>
#include <sys/file.h>
#include <sys/time.h>

#include <ctype.h>
#include <errno.h>
#include <signal.h>
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
static const ms_time_t debounce_ms = 50;    // time switch state must remain stable


// Flow-control switch states which are owned by -- that is, primarily
// modified by -- the PDP8/pidp8i module, but we can't define these
// there because we refer to them below, and not all programs that link
// to us link to that module as well.  For such programs, it's fine if
// these two flags stay 0.
int swStop = 0, swSingInst = 0;

// Flag to override ILS mode, forcing fallback to NLS mode.  Set when
// the PDP-8 instruction decoding loop detects that we're using the
// ratio form of SET THROTTLE, which prevents the use of ILS due to the
// way instructions are executed in that mode.  Defined here rather than
// in gpio-ils.c because we don't want to make code that sets this
// conditional based on whether ILS is in fact actually enabled.







|
|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
static const ms_time_t debounce_ms = 50;    // time switch state must remain stable


// Flow-control switch states which are owned by -- that is, primarily
// modified by -- the PDP8/pidp8i module, but we can't define these
// there because we refer to them below, and not all programs that link
// to us link to that module as well.  For such programs, it's fine if
// these flags stay 0.
int cpuRun = 0, swSingInst = 0, swSingStep = 0;

// Flag to override ILS mode, forcing fallback to NLS mode.  Set when
// the PDP-8 instruction decoding loop detects that we're using the
// ratio form of SET THROTTLE, which prevents the use of ILS due to the
// way instructions are executed in that mode.  Defined here rather than
// in gpio-ils.c because we don't want to make code that sets this
// conditional based on whether ILS is in fact actually enabled.
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// thread consumes the values provided by the CPU thread.

#define SWAP(dir) \
    __atomic_exchange_n (&pdis_update, display_bufs + dir, __ATOMIC_SEQ_CST)

void swap_displays ()
{
    if (!swStop && !swSingInst) {
        if (pidp8i_simple_gpio_mode) {
            // We're linked to a program that wants to use the old
            // ledstatus[] interface for updating the display, so copy
            // its values into the paint-from display structure and
            // return.  We don't need to touch the update-to display or
            // swap anything, because set_pidp8i_leds won't be called.
            static const int levels = 32;
            memcpy (pdis_paint->curr, ledstatus, 
                    sizeof (pdis_paint->curr));
            pdis_paint->inst_count = levels;

            for (size_t row = 0; row < NLEDROWS; ++row) {
                size_t *prow = pdis_paint->on[row];
                for (size_t col = 0, mask = 1; col < NCOLS;
                        ++col, mask <<= 1) {
                    prow[col] = !!(ledstatus[row] & mask) * levels;
                }







|









|







242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// thread consumes the values provided by the CPU thread.

#define SWAP(dir) \
    __atomic_exchange_n (&pdis_update, display_bufs + dir, __ATOMIC_SEQ_CST)

void swap_displays ()
{
    if (cpuRun == 1) {
        if (pidp8i_simple_gpio_mode) {
            // We're linked to a program that wants to use the old
            // ledstatus[] interface for updating the display, so copy
            // its values into the paint-from display structure and
            // return.  We don't need to touch the update-to display or
            // swap anything, because set_pidp8i_leds won't be called.
            static const int levels = 32;
            memcpy (pdis_paint->curr, ledstatus, 
                    sizeof (pdis_paint->curr));
            pdis_paint->cycle_count = levels;

            for (size_t row = 0; row < NLEDROWS; ++row) {
                size_t *prow = pdis_paint->on[row];
                for (size_t col = 0, mask = 1; col < NCOLS;
                        ++col, mask <<= 1) {
                    prow[col] = !!(ledstatus[row] & mask) * levels;
                }
454
455
456
457
458
459
460


461
462
463
464
465
466

467
468
469
470
471
472
473
    if (time(&now) != last) {
        printf("\r\nLED: [PC:%04o] [MA:%04o] [MB:%04o] [AC:%04o] [MQ:%04o]",
                pcurr[0], pcurr[1], pcurr[2], pcurr[3], pcurr[4]);
        last = now;
    }
#endif
    


    // Override Execute and Run LEDs if the CPU is currently stopped,
    // since we only get set_pidp8i_leds calls while the CPU's running.
    if (swStop || swSingInst) {
        pdis_paint->curr[5] &= ~(1 << 2);
        pdis_paint->curr[6] &= ~(1 << 7);
    }


    for (size_t row = 0; row < NLEDROWS; ++row) {
        for (size_t col = 0; col < NCOLS; ++col) {
            gpio_set_fsel(cols[col], GPIO_FSEL_INPUT);
            if ((pcurr[row] & (1 << col)) == 0) {
                //GPIO_SET = 1 << cols[col];
		gpio_set_drive(cols[col], DRIVE_HIGH);







>
>


|



>







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
    if (time(&now) != last) {
        printf("\r\nLED: [PC:%04o] [MA:%04o] [MB:%04o] [AC:%04o] [MQ:%04o]",
                pcurr[0], pcurr[1], pcurr[2], pcurr[3], pcurr[4]);
        last = now;
    }
#endif
    
#if 0
// This appears to be no longer necessary in the cycle-realistic version
    // Override Execute and Run LEDs if the CPU is currently stopped,
    // since we only get set_pidp8i_leds calls while the CPU's running.
    if (cpuRun == 0) {
        pdis_paint->curr[5] &= ~(1 << 2);
        pdis_paint->curr[6] &= ~(1 << 7);
    }
#endif

    for (size_t row = 0; row < NLEDROWS; ++row) {
        for (size_t col = 0; col < NCOLS; ++col) {
            gpio_set_fsel(cols[col], GPIO_FSEL_INPUT);
            if ((pcurr[row] & (1 << col)) == 0) {
                //GPIO_SET = 1 << cols[col];
		gpio_set_drive(cols[col], DRIVE_HIGH);

Changes to src/pidp8i/gpio-common.h.

109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
    // in instructions since the last display paint.
    size_t on[NLEDROWS][NCOLS];

    // Most recent state for each LED, for use by NLS full-time and by
    // ILS in STOP mode.  (One bitfield per row.)
    uint16_t curr[NLEDROWS];

    // Number of instructions executed since this display was cleared
    int inst_count;
} display;
extern display* pdis_update, *pdis_paint;

// Compatibility interface for programs like src/test.c that depend on
// being able to modify the LED values directly.
#define ledstatus (pdis_update->curr)
extern int pidp8i_simple_gpio_mode;







|
|







109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
    // in instructions since the last display paint.
    size_t on[NLEDROWS][NCOLS];

    // Most recent state for each LED, for use by NLS full-time and by
    // ILS in STOP mode.  (One bitfield per row.)
    uint16_t curr[NLEDROWS];

    // Number of cycles (major states) executed since this display was cleared
    int cycle_count;
} display;
extern display* pdis_update, *pdis_paint;

// Compatibility interface for programs like src/test.c that depend on
// being able to modify the LED values directly.
#define ledstatus (pdis_update->curr)
extern int pidp8i_simple_gpio_mode;

Changes to src/pidp8i/gpio-ils.c.

1
2
3
4
5

6
7
8
9
10
11
12
/*
 * gpio-ils.c: implements gpio_core () for Ian Schofield's incandescent
 *             lamp simulator
 *
 * Copyright © 2015-2017 Oscar Vermeulen, Ian Schofield, and Warren Young

 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:





>







1
2
3
4
5
6
7
8
9
10
11
12
13
/*
 * gpio-ils.c: implements gpio_core () for Ian Schofield's incandescent
 *             lamp simulator
 *
 * Copyright © 2015-2017 Oscar Vermeulen, Ian Schofield, and Warren Young
 *           © 2021 Steve Tockey
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175



176
177
178
179
180
181
182









183
184
185
186
187
188
189
        // in control of the swap timing, we don't need to copy the
        // pdis_paint pointer: it points to the same thing between
        // these swap_displays() calls.
        swap_displays();

        // Recalculate the brightness target values based on the
        // "on" counts in *pdis_paint and the quantized brightness
        // level, which is based on the number of instructions
        // executed for this display update.

        const size_t inst_count =
              (pdis_paint->inst_count >0) ? pdis_paint->inst_count : 1;
        const float one_div_inst_count = 1.0 / (float) inst_count;
        for (int row = 0; row < NLEDROWS; ++row) {
            size_t *prow = pdis_paint->on[row];
            for (int col = 0; col < NCOLS; ++col) {
                // this gives range from [0 .. 32] incl (!)
                // using ceil will boost even low but nonzero counts into
                // brightness bin no 1 => 1 short pulse
                br_targets[row][col] = ceilf((prow[col] << 5) * one_div_inst_count);
            }
        }




        // Hard-code the Fetch and Execute brightnesses; in running
        // mode, they're both on half the instruction time, so we
        // just set them to 50% brightness.  Execute differs in STOP
        // mode, but that's handled in update_led_states () because
        // we fall back to NLS in STOP mode.
        br_targets[5][2] = br_targets[5][3] = MAX_BRIGHTNESS / 2;










        // Update the brightness values.
        for (int row = 0; row < NLEDROWS; ++row) {
            //size_t *prow = pdis_paint->on[row];
            for (int col = 0; col < NCOLS; ++col) {
                uint8 br_target = br_targets[row][col];
                float *p = brightness[row]+col;







|


|
|
|






|


>
>
>







>
>
>
>
>
>
>
>
>







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
        // in control of the swap timing, we don't need to copy the
        // pdis_paint pointer: it points to the same thing between
        // these swap_displays() calls.
        swap_displays();

        // Recalculate the brightness target values based on the
        // "on" counts in *pdis_paint and the quantized brightness
        // level, which is based on the number of cycles
        // executed for this display update.

        const size_t cycle_count =
              (pdis_paint->cycle_count >0) ? pdis_paint->cycle_count : 1;
        const float one_div_cycle_count = 1.0 / (float) cycle_count;
        for (int row = 0; row < NLEDROWS; ++row) {
            size_t *prow = pdis_paint->on[row];
            for (int col = 0; col < NCOLS; ++col) {
                // this gives range from [0 .. 32] incl (!)
                // using ceil will boost even low but nonzero counts into
                // brightness bin no 1 => 1 short pulse
                br_targets[row][col] = ceilf((prow[col] << 5) * one_div_cycle_count);
            }
        }
#if 0
// This has been disabled because Fetch and Execute are now accurate with
// the actual major state of the simulated machine

        // Hard-code the Fetch and Execute brightnesses; in running
        // mode, they're both on half the instruction time, so we
        // just set them to 50% brightness.  Execute differs in STOP
        // mode, but that's handled in update_led_states () because
        // we fall back to NLS in STOP mode.
        br_targets[5][2] = br_targets[5][3] = MAX_BRIGHTNESS / 2;
            for (int row = 0; row < NLEDROWS; ++row) {
                size_t *prow = pdis_paint->on[row];
                for (int col = 0; col < NCOLS; ++col) {
                    br_targets[row][col] = prow[col] / br_quant;

                }
            }

#endif

        // Update the brightness values.
        for (int row = 0; row < NLEDROWS; ++row) {
            //size_t *prow = pdis_paint->on[row];
            for (int col = 0; col < NCOLS; ++col) {
                uint8 br_target = br_targets[row][col];
                float *p = brightness[row]+col;
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
            // fill one panel row of 12 lights with test pattern
            for (int col=0; col < 12 ; col++) {
                brightness[2][col]=test_pattern[col];
            }
        }

        // Light up LEDs
        extern int swStop, swSingInst, suppressILS, forceNLS;
        if (swStop || swSingInst || suppressILS || forceNLS) {

	    // The CPU is in STOP mode or someone has suppressed the ILS,
            // so show the current LED states full-brightness using the
            // same mechanism NLS uses.  No need to force a display swap
            // in case this isn't STOP mode as it will happen on the next
            // loop interation anyway.
            update_led_states (intervl * 60);







|
|







214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
            // fill one panel row of 12 lights with test pattern
            for (int col=0; col < 12 ; col++) {
                brightness[2][col]=test_pattern[col];
            }
        }

        // Light up LEDs
        extern int cpuRun, suppressILS, forceNLS;
        if (cpuRun == 0 || suppressILS || forceNLS) {

	    // The CPU is in STOP mode or someone has suppressed the ILS,
            // so show the current LED states full-brightness using the
            // same mechanism NLS uses.  No need to force a display swap
            // in case this isn't STOP mode as it will happen on the next
            // loop interation anyway.
            update_led_states (intervl * 60);

Changes to src/pidp8i/main.c.in.

1
2
3

4


5
6
7
8
9
10
11
/* pidp8i.c: PiDP-8/I additions to the PDP-8 simulator

   Copyright © 2015 by Oscar Vermeulen, © 2017 by Ian Schofield, and

   © 2016-2018 by Warren Young



   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:


|
>
|
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* pidp8i.c: PiDP-8/I additions to the PDP-8 simulator

   Copyright © 2015 by Oscar Vermeulen,
             © 2017 by Ian Schofield,
             © 2016-2018 by Warren Young,
             © 2021 by HB Eggenstein,
             © 2021 by Steve Tockey

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41




42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the names of the authors above shall
   not be used in advertising or otherwise to promote the sale, use or other
   dealings in this Software without prior written authorization from those
   authors.

*/

#include "pidp8i.h"

#include <PDP8/pdp8_defs.h>

#include <assert.h>
#include <dirent.h> // for USB stick searching
#include <errno.h>
#include <string.h>


//// MODULE GLOBALS ////////////////////////////////////////////////////





// handle_sing_step() sets this to nonzero and returns a value breaking
// us out of the PDP-8 simulator's sim_instr() loop, which causes SCP to
// call our build_pidp8i_scp_cmd(), which gives SCP a command to run:
// either "exit" when it wants the simulator to stop (e.g the shutdown
// and reboot combos) or "do $script" on IF + SING_STEP combo.
//
// We loop the flow control from this module out into the generic SIMH
// code and then back in here so we don't have to export this global.
// Basically, this module global lets us remember what handle_sing_step
// wants SCP to do in the window between switch handling time and SCP
// command handling time.

static enum {
    CMD_NONE = 0,            // "do nothing" idle case
    CMD_DO_BOOTSCRIPT_1,     // SING_STEP + IF combos
    CMD_DO_BOOTSCRIPT_2,
    CMD_DO_BOOTSCRIPT_3,
    CMD_DO_BOOTSCRIPT_4,
    CMD_DO_BOOTSCRIPT_5,
    CMD_DO_BOOTSCRIPT_6,
    CMD_DO_BOOTSCRIPT_7,
    CMD_EXIT,
} insert_scp_cmd = CMD_NONE;


//// get_this_executable_path //////////////////////////////////////////
// Uses various non-portable tricks to come up with an absolute path to
// the current executable.  We can't just copy argv[0] from main()
// because that might be a path relative to a directory we aren't in any
// more, it could be NULL, it could be entirely bogus, or it might only
// work with our caller's non-exported PATH.







>














>
>
>
>
|
|
|
|
|

|
|
|
|
<
>











<







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.

   Except as contained in this notice, the names of the authors above shall
   not be used in advertising or otherwise to promote the sale, use or other
   dealings in this Software without prior written authorization from those
   authors.

*/

#include "pidp8i.h"

#include <PDP8/pdp8_defs.h>

#include <assert.h>
#include <dirent.h> // for USB stick searching
#include <errno.h>
#include <string.h>


//// MODULE GLOBALS ////////////////////////////////////////////////////

extern int cpuRun, swSingInst, swSingStep;
static int sing_step_single_shot = 0;

//##########################################################################################################
// handle_sing_step_special_cases() sets this to nonzero and returns a value breaking
// us out of the PDP-8 simulator's sim_instr() loop, which causes SCP to call our
// build_pidp8i_scp_cmd(), which gives SCP a command to run, either:
// "exit" when it wants the simulator to stop (e.g the shutdown and reboot combos), or
// "do $script" on IF + SING_STEP + ... combo.
//
// We loop the flow control from this module out into the generic SIMH code and then
// back in here so we don't have to export this global. Basically, this module global
// lets us remember what handle_sing_step_special_cases wants SCP to do in the window
// between switch handling time and SCP command handling time.

//##########################################################################################################
static enum {
    CMD_NONE = 0,            // "do nothing" idle case
    CMD_DO_BOOTSCRIPT_1,     // SING_STEP + IF combos
    CMD_DO_BOOTSCRIPT_2,
    CMD_DO_BOOTSCRIPT_3,
    CMD_DO_BOOTSCRIPT_4,
    CMD_DO_BOOTSCRIPT_5,
    CMD_DO_BOOTSCRIPT_6,
    CMD_DO_BOOTSCRIPT_7,
    CMD_EXIT,
} insert_scp_cmd = CMD_NONE;


//// get_this_executable_path //////////////////////////////////////////
// Uses various non-portable tricks to come up with an absolute path to
// the current executable.  We can't just copy argv[0] from main()
// because that might be a path relative to a directory we aren't in any
// more, it could be NULL, it could be entirely bogus, or it might only
// work with our caller's non-exported PATH.
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
// Given all of the PDP-8's internal registers that affect the front
// panel display, modify the GPIO thread's LED state values accordingly.
//
// Also update the LED brightness values based on those new states.

void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint32_t sMB,
    uint16_t sIR, int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF,
    int32_t sSC, int32_t int_req, int Pause)
{
    // Bump the instruction count.  This should always be equal to the
    // Fetch LED's value, but integers are too cheap to get cute here.
    //
    // Note that we only update pdis_update directly once in this whole
    // process.  This is in case the display swap happens while we're
    // working: we want to finish work on the same display even though
    // it's now called the paint-from display, so it's consistent.
    display* pd = pdis_update;
    ++pd->inst_count;

    // Rows 0-4, easy cases: single-register LED strings.
    // 
    // The values passed for rows 1 and 2 are non-obvious.  See the code
    // calling us from ../SIMH/PDP8/pdp8_cpu.c for details.
    set_pidp8i_row_leds (pd, 0, sPC);
    set_pidp8i_row_leds (pd, 1, sMA);







|

|
<






|







316
317
318
319
320
321
322
323
324
325

326
327
328
329
330
331
332
333
334
335
336
337
338
339
// Given all of the PDP-8's internal registers that affect the front
// panel display, modify the GPIO thread's LED state values accordingly.
//
// Also update the LED brightness values based on those new states.

void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint32_t sMB,
    uint16_t sIR, int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF,
    int32_t sSC, int32_t int_req, uint16_t this_Major_State, int Pause)
{
    // Bump the memory cycle (major state) count.

    //
    // Note that we only update pdis_update directly once in this whole
    // process.  This is in case the display swap happens while we're
    // working: we want to finish work on the same display even though
    // it's now called the paint-from display, so it's consistent.
    display* pd = pdis_update;
    ++pd->cycle_count;

    // Rows 0-4, easy cases: single-register LED strings.
    // 
    // The values passed for rows 1 and 2 are non-obvious.  See the code
    // calling us from ../SIMH/PDP8/pdp8_cpu.c for details.
    set_pidp8i_row_leds (pd, 0, sPC);
    set_pidp8i_row_leds (pd, 1, sMA);
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
        case 03000: set_pidp8i_led (pd, 5,  8); break; // 011 ISZ
        case 04000: set_pidp8i_led (pd, 5,  7); break; // 100 JMS
        case 05000: set_pidp8i_led (pd, 5,  6); break; // 101 JMP
        case 06000: set_pidp8i_led (pd, 5,  5); break; // 110 IOT
        case 07000: set_pidp8i_led (pd, 5,  4); break; // 111 OPR 1 & 2
    }

    // Row 5b: set the Defer LED if...
    if ((inst_type <= 05000) &&  // it's a memory reference instruction
            (sIR & 00400)) {     // and indirect addressing flag is set
        set_pidp8i_led (pd, 5, 1);
    }

    // Row 5c: The Fetch & Execute LEDs are pulsed once per instruction.
    // On real hardware, the pulses don't happen at exactly the same
    // time, but we can't simulate that because SIMH handles each CPU
    // instruction "whole."  When running real code, all we care about
    // is that both LEDs are twiddled so rapidly that they both just
    // become a 50% blur, mimicking the hardware closely enough.
    //
    // The exception is that when the CPU is stopped, both LEDs are off,
    // because the pulses happen "outside" the STOP state: Fetch before
    // and Execute after resuming from STOP.
    extern int swStop, swSingInst;
    int running = !swStop && !swSingInst;
    if (running) {
        set_pidp8i_led (pd, 5, 2);    // Execute
        set_pidp8i_led (pd, 5, 3);    // Fetch
    }

    // Row 6a: Remaining LEDs in upper right block
    pd->curr[6] = 0;
    if (running)           set_pidp8i_led (pd, 6, 7); // bump Run LED
    if (Pause)             set_pidp8i_led (pd, 6, 8); // bump Pause LED
    if (int_req & INT_ION) set_pidp8i_led (pd, 6, 9); // bump ION LED

    // Row 6b: The Step Count LEDs are also on row 6
    set_5_pidp8i_leds (pd, sSC);

    // Row 7: DF, IF, and Link.
    pd->curr[7] = 0;
    set_3_pidp8i_leds (pd, 9, sDF);
    set_3_pidp8i_leds (pd, 6, sIF);
    if (sLAC & 010000) set_pidp8i_led (pd, 7, 5);

    // If we're stopped or single-stepped, the display-swapping code
    // won't happen, so copy the above over to the paint-from version.
    extern int resumeFromInstructionLoopExit;
    if (!running || resumeFromInstructionLoopExit) {
        memcpy(pdis_paint, pdis_update, sizeof(struct display));
    }
}


//// mount_usb_stick_file //////////////////////////////////////////////
// Search for a PDP-8 media image on a USB device mounted under /media







|
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|




|












|


|







362
363
364
365
366
367
368
369
370

371















372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
        case 03000: set_pidp8i_led (pd, 5,  8); break; // 011 ISZ
        case 04000: set_pidp8i_led (pd, 5,  7); break; // 100 JMS
        case 05000: set_pidp8i_led (pd, 5,  6); break; // 101 JMP
        case 06000: set_pidp8i_led (pd, 5,  5); break; // 110 IOT
        case 07000: set_pidp8i_led (pd, 5,  4); break; // 111 OPR 1 & 2
    }

    // Row 5b: Major States
    switch (this_Major_State) {

        case 00001: set_pidp8i_led (pd, 5,  3); break; // Fetch















        case 00002: set_pidp8i_led (pd, 5,  1); break; // Defer
        case 00003: set_pidp8i_led (pd, 5,  2); break; // Execute
    }

    // Row 6a: Remaining LEDs in upper right block
    pd->curr[6] = 0;
    if (cpuRun)            set_pidp8i_led (pd, 6, 7); // bump Run LED
    if (Pause)             set_pidp8i_led (pd, 6, 8); // bump Pause LED
    if (int_req & INT_ION) set_pidp8i_led (pd, 6, 9); // bump ION LED

    // Row 6b: The Step Count LEDs are also on row 6
    set_5_pidp8i_leds (pd, sSC);

    // Row 7: DF, IF, and Link.
    pd->curr[7] = 0;
    set_3_pidp8i_leds (pd, 9, sDF);
    set_3_pidp8i_leds (pd, 6, sIF);
    if (sLAC & 010000) set_pidp8i_led (pd, 7, 5);

    // If we're stopped, the display-swapping code
    // won't happen, so copy the above over to the paint-from version.
    extern int resumeFromInstructionLoopExit;
    if (cpuRun == 0 || resumeFromInstructionLoopExit) {
        memcpy(pdis_paint, pdis_update, sizeof(struct display));
    }
}


//// mount_usb_stick_file //////////////////////////////////////////////
// Search for a PDP-8 media image on a USB device mounted under /media
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554

555
556

557
558


559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612

613
614
615
616
617
618
619
620
621
622

623
624
625
626
627
628
629
630
631
632
633
634
635

636
637
638
639
640
641
642
643
644
645
646
647




648

649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
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
715
716
717
718
719
720
721
722
723
724
725
726





727
728
729
730
731
732
733

734
735
736
737
738
739
740

741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806

807
808
809
810
811
812
813

814
815
816
817
818
819
820

821
822
823
824
825

826

827
828
829
830
831
832
833
834
835
836
837
838

839
840
841
842
843
844
845

846
847
848

849




850



851
852

853
854
855
856
857
858
859
860
861





862








863






















864
865
866
867
868
869
870
871
872






































873
874
875
876



877
878
879
880
881
882
883
884
885
    }
    else {
        printf ("\r\nNo unmounted %s file found\r\n", devCode);
    }
}


//// handle_sing_step //////////////////////////////////////////////////
// Handle SING_STEP combinations as nonstandard functions with respect
// to a real PDP-8, since SIMH doesn't try to emulate the PDP-8's
// single-stepping mode — not to be confused with single-instruction
// mode, which SIMH *does* emulate — so the SING_STEP switch is free
// for our nonstandard uses.
//
// This is separate from handle_flow_control_switches only because
// there are so many cases here that it would obscure the overall flow
// of our calling function to do all this there.

static pidp8i_flow_t handle_sing_step (int closed)
{
    // If SING_STEP is open, we do nothing here except reset the single-shot
    // flag if it was set.
    static int single_shot = 0;
    if (!closed) {
        single_shot = 0;
        return pft_normal;
    }

    // There are two sets of SING_STEP combos: first up are those where the
    // other switches involved have to be set already, and the function is
    // triggered as soon as SING_STEP closes.  These are functions we don't
    // want re-executing repeatedly while SING_STEP remains closed.

    if (single_shot == 0) {
        // SING_STEP switch was open last we knew, and now it's closed, so

        // set the single-shot flag.
        single_shot = 1;



        // 1. Convert DF switch values to a device number, which
        // we will map to a PDP-8 device type, then attempt to
        // ATTACH some unmounted medium from USB to that device
        //
        // We treat DF == 0 as nothing to mount, since we use
        // SING_STEP for other things, so we need a way to
        // decide which meaning of SING_STEP to take here.
        //
        // The shift by 9 is how many non-DF bits are below
        // DF in switchstatus[1]
        //
        // The bit complement is because closed DF switches show
        // as 0, because they're dragging the pull-up down, but
        // we want those treated as 1s, and vice versa.
        uint16_t css1 = ~switchstatus[1]; 
        int swDevice = (css1 & SS1_DF_ALL) >> 9;
        if (swDevice) {
            char swDevCode[4] = { '\0' };
            switch (swDevice) {
                case 1: strcpy (swDevCode, "ptr"); break; // PTR paper tape reader
                case 2: strcpy (swDevCode, "ptp"); break; // High speed paper tape punch
                case 3: strcpy (swDevCode, "dt0"); break; // TC08 DECtape (#8 is first!)
                case 4: strcpy (swDevCode, "dt1"); break;
                case 5: strcpy (swDevCode, "rx0"); break; // RX8E (8/e peripheral!)
                case 6: strcpy (swDevCode, "rx1"); break;
                case 7: strcpy (swDevCode, "rk1"); break; // second RK05 disk pack
            }
            if (swDevCode[0]) mount_usb_stick_file (swDevice, swDevCode);
        }

        // 2. Do the same with IF, except that the switch value
        // is used to decide which boot script to restart with via
        // SIMH's DO command.
        //
        // The shift value of 6 is because the IF switches are 3
        // down from the DF switches above.
        int swScript = (css1 & SS1_IF_ALL) >> 6;
        if (swScript) {
            printf ("\r\n\nRestarting with IF == %d...\r\n\r\n", swScript);
            insert_scp_cmd = swScript;
            return pft_halt;
        }
    } // end if single-shot flag clear
    else {
        // Now handle the second set of SING_STEP special-function
        // combos, being those where the switches can be pressed in any
        // order, so that we take action when the last one of the set
        // closes, no matter which one that is.  These immediately exit
        // the SIMH instruction interpreter, so they won't re-execute
        // merely because the human isn't fast enough to lift his finger
        // by the time the next iteration of that loop starts.

        // 3. Scan for host poweroff command (Sing_Step + Sing_Inst + Stop)

        if ((switchstatus[2] & (SS2_S_INST | SS2_STOP)) == 0) {
            printf ("\r\nShutdown\r\n\r\n");
            insert_scp_cmd = CMD_EXIT;
            if (spawn_cmd (0, "sudo /bin/systemctl poweroff") != SCPE_OK) {
                printf ("\r\n\r\npoweroff failed\r\n\r\n");
            }
            return pft_halt;
        }

        // 4. Scan for host reboot command (Sing_Step + Sing_Inst + Start)

        if ((switchstatus[2] & (SS2_S_INST | SS2_START)) == 0) {
            printf ("\r\nReboot\r\n\r\n");
            insert_scp_cmd = CMD_EXIT;
            if (spawn_cmd (0, "sudo /bin/systemctl reboot") != SCPE_OK) {
                printf ("\r\n\r\nreboot failed\r\n\r\n");
            }
            return pft_halt;
        }

        #if 0
        // These combos once meant something, but no longer do.  If you
        // reassign them, think carefully whether they should continue to
        // be handled here and not above in the "if" branch.  If nothing

        // prevents your function from being re-executed while SING_STEP
        // remains closed and re-execution would be bad, move the test
        // under the aegis of the single_shot flag.

        // 5. Sing_Step + Sing_Inst + Load Add
        if ((switchstatus[2] & (SS2_S_INST | SS2_L_ADD)) == 0) { }

        // 6. Sing_Step + Sing_Inst + Deposit
        if ((switchstatus[2] & (SS2_S_INST | SS2_DEP)) == 0) { }
        #endif
    }





    return pft_normal;

}


//// handle_flow_control_switches //////////////////////////////////////
// Process all of the PiDP-8/I front panel switches that can affect the
// flow path of the PDP-8 simulator's instruction interpretation loop,
// returning a code telling the simulator our decision.
//
// The simulator passes in pointers to PDP-8 registers we may modify as
// a side effect of handling these switches.

pidp8i_flow_t handle_flow_control_switches (uint16* pM,
    uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF,
    int32 *pDF, int32* pint_req)
{
    // Exit early if the blink thread has not attached itself to the GPIO
    // peripheral in the Pi, since that means we cannot safely interpret the
    // data in the switchstatus array.  This is especially important on
    // non-Pi hosts, since switchstatus will remain zeroed, which we would
    // interpret as "all switches are pressed!", causing havoc.
    //
    // It would be cheaper for our caller to check this for us and skip the
    // call, but there's no good reason to expose such implementations
    // details to it.  We're trying to keep the PDP-8 simulator's CPU core
    // as free of PiDP-8/I details as is practical.
    if (!pidp8i_gpio_present) return pft_normal;

    // Handle the nonstandard SING_STEP + X combos, some of which halt
    // the processor.
    if (handle_sing_step ((switchstatus[2] & SS2_S_STEP) == 0) == pft_halt) {
        return pft_halt;
    }

    // Check for SING_INST switch close...
    extern int swSingInst;
    if (((switchstatus[2] & SS2_S_INST) == 0) && (swSingInst == 0)) {
        // Put the processor in single-instruction mode until we get a
        // CONT or START switch closure.  Technically this is wrong
        // according to DEC's docs: we're supposed to finish executing
        // the next instruction before we "clear the RUN flip-flop" in
        // DEC terms, whereas we're testing these switches before we
        // fetch the next instruction.  Show me how it matters, and
        // I'll fix it. :)
        swSingInst = 1;
    }

    // ...and SING_INST switch open
    extern int swStop;
    if (swSingInst && (switchstatus[2] & SS2_S_INST)) {
        swSingInst = 0;
        swStop = 1;     // still stopped on leaving SING_INST mode
    }

// bug fix oscarv 20240225: start/load_add/dep/exam/cont should only 
// respond in stop mode!
if (swStop)
{

    // Check for START switch press...
    static int swStart = 0;
    if (((switchstatus[2] & SS2_START) == 0) && (swStart == 0)) {
        // Reset the CPU.
        extern DEVICE cpu_dev;
        extern t_stat cpu_reset (DEVICE *);
        cpu_reset (&cpu_dev);

        // DEC's docs say there are a few additional things START does
        // that cpu_reset() doesn't do for us.
        //
        // Don't need to do anything with MA and IR, as SIMH does that
        // shortly after this function returns.
        *pLAC = *pMB = 0;

        // cpu_reset() does its thing to the saved_* register copies
        // in a few cases, but we need it to happen to the "real"
        // registers instead, since our STOP/START behavior doesn't
        // make use of saved_*.
        REG* pibr = find_reg ("IB", NULL, &cpu_dev);





        int32* pIB = pibr ? pibr->loc : 0 /* force segfault on err */ ;
        *pIB = *pIF;

        // Reset our switch flags, too
        swStop = 0;            // START cancels STOP mode
        swSingInst = 0;        // allow SING INST mode re-entry
        swStart = 1;           // make it single-shot


#if 0   // debugging
        printf("\r\nSTART: [DF:%o] [IF:%o] [IB:%o] [PC:%04o] "
                "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                (*pDF >> 12), (*pIF >> 12), (*pIB >> 12), (*pPC & 07777),
                *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif

    }

    // ...and START switch release
    if (swStart && (switchstatus[2] & SS2_START)) {
        swStart = 0;
    }

    // Check for CONT switch press...
    static int swCont = 0;
    extern int resumeFromInstructionLoopExit;
    if ((((switchstatus[2] & SS2_CONT) == 0) && (swCont == 0)) ||
            resumeFromInstructionLoopExit) {
        // The initial CONT press is special: how we handle it
        // depends on the processor's state.
        swCont = 1;                 // make it single-shot
        resumeFromInstructionLoopExit = 0;
        if (swSingInst) {
            // On the initial CONT press while in SING_INST mode, run
            // one instruction only.
            return pft_normal;
        }
        else if (swStop) {
            // We were HLTed or STOPped, so CONT returns us to
            // free-running mode.
            swStop = 0;

#if 0   // debugging
            printf("\r\nCONT: [DF:%o] [IF:%o] [PC:%04o] "
                    "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                    (*pDF >> 12), (*pIF >> 12), (*pPC & 07777),
                    *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif
        }
        // else, CONT has no effect in this state
    }

    // ...and CONT switch release
    if (swCont && (switchstatus[2] & SS2_CONT)) {
        swCont = 0;
    }

    // Check for LOAD_ADD switch press.  The only reason we bother
    // making it single-shot is in case debugging is enabled.
    // Otherwise, it matters not how long the slow human holds this
    // swithc down, and thus how often we apply the values: all else
    // but our printf() here is idempotent.
    static int swLAdd = 0;
    if ((swLAdd == 0) && (switchstatus[2] & SS2_L_ADD) == 0) {
        // Copy SR into PC.  Have to flip the bits because GPIO gives
        // 0 for a closed switch and 1 for open, opposite what we want.
        *pPC = (~switchstatus[0]) & 07777;
                               
        // Copy DF switch settings to DF register
        //
        // The shift is because the DF positions inside the switchstatus[1]
        // register happen to be 3 bit positions off of where we want them
        // in DF here: we want to be able to logically OR PC and DF to make
        // 15-bit data access addresses.
        //
        // We complement the bits here for the same reason we did above
        uint16_t css1 = ~switchstatus[1]; 
        *pDF = (css1 & SS1_DF_ALL) << 3;

        // Do the same for IF.  The only difference comes from the fact
        // that IF is the next 3 bits down in switchstatus[1].
        *pIF = (css1 & SS1_IF_ALL) << 6;


#if 0   // debugging
        printf("\r\nL_ADD: [DF:%o] [IF:%o] [PC:%04o] "
                "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                (*pDF >> 12), (*pIF >> 12), (*pPC & 07777),
                *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif

        swLAdd = 1;                 // make it single-shot
    }

    // ...and L_ADD switch release
    if (swLAdd && (switchstatus[2] & SS2_L_ADD)) {
        swLAdd = 0;
    }


    // Check for DEP switch press...
    static int swDep = 0;
    if (((switchstatus[2] & SS2_DEP) == 0) && (swDep == 0)) {
        uint16 sSR = (~switchstatus[0]) & 07777; // bit flip justified above

        *pPC = *pPC & 07777;  // sometimes high bits get set; squish 'em


#if 0   // debugging
        printf("\r\nDEP: [IF:%o] [PC:%04o] [SR:%04o]",
                (*pIF >> 12), *pPC, sSR);
#endif

        /* ??? in 66 handbook: strictly speaking, SR goes into AC,
           then AC into MB. Does it clear AC afterwards? If not, needs fix */
        pM[*pPC] = sSR;             // FIXME: shouldn't we use IF/DF here?
        *pMB = sSR;
        *pMA = *pPC & 07777;        // MA trails PC on FP; FIXME: OR in IF?
        *pPC = (*pPC + 1) & 07777;  // increment PC

        swDep = 1;                  // make it single-shot
    }

    // ...and DEP switch release
    if (swDep && (switchstatus[2] & SS2_DEP)) {
        swDep = 0;
    }


    // Check for EXAM switch press...
    static int swExam = 0;

    if (((switchstatus[2] & SS2_EXAM) == 0) && (swExam == 0)) {




        *pMB = pM[*pPC];



        *pMA = *pPC & 07777;          // MA trails PC on FP
        *pPC = (*pPC + 1) & 07777;    // increment PC

        swExam = 1;                   // make it single-shot
    }

    // ...and EXAM switch release
    if (swExam && (switchstatus[2] & SS2_EXAM)) {
        swExam = 0;
    }
}
    // Check for STOP switch press.  No "and release" because we get out of





    // STOP mode with START or CONT, not by releasing STOP, and while in








    // STOP mode, this switch's function is idempotent.






















    if (!swStop && ((switchstatus[2] & SS2_STOP) == 0)) {
        swStop = 1;

#if 0   // debugging
            printf("\r\nSTOP: [DF:%o] [IF:%o] [PC:%04o] "
                    "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                    (*pDF >> 12), (*pIF >> 12), (*pPC & 07777),
                    *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif






































    }

    // If any of the above put us into STOP or SING_INST mode, go no
    // further.  In particular, fetch no more instructions, and do not



    // touch PC!  The only way to get un-stuck is CONT or STOP.
    return (swStop || swSingInst) ? pft_stop : pft_normal;
}


//// get_switch_register ///////////////////////////////////////////////
// Return the current contents of the switch register.
//
// The sensed values are backwards due to the GPIO pull-ups, with 1=open







|
<
<
<
<
<
<
|
|
|

|

<
<
<
<
<
<
<
<



|
>
|
|
>
|
|
>
>

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
<
<
<

|
>
|
|
|
|
|
|
|
|

|
>
|
|
|
|
|
|
|
|

|
|
|
|
>
|
|
|

|
|

|
|
|
|
|
>
>
>
>
|
>











|

|











|
|
<
<
<
|
<
|
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
|

<
<
<
|
<


|

















>
>
>
>
>



|
|
<

>







>







<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<



|


|












|





>







>







>



|

>

>






|
|
|

|

>







>



>
|
>
>
>
>
|
>
>
>


>







|
|
>
>
>
>
>
|
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
|
>
>
>
|
|







513
514
515
516
517
518
519
520






521
522
523
524
525
526








527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585





586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658



659

660












661





662
663



664

665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
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
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    }
    else {
        printf ("\r\nNo unmounted %s file found\r\n", devCode);
    }
}


//// handle_sing_step_special_cases //////////////////////////////////////






// This is separate from handle_flow_control_switches because there are
// several non-trivial cases that would obscure the overall flow of the
// calling function to do all this in that function.

static pidp8i_flow_t handle_sing_step_special_cases ()
{








    // There are two sets of SING_STEP combos: first up are those where the
    // other switches involved have to be set already, and the function is
    // triggered as soon as SING_STEP closes.  These are functions we don't
    // want re-executing repeatedly while SING_STEP remains closed. So, the
    // sing_step_single_shot test blocks checking of the once-only combinations
    if (sing_step_single_shot == 0) {

        sing_step_single_shot = 1;                           // make it single shot

        // 1. (Sing_Inst + Cont) + Sing_Step
        //    Sing_Inst and Cont must both be set before setting Sing_Step
        if ((switchstatus[2] & (SS2_S_INST | SS2_CONT)) == 0) {

            // 1a. Convert DF switch values to a device number, which
            // we will map to a PDP-8 device type, then attempt to
            // ATTACH some unmounted medium from USB to that device
            //
            // We treat DF == 0 as nothing to mount, since we use
            // SING_STEP for other things, so we need a way to
            // decide which meaning of SING_STEP to take here.
            //
            // The shift by 9 is how many non-DF bits are below
            // DF in switchstatus[1]
            //
            // The bit complement is because closed DF switches show
            // as 0, because they're dragging the pull-up down, but
            // we want those treated as 1s, and vice versa.
            uint16_t css1 = ~switchstatus[1]; 
            int swDevice = (css1 & SS1_DF_ALL) >> 9;
            if (swDevice) {
                char swDevCode[4] = { '\0' };
                switch (swDevice) {
                    case 1: strcpy (swDevCode, "ptr"); break; // PTR paper tape reader
                    case 2: strcpy (swDevCode, "ptp"); break; // High speed paper tape punch
                    case 3: strcpy (swDevCode, "dt0"); break; // TC08 DECtape (#8 is first!)
                    case 4: strcpy (swDevCode, "dt1"); break;
                    case 5: strcpy (swDevCode, "rx0"); break; // RX8E (8/e peripheral!)
                    case 6: strcpy (swDevCode, "rx1"); break;
                    case 7: strcpy (swDevCode, "rk1"); break; // second RK05 disk pack
                }
                if (swDevCode[0]) mount_usb_stick_file (swDevice, swDevCode);
            }

            // 1b. Do the same with IF, except that the switch value
            // is used to decide which boot script to restart with via
            // SIMH's DO command.
            //
            // The shift value of 6 is because the IF switches are 3
            // down from the DF switches above.
            int swScript = (css1 & SS1_IF_ALL) >> 6;
            if (swScript) {
                printf ("\r\n\nRestarting with IF == %d...\r\n\r\n", swScript);
                insert_scp_cmd = swScript;
                return pft_exit;
            }

        } // end of (Sing_Inst) + Cont + Sing_Step

    } // end of single-shot == 0 flag






    // 2. Scan for host poweroff command (Sing_Inst + Stop) + Sing_Step
    //    Sing_Inst and Stop must both be set before setting Sing_Step
    if ((switchstatus[2] & (SS2_S_INST | SS2_STOP)) == 0) {
        printf ("\r\nShutdown\r\n\r\n");
        insert_scp_cmd = CMD_EXIT;
        if (spawn_cmd (0, "sudo /bin/systemctl poweroff") != SCPE_OK) {
            printf ("\r\n\r\npoweroff failed\r\n\r\n");
        }
        return pft_exit;
    }

    // 3. Scan for host reboot command (Sing_Inst + Start ) + Sing_Step
    //    Sing_Inst and Start must both be set before setting Sing_Step
    if ((switchstatus[2] & (SS2_S_INST | SS2_START)) == 0) {
        printf ("\r\nReboot\r\n\r\n");
        insert_scp_cmd = CMD_EXIT;
        if (spawn_cmd (0, "sudo /bin/systemctl reboot") != SCPE_OK) {
            printf ("\r\n\r\nreboot failed\r\n\r\n");
        }
        return pft_exit;
    }

    #if 0
    // These combos once meant something, but no longer do. They can
    // be reactivated if useful. If you reassign them, think carefully
    // whether they should continue to be handled here and not above
    // in the "if (..._single_shot == 0)" branch. If nothing prevents your
    // function from being re-executed while SING_STEP remains closed
    // and re-execution would be bad, move the test under the aegis
    // of the single_shot flag.

    // 4. Sing_Step + Sing_Inst + Load Add
    if ((switchstatus[2] & (SS2_S_INST | SS2_L_ADD)) == 0) { }

    // 5. Sing_Step + Sing_Inst + Deposit
    if ((switchstatus[2] & (SS2_S_INST | SS2_DEP)) == 0) { }
    #endif

    // We would not be in this function without SingInst being set in the
    // first place, so it's safe to default to returning pft_stopped. But
    // this is really only to distinguish it from the pft_exit that gets
    // returned on PiDP-8/I OS reboot via IF switches, Pi reboot, and
    // Pi poweroff
    return pft_stopped;

}


//// handle_flow_control_switches //////////////////////////////////////
// Process all of the PiDP-8/I front panel switches that can affect the
// flow path of the PDP-8 simulator's instruction interpretation loop,
// returning a code telling the simulator our decision.
//
// The simulator passes in pointers to PDP-8 registers we may modify as
// a side effect of handling these switches.

pidp8i_flow_t handle_flow_control_switches (uint16* pM, uint16 memcapacity,
    uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF,
    int32 *pDF, int16* pnext_Major_State, int32* pint_req)
{
    // Exit early if the blink thread has not attached itself to the GPIO
    // peripheral in the Pi, since that means we cannot safely interpret the
    // data in the switchstatus array.  This is especially important on
    // non-Pi hosts, since switchstatus will remain zeroed, which we would
    // interpret as "all switches are pressed!", causing havoc.
    //
    // It would be cheaper for our caller to check this for us and skip the
    // call, but there's no good reason to expose such implementations
    // details to it.  We're trying to keep the PDP-8 simulator's CPU core
    // as free of PiDP-8/I details as is practical.
    if (!pidp8i_gpio_present)
        if (cpuRun == 1)



            return pft_running;

        else












            return pft_stopped;












    // Check for START switch press...
    static int swStart = 0;
    if (((switchstatus[2] & SS2_START) == 0) && (swStart == 0) && (cpuRun == 0)) {
        // Reset the CPU.
        extern DEVICE cpu_dev;
        extern t_stat cpu_reset (DEVICE *);
        cpu_reset (&cpu_dev);

        // DEC's docs say there are a few additional things START does
        // that cpu_reset() doesn't do for us.
        //
        // Don't need to do anything with MA and IR, as SIMH does that
        // shortly after this function returns.
        *pLAC = *pMB = 0;

        // cpu_reset() does its thing to the saved_* register copies
        // in a few cases, but we need it to happen to the "real"
        // registers instead, since our STOP/START behavior doesn't
        // make use of saved_*.
        REG* pibr = find_reg ("IB", NULL, &cpu_dev);

        // TBD FIX, it appears that the following 2 lines lead to *pIB being
        // null but not crashing on *pIB being a null pointer. This is why
        // there needs to be the "if (IB == -1)" TSS-8 patch in pdp8_cpu.c
        // When these two lines are resolved, the TSS-8 patch should be removed
        int32* pIB = pibr ? pibr->loc : 0 /* force segfault on err */ ;
        *pIB = *pIF;

        *pnext_Major_State = 1;// START always begins with Fetch state
        cpuRun = 1;

        swStart = 1;           // make it single-shot
        return pft_go;

#if 0   // debugging
        printf("\r\nSTART: [DF:%o] [IF:%o] [IB:%o] [PC:%04o] "
                "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                (*pDF >> 12), (*pIF >> 12), (*pIB >> 12), (*pPC & 07777),
                *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif

    }

    // ...and START switch release
    if (swStart && (switchstatus[2] & SS2_START)) {
        swStart = 0;
    }



































    // Check for LOAD_ADD switch press.  The only reason we bother
    // making it single-shot is in case debugging is enabled.
    // Otherwise, it matters not how long the slow human holds this
    // switch down, and thus how often we apply the values: all else
    // but our printf() here is idempotent.
    static int swLAdd = 0;
    if (((switchstatus[2] & SS2_L_ADD) == 0) && (swLAdd == 0) && (cpuRun == 0)) {
        // Copy SR into PC.  Have to flip the bits because GPIO gives
        // 0 for a closed switch and 1 for open, opposite what we want.
        *pPC = (~switchstatus[0]) & 07777;
                               
        // Copy DF switch settings to DF register
        //
        // The shift is because the DF positions inside the switchstatus[1]
        // register happen to be 3 bit positions off of where we want them
        // in DF here: we want to be able to logically OR PC and DF to make
        // 15-bit data access addresses.
        //
        // We complement the bits here for the same reason we did above
        uint16_t css1 = ~switchstatus[1];
        *pDF = (css1 & SS1_DF_ALL) << 3;

        // Do the same for IF.  The only difference comes from the fact
        // that IF is the next 3 bits down in switchstatus[1].
        *pIF = (css1 & SS1_IF_ALL) << 6;
        *pnext_Major_State = 1;     // LOAD_ADD sets major state to Fetch

#if 0   // debugging
        printf("\r\nL_ADD: [DF:%o] [IF:%o] [PC:%04o] "
                "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                (*pDF >> 12), (*pIF >> 12), (*pPC & 07777),
                *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif

        swLAdd = 1;                 // make it single-shot
    }

    // ...and L_ADD switch release
    if (swLAdd && (switchstatus[2] & SS2_L_ADD)) {
        swLAdd = 0;
    }


    // Check for DEP switch press...
    static int swDep = 0;
    if (((switchstatus[2] & SS2_DEP) == 0) && (swDep == 0) && (cpuRun == 0)) {
        uint16 sSR = (~switchstatus[0]) & 07777; // bit flip justified above
        uint32 effaddr;
        *pPC = *pPC & 07777;  // sometimes high bits get set; squish 'em
        effaddr= *pPC | *pIF;

#if 0   // debugging
        printf("\r\nDEP: [IF:%o] [PC:%04o] [SR:%04o]",
                (*pIF >> 12), *pPC, sSR);
#endif

        if (effaddr < memcapacity) {
            pM[effaddr] = sSR;      // if deposit is outside phys. memory 
        }                           // don't write but otherwise proceed 
        *pMB = sSR;
        *pMA = *pPC & 07777;        // MA trails PC 
        *pPC = (*pPC + 1) & 07777;  // increment PC
        *pnext_Major_State = 1;     // DEP sets major state to Fetch
        swDep = 1;                  // make it single-shot
    }

    // ...and DEP switch release
    if (swDep && (switchstatus[2] & SS2_DEP)) {
        swDep = 0;
    }


    // Check for EXAM switch press...
    static int swExam = 0;

    if (((switchstatus[2] & SS2_EXAM) == 0) && (swExam == 0) && (cpuRun == 0)) {
        uint32 effaddr;
        *pPC = *pPC & 07777;  // sometimes high bits get set; squish 'em
        effaddr= *pPC | *pIF;
        if (effaddr < memcapacity) { 
            *pMB = pM[effaddr];
        } else {
            *pMB = 0;
        }
        *pMA = *pPC & 07777;          // MA trails PC on FP
        *pPC = (*pPC + 1) & 07777;    // increment PC
        *pnext_Major_State = 1;       // EXAM sets major state to Fetch
        swExam = 1;                   // make it single-shot
    }

    // ...and EXAM switch release
    if (swExam && (switchstatus[2] & SS2_EXAM)) {
        swExam = 0;
    }


    // Check for CONT switch press...
    static int swCont = 0;
    extern int resumeFromInstructionLoopExit;
    if (((switchstatus[2] & SS2_CONT) == 0) && (swCont == 0) && (cpuRun == 0) ||
            resumeFromInstructionLoopExit) {

#if 0
TBD--Is this still true? CONT should not be special any more so it should be
safe to delete these comments. Also MB is (presumably) being correctly handled
        // The initial CONT press is special: how we handle it
        // depends on the processor's state.
        //
        // FIXME: Are we handling MB correctly? [973271ae36]
#endif

        resumeFromInstructionLoopExit = 0;
        cpuRun = 1;
        swCont = 1;                 // make it single-shot
        return pft_go;

#if 0   // debugging
            printf("\r\nCONT: [DF:%o] [IF:%o] [PC:%04o] "
                    "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                    (*pDF >> 12), (*pIF >> 12), (*pPC & 07777),
                    *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif

    }

    // ...and CONT switch release
    if (swCont && (switchstatus[2] & SS2_CONT)) {
        swCont = 0;
    }


    static int swStop = 0;
    // Check for STOP switch press.
    if (((switchstatus[2] & SS2_STOP) == 0)) {
        swStop = 1;

#if 0   // debugging
            printf("\r\nSTOP: [DF:%o] [IF:%o] [PC:%04o] "
                    "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]",
                    (*pDF >> 12), (*pIF >> 12), (*pPC & 07777),
                    *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777);
#endif

        if (cpuRun == 1) {
            cpuRun = 0;
        }
    }

    // ...and STOP switch release
    if (swStop && (switchstatus[2] & SS2_CONT)) {
        swStop = 0;
    }


    // Check for SING_STEP switch close...
    if (((switchstatus[2] & SS2_S_STEP) == 0) && (swSingStep == 0)) {
        swSingStep = 1;
        pidp8i_flow_t result = handle_sing_step_special_cases ();
        if (result == pft_exit)
            return result;
        cpuRun = 0;
    }


    // ...and SING_STEP switch open
    if (swSingStep == 1 && (switchstatus[2] & SS2_S_STEP)) {
        swSingStep = 0;
        sing_step_single_shot = 0;
    }


    // Check for SING_INST switch close...
    if (((switchstatus[2] & SS2_S_INST) == 0) && (swSingInst == 0)) {
        swSingInst = 1;
        cpuRun = 0;
    }

    // ...and SING_INST switch open
    if (swSingInst && (switchstatus[2] & SS2_S_INST)) {
        swSingInst = 0;
    }


if (cpuRun == 1)
    return pft_running;
else
    return pft_stopped;


}


//// get_switch_register ///////////////////////////////////////////////
// Return the current contents of the switch register.
//
// The sensed values are backwards due to the GPIO pull-ups, with 1=open

Changes to src/pidp8i/pidp8i.h.

1
2
3
4



5
6
7
8
9
10
11
/* pidp8i.h: Interface between PiDP-8/I additions and the stock SIMH PDP-8
   simulator

   Copyright © 2015-2017 by Oscar Vermeulen and Warren Young




   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:




>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* pidp8i.h: Interface between PiDP-8/I additions and the stock SIMH PDP-8
   simulator

   Copyright © 2015-2017 by Oscar Vermeulen and Warren Young

   Portions copyright © 2021 by HB Eggenstein
                      © 2021 by Steve Tockey

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:
32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

#include "gpio-common.h"

#include <stdint.h>
#include <stdlib.h>

typedef enum {
    pft_normal,
    pft_halt,

    pft_stop,
} pidp8i_flow_t;

extern char *build_pidp8i_scp_cmd (char* cbuf, size_t cbufsize); 

extern int32_t get_switch_register (void);
extern size_t get_pidp8i_initial_max_skips (size_t updates_per_sec);

extern pidp8i_flow_t handle_flow_control_switches (uint16_t* pM,
        uint32_t *pPC, uint32_t *pMA, int32_t *pMB, int32_t *pLAC, int32_t *pIF,
        int32_t *pDF, int32_t* pint_req);

extern void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint32_t sMB,
        uint16_t sIR, int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF,
        int32_t sSC, int32_t int_req, int Pause);

#endif // !defined(PIDP8I_H)







|
|
>
|


|




|
|
|



|


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

#include "gpio-common.h"

#include <stdint.h>
#include <stdlib.h>

typedef enum {
    pft_stopped,        /* the cpu is not running */
    pft_running,        /* the cpu is running */
    pft_go,             /* the cpu is (re-) starting from being stopped */
    pft_exit            /* exit(ing) SIMH */
} pidp8i_flow_t;

extern char *build_pidp8i_scp_cmd (char* cbuf, size_t cbufsize);

extern int32_t get_switch_register (void);
extern size_t get_pidp8i_initial_max_skips (size_t updates_per_sec);

extern pidp8i_flow_t handle_flow_control_switches (uint16_t* pM, uint16_t memcapacity,
         uint32_t *pPC, uint32_t *pMA, int32_t *pMB, int32_t *pLAC, int32_t *pIF,
         int32_t *pDF, int16_t* pMajor_State, int32_t* pint_req);

extern void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint32_t sMB,
        uint16_t sIR, int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF,
        int32_t sSC, int32_t int_req, uint16_t Major_State, int Pause);

#endif // !defined(PIDP8I_H)