So, after getting the family to bed, a bit more detailed look.
1. LOOP (or +LOOP) prevent crashing if in the same line as DO. If there is no LOOP in that line, Tali will crash after whatever is the last entry in the line.
2. Everything up till then is correctly compiled, starting with (DO) and continuing until the end of the line. The crash occurs at the first address after the last byte of compiled code. Put differently, it crashes to the Compiler Pointer (CP), aka what HERE would give us. This makes sense, because in the py65mon emulator, the unused memory locations are all zero (BRK).
3. The crash happens before the "compiled" message can be printed.
4. DO and ?DO are both affected, which is to be expected, because they share the same code. No other words seem to have this problem.
5. The code after the DO is executed (!) once before the crash, even though it really, really shouldn't be. Take this small bit of code:
Code:
: aaa 10 1 do ." frog"
If you hit ENTER after that, "frog" is printed once. A complete session:
Code:
hex here . 648 ok
: aaa 10 1 do ." frog"<ENTER> frog
PC AC XR YR SP NV-BDIZC
65C02: 069d 67 7d 04 f9 00110101
This also works with stuff like "1 1 + ." as well. This means that we're not jumping directly to the current CP, but futher back to
at least after the compiled (DO) routine.
6. Possibly we're even entering before then. Compile-time DO actually does start off by saving HERE to the Return Stack, then compiles six dummy bytes $05 as placeholders (which unhelpfully compile to 3 x ORA $05), then compiles (DO), which calculates the "fudge factor" for the loop. None of that would cause a crash if it were to execute during compile-time, so the 00 (BRK) at the end of the compiled code would be the first stop.
The next step is to trace exactly where we're landing by inserting some breaks and whatnot, but that's all I have time for today. Ye gods, I forgot how complicated the loop machinery is ... I'm glad I wrote down how it works in plain text in a separate file back then.