Hacker News new | past | comments | ask | show | jobs | submit login

My 6502 based FORTH systems would sometimes crash for no apparent reason after I tweaked some code and recompiled it. Whenever it got into crashy mode, it would crash in a completely different way, on a randomly different word. I'd put some debugging code in to diagnose the problem, and it would either disappear or move to another word! It was an infuriating Heizenbug!

It turns out that the 6502 has a bug [1] that when you do an indirect JMP ($xxFF) through a two byte address that straddles a page boundary, it would wrap around to the first byte of the same page instead of incrementing the high half of the address to get the first byte of the next page.

And of course the way that an indirect threaded FORTH system works is that each word has a "code field address" that the FORTH inner loop jumps through indirectly. So if a word's CFA just happened to straddle a page boundary, that word would crash!

6502 FORTH systems typically implemented the NEXT indirect threaded code inner interpreter efficiently by using self modifying code that patched an indirect JMP instruction on page zero whose operand was the W code field pointer. [2]

JMP indirect is a relatively rare instruction, and it's quite rare that it's triggered by normal static code (since you can usually catch the problem during testing), but self modifying code has a 1/256 chance of triggering it!

A later version of the 65C02 fixed that bug. It could manifest in either compiled FORTH code, or the assembly kernel. The FIG FORTH compiler [3] worked around it at compile time by allocating an extra byte before defining a new word if its CFA would straddle a page boundary. I defined an assembler macro for compiling words in the kernel that automatically padded in the special case, but the original 6502 FIG FORTH kernel had to be "checked and altered on any alteration" manually.

[1] http://everything2.com/title/6502+indirect+JMP+bug

[2] http://forum.6502.org/viewtopic.php?t=1619

"I'm sure some of you noticed my code will break if the bytes of the word addressed by IP straddle a page boundary, but luckily that's a direct parallel to the NMOS 6502's buggy JMP-Indirect instruction. An effective solution can be found in Fig-Forth 6502, available in the "Monitors, Assemblers, and Interpreters" section here. (The issue is dealt with at compile time; there is no run-time cost. The word CREATE pre-pads the dictionary with an unused byte in the rare cases when the word about to be CREATEd would otherwise end up with a code-field straddling a page boundary.)"

[3] http://www.dwheeler.com/6502/FIG6502.ASM

    ;    The following offset adjusts all code fields to avoid an
    ;    address ending $XXFF. This must be checked and altered on
    ;    any alteration , for the indirect jump at W-1 to operate !
              .ORIGIN *+2

              .WORD DP       ;)
              .WORD CAT      ;| 6502 only. The code field
              .WORD CLIT     ;| must not straddle page
              .BYTE $FD      ;| boundaries
              .WORD EQUAL    ;|
              .WORD ALLOT    ;)

I vote this as the best heisenbug I've read so far. That's sounds like such a nightmare to debug. I might have never found it due to throwing the thing in the trash and buying a different machine. Forth is easy to port after all. :)

Funny, I remember that bug very well, it was used on the earlier apple IIs, sometime on purpose (!) (mostly by game protections). This was fixed on the 65c02.

Applications are open for YC Winter 2021

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact