updated spec

This commit is contained in:
~d6 2023-11-16 00:12:34 -05:00
parent 813cc5910c
commit 496b55b45b
1 changed files with 190 additions and 56 deletions

View File

@ -1,78 +1,212 @@
CONSOLE DEVICE
0x10 vector* 0x18 out*
0x11 (vector) 0x19 err*
0x12 read 0x1a --
0x13 exec arguments 0x1b --
0x14 (exec arguments) 0x1c --
0x15 exec mode 0x1d --
0x16 exec action* 0x1e --
0x17 input type 0x1f --
0x10 vector 0x18 stdout+
0x11 (vector) 0x19 stderr+
0x12 stdin 0x1a send+
0x13 0x1b
0x14 proc-get 0x1c param
0x15 host-get 0x1d (param)
0x16 0x1e opts
0x17 type 0x1f action+
(* denotes a register that causes an immediate effect)
(+ denotes a register that causes an immediate effect)
vector (0x10) contains 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 input type (0x17).
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 read port (0x12) contains one byte of data to be read. it usually
occurs as the result of data being available on stdin, but may have
other meanings (such as an exit code from a finished process).
the stdin port (0x12) contains one byte of data to be read. the byte
represents input read from the emulator's stdin.
the meaning of exec args (0x13) depends on which action it is used with:
- 0x00 - unused
- 0x01 - address of a command string (e.g. "gcc -o demo demo.c")
- 0x02 - address of an env. variable name (e.g. "TERM")
- 0x03 - address of an env. variable name=value pair (e.g. "TERM=vt100")
- 0x04 - unsigned 16-bit integer (pty height, e.g. 0x0018)
- 0x05 - unsigned 16-bit integer (pty width, e.g. 0x0050)
- (all strings must be null-terminated)
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.
exec mode (0x15) provides context for exec actions (0x16):
- lower 2 bits control which child process to use:
the host-get port (0x15) contains one byte of data to be read. the byte
represents part of a response to a system action.
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 send 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 system action (0x1f). the meaning values by action 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 system actions run
using the action 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
- (next 2 bits unused)
- 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
exec action (0x16) acts on the exec args address (0x13):
the action port (0x1f) specifies which system action to take:
- 0x00 - nop: does nothing
- 0x01 - execute: reads command string, starts a subprocess
- 0x02 - getenv: reads string (NAME) from exec args
- 0x03 - setenv: reads string (NAME=value) and updates environment
- 0x04 - set-pty-height: reads u16 from exec args, updates winsize
- 0x05 - set-pty-width: reads u16 from exec args, updates winsize
(getenv data returned using console vector with input type 0x05, 0x06)
- 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
the input type (0x17) field explains how to interpret the read port
(0x12) when executing the console vector (0x10). these are the input
types that are expected:
- 0x00 - no input (read port is unused)
- 0x01 - stdin
- 0x02 - argument
- 0x03 - argument spacer (read port is \n)
- 0x04 - argument end (read port is \n)
- 0x05 - action output
- 0x06 - action output end (read port is \n)
- 0x40 - child 0 exited (read port is exit code)
- 0x41 - child 1 exited (read port is exit code)
- 0x42 - child 2 exited (read port is exit code)
- 0x43 - child 3 exited (read port is exit code)
- 0x81 - child 1
- 0x82 - child 2
- 0x83 - child 3
- (child 0 uses 0x01, i.e. "stdin", instead of 0x80)
EXAMPLE PROGRAM FRAGMENTS
writing a byte to the out port (0x18) will send one byte of data to
the standard output. if exec mode (0x15) is non-zero the lower 2 bits
will determine which child process to send the output to.
( ----------------------------------- )
writing a byte to the err port (0x19) will send one byte of data to
stderr. unlike with the out port (0x18), stderr is never redirected to
child processes.
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/action 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 )
@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/action 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
@resume $2 ( when set should have signature: -- 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/action 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* continuation* -- )
#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/send DEO INC2 !&loop ( ; send byte to child 1 )