Output to the screen can be achieved by treating the screen as a device and using the file I/O system calls. However, there are six calls which can be used to send output to what is called standard output, which although it may be redefined by the CLI, see 'Standard I/O and the CLI', is usually the screen device. The calls are:
- OS_Out, write character to standard output
- OS_Nln, write newline to standard output (<CR><LF>), replace GN_Nln
- OS_Sout, write a string in local memory to standard output, replace GN_Sop
- OS_Bout, as OS_Sout, but from an extended address, replace GN_Soe
- OS_Pout, write an embedded string at (PC)
- OS_Hout, write a hexadecimal byte to standard output
These calls are fully compatible with GN_Sop, GN_Soe, GN_Nln. They are deprecated and maintained for compatibility. They perform a significantly slower rendering.
These calls are fairly friendly as far as register corruption is concerned. OS_Out writes the character held in register A and forces Fc = 0 before returning with no other register or flag changes. GN_Nln changes no registers and returns Fc = 0 unless there is an error. GN_Sop and GN_Soe expect HL (or BHL) to point to a null-terminated string, and on exit HL (or BHL) will point to the null, with other registers remaining the same, unless there is an error. Errors can occur if the standard output device has been redirected to somewhere other than the screen. In the case of errors Fc = 1 and register A will contain an error code. If you are not doing any redirection yourself then these kind of error situations can be largely ignored, since if the user is doing bizarre stream rebindings it is not your responsibility what happens. Examples of these routines in use can be seen in the "Input and the keyboard decoder" section.
In using the Z88 you may well have come across a "Page Wait" message which appears on the left hand side of the screen in place of the application name and list of topics. This message can be generated by an application (it is not done automatically) when scrolling output is being sent to the screen. The actual system call involved is OS_Sr with register A = SR_PWT. This call produces the "Page Wait" message, waits for a key, restores the application name and topic list and then returns to the application. Because at the core of this routine there is a keyread, application writers must be careful to check for pre-emption codes, especially RC_QUIT. Although the writer may think about "Page Wait" as primarily an output function, it has all the characteristics of an input function. However, it only returns <BACKSPACE> (ASCII 8) and all relevant error codes like RC_QUIT and RC_DRAW.
The screen driver
The Z88 incorporates a comprehensive screen driver, so that apart from printing normal characters, the user may, by sending suitable control codes to the screen:
- Set fonts and select bold or flash effects for characters
- Switch the cursor on and off
- Simulate the effects of special keys (Caps lock, index etc.)
- Define, select and scroll windows
- Select the horisontal and vertical print positions
- Set justification or alignment
- Set up user defined characters
- Make various strings of beeps
In fact, any graphic effects used by the built-in software with the single exception of the map. This is accessed more directly through the system calls, in particular OS_Map described in "Miscellaneous useful routines". The Standard library have also supplied utilities to plot, draw lines and sprites in the map area, which easily can be linked with application code using the Z80 Module Assembler Mpm.
Direct Control Characters
The following standard control characters have a special effect:
ASCII name Value Z88 effect
NUL $00 Ignored (Possibly used by the system for control of screen restoring.)
SOH $01 Escape character for screen driver
ENQ $05 Escape character for printer filter
BEL $07 Make a beep
BS $08 Move Cursor Left
HT $09 Move Cursor Right
LF $0A Move Cursor Down
VT $0B Move Cursor Up
FF $0C Clear screen (current window)
DEL $7F Draw black box, (currently, undefined codes do too)
The characters SOH ($01) is used as an escape character to prefix the special character combinations. It may be followed either by a single special character, or if more than one parameter is to follow, by a count byte which specifies the number of following parameters - this may be in the form of a binary number with the top bit set, ie. count plus 128, or an ASCII code for a single decimal digit - clearly the latter is impossible for a count of more than 9. Thus the following sequences are equivalent - both move the cursor to character position (x,y):
1, '3', '@', 32+x, 32+y 1, 128+3, '@', 32+x, 32+y
NOTE: A character in single quotes is used in this section as a concise notation for a character constant, so for instance 'A' is the ASCII code for A, ie. $41 or 65 decimal. This translates into rather less elegant ASC"A" when used in BBC BASIC or its assembler, so to send the former of the above sequences to the screen from BBC BASIC one might do:
VDU 1,ASC"3", ASC"@", 32+x,32+y
The following screen driver codes generate special printable characters when preceeded by SOH:
Vertical unbroken bar
Outline arrow Left
Outline arrow Right
Outline arrow Down
Outline arrow Up
Bullet arrow Left
Bullet arrow Right
Bullet arrow Down
Bullet arrow Up
Pointer arrow Left
Pointer arrow Right
Pointer arrow Down
Pointer arrow Up
1, '2', '*', <char>
(where <char> is from 'A' to 'O') draws various character such as arrows or box construction shapes (which are all boldable), with the following logic:
Each of the bottom 4 bits of 'char' represents a direction:
bit 0 decimal 1 Left
bit 1 decimal 2 Down
bit 2 decimal 4 Right
bit 3 decimal 8 Up
If one bit is set, a pointer arrow in the relevant direction is drawn. If two bits are set, two sides of a square or a line will be drawn. If three bits are set, a 'T' shape will be drawn. If all four bits are set, a cross will be drawn.
For example, the corner generated:
VDU 1, ASC"2", ASC"*", ASC"F"
makes a reasonable logical NOT sign. Here's the complete VDU's:
1, '2', '*', 'A'
Pointer arrow Right
1, '2', '*', 'B'
Pointer arrow Down
1, '2', '*', 'C'
Corner Down Right
1, '2', '*', 'D'
Pointer arrow Left
1, '2', '*', 'E'
1, '2', '*', 'F'
Corner Left Down
1, '2', '*', 'G'
1, '2', '*', 'H'
Pointer arrow Up
1, '2', '*', 'I'
Corner Up Right
1, '2', '*', 'J'
Vertical unbroken bar
1, '2', '*', 'K'
1, '2', '*', 'L'
Corner Up Left
1, '2', '*', 'M'
1, '2', '*', 'N'
1, '2', '*', 'O'
To draw a horizontally and vertically divided window:
User Defined Characters
Finally, the screen driver may create user defined characters by the following combination which specifies the rows on a 6x8 matrix:
1, 138, '=', charcode, r0, r1, r2, r3, r4, r5, r6, r7
Note that the count of 10 is specified in numeric form with the top bit set (138) as it will not fit in a single digit. If you use a smaller count, the unspecified rows will be set to 0. This can be useful since most system characters have a blank bottom row. The number 'charcode' is the character code of the defined character and must be between 64 ('@') and 127 (DEL) inclusive providing 64 user characters on an expanded machine. On an unexpanded machine (ie. without 128K or more in slot 1) only 16 characters can be defined without encroaching on the memory used for the PipeDream map. Characters above the limit of 16, will have their definitions overwritten by map information when PipeDream is used. If the map width is 64 pixels or less then the full 64 user characters can be used.
r0 to r7 are the numeric representations of the top to bottom rows of the character, with the top bit set. Bit 5 is on the left edge of the character and bit 0 the right. The standard characters have their left-hand rows blank, so if the user-defined characters are to sit alongside them, they should follow this convention. The characters coexist with the normal characters with the same code. Doing "VDU charcode" will print the system character, whereas the sequence:
1, '2', '?', charcode
will print the user defined one. For instance:
VDU 1, 138, ASC"=", ASC"@", 155, 155, 128, 132, 132, 132, 145, 142, 128
VDU 1, ASC"2", ASC"?", ASC"@"
will draw a little smiling face to the screen.
These combinations toggle various display modes of the current window (applying to subsequently written characters):
1, 'B' Bold
1, 'C' Cursor visible
1, 'F' Flash
1, 'G' Grey
1, 'L' Caps Lock
1, 'R' Reverse video
1, 'S' Vertical scrolling
1, 'T' Tiny font
1, 'U' Underline
1, 'W' Horizontal scrolling
With vertical scrolling on, the window scrolls when the cursor tries to go outside of the window else the cursor wraps from top row to bottom row or vice versa.
With horizontal scrolling on, the row scrolls between the left and right margins when the cursor tries to go outside of the window margins else the cursor wraps from left column to right column or vice versa.
Horizontal scrolling disables vertical scrolling.
Rather than toggling these modes, they may be set or reset explicitly by prefixing them with '+' (on) or '-' (off), and a count byte of two to indicate that there are two parameters. For instance:
1, '2', '+', 'B'
sets bold on. When written in this form, modes may be combined in a list, for example:
1, '5', '-', 'B', 'F', 'T', 'U'
resets the bold, flash, tiny, underline toggles. Finally SD_DTS ($7F) deletes all toggle settings, ie. sets all toggles to off:
Changing Display Attributes
The display modes are usually set at the time of writing text to the screen, however it is possible to apply the various effects to text already present. This approach is used in the menu system to highlight commands and by the Filer to highlight files. The technique can only be used with the hardware attributes ie. flash, grey, inverse and underline. The two commands, apply and eor, work over the next <n> characters where <n> is the second parameter and offset from 32:
1, '2', 'A', 32+<n> Apply toggles
1, '2', 'E', 32+<n> EOR toggles
The following sequence inverts a 20 character bar at cursor position (0,0):
1, '2', '+', 'R', 1, '3', '@', 32, 32, 1, '2', 'E', 52
Windows on the Z88 are referred to by a single ASCII numeric character. Windows '1' to '6' are available for the user to redefine; '7' and '8' are already used by the system - window '7' is the topic area, window '8' is used by many of the system calls during error processing. Windows remember their toggle settings, but if the window area is overwritten (by text from an overlapping window, for example) then the window contents are lost. The following sequences manipulate the windows. The # character is used because it is vaguely reminiscent of a window:
Defining a Window
1, '7', '#', '<n>', 32+x, 32+y, 32+w, 32+d, t
The "type" of the window is defined by the parameter t:
bit 0 sets left and right bars on
bit 1 sets shelf brackets on
bit 2 - 6 ignored
bit 7 must be set.
The type parameter is optional. Using a count byte of '6' will define the window without bars or brackets. The PipeDream map window actually has bit 5 and 6 set and not bit 7.
This redefines window <n>, with the top left corner at (x,y) and horizontal and vertical sizes of w and d respectively. The values of x and y are offset from the top left corner of the application area ie. (10,0), but can be made absolute (ie. offset from the top left corner of the screen) by using 128+x, 128+y in the above sequence. E.g. for placing a window at top left corner of the screen use relative coordinates 32-10,32-0, absolute coordinates 128+0, 128+0 or mixed 128+0,32+0. It is very important that windows do not go beyond the width of the screen. To avoid the OZ window, windows should be no more than 94 6 pixel wide characters from the edge of the application window, or 104 from the leftmost edge of the screen.
Application windows may be preserved by using the system call OS_Sr, A=SR_SUS. This is a lazy method and consumes 2K of continuous memory - don't use use it unless strictly necessary. Further, only the screen area starting from (10,0) to the right edge (before the OZ window) are preserved. Windows beyond that has to be redrawn explicitly. In any case, application windows must be redrawn when your application is re-entered (otherwise your are faced with a blank screen). If your application uses a window that overlaps the system's topic window you must also re-draw your window after an executed command, pressing <MENU> or the <HELP> key.
The OZ window contains special characters which control the LCD scanning and if these are disturbed, the display will not work properly. If your window overlaps the OZ area, these characters are almost certain to be disturbed.
Selecting a Window
To direct output to a window use one of the three following sequences:
1, '2', 'H', '<n>' (Select and Hold)
1, '2', 'I', '<n>' (Select and Init)
1, '2', 'C', '<n>' (Select and Clear)
The 'H' version will maintain display modes, whereas the 'I' version will reset them all (ie. cursor off, scrolling disabled). If this is the first time the window has been selected (after being redefined) then the 'I' version will also clear the window. The 'C' version resets the display modes and clears the window.
Deleting a Window
The following sequence clears a window including side bars and shelf brackets:
1, '2', 'D', '<n>' (Delete/Destroy)
1, SD_DWN ($FE) Scroll downwards
1, SD_UP ($FF) Scroll upwards
Scrolling downwards is the usual direction. These commands operate over the current window, not the screen.
1, '2', 'G', '+' Grey window
1, '2', 'G', '-' Ungrey window
Please note that if you grey a window that has already been greyed then ungreying doesn't work.
Bannered Window Example
The shelf brackets are used by the system to create banners at the top of a window (see the Filer display for an example). This effect is achieved by using inverted, tiny and underlined characters. We give here an example in BBC BASIC. You can enter this program on the Z88 and see the effect of a bannered window. It is worth modifying the program to gain a feel for the way the various screen effects work and how they are accessed by the screen driver:
10 VDU 1, ASC"7", ASC"#", ASC"1", 33, 32, 72, 40, 131
20 VDU 1, ASC"2", ASC"I", ASC"1"
30 VDU 1, ASC"4", ASC"+", ASC"T", ASC"U", ASC"R", 1, ASC"2", ASC"J", ASC"C"
40 PRINT TAB(0,0);"CENTRAL BANNER"; TAB(0,0;
50 VDU 1, ASC"2", ASC"A", 72
60 VDU 1, ASC"7", ASC"#", ASC"1", 33, 33, 39, 129
70 VDU 1, ASC"2", ASC"I", ASC"1"
80 VDU 1, ASC"3", ASC"+", ASC"C", ASC"S"
Note how the window is defined one character in from the edge of the screen to allow space for the vertical bar. It is then redefined to exclude the top line of the screen, thus the banner will be undisturbed by text written to the window. The centralization is achieved by using the justification codes, which are as follows:
Justification and Margins
1, '2', 'J', 'N' Normal
1, '2', 'J', 'C' Centre
1, '2', 'J', 'L' Left align
1, '2', 'J', 'R' Right align
1, 'L', 32+n Set left margin (n=leftmost character position)
1, 'R', 32+n Set right margin (n=rightmost character position)
The default is 'normal'. Note that there is no count byte for the left and right margin commands. The non-normal justification modes only work with directly output text, in particular the system input routine fails spectacularly if it is used with these modes. It is also worth noting that if you output more characters than will fit between the margins then horisontal scrolling, and not wrapping, will occur.
The cursor position, ie. the next print position, may be moved by the following sequences - x and y are the column and row respectively, with (0,0) being the top left of the current window:
1, '3', '@', 32+x, 32+y move to x,y
1, '2', 'X', 32+x horisontal tab
1, '2', 'Y', 32+y vertical tab
Clear from Cursor
1, '2', 'C', 253 Clear from the cursor to the end of the line.
1, '2', 'C', 254 Clear from the cursor to the end of the window.
1, '3', 'N', 32+n, m Output n copies of the code m
1, '4', '!', 32+r, 32+s, 32+m
A beep sequence is a series of beeps (NOT a single beep), with mark m and space s (in 10ms ticks), and r cycles.
Toggle redirection to printer
1, '2', '.', '[' Disable CLI redirection to printer
1, '2', '.', ']' Enable CLI redirection to printer
These sequence are used to avoid sending SOH sequence to the printer when a CLI redirection to printer is active.
Low Level Access
There are three calls which can be used to obtain information about the current state of the screen and they are all accessed via OS_Nq which takes a reason code in the BC register. A fuller description is given in "Miscellaneous useful routines", but the reason codes of interest are:
NQ_WBOX return the size of a window
NQ_WCUR return the cursor location in a window and indicate whether enabled
NQ_RDS read characters from the screen starting at the cursor position