USR
USR is a BASIC function to call (execute) a machine code routine in memory or BBC MOS API entry. It returns a numerical result back to BASIC. On most non-6502 platforms, BBC BASIC intercepts calls to the standard BBC MOS API entries and translates them to an equivalent action.
Availability | Present in all versions of BBC BASIC. | |
Syntax | BASIC I-V | <num-var> = USR( <numeric>)
|
Token (hex) | BASIC I-V | BA (function)
|
Description | BASIC I-V | Sets the main CPU registers to the values of the
resident integer variables, and calls a machine code routine at the address given in the <numeric>. The routine should exit with the normal subroutine return instruction. After the routine exits, USR returns an integer holding values returned in CPU registers. |
Associated keywords | ? , ! , $ , CALL
|
Description
USR
allows a BASIC program to use a piece of machine code
as part of its operation. Machine code is a program in the form of binary
data that can be run directly by the central processing unit (CPU).
USR
is one of the ways to access functions of the MOS (its
Application Programming Interface, or API.) Advanced programmers
can also use USR
to improve the performance of a BASIC program
by rewriting the most time-consuming parts in machine code, as it runs much,
much faster than the equivalent BASIC code.
Because USR
is involved with the low level architecture of the
computer, its precise function depends on which CPU is running BASIC. The
CPU-specific preparation is the same as in CALL
, except
that there is no table of variables as USR
's only argument
is the <numeric> address. In general, USR
sets certain CPU
registers to the values of the resident integer variables, and jumps
into a machine code routine at the address given in the <numeric>.
After the routine exits, BASIC captures a certain portion of the CPU's
state, and returns it as the numeric result of the USR
function. The value that is returned is again CPU-dependent:
BBC MOS API Access
To ensure cross-platform compatibility, most non-6502 implementations of BBC
BASIC translate CALL
s and USR
to MOS API entries
to an equivalent call to the underlying MOS. The only addresses that are
supported by this are those listed below. The values in A%, X% and Y%, or
the data or control block pointed to by X%, are passed in an appropriate
manner to the underlying system.
Address | MOS call | Action |
---|---|---|
&FFF7 | OSCLI | Execute *command. |
&FFF4 | OSBYTE | Various byte-wide functions. |
&FFF1 | OSWORD | Various control block functions. |
&FFEE | OSWRCH | Write character to output stream. |
&FFE7 | OSNEWL | Write NewLine to output stream. |
&FFE3 | OSASCI | Write character or NewLine to output stream. |
&FFE0 | OSRDCH | Wait for character from input stream. |
&FFDD | OSFILE | Perform actions on whole files or directories. |
&FFDA | OSARGS | Read and write information on open files or filing systems. |
&FFD7 | OSBGET | Read a byte from an a channel. |
&FFD4 | OSBPUT | Write a byte to a channel. |
&FFD1 | OSGBPB | Read and write blocks of data. |
&FFCE | OSFIND | Open or close a file. |
Register usage
CPU | Registers on entry | Result returned by USR |
---|---|---|
6502 | A=A%, X=X%, Y=Y%, Cy=b0 of C% | b0-7=A, b8-15=X b16-23=Y, b24-31=P |
6809 | A=A%, B=B%, U=U%, X=X%, Y=Y%, Cy=b0 of C% | b0-7=A, b8-15=X b16-23=Y, b24-31=CC |
Z80 | A=A%, B=B%, C=C%, D=D%, E=E%, H=H%, L=L% | b0-b15=HL' b16-b31=HL |
Z80 BBC API call |
A=A%, L=X%, H=H%, E=E% | b0-b7=A, b8-b23=HL, b24-31=F |
32016 | Testing and disassembly shows 32016 BASIC only sets up registers for CALL parameter block or for MOS API calls | b0-b31=R1 |
32016 BBC API call |
R1=A%, R2=X%, R3..R7=contents of control block at X% |
b0-b7=R1, b8-b23=R2 |
PDP11 | R0=A%, R1=B%, R2=C%, R3=D%, R4=E%, R5=F% | b0-b15=R0, b16-b31=R1 |
PDP11 BBC API call |
R0=A%, R1=X%, R2=Y% | b0-b7=R0, b8-b23=R1 |
ARM | R0=A%, R1=B%, R2=C%, R3=D%, R4=E%, R5=F%, R6=G%, R7=H%, R8=I%, R9=J% |
b0-b31=R0 |
ARM BBC API call |
R0=A%, R1=X%, R2=Y%, or R1..R5=contents of control block at X% |
b0-b7=R0, b8-b31=R1 |
When passing the address of a data structure or control block, code similar to the following should be used.
DIM ctrl% 31 :REM Control block, may be anywhere in 32-bit memory space X% = ctrl% :REM X% holds full 32-bit address Y% = X% DIV 256 :REM Y% holds 32-bit address, shifted right 8 bits
On 8-bit platforms the high 24 bits of X% and Y% are ignored, and the control block is found with X%+256*Y%. On platforms with larger registers, the control block is found at X%, ignoring Y% completely. Some platforms will check if X%<256 and use X%+256*Y%, though this should not be relied on.
An additional advantage is that the control block is easily accessed with X%, for example, X%!2=load%, etc.
Notes
Yes, 32000 passes A%=R1, etc., not R0.
When calling &FFCE with A%=0 to close a file, 6502 BASIC requires the
channel to be passed in Y% but in ARM BASIC the channel must be passed in
X%. When calling from BASIC, the CLOSE#
statement
should be used.
-- beardo 19:06, 5 May 2007 (BST)
Jgharston 17:40, 8 October 2007 (BST)
Jgharston (talk) 15:02, 19 August 2017 (CEST)