In This Issue...
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.
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 |
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!
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 |
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 machine 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.)
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 |
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.
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 |