tabemann / zeptoforth

A not-so-small Forth for Cortex-M
MIT License
190 stars 18 forks source link

Interrupting loops #9

Open leahneukirchen opened 2 years ago

leahneukirchen commented 2 years ago

I didn't see any way to interrupt evaluation from the console, would it be possible to detect a BREAK on the serial line and throw and exception or something? It would simplify interactive usage, for now I had to reset on every infinite loop.

tabemann commented 2 years ago

I have thought of implementing this very feature that you suggest, but from some thought I have come to the conclusion that it has too much complexity and is hard to do safely if the goal is to return control to the REPL (what happens if you break during an operation on a lock or a channel?), especially since it would have to be done within an interrupt handler with any potential number of nested interrupts.

An alternative might be to trap a character in an interrupt handler and use it to signal a reboot. However, this would be the same as the manual reset you refer to, except it would be via serial, and it would not work if the MCU has crashed anyways. However, if you are using swdcom, ctrl-C for rebooting is not only baked into it but works even if the MCU has crashed since it uses ST-Link to do the rebooting; swdcom does not support the Raspberry Pi Pico though.

Of course, some terminals used with embedded Forth, such as e4thcom or my own zeptocom.js, do not allow the user to send arbitrary characters such as ctrl-C to the target. Also, if you dedicated a particular character to break it would deny use of that character by applications running within zeptoforth.

If you are running code that may run infinitely, I would suggest running it in a separate task from the main REPL task. Take the following:

task import
: test ( -- ) 0 [: begin put-your-code-here again ;] 320 128 512 spawn run ;

test when run will run put-your-code-here in an infinite loop, while still retaining a usable REPL. If you want to stop the task, one would reboot the MCU with reboot. If you want to be able to arbitrarily start and stop the task, you can do something like:

task import
variable my-task
: test ( -- ) 0 [: begin put-your-code-here again ;] 320 128 512 spawn my-task ! ;

Here you can then execute my-task @ run to start the task and my-task @ stop to stop the task, any number of times.