Apple Assembly Line
Volume 7 -- Issue 3 December 1986

In This Issue...

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