Volume 4 -- Issue 9 | June 1984

In This Issue...

- 18-Digit Arithmetic, Part 2
- DOSology and DOSonomy
- OBJ.APWRT][F Updated to AWIIe Toolkit
- Using the PRT Command
- Revisiting $48:0
- More Random Number Generators
- Booting ProDOS with a Modified Monitor ROM
- Fixing the Andromeda 16K Card
- Finding the Erroneous Bit Using CRC
- The Barkovitch Utilities
- Converting to Motorola S-Format
- Making a 65C02 Work in my Apple II Plus

More on ProDOS and Nonstandard Apples

In the March issue we published Bob Stout's note on how to make ProDOS boot in a Franklin computer. The current issue, (No. 9) of Hardcore Computist points out that the address given in that note didn't work for the ProDOS version dated 1-JAN-84. Apparently Bob was referring to an earlier version. The correct address for the NOPs is $265B.

In a similar vein, inside this issue Jan Eugenides points out that ProDOS will also fail in an Apple with a modified Monitor ROM. He then gives a slightly different patch to defeat the check code.

18-Digit Arithmetic, Part 2 | Bob Sander-Cederlof

Feedback on installment one of this series came from as far away as Sweden. Paul Schlyter, with others, pointed out the omission of three very important letters. PRINT (14.9*10) indeed prints 149, as expected. What I meant to say was that PRINT INT(14.9*10) prints 148.

I noticed another error at the top of page 21. The exponent range runs from 10^-63 thru 10^63, not 10^64.

Paul pointed out that my routines did not check for underflow and overflow. I did have such checks in another part of the code, as yet unlisted, but I now agree with him that some checks belong in the routines printed last month.

The subroutine SHIFT.DAC.RIGHT.ONE is called when a carry beyond the most significant bit is detected in DADD, at line 1620. If the exponent is already 10^63, or $7F, this shift right will cause overflow. That means the sum formed by DADD is greater than 10^63, and we need to do either of two things. My usual choice, assuming the routines are being used from Applesoft, is to JMP directly to the Applesoft ROM overflow error routine, at $E8D5. Another option is to set the DAC exponent to $7F, and the mantissa to all 9's. To implement it my way, add these lines:

           1945       BMI .2

           2085 .2    JMP $E8D5

Underflow needs to be tested in the NORMALIZE.DAC subroutine. Underlofw happens when the exponent falls below 10^-63. The normal procedure upon underflow is to set the result to zero. Zero values in DP18 are indicated by the exponent being zero, regardless of the mantissa value. Delete lines 2400-2480 and line 2730, and enter the following lines

           2400        LDY #-1
           2410 .1     INY
           2420        CPY #10
           2430        BCS .7
           2440        LDA DAC.HI,Y
           2450        BEQ .1

           2730 .6     LDA DAC.EXPONENT
           2731        BPL .8
           2732 .7     LDA #0
           2733        STA DAC.EXPONENT
           2734        STA DAC.SIGN
           2735 .8     RTS

All these changes will be installed on Quarterly Disk 15.

This month I want to present several pack and unpack subroutines, and one which rounds the value in DAC according to the value in the extension byte.

Note that I have just LISTed the subroutines below, rather than showing the assembly listing, because the program parts need to all be assembled together before they are meaningful.

There are two "unpack" subroutines, MOVE.YA.DAC and MOVE.YA.ARG. They perform the "load accumulator" function. There is one "pack" subroutine, MOVE.DAC.YA, which performs the "store accumulator" function.

The MOVE routines use a page-zero pair at $5E and $5F. Assuming the DP18 package will be called from Applesoft via the &-vector, there will be no page-zero conflicts here.

The subroutines DADD and DSUB from last month, and DMULT and DDIV to come, all expect two arguments in DAC and ARG and leave the result in DAC. Assuming there are two packed DP18 value at VAL.A and VAL.B, and that I want to add them together and store the result in VAL.C, I would do it this way:

           LDA #VAL.A
           LDY /VAL.A
           JSR MOVE.YA.DAC
           LDA #VAL.B
           LDY /VAL.B
           JSR MOVE.YA.ARG
           JSR DADD
           LDA #VAL.C
           LDY /VAL.C
           JSR MOVE.DAC.YA

Note that MOVE.DAC.YA calls ROUND.DAC before storing the result. ROUND.DAC checks the extension byte. If the extension byte has a value less than $50, no rounding need be done. If it is $50 through $99, the value in DAC should be rounded up. If the higher digits are less than .999999999999999999, then there will be no carry beyond the most significant digit, and no chance for overflow. However, if it is all 9's we will get a final carry and we will need to change the number to 100000000000000000 and add one to the exponent. In tiny precision, this is like rounding .995 up to 1.00. If the exponent was already 10^63, rounding up with a final carry causes overflow, so I jump to the Applesoft error handler.

      1000 *SAVE S.DP18 PACK & UNPACK
      1010 *-------------------------------
      1020 *     ADDRESSES INSIDE APPLESOFT
      1030 *-------------------------------
      1040 AS.OVRFLW .EQ $E8D5    OVERFLOW ERROR
      1050 *--------------------------------
      1060 *      PAGE ZERO USAGE
      1070 *-------------------------------
      1080 PNTR   .EQ $5E,5F
      1090 *-------------------------------
      1100 *      MOVE (Y,A) INTO DAC AND UNPACK
      1110 *--------------------------------
      1120 MOVE.YA.DAC
      1130        STA PNTR
      1140        STY PNTR+1
      1150        LDY #9       MOVE 10 BYTES
      1160 .1     LDA (PNTR),Y
      1170        STA DAC,Y
      1180        DEY
      1190        BPL .1
      1200        INY          Y=0
      1210        STY DAC.EXTENSION
      1220        LDA DAC.EXPONENT
      1230        STA DAC.SIGN
      1240        AND #$7F
      1250        STA DAC.EXPONENT
      1260        RTS
      1270 *--------------------------------
      1280 *      MOVE (Y,A) INTO ARG AND UNPACK
      1290 *--------------------------------
      1300 MOVE.YA.ARG
      1310        STA PNTR
      1320        STY PNTR+1
      1330        LDY #9       MOVE 10 BYTES
      1340 .1     LDA (PNTR),Y
      1350        STA ARG,Y
      1360        DEY
      1370        BPL .1
      1380        INY          Y=0
      1390        STY ARG.EXTENSION
      1400        LDA ARG.EXPONENT
      1410        STA ARG.SIGN
      1420        AND #$7F
      1430        STA ARG.EXPONENT
      1440        RTS
      1450 *--------------------------------
      1460 *      PACK AND MOVE DAC TO (Y,A)
      1470 *--------------------------------
      1480 MOVE.DAC.YA
      1490        STA PNTR
      1500        STY PNTR+1
      1510        JSR ROUND.DAC
      1520        LDA DAC.EXPONENT
      1530        BIT DAC.SIGN
      1540        BPL .1       POSITIVE
      1550        ORA #$80     NEGATIVE
      1560 .1     LDY #0
      1570 .2     STA (PNTR),Y
      1580        INY
      1590        LDA DAC,Y
      1600        CPY #10
      1610        BCC .2
      1620        RTS
      1630 *--------------------------------
      1640 *      ROUND DAC BY EXTENSION
      1650 *--------------------------------
      1660 ROUND.DAC
      1670        LDA DAC.EXTENSION
      1680        CMP #$50     COMPARE TO .5
      1690        BCC .3       NO NEED TO ROUND
      1700        LDY #8
      1710        SED          DECIMAL MODE
      1720 .1     LDA #0
      1730        ADC DAC.HI,Y
      1740        STA DAC.HI,Y
      1750        BCC .2       NO NEED FOR FURTHER PROPAGATION
      1760        DEY
      1770        BPL .1       ...MORE BYTES
      1780        INC DAC.EXPONENT
      1790        BMI .4       ...OVERFLOW
      1800        LDA #$10     .999...9 ROUNDED TO 1.000...0
      1810        STA DAC.HI
      1820 .2     CLD
      1830 .3     LDA #0
      1840        STA DAC.EXTENSION
      1850        RTS
      1860 .4     CLD
      1870        JMP AS.OVRFLW
      1880 *--------------------------------

None of the pack/unpack code is especially tricky, but the same cannot be said for DMULT. Multiplication is handled "just like you do it with pencil and paper", but making it happen at all efficiently makes things look very tricky.

Call DMULT after loading the multiplier and multiplicand into DAC and ARG (doesn't matter which is which, because multiplication is commutative). Then JSR DMULT to perform the multiply. The result will be left in DAC.

Looking at the DMULT code, lines 1040-1070 handle the special cases of either argument being 0. Anything times zero is zero, and zero values are indicated by the exponent being zero, so this is real easy.

Lines 1090-1130 clear a temporary register which is 20 bytes long. This register will be used to accumulate the partial products. Just in case some of the terminology is losing you, here are some definitions:

           12345  <-- multiplicand
         x 54321  <-- multiplier
       ---------
           12345  <-- 1st partial product
          24690   <-- 2nd partial product
         37035    <-- 3rd    "       "
        49380     <-- 4th    "       "
       61725      <-- 5th    "       "
       ---------
       670592745  <-- product

Lines 1150-1180 form the 20-digit product of the two 10-digit arguments. I wanted to reduce the number of times the individual digits have to be isolated, or the accumulators shifted by 4-bits, so I used a trick. Line 1150 calls a subroutine which multiplies the multiplicand (in ARG) by all the low-order digits in each byte of the multiplier (in DAC). In other words, I accumulate only the odd partial products at this time. Then I shift DAC 4-bits right, which places the other set of digits in the low-order side of each byte. I also have to shift the result register, MAC, right 4-bits, and then I call the MULTIPLY.BY.LOW.DIGITS subroutine again.

Lines 1200-1270 form the new exponent, which is the sum of the exponents of the two arguments. Since both exponents have the value $40 added to make them appear positive, one of the $40's has to be subtracted back out. But before that, if the sum is above $C0 then we have an overflow condition. After subtracting out one of the $40's, if the result is negative we have an underflow condition. Note that since the carry status was clear at line 1250, I subtracted $3F; for one more byte, I could have done it the normal way and used SEC, SBC #$40.

Lines 1290-1310 form the sign of the product, which is the exclusive-or of the signs of the two arguments. Lines 1330-1370 copy the most significant 10 bytes of the product from MAC to DAC.

The result may have a leading zero digit in the left half of the first byte, so I call NORMALIZE.DAC at line 1390. If The leading digit was zero, normalizing will shift DAC left one digit position, leaving room for another significant digit on the right end. Lines 1400-1490 handle installing the extra digit if necessary.

MULTIPLY.BY.LOW.DIGITS picks up the low-order digit out of each byte of the multiplier, one-by-one, and calls MULTIPLY.ARG.BY.N.

MULTIPLY.ARG.BY.N does the nitty-gritty multiplication. And here is where I lost all my ingenuity, too. The multiplier digit is stored in DIGIT, and used to count down a loop which adds ARG to MAC DIGIT times. Surely this can be done more efficiently! How about it Paul? Or Charlie? Anyone?

      1000 *SAVE S.DP18 MULTIPLY
      1010 *--------------------------------
      1020 * DAC = ARG * DAC
      1030 *--------------------------------
      1040 DMULT  LDA DAC.EXPONENT  IF DAC=0, EXIT
      1050        BEQ .3
      1060        LDA ARG.EXPONENT  IF ARG=0, SET DAC=0 AND EXIT
      1070        BEQ .4
      1080 *---CLEAR RESULT REGISTER--------
      1090        LDA #0
      1100        LDY #19
      1110 .1     STA MAC,Y
      1120        DEY
      1130        BPL .1
      1140 *---FORM PRODUCT OF FRACTIONS----
      1150        JSR MULTIPLY.BY.LOW.DIGITS
      1160        JSR SHIFT.MAC.RIGHT.ONE
      1170        JSR SHIFT.DAC.RIGHT.ONE
      1180        JSR MULTIPLY.BY.LOW.DIGITS
      1190 *---ADD THE EXPONENTS------------
      1200        LDA DAC.EXPONENT
      1210        CLC
      1220        ADC ARG.EXPONENT
      1230        CMP #$C0     CHECK FOR OVERFLOW
      1240        BCS .5       ...OVERFLOW
      1250        SBC #$3F     ADJUST OFFSET
      1260        BMI .4       ...UNDERFLOW
      1270        STA DAC.EXPONENT
      1280 *---FORM SIGN OF PRODUCT---------
      1290        LDA DAC.SIGN
      1300        EOR ARG.SIGN
      1310        STA DAC.SIGN
      1320 *---MOVE MAC TO DAC--------------
      1330        LDY #9
      1340 .2     LDA MAC,Y
      1350        STA DAC.HI,Y
      1360        DEY
      1370        BPL .2
      1380 *---NORMALIZE DAC----------------
      1390        JSR NORMALIZE.DAC
      1400        LDA MAC      IF LEADING DIGIT=0,
      1410        AND #$F0     THEN GET ANOTHER DIGIT
      1420        BNE .3
      1430        LDA MAC+10
      1440        LSR
      1450        LSR
      1460        LSR
      1470        LSR
      1480        ORA DAC.HI+9
      1490        STA DAC.HI+9
      1500 .3     RTS
      1510 .4     LDA #0
      1520        STA DAC.SIGN
      1530        STA DAC.EXPONENT
      1540        RTS
      1550 .5     JMP AS.OVRFLW
      1560 *--------------------------------
      1570 *      MULTIPLY BY EVERY OTHER DIGIT
      1580 *--------------------------------
      1590 MULTIPLY.BY.LOW.DIGITS
      1600        SED          DECIMAL MODE
      1610        LDX #9
      1620        LDY #19
      1630 .1     LDA DAC.HI,X
      1640        AND #$0F     ISOLATE NYBBLE
      1650        BEQ .2       0, SO NEXT DIGIT
      1660        JSR MULTIPLY.ARG.BY.N
      1670 .2     DEY          NEXT MAC POSITION
      1680        DEX          NEXT DAC DIGIT
      1690        BPL .1       DO NEXT DIGIT
      1700        CLD          BINARY MODE
      1710        RTS          DONE
      1720 *--------------------------------
      1730 MULTIPLY.ARG.BY.N
      1740        STA DIGIT    N = 1...9
      1750        STY TEMP     SAVE Y
      1760        STX TEMP+1   SAVE X
      1770 .1     LDX #9       INDEX INTO ARG
      1780        CLC
      1790 .2     LDA ARG.HI,X
      1800        ADC MAC,Y    ADD IT
      1810        STA MAC,Y
      1820        DEY          NEXT MAC
      1830        DEX          NEXT ARG
      1840        BPL .2       NEXT DIGIT
      1850        BCC .4       NO CARRY
      1860 .3     LDA #0       PROPAGATE CARRY
      1870        ADC MAC,Y
      1880        STA MAC,Y
      1890        DEY
      1900        BCS .3       MORE CARRY
      1910 .4     LDY TEMP     GET POSITION IN MAC
      1920 .5     DEC DIGIT    NEXT DIGIT
      1930        BNE .1
      1940        LDX TEMP+1
      1950        RTS          DONE
      1960 *--------------------------------
      1970 SHIFT.MAC.RIGHT.ONE
      1980        LDY #4       4 BITS RIGHT
      1990 .0     LDX #1       20 BYTES
      2000        LSR MAC
      2010 .1     ROR MAC,X
      2020        INX          NEXT BYTE
      2030        PHP
      2040        CPX #20
      2050        BCS .2       NO MORE BYTES
      2060        PLP
      2070        JMP .1
      2080 .2     PLP
      2090        DEY          NEXT BIT
      2100        BNE .0
      2110        RTS
      2120 *--------------------------------

Well, that's all for this month. Next month expect some simple I/O routines and the divide subroutine.

DOSology and DOSonomy | Bob Sander-Cederlof

The other day I was thinking about the way Apple spells ProDOS. They jealously guard the spelling, having trademarked the idea of upper-case "P" and "DOS" with lower-case "ro".

Of course, we all know that "Pro" is a standard prefix, with origins in the Greek language. In Greek it means "before". I think Apple derived it from the English word "professional", so that ProDOS means "professional DOS". Nevertheless, the "pro" even in the word professional means before, according to the etymologies in dictionaries.

I took some Greek courses at Dallas Theological Seminary back in 1973 and 1974. I remember very little now, but one thing stuck with me: prepositions. "Pro" is one, but there are a lot more. What other interesting DOSses can we invent?

By the way, the preferred pronunciation of DOS rhymes with "boss", not "gross". If you insist on rhyming with the latter, your pronunciation has a decided Spanish accent. For you we have invented "UnoDOS", which is of course two-thirds of a popular product on the IBM-PC, uno-dos-tres by Lotus. Ha!

The first that came to mind was "ParaDOS". We like it so well, we've decided to trademark it! It could relate to either paradox or pair-of-dice or paradise, take your pick. A shrewdly written DOS could appear as all three at different times to different people.

Bill and I then started to brainstorm, and we can't stop. We've got a blackboard full of neat names, just waiting for some one to write code for. We may have stumbled on to some previously-used names, like SoliDOS and ProntoDOS, but for the most part I think we have cornered the market.

         AmbiDOS   MisoDOS   PhiloDOS  BiblioDOS ViviDOS   DiaDOS
         PaleoDOS  MesoDOS   NeoDOS    PsychoDOS MoriDOS   Dial-a-DOS
         ChromoDOS BlancoDOS TechniDOS SomatoDOS DulciDOS  AnoDOS
         AcriDOS   FeloniDOS BaloniDOS FormiDOS  MiniDOS   CathoDOS
         MicroDOS  MidiDOS   MilliDOS  MegaDOS   NanoDOS   VagaDOS
         TeraDOS   UniDOS    BioDOS    StupiDOS  TorriDOS  FabriDOS
         SemiDOS   PeriDOS   AntiDOS   AnteDOS   ProsDOS   ExoDOS
         HypoDOS   HyperDOS  OvaDOS    PupaDOS   PropoDOS  EnDOS
         ArcheDOS  StatiDOS  DynamoDOS DynaDOS   ProtoDOS  EschatoDOS
         OsteoDOS  MultiDOS  PuroDOS   CardioDOS PyroDOS   PrimaDOS
         FrigiDOS  InterDOS  AndroDOS  GynoDOS   GymnoDOS  PseudoDOS
         HieroDOS  SpiroDOS  HelioDOS  CycloDOS  AutoDOS   AggreDOS
         ManoDOS   ChiroDOS  PetroDOS  LithoDOS  AeroDOS   PosiDOS
         PlanoDOS  LiquiDOS  MarbleDOS PedoDOS   GraviDOS  NegaDOS
         PedaDOS   GeriaDOS  NutriDOS  FlexiDOS  PleniDOS  NecroDOS
         VisiDOS   InvisiDOS FluoriDOS FloriDOS  FaunaDOS  PensaDOS
         ThanaDOS  AgriDOS   NaviDOS   NovaDOS   SpuriDOS  MensaDOS
         StereoDOS VerbiDOS  VermiDOS  CineDOS   GeoDOS    TragiDOS
         MonoDOS   DuoDOS    CobraDOS  FerroDOS  OxyDOS    AfroDOS
         EuroDOS   NippoDOS  FrancoDOS IndoDOS   CanaDOS   HispanoDOS

Get the idea?

OBJ.APWRT][F updated to AWIIe Toolkit | Don Lancaster

I have packed even more goodies on eight disk sides, combining the HACKER and USER packages into one powerful Toolkit. The price is only slightly higher... They were $29.50 each, now only $39.50 together.

Now that we have yet another Apple monitor, vastly different yet purportedly compatible, guess what! Applewriter //e is not QUITE compatible with the //c. Surprise, surprise! The status line display gets turned into garbage. One of the patches included in the new AWIIe Toolkit solves the problem admirably. This AWIIE CLARIFIER Applesloth program modifies your Applewriter IIe backup diskettes to eliminate trashing of the IIc status display line. Here it is now, more than slightly compressed for AAL, to tease you into getting the whole Toolkit:

         100  REM *---------------------------------------*
         200  REM *  COPYRIGHT 1984 BY DON LANCASTER AND  *
         220  REM *  SYNERGETICS,  BOX 1300, THATCHER AZ  *
         240  REM *  85552      Phone:  (602) 428-4073    *
         260  REM *   ALL  COMMERCIAL RIGHTS RESERVED     *
         280  REM *---------------------------------------*
         380  TEXT : HOME : HIMEM: 8000
         400  HTAB 8: PRINT "Applewriter IIe CLARifier": PRINT
         600  REM Check Validity
         660  PRINT CHR$(4)"BLOAD OBJ.APWRT][F,A$2300
         670  IF  PEEK (14815) <  > 188 THEN 880
         680  IF  PEEK (15052) <  > 41 THEN 880
         690  IF  PEEK (15096) <  > 59 THEN 880
         695  REM Install Patches
         700  POKE 14815,60: POKE 14816,36: POKE 14817,207:
               POKE 14818,16: POKE 14819,2: POKE 14820,169:
               POKE 14821,62
         710  POKE 15052,208: POKE 15053,42
         720  POKE 15062,96
         730  POKE 15096,41: POKE 15097,127: POKE 15098,201:
               POKE 15099,96: POKE 15100,176: POKE 15101,208:
               POKE 15102,201: POKE 15103,64
         740  POKE 15104,144: POKE 15105,204: POKE 15106,41:
               POKE 15107,63: POKE 15108,176: POKE 15109,200
         750  PRINT CHR$(4)"UNLOCK OBJ.APWRT][F"
         760  PRINT CHR$(4)"BSAVE OBJ.APWRT][F,A$2300,L$30D3"
         770  PRINT CHR$(4)"LOCK OBJ.APWRT][F"
         870  PRINT "IT WORKED!" : END
         880  PRINT "Will not verify as AWIIe; patch ABORTED" : END

Gotchas: Fixes only the status line. Rare and brief changes in the flashing cursor symbol will remain.

Using the PRT Command | Bill Morgan

New users of the S-C Macro Assembler have asked for examples of how to use some of the customizing features. For example, just now I had a call from a gentleman who needed to know how to set up the PRT vector to turn on his printer and send the special control strings it requires.

It happens that I had the same problem just a few weeks ago. I just picked up an OkiData 92 printer, which I am quite happy with, except for a couple of small warts. Setting Elite spacing (12 characters/inch, 8 lines/inch) on that printer requires these hex codes: 9C 9B B8. The catch is that 9C, which corresponds to Control-backslash. I can't type CTRL-\ on my Apple II+! Besides, by the time I type in the commands to turn on the printer, set Elite mode, and set a left margin, I have entered 15 keystrokes. That's too many for my lazy, dyslexic fingers, so I came up with a PRT command to do the whole job.

The addresses in this routine are set up for the 40-column Version 1.1 Language Card assembler. If you are using another version, check to make sure that the patch space is indeed all zeroes. All $D000 versions of the assembler have some blank space before $E000. If you are using a $1000 version, look to see if there is some space available between the end of the assembler and the beginning of the Symbol Table and set PATCH.SPACE to that address. You will also have to set PRT.VECTOR to $1009.

Here are the exact steps to use this patch:

         Start the assembler.

         $C083 C083
         $D01C:0 D0 0 F8

         $AA60.AA61

         LOAD S.PRT

         ASM

         $D01C:0 0 0 0
         $C080

         BSAVE <assembler>,A$D000,L$XXXX

The $AA60.AA61 line gives you the length that you will need to use for the BSAVE command. Substitute the filename of the version you use for <assembler> in the above command.

If you are using Version 1.0 of the assembler, things are a little different. You should omit the $D01C entries in the above commands, delete lines 1090 and 1100, and add this line to the program:

         1125        .TA $800

Then, after the assembly, install the patch with $DF00<800.81EM and $D009: 4C 00 DF. These extra steps are necessary because Version 1.0 lacks the ability to override memory protection during assembly.

Lines 1270-1290 are where you should install the codes your printer needs.

      1000 *--------------------------------
      1010 *
      1020 *   SAMPLE PRT COMMAND
      1030 *
      1040 *--------------------------------
      1050 PRT.VECTOR  .EQ $D009
      1060 PATCH.SPACE .EQ $DF00
      1070 MON.COUT    .EQ $FDED
      1080 *--------------------------------
      1090        .OR PRT.VECTOR
      1100        JMP PRT         JUMP TO HANDLER
      1110 *--------------------------------
      1120        .OR PATCH.SPACE
      1130
      1140 PRT    LDX #0
      1150 .1     LDA STRING,X    OUTPUT THE
      1160        BEQ .2          CONTROL
      1170        JSR MON.COUT    STRING
      1180        INX
      1190        BPL .1
      1200
      1210 .2     RTS
      1220 *--------------------------------
      1230 STRING .HS 8D84        <CR><^D>
      1240        .AS -/PR#1/
      1250        .HS 8D          <CR>
      1260
      1270        .HS 9C9BB8      ELITE SPACING
      1280        .HS 9BA5C3      LEFT MARGIN
      1290        .HS B0B9B0      90 DOT SPACES
      1300        .HS 00          END MARKER

Revisiting $48:0 | Bob Sander-Cederlof

Remember all those warnings about storing 0 in $48 after DOS had a whack at your zero page? Maybe not, but let me remind you.

Apple's monitor uses locations $45 through $49 in a very special way. Ignoring this, the writers of DOS also used them. When you start execution from the monitor (using the G, S, or T commands) The data in these locations gets loaded into the registers: $45 into A, $46 into X, $47 into Y, $48 into P (status), and $49 into S (stack pointer). When a program hits a BRK opcode, or the S command has finished executing a single opcode, the monitor saves these five registers back into $45...$49.

No serious problem, unless you like to enter the monitor and issue the G, S, or T commands. Even less of a problem, because the S and T commands were removed from the monitor ROM when the Apple II Plus came out. And if you don't care what is in the registers anyway....

But the P-register is rather special, too. One of its bits, called "D", controls how arithmetic is performed. If "D" is zero, arithmetic will be done in the normal binary way; if D=1, arithmetic is done in BCD mode. That is, adding one to $49 will produce $50 rather than $4A. If the program you are entering doesn't expect to be in decimal mode, and tries arithmetic, you will get some rather amusing results.

Hence the warning: before using the G command from the monitor, type 48:0 to be sure decimal mode is off. Later versions of DOS store 0 into $48 after calling those routines which use $48. And the monitor stores 0 into $48 whenever you hit the RESET key (or Control-RESET).

         ******************************************************
         *                                                    *
         *   Now I am here to tell you that storing 0 into    *
         *   $48 is ALL WRONG!  It took Bill and me 5 hours   *
         *   to unravel the mystery caused by storing zero    *
         *   there!                                           *
         *                                                    *
         ******************************************************

You should put into $48 a sensible value. Better, DOS should never use $45 through $48; if it must use them, save and restore them. There are eight bits in the P-register, and in the 6502 seven of them are important. One of them, we discovered, is VERY important.

The bit named "I" controls the IRQ interrupt. If I=1, IRQ interrupts will not be accepted. If I=0, IRQ interrupts will be accepted. So...who cares about interrupts?

Hardly anyone uses interrupts in Apple II's, because of all the hidden problems. But there are some very nice boards for the Apple that are designed to be used with interrupts. Most of them are safe, because RESET disables their interrupt generators.

Need I say that we discovered a board that does not disable the interrupt generators when you hit RESET? The Novation Cat Modem (a very excellent product) leaves at least one of its potential IRQ sources in an indeterminate state. IRQ's don't immediately show up, though, because they are trapped until you have addressed any of the soft switches on the card. But, for example, if that card is in slot 2 and I read or write any location from $C0A0 through $C0AF, IRQ's start coming. Still no problem, because I=1 in the P-register.

UNTIL WE USE THE MONITOR G COMMAND!

If I use the monitor G command, location $48, containing 0, is loaded into the P-register. Then an IRQ gets through and sends the 6502 vectoring through an unprepared vector at $3FE,3FF and BANG!

Our solution was to put SEI instructions in various routines, and to make sure that $48 contains 4, not 0, before using the G command.

From now on, whenever you hear that you need to be sure $48 contains zero, think four.

More Random Number Generators | Bob Sander-Cederlof

I published my "Random Numbers for Applesoft" article last month just in time. The June issue of Micro includes a 9.5 page article called "A Better Random Number Generator", by H. Cem Kaner and John R. Vokey. The article reports on work funded by the Natural Sciences and Engineering Research Council of Canada (NSERC).

The authors give an excellent overview of the various methods used to test random number generators, and the methods they used during their seven years of research to produce three "better" generators. It is worth buying a copy of Micro to get a copy of this article.

The authors used the same linear congruential algorithm I discussed last month, but with different parameters. My favorite last month was:

           R(new) = ( R(old) * A + C ) mod 2^32
           where A = 314159269
             and C = 907633386

Kaner and Vokey decided to use a 40-bit seed and use mod 2^40 in calculating each successive value. After extensive analysis and testing, they came up with three generators based on the following values for "A" and "C":

           RNG 1:  A = 31415938565
                   C = 26407

           RNG 2:  A = 8413453205
                   C = 99991

           RNG 3:  A = 27182819621
                   C = 3

There are an unusually large number of typos in the article, and some of them are hard to decipher. The value 26407 above was written in the comment field as 24607; however, in the hexadecimal constant assembly code it was 0000006727, which is 26407. Even worse, in the listing there are missing lines of code and missing characters here and there. All of the immediate mode instructions are missing a "#" character. Four or five labels are never defined in the listing.

Since the program as printed cannot possibly be successfully loaded, assembled, POKEd, or executed, I have chosen to re-write it for inclusion here, after my own style. I believe my version is also significantly improved in coding and speed.

The reason given for choosing to work with 40 bits rather than 32, even though Applesoft only stores 32 and using 40 takes longer, was that it is important to provide more values between 0.0 and 2^-32. I tend to disagree on the importance of this, since most uses of random numbers on the Apple are for games, and simulate such simple things as dealing cards or throwing dice. Perhaps more serious simulations need more precise randomness. Of course they also increase by a factor of 256 the number of numbers generated before the sequence repeats.

Buried in the middle of the program is a well-optimized 40-bit multiplication loop. You might enjoy puzzling out how it works!

The program uses USR(x), where x selects which of the three generators to use. There is no provision for setting the seed or for selecting a range other than 0...1, such as I included in my programs last month. Some enterprising individual will marry the shell of my USR subroutine with the RNG of Kaner and Vokey to produce a really great Applesoft Random Number Generator.

      1  .LIF
      1000 *SAVE S.KANER & VOKEY
      1010 *--------------------------------
      1020 *      BASED ON PROGRAM PRINTED IN MICRO MAGAZINE
      1030 *      JUNE 1984, PAGES 33,34, BY H. CEM KANER
      1040 *                             AND JOHN R. VOKEY
      1050 *--------------------------------
      1060 USER.VECTOR .EQ $0A,0B,0C
      1070 FAC         .EQ $9D THRU $A1
      1080 FAC.SIGN    .EQ $A2
      1090 FAC.EXT     .EQ $AC
      1100 *--------------------------------
      1110 NORMALIZE.FAC.2 .EQ $E82E
      1120 *--------------------------------
      1130        .OR $300
      1140        .TF B.KANER & VOKEY
      1150 *--------------------------------
      1160 LINK   LDA #$4C     "JMP" OPCODE
      1170        STA USER.VECTOR
      1180        LDA #RANDOM
      1190        STA USER.VECTOR+1
      1200        LDA /RANDOM
      1210        STA USER.VECTOR+2
      1220        RTS
      1230 *--------------------------------
      1240 Z.C    .HS 00.00.00.67.27  26407
      1250 Z.A    .HS 07.50.89.2E.05  31415938565
      1260 Z.OLD  .HS 00.00.00.00.00
      1270 *--------------------------------
      1280 Y.C    .HS 00.00.01.86.97  99991
      1290 Y.A    .HS 01.F5.7B.1B.95  8413453205
      1300 Y.OLD  .HS 00.00.00.00.00
      1310 *--------------------------------
      1320 X.C    .HS 00.00.00.00.03  3
      1330 X.A    .HS 06.54.38.E9.25  27182819621
      1340 X.OLD  .HS 00.00.00.00.00
      1350 *--------------------------------
      1360 GROUP      .BS 1
      1370 MULTIPLIER .BS 5
      1380 OLD        .BS 5
      1390 *--------------------------------
      1400 RANDOM LDY #Z.C-Z.C+4
      1410        LDA FAC.SIGN
      1420        BMI .1       SELECT Z
      1430        LDY #Y.C-Z.C+4
      1440        LDA FAC
      1450        BEQ .1       SELECT Y
      1460        LDY #X.C-Z.C+4   SELECT X
      1470 .1     STY GROUP    SAVE FOR LATER
      1480 *---LOAD SELECTED GROUP----------
      1490        LDX #4       MOVE 5 BYTES
      1500 .2     LDA Z.C,Y
      1510        STA FAC+1,X
      1520        LDA Z.A,Y
      1530        STA MULTIPLIER,X
      1540        LDA Z.OLD,Y
      1550        STA OLD,X
      1560        DEY
      1570        DEX
      1580        BPL .2
      1590 *---MULTIPLY INTO FAC------------
      1600        LDX #4
      1610 .3     STX FAC.EXT  USE FOR BYTE COUNT
      1620        LDA MULTIPLIER,X
      1630        STA FAC      SAVE FOR 8-BIT MULITPLY
      1640        LDY #7       COUNT BITS
      1650 .4     LSR FAC      GET RIGHTMOST BIT INTO CARRY
      1660        BCC .6       =0, SO WE DO NOT ADD THIS TIME
      1670        CLC          =1, SO WE BETTER ADD
      1680 .5     LDA FAC+1,X
      1690        ADC OLD,X
      1700        STA FAC+1,X
      1710        DEX
      1720        BPL .5
      1730 .6     ASL OLD+4    SHIFT MULTIPLICAND LEFT
      1740        ROL OLD+3
      1750        ROL OLD+2
      1760        ROL OLD+1
      1770        ROL OLD
      1780        LDX FAC.EXT  GET BYTE COUNT AGAIN
      1790        DEY          NEXT BIT
      1800        BPL .4
      1810        DEX          REDUCE BYTE COUNT
      1820        BPL .3       ...MORE BYTES
      1830 *---SAVE NEW SEED IN OLD---------
      1840        LDX #4
      1850        LDY GROUP
      1860 .7     LDA FAC+1,X
      1870        STA Z.OLD,Y
      1880        DEY
      1890        DEX
      1900        BPL .7
      1910 *---NORMALIZE NEW VALUE----------
      1920        LDY #$80     ASSUME A FRACTION
      1930 .8     LDA FAC+1    LOOK AT LEADING BIT
      1940        BMI .9       ...FINISHED NORMALIZING
      1950        LSR FAC.SIGN
      1960        ROR FAC+4
      1970        ROR FAC+3
      1980        ROR FAC+2
      1990        ROR FAC+1
      2000        DEY
      2010        CPY #$58
      2020        BCS .8
      2030        LDY #0       LESS THAN 2^-40 IS ZERO
      2040 .9     STY FAC      EXPONENT
      2050        LDA #0
      2060        STA FAC.SIGN MAKE IT POSITIVE
      2070        RTS

Booting ProDOS with a Modified Monitor ROM | Jan Eugenides

You may have already figured this out, but ProDOS won't boot if you have installed S. Knouse's modified ROM in your Apple. This can easily be fixed, as follows:

- On track 1, sector C, change bytes B4-B6 from AE B3 FB to A2 EA EA. This tells ProDOS your machine is a II+. If it's a //e, make B5 an A0 instead.
- On track 1, sector 9, change bytes 60-61 from A9 00 to A5 0C. This defeats the ROM check routine.

Ta daaa! Now ProDOS works just fine with your modified ROM.

Fixing the Andromeda 16K Card | Bob Bernard

In the April 1984 Call-APPLE there was a letter from John Wallace regarding a problem with the Andromeda 16K RAM card. As this card was the second on the market, first after Apple's (which was bundled with Pascal), there are probably still tens of thousands in use. Yet the Andromeda is anathema to some hardware and software.

In particular, it played havoc with John Wallace's copy of Apple PIE (a popular word processor from yesteryear), and my Lobo 8" floppy drive controller (another relic, I suppose). Bob S-C tells of running into the problem too:

> "I have an Andromeda board, and I ran into this problem with early versions of ES-CAPE. Using a STA (or other store) opcode to any soft switches on the Andromeda card write-protected the card. Using two stores in a row to try to write-enable the card does no good either. I had to change all stores to loads or BITs to make it work. Apple's board accepts either stores or loads, as do all other memory cards I have tested."

There are probably lots of interfaces and programs out there which stumble over Andromeda. Wallace details a hardware modification to the Andromeda board which makes it work the same as all other memory boards. I found a slightly simpler way, and I recommend that all Andromeda owners fix their boards as soon as possible.

Remove the 74LS08 chip at board location U13. Bend pin 10 out so that it sticks straight out, and plug the chip back into its socket so that pin 10 is on the outside. Solder a small wire to pin 10 (carefully), and solder the other end of the wire to pin 14 of the same chip. Or, you can solder to a solder pad pin 14 is connected to, as shown in the drawing below. (Pin 14 is connected to Vcc, the +5 volts line.) That's all there is to it.

John Wallace suggests using a 1K resistor rather than a wire, but I found a wire is sufficient.

With the wire installed, both reads and writes can be used to switch the card, just like Apple intended it.

Finding the Erroneous Bit Using CRC | Bruce Love Hamilton, New Zealand

The April 1984 AAL article about using Cyclic Redundancy Codes posed the question, "How do you find out which bit was in error, assuming only one was wrong?" I found a way.

My algorithm assumes that there was one and only one bit wrong in the entire 258-byte message (256 bytes of original message plus 2 bytes of CRC). The bits are numbered left-to-right, or most significant bit of first byte received through the least significant bit of the CRC, 0 through $80F (or 2063, if you prefer decimal).

After receiving the data and CRC, the RECV program has computed a composite CRC and the result will be $0000 if there were no errors. If the result is non-zero, it uniquely determines which bit was wrong. Here is a summary of my algorithm for finding which bit:

           let bit.number = 2063
           let dummy.crc  = 1
        -->if dummy.crc = crc, then we found the bit
       |   decrement bit.number
       |   shift dummy.crc left 1 bit
       |   if carry set, EOR with $1021
        ---loop

[ The following comments are by Bob Sander-Cederlof. ]

The program listing which follows is an addendum to the listing in the April issue of AAL. Lines 3070 through the end should be appended to the program in that issue. If you buy the AAL Quarterly Disk, it will already be there.

The sequence I used for testing the program went like this. First I assembled the whole program, April's plus the one below. Then I typed "$4000<F800.F8FFM" to move a copy of the monitor's first page into the test buffer. I thought this would be "interesting" data to play with. Then these steps:

           :MGO SEND       (fakes sending the buffer)
           1F45            (SEND prints out the CRC for BUFFER)
           :$4000          (see what is there)
           4A              (it was $4A)
           :$4000:CA       (make a fake error in the 1st bit)
           :MGO RECV
           xxxx            (some non-zero value)
           :MGO FIND.BAD.BIT
           0000            (the bad bit was the first bit)
           $4000:4A        (restore the correct bit

Then I tried the same steps on various other bit positions, with accurate results in every case.

      1000 *SAVE S.CRC BAD BIT FINDER
      1010 *--------------------------------
      1020 BUFFER .EQ $4000
      1030 LIMIT  .EQ $4102
      1040 *--------------------------------
      1050 CRC    .EQ $00,01
      1060 PNTR   .EQ $02,03
      1070 TPTR   .EQ $04,05
      1080 TMASK  .EQ $06
      1090 SPTR   .EQ $07,08
      1100 SMASK  .EQ $09
      1110 *--------------------------------
      1120 PRNTAX .EQ $F941
      1130 CROUT  .EQ $FD8E
      1140 PRBYTE .EQ $FDDA
      1150 COUT   .EQ $FDED
      1160 *--------------------------------
      3060 *--------------------------------
      3070 *   FIND WHICH BIT IS BAD IN BUFFER+CRC
      3080 *
      3090 *      RESULT IS BIT POSITION IN MESSAGE,
      3100 *      WHERE THE FIRST BIT OF THE MESSAGE IS BIT 0
      3110 *      AND (IN THIS CASE) THE LAST CRC BIT IS BIT $80F.
      3120 *
      3130 *      ALGORITHM BY BRUCE LOVE, AUSTRIALIA.
      3140 *--------------------------------
      3150 BIT.NUMBER .EQ $10,11
      3160 DUMMY.CRC  .EQ $12,13
      3170 *--------------------------------
      3180 FIND.BAD.BIT
      3190        LDA #$80F    TOTAL # BITS - 1
      3200        STA BIT.NUMBER    (WE WILL COUNT BACKWARDS)
      3210        LDA /$80F
      3220        STA BIT.NUMBER+1
      3230        LDA #$0001   STARTING POINT FOR BIT FINDER
      3240        STA DUMMY.CRC
      3250        LDA /$0001
      3260        STA DUMMY.CRC+1
      3270 .1     LDA CRC      COMPARE RECEIVED CRC WITH
      3280        CMP DUMMY.CRC        PROCESSED VALUE;
      3290        BNE .2       IF THEY MATCH, WE HAVE FOUND THE
      3300        LDA CRC+1    BAD BIT.
      3310        CMP DUMMY.CRC+1
      3320        BEQ .4       ...FOUND BAD BIT!
      3330 .2     LDA BIT.NUMBER        DECREMENT BIT COUNTER
      3340        BNE .3
      3350        DEC BIT.NUMBER+1
      3360        BMI .5       WENT TOO FAR
      3370 .3     DEC BIT.NUMBER
      3380        ASL DUMMY.CRC
      3390        ROL DUMMY.CRC+1
      3400        BCC .1
      3410        LDA DUMMY.CRC
      3420        EOR #$21
      3430        STA DUMMY.CRC
      3440        LDA DUMMY.CRC+1
      3450        EOR #$10
      3460        STA DUMMY.CRC+1
      3470        JMP .1
      3480 .4     LDA BIT.NUMBER+1  PRINT THE BIT NUMBER
      3490        JSR PRBYTE        (IF $8000, THE ERROR WAS
      3500        LDA BIT.NUMBER    NOT A SINGLE BIT)
      3510        JSR PRBYTE
      3520        JMP CROUT
      3530 .5     BRK
      3540 *--------------------------------

The Barkovitch Utilities | Bob Sander-Cederlof

Did you notice Dave Barkovitch's ad last month? He has written a very handy set of utilities for us serious Applers, and sells 'em cheap! Be prepared to puzzle your way through his admittedly skimpy documentation, but it is all there.

The I/O Tracer comes in EPROM on a little card that plugs into any slot 1-7 for only $40.50 (including shipping). I/O Tracer is essentially a powerful disk ZAP utility, allowing you to read/write/edit any DOS 3.3 sector. You see an entire sector at once on the screen, in either hex or ASCII, along with all status information.

Dave's Single-Step Trace program will help you debug assembly language. He likes it better than the other commercial varieties of debuggers, and sells it for only $35.

Any questions, call Dave at (201) 499-0636.

Converting to Motorola S-Format | Bob Sander-Cederlof

Last April I told how to convert object code to the Intellec Hex Format (AAL pages 14-18, April, 1984). Both Intel and Zilog use that format. Motorola, on the other hand, has its own format for object code. It is similar, but it is significantly different. If you are programming for a Motorola chip, or using a PROM burner that uses Motorola format, then the following program is for you.

The Motorola S-Type format has three kinds of records: header, data, and end-of-file. Each record begins with the letter "S" and ends with a carriage return linefeed (CRLF). According to the samples I have seen, all of the bytes in a record are in ASCII code with the high bit zero. (Apple peripherals tend to like the high bit = 1, so I made this an option.) The maximum length including the "S" and up to but not including the CRLF is 64 "frames". Between the "S" and CRLF, each record consists of five fields:

> Record format field: ASCII 0, 1, or 9 (hex $30, $31, or $39) for header, data, or end-of-file records respectively.

> Byte count field: the count expressed as two ASCII digits of the number of bytes (half the number of frames) from address field through the checksum field. The minimum is 3, and the maximum is 60 decimal or $3C hexadecimal.

> Address field: four frames representing the four digits of the load address for data bytes in a data record, or the run address in an end-of-file record. All four digits will be "0" in a header record.

> Data field: two hex digits for each byte of data. The number of bytes will be 3 less than the number specified in the byte count field, because that count includes two bytes for the address and one byte for the checksum.

> Checksum field: two hex digits representing the 1's complement of the binary sum of all the bytes in the previous four fields.

If you compare the S-Type format with the Intellec format, you will note several differences:

- records start with "S" instead of ":"
- the fields are in a different order
- there was no header record for Intellec
- the byte count covers three fields instead of only the data field
- the checksum is computed by a different algorithm and covers different data.

I tried to use as much as possible of the Intellec program when writing the Motorola program. You will find a lot of similarities if you compare the two. Both are designed to be used with the monitor's control-Y instruction. Both expect you to enter the output slot number or address in zero-page bytes 0 and 1.

The Motorola program requires two additional pieces of information. It needs a byte at 0002 which will be either $00 or $80, denoting whether to set the high bit to 0 or 1 on every output byte. It also needs an eight character name for the header record. This should be entered in zero-page locations 0003 through 000A.

For example, assume the object code I want to format is in the Apple between $6000 and $67FF. In the target processor it will load at address $1000. The name of the program is "SAMPLE". I want to send the data with the high bit = 0. The device I want to send it to is connected to an intelligent peripheral card in slot 2. Here is what I type:

    	]BRUN B.MOTOROLA FORMATTER     (or :BRUN B.MOTOROLA FORMATTER
    	]CALL -151                     (or :MNTR)
    	*0:2 0                         (send to slot 2)
    	*:0                            (hi-bit = 0)
    	*:53 41 4D 50 4C 45 20 20      ("SAMPLE")
    	*1000<6000.67FF^Y              (^Y means control-Y)

I recommend comparing this program and my description of it with the Intellec program and article in the April AAL. Here is the Motorola formatter:

      1000 *SAVE S.MOTOROLA S-TYPE OBJECT
      1010        .OR $800
      1020 *--------------------------------
      1030 PORT       .EQ $00,01
      1040 HI.BIT     .EQ $02
      1050 NAME       .EQ $03 ... $0A
      1060 CHECK.SUM  .EQ $12
      1070 TYPE       .EQ $13
      1080 COUNT      .EQ $14
      1090 REMAINING  .EQ $15,16
      1100 START      .EQ $17,18
      1110 END        .EQ $19,1A
      1120 TARGET     .EQ $1B,1C
      1130 *--------------------------------
      1140 A1     .EQ $3C,3D
      1150 A2     .EQ $3E,3F
      1160 A3     .EQ $40,41
      1170 A4     .EQ $42,43
      1180 A5     .EQ $44,45
      1190 *--------------------------------
      1200 CTRLY.VECTOR        .EQ $3F8 THRU $3FA
      1210 DOS.REHOOK          .EQ $3EA
      1220 *--------------------------------
      1230 MON.NXTA4           .EQ $FCB4
      1240 MON.CROUT           .EQ $FD8E
      1250 MON.PRHEX           .EQ $FDDA
      1260 MON.COUT            .EQ $FDED
      1270 MON.SETVID          .EQ $FE93
      1280 *--------------------------------
      1290 *      SETUP CONTROL-Y
      1300 *--------------------------------
      1310 SETUP  LDA #SEND.DATA
      1320        STA CTRLY.VECTOR+1
      1330        LDA /SEND.DATA
      1340        STA CTRLY.VECTOR+2
      1350        LDA #$4C
      1360        STA CTRLY.VECTOR
      1370        RTS
      1380 *--------------------------------
      1390 *   *0:XX YY   (LO,HI OF PORT)
      1400 *   *:ZZ       (00 OR 80, FOR ASCII HI-BIT)
      1410 *   *:C1 C2 C3 C4 C5 C6 C7 C8   ASCII VALUES FOR
      1420 *                   THE 8 CHARACTERS OF THE NAME
      1430 *   *TARGET<START.END<Y>
      1440 *      IF PORT IS 0, DO NOT CHANGE OUTPUT
      1450 *      IF PORT IS 1...7, OUTPUT TO SLOT.
      1460 *         ELSE OUTPUT TO SUBROUTINE
      1470 *      SEND BYTES START...END
      1480 *
      1490 *      1.  TURN ON OUTPUT PORT
      1500 *      2.  SEND ID RECORD
      1510 *      3.  SEND DATA RECORDS
      1520 *      4.  SEND EOF RECORD
      1530 *      5.  TURN OFF OUTPUT PORT
      1540 *--------------------------------
      1550 SEND.DATA
      1560        JSR SAVE.PARAMETERS
      1570        JSR TURN.ON.OUTPUT.PORT
      1580        JSR SEND.ID.RECORD
      1590        JSR RESTORE.PARAMETERS
      1600        JSR SEND.DATA.RECORDS
      1610        JSR SEND.EOF.RECORD
      1620        JMP TURN.OFF.OUTPUT.PORT
      1630 *--------------------------------
      1640 SAVE.PARAMETERS
      1650        LDX #1
      1660 .1     LDA A1,X
      1670        STA START,X
      1680        LDA A2,X
      1690        STA END,X
      1700        LDA A4,X
      1710        STA TARGET,X
      1720        DEX
      1730        BPL .1
      1740        RTS
      1750 *--------------------------------
      1760 RESTORE.PARAMETERS
      1770        LDX #1
      1780 .1     LDA START,X
      1790        STA A1,X
      1800        LDA END,X
      1810        STA A2,X
      1820        LDA TARGET,X
      1830        STA A4,X
      1840        DEX
      1850        BPL .1
      1860        RTS
      1870 *--------------------------------
      1880 TURN.ON.OUTPUT.PORT
      1890        LDX PORT+1     HI-BYTE OF PORT SPECIFIED
      1900        BNE .1
      1910        LDA PORT       LO-BYTE, MUST BE SLOT
      1920        AND #$07
      1930        BEQ .3       SLOT 0, JUST SCREEN
      1940        ORA #$C0
      1950        BNE .2       ...ALWAYS
      1960 .1     TXA          HI-BYTE OF SUBROUTINE
      1970        LDX PORT       LO-BYTE OF SUBROUTINE
      1980 .2     STA $37
      1990        STX $36
      2000        JSR DOS.REHOOK
      2010 .3     RTS
      2020 *--------------------------------
      2030 SEND.ID.RECORD
      2040        LDA #'0'     TYPE = "0"
      2050        STA TYPE
      2060        LDA #8       COUNT = 8
      2070        STA COUNT
      2080        LDA #0       ADDR=0
      2090        STA A4
      2100        STA A4+1
      2110        STA A1+1
      2120        STA A2+1
      2130        LDA #NAME
      2140        STA A1
      2150        LDA #NAME+7
      2160        STA A2
      2170        JMP SEND.RECORD
      2180 *--------------------------------
      2190 SEND.DATA.RECORDS
      2200        LDA #'1'     TYPE = "1"
      2210        STA TYPE
      2220        INC A2       POINT JUST BEYOND THE END
      2230        BNE .1
      2240        INC A2+1
      2250 .1     SEC
      2260        LDX #20
      2270        LDA A2       SEE HOW MANY BYTES LEFT
      2280        SBC A1
      2290        STA REMAINING
      2300        LDA A2+1
      2310        SBC A1+1
      2320        STA REMAINING+1
      2330        BNE .2       USE MIN(20,A2-A1+1)
      2340        CPX REMAINING
      2350        BCC .2
      2360        LDX REMAINING
      2370        BEQ .3       ...FINISHED
      2380 .2     STX COUNT
      2390        JSR SEND.RECORD
      2400        JMP .1       ...ALWAYS
      2410 .3     RTS
      2420 *--------------------------------
      2430 SEND.EOF.RECORD
      2440        LDA #0       # OF DATA BYTES = 0
      2450        STA COUNT
      2460        LDA #'9'     TYPE="9"
      2470        STA TYPE
      2480        LDA TARGET   RUN ADDRESS (LO)
      2490        STA A4
      2500        LDA TARGET+1 RUN ADDRESS (HI)
      2510        STA A4+1
      2520        JMP SEND.RECORD
      2530 *--------------------------------
      2540 TURN.OFF.OUTPUT.PORT
      2550        JSR MON.SETVID
      2560        JMP DOS.REHOOK
      2570 *--------------------------------
      2580 SEND.RECORD
      2590        LDA #'S'     LETTER "S"
      2600        JSR SEND.FRAME
      2610        LDA TYPE     TYPE "0", "1", OR "9"
      2620        JSR SEND.FRAME
      2630        LDA #0       INIT CHECKSUM
      2640        STA CHECK.SUM
      2650        CLC
      2660        LDA COUNT    SEND BYTE COUNT
      2670        ADC #3       ...INCLUDING ADDR AND CSUM
      2680        JSR SEND.BYTE
      2690        LDA A4+1     SEND ADDRESS
      2700        JSR SEND.BYTE
      2710        LDA A4
      2720        JSR SEND.BYTE
      2730        LDA COUNT    ANY DATA?
      2740        BEQ .2       ...NO
      2750        LDY #0       ...YES, SEND DATA
      2760 .1     LDA (A1),Y
      2770        JSR SEND.BYTE
      2780        JSR MON.NXTA4
      2790        DEC COUNT
      2800        BNE .1
      2810 .2     LDA CHECK.SUM     SEND CHECK SUM
      2820        EOR #$FF
      2830        JSR SEND.BYTE
      2840        LDA #$0D     SEND CRLF
      2850        JSR SEND.FRAME
      2860        LDA #$0A     LINEFEED
      2870        JMP SEND.FRAME
      2880 *--------------------------------
      2890 SEND.BYTE
      2900        PHA          SAVE BYTE
      2910        CLC
      2920        ADC CHECK.SUM     ACCUMULATE CHECKSUM
      2930        STA CHECK.SUM
      2940        PLA          RECOVER BYTE
      2950        PHA          SAVE ANOTHER COPY
      2960        LSR          READY FIRST DIGIT
      2970        LSR
      2980        LSR
      2990        LSR
      3000        JSR SEND.DIGIT
      3010        PLA          RECOVER BYTE
      3020        AND #$0F     READY SECOND DIGIT
      3030 SEND.DIGIT
      3040        ORA #$30     CHANGE TO ASCII
      3050        CMP #$3A
      3060        BCC SEND.FRAME
      3070        ADC #6       CHANGE TO A...F
      3080 SEND.FRAME
      3090        ORA HI.BIT   $00 OR $80
      3100        JMP MON.COUT
      3110 *--------------------------------
      3120        .OR $300
      3130 SAMPLE
      3140        .HS 86.44.B7.01.00.41.42.43
      3150        .HS 44.45.46.47.48.49.4A.4B
      3160        .HS 4C.4D.4E

Making a 65C02 Work in my Apple II Plus | William O'Ryan

I am writing this on my Apple II Plus running a 2 MHz 65C02 (GTE G65SC02PI-2). All is well now, but it took some doing.

A few days after pluggin in the chip I started noticing problems. Applesoft found itself unable to process numeric literals, and the version of FORTH I have been developing began acting weird.

Following the tip in AAL that the timing of the fetch-process- save instructions might be responsible, I ran some tests on them. The 65C02 worked flawlessly. Apparently the problem is elsewhere.

After further checking, especially in my FORTH, I found that a certain BNE instruction sitting in the first byte of a page and branching backward into the prior page frequently branched back one byte less than it should.

I'm not a hardware person, but I figured debugging is debugging and I really wanted that chip to work, so I began staring at the circuit diagram in the Apple Reference manual. After several hours I concluded that I stood for input, O for output, D for data, and A for address.

The easiest hypothesis to check seemed to be that data was not getting back from the RAMs to the microprocessor in time. So I wrote down some chip numbers and went downtown to see if I could buy some faster variants. Well, the first two chips I replaced solved the problem.

They were 74LS257 chips at B6 and B7. These chips multiplex the output of RAM with the output of the keyboard and send the result to the 65C02. I replaced them with 74F257 chips. I understand these consume less power, respond faster, and are more susceptible to electrostatic damage.

Anyway, my 65C02 is happy now. I would like to hear whether this modification works in other Apples, and with other 65C02s. Drop a line to Bob and Bill at S-C if you have any word on this.

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

All material herein is copyrighted by S-C SOFTWARE, all rights reserved. Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof. (Apple is a registered trademark of Apple Computer, Inc.)