Contents / Reference section / Previous chapter / Next chapter / Index


43 Assembly language

Why use an assembly language rather than BASIC?

BASIC is a relatively easy language to learn and another of its great strengths is that when you make a mistake the computer tries to give you some helpful message explaining what you have done wrong - or what it is incapable of doing.

But there are prices to be paid for this "friendliness" and sometimes one has to communicate with the computer in its own language (machine code) or a low-level language like assembly language - despite all the problems this can bring. Assembly language consists of statements like

LDA #03

which means "Load the accumulator with 3" or

JSR &6000

which means "Jump to the subroutine at location 6000 hex"

An Assembler is a computer program which converts these assembly language "mnemonics" into the Machine code numbers that the computer actually understands.

Assembly language is used where high speed is vital or where the minimum amount of memory must be used. BASIC is used where ease of programming is more important than either speed or memory requirements. There are many other computer languages each with their own strengths and weaknesses.

It is unfortunately not possible, in a book this size, to teach you to write in assembly language, even though the BASIC interpreter contains an assembler. However those familiar with 6502 assembly language will need a good deal of information to enable them to write effective programs. The rest of this section explains, for those familar with 6502 assembly, the main features of the machine code access routines.

As the reader will be aware 16K of ROM is set aside for the Machine Operating System (MOS). The MOS looks after all input/output and is accessed via a set of well defined entry points, many of which are vectored via RAM.

Access is provided to all graphics and text features including MODE selection, line drawing, text and graphic windowing, redefinition of characters, 6845 CRTC registers, flash rate control.

Access is also provided to sound generation, clock set/read, parallel/serial printer I/O, baud rate selection, ADC results and event completion flags.

The machine code programmer may select a different file system (tape, disk or net) and may perform a wide range of file input/output to the selected file system.

Considerable efforts have been made to give the assembly language programmer access to entry points. Professional programmers will understand the need to use the entry points provided. These will remain static and will work in all configurations. The assembler is part of the BASIC interpreter and can be entered at any time by placing a left hand square bracket [ as a BASIC statement. The end of the assembly language section is indicated by a right hand square bracket ]. Note that in MODE 7, the Teletext mode, these symbols are displayed as left and right arrows ? and ? respectively.

10 PRINT "THIS IS BASIC"

20 [

30 JSR &FFE7

40 ]

(Do not try this program - it is incomplete and could prove 'fatal')!

In the example given the assembler has been given no indication of where to store the assembled machine code. The "program counter" is set by the variable P% and this can be set to any desired value e.g.

10 PRINT "THIS IS BASIC"

15 P%=&7000

20 [

30 JSR &FFE7

40 ]

50 PRINT "AND THIS IS BASIC TOO"

>RUN

THIS IS BASIC

7000

7000 20 E7 FF JSR &FFE7

AND THIS IS BASIC TOO

However another, and much more useful, way is provided to enable the programmer to allocate a few bytes of memory to a piece of machine code. A special version of the DIM statement is used.

10 PRINT "THIS IS BASIC"

15 DIM GAP% 20

16 P%=GAP%

20 [

30 JSR &FFE7

40 ]

50 PRINT "AND THIS IS BASIC TOO"

>RUN

THIS IS BASIC

0E74

0E74 20 E7 FF JSR &FFE7

AND THIS IS BASIC TOO

The above program dynamically allocates a gap of 21 bytes and the address of the start of the block is given in the variable GAP%. This is the safe way of inserting machine code sections into BASIC programs. Of course the actual location of the routine will change if more BASIC statements are inserted in the program before the DIM statement.

10 PRINT "THIS IS BASIC"

12 REM INSERT AN EXTRA LINE

15 DIM GAP% 20

16 P%=GAP%

20 [

30 JSR &FFE7

40 ]

50 PRINT "AND THIS IS BASIC TOO"

>RUN

THIS IS BASIC

0E8F

0E8F 20 E7 FF JSR &FFE7

AND THIS IS BASIC TOO

Two BASIC statements provide access to machine code routines: CALL and USR. The USR statement provides an easy way of passing values to and from the microprocessor's registers before and after the machine code call. When the statement

R = USR(Z)

is executed the computer first transfers values from the BASIC variables X%, Y%, A% and C% into the processor's X and Y register, accumulator and carry flag respectively. Of course, only the least significant byte of X%, Y% and A% can be used and only the least significant bit of C% can be copied into the carry flag.

After the various registers have been set up, control is passed to the machine code subroutine (at address Z). This routine must end with an RTS if control is to return to BASIC. On return the A, X and Y registers and the program status register (P) are used to produce a number which is placed in the BASIC variable R.

This can be neatly illustrated by a short program which uses an OSBYTE (Operating System Byte) call to determine the position of the text cursor on the screen.

30 A%=&86

40 X%=0

50 R=USR(&FFF4)

60 PRINT ~R

Lines 30 and 40 ensure that the accumulator and X register will be set up correctly. Line 50 obtains the result in R and line 60 prints the result in hexadecimal. A typical result would be

B1120086

B1 has come from the program status register (P)

12 from the Y register which gives the Y co-ordinate of the cursor

00 from the X register giving the X co-ordinate

86 from accumulator which was set up from A%

The CALL statement provides much more flexible access to machine code routines. CALL does not return a calculated result into a BASIC variable. However, the user can, if he or she wishes, make any BASIC variable or group of variables available to the machine code routine. If a list of BASIC variables follows the CALL statement then a "parameter block" will be set up starting at location &600. The sole purpose of this block is to let a machine code routine know where variables are stored in memory and what type each variable is.

The demonstration program which follows shows the use of the CALL statement and also of the error handling features.

10 REM Demonstration of CALL with a parameter

20 PROCINIT

30 A%=R12345678

40 CALL SWAP,A%

50 PRINT A%

60 END

70 DEF PROCINIT

80 DIM Q% 100

90 FOR C=0 TO 2 STEP 2

100 PAR=&600: REM Parameter block

110 ZP=&80: REM Usable zero page

120 P%=Q%

130 [OPT C

140 .SWAP LDA PAR

150 CMP#1:BEQ OK

160 .ERRO BRK

161 ]

162 ?P%=45

163 $(P%+1)="Swap Parameters"

164 P%=P%+16

170 [OPT C:BRK

180 .OK LDA PAR+3:CMP #4:BNE ERRO

190 LDA PAR+1:STA ZP

200 LDA PAR+2:STA ZP+1

210 LDY#3:LDX#0

220 LDA(ZP,X):PHA:LDA(ZP), Y:STA(ZP,X):PLA:STA(ZP),V

230 RTS

240 ] NEXT

250 ENDPROC

Lines 10 to 60 form the main BASIC program. This uses a machine code program to swap parts of an integer number around. Line 20 calls a procedure which assembles a piece of machine code. Line 30 sets A% to some arbitrary value and then line 40 calls the machine code subroutine to swap around. Line 50 shows A% after it has been "vandalised".

The procedure itself (lines 70 to 250) assembles the assembly language mnemonics into machine code. Line 80 tells the computer to allocate 101 bytes of memory and to set Q% to the address of the memory allocated. Lines 90 and 240 form a FOR...NEXT loop which makes the computer assemble the code twice - the first pass to work out the addresses of the labels, and the second pass to put them into code. Line 120 sets the program counter at the start of each of the two passes. Lines 140 and 150 check to see that the value in location &600 is 1. Location &600 contains the "number of parameters" and our machine code routine is only meant to have one parameter. See page 214 for information about parameter passing from CALL. If location &600 does not contain 1 then control will pass to the routine labelled ERRO (line 160).

This routine shows how a user supplied routine can produce standard BASIC error messages and error numbers. This is explained further on page 464. Suffice it to say that here the code will generate BASIC error number 45 (a new one) and will issue the message Swap Parameters before returning control to BASIC. Line 164 increments the internal program counter enough to allow space for the messages etc.

Line 170 re-enters normal assembler operation and re-sets the OPT. Line 180 checks to see that the parameter is of type 4 (an integer variable - see page 211) and if not reports an error. Lines 190 to 220 perform the actual swap and line 230 ensures an orderly return to BASIC.

The BASIC assembler

The assembler is entered with a statement starting with [ and exited with ]. In Teletext mode these characters are displayed as 'arrow left' and 'arrow right'. In the assembly language section the pseudo-operation OPT can be used to control listing and error reporting. See the keyword OPT for more details, in brief:

OPT 0 gives no errors and no listing

OPT 1 gives no errors and a listing

OPT 2 reports errors but gives no listing

OPT 3 reports errors and gives a listing.

Normally the "first pass" should be with OPT 0 and the "second pass" with OPT 2. If a listing is required then the "second pass" should be OPT 3.

Labels are defined by preceding them with a full stop. No full stop is required when they are referenced.

	LDA #8
.LOOP	BIT VIA+&D
	BNE LOOP

Labels are normal BASIC variable names. Thus they should start with a letter and not start with a reserved word. During the "first pass" unknown labels will generate an address of the current op-code. Thus JMP and branch instructions will jump or branch to themselves.

To enter byte or string constants the programmers should leave the assembler temporarily and use indirection operators. For example

JMP LABEL1

]

$P%="TODAY"

P%=P%+6

.LABEL1 LDA&

Remembering that the BASIC variable P% is used to store the value of the program counter, the string indirection operator ($) can be used to insert ASCII characters directly into memory. The word TODAY will be copied to memory followed by a carriage-return (&0D) -, a total of 6 characters. If you wish to follow the word by a byte containing zero instead of &0D you could write

$P%="TODAY"

P%?5=0

P%=P%+6

To insert numeric values directly into memory you can use byte or word indirection operators:

JMP LABEL2

]

?P%=10

P%?1=20

P%?2=66

P%=P%+3

[

The various 6502 addressing modes are indicated in assembly language in the standard way, but using an ampersand to represent a hexadecimal number.

Addressing mode Examples
Immediate LDA #4 LDA #VALUE
Absolute JMP &8000 LDA LOCATION
Zero page LDA &FC STY TEMPI
Accumulator ASL A
Implied RTS
Pre-indexed indirect LDA (5,X) CMP(OFFSET, X)
Post-indexed indirect LDA (&84),Y ORA (TABLE),Y
Zero page X LDA 10,X INC CNT,X
Absolute X BNE &3210 BNE LOOP3
Relative Indirect JMP (&20E) JMP (WRCHV)
Zero page indexed with Y LDX 24,Y LDX VAR,Y

Comments can be inserted into assembly language by preceding them with a backslash. On the keyboard and in modes 0 to 6 this is shown as \ whereas in Teletext mode it is displayed as one-half (½). In BASIC everything after a REM statement is ignored on the current line so it is not possible to put another statement on the same line. Thus

10 REM HELLO : PRINT "FRIDAY"

would not print the word FRIDAY. However, in assembly language a comment terminates at the end of the statement. All the following statements would be assembled

LDA #&10 \MASK :.LOOP BIT VIA+13

\INTERRUPT FLAG :BEQ LOOP

Machine code entry points

The BBC computer is unusual in a number of respects, not least because of the care taken to ensure that everything that can be done by programs written in the "Input/Output processor" (the BBC computer) can also be done in the "second processor" which is on the far side of the Tube®.

If a piece of machine code alters a particular memory location that controls the screen display directly, then that same piece of machine code will not work in the second processor because the screen will not be affected by the memory location in the second processor.

It is vital that programmers avoid reading and writing to specific memory locations such as the screen memory, zero page locations used by BASIC and memory mapped input/output devices. System calls are provided to enable you to access all these important locations and use of these system calls will ensure that your programs interact successfully with the machine. Don't feel that we are trying to hide anything from you, on the contrary we are offering you access to all the I/O routines that BASIC uses! Cultivate the habit of using system calls and then you will not need to re-write your code when you move it to the second processor.

The operating system calls

Machine code user programs should communicate with the operating system by calling routines in the address range &FF00 to &FFFF. These routines then call a specific internal routine whose address may change in different operating systems. The address of the specific routine is held in RAM between locations &200 and &2FF. The user may change the address held in these RAM locations to intercept any operating system call he wishes.

Thus the "output the character in A" routine is entered at &FFEE in all environments. The routine indirects through location &20E in all machines. The contents of locations &20E and &20F will vary depending on the machine and the version of the operating system. In one particular machine the address in &20E and &20F is &E1DC which is the local internal address of the normal "output character in A" routine.

Parameters are passed to the routines in various ways using either the 6502 A, X and Y registers, zero page locations or a parameter block. All routines should be called with a JSR and with the decimal flag clear (i.e. in binary mode).

In the detailed descriptions which follow A refers to the accumulator, X and Y refer to the registers,

C, D, N, V, and Z refer to the processor flags.

Below is a summary of operating system calls and indirection vectors.

Routine Vector Summary of function
Name Address Name Address
UPTV 222 User print routine
EVNTV 220 Event interrupt
FSCV 21E File system control entry
OSFIND FFCE FINDV 21C Open or close a file
OSGBPB FFD1 GBPBV 21A Load or save a block of memory to a file
OSBPUT FFD4 BPUTV 218 Save a single byte to file from A
OSBGET FFD7 BGETV 216 Load a single byte to A from file
OSARGS FFDA ARGSV 214 Load or save data about a file
OSFILE FFDD FILEV 212 Load or save a complete file
OSRDCH FFE0 RDCHV 210 Read character (from keyboard) to A
OSASCI FFE3 ?? ?? Write a character (to screen) from A plus LF if (A)=&0D
OSNEWL FFE7 ?? ?? Write LF,CR (&0A,&0D) to screen
OSWRCH FFEE WRCHV 20E Write character (to screen) from A
OSWORD FFF1 WORDV 20C Perfrom miscellaneous OS operation using control block to pass parameters
OSBYTE FFF4 BYTEV 20A Perfrom miscellaneous OS operation using registers to pass parameters
OSCLI FFF7 CLIV 208 Interpret the command line given
IRQ2V 206 Unrecognised IRQ vector
IRQ1V 204 All IRQ vector
BRKV 202 Break vector
USERV 200 Reserved

FILES

Files are treated as a sequence of 8 bit bytes. They can be accessed in one operation (using OSFILE) or in blocks (using OSGBPB) or a byte at a time (using OSBGET and OSBPUT). The following attributes may be associated with each file.

Load address is the address in memory to which the file should normally be loaded. This can be over-ridden when the file is loaded, if necessary.

Execution address is meaningful only if the file contains executable machine code, in which case it is the address where execution should start. If the file contains a high level language program then the execution address in unimportant.

Length is the total number of bytes in the file. It may be zero.

Pointer is an index pointing to the next byte of data that is to be processed. The value of "Pointer" may be read or written (using OSARGS), and it does not indicate whether the appropriate byte has yet been transferred from file to memory or vice versa. Pointer is automatically updated by 0SBGET and OSBPUT.

OSFIND

Opens a file for writing or reading and writing. The routine is entered at &FFCE and indirects via &21C. The value in A determines the type of operation.

A=0 causes a file or files to be closed.
A=&40 causes a file to be opened for input (reading).
A=&80 causes a file to be opened for output (writing).
A=&C0 causes a file to be opened for input and output (random access).

If A=&40, &80 or &C0 then Y(high byte) and X(low byte) must contain the address of a location in memory which contains the file name terminated with CR (&0D). On exit Y will contain the channel number allocated to the file for all future operations. If Y=0 then the operating system was unable to open the file.

If A=0 then a file, or all files, will be closed depending on the value of Y. Y=0 will close all files, otherwise the file whose channel number is given in Y will be closed.

On exit C, N, V and Z are undefined and D=0. The interrupt state is preserved, however interrupts may be enabled during the operation.

OSGBPB

The operating system call to get or put a block of bytes to a file which has been opened with OSFIND. The routine is entered at &FFD1 and vectors via &21A. It is not available on cassette systems, and is documented with the disc system.

OSBPUT

Writes (puts) a byte in A to the file previously opened using OSFIND. The routine is entered at &FFD4 which indirects through &218. On entry Y contains the channel number allocated by OSFIND.

On exit A, X and Y are preserved, N, V and Z are undefined and D=0. The interrupt state is preserved but interrupts may be enabled during the operation.

OSBGET

Gets (reads) a byte from a file into A. The file must have been previously opened using OSFIND and the channel number allocated must be in Y. The routine is entered at &FFD7 which indirects via &216.

On exit C=0 indicates a valid character in A. C=1 indicates an error and A indicates the type of error, A=&FE indicating an end-of-file condition. X and Y are preserved, N, V and Z are undefined and D=0. The interrupt state is preserved but interrupts may be enabled during the operation.

OSARGS

This routine enables a file's attributes to be read from file or written to file. The routine is entered at &FFDA and indirects via &214. On entry X must point to four locations in zero page and Y contains the channel number.

If Y is non-zero then A will determine the function to be carried out on the file whose channel number is in Y.

A= 0 read sequential pointer
A= 1 write sequential pointer
A= 2 read length
A=&FF "ensure" that this file is up to date on the media

If Y is zero then the contents of A will determine the function to be carried out.

A= 0 will return, in A, the type of file system in use. The value of A on exit has the following significance

0- no file system

1- 1200 baud cassette file system

2- 300 baud cassette file system 3 ROM pack file system

4- Disc file system

5- Econet file system

6- Teletext/Prestel Telesoftware file system

A= 1 return address of the rest of the command line in the zero page locations
A=&FF "ensure" that all open files are up to date on the media.

On exit X and Y are preserved, C, N, V and Z are undefined and D=0. The interrupt state is preserved but interrupts may be enabled during the operation.

OSFILE

This routine, by itself, allows a whole file to be loaded or saved. The routine is entered at &FFDD and indirects via &212.

On entry A indicates the function to be performed. X and Y point to an 18 byte control block anywhere in memory. X contains the low byte of the control block address and Y the high byte. The control block is structured as follows from the base address given by X and Y.

OSFILE control block

00
01
Address of file name, which must be terminated by &0D LSB
MSB
02
03
04
05
Load address of file LSB


MSB
06
07
08
09
Execution address of file. LSB


MSB
0A
0B
0C
0D
Start address of data for write operations, or length of file for read operations LSB


MSB
0E
0F
10
11
End address of data, that is byte after last byte to be written or file attributes. LSB


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

When loading a file the byte at XY+6 (the LSB of the execution address), determines where the file will be loaded in memory. If it is zero then the file will be loaded to the address given in the control block. If non-zero then the file will be loaded to the address stored with the file when it was created.

The file attributes are stored in four bytes. The least significant 8 bits have the following meanings

Bit Meaning
0 not readable by you
1 not writable by you
2 not executable by you
3 not deletable by you
4 not readable by others
5 not writable by others
6 not executable by others
7 not deletable by others

File types are as follows

0 nothing found
1 file found
2 directory found

A BRK will occur in the event of an error and this can be trapped if required. See page 464 which deals with BRK handling.

On exit X and Y are preserved, C, N, V and Z are undefined and D=0. The interrupt state is preserved but interrupts may be enabled during the operation.

OSRDCH

This routine reads a character from the currently selected input stream into A. The routine is called at location &FFE0 and indirects via &210. The input stream can be selected by an OSBYTE call with A=2. See page 421.

On exit C=0 indicates a successful read and the character will be in A. C=1 indicates an error and the error type is returned in A. If C=1 and A=&1B then an escape condition has been detected and the user must at least acknowledge this by performing an OSBYTE call with A=&7E; BASIC will normally do this for you. X and Y are preserved, N, V and Z are undefined and D=0. The interrupt state is preserved.

OSASCI

This routine writes the character in A to the currently selected output stream by using OSWRCH. However, if A contains &0D then OSNEWL is called instead. The actual code at location &FFE3 is

FFE3 C9 0D OSASCI CMP #&0D
FFE5 D0 07 BNE OSWRCH
FFE7 A9 0A OSNEWL LDA #&0A
FFE9 20 EEFF JSR OSWRCH
FFEC A9 0D LDA #&0D
FFEE 6C 0E02 OSWRCH JMP(WRCHV)

On exit A, X and Y are preserved, C, N, V and Z are undefined and D=0. The interrupt state is preserved.

OSNEWL

This call issues a LF CR (line feed, carriage return), to the currently selected output stream. The routine is entered at &FFE7.

On exit X and Y are preserved, C, N, V and Z are undefined and D=0. The interrupt state is preserved.

OSWRCH

This call writes the character in A to the currently selected output stream. The output stream may be changed using an OSBYTE call with A=3. See page 422 for more details. OSWRCH is entered at location &FFEE and indirects via &20E.

On exit A, X and Y are preserved, C, N, V and Z are undefined and D=0. The interrupt state is preserved but interrupts may be enabled during the operation.

All character output from BASIC, the operating system and anything else uses this routine. It is, therefore, easy to pass all output to a user provided output routine by placing the address of the user routine at WRCHV (&20E). However, the user should note that all control characters have special significance. For example, &1C is followed by 4 bytes which defines a text window. See the section on VDU codes for a complete listing of control characters. If the user wishes to intercept any control characters then his or her routine must check for all control characters. The routine must arrange to

skip however many bytes follow a particular code since these bytes might, inadvertently, contain a control code. For example, the BASIC statement

GCOL 1,3

is passed to the operating system as a string of bytes through OSWRCH. In fact, in this case the bytes would be &12,1,3.

OSWORD

The OSWORD routine invokes a number of miscellaneous operations all of which require more parameters or produce more results than can be passed in A, X and Y. As a result, all OSWORD calls use a parameter block somewhere in memory. The exact location of the parameter block is given in X (low byte) and Y (high byte). The contents of A determine the exact nature of the OSWORD call.

All OSWORD calls are entered at location &FFF1 which indirects through &20C. The table below summarises the OSWORD calls available in release 1.0 of the operating system.

OSWORD summary

A= Summary of function
0 Read a line rom the current input stream to memory
1 Read the elapsed-time clock
2 Write the elapsed-time clock
3 Read internal timer
4 Write internal timer
5 Read a byte in the input/output processor memory
6 Write a byte in the input/output processor memory
7 Generate a sound
8 Define an envelope for use with the sound statement
9 Read pixel colour at screen position X, Y
A Read dot pattern of a specific displayable character
B Read the palette value for a given logical colour

OSWORD with A=0

This routine accepts characters from the current input stream and places them at a specified location in memory. During input the DELETE code (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 an ESCAPE condition occurs.

The control block contains 5 bytes

00
01
Address of buffer for input line LSB
MSB
02 Maximum length of line
03 Minimum acceptable ASCII value
04 Maximum acceptable ASCII value

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

On exit C=0 indicates that (CR; ASCII code 13 or &D), ended the line. C not equal to zero indicates that an escape condition terminated entry. Y is set to the length of the line, including the CR if C=0.

OSWORD call with A=1 Read clock

This call is used to read the internal elapsed-time clock into the 5 bytes pointed to by X and Y. This clock is the one used by BASIC for its TIME function. The elapsed-time clock is reset to zero when the computer is switched on and if a hard-reset is executed. Otherwise it is incremented every hundredth of a second. The only thing that will cause it to lose time is pressing the BREAK key and keeping it pressed.

On entry X and Y should point to the memory locations where the result is to be stored. Y contains the high byte and X the low byte of the address.

On exit X and Y are undefined and the time is given in location XY (lsb) to XY+4 (msb). The time is stored in pure binary.

OSWORD call with A=2 Write clock

This call is used to set the internal elapsed-time clock from the 5 bytes pointed to by XY.

On entry X and Y should point to the memory locations where the new time is stored. Y contains the high byte and X the low byte of the address. The least significant byte of the time is stored at the address pointed to by XY and the most significant byte of the time is stored at address XY +4. A total of 5 bytes are required.

OSWORD call with A=3 Read interval timer

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

OSWORD call with A=4 Write interval timer

On entry X and Y point to 5 locations which contain the new value to which the clock is to be set. The interval timer increments and may cause an event (see page 464) when it reaches zero. Thus setting the timer to &FFFFFFFFFE would cause an event after 2 hundredths of a second.

OSWORD call with A=5 Read I/O processor memory

This call enables any program to read a byte in the I/O processor no matter in which processor the program is executing.

On entry X and Y point to a block of memory as follows

XY LSB of address to be read
XY+1
XY+2
XY+3 MSB of address to be read

On exit the 8 bit byte will be stored in XY+4.

OSWORD call with A=6 Write to I/O processor memory.

As pointed out elsewhere, programs that are to work through the Tube® must not attempt to access memory locations in the I/O processor directly. This call provides easy access to

locations in the BBC Microcomputer wherever the user's program happens to be.

On entry X and Y point to a block of memory initialised as follows:

XY LSB of address to be changed
XY+1
XY+2
XY+3 MSB of address to be changed
XY+4 byte to be entered at address given

OSWORD call with A=7 Make a sound

This call can be used to generate a sound. The 8 bytes pointed to by locations XY to XY+7 are treated as four 2-byte values. These four values determine the sound effect. See the keyword SOUND for a detailed description.

XY channel LSB 1 01
XY+1 MSB 00
XY+2 amplitude LSB -15 F1
XY+3 MSB FF
XY+4 pitch LSB 200 C8
XY+5 MSB 00
XY+6 duration LSB 20 14
XY+7 MSB 00

The example figures on the right of the table show first the required decimal value and secondly the 2 hexadecimal values required. The figures are only illustrative.

On exit X and Y are undefined.

OSWORD call with A=8 Define an envelope

This call is used to define an envelope which can be used by a SOUND statement or equivalent OSWORD call. On entry X and Y point to an address in memory where 14 bytes of data are stored. Y contains the high part of the address and X the low part. The envelope number is stored at XY and the following 13 locations contain data for that envelope. See the entry for the ENVELOPE keyword for more details.

On exit X and Y are undefined.

OSWORD call with A=9 Read a pixel

This call enables the machine code programmer to read the status of a graphics point at any specified location. On entry X and Y point to a block of 5 bytes. Y contains the most significant byte of the address and X the least significant byte. On entry the first four bytes are set up thus:

XY LSB of X co-ordinate
XY+1 MSB of X co-ordinate
XY+2 LSB of Y co-ordinate
XY+3 MSB of Y co-ordinate

On exit XY+4 contains the logical colour of the point or &FF if the point is off the screen. X and Y are undefined.

OSWORD call with A=&0A Read character definition.

Characters are displayed on the screen as an 8 by 8 matrix of dots. The pattern of dots for each character in MODES 0 to 6, including user-defined characters, is stored as 8 bytes (see page 384). This call enables the 8 bytes to be read into a block of memory starting at an address given in X and Y.

On entry the ASCII code of the character is the first entry on the block.

On exit the block contains data as shown below. X and Y are undefined.

XY Character required
XY+1 Top row of displayed character
XY+2 Second row
...
XY+8 Bottom row of displayed character

OSWORD call with A=&0B Read palette

The reader will be aware that each logical colour (0 to 15) has a physical (or displayed) colour associated with it. The physical to logical association can be changed with VDU 19. This OSWORD call enables one to determine the physical colour currently assigned to each logical colour. On entry the X and Y registers contain the address of the start of a block of 5 bytes.

The first byte should contain a value representing the logical colour.

On exit the following four bytes will contain the same four numbers used when VDU 19 assigned a physical colour to the same logical colour. Suppose that logical colour 2 was in fact set to blue (4) by the statement

VDU 19,2,4,0,0,0

then this call would produce the following result:

XY 2 Logical colour
XY+1 4 Physical colour (blue)
XY+2 0 For future expansion
XY+3 0 ...
XY+4 0 ...

Command Line Interpreter (&FFF7)

The machine operating system CLI is usually accessed from a high level language by starting a statement with an asterisk. For example:

*MOTOR 0,1

The command line itself (excluding the asterisk) is then passed, without any further processing, to the CLI.

Machine code programs can use all operating system commands by placing the address of a command line in the X(LSB) and Y(MSB) registers and calling &FFF7. This routine indirects through location &208.

The command line should not start with an asterisk and must end with an &0D. In fact any leading asterisk or spaces will be stripped.

The following BASIC program illustrates this:

10 DIM C 20

20 $C="MOTOR 1"

30 X%=C MOD 256

40 Y%=C DIV 256

50 CALL &FFF7

When RUN the cassette motor will turn on. The computer will have allocated a space for C - perhaps at location &E0A in which case successive bytes would contain:

Address	Contents
&E0A	4D	(M)
&E08	4F	(O)
&E0C	54	(T)
&E0D	4F	(O)
&E0E	52	(R)
&E0F	20	(space)
&E10	31	(1)
&E11	00	(return)

Of course, this particular example would have been easier as an

FX call or simply as *MOTOR 1. However, complex commands may need this call.

Faults, Events and BRK handling

It is necessary to provide some means to enable programs to deal with faults such as Illegal command or Division by zero. BASIC uses the 6502 BRK instruction when dealing with faults like this and user written programs can also use the same facility. In BASIC (for example), a BRK instruction is followed by a sequence of bytes giving the following information:

BRK instruction - value &00

Fault number

Fault message - may contain any non-zero character

&00 to terminate message

When the 6502 encounters a BRK instruction the operating system places the address following the BRK instruction in locations &FD and &FE. Thus these locations point to the "Fault number". The operating system then indirects via location &202. In other words control is transferred to a routine whose address is given in locations &202 (low byte) and &203 (high byte). The default routine, whose address is given at the location, prints the fault message.

The BRK handling outline above enables the user to intercept normal procedures and to generate his or her own special messages and error numbers in user written machine code routines. The program on page 446 shows this in practice. See also page 466 (IRQ).

Whilst faults are, in general, "fatal", there is another class of events, called "Events", which are informative rather than fatal. This class of events includes, for example, a key being pressed on the keyboard. The user may wish to detect such an operation or may be happy to ignore it. When the operating system detects an "Event" then, if that event is enabled (by using *FX 14) it indirects via &220 with an event code in the accumulator. The contents of X and Y depend on the event. The event codes in A indicate the following:

Accumulator description

0 Buffer empty. X=buffer identity
1 Buffer full. X=buffer identity
Y=character that could not be stored
2 Keyboard interrupt
3 ADC conversion complete
4 Start of TV field pulse (vertical sync)
5 Interval timer crossing zero
6 Escape condition detected

The user supplied event handling routine is entered with interrupts disabled and it should not enable interrupts. The routine should return (RTS) after a short period, say one millisecond maximum, and should preserve the processors P, A, X and Y registers.

Interrupt Handling

The whole machine runs under continuous interrupts but nonetheless the user can easily add his or her own interrupts and handling routines. Because the machine runs under interrupts, software timing loops should not be used. Several hardware timers are available to the user and these should be used wherever possible.

NMI Non-Maskable Interrupt

In general these should be avoided. When a disc operating system ROM is fitted NMI's will be handled by the ROM. Again, it should be emphasised that NMI is reserved for the Operating System.

IRQ Interrupt Request

When an IRQ is detected the operating system immediately indirects through location &204 (IRQ1V) to an operating system routine which handles all anticipated internal IRQ's. If the operating system is unable to deal with the IRQ (because it has come from an unexpected device such as the user 6522), then the system indirects through &206 (IRQ2V). Thus the user routine for handling IRQ's should normally be indirected via IRQ2V but if top priority is required the user routine can be indirected via IRQ1V.

In either case the user supplied routine must return control to the operating system routine to ensure clean handling.

The operating system handles BRK and IRQ's with the following code

STA &FC \ temporary for A
PLA
PHA \ get processor status
AND #&10
BNE BRK
JMP (&0204) \ IRQ1V
BRK TXA \ BRK handling
PHA \save X
TSX
LDA &103,X \get address low
CLD
SEC
SBC #1
STA &FD
LDA &104,X \ get address high
SBC #0
STA &FE

Note that A is stored in location &FC so that it can be accessed by user routines. When the computer indirects through &202 (BRKV), &204(IRQ1V) and &206(IRQ2V) X and Y will contain correct values. The user must not enable interrupts during his or her IRQ service routine.

This facility is only available from release 1.0.

Exit: BBC Microcomputer User Guide; Kasoft Typesetting; Archer


The BBC Microcomputer User Guide was written by John Coll and edited by David Allen for the British Broadcasting Corporation.

Optical character recognition and original formatting effort by Mark Usher.

HTML version maintained by: Kade "Archer" Hansson; e-mail: archer@dialix.com.au

Last updated: Monday 12th February 2001