Volume 4 -- Issue 6 | March 1984

In This Issue...

- Fast Garbage Collection
- Changing VERIFY to DISPLAY
- Changing Tab Stops in the 68000 Cross Assembler
- S-C Macro and GPLE.LC on the //e
- Redundancy in Tables for Faster Lookups
- Speaking of Locksmith
- Lancaster's OBJ.APWRT ][ F
- About Disk Drive Pressure Pads
- Will ProDOS Work on a Franklin?
- Rod's Color Pattern in 6502 Code
- Will ProDOS Really Fly?

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