Volume 2 -- Issue 1 | October 1981

In This Issue...

- Sifting Primes Faster and Faster
- 6809 Cross Assembler
- Extending the Apple Monitor
- Errata
- DOS 3.3 Disassembly: $B052-B0B5 and $B35F-B7FF

Source Code for S-C Assembler II Version 4.0

At long last, I have decided to start selling the source code for my assembler. So many of you have asked for it! I am sure you understand my reluctance; after all, with a wife and five kids to support, and most of our income coming from this one product....

If I have your registration card for Version 4.0 on file, or some other proof-of-purchase, I will send you a disk with all of the commented source code on it. You can study it, assemble it, modify it, et cetera; just don't start selling it! With your check for $95, you will need to include the following signed declaration:

> "I am purchasing the source code of S-C Assembler II Version 4.0 with the understanding that it is proprietary information belonging to S-C SOFTWARE. The disk, and any copies or listings I may make of it, are only for my own personal use."

Another Way Out of the Assembler

James Church, from Trumbull, CT, writes that he has found a way to get from the assembler into Applesoft, without wiping out an Applesoft program.

The normal way to leave is by typing FP, and then PR#0. This of course clears any Applesoft program from memory. But by typing $AAB6:40, $E003G, and PR#0 you can enter Applesoft softly.

Sifting Primes Faster and Faster | Bob Sander-Cederlof

Benchmark programs are sometimes useful for selecting between various processors. Quite a few articles have been published which compare and rank the various Z-80, 8080, 6800, and 6502 systems based on the speed with which they execute a given BASIC program. Some of us cannot resist the impulse to show them up by recoding the benchmark in our favorite language on our favorite processor, using our favorite secret tricks for trimming microseconds.

"A High-Level Language Benchmark" (by Jim Gilbreath, BYTE, September, 1981, pages 180-198) is just such an article. Jim compared execution time in Assembly, Forth, Basic, Fortran, COBOL, PL/I, C, and other languages; he used all sorts of computers, including the above four, the Motorola 68000, the DEC PDP 11/70, and more. He used a short program which finds the 1899 primes between 3 and 16384 by means of a sifting algorithm (Sieve of Eratosthenes).

His article includes table after table of comparisons. Some of the key items of interest to me were:

         Language and Machine                    Seconds

         Assembly Language 68000 (8 MHz)            1.12
         Assembly Language Z80                      6.80
         Digital Research PL/I (Z80)               14.0
         Microsoft BASIC Compiler (Z80)            18.6
         FORTH 6502                               265.
         Apple UCSD Pascal                        516.
         Apple Integer BASIC                     2320.
         Applesoft BASIC                         2806.
         Microsoft COBOL Version 2.2 (Z80)       5115.

There is a HUGE error in the data above; I don't know if it is the only one or not. The time I measured for the Apple Integer BASIC version was only 188 seconds, not 2320 seconds! How could he be so far off? His data is obviously wrong, because Integer BASIC in his data is too close to the same speed as Applesoft.

I also don't know why they neglected to show what the 6502 could do with an assembly language version. Or maybe I do....were they ashamed?

William Robert Savoie, an Apple owner from Tennessee, sent me a copy of the article along with his program. He "hand-compiled" the BASIC version of the benchmark program, with no special tricks at all. His program runs in only 1.39 seconds! That is almost as fast as the 8 MHz Motorola 68000 system! The letter that accompanied his program challenged anyone to try to speed up his program.

How could I pass up a challenge like that? I wrote my own version of the program, and cut the time to 0.93 seconds! Then I made one small change to the algorithm, and produced exactly the same results in only 0.74 seconds!

Looking back at Jim Gilbreath's article, he concludes that efficient, powerful high-level languages are THE way to go. He eschews the use of assembly language for any except the most drastic requirements, because he could not see a clear speed advantage. He points out the moral that a better algorithm is superior to a faster CPU. (Note that his algorithm is by no means the fastest one, by the way.)

Here is Gilbreath's algorithm, in Integer BASIC:

         >LIST
            10 S=8190: DIM F(8191): N=0
            20 FOR I=0 TO S: F(I)=1: NEXT I
            30 FOR I=0 TO S: IF F(I)=0 THEN 80
            40 P=I+I+3: K=I+P
            50 IF K>S THEN 70
            60 F(K)=0: K=K+P: GOTO 50
            70 N=N+1: REM PRINT P;" ";
            80 NEXT I
            90 PRINT: PRINT N;" PRIMES": END

The REM tagged onto the end of line 70, if changed to a real PRINT statement, will print the list of prime numbers as they are generated. Of course printing them was not included in any of the time measurements. According to my timing, printing adds 12 seconds to the program.

I modified the algorithm to take advantage of some more prior knowledge about sifting: There is no need to go through the loop in lines 50 and 60 if P is greater than 127 (the largest prime no bigger than the square root of 16384). This means changing line 40 to read:

         40 P=I+I+3 : IF P>130 THEN 70 : K=I+P

This change cut the time for the program from 188 seconds to 156 seconds. My assembly language version of the original algorithm ran in 0.93 seconds, or 202 times faster; the better algorithm ran in 0.74 seconds, or almost 211 times faster.

William Savoie has done a magnificent job in hand-compiling the first program. He ran the program 100 times in a loop, so that he could get an accurate time using his Timex watch. Here is the listing of his program.

     1000         .LIF
     1010  *---------------------------------
     1020  *      SIEVE PROGRAM:
     1030  *      CALCULATES FIRST 1899 PRIMES IN 1.39 SECONDS!
     1040  *
     1050  *      INSPIRED BY JIM GILBREATH, BYTE, 9/81
     1060  *
     1070  *      WRITTEN BY WILLIAM ROBERT SAVOIE
     1080  *                 4405 DELASHMITT RD. APT 15
     1090  *                 HIXSON, TENN  37343
     1100  *---------------------------------
     1110  BUFF   .EQ $3500    START OF BUFFER (#BUFF=0)
     1120  SIZE   .EQ 8189     SIZE OF FLAG ARRAY
     1130  *---------------------------------
     1140  *      PAGE-ZERO VARIABLES
     1150  *---------------------------------
     1160  INDEX  .EQ $06      PAGE ZERO INDEX (LOCATION FOR I)
     1170  PRIME  .EQ $08      PRIME LOCATION
     1180  KVAR   .EQ $19      K VARIABLE
     1190  CVAR   .EQ $1B      COUNT OF PRIME
     1200  ARRAY  .EQ $1D      ARRAY POINTER
     1210  SAVE   .EQ $1F      COUNT LOOP
     1220  *---------------------------------
     1230  *      ROM ROUTINES
     1240  *---------------------------------
     1250  HOME   .EQ $FC58    CLEAR VIDEO
     1260  CR     .EQ $FD8E    CARRIAGE RETURN
     1270  LINE   .EQ $FD9E    PRINT "-"
     1280  PRINTN .EQ $F940    PRINT 2 BYTE NUMBER IN HEX
     1290  BELL   .EQ $FBE2    SOUND BELL WHEN DONE
     1300  *---------------------------------
     1310  * RUN PROGRAM 100 TIMES FOR ACCURATE TIME MEASUREMENTS!
     1320  *---------------------------------
     1330  START  JSR HOME     CLEAR SCREEN
     1340         JSR CR       CARRIAGE RETURN
     1350         LDA #100     LOOP 100 TIMES
     1360         STA SAVE     SET COUNTER
     1370  .01    JSR GO       RUN PRIME
     1380         DEC SAVE     DECREASE SAVE
     1390         BNE .01      LOOP
     1400         JSR PRINT    PRINT COUNT
     1410         JSR BELL     READ WATCH!
     1420         RTS
     1430  *---------------------------------
     1440  *      RESET VARIABLES
     1450  *---------------------------------
     1460  GO     LDY #00      CLEAR INDEX
     1470         STY CVAR     CLEAR COUNT VARIABLE
     1480         STY CVAR+1   HI BYTE TOO
     1490         STY INDEX    CLEAR INDEX
     1500         STY INDEX+1  HI BYTE TOO
     1510         STY ARRAY    LOW BYTE OF ARRAY
     1520         LDA /BUFF    GET BUFFER LOCATION
     1530         STA ARRAY+1  SET ARRAY POINTER
     1540         LDA #$01     LOAD WITH ONE
     1550         LDX /SIZE    LOAD STOP BYTE
     1560         INX          MAKE PAGE LARGER
     1570  *---------------------------------
     1580  *      SET EACH ELEMENT IN ARRAY TO ONE
     1590  *---------------------------------
     1600  SET    STA (ARRAY),Y  SET MEMORY
     1610         DEY          NEXT LOCATION
     1620         BNE SET      GO 256 TIMES
     1630         INC ARRAY+1  MOVE ARRAY INDEX
     1640         DEX          TEST END
     1650         BNE SET      GO TELL END
     1660
     1670  * SET ARRAY INDEX AT START OF BUFFER
     1680         LDA #BUFF    SET BUFFER LOCATION
     1690         STA ARRAY    IN ARRAY POINTER LOW
     1700         LDA /BUFF    SET BUFFER LOCATION
     1710         STA ARRAY+1  IN ARRAY POINTER
     1720         JMP FORIN    ENTER SIEVE ALGORITHM
     1730
     1740  * SCAN ENTIRE ARRAY AND PROBAGATE LAST PRIME
     1750  FORNXT INC INDEX    INCREASE LOW BYTE
     1760         BNE FORIN    GO IF < 256
     1770         INC INDEX+1  INCREASE HI BYTE
     1780  FORIN  LDA INDEX    GET INDEX TO ARRAY
     1790         CLC          READY ADD
     1800         STA ARRAY    SAVE LOW BYTE
     1810         LDA INDEX+1  GET HI BYTE
     1820         ADC /BUFF    ADD BUFFER LOCATION
     1830         STA ARRAY+1  SET POINTER
     1840         LDY #00      CLEAR Y REGISTER
     1850         LDA (ARRAY),Y  GET ARRAY VALUE
     1860         BEQ FORNXT   GO IF FLAG=0 SINCE NOT PRIME
     1870  * CALCULATE NEXT PRIME NUMBER WITH P=I+I+3
     1880         LDA INDEX    MAKE P=I+3
     1890         ADC #03      ADD THREE
     1900         STA PRIME
     1910         LDA INDEX+1
     1920         ADC #00      ADD CARRY
     1930         STA PRIME+1
     1940  * NOW P=I+3
     1950         LDA PRIME
     1960         ADC INDEX    MAKE P=P+I
     1970         STA PRIME
     1980         LDA PRIME+1
     1990         ADC INDEX+1  ADD HI BYTE
     2000         STA PRIME+1  SAVE P
     2010
     2020  * NOW CALCULATE K=I+PRIME (CLEAR BEYOND PRIME)
     2030         LDA INDEX    ADD I TO P
     2040         ADC PRIME
     2050         STA KVAR     SAVE IN K
     2060         LDA INDEX+1
     2070         ADC PRIME+1  ADD HI BYTE TOO
     2080         STA KVAR+1   SAVE K VALUE
     2090
     2100  * SEE IF K > SIZE AND MODIFY ARRAY IF NOT
     2110  .02    LDA KVAR     GET K VAR
     2120         SEC          SET CARRY FOR SUB
     2130         SBC #SIZE    SUBTRACT SIZE
     2140         LDA KVAR+1   GET HI BYTE
     2150         SBC /SIZE    SUBTRACT TOO
     2160         BCS .03      GO IF K < SIZE
     2170  * ASSIGN ARRAY(K)=0 SINCE PRIME CAN BE ADDED TO MAKE NUMBER
     2180  * THEREFORE THIS CANNOT BE PRIME! (PROBAGATE THROUGH ARRAY)
     2190         LDA KVAR     GET INDEX TO ARRAY
     2200         STA ARRAY    SAVE LOW BYTE
     2210         LDA KVAR+1   GET HI BYTE
     2220         ADC /BUFF    ADD BUFFER OFFSET
     2230         STA ARRAY+1  SAVE ARRAY INDEX
     2240         LDA #00      CLEAR A
     2250         TAY          AND Y REGISTER
     2260         STA (ARRAY),Y  CLEAR ARRAY LOCATION
     2270  * CREATE NEW K FROM K=K+PRIME (MOVE THROUGH ARRAY)
     2280         LDA KVAR     GET K LOW
     2290         ADC PRIME    ADD PRIME
     2300         STA KVAR     SAVE K
     2310         LDA KVAR+1   NOW ADD HI BYTES
     2320         ADC PRIME+1
     2330         STA KVAR+1
     2340         JMP .02      LOOP TELL ARRAY DONE
     2350  * NOW COUNT PRIMES FOUND  (C=C+1)
     2360  .03
     2370  * --NOTE-- DELETE NEXT LINE TO TIME PROGRAM (JSR PRINTP)
     2380         JSR PRINTP   PRINT PRIME
     2390         INC CVAR     ADD ONE
     2400         BNE .04      GO IF NO OVERFLOW
     2410         INC CVAR+1   HI BYTE COUNTER
     2420  .04    LDA INDEX    GET INDEX
     2430  * TEST TO SEE IF WE HAVE INDEXED THROUGH ENTIRE ARRAY
     2440         SBC #SIZE    SUBTRACT SIZE
     2450         LDA INDEX+1  GET HI BYTE TOO
     2460         SBC /SIZE    SUBTRACT HI BYTE
     2470         BCC FORNXT   CONTINUE?
     2480         RTS
     2490  *---------------------------------
     2500  * PRINT THE NUMBER OF PRIMES FOUND
     2510  *---------------------------------
     2520  PRINT  LDY CVAR+1   GET HI BYTE OF COUNT
     2530         LDX CVAR
     2540         JSR PRINTN   PRINT PRIMES FOUND
     2550         RTS          JOB DONE, RETURN
     2560  *---------------------------------
     2570  *      PRINT THE PRIME NUMBER (OPTIONAL)
     2580  *---------------------------------
     2590  PRINTP LDY PRIME+1  HI BYTE
     2600         LDX PRIME
     2610         JSR PRINTN
     2620         JSR LINE     VIDEO "-" OUT
     2630         SEC
     2640         RTS

Here is a listing of my fastest version.

     1000  *---------------------------------
     1010  * SIEVE PROGRAM:
     1020  * CALCULATES FIRST 1899 PRIMES IN .74 SECONDS!
     1030  *
     1040  * INSPIRED BY JIM GILBREATH
     1050  *   (SEE BYTE MAGAZINE, 9/81, PAGES 180-198.)
     1060  * AND BY WILLIAM ROBERT SAVOIE
     1070  *   4405 DELASHMITT RD. APT 15
     1080  *   HIXSON, TENN  37343
     1090  *---------------------------------
     1100  ARRAY  .EQ $3500    FLAG BYTE ARRAY
     1110  SIZE   .EQ 8192     SIZE OF FLAG ARRAY
     1120  *---------------------------------
     1130  * PAGE-ZERO VARIABLES
     1140  *---------------------------------
     1150  A.PNTR .EQ $06,07   POINTER TO FLAG ARRAY FOR OUTER LOOP
     1160  B.PNTR .EQ $08,09   POINTER TO FLAG ARRAY FOR INNER LOOP
     1170  PRIME  .EQ $1B,1C   LATEST PRIME NUMBER
     1180  COUNT  .EQ $1D,1E   # OF PRIMES SO FAR
     1190  TIMES  .EQ $1F      COUNT LOOP
     1200  *---------------------------------
     1210  * APPLE ROM ROUTINES USED
     1220  *---------------------------------
     1230  PRINTN .EQ $F940    PRINT 2 BYTE NUMBER FROM MONITOR
     1240  HOME   .EQ $FC58    CLEAR VIDEO
     1250  CR     .EQ $FD8E    CARRIAGE RETURN
     1260  LINE   .EQ $FD9E    PRINT "-"
     1270  BELL   .EQ $FBE2    SOUND BELL WHEN DONE
     1280  *---------------------------------
     1290  * RUN PROGRAM 100 TIMES FOR ACCURATE TIME MEASUREMENTS!
     1300  *---------------------------------
     1310  START  JSR HOME     CLEAR SCREEN
     1320         LDA #100     LOOP 100 TIMES
     1330         STA TIMES    SET COUNTER
     1340  .1     JSR GENERATE.PRIMES
     1350         LDA $400     TOGGLE SCREEN FOR VISIBLE INDICATOR
     1360         EOR #$80     OF ACTION
     1370         STA $400
     1380         DEC TIMES
     1390         BNE .1       LOOP
     1400         JSR BELL     READ WATCH!
     1410         LDY COUNT+1  GET HI BYTE OF COUNT
     1420         LDX COUNT
     1430         JSR PRINTN   PRINT PRIMES FOUND
     1440         RTS
     1450  *---------------------------------
     1460  *      GENERATE THE PRIMES
     1470  *---------------------------------
     1480  GENERATE.PRIMES
     1490         LDY #0       CLEAR INDEX
     1500         STY COUNT    CLEAR COUNT VARIABLE
     1510         STY COUNT+1
     1520         STY A.PNTR   SET UP POINTER FOR OUTER LOOP
     1530         LDA /ARRAY
     1540         STA A.PNTR+1
     1550         LDA #1       LOAD WITH ONE
     1560         LDX /SIZE      NUMBER OF PAGES TO STORE IN
     1570  *---------------------------------
     1580  * SET EACH ELEMENT IN ARRAY TO ONE
     1590  *---------------------------------
     1600  .1     STA (A.PNTR),Y  SET FLAG TO 1
     1610         INY          NEXT LOCATION
     1620         BNE .1       GO 256 TIMES
     1630         INC A.PNTR+1 POINT AT NEXT PAGE
     1640         DEX          NEXT PAGE
     1650         BNE .1       MORE PAGES
     1660  *---------------------------------
     1670  * SCAN ENTIRE ARRAY, LOOKING FOR A PRIME
     1680  *---------------------------------
     1690         LDA /ARRAY   SET A.PNTR TO BEGINNING AGAIN
     1700         STA A.PNTR+1
     1710  .2     LDY #0       CLEAR INDEX
     1720         LDA (A.PNTR),Y  LOOK AT NEXT FLAG
     1730         BEQ .6       NOT PRIME, ADVANCE POINTER
     1740  *---------------------------------
     1750  * CALCULATE CURRENT INDEX INTO FLAG ARRAY
     1760  *---------------------------------
     1770         SEC
     1780         LDA A.PNTR+1
     1790         SBC /ARRAY
     1800         TAX          SAVE HI-BYTE OF INDEX
     1810         LDA A.PNTR   LO-BYTE OF INDEX
     1820  *---------------------------------
     1830  * CALCULATE NEXT PRIME NUMBER WITH P=I+I+3
     1840  *---------------------------------
     1850         ASL          DOUBLE THE INDEX
     1860         TAY
     1870         TXA          HI-BYTE OF INDEX
     1880         ROL
     1890         TAX
     1900         TYA          NOW ADD 3
     1910         ADC #3
     1920         STA PRIME
     1930         BCC .3
     1940         INX
     1950  .3     STX PRIME+1
     1960  *---------------------------------
     1970  * FOLLOWING 4 LINES CHANGE ALGORITHM SLIGHTLY
     1980  * TO SPEED IT UP FROM .93 TO .74 SECONDS
     1990  *---------------------------------
     2000         TXA          TEST HIGH BYTE
     2010         BNE .5       PRIME > SQRT(16384)
     2020         CPY #127
     2030         BCS .5       PRIME > SQRT(16384)
     2040  *---------------------------------
     2050  * NOW CLEAR EVERY P-TH ENTRY AFTER P
     2060  *---------------------------------
     2070         LDY #0
     2080         LDA A.PNTR   USE CURRENT OUTER POINTER FOR INNER POINTER
     2090         STA B.PNTR
     2100         LDA A.PNTR+1
     2110         STA B.PNTR+1
     2120         CLC          BUMP ARRAY POINTER BY P
     2130  .4     LDA B.PNTR   BUMP TO NEXT SLOT
     2140         ADC PRIME
     2150         STA B.PNTR
     2160         LDA B.PNTR+1
     2170         ADC PRIME+1
     2180         STA B.PNTR+1
     2190         CMP /ARRAY+SIZE     SEE IF BEYOND END OF ARRAY
     2200         BCS .5       YES, FINISHED CLEARING
     2210         TYA          NO, CLEAR ENTRY IN ARRAY
     2220         STA (B.PNTR),Y
     2230         BEQ .4       ...ALWAYS
     2240  *---------------------------------
     2250  * NOW COUNT PRIMES FOUND  (C=C+1)
     2260  *---------------------------------
     2270  .5
     2280  *      JSR PRINTP   PRINT PRIME
     2290         INC COUNT
     2300         BNE .6
     2310         INC COUNT+1
     2320  *---------------------------------
     2330  * ADVANCE OUTER POINTER AND TEST IF FINISHED
     2340  *---------------------------------
     2350  .6     INC A.PNTR
     2360         BNE .7
     2370         INC A.PNTR+1
     2380  .7     LDA A.PNTR+1
     2390         CMP /ARRAY+SIZE
     2400         BCC .2
     2410         RTS
     2420  *---------------------------------
     2430  * OPTIONAL PRINT PRIME SUBROUTINE
     2440  *---------------------------------
     2450  PRINTP LDY PRIME+1  HI BYTE
     2460         LDX PRIME
     2470         JSR PRINTN   PRINT DECIMAL VAL
     2480         JSR LINE     VIDEO "-" OUT
     2490         RTS

Michael R. Laumer, of Carrollton, Texas, has been working for about a year on a full-scale compiler for the Integer BASIC language. He has it nearly finished now, so just for fun he used it to compile the algorithm from Gilbreath's article. Mike used a slightly different form of the Integer BASIC program than I did, which took 238 seconds to execute. But the compiled version ran in only 20 seconds! If you are interested in compiling Integer BASIC programs, you can write to Mike at Laumer Research, 1832 School Road, Carrollton, TX 75006.

If you want to, you can easily cut the time of my program from 0.74 to about .69 seconds. Lines 1600-1650 in my program set each byte in ARRAY to $01. If I don't mind the extra program length, I can rewrite this loop to run in about 42 milliseconds instead of the over 90 it now takes. Here is how I would do it:

         .1     STA ARRAY,Y
                STA ARRAY+$100,Y
                STA ARRAY+$200,Y
                STA ARRAY+$300,Y          TOTAL OF 32
                 .                       LINES LIKE THESE
                 .
                 .
                STA ARRAY+$1E00,Y
                STA ARRAY+$1F00,Y
                INY
                BNE .1

If you can find a way to implement the same program in less than 0.69 seconds, you are hereby challenged to do so!

6809 Cross Assembler | Bob Sander-Cederlof

Chris Wiggs, of Rockford, IL, has developed a cross assembler for the 6809 which runs in the Apple. In fact, it is really a set of patches to the S-C Assembler II Version 4.0. If you BLOAD your copy of the assembler, and then BRUN his patch file, and BSAVE the result, you have a brand new assembler for 6809 code.

It is set up to work with "The Mill". Typing MGO turns on the mill and starts 6809 code executing, while the Apple's 6502 is left in a waiting loop.

Chris has authorized me to distribute these patches. For only $20 you will get a disk which includes all of the source code for the patches (in S-C Assembler II Version 4.0 format), the already-assembled patch file, a sample 6809 program, and some instructions (in the form of an assembly source file of comments).

I have not put this program through any rigorous test, but Chris is using it himself and is satisfied that it is working correctly. Anyway, you will actually have the SOURCE code, so you can make any further changes you wish with ease.

You might also study how he did it, and then write a cross assembler for some other chip, such as Z-80, 68000, 1802, TMS7000, or whatever.

Here is a sample 6809 assembly:

                     1000 *---------------------------------
                     1010        6809 MULTI-PRECISION ADDITION SUBROUTINE
                     1020        FROM "6809 ASSEMBLY LANGUAGE PROGRAMMING",
                     1030             LANCE LEVENTHAL, OSBORN/MCGRAW-HILL
                     1040             PAGE 11-7
                     1050 *---------------------------------
                     1060        CALL: JSR MPAD
                     1070              .DA #N    NUMBER OF BYTES TO ADD
                     1080              .DA ARG1  ADDRESS OF FIRST ARGUMENT
                     1090              .DA ARG2  ADDRESS OF SECOND ARGUMENT
                     1100              .DA SUM   ADDRESS FOR SUM
                     1110 *---------------------------------
     0800- 34 77     1120 MPADD  PSHS X,Y,U,A,B,CCR  SAVE ALL REGISTERS
     0802- EE 69     1130        LDU  9,S    ACCESS PARAMETER LIST
     0804- 37 34     1140        PULU X,Y,B  GET LENGTH AND ADDRESS OF ARGS
     0806- EE C4     1150        LDU  ,U     GET ADDRESS OF SUM
     0808- 1C FE     1160        ANDCC #$FE  CLEAR CARRY TO START SUM
     080A- A6 80     1170 .1     LDA  ,X+    GET BYTE FROM 1ST ARG
     080C- A0 A0     1180        ADCA ,Y+    ADD BYTE FROM 2ND ARG
     080E- A7 C0     1190        STA  ,U+    STORE BYTE IN SUM
     0810- 5A        1200        DECB        ALL BYTES ADDED?
     0811- 26 F7     1210        BNE .1      NOT YET
     0813- EE 69     1220        LDU  9,S    ADJUST RETURN ADDRESS PAST
     0815- 33 47     1230        LEAU 7,U       THE ARGUMENT LIST
     0817- EF 69     1240        STU  9,S
     0819- 35 F7     1250        PULS PC,U,Y,X,B,A,CCR  RESTORE REGISTERS, RETURN

     SYMBOL TABLE

     0800- MPADD
     .01=080A

Extending the Apple Monitor | Bob Sander-Cederlof

Just as the creators of Applesoft included the wonderful "&" statement to allow language extensions, so also Steve Wozniak included a means for adding new monitor commands. The "control-Y" command branches to a user-defined maching language routine, which can supplement the existing commands in the Monitor ROM.

The control-Y command executes your subroutine starting at $3F8. All there is room for at $3F8 is a JMP to where your subroutine is REALLY stored. When you boot DOS, a JMP $FF65 instruction is inserted at $3F8, setting the control-Y command to merely re-enter the monitor. By changing the address of that JMP instruction, you can have it jump to your own code. If you look ahead at the listing of MONITOR EXTENSIONS, lines 1170-1210 store the address of my CTRLY subroutine into the JMP instruction.

I have thought of at least three features that I miss all the time in the monitor. (I just now thought of several more, but they will have to wait for another article.)

1. The monitor already includes the ability to add and subtract single-byte values, and print the single-byte result. I would like to be able to do this with 16-bit values.
2. The monitor can already dump memory in hexadecimal, but I want to see it as ASCII characters also. There is room on the screen for both at once.
3. The monitor can already disassemble code to the screen, 20 lines at a time. If I want more than 20 lines, I can type "LLLLLL", one L for each 20 lines. But I would like to be able to just specify the beginning and ending addresses for the disassembly, like I do for the hexadecimal printout.

If you enter the MONITOR EXTENSIONS program, these three functions will be added to the monitor. To add or subtract two values, type the two values separated by "+" or "-"; then type control-Y, and carriage return. To dump in combined hex and ASCII, type the beginning and ending addresses separated by a period, then control-Y and carriage return. To disassemble a range of memory, type the beginning and ending addresses separated by a period, then control-Y, "L", and a carriage return.

Looking again at the listing, lines 1230-1340 figure out which of the above command options you have typed in. When the monitor branches to $3F8, the following conditions have been set up:

         (A) = 0 if only one address was typed;
             = code for separator character if two addresses
               were typed.

         (X) = 0 if no hex digit typed immediately before the
                 control-Y;
             = 1 if any hex digits immediately before the
                 control-Y.

         (Y) = 0

         ($34) = index into input buffer of next character after
                 the control-Y.

Up to five 16-bit variables (called A1, A2, A3, A4, and A5) are filled from the hexadecimal values in the command. If you type a "<" after the first value, then that value will be stored in A4 and A5 (A4 is at $42,43; A5 at $44,45). If you type a ".", "+", "-", or ":" after a hexadecimal value, then that value will be stored in A1 and A3 (A1 is at $3C,3D; A3 at $40,41). If you type a hexadecimal value immediately before the control-Y, then that value will be stored in A2 (which is at $3E,3F).

Looking again at lines 1230-1340, I branch to SUB if the separator is "-", or ADD if it is "+". If the separator is a colon, I just return; I don't have any control-Y command which accepts a colon separator. If the separator is not any of the above, then either there was no separator, or it was a period. In both of these cases, I want to dump memory. If the character after the control-Y is not "L", then I want a combined hex-ASCII dump; if it is "L", I want disassembly. Line 1340 increments the buffer pointer so that the "L" command will not be re-executed by the regular monitor routine after my control-Y routine is finished.

Lines 1360-1450 control the disassembly option. I used a monitor subroutine to copy the beginning address from A1 into PC. Then I wrote a loop that calles the monitor routine to disassemble one line, and then checks to see if we have reached the ending address. Compare this to the code in the monitor ROM at $FE5E through $FE74. There is one trick in this code. I wanted to compare PC to END.ADDR, and continue if PC was less than or equal to END.ADDR. The normal comparison technique would either SET carry at line 1390, but I CLEARed it. This has the same affect as using one less than the value in PC as the first comparand. I needed this, because BCC at line 1440 only branches if the first comparand is LESS THAN the second one. In other words, since it is difficult to implement IF PC <= END.ADDR THEN ..., I implemented IF PC-1 < END.ADDR THEN ....

Lines 1470-1780 perform the combined hex-ASCII dump. I must give credit to Hugh McKinney, of Dunwoody, GA, for some of the ideas in this code. Just for fun, I set it up to always print complete rows of eight bytes; the starting address is rounded down to the nearest multiple of 8, and the ending address is rounded up. This means that typing just one address will get you eight, also.

I had to make a judgment about what characters to display for the ASCII portion of the dump. There are 256 possible values, and only 96 printing characters. In fact, if you don't have a lower case adapter, your screen only shows 64 printing characters (unless you count inverse and flashing characters as different; in that case you have 192). I decided to display control characters (codes 00-1F and 80-9F) as flashing characters (codes 40-5F). Codes 60-7F and E0-FF display as lower case characters if you have a lower case adapter. Codes 20-5F and A0-DF display as normal video characters (the standard upper case set). If you want a different mapping, change lines 1660-1690 to do it your way.

Lines 1800-1930 perform the 16-bit addition and subtraction in the normal way. Lines 1940-1980 print out an equal sign, and the value.

If you get really ambitious, you might try programming for your Apple II Plus the S and T commands that Apple removed from the Autostart ROM. You can just about copy the code right out of the reference manual. You might also like to add a memory move command that will work correctly even when the target area overlaps the source area.

     1000  *---------------------------------
     1010  *      MONITOR EXTENSIONS
     1020  *---------------------------------
     1030  MON.YSAV   .EQ $34
     1040  PC         .EQ $3A,3B
     1050  BGN.ADDR   .EQ $3C,3D
     1060  END.ADDR   .EQ $3E,3F
     1070  WBUF       .EQ $200
     1080  MON.PRNTYX .EQ $F940
     1090  MON.NXTA1  .EQ $FCBA
     1100  MON.XAM8   .EQ $FDA3
     1110  MON.COUT   .EQ $FDED
     1120  MON.LIST   .EQ $FE63
     1130  MON.A1PC   .EQ $FE75
     1140  *---------------------------------
     1150         .OR $300
     1160  *---------------------------------
     1170  SETUP  LDA #CTRLY
     1180         STA $3F9
     1190         LDA /CTRLY
     1200         STA $3FA
     1210         RTS
     1220  *---------------------------------
     1230  CTRLY  CMP #$AD     MINUS?
     1240         BEQ SUB
     1250         CMP #$AB     PLUS?
     1260         BEQ ADD
     1270         CMP #$BA     COLON?
     1280         BEQ RETURN
     1290         LDY MON.YSAV LOOK BEYOND CONTROL-Y
     1300         LDA WBUF,Y
     1310         LDY #0
     1320         CMP #'L+$80
     1330         BNE DUMP
     1340         INC MON.YSAV
     1350  *---------------------------------
     1360  DISASM JSR MON.A1PC
     1370  .1     LDA #1       DISASSEMBLE ONE LINE
     1380         JSR MON.LIST
     1390         CLC
     1400         LDA PC
     1410         SBC END.ADDR
     1420         LDA PC+1
     1430         SBC END.ADDR+1
     1440         BCC .1
     1450  RETURN RTS
     1460  *---------------------------------
     1470  DUMP   LDA END.ADDR
     1480         ORA #7       FINISH LAST ROW OF 8
     1490         STA PC
     1500         LDA END.ADDR+1
     1510         STA PC+1
     1520         LDA BGN.ADDR START WITH FULL ROW OF 8
     1530         AND #$F8
     1540         STA BGN.ADDR
     1550  .1     JSR MON.XAM8
     1560         SEC          BACK UP POINTER FOR ROW
     1570         LDA BGN.ADDR
     1580         SBC #8
     1590         STA BGN.ADDR
     1600         BCS .2       NO BORROW
     1610         DEC BGN.ADDR+1
     1620  .2     LDA #$A0     PRINT BLANK
     1630         JSR MON.COUT
     1640  .3     LDY #0
     1650         LDA (BGN.ADDR),Y
     1660         ORA #$80     MAKE NORMAL VIDEO
     1670         CMP #$A0     SEE IF PRINTABLE
     1680         BCS .4       YES
     1690         EOR #$C0     MAKE CONTROLS INTO FLASHING ALPHA
     1700  .4     JSR MON.COUT PRINT IT
     1710         JSR MON.NXTA1  ADVANCE POINTER
     1720         BCC .3       MORE ON THIS ROW
     1730         LDA BGN.ADDR
     1740         CMP PC       SEE IF FINISHED WITH DUMP
     1750         LDA BGN.ADDR+1
     1760         SBC PC+1
     1770         BCC .1       NO
     1780         RTS          YES
     1790  *---------------------------------
     1800  SUB    SEC
     1810         LDA BGN.ADDR
     1820         SBC END.ADDR
     1830         TAX
     1840         LDA BGN.ADDR+1
     1850         SBC END.ADDR+1
     1860         JMP AS1
     1870  *---------------------------------
     1880  ADD    CLC
     1890         LDA BGN.ADDR
     1900         ADC END.ADDR
     1910         TAX
     1920         LDA BGN.ADDR+1
     1930         ADC END.ADDR+1
     1940  AS1    TAY
     1950
     1960         LDA #$BD     EQUAL SIGN
     1970         JSR MON.COUT
     1980         JMP MON.PRNTYX

Errata | Bob Sander-Cederlof

Volume 1, Issue 12 (Sep 1981) page 8: Line 1120 in the CHRGET/CHRGOT subroutine should be BCS instead of BEQ.

Volume 1, Issue 7 (Apr 1981) page 8: Insert the following lines:

         1331       TXA          LINE LENGTH
         1332       TAY          IN Y-REG FOR LOOP COUNT
         1333 .2    LDA $200,Y   STRIP SIGN-BITS FROM EACH BYTE
         1334       AND #$7F
         1335       STA $200,Y
         1336       DEY
         1337       BPL .2

This patch is necessary because characters Applesoft strings are supposed to have the sign-bit clear. Everything is fine unless you try compare input strings with constant strings.

DOS Disassembly: $B052-B0B5 AND $B35F-B7FF | Bob Sander-Cederlof

Everything from $B800 through $BFFF has now been covered in previous issues of AAL. Also, the 3.3 boot ROM was covered in the August issue. In this issue I present the rest of the boot code and part of the File Manager (FM).

Lines 1000-1570 are a subroutine inside FM which calls RWTS. The main entry at line 1170 assumes (A)=opcode, (X)=track, and (Y)=sector. A subsidiary entry at line 1200 assumes (A)=opcode, and track and sector were already set up. The valid opcodes are SEEK=0, READ=1, WRITE=2, and FORMAT=4.

Lines 1580-1970 are the various exits from FM. Upon exit, (A)=error code and CARRY status is set if there was an error, clear if not.

Lines 1980-2560 are various buffers, constants, and variables for FM. Notice there are some apparently unused bytes in this area.

Lines 2570-3690 are what is written on track 0 sector 0. It loads and executes BOOT.STAGE1 at $0800 (execution starts at $0801). This code reads in RWTS and BOOT.STAGE2. Since most of this area was unused, patches to solve the APPEND problem are here (lines 3020-3640).

Lines 3700-4080 are BOOT.STAGE2, which read in the rest of DOS and jump to $9D84.

Routines to write the DOS image on tracks 0-2, to enter RWTS with interrupts disabled, and to clear a 256-byte buffer are in lines 4090-4990.

Lines 5100-5300 are the IOB and DCT used by FM for all calls to RWTS. The contents of these are described in the DOS Reference Manual pages 95-98.

     1000  *---------------------------------
     1010  *      DOS 3.2.1/3.3 FILE MANAGER $B052-B0B5
     1020  *---------------------------------
     1030         .OR $B052
     1040         .TA $0852
     1050  *---------------------------------
     1060  MON.STATUS .EQ $48
     1070  IOB.ADDR   .EQ $AAC1
     1080  SAVE.FMW   .EQ $AE7E
     1090  RWTS       .EQ $BD00
     1100  MON.INIT   .EQ $FB2F
     1110  MON.HOME   .EQ $FC58
     1120  MON.PRBYTE .EQ $FDDA
     1130  MON.COUT   .EQ $FDED
     1140  MON.SETKBD .EQ $FE89
     1150  MON.SETVID .EQ $FE93
     1160  *---------------------------------
     1170  CALL.RWTS
     1180         STX IOB.TRACK
     1190         STY IOB.SECTOR
     1200  CALL.RWTS.1
     1210         STA IOB.OPCODE (SEEK=0, READ=1, WRITE=2, FORMAT=4)
     1220         CMP #2       OPCODE="WRITE"?
     1230         BNE .1
     1240         ORA FMW.FLAGS SET "LAST OP WAS WRITE" FLAG
     1250         STA FMW.FLAGS
     1260  .1     LDA FMW.VOLUME
     1270         EOR #$FF     UN-COMPLEMENT THE VOLUME #
     1280         STA IOB.VOLUME
     1290         LDA FMW.SLOT16  SLOT # TIMES 16
     1300         STA IOB.SLOT16
     1310         LDA FMW.DRIVE   DRIVE #
     1320         STA IOB.DRIVE
     1330         LDA FMW.SECTSZ  SECTOR LENGTH IN BYTES
     1340         STA IOB.SECTSZ
     1350         LDA FMW.SECTSZ+1
     1360         STA IOB.SECTSZ+1
     1370         LDA #1       SET TABLE TYPE
     1380         STA IOB.TYPE
     1390         LDY IOB.ADDR GET ADDRESS OF IOB
     1400         LDA IOB.ADDR+1
     1410         JSR ENTER.RWTS   PERFORM THE OPERATION
     1420         LDA IOB.ACTVOL   VOUME # FOUND
     1430         STA FMP.DATA+2
     1440         LDA #$FF     RESET VOLUME EXPECTED IN IOB
     1450         STA IOB.VOLUME
     1460         BCS .2       CARRY SET IF RWTS ERROR
     1470         RTS          RETURN TO CALLER
     1480  .2     LDA IOB.ERROR  GET ERROR CODE
     1490         LDY #7       ERR=7 IF VOLUME MISMATCH
     1500         CMP #$20     VOLUME MISMATCH?
     1510         BEQ .3       YES
     1520         LDY #4       ERR=4 IF WRITE PROTECTED
     1530         CMP #$10     WRITE PROTECTED?
     1540         BEQ .3       YES
     1550         LDY #8       ERR=8 (I/O ERROR) FOR ALL OTHERS
     1560  .3     TYA          ERR IN A-REG
     1570         JMP FM.EXIT.ERROR
     1580  *---------------------------------
     1590  *      DOS 3.3 FILE MANAGER $B35F-B5FF
     1600  *---------------------------------
     1610                .OR $B35F
     1620                .TA $0B5F
     1630  FM.EXIT.ERR1  LDA #1 "LANGUAGE NOT AVAILABLE"
     1640                BNE FM.EXIT.ERROR
     1650  FM.EXIT.ERR2  LDA #2 "RANGE ERROR" (OPCODE)
     1660                BNE FM.EXIT.ERROR
     1670  FM.EXIT.ERR3  LDA #3 "RANGE ERROR" (SUBCODE)
     1680                BNE FM.EXIT.ERROR
     1690  FM.EXIT.ERR4  LDA #4 "WRITE PROTECTED"
     1700                BNE FM.EXIT.ERROR
     1710  FM.EXIT.ERR5  LDA #5 "END OF DATA"
     1720                BNE FM.EXIT.ERROR
     1730  FM.EXIT.ERR6  LDA #6 "FILE NOT FOUND"
     1740                BNE FM.EXIT.ERROR
     1750  FM.EXIT.ERR9  JMP $BFED "DISK FULL"
     1760                NOP
     1770  FM.EXIT.ERR10 LDA #10 "FILE LOCKED"
     1780                BNE FM.EXIT.ERROR
     1790  *---------------------------------
     1800  FM.EXIT.GOOD
     1810         LDA FMP.RETURN  GET RETURN CODE (ZERO)
     1820         CLC          SIGNAL NO ERROR
     1830         BCC FM.EXIT  ...ALWAYS
     1840  *---------------------------------
     1850  FM.EXIT.ERROR
     1860         SEC
     1870  *---------------------------------
     1880  FM.EXIT
     1890         PHP          SAVE STATUS ON STACK
     1900         STA FMP.RETURN  RETURN CODE
     1910         LDA #0       CLEAR MONITOR STATUS (JUST IN CASE)
     1920         STA MON.STATUS
     1930         JSR SAVE.FMW SAVE FM WORKAREA IN FILE BUFFER
     1940         PLP          RETRIEVE STATUS FROM STACK
     1950         LDX FMS.STACK RESTORE STACK POINTER
     1960         TXS
     1970         RTS          RETURN TO WHOEVER CALLED FM
     1980  *---------------------------------
     1990  *      SCRATCH AREA
     2000  *---------------------------------
     2010  FMS.TS.CD  .BS 2    T/S OF CURRENT DIRECTORY SECTOR
     2020             .BS 2    ?
     2030  FMS.STACK  .BS 1    S-REG WHEN FM CALLED
     2040  FMS.DIRNDX .BS 1    VARIOUS USES
     2050             .BS 1       "     "
     2060             .BS 2    ?
     2070             .HS 0000FFFF  USED BY INIT TO CLEAR VTOC ENTRY
     2080  *---------------------------------
     2090         .DA #1,#10,#100  DECIMAL CONVERSION TABLE
     2100         .AS -/TIABSRAB/  FILE TYPE CODES
     2110         .AS -/ EMULOV KSID/  MSG SPELLED BACKWARDS
     2120  *---------------------------------
     2130  *      VTOC SECTOR BUFFER
     2140  *---------------------------------
     2150         .BS 256
     2160  *---------------------------------
     2170  *      DIRECTORY SECTOR BUFFER
     2180  *---------------------------------
     2190         .BS 256
     2200  *---------------------------------
     2210  *      FILE MANAGER PARAMETERS
     2220  *---------------------------------
     2230  FMP.OPCODE    .BS 1
     2240  FMP.SUBCOD    .BS 1
     2250  FMP.DATA      .BS 8 USE DEPENDS ON OPCODE
     2260  FMP.RETURN    .BS 1 ERROR CODE
     2270                .BS 1 ?
     2280  FMP.PNTR.WORK .BS 2 ADDR OF WORKAREA IN FILE BUFFER
     2290  FMP.PNTR.TS   .BS 2 ADDR OF T/S LIST IN FILE BUFFER
     2300  FMP.PNTR.DATA .BS 2 ADDR OF DATA IN FILE BUFFER
     2310                .BS 4 ?
     2320  *---------------------------------
     2330  *      FILE MANAGER WORKAREA
     2340  *---------------------------------
     2350  FMW.TS.TS1  .BS 2 T/S OF FIRST T/S LIST SECTOR
     2360  FMW.TS.TSC  .BS 2 T/S OF CURRENT T/S LIST SECTOR
     2370  FMW.FLAGS   .BS 1 CHECKPOINT FLAGS
     2380  FMW.TS.DATA .BS 2 T/S OF CURRENT DATA SECTOR
     2390              .BS 2 DIRECTORY SECTOR INDEX
     2400              .BS 2  # SECTORS PER TS LIST
     2410              .BS 2  1ST SECTOR
     2420              .BS 2  LAST SECTOR+1
     2430              .BS 2  CURRENT SECTOR
     2440  FMW.SECTSZ  .BS 2  SECTOR SIZE IN BYTES
     2450              .BS 4 FILE POSITION
     2460              .BS 2 RECORD LENGTH FROM OPEN
     2470              .BS 2  RECORD NUMBER
     2480              .BS 2  BYTE OFFSET INTO RECORD
     2490              .BS 2  # SECTORS IN FILE
     2500              .BS 6 SECTOR ALLOCATION AREA
     2510  FMW.FILTYP  .BS 1
     2520  FMW.SLOT16  .BS 1
     2530  FMW.DRIVE   .BS 1
     2540  FMW.VOLUME  .BS 1  (COMPLEMENT FORM)
     2550  FMW.TRACK   .BS 1
     2560              .BS 5  <NOT USED>
     2570  *---------------------------------
     2580  *      STAGE 1 OF BOOT (EXECUTES AT $0800)
     2590  *---------------------------------
     2600         .OR $800
     2610         .TA $E00
     2620  BOOT.STAGE1
     2630         .HS 01
     2640  *   COMES HERE AFTER EACH SECTOR IS READ
     2650         LDA $27      NEXT PAGE TO READ INTO
     2660         CMP #9       FIRST TIME HERE?
     2670         BNE .1       NO, SKIP OVER INITIALIZATION
     2680         LDA $2B      SLOT*16
     2690         LSR          GET SLOT #
     2700         LSR
     2710         LSR
     2720         LSR
     2730         ORA #$C0     BUILD ADDRESS INTO ROM
     2740         STA $3F      FOR READING A SECTOR
     2750         LDA #$5C
     2760         STA $3E
     2770         CLC
     2780         LDA BT1.ADDR+1  COMPUTE ADDRESS OF LAST PAGE
     2790         ADC BT1.N       TO BE READ
     2800         STA BT1.ADDR+1
     2810  .1     LDX BT1.N    # PAGES LEFT TO READ - 1
     2820         BMI .2       FINISHED
     2830         LDA SECTOR.NUMBER,X  CONVERT TO PHYSICAL SECTOR #
     2840         STA $3D
     2850         DEC BT1.N
     2860         LDA BT1.ADDR+1
     2870         STA $27
     2880         DEC BT1.ADDR+1
     2890         LDX $2B      SLOT*16
     2900         JMP ($3E)    READ NEXT SECTOR
     2910  .2     INC BT1.ADDR+1  POINT AT STAGE 2 LOADER
     2920         INC BT1.ADDR+1
     2930         JSR MON.SETKBD
     2940         JSR MON.SETVID
     2950         JSR MON.INIT
     2960         LDX $2B      SLOT*16
     2970         JMP (BT1.ADDR)
     2980  *---------------------------------
     2990  SECTOR.NUMBER
     3000         .HS 000D0B0907050301
     3010         .HS 0E0C0A080604020F
     3020  *---------------------------------
     3030  *      DOS 3.3 PATCHES FOR APPEND AND VERIFY
     3040  *---------------------------------
     3050         .OR $B65D
     3060         .TA $0E5D
     3070  APPEND.FLAG .BS 1
     3080  PATCH.DOS33.1
     3090         JSR $A764    LOCATE AND FREE FILE BUFFER
     3100         BCS .1
     3110         LDA #0       CLEAR APPEND FLAG
     3120         TAY
     3130         STA APPEND.FLAG
     3140         STA ($40),Y
     3150  .1     LDA FMP.RETURN
     3160         JMP $A6D2
     3170  *---------------------------------
     3180  PATCH.DOS33.2
     3190         LDA APPEND.FLAG
     3200         BEQ .1
     3210         INC FMP.DATA
     3220         BNE .1
     3230         INC FMP.DATA+1
     3240  .1     LDA #0       CLEAR APPEND FLAG
     3250         STA APPEND.FLAG
     3260         JMP $A546
     3270  *---------------------------------
     3280  PATCH.DOS33.3
     3290         STA FMP.SUBCOD
     3300         JSR $A6A8
     3310         JSR $A2EA
     3320         JMP $A27D
     3330  *---------------------------------
     3340  PATCH.DOS33.4
     3350         LDY #19      LOOK AT FILE POSITION
     3360  .1     LDA ($42),Y
     3370         BNE .4       NOT AT 0000
     3380         INY
     3390         CPY #23
     3400         BNE .1       TEST 4 BYTES
     3410         LDY #25
     3420  .2     LDA ($42),Y
     3430         STA FMP.DATA-25,Y
     3440         INY
     3450         CPY #29      MOVE 4 BYTES
     3460         BNE .2
     3470  .3     JMP $A6BC
     3480  .4     LDX #$FF
     3490         STX APPEND.FLAG
     3500         BNE .3       ...ALWAYS
     3510         .BS 29       <NOT USED>
     3520  *---------------------------------
     3530  *      STRANGE CODE IN THE MIDDLE OF NOWHERE
     3540  *---------------------------------
     3550         JSR MON.HOME  CLEAR SCREEN
     3560         LDA #$C2      PRINT "B01-00"
     3570         JSR MON.COUT
     3580         LDA #1
     3590         JSR MON.PRBYTE
     3600         LDA #$AD
     3610         JSR MON.COUT
     3620         LDA #0
     3630         JSR MON.PRBYTE
     3640         RTS
     3650         .BS 21       <NOT USED>
     3660         .OR $08FD
     3670         .TA $0EFD
     3680  BT1.ADDR .DA $3600
     3690  BT1.N    .DA #9
     3700  *---------------------------------
     3710  *      SECOND STAGE OF BOOT
     3720  *---------------------------------
     3730         .OR $B700
     3740         .TA $0F00
     3750  BOOT.STAGE2
     3760         STX IOB.SLOT16
     3770         STX IOB.PRVSLT
     3780         LDA #1
     3790         STA IOB.PRVDRV
     3800         STA IOB.DRIVE
     3810         LDA BT.N
     3820         STA BT.CNT
     3830         LDA #2
     3840         STA IOB.TRACK
     3850         LDA #4
     3860         STA IOB.SECTOR
     3870         LDY BT.BT1+1
     3880         DEY
     3890         STY IOB.BUFFER+1
     3900         LDA #1
     3910         STA IOB.OPCODE
     3920         TXA          SLOT*16
     3930         LSR          GET SLOT #
     3940         LSR
     3950         LSR
     3960         LSR
     3970         TAX
     3980         LDA #0
     3990         STA $4F8,X
     4000         STA $478,X
     4010         JSR RW.PAGES
     4020         LDX #$FF
     4030         TXS          EMPTY STACK
     4040         STX IOB.VOLUME
     4050         JMP $BFC8    PATCH TO SETVID AND CLOBBER
     4060  *                   THE LANGUAGE CARD, IF IN SLOT 0
     4070         JSR MON.SETKBD
     4080         JMP $9D84    DOS HARD ENTRY
     4090  *---------------------------------
     4100  *      WRITE DOS IMAGE ON TRACKS 0-2
     4110  *---------------------------------
     4120  WRITE.DOS.IMAGE
     4130         LDA BT.BT1+1 COMPUTE # OF PAGES
     4140         SEC
     4150         SBC IOB.BUFFER+1
     4160         STA BT.CNT
     4170         LDA BT.BT1+1 START AT END, WORK BACKWARD
     4180         STA IOB.BUFFER+1
     4190         DEC IOB.BUFFER+1
     4200         LDA #2       START ON TRACK 2
     4210         STA IOB.TRACK
     4220         LDA #4       SECTOR 4
     4230         STA IOB.SECTOR
     4240         LDA #2
     4250         STA IOB.OPCODE
     4260         JSR RW.PAGES WRITE STAGE2 PART OF DOS
     4270         LDA BT.BT1+1 SET UP BOOT SECTOR IMAGE
     4280         STA BT1.ADDR+1+$B600-$0800
     4290         CLC          COMPUTE STARTING ADDRESS OF WRITE
     4300         ADC #9
     4310         STA IOB.BUFFER+1
     4320         LDA #10      WRITE 10 PAGES
     4330         STA BT.CNT
     4340         SEC
     4350         SBC #1
     4360         STA BT1.N+$B600-$0800
     4370         STA IOB.SECTOR
     4380         JSR RW.PAGES WRITE SECTORS 9-0 ON TRACK 0
     4390         RTS
     4400  *---------------------------------
     4410         .HS 000000000000   <NOT USED>
     4420  *---------------------------------
     4430  *      READ/WRITE A GROUP OF PAGES
     4440  *
     4450  *      BT.CNT       # OF SECTORS TO READ/WRITE
     4460  *      IOB          SET UP FOR FIRST TS TO R/W
     4470  *---------------------------------
     4480  RW.PAGES
     4490         LDA BT.IOB+1 GET IOB ADDRESS
     4500         LDY BT.IOB
     4510         JSR ENTER.RWTS  READ/WRITE ONE SECTOR
     4520         LDY IOB.SECTOR  IGNORE ERRORS IF ANY
     4530         DEY             BACK UP SECTOR #
     4540         BPL .1          STILL IN SAME TRACK
     4550         LDY #15         START WITH SECTOR 15 IN NEXT TRACK
     4560         NOP
     4570         NOP
     4580         DEC IOB.TRACK   BACKWARD THROUGH THE TRACKS
     4590  .1     STY IOB.SECTOR
     4600         DEC IOB.BUFFER+1  DOWN ONE PAGE IN MEMORY
     4610         DEC BT.CNT      ANY MORE PAGES TO DO?
     4620         BNE RW.PAGES    YES
     4630         RTS             NO, RETURN
     4640  *---------------------------------
     4650  *      ENTER RWTS
     4660  *---------------------------------
     4670  ENTER.RWTS
     4680         PHP          SAVE STATUS ON STACK
     4690         SEI          DISABLE INTERRUPTS
     4700         JSR RWTS     CALL RWTS
     4710         BCS .1       ERROR RETURN
     4720         PLP          RESTORE STATUS
     4730         CLC          SIGNAL NO RWTS ERROR
     4740         RTS          RETURN TO CALLER
     4750  .1     PLP          RESTORE STATUS
     4760         SEC          SIGNAL RWTS ERROR
     4770         RTS          RETURN TO CALLER
     4780  *---------------------------------
     4790  *      SET UP RWTS TO WRITE DOS
     4800  *---------------------------------
     4810  SETUP.WRITE.DOS
     4820         LDA FMP.SUBCOD  IMAGE ADDRESS
     4830         STA IOB.BUFFER+1
     4840         LDA #0
     4850         STA IOB.BUFFER
     4860         LDA FMW.VOLUME  VOLUME #
     4870         EOR #$FF        UNCOMPLEMENT IT
     4880         STA IOB.VOLUME
     4890         RTS
     4900  *---------------------------------
     4910  *      CLEAR 256 BYTES STARTING AT ($42,43)
     4920  *---------------------------------
     4930  ZERO.CURRENT.BUFFER
     4940         LDA #0
     4950         TAY
     4960  .1     STA ($42),Y
     4970         INY
     4980         BNE .1
     4990         RTS
     5000  *---------------------------------
     5010  *      PARAMETERS FOR SECOND STAGE OF BOOT PROCESS
     5020  *---------------------------------
     5030         .BS 1        <NOT USED>
     5040  BT.N   .DA #27      # OF PAGES TO R/W (PARAMETER)
     5050  BT.CNT .BS 1        # OF PAGES TO R/W (VARIABLE)
     5060  BT.1S  .DA #10      1ST SECTOR # IN THIS STAGE
     5070         .BS 1
     5080  BT.IOB .DA IOB      ADDRESS OF IOB
     5090  BT.BT1 .DA BOOT.STAGE1+$B600-$0800  ADDR OF 1ST STAGE BOOT
     5100  *---------------------------------
     5110  *      IOB FOR RWTS CALLS
     5120  *---------------------------------
     5130  IOB
     5140  IOB.TYPE   .BS 1    0--MUST BE $01
     5150  IOB.SLOT16 .BS 1    1--SLOT # TIMES 16
     5160  IOB.DRIVE  .BS 1    2--DRIVE # (1 OR 2)
     5170  IOB.VOLUME .BS 1    3--DESIRED VOL # (0 MATCHES ANY)
     5180  IOB.TRACK  .BS 1    4--TRACK # (0 TO 34)
     5190  IOB.SECTOR .BS 1    5--SECTOR # (0 TO 15)
     5200  IOB.PNTDCT .DA DCT  6--ADDRESS OF DCT
     5210  IOB.BUFFER .BS 2    8--ADDRESS OF DATA
     5220  IOB.SECTSZ .BS 2   10--# BYTES IN A SECTOR
     5230  IOB.OPCODE .BS 1   12--0=SEEK, 1=READ, 2=WRITE, OR 4=FORMAT
     5240  IOB.ERROR  .BS 1   13--ERROR CODE: 0, 8, 10, 20, 40, 80
     5250  IOB.ACTVOL .BS 1   14--ACTUAL VOLUME # FOUND
     5260  IOB.PRVSLT .BS 1   15--PREVIOUS SLOT #
     5270  IOB.PRVDRV .BS 1   16--PREVIOUS DRIVE #
     5280             .BS 2
     5290  DCT    .HS 0001EFD8
     5300         .BS 1

Apple Assembly Line is published monthly by S-C SOFTWARE, P. O. Box 280300, Dallas, TX 75228. Phone (214) 324-2050 Subscription rate is $12/year, in the U.S.A., Canada, and Mexico. Other countries add $6/year for extra postage. Back issues are available for $1.20 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.)