Volume 6 -- Issue 4 | January 1986

In This Issue...

- Convert Lo-Res Pictures to Hi-Res
- A Question About BRUN
- Monthly AAL Source Disk Subscriptions
- Text File Transfer Using DOS 3.3 File Manager
- Fast 6502 & 65802 Multiply Routines
- RAMWORKS Compatible Auxiliary MOVE Routine
- Correction to DOS/ProDOS Double INIT
- An Interesting Bit of Trivia

New Low Diskette Price

When we first started offering blank diskettes for sale, back in May of 1981, we were able to sell them for the then below retail price of only $50 for 20 diskettes. There have been quite a few changes in this industry in the last five years, and prices have continued to fall. We have a new supplier and can now offer you quality diskettes for only $20 per package of 20, a reduction of 37% since last month.

New Monthly Disks

Since diskette prices have fallen so far, we are now planning to send out disks containing the source code from AAL on a monthly basis, instead of quarterly as we have in the past. See the note on page 11 for the details.

Discover

Sears Financial Network is launching a new credit card this year, called Discover. They offer lower interest rates and no yearly charge to the consumer, and better rates to the merchants as well, so we are pleased to be able to accept this card now. You can use it just like any other card for your phone and mail orders.

Convert Lo-Res Pictures to Hi-Res | Bob Sander-Cederlof

Most Apple dot-matrix printer interfaces now include the firmware to print hi-res graphics pictures directly from a screen image. However, most do not provide any way to print lo-res graphics pictures. With the program presented here you can convert a lo-res graphics image into a hi-res picture, ready to be printed by your interface firmware.

Even if you don't ever plan to do such a thing, there are some neat coding tricks in the following program, which you might be able to apply to other hi-res programs.

Lines 1070-1200 demonstrate the use of my lo- to hi-res conversion program. Notice that I started with the label "T". I find I am using that label all the time, lately. I think I started using it as a short mnemonic for "TEST". It is convenient, because in the S-C Macro Assembler environment I can test the program I just assembled by typing "MGO" and the label I want to start at. I find my fingers can now type "MGOT" without my brain even realizing it happened.

The first thing my demo does is call PLOT to create a lo-res picture. I didn't have any real lo-res art around, so I simply drew a 4-by-4 pattern showing all 16 lo-res colors. PLOT fills 16 (4x4) pixels with color 0, 16 with color 1, and so on:

    lo-res   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

       0     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
       1     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
       2     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
       3     0  0  0  0  4  4  4  4  8  8  8  8  C  C  C  C
        ----
      15     3  3  3  3  7  7  7  7  B  B  B  B  F  F  F  F

The rest of the lo-res screen I did not change, so it will normally show the lo-res equivalent of whatever text was on the screen. Of course if you were really trying to use my CONVERT program you would draw your real lo-res picture.

Lines 1090-1120 turn on the lo-res graphics display, and line 1130 waits until I press a key on the keyboard. After running this much of the program, and studying the dot patterns on the screen, I realized that it is not possible to exactly reproduce the lo-res colors on the hi-res screen (unless I used //e or //c double hi-res). However, by mixing various patterns of dots within the 28 dots (7x4) each lo-res pixel maps to, I can come close to the same color. I don't really know how close I came, because I do not have a color monitor. However, I can at least tell by inspection that all 16 colors map to different dot patterns that will be distinguishable colors.

The PAUSE.FOR.ANY.KEY subroutine will return EQ status if the key I press is RETURN, and NE status if it is any other key. Line 1140 will terminate my test program if RETURN was typed. If it was not RETURN, line 1150 turns on hi-res graphics and line 1160 calls the convert program. Then line 1170 waits for another keypress. Again, RETURN will terminate the test, and any other key will flop back to let me see the lo-res display again. Line 1190 turns the text display back on.

CONVERT is very straightforward. The outer loop, using the X-register, runs from 23 down to 0. This corresponds to the 24 text lines on the screen, or 48 lo-res rows. If your lo-res picture does not use the bottom 4 lines (8 rows), change line 1300 to "LDX #19".

The inner loop, using the Y-register, runs from 39 down to 0, corresponding to the 40 columns of lo-res pixels. Each of the 960 bytes addressed by X and Y contains two lo-res pixels. The top lo-res row (HLIN 0,39 AT 0) is in the low-order nybble of each of 40 bytes starting at $400. The second row (HLIN 0,39 AT 1) is in the high-order nybble of the same 40 bytes. The third and fourth rows are in the 40 bytes starting at $480, and so on. The starting addresses for each row-pair are exactly the same as those for the 24 lines of the text screen. They also happen to be very closely related to the starting addresses for the corresponding rows on the hi-res screen.

I stored the 24 starting addresses in two tables, LOL and LOH. LOL contains the low-half of each address, and LOH the high. Lines 1320-1360 pick up the base address for the current row-pair and put it in pointer LBAS. Lines 1340 and 1370-1380 set up a similar pointer for the hi-res screen. Note that the only difference is that the lo-res screen starts at $400, and the hi-res screen starts at $2000. This address points at the first byte (first seven dots) of the top line of the eight hi-res lines that are in the same position as the lo-res row-pair.

Each lo-res pixel will be mapped to four lines on the hi-res screen, and will be seven dots (or one byte) wide. Each of the 960 lo-res bytes has two pixels, so each byte uses eight lines on the hi-res screen. The right lo-res nybble will be the top four lines, and the left nybble will be the bottom four. After studying the tables of hi-res addresses, I noticed that each set of eight lines follow a very regular pattern. Given the address for the leftmost byte of the top line of a set of eight lines, I can compute the addresses for the next seven lines by successively adding 4 to the high byte of the address. Thus the base addresses for the first eight lines are $2000, $2400, $2800, $2C00, $3000, $3400, $3800, and $3C00. I can always get the base address for the first of the eight by subtracting $400 and adding $2000 to the corresponding lo-res pointer. Line 1370 does that operation in one step with "EOR #$24".

Lines 1400-1480 pick up the current lo-res byte and feed first the right nybble and then the left nybble to PROCESS.NYBBLE. For indexing purposes I multiply the nybble by 8, so that the lo-res color is in the A-register like this: xCCCCxxx. More on that later. Lines 1490-1530 are the south ends of the two nested loops, equivalent to NEXT Y and NEXT X. By the way, please don't get confused by the terms Y and X. They refer in my program to 6502 registers, not Cartesian coordinates. Just to keep your minds nimble, I use the Y-register for the X-coordinate. The X-register is half the lo-res Y-coordinate.

I mentioned above the problem of coming up with patterns of 28 dots to approximate the lo-res colors. There are only six solid hi-res colors, which correspond exactly to lo-res colors 0, 3, 6, 9, 12, and 15. The other 10 lo-res colors take double the normal hi-res resolution to reproduce exactly. However, as Don Lancaster explains in detail in his "Enhancing Your Apple II -- Volume I", you can produce thousands of shades in hi-res by using dot patterns. I picked 12 of his patterns based on the names he gave them, since I did not have a color monitor. His patterns fit in a 28-dot by two line array. Since each byte stores seven dots, it takes 28 dots before the some of the patterns repeat. Using two lines with different or offset patterns gives even more variety.

The table SHADES in lines 1900-2050 give sixteen patterns. The first four bytes of each color are for the first line of 28 dots, and the other four bytes give the second line of 28 dots. Each lo-res pixel will use only one pair of bytes from the set of eight, depending on which column it is in. The last two bits of the lo-res column number (in the Y-register) select which byte pair to use.

Lines 1650-1700 build an index to the byte pair by merging those two bits with the color*8. Then by addressing "SHADE,X" I get the first byte of a pair, and by using "SHADE+4,X" I get the second one. Each lo-res pixel will use the four hi-res bytes by repeating the pair selected from SHADES.

The rest of the code in PROCESS.NYBBLE involves putting the selected color bytes into the hi-res area. HBAS points at the top line of the four to be stored into, and the Y-register points to the byte on that line; so "STA (HBAS),Y" will store into that byte. COMMON.CODE (so named because of a lack of creativity on my part this morning when I discovered that the same eight lines appeared twice) gets and puts two color bytes. The first byte goes into (HBAS),Y; then I add 4 to the high byte of HBAS (since I KNOW it is zero, ORA can be used to add the bit) and store the second byte at the new (HBAS),Y. The "EOR #$0C" at line 1720 changes $24 to $28 or $34 to $38. Similarly, the "EOR #$1C" at line 1750 changes $2C to $30 or $3C to $20. This last possibility leaves HBAS prepared for the next column, automatically!

Some of the same tricks could be used in writing a program to copy text from the text screen to the hi-res graphics screen, or for a general purpose routine to write characters onto the hi-res screen. Instead of using a color map, we would need a table of dot-matrix characters. Maybe this is just how everyone does it, but I don't recall seeing all of these tricks in any previous code. Especially the idea of getting the hi-res base pointer by merely toggling two bits in the equivalent text pointer, and the idea of generating successive hi-res pointers by merely adding 4 to that base pointer.

When I wrote this program I wasn't really worrying about speed or space. Nevertheless, as you can see, it is fairly compact. As for speed, it takes less than a second.

      1000 *SAVE S.LORES TO HIRES
      1010 *--------------------------------
      1020 LBAS   .EQ $26,27
      1030 HBAS   .EQ $2A,2B
      1040 SAVEX  .EQ $2E
      1050 COLOR  .EQ $30
      1060 *--------------------------------
      1070 T
      1080        JSR PLOT
      1090        LDA $C050    GRAPHICS
      1100        LDA $C052    SOLID (40 BY 48 PIXELS)
      1110        LDA $C054    PRIMARY PAGE
      1120 .1     LDA $C056    LO-RES
      1130        JSR PAUSE.FOR.ANY.KEY
      1140        BEQ .2       ...<RETURN>
      1150        LDA $C057    HIRES
      1160        JSR CONVERT
      1170        JSR PAUSE.FOR.ANY.KEY
      1180        BNE .1       ...NOT <RETURN>
      1190 .2     LDA $C051    TEXT
      1200        RTS
      1210 *--------------------------------
      1220 PAUSE.FOR.ANY.KEY
      1230 .1     LDA $C000    WAIT FOR ANY KEY
      1240        BPL .1       ...NOT YET
      1250        STA $C010    CLEAR STROBE
      1260        CMP #$8D     SET .EQ. IF <RETURN>
      1270        RTS
      1280 *--------------------------------
      1290 CONVERT
      1300        LDX #23      OR #19 IF MIXED MODE
      1310 .1     LDY #39      COLUMNS 0...39
      1320        LDA LOL,X    SET UP BASE POINTER FOR LINE
      1330        STA LBAS
      1340        STA HBAS     SAME FOR HI-RES
      1350        LDA LOH,X
      1360        STA LBAS+1
      1370        EOR #$24     SHIFT FROM $400 TO $2000 FOR HI-RES
      1380        STA HBAS+1
      1390        STX SAVEX    SAVE X-REG
      1400 .2     LDA (LBAS),Y GET TWO LO-RES PIXELS
      1410        PHA          SAVE FOR LOWER ONE
      1420        ASL          UPPER PIXEL * 8
      1430        ASL
      1440        ASL
      1450        JSR PROCESS.NYBBLE
      1460        PLA          GET LOWER PIXEL
      1470        LSR          TIMES 8
      1480        JSR PROCESS.NYBBLE
      1490        DEY          NEXT COLUMN, SCANNING RIGHT TO LEFT
      1500        BPL .2       ...ANOTHER ONE
      1510        LDX SAVEX    RESTORE X-REG
      1520        DEX          NEXT LINE, SCANNING BOTTOM TO TOP
      1530        BPL .1       ...ANOTHER ONE
      1540        RTS          FINISHED!
      1550 *--------------------------------
      1560 LOH    .HS 04.04.05.05.06.06.07.07  HIGH BYTES
      1570        .HS 04.04.05.05.06.06.07.07  OF SCRN PNTRS
      1580        .HS 04.04.05.05.06.06.07.07  (TEXT OR LO-RES)
      1590 *--------------------------------
      1600 LOL    .HS 00.80.00.80.00.80.00.80  LOW BYTES
      1610        .HS 28.A8.28.A8.28.A8.28.A8  OF SCRN PNTRS
      1620        .HS 50.D0.50.D0.50.D0.50.D0
      1630 *--------------------------------
      1640 PROCESS.NYBBLE
      1650        AND #$78     MASK THE SHIFTED NYBBLE
      1660        STA COLOR
      1670        TYA          LO-RES COLUMN
      1680        AND #3       LOW 2 BITS
      1690        ORA COLOR         0CCCC0YY
      1700        TAX
      1710        JSR COMMON.CODE
      1720        EOR #$0C          3RD LINE OF 4
      1730        STA HBAS+1
      1740        JSR COMMON.CODE
      1750        EOR #$1C          NEXT LINE
      1760        STA HBAS+1
      1770        RTS
      1780 *--------------------------------
      1790 COMMON.CODE
      1800        LDA SHADES,X      EVEN LINE
      1810        STA (HBAS),Y
      1820        LDA HBAS+1
      1830        ORA #4
      1840        STA HBAS+1
      1850        LDA SHADES+4,X    ODD LINE
      1860        STA (HBAS),Y
      1870        LDA HBAS+1
      1880        RTS
      1890 *--------------------------------
      1900 SHADES .HS 00.00.00.00.00.00.00.00  0--BLACK
      1910        .HS AA.D5.AA.D5.55.2A.55.2A  1--MAGENTA
      1920        .HS 91.A2.C4.88.C4.88.91.A2  2--DARK BLUE
      1930        .HS 11.22.44.08.44.08.11.22  3--PURPLE
      1940        .HS 2A.55.2A.55.2A.55.2A.55  4--DARK GREEN
      1950        .HS 33.66.4C.19.4C.19.33.66  5--GRAY 1
      1960        .HS D5.AA.D5.AA.D5.AA.D5.AA  6--MEDIUM BLUE
      1970        .HS DD.BB.F7.EE.F7.EE.DD.BB  7--LIGHT BLUE
      1980        .HS A2.C4.88.91.88.91.A2.C4  8--BROWN
      1990        .HS AA.D5.AA.D5.AA.D5.AA.D5  9--ORANGE
      2000        .HS B3.E6.CC.99.CC.99.B3.E6  A--GRAY 2
      2010        .HS D5.AA.D5.AA.AA.D5.AA.D5  B--PINK
      2020        .HS 6E.5D.3B.77.3B.77.6E.5D  C--LIGHT GREEN
      2030        .HS 2A.55.2A.55.AA.D5.AA.D5  D--YELLOW
      2040        .HS 2A.55.2A.55.D5.AA.D5.AA  E--AQUAMARINE
      2050        .HS 7F.7F.7F.7F.7F.7F.7F.7F  F--WHITE
      2060 *--------------------------------
      2070 *   FILL CORNER WITH SAMPLES OF EACH COLOR
      2080 *--------------------------------
      2090 PLOT   LDY #0
      2100        STY COLOR
      2110 .1     LDX #3
      2120 .2     LDA COLOR    00, 44, 88, CC
      2130        STA $400,Y   GR ROWS 0-3
      2140        STA $480,Y
      2150        CLC
      2160        ADC #$11     11, 55, 99, DD
      2170        STA $500,Y   GR ROWS 4-7
      2180        STA $580,Y
      2190        ADC #$11     22, 66, AA, EE
      2200        STA $600,Y   GR ROWS 8-11
      2210        STA $680,Y
      2220        ADC #$11     33, 77, BB, FF
      2230        STA $700,Y   GR ROWS 12-15
      2240        STA $780,Y
      2250        INY
      2260        DEX
      2270        BPL .2
      2280        ADC #$11     .., 44, 88, CC, END
      2290        STA COLOR
      2300        BCC .1       ...MORE
      2310        RTS
      2320 *--------------------------------

A Question About BRUN | Bob Sander-Cederlof

Mike Lawrie, a reader in South Africa, reports that he tried our prime benchmark (Sep 85 AAL) in a Titan Accelerator card equipped with a 65802. It ran 1000 times in 41 seconds, which correlates very nicely with my predictions in the article. The Titan card runs at 3.58 MHz, and I predicted .35 seconds for 10 repetitions at 4 MHz.

Mike also asked an interesting question, which has been asked by a lot of you at one time or another. Why is it that some assembly language programs can be BLOADed and CALLed, but not BRUN? Even the following very simple program will not return from a BRUN, while it will from a BLOAD followed by a CALL:

           JSR $FF3A   Ring the bell
           RTS

The problem is inside the DOS BRUN command. This command does not use a JSR command to jump to the binary code just loaded. Rather, it uses a JMP command. No return address is left on the stack. When the RTS at the end of the program is executed, it pops garbage off the stack and returns wherever that garbage indicates. What will happen is rather unpredictable.

The Applesoft CALL command does use JSR, and so it works. So does the monitor G command, and the S-C Macro Assembler MGO command. In ProDOS, the BRUN processor works correctly, using a JSR.

This leaves the question: How should a BRUNnable program end under DOS 3.3? If it is to return to the command prompt (] for Applesoft) then the last line should be JMP $3D0. If the BRUN command came from a machine language program (unlikely) then the called program should end with a JMP to a known entry point in the calling program. The most likely case is an Applesoft program that uses a machine language routine. The best way to handle this is to use BLOAD and CALL.

Monthly AAL Source Disk Subscriptions Now Available | Bob Sander-Cederlof

We have always made the source code of all the programs published in Apple Assembly Line available on disks. We have collected the programs from three issues together on Quarterly Disks, priced at $15 each or $45/year.

Now that diskettes are so much less expensive, we have decided to try another approach. For those who are interested in getting the source code on disk, we would like to send the source disk along with each newsletter. We will still collect three issues onto Quarterly Disks, for late comers. But those of you who have Quarterly Disk subscriptions will start getting the Monthly Disks. We will send the disk and newsletter in the same envelope, First Class Mail.

The price for combined newsletter/disk subscriptions will be $64 in the USA, Canada, and Mexico. For other countries the postage is higher, so the fee will be $87.

If you want to synchronize your newsletter and Monthly Disk subscriptions, you can pro-rate the Monthly Disk at $3.75 per month ($4.75 for overseas). You can check the length remaining on your current newsletter subscription by looking at the mailing label: the number in the upper-right corner is the year and month of the last issue of your current subscription.

If you currently are receiving the Quarterly Disk by an automatic charge to your credit card each quarter there will be no change: you will still get the Quarterly Disk rather than the monthly disk.

Text File Transfer Using DOS 3.3 File Manager | Bob Potts

Transferring text files from one drive to another can be frustrating and time consuming. The standard procedure is to read from the file on the source drive and write to the file on the target drive. One possible solution is to use FID, but you must BRUN FID and cannot use it from inside an Applesoft program.

With this in mind, I set out to write a utility which will transfer a text file using the DOS 3.3 File Manager (FM) routines. FM has been a part of every release of DOS, but very little documentation has been written about these powerfull routines. While RWTS concerns itself with tracks and sectors, FM deals with whole files, be they binary, text, or Applesoft. I recalled that a couple of years ago, Bob Sander-Cederlof had assisted me with a communications program and had used the FM routines to read and store the file. I located the listing we used, analyzed the code, and here is the result.

The entire program could have been written in assembler, but since most of my programs are in Applesoft (with machine language support routines), I decided to write it as simple as possible. The name of the file to be transferred, the OPEN, READ, WRITE, and CLOSE commands are all obtained through a short Applesoft front end program. The machine language portion is broken down as follows.

Lines 1130-1150 are simply easily accessible jump vectors to the two routines which will be CALLed from inside an Applesoft driver.

Lines 1190-1320 clear the buffer, in this case $2000-95FF, to zeroes. This gives us a buffer of 30,208 bytes, which should be large enough for most text files. (This is 118 sectors.) Lines 1330-1340 reset the base buffer address, for use later to find the end of the data in the buffer.

Lines 1360-1460 load the file that has been OPENed by the Applesoft driver. The process of setting up a FM parameter block is simplified by using a preset data area called RD.BLK, lines 1790-1800. Calling FM.SETUP sets up the Y- and A-registers properly, and then calling FM.ENTRY reads the text file.

Lines 1500-1580 search through the data buffer for the first occurrence of a 00 byte, which will signal the end of data. By subtracting the base buffer address in lines 1660-1710 we get the actual length of the data. Lines 1600-1650 copy in the initial parameter values for writing, and lines 1660-1710 set up the length.

Lines 1720-1740 call on FM to actually write the data on the file that has been opened in the Applesoft driver.

The time saving using this transfer is significant. A text file containing 8000 bytes took 49 seconds to read and write using pure Applesoft. Using the FM the same operation was accomplished in only 17 seconds.

Since the program is only 120 bytes long, it can be placed almost anywhere there is free space, especially on page 3. If you are working from a larger Applesoft program, the starting point for the buffer could be moved as needed to load your text file.

      1000 *SAVE POTTS TEXT COPIER
      1010 *--------------------------------
      1020        .OR $300
      1025        .TF TEXT.TRANSFER.OBJ
      1030 *--------------------------------
      1040 MY.BUFFER  .EQ $2000
      1050 *--------------------------------
      1060 BUFFER     .EQ $E0,E1    POINT TO FILE BUFFER
      1070 RESULT     .EQ $E2       FILE MANAGER RETURN CODE
      1080 *--------------------------------
      1090 FM.SETUP   .EQ $3DC      INITIALIZE Y & A
      1100 FM.ENTRY   .EQ $3D6      FILE MANAGER ENTRY POINT
      1110 FM.BLK     .EQ $B5BB     FILE MANAGER PARM LIST
      1120 *--------------------------------
      1130 *   SET UP JUMP VECTORS
      1140        JMP INITIALIZE.AND.READ
      1150        JMP FIND.END.AND.WRITE
      1160 *--------------------------------
      1170 INITIALIZE.AND.READ
      1180 *--------------------------------
      1190 INITIALIZE.THE.BUFFER
      1200        LDA #MY.BUFFER
      1210        STA BUFFER        LSB
      1220        LDA /MY.BUFFER
      1230        STA BUFFER+1      MSB
      1240        LDY #0
      1250 .1     LDA #0            CLEAR BUFFER UP TO $95FF
      1260 .2     STA (BUFFER),Y
      1270        INY               NEXT BYTE IN THIS PAGE
      1280        BNE .2            ...STILL IN THE PAGE
      1290        INC BUFFER+1      NEXT PAGE
      1300        LDA BUFFER+1
      1310        CMP #$96          AT END OF STORAGE?
      1320        BNE .1            ...NO, KEEP CLEARING
      1330        LDA /MY.BUFFER    RESET BUFFER POINTER
      1340        STA BUFFER+1
      1350 *--------------------------------
      1360 READ.THE.FILE
      1370        LDX #9            10 BYTES
      1380 .1     LDA RD.BLK,X
      1390        STA FM.BLK,X
      1400        DEX
      1410        BPL .1
      1420        JSR FM.SETUP
      1430        JSR FM.ENTRY
      1440        LDA FM.BLK+10     GET RETURN CODE
      1450        STA RESULT        SAVE FOR APPLESOFT PEEK
      1460        RTS               RETURN TO APPLESOFT
      1470 *--------------------------------
      1480 FIND.END.AND.WRITE
      1490 *--------------------------------
      1500 FIND.END.OF.BUFFER
      1510        LDY #0            SEARCH FOR 00 BYTE
      1520 .1     LDA (BUFFER),Y
      1530        BEQ .2            ...FOUND END
      1540        INY
      1550        BNE .1            ...NEXT BYTE IN SAME PAGE
      1560        INC BUFFER+1      NEXT PAGE
      1570        BNE .1            ...ALWAYS
      1580 .2     STY BUFFER        LSB OF EOF BYTE
      1590 *--------------------------------
      1600 WRITE.FILE
      1610        LDX #9            10 BYTES
      1620 .1     LDA WR.BLK,X
      1630        STA FM.BLK,X
      1640        DEX
      1650        BPL .1
      1660        LDA BUFFER        LSB
      1670        STA FM.BLK+6      LSB OF FILE LENGTH
      1680        SEC
      1690        LDA BUFFER+1
      1700        SBC /MY.BUFFER
      1710        STA FM.BLK+7      MSB OF FILE LENGTH
      1720        JSR FM.SETUP
      1730        LDX #1            IF NO FILE, ALLOCATE ONE
      1740        JSR FM.ENTRY      WRITE THE FILE
      1750        LDA FM.BLK+10     RETURN CODE
      1760        STA RESULT        SAVE FOR APPLESOFT PEEK
      1770        RTS               RETURN TO APPLESOFT
      1780 *--------------------------------
      1790 RD.BLK .HS 03.02.0000.0000
      1800        .DA $9600-MY.BUFFER,MY.BUFFER
      1810 WR.BLK .HS 04.02.0000.0000
      1820        .DA $9600-MY.BUFFER,MY.BUFFER
      1830 *--------------------------------

         10  REM  PROGRAM TO TRANSFER A TEXT FILE
         20  REM  FROM DRIVE 1 TO DRIVE 2 USING
         30  REM  DOS 3.3 FILE MANAGER ROUTINES
         40  REM ---BY BOB POTTS, LOUISVILLE, KENTUCKY---
         99  REM -------------------
         100  HIMEM: 8192: REM  $2000-95FF IS MY BUFFER
         110 D$ =  CHR$ (4)
         120  PRINT D$"NOMON I,O,C"
         130  PRINT D$"BLOAD TEXT.TRANSFER.OBJ"
         199  REM -------------------
         200 RF = 768: REM CALL ADDRESS TO READ FILE
         210 WF = 771: REM CALL ADDRESS TO WRITE FILE
         220 RC = 226: REM PEEK ADDRESS FOR FM RETURN CODE
         300  REM ----------------------
         310  TEXT : HOME : PRINT "TEXT FILE TRANSFER"
         320  PRINT "------------------"
         330  INPUT "ENTER FILE NAME:  ";F$
         400  REM READ FILE FROM DRIVE 1
         410  PRINT D$"OPEN"F$",D1
         420  PRINT D$"READ"F$
         430  CALL RF
         440  IF  PEEK (RC) = 5 THEN 500
         450  PRINT D$"CLOSE"
         460  PRINT "RETURN CODE NOT 'END OF DATA'"
         470  STOP
         500  REM WRITE FILE TO DRIVE 2
         510  PRINT D$"CLOSE"
         520  PRINT D$"OPEN"F$",D2
         530  PRINT D$"WRITE"F$
         540  CALL WF
         550  IF  PEEK (RC) = 0 THEN 600
         560  PRINT D$"CLOSE"
         570  PRINT "RETURN CODE WAS " PEEK (RC)
         580  STOP
         600  REM FINISHED
         610  PRINT D$"CLOSE"
         620  PRINT "TRANSFER COMPLETE"
         630  END

Fast 6502 & 65802 Multiply Routines | Bob Sander-Cederlof

Since multiplication is not a built-in function in the 6502, 65C02, or 65802, many of us have written our own subroutines for the purpose. I will present some efficient subroutines here, to handle the 8-bit and 16-bit cases.

I will assume both arguments are the same length (either 8-bits or 16-bits) and that we want the full product. If the arguments are only 8-bits long, the product will by 16-bits long. If the arguments are 16-bits long, the product will be 32-bits long. I will also assume the arguments are unsigned values. Thus $FF times $FF will be $FE01 (in decimal, 255x255 = 65025).

Way back in February, 1981, I published an article with a Brooke Boering's fast 16-bit multiplication subroutine. His subroutine duplicated the functions of the subroutine in the original Apple Monitor ROM, but was nearly twice as fast. Brooke's programs were originally published in the December, 1980, Micro magazine (now defunct). He included an 8-bit multiply subroutine with an average time of only 192 cycles.

Damon Slye wrote an article for Call APPLE, published June, 1983. He introduced some coding tricks which allow an 8-bit multiply in an average of 160 cycles. I have reproduced Damon's program below, in lines 1010-1300. His trick involves eliminating a CLC opcode from the loop in lines 1210-1260. Ordinarily you would need a CLC before the ADC instruction; Damon decremented the multiplicand by one before starting the loop, so that adding with carry set works. He does the decrementing in lines 1130-1160. Note that if the original multiplicand was zero, he skips all the rest of the code and just returns the answer: 0.

I had to go at least one step faster, so I partially "un-wrapped" the 8-step loop. I changed it to loop only four times, but handled two bits of the multiplier each time. This runs an average time of 140 cycles. You could unwrap it all the way, writing out the BCC-ADC-ROR-ROR lines a total of 8 times, and cut the average time down to only 111 cycles.

Let me stop here and say what I mean by average time. I am stating time in terms of "cycles", rather than seconds or microseconds. The Apple two different cycle times, depending on the video timing logic. The average Apple speed is 1020488 cycles per second. The multiply algorithms will vary in speed depending on the number of bits in the multiplier which equal "1". If the multiplier = $FF (all ones) the algorithm will take the maximum time. If the multiplier is $00, it will take the minimum time. On the average for random arguments, the multiplier will have four zeroes and four ones, so the average time is equal to the average of the minimum and maximum times. For all of the subroutines, I included the cycles for a JSR to call them, and for the RTS at the end.

I programmed an 8-bit multiply using 65802 opcodes, as shown below in lines 1560-1790. The program is slightly shorter (one byte), but that really isn't a fair comparison. The arguments and product are handled differently, and so the effort to call the program may be more or less than that for the 6502 version. Rather than passing the multiplicand in the X-register, I have it in the A-register. I pass the multiplier in the high byte of the A-register. Since X is not used for passing any values, I saved and restored it (lines 1620 and 1770). I assumed the program would be called from the 6502 mode, which of course it was as long as I was testing it. In "real life" it might be written to be called from Native 65802 mode, since the larger program it was a part of would also be taking advantage of all the 65802 features.

I used a couple of tricks to save space and time. One you may justly complain about is that I store the multiplicand directly into the operand field of the ADC instruction at line 1720. This definitely saves time, but it also could have serious drawbacks. (For example, it would not work if executed from ROM.) Since I enter in 6502 Emulation mode, line 1640 only loads 0 into the low byte of the A-register. Lines 1650-1660 enter the 65802 Native mode. Line 1680 sets the A-register to 16-bit mode.

In line 1690 I form the inverse (one's complement) of the multiplier. This is just another way of eliminating the CLC from the loop. Note that the multiplier is in the high byte of A, and the product is going to be accumulated in the low byte. The loop runs from line 1700 through line 1740. Line 1700 shifts to the left both the partial product and what remains of the multiplier, putting the highest remaining bit of the multiplier into the carry status bit. If that bit = 1, then the original bit in the multiplier before complementing was a zero, so we do not add the multiplicand to the current partial product. As we continue through the loop, the bits of the multiplier keep shifting out just ahead of the ever-growing partial product, until finally we have the answer.

Lines 1750-1780 restore the machine state to the 6502 Emulation mode and restore the original X-register value. The full product is now in the A-register. If I wanted to print out the product, I might do it like this:

           XBA             GET HIGH BYTE INTO LOW-A
           JSR $FDDA       MONITOR PRINT-BYTE SUBROUTINE
           XBA             GET LOW BYTE INTO LOW-A
           JMP $FDDA

Here is a summary of the execution times (in cycles) for the three 8-bit multiply subroutines:

                  Minimum  Maximum  Average
            Slye    152       168      160
            RBSC    132       148      140
            65802   119       135      127

The 65802 version would be seven cycles faster if we did not require saving and restoring the X-register. If you want to change the 65802 version for calling from Native mode, delete lines 1650, 1660, 1750, and 1760. Then insert the following:

           1612         PHP
           1614         SEP #$30
           ...
           1772         PLP

These changes add one cycle to the time.

      1000 *SAVE S.MULTIPLY 8X8
      1010 *--------------------------------
      1020 CAND   .EQ 2
      1030 PLIER  .EQ 3
      1040 PROD   .EQ 4,5
      1050 *--------------------------------
      1060 *   FAST 6502 MULTIPLICATION, BY DAMON SLYE
      1070 *   CALL APPLE, JUNE 1983, P45-48.
      1080 *      (A-REG) = MULTIPLIER
      1090 *      (X-REG) = MULTIPLICAND
      1100 *      RETURNS PRODUCT IN A,X (X=LO-BYTE)
      1110 *--------------------------------
      1120 FAST.8X8.SLYE
      1130        CPX #0
      1140        BEQ .3       A*0=0
      1150        DEX          DECR. CAND TO AVOID
      1160        STX CAND        THE CLC BEFORE ADC CAND
      1170        LSR          PREPARE FIRST BIT
      1180        STA PLIER
      1190        LDA #0
      1200        LDX #8
      1210 .1     BCC .2       NO ADD
      1220        ADC CAND
      1230 .2     ROR
      1240        ROR PLIER
      1250        DEX
      1260        BNE .1
      1270        LDX PLIER
      1280        RTS
      1290 .3     TXA
      1300        RTS
      1310 *--------------------------------
      1320 FAST.8X8.RBSC
      1330        CPX #0
      1340        BEQ .3       A*0=0
      1350        DEX          DECR. CAND TO AVOID
      1360        STX CAND        THE CLC BEFORE ADC CAND
      1370        LSR          PREPARE FIRST BIT
      1380        STA PLIER
      1390        LDA #0
      1400        LDX #4
      1410 .1     BCC .2       NO ADD
      1420        ADC CAND
      1430 .2     ROR
      1440        ROR PLIER
      1450        BCC .25      NO ADD
      1460        ADC CAND
      1470 .25    ROR
      1480        ROR PLIER
      1490        DEX
      1500        BNE .1
      1510        LDX PLIER
      1520        RTS
      1530 .3     TXA
      1540        RTS
      1550 *--------------------------------
      1560        .OP 65816
      1570 *--------------------------------
      1580 *      MULTIPLIER IN A(15-8), MULTIPLICAND IN A(7-0)
      1590 *      RETURN PRODUCT IN A(15-0)
      1600 *--------------------------------
      1610 MULTIPLY.8X8.65802
      1620        PHX
      1630        STA .2+1     SAVE MULTIPLICAND
      1640        LDA #0
      1650        CLC
      1660        XCE
      1670        LDX #8
      1680        REP #$20     A-REG 16 BITS
      1690        EOR ##$FF00  COMPLEMENT MULTIPLIER
      1700 .1     ASL
      1710        BCS .3       ...IF ORIGINAL BIT=0
      1720 .2     ADC ##0      ADD MULTIPLICAND
      1730 .3     DEX
      1740        BNE .1
      1750        SEC
      1760        XCE
      1770        PLX
      1780        RTS
      1790 *--------------------------------
      1800        .LIF

I will also show three sample 16-bit multiply subroutines....no, four. The first one is a copy of Brooke Boering's code. The second is a direct conversion of Brooke's code to 65802 code, with emphasis on space. The third modifies the second with the tricks of Damon Slye; it takes more space, but it is faster.

The first three of these subroutines are modeled after the code in the original Apple monitor ROM. The arguments are expected in page zero locations, low-byte first. The result will also be in page zero locations. The function performed is actally a little more than just multiplication, because it is possible to specify an addend as well. The final result will be PRODUCT = ADDEND + (MULTIPLIER * MULTIPLICAND). PRODUCT is stored in four consecutive bytes, backwards. The highest byte is at PRODUCT+1, the next at PRODUCT, the next at PLIER+1, and the lowest at PLIER. The fourth subroutine differs in that the product does not overlap the multiplier.

Looking at Brooke's version (lines 1000-1270) you can see that the loop contains a 16-bit addition (lines 1130-1190). There are also two 16-bit ROR shifts, at lines 1200-1230. These are the likely candidates for shortening via 65802 code. My first version for the 65802 made no other changes in the loop. I merely prefixed Brooke's code with CLC-XCE-REP to get into the 16-bit Native mode, and suffixed it with SEC-XCE to get back to Emulation mode. Then I noticed another shortcut, and the result is in lines 1300-1480.

By moving the LDA PRODUCT up before the BCC opcode in lines 1370-1380, I was able to change a ROR PRODUCT to a simple ROR on the A-register followed by a STA PRODUCT. This saves a net six cycles when the multiplier bit is "1", and costs two cycles when the multiplier bit is "0". The average savings for random multipliers is four cycles, inside a loop that runs 16 times.

The faster version, in lines 1500-1780, merely implements Damon Slye's trick of pre-decrementing the multiplicand so as to avoid an explicit CLC opcode inside the 16-time loop. It costs 12 cycles for the extra setup, but it saves two cycles for each one-bit in the multiplier.

The fourth version, in the separate listing as lines 1000-1430, uses the trick of splitting the multiplier in half. In effect, two parallel 8-bit by 16-bit multiplies are accomplished, with the result usually taking less time than any of the other algorithms. By deleting line 1130 (which shaves off another four cycles) the feature of allowing an addend can be included.

Here is a summary of the execution cycles for the four 16-bit multiply subroutines:

                   Minimum  Maximum  Average
           Boering   541      845      693
           Smaller   519      599      559
           Faster    531      579      555
           Fourth    332      684      508 (usually fastest)

Note that the third subroutine also goes even faster when the multiplicand = zero, because the bulk of the code is skipped.

These are pretty good subroutines, but I have no doubt that they can be improved upon. Why not try your hand? If you can significantly improve either space or time or features, send your code to AAL. We'll publish the best ones, and help advance the state of the art. And if you have some classy division subroutines, they are welcome too!

      1000 *SAVE S.MULTIPLY 16X16
      1010 *--------------------------------
      1020 PLICAND    .EQ $00,01    MULTIPLICAND
      1030 PLIER      .EQ $02,03    MULTIPLIER, LO-16 OF PRODUCT
      1040 PRODUCT    .EQ $04,05    HI-16 OF PRODUCT
      1050 *--------------------------------
      1060        .OP 6502
      1070 *--------------------------------
      1080 MULTIPLY.16X16.6502
      1090        LDX #16
      1100 .1     LDA PLIER    CHECK NEXT BIT OF MULTIPLIER
      1110        LSR
      1120        BCC .2       ...DON'T ADD MULTIPLICAND
      1130        CLC
      1140        LDA PRODUCT
      1150        ADC PLICAND
      1160        STA PRODUCT
      1170        LDA PRODUCT+1
      1180        ADC PLICAND+1
      1190        STA PRODUCT+1
      1200 .2     ROR PRODUCT+1
      1210        ROR PRODUCT
      1220        ROR PLIER+1
      1230        ROR PLIER
      1240        DEX
      1250        BNE .1
      1260        RTS
      1270 *--------------------------------
      1280        .OP 65802
      1290 *--------------------------------
      1300 MULTIPLY.16X16.65802.SMALLER
      1310        CLC
      1320        XCE          NATIVE MODE
      1330        REP #$20     A-REG 16-BITS
      1340        LDX #16      LOOP 16 TIMES
      1350 .1     LDA PLIER    CHECK NEXT BIT OF MULTIPLIER
      1360        LSR
      1370        LDA PRODUCT  GET HI-16 OF PRODUCT
      1380        BCC .2       ...DO NOT NEED TO ADD
      1390        CLC
      1400        ADC PLICAND
      1410 .2     ROR
      1420        STA PRODUCT
      1430        ROR PLIER    USE FOR LO-16 OF PRODUCT
      1440        DEX
      1450        BNE .1
      1460        SEC
      1470        XCE          BACK TO EMULATION MODE
      1480        RTS
      1490 *--------------------------------
      1500 MULTIPLY.16X16.65802.FASTER
      1510        CLC
      1520        XCE          NATIVE MODE
      1530        REP #$20     A-REG 16-BITS
      1540        LDA PLICAND
      1550        BEQ .3       0*ANYTHING=0
      1560        DEC
      1570        STA PLICAND
      1580        LDX #16      LOOP 16 TIMES
      1590 .1     LDA PLIER    CHECK NEXT BIT OF MULTIPLIER
      1600        LSR
      1610        LDA PRODUCT  GET HI-16 OF PRODUCT
      1620        BCC .2       ...DO NOT NEED TO ADD
      1630        ADC PLICAND
      1640 .2     ROR
      1650        STA PRODUCT
      1660        ROR PLIER    USE FOR LO-16 OF PRODUCT
      1670        DEX
      1680        BNE .1
      1690        SEC
      1700        XCE          BACK TO EMULATION MODE
      1710        RTS
      1720 .3     LDA PRODUCT  INITIAL ADDEND
      1730        STA PLIER    LOW 16 OF PRODUCT
      1740        STZ PRODUCT  HIGH 16 OF PRODUCT
      1750        SEC
      1760        XCE          BACK TO EMULATION MODE
      1770        RTS
      1780 *--------------------------------
      1790        .LIF

And here is a 65802 version, even faster.

      1000 *SAVE S.MUL16X1665802
      1010 *--------------------------------
      1020        .OP 65802
      1030 *--------------------------------
      1040 A      .EQ 0,1
      1050 B      .EQ 2,3
      1060 P      .EQ 4,5,6,7
      1070 *--------------------------------
      1080 MUL.EVEN.FASTER
      1090        CLC
      1100        XCE          ENTER NATIVE MODE
      1110        REP #$20     16-BIT A-REGISTER
      1120        STZ P+2      MAKE SURE NO ADDEND IN HI-16
      1130        STZ P   (DELETE IF WANT AN ADDEND IN LO-16)
      1140        LDX #8
      1150        BRA .2       ...HOP OVER SHIFTS
      1160 *--------------------------------
      1170 .1     ASL P        DOUBLE THE PRODUCT
      1180        ROL P+2
      1190 .2     LDA A
      1200        AND ##$0080  LOOK AT SIGN OF LO-BYTE
      1210        BEQ .3       ...DON'T ADD MULTIPLICAND
      1220        CLC
      1230        LDA P
      1240        ADC B
      1250        STA P
      1260        BCC .3
      1270        INC P+2      ADD CARRY TO HI-16
      1280 *--------------------------------
      1290 .3     ASL A        SHIFT MULTIPLIER
      1300        BCC .4
      1310        CLC
      1320        LDA P+1      ADD TO MIDDLE OF PRODUCT
      1330        ADC B
      1340        STA P+1
      1350        BCC .4
      1360        INC P+3      (NEVER BOTHERS P+4)
      1370 *--------------------------------
      1380 .4     DEX
      1390        BNE .1
      1400        SEC
      1410        XCE
      1420        RTS
      1430 *--------------------------------
      1440 T
      1450        JSR MUL.EVEN.FASTER
      1460        LDA P+3
      1470        JSR PRB
      1480        LDA P+2
      1490        JSR PRB
      1500        LDA P+1
      1510        JSR PRB
      1520        LDA P+0
      1530 PRB    JMP $FDDA
      1540 *--------------------------------
      1550        .LIF

RAMWORKS Compatible Auxiliary MOVE Routine | Harvey Brown Spirit River, Alberta, CANADA

The MOVE routine inside the Apple //c and //e ROM transfers data conveniently to and from the auxiliary 48K area, but it does not work with the upper 16K area. Also, it does not work with the extra banks of RAM available in cards such as the RAMWORKS from Applied Engineering.

I needed that ability, so I wrote my own MOVE subroutine. Mine uses the page at $200 in main RAM as a buffer, to simplify the movement code. If you want to move from an arbitrary bank to another arbitrary bank, my program will require you to use $200 in main RAM as an intermediate buffer. (Somewhat like stopping at Chicago on your way from Toronto to Dallas.) My program also assumes you are always moving exactly 256 bytes (one page). This simplifies the code and the calling sequence, and is probably a reasonable restriction.

The program begins by copying itself into every bank you are using. The bank numbers must be assembled in to the list in lines 1800-1870. Notice that I use bank number $FF to signal the main RAM, and banks from $00 up to signal the banks of Auxiliary RAM. This code needs to reside in the same location in every bank that will be switched on, because when you move from an auxiliary bank to the main RAM that auxiliary bank will be set up so that all RAM reads come from it. This includes reads for the program, so the program had better be there.

Once the program has been initialized, you can JSR MOVE (or JSR $C03 if you want to use a "frozen" entry point) to move a page. At the time of the JSR MOVE, you should have the high byte of the Auxiliary RAM address in the A-register, and the bank number in the X-register. Set carry (SEC opcode) to indicate moving from main $200, or clear carry (CLC opcode) to indicate moving into main $200. Thinking in terms of a ramdisk application, SEC for a write or CLC for a read.

Warning: my program assumes you are calling from a program that runs with the Applesoft ROM selected (see line 1780). If you plan to run it with RAM selected in the upper 16K, you will have to make appropriate changes. You could save the status of the LCRAM and LCBANK soft switches ($C012 and $C011 respectively) before changing them. These partially indicate the status of the $C08x switches. You can tell whether RAM or ROM was selected, and restore the proper one after MOVE is finished. You can also tell which $D000 bank was selected. However, you cannot tell whether the RAM was write-enabled or not; also, you cannot tell if it was in the special mode in which you read ROM and write RAM.

      1000 *SAVE BROWNS.MOVE
      1010 *--------------------------------
      1020 * MOVE by H. Brown
      1030 * Jan 18/86
      1040 *--------------------------------
      1050 PTR    .EQ $00,01
      1060 BUFFER .EQ $200
      1070 RAMRD  .EQ $C002
      1080 RAMWRT .EQ $C004
      1090 ALTZP  .EQ $C008
      1100 BNKSEL .EQ $C073    RAMWORKS BANK SELECT REGISTER
      1110 ROM    .EQ $C082
      1120 RAM1   .EQ $C08B
      1130 RAM2   .EQ $C083
      1140 *--------------------------------
      1150        .OR $C00     ORG  AT BEGINNING OF A PAGE
      1160 *--------------------------------
      1170 COMMONPG
      1180        JMP INIT     BRUN OR JSR TO INITIALIZE
      1190        JMP MOVE     NORMAL ENTRY
      1200 *--------------------------------
      1210 *   INIT copies COMMONPG to all 64K banks
      1220 *--------------------------------
      1230 INIT   LDX BANKS    START WITH LAST 64K BANK
      1240 .1     LDA BANKS,X  GET  BANK #
      1250        STA BNKSEL   SELECT 64K BANK
      1260        STA RAMWRT+1 CHOOSE TO WRITE
      1270        LDY #0       COPY PAGE
      1280 .2     LDA COMMONPG,Y
      1290        STA COMMONPG,Y
      1300        INY
      1310        BNE .2       LOOP TO END OF PAGE
      1320        DEX
      1330        BNE .1       LOOP TO START OF TABLE
      1340        BEQ EXIT     RESTORE STANDARD MEMORY
      1350 *--------------------------------
      1360 *   enter MOVE with A = page (CX for 2nd DX)
      1370 *                   X = 64K bank #
      1380 *                   Carry SET for write, CLEAR for read
      1390 *--------------------------------
      1400 MOVE   BCS .3       BRANCH IF WRITING
      1410        CMP #$C0
      1420        BCS .1       BRANCH IF UPPER 16K
      1430        CPX #$FF     ---  READ 48K ---
      1440        BEQ .2       SKIP IF MAIN 64K
      1450        STA RAMRD+1  READ FROM AUX 48K
      1460        STX BNKSEL   SELECT 64K BANK
      1470        BNE .2
      1480 .1     JSR SEL16K   ---  READ 16K ---
      1490        CPX #$FF
      1500        BEQ .2       SKIP IF MAIN 64K
      1510        STX BNKSEL   SELECT 64K BANK
      1520        STA ALTZP+1  SELECT AUX 16K
      1530 .2     CLC
      1540        JSR COPYPAGE
      1550        BEQ EXIT
      1560 *--------------------------------
      1570 *   WRITING
      1580 *--------------------------------
      1590 .3     CMP #$C0
      1600        BCS .4       BRANCH IF UPPER 16K
      1610        CPX #$FF     ---  WRITE 48K ---
      1620        BEQ .5       SKIP IF MAIN 64K
      1630        STX BNKSEL
      1640        STA RAMWRT+1 WRITING TO AUX 48K
      1650        BNE .5
      1660 .4     JSR SEL16K   ---  WRITE 16K ---
      1670        CPX #$FF
      1680        BEQ .5
      1690        STX BNKSEL
      1700        STA ALTZP+1
      1710 .5     SEC
      1720        JSR COPYPAGE
      1730 *--------------------------------
      1740 EXIT   STY BNKSEL   RESORE STD 64K FOR VIDEO
      1750        STA RAMWRT   MAIN 48K
      1760        STA RAMRD
      1770        STA ALTZP    MAIN 16K
      1780        LDA ROM
      1790        RTS
      1800 *--------------------------------
      1810 *   BANKS is a table of 64K bank #'s, where
      1820 *      FF = main 64k, 00 = alt 64K when no RAMWORKS
      1830 *      00,04,08,0C = banks of a 256K RAMworks
      1840 *      1st entry is # of banks
      1850 *--------------------------------
      1860 BANKS  .HS 05       Five banks all told
      1870        .HS FF.00.04.08.0C
      1880 *--------------------------------
      1890 *   COPYPAGE copies 256 bytes
      1900 *      from (PTR) in specified bank to motherboard $200
      1910 *   or from motherboard $200 to (PTR) in specified bank
      1920 *--------------------------------
      1930 COPYPAGE
      1940        STA PTR+1
      1950        LDY #0
      1960        STY PTR
      1970        BCS .2
      1980 .1     LDA (PTR),Y
      1990        STA BUFFER,Y
      2000        INY
      2010        BNE .1
      2020        RTS
      2030 .2     LDA BUFFER,Y
      2040        STA (PTR),Y
      2050        INY
      2060        BNE .2
      2070        RTS
      2080 *--------------------------------
      2090 *   SEL16K selects the appropriate bank in 16K area
      2100 *--------------------------------
      2110 SEL16K CMP #$D0
      2120        BCS .1
      2130        LDY RAM2     C0   -> AUX D0
      2140        LDY RAM2
      2150        ADC #$10
      2160        RTS
      2170 .1     LDY RAM1     SELECT RD/WRT RAM
      2180        LDY RAM1
      2190        RTS
      2200 *--------------------------------

Correction to DOS/ProDOS Double Init | Bob Sander-Cederlof

The Sep 85 (V5N12) issue of AAL included an article and program to initialize a disk with both DOS and ProDOS catalogs in separate halves of the disk. After trying to use Catalog Arranger on a disk we made with DOUBLE.INIT, we discovered that program has a bug.

The DOS catalog is written in track $11, starting with sector 15 and going backwards to sector 1. The second and third bytes in each catalog sector are supposed to point to the next catalog sector, with the exception of those bytes in the LAST catalog sector. In the last catalog sector, the link bytes should both be $00, to signal to anyone who tries to read the catalog that this is indeed the last sector. DOUBLE.INIT stored $11 in the first link byte, and so some catalog reading programs such as Catalog Arranger get very confused.

The fix is to add the following lines to the program, where the line numbers correspond to those in the printed listing in AAL:

           2201      BNE .5
           2202      STY C.TRACK   (Y=0)

Add the label ".5" to line 2210, so that it reads:

           2210 .5   JSR CALL.RWTS

If you have already created some disks with DOUBLE.INIT, we suggest you use a program such as Bag of Tricks, CIA, or some other disk zap program to clear the second byte of track $11, sector $01 on those disks.

An Interesting Bit of Trivia | Bill Parker

Some time ago I asked Bob S-C if he knew the origin of the term "6502". Why was this particular number chosen? Bob didn't know, but referred me to Bill Mensch at Western Design Center.

Bill worked at Motorola and was on the design team that created the 6800, which later led to the development of the 68000. He left Motorola with a few others and formed MOS Technology (now absorbed into Commodore), where they developed a new microprocessor which was supposed to be an improved version of the 6800. Hence the decision was made to use a number in the 6000 series. As for the hundreds digit, Commodore already had chips that used just about every digit, except "5". Thus, the "6500" series of chips was born.

As history tells us, the first chip in the series, the 6501, was too close to Motorola's design, and had to be revised. The result was the 6502.

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

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