BApple Assembly Line Volume 3 -- Issue 1 October, 1982 In This Issue... ---------------- Catalog Arranger . . . . . . . . . . . . . . . . . . . . . 2 So You Never Need Macros!. . . . . . . . . . . . . . . . . 17 Converting ToolKit Source to S-C . . . . . . . . . . . . . 21 Correction to Bob's Fast Screen Scroll . . . . . . . . . . 28 . . . . . . . . . . . . . . . . 30-31 Another Lower Case Patch for S-C Macro . . . . . . . . . . 32 Writing for AAL . . . . . . . . . . . . . . . . . . . . . 32 This issue of AAL is late. No sooner do I warn you of one magazine that is behind in their publication schedule, than I get behind myself! We plan to catch up with the next issue. I just returned from 8 days in California. Some of you know that I am on the board of directors of the International Apple Core. After the board meeting I contacted a few long-time customers. I also attended the San Francisco Apple Corps Swap Meet. I looked up Peter Meyer, author of SDS's "Routine Machine"; together we had dinner at the home of Pat Caffrey, co-author of "Doubletime Printer". Peter is now working on the fourth volume of additional Applesoft-extenders for his Routine Machine. I also spent two half-days with Henry Spragens, well known for his early contributions to Apple graphics lore. He bought his (first) Apple long ago in Kentucky, where he was one of the original members of LAUGHS (Louisville Apple User's Group for Hardware and Software!). Now he works at Beck-Tech in Berkeley, doing exotic things in the world of synthesized video graphics with the Apple and other machines. A few weeks earlier I spent the afternoon with DeWayne Van Hoozer in Houston, at the HAAUG meeting (you're right, it is pronounced "hog"!). DeWayne's Genasys project is nearing the publication stage, so you'll probably be hearing more about it soon. I am looking forward to some more time in Houston around Halloween, at the AppleFest there. Look me up if you are there, in or near the International Apple Core booth. Catalog Arranger ...................................Bill Morgan --------------------------------------------------------------- We all have the problem: a disk starts getting full, we delete some files to make space, and our new files (from our latest project) end up scattered all through the catalog. A disk that has been used for a few months ends up with a thoroughly shuffled catalog. There are programs available to alphabetize a catalog, but that's not always what I want to do. I want HELLO at the beginning, utilities next (assembler, text editor, ES-CAPE, disk zap, etc.), then various projects. The files for each project should all be grouped together, with the current job at the end of the catalog. I decided that what I want to be able to do is to "pick up" one entry in the catalog, move it to exactly where I want it, put it down, then go get another one and put that one in its place, and so on. Here's my program to do just that. Using Catalog Arranger First BLOAD CATALOG ARRANGER, then insert the disk you want to modify. When you type CALL 2051 (or 803G from the monitor) the disk will spin for a little while as the catalog is read into a sort of string array. The first 22 entries in the catalog will then be displayed, with the first entry shown in inverse. You may notice that deleted files are also displayed, with a minus sign before the file type and a stray inverse character out at the end of the file name. Control characters are also displayed in inverse. The inverted entry is a cursor showing the "active entry". If you press the arrow keys, this cursor will move up and down the display. When the cursor reaches the center of the screen, it will stop moving and the display will scroll up and down around it. When you have the cursor on an item you wish to move, press RETURN. The word "MOVING" will appear in inverse in the lower left corner of the screen. When this "moving flag" is on, the entry in the cursor will be carried wherever the cursor goes. When it reaches the place where you want to put it, press RETURN again. The moving flag will disappear and that entry will stay where you just put it. There are a couple of other commands as well. Pressing the "B" key moves the cursor to the beginning of the catalog, and the "E" key moves it to the end. If the moving flag is on, the item in the cursor will be carried right along. There is also an "R" command, to read in a new catalog. This is useful if you want to reread the current catalog and start all over again, or to move on to another disk. When you have the catalog arranged just the way you want it, press the "W" key to write the revised catalog onto the disk. Press ESC when you want to exit the program. Catalog Organization If you are familiar with the internal structure of an Apple DOS catalog, you can skip ahead to the section labelled "How Catalog Arranger Works". The first step in reading a disk catalog is to read the VTOC (Volume Table of Contents), which is always located at track $11, sector 0. The second and third bytes in the VTOC (offsets 1 and 2) contain the track and sector of the start of the catalog. On a standard DOS 3.3 disk these always point to track $11, sector $0F, and the rest of the catalog is always on track $11. These locations can be changed, however. For example, some programs to convert DOS 3.2 disks to DOS 3.3 leave the first catalog sector at track $11, sector $0C. Therefore, it is safest to follow the pointers rather than assuming that the catalog will always be in its usual place. In a catalog sector, the first byte is not used. The second and third bytes point to the next track and sector of the catalog. If the track byte is zero, it means that there is no next sector, this is the end of the catalog. The fourth through the eleventh bytes are not used. The actual catalog information starts at the twelfth byte of the sector (offset $B) and fills the rest of the sector. Each catalog entry takes 35 bytes, so there are 7 entries in each sector. The first two bytes of an entry contain the track and sector of the file's track/sector list. If the first byte is $FF, the file has been deleted. In that case, the track number has been moved to the end of the file name. If the first byte is zero, this entry has never been used, and we are past the end of the catalog. The third byte tells the file type and whether the file is locked. Here are the type codes: 00 -- TEXT file 01 -- INTEGER BASIC program 02 -- APPLESOFT BASIC program 04 -- BINARY file 08 -- S file -- not used 10 -- R file -- DOS Toolkit relocatable object file 20 -- A file -- Lazer Pascal source file 40 -- B file -- Lisa assembler source file If the file is locked, $80 is added to the type code. The next 30 bytes are the file name. If the name is less than 30 characters long, it is filled out with spaces (ASCII $A0). The last two bytes are the length of the file, in sectors. This is expressed as first low byte, then high byte. Here's a diagram: 0 1 2 3 . . . . 32 33 34 --------------------------------- -------------------------- | Track | Sector | Type | Name . . . . | Len - Lo | Len - Hi | --------------------------------- -------------------------- You can find more information on the structure of the catalog in the DOS Manual on pages 129-134 or in the book Beneath Apple DOS on pages 4-4 through 4-7. It is impossible to recommend Beneath Apple DOS too highly; if you have any interest in the internals of DOS, get that book. How Catalog Arranger Works After initialization, the program builds a table of pointers into the storage area. We can build this table in advance because we know that all entries will be the same length. The catalog is then read into the array, each entry being placed according to the next pointer in the table. The end of the table is then marked with two zero bytes after the last element used. The next step is to display the entries in the array. The display routine starts by checking ACTIVE.ELEMENT to see whether to start the display with the first element, or somewhere in the middle. It then scans up the table, displaying each catalog entry and inverting the one corresponding to ACTIVE.ELEMENT. The routine that actually displays each line borrows a couple of subroutines in DOS to decode the file type and display the file size. When MOVING.FLAG is off, the arrow, B, and E commands simply change the value of ACTIVE.ELEMENT. When MOVING.FLAG is on, the arrows swap entries in the table up or down, and B and E repeatedly swap entries to move ACTIVE.ELEMENT to the beginning or end. When writing the catalog back onto the disk we have to be careful to put the catalog sectors back in the same place they came from, since we can't assume that the catalog came from track $11. We do this by reading the first catalog sector into the buffer, scanning up the pointer table and moving the indicated entries into the catalog buffer, and then writing the buffer to the same disk sector it came from. We then get the track and sector pointers (which haven't been changed) from the buffer and use them to read the next sector. This whole process ends when we run out of entries in the pointer table. Limitations and Additional Features There are a few points that need more work: Disk error handling. The program just prints "I/O ERROR" and stops. It needs a real error handler. This program will handle catalogs with up to 127 entries. A standard disk has no more than 105, but some catalogs are modified to have more. I plan to add several more commands to Catalog Arranger: Alphabetic sort. This would be useful too, maybe just from the cursor to the end of the catalog. That would keep the utilities in place at the beginning. Sort by file type. Delete and undelete files. Move deleted files to the end. Rename files by editing the file name in the cursor. That would be a lot easier than typing entire file names twice, as RENAME requires. Display and allow changing the values of SLOT and DRIVE. Display the value of ACTIVE.ELEMENT and NUMBER.OF.ENTRIES, and maybe free sectors on the bottom line. Write the catalog out to the disk as a text file. Adding many of these features would also require reworking the command structure (which wouldn't hurt anyway!) Here is a summary of the commands: B -- Move the cursor to the beginning of the catalog. E -- Move the cursor to the end of the catalog. R -- Read the catalog from the disk. W -- Write the catalog to the disk. --> -- Move the cursor down one item. <-- -- Move the cursor up one item. RETURN -- Toggle the moving flag on or off. ESC -- Exit the program. 1000 *SAVE S.CATALOG ARRANGER 1010 *-------------------------------- 1020 .OR $803 1030 .TF CATALOG ARRANGER 1040 *-------------------------------- 1050 POINTER .EQ 0 1060 * 1070 MON.CV .EQ $25 1080 PREG .EQ $48 1090 * 1100 DOS.RESTART .EQ $3D0 1110 DOS.RWTS .EQ $3D9 1120 * 1130 CORNER .EQ $7D0 1140 * 1150 KEYBOARD .EQ $C000 1160 KEYSTROBE .EQ $C010 1170 * 1180 DOS.SIZEOUT .EQ $AE42 1190 DOS.PRNTERR .EQ $A702 1200 DOS.TYPTABL .EQ $B3A7 1210 IOB .EQ $B7E8 1220 IOB.SLOT .EQ $B7E9 1230 IOB.DRIVE .EQ $B7EA 1240 IOB.VOLUME .EQ $B7EB 1250 IOB.TRACK .EQ $B7EC 1260 IOB.SECTOR .EQ $B7ED 1270 IOB.BUFFER .EQ $B7F0,F1 1280 IOB.COMMAND .EQ $B7F4 1290 IOB.ERROR .EQ $B7F5 1300 IOB.OSLOT .EQ $B7F7 1310 IOB.ODRIVE .EQ $B7F8 1320 * 1330 * MONITOR CALLS 1340 * 1350 MON.VTAB .EQ $FC22 1360 MON.CLREOP .EQ $FC42 1370 MON.HOME .EQ $FC58 1380 MON.PRBYTE .EQ $FDDA 1390 MON.COUT1 .EQ $FDF0 1400 MON.SETINV .EQ $FE80 1410 MON.SETNORM .EQ $FE84 1420 * 1430 * SYMBOLIC CONSTANTS 1440 * 1450 ZERO .EQ 0 1460 READ .EQ 1 1470 WRITE .EQ 2 1480 LINE.COUNT .EQ 22 1490 ENTRY.LENGTH .EQ 35 1500 RETURN .EQ $8D 1510 SPACE .EQ $A0 1520 *-------------------------------- 1530 SETUP 1540 LDA IOB.OSLOT SET SLOT AND 1550 STA SLOT DRIVE TO WHERE 1560 LDA IOB.ODRIVE WE CAME FROM 1570 STA DRIVE 1580 1590 LDA #ZERO INITIALIZE 1600 STA VOLUME VARIABLES 1610 STA NUMBER.OF.ELEMENTS 1620 STA MOVING.FLAG 1630 LDA #$FF 1640 STA ACTIVE.ELEMENT 1650 1660 JSR BUILD.ARRAY.TABLE 1670 JSR READ.CATALOG 1680 JSR MON.HOME 1690 *-------------------------------- 1700 DISPLAY.AND.READ.KEY 1710 JSR DISPLAY.ARRAY 1720 1730 .1 LDA KEYBOARD 1740 BPL .1 1750 STA KEYSTROBE 1760 CMP #$95 --> 1770 BEQ HANDLE.RIGHT.ARROW 1780 CMP #$88 <-- 1790 BEQ HANDLE.LEFT.ARROW 1800 CMP #$9B ESC 1810 BEQ HANDLE.ESC 1820 CMP #RETURN 1830 BEQ HANDLE.RETURN 1840 CMP #$C2 B 1850 BEQ HANDLE.B BEGINNING 1860 CMP #$C5 E 1870 BEQ HANDLE.E END 1880 CMP #$D2 R 1890 BEQ HANDLE.R READ CATALOG 1900 CMP #$D7 W 1910 BEQ HANDLE.W WRITE CATALOG 1920 JMP .1 NONE OF THE ABOVE 1930 *-------------------------------- 1940 HANDLE.RIGHT.ARROW 1950 * MOVE UP ONE ELEMENT 1960 JSR CHECK.FOR.END.OF.ARRAY 1970 BCS .2 DO NOTHING IF ALREADY AT END 1980 BIT MOVING.FLAG SKIP SWAP IF 1990 BPL .1 NOT MOVING 2000 JSR MOVE.ELEMENT.UP 2010 .1 INC ACTIVE.ELEMENT FOLLOW IT UP 2020 .2 JMP DISPLAY.AND.READ.KEY 2030 *-------------------------------- 2040 HANDLE.LEFT.ARROW 2050 * MOVE DOWN ONE ELEMENT 2060 JSR CHECK.FOR.BEGINNING.OF.ARRAY 2070 BCS .2 IF AT BEGINNING, DO NOTHING 2080 BIT MOVING.FLAG IF NOT MOVING, 2090 BPL .1 SKIP SWAP 2100 JSR MOVE.ELEMENT.DOWN 2110 .1 DEC ACTIVE.ELEMENT 2120 .2 JMP DISPLAY.AND.READ.KEY 2130 *-------------------------------- 2140 HANDLE.B 2150 * MOVE CURSOR TO BEGINNING OF ARRAY 2160 .1 JSR CHECK.FOR.BEGINNING.OF.ARRAY 2170 BCS .3 DO NOTHING IF AT BEGINNING 2180 BIT MOVING.FLAG 2190 BPL .2 2200 JSR MOVE.ELEMENT.DOWN 2210 .2 DEC ACTIVE.ELEMENT 2220 BPL .1 2230 .3 JMP DISPLAY.AND.READ.KEY 2240 *-------------------------------- 2250 HANDLE.E 2260 * MOVE CURSOR TO END OF ARRAY 2270 .1 JSR CHECK.FOR.END.OF.ARRAY 2280 BCS .3 2290 BIT MOVING.FLAG 2300 BPL .2 2310 JSR MOVE.ELEMENT.UP 2320 .2 INC ACTIVE.ELEMENT 2330 BPL .1 ...ALWAYS 2340 .3 JMP DISPLAY.AND.READ.KEY 2350 *-------------------------------- 2360 HANDLE.W 2370 * WRITE CATALOG TO DISK 2380 JSR WRITE.CATALOG 2390 JMP DISPLAY.AND.READ.KEY 2400 *-------------------------------- 2410 HANDLE.RETURN 2420 * TOGGLE MOVING FLAG 2430 * =FF IF MOVING 2440 * =0 IF NOT 2450 LDA MOVING.FLAG 2460 EOR #$FF 2470 STA MOVING.FLAG 2480 JMP DISPLAY.AND.READ.KEY 2490 *-------------------------------- 2500 HANDLE.ESC 2510 * EXIT PROGRAM 2520 JMP DOS.RESTART 2530 *-------------------------------- 2540 HANDLE.R 2550 * READ NEW CATALOG 2560 JMP SETUP RESTART PROGRAM 2570 *-------------------------------- 2580 READ.CATALOG 2590 JSR READ.VTOC 2600 JSR POINT.TO.FIRST.CATALOG.SECTOR 2610 .1 JSR READ.CATALOG.SECTOR 2620 BCS .4 .CS. IF END OF CHAIN 2630 2640 * MOVE CATALOG SECTOR INTO ARRAY 2650 * X STEPS THROUGH BUFFER, $B-$FF 2660 * Y STEPS THROUGH ENTRY, 0-$23 2670 LDX #$B 2680 .2 LDA CATALOG.BUFFER,X 2690 BEQ .4 END OF CATALOG? 2700 INC ACTIVE.ELEMENT NO, WE HAVE 2710 INC NUMBER.OF.ELEMENTS A NEW ENTRY 2720 LDA ACTIVE.ELEMENT 2730 JSR POINT.TO.A SET POINTER 2740 LDY #ZERO 2750 .3 LDA CATALOG.BUFFER,X 2760 STA (POINTER),Y 2770 INX 2780 BEQ .1 END OF BUFFER? 2790 * IF SO, READ NEW SECTOR 2800 INY 2810 CPY #ENTRY.LENGTH END OF ENTRY? 2820 BCC .3 NO, KEEP GOING 2830 BCS .2 YES, GET NEXT ONE 2840 2850 .4 LDA ACTIVE.ELEMENT 2860 CLC GO ONE PAST 2870 ADC #1 LAST ELEMENT 2880 ASL AND STORE 2890 TAY TWO ZEROES 2900 LDA #ZERO 2910 STA ARRAY.TABLE,Y 2920 STA ARRAY.TABLE+1,Y 2930 STA ACTIVE.ELEMENT 2940 RTS 2950 *-------------------------------- 2960 READ.VTOC 2970 LDA #ZERO 2980 STA SECTOR 2990 LDA #$11 3000 STA TRACK 3010 LDA #VTOC.BUFFER 3020 STA BUFFER 3030 LDA /VTOC.BUFFER 3040 STA BUFFER+1 3050 LDA #READ 3060 STA COMMAND 3070 JMP RWTS.CALLER 3080 *-------------------------------- 3090 READ.CATALOG.SECTOR 3100 LDA CATALOG.BUFFER+1 GET NEXT TRACK 3110 BEQ .1 END OF CATALOG CHAIN? 3120 STA TRACK 3130 LDA CATALOG.BUFFER+2 GET NEXT SECTOR 3140 STA SECTOR 3150 LDA #CATALOG.BUFFER 3160 STA BUFFER 3170 LDA /CATALOG.BUFFER 3180 STA BUFFER+1 3190 LDA #READ 3200 STA COMMAND 3210 JSR RWTS.CALLER 3220 CLC 3230 RTS 3240 3250 * SET CARRY TO SHOW END-OF-CHAIN 3260 .1 SEC 3270 RTS 3280 *-------------------------------- 3290 WRITE.CATALOG 3300 LDA #$FF 3310 STA ACTIVE.ELEMENT 3320 JSR POINT.TO.FIRST.CATALOG.SECTOR 3330 .1 JSR READ.CATALOG.SECTOR 3340 LDX #$B 3350 .2 INC ACTIVE.ELEMENT 3360 LDA ACTIVE.ELEMENT 3370 JSR POINT.TO.A 3380 BCS .5 .CS. IF AT END OF TABLE 3390 LDY #ZERO 3400 .3 LDA (POINTER),Y 3410 STA CATALOG.BUFFER,X 3420 INX 3430 BEQ .4 END OF BUFFER? 3440 INY 3450 CPY #ENTRY.LENGTH END OF ENTRY? 3460 BCC .3 NO, KEEP GOING 3470 BCS .2 YES, GET NEXT ONE 3480 3490 .4 JSR WRITE.CATALOG.SECTOR 3500 JMP .1 AND READ THE NEXT SECTOR 3510 3520 * FILL THE REST OF THE BUFFER WITH ZEROES 3530 .5 LDA #ZERO 3540 .6 STA CATALOG.BUFFER,X 3550 INX 3560 BNE .6 3570 3580 JSR WRITE.CATALOG.SECTOR 3590 LDA #ZERO 3600 STA ACTIVE.ELEMENT 3610 JMP DISPLAY.AND.READ.KEY 3620 *-------------------------------- 3630 WRITE.CATALOG.SECTOR 3640 LDA #WRITE WRITE THE SECTOR 3650 STA COMMAND BACK JUST WHERE 3660 JMP RWTS.CALLER IT CAME FROM 3670 *-------------------------------- 3680 POINT.TO.FIRST.CATALOG.SECTOR 3690 * GET THE FIRST TRACK AND SECTOR FROM THE VTOC 3700 LDA VTOC.BUFFER+1 3710 STA CATALOG.BUFFER+1 3720 LDA VTOC.BUFFER+2 3730 STA CATALOG.BUFFER+2 3740 RTS 3750 *-------------------------------- 3760 DISPLAY.ARRAY 3770 LDA #ZERO START AT 3780 STA MON.CV TOP OF 3790 JSR MON.VTAB SCREEN 3800 LDA ACTIVE.ELEMENT 3810 SEC 3820 SBC #LINE.COUNT/2 3830 BPL .1 IF RESULT IS +, USE IT 3840 LDA #ZERO OTHERWISE, USE ZERO 3850 .1 TAX X KEEPS TRACK OF 3860 .2 TXA WHERE WE ARE 3870 CMP ACTIVE.ELEMENT 3880 BNE .3 3890 PHA 3900 JSR MON.SETINV INVERT ACTIVE ELEMENT 3910 PLA 3920 .3 JSR POINT.TO.A SET POINTER 3930 BCS .5 .CS. IF AT END OF TABLE 3940 JSR INTERPRET.ENTRY WRITE A LINE 3950 LDA #RETURN 3960 JSR MON.COUT1 3970 JSR MON.SETNORM RESTORE NORMAL 3980 INX 3990 LDA MON.CV 4000 CMP #LINE.COUNT END OF SCREEN? 4010 BCC .2 IF NOT, DO ANOTHER LINE 4020 .5 JSR MON.CLREOP CLEAR TO END OF PAGE 4030 JSR MON.SETNORM JUST IN CASE 4040 BIT MOVING.FLAG 4050 BPL .6 4060 JSR SHOW.MOVING.FLAG IF MOVING 4070 .6 RTS 4080 *-------------------------------- 4090 INTERPRET.ENTRY 4100 LDY #ZERO 4110 LDA (POINTER),Y DELETED? 4120 BPL .1 MINUS IF YES 4130 LDA #$AD - 4140 BMI .3 ...ALWAYS 4150 .1 LDY #2 4160 LDA (POINTER),Y LOCKED? 4170 BPL .2 MINUS IF YES 4180 LDA #$AA * 4190 BMI .3 ...ALWAYS 4200 .2 LDA #SPACE NEITHER DELETED NOR LOCKED 4210 .3 JSR MON.COUT1 4220 LDY #2 4230 LDA (POINTER),Y GET FILE TYPE 4240 AND #$7F MAKE POINTER 4250 LDY #7 INTO DOS'S 4260 ASL TYPE TABLE 4270 .4 ASL (ROUTINE BORROWED 4280 BCS .5 FROM DOS, $ADE8-ADF9) 4290 DEY 4300 BNE .4 4310 .5 LDA DOS.TYPTABL,Y 4320 JSR MON.COUT1 DISPLAY TYPE 4330 LDA #SPACE 4340 JSR MON.COUT1 4350 LDY #$21 4360 LDA (POINTER),Y SET UP FOR 4370 STA $44 ROUTINE TO 4380 INY DISPLAY FILE 4390 LDA (POINTER),Y SIZE 4400 STA $45 4410 JSR DOS.SIZEOUT DO IT 4420 LDA #SPACE 4430 JSR MON.COUT1 4440 LDY #3 4450 .6 LDA (POINTER),Y GET A CHARACTER 4451 CMP #SPACE CONTROL? 4452 BCS .7 NO, GO ON 4453 AND #$7F YES, MAKE IT INVERSE 4460 .7 JSR MON.COUT1 DISPLAY IT 4470 INY 4480 CPY #33 DONE WITH FILE NAME? 4490 BCC .6 4500 RTS 4510 *-------------------------------- 4520 SHOW.MOVING.FLAG 4530 LDY #5 PUT INVERSE 4540 .1 LDA QMOVING,Y "MOVING" AT 4550 STA CORNER,Y BOTTOM OF 4560 DEY SCREEN 4570 BPL .1 4580 RTS 4590 *-------------------------------- 4600 RWTS.CALLER 4610 LDA SLOT TRANSFER 4620 STA IOB.SLOT VALUES 4630 LDA DRIVE INTO 4640 STA IOB.DRIVE IOB 4650 LDA VOLUME 4660 STA IOB.VOLUME 4670 LDA TRACK 4680 STA IOB.TRACK 4690 LDA SECTOR 4700 STA IOB.SECTOR 4710 LDA COMMAND 4720 STA IOB.COMMAND 4730 LDA BUFFER 4740 STA IOB.BUFFER 4750 LDA BUFFER+1 4760 STA IOB.BUFFER+1 4770 LDA #$00 4780 STA IOB.ERROR 4790 * 4800 LDY #IOB LOAD IOB 4810 LDA /IOB ADDRESS 4820 JSR DOS.RWTS CALL RWTS 4830 LDA #$00 4840 STA PREG SOOTHE MONITOR 4850 BCS ERROR.HANDLER 4860 RTS 4870 *-------------------------------- 4880 ERROR.HANDLER 4890 LDA #$87 BELL 4900 JSR MON.COUT1 RING 4910 JSR MON.COUT1 ING 4920 JSR MON.COUT1 ING 4930 LDA #23 4940 STA MON.CV USE LINE BELOW DISPLAY 4950 JSR MON.VTAB 4960 LDX #8 4970 JSR DOS.PRNTERR DISPLAY "I/O ERROR" 4980 JMP DOS.RESTART EXIT PROGRAM 4990 *-------------------------------- 5000 BUILD.ARRAY.TABLE 5010 LDA #CATALOG.ARRAY SET FIRST ENTRY 5020 STA ARRAY.TABLE TO POINT TO 5030 LDA /CATALOG.ARRAY START OF 5040 STA ARRAY.TABLE+1 ARRAY 5050 LDX #2 5060 .1 LDA ARRAY.TABLE-2,X MAKE EACH 5070 CLC SUCCESSIVE 5080 ADC #ENTRY.LENGTH ENTRY $23 5090 STA ARRAY.TABLE,X LARGER THAN 5100 LDA ARRAY.TABLE-1,X THE LAST 5110 ADC #ZERO 5120 STA ARRAY.TABLE+1,X 5130 INX 5140 INX 5150 CPX #$FE 127 ENTRIES YET? 5160 BNE .1 5170 LDA #ZERO END TABLE 5180 STA ARRAY.TABLE,X WITH TWO 5190 STA ARRAY.TABLE+1,X ZEROES 5200 RTS 5210 *-------------------------------- 5220 POINT.TO.A 5230 ASL MAKE (A) INTO INDEX 5240 TAY 5250 LDA ARRAY.TABLE,Y CHECK FOR TWO 5260 ORA ARRAY.TABLE+1,Y CONSECUTIVE 5270 BEQ .1 ZERO BYTES 5280 LDA ARRAY.TABLE,Y 5290 STA POINTER PUT TABLE ENTRY 5300 LDA ARRAY.TABLE+1,Y INTO POINTER 5310 STA POINTER+1 5320 CLC 5330 RTS 5340 5350 .1 SEC END OF TABLE 5360 RTS 5370 *-------------------------------- 5380 CHECK.FOR.END.OF.ARRAY 5390 * RETURNS CARRY SET IF AT END 5400 * " " CLEAR IF NOT 5410 LDA ACTIVE.ELEMENT 5420 CLC 5430 ADC #1 5440 CMP NUMBER.OF.ELEMENTS 5450 RTS 5460 *-------------------------------- 5470 CHECK.FOR.BEGINNING.OF.ARRAY 5480 LDA ACTIVE.ELEMENT 5490 BNE .1 5500 SEC ACTIVE = 0, WE ARE AT BEGINNING 5510 RTS 5520 5530 .1 CLC NONZERO, WE'RE OKAY 5540 RTS 5550 *-------------------------------- 5560 MOVE.ELEMENT.UP 5570 LDA ACTIVE.ELEMENT 5580 ASL MAKE INDEX INTO TABLE 5590 TAX 5600 LDY #2 DO THIS TWICE, FIRST LO, THEN HI 5610 .1 LDA ARRAY.TABLE,X 5620 PHA 5630 LDA ARRAY.TABLE+2,X 5640 STA ARRAY.TABLE,X 5650 PLA 5660 STA ARRAY.TABLE+2,X 5670 INX NOW DO HIGH BYTES 5680 DEY 5690 BNE .1 DONE? 5700 RTS 5710 *-------------------------------- 5720 MOVE.ELEMENT.DOWN 5730 LDA ACTIVE.ELEMENT 5740 ASL 5750 TAX 5760 LDY #2 5770 .1 LDA ARRAY.TABLE,X 5780 PHA 5790 LDA ARRAY.TABLE-2,X 5800 STA ARRAY.TABLE,X 5810 PLA 5820 STA ARRAY.TABLE-2,X 5830 INX 5840 DEY 5850 BNE .1 5860 RTS 5870 *-------------------------------- 5880 QMOVING 5890 * INVERSE "MOVING" 5900 .HS 0D0F16090E07 5910 *-------------------------------- 5920 SLOT .BS 1 (USUALLY 6) 5930 DRIVE .BS 1 (USUALLY 1) 5940 VOLUME .BS 1 (0 = ANY) 5950 TRACK .BS 1 (USUALLY $11) 5960 SECTOR .BS 1 (0 TO F) 5970 BUFFER .BS 2 (VARIES) 5980 COMMAND .BS 1 (1 OR 2) 5990 6000 NUMBER.OF.ELEMENTS .BS 1 (1 TO N) 6010 ACTIVE.ELEMENT .BS 1 (0 TO N-1) 6020 MOVING.FLAG .BS 1 (0 OR FF) 6030 *-------------------------------- 6040 END.OF.PROGRAM 6050 6060 VTOC.BUFFER .EQ END.OF.PROGRAM 6070 CATALOG.BUFFER .EQ END.OF.PROGRAM+$100 6080 ARRAY.TABLE .EQ END.OF.PROGRAM+$200 6090 CATALOG.ARRAY .EQ END.OF.PROGRAM+$300 So You Never Need Macros!..................Bob Sander-Cederlof -------------------------------------------------------------- I have said it many times myself, "I don't need macros!" But now that I have them, I seem to find more and more uses for them. Not the traditional uses, to generate common sequences of opcodes. I am using them to build tables of data, rather than typing in line after line of very similar stuff. I have been working some more on the Prime Number Generator program. You may remember the series: first the articles in BYTE Magazine, then my faster version in an early Apple Assembly Line, then Charles Putney's version at double my speed. Now Tony Brightwell has cut Charlie's time nearly in half. (His program will probably appear next month.) Anyway, I have done some more investigation. One approach required a precomputed table of the squares of the odd numbers from 1 to 127. An easy way to enter this table might be: .DA 1*1,3*3,5*5,7*7,9*9 .DA 11*11,13*13,15*15,17*17 et cetera I had type about that much when I said, "There has to be an easier way." I made up the following macro definition: .MA SQ :0 .EQ ]1 :1 .EQ ]1+2 :2 .EQ ]1+4 :3 .EQ ]1+6 :4 .EQ ]1+8 :5 .EQ ]1+10 :6 .EQ ]1+12 :7 .EQ ]1+14 .DA :0*:0,:1*:1,:2*:2,:3*:3 .DA :4*:4,:5*:5,:6*:6,:7*:7 .DO ]2<8 >SQ ]1+16,]2+1 .FIN .EM Then the single line of code 2200 >SQ 1,1 generated all 64 squares for me. How does it work? Good question.... The eight .EQ lines create 8 private labels with the values of 8 consecutive odd numbers starting with whatever the first parameter from the call line happens to be. Line 2200 has the first parameter "1", so the private labels will have values of 1, 3, 5, 7, 9, 11, 13, and 15 respectively. The two .DA lines generate the squares of these 8 values. The next three lines are the tricky part. If the second parameter has a value less than 8 then the line between .DO and .FIN is assembled. It is a nested call on the SQ macro. Only this time the first parameter is 16 greater than it was, and the second parameter is one greater. After going through this nesting process 7 times, we will have generated 8 sets of 8 values each. When the second parameter has worked its way up to 8, the nested calls will exit in turn, and the table is finished. If you have the macro expansion listing option on during assembly, the expanded form takes 2 1/2 pages Converting ToolKit Source to S-C...........Bob Sander-Cederlof -------------------------------------------------------------- I had the source code for FIG-FORTH on the Apple, entered by some members of the Dallas Apple Corps. For some reason they decided to use the DOS ToolKit Assembler when they typed in all those lines. Naturally, I had a strong desire to convert the source files to the format of my S-C Macro Assembler. The first step, and one of the easiest in this case, is to figure out how to read the ToolKit source files into S-C. ToolKit source files are standard DOS text files (type "T"). There are no line numbers. S-C allows such files to be read in by typing the following commands: NEW AUTO <<<<SSS" to indicate the low byte. S-C uses "LDA /SSS" for the high byte, and "LDA #SSS" for the low byte. I fixed them with: REP " <#" /"A REP " >#" #"A Now about those snaky columns.... I wanted to somehow put a tab before each opcode field, and before each comment field. I thought, "Why not just use the replace command to put in a control-I?": REP / EQU /^I.EQ /A (where ^I means I typed control-I) et cetera My first problem was that typing controll-I when entering the REPLACE command made a tab. I overcame that by typing the sequence "control-O control-I". Control-O makes the next character become part of the input line regardless of its normal meaning. That worked, but.... My second problem was that getting a control-I into the source program did not make it a tab. Somehow the control-I had to be "executed". So I wrote the converted program on a text file, this time with line numbers, and then EXECed it back in. TEXT# filename EXEC filename That "executed" the control-I's, and I had tabs. But.... My third problem was that I wanted to save all the REPLACE commands as an EXEC file, so that I did not have to manually retype them for every file to be converted. When I EXECed the REPLACE command file, the control-I's were executed immediately! I had to change my replace commands to include both a control-O and a control-I, so that the control-I in the REPLACE command would be read in from the EXEC file but not executed until it was later EXECed from the temporary source text file. Still with me? If not, keep reading anyway, because I will show you what I mean. Using S-C, I entered the following "program": 1000 "TOOLKIT CONVERTER 1010 " 1020 " 1030 "COMMENTS 1040 REP/ SKP 1/*/A 1050 REP/ SKP 2/^O^[L/A 1060 REP/ ;/ ^O^I/A 1070 REP/;/*/A 1080 "DIRECTIVES 1090 REP/ EQU /^O^I.EQ /A 1100 REP/ DW /^O^I.DA /A 1110 REP/ ORG /^O^I.OR /A 1120 REP/ DS /^O^I.BS /A 1130 REP/ DCI /^O^I.AT /A 1140 REP/ ASC /^O^I.AS /A 1150 REP/ DFB /^O^I.DA #/A 1160 REP/ CHN /*** CHN /A 1170 "OPCODE TABS 1180 REP/ ADC /^O^IADC /A 1190 REP/ AND /^O^IAND /A 1200 REP/ ASL/^O^IASL/A 1210 REP/ BIT /^O^IBIT /A 1220 REP/ CMP /^O^ICMP /A 1230 REP/ CPX /^O^ICPX /A 1240 REP/ CPY /^O^ICPY /A 1250 REP/ DEC /^O^IDEC /A 1260 REP/ EOR /^O^IEOR /A 1270 REP/ INC /^O^IINC /A 1280 REP/ LDA /^O^ILDA /A 1290 REP/ LDX /^O^ILDX /A 1300 REP/ LDY /^O^ILDY /A 1310 REP/ LSR/^O^ILSR/A 1320 REP/ ORA /^O^IORA /A 1330 REP/ ROL/^O^IROL/A 1340 REP/ ROR/^O^IROR/A 1350 REP/ SBC /^O^ISBC /A 1360 REP/ STA /^O^ISTA /A 1370 REP/ STX /^O^ISTX /A 1380 REP/ STY /^O^ISTY /A 1390 REP/ BPL /^O^IBPL /A 1400 REP/ BMI /^O^IBMI /A 1410 REP/ BEQ /^O^IBEQ /A 1420 REP/ BNE /^O^IBNE /A 1430 REP/ BVS /^O^IBVS /A 1440 REP/ BVC /^O^IBVC /A 1450 REP/ BCC /^O^IBCC /A 1460 REP/ BCS /^O^IBCS /A 1470 REP/ JMP /^O^IJMP /A 1480 REP/ JSR /^O^IJSR /A 1490 REP/ BRK/^O^IBRK/A 1500 REP/ CLC/^O^ICLC/A 1510 REP/ CLD/^O^ICLD/A 1520 REP/ CLV/^O^ICLV/A 1530 REP/ DEX/^O^IDEX/A 1540 REP/ DEY/^O^IDEY/A 1550 REP/ INX/^O^IINX/A 1560 REP/ INY/^O^IINY/A 1570 REP/ NOP/^O^INOP/A 1580 REP/ PHA/^O^IPHA/A 1590 REP/ PLA/^O^IPLA/A 1600 REP/ PLP/^O^IPLP/A 1610 REP/ RTS/^O^IRTS/A 1620 REP/ SEC/^O^ISEC/A 1630 REP/ TAX/^O^ITAX/A 1640 REP/ TAY/^O^ITAY/A 1650 REP/ TSX/^O^ITSX/A 1660 REP/ TXA/^O^ITXA/A 1670 REP/ TXS/^O^ITXS/A 1680 REP/ TYA/^O^ITYA/A 1690 "ADDRESS MODES 1700 REP" #>" #"A 1710 REP" #<" /"A 1720 "WRITE ON TEMP FILE 1730 TEXT#F In the listing above, I have used "^O" to mean "control-O"; "^I" to mean "control-I"; and "^[" to mean "ESCAPE key". In order to get "control-O control-I" in a line, I had to type "control-O control-O control-O control-I". Lines 1000-1030, 1080,1130, 1170,1690, and 1720 begin with a quotation mark. These are comment lines to the S-C input routine; they print on the screen when they are read from the EXEC file, but are otherwise ignored. I saved the file as is using "SAVE TOOLKIT CONVERTER", in case I might want to modify it again. And I did, again and again and again. Then I wrote it on a text file without line numbers using "TEXT TKC". Here is the sequence of steps I went through for each source file: NEW AUTO 1000 * filename <<<<