221 lines
9.3 KiB
Plaintext
221 lines
9.3 KiB
Plaintext
CONSOLE DEVICE
|
|
|
|
0x10 vector* 0x18 @stdout
|
|
0x11 (vector) 0x19 @stderr
|
|
0x12 stdin 0x1a @proc-put
|
|
0x13 0x1b
|
|
0x14 proc-get 0x1c param*
|
|
0x15 host-get 0x1d (param)
|
|
0x16 0x1e opts
|
|
0x17 type 0x1f @host-put
|
|
|
|
(* denotes a short register, i.e. two bytes wide)
|
|
(@ denotes a register that causes an immediate effect)
|
|
|
|
vector ports (0x10-0x11) contain an address to jump to when input is
|
|
available. when the vector fires one byte can be read from the read
|
|
port (0x12), and its meaning is defined by the type (0x17).
|
|
|
|
the stdin port (0x12) contains one byte of data to be read. the byte
|
|
represents input read from the emulator's stdin.
|
|
|
|
the proc-get port (0x14) contains one byte of data to be read. the byte
|
|
represents input read from one of the emulator's subprocesses.
|
|
|
|
the host-get port (0x15) contains one byte of data to be read. the byte
|
|
represents part of a response to a host-put (0x1f).
|
|
|
|
the type (0x17) field explains how to interpret calls to the console
|
|
vector (0x10) and where data can be read from:
|
|
- 0x00 - no input (n/a)
|
|
- 0x01 - stdin (stdin)
|
|
- 0x02 - argument (stdin)
|
|
- 0x03 - argument spacer (stdin)
|
|
- 0x04 - argument end (stdin)
|
|
- 0x05 - host response (host-get)
|
|
- 0x06 - host response end (host-get)
|
|
- 0x40 - child 0 exited (host-get contains exit code)
|
|
- 0x41 - child 1 exited (host-get contains exit code)
|
|
- 0x42 - child 2 exited (host-get contains exit code)
|
|
- 0x43 - child 3 exited (host-get contains exit code)
|
|
- 0x80 - child 0 sent data (proc-get)
|
|
- 0x81 - child 1 sent data (proc-get)
|
|
- 0x82 - child 2 sent data (proc-get)
|
|
- 0x83 - child 3 sent data (proc-get)
|
|
|
|
writing a byte to the stdout port (0x18) will send one byte of data to
|
|
the emulator's stdout.
|
|
|
|
writing a byte to the stderr port (0x19) will send one byte of data to
|
|
the emulator's stderr.
|
|
|
|
writing a byte to the proc-put port (0x1a) will send one byte of data to
|
|
one of the emulator's child processes. the lower 2 bits of the opts
|
|
port (0x1e) determine which one:
|
|
- 0x00: child 0
|
|
- 0x01: child 1
|
|
- 0x02: child 2
|
|
- 0x03: child 3
|
|
|
|
the param ports (0x1c-0x1d) specify the a value to use as a parameter
|
|
for a host-put (0x1f). the meaning values by host-put value:
|
|
- 0x00 - (nop) unused
|
|
- 0x01 - (execute) command string (e.g. 'gcc -o "demo" demo.c')
|
|
- 0x02 - (pid) unused
|
|
- 0x03 - (kill) unused
|
|
- 0x08 - (pty-set-height) height as u16 integer
|
|
- 0x09 - (pty-set-width) width as u16 integer
|
|
- 0x10 - (getenv) name string (e.g. "TERM")
|
|
- 0x11 - (setenv) assignment string (e.g. "TERM=vt100")
|
|
- 0x20 - (tty-set-raw) unused
|
|
- 0x21 - (tty-unset-raw) unused
|
|
strings must be null-terminated. commands are parsed by /bin/sh -c.
|
|
|
|
the opts port (0x1e) specifies options that affect host actions run
|
|
using the host-put port (0x1f):
|
|
- lower 2 bits control which child process to use (when applicable)
|
|
+ 0x00 - child 0
|
|
+ 0x01 - child 1
|
|
+ 0x02 - child 2
|
|
+ 0x03 - child 3
|
|
- upper 4 bits control which pipes to use with execute:
|
|
+ 0x80 - use child's pty (implies 0x70)
|
|
+ 0x40 - read from child's stderr
|
|
+ 0x20 - read from child's stdout
|
|
+ 0x10 - write to child's stdin
|
|
|
|
the host-put port (0x1f) specifies which host action to take:
|
|
- 0x00 - nop: does nothing
|
|
- 0x01 - execute: reads command string, starts a subprocess
|
|
- 0x02 - pid: responds with child process pid (if any)
|
|
- 0x03 - kill: shuts down child process
|
|
- 0x08 - pty-set-height: set a child's pty height
|
|
- 0x09 - pty-set-width: set a child's pty width
|
|
- 0x10 - getenv: looks up a name (e.g. TERM) in env, responds with value
|
|
- 0x11 - setenv: reads an assignment (e.g. TERM=vt100), updates env
|
|
- 0x20 - tty-set-raw: enable raw mode in emulator's terminal
|
|
- 0x21 - tty-unset-raw: disable raw mode in emulator's terminal
|
|
|
|
EXAMPLE PROGRAM FRAGMENTS
|
|
|
|
( ----------------------------------- )
|
|
|
|
1. The following fragment runs `make` and acts based on its exit code:
|
|
|
|
|0100 ( -- BRK )
|
|
;on-console .Console/vector DEO2 ( ; set up console vector callback )
|
|
... BRK ( ; do other initialization )
|
|
|
|
@on-console ( -- BRK )
|
|
.Console/type DEI #41 ?on-child-exit ( ; 0x41 signals child 1's exit )
|
|
... BRK ( ; handle other console input )
|
|
|
|
@on-child-exit ( -- BRK )
|
|
.Console/host-get DEI ( ; read child 1's exit code )
|
|
?{ display-success-msg BRK } ( ; zero exit code means success )
|
|
display-failure-msg BRK ( ; non-zero exit code means failure )
|
|
|
|
@run-make ( -- )
|
|
;make-cmd .Console/param DEO2 ( ; set up make to run )
|
|
#01 .Console/opts DEO ( ; use child without pipelines )
|
|
#01 .Console/host-put DEO JMP2r ( ; run the command now and return )
|
|
|
|
@make-cmd "make $3c ( ; buffer containing cmd to run )
|
|
|
|
( ----------------------------------- )
|
|
|
|
2. The `mpg123 <path>` commands plays an mp3 from the given path. The
|
|
following fragment demonstrates part of an mp3 jukebox:
|
|
|
|
|0000
|
|
@running $1
|
|
|
|
|0100
|
|
#01 .running STZ ( ; start running )
|
|
;on-console .Console/vector DEO2 ( ; set up console vector callback )
|
|
... BRK ( ; do other initialization and exit )
|
|
|
|
@on-console ( -- BRK )
|
|
.Console/type DEI #04 ?play-jukebox ( ; start jukebox after parsing cmd line )
|
|
.Console/type DEI #42 ?on-child-exit ( ; 0x42 signals child 2's exit )
|
|
... BRK ( ; handle other console input )
|
|
|
|
@on-child-exit ( -- BRK )
|
|
.running LDZ ?{ exit } ( ; if we are done, exit )
|
|
( ; else fall-through to play-jukebox )
|
|
@play-jukebox ( -- BRK )
|
|
next-song-path run-mpg123 BRK ( ; play the next song )
|
|
|
|
@exit ( -- BRK )
|
|
#80 .System/halt DEO BRK ( ; exit immediately with code 0 )
|
|
|
|
@quit-immediately ( -- BRK )
|
|
#00 .running STZ ( ; note that we are stopping )
|
|
#02 .Console/opts DEO ( ; set host action to use child 2 )
|
|
#03 .Console/host-put BRK ( ; kill child process 2 )
|
|
( ; this will trigger on-console )
|
|
( ; and then on-child-exit. )
|
|
|
|
@run-mpg123 ( path* -- )
|
|
;mpg123-cmd/buf scpy ( ; copy path into cmd buffer )
|
|
;mpg123-cmd .Console/param DEO2 ( ; set up `mpg123 <path>` cmd to run )
|
|
#02 .Console/opts DEO ( ; use child 2 without pipelines )
|
|
#01 .Console/host-put DEO JMP2r ( ; start playing mp3 now and return )
|
|
|
|
@mpg123-cmd "mpg123 20 &buf $100
|
|
|
|
@next-song-path ( -- path* ) ... JMP2r ( ; load the next song's path )
|
|
|
|
@scpy ( s* dest* -- ) ... JMP2r ( ; copy string, including null, to dest )
|
|
|
|
( ----------------------------------- )
|
|
|
|
3. The `ispell -a` command accepts a line. It prints "*\n\n" if the input
|
|
is a correctly-spelled word; otherwise it prints "<some other text>\n\n".
|
|
|
|
The following fragment uses `ispell -a` to implement a basic spell checker:
|
|
|
|
|0000
|
|
@spelled-ok? $1 ( ; was the last word spelled ok? )
|
|
@resume $2 ( ; if set should have effect ` -- BRK` )
|
|
|
|
|0100 ( -- BRK )
|
|
init-ispell ( ; set up ispell child process )
|
|
;on-ispell-init .Console/vector DEO2 ( ; set up console vector callback )
|
|
... BRK ( ; do other initialization )
|
|
|
|
@init-ispell ( -- )
|
|
;ispell-cmd .Console/param DEO2 ( ; set up `ispell -a` to run )
|
|
#61 .Console/opts DEO ( ; child 1: write to stdin, read from stdout )
|
|
#01 .Console/host-put DEO JMP2r ( ; run the command now and return )
|
|
|
|
@ispell-cmd "ispell 20 "-a 00 ( ; "ispell -a" )
|
|
|
|
@on-ispell-init ( -- BRK )
|
|
.Console/type DEI #81 EQU ?{ BRK } ( ; 0x81 signals input from child 1 )
|
|
.Console/proc-get #0a EQU ?{ BRK } ( ; skip ispell's one line banner )
|
|
;on-ispell-ready .Console/vector DEO2 ( ; finished banner, checker is ready )
|
|
|
|
@on-ispell-ready ( -- BRK )
|
|
.Console/type DEI #81 EQU ?{ BRK } ( ; 0x81 signals input from child 1 )
|
|
.Console/proc-get LIT "* EQU ( ; line starting with "*" means ok )
|
|
.spelled-ok STZ ( ; store spelling result )
|
|
#00 ,on-spell-drain/done STR ( ; drain two lines )
|
|
;on-ispell-drain .Console/vector DEO2 ( ; ignore rest of line )
|
|
BRK
|
|
|
|
@on-spell-drain ( -- BRK )
|
|
.Console/type DEI #81 EQU ?{ BRK } ( ; 0x81 signals input from child 1 )
|
|
.Console/proc-get #0a EQU ?{ BRK } ( ; skip ispell's outupt )
|
|
LIT [ &done $1 ] ?{ ( ; is this the second newline? )
|
|
#01 ,on-spell-drain/done STR BRK ( ; no, but next one will be. )
|
|
}
|
|
;on-ispell-ready .Console/vector DEO2 ( ; drain finished, checker is ready )
|
|
!resume ( ; resume whatever we wanted to do after checking )
|
|
|
|
@check-spelling ( word* continue* -- )
|
|
#01 .Console/opts DEO ( ; act on child 1 )
|
|
.resume STZ2 ( ; write continuation to resume )
|
|
&loop LDAk ?{ POP2 JMP2r } ( ; when we read \0 we are done )
|
|
LDAk .Console/proc-put DEO INC2 !&loop ( ; send byte to child 1 and loop )
|