EhBASIC Using USR()

Enhanced BASIC, using USR()

USR(<expression[$]>) calls the machine code function pointed to by the user jump vector after evaluating <expression[$]> and placing the result in the first floating accumulator. Once the user function exits, via an RTS, the value in the floating accumulator is passed back to EhBASIC.

Either a numeric value or a string can be passed, and either type can be returned depending on the setting of the data type flag at the end of the user code and the return point (see code examples for details).

It can also be extended by adding parameters to USR() and parsing them from within the routine in the same way that CALL can be extended, just remember to get the value from FAC1 first.

How to – numeric source, numeric result.

First you need to write the code.

; this code demonstrates the use of USR() to quickly calculate the square of a
; byte value. Compare this with doing SQ=A*A or even SQ=A^2.
	.include BASIC.DIS	; include the BASIC labels file. this allows
				; you easy access to the internal routines you
				; need to parse the command stream and access
				; some of the internals of BASIC. It is usually
				; output by the assembler as part of the listing
				; or as a separate, optional, file.
; for now we'll put this in the spare RAM at $F400
	*=	$F400
Square
	JSR	LAB_EVBY	; evaluate byte expression, result in X and FAC1_3
	LDA	#$00		; clear A
	STA	FAC1_2		; clear square low byte (use FAC1 as the workspace)
				; (no need to clear the high byte, it gets shifted out)
	TXA			; copy byte to A
	LDX	#$08		; set bit count
Nextr2bit
	ASL	FAC1_2		; low byte *2
	ROL	FAC1_1		; high byte *2+carry from low
	ASL	A		; shift byte
	BCC	NoSqadd		; don't do add if C = 0
	TAY			; save A
	CLC			; clear carry for add
	LDA	FAC1_3		; get number
	ADC	FAC1_2		; add number^2 low byte
	STA	FAC1_2		; save number^2 low byte
	LDA	#$00		; clear A
	ADC	FAC1_1		; add number^2 high byte
	STA	FAC1_1		; save number^2 high byte
	TYA			; get A back
NoSqadd
	DEX			; decrement bit count
	BNE	Nextr2bit	; go do next bit
	LDX	#$90		; set exponent=2^16 (integer)
	SEC			; set carry for +ve result
	JMP	LAB_STFA	; set exp=X, clearFAC1 mantissa3, normalise & return

Now you need to set up the address for your function. This is done by DOKEing an address into the USR() vector e.g.

DOKE $0B,$F400		; set the user function address to addr
			; $0B	- user function vector address
			; $F400	- routine address

Finally you need to set the vector in your BASIC program and use that to call the function

.
10 DOKE $0B,$F400
.
.
.
145 SQ=USR(A)
.

How to – numeric source, string result.
As before, first you need to write the code.

; this code demonstrates the use of USR() to generate a string of # characters.
; the length of the required string is the parameter passed.
	.include BASIC.DIS	; include the BASIC labels file. this allows
				; you easy access to the internal routines you
				; need to parse the command stream and access
				; some of the internals of BASIC. It is usually
				; output by the assembler as part of the listing
				; or as a separate, optional, file.
; for now we'll put this in the spare RAM at $F400
	*=	$F400
STRING
	JSR	LAB_EVBY	; evaluate byte expression, result in X and FAC1_3
	TXA			; string is byte length
	BEQ	NUL_STRN	; branch if null string
	JSR	LAB_MSSP	; make string space A bytes long A=$AC=length,
				; X=$AD=Sutill=ptr low byte, Y=$AE=Sutilh=ptr high byte
	LDA	#"#"		; set character
	LDY	FAC1_3		; get length
SAV_HASH
	DEY			; decrement bytes to do
	STA	(str_pl),Y	; save byte in string
	BNE	SAV_HASH	; loop if not all done
NUL_STRN
	PLA			; dump return address (return via get value
	PLA			; from line, this skips the type checking and
				; so allows a string result to be returned)
	JMP	LAB_RTST	; check for space on descriptor stack then put
				; string address and length on descriptor stack
				; & update stack pointers

Now you need to set up the address for your function. This is done by DOKEing an address into the USR() vector e.g.

DOKE $0B,$F400		; set the user function address to addr
			; $0B	- user function vector address
			; $F400	- routine address

Finally you need to set the vector in your BASIC program and use that to call the function

.
10 DOKE $0B,$F400
.
.
.
145 HA$=USR(A)
.

How to – string source, numeric result.

As before, first you need to write the code.

; this code demonstrates the use of USR() to test a string of characters.
; if all the string is alpha -1 is returned, else 0 is returned.
	.include BASIC.DIS	; include the BASIC labels file. this allows
				; you easy access to the internal routines you
				; need to parse the command stream and access
				; some of the internals of BASIC. It is usually
				; output by the assembler as part of the listing
				; or as a separate, optional, file.
; for now we'll put this in the spare RAM at $F400
	*=	$F400
ALPHA
	JSR	LAB_EVST	; evaluate string
	TAX			; copy length to X
	BEQ	NOT_ALPH	; branch if null string
	LDY	#$00		; clear index
ALP_LOOP
	LDA	(ut1_pl),Y	; get byte from string
	JSR	LAB_CASC	; is character "a" to "z" (or "A" to "Z")
	BCC	NOT_ALPH	; branch if not alpha
	INY			; increment index
	DEX			; decrement count
	BNE	ALP_LOOP	; loop if not all done
	LDA	#$FF		; set for -1
	BNE	IS_ALPHA	; branch always
NOT_ALPH
	LDA	#$00		; set for 0
IS_ALPHA
	TAY			; copy byte
	LDX	#$00		; clear byte
	STX	Dtypef		; clear data type flag, $00=numeric
	JMP	LAB_AYFC	; save & convert integer AY to FAC1 & return

Now you need to set up the address for your function. This is done by DOKEing an address into the USR() vector e.g.

DOKE $0B,$F400		; set the user function address to addr
			; $0B	- user function vector address
			; $F400	- routine address

Finally you need to set the vector in your BASIC program and use that to call the function

.
10 DOKE $0B,$F400
.
.
.
145 AL=USR(A$)
.

How to – string source, string result.
As before, first you need to write the code.

; this code demonstrates the use of USR() invert the case of a string of
; characters. only alpha characters will be affected.
	.include BASIC.DIS	; include the BASIC labels file. this allows
				; you easy access to the internal routines you
				; need to parse the command stream and access
				; some of the internals of BASIC. It is usually
				; output by the assembler as part of the listing
				; or as a separate, optional, file.
; for now we'll put this in the spare RAM at $F400
	*=	$F400
ALPHA
	JSR	LAB_EVST	; evaluate string
	STA	str_ln		; set string length
	STX	str_pl		; set string pointer low byte
	STY	str_ph		; set string pointer high byte
	TAX			; copy length to X
	BEQ	NO_STRNG	; branch if null string
	LDY	#$00		; clear index
ALP_LOOP
	LDA	(ut1_pl),Y	; get byte from string
	JSR	LAB_CASC	; is character "a" to "z" (or "A" to "Z")
	BCC	NOT_ALPH	; branch if not alpha
	EOR	#$20		; toggle case
	STA	(ut1_pl),Y	; save byte back to string
NOT_ALPH
	INY			; increment index
	DEX			; decrement count
	BNE	ALP_LOOP	; loop if not all done
NO_STRNG
	PLA			; dump return address (return via get value
	PLA			; from line, this skips the type checking and
				; so allows a string result to be returned)
	JMP	LAB_RTST	; check for space on descriptor stack then put
				; string address and length on descriptor stack
				; & update stack pointers

Now you need to set up the address for your function. This is done by DOKEing an address into the USR() vector e.g.

DOKE $0B,$F400		; set the user function address to addr
			; $0B	- user function vector address
			; $F400	- routine address

Finally you need to set the vector in your BASIC program and use that to call the function

.
10 DOKE $0B,$F400
.
.
.
145 A$=USR(A$)
.