* * * * *

  The definitive guide to writing assembly language subroutines for Extended
                                 Color BASIC

And in keeping with documenting 40 year old technology [1], I'm documenting
how to call assembly language subroutines for Extended Color BASIC
(Beginner's All-purpose Symbolic Instruction Code).

One major difference between Color BASIC and Extended Color BASIC is how to
define the address to call. No longer do you have to poke the address into
memory, but use the BASIC command DEFUSRn (where n is between 0 and 9). And
you can define up to 10 such routines.

From that point on, existing code written for Color BASIC will just work.
VARTYP will still have the value type, FP0 will still be a floating point
value or contain an address to a string descriptor, and all the functions
defined for Color BASIC still function the same.

But there are some major differences. First off, the registers are defined
upon entry now. The A register contains the contents of VARTYP and the
condition codes are set appropriately, so that one can immedately do a
conditional branch to check the type (BEQ (Branch if EQual) for a number, BMI
(Branch if MInus) for a string). The X register either points to FP0
(technically, one byte prior to FP0 for internal reasons) if the parameter is
a number, or it points to the string descriptor if the parameter is a string.
And if the parameter is a string, the B register has the length of the
string. So the chksum function can be rewritten to read:

-----[ Assembly ]-----
CHKSTR          equ     $B146
GIVBF           equ     $B4F3

               org     $7F00

checksum        jsr     CHKSTR
               lda     ,x
               ldx     2,x
               clrb
sum             addb    ,x+
               deca
               bne     .sum
               comb
               jmp     GIVBF
               end
-----[ END OF LINE ]-----

Not a big change, but no longer do we have to load the string descriptor
pointer from memory.

The biggest change however, is the ability to return a string from an
assembly language subroutine. No longer do you have to modify the passed in
string descriptor, you can return a new string. To do so, you call RSVPSTR to
reserve the space, put the length into the BASIC variable STRDES (name from
the Unravelled Series [2] and located at address $0056), the pointer into
STRDES+2, and call GIVSTR (name I came up with since it's not named in the
Unravelled series and located at address $B54C). Here is the revised ROT-13
[3] code for Extended Color BASIC:

-----[ Assembly ]-----
rot13           jsr     CHKSTR
               tfr     x,y             ; save string descriptor
               jsr     RSVPSTR         ; B is already set
               stb     STRDES          ; save new string length
               stx     STRDES + 2      ; and the space for it
               ldy     2,y             ; get stirng data, which RSVPSTR may have moved

loop            lda     ,y+             ; ROT-13 blah blah ...
               cmpa    #'A'
               blo     .out
               cmpa    #'Z'
               bhi     .out
               adda    #13
               cmpa    #'Z'
               bls     .out
               suba    #26
out             sta     ,x+
               decb
               bne     .loop

               jmp     GIVSTR          ; return new string to BASIC
               end
-----[ END OF LINE ]-----

And this:

-----[ ExtendedColorBASIC ]-----
110 X$ = USR0("ABCXYZ")
120 PRINT X$
-----[ END OF LINE ]-----

will work as expected—X$ will be the ROT-13 version of the string literal.
And with that, we're done. That's all there is to interfacing an assembly
language subroutine to Color BASIC and Extended Color BASIC.

I'm not sure why this wasn't documented in _Getting Started With Extended
Color BASIC_ [4]—perhaps no one talked about the changes to Extended Color
BASIC to the documentation department, or there wasn't time before shipping
the updated BASIC, or what. It's clear from the Unravelled series that the
change was a deliberate change to make it easier to interface with BASIC and
cache useful values in the various registers, but for some reason, it never
got documented. What is documented will work, you can pass in a string as:

-----[ ExtendedColorBASIC ]-----
110 X$="ABCXYZ" : X=USR0(VARPTR(X$))
-----[ END OF LINE ]-----

but it's not needed as I've shown. And here's one last example, defining
multiple subroutines that show handling of various parmaters of strings and
numbers and returning strings or numbers (here are links to basic.i [5] and
basic-fp.i [6]):

-----[ Assembly ]-----
               include "basic.i"
               include "basic-fp.i"

               .opt    basic strspace 300
               .opt    basic defusr0 num2num
               .opt    basic defusr1 fp2fp
               .opt    basic defusr2 num2str
               .opt    basic defusr3 str2num
               .opt    basic defusr4 str2str
               .opt    basic defusr5 son2son
               .opt    basic defusr6 son2nos

               org     $7F00

;***************************************************************************

num2num         jsr     CHKNUM          ; check for numeric argument
               jsr     INTCVT          ; convert to integer
               coma                    ; 1s complement
               comb
               jmp     GIVABF          ; return it to BASIC

;***************************************************************************

fp2fp           jsr     CHKNUM          ; check for numeric argument
               ldx     #.pi            ; * 3.1415926
               jmp     CB.FMULx

pi              .float  3.1415926

;***************************************************************************

num2str         jsr     CHKNUM          ; check for number
               jsr     INTCVT          ; convert to integer
               bmi     .minus          ; if negative, return 'MINUS'
               beq     .zero           ; if zero, return "ZERO"
               ldx     #.textplus      ; else return "PLUS"
               bra     .return
minusinf        ldx     #.textminus
               bra     .return
zero            ldx     #.textzero
return          ldb     ,x+             ; get length
               stb     STRDES + _STRLEN        ; save length
               stx     STRDES + _STRPTR        ; save text
               jmp     GIVSTR                  ; return string to BASIC

textminus       ascii   'MINUS'c        ; a "counted" string
textzero        ascii   'ZERO'c         ; where the first byte is the length
textplus        ascii   'PLUS'c         ; of the string

;***************************************************************************

str2num         jsr     CHKSTR          ; check for string
               lda     _STRLEN,x       ; get length
               ldx     _STRPTR,x       ; get string data
               clrb                    ; clear checksum
sum             addb    ,x+             ; add next byte
               deca                    ; if more, do more
               bne     .sum
               comb                    ; complement checksum
               jmp     GIVBF           ; return it to BASIC

;***************************************************************************

str2str         jsr     CHKSTR          ; check for string
               tfr     x,y             ; save descriptor
               jsr     RSVPSTR         ; reserve space for return string
               stb     STRDES + _STRLEN        ; save length
               stx     STRDES + _STRPTR        ; and allocated space
               ldy     _STRPTR,y       ; get original string

loop            lda     ,y+             ; get character
               cmpa    #'A'            ; < 'A', just store
               blo     .store
               cmpa    #'Z'            ; if <= 'Z', convert to lower case
               bls     .lower
               cmpa    #'a'            ; if between 'a' and 'z',
               blo     .store          ; convert to upper case
               cmpa    #'z'
               bhi     .store
               anda    #$5F
               bra     .store
lower           ora     #$20
store           sta     ,x+             ; save character in new string
               decb                    ; if more, do more
               bne     .loop
               jmp     GIVSTR          ; return new string to BASIC

;***************************************************************************

son2son         beq     .num            ; if 0, number (string or number to string or number)
               lsrb                    ; cut length in half
               stb     _STRLEN,x
               rts
num             ldx     #.one           ; add 1.0 to number
               jmp     CB.FADDx        ; and return new value to BASIC

one             .float  1.0

;***************************************************************************

son2nos         bmi     .string         ; if minus, string (string or number to number or string)
               ldb     #4              ; return "0" to BASIC, len of 1
               ldx     #num2str.textzero+1     ; text of "ZERO"
               stb     STRDES + _STRLEN
               stx     STRDES + _STRPTR
               jmp     GIVSTR

string          tfr     a,b             ; transfer type to B
               jmp     GIVBF           ; and return it to BASIC

;***************************************************************************

               end
-----[ END OF LINE ]-----

And the resulting BASIC file output from my assembler [7]:

-----[ ExtendedColorBASIC ]-----
10 DATA189,177,67,189,179,237,67,83,126,180,244,189,177,67,142,127,20,126,186,202,130,73,15,218,104,189,177,67,189,179,237,43,7,39,10,142,127,67,32,8,142,127,57,32,3,142,127,62,230,128,215,86,159,88,126,181,76,4,45,73,78,70,4,90,69,82,79,4,43,73,78
20 DATA70,189,177,70,166,132,174,2,95,235,128,74,38,251,83,126,180,243,189,177,70,31,18,189,181,109,215,86,159,88,16,174,34,166,160,129,65,37,18,129,90,35,12,129,97,37,10,129,122,34,6,132,95,32,2,138,32,167,128,90,38,227,126,181,76,39,4,84,231,132
30 DATA57,142,127,148,126,185,194,129,0,0,0,0,43,12,198,4,142,127,63,215,86,159,88,126,181,76,31,137,126,180,243
40 CLEAR300,32511:FORA=32512TO32683:READB:POKEA,B:NEXT:DEFUSR0=32512:DEFUSR1=32523:DEFUSR2=32537:DEFUSR3=32584:DEFUSR4=32601:DEFUSR5=32648:DEFUSR6=32665
-----[ END OF LINE ]-----

[1] gopher://gopher.conman.org/0Phlog:2024/11/26.2
[2] https://colorcomputerarchive.com/repo/Documents/Books/Unravelled%20Series/
[3] https://en.wikipedia.org/wiki/ROT13
[4] https://archive.org/details/Getting_Started_with_Extended_Color_BASIC_1984_Tandy
[5] gopher://gopher.conman.org/0Phlog:2024/11/26/basic.i
[6] gopher://gopher.conman.org/0Phlog:2024/11/26/basic-fp.i
[7] https://github.com/spc476/a09
---

Discussions about this page

Two Stop Bits | The definitive guide to writing assembly subroutines for Extended Color BASIC
 https://twostopbits.com/item?id=4700

Writing assembly language subroutines for Extended Color BASIC | Hacker News
 https://news.ycombinator.com/item?id=42253601

Email author at [email protected]