Volume 7 -- Issue 8 | May 1987

In This Issue...

- A New Pattern Search Monitor Command
- Reading the IIgs ROMs
- More About Patching Apple's ProDOS Releases
- ABC*DE=FG*HI Puzzle Solved
- Problem with IIgs 3.5" Drive Firmware
- IIgs Tool Set Version Numbers

The Chips are Falling

We have just learned that Western Design Center is dramatically lowering the price of the 65802 microprocessors. We've been selling the '802 for $50.00: Effective about the time you read this our price will be $25.00. All the chips they produce are now rated to operate to at least 4 MHz, and they're now getting a significant yield of 6 and even 8 MHz parts. Evidently the volume production made possible by Apple's use of the 65816 in the IIgs is paying off.

Missing ProDOS Books

We had a scare a couple of weeks ago: Quality Software and Simon & Schuster had both run out of copies of Beneath Apple ProDOS, that excellent reference on the inner workings of ProDOS, so it looked for a while like we might lose a valuable resource. All is well, though, the folks at Quality are planning a new printing, so we expect to have more copies of the book in a month or two. We'll just hold any orders until that time.

Curiously, both Addison-Wesley's ProDOS Technical Reference Manual and Simon & Schuster's Apple ProDOS: Advanced Features for Programmers have been out of stock at the publishers for a couple of months now. A-W tells us that a revised edition of Apple's manual will be published in late June. S & S has Advanced Features on backorder, but won't quote even a tentative delivery date.

A New Pattern Search Monitor Command | Bob Sander-Cederlof

As I mentioned in my description of the new Apple IIgs monitor commands (AAL Jan 87, pp.23-27), there is a new "P" command which searches through a range of memory for all occurrences of a pattern. Well, I tried to use that command the other day.

First, I couldn't remember with confidence what the syntax was. Then I thought that it must be \pattern\adr1.adr2P. I tried that and it did not work. I looked it up in Fischer's "IIgs Technical Reference", and verified that I did have the correct syntax. It still did not work, and I did need a search capability, so I wrote my own. More on it later. That was last week. I don't know why I didn't look upo the command in another reference, but today I did. Both my memory and Fischer's book were wrong!

For some esoteric reason, Apple requires that the "<" character appear between the pattern and the address range. My January article and Apple's own reference material clearly show the "<" there: \pattern\<adr1.adr2P is the correct syntax. Type it correctly, and it works as advertised. For example:

         \8E C0\<0/2000.5FFFP will search $2000 through $5FFF
                              in bank 0 for the two bytes $8E
                              and $C0 in successive locations.
         \"Apple"\<FF/0.FFFFP will search all of bank $FF
                              for the string "Apple".  (In my
                              IIgs it found that string five
                              times.)

If you leave out the "<" character, nothing happens.

The "P" command is a powerful tool, and a welcome addition the Apple monitor. However, I still need some capabilities it does not have. For example, what if I want to find all references to a particular address made using any of the LDA instructions. I don't want to see any references using LDX, LDY, CMP, STA, etc. If I could tell it to only compare certain bits in some bytes, I could accomplish this and many other interesting feats. We might also like the feature of allowing a variable length wild card, such as in FID and FILER filenames and in the S-C Macro Assembler REPLACE command. Maybe I want to include the ability to find PC-relative references to an address. There is probably an endless list of possible features.

I did write a new search program, and did include one of the above mentioned new features. I linked it in through the control-Y vector, so that I can use it from within the monitor. It in effect adds a new command to the monitor, with this syntax "adr1.adr2Ypattern". (Where I show "Y", you have to type control-Y. If you are using the command from within the S-C Macro Assembler, you have to type control-O followed by control-Y.) The pattern is a sequence of hex bytes and/or byte-pairs. A hex byte will have to be completely matched. A byte-pair specifies which bits to compare by giving both a mask byte and a key byte. For example:

         0/2000.5FFFYAD F0:80 C0 will find all occurrences
                           of LDA $C080 through $C08F in
                           bank 0, 2000-5FFF.

         FF/0.FFFFYC1 DF:D0 DF:D0 DF:CC DF:C5 will find all
                           occurrences of "Apple" without
                           regard to upper or lower case.

In the January issue of AAL I also discussed the way to hook into the control-Y vector in the IIgs. It is different from the older Apples. Lines 1160-1230 in the following program install the control-Y vector in a IIgs. In an older Apple we would store only a 2-byte address at $3F9 and $3FA. This will still work in a IIgs, but then you would be limited to running in bank 0 in emulation mode. The bank $E1 vector allows you to be called in native mode, and your code can be in any bank of memory.

When you enter a control-Y command (assuming the vector has been installed) control will come to the SEARCH program. Lines 1260-1290 are a nice general way to save the current mode and status and get into emulation mode. Lines 1600-1630 restore the mode and status before returning.

Lines 1310-1320 call the GET.PATTERN subroutine and quit if that subroutine reports an error. GET.PATTERN scans the input buffer from just after the control-Y to a carriage return. It seemed reasonable to me to require that the entire command line be in the first 127 characters of the input buffer; that left the other half ($280-2FF) free for me to use for storing the converted key and mask bytes. GET.PATTERN stores the key bytes starting at $280, and the mask bytes starting at $2C0. It returns the number of bytes in the key in the X-register. Line 1330 stores this count at WBUF, which happens to also be the beginning of the input buffer.

Lines 1340-1350 copy the bank byte of the address range from the place the monitor keeps it ($E1013E) to the end of a 3-byte pointer in page zero. This seems like a good time to list all the places the monitor puts the addresses it parses. The general form of a monitor command is "a/b<c/d.eX". "a/b" is a target address, with "a" specifying the bank and "b" specifying the address within the bank. "c/d" is the beginning address of a range, with "c" specifying the bank and "d" the address within the bank. "e" is the ending address of the range, in the same bank with "d". "X" represents any monitor command letter. Some commands do not use all of the parsable parts, but you can usually get away with entering them anyway. The monitor subroutine called GETNUM is responsible for parsing the addresses. In older machines GETNUM was at $FFA7. In the IIgs there is still a copy of GETNUM at $FFA7, but it is not used. Instead a new version which starts at $F888 is used. This new version allows the "a/" and "c/" parts. From my analysis, here is where the new GETNUM stores a, b, c, d, and e:

           a -- $E1/013F
           b -- $00/0042,43
           c -- $E1/013E
           d -- $00/003C,3D and $00/0040,41
           e -- $00/003E,3F

GETNUM will also leave the pointer into the input buffer which points to the next character after the command character in YSAVE, which is $00/0034.

The IIgs also has a new subroutine at $FCCA, which gets the next character from the input buffer and converts it from lower-case to upper-case if it is a lower-case letter.

Lines 1370-1580 perform the actual search operation. Lines 1370-1440 compare the masked portions of the key bytes with the masked portion of the memory bytes starting at PNTR. If they all match, lines 1460-1490 call on PRADR to print the 3-byte address. Lines 1500-1580 increment the PNTR and test to see if we have come to the end of the range. [Note that a multiple- byte key will be tested beyond the end of the range. That is, the search will continue until the first key byte has been tested throughout the range. This means the key bytes after the first one will extend beyond the end of the range. I am pointing this out so that you will not accidentally start tripping softswitches in the I/O space when you have a range ending at $BFFF.]

There are ample possibilities here for you to expand the features of this search. You could add to the GET.PATTERN a more flexible wild-card scheme. You could allow ASCII strings to be entered. You could automatically protect the I/O softswitch range, especially $C000-C0FF. You could save the current shadowing status and then turn off all shadowing during the search. And so on.

I slipped in another little routine at lines 2150-2420. I am not calling it here, but when I did it printed out the currently parsed addresses. It helped me to get a better feeling for what happens when you enter an incomplete address specification. If you leave off the bank, it uses the same one you used before. If you leave out the range beginning, it begins where the previous command ended. There are a lot of choices here, so I find I usually just type the complete command to be sure.

      1000 *SAVE S.SEARCH.IIGS
      1010 *--------------------------------
      1020 YSAVE  .EQ $34
      1030 END    .EQ $3E,3F   ADDRESS OF END OF RANGE
      1040 PNTR   .EQ $40,41,42   START OF RANGE, CURRENT PNTR
      1050 *--------------------------------
      1060 WBUF   .EQ $200
      1070 KBUF   .EQ $280
      1080 MBUF   .EQ $2C0
      1090 *--------------------------------
      1100 NXTCHR .EQ $FCCA
      1110 CROUT  .EQ $FD8E
      1120 PRBYTE .EQ $FDDA
      1130 COUT   .EQ $FDED
      1140 *--------------------------------
      1150        .OP 8
      1160 T
      1170        LDA #SEARCH  Setup Control-Y vector
      1180        STA $E100A1
      1190        LDA /SEARCH
      1200        STA $E100A2
      1210        LDA #0
      1220        STA $E100A3
      1230        RTS
      1240 *--------------------------------
      1250 SEARCH
      1260        PHP          Save mode, get into emulation mode
      1270        SEC
      1280        XCE
      1290        PHP
      1300 *--------------------------------
      1310        JSR GET.PATTERN   Crack the search pattern
      1320        BCS .99           ...nothing there or too long
      1330        STX WBUF          save # bytes in pattern
      1340        LDA $E1013E       Bank to search
      1350        STA PNTR+2        make 3-byte address
      1360 *---Compare pattern--------------
      1370 .1     LDY #-1
      1380 .2     INY
      1390        LDA >(PNTR),Y     Byte from source
      1400        EOR KBUF,Y        Byte from pattern
      1410        AND MBUF,Y        Byte from mask
      1420        BNE .3            ...does not pass inspection
      1430        CPY WBUF          At end of pattern yet?
      1440        BCC .2            ...no
      1450 *---Found a match----------------
      1460        LDA PNTR+2        Print 3-byte address
      1470        LDX #PNTR
      1480        LDY #" "          ...followed by a blank
      1490        JSR PRADR
      1500 *---Slip to next postion---------
      1510 .3     LDA PNTR     Set carry if at end of range
      1520        CMP END
      1530        LDA PNTR+1
      1540        SBC END+1
      1550        INC PNTR     Bump comparison pointer
      1560        BNE .4
      1570        INC PNTR+1
      1580 .4     BCC .1
      1590 *--------------------------------
      1600 .99    PLP          Restore machine mode
      1610        XCE
      1620        PLP
      1630        RTS
      1640 *--------------------------------
      1650 GET.PATTERN
      1660        LDY YSAVE
      1670        LDX #-1
      1680 .1     INX
      1690        LDA #$FF     Assume mask =$FF
      1700 .2     STA MBUF,X   Store a mask value
      1710        LDA #$00     Start Keybyte = $00
      1720 .3     STA KBUF,X
      1730        CPY #$7F
      1740        BCS .7       ...LINE TOO LONG
      1750        JSR NXTCHR   GET UPPER-CASE CHAR AT WBUF,Y
      1760        CMP #" "     Is it a space?
      1770        BEQ .1       ...yes, next byte
      1780        CMP #":"
      1790        BCC .5       < $BA, must be 0...9
      1800        BNE .4       > $BA, must be A...F
      1810        LDA KBUF,X   = $BA, ":", means to put
      1820        BCS .2       ...ALWAYS   value into MASK
      1830 *---Try letter A...F-------------
      1840 .4     CMP #"G"
      1850        BCS .7       ...ERROR
      1860        CMP #"A"
      1870        BCC .7       ...ERROR
      1880        SBC #7       "A"..."F"
      1890 *---Try hex digit range----------
      1900 .5     CMP #"0"     Is it 0...9 or A...F?
      1910        BCC .6       ...no
      1920        AND #$0F     ...yes, isolate digit
      1930        ASL KBUF,X   Shift previous value
      1940        ASL KBUF,X
      1950        ASL KBUF,X
      1960        ASL KBUF,X
      1970        ORA KBUF,X   Merge with digit
      1980        JMP .3
      1990 *---End of line or error---------
      2000 .6     CMP #$8D
      2010        BNE .7       ...ERROR
      2020        DEY
      2030        STY YSAVE
      2040        CLC
      2050        RTS
      2060 *---Bad char or line too long----
      2070 .7     STY YSAVE
      2080        LDA #$8D     STORE <RETURN> IN MONITOR
      2090        STA WBUF+1,Y      INPUT BUFFER
      2100        LDA #$87     RING THE BELL
      2110        JSR COUT
      2120        SEC          INFORM OF ERROR
      2130        RTS
      2140 *--------------------------------
      2150 DISPLAY.ADDRESSES
      2160        PHP
      2170        SEC
      2180        XCE
      2190        PHP
      2200 *--------------------------------
      2210        LDA YSAVE      WBUF,Y POINTS TO NEXT CHAR
      2220        JSR PRBYTE         AFTER THE CTRL-Y
      2230        JSR CROUT
      2240 *--------------------------------
      2250        LDA $E1013F
      2260        LDX #PNTR+2
      2270        LDY #"<"
      2280        JSR PRADR
      2290 *--------------------------------
      2300        LDA $E1013E
      2310        LDX #PNTR
      2320        LDY #"."
      2330        JSR PRADR
      2340 *--------------------------------
      2350        LDX #END
      2360        LDY #$8D
      2370        JSR PRADR0
      2380 *--------------------------------
      2390        PLP
      2400        XCE
      2410        PLP
      2420        RTS
      2430 *--------------------------------
      2440 PRADR  JSR PRBYTE    PRINT BANK
      2450        LDA #"/"
      2460        JSR COUT
      2470 PRADR0
      2480        LDA 1,X      PRINT HI-BYTE OF ADDRESS
      2490        JSR PRBYTE
      2500        LDA 0,X      PRINT LO-BYTE OF ADDRESS
      2510        JSR PRBYTE
      2520        TYA          PRINT TRAILING CHAR
      2530        JMP COUT
      2540 *--------------------------------

Another project I thought of was modifying this search program so that it could run in an older Apple. Naturally we would lose the 3-byte addresses, so the program actually becomes simpler. A listing of this simpler version is shown below.

      1000 *SAVE S.SEARCH.II
      1010 *--------------------------------
      1020 YSAVE  .EQ $34
      1030 END    .EQ $3E,3F   ADDRESS OF END OF RANGE
      1040 PNTR   .EQ $40,41,42   START OF RANGE, CURRENT PNTR
      1050 *--------------------------------
      1060 WBUF   .EQ $200
      1070 KBUF   .EQ $280
      1080 MBUF   .EQ $2C0
      1090 *--------------------------------
      1100 CROUT  .EQ $FD8E
      1110 PRBYTE .EQ $FDDA
      1120 COUT   .EQ $FDED
      1130 *--------------------------------
      1140 T
      1150        LDA #SEARCH  Setup Control-Y vector
      1160        STA $3F9
      1170        LDA /SEARCH
      1180        STA $3FA
      1190        RTS
      1200 *--------------------------------
      1210 SEARCH
      1220        JSR GET.PATTERN   Crack the search pattern
      1230        BCS .99           ...nothing there or too long
      1240        STX WBUF          save # bytes in pattern
      1250 *---Compare pattern--------------
      1260 .1     LDY #-1
      1270 .2     INY
      1280        LDA (PNTR),Y      Byte from source
      1290        EOR KBUF,Y        Byte from pattern
      1300        AND MBUF,Y        Byte from mask
      1310        BNE .3            ...does not pass inspection
      1320        CPY WBUF          At end of pattern yet?
      1330        BCC .2            ...no
      1340 *---Found a match----------------
      1350        LDA PNTR+1
      1360        JSR PRBYTE
      1370        LDA PNTR
      1380        JSR PRBYTE
      1390        LDA #" "
      1400        JSR COUT
      1410 *---Slip to next postion---------
      1420 .3     LDA PNTR     Set carry if at end of range
      1430        CMP END
      1440        LDA PNTR+1
      1450        SBC END+1
      1460        INC PNTR     Bump comparison pointer
      1470        BNE .4
      1480        INC PNTR+1
      1490 .4     BCC .1
      1500 *--------------------------------
      1510 .99    RTS
      1520 *--------------------------------
      1530 GET.PATTERN
      1540        LDY YSAVE
      1550        LDX #-1
      1560 .1     INX
      1570        LDA #$FF     Assume mask =$FF
      1580 .2     STA MBUF,X   Store a mask value
      1590        LDA #$00     Start Keybyte = $00
      1600 .3     STA KBUF,X
      1610        CPY #$7F
      1620        BCS .7       ...LINE TOO LONG
      1630        JSR NXTCHR   GET UPPER-CASE CHAR AT WBUF,Y
      1640        CMP #" "     Is it a space?
      1650        BEQ .1       ...yes, next byte
      1660        CMP #":"
      1670        BCC .5       < $BA, must be 0...9
      1680        BNE .4       > $BA, must be A...F
      1690        LDA KBUF,X   = $BA, ":", means to put
      1700        BCS .2       ...ALWAYS   value into MASK
      1710 *---Try letter A...F-------------
      1720 .4     CMP #"G"
      1730        BCS .7       ...ERROR
      1740        CMP #"A"
      1750        BCC .7       ...ERROR
      1760        SBC #7       "A"..."F"
      1770 *---Try hex digit range----------
      1780 .5     CMP #"0"     Is it 0...9 or A...F?
      1790        BCC .6       ...no
      1800        AND #$0F     ...yes, isolate digit
      1810        ASL KBUF,X   Shift previous value
      1820        ASL KBUF,X
      1830        ASL KBUF,X
      1840        ASL KBUF,X
      1850        ORA KBUF,X   Merge with digit
      1860        JMP .3
      1870 *---End of line or error---------
      1880 .6     CMP #$8D
      1890        BNE .7       ...ERROR
      1900        DEY
      1910        STY YSAVE
      1920        CLC
      1930        RTS
      1940 *---Bad char or line too long----
      1950 .7     STY YSAVE
      1960        LDA #$8D     STORE <RETURN> IN MONITOR
      1970        STA WBUF+1,Y      INPUT BUFFER
      1980        LDA #$87     RING THE BELL
      1990        JSR COUT
      2000        SEC          INFORM OF ERROR
      2010        RTS
      2020 *--------------------------------
      2030 NXTCHR LDA WBUF,Y
      2040        INY
      2050        CMP #$E1
      2060        BCC .1
      2070        CMP #$FB
      2080        BCS .1
      2090        AND #$DF
      2100 .1     RTS
      2110 *--------------------------------

Reading the IIgs ROMs | Bob Sander-Cederlof

A few days ago a friend and I were burning some EPROMs using the SCRG PromGramer in my IIgs. The friend said, "Why don't we use this card to read the IIgs ROM?" "We could put the PromGramer in another machine, and then put the IIgs ROMs in it and read them."

Well, I don't really know why he wanted to do this. He has his own IIgs already anyway. Nevertheless, I can think of reasons to want to capture all of the ROM info on disk files. Disassembly, for one, using the S-C DisAssembler. Another reason would be to be able to compare different machines to see if they have the same ROM contents. But do we have to take out the chips to read them?

No. The following simple commands, issued from inside the S-C Macro Assembler running in your IIgs, will write the entire ROM contents to a series of eight files. It works under either ProDOS or DOS 3.3:

         $00/1000<FE/0000.3FFFM
         BSAVE ROM.FE.0,A$1000,L$4000
         $00/1000<FE/4000.7FFFM
         BSAVE ROM.FE.4,A$1000,L$4000
         $00/1000<FE/8000.BFFFM
         BSAVE ROM.FE.8,A$1000,L$4000
         $00/1000<FE/C000.FFFFM
         BSAVE ROM.FE.C,A$1000,L$4000
         $00/1000<FF/0000.3FFFM
         BSAVE ROM.FF.0,A$1000,L$4000
         $00/1000<FF/4000.7FFFM
         BSAVE ROM.FF.4,A$1000,L$4000
         $00/1000<FF/8000.BFFFM
         BSAVE ROM.FF.8,A$1000,L$4000
         $00/1000<FF/C000.FFFFM
         BSAVE ROM.FF.C,A$1000,L$4000

I found it convenient to put those commands in a text file, which I named GET.ROMS. Then, whenever I want to use it, I can just type EXEC GET.ROMS. I took my disk down to the local Heathkit store around 10:45 this morning and came back with a copy of their ROM.

It strikes me just now that I am being inconsistent. Sometimes I am saying ROM, and sometimes ROMs. There is a reason....

Real IIgs machines come with one system ROM chip, which is a 1 megabit ROM. It holds all 128K bytes, in only one 28-pin package. I presume it is uses pinouts like a 27512 EPROM, but selects one half or the other of the 1024K bits using what would normally be the programming pin on the 27512. My IIgs, on the other hand, is a prototype machine. It has an adapter plugged into the single ROM socket, which holds four 27256 EPROM chips and one 74F139. They call it the "airplane ROMs". I like it, because if I want to I can easily burn a new version of the firmware and plug it in. Some enterprising soul might find a market for a board like this.

More About Patching Apple's ProDOS Releases | Bob Sander-Cederlof

You remember in our March issue we talked about patches to fix Version 1.3 of ProDOS. Apple has pulled this version off the market, but there are still a lot of copies floating around. The patches we gave in the March article should make ProDOS 1.3 as good as any other version, but who knows?

Anyway, we heard through the grapevine that some unofficial copies of version 1.4 are out, and that a brand new bug has surfaced in this one. It seems someone put a "LDA $C09C,X" where "LDA $C08E,X" should be.

I ran across a listing in the Washington Apple Pi newsletter (May 1987 issue, page 16) of an Applesoft program which can install all known necessary patches in versions 1.1.1, 1.2, 1.3, and 1.4 of ProDOS. The program was originally written by Stephen Thomas to fix version 1.1.1, when the problem of the four STA's to the stepper motor soft-switches was discovered. (See Nov 86 AAL) Later Glen Bredon modified it to make the corresponding patches to later versions, as well as to fix the additional new bugs. I have further modified it, in an attempt to make it easier to understand.

         100  TEXT : HOME :E = 0: PRINT "PRODOS PATCH PROGRAM"
         110  IF  PEEK (116) < 128 THEN E = 1: GOTO 900: REM ENUF MEM?
         120  ONERR  GOTO 900
         130  REM ---READ PRODOS FILE---
         140  PRINT  CHR$ (4)"UNLOCK PRODOS"
         150  PRINT  CHR$ (4)"BLOAD PRODOS,TSYS,A$2000"

         200  REM ---SEARCH $4000-$60FF FOR PATTERN---
         210 V = 1: FOR B = 64 TO 96:A = B * 256
         220  IF  PEEK (A + 4) <  > 189 THEN 250
         230  IF  PEEK (A + 5) <  > 156 THEN 290
         240  IF  PEEK (A + 6) = 192 THEN V = 3:B = 96: GOTO 290: REM VERSION 1.4
         250  IF  PEEK (A + 4) <  > 234 THEN 290
         260  IF  PEEK (A + 5) <  > 234 OR  PEEK (A + 6) <  > 234 THEN 290
         270  IF  PEEK (A + 7) <  > 234 OR  PEEK (A + 8) <  > 234 THEN 290
         280 V = 2:B = 96: REM VERSION BEFORE 1.4
         290  NEXT B:E = 2: ON V GOTO 900,300,700

         300  REM ---FOUND VERSION BEFORE 1.4---
         310  POKE A + 4,189: POKE A + 5,142: POKE A + 6,192: REM "LDA $C08E,X"

         400  REM ---LOOK FOR OTHER PATCH AREA---
         410 A =  PEEK (A + 2) + 256 *  PEEK (A + 3) - 13 * 4096 + A + 5
         420 E = 3: IF A < 4 * 4096 OR A > 6 * 4096 THEN 900
         430  IF  PEEK (A) <  > 157 OR  PEEK (A + 3) <  > 157 THEN 500
         440  IF  PEEK (A + 6) <  > 157 OR  PEEK (A + 9) <  > 157 THEN 500

         450  REM ---FOUND VERSION 1.1.1 OR 1.2, SO CHANGE "STA" TO "LDA"---
         460  FOR I = 0 TO 9 STEP 3: POKE A + I,189: NEXT I
         470 V$ = "1.1.1": GOTO 800

         500  REM  ---VERSION 1.3---
         510  FOR I = 0 TO 12: READ B: IF  PEEK (A + I) <  > B THEN  GOTO 900
         520  NEXT I: DATA 160,8,189,128,192,232,232,136,208,248,234,234,96
         530  FOR I = 0 TO 11: READ B: POKE A + I,B: NEXT I
         540  DATA 189,128,192, 189,130,192, 189,132,192, 189,134,192
         550 A = 4 * 4096 + 12 * 256 + 12 * 16 + 13: REM  ADDRESS = $4CCD
         560  FOR I = 0 TO 3: READ B: IF  PEEK (A + I) <  > B THEN 900
         570  NEXT I: POKE A,240: REM CHANGE "BRA" TO "BEQ"
         580 V$ = "1.3": GOTO 800
         590  DATA 128,6,190,0

         700  REM ---VERSION 1.4---
         710  POKE A + 5,142: REM "LDA $C09C,X" TO "LDA $C08E,X"
         720 V$ = "1.4"

         800  REM ---WRITE PATCHED VERSION ON DISK---
         810  PRINT  CHR$ (4)"BSAVE PRODOS,TSYS,A$2000"
         820  PRINT  CHR$ (4)"LOCK PRODOS"
         830  PRINT "PATCHES COMPLETED TO VERSION "V$: END

         900  REM ---ERROR HANDLER---
         910  PRINT  CHR$ (7)"ERROR!  NO PATCHES WERE MADE."
         915  ON E GOTO 930,940,950
         920  PRINT "PRODOS FILE NOT FOUND.": END
         930  PRINT "NOT ENOUUGH ROOM TO LOAD PRODOS.": END
         940  PRINT "PATCH LOCATION NOT FOUND.": END
         950  PRINT "PRODOS FILE MAY HAVE BEEN PATCHED,"
         960  PRINT "ALREADY, OR IS NOT A COMPATIBLE VERSION."
         970  END

Lines 100-150 read the ProDOS system file into memory. Then Lines 200-290 search every page from $4000 through $60FF for either five NOPs starting at $xx04 or a "LDA $C09C,X" instruction at $xx04. If neither is found, nothing is patched. If the five NOPs are found, we have version 1.1.1, 1.2, or 1.3. If the LDA is found, we have version 1.4. If it is version 1.4, the only patch needed is to change it to "LDA $C08E,X", which is done at lines 700-720.

Older versions all need "LDA $C08E,X" poked where the five NOPs were, so line 310 takes care of this. Then we look at the address in the operand field of the instruction just prior to the five NOPs. This is a JSR to a little subroutine which we need to modify. Line 410 computes the location within the system file image for the twelve bytes we need to change.

There are several possible versions of this subroutine. If it is a series of "STA $C08x,X" instructions, we have version 1.1.1 or 1.2 and the STA opcodes should be changed to LDA opcodes. Lines 430 and 440 test for STA opcodes, and lines 450-470 make the changes. On the other hand, if the subroutine is like Apple put in version 1.3 we will replace it with a series of four LDAs just like we made in the older versions. Lines 500-590 handle this, and also change an errant "BRA" opcode to a "BEQ" opcode.

Finally, lines 800-830 write out the modified code and re-LOCK the file. I would be careful to check the changes made before doing this to every copy I own, if I were you. And bear in mind that Apple as a company has never authorized any of these changes. (They have only made them necessary, by their own incorrect changes.)

While this article was waiting for the press, Apple finally sent out correct copies of version 1.4. I received my master copy June 1st, and checked it against our patched version 1.3. They were identical except for the copyright dates and version numbers. The official date on this GOOD version 1.4 is April 17, 1987.

ABC*DE=FG*HI Puzzle Solved | Bob Sander-Cederlof

A+ Magazine has an interesting puzzle each month, with a challenge to solve it with your Apple and possibly win a prize. The June 1987 puzzle (see page 110, "Computer Calisthenics" by Michael Wiesenberg) involves writing a program to find all possible solutions to the equation ABC*DE=FG*HI, where each letter represents a different digit between 1 and 9. All nine digits are different.

I wrote a quick-and-dirty program in Applesoft, and then refined it a little for speed. Maybe speed isn't the correct work, because even the refined result takes nearly six minutes to find the eleven solutions. The first solution my program found was the same as the one example solution given in the A+ article: 158*23 = 46*79.

Puzzles of this type can be solved, if you have enough time, by trying all possible combinations. A series of nested FOR loops, one for each digit, will produce all possible nine-digit arrangements. Appropriate tests can weed out all values which have more than one letter sharing the same digit. Inside all the loops you test the letter assignments against the puzzle equation, and print the solution if it passes.

This will take way too long, even on a computer, so you start looking for ways to eliminate some combinations. The first thing I noticed is that I can eliminate symmetric solutions by forcing HI>FG. This means H>F, so I can run my FOR loop for H from F+1 to 9, instead of from 1 to 9.

The smallest possible value for abc*de is 245*13=3185. This means F cannot be 1 or 2, because even 29*99 is too small. A little more examination shows that FG must be at least 34, so I put this into my program.

Neither C nor E can be 5, because this would force either G or I to also be 5. I also notice that you cannot have a 1 on the right-hand side of the puzzle equation. If there were a 1 there, the largest possible FG*HI would be 91*87 = 7917. This is still smaller than the smallest possible left-hand side without a 1 (356*24 = 8544), so it cannot work.

Similar analysis shows that D cannot be 8 or 9. The largest possible FG*HI is 87*96 = 8352. If D is 8, the smallest ABC*DE is 134*82 = 10988; D=9 is even worse. This also cannot work, so I limit my D-loop to values from 1 to 7.

I also refined the program in the area of testing whether a digit has already been used by a previous letter. I maintain an array of flags. At the top of each loop I test the flag array entry to see if the digit has already been used. If not, I mark it used and continue. If it is already in use, I skip around all the inner loops to the corresponding NEXT. Here is the Applesoft version of my program:

         10  DIM N(9): FOR I = 1 TO 9:N(I) = I: NEXT :EPS = .0001
         100  FOR F = 3 TO 8:N(F) = 0
         110  FOR GX = 2 TO 9:G = N(GX): IF G = 5 OR G = 0 THEN 320
         115 FG = F * 10 + G: IF FG < 34 THEN 320
         120 N(GX) = 0: FOR HX = F + 1 TO 9:H = N(HX): IF H = 0 THEN 310
         130 N(HX) = 0: FOR IX = 2 TO 9:I = N(IX): IF I = 0 OR I = 5 THEN 300
         140 HI = H * 10 + I:P = FG * HI: IF P < 3240 THEN 300
         150 N(IX) = 0: FOR DX = 1 TO 8:D = N(DX): IF D = 0 THEN 290
         160 N(DX) = 0: FOR EX = 1 TO 9:E = N(EX): IF E = 0 OR E = 5 THEN 280
         170 DE = D * 10 + E:Q = P / DE: IF Q < 123 OR Q <  >  INT (Q + EPS) THEN 280
         180 N(EX) = 0:A =  INT (Q / 100 + EPS): IF N(A) = 0 THEN 270
         190 N(A) = 0:B =  INT (Q / 10 - A * 10 + EPS): IF N(B) = 0 THEN 260
         200 N(B) = 0:C =  INT (Q - A * 100 - B * 10 + EPS): IF N(C) = 0 THEN 250
         210 N = N + 1: PRINT  SPC( N < 10);N":  "FG" X "HI" = "DE" X "Q" = "P
         250 N(B) = B
         260 N(A) = A
         270 N(EX) = EX
         280  NEXT EX:N(DX) = DX
         290  NEXT DX:N(IX) = IX
         300  NEXT IX:N(HX) = HX
         310  NEXT HX:N(GX) = GX
         320  NEXT GX:N(F) = F: NEXT F

I wanted to see how hard it would be to re-write the above program in assembly language, and if so how much speedier it would be. I am not proud that it took me over four hours to perfect the assembly language version. But the result is nice. It executes in less than seven seconds! This does not necessarily argue well for assembly language programming, if all I care about is the answers to the puzzle. But if I view it as an example, and consider that the same speedup may be possible in much larger and much more frequently used programs, it does make assembly language look good. That is why programs like the S-C Macro Assembler and even the Applesoft firmware itself are written in assembly language.

I used the same overall approach in assembly language version. Lines 1040-1090 initialize an array of flags I use to quickly check whether a digit is already in use. The flag values are either 0 or 1: 0 means a digit is in use, and 1 means it is available. The array is accessed by using the digit value for an index. I can both test a flag and change it to zero with one instruction: LSR FLAGS,X will shift bit 0 into carry. If the flag was 0, it still is and carry is clear. If the flag was 1 it changes to 0 and carry is set.

Lines 1110-1520 are a equivalent of FOR statements, starting up a series of six nested loops to generate values for D, E, F, G, H, and I. Lines 1580-1880 are the equivalent to the NEXT statements. All the flag array handling is also included in these two groups of lines. I put the prior knowledge about the ranges of possible values for these six letters into the FOR-loop values, and eliminate the value 5 for letters E, G, and I.

Lines 1540-1560 call subroutines to test the choices for letters D through I and print the resulting equation if it is a valid solution to the puzzle. Both of these subroutines use some really interesting techniques.

The PRINT subroutine (lines 2820-3060) is controlled by a format string in lines 3080-3160. This string has two kinds of bytes: index values between 0 and 8, and ASCII characters between 80 and FF. The index values point to the table of digits A through I. The print loop, lines 2840-2910, only needs four extra lines to pick up digits out of the digit table and convert them to ASCII. Line 2850 branches directly to the JSR COUT in line 2890 if the format byte is an ASCII character already. Otherwise, lines 2860-2880 use the format byte as an index to pick up the digit and merge $B0 with it to convert it to ASCII. This is not only neat, it is also short and fast. Lines 2940-3050 allow you to pause and abort the program by typing any key to pause, <RETURN> to abort.

The COMPUTE.ABC subroutine (lines 1920-2710) checks the chosen values for D through I to see if they are a valid solution to the puzzle. If they are, values for A, B, and C will be chosen in the process of the check. Then the subroutine returns with carry clear to indicate a valid solution. If the solution is not valid, the subroutine will return with carry set.

Lines 1930-2010 call on CALC.XX (lines 2730-2800) to calculate binary values for DE, FG, and HI. I could have added a test here to be sure that DE>33, but it did not seem to be worth the effort. I didn't try it, though, so I might be wrong. CALC.XX mutiplies the first of each pair of digits by ten, and then adds the second.

Lines 2020-2160 compute WXYZ = FG*HI. The highest possible value for FG will be 79 and for HI will be 98. Since we are limiting F to the range 1 through 7, the largest possible combination will be 78*96 = 7488. This is $1D40. The calculation is a simple 8-bit by 8-bit multiplication, with a 16-bit result.

Lines 2170-2330 compute ABC = (FG*HI)/DE. I could have used two or three more nested loops to pick values for A, B, and C, but "it seemed like a good idea at the time" to do it this way. Now I think two more loops to pick A and B using the same techniques as for the other letters, plus a simple search through the flags to pick C, would be nicer. You might try it that way and compare the speeds of the two approaches. My calculation is a simple division with a 16-bit dividend, 8-bit divisor, 16-bit quotient, and 8-bit remainder. If the remainder is non-zero, then the numbers picked do not form a valid solution.

Lines 2340-2460 determine the value of A by essentially dividing ABC by 100. ABC is a 16-bit value, so the subtraction loop has to do a 16-bit subtraction. The loop subtracts one extra time, so line 2440 corrects the remainder (which will be the value BC). Lines 2450-2460 check to see if the value for A was already used for D through I.

Lines 2480-2580 do a similar operation to separate out the value of B, and by default leave the value of C in the A-register. Lines 2540-2570 check to see if the value for B was already used for D through I or for A. Finally, lines 2590-2660 check the value for C against all the other choices.

As a final product, a program like this has little value (unless you win the contest!). However, it can be a great tool for perfecting your skill as a programmer. It is also a pleasant and harmless form of recreation. If you enjoy articles like these, let us know: we may do some more. Or, how about sending us one yourself?

      1000 *SAVE S.PUZZLE
      1010 *--------------------------------
      1020 COUT   .EQ $FDED
      1030 *--------------------------------
      1040 T
      1050        LDX #9
      1060        LDA #1       STORE 1 IN EACH FLAG
      1070 .1     STA FLAGS,X
      1080        DEX
      1090        BNE .1
      1100 *--------------------------------
      1110        LDX #2       FOR F=3 TO 8
      1120 LOOP.F INX
      1130        STX F
      1140        LSR FLAGS,X  mark digit in use
      1150 *--------------------------------
      1160        CPX #3       IF F=3, START G-LOOP AT 4
      1170        BEQ LOOP.G
      1180        LDX #1       FOR G=2 TO 9
      1190 LOOP.G INX
      1200        LSR FLAGS,X
      1210        BCC NEXT.G   ...DIGIT ALREADY USED
      1220        STX G
      1230        CPX #5
      1240        BEQ FIX.G    G can't be 5
      1250 *--------------------------------
      1260        LDX F        FOR H=F+1 TO 9
      1270 LOOP.H INX
      1280        LSR FLAGS,X
      1290        BCC NEXT.H   ...DIGIT ALREADY USED
      1300        STX H
      1310 *--------------------------------
      1320        LDX #1       FOR I=2 TO 9
      1330 LOOP.I INX
      1340        LSR FLAGS,X
      1350        BCC NEXT.I   ...DIGIT ALREADY USED
      1360        STX I
      1370        CPX #5
      1380        BEQ FIX.I    I can't be 5
      1390 *--------------------------------
      1400        LDX #0       FOR D=1 TO 7
      1410 LOOP.D INX
      1420        LSR FLAGS,X
      1430        BCC NEXT.D   ...DIGIT ALREADY USED
      1440        STX D
      1450 *--------------------------------
      1460        LDX #0       FOR E=1 TO 9
      1470 LOOP.E INX
      1480        LSR FLAGS,X
      1490        BCC NEXT.E   ...DIGIT ALREADY USED
      1500        STX E
      1510        CPX #5
      1520        BEQ FIX.E    E can't be 5
      1530 *--------------------------------
      1540        JSR COMPUTE.ABC
      1550        BCS FIX.E    ...NOT AN ANSWER
      1560        JSR PRINT
      1570 *--------------------------------
      1580 FIX.E  LDX E        NEXT E (1...9)
      1590        INC FLAGS,X
      1600 NEXT.E CPX #9
      1610        BCC LOOP.E
      1620 *--------------------------------
      1630 FIX.D  LDX D        NEXT D (1...7)
      1640        INC FLAGS,X
      1650 NEXT.D CPX #7
      1660        BCC LOOP.D
      1670 *--------------------------------
      1680 FIX.I  LDX I        NEXT I (2...9)
      1690        INC FLAGS,X
      1700 NEXT.I CPX #9
      1710        BCC LOOP.I
      1720 *--------------------------------
      1730 FIX.H  LDX H        NEXT H (F+1...9)
      1740        INC FLAGS,X
      1750 NEXT.H CPX #9
      1760        BCC LOOP.H
      1770 *--------------------------------
      1780 FIX.G  LDX G        NEXT G (2...9)
      1790        INC FLAGS,X
      1800 NEXT.G CPX #9
      1810        BCS FIX.F
      1820        JMP LOOP.G
      1830 *--------------------------------
      1840 FIX.F  LDX F        NEXT F (3...8)
      1850        INC FLAGS,X
      1860 NEXT.F CPX #8
      1870        BCS END
      1880        JMP LOOP.F
      1890 *--------------------------------
      1900 END    RTS          END OF PROGRAM
      1910 *--------------------------------
      1920 COMPUTE.ABC
      1930        LDX #D-DIGITS     DE = D*10+E
      1940        JSR CALC.XX
      1950        STA DE
      1960        LDX #F-DIGITS     FG = F*10+G
      1970        JSR CALC.XX
      1980        STA FG
      1990        LDX #H-DIGITS     HI = H*10+I
      2000        JSR CALC.XX
      2010        STA HI
      2020 *---WXYZ = FG * HI---------------
      2030        LDY #8       multiply 8-bits by 8-bits
      2040        LDA #0
      2050 .1     LSR FG       get next bit of multiplier
      2060        BCC .2       bit = 0
      2070        CLC          bit = 1
      2080        ADC HI       Add multiplicand
      2090 .2     ROR          Shift product hi-byte
      2100        ROR WXYZ           product lo-byte
      2110        DEY          Next bit
      2120        BNE .1       ...more to go
      2130        STA WXYZ+1   store hi-byte of product
      2140        STA ABC+1
      2150        LDA WXYZ
      2160        STA ABC
      2170 *---ABC = WXYZ / DE--------------
      2180        LDY #16
      2190        LDA #0
      2200        CLC
      2210 .3     ROL ABC
      2220        ROL ABC+1
      2230        ROL
      2240        CMP DE
      2250        BCC .4
      2260        SBC DE
      2270 .4     DEY
      2280        BNE .3
      2290        ROL ABC      ...final bit into quotient
      2300        ROL ABC+1
      2310        STA REM
      2320        CMP #1       If any remainder, not a valid answer
      2330        BCS .9
      2340 *---Check digits of ABC----------
      2350        LDX #-1
      2360        LDA ABC
      2370        LDY ABC+1
      2380 .45    SEC
      2390 .5     INX          COUNT 100'S
      2400        SBC #100
      2410        BCS .5
      2420        DEY
      2430        BPL .45
      2440        ADC #100     CORRECT FOR OVER-SUBTRACTION
      2450        LDY FLAGS,X  SEE IF DIGIT ALREADY USED
      2460        BEQ .9       ...YES, NO NEED TO LOOK FURTHER
      2470        STX A
      2480        LDX #-1
      2490        SEC
      2500 .6     INX          COUNT 10'S
      2510        SBC #10
      2520        BCS .6
      2530        ADC #10      CORRECT FOR OVER-SUBTRACTION
      2540        LDY FLAGS,X  SEE IF DIGIT ALREADY USED
      2550        BEQ .9       ...YES, NO NEED TO LOOK FURTHER
      2560        CPX A
      2570        BEQ .9
      2580        STX B
      2590        TAX
      2600        LDY FLAGS,X  SEE IF DIGIT ALREADY USED
      2610        BEQ .9       ...YES, NOT A SOLUTION
      2620        CPX A
      2630        BEQ .9
      2640        CPX B
      2650        BEQ .9
      2660        STX C        ...NO, WE HAVE A SOLUTION
      2670 *--------------------------------
      2680        CLC
      2690        RTS
      2700 .9     SEC
      2710        RTS
      2720 *--------------------------------
      2730 CALC.XX
      2740        LDA DIGITS,X
      2750        ASL
      2760        ASL
      2770        ADC DIGITS,X
      2780        ASL
      2790        ADC DIGITS+1,X
      2800        RTS
      2810 *--------------------------------
      2820 PRINT
      2830        LDY #0
      2840 .1     LDA FORMAT,Y
      2850        BMI .2
      2860        TAX
      2870        LDA DIGITS,X
      2880        ORA #"0"
      2890 .2     JSR COUT
      2900        INY
      2910        CPY #FMT.SZ
      2920        BCC .1
      2930 *--------------------------------
      2940        LDA $C000
      2950        BPL .5
      2960        STA $C010
      2970        CMP #$8D
      2980        BEQ .4
      2990 .3     LDA $C000
      3000        BPL .3
      3010        STA $C010
      3020        CMP #$8D
      3030        BNE .5
      3040 .4     PLA          POP RETURN, SO RTS QUITS
      3050        PLA
      3060 .5     RTS
      3070 *--------------------------------
      3080 FORMAT .HS 00.01.02      ABC
      3090        .AS -" X "        times
      3100        .HS 03.04         DE
      3110        .AS -" = "
      3120        .HS 05.06         FG
      3130        .AS -" X "        times
      3140        .HS 07.08         HI
      3150        .HS 8D
      3160 FMT.SZ .EQ *-FORMAT
      3170 *--------------------------------
      3180 DIGITS
      3190 A      .BS 1
      3200 B      .BS 1
      3210 C      .BS 1
      3220 D      .BS 1
      3230 E      .BS 1
      3240 F      .BS 1
      3250 G      .BS 1
      3260 H      .BS 1
      3270 I      .BS 1
      3280 *--------------------------------
      3290 DE     .BS 1
      3300 FG     .BS 1
      3310 HI     .BS 1
      3320 WXYZ   .BS 2
      3330 ABC    .BS 2
      3340 REM    .BS 1
      3350 *--------------------------------
      3360 FLAGS  .BS 10
      3370 *--------------------------------

Problem with IIgs 3.5"-Drive Firmware | Bob Sander-Cederlof

The firmware in the IIgs for the 3.5"-drive includes the so-called "Protocol Converter" interface. We discussed this interface a little in previous issues, especially in the May 1986 article showing how to make DOS 3.3 work with the little drives.

Last month we reported a fix you need to make to our DOS 3.3 patches if you want it to work on a IIgs. Then last week I found out about another IIgs-related problem.

The 3.5"-drive firmware in the IIgs, when you call it through the Protocol Converter interface, stomps on four pagezero locations which are used by other programs. Locations $57, 58, 59, and 5A all are used without any concern for how they might already be in use. The firmware makes a great effort to save and restore all sorts of other locations, but these it just walks over, kicking sand like the big bully at the beach.

We first noticed the problem when using the S-C Macro Assembler with our 3.5" version of DOS 3.3. After any commands using the 3.5" drive, such as LOAD, SAVE, CATALOG, or whatever, the line number INCREMENT would be cleared to zero. That value is kept by S-C Macro in $5A, which the 3.5" firmware zeroes. This means after any disk operation you need to type INC 10, or INC with whatever increment you want to use. Luckily, the other three bytes are not actively in use during a disk operation.

In Applesoft these locations hold temporary string descriptors. I think they are only used while executing one statement, so it is possibly all right to share them with the firmware.

Apple probably is not going to do anything about this problem, because supposedly only NEW software would be coming in through the Protocol Converter interface. The ProDOS interface does not have any problem, because it either does not use those locations or it saves-restores them properly.

I think we probably are going to have to be the ones to change. Either we have to add code to our Unidisk DOS 3.3 to save and restore those four bytes, or we have to change the patch to use the ProDOS interface for reading and writing blocks rather than the Protocol Converter. The trouble with the latter approach is that the ProDOS interface requires the use of locations $42-$47, and four of these are already in use by higher levels of DOS 3.3! It is hard to win at this game. Either way we end up needing to save and restore four bytes.

Until we decide one way or the other, we need to at least come up with a fix for our Unidisk DOS 3.3 that will allow you to use it with the S-C Macro Assembler without losing yur INCrement every time you turn around. Of the four clobbered bytes, only $5A is critical to the assembler. I found a way to save enough bytes in Bill's code to slip this in. It had to be "slipped" in, so that the references to the DOS patches made in Bill's boot program do not have to be changed.

Lines 1970-2020 in Bill's Unidisk driver were:

           1970    sta block
           1980    ldy #5
           1990    lda (iob.ptr),y
           2000    lsr
           2010    ora block
           2020    sta block

Replace those lines with the following, which take the same space:

           1970    asl
           1980    ldy #5
           1990    ora (iob.ptr),y
           2000    ror
           2010    sta block
           2012    lda $5a
           2014    pha
           2016    nop
           2020    nop

This performs the same function as the original code, but adds the feature of saving location $5A on the stack. At the exit we need to restore $5A from the stack, so look at lines 3100-3110 of Bill's program. They were:

           3100    sta (iob.ptr),y
           3110    rts

Change them to:

           3100    jmp bobs.patch

Then insert new code as follows:

           3202 bobs.patch
           3203    sta (iob.ptr),y
           3204    pla
           3205    sta $5a
           3206    rts

I entered the above patches by using the monitor peeking and poking commands, and they do seem to work correctly. Here are the commands I used:

           00/BF8E:91 48 68 85 5A 60
           00/BF82:4C 8E BF
           00/BED6:0A A0 05 11 48 6A 8D 89 BF A5 5A 48 EA EA

You could add these commands to the EXEC file which loads the IIgs version of the S-C Macro Assembler.

In going over Bill's code while preparing this article, I also noticed that his line 1860, "STA LAST.BLOCK", should be "STA LAST.BLOCK+1". You probably should make this change regardless of what kind of computer you are using.

Here is the complete program with all the changes in place:

      1000 *SAVE S.UNIDISK RWTS
      1010 *--------------------------------
      1020 UNIDISK.SLOT        .EQ 5
      1030
      1040 MY.COMMAND          .EQ $26
      1050 MY.BUFFER.POINTER   .EQ $3C
      1060 IOB.BUFFER.POINTER  .EQ $3E
      1070 IOB.PTR             .EQ $48
      1080
      1090 MY.BUFFER           .EQ $BB00
      1100
      1110 PATCH.POINT         .EQ $BD12
      1120 PATCH.RETURN        .EQ $BD15
      1130
      1140 PC.DISPATCH         .EQ UNIDISK.SLOT*$100+$C000
      1150
      1160 PRBYTE              .EQ $FDDA
      1170 COUT                .EQ $FDED
      1180 *--------------------------------
      1190        .OR $803
      1200        .TF RWTS 3.5
      1210
      1220 INSTALL
      1230        LDX #6            make sure we have a
      1240 .1     LDA ID.TABLE,X    protocol converter
      1250        CMP UNIDISK.SLOT*$100+$C001,X
      1260        BNE NO.PC
      1270        DEX
      1280        DEX
      1290        BPL .1
      1300
      1310        LDA #$4C          patch in the JMP
      1320        STA PATCH.POINT   to our code
      1330        LDA #MY.RWTS
      1340        STA PATCH.POINT+1
      1350        LDA /MY.RWTS
      1360        STA PATCH.POINT+2
      1370        LDA #$60
      1380        STA $A54F         disable INIT
      1390
      1400 MOVE   LDY #IMAGE.SIZE+1 install our code
      1410 .1     LDA IMAGE-1,Y
      1420        STA MY.RWTS-1,Y
      1430        DEY
      1440        BNE .1
      1450
      1460        CLC
      1470        LDA UNIDISK.SLOT*$100+$C0FF
      1480        ADC #3            find protocol
      1490        STA READ.CALL     converter entry
      1500        STA WRITE.CALL
      1510        BNE DONE          ...always
      1520
      1530 NO.PC  LDX #0
      1540 .1     LDA MESSAGES,X    print an error message
      1550        BEQ DONE
      1560        JSR COUT
      1570        INX
      1580        BNE .1
      1590 DONE   JMP $3D0
      1600 *--------------------------------
      1610 MESSAGES
      1620        .HS 8D
      1630        .AS -/No PC in slot /
      1640        .DA #$B0+UNIDISK.SLOT
      1650        .HS 878D00
      1660 *--------------------------------
      1670 ID.TABLE .HS 20.FF.00.FF.03.FF.00
      1680 *            ^     ^     ^     ^
      1690 *        Protocol Converter ID Bytes
      1700 *--------------------------------
      1710 IMAGE  .EQ *
      1720        .PH $BEAF
      1730 MY.RWTS
      1740        CMP #UNIDISK.SLOT*$10
      1750        BEQ MINE          my call!
      1760        TAX               not mine, so do
      1770        LDY #$F           patched-over code
      1780        JMP PATCH.RETURN  and go back
      1790 *--------------------------------
      1800 MINE
      1810        LDY #$F
      1820        CMP (IOB.PTR),Y   check previous slot
      1830        BEQ SET.BLOCK     same, so go on
      1840        STA (IOB.PTR),Y   set previous slot
      1850        LDA #$FF
      1860        STA LAST.BLOCK+1  trash LAST.BLOCK
      1870
      1880 SET.BLOCK
      1890        LDA #0
      1900        STA BLOCK+1
      1910        LDY #4
      1920        LDA (IOB.PTR),Y   get track
      1930 .1     ASL
      1940        ROL BLOCK+1       *16
      1950        DEY
      1960        BNE .1
      1970        ASL
      1980        LDY #5
      1990        ORA (IOB.PTR),Y   get sector
      2000        ROR               /2, odd/even into carry
      2010        STA BLOCK
      2012        LDA $5A      SAVE $5A FOR IIGS
      2014        PHA
      2016        NOP
      2020        NOP
      2030
      2040 SET.POINTERS
      2050        LDA #MY.BUFFER
      2060        STA MY.BUFFER.POINTER
      2070        LDA /MY.BUFFER
      2080        ADC #0       carry sets hi/lo half of buffer
      2090        STA MY.BUFFER.POINTER+1
      2100        LDY #8
      2110        LDA (IOB.PTR),Y   get IOB buffer
      2120        STA IOB.BUFFER.POINTER
      2130        INY
      2140        LDA (IOB.PTR),Y
      2150        STA IOB.BUFFER.POINTER+1
      2160
      2170 SET.DRIVE
      2180        LDY #2
      2190        LDA (IOB.PTR),Y   get drive
      2200        LDY #$10
      2210        STA (IOB.PTR),Y   set previous drive
      2220        DEY
      2230        DEY
      2240        STA (IOB.PTR),Y   set previous volume
      2250        LSR
      2260        BCS SET.COMMAND   .CS. if D1
      2270        LDA BLOCK         add 800 to BLOCK if D2
      2280        ADC #800
      2290        STA BLOCK
      2300        LDA BLOCK+1
      2310        ADC /800
      2320        STA BLOCK+1
      2330
      2340 SET.COMMAND
      2350        LDY #$C
      2360        LDA (IOB.PTR),Y   get command
      2370        BEQ GOOD.EXIT
      2380        CMP #3            exit if not READ or WRITE
      2390        BCS GOOD.EXIT
      2400        STA MY.COMMAND    save command
      2410
      2420 CHECK.FOR.RE.READ
      2430        LDX #0            zero the flag
      2440        LDY #1            check two bytes
      2450 .1     LDA BLOCK,Y
      2460        CMP LAST.BLOCK,Y  compare
      2470        BEQ .2            same, so go on
      2480        INX               different, so flag it
      2490        STA LAST.BLOCK,Y  and store new value
      2500 .2     DEY
      2510        BPL .1            now do low bytes
      2520        TXA               check the flag
      2530        BNE READ          if different, go read
      2540
      2550 CHECK.FOR.VTOC
      2560        LDY #5
      2570        LDA (IOB.PTR),Y   get sector
      2580        BNE SKIP.READ     non-zero isn't VTOC
      2590        DEY
      2600        LDA (IOB.PTR),Y   get track
      2610        CMP #$11
      2620        BNE SKIP.READ     not $11 isn't VTOC
      2630
      2640 READ   JSR PC.DISPATCH
      2650 READ.CALL .EQ *-2
      2660        .DA #1            READ
      2670        .DA PARMLIST
      2680        BCS ERROR.EXIT
      2690
      2700 SKIP.READ
      2710        LDA MY.COMMAND    check command
      2720        CMP #2
      2730        BEQ WRITE.MOVE.BUFFER
      2740
      2750 READ.MOVE.BUFFER
      2760        LDY #0
      2770 .1     LDA (MY.BUFFER.POINTER),Y
      2780        STA (IOB.BUFFER.POINTER),Y
      2790        INY
      2800        BNE .1
      2810        BEQ GOOD.EXIT     ...always
      2820
      2830 WRITE.MOVE.BUFFER
      2840        LDY #0
      2850 .1     LDA (IOB.BUFFER.POINTER),Y
      2860        STA (MY.BUFFER.POINTER),Y
      2870        INY
      2880        BNE .1
      2890
      2900 WRITE  JSR PC.DISPATCH
      2910 WRITE.CALL .EQ *-2
      2920        .DA #2            WRITE
      2930        .DA PARMLIST
      2940        BCS ERROR.EXIT
      2950
      2960 GOOD.EXIT
      2970        CLC
      2980        LDA #0
      2990        BEQ EXIT          ...always
      3000
      3010 ERROR.EXIT
      3020        CMP #$2B     write protect?
      3030        BEQ .1
      3040        LDA #$40     make everything else DRIVE ERROR
      3050        .HS 2C
      3060 .1     LDA #$10
      3070        SEC
      3080
      3090 EXIT   LDY #$D
      3100        JMP BOBS.PATCH
      3110
      3120 *--------------------------------
      3130 PARMLIST
      3140        .DA #3        3 parameters
      3150        .DA #1        unit number
      3160        .DA MY.BUFFER buffer address
      3170 BLOCK  .BS 3         block number
      3180
      3190 LAST.BLOCK .HS FFFF
      3200 *--------------------------------
      3202 BOBS.PATCH
      3203        STA (IOB.PTR),Y
      3204        PLA          RESTORE $5A FOR IIGS
      3205        STA $5A
      3206        RTS
      3207 *--------------------------------
      3210        .BS $BF97-*
      3220        .EP
      3230 IMAGE.END .EQ *-1
      3240 IMAGE.SIZE .EQ IMAGE.END-IMAGE
      3250        .LIF

IIgs Tool Set Version Numbers | Bob Sander-Cederlof

According to Apple's published specifications, all IIgs tool sets are supposed to contain a function which returns a version number. Furthermore I think we can expect that the version numbers will be updated whenever any changes are made to the tools. Hopefully, programs which use the tools can first check to see if the version in ROM or RAM is up-to-date enough to be used.

It would be nice to be able to run a little program which would list the current version number for all installed tools. I wrote such a program, and it is shown below.

Since I am running under control of the S-C Macro Assembler Version 2.0, my program will receive control from the emulation mode. Therefore lines 1110-1130 get me into full native mode so that I can call tools. When I am finished, lines 1280-1290 put me back in emulation mode and return to the S-C Macro control.

The body of the program is a loop to call each of the tool sets from 1 to $20. Function number 4 of each tool is supposed to return the version number. This function requires one two-byte parameter space on the stack, in which to return the version number. Lines 1170-1190 set up the call and call a tool, and lines 1200-1220 store any error code returned as well as the version number. If the tool is not installed we will get an error code of 0002.

To display the results I wrote a little subroutine which runs in emulation mode. This is convenient, since all of our friendly monitor ROM I/O routines left over from the old days are only callable from emulation mode. There are probably some nice tools in the IIgs to take the place of COUT, PRBYTE, CROUT, and others, but these old friends are certainly easier to use and to remember.

My display routine prints an encoded string. Any bytes in the string which have the high bit on are printed as ASCII characters by calling COUT at $FDED. Any byte in value between $01 and $7F is treated as an index into page zero. The contents of the indexed location are printed out in hexadecimal by calling PRBYTE at $FDDA. A $00 byte in the string marks the end.

PRINT.FORMAT is called with the Y-register pointing just before the first character to be printed. This arrangement lets me break after printing the version number, and then just call PRINT.FORMAT again to print the error number when there is one.

Note at line 1370 I get the status byte I pushed at line 1320 by using a stack-relative LDA. Then I used LSR to shift bit 0, which was the carry bit, into C. Why didn't I use PLP followed by a PHP here? It would save one byte and have the same net effect, right?

Well I tried that, and it didn't work. The reason is that the PLP-PHP is inside my emulation mode subroutine. In emulation mode it is impossible to keep the status bits in the m- and x-bit positions in a zero state. Regardless of what they were when I pushed them at line 1320, after a PLP-PHP operation in emulation mode they will both be 1's. This undid the calling program, which believed it was still in full 16-bit mode. Zap! Pow! Wham! Beep! So I used the safer approach.

If you want to adapt the display subroutine for other purposes and use it in an older Apple that doesn't have a 65816 in it, you can go back to the PLP-PHP method. You will also need to change the BRA opcodes at lines 1540 and 1560 to JMP's.

      1000 *SAVE S.TOOL.VERSIONS
      1010 *--------------------------------
      1020        .OP 65816
      1030 *--------------------------------
      1040 TOOL    .EQ $01,02
      1050 VERSION .EQ $03,04
      1060 ERROR   .EQ $05,06
      1070 *--------------------------------
      1080 *   Program to display Version Numbers of All Tools
      1090 *--------------------------------
      1100 DVN
      1110        CLC
      1120        XCE
      1130        REP #$30     "Full" Native mode
      1140        LDA ##$0401  For TOOL = $0401 to $0420
      1150        STA TOOL
      1160 *--------------------------------
      1170 .1     PEA 0        Make room for version number
      1180        LDX TOOL     Tool # and function
      1190        JSR $E10000  Go get it!
      1200        STA ERROR    Any error code
      1210        PLA          Tool version #
      1220        STA VERSION
      1230        JSR DISPLAY.RESULTS
      1240        INC TOOL     Next tool set
      1250        LDA TOOL     ...only go so far...
      1260        CMP ##$0421
      1270        BCC .1       ...not there yet
      1280        XCE          ...that is far enough
      1290        RTS
      1300 *--------------------------------
      1310 DISPLAY.RESULTS
      1320        PHP
      1330        SEC
      1340        XCE
      1350        LDY #-1
      1360        JSR PRINT.FORMAT
      1370        LDA 1,S      GET STATUS BYTE
      1380        LSR          TO SEE IF THERE WAS AN ERROR
      1390        BCC .1       ...NO ERROR
      1400        JSR PRINT.FORMAT
      1410 .1     CLC
      1420        XCE
      1430        PLP
      1440        RTS
      1450 *--------------------------------
      1460 PRINT.FORMAT
      1470 .1     INY
      1480        LDA FORMAT,Y
      1490        BMI .2
      1500        BEQ .3       ...END OF FORMAT
      1510        TAX
      1520        LDA 0,X
      1530        JSR $FDDA
      1540        BRA .1
      1550 .2     JSR $FDED
      1560        BRA .1
      1570 .3     RTS
      1580 *--------------------------------
      1590 FORMAT .HS 8D
      1600        .AS -"Tool "
      1610        .DA #TOOL
      1620        .AS -" version "
      1630        .DA #VERSION+1,#VERSION
      1640        .HS 00
      1650 *
      1660        .AS -" error "
      1670        .DA #ERROR+1,#ERROR
      1680        .HS 00
      1690 *--------------------------------

Apple Assembly Line (ISSN 0889-4302) 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.)