Volume 6 -- Issue 2 | November 1985

In This Issue...

- Little DOS RAM Disk in Language Card
- Kablit Security System
- Easier QUIT from ProDOS
- Solutions to Adam Levin's Painting Puzzle
- Using the Object Vector in S-C Macro Assembler
- Note on Mainstay MacASM for Macintosh
- Commented Listing of ProDOS QUIT Code
- Two Ways to Merge Fields in a Byte
- Comments on O'Ryan's 65C02 mod for Apple II

Programming the 65816

Last month we expected to have ready for this issue a review of David Eyes' new book on programming the 65816 microprocessor. Well the books still haven't arrived, despite the passing of two promised shipping dates, so we're still waiting to see when they will really be available and what they come out like. We are accepting orders (about 20 so far!) and will send out the books and publish a review as soon as they arrive from Prentice-Hall.

quikLoading AppleWorks

For you quikLoader owners who are also using AppleWorks (or for you AppleWorks enthusiasts who want your computer to instantly start up in AppleWorks), Southern California Research Group can now produce a set of quikLoader EPROMs from your configured AppleWorks program disks. The price for the EPROMs and the programming service is $89.50. For more information call SCRG at (805) 529-2082.

Little DOS RAM Disk in Language Card | Bob Sander-Cederlof

For some reason, we have until now avoided this subject. Many versions of RAM disks have been created and published in various magazines. The programs always seemed to me to be rather long and involved for what they really had to do. Recently a friend typed one in from Nibble, prompting me to try my hand.

The so-called "language card" is really the 16K RAM area. In //e and //c computers it is not a separate card at all, just the top 16K of the motherboard RAM. It received the monicker of "language card" because it was first sold as a separate card with the Pascal language system. The RAM in this area is not directly addressable, because the top 16K of Apple's address space is normally allocated to I/O ($C000-CFFF) and ROM ($D000-FFFF).

By flipping a few software-controlled switches the address range from $D000 through $FFFF can be made to point at the 16K RAM instead of ROM. Furthermore, the addresses from $D000 through $DFFF can be pointed at either of two 4K banks. If you have an Apple II or II+ with a 16K RAM card you already know this, of course.

Some programs use the language card under DOS, and some do not. Some which do are Integer BASIC, S-C Macro Assembler, Visicalc, Magicalc, Big Mac, and Merlin. If you are just using Applesoft to run your own programs, the language card is not used.

If the card is otherwise idle, that RAM could be used to simulate a small disk drive. My program sets it up as a 64 sector drive, with 60 sectors available for files. One sector is used for the VTOC, and three sectors are used for the catalog. You can save up to 21 files into the disk, or one file of up to 60 sectors.

One of the first questions I had to answer was where to put the program. Naturally, it ended up at $300. This is almost always my first choice, because it is so easy. If I find some substantial reasons, I try harder and find some other place in RAM for my programs. The ramdisk code could be placed inside DOS itself, on top of the RWTS format code. Another choice might be to use up one page of the language card for the bulk of the code, using only a few lines of code inside RWTS to switch it on and off. I like this idea, but it does deprive me of one sector out of 60. Anyway, for now let's just leave it at $300.

Another choice to be made is how to link into DOS. Many hard disks and other ramdisks do it by placing a JMP or JSR instruction at the beginning of RWTS ($BD00-BD02). This works very well, but it would be nice to be able to use both our ramdisk and any hard disk also. Therefore, I figured out a way to chain my ramdisk together with my Sider hard disk. The method should be compatible with all the ramdisks and hard disks which patch in at $BD00.

The program is broken into two parts. The first part installs the ramdisk, and the second part performs the reads and writes. The installer loads and executes at $4000, but of course you could change it to whatever you wish.

I use six page zero locations. These are all locations which are used by regular RWTS, so it is all right for me to use them. I don't even need to save the original data and restore it when I am finished.

Lines 1090-1150 copy the read/write part down to $300-3B4. I actually copy a few extra bytes, but no harm done. I do have to be careful not to write any bytes above $3CF, because $3D0-3FF is already used by DOS and the monitor.

Lines 1160-1230 save the current contents of $BD00-BD02, and place a JMP to my ramdisk code there. Any future calls to RWTS will be vectored to my code down in page 3.

Lines 1250 and 1260 may look ridiculous, if you have not tried programming the language card before. The software-controlled switches ("soft switches") in the Apple are designed so that you have to make two references to address $C083 to turn it on and un-protect it. Two references to $C08B turn on the card also, but with the other 4K bank at $D000.

Lines 1270-1340 store zeroes in every byte from $D000-D3FF. In my scheme, those four pages are equivalent to four sectors (track $11, sectors 0-3). Now that I have mentioned that, why not tell you how I have laid out the whole 16K?

           Bank  Addresses  Trk Sectors
           ----------------------------
           C083  D000-D3FF  $11   0-3
           C083  D400-DFFF  $01   4-F
           C08B  D000-DFFF  $02   0-F
                 E000-EFFF  $03   0-F
                 F000-FFFF  $04   0-F

Lines 1350-1420 chain the three catalog sectors together. I have set up track $11 sector 3 as the first catalog sector, sector 2 as the second, and sector 1 as the third and last. This is the same kind of chain DOS makes on a real disk, but shorter.

Lines 1430-1500, together with the two data lines at 1550 and 1560, fill in the non-zero bytes in the VTOC sector. This table driven technique takes somewhat fewer bytes than direct code. I know, because the first time I wrote it the direct way: LDA, STA, LDA, STA, etc. The code as it now is plus the tables takes 45 bytes. The other way it takes 42 bytes just for the STA instructions. If I use LDA #$xx for each of the different values, that is another 16 bytes. So, I saved about 13 bytes. The TBLX line gives the offsets into the $D000 page, and the TBLA line gives the data value which should be stored at each one. I use a 00 offset to indicate the end of the list.

Line 1580 tells the assembler to start assembling code to be executed at $300, but to keep putting the object code bytes in a continuous stream. Since we are writing the code on a target file (see line 1030), the whole program is on one file. RAMDISK.IMAG gets the value $4076, which is what the program counter is BEFORE the .PH directive takes effect. At line 1590 RAMDISK.REAL gets the value $300.

When a program calls RWTS, it is usually through as JSR $B7B5 instruction. The code at $B7B5 disables the interrupts and then does a JSR $BD00. We put our hook at $BD00, so the code jumps to $306, my label LITTLE.RAM.DISK. Lines 1650 and 1660 are the code which normally is executed at $BD00-BD03. They store the IOB address.

Lines 1670-1700 pick up the slot number out of the IOB. This is actually the slot number times 16. If the caller has specified slot 3, he wants to read or write the ramdisk. Any other slot, we need to let regular RWTS do the work. Lines 1710-1750 copy the original contents back to $BD00-BD02. Then I can call RWTS again, and this time it won't come back until it has done its job. Lines 1760-1780 restore Y and A as they were before we got involved, and re-call RWTS. When RWTS is finished, lines 1790-1830 put my hook back into $BD00-BD02. You might wonder if I should be saving and restoring the Y- and A-registers here. I originally did, saving them before line 1790 and restoring them before 1840. Then I realized that the normal contents of Y and A after visiting RWTS are not meaningful. Only the carry status bit is important, as it signifies whether there was an error or not.

If the caller specified slot 3, he wants to talk to our ramdisk. Lines 1860-1900 check to make sure he specified drive 1. If not, we call it an error. I funneled all of the messages through .99, setting the error byte in the IOB to $40. This causes DOS to say there was an I/O error.

I used an EOR #1 rather than CMP #1 at line 189~ so that if the drive was correct, we would also have 0 in the A-register. At some point I need to store 0 into RAMP, and this saves me a LDA #0 instruction. Then line 1910 can set RAMP to 0.

Lines 1930-1970 pick up the sector number the caller specified, and checks it for proper range. It must be from 0 to 15 to be valid. For the time being I save it in a handier location, RAMP+1.

Lines 1980-2020 and 2110-2120 check the track value. I will accept tracks 1-4 and $11, but no others. I have to accept $11, because that is where DOS always expects the VTOC to be, and where the catalog almost always is. The other four tracks could be anything I want, just so they are not $11. Since I am only using 4 sectors of track 11 for VTOC and catalog, I want the others to be usable for files. DOS refuses to allocate any sectors to files in track 11 unless we patch some code in the file manager, so I just put the rest of that bank of ram in another track.

Lines 2040-2060 make sure that if the caller wants track $11, his sector number is not bigger than 3. Lines 2130-2170 make sure that if the caller wants track 1, his sector number is not less than 4. If the track is either $11 or 1, lines 2070-2090 set us up to use the $C083 bank at $D000, with the sector specifying which page in that bank to use.

If the caller wants track 2, 3, or 4 then lines 2250-2310 set up the $C08B side, and compute the page number according to the table given above.

All this may be academic, because we have yet to look at the opcode. We are only implementing read and write, so if the opcode is something else we give an error. Lines 2340-2390 check the opcode, and also set the carry status for read or clear carry for write.

Lines 2400-2420 write enable the ramcard and select the proper $D000 bank. The value in the X-register is either 0 or 8, so we are either addressing $C083 or $C08B twice. We don't really need to write enable it unless the opcode was WRITE, but it doesn't hurt anything.

Lines 2430-2460 clear the error byte in the IOB. I could save two bytes by doing this above, just after line 1910.

Lines 2470-2530 pick up the caller's buffer address and store it in a pointer in page zero. I don't do any range checking on the buffer address, but then neither does RWTS.

Lines 2540-2550 set Y=0 to start the read or write loop, and then branch to the read loop if carry was set. Lines 2570-2610 comprise the write loop, and lines 2620-2660 the read loop.

Finally, line 2670 turns the language card back off. Then we clear carry status to indicate no errors, and return.

And that is how you make a ramdisk. If you have a bigger RAM card, it probably came with a ramdisk program. But if not, you ought to be able to see how to extend this program to handle larger amounts of memory.

      1   .LIF
      1000 *SAVE S.LITTLE RAM DISK
      1010 *--------------------------------
      1020        .OR $4000
      1030        .TF B.LITTLE RAM DISK
      1040 *--------------------------------
      1050 RAMP   .EQ $3C,3D
      1060 BUFP   .EQ $3E,3F
      1070 IOB    .EQ $48,49
      1080 *--------------------------------
      1090 INSTALL
      1100        LDY #0       COPY CODE TO PAGE 3
      1110 .0     LDA RAMDISK.IMAG,Y
      1120        STA RAMDISK.REAL,Y
      1130        INY
      1140        CPY #$D0     NOT PAST $3CF
      1150        BCC .0
      1160 *---INSTALL DOS HOOK-------------
      1170        LDY #2
      1180 .1     LDA $BD00,Y
      1190        STA OLD.BD00,Y
      1200        LDA NEW.BD00,Y
      1210        STA $BD00,Y
      1220        DEY
      1230        BPL .1
      1240 *---INIT VTOC & CATALOG----------
      1250        LDA $C083
      1260        LDA $C083
      1270        INY          Y=0
      1280        TYA
      1290 .2     STA $D000,Y  CLEAR VTOC
      1300        STA $D100,Y  CLEAR THREE CATALOG PAGES
      1310        STA $D200,Y  ...ROOM FOR 21 FILES
      1320        STA $D300,Y
      1330        INY
      1340        BNE .2
      1350 *---CATALOG CHAIN----------------
      1360        LDA #$11     SIMULATED TRACK 11
      1370        STA $D201
      1380        STA $D301
      1390        INY          Y=1
      1400        STY $D202    POINT TO 3RD CATALOG SECTOR
      1410        INY          Y=2
      1420        STY $D302    POINT TO 2ND CATALOG SECTOR
      1430 *---FINISH THE VTOC--------------
      1440        LDY #0       USE TABLES FOR VTOC
      1450 .3     LDX TBLX,Y   INDEX INTO VTOC
      1460        BEQ .4       ...FINISHED
      1470        LDA TBLA,Y
      1480        STA $D000,X
      1490        INY
      1500        BNE .3       ...ALWAYS
      1510 *--------------------------------
      1520 .4     LDA $C082    BACK TO MOTHERBOARD ROM
      1530        RTS
      1540 *--------------------------------
      1550 TBLX   .HS 01.02.27.34.35.37.3C.3D.40.41.44.45.48.49.00
      1560 TBLA   .HS 11.03.7A.23.10.01.FF.F0.FF.FF.FF.FF.FF.FF
      1570 *--------------------------------
      1580 RAMDISK.IMAG .PH $300
      1590 RAMDISK.REAL
      1600 *--------------------------------
      1610 OLD.BD00   .BS 3
      1620 NEW.BD00   JMP LITTLE.RAM.DISK
      1630 *--------------------------------
      1640 LITTLE.RAM.DISK
      1650        STY IOB
      1660        STA IOB+1
      1670        LDY #1       LOOK AT SLOT NUMBER
      1680        LDA (IOB),Y
      1690        CMP #$30     RAMDISK IN SLOT 3
      1700        BEQ RAM.DISK.SELECTED
      1710        LDY #2
      1720 .1     LDA OLD.BD00,Y
      1730        STA $BD00,Y
      1740        DEY
      1750        BPL .1
      1760        LDY IOB
      1770        LDA IOB+1
      1780        JSR $BD00
      1790        LDY #2
      1800 .2     LDA NEW.BD00,Y
      1810        STA $BD00,Y
      1820        DEY
      1830        BPL .2
      1840        RTS
      1850 *--------------------------------
      1860 RAM.DISK.SELECTED
      1870        INY          LOOK AT DRIVE
      1880        LDA (IOB),Y
      1890        EOR #1       MUST BE DRIVE 1
      1900        BNE .99      ...NOT DRIVE 1, ERROR
      1910        STA RAMP     LO-BYTE OF RAMPAGE
      1920 *--------------------------------
      1930        LDY #5       GET SECTOR #
      1940        LDA (IOB),Y
      1950        CMP #16
      1960        BCS .99      BAD T/S
      1970        STA RAMP+1
      1980        DEY          GET TRACK #
      1990        LDA (IOB),Y
      2000        BEQ .99      INVALID TRACK #
      2010        CMP #$11     IS IT VTOC TRACK?
      2020        BNE .2       NOT TRACK 17
      2030 *---TRACK 17---------------------
      2040        LDA RAMP+1        GET SECTOR #
      2050        CMP #4            MUST BE 0-3
      2060        BCS .99           NOT VALID T/S
      2070 .1     ORA #$D0     FORM HI-BYTE OF ADDRESS
      2080        LDX #0       C083 BANK
      2090        BEQ .4       ...ALWAYS
      2100 *---TRACK 1-4--------------------
      2110 .2     CMP #5       OTHERWISE MUST BE TRACK 1-4
      2120        BCS .99      NOT VALID T/S
      2130        CMP #1       TRACK 1?
      2140        BNE .3       ...NO
      2150        LDA RAMP+1        GET SECTOR #
      2160        CMP #4            MUST BE 4-F
      2170        BCS .1            ...GOOD
      2180 *---ERROR------------------------
      2190 .99    LDY #13
      2200        LDA #$40
      2210        STA (IOB),Y
      2220        SEC
      2230        RTS
      2240 *--------------------------------
      2250 .3     ASL          CHANGE 2,3,4 TO 20,30,40
      2260        ASL
      2270        ASL
      2280        ASL
      2290        ADC #$B0     ... TO D0,E0,F0
      2300        ORA RAMP+1        MERGE SECTOR
      2310        LDX #8       C08B BANK
      2320 .4     STA RAMP+1
      2330 *--------------------------------
      2340        LDY #12      LOOK AT OPCODE
      2350        LDA (IOB),Y
      2360        BEQ .99      ...NOT RD OR WRT
      2370        CMP #3       IS IT RD OR WRT?
      2380        BCS .99      ...NO, IGNORE
      2390        LSR          SET CARRY IF READ, CLR IF WRT
      2400 *---SELECT RAMCARD BANK----------
      2410        LDA $C083,X
      2420        LDA $C083,X
      2430 *---CLEAR ERROR CODE-------------
      2440        LDY #13
      2450        LDA #0
      2460        STA (IOB),Y
      2470 *---GET BUFFER ADDRESS-----------
      2480        LDY #8
      2490        LDA (IOB),Y
      2500        STA BUFP
      2510        INY
      2520        LDA (IOB),Y
      2530        STA BUFP+1
      2540        LDY #0
      2550        BCS .6       ...READ
      2560 *---WRITE A SECTOR---------------
      2570 .5     LDA (BUFP),Y
      2580        STA (RAMP),Y
      2590        INY
      2600        BNE .5
      2610        BEQ .7       ...ALWAYS
      2620 *---READ A SECTOR----------------
      2630 .6     LDA (RAMP),Y
      2640        STA (BUFP),Y
      2650        INY
      2660        BNE .6
      2670 .7     LDA $C082    BACK TO MOTHERBOARD ROM
      2680        CLC
      2690        RTS
      2700 *--------------------------------
      2710        .EP
      2720        .LIF

Kablit Security System | Bob Sander-Cederlof

After three burglaries or attempts here at the office, and four at Bill's house, we have been looking into ways to make our Apples a little more secure. There are a variety of products available these days, some involving special furniture that locks up around computer, and others consisting of brackets that lock the equipment to the desk top. These solutions seem too expensive and limiting for our purposes. We are always shifting the computers and monitors around to install or remove cards or to connect or disconnect some accessory. And four computers here in the office and two more at our houses mean that the system had better be inexpensive.

Well we have found what looks to be the answer: the Kablit Security System, from Secure-It, Inc. This is 10 feet of 3/16" steel cable with a high-quality padlock-type lock and an assortment of special hardware to attach the cable to your computer, monitor, disk drives, printer, or whatever. The connectors attach using the normal case screws of your equipment, so in most cases there is no need to drill holes or otherwise tear things up. There are specific kits for the Apple //c and the Macintosh.

The list price of the Kablit Security System is $49.95; we will be offering them for $45 + shipping.

An Easier QUIT from ProDOS | Mark Jackson Chicago, IL

When using a hard disk with ProDOS it is often useful to use the MLI QUIT call to go from one application to another. However, if you are deep within a subdirectory the QUIT code makes you retype the entire Prefix if you want to shorten it. To allow the use of the right arrow during the QUIT call do the following:

    UNLOCK PRODOS
    BLOAD PRODOS,A$2000,TSYS
    CALL-151
    5764:75      (for ProDOS 1.1.1 -- use 5964 for 1.0.1)
    BSAVE PRODOS,A$2000,TSYS
    LOCK PRODOS

This changes the input call to $FD75 which allows right arrow input. There is one drawback: now to restore the prompted prefix you must press ESCape when asked for the Pathname of the next application.

Solutions to Adam Levin's Painting Puzzle | Adam Levin, et al

The puzzle, published last month, was to write a program which would fill all RAM from $0000 through $BFFF with the same value. What value is your choice.

The listing of my solution follows. It executes at $9966, which is inside the middle DOS buffer. To get it there, you can BLOAD or BRUN it. A few seconds after the screen fill's up with "Y" characters, the program has completely filled RAM from $0000 through $BFFD with $99.

Lines 1080-1200 fill all the RAM not occupied by my program (addresses $0000-$98FF and $99C8-$BFFF) with $99. I first fill the RAM from $99C8 up, and then from $0000 up through $98FF. You have to forgive the self-modifying code in a puzzle solution like this.

Lines 1210-1280 store a NOP and a JMP $0000 at the end of RAM. Lines 1320-1350 store $99 into $9900-$9999. It's getting hot in here!

Lines 1390-1590 get executed more than once. The first time, they store $99 into $999B-$99A3, and $99A5. By this time every byte from $0000 through $99A5 is set $99. All those bytes can be executed as "STA $9999,Y" instructions, and the JMP $0000 we placed at the end of RAM will do just that. When we get back up to line 1430, at $99A9, we start moving Y again and store $99 into $99A6-99AC and $99AE. It progressively keeps covering itself up, and eventually it is all gone:

           999B
           999C
           999D 99A6
           999E 99A7 99AF
           999F 99A8 99B0
           99A0 99A9 99B1 99B7
           99A1 99AA 99B2 99B8
           99A2 99AB 99B3 99B9 99BD
           99A3 99AC 99B4 99BA 99BE 99C1
           ...
           99A5 99A3 99B6 99BC 99C0 99C3 99C5 99C6

      1000 *SAVE S.RAMFILL ADAM
      1010 *--------------------------------
      1020 *   ADAM LEVIN'S SOLUTION TO THE PUZZLE
      1030 *--------------------------------
      1040        .OR $9966    MUST START HERE
      1050        .TF B.PAINTER
      1060 *--------------------------------
      1070 PAINTER
      1080        LDY #END-1
      1090        LDA #$99     STORE $99 FROM END OF PROGRAM
      1100 COAT1  STA $9900,Y  THROUGH $BFFF
      1110        INY
      1120        BNE COAT1
      1130        INC COAT1+2  NEXT PAGE
      1140        LDX COAT1+2
      1150        CPX #$C0     REACHED $BFFF YET?
      1160        BNE .2       ...NOT YET
      1170        LDX #0       WRAP AROUND AND STORE FROM
      1180        STX COAT1+2  $0000 THRU $HERE
      1190 .2     CPX #$99     HAVE WE COME FULL CIRCLE?
      1200        BNE COAT1    ...NO, KEEP PAINTING
      1210        LDY #$EA     ...YES, NOW PATCH END OF RAM
      1220        STY $BFFB    FOR WRAPPING AROUND
      1230        STY $BFFC
      1240        LDY #$4C     NOP, JMP $0000
      1250        STY $BFFD
      1260        LDY #0
      1270        STY $BFFE
      1280        STY $BFFF
      1290 *--------------------------------
      1300 *   PAINT $9900-HERE
      1310 *--------------------------------
      1320 COAT2  STA $9900,Y
      1330        INY
      1340        CPY #COAT2+2
      1350        BCC COAT2
      1360 *--------------------------------
      1370 *   TRY TO GET OUT WITHOUT LEAVING FOOTPRINTS!
      1380 *--------------------------------
      1390        LDY #2       SET INDEX TO POINT TO $999B
      1400        STA $9999,Y
      1410        INY          $999C
      1420        STA $9999,Y
      1430        INY          $999D
      1440        STA $9999,Y
      1450        INY          $999E
      1460        STA $9999,Y
      1470        INY          $999F
      1480        STA $9999,Y
      1490        INY          $99A0
      1500        STA $9999,Y
      1510        INY          $99A1
      1520        STA $9999,Y
      1530        INY          $99A2
      1540        STA $9999,Y
      1550        INY          $99A3
      1560        STA $9999,Y
      1570        INY          $99A4
      1580        INY          $99A5
      1590 END    STA $9999,Y
      1600 *--------------------------------

Bob S-C's solution

The program loads at $800, but actually executes at $100. Lines 1030-1080 move the filler program down to $100 and jump to it. This solution fills all of RAM from $0000-BFFF with $48, which is a "PHA" instruction.

To keep from running off the end of RAM into the I/O space, I took advantage of the fact that the keyboard register can be read at both $C000 and $C001. Lines 1140-1160 wait until you type a zero key ("0"). The ASCII code for "0" is $B0. Two $B0 values in a row at $C000 and $C001 will dis-assemble as a BCS to $BFB2. Hence my solution finishes with an infinite loop running from $BFB2 to $C001.

Lines 1170-1290 fill RAM from $200-$BFFF with $48's, which are "PHA" opcodes. Lines 1300-1330 do the same with page zero.

Line 1350 jumps to $200, which means that the PHA opcodes start being executed. Since the stack is only 256 bytes long, and since the stack pointer wraps around, by the time the PHA at $2FF has executed all of page 1 will have been filled with $48. Since carry is set, when execution reaches $C000 the processor will go into that infinite loop I mentioned above.

      1000 *SAVE S.RAMFILL RBSC
      1010 *--------------------------------
      1020        .OR $800
      1030 MOVER  LDY #LENGTH       MOVE "FILLER" PROGRAM
      1040 .1     LDA MY.FILLER,Y   TO EXECUTION AREA
      1050        STA FILLER,Y      AT $100...
      1060        DEY
      1070        BPL .1
      1080        JMP FILLER        NOW START FILLING!
      1090 *--------------------------------
      1100 *    FOLLOWING CODE EXECUTES AT $100...
      1110 *--------------------------------
      1120 MY.FILLER .PH $100
      1130 FILLER
      1140 .1     LDA $C000    WAIT UNTIL "0" TYPED
      1150        CMP #$B0     ($B0 IS ALSO BCS OPCODE)
      1160        BNE .1
      1170 *---FILL $200-$BFFF--------------
      1180        LDY #0
      1190        STY 0
      1200        LDA #2
      1210        STA 1
      1220        LDA #$48     PHA OPCODE
      1230 .2     STA (0),Y
      1240        INY
      1250        BNE .2
      1260        INC 1
      1270        LDX 1
      1280        CPX #$C0     UNTIL $BFFF
      1290        BCC .2
      1300 *---FILL PAGE ZERO---------------
      1310 .3     STA 0,Y
      1320        INY
      1330        BNE .3
      1340 *---FILL PAGE ONE----------------
      1350        JMP $200
      1360 LENGTH .EQ *-FILLER
      1370        .EP
      1380 *--------------------------------

David Johnson's solution

My solution uses the power of the 65802. There was no restriction to the 6502 mentioned in the puzzle last month. All 49152 locations of motherboard RAM are filled with $DB, which happens to be the opcode value for the "STP" opcode. STP means "stop the processor", so once all RAM is filled it quits!

I use the MVP instruction to do the actual filling. The MVP instruction is located at $0000. I first put $DB into $BFFF. Then I set up the registers so that MVP will copy $BFFF into $BFFE, then $BFFE into $BFFD, and so on down to copying $0001 into $0000. By this time the MVP runs out, and the processor executes the STP opcode at $0003.

The 2nd and 3rd bytes of the MVP opcode specify which 64K memory banks to use; on a 65802 these don't do anything, because the bank addresses don't get out of the chip. On a 65816 my program won't work correctly, because the bank bytes will be changed at the end. First the Source bank address will be changed, so that a byte will be copied from $DB.0002 into $00.0001. Now the Destination Bank Address is changed, to we don't know what: we will finally copy $DB.0001 into $xx.0000. That last byte-move could be catastrophic (who knows, since we don't have any 65816-based systems yet?). Anyway, my program works fine in an Apple equipped with a 65802.

      1000 *SAVE DAVID JOHNSON'S FILLER
      1010 *--------------------------------
      1020 *      SOLUTION TO PUZZLE BY DAVID C. JOHNSON
      1030 *--------------------------------
      1040        .OP 65802    I got mine!
      1050 *--------------------------------
      1060        .OR $00
      1070 *--------------------------------
      1080 paint  mvp 0,0      fill $BFFE-$0000 from $BFFF
      1090 *--------------------------------
      1100 START  LDA #$DB     "STP" OPCODE
      1110        STA $BFFF    SEED FOR THE "MVP" INSTRUCTION
      1120        CLC          GET INTO NATIVE MODE
      1130        XCE
      1140        REP #$30     16-BIT REGISTERS
      1150        LDX ##$BFFF  Source Address = $BFFF
      1160        TXY
      1170        DEY          Destination Address = $BFFE
      1180        TYA          # Bytes -1 to be "moved"
      1190        BRA paint    MVP must be at $0000
      1200 *--------------------------------

Using the Object Vector in S-C Macro Assembler | Bill Morgan

Sometimes we want to do something special with the object code generated by the S-C Macro Assembler. Maybe write it directly into an EPROM programmer, send it out through a serial port, or store it into some special device. One such device is the Douglas Electronics Writable ROM Board, which appears to the Apple as 2K of RAM at $C800 but brings out a cable that plugs right into a 2716 EPROM socket. With this card we can test the assembled code instantly in the target machine, without the delay and hassle of programming and transferring an EPROM.

There are a couple of hitches along the way. The assembler normally protects everything above $BFFF from code storage, and we need some special code because we have to temporarily switch off any other card using $C800, switch on the WROM Board, write a byte, and switch the WROM Board off again.

Fortunately, Version 2.0 of the S-C Macro Assembler has some special features for cases just like this. There are parameters at the beginning of the assembler to unprotect a specified area of memory, and each byte generated is passed through an Object Vector on its way to storage, so we can intercept the byte and do our memory switching before passing it back to the assembler.

Since the object code is going to be stored in successive memory locations pointed to by the Target Address, we can just use the Macro Assembler's normal STORE.OBJECT.BYTE routine. The address of STORE.OBJECT.BYTE is in the JMP instruction at OBJECT.VECTOR, so it's easy to get that address, plug it into our code, and then install our address in OBJECT.VECTOR. If we needed to do something different with the object code, like storing each byte into the same hardware register, we would do that instead at the line labelled CALL.

Writable ROM Board, by Douglas Electronics, 718 Marina Blvd., San Leandro, CA 94577. (415) 483-8770. $95.

      1000 *SAVE S.WROMWRITE
      1010 *--------------------------------
      1020 LC       .EQ 1      1 if $D000 assembler
      1030 WROMSLOT .EQ 7
      1040 *--------------------------------
      1050      .DO LC
      1060 OBJECT.VECTOR  .EQ $D012
      1070 UNPROTECT.LOW  .EQ $D024
      1080 UNPROTECT.HIGH .EQ $D026
      1090      .ELSE
      1100 OBJECT.VECTOR  .EQ $1012
      1110 UNPROTECT.LOW  .EQ $1024
      1120 UNPROTECT.HIGH .EQ $1026
      1130      .FIN
      1140
      1150 WRITECARD   .EQ WROMSLOT*$10+$C080
      1160 CARDOFF     .EQ WROMSLOT*$10+$C081
      1170 C800.OFF    .EQ $CFFF
      1180
      1190 TARGET.LOW  .EQ $C800
      1200 TARGET.HIGH .EQ $CFFF
      1210 *--------------------------------
      1220        .OR $300
      1230 *      .TF WROMWRITE
      1240 INSTALL
      1250      .DO LC
      1260        BIT $C083
      1270        BIT $C083
      1280      .FIN
      1290        LDA /TARGET.LOW
      1300        STA UNPROTECT.LOW+1
      1310        LDA #TARGET.LOW
      1320        STA UNPROTECT.LOW
      1330        LDA /TARGET.HIGH
      1340        STA UNPROTECT.HIGH+1
      1350        LDA #TARGET.HIGH
      1360        STA UNPROTECT.HIGH
      1370        LDA OBJECT.VECTOR+2
      1380        STA CALL+2
      1390        LDA OBJECT.VECTOR+1
      1400        STA CALL+1
      1410        LDA /CARDON
      1420        STA OBJECT.VECTOR+2
      1430        LDA #CARDON
      1440        STA OBJECT.VECTOR+1
      1450      .DO LC
      1460        BIT $C080
      1470      .FIN
      1480        RTS
      1490 *--------------------------------
      1500 CARDON BIT C800.OFF
      1510        BIT WRITECARD
      1520 CALL   JSR $FFFF
      1530        BIT CARDOFF
      1540        RTS

Note on Mainstay MACASM for the Macintosh | Bob Sander-Cederlof

We still have a small supply of the original release of this highly-praised development tool for the Macintosh. (Even Jerry Pournelle had good words for it.) I say original edition, because they are now at version 1.2, with 1.3 scheduled in January.

Mainstay has told us that there is little real difference in the various versions, not enough to influence your decision as to where to buy. And they also have a policy that they will provide your first upgrade absolutely free. All you need to do is fill in your registration card, make a backup copy of MacASM to use in the interim, and send them your original MacASM disk.

Their current end-user price is $125. Note that ours are still being sold at the intorductory price of $100. Wow! It's a steal!

Commented Listing of ProDOS QUIT Code | Bob Sander-Cederlof

After reading Mark Jackson's article on improving the ProDOS QUIT code, I though it would be nice to have a commented listing of that program. The listing which follows is just that.

The ProDOS QUIT code is booted into $D100-D3FF in the alternate $D000 bank (the one you get by diddling $C083). Normally ProDOS MLI stays in the $C08B side. When a program issues the QUIT call (MLI code $65), the contents of $D100-D3FF are copied to $1000-12FF; then ProDOS jumps to $1000.

If you BLOAD the SYS file named PRODOS from a bootable ProDOS 1.1.1 disk, and examine it, you will find that it is laid out in eight parts. The first part is a relocator, which copies the other seven parts into their normal homes. Like this:

           Position        Position
           as loaded       copied to
           ---------------------------------------------------
           2000-29FF       ---             Relocator
           2A00-2BFF       Aux 200-3FF     /RAM/ driver
           2C00-2C7F       FF00-FF7F       /RAM/ driver
           2C80-2CFF       nowhere         All zeroes
           2D00-4DFF       D000-F0FF       MLI Kernel
           4E00-4EFF       BF00-BFFF       System Global Page
           4F00-4F7F       D742-D7BD       Thunderclock driver
           4F80-4FFF       FF80-FFFF       Interrupt Code
           5000-56FF       F800-FEFF       Device Drivers
           5700-59FF       D100-D3FF(alt)  QUIT Code
           zeroes          F100-F7FF

The part I am interested in right now is the QUIT code, which is at $5700-$59FF in the PRODOS file.

The QUIT code is not written very efficiently. For some reason, there are two completely separate editing programs: one for the prefix, and another for the pathname. (And as Mark points out, neither one is very handy.) Even the code that initializes the BITMAP is inefficient.

      1000 *SAVE S.PRODOS.QUIT
      1010 *--------------------------------
      1020 CH     .EQ $24
      1030 CV     .EQ $25
      1040 ERRCOD .EQ $DE
      1050 *--------------------------------
      1060 BUF    .EQ $0280
      1070 *--------------------------------
      1080 SYSTEM .EQ $2000
      1090 *--------------------------------
      1100 MLI    .EQ $BF00
      1110 BITMAP .EQ $BF58
      1120 *--------------------------------
      1130 KEY    .EQ $C000
      1140 S80STOREOFF .EQ $C000
      1150 S80OFF .EQ $C00C
      1160 SALTON .EQ $C00F
      1170 STROBE .EQ $C010
      1180 ROM    .EQ $C082
      1190 *--------------------------------
      1200 HOME   .EQ $FC58
      1210 CLREOL .EQ $FC9C
      1220 RDKEY  .EQ $FD0C
      1230 CROUT  .EQ $FD8E
      1240 COUT   .EQ $FDED
      1250 SETKBD .EQ $FE89
      1260 SETVID .EQ $FE93
      1270 BELL   .EQ $FF3A
      1280 *--------------------------------
      1290        .MA MLI
      1300        JSR MLI
      1310        .DA #$]1,]2
      1320        .EM
      1330 *--------------------------------
      1340        .OR $1000
      1350        .TA $5700
      1360 *--------------------------------
      1370 PRODOS.QUIT
      1380        LDA ROM      TURN ON THE MONITOR ROM
      1390        JSR SETVID   GET BACK TO GOOD OLD-FASHIONED
      1400        JSR SETKBD        DOWN-HOME 40 COLUMN DISPLAY
      1410        STA S80OFF
      1420        STA SALTON   Know what I mean, Vern?
      1430        STA S80STOREOFF
      1440 *---PREPARE BITMAP---------------
      1450        LDX #$17
      1460        LDA #1     Mark $BFxx in use
      1470        STA BITMAP,X
      1480        DEX
      1490        LDA #0       Most pages are free
      1500 .1     STA BITMAP,X
      1510        DEX
      1520        BPL .1
      1530        LDA #$CF     $0000-01FF, $0400-07FF in use
      1540        STA BITMAP
      1550 *---DISPLAY PREFIX---------------
      1560 GET.PREFIX
      1570        JSR HOME
      1580        JSR CROUT
      1590        LDA #Q.PRFX
      1600        STA MSG.ADDR
      1610        LDA /Q.PRFX
      1620        STA MSG.ADDR+1
      1630        JSR PRINT.MESSAGE
      1640        LDA #3       VTAB 4
      1650        STA CV
      1660        JSR CROUT    MAKE IT 5
      1670        >MLI C7,PREFIX.PARM
      1680        LDX BUF      # CHARS IN PREFIX
      1690        LDA #0       MARK END OF PREFIX WITH 00
      1700        STA BUF+1,X  SO OUR MESSAGE PRINTER WILL
      1710        LDA #BUF+1        PRINT IT.
      1720        STA MSG.ADDR
      1730        LDA /BUF+1
      1740        STA MSG.ADDR+1
      1750        JSR PRINT.MESSAGE
      1760 *---GET NEW PREFIX---------------
      1770        LDX #0
      1780        DEC CV       MOVE CURSOR TO BEGINNING OF LINE
      1790        JSR CROUT
      1800 NEXT.PREFIX.CHAR
      1810        JSR RDKEY
      1820        CMP #$8D
      1830        BEQ SET.NEW.PREFIX   ...ACCEPT WHAT IS ON SCREEN
      1840        PHA          ERASE PREFIX FROM SCREEN
      1850        JSR CLREOL
      1860        PLA
      1870        CMP #$9B     IS CHAR <ESCAPE>?
      1880        BEQ GET.PREFIX   ...YES, START ALL OVER
      1890        CMP #$98     IS CHAR CTRL-X?
      1900 START.PREFIX.OVER
      1910        BEQ GET.PREFIX    ...START ALL OVER
      1920        CMP #$89     IS CHAR <TAB>?
      1930        BEQ .3       ...YES, RING BELL
      1940        CMP #$88     IS CHAR BACKSPACE?
      1950        BNE .2       ...NO, APPEND TO LINE
      1960        CPX #0       ...BACKSPACE, UNLESS AT BEGINNING
      1970        BEQ .1       AT BEGINNING ALREADY
      1980        DEC CH       BACK UP
      1990        DEX
      2000 .1     JSR CLREOL   CHOP OFF AFTER CURSOR
      2010        JMP NEXT.PREFIX.CHAR
      2020 .2     BCS .4       OTHER CONTROL CHAR < $88
      2030 .3     JSR BELL
      2040        JMP NEXT.PREFIX.CHAR
      2050 .4     CMP #"Z"+1
      2060        BCC .5       ...NOT LOWER CASE
      2070        AND #$DF     CONVERT LOWER CASE TO UPPER
      2080 .5     CMP #"."     ALLOW PERIOD, SLASH, DIGITS
      2090        BCC .3       ...TOO SMALL
      2100        CMP #"Z"+1   ALLOW LETTERS
      2110        BCS .3       ...TOO LARGE
      2120        CMP #"9"+1
      2130        BCC .6       ...PERIOD, SLASH, OR DIGIT
      2140        CMP #"A"
      2150        BCC .3       ...NOT A LEGAL CHARACTER
      2160 .6     INX
      2170        CPX #$27
      2180        BCS START.PREFIX.OVER   ...TOO LONG
      2190        STA BUF,X
      2200        JSR COUT     ECHO THE CHARACTER
      2210        JMP NEXT.PREFIX.CHAR
      2220 *--------------------------------
      2230 SET.NEW.PREFIX
      2240        CPX #0       DID WE CHANGE IT?
      2250        BEQ GET.PATHNAME   ...NO
      2260        STX BUF      ...YES, SO TELL SYSTEM
      2270        >MLI C6,PREFIX.PARM
      2280        BCC GET.PATHNAME   ...NO ERRORS
      2290        JSR BELL     DING, DONG!
      2300        LDA #0       SET .EQ. STATUS
      2310 PFXOVR BEQ START.PREFIX.OVER    ...ALWAYS
      2320 *--------------------------------
      2330 GET.PATHNAME
      2340        JSR HOME
      2350 START.PATHNAME.OVER
      2360        JSR CROUT
      2370        LDA #Q.PATH
      2380        STA MSG.ADDR
      2390        LDA /Q.PATH
      2400        STA MSG.ADDR+1
      2410        JSR PRINT.MESSAGE
      2420        LDA #3       VTAB 4
      2430        STA CV
      2440        JSR CROUT    MAKE IT 5
      2450        LDX #0
      2460 NEXT.PATHNAME.CHAR
      2470        LDA #$FF     CURSOR CHARACTER
      2480        JSR COUT
      2490        DEC CH       BACK UP OVER CURSOR
      2500 .1     LDA KEY
      2510        BPL .1       ...WAIT TILL KEY PRESSED
      2520        STA STROBE
      2530        CMP #$9B     <ESCAPE>?
      2540        BNE .2       ...NO
      2550        LDA CH       IF AT BEGINNING, GET PREFIX OVER
      2560        BNE GET.PATHNAME      ...ELSE GET PATHNAME OVER
      2570        BEQ PFXOVR
      2580 .2     CMP #$98     CONTROL-X?
      2590 .3     BEQ GET.PATHNAME
      2600        CMP #$89     TAB KEY?
      2610        BEQ .5       ...YES
      2620        CMP #$88     BACKSPACE?
      2630        BNE .4       ...NO
      2640        JMP BACKSPACE.IN.PATHNAME
      2650 *--------------------------------
      2660 .4     BCS .6
      2670 .5     JSR BELL     ...INVALID CHAR, RING BELL
      2680        JMP NEXT.PATHNAME.CHAR
      2690 *--------------------------------
      2700 .6     CMP #$8D
      2710        BEQ SET.NEW.PATHNAME
      2720        CMP #"Z"+1
      2730        BCC .7
      2740        AND #$DF     CHANGE LOWER CASE TO UPPER
      2750 .7     CMP #"."     ACCEPT DOT, SLASH, OR DIGIT
      2760        BCC .5       ...TOO SMALL
      2770        CMP #"Z"+1   ACCEPT LETTERS
      2780        BCS .5       ...TOO LARGE
      2790        CMP #"9"+1
      2800        BCC .8       ...DOT, SLASH, OR DIGIT
      2810        CMP #"A"
      2820        BCC .5       ...NOT A VALID CHARACTER
      2830 .8     PHA          CLEAR BEYOND THIS POINT
      2840        JSR CLREOL
      2850        PLA
      2860        JSR COUT     ECHO THE NEW CHARACTER
      2870        INX
      2880        CPX #$27
      2890        BCS .3       ...NAME TOO LONG
      2900        STA BUF,X    APPEND CHAR TO NAME
      2910        JMP NEXT.PATHNAME.CHAR
      2920 *--------------------------------
      2930 SET.NEW.PATHNAME
      2940        LDA #" "
      2950        JSR COUT
      2960        STX BUF
      2970        >MLI C4,FILE.INFO.PARM
      2980        BCC .1       ...NO ERRORS
      2990        JMP PROCESS.ERROR
      3000 *--------------------------------
      3010 .1     LDA FILTYP   FILE.INFO.PARM+4
      3020        CMP #$FF
      3030        BEQ .2       "SYS" FILE
      3040        LDA #1
      3050        JMP PROCESS.ERROR
      3060 *--------------------------------
      3070 .2     LDA #0
      3080        STA CL.REF   CLOSE.PARM+1, REF NO.
      3090        >MLI CC,CLOSE.PARM
      3100        BCC .3       ...NO ERROR
      3110        JMP PROCESS.ERROR
      3120 *--------------------------------
      3130 .3     LDA ACBITS   FILE.INFO.PARM+3
      3140        AND #1
      3150        BNE .4       ...OKAY TO READ IT
      3160        LDA #$27
      3170        JMP PROCESS.ERROR
      3180 *--------------------------------
      3190 .4     >MLI C8,OPEN.PARM
      3200        BCC .5       ...NO ERRORS
      3210        JMP PROCESS.ERROR
      3220 *--------------------------------
      3230 .5     LDA OP.REF   OPEN.PARM+5, REF NO.
      3240        STA RD.REF   READ.PARM+1, REF NO.
      3250        STA EF.REF   EOF.PARM+1, REF NO.
      3260        >MLI D1,EOF.PARM
      3270        BCC .6       ...NO ERRORS
      3280        JMP PROCESS.ERROR
      3290 *--------------------------------
      3300 .6     LDA FIL.SZ+2    EOF.PARM+4
      3310        BEQ .7       ...NOT TOO LONG
      3320        LDA #$27
      3330        JMP PROCESS.ERROR
      3340 *--------------------------------
      3350 .7     LDA FIL.SZ      EOF.PARM+2
      3360        STA READ.PARM+4
      3370        LDA FIL.SZ+1    EOF.PARM+3
      3380        STA READ.PARM+5
      3390        >MLI CA,READ.PARM
      3400        PHP
      3410        >MLI CC,CLOSE.PARM
      3420        BCC .9
      3430        PLP
      3440 .8     JMP PROCESS.ERROR
      3450 *--------------------------------
      3460 .9     PLP
      3470        BCS .8
      3480        JMP SYSTEM
      3490 *--------------------------------
      3500 BACKSPACE.IN.PATHNAME
      3510        LDA CH       UNLESS ALREADY AT BEGINNING
      3520        BEQ .1       ...WE WERE
      3530        DEX
      3540        LDA #" "
      3550        JSR COUT
      3560        DEC CH
      3570        DEC CH
      3580        JSR COUT
      3590        DEC CH
      3600 .1     JMP NEXT.PATHNAME.CHAR
      3610 *--------------------------------
      3620 PRINT.MESSAGE
      3630        LDX #0
      3640 MSG.LP LDA MSG.LP,X
      3650 MSG.ADDR   .EQ *-2
      3660        BEQ .1
      3670        ORA #$80
      3680        JSR COUT
      3690        INX
      3700        BNE MSG.LP
      3710 .1     RTS
      3720 *--------------------------------
      3730 PROCESS.ERROR
      3740        STA ERRCOD
      3750        LDA #12      VTAB 13
      3760        STA CV
      3770        JSR CROUT    MAKE IT 14
      3780        LDA ERRCOD
      3790        CMP #1
      3800        BNE .1
      3810        LDA #ERQT.1
      3820        STA MSG.ADDR
      3830        LDA /ERQT.1
      3840        STA MSG.ADDR+1
      3850        BNE .3
      3860 .1     CMP #$40
      3870        BEQ .2
      3880        CMP #$44
      3890        BEQ .2
      3900        CMP #$45
      3910        BEQ .2
      3920        CMP #$46
      3930        BEQ .2
      3940        LDA #ERQT.2
      3950        STA MSG.ADDR
      3960        LDA /ERQT.2
      3970        STA MSG.ADDR+1
      3980        BNE .3       ...ALWAYS
      3990 .2     LDA #ERQT.3
      4000        STA MSG.ADDR
      4010        LDA /ERQT.3
      4020        STA MSG.ADDR+1
      4030 .3     JSR PRINT.MESSAGE
      4040        LDA #0       VTAB 1
      4050        STA CV
      4060        JMP START.PATHNAME.OVER
      4070 *--------------------------------
      4080 Q.PRFX .AS -/ENTER PREFIX (PRESS "RETURN" TO ACCEPT)/
      4090        .HS 00
      4100 Q.PATH .AS -/ENTER PATHNAME OF NEXT APPLICATION/
      4110        .HS 00
      4120 ERQT.1 .HS 87
      4130        .AS -/NOT A TYPE "SYS" FILE/
      4140        .HS 00
      4150 ERQT.2 .HS 87
      4160        .AS -"I/O ERROR            "
      4170        .HS 00
      4180 ERQT.3 .HS 87
      4190        .AS -"FILE/PATH NOT FOUND  "
      4200        .HS 00
      4210 *--------------------------------
      4220 FILE.INFO.PARM
      4230        .DA #10
      4240        .DA BUF
      4250 ACBITS .HS 00
      4260 FILTYP .HS 00
      4270        .BS 13
      4280 *--------------------------------
      4290 OPEN.PARM
      4300        .DA #3
      4310        .DA BUF
      4320        .DA $1800    BUFFER ADDR
      4330 OP.REF .BS 1        REF NO.
      4340 *--------------------------------
      4350 CLOSE.PARM
      4360        .DA #1
      4370 CL.REF .BS 1        REF NO.
      4380 *--------------------------------
      4390 READ.PARM
      4400        .DA #4
      4410 RD.REF .BS 1        REF NO.
      4420        .DA $2000    BUFFER ADDR
      4430        .BS 2        # BYTES TO READ
      4440        .BS 2        # ACTUALLY READ
      4450 *--------------------------------
      4460 EOF.PARM
      4470        .DA #2
      4480 EF.REF .BS 1        REF NO.
      4490 FIL.SZ .BS 3        EOF POSITION
      4500 *--------------------------------
      4510 PREFIX.PARM
      4520        .DA #1
      4530        .DA BUF
      4540 *--------------------------------
      4550        .LIF

Two Ways to Merge Fields in a Byte | Bob Sander-Cederlof

One of the advantages of assembly language is that data can be manipulated easily at the bit and byte level. This leads to efficiencies in both speed and memory usage which cannot be matched with most higher-level languages.

We can pack more than one data item into the same byte. For example, I may use the first three bits of a byte to indicate which of eight colors to use, and the other five bits to indicated position on a 32-pixel line. There are endless examples. Since we need to be able to store into and retrieve from bit-fields within bytes, all of the microprocessors include opcodes which make it possible.

To merge two values together which already are "clean", we simply use the ORA opcode. For example, if I have data for field A in VAL.A as xxx00000 and data for field B in VAL.B as 000xxxxx, I merge them like this:

           LDA VAL.A
           ORA VAL.B

By "clean" I mean that all the bits in VAL.A and VAL.B which are not part of the field values are already zero. If they are not, then we must first strip out those bits with the AND opcode:

           LDA VAL.A
           AND #$E0
           STA TEMP
           LDA VAL.B
           AND #$1F
           ORA TEMP

There is another way, which is shorter and faster and does not need TEMP. However, it is harder to figure out why it works.

           LDA VAL.A
           EOR VAL.B
           AND #$1F
           EOR VAL.A

Can you explain it? I was so unsure of myself when I first ran into this technique that I devised a test program. My test tries all 256 values of VAL.A and VAL.B, with all possible contiguous fields from 1 bit for VAL.A to 7 bits for VAL.A. Probably overkill, but it runs in a few seconds.

My program prints out the two field masks for each of the seven field sizes, so that I can tell it is running. If the two methods for merging get the same results, that is the only output. If they do not, indicating that one method or the other does not work, I print out more data.

While I was writing the program I tried several variations, such as printing all the results whether they agreed or not. In order to be able to look at that volume of output reasonably, I added a PAUSE subroutine which enabled me to stop the output by tapping any key, restart it the same way, and abort by tapping the RETURN key.

The code for the first merging method is in lines 1310-1380; that for the second at lines 1400-1450.

The test was conclusive. I tried every possible combination, and both methods always give the same results. Looking back, I can see that the whole test was unnecessary; the second method will OBVIOUSLY produce the same results. Now I see it. Do you?

      1000 *SAVE MERGE FIELDS IN A BYTE
      1010 *--------------------------------
      1020 CROUT  .EQ $FD8E
      1030 PRBYTE .EQ $FDDA
      1040 COUT   .EQ $FDED
      1050 *--------------------------------
      1060 FIELD.A    .EQ $00
      1070 FIELD.B    .EQ $01
      1080 VAL.A      .EQ $02
      1090 VAL.B      .EQ $03
      1100 MERGE.1    .EQ $04
      1110 MERGE.2    .EQ $05
      1120 *--------------------------------
      1130 T
      1140 *---FOR FIELD= 80,7F TO 7F,80----
      1150        LDA #$7F     DEFINE FIELDS AS 1,7
      1160        STA FIELD.B
      1170        LDA #$80
      1180        STA FIELD.A
      1190 *---FOR A=0 TO MAX VAL-----------
      1200 .1     LDA #0
      1210        STA VAL.A
      1220        JSR CROUT
      1230        LDA FIELD.A
      1240        JSR PRBYTESP
      1250        LDA FIELD.B
      1260        JSR PRBYTE
      1270 *---FOR B=0 TO MAX VAL-----------
      1280 .2     LDA #0
      1290        STA VAL.B
      1300
      1310 *---MERGE FIRST METHOD-----------
      1320 .3     LDA VAL.A
      1330        AND FIELD.A
      1340        STA MERGE.1
      1350        LDA VAL.B
      1360        AND FIELD.B
      1370        ORA MERGE.1
      1380        STA MERGE.1
      1390
      1400 *---MERGE SECOND METHOD----------
      1410        LDA VAL.A
      1420        EOR VAL.B
      1430        AND FIELD.B
      1440        EOR VAL.A
      1450        STA MERGE.2
      1460
      1470 *---PRINT RESULTS, IF NOT EQUAL--
      1480        CMP MERGE.1
      1490        BEQ .4
      1500        JSR CROUT
      1510        LDA FIELD.A
      1520        JSR PRBYTESP
      1530        LDA VAL.A
      1540        JSR PRBYTESP
      1550        LDA VAL.B
      1560        JSR PRBYTESP
      1570        LDA MERGE.1
      1580        JSR PRBYTESP
      1590        LDA MERGE.2
      1600        JSR PRBYTE
      1610        JSR PAUSE
      1620 *---NEXT B-----------------------
      1630 .4     INC VAL.B
      1640        BNE .3
      1650 *---NEXT A-----------------------
      1660        INC VAL.A
      1670        BNE .2
      1680 *---NEXT FIELD-------------------
      1690        SEC
      1700        ROR FIELD.A
      1710        LSR FIELD.B
      1720        BNE .1       CONTINUE
      1730        RTS          FINISHED
      1740 *--------------------------------
      1750 PRBYTESP
      1760        JSR PRBYTE
      1770        LDA #$A0
      1780        JMP COUT
      1790 *--------------------------------
      1800 PAUSE  LDA $C000
      1810        BPL .3
      1820        STA $C010
      1830        CMP #$8D
      1840        BNE .2
      1850 .1     PLA
      1860        PLA
      1870        RTS
      1880 .2     LDA $C000
      1890        BPL .2
      1900        STA $C010
      1910        CMP #$8D
      1920        BEQ .1
      1930 .3     RTS
      1940 *--------------------------------

Comments on O'Ryan's 65C02 Mod for Apple II | Jim Sather

William O'Ryan's method (October 1985 AAL) of modifying old Apples to accept 65C02s looks like a very reliable fix. I notice no negative consequences in RAM or video timing. I do however recommend switching to 150 nanosecond motherboard RAM.

Apple motherboard RAM read access is CAS' limited, meaning TCAC (delay from CAS' falling to read data valid) is the critical RAM chip specification. In an Apple with O'Ryan's fix, RAM chips have 140 nsec minus 74LS139 pin 1 to pins 4,5,6 high/low propagation delay to get RAM read data valid after CAS' falls at the RAM chips. This means TCAC needs to be 119 nsec or less with a typical LS139. TCAC specifications are 100 nsec for 150 nsec RAM and 135 nsec for 200 nsec RAM, so 150 nsec or faster chips should be installed to be within RAM chip specifications with O'Ryan's fix.

A given Apple II may work with O'Ryan's fix and 200 nsec RAM chips, but operation may not be reliable over a wide range of room temperatures. Again I say, O'Ryan's fix calls for 150 nsec RAM chips. To operate with slower chips is asking for trouble.

Incidentally, 16K RAM chips don't cost as much as they used to. The cheapest 150 nsec 16K RAM chips I can find in my current mail order catalogs are 45 cents apiece at Jameco Electronics, 1355 Shoreway Rd., Belmont, CA 94002. [Slower ones were $65.00 apiece in 1978!]

As an alternative to replacing slow motherboard RAM chips, one can replace the 74LS139 at F2 with a 74S139. This changes the TCAC requirement with O'Ryan's fix to 133 nsec for a typical S139, and to 130 nsec for a worst case S139. These are barely less than the 135 nsec specification of 200 nsec RAM, so operation with 200 nsec RAM is probably reliable.

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