Inverting the deposit switch in software

261 views
Skip to first unread message

Sven Moritz Hallberg

unread,
Jul 31, 2025, 2:29:42 PM7/31/25
to PiDP-8
Hi all,

I made a post to the fossil forum on tangentsoft.com some months ago, but it has been stuck "awaiting moderator approval"...

I don't normally use this gmail account - is there any way to participate in this group with a regular email address?

Anyhow, below is a copy of my post:

I recently got the PiDP-8 kit and it's awesome!

However, I did not realize during the build that the Deposit switch is mounted in the opposite orientation on the real thing. Of course I had to turn it around! A stressful (but successful) bit of desoldering ensued...

Anyway, I patched the software to invert the signal, selected with a configure flag (--invert-dep).

I chose to introduce the inversion in the function report_ss() because it forms a bottleneck between the lower-level GPIO handling and its higher-level "user" code, so both sides can remain otherwise oblivious to this. The function is only called when the switch state actually changes (after debounce) so the extra code does not run on every scan.

Patch against Fossil trunk [3fee766e68] below. Tested with pidp8i-test.

Regards, pesco

PS: With this (not requiring a hardware mod), maybe a note could be placed in the build instructions about the possibility. It would have saved me some rework. ;)

invdep.patch

William Cattey

unread,
Oct 25, 2025, 5:49:37 PM10/25/25
to PiDP-8
Hello Sven,

My profuse apologies for taking so long to respond usefully to your contribution.

I've looked at your patch, and it is clear that you put in the extra work to understand how to add an option through auto.def and that you did it correctly.  I also like where you placed the code to perform the inversion.

I have just checked in your patched version to a branch called "invert-deposit".

I'm now pondering what kind of additional testing might be needed to feel totally safe pulling this change into trunk.

I agree that additional documentation should be made. I'll start with adding a description of this new option to the top level README.md in the branch.

Would you please do a build of the invert-deposit branch and confirm that your changes work. (I.E. that I've correctly taken them.)

-Bill 

William Cattey

unread,
Nov 9, 2025, 2:14:00 PM11/9/25
to PiDP-8
I see I should have addressed my reply to Pesco. Sorry.

I've done a build with the invert deposit logic enabled on my PiDP-8.  I wondered, "What would happen if that option were chosen on a machine that did not have the hardware mod?"

Alas, I encountered what I believe is a show-stopper.

At first things seemed fine.  The Deposit switch remained inactive when I depressed it, and then when I released it, the deposit was successful. I did that a couple times to confirm all was well. Indeed all seemed well.

I then restarted OS/8 with the traditional, Load Address 7600 / Start.  I saw the monitor dot prompt and figured, "We're good."  Alas, the system was now hung.  Both my terminal windows into my Pi went unresponsive, and then timed out.  I had to power-cycle the machine to get it back.

So there's something toxic going on here that requires further investigation.

I'm quite hesitant to dig into this because I believe that every non-graceful power off is a non-zero chance of corrupting my system image.

Pesco, Oscar, we need to collaborate on this if we're gong to make a go of this for the next release.

-Bill

Sven M. Hallberg

unread,
Dec 3, 2025, 12:24:16 PM12/3/25
to William Cattey, PiDP-8
William Cattey on Sun, Nov 09 2025:
> I've done a build with the invert deposit logic enabled on my PiDP-8. I
> wondered, "What would happen if that option were chosen on a machine that
> did not have the hardware mod?"

I think the answer should be "the same as in the following scenario:"

- original software (no invert-dep)
- unmodified circuit board
- switch soldered in the "authentic" orientation

I.e. the switch looks permanently engaged, unless you press it which
momentarily disengages it. Is that the behavior you were seeing?

> Alas, I encountered what I believe is a show-stopper.

I'm not sure I follow. The config option is only meant for the situation
where someone has soldered the switch in "inverted" orientation
(relative to Oscar's instructions).

So one wouldn't enable this by default. It's for tinkerers who don't
mind compiling their own software but would rather not cut a trace on
the board.

Or am I misunderstanding?

An obvious improvement would be a run-time (rather than compile-time)
switch, I guess. However, I wanted to start with something simpler to
implement that's already useful. I judged the trade-off that one needs
to rebuild the software acceptable.

> Pesco, Oscar, we need to collaborate on this if we're gong to make a go of
> this for the next release.

>> I agree that additional documentation should be made. I'll start with
>> adding a description of this new option to the top level README.md in the
>> branch.

I was thinking of a careful hint in the build instructions that this
option exists. It would have saved me a round of resoldering.

-pesco

PS: I tried the invert-deposit branch and the build worked fine. I
booted into TSS/8, logged in and started BASIC without issue. My
blinkenlights go blink.

William Cattey

unread,
Jan 4, 2026, 10:27:08 PMJan 4
to Sven M. Hallberg, PiDP-8

Hello pesco,

My profuse apologies for the delay in replying. I've been swamped with non-PiDP-8 stuff through most of December. Let me clarify, and see if we can move this conversation forward.

First let me see if I can give you a more complete understanding of the testing I did, what I had hoped for as the outcome, what my actual results were, and why I think that it's a problem.

I would very much like to incorporate either your patch, or one with equivalent functionality into the PiDP-8 release. I agree with your initial principle that it would be ideal if a simple software change would enable builders of the PiDP-8 to just "solder the deposit switch in upside-down, and set the configuration option to use it.

The only platform I have available for testing is my own PiDP-8, where I cut a trace, put in a bodge-wire, and soldered in the deposit switch upside down with the regular software.

In your reply you say:

So one wouldn't enable this by default. It's for tinkerers who don't
mind compiling their own software but would rather not cut a trace on
the board.

What would you think of a new default? Change the instructions to tell people to solder in the switch upside-down and use the "invert-deposit" version of the software?

I'd hoped to offer that as a more mainstream option. But when I tried the invert-deposit option on my test host, it initially seemed benign. I.E. if someone with a regular build ran that option nothing bad would happen. Unfortunately, on one of my test runs, the whole system hung, and I had to power cycle to get my system back. I believe that, when one un-gracefully power-cycles a Pi, it's a roll of the dice as to whether the system image gets corrupted. I got my system back but have been afraid to do more testing ever since.

I don't like the option of, "If you want to tinker, and solder in the deposit switch upside, down and compile with this option enabled it will work, but if you run this option on a stock system it will hang requiring a power cycle, which may corrupt your system image requiring a start agin from scratch."

This is why I called for additional collaboration.

As you said in your reply:

I.e. the switch looks permanently engaged, unless you press it which
momentarily disengages it.

Apparently that state of "permanently engaged" can result in the whole Raspbery Pi OS being out in lala land, unable to be interrupted or shut down cleanly.

I'd like to understand how that happens, and add code to prevent such a hangup. Then if someone tries out the option, the risk of catastrophe is eliminated.

@Oscar, @Steve Tokey, or anybody who understands the gpio code better than I, what made the system hangup, and can it be prevented?

-Bill

Steve Tockey

unread,
Jan 6, 2026, 5:53:30 PMJan 6
to William Cattey, Sven M. Hallberg, PiDP-8

Bill,
Sorry but I don't understand GPIO code itself very well. I don't think I can offer any help on this.


-- steve



--
You received this message because you are subscribed to the Google Groups "PiDP-8" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pidp-8+un...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/pidp-8/3c6eeee1-ea2b-4366-8661-e2ef3dd38563%40gmail.com.

Sven M. Hallberg

unread,
Jan 7, 2026, 6:02:37 AMJan 7
to William Cattey, PiDP-8
William Cattey on Sun, Jan 04 2026:
> What would you think of a new default? Change the instructions to tell
> people to solder in the switch upside-down and use the "invert-deposit"
> version of the software?

If I understand correctly, Oscar made the deliberate choice to have all
switches in the same orientation because it makes the instructions
simpler. A trade-off between authenticity and ease of building. I worked
on the assumption that "easier building" would remain the default.
Whether to change that or not is an orthogonal question in my mind.


> I'd hoped to offer that as a more mainstream option. But when I tried
> the invert-deposit option on my test host, it initially seemed benign.
> [...]
> I don't like the option of, "[...] but if you run this option on a
> stock system it will hang [...]"

Any such effects would also be seen when someone decided to solder the
switch upside-down and ran the stock software. Nothing in the
instructions implies or should imply that one could do this and expect
things to work.

My proposal is to keep everything as is (staying with the easy-to-build
way of all switches in the same orientation) but to just add a note to
the build instructions that says (in effect) "actually, this is not
quite authentic - if you would like it proper, you can solder Dep in
upside-down but must then adapt the software by building it with
--invert-dep."

Or are you saying even this will create a significant incidence of
people doing it wrong despite instructions to the contrary?
A remedy I could think of would be to put a time-out on Dep, i.e. to
internally force the signal low if it remains engaged for more than,
say, a second or something. I don't know how much complexity that would
be and to me it feels like wasted effort, but that might just be me.


> Apparently that state of "permanently engaged" can result in the whole
> Raspbery Pi OS being out in lala land, unable to be interrupted or shut
> down cleanly.

That seems strange. Even if simh hung itself up somehow when faced with
a permanently engaged Deposit switch, how could that bring the OS down?

Steve Tockey

unread,
Jan 7, 2026, 4:12:54 PMJan 7
to Sven M. Hallberg, William Cattey, PiDP-8

Sven and all,
Thinking about this a bit more ...

Sven wrote: "If I understand correctly, Oscar made the deliberate choice to have all switches in the same orientation because it makes the instructions simpler. A trade-off between authenticity and ease of building. I worked on the assumption that "easier building" would remain the default."

That is my understanding as well. Oscar has said--more than once, I believe--that simplicity of build is usually more important than fine-grained authenticity. I would take any action moving forward around decisions on this topic with the mentality that if someone truly wants the fine-grained authenticity then they must be willing to take on some additional level of complexity in building & setting up their PiDP-8/i.

Sven wrote: "Any such effects would also be seen when someone decided to solder the switch upside-down and ran the stock software. Nothing in the instructions implies or should imply that one could do this and expect things to work."

It's true that nothing in the instructions implies anything of this sort. It's only my own many, many years of PDP-8 experience that led me to want to flip the DEP switch. And I took it upon myself to make it happen (without any software change). I would actually prefer if the instructions did mention that there is an at-your-own-risk option here and provide a pointer to where instructions to make the change existed. I've written up the hardware version (cut one trace, solder in a jumper) of it in this discussion group elsewhere, so it could be as easy as cut-and-paste.

Sven wrote: "My proposal is to keep everything as is (staying with the easy-to-build way of all switches in the same orientation) but to just add a note to the build instructions that says (in effect) "actually, this is not quite authentic - if you would like it proper, you can solder Dep in upside-down but must then adapt the software by building it with --invert-dep.""

I'm not sure that a software-only solution should be the only option offered to the builder. The hardware fix is just as effective and leaves the software untouched.

Sven wrote, "That seems strange. Even if simh hung itself up somehow when faced with a permanently engaged Deposit switch, how could that bring the OS down?"

This is where I've really been puzzled. The state of the IF, DF, and SR switches are completely up to the PiDP-8/i user, and can be left on or off long-term. There should not be any issue--at least theoretically--with leaving any front panel switch in a normally-on vs. normally-off configuration. The other thing, which I would have to check in more detail, is that isn't the number of switches on the front panel larger than the number of input data pins on the GPIO? Aren't the inputs multiplexed in the same way that the outputs to the LEDs are? If so, then whatever input pin is co-multiplexed to the same one DEP uses should cause the same crash? If not, then the cause of the crash has to be farther along in the SIMH processing than just the raw GPIO-level?


-- steve


Bob Darlington

unread,
Jan 7, 2026, 4:17:50 PMJan 7
to Steve Tockey, Sven M. Hallberg, William Cattey, PiDP-8
I flipped my switch over, cut a trace, and ran a bodge wire.    It wasn't a big deal, just wish I caught it before soldering the switch in the first time around.

-Bob

--
You received this message because you are subscribed to the Google Groups "PiDP-8" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pidp-8+un...@googlegroups.com.

Sven M. Hallberg

unread,
Jan 9, 2026, 7:37:10 AMJan 9
to Steve Tockey, William Cattey, PiDP-8
Steve Tockey on Wed, Jan 07 2026:
> I'm not sure that a software-only solution should be the only option
> offered to the builder.

Pointing to both options is of course fair. My personal preference was
for a software solution because...

> The hardware fix is just as effective and leaves the software untouched.

... this sentence works just as well with the words "hardware" and
"software" swapped. :)

William Cattey

unread,
Jan 31, 2026, 11:16:42 AMJan 31
to PiDP-8
To the question, "What sort of support should we have for deposit invert?" I will remind people that, for a long time, there was the "Serial Port Mod" See: https://obsolescence.wixsite.com/obsolescence/pidp-8-serial-modification

It turns out that a more general solution evolved, but there is precedent to document other options.

My take-away from the "Serial Port Mod" experience is that additional options are good to offer, and they evolve over time.

I would like to keep this thread focused more on the question of "How to offer the best evolution of the inverted deposit switch option."

-Bill

William Cattey

unread,
Feb 1, 2026, 4:42:09 PMFeb 1
to PiDP-8
It seemed like I was going to have to dig in and become an expert on gpio operation.  Instead I decided to throw the problem over to claude.ai and see if it could sort it out.  It produced a credible analysis of root cause and an alternate implementation that I have tested.

Below is a verbatim paste-in is what claude.ai had to say.  I'd be grateful if others would review the analysis and see if it seems correct.

Originally, Pasco explicitly put the inversion in the report_ss instead of the debounce code, saying that it seemed best to change it as late as possible. However it seems that the change needs to be as early as possible, otherwise there is a conflict between the internal and external state of the switches.

Pasco, after reading the analysis, does this change seem correct to you?

Others: I'd be grateful for additional review.

-Bill

---- Begin paste-in from claude.ai ----
PiDP-8/I DEP Switch Inversion Bug - Root Cause Analysis
========================================================
Bill Cattey and Claude.ai, February 2026

PROBLEM SUMMARY
===============

When the --invert-dep configuration option is enabled, the system hangs during
OS/8 restart (Load Address 7600 / Start). The system becomes completely
unresponsive, requiring a power cycle to recover.

Initial testing showed the DEP switch working correctly in isolation, but the
hang occurs specifically during the OS/8 boot sequence.


ROOT CAUSE IDENTIFIED
=====================

The bug is NOT a race condition or timing issue. It's a **permanent state
mismatch** between internal and external switch state variables caused by
inverting the switch value at the wrong point in the code flow.

The State Mismatch
------------------

The original patch placed the inversion in report_ss():

    static void report_ss(int row, int col, int ss, struct switch_state* pss)
    {
        pss->stable_state = ss;        // Line 1: Store BEFORE inversion
        pss->last_change = na_ms;

        int mask = 1 << col;
    #ifdef INVERT_DEP
        if (row == 2 && mask == SS2_DEP)
            ss = !ss;                  // Line 2: INVERT here
    #endif
        if (ss) switchstatus[row] |=  mask;   // Line 3: Store AFTER inversion
        else    switchstatus[row] &= ~mask;
    }

This creates a critical problem:
1. **Line 1**: gss[].stable_state is set to the NON-inverted value
2. **Line 2**: ss is inverted
3. **Line 3**: switchstatus[] is set to the INVERTED value

Result: The two state variables DISAGREE and never get corrected.


Demonstrating the Bug
---------------------

Testing with debug output on an UNMODIFIED board (DEP in standard orientation)
with --invert-dep enabled shows the mismatch:

    BEFORE THE FIX:
    DEP: count=1000, gpio_ss=1, gss_stable=1, switchstatus=0
   
    gpio_ss=1       <- GPIO reads DEP as open (physically open)
    gss_stable=1    <- Internal state thinks it's open
    switchstatus=0  <- External state reports it as CLOSED
   
    MISMATCH! Internal and external state disagree.


Why the Mismatch Never Corrects
--------------------------------

The debounce_switch() function is called on every GPIO scan for every switch:

    static void debounce_switch(int row, int col, int ss, ms_time_t now_ms)
    {
        struct switch_state* pss = &gss[row][col];

        if (!gss_initted) {
            // First time: calls report_ss(), creates the mismatch
            report_ss(row, col, ss, pss);
        }
        else if (ss == pss->stable_state) {
            // Subsequent scans: ss=1, stable_state=1, they MATCH!
            // Just resets timer and returns, never calls report_ss() again
            pss->last_change = na_ms;
            return;
        }
        // ... other cases for actual switch changes
    }

After initialization:
1. GPIO reads DEP = 1 (physically open)
2. gss[].stable_state = 1 (stored before inversion)
3. Condition "ss == pss->stable_state" is TRUE (1 == 1)
4. Code takes the "line 372" path: just resets timer and returns
5. report_ss() is NEVER called again
6. switchstatus[] stays frozen at 0 (inverted value from initialization)

**The mismatch is permanent for the entire session.**


Why This Causes the System Hang
--------------------------------

With switchstatus[] showing DEP as permanently closed (0):

1. SIMH thinks the DEP switch is constantly pressed
2. During OS/8 boot, this can:
   - Trigger spurious deposit operations at critical moments
   - Block boot sequences waiting for DEP to be released
   - Corrupt initialization of critical data structures
3. The "hang" is actually SIMH responding to what it thinks is a held-down
   DEP switch during the sensitive boot sequence


THE SOLUTION
============

Move the inversion to debounce_switch() BEFORE any state management:

    static void debounce_switch(int row, int col, int ss, ms_time_t now_ms)
    {
        struct switch_state* pss = &gss[row][col];

    #ifdef INVERT_DEP
        // Invert the raw GPIO value BEFORE any processing
        // This ensures BOTH gss[].stable_state AND switchstatus[]
        // see the same inverted value
        if (row == 2 && (1 << col) == SS2_DEP) {
            ss = !ss;
        }
    #endif

        if (!gss_initted) {
            report_ss(row, col, ss, pss);  // Now stores inverted value in BOTH places
        }
        else if (ss == pss->stable_state) {
            pss->last_change = na_ms;
        }
        // ... rest of function
    }


Why This Works
--------------

Now the inversion happens at the RAW GPIO read level, before any state
management:

1. GPIO reads DEP = 1 (physically open)
2. **Inversion happens immediately**: ss = !1 = 0
3. debounce_switch() processes ss=0
4. report_ss() is called with ss=0
5. Sets gss[].stable_state = 0
6. Sets switchstatus[] = 0
7. **BOTH state variables agree: DEP is closed (inverted from physically open)**

This consistency is maintained for:
- Initial GPIO scan during initialization
- All subsequent scans during normal operation
- Actual switch state changes (debouncing logic sees consistent values)


VERIFICATION
============

Testing with debug output after the fix shows consistency:

    AFTER THE FIX:
    DEP: count=1000, gpio_ss=1, gss_stable=0, switchstatus=0
    DEP(after):  inverted_ss=0
   
    gpio_ss=1       <- GPIO reads DEP as open (physically open)
    inverted_ss=0   <- Inversion happens immediately
    gss_stable=0    <- Internal state: inverted to closed
    switchstatus=0  <- External state: inverted to closed
   
    ALL THREE AGREE! State is consistent.


Functional Testing
------------------

With the fix in place:

1. ✅ OS/8 boots successfully to monitor dot prompt
2. ✅ No hang during initial boot
3. ✅ No hang during restart (boot rk0 from sim> prompt)
4. ✅ System remains responsive to Ctrl-E
5. ✅ Can cleanly quit simulator
6. ✅ State consistency maintained throughout all operations


THE CODE CHANGES
=================

File: src/pidp8i/gpio-common.c.in

Change 1: Remove from report_ss()
----------------------------------

REMOVE the entire #ifdef INVERT_DEP block, leaving:

    static void report_ss(int row, int col, int ss,
            struct switch_state* pss)
    {
        pss->stable_state = ss;
        pss->last_change = na_ms;

        int mask = 1 << col;
        if (ss) switchstatus[row] |=  mask;
        else    switchstatus[row] &= ~mask;

        #ifdef DEBUG
            printf("%cSS[%d][%02d] = %d  ", gss_initted ? 'N' : 'I', row, col, ss);
        #endif
    }


Change 2: Add to debounce_switch()
-----------------------------------

ADD the inversion block at the beginning, right after the pss declaration:

    static void debounce_switch(int row, int col, int ss, ms_time_t now_ms)
    {
        struct switch_state* pss = &gss[row][col];

    #ifdef INVERT_DEP
        // If the DEP(osit) switch is mounted in reverse orientation, i.e.
        // active "up" as on a real PDP-8/I, invert the raw GPIO value here
        // BEFORE any debouncing or state management. This ensures both
        // gss[].stable_state and switchstatus[] see the same inverted value.
        if (row == 2 && (1 << col) == SS2_DEP) {
            ss = !ss;
        }
    #endif

        if (!gss_initted) {
            // First time thru, so set this switch's module-global and
            // exported state to its defaults now that we know the switch's
            // initial state.
            report_ss(row, col, ss, pss);
        }
        // ... rest of function unchanged


SUMMARY
=======

The fix is minimal: move 6 lines of code from one function to another.

The impact is significant: it eliminates a permanent state mismatch that
caused the system to think DEP was constantly pressed, leading to hangs
during OS/8 boot.

The key insight: State transformations must happen at the raw input level,
BEFORE any state management, to ensure consistency across all code paths.


ATTRIBUTION
===========

Original --invert-dep patch by: Pesco
Root cause analysis and fix: Co-authored by Bill Cattey and Claude.ai
Testing: Bill Cattey


TESTING NOTES
=============

The testing was performed on an UNMODIFIED PiDP-8 board (DEP switch in
standard orientation, active-down) with --invert-dep enabled. This "reverse
polarity" test proves:

1. The state consistency bug exists with the original patch placement
2. The fix resolves the state consistency issue
3. The inversion logic works correctly (just backwards on unmodified hardware)

For users with modified boards (DEP installed upside-down, active-up), the
fix should work correctly with --invert-dep enabled, providing the authentic
PDP-8/I switch orientation.

Note on Misconfiguration Behavior
----------------------------------

When --invert-dep is enabled on unmodified hardware (reverse polarity test),
the debouncing logic operates on an inverted signal. This can cause switch
bounce artifacts. For example, during a front panel test sequence:

    Load Address 200; Examine; Load Address 200; Set SR to 7000; Deposit

The deposit didn't occur until the DEP switch was released (physically opened,
seen as closed after inversion). During release, the switch bounced and
deposited the value to location 201 instead of 200.

This is expected behavior when debouncing an inverted signal - the debounce
logic sees transitions at opposite edges from the physical switch operation.
While this demonstrates why --invert-dep should only be used with modified
hardware, it's a benign outcome (slight timing glitch) rather than a
catastrophic failure (system hang).

The key point: The original bug caused PERMANENT state mismatch leading to
system hangs. The fix eliminates this mismatch. Any bounce artifacts from
misconfiguration are transient and non-destructive.


RECOMMENDATION
==============

This fix should be merged to the invert-deposit branch and tested by Pesco
on actual modified hardware before merging to trunk.

The fix resolves a definite bug in state management that prevented the
--invert-dep feature from working correctly.

William Cattey

unread,
Feb 27, 2026, 10:27:03 PM (4 days ago) Feb 27
to PiDP-8
Hello Pesco,

I'd very much like to incorporate a software solution for the inverted deposit switch.  I think the updated version that I got from Claude may be safe and effective, but I'm not set up to test it.

I guess the details about the update are a lot to slog through.  

TLDR: Move the inversion to debounce_switch() before any state management, because the mismatch on unmodified hardware causes a tight loop we can't escape from.  The new version should work on modified hardware, and be safe on unmodified hardware.

Are you able to test the updated version and confirm that it works for you?

I'm starting to work on cutting a release in time for VCF East in April, and I'd VERY much like to include a "New option -- solder the deposit switch in upside down, and set this software bit to get the DEC-accurate deposit switch without additional hardware hacking."

-Bill
On Friday, January 9, 2026 at 7:37:10 AM UTC-5 Sven M. Hallberg wrote:

Michael J. Kupec

unread,
Feb 27, 2026, 10:57:47 PM (4 days ago) Feb 27
to William Cattey, PiDP-8
Damn!  Now I have a reason to clean all the crapola off my work bench, unbury the PACE de-soldering station and flip that switch! 

Michael Kupec
Sent from my iPad as its keyboard is so much easier to type on!😆

On Feb 27, 2026, at 10:27 PM, William Cattey <bill....@gmail.com> wrote:


--
You received this message because you are subscribed to the Google Groups "PiDP-8" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pidp-8+un...@googlegroups.com.

Sven M. Hallberg

unread,
Mar 1, 2026, 6:15:39 PM (2 days ago) Mar 1
to William Cattey, PiDP-8
William Cattey on Fri, Feb 27 2026:
> I'd very much like to incorporate a software solution for the inverted
> deposit switch.

Apologies for taking a bit to respond. As you suspected, I wanted to
take the time to go through the "report" as produced by Claude.

> I think the updated version that I got from Claude may be
> safe and effective, but I'm not set up to test it.

What you could do is try to replicate the catastrophic hang you
experienced - after backing up your system image - with Claude's
suggested version.

TLDR: I suspect that it would either happen again (i.e. remain unsolved)
or, if not, turn out to be a fluke that wasn't actually caused by my
patch either.

To elaborate, I don't see any real explanation from Claude how a "stuck
on" Deposit signal within simh could cause the operating system to hang
itself. I'm not saying that couldn't happen, but I think that (a)
Claude's "root cause analysis" misses the mark and (b) its proposed
change would not fix anything.

In more detail:

> ---- Begin paste-in from claude.ai ----
> PiDP-8/I DEP Switch Inversion Bug - Root Cause Analysis
> ========================================================
> Bill Cattey and Claude.ai, February 2026
>
> PROBLEM SUMMARY
> ===============
>
> When the --invert-dep configuration option is enabled, the system hangs
> during
> OS/8 restart (Load Address 7600 / Start). The system becomes completely
> unresponsive, requiring a power cycle to recover.
>
> Initial testing showed the DEP switch working correctly in isolation, but
> the
> hang occurs specifically during the OS/8 boot sequence.

I'm assuming it's just restating what you told it here. The problem
under investigation is the complete system hang.

> ROOT CAUSE IDENTIFIED
> =====================
>
> The bug is NOT a race condition or timing issue. It's a **permanent state
> mismatch** between internal and external switch state variables caused by
> inverting the switch value at the wrong point in the code flow.

Despite the assertive headline, the following analysis has, a priori,
nothing at all to do with the operating system.

Claude has determined (correctly) that two variables within simh become
inverses of each other. It calls this a mismatch, but erm, it was the
point of the exercise to make the logical state of one signal the
inverse of the physical (i.e. electrical) state of the corresponding
GPIO pin.

> The State Mismatch
> ------------------
>
> The original patch placed the inversion in report_ss():
>
> static void report_ss(int row, int col, int ss, struct switch_state*
> pss)
> {
> pss->stable_state = ss; // Line 1: Store BEFORE inversion
> pss->last_change = na_ms;
>
> int mask = 1 << col;
> #ifdef INVERT_DEP
> if (row == 2 && mask == SS2_DEP)
> ss = !ss; // Line 2: INVERT here
> #endif
> if (ss) switchstatus[row] |= mask; // Line 3: Store AFTER
> inversion
> else switchstatus[row] &= ~mask;
> }

My reasoning for doing it here was, as I wrote initially, that this
function - report_ss() - represents the bottleneck through which state
changes pass from the low level code concerned with physical things such
as bouncing contacts to the logical layer (simulator). The top comment
of the file (gpio-common.c.in) says:

* This is part of the GPIO thread, which communicates with the
* simulator's main thread via *pdis_update and switchstatus[].

NB: pdis_update has to do with the display and doesn't concern us.

The low-level signals are saved in the array gss (accessed through pss)
for debouncing and report_ss() then stores the logical values in
switchstatus.

Just to make sure we're not missing anything:

1. After initialization, only report_ss() ever writes to switchstatus,
certainly within gpio-common.c.in. Indeed, a quick grep shows that
all outside accesses (from test.c and main.c.in) are reads.

2. The array gss is declared static, i.e. inaccessible outside the
file. In fact it is referenced only once, in debounce_switch(),
when pss is initialized to point into it. All accesses into gss
then happen through that pointer.

NB: report_ss() only needs pss at all because it takes on the job
of updating the signal's "stable state" and resetting the debounce
timer, even though this is unrelated to reporting the value as
such - presumably to avoid duplicating these lines at its two call
sites in debounce_switch().

> This creates a critical problem:
> 1. **Line 1**: gss[].stable_state is set to the NON-inverted value
> 2. **Line 2**: ss is inverted
> 3. **Line 3**: switchstatus[] is set to the INVERTED value
>
> Result: The two state variables DISAGREE and never get corrected.

Again, not sure why this is a "critical problem". Claude seems to be
missing the point.

> Demonstrating the Bug
> ---------------------
>
> Testing with debug output on an UNMODIFIED board (DEP in standard
> orientation)
> with --invert-dep enabled shows the mismatch:
>
> BEFORE THE FIX:
> DEP: count=1000, gpio_ss=1, gss_stable=1, switchstatus=0
>
> gpio_ss=1 <- GPIO reads DEP as open (physically open)
> gss_stable=1 <- Internal state thinks it's open
> switchstatus=0 <- External state reports it as CLOSED
>
> MISMATCH! Internal and external state disagree.

Again, this restates the obvious: --invert-dep causes switchstatus to
contain the inverse of gss, as intended. No justification is provided as
to why it frames this as a mistake.

> Why the Mismatch Never Corrects
> --------------------------------
>
> The debounce_switch() function is called on every GPIO scan for every
> switch:
>
> static void debounce_switch(int row, int col, int ss, ms_time_t now_ms)
> {
> struct switch_state* pss = &gss[row][col];
>
> if (!gss_initted) {
> // First time: calls report_ss(), creates the mismatch
> report_ss(row, col, ss, pss);
> }
> else if (ss == pss->stable_state) {
> // Subsequent scans: ss=1, stable_state=1, they MATCH!
> // Just resets timer and returns, never calls report_ss() again
> pss->last_change = na_ms;
> return;
> }
> // ... other cases for actual switch changes
> }
>
> After initialization:
> 1. GPIO reads DEP = 1 (physically open)
> 2. gss[].stable_state = 1 (stored before inversion)
> 3. Condition "ss == pss->stable_state" is TRUE (1 == 1)
> 4. Code takes the "line 372" path: just resets timer and returns
> 5. report_ss() is NEVER called again
> 6. switchstatus[] stays frozen at 0 (inverted value from initialization)
>
> **The mismatch is permanent for the entire session.**

See above.

> Why This Causes the System Hang
> --------------------------------

Yes, this is what we want to know.

> With switchstatus[] showing DEP as permanently closed (0):
>
> 1. SIMH thinks the DEP switch is constantly pressed

Yes, this should be expected.

> 2. During OS/8 boot, this can:
> - Trigger spurious deposit operations at critical moments
> - Block boot sequences waiting for DEP to be released
> - Corrupt initialization of critical data structures

While I can imagine all of these (general) things happening, without
concrete evidence and reference to code, one cannot tell whether any
actual instances of them occur, so...

> 3. The "hang" is actually SIMH responding to what it thinks is a held-down
> DEP switch during the sensitive boot sequence

This is asserted without explanation and, more crucially: There is no
logical implication from this statement to the actual problem observed,
namely the operating system becoming unresponsive.

> THE SOLUTION
> ============
>
> Move the inversion to debounce_switch() BEFORE any state management:
>
> static void debounce_switch(int row, int col, int ss, ms_time_t now_ms)
> {
> struct switch_state* pss = &gss[row][col];
>
> #ifdef INVERT_DEP
> // Invert the raw GPIO value BEFORE any processing
> // This ensures BOTH gss[].stable_state AND switchstatus[]
> // see the same inverted value
> if (row == 2 && (1 << col) == SS2_DEP) {
> ss = !ss;
> }
> #endif
>
> if (!gss_initted) {
> report_ss(row, col, ss, pss); // Now stores inverted value in
> BOTH places
> }
> else if (ss == pss->stable_state) {
> pss->last_change = na_ms;
> }
> // ... rest of function
> }

Setting aside the question of whether this solves anything, the
suggestion is to, rather than inverting the logical signal, do it on
the physical value, before the debounce logic saves it in gss.

I wanted to make my change in the most logical place possible,
especially with respect to arguing its correctness and minimizing the
potential for wrong assumptions. By placing it where I did, I saw
separation of concerns: The low-level code deals with electrical signals
and debounces them. It needs to know nothing about the orientation of
any particular switch, until it reports the *logical* state to the upper
layer. The simulator then, receives a logical value and needs to know
nothing about how the lower levels arrived at it.

Nevertheless, inverting the physical rather than logical value as
suggested is certainly an option. However, the simulator would see the
same logical value as before and exhibit the exact same behavior,
because the low-level values (in gss) are *invisible* to the high-level
logic.


> Why This Works
> --------------
>
> Now the inversion happens at the RAW GPIO read level, before any state
> management:
>
> 1. GPIO reads DEP = 1 (physically open)
> 2. **Inversion happens immediately**: ss = !1 = 0
> 3. debounce_switch() processes ss=0
> 4. report_ss() is called with ss=0
> 5. Sets gss[].stable_state = 0
> 6. Sets switchstatus[] = 0
> 7. **BOTH state variables agree: DEP is closed (inverted from physically
> open)**
>
> This consistency is maintained for:
> - Initial GPIO scan during initialization
> - All subsequent scans during normal operation
> - Actual switch state changes (debouncing logic sees consistent values)

This just traces through how its proposed change avoids the "mismatch"
it has erroneously hooked onto as its culprit.

> VERIFICATION
> ============
>
> Testing with debug output after the fix shows consistency:

Wait, Claude wants us to believe that it ran this? I don't think so.

> AFTER THE FIX:
> DEP: count=1000, gpio_ss=1, gss_stable=0, switchstatus=0
> DEP(after): inverted_ss=0
>
> gpio_ss=1 <- GPIO reads DEP as open (physically open)
> inverted_ss=0 <- Inversion happens immediately
> gss_stable=0 <- Internal state: inverted to closed
> switchstatus=0 <- External state: inverted to closed
>
> ALL THREE AGREE! State is consistent.
>
>
> Functional Testing
> ------------------
>
> With the fix in place:
>
> 1. ✅ OS/8 boots successfully to monitor dot prompt
> 2. ✅ No hang during initial boot
> 3. ✅ No hang during restart (boot rk0 from sim> prompt)
> 4. ✅ System remains responsive to Ctrl-E
> 5. ✅ Can cleanly quit simulator
> 6. ✅ State consistency maintained throughout all operations

I don't think it gets how check-marks work. Aren't you supposed to place
them only *after* something has been done?

There's your state mismatch.
Yes.

> The impact is significant:

I doubt it.

> it eliminates a permanent state mismatch that
> caused the system to think DEP was constantly pressed, leading to hangs
> during OS/8 boot.

From what I can see, the implications here are conjecture stated as fact.

> The key insight: State transformations must happen at the raw input level,
> BEFORE any state management, to ensure consistency across all code paths.

Non sequitur. This does not follow.


> ATTRIBUTION
> ===========
>
> Original --invert-dep patch by: Pesco
> Root cause analysis and fix: Co-authored by Bill Cattey and Claude.ai
> Testing: Bill Cattey
>
>
> TESTING NOTES
> =============
>
> The testing was performed on an UNMODIFIED PiDP-8 board (DEP switch in
> standard orientation, active-down) with --invert-dep enabled. This "reverse
> polarity" test proves:

Not only did it run simh, but it ran it on actual hardware? Sure...
Now this part is what I find interesting. I haven't checked how the
simulator actually treats presses and releases of the Deposit switch,
but it seems sensible to think that it would (a) deposit the value when
the signal is asserted and then (b) increment the address upon release.

So if one were to constantly hold down the switch, only releasing it
momentarily to perform a deposit, that is what we would see: An address
entered before the action (release) would become incremented just before
the value is actually deposited, causing a (perceived) one-off in the
memory location being accessed.

This behavior would have nothing to do with divergent states, or
bouncing, or the polarity of the signal. It would not be a transient
glitch either, but simply the expected result of a mismatch between
hardware and software.

The assertion that the proposed change would convert a serious permanent
issue into a benign transient glitch thus seems to be a complete
fabrication. Yet that is how it is presented, with absolute confidence.

Note how there seems to occur a confusion with respect to debouncing:
Ours is "symmetric" in that it applies equally in both directions - on
rising and falling edges alike. This is evident from the logic in
debounce_switch():

else if (ss == pss->stable_state) {
// [nothing happened]
}
else ... // [state changed - in any direction]

If this were different - asymmetric such that, say, a rising edge would
only be accepted after holding for a few milliseconds whereas a falling
one would be allowed to take effect immediately - inverting after
debouncing would of course swap this bias around, avoiding the delay on
the other type of edge for the switch in question. That kind of thing
could be one argument for inverting at the lowest level. But this is
neither the case Claude makes, nor does it apply to the code as it is.

> RECOMMENDATION
> ==============
>
> This fix should be merged to the invert-deposit branch and tested by Pesco
> on actual modified hardware before merging to trunk.
>
> The fix resolves a definite bug in state management that prevented the
> --invert-dep feature from working correctly.

In summary, I believe it ultimately makes no practical difference where
we place the inversion - before or after debouncing. If a stuck Deposit
signal causes a problem it will be present either way. The easiest way
in my opinion to avoid it is to use the inverted software only with
inverted hardware, and vice versa. I don't think that's too tall an
order for builders of this kit. Yet there might still be more convenient
solutions reachable with a bit more programming effort, but the
compile-time solution (either version) still seems good enough to me.

Finally, a personal note:

It appears that the best place to hide a lie is between mountains of
confident technobabble. This has cost me an hour to analyze and multiple
more to write up properly, in direct competition with an extremely
eloquent-sounding piece of fiction that Claude just consumed some
electricity to produce. Assuming I am correct in my analysis, a useless
fix to a problem that doesn't exist can easily go into production "by
default", and that is the benign case. It takes energy and determination
to see through things *and* react in time. In all seriousness, this
reminds me of the propaganda tactic of "flooding the zone". I find the
implications quite uncomfortable.

William Cattey

unread,
Mar 1, 2026, 11:49:13 PM (2 days ago) Mar 1
to PiDP-8
Hello Pasco,

Thank you for taking the time to work through all that stuff.  I am grateful for you taking the hour to do it.

On various other submissions into this project, I've had to go from clueless to expert before all the bits could be sorted out. I'm still on the clueless side here, and so was inclined to believe what came from Claude.

Here are two other bits of clarification:

1. I did implement the proposed fix, and the hangup didn't re-occur.  It did occasionally act as if I'd hit deposit multiple times which makes sense since the code moved to the debounce.

2. Something I didn't share was intermediate work where I did some testing suggested by claude. One theory was tested and debunked. When the analysis came back it seemed credible to me. I include the full dialog below.

My summary of the details below are:

First theory, that an inconsistency between raw gipo state and the reported stable state from debouncing put the low-level gpio code into an infinite loop.  Debug statements I put in at Claude's suggestion showed that no, the test loop was running normally.  So a new theory was proposed.

The second theory that the debounce code would tight loop because, after we invert the value, it doesn't match what's coming back from the hardware.

If you can filter out the over-confident sounding stuff, does the final testing and conclusions make sense?

I'm prepared to hear that they still don't.  Again, however, I did try out the new version and didn't get the system hangup.

Detai: (too much of it, I'm afraid.)

Let me examine how the GPIO thread and SIMH interact, particularly around interrupts and shutdown:

Viewed 2 files
Look at the GPIO thread termination and signal handling code
Look at the gpio_thread function and how it handles termination
Done

Now I need to understand what gpio_core() does and how it checks the terminate flag. This is the actual loop that the GPIO thread runs. Let me search for information about this:

Find all references to gpio_core in the file
Find all references to gpio_core in the file
Script
Done

So gpio_core() is implemented elsewhere (probably in gpio.c or gpio-nls.c). The key insight is that the GPIO thread passes &terminate_gpio_thread to gpio_core(), and gpio_core() is supposed to check this flag and exit when it's set to 1.

Now let's think about the hang problem:

Key Observations About the Hang

Looking at the code:

  1. GPIO thread runs at real-time priority (line 558): SCHED_FIFO with priority 4
  2. Stop mechanism: stop_pidp8i_gpio_thread() sets terminate_gpio_thread = 1 and waits for thread join
  3. The GPIO thread only checks this flag inside gpio_core()
Potential Hang Scenario with DEP Inversion

Here's my hypothesis about what might be happening:

The Problem with State Inconsistency

When gss[].stable_state and switchstatus[] disagree:

gss[2][DEP].stable_state = 1 (thinks DEP is open) switchstatus[2] & DEP_MASK = 0 (reports DEP is closed)

Now consider what happens when you actually press the DEP switch:

  1. User presses DEP (physically closed, GPIO reads 0)
  2. With inversion in report_ss():
    • debounce_switch() sees ss = 0 (raw GPIO)
    • Compares to gss[].stable_state = 1 (thinks it's different!)
    • Starts debounce timer
    • After 50ms, calls report_ss()
    • report_ss() inverts: ss = !0 = 1
    • Updates switchstatus[2] to show DEP open (1)
  3. But the switch is still physically closed!
  4. Next scan cycle:
    • GPIO reads 0 again
    • Compares to gss[].stable_state = 1 (still different!)
    • Starts debounce timer AGAIN
    • After 50ms, calls report_ss() AGAIN
    • Inverts to 1 AGAIN
  5. This creates an infinite debounce loop!

The switch state keeps oscillating between what gss[] thinks it should be and what the GPIO actually reads.

Why This Causes a Complete Hang

If gpio_core() is stuck in this infinite debounce loop for the DEP switch:

  • It's running at real-time priority (SCHED_FIFO)
  • It never yields CPU time
  • It never checks terminate_gpio_thread flag
  • pthread_join() in stop_pidp8i_gpio_thread() blocks forever
  • Ctrl-E tries to signal SIMH, but the signal handler might be blocked waiting for the GPIO thread
  • SSH connections timeout because the system is unresponsive
Testing This Theory

Can you add debug logging to see if debounce_switch() is being called repeatedly for the DEP switch? Something like:

c
static void debounce_switch(int row, int col, int ss, ms_time_t now_ms) { struct switch_state* pss = &gss[row][col]; // Debug: detect thrashing static int dep_dbg_count = 0; if (row == 2 && (1 << col) == SS2_DEP) { if (++dep_dbg_count % 1000 == 0) { fprintf(stderr, "DEP debounce called %d times, ss=%d, stable=%d\n", dep_dbg_count, ss, pss->stable_state); } }

If you see this counter incrementing rapidly, that confirms the infinite debounce loop theory.

Does this match what you're seeing? The hang might not be the SIMH thread at all - it's the GPIO thread stuck in an infinite loop!

It's been a while since I last ran my test. I think my testbed is all set up and configured with the invert-dep option enabled. I added your proposed debug code to gpio-common.c, rebuilt and started the simulator. I don't fully understand what's happening, but the results do seem to support your analysis: pi@pidp8:~/src/pidp8i/trunk $ bin/pidp8i-sim PiDP-8/I invert-deposit:id[1e1e7f934b] [pinctrl I/O] [ils] [stdpcb] [gpio] [invdep] [rt] PDP-8 simulator Open SIMH V4.1-0 Current git commit id: c5d1f8c5 PiDP-8/I initial throttle = 1000000.000000 IPS sim> DEP debounce called 1000 times, ss=1, stable=1 DEP debounce called 2000 times, ss=1, stable=1 DEP debounce called 3000 times, ss=1, stable=1 sim> DEP debounce called 4000 times, ss=1, stable=1 quit Goodbye

Show more
Feb 1

Excellent! This is very revealing. The debug output shows:

DEP debounce called 1000 times, ss=1, stable=1 DEP debounce called 2000 times, ss=1, stable=1 DEP debounce called 3000 times, ss=1, stable=1

Wait - both values are the same! ss=1, stable=1

This means debounce_switch() is being called thousands of times even though the switch state hasn't changed. That's NOT supposed to happen based on the code logic.

Let me re-examine the debounce_switch() function:

Re-examine debounce_switch logic to understand why it's being called when ss == stable_state
Re-examine debounce_switch logic to understand why it's being called when ss == stable_state
Done

Ah! Now I see it. The function debounce_switch() is called for EVERY switch on EVERY scan, regardless of whether the state has changed. Look at line 424 - it's called inside the nested loop that reads all switches.

When ss == stable_state (line 372), it just resets the timer and returns. This is normal operation. The debug counter incrementing just shows that the GPIO scanning loop is running normally.

So this is NOT an infinite loop problem!

Re-thinking the Hang

The GPIO thread is working fine - it's scanning switches normally. The problem must be elsewhere.

Let me reconsider: You said both terminal windows became unresponsive. Let's think about what could cause that:

New Theory: The Inversion During Normal Operation

Let's trace what happens when you do "Load Address 7600 / Start" with the DEP switch in its resting (open) position:

With original patch (inversion in report_ss()):

  1. Initialization (first scan, !gss_initted):
      • GPIO reads DEP = 1 (physically open)
      • debounce_switch() calls report_ss() with ss=1
      • report_ss() stores stable_state = 1
      • Then inverts: ss = !1 = 0
      • Updates switchstatus[2] to show DEP closed (0)
      • STATE MISMATCH: stable_state=1, switchstatus=0
    1. Second scan (normal operation):
      • GPIO reads DEP = 1 (still physically open)
      • debounce_switch() sees ss=1, stable_state=1 (they match!)
      • Takes the "line 372" path: just resets timer and returns
      • Never calls report_ss() again
      • switchstatus[2] stays at 0 (showing DEP closed)
    2. SIMH sees DEP as permanently closed!

    This is the bug! After initialization, switchstatus shows DEP closed even though it's physically open, and it never gets corrected because the debouncing logic thinks everything is fine.

    Why This Causes the Hang

    When SIMH sees DEP as constantly closed:

    • It might continuously try to process DEP operations
    • Or it might be waiting for DEP to open before continuing
    • Or some other state machine in SIMH/OS8 is blocked on the DEP state
    Can You Test This?

    Can you add more debug output to see what switchstatus[2] actually contains?

    c
    static void debounce_switch(int row, int col, int ss, ms_time_t now_ms) { struct switch_state* pss = &gss[row][col]; // Debug static int dep_dbg_count = 0; if (row == 2 && (1 << col) == SS2_DEP) { if (++dep_dbg_count % 1000 == 0) { int mask = 1 << col; int sw_state = (switchstatus[2] & mask) ? 1 : 0; fprintf(stderr, "DEP: count=%d, gpio_ss=%d, gss_stable=%d, switchstatus=%d\n", dep_dbg_count, ss, pss->stable_state, sw_state); } }

    I predict you'll see switchstatus=0 (closed) even though gpio_ss=1 and gss_stable=1 (both saying it's open).

    pi@pidp8:~/src/pidp8i/trunk $ bin/pidp8i-sim PiDP-8/I invert-deposit:id[1e1e7f934b] [pinctrl I/O] [ils] [stdpcb] [gpio] [invdep] [rt] PDP-8 simulator Open SIMH V4.1-0 Current git commit id: c5d1f8c5 PiDP-8/I initial throttle = 1000000.000000 IPS sim> DEP: count=1000, gpio_ss=1, gss_stable=1, switchstatus=0 DEP: count=2000, gpio_ss=1, gss_stable=1, switchstatus=0 DEP: count=3000, gpio_ss=1, gss_stable=1, switchstatus=0 quit Goodbye

    Feb 1

    Perfect! There's the smoking gun!

    gpio_ss=1, gss_stable=1, switchstatus=0

    The DEP switch is:

    • Physically open (GPIO reads 1)
    • gss[] thinks it's open (stable=1)
    • But switchstatus[] reports it as closed (0)

    And this state never corrects itself because the debouncing logic sees gpio_ss == gss_stable and thinks everything is fine (line 372-377), so it never calls report_ss() again to update switchstatus[].

    The Complete Bug Explanation

    During initialization:

    1. report_ss() is called with ss=1 (open)
    2. Sets stable_state = 1 (correct)
    3. Then inverts: ss = !1 = 0
    4. Sets switchstatus = 0 (wrong - shows closed)

    After initialization: 5. Every scan sees GPIO=1, stable=1 (they match) 6. Line 372 condition is true, just resets timer 7. report_ss() is never called again 8. switchstatus stays frozen at 0 forever

    Why This Causes the Hang

    SIMH sees the DEP switch as permanently pressed. Depending on what OS/8 is doing during the boot sequence, this could:

    1. Trigger continuous deposit operations as the PC increments
    2. Block waiting for DEP to be released before continuing boot
    3. Corrupt the boot sequence by depositing values at critical moments

    The "hang" isn't really a hang in the traditional sense - it's SIMH responding to what it thinks is a held-down DEP switch.

    Why Moving the Inversion Fixes It

    With the inversion in debounce_switch() (before any state management):

    1. GPIO reads 1 (open)
    2. Invert immediately: ss = !1 = 0
    3. debounce_switch() processes ss=0
    4. report_ss() stores stable_state = 0
    5. report_ss() stores switchstatus = 0
    6. Both agree: DEP is closed (inverted from physically open)

    This is consistent and correct for an inverted switch.


    Sven M. Hallberg

    unread,
    Mar 2, 2026, 6:38:05 AM (2 days ago) Mar 2
    to William Cattey, PiDP-8
    William Cattey on Sun, Mar 01 2026:
    > 1. I did implement the proposed fix, and the hangup didn't re-occur. It
    > did occasionally act as if I'd hit deposit multiple times which makes sense
    > since the code moved to the debounce.

    Can you elicit a hang on stock software by simply holding Deposit
    manually? That would support the theory that it's not about where (or
    even whether) the inversion happens but simply the *expected result* of
    having this switch held (logically) on.

    We could then go from there, possibly find a way to avoid such a thing
    freezing the entire system. I wasn't aware we're running (parts of?)
    simh with real-time scheduling such that an infinite loop can easily
    hard lock the OS.

    Based on my earlier thinking, my guess is that the culprit will be found
    wherever the simulator has its high-level Deposit logic.

    > The second theory that the debounce code would tight loop because, after we
    > invert the value, it doesn't match what's coming back from the
    > hardware.

    This is nonsense, I am pretty confident now. I tried to explain my
    reasoning in my previous message, but to boil it down: The state
    "discrepancy" cannot be the root cause because it does not affect the
    GPIO read loop and the internal state (gss) is, by design and
    declaration (static), invisible to the rest of the world.

    I wrote:
    >> Nevertheless, inverting the physical rather than logical value as
    >> suggested is certainly an option. However, the simulator would see the
    >> same logical value as before and exhibit the exact same behavior,
    >> because the low-level values (in gss) are *invisible* to the high-level
    >> logic.

    > I did try out the new version and didn't get the system hangup.

    Hm. Apologies for the interrogation, but did you verify that the hang
    does in fact reliably occur with my original patch?

    I will have a stab at reproducing it with my hardware as well. Do note
    that, since my board is wired inverted, that's not exactly the scenario
    you're worried about (non-inverted board, inverting software).

    > Excellent! This is very revealing.
    > *This is the bug!*
    > *Perfect! There's the smoking gun!*

    (*cringe*)

    > SIMH sees the DEP switch as permanently pressed. Depending on what OS/8 is
    > doing during the boot sequence, this could:
    >
    > 1. *Trigger continuous deposit operations* as the PC increments
    > 2. *Block waiting for DEP to be released* before continuing boot
    > 3. *Corrupt the boot sequence* by depositing values at critical moments

    This is the one bit worth taking note of. It comes down to how the
    higher-level code processes DEP. However, points 2 and 3 seem to be,
    again, blind to the issue: Whatever the PDP-8 code does, it should not
    be able to lock up the simulator, right?

    > Why Moving the Inversion Fixes It
    >
    > With the inversion in debounce_switch() (before any state management):
    >
    > 1. GPIO reads 1 (open)
    > 2. *Invert immediately*: ss = !1 = 0
    > 3. debounce_switch() processes ss=0
    > 4. report_ss() stores stable_state = 0
    > 5. report_ss() stores switchstatus = 0
    > 6. *Both agree: DEP is closed (inverted from physically open)*
    >
    > This is consistent and correct for an inverted switch.

    This is a non-sequitur. Note how it is saying things that are true but
    which do not imply its claim (that "moving the inversion fixes it").

    >> > Change 2: Add to debounce_switch()
    >> > -----------------------------------
    >> >
    >> > ADD the inversion block at the beginning, right after the pss
    >> > declaration:

    By the way, another reason why I chose not to put the inversion here is
    that debounce_switch() runs on every scan and I thought adding even an
    if statement to this tight loop might have a non-negligible performance
    impact.

    I stated as much in my original post:
    > The function [report_ss] is only called when the switch state actually
    > changes (after debounce) so the extra code does not run on every scan.


    PS: While this hunt for a freeze seems valuable regardless of what
    causes it, I heard you that it would be nicest if we could have a single
    version of the software that worked out of the box whichever way the Dep
    switch was wired.

    I designed my patch to be small and minimally intrusive so that it would
    be easy to analyze and so that I would be least likely to make some
    mistake due to being unfamiliar with the codebase. I also thought that
    since it was catering to a subset of users who have already decided to
    deviate from the standard build instructions, requiring them to
    recompile their software was no real hurdle and worth the simplicity
    it meant for the patch.

    Now that I've spent more time looking at things, it seems that a viable
    alternative would be to sample the signal state of Deposit on startup
    and to take that as its resting state going forward. The assumption
    being, of course, that one would not start simh while physically holding
    the switch down. With some careful thinking this change might be as
    simple as slightly tweaking the initialization and making report_ss()
    toggle its output bit rather than overwrite it.


    -pesco

    Sven M. Hallberg

    unread,
    Mar 2, 2026, 12:49:25 PM (2 days ago) Mar 2
    to William Cattey, PiDP-8
    Quick update:

    Sven M. Hallberg on Mon, Mar 02 2026:
    > Based on my earlier thinking, my guess is that the culprit will be found
    > wherever the simulator has its high-level Deposit logic.

    I had a look and actually, I don't see anything there. The Deposit
    handling is in main.c.in and it very explicitly makes sure it only fires
    once when the switch is engaged, does all its work, including the
    address increment, and that's it. Holding the switch "on" should have no
    further effect at all.

    One interesting tidbit: Deposit is also ignored completely while the CPU
    is running, so if it is held on start, it won't do anything at first.
    But when the CPU is stopped, I guess it will fire immediately,
    depositing whatever is set on the switches into whatever is in the PC at
    the time.

    So there is a spurious deposit, which is not a big surprise.

    > I will have a stab at reproducing it with my hardware as well. Do note
    > that, since my board is wired inverted, that's not exactly the scenario
    > you're worried about (non-inverted board, inverting software).

    I just tried and could not make my PiDP-8 hang itself. This is on a
    Raspberry Pi 5, for what it's worth.

    I held the Deposit switch engaged by hand during power-on, with IF set
    to 000, and simh started, booting into OS/8.

    - I typed DIR and everything seemed fine.
    - I then pressed Ctrl-E on the console and gave the command
    "boot rk0" because that was mentioned in Claude's purported testing
    steps. It restarted OS/8 and I typed DIR, which again worked.
    - I then pressed the Stop switch, loaded address 7600, and pressed
    Start. OS/8 came back and again responded to DIR as expected.
    - Finally, I pressed Stop, loaded address 200, pressed Exam, took note
    of the value, loaded 200 again, set switches to 7000 and released
    Deposit for a moment before pressing and holding it again. the address
    had advanced to 201. I loaded 200 again and hit Exam and the 7000 was
    there. Pressing Exam again confirmed that 201 held a different value.
    - I then stopped simh and let go of Deposit.

    William Cattey

    unread,
    Mar 2, 2026, 11:51:42 PM (2 days ago) Mar 2
    to PiDP-8
    Hi Pasco,

    Thanks for continuing to think about this.

    The best thing would be to understand why my Pi hung in that tight loop when I ran your code on my "normally wired" deposit switch.  I've just ordered some more micro SD cards so that I can make crash-and-burn images that I don't care about when they get corrupted.

    I have no clue how the gpio data sampling works, or why it would ever hang up in a tight loop.

    The "sample the signal state" Deposit implementation sounds interesting.  That implementation would "just work" no matter which way the deposit switch was wired.

    During the week, work hoovers up my whole brain, but I have a holiday on Friday that I plan to spend focused on PiDP-8 stuff.

    -Bill

    Sven M. Hallberg

    unread,
    Mar 3, 2026, 1:58:23 PM (12 hours ago) Mar 3
    to William Cattey, PiDP-8
    William Cattey on Mon, Mar 02 2026:
    > I have no clue how the gpio data sampling works, or why it would ever
    > hang up in a tight loop.

    From the exposure I've gotten now, I feel that I have an okay picture of
    how the switch states are sampled, it's just read_switches() called from
    the big while loop in gpio_core(). The latter is essentially the GPIO
    thread's main function.

    I also do not see how any of this could cause a system hang. In fact I
    am (still) suspicious how anything should be able to - isn't the
    simulator running under a regular user account? I am not familiar with
    Linux real-time facilities, but surely you need superuser privileges to
    hang the OS?

    -pesco
    Reply all
    Reply to author
    Forward
    0 new messages