< Apple Assembly Line - V4N3 - Dec 1983 Apple Assembly Line
Volume 4 -- Issue 3 December 1983

In This Issue...

Demise of Bailey's DataPhile Digest

Unfortunately, we no sooner sent out last month's AAL than we received a letter from the Baileys saying that they have ceased to publish the DataPhile Digest.

Quarterly Disk 13

QD 13 is now ready, and it includes both installments of ProDOS commented source code as listed last month and this. The code is in the format used by the S-C Macro Assembler. (Since the disk also includes the CONVERT S-C TO TEXT program in this issue, all of you can use it!) Quarterly Disks are $15 each, or $45 for a year's subscription.

Subscription Rates

Remember, subscriptions to Apple Assembly Line will be increasing to $18/year effective January 1. Since some of you may not receive this issue (or your renewal notice) until after that date, we'll extend the deadline to January 15 for renewals.


Commented Listing of ProDOS
$F90C-F995, $FD00-FE9A, $FEBE-FFFF
Bob Sander-Cederlof

Last month I printed the commented listing of the disk reading subroutines. This month's selection covers disk writing, track positioning, and interrupt handling. Together the two articles cover all the code between $F800 and $FFFF.

Several callers have wondered if this is all there is to ProDOS. No! It is only a small piece. In my opinion, this is the place to start in understanding ProDOS's features: A faster way of getting information to and from standard floppies. But remember that ProDOS also supports the ProFILE hard disk, and a RAM disk in the extended Apple //e memory.

Further, ProDOS has a file structure exactly like Apple /// SOS, with a hierarchical directory and file sizes up to 16 megabytes.

Further, ProDOS includes support for a clock/calendar card, 80-columns with Smarterm or //e, and interrupts.

ProDOS uses or reserves all but 255 bytes of the 16384 bytes in the language card area (both $D000-DFFF banks and all #E000-FFFF). The 255 bytes not reserved are from $D001 through $D0FF in one of the $D000 banks. The byte at $D000 is reserved, because ProDOS uses it to distinguish which $D000 bank is switched on when an interrupt occurs. The space at $BF00-BFFF is used by ProDOS for system linkages and variables (called the System Global Page).

In addition, if you are using Applesoft, ProDOS uses memory from $9600-BEFF. This space does not include any file buffers. When you OPEN files, buffers are allocated as needed. CLOSEing automatically de-allocates buffers. Each buffer is 1024 bytes long. As you can see, with ProDOS in place your Applesoft program has less room than ever.

Track Seeking: $F90C-F995

The SEEK.TRACK subroutine begins at $F90C. The very first instruction multiplies the track number by two, converting ProDOS logical track number to a physical track number. If you want to access a "half-track" position, you could either store a NOP opcode at $F90C, or enter the subroutine at $F90D.

A table is maintained of the current track position for each of up to 12 drives. I call it the OLD.TRACK.TABLE. The subroutine GET.SSSD.IN.X forms an index into OLD.TRACK.TABLE from slot# * 2 + drive#. There are no entries in the table for drives in slots 0 or 1, which is fine with me. ProDOS uses these slots as pseudo slots for the RAM-based pseudo-disk and for ProFILE, if I remember correctly.

The code in SEEK.TRACK.ABSOLUTE is similar but not identical to code in DOS 3.3. The differences do not seem to be significant.

Disk Writing: $FD00-FE9A

The overall process of writing a sector is handled by code in RWTS, which was listed last month. After the desired track is found, RWTS calls PRE.NYBBLE to build a block of 86 bytes containing the low-order two bits from each byte in the caller's buffer. PRE.NYBBLE also stores a number of buffer addresses and slot*16 values inside the WRITE.SECTOR subroutine. Next RWTS calls READ.ADDRESS to find the sector, and then WRITE.SECTOR to put the data out.

WRITE.SECTOR is the real workhorse. And it is very critically timed. Once the write head in your drive is enabled, every machine cycle is closely counted until the last byte is written. First, five sync bytes are written (ten bits each, 1111111100). These are written by putting $FF in the write register at 40 cycle intervals. Following the sync bytes W.S writes a data header of D5 AA AD.

Second, the 86-byte block which PRE.NYBBLE built is written, followed by the coded form of the rest of your buffer. WRITE.SECTOR picks up bytes directly from your buffer, keeps a running checksum, encodes the high-order six bits into an 8-bit value, and writes it on the disk...one byte every 32 cycles, exactly. Since your buffer can be any arbitrary place in memory, and since the 6502 adds cycles for indexed instructions that cross page boundaries, WRITE.SECTOR splits the buffer in parts before and after a page boundary. All the overhead for the split is handled in PRE.NYBBLE, before the timed operations begin.

Finally, the checksum and a data trailer of DE AA EB FF are written.

Empty Space: $FEBE-FF9A

This space had no code in it. Nearly a whole page here.

Interrupt & RESET Handling: $FF9B-FFFF

If the RAM card is switched on when an interrupt or RESET occurs, the vectors at $FFFA-FFFF will be those ProDOS installed rather than the ones permanently coded in ROM. It turns out the non-maskable interrupt (NMI) is still vectored down into page 3. But the more interesting IRQ interrupt is now vectored to code at $FF9B inside ProDOS.

The ProDOS IRQ handler performs two functions beyond those built-in to the monitor ROM. First, the contents of location $45 are saved so that the monitor can safely clobber it. Second, a flag is set indicating which $D000 bank is currently switched on, so that it can be restored after the interrupt handler is finished. (The second step is omitted if the interrupt was caused by a BRK opcode.)

If the IRQ was not due to a BRK opcode, a fake "RTI" vector is pushed on the stack. This consists of a return address of $BF50 and a status of $04. The status keeps IRQ interrupts disabled, and $BF50 is a short routine which turns the ProDOS memory back on and jumps up to INT.SPLICE at $FFD8:

       BF50-  8D 8B C0  STA $C08B
       BF53-  4C D8 FF  JMP $FFD8

Of course, before coming back via the RTI, ProDOS tries to USE the interrupt. If you have set up one or more interrupt vectors with the ProDOS system call, they will be called.

INT.SPLICE restores the contents of $45 and switches the main $D000 bank on. Then it jumps back to $BFD3 with the information about which $D000 bank really should be on. $BFD3 turns on the other bank if necessary, and returns to the point at which the interrupt occured.

The instruction at $FFC8 is interesting. STA $C082 turns on the monitor ROM, so the next instruction to be executed is at $FFCB in ROM. This is an RTS opcode, so the address on the stack at that point is used. There are two possible values: $FA41 if an IRQ interrupt is being processed, or $FA61 if a RESET is being processed. This means the RTS will effectively branch to $FA42 or $FA62.

Uh Oh! At this point you had better hope that you are not running with the original Apple monitor ROM. The Apple II Plus ROM (called Autostart Monitor) and the Apple //e ROM are fine. $FA42 is the second instruction of the IRQ code, and $FA62 is the standard RESET handler. But the original ROM, like I have in my serial 219 machine, has entirely different code there.

I have an $FF at $FA42, followed by code for the monitor S (single step) command. And $FA62 is right in the middle of the S command. There is no telling what might happen, short of actually trying it out. No thanks. Just remember that RESET, BRK, and IRQ interrupts will not work correctly if they happen when the RAM area is switched on and you have the old original monitor in ROM.

There is another small empty space from $FFE9 through $FFF9, 17 bytes.

Perhaps I should point out that the listings this month and last are from the latest release of ProDOS, which may not be the final released version. However, I would expect any differences in the regions I have covered so far to be slight.

[ In this web edition I have included the entire code, instead of just the pieces not printed in the November 1983 issue.]
  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
  2940 *--------------------------------
  2950 SEEK.TRACK
  2960        ASL          GET PHYSICAL TRACK #
  2970        STA HDR.TRACK     ...SAVE HERE
  2980        JSR CLEAR.PHASES  (CARRY WAS CLEAR)
  2990        JSR GET.SSSD.IN.X
  3000        LDA OLD.TRACK.TABLE,X
  3010        STA CURRENT.TRACK
  3020        LDA HDR.TRACK
  3030        STA OLD.TRACK.TABLE,X
  3040        JSR SEEK.TRACK.ABSOLUTE
  3050 *--------------------------------
  3060 CLEAR.PHASES
  3070        LDY #3
  3080 .1     TYA
  3090        JSR PHASE.COMMANDER
  3100        DEY
  3110        BPL .1
  3120        LSR CURRENT.TRACK    BACK TO LOGICAL TRACK #
  3130        CLC          SIGNAL NO ERROR
  3140        RTS
  3150 *--------------------------------
  3160 SEEK.TRACK.ABSOLUTE
  3170        STA TARGET.TRACK  SAVE ACTUAL TRACK #
  3180        CMP CURRENT.TRACK ALREADY THERE?
  3190        BEQ .7            ...YES
  3200        LDA #0
  3210        STA STEP.CNT      # STEPS SO FAR
  3220 .1     LDA CURRENT.TRACK
  3230        STA CURRENT.TRACK.OLD
  3240        SEC
  3250        SBC TARGET.TRACK
  3260        BEQ .6            ...WE HAVE ARRIVED
  3270        BCS .2            CURRENT > DESIRED
  3280        EOR #$FF          CURRENT < DESIRED
  3290        INC CURRENT.TRACK
  3300        BCC .3            ...ALWAYS
  3310 .2     ADC #$FE          .CS., SO A=A-1
  3320        DEC CURRENT.TRACK
  3330 .3     CMP STEP.CNT GET MINIMUM OF:
  3340        BCC .4         1. # OF TRACKS TO MOVE LESS 1
  3350        LDA STEP.CNT   2. # OF STEPS SO FAR
  3360 .4     CMP #9         3. EIGHT
  3370        BCS .5
  3380        TAY
  3390        SEC          TURN NEW PHASE ON
  3400 .5     JSR .7
  3410        LDA ONTBL,Y  DELAY
  3420        JSR DELAY.100
  3430        LDA CURRENT.TRACK.OLD
  3440        CLC          TURN OLD PHASE OFF
  3450        JSR PHASE.COMMANDER
  3460        LDA OFFTBL,Y DELAY
  3470        JSR DELAY.100
  3480        INC STEP.CNT # OF STEPS SO FAR
  3490        BNE .1       ...ALWAYS
  3500 .6     JSR DELAY.100
  3510        CLC          TURN PHASE OFF
  3520 .7     LDA CURRENT.TRACK
  3530 *--------------------------------
  3540 *      (A) = TRACK #
  3550 *      .CC. THEN PHASE OFF
  3560 *      .CS. THEN PHASE ON
  3570 *--------------------------------
  3580 PHASE.COMMANDER
  3590        AND #3       ONLY KEEP LOWER TWO BITS
  3600        ROL               00000XXC
  3610        ORA SLOT.X16      0SSS0XXC
  3620        TAX
  3630        LDA DRV.PHASE,X
  3640        LDX SLOT.X16      RESTORE SLOT*16
  3650        RTS
  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 *--------------------------------
  9530        .BS $FF9B-*       <<<<EMPTY SPACE>>>>
  9540 *--------------------------------
  9550 IRQ
  9560        PHA          SAVE A-REG
  9570        LDA $45     SAVE LOC $45
  9580        STA SAVE.LOC45
  9590        PLA          SAVE A-REG AT LOC $45
  9600        STA $45  
  9610        PLA          GET STATUS BEFORE IRQ
  9620        PHA
  9630        AND #$10     SEE IF "BRK"
  9640        BNE .2       ...YES, LET MONITOR DO IT
  9650        LDA $D000    SAVE $D000 BANK ID
  9660        EOR #$D8
  9670        BEQ .1
  9680        LDA #$FF
  9690 .1     STA INTBANKID
  9700        STA SAVE.D000
  9710        LDA #$BF     PUSH FAKE "RTI" VECTOR WITH
  9720        PHA               IRQ DISABLED
  9730        LDA #$50          AND SET TO RETURN TO $BF50
  9740        PHA
  9750        LDA #4
  9760        PHA
  9770 .2     LDA #$FA     PUSH "RTS" VECTOR FOR MONITOR
  9780        PHA
  9790        LDA #$41
  9800        PHA
  9810 CALL.MONITOR
  9820        STA $C082    SWITCH TO MOTHERBOARD
  9830 *--------------------------------
  9840 RESET
  9850        LDA RESET.VECTOR+1
  9860        PHA          PUSH "RTS" VECTOR FOR MONITOR
  9870        LDA RESET.VECTOR
  9880        PHA
  9890        JMP CALL.MONITOR
  9900 *--------------------------------
  9910 RESET.VECTOR
  9920        .DA $FA61    MON.RESET-1
  9930 *--------------------------------
  9940 INT.SPLICE
  9950        STA INTAREG
  9960        LDA SAVE.LOC45
  9970        STA $45  
  9980        LDA $C08B    SWITCH TO MAIN $D000 BANK
  9990        LDA SAVE.D000
  10000        JMP IRQXIT.3
  10010 *--------------------------------
  10020        .BS $FFFA-*       <<<<<EMPTY SPACE>>>>>
  10030 *--------------------------------
  10040 V.NMI      .DA $03FB
  10050 V.RESET    .DA RESET
  10060 V.IRQ      .DA IRQ
  10070 *--------------------------------


More Assembly Listing into Text Files Tracy L. ShaferMacDill AFB, FL

In the October '83 issue of AAL, Robert F. O'Brien presented a way to create a text file containing the assembly listing of a large program. (See also "Assembly Listing Into a Text File", by Bill Morgan, July '83 AAL.) Actually, he created several text files; one for each .IN directive in the root file. You can't put the whole listing into one text file by using one .TF directive because of the way the .IN directive affects the DOS I/O hooks.

Robert's method for obtaining assembly listing text files is good, but I found a different way to create the text files of assembly listings that doesn't involve creating separate SYMBOLS sections, deleting duplicate labels, and putting up with "EXTRA DEFINITIONS ERROR" messages. It's a fairly simple approach and hinges on the fact that the problem presented by the .IN directive affects the source file containing the .IN, but not the source file to which the .IN refers. Instead of putting one .TF directive in the root file, put a .TF in each source file pointed to by a .IN directive.

For example:

        ROOT FILE

        1000    .DU
        1010    .IN PART 1
        1020    .IN PART 2
        1030    .ED

        PART 1

        1000    .TF LISTING 1
        1010    (source for part 1)

        PART 2

        1000    .TF LISTING 2
        1010    (source for part 2)

From here on, follow Bill Morgan's original instructions. What follows is a summary of those instructions.

After deleting all other .TF directives, or turning them into comments by inserting "*" at the beginning of the line, typing ASM will create two binary files named LISTING 1 and LISTING 2. Each of these contains the assembly listing of PART 1 and PART 2 respectively, in text form. These binary files will not have starting address and length in the first four bytes. DO NOT attempt to BLOAD these files. You could really clobber DOS. To obtain true text files, make the following patch to the S-C Assembler before you assemble the program:

        $1000 versions:  $29DF:0  (original value is 04)
        $D000 versions:  $C083 C083 EAF9:0 N C083

After the patch is made, assemble the program and restore the original value to $29DF ($EAF9).

For really large programs, it could get very tedious adding a .TF directive to each sub-file to obtain a text file listing and then deleting those .TF directives to prevent messing up the object file the next time the program is assembled. Fortunately, the S-C Macro Assembler's conditional assembly feature makes our work a lot easier. By placing an equated flag in the root file and surrounding each .TF with .DO and .FIN, we only have to change one line to set up our program for text file output or object file creation. For example:

        ROOT FILE

        1000 LSTOUT .EQ 0       TO ASSEMBLE OBJECT
        1010 *          1       TO OUTPUT TEXT FILES
        1020        .DO LSTOUT
        1040        .DU
        1050        .ELSE
        1060        .TF OBJECT FILE
        1070        .FIN
        1080        .IN PART 1
        1090        .IN PART 2
        1100        .DO LSTOUT
        1110        .ED
        1120        .FIN

        PART 1

        1000        .DO LSTOUT
        1010        .TF LISTING 1
        1020        .FIN
        1030        (source for part 1)

        PART 2

        1000        .DO LSTOUT
        1010        .TF LISTING 2
        1020        .FIN
        1030        (source for part 2)

Don't forget to patch $29DF ($EAF9 for the language card version) with 0 to output true text files and back to 04 create object files. The last thing to remember is to use .LIST ON during the assembly. You won't write any text files if the assembler isn't producing a listing.


Note on Aztec C Bill Morgan

I just talked to the people at Manx Software about the ProDOS version of their C compiler, and this time they assured me that owners of the current Apple DOS version will be able to purchase the ProDOS version at a reduced upgrade price. That is enough to tip the balance in favor of buying the compiler right now, so I have ordered some. List price is $199: we'll have them for $180 + shipping.


Generalized GOTO and GOSUB Bob Sander-Cederlof

Tim Mowchanuk, a lecturer at Brisbane College in Australia, sent the following suggestion:

"How can I implement a named GOTO or GOSUB routine? There are numerous routines that implement computed GOTO/GOSUB, but I consider that a futile exercise. Computed GOTO/GOSUB mess up renumbering utilities, and violate modern trends toward structured programming.

"What I really want is something that will handle BASIC like

       100 & GOSUB NAME$

where NAME$ holds the name of a subroutine. I envision subroutine names being defined by a special REM statement of the form

       200 REM "SUBROUTINE NAME"

The &GOSUB or &GOTO processor can search through the program for a line beginning with a REM token. If the first non-blank after the REM token is a quotation mark, the processor can compare the characters to the string value. If there is an exact match, the line containing the REM is the target for the &GOTO or &GOSUB."

The problem sounded just the right size for an interesting AAL article, so I started trying to write some code.

I published an &GOSUB routine back in April 1981 of the type that Tim thinks futile. The following program combines the two "futile" computed &GOSUB and &GOTO routines with two new ones that allow the computed value to be a string expression. If the expression after &GOTO or &GOSUB is numeric, the processor will search for a matching line number. If the result is a string, the processor will search for a REM label as Tim described above.

Only REM's at the beginning of a numbered line will be considered as labels. The label must be included in quotation marks. Spaces are OK between the word REM and the first quotation mark. Anything after the second quotation mark will be ignored.

You can now write a menu program that uses the actual command word as the name of a subroutine, and cease worrying about line numbers. The accompanying Applesoft program is an example of just such a technique.

     100  PRINT  CHR$ (4)"BLOAD B.LABELLED GO'S": CALL 768
     1000  DATA  SEND,RECEIVE,EDIT,LOAD,SAVE,EXIT,.
     1010 I = 0
     1020 I = I + 1: READ A$(I): IF A$(I) <  > "." THEN 1020
     1030 N = I - 1
     1100  INPUT C$:I = 0
     1110 I = I + 1: IF C$ = A$(I) THEN  &  GOSUB C$: GOTO 1100
     1120  IF I < N THEN 1110
     1130  PRINT "NO SUCH COMMAND": GOTO 1100
     2000  REM "SEND"
     2010  PRINT "SEND NOT YET IMPLEMENTED"
     2020  RETURN 
     2500  REM "RECEIVE"
     2510  PRINT "RECEIVE IS NOT READY"
     2520  RETURN 
     3000  REM "EDIT"
     3010  PRINT "MAYBE YOU CAN EDIT LATER"
     3020  RETURN 
     3500  REM "LOAD"
     3510  PRINT "LOAD WHAT, WHERE, HOW?"
     3520  RETURN 
     4000  REM "SAVE"
     4010  PRINT "SAVE WHAT, WHERE, HOW"
     4020  RETURN 
     4500  REM "EXIT"
     4510  PRINT "AH!  THAT I CAN DO!"
     4520  POP : END 
  1000 *SAVE S.LABELLED GO'S
  1010 *--------------------------------
  1020 *      & GOTO <STR EXP>
  1030 *      & GOSUB<STR EXP>
  1040 *      REM "<LABEL>"
  1050 *
  1060 *      AS SUGGESTED BY TIM MOWCHANUK
  1070 *--------------------------------
  1080 AS.VALTYP  .EQ $11
  1090 AS.TEMPPT  .EQ $52,53
  1100 INDEX.REM  .EQ $5E
  1110 INDEX.GO   .EQ $5F
  1120 PRGBOT     .EQ $67,68
  1130 AS.CURLIN  .EQ $75,76
  1140 PNTR       .EQ $9B,9C
  1150 STRLEN     .EQ $9D
  1160 STRADR     .EQ $9E,9F
  1170 VPNT       .EQ $A0,A1
  1180 TXTPTR     .EQ $B8,B9
  1190 *--------------------------------
  1200 TKN.GOTO   .EQ $AB
  1210 TKN.GOSUB  .EQ $B0
  1220 TKN.REM    .EQ $B2
  1230 *--------------------------------
  1240 AMPERSAND.VECTOR .EQ $3F5 ... 3F7
  1250 *--------------------------------
  1260 AS.CHRGET  .EQ $00B1
  1270 AS.CHRGOT  .EQ $00B7
  1280 AS.MEMCHK  .EQ $D3D6
  1290 AS.NEWSTT  .EQ $D7D2
  1300 AS.GOTO1   .EQ $D941
  1310 AS.GOTO.3  .EQ $D95E
  1320 AS.UNDERR  .EQ $D97C
  1330 AS.FRMEVL  .EQ $DD7B
  1340 AS.SYNERR  .EQ $DEC9
  1350 AS.FRETMP  .EQ $E604
  1360 AS.GETADR  .EQ $E752
  1370 *--------------------------------
  1380        .OR $300
  1390        .TF B.LABELLED GO'S
  1400 *--------------------------------
  1410 SETUP  LDA #LABELLED.GOTO.AND.GOSUB
  1420        STA AMPERSAND.VECTOR+1
  1430        LDA /LABELLED.GOTO.AND.GOSUB
  1440        STA AMPERSAND.VECTOR+2
  1450        RTS
  1460 *--------------------------------
  1470 LABELLED.GOTO.AND.GOSUB
  1480        JSR AS.CHRGOT
  1490        CMP #TKN.GOTO
  1500        BEQ .3
  1510        CMP #TKN.GOSUB
  1520        BEQ .2       ...GOOD SYNTAX SO FAR
  1530 .1     JMP AS.SYNERR
  1540 *---SETUP GOSUB RETURN DATA------
  1550 .2     LDA #3
  1560        JSR AS.MEMCHK
  1570        LDA TXTPTR+1
  1580        PHA
  1590        LDA TXTPTR
  1600        PHA
  1610        LDA AS.CURLIN+1
  1620        PHA
  1630        LDA AS.CURLIN
  1640        PHA
  1650        LDA #TKN.GOSUB
  1660        PHA
  1670        BNE .4       ...ALWAYS
  1680 *---SETUP FOR GOTO---------------
  1690 .3     PLA          POP RETURN TO "NEWSTT"
  1700        PLA
  1710 *---FIND LABEL AFTER TOKEN-------
  1720 .4     JSR AS.CHRGET
  1730        BEQ .1
  1740        JSR AS.FRMEVL     EVALUATE EXPRESSION
  1750        BIT AS.VALTYP     $00 IF NUMERIC, $FF IF STRING
  1760        BMI .5            ...STRING
  1770 *---NUMERIC EXPRESSION-----------
  1780        JSR AS.GETADR     CONVERT TO INTEGER
  1790        JSR AS.GOTO1
  1800        JMP AS.NEWSTT
  1810 *---FREE ANY TEMP STRINGS--------
  1820 .45    LDA AS.TEMPPT+1
  1830        LDY #0
  1840        JSR AS.FRETMP
  1850 .5     LDA AS.TEMPPT
  1860        CMP #$56     EMPTY?
  1870        BCS .45      ...NO, FREE A STRING
  1880 *---COPY STRING LENGTH/ADDRESS---
  1890        LDY #2
  1900 .55    LDA (VPNT),Y
  1910        STA STRLEN,Y
  1920        DEY
  1930        BPL .55
  1940 *---SEARCH PROGRAM FOR LABEL-----
  1950        LDA PRGBOT+1      POINT TO BEGINNING
  1960        LDX PRGBOT           OF PROGRAM
  1970 *---LOOK AT NEXT LINE------------
  1980 .6     STA PNTR+1   UPDATE PNTR TO NEXT LINE
  1990        STX PNTR
  2000        LDY #1       HI-BYTE OF FWD PNTR
  2010        LDA (PNTR),Y
  2020        BEQ .11      ...END OF PROGRAM
  2030 *---CHECK FOR 'REM "'------------
  2040        LDY #4
  2050        LDA (PNTR),Y
  2060        CMP #TKN.REM
  2070        BNE .10      ...NOT REM STATEMENT
  2080 .7     INY          NEXT BYTE OF LINE
  2090        LDA (PNTR),Y
  2100        CMP #' '     IGNORE BLANKS BEFORE "
  2110        BEQ .7
  2120        CMP #'"'     " YET?
  2130        BNE .10      ...NO, NOT A LABEL
  2140 *---COMPARE LABEL----------------
  2150        STY INDEX.REM
  2160        LDA #-1
  2170        STA INDEX.GO
  2180 .8     INC INDEX.REM
  2190        LDY INDEX.REM
  2200        LDA (PNTR),Y
  2210        BEQ .1       ...EARLY END OF LABEL
  2220        INC INDEX.GO
  2230        LDY INDEX.GO
  2240        CMP #'"'     LEGAL END OF LABEL?
  2250        BEQ .9       ...YES
  2260        CMP (STRADR),Y
  2270        BEQ .8       ...KEEP MATCHING
  2280        BNE .10      ...DOESN'T MATCH
  2290 .9     CPY STRLEN   CORRECT LENGTH?
  2300        BNE .10      ...NO, KEEP SEARCHING
  2310 *---FOUND LABEL, SO GO TO IT-----
  2320        JSR AS.GOTO.3
  2330        JMP AS.NEWSTT
  2340 *---DOESN'T MATCH, TRY NEXT LINE-
  2350 .10    LDY #0       GET FORWARD POINTER
  2360        LDA (PNTR),Y      LO-BYTE
  2370        TAX
  2380        INY               HI-BYTE
  2390        LDA (PNTR),Y
  2400        BNE .6       ...NOT END OF PROGRAM YET
  2410 *---END OF PROGRAM, UNDEF LBL----
  2420 .11    JMP AS.UNDERR

Timemaster II from Applied Engineering Bob Sander-Cederlof

It may come as a surprise (it did to me), but there are apparently now only three calendar/clocks still on the market for the Apple II, II Plus, //e. The others, and there were a lot of them, seemed to have dropped off the map. And even one of the three (Mountain Computer) does not advertise anywhere I can find.

Another surprise: the most expensive clock has the fewest features, and the least expensive has the most features.

Mountain Computer Apple Clock:

$280 in current catalog listing; most recent ad I could find was in Jan 1980 Byte, at $199. Features below are guessed at from ad and conversations with Dan Pote. Works with BASIC only, does not include any DOS Dater or ProDOS support.

Gives month, day of month, hour, minute, second, millisecond

Interrupt available: Second, Millisecond

Thunderware Thunderclock Plus:

Gives month, day of month, day of week, hour, minute, second.

$150 with BASIC software for DOS or ProDOS $ 29 extra for Pascal software $ 29 extra for DOS-DATER/DEMO disk

Interrupts available: 64, 256, or 2048 times per second

Applied Engineering Timemaster:

$129 includes Applesoft support for DOS or ProDOS includes Pascal and CP/M support includes DOS Dater

Gives year, month, day of month, day of week, hour, minute, second

Interrupts available: Millisecond, Second, Minute, Hour. Switchable to either NMI or IRQ interrupt line.

For some reason they have not chosen to explain, the wizards at Apple who created ProDOS decided to "wire in" support for the Thunderclock (and ONLY Thunderclock). A system call reads the time and date from Thunderclock, calculates the year from the given information, and stores year-month-day-hour-minute in a packed format at $BF90...BF93. ProDOS automatically records time/date of creation and time/date of last modification.

In order to get the year with these dates, ProDOS goes through a calculation to derive year from given day of month, month, and day of week information. The calculation involves remaindering and table lookup...but it only works from 1982 through 1987. I suppose by 1988 they will have generated a new version which works beyond, or else we won't care anymore. Better yet, by 1988 maybe they will have driver-ized the clock support so we can use Dan's card directly.

Dan Pote sent me a Timemaster to play with, in hopes that I would figure out how to make it look like a Thunderclock to ProDOS. I did, so if you buy one now it will be completely compatible with ProDOS. You select by DIP Switch which page of the onboard EPROM will be mapped into the $CN00 space (where N is slot 1-7). One setting selects the ProDOS section, and the others select various versions designed for use with DOS and Applesoft.

You can talk to Dan's card directly, as well as through the EPROM. If you don't like the way his firmware works (unlikely), you can either ignore it or change it.

(By the way.... Call A.P.P.L.E., a club/magazine with a penchant for value and quality, has chosen to offer another one of Applied Engineering's boards in its latest catalog: the Viewmaster 80. Their price is $140, which is 20% below normal retail.)


Finding Trouble in a Big RAM Card Bob Sander-Cederlof

Last night (Monday, Nov 28th) I took home an Apple to do some spreadsheet work. I took home the most portable one, but first swapped RAM cards. I took the STB-128 out of my oldest Apple and put it into the Apple II Plus with the fewest attachments.

When I plugged it in at home and booted the spreadsheet program, all appeared to be well. But it wasn't. I loaded in a model, and during the re-calculation the spreadsheet program hit a BRK opcode and died. I pressed RESET and looked at the partially re-calculated sheet: it was sprinkled with nonsense characters, and the keyboard was locked up. I played with various combinations for an hour or so, including other programs which use the RAM card. Everything pointed to there being a bad bit somewhere in the card.

Of course the RAM card test program was back at the office. I decided to write another one rather than face the two mile round trip.

The 128K space on the STB-128 is divided into 8 banks. You select a bank by storing a bank number (0-7) at any address in the $C080+slot*16 space which has bit 2 = 1. For slot 0, that means store in $C080, $C081, $C082, $C083, $C088, $C089, $C08A, or $C08B. The card has three green LEDs on top which show which bank is currently selected.

Each 16K bank is further divided to fit into the 12K address space between $D000 and $FFFF. The softswitch controlled by bit 3 in the $C08x address selects which of two 4K banks will be enabled at $D000-DFFF. The other 8K always sits at $E000-FFFF. A red LED signals which $D000 bank is selected.

The low-order two bits of the $C08x address control the mode of the RAM card. Accessing $C080 or $C088 write protects the card, and read enables it. This means the $D000-FFFF references the RAM card rather than the motherboard ROM. Accessing $C082 or $C08A write protects the RAM card and disables reading it; in other words, it switches on the motherboard ROM.

$C081 or $C089 also turn on the mother board ROM for reading, but if you access one of these twice in a row it will write enable the RAM card. In this mode reads reference the motherboard ROM, but writes write into the RAM card. This mode is used when loading the RAM card so that monitor and Applesoft routines which are in motherboard ROM can be used for the loading process.

Accessing $C083 or $C08B once read enables the STB-128 card and write protects it. A second access write enables the card. This is the mode we use for a memory test.

Thinking about how to test such a card, I wrote down the following "flow chart":

       For Bank = 0 to 7
           Store Bank in $C083
           Access $C083 again to write enable
               Test $D000-DFFF
           Access $C08B twice
               Test $D000-FFFF
       Next Bank

I broke the actual testing of a range of memory into four parts. First I stored zeroes into every location, and checked to be sure I read zeroes back. Then I did the same with $FF. Then, $55. Then, $AA. This is certainly not an exhaustive test, but I hoped it would be sufficient.

The tricky part was informing myself of the locations and values involved of any memory errors found during the test. I could not conveniently use the monitor subroutines to write addresses and values on the screen, because the monitor only existed in the motherboard ROM and it was switched off! So, I wrote a quick and dirty display routine.

The routine for display in the listing below is not quite so "quick and dirty". The program starts by clearing the screen using the monitor HOME subroutine at $FC58. Then it switches to the RAM card and runs the test. The program pokes test failure data directly to the screen. I direct the data for each of the 8 banks to a different line. When a failure occurs, I print the address, the value that should have been there, the actual value found, and the exclusive-or of the two values. The exclusive-or shows me which bit or bits was incorrect.

After running the test it was obvious that the least significant bit in banks 5 and 6 was not working. When it should be zero it was sometimes one, and vice versa.

I did not know which chip on the STB-128 card belonged to which bit slice or which bank, so I guessed. I was lucky, and guessed right the first time. I pulled out the chip I thought might be the bad one, and re-ran the test. This time the test indicated the least significant bit of banks 4-7 was missing. (It happened to be the chip in the lower-left corner when looking at the face of the card.)

I put the chip back in, hoping that it would miraculously heal itself. Then I looked at the back of the board to see if anything looked suspicious there. Sure enough! STB did not trim off the excess length of the socket pins after soldering the board. One of those long pins had bent over and was possibly shorted to another, on the lower left socket. I straightened the pin and re-ran the test. Voila! It passed!

After I finished patting myself on the back I tried to run the spreadsheet again. It still failed! This morning I put the cards back in their usual homes, and everything works fine.

Tuesday Afternoon....Lo and behold, the card is still bad. I found the STB Systems diskette, and ran their RAM test program. It identified the same chip as being bad. But after running the test for several hours, the errors stopped. Obviously the chip's problems are intermittent.

Wednesday Morning....The chip is still giving errors. I called STB and they said to bring the board by. Wednesday afternoon....STB replaced the chip, and all is well.
  1000  .LIF
  1010 *SAVE S.TEST STB-128
  1020 *--------------------------------
  1030 *      TEST STB-128
  1040 *--------------------------------
  1050 YSAVE  .EQ 0
  1060 LIMIT  .EQ 1
  1070 ADDR   .EQ 2,3
  1080 BANK   .EQ 4
  1090 BYTE   .EQ 5
  1100 SCREEN .EQ 6,7
  1110 *--------------------------------
  1120 SELECT .EQ $C080
  1130 *--------------------------------
  1140 TTTT   JSR TEST
  1150        JSR TEST
  1160        JSR TEST
  1170        JSR TEST
  1180        RTS
  1190 *--------------------------------
  1200 TEST   LDA #0
  1210        STA BANK
  1220        STA ADDR
  1230        JSR $FC58    CLEAR SCREEN
  1240        LDA #$04
  1250        STA SCREEN+1
  1260        LDA #$28
  1270        STA SCREEN
  1280 *---SELECT BANK------------------
  1290 .1     LDA BANK
  1300        STA SELECT+$07
  1310        ORA #$B0     CONVERT TO SCREEN ASCII
  1320        LDY #0
  1330        STA (SCREEN),Y
  1340        LDA SELECT+$03
  1350 *---TEST D000...DFFF-------------
  1360        LDA #$E0
  1370        STA LIMIT
  1380        JSR TEST.ZEROS
  1390        JSR TEST.ONES
  1400        JSR TEST.FIVES
  1410        JSR TEST.AYES
  1420 *---SWITCH TO OTHER D000---------
  1430        LDA SELECT+$0B
  1440        LDA SELECT+$0B
  1450 *---TEST D000...FFFF-------------
  1460        LDA #0
  1470        STA LIMIT
  1480        JSR TEST.ZEROS
  1490        JSR TEST.ONES
  1500        JSR TEST.FIVES
  1510        JSR TEST.AYES
  1520 *---NEXT BANK--------------------
  1530        LDA SCREEN
  1540        EOR #$80
  1550        STA SCREEN
  1560        BMI .2
  1570        INC SCREEN+1
  1580 .2     INC BANK
  1590        LDA BANK
  1600        CMP #8
  1610        BCC .1
  1620 *---SWITCH TO ROMS---------------
  1630        LDA SELECT+$01
  1640        RTS
  1650 *--------------------------------
  1660 TEST.ZEROS
  1670        LDA #0
  1680        .HS 2C
  1690 TEST.ONES
  1700        LDA #$FF
  1710        .HS 2C
  1720 TEST.FIVES
  1730        LDA #$55
  1740        .HS 2C
  1750 TEST.AYES
  1760        LDA #$AA
  1770        STA BYTE
  1780        LDA #$D0
  1790        STA ADDR+1
  1800 .1     JSR FILL
  1810        JSR COMPARE
  1820        INC ADDR+1
  1830        LDA ADDR+1
  1840        CMP LIMIT
  1850        BNE .1
  1860        RTS
  1870 *--------------------------------
  1880 FILL   LDY #0
  1890        LDA BYTE
  1900 .1     STA (ADDR),Y
  1910        INY
  1920        BNE .1
  1930        RTS
  1940 *--------------------------------
  1950 COMPARE
  1960        LDY #0
  1970 .1     LDA (ADDR),Y
  1980        CMP BYTE
  1990        BNE .3
  2000 .2     INY
  2010        BNE .1
  2020        RTS
  2030 .3     PHA          SAVE ACTUAL DATA
  2040        STY YSAVE    SAVE Y-REG
  2050        LDA ADDR+1   PRINT ADDRESS OF FAILURE
  2060        LDY #2
  2070        JSR CONBYTE
  2080        LDA YSAVE    LO-BYTE OF ADDRESS
  2090        JSR CONBYTE
  2100        INY
  2110        LDA BYTE     WHAT DATA SHOULD HAVE BEEN
  2120        JSR CONBYTE
  2130        INY
  2140        PLA          WHAT DATA REALLY WAS
  2150        PHA          KEEP ON STACK TOO
  2160        JSR CONBYTE
  2170        INY
  2180        PLA          FIGURE WHICH BITS WERE WRONG
  2190        EOR BYTE
  2200        JSR CONBYTE
  2210        LDY #0       DELAY LOOP TO SLOW THINGS DOWN
  2220 .4     DEY          FOR OBSERVATION
  2230        BNE .4
  2240        LDY YSAVE
  2250        JMP .2       REJOIN TEST
  2260 *--------------------------------
  2270 CONBYTE
  2280        PHA
  2290        LSR
  2300        LSR
  2310        LSR
  2320        LSR
  2330        JSR CONNYBBLE
  2340        PLA
  2350 CONNYBBLE
  2360        AND #$0F
  2370        CMP #10
  2380        BCC .1
  2390        ADC #6
  2400 .1     ADC #$B0
  2410        STA (SCREEN),Y
  2420        INY
  2430        RTS
  2440 *--------------------------------


Procedure for Converting
S-C Source Files to Text Files
Without Owning an S-C Assembler
Bob Sander-Cederlof

Strangely enough, there are some of you who still do not own an S-C Assembler. And some of you buy or would like to buy our Quarterly Disks or the Applesoft Docu-Mentor disks.

These disks contain source files which are only usable by the S-C Macro Assembler. However, it is possible (even without an S-C Assembler) to convert them to regular text files so as to be readable by another brand assembler/editor.

The files appear in the catalog as type "I", which is supposed to mean Integer BASIC. Of course the contents has nothing to do with Integer BASIC, but making them "I-files" has several advantages:

There are also some dis-advantages:

Which brings us back to the point of this article.

To make the procedure simple, you need at least a 64K Apple. If you have an Apple //e, you are all set. An older Apple needs a "language card", or "RAM card".

The first step in the conversion process is to load the file into memory and find out where it is. Start by booting with your DOS 3.3 System Master disk, which loads Integer BASIC into the RAM card. Then LOAD the S-C source file which you want to convert. Integer BASIC will be switched on, but don't try to LIST or RUN!

Enter the Monitor by typing "CALL -151". At this point you will get an asterisk prompt. Look at locations $4C, $4D, $CA, and $CB. You can do it like this:

       *4C.4D CA.CB
       004C- 00 96
       00CA- 58 73

Interpret the above as meaning that the source code begins in memory at $7358 and ends one byte before $9600.

If you use the monitor commands to look at the first 30 or 40 bytes (or more), you will discover how the source lines are stored. Each line begins with a byte count, which if added to the address will give the address of the first byte of the next line. Each line ends with a 00 byte. The byte count includes both of these bytes, and all in between. Here is a sample line:

       0F E8 03 41 42 43 84 4C 44 41 81 23 24 35 00

The second and third bytes are the binary form of the line number. As usual in 6502 domain, the number is stored low-byte first. $3E8 means the line above is line 1000.

The fourth byte and beyond are ASCII codes for the text of the line, with two exceptions. If the bytes are less than $80, they are plain ASCII. If they are in the range from $81 through $BF, they represent a series of blanks. $81 means one blank, $84 means four blanks, and so on. The line above now decodes to:

       1000 ABC    LDA #$5

The other exception is not illustrated above, but here is one:

       08 F2 03 2A C0 20 2D 00

The token $C0 means "repeated character". The next byte after $C0 gives the number of repetitions, and the byte after that tells what character to repeat. Above the C0 20 2D means 32 "-" characters, so the whole line looks like this:

       1010 *--------------------------------

Armed with all that information, you can probably see how to write a simple Applesoft program to convert the memory image of the S-C source file to plain text and then write it on a text file.

In fact, here is just such a program:

     100  REM CONVERT MEMORY IMAGE OF S-C SOURCE
     110  REM TO ORDINARY TEXT FILE
     200 HM =  PEEK (76) + 256 *  PEEK (77)
     210 PP =  PEEK (768) + 256 *  PEEK (769)
     220  HIMEM: PP
     300  REM OPEN A TEXT FILE
     310 D$ =  CHR$ (4)
     320  PRINT D$"OPEN TEXTFILENAME": PRINT D$"DELETE TEXTFILENAME"
     330  PRINT D$"OPEN TEXTFILENAME": PRINT D$"WRITE TEXTFILENAME"
     400 L = PP
     410  IF L = HM THEN  PRINT D$"CLOSE": END 
     420  GOSUB 500: REM  DO ONE LINE
     430  GOTO 410
     500  REM DO ONE SOURCE LINE
     510 N =  PEEK (L)
     520 LN =  PEEK (L + 1) + 256 *  PEEK (L + 2): PRINT LN" ";:L = L + 
          2
     530 L = L + 1:C =  PEEK (L): IF C = 0 THEN  PRINT :L = L + 1: RETURN 
     540  IF C < 128 THEN  PRINT  CHR$ (C);: GOTO 530
     550  IF C < 192 THEN  FOR I = 1 TO C - 128: PRINT " ";: NEXT : GOTO 
          530
     560  IF C = 192 THEN  FOR I = 1 TO  PEEK (L + 1): PRINT  CHR$ ( PEEK 
          (L + 2));: NEXT I:L = L + 2: GOTO 530
     570  PRINT : PRINT D$"CLOSE": PRINT "***ERROR IN SOURCE AT "L"***
          ": END 

Here is a blow-by-blow description of how to use the program.

  1. Boot your DOS System Master to load INTBASIC into the RAM card.
  2. Load the S-C source file.
  3. Type CALL-151 to get into the monitor.
  4. Type CA.CB to get the starting address of the S-C source program (xx yy).
  5. Type 300:xx yy to store the starting address in a place Applesoft will not clobber.
  6. Type 3D0G to return to Integer BASIC.
  7. Type RUN CONVERT S-C TO TEXT to execute the Applesoft program listed above.
  8. Stand back and wait while the program chugs through the bytes. When you see the Applesoft prompt again, it is all done!

If you add a line at 315 to turn on MONCIO, you can see the text as it is produced.


Where To?, Revisited Bill Morgan

Many thanks to all of you who responded to my questions about 68000, C, and the future of Apple Assembly Line.

Your answers ran about eight to one in favor of including 68000 information in AAL. Several writers suggested starting with a few pages, and possibly splitting off a separate newletter someday. That sounds like a good plan, so we'll start a regular section next issue. Those of you who already know 68000 can now start teaching the rest of us. Bob Urschel has already sent in a brief article and program! He has a QWERTY Q68 board like that we reviewed last month, and speaks very highly of it.

Interest in Macintosh (Apple 32?) is growing rapidly: the announcement is expected at the Apple shareholder meeting in mid-January. Some reports claim that some developers have had Mac for up to 18 months now. We haven't been among those so privileged, but I hope to be the first on my block with one. (Unless the thing turns out to have some fatal flaw, like no expansion slots. That was one rumor!)

Several of you also expressed an interest in C, but not even a majority. More like 30%. It looks like a number of people are curious, but feel that too much coverage would dilute AAL. Stephen Bach said it best, "... don't spread yourselves too thin and try to do C also." I expect to do occasional reviews and mentions of books and other aids to learning C, and to report on anything specifically related to C on Apple computers, but not much more.


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