The Apple Assembly Line is still growing! I now am sending out over 300 copies per month! It is also growing in size, as you can see: this is the first 20 page issue.
In This Issue...
New Subscription Rates
For the first time since January of 1984, we are going to have to increase our subscription rates. The Post Office is raising the postage again, for the third time since then, and we have to respond. Of course postage is not the only expense that has increased, just the most recent, and most noticeable. Here are the old and new rates for a year's subscription:
Newsletter Only, 12 issues: Current New Bulk Mail (USA only) . . . . . . . . . . $18 --- First Class Mail (USA, Canada, Mexico) . $21 $24 All Other Countries . . . . . . . . . . $32 $36 Newsletter plus the Monthly Disk, 12 issues: USA, Canada, Mexico . . . . . . . . . . $64 $64 All Other Countries . . . . . . . . . . $87 $90
You will notice that the Bulk Mail option is being phased out, as I am planning to mail by First Class to all USA subscribers. The reliability of Bulk Mail has been entirely too erratic, and increasingly so.
You will also notice that the price of a subscription including disk, delivered in the USA, Canada, or Mexico, has not increased at all. Maybe now is the time to upgrade, and save yourself a lot of typing?
The new prices go into effect for new subscriptions as of March 15, 1988. Renewals at the old prices (but no more bulk mail) will continue to be accepted through the 4th of April.
At least twice in the last eight years I have published fancy message display subroutines. In the original Volume 1, Number 1, way back in October, 1980, I gave a really nice 40-column version. That one was actually used in a slightly different form inside a system that we developed for the American Heart Association for teaching Cardio-Pulmonary Resuscitation (CPR).
In the April 1987 issue I published an 80-column version for the //e and //c, which had similar but more powerful capabilities.
Last month I started revealing some of the code from inside AppleWorks (version 1.3). I covered parameter passing and some string handling subroutines, plus block move. I also described and printed the subroutine which polls the keyboard, so that you can type before being asked. POLL.KEYBOARD is called from all over everywhere, just to be sure no characters are lost. I mention that, because there is a call to it from the code I am unveiling this month. In the listing which follows, I have put a dummy POLL.KEYBOARD subroutine which simply does an RTS. In the real AppleWorks code, DISPLAY.STRING calls the real POLL.KEYBOARD.
As I mentioned last month, I am NOT showing in these pages the exact code you would find inside AppleWorks. Since my purpose is not to document AppleWorks, but rather to cull out generally useful code which we can adapt and use, I have rearranged and modified a little. My versions are, in general, shorter and faster. Maybe whoever is currently maintaining AppleWorks at Claris will notice and use these improvements.
In case you ARE interested in documenting AppleWorks, or just want to see what I changed, I have included comment lines with each segment of code which show what the corresponding address was inside AppleWorks 1.3. As was true last month, all the code shown here is found in the main segment called APLWORKS.SYSTEM, which begins at $1000 once it is up and running.
I will begin with a general description of features. DISPLAY.STRING began at $14D1, and there are numerous calls to it in the code. There is also a JMP vector to it in the JMP table which begins at $1000; if you find any JSR $1015 instructions in any of the other segments, they are calling this DISPLAY.STRING subroutine. Unlike all of the subroutines I discussed last month, this subroutine does not expect to find parameters following the JSR which called it. Instead, it expects the length of the string to be in the A-register, and the address of the string to be in locations $80,81. (There is another subroutine which uses the parameter-passing protocol to display a string which starts with a length byte; it simply sets up $80, $81, and the A-register and calls DISPLAY.STRING. You can find it at $2093, with a JMP vector at $1087.)
DISPLAY.STRING does not use any Apple firmware at all. The display techniques used here work faster than the firmware, because they are dedicated to 80-columns and do not have to retain any compatibility with older machines. If you remember that the original Apple //e firmware scrolled the 80-column screen with a slow zigzag motion, you can see why Rupert Lissner decided to code his own.
The characters within the string to be displayed consist of control codes and displayable characters. Displayable characters include the full upper- and lower-case alphabet, numbers, and punctuation signs; all of these can be displayed in both normal and inverse mode. All 32 "mouse-text" characters can also be displayed, although the only one I have noticed in quickly scanning through the AppleWorks messages is the open-apple.
Inverse and normal mode is controlled by a flag byte, which I have called INVERSE.FLAG in my code. It is located at $14D0 in AppleWorks. If that byte contains $00, characters will display in inverse; if $80, normal. A pair of control codes lets you switch INVERSE.FLAG back and forth from within a string, or you can directly set it between calls to DISPLAY.STRING.
The following table shows the hex values for the various character groups as interpreted by DISPLAY.STRING:
00-1F Control Codes 20-7F 96 ASCII Characters 80-9F 32 Mouse Text Characters
The codes 20-7F display in either normal or inverse, depending on INVERSE.FLAG, as described above. Codes C0-DF duplicate 80-9F, displaying the mouse text characters; codes A0-BF and E0-FF both display the 32 lower-case characters in inverse mode.
There are a couple of un-features in DISPLAY.STRING. There is a JMP $1815 instruction located at $1815 inside AppleWorks 1.3. This, of course, hangs up the Apple. The only way out is by hitting RESET. DISPLAY.STRING goes to this HANG.UP code under some circumstances. Since it is a deadly trap, I assume the author of AppleWorks used to have some debugging code there. It gets called from all over, when errors occur that are programming bugs. There is even a JMP vector to it in the JMP table, at $101B! I have left the jumps and branches to HANG.UP in my version, but you might want to modify them to do something reasonable if you are going to use DISPLAY.STRING yourself.
The other un-feature is what happens if you try to print past the right edge of the text window. You might think it would automatically wrap to the next line, like the standard Apple firmware does; no, it just piles up the characters at the end of the line like old manual typewriters used to do. Possibly you might consider a third un-feature to be the limitation of 255 characters in a string, but this is easy enough to work around. My demonstration program, included at the end of the following listing, shows one way.
There are 17 control codes interpreted by DISPLAY.STRING, and room for adding 15 more. Most of these are single byte codes, but two are followed by parameter bytes. Here is a table of the codes:
01 -- Clear from cursor to end of line. 02 -- Clear entire line cursor is on. 03 -- Home (go to 0,0 and clear window). 04 -- Clear from cursor to end of window. 05xxyy -- Move cursor to column xx, row yy of window (HANG.UP if string ends with no xxyy). 06 -- Move cursor left (HANG.UP if beyond window). 07 -- Move cursor right (HANG.UP if beyond window). 08 -- Move cursor up, scroll if already at top. 09 -- Move cursor down, scroll if alread at bottom. 0A -- Set inverse mode. 0B -- Set normal mode. 0C -- Store current cursor position as bottom-right corner of the window. 0D -- Move cursor to beginning of current line. 0E -- Store current cursor position as top-left corner of the window. 0F -- Set up a full-screen window. 10 -- Beep! (the AppleWorks low-tone bell). 11xx -- Slide the screen sideways xx columns. If xx>0 slide to right; if xx<0, slide left.
You can see that setting windows is fairly easy. Use code 05xxyy to position the cursor where you want the bottom-right corner of the new window to be, and then use code 0C to store it; then use 05xxyy to position the cursor where you want the top-left corner of the new window to be, and then use code 0E to make the new window. Since all 05xxyy moves are relative to the current window, you need to set the new bottom-right corner first. (You should now have a clue how AppleWorks nests the file folders on the screen.)
Since AppleWorks does not use any of the Apple firmware, it is also not tied to the standard page-zero locations for window info and cursor position. DISPLAY.STRING uses $10 through $13 to store the window definition. It is not the same as Apple's window definition bytes at $20-$23. Apple's firmware uses a starting column and a width, whereas AppleWorks uses a starting column and an ending column. The bytes are in a different order, too. See lines 1030-1130 for DISPLAY.STRINGs page zero-usage. The two bytes defined in lines 1120-1130, at $F0 and $F1, are the ones AppleWorks uses. However, you could change those two lines to share $18 and $19 with the labels defined on lines 1090-1100, if you wish.
Lines 1150-1320 define two macros, one for storing a byte on the screen and one for picking a byte off the screen. There is another macro definition in lines 4820-4840, for the function vector table. I decided to put these in as macros to shorten the program listing so it would fit in this issue of AAL. It also makes the code in the left-right scroll subroutine easier to follow. My code inside these macros is different from the code in AppleWorks: it is shorter, and on the average one cycle SLOWER. I more than made up for the speed loss in other places, though.
You will find the main body of DISPLAY.STRING in lines 1390-1920. Lines 1470-1480 are curious. They cause the entire call to DISPLAY.STRING to be ignored if the contents of $A4 is non-zero. I don't know why or when AppleWorks would use this. Probably you will want to delete these two lines if you use a revision of DISPLAY.STRING in your own programs. In that case, you would want to substitute a LDA #0 instruction, so that line 1490 would store a zero as the beginning position in the string to be displayed. Line 1500 calls the POLL.KEYBOARD subroutine, which as I mentioned above is just a dummy routine in this listing. You will probably want to delete this line too.
By the time we get to line 1520, everything is set up. A pointer to the first character of the string is in $80,81; POSITION.IN.STRING holds the index relative to that pointer for the next character to be processed; INVERSE.FLAG is either 00 or 80; and BYTES.IN.STRING is set to the string length. We come back to line 1520 for each character in the string, except for the parameter bytes on control codes 05 and 11.
Lines 1520-1540 test to see if we have finished the string, and go to an RTS if so. Lines 1550-1670 pick up the next character, and decide how to process it according to the range: values 80-FF are treated as mouse text, even though we only expect 80-9F for these; values 00-1F are control (function) codes; and values 20-7F are regular ASCII characters.
Mouse text characters are really put on the screen by using values from $40 to $5F, so lines 1680-1700 do the honors.
Regular ASCII characters are EORed with the INVERSE.FLAG in line 1600. If the result is negative, we have a "normal" character; if positive, "inverse". Normal characters are ready now to display, but inverse take some care. The range 40-5F should print as letters rather than mouse text, so they are mapped down to 00-1F in lines 1620-1670.
Control codes are handled in lines 1830-1920. This is not the same way AppleWorks did it. AppleWorks used the trick of pushing the table address on the stack and doing an RTS to effectively JMP to the function code; then each function code processor finished by doing a JMP $14E1 (my line 1520, label .1). My code effectively does a JSR to the function code, so each processor can finish by doing an RTS. This saves space, but is a tiny bit slower. However, I made up for the speed loss by eliminating one unnecessary range check. AppleWorks tested the function code range to be sure it was no larger than $11; if it was larger, AppleWorks jumped to HANG.UP. My code gives the same results, by merely extending the function code table in lines 4870-5190 to include all 32 vectors. The extra 28 bytes of table are more than saved elsewhere. By making the function code processors into subroutines which end with an RTS I have made them accessible to JSR calls from anywhere. This could save even more space.
You can see that to add more functions you merely have to write the processing subroutines and enter the vectors in the function code table using the >VEC macro. I can think of several neat additions. For example, I might use code 00 to initialize full screen, home, normal mode all in one code. It might also be useful to add a code to draw a file folder in the current window. A single code could shrink the window one notch, clear it, and draw a folder. Another code could pop back out to the next larger window. Let your imagination and creativity loose!
I wrote a little demonstration program, shown in lines 6040-6270. This program steps through a list of strings. Each string starts with a length byte. When a length byte of 00 is found, the demonstration stops. I have listed the demonstration strings in raw form to save paper, in lines 6290-6970. The demonstration is not too fancy, but it is fun. I display some characters, then move them around the screen in all four directions, with intermittent beeps to slow it down enough to see. Then I wipe it clean and display the entire character set.
Let me know how you like this series of articles, and what kind of uses you find for the code. If you come up with some really great new function codes for DISPLAY.STRING, send them in and we'll share them in future issues.
1000 .LIST MOFF Do not show Macro Expansions (save paper) 1010 *SAVE AW.SUBS.2 1020 *-------------------------------- 1030 AW.LEFT .EQ $10 DEFINES CURRENT WINDOW 1040 AW.TOP .EQ $11 " 1050 AW.RIGHT .EQ $12 " 1060 AW.BOTTOM .EQ $13 " 1070 AW.CH .EQ $14 CURSOR HORIZONTAL 1080 AW.CV .EQ $15 CURSOR VERTICAL 1090 AW.BASE .EQ $16,17 1100 AW.BASE2 .EQ $18,19 1110 *-------------------------------- 1120 SHUFFLE.SOURCE .EQ $F0 1130 SHUFFLE.DEST .EQ $F1 1140 *-------------------------------- 1150 .MA ST.SCRN 1160 LSR LSB into Carry 1170 TAY Index into Y-reg 1180 PLA Get char again 1190 BCS :1 ...Odd character, in Main RAM 1200 STA $C055 ...Even character, in Aux RAM 1210 :1 STA (AW.BASE),Y 1220 STA $C054 1230 .EM 1240 *-------------------------------- 1250 .MA LD.SCRN 1260 LSR LSB into Carry 1270 TAY Index into Y-reg 1280 BCS :1 ...Odd character, in Main RAM 1290 STA $C055 ...Even character, in Aux RAM 1300 :1 LDA (AW.BASE),Y 1310 STA $C054 1320 .EM 1330 *-------------------------------- 1340 INVERSE.FLAG .BS 1 00=display chars inverse, 80=display chars normal 1350 BYTES.IN.STRING .BS 1 1360 POSITION.IN.STRING .BS 1 1370 SCROLL.DIRECTION .BS 1 1380 *-------------------------------- 1390 * DISPLAY A STRING WITH FUNCTION CODES 1400 * (A) = # BYTES IN STRING 1410 * ($80,81) = Address of string 1420 * ($A4) = flag: if 00 display, else ignore. 1430 * at $14D1 in AppleWorks 1.3 1440 *-------------------------------- 1450 DISPLAY.STRING 1460 STA BYTES.IN.STRING 1470 LDA $A4 If non-zero, don't display anything 1480 BNE .99 ...to an RTS 1490 STA POSITION.IN.STRING 1500 JSR POLL.KEYBOARD GET KEY IF ANY 1510 *-------------------------------- 1520 .1 LDY POSITION.IN.STRING 1530 CPY BYTES.IN.STRING 1540 BCS .99 ...to an RTS 1550 INC POSITION.IN.STRING 1560 LDA ($80),Y GET CHAR FROM STRING 1570 BMI .2 ...80-FF, Mouse char for screen 1580 CMP #$20 1590 BCC .5 ...00-1F 1600 EOR INVERSE.FLAG 1610 BMI .3 ...Normal Char 1620 CMP #$40 ...Inverse char 1630 BCC .3 ...00-3F, inverse A-Z, 0-9, punctuation 1640 CMP #$60 1650 BCS .3 ...60-7F, inverse a-z, punctuation 1660 AND #$BF map 40-5F into 00-1F, more A-Z 1670 BCC .3 ...always 1680 *---Display a Mouse Char--------- 1690 .2 AND #$7F ...map into 40-5F, mouse char range 1700 ORA #$40 1710 *---Display Char in A-register--- 1720 .3 PHA Save the character 1730 JSR BASE.CALC.CV 1740 LDA AW.CH Char position on line 1750 >ST.SCRN Store character on screen 1760 *-------------------------------- 1770 LDX AW.CH 1780 CPX #79 End of line yet? 1790 BCS .1 ...yes, stick there, pile em' up 1800 INC AW.CH ...no, advance CH 1810 BNE .1 ...always 1820 *-------------------------------- 1830 .5 JSR .6 CALL THE FUNCTION 1840 JMP .1 ...ALWAYS 1850 *-------------------------------- 1860 .6 ASL Convert code to index 1870 TAX 1880 LDA FUNTBL+1,X 1890 PHA 1900 LDA FUNTBL,X 1910 PHA 1920 .99 RTS 1930 *-------------------------------- 1940 * FUNCTION 03 -- CLEAR ENTIRE WINDOW, CURSOR TO TOP-LEFT 1950 * at $154A in AppleWorks 1.3 1960 *-------------------------------- 1970 FUN.HOME 1980 LDA AW.LEFT 1990 STA AW.CH 2000 LDA AW.TOP 2010 STA AW.CV 2020 *-------------------------------- 2030 * FUNCTION 04 -- CLEAR REST OF WINDOW 2040 * at $1552 in AppleWorks 1.3 2050 *-------------------------------- 2060 FUN.CLR.CH.TO.EOS 2070 LDA AW.CH SAVE CH AND CV 2080 PHA 2090 LDA AW.CV 2100 PHA 2110 .1 JSR CLR.CH.TO.EOL 2120 LDA AW.CV 2130 CMP AW.BOTTOM 2140 BCS .2 ...THAT WAS THE BOTTOM LINE 2150 INC AW.CV 2160 LDA AW.LEFT 2170 STA AW.CH 2180 BCC .1 ...ALWAYS 2190 .2 PLA 2200 STA AW.CV 2210 PLA 2220 STA AW.CH 2230 RTS 2240 *-------------------------------- 2250 * FUNCTION 05 -- GO TO X,Y IN THIS WINDOW 2260 * Equivalent to HTAB X, VTAB Y 2270 * 05 XX YY in string 2280 * at $1576 in AppleWorks 1.3 2290 *-------------------------------- 2300 FUN.GOTO.XY 2310 LDY POSITION.IN.STRING 2320 LDA ($80),Y 2330 INY 2340 CPY BYTES.IN.STRING 2350 BCS FUN.HANG.UP 2360 ADC AW.LEFT 2370 STA AW.CH 2380 LDA ($80),Y 2390 ADC AW.TOP 2400 STA AW.CV 2410 INY 2420 STY POSITION.IN.STRING 2430 RTS 2440 *-------------------------------- 2450 * FUNCTION 06 -- BACK UP CURSOR 2460 * at $1593 in AppleWorks 1.3 2470 *-------------------------------- 2480 FUN.CURSOR.LEFT 2490 LDA AW.CH 2500 CMP AW.LEFT 2510 BEQ FUN.HANG.UP 2520 DEC AW.CH 2530 RTS 2540 *-------------------------------- 2550 * at $1599 and $1815 in AppleWorks 1.3 2560 FUN.HANG.UP JMP FUN.HANG.UP 2570 *-------------------------------- 2580 * FUNCTION 07 -- CURSOR RIGHT 2590 * at $15A1 in AppleWorks 1.3 2600 *-------------------------------- 2610 FUN.CURSOR.RIGHT 2620 LDA AW.CH 2630 CMP AW.RIGHT 2640 BEQ FUN.HANG.UP 2650 INC AW.CH 2660 RTS 2670 *-------------------------------- 2680 * FUNCTION 08 -- CURSOR UP (SCROLL DOWN IF NECESSARY) 2690 * at $15AB in AppleWorks 1.3 2700 *-------------------------------- 2710 FUN.CURSOR.UP 2720 LDA AW.CV 2730 CMP AW.TOP 2740 BEQ .1 ...ALREADY AT TOP, SCROLL DOWN 2750 DEC AW.CV 2760 RTS 2770 .1 LDA AW.BOTTOM 2780 LDX #-1 2790 BNE SCROLL ...ALWAYS 2800 *-------------------------------- 2810 * FUNCTION 09 -- CURSOR DOWN (SCROLL UP IF NECESSARY) 2820 * at $15BC in AppleWorks 1.3 2830 *-------------------------------- 2840 FUN.CURSOR.DOWN 2850 LDA AW.CV 2860 CMP AW.BOTTOM 2870 BEQ .1 ...ALREADY AT BOTTOM, SCROLL UP 2880 INC AW.CV 2890 RTS 2900 .1 LDA AW.TOP 2910 LDX #1 2920 SCROLL 2930 STX SCROLL.DIRECTION 01 IF UP, FF IF DOWN 2940 PHA SAVE LINE NUMBER 2950 JSR BASE.CALC.A 2960 .1 LDA AW.BASE 2970 STA AW.BASE2 2980 LDA AW.BASE+1 2990 STA AW.BASE2+1 3000 PLA GET LINE NUMBER AGAIN 3010 CMP AW.CV IS IT THE LAST LINE? 3020 BEQ FUN.CLR.LINE 3030 CLC 3040 ADC SCROLL.DIRECTION 3050 PHA SAVE SOURCE LINE NUMBER 3060 JSR BASE.CALC.A 3070 LDA AW.LEFT 3080 TAX 3090 LSR 3100 TAY 3110 BCS .3 START WITH ODD COLUMN 3120 .2 STA $C055 EVEN COLUMNS IN AUX MEM 3130 LDA (AW.BASE),Y 3140 STA (AW.BASE2),Y 3150 STA $C054 BACK TO MAIN MEM 3160 CPX AW.RIGHT 3170 BEQ .1 ...END OF THIS LINE 3180 INX 3190 .3 LDA (AW.BASE),Y 3200 STA (AW.BASE2),Y 3210 CPX AW.RIGHT 3220 BEQ .1 ...END OF THIS LINE 3230 INX 3240 INY 3250 BNE .2 ...ALWAYS 3260 *-------------------------------- 3270 * FUNCTION 02 -- CLEAR ENTIRE CURRENT LINE 3280 * at $1540 in AppleWorks 1.3 3290 *-------------------------------- 3300 FUN.CLR.LINE 3310 LDA AW.LEFT 3320 STA AW.CH 3330 *-------------------------------- 3340 * FUNCTION 01 -- CLEAR REST OF CURRENT LINE 3350 * at $1544 in AppleWorks 1.3 3360 *-------------------------------- 3370 FUN.CLR.CH.TO.EOL 3380 JMP CLR.CH.TO.EOL 3390 *-------------------------------- 3400 * FUNCTION 0A -- INVERSE 3410 * at $160B in AppleWorks 1.3 3420 *-------------------------------- 3430 FUN.INVERSE 3440 LDA #$00 3450 .HS 2C SKIP OVER LDA #$80 3460 *-------------------------------- 3470 * FUNCTION 0B -- NORMAL 3480 * at $160F in AppleWorks 1.3 3490 *-------------------------------- 3500 FUN.NORMAL 3510 LDA #$80 3520 STA INVERSE.FLAG 3530 RTS 3540 *-------------------------------- 3550 * FUNCTION 0C -- DEFINE BOTTOM RIGHT CORNER 3560 * at $1617 in AppleWorks 1.3 3570 *-------------------------------- 3580 FUN.CORNER.BR 3590 LDA AW.CH 3600 LDX AW.CV 3610 SET.CORNER.BR 3620 STA AW.RIGHT 3630 STX AW.BOTTOM 3640 RTS 3650 *-------------------------------- 3660 * FUNCTION 0D -- CURSOR TO BEGINNING OF LINE 3670 * Equivalent to RETURN without LINEFEED 3680 * at $1622 in AppleWorks 1.3 3690 *-------------------------------- 3700 FUN.CURSOR.BOL 3710 LDA AW.LEFT 3720 STA AW.CH 3730 RTS 3740 *-------------------------------- 3750 * FUNCTION 0E -- DEFINE TOP LEFT CORNER 3760 * at $1629 in AppleWorks 1.3 3770 *-------------------------------- 3780 FUN.CORNER.TL 3790 LDA AW.CH 3800 LDX AW.CV 3810 STA AW.LEFT 3820 STX AW.TOP 3830 RTS 3840 *-------------------------------- 3850 * FUNCTION 0F -- SET FULL SCREEN WINDOW 3860 * at $1634 in AppleWorks 1.3 3870 *-------------------------------- 3880 FUN.FULL.SCREEN 3890 LDA #0 3900 STA AW.TOP 3910 STA AW.LEFT 3920 LDA #79 3930 LDX #23 3940 BNE SET.CORNER.BR ...ALWAYS 3950 *-------------------------------- 3960 * FUNCTION 10 -- "BEEP!" 3970 * at $1645 in AppleWorks 1.3 3980 *-------------------------------- 3990 FUN.BEEP 4000 LDY #149 4010 .1 LDA #149 4020 .2 SEC 4030 SBC #1 4040 SEC 4050 SEC 4060 BNE .2 11*149-1 = 1638 CYCLES IN INNER LOOP 4070 LDA $C030 TOGGLE SPEAKER 4080 DEY 4090 BNE .1 1649 CYCLES BETWEEN CLICKS 4100 LDA #1 DELAY ABOUT 1/10 SECOND 4110 JMP DELAY.TENTHS 4120 *-------------------------------- 4130 * FUNCTION 11 -- SHUFFLE SCREEN LEFT OR RIGHT 4140 * Following byte is scroll distance and direction: 4150 * If positive, SCROLL RIGHT; else SCROLL LEFT. 4160 * at $165E in AppleWorks 1.3 4170 *-------------------------------- 4180 FUN.SHUFFLE 4190 LDY POSITION.IN.STRING 4200 LDA ($80),Y If positive, SCROLL RIGHT; else SCROLL LEFT 4210 STA SCROLL.DIRECTION 4220 INC POSITION.IN.STRING 4230 LDA AW.TOP POINT TO TOP LINE FIRST 4240 .1 PHA 4250 JSR BASE.CALC.A 4260 LDA AW.RIGHT 4270 BIT SCROLL.DIRECTION 4280 BPL .2 4290 LDA AW.LEFT 4300 .2 STA SHUFFLE.DEST 4310 SEC 4320 SBC SCROLL.DIRECTION 4330 STA SHUFFLE.SOURCE 4340 JSR SCROLL.LEFT.OR.RIGHT 4350 PLA 4360 CMP AW.BOTTOM 4370 BEQ .3 4380 CLC 4390 ADC #1 4400 BNE .1 4410 .3 RTS 4420 *-------------------------------- 4430 SCROLL.LEFT.OR.RIGHT 4440 LDA SCROLL.DIRECTION 4450 BPL .6 ...shuffle right 4460 BMI .2 ...shuffle left 4470 *---Scroll Left------------------ 4480 .1 INC SHUFFLE.DEST 4490 INC SHUFFLE.SOURCE 4500 .2 LDA #" " blank, in case off edge 4510 LDY SHUFFLE.SOURCE 4520 CPY AW.RIGHT 4530 BCC .3 4540 BNE .4 ...off the edge, use a blank 4550 .3 TYA Get source pointer 4560 >LD.SCRN Get character from screen 4570 .4 PHA Save the character 4580 LDA SHUFFLE.DEST destination pointer 4590 >ST.SCRN Store the character on the screen 4600 LDA SHUFFLE.DEST destination pointer 4610 CMP AW.RIGHT was it the right edge? 4620 BNE .1 ...no, keep shuffling 4630 RTS 4640 *---Scroll Right----------------- 4650 .5 DEC SHUFFLE.DEST 4660 DEC SHUFFLE.SOURCE 4670 .6 LDA #" " blank, in case off edge 4680 LDY SHUFFLE.SOURCE 4690 BMI .7 ...off edge, use blank 4700 CPY AW.LEFT 4710 BCC .7 ...off edge, use blank 4720 TYA Get source pointer 4730 >LD.SCRN Get character from screen 4740 .7 PHA Save the character 4750 LDA SHUFFLE.DEST destination pointer 4760 >ST.SCRN Store the character on the screen 4770 LDA SHUFFLE.DEST destination pointer 4780 CMP AW.LEFT was it the left edge? 4790 BNE .5 ...no, keep shuffling 4800 RTS 4810 *-------------------------------- 4820 .MA VEC 4830 .DA FUN.]1-1 4840 .EM 4850 *-------------------------------- 4860 * at $1779 in AppleWorks 1.3 4870 FUNTBL 4880 >VEC HANG.UP 00 4890 >VEC CLR.CH.TO.EOL 01 4900 >VEC CLR.LINE 02 4910 >VEC HOME 03 4920 >VEC CLR.CH.TO.EOS 04 4930 >VEC GOTO.XY 05 4940 >VEC CURSOR.LEFT 06 4950 >VEC CURSOR.RIGHT 07 4960 >VEC CURSOR.UP 08 4970 >VEC CURSOR.DOWN 09 4980 >VEC INVERSE 0A 4990 >VEC NORMAL 0B 5000 >VEC CORNER.BR 0C 5010 >VEC CURSOR.BOL 0D 5020 >VEC CORNER.TL 0E 5030 >VEC FULL.SCREEN 0F 5040 >VEC BEEP 10 5050 >VEC SHUFFLE 11 5060 >VEC HANG.UP 12 5070 >VEC HANG.UP 13 5080 >VEC HANG.UP 14 5090 >VEC HANG.UP 15 5100 >VEC HANG.UP 16 5110 >VEC HANG.UP 17 5120 >VEC HANG.UP 18 5130 >VEC HANG.UP 19 5140 >VEC HANG.UP 1A 5150 >VEC HANG.UP 1B 5160 >VEC HANG.UP 1C 5170 >VEC HANG.UP 1D 5180 >VEC HANG.UP 1E 5190 >VEC HANG.UP 1F 5200 *-------------------------------- 5210 POLL.KEYBOARD RTS (at $1FA7 in AppleWorks 1.3) 5220 *-------------------------------- 5230 * Calculate Address of Screen Line 5240 * at $1717 in AppleWorks 1.3 5250 *-------------------------------- 5260 BASE.CALC.CV 5270 LDA AW.CV 5280 BASE.CALC.A 5290 CMP #$FF <<<filled in with current line number>>> 5300 CURR.LINE .EQ *-1 5310 BEQ .2 5320 STA CURR.LINE 5330 LSR 0000ABCD--E 5340 AND #$03 000000CD--E 5350 ORA #$04 000001CD--E 5360 STA AW.BASE+1 5370 LDA CURR.LINE 5380 AND #$18 000AB000--E 5390 BCC .1 E=0 5400 ADC #$7F E00AB000 5410 .1 STA AW.BASE 5420 ASL 00AB0000 5430 ASL 0AB00000 5440 ORA AW.BASE EABAB000 5450 STA AW.BASE 5460 .2 RTS 5470 *-------------------------------- 5480 * Blank Line from Cursor to End of Line 5490 * at $173A in AppleWorks 1.3 5500 *-------------------------------- 5510 CLR.CH.TO.EOL 5520 LDA AW.RIGHT 5530 LSR 5540 STA N.OVER.2 5550 ROL GET AW.RIGHT AGAIN 5560 SEC 5570 SBC #1 5580 LSR 5590 STA N.MINUS.1.OVER.2 5600 JSR BASE.CALC.CV 5610 LDA AW.CH 5620 LSR 5630 PHA 5640 *---Clear Even Columns----------- 5650 TAY 5660 LDA #" " 5670 STA $C055 INTO AUX MEM 5680 BCC .2 5690 .1 INY 5700 .2 CPY N.OVER.2 5710 BEQ .3 5720 BCS .4 5730 .3 STA (AW.BASE),Y 5740 JMP .1 5750 .4 STA $C054 BACK TO MAIN MEM 5760 *---Clear Odd Columns------------ 5770 PLA 5780 TAY 5790 LDA #" " 5800 .5 STA (AW.BASE),Y 5810 INY 5820 CPY N.MINUS.1.OVER.2 5830 BCC .5 5840 BEQ .5 5850 RTS 5860 *-------------------------------- 5870 N.OVER.2 .BS 1 5880 N.MINUS.1.OVER.2 .BS 1 5890 *-------------------------------- 5900 * DELAY ABOUT (A) TENTHS OF A SECOND 5910 * at $1FD1 in AppleWorks 1.3 5920 *-------------------------------- 5930 DELAY.TENTHS 5940 TAY 5950 .1 LDX #100 ...ABOUT 100 MILLISECONDS 5960 .2 CLC 5970 .3 ADC #1 5980 BCC .3 ...LOOP IS 256*5 CYCLES 5990 DEX 6000 BNE .2 (5*256+6)*100 = 128600 CYCLES 6010 DEY 6020 BNE .1 128606*A CYCLES 6030 RTS 6040 *-------------------------------- 6050 * Some Demonstrations 6060 *-------------------------------- 6070 T 6080 LDA #MY.STRINGS 6090 LDX /MY.STRINGS 6100 .1 STA $80 6110 STX $81 6120 LDY #0 6130 LDA ($80),Y 6140 BEQ .99 ...FINISHED 6150 PHA SAVE LENGTH 6160 INC $80 6170 BNE .2 6180 INC $81 6190 .2 JSR DISPLAY.STRING 6200 CLC 6210 PLA GET LENGTH 6220 ADC $80 ADD TO POINTER 6230 LDX $81 6240 BCC .1 6250 INX 6260 BCS .1 ...ALWAYS 6270 .99 RTS 6280 *-------------------------------- 6290 MY.STRINGS 6300 .DA #Z1 6310 A1 .HS 0F.03.0B Full Scrn, Home, Normal 6320 .AS /ABCDECDEFGHIJKLMNOPQRSTUVWXYZ !@#$%^&*()-+=[]\;:'",.<>?{}|/ 6330 .HS 0A.0D.09 Inverse, RETURN, LINEFEED 6340 .AS /ABCDECDEFGHIJKLMNOPQRSTUVWXYZ !@#$%^&*()-+=[]\;:'",.<>?{}|/ 6350 Z1 .EQ *-A1 6360 *-------------------------------- 6370 .DA #Z2 6380 A2 .HS 0B.0D.09 Normal, RETURN, LINEFEED 6390 .AS "abcdefghijklmnopqrstuvwxyz / 0123456789 _ " 6400 .HS 0A.0D.09 Inverse, RETURN, LINEFEED 6410 .AS "abcdefghijklmnopqrstuvwxyz / 0123456789 _ " 6420 Z2 .EQ *-A2 6430 *-------------------------------- 6440 .DA #Z3 6450 A3 .HS 0B.0D.09 Normal, RETURN, LINEFEED 6460 .AS -/@ABCDECDEFGHIJKLMNOPQRSTUVWXYZ[\]^_/ 6470 .HS 05.00.00 GO TO 0,0 6480 .HS 10.08.08.08.08.08.08.08 Beep, 7 cursor ups 6490 .HS 10.08.08.08.08.08.08.08 Beep, 7 cursor ups 6500 .HS 05.00.17 GO TO 0,23 6510 .HS 10.09.09.09.09.09.09.09 Beep, 7 cursor downs 6520 .HS 10.09.09.09.09.09.09.09 Beep, 7 cursor downs 6530 Z3 .EQ *-A3 6540 *-------------------------------- 6550 .DA #Z4 6560 A4 .HS 10.11.08 Beep, Shuffle Right 8 6570 .HS 10.11.F8 Beep, Shuffle Left 8 6580 .HS 10.11.01.11.01.11.01.11.01.11.01.11.01.11.01 6590 .HS 10.11.FF.11.FF.11.FF.11.FF.11.FF.11.FF.11.FF 6600 .HS 10.11.F8 6610 .HS 10.11.08 6620 .HS 10.11.28 6630 .HS 10.11.D8 6640 Z4 .EQ *-A4 6650 *-------------------------------- 6660 .DA #Z5 6670 A5 .HS 03 HOME 6680 .HS 20.21.22.23.24.25.26.27.28.29.2A.2B.2C.2D.2E.2F 6690 .HS 30.31.32.33.34.35.36.37.38.39.3A.3B.3C.3D.3E.3F.0D.09 6700 .HS 40.41.42.43.44.45.46.47.48.49.4A.4B.4C.4D.4E.4F 6710 .HS 50.51.52.53.54.55.56.57.58.59.5A.5B.5C.5D.5E.5F.0D.09 6720 .HS 60.61.62.63.64.65.66.67.68.69.6A.6B.6C.6D.6E.6F 6730 .HS 70.71.72.73.74.75.76.77.78.79.7A.7B.7C.7D.7E.7F.0D.09 6740 .HS 0A INVERSE 6750 .HS 20.21.22.23.24.25.26.27.28.29.2A.2B.2C.2D.2E.2F 6760 .HS 30.31.32.33.34.35.36.37.38.39.3A.3B.3C.3D.3E.3F.0D.09 6770 .HS 40.41.42.43.44.45.46.47.48.49.4A.4B.4C.4D.4E.4F 6780 .HS 50.51.52.53.54.55.56.57.58.59.5A.5B.5C.5D.5E.5F.0D.09 6790 .HS 60.61.62.63.64.65.66.67.68.69.6A.6B.6C.6D.6E.6F 6800 .HS 70.71.72.73.74.75.76.77.78.79.7A.7B.7C.7D.7E.7F.0D.09 6810 .HS 0B NORMAL 6820 Z5 .EQ *-A5 6830 *-------------------------------- 6840 .DA #Z6 6850 A6 .HS 0D.09 CRLF 6860 .HS 80.81.82.83.84.85.86.87.88.89.8A.8B.8C.8D.8E.8F 6870 .HS 90.91.92.93.94.95.96.97.98.99.9A.9B.9C.9D.9E.9F.0D.09 6880 .HS A0.A1.A2.A3.A4.A5.A6.A7.A8.A9.AA.AB.AC.AD.AE.AF 6890 .HS B0.B1.B2.B3.B4.B5.B6.B7.B8.B9.BA.BB.BC.BD.BE.BF.0D.09 6900 .HS C0.C1.C2.C3.C4.C5.C6.C7.C8.C9.CA.CB.CC.CD.CE.CF 6910 .HS D0.D1.D2.D3.D4.D5.D6.D7.D8.D9.DA.DB.DC.DD.DE.DF.0D.09 6920 .HS E0.E1.E2.E3.E4.E5.E6.E7.E8.E9.EA.EB.EC.ED.EE.EF 6930 .HS F0.F1.F2.F3.F4.F5.F6.F7.F8.F9.FA.FB.FC.FD.FE.FF.0D.09 6940 Z6 .EQ *-A6 6950 *-------------------------------- 6960 .HS 00 6970 *-------------------------------- |
About a year and a half ago, in the July 1986 issue of Apple Assembly Line, I published my replacement for the ProDOS "QUIT" code. It turned out to be a very popular article and program, and various updates and corrections were printed in the August, September, October, and December issues that same year.
In review, the QUIT code is a 768-byte program inside ProDOS-8 which is executed when you leave a system program, such as FILER, AppleWorks, BASIC.SYSTEM (Applesoft), Copy II Plus, the S-C Macro Assembler, and so on. When you QUIT you are really executing the MLI Quit call ($65), which copies those 768 bytes down to RAM starting at $1000 and jumps there. Apple's built-in QUIT code is about as user-friendly as an angry porcupine: if you haven't MEMORIZED the volume names and file names of all your system programs, you almost always end up resetting and re-booting instead of filling in the blanks.
My 1986 Program Selector exactly fits in the 768-byte space Apple's version occupies in the ProDOS system file. It puts a menu of online volumes on the screen, allowing you to select one with the arrow keys and RETURN. Once you select a volume, you see a menu of the SYS and DIR files in the main directory of that volume. Using the arrows and RETURN you can either start up a SYS program or select a subdirectory. If you choose a subdirectory, you get a menu of SYS and DIR files inside it. Hitting ESCAPE always takes you back to the Volume Menu. As I wrote the Program Selector, it requires an Apple //e, //c, or //gs, and works in 80-columns.
The modifications already published include fixing one bug, making it work with a Videx-compatible 80-column card for Apple II Plus owners, following Apple's published spec's for substitute QUIT-code, and making it begin with the menu bar on something other than the first volume in the menu.
Quite a few readers of AAL are now using this Program Selector. Some have gone the extra mile by adapting the S-C Program Selector to their own preferences. Jim Hammond (of FastFind SUPER INDEX) liked it so well he turned it into a product which he sells (with my permission) as "STARTER/QUITTER". Larry Skutchan (a blind user who adapted the S-C Word Processor into a talking version) adapted it to work with the Echo Speech Synthesizer. Brooke Boering (creator of CeeMac and Fire Organ) put in a feature allowing you to limit the Volume Menu to a particular disk drive. Brooke's ideas are what led me to try to improve and upgrade my program.
The main computer here at the office is an Apple //e with a 10-meg Sider (ProDOS sees it as two drives in slot 7), two standard Apple floppies in slot 6, a RamFactor card in slot 4 (simulates one drive), a 1-meg RamWorks card in the AuxSlot (simulates a drive in slot 3, Drive 2), and a 3.5 inch drive in slot 2 (ProDOS thinks there are two drives there, even though I only have one). When I type BYE or in some other way select the ProDOS QUIT code, my S-C Program Selector takes over. The old version seemed to go away and die for several seconds while ProDOS did a complete ONLINE check to find out what volume if any was mounted in each and every drive. If my hard disk is not turned on, that takes several seconds for the firmware to timeout. If no floppies are in the 5.25 drives, they go through spinning, re-calibration, and the works before giving up.
It finally dawned on me that what I wanted was to direct the Program Selector to only try the slot and drive I booted from. Most likely if I booted from it, there is some kind of volume there. Of course I still wanted the capability of seeing every volume, but I did not want to waste all that time EVERY time!
Brooke told me six months or more ago that I could plug a drive ID into my ONLINE call (line 2760 of the original program) and it would limit the display to that one volume. It turned out to be a little harder than that, but I did get that to work. But I could not decide on just one slot and drive. I wanted that byte to be set up by ProDOS at boot-time to point to the booting drive.
I remembered that the ProDOS startup code began by plugging the boot drive ID into its own ONLINE call, which it uses to get the Volume Name. I looked up the code in the Supplement to "Beneath Apple ProDOS", and found it. In ProDOS 1.1.1 it is done by the first two instructions at $2000; in ProDOS 1.2, 1.3, and 1.4 it starts six bytes later at $2006. The first instruction is "LDA $43", which picks up the drive ID (slot number times 16) that was used to load the PRODOS or P8 file. The second one is "STA $21FE" for ProDOS 1.1.1, and "STA" somewhere else for later versions. I decided I could patch into that "STA" instruction a call to a piece of patch code which would not only do the patched-over STA for Apple, but also an additional one for me. More on this later, when we get into the code for my new Program Selector.
Anyway, the Program Selector now comes up only showing the Volume Name of the volume currently in the drive ProDOS was booted from. If that is the volume I want, I just hit RETURN and see the menu of SYS and DIR files on that volume. If I booted from RamFactor, this all happens at blinding speed.
If the boot-drive is not the drive I want, I can type a digit and select a different drive or all drives. Typing a digit 1 through 7 changes to drive 1 of that slot and displays the Volume Name in that drive. Typing the digit "8" changes to drive 2 in the same slot. Why 8? Why not? It made the code shorter, and it worked. Typing "0" changes back to the old way, making a menu of all online volumes. If you select a drive which has no volume mounted, you will get an empty menu. No problem, just select a different drive or all-drives, and continue.
I also made various other improvements here and there, such as making sure that text mode with a full-screen window is selected. I had to revise the "help" message at the bottom of the menu display to include information on the digits 0-8.
A major constraint in adding new features was that I wanted to retain the advantage of fitting inside the 768-byte hole in the ProDOS file. In developing the new version I decided not to worry about size too much until all the new features were working. I tested them by BRUNning the code at $1000, instead of going through the process of putting it into ProDOS every time. Then when it was all ready, I started looking for ways to shrink the code and make it fit in only 768 bytes.
It ran about 32 bytes over, so I needed a lot of shrinking. For some reason I don't remember, back in 1986 I decided to keep a lot of variables out of page zero. There is no requirement to do this, so I moved these variables and saved almost all the bytes I wanted. All instructions referencing these variables shrank from three to two bytes. The rest of the savings were found by careful study of the code. If you compare the new listing which follows with the one I published in 1986 you can find the tricks I pulled. The new version, even with all the new features, is now shorter than the original! There are actually four unused bytes!
I also wrote a program to automatically install the new Program Selector inside the ProDOS file. Well, almost automatically. You still have to BLOAD PRODOS or BLOAD P8, and UNLOCK the file if it is LOCKed. Then you "-" or BRUN my INSTALL.QUITTER program, and it automatically does the installation. If successful, you get a nice message to that effect; then you have to BSAVE the image and re-LOCK it. I thought it would be too dangerous to make all of the above entirely automatic. If my installer made a mistake.... So, I left the crucial part manual.
The auto-installer does differentiate between ProDOS 1.1.1 and the later versions. It makes the boot-drive patch at either $2002 or $2008, depending on where it finds the STA instruction. And if it cannot find that instruction, it tells you so and quits. The Program Selector image is copied either to $5700 (for ProDOS 1.1.1) or $5900 (for later versions).
The code for the auto-installer is executed when you BRUN INSTALL.QUITTER. Lines 1460-2270 are the installation code, and lines 2280 to the end are the Program Selector image. Lines 1490-1710 try to determine which version of ProDOS, if any, is in memory starting at $2000. If it finds a recognizable version, it sets up various pointers according to the version. If not, it prints out the long message from lines 2160-2220.
Lines 1720-1800 copy a JSR to my patch code over the top of the STA xxxx instruction which starts at either $2002 or $2008. It also modifies my patch code to include exactly the correct STA xxxx instruction which we are patching over. The patch code is at the very end of the Program Selector image, in lines 5970-6020. Later, when this patched ProDOS file is booted or otherwise executed, my patch code will install the boot drive ID into the ONLINE call block at line 5880.
Lines 1810-1950 copy the Program Selector image into the ProDOS image, at either $5700 or $5900 depending on version. Assuming we got this far, lines 1970-1990 will print out the "SUCCESSFUL" message.
900 .LIST MOFF 1000 *SAVE NEW.QUIT.CODE 1010 *-------------------------------- 1020 * Installation: 1030 * 1. BLOAD PRODOS,TSYS,A$2000 1040 * 2. BRUN INSTALL.QUITTER 1050 * 3. BSAVE PRODOS,TSYS,A$2000 1051 *-------------------------------- 1052 .MA ASC Macro to shorten listing 1053 .AS -"]1" 1054 .EM 1060 *-------------------------------- 1070 .DUMMY 1080 .OR 0000 1090 BPNTR .BS 2 1100 SPNTR .BS 2 1110 DPNTR .BS 2 1120 DIR.INDEX .BS 1 1130 DIR.START .BS 1 1140 MAX.DIRPNT .BS 1 1150 SEL.LINE .BS 1 1160 MAX.LINE .BS 1 1170 UNIT .BS 1 1180 LENGTH .BS 1 1190 CURTYP .BS 1 1200 CURBLK .BS 1 1210 *-------------------------------- 1220 .OR $800 1230 OPNBUF .BS 1024 1240 DIRBUF .BS 512 1250 .ED 1260 *-------------------------------- 1270 PATHNAME .EQ $280 1280 BUFFER .EQ $2000 1290 ENTLEN .EQ BUFFER+$23 ENTRY LENGTH 1300 ENTCNT .EQ BUFFER+$24 # ENTRIES PER BLOCK 1310 *-------------------------------- 1320 CV .EQ $25 1330 INVFLG .EQ $32 1340 *-------------------------------- 1350 INIT .EQ $FB2F 1360 HOME .EQ $FC58 1370 CLREOL .EQ $FC9C 1380 COUT .EQ $FDED 1390 CROUT .EQ $FD8E 1400 SETINV .EQ $FE80 1410 SETNORM .EQ $FE84 1420 *-------------------------------- 1430 MLI .EQ $BF00 1440 BITMAP .EQ $BF58 1450 *-------------------------------- 1460 .OR $1000 1470 .TF INSTALL.QUITTER 1480 *-------------------------------- 1490 INSTALL.QUITTER 1500 LDX /$5700 WHERE IMAGE IS IN 1.1.1 1510 LDA /$2000 WHERE LDA $43 IS IN 1.1.1 1520 STA BPNTR+1 1530 LDY #0 1540 STY DPNTR 1550 LDA $2000 SEE IF PRODOS IMAGE IS HERE 1560 CMP #$4C IF "JMP" THEN PROBABLY 1.4 1570 BNE .1 ...PROBABLY 1.1.1 1580 LDY #6 WHERE LDA $43 IS IN 1.4 1590 LDX /$5900 WHERE IMAGE IS IN 1.4 1600 .1 STY BPNTR POINT AT LDA $43 1610 STX DPNTR+1 POINT AT IMAGE OF QUITTER 1620 INX 1630 INX 1640 STX QPATCH+2 ADDRESS IN IMAGE OF "ONLINE+1" 1650 STX JSR.QPATCH+2 ADDRESS IN IMAGE OF QPATCH 1660 LDY #2 1670 .2 LDA PRODOS.ID.STRING,Y 1680 CMP (BPNTR),Y 1690 BNE .99 ...NEITHER, QUIT. 1700 DEY 1710 BPL .2 1720 *---Trust we have ProDOS image--- 1730 LDY #2 POINT AT STA XXXX 1740 .3 LDA (BPNTR),Y 1750 STA QPATCH-2+3,Y 1760 LDA JSR.QPATCH-2,Y 1770 STA (BPNTR),Y 1780 INY 1790 CPY #5 1800 BCC .3 1810 *---Copy Quitter into ProDOS----- 1820 LDA #QUIT.IMAGE 1830 STA SPNTR 1840 LDA /QUIT.IMAGE 1850 STA SPNTR+1 1860 LDX #3 COPY 3 PAGES 1870 LDY #0 1880 .4 LDA (SPNTR),Y 1890 STA (DPNTR),Y 1900 INY 1910 BNE .4 1920 INC SPNTR+1 1930 INC DPNTR+1 1940 DEX 1950 BNE .4 1960 *---Successful, say so and end--- 1970 LDY #IQ.GOOD 1980 JSR IQ.PRINT 1990 RTS FINISHED 2000 *---No ProDOS, or already patched--- 2010 .99 LDY #IQ.BAD 2020 JSR IQ.PRINT 2030 RTS 2040 *-------------------------------- 2050 IQ.OUT JSR COUT 2060 INY 2070 IQ.PRINT 2080 LDA IQ,Y 2090 BNE IQ.OUT 2100 RTS 2110 *-------------------------------- 2120 IQ .EQ * 2130 IQ.GOOD .EQ *-IQ 2140 >ASC "SUCCESSFULLY INSTALLED NEW QUIT CODE." 2150 .HS 8D00 2160 IQ.BAD .EQ *-IQ 2170 >ASC "EITHER PRODOS NOT HERE," 2180 .HS 8D 2190 >ASC "OR NOT VERSION 1.1.1 OR 1.4," 2200 .HS 8D 2210 >ASC "OR ALREADY INSTALLED QUIT CODE." 2220 .HS 8D00 2230 *-------------------------------- 2240 PRODOS.ID.STRING 2250 .HS A5.43.8D 2260 JSR.QPATCH 2270 JSR QPATCH.EP 2280 *-------------------------------- 2290 QUIT.IMAGE 2300 .PH $1000 2310 *-------------------------------- 2320 QUITTER 2330 CLD REQUIRED BY "STANDARDS" 2340 LDA $C082 MOTHERBOARD ROMS 2350 JSR INIT TEXT MODE, FULL SCREEN WINDOW 2360 JSR SETNORM 2370 LDX #$16 2380 LDA #0 PREPARE VIRGIN BITMAP 2390 .1 STA BITMAP,X 2400 STX BITMAP+$17 LAST TIME STORES $01, LOCK OUT $BF00 PAGE 2410 DEX 2420 BNE .1 2430 LDA #$CF 2440 STA BITMAP 2450 *---LIST VOLUME NAMES------------ 2460 .2 LDA #$99 CTRL-Y 2470 JSR $C300 SET I/O HOOKS, 80-COL MODE, CLEAR SCREEN 2480 LDY #Q.SDV 2490 JSR MSG 2500 JSR CLOSE.ALL.FILES 2510 LDY #0 2520 STY MAX.DIRPNT 2530 STY DIR.START 2540 STY PATHNAME 2550 STY BUFFER+16 (IN CASE "ONLINE" PRESET TO SPECIFIC S/D) 2560 JSR MLI 2570 .DA #$C5,ONLINE 2580 .3 STY SEL.LINE 2590 JSR DISPLAY.VOLUMES 2600 LDY #Q.VHELP 2610 JSR MSG 2620 JSR GET.KEY 2630 BCC .3 ...ARROW KEYS 2640 BNE .2 ...ESCAPE KEY 2650 *---READ DIRECTORY--------------- 2660 .4 JSR READ.THE.FILE 2670 BCS .7 2680 *---PRINT PATHNAME--------------- 2690 JSR HOME 2700 LDY #0 2710 .5 LDA PATHNAME+1,Y 2720 ORA #$80 2730 JSR COUT 2740 INY 2750 CPY PATHNAME 2760 BCC .5 2770 *---COLLECT FILENAMES------------ 2780 LDX #0 2790 LDA #$FF FIRST JUST "SYS" FILES 2800 JSR SCAN.DIRECTORY 2810 LDA #$0F THEN JUST "DIR" FILES 2820 JSR SCAN.DIRECTORY 2830 TXA SEE IF ANY FILES FOUND 2840 BEQ .2 ...NO, BACK TO THE TOP 2850 LDA #0 MARK END OF LIST 2860 STA DIRBUF+256,X 2870 STX MAX.DIRPNT 2880 *---LIST THE FILENAMES----------- 2890 TAY Y=0 2900 STY DIR.START 2910 .6 STY SEL.LINE 2920 JSR DISPLAY.FILES 2930 LDY #Q.VHELP 2940 JSR MSG 2950 JSR GET.KEY 2960 BCC .6 ...ARROW KEYS 2970 BNE .2 ...ESCAPE KEY 2980 LDY #$10 2990 LDA (SPNTR),Y GET FILE TYPE 3000 BPL .4 DIRECTORY ($0F) 3010 *---SYS FILE, LOAD & EXECUTE----- 3020 JSR MLI SET PREFIX 3030 .DA #$C6,PATH 3040 JSR READ.THE.FILE 3050 BCS .7 ...ERROR IN READING 3060 JMP BUFFER 3070 .7 JMP QUITTER 3080 *-------------------------------- 3090 READ.THE.FILE 3100 LDY #0 APPEND CURRENTLY SELECTED NAME 3110 LDA (SPNTR),Y GET LENGTH OF NAME 3120 AND #$0F 3130 STA LENGTH 3140 LDX PATHNAME CURRENT LENGTH 3150 LDA #'/' 3160 .1 INX 3170 INY 3180 STA PATHNAME,X 3190 LDA (SPNTR),Y 3200 DEC LENGTH 3210 BPL .1 3220 STX PATHNAME 3230 JSR MLI OPEN THE FILE 3240 .DA #$C8,OPEN 3250 BCS RF.ERR 3260 LDA O.REF FILE REFERENCE NUMBER 3270 STA R.REF 3280 JSR MLI READ THE WHOLE FILE 3290 .DA #$CA,READ 3300 BCC CLOSE.ALL.FILES 3310 CMP #$4C IS IT JUST EOF? 3320 SEC 3330 BNE RF.ERR ...NO 3340 CLOSE.ALL.FILES 3350 JSR MLI CLOSE THE FILE 3360 .DA #$CC,CLOSE 3370 RF.ERR RTS 3380 *-------------------------------- 3390 SCAN.DIRECTORY 3400 STA CURTYP TYPE WE ARE COLLECTING 3410 LDA #0 START WITH FIRST BLOCK 3420 .1 STA CURBLK 3430 LDA #BUFFER+4 FIRST 4 BYTES OF BLOCK SKIPPED 3440 STA DPNTR 3450 CLC COMPUTE PAGE OF PNTR 3460 LDA /BUFFER+4 3470 ADC CURBLK 3480 STA DPNTR+1 3490 LDA ENTCNT 3500 STA LENGTH 3510 *-------------------------------- 3520 .2 LDY #0 3530 LDA (DPNTR),Y 3540 AND #$F0 3550 BEQ .4 ...DELETED FILE 3560 CMP #$E0 ...HEADER? 3570 BCS .4 ...YES 3580 LDY #$10 3590 LDA (DPNTR),Y LOOK AT FILE TYPE 3600 CMP CURTYP 3610 BNE .4 ...NOT CURRENT TYPE 3620 *---DIR or SYS file-------------- 3630 .3 LDA DPNTR 3640 STA DIRBUF,X 3650 LDA DPNTR+1 3660 STA DIRBUF+256,X 3670 INX 3680 *---ADVANCE TO NEXT ENTRY-------- 3690 .4 CLC 3700 LDA DPNTR 3710 ADC ENTLEN 3720 STA DPNTR 3730 BCC .5 3740 INC DPNTR+1 3750 .5 DEC LENGTH AT END OF BLOCK YET? 3760 BNE .2 ...NO, CONTINUE IN BLOCK 3770 CLC 3780 LDA CURBLK 3790 ADC #2 3800 CMP ACTLEN+1 3810 BCC .1 ...YES, READ NEXT BLOCK 3820 RTS 3830 *-------------------------------- 3840 DISPLAY.VOLUMES 3850 JSR SETUP.DISPLAY.LOOP 3860 LDA /BUFFER 3870 STA BPNTR+1 3880 LDA #BUFFER 3890 .1 STA BPNTR 3900 LDY #0 3910 LDA (BPNTR),Y 3920 BEQ .5 ...END OF LIST 3930 AND #$0F 3940 BEQ .3 ...NO VOLUME HERE 3950 *-------------------------------- 3960 JSR CHECK.FOR.SEL.LINE 3970 *-------------------------------- 3980 LDA (BPNTR),Y GET UNIT NUMBER 3990 LSR ISOLATE SLOT NUMBER 4000 LSR 4010 LSR 4020 LSR 4030 AND #7 4040 ORA #"0" 4050 JSR COUT PRINT SLOT NUMBER 4060 LDA #"/" 4070 JSR COUT 4080 LDA (BPNTR),Y GET UNIT NUMBER AGAIN 4090 ASL SET CARRY IF DRIVE 2 4100 LDA #"1" ASSUME DRIVE 1 4110 ADC #0 CHANGE TO 2 IF TRUE 4120 JSR COUT 4130 LDA #" " PRINT TWO SPACES 4140 JSR COUT 4150 JSR COUT 4160 JSR PRINT.BPNTR.NAME 4170 *-------------------------------- 4180 .3 CLC POINT TO NEXT VOLUME NAME 4190 LDA BPNTR 4200 ADC #16 4210 BCC .1 STILL IN SAME PAGE 4220 .5 RTS 4230 *-------------------------------- 4240 PRINT.BPNTR.NAME 4250 LDY #0 4260 LDA (BPNTR),Y GET NAME LENGTH 4270 AND #$0F 4280 TAX 4290 .1 INY PRINT THE VOLUME OR FILE NAME 4300 LDA (BPNTR),Y 4310 ORA #$80 4320 JSR COUT 4330 DEX 4340 BNE .1 4350 *-------------------------------- 4360 .2 LDA #" " PRINT TRAILING BLANKS 4370 JSR COUT 4380 INY 4390 CPY #16 4400 BCC .2 4410 JSR SETNORM NORMAL MODE NOW 4420 INC MAX.LINE COUNT THE LINE 4430 JMP CROUT 4440 *-------------------------------- 4450 GET.KEY 4460 LDY SEL.LINE CURRENT BRIGHT LINE 4470 .1 LDA $C000 READ KEY FROM KEYBOARD 4480 BPL .1 4490 STA $C010 CLEAR THE STROBE 4500 CMP #$B0 CHECK FOR "0"..."7" 4510 BCC .12 4520 CMP #$B8 4530 BEQ .25 4540 BCS .1 4550 ASL 4560 ASL 4570 ASL 4580 ASL LEAVE SLOT*16 IN A, CARRY SET 4590 .11 STA ONLINE+1 CHANGE ONLINE CALL 4600 LDA #$9B SIMULATE AN <ESCAPE> 4610 .12 CMP #$8D 4620 BEQ .2 <RETURN> 4630 CMP #$88 <-- 4640 BEQ .3 4650 CMP #$95 --> 4660 BEQ .7 4670 CMP #$8A DOWN ARROW 4680 BEQ .7 4690 CMP #$8B UP ARROW 4700 BEQ .3 4710 CMP #$9B ESCAPE 4720 BNE .1 GET ANOTHER CHARACTER 4730 ASL ...SET .NE. and CARRY 4740 .2 RTS 4750 .25 LDA ONLINE+1 4760 ORA #$80 TRY DRIVE 2 4770 BNE .11 ...ALWAYS 4780 *---<UP OR LEFT ARROW>----------- 4790 .3 CLC 4800 DEY IS CURRENT BRIGHT LINE TOP LINE? 4810 BPL .8 ...NOT TOP LINE 4820 LDY DIR.START ARE WE DISPLAYING THE FIRST ONE? 4830 BEQ .5 ...YES 4840 DEC DIR.START ...NO, MOVE TOWARD FIRST LINE 4850 .4 LDY #0 MAKE FIRST LINE BRIGHT 4860 RTS 4870 .5 LDY MAX.LINE MAKE LAST LINE BRIGHT 4880 DEY 4890 RTS 4900 *---<DOWN OR RIGHT ARROW>-------- 4910 .7 INY MOVE TOWARD LAST LINE 4920 CPY MAX.LINE BEYOND END OF SCREEN? 4930 BCC .8 ...NO 4940 LDA MAX.DIRPNT ...YES, CHECK IF SHOWING LAST LINE 4950 SBC #17 4960 BCC .4 ...YES 4970 CMP DIR.START 4980 BCC .4 ...YES 4990 INC DIR.START ...NO, MOVE TOWARD LAST LINE 5000 LDY SEL.LINE 5010 CLC 5020 .8 RTS 5030 *-------------------------------- 5040 DISPLAY.FILES 5050 JSR SETUP.DISPLAY.LOOP 5060 LDA DIR.START 5070 STA DIR.INDEX 5080 JSR CLEAR.LINE.OR.PRINT.MORE.MSG 5090 *-------------------------------- 5100 .1 LDX DIR.INDEX 5110 LDY DIRBUF+256,X 5120 BEQ .4 ...END OF LIST 5130 STY BPNTR+1 5140 LDA DIRBUF,X 5150 STA BPNTR 5160 JSR CHECK.FOR.SEL.LINE 5170 *-------------------------------- 5180 .2 LDY #$10 5190 LDA (BPNTR),Y 5200 BMI .3 ...SYS FILE 5210 LDY #Q.DIR 5220 .HS 2C 5230 .3 LDY #Q.SYS 5240 JSR MSG 5250 JSR PRINT.BPNTR.NAME 5260 *-------------------------------- 5270 INC DIR.INDEX 5280 DEC LENGTH 5290 BNE .1 5300 .4 LDA DIR.INDEX 5310 CMP MAX.DIRPNT 5320 *-------------------------------- 5330 CLEAR.LINE.OR.PRINT.MORE.MSG 5340 BEQ .1 CLEAR LINE 5350 LDY #Q.MORE 5360 BNE MSG ...ALWAYS 5370 .1 JSR CLREOL 5380 JMP CROUT 5390 *-------------------------------- 5400 SETUP.DISPLAY.LOOP 5410 LDA #16 MAX 16 LINES IN LIST 5420 STA LENGTH 5430 LDY #0 5440 STY MAX.LINE 5450 INY SAME AS VTAB 3, HTAB 1 5460 STY CV 5470 JMP CROUT 5480 *-------------------------------- 5490 CHECK.FOR.SEL.LINE 5500 LDA MAX.LINE SEE IF CURRENT LINE SHOULD 5510 CMP SEL.LINE BE INVERSE MODE 5520 BNE .1 ...NO 5530 LDA BPNTR ...YES, SO SETUP POINTER 5540 STA SPNTR 5550 LDA BPNTR+1 5560 STA SPNTR+1 5570 LDA #$3F & SET INVERSE MODE 5580 STA INVFLG 5590 .1 RTS 5600 *-------------------------------- 5610 MSG1 JSR COUT 5620 INY 5630 MSG LDA QTS,Y 5640 BNE MSG1 5650 RTS 5660 *-------------------------------- 5670 QTS .EQ * 5680 Q.SDV .EQ *-QTS 5690 >ASC "S/D Volume Name" 5700 .HS 00 5710 Q.VHELP .EQ *-QTS 5720 .HS 8D 5730 >ASC "Select with ARROWS and <RETURN>" 5740 .HS 8D 5750 >ASC "See Volumes with <ESCAPE> or 0-8" 5760 .HS 8D00 5770 Q.SYS .EQ *-QTS 5780 >ASC "SYS -- " 5790 .HS 00 5800 Q.DIR .EQ *-QTS 5810 >ASC "DIR -- " 5820 .HS 00 5830 Q.MORE .EQ *-QTS 5840 >ASC "<<<MORE>>>" 5850 .HS 8D00 5860 *-------------------------------- 5870 CLOSE .DA #1,#0 5880 ONLINE .DA #2,#0,BUFFER 5890 OPEN .DA #3,PATHNAME,OPNBUF 5900 O.REF .BS 1 5910 READ .DA #4 5920 R.REF .BS 1 5930 .DA BUFFER,$9F00 5940 ACTLEN .BS 2 5950 PATH .DA #1,PATHNAME 5960 *-------------------------------- 5970 .BS $1300-*-7 5980 QPATCH.EP 5990 .EP 6000 QPATCH STA ONLINE+1 6010 STA * 6020 RTS 6030 *-------------------------------- |
Sometimes there just is not enough memory to hold the entire symbol table of a large assembly, especially in the ProDOS version of the S-C Macro Assembler. In that version, the symbol table normally begins at $1000 and grows upward, while the source program snugs up against $7400, hanging downward from there. Even with the use of the .INB directive to bring in segments of the source code one disk block at a time, extra-large programs can run out of memory during assembly. It can be especially frustrating in an Apple //e or later machine, when you know there is a lot of free memory just across the Soft-Switch River, over there in Aux-land.
Until now I have resisted using this memory, trying to remain fully compatible with older 64K machines and trying to keep the speed advantages of all-main-memory. But finally, the need became personal enough. I created a special version which puts the entire symbol table in Aux RAM. It will run on a 128K or larger //e, //c, or IIgs; I haven't tried it, but it should also run in an Apple II Plus with an Applied Engineering Transwarp card plugged and turned on. The symbol table still begins at $1000, but in AuxRAM; and it can rise as high as $BFFF without challenge. That is $B000 total bytes, or about twice as many as were available between $1000 and the bottom of the source program in MainRAM. The space below $1000, down as far as $800, is occupied by macro private labels, if you use any. Obviously, there is also now more room in Main RAM for your source code and/or object code. A Minus: if you have a /RAM disk installed in AuxRAM, the symbol table walks all over it. However, if you are using a RamWorks card or the like, with their PRODRIVE software, bank 0 of AuxRAM is left available for just these type uses.
If you need something like this, it's finally here. I'm calling it Version 2.1, and as a registered owner of the ProDOS version of the S-C Macro Assembler you can have a copy for only $10.
At long last, Addison-Wesley assures me they have printed the "IIgs Toolbox Reference" manuals, Volumes 1 and 2. These are probably indispensable tools for any serious IIgs programmers. Until now, you could only get them in beta versions through APDA. They are over 750 pages each, and cost $26.95 each ($24 plus shipping charge if you order from us). Each of the standard tools is described in great detail, with examples in both assembly language and C.
If they kept the same arrangement as my pre-beta copy (dated Nov 1986), there are a total of 23 chapters in the set. In my edition, Volume 1 covers toolsets in general, Desktop Bus, Control Mngr, Desk Mngr, Dialog Mngr, Event Mngr, Font Mngr, Integer Math, Line Edit, Memory Mngr, Menu Mngr, Misc.Tools, and Print Mngr; Volume 2 covers Quickdraw, SANE, Scheduler, Scrap Mngr, Sound Mngr, Std. File Operations, Text.tools, Tool Locater, and Window Manager. Volume 2 also includes an appendix on writing your own tool set.
There remains one more book in the IIgs series, the "IIgs Programmer's Introduction". A-W is now predicting April '88 for the publishing date, and a price of $32.95. I guess I am old-fashioned, but to my mind this book should have been published FIRST, and included FREE with every IIgs purchase. Oh, well.... We will accept orders on this one now, and hold them until the book comes, at a price of $30 plus shipping (see note on page 3 for details on shipping charges).
BONUS PROGRAM (not printed in the original newsletter)
1000 .LIST XOFF 1010 *SAVE ONLINE.CMD 1020 *-------------------------------- 1030 BUFFER .EQ $2000 1040 *-------------------------------- 1050 PRBYTE .EQ $FDDA 1060 COUT .EQ $FDED 1070 CROUT .EQ $FD8E 1080 *-------------------------------- 1090 MLI .EQ $BF00 1100 *-------------------------------- 1110 T 1120 DISPLAY.VOLUMES 1130 JSR MLI 1140 .DA #$C5,ONLINE 1150 LDA #0 1160 .1 PHA 1170 TAY 1180 LDA BUFFER,Y 1190 BEQ .5 ...END OF LIST 1200 PHA 1210 LDA #"S" 1220 JSR COUT 1230 PLA 1240 PHA 1250 LSR ISOLATE SLOT NUMBER 1260 LSR 1270 LSR 1280 LSR 1290 AND #7 1300 ORA #"0" 1310 JSR COUT PRINT SLOT NUMBER 1320 LDA #"," 1330 JSR COUT 1340 LDA #"D" 1350 JSR COUT 1360 PLA 1370 PHA 1380 ASL SET CARRY IF DRIVE 2 1390 LDA #"1" ASSUME DRIVE 1 1400 ADC #0 CHANGE TO 2 IF TRUE 1410 JSR COUT 1420 LDA #" " PRINT SPACE 1430 JSR COUT 1440 PLA get dsssllll again 1450 AND #$0F isolate length 1460 BEQ .3 no name, show error code 1470 TAX 1480 LDA #"/" 1490 .2 JSR COUT 1500 INY PRINT THE VOLUME OR FILE NAME 1510 LDA BUFFER,Y 1520 ORA #$80 1530 DEX 1540 BPL .2 1550 LDA #"/" 1560 BNE .4 ...ALWAYS 1570 .3 LDA #"(" 1580 JSR COUT 1590 LDA BUFFER+1,Y GET ERROR CODE 1600 JSR PRBYTE 1610 LDA #")" 1620 .4 JSR COUT 1630 JSR CROUT 1640 *-------------------------------- 1650 .5 CLC POINT TO NEXT VOLUME NAME 1660 PLA 1670 ADC #16 1680 BCC .1 STILL IN SAME PAGE 1690 RTS 1700 *-------------------------------- 1710 ONLINE .DA #2,#0,BUFFER 1720 *-------------------------------- 1730 .LIF |
Apple Assembly Line (ISSN 0889-4302) is published monthly by S-C SOFTWARE CORPORATION,
P. O. Box 280300, Dallas, TX 75228 Phone (214) 324-2050.
Subscription rate is $24 per year in the USA, Canada, and Mexico, or $36 in other countries.
Back issues are $1.80 each for Volumes 1-7, $2.40 each for Volume 8
(plus postage). A subscription tothe newsletter with a Monthly
Disk containing all program source code and article text is $64 per year in
the USA, Canada and Mexico, and $90 to other countries.
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.)