Apple Assembly Line
Volume 4 -- Issue 12 September 1984

In This Issue...

News about Micromation.

Jack Lewis's company, which among other things makes a line of Apple-related products to support the Heathkit Hero robot, has changed its name to Arctec Systems, Inc.

Jack also has a stand alone voice recognition system with an RS-232C interface which may be of interest to some of you. It contains a 65C02 processor, 4K ROM, and 16K of battery-backed-up RAM. Speaker-dependent recognition of up to 256 words or short phrases is possible, with 95-98% accuracy claimed. Arctec's number is (301) 730-1237, in Columbia, Maryland.

And Some Bad Tidings

The saddest news I have heard lately is of the demise (bankruptcy) of Softalk Publishing. Softalk has been my favorite of all the magazines devoted to the Apple. At this point I do not know how to obtain copies of any of their back issues, or of the books they have published. I assume, and hope, they will be available again soon. With the passing of so many companies, via Chapter 11, many magazines are having great difficulty this year. Unpaid advertising bills then cause a domino effect....


18-Digit Arithmetic, Part 5 Bob Sander-Cederlof

There is a lot of ground to cover in this installment, so I have been forced to use smaller type to squeeze it all in. I want to describe and list the code for the linkage to Applesoft, and for handling arithmetic expressions.

Loading and Linking to Applesoft

The ampersand (&) statement, according to the Applesoft Reference Manual (page 123, top of page) is

"intended for the computer's internal use only; it is not a proper Applesoft command. This symbol, when executed as an instruction, causes an unconditional jump to location $3F5. Use reset ctrl-C return to recover."

Not so! The &-statement is intended for adding extensions to the Applesoft language! It does cause a jump by the Applesoft interpreter to $3F5. If you have not set up any extensions you will get a syntax error when you use "&". But if you have extensions installed, you can work all manner of miracles. DP18 is one such miraculous extension. There are many more around, both in the public domain and in the form of commercial products.

This of course leads to a problem. What if you want to use two or more such extensions? I have written DP18 so that you can chain together one or more additional extension packages as you see fit.

It is very important to decide where the DP18 package will reside in memory. I spent weeks tossing around various options, back when I was designing the DPFP 21-digit package. Of course, at that time, Apples came equipped with anywhere from 16K to 64K RAM; now you can depend on almost all Apples having at least 48K RAM. I still favor the decision I made four years ago, to load the double precision code at $803, after shifting the Applesoft program far enough up in RAM to leave room.

I have a program I call ML LOADER, which is included on the S-C Macro Assembler disk as a sample program. It performs the function of moving an already- executing Applesoft program up higher in RAM. By including the following line at the beginning of my Applesoft program, I can load DP18 and link it to the & hook at $3F5:

     10 IF PEEK(104)=8 THEN PRINT CHR$(4)"BLOADB.ML LOADER"
        :POKE 768,0 : POKE769,30 : CALL770
        :PRINT CHR$(4)"BLOAD DP18"
        :POKE 1014,PEEK(2051) : POKE 1015,PEEK(2052)

PEEK(104) looks at the high byte of the starting address of the Applesoft program. Normally Applesoft programs begin at $801, so PEEK(104)=8. If DP18 has not yet been loaded, then PEEK(104) will still be equal to 8. If it has already been loaded, then the rest of line 10 is skipped.

B.ML LOADER loads at $300. Its function is to shove the Applesoft program higher in RAM. You POKE the distance to shove into 768 (low byte) and 769 (high byte), than CALL 770. When you wake up an instant later, you have been relocated. The Applesoft program keeps on executing as though nothing happened. Only now there is a gaping hole between $800 and whatever.

DP18 loads at $803 and extends well into page $25. I grabbed 30 pages, moving the Applesoft program to $2601.It thus clobbers hires screen 1 memory. If you want to use hires screen 2 and the program is too large to fit under it, use POKE 769,88 instead of POKE 769,30 in line 10. This makes the program start at $6001, and leaves $2600-3FFF totally unused.

If you want to use other ampersand routines, POKE the link address at locations 2053 and 2054 ($805 and $806). If DP18 finds an ampersand command not starting with "DP", it jumps indirectly through this vector. The vector initially contains the address of Applesoft's SYNTAX ERROR routine, but it can be changed to allow using more than one set of &-routines.

Calling DP18

Whenever you want to execute a DP18 feature, you use the "&DP" statement. If DP18 has been properly connect to the & hook at $3F5, then the & will send the computer to DP18 (at line 2430 in the listing which follows). At this point DP18 begins to analyze and execute the characters that follow the ampersand.

If the first two characters after the ampersand are not "DP", the program will jump to a vector at $805 & $806. This normally points to Applesoft's SYNTAX ERROR routine. However, this location can easily be patched to point to your own ampersand routine.

If the first two characters are correct, DP18 will analyze succeeding statements separated by colons on the same line. There must be a colon immediately after the "&DP" statement. All of the rest of the statements on the line will be executed by DP18, rather than by the normal Applesoft interpreter. If you want to shut off DP18 before the end of the line, two colons in a row with nothing between will do so.

     150  & DP: INPUT X(0)
     160  & DP:Y(0) = X(0) * X(0) * PI: PRINT Y(0) :: GOTO 150

It is not necessary that the "&DP:" be the first statement in a line. For example, the following statement will take the square root of a number if the two strings are equal. It uses an Applesoft string comparison, and a double precision square root.

     170  IF A$ = "SQR" THEN  & DP:Y(0) =  SQR (X(0))

You can also type double precision statements as direct commands in Applesoft once DP18 has been loaded.

     ]&DP:PRINT X(0): PRINT X(0) ^ 2

Four types of statements can be executed by the DP18 package: assignment, INPUT, PRINT, and IF statements. INPUT and PRINT statements will be covered in a later installment.

The DP18 IF statement evaluates a logical expression in 18-digit precision, and then reverts to normal Applesoft processing:

     180 &DP : IF A(0) < 1.52345678976543 THEN X = 3

The DP18 assignment statement takes two forms: real assignment, and string assignment. String assignment is used to convert DP18 values to strings, so that they can be used by normal Applesoft:

     190 &DP : A$ = STR$ (X(0))

Real assignments are the normal computational statements, like:

     200 &DP : A(0) = (4*PI*R(0)^3)/3

DP18 Variables

All variables referenced by DP18 must consist of two adjacent array elements. The array must be a REAL array, that is, it must not be INTEGER or STRING.

Remember that Applesoft array subscripts begin with 0 and go up to the limit defined in the DIM statement. An array dimensioned "3,11,11" has three dimensions. The first runs from 0 to 3; the second from 0 to 11; and the third also from 0 to 11. It could contain 4*12*12=576 real elements, or 2*12*12=288 double precision elements.

Applesoft arrays are stored in memory with the leftmost subscript varying the fastest. For example, in the array XY(3,10,10), element XY(0,j,k) comes immediately before element XY(1,j,k). Therefore you may, in effect, create an array of double precision values by merely prefixing an extra dimension to the dimension list.

If you wish to set up separate variables, you may do so by dimensioning them to have two real elements each. For example, the statement

     10  DIM A(1),B(1),C(1),X(1)

will set up four separate variables for use with DP18. You reference the variables within double precision statements with the subscript 0. For example:

     20  & DP:X(0) = (A(0) + B(0)) * C(0)

Note that you don't have to dimension these variables, since Applesoft will default to a dimension of 10. However, it is a good idea to dimension all double precision variables because it saves memory (only 2 real elements are allocated instead of 11) and it makes it easier for someone else to follow your program.

If you wish to create an array of double precision values, you do so by dimensioning the array with one extra dimension. The extra dimension comes first and should be "1"; this dimension generates two real items, or one double precision item. For example,

     10  DIM A(1,12),B(1,5,6)

creates two arrays that can be used for double precision values. The array A can be thought of as an array of 13 double precision values from A(0,0) to A(0,12). The array B could store 42 double precision values from B(0,0,0) to B(0,5,6). If you always remember to use one extra dimension, to put that extra dimension first, to set that dimension to "1", and to refer to items with the first subscript = 0, then you will succeed in using DP18.

DP18 Constants

Double precision constants are entered in the same way as single precision constants. The differences between standard Applesoft and the DP18 constants are that DP18 converts and stores 18 significant digits rather than 9, and that exponents may be in the range of +/- 63 rather than +/- 38.

Conversion of constants is very fast in DP18. DP18 will convert constants over 4 times faster than normal Applesoft, even using more digits! It is quicker to convert a constant than it is to find and use a DP18 variable, especially multi-dimensioned variables. This is completely opposite from normal Applesoft, where variables are quicker than constants.

Conversion Between Single and Double Precision

You will often need to convert a single precision value into a double precision one for purposes of computation. This is easily done by first converting it to a string and then using DP18's VAL function as shown here.

     100  REM CONVERT X TO DOUBLE PRECISION VALUE
     110  DIM DP(1)
     120  INPUT "VALUE TO BE CONVERTED? ";X
     130  &DP:DP(0) =  VAL ( STR$ (X))
     140  &DP: PRINT DP(0)
     150  GOTO 120

You will also want to convert from double precision back to single precision. This also involves converting to a string, but takes more than one statement.

     100  REM CONVERT DP(0) TO SINGLE PRECISION VALUE
     110  DIM DP(1)
     120  &DP:INPUT "VALUE TO BE CONVERTED? ";DP(0)
     130  &DP:A$ =  STR$ (DP(0))
     140  X =  VAL (A$) : PRINT X
     150  GOTO 120

Note that lines 130 and 140 could be combined onto one line if there were two colons separating the statements. See the section on functions for more information about the STR$ and VAL functions.

DP18 Arithmetic Expressions

Expressions in DP18 are very much like expressions in Applesoft. Except for AND and OR, they are evaluated using the standard rules of precedence as found on page 36 of the Applesoft manual. AND and OR have the same precedence in DP18 and are executed left to right. The order of precedence is listed below. Operations on a higher line are executed before operations on a lower line. Operators on the same line are executed left to right.

     ( ) function calls
     + - NOT        unary operators
     ^
     * /
     + -
     <  >  =  <=  >=  =>  =<  <>  ><
     AND OR

These all work the same as they do in Applesoft, except that they operate on double precision numbers.

DP18 supports many of the numerical functions that Applesoft does: SIN, COS, TAN, LOG, EXP, SGN, ABS, INT, SQR, ATN, VAL, and the string function STR$. There is also a special function, PI, which has no arguments. You don't even write parentheses after it. You just use it like it was a constant. Wherever you use it, you get the value pi accurate to 20 digits.

Explanation of the Code

As in previous installments of this series on DP18, I cannot show everything at once. A whole series of subroutines which have either already been printed or will be printed in future installments are represented in this listing by ".EQ $FFFF" in lines 1330-1550. All the data areas actually used in the code listed this month are included, so that you can see what the code is working with and on.

As mentioned above, the "&" statement sends Applesoft to line 2430. Lines 2430-2500 check for "DP" following the ampersand. If not "DP", then lines 2370-2390 branch to the next ampersand interpreter in your chain. If you have not set up another &-interpreter, then the SYNTAX ERROR message will pop out.

DP.NEXT.CMD (lines 2520-2800) begins by looking for a colon or end-of-line. End of line means you are through with DP18, so an RTS carries you back to the Applesoft interpreter. A colon means you are ready with a DP18 statement. If the next character is also a colon, however, you are sent back to Applesoft (lines 2570-2580). Next I check for the three legal tokens (IF, INPUT, and PRINT) and branch accordingly.

Since IF is simple and IF is included in this listing, let's look at IF now. Lines 3130-3280 handle the IF statement. First I evaluate the expression, which is considered to be a logical expression with a true-or-false value. Zero means false, non-zero means true. Following the expression I must find either a THEN or GOTO token. The truth value is found in DAC.EXPONENT, because a $00 exponent means a zero value. AS.IF.JUMP in the Applesoft ROMs can handle the rest, because the THEN or GOTO pops us out of DP18 back to normal Applesoft. Neat!

Meanwhile, back in DP.NEXT.CMD, if the statement is not IF-INPUT-PRINT it must be an assignment statement. If I am successful at getting a variable name next, it may be either a DP18 variable or a string variable. If AS.VALTYP is negative, it is a string variable and DP.STR takes over. If not, CHECK.DP.VAR will verify that it is a real array variable. The address is saved at RESULT, the DP18 expression evaluated, and then the answer saved at RESULT. And back to the top of DP.NEXT.CMD.

DP.STR handles statements like A$=STR$(xxx) where xxx is a DP18 expression. You can probably follow the comments in this section.

GET.A.VAR checks to see that the current character from your program is a letter, because all variables must start with a letter. If so, AS.PTRGET will search the variable tables and return with an address in the Y- and A-registers. CHECK.DP.VAR compares this address with the beginning of the array variable table. If it is inside the array table, and if the variable is real (not string or integer), it is a valid DP18 variable.

DP.EVALUATE cracks and calculates a DP18 expression. A special stack is used for temporary values, and it is deep enough to hold 10 of them. If your expression is so complicated that more than 10 temporary values need to be stacked (very unlikely), then the FORMULA TOO COMPLEX message will scream. Applesoft uses the hardware stack in page 1 for the same purpose, but it only has to stack 5-byte values; DP18 stacks 12 bytes for each value. EVALUATE starts by emptying the stack, zeroing a parenthesis level count, and clearing the accumulator (DAC). After DP.EXP finishes all the dirty work, The stack must be empty and the parenthesis level zero or there was a SYNTAX ERROR.

Actually parsing and computing an expression can be done in many ways. I chose a recursive approach that breaks the job up into little independent pieces small enough to understand. First, let's allow all expressions to be a series of relational expressions connected with ANDs and ORs. The simplest case of this is merely a relational expression alone. And the simplest relational expression is an expression all by itself with no relations. If the expression does have relational operators or ANDs or ORs, the result will be a true or false value. If not, it will have a numerical value.

Comment blocks atop DP.EXP, DP.RELAT, DP.SUM, etc. show the continued breakdown of parts of an expression. DP.RELAT connects one or more sums with relational operators. DP.SUM connects one or more terms with "+" and "-" operators. DP.TERM connects one or more factors with "*" and "/" operators. DP.FACTOR connects one or more elements with the exponentiation operator (^). DP.ELEMENT cracks a constant, searches for a variable's value, calls a function, or calls on DP.EXP recursively to handle an expression in parentheses. DP.ELEMENT also handles the unary operators "+", "-", and "NOT".

If DP.ELEMENT determines that the element is a function call, there are several types. The VAL function is supervised by lines 5800-5830. Since the argument of the VAL function is a string expression, it is significantly different from the other functions. The ATN function is also given special treatment, because DP18 allows the ATN function to be called with one or two arguments. All the rest of the functions have one DP18 expression for an argument, so they are handled as a group. A table of addresses at lines 2160-2310 directs us to the appropriate processor. The code for all these functions will be revealed in future installments.

DP.VARNUM is called upon to handle variables and numbers. First lines 6130- 6280 check for and handle the special DP18 constant "PI". Lines 6300-6350 handle DP18 variables, and lines 6370-6470 handle numbers.

PUSH.DAC.STACK pushes the 12-byte value in DAC on the special expression stack, unless there is not enough room. POP.STACK.ARG pulls a 12-byte value off the stack and plops it into ARG.

And Next Month...

There are three major areas left for future installments: INPUT, PRINT, and the math functions. Some of you have been diligently studying and entering each installment as we go, and are gradually obtaining a powerful package. Others are waiting for the Quarterly Disks, to conserve their fingertips. Remember, all the source code each three months is available on disk for only $15.

  1000 *SAVE S.DP18 AMPER-LINK
  1010 *--------------------------------
  1020        .OR $803
  1030 *-------------------------------
  1040 *    APPLESOFT SUBROUTINES
  1050 *-------------------------------
  1060 AS.ADDON     .EQ $D998    ADD (Y) TO TXTPTR
  1070 AS.IF.JUMP   .EQ $D9DA    HANDLE T/F FOR IF
  1080 AS.FRMNUM    .EQ $DD67    EVAL FP FORMULA
  1090 AS.CHKCLS    .EQ $DEB8    CHECK FOR )
  1100 AS.CHKOPN    .EQ $DEBB    CHECK FOR (
  1110 AS.CHKCOM    .EQ $DEBE    CHECK FOR COMMA
  1120 AS.SYNCHR    .EQ $DEC0    CHARACTER SCAN OR FAIL
  1130 AS.SYNERR    .EQ $DEC9    SYNTAX ERROR
  1140 AS.PTRGET    .EQ $DFE3    FIND VARIABLE
  1150 AS.ISLETC    .EQ $E07D    LETTER CHECK
  1160 AS.FRMCPX    .EQ $E430    "FORMULA TOO COMPLEX" ERROR
  1170 AS.GETSPA    .EQ $E452    GET SPACE FOR STRING
  1180 AS.MOVSTR    .EQ $E5E2    MOVE STRING
  1190 *--------------------------------
  1200 *      PAGE ZERO USAGE
  1210 *-------------------------------
  1220 AS.VALTYP    .EQ $11    LAST FAC OP 0=NUM,FF=STRING 
  1230 ARYTAB       .EQ $6B,6C
  1240 AS.FRESPA    .EQ $71,72
  1250 VARNAM       .EQ $81,82
  1260 AS.CHRGET    .EQ $B1
  1270 AS.CHRGOT    .EQ $B7
  1280 TXTPTR       .EQ $B8,B9
  1290 P2           .EQ $F9
  1300 *--------------------------------
  1310 *   DP18 SUBROUTINES ASSEMBLED ELSEWHERE
  1320 *--------------------------------
  1330 DP.PRINT            .EQ $FFFF
  1340 DP.INPUT            .EQ $FFFF
  1350 FIN                 .EQ $FFFF
  1360 DP.SGN              .EQ $FFFF
  1370 DP.INT              .EQ $FFFF
  1380 DP.ABS              .EQ $FFFF
  1390 DP.SQR              .EQ $FFFF
  1400 DP.LOGE             .EQ $FFFF
  1410 DP.EXPE             .EQ $FFFF
  1420 DP.COS              .EQ $FFFF
  1430 DP.SIN              .EQ $FFFF
  1440 DP.TAN              .EQ $FFFF
  1450 MOVE.DAC.YA         .EQ $FFFF
  1460 QUICK.FOUT          .EQ $FFFF
  1470 DP.POWER            .EQ $FFFF
  1480 DSUB                .EQ $FFFF
  1490 DADD                .EQ $FFFF
  1500 DMULT               .EQ $FFFF
  1510 DDIV                .EQ $FFFF
  1520 DP.ATN              .EQ $FFFF
  1530 DP.VAL              .EQ $FFFF
  1540 MOVE.YA.DAC         .EQ $FFFF
  1550 MOVE.YA.DAC.1       .EQ $FFFF
  1560 *--------------------------------
  1570 *   AMPERSAND VECTORS
  1580 *--------------------------------
  1590          .DA DP18     STARTING ADDRESS FOR &-INTERPRETER
  1600 AMP.LINK .DA AS.SYNERR   LINK TO NEXT &-INTERPRETER
  1610 *-------------------------------
  1620 *      WORK AREAS FOR DPFP
  1630 *-------------------------------
  1640 WORK          .EQ * 
  1650 SGNEXP        .BS 1
  1660 EXP           .BS 1
  1670 DGTCNT        .BS 1
  1680 DECFLG        .BS 1
  1690 *-------------------------------
  1700 DAC           .BS 12
  1710 DAC.EXPONENT  .EQ DAC
  1720 DAC.HI        .EQ DAC+1
  1730 DAC.EXTENSION .EQ DAC+10
  1740 DAC.SIGN      .EQ DAC+11
  1750 *-------------------------------
  1760 WRKSZ         .EQ *-WORK
  1770 *-------------------------------
  1780 ARG           .BS 12
  1790 *--------------------------------
  1800 *BUFFER FOR 'FOUT' AND
  1810 *LARGE ACC FOR MULTIPLICATION
  1820 *--------------------------------
  1830 FOUT.BUF      .BS 41
  1840 FOUT.BUF.SIZE .EQ *-FOUT.BUF
  1850 MAC           .EQ FOUT.BUF
  1860 *-------------------------------
  1870 STACK.SIZE    .EQ 12*10 10 ENTRIES BEFORE OVERFLOW
  1880 STACK.PNTR    .BS 1
  1890 STACK         .BS STACK.SIZE
  1900 RPAREN.CNT    .BS 1
  1910 *-------------------------------
  1920 REL.OPS       .BS 1
  1930 RESULT        .BS 2
  1940 INDEX         .BS 1
  1950 *-------------------------------
  1960 *      TOKEN ASSIGNMENTS
  1970 *-------------------------------
  1980 TKN.PLUS   .EQ 200  +
  1990 TKN.MINUS  .EQ 201  -
  2000 TKN.STAR   .EQ 202  *
  2010 TKN.SLASH  .EQ 203  /
  2020 TKN.POWER  .EQ 204  ^
  2030 TKN.EQUAL  .EQ 208  =
  2040 TKN.PRINT  .EQ 186  PRINT
  2050 TKN.INPUT  .EQ 132  INPUT
  2060 TKN.STR    .EQ 228  STR$
  2070 TKN.IF     .EQ 173  IF
  2080 TKN.THEN   .EQ 196  THEN
  2090 TKN.GOTO   .EQ 171  GOTO
  2100 TKN.NOT    .EQ 198  NOT
  2110 TKN.AND    .EQ 205  AND
  2120 TKN.OR     .EQ 206  OR
  2130 *-------------------------------
  2140 *      JMP TABLE FOR FUNCTIONS
  2150 *--------------------------------
  2160 DP.FUNC
  2170        .DA DP.SGN-1        SGN (TKN 210)
  2180        .DA DP.INT-1        INT
  2190        .DA DP.ABS-1        ABS
  2200        .DA AS.SYNERR-1     USR
  2210        .DA AS.SYNERR-1     FRE
  2220        .DA AS.SYNERR-1     SCRN(
  2230        .DA AS.SYNERR-1     PDL
  2240        .DA AS.SYNERR-1     POS
  2250        .DA DP.SQR-1        SQR
  2260        .DA AS.SYNERR-1     RND
  2270        .DA DP.LOGE-1       LOG  #220
  2280        .DA DP.EXPE-1       EXP
  2290        .DA DP.COS-1        COS
  2300        .DA DP.SIN-1        SIN
  2310        .DA DP.TAN-1        TAN
  2320 *      ATN HANDLED SPECIALLY
  2330 *--------------------------------
  2340 *--------------------------------
  2350 *      &-INTERPRETER FOR DP18
  2360 *--------------------------------
  2370 NOT.DP18.CALL
  2380        JSR AS.CHRGOT
  2390        JMP (AMP.LINK)  SYNTAX ERROR OR NEXT CHAINED &-ROUTINE
  2400 *--------------------------------
  2410 *   & ENTRY POINT
  2420 *--------------------------------
  2430 DP18   CMP #'D'     CHECK FOR "DP:" AFTER "&"
  2440        BNE NOT.DP18.CALL
  2450        LDY #1
  2460        LDA (TXTPTR),Y
  2470        CMP #'P'
  2480        BNE NOT.DP18.CALL
  2490        INY          ADD 2 TO TXTPTR, TO POINT
  2500        JSR AS.ADDON      AT NEXT CHAR AFTER "&DP"
  2510 *--------------------------------
  2520 DP.NEXT.CMD
  2530        JSR AS.CHRGOT  SEE IF EOL
  2540        BNE DP.SYNERR.1  ...NEITHER COLON NOR EOL
  2550        TAY          CHECK FOR EOL
  2560        BEQ .3       ...EOL, SO RETURN
  2570        JSR AS.CHRGET     CHARACTER AFTER COLON
  2580        BEQ .3       ...COLON OR EOL
  2590        CMP #TKN.PRINT
  2600        BEQ .1
  2610        CMP #TKN.INPUT
  2620        BEQ .2
  2630        CMP #TKN.IF
  2640        BEQ DP.IF
  2650        JSR GET.A.VAR GET ADDRESS OF VAR
  2660        LDX AS.VALTYP
  2670        BMI DP.STR  STRING VAR
  2680        JSR CHECK.DP.VAR
  2690        STY RESULT+1   SAVE ADRS OF VARIABLE
  2700        STA RESULT
  2710        LDA #TKN.EQUAL   NEXT CHAR MUST BE "="
  2720        JSR AS.SYNCHR  OR ELSE SYNTAX ERROR
  2730        JSR DP.EVALUATE
  2740        LDA RESULT
  2750        LDY RESULT+1
  2760        JSR MOVE.DAC.YA
  2770        JMP DP.NEXT.CMD
  2780 .1     JMP DP.PRINT
  2790 .2     JMP DP.INPUT
  2800 .3     RTS
  2810 *--------------------------------
  2820 DP.SYNERR.1
  2830        JMP AS.SYNERR
  2840 *--------------------------------
  2850 *   <STRING> = STR$(<DPEXP>)
  2860 *--------------------------------
  2870 DP.STR STA P2            SAVE ADDR OF STRING VARIABLE
  2880        STY P2+1
  2890        LDA #TKN.EQUAL    MUST HAVE "="
  2900        JSR AS.SYNCHR
  2910        LDA #TKN.STR      MUST HAVE "STR$"
  2920        JSR AS.SYNCHR
  2930        JSR AS.CHKOPN     MUST HAVE "("
  2940        JSR DP.EVALUATE   GET EXPRESSION
  2950        JSR AS.CHKCLS     MUST HAVE ")"
  2960        JSR QUICK.FOUT    CONVERT TO SIMPLE STR$ FORMAT
  2970        DEC INDEX         DON'T COUNT TRAILING $00 BYTE
  2980        LDA INDEX         GET LENGTH
  2990        JSR AS.GETSPA     GET SPACE IN STRING AREA
  3000        LDY #0            MOVE DATA INTO VARIABLE
  3010        STA (P2),Y        LENGTH
  3020        LDA AS.FRESPA
  3030        INY
  3040        STA (P2),Y        LO ADDRESS
  3050        LDA AS.FRESPA+1
  3060        INY
  3070        STA (P2),Y        HI ADDRESS
  3080        LDX #FOUT.BUF     COPY STRING DATA INTO PLACE
  3090        LDY /FOUT.BUF
  3100        LDA INDEX
  3110        JSR AS.MOVSTR
  3120        JMP DP.NEXT.CMD
  3130 *--------------------------------
  3140 *   IF <DPEXP> THEN <NORMAL STATEMENTS>
  3150 *   IF <DPEXP> THEN <LINE #>
  3160 *   IF <DPEXP> GOTO <LINE #>
  3170 *--------------------------------
  3180 DP.IF  JSR AS.CHRGET     GOBBLE THE IF
  3190        JSR DP.EVALUATE   GET THE EXPRESSION
  3200        JSR AS.CHRGOT     GET NEXT CHAR
  3210        CMP #TKN.GOTO     GOTO?
  3220        BEQ .1            ...YES
  3230        LDA #TKN.THEN     ...NO, TRY "THEN"
  3240        JSR AS.SYNCHR
  3250 .1     LDA DAC.EXPONENT  GET RESULT OF EXPRESSION
  3260        JMP AS.IF.JUMP    LET APPLESFOT FIRMWARE DO IT
  3270 * AS.IF.JUMP COMPARES ACC TO 0. IF 0, IT SKIPS
  3280 * TO NEXT PROG. LINE. IF # 0, IT EXECUTES NEXT STATEMENT
  3290 *--------------------------------
  3300 *   GET VARIABLE NAME AND ADDRESS
  3310 *--------------------------------
  3320 GET.A.VAR
  3330        JSR AS.ISLETC     1ST CHAR MUST BE LETTER
  3340        BCC DP.SYNERR.1       NO, SYN ERR
  3350        JMP AS.PTRGET GET ADRS OF VAR
  3360 *--------------------------------
  3370 *   CHECK IF VALID DP18 VARIABLE
  3380 *      ASSUME THIS ROUTINE CALLED AFTER "GET.A.VAR"
  3390 *      A DP18 VARIABLE MUST BE A REAL ARRAY
  3400 *--------------------------------
  3410 CHECK.DP.VAR
  3420        CPY ARYTAB+1      BE SURE IT IS AN ARRAY
  3430        BCC DP.SYNERR.1       NO, SYNTAX ERROR
  3440        BNE .1       YES, AN ARRAY
  3450        CMP ARYTAB
  3460        BCC DP.SYNERR.1       NOT AN ARRAY, SYNTAX ERROR
  3470 .1     BIT VARNAM+1 BE SURE FLOATING POINT
  3480        BMI DP.SYNERR.1       NO, SYNTAX ERROR
  3490        RTS
  3500 *--------------------------------
  3510 *   EVALUATE DP18 EXPRESSION
  3520 *--------------------------------
  3530 DP.EVALUATE
  3540        LDA #0            START WITH EMPTY STACK
  3550        STA RPAREN.CNT    ...AND NO PARENTHESES
  3560        STA STACK.PNTR
  3570        JSR DP.ZERO       ZERO TO DAC
  3580        JSR DP.EXP        EVALUATE AN EXPRESSION
  3590        LDA STACK.PNTR    SHOULD BE BACK TO EMPTY STACK
  3600        ORA RPAREN.CNT    AND NO PARENTHESES
  3610        BNE DP.SYNERR.2   ...SYNTAX ERROR
  3620        RTS               ...ALL OKAY!
  3630 *--------------------------------
  3640 *   GENERAL EXPRESSION
  3650 *      EXP = RELAT
  3660 *      EXP = EXP LOGOP RELAT
  3670 *    LOGOP = "AND" OR "OR"
  3680 *--------------------------------
  3690 DP.EXP JSR DP.RELAT
  3700 .1     JSR AS.CHRGOT
  3710        CMP #TKN.AND
  3720        BEQ .3
  3730        CMP #TKN.OR
  3740        BNE .6       ...FINISHED
  3750 *---<EXP> OR <EXP>---------------
  3760        JSR .5       GET NEXT RELAT
  3770        ORA DAC.EXPONENT
  3780        BNE .4       ...TRUE
  3790 .2     JSR DP.FALSE ...FALSE
  3800        JMP .1
  3810 *---<EXP> AND <EXP>--------------
  3820 .3     JSR .5       GET NEXT RELAT
  3830        AND DAC.EXPONENT
  3840        BEQ .2       ...FALSE
  3850 .4     JSR DP.TRUE  ...TRUE
  3860        JMP .1
  3870 *---GET <EXP> AFTER RELOP--------
  3880 .5     LDA DAC.EXPONENT
  3890        PHA
  3900        JSR AS.CHRGET
  3910        JSR DP.RELAT
  3920        PLA
  3930 .6     RTS
  3940 *--------------------------------
  3950 DP.SYNERR.2
  3960        JMP AS.SYNERR
  3970 *--------------------------------
  3980 *   RELATIONAL EXPRESSION
  3990 *      RELAT = SUM
  4000 *      RELAT = RELAT RELOP SUM
  4010 *      RELOP = "<", "=", ">", "<=", "=<", ">=",
  4020 *              "=>", "<>", OR "><"
  4030 *--------------------------------
  4040 DP.RELAT
  4050        JSR DP.SUM   GET <EXP>
  4060 .1     LDA #0
  4070        STA REL.OPS
  4080        JSR AS.CHRGOT
  4090 .2     SEC          > IS $CF, = IS $D0, < IS $D1
  4100        SBC #$CF     > IS 0, = IS 1, < IS 2
  4110        BCC .4       ...NOT RELOP
  4120        CMP #$03
  4130        BCS .4       ...NOT RELOP
  4140        ROL          > IS 0, = IS 2, < IS 4
  4150        BNE .3       4 OR 2
  4160        LDA #1       > IS 1
  4170 .3     EOR REL.OPS  SET BITS IN REL.OPS: 00000<=>
  4180        CMP REL.OPS  CHECK FOR REPEATED OPS
  4190        BCC DP.SYNERR.2   ...YES, SYNTAX ERROR
  4200        STA REL.OPS
  4210        JSR AS.CHRGET     GET NEXT CHAR
  4220        JMP .2       CHECK FOR <=> AGAIN
  4230 *---PERFORM RELOP----------------
  4240 .4     LDA REL.OPS  WERE THERE ANY?
  4250        BEQ .8       NO, RETURN
  4260        CMP #7       ALL THREE OPS?
  4270        BEQ DP.SYNERR.2   ...YES, SYNTAX ERROR
  4280        JSR PUSH.DAC.STACK  SAVE EXP1
  4290        JSR DP.SUM          GET NEXT EXP2
  4300        JSR POP.STACK.ARG   GET EXP1 IN ARG
  4310        JSR DSUB            FORM EXP1 - EXP2
  4320        LDA DAC.EXPONENT
  4330        BEQ .45             EXP1 = EXP2
  4340        LDA DAC.SIGN
  4350        BMI .6              EXP1 < EXP2
  4360        LDA REL.OPS         EXP1 > EXP2
  4370        AND #$01            ">" OPERATOR?
  4380        BEQ .7              ...NO, FALSE
  4390        BNE .5              ...YES, TRUE
  4400 .45    LDA REL.OPS  EXP1 = EXP2
  4410        AND #$02     "=" OPERATOR?
  4420        BEQ .7       ...NO, FALSE
  4430 .5     JSR DP.TRUE  ...YES, TRUE
  4440        JMP .1
  4450 .6     LDA REL.OPS  EXP1 < EXP2
  4460        AND #$04     "<" OPERATOR?
  4470        BNE .5       ...YES, TRUE
  4480 .7     JSR DP.FALSE ...NO, FALSE
  4490        JMP .1
  4500 .8     RTS
  4510 *--------------------------------
  4520 *   SUMMATION
  4530 *      SUM = TERM
  4540 *      SUM = SUM ADDOP TERM
  4550 *    ADDOP = "+" OR "-"
  4560 *--------------------------------
  4570 DP.SUM JSR DP.TERM
  4580 .1     JSR AS.CHRGOT
  4590        CMP #TKN.PLUS
  4600        BEQ .3       +
  4610        CMP #'+
  4620        BEQ .3       +
  4630        CMP #TKN.MINUS
  4640        BEQ .4       -
  4650        CMP #'-
  4660        BEQ .4       -
  4670        RTS          END OF EXP
  4680 .3     CLC          .CC. FOR +, .CS. FOR -
  4690 .4     PHP          SAVE WHETHER + OR -
  4700        JSR PUSH.DAC.STACK
  4710        JSR AS.CHRGET
  4720        JSR DP.TERM
  4730        JSR POP.STACK.ARG
  4740        PLP          .CC. FOR +, .CS. FOR -
  4750        BCC .5
  4760        LDA DAC.SIGN
  4770        EOR #$FF
  4780        STA DAC.SIGN
  4790 .5     JSR DADD
  4800        JMP .1
  4810 *--------------------------------
  4820 *   TERMS OF A SUMMATION
  4830 *      TERM = FACTOR
  4840 *      TERM = TERM MULOP FACTOR
  4850 *     MULOP = "*" OR "/"
  4860 *--------------------------------
  4870 DP.TERM
  4880        JSR DP.FACTOR
  4890 .1     JSR AS.CHRGOT
  4900        CMP #TKN.STAR     *?
  4910        BEQ .2
  4920        CMP #TKN.SLASH    / ?
  4930        BEQ .3
  4940        RTS
  4950 .2     CLC          .CC. FOR *, .CS. FOR /
  4960 .3     PHP          SAVE * OR / FLAG
  4970        JSR PUSH.DAC.STACK
  4980        JSR AS.CHRGET
  4990        JSR DP.FACTOR
  5000        JSR POP.STACK.ARG
  5010        PLP          GET * OR / FLAG
  5020        BCS .4       .../
  5030        JSR DMULT    ...*
  5040        JMP .1
  5050 .4     JSR DDIV
  5060        JMP .1
  5070 *--------------------------------
  5080 *   FACTORS OF A TERM
  5090 *      FACTOR = ELEMENT
  5100 *      FACTOR = FACTOR ^ ELEMENT
  5110 *--------------------------------
  5120 DP.FACTOR
  5130        JSR AS.CHRGOT
  5140        JSR DP.ELEMENT.1
  5150 .1     JSR AS.CHRGOT
  5160        CMP #TKN.POWER  ^?
  5170        BEQ .2
  5180        RTS          NO
  5190 .2     JSR PUSH.DAC.STACK
  5200        JSR DP.ELEMENT
  5210        JSR POP.STACK.ARG
  5220        JSR DP.POWER
  5230        JMP .1
  5240 *--------------------------------
  5250 *   ELEMENTS OF A FACTOR
  5260 *      ELEMENT = NUMBER, VARIABLE, OR FUNCTION()
  5270 *      ELEMENT = (EXP)
  5280 *      ELEMENT = UNARY ELEMENT
  5290 *        UNARY = "+" OR "-" OR "NOT"
  5300 *--------------------------------
  5310 DP.ELEMENT
  5320        JSR AS.CHRGET
  5330 DP.ELEMENT.1
  5340        CMP #TKN.PLUS     CHECK FOR UNARY +
  5350        BEQ DP.ELEMENT    ...YES, JUST IGNORE IT
  5360        CMP #TKN.MINUS    CHECK FOR UNARY -
  5370        BNE .1            ...NO
  5380        JSR DP.ELEMENT    GET THE EXP VALUE (RECURSIVE CALL)
  5390        LDA DAC.SIGN   AND NEGATE IT
  5400        EOR #$FF
  5410        STA DAC.SIGN
  5420        RTS
  5430 *---CHECK FOR (EXP)--------------
  5440 .1     CMP #'(
  5450        BNE .2       ...NO
  5460        INC RPAREN.CNT
  5470        JSR AS.CHRGET  GET 1ST CHAR OF EXP
  5480        JSR DP.EXP   (EXP)
  5490        JSR AS.CHKCLS
  5500        DEC RPAREN.CNT
  5510        RTS
  5520 *---TRY VARIOUS FUNCTIONS--------
  5530 .2     TAY               SEE IF FUNCTION
  5540        BPL DP.VARNUM     ...NO, TRY NUMBER OR VARIABLE
  5550        CMP #TKN.NOT      "NOT"?
  5560        BEQ .5            ...YES
  5570        CMP #210          CHECK RANGE
  5580        BCC DP.SYNERR.3   ...NOT VALID DP FUNCTION
  5590        CMP #229          MAY BE "VAL"
  5600        BEQ .4            ...VAL(STRING)
  5610        CMP #225          ATN?
  5620        BCC .3            ...NO, BUT IN RANGE FOR OTHERS
  5630        BNE DP.SYNERR.3   ...NOT VALID DP18 FUNCTION
  5640        JMP DP.ATN
  5650 .3     SBC #209          CARRY CLEAR SUBS 1 MORE
  5660        ASL               MULT BY 2
  5670        TAY               INDEX INTO TABLE
  5680        LDA DP.FUNC+1,Y   GET HI ADR
  5690        PHA
  5700        LDA DP.FUNC,Y     GET LO ADR
  5710        PHA
  5720        JSR AS.CHRGET
  5730        JSR AS.CHKOPN MUST HAVE (
  5740        INC RPAREN.CNT
  5750        JSR DP.EXP   EVALUATE ARG
  5760        JSR AS.CHKCLS
  5770        DEC RPAREN.CNT
  5780        RTS          EVALUATES FUNCTION
  5790 *---"VAL" FUNCTION---------------
  5800 .4     JSR AS.CHRGET
  5810        JSR AS.CHKOPN
  5820        JSR DP.VAL
  5830        JMP AS.CHKCLS
  5840 *---"NOT" ELEMENT----------------
  5850 .5     JSR DP.ELEMENT    GET ARGUMENT (RECURSIVE CALL)
  5860        LDA DAC.EXPONENT
  5870        BEQ DP.TRUE
  5880 *      FALL INTO DP.FALSE
  5890 *--------------------------------
  5900 DP.ZERO
  5910 DP.FALSE
  5920        LDA #0       FALSE, PUT 0 IN DAC
  5930        LDY #11
  5940 .1     STA DAC,Y
  5950        DEY
  5960        BPL .1
  5970        RTS
  5980 *--------------------------------
  5990 DP.TRUE
  6000        LDA #CON.ONE      TRUE, PUT 1 IN DAC
  6010        LDY /CON.ONE
  6020        JMP MOVE.YA.DAC
  6030 *--------------------------------
  6040 DP.SYNERR.3 JMP AS.SYNERR
  6050 *--------------------------------
  6060 *   VARIABLE OR NUMBER
  6070 *      VARNUM = DP18 VARIABLE
  6080 *      VARNUM = NUMBER
  6090 *      VARNUM = NEGOP NUMBER
  6100 *      VARNUM = "PI"
  6110 *--------------------------------
  6120 DP.VARNUM
  6130        LDY #0
  6140        LDA (TXTPTR),Y
  6150        CMP #'P      CHECK FOR PI
  6160        BNE .1
  6170        INY          Y=1
  6180        LDA (TXTPTR),Y
  6190        CMP #'I
  6200        BNE .1
  6210        INY          Y=2
  6220        LDA (TXTPTR),Y
  6230        CMP #'(      MUST NOT BE ARRAY
  6240        BEQ .1
  6250        JSR AS.ADDON      ADVANCE TXTPTR PAST "PI"
  6260        LDA #CON.PI
  6270        LDY /CON.PI
  6280        JMP MOVE.YA.DAC.1 GET PI INTO DAC W/GUARD DIGITS
  6290 *---CHECK FOR VARIABLE-----------
  6300 .1     JSR AS.CHRGOT
  6310        JSR AS.ISLETC
  6320        BCC .2            ...NOT LETTER, TRY NUMBER
  6330        JSR AS.PTRGET     ...LETTER, GET VARIABLE ADDR
  6340        JSR CHECK.DP.VAR  BE SURE IT IS REAL ARRAY
  6350        JMP MOVE.YA.DAC   GET VALUE INTO DAC
  6360 *---CHECK FOR NUMBER-------------
  6370 .2     CMP #'.      DECIMAL POINT?
  6380        BEQ .3       YES
  6390        CMP #TKN.PLUS PLUS?
  6400        BEQ .3       YES
  6410        CMP #TKN.MINUS MINUS?
  6420        BEQ .3       YES
  6430        CMP #'0
  6440        BCC DP.SYNERR.3       NOT A DIGIT
  6450        CMP #'9+1
  6460        BCS DP.SYNERR.3       NOT A DIGIT
  6470 .3     JMP FIN      CONVERT NUMBER
  6480 *--------------------------------
  6490 *      PUSH (DAC) ONTO EXPRESSION STACK
  6500 *--------------------------------
  6510 PUSH.DAC.STACK
  6520        LDY STACK.PNTR
  6530        CPY #STACK.SIZE-12
  6540        BCS .2       STACK ALREADY FULL
  6550        LDX #0
  6560 .1     LDA DAC,X
  6570        STA STACK,Y
  6580        INY
  6590        INX
  6600        CPX #12      STACK 12 BYTES
  6610        BCC .1
  6620        STY STACK.PNTR
  6630        RTS
  6640 .2     JMP AS.FRMCPX  FORMULA TOO COMPLEX
  6650 *--------------------------------
  6660 *      POP EXPRESSION STACK INTO ARG
  6670 *--------------------------------
  6680 POP.STACK.ARG
  6690        LDY STACK.PNTR
  6700        BEQ DP.SYNERR.3   STACK IS EMPTY
  6710        LDX #11
  6720 .1     DEY
  6730        LDA STACK,Y
  6740        STA ARG,X
  6750        DEX
  6760        BPL .1
  6770        STY STACK.PNTR
  6780        RTS
  6790 *--------------------------------
  6800 CON.ONE    .HS 4110000000000000000000
  6810 CON.PI     .HS 4131415926535897932385
  6820 *--------------------------------

Faster Amper-routine to Zero Arrays Johan Zwiekhorst
Maasmechelen, Belgium

Although I have never subscribed to Apple Assembly Line, a friend of mine (who lives in nearby Heerlen, the Netherlands) does, and I always read his copies.

A few days ago I needed a routine to clear to zero all the elements in a number of Applesoft arrays, so I started looking in my friend's collection of AAL for such a program. I found the article entitled "Save Garbage by Emptying Arrays" in the December 1982 issue, pages 22-25.

That routine, however, only cleared string arrays. Bob designed it to set all strings in an array to null strings, so that garbage collection would be faster. But I needed a fast way to clear integer and real arrays as well. Bob's routine was also limited to clearing one array per call.

My routine clears any type of arrays, and can accept a list of array names separated by commas. It uses the ampersand hook, like this:

       & CLEAR array1,array2,array3,...

You can load the routine in any available memory, anywhere you have a spare 79 bytes. The listing shows it assembled into the ever-popular $300 space, but there are no internal addresses which require it to be there. Just be sure you hook the ampersand to the program, wherever you put it. If it is at $300, hook it like this:

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

The program is very similar to Bob's 1982 version: I eliminated the check he made for string arrays, added ampersand control, and checked for a comma to allow a list of array names rather than just one.

Lines 1250-1260 check that the byte following the ampersand is the CLEAR token. If not, a SYNTAX ERROR will result. If it is CLEAR, all is well.

Lines 1280-1290 check for a comma, and are not used until we have finished clearing an array. At the end, lines 1690-1710, you find my test after clearing an array. If the next byte of program is not a colon or end of line, it will branch back to the comma-test.

The code in between zeroes all the data bytes in an array. I could have done it the same way Bob did, but I did change a few things. Compare mine with his and you will learn two ways to control a clearing loop.

How about a complete example of using &CLEAR? Lets make three arrays, with a mixture of types and dimensions. Of course, when the DIM statement works it initially zeroes the arrays, but I needed them cleared again later on.

       100 DIM A(10,20), B%(200,4,4), C%(20)
       110 PRINT CHR$(4)"BLOAD B.CLEAR ARRAYS,A$300"
       120 POKE 1013,76:POKE1014,0:POKE1015,3
       ...
       500 &CLEAR A,B%,C$
  1000 *SAVE S.CLEAR ARRAYS
  1010 *--------------------------------
  1020 *   &CLEAR <ARRAY LIST>
  1030 *      SETS ALL VALUES IN REAL ARRAYS TO 0
  1040 *                      INTEGER ARRAYS TO 0
  1050 *                       STRING ARRAYS TO ""
  1060 *--------------------------------
  1070 *   WRITTEN BY JOHAN ZWIEKHORST, BASED ON
  1080 *   "CLEAR STRING ARRAY" BY BOB SANDER-CEDERLOF
  1090 *   IN DECEMBER, 1982 APPLE ASSEMBLY LINE
  1100 *--------------------------------
  1110 T.CLEAR    .EQ $BD  "CLEAR" TOKEN
  1120 *--------------------------------
  1130 ARYPT      .EQ $94
  1140 LOWTR      .EQ $9B
  1150 ARYEND     .EQ $9D  (= FAC)
  1160 *--------------------------------
  1170 CHRGET     .EQ $B1
  1180 CHRGOT     .EQ $B7
  1190 SYNERR     .EQ $DEC9
  1200 GETARYPT   .EQ $F7D9
  1210 *--------------------------------
  1220        .OR $300     (COULD BE ANYWHERE YOU LIKE)
  1230 *--------------------------------
  1240 CLEAR.ARRAYS
  1250        CMP #T.CLEAR      &CLEAR?
  1260        BEQ .3            ...YES
  1270 .1     JMP SYNERR
  1280 .2     CMP #$2C          COMMA?
  1290        BNE .1
  1300 *---GET STARTING ADDRESS---------
  1310 .3     JSR CHRGET   GET NEXT CHAR (SHOULD BE LETTER)
  1320        JSR GETARYPT FIND NAME/ADDRESS OF ARRAY
  1330        LDY #4       COMPUTE SIZE OF PREAMBLE
  1340        LDA (LOWTR),Y     # DIMENSIONS
  1350        ASL               *2, AND CLEAR CARRY
  1360        ADC #5            +5 (2 FOR NAME)
  1370        ADC LOWTR            (2 FOR OFFSET)
  1380        PHA                  (1 FOR # DIMS)
  1390        LDA LOWTR+1
  1400        ADC #0            ADD CARRY
  1410        STA ARYPT+1
  1420 *---GET ENDING ADDRESS-----------
  1430        CLC          ADD OFFSET TO GET ADDRESS OF END
  1440        LDY #2
  1450        LDA (LOWTR),Y
  1460        ADC LOWTR
  1470        STA ARYEND
  1480        INY
  1490        LDA (LOWTR),Y
  1500        ADC LOWTR+1
  1510        STA ARYEND+1
  1520 *---SET UP POINTER TO START------
  1530        PLA
  1540        TAY
  1550        LDA #0
  1560        STA ARYPT
  1570        LDX ARYPT+1
  1580 *---LOOP TO SET ELEMENTS ZERO----
  1590 .4     STA (ARYPT),Y
  1600        INY
  1610        BNE .5       ...USUALLY
  1620        INX          ...NEXT PAGE
  1630        STX ARYPT+1
  1640 .5     CPY ARYEND   AT END YET?
  1650        BNE .4       ...NO
  1660        CPX ARYEND+1
  1670        BNE .4       ...NO
  1680 *---CHECK IF ANOTHER ARRAY-------
  1690        JSR CHRGOT
  1700        BNE .2       ...YES, UNLESS SYNTAX ERROR
  1710        RTS

Turn an Index into a Mask Bob Sander-Cederlof

How do you write a program that will turn a number from 0 to 7 into a bit mask $01, $02, ...$40, $80? I want an index of 0 to return $01, 1 to return $02, 2 to return $04, and so on up to 7 returning $80.

The simplest, shortest, and speediest is to use a direct table look-up. Assuming the byte with the index value is in the A-register, the code would look like this:

       AND #7          isolate index bits
       TAX             index to X-register
       LDA TABLE,X     get mask from table

and the table would look like this:

       TABLE .HS 01020408
             .HS 10204080

This technique has the wonderful advantage that if you need a different translation, you can simply use a different table. For example, if you want the reverse pattern, with 0 returning $80 and 7 returning $01, simply change the table to:

      TABLE .HS 80402010
             .HS 08040201

The table lookup method has the shortest code, but counting the table does take 14 bytes. If you don't worry so much about speed and flexibility, you can write a little loop that will create the mask value like this:

       MAKE.MASK.2
               AND #7     isolate index bits
               TAX        index into X-register
               LDA #$01   initial mask value
       .1      ASL        shift loop to position
               DEX        to Xth bit
               BPL .1     shifts once to many
               ROR        restore after extra shift
               RTS

I put an RTS at the end because this piece of code makes a nice size subroutine. Nevertheless, for comparison to the table lookup code above, let's count neither the JSR to call it nor the RTS at the end. The shift-loop method takes only 10 bytes, four less than the table lookup. But it is slower, taking 14 cycles if the index is 0, 21 if 1, up to 63 for an index of 7. Sometimes saving four bytes is more important that speed, and sometimes speed is more important.

To generate the reverse sequence with the shift loop method, make three simple changes to MAKE.MASK.2: the initial mask value from $01 to $80; the ASL to LSR; and the ROR to ROL.

Note that both techniques shown above use the X-register. If the X-register is busy, you could use the Y-register instead. Just for the challenge, I wanted to see if I could write a reasonably efficient index-to-mask routine that did not use the X- or Y- registers at all.

The first method that came to mind was fast enough, but took too much space and did not seem creative. It involved a series of CMP and BEQ instructions to branch to 8 different LDA's:

       SILLY.WAY
               AND #7  isolate index
               BEQ .0  index=0
               CMP #1
               BEQ .1  index=1
               ...
               CMP #6
               BEQ .6    index=6
               LDA #$80  index=7
               RTS
       .0      LDA #$01
               RTS
       .1      LDA #$02
               RTS
               ...
       .6      LDA #$40  index=6
               RTS

If I had written every line above, you would see that it takes 52 bytes.

Next I though of a more efficient way to do the CMP's so that not so many were needed.

       NOT.SO.SILLY.WAY
               AND #7  isolate mask
               BEQ .0  index=0
               CMP #4
               BEQ .4  index=4
               BCS .60 index=5, 6, or 7
               CMP #2  index=1, 2, or 3
               BEQ .2  index=2
               BCS .3  index=3
               LDA #$02  index=1
               RTS
       .60     CMP #6  index=5, 6, 0r 7
               BEQ .6  index=6
               BCS .7  index=7
               LDA #$20  index=5
               RTS
       .0      LDA #$01  index=0
               RTS
       .2      LDA #$04  index=2
               RTS
           and so on.

This method takes a total of 46 bytes.

Here is one which is even shorter, which uses "tricky" arithmetic.

       TRICKY.WAY
               AND #7
               CMP #2
               BCC .5  (0 or 1) plus 1
               BEQ .5  (2) plus CARRY plus 1 --> 4
               CMP #4
               BCC .4  (3) plus 4+1 --> 8
               BEQ .3  (4) plus 6+4+1+C --> $10
               CMP #6
               BCC .2  (5) plus $10+6+4+1
               BEQ .1  (6) plus $1E+$10+6+4+1+C
               ADC #$3F  (7) plus $3F+$1E+$10+6+4+1+C
       .1      ADC #$1E
       .2      ADC #$10
       .3      ADC #6
       .4      ADC #4
       .5      ADC #1
               RTS

Not counting the RTS, that is 31 bytes. Cases 0 and 1 take only 9 cycles. The longest one, when the index is 7, takes 32 cycles.

All of these longer methods can be made to generate the reverse sequence by simply inverting the index before beginning the tests. Use "EOR #7" before the "AND #7".

I came up with an even trickier version, which shaved another byte or two off TRICKY.WAY. Believe it or not, it really works:

       TRICKIER.WAY.REVERSE
               EOR #7
       TRICKIER.WAY
               AND #7     isolate index
               SEC        00-01-02-03-04-05-06-07
               ROL        01-03-05-07-09-0B-0D-0F
               CMP #3
               BCC .0     turn 0 into $01
               CMP #7
               BCC .12    03-->02, 05-->04
               ADC #6     ..-..-..-0E-10-12-14-16
               CMP #$12
               BCC .34    0E-->08, 10-->10
               ADC #$2B   ..-..-..-..-..-3E-40-42
               CMP #$42
               BCC .56    3E-->20, 40-->40
               ASL        42-->84-->80
       .56     AND #$E0
       .34     AND #$F8
       .12     AND #$FE
       .0      RTS

If the index is 0, this one takes 11 cycles. Worst case is for index 7, at 34 cycles.

A source file on the quarterly disk will include all of the above examples, plus a driving program that runs through all 8 cases and displays the results for each and every method.

In real life, I would probably use the shift-loop or the table look up. Most likely the table lookup, because it is the easiest to understand and modify, and by far the shortest in time. Nevertheless, it is very useful to experiment with other techniques. You learn a lot from the experience, and it is fun!


Put Your Messages on the Screen William M. Reed

COUT is slow. COUT with DOS looking on is even slower. And I suppose with ProDOS, more so. If you want to get a short message on the screen in a hurry, you can bypass COUT and put it there directly.

In all of the following examples I am going to assume that the message is stored in RAM exactly as it should be on screen, and that after the last character is a byte with $00 in it. I also assume that you are only writing one line, so that the message will not spill over to another line.

Here is a loop that writes a message on the bottom line of the screen:

     MESSAGE
            LDY #0
     .1     LDA MESSAGE,Y
            BEQ .2          ...END OF MESSAGE
            STA $7D0,Y
            INY
            BNE .1          ...ALWAYS
     .2     RTS

If you want to write on the current line, whose base address is kept by the monitor in BASL and BASH ($28 and $29), just change the STA $7D0,Y line to STA (BASL),Y.

All well and good for 40 columns, but what about the 80-column //e and //c screens? Well, you can still do it, like this:

     MESSAGE.80
            LDX #0       MESSAGE INDEX
     .1     TXA
            LSR          COLUMN/2, ODD/EVEN TO CARRY
            TAY          INDEX INTO SCREEN MEMORY
            LDA MESSAGE,X
            BEQ .3       ...END OF MESSAGE
            STA PAGE1
            BCS .2       ...ODD, PAGE 1
            STA PAGE2    ...EVEN, PAGE 2
     .2     STA (BASL),Y
            INX
            BNE .1       ...ALWAYS
     .3     RTS
     PAGE1  .EQ $C054
     PAGE2  .EQ $C055

Of course, these routines put the messages on the screen only. But that may be just what you want, to put messages on the screen without affecting the report going out to file or printer. Also, these routines do not handle CR, end of line, scroll, etc; but they sure get to the screen in a hurry!


Bibliography on Apple Hi-Res Graphics Bob Sander-Cederlof

There has been a lot of material published in the last seven years about Apple's hi-res graphics. The problem is finding it! Most of the neat programs and explanations have not yet made it from the pages of various magazines into full size books. I recently decided to make a list, so that I don't have to keep leafing through mile-high stacks of magazines. Since I have never been a devotee of Pascal, I purposely omitted most articles relating to graphics in that language. I also omitted reviews and announcements of commercial hi-res products.

I looked through my book shelves and noted all books I could find there. I also went through all my back issues of Byte, Micro, Call APPLE, and Apple Orchard. Still to go are Nibble, Kilobaud, Softalk, and Creative Computing.

Books

Apple Graphics & Arcade Game Design, Jeffrey Stanton. The Book Co., 1982, 288 pages, $19.95. By the time you work through this one, you have a functioning hi-res arcade game!

Apple II Graphics, Ken Williams. Prentice Hall, 1983, 150 pages, $19.95. (Originally a series of articles in Softline Magazine, Sep 81 through Jan 83.)

Applied Apple Graphics, Pip Forer. Prentice-Hall, 1984, about 400 pages plus diskette, price unknown. Lo-res, Hi-res, 3-D, etc., with over 50 program in BASIC on disk.

Graphically Speaking, Mark Pelczarski. Softalk Books, 1984, 170 pages, $19.95. Originally a series of articles which ran from May 1982 through September 1983 in Softalk Magazine. Includes many programs in Applesoft and assembly language. Covers drawing, animation, filling, packing/unpacking, and 3-D. Disk available.

Microcomputer Graphics, Roy E. Myers. Addison-Wesley, 1982, 282 pages, $12.95. More than 80 Applesoft programs. 2-D and 3-D graphics, windowing, transformations, hidden lines, and much more. Disk available.

Books with some material on Apple Graphics

Animation, Games, and Sound for the Apple II/IIe, Tony Fabbri. Prentice-Hall, 144 pages.

Enhancing Your Apple II, Volume I, Don Lancaster. Howard Sams & Co., 1984, 268 pages, $15.95. Hardware and software tricks for switching between modes and screens dynamically, programs for hundreds of hi-res colors and patterns, fast screen fill. Good explanations of the way things work.

What's Where in the Apple, William F. Luebbert. Micro Ink, 1982, about 300 pages. First half of book is text describing Apple; chapter 14 covers lo-res graphics, and chapter 16 covers hi-res graphics. Includes details about hardware switches, memory mapping, and firmware.

Magazine columns

Assembly Lines, Roger Wagner, Softalk Magazine. From March 82 to June 83 this column covered various topics in Apple hi-res graphics. It should be made into a book, but has not yet been.

The Graphics Page, Bill Budge, Softalk Magazine. Oct 83 through Jun 84. Deep material, by the author of Pinball Construction Set. Further installments were promised, but not yet seen.

Apple II Graphics, Ken Williams, Softline Magazine. Sep 81 through Jan 83. Now available in book form (see above).

Graphically Speaking, Mark Pelczarski, Softalk Magazine. May 82 through Sep 83. Now available in book form.

Magazine Articles: Byte

Apple FAX: Weather Maps on a Video Screen, Keith H. Sueker. Jun 84, 146-151.

CHEDIT: a Graphics-Character Editor, Jerry Sweet. May 82, p426-444.

Double the Apple II's Color Choices, Robert H. Sturges. Nov 83, p449-463.

Double-Width Silentype Graphics for Apple, Charles Putney. Feb 82, p413-423.

GRPRINT: an Apple Utility Program, Douglas Arnott. Dec 82, 398-403.

Interactive 3-D Graphics for Apple II, Andrew Pickholtz. Nov 82, 474-505.

Kinetic String Art for the Apple, Louis Cesa. Nov 80, p62-63.

More Colors for your Apple, Allen Watson, Steve Wozniak. Jun 79, p60-68.

New Shape Subroutine for the Apple, Richard T. Simoni. Aug 83, p292-309.

Picture Perfect Apple, Phil Roybal. Jan 81, p226-235.

Shape Table Conversion for the Apple II, Dave Partyka. Nov 79, p63.

Simplified Theory of Video Graphics, Allen Watson. Part 1, Nov 80, p180-189. Part 2, Dec 80, p142-156.

Three-dimensional Graphics for the Apple II, Dan Sokol, Nov 80, p148-154.

Magazine Articles: Micro

Apple Bits, Richard C. Vile Jr. Part I, Sep 81, p75-77. Part 2, Oct 81, p94-96. Part 3, Nov 81, p105-108. (Lo-Res)

Apple Color Filter, Stephen R. Berggren. Jun 81, p53-54.

A Hi-Res Graph Plotting Subroutine in Integer BASIC for the Apple II, Richard Fam. Feb 80, p9-10.

Apple Graphics, staff. Sep 81, p49. Intro to several other articles.

Apple Graphics for Okidata Microline 80, Gary Little. May 83, p80-86.

Apple Hi-Res Graphics and Memory Use, Dan Weston. Nov 82, p79-81.

Apple II High Resolution Graphics Memory Organization, Andrew H. Eliason. Oct-Nov 1978, p43-44.

Apple II Hi-Res Picture Compression, Bob Bishop. Nov 79, p17-24.

Apple Pascal Hi-Res Screen Dump, Robert D. Walker. Feb 83, p54-55.

Apple Shootdown, a lo-res graphics game, Eric Grammer. Nov 82, p72-73.

A Versatile Hi-Res Graphics Routine for the Apple, Adam P. King. Mar 83, p77-81.

Constructing Truly 3-D Mazes, Dr. Alan Stankiewicz. Aug 84, p19-21.

Creating Shape Tables, Improved!, Peter A. Cook. Sep 80, p7-12.

Define Hi-Res Characters for the Apple II, Robert F. Zant. Aug 79, p44-45.

Getting Around the Apple Hi-Res Graphics Page, Eagle Berns. Nov 82, p93-95.

Graphing Rational Functions, Ron Carlson. Dec 80, 7-9.

Hi-Res Characters for Logo, Dan Weston. Sep 83, p50-53.

Hi-Res Screen Dump for Epson MX-80, Robert D. Walker. Apr/May 84, p55-61.

How to Do a Shape Table Easily and Correctly, John Figueras. Dec 79, p11-22.

Introduction to 3-D Rotation on the Apple, Chris Williams. Nov 82, p99-101.

Paddle Hi-Res Graphics, Kim G. Woodward. Sep 81, p68-69.

Random Number Generator in Machine Language for the Apple, Arthur Matheny. Includes a graphics simulation of a globular cluster. Aug 82, p57-60.

SHAPER: A Utility Program for Managing Shape Tables, Clement D. Osborne. Sep 81, p50-56.

Sun and Moon on the Apple, Svend Ostrup. Jan 83, p35-37. Hi-res simulation of orbits and phases.

True 3-D Images on Apple II, Art Radcliffe. Sep 81, p71-73.

Magazine Articles: Call-A.P.P.L.E.

80-column //e Lo-Res Graphics, Rob Moore. Jul 83, p9-13.

Adding XPLOT to Applesoft, Mark Harris. Apr 84, p17-18,24.

A Higher Text Apple-cation, Donal Buchanan. Nov 82, p47-50. Using Higher Text for ancient alphabets.

Animation with Data Arrays, Pat Connelly. Nov-Dec 80, p11-17.

Apple Gaming: Playing Card Generation, Jim Hilger. Nov-Dec 79, p39-45; Jan 80, p39. Hi-res playing card pictures from Integer BASIC.

Applesoft Firmware Card Hi-Res Routines, Steve Alex. Oct 79, p33.

Applesoft Graphics Mover, Homer O. Porter. Sep 83, p29-31.

Arcade Graphics Techniques, Chris Jochumson. Apr 83, p9-14.

Character Generator ROM, Ian M. Jackson. Nov 82, p21-29. Programs for moving a Higher Text font into ROM.

Color 21, Darrell Aldrich. Jul-Aug 79, p21.

Color Me Apple, M. A. Iannce. Nov 82, p9-18. In-depth explanation of hi-res color with demo program.

Doing the Splits, Roy Myers. Aug 82, p61-65. Making room for hi-res pictures by moving your program.

Graphic Garbage Collection, Richard Cornelius & Melvin Zandler. Nov 82, p53-55. Lets you watch garbage collection activity on the hi-res screen.

Higher Text in Action, Steve Brugger. Jan 84, p30-31.

Higher Text on the Loose, Val J. Golding. Jun 81, p47-49. Explanation of the background functions of Higher Text.

Hi-Res Dump program modification, Tom Lewellen. Jul-Aug 79, p36.

Hi-Res Full Scroll, Edward C. So. Feb 82, p23-34. Scroll up, down, left, or right by one pixel position at a time.

Hi-Res Hi-Jinx, Edward C. So. Apr 82, p59. POKEing and PEEKing dots.

Hi-Res Screen Switch (program), Wes Huntress. Jul-Aug 80, p48-49.

Hi-Res Slide Show, Stowe Keller. Dec 83, p49-54.

Magic Square Dance, J. Taylor. Sep 83, p51-52.

MX-100 Hi-Res Dump, Bruce C. Detterich. Mar 82, p41-49.

Painting the RAMcard, Donald W. Miller Jr. Apr 83, p51-54.

Picture Compression, Edward C. So. May 82, p21-35. Very complete, builds on Bob Bishop's attempts.

Playing Card Generator, Vincent Aderente. Nov 82, p31-35. Applesoft versions of Jim Hilger's stuff.

Scrunch, Darrell Aldrich. Jun 79, p21-23. Squeeze four pictures into one screen.

Shape Display Utility, Major Peter M. Beck. Mar-Apr 80, p39.

Shape Table Splicer, Cyrus W. Roton. Sep 83, p33-35.

Slow Plot, Jim Morriset. Nov 82, p63-64. Speed control for hi-res drawing.

Smooth Animation, Jonathan Kandell. Feb 83, p61-62.

The Graphics Toolkit, Randi J. Rost. Part 1: Apr 84, p10-15 (screen mapping). Part 2: May 84, p23-26. Part 3: Aug 84, p43-48. Line drawing algorithms, disassembly of Applesoft HPLOT.

Three Dee Demos, David Sun. Jan 83, p49-51.

Understanding Hi-Res Graphics, Loy Spurlock. Jan 80, p6-15.

Using the Splitter, Norman L. Kushnick. Jan 83, p53-55. More help in making room for pictures.

Why Don't You Watch Where You're Going?, Kenneth Manly. Oct 80, p25-28. A hi-res SCRN function.

Zoom, Neil Konzen. Jan 80, p28-32. Expand a 1/9th screen to full size.

Magazine Articles: Apple Orchard

Double-Size Graphics for the Silentype, Bruce F. Field. Spring 81, p30-34.

Hi-Res Dump Routine for Integral Data Printer (IDS-225), Darrell & Ron Aldrich. Mar-Apr 80, p54-55. (Originally published in Call APPLE).

Hi-Res Graphics: Resolving the Resolution Myth, Bob Bishop. Fall 80, p7-10.

How the Dot Patterns Produce Colors, Allen Watson III. Jan 84, p44-46.

How the Double Hi-Res Hardware Came to Be, Allen Watson III. Jan 84, p42,43.

Notes on Hi-Res Graphics Routines in Applesoft, C. K. Mesztenyi. Spring 81, p17-19.

Practical Super Hi-Res Graphics, R. H. Good. Spring 81, p20.

Secrets of Professional Graphics, William Harvey. Part I, Behind the Scenes, Sep-Oct 82, p64-72. Part II, The Real Challenge: Putting It All Together, Nov-Dec 82, p36-53. Part III, Techniques of Animation, Mar 83, p62-69.

Shape Definition Conversion Table, David G. Huffman. Fall 81, p78-79.

Shaping Up with the Apple II, Mark L. Crosby. Mar-Apr 80, p37-45.

The Mysterious Orange Vertical Line, Pete Rowe. Fall 80, p11.

True 16-color Hi-Res, Allen Watson III. Jan 84, p26-41.

Understanding Hi-Res Graphics, and how to include text in your Hi-Res Graphics Programs, Loy Spurlock. Fall 80, p12-21.


Some Great New Books Bob Sander-Cederlof

"Beneath Apple ProDOS", by Don Worth and Pieter Lechner. Quality Software, 1984, 276 pages plus 10 page reference card, $19.95. (By it from us for $18 plus shipping.)

We have been waiting a long time for this one, by the authors of "Beneath Apple DOS". If you read that one, you'll want this one too. And if you use or plan to use ProDOS, you will almost REQUIRE this new book.

Apple has documented ProDOS pretty thoroughly, but just TRY to get a copy of their books. Hardly any Apple dealers stock the reference manuals now. Apple requires a minimum order to buy the manuals, and they are a relatively slow moving item. Hence, dealers don't order them. Some we have talked with lately refused to admit they knew of the existence of even the Apple //e Reference Manual (over 18 months old now)! And Apple so far will not sell the books to anyone who is not an authorized Apple dealer. Catch-22, right?

But even if you have Apple's ProDOS reference manuals, as I do, you still need "Beneath Apple ProDOS". Look at the table of contents, and see if you can resist.

The most heavily thumbed pages in my copy of "Beneath Apple DOS" are the ones which give detailed comments on the entire DOS assembly language image. Unfortunately, the equivalent section does not come bound in to "Beneath Apple ProDOS". Since Apple has decided to freeze DOS, a published commentary is possible. But ProDOS is deliberately kept warm and fluid. So far there are at least four versions around; all have the same characteristics and machine language interface, but subroutines have been shuffled and rewritten. A line-by-line commentary could become obsolete every six months.

A special coupon is bound into the book at the place where you would expect the commentary. If you want the commentary, you remove the coupon page, fill in your name, address, and ProDOS version number, and send it with $12.50 to Quality Software. With the commentary you will receive a new coupon so your can order a subsequent supplement when ProDOS changes versions.

"Assembly Cookbook for the Apple II/IIe", Don Lancaster. Howard Sams & Co., 1984, 408 pages, $21.95. (Buy it from us for $20 plus shipping.)

Don is sold on the synergistic combination of a full-screen 80-column word processor for handling source code with an assembler. His favorite pairing is Applewriter //e with EDASM (from DOS ToolKit). Consequently a large section of the book is devoted to how the marriage is performed, what the advan- tages are, and how to work around or ignore the disadvantages. Don knows Applewriter inside out, and uses it for all his word processing as well as for programming. There are some distinct advantages to using the same editor for both: writing books about assembly language programming is easier; only one set of commands, tricks, and quirks need be learned. Applewriter //e's WPL language helps overcome the disadvantages of using a screen-oriented processor on line-oriented information.

The second half of the book contains sample assembly language programs, explained in detail. These are not your run-of-the- mill examples, but great subroutines and programs you can actually use, as well as learn from.

"Microcomputer Design and Troubleshooting", by Eugene M. Zumchak. Howard Sams & Co., 1982, 350 pages, $17.95. (Buy it from us for $17 plus shipping.)

From time to time I am called upon to understand and work with electronics. My degree is in Electronic Engineering, but I got it in the vacuum tube era (over 20 years ago). What now fits on one chip used to fill a whole ship.... Anyway, I struggle through. But I have found a book recently that has really helped: it is not really a new book, but is new to me.

Gene Zumchak has a unique approach, which is PRACTICAL. He believes in designs which are easy to troubleshoot. He tells how adding a few low cost components here and there will avoid the expense of a logic analyzer and three weeks of debugging time. For example, using an EPROM emulator and a few LED's in critical places in a microprocessor design could save endless hours of burning and erasing EPROMs, attaching logic analyzer leads and watching oscilloscope traces, and pulling all your hair out. Although every chapter has helpful ideas in the areas of trouble prevention and diagnosis, chapter 6 is devoted entirely to the subject. Another feature Gene promotes is low power consumption.

Jack Lewis is president of Micromation, a company which makes hardware for use with the Hero-1 Robot. They have designed interfaces between Apple and Hero, speech input processors, and much more. When Jack began, he contracted with Gene Zumchak to teach his people the techniques which are now in this book. Jack is the one who recommended the book to me.

And now I recommend the book to you, if you like to dabble in hardware design. Even practicing designers will find the ideas well worth the price of reading the book.

I also recommend "The Computer Journal", a monthly newsletter/ magazine published by Art Carlson. $24/year (U.S.) gets you regular articles such as "Build a 68008 CPU Board for the S-100 Bus", "Electronic Dial Indicator", "Writing Your Own Threaded Language", and "Interfacing Tips and Troubles". Write to Art at P. O. Box 1697, Kalispell, MT 59903.


Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $12 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE, all rights reserved. Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof. (Apple is a registered trademark of Apple Computer, Inc.)