In This Issue...
65816 Books
65816/65802 Assembly Language Programming is here! We have Michael Fischer's book in stock and have filled all the back orders for the first substantial book on programming the 658xx chips. We can now ship your copy immediately, for only $18 + shipping, so give us a call.
Simon & Schuster reports that Programming the 65816, by David Eyes, will be ready to ship in about two weeks. This time it sounds believable, so next month you should be reading another notice like the one above. Our thanks to all of you who have been so patiently waiting for this long-delayed title.
A New Debugger
You may have noticed the ads in a couple of recent issues of AAL for a debugger called Quick Tracy. This program proved to be just the thing Bill needed while working on the UniDisk boot program in this issue. Quick Tracy co-resides with the DOS Version 2.0 S-C Macro Assembler, taking up 2K of memory, and you enter all the debugging commands directly from the assembler. You can single-step or trace through your code and set breakpoints on any register's contents (including PC and Stack) or the state of any status flag. This is a useful utility for only $35. Contact Eric Trehus at (408) 379-0563.
In the November 1985 issue of Apple Assembly Line I printed a complete commented listing of the ProDOS QUIT code. This code resides at $D100-D3FF inside ProDOS, and is downloaded to $1000 and executed by the $65 MLI call.
The BYE command in BASIC.SYSTEM and SCASM.SYSTEM both call ProDOS MLI with call number $65, and so do many other system programs. For some reason FILER has its own quit code, which operates slightly differently from MLI-$65, but not really better. No one seems to particularly like MLI-$65, but they usually learn to live with it. That is, unless they purchase Catalyst, MouseFiler, or one of the other commercially-available ProDOS program selectors.
Not wanting to buy three or four different program selectors until I found one I liked, I decided to try writing my own. It replaces the standard QUIT code inside ProDOS, so that MLI-$65 downloads and executes my new code. My program first lists all of the on-line volume names, so that you can select a volume. You perform the selection by moving the cursor-bar with the arrow keys, and pressing RETURN. ESCAPE makes the program re-do the list of volume names, in case you want to change diskettes. Once a volume is selected, all of the system (SYS) and directory (DIR) filenames in that volume will be listed. Again, you use the arrow keys and RETURN to select either a system program to be executed, or a sub-directory to display. Just a few quick keystrokes and you are in a new application!
Here is an example of the volume name display:
S/D VOLUME NAME 3/2 RAM 7/1 HARD1 7/2 HARD2 6/1 UTILITIES USE ARROWS AND <RETURN> TO SELECT USE <ESCAPE> TO TRY AGAIN
And here is an example of a filename display:
/HARD1 SYS -- PRODOS SYS -- SCASM.SYSTEM SYS -- BASIC.SYSTEM SYS -- CONVERT SYS -- UTIL.SYSTEM DIR -- ASM1 DIR -- ASM2 DIR -- SCI DIR -- FSE DIR -- XREF DIR -- SCWP DIR -- TIMEMASTER DIR -- THUNDERCLOCK DIR -- PHASOR DIR -- MINTERMS DIR -- DP18 <<<MORE>>> USE ARROWS AND <RETURN> TO SELECT USE <ESCAPE> TO TRY AGAIN
All of the SYS files are listed first, and then all of the DIR files, regardless of the order within the directory. This makes it easier to find the file you are looking for. If there are more than 16 filenames to display, the first 16 will be listed, followed by the word "<<<MORE>>>". When you use the arrow keys to move beyond the bottom of the list, if there are more filenames, the list will scroll up to make room for the next name on the screen. When the top name listed is not the first name in the list, the word "<<<MORE>>>" will be displayed above the list. Actually, it is easier to use than it is to describe.
I have gotten so used to an 80-column display now that I decided to make the menu in that mode. Lines 1425-1430 initialize the 80-column display for an enhanced Apple //e or //c. If you want to use some other configuration, or just like 40-columns better, replace those two lines with the following:
1421 JSR $FE93 1422 JSR $FE89 1423 STA $C00C 1424 STA $C00F 1425 STA $C000 1426 .2 JSR HOME
The six lines above make QUITTER a little too long to fit in three pages, so you need to make room for it somehow. I suggest putting the variables from lines 4870-4950 into page zero, say at $06-$0E. This will make the code assemble shorter, so it still fits between $D100 and $D3FF inside ProDOS.
An alternative is to make a further modification to ProDOS. The subroutine which downloads the QUIT code is at $FCE5-FD3A inside ProDOS. It is very inefficient, so there is ample room for adding features. However, by merely changing the LDX #3 at $FD06 to LDX #4, you can make it download four pages instead of three. When you BLOAD PRODOS at $2000, the LDX #3 is found at $4C06. Since the QUIT code is at the end of the PRODOS file, you can write a longer QUIT program if you wish. You also need to change the $03 at $2233 to $04, so that the boot code will install QUIT where it belongs.
Walking through the New QUITTER
The comments in lines 1010-1090 explain how to install the new QUITTER inside the PRODOS system file. Just in case there is an error, I recommend you try this first on a disk you can afford to lose. It all works here, but there's many a slip 'twixt the cup and the lip!
Line 1320 switches on the motherboard ROM code, so that we can use Apple monitor routines. Lines 1330-1410 clear out the memory bitmap in the ProDOS System Global Page. We have to do that so we can load another system file. Once the bitmap has been cleared, it is not safe to try to return to whatever system program was operating before QUITTER was entered. Anyway, the RESET vector has already been pointed at QUITTER, so it is pretty difficult to get out of QUITTER. If you wish, you could add a feature that allows aborting the QUIT call, but be aware that the memory bitmap will have been messed up.
Lines 1420-1590 display a list of all the volumes currently on-line, and allow you to move the cursor bar up and down the list. The subroutine DISPLAY.VOLUMES lists the volume names, displaying the one under the cursor in inverse mode. The subroutine GET.KEY accepts the four arrow keys, RETURN, and ESCAPE. The left and up arrows move the cursor bar up, while the right and down arrows move the cursor bar down. GET.KEY is a little complicated, since it also handles windowing for long lists of filenames.
The subroutine READ.THE.FILE, called from line 1610, reads in an entire volume directory or sub-directory. ProDOS has the built-in ability to read directories just as though they were regular files, so READ.THE.FILE is pretty simple: it merely OPENs the file, READs it, and CLOSEs it. Lines 2030-2150 perform the additional task of appending the current volume or filename to the previous prefix.
Lines 1640-1710 clear the screen and display the pathname of the selected directory, in preparation for display a file menu. Lines 1720-1800 collect a list of pointers to all of the SYS and DIR files in the directory, using the SCAN.DIRECTORY subroutine. SCAN.DIRECTORY appends a pointer to a list of pointers in DIRBUF for each file it finds of the specified type.
Lines 1810-1900 display the SYS and DIR files found in the directory. If there are more than 16 files, the word "<<<MORE>>>" will be displayed after the 16th name. Moving the cursor bar down will scroll the list up, so that you can see the rest of the filenames. If you press ESCAPE or RESET, it all starts over collecting volume names. If you press RETURN when the cursor bar is on a DIR file, the directory name will be added to the current prefix and a new filename list will appear.
If you press RETURN when the cursor bar is on a SYS file, lines 1950-1990 will load the system file and start it running. Lines 1950-1960 set the system prefix to the directory the system file is in. Lines 1970-1980 read the file into RAM starting at $2000, and if there are no errors we blast-off with a JMP $2000. If there ARE errors, the program just starts over.
1000 *SAVE NEW.QUIT.CODE 1010 *-------------------------------- 1020 * Installation: 1030 * 1. BLOAD PRODOS,TSYS,A$2000 1040 * 2. BLOAD B.NEW.QUITTER,A$5700 1050 * 3. BSAVE PRODOS,TSYS,A$2000,L$3A00 1060 * Location: 1070 * In PRODOS file: $5700-59FF 1080 * In ProDOS image: $D100-D3FF 1090 * For execution: $1000-12FF 1100 *-------------------------------- 1110 * Code which downloads the QUIT code resides at 1120 * $FCE5-FD3A. This is loaded from $4BE5-4C3A. 1130 *-------------------------------- 1140 BPNTR .EQ $00,01 1150 SPNTR .EQ $02,03 1160 DPNTR .EQ $04,05 1170 CV .EQ $25 1180 INVFLG .EQ $32 1190 *-------------------------------- 1200 HOME .EQ $FC58 1210 CLREOL .EQ $FC9C 1220 COUT .EQ $FDED 1230 CROUT .EQ $FD8E 1240 *-------------------------------- 1250 MLI .EQ $BF00 1260 BITMAP .EQ $BF58 1270 *-------------------------------- 1280 .OR $1000 1290 .TF B.NEW.QUITTER 1300 *-------------------------------- 1310 QUITTER 1320 LDA $C082 MOTHERBOARD ROMS 1330 LDX #$16 1340 LDA #0 PREPARE VIRGIN BITMAP 1350 .1 STA BITMAP,X 1360 DEX 1370 BNE .1 1380 INX X=1, LOCKOUT $BF00 PAGE 1390 STX BITMAP+$17 1400 LDA #$CF 1410 STA BITMAP 1420 *---LIST VOLUME NAMES------------ 1425 .2 LDA #$99 CTRL-Y 1430 JSR $C300 SET I/O HOOKS, 80-COL MODE, CLEAR SCREEN 1440 LDY #Q.SDV 1450 JSR MSG 1460 JSR CLOSE.ALL.FILES 1470 JSR MLI 1480 .DA #$C5,ONLINE 1490 LDY #0 1500 STY MAX.DIRPNT 1510 STY DIR.START 1520 STY PATHNAME 1530 .3 STY SEL.LINE 1540 JSR DISPLAY.VOLUMES 1550 LDY #Q.VHELP 1560 JSR MSG 1570 JSR GET.KEY 1580 BCC .3 ...ARROW KEYS 1590 BNE .2 ...ESCAPE KEY 1600 *---READ DIRECTORY--------------- 1610 .4 JSR READ.THE.FILE 1620 BCS .7 1630 *---PRINT PATHNAME--------------- 1640 JSR HOME 1650 LDY #0 1660 .5 LDA PATHNAME+1,Y 1670 ORA #$80 1680 JSR COUT 1690 INY 1700 CPY PATHNAME 1710 BCC .5 1720 *---COLLECT FILENAMES------------ 1730 LDX #0 1740 LDA #$FF FIRST JUST "SYS" FILES 1750 JSR SCAN.DIRECTORY 1760 LDA #$0F THEN JUST "DIR" FILES 1770 JSR SCAN.DIRECTORY 1771 TXA SEE IF ANY FILES FOUND 1772 BEQ .2 ...NO, BACK TO THE TOP 1780 LDA #0 MARK END OF LIST 1790 STA DIRBUF+256,X 1800 STX MAX.DIRPNT 1810 *---LIST THE FILENAMES----------- 1820 TAY Y=0 1830 STY DIR.START 1840 .6 STY SEL.LINE 1850 JSR DISPLAY.FILES 1860 LDY #Q.VHELP 1870 JSR MSG 1880 JSR GET.KEY 1890 BCC .6 ...ARROW KEYS 1900 BNE .2 ...ESCAPE KEY 1910 LDY #$10 1920 LDA (SPNTR),Y GET FILE TYPE 1930 BPL .4 DIRECTORY ($0F) 1940 *---SYS FILE, LOAD & EXECUTE----- 1950 JSR MLI SET PREFIX 1960 .DA #$C6,PATH 1970 JSR READ.THE.FILE 1980 BCS .7 ...ERROR IN READING 1990 JMP BUFFER 2000 .7 JMP QUITTER 2010 *-------------------------------- 2020 READ.THE.FILE 2030 LDY #0 APPEND CURRENTLY SELECTED NAME 2040 LDA (SPNTR),Y GET LENGTH OF NAME 2050 AND #$0F 2060 STA LENGTH 2070 LDX PATHNAME CURRENT LENGTH 2080 LDA #'/' 2090 .1 INX 2100 INY 2110 STA PATHNAME,X 2120 LDA (SPNTR),Y 2130 DEC LENGTH 2140 BPL .1 2150 STX PATHNAME 2160 JSR MLI OPEN THE FILE 2170 .DA #$C8,OPEN 2180 BCS RF.ERR 2190 LDA O.REF FILE REFERENCE NUMBER 2200 STA R.REF 2210 JSR MLI READ THE WHOLE FILE 2220 .DA #$CA,READ 2221 BCC CLOSE.ALL.FILES 2222 CMP #$4C IS IT JUST EOF? 2223 SEC 2230 BNE RF.ERR ...NO 2240 CLOSE.ALL.FILES 2250 JSR MLI CLOSE THE FILE 2260 .DA #$CC,CLOSE 2270 RF.ERR RTS 2280 *-------------------------------- 2290 SCAN.DIRECTORY 2300 STA CURTYP TYPE WE ARE COLLECTING 2310 LDA #0 START WITH FIRST BLOCK 2320 .1 STA CURBLK 2330 LDA #BUFFER+4 FIRST 4 BYTES OF BLOCK SKIPPED 2340 STA DPNTR 2350 CLC COMPUTE PAGE OF PNTR 2360 LDA /BUFFER+4 2370 ADC CURBLK 2380 STA DPNTR+1 2390 LDA ENTCNT 2400 STA LENGTH 2410 *-------------------------------- 2420 .2 LDY #0 2430 LDA (DPNTR),Y 2440 AND #$F0 2450 BEQ .4 ...DELETED FILE 2460 CMP #$E0 ...HEADER? 2470 BCS .4 ...YES 2480 LDY #$10 2490 LDA (DPNTR),Y LOOK AT FILE TYPE 2500 CMP CURTYP 2510 BNE .4 ...NOT CURRENT TYPE 2520 *---DIR or SYS file-------------- 2530 .3 LDA DPNTR 2540 STA DIRBUF,X 2550 LDA DPNTR+1 2560 STA DIRBUF+256,X 2570 INX 2580 *---ADVANCE TO NEXT ENTRY-------- 2590 .4 CLC 2600 LDA DPNTR 2610 ADC ENTLEN 2620 STA DPNTR 2630 BCC .5 2640 INC DPNTR+1 2650 .5 DEC LENGTH AT END OF BLOCK YET? 2660 BNE .2 ...NO, CONTINUE IN BLOCK 2670 CLC 2680 LDA CURBLK 2690 ADC #2 2700 CMP ACTLEN+1 2710 BCC .1 ...YES, READ NEXT BLOCK 2720 *-------------------------------- 2730 RTS 2740 *-------------------------------- 2750 CLOSE .DA #1,#0 2760 ONLINE .DA #2,#0,BUFFER 2770 OPEN .DA #3,PATHNAME,OPNBUF 2780 O.REF .BS 1 2790 READ .DA #4 2800 R.REF .BS 1 2810 .DA BUFFER,$9F00 2820 ACTLEN .BS 2 2830 PATH .DA #1,PATHNAME 2840 *-------------------------------- 2850 DISPLAY.VOLUMES 2860 JSR SETUP.DISPLAY.LOOP 2870 LDA #BUFFER 2880 STA BPNTR 2890 LDA /BUFFER 2900 STA BPNTR+1 2910 *-------------------------------- 2920 .1 LDY #0 2930 LDA (BPNTR),Y 2940 AND #$0F 2950 BEQ .3 ...NO VOLUME HERE 2960 *-------------------------------- 2970 JSR CHECK.FOR.SEL.LINE 2980 *-------------------------------- 2990 .2 LDA (BPNTR),Y GET UNIT NUMBER 3000 LSR ISOLATE SLOT NUMBER 3010 LSR 3020 LSR 3030 LSR 3040 AND #7 3050 ORA #"0" 3060 JSR COUT PRINT SLOT NUMBER 3070 LDA #"/" 3080 JSR COUT 3090 LDA (BPNTR),Y GET UNIT NUMBER AGAIN 3100 ASL SET CARRY IF DRIVE 2 3110 LDA #"1" ASSUME DRIVE 1 3120 ADC #0 CHANGE TO 2 IF TRUE 3130 JSR COUT 3140 LDA #" " PRINT TWO SPACES 3150 JSR COUT 3160 JSR COUT 3170 JSR PRINT.BPNTR.NAME 3180 *-------------------------------- 3190 .3 CLC POINT TO NEXT VOLUME NAME 3200 LDA BPNTR 3210 ADC #16 3220 STA BPNTR 3230 BCC .4 3240 INC BPNTR+1 3250 .4 DEC LENGTH ANY MORE LEFT? 3260 BNE .1 ...YES 3270 RTS 3280 *-------------------------------- 3290 PRINT.BPNTR.NAME 3300 LDY #0 3310 LDA (BPNTR),Y GET NAME LENGTH 3320 AND #$0F 3330 TAX 3340 .1 INY PRINT THE VOLUME NAME 3350 LDA (BPNTR),Y 3360 ORA #$80 3370 JSR COUT 3380 DEX 3390 BNE .1 3400 *-------------------------------- 3410 .2 LDA #" " PRINT TRAILING BLANKS 3420 JSR COUT 3430 INY 3440 CPY #16 3450 BCC .2 3460 LDA #$FF NORMAL MODE NOW 3470 STA INVFLG 3480 INC MAX.LINE COUNT THE LINE 3490 JMP CROUT 3500 *-------------------------------- 3510 GET.KEY 3520 .1 LDA $C000 READ KEY FROM KEYBOARD 3530 BPL .1 3540 STA $C010 CLEAR THE STROBE 3550 CMP #$8D 3560 BEQ .2 <RETURN> 3570 CMP #$88 <-- 3580 BEQ .3 3590 CMP #$95 --> 3600 BEQ .7 3610 CMP #$8A DOWN ARROW 3620 BEQ .7 3630 CMP #$8B UP ARROW 3640 BEQ .3 3650 CMP #$9B ESCAPE 3660 BNE .1 GET ANOTHER CHARACTER 3670 LDA #$9B ...SET .NE. 3680 .2 RTS 3690 *---<UP OR LEFT ARROW>----------- 3700 .3 LDY SEL.LINE CURRENT BRIGHT LINE 3710 BNE .6 ...NOT TOP LINE 3720 LDY DIR.START ARE WE DISPLAYING THE FIRST ONE? 3730 BEQ .5 ...YES 3740 DEC DIR.START ...NO, MOVE TOWARD FIRST LINE 3750 .4 LDY #0 MAKE FIRST LINE BRIGHT 3760 CLC 3770 RTS 3780 .5 LDY MAX.LINE MAKE LAST LINE BRIGHT 3790 .6 DEY 3800 CLC 3810 RTS 3820 *---<DOWN OR RIGHT ARROW>-------- 3830 .7 LDY SEL.LINE CURRENT BRIGHT LINE 3840 INY MOVE TOWARD LAST LINE 3850 CPY MAX.LINE BEYOND END OF SCREEN? 3860 BCC .8 ...NO 3870 LDA MAX.DIRPNT ...YES, CHECK IF SHOWING LAST LINE 3880 SBC #17 3890 BCC .4 ...YES 3900 CMP DIR.START 3910 BCC .4 ...YES 3920 INC DIR.START ...NO, MOVE TOWARD LAST LINE 3930 LDY SEL.LINE 3940 CLC 3950 .8 RTS 3960 *-------------------------------- 3970 DISPLAY.FILES 3980 JSR SETUP.DISPLAY.LOOP 3990 LDA DIR.START 4000 STA DIR.INDEX 4010 JSR CLEAR.LINE.OR.PRINT.MORE.MSG 4020 *-------------------------------- 4030 .1 LDX DIR.INDEX 4040 LDY DIRBUF+256,X 4050 BEQ .4 ...END OF LIST 4060 STY BPNTR+1 4070 LDA DIRBUF,X 4080 STA BPNTR 4090 JSR CHECK.FOR.SEL.LINE 4100 *-------------------------------- 4110 .2 LDY #$10 4120 LDA (BPNTR),Y 4130 BMI .3 ...SYS FILE 4140 LDY #Q.DIR 4150 .HS 2C 4160 .3 LDY #Q.SYS 4170 JSR MSG 4180 JSR PRINT.BPNTR.NAME 4190 *-------------------------------- 4200 INC DIR.INDEX 4210 DEC LENGTH 4220 BNE .1 4230 .4 LDA DIR.INDEX 4240 CMP MAX.DIRPNT 4250 *-------------------------------- 4260 CLEAR.LINE.OR.PRINT.MORE.MSG 4270 BEQ .1 CLEAR LINE 4280 LDY #Q.MORE 4290 BNE MSG ...ALWAYS 4300 .1 JSR CLREOL 4310 JMP CROUT 4320 *-------------------------------- 4330 SETUP.DISPLAY.LOOP 4340 LDA #16 MAX 16 LINES IN LIST 4350 STA LENGTH 4360 LDY #0 4370 STY MAX.LINE 4380 INY SAME AS VTAB 3, HTAB 1 4390 STY CV 4400 JMP CROUT 4410 *-------------------------------- 4420 CHECK.FOR.SEL.LINE 4430 LDA MAX.LINE SEE IF CURRENT LINE SHOULD 4440 CMP SEL.LINE BE INVERSE MODE 4450 BNE .1 ...NO 4460 LDA BPNTR ...YES, SO SETUP POINTER 4470 STA SPNTR 4480 LDA BPNTR+1 4490 STA SPNTR+1 4500 LDA #$3F & SET INVERSE MODE 4510 STA INVFLG 4520 .1 RTS 4530 *-------------------------------- 4540 MSG1 JSR COUT 4550 INY 4560 MSG LDA QTS,Y 4570 BNE MSG1 4580 RTS 4590 *-------------------------------- 4600 QTS .EQ * 4610 Q.SDV .EQ *-QTS 4620 .AS -"S/D VOLUME NAME" 4630 .HS 00 4640 Q.VHELP .EQ *-QTS 4650 .HS 8D 4660 .AS -/USE ARROWS AND <RETURN> TO SELECT/ 4670 .HS 8D 4680 .AS -/USE <ESCAPE> TO TRY AGAIN/ 4690 .HS 8D 4700 .HS 00 4710 Q.SYS .EQ *-QTS 4720 .AS -/SYS -- / 4730 .HS 00 4740 Q.DIR .EQ *-QTS 4750 .AS -/DIR -- / 4760 .HS 00 4770 Q.MORE .EQ *-QTS 4780 .AS -/<<<MORE>>>/ 4790 Q.CR .EQ *-QTS 4800 .HS 8D00 4810 *-------------------------------- 4820 .DUMMY 4830 .OR $800 4840 OPNBUF .BS 1024 4850 DIRBUF .BS 512 4860 PATHNAME .EQ $280 4870 DIR.INDEX .BS 1 4880 DIR.START .BS 1 4890 MAX.DIRPNT .BS 1 4900 SEL.LINE .BS 1 4910 MAX.LINE .BS 1 4920 UNIT .BS 1 4930 LENGTH .BS 1 4940 CURTYP .BS 1 4950 CURBLK .BS 1 4960 .ED 4970 *-------------------------------- 4980 BUFFER .EQ $2000 4990 ENTLEN .EQ BUFFER+$23 ENTRY LENGTH 5000 ENTCNT .EQ BUFFER+$24 # ENTRIES PER BLOCK 5010 *-------------------------------- |
The QUIT-code Installer
As I just mentioned, the code which downloads the QUIT-code from $D100-D3FF to $1000-12FF is located at $FCE5 inside ProDOS 1.1.1. Here is a commented listing of that code.
1000 *SAVE S.FCE5.FD3A 1010 *-------------------------------- 1020 .OR $FCE5 1030 .TA $800 1040 DOWNLOAD.QUITTER 1050 LDA $C083 SWITCH IN CORRECT D000 BANK 1060 LDA $C083 1070 *-------------------------------- 1080 LDA $00 SAVE 00...03 ON STACK 1090 PHA 1100 LDA $01 1110 PHA 1120 LDA $02 1130 PHA 1140 LDA $03 1150 PHA 1160 *---SETUP POINTERS FOR MOVING---- 1170 LDA /$1000 Destination Pointer 1180 STA $03 1190 LDA /$D100 Source Pointer- 1200 STA $01 1210 LDA #0 1220 STA $00 1230 STA $02 1240 *-------------------------------- 1250 TAY Y=0 1260 LDX #3 Move 3 Pages 1270 .1 DEY 1280 LDA ($00),Y 1290 STA ($02),Y 1300 TYA 1310 BNE .1 ...More in same page 1320 INC $01 1330 INC $03 Advance to next pages 1340 DEX Count the page 1350 BNE .1 ...Copy another page 1360 *---Restore $03...$00------------ 1370 PLA 1380 STA $03 1390 PLA 1400 STA $02 1410 PLA 1420 STA $01 1430 PLA 1440 STA $00 1450 *-------------------------------- 1460 LDA $C08B Select normal D000 bank 1470 LDA $C08B 1480 *---Set up RESET Vector---------- 1490 LDA #$1000 Lo-byte 1500 STA $3F2 1510 LDA /$1000 Hi-byte 1520 STA $3F3 1530 EOR #$A5 Power-up byte 1540 STA $3F4 1550 *-------------------------------- 1560 JMP $1000 1570 *-------------------------------- |
The program above can be written in a lot less space, as follows:
1580 1590 *-------------------------------- 1600 .OR $FCE5 1610 .TA $900 1620 SC.DOWNLOAD.QUITTER 1630 LDA $C083 Select D000 bank 1640 *-------------------------------- 1650 LDY #0 1660 .1 LDA $D100,Y 1670 STA $1000,Y 1680 LDA $D200,Y 1690 STA $1100,Y 1700 LDA $D300,Y 1710 STA $1200,Y 1720 INY 1730 BNE .1 1740 *-------------------------------- 1750 LDA $C08B Select normal D000 bank 1760 *---Set up RESET Vector---------- 1770 STY $3F2 RESET Vector Lo-byte 1780 LDA /$1000 Hi-byte 1790 STA $3F3 1800 EOR #$A5 Power-up byte 1810 STA $3F4 1820 *-------------------------------- 1830 JMP $1000 1840 *-------------------------------- |
A customer called up the other day to order the DP18 Source Code package, but he wanted it only if it ran under ProDOS. (That's 18-digit Binary Coded Decimal arithmetic for Applesoft.) Well, we hadn't tried to move it over before, but it didn't sound like too much of a problem, so I gave it a shot. It did turn out to be quite easy.
I first tried simply CONVERTing all the files over, including the binary object code, and RUNning the example programs. That almost worked! The DP18 arithmetic all operated just right, but the scheme of moving the Applesoft program up and BLOADing DP18 at $803 ran into a little trouble. The forward pointers in each line of the program weren't set up properly. Bob then pointed out to me that it's very easy to install a program between BASIC.SYSTEM and the buffers, so that might be the way to go in this situation. All it took was a little arithmetic to figure out that DP18 needs $1C pages and should therefore have an origin of $7E00. The .OR directive was the only line inside DP18 that I had to change!
After that I needed only two more things: a short machine language program to get the buffer from BI, issue the BLOAD command, and set the ampersand vector; and a one-line Applesoft routine that checks the vector to find out if DP18 is already installed and call the loader if not.
Here's the Applesoft routine:
10 IF PEEK (1014) + 256 * PEEK (1015) < > 32563 THEN PRINT CHR$ (4)"BRUN INSTALL.DP18"
And here's all there is to the loader:
1000 *SAVE S.INSTALL.DP18 1010 *-------------------------------- 1020 BUFFER .EQ $200 1030 1040 AMPERSAND .EQ $3F6 1050 1060 DP.LINK .EQ $7E00 1070 AMP.LINK .EQ $7E02 1080 1090 DOS.COMMAND .EQ $BE03 1100 GET.BUFFER .EQ $BEF5 1110 FREE.BUFFER .EQ $BEF8 1120 1130 COUT1 .EQ $FDF0 1140 *-------------------------------- 1150 .OR $300 1160 * .TF INSTALL.DP18 1170 1180 T JSR FREE.BUFFER kick others out 1190 LDA #$1C 1200 JSR GET.BUFFER get 28 pages 1210 BCS ERROR 1220 CMP #$7E must be at $7E00 1230 BNE ERROR 1240 1250 LDX #LENGTH 1260 .1 LDA COMMAND,X "BLOAD DP18" 1270 STA BUFFER,X 1280 DEX 1290 BPL .1 1300 JSR DOS.COMMAND do it 1310 BCS ERROR 1320 1330 LDX #1 1340 .2 LDA AMPERSAND,X save old vector 1350 STA AMP.LINK,X 1360 LDA DP.LINK,X & point to DP18 1370 STA AMPERSAND,X 1380 DEX 1390 BPL .2 1400 EXIT RTS 1410 1420 ERROR LDX #0 1430 .1 LDA MESSAGE,X show error message 1440 BEQ EXIT 1450 JSR COUT1 1460 INX 1470 BNE .1 1480 *-------------------------------- 1490 MESSAGE .HS 8D 1500 .AS -/Error loading DP18/ 1510 .HS 8D00 1520 1530 COMMAND .AS -/BLOAD DP18/ 1540 .HS 8D 1550 LENGTH .EQ *-COMMAND-1 1560 *-------------------------------- |
A couple of months ago I presented an RWTS so DOS 3.3 could read and write Apple's new UniDisk 3.5. At that time I promised the additional code so we could boot DOS from the new device. Well, it took a little longer than I had planned, but here are the changes necessary so FORMAT.UNIDISK can produce a bootable disk.
There are two parts to the problem. We must first come up with a way to write a DOS image on the disk during formatting, and we must have a boot sector that can read in the new image and start it up. Since we have 32 sectors in track 0, with 30 of them available for the DOS image, I looked up Bob S-C's article from the April '86 issue on stuffing DOS into fewer sectors for faster booting. In that article he pointed out that we don't need to deal with pages $B3-B4 and $BB, since they're used for buffers, and that these days we can assume that all machines have at least 48K of RAM, so we don't need the relocator code from a DOS master. Eliminating these areas shaves the DOS image down to 32 sectors, but I still needed to get rid of two more to avoid carrying the boot code around inside DOS.
One more was easy: page $BC is just buffer in my RWTS. Then I noticed that nearly all of the code in pages $B6 and $B7 is used only by INIT and the boot process, and could therefore be done away with. It turns out that the only necessary part of $B6 is the various APPEND and VERIFY patches from $B65D-B6B2. Similarly, the only vital things in page $B7 are the RWTS entry at $B7B5-B7C1, a buffer clearing routine at $B7D6-B7DE, and RWTS's IOB and DCT from $B7E8-B7FE. I also decided to keep the read/write a group of pages code from $B793-B7B4, since some programs borrow that routine. So, I came up with some routines to pack the necessary areas in $B7 into the unused space in $B6 and thereby save another page.
Since we're booting in from the Protocol Converter, we don't immediately have the ability to read individual sectors. All we can do is read blocks, or pairs of sectors, and the DOS image doesn't conveniently split up into even blocks. Here's how I ended up mapping DOS pages into blocks:
0 - Boot 4 - A3-A4 8 - AB-AC C - B3,B6 1 - 9D-9E 5 - A5-A6 9 - AD-AE D - B8-B9 2 - 9F-A0 6 - A7-A8 A - AF-B0 E - BA,BD 3 - A1-A2 7 - A9-AA B - B1-B2 F - BE-BF
Note that this formatter uses the DOS image in memory. You need to make sure that this DOS contains all of the patches you want to use, and ONLY the patches that can be used from bootup time. For example, I got into trouble when I initialized a disk with a DOS patched to use my memory expansion card as a RAM disk. Those patches depend on some code being in place in page zero of the alternate banks of the memory card, and that code wasn't there at boot time. The answer is to initialize the disk without these patches, and then make BRUN RAMDRIVE part of the HELLO program. You may have to make some such adjustment for your own system.
If you need to install a bootable DOS image on a disk formatted with the old version of this program, all you have to do is delete lines 1140-1690 and replace line 1710 with:
LDA #1 STA DRIVE
This program doesn't automatically save a HELLO program after formatting, so after it's done you need to LOAD your HELLO from another disk and SAVE it onto the freshly formatted one.
To create this new version you need to make three changes in S.FORMAT.UNIDISK: First, since ERROR is going to be too far away for a direct branch, replace lines 1220-1230 with these:
1220 BCC .1 1225 JMP ERROR 1230 .1 LDA #2
Second, we need to replace DO.BOOT.SECTOR with DO.DOS.IMAGE, to write a complete DOS on the disk. Delete lines 1700-1790 and replace them with the following:
1700 DO.DOS.IMAGE 1710 INC DRIVE that WAS drive one 1720 LDA #0 1730 STA BUFFER buffer on even page 1740 STA TRACK all in track zero 1750 LDX #$1F start at sector $1F 1760 .1 STX SECTOR set sector 1770 LDA PAGE.TABLE,X get next page 1780 CMP #$AA special case? 1790 BNE .2 1800 JSR HANDLE.AA 1810 .2 CMP #$B6 other special case? 1820 BNE .3 1830 JSR HANDLE.B6 1840 .3 STA BUFFER+1 set buffer page 1850 JSR CALL.RWTS write it 1860 LDX SECTOR 1870 DEX done? 1880 BPL .1 1890 RTS 1900 *-------------------------------- 1910 HANDLE.AA 1920 JSR MOVE.PAGE.TO.BUFFER 1930 LDY #29 1940 .1 LDA STARTUP.NAME,Y install HELLO name 1950 STA MY.BUFFER+$75,Y 1960 DEY 1970 BPL .1 1980 LDA #0 mark last command 1990 STA MY.BUFFER+$5F as INIT 2000 LDA /MY.BUFFER point BUFFER here 2010 RTS 2020 *-------------------------------- 2030 HANDLE.B6 2040 JSR MOVE.PAGE.TO.BUFFER 2050 LDX #0 2060 .1 LDY B7.TABLE,X get segment length 2070 BEQ .5 0 marks end 2080 INX next byte 2090 LDA B7.TABLE,X get offset into $B7 2100 STA .3 stuff into LDA 2110 .2 LDA $B700 get byte from $B7 2120 .3 .EQ *-2 2130 STA MY.BUFFER put it in with $B6 2140 .4 .EQ *-2 2150 INC .3 next source byte 2160 INC .4 next destination 2170 DEY done with segment? 2180 BNE .2 2190 INX next byte in table 2200 BNE .1 ...always 2210 2220 .5 LDA /MY.BUFFER point BUFFER here 2230 RTS 2240 2250 B7.TABLE 2260 .DA #$2F,#$B793 R/W group & call RWTS 2270 .DA #$9,#$B7D6 zero a buffer 2280 .DA #$17,#$B7E8 IOB & DCT 2290 .DA #0 2300 *-------------------------------- 2310 MOVE.PAGE.TO.BUFFER 2320 STA .2 A has either $AA or $B6 2330 LDY #0 2340 .1 LDA $FF00,Y 2350 .2 .EQ *-1 2360 STA MY.BUFFER,Y 2370 INY 2380 BNE .1 2390 RTS 2400 *-------------------------------- 2410 PAGE.TABLE 2420 .DA /BOOT.IMAGE,/BOOT.IMAGE+$100 2430 .HS 9D.9E.9F.A0.A1.A2.A3.A4 2440 .HS A5.A6.A7.A8.A9.AA.AB.AC 2450 .HS AD.AE.AF.B0.B1.B2.B3 2460 .HS B6.B8.B9.BA.BD.BE.BF 2470 2480 STARTUP.NAME 2490 .AS -/HELLO/ 2500 .BS STARTUP.NAME+30-*," " |
The main body of DO.DOS.IMAGE just writes the DOS pages listed in PAGE.TABLE onto sectors 0-$1F of track 0, providing special handling for pages $AA and $B6. In page $AA we must install a startup program name into the filename buffer and then force the last-command-executed index to its INIT value, so the coldstart code will work properly after a boot. Page $B6 is a little more involved. We need to move certain segments from page $B7 in with $B6, using a table containing the lengths and addresses of the segments.
And the last step is installing some real boot code in place of the dummy message. Just delete lines 2200-2390 of the old program and put in the rest of the new code:
2900 *-------------------------------- 2910 2920 .BS *+255/256*256-* 2930 2940 COLDSTART .EQ $9D84 2950 IOB.SLOT .EQ $B7E9 2960 IOB.DRIVE .EQ $B7EA 2970 IOB.OSLOT .EQ $B7F7 2980 IOB.ODRIVE .EQ $B7F8 2990 MY.COMPARE .EQ $BEB0 } 3000 MY.READ .EQ $BF49 } inside RWTS.3.5 3010 MY.WRITE .EQ $BF6B } 3020 SETKBD .EQ $FE89 3030 SETVID .EQ $FE93 3040 *-------------------------------- 3050 BOOT.IMAGE 3060 .PH $800 3070 .DA #1 3080 BOOT TXA save slot*16 3082 PHA 3090 LSR 3092 LSR 3094 LSR 3096 LSR 3100 ORA #$C0 form Cslot 3110 PHA save it 3120 STA .1+2 fix entry address 3130 STA .2+2 fix call address 3140 .1 LDA $FFFF get ProDOS entry 3150 CLC 3160 ADC #3 3170 STA .2+1 set PC entry 3180 3190 .2 JSR $FFFF call PC 3200 .DA #1 READ 3210 .DA PARMLIST 3220 INC PC.BUFFER+1 ready for next block 3230 INC PC.BUFFER+1 3240 INC BLOCK next block 3250 LDA BLOCK 3260 CMP #$10 we only want $1-F 3270 BCC .2 3280 3290 RELOCATE.PAGES 3300 LDX #0 3310 .1 LDA $BA00,X relocate packed pages 3320 STA $BF00,X 3330 LDA $B900,X 3340 STA $BE00,X 3350 LDA $B800,X 3360 STA $BD00,X 3370 LDA $B700,X 3380 STA $BA00,X 3390 LDA $B600,X 3400 STA $B900,X 3410 LDA $B500,X 3420 STA $B800,X 3430 LDA $B400,X 3440 STA $B600,X 3450 INX 3460 BNE .1 3470 3480 FIX.B7 LDY TABLE.B7,X get segment size 3490 BEQ FIX.SLOT.REFS done? 3500 INX 3510 LDA TABLE.B7,X get $B7 offset 3520 STA .3 into STA 3530 .1 LDA $B600 3540 .2 .EQ *-2 3550 STA $B700 3560 .3 .EQ *-2 3570 INC .2 next source 3580 INC .3 next destination 3590 DEY 3600 BNE .1 segment done? 3610 INX 3620 BNE FIX.B7 ...always 3630 3640 FIX.SLOT.REFS 3650 PLA get Cslot 3660 STA MY.READ 3670 STA MY.WRITE 3680 PLA get slot*16 3690 STA IOB.SLOT 3700 STA IOB.OSLOT 3710 STA MY.COMPARE 3720 LDA #1 3730 STA IOB.DRIVE 3740 STA IOB.ODRIVE 3750 LDX #$FF new stack 3760 TXS 3770 JSR SETKBD set I/O hooks 3780 JSR SETVID 3790 JMP COLDSTART & go to DOS 3800 *-------------------------------- 3810 PARMLIST .DA #3 3820 .DA #1 3830 PC.BUFFER .DA $9D00 3840 BLOCK .DA <1 3850 3860 TABLE.B7 .DA #$2F,#$B793 3870 .DA #$9,#$B7D6 3880 .DA #$17,#$B7E8 3890 .DA #0 3900 3910 .EP 3920 BOOT.SIZE .EQ *-BOOT.IMAGE 3930 *-------------------------------- 3940 .BS BOOT.IMAGE+$200-* 3950 MY.BUFFER |
Here we take note of which slot we're booting from and set up the Protocol Converter calls to read blocks 1-F into memory starting at $9D00. Then we move the last seven pages up to their real locations and unpack the $B7 information from page $B6. The last step is to plug the current slot and drive info into the IOB, so DOS will know where to look for the HELLO program, and the correct slot location into MY.READ, MY.WRITE and MY.COMPARE. Note that these addresses are inside RWTS 3.5 and may change if you modify that program.
There seems to be some disagreement among the various references for the 65802/16 chip as to how many cycles some of the opcodes take. One in particular that is wrong in almost all places is the BRL (BRanch Long) opcode.
BRL is an unconditional branch, just like BRA. Neither opcode is found in the 6502. BRA (BRanch Always) is found in both the 65C02 and the 65802/16 chips. BRL is only in the 65802/16. BRA takes a single-byte relative offset, just like the conditional branch instructions; therefore, BRA can only branch within -127 to +128 bytes from the instruction. BRL takes a two-byte relative offset, so it can branch anywhere within the 64K bank of memory containing the instruction.
Most references, including the data sheets from Western Design Center and GTE, say that the BRL opcode takes 3 cycles. One reference even went so far as to claim the JMP opcode was obsolete, because BRL and JMP both took 3 cycles and 3 bytes, and BRL had the advantage of being a "run-anywhere" instruction.
However, David Eyes claims in his book that BRL takes 4 cycles.
Naturally, the final authority is the chip itself. But how do you measure the timing of one little instruction? One way is to use a logic analyzer (a very expensive piece of hardware that can give a cycle-by-cycle description of whatever electronic circuit it is attached to). Another is to write a program that will run a loop millions or billions of times, and time it with a stopwatch. The loop first has to be timed without the opcode you are examining, and later with the opcode; then you subtract and divide to get the number of cycles the opcode took.
I thought of a simpler way. I just use my ears! I wrote a loop that toggled the speaker, and put a known 3-cycle opcode inside the loop. Then I duplicated the loop using a BRL instruction in place of the known 3-cycle opcode. When the program runs, the first loop makes a certain tone until I press a key. Then control passes to the second loop. If the second loop makes the same tone, then BRL also takes 3 cycles. If the second tone is higher, BRL takes less than 3 cycles; if lower, then more than 3 cycles. Here is the program:
1000 *SAVE S.TEST.BRL.TIMING 1010 .OP 65802 1020 *-------------------------------- 1030 TEST.BRL.TIMING 1040 .1 LDA $C030 PLAY A TONE 1050 LDY #128 1060 .2 DEY 1070 JMP .3 KNOWN TO BE 3 CYCLES 1080 .3 BNE .2 1090 LDA $C000 REPEAT TONE UNTIL KEYPRESS 1100 BPL .1 1110 STA $C010 1120 *---NOW MAKE TONE WITH "BRL"--- 1130 .4 LDA $C030 1140 LDY #128 1150 .5 DEY 1160 BRL .6 IF 4 CYCLES, LOWER PITCH TONE 1170 .6 BNE .5 1180 LDA $C000 REPEAT TONE UNTIL KEYPRESS 1190 BPL .4 1200 STA $C010 1210 RTS 1220 *-------------------------------- |
The known 3-cycle intruction is the JMP at line 1070. The BRL is inside the second loop at line 1160. When I ran the program, it produced a lower pitch for the BRL-loop. So, BRL takes more than 3 cycles. Is it 4 cycles?
I replaced line 1070 with two NOP lines, which takes 4 cycles altogether. Then I ran the program again, and both loops made the same pitch. Voila! BRL does take 4 cycles. Just to be sure, I made some more tests. They all told the same story.
Then I noticed that the references also disagree on the BRA opcode. Most say it takes 2 cycles. Since the other relative branches only take 2 cycles when they DON'T branch, and BRA never does that, something had to be wrong. In a 6502, 65C02, or 65802/16 Emulation mode, other relative branches take 3 cycles to branch when the target is in the same page as the next byte after the instruction itself, and 4 cycles when the target is in a different page. In 65802/16 Native mode, the conditional relative branches never take more than 3 cycles.
I modified my program to set up page-boundary crossings in various combinations and tested the BRA opcode. It always takes 3 cycles in Native mode. In Emulation mode or older chips it takes 3 or 4 cycles, just like the conditional branches.
If you wonder about the timing of other opcodes, you can set up similar tests.
Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $14 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).
All material herein is copyrighted by S-C SOFTWARE CORPORATION,
all rights reserved. (Apple is a registered trademark of Apple Computer, Inc.)