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.