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