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 &amp; 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
        ; &amp; 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 &amp; convert integer AY to FAC1 &amp; 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
        ; &amp; 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$)
.