Volume 7 -- Issue 3 | December 1986

In This Issue...

- Eight Ways to Count Bits in a Byte
- Apple //gs ROM Checksummer
- Who Worked on the //gs?
- Commented Listing of ProDOS, $DE00-DEF2
- Another Update to Bob's Program Selector
- Beginning to Peek at the //gs Monitor
- Corrections to HR to DHR Converter
- Demo of Two Simple //gs Tools
- Woz Re-Codes Hi-Res Address Calculations
- Woz Number Game
- Customizing Format Tables in Rak-Ware DISASM

New ProDOS Book

Dennis Doms and Tom Weishaar, Technical Consultant and Publisher of Open-Apple, have conspired to bring us an interesting new book on programming under ProDOS, especially focussing on BASIC.SYSTEM.

"ProDOS Inside and Out" begins by explaining what an operating system is, progresses by describing files and directories, and goes on into simple commands. The next sections cover Applesoft programming and text file handling, followed by information about using machine language under BASIC.SYSTEM and using the ProDOS Kernel and MLI calls from BASIC.

This book does an excellent job of introducing the basic concepts of ProDOS, and then takes the reader on into quite advanced territory. It's very refreshing to find a book that doesn't assume you're already an expert and still has enough substance to help make you into one.

"ProDOS Inside and Out", by Dennis Doms and Tom Weishaar, from TAB Books. List is $16.95, we'll have it for $16 + shipping.

Eight Ways to Count Bits in a Byte | Bob Sander-Cederlof

Of course, there are always eight bits in a byte, by definition. But sometimes we want to know how many of those eight bits are 1's. There are enough reasons to generate this count that some computers have a special machine language opcode to count the number of 1-bits in a byte or word.

One reason that comes to mind is to compute the odd- or even-parity bit for a byte of ASCII data. Another is in processing of picture data in a computer vision system.

I came up with at least eight different programs to count the 1-bits in a byte. I would choose one based on how critical speed or memory-usage is in a particular case.

The fastest one, table lookup, can translate a byte to a bit count in 4 cycles (7 if you also count getting the byte-value into X or Y. It is fast, but it requires a 256-byte table. The table has the counts for each possible value. If speed is critical, I would use this method. I would probably use one of the other methods to create the table during initialization, rather than assembling it from source code. An example of the table lookup method is shown in lines 2130-2160, as a subroutine.

The next method that I thought of is shown in lines 1500-1590. I count the 1-bits in the X-register, while shifting the data byte. The loop runs eight times, once for each bit position. This one takes from 86 cycles to 94 cycles, depending on how many 1-bits there are. The average time is 90 cycles. (I am not counting the JSR and RTS.)

A slightly faster method checks two bit positions during each loop (lines 1610-1730). The reduction in loop overhead changes the times to minimun 66, maximum 74, average 70 cycles.

The fastest method I found without table lookup is shown in lines 1750-2010. This one is completely unwrapped, so there is no loop overhead at all. The times come out to minimum 43, maximum 51, average 47 cycles.

I tried just optimizing the first method, and lines 2030-2110 are the result. This one gets more average speed because it loops only until there are no more 1-bits left. The minimum is only 8 cycles (when the byte = $00). If the byte = $80, it takes 14 cycles. For $C0 or $40, it takes 128 or 120 cycles. For values that have bit 0 = 1, it will take from 70 to 77 cycles, depending on the number of 1-bits in the other seven bit positions. The overall average is 65 cycles.

By changing this last method just a little, the overall average can be reduced to between 58 and 59 cycles. The result is still fairly small, so I think this one would be may favorite choice when the blinding speed of a table lookup is not necessary:

           COUNT   LDX #0
                   LDA BYTE
                   BEQ .3          value is 00000000
                   BPL .2          bit 7 = 0
           .1      INX             count the 1-bit
           .2      ASL             shift the value
                   BMI .1          ...its 1, count it
                   BNE .2          ...its 0, do not count
           .3      RTS             finished

Lines 2270-2460 implement still another method, which always takes 57 cycles regardless of the mix of zeroes and ones. It uses an extra pagezero location for a temporary cell. The TAX in line 2450 is required only if it is really required that the bit count be returned in the X-register. My test program has this requirement, but there is no reason to force that requirement on a real program. If the A-register is good enough, then this method only takes 55 cycles.

Lines 2180-2250 are "half" a table lookup. That is, I am using half a table: entries for values from $00 to $7F. This is a tiny bit slower than the full table lookup, but saves 128 bytes of table. I shift the value right one bit position, guaranteeing a value from $00 to $7F, and save the bit shifted out in carry. Then adding carry to the value from the table gives me the count for the whole original value. A slight change to how carry is added can reduce the average time by half a cycle:

           COUNT   LDA BYTE
                   LSR
                   TAY
                   LDX TBL1,Y
                   BCC .1
                   INX
           .1      RTS

The wildest way I came up with is based on one I read about in the latest issue of Byte magazine (the IBM special issue, in the BIX section). By masking, adding, and shifting, the bits can be all aligned and added into a count. Lines 2480-2720 are my code for this method. The time is 58 cycles, regardless of the 0/1 mix.

I wrote a test routine, so I could tell whether my methods really worked or not. Lines 1070-1480 call each of three different bit-counters for every possible value of the byte. It keeps calling the first method until I hit the space bar, and then advances to the second method. Then it keeps calling the second, until another key-tap advances it to the third method. I can keep cycling through the methods this way, until I type a RETURN to end it. Inside each of the three loops I have a "LDA $C030" instruction, to toggle the speaker. The three loops are identical in timing except for the bit-counting code itself. The result is that I can tell by "ear" which methods are fastest, and which ones have a constant time regardless of the 0/1 mix. I tested the accuracy by comparing the results in TBL1 and TBL2 with the monitor "V" command, and by displaying the TBL using the monitor.

      1000 *SAVE S.BIT COUNTERS
      1010 *--------------------------------
      1020 BYTE   .EQ $00
      1030 B      .EQ $01
      1040 SUM1   .EQ $01
      1050 SUM2   .EQ $02
      1060 *--------------------------------
      1070 T
      1080        LDA #0
      1090        STA BYTE
      1100 *--------------------------------
      1110 .1     JSR COUNT.1
      1120        LDY BYTE
      1130        TXA
      1140        STA TBL1,Y
      1150        LDA $C030
      1160        INC BYTE
      1170        LDA $C000
      1180        BPL .1
      1190        STA $C010
      1200        CMP #$8D
      1210        BEQ .99
      1220 *--------------------------------
      1230 .2     JSR COUNT.2
      1240        LDY BYTE
      1250        TXA
      1260        STA TBL2,Y
      1270        LDA $C030
      1280        INC BYTE
      1290        LDA $C000
      1300        BPL .2
      1310        STA $C010
      1320        CMP #$8D
      1330        BEQ .99
      1340 *--------------------------------
      1350 .3     JSR COUNT.3
      1360        LDY BYTE
      1370        TXA
      1380        STA TBL2,Y
      1390        LDA $C030
      1400        INC BYTE
      1410        LDA $C000
      1420        BPL .3
      1430        STA $C010
      1440        CMP #$8D
      1450        BEQ .99
      1460 *--------------------------------
      1470        JMP .1
      1480 .99    RTS
      1490 *--------------------------------
      1500 COUNT.1
      1510        LDY #8       TEST 1 BIT AT A TIME,
      1520        LDX #0          LOOPING 8 TIMES
      1530        LDA BYTE
      1540 .1     ASL          IF BIT = 1, COUNT IT
      1550        BCC .2
      1560        INX
      1570 .2     DEY
      1580        BNE .1       NEXT BIT
      1590        RTS
      1600 *--------------------------------
      1610 COUNT.2
      1620        LDY #4       TEST 2 BITS AT A TIME,
      1630        LDX #0          LOOPING 4 TIMES
      1640        LDA BYTE
      1650 .1     ASL
      1660        BPL .2       IF BIT = 1, COUNT IT
      1670        INX
      1680 .2     BCC .3       IF BIT = 1, COUNT IT
      1690        INX
      1700 .3     ASL
      1710        DEY
      1720        BNE .1       NEXT PAIR OF BITS
      1730        RTS
      1740 *--------------------------------
      1750 COUNT.3
      1760        LDX #0       NO LOOPS, JUST STRAIGHT-LINE CODE
      1770        LDA BYTE
      1780        BPL .1       BIT 7
      1790        INX
      1800 .1     LSR
      1810        BCC .2       BIT 0
      1820        INX
      1830 .2     LSR
      1840        BCC .3       BIT 1
      1850        INX
      1860 .3     LSR
      1870        BCC .4       BIT 2
      1880        INX
      1890 .4     LSR
      1900        BCC .5       BIT 3
      1910        INX
      1920 .5     LSR
      1930        BCC .6       BIT 4
      1940        INX
      1950 .6     LSR
      1960        BCC .7       BIT 5
      1970        INX
      1980 .7     LSR
      1990        BCC .8       BIT 6
      2000        INX
      2010 .8     RTS
      2020 *--------------------------------
      2030 COUNT.4
      2040        LDX #0
      2050        LDA BYTE
      2060        BEQ .3
      2070 .1     BPL .2
      2080        INX
      2090 .2     ASL
      2100        BNE .1
      2110 .3     RTS
      2120 *--------------------------------
      2130 COUNT.5
      2140        LDY BYTE
      2150        LDX TBL1,Y
      2160        RTS
      2170 *--------------------------------
      2180 COUNT.6
      2190        LDA BYTE
      2200        LSR
      2210        TAX
      2220        LDA #0
      2230        ADC TBL1,X
      2240        TAX
      2250        RTS
      2260 *--------------------------------
      2270 COUNT.7
      2280        LDA BYTE             3
      2290        LSR          BIT 0   2
      2300        STA B                3
      2310        LDA #0               2
      2320        ROL                  2
      2330        LSR B        BIT 1   5
      2340        ADC #0
      2350        LSR B        BIT 2
      2360        ADC #0
      2370        LSR B        BIT 3
      2380        ADC #0
      2390        LSR B        BIT 4
      2400        ADC #0
      2410        LSR B        BIT 5
      2420        ADC #0
      2430        LSR B        BIT 6
      2440        ADC B        BIT 7
      2450        TAX
      2460        RTS
      2470 *--------------------------------
      2480 COUNT.8
      2490        LDA BYTE
      2500        AND #$55     BITS 6,4,2,0
      2510        STA SUM1
      2520        EOR BYTE     BITS 7,5,3,1
      2530        LSR          CLEARS CARRY
      2540        ADC SUM1     FORM wwxxyyzz, where each pair
      2550        STA SUM1         is 0, 1, or 2.
      2560        AND #$33     Isolate 00xx00zz
      2570        STA SUM2
      2580        EOR SUM1     Isolate ww00yy00
      2590        LSR
      2600        LSR          Now it is 00ww00yy
      2610        ADC SUM2     Form 0uuu0vvv, where each group
      2620        STA SUM2          is 0, 1, 2, 3, or 4.
      2630        AND #$0F     Isolate 00000vvv
      2640        STA SUM1
      2650        EOR SUM2     Isolate 0uuu0000
      2660        LSR
      2670        LSR
      2680        LSR
      2690        LSR          Now it is 00000uuu
      2700        ADC SUM1     Form count 0-8
      2710        TAX
      2720        RTS
      2730 *--------------------------------
      2740        .BS *+255/256*256-*
      2750 *--------------------------------
      2760 TBL1   .BS 256
      2770 TBL2   .BS 256
      2780 *--------------------------------

Apple //gs ROM Checksummer | Bob Sander-Cederlof

Somewhere I ran across a statement that the 128K bytes of ROM in the //gs have a standard checksum. A value is stored at $FFFFF6 and $FFFFF7 to pad out the checksum, so that it will always add up to $1234. I tried various ways of adding up the bytes to get that value, and came up with the following little program.

      1000 *SAVE S.CHECKSUM
      1010 *--------------------------------
      1020        .OP 65816
      1030 *--------------------------------
      1040 FORM.AND.DISPLAY.IIGS.CHECKSUM
      1050        CLC          ENTER NATIVE MODE
      1060        XCE
      1070        REP #$30     FULL 16
      1080 *--------------------------------
      1090        LDX ##0      0 TO FFFF
      1100        TXA          START WITH A=0
      1110        SEC          WHY?  IT MAKES ANSWER $1234
      1120 .1     ADC $FE0000,X     BANK $FE
      1130        INX
      1140        INX               TWO BYTES AT A TIME
      1150        BNE .1            ...UNTIL WHOLE BANK
      1160 .2     ADC $FF0000,X     BANK $FF
      1170        INX
      1180        INX               TWO BYTES AT A TIME
      1190        BNE .2            ...UNTIL WHOLE BANK
      1200        STA 0             SAVE RESULT AT $00,01
      1210 *--------------------------------
      1220        SEC          EMULATION MODE
      1230        XCE
      1240        LDA 1        PRINT CHECKSUM WITH OLD MONITOR
      1250        JSR $FDDA         SUBROUTINE 'PRBYTE'
      1260        LDA 0
      1270        JSR $FDDA
      1280        RTS
      1290 *--------------------------------

The 128K ROM occupies the space from $FE0000 through $FFFFFF. My program adds up the data there two bytes at a time in 16-bit registers. Doing a normal add of these "words" gave a sum of $1233, so I started with SEC instead of CLC to get a sum of $1234. I took my program to a computer store and tried it on a "real" //gs, with a different ROM set, and got the same result: $1234.

If you make any changes to the ROMs yourself, be sure to fix the checksum too. Otherwise you may not even be able to boot!

Who Worked on the //gs? | Bob Sander-Cederlof

You can see a list of the names of the people inside Apple who worked on the //gs by typing in the following program. The names are stored inside the //gs ROM, organized according to which project they worked on. I suppose future versions of the //gs ROMs may not have this information (they may need the space for more useful tools), or it may be moved around, but so far it is all the machines I have looked at.

The names start at $BA0B, and are in two consecutive blocks terminated by a 00 byte. Most of the bytes are ASCII characters with bit 7 high (=1). Whenever you find a byte with bit 7 low (=0), it is a repeat count for the following character. Thus 07 A0 means repeat $A0 seven times, or print seven blanks. You will also find repeat counts followed by $53, which is an inverse S. However, in MouseText, it is a horizontal line. Evidently it is supposed to be displayed with MouseText turned on.

There may be a program somewhere inside the ROM that prints the list of names, but I haven't even tried finding it yet. Anyway, the following one will do it.

      1000 *SAVE S.NAMES.IIGS
      1010 *--------------------------------
      1020 *      PRINT NAMES OF APPLE //GS DEVELOPERS
      1030 *--------------------------------
      1040        .OP 65816
      1050 *--------------------------------
      1060 PRINT.NAMES.OF.APPLE.PEOPLE
      1080        CLC          ENTER NATIVE MODE
      1090        XCE
      1100        REP #$10     X,Y 16-BIT MODE
      1101        LDA #$8D     SKIP TWO LINES
      1102        JSR MY.COUT
      1103        JSR MY.COUT
      1110        LDX ##$BA0A  NAMES START AT $BA0B
      1120        JSR PRINT.NAMES   FIRST BLOCK
      1130        JSR PRINT.NAMES   SECOND BLOCK
      1140        SEC
      1150        XCE          BACK TO NATIVE MODE
      1190        RTS
      1200 *--------------------------------
      1210 PRINT.NAMES
      1220 .1     INX          NEXT CHAR
      1230        LDA $FF0000,X
      1240        BEQ .2       ...END OF A BLOCK
      1250        BPL .3       ...REPEAT COUNT
      1260        JSR MY.COUT  PRINT THE CHAR
      1270        BRA .1
      1280 .2     RTS          RETURN
      1290 *--------------------------------
      1320 .3     TAY          REPEAT COUNT TO Y-REG
      1340        INX
      1350        LDA $FF0000,X   GET REPEATED CHAR
      1351        CMP #$53     MOUSE TEXT LINE?
      1352        BNE .4       ...NO
      1353        LDA #"-"     ...YES, SUBSTITUTE DASH
      1360 .4     JSR MY.COUT  PRINT THE CHAR
      1370        DEY          N TIMES
      1380        BNE .4
      1390        BRA .1
      1400 *--------------------------------
      1410 MY.COUT
      1420        PHA          SAVE EVERYTHING!!!
      1430        PHX
      1440        PHY
      1450        PHP
      1460        SEC          EMULATION MODE
      1470        XCE
      1480        PHP
      1490        JSR $FDED
      1500        PLP          ORIGINAL MODE
      1510        XCE
      1520        PLP          RESTORE REGISTERS
      1530        PLY
      1540        PLX
      1550        PLA
      1560        RTS
      1570 *--------------------------------
      1580   .LIF

Commented Listing of ProDOS, $DE00-DEF2 | Bob Sander-Cederlof

What happens when you call ProDOS MLI? In assembly language, MLI calls look like this:

           JSR $BF00
           .DA #command,IOB.Address

The instruction at $BF00 is a "JMP $BFB7" in ProDOS 1.1.1; it is possibly different in other versions. All of the following disassembly is for ProDOS 1.1.1. The changes in the new ProDOS 1.2 are minor, and if you have 1.2 you should be able to figure out what they are.

At $BFB7 there is some code I call LC.BRIDGE.ENTRY. It "remembers" what language card areas are switched in at $D000 and at $E000, and then turns on the language card so that it can jump into the MLI call processor.

           BFB7:  SEC      Set flag
                  ROR MLI.ACTIVE.FLAG
                  LDA $E000
                  STA E000.BYTE  (BFF4)
                  LDA $D000
                  STA D000.BYTE  (BFF5)
                  LDA $C08B
                  LDA $C08B
                  JMP $DE00

Now comes the good part. The following listing is of the code starting at $DE00, which decodes the bytes following your JSR $BF00 and performs your request.

Lines 1010-1080 define some page-zero variables used by MLI. Lines 1090-1220 define some items in the system global page. Lines 1230-1280 define some entry points inside the rest of MLI, not listed here.

MLI calls don't change the X and Y registers, so they are saved at line 1390. The return address (of the JSR $BF00) is pulled off the stack and saved at PARM.PNTR in page zero, so that it can be used to access your command code and IOB address. Lines 1410-1490 also compute the address of the next instruction, to be used later for a return address. This address is saved in the system global page, and is useful sometimes for debugging. (We have published several articles on enhanced error messages and tracers for MLI calls in previous issues of AAL.)

Lines 1500-1650 convert the command code to an index by a strange scheme. The legal command codes are (in hex): 40, 41, 65, 80 thru 82, and C0 thru D3. The hashing algorithm used here adds the high nybble of the command code to the whole code, and then masks it to the lower five bits. This compresses the range of the codes, without any overlapping.

           40,41 --> 04,05         C0-CF --> 0C-1B
           65    --> 0B            D0-D2 --> 1D-1F
           80-82 --> 08-0A         D3    --> 00

This index is used then to look into the COMMAND.HASH.TABLE, which has the actual command codes in the indexed positions. If the original code is not found there, then the original code was an illegal command number. The hash index is also used to look up the parameter count in PARM.CNT.TABLE. I have appended the code for these two tables to the end of today's listing, at lines 3100 to the end.

Lines 1810-1920 branch various ways according to the command code. Most of the commands are not shown in this listing, but most of the code for READ BLOCK and WRITE BLOCK is shown (lines 2690-3080). When a command is finished, it eventually finds its way back to EXIT.TO.CALLER at line 2180.

Lines 2180-2560 get us back to our own code again, after the JSR $BF00. If the MLI call produced an error, the code number for that error will be in SYS.ERRNUM. The error code will be returned in the A-register, with carry SET. If there is no error to report, A=0 and carry is clear.

We will probably be presenting more sections of MLI disassembly in the near future. You may remember that we published portions of an earlier ProDOS version back in November and December of 1983.

      1000 *SAVE S.MLI.DE00.DEF2
      1010 *--------------------------------
      1020 PARM.PNTR  .EQ $40,41
      1030 COMMAND    .EQ $42
      1040 UNIT.NO    .EQ $43
      1050 BUFF.PNTR  .EQ $44,45
      1060 BLOCK.NO   .EQ $46,47
      1070 GEN.PNTR1  .EQ $48,49
      1080 GEN.PNTR2  .EQ $4E,4F
      1090 *--------------------------------
      1100 CALL.QUIT           .EQ $BF03
      1110 CALL.TIME           .EQ $BF06
      1120 CALL.SYSERR         .EQ $BF09
      1130 SYS.ERRNUM          .EQ $BF0F
      1140 DRIVER.ADDR.TABLE   .EQ $BF10 thru BF2F
      1150 BACKUP.BIT          .EQ $BF95
      1160 MLI.ACTIVE.FLAG     .EQ $BF9B
      1170 MLI.RETURN          .EQ $BF9C,D
      1180 MLI.X               .EQ $BF9E
      1190 MLI.Y               .EQ $BF9F
      1200 LC.BRIDGE.EXIT      .EQ $BFA0
      1210 E000.BYTE           .EQ $BFF4
      1220 D000.BYTE           .EQ $BFF5
      1230 *--------------------------------
      1240 INTERRUPT.HANDLER   .EQ $DEF3
      1250 FILING.FUNCTIONS    .EQ $E047
      1260 CHECK.IF.MEM.FREE   .EQ $FC9F
      1270 *--------------------------------
      1280 JUMP                .EQ $FEF5,6
      1290 *--------------------------------
      1300        .OR $DE00
      1310        .TA $800
      1320 *--------------------------------
      1330 *   JSR $BF00 comes here
      1340 *   .DA #$xx  command byte
      1350 *   .DA xxxx  IOB Address
      1360 *--------------------------------
      1370 MLI.ENTRY
      1380        CLD
      1390        STY MLI.Y
      1400        STX MLI.X
      1410        PLA          GET RETURN ADDRESS
      1420        STA PARM.PNTR     WILL POINT AT BYTES
      1430        CLC               FOLLOWING JSR $BF00
      1440        ADC #4       COMPUTE ACTUAL RETURN
      1450        STA MLI.RETURN    AND SAVE FOR LATER
      1460        PLA
      1470        STA PARM.PNTR+1
      1480        ADC #0
      1490        STA MLI.RETURN+1
      1500 *---Check Command Code-----------
      1510        LDY #0
      1520        STY SYS.ERRNUM
      1530        INY
      1540        LDA (PARM.PNTR),Y
      1550        LSR          Hash it (CC/16 + CC) & $1F
      1560        LSR
      1570        LSR
      1580        LSR
      1590        CLC
      1600        ADC (PARM.PNTR),Y
      1610        AND #$1F
      1620        TAX          Use hashcode as index
      1630        LDA (PARM.PNTR),Y  Original command code
      1640        CMP COMMAND.HASH.TABLE,X
      1650        BNE ERR.CALL.NO    Not valid command
      1660 *---Get IOB Address--------------
      1670        INY
      1680        LDA (PARM.PNTR),Y
      1690        PHA
      1700        INY
      1710        LDA (PARM.PNTR),Y
      1720        STA PARM.PNTR+1
      1730        PLA
      1740        STA PARM.PNTR
      1750 *---Check Parm Count-------------
      1760        LDY #0
      1770        LDA PARM.CNT.TABLE,X
      1780        BEQ MLI.GETTIME   ...only one with 0 parms
      1790        CMP (PARM.PNTR),Y
      1800        BNE ERR.PARM.CNT
      1810 *---Branch Various Ways----------
      1820        LDA COMMAND.HASH.TABLE,X
      1830        CMP #$65
      1840        BEQ .1       ...QUIT CALL
      1850        ASL
      1860        BPL MLI.RWBLK        $80 or $81
      1870        BCS MLI.CX.AND.DX    $Cx or $Dx
      1880        LSR                  $40 or $41
      1890        AND #$03
      1900        JSR INTERRUPT.HANDLER
      1910        JMP EXIT.TO.CALLER
      1920 .1     JMP CALL.QUIT        $65
      1930 *--------------------------------
      1940 *   Command $82, Get the Date and Time
      1950 *--------------------------------
      1960 MLI.GETTIME
      1970        JSR CALL.TIME
      1980        JMP EXIT.TO.CALLER
      1990 *--------------------------------
      2000 *   Commands $80 and $81
      2010 *--------------------------------
      2020 MLI.RWBLK
      2030        LSR          Make $00 and 01
      2040        ADC #1       Into $01 and 02
      2050        STA COMMAND  Store into command block
      2060        JSR BLOCK.IO.SETUP  Do the I/O
      2070        JMP EXIT.TO.CALLER
      2080 *--------------------------------
      2090 *   Commands $C0 thru $D3
      2100 *--------------------------------
      2110 MLI.CX.AND.DX
      2120        LSR          Make command code into
      2130        AND #$1F          an index
      2140        TAX
      2150        JSR FILING.FUNCTIONS
      2160 *---fall into EXIT routine-------
      2170 * (DE78) DE5A DE63 DE6E DEB0        callers
      2180 EXIT.TO.CALLER
      2190        LDA #0       Clear BACKUP bit
      2200        STA BACKUP.BIT
      2210        LDY SYS.ERRNUM    If any error code,
      2220        CPY #1            then set carry
      2230        TYA               and clear Z-bit
      2240        PHP               Save this status
      2250        SEI               Disable IRQ's
      2260        LSR MLI.ACTIVE.FLAG  Clear this flag
      2270        PLA          Get saved status
      2280        TAX          and keep it in X-reg
      2290        LDA MLI.RETURN+1
      2300        PHA          Put return address on stack
      2310        LDA MLI.RETURN
      2320        PHA
      2330        TXA          Now push the status for RTI
      2340        PHA
      2350        TYA          Get error code in A-reg
      2360        LDX MLI.X    Restore X and Y
      2370        LDY MLI.Y
      2380        PHA          Error code on stack
      2390        LDA E000.BYTE
      2400        JMP LC.BRIDGE.EXIT
      2410 *--------------------------------
      2420 *   LC.BRIDGE.EXIT is code at $BFA0 in
      2430 *   the system global page.  It restores
      2440 *   the language card to the state it
      2450 *   was in when JSR $BF00 was exectuted.
      2460 *--------------------------------
      2470 *  LC.BRIDGE.EXIT EOR $E000
      2480 *                 BEQ .1   BFAA
      2490 *                 STA $C082
      2500 *                 BNE .2   BFB5
      2510 *  .1             LDA D000.BYTE  $BFF5
      2520 *                 EOR $D000
      2530 *                 BEQ .2   BFB5
      2540 *                 LDA $C083
      2550 *  .2             PLA
      2560 *                 RTI
      2570 *--------------------------------
      2580 ERR.NO.DEVICE
      2590        LDA #$28     "NO DEVICE CONNECTED"
      2600        JSR CALL.SYSERR
      2610 ERR.CALL.NO
      2620        LDA #1       "BAD CALL TYPE"
      2630        BNE DEAD
      2640 ERR.PARM.CNT
      2650        LDA #4       "BAD PARAMETER COUNT"
      2660 DEAD   JSR CALL.CALL.SYSERR
      2670        BCS EXIT.TO.CALLER  ...ALWAYS
      2680 *--------------------------------
      2690 BLOCK.IO.SETUP
      2700        LDY #5       COPY REST OF COMMAND BLOCK
      2710        PHP          FROM IOB TO ZERO-PAGE
      2720        SEI          DO NOT ALLOW IRQ'S
      2730 .1     LDA (PARM.PNTR),Y
      2740        STA COMMAND,Y
      2750        DEY
      2760        BNE .1
      2770        LDX BUFF.PNTR+1
      2780        STX GEN.PNTR2+1
      2790        INX
      2800        INX
      2810        LDA BUFF.PNTR
      2820        BEQ .2
      2830        INX
      2840 .2     JSR CHECK.IF.MEM.FREE
      2850        BCS .3       ...NOT FREE
      2860        JSR BLOCK.IO
      2870        BCS .3       ...I/O ERROR
      2880        PLP          RESTORE IRQ STATUS
      2890        CLC          NO ERRORS
      2900        RTS
      2910 *--------------------------------
      2920 .3     PLP          RESTORE IRQ STATUS
      2930 CALL.CALL.SYSERR JSR CALL.SYSERR
      2940 *--------------------------------
      2950 * (DEDA) DECE EC0A EE83 F0E4 F475   callers
      2960 BLOCK.IO
      2970        LDA UNIT.NO  Clean this up a little
      2980        AND #$F0
      2990        STA UNIT.NO
      3000        LSR          Make it into index too
      3010        LSR
      3020        LSR
      3030        TAX
      3040        LDA DRIVER.ADDR.TABLE,X
      3050        STA JUMP
      3060        LDA DRIVER.ADDR.TABLE+1,X
      3070        STA JUMP+1
      3080        JMP (JUMP)
      3090 *--------------------------------
      3100        .OR $FD65
      3110        .TA $800
      3120 COMMAND.HASH.TABLE
      3130        .HS D3.00.00.00.40.41.00.00
      3140        .HS 80.81.82.65.C0.C1.C2.C3
      3150        .HS C4.C5.C6.C7.C8.C9.CA.CB
      3160        .HS CC.CD.CE.CF.00.D0.D1.D2
      3170 PARM.CNT.TABLE
      3180        .HS 02.FF.FF.FF.02.01.FF.FF
      3190        .HS 03.03.00.04.07.01.02.07
      3200        .HS 0A.02.01.01.03.03.04.04
      3210        .HS 01.01.02.02.FF.02.02.02
      3220 *--------------------------------
      3230        .LIF

Another Update to Bob's ProDOS Program Selector | Bob Sander-Cederlof

The following refers back to the new ProDOS Quit Code I wrote and published in the July 86 issue of AAL. It has been very popular, judging from the number of letters and phone calls we have received.

Eric Trehus (T'n'T Software) pointed out that I ignored one or more of the conventions Apple established for Quit-Code Program Selectors. On page 87 of the ProDOS Technical Reference Manual, the paragraph with number 2 states that the name of the system program should be stored in a buffer at $280, starting with a length byte. The first paragraph on page 88 says any non-standard Quit Code must begin with a CLD instruction, so programs can tell who loaded them.

If you want the CLD instruction there, go ahead and insert one between lines 1310 and 1320. I have not found it necessary for any programs I use.

Eric says that when going from BASIC.SYSTEM to APLWORKS.SYSTEM he needed the program name stored in $280. I have never run into the problem, but it is easy to fix. Eric suggested inserting the following two lines:

           2065    STA $280
           2125    STA $280,Y

[Eric's change takes six bytes, so you need to be sure the code still fits in $300 bytes.]

If you do it Eric's way, only the name of the system file gets stored, without any prefix. I wondered whether or not a full pathname should be there, so I consulted Gary Little's "Apple ProDOS--Advanced Features" book. On page 141, near the bottom, he says either a full or a partial pathname should be put at $280. We can get the full pathname into $280 without Eric's two lines, by simply changing line 4860 from "PATHNAME .BS 64" to "PATHNAME .EQ $280". This is my preference.

When I was trying out the above, I stumbled across a problem. If my Selector finds no SYS or DIR files in a directory, it still displays the pathname and prompt messages. If you then type the RETURN key, it may try to execute garbage, or try various other things. The only valid keystroke when no files are listed is ESCAPE, which will take you back to the list of volume names. Adding two lines makes it go there without displaying the empty list:

           1771    TXA     see if any files listed
           1772    BEQ .2  ...none listed, start over

We noticed the other day that when we ran Erv Edge's correction to my program (Aug 86, page 1), we reversed the information. We said change line 3390 from BNE .1 to BPL .1; actually, it is the other way around: change from BPL to BNE. Most of you figured that out already, but we are sorry for the confusion.

Beginning to Peek at the //gs Monitor | Bob Sander-Cederlof

The //gs Monitor has a lot of new features not found in any earlier model Apple. Unfortunately, you do not get any documentation about the monitor with your machine! Next year you will be able to buy a book that will tell you about it, but who wants to wait?

If you go into the monitor and try some of the old commands, you will find that most of them work. Memory display now shows an additional two digits of address, the bank number, and then a slash and the rest of the address. You can enter addresses the same way, so you can display any memory in the entire 16-megabyte range. For example, to disassemble ROM inside the monitor at $FDF0, type "FF/FDF0L". To look at the range of memory in bank FE from 0 to FF, type "FE/0.FF" and a <RETURN>. Note that the disassembler output looks a little different now. There are no dollar signs for hex values, and all opcodes for the 65816 are disassembled. Also note that in memory range display, you get both hex and ASCII values for each byte. If you are in 80-column mode, range display shows 16 bytes per line rather than only 8.

The new monitor preserves almost all the standard entry points, so they are clues to deciphering the rest. Looking at the routines TOSUB (FF/FFBE) and NXTITM (FF/FF73) I found the new addresses for the command character and branch tables. The command characters are in coded form at FF/F98B and following, and the branches are at FF/F9AE and following. There are 35 commands now, a fact learned by the disassembly of NXTITM.

I wrote a program to decipher the contents of these two tables and print the results. It takes a little work, because the characters in the table are not in ASCII. They correspond to ASCII values exclusive-ORed with $B0 and diminished by $89, which takes place inside the GETNUM subroutine (FF/FFA7). The addresses are the low-bytes only of entry points in page $FE of bank $FF (FF/FExx). These addresses must be incremented by one to get the real entry points, because TOSUB uses them by pushing them on the stack and doing an RTS. Anyway, the following program does all the unraveling for you, and prints 35 lines of commands in the order they appear in the tables, showing the entry points for each.

Lines 1110-1170 are an overall loop which runs 35 times, to print the 35 commands. The rest is a subroutine to print one command. By removing the stars from lines 1210-1230, you can get the output in two columns. An advantage to this is that it all fits on one screen.

      1000 *Save s.mon.cmd.tbl
      1010 *--------------------------------
      1020 COUT   .EQ $FDED
      1030 PRBYTE .EQ $FDDA
      1040 CROUT  .EQ $FD8E
      1050 *--------------------------------
      1060 LTRS   .EQ $F98B    Encoded Table of Letters
      1070 ADR.LO .EQ $F9AE    Command starts at $FExx+1
      1080 *--------------------------------
      1090        .OP 65816
      1100 *--------------------------------
      1110 PRINT.MONITOR.COMMAND.TABLE
      1120        LDY #0
      1130 .1     JSR PRINT.ONE.COMMAND
      1140        INY
      1150        CPY #35      There are 35 commands in table
      1160        BCC .1
      1170        JMP CROUT
      1180 *--------------------------------
      1190 PRINT.ONE.COMMAND
      1200 *---REMOVE "***" FOR 2 COLUMNS--------------|
      1210 ***    TYA          CHECK IF ODD OR EVEN    |
      1220 ***    LSR                                  |
      1230 ***    BCS .0                               |
      1240 *-------------------------------------------|
      1250        JSR CROUT
      1260 *---TAB 10 SPACES----------------
      1270 .0     LDX #10      TAB OVER 10 SPACES
      1280        LDA #" "
      1290 .1     JSR COUT
      1300        DEX
      1310        BPL .1
      1320 *---Convert char to ASCII--------
      1330        LDA LTRS,Y   Value from table
      1340        SEC
      1350        SBC #$89     Reverse process from GETNUM
      1360        EOR #$B0
      1370 *---Prepare char to print--------
      1380        LDX #" "     Space before regular chars
      1390        CMP #$A0
      1400        BCS .2       ...not control-char
      1410        LDX #"^"     Caret before control-chars
      1420        ORA #$40     Make control-char printable
      1430 *---Print the char---------------
      1440 .2     PHA          Save char itself
      1450        TXA          Print Space or Caret
      1460        JSR COUT
      1470        PLA          Print char
      1480        JSR COUT
      1490 *---Print the address------------
      1500        LDX #3       PRINT " $FE"
      1510 .3     LDA FE,X
      1520        JSR COUT
      1530        DEX
      1540        BPL .3
      1550        LDA ADR.LO,Y
      1560        INC          Add 1 because it needs it
      1570        JMP PRBYTE
      1580 *--------------------------------
      1590 FE     .AS -/EF$ /
      1600 *--------------------------------
      1610    .LIF

Seeing all the commands is nice, but it would be easier to read the list if they were in alphabetical order. I modified the program a little, sorted them, and printed them in the order of their ASCII values. Lines 1110-1250 now have two loops. The first one goes through the 35 commands, and stores them into two "sorting trays". I first emptied one of the "trays", by storing zeroes in all 256 locations. Then my SORT.ONE.COMMAND subroutine stores the command ASCII code into the "tray" at the position indexed by the ASCII value itself. The address byte goes into the other "tray" at the same position.

When all 35 commands have been placed into the appropriate positions in the two trays, I run another loop to print out all the non-empty positions. There it is! Simple as can be, they are sorted almost instantaneously.

Then I tried to sort them using the same technique but in the order of the addresses. This did not work, because some addresses are used by more than one command. Only the last command using a particular address printed out. Sigh....

      1000 *Save s.moncmds.sort
      1010 *--------------------------------
      1020 COUT   .EQ $FDED
      1030 PRBYTE .EQ $FDDA
      1040 CROUT  .EQ $FD8E
      1050 *--------------------------------
      1060 LTRS   .EQ $F98B    Encoded Table of Letters
      1070 ADR.LO .EQ $F9AE    Command starts at $FExx+1
      1080 *--------------------------------
      1090        .OP 65816
      1100 *--------------------------------
      1110 PRINT.IIGS.MONITOR.COMMANDS.SORTED
      1120        JSR CLEAR.SORTING.TRAY.A
      1130        LDY #0
      1140 .1     JSR SORT.ONE.COMMAND
      1150        INY
      1160        CPY #35      There are 35 commands in table
      1170        BCC .1
      1180 *---Print the commands-----------
      1190        LDY #0
      1200 .2     LDA SORTING.TRAY.A,Y
      1210        BEQ .3
      1220        JSR PRINT.ONE.COMMAND
      1230 .3     INY
      1240        BNE .2
      1250        JMP CROUT
      1260 *--------------------------------
      1270 CLEAR.SORTING.TRAY.A
      1280        LDX #0
      1290 .1     STZ SORTING.TRAY.A,X
      1300        INX
      1310        BNE .1
      1320        RTS
      1330 *--------------------------------
      1340 SORT.ONE.COMMAND
      1350 *---Convert char to ASCII--------
      1360        LDA LTRS,Y   Value from table
      1370        SEC
      1380        SBC #$89     Reverse process from GETNUM
      1390        EOR #$B0
      1400        TAX          It is the sorting index
      1410        STA SORTING.TRAY.A,X
      1420 *--------------------------------
      1430        LDA ADR.LO,Y
      1440        INC
      1450        STA SORTING.TRAY.B,X
      1460        RTS
      1470 *--------------------------------
      1480 PRINT.ONE.COMMAND
      1490        JSR CROUT
      1500 *---TAB 10 SPACES----------------
      1510        LDX #10      TAB OVER 10 SPACES
      1520        LDA #" "
      1530 .1     JSR COUT
      1540        DEX
      1550        BPL .1
      1560 *---Convert char to ASCII--------
      1570        LDA SORTING.TRAY.A,Y
      1580 *---Prepare char to print--------
      1590        LDX #" "     Space before regular chars
      1600        CMP #$A0
      1610        BCS .2       ...not control-char
      1620        LDX #"^"     Caret before control-chars
      1630        ORA #$40     Make control-char printable
      1640 *---Print the char---------------
      1650 .2     PHA          Save char itself
      1660        TXA          Print Space or Caret
      1670        JSR COUT
      1680        PLA          Print char
      1690        JSR COUT
      1700 *---Print the address------------
      1710        LDX #3       PRINT " $FE"
      1720 .3     LDA FE,X
      1730        JSR COUT
      1740        DEX
      1750        BPL .3
      1760        LDA SORTING.TRAY.B,Y
      1770        JMP PRBYTE
      1780 *--------------------------------
      1790 FE     .AS -/EF$ /
      1800 *--------------------------------
      1810 SORTING.TRAY.A .BS 256
      1820 SORTING.TRAY.B .BS 256
      1830 *--------------------------------

Corrections to HR to DHR Converter | Bob Sander-Cederlof

David Johnson just called to point out a couple of corrections to his program that converts hi-res graphic images to double hi-res. First, we somehow managed to lose a line of his code. You need to add this line:

         2435       LSR XLATE.MONO.AUX,X

Another spot to change is line 2120. The LDA #$03 should really be a LDA #$02, since 3 specifies a full color image and 2 specifies black and white. David reports that 3 has always worked OK for him, so this may not make a real difference, but the specification (ProDOS Technical Note #13) calls for a 2.

An error also crept into the text. Near the end of the fourth paragraph the article says that the AUXMEM portion of the picture is copied into main memory at $4000-5FFF. What the program really does is copy main memory from $2000-3FFF to $4000-5FFF, and then transfer the AUXMEM segment into main memory at $2000-3FFF.

Demo of Two Simple //gs Tools | Bob Sander-Cederlof

The //gs Toolbox is chock full of useful tools, and no doubt you have heard about a lot of them by now. However, all of the books and articles I have found so far just describe the tools, without showing any actual addresses or tool numbers so they can be called. Most frustrating!

If you remember the article last month about reading and writing the battery RAM in the //gs, you will remember the general way all tools are called. You must be in full-16 native mode, set up the stack in just the right way, put a tool code in the X-register, and do a JSL $E10000.

The tool code is made up of two bytes. The low-order byte is the tool set number, and the high-order byte is the tool number in that tool set. One of the missing items of information in most documentation I have seen so far is the list of tool set numbers. As near as I can figure it all out at this time, the following numbers seem to be established:

           Set #   Tool
          -------  --------------------------------
            $01    Tool Locater itself
            $02    Memory Manager
            $03    Miscellaneous Tools
            $04    Graphics Core (QuickDraw?)
            $05    Desk Manager
            $06    Event Manager
            $07    Scheduler
            $08    Sound Manager (Ensoniq stuff)
            $09    Front Desk Bus
            $0A    SANE (Fancy Floating Point stuff)
            $0B    Integer Math
            $0C    Text Tools
            $0D    <<<I don't know>>>
         $0E-$20   Various RAM-based tools

The first eight tools in every tools set are all the same, and do not seem to be too important to the casual user. They include boot initialization code, version and status information, reset, and so on. Even a couple of spares.

Of interest to this article, tool number $2A in set $0B will convert a two-byte value to a four-character ASCII string. For example, $12AF would be converted to the four bytes $31, $32, $41, $46. These are the ASCII values for the four hexadecimal digits of $12AF. Lines 1090-1170 in the following program use this tool. Note that all inputs and outputs for the tool are handled through the stack.

Tool $20 of tool set $0C will print out a string in ASCIIZ format. ASCIIZ is a new term to me, which I discovered this week in the Microsoft book "Advanced MS-DOS". It means a string of ASCII characters terminated by a 00 byte. After lines 1140-1170 have placed the ASCII form of the number we converted into the four bytes at HEX (line 1290), we have an ASCIIZ string starting at MSG (lines 1280-1300). Pushing the address of MSG onto the stack and calling for tool $200C will print out the string on the screen. See lines 1180-1220.

Line 1190 may need a word of explanation. The tool needs a four-byte address on the stack, so line 1190 pushes the high-order two bytes of the address. I could have used "PEA MSG/65536", but I like "PEA MSG/256/256" better.

If you have a //gs, try out this little example. It will get you started in understanding all the new tools you have in your toolbox. If you don't have a //gs yet, start saving your nickels!

      1000 *SAVE S.PRINT.DEMO
      1010 *--------------------------------
      1020        .OP 65816
      1030 T
      1040        clc          GO INTO NATIVE MODE
      1050        xce
      1060        rep #$30     FULL 16
      1070        lda ##$1234  SAMPLE VALUE IN A-REG
      1080 *---Tool: convert hex to string-------
      1090        PEA 0        4-BYTES FOR RESULT
      1100        PEA 0
      1110        PHA          VALUE TO BE CONVERTED
      1120        LDX ##$2A0B  TOOL: HEX-VALUE TO ASCII
      1130        JSL $E10000
      1140        PLA          TWO CHARS
      1150        STA HEX      INTO STRING
      1160        PLA          TWO MORE CHARS
      1170        STA HEX+2    INTO STRING
      1180 *---Tool: print a string--------------
      1190        PEA MSG/256/256
      1200        PEA MSG      ADDRESS OF STRING
      1210        LDX ##$200C  TOOL: PRINT STRING
      1220        JSL $E10000
      1230 *--------------------------------
      1240        SEC          RETURN IN EMULATION MODE
      1250        XCE
      1260        RTS
      1270 *--------------------------------
      1280 MSG    .AS "THE NUMBER IN THE A-REGISTER IS $"
      1290 HEX    .BS 4
      1300        .HS 0D.00
      1310 *-------------------------------
      1320        .LIF

Woz Re-Codes Hi-Res Address Calculations | Bob Sander-Cederlof

In the October or November issue of the Washington Apple Pi newsletter, Rick Chapman wrote a review of various methods of calculating the hi-res base addresses. Steve Wozniak liked the article, and responded with a long "letter to the editor" in the December issue. Steve also presented a new version of the hi-res address calculator which is both shorter and faster. In fact, as far as I am aware, it is the fastest method ever, except for table-lookups.

In the September 1983 issue of Apple Assembly Line, I presented both the original Woz code and a shorter-faster version by Harry Cheung of Nigeria. Here are the specs:

           Applesoft ROM version:  33 bytes, 61 cycles
            Harry Cheung version:  25 bytes, 46 cycles
             New Wozniak version:  26 bytes, 36 or 37 cycles

The byte counts do not include an RTS at the end of the code, nor do the times include a JSR-RTS. After all, if you are really working for speed you will put the code in its place, not make it a subroutine.

Woz's new version takes either 36 or 37 cycles, depending on the values for the first two bits of the line number. Remember that the line number can be any value from 0 to 191, or $00...$BF. That means the first two bits are either 00, 01, or 10. If you look at lines 1090-1120 below, you will see that the shortest path is for 00, taking both branches, giving a running time for the whole calculation of 36 cycles. If the first two bits are 01 or 10, one branch will be taken and the other not, making the total time 37 cycles. In Woz's letter he shortchanged himself, thinking possibly both branches might not be taken, giving a total running time of 38 cycles; this cannot happen with legal line numbers.

Line 1180 adds in either $10 or $20, depending on which hi-res page you are using. The Applesoft code here adds in a value of either $20 or $40, so if this version were to be inserted into Applesoft the generation of HPAG2 would have to be changed. No problem, and not likely anyway. By the way, if you are only using one specific hi-res page, you can change line 1180 to an immediate mode form, saving yet another cycle.

Here is Woz's new version, reformatted for the S-C Assembler and with some changes in comments:

      1000 *SAVE S.NEW.WOZ.HIRES.CALC
      1010 *--------------------------------
      1020 GBASL  .EQ $26
      1030 GBASH  .EQ $27
      1040 HPAG2  .EQ $E6     Applesoft puts it here anyway.
      1050 *--------------------------------
      1060 CALC   ASL          A--BCDEFGH0
      1070        TAX          TAX...TXA could be TAY...TYA
      1080        AND #$F0     A--BCDE0000
      1090        BPL .1       B=0
      1100        ORA #$05     A--BCDE0B0B
      1110 .1     BCC .2       A-0
      1120        ORA #$0A     A--BCDEABAB
      1130 .2     ASL          B--CDEABAB0
      1140        ASL          C--DEABAB00
      1150        STA GBASL
      1160        TXA          C--BCDEFGH0
      1170        AND #$0E     C--0000FGH0
      1180        ADC HPAG2    O--OOxxFGH0
      1190 *   HPAG2 = $10 for base $2000, $20 for base $4000
      1200        ASL GBASL    D--00xxFGHC  GBASL=EABAB000
      1210        ROL          0--0xxFGHCD
      1220        STA GBASH
      1230        RTS
      1240 *--------------------------------

Woz Number Game | Bob Sander-Cederlof

[ Not in printed edition of the newsletter, as far as I can find. However, it was on the Monthly Disk, so I am including it here. ]

      1000 *SAVE S.WOZNIAK
      1010 *--------------------------------
      1020 *   A Number Game for the 6502, by Steve Wozniak
      1030 *   Published in Dr. Dobb's Journal, September 1976.
      1040 *--------------------------------
      1050 *   Adapted for the Apple II by Bob Sander-Cederlof
      1060 *--------------------------------
      1070 KEYBOARD .EQ $C000
      1080 STROBE   .EQ $C010
      1090 *--------------------------------
      1100 MON.RDKEY  .EQ $FD0C
      1110 MON.PRBYTE .EQ $FDDA
      1120 MON.CROUT  .EQ $FD8E
      1130 MON.COUT   .EQ $FDED
      1140 *--------------------------------
      1150 TRIES  .EQ $00
      1160 RND2L  .EQ $03
      1170 N      .EQ $04 ...08
      1180 GUESS  .EQ $09 ...0D
      1190 *--------------------------------
      1200 RNDL   .EQ $4E
      1210 RNDH   .EQ $4F
      1220 *--------------------------------
      1230 T
      1240 MSTMND
      1250        LDX #Q.RDY   Print "READY?"
      1260 MSGLP  LDA MSG-1,X
      1270        JSR MON.COUT
      1280        DEX
      1290        BNE MSGLP
      1300 *---Clear TRY count--------------
      1310        STX TRIES    X = 0
      1320 *---Wait until player ready------
      1330 *   Creates random number in RNDH,RNDL
      1340        JSR MON.RDKEY
      1350        cmp #$8D     Stop if <RETURN>
      1360        bne NXTRY
      1370        rts
      1380 *---Count the try----------------
      1390 *   (X-reg is 0 now)
      1400 NXTRY  SEC
      1410        SED
      1420        TXA          X=0
      1430        ADC TRIES    Add 1 to tries in BCD mode
      1440        STA TRIES
      1450        CLD
      1460 *---Display the try count--------
      1470 NXTLIN JSR MON.CROUT
      1480        LDA TRIES    Print number of tries in BCD
      1490        JSR MON.PRBYTE
      1500        LDA #" "     BLANK
      1510        TAY          $A0, bits 43210 all clear
      1520        JSR MON.COUT Print the space
      1530 *---Build array of digits--------
      1540        LDA RNDL     Use the random number
      1550        STA RND2L       without changing it
      1560        LDA RNDH     x5554443 11122233
      1570 *                   (Remember that Y-reg holds $A0)
      1580        LDX #5       Do 5 digits
      1590 .1     STY N-1,X    Be sure bits 43210 are clear
      1600        LDY #3       Shift in next three bits from RND
      1610 .2     LSR
      1620        ROL RND2L
      1630        ROL N-1,X
      1640        DEY
      1650        BNE .2       Next bit
      1660        DEX
      1670        BNE .1       Next digit
      1680 *---Read player's guess----------
      1690 *   X- and Y-regs are 0 now
      1700        JSR GET.GUESS
      1710        BCC NXTLIN
      1720 *---Check digits in position-----
      1730 *   (X-reg is -5 now)
      1740        LDY #-5
      1750        LDA #" "     Print a space
      1760 .4     JSR MON.COUT   (or a "+")
      1770 .5     LDA GUESS+5,X
      1780        CMP N+5,X
      1790        BNE .6       ...not an exact match
      1800        STY N+5,X    ...matches, so clobber it
      1810        LDA #"+"        and print a "+"
      1820        STA GUESS+5,X   (clobber here too)
      1830        INY          Count the +
      1840        BNE .4       ...not 5 yet, try another
      1850        LDX #Q.WIN   All 5 correct!
      1860        BNE MSGLP    Say so, invite another game.
      1870 .6     INX          Next digit position
      1880        BNE .5
      1890 *---Check for digits out of position---
      1900        LDY #-5      For each digit in guess...
      1910 .7     LDX GUESS+5,Y     (Cannot use LDA GUESS+5,Y
      1920        TXA                because that would not wrap!)
      1930        LDX #-5         For each digit in puzzle...
      1940 .8     CMP N+5,X
      1950        BNE .9          ...different
      1960        STY N+5,X       ...same, clobber in puzzle
      1970        LDA #"-"           and print a "-"
      1980        JSR MON.COUT
      1990 .9     INX             Next puzzle digit
      2000        BNE .8
      2010        INY          Next guess digit
      2020        BNE .7
      2030        BEQ NXTRY    ...always, and X must = 0
      2040 *--------------------------------
      2050 MSG    .AS -/?YDAER/
      2060        .HS 8D
      2070        .HS 8D
      2080 Q.RDY  .EQ *-MSG
      2090        .AS -/NIW UOY +/
      2100 Q.WIN  .EQ *-MSG
      2110 *--------------------------------
      2120 GET.GUESS
      2130 .1     LDA KEYBOARD      Read char from keyboard
      2140        BPL .1
      2150        STA STROBE        Clear the strobe
      2160        CMP #"8"          <My little secret!>
      2170        BNE .2
      2180        LDA N+4,X         <Cheat!>
      2190        ORA #"0"
      2200 .2     JSR MON.COUT      Echo the character
      2210        EOR #"0"          Convert to binary if 0-7
      2220        CMP #8
      2230        BCS .3            ...Not digit, start try over
      2240        STA GUESS+4,X     Valid, save the guessed digit
      2250        DEX
      2260        CPX #-5
      2270        BNE .1            Input next digit
      2280        RTS
      2290 .3     CLC
      2300        RTS
      2310 *--------------------------------

Customizing Format Tables in Rak-Ware DISASM | Bob Kovacs

[ Not in printed edition of the newsletter, as far as I can find. However, it was on the Monthly Disk, so I am including it here. ]

I recently received a request for information about the format options in DISASM, the intelligent dis-assembler. Since the format of the generated source code is essentially table-driven, it is rather straightforward to customize the format to match the requirements of different assemblers. The formats for three popular assemblers is built-in, but you could make slight changes to accommodate other assemblers.

The following chart identifies each output parameter, showing its location (in version 2.2e of DISASM), length, and default value. Most of the parameters have three entries, corresponding to the three built-in formats: DOS Toolkit, S-C, and Lisa.

    Location  Len  Name      Default     Description

    $0EA5     1    LABELCHR  $AE (period)   Character after label
                                            prefix character.
    $1309     35   MENUTBL   DOS TOOLKIT 8D.00    Info for menu.
                             S-C ASSEMBLER 8D.00  (00 ends item,
                             LISA 8D.00.00      extra 00 ends list)
    $1326     3    PREFIX    IZX         First letter for labels:
                                         Internal, Zeropage, and
                                         eXternal.
    $1331     3    COMMENT   **;         Comment character.
    $1334     3    FIRSTCHR  00.89.00    First character of each
                                         line (00 means omit).
    $1337     6    TABCHR    A0.89.A0    Opcode field tab char.
                             A0.A0.A0    Operand field tab char.
    $133D     3    OPCHR     C1.00.00    Operand for implied
                                         Accum-mode instruction.
    $1340     9    PGZEQU    EQU.EQEPX   Opcode for page-zero
                                         definitions.
    $1349     9    EXTEQU    EQU.EQEQU   Opcode for other defs.
    $1352     9    HEXCHR    DFB.HSHEX   Opcode for hex data.
    $135B     9    ORGVHR    ORG.ORORG   Opcode for origin.
    $1364     9    PRECHR                Preamble (start of file).
                             AA.00.00    DOS TK Preamble.
                             00.00.00    S-C preamble (none).
                             INS         Lisa preamble.
    $136D     3    POSTCHR   00.98.85    Postamble
                                         (last char in file).

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