Volume 4 -- Issue 8 | May 1984 |

In This Issue...

- Random Numbers for Applesoft
- Apple //c
- News from Roger Wagner
- Apple //e ROM Revision
- 65C02 vs the Older Apples
- Decimal Floating Point Arithmetic
- What That Code Did
- Making a Map of Differences

This month we are beginning a series of articles describing a double-precision decimal arithmetic package for Applesoft. Imagine 18-digit arithmetic with none of the screwy rounding errors we are used to seeing in Applesoft's binary arithmetic.

You will also find quick looks at the new Apple //c and a forthcoming set of revised ROMs for the //e. We finally have the solution to a three-year-old mystery! You old-timers might remember that in August of 1981 we published a peculiar little "what does this code do?" item from John Broderick. Well he has revealed answer at long last.

Oops!

There are a couple of bugs in the Intellec Hex Converter we published last month. To correct the program you should delete line 2240 (the INY) and add a LDA #0 at line 2285. That will take care of it! Our thanks to Chaim Palman, of Calcomp, for pointing out the problems.

Random Numbers for Applesoft |
Bob Sander-Cederlof |

The RND function in Applesoft is faulty, and many periodicals have loudly proclaimed its faults. "Call APPLE", Jan 83, pages 29-34, tells them in "RND is Fatally Flawed", and presents an alternative routine which can be called with the USR function.

First, the flaws: 1) the initialization code fails to preset all five bytes of the seed value (only the first four of five are loaded); 2) the RND code uses a poor algorithm, and depends on "tweaks" to make the numbers more random; 3) the RND code does not properly implement the algorithm it appears to be aiming at.

BAD INITIALIZATION. The initialization code is at $F150 in the Applesoft ROMs. This loop moves the CHRGET subroutine down to $B1-C8, and is also supposed to copy the random number seed into $C9-CD. The last byte does not get copied, due to a bug. Changing $F151 from $1C to $1D would fix it. Most of us don't really care about this bug, because we are trying to get random numbers for games and the like, and the more random the better: not copying the last byte could make the numbers generated a little more random from one run to the next. However, some applications in simulation programs require REPEATABLE sequences of random numbers, so the effect of model changes can be seen independent of the random number generator.

POOR ALGORITHM. Most generators use an algorithm which makes the next random number by multiplying the previous one by a constant, and adding another constant. The result is reduced by dividing by a third constant and saving the remainder as the next random number. More on this later. The proper choice of the three constants is critical. I am not sure whether the Applesoft authors just made poor choices, or whether the bugs mentioned below drove them to tweaking. Tweaking the generated value is often thought to produce even more random results. In fact, according to authorities like Donald Knuth, they almost always ruin the generator. Applesoft tweaks the generated value by reversing the middle two bytes of the 32-bit value. Guess what: it ruins the generator, assuming it was good to start with.

BUGGY ALGORITHM. The congruency algorithm described in words above will only work properly when integer arithmetic is used. Applesoft uses floating point arithmetic. Further, Applesoft arithmetic routines expect five-byte operands. For some reason the constants used in RND are only four bytes long each. It appears that the exponents may have been omitted, in the expectation that integer arithmetic was going to be used. You can see the code for RND at $EFAE.

If you want to see some non-random features using RND, type in and RUN the following program:

10 HGR:HCOLOR=3 20 X=RND(1)*280:Y=RND(1)*160 30 HPLOT X,Y 40 GO TO 20

You will see the Hi-Res screen being sprinkled with dots. After about seven minutes, but long before the screen is full, new dots stop appearing. RND has looped, and is replotting the same sequence of numbers. Another test disclosed that the repetition starts at the 37,758th "random" number.

Mathematicians have developed many sophisticated tests for random number generators, but Applesoft fails even these simple ones! Depending on the starting value, you can get the Applesoft generator in a loop. You never get anywhere near the theoretically possible 4 billion different values.

The Call APPLE article proposes a new algorithm. It comes with impressive claims and credentials, but I have not found it to be better than a properly implemented congruential algorithm. The algorithm multiplies the previous seed by 8192, and takes the remainder after dividing by 67099547. This is a congruency algorithm:

X(n+1) = ( a * X(n) + c ) mod m with a=8192, c=0, m=67099547

I re-implemented the Call APPLE algorithm, and my listing follows. The Call APPLE version would not quite fit in page 3, but mine does with a little room to spare. I also dug into some other references and came up with another algorithm, from Knuth. It is also a congruency, but with a=314159269, c=907633386, and m=2^32. This turns out to be easier to compute, and according to Knuth it should be "better". "Better" is in quotes because it is really hard to pin down what are the most important properties. Anyway this one should have very good characteristics.

The RND function does three different things, depending on the argument. You write something like R=RND(X). If X=0, you get the same number as the previous use of RND produced. If X<0, the absolute value of X becomes the new seed value. This allows you to control the sequence when you wish, and also to randomize it somewhat by using a "random" seed. If X>0, you get the next random number. The value will always be a positive number less than 1. If you want to generate a number in a range, you multiply by the width of the range and add the starting value. For example, to generate a random integer between 1 and 10:

R = INT( RND(1)*10 ) + 1

The programs I have written build a little on the options available with RND. They all begin with a little routine which hooks in the USR vector. After executing this, you can write R=USR(X), in other words substitute USR(X) anywhere you would have used RND(X). But I have added, following the Call APPLE article, the option to automatically generate integers in a range based at 0. If 0<X<2, you will get the next random fraction. If X is 2 or greater than 2, you will get a random integer between 0 and X-1. Thus you can make a random integer between 1 and 10 like this:

R = USR(10) + 1

as well as with:

R = INT (USR(1)*10) + 1

I wrote a third program which makes a 16-bit random value. This one uses the seed at $4E and $4F which the Apple increments continuously whenever the standard monitor input loop is waiting for an input keystroke. Integer BASIC uses this seed, and as a result is quite valuable in writing games. My new program gives you all the options stated above, and is significantly quicker than any of the others. It uses a=19125, c=13843, and m=2^16 in a standard congruency algorithm.

If you are seriously interested in random numbers, you need to read and study Donald Knuth. Volume 2 of his series "The Art of Computer Programming" is called "Seminumerical Algorithms". Chapter 3, pages 1-160, is all about random numbers. (There is only one other chapter in this volume, all about arithmetic in nearly 300 pages!) Knuth started the series back in the 60's, with the goal of seven volumes covering most of what programmers do. He finished the first three by 1972, went back and revised the first one, and then evidently got sidetracked into typesetting (several books around a typesetting language he calls "Tex").

Speaking of being sidetracked...!

Knuth ends his chapter with a list of four rules for selecting a, c, and m for congruency algorithms. Let me summarize those rules here:

1. The number m is conveniently taken as the word size. In Applesoft, the floating point mantissa is 32 bits; hence, I chose m=2^32.

2. If m is a power of 2 (and mine is), pick "a" so that "a mod 8 = 5". This, together with the rules on choosing c below, ensure that all m values will produced before the series repeats.

3. Pick "a" between m/100 and m-sqrt(m). The binary digits should NOT have a simple, regular pattern. Knuth recommends taking some haphazard constant, such as a=3131492621.

4. "c" should be odd, and preferably near "m*.2113248654".

Now for the program listings.

The first listing is for my rendition of Call APPLE's algorithm. Lines 1220-1280 link in the USR vector. Lines 1370-1450 branch according to the value of the argument of the USR function. If the argument is negative, lines 1550-1620 set up its absolute value as the new seed. If the argument is zero, the old seed is used without change, lines 1420-1450. If positive non-zero, lines 1470-1490 set up the argument as the RANGE.

Lines 1640-1690 calculate the new seed, which will be 8192 times the old seed, modulo 67099547. 8192 is 2^13, so we can multiply be 13 left shifts. After each shift, if the result is bigger than 67099547, we subtract that value and keep the remainder. The final result will be some number smaller than 67099547.

Lines 1700-1770 save the new seed, and then divide it by 67099547 to get a fraction for the USR function result. Lines 1780-1860 check the initial argument to see if you wanted a fraction between 0 and 1, or an integer between 0 and arg-1. If the latter, the fraction is multiplied by the range and reduced to an integer.

The subroutine named MODULO subtracts 67099547 from the seed value if it would leave a positive remainder, and then renormalizes the result into floating point.

Line 2270 defines the initial seed after loading the program to be 1.0. If you want some other seed, change this line or be sure to seed it with R=USR(-seed) in your Applesoft program.

1000 *-------------------------------- 1010 *SAVE S.USRND S-C 1020 *-------------------------------- 1030 * FROM CALL APPLE, JAN 1983, PAGE 29-34 1040 *-------------------------------- 1050 .OR $300 1060 .TF B.USRND 1070 *-------------------------------- 1080 NORMALIZE.FAC .EQ $E82E 1090 FMUL.FAC.BY.YA .EQ $E97F 1100 LOAD.ARG.FROM.YA .EQ $E9E3 1110 FDIV.ARG.BY.YA .EQ $EA5C 1120 LOAD.FAC.FROM.YA .EQ $EAF9 1130 STORE.FAC.AT.YX.ROUNDED .EQ $EB2B 1140 COPY.FAC.TO.ARG .EQ $EB66 1150 AS.INT .EQ $EC23 1160 *-------------------------------- 1170 USER.VECTOR .EQ $0A THRU $0C 1180 FAC .EQ $9D THRU $A2 1190 FAC.SIGN .EQ $A2 1200 CNTR .EQ $A5 1210 *-------------------------------- 1220 LINK LDA #$4C "JMP" OPCODE 1230 STA USER.VECTOR 1240 LDA #RANDOM 1250 STA USER.VECTOR+1 1260 LDA /RANDOM 1270 STA USER.VECTOR+2 1280 RTS 1290 *-------------------------------- 1300 * R = USR (X) 1310 * IF X < 0 THEN RESEED WITH ABS(X) 1320 * IF X = 0 THEN R = REPEAT OF PREVIOUS VALUE 1330 * IF 0 < X < 2 THEN GENERATE NEXT SEED AND RETURN 1340 * 0 <= R < 1 1350 * IF X >= 2 THEN R = INT(RND*X) 1360 *-------------------------------- 1370 RANDOM 1380 LDA FAC.SIGN CHECK FOR RESEEDING 1390 BMI .2 ...YES 1400 LDA FAC CHECK FOR X=0 1410 BNE .1 ...NO, X=RANGE 1420 LDA #SEED 1430 LDY /SEED 1440 JSR LOAD.ARG.FROM.YA 1450 JMP .5 1460 *---X --> RANGE------------------ 1470 .1 LDX #RANGE 1480 LDY /RANGE 1490 JSR STORE.FAC.AT.YX.ROUNDED $EB2B 1500 *---SEED --> FAC----------------- 1510 LDA #SEED 1520 LDY /SEED 1530 JSR LOAD.FAC.FROM.YA $EAF9 1540 *---PREPARE SEED----------------- 1550 .2 LDA #0 MAKE SEED POSITIVE 1560 STA FAC.SIGN 1570 LDA FAC LIMIT SEED TO 67099547 1580 CMP #$9A 1590 BCC .3 1600 LDA #$9A 1610 STA FAC 1620 JSR MODULO 1630 *---(8192*SEED) MOD 67099547----- 1640 .3 LDA #13 1650 STA CNTR 1660 .4 INC FAC 1670 JSR MODULO 1680 DEC CNTR 1690 BNE .4 1700 *---SEED/67099547---------------- 1710 LDX #SEED 1720 LDY /SEED 1730 JSR STORE.FAC.AT.YX.ROUNDED 1740 JSR COPY.FAC.TO.ARG $EB66 1750 .5 LDA #FLT67 1760 LDY /FLT67 1770 JSR FDIV.ARG.BY.YA $EA5C 1780 *---SCALE TEST------------------- 1790 LDA RANGE 1800 CMP #$82 IS RANGE BETWEEN ZERO AND ONE? 1810 BCC .6 ...YES 1820 *---SCALE------------------------ 1830 LDA #RANGE 1840 LDY /RANGE 1850 JSR FMUL.FAC.BY.YA $E97F 1860 JSR AS.INT $EC23 1870 *---RETURN----------------------- 1880 .6 RTS 1890 *-------------------------------- 1900 MODULO 1910 LDY #0 1920 LDA FAC 1930 CMP #$9A 1940 BCC .3 < 67099547 1950 BEQ .1 67099547... 1960 LDY #4 1970 .1 SEC 1980 LDA FAC+4 LSB 1990 SBC MAN67+3,Y 2000 PHA 2010 LDA FAC+3 2020 SBC MAN67+2,Y 2030 PHA 2040 LDA FAC+2 2050 SBC MAN67+1,Y 2060 PHA 2070 LDA FAC+1 2080 SBC MAN67+0,Y 2090 PHA 2100 BCC .2 <67099547 2110 PLA 2120 STA FAC+1 2130 PLA 2140 STA FAC+2 2150 PLA 2160 STA FAC+3 2170 PLA 2180 STA FAC+4 2190 JMP NORMALIZE.FAC $E82E 2200 .2 PLA 2210 PLA 2220 PLA 2230 PLA 2240 .3 RTS 2250 *-------------------------------- 2260 RANGE .HS 81.00000000 2270 SEED .HS 81.00000000 2280 FLT67 .HS 9A.7FF6E6C0 = 67,099,547 2290 MAN67 .HS FFF6E6C0 2300 .HS 7FFB7360 2310 *-------------------------------- |

The second listing is for my 32-bit algorithm based on Knuth's rules. Again, lines 1210-1270 set up the USR linkage. Lines 1360-1400 decide what kind of argument has been used. If negative, lines 1470-1590 prepare a new seed value. If zero, the previous value is re-used. If positive, the argument is the range.

In this version the seed is maintained as a 32-bit integer. Lines 1470-1590 convert from the floating point form of the argument in FAC to the integer form in SEED. If the argument happens to be bigger than 2^32, I simply force the exponent to 2^32.

Lines 1600-1690 form the next seed by multiplying by 314159269 and adding 907633386. The calculation is done in a somewhat tricky way. Essentially it involves loading 907633386 into the product register, and then adding the partial products of 314159269*seed to that register. The tricks allow me to do all that with a minimum of program and variable space, and I hope with plenty of speed. I understood it all this morning, but it is starting to get hazy now. If you really need a detailed explanation, call me some day. The modulo 2^32 part is automatic, because bits beyond 32 are thrown away.

Lines 1700-1780 load the seed value into FAC and convert it to a floating point fraction.

Lines 1790-1870 check the range requested. If less than 2, the fraction is returned as the USR result. If 2 or more, the fraction is multiplied by the range and integerized.

1000 *-------------------------------- 1010 *SAVE S.RANDOM KNUTH 1020 *-------------------------------- 1030 * FROM KNUTH'S "THE ART OF COMPUTER PROGRAMMING" 1040 * VOLUME 2, PAGES 155-157. 1050 *-------------------------------- 1060 .OR $300 1070 .TF B.RANDOM KNUTH 1080 *-------------------------------- 1090 NORMALIZE.FAC .EQ $E82E 1100 FMUL.FAC.BY.YA .EQ $E97F 1110 STORE.FAC.AT.YX.ROUNDED .EQ $EB2B 1120 AS.QINT .EQ $EBF2 1130 AS.INT .EQ $EC23 1140 *-------------------------------- 1150 USER.VECTOR .EQ $0A THRU $0C 1160 FAC .EQ $9D THRU $A2 1170 FAC.SIGN .EQ $A2 1180 FAC.EXTENSION .EQ $AC 1190 AS.SEED .EQ $CA THRU $CD 1200 *-------------------------------- 1210 LINK LDA #$4C "JMP" OPCODE 1220 STA USER.VECTOR 1230 LDA #RANDOM 1240 STA USER.VECTOR+1 1250 LDA /RANDOM 1260 STA USER.VECTOR+2 1270 RTS 1280 *-------------------------------- 1290 * R = USR (X) 1300 * IF X < 0 THEN RESEED WITH ABS(X) 1310 * IF X = 0 THEN R = REPEAT OF PREVIOUS VALUE 1320 * IF 0 < X < 2 THEN GENERATE NEXT SEED AND RETURN 1330 * 0 <= R < 1 1340 * IF X >= 2 THEN R = INT(RND*X) 1350 *-------------------------------- 1360 RANDOM 1370 LDA FAC.SIGN CHECK FOR RESEEDING 1380 BMI .1 ...YES 1390 LDA FAC CHECK FOR X=0 1400 BEQ .6 ...YES, REUSE LAST NUMBER 1410 *---X --> RANGE------------------ 1420 LDX #RANGE 1430 LDY /RANGE 1440 JSR STORE.FAC.AT.YX.ROUNDED $EB2B 1450 JMP .4 1460 *---PREPARE SEED----------------- 1470 .1 LDA #0 MAKE SEED POSITIVE 1480 STA FAC.SIGN 1490 LDA FAC LIMIT SEED TO 2^32-1 1500 CMP #$A0 1510 BCC .2 1520 LDA #$A0 1530 STA FAC 1540 .2 JSR AS.QINT $EBF2 1550 LDX #3 COPY FAC INTO SEED 1560 .3 LDA FAC+1,X 1570 STA SEED,X 1580 DEX 1590 BPL .3 1600 *---SEED*314159269+907633386----- 1610 .4 LDX #0 1620 .5 LDA SEED,X 1630 STA MULTIPLIER 1640 LDA C,X 1650 STA SEED,X 1660 JSR MULTIPLY 1670 INX 1680 CPX #4 1690 BCC .5 1700 *---LOAD SEED INTO FAC----------- 1710 .6 LDX #5 1720 .7 LDA FLT.SEED,X 1730 STA FAC,X 1740 DEX 1750 BPL .7 1760 LDA #0 1770 STA FAC.EXTENSION 1780 JSR NORMALIZE.FAC 1790 *---SCALE TEST------------------- 1800 LDA RANGE 1810 CMP #$82 IS RANGE BETWEEN ZERO AND ONE? 1820 BCC .8 ...YES 1830 *---SCALE------------------------ 1840 LDA #RANGE 1850 LDY /RANGE 1860 JSR FMUL.FAC.BY.YA $E97F 1870 JSR AS.INT $EC23 1880 *---RETURN----------------------- 1890 .8 RTS 1900 *-------------------------------- 1910 MULTIPLY 1920 STX BYTE.CNT 1930 LDY #3 1940 .1 LDA A,Y 1950 STA MULTIPLICAND,X 1960 DEY 1970 DEX 1980 BPL .1 1990 LDY #8 2000 BNE .2 ...ALWAYS 2010 *-------------------------------- 2020 .5 CLC DOUBLE THE MULTIPLICAND 2030 .6 ROL MULTIPLICAND,X 2040 DEX 2050 BPL .6 2060 .2 LSR MULTIPLIER 2070 BCC .4 2080 LDX BYTE.CNT 2090 CLC 2100 .3 LDA MULTIPLICAND,X 2110 ADC SEED,X 2120 STA SEED,X 2130 DEX 2140 BPL .3 2150 .4 LDX BYTE.CNT 2160 DEY 2170 BNE .5 2180 RTS 2190 *-------------------------------- 2200 RANGE .HS 81.00000000 2210 FLT.SEED .HS 80 2220 SEED .HS 00.00.00.00 2230 .HS 00 SIGN 2240 A .HS 12.B9.B0.A5 314159269 2250 C .HS 36.19.62.EB 907633386 2260 MULTIPLIER .BS 1 2270 MULTIPLICAND .BS 4 2280 BYTE.CNT .BS 1 2290 *-------------------------------- |

The third listing is cut down from the second one, to produce a 16-bit random number. The code is very similar to the program above, so I will not describe it line-by-line. If you want an optimized version of this, the multiply especially could be shortened.

1000 *-------------------------------- 1010 *SAVE S.RANDOM KEYIN 1020 *-------------------------------- 1030 * ALLOWS ACCESS TO THE KEYIN RANDOM VALUE 1040 *-------------------------------- 1050 .OR $300 1060 .TF B.RANDOM KEYIN 1070 *-------------------------------- 1080 NORMALIZE.FAC .EQ $E82E 1090 FMUL.FAC.BY.YA .EQ $E97F 1100 STORE.FAC.AT.YX.ROUNDED .EQ $EB2B 1110 AS.QINT .EQ $EBF2 1120 AS.INT .EQ $EC23 1130 *-------------------------------- 1140 USER.VECTOR .EQ $0A THRU $0C 1150 FAC .EQ $9D THRU $A2 1160 FAC.SIGN .EQ $A2 1170 FAC.EXTENSION .EQ $AC 1180 KEY.SEED .EQ $4E,4F 1190 *-------------------------------- 1200 LINK LDA #$4C "JMP" OPCODE 1210 STA USER.VECTOR 1220 LDA #RANDOM 1230 STA USER.VECTOR+1 1240 LDA /RANDOM 1250 STA USER.VECTOR+2 1260 RTS 1270 *-------------------------------- 1280 * R = USR (X) 1290 * IF X < 0 THEN RESEED WITH ABS(X) 1300 * IF X = 0 THEN R = REPEAT OF PREVIOUS VALUE 1310 * IF 0 < X < 2 THEN GENERATE NEXT SEED AND RETURN 1320 * 0 <= R < 1 1330 * IF X >= 2 THEN R = INT(RND*X) 1340 *-------------------------------- 1350 RANDOM 1360 LDA FAC.SIGN CHECK FOR RESEEDING 1370 BMI .1 ...YES 1380 LDA FAC CHECK FOR X=0 1390 BEQ .6 ...YES, REUSE LAST NUMBER 1400 *---X --> RANGE------------------ 1410 LDX #RANGE 1420 LDY /RANGE 1430 JSR STORE.FAC.AT.YX.ROUNDED $EB2B 1440 JMP .4 1450 *---PREPARE SEED----------------- 1460 .1 LDA #0 MAKE SEED POSITIVE 1470 STA FAC.SIGN 1480 LDA FAC LIMIT SEED TO 2^16-1 1490 CMP #$90 1500 BCC .2 1510 LDA #$90 1520 STA FAC 1530 .2 JSR AS.QINT $EBF2 1540 LDA FAC+3 1550 STA KEY.SEED 1560 LDA FAC+4 1570 STA KEY.SEED+1 1580 *---SEED*19125+13843------------- 1590 .4 LDX #0 1600 .5 LDA KEY.SEED,X 1610 STA MULTIPLIER 1620 LDA C,X 1630 STA KEY.SEED,X 1640 JSR MULTIPLY 1650 INX 1660 CPX #2 1670 BCC .5 1680 *---LOAD SEED INTO FAC----------- 1690 .6 LDA #0 1700 STA FAC+3 1710 STA FAC+4 1720 STA FAC.SIGN 1730 STA FAC.EXTENSION 1740 LDA #$80 1750 STA FAC 1760 LDA KEY.SEED 1770 STA FAC+1 1780 LDA KEY.SEED+1 1790 STA FAC+2 1800 JSR NORMALIZE.FAC 1810 *---SCALE TEST------------------- 1820 LDA RANGE 1830 CMP #$82 IS RANGE BETWEEN ZERO AND ONE? 1840 BCC .8 ...YES 1850 *---SCALE------------------------ 1860 LDA #RANGE 1870 LDY /RANGE 1880 JSR FMUL.FAC.BY.YA $E97F 1890 JSR AS.INT $EC23 1900 *---RETURN----------------------- 1910 .8 RTS 1920 *-------------------------------- 1930 MULTIPLY 1940 STX BYTE.CNT 1950 LDY #1 1960 .1 LDA A,Y 1970 STA MULTIPLICAND,X 1980 DEY 1990 DEX 2000 BPL .1 2010 LDY #8 2020 BNE .2 ...ALWAYS 2030 *-------------------------------- 2040 .5 CLC DOUBLE THE MULTIPLICAND 2050 .6 ROL MULTIPLICAND,X 2060 DEX 2070 BPL .6 2080 .2 LSR MULTIPLIER 2090 BCC .4 2100 LDX BYTE.CNT 2110 CLC 2120 .3 LDA MULTIPLICAND,X 2130 ADC KEY.SEED,X 2140 STA KEY.SEED,X 2150 DEX 2160 BPL .3 2170 .4 LDX BYTE.CNT 2180 DEY 2190 BNE .5 2200 RTS 2210 *-------------------------------- 2220 RANGE .HS 81.00000000 2230 A .DA /19125,#19125 2240 C .DA /13843,#13843 2250 MULTIPLIER .BS 1 2260 MULTIPLICAND .BS 2 2270 BYTE.CNT .BS 1 2280 *-------------------------------- |

What do you do if you want even more randomness than you can get from one generator? You can use two together. The best way (for greatest randomness) is to use one to select values from a table produced by the other. First generate, say 50 or 100, random values with one generator. The generate a random value with the second generator and use it to pick one of the 50 or 100 values. That picked value is the first number to use. Then replace the picked value with a new value from the first generator. Pick another value randomly using the second generator, and so on. This is analogous to two people working together. The first person picks a bowlful at random from the universe. The second person picks items one at a time from the bowl. The first person keeps randomly picking from the universe to replace the items removed from the bowl by the second person.

You could use the 16-bit generator to pick values from a "bowl" kept full by my 32-bit generator.

Now back to those tests mentioned at the beginning. I am happy to report that all three of the algorithms listed above completely fill the hi-res screen, no holes left, eventually.

By the way, the August 1981 AAL contained an article about the Integer BASIC RND function, and how to use it from assembly language.

The Apple //c |
Bob Sander-Cederlof |

In August 1977 I walked into CompuShop with checkbook in hand, hoping to fill a void in my life by (finally) buying my own personal computer. I didn't know one brand from another, but there was a 4K Apple II running a color demo in lo-res graphics that caught my eye. I bought it. My toy, because I certainly could think of no possible way to consider it more than a toy. The serial number is 219, and I am using it to write this article. By the way, the other brands that were at CompuShop in 1977 are now all out of business.

The price for 4K was $1298; I got 4K extra RAM and paid $1348 plus sales tax. No software. No CRT. No floating point BASIC. No slick manuals. About 45 pages of mimeographed notes was the total documentation package. I had to build a modulator kit that afternoon so I could hook it up to my TV set. The only other connection which seemed of any use was the cassette tape, which several hundred of you may remember. The store gave me a cassette containing the color demo and Woz's Breakout game. That was all there was! Eight empty slots, and absolutely nothing on the market to plug into them. Not even enough memory for hi-res graphics, which I did not even know existed. Absolutely no software for sale from any vendor.

I have spent a lot of time on this Apple. And money. And it is not JUST a toy any more! It has Applesoft on the motherboard, with 48K RAM. Slot 0 has an STB 128K RAM card (the best, in my opinion). All the other slots are full, but with what depends on the work for the day.

Now there is the Apple //c. $1295 buys you 128K RAM, Applesoft BASIC, a disk drive, and ProDOS! Probably over 10,000 programs on the market which will run in it, and many more to come. Built-in interfaces including two serial ports, mouse, disk controller, 80-columns, many video options, and more. The most often purchased interfaces are all there, enough to fill five slots in an older Apple. They added a headphone jack and volume control, too; it is recessed under the left edge. Using it will let you work later at night without disturbing light sleepers. You still get a "game" port, but it is a 9-pin D-socket and doubles as the mouse port. Sorry, no more Cassette port. A second disk drive can be added, and it costs significantly less than a second //e drive.

There are two new switches beside the RESET switch, labeled 40/80 and Keyboard. The first switches between 40 and 80 columns. The second selects QWERTY or Dvorak keyboard arrangement. Think a while of the implications to future generations of including THAT switch. The 40/80 switch is really just connected to what used to be cassette input $C060. You can read the switch position like the firmware does, by looking at the sign bit of that byte.

Until now all Apple game ports had four analog inputs, four switch outputs, and three switch inputs. The //c has only two analog inputs, and no switch outputs. The three switch inputs remain, with switch two dedicated to the mouse button. The other two analog input addresses are used as single bits to read the mouse X and Y direction. The four output bits are now used to control various interrrupt modes.

An interesting new softswitch input is at $C077. If bit 7 of the byte is 1, the current line being stroked on the screen is graphics; if 0, it is text. People like Bob Bishop, Don Lancaster, and Bill Budge probably already have figured out fantastic new tricks using this bit.

The power supply is now in a little box that is part of the power cord. 115 volts AC in, 12 volts DC out. The rest of the supply voltages derived inside the case. There will be a battery pack option later. And how about an adapter for running in the car?

The video output capability is phenomenal. Now you get all the American and European options built in. One connector gives you the NTSC we are all used to. Another gives you RF-modulated form for an American TV set. You also get RGB and various European standards. The 15-pin video connector also gives you an audio signal and various timing signals.

The ROM in the //c is VERY different. The differences include serial port and mouse firmware, better interrupt handling, the improvements made in the new //e ROMs, no more self-test program, and extensions to the disassembler (monitor L-command) for the 65C02 chip.

It is getting to be quite a chore for software to distinguish which kind of Apple II it is in. Here is a chart showing Apple's official ID bytes:

$FBB3 $FB1E $FBC0 Environment --------------------------------------------------- $38 Old (Original) Apple ][ $EA $AD Apple ][ Plus Autostart $EA $8A Apple /// Emulation $06 $EA Apple //e $06 $E0 New Apple //e ROM $06 $00 Apple //c

Interrupts are used extensively by the mouse firmware. A keyboard interrupt plus firmware implements a 128-character type-ahead buffer.

All this talk about mouse support leads me to make one clarification. You don't get a mouse unless you pay an extra $100. The firmware and interface are built-in, but the actual device is optional.

By the way, besides the 16 memory chips there are only 21 other chips. More special chips, including IWM (Integrated Woz Machine, the disk controller); GLU (General Logical Unit); and TMG (Timing Generator). Compare the total 37 chips with about 50 in the Macintosh, and more than 90 in the IBM PCjr. Most of the chips are soldered in, but a few still sit in sockets.

News from Roger Wagner |
Bob Sander-Cederlof |

Roger Wagner: well known to most of us as owner of Southwestern Data Systems, author of "Assembly Lines: The Book", author of several popular programs in early Apple days, speaker at AppleFests, and so forth. Roger is branching out.

Last month he incorporated and changed the name of SDS to Roger Wagner Publishing. Along with the name change, the product packaging has been changed. After a poll of dealers, they decided to replace the plush padded binders with a new package design which allows customers to browse through the manuals, while the diskette and other package contents are securely kept intact. No more shrink wrap! Simpler packaging is less expensive, so the prices of some products have been lowered. And one step further, even more significant: no more copy protection!

We applaud Roger for taking this step. As I remember it, Roger was one of the first publishers to use any kind of software protection, back in the 70's. His scheme included a program on the master disk which allowed you to make a limited number of back up copies. Now Roger joins us and a handful of other publishers who refuse to shackle users with protected software.

Roger has also joined forces with Val Golding (founder and long-time editor of Call-A.P.P.L.E.) to form Emerald City Publishing, Inc. Their first project is "The Apple's Apprentice", a magazine aimed at Apple-teens.

Apple //e ROM Revision |
Bob Sander-Cederlof |

Dated March 21, 1984, I received a pair of 2764 ROMs and 12-page writeup. These are preliminary versions of a new set predicted to be in general distribution by early next year.

The new //e ROMs are substantially better than the current ones. Changes include:

Applesoft: modified to work in 80-column mode, and with lower case.

Monitor:

- modified to work with new Mouse ICON characters;
- modified to accept lowercase input;
- location $1F no longer used;
- miniassembler is back;
- search command added;
- IRQ handling substantially modified.

Video Firmware (after PR#3):

- fixed many bugs;
- no more jagged scrolling, now smooth and 30% faster;
- two new escape commands to enable/disable printing of control characters;
- SETVID ($FE93) now turns off 80-column mode;
- escape-R removed.

The new IRQ handler should finally make interrupts actually usable on the Apple. The old problem with location $45 is fixed. The settings of the various soft-switches which control memory mapping are saved and the machine is put into a cononical state. The standard IRQ return sequence will restore the interrupted state of all those switches.

The total overhead from IRQ-event to your IRQ-subroutine will run from 250 to 300 microseconds, depending on the soft-switch settings. If you are in a ProDOS environment, you will have to add all the overhead caused by ProDOS.

Of course, there will be new problems. ProDOS bent over backwards in a very strange way to solve the $45 problem with interrupts. Now that it is not necessary, ProDOS should be changed. But it can't be changed for the new and still work in the old, so.... The new IRQ and BRK handler also clobbers locations $100 and $101, which is BAD! Both those locations are used by Applesoft and many other programs!

If you think these changes will impact your work, or want to be involved in shaking out bugs, you might contact Developer Relations at Apple (408) 996-1010 and discuss the Certified Apple Developer program. I think it is because I am one of those that I received this material.

65C02 vs the older Apples |
Bob Sander-Cederlof |

A few months ago we reported that apparently 2-MHz versions of the 65C02 chip worked in Apple IIs and II Plusses. (Even 1-MHz versions work in //e's.) Bob Stout was our source: he tried it, it worked, and he told us so.

Based on Bob's good luck, Stephen Bach tried it, it did not work, and he told us so. Steve and Bob got together, and it seems that the 2-MHz parts work in some IIs and II Plusses, but not all. "Try it and see" seems to be the only definitive answer.

By the way, you can get the 65C02 from Hamilton/Avnet and several other distributors for under $15 each. The 1MHz version is under $10 from Western Design Center. There is no incentive for dealers to get into the distribution of chips like this, because quantity price breaks depend on volumes in the thousands.

If you are having trouble finding a distributor, call Rockwell International's sales office; they might sell to you directly, point you to a distributor, or even give you a free sample. If not Rockwell, then try GTE or NCR, who also manufacture the 65C02, albeit without the extra 32 instructions Rockwell inserted. Here are some phone numbers for Rockwell:

California: (714) 833-4655 Texas: (214) 996-6500 Illinois: (312) 297-8862 New Jersey: (609) 596-0090 Tokyo: (03) 265-8806 West Germany:(089) 857-6016 England: (01) 759-9911

You might possibly find these chips at Apple dealers or repair centers in the near future, because it is being used in the Apple //c. Apple is apparently not using the Rockwell version, because the BYTE article about the //c says the chip has 27 new opcodes. This is the total count of new opcodes including the new addressing modes added by the 65C02 offered by NCR, GTE, Western Design, and others. The Rockwell version adds an additonal 32. Those 32 are NOT in the 65802 or 65816, so chasing after them will lead you into dead-end streets.

If you are able to wait, the 65802 and 65816 far surpass the 65C02. You can order samples from Western Design Center, (602)962-4545, at $95 each. Originally expected in January, they are now targeting June 15th.

Decimal Floating Point Arithmetic |
Bob Sander-Cederlof |

Perhaps you have wondered why PRINT INT(14.9 * 10) in Applesoft prints 148. This and many other such seeming bugs are a very common idiosyncrasy in the computer world.

Applesoft use binary floating point format for storing numbers and doing arithmetic. The number 14.9 is very clean in decimal, but it is an awful mess in binary. If you look at what is stored in RAM after doing X=14.9, you will find 84 6E 66 66 66. The first byte, 84, means the remaining four should be understood as four bits of binary integer (the "14" of "14.9") and 28 bits of binary fraction (the ".9" part). The first bit of the second byte is zero, which means the number is positive. Applesoft stores the sign in this bit position, knowing that ALL values other than 0.0 will have a 1-bit in this position of the magnitude.

Just before doing any arithmetic on the value above, Applesoft will unpack it, separating the sign, binary exponent, and the rest. The fancy name for the rest is the "mantissa". Writing out the mantissa for 14.9 we see EE 66 66 66. The first "E" means 14, and the .E666666 is APPROXIMATELY equal to .9. It is actual less than .9 by .000000066666666...forever. Since the number is not quite 14.9, multiplying by 10 gives not quite 149. And taking the INT of not-quite-149 gives the CORRECT answer of 148.

CORRECT, but not what you WANTED or EXPECTED. Right, Ethan? That is why you will find business software written in Applesoft is full of little fudge factors. We always need to multiply by enough 10's to make all pennies into integers, and then round up, and then truncate.

An alternative is to use DECIMAL arithmetic. And guess what: the 6502 has built-in decimal arithmetic. The only trouble is that Applesoft does not know about it.

I wrote an Applesoft extension package called DPFP which gives Applesoft 21-digit precision, rather than the normal 9. But it is still binary, so you still get those round-off and truncation problems with clearcut decimal fractions. About two and a half years ago I wrote another Applesoft extension package called DP18. This one is DECIMAL, and gives 18-digit precision. Bobby Deen helped me flesh it out with full support for arithmetic expressions and all the math functions.

Well, it has been hiding on my shelf long enough! I am going to start publishing it in AAL, a piece at a time. In this issue you will find the routines for addition and subtraction.

First a word about the way DP18 stores numbers. Since Applesoft uses five bytes for each floating point value, and since it is relatively easy to connect to Applesoft using multiples of five bytes, I use ten bytes for each DP18 value. The first byte holds the sign and exponent for the value. The remaining nine bytes hold 18 decimal digits, in BCD format. That is, each digit takes four bits.

The first bit of the first byte is the sign bit. Zero means plus, one means minus. If the whole first byte is zero, the whole number is zero. The remaining seven bits of the first byte are the decimal exponent, excess $40. The value $40 means ten to the zero power. $41 means 10, $42 means 100, and so on. $3F means .1, $3E means .01, and so on. Thus the exponent range is from $01 through $7F, meaning from 10^-63 through 10^63.

smallest: .1 * 10^-63 largest: .9999...9 * 10^63

The mantissa bytes are considered to be a decimal fraction. The number is stored so that the most significant digit is always in the first nybble of the first byte, and the exponent is adjusted accordingly. Let's look at a few examples:

42 14 90 00 00 00 00 00 00 00 = 14.9 41 31 41 59 26 53 58 97 93 23 = pi 38 50 00 00 00 00 00 00 00 00 = .000000005 B8 50 00 00 00 00 00 00 00 00 = -.000000005

Since listing the whole program at once is impossible, I have jumped right down to the lowest level so you can see how the elementary functions of addition and subtraction work. I put the origin at $0800 for this listing, but of course the final package will run wherever you assemble it for. Later we will get into I/O conversions, multiply and divide, math functions, print using, conversions between Applesoft and DP18 values, handling expressions with precedence and parentheses, and the linkage between DP18 and Applesoft.

The listing shown below has two main entry points, DSUB and DADD. You can guess what they mean! The two values to be operated on will already be unpacked into DAC and ARG by the time DSUB or DADD is called. Note that there is one extra byte for each accumulator, so that series of calculations will carry around an extra two digits of precision to avoid rounding errors. Unpacking a value into DAC involves storing the exponent byte in DAC.SIGN and then stripping the sign bit from DAC.EXPONENT.

DSUB and DADD both begin with the easiest cases, in which at least one of the values is zero. DSUB complements the value in DAC by merely toggling the sign bit, and then falls into DADD. In other words, ARG-DAC is the same as ARG+(-DAC).

DADD then determines which of the two values has the larger exponent. If necessary, it swaps ARG and DAC: the object is to have the value with the larger exponent in DAC (unless they are the same). Then the value in ARG is shifted right N digits, where N is the difference in the exponents. This what our teachers called "lining up the decimal points".

The subroutine which shifts ARG right N digits is rather smart. First, it will just fill ARG with zeros if the shift is 20 or more. Next, if the shift count is odd, it shifts right one digit position, or four bits. Then it does a direct move to shift the rest of the digits by N/2 bytes, and fills in with zero bytes on the left.

Addition is divided into two cases: either both arguments have the same sign, or they are different. If they are both the same, a simple addition loop is used. If the result carries into the next digit, DAC is shifted right one digit and a "1" is installed in the leftmost digit.

Otherwise, ARG is subtracted from DAC. If both ARG and DAC had the same exponents, it is possible that the value in ARG is larger than the value in DAC. In this case the subtracion loop will end with a "borrow" status, so the result needs to be complemented. I complement by subtracting from zero. Note that the three loops just described are all performed with the 6502 in decimal mode (the SED opcode at line 1490). CLD later reverts back to binary mode. After the mantissas are combined, the result may have one or more zero digits on the left. Therefore we go to a NORMALIZE subroutine.

NORMALIZE shifts the mantissa left until a non-zero digit is in the leftmost digit position. It also decrements the exponent for each digit-shift. I tried to do the shifting involved as intelligently as possible.

1000 .LIF 1010 *SAVE S.DP18 ADD & SUB 1020 *-------------------------------- 1030 * 18-DIGIT DECIMAL FLOATING POINT 1040 * ADDITION AND SUBTRACTION 1044 *-------------------------------- 1046 AS.OVRFLW .EQ $E8D5 1050 *-------------------------------- 1060 DAC .BS 12 1070 DAC.EXPONENT .EQ DAC 1080 DAC.HI .EQ DAC+1 1090 DAC.EXTENSION .EQ DAC+10 1100 DAC.SIGN .EQ DAC+11 1110 *-------------------------------- 1120 ARG .BS 12 1130 ARG.EXPONENT .EQ ARG 1140 ARG.HI .EQ ARG+1 1150 ARG.EXTENSION .EQ ARG+10 1160 ARG.SIGN .EQ ARG+11 1170 *-------------------------------- 1180 SWAP.ARG.DAC 1190 LDY #11 SWAP 12 BYTES 1200 .1 LDA ARG,Y 1210 LDX DAC,Y 1220 STA DAC,Y 1230 TXA 1240 STA ARG,Y 1250 DEY 1260 BPL .1 1270 RTS 1280 *-------------------------------- 1290 * SUBTRACT DAC FROM ARG 1300 * DAC = ARG - DAC 1310 *-------------------------------- 1320 DSUB LDA DAC.EXPONENT 1330 BEQ SWAP.ARG.DAC ARG-0=ARG 1340 LDA DAC.SIGN 1350 EOR #$80 1360 STA DAC.SIGN 1370 *-------------------------------- 1380 * ADD ARG TO DAC 1390 * DAC = ARG + DAC 1400 *-------------------------------- 1410 DADD LDA ARG.EXPONENT 1420 BEQ .3 DAC+0=DAC 1430 .1 SEC COMPARE EXPONENTS 1440 LDA DAC.EXPONENT 1450 BEQ SWAP.ARG.DAC ARG+0=ARG 1460 SBC ARG.EXPONENT 1470 BMI .8 ARG IS LARGER 1480 JSR SHIFT.ARG.RIGHT.N 1490 SED SET DECIMAL MODE 1500 LDA DAC.SIGN COMPARE SIGNS 1510 EOR ARG.SIGN 1520 BMI .4 OPPOSITE SIGNS 1530 *---SAME SIGNS------------------- 1540 CLC SAME SIGNS, JUST ADD VALUES 1550 LDY #9 TEN BYTES 1560 .2 LDA DAC.HI,Y 1570 ADC ARG.HI,Y 1580 STA DAC.HI,Y 1590 DEY 1600 BPL .2 1610 CLD BINARY MODE 1620 BCC .3 NO CARRY 1630 JSR SHIFT.DAC.RIGHT.ONE 1640 LDA DAC.HI 1650 ORA #$10 1660 STA DAC.HI 1670 .3 RTS 1680 *---DIFFERENT SIGNS-------------- 1690 .4 SEC SUBTRACT ARG FROM FAC 1700 LDY #9 TEN BYTES 1710 .5 LDA DAC.HI,Y 1720 SBC ARG.HI,Y 1730 STA DAC.HI,Y 1740 DEY 1750 BPL .5 1760 BCS .7 NO BORROW 1770 SEC BORROW, SO COMPLEMENT 1780 LDY #9 1790 .6 LDA #0 1800 SBC DAC.HI,Y 1810 STA DAC.HI,Y 1820 DEY 1830 BPL .6 1840 LDA ARG.SIGN 1850 STA DAC.SIGN 1860 .7 CLD 1870 JMP NORMALIZE.DAC 1880 *---SWAP ARG & DAC, TRY AGAIN---- 1890 .8 JSR SWAP.ARG.DAC 1900 JMP .1 1910 *-------------------------------- 1920 * SHIFT DAC RIGHT ONE DECIMAL DIGIT 1930 *-------------------------------- 1940 SHIFT.DAC.RIGHT.ONE 1950 INC DAC.EXPONENT 1955 BMI .2 1960 LDY #4 4 BITS RIGHT 1970 .1 LSR DAC.HI 1980 ROR DAC.HI+1 1990 ROR DAC.HI+2 2000 ROR DAC.HI+3 2010 ROR DAC.HI+4 2020 ROR DAC.HI+5 2030 ROR DAC.HI+6 2040 ROR DAC.HI+7 2050 ROR DAC.HI+8 2060 ROR DAC.HI+9 EXTENSION 2070 DEY 2080 BNE .1 2090 RTS 2095 .2 JMP AS.OVRFLW 2100 *-------------------------------- 2110 * SHIFT ARG RIGHT N DIGITS 2120 *-------------------------------- 2130 SHIFT.ARG.RIGHT.N 2140 LDY #9 SET UP FOR 10 BYTES 2150 CMP #20 DON'T BOTHER IF OFF END 2160 BCS .4 JUST ENTER ZERO INTO ARG 2170 LSR TEST SHIFT COUNT ODD OR EVEN 2180 BCC .2 EVEN 2190 JSR SHIFT.ARG.RIGHT.ONE 2200 .2 TAY # BYTES TO SHIFT 2210 BEQ .6 NONE 2220 EOR #$FF -(#BYTES+1) 2230 CLC 2240 ADC #10 9-#BYTES 2250 TAX 2260 LDY #9 2270 .3 LDA ARG.HI,X 2280 STA ARG.HI,Y 2290 DEY 2300 DEX 2310 BPL .3 2320 .4 LDA #0 2330 .5 STA ARG.HI,Y 2340 DEY 2350 BPL .5 2360 .6 RTS 2370 *-------------------------------- 2380 * NORMALIZE VALUE IN DAC 2390 *-------------------------------- 2400 NORMALIZE.DAC 2410 LDY #-1 2420 .1 INY NEXT BYTE 2430 CPY #10 2440 BCS .7 ...NO MORE BYTES 2450 LDA DAC.HI,Y 2460 BEQ .1 ...STILL ZEROES 2500 *-------------------------------- 2510 .2 TYA TEST BYTE COUNT 2520 BEQ .5 FIRST BYTE IS NON-ZERO 2530 LDX #0 POINT X AT FIRST BYTE 2540 .3 LDA DAC.HI,Y 2550 STA DAC.HI,X 2560 INX 2570 INY 2580 CPY #10 2590 BCC .3 2600 *-------------------------------- 2610 LDA #0 FILL REST OF DAC WITH ZEROES 2620 .4 STA DAC.HI,X 2630 DEC DAC.EXPONENT ADJUST EXPONENT 2640 DEC DAC.EXPONENT FOR SHIFT DISTANCE 2650 INX 2660 CPX #10 2670 BCC .4 2680 *-------------------------------- 2690 .5 LDA DAC.HI SEE IF NEED ONE-DIGIT SHIFT 2700 AND #$F0 2710 BNE .6 NO NYBBLE SHIFT NEEDED 2720 DEC DAC.EXPONENT 2730 JSR SHIFT.DAC.LEFT.ONE 2740 .6 LDA DAC.EXPONENT 2741 BPL .8 2742 .7 LDA #0 2743 STA DAC.EXPONENT 2744 STA DAC.SIGN 2745 .8 RTS 2750 *-------------------------------- 2760 SHIFT.DAC.LEFT.ONE 2770 LDY #4 2780 .1 ASL DAC.EXTENSION 2790 ROL DAC.HI+8 2800 ROL DAC.HI+7 2810 ROL DAC.HI+6 2820 ROL DAC.HI+5 2830 ROL DAC.HI+4 2840 ROL DAC.HI+3 2850 ROL DAC.HI+2 2860 ROL DAC.HI+1 2870 ROL DAC.HI 2880 DEY 2890 BNE .1 2900 RTS 2910 *-------------------------------- 2920 * SHIFT ARG RIGHT ONE DECIMAL DIGIT 2930 *-------------------------------- 2940 SHIFT.ARG.RIGHT.ONE 2950 LDY #4 2960 .1 LSR ARG.HI 2970 ROR ARG.HI+1 2980 ROR ARG.HI+2 2990 ROR ARG.HI+3 3000 ROR ARG.HI+4 3010 ROR ARG.HI+5 3020 ROR ARG.HI+6 3030 ROR ARG.HI+7 3040 ROR ARG.HI+8 3050 ROR ARG.HI+9 EXTENSION 3060 DEY 3070 BNE .1 3080 RTS 3090 *-------------------------------- |

What That Code Did |
Bob Sander-Cederlof |

Way back in August 1981 I published a short article by John Broderick titled "What Does This Code Do?" Well, John never did tell us. But in the May 1984 Nibble, page 115, he finally has let the cat out of the bag. I think this article has probably been banging around the Nibble office for some time now, because John hasn't done anything with Apple's in quite a while. He developed a super fast accounting program in Apple II assembly language, then re-wrote the whole thing for the Sage 68000-based system. Last I heard he was in the IBM world.

The code he gave us three years ago was five bytes long:

BRK PLA PLA PLA RTS

As published in Nibble, it is a little longer:

BREAK BRK NOP PLA PLA JSR $FF3F RTS

Boiling it all down, John used this code during debugging sessions. By putting a JSR to the 8-byte program he can effect a clean breakpoint. Clean, in that he can use the monitor "G" command to continue execution after the BRK.

When JSR BREAK is executed, the BRK opcode will send Apple into the monitor and display the five registers. Their contents will have been saved at $45 thru $49. The address of the first PLA will also be saved. Typing the monitor "G" command will continue execution at that PLA. The two PLA's will pop off the return address the G command put on the stack, leaving it as it was before the BRK. The JSR $FF3F will restore the A-register, which the two PLA's clobbered. The the RTS will return right after the JSR BREAK which started this paragraph.

The original five-byte version was both confusing and erroneous. Confusing, because the PLA immediately after the BRK is never executed. BRK seems like a two-byte opcode to the 6502, so the saved address skips over the following byte. Erroneous, because the A-register has been changed by the time the RTS is executed. I think I would amend both of his versions to this:

BREAK BRK NOP PLA PLA LDA $45 RTS

Making a Map of Differences |
Bob Sander-Cederlof |

Many times I have had two versions of the same program, and wondered where the differences might be.

For example, where are the differences between DOS 3.2 and 3.3, or between the various releases of DOS 3.3? And now that Apple has sent out some pre-releases of a new set of CDEF ROMs for the //e, where are the differences between these and the current //e ROMs?

I have always used the monitor V command to find them. By doing it a small piece at a time, I can pinpoint the changes. Then I turn on my printer and use the L command to document the new version wherever there are differences. But the piecemeal use of the V command wastes a lot of time. I wish I had some way of printing a complete map of all the differences....

What if I had a command which would compare two areas of memory, and print a map of differences? I could use a "." to represent matching locations, and a "*" to represent those that do not match. I could print either 32 or 64 per line: 32 on a 40-column screen, 64 on an 80-column screen or printer. Then I could tell at a glance where all changes had occurred!

I looked at the October 1981 issue of AAL to find out how to use the control-Y monitor command to add a new monitor feature. Then I looked in the listing of the monitor ROM (in my old "red" Apple Reference Manual) at the code for the V command and the command which prints a range of memory.

The program on the next page is the result.

Lines 1150-1190 set up the monitor control-Y vector. Booting DOS stores a branch which effectively makes the control-Y command do nothing. Storing the address of a real program there allows you to add your own commands to the monitor. Once installed, typing a control-Y into the monitor will execute the program named DIFFERENCES.

When we get there, if we typed a full length monitor command of the form "address1<address2.address3^Y" (by "^Y" I mean control-Y), all three of the addresses will have been converted to binary and stored in some standard locations. Address1 will be in $42 and $43, address2 in $3C and $3D, and address3 in $3E and $3F. We will interpret the addresses to mean to compare the block of memory beginning at address1 with the block running from address2 through address3.

Line 1220 prints a carriage return, the current address value in $3C and $3D, and a dash. Lines 1230-1280 compare the bytes at corresponding positions in the two blocks of memory, and select either a "." or a "*" accordingly. Line 1290 prints the selected character.

Lines 1300-1310 increment the two base addresses to point to the next byte in both memory blocks. The new address2 is also compared to address3 to see if we are finished yet.

Lines 1320-1350 check to see if we have printed all 32 on the current screen line. If not, back to .1 to print the next one. Otherwise, all the way back to print a new address and dash, starting a new line. If you want 64 bytes per line, change the mask in line 1330 from #$1F to #$3F. You might want to have the program check to see whether 80-columns is turned on or not, and automatically select #$1F or #$3F accordingly. You could also check to see if the output hook at $36, 37 is pointing at a printer, and use the longer lines.

Experiment. You'll learn a lot and have a lot of fun at the same time!

1000 *SAVE S.DIFFERENCES 1010 *-------------------------------- 1020 * DISPLAY MAP OF DIFFERENCES 1030 * IN TWO MEMORY REGIONS 1040 * 1050 * ADR1<ADR2.ADR3^Y 1060 * 1070 *-------------------------------- 1080 A1 .EQ $3C,3D 1090 A4 .EQ $42,43 1100 *-------------------------------- 1110 MON.NXTA4 .EQ $FCB4 1120 MON.PRA1 .EQ $FD92 1130 MON.COUT .EQ $FDED 1140 *-------------------------------- 1150 SETUP LDA #DIFFERENCES 1160 STA $3F9 1170 LDA /DIFFERENCES 1180 STA $3FA 1190 RTS 1200 *-------------------------------- 1210 DIFFERENCES 1220 JSR MON.PRA1 PRINT CR, ADDRESS AND "-" 1230 .1 LDY #0 COMPARE TWO BYTES 1240 LDA (A1),Y 1250 CMP (A4),Y 1260 BEQ .2 SAME, SELECT FIRST CHAR 1270 INY DIFF, SELECT 2ND CHAR 1280 .2 LDA CHARS,Y GET DISPLAY CHAR 1290 JSR MON.COUT PRINT SAME OR DIFF CHAR 1300 JSR MON.NXTA4 NEXT ADDRESS AND TEST 1310 BCS .3 ...FINISHED 1320 LDA A1 CHECK FOR FULL LINE 1330 AND #$1F OF 32 1340 BNE .1 ...FULL YET 1350 BEQ DIFFERENCES ...FULL 1360 .3 RTS 1370 *-------------------------------- 1380 CHARS .AS -/.*/ SAME AND DIFF CHARS 1390 *-------------------------------- |

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.)