Apple Assembly Line
Volume 3 -- Issue 3 December 1982

In This Issue...

Cross Assemblers continue to appear. We now have ready a version for the Intel 8048, and one for the yet unreleased Rockwell 65C02. More on the latter inside.

Don Lancaster, famous author of many books published by Howard Sams, says the Apple II is probably going to have a greater impact on history than the automobile or television! Perhaps verging on Apple-olatry, but you will surely enjoy his new book. See Bill's reviews inside.

If I am trying to learn how to program in assembly language, or to increase my skill at it, what (besides AAL) should I read? I strongly recommend Softalk, Nibble, Micro, and Call APPLE. Every month they publish excellent examples of assembly language programs which you can study, modify, and use. As for books, Roger Wagner's, Lance Leventhal's, and Don Lancaster's are my favorites at this time.


Making Internal JMPs and JSRs Relocatable Peter Meyer

A machine language routine is said to be relocatable if it can function properly regardless of its absolute location in memory. If a routine contains a JMP or a JSR to an INTERNAL address then it is not relocatable; if it is run in another part of memory then the internal JSR or JMP will still reference the former region of memory. JMPs and JSRs to subroutines at absolute locations (e.g. in the Monitor) do not impair the relocatability of a routine.

I will explain here a technique whereby you can, in effect, perform internal JSRs and JMPs without impairing the relocatability of your routine. There is a small price to pay for this: namely, an increase in the length of your routine. Your routine must be preceded by a 2-part Header Routine which is 43 bytes long. In addition, each internal JSR requires 8 bytes of code, and each internal JMP requires 11 bytes of code.

No tables or other data storage are required, except that three bytes must be reserved for a JMP instruction. These three bytes can be anywhere in memory, but must be at an absolute location. There are three bytes that normally are used only by Applesoft, namely, the ampersand JMP vector at $3F5 to $3F7. Since we are here concerned only with machine language routines in their own right, we can use the locations $3F5 to $3F7 for our own purposes. However, other locations would do just as well.

The technique is fully illustrated in the accompanying assembly language program. This routine consists of three parts:

(1) Header Part 1 (SETUP), which sets up a JMP instruction at VECTOR (at $3F5-$3F7, but could be different, as explained above) to point to Header Part 2.

(2) Header Part 2 (HANDLER), which is a 15-byte section of code whose task is to handle requests to perform internal JSRs and JMPs (more on this below).

(3) The main part of the routine, in which internal JSRs and JMPs (in effect) are performed using macro instructions.

When your routine (including the Header) is executed, the first thing that happens is that Header Part 1 locates itself (using the well-known JSR $FF58 technique), then places a JMP HANDLER at VECTOR. Thereafter a JMP VECTOR is equivalent to JMP HANDLER, and a JSR VECTOR is equivalent to a JSR HANDLER. The HANDLER routine handles requests from your routine for internal JSRs and JMPs. To perform a JSR to an internal subroutine labelled SUBR simply include the following code:

     HERE   LDA #SUBR-HERE-7 low byte of offset
            LDY /SUBR-HERE-7 high byte of offset
            TSX
            JSR VECTOR

As explained above, the JSR VECTOR is in effect a JSR HANDLER. The Header Part 2 code takes the values in the A and Y registers and adds them to an address which it obtains from the stack to obtain the address of SUBR. It then places this address in INDEX ($5E,5F) and executes "JMP (INDEX)".

An internal JMP, from one part of your routine to another, is performed in a similar manner. Suppose you wish to JMP from HERE to THERE. It is done as follows:

     HERE   LDA #THERE-HERE-7 low byte of offset
            LDY /THERE-HERE-7 high byte of offset
            TSX
            JSR $FF58
            JMP VECTOR

Since we are (in effect) performing a JMP, rather than a JSR, we do a JMP VECTOR rather than a JSR VECTOR. The other difference is that we have a JSR $FF58 following the TSX.

Clearly the sequence of instructions which allows you to perform a JMP or a JSR could be coded as a macro. The macros to use are shown in the accompanying program listing. By using macros an internal JMP or JSR can be performed with a single macro instruction bearing a very close resemblance to a real JSR or JMP instruction.

The following program, which consists of the Header Routine plus a demonstration routine, can be assembled to disk using the .TF directive. It can then be BRUN at any address and it will function properly. Thus it is relocatable, despite the fact that there are (in effect) an internal JMP and two internal JSRs performed.

When performing an internal JSR or JMP using my techniques, it is not possible to pass values in the registers, since these are required to pass information to the HANDLER routine. Nor is it advisable to try to pass parameters on the stack, even though the HANDLER routine does not change the value of the stack pointer. Better is to deposit values in memory and retrieve them after the transition has been made.

The HANDLER routine passes control to the requested part of your routine using a JMP indirect. (INDEX at $5E,5F, has been used in the accompanying program, but any other address would do as well, provided that it does not cross a page boundary.) This means that the section of your routine to which control is passed (whether or not it is a subroutine) may find its own location by inspecting the contents of the location used for the JMP indirect. This feature of this technique is also illustrated in the accompanying program, in the PRINT.MESSAGE subroutine.

The use of internal data blocks is something not normally possible in a relocatable routine, but it can be done if the techniques shown here are used.

This method of performing internal JSRs and JMPs in a relocatable routine may be simplified if the routine is intended to function as a subroutine appended to an Applesoft program. If the subroutine is appended using my utility the Routine Machine (available from Southwestern Data Systems), then it is not necessary to include the 47-byte Header Routine. Internal JMPs and JSRs can still be performed exactly as described above, except that the address of VECTOR must be $3F5-$3F7. This technique is not described in the documentation to the Routine Machine. A full explanation may be found in the Appendix to the documentation accompanying Ampersoft Program Library, Volume 4 (also available from Southwestern Data Systems).

 1010         .TF B.MEYER.1
 1020  *SAVE S.MEYER.1
 1030  *--------------------------------
 1040  *   SETUP AND HANDLER ROUTINES
 1050  *   TO ALLOW INTERNAL JSRS AND
 1060  *   JMPS IN A RELOCATABLE MACHINE
 1070  *   LANGUAGE ROUTINE
 1080   
 1090  *   BY PETER MEYER, 11/3/82
 1100  *--------------------------------
 1110  *   LOCATIONS
 1120   
 1130  INDEX      .EQ $5E,5F
 1140  STACK      .EQ $100 - $1FF
 1150  VECTOR     .EQ $3F5 - $3F7
 1160  *--------------------------------
 1170  *   MACRO DEFINITIONS
 1180    
 1190         .MA JSR
 1200  :1     LDA #]1-:1-7
 1210         LDY /]1-:1-7
 1220         TSX
 1230         JSR VECTOR
 1240         .EM
 1250   
 1260         .MA JMP
 1270  :1     LDA #]1-:1-7
 1280         LDY /]1-:1-7
 1290         TSX
 1300         JSR $FF58
 1310         JMP VECTOR
 1320         .EM
 1330  *--------------------------------
 1340  *   HEADER PART 1
 1350   
 1360  SETUP  JSR $FF58    FIND OURSELVES
 1370         TSX
 1380         CLC
 1390         LDA #HANDLER-SETUP-2
 1400         .DA #$7D,STACK-1   FORCE ABS,X MODE
 1410         STA VECTOR+1
 1420   
 1430         LDA /HANDLER-SETUP-2
 1440         ADC STACK,X
 1450         STA VECTOR+2
 1460   
 1470         LDA #$4C     "JMP"
 1480         STA VECTOR
 1490         BNE MAIN.ROUTINE   ALWAYS
 1500  *--------------------------------
 1510  *   HEADER PART 2
 1520   
 1530  HANDLER
 1540   
 1550  *   ON ENTRY A,Y HOLD OFFSET
 1560  *   FOR JMP OR JSR FROM ROUTINE
 1570  *   X IS STACK POINTER FROM BEFORE LAST JSR
 1580   
 1590         CLC
 1600         .DA #$7D,STACK-1   FORCE ABS,X MODE
 1610         STA INDEX
 1620         TYA
 1630         ADC STACK,X
 1640         STA INDEX+1
 1650         JMP (INDEX)
 1660  *--------------------------------
 1670  *      MAIN ROUTINE, FOR EXAMPLE
 1680  *--------------------------------
 1690  MSG    .EQ $06 AND $07
 1700  CH     .EQ $24
 1710  CV     .EQ $25
 1720  INVFLG .EQ $32
 1730  COUNT  .EQ $3C
 1740  SETTXT .EQ $FB39
 1750  VTABZ  .EQ $FC24
 1760  HOME   .EQ $FC58
 1770  COUT   .EQ $FDED
 1780  *--------------------------------
 1790  MAIN.ROUTINE
 1800         JSR SETTXT
 1810         JSR HOME
 1820  MAIN.LOOP
 1830         LDA #190
 1840         STA COUNT
 1850  .1     LDA #AALQT-PRINT.MESSAGE
 1860         STA MSG
 1870         LDA /AALQT-PRINT.MESSAGE
 1880         STA MSG+1
 1890         >JSR PRINT.MESSAGE
 1900         DEC COUNT
 1910         BNE .1
 1920         LDA #LONGQT-PRINT.MESSAGE
 1930         STA MSG
 1940         LDA /LONGQT-PRINT.MESSAGE
 1950         STA MSG+1
 1960         >JSR PRINT.MESSAGE
 1970         >JMP FORWRD
 1980   
 1990  *--------------------------------
 2000  PRINT.MESSAGE
 2010         CLC
 2020         LDA MSG      CHANGE RELATIVE ADDRESS TO
 2030         ADC INDEX    AN ABSOLUTE ADDRESS, BY
 2040         STA MSG      ADDING THE OFFSET
 2050         LDA MSG+1
 2060         ADC INDEX+1
 2070         STA MSG+1
 2080         LDY #0       POINT AT FIRST CHAR OF MSG
 2090  .1     LDA (MSG),Y  GET NEXT CHAR OF MSG
 2100         BMI .2       IT IS LAST CHAR
 2110         ORA #$80     MAKE APPLE VIDEO FORM
 2120         JSR COUT     PRINT IT
 2130         INY          ADVANCE POINTER
 2140         BNE .1       ...ALWAYS
 2150  .2     JMP COUT     PRINT AND RETURN
 2160  *--------------------------------
 2170  *   256 BYTES TO JUMP OVER, JUST FOR ILLUSTRATION
 2180   
 2190         .BS $100
 2200  *--------------------------------
 2210  *   TOGGLE INVERSE FLAG, AND HOME CURSOR
 2220   
 2230  FORWRD LDA INVFLG
 2240         EOR #$C0
 2250         STA INVFLG
 2260         LDA #0
 2270         STA CH
 2280         STA CV
 2290         JSR VTABZ
 2300         >JMP MAIN.LOOP
 2310  *--------------------------------
 2320  AALQT  .AT /AAL /
 2330  LONGQT .HS 0D0D
 2340         .AS / A P P L E   A S S E M B L Y   L I N E /
 2350         .HS 0D02
 2360         .AT /  S - C   S O F T W A R E   C O R P .  /

Add Bit-Control to Apple Monitor Bob Sander-Cederlof

The other day someone sent me a disk with an article for AAL on it as a binary file. The codes in the file were all ASCII characters, but they were nevertheless not compatible with any word processors I had around.

All blanks were coded as $A0 (hi-bit on), and all other characters were coded in the range $00-$7F (hi-bit off). Otherwise, everything was compatible with my favorite word processor (the one I am still in the process of finishing).

I need to have all the hi-bits set to one in the file, or in the memory image after BLOADing the file. That's the motivation for the following neat enhancement to the Apple monitor.

The enhancement hooks in through the control-Y user command vector. By merely typing:

     *80FF<2000.3FFF^Y    (^Y means control-Y)

I set all the hi-bits between $2000 and $3FFF.

The "80FF" in the command line above is the magic part of this enhancement. The last two digits are ANDed with every byte in the specified range, clearing every bit which is zero in those two digits. By using $FF, no bits are cleared. Other values for these two digits will clear whatever bits you wish.

The first two digits are ORed into every byte in the specified range, setting every bit which is one in the two digits. $80 in my example sets the top bit in every byte.

The code is designed to be BRUN from a binary file, and it is completely relocatable. You can BRUN it anywhere in memory that you have room for 36 bytes. That is why the SETUP code is longer than the actual control-Y code!

The SETUP routine first discovers where in memory it is running, and then sets up the control-Y vector in page 3 to point at the BITS code. The discovery is done in the usual way, by JSRing to a guaranteed RTS in the monitor ROM, at $FF58. This leaves a return address just below the stack pointer. I pick up that address and add the difference between that and BITS to get the appropriate control-Y vector pointer.

Line 1200 needs a little explanation. My assembler automatically switches to page zero addressing mode if the address is less than $100. STACK-1 is less than $100, so "ADC STACK-1,X" would assemble as though I wrote "ADC $FF,X". Indexed addressing in page zero mode stays in page zero, wrapping around. If X=3, "ADC $FF,X" would reference location $02 in page zero rather than $102. Therefore I had to use the ".DA #$7D" to force the assembler to use a full 16-bit address mode. (Some assemblers have a special way of forcing 16-bit addressing in cases like this; others require special marks to get zero-page modes.)

BITS itself is very straightforward code. The monitor leaves the starting address of the specified range in A1 ($3C,3D), the ending address in A2 ($3E,3F), and the mask in A4 ($42,43). The subroutine at $FCBA increments A1 and compares it to A2, leaving carry clear if the range is not yet complete.

 1000  *--------------------------------
 1010  *      MONITOR CTRL-Y COMMAND
 1020  *
 1030  *      TO SET AND CLEAR ANY COMBINATION
 1040  *      OF BITS IN A RANGE OF MEMORY
 1050  *
 1060  *      *MASK<ADR1.ADR2Y   (WHERE Y MEANS CTRL-Y)
 1070  *
 1080  *      MASK = XXYY  BITS = 0 IN YY WILL BE CLEARED
 1090  *                   BITS = 1 IN XX WILL BE SET
 1100  *--------------------------------
 1110  A1     .EQ $3C
 1120  A4L    .EQ $42
 1130  A4H    .EQ $43
 1140  STACK  .EQ $100
 1150  *--------------------------------
 1160  SETUP  JSR $FF58    FIND SELF FIRST
 1170         TSX
 1180         CLC
 1190         LDA #BITS-SETUP-2
 1200         .DA #$7D,STACK-1    FORCE ABS,X MODE
 1210         STA $3F9          MONITOR CTRL-Y VECTOR
 1220         LDA /BITS-SETUP-2
 1230         ADC STACK,X
 1240         STA $3FA
 1250         RTS
 1260  *--------------------------------
 1270  BITS   LDA (A1),Y   GET NEXT BYTE IN SPECIFIED RANGE
 1280         AND A4L      CLEAR BITS USING LO-BYTE OF MASK
 1290         ORA A4H      SET BITS FROM HI-BYTE OF MASK
 1300         STA (A1),Y   STORE MODIFIED BYTE
 1310         JSR $FCBA    ADVANCE POINTER
 1320         BCC BITS     MORE IN RANGE
 1330         RTS          FINISHED
 1340  *--------------------------------

Assembly Listings on TEXT Files Bob Sander-Cederlof

Today Jules Gilder called, asking for patches to allow sending the assembly listing to a TEXT file. He is in the process of writing a book, and wanted the listings on TEXT files so they could be automatically typeset.

My first response was a familiar one: "It is a very difficult problem, because of the interaction of .IN and .TF files."

"But I don't need .TF or .IN files!", he swiftly parried.

Suddenly in a flash of insight I saw that it could be EASILY done. All that is needed is to patch the .TF directive so that it opens a TEXT file, but does not set the TF-flag. Then all listing output will go to the text file, and the object code will go to the current origin or target address.

Here are the patches for the motherboard version:

       :$2998:A5 60 D0 03 20 6A 2A 4C 04 1F

And the language card version:

       :$C083 C083 EAE4:A5 60 D0 03 20 B6 EB 4C 50 E0

Note that the two "C083's" above write-enable the language card so the patches will be effective.

After the patches are installed, a .TF directive will direct the listing to your text file. Here is an example:

            .TF T.LISTING
     SAMPLE LDA #3
            STA $06
            RTS

Just remember that the normal use of .TF is not available when this patch is in place; also that .IN should not be used. Using .IN would turn off the listing output, directing it back to the screen.


Quickie No. 1 Bob Sander-Cederlof

To merge selected bits from one byte with the rest of the bits of another byte:

       Code           Example
     LDA MASK         00011111
     EOR #$FF         11100000
     ORA BYTE1        111xxxxx
     STA TEMP
     LDA BYTE2        yyyzzzzz
     ORA MASK         yyy11111
     AND TEMP         yyyxxxxx

Add a New Feature to ES-CAPE Bill Linn

Since most of the owners of ES-CAPE also subscribe to the Apple Assembly Line, I thought I would pass on some neat patches here.

The automatic line numbering feature in ES-CAPE can be enhanced by the following patches, which make the numbers track whatever you type. With these patches, each time an Applesoft line is changed via hitting return or via a CHANGE command, that line number plus the current increment becomes the next automatic line number to be generated when the space bar is pressed. (Now ES-CAPE works more like the S-C Macro Assembler.)

Here are the patches for the mother-board version:

    LOAD ES-CAPE
    CALL -151
    E44:69 00 EA
    102E:4C 51 9B
    1C51:A5 51 8D 34 9B 18 A5 50 6D 35 9B 8D 33 9B
    :90 03 EE 34 9B A5 25 F0 02 C6 25 4C 34 8F
    3D0G
    SAVE ES-CAPE REVISED

Here are the patches for the RAM-card version:

    LOAD ES-CAPE LC
    CALL -151
    E41:69 00 EA
    102B:4C 60 E1
    1B60:A5 51 8D 51 E1 18 A5 50 6D 52 E1 8D 50 E1
    :90 03 EE 51 E1 A5 25 F0 02 C6 25 4C 31 D6
    3D0G
    SAVE ES-CAPE LC REVISED

I believe these patches make ES-CAPE even easier to use. If any of you still have not upgraded your AED II copies to ES-CAPE, send me $10 and your old disk and I'll return a new version and the great new manual.

I am continuing to work on ES-CAPE, hoping for a new version around the middle of next year. What new features would you like? The main ones we have in mind are 80-column support, renumber, and merge. If you have some good ideas, drop me a line at 3199 Hammock Creek, Lithonia, GA 30058.


Quickie No. 2 Bob Sander-Cederlof

To test a byte in memory without disturbing any registers:

     INC BYTE
     DEC BYTE       Restore value, and test against zero
     BEQ ....

Applesoft Source, Completely Commented Bob Sander-Cederlof

For several years I have been working on this. I even bought an assembler from another company just to get the promised source code of Applesoft that came in the package, but I was very disappointed. (No complaints, it was intended as a "freebie" with their assembler.) I wanted LOTS of comments, MEANINGFUL labels, and I wanted it in the format of the S-C Macro Assembler.

Now I have it. And you can have a copy, on two diskettes, for only $50. It comes in an encrypted form, with a driving program which creates the source code files on your machine with the aid of the Applesoft image in ROM or RAM. The encryption is meant to protect the interests of Apple and Microsoft.

You need two disk drives to assemble Applesoft, a printer to get a permanent listing, and of course you need the S-C Macro Assembler. The source code is split into more than a dozen source files, assembled together using a control file full of .IN directives. After assembling and printing, you will have well over 100 pages of the best documentation of Applesoft internals available anywhere.

In the process of writing the comments, I discovered some very interesting bugs. Some have been published before, and others I have never heard of. Just for fun, try this:

     ]A%=-32768     (you get an error message, correctly)
     ]A%=-32768.00049    (No error message!)
     ]PRINT A%           (You get 32767!)

Or open your disk drive doors, just in case, and type:

     ]LIST 440311

That last one can be disastrous! Any use of a six-digit line number (illegal, but not caught by Applesoft) between 437760 and 440319 will cause a branch to a totally incorrect place. For example, GOTO 440311 branches to $22D9!

The causes of these and other interesting phenomena, as well as some suggested improvements resulting in smaller/faster code, are documented in my comments.


Quickie No. 3 Bob Sander-Cederlof

To shift a two-byte value right one bit with sign extension:

     LDA HI.BYTE
     ASL            SIGN BIT INTO CARRY
     ROR HI.BYTE    HI BYTE RIGHT ONE, CARRY (SIGN) INTO BIT 7
     ROR LO.BYTE

New Enhanced 6502 Nearly Here! Bob Sander-Cederlof

Nigel Nathan from Micro-Mixedware in Stow, MA, sent me some copies of reference material for the new 65C02 chip. This is the enhanced CMOS version, soon to be available from GTE and Rockwell.

Nigel's interest was that I might produced an enhanced S-C Macro Assembler to accommodate the new opcodes and addressing modes. I not only might...I did it right away! It is ready now, although you may have some difficulty getting the chips for a few more months.

Rockwell is sampling the 65C02 now, and scheduled for production in February. Rockwell is also readying an entire family of CMOS products to go with the 65C02, including 2Kx8 CMOS static RAM and multi-byte parallel interfaces.

The 65C02 is expected to be plug-compatible with the 6502 in your Apple II. In fact, Cliff Whitaker of Rockwell told me that the first chips they made were tested by plugging them into Apples. Hopefully you will be able to simply plug them in and start using the new opcodes. If true, then I will probably become a source for these chips.

What enhancements did they make? According to the GTE document, some "bugs" in the 6502 design were corrected:

The Rockwell literature makes reference to the following new opcodes, or new addressing modes for old opcodes:

     80   BRA rel    Branch Always

     12   ORA (zp)   )
     32   AND (zp)   )
     52   EOR (zp)   )  new addressing mode:
     72   ADC (zp)   )
     92   STA (zp)   )  zero-page indirect
     B2   LDA (zp)   )
     D2   CMP (zp)   )  without indexing
     F2   SBC (zp)   )

     04   TSB zp     Test and set bits
     14   TRB zp     Test and reset bits
     34   BIT zp,X   new addressing mode for BIT
     64   STZ zp     Store Zero
     74   STZ zp,X     "    "

     07-77   RMB b,zp   Reset bit b in zp
     87-F7   SMB b,zp   Set bit b in zp

     89   BIT imm    new addressing mode for BIT

     1A   INC        Increment A-register
     3A   DEC        Decrement A-register
     5A   PHY        Push Y
     7A   PLY        Pull Y
     DA   PHX        Push X
     FA   PLX        Pull X

     0C   TSB abs    Test and set bits
     1C   TRB abs    Test and reset bits
     3C   BIT abs,X  new addressing mode for BIT
     7C   JMP (abs),X   new addressing mode for JMP
     9C   STZ abs    Store zero

     9E   STZ abs,X  Store zero

     0F-7F   BBR b,zp,rel   Branch if bit b in zp is zero
     8F-FF   BBS b,zp,rel   Branch if bit b in zp is one

Let your imagination run wild with all the great ways to use these new opcodes! If you feel the need for the ability to assemble them now, the Cross Assembler upgrade for the 65C02 is available for $20 to subscribers of the Apple Assembly Line who already own the S-C Macro Assembler.


Toggling Upper/Lower Case in the S-C Macro Assembler Steven Mann

[ Psychology Dept., Univ. of So. Dakota, P. O. Box 7187, Grand Forks, ND 58202 ]

I have made the necessary modifications to the assembler (and to my Apple) that allow me to enter lower case characters in my source programs, but have found that I like to have all upper case in certain sections (the labels and opcodes) and mixed (mostly lower) case only in the comment field. In order to do accommodate this most effectively, I wanted to be able to toggle back and forth from upper to lower case while I was entering my source code.

The simplest solution for me was to patch the assembler to accept one of the escape key sequences as an upper/lower case toggle. From back issues of AAL I was able to determine that a table of address vectors for the escape keys A-L is maintained from $1467 thru $1482 ($D467 thru $D482 in the language card version). Each two-byte entry is the address-1 (low byte first) of the routine that will handle that particular escape sequence.

Certain of the sequences are already taken (e.g. ESC L loads a disk file; ESC I,J,K, and M move the cursor, etc.) Since I don't use the ESC A,B,C,D cursor moves, I selected the ESC A sequence as the code for the case toggle. I also used ESC C for "CATALOG", as suggested by Bill Morgan some months back in these pages.

Implementation of the toggle is accomplished with the following patches to the HELLO program (for the RAM version of the assembler.) First, line 50 should be changed to:

     50 PRINT A: IF A=1 THEN PRINT CHR$(4)"BLOAD S-C.ASM.MACRO"
        :GOSUB 200: CALL 4096

The subroutine at 200 is as follows:

     197 REM
     198 REM ESC A TOGGLES UP/LOW CASE
     199 REM
     200 POKE 5229,109:POKE 5230,165
     210 FOR I=1 TO 9:READ J:POKE 48350+I,J:NEXT
     220 DATA 173,22,16,73,255,141,22,16,96
     230 RETURN
     240 REM
     250 REM ROUTINE RESIDES AT $BCDF
     260 REM
     270 REM CODE IS AS FOLLOWS:
     280 REM
     290 REM     LDA $1016
     300 REM     EOR #FF
     310 REM     STA $1016
     320 REM     RTS
     330 REM

Note that I put the patch code at $BCDF, which is in an unused area inside DOS 3.3. If you have already used this area for other purposes, you can tack the patch on to the end of the assembler image instead.

The actual code is very simple. The upper/lower case flag is stored at $1016 and is either a $00 or a $FF (in binary all zeros or all ones.) Toggling the flag involves loading the current flag and EORing it with #$FF. This will cause all set bits to be cleared and all clear bits to be set, so the zeros become ones and the ones become zeros. Thus, an #$FF byte becomes a #$00 or a #$00 becomes an #$FF. The new flag value is then stored back in $1016.

For the language card version the program is basically the same, but slightly longer due to the need to first write enable the language card. The code looks like this:

     PATCH   LDA $C083       Two of these in succession
             LDA $C083       write-enable the card
             LDA $D016       Get the flag
             EOR #$FF        Complement it
             STA $D016       Save the new flag
             LDA $C080       Write protect the card
             RTS

I put the code in the same place as in the RAM version ($BCDF) and put it into memory from the LOAD LCASM exec file which loads the assembler onto the card. Two lines need to be added to the file. Between lines 1070 and 1080 (assuming your version has not been modified) you need to place these two lines:

     1072 D469:DE BC
     1074 BCDF:AD 83 C0 AD 83 C0 AD 16 D0 49 FF 8D 16 D0 AD 80 C0 60

The first line places the address of the case toggle handler in the escape vector table and the second line contains the assembled source code listed above. If you are not sure how to modify the LOAD LCASM file see the step by step description given in the May 1982 AAL (page 3).

After you have made the patch, experiment with the toggle. One particularly valuable feature is that you can toggle the case within a single line as you enter the line. This means that you can enter the label and opcode in upper case, tab over to the comment field, toggle the case flag, and then enter your comment in lower case.

I have found using the case toggle to be easy while improving the appearance and readability of my source listings. The only problem I have encountered so far is that the flag can not be toggled from within the edit mode (either case can be used in the edit mode, but you can't change the case in the middle of editing.) If any of you find a way to add this to the assembler be sure to let me know.

[ P.S. If you haven't put in the automatic catalog yet, here is an easy way. Add the following line to your LOAD LCASM file:

     1076 D46D:6D A5

and then ESC C will do a catalog as long as you don't mind having to hit return at the end of the catalog. For the motherboard version, add:

     205 POKE 5225,222: POKE 5226,188

in the subroutine I've added to the HELLO program. ]

{Ouch! Why didn't I think of that? One caution: With this method ESC C will do a CATALOG even if you are in the middle of typing a line. . . . Bill Morgan}


Save Garbage by Emptying Arrays Bob Sander-Cederlof

As we all know, Applesoft programs which use a significant number of strings can appear to die for long periods of time while "garbage collection" is performed. Many techniques have been published to reduce the frequency of garbage collection, or reduce the amount of garbage to be collected, or to speed up the collector.

Randy Wiggington published a much faster garbage collector in "Call-A.P.P.L.E.", January, 1981, pages 40-45. The source code is available in S-C format on the Dallas Apple Corps disk of the month for March, 1981. (Copies available for $10 from me.) Randy's speed improvement is gained by searching for the highest 16 strings in memory at once, rather than just the highest one string. It is much faster, but not the fastest. The time for collection still varies quadratically with the number of strings in use.

Cornelius Bongers wrote the fastest collector I have seen. It was published in "MICRO -- The 6502/6809 Journal", August, 1982, pages 90-97. Corny's method is very straightforward, and has the advantage that execution time varies linearly with the number of strings in use. His method also has the disadvantage that it does not work if any strings contain any characters with the high bit on. (Applesoft normally does not generate any strings with high-bit-set-characters, but you can do it with oddball code.) I typed in the program from MICRO, made a few changes here and there, and found it to be lightning fast.

I installed the linear method in a client's program, and his garbage collection time went from 100 seconds after printing every four lines, to only 1/4 second. Other changes, such as the one described below, cut the interval of collection to once every 12 lines.

It so happens that strings which are empty do not increase the garbage collection time. Many times in Applesoft programs a string array is filled with data from a disk file, processed, and then filled with fresh data, and so on. By emptying the array before each pass at reading the disk file, the number of active strings can be greatly reduced.

One of my programs was like this: the one that prints your mailing label so that the AAL gets to you every month. Before reading each file (the list is divided into about 12 different files, based on zip code and other factors), I wrote a loop that set each string in the array to a null string, and then forced garbage collection:

     FOR I = 1 TO NH : A$(I)="" : NEXT : F = FRE(0)

The only problem with this was that the loop takes so long! About ten seconds, anyway, enought to try my patience.

All the above motivated me to write the following little subroutine, which nulls out (or empties) a string array. Bongers included an array eraser in his article, which completely released an array; however, that requires re-DIMming the array each time. My program is faster in my case, and it was fun to write.

The program is listed below with the origin set at $300, so "CALL 768,arrayname" will activate it. It happens to be fully relocatable, so you can load it anywhere in memory you wish. You could easily add it to your Applesoft program with Amper-Magic or Amperware or The Routine Machine.

I used three subroutines inside the Applesoft ROMs. CHKCOM gives SYNTAX ERROR unless the next character is a comma. I use it to check for the comma that separates the call address from the array name. CHKSTR checks to make sure that the last-parsed variable is a string variable, and gives TYPE MISMATCH if not. GETARYPT scans an array name and returns the address of the start of the array in variable space.

If you look at page 137 of your Applesoft reference manual, you will see in the third column a picture of a string array. (Notice first the error: the signs of the first and second bytes of the string name are just the reverse of what the book says.) My program looks at the number of dimensions to determine the size of the preamble (the number of bytes before you get to the actual string pointers).

I use the preamble size to compute the starting address of the string pointers, and the number of bytes of string pointers that there are. Then a tight little loop stores zeros on top of all the descriptors.

The Applesoft program below illustrates the CLEAR subroutine in action.

 100 A$(25),B$(5)
 105 FOR I = 0 TO 5: B$(I) = "ABCD": NEXT
 110 FOR I = 0 TO 25: N = INT(RND(1)*5)+1
 120 FOR J = 1 TO N: A$(I) = A$(I) + CHR$(RND(1)*26+65): NEXT
 130 NEXT: PRINT: PRINT
 140 CALL 768,A$
 150 FOR I = 0 TO 25: PRINT LEN(A$(I))":"A$(I),: NEXT
 155 PRINT: PRINT
 160 FOR I = 0 TO 5: PRINT B$(I),: NEXT
 170 GOTO 110


 1000  *SAVE S.STRING ARRAY CLEAR
 1010  *--------------------------------
 1020  *      CLEAR STRING ARRAY
 1030  *--------------------------------
 1040  GETARYPT   .EQ $F7D9
 1050  CHKSTR     .EQ $DD6C
 1060  CHKCOM     .EQ $DEBE
 1070  *--------------------------------
 1080  FAC    .EQ $9D
 1090  LOWTR  .EQ $9B,9C
 1100         .OR $300
 1110  *--------------------------------
 1120  CLEAR
 1130         JSR CHKCOM
 1140         JSR GETARYPT
 1150         JSR CHKSTR
 1160         LDY #4       COMPUTE SIZE OF PREAMBLE
 1170         LDA (LOWTR),Y     # OF DIMS
 1180         ASL               *2, CLEAR CARRY
 1190         ADC #5            +5
 1200         STA FAC      SAVE PREAMBLE SIZE
 1210         LDY #2       POINT AT OFFSET
 1220         SEC          COMPUTE ARRAY LENGTH
 1230         LDA (LOWTR),Y
 1240         SBC FAC
 1250         PHA          # BYTES IN PARTIAL PAGE
 1260         INY
 1270         LDA (LOWTR),Y
 1280         SBC #0
 1290         TAX          # WHOLE PAGES
 1300         CLC          POINT AT BEGINNING OF DATA
 1310         LDA LOWTR
 1320         ADC FAC
 1330         STA LOWTR
 1340         BNE .2
 1350         INC LOWTR+1
 1360  .2     LDY #0
 1370         TXA          # WHOLE PAGES
 1380         BEQ .4 
 1390         TYA          SET A=0
 1400  .3     STA (LOWTR),Y
 1410         DEY
 1420         BNE .3
 1430         INC LOWTR+1
 1440         DEX          COUNT WHOLE PAGES
 1450         BNE .3
 1460  .4     PLA
 1470         BEQ .6       FINISHED
 1480         TAY
 1490         LDA #0
 1500  .5     DEY
 1510         STA (LOWTR),Y
 1520         BNE .5
 1530  .6     RTS

Splitting Strings to Fit Your Display Bob Sander-Cederlof

Printing text on the screen, or even on a printer, is not as easy at it ought to be. The problem is splitting words at the right margin. Word processors handle it nicely, but what do you do in an Applesoft program?

You might write a subroutine, in Applesoft, which looks for the first space between words before a specified column position. The subroutine could split a string at the space into two substrings: one containing the next display line, the other the remainder of the original string.

You might. But believe me, it builds up a lot of garbage strings and takes a long time to execute. If you like the general approach, you might try coding the subroutine in assembly language. You can avoid garbage build-up and save lots of running time, so it is probably worth the effort. Especially since I already wrote the program for you!

The program is written to be called from an ampersand parser like the one in last month's article on REPEAT/UNTIL. Or, you can use it with Amper-Magic, Amperware, The Routine Machine, etc. It is fully relocatable, having no internal data or JMP/JSR addresses. I set the origin to $300, but it can be loaded and used anywhere without re-assembly.

Here is an Applesoft program to show how to call SPLIT:

     100  POKE 1014,0: POKE 1015,3
     120  FOR N = 5 TO 40 STEP 3: GOSUB 1000: NEXT : END 
     1000 A$ = "NOW IS THE TIME FOR ALL GOOD MEN TO COME
          TO THE AID OF THEIR PARTY."
     1005  & ,A$,B$,N
     1010  PRINT B$
     1020  IF A$ <  > "" THEN 1005
     1025  PRINT 
     1030  RETURN 

Call SPLIT with three parameters. The first (A$ above) is the source string, which will be split. After SPLITting, the remainder string will be left in A$.

The second parameter, B$ above, will receive the left part, including the last complete word, up to N (the 3rd parameter) characters. If there is no space in the left N characters, exactly N characters will be split.

Here are some of the printouts from the test program:

     N=5            N=11            N=20
     -----          -----------     --------------------
     NOW            NOW IS THE      NOW IS THE TIME FOR
     IS             TIME FOR        ALL GOOD MEN TO COME
     THE            ALL GOOD        TO THE AID OF THEIR
     TIME           MEN TO COME     PARTY.
     ...etc.        ...etc.
 1000  *SAVE S.SPLIT
 1010  *--------------------------------
 1020  *      & SPLIT,A$,B$,W
 1030  *
 1040  *      A$ -- SOURCE STRING
 1050  *      W  -- MAXIMUM WIDTH OF SPLIT
 1060  *
 1070  *      B$ -- LEFT W CHARS OF A$
 1080  *      A$ -- REST OF A$
 1090  *
 1100  *--------------------------------
 1110         .OR $300
 1120         .TF B.SPLIT
 1130  *--------------------------------
 1140  LINNUM     .EQ $50,51
 1150  DPTRA      .EQ $06,07
 1160  DPTRB      .EQ $08,09
 1170  SPTRA      .EQ $FE,FF
 1180  *--------------------------------
 1190  AS.CHKCOM  .EQ $DEBE
 1200  AS.PTRGET  .EQ $DFE3
 1210  AS.GETADR  .EQ $E752
 1220  AS.FRMNUM  .EQ $DD67
 1230  *--------------------------------
 1240  SPLIT  JSR AS.CHKCOM     GET COMMA
 1250         JSR AS.PTRGET     GET SOURCE STRING
 1260         STA DPTRA
 1270         STY DPTRA+1
 1280         JSR AS.CHKCOM     ANOTHER COMMA
 1290         JSR AS.PTRGET     GET TARGET STRING
 1300         STA DPTRB
 1310         STY DPTRB+1
 1320         JSR AS.CHKCOM     ANOTHER COMMA
 1330         JSR AS.FRMNUM
 1340         JSR AS.GETADR     GET MAXIMUM WIDTH
 1350         LDY #2
 1360         LDA (DPTRA),Y
 1370         STA SPTRA+1
 1380         STA (DPTRB),Y
 1390         DEY
 1400         LDA (DPTRA),Y
 1410         STA SPTRA
 1420         STA (DPTRB),Y
 1430         DEY
 1440         LDA LINNUM
 1450         CMP (DPTRA),Y
 1460         BCC .1
 1470         LDA (DPTRA),Y     A$ SHORTER THAN OR SAME AS W
 1480         STA (DPTRB),Y
 1490         LDA #0
 1500         STA (DPTRA),Y
 1510         RTS
 1520  *--------------------------------
 1530  .1     TAY
 1540  .2     LDA (SPTRA),Y     LOOK AT SPLIT BOUNDARY
 1550         CMP #$20          FOR A BLANK
 1560         BEQ .3            FOUND ONE
 1570         DEY
 1580         BNE .2            BACK UP THE SPLIT
 1590  *---NO BLANK IN W CHARS----------
 1600         LDA LINNUM
 1610         BNE .4       ...ALWAYS
 1620  *--------------------------------
 1630  .3     TYA
 1640         INY          SKIP OVER BLANK
 1650         STY LINNUM
 1660  .4     LDY #0       LENGTH OF B$
 1670         STA (DPTRB),Y
 1680         SEC
 1690         LDA (DPTRA),Y     LENGTH OF A$
 1700         SBC LINNUM
 1710         STA (DPTRA),Y
 1720         INY
 1730         CLC
 1740         LDA (DPTRA),Y
 1750         ADC LINNUM
 1760         STA (DPTRA),Y
 1770         INY
 1780         LDA (DPTRA),Y
 1790         ADC #0
 1800         STA (DPTRA),Y
 1810         RTS
 1820  *--------------------------------

Enhancing Your Apple II (A Review) Bill Morgan

Don Lancaster, the well-known author of electronics books for the hobbyist (and a subscriber to AAL), has now entered the Apple arena in a big way. His latest book, "Enhancing Your Apple II, Vol. 1", promises to be the start of a long series of easy-to-use guides to the important internal workings of the Apple.

The main enhancements he offers in this volume are simple modifications to the Apple's video circuitry, to allow EXACT software access to the video timing. This permits your program to play all sorts of tricks with the display modes. There is also a wealth of information on the Apple's techniques of video storage and output.

The basic hardware modification is a single wire from an IC in the video circuitry to either the cassette or the game input. With this wire and a little bit of code, it is easy to switch display modes between screen scans, avoiding a lot of messy glitches on the screen. With more code, and careful timing, you can lock the processor to the display timing and switch between text and graphic modes (hi-res or lo-res) in mid-line.

There is also a very good 60-page chapter on disassembling and understanding other people's programs. Don presents a novel technique of color-coding a printout of a monitor disassembly, to bring out the structure of a program and the function of each routine. The example program is Apple's High-Res Character Generator, from the DOS Toolkit. He later uses the information discovered about the character generator and the Hi-Res display to develop a slower and smoother scrolling routine for Hi-Res text.

He shows us other enhancements, as well. There are two different ways to attach a modulator's output line to your TV set, avoiding that clumsy little switch box that the manufacture gives us. How about a programmable color-killer circuit? With this one you can have software control of color vs. black-and-white display. There are sections about generating extra colors, in both Hi-Res and Lo-Res graphics.

In the back of the book are postcards for sending feedback and ordering other materials. All the code in the book (26 programs) can be ordered on diskette, for $14.95. He uses the DOS Toolkit Assembler, but we plan to talk to him about providing the programs in S-C format. You can also order a kit of the parts for all of the hardware modifications he describes. That kit costs only $11.95 + shipping, from a dealer in Oklahoma. Future plans include more volumes of enhancements and a possible bulletin board system for updates to the books.

All in all, "Enhancing Your Apple II" looks to be an important and useful book. Like all of Lancaster's books, it is published by Howard W. Sams. It is 232 pages long, size 8 1/2 X 11 inches, and sells for $15.95. We have ordered a stock here at S-C, and will sell them for $15.00 + postage.

For Volume 2 of the "Enhancing" series, he has promised us more video techniques, a keyboard enhancer, something called an "Adventure Emergency Toolkit", graphics software for daisy-wheel printers, a two-dollar interface for the BSR controller, and much more. I'm looking forward to it!

This is a good time to mention another of Don's books, which has received too little attention. I am speaking of "The Incredible Secret Money Machine". Despite the title, it is not a get-rich-quick pamphlet, but rather a very, very useful guide to starting and operating a free-lance technical or craft business. "Money Machine" is 160 pages of tightly packed information on strategy and tactics, getting started, and dealing with customers, suppliers, and the government.

There is enough practical advice on communication, both verbal and graphic, to make up several courses in advertising and technical writing. Bob and I refer to this book regularly, and have long felt that it is one of the best books around for the budding entrepeneur. We have also ordered "Money Machine", and will sell it for $7.50 + postage.

Last minute addition: We just received a review copy of another new book from Don Lancaster, Micro Cookbook Vol. 1 - Fundamentals. This one is a very basic introduction to microcomputer principles. He talks about how to learn and what to learn, and introduces some hardware fundamentals. He also promises Vol. 2, about Machine-Language Programming. It looks very good; I'll have more details next month. We especially like this sentence at the end of the Preface: This book is dedicated to the 6502.


Quickie No. 4 Bob Sander-Cederlof

To print a two byte value in hexadecimal:

     LDA HI.BYTE
     LDX LO.BYTE
     JSR $F941

Clarification on Loading the RAM Card Paul Schlyter

Last month Bob S-C told how to use an EXEC command file without ENDing an Applesoft program. His example may have obfuscated the process of loading a file into a RAM card, because it really is not necessary to use an EXEC file for this purpose.

You can BLOAD into the RAM card without leaving Applesoft, contrary to Bob's information, by merely write-enabling it. The soft-switches $C081 and $C089 write-enable the RAM card (with bank 2 or bank 1 at $D000, respectively), leaving the motherboard ROMs read-enabled. This means everything you write goes into the RAM card, and everything you read comes from the motherboard ROMs. Thus you can simply BLOAD into the RAM card, and BLOAD will write to those addresses.

Here is a short program that loads the whole 16K RAM card, all from within a running Applesoft program, without EXEC files.

     100 D$ = CHR$ (4)
     110 B2 = 49281 : REM $C081 -- SELECT BANK TWO
     120 B1 = 49289 : REM $C089 -- SELECT BANK ONE
     130 P = PEEK(B2) + PEEK(B2) : REM WRITE ENABLE BANK TWO
     140 PRINT D$"BLOAD LC.BANK 2"
     150 P = PEEK(B1) + PEEK(B1) : REM WRITE ENABLE BANK ONE
     160 PRINT D$"BLOAD LC.BANK 1"

[ Note from Bob S-C: My face is red! Paul will note that I modified his program above in lines 130 and 150. He wrote "130 POKE B2, PEEK(B2)" and similarly for line 150. However, some RAM cards, such as my Andromeda board, will disable write-enable if the soft-switches are addressed during a write-cycle. The POKE does just that; therefore, I changed 130 and 150 to do two PEEKs in a row. Further, I recall when working with a Corvus drive last year that BLOADing from a Corvus into the RAM card did not work unless a monitor had already been copied into the space from $F800-$FFFF. ]


Apple Assembly Line is published monthly by S-C SOFTWARE, P. O. Box 280300, Dallas, TX 75228. Phone (214) 324-2050 Subscription rate is $15 per year, in the USA, sent Second Class Mail; $18 per year sent First Class Mail in USA, Canada, and Mexico; $28 per year sent Air Mail to 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.)