Use of Operating System Calls

OSWRCH

OSWRCH entry: &FFEE vector: &20E

This call writes the character whose ASCII code is in the accumulator to the screen.

Here is an example which will print the character L on the screen:

 10 P% = &70
 20 [OPT 3
 30 .Start LDA #76     \Load accumulator with ASCII code for L
 40 JSR &FFEE          \Jump to OSWRCH
 50 RTS
 60 ]
 70 CALL Start

OSWRCH is also used with ASCII control codes (from 0 to 31). If you change line 30 to:

30.Start LDA #7

then the program will output ASCII character 7, which is a 'beep'.

Some BASIC instructions have ASCII values in the control code range, and these can therefore be used with OSWRCH. For example, PLOT has an ASCII value of 25, TAB an ASCII value of 31.

The following program uses TAB to print the character L half way across the screen:

 10 p% = &70
 20 [ OPT 2
 30 .Start LDA #31
 40 JSR &FFEE
 50 LDA #19
 60 JSR &FFEE
 70 LDA #VPOS
 80 JSR &FFEE
 90 LDA #76
100 JSR &FFEE
110 RTS
120 ]
130 CALL Start

Each parameter is passed in turn to OSWRCH via the accumulator. The BASIC statement equivalent to the above program is:

PRINT TAB(19);"L";

(Note that this program will not work with OPT 3 because VPOS is affected.)

The BASIC instruction PLOT takes three parameters, PLOT A,X,Y. However, X may be 0 to 1279 and Y may be 0 to 1023, so each must be represented by two bytes. That means that an OSWRCH call with the accumulator set to 25 must be followed by five more OSWRCH calls to pass the parameters. The following program will plot a line on the screen:

 10 MODE 4
 20 P% = &70
 30 [ OPT 3
 40 .Start LDA #25
 50 JSR &FFEE
 60 LDA #5
 70 JSR &FFEE
 80 LDA #88
 90 JSR &FFEE
100 LDA #2
110 JSR &FFEE
120 LDA #44
130 JSR &FFEE
140 LDA #1
150 JSR &FFEE
160 RTS
170 ]
180 CALL Start

This program is equivalent to

PLOT5,600,300

Lines 100 and 80 give X (2*256 + 88) and lines 140 and 120 give Y (1*256 + 44).

You'll see from the listing that the above routine, when assembled, occupies memory from &70 to &8E. Remember that user programs must use zero-page locations only between &70 and &8F, so this is almost the largest size routine that may be stored in the zero-page.

OSASCI

OSASCI entry: &FFE3

Writes the character whose code is in the accumulator to the screen using OSWRCH. However, if the accumulator contains &D then OSNEWL is called instead. The actual code at location &FFF3 is:

.OSASCI     CMP #&D
            BNE OSWRCH
.OSNEWL     LDA #&A
            JSR OSWRCH
            LDA #&D
.OSWRCH     JMP(WRCHV)

OSNEWL

OSNEWL entry: &FFE7

This call issues a line feed/carriage return to the screen, as shown above.

After using OSWRCH, OSASCI or OSNEWL, the contents of the accumulator X and Y registers are unchanged. Flags C, N, V and Z are undefined, and D=0.

OSRDCH

OSRDCH entry: &FFE0 vector: &210

This call reads a character code from the keyboard into the accumulator.

After using OSRDCH, the contents of the X and Y registers are unchanged. Flags N, V and Z are undefined, and D=0. Flag C tells whether the read has been successful (C=0). If C=1 then an error has occurred and the error number is given in the accumulator. If C=1 and A=&1B then an escape condition has been detected and you must acknowledge this by performing an OSBYTE call with A=&7E or *FX126.

OSCLI

OSCLI entry: &FFF7 vector: &208

This call is used by the BASIC OSCLI instruction. From assembler it consists of a JSR to &FFF7, the command line string being placed in memory at a location given by the contents of the X register (address low byte) and Y register (address high byte). The command line string must be terminated by &D RETURN.

The following BASIC program illustrates this:

 10 DIM address 20
 20 keynumber=4
 30 $address="KEY"+STR$ keynumber+"LIST |M"
 40 X%=address MOD 256
 50 Y%=address DIV 256
 60 CALL &FFF7

This will have the same effect as

*KEY 4"LIST|M"

Note: The string indirection operator $ automatically puts a RETURN code (&D) after the string. EQUS however does not, and it must be inserted afterwards using EQUB &D or something like EQUS"FRED"+CHR$13.

OSFIND

OSFIND entry: &FFCE vector: &21C

Opens a file from cassette or disc for reading or writing. The contents of the accumulator determine the operation performed:

A=0 close a file or files (CLOSE#).
A=&40 opens a file for input (OPENIN).
A=&80 opens a file for output (OPENOUT)
A=&C0 opens a file for input or output (OPENUP).

When OPENIN, OPENUP or OPENOUT is used, X and Y must contain the address of the filename. After the subroutine call, the accumulator will contain the channel number allocated to that file by the Operating System.

If CLOSE# is used then Y must contain the channel number of the file to be closed. If Y is 0 then all files will be closed.

OSBPUT

OSBPUT entry: &FFD4 vector: &218

Writes the byte contained in the accumulator to the cassette or disc file (same as BPUT#). Y must contain the file channel number. After using OSBPUT, the contents of the accumulator, X and Y registers are unchanged.

OSBGET

OSBGET entry: &FFD7 vector: &216

Reads a byte from the cassette or disc file into the accumulator (same as BGET#). Y must contain the file channel number. After using OSBGET, the contents of the X and Y registers are unchanged. Flags N, V and Z are undefined, and D=0. Flag C tells whether the read has been successful (C=0). If C=1 then an error has occurred and the error number is given in the accumulator. If C=1 and A=&FE then the end of file has been reached.

OSFILE

OSFILE entry: &DDFF vector: &212

Allows a whole file to be loaded or saved. The contents of the accumulator indicate the function to be performed. X and Y point to an 18 byte control block anywhere in memory, the structure of which is as follows:

OSFILE Control Block
 
00   Address of file name, which must be terminated by &0D   LSB
01     MSB
 
02   Load address of file   LSB
03      
04      
05     MSB
 
06   Execution address of file   LSB
07      
08      
09     MSB
 
0A   Start address of data for write operations, or length of file for read operations   LSB
0B      
0C      
0D     MSB
 
0E   End address of data, that is byte after last byte to be written or file attributes   LSB
0F      
10      
11     MSB

The table below indicates the function performed by OSFILE for each value of A.

A=0 Save a section of memory as a named file. The file's catalogue information is also written.
A=1 Write the catalogue information for the named file.
A=2 Write the load address (only) for the named file.
A=3 Write the execution address (only) for the named file.
A=4 Write the attributes (only) for the named file.
A=5 Read the named file's catalogue information. Place the file type in A.
A=6 Delete the named file.
A=&FF Load the named file and read the named file's catalogue information.

Note: Values 1 to 6 are not available on a cassette filing system.

OSBYTE

OSBYTE entry: &FFF4 vector: &20A

This is a family of Operating System calls which includes all the *FX calls available from BASIC. (These are not repeated here.) The call number is passed in the accumulator and parameters are passed in X or Y or both.

All OSBYTE calls are available from BASIC via a USR call, or by using a *FX call.

Here is a list of functions as given by each accumulator value (A):

A = 127 (EOF#) *FX127

Gives the end of file status of a previously opened file. X must contain the file channel number. Afterwards, X will be zero if the end of file has not been reached, non-zero if the end of file has been reached.

A = 129 (INKEY) *FX129

Either waits for a character from the keyboard buffer until a time limit expires (INKEYnegative). All the discussion about auto-repeat and buffer flushing applies to this call.

For INKEYpositive, Y must contain the most significant byte of the delay, and X the least significant (in hundredths of a second).

Afterwards, if Y=0 then a character has been detected and its code appears in X. Y=&1B indicates that ESCAPE was pressed and must be acknowledged with *FX126. Y=&FF indicates that no key was pressed in the allocated time.

A = 131 (OSHWM) *FX131

Gives the address of the first free location in memory above that required for the Operating System. Usually equal to &E00. The address is given in X (low byte) and Y (high byte). For example, after *FX20,.

A = 132 *FX132

Gives the lowest memory address used by the screen display in X (low byte) and Y (high byte).

A = 133 Low mode address *FX133

Gives the lowest address in memory used by a particular mode. Does not change mode but merely investigates the consequences of doing so. The mode to be investigated must be in X. Afterwards, the address is contained in X (low byte) and Y (high byte).

A = 134 Read position of text cursor *FX134

Gives in X the X coordinate of the text cursor, and in Y the Y co-ordinate (same as POS and VPOS),

A = 135 Read character at position of text cursor *FX135

Gives in X the ASCII code of the character at the current text cursor position, and in Y the current mode number. X is 0 if the character is not recognisable.

Here is a BASIC function which can be used to read the character at any position X,Y on the screen:

1000 DEF FNreadcharacter(column%,row%)
1100 LOCAL A%,currentX%,currentY%,character%
1200 currentX% = POS:currentY% = VPOS
1300 VDU31,column%,row%
1400 A% = 135
1500 character% = (USR(&FFF4) AND &FF00) DIV &100
1700 VDU31,currentX%,currentY%
1800 = CHR$ character%

To give the character at position X,Y this function would be called by passing X and Y as the two parameters:

PRINT FNreadcharacter(X,Y)

A = 137 Motor control *FX137

Similar to *MOTOR. X=0 will turn off the cassette motor, X=1 will turn it on.

A = 138 (*OPT) *FX138

Exactly the same as *OPT. The parameters are passed in X and Y.

A = 145 Get character from keyboard buffer *FX145

Reads a character code from a buffer into the Y register. X=buffer number (0 to 9 inclusive). C=0 indicates a successful read, C=1 indicates that the buffer is empty.

A = 218 Cancel VDU queue *FX218

Many VDU codes expect a sequence of bytes (as shown earlier with PLOT and TAB). This call signals the VDU software to throw away the bytes it has received so far. Before use, X and Y must contain zero.

OSWORD

OSWORD entry: &FFF1 vector: &20C

This is a family of Operating System calls which uses a parameter block somewhere in memory to supply data to the routine and to receive results from it. The exact location of the parameter block must be specified in X (low byte) and Y (high byte). The accumulator contents determine the action of the OSWORD call.

A = 0 Read a line from keyboard to memory.

Accepts characters from the keyboard and places them at a specified location in memory. During input the DELETE key (ASCII 127) deletes the last character entered, and CTRL-U (ASCII 21) deletes the entire line. The routine ends if RETURN is entered (ASCII 13) or the ESCAPE key is pressed.

The control block contains five bytes:

YX (low byte) Address at which
YX+1 (high byte) line is to be stored
YX+2   Maximum length of line
YX+3   Minimum acceptable ASCII value
YX+4   Maximum acceptable ASCII value

Characters will only be entered if they are in the range specified by YX+3 and YX+4.

Afterwards, C=0 indicates that the line was terminated by a RETURN. C not equal to zero indicates that the line was terminated by an ESCAPE. Y is set to the length of the line, excluding the carriage return if C=0.

A=1 Read clock

Reads the internal elapsed-time clock into the five bytes pointed to by X and Y. The clock is incremented every hundredth of a second, and is used by the BASIC variable TIME.

A=2 Write clock

Sets the internal elapsed-time clock to the value given in the five bytes pointed to by X and Y. Location YX is the least significant byte of the clock, YX+4 is the most significant.

A=3 Read interval timer

In addition to the clock there is an interval timer which is also incremented every hundredth of a second. The interval is stored in five bytes pointed to by X and Y. See OSWORD with A=1.

A=4 Write interval timer

X and Y point to five bytes which contain the new value to which the clock is to be set. The interval timer may cause an event when it reaches zero. Thus setting the timer to &FFFFFFFFFD would cause an event after three hundredths of a second.

A=7 SOUND

Equivalent to the BASIC SOUND statement. The eight bytes pointed to by X and Y contain the four two-byte parameters (in fact only the least significant byte of each need be used).

YX Q (channel, 0 to 3)
YX+1 zero
YX+2 A (envelope, -15 to 4)
YX+3 zero, or &FF if -1 or some other negative value
YX+4 P (pitch, 0 to 255)
YX+5 zero
YX+6 D (duration, 1 to 255)
YX+7 zero

A=8 ENVELOPE

Equivalent to the BASIC ENVELOPE statement, X and Y point to 14 bytes of data which are the 14 parameters used by ENVELOPE.

A=9 POINT

Equivalent to BASIC POINT function. The parameter block pointed to by X and Y must be set up as follows:

YX X (low byte) coordinate
YX+1 X (high byte) coordinate
YX+2 Y (low byte) coordinate
YX+3 Y (high byte) coordinate

Afterwards, YX+4 will contain the logical colour value of that particular graphics coordinate. If the coordinate is off the screen then YX4 contains &FF.

A = 10 Read character definition

Characters are displayed on the screen as an 8 × 8 matrix of dots. The pattern of dots for each character, including user-defined characters, is stored as eight bytes. This call enables the eight bytes to be read into a block of memory starting at the address given in X and Y, plus 1. The ASCII code of the character must be the first entry on the parameter block when the routine is called.

Afterwards, the parameter block contains data as shown below:

YX Character code
YX+1 Top tow of displayed character
YX+2 Second row of displayed character
...  
...  
...  
YX+8 Bottom row of displayed character

Here is a program to illustrate this OSWORD call, and the method of calling OSWORDs in general. It takes each of the characters in turn, reads the matrix definition, and then reverses this definition by shifting the bits in each byte, and then redefining each character using VDU 23. The result makes your program interesting to read, to say the least!

 10 MODE 6
 20 DIM Q% 100
 30 FOR I=0 TO 3 STEP 3
 40 P%=Q%
 50 [OPT I
 60 .Character LDA &601  \Take low address of parameter
 70 STA &70              \and store it in zero-page.
 80 LDA &602             \Take high address of parameter
 90 STA &71              \and store it in zero-page.
100 LDY #0               \Clear Y register.
110 LDA (&70),Y          \Get parameter (ASCII code)
120 STA &70              \and store it in zero-page.
130 LDX #&70             \Set X to OSWORD parameter block address low byte.
140 LDA #10              \Set OSWORD function.
150 JSR &FFF1            \Jump to OSWORD.
160 LDX #0               \Clear X register.
170 .Reverse LDY #8      \Set Y to 8 as shift counter.
180 .Loop ASL &71,X      \Shift each byte into the byte
190 ROR &70,X            \below, thereby reversing it.
200 DEY                  \Decrement Y.
210 BNE Loop             \Repeat if not zero.
220 INX                  \Increment X.
230 CPX #8               \Compare with 8.
240 BNE Reverse          \Repeat if not equal.
250 RTS
260 ]
270 NEXT
280 *FX20,6
290 FOR I%=33 TO 126
300 PRINT CHR$ I%
310 CALL Character,I%
320 VDU23,I%,?&77,?&76,?&75,?&74,?&73,?&72,?&71,?&70
330 NEXT

This program illustrates a number of the features demonstrated in this part of the book. It calls the machine-code routine Character with a parameter, and lines 60 to 120 transfer the parameter to location &70, which is a safe place at which to store any OSWORD data.

Line 130 sets X to the low byte of the address of the OSWORD parameter block (it is not necessary to set Y because Y is already zero).

Lines 140 and 150 carry out the OSWORD call.

Lines 160 to 240 reverse each of the bytes of the character definition.

Line 280 explodes the character memory allocation to its maximum allowing all the characters to e redefined, and line 320 carries out this redefinition.

It should be noted that if this program were more than &300 bytes long, it would get overwritten by the soft characters.

A=11 Read colour assigned to logical value

Gives the actual colour value assigned to the logical colour value contained in the location pointed to by X and Y. Afterwards, location YX will contain the logical value, and location YX+1 contain the actual value. In fact YX+1 to YX+4 contain the four-byte physical colour - you must reserve space for five bytes.

Events

Events are conditions which occur within the computer and which can be trapped by the user so as to provide useful information. For example, it is possible to detect when ESCAPE has been pressed.

To be able to act upon an event, that event must first be enabled by *FX14:

*FX14,0 enables output buffer empty event.
*FX14,1 enables input buffer full event.
*FX14,2 enables character entering keyboard buffer event.
*FX14,4 enables start of vertical synchronisation of screen display event.
*FX14,5 enables the interval timer crossing zero event.
*FX14,6 enables ESCAPE pressed event.

The Operating System detects all the above events when they occur, but ignores them if they have not been enabled with the appropriate *FX14 call. If an event occurs which has been enabled then program execution indirects via &220 and places an event code (shown below) in the accumulator. The contents of X and Y may also depend upon the event.

The event codes are as follows:

A=0 Output buffer empty. (X contains buffer identity.)
A=1 Input buffer full. (X contains buffer identity.)
A=2 Key pressed.
A=4 Vertical synchronisation of screen display.
A=5 Interval timer crossing zero.
A=6 ESCAPE detected.

Any address may be stored in the two bytes &220 and &221 to which the program will transfer execution on detection of an enabled event. You may write your own code at this address in order to process the event, but it must be terminated by RTS, and should not take too long (one millisecond maximum).

Each of the events may be disabled by a corresponding *FX13. For example, *FX13,1 will disable the input buffer full event.