Apple Assembly Line
Volume 4 -- Issue 6 March 1984

In This Issue...

For some time now we have been selling our S-C Word Processor, complete with all source code on disk. We hoped that some users would send us their improvements, and sure enough they have. Bob Gardner recently sent us a bunch, and that motivated me to go back over the package.

The disk now includes both II/IIPlus and //e versions. The //e version allows an 80-column preview (still only 40-column during edit mode). I added titles and page numbers, a single sheet mode, and more. Even with all the new features, the new object code is a little shorter than the old, leaving even more room for your own modifications and enhancements. I improved the internal documentation. The "manual-ette" is now 10 pages rather than 6. A small tutorial file helps you get started.

The price is still only $50. Owners of the old version can get a new copy for only $5.


Will Rockwell 65C02's work in an old Apple? Bob Stout

Not unless you have the 2 MHz part. For some reason the timing is too tight and slightly different to use a 1MHz 65C02, unless you have an Apple //e. The 2 MHz chips WILL work in Apple II and II Plus.


Fast Garbage Collection Col. Paul Shetler, MD
Honolulu, Hawaii

When Applesoft programs manipulate strings, memory gradually fills up with little bits and pieces of old strings. Eventually this space needs to be recovered so the program can continue. The process of searching through all the still active strings, moving them back to the top of free memory, and making the remaining space available again is called "garbage collection".

Applesoft will automatically collect garbage when memory fills up. However, the garbage collector in the Applesoft ROMs is pitifully slow. Worse yet, the time to collect is proportional to the square of the number of strings in use. That is, if you have 100 active strings it will take four times as long to collect garbage as if you had only 50 active strings.

Cornelis Bongers, of Erasmus University in Rotterdam, Netherlands, published a brilliant Garbage Collector for Applesoft strings in Micro, August 1982. The speed of his program, when compared to the one residing in ROM, is incredible. And the time is directly proportional to the number of strings, rather than the square of the number of strings. The only problem with his program is that it belongs to the magazine that published it. Or worse yet, it is tied to a program called Ampersoft, marketed by Microsparc (publishers of Nibble magazine) for $50. When I asked them about a license, they wanted big bucks.

So, I decided to write my own garbage collector, based on the ideas behind Cornelis Bongers' program. And then I further decided to make it available to all readers of Apple Assembly Line, where I myself have received so much help.

There are several catches. Normal Applesoft programs save all string data with the high-order bit of each byte zero (positive ASCII). Further, normal Applesoft programs never allow more than one string variable to point to the same exact memory copy of the string. The method of garbage collection my program uses (Bongers' method) DEPENDS on these constraints. If either is not true, LOOK OUT! Of course, if your Applesoft programs are normal, you need have no fear. Only if you are doing exotic things with your own machine language appendages to Applesoft might these constraints be violated.

The basic concept is fairly simple. Applesoft uses descriptors to point to the string in the string pool. The descriptor consists of three bytes -- the length, and the address of the characters in the string pool.

Strings build down from the top of memory (HIMEM) and the descriptors build up from the end of the program in the variable space. Since a new value assigned to a string is added to the bottom of the string pool, the pool is soon full of "garbage".

Applesoft frees the garbage one string at a time. This n-square method takes forever, when there are large string arrays. Bongers introduced the idea of marking active strings in the pool by setting the third byte in the string to a negative ASCII value, then storing the location of the descriptor in the first two bytes. The first two bytes of the string are saved safely in the address of field of the descriptor. The address previously in the address field will be changed anyway after all the strings are moved up in memory.

Another pass through the string pool moves all active strings as high in memory as they can go, retrieves the first two characters from storage in the descriptor, and points it to the new string location.

Since three bytes are used in the active strings, one and two character strings require different treatment. On the first pass through the variable space, the characters pointed to by the 'short' descriptors are stored in the length and, if len=2, the low address byte of the descriptor. The short descriptor is flagged with one or more "FF"'s, since no string can have an address greater than $FF00.

If short strings are found on the first pass, a third pass returns them to the string pool and points the descriptors to their new location.

Short strings do slow collection a little, however, the number of passes is proportional to the number of strings, and not the number squared.

Bongers' program was driven by calls via the &-statement. Mine differs in that it invoked with the USR function. Although it is easily converted to an ampersand routine, I wrote it using the USR function to provide fast garbage collection with Hayden's compiler (which also uses string descriptors and a string pool). The compiler allows USR functions, but makes & difficult. Another reason is to investigate some uses for USR.

USR(#) converts '#' to a floating point value in the FAC (floating point accumulator) and then jumps via $0A to the address pointed to in $0B, $0C. The results of the machine language subroutine can be returned in the FAC. The USR function, floating point calls, and addresses are described in Apple's BASIC REFERENCE MANUAL FOR APPLESOFT (Product #A2L0006).

The USR argument for my garbage collector requires a number in the range of +32767 to -32767. If the number is negative, the string pool is checked for negative ASCII. If any such characters are found, USR(-1) will return a value of 0, and no garbage collection will be attempted. If no negative ASCII characters are found, garbage collection will proceed. In this case USR(-1) returns the number of bytes of free space after collection.

If the USR argument is zero, for example K = USR(0), then collection is forced and USR will return the amount of free space. This is slightly faster than calling with USR(-1), because the preliminary scan for negative ASCII bytes is skipped. But USR(-1) is safer, if you are not sure.

If you use a positive argument N in the USR function, then no garbage collection will be performed unless there is less than 256*N bytes of free space left. Whether or not collection is performed, USR will tell you how much free space is left.

Only the lower five bits of the USR argument are tested. This means that USR(32) is the same as USR(0), USR(33) is the same as USR(1), and so on.

I have shown the program as residing at $9400, but of course you may re-assemble it for any favorite place.

The following Applesoft program makes a lot of garbage, and sees to the collection of it using my garbage collector. If the call to the USR function in line 245 left out, the program dies for 47 seconds while Applesoft does its own garbage collection. With the USR call as shown, the delay is less than one second.

     100  HIMEM: 37888: REM  $9400
     110  PRINT  CHR$ (4)"BLOAD B.FAST GARBAGE COLLECTOR"
     120  POKE 10,76: POKE 11,0: POKE 12,148
     200 N = 25: DIM A$(N,N),B$(N,N)
     210  FOR I = 1 TO N: FOR J = 1 TO N
     220 A$(I,J) = "X":B$(I,J) = "Y": NEXT : NEXT 
     230  FOR I = 1 TO N
     240 F = ( PEEK (112) * 256 +  PEEK (111)) - ( PEEK (110) * 256 + 
           PEEK (109)): PRINT F" ";
     245 L =  USR (2)
     250  FOR J = 1 TO N
     260  FOR K = 1 TO 10:A$(I,J) = A$(I,J) + "*": NEXT 
     270  PRINT "*";: NEXT 
     280  PRINT : NEXT 
  1000 *SAVE S.FAST GARBAGE COLLECTOR
  1010 *--------------------------------
  1020 *   FAST GARBAGE COLLECTOR
  1030 *--------------------------------
  1040 *   BY COL. PAUL SHETLER, MD
  1050 *   INSPIRED BY CORNELIS BONGERS
  1060 *--------------------------------
  1070 *
  1080 *   CALL FROM APPLESOFT WITH K=USR(N)
  1090 *
  1100 *   IF N=0, THEN COLLECTION FORCED
  1110 *
  1120 *   IF N<0, THEN POOL CHECKED FOR NEG ASCII.
  1130 *           IF NO NEG ASCII, THEN GC FORCED
  1140 *           IF NEG ASCII FOUND, THEN
  1150 *                SET USER(#)=0 AND QUIT.
  1160 *
  1170 *   IF N>0, THEN COLLECTION PERFORMED ONLY IF
  1180 *          LESS THAN N*256 BYTES OF FREE
  1190 *          SPACE LEFT.
  1200 *--------------------------------
  1210 *      THE APPLESOFT PROGRAM MUST INLCUDE
  1220 *      THE FOLLOWING STATEMENTS TO SET UP
  1230 *      THIS GARBAGE COLLECTOR:
  1240 *
  1250 *      100 HIMEM:37888:REM$9400
  1260 *      110 PRINT CHR$(4)"BLOAD B.FAST GARBAGE COLL
  1270 *          ECTOR"
  1280 *      120 POKE 10,76 : POKE 11,0 : POKE 12,148
  1290 *--------------------------------
  1300 * EQUATES FOR GARBAGE COLLECTION
  1310 *--------------------------------
  1320 SHORT.FLAG          .EQ $06
  1330 STRING.LENGTH       .EQ $07
  1340 INDEX               .EQ $19
  1350 OFFSET              .EQ $1B
  1360 ARRAY.END           .EQ $1D
  1370 *--------------------------------
  1380 *      USER(#)  EQUATES
  1390 *--------------------------------
  1400 FACMO               .EQ $A0
  1410 FACLO               .EQ $A1
  1420 AYINT               .EQ $E10C
  1430 GIVAYF              .EQ $E2F2
  1440 *--------------------------------
  1450 *      STANDARD EQUATES
  1460 *--------------------------------
  1470 LOWTR               .EQ $9B
  1480 FORPNT              .EQ $08
  1490 STREND              .EQ $6D
  1500 VARTAB              .EQ $69
  1510 FRESPC              .EQ $71
  1520 FRETOP              .EQ $6F
  1530 MEMSIZE             .EQ $73
  1540 ARYTAB              .EQ $6B
  1550 *--------------------------------
  1560        .OR $9400
  1570        .TF B.FAST GARBAGE COLLECTOR
  1580 *--------------------------------
  1590 USR.GARBAGE.COLLECTOR
  1600        JSR AYINT    CONVERT USR ARGUMENT TO INTEGER
  1610 *                   WITH HIBYTE IN FACMO, LO IN FACLO
  1620        LDA FACMO    IS # MINUS?
  1630        BMI .3       ...NEED TO CHECK FOR NEG ASCII
  1640        LDA FACLO
  1650        AND #$1F     8136 BYTES
  1660        BEQ .4       ...IF =0 THEN FORCED COLLECTION
  1670        CLC
  1680        ADC STREND+1
  1690        CMP FRETOP+1
  1700        BCS .4       ...NEED TO COLLECT NOW
  1710 *---CALC FREE SPACE--------------
  1720 .1     SEC
  1730        LDA FRETOP
  1740        SBC STREND
  1750        TAY          LO BYTE
  1760        LDA FRETOP+1
  1770        SBC STREND+1
  1780 *---FLOAT (AY) FOR USR RESULT----
  1790 .2     JMP GIVAYF   FLOAT (AY) AND RETURN
  1800 *---CHECK POOL FOR NEG ASCII-----
  1810 .3     JSR SET.STRING.POOL.STROLL
  1820        JSR FIND.NEXT.NEG.BYTE.IN.POOL
  1830        LDA #0       PREPARE ZERO IN CASE NEG ASCII
  1840        TAY
  1850        BCS .2       ...FOUND SOME NEG ASCII
  1860 *---COLLECT GARBAGE NOW----------
  1870 .4     JSR MARK.ALL.ACTIVE.STRINGS
  1880        JSR RAISE.ALL.ACTIVE.STRINGS
  1890 *---FINAL CLEAN UP---------------
  1900        LDA LOWTR    STORE NEW BOTTOM OF STRING POOL
  1910        STA FRESPC
  1920        LDA LOWTR+1
  1930        STA FRESPC+1
  1940        LDA SHORT.FLAG    NEED TO FIX SHORT STRINGS?
  1950        BEQ .5            ...NO, NOT ANY SHORT ONES
  1960        JSR FIX.SHORT.STRINGS
  1970 .5     LDA FRESPC   SET FRETOP TO TOP OF FREE SPACE
  1980        STA FRETOP
  1990        LDA FRESPC+1
  2000        STA FRETOP+1
  2010        JMP .1
  2020 *--------------------------------
  2030 *   MARK ACTIVE STRINGS WITH NEG BYTE
  2040 *--------------------------------
  2050 MARK.ALL.ACTIVE.STRINGS
  2060        LDA #0       FLAG->NONE
  2070        STA SHORT.FLAG
  2080        JSR INITIATE.SEARCH
  2090 .1     JSR FIND.NEXT.STRING.VARIABLE
  2100        BCS .5       ...NO MORE VARIABLES
  2110        LDY #0       POINT AT LENGTH BYTE OF DESC.
  2120        LDA (LOWTR),Y
  2130        BEQ .1       STRING LEN =0
  2140 *---CHECK LOCATION OF STRING-----
  2150        TAX          SAVE STRLEN IN X-REG
  2160        INY          IF STRING DATA INSIDE PROGRAM,
  2170        LDA (LOWTR),Y     THEN NO NEED TO FIDDLE
  2180        STA FORPNT        WITH IT FURTHER.
  2190        CMP VARTAB
  2200        INY
  2210        LDA (LOWTR),Y
  2220        STA FORPNT+1
  2230        SBC VARTAB+1      IN PROGRAM?
  2240        BCC .1            ...YES, SO PASS
  2250 *---CHECK FOR SHORT STRING-------
  2260        CPX #3       IF 1 OR 2, SPECIAL TREATMENT
  2270        BCS .3       ...LONG STRING
  2280 *---SHORT STRING HANDLER---------
  2290        STX SHORT.FLAG    NON-ZERO TO FLAG
  2300        LDA #$FF
  2310        STA (LOWTR),Y     MARKER IN 3RD DESC. BYTE
  2320        DEY               POINT AT 2ND DESC. BYTE
  2330        DEX               CHECK LENGTH
  2340        BEQ .2            LEN=1, PUT $FF IN 2ND BYTE
  2350        LDA (FORPNT),Y    LEN=2, SAVE CHAR IN 2ND BYTE
  2360 .2     STA (LOWTR),Y
  2370        DEY               POINT AT 1ST BYTE OF DESC.
  2380        LDA (FORPNT),Y    MOVE FIRST BYTE OF STRING
  2390        STA (LOWTR),Y         TO DESC.
  2400        BPL .1       ALWAYS
  2410 *---LONG STRING HANDLER----------
  2420 .3     LDA (FORPNT),Y    MARK FIRST BYTE OF STRING
  2430        ORA #$80          MAKE NEG ASCII
  2440 .4     STA (FORPNT),Y
  2450        DEY               BACK UP TOWARD BEG. OF DATA
  2460        BMI .1            ...FINISHED MARKING THIS
  2470        LDA (FORPNT),Y    SAVE STRING CHAR IN DESC.
  2480        INY
  2490        STA (LOWTR),Y     IN LAST 2 BYTES
  2500        DEY          OF DESCRIPTOR
  2510        LDA LOWTR,Y  SAVE ADDR INSIDE STRING
  2520        BCS .4       ALWAYS SET
  2530 *---FINISHED MARKING STRINGS-----
  2540 .5     RTS
  2550 *--------------------------------
  2560 *   MOVE THE STRINGS AS HIGH AS POSSIBLE
  2570 *--------------------------------
  2580 RAISE.ALL.ACTIVE.STRINGS
  2590        JSR SET.STRING.POOL.STROLL
  2600        STX LOWTR+1  STARTS AT TOP
  2610        STA LOWTR    OF STRNG POOL
  2620 .1     JSR FIND.NEXT.NEG.BYTE.IN.POOL
  2630 *--------------------------------
  2640 * CARRY CLEAR ON RETURN WHEN THRU
  2650 *--------------------------------
  2660        BCC .4       ...NO MORE STRINGS IN POOL
  2670        LDY #0
  2680        AND #$7F
  2690        STA (FRESPC),Y
  2700 *--------------------------------
  2710 * RESTORE STRING POOL TO POS ASC
  2720 * THEN RESET POINTERS
  2730 *--------------------------------
  2740        SEC
  2750        LDA FRESPC   RECOVER ADDR.
  2760        SBC #2       OF DESCRIPTOR
  2770        STA FRESPC   FROM THE STR
  2780        BCS .2       ...NO BORROW
  2790        DEC FRESPC+1
  2800 .2     LDA (FRESPC),Y
  2810        STA FORPNT   AND PUT IT
  2820        INY          IN FORPNT
  2830        LDA (FRESPC),Y
  2840        STA FORPNT+1
  2850        INY          Y=2
  2860        LDA (FORPNT),Y
  2870 *--------------------------------
  2880 * RESTORE STRING BY RETURNING
  2890 * THE FIRST TWO BYTES WHICH WERE
  2900 * STORED IN THE DESCRIPTOR.
  2910 *
  2920 * THEN POINT DESCRIPTOR TO THE 
  2930 * NEW, CORRECT STRING POSITION.
  2940 *--------------------------------
  2950        DEY
  2960        STA (FRESPC),Y
  2970        LDA (FORPNT),Y
  2980        DEY          Y=0
  2990        STA (FRESPC),Y
  3000        LDA (FORPNT),Y
  3010        STA STRING.LENGTH
  3020        SEC
  3030        LDA LOWTR
  3040 *--------------------------------
  3050 * POINT LOWTR & STRING DESCRIPTOR
  3060 * TO BOTTOM OF NEW STRING POOL.
  3070 *
  3080 * LOWTR HOLDS THE MOVING BOTTOM
  3090 * OF THE STRING POOL.
  3100 *--------------------------------
  3110        SBC STRING.LENGTH
  3120        STA LOWTR
  3130        INY
  3140        STA (FORPNT),Y
  3150        LDA LOWTR+1
  3160        SBC #0
  3170        STA LOWTR+1
  3180        INY
  3190        STA (FORPNT),Y
  3200 *--------------------------------
  3210 * NOW MOVE THE STRING TO ITS
  3220 * NEW ADDRESS.
  3230 *--------------------------------
  3240        LDY STRING.LENGTH
  3250 .3     DEY
  3260        LDA (FRESPC),Y
  3270        STA (LOWTR),Y
  3280        TYA
  3290        BNE .3       ...NOT FINISHED YET
  3300        BEQ .1       ...ALWAYS
  3310 *---FINISHED MOVING STRINGS------
  3320 .4     RTS
  3330 *--------------------------------
  3340 *   RESTORE NORMAL CONFIGURATION OF PNTR AND DATA
  3350 *      FOR STRINGS OF 1 OR 2 CHARACTERS
  3360 *
  3370 *      SCAN THRU VARIABLE SPACE AGAIN:
  3380 *          DESCRIPTORS OF STRINGS MARKED WITH $FF
  3390 *          CONTAIN THE CHAR(S) TO RESTORE TO POOL.
  3400 *
  3410 *      FRESPC POINTS AT BOTTOM OF POOL
  3420 *      LOWTR POINTS AT DESCRIPTORS
  3430 *--------------------------------
  3440 FIX.SHORT.STRINGS
  3450        JSR INITIATE.SEARCH
  3460 .1     JSR FIND.NEXT.STRING.VARIABLE
  3470        BCS .5       ...FINISHED!
  3480        LDY #2       POINT AT 3RD BYTE, 2ND OF ADDR
  3490        STY STRING.LENGTH
  3500        LDA (LOWTR),Y     IF 3RD BYTE =$FF, SHORTY.
  3510        CMP #$FF     A SHORTY?
  3520        BNE .1       ...NO, KEEP SCANNING VARIABLES
  3530        DEY          ...YES, POINT AT 2ND BYTE
  3540        LDA (LOWTR),Y     IF 2ND BYTE ALSO $FF, 
  3550        PHA               THEN LEN=1
  3560        BPL .2       ...NOT $FF, ITS A STR CHAR
  3570        DEC STRING.LENGTH
  3580 .2     DEY          POINT AT 1ST BYTE OF DESCRIPTOR
  3590        LDA (LOWTR),Y     GET 1ST ASC CHAR OF STRING
  3600        PHA               SAVE ON STACK
  3610 *---CALC PLACE IN POOL FOR DATA--
  3620        SEC
  3630        LDA FRESPC   REPOINT FRESPC
  3640        SBC STRING.LENGTH
  3650        STA FRESPC
  3660        BCS .3
  3670        DEC FRESPC+1
  3680 *---RESTORE LENGTH TO DESC.------
  3690 .3     LDA STRING.LENGTH
  3700        STA (LOWTR),Y
  3710 *---STORE CHARS INTO POOL--------
  3720 *--AND ADDR INTO DESCRIPTOR------
  3730        PLA          FIRST CHAR
  3740        STA (FRESPC),Y
  3750        INY
  3760        LDA FRESPC   LOBYTE OF ADDR
  3770        STA (LOWTR),Y
  3780        PLA          2ND CHAR
  3790        BMI .4       ...IT IS $FF, ONLY 1 CHAR
  3800        STA (FRESPC),Y
  3810 .4     INY
  3820        LDA FRESPC+1 HIBYTE OF ADDR
  3830        STA (LOWTR),Y
  3840        BNE .1       ALWAYS
  3850 *---ALL FINISHED WITH SHORTIES---
  3860 .5     RTS
  3870 *--------------------------------
  3880 *      STRING POOL STROLL
  3890 *--------------------------------
  3900 SET.STRING.POOL.STROLL
  3910        LDX MEMSIZE+1 POINT FRESPC
  3920        LDA MEMSIZE   AT HIMEM
  3930        STA FRESPC    TO START
  3940        STX FRESPC+1  STROLL.
  3950        RTS
  3960 *--------------------------------
  3970 *   SEARCH STRING POOL FROM TOP TO BOTTOM
  3980 *      FOR A NEGATIVE BYTE.
  3990 *
  4000 *      RETURN .CS. IF NEG BYTE FOUND,
  4010 *             .CC. IF REACHED END OF POOL
  4020 *--------------------------------
  4030 FIND.NEXT.NEG.BYTE.IN.POOL
  4040        LDX FRESPC+1
  4050        LDY FRESPC
  4060        LDA #0       PAGE AT A TIME
  4070        STA FRESPC
  4080        TYA          IS IT ZERO?
  4090        BNE .2       NO!
  4100 .1     DEX          YES
  4110        CPX FRETOP+1 STILL IN POOL?
  4120        BCC .5       ...NO
  4130        STX FRESPC+1 DO NEXT PAGE
  4140 .2     DEY
  4150        BEQ .3 
  4160        LDA (FRESPC),Y
  4170        BPL .2       POS ASCII
  4180        BMI .4       NEG SO QUIT
  4190 .3     LDA (FRESPC),Y
  4200        BPL .1 NEW PAGE
  4210 .4     CPX FRETOP+1
  4220        BNE .5       FRESPC>FRETOP
  4230        CPY FRETOP   FOR CARRY FLAG
  4240 .5     STY FRESPC   FRESPC POINTS TO NEG ASC
  4250        RTS
  4260 *--------------------------------
  4270 *   SET UP SEARCH OF VAR TABLE
  4280 *--------------------------------
  4290 INITIATE.SEARCH
  4300        LDA VARTAB   START AT BEGINNING OF VARIABLES
  4310        STA INDEX
  4320        LDX VARTAB+1
  4330        STX INDEX+1
  4340        LDY #7       EACH VAR TAKES 7 BYTES
  4350        STY OFFSET
  4360        RTS
  4370 *--------------------------------
  4380 *   FIND NEXT STRING VARIABLE
  4390 *--------------------------------
  4400 FIND.NEXT.STRING.VARIABLE
  4410 .1     LDX INDEX+1   SETUP SEARCH FOR NEXT STRING
  4420        LDA INDEX
  4430        LDY OFFSET
  4440        CPY #7       STILL IN SIMPLE VARIABLES?
  4450        BNE .4       ...NO, IN ARRAYS
  4460        CPX ARYTAB+1 WE WERE, CHECK FURTHER...
  4470        BCC .2       ...YES, STILL SIMPLE
  4480        CMP ARYTAB
  4490        BCS .3       ...NO
  4500 .2     JSR IS.THIS.A.STRING.VARIABLE
  4510        BCS .8       ...STRING FOUND
  4520        JSR NXTEL    NOT A STRING
  4530        BCC .1       ...ALWAYS, TRY AGAIN
  4540 .3     LSR OFFSET   SET OFFSET TO 3 NOW
  4550        STA ARRAY.END
  4560        STX ARRAY.END+1
  4570 .4     CPX ARRAY.END+1 INSIDE AN ARRAY?
  4580        BCC .8       ...YES
  4590        CMP ARRAY.END
  4600        BCC .8
  4610        CPX STREND+1 STILL IN VAR SPC?
  4620        BCC .5       ...YES
  4630        CMP STREND
  4640        BCC .5       ...YES
  4650        RTS          CARRY SET WHEN THRU VAR SPC
  4660 *---SET UP A NEW ARRAY-----------
  4670 .5     LDY #2
  4680        CLC
  4690        LDA (INDEX),Y
  4700        ADC INDEX
  4710        STA ARRAY.END   POINTER TO
  4720        INY          NEXT ARRAY
  4730        LDA (INDEX),Y
  4740        ADC INDEX+1
  4750        STA ARRAY.END+1
  4760        JSR IS.THIS.A.STRING.VARIABLE IS THIS A STR?
  4770        BCS .6       ...YES
  4780        LDA ARRAY.END
  4790        STA INDEX    NO
  4800        LDX ARRAY.END+1
  4810        STX INDEX+1
  4820        BNE .4       ...ALWAYS
  4830 *---FOUND STRING ARRAY-----------
  4840 .6     LDY #4       POINT AT
  4850        LDA (INDEX),Y     #DIMENSIONS OF ARRAY
  4860        ASL          *2
  4870        ADC #5
  4880        ADC INDEX    POINT INDEX TO
  4890        STA INDEX    FIRST ELEMENT
  4900        BCC .7       OF NEW ARRAY
  4910        INC INDEX+1
  4920 .7     LDX INDEX+1
  4930 *
  4940 .8     STA LOWTR    LOWTR->STR DESCRIPTOR
  4950        STX LOWTR+1
  4960 *---NEXT VARIABLE----------------
  4970 NXTEL  CLC
  4980        LDA OFFSET   POINT INDEX TO
  4990        ADC INDEX    NEXT VAR OR ELEMENT
  5000        STA INDEX
  5010        BCC .1 
  5020        INC INDEX+1
  5030        CLC
  5040 .1     RTS          STR FOUND,CARRY CLEAR
  5050 *--------------------------------
  5060 * SUBROUTINE STRING CHECK
  5070 *--------------------------------
  5080 IS.THIS.A.STRING.VARIABLE
  5090        LDY #0
  5100        CLC          INCASE NOT STR
  5110        LDA (INDEX),Y
  5120        BMI .2       ...NOT STRING
  5130        INY
  5140        LDA (INDEX),Y
  5150        BPL .2       ...NOT STRING
  5160        LDA #2       POINT PAST STR NAME
  5170        ADC INDEX
  5180        BCC .1       ...STRING
  5190        INX          INDEX+1
  5200 .1     SEC          CARRY SET IF STR FOUND
  5210 .2     RTS
  5220 *--------------------------------

Changing VERIFY to DISPLAY Bob Sander-Cederlof

In the July 1982 issue of AAL we showed how to make a text file display command inside DOS. Bob Bragner added 80-column display to it in the July 1983 issue. The Dec 1983 InCider printed an article by William G. Wright about a DOS modification that provided text file display without removing any previous features.

Wright's patches modify the VERIFY command so that as sectors are being verified, if the file is a text file, they are displayed on the screen or printer. If there are any $00 bytes in a sector, they are merely skipped over, so his patches will handle some random access files, as well as sequential. Non-text files are still verified in the normal manner.

I was prompted by his article to write ukp another little program. This one will hook into the VERIFY processor in the file manager when you BRUN the program. Later, 30BG from the monitor or CALL 779 from Applesoft will dis-install the patch. My patch modifies VERIFY so that as each sector of a file is verified it is displayed in hexadecimal on the screen or a printer. I do not distinguish between text and non-text files, although it would be a simple matter to do so. As with Wright's patches, random access files may also be displayed, up to the first hole in the track/sector list.

The creative among you will want to add many bells and whistles to my little program. Perhaps 80-column display, showing an entire sector at a time rather than half a sector. Perhaps display of the bytes in both hex and ASCII on the same line. Perhaps the ability to scan back and forth through a file, using the arrow keys. All these are possible, and not too difficult. Have fun!

  1000 *SAVE S.DISPLAY FILE
  1010 *--------------------------------
  1020 *      PATCH DOS TO CHANGE VERIFY
  1030 *      INTO DISPLAY
  1040 *--------------------------------
  1050 MON.PRNTAX .EQ $F941
  1060 MON.PRBL2  .EQ $F94A
  1070 MON.CROUT  .EQ $FD8E
  1080 MON.PRBYTE .EQ $FDDA
  1090 MON.COUT   .EQ $FDED
  1100 *--------------------------------
  1110        .OR $300
  1120 *--------------------------------
  1130 PATCH  LDA #DISPLAY      HOOK INTO VERIFY COMMAND
  1140        STA $AD1C
  1150        LDA /DISPLAY
  1160        STA $AD1D
  1170        RTS
  1180 *--------------------------------
  1190 UNPATCH
  1200        LDA #$B0B6        RESTORE NORMAL VERIFY
  1210        STA $AD1C
  1220        LDA /$B0B6
  1230        STA $AD1D
  1240        RTS
  1250 *--------------------------------
  1260 DISPLAY
  1270        JSR MON.CROUT     START SECTOR WITH <RET>
  1280        JSR $B0B6         READ NEXT SECTOR
  1290        BCS .1            END OF FILE
  1300        LDY #0            DISPLAY FIRST HALF SECTOR
  1310        JSR SHOW
  1320        JSR SHOW          DISPLAY SECOND HALF
  1330        CLC               SIGNAL NOT END OF FILE
  1340 .1     RTS
  1350 *--------------------------------
  1360 SHOW   LDA $B5E5    DISPLAY SECTOR POSITION
  1370        LDX $B5E4
  1380        JSR MON.PRNTAX
  1390        LDA #16      16 LINES PER BLOCK
  1400        STA LCNT
  1410        BNE .2       ...ALWAYS
  1420 .1     LDX #4       PRINT 4 BLANKS
  1430        JSR MON.PRBL2     SO COLUMNS LOOK NEATER
  1440 .2     LDA #8       8 BYTES PER LINE
  1450        STA BCNT
  1460        TYA          PRINT BYTE COUNT
  1470        JSR MON.PRBYTE
  1480        LDA #"-"     PRINT "-"
  1490        JSR MON.COUT
  1500 .3     LDA #" "     PRINT " "
  1510        JSR MON.COUT
  1520        LDA ($42),Y  NEXT BYTE FROM FILE
  1530        INY
  1540        JSR MON.PRBYTE
  1550        DEC BCNT
  1560        BNE .3       MORE TO THIS LINE
  1570        JSR MON.CROUT     NEXT LINE
  1580        DEC LCNT
  1590        BNE .1
  1600        RTS
  1610 *--------------------------------
  1620 BCNT   .BS 1
  1630 LCNT   .BS 1
  1640 *--------------------------------

Changing Tab Stops
in the 68000 Cross Assembler
Bob Sander-Cederlof

The procedure as described in the S-C Macro Assembler manual works for the 6502 version and for all the cross assemblers except the 68000 cross assembler. The procedure described in Appendix D will not work because the 68000 cross assembler uses both banks of memory at $D000-DFFF. In order to be certain the correct one is switched on, the command interpreter keeps using the selection soft switches. The result is that the bank stays write-protected, and no patches ever get installed.

Of course, there is a simple way around the problem. Here is how to change the tab stops in the 68000 Cross Assembler:

First, boot the cross assembler disk and select option 2, loading the language card version at $D000.

     :BLOAD S-C.ASM.MACRO.68000.LC
     :MNTR
     *AA60.AA61
     *AA60- xx yy   (probably C6 27)
     *D010.D014
     D010- 0E 16 1B 20 00
     *C083 C083 D010:7 10 1B 2B  (or whatever values you like)
     C083- zz
     C083- zz
     *D010.D014
     D010- 07 10 1B 2B 00
     *BSAVE S-C.ASM.MACRO.68000.LC,A$D000,L$yyxx
     *3D0G
     :     that's it!

Similar methods apply to the other customizing patches mentioned in Appendix D.


S-C Macro and GPLE.LC on the //e Bob Bragner
Istanbul, Turkey

I've long been bothered by the way loading the S-C Macro Assembler wipes out GPLE (Neil Konzen's Global Program Line Editor) when you go from Applesoft to Assembler. It shouldn't happen, because GPLE resides in the alternate bank at $D000, not used by Macro. I've also been unhappy that the //e version of S-C Macro Assembler doesn't have the automatic line of dashes provided by <esc> L after a line number when you are in 80-column mode.

Just the other day I discovered by accident that all is not lost. If things are done in just the right sequence both of my peeves vanish.

First load up the S-C Macro Assembler into the $D000 area. Then enter Applesoft by typing the FP command, and BRUN GPLE.LC. Initialize the 80-column card with ctrl-V and enter the assembler by typing the INT command. This leaves GPLE connected so that the assembler sees the <esc> L command. Try it by typing a line number and <esc> L.

It also allows the assembler to see <esc> L to turn a catalog line into a LOAD command, but due to the way the word LOAD is poked onto the screen you get L O A D which clobbers the file name. (I never use the automatic load anyway, so this does not bother me.)

RESET will partially disable GPLE.LC, but you can restore it by typing the & command from Applesoft. If you want RESET to NOT molest GPLE, change the reset vector to $B6B3. You can do this from the monitor with "3F2:B3 B6 13", or from S-C Macro with "RST $B6B3".

I don't know why this all works, but I think it has something to do with the way the 80-column card initializes itself by copying the //e's monitor ROM into the $F800-FFFF space of RAM.

By the way, GPLE uses some patch space inside DOS 3.3 which is also used by the fast DOS text file I/O patch, so beware of mixing them.


Redundancy in Tables for Faster Lookups Bob Sander-Cederlof

When speed is the main objective, you can sometimes use table lookups to great advantage. You trade program size for speed.

Here is an easy example. Suppose I want to convert the two nybbles of a byte to ASCII characters. I can do it all with code, like this:

CONVERT
       PHA             Save original byte
       LSR             Position first nybble
       LSR
       LSR
       LSR
       JSR MAKE.ASCII
       STA XXX
       PLA             Original byte
       AND #$0F        Isolate second nybble
       JSR MAKE.ASCII
       STA XXX+1
       RTS

MAKE.ASCII
       ORA #$B0        Make B0...BF
       CMP #$BA
       BCC .1          It is 0-9
       ADC #6          Make A-F codes
.1     RTS

That takes 30 bytes, and 75-77 cycles including a JSR CONVERT to call it. Actually 75 cycles if both nybbles are 0-9, 77 cycles if they both are A-F, and 76 cycles if there is one of each. If I move the code from MAKE.ASCII in-line, it saves 24 cycles (two JSRs, two RTSs), and only lengthens the program by one byte.

Or I can do a table lookup by substituting these two lines for both JSR MAKE.ASCII lines above:

       TAX
       LDA ASCII.TABLE,X

and making a little table like this:

ASCII.TABLE .AS -/0123456789ABCDEF/

In this form, the program takes 49 cycles, and uses a total of 39 bytes including the table. Perhaps it could be an advantage that the # of cycles is always constant, regardless of the value being converted.

You can make it even faster by using two whole pages for table space, like this:

CONVERT
       TAX
       LDA HI.TABLE,X
       STA XXX
       LDA LO.TABLE,X
       STA XXX+1
       RTS

HI.TABLE
       .AS -/0000000000000000/
       .AS -/1111111111111111/
       .
       .
       .AS -/FFFFFFFFFFFFFFFF/

LO.TABLE
       .AS -/0123456789ABCDEF/
       .AS -/0123456789ABCDEF/
       .
       .
       .AS -/0123456789ABCDEF/

The program itself is 14 bytes long, but there are 512 bytes of tables. The conversion, including JSR and RTS, now takes only 30 cycles. And since the program is now so short, it would probably get placed in line, saving the JSR-RTS, converting in only 18 cycles. And if the in-line routine already had the nybble in the X-reg, whack off another two cycles.

The redundancy in the tables gives a huge speed increase.

I have been tearing into the super fast copy utility that comes with Locksmith 5.0, and I discovered some of these redundancy tricks in their disk I/O tables. For example, the table for converting a six-bit value into a disk-code normally takes 64 bytes. The table looks like this:

TABLE  .HS 96979A9B9D9E9FA6
       .
       .
       .HS F7F9FAFBFCFDFEFF

Code to access the table might look like this:

       LDA BUFFER,X
       AND #$3F        Mask to 6 bits
       TAY
       LDA TABLE,Y

When you are writing to a disk, every single cycle counts. Therefore, it is pleasant to discover redundant tables. By making four copies of the table, using 256 bytes rather than 64, we no longer need to strip off the first two bits. The code can be shortened to this:

       LDY BUFFER,X
       LDA TABLE,Y

It only saves 3 cycles, but those three cycles can and do make the whole difference in the fast copy program. That is part of Locksmith's secret to reading a whole disk into RAM in only 8 seconds.


Speaking of Locksmith Warren R. Johnson

Did you know that Locksmith 5.0 can nearly be copied by plain old COPYA? Or with its own fast backup copier? All but the last few tracks copy, and they may not be necessary.

The only problem is, the resulting copy will not boot until you make a small patch using some sort of disk ZAP utility. (You can use Omega's Inspector/Watson team, Bag of Tricks, Disk Fixer, CIA, for example.) Patch Track-0F Sector-0E Byte-6F: change it from 6C to 0F. [ Editor's note: in my copy, Locksmith had C6 in that byte rather than 6C. And I have not tried the resulting disk to see if all functions work. ]

I have modified my Apple a little to make my life easier. I have 2732's in the motherboard ROM sockets, with bank switch selection. Applesoft is in one bank, and a modified version of Applesoft in the other. My modifications include replacing the old cassette commands (LOAD/SAVE/SHLOAD etc.) for an INWAT command. INWAT downloads the Inspector and Watson from some expansion chassis ROM boards.


Lancaster's OBJ.APWRT ][ F Bob Sander-Cederlof

You may have noticed a little ad in the last few issues for an obscure title, "OBJ.APWRT ][ F". Don Lancaster, author of such favorites as Enhancing the Apple, Incredible Secret Money Machine, Micro Cookbook, etc., has torn into Applewriter //e. After a thorough analysis, he completely documented it, in the style of Beneath Apple DOS. The results, or at least part of them, will be chapter 12 in volume 2 of Enhancing.

He sent me a pre-print to look at and make comments about. My main comment is WOW! It doesn't matter if you like Applewriter or not. It doesn't even matter if you have never seen Applewriter. You still can learn a tremendous amount by reading through Don's text and comments. Of course it is better if you DO have Applewriter //e, because he tells you how to make some great customizing modifications.

You can get it all on disk for only $29.95. Actually, it is not on "disk" ... it is on SIX disk sides, jam-packed full. Don even throws in a free book for good measure.

You can order OBJ.APWRT ][ F directly from Synergetics, or from S-C Software.

Speaking of Word Processors................Bob Sander-Cederlof

For some time now we have been selling our S-C Word Processor, complete with all source code on disk. We hoped that some users would send us their improvements, and sure enough they have. Bob Gardner recently sent us a bunch, and that motivated me to go back over the package.

The disk now includes both II/IIPlus and //e versions. The //e version allows an 80-column preview (still only 40-column during edit mode). I added titles and page numbers, a single sheet mode, and more. Even with all the new features, the new object code is a little shorter than the old, leaving even more room for your own modifications and enhancements. I improved the internal documentation. The "manual-ette" is now 10 pages rather than 6. A small tutorial file helps you get started.

The price is still only $50. Owners of the old version can get a new copy for only $5.


About Disk Drive Pressure Pads Bob Sander-Cederlof

After you have used your disk drive for six months or so, it will probably develop a scary noise or two. I know mine have.

My oldest drive is serial 1901 (the Shugart mechanics inside the box have a number somewhere in the low 400's). Every once and a while it will make the most dangerous sounding noise you ever heard, something like dragging rusty chains across the road. I have read in various magazines and newsletters that these noises are almost always caused by a dirty pressure pad.

The pressure pad rides on the top surface of the disk, pressing the disk surface down against the recording head. It is a 1/8 inch circle of felt glued to a slightly larger plastic stud. The shaft of the stud is split and tapered, so it will fit through a hole and lock in place. You can easily remove the pad and stud by pressing on the split end.

But where do you get new ones? Maybe at a computer store, but they sure don't keep them on display. I decided to try a little home maintenance, and it worked. I gently scraped the felt surface with the blade of my pocket knife, and all the old caked oxide turned to powder and fell off. Then I rubbed the oxide on a piece of paper, to smooth out the felt. After putting the drive all back together, it ran quietly.

It worked so well, I performed the operation on two more drives. And surprisingly, one drive which had been giving lots of errors was working accurately again.

A few other disk maintenance tips:

1. One particularly noisy drive a few years ago had loose screws trying to hold the drive motor down. A few wrist twists and all was well.

2. If a drive can read, but writes garbage, it is probably the 74LS125 on the analog board inside the drive. Replace that chip for 25 cents or so, and you have saved $60 in repair bills.


Will ProDOS Work on a Franklin? Bob Stout

If you try to boot up ProDOS on a Franklin, it probably will fail. The ProDOS boot routine checks to see if you are in a genuine Apple monitor ROM. However, you can make it work.

Start the boot procedure; when meaningful action appears to have ceased, press the RESET switch. Get into the monitor and type 2647:EA EA and 2000G. Voila!


Rod's Color Pattern in 6502 Code Charles H. Putney

When I read the January AAL with Bob Urschel's article about running Rod's Color Pattern on the QWERTY 68000 board, it sounded like a challenge. You may remember I like speed challenges, at least inside computers.

Fifty times faster than Basic didn't sound too fast, so I checked a simple loop to see if it might be possible to save the dignity of the 6502. It did look possible, at least by using tricky table-driven code.

So, I wrote some more code and it looked like 8.0 seconds per loop. This clocks out at 55 times faster than Integer BASIC, but I didn't have the internal calculation for the color value exactly like the original.

I finally decided to use a table lookup for the color calculation. Now the problem was how to create all those data statements. I thought about using some macros, but the calculations are too involved. I wrote an Applesoft program to generate the lines of code for the assembler, and then EXECed them into my source code. I finally got all the bugs out and timed it.

The table-driven version performs a main loop every 6.2 seconds, compared to 446 seconds per loop for the Integer BASIC version. That is nearly 72 times faster.

Well, my only worry now is that Bob Urschel made an error in his timing, and his really runs 200 times faster. If not, we have saved face for the venerable 6502.

Of course, we did use a little more memory. But that is frequently a trade-off worth making in important programs.

For comparison purposes, here is the Integer BASIC program again:

     10 GR
     20 FOR W = 3 TO 50
     30 FOR I = 1 TO 19
     40 FOR J = 0 TO 19
     50 K = I+J
     60 COLOR = J*3 / (I+3) + I*W/12
     70 PLOT I,K: PLOT K,I: PLOT 40-I,40-K:PLOT40-K,40-I
     80 PLOT K,40-I: PLOT 40-I,K: PLOT I,40-K: PLOT 40-K,I
     90 NEXT J: NEXT I: NEXT 2
     100 GO TO 20

My program to generate the data tables includes similar logic. I broke the tables into two planes, rather than storing one data table 48*19*20 = 18,240 bytes long. One plane computes J*3/(I+3), and includes 380 bytes. The other computes I*W/12 and includes 48*19=912 bytes. My table lookup routine uses I and J to index into the first plane, and I and W into the second. Then the two values are added together. Pretty tricky!

I believe in letting computers work for me, so I had to use some macros to simplify typing in all the code for those eight plot statements. I wrote a PLOT macro, but then I noticed that there was some redundant code that way. By rearranging the order of the PLOT statements, I can separate the y-setup from the x-setup and plot. That way the base address does not get re-calculated as often, saving more time. Here is my program:

     100  REM BUILD TABLES FOR ROD'S COLOR
     110 D$ =  CHR$ (4)
     120  PRINT D$"OPEN TTT": PRINT D$"WRITE TTT"
     130  GOSUB 200
     140  PRINT D$"CLOSE"
     150  END 
     200  FOR I = 1 TO 19
     210  PRINT "WI" LEFT$ ( STR$ (I) + " ",2)" .HS ";
     220  FOR W = 3 TO 26: GOSUB 500: NEXT : PRINT 
     230  PRINT "     .HS ";
     240  FOR W = 27 TO 50: GOSUB 500: NEXT : PRINT 
     250  NEXT I
     300  FOR I = 1 TO 19
     310  PRINT "JI" LEFT$ ( STR$ (I) + " ",2)" .HS ";
     320  FOR J = 0 TO 19
     330 C =  INT (J * 3 / (I + 3)):C = C -  INT (C / 16) * 16: IF C > 
          9 THEN C = C + 7
     340  PRINT "0" CHR$ (C + 48);
     350  NEXT : PRINT 
     360  NEXT I
     370  RETURN 
     500 C =  INT (I * W / 12):C = C -  INT (C / 16) * 16: IF C > 9 THEN 
          C = C + 7
     510  PRINT "0" CHR$ (C + 48);
     520  RETURN 
  1000        .LIST MOFF
  1010 *SAVE S.PUTNEY'S COLOR
  1020        .OR $6000
  1030        .TF B.PUTNEY
  1040 *--------------------------------
  1050 *
  1060 *      FAST ROD'S COLOR PATTERN
  1070 *
  1080 *      CHARLES H. PUTNEY
  1090 *      18 QUINNS ROAD
  1100 *      SHANKILL
  1110 *      CO. DUBLIN
  1120 *      IRELAND
  1130 *
  1140 *--------------------------------
  1150 *
  1160 *      PAGE ZERO ADDRESSES
  1170 *
  1180 INVI   .EQ $EE      VARIABLE 40 - I
  1190 INVK   .EQ $EF      VARIABLE 40 - K
  1200 POINTR .EQ $F9      LORES PAGE POINTER (TWO BYTES)
  1210 I      .EQ $FB      VARIABLE I
  1220 J      .EQ $FC      VARIABLE J
  1230 K      .EQ $FD      VARIABLE K
  1240 W      .EQ $FE      VARIABLE W
  1250 COLOR1 .EQ $07      HALF OF COLOR FORMULA
  1260 *--------------------------------
  1270 COLOR  .EQ $08,09
  1280 COLEVN .EQ $08      EVEN ROW COLOR
  1290 COLODD .EQ $09      ODD ROW COLOR 
  1300 MASK   .EQ $0A,0B
  1310 MSKODD .EQ $0A
  1320 MSKEVN .EQ $0B
  1330 *
  1340 *--------------------------------
  1350 *
  1360 *      ADDRESS TABLE
  1370 *
  1380 ODDMSK .EQ $F0      MASK FOR ELIMINATING UPPER BLOCK (LOWER NIBBLE)
  1390 EVNMSK .EQ $0F      MASK FOR ELIMINATING LOWER BLOCK (UPPER NIBBLE)
  1400 GRAPH  .EQ $FB40    ENABLE LO RES GRAPHICS
  1410 *--------------------------------
  1420 *   MACRO DEFINITIONS
  1430 *--------------------------------
  1440        .MA PLY      PLY ]1
  1450        LDY ]1       Y-COORD
  1460        LDA LORESL,Y 
  1470        STA POINTR 
  1480        LDA LORESH,Y
  1490        STA POINTR+1
  1500        TYA
  1510        AND #1       GET LSB
  1520        TAX
  1530        .EM
  1540 *--------------------------------
  1550        .MA PLX      PLX ]1
  1560        LDY ]1       X-COORD
  1570        LDA (POINTR),Y GET THE PAGE BYTE
  1580        AND MASK,X
  1590        ORA COLOR,X
  1600        STA (POINTR),Y PUT IT BACK
  1610        .EM
  1620 *--------------------------------
  1630        .MA NEXT     NEXT VAR,LIMIT+1
  1640        INC ]1       INCREMENT ]1 VARIABLE
  1650        LDA ]1       TEST IF ]1=]2 YET
  1660        CMP #]2
  1670        BCS :1       YES, LEAVE LOOP
  1680        JMP NEXT.]1  NO, KEEP LOOPING
  1690 :1
  1700        .EM
  1710 *--------------------------------
  1720        .MA GET      GET FORMULA,INDEX
  1730        LDY I
  1740        LDA ]1L-1,Y
  1750        STA POINTR
  1760        LDA ]1H-1,Y
  1770        STA POINTR+1
  1780        LDY ]2
  1790        LDA (POINTR),Y
  1800        .EM
  1810 *--------------------------------
  1820 *
  1830 *
  1840 *      MAIN LOOP
  1850 *
  1860 ROD    LDA $C056    SET LORES GRAPHICS ON
  1870        LDA $C053    MIXED MODE
  1880        JSR GRAPH
  1890        LDA #ODDMSK
  1900        STA MSKODD
  1910        LDA #EVNMSK
  1920        STA MSKEVN
  1930 *--------------------------------
  1940 BIG.LOOP
  1950        JSR $FBE2    *** TESTING BEEP ***
  1960        LDA #0       FOR W=3 TO 50 (0...47)
  1970        STA W
  1980 *---NEXT W COMES HERE------------
  1990 NEXT.W LDA #1       FOR I=1 TO 19
  2000        STA I 
  2010        LDA #39
  2020        STA INVI
  2030        LDA $C030    JUST FOR AUDIBLE FEEDBACK
  2040 *---NEXT I COMES HERE------------
  2050 NEXT.I LDA I        SET UP K = I+J
  2060        STA K
  2070        LDA INVI
  2080        STA INVK
  2090        >GET FORM1,W
  2100        STA COLOR1   SAVE IT FOR INNER LOOP
  2110        LDA #0       FOR J=0 TO 19
  2120        STA J
  2130 *---NEXT J COMES HERE------------
  2140 NEXT.J >GET FORM2,J
  2150        CLC          ADD THE FORMULAS
  2160        ADC COLOR1   ACC = J*3/(I+3)+I*W/12
  2170        AND #$0F     MASK OFF TOP
  2180        STA COLEVN   EVEN COLOR
  2190        ASL          SHIFT 4 BITS
  2200        ASL
  2210        ASL
  2220        ASL
  2230        STA COLODD   ODD COLOR
  2240 *--------------------------------
  2250        >PLY I
  2260        >PLX K
  2270        >PLX INVK
  2280 *--------------------------------
  2290        >PLY INVI
  2300        >PLX K
  2310        >PLX INVK
  2320 *--------------------------------
  2330        >PLY K
  2340        >PLX I
  2350        >PLX INVI
  2360 *--------------------------------
  2370        >PLY INVK
  2380        >PLX I
  2390        >PLX INVI
  2400 *--------------------------------
  2410        INC K
  2420        DEC INVK
  2430        >NEXT J,20
  2440 *--------------------------------
  2450        DEC INVI
  2460        >NEXT I,20
  2470 *--------------------------------
  2480        >NEXT W,48
  2490 *--------------------------------
  2500        LDA $C000    ANY KEY PRESSED?
  2510        BMI ROD4     YES
  2520        JMP BIG.LOOP   NO, KEEP LOOPING
  2530 ROD4   STA $C010
  2540        RTS
  2545        .LIST OFF
  2550 *--------------------------------
  2560 *
  2570 *      LORES GRAPHICS PAGE
  2580 *      BASE ADDRESSES (40 COL)
  2590 *
  2600 LORESL .HS 0000808000008080
  2610        .HS 0000808000008080
  2620        .HS 2828A8A82828A8A8
  2630        .HS 2828A8A82828A8A8
  2640        .HS 5050D0D05050D0D0
  2650 LORESH .HS 0404040405050505
  2660        .HS 0606060607070707
  2670        .HS 0404040405050505
  2680        .HS 0606060607070707
  2690        .HS 0404040405050505
  2700 *
  2710 *--------------------------------
  2720 *
  2730 *      TABLE FOR I*W/12
  2740 *
  2750 FORM1L .DA #WI1,#WI2,#WI3,#WI4,#WI5,#WI6,#WI7
  2760        .DA #WI8,#WI9,#WI10,#WI11,#WI12,#WI13,#WI14
  2770        .DA #WI15,#WI16,#WI17,#WI18,#WI19
  2780 FORM1H .DA /WI1,/WI2,/WI3,/WI4,/WI5,/WI6,/WI7
  2790        .DA /WI8,/WI9,/WI10,/WI11,/WI12,/WI13,/WI14
  2800        .DA /WI15,/WI16,/WI17,/WI18,/WI19
  2810 *--------------------------------
  2820 WI1  .HS 000000000000000000010101010101010101010101020202
  2830      .HS 020202020202020202030303030303030303030303040404
  2840 WI2  .HS 000000010101010101020202020202030303030303040404
  2850      .HS 040404050505050505060606060606070707070707080808
  2860 WI3  .HS 000101010102020202030303030404040405050505060606
  2870      .HS 060707070708080808090909090A0A0A0A0B0B0B0B0C0C0C
  2880 WI4  .HS 010101020202030303040404050505060606070707080808
  2890      .HS 0909090A0A0A0B0B0B0C0C0C0D0D0D0E0E0E0F0F0F000000
  2900 WI5  .HS 0101020202030304040505050606070707080809090A0A0A
  2910      .HS 0B0B0C0C0C0D0D0E0E0F0F0F000001010102020303040404
  2920 WI6  .HS 01020203030404050506060707080809090A0A0B0B0C0C0D
  2930      .HS 0D0E0E0F0F00000101020203030404050506060707080809
  2940 WI7  .HS 0102020304040505060707080809090A0B0B0C0C0D0E0E0F
  2950      .HS 0F00000102020303040505060607070809090A0A0B0C0C0D
  2960 WI8  .HS 0202030404050606070808090A0A0B0C0C0D0E0E0F000001
  2970      .HS 0202030404050606070808090A0A0B0C0C0D0E0E0F000001
  2980 WI9  .HS 02030304050606070809090A0B0C0C0D0E0F0F0001020203
  2990      .HS 04050506070808090A0B0B0C0D0E0E0F0001010203040405
  3000 WI10 .HS 0203040505060708090A0A0B0C0D0E0F0F00010203040405
  3010      .HS 06070809090A0B0C0D0E0E0F000102030304050607080809
  3020 WI11 .HS 02030405060708090A0B0B0C0D0E0F000102030405060607
  3030      .HS 08090A0B0C0D0E0F00010102030405060708090A0B0C0C0D
  3040 WI12 .HS 030405060708090A0B0C0D0E0F000102030405060708090A
  3050      .HS 0B0C0D0E0F000102030405060708090A0B0C0D0E0F000102
  3060 WI13 .HS 030405060708090A0B0D0E0F0001020304050607080A0B0C
  3070      .HS 0D0E0F0001020304050708090A0B0C0D0E0F000102040506
  3080 WI14 .HS 0304050708090A0B0C0E0F0001020305060708090A0C0D0E
  3090      .HS 0F00010304050607080A0B0C0D0E0F01020304050608090A
  3100 WI15 .HS 03050607080A0B0C0D0F00010204050607090A0B0C0E0F00
  3110      .HS 010304050608090A0B0D0E0F00020304050708090A0C0D0E
  3120 WI16 .HS 04050608090A0C0D0E00010204050608090A0C0D0E000102
  3130      .HS 04050608090A0C0D0E00010204050608090A0C0D0E000102
  3140 WI17 .HS 04050708090B0C0E0F010203050608090A0C0D0F00020304
  3150      .HS 0607090A0B0D0E000103040507080A0B0C0E0F0102040506
  3160 WI18 .HS 040607090A0C0D0F000203050608090B0C0E0F0102040507
  3170      .HS 080A0B0D0E000103040607090A0C0D0F000203050608090B
  3180 WI19 .HS 040607090B0C0E0F0103040607090A0C0E0F010204060709
  3190      .HS 0A0C0D0F0102040507090A0C0D0F0002040507080A0C0D0F
  3200 *--------------------------------
  3210 *
  3220 *      TABLE FOR J*3/(I+3)
  3230 *
  3240 FORM2L .DA #JI1,#JI2,#JI3,#JI4,#JI5,#JI6,#JI7
  3250        .DA #JI8,#JI9,#JI10,#JI11,#JI12,#JI13,#JI14
  3260        .DA #JI15,#JI16,#JI17,#JI18,#JI19
  3270 FORM2H .DA /JI1,/JI2,/JI3,/JI4,/JI5,/JI6,/JI7
  3280        .DA /JI8,/JI9,/JI10,/JI11,/JI12,/JI13,/JI14
  3290        .DA /JI15,/JI16,/JI17,/JI18,/JI19
  3300 *--------------------------------
  3310 JI1  .HS 00000102030304050606070809090A0B0C0C0D0E
  3320 JI2  .HS 00000101020303040405060607070809090A0A0B
  3330 JI3  .HS 0000010102020303040405050606070708080909
  3340 JI4  .HS 0000000101020203030304040505060606070708
  3350 JI5  .HS 0000000101010202030303040404050506060607
  3360 JI6  .HS 0000000101010202020303030404040505050606
  3370 JI7  .HS 0000000001010102020203030303040404050505
  3380 JI8  .HS 0000000001010101020202030303030404040405
  3390 JI9  .HS 0000000001010101020202020303030304040404
  3400 JI10 .HS 0000000000010101010202020203030303030404
  3410 JI11 .HS 0000000000010101010102020202030303030304
  3420 JI12 .HS 0000000000010101010102020202020303030303
  3430 JI13 .HS 0000000000000101010101020202020203030303
  3440 JI14 .HS 0000000000000101010101010202020202030303
  3450 JI15 .HS 0000000000000101010101010202020202020303
  3460 JI16 .HS 0000000000000001010101010102020202020203
  3470 JI17 .HS 0000000000000001010101010101020202020202
  3480 JI18 .HS 0000000000000001010101010101020202020202
  3490 JI19 .HS 0000000000000000010101010101010202020202
  3500 *--------------------------------

Will ProDOS Really Fly? Bob Sander-Cederlof

ProDOS appears to have been eclipsed by MacIntosh. The major software houses are probably putting their main effort into Mac.

ARTSCI has announced a ProDOS version of their MagiCalc spreadsheet program. Owners of the DOS 3.3 version may upgrade for $40, new customers pay $149.95. The only differences claimed are faster disk I/O and ability to edit the printer setup string. Nice, but $40 is a lot. And the spreadsheet files would no longer be accessible to DOS-based utilities.

ARTSCI will also send you their ProDOS catalog sorter program, complete with BASIC.SYSTEM, CONVERT, FILER, and the ProDOS image for only $24.95. Apple will reputedly be selling ProDOS with a user's manual and some tutorial files in addition to the files on ARTSCI's disk, but price and date are still unclear. (You get them free with a new disk drive.)

Practical Peripherals has announced a new clock card which is ProDOS compatible. Their design is apparently an upgrade of Superclock II (by Jeff Mazur, Westside Electronics). ProDOS was designed around Thunderclock, so other clocks must either emulate one of the Thunderclock modes or patch ProDOS during the boot process. Applied Engineering's new Timemaster II emulates Thunderclock and several others, so it is fully ProDOS compatible.

According to Don Lancaster, Applewriter //e has been written so that changing to ProDOS would be easy. Therefore we might expect a ProDOS-based version of this popular word processor to be announced soon. Or maybe they won't bother to announce it.

Meanwhile, I know of at least two people with plans to integrate the faster RWTS ProDOS uses into their enhanced DOS 3.3 packages. Have you seen the latest ads for David-DOS? Dave Weston compares the speeds of his fast DOS with DOS 3.3 and Pro-DOS. Guess what ... Pro-DOS doesn't win.

Unless you MUST have file compatibility with Apple /// SOS; or you MUST have hard hard-disk support for very large files; or you MUST have a hierarchical file directory; then stick with DOS 3.3, enhanced by Dave, or Bill Basham, or Art Schumer, or others. And if you MUST have at least 32K of program space with Applesoft; or you MUST have Integer BASIC support; or you MUST have compatibility with hundreds of existing software products; then stick with DOS 3.3.


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.)