Standard Input - Output and the CLI

The Z88 operating system has, at any one time, a standard input source and a standard output destination. A number of calls use these standard I/O streams:

OS_In       read a character from standard input
OS_Tin      timed read a character from standard output
OS_Out      write a character to standard output
OS_Sout     write a string to standard output
OS_Bout     write a string at extended address to standard output
GN_Sip      fetch an input line using standard I/O
GN_Sdo      write date to standard output

Initially standard input and output are bound to the keyboard and screen respectively, but may be redefined to any file or device. The user may do this via the Command Line Interpreter (CLI). CLI commands are prefixed by a full stop and must be the first thing on the line.

.< INFILE               take input from file/device "INFILE"
.> OUTFILE              send output to file/device "OUTFILE"

which binds the standard input to "infile" and the standard output to "outfile". Redirection can also be done as T-fashion, where the ordinary streams remain in place, but copies of their contents are sent to a file or a device.

.T< INFILE              send copy of input to file/device "INFILE"
.T> INFILE              send copy of output to file/device "OUTFILE"

The printer output can be redirected in both ways:

.= PRTFILE              redirect pre-filter printer output to file/device
.T= PRTFILE             as .=, but also send the output to the printer filter.

Certain keystroke operations (such as <SQUARE> and <DIAMOND> sequences) have a special representation which is used when input is redirected. These sequences are generated when input is T-redirected and can be used in ordinary redirection to simulate the effect of the keyboard. Note that there are some keyboard features which have no representation, such as: the CAPS LOCK key, <SHIFT> or <DIAMOND> with <ESC>, the effect of holding down <SHIFT> and <DIAMOND> to stop output scrolling.

Sequence    Significance

#           holding down <SQUARE> and pressing another key
|           holding down <DIAMOND> and pressing another key
~A          pressing <SQUARE> and releasing it before another keypress
~C          pressing <DIAMOND> and releasing it before another keypress
|[          <ESC> key
~E          <ENTER> key
~S          <SHIFT> key (only generated if <SHIFT> had an effect)
~I          <INDEX> key
~M          <MENU> key
~H          <HELP> key
~X          <DEL> key
~U          <UP> arrow key
~D          <DOWN> arrow key
~L          <LEFT> arrow key
~R          <RIGHT> arrow key
##          a single # (hash) character
||          a single | character
~~          a single ~ tilde (pronounced 'tilda' or 'twiddle')

The CLI also has the following commands:

.S          suspend the current CLI but maintain all rebindings
.D n        delay for n centiseconds (if <ESC> is pressed during a delay then
            subsequent delays will fail for the rest of the current CLI)
.J          Jammer. Ignore all special sequences for the rest of the CLI (eg.
            after this command #B will generate '#B' and try and enter a BBC
            BASIC application)
.* file     Invoke a new CLI file
.;          CLI comment (rest of line until CR is ignore by CLI)

NOTE: All CLI commands prefixed by a single dot must be at the start of a line. Any number of dots at the start of the line will be ignored. One way to produce a single dot is to use '~.' at the start of a line to generate a full stop. Subsequent dots (ie. ones which do not appear as the first character of the line) are treated like ordinary alpha characters.

CLI files can invoke other CLI files. A CLI file is terminated when the end of a file is reached or a suspension command is issued. CLI's can be forcibly removed one at a time by pressing <SHIFT> and <ESC>, or all current CLI's can be removed by using <DIAMOND> and <ESC>. Note that <DIAMOND> must be actually held down while <ESC> is pressed, ie. the usual latching operation does not apply. The CLI can be accessed by BBC BASIC by using the *CLI command or the OSCLI command. For example, to create a 10 second delay:

*CLI .D 100 
OSCLI ("*CLI .D 100"): REM these two lines are equivalent

Most CLI use will be in the form of executable files, consisting of lines of CLI commands and text. These can be generated in PipeDream and saved as text files. Or generated by hand and then executed. CLI files can be executed from the FILER (using <>EX command) or from BBC BASIC using:

OSCLI ("*CLI .*"+fname$)

where fname$ contains the filename. Typical CLI files consist of short sequences like:

.T> copyout .S

The first line sends a copy of all output to the file 'copyout', and the second line maintains this new binding on exit from the CLI file. The binding (T-output to 'copyout') will now stay in place until the CLI is terminated by <SHIFT><ESC> to remove the current CLI (or <><ESC>, which removes all CLI's running). This situation will generally be undesirable, because it requires user intervention, since the program cannot simulate the effect of <SHIFT> <ESC>. Ideally the CLI should be avoided. Redirection can be easily achieved in applications by using file I/O, however, if necessary the CLI can be accessed fairly directly from an application. The operating system call DC_Icl invokes a new CLI, and is effectively the equivalent of BASIC's *CLI. The call DC_Rbd is used to directly rebind streams in the current CLI layer and can also terminate the CLI.

Example

The program below starts a CLI, using DC_Rbd, rebinds the T-output stream, and finally closes the stream and terminates the CLI. Refer to "System Calls Reference" for details of the calls used:

 
include "director.def"              ; director and CLI call definitions
include "stdio.def"                 ; standard I/O call defs. & parameters
include "fileio.def"                ; standard file I/O call defs. & parameters
include "errors.def"                ; error code calls and definitions

 ; entry point is .main 
; on entry, (name) contains the address of a buffer which hold a filename 
; to be used with the CLI, and (scratch) holds the address of a scratch 
; buffer which need be no more than a few bytes in length 

.main       ld   hl, cli_string     ; string to pass the CLI
            ld   c, 2               ; length
            ld   b, 0               ; must be zero
            oz   DC_Icl             ; invoke a new CLI
            ld   bc, 0              ; key read to allow CLI to be processed
            oz   OS_Tin

            ld   b, 0               ; HL is a local address
            ld   hl, (name)         ; filename
            ld   de, (scratch)      ; address of scratch buffer
            ld   c, 1               ; explicit filename length
            ld   a, OP_OUT          ; open a file for output
            oz   GN_Opf             ; open...
            jr   nc, rebind
            oz   GN_Err             ; display error in box
            ret                     ; note that on exit the CLI will
                                    ; still be active
; code to rebind standard output 

.rebind     ld   a, 4               ; T-output code
            oz   DC_Rbd             ; rebind to stream IX
            jr   nc, continue
            oz   GN_Cl              ; something has gone badly wrong
            ret                     ; attempt to close the file and exit
; write to standard output, and hence also to the file 

.continue   ld   hl, message
            oz   GN_Sop             ; output message to std. output and file
            ld   ix, 0              ; close off CLI and file
            ld   a, 4               ; T-output code
            oz   DC_Rbd             ; close file and quit CLI
            ret                     ; return (ignore all errors)

.message    defm "This should go to the file and the screen.", 0
.cli_string defm ".S", 0