LLX > Neil Parker > Apple II > Addons for Applesoft

Addons for Applesoft

Here are a few machine-language snippets that add useful new statements and functions to Applesoft. Most of them are one of two varieties: new statements, added through Applesoft's ampersand (&) vector, and new functions, added through the lesser-known and often-neglected USR vector.

Though the assembly listings below all start at memory location $300, they are all fully relocatable, and may be positioned anywhere in memory without change. Only the address to be POKEed into the ampersand or USR vector would change.

Additionally, the ampersand routines are chainable - if you want to use more than one of them at a time, just position each one so that its first byte overwrites the RTS at the end of the previous one.

Unfortunately, the USR functions are not as easily chainable. USR wasn't designed to allow more than one user-defined function at a time (not that it can't be done, but doing it requires jumping through some additional hoops).

Actually setting up the ampersand or USR vector isn't illustrated below. If you load an ampersand routine at $300, connect it up like this:

 POKE 1013,76: POKE 1014,0: POKE 1015,3

Setting up a USR routine is similar:

 POKE 10,76: POKE 11,0: POKE 12,3

The 0 and the 3 will change depending on where you load the routine, but the 76 ($4C, the 6502 JMP instruction) is always constant.

(If there's any doubt about which setup to use, look at the syntax example below the code.)

Some of the ampersand routines below end with two consecutive RTS instructions. This looks a bit strange, but is intentional: The first one is the normal exit from the routine, and the second one gets branched to if the token immediately following the ampersand isn't recognized. This is to facilitate the chaining technique described above: Overlay the second RTS (not the first) with the start of the next routine. If you're not chaining routines together, the second RTS can be eliminated by adjusting the BNE instruction at the beginning to point to the first RTS.

License: The code below may be used by anyone at any time for any purpose with no restrictions whatsoever. Though I would appreciate an acknowledgement if you find any of these useful, it's not required. There is, of course, no warranty - I cannot claim that any of them is bug-free, or that you will find any of them useful, nor can I provide any form of formal support or assume any legal liability.

Input (Almost) Anything

Applesoft's INPUT statement really isn't designed for human beings to interact with - its parsing rules are somewhat complex, and if you want to respond to an INPUT with a string that contains both commas and quotation marks in it, you're out of luck: it's impossible.

The code below adds a statement similar to the LINE INPUT found in many other BASICs. You can read into a string variable almost any ASCII characters, up to 255 characters - the only ASCII characters that can't be read as data are the standard Apple input-editing keys (Return, Backspace, Forward Arrow, Escape, and Delete - CHR$'s 13, 8, 21, 27, and 127).

                1    INPUTTKN =     $84        ;Asoft INPUT token
                2    PROMPT   =     $33        ;Prompt char for GETLN
                3    FORPNT   =     $85        ;Var ptr for GETSPT
                4    IN       =     $200       ;Buffer for GETLN
                5    CHRGET   =     $B1        ;Get next program token
                6    GDBUFS   =     $D539      ;Mask off hi bits of input
                7    GETSPT   =     $DA7B      ;Assign FAC to str var
                8    STRPRT   =     $DB3D      ;Print string in FAC
                9    CHKSTR   =     $DD6C      ;TYPE MISMATCH if not string
                10   STRTXT   =     $DE81      ;Parse string from prog text
                11   SYNCHR   =     $DEC0      ;SYNTAX err if next token<>A
                12   PTRGET   =     $DFE3      ;Parse var name, find in mem
                13   ERRDIR   =     $E306      ;ILLEGAL DIRECT if not running
                14   STRSPA   =     $E3DD      ;Get space for new str
                15   PUTNEW   =     $E42A      ;Make temp str descript
                16   MOVSTR   =     $E5E2      ;Move data to str space
                17   GETLN    =     $FD6A      ;Read line of input
                18            ORG   $300
0300: C9 84     19            CMP   #INPUTTKN  ;Is it INPUT?
0302: D0 3E     20            BNE   OUT        ;Skip if not
0304: 20 06 E3  21            JSR   ERRDIR     ;Make sure prog is running
0307: 20 B1 00  22            JSR   CHRGET     ;Get next token
030A: C9 22     23            CMP   #$22       ;Quote?
030C: D0 0B     24            BNE   GETVAR     ;Don't prompt if not
030E: 20 81 DE  25            JSR   STRTXT     ;Parse str const
0311: 20 3D DB  26            JSR   STRPRT     ;Print it
0314: A9 3B     27            LDA   #';'       ;Check for ';'
0316: 20 C0 DE  28            JSR   SYNCHR
0319: 20 E3 DF  29   GETVAR   JSR   PTRGET     ;Parse var name
031C: 85 85     30            STA   FORPNT     ;Save its address
031E: 84 86     31            STY   FORPNT+1
0320: 20 6C DD  32            JSR   CHKSTR     ;Error if not string
0323: A9 80     33            LDA   #$80       ;No prompt
0325: 85 33     34            STA   PROMPT
0327: 20 6A FD  35            JSR   GETLN      ;Get input
032A: 8A        36            TXA              ;Save its length
032B: 48        37            PHA
032C: 20 39 D5  38            JSR   GDBUFS     ;Chop off hi bits
032F: 68        39            PLA              ;Get saved len
0330: 48        40            PHA
0331: 20 DD E3  41            JSR   STRSPA     ;Get space for str
0334: A0 02     42            LDY   #>IN
0336: A2 00     43            LDX   #<IN
0338: 68        44            PLA
0339: 20 E2 E5  45            JSR   MOVSTR     ;Move input to string space
033C: 20 2A E4  46            JSR   PUTNEW     ;Make new temp descriptor
033F: 4C 7B DA  47            JMP   GETSPT     ;Assign descriptor to var
0342: 60        48   OUT      RTS


--End assembly, 67 bytes, Errors: 0

The syntax is:

 &  INPUT ["prompt string";] string-variable

The prompt is optional; if present, it must be a literal quoted string followed by a semicolon. If it's not present, no prompt at all is printed - if you want a question mark prompt like the one Applesoft's INPUT provides, say something like & INPUT "?";A$.

You only get one variable per & INPUT statement. If you want to input two or more variables, use two or more & INPUT statements.

Input (Almost) Anything, Again

Here's another, rather different way to do the same thing:

                1    PROMPT   =     $33        ;Prompt char for GETLN
                2    IN       =     $200       ;Buffer for GETLN
                3    GDBUFS   =     $D539      ;Mask off hi bits of input
                4    STRPRT   =     $DB3D      ;Print string in FAC
                5    CHKSTR   =     $DD6C      ;TYPE MISMATCH if not string
                6    ERRDIR   =     $E306      ;ILLEGAL DIRECT if not running
                7    STRSPA   =     $E3DD      ;Get space for new str
                8    PUTNEW   =     $E42A      ;Make temp str descript
                9    MOVSTR   =     $E5E2      ;Move data to str space
                10   GETLN    =     $FD6A      ;Read line of input
                11            ORG   $300
0300: 20 06 E3  12            JSR   ERRDIR     ;Err if prog not running
0303: 20 6C DD  13            JSR   CHKSTR
0306: 20 3D DB  14            JSR   STRPRT     ;Print arg
0309: A9 80     15            LDA   #$80       ;No prompt
030B: 85 33     16            STA   PROMPT
030D: 20 6A FD  17            JSR   GETLN      ;Get input
0310: 8A        18            TXA              ;Save its length
0311: 48        19            PHA
0312: 20 39 D5  20            JSR   GDBUFS     ;Chop off hi bits
0315: 68        21            PLA              ;Get saved len
0316: 48        22            PHA
0317: 20 DD E3  23            JSR   STRSPA     ;Get space for str
031A: A0 02     24            LDY   #>IN
031C: A2 00     25            LDX   #<IN
031E: 68        26            PLA
031F: 20 E2 E5  27            JSR   MOVSTR     ;Move input to string space
0322: 68        28            PLA              ;Must pop 1 return addr
0323: 68        29            PLA              ; before returning a string
0324: 4C 2A E4  30            JMP   PUTNEW     ;Return new temp descriptor


--End assembly, 39 bytes, Errors: 0

The syntax is:

 USR (string-expression)

This is a function that returns a string, and can be used anywhere in an expression where a string-valued function would be legal. The argument is printed as a prompt, and in this case you can use any string expression at all, not just a literal quoted string. For example:

100 P$ = "Yes, Master?"
110 A$ = USR (P$ + " ")
120  PRINT "You typed "; A$

If you want no prompt at all, pass an empty string, for example:

200 A$ =  USR ("")

Computed GOTO

One feature that old Integer BASIC programmers often missed in Applesoft is the ability to GOTO any numeric expression, not just a literal line number. Applesoft does have ON ... GOTO, but it's just not the same.

Adding an Integer-like computed GOTO to Applesoft is particularly easy:

                1    GOTOTKN  =     $AB        ;Asoft GOTO token
                2    CHRGET   =     $B1        ;Get next program token
                3    GOTO     =     $D93E      ;Parse & run GOTO stmt
                4    FRMNUM   =     $DD67      ;Eval numeric expr
                5    GETADR   =     $E752      ;Convert to 2-byte int
                6             ORG   $300
0300: C9 AB     7             CMP   #GOTOTKN   ;Is it GOTO?
0302: D0 0C     8             BNE   OUT        ;Skip if not
0304: 20 B1 00  9             JSR   CHRGET     ;Get next token
0307: 20 67 DD  10            JSR   FRMNUM     ;Eval numeric expr
030A: 20 52 E7  11            JSR   GETADR     ;Convert to int
030D: 4C 41 D9  12            JMP   GOTO+3     ;Find line in mem & goto it
0310: 60        13   OUT      RTS


--End assembly, 17 bytes, Errors: 0

Syntax:

 &  GOTO numeric-expression

Of course the expression must evaluate to an actual line number in your program, or you'll get ?UNDEF'D STATEMENT ERROR.

The use of the GETADR routine (the same routine used to get the memory address for PEEK, POKE, and CALL) has an odd side effect: negative line numbers are accepted. The routine will make it positive by adding 65536 to it.

Computed GOSUB

Integer BASIC also had computed GOSUBs. Here it is for Applesoft:

                1    GOSUBTKN =     $B0        ;Asoft GOSUB token
                2    CURLIN   =     $75        ;Current line no.
                3    TXTPTR   =     $B8        ;Current token addr
                4    CHRGET   =     $B1        ;Get next program token
                5    GETSTK   =     $D3D6      ;Check stack space
                6    NEWSTT   =     $D7D2      ;Execute statements
                7    GOTO     =     $D93E      ;Go to new line no.
                8    FRMNUM   =     $DD67      ;Eval numeric expression
                9    GETADR   =     $E752      ;Convert number to 2-byte int
                10            ORG   $300
0300: C9 B0     11            CMP   #GOSUBTKN  ;Is it GOSUB?
0302: D0 23     12            BNE   OUT        ;Skip if not
0304: A9 03     13            LDA   #3         ;Make sure there's enough stack
0306: 20 D6 D3  14            JSR   GETSTK
0309: A5 B9     15            LDA   TXTPTR+1   ;Push marker for RETURN
030B: 48        16            PHA
030C: A5 B8     17            LDA   TXTPTR
030E: 48        18            PHA
030F: A5 76     19            LDA   CURLIN+1
0311: 48        20            PHA
0312: A5 75     21            LDA   CURLIN
0314: 48        22            PHA
0315: A9 B0     23            LDA   #GOSUBTKN
0317: 48        24            PHA
0318: 20 B1 00  25            JSR   CHRGET     ;Get next token
031B: 20 67 DD  26            JSR   FRMNUM     ;Parse numeric expr
031E: 20 52 E7  27            JSR   GETADR     ;Convert it to int
0321: 20 41 D9  28            JSR   GOTO+3     ;Point at chosen statement
0324: 4C D2 D7  29            JMP   NEWSTT     ;Start running it
0327: 60        30   OUT      RTS


--End assembly, 40 bytes, Errors: 0

Syntax:

 &  GOSUB numeric-expression

Again, the expression must evaluate to an actual line number, or you'll get ?UNDEF'D STATEMENT ERROR. Negative line numbers are accepted just as with computed GOTO.

RESTORE to Arbitrary (Computed) Line Number

It's sometimes handy to be able to RESTORE to a DATA statement other than the first one in the program, and many other BASICs allow it. This adds it to Applesoft:

                1    RESTORETKN =   $AE        ;Asoft RESTORE token
                2    DATPTR   =     $7D        ;DATA stmt pointer
                3    LOWTR    =     $9B        ;FNDLIN puts link ptr here
                4    CHRGET   =     $B1        ;Get next program token
                5    FNDLIN   =     $D61A      ;Find line in memory
                6    FRMNUM   =     $DD67      ;Evaluate a numeric expression
                7    GETADR   =     $E752      ;Convert number to 2-byte int
                8             ORG   $300
0300: C9 AE     9             CMP   #RESTORETKN ;Is it RESTORE?
0302: D0 19     10            BNE   OUT        ;Skip if not
0304: 20 B1 00  11            JSR   CHRGET     ;Get next token
0307: 20 67 DD  12            JSR   FRMNUM     ;Eval expression
030A: 20 52 E7  13            JSR   GETADR     ;Convert to int
030D: 20 1A D6  14            JSR   FNDLIN     ;Find chosen line no.
0310: A4 9C     15            LDY   LOWTR+1    ;Point DATPTR at byte before it
0312: A6 9B     16            LDX   LOWTR
0314: D0 01     17            BNE   DX
0316: 88        18            DEY
0317: CA        19   DX       DEX
0318: 84 7E     20            STY   DATPTR+1
031A: 86 7D     21            STX   DATPTR
031C: 60        22            RTS
031D: 60        23   OUT      RTS


--End assembly, 30 bytes, Errors: 0

Syntax:

 &  RESTORE numeric-expression

After executing & RESTORE, the next READ will start searching for DATA statements at the indicated line number.

The line number can be any numeric expression. It does not need to evaluate to a line number that actually exists in your program; if there is no such line number, READ will start searching at the next higher line number that does actually exist.

Negative line number are accepted just as with & GOTO and & GOSUB.

ON ... RESTORE

For those who prefer their computed RESTORE to work in a more classically Applesoft-like manner, here's an ON ... RESTORE statement that works like ON ... GOTO and ON ... GOSUB:

                1    RESTORETKN =   $AE        ;Asoft RESTORE token
                2    ONTKN    =     $B4        ;Asoft ON token
                3    DATPTR   =     $7D        ;DATA stmt pointer
                4    LOWTR    =     $9B        ;FNDLIN puts link ptr here
                5    FACLO    =     $A1        ;GETBYT puts result here
                6    CHRGET   =     $B1        ;Get next program token
                7    FNDLIN   =     $D61A      ;Find line in memory
                8    DATA     =     $D995      ;DATA stmt handler - skip to end of stmt
                9    LINGET   =     $DA0C      ;Parse line number
                10   SNERR    =     $DEC9      ;Fail with SYNTAX err
                11   GETBYT   =     $E6F8      ;Evaluate expr in range 0..255
                12            ORG   $300
0300: C9 B4     13            CMP   #ONTKN     ;Is it ON?
0302: D0 34     14            BNE   OUT        ;Skip if not
0304: 20 B1 00  15            JSR   CHRGET     ;Get next token
0307: 20 F8 E6  16            JSR   GETBYT     ;Eval expr, ILLEGAL QUANTITY if not in 0..255
030A: C9 AE     17            CMP   #RESTORETKN ;Is next token RESTORE?
030C: F0 03     18            BEQ   COUNT      ;Continue if so
030E: 4C C9 DE  19            JMP   SNERR      ;Else SYNTAX err
0311: C6 A1     20   COUNT    DEC   FACLO      ;Skipped enough line nums yet?
0313: D0 18     21            BNE   NXTNUM     ;If not, go skip another
0315: 20 B1 00  22            JSR   CHRGET     ;Else advance to 1st digit
0318: 20 0C DA  23            JSR   LINGET     ;Parse it
031B: 20 1A D6  24            JSR   FNDLIN     ;Find it in memory
031E: A4 9C     25            LDY   LOWTR+1    ;Point DATPTR at byte before it
0320: A6 9B     26            LDX   LOWTR
0322: D0 01     27            BNE   DX
0324: 88        28            DEY
0325: CA        29   DX       DEX
0326: 84 7E     30            STY   DATPTR+1
0328: 86 7D     31            STX   DATPTR
032A: 4C 95 D9  32            JMP   DATA       ;Skip over any remaining line nums & exit
032D: 20 B1 00  33   NXTNUM   JSR   CHRGET     ;Skipping - advance to 1st digit
0330: 20 0C DA  34            JSR   LINGET     ;Parse line num (& ignore it)
0333: C9 2C     35            CMP   #','       ;Followed by comma?
0335: F0 DA     36            BEQ   COUNT      ;If so, check if next line num is right
0337: 60        37            RTS              ;Else we're done.
0338: 60        38   OUT      RTS


--End assembly, 57 bytes, Errors: 0

Syntax:

 &  ON numeric-expression RESTORE linenum[,linenum[,...]]

As with Applesoft's ON ... GOTO/GOSUB, the numeric expression is any expression that evaluates to a number in the range 0 to 255. The linenums are a list of literal line numbers (no expressions allowed) separated by commas, and the numeric expression selects which line number to RESTORE to: if the expression is 1, the first line number is used, and if the expression is 2, the second line number is used, and so on. If the expression is 0 or greater than the number of line numbers, no RESTORE is done, and no error occurs.

If the expression is less than zero or greater than 255, ?ILLEGAL QUANTITY ERROR occurs.

The line number chosen need not actually exist in the program. If it doesn't, READ will start searching at the next higher line that does exist.

Two-byte PEEK

Sometimes it's convenient to deal with data PEEKed from memory in two-byte quantities. Here's a routine to help with that:

                1    LINNUM   =     $50        ;Result from GETADR
                2    CHKNUM   =     $DD6A      ;Make sure expr is number
                3    GIVAYF   =     $E2F2      ;Float signed int in A,Y
                4    GETADR   =     $E752      ;Convert number to int
                5             ORG   $300
0300: 20 6A DD  6             JSR   CHKNUM     ;TYPE MISMATCH if not number
0303: A5 51     7             LDA   LINNUM+1   ;Preserve LINNUM
0305: 48        8             PHA
0306: A5 50     9             LDA   LINNUM
0308: 48        10            PHA
0309: 20 52 E7  11            JSR   GETADR     ;Convert num to int
030C: A0 01     12            LDY   #1         ;Get value from mem into X,Y
030E: B1 50     13            LDA   (LINNUM),Y
0310: AA        14            TAX
0311: 88        15            DEY
0312: B1 50     16            LDA   (LINNUM),Y
0314: A8        17            TAY
0315: 68        18            PLA              ;Restore saved LINNUM
0316: 85 50     19            STA   LINNUM
0318: 68        20            PLA
0319: 85 51     21            STA   LINNUM+1
031B: 8A        22            TXA
031C: 4C F2 E2  23            JMP   GIVAYF     ;Float result


--End assembly, 31 bytes, Errors: 0

Syntax:

 USR (numeric-expression)

The argument must evaluate to a number from 0 to 65535 (or equivalently, -65536 to -1), which is interpreted as a memory address. W = USR (A) is roughly equivalent to W = PEEK (A) + 256 * PEEK (A + 1), except that runs faster and with fewer tokens, and it returns its result as a signed number (in the range -32768 ... 32767). If you need a positive result, you can get it like this:

 W = USR (A): IF W < 0 THEN W = W + 65536

Two-byte POKE

Here's the inverse of Two-byte PEEK - a statement that POKEs a two-byte number into memory:

                1    POKETKN  =     $B9        ;Asoft POKE token
                2    LINNUM   =     $50        ;Result from GETADR
                3    FORPNT   =     $85        ;Temp pointer
                4    CHRGET   =     $B1        ;Get next program token
                5    FRMNUM   =     $DD67      ;Evaluate a numeric expression
                6    CHKCOM   =     $DEBE      ;SYNTAX err if not comma
                7    GETADR   =     $E752      ;Convert num to 2-byte int
                8             ORG   $300
0300: C9 B9     9             CMP   #POKETKN   ;Is it POKE?
0302: D0 2A     10            BNE   OUT        ;Skip if not
0304: 20 B1 00  11            JSR   CHRGET     ;Get next token
0307: 20 67 DD  12            JSR   FRMNUM     ;Eval expression
030A: 20 52 E7  13            JSR   GETADR     ;Convert to int
030D: A5 51     14            LDA   LINNUM+1   ;Save it
030F: 48        15            PHA
0310: A5 50     16            LDA   LINNUM
0312: 48        17            PHA
0313: 20 BE DE  18            JSR   CHKCOM     ;Check for comma
0316: 20 67 DD  19            JSR   FRMNUM     ;Eval expression
0319: 20 52 E7  20            JSR   GETADR     ;Convert to int
031C: 68        21            PLA              ;Get saved number
031D: 85 85     22            STA   FORPNT
031F: 68        23            PLA
0320: 85 86     24            STA   FORPNT+1
0322: A0 00     25            LDY   #0         ;Store 2 bytes in memory
0324: A5 50     26            LDA   LINNUM
0326: 91 85     27            STA   (FORPNT),Y
0328: C8        28            INY
0329: A5 51     29            LDA   LINNUM+1
032B: 91 85     30            STA   (FORPNT),Y
032D: 60        31            RTS
032E: 60        32   OUT      RTS


--End assembly, 47 bytes, Errors: 0

Syntax:

 &  POKE numeric-expression,numeric-expression

Each argument must evaluate to a number from 0 to 65535 (or equivalently, -65536 to -1). The first argument is a memory address, and the second is a value to be POKEed into that address. & POKE A,W is roughly equivalent to WH = INT (W / 256):WL = W - WH * 256: POKE A,WL: POKE A + 1,WH, except that it runs faster and with fewer tokens, and doesn't use any temporary variables, and it handles negative values properly.

Delete an Array

Here's a routine to delete an array. This can be handy for saving memory, or if you need to re-dimension an array.

                1    DELTKN   =     $85        ;DEL token
                2    A1L      =     $3C        ;MOVE source start
                3    A1H      =     $3D
                4    A2L      =     $3E        ;MOVE source end
                5    A2H      =     $3F
                6    A4L      =     $42        ;MOVE dest start
                7    A4H      =     $43
                8    STREND   =     $6D        ;Ptr to end of vars
                9    LOWTR    =     $9B        ;GETARYPT puts address here
                10   CHRGET   =     $B1        ;Get next token
                11   GETARYPT =     $F7D9      ;Find array in memory
                12   MOVE     =     $FE2C      ;Block move
                13            ORG   $300
0300: C9 85     14            CMP   #DELTKN    ;Is it DEL?
0302: D0 44     15            BNE   DONE       ;Skip if not
0304: 20 B1 00  16            JSR   CHRGET     ;Advance to next token
0307: 20 D9 F7  17            JSR   GETARYPT   ;Parse array name & find
030A: A5 9B     18            LDA   LOWTR      ;Array address -> MOVE dest
030C: 85 42     19            STA   A4L
030E: A5 9C     20            LDA   LOWTR+1
0310: 85 43     21            STA   A4H
0312: A0 02     22            LDY   #2         ;Offset to array len
0314: B1 9B     23            LDA   (LOWTR),Y  ;Next array address -> MOVE src start
0316: 18        24            CLC
0317: 65 9B     25            ADC   LOWTR
0319: 85 3C     26            STA   A1L
031B: C8        27            INY
031C: B1 9B     28            LDA   (LOWTR),Y
031E: 65 9C     29            ADC   LOWTR+1
0320: 85 3D     30            STA   A1H
0322: A5 3C     31            LDA   A1L        ;Anything to move?
0324: C5 6D     32            CMP   STREND
0326: A5 3D     33            LDA   A1H
0328: E5 6E     34            SBC   STREND+1
032A: B0 13     35            BCS   FIXPTR     ;If not, don't bother moving
032C: A5 6E     36            LDA   STREND+1   ;End of arrays minus 1 -> MOVE src end
032E: 85 3F     37            STA   A2H
0330: A5 6D     38            LDA   STREND
0332: 85 3E     39            STA   A2L
0334: D0 02     40            BNE   DLO
0336: C6 3F     41            DEC   A2H
0338: C6 3E     42   DLO      DEC   A2L
033A: A0 00     43            LDY   #0
033C: 20 2C FE  44            JSR   MOVE       ;Leaves A4H,A4L pointing at new end of vars
033F: A5 42     45   FIXPTR   LDA   A4L        ;Adjust end of vars
0341: 85 6D     46            STA   STREND
0343: A5 43     47            LDA   A4H
0345: 85 6E     48            STA   STREND+1
0347: 60        49            RTS
0348: 60        50   DONE     RTS


--End assembly, 73 bytes, Errors: 0

Syntax:

 &  DEL array-name

The array-name is the name of any previously-DIM'd (or default-dimensioned) array, without the subscript specification. For example, after DIM A(100), the statement & DEL A will delete the array A. (The non-array variable A, if it exists, will be unaffected.)

Only one array can be deleted per & DEL statement. The array must already exist; if it doesn't, you'll get ?OUT OF DATA ERROR.

Any array - real, integer, or string - can be deleted (though in the case of string arrays, the strings themselves aren't freed...you'll need to use FRE (0), or under ProDOS, PRINT CHR$ (4);"FRE" to do that).

Deleting an array makes Applesoft forget entirely about it, and it can subsequently be re-created with different dimensions without error.

A corresponding function for deleting non-array variables is not supplied, as each deleted variable would recover only seven bytes of memory, and because deleting a non-array variable would require scanning the variable table for later DEF FN functions and adjusting them (this isn't an issue for arrays because there are no arrays of DEF FN functions).

Applesoft Program Relocator

Heres's a routine that moves Applesoft programs to different memory locations. This is often desirable, for example, to move a large program that uses hi-res graphics out of the way of the hi-res screen buffer.

Many programs do this without machine language help, by starting with a program line something like this:

10  IF  PEEK (103) + 256 *  PEEK (104) <  > 16385 THEN  POKE 103,1:
     POKE 104,64: POKE 16384,0: PRINT  CHR$ (4);"RUN THIS.PROGRAM"

But that ends up loading your program twice, once at the standard address, and once at the new address. If you use this routine, you only have to load your program once.

Due to the length of this routine, only the object code is shown here; the assembly listing is in a separate file.

0300:20 BE DE 20 67 DD 20 52 E7 A5 50 38 E5 67 85 40
0310:A5 51 E5 68 85 41 05 40 F0 22 B0 22 A5 67 85 3C
0320:A5 68 85 3D A5 AF 85 3E A5 B0 85 3F A5 50 85 42
0330:A5 51 85 43 A0 00 20 2C FE 98 F0 1E F0 70 A5 67
0340:85 9B A5 68 85 9C A5 AF 85 96 18 65 40 85 94 A5
0350:B0 85 97 65 41 85 95 20 9A D3 A5 50 85 3C A5 51
0360:85 3D C8 B1 3C F0 17 88 B1 3C 18 65 40 91 3C AA
0370:C8 B1 3C 65 41 91 3C 85 3D 86 3C 98 D0 E5 A5 51
0380:85 68 A5 50 85 67 D0 02 C6 51 C6 50 88 98 91 50
0390:A5 AF 18 65 40 85 AF 85 69 A5 B0 65 41 85 B0 85
03A0:6A A5 B8 18 65 40 85 B8 A5 B9 65 41 85 B9 20 6C
03B0:D6 4C D2 D7

This routine is unusual in that it doesn't use ampersand or USR. It's used like this:

 CALL 768,numeric-expression

where the numeric expression gives the address where the program should be moved to.

The address you pass should be address where you want the first byte of of the moved program to go. Be careful to choose an address with at least one free byte of memory before it...various parts of Applesoft need that byte to be there (it must contain the value 0, but you don't need to POKE it yourself - the routine takes care of that for you). Thus you should use the address 16385 to move above hi-res page 1, or 24577 to move above hi-res page 2, or 2049 to move back to the standard location.

Warnings:

The version of the Applesoft program relocator that used to be on this page before June 19, 2018 (the 183-byte version) had a bug in it that would damage your program if the new starting address was between the old starting address and the old ending address. Throw it away and use this new version instead.

Downloads

Source code files are for the Merlin assembler.

LLX > Neil Parker > Apple II > Addons for Applesoft

Original: June 2, 2016
Modified February 26, 2017--added ON ... RESTORE
Modified March 27, 2018--minor rewording; added downloadable files
Modified June 5, 2018--added Applesoft program relocator
Modified June 19, 2018--fixed a bug in the Applesoft program relocator
Modified June 26, 2018--added array deleter
Modified November 23, 2021--added separate DOS 3.3 .sdk archive