Volume 5 -- Issue 9 | June 1985

- The Boyer-Morris String Search Algorithm
- Short Integer Square Root Subroutine
- Note on the TXS Instruction in the 65802
- Interrupt Trace
- Improving the Single-Byte Converter
- AppleVisions, A Glimpse
- Two ROM Sets in One Apple //e
- Call Utility for Applesoft
- Some Final DP18 Subroutines

S-C Macro Assembler ProDOS

We will begin shipping the ProDOS version of the S-C Macro Assembler in July, so we are now accepting advance orders. There is more to the ProDOS version than just a change of operating systems. The new upgrade includes a couple of major new features:

        .INB (INclude Blocked) directive -- This works just like
             .IN, except that only one disk block at a time is
             overlaid into memory. Allows assembly of much larger
             files, with only a minor speed penalty.

        .AC  (Ascii Compressed) directive -- Generates compressed
             strings from a string between delimiters, according
             to rule tables.  Very complex, but worth the effort
             if you have a lot of messages and need to save memory.

The price of the ProDOS version alone will be $100. The up- grade from DOS Version 2.0 to ProDOS will be $30. The upgrade from DOS Version 1.0 or 1.1 to ProDOS will be $50, and will include DOS Version 2.0. The initial purchase price of the DOS 3.3 and ProDOS versions together will be $120. These are introductory prices which may well be raised in a few months.

65802's Are Here!

After many months of manufacturing delays, Western Design Center is shipping 65802 and 65816 microprocessors. We recently received a final production '802, and it's now happily processing away in Bob's oldest Apple II (#219). You can order the chips from WDC for $95.00. Call (602) 962-4545.

Note About Alliance Computers

Back in January Alliance Computers advertised 65802's for $50.00, but couldn't fill the orders because the chips didn't exist yet. Some of their formerly unhappy customers tell me that their orders have now arrived, so Alliance is taking care of their customers. You can reach Alliance Computers at P.O. Box 408, Corona, NY 11368.

The Boyer-Morris String Search Algorithm | Bob Bernard Westport, CT

For years now, I have been working on a debugger for the Apple. Lately I have been adding a hex string search capability to it.

I needed one so I could look through the Apple IIc (ProDOS) utilites to see how it squirrels away in the alternate page screen holes user specified default settings for the serial ports. These are used at PR#1 or 2 time to simulate the dip switches on the Super Serial Card in a IIe. Without setting them you always get 9600 bps, etc. (Imagewriter settings, that is). I (and I assume other AAL readers) want a little routine for DOS 3.3 hello that will allow the user's defaults to be put away the same as the IIc utility does.

Well, that routine is not ready yet. However, the search utility is rather interesting in its own right.

I was just going to code up a straight hex search, but then I mentioned it to my computer science graduate son, David. He was horrified that I would waste my time on anything so crude. That's what I get for bringing up a programmer! David insisted that I should instead code an implementation of Boyer and Moore's algorithm, which appeared in the October 1977 issue of the Communications of the ACM. [A more recent reference is in the book "Algorithms", by Robert Sedgewick, (Addison-Wesley Publishing Co., 1983, 551 pages) on pages 249-252.]

Well, I read the article and it seemed like a challenge. Besides it looked like a real time saver, and could also be used for character string searches. The code here has been excerpted from my debugger, and then worked over by Bob S-C.

The "conventional" or "brute-force" search technique aligns the search pattern with the left end of the string to be searched through and compares one byte at a time, from left to right, until either the entire pattern is compared successfully or a mismatch occurs. In the latter case the search window is moved one byte to the right, and the comparing process is repeated.

Without any knowledge about the contents of the search pattern, the most the window can be moved is one place to the right. Boyer-Moore owes its speed advantage to the fact that it uses context (i.e. knowledge about the contents of the pattern to be searched for) to increase the distance that the search window can be advanced when a mismatch occurs. Thus efficiency increases as the length of the pattern increases, which does not happen in a conventional search.

The cost of this benefit (there always is a cost) is that a table (called DELTA1 in the CACM article and DELTA.TABLE in my program) is required to store this context information, 256 bytes in this implementation. One byte is needed in the table for every possible value of the characters in the string to be searched.

If a particular byte appears in the search pattern, then the corresponding DELTA table entry contains the distance that the rightmost occurrence of that byte is from the left end of the pattern. All other entries contain the value -1. When a mismatch occurs, the DELTA table entry corresponding to that byte from the text being searched is used to compute how far to advance the search window. If that byte does not appear anywhere in the pattern, then the search window can be advanced by the full length of the pattern.

Since moving the search window, and the associated testing for finished, take most of the time in any searching technique, saving time here can be extremely beneficial, and explains why Boyer and Moore should be complimented.

My program uses the control-Y monitor command, in the form

         adr1.adr2^Y <hexstring>

The two addresses specify the start and end of the area to be searched. "^Y" stands for "control-Y". The hex string may be separated from the control-Y by one or more spaces, if you desire. Since the control-Y doesn't show up on the screen, I usually type at least one space before the hex string. The hex string itself is a continuous string of hex digits, with no imbedded spaces. Here is an example that will search from $800 to $BFFF for "BERNARD":

         800.BFFF^Y 4245524E415244

The program will list the starting addresses of any and all complete matches that are found.

The maximum length of the hex string is limited by the monitor input buffer. Since the longest command you can type is less than 256, and you have to use around ten characters for the addresses and control-Y, that puts an upper limit of less than 246 hex digits in your command. Each byte of the search pattern (or "key") is made up of two hex digits, so the maximum hex string will be less than 123 bytes long.

I assigned DELTA.TABLE to the area $02D0.03CF. Since I scan and collect the search pattern right in the monitor keyboard buffer at $0200, after converting to hex bytes it will run no higher than $027F.

Actually, I only implemented a simplified version of Boyer and Moore's procedure. The CACM article also discusses a second table, DELTA2, which is filled with additional context information regarding "terminating substrings" of the search pattern. In cases where a partial mismatch occurs, it may be possible to advance the search window farther than the DELTA1 table would indicate. However, since such situations occur in less than 20% of the cases, David allowed that the potential additional speed did not justify the time and effort and the additional table and code space that would have been required, and he gave me a passing grade on my effort without it. The incorporation of this additional capability, and changes to make the program an ASCII search, are left "as a exercise for the reader."

My program must go through several steps. First it has to find and pack up the search key. Next it must build the DELTA table. And finally the search can be performed.

Lines 1290-1360 will be executed when you BRUN the program. They install the control-Y vector and jump into the monitor, just as though you entered with CALL-151.

When you enter the search command, the Apple monitor parses the command line up to and including the control-Y, and then branches to my code at line 1380. The two addresses will have been converted and stuffed into A1 ($3C,3D) and A2 ($3E,3F). A variable named YSAV (at $34) contains the index to the next character following the control-Y.

Lines 1400-1440 skip over any blanks you may have typed between the control-Y and the first hex digit. Actually, the Y-register gets incremented once too often, so lines 1460-1470 decrement Y and save it; now YSAV points to the first hex digit in the search key.

The next problem I had to solve was to differentiate odd from even length strings and arrange them properly, adding a leading zero when an odd number of hex digits is input. Lines 1490-1530 search for the end of the hex string; if there are no digits at all, we are finished and line 1530 returns for the next monitor command.

This is a nice place to insert a brief description of the NXTCHAR subroutine, found in lines 2460-2590. NXTCHAR picks up the next character from the input buffer, and tests to see if it is a hex digit. If so, it returns either $00-09 or $FA-FF in the A-register, and carry will be clear. If not a hex digit, it returns with carry set. If we got a digit, the Y-register indexing the input buffer will have been advanced.

Lines 1550-1590 compute the key length. Since two digits make a byte, the number of digits in the hex string divided by two gives the number of bytes. But I actually want to use the byte-count-minus-one. Also I need to adjust for odd or even length strings. Lines 1600-1650 take care of these details. If the count was odd, I jump into the middle of the packing loop so that a leading zero gets inserted.

Lines 1670-1800 comprise the packing loop. NXTCHAR will return with carry set when we try to get a digit beyond the end of the key, so line 1680 is the only test in the loop. Lines 1670-1730 retrieve a left-hand digit and store it in the buffer. Lines 1740-1800 do the same for right-hand digits. Key bytes are stored starting at $0200, so they never catch up to the advancing retrieval of digits.

Line 1810 sets YSAV to point to the first character past the end of the hex string. This will usually be a carriage return, or another monitor command. Unless it is beyond $2CF, the monitor will correctly continue parsing whatever is in the buffer when we are through searching. At $2D0 and beyond, the DELTA table will clobber any further characters.

Now we come to the Boyer-Moore part. Lines 1820-1870 initialize the DELTA table to all -1 values, which is what we want for any bytes not present in the key. When the loop finishes, X=0 again.

Lines 1880-1970 scan through the search key from left to right, and store into DELTA the index of the rightmost occurrence of each value in the key. For example, if the key is "4245524E415244" ("BERNARD" again), the DELTA values will be:

         DELTA+$41:  4
         DELTA+$42:  0
         DELTA+$44:  6
         DELTA+$45:  1
         DELTA+$4E:  3
         DELTA+$52:  5 (also at 2, but 5 is rightmost)
        all others:  -1

We'll continue with this example after a brief look at the rest of the code.

Lines 1980-2040 back up the end pointer, which has been patiently waiting all this time in A2L and A2H. We subtract the key length (in bytes, not digits) from the end pointer, so that we will not try to match the key any further than necessary. We could do this inside the search loop, but it will run faster if we do it once before the loop.

Lines 2050-2440 perform the search. I inserted lines 2070-2110 inside the loop to printout the search window start address each time through the loop. This helps me to make sure it is working, and to explain how. Of course you should remove these five lines before using the routine for real problems. Notice they are all marked "<<<DEBUG>>>".

Lines 2120-2170 check whether the beginning of the search window has moved past the end of the area to be searched. If so, we are finished.

Lines 2180-2240 compare bytes from the key and the search window. If the entire key matches, we fall out of the loop into lines 2250-2300, where the address of the match will be printed. After a successful match the search window will be moved one byte to the right by lines 2370-2430, and we will begin the SEARCH.LOOP again.

Notice that the key is compared from right-to-left, not left- to-right. This is a critical part of the Boyer-Moore method. If a key byte does not match a search-window byte, we branch to line 2320. The byte from the search window is in the A-register. Lines 2320-2370 compute how far we can advance the search window, based on just what character we DID find in the search window, and how far into the key we had already matched.

To see how this works, let's continue the "BERNARD" example. Suppose the text we are searching is "THERE ARE FEW ST. BERNARDS IN SAN BERNARDINO." The key will be BERNARD, entered in hex as shown above. We first try to match BERNARD at the beginning of the text. We start at the right end, matching the "D" of the key with "A" of the text. The match fails, so we look up the "A" value in the DELTA table, which is 4. We subtract the delta value (4) from the current key index (6) and add the result (6-4=2) to the search window address. Note that this has the result of aligning the "A" of BERNARD with the "A" in the text.

Back to the top, and we now try to match the "D" of BERNARD to the "E" at the end of "ARE". Failure again! This time the DELTA value is 1, and we are still at position 6 in the key: index-delta is 5, so we advance the window by 5. This lines up the "E" of BERNARD with the E of the text. The next attempted match will find a blank in the text, which does not occur in the key at all. The delta value for blank is -1: 6-(-1)=7, so we will advance the window by 7. Now the window is up to "ST. BER" in the text.

When we compare "D" of BERNARD to "R" in the text, we fail again. The delta value for R is 5. There are two R's in BERNARD, but the rightmost one is at index 5. We can move the search window by 6-5=1. Next we try "D" against "N". The delta value of "N" is 3, so we can move the window 6-3=3 bytes. This time we have found "BERNARD"!

If you count it all up, we have compared the "D" of BERNARD with only six characters, and already we are at the first occurrence of the whole key in the text. A conventional search would have tried to match the first character of the key ("B") with all 18 characters in the text which precede the first "B" of the text. We have saved 13 times around the main loop! Of course, our loop is a tiny bit longer, but the end result is faster.

Here is a step-by-step picture of the entire search, which finds BERNARD twice:

         THERE ARE FEW ST. BERNARDS IN SAN BERNARDINO.
         BERNARD
           BERNARD
                BERNARD
                       BERNARD
                        BERNARD
                           BERNARD  (success!)
                            BERNARD
                                   BERNARD
                                      BERNARD
                                           BERNARD  (success!)
                                            BERNARD
                                                   BER... (end)

I have tacked two more examples onto the end of the source code, at lines 2620-2690. You can play with them. The five <<<DEBUG>>> lines will print out the window address at each step, so you can see how the search progresses. Remember to take those lines out before you make a production version of the program.

If you decide to include this search algorithm in your own private debugger program, like I am, you might want to add the ability to use an ASCII string for the key. You could use a quotation mark after the control-Y to signal the packer loop that an ASCII string follows. You might also want to add single-byte wildcard characters, and/or the ability to ignore the high-order bit of each byte matched.

Perhaps the Boyer-Moore algorithm would be even more useful in a data base program, a word processor, or other context in which you are searching through huge quantities of text for relatively interesting keys. My example should get you started, and my son will be proud of you!

      1000 *SAVE S.HEX.SEARCH
      1010 *--------------------------------
      1020 *   MEMORY SEARCH FOR HEX STRING
      1030 *      BY BOB BERNARD, MAY 17, 1985
      1040 *      MODIFIED BY BOB S-C, MAY 27TH
      1050 *   ADR1.ADR2^YXXXXXXXXXXXX
      1060 *      ("^Y" MEANS CONTROL-Y)
      1070 *
      1080 *   SEARCH MEMORY FROM ADR1 THRU ADR2
      1090 *   LOOKING FOR REFERENCES TO
      1100 *   THE HEX STRING, XXXXXXXXXX
      1110 *
      1120 *--------------------------------
      1130 YSAV        .EQ $34
      1140 A1L         .EQ $3C,3D   START OF SEARCH AREA
      1150 A2L         .EQ $3E,3F   END OF SEARCH AREA
      1160 KEY.LENGTH  .EQ $40      (MONITOR'S A3L)
      1170 *--------------------------------
      1180 KBDBUF      .EQ $0200 THRU $2CF
      1190 DELTA.TABLE .EQ $02D0 THRU $3CF
      1200 USRADR      .EQ $03F8   CTL-Y JUMPS HERE
      1210 *--------------------------------
      1220 PRINTAX     .EQ $F941
      1230 CROUT       .EQ $FD8E   NEW LINE
      1240 MONZ        .EQ $FF69   MONITOR, NO BELL
      1250 *--------------------------------
      1260          .OR $0800
      1270          .TF B.HEX.SEARCH
      1280 *--------------------------------
      1290 HEX.SEARCH
      1300          LDA #$4C       JMP OPCODE
      1310          STA USRADR       STUFF INTO CNTL-Y EXIT LOC
      1320          LDA #SEARCH      LO ADR
      1330          STA USRADR+1
      1340          LDA /SEARCH      HI ADR
      1350          STA USRADR+2
      1360          JMP MONZ         MONITOR, NO BELL
      1370 *--------------------------------
      1380 SEARCH
      1390 *---SKIP LEADING BLANKS----------
      1400          LDY YSAV         NEXT VALID KBDBUF CHAR
      1410 .1       LDA KBDBUF,Y     GET CHAR FROM
      1420          INY              KEYBOARD BUFFER
      1430          CMP #" "         SKIP LEADING BLANKS
      1440          BEQ .1
      1450 *---MARK KEY START---------------
      1460          DEY
      1470          STY YSAV         WHERE SCAN STARTS
      1480 *---FIND END OF KEY--------------
      1490 .2       JSR NXTCHAR
      1500          BCC .2           ...HEX DIGIT
      1510          CPY YSAV         CHECK FOR NULL KEY
      1520          BNE .3           ...NOT NULL
      1530          RTS              NULL KEY
      1540 *---COMPUTE KEY LENGTH-----------
      1550 .3       TYA
      1560          SBC YSAV
      1570
      1580          LSR
      1590          STA KEY.LENGTH
      1600          LDY YSAV
      1610          LDX #0
      1620          STX KBDBUF       (IN CASE ODD COUNT)
      1630          BCS .5           ...ODD NUMBER OF BYTES
      1640 *---ADJUST FOR EVEN LENGTH-------
      1650          DEC KEY.LENGTH MAKE EVEN LENGTH ONE LESS
      1660 *---LEFT NYBBLE------------------
      1670 .4       JSR NXTCHAR
      1680          BCS .6           END OF KEY
      1690          ASL
      1700          ASL
      1710          ASL
      1720          ASL
      1730          STA KBDBUF,X     LEFT HALF DEST CHAR
      1740 *---RIGHT NYBBLE-----------------
      1750 .5       JSR NXTCHAR
      1760          AND #$0F
      1770          ORA KBDBUF,X     MERGE HI NIBBLE
      1780          STA KBDBUF,X
      1790          INX
      1800          BNE .4           ...ALWAYS
      1810 .6       STY YSAV
      1820 *---INIT ALL DELTAS=-1 ----------
      1830          LDX #0
      1840          LDA #-1
      1850 .7       STA DELTA.TABLE,X
      1860          INX
      1870          BNE .7     ...256 OF THEM
      1880 *---DELTA(KEY(I))=I--------------
      1890          LDY #0           FOR I=0 TO KEYLEN
      1900 .8       LDA KBDBUF,Y    DELTA(K) = DISTANCE FROM LEFT END
      1910          TAX                  OF RIGHT-MOST OCCURENCE OF
      1920          TYA                  8-BIT VALUE "K" IN KEY.
      1930          STA DELTA.TABLE,X
      1940          INY              NEXT I
      1950          CPY KEY.LENGTH
      1960          BCC .8
      1970          BEQ .8
      1980 *---ADJUST END OF SEARCH---------
      1990          SEC
      2000          LDA A2L
      2010          SBC KEY.LENGTH
      2020          STA A2L
      2030          BCS SEARCH.LOOP
      2040          DEC A2L+1
      2050 *--------------------------------
      2060 SEARCH.LOOP
      2070        LDA A1L+1    <<<DEBUG>>>
      2080        LDX A1L      <<<DEBUG>>>
      2090        JSR PRINTAX  <<<DEBUG>>>
      2100        LDA #"-"     <<<DEBUG>>>
      2110        JSR $FDED    <<<DEBUG>>>
      2120          LDA A2L          CHECK AGAINST
      2130          CMP A1L           UPPER BOUND
      2140          LDA A2L+1         FOR SEARCH
      2150          SBC A1L+1
      2160          BCS .1           A1<=A2, NOT FINISHED
      2170          RTS              A1>A2, FINISHED
      2180 *---COMPARE IN THIS POSITION-----
      2190 .1       LDY KEY.LENGTH   FOR I=KEYLEN TO 0
      2200 .2       LDA (A1L),Y      CHECK BYTES FROM
      2210          CMP KBDBUF,Y        RIGHT TO LEFT
      2220          BNE .3           ...DID NOT MATCH
      2230          DEY              NEXT I
      2240          BPL .2
      2250 *---MATCH FOUND------------------
      2260          LDX A1L          PRINT ADR
      2270          LDA A1L+1        WHERE MATCH
      2280          JSR PRINTAX         WAS FOUND
      2290          JSR CROUT        NEW LINE
      2300          JMP .4
      2310 *---ADVANCE SEARCH POINTER-------
      2320 .3       TAX              STRING CHAR JUST LOOKED AT
      2330          TYA              I
      2340          CLC
      2350          SBC DELTA.TABLE,X
      2360          BPL .5           ...VALUE IS POSITIVE
      2370 .4       LDA #0           ...ADVANCE BY 1
      2380 .5       SEC              COMPENSATE
      2390          ADC A1L
      2400          STA A1L
      2410          BCC SEARCH.LOOP
      2420          INC A1L+1
      2430          BNE SEARCH.LOOP  ...ALWAYS, UNLESS WE
      2440          RTS              ...RAN OFF THE END OF MEMORY
      2450 *--------------------------------
      2460 NXTCHAR
      2470          LDA KBDBUF,Y     NEXT ACTIVE CHAR
      2480          INY
      2490          EOR #$B0         CONVERT ASCII TO DIGIT
      2500          CMP #10          0..9?
      2510          BCC .1           YES
      2520          ADC #$88         SHIFT RANGE FOR A-F TEST
      2530          CMP #$FA         A..F?
      2540          BCS .1           YES. EXIT CC
      2550          SEC              NOT HEX CHAR
      2560          DEY              BACK UP INDEX
      2570          RTS
      2580 .1       CLC              SIGNAL HEX CHAR
      2590          RTS
      2600 *--------------------------------
      2610 END    .BS $A00-*
      2620 TEST.STRING
      2630        .AS /XXXXXXXCOCACACACACACACACACACACAC/
      2640 * TRY A00.A1F^Y  43414341434143
      2650 * SHOULD GET A09-A0B-A0D-A0F-A11-A13-A15-A17-A19
      2660 *--------------------------------
      2670 TS2    .AS /A STRING SEARCHING EXAMPLE CONSISTING OF SIMPLE TEXT/
      2680 * TRY A20.A53^Y48494E47
      2690 *--------------------------------

Short Integer Square Root Subroutine | Bob Sander-Cederlof

In some graphics situations you need a square root subroutine (it is probably the fault of Pythagoras). Since the screen coordinates are integers, a short and fast integer square root subroutine can be handy.

The following program is probably not in the "fast" category, but it is indeed short. It can produce the integer value of the square root of any integer from 0 through 65535. The program uses the method of subtracting successive odd numbers.

Every perfect square (N*N, where N is an integer) is the sum of a series of odd numbers from 1 through 2*N-1. Thus 4=1+3, 25=1+3+5+7+9, etc.

The program starts by subtracting 1, then 3, then 5, and so on until the remainder is negative. When the remainder goes negative, the last odd number subtracted was 2*N+1, so we can get the square root by dividing that odd number by 2.

I set up the routine so I could test it with an Applesoft pro- gram. You can POKE the low 8-bits of a number at 768 ($300), the high 8-bits at 769, and CALL 772. Upon return, PEEK(770)+ 256*PEEK(771) gives you the integer value of the square root.

I used a couple of tricks in the code. For one, the variable ODD is always an even number. Since I preface the subtraction with CLC, a "borrow" is assumed, so it has the effect of sub- tracting the odd number which is one larger than the even number in ODD. This save a LDA #1 instruction after line 1090.

In lines 1190-1230, I add 2 to the even number in ODD. But you can see that line 1200 is ADC #1. This adds 2 because carry happens to be set.

      1000 *SAVE S.SQRT16
      1010 *--------------------------------
      1020        .OR $300
      1030 ARG    .BS 2
      1040 ODD    .BS 2
      1050 *--------------------------------
      1060 SQRT   LDX ARG+1    X = HI BYTE   HI
      1070        LDY ARG      Y = LO BYTE   LO
      1080        LDA #0       START ODD=0
      1090        STA ODD+1
      1100 .1     STA ODD
      1110        CLC          BORROW ON, SUBTRACT (ODD+1)
      1120        TYA          LO
      1130        SBC ODD
      1140        TAY
      1150        TXA          HI
      1160        SBC ODD+1
      1170        TAX
      1180        BCC .2       ...ODD>REMAINDER, FINISHED
      1190        LDA ODD      CARRY SET, ADD 2 TO ODD
      1200        ADC #1
      1210        BCC .1       ...NEXT
      1220        INC ODD+1
      1230        BNE .1       ...ALWAYS     ...ALWAYS
      1240 .2     LSR ODD+1    SQRT IS (ODD/2)
      1250        ROR ODD
      1260        RTS
      1270 *--------------------------------

You can use the following Applesoft program to test the square root subroutine.

         1000  REM TEST SQRT16
         1005 W = 256:A = 768:B = 769:C = 770:D = 771:E = 772
         1010  FOR XH = 0 TO 255: PRINT XH"-";: FOR XL = 0 TO 255
         1030  POKE A,XL: POKE B,XH: CALL E
         1040 Y =  PEEK (D) * W +  PEEK (C)
         1050  IF Y <  >  INT ( SQR (XH * W + XL)) THEN  PRINT : PRINT XH *
               W + L,Y
         1060  NEXT : NEXT

Note on the TXS instruction in the 65802 | Bob Sander-Cederlof

Sandy Greenfarb wrote the other day that he had received a 65802 and plugged it into his Basis 108 with success.

He has been trying various permutations of the new opcodes and modes, and discovered some stones are better left unturned:

> "The following programs should both print the letter "A" on the screen. However, the one on the left works, while the one on the right hangs up the computer."

           Works           Hangs Up
           --------        --------
           CLC             CLC
           XCE             XCE
           LDA #"A         LDA #"A
           JSR $FDF0       JSR $FDED
           SEC             SEC
           XCE             XCE
           RTS             RTS

The only difference in the two programs is that the unsuccessful one weaves its way through DOS. I looked at the DOS code it goes through, and at first glance it appears there should be NO PROBLEMS associated with executing all this code in 65802 mode, since both 16-bit modes are off.

However, for some reason it still hangs up. Actually, it might not always hang: it depends on what is in page zero at the corresponding position as the stack pointer in page one.

I do not know why, but the TXS instruction transfers the entire 16-bit value of X to S when you are in the 65802 mode, regardless of the status of the M and X bits. Since M and X are both 1, the high byte of the X-register is 00. Therefore the TXS instruction at $9FB9 in DOS clears the high byte of the S-register. The RTS at $9FC4 then uses a return address from page zero, rather than page one.

I tried various experiments to see how TXS and TSX worked, and also examined TXA and TAX. In my humble opinion, the 65802 is inconsistent here. If you are in 65802 mode with M and X = 1, TXA does not modify the high byte of the A-register. This is what I expect and what I want. But TXS does modify the high byte of the S-register, contrary to my expectations.

Of course, as long as you know exactly how the chip works it really doesn't matter a lot. The problems come when we ASSUME we know how it works, but are wrong. The best antidote for these kind of assumptions, at least until a definitive reference manual for the chip is published, is trial and error.

I have had my 65802 for about six months now, and still have had no problems whatsoever with compatibility as long as I stay in normal 6502 mode. If I leave it in 65802 and go charging through a program written for the 6502 mode, I expect I will run into trouble.

Interrupt Trace | Charles H. Putney Dublin, Ireland

Have you ever wondered what's happening when the Apple goes off into nothingness? If your answer is yes, then this short utility will help you find out.

I was recently debugging an assembly language program and ran into this problem. The program seemed to work for almost all the input data, but occasionally would hang. After several frustrating hours trying to simulate the event, I decided that an interrupt trace utility would solve my problems. Later when I had this utility working, it was easy to see why the program was hanging.

This utility consists of a pushbutton addition to the Apple which connects to the interrupt request line (IRQ) of the 6502 and an interrupt service routine which is in page three. When the interrupt pushbutton is depressed, the interrupt service routine displays the program counter and all the registers on the bottom line of the screen. It also displays a flashing cursor and waits for an "S" or "G" from the keyboard to stop or resume execution.

I have mounted a pushbutton switch at the upper right hand side of the keyboard in the center of the styling surface. For a temporary installation I suggest leaving the pushbutton on a flexible lead. The wiring is easily done with 30 gage wire wrap wire. One side of the pushbutton is connected to ground. You may solder a wire to any convenient ground point on the top of the circuit board. Or, for a temporary installation, you could stick a wire into pin 8 of the game I/O connector.

The other side of the pushbutton is connected to the IRQ signal. I found that signal at pin 4 of the 6502. Remove the 6502 from the socket and strip the insulation from the end of the 30 gage wire. Insert it in the socket for the 6502 in pin 4 and replace the 6502 to retain the wire. Route the wire along the chips for a neat installation.

For a temporary hookup, Bob S-C suggests folding a 3-by-5 card in half, and triming it so that the folded edge just fits into an empty slot. Then, while power to the Apple is off, slip one wire into the space between the card and pin 26 (ground) and the other wire between the card and pin 30 (IRQ). Both of these wires will be on the power-supply side of the card: pin 26 is at the back edge, and pin 30 is the fifth from the back. Once the wires are inserted, you may wish to tape them down.

Enter the routine at address $300 and BSAVE it. When you want to debug a hanging program, first BRUN the INTERRUPT TRACE utility. This installs the utility at address $300 to $3CA. Pressing the pushbutton will cause an immediate display of the current program counter and registers. The utility will wait with a blinking cursor for a "G" or "S" from the keyboard to continue or enter monitor.

Sometimes the program you're investigating may not respond to the pushbutton. This is because somewhere in the program interrupts have been disabled with the SEI command ($78). You must search through the entire program and replace these with a CLI instruction ($58). Make sure that each $78 found is not data in the program and is a valid instruction before you replace it.

The next time that you have a problem with your Apple "hanging" for no apparent reason, use this utility to see where the 6502 "is". It may help solve those "hard to debug programs".

When you run the program, the SETUP routine (from $300 to $30B) sets the interrupt vector location and then enables interrupts. When the pushbutton is depressed, the IRQ line (pin 4 on the 6502) is pulled low. At the completion of the current instruction, the program counter high, program counter low, and processor status are pushed on the stack. Interrupt disable is automatically set and the program counter is loaded with the contents of $FFFE and $FFFF. In the Autostart monitor ROM the program counter is set to $FA40 where the monitor interrupt service routine is located. (In the old monitor the identical routine is at $FA86) This routine saves the accumulator in $45 and examines the processor status register to see if the inter- rupt was caused by a BRK command. Remember, the BRK command shares the same vector location with the interrupt for software simulation of interrupts. If the interrupt was not caused by BREAK then a JMP indirect to location ($3FE) is performed.

Lines 1280-1290 save the X- and Y-registers. The accumulator has already been saved by the monitor interrupt routine.

Lines 1300-1350 copy the register display titles to the bottom of the screen. Of course, if your program happened to be in one of the full-screen graphics modes, this line will not be visible. If you have a //e, you can add code to sense the graphics mode, save it, switch to text mode; then you will have to restore it all when you type "G" to continue after the interrupt. The new enhanced //e ROMs automatically handle saving and restoring all the bank switched memory, but they still leave the graphics modes up to the programmer.

Lines 1360-1510 convert the values of the five registers and store them into the bottom line. I add 3 to the S-register value before displaying it, so you see the value before the IRQ code pushed PC and S onto the stack. I start with the Y-register pointing at the point on the bottom line where the A-register should be displayed. The DISPLAY.HEX subroutine advances the Y-register by 5, so it is always ready for displaying the next register.

Lines 1520-1590 display the PC-register. This value is taken from the stack, where the IRQ automatically saved it.

Lines 1600-1750 wait for you to type "G" or "S". While waiting, the last character on the bottom line is flashed to remind you to type. If you type "G", lines 1760-1800 restore the registers and return to the interrupted program. If you type "S", line 1820 takes you to the monitor.

      1000 *SAVE S.IRQ TRAPPER
      1010 *--------------------------------
      1020 *      INTERRUPT TRACE UTILITY
      1030 *
      1040 *      BY: CHARLES H. PUTNEY
      1050 *          18 QUINNS ROAD
      1060 *          SHANKILL
      1070 *          COUNTY DUBLIN
      1080 *          IRELAND
      1090 *--------------------------------
      1100 A.REG       .EQ $45      A-REG SAVE AREA USED BY MONITOR
      1110 STACK       .EQ $100     STACK PAGE
      1120 INTVEC      .EQ $3FE     INTERRUPT VECTOR
      1130 BOTTOM.LINE .EQ $7D0     LINE 24 OF TEXT SCREEN
      1140 *--------------------------------
      1150 KEYBD  .EQ $C000    KEYBOARD DATA
      1160 KEYSTB .EQ $C010    KEYBOARD STROBE
      1170 MNTR   .EQ $FF69    MONITOR ENTRY POINT (CALL -151)
      1180 *--------------------------------
      1190        .OR $300     PAGE THREE
      1200 *--------------------------------
      1210 SETUP  LDA #INT     LOAD IRQ VECTOR
      1220        STA INTVEC   LOW BYTE
      1230        LDA /INT
      1240        STA INTVEC+1 HIGH BYTE
      1250        CLI          ALLOW IRQ'S
      1260        RTS
      1270 *--------------------------------
      1280 INT    STX XREG     SAVE X (A-REG SAVED BY MONITOR)
      1290        STY YREG     SAVE Y
      1300 *---DISPLAY REG TITLES-----------
      1310        LDX #39      PUT UP MESSAGE LINE
      1320 .1     LDA TITLES,X  GET MESSAGE CHAR
      1330        STA BOTTOM.LINE,X   PUT ON SCREEN
      1340        DEX
      1350        BPL .1       DONE ?
      1360 *---DISPLAY REG VALUES-----------
      1370        LDY #10      START OF REG DISPLAY AREA
      1380        LDA A.REG    ...A-REG
      1390        JSR DISPLAY.HEX
      1400        LDA XREG     ...X-REG
      1410        JSR DISPLAY.HEX
      1420        LDA YREG     ...Y-REG
      1430        JSR DISPLAY.HEX
      1440        TSX          GET STACK POINTER
      1450        INX          POINT AT PROCESSOR STATUS
      1460        LDA STACK,X  ...P-REG
      1470        JSR DISPLAY.HEX
      1480        INX          ADJUST S-REG
      1490        INX
      1500        TXA          ...S-REG AS WAS BEFORE INTERRUPT
      1510        JSR DISPLAY.HEX
      1520 *---DISPLAY PC-REG---------------
      1530        LDY #0       START OF PC-REG DISPLAY
      1540        LDA STACK,X  GET PC HIBYTE
      1550        JSR DISPLAY.HEX
      1560        DEX
      1570        LDY #2
      1580        LDA STACK,X  GET PC LOBYTE
      1590        JSR DISPLAY.HEX
      1600 *---WAIT FOR "S" OR "G"----------
      1610 .2     LDA KEYBD    KEY PRESSED ?
      1620        BPL .3       NO
      1630        STA KEYSTB   CLEAR THE KEY
      1640        CMP #"G"     GO AHEAD ?
      1650        BEQ .4       YES
      1660        CMP #"S"     STOP ?
      1670        BEQ .5       YES
      1680 .3     DEX          BLINK CURSOR
      1690        BNE .3
      1700        DEY          LONGER DELAY
      1710        BNE .3
      1720        LDA BOTTOM.LINE+39  LAST CHAR ON SCREEN
      1730        EOR #$80     INVERT IT
      1740        STA BOTTOM.LINE+39  REPLACE IT
      1750        BNE .2       BRANCH ALWAYS
      1760 *---"G" TYPED, RETURN------------
      1770 .4     LDA A.REG      RESTORE THE REGISTERS
      1780        LDX XREG
      1790        LDY YREG
      1800        RTI          BACK TO WORK
      1810 *---"S" TYPED, SO STOP-----------
      1820 .5     JMP MNTR     ENTER THE MONITOR
      1830 *--------------------------------
      1840 DISPLAY.HEX
      1850        PHA          SAVE THE A-REG
      1860        LSR          SHIFT INTO LOWER NIBBLE
      1870        LSR
      1880        LSR
      1890        LSR
      1900        JSR DIGIT    MAKE IT A DIGIT
      1910        STA BOTTOM.LINE,Y   SHOW HIGH NIBBLE
      1920        PLA          GET A-REG AGAIN
      1930        AND #$0F     MASK IT
      1940        JSR DIGIT    MAKE IT A DIGIT
      1950        STA BOTTOM.LINE,Y   SHOW LOWER NIBBLE
      1960        INY
      1970        INY
      1980        INY
      1990        RTS
      2000 *--------------------------------
      2010 DIGIT  INY
      2020        ORA #$B0     ADD NUMBER ZERO
      2030        CMP #$BA     IS IT A LETTER
      2040        BCC .1       NO - DONE
      2050        ADC #$6      6 PLUS CARRY MAKES A
      2060 .1     RTS
      2070 *--------------------------------
      2080 TITLES .AS -/     -   A=   X=   Y=   P=   S=         /
      2090 *--------------------------------
      2100 XREG   .DA #*-*     X REGISTER SAVE AREA
      2110 YREG   .DA #*-*     Y REGISTER SAVE AREA
      2120 *--------------------------------

Improving the Single-Byte Converter | Bruce Love New Zealand

Bob's s;ingle byte converter (see Jan 85 issue, pages 31-32) can be shortened by one byte. The left column is from Bob's code, the right a shorter version:

    1040 .1  LDX #"0"
    1050 .2  CMP DECTBL,Y
    1060     BCC .3
    1070     SBC DECTBL,Y
    1080     INX
    1090     BNE .2
     .1  LDX #"0"-1
         SEC
     .2  SBC DECTBL,Y
         INX
         BCS .2
         ADC DECTBL,Y

I also tried a different approach, using the decimal mode to count tens, then printing the tens as a hex value with the monitor routine at $FDDA and the remainder (units digit) with $FDED. This routine takes longer time, but does not need to use the X-register.

      1000 *SAVE S.LOVE'S CONVERSION
      1010 *--------------------------------
      1020        .LIST OFF
      1030 *--------------------------------
      1040 BYTE   .EQ $00
      1050 COUT   .EQ $FDED
      1060 CROUT  .EQ $FD8E
      1070 PRBYTE .EQ $FDDA
      1080 *--------------------------------
      1090 *      COMMAND
      1100 *--------------------------------
      1110 P      LDA #0
      1120        STA BYTE
      1130 .1     JSR PRINT.000.255
      1140        JSR CROUT
      1150        INC BYTE
      1160        LDA BYTE
      1170        BNE .1
      1180        RTS
      1190 *--------------------------------
      1200 *      PRINT.000.255
      1210 *--------------------------------
      1220 PRINT.000.255
      1230        LDY #0
      1240        SEC
      1250 .1     SBC #10
      1260        PHP
      1270        PHA
      1280        TYA
      1290        SED
      1300        ADC #0
      1310        TAY
      1320        PLA
      1330        PLP
      1340        BCS .1
      1350        ADC #"0+10
      1360        PHA
      1370        TYA
      1380        JSR PRBYTE
      1390        PLA
      1400        JMP COUT
      1410 *--------------------------------
      1420 SC
      1430        LDY #"0"
      1440        TAX
      1450        BEQ .3
      1460        LDA #0
      1470        SED
      1480 .2     CLC
      1490        ADC #1
      1500        BCC .25
      1510        INY
      1520 .25    DEX
      1530        BNE .2
      1540        CLD
      1550 .3     PHA
      1560        TYA
      1570        JSR COUT
      1580        PLA
      1590        JMP $FDDA
      1600 *--------------------------------
      1610 T      LDA #0
      1620 .1     STA BYTE
      1630        JSR SC
      1640        LDA #" "
      1650        JSR $FDED
      1660        LDA BYTE
      1670        JSR $FDDA
      1680        JSR CROUT
      1690        LDA $C000
      1700        BPL .2
      1710        STA $C010
      1720 .3     LDA $C000
      1730        BPL .3
      1740 .2     STA $C010
      1750        LDA BYTE
      1760        CLC
      1770        ADC #1
      1780        BNE .1
      1790        RTS
      1800 *--------------------------------

AppleVisions, a Glimpse | Bob Sander-Cederlof

Here is a very elementary introduction to Apple Assembly Language programming by that old master Bob Bishop, along with Linda Gross- berger and Harry Vertelney. This 150+ page book and its companion diskette gently and humorously guide the beginning programmer into the realm of machine code. A "Cardboard Computer" introduces the concepts of registers, machine instructions, addressing, and branching. This background is then applied to the Apple's 6502 and the surrounding computer. AppleVisions is a nice place for the absolute beginner to start, especially the younger programmers interested in finding out what assembly language is.

AppleVisions. Addison-Wesley, 1985. $39.95 including diskette.

Two ROM Sets in One Apple //e | Bob Sander-Cederlof

If and when you decide to upgrade to the new enhanced //e ROMs (which Apple sells for $70 along with a 65C02), you will probably have to turn your old ROMs over to the store that makes the switch. Reportedly, Apple is binding the stores with a contract that forces them to collect all the old chips.

That is VERY unfortunate. It could lead to wild shouting and panic, when you discover some of your favorite old software no longer works.

The upgrade consists of three parts:

- the new processor chip (65C02), which is nice but not especially useful until software which uses its new features becomes available;
- a new character generator ROM which includes special characters for icons and line drawing in text mode (called the "mouse" characters).
- new CD and EF ROMs which upgrade the firmware.

The new firmware does NOT use any of the new features in the 65C02, so you could use it without the new cpu chip. Furthermore, there is no absolute requirement to have the new character generator installed. The new firmware is much better than the old, having lost some bugs and speeded up the 80-column scrolling and added lower-case support to Applesoft (among other things). It is compatible with the 6502, the 65C02, and the new 65802.

I personally do not yet have any use for the mouse characters, and do not expect to. Don Lancaster, in the June 1985 issue of "Computer Shopper", tells how to connect a 2764 EPROM in the character generator socket. The 2764 can hold two complete character sets, because it has twice the capacity of the 2732 normally in that socket. However, the socket has only 24 holes and the 2764 has 28 pins! Don shows how to wire this up with a socket adapter, and use a toggle switch to select either half.

And now Apple has "sort of" released an even more enhanced set of firmware, with debugging stuff built in. You may not see them on the open market for some time, but I like them even better than the standard enhanced ROMs. The "debug" ROMs add an absolute RESET (ctrl-RESET with solid apple), 16-byte hex display in the monitor when in 80-column mode, display of both hex and ASCII values of each byte in a memory dump, and the ability to use all monitor commands on both main and auxiliary memory. The disassembler and miniassembler are both present, and enhanced to include the 65C02 extensions.

The CD and EF ROM sockets are compatible with 2764 EPROMs. You can also use 27128s, which have twice the space. Pin 26 on the 2764 is always tied to +5 volts. On a 27128, pin 26 selects the top or bottom half of the 16K bytes inside. You can burn one set of firmware in one half, and the other set in the other half. Then bend out pin 26 a little, so that it does not go into the socket when you insert the chip. Attach a clip lead to the bent-out pin, and connect the other end to either +5 volts or ground, to select the half you want at any given time.

You can connect it to a toggle switch, or just stick the bare end of a wire into the game paddle connector. If you use the game socket on the motherboard, pin 1 is +5 volts and pin 8 is ground. Or stick a wire into one of the annunciator outputs (pins 12, 13, 14, and 15) so you can flip back and forth between firmware sets by software control.

It can be a little tricky to make a copy of the ROM firmware and get it into RAM or on a disk, so that you can later burn it in your own EPROM. Especially in the Cxxx part. My approach, since I have more than one Apple, is to put my SCRG PromGramer card in a different machine. Then one by one I can read the //e ROMs and burn them into the appropriate 27128s. This a lot faster than trying to figure out how to flip all the //e soft switches so as to get at the different banks of Cx ROM code.

I have recently seen 27128s priced as low as $5 and as high as $20, in the back of Byte magazine. It is well worth it to invest in a PromGramer, at $140, and an EPROM eraser ($50 to $100 from Logical Devices in Florida, see Byte ads). You can keep your Apple standard for commercial software, and still have your own private firmware on the motherboard at the flip of a switch!

A CALL Utility for Applesoft | David C. Johnson Applied Engineering

Anyone who has ever used Applesoft eventually realizes that the most powerful statement in the language is CALL. It allows you to get to the Monitor for instance (however, Extended Debugging Monitor users have a better way). When writing a program in BASIC, you invariably will want to do something that is at best difficult and often impossible to code using the other Applesoft statements.

The solution to this type of situation is to speak to your Apple in its native tongue. There are several way this can be done. Ampersand (&) routines are a popular technique. The USR( function even has its uses. The most logical way, for me, is the CALL statement.

Using CALL neatly transfers control from the Applesoft interpreter to whatever you want to do in machine language. The one disadvantage to CALL is that the processor's registers do not contain useful data when your machine code gets control.

The CALL utility presented in this article will allow you to specify, as part of the CALL statement, the contents of any or all of the registers upon entry of your machine language subroutine. You assign the register contents with LET-like structures. Obviously you can only fit an 8 bit value into the 8 bit registers and the program counter value will probably be a 16 bit number. Here's how the CALL statement should be written:

             CALL 768,PC=word,A=byte,X=byte,Y=byte,P=byte

The expressions "word" and "byte" may be any valid Applesoft numeric expression. This is because the utility calls routines internal to Applesoft to evaluate the expressions. If an expression results in a value larger than the register to which it is being assigned, or isn't numeric, or is invalid, you will get one of the usual errors. The commas shown separating the register assignments are required (syntax error if comma missing). The equals characters ("=") are also required. The register names ( PC, A, X, Y, & P) must be upper case on older Apples, while the newer firmware will convert lower case for you (or in spite of you). The register assignments may appear, after the first comma, in any order and need not all be specified. Unspecified registers will be loaded with their last used value. Previously unused registers default to zero, except the P-register which defaults to $04 in order to set the interrupt disable flag.

The program is well commented, but I'll add one more note of caution. Readers with Apples containing reqular 6502s (not 65C02s or 65802s) should avoid re-assembling the code with the label PC.Sav's bytes falling across a page boundary ($XXFF).

The program was written using the ProDOS version of the S-C Macro Assembler 2.0, while I was beta testing it for Bob. It works GREAT!

      1000 *SAVE S.CALL.UTIL
      1010
      1020 * 6/13/85 dcj
      1030
      1040 * CALL 768{,pc=word,a=byte,x=byte,y=byte,p=byte}
      1050
      1060 *--------------------------------
      1070
      1080        .OR $300
      1090        .TF CU
      1100
      1110 EQ.TOK .EQ $D0      Applesoft '=' token
      1120
      1130 CHRGET .EQ $B1 -$C8 advance TXTPTR & fetch chr
      1140 CHRGOT .EQ $B7      just fetch chr
      1150
      1160 FRMNUM .EQ $DD67    evaluate FP expression (FAC)
      1170 SYNCHR .EQ $DEC0    require chr in Acc syntax @ TXTPTR
      1180 SYNERR .EQ $DEC9    syntax error
      1190 GETBYT .EQ $E6F8    evaluate 8 bits @ TXTPTR (X-reg)
      1200 GETADR .EQ $E752    convert FAC to 16 bits in Acc & Y-reg (hi/lo)
      1210
      1220 *--------------------------------
      1230
      1240 CALL.UTIL
      1250
      1260        JSR CHRGOT   get chr after call adr expression
      1270        CMP #','     comma indicates more stuff follows
      1280        BEQ .1       =>go continue parsing
      1290        LDA P.SAV    load registers
      1300        PHA           (P-reg via stack)
      1310        LDA ACC.SAV
      1320        LDX X.SAV
      1330        LDY Y.SAV
      1340        PLP
      1350        JMP (PC.SAV) go 4 it!
      1360
      1370 * we got something to parse
      1380
      1390 .1     JSR CHRGET   get chr after comma
      1400        CMP #'A'     (as in 'Acc')
      1410        BEQ .2       =>go get '=' & byte for Acc
      1420        CMP #'X'     (as in 'X-reg')
      1430        BEQ .3       =>go get '=' & byte for X-reg
      1440        CMP #'Y'     (as in 'Y-reg')
      1450        BEQ .4       =>go get '=' & byte for Y-reg
      1460        CMP #'P'     (as in P-reg or Program Counter)
      1470        BEQ .5       =>go get '=' or 'C='...
      1480        JMP SYNERR   razz
      1490
      1500 * pickup Acc byte
      1510
      1520 .2     JSR .7       require '=' (@ next) & fetch byte exp
      1530        STX ACC.SAV  stuff it
      1540        BVC CALL.UTIL ...always
      1550
      1560 * pickup X-reg byte
      1570
      1580 .3     JSR .7       require '=' (@ next) & fetch byte exp
      1590        STX X.SAV    stuff it
      1600        BVC CALL.UTIL ...always
      1610
      1620 * pickup Y-reg byte
      1630
      1640 .4     JSR .7       require '=' (@ next) & fetch byte exp
      1650        STX Y.SAV    stuff it
      1660        BVC CALL.UTIL ...always
      1670
      1680 * Finish parsing 'P=' or 'PC='
      1690
      1700 .5     JSR CHRGET   advance to next chr position & fetch it
      1710        CMP #'C'     (as in 'Program Counter')
      1720        BEQ .6       =>go get '=' & 16 bits for PC
      1730
      1740 * pickup P-reg byte
      1750
      1760        JSR .10      require '=' @ current chr position
      1770        JSR .8       fetch byte expression
      1780        STX P.SAV    stuff it
      1790        BVC CALL.UTIL ...always
      1800
      1810 * pickup PC word
      1820
      1830 .6     JSR .9       require '=' @ next chr position
      1840        JSR FRMNUM   fletch FP expression
      1850        JSR GETADR   convert FP expression to Acc & Y-reg (hi/lo)
      1860        STY PC.SAV   stuff 'em
      1870        STA PC.SAV+1
      1880        JMP CALL.UTIL no flag known...
      1890
      1900 .7     JSR .9       require '=' @ next chr position
      1910
      1920 .8     JSR GETBYT   fetch byte expression (2 X-reg)
      1930        CLV          to force branch
      1940        RTS
      1950
      1960 .9     JSR CHRGET   1st advance to next chr position
      1970
      1980 .10    LDA #EQ.TOK  require '=' before register expressions
      1990        JMP SYNCHR    (SYNTAX ERROR IF '=' NOT FOUND)
      2000
      2010 *--------------------------------
      2020
      2030 ACC.SAV    .DA #$00
      2040 X.SAV      .DA #$00
      2050 Y.SAV      .DA #$00
      2060 P.SAV      .DA #$04
      2070 PC.SAV     .DA $0000
      2080
      2090 *--------------------------------

Some Final DP18 Subroutines | Bob Sander-Cederlof

Gerald Ferrier (Princeton, Minnesota) wrote to point out that we somewhow omitted a double-handful of subroutines from our lengthy series on 18-digit arithmetic for Applesoft. With apologies to you all, and thanks to Gerald, here they are:

      1000 *SAVE DP18.MOVE.SUBS
      2020 *--------------------------------
      2030 *      MOVE (Y,A) INTO DAC. YA IS UNPACKED
      2040 *--------------------------------
      2050 MOVE.YA.DAC.1
      2060        STA PNTR
      2070        STY PNTR+1
      2080        LDY #10      MOVE 11 BYTES
      2090 .1     LDA (PNTR),Y
      2100        STA DAC,Y
      2110        DEY
      2120        BPL .1
      2130        LDA DAC.EXPONENT
      2140        STA DAC.SIGN
      2150        AND #$7F
      2160        STA DAC.EXPONENT
      2170        RTS
      2180 *--------------------------------
      2190 *      MOVE (Y,A) INTO ARG. YA IS UNPACKED
      2200 *--------------------------------
      2210 MOVE.YA.ARG.1
      2220        STA PNTR
      2230        STY PNTR+1
      2240        LDY #10      MOVE 11 BYTES
      2250 .1     LDA (PNTR),Y
      2260        STA ARG,Y
      2270        DEY
      2280        BPL .1
      2290        LDA ARG.EXPONENT
      2300        STA ARG.SIGN
      2310        AND #$7F
      2320        STA ARG.EXPONENT
      2330        RTS
      2340 *--------------------------------
      2350 *      MOVE DAC TO (Y,A) WITHOUT PACKING
      2360 *--------------------------------
      2370 MOVE.DAC.YA.1
      2380        STA PNTR
      2390        STY PNTR+1
      2400        LDA DAC.EXPONENT
      2410        BPL .0
      2420        JMP DAC.YA.O.U  OVER- OR UNDER-FLOW
      2430 .0     BIT DAC.SIGN
      2440        BPL .1       POSITIVE
      2450        ORA #$80     NEGATIVE
      2460 .1     LDY #0
      2470 .2     STA (PNTR),Y
      2480        INY
      2490        LDA DAC,Y
      2500        CPY #11      11 BYTES
      2510        BCC .2
      2520        RTS
      2530 *--------------------------------
      2540 MOVE.DAC.TEMP1
      2550        LDA #DP.TEMP1
      2560        LDY /DP.TEMP1
      2570        JMP MOVE.DAC.YA.1
      2580 *--------------------------------
      2590 MOVE.TEMP1.ARG
      2600        LDA #DP.TEMP1
      2610        LDY /DP.TEMP1
      2620        JMP MOVE.YA.ARG.1
      2630 *--------------------------------
      2640 MOVE.TEMP1.DAC
      2650        LDA #DP.TEMP1
      2660        LDY /DP.TEMP1
      2670        JMP MOVE.YA.DAC.1
      2680 *--------------------------------
      2690 MOVE.DAC.TEMP2
      2700        LDA #DP.TEMP2
      2710        LDY /DP.TEMP2
      2720        JMP MOVE.DAC.YA.1
      2730 *--------------------------------
      2740 MOVE.TEMP2.DAC
      2750        LDA #DP.TEMP2
      2760        LDY /DP.TEMP2
      2770        JMP MOVE.YA.DAC.1
      2780 *--------------------------------
      2790 MOVE.TEMP2.ARG
      2800        LDA #DP.TEMP2
      2810        LDY /DP.TEMP2
      2820        JMP MOVE.YA.ARG.1
      2830 *--------------------------------
      2840 MOVE.TEMP3.DAC
      2850        LDA #DP.TEMP3
      2860        LDY /DP.TEMP3
      2870        JMP MOVE.YA.DAC.1
      2880 *--------------------------------
      2890 MOVE.TEMP3.ARG
      2900        LDA #DP.TEMP3
      2910        LDY /DP.TEMP3
      2920        JMP MOVE.YA.ARG.1
      2930 *--------------------------------
      2940 MOVE.DAC.TEMP3
      2950        LDA #DP.TEMP3
      2960        LDY /DP.TEMP3
      2970        JMP MOVE.DAC.YA.1
      2980 *--------------------------------

Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $14 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE CORPORATION, all rights reserved. (Apple is a registered trademark of Apple Computer, Inc.)