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