Volume 4 -- Issue 2 | November 1983

In This Issue...

- Commented Listing of ProDOS $F800-F90B, $F996-FEBD
- Qwerty 68000 Training/Development System
- A Look at the Aztec C Compiler for Apple DOS
- Hitachi 6301 Cross Support
- Killing the EXEC
- The Computer Hacker and Dataphile Digest
- Shapemaker Enhancements
- ProDOS and Clock Drivers
- Lower Case Titles Revisited

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:

- Clearer, more accessible HELP screens.
- RENUMBER command in the Shape Editor.
- Two grid sizes: 18x30 and 24x40.
- Hi-Res Dump for Epson printer, accessible both in Shapemaker and with an &-command.
- Four new typefaces (total now 9).
- Manual now 55 pages long.

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