Console Redirection in zeptoforth - tabemann/zeptoforth GitHub Wiki
zeptoforth supports console I/O redirection along with error output redirection. Console I/O redirection redirects the words emit
, emit?
, key
, and key?
to arbitrary hooks. These hooks are stored in the user variables emit-hook
, emit?-hook
, key-hook
, and key?-hook
. Additionally error output is invoked via with-error-console
and has hooks stored in the user variables error-emit-hook
and error-emit?-hook
which, when this is called, temporarily replace the values of emit-hook
and emit?-hook
.
There is built-in support for console I/O redirection and error output to and from null (the bit bucket in the case of console output and error output, no input in the case of console input), the serial console, streams (akin to pipes in Unix), and files in FAT32 filesystems.
Redirection in the basic case where hooks are modified in the context of an execution token's execution and restored after it completes or when an exception is raised (after which the exception is re-raised) is accomplished with the words with-input
, with-output
, and with-error-output
in the console
module. with-input
, which temporarily modifies key-hook
and key?-hook
, takes a key hook, a key? hook, and an execution token. with-output
, which temporarily modifies emit-hook
, emit?-hook
, and flush-console-hook
, takes an emit hook, an emit? hook, a flush-console hook, and an execution token. with-error-output
, which temporarily modifies error-emit-hook
, error-emit?-hook
, and error-flush-console-hook
, takes an error-emit hook, an error-emit? hook, an error-flush-console hook, and an execution token.
Redirection to and from null is accomplished with the words with-null-input
, with-null-output
, and with-null-error-output
in the console
module. with-null-input
, which temporarily modifies key-hook
to return 0 and key?-hook
to always return false, takes an execution token. with-null-output
, which temporarily modifies emit-hook
to drop its argument without effect,
emit?-hookto always return *true*, and
flush-console-hookto be a no-op, takes an execution token.
with-null-error-output, which temporarily modifies
error-emit-hookto drop its argument without effect,
error-emit?-hookto always return *true*, and
error-flush-console-hook` to be a no-op, takes an execution token.
Redirection to and from the serial console is accomplished with the words with-serial-input
, with-serial-output
, and with-serial-error-output
in the console
module. with-serial-input
, which temporarily modifies key-hook
and key?-hook
to provide serial console input, takes an execution token. with-serial-output
, which temporarily modifies emit-hook
, emit?-hook
, and flush-console-hook
to provide serial console output, takes an execution token. with-serial-error-output
, which temporarily modifies error-emit-hook
, error-emit?-hook
, and error-flush-console-hook
to provide serial console error output, takes an execution token. Also, serial-console
in the int-io
module selects serial console I/O for the current task without limiting it to a particular scope.
Redirection to and from the USB CDC console, for full_usb
builds, is accomplished with the words with-usb-input
, with-usb-output
, and with-usb-error-output
in the usb
module. with-usb-input
, which temporarily modifies key-hook
and key?-hook
to provide USB CDC console input, takes an execution token. with-usb-output
, which temporarily modifies emit-hook
, emit?-hook
, and flush-console-hook
to provide USB CDC console output, takes an execution token. with-usb-error-output
, which temporarily modifies error-emit-hook
, error-emit?-hook
, and error-flush-console-hook
to provide USB CDC console error output, takes an execution token. Also, usb-console
in the usb
module selects USB CDC console I/O for the current task without limiting it to a particular scope.
Redirection to and from the USB CDC console, for full_swdcom
and mini_swdcom
builds, is accomplished with the words with-swd-input
, with-swd-output
, and with-swd-error-output
in the swd
module. with-swd-input
, which temporarily modifies key-hook
and key?-hook
to provide swdcom console input, takes an execution token. with-swd-output
, which temporarily modifies emit-hook
, emit?-hook
, and flush-console-hook
to provide swdcom console output, takes an execution token. with-swd-error-output
, which temporarily modifies error-emit-hook
, error-emit?-hook
, and error-flush-console-hook
to provide swdcom console error output, takes an execution token. Also, swd-console
in the swd
module selects swdcom console I/O for the current task without limiting it to a particular scope
Redirection to and from streams is accomplished with the words with-stream-input
, with-stream-output
, and with-stream-error-output
in the console
module. Each of these are buffered; a buffer is allotted temporarily which stores data being input or output, to reduce the frequency of actual receives or sends on the stream in question. In particular, when data is output, a software alarm is set to send the contents of the buffer after a short delay if it has not already been set, unless the buffer is filled where then the alarm is unset and the entire buffer is immediately sent. with-stream-input
, which temporarily modifies key-hook
to do a buffered byte receive from a stream and key?-hook
to report whether either a buffered byte is available or a byte is available in a stream, takes a stream and an execution token. with-stream-output
, which temporarily modifies emit-hook
to do a buffered byte send to a stream, emit?-hook
to report whether space is available in the buffer for another byte or space is available in a stream, and flush-console-hook
to flush buffered output to the stream, takes a stream and an execution token. with-stream-error-output
, which temporarily modifies error-emit-hook
to do a buffered byte send to a stream, error-emit?-hook
to report whether space is available in the buffer for another byte or space is available in a stream, and error-flush-console-hook
to flush buffered output to the stream, takes a stream and an execution token.
For an example of redirection to and from a stream, take the following:
begin-module stream-console-test
task import
console import
stream import
1024 constant data-size
data-size stream-size buffer: my-stream
data-size my-stream init-stream
1 cells buffer: out-notify-area
1 cells buffer: in-notify-area
0 :noname
my-stream [:
0 wait-notify drop
begin
[char] Z 1+ [char] A ?do i emit loop \ 100 ms
again
flush-console
;] with-stream-output
; 512 128 512 spawn constant out-task
0 :noname
my-stream [:
0 wait-notify drop
begin
key emit
again
;] with-stream-input
; 512 128 512 spawn constant in-task
out-notify-area 1 out-task config-notify
in-notify-area 1 in-task config-notify
out-task run
in-task run
0 out-task notify
0 in-task notify
end-module
This, when executed, outputs:
ok
AenBd-module CD ok
EFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
and it goes on ad infinitum.
Redirection to and from files is accomplished with the words with-file-input
, with-file-output
, and with-file-error-output
in both the fat32
module and the fat32-tools
module. These two sets of words differ in that the fat32
words take an instance of fat32::<fat32-file>
in any FAT32 filesystem while the fat32-tools
words take the path of a file in the current FAT32 filesystem as returned by fat32-tools::current-fs@
. Each of these are buffered; a buffer is allotted temporarily which stores data being input or output, to reduce the frequency of actual reads or writes on the file in question. Note that it is assumed that no other accesses to the file in question will occur; if they do occur undefined behavior will result. In particular, when data is output, a software alarm is set to write the contents of the buffer after a short delay if it has not already been set, unless the buffer is filled where then the alarm is unset and the entire buffer is immediately written. Also note that exhausting filesystem space when using with-file-output
or with-file-error-output
results in raising fat32::x-no-clusters-free
only when emit
is executed; these do not preemptively detect filesystem space exhaustion. with-file-input
, which temporarily modifies key-hook
to do a buffered byte read from a file and key?-hook
to report whether either a buffered byte is available or the end of file has not yet been reached, takes either a fat32::<fat32-file>
instance or the path of a file in the current FAT32 filesystem. with-file-output
, which temporarily modifies emit-hook
to do a buffer byte write to a file, emit?-hook
to always return true, and flush-console-hook
to flush all buffered data to the physical SDHC/SDXC card, takes either a fat32::<fat32-file>
instance or the path of a file in the current FAT32 filesystem. with-file-error-output
, which temporarily modifies error-emit-hook
to do a buffer byte write to a file, error-emit?-hook
to always return true, and error-flush-console-hook
to flush all buffered data to the physical SDHC/SDXC card, takes either a fat32::<fat32-file>
instance or the path of a file in the current FAT32 filesystem.
For an example of writing to a file (which, forgive me, is intolerably slow for reasons I have yet to elucidate) with console redirection, take the following:
begin-module fat32-test
oo import
fat32 import
simple-fat32 import
<simple-fat32-fs> class-size buffer: my-fs
0 constant my-spi
2 constant spi0-sck-pin
3 constant spi0-tx-pin
4 constant spi0-rx-pin
5 constant cs-pin
: run-test ( -- )
spi0-sck-pin spi0-tx-pin spi0-rx-pin cs-pin my-spi <simple-fat32-fs> my-fs init-object
false my-fs write-through!
my-fs fat32-tools::current-fs!
s" " s" /HEX.TXT" fat32-tools::create-file
s" /HEX.TXT" [:
$1000 $0000 ?do i h.4 loop
flush-console
;] fat32-tools::with-file-output
;
end-module
This is rather uneventful when executed:
fat32-test::run-test SD card timeout
fat32-test::run-test SD card timeout
fat32-test::run-test SD card timeout
fat32-test::run-test ok
The "SD card timeout" messages were due to bad electrical connections between the SDHC/SDXC card and the Pico, which were resolved by jostling them around a bit:
To see what was written, then I executed:
s" /HEX.TXT" fat32-tools::dump-file
00000000 30 30 30 30 30 30 30 31 30 30 30 32 30 30 30 33 |0000000100020003|
00000010 30 30 30 34 30 30 30 35 30 30 30 36 30 30 30 37 |0004000500060007|
00000020 30 30 30 38 30 30 30 39 30 30 30 41 30 30 30 42 |00080009000A000B|
00000030 30 30 30 43 30 30 30 44 30 30 30 45 30 30 30 46 |000C000D000E000F|
00000040 30 30 31 30 30 30 31 31 30 30 31 32 30 30 31 33 |0010001100120013|
[...]
00003FA0 30 46 45 38 30 46 45 39 30 46 45 41 30 46 45 42 |0FE80FE90FEA0FEB|
00003FB0 30 46 45 43 30 46 45 44 30 46 45 45 30 46 45 46 |0FEC0FED0FEE0FEF|
00003FC0 30 46 46 30 30 46 46 31 30 46 46 32 30 46 46 33 |0FF00FF10FF20FF3|
00003FD0 30 46 46 34 30 46 46 35 30 46 46 36 30 46 46 37 |0FF40FF50FF60FF7|
00003FE0 30 46 46 38 30 46 46 39 30 46 46 41 30 46 46 42 |0FF80FF90FFA0FFB|
00003FF0 30 46 46 43 30 46 46 44 30 46 46 45 30 46 46 46 |0FFC0FFD0FFE0FFF|
ok
(Shortened for brevity's sake.)
For a similar test of redirecting a file to the console input, take the following:
begin-module fat32-test
oo import
fat32 import
simple-fat32 import
<simple-fat32-fs> class-size buffer: my-fs
0 constant my-spi
2 constant spi0-sck-pin
3 constant spi0-tx-pin
4 constant spi0-rx-pin
5 constant cs-pin
: run-test ( -- )
spi0-sck-pin spi0-tx-pin spi0-rx-pin cs-pin my-spi <simple-fat32-fs> my-fs init-object
false my-fs write-through!
my-fs fat32-tools::current-fs!
s" /HEX.TXT" [:
begin key? while key emit repeat
;] fat32-tools::with-file-input
;
end-module
When one runs the following one gets:
fat32-test::run-test 0000000100020003000400050006000700080009000A000B000C000D000E000F[...]
(Also shortened for brevity's sake.)
For a test involving redirecting console output to a file using a fat32::<fat32-file>
object, take the following:
begin-module fat32-test
oo import
fat32 import
simple-fat32 import
<simple-fat32-fs> class-size buffer: my-fs
<fat32-file> class-size buffer: my-file
0 constant my-spi
2 constant spi0-sck-pin
3 constant spi0-tx-pin
4 constant spi0-rx-pin
5 constant cs-pin
: run-test ( -- )
spi0-sck-pin spi0-tx-pin spi0-rx-pin cs-pin my-spi <simple-fat32-fs> my-fs init-object
false my-fs write-through!
s" /HEX1.TXT" [: my-file swap create-file ;] my-fs with-root-path
my-file [:
$1000 $0000 ?do i h.4 loop
flush-console
;] with-file-output
;
end-module
This is uneventful when the following is executed:
fat32-test::run-test ok
To see what was written the file, I then executed the following:
fat32-test::my-fs fat32-tools::current-fs! ok
s" /HEX1.TXT" fat32-tools::dump-file
00000000 30 30 30 30 30 30 30 31 30 30 30 32 30 30 30 33 |0000000100020003|
00000010 30 30 30 34 30 30 30 35 30 30 30 36 30 30 30 37 |0004000500060007|
00000020 30 30 30 38 30 30 30 39 30 30 30 41 30 30 30 42 |00080009000A000B|
00000030 30 30 30 43 30 30 30 44 30 30 30 45 30 30 30 46 |000C000D000E000F|
00000040 30 30 31 30 30 30 31 31 30 30 31 32 30 30 31 33 |0010001100120013|
[...]
00003FB0 30 46 45 43 30 46 45 44 30 46 45 45 30 46 45 46 |0FEC0FED0FEE0FEF|
00003FC0 30 46 46 30 30 46 46 31 30 46 46 32 30 46 46 33 |0FF00FF10FF20FF3|
00003FD0 30 46 46 34 30 46 46 35 30 46 46 36 30 46 46 37 |0FF40FF50FF60FF7|
00003FE0 30 46 46 38 30 46 46 39 30 46 46 41 30 46 46 42 |0FF80FF90FFA0FFB|
00003FF0 30 46 46 43 30 46 46 44 30 46 46 45 30 46 46 46 |0FFC0FFD0FFE0FFF|
ok
(Truncated for brevity.)
For a test involving redirecting console input from a file using a fat32::<fat32-file>
object, take the following:
begin-module fat32-test
oo import
fat32 import
simple-fat32 import
<simple-fat32-fs> class-size buffer: my-fs
<fat32-file> class-size buffer: my-file
0 constant my-spi
2 constant spi0-sck-pin
3 constant spi0-tx-pin
4 constant spi0-rx-pin
5 constant cs-pin
: run-test ( -- )
spi0-sck-pin spi0-tx-pin spi0-rx-pin cs-pin my-spi <simple-fat32-fs> my-fs init-object
false my-fs write-through!
s" /HEX1.TXT" [: my-file swap open-file ;] my-fs with-root-path
my-file [:
begin key? while key emit repeat
;] with-file-input
;
end-module
When run one gets the following:
fat32-test::run-test 0000000100020003000400050006000700080009000A000B000C000D000E000F[...]
(Truncated for brevity's sake.)
To temporarily modify the error output redirection to match the current console output redirection, execute with-output-as-error-output
in the console
module, which takes an execution token. Unlike separately executing, say, with-stream-error-output
or with-file-error-output
, this both avoids excessive dictionary and return stack space usage, and avoids undesirable results -- in particular, executing with-file-output
and with-file-error-output
on the same file will result in undefined behavior.
It should be noted that copious amounts of RAM dictionary space and return stack space are needed for redirection to and from streams and files in FAT32 filesystems. Redirection to and from streams requires at least 512 bytes of free RAM dictionary space for the current task, and for redirection to and from files in FAT32 filesystems 1024 bytes of free RAM dictionary space and 1024 bytes of return stack space are needed (in particular, from testing, the previous default of 512 bytes of return stack space is not enough for redirection to and from files in FAT32 filesystems, and redirection to and from files in FAT32 filesystems requires 512 bytes for representing a block of data alone, not counting all the other space requires). For this reason the size of the return stack for the main task has been increased to 1024 bytes as of release 0.64.1.