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....
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 *-------------------------------- |
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 |
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!
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!
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.
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.
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.
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.
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.
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.
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.
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.
"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.)