Apple Assembly Line
Volume 4 -- Issue 2 November 1983

In This Issue...

Tearing into ProDOS

Have we got a treat for you! You've heard about ProDOS, the new operating system for the Apple II's. Its main advantage over DOS 3.3 is speed, and on the next page of this issue you'll start to see what makes it so fast. ProDOS uses a completely different technique for translating between memory bytes and nibble-coded disk data, and here it is! Start reading Bob's completely commented disassembly.

Holiday Special Prices

Remember that we are offering special prices on several popular products from our list. Check the ad on page two for details. We are also having a sale on back issues of Apple Assembly Line: now only $1.00 each, rather than the usual $1.50. This is the time to complete your set! Subscription rates will be going up as of the first of the year, but you can still renew at the current prices. Let us hear from you.

Non-volatile RAM

Rodney Jacks, a Mostek engineer, tells us of a very interesting new chip: a 2K-byte static RAM, plug compatible with a 2716 EPROM, with a built-in lithium battery. Call your distributor and ask for Mostek MK48Z02. I can hardly wait to get some.


Commented Listing of ProDOS
$F800-$F90B, $F996-FEBD
Bob Sander-Cederlof

ProDOS boots its bulk into the RAM card, from $D000 thru $FFFF. More is loaded into the alternate $D000-DFFF space, and all but 255 bytes are reserved out of the entire 16K space.

A system global page is maintained from $BF00-BFFF, for various variables and linkage routines. All communication between machine language programs and ProDOS is supposed to be through MLI (Machine Language Interface) calls and the system global page.

One of the first things I did with ProDOS was to start dis-assembling and commenting it. I want to know what is inside and how it works! Apple's 4-inch thick binder tells a lot, but not all.

Right away I ran into a roadblock: to disassemble out of the RAM card it has to be turned on. There is no monitor in the RAM card when ProDOS is loaded. Turning on the RAM card from the motherboard monitor causes a loud crash!

I overcame most of the problem by copying a monitor into the $F800-FFFF region of the RAM card like this:

       *C089 C089 F800<F800.FFFFM
       *C083 C083

The double C089 write-enables the RAM card, while memory reads are still from the motherboard. The rest of the line copies a monitor up. The two C083's get me into the RAM card monitor, ready to type things like "D000LLLLLLLLLLLL"

But what about dis-assemblies of the space between $F800 and $FFFF? For this I had to write a little move program. My program turned on the RAM card and copied $F800-FFFF down to $6800-6FFF. Then I BSAVEd it, and later disassembled it.

The code from $F800-FFFF is mostly equivalent to what is in DOS 3.3 from $B800-BFFF. First I found a read/write block subroutine, which calls an RWTS-like subroutine twice per block. (All ProDOS works with 512-byte blocks, rather than sectors; this is like Apple Pascal, and the Apple ///.)

The listing which follows shows the RWB and RWTS subroutines, along with the READ.ADDRESS and READ.SECTOR subroutines. Next month I plan to lay out the SEEK.TRACK and WRITE.SECTOR subroutines, as well as the interrupt and reset handling code.

The outstanding difference between ProDOS and DOS 3.3 disk I/O is speed. ProDOS is considerably faster. Most of the speed increase is due to handling the conversion between memory-bytes and disk-bytes on the fly. DOS pre-converted a 256-byte block into 342 bytes in a special buffer, and then wrote the 342 bytes; ProDOS forms the first 86 bytes of the disk data in a special buffer, writes them, and then proceeds to write the rest of the data directly from the caller's buffer. When reading, DOS read the 342 disk-bytes into a buffer for later decoding into the caller's buffer. ProDOS reads and decodes simultaneously directly into the caller's buffer. This is achieved by extensive use of tables and self-modifying code.

Not only is direct time saved by doing a lot less copying of buffers, but also the sector interleaving can be arranged so that only two revolutions are required to read all 8 blocks on a track.

I believe Apple Pascal uses the same technique, at least for reading.

Whoever coded ProDOS decided to hard-code some parameters which DOS used to keep in tables specified by the user. For example, the number which tells how long to wait for a drive motor to rev up used to be kept in a Device Characteristics Table (DCT). That value is now inside a "LDA #$E8" instruction at $F84F. That means that if you are using a faster drive you have to figure out how to patch and unpatch ProDOS to take advantage of it.

Another hard-coded parameter is the maximum block number. This is no longer part of the data on an initialized disk. It is now locked into the four instructions at $F807-F80D, at a maximum of 279. If you have a 40- or 70-track drive, you can only use 35. Speaking of tracks, the delay tables for track seeking are still used, but they are of course buried in this same almost-unreachable area. If you have a drive with faster track-to-track stepping, the table to change is at $FB73-FB84.

Calls to RWTS in DOS 3.3 involved setting up two tables, an IOB and a DCT. The IOB contained all the data about slot, drive, track, sector, buffer address, etc. The DCT was a 5-byte table with data concerning the drive. ProDOS RWB is called in an entirely different way. A fixed-position table located at $42-47 in page zero is set up with the command, slot, buffer address, and block number.

There are three valid commands, which I call test, read, and write. Test (0) starts up the indicated drive. If it is successful, a normal return occurs; if not, you get an error return (carry set, and (A) non-zero). Read (1) and write (2) are what you expect them to be. RWB has a very simple job: validate the call parameters in $42-47, convert block number to track and sector, and call RWTS twice (once for each sector of the block).

ProDOS RWTS expects the sector number in the A-register, and the track in a variable at $FB56. RWTS handles turning on the drive motor and waiting for it to come up to speed. RWTS then calls SEEK.TRACK to find the desired track, READ.ADDRESS to find the selected sector, and branches to READ.SECTOR or WRITE.SECTOR depending on the command.

READ.ADDRESS is virtually the same in ProDOS as it was in DOS 3.3. READ.SECTOR is entirely different. I should point out here that ProDOS diskettes are entirely compatible with Apple /// diskettes. The file structures are exactly the same. Both ProDOS and Apple /// diskettes use the same basic recording techniques on the disk as DOS 3.3, so the diskettes are copyable using standard DOS 3.3 copiers such as the COPYA program on your old System Master Diskette.

READ.SECTOR begins by computing several addresses and plugging them into the code further down. (This enables the use of faster addressing modes, saving enough cycles to leave time for complete decoding of disk data on the fly.) First the disk slot number is merged into the instructions which read bytes from the drive. Next the caller's buffer address is put into the store instructions.

Note that the byte from the disk is loaded into the X-register, then used to index into BYTE.TABLE, at $F996, to get the equivalent 6-bit data value. Since a disk byte may only have certain values, there is some space within BYTE.TABLE that will never be accessed. Most of this unused space contains $FF bytes, but some of it is used for other small tables: BIT.PAIR.LEFT, .MIDDLE, and .RIGHT, and DATA.TRAILER. These are used by WRITE.SECTOR, which we'll look at next month.

Your buffer is divided into three parts: two 86-byte chunks, and one of 84 bytes. Data coming from the disk is in four chunks: three of 86 bytes, and one of 84.

The first chunk contains the lower two bits from every byte in the original data. READ.SECTOR reads this chunk into TBUF, so that the bits will be available later for merging with the upper six of each byte. ($FC53-FC68)

The second chunk contains the upper six bits from the first 86 bytes of the original data. $FC69-FC83 reads the chunk and merges in the lower two bits from TBUF, storing the completed bytes in the first 85 bytes of the caller's buffer. The last (86th) byte is saved on the stack (I am not sure why), and not stored in the caller's buffer until after all the rest of the data has been read.

A tricky manipulation is necessary to merge in those lower two bits. The data in TBUF has those bits in backward order, packed together with the bits from the other chunks. There was a good diagram of this on page 10 of the June 1981 issue of AAL. DOS merged them with a complex time-consuming shifting process. ProDOS does a swift table lookup, using the TBUF byte as an index to the BIT.PAIR.TABLE.

BIT.PAIR.TABLE has four bytes per row. The first three in each row supply the bit pairs; the fourth is used by SECTOR.WRITE to encode data, and will be covered next month.

When $FC69-FC83 is reading the first chunk, the first byte in each row is used to supply the lower two data bits. The byte in TBUF corresponding to the current position in the chunk selects a byte from BIT.PAIR.TABLE, and the two parts are merged together.

The next two chunks are handled just like the one I just described. After all the data has been read, READ.SECTOR expects to have accumulated a checksum of 00, and expects to find a trailing $EB after the data. Return with carry clear indicates all went well; carry set indicates a read error (bad checksum, missing header, or missing trailer).

I can't help wondering: can this fast read technique be fit into DOS 3.3? It takes a little more code and table space, but on the other hand it uses 256 bytes less of intermediate buffer. If we sacrificed the INIT command, could both the fast read and write be squeezed into DOS 3.3?

For more good information on ProDOS, be sure to take a look at Tom Weishaar's DOStalk column in the current issue of Softalk.

  1000        .TI 76,PRODOS F800-FFFF.....COMMENTED BY RBS-C  11-8-83............
  1010 *SAVE S.PRODOS F800-FFFF
  1020 *--------------------------------
  1030 RUNNING.SUM        .EQ $3A
  1040 TBUF.0             .EQ $3A
  1050 BYTE.AT.BUF00      .EQ $3B
  1060 BYTE.AT.BUF01      .EQ $3C
  1070 LAST.BYTE          .EQ $3D
  1080 SLOT.X16           .EQ $3E
  1090 INDEX.OF.LAST.BYTE .EQ $3F
  1100 *--------------------------------
  1110 RWB.COMMAND .EQ $42
  1120 RWB.SLOT    .EQ $43   DSSSXXXX
  1130 RWB.BUFFER  .EQ $44,45
  1140 RWB.BLOCK   .EQ $46,47   0...279
  1150 *--------------------------------
  1160 BUFF.BASE .EQ $4700 DUMMY ADDRESS FOR ASSEMBLY ONLY
  1170 *--------------------------------
  1180 SAVE.LOC45 .EQ $BF56
  1190 SAVE.D000  .EQ $BF57
  1200 INTAREG    .EQ $BF88
  1210 INTBANKID  .EQ $BF8D
  1220 IRQXIT.3   .EQ $BFD3
  1230 *--------------------------------
  1240 DRV.PHASE  .EQ $C080
  1250 DRV.MTROFF .EQ $C088
  1260 DRV.MTRON  .EQ $C089
  1270 DRV.ENBL.0 .EQ $C08A
  1280 DRV.Q6L    .EQ $C08C
  1290 DRV.Q6H    .EQ $C08D
  1300 DRV.Q7L    .EQ $C08E
  1310 DRV.Q7H    .EQ $C08F
  1320 *--------------------------------
  1330 *                 <<<COMPUTED >>>
  1340 MODIFIER .EQ $60  <<<SLOT * 16>>>
  1350 *--------------------------------
  1360        .OR $F800
  1370        .TA $800
  1380 *--------------------------------
  1390 *      READ/WRITE A BLOCK
  1400 *
  1410 *      1.  ASSURE VALID BLOCK NUMBER (0...279)
  1420 *      2.  CONVERT BLOCK NUMBER TO TRACK/SECTOR
  1430 *          TRACK = INT(BLOCK/8)
  1440 *          BLOCK   SECTORS
  1450 *          -----  ---------
  1460 *            0     0 AND 2
  1470 *            1     4 AND 6
  1480 *            2     8 AND 10
  1490 *            3    12 AND 14
  1500 *            4     1 AND 3
  1510 *            5     5 AND 7
  1520 *            6     9 AND 11
  1530 *            7    13 AND 15
  1540 *      3.  CALL RWTS TWICE
  1550 *      4.  RETURN WITH ERROR STATUS
  1560 *--------------------------------
  1570 RWB
  1580        LDA RWB.BLOCK     BLOCK MUST BE 0...279
  1590        LDX RWB.BLOCK+1
  1600        STX RWTS.TRACK
  1610        BEQ .1       ...BLOCK # LESS THAN 256
  1620        DEX
  1630        BNE .5       ...BLOCK # MORE THAN 511
  1640        CMP #$18
  1650        BCS .5       ...BLOCK # MORE THAN 279
  1660 .1     LDY #5       SHIFT 5 BITS OF TRACK # 
  1670 .2     ASL              RWTS.TRACK   A-REG
  1680        ROL RWTS.TRACK   ----------   --------
  1690        DEY               00TTTTTT    ABC00000
  1700        BNE .2
  1710        ASL          TRANSFORM BLOCK # INTO SECTOR #
  1720        BCC .3       ABC00000 --> 0000BC0A
  1730        ORA #$10
  1740 .3     LSR
  1750        LSR
  1760        LSR
  1770        LSR
  1780        PHA
  1790        JSR RWTS     R/W FIRST SECTOR OF BLOCK
  1800        PLA
  1810        BCS .4       ...ERROR
  1820        INC RWB.BUFFER+1  
  1830        ADC #2
  1840        JSR RWTS     R/W SECOND SECTOR OF BLOCK
  1850        DEC RWB.BUFFER+1  
  1860 .4     LDA RWTS.ERROR
  1870        RTS
  1880 *---BLOCK NUMBER > 279-----------
  1890 .5     LDA #$27     I/O ERROR
  1900        SEC
  1910        RTS
  1920 *--------------------------------
  1930 *      READ/WRITE A GIVEN SECTOR
  1940 *--------------------------------
  1950 RWTS
  1960        LDY #1       TRY SEEKING TWICE
  1970        STY SEEK.COUNT
  1980        STA RWTS.SECTOR
  1990        LDA RWB.SLOT  
  2000        AND #$70     0SSS0000
  2010        STA SLOT.X16  
  2020        JSR WAIT.FOR.OLD.MOTOR.TO.STOP
  2030        JSR CHECK.IF.MOTOR.RUNNING
  2040        PHP          SAVE ANSWER (.NE. IF RUNNING)
  2050        LDA #$E8     MOTOR STARTING TIME
  2060        STA MOTOR.TIME+1  ONLY HI-BYTE NECESSARY
  2070        LDA RWB.SLOT      SAME SLOT AND DRIVE?
  2080        CMP OLD.SLOT
  2090        STA OLD.SLOT
  2100        PHP               SAVE ANSWER
  2110        ASL               DRIVE # TO C-BIT
  2120        LDA DRV.MTRON,X   START MOTOR
  2130        BCC .1            ...DRIVE 0
  2140        INX               ...DRIVE 1
  2150 .1     LDA DRV.ENBL.0,X  ENABLE DRIVE X
  2160        PLP               SAME SLOT/DRIVE?
  2170        BEQ .3            ...YES
  2180        PLP          DISCARD ANSWER ABOUT MOTOR GOING
  2190        LDY #7       DELAY 150-175 MILLISECS
  2200 .2     JSR DELAY.100     DELAY 25 MILLISECS
  2210        DEY
  2220        BNE .2
  2230        PHP          SAY MOTOR NOT ALREADY GOING
  2240 .3     LDA RWB.COMMAND   0=TEST, 1=READ, 2=WRITE
  2250        BEQ .4            ...0, MERELY TEST
  2260        LDA RWTS.TRACK
  2270        JSR SEEK.TRACK
  2280 .4     PLP          WAS MOTOR ALREADY GOING?
  2290        BNE .6       ...YES
  2300 .5     LDA #1       DELAY 100 USECS
  2310        JSR DELAY.100
  2320        LDA MOTOR.TIME+1
  2330        BMI .5       ...WAIT TILL IT OUGHT TO BE
  2340        JSR CHECK.IF.MOTOR.RUNNING
  2350        BEQ .14      ...NOT RUNNING YET, ERROR
  2360 .6     LDA RWB.COMMAND  
  2370        BEQ .17      CHECK IF WRITE PROTECTED
  2380        LSR          .CS. IF READ, .CC. IF WRITE
  2390        BCS .7       ...READ
  2400        JSR PRE.NYBBLE   ...WRITE
  2410 .7     LDY #64      TRY 64 TIMES TO FIND THE SECTOR
  2420        STY SEARCH.COUNT
  2430 .8     LDX SLOT.X16  
  2440        JSR READ.ADDRESS
  2450        BCC .10      ...FOUND IT
  2460 .9     DEC SEARCH.COUNT
  2470        BPL .8       ...KEEP LOOKING
  2480        LDA #$27     I/O ERROR CODE
  2490        DEC SEEK.COUNT   ANY TRIES LEFT?
  2500        BNE .14      ...NO, I/O ERROR
  2510        LDA CURRENT.TRACK
  2520        PHA
  2530        ASL          SLIGHT RE-CALIBRATION
  2540        ADC #$10
  2550        LDY #64      ANOTHER 64 TRIES
  2560        STY SEARCH.COUNT
  2570        BNE .11      ...ALWAYS
  2580 .10    LDY HDR.TRACK     ACTUAL TRACK FOUND
  2590        CPY CURRENT.TRACK
  2600        BEQ .12           FOUND THE RIGHT ONE
  2610        LDA CURRENT.TRACK WRONG ONE, TRY AGAIN
  2620        PHA
  2630        TYA          STARTING FROM TRACK FOUND
  2640        ASL
  2650 .11    JSR UPDATE.TRACK.TABLE
  2660        PLA
  2670        JSR SEEK.TRACK
  2680        BCC .8       ...ALWAYS
  2690 .12    LDA HDR.SECTOR
  2700        CMP RWTS.SECTOR
  2710        BNE .9
  2720        LDA RWB.COMMAND  
  2730        LSR
  2740        BCC .15           ...WRITE
  2750        JSR READ.SECTOR   ...READ
  2760        BCS .9            ...READ ERROR
  2770 .13    LDA #0       NO ERROR
  2780        .HS D0       "BNE"...NEVER, JUST SKIPS "SEC"
  2790 .14    SEC          ERROR
  2800        STA RWTS.ERROR    SAVE ERROR CODE
  2810        LDA DRV.MTROFF,X  STOP MOTOR
  2820        RTS               RETURN
  2830 *--------------------------------
  2840 .15    JSR WRITE.SECTOR
  2850 .16    BCC .13      ...NO ERROR
  2860        LDA #$2B     WRITE PROTECTED ERROR CODE
  2870        BNE .14      ...ALWAYS
  2880 .17    LDX SLOT.X16 CHECK IF WRITE PROTECTED
  2890        LDA DRV.Q6H,X
  2900        LDA DRV.Q7L,X
  2910        ROL
  2920        LDA DRV.Q6L,X
  2930        JMP .16      GIVE ERROR IF PROTECTED
 [ SEEK.TRACK is in this gap. It will be published next month. ]
 [ The following tables start at $F996.                        ]

  3660 *--------------------------------
  3670 *      VALUE READ FROM DISK IS INDEX INTO THIS TABLE
  3680 *      TABLE ENTRY GIVES TOP 6 BITS OF ACTUAL DATA
  3690 *
  3700 *      OTHER DATA TABLES ARE IMBEDDED IN THE UNUSED
  3710 *      PORTIONS OF THE BYTE.TABLE
  3720 *--------------------------------
  3730 BYTE.TABLE .EQ *-$96
  3740        .HS 0004FFFF080CFF101418
  3750 BIT.PAIR.LEFT
  3760        .HS 008040C0
  3770        .HS FFFF1C20FFFFFF24282C
  3780        .HS 3034FFFF383C4044
  3790        .HS 484CFF5054585C606468
  3800 BIT.PAIR.MIDDLE
  3810        .HS 00201030
  3820 DATA.TRAILER
  3830        .HS DEAAEBFF
  3840        .HS FFFFFF6CFF70
  3850        .HS 7478FFFFFF7CFFFF
  3860        .HS 8084FF888C9094989CA0
  3870 BIT.PAIR.RIGHT
  3880        .HS 0008040C
  3890        .HS FFA4A8ACFFB0B4B8BCC0
  3900        .HS C4C8FFFFCCD0D4D8
  3910        .HS DCE0FFE4E8ECF0F4
  3920        .HS F8FC
  3930 *--------------------------------
  3940 BIT.PAIR.TABLE
  3950        .HS 00000096
  3960        .HS 02000097
  3970        .HS 0100009A
  3980        .HS 0300009B
  3990        .HS 0002009D
  4000        .HS 0202009E
  4010        .HS 0102009F
  4020        .HS 030200A6
  4030        .HS 000100A7
  4040        .HS 020100AB
  4050        .HS 010100AC
  4060        .HS 030100AD
  4070        .HS 000300AE
  4080        .HS 020300AF
  4090        .HS 010300B2
  4100        .HS 030300B3
  4110        .HS 000002B4
  4120        .HS 020002B5
  4130        .HS 010002B6
  4140        .HS 030002B7
  4150        .HS 000202B9
  4160        .HS 020202BA
  4170        .HS 010202BB
  4180        .HS 030202BC
  4190        .HS 000102BD
  4200        .HS 020102BE
  4210        .HS 010102BF
  4220        .HS 030102CB
  4230        .HS 000302CD
  4240        .HS 020302CE
  4250        .HS 010302CF
  4260        .HS 030302D3
  4270        .HS 000001D6
  4280        .HS 020001D7
  4290        .HS 010001D9
  4300        .HS 030001DA
  4310        .HS 000201DB
  4320        .HS 020201DC
  4330        .HS 010201DD
  4340        .HS 030201DE
  4350        .HS 000101DF
  4360        .HS 020101E5
  4370        .HS 010101E6
  4380        .HS 030101E7
  4390        .HS 000301E9
  4400        .HS 020301EA
  4410        .HS 010301EB
  4420        .HS 030301EC
  4430        .HS 000003ED
  4440        .HS 020003EE
  4450        .HS 010003EF
  4460        .HS 030003F2
  4470        .HS 000203F3
  4480        .HS 020203F4
  4490        .HS 010203F5
  4500        .HS 030203F6
  4510        .HS 000103F7
  4520        .HS 020103F9
  4530        .HS 010103FA
  4540        .HS 030103FB
  4550        .HS 000303FC
  4560        .HS 020303FD
  4570        .HS 010303FE
  4580        .HS 030303FF
  4590 *--------------------------------
  4600 TBUF   .BS 86
  4610 *--------------------------------
  4620 RWTS.TRACK    .HS 07
  4630 RWTS.SECTOR   .HS 0F
  4640 RWTS.ERROR    .HS 00
  4650 OLD.SLOT      .HS 60
  4660 CURRENT.TRACK .HS 07
  4670               .HS 00
  4680 *--------------------------------
  4690 OLD.TRACK.TABLE .EQ *-4
  4700        .HS 0000     SLOT 2, DRIVE 0--DRIVE 1
  4710        .HS 0000     SLOT 3
  4720        .HS 0000     SLOT 4
  4730        .HS 0000     SLOT 5
  4740        .HS 0E00     SLOT 6
  4750        .HS 0000     SLOT 7
  4760 *--------------------------------
  4770        .HS 00
  4780 *--------------------------------
  4790 SEARCH.COUNT      .BS 1
  4800 SEEK.COUNT        .BS 1
  4810 STEP.CNT      .EQ *
  4820 SEEK.D5.CNT   .EQ * 
  4830 X1X1X1X1          .BS 1   ALSO STEP.CNT & SEEK.D5.CNT
  4840 CHECK.SUM         .BS 1
  4850 HDR.CHKSUM        .BS 1
  4860 HDR.SECTOR        .BS 1
  4870 HDR.TRACK     .EQ *
  4880 MOTOR.TIME        .BS 2   ALSO HDR.TRACK & HDR.VOLUME
  4890 CURRENT.TRACK.OLD .BS 1
  4900 TARGET.TRACK      .BS 1
  4910 *--------------------------------
  4920 *      DELAY TIMES FOR ACCELERATION & DECELERATION
  4930 *      OF TRACK STEPPING MOTOR
  4940 *--------------------------------
  4950 ONTBL  .HS 01302824201E1D1C1C
  4960 OFFTBL .HS 702C26221F1E1D1C1C 
  4970 *--------------------------------
  4980 *      DELAY ABOUT 100*A MICROSECONDS
  4990 *      RUN DOWN MOTOR.TIME WHILE DELAYING
  5000 *--------------------------------
  5010 DELAY.100
  5020 .1     LDX #17
  5030 .2     DEX
  5040        BNE .2
  5050        INC MOTOR.TIME
  5060        BNE .3
  5070        INC MOTOR.TIME+1
  5080 .3     SEC
  5090        SBC #1
  5100        BNE .1
  5110        RTS
  5120 *--------------------------------
  5130 READ.ADDRESS
  5140        LDY #$FC     TRY 772 TIMES TO FIND $D5
  5150        STY SEEK.D5.CNT        (FROM $FCFC TO $10000)
  5160 .1     INY
  5170        BNE .2       ...KEEP TRYING
  5180        INC SEEK.D5.CNT
  5190        BEQ .11      ...THAT IS ENUF!
  5200 .2     LDA DRV.Q6L,X     GET NEXT BYTE
  5210        BPL .2
  5220 .3     CMP #$D5     IS IT $D5?
  5230        BNE .1       ...NO, TRY AGAIN
  5240        NOP          ...YES, DELAY
  5250 .4     LDA DRV.Q6L,X     GET NEXT BYTE
  5260        BPL .4
  5270        CMP #$AA     NOW NEED $AA AND $96
  5280        BNE .3       ...NO, BACK TO $D5 SEARCH
  5290        LDY #3       (READ 3 BYTES LATER)
  5300 .5     LDA DRV.Q6L,X     GET NEXT BYTE
  5310        BPL .5
  5320        CMP #$96     BETTER BE...
  5330        BNE .3       ...IT IS NOT
  5340        SEI          ...NO INTERRUPTS NOW
  5350        LDA #0       START CHECK SUM
  5360 .6     STA CHECK.SUM
  5370 .7     LDA DRV.Q6L,X     GET NEXT BYTE
  5380        BPL .7       1X1X1X1X
  5390        ROL          X1X1X1X1
  5400        STA X1X1X1X1
  5410 .8     LDA DRV.Q6L,X     GET NEXT BYTE
  5420        BPL .8       1Y1Y1Y1Y
  5430        AND X1X1X1X1   XYXYXYXY
  5440        STA HDR.CHKSUM,Y
  5450        EOR CHECK.SUM
  5460        DEY
  5470        BPL .6
  5480        TAY          CHECK CHECKSUM
  5490        BNE .11      NON-ZERO, ERROR
  5500 .9     LDA DRV.Q6L,X     GET NEXT BYTE
  5510        BPL .9
  5520        CMP #$DE     TRAILER EXPECTED $DE.AA.EB
  5530        BNE .11      NO, ERROR
  5540        NOP
  5550 .10    LDA DRV.Q6L,X
  5560        BPL .10
  5570        CMP #$AA
  5580        BNE .11      NO, ERROR
  5590        CLC
  5600        RTS
  5610 .11    SEC
  5620        RTS
  5630 *--------------------------------
  5640 READ.SECTOR
  5650        TXA          SLOT*16 ($60 FOR SLOT 6)
  5660        ORA #$8C     BUILD Q6L ADDRESS FOR SLOT
  5670        STA .9+1     STORE INTO READ-DISK OPS
  5680        STA .12+1
  5690        STA .13+1
  5700        STA .15+1
  5710        STA .18+1
  5720        LDA RWB.BUFFER    PLUG CALLER'S BUFFER
  5730        LDY RWB.BUFFER+1  ADDRESS INTO STORE'S
  5740        STA .17+1    PNTR FOR LAST THIRD
  5750        STY .17+2
  5760        SEC          PNTR FOR MIDDLE THIRD
  5770        SBC #84
  5780        BCS .1
  5790        DEY
  5800 .1     STA .14+1
  5810        STY .14+2
  5820        SEC          PNTR FOR BOTTOM THIRD
  5830        SBC #87
  5840        BCS .2
  5850        DEY
  5860 .2     STA .11+1
  5870        STY .11+2
  5880 *---FIND $D5.AA.AD HEADER--------
  5890        LDY #32      MUST FIND $D5 WITHIN 32 BYTES
  5900 .3     DEY
  5910        BEQ .10      ERROR RETURN
  5920 .4     LDA DRV.Q6L,X
  5930        BPL .4
  5940 .5     EOR #$D5
  5950        BNE .3
  5960        NOP
  5970 .6     LDA DRV.Q6L,X
  5980        BPL .6
  5990        CMP #$AA
  6000        BNE .5
  6010        NOP
  6020 .7     LDA DRV.Q6L,X
  6030        BPL .7
  6040        CMP #$AD
  6050        BNE .5
  6060 *---READ 86 BYTES INTO TBUF...TBUF+85----------
  6070 *---THESE ARE THE PACKED LOWER TWO BITS--------
  6080 *---FROM EACH BYTE OF THE CALLER'S BUFFER.-----
  6090        LDY #170
  6100        LDA #0       INIT RUNNING EOR-SUM
  6110 .8     STA RUNNING.SUM  
  6120 .9     LDX DRV.Q6L+MODIFIER  READ NEXT BYTE
  6130        BPL .9
  6140        LDA BYTE.TABLE,X      DECODE DATA
  6150        STA TBUF-170,Y
  6160        EOR RUNNING.SUM  
  6170        INY
  6180        BNE .8
  6190 *---READ NEXT 86 BYTES-------------------------
  6200 *---STORE 1ST 85 IN BUFFER...BUFFER+84---------
  6210 *---SAVE THE 86TH BYTE ON THE STACK------------
  6220        LDY #170
  6230        BNE .12      ...ALWAYS
  6240 *--
  6250 .10    SEC          I/O ERROR EXIT
  6260        RTS
  6270 *--
  6280 .11    STA BUFF.BASE-171,Y
  6290 .12    LDX DRV.Q6L+MODIFIER  READ NEXT BYTE
  6300        BPL .12   
  6310        EOR BYTE.TABLE,X      DECODE DATA
  6320        LDX TBUF-170,Y        MERGE LOWER 2 BITS
  6330        EOR BIT.PAIR.TABLE,X
  6340        INY
  6350        BNE .11
  6360        PHA          SAVE LAST BYTE (LATER BUFFER+85)
  6370 *---READ NEXT 86 BYTES-----------
  6380 *---STORE AT BUFFER+86...BUFFER+171------------
  6390        AND #$FC              MASK FOR RUNNING EOR.SUM
  6400        LDY #170
  6410 .13    LDX DRV.Q6L+MODIFIER  READ NEXT BYTE
  6420        BPL .13   
  6430        EOR BYTE.TABLE,X      DECODE DATA
  6440        LDX TBUF-170,Y        MERGE LOWER 2 BITS
  6450        EOR BIT.PAIR.TABLE+1,X
  6460 .14    STA BUFF.BASE-84,Y
  6470        INY
  6480        BNE .13   
  6490 *---READ NEXT 84 BYTES-------------------------
  6500 *---INTO BUFFER+172...BUFFER+255---------------
  6510 .15    LDX DRV.Q6L+MODIFIER  READ NEXT BYTE
  6520        BPL .15   
  6530        AND #$FC
  6540        LDY #172
  6550 .16    EOR BYTE.TABLE,X      DECODE DATA
  6560        LDX TBUF-172,Y        MERGE LOWER 2 BITS
  6570        EOR BIT.PAIR.TABLE+2,X
  6580 .17    STA BUFF.BASE,Y
  6590 .18    LDX DRV.Q6L+MODIFIER  READ NEXT BYTE
  6600        BPL .18   
  6610        INY
  6620        BNE .16   
  6630        AND #$FC
  6640 *---END OF DATA------------------
  6650        EOR BYTE.TABLE,X      DECODE DATA
  6660        BNE .20           ...BAD CHECKSUM
  6670        LDX SLOT.X16      CHECK FOR TRAILER $DE
  6680 .19    LDA DRV.Q6L,X
  6690        BPL .19   
  6700        CMP #$DE
  6710        CLC
  6720        BEQ .21           ...GOOD READ!
  6730 .20    SEC               ...SIGNAL BAD READ
  6740 .21    PLA               STORE BYTE AT BUFFER+85
  6750        LDY #85
  6760        STA (RWB.BUFFER),Y
  6770        RTS
  6780 *--------------------------------
  6790 UPDATE.TRACK.TABLE
  6800        JSR GET.SSSD.IN.X
  6810        STA OLD.TRACK.TABLE,X
  6820        RTS
  6830 *--------------------------------
  6840 CHECK.IF.MOTOR.RUNNING
  6850        LDX SLOT.X16  
  6860 CHECK.IF.MOTOR.RUNNING.X
  6870        LDY #0
  6880 .1     LDA DRV.Q6L,X     READ CURRENT INPUT REGISTER
  6890        JSR .2            ...12 CYCLES...
  6900        PHA               ...7 MORE CYCLES...
  6910        PLA
  6920        CMP DRV.Q6L,X     BY NOW INPUT REGISTER
  6930        BNE .2            SHOULD HAVE CHANGED
  6940        LDA #$28     ERROR CODE: NO DEVICE CONNECTED
  6950        DEY          BUT TRY 255 MORE TIMES
  6960        BNE .1       ...RETURN .NE. IF MOVING...
  6970 .2     RTS          ...RETURN .EQ. IF NOT MOVING...
  6980 *--------------------------------
  6990 GET.SSSD.IN.X
  7000        PHA          SAVE A-REG
  7010        LDA RWB.SLOT     DSSSXXXX
  7020        LSR
  7030        LSR
  7040        LSR
  7050        LSR          0000DSSS
  7060        CMP #8       SET CARRY IF DRIVE 2
  7070        AND #7       00000SSS
  7080        ROL          0000SSSD
  7090        TAX          INTO X-REG
  7100        PLA          RESTORE A-REG
  7110        RTS
  7120 *--------------------------------
  7130 WRITE.SECTOR
  7140        SEC          IN CASE WRITE-PROTECTED
  7150        LDA DRV.Q6H,X
  7160        LDA DRV.Q7L,X
  7170        BPL .1       ...NOT WRITE PROTECTED
  7180        JMP WS.RET   ...PROTECTED, ERROR
  7190 *--------------------------------
  7200 .1     LDA TBUF
  7210        STA TBUF.0  
  7220 *---WRITE 5 SYNC BYTES-----------
  7230        LDA #$FF
  7240        STA DRV.Q7H,X
  7250        ORA DRV.Q6L,X
  7260        LDY #4
  7270        NOP          $FF AT 40-CYCLE INTERVALS LEAVES
  7280        PHA          TWO ZERO-BITS AFTER EACH $FF
  7290        PLA
  7300 .2     PHA
  7310        PLA
  7320        JSR WRITE2
  7330        DEY
  7340        BNE .2
  7350 *---WRITE $D5 AA AD HEADER-------
  7360        LDA #$D5
  7370        JSR WRITE1
  7380        LDA #$AA
  7390        JSR WRITE1
  7400        LDA #$AD
  7410        JSR WRITE1
  7420 *---WRITE 86 BYTES FROM TBUF-------------------
  7430 *---BACKWARDS:   TBUF+85...TBUF+1, TBUF.0------
  7440        TYA          =0
  7450        LDY #86
  7460        BNE .4
  7470 .3     LDA TBUF,Y
  7480 .4     EOR TBUF-1,Y
  7490        TAX
  7500        LDA BIT.PAIR.TABLE+3,X
  7510        LDX SLOT.X16  
  7520        STA DRV.Q6H,X
  7530        LDA DRV.Q6L,X
  7540        DEY
  7550        BNE .3
  7560        LDA TBUF.0  
  7570 *---WRITE PORTION OF BUFFER------
  7580 *---UP TO A PAGE BOUNDARY--------
  7590        LDY #*-*     FILLED IN WITH LO-BYTE OF BUFFER ADDRESS
  7600 WS...5 EOR BUFF.BASE,Y   HI-BYTE FILLED IN
  7610        AND #$FC
  7620        TAX
  7630        LDA BIT.PAIR.TABLE+3,X
  7640 WS...6 LDX #MODIFIER
  7650        STA DRV.Q6H,X
  7660        LDA DRV.Q6L,X
  7670 WS...7 LDA BUFF.BASE,Y   HI-BYTE FILLED IN
  7680        INY
  7690        BNE WS...5
  7700 *---BRANCH ACCORDING TO BUFFER BOUNDARY CONDITIONS-----
  7710        LDA BYTE.AT.BUF00  
  7720        BEQ WS..17   ...BUFFER ALL IN ONE PAGE
  7730        LDA INDEX.OF.LAST.BYTE  
  7740        BEQ WS..16   ...ONLY ONE BYTE IN NEXT PAGE
  7750 *---MORE THAN ONE BYTE IN NEXT PAGE--------------------
  7760        LSR          ...DELAY TWO CYCLES
  7770        LDA BYTE.AT.BUF00  PRE.NYBBLE ALREADY ENCODED
  7780        STA DRV.Q6H,X      THIS BYTE
  7790        LDA DRV.Q6L,X
  7800        LDA BYTE.AT.BUF01  
  7810        NOP
  7820        INY
  7830        BCS WS..12
  7840 WS...8 EOR BUFF.BASE+256,Y    HI-BYTE FILLED IN
  7850        AND #$FC
  7860        TAX
  7870        LDA BIT.PAIR.TABLE+3,X
  7880 WS...9 LDX #MODIFIER
  7890        STA DRV.Q6H,X
  7900        LDA DRV.Q6L,X
  7910 WS..10 LDA BUFF.BASE+256,Y    HI-BYTE FILLED IN
  7920        INY
  7930 WS..11 EOR BUFF.BASE+256,Y    HI-BYTE FILLED IN
  7940 WS..12 CPY INDEX.OF.LAST.BYTE  
  7950        AND #$FC
  7960        TAX
  7970        LDA BIT.PAIR.TABLE+3,X
  7980 WS..13 LDX #MODIFIER
  7990        STA DRV.Q6H,X
  8000        LDA DRV.Q6L,X
  8010 WS..14 LDA BUFF.BASE+256,Y    HI-BYTE FILLED IN
  8020        INY
  8030        BCC WS...8
  8040        BCS .15      ...3 CYCLE NOP
  8050 .15    BCS WS..17   ...ALWAYS
  8060 *---WRITE BYTE AT BUFFER.00---------------------------
  8070 WS..16 .DA #$AD,BYTE.AT.BUF00    4 CYCLES: LDA BYTE.AT.BUF00
  8080        STA DRV.Q6H,X
  8090        LDA DRV.Q6L,X
  8100        PHA
  8110        PLA
  8120        PHA
  8130        PLA
  8140 WS..17 LDX LAST.BYTE  
  8150        LDA BIT.PAIR.TABLE+3,X
  8160 WS..18 LDX #MODIFIER
  8170        STA DRV.Q6H,X
  8180        LDA DRV.Q6L,X
  8190        LDY #0
  8200        PHA
  8210        PLA
  8220 *---WRITE DATA TRAILER:  $DE AA EB FF----------
  8230        NOP
  8240        NOP
  8250 .19    LDA DATA.TRAILER,Y
  8260        JSR WRITE3
  8270        INY
  8280        CPY #4
  8290        BNE .19
  8300        CLC          SIGNAL NO ERROR
  8310 WS.RET LDA DRV.Q7L,X     DRIVE TO SAFE MODE
  8320        LDA DRV.Q6L,X
  8330        RTS
  8340 *--------------------------------
  8350 WRITE1 CLC
  8360 WRITE2 PHA
  8370        PLA
  8380 WRITE3 STA DRV.Q6H,X
  8390        ORA DRV.Q6L,X
  8400        RTS
  8410 *--------------------------------
  8420 PRE.NYBBLE
  8430        LDA RWB.BUFFER    PLUG IN ADDRESS TO LOOP BELOW
  8440        LDY RWB.BUFFER+1  
  8450        CLC
  8460        ADC #2
  8470        BCC .1
  8480        INY
  8490 .1     STA PN...6+1
  8500        STY PN...6+2
  8510        SEC
  8520        SBC #$56
  8530        BCS .2
  8540        DEY
  8550 .2     STA PN...5+1
  8560        STY PN...5+2
  8570        SEC
  8580        SBC #$56
  8590        BCS .3
  8600        DEY
  8610 .3     STA PN...4+1
  8620        STY PN...4+2
  8630 *---PACK THE LOWER TWO BITS INTO TBUF-------------
  8640        LDY #170
  8650 PN...4 LDA BUFF.BASE-170,Y   ADDRESS FILLED IN
  8660        AND #3
  8670        TAX
  8680        LDA BIT.PAIR.RIGHT,X
  8690        PHA
  8700 PN...5 LDA BUFF.BASE-84,Y
  8710        AND #3
  8720        TAX
  8730        PLA
  8740        ORA BIT.PAIR.MIDDLE,X
  8750        PHA
  8760 PN...6 LDA BUFF.BASE+2,Y
  8770        AND #3
  8780        TAX
  8790        PLA
  8800        ORA BIT.PAIR.LEFT,X
  8810        PHA
  8820        TYA
  8830        EOR #$FF
  8840        TAX
  8850        PLA
  8860        STA TBUF,X
  8870        INY
  8880        BNE PN...4
  8890 *---DETERMINE BUFFER BOUNDARY CONDITIONS----------
  8900 *---AND SETUP WRITE.SECTOR ACCORDINGLY------------
  8910        LDY RWB.BUFFER  
  8920        DEY
  8930        STY INDEX.OF.LAST.BYTE  
  8940        LDA RWB.BUFFER  
  8950        STA WS...5-1
  8960        BEQ .7
  8970        EOR #$FF
  8980        TAY
  8990        LDA (RWB.BUFFER),Y
  9000        INY
  9010        EOR (RWB.BUFFER),Y
  9020        AND #$FC
  9030        TAX
  9040        LDA BIT.PAIR.TABLE+3,X
  9050 .7     STA BYTE.AT.BUF00    =0 IF BUFFER NOT SPLIT
  9060        BEQ .9
  9070        LDA INDEX.OF.LAST.BYTE  
  9080        LSR
  9090        LDA (RWB.BUFFER),Y
  9100        BCC .8
  9110        INY
  9120        EOR (RWB.BUFFER),Y
  9130 .8     STA BYTE.AT.BUF01  
  9140 .9     LDY #$FF
  9150        LDA (RWB.BUFFER),Y
  9160        AND #$FC
  9170        STA LAST.BYTE  
  9180 *---INSTALL BUFFER ADDRESSES IN WRITE.SECTOR------
  9190        LDY RWB.BUFFER+1  
  9200        STY WS...5+2
  9210        STY WS...7+2
  9220        INY
  9230        STY WS...8+2
  9240        STY WS..10+2
  9250        STY WS..11+2
  9260        STY WS..14+2
  9270 *---INSTALL SLOT*16 IN WRITE.SECTOR---------------
  9280        LDX SLOT.X16  
  9290        STX WS...6+1
  9300        STX WS...9+1
  9310        STX WS..13+1
  9320        STX WS..18+1
  9330        RTS
  9340 *--------------------------------
  9350 WAIT.FOR.OLD.MOTOR.TO.STOP
  9360        EOR OLD.SLOT      SAME SLOT AS BEFORE?
  9370        ASL               (IGNORE DRIVE)
  9380        BEQ .2            ...YES
  9390        LDA #1       LONG MOTOR.TIME
  9400        STA MOTOR.TIME+1  (COUNTS BACKWARDS)
  9410 .1     LDA OLD.SLOT
  9420        AND #$70
  9430        TAX
  9440        BEQ .2       ...NO PREVIOUS MOTOR RUNNING
  9450        JSR CHECK.IF.MOTOR.RUNNING.X
  9460        BEQ .2       ...NOT RUNNING YET
  9470        LDA #1       DELAY ANOTHER 100 USECS
  9480        JSR DELAY.100
  9490        LDA MOTOR.TIME+1
  9500        BNE .1       KEEP WAITING
  9510 .2     RTS
  9520 *--------------------------------

Qwerty 68000 Training/Development System Bob Sander-Cederlof

There is now a plethora of 68000 boards designed to fit inside, or nearly inside, your Apple. Names like DTACK Grounded, PDQ, Saybrook, and Acorn.

Most of these are aimed at hot-rodding your Apple. Some come with the UCSD p-System, including Pascal and an Applesoft- compatible BASIC and much more. Others have a more limited selection. Most are too costly for most of us, around $1500.

Motorola and others sell development systems based on the 68000 for $10K-30K. The Apple Lisa makes an excellent development system, at $6995 plus the developer's software kit (when it becomes available).

"Wait a minute! I don't even have a spare $1500, let alone $10K! And I want to get my feet wet first, before diving in over my head!"
"In fact, I want to try my hand at learning 68000 assembly language first. I need an assembler, some books, and a monitor with step and trace commands. I would like a hands-on tutorial I can work though at my own pace."
"I can't afford to lay out more than $750 right now. But I want an expandable system, that can grow with my knowledge and needs."

Guess what...somebody overheard our thoughts! Jerry Hansen and Lane Hauck, of Qwerty Inc., have put together a package deal too good to resist: a complete integrated training and software development package for only $695.

The package includes a card to plug in any slot of your Apple II, II Plus, or //e; a reference manual which leads you through the details of the card, their firmware, and the assembler; a full-fledged macro assembler; the best three reference books, with other booklets and reference cards. You can use the books in a hands-on tutorial fashion, mastering the 68000 assembly language as you go.

The Q-68 card is the heart of the package. It is a compact, well-crafted design, with a 68008 microprocessor, 2K bytes of RAM, and 8K bytes of EPROM. The full Apple address-space can be addressed by the 68008 as well, including any memory expansion cards you may have. RAM can be expanded on-board to 8K, and EPROM to 32K. A 50-pin expansion connector allows connection of additional memory, to a total of 1 megabyte.

You don't need any external power supply or chassis. The card draws a maximum of 400 milliamps. (While this is more than Apple will recommend, it seems to be well within the capability of the Apple power supply.) If you don't already have a cooling fan, you will probably want one after installing this card. The 68008 is the main power user, which fact makes me ever-so-hungry for a CMOS version.

The 68008 is a trimmed-down version of the 68000, with an 8-bit data bus. The instruction set is unchanged, but it comes in a smaller package: fewer pins, fewer milliamps, fewer dollars. On the Q-68 board, the 68008 is clocked at 7.16 MHz.

The Apple 6502 keeps running while the 68008 is executing code; when the 68008 refers to Apple memory, the 68008 slows down to wait for the Apple bus, and the Apple slows to half speed during that cycle. True multiprocessing is possible.

The Q-68 EPROM is loaded with good things. You get a comprehensive self-test facility, and an easy-to-use debugging monitor. The debugging monitor allows you to step and trace through your programs, and set breakpoints. There are five different display windows you can cycle through with a single keystroke: Register, Memory, Disassembly, and Breakpoint displays, and a helpful Command Summary.

Qwerty is aiming primarily at the those of us who want to learn 68000 programming and/or develop 68000 software without investing in an expensive complete 68000 system. However, there are many other exciting possibilities for this board. Those of you who really do want to speed up your Apple can certainly write code for the purpose. (Or maybe adapt public domain code already written for other 68K boards.) The Q-68 card may be used as a powerful controller or co-processor with your yet-to-be-written software. You can connect the Q-68 to the outside world directly, as well as through the Apple bus.

Now for something truly unique: the package comes with a special version of the S-C 68000 Cross Assembler. The S-C manual has been re-written to give 68000 code examples throughout. New commands have been added to start the Q-68 card, either in debug mode or at full speed. Three versions are included to provide different memory usage options.

What you get is a near optimum environment both for learning and for serious software development. Gone are the "load the editor, load-edit-save the source program, load the assembler, assemble, load the loader, load the object program, run into a bug, load the editor...." blues. With this package you simply edit, assemble, and run directly from RAM.

Programs too large for RAM can be assembled and loaded using multiple source and object files when necessary, but you still never need to reload the editor/assembler or monitor/debugger.

Current users of the S-C Assembler family already know the commands and editing techniques. You can concentrate on learning the 68000 itself, and the Qwerty debugger, without being distracted by a whole new operating system. (Later, when you can afford a Lisa or MacIntosh, you will already know the language and can concentrate on learning the operating system.)

Here is another new twist: Qwerty offers a free 30-day trial period. If you're not happy with the package for any reason, you can return it within 30 days in salable condition for a full refund. Qwerty, Inc. Phone (619) 569-5283.


A Look at the Aztec C Compiler for Apple DOS Bill Morgan

As I mentioned last month, I'm getting very interested in the C language. That August issue of Byte definitely turned me on, so I've started to look at ways to get C into my Apple.

Byte featured a comparative review of several C compilers for CP/M. One of the highest-rated was the Aztec C Compiler System, which is also available for Apple DOS 3.3. The Aztec compiler was given especially high marks for being truly complete and compatible with the standard for C, the book "The C Programming Language", by Kernigan and Ritchie.

I haven't had a chance to actually do any programming with the Aztec system yet, but, thanks to Donna Lamb, a subscriber in New York City, I was able to spend an afternoon looking over the manual. Here are some of my impressions.

Manual

The manual is 135 pages long in 5 chapters and 2 appendices:

Tutorial Intro - 15pp - Getting started, configuring and using the SHELL, compiling, assembling, linking and executing. A get-your-toes-damp intro to the system.

Shell - 22pp - The SHELL program resides in the language card, at $D000-$F7FF. It replaces the Command Interpreter portion of DOS 3.3 and provides a UNIX-like user interface, including I/O redirection and command parsing with argument passing.

Programs - 23pp - Using the editor, compilers, assemblers, linker, and utilities.

Libraries - 33pp - Discussion of the Standard I/O, System I/O, Utility, and Math Routines supplied with the system.

Technical Info - 28pp - Miscellaneous information on the internals of the system and the assembly-language interface. Manx promises continuing additions to this chapter, as part of the updates.

Appendices - 12pp - Error messages and examples of the compiler and assembler outputs for a simple program.

DOS 3.3 Interface

The disks you receive from Manx do not include DOS, so to enter the system you must first boot DOS, then BRUN SHELL.

SHELL overlays the DOS Command Interpreter and patches at least two (unspecified) points inside the File Manager. All the documentation has to say about non-standard (i.e., fast) DOS's is "try it and see." I am told that Diversi-DOS does not work; I don't know about others. Two Compilers for the Price of One

The Aztec system includes two separate compilers and two assemblers. There is a compiler/assembler pair for generating native 6502 code, and another compiler/assembler for an interpreted pseudo-code. The native code is fast but large, while the pseudo-code is slower but smaller. You can compile most of your program to pseudo-code, compile the time-critical parts to machine code, and write any extremely critical sections directly in assembly language. You can then link all these different object modules into one executable program.

Updates

The copy I saw was Version 1.05b of the Aztec system. Updates are available for an unspecified "nominal" fee, or an automatic update service is available for $50 per year.

Drawbacks

The people I have talked to who use the Aztec system regularly mention two drawbacks: compilation time and program size. Much of the compile time problem seems to be a matter of the Apple's disk speed, which can be improved.

The program size is related to the size of the run-time routines and the libraries included in a program. Experienced C programmers say that it is usually possible to manipulate the libraries to minimize the size of included code, but that is a fairly advanced technique.

ProDOS Version

There is supposed to be a ProDOS version of the Aztec system, which should be significantly faster, coming sometime. It's too soon to tell when that is likely to appear, so we'll just have to wait. The ProDOS version will be marketed as a completely separate version, rather than as an update to the DOS 3.3 version.

Conclusions

The Aztec C Compiler System is a full C compiler that runs in an Apple ][, and that makes it unique. Since my interest is in learning C and starting to develop programs that will be used on other, more powerful computers, I plan to place my order as soon as the ProDOS version is available.

All things considered, the Aztec system is not a great approach for developing applications intended only for use on Apple ][ computers. The Apple is simply too limited for full C.

Available from: Manx Software Systems, Box 55, Shrewsbury, NJ 07701. (201) 780-4004.


Hitachi 6301 Cross Support Bob Sander-Cederlof

As you probably know, we have a growing line of cross assemblers available. You can use your Apple as a development system without ever learning another editor/assembler/operating-system, on any of ten or more different chips.

It all started back in 1980 when Nigel Nathan paid me to create a 6801 cross assembler based on version 4.0 of the S-C Assembler II. Later Bob Urschel bought a copy. Back then we thought $300 a copy was a pretty good price.

All our competition in this field seems to agree. Avocet charges $200 or more per cross assembler. Byte magazine carries several ads showing prices for cross assemblers between $395 and $1000 apiece. Our assemblers are just as good, and many of you tell us ours are easier to use and more powerful. But we charge either $32.50 or $50 apiece, after you own the $80 S-C Macro Assembler.

Until very recently, the 6800/1/2 Macro Cross Assembler came with only one version on the disk. This one version assembled all of the opcodes of the 6801 chip. If you were programming for a 6800, which did not support all of those opcodes and addressing modes, it was a little dangerous. Last month we upgraded this disk by making two versions: one for 6800 only, and one for 6801.

Now I have added a third version for the Hitachi 6301. The 6301 is a CMOS chip, includes all the opcodes of the 6801, and adds six more:

       XGDX    Exchange D and X
       SLP     Sleep (reduced power mode)
       AIM     And Immediate into Memory
       OIM     Or Immediate into Memory
       EIM     Exclusive Or Immediate into Memory
       TIM     Test Memory Immediate

The last four each have two addressing modes. You can write "AIM #val,addr" or "AIM #val,addr,X". In both modes the address is only 8 bits. You can see that AIM lets you clear any bits in a memory byte; OIM lets you set any bits in a byte; EIM lets you toggle any bits; and TIM lets you test any bits. TIM forms the logical product (AND) of the memory byte and the immediate value, and tests for sign and zero.

The 6301 includes extensive memory mapped I/O on the chip, mapped into the zero page. With these "xIM" opcodes you have an extremely powerful I/O capability.

If you have the older disk of the 6800/1/2 cross assembler, and want to upgrade to get the 6301 version, send $5.


Killing the EXEC Bob Bragner
Istanbul, Turkey

Have you ever been at the beginning of the execution of a l-o-n-g EXEC file and realized you didn't really want to go through with it? There's not really much you can do. Control-C and RESET are ineffective even if you have an old Apple ][ without the Autostart ROM. On a //e you can hit Control-Open Apple-RESET, but at the expense of anything you may have in the Apple's memory -- a rather drastic solution.

As it turns out, there is a very easy way to terminate an EXEC file in progress. Apple DOS contains a single byte ($AAB3 when DOS is at its normal location) which is called "EXEC.STATUS". If the value of this byte is not 0 DOS thinks an EXEC file is in charge. If it is 0 then as far as DOS is concerned, no EXEC file is active. So we have the following little routine:

  1000 *SAVE S.KILL.EXEC
  1010 *--------------------------------
  1020 RESET        .EQ $3F2
  1030 SET.PWR.BYTE .EQ $FB6F
  1040 DOS.ENTRY    .EQ $3D0
  1050 EXEC.STATUS  .EQ $AAB3
  1060 *--------------------------------
  1070        .OR $300
  1080        .TF B.KILL.EXEC
  1090 *--------------------------------
  1100 INIT   LDA #KILL.EXEC
  1110        STA RESET
  1120        LDA /KILL.EXEC
  1130        STA RESET+1
  1140        JMP SET.PWR.BYTE
  1150 *--------------------------------
  1160 KILL.EXEC
  1170        LDA #0
  1180        STA EXEC.STATUS
  1190        JMP DOS.ENTRY

This routine can be reassembled to run anywhere. the INIT portion simply directs the RESET vector to the KILL.EXEC part of the routine and must be called before the EXEC command is issued. KILL.EXEC stores a 0 in the EXEC.STATUS flag and jumps to the DOS warm start at $3D0. Now if you hit RESET during an EXEC file's operation, the file will terminate politely.

Here is a series of POKES and a CALL that could be placed at the beginning of any EXEC program:

     POKE 1010,13 : POKE 1011,3 : CALL 64367
     POKE 781,169 : POKE 782,0 : POKE 783,141 : POKE 784,179
     POKE 785,170 : POKE 786,76 : POKE 787,208 : POKE 788,3
     (the rest of your program goes here)

This works from machine language, Integer BASIC, Applesoft, AND the S-C RAMcard Macro Assembler. The latter is a big help when you discover you're EXEC'ing the wrong 2000-line text file into the assembler, or you've forgotten to turn AUTO on!

[ Just a couple of comments: this trick won't work with an old non-Autostart ROM Apple, since you can't redirect RESET; and be sure to type the CLOSE command after the RESET, to free up the file buffer that the EXEC file was using. Bill ]


The Computer Hacker and Dataphile Digest Bob Sander-Cederlof

I received Vol 1 No 2 of the "Computer Hacker", and I think it will be a useful newsletter. As the magazines become more and more general, filled with reviews of packaged systems and software, we will have to look elsewhere for articles that get down to the nitty-gritty. Even local club newsletters are steering away from the hobbyist's or technician's needs.

The issue I have includes listings of a pair of programs to transfer data from one computer to another in the CP/M environment; part two of a detailed explanation of the RS-232 "standard"; part one of directions for building a hardware print spooler; a review of floppy disk formats; an Apple (6502) assembly language program for sending Morse code; and a beginner's introduction to electronics.

The Computer Hacker, 12 issues per year for $24, P. O. Box 1697, Kalispell, MT 59903.

Dataphile Digest is a monthly survey of Apple related periodicals. Bill & Shannon Bailey scan more than a dozen magazines each month, and write brief descriptions of each article relating to Apple computers. They organize the descriptions into categories that make it easy to find any topic you like. The second issue covered one or two issues of 14 different magazines, and included 840 entries organized into 38 categories.

Dataphile Digest is typeset, and printed the same size as Apple Assembly Line. The current issue is 78 pages (plus cover and contents pages), and bears a cover price of $3.50. No subscription price is given, so I would suggest writing to them at P. O. Box 2806, Del Mar, CA 92014. Or call at (619) 436-9382.


Shapemaker Enhancements Bob Sander-Cederlof

Frank Belanger sent me a new updated version of his Shapemaker Utility. He says it is now the best program of its type on the market, and he is really proud of it. Here are the new features:

Shapemaker is still just $35, from Frank at 4200 Avenue B, Austin, TX 78751.


ProDOS and Clock Drivers, with a
Commented Listing of ProDOS $F142-$F1BE
Bob Sander-Cederlof

ProDOS is a new operating system which Apple expects to release to the public during the first quarter of 1984. I am told that new computers and disk drives will be shipped with ProDOS rather than DOS 3.3. Version 1.0 is already available to licensed developers (I have it).

Apple has released massive amounts of documentation to licensed developers, and has even been offering a full day class at $225 per seat in various cities around the country. I attended the Dallas class on October 21st. Even with all the help they are giving, there are still a lot of unclear details that can only be illuminated by well-commented assembly listings of the actual ProDOS code. Apple will never publish these, so we will do it ourselves.

My first serious foray into ProDOS began at the request of Dan Pote, Applied Engineering. Dan wanted me to modify the firmware of his Timemaster clock card so that it automatically had full compatibility with ProDOS. Dan wanted all programs, even protected ones, which boot under ProDOS, to be able to read the date and time from his card. Also, he wanted ProDOS to time/date stamp the files in the directory with his card, just as it does with Thunderclock. (No small task, it turned out.)

ProDOS, when booting, searches the slots for a Thunderclock. If it finds one, it marks a bit in the machine ID byte (MACHID, bit 0 of $BF98 = 1); it plugs two bytes at $F14D and F150 with $CN, where N is the slot number; and it stores a JMP opcode ($4C) at $BF06.

$BF06 is a standard vector to whatever clock routine is installed. If no Thunderclock was found, an RTS opcode will be stored there.

The ProDOS boot slot search looks for these Thunderclock ID bytes:

       $CN00 = $08
       $CN02 = $28
       $CN04 = $58
       $CN08 = $70

After booting, ProDOS loads and executes the program called STARTUP. The standard STARTUP program searches the slots for various cards and displays a list of what it finds. Unfortunately this list seldom agrees with the true configuration in any of my computers. For one thing, STARTUP examines different bytes than the boot search does. In looking for a clock card, STARTUP wants:

       $CN00 = $08
       $CN01 = $78
       $CN02 = $28

If you do not have a Thunderclock, but do have some other clock, you have several options. What I did for Dan was change the firmware of Timemaster so that it emulates Thunderclock. ProDOS is convinced it has a Thunderclock, but you are saved the extra expense, and you gain extra features.

Another approach is to write a program which installs your own clock driver inside ProDOS. Mike Owen, of Austin, Texas, did this for Dan. After ProDOS boots it loads the first type SYS file it can find in the directory whose name ends with ".SYSTEM". Normally this is "BASIC.SYSTEM", which then proceeds to execute STARTUP. However, you can set up your disk with CLOCK.SYSTEM before BASIC.SYSTEM in the directory.

Write CLOCK.SYSTEM so that it begins at $2000, because all type SYS files load there. The program should mark the clock ID bit in MACHID, punch a JMP opcode at $BF06, and look at the address in $BF07,BF08. That address is the beginning of the clock driver inside the language card. Right now that address is $F142, but it could change.

Your program should write enable the language card by two "LDA $C081" instructions in a row, and then copy your clock driver into the space starting at that address. You can use up to 124 bytes. If your driver has references to the clock slot, be sure to modify them to the actual slot you are using. If your driver has internal references, be sure to modify them to point to the actual addresses inside the new physical location.

It is standard practice in peripheral firmware to use the following code to find out which slot the card is in:

       JSR $FF58     A Guaranteed $60 (RTS opcode)
       TSX           Stack pointer
       LDA $100,X    Get $CN off stack

Many cards also use "BIT $FF58" as a means for setting the V-bit in the status register. BE AWARE THAT ProDOS DOES NOT HAVE $60 AT $FF58 in the language card!!!!

The Thunderclock has two entries, at $CN08 and $CN0B, which assume that $CN is already in the X-register. $CN0B allows setting the clock mode, and $CN08 reads the clock in the current mode. The ProDOS driver calls on these two entries, as the following listing shows.

ProDOS maintains a full page at $BF00 called the System Global Page. The definition of this page should not change, ever. They say. Locations $BF90-BF93 contain the current date and time in a packed format. A system call will read the clock, if a driver is installed, and format the year-month-day-hour-minute into these four bytes.

Now here is a listing of the current Thunderclock driver, as labelled and commented by me.

  1000 *SAVE S.PRODOS $F142...$F1BE
  1010 *--------------------------------
  1020 *  IF THE PRODOS BOOT RECOGNIZES A THUNDERCLOCK,
  1030 *  A "JMP $F142" IS INSTALLED AT $BF06 AND
  1040 *  THE SLOT ADDRESS IS PATCHED INTO THE FOLLOWING
  1050 *  CODE AT SLOT.A AND SLOT.B BELOW.
  1060 *--------------------------------
  1070 DATE   .EQ $BF90    $BF91 = YYYYYYYM
  1080 *                   $BF90 = MMMDDDDD
  1090 TIME   .EQ $BF92    $BF93 = 000HHHHH
  1100 *                   $BF92 = 00MMMMMM
  1110 MODE   .EQ $5F8-$C0 THUNDERCLOCK MODE IN SCREEN HOLE
  1120 *--------------------------------
  1130        .OR $F142
  1140        .TA $800
  1150 *--------------------------------
  1160 PRODOS.THUNDERCLOCK.DRIVER
  1170        LDX SLOT.B   $CN
  1180        LDA MODE,X   SAVE CURRENT THUNDERCLOCK MODE
  1190        PHA
  1200        LDA #$A3     SEND "#" TO THUNDERCLOCK TO
  1210        JSR $C20B       SELECT INTEGER MODE
  1220 SLOT.A .EQ *-1
  1230 *--------------------------------
  1240 *      READ TIME & DATE INTO $200...$211 IN FORMAT:
  1250 *--------------------------------
  1260        JSR $C208
  1270 SLOT.B .EQ *-1
  1280 *--------------------------------
  1290 *      CONVERT ASCII VALUES TO BINARY
  1300 *      $3E -- MINUTE
  1310 *      $3D -- HOUR
  1320 *      $3C -- DAY OF MONTH
  1330 *      $3B -- DAY OF WEEK
  1340 *      $3A -- MONTH
  1350 *--------------------------------
  1360        CLC
  1370        LDX #4
  1380        LDY #12      POINT AT MINUTE
  1390 .1     LDA $200,Y   TEN'S DIGIT
  1400        AND #$07     IGNORE TOP BIT
  1410        STA $3A      MULTIPLY DIGIT BY TEN
  1420        ASL          *2
  1430        ASL          *4
  1440        ADC $3A      *5
  1450        ASL          *10
  1460        ADC $201,Y   ADD UNIT'S DIGIT
  1470        SEC
  1480        SBC #$B0     SUBTRACT ASCII ZERO
  1490        STA $3A,X    STORE VALUE
  1500        DEY          BACK UP TO PREVIOUS FIELD
  1510        DEY
  1520        DEY
  1530        DEX          BACK UP TO PREVIOUS VALUE
  1540        BPL .1       ...UNTIL ALL 5 FIELDS CONVERTED
  1550 *--------------------------------
  1560 *      PACK MONTH AND DAY OF MONTH,
  1570 *--------------------------------
  1580        TAY          MONTH (1...12)
  1590        LSR          00000ABC--D
  1600        ROR          D00000AB--C
  1610        ROR          CD00000A--B
  1620        ROR          BCD00000--A
  1630        ORA $3C      MERGE DAY OF MONTH
  1640        STA DATE     SAVE PACKED DAY AND MONTH
  1650        PHP          SAVE TOP BIT OF MONTH
  1660 *--------------------------------
  1670 *      CONVERT MONTH, DAY OF MONTH,
  1680 *      AND DAY OF WEEK INTO YEAR.
  1690 *--------------------------------
  1700        AND #$1F     ISOLATE DAY OF MONTH (1...31)
  1710 *      CARRY SET FOR MONTHS 8...12
  1720        ADC YEAR.DAY,Y    COMPUTE DAY OF YEAR
  1730        BCC .2
  1740        ADC #3    ADJUST REMAINDER FOR YEARDAY > 255
  1750 .2     SEC          GET REMAINDER MODULO 7
  1760 .3     SBC #7
  1770        BCS .3       ...UNTIL ALL 7'S REMOVED
  1780        ADC #7       RESTORE TO POSITIVE VALUE
  1790        SBC $3B      SUBTRACT KNOWN DAY OF WEEK
  1800        BCS .4       NO BORROW
  1810        ADC #7       BORROWED, SO ADD 7 BACK
  1820 .4     TAY          ADJUSTED DAY OW WEEK AS INDEX
  1830        LDA YRTBL,Y  GET YEAR (82...87)
  1840        PLP          GET HIGH BIT OF MONTH IN CARRY
  1850        ROL          FORM YYYYYYYM
  1860        STA DATE+1
  1870        LDA $3D      GET HOUR
  1880        STA TIME+1
  1890        LDA $3E      GET MINUTE
  1900        STA TIME
  1910        PLA          RESTORE THUNDERCLOCK MODE
  1920        LDX SLOT.B   GET $CN FOR INDEX
  1930        STA MODE,X
  1940        RTS
  1950 *--------------------------------
  1960 YEAR.DAY   .EQ *-1  OFFSET BECAUSE INDEX 1...12
  1970    .DA #0,#31,#59,#90      JAN,FEB,MAR,APR
  1980    .DA #120,#151,#181,#211 MAY,JUN,JUL,AUG
  1990    .DA #242,#20,#51,#81    SEP,OCT,NOV,DEC
  2000 *--------------------------------
  2010 YRTBL  .DA #84,#84,#83,#82,#87,#86,#85
  2020 *--------------------------------

Lower Case Titles Revisited Bill Morgan

Last month we published Bob Matzinger's patch to Version 1.1 of the Macro Assembler to allow lower-case characters in a .TItle line. The article contained this sentence: "Here is a hex dump of the code, with a square around the byte to be changed:" But I forgot to draw the square on the page!

Here is that section of code again, this time with the square drawn in:

For the benefit of this web-publication, here it is with the byte in red, instead of in a box:

       A2 00     LDX #0
       20 3E x2  JSR $123E or $D23E
       C9 2C     CMP #$2C
       D0 0D     BNE ...

       20 3E x2  JSR $123E or $D23E

       B0 08     BCS ...
       9D 70 01  STA $170,X       

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 $15 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $13 postage for other countries. Back issues are available for $1.50 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE, all rights reserved. Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof. (Apple is a registered trademark of Apple Computer, Inc.)