Apple Assembly Line
Volume 3 -- Issue 11 August 1983

In This Issue...

Mailing AAL

Let's review how AAL is mailed, when you should expect to receive it, and what to do about it when you don't. Most of you get your newletter by Bulk Mail, which is a little erratic. You should receive your issue around the third week of each month, but don't start worrying until the end of the month. If you haven't received an issue by the end of the month, call or write and we'll send a replacement. The Post Office does not forward Bulk Mail, so make certain to tell us if you move. Those of you who have First Class Mail subscriptions should receive your issue around the tenth of the month, and certainly before the twentieth.

The number in the upper right corner of your mailing label is the expiration date of your subscription. If that number is 8308, you're holding your last issue and better renew now. We send out postcards when your subscription is about to expire, and when it has expired. All you have to do is send us a check, or phone with a charge card number, and we'll keep your AAL coming.


Using Auxiliary Memory in the //e David C. Johnson
Ridgefield, CT

When I bought my Apple //e (3 days after they became available!), I also got the Extended 80-Column Text Card. I wanted it both to have 80 column text capability and a full complement of Apple Computer Inc. supported memory. However, Apple only supplied two small subroutines in ROM and incomplete (but otherwise excellent) documentation in their manuals, in "support" of the auxiliary memory.

I say "incomplete" because two I/O locations that I used in my program are not mentioned (in English anyway) anywhere in the manuals except in the listings of the 80-column firmware. The two I/O locations are $C011 & $C012 which I call READ.BSR.BANK & READ.BSR.RAM.READ. Apple evidently intends to let software developers determine how the auxiliary memory is to be used.

Well here goes: my program is called "USE.AUXMEM". This program allows you to access the "other" 64K in a manner most Apple users should already be familiar with: monitor commands.

The simplest way to see what I mean is to type in & assemble the program (not so simple), type :"MGO G", :"PR#3" and then :"$^Y" (that is control-Y). You will get a bell and the monitor's prompt. Any monitor commands you type now will "use" the auxiliary memory. Try these now:

       *3D0:55
       *3D0     (double nickels, right?)
       *^Y      (back to SCASM!)
       :$3D0    (a $4C!)

You should note that control-Y while using the auxiliary memory returns to main memory with everything as it was. Now try these:

       :$^Y 3D0
       *3D0 ^Y

After the second control-Y returned to main memory, SCASM finished the first command line!

The reason I had you type :"PR#3" before is quite simple: things don't all work right without the 80 column firmware active; specifically, right-arrow & escape functions. You can also type "escape 4" if you don't want 80 columns.

But wait a minute, if you read the 80-column firmware listing (carefully), you know that it does NOT work with the auxiliary memory enabled (as doesn't the regular 40-column firmware), so how is this all working? Well, the I/O hooks in the auxiliary memory zero page point to routines in USE.AUXMEM which switch to main memory, perform the I/O, switch back to auxiliary memory, and return to the monitor. The monitor executes its commands between I/O calls while auxiliary memory is enabled. These switchings also change the bank switched memory state.

The USE.AUXMEM program has two other control-Y commands. They implement the crossbank subroutines AUXMOVE & XFER (supplied in ROM) as monitor commands. See the comments at the top of the source listing for their syntax.

About Some //e Monitor Bugs...

One routine, USE.AUXMEM.CONTROL.Y.HANDLER, deserves a special note. It compensates for a bug in the Apple //e version of the monitor: when parsing a control-Y command the ASCII string "Bryan" at $FEC5 is executed as instructions prior to JMPing to USRADR ($03F8). This bug has a long history.

In the original Apple monitor the CHRSRCH loop ($FF78 - $FF81) scans the CHRTBL ($FFCC - $FFE2) from end to beginning, which matches the $B2 at $FFCD causing TOSUB ($FFBE - $FFCB) to load the $C9 at $FFE4 and RTS to USR ($FECA) which is a JMP USRADR ($03F8).

Things started to go astray when the autostart ROM was created, and the Apple II Plus. To make room for new features, (like printing "APPLE ][" at the top of the screen on power up, and like the escape-IJKM cursor motion), the TRACE and STEP commands were removed. To disable the entries for Trace and Step in CHRTBL, the bytes for "T" and "S" were changed: ($FFCF:B2 & $FFD2:B2, also $FFE9:C4). Locations $FEC5 - $FEC9, immediately prior to USR, were changed to NOPs.

Unfortunately, someone forgot that CHRTBL is searched from end to beginning, causing a control-Y command to be matched with the $B2 at $FFD2, corresponding to the branch address in SUBTBL at $FFE9. So when you type a control-Y command the monitor branches to $FFC5 and executes the 5 NOPs. If $FFE9 had been changed to $C9 instead of $C4, everything would have still been fine.

Executing 5 NOPs is not a bad bug. But when the Apple //e monitor was created those 5 NOPs were replaced by the string "Bryan". In hex it is C2 F2 F9 E1 EE. The 6502 instruction set does not include a definition for $C2, but after a little investigation, or after reading Bob Sander-Cederlof's article in AAL March 1981, you find out that $C2 acts like a two-byte NOP. The "r" is skipped over. The "yan", however, is a SBC $EEE1,Y instruction.

The USE.AUXMEM.CONTROL.Y.HANDLER uses the passed contents of the A & X registers to decide which of the three control-Y commands you've typed. The SBC $EEE1,Y changes the A register so its contents must be reconstructed. The reconstruction is further complicated by the fact that the monitor leaves the carry flag set when it RTS's to $FEC5, while the S-C Assembler and Mini-Assembler leave the carry flag clear.

To restore the A register to its proper value you must set the carry to the complement of the value that it was set to prior to the SBC $EEE1,Y then execute ADC $EEE1,Y.

The Apple //e 80 column firmware also contains a bug. Because of the $11 at $C92A, the key sequence "ESCape ^L" causes a RTS to $4CCE. Location $C92A should contain a $10. This bug can be used to advantage if you feel like adding a secret command to your own software. Just be certain you have the code for your command starting at $4CCE, and that you are running in 80-column mode. Then whenever you type control-L in the escape mode (cursor is an inverse plus) your code will be executed.

I hope all of you enjoy using your auxiliary memory as much as I do.

Last Minute Note: David just called to report yet another oddity in the //e ROMs. In 40-column ESCape mode the (, 5, *, and + keys duplicate the arrow keys. That is, "ESC 5" moves the cursor right one space, just like ESC right arrow. This is a little bit weird, but it doesn't seem to hurt anything. The effect is caused by an unnecessary AND #$DF instruction at $C26E.

  1000 *SAVE JOHNSON'S USE AUXMEM
  1010 *--------------------------------
  1020 * SWITCH.MIND Command:  ^Y
  1030 *
  1040 *   When in main bank, enters monitor in
  1050 *   auxmem BSR (hooks I/O through main
  1060 *   and brings USE.AUXMEM to auxmem too)
  1070 *   When in aux bank, returns to main bank
  1080 *   Best used w/80 column firmware active
  1090 *--------------------------------
  1100 * USE.AUXMOVE Command:  DEST<SOURCE.END^Y{CARRY}
  1110 *
  1120 *   DEST      = Destination in one bank
  1130 *   SOURCE    = Start in other bank
  1140 *   END       = End in other bank
  1150 *   CARRY     = Direction of move
  1160 *               (1 = Main Ram-->Card Ram)
  1170 *               (0 = Card Ram-->Main Ram)
  1180 *   DEST, SOURCE, & END must be: >=$0200 & <=$BFFF
  1190 *--------------------------------
  1200 * USE.XFER Command:  ADDRESS^Y{CARRY}{OVERFLOW}
  1210 *
  1220 *   ADDRESS   = Transfer address
  1230 *   CARRY     = Desired 48K Bank ($0200 - $BFFF)
  1240 *               (1 = Use 48K in Card Ram)
  1250 *               (0 = Use 48K in Main Ram)
  1260 *   OVERFLOW  = Desired ZP/STK/BSR
  1270 *               (1 = Use ZP/STK/BSR in Card Ram)
  1280 *               (0 = Use ZP/STK/BSR in Main Ram)
  1290 *   If using USE.XFER from auxmem, routine in main mem
  1300 *   MUST LDX BANK.SP.SAVE, TXS if it uses the stack at all
  1310 *--------------------------------
  1320 MON.BASL         .EQ $28,$29
  1330 MON.YSAV         .EQ $34
  1340 MON.CSWL         .EQ $36,$37
  1350 MON.KSWL         .EQ $38,$39
  1360 MON.A1           .EQ $3C,$3D   Source,Address
  1370 MON.A2           .EQ $3E,$3F   End
  1380 MON.A4           .EQ $42,$43   Dest
  1390 MON.STATUS       .EQ $48
  1400 *--------------------------------
  1410 IN                       .EQ $0200 - $02FF
  1420 BANK.X.SAVE              .EQ $03CC
  1430 BANK.BSR.BANK.SAVE       .EQ $03CD
  1440 BANK.BSR.RAM.READ.SAVE   .EQ $03CE
  1450 BANK.SP.SAVE             .EQ $03CF
  1460 TRANSFER                 .EQ $03ED,$03EE
  1470 MON.BRKV                 .EQ $03F0,$03F1
  1480 USRADR                   .EQ $03F8 - $03FA
  1490 NMI                      .EQ $03FB - $03FD
  1500 MON.IRQLOC               .EQ $03FE,$03FF
  1510 *--------------------------------
  1520 READ.MAIN.RAM            .EQ $C002
  1530 READ.AUX.RAM             .EQ $C003
  1540 WRITE.MAIN.RAM           .EQ $C004
  1550 WRITE.AUX.RAM            .EQ $C005
  1560 USE.MAIN.ZP.STK.BSR      .EQ $C008
  1570 USE.AUX.ZP.STK.BSR       .EQ $C009
  1580 READ.BSR.BANK            .EQ $C011
  1590 READ.BSR.RAM.READ        .EQ $C012
  1600 READ.RAM.READ.STATUS     .EQ $C013
  1610 READ.RAM.WRITE.STATUS    .EQ $C014
  1620 READ.ZP.STK.BSR.STATUS   .EQ $C016
  1630 BSR.2.RAM.READ.ONLY      .EQ $C080
  1640 BSR.2.ROM.READ.RAM.WRITE .EQ $C081
  1650 BSR.2.ROM.READ.ONLY      .EQ $C082
  1660 BSR.2.RAM.READ.RAM.WRITE .EQ $C083
  1670 BSR.1.RAM.READ.ONLY      .EQ $C088
  1680 BSR.1.ROM.READ.RAM.WRITE .EQ $C089
  1690 BSR.1.ROM.READ.ONLY      .EQ $C08A
  1700 BSR.1.RAM.READ.RAM.WRITE .EQ $C08B
  1710 *--------------------------------
  1720 AUXMOVE                  .EQ $C311
  1730 XFER                     .EQ $C314
  1740 MONITOR                  .EQ $F800 - $FFFF
  1750 MON.OLDBRK               .EQ $FA59
  1760 BEEP                     .EQ $FBDD
  1770 MON.RDKEY                .EQ $FD0C
  1780 MON.JSR.CLREOL           .EQ $FD8B - $FD8D
  1790 MON.COUT                 .EQ $FDED
  1800 MON                      .EQ $FF65
  1810 *--------------------------------
  1820            .OR $0803
  1830 USE.AUXMEM
  1840 G
  1850            JMP CONNECT.CONTROL.Y
  1860 JMP.TO.RETURN.TO.MAIN
  1870            JMP RETURN.TO.MAIN
  1880 JMP.TO.RETURN.TO.AUX
  1890            JMP RETURN.TO.AUX
  1900 JMP.TO.SAVE.BSR.STATE
  1910            JMP SAVE.BSR.STATE
  1920 JMP.TO.RESTORE.BSR.STATE
  1930            JMP RESTORE.BSR.STATE
  1940 *--------------------------------
  1950 CONNECT.CONTROL.Y
  1960            LDA /USE.AUXMEM.CONTROL.Y.HANDLER
  1970            STA USRADR+2
  1980            LDA #USE.AUXMEM.CONTROL.Y.HANDLER
  1990            STA USRADR+1
  2000            LDA #$4C      JMP
  2010            STA USRADR
  2020            RTS
  2030 *--------------------------------
  2040 USE.AUXMEM.CONTROL.Y.HANDLER
  2050 * Reconstruct monitor mode byte
  2060 * after "Bryan" messed with it
  2070 * ("Br" is NOPish)
  2080            PHA
  2090            LDA IN
  2100            CMP #"$"
  2110 * Branch w/Carry set causa S-C or Mini-Asm
  2120            BEQ .1
  2130            CLC
  2140 .1         PLA
  2150 * These lines are for you Bryan
  2160            .DA #'y'
  2170            .AS -'an'     Builds SBC $EEE1,Y
  2180 * Check for user specified address
  2190            CPX #$01
  2200            BNE SWITCH.MIND
  2210            TAY
  2220 * Lesser complex is USE.XFER
  2230            BEQ USE.XFER
  2240 * Most complex is USE.AUXMOVE
  2250 *--------------------------------
  2260 USE.AUXMOVE
  2270 * Fetch what should be a "0"
  2280 * or "1" to be AUXMOVE's carry
  2290            LDY MON.YSAV
  2300            LDA IN,Y
  2310 * Shift what we fetched to carry
  2320            LSR
  2330 * Save carry while comparing
  2340            PHP
  2350 * This is a "0" or "1" after a LSR
  2360            CMP #"0"/2
  2370            BNE INVALID.CARRY
  2380            INC MON.YSAV
  2390 * Recover Carry
  2400            PLP
  2410 CALL.AUXMOVE.WITH.CARRY
  2420            JSR AUXMOVE
  2430            RTS
  2440 *--------------------------------
  2450 USE.XFER
  2460 * Set XFER Transfer address
  2470 * from monitor parameter
  2480            LDA MON.A1,X
  2490            STA TRANSFER,X
  2500            DEX
  2510            BPL USE.XFER
  2520 * Fetch what should be a "0"
  2530 * or "1" to be XFER's carry
  2540            LDY MON.YSAV
  2550            LDA IN,Y
  2560 * Shift what we fetched to carry
  2570            LSR
  2580 * Save carry for a while
  2590            PHP
  2600 * This is a "0" or "1" after a LSR
  2610            CMP #"0"/2
  2620            BNE INVALID.CARRY
  2630            INC MON.YSAV
  2640 * Fetch what should be a "0"
  2650 * or a "1" to be XFER's overflow
  2660            INY
  2670            LDA IN,Y
  2680 * Shift what we fetched to carry
  2690            LSR
  2700 * Save this carry too, while we compare
  2710            PHP
  2720 * This is a "0" or "1" after a LSR
  2730            CMP #"0"/2
  2740            BNE INVALID.OVERFLOW
  2750            INC MON.YSAV
  2760 * Recovered carry is valid overflow
  2770            PLP
  2780 * Move it back to bit 0
  2790            ROL
  2800 * Recover carry
  2810            PLP
  2820 * Construct overflow
  2830            CLV
  2840            AND #%0000.0001
  2850            BEQ .1
  2860            BIT SEV
  2870 * Save BSR bank, BSR ram read, and SP
  2880 * for any calls or returns to main/auxmem
  2890 .1         JSR SAVE.BSR.STATE
  2900            TSX
  2910            STX BANK.SP.SAVE
  2920 JMP.XFER.WITH.CARRY.AND.OVERFLOW
  2930 * Routines in aux/main bank may jmp
  2940 * to RETURN.TO.MAIN/AUX when done
  2950 SEV        JMP XFER
  2960 *--------------------------------
  2970 INVALID.OVERFLOW
  2980            PLP
  2990 INVALID.CARRY
  3000            PLP
  3010 * Let's not process rest of line
  3020            LDY MON.YSAV
  3030            LDA #$8D
  3040            STA IN,Y
  3050            JMP BEEP
  3060 *--------------------------------
  3070 SWITCH.MIND
  3080 * Check in main or aux now
  3090            LDA READ.RAM.READ.STATUS
  3100            BPL ENTER.AUX.MON
  3110            JMP RETURN.TO.MAIN
  3120 ENTER.AUX.MON
  3130 * Move USE.AUXMEM to auxmem too
  3140            LDA #USE.AUXMEM
  3150            STA MON.A1
  3160            STA MON.A4
  3170            LDA /USE.AUXMEM
  3180            STA MON.A1+1
  3190            STA MON.A4+1
  3200            LDA #USE.AUXMEM.END
  3210            STA MON.A2
  3220            LDA /USE.AUXMEM.END
  3230            STA MON.A2+1
  3240            SEC
  3250            JSR AUXMOVE
  3260 * Save BSR bank, BSR ram read, and SP
  3270 * for calls and return to main mem
  3280            JSR SAVE.BSR.STATE
  3290            TSX
  3300            STX BANK.SP.SAVE
  3310 * Continue in auxmem w/rom
  3320            STA READ.AUX.RAM
  3330            STA WRITE.AUX.RAM
  3340            STA USE.AUX.ZP.STK.BSR
  3350            LDA BSR.2.ROM.READ.RAM.WRITE
  3360            LDA BSR.2.ROM.READ.RAM.WRITE
  3370 * What else but this too
  3380            LDX #$FF
  3390            TXS
  3400 * Copy rom monitor to auxmem BSR
  3410            LDY #MONITOR
  3420            STY MON.A1
  3430            STY MON.STATUS
  3440            LDA /MONITOR
  3450            STA MON.A1+1
  3460 .1         LDA (MON.A1),Y
  3470            STA (MON.A1),Y
  3480            INY
  3490            BNE .1
  3500            INC MON.A1+1
  3510            BNE .1
  3520 * Now use auxmem BSR
  3530            LDA BSR.2.RAM.READ.RAM.WRITE
  3540            LDA BSR.2.RAM.READ.RAM.WRITE
  3550 * Fix monitor in BSR
  3560            LDA /DO.CLREOL
  3570            STA MON.JSR.CLREOL+2
  3580            LDA #DO.CLREOL
  3590            STA MON.JSR.CLREOL+1
  3600 * Hook I/O through main
  3610            LDA #COUT.TO.MAIN
  3620            STA MON.CSWL
  3630            LDA #RDKEY.FROM.MAIN
  3640            STA MON.KSWL
  3650            LDA /COUT.TO.MAIN
  3660            STA MON.CSWL+1
  3670 *          LDA /RDKEY.FROM.MAIN
  3680            STA MON.KSWL+1
  3690 * USE.AUXMEM in auxmem too
  3700            JSR CONNECT.CONTROL.Y
  3710 * Do page 3 locs
  3720            STA NMI
  3730            LDA #MON
  3740            STA NMI+1
  3750            STA MON.IRQLOC
  3760            LDA /MON
  3770            STA NMI+2
  3780            STA MON.IRQLOC+1
  3790            LDA #MON.OLDBRK
  3800            STA MON.BRKV
  3810            LDA /MON.OLDBRK
  3820            STA MON.BRKV+1
  3830 * Enter monitor in auxmem BSR
  3840            JMP MON
  3850 *--------------------------------
  3860 RETURN.TO.AUX
  3870 * Continue in aux ram
  3880            STA READ.AUX.RAM
  3890            STA WRITE.AUX.RAM
  3900            STA USE.AUX.ZP.STK.BSR
  3910            JMP RETURN.COMMON
  3920 RETURN.TO.MAIN
  3930 * Continue in main ram
  3940            STA READ.MAIN.RAM
  3950            STA WRITE.MAIN.RAM
  3960            STA USE.MAIN.ZP.STK.BSR
  3970 RETURN.COMMON
  3980 * Recover SP
  3990            LDX BANK.SP.SAVE
  4000            TXS
  4010 RESTORE.BSR.STATE
  4020            CLV
  4030            LDX BANK.BSR.BANK.SAVE
  4040            BPL .2
  4050            LDX BANK.BSR.RAM.READ.SAVE
  4060            BPL .1
  4070            LDX BSR.2.RAM.READ.RAM.WRITE
  4080            LDX BSR.2.RAM.READ.RAM.WRITE
  4090            BVC .4
  4100 .1         LDX BSR.2.ROM.READ.RAM.WRITE
  4110            LDX BSR.2.ROM.READ.RAM.WRITE
  4120            BVC .4
  4130 .2         LDX BANK.BSR.RAM.READ.SAVE
  4140            BPL .3
  4150            LDX BSR.1.RAM.READ.RAM.WRITE
  4160            LDX BSR.1.RAM.READ.RAM.WRITE
  4170            BVC .4
  4180 .3         LDX BSR.1.ROM.READ.RAM.WRITE
  4190            LDX BSR.1.ROM.READ.RAM.WRITE
  4200 .4         RTS
  4210 *--------------------------------
  4220 SAVE.BSR.STATE
  4230            LDX READ.BSR.BANK
  4240            STX BANK.BSR.BANK.SAVE
  4250            LDX READ.BSR.RAM.READ
  4260            STX BANK.BSR.RAM.READ.SAVE
  4270            RTS
  4280 *--------------------------------
  4290 DO.CLREOL
  4300            LDA #"]"-'@'
  4310 COUT.TO.MAIN
  4320 * Save auxmem's X
  4330            STX BANK.X.SAVE
  4340 * Save BSR bank, BSR ram read, and SP
  4350 * over call to main ram
  4360            JSR SAVE.BSR.STATE
  4370            TSX
  4380            STX BANK.SP.SAVE
  4390 * Continue in main ram
  4400            STA READ.MAIN.RAM
  4410            STA WRITE.MAIN.RAM
  4420            STA USE.MAIN.ZP.STK.BSR
  4430 * Recover SP
  4440            LDX BANK.SP.SAVE
  4450            TXS
  4460            JSR RESTORE.BSR.STATE
  4470            JSR MON.COUT
  4480            JMP IO.COMMON
  4490 *--------------------------------
  4500 RDKEY.FROM.MAIN
  4510 * Repair monitor's sillier attempt
  4520            STA (MON.BASL),Y
  4530 * Save auxmem's X
  4540            STX BANK.X.SAVE
  4550 * Save BSR bank, BSR ram read, and SP
  4560 * over call to main ram
  4570            JSR SAVE.BSR.STATE
  4580            TSX
  4590            STX BANK.SP.SAVE
  4600 * Continue in main ram
  4610            STA READ.MAIN.RAM
  4620            STA WRITE.MAIN.RAM
  4630            STA USE.MAIN.ZP.STK.BSR
  4640            LDX BANK.SP.SAVE       Recover SP
  4650            TXS
  4660            JSR RESTORE.BSR.STATE
  4670            JSR MON.RDKEY
  4680 *--------------------------------
  4690 IO.COMMON
  4700            STA READ.AUX.RAM       Continue in Aux RAM
  4710            STA WRITE.AUX.RAM
  4720            STA USE.AUX.ZP.STK.BSR
  4730            LDX BANK.SP.SAVE       Recover SP
  4740            TXS
  4750            JSR RESTORE.BSR.STATE
  4760            LDX BANK.X.SAVE        Recover X
  4770            RTS
  4780 *--------------------------------
  4790 USE.AUXMEM.END           .EQ *-1

65C02 Bob Sander-Cederlof

People who have started reading AAL since last December have asked what is all this 65C02 business, anyway? Well the 65C02 is a new CMOS version of the 6502 microprocessor. (CMOS stands for Complementary Metal Oxide Semiconductor. That's a different way of making chips. CMOS circuits are noted for extremely low power consumption and extremely high sensitivity to static electricity.) To us Apple owners, the important thing is that the designers of the new chip corrected the bugs in the 6502 and added several new instructions and addressing modes.

The new instructions include PHX, PLX, PHY, and PLY (push and pull the X and Y registers from the stack), BRA (branch always), STZ (store zero), TSB and TRB (test and set or reset bits), and SMB, RMB, BBR and BBS (set, reset and branch on single bits). The main new addressing mode is true indirect without indexing, LDA ($12). This mode is now available for ORA, AND, EOR, ADC, STA, LDA, CMP, and SBC. There are also new modes for the BIT and JMP instructions. INC and DEC can now work on the A register.

There are some problems, though. Rockwell, GTE, NCR, and Synertek (maybe) are manufacturing 65C02 processors, but they are not all the same. The SMB, RMB, BBS, and BBR instructions are only available in the Rockwell chip. The NCR chip works in the Apple //e, but not in older Apples. The GTE processor does work in all Apples (this is being written on an Apple ][+ with a GTE 65C02). I haven't yet received a sample of the Rockwell processor, so I don't personally know if it works in older Apples. Some people say yes, others no.

That's a summary of what we know so far. The confusion is beginning to clear up, but there are still questions about what will or won't work in which Apples, and why. Stay tuned...


Speeding Up Spirals Bob Sander-Cederlof

Several have written to us about Roger Keating's Spiral Screen Clear (AAL June 1983). Charles Putney, who you may remember as the first one to double the speed of the prime number program in AAL several years ago, has now applied his talent to unwinding the screen.

Roger's program ran in 55 seconds, my table-lookup for BASCALC shortened it to 40 seconds. Charlie wrote the whole thing out as one long string of LDA-STA pairs, and trimmed the time to only 7 seconds!

Let's see...there are 960 characters on the screen. If I write a LDA-STA pair to move each byte ahead one position along the spiral path, I will have 959 such pairs. Each LDA and each STA will take 3 bytes, so the program to shift the whole screen one step around the spiral path will take 2x3x959 = 5754 bytes. Add another 5 bytes to LDA #$A0 and store it in the center of the screen before the first rotation. Then add some code to re- run the 959 steps 959 more times, so that the whole screen clears, and you get Charlie's program, 5777 bytes.

Now try to type it all in! Don't worry, we aren't even going to list it here. It will be on the next Quarterly disk, though.

Charlie decided to use five macros, to decrease the amount of manual labor involved. He defined a macro named MOVE which builds the LDA-STA pair for a pair of arguments:

       .MA MOVE
       LDA ]1
       STA ]2
       .EM

Then he defined one macro for each leg of the spiral: MOVED, MOVEL, MOVEU, and MOVER for down, left, up, and right respec- tively. With a few comment lines, the macro definitions take a mere 488 lines! The macros are each called with three parameters:

       >MOVED col,low.row,high.row
       >MOVEL row,low.col,high.col
       >MOVEU col,low.row,high.row
       >MOVER row,low.col,high.col

The definitions out of the way, it only remains to write 12 sets of 4 macro calls, or 48 lines, and a driving loop to do it all 960 times. Here is a condensed listing of the actual code part of Charlie's program:

  1000 *SAVE S.PUTNEY'S SPIRAL
  1010 *
  1020 *
  1030 *      FAST SPIRAL SCREEN CLEAR
  1040 *
  1050 *      CHARLES H. PUTNEY
  1060 *      18 QUINNS ROAD
  1070 *      SHANKILL
  1080 *      CO. DUBLIN
  1090 *      IRELAND
  1100 *
  1110 *
  1120 *
  1130 *
  1140 *--------------------------------
  1150 *
  1160 *      TEXT PAGE BASE ADDRESSES
  1170 *
  1180 *
  1190 R0     .EQ $400
  1200 R1     .EQ $480
  1210 R2     .EQ $500
  1220 R3     .EQ $580 
  1230 R4     .EQ $600
  1240 R5     .EQ $680
  1250 R6     .EQ $700
  1260 R7     .EQ $780
  1270 R8     .EQ $428
  1280 R9     .EQ $4A8
  1290 R10    .EQ $528
  1300 R11    .EQ $5A8 
  1310 R12    .EQ $628
  1320 R13    .EQ $6A8
  1330 R14    .EQ $728
  1340 R15    .EQ $7A8
  1350 R16    .EQ $450
  1360 R17    .EQ $4D0
  1370 R18    .EQ $550
  1380 R19    .EQ $5D0 
  1390 R20    .EQ $650
  1400 R21    .EQ $6D0
  1410 R22    .EQ $750
  1420 R23    .EQ $7D0
  1430 *
  1440 *
  1450 *--------------------------------
  1460 *
  1470 *      MACRO DEFINITIONS
  1480 *
  1490 *
  1500        .MA MOVE 
  1510        LDA ]1
  1520        STA ]2
  1530        .EM
  1540 *
  1550 *
  1560 *
  1570        .MA MOVER    MOVE RIGHT ROW,COLLOW,COLHIGH FROM 0-39
  1580        .DO ]3>]2
  1590        >MOVE ]1+]3-1,]1+]3
  1600        .FIN
  1610        .DO ]3-1>]2
  1620        >MOVE ]1+]3-2,]1+]3-1
  1630        .FIN
  1640        .DO ]3-2>]2
  1650        >MOVE ]1+]3-3,]1+]3-2 
  1660        .FIN
  1670        .DO ]3-3>]2
  1680        >MOVE ]1+]3-4,]1+]3-3
  1690        .FIN
  1700        .DO ]3-4>]2
  1710        >MOVE ]1+]3-5,]1+]3-4
  1720        .FIN
  1730        .DO ]3-5>]2
  1740        >MOVE ]1+]3-6,]1+]3-5
  1750        .FIN
  1760        .DO ]3-6>]2
  1770        >MOVE ]1+]3-7,]1+]3-6
  1780        .FIN
  1790        .DO ]3-7>]2
  1800        >MOVE ]1+]3-8,]1+]3-7
  1810        .FIN
  1820        .DO ]3-8>]2
  1830        >MOVE ]1+]3-9,]1+]3-8
  1840        .FIN
  1850        .DO ]3-9>]2
  1860        >MOVE ]1+]3-10,]1+]3-9
  1870        .FIN
  1880        .DO ]3-10>]2
  1890        >MOVE ]1+]3-11,]1+]3-10
  1900        .FIN
  1910        .DO ]3-11>]2
  1920        >MOVE ]1+]3-12,]1+]3-11
  1930        .FIN
  1940        .DO ]3-12>]2
  1950        >MOVE ]1+]3-13,]1+]3-12
  1960        .FIN
  1970        .DO ]3-13>]2
  1980        >MOVE ]1+]3-14,]1+]3-13
  1990        .FIN
  2000        .DO ]3-14>]2
  2010        >MOVE ]1+]3-15,]1+]3-14
  2020        .FIN
  2030        .DO ]3-15>]2
  2040        >MOVE ]1+]3-16,]1+]3-15
  2050        .FIN
  2060        .DO ]3-16>]2
  2070        >MOVE ]1+]3-17,]1+]3-16
  2080        .FIN
  2090        .DO ]3-17>]2
  2100        >MOVE ]1+]3-18,]1+]3-17
  2110        .FIN
  2120        .DO ]3-18>]2
  2130        >MOVE ]1+]3-19,]1+]3-18
  2140        .FIN
  2150        .DO ]3-19>]2
  2160        >MOVE ]1+]3-20,]1+]3-19
  2170        .FIN
  2180        .DO ]3-20>]2
  2190        >MOVE ]1+]3-21,]1+]3-20
  2200        .FIN
  2210        .DO ]3-21>]2
  2220        >MOVE ]1+]3-22,]1+]3-21
  2230        .FIN
  2240        .DO ]3-22>]2
  2250        >MOVE ]1+]3-23,]1+]3-22
  2260        .FIN
  2270        .DO ]3-23>]2
  2280        >MOVE ]1+]3-24,]1+]3-23
  2290        .FIN
  2300        .DO ]3-24>]2
  2310        >MOVE ]1+]3-25,]1+]3-24
  2320        .FIN
  2330        .DO ]3-25>]2
  2340        >MOVE ]1+]3-26,]1+]3-25
  2350        .FIN
  2360        .DO ]3-26>]2
  2370        >MOVE ]1+]3-27,]1+]3-26
  2380        .FIN
  2390        .DO ]3-27>]2
  2400        >MOVE ]1+]3-28,]1+]3-27
  2410        .FIN
  2420        .DO ]3-28>]2
  2430        >MOVE ]1+]3-29,]1+]3-28
  2440        .FIN
  2450        .DO ]3-29>]2
  2460        >MOVE ]1+]3-30,]1+]3-29
  2470        .FIN
  2480        .DO ]3-30>]2
  2490        >MOVE ]1+]3-31,]1+]3-30
  2500        .FIN
  2510        .DO ]3-31>]2
  2520        >MOVE ]1+]3-32,]1+]3-31
  2530        .FIN
  2540        .DO ]3-32>]2
  2550        >MOVE ]1+]3-33,]1+]3-32
  2560        .FIN
  2570        .DO ]3-33>]2
  2580        >MOVE ]1+]3-34,]1+]3-33
  2590        .FIN
  2600        .DO ]3-34>]2
  2610        >MOVE ]1+]3-35,]1+]3-34
  2620        .FIN
  2630        .DO ]3-35>]2
  2640        >MOVE ]1+]3-36,]1+]3-35
  2650        .FIN
  2660        .DO ]3-36>]2  
  2670        >MOVE ]1+]3-37,]1+]3-36
  2680        .FIN
  2690        .DO ]3-37>]2
  2700        >MOVE ]1+]3-38,]1+]3-37
  2710        .FIN
  2720        .DO ]3-38>]2
  2730        >MOVE ]1+]3-39,]1+]3-38
  2740        .FIN
  2750        .EM
  2760 *
  2770 *
  2780 *
  2790        .MA MOVEL    MOVE LEFT ROW,COLLOW,COLHIGH FROM 0-39
  2800        .DO ]3>]2
  2810        >MOVE ]1+]2+1,]1+]2
  2820        .FIN
  2830        .DO ]3-1>]2
  2840        >MOVE ]1+]2+2,]1+]2+1
  2850        .FIN
  2860        .DO ]3-2>]2
  2870        >MOVE ]1+]2+3,]1+]2+2 
  2880        .FIN
  2890        .DO ]3-3>]2
  2900        >MOVE ]1+]2+4,]1+]2+3
  2910        .FIN
  2920        .DO ]3-4>]2
  2930        >MOVE ]1+]2+5,]1+]2+4
  2940        .FIN
  2950        .DO ]3-5>]2
  2960        >MOVE ]1+]2+6,]1+]2+5
  2970        .FIN
  2980        .DO ]3-6>]2
  2990        >MOVE ]1+]2+7,]1+]2+6
  3000        .FIN
  3010        .DO ]3-7>]2
  3020        >MOVE ]1+]2+8,]1+]2+7
  3030        .FIN
  3040        .DO ]3-8>]2
  3050        >MOVE ]1+]2+9,]1+]2+8
  3060        .FIN
  3070        .DO ]3-9>]2
  3080        >MOVE ]1+]2+10,]1+]2+9
  3090        .FIN
  3100        .DO ]3-10>]2
  3110        >MOVE ]1+]2+11,]1+]2+10
  3120        .FIN
  3130        .DO ]3-11>]2
  3140        >MOVE ]1+]2+12,]1+]2+11
  3150        .FIN
  3160        .DO ]3-12>]2
  3170        >MOVE ]1+]2+13,]1+]2+12
  3180        .FIN
  3190        .DO ]3-13>]2
  3200        >MOVE ]1+]2+14,]1+]2+13
  3210        .FIN
  3220        .DO ]3-14>]2
  3230        >MOVE ]1+]2+15,]1+]2+14
  3240        .FIN
  3250        .DO ]3-15>]2
  3260        >MOVE ]1+]2+16,]1+]2+15
  3270        .FIN
  3280        .DO ]3-16>]2
  3290        >MOVE ]1+]2+17,]1+]2+16
  3300        .FIN
  3310        .DO ]3-17>]2
  3320        >MOVE ]1+]2+18,]1+]2+17
  3330        .FIN
  3340        .DO ]3-18>]2
  3350        >MOVE ]1+]2+19,]1+]2+18
  3360        .FIN
  3370        .DO ]3-19>]2
  3380        >MOVE ]1+]2+20,]1+]2+19
  3390        .FIN
  3400        .DO ]3-20>]2
  3410        >MOVE ]1+]2+21,]1+]2+20
  3420        .FIN
  3430        .DO ]3-21>]2
  3440        >MOVE ]1+]2+22,]1+]2+21
  3450        .FIN
  3460        .DO ]3-22>]2
  3470        >MOVE ]1+]2+23,]1+]2+22
  3480        .FIN
  3490        .DO ]3-23>]2
  3500        >MOVE ]1+]2+24,]1+]2+23
  3510        .FIN
  3520        .DO ]3-24>]2
  3530        >MOVE ]1+]2+25,]1+]2+24
  3540        .FIN
  3550        .DO ]3-25>]2
  3560        >MOVE ]1+]2+26,]1+]2+25
  3570        .FIN
  3580        .DO ]3-26>]2
  3590        >MOVE ]1+]2+27,]1+]2+26
  3600        .FIN
  3610        .DO ]3-27>]2
  3620        >MOVE ]1+]2+28,]1+]2+27
  3630        .FIN
  3640        .DO ]3-28>]2
  3650        >MOVE ]1+]2+29,]1+]2+28
  3660        .FIN
  3670        .DO ]3-29>]2
  3680        >MOVE ]1+]2+30,]1+]2+29
  3690        .FIN
  3700        .DO ]3-30>]2
  3710        >MOVE ]1+]2+31,]1+]2+30
  3720        .FIN
  3730        .DO ]3-31>]2
  3740        >MOVE ]1+]2+32,]1+]2+31
  3750        .FIN
  3760        .DO ]3-32>]2
  3770        >MOVE ]1+]2+33,]1+]2+32
  3780        .FIN
  3790        .DO ]3-33>]2
  3800        >MOVE ]1+]2+34,]1+]2+33
  3810        .FIN
  3820        .DO ]3-34>]2
  3830        >MOVE ]1+]2+35,]1+]2+34
  3840        .FIN
  3850        .DO ]3-35>]2
  3860        >MOVE ]1+]2+36,]1+]2+35
  3870        .FIN
  3880        .DO ]3-36>]2  
  3890        >MOVE ]1+]2+37,]1+]2+36
  3900        .FIN
  3910        .DO ]3-37>]2
  3920        >MOVE ]1+]2+38,]1+]2+37
  3930        .FIN
  3940        .DO ]3-38>]2
  3950        >MOVE ]1+]2+39,]1+]2+38
  3960        .FIN
  3970        .EM
  3980 *
  3990 *
  4000 *
  4010        .MA MOVEU    MOVE UP COL,ROWLOW,ROWHIGH FROM 0-23
  4020        .DO ]2<1
  4030        .DO ]3+1>1
  4040        >MOVE ]1+R1,]1+R0
  4050        .FIN
  4060        .FIN
  4070        .DO ]2<2
  4080        .DO ]3+1>2
  4090        >MOVE ]1+R2,]1+R1
  4100        .FIN
  4110        .FIN
  4120        .DO ]2<3
  4130        .DO ]3+1>3
  4140        >MOVE ]1+R3,]1+R2
  4150        .FIN
  4160        .FIN
  4170        .DO ]2<4
  4180        .DO ]3+1>4
  4190        >MOVE ]1+R4,]1+R3
  4200        .FIN
  4210        .FIN
  4220        .DO ]2<5
  4230        .DO ]3+1>5
  4240        >MOVE ]1+R5,]1+R4
  4250        .FIN
  4260        .FIN
  4270        .DO ]2<6
  4280        .DO ]3+1>6
  4290        >MOVE ]1+R6,]1+R5
  4300        .FIN
  4310        .FIN
  4320        .DO ]2<7
  4330        .DO ]3+1>7
  4340        >MOVE ]1+R7,]1+R6
  4350        .FIN
  4360        .FIN
  4370        .DO ]2<8
  4380        .DO ]3+1>8
  4390        >MOVE ]1+R8,]1+R7
  4400        .FIN
  4410        .FIN
  4420        .DO ]2<9
  4430        .DO ]3+1>9
  4440        >MOVE ]1+R9,]1+R8
  4450        .FIN
  4460        .FIN
  4470        .DO ]2<10
  4480        .DO ]3+1>10
  4490        >MOVE ]1+R10,]1+R9
  4500        .FIN
  4510        .FIN
  4520        .DO ]2<11
  4530        .DO ]3+1>11
  4540        >MOVE ]1+R11,]1+R10
  4550        .FIN
  4560        .FIN
  4570        .DO ]2<12
  4580        .DO ]3+1>12
  4590        >MOVE ]1+R12,]1+R11
  4600        .FIN
  4610        .FIN
  4620        .DO ]2<13
  4630        .DO ]3+1>13
  4640        >MOVE ]1+R13,]1+R12
  4650        .FIN
  4660        .FIN
  4670        .DO ]2<14
  4680        .DO ]3+1>14
  4690        >MOVE ]1+R14,]1+R13
  4700        .FIN
  4710        .FIN
  4720        .DO ]2<15
  4730        .DO ]3+1>15
  4740        >MOVE ]1+R15,]1+R14
  4750        .FIN
  4760        .FIN
  4770        .DO ]2<16
  4780        .DO ]3+1>16
  4790        >MOVE ]1+R16,]1+R15
  4800        .FIN
  4810        .FIN
  4820        .DO ]2<17
  4830        .DO ]3+1>17
  4840        >MOVE ]1+R17,]1+R16
  4850        .FIN
  4860        .FIN
  4870        .DO ]2<18
  4880        .DO ]3+1>18
  4890        >MOVE ]1+R18,]1+R17
  4900        .FIN
  4910        .FIN
  4920        .DO ]2<19
  4930        .DO ]3+1>19
  4940        >MOVE ]1+R19,]1+R18
  4950        .FIN
  4960        .FIN
  4970        .DO ]2<20
  4980        .DO ]3+1>20
  4990        >MOVE ]1+R20,]1+R19
  5000        .FIN
  5010        .FIN
  5020        .DO ]2<21
  5030        .DO ]3+1>21
  5040        >MOVE ]1+R21,]1+R20
  5050        .FIN
  5060        .FIN
  5070        .DO ]2<22
  5080        .DO ]3+1>22
  5090        >MOVE ]1+R22,]1+R21
  5100        .FIN
  5110        .FIN
  5120        .DO ]2<23
  5130        .DO ]3+1>23
  5140        >MOVE ]1+R23,]1+R22
  5150        .FIN
  5160        .FIN
  5170        .EM
  5180 *
  5190 *
  5200 *
  5210        .MA MOVED    MOVE DOWN COL,ROWLOW,ROWHIGH FROM 0-23
  5220        .DO ]2<23
  5230        .DO ]3+1>23
  5240        >MOVE ]1+R22,]1+R23
  5250        .FIN
  5260        .FIN
  5270        .DO ]2<22
  5280        .DO ]3+1>22
  5290        >MOVE ]1+R21,]1+R22
  5300        .FIN
  5310        .FIN
  5320        .DO ]2<21
  5330        .DO ]3+1>21
  5340        >MOVE ]1+R20,]1+R21
  5350        .FIN
  5360        .FIN
  5370        .DO ]2<20
  5380        .DO ]3+1>20
  5390        >MOVE ]1+R19,]1+R20
  5400        .FIN
  5410        .FIN
  5420        .DO ]2<19
  5430        .DO ]3+1>19
  5440        >MOVE ]1+R18,]1+R19
  5450        .FIN
  5460        .FIN
  5470        .DO ]2<18
  5480        .DO ]3+1>18
  5490        >MOVE ]1+R17,]1+R18
  5500        .FIN
  5510        .FIN
  5520        .DO ]2<17
  5530        .DO ]3+1>17
  5540        >MOVE ]1+R16,]1+R17
  5550        .FIN
  5560        .FIN
  5570        .DO ]2<16
  5580        .DO ]3+1>16
  5590        >MOVE ]1+R15,]1+R16
  5600        .FIN
  5610        .FIN
  5620        .DO ]2<15
  5630        .DO ]3+1>15
  5640        >MOVE ]1+R14,]1+R15
  5650        .FIN
  5660        .FIN
  5670        .DO ]2<14
  5680        .DO ]3+1>14
  5690        >MOVE ]1+R13,]1+R14
  5700        .FIN
  5710        .FIN
  5720        .DO ]2<13
  5730        .DO ]3+1>13
  5740        >MOVE ]1+R12,]1+R13
  5750        .FIN
  5760        .FIN
  5770        .DO ]2<12
  5780        .DO ]3+1>12
  5790        >MOVE ]1+R11,]1+R12
  5800        .FIN
  5810        .FIN
  5820        .DO ]2<11
  5830        .DO ]3+1>11 
  5840        >MOVE ]1+R10,]1+R11
  5850        .FIN
  5860        .FIN
  5870        .DO ]2<10
  5880        .DO ]3+1>10
  5890        >MOVE ]1+R9,]1+R10
  5900        .FIN
  5910        .FIN
  5920        .DO ]2<9
  5930        .DO ]3+1>9
  5940        >MOVE ]1+R8,]1+R9
  5950        .FIN
  5960        .FIN
  5970        .DO ]2<8 
  5980        .DO ]3+1>8
  5990        >MOVE ]1+R7,]1+R8
  6000        .FIN
  6010        .FIN
  6020        .DO ]2<7  
  6030        .DO ]3+1>7
  6040        >MOVE ]1+R6,]1+R7
  6050        .FIN
  6060        .FIN
  6070        .DO ]2<6
  6080        .DO ]3+1>6
  6090        >MOVE ]1+R5,]1+R6
  6100        .FIN
  6110        .FIN
  6120        .DO ]2<5
  6130        .DO ]3+1>5
  6140        >MOVE ]1+R4,]1+R5
  6150        .FIN
  6160        .FIN
  6170        .DO ]2<4
  6180        .DO ]3+1>4
  6190        >MOVE ]1+R3,]1+R4
  6200        .FIN
  6210        .FIN
  6220        .DO ]2<3
  6230        .DO ]3+1>3
  6240        >MOVE ]1+R2,]1+R3
  6250        .FIN
  6260        .FIN
  6270        .DO ]2<2
  6280        .DO ]3+1>2
  6290        >MOVE ]1+R1,]1+R2
  6300        .FIN
  6310        .FIN
  6320        .DO ]2<1
  6330        .DO ]3+1>1
  6340        >MOVE ]1+R0,]1+R1
  6350        .FIN
  6360        .FIN
  6370        .EM
  6380 *
  6390 *
  6400 *--------------------------------
  6410 *
  6420 *      SPIRAL PROGRAM
  6430        .OR $6000    OUT OF THE WAY
  6440        .TF SPIRAL.OBJ
  6450 *
  6460 *
  6470 SPIRAL LDA #' '+$80 GET A SPACE
  6480        STA R12+12   PUT IT IN CENTER
  6490        LDX #960     HOW MANY TIMES ?
  6500        LDY /960     HIGH ORDER
  6510 *
  6520 SPI1   >MOVED 0,0,23
  6530        >MOVEL R0,0,39
  6540        >MOVEU 39,0,23
  6550        >MOVER R23,1,39
  6560 *
  6570        >MOVED 1,1,23
  6580        >MOVEL R1,1,38
  6590        >MOVEU 38,1,22
  6600        >MOVER R22,2,38
  6610 *
  6620        >MOVED 2,2,22
  6630        >MOVEL R2,2,37
  6640        >MOVEU 37,2,21
  6650        >MOVER R21,3,37
  6660 *
  6670        >MOVED 3,3,21
  6680        >MOVEL R3,3,36
  6690        >MOVEU 36,3,20
  6700        >MOVER R20,4,36
  6710 *
  6720        >MOVED 4,4,20
  6730        >MOVEL R4,4,35
  6740        >MOVEU 35,4,19
  6750        >MOVER R19,5,35
  6760 *
  6770        >MOVED 5,5,19
  6780        >MOVEL R5,5,34
  6790        >MOVEU 34,5,18
  6800        >MOVER R18,6,34
  6810 *
  6820        >MOVED 6,6,18
  6830        >MOVEL R6,6,33
  6840        >MOVEU 33,6,17
  6850        >MOVER R17,7,33
  6860 *
  6870        >MOVED 7,7,17
  6880        >MOVEL R7,7,32
  6890        >MOVEU 32,7,16
  6900        >MOVER R16,8,32
  6910 *
  6920        >MOVED 8,8,16
  6930        >MOVEL R8,8,31
  6940        >MOVEU 31,8,15
  6950        >MOVER R15,9,31
  6960 *
  6970        >MOVED 9,9,15
  6980        >MOVEL R9,9,30
  6990        >MOVEU 30,9,14
  7000        >MOVER R14,10,30
  7010 *
  7020        >MOVED 10,10,14
  7030        >MOVEL R10,10,29
  7040        >MOVEU 29,10,13
  7050        >MOVER R13,11,29
  7060 *
  7070        >MOVED 11,11,13
  7080        >MOVEL R11,11,28
  7090        >MOVEU 28,11,12
  7100        >MOVER R12,12,28
  7110 *
  7120        DEX
  7130        CPX #$FF
  7140        BNE SPI2
  7150        DEY
  7160        CPY #$FF
  7170        BNE SPI2
  7180        RTS
  7190 SPI2   JMP SPI1
  7200 *
  7210 ZZSIZE .EQ *-SPIRAL

Remember, the whole source with the full macro definitions will be on the next quarterly disk ($15, for all source code in issues July-August-September 1983).

Because Charlie's program makes such heavy use of macros, it takes considerable time to assemble. He timed it at nearly two minutes. If the program were written out the long way, without macros, it would take only about 20 seconds to assemble.

Charlie pointed out that we are needlessly moving the center of the spiral, which is already blank. As the blanked portion grows, this becomes very significant. In fact, by eliminating moving the cleared portion, the time could be further reduced to only 3 1/2 seconds. Each LDA-STA takes 8 cycles. The long way takes 959*960 pairs, plus some overhead. Ignoring the overhead, we get 7365120 cycles, or about 7.2 seconds. Forgetting the blanked stuff makes it 3.6 seconds. Any takers?

And I was just wondering...how about an Applesoft program which writes the 959 LDA-STA pairs as assembly language source on a text file? Or POKEs the actual object code, by computing the addresses necessary, into a binary buffer area. Again, any takers?


Tinkering with Variable Cross Reference Louis Pitz
De Witt, Iowa

I am a tinkerer! Yes I love to take programs and add features to improve them. Sometimes the "improved" version even works! Usually I learn a lot about humility, and occasionally a bit about programming.

A case in point is the program for doing an Applesoft Variable Cross Reference (from the November 1980 issue of Apple Assembly Line). I just recently got Quarterly Disk #1 with its source code, and so it became "tinker-time".

VCR works just fine, and is fast! But it only produces 40- column output, and I wanted both 40-column screen and 80-column printer hardcopy. Here are some patches which will do the job. It makes a good short example of changing output hooks in the middle of a program without goofing up DOS.

     1060       .TF B.VCRP  "P" FOR PRINTER VERSION

     4534        LDA #0     RESET COUNTER TO 0
     4538        STA $6     FOR EACH VARIABLE

     4821        INC $6     COUNT THE SCREEN LINE
     4822        LDA $6
     4823        AND #1     LOOK AT ODD-EVEN BIT
     4824        BEQ TAB.NEW.LINE   BOTH SCRN AND PRINTER
     4825        LDA #$FDF0         ONLY SCRN GETS NEW LINE
     4826        STA $36    SO DISCONNECT PRINTER
     4827        LDA /$FDF0
     4828        STA $37
     4829        JSR $3EA           PASS TO DOS
     4830        JSR MON.CROUT      SCREEN ONLY
     4831        LDA #$C100         REHOOK PRINTER
     4832        STA $36
     4833        LDA /$C100
     4834        STA $37
     4835        JSR $3EA           PASS TO DOS
     4836        BNE .1             ...ALWAYS

To use the printer version of VCR, BRUN B.VCRP. This sets up the ampersand vector. Then LOAD your Applesoft program. Use PR#1 to turn on your printer. Then type "&" and RETURN, and watch the cross reference.

If your printer is in some slot other than 1, change lines 4831 and 4833 to the correct value ($Cs00, where s=slot#).


Reversing, Getting, and Putting Nybbles Bob Sander-Cederlof

In the process of de-crypting a large data base, I needed to reverse the nybbles in each of roughly 32000 bytes. There are probably a lot of ways to do this, but I found one which takes only 12 bytes to reverse the nybbles in the A-register.

Just to be sure we agree on what I am talking about, here is a little diagram:

       a b c d   e f g h

       e f g h   a b c d

One way, sort of brute force, involves breaking the nybbles out and remerging them:

       LDA (PNTR),Y
       ASL             SHIFT EFGH LEFT
       ASL
       ASL
       ASL
       STA TEMP
       LDA (PNTR),Y
       LSR             SHIFT ABCD RIGHT
       LSR
       LSR
       LSR
       ORA TEMP        RE-MERGE NYBBLES
       STA (PNTR),Y

From another perspective, I am trying to rotate the data byte half-way around. But if I try to do it with ROL or ROR instructions, one bit gets left in CARRY, and an extra bit gets inserted in the middle. Here is how I finally did it:

       LDA (PNTR),Y    abcd efgh
       ASL             bcde fgh0
       ADC #0          bcde fgha
       ASL             cdef gha0
       ADC #0          cdef ghab
       ASL             defg hab0
       ADC #0          defg habc
       ASL             efgh abc0
       ADC #0          efgh abcd
       STA (PNTR),Y

Each ASL-ADC pair shifts the byte around one bit. The ASL shifts the leftmost bit into the CARRY bit, and a zero into the right end. The ADC #0 adds CARRY into the rightmost bit.

Naturally, curiosity forces me to look at the possibility of shifting right one bit also. We have LSR and ROR, of course, but both of these leave the shifted out bit in CARRY. I want that bit back in the sign position, like this:

       ABCDEFGH should become HABCDEFG

Two similar methods come to mind, depending on how I might use it. If the byte to be shifted is in A-reg, and needs to remain there, and I don't want to upset any other registers, I can do it like this:

       PHA     save unshifted value
       LSR     get rightmost bit in CARRY
       PLA     restore unshifted value
       ROR     shift again, putting right bit on left

If the byte to be shifted is in memory, and I want the results to be in memory, I might do it like this:

       LDA FLAG
       LSR        RIGHTMOST BIT INTO CARRY
       ROR FLAG   SHIFT BYTE, PUTTING RIGHT INTO LEFT

Note that I can branch according to the value of the bit which moved around by using BMI or BPL, because that bit is the new sign bit.

The last method above can be useful when you have a program that needs to alternate between two paths. For example, suppose I write a program to pick up the "next nybble" from a data area. The first time I call it, I want to get the left nybble of the first byte. Next time, the right nybble of the same byte. Next time the left nybble of the next byte. And so on.

I might store the value $55 in FLAG initially, and then use LDA FLAG, LSR, ROR FLAG, to shift it around. FLAG will alternate between $55 and $AA. My subroutine can alternate between left and right nybbles.

Not to leave you hanging, I wrote "get next nybble" and "put next nybble" subroutines. By the time I finished polishing, yet another technique had surfaced for rotating the $55/$AA flag. I used this new method so as not disturb the contents of the A-register.

To set up either routine, the address of the beginning of the data area must be put into PNTR and PNTR+1, and $55 must be put into FLAG.

  1000 *SAVE NYBBLE GET & PUT
  1010 *--------------------------------
  1020 PNTR   .EQ 0 AND 1
  1030 FLAG   .EQ 2
  1040 *--------------------------------
  1050 *      PUT NEXT NYBBLE AT (PNTR)
  1060 *      IF FLAG = $55, PUT LEFT NYBBLE
  1070 *              = $AA, PUT RIGHT NYBBLE
  1080 *--------------------------------
  1090 PUT.NEXT.NYBBLE
  1100        LDX #0
  1110        LSR FLAG          $55 OR $AA
  1120        BCS .1            ...IT WAS $AA, NOW $54
  1130 *---STORE IN LEFT NYBBLE---------
  1140        ASL          FLAG NOW $AA
  1150        ASL
  1160        ASL
  1170        ASL
  1180        STA (PNTR,X)
  1190        RTS
  1200 *---STORE IN RIGHT NYBBLE--------
  1210 .1     ORA (PNTR,X) MERGE WITH LEFT NYBBLE
  1220        STA (PNTR,X)
  1230        INC FLAG     MAKE $54 INTO $55
  1240        INC PNTR     MOVE PNTR TO NEXT BYTE
  1250        BNE .2
  1260        INC PNTR+1  
  1270 .2     RTS
  1280 *--------------------------------
  1290 *      GET NEXT NYBBLE
  1300 *      IF FLAG = $55, GET LEFT NYBBLE
  1310 *              = $AA, GET RIGHT NYBBLE
  1320 *--------------------------------
  1330 GET.NEXT.NYBBLE
  1340        LDX #0
  1350        LSR FLAG          WAS $55 OR $AA
  1360        LDA (PNTR,X)    GET BYTE WITH NYBBLES
  1370        BCS .1            ...WAS $AA, NOW $54
  1380 *---GET LEFT NYBBLE--------------
  1390        LSR
  1400        LSR
  1410        LSR
  1420        LSR
  1430        RTS
  1440 *---GET RIGHT NYBBLE--------------
  1450 .1     INC FLAG     MAKE $54 INTO $55
  1460        INC PNTR     ADVANCE TO NEXT BYTE
  1470        BNE .2
  1480        INC PNTR+1  
  1490 .2     AND #$0F     ISOLATE NYBBLE
  1500        RTS
  1510 *--------------------------------

Odds and Ends Bob Sander-Cederlof

Grappler Interfaces

There should be a leaflet included with this issue describing the Grappler printer interfaces. We now have three of them "in the family" here, and have been very pleased with their performance. Check the brochure for features, the ad on page three for our prices, and let us hear from you.

WICO Track Ball

Several of you have inquired about or ordered the WICO Track Ball that I reviewed a couple of months ago, so we've decided to carry them regularly. WICO has since raised their price from $79.95 to $89.95, so we're going from $75 to $80.

Diskettes

There's getting to be a lot more competition in the diskette business, so prices are falling. After seeing so many ads at such attractive prices, Bob called Verbatim and told them that we had to have a better price, or we would have to change brands. That paid off, so we can now offer the same high- quality Verbatim Datalife diskettes at $45.00 for a package of 20. That's $2.25 each for the best diskettes we've found.

Whatever You Want

If you're shopping for a new peripheral, accessory, or program, give us a call and ask for a quote. We can get nearly anything you might want, and we'd love the chance to serve you.


Some Small Patches Bill Morgan

We've had several calls requesting the patch addresses for a couple of features in the S-C Macro Assemblers.

Ansert?

In Version 1.1 of the Macro Assembler, Bob changed the CTRL-I (Insert) command in the EDIT mode to CTRL-A (for ADD). This was done because the Apple //e keyboard has the TAB key, which generates a CTRL-I code. It didn't seem to make much sense to have the TAB key do an insert operation, so he added a clear- to-next-tab-stop function for CTRL-I.

Well, a lot of people don't have //e's, or don't much care about the TAB key. A lot of us are used to CTRL-I for Insert, and would like to keep it that way.

The CTRL-A character ($81) is at $1C87 in the $1000 version, and at $DCB7 in the $D000 version. Just change that byte to a $89, and you'll have your good old CTRL-I back. If you want to keep the clear-to-tab-stop function, you can change the $89 at $1CC6 ($DCC6) to a $81. That will make CTRL-A do the clear-to- tab.

.BS Filler Byte

The directive .BS <expr> skips over <expr> bytes when you are assembling to memory, and sends <expr> zero bytes to the target file when you are assembling to disk. Several people have asked how to change the zero to some other value.

For example, a freshly-erased EPROM contains all $FF bytes. When you burn data into the chip, you actually write in just the zero bits. If you are assembling code to be written into an EPROM, you want any fill bytes to be $FF, so you can add patches later without having to erase and re-write the whole chip.

The following table shows the addresses of the zero byte in the various versions of the Macro Assembler. Just change the indicated byte to the value you want to use for filler.

     Version  1.0  |              1.1
                   | 40-col   //e    Videx   STB
      $1000   2D43 |  2D62    2D48   2E37    2E60
      $D000   EE8F |  EE86    EE62   EF5A    EF83

Some More 68000 Boards Bob Sander-Cederlof

First let me apologize for an erroneous statement in the May '83 issue, in which I juxtaposed two unrelated facts in a cause-effect sentence. Many readers have sent corrections: I am told that grounding the DTACK signal has nothing to do with how much memory you can add. How did I ever get the idea that it did? If you want the straight scoop on this, subscribe to Digital Acoustics' newsletter "DTACK Grounded".

Digital Acoustics has announced a new board, called the "DTACK Grande". Almost sounds like "grounded", but this time it isn't. You get one megabyte of RAM and a 12.5 MHz 68000. RAM refresh is handled by an interrupt routine, with software. The overhead is only 4%, giving an effective speed of 10 MHz. Expansion connectors on the card can connect to another 15.7 megabytes. I'd say Saybrook has been passed by, but Hal Hardenburg beat me to it! (Digital Acoustics, 1415 E. McFadden, Suite F, Santa Ana, CA 92705. (714) 835-4884)

Mike Heckman at Anthro-Digital sent me some literature on another new 68000 board. Enhancement Technology Corporation calls it the "PDQ//". Specs include: 10 MHZ, 256K RAM, UCSD p-system, Applesoft-compatible BASIC. The price will be $1495, available by the end of August. We may be able to make you a deal on one of these. (ETC, P.O.Box 1267, Pittsfield, MA 01202. (413) 445-4219)


Bringing Some Patches Together Jim Wetzel

Earlier this year I decided to break down and finally buy an 80-column card for my Apple II+. After all, it's cheaper than a IIe. I was just about to type in the Videx patches from AAL Volume 2, No.11, when Bob announced Version 1.1. Well with the Videx patches and all the new features I just couldn't pass up his offer. After a call to Bob and a three day wait I had version 1.1 of the S-C Macro Assembler.

While testing out the new version I soon discovered most of the patches I had applied to version 1.0 would not work properly. The addresses of the routines/tables had all moved. After a few hours work and a lot of dis-assembling I would like to share the new locations with AAL readers and bring some of the patches together.

First I will describe the new addresses and then show how I used them.

The Escape Function Table is now located at $14AB-$14C6 <ESC-@ thru ESC-M>. This is a group of two-byte addresses (minus 1, because they are of the PHA-PHA-RTS variety) of the routines to handle the escape functions.

The Edit Function Table is now located at $1CB4-$1CE3 <ctrl-@ thru ctrl-X>. This table is somewhat different. Each entry is three bytes long and it contains the control character and the address-minus-1 of the routine to handle the function.

Location $14D3 contains the dash count <$26> for the ESC-L function.

Location $13FF contains a JSR to the monitor Bell routine. This is the end of the input checker, the JSR BELL is executed when an invalid character is entered, and a good place to put a JSR to an extended input processor.

These locations are valid for the regular version and the Videx version which load at $1000. For language card users just add $C000 to the address. My hat is off to Bob for adding all the features of Videx and still keeping the assembler looking the same. I have not checked the STB or the IIe versions for compatibility but, with a little bit of work and knowing what to look for it should be an easy process.

Now, what can you do with this information? I have modified Bob's language card loader to show you (figure 1). With the exception of the REM statements, lines 1000-1140 of the file are as Bob supplied; after that the changes begin. I will not spend a lot of time explaining the routines themselves because they are all well documented in the referenced AAL articles.

The first thing I do is load in my extended input processor (figure 2) at $F600. There appears to be about two free pages after the assembler and before monitor in the language card version. For standard version users just move the symbol table up as described AAL Vol. 2, No. 9. My input processor is a combination of Auto Catalog (AAL 2.9) and Toggling Upper/Lower Case (AAL 3.3). Next I modify the JSR BELL to JSR CONTROL.A.

Once you have control you can add any routines you wish (such as R. F. O'Brien's Auto/Manual Toggle AAL 2.11). For now I am only interested in an upper/lower case toggle.

Line 1170 modifies the ESC-C function to JSR to my routine for auto Catalog. Remember this should be the address of the routine - 1. Lines 1180-1190 change the cursor to a blinking underline (as described in AAL 3.5) along with line 1200 which changes the number of "-"'s from 38 to 64 (I found 68 to be too many).

Last but not least is an answer to Steve Mann's request for a upper/lower case toggle in EDIT mode. In version 1.1 Bob changed the ctrl-I key function in EDIT mode and added a ctrl-A key function in its place. He did it so that the //e TAB key, which generates control-I, would really mean TAB.

Well Bob, I like mnemonic commands (like ctrl-I for Insert), and think the older Apples should still take precedence. Line 1210 changes the ctrl-A key to branch to my upper/lower case toggle routine, just past the character check, and line 1220 changes the ctrl-I routine back to its proper function (this was the address found in the ctrl-A area).

I hope these patches will be useful to other AAL readers not only for what they do, but for how they do it.

  1000 REM LOAD S-C MACRO ASSEMBLER (VIDEX)
  1010 REM INTO RAM AT $D000
  1020 REM LOAD PATCHES AT $F600
  1030 REM PATCH INPUT TEST TO CHECK FOR MY COMMANDS BEFORE ERROR
  1040 REM PATCH ESCAPE TABLE ($D4AB-) FOR ESCAPE-C
  1050 REM CHANGE CURSOR TO BLINKING UNDERLINE
  1060 REM PATCH ESC-L DASH LINE COUNT
  1070 REM PATCH EDIT CNTL-A TO MY ROUTINE
  1080 REM PATCH EDIT CNTL-I BACK TO INSERT FUNCTION
  1090 CALL-151
  1100 C081 C081
  1110 F800<F800.FFFFM
  1120 BLOAD S-C.ASM.MACRO.D000.VIDEX
  1130 300:A9 4C CD 00 E0 F0 12 8D 00 E0 A9 00 8D 01 E0 A9 D0 8D 02 E0 A9 CB 8D D1 03 60
  1140 300G
  1150 BLOAD SCM.PATCH
  1160 D3FF:20 00 F6
  1170 D4B1:19 F6
  1180 C0B0:0A 68
  1190 C0B0:0B 08
  1200 D4D3:40
  1210 DCB8:03 F6
  1220 DCC7:0B DC
  1230 C080
  1240 3D3G


  1000 *SAVE WETZEL'S PATCHES TO 1.1
  1010        .OR $F600
  1020        .TF SCM.PATCH
  1030 *----------------------------------------------------------
  1040 CH     .EQ $24
  1050 BASL   .EQ $28
  1060 YSAVE  .EQ $40
  1070 WBUF   .EQ $200
  1080 LCPROT .EQ $C080    LC Protect
  1090 LCWRT  .EQ $C083    LC Write enable
  1100 UCFLAG .EQ $D016    UC/LC Flag
  1110 BELL   .EQ $FF3A    Monitor Bell
  1120 *----------------------------------------------------------
  1130 CONTROL.A
  1140        CMP #$81     Was a CNTL-A entered
  1150        BNE ERROR    No - then signal error
  1160        LDA LCWRT    Write enable Language card
  1170        LDA LCWRT    
  1180        LDA UCFLAG   Get upper case flag
  1190        EOR #$FF     Reverse it 
  1200        STA UCFLAG   Put it back 
  1210        LDA LCPROT   Write protect Language card
  1220        RTS
  1230 ERROR
  1240        JSR BELL     Ring bell to signal error
  1250        RTS          Return
  1260 *----------------------------------------------------------
  1270 ESCAPE.C
  1280        CPX #0       Start of line?
  1290        BNE .2       No, rtn
  1300        LDY #0
  1310 .1     LDA MSG,Y    Get message
  1320        STA WBUF,Y   Put in buffer
  1330        STA (BASL),Y Put on screen (40-column)
  1340        INY
  1350        CPY #7       Finished ?
  1360        BNE .1       Not yet 
  1370        STY YSAVE
  1380        INY
  1390        STY CH       Tell assembler
  1400        TSX          this was an 
  1410        LDA #$CC     ESC-L so it will
  1420        STA $103,X   exec command
  1430        LDX YSAVE
  1440 .2     RTS
  1450 MSG    .AS -/CATALOG/

"One more beep and you're out!" Bob Sander-Cederlof

We have uncovered another neat new Apple accessory: a volume control for the speaker! If other people within earshot of your computer are trying to sleep, or just can't take another five minutes of bells, beeps, and buzzes, the WHISPER VOLUME CONTROL is for you. The Apple version works with II, II Plus, //e, or ///. All you have to do to install it is take the case off your Apple, unplug the speaker wire from the board, plug in the WVC cable, and plug the speaker wire into the other end of the WVC connector. You can also get WVC for the IBM/PC. The retail price is $22.95 for the standard version, or $25.95 with a headphone jack, from Information Dynamics Corp., 1251 Exchange Drive, Richardson, TX 75081. Phone (214)783-8090. Or if you like, buy them from us at $21 and $24, respectively.


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 $15 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $13 postage for other countries. Back issues are available for $1.50 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.)