In This Issue...
About Back Issues...
Don't be bashful, just tell me what you need! I have copies of all back issues in stock, at $1.80 each, $18 per volume, or $120 for all seven of the completed volumes. Remember, our volume-year runs from October through September, and I started in 1980.
I also have Quarterly Disks for the entire period, $15 each or four for $60. Each Quarterly Disk covers a calendar quarter, so just specify which months you need. Starting with January 1986 I went to Monthly Disks, so you can get any individual month for only $5 from then till now. Each Quarterly or Monthly Disk contains all of the source code printed in the AAL issues it covers, in the format for the S-C Macro Assembler. Early ones are all DOS, later are split with both DOS and ProDOS directories on the same disk. And since sometime last year we have also been including all of the text files for the articles themselves, as a service to readers who like to have the Echo Speech Synthesizer (or other brands) read it all to them.
Toward a New Standard Assembly Language...
Randy Hyde, who you may remember as author of the Lisa 6502 Assembler, is attempting to organize interested parties to produce a new definitive 65816 assembly language standard. He claims the existing standard, based on Orca, is confusing, overly complex, and idiosyncratic; a new standard could allow assemblers with more power than the Microsoft 8086 assembler to be written for the 65816. Right now Randy is collecting ideas and contacting key individuals (such as the authors of the various existing 65816 assemblers and 65816 books, and the chip's designer), and planning for a conference at WDC in Arizona some time this summer. If you are interested in participating in any way, write to him: Randall Hyde, 65C816 Standards, 2271 Indian Horse Drive, Norco, CA 91760.
There are a lot of useful subroutines inside AppleWorks. I have been looking at a raw disassembly, and have learned a few new tricks. Even though AppleWorks is ProDOS-based, the subroutines are general enough that you can use them in your own code in any operating system.
All my observations are based on version 1.3, as that is the only one I have. Meanwhile, Apple has moved on to version 2.0 and turned it all over to Claris. That is all right, because I am not proposing that we use the subroutines by loading AppleWorks and calling them; I am proposing that we copy the code or some modification of it into our own programs.
When you boot AppleWorks 1.3 the first thing it does is to copy the APLWORKS.SYSTEM image down from $2000 to $1000. I simply loaded it there from inside the S-C assembler with "BLOAD APLWORKS.SYSTEM,TSYS,A$1000". Then I printed out a huge listing with the monitor's "L" command, and went to work with a pencil. I don't even know what section of AppleWorks I am looking at yet, but it is chock full of interesting code I can use.
The first thing I noticed was that a lot of code did not disassemble correctly: The "L" command went weird after a lot of JSR's. It seems the author liked to call subroutines with parameters in data form following the JSR, like ProDOS MLI calls. In most cases (all I could find) there are either two, four, or six bytes of parameters after these JSR's. The subroutines all call, in turn, on a magic little subroutine which copies the parameter bytes to a standard area in page zero, starting at $9A. This GET.x.PARMS subroutine also updates the return address on the stack so that, when the parameterized call is completed, execution will resume after the parameter bytes.
The GET.x.PARMS subroutine is shown as I found it inside AppleWorks 1.3, in lines 1300-1580. I used the .PH $18AD line to make it look exactly the same as the AppleWorks image. The code looked a little fluffy to me, so I wrote my own version (which is shorter and swifter); you can see it in lines 1830-2070.
The AppleWorks version is evidently one of the busiest pieces of code in the system. I say that, because the author chose to poll the keyboard inside GET.x.PARMS. Line 1550 calls the POLL.KEYBOARD subroutine, which I show with comments in lines 1600-1810. I left this out of my rendition of GET.x.PARMS, because I am building a little package of routines for my own use. I included the listing here because I thought you might like to see how it is done. Notice the buffer holds only ten characters, as written.
Notice that there are three entry points to the GET.x.PARMS subroutine. The first copies four bytes following the JSR to $9A...9D; the second copies only two bytes; and the third copies any number, which you specify in the A-register. I used a memory search to uncover all the calls on this third entry, and I only found calls which wanted to copy six bytes. There may be others, hidden in other sections of the AppleWorks code.
My more efficient rendition saves one byte by using the BIT opcode ($2C) to skip over the two-byte LDA #2 instruction (see lines 1370 and 1870). I also save by pushing the byte count on the stack instead of saving it in RAM: the storage location is saved, and the PLA is two bytes shorter than a STA. However, I have to pull the byte back off the stack at line 2050, so the net saving is only two bytes. Line 2060, the LDY #0, can be deleted and save another two bytes (Y is already 0, in order for the loop in lines 2010-2040 to terminate). I don't need it because I have not called POLL.KEYBOARD. By the way, we do want to be sure that Y=0 here, because a lot of the subroutines depend on it. If we don't make it one of the functions of GET.x.PARMS, we will have to add a line to most of the subroutines which call GET.x.PARMS.
Inside AppleWorks a lot of string processing goes on. All of the strings I have observed are stored in memory as a length byte followed by the string data bytes. The maximum string will have 255 data bytes. A byte count equal to $00 represents a null string. Lines 2980-3130 are my own code, simply to illustrate how you might code a subroutine to display a string stored in this fashion. Lines 3640-3690 show some strings built by the assembler. In my DEMO code, starting at line 3470, I called on DISPLAY.STRING to print these strings.
Lines 2080-2970 show my renditions of four subroutines I found inside AppleWorks, which I have named MOVE.STRING, COMPARE.STRING, APPEND.STRINGS, and FILTER.LC.TO.UC. These are not identical to the AppleWorks code, as I found some easy improvements here and there. I showed in comment lines where you can find these subroutines inside the AppleWorks 1.3 image. My DEMO program, lines 3470 to the end, shows some of these in action.
Not all of the subroutines which I found have to do with strings. Lines 3140-3410 show MOVE.BLOCK, which does the equivalent of a monitor "M" command. The three parameters are the destination starting address, the source starting address, and the number of bytes to be moved. (I say "moved", perhaps I should really say "copied".) A similar subroutine which starts immediately after this one, at $1BAC, moves a block of memory "up", by starting at the last byte of the source block and moving backwards. This gives the ability to move a block of memory up to an overlapping area, without clobbering the data.
At the beginning of the code at $1000 there is a JMP table (a long series of JMP xxxx instructions, one right after the other) which is evidently used when other segments want to use some of these general subroutines. Each of the other segments also begins with a JMP table. This is a good scheme for joining together pieces of a large system, and is easy to do. I find it a lot handier than the use of a Link Editor approach, as is used under ProDOS-16. On the other hand, subroutines such as these I have shown in this article are the very type you might want to keep in assembled form as relocatable, linkable, object files, on a library ready to be used by all your future code. Even better, they are the type of subroutines I wish were in the //gs tool boxes, in ROM. And there, they would best be called via a JMP table, for efficiency. These routines are too short to afford the tremendous overhead of "real" toolbox calls.
The handiest way to use subroutines like these would involve writing macros for the calls. For example, here are some macro definitions for MOVE.STRING, APPEND.STRINGS, and MOVE.BLOCK:
.MA MOVE.STRING JSR MOVE.STRING .DA ]1,]2 .EM .MA APPEND.STRINGS JSR APPEND.STRINGS .DA ]1,]2 .EM .MA MOVE.BLOCK JSR MOVE.BLOCK .DA ]1,]2,]3 .EM
These are simple enough, but they can make coding a program easier. If you are error prone, or simply enjoy being cautious, you might add code to the definitions to check for the correct number of macro parameters. For example, MOVE.BLOCK requires three parameters:
.MA MOVE.BLOCK .DO ]#=3 JSR MOVE.BLOCK .DA ]1,]2,]3 .ELSE ==> ERROR: WRONG NUMBER OF PARAMETERS. You have ]#, I need 3. .FIN .EM
The line starting "==>" will not be assembled as long as you have 3 parameters. If you have some other number, that line will cause an assembly error, since it starts with an illegal character. This will make it display during assembly, so you can catch and correct your call.
For example, if I try to assemble the following line:
1140 >MOVE.BLOCK BUFFER,SIZE
you will get the message: >1140 ==> ERROR: WRONG NUMBER OF PARAMETERS. You have 2, I need 3.
Did you know you could do that? I wasn't sure, but I tried it and it works.
1000 *SAVE S.AW.SUBS 1010 *-------------------------------- 1020 PNTR .EQ $98,99 1030 P0 .EQ $9A 1040 P1 .EQ $9B 1050 P2 .EQ $9C 1060 P3 .EQ $9D 1070 P4 .EQ $9E 1080 P5 .EQ $9F 1090 COUNT .EQ $A0 1100 *-------------------------------- 1110 MON.CROUT .EQ $FD8E 1120 MON.COUT .EQ $FDED 1130 *-------------------------------- 1140 * GET PARMS 1150 * (A) = # bytes of parameter info 1160 * Copy the bytes to $9A, 9B, ... etc. 1170 * Update Return Address 1180 * Poll Keyboard for Type-Ahead 1190 * Set Y=0, clobbers A and X 1200 * 1210 * For Example: 1220 * JSR subroutine 1230 * .DA parm1,parm2 1240 * <return here> 1250 * 1260 * subroutine JSR GET.4.PARMS 1270 * ... 1280 * RTS 1290 * 1300 *-------------------------------- 1310 * The following code is as it exists in AppleWorks 1.3 1320 .PH $18AD 1330 *-------------------------------- 1340 AW.GET.PARM.TEMP .BS 1 1350 *-------------------------------- 1360 AW.GET.4.PARMS LDA #4 1370 BNE AW.GET.A.PARMS 1380 AW.GET.2.PARMS LDA #2 1390 AW.GET.A.PARMS TAY 1400 STA AW.GET.PARM.TEMP 1410 TSX 1420 LDA $0103,X 1430 STA PNTR 1440 CLC 1450 ADC AW.GET.PARM.TEMP 1460 STA $0103,X 1470 LDA $0104,X 1480 STA PNTR+1 1490 ADC #0 1500 STA $0104,X 1510 .1 LDA (PNTR),Y 1520 STA PNTR+1,Y 1530 DEY 1540 BNE .1 1550 JSR POLL.KEYBOARD 1560 LDY #0 1570 RTS 1580 .EP 1590 *-------------------------------- 1600 * POLL KEYBOARD 1610 *-------------------------------- 1620 .PH $1FA7 1630 POLL.KEYBOARD 1640 LDA $C000 ANY KEY PRESSED? 1650 BPL .3 ...NO, RETURN NOW 1660 STA $C010 ...YES, CLEAR STROBE 1670 LDX $C061 OPEN APPLE PRESSED? 1680 BMI .1 ...YES 1690 LDX $C062 SOLID APPLE PRESSED? 1700 BMI .1 ...YES 1710 AND #$7F ...NO APPLES, SO CLEAR BIT 7 1720 .1 LDX $1184 <<KEY.BUFFER.INDEX>> 1730 STA $117A,X <<KEY.BUFFER>> 1740 INX 1750 CPX #10 AT END OF BUFFER YET? 1760 BCC .2 ...NO END YET 1770 LDX #0 ...END, SO WRAP AROUND 1780 .2 CPX $1185 <<KEY.BUFFER.OUTDEX>> 1790 BEQ .3 BUFFER IS FULL 1800 STX $1184 <<KEY.BUFFER.INDEX>> 1810 .3 RTS 1820 .EP 1830 *-------------------------------- 1840 * My More Efficient Version 1850 *-------------------------------- 1860 GET.4.PARMS LDA #4 1870 .HS 2C SKIP NEXT 2 BYTES 1880 GET.2.PARMS LDA #2 1890 GET.A.PARMS TAY 1900 PHA 1910 TSX 1920 LDA $0104,X GET RETURN ADDR-LO 1930 STA PNTR KEEP FOR VECTOR 1940 CLC 1950 ADC $0101,X ADD # BYTES 1960 STA $0104,X UPDATE RETURN ADDR-LO 1970 LDA $0105,X GET RETURN ADDR-HI 1980 STA PNTR+1 SAVE FOR VECTOR 1990 ADC #0 2000 STA $0105,X UPDATE RETURN ADDR-HI 2010 .1 LDA (PNTR),Y USING VECTOR, COPY PARMS 2020 STA PNTR+1,Y ...TO 9A,9B,etc. 2030 DEY 2040 BNE .1 2050 PLA GET LENGTH OFF STACK 2060 LDY #0 2070 RTS 2080 *-------------------------------- 2090 * MOVE STRING 2100 * JSR MOVE.STRING 2110 * .DA destination,source 2120 * (at $1EF8 in AppleWorks 1.3) 2130 *-------------------------------- 2140 MOVE.STRING 2150 JSR GET.4.PARMS 2160 LDA (P2),Y COPY THE LENGTH BYTE 2170 STA (P0),Y 2180 BEQ .2 STRING IS EMPTY 2190 TAY LENGTH TO Y 2200 .1 LDA (P2),Y 2210 STA (P0),Y 2220 DEY 2230 BNE .1 2240 .2 RTS 2250 *-------------------------------- 2260 * COMPARE TWO STRINGS 2270 * JSR COMPARE.STRINGS 2280 * .DA str1,str2 2290 * return Carry Clear if str1 < str2; else Carry Set 2300 * (at $1ED9 in AppleWorks 1.3) 2310 *-------------------------------- 2320 COMPARE.STRINGS 2330 JSR GET.4.PARMS 2340 LDA (P0),Y GET LENGTH OF SHORTER STRING 2350 CMP (P2),Y 2360 BCC .1 2370 LDA (P2),Y 2380 .1 TAX LENGTH OF SHORTEST TO X-REG 2390 BEQ .3 SHORTEST IS NULL 2400 .2 INY COMPARE BODY OF STRINGS 2410 LDA (P0),Y 2420 CMP (P2),Y 2430 BNE .4 NOT SAME, SO CARRY GIVES RELATION 2440 DEX 2450 BNE .2 MORE TO COMPARE 2460 .3 LDY #0 STRINGS MATCH TO END OF SHORTEST 2470 LDA (P0),Y COMPARE ON BASIS OF LENGTH 2480 LDA (P2),Y 2490 .4 RTS 2500 *-------------------------------- 2510 * APPEND TWO STRINGS 2520 * JSR APPEND.STRINGS 2530 * .DA stringA,stringB 2540 * (at $1341 in AppleWorks 1.3) 2550 *-------------------------------- 2560 APPEND.STRINGS 2570 JSR GET.4.PARMS 2580 LDA (P0),Y GET LENGTH OF STRING A 2590 PHA SAVE IT 2600 CLC 2610 ADC (P2),Y ADD LENGTH OF STRING B 2620 STA (P0),Y MAKES LENGTH OF COMBINED STRING 2630 PLA GET LENGTH OF STRING A AGAIN 2640 CLC 2650 ADC P0 BUMP POINTER TO END OF STRING A 2660 STA P0 2670 BCC .1 2680 INC P1 2690 .1 LDA (P2),Y LENGTH OF STRING B 2700 BEQ .3 ...NULL, SO FINISHED 2710 TAY 2720 .2 LDA (P2),Y 2730 STA (P0),Y 2740 DEY 2750 BNE .2 2760 .3 RTS 2770 *-------------------------------- 2780 * FILTER LOWER CASE to UPPER CASE in a STRING 2790 * JSR FILTER.LC.TO.UC 2800 * .DA string 2810 * (at $1BF in AppleWorks 1.3) 2820 *-------------------------------- 2830 FILTER.LC.TO.UC 2840 JSR GET.2.PARMS 2850 LDA (P0),Y GET LENGTH OF STRING 2860 BEQ .3 NULL STRING 2870 TAY LENGTH TO Y-REG 2880 .1 LDA (P0),Y 2890 CMP #'a' 2900 BCC .2 2910 CMP #'z'+1 2920 BCS .2 2930 AND #$DF TURN OFF LOWER/CASE BIT 2940 STA (P0),Y 2950 .2 DEY 2960 BNE .1 ...MORE BYTES 2970 .3 RTS 2980 *-------------------------------- 2990 * DISPLAY STRING 3000 * JSR DISPLAY.STRING 3010 * .DA string.address 3020 *-------------------------------- 3030 DISPLAY.STRING 3040 JSR GET.2.PARMS 3050 LDA (P0),Y GET LENGTH 3060 BEQ .2 ...NULL STRING 3070 STA COUNT 3080 .1 INY 3090 LDA (P0),Y 3100 JSR MON.COUT 3110 CPY COUNT 3120 BCC .1 3130 .2 RTS 3140 *-------------------------------- 3150 * MOVE MEMORY BLOCK 3160 * JSR MOVE.BLOCK 3170 * .DA destination,source,num.bytes 3180 * (at $1B84 in AppleWorks 1.3) 3190 *-------------------------------- 3200 MOVE.BLOCK 3210 LDA #6 GET 6 PARM BYTES 3220 JSR GET.A.PARMS 3230 LDA P5 GET NUMBYTES-HI (# FULL PAGES) 3240 BEQ .2 ...NO FULL PAGES TO MOVE 3250 *---Move Full Pages-------------- 3260 .1 LDA (P2),Y 3270 STA (P0),Y 3280 INY 3290 BNE .1 ...UNTIL FULL PAGE MOVED 3300 INC P1 SOURCE-HI 3310 INC P3 DESTINATION-HI 3320 DEC P5 # FULL PAGES LEFT 3330 BNE .1 ...STILL MORE 3340 *---Move Partial Page------------ 3350 .2 CPY P4 FINISHED PARTIAL PAGE? 3360 BEQ .3 ...YES 3370 LDA (P2),Y 3380 STA (P0),Y 3390 INY 3400 BNE .2 ...ALWAYS 3410 .3 RTS 3420 *-------------------------------- 3430 * 3440 * DEMONSTRATION OF SOME STRING SUBROUTINES 3450 * 3460 *-------------------------------- 3470 DEMO JSR DISPLAY.STRING 3480 .DA STR.A 3490 JSR MON.CROUT 3500 JSR DISPLAY.STRING 3510 .DA STR.B 3520 JSR MON.CROUT 3530 JSR MOVE.STRING 3540 .DA STR.C,STR.B 3550 JSR DISPLAY.STRING 3560 .DA STR.C 3570 JSR MON.CROUT 3580 JSR APPEND.STRINGS 3590 .DA STR.C,STR.A 3600 JSR DISPLAY.STRING 3610 .DA STR.C 3620 RTS 3630 *-------------------------------- 3640 STR.A .DA #SZ.A 3650 .AS -/THIS IS STRING A./ 3660 SZ.A .EQ *-STR.A-1 3670 STR.B .DA #SZ.B 3680 .AS -/THIS IS STRING B./ 3690 SZ.B .EQ *-STR.B-1 3700 STR.C .BS 80 3710 *-------------------------------- |
Enclosed is SCRNDUMP.PLUS, an enhancement to Steve Knouse's Generic Screen Dump in Apple Assembly Line, September 1983. The main enhancements are 40/80/Lores capability and, via conditional assembly, either a DOS 3.3 or a ProDOS version. The Lores capability is modified from a routine by R.M. Mottola in Nibble/#3/p.18.
The idea is to squeeze in as many features as possible and still have a utility that will fit in good old page 3 space ($300.3CF). However, there are times you have something else in page 3 and need your screendump utility elsewhere. This is easy to do just by reassembling the screendump at another address. However, I'm lazy, and when I need a screendump utility installed I don't want to have to hunt for my assembler disk. So this version is self-relocating in that you can BLOAD and CALL to install it at any address where you have 284 bytes ($11C) free.
This is too big to fit in page 3, so I borrowed Bill Morgan's idea from AAL/Nov.82, to use the upper part of page 2. The input trap and dump portion of SCRNDUMP.PLUS fits in page 3, while the installation takes the top of page 2. Since installation is a one-time affair, it's disposable, although it will remain there if you don't make too heavy use of the input buffer.
In addition to Steve Knouse and R.M. Mottola, I also learned and borrowed from Bill Parker's EPSON TEXT SCREEN DUMP, 1982 and Gary Little's ProDOS DUMP# utility in A+/Jul.85/p.69. I give credit to Roger Wagner's "Assembly Lines: The Book" for relocation ideas. There are a lot of neat techniques in this short utility, most of course 'borrowed' from other sources.
Now for some comments on why I chose BLOAD & CALL logic. You can install the dump via BRUN in immediate mode in DOS 3.3. However, readers of AAL know (AAL/JUN.86, AUG.86, SEP.86) that there are problems in DOS 3.3 with BRUN from within an Applesoft program. So I chose BLOAD & CALL to avoid the extra code you'd need to solve the problem. The BLOAD and CALL 41876 approach of AAL/AUG.86 is a good alternative to BRUN.
However, 3.3 DOSologists need not be ashamed, for ProDOS has its quirks also! See Call-Apple/Apr.84/p.39/Cecil Fretwell for details, but in ProDOS you must choose between a BLOAD & CALL or BRUN approach even for immediate mode! Replace Lines 2120 & 2130 by
2120 STX KSWL 2130 STY KSWH 2134 RTS 2138 .BS 2 (filler)
for BRUN logic. Again, I chose BLOAD & CALL logic for both DOS 3.3 and ProDOS to be consistent. Also, you can CALL 692 (or 2B4G from monitor) to rehook if you haven't made too heavy use of the input buffer.
Now to describe the utility in more detail. Lines 1540-1630 find where it has been BLOADed. I think it's a good idea to inhibit interrupts while getting information from the stack. Then lines 1640-1720 modify (SETRAP+1) and (SETRAP+3) to point at TRAP. Lines 1730-1900 put the current INPUT address at TRAP+1.TRAP+2 and TRAP+8.TRAP+10: the first so we can 'daisy-chain' our input TRAP at the end of the current INPUT routine, and the second so we can terminate via CTRL-T to remove our INPUT trap. Lines 1910-1980 set default parameters at locations 6-9 and $ED. If you BLOAD without CALL-ing, you must set these parameters yourself. As far as I can tell, there are 11 page 0 locations not used by Applesoft, Integer Basic, DOS 3.3, ProDOS (including interrupt handlers), or the System Monitor. They are 6-9, $1E, $EB-EF, and $F9.
Anyway, 6 (PRFLAG) is used as an enable/disable (1/0) flag- for when you want to disable dumping without removing the input trap. 7 (WINTOP) and 8 (WINBOT) are used to set a 'window' to dump if you don't want to dump the whole screen. Here the top line is 0 and bottom is 23 ($17). Note that I couldn't fit left and right margin windows in. If you want a routine that does both 40 and 80 column dumps you'll have to be careful in adding left and right margins. $ED (FLAG) is used to flag if a dump has been done via CTRL-P from the TRAP (value $FF) or just a CALL 802 (JSR $322) (value 0).
SETRAP, Lines 2050-2150, hooks TRAP into DOS or ProDOS.
TRAP, our input 'filter', does its thing by first daisy-chaining to the current input routine and letting it do all the dirty work. Only then does TRAP check for either CTRL-T (Terminate TRAP and reset input hooks) or CTRL-P (Print the screen). If neither, exit to caller of input routine. If a CTRL-P, Set FLAG ($ED) to $FF to show we came thru TRAP. This avoids a JSR DUMP, which would not be relocatable. You could have a JSR DUMP with address filled in during the first portion of the utility just like SETRAP+1, +3 are filled in from Lines 1640-1720. That way you wouldn't need FLAG at all, but the approach takes 3 more bytes. Roger Wagner's book has other ways to do a relocatable JSR, but remember that we want a DUMP that can be called either directly or from TRAP.
Note that lines 2280 and 2290 inside TRAP avoid another ProDOS 'gotcha'. A JMP $3D0 (DOS.WARM) in ProDOS (Q from IIgs Monitor) returns to immediate mode but erases Applesoft variables, unlike the friendly DOS 3.3! Thanks to Steve and Marsha Meuse for this one (Nibble/Nov.85/p.20). So from ProDOS JSR RSTINT and JSR BASIC2 exit without erasing variables. BASIC2 ($E003) is the address a CTRL-C <Return> from monitor uses. RSTINT ($9A17) is the same in BASIC.SYSTEM versions 1.0 and 1.1, the only ones so far. Future versions may change, so beware.
For the DUMP itself, we disable interrupts and save the A-X-Y registers, the cursor location CH (so after the dump is done you end up with the cursor where it was when you started), and the output hooks, in lines 2370-2490.
Lines 2510 and 2520 test the enable flag and exit if it's zero. Lines 2530-2650 simulate a PR#SLOT, and kill video echo. Change SLOT in Line 1060 if printer isn't in slot 1, and change NOAIO to 0 in Line 1080 if you have an AIO interface, which is code from Steve Knouse.
Lines 2670-2720 start the dump with screenline specified by WINTOP. In Lines 2730-2820 we handle 80-column dumps. This is good only for //e or //c or IIgs using firmware 80-column routines. There are too many video interface cards that work in different ways to support them all. Some cards have their own dump utilities, so you may not be out of luck if you have a non-standard interface. In line 2810 I use $FF to flag the X-register if 80-columns are active. Otherwise, lines 2840-2870 in MORCHR will end with X in range 0-23. That way in Lines 3090 and 3100 if X gets incremented to 0 then branch back to MORCHR to get even column character, so 2*40=80 characters/line get dumped. This trick of using a register value out of the range you would normally get in the rest of your code is a handy way of saving bytes.
In Line 2840 PRFN gets tested. 0->LORES, and 1->TEXT. If LORES, still only do a mixed mode dump of 20 LORES lines and 4 TEXT lines. Replace Line 2880 by 2 NOP's ($EA) to do full-screen LORES. Note that if you're in 80-column mode, the odd columns are dumped as text regardless of PRFN, since 2820 skips MORCHR. So this dump doesn't do double-LORES. Also, your LORES-creating program should start with TEXT:HOME:GR since TEXT:HOME clears even 80-columns to spaces, whereas GR only clears 40 columns to nulls. If you just do GR, some text may be left in the aux memory text pages, and you'll see it when you do a LORES dump.
So a LORES dump in 80-column mode still dumps only 40 column LORES, spaced out (if you did TEXT:HOME:GR) to match the 4 lines of 80-column text at the bottom.
In Lines 2890-3010 a quasi-40-by-40 resolution for LORES is done by using a space for both LORES blocks off, a caret for top LORES block on, an underline for bottom LORES block on, and an X for both LORES blocks on. This should work for most printers, but feel free to use other characters or symbols, especially if your printer has graphics. The resulting dump is good for monochrome LORES pictures, and especially Bar Charts. Multi-colored LORES and even abstract monochrome patterns lose a lot in the 'translation' to print.
Note especially how 2910 turns off the lo-nibble, while the exclusive-or in 2930 turns off the hi-nibble and turns back on the lo-nibble. Exclusive-or is a handy opcode to learn, and to save bytes by using. Also, the BIT opcode is used in Lines 2960, 2980, and 3000 as a 2-byte NOP to skip the following 2 bytes. BIT changes the N,V, and Z flags, but since 3020 does another CMP anyway, this dosn't matter here. Another 'gotcha' is to avoid inadvertently toggling a soft-switch in the $C000-C0FF range via BIT. I did that once and it took me a long time to figure out!
Lines 3020-3050 make sure a value of $A0 or greater is sent to printer, to avoid control or inverse or flashing characters. Line 3060 then masks off the MSB to avoid graphics in EPSON printers. Lines 3080-3190 send the character to the printer and do loop checking for 80-columns, line length, and WINBOT screen checking.
Lines 3210-3410 restore everything saved upon entry to DUMP. If FLAG was set to show we came thru TRAP, it's cleared in case next time we don't. Besides, if we came thru TRAP, we're in input mode and need to JMP RDKEY to get the next keystroke. If we didn't come thru TRAP, we can just exit via RTS.
Some Demonstrations
So much for the dump. Now it's time for some demo programs. The following, which I call CHARDEMO, shows the active character set, including MouseText if you're in 80-column mode. //e's, c's, and IIgs's have different character sets for 40 and 80 column modes, and you'll see that most printers match the 40-column character set best for INVERSE and FLASHing characters. So what you dump isn't always what you see! If you avoid MouseText and INVERSE and FLASH, you'll do all right except that most printers use code $7F=127 as 'delete previous char' instead of a checkerboard. So if you don't see a tilde (code $7E=126) or a checkerboard, you'll know what happened.
5 REM CHARDEMO 6 PRINT CHR$ (4)"BLOAD SCRNDUMP.PLUS" 7 CALL 692: REM INSTALL HOOKS 10 INVERSE 20 PRINT CHR$ (27);"@ABCDEFGHIJKLMNOPQRS TUVWXYZ[\]^_"; CHR$ (24) 30 NORMAL 40 GOSUB 1000 50 INVERSE : GOSUB 1000: NORMAL 60 FLASH : GOSUB 1000: NORMAL 70 END 1000 FOR I = 32 TO 127: PRINT CHR$ (I);: NEXT 1010 PRINT : RETURN
TESTGR does a LORES dump by CALLing DUMP directly. Try in 40 and 80-column mode to see the difference.
5 TEXT : HOME : GR 10 COLOR= 15 20 FOR I = 0 TO 39 30 VLIN I,39 AT I: NEXT 40 PRINT "THIS IS A TEST." 50 POKE 9,0: CALL 802: POKE 9,1 60 REM ASSUNES M/L BLOADED & PARAMETERS SET
TEST40 and TEST80 merely show that no top lines or characters get dropped.
5 TEXT : HOME 10 FOR I = 1 TO 22 20 PRINT "LINE NUMBER ";I;: HTAB 16 25 PRINT "THIS IS A TEST." 30 NEXT 5 TEXT : HOME 10 FOR I = 1 TO 22 20 PRINT "LINE NUMBER ";I;: HTAB 16 25 PRINT "THIS IS A TEST."; 26 FOR J = 1 TO 40: PRINT "A";: NEXT : PRINT 30 NEXT
LORES.PIC, adapted from a David Thornburg program to do LORES and not HIRES patterns, allows you to experiment with patterns. Try Inputs x=0,y=0,s=field size=10, and 2 colors to see an abstract monochrome pattern-it loses a lot in the translation to print.
5 REM LORES.PIC/DAVID THORNBURG/A+/DEC.86&JUN.87 6 REM ASSUMES M/L BLOADED & PARAMETERS SET 7 POKE 9,0: REM FOR LORES DUMP 10 PRINT "ENTER STARTING X-VALUE "; 20 INPUT A 30 PRINT "ENTER STARTING Y-VALUE "; 40 INPUT B 50 PRINT "ENTER FIELD SIZE "; 60 INPUT S 70 PRINT "ENTER NUMBER OF COLORS (2-16) "; 80 INPUT N 90 TEXT : HOME : GR 100 FOR I = 0 TO 39 110 FOR J = 0 TO 39 120 X = A + (S * (I - 20) / 100) 130 Y = B + (S * (J - 20) / 100) 140 Z = 10 * SIN (X * X) + 10 * SIN (Y * Y) 160 COLOR= (Z - (N * INT (Z / N))) 170 PLOT I,J 180 NEXT J 190 NEXT I 200 CALL 802: POKE 9,1: REM DUMP, THEN BACK TO TEXT DEFAULT
Try changing Line 140 to Z= X*X + Y*Y and use inputs 0,0,25,2 to see circles. If the pattern isn't too abstract, the resulting dump is ok. Also try 140 Z = X*Y and inputs 0,0,20,2 to see hyperbolas. It's a neat program to play with, but unfortunately abstract and/or multi-colored patterns go past the capabilities of the simplistic LORES dump here.
1000 *SAVE S.SCRNDUMP.PLUS 1010 *-------------------------------- 1020 * See AAL/SEP.83/P.22-24/Steve Knouse 1030 * and NIBBLE/#3/P.19&40/R.M. Mottola 1040 * ...modified 5/10/87 Louis Pitz 1050 *-------------------------------- 1060 SLOT .EQ 1 PRINTER SLOT# 1070 DOS .EQ 1 DOS3.3 ACTIVE 1080 NOAIO .EQ 1 GENERIC INTERFACE 1090 .LIST CON SHOW ALL CONDITIONAL LINES 1100 *-------------------------------- 1110 PRFLAG .EQ $6 0->DISABLE, 1->ENABLE 1120 WINTOP .EQ $7 TOP OF WINDOW TO PRINT 1130 WINBOT .EQ $8 BOTTOM 1140 PRFN .EQ $9 0->LORES, 1->TEXT 1150 PTR .EQ $EB USE FOR RELOCATING 1160 CH .EQ $24 1170 BASL .EQ $28 1180 CSWL .EQ $36 1190 CSWH .EQ $37 1200 KSWL .EQ $38 1210 KSWH .EQ $39 1220 FLAG .EQ $ED 1230 LINCTR .EQ $EE 1240 *-------------------------------- 1250 STACK .EQ $100 USE FOR RELOCATING 1260 *-------------------------------- 1270 DOS.WARM .EQ $3D0 1280 DOS.HOOK .EQ $3EA 1290 DOSKSW .EQ $AA55 DOS ACTIVE->TRUE KSW 1300 VECTIN .EQ $BE32 PRODOS ACTIVE->TRUE KSW 1310 RSTINT .EQ $9A17 1320 BASIC2 .EQ $E003 1330 *-------------------------------- 1340 NOVID .EQ $578 1350 COL80 .EQ $C01F 80-COLUMN ON IF MSB=1 1360 REGRAM .EQ $C054 SELECT MAIN RAM TEXT PAGE 1370 AUXRAM .EQ $C055 ...AUX RAM 1380 IDBYTE .EQ $FBB3 6->//e OR c OR gs 1390 BASCAL .EQ $FBC1 1400 VTAB .EQ $FC22 1410 RDKEY .EQ $FD0C 1420 KEYIN .EQ $FD1B 1430 CROUT .EQ $FD8E 1440 COUT .EQ $FDED 1450 OUTPRT .EQ $FE95 1460 RTRN .EQ $FF58 1470 *-------------------------------- 1480 * First find out 'WHERE AM I?' 1490 * See "Assembly Lines: The Book", chapter 14 1500 * by Roger Wagner 1510 *-------------------------------- 1520 .OR $2B4 FIT $2B4.3CF A$2B4,L$11C [A692,L284] 1530 .TF SCRNDUMP.PLUS 1540 DISINT PHP SAVE INTERRUPT STATUS 1550 SEI DISABLE INTERRUPTS 1560 START JSR RTRN PUT START+2 ON STACK 1570 TSX NOW GET STACK POINTER 1580 LDA STACK,X GET (START+2)-HI BYTE 1590 STA PTR+1 1600 DEX 1610 LDA STACK,X GET (START+2)-LO BYTE 1620 STA PTR 1630 PLP RESTORE INTERRUPT STATUS 1640 LDY #SETRAP-START-1 OFFSET (SETRAP+1)-(START+2) 1650 CLC 1660 ADC #TRAP-START-2 OFFSET (TRAP)-(START+2) 1670 STA (PTR),Y PUT IN SETRAP+1 1680 LDA PTR+1 1690 ADC #0 IN CASE CROSS PAGE BOUNDARY 1700 INY 1710 INY SO Y=OFFSET (SETRAP+3)-(START+2) 1720 STA (PTR),Y PUT IN SETRAP+3 1730 .DO DOS IF DOS ACTIVE 1740 LDA DOSKSW SAVE INPUT HOOKS INSIDE TRAP 1750 .ELSE IF PRODOS 1760 LDA VECTIN 1770 .FIN 1780 LDY #TRAP+6-START OFFSET (TRAP+8)-(START+2) 1790 STA (PTR),Y 1800 LDY #TRAP-START-1 OFFSET (TRAP+1)-(START+2) 1810 STA (PTR),Y 1820 INY NOW OFFSET (TRAP+2)-(START+2) 1830 .DO DOS IF DOS ACTIVE 1840 LDA DOSKSW+1 1850 .ELSE IF PRODOS 1860 LDA VECTIN+1 1870 .FIN 1880 STA (PTR),Y 1890 LDY #TRAP+8-START OFFSET (TRAP+10)-(START+2) 1900 STA (PTR),Y 1910 LDX #0 SET DEFAULT VALUES 1920 STX WINTOP TOP OF SCREEN=LINE 0 1930 STX FLAG CLEAR FLAG AT START 1940 INX 1950 STX PRFLAG ENABLE ROUTINE 1960 STX PRFN SET FOR TEXT 1970 LDX #23 SET BOTTOM SCREEN 1980 STX WINBOT TO LINE 23 1990 *-------------------------------- 2000 * NOW FOR SETRAP -SET INPUT HOOKS 2010 * TO POINT AT 'TRAP' OR INPUT FILTER 2020 * NOTE HOW FIRST SECTION MODIFIES (SETRAP+1) 2030 * AND (SETRAP+3) TO POINT AT TRAP! 2040 *-------------------------------- 2050 SETRAP LDX #TRAP 2060 LDY /TRAP 2070 .DO DOS IF DOS ACTIVE 2080 STX KSWL 2090 STY KSWH 2100 JMP DOS.HOOK 2110 .ELSE PRODOS 2120 STX VECTIN 2130 STY VECTIN+1 2140 RTS 2150 .FIN 2160 *-------------------------------- 2170 TRAP JSR KEYIN GET KEYPRESS 2180 CMP #$94 CTRL-T FOR TERMINATE? 2190 BNE .1 NO 2200 LDX #KEYIN RESET INPUT HOOKS 2210 LDY /KEYIN WHICH HAVE BEEN SAVED 2220 STX KSWL 2230 STY KSWH 2240 .DO DOS IF DOS ACTIVE 2250 JSR DOS.HOOK PASS TO DOS 2260 JMP DOS.WARM AND EXIT 2270 .ELSE PRODOS 2280 JSR RSTINT PASS TO PRODOS 2290 JMP BASIC2 AND EXIT 2300 .FIN 2310 .1 CMP #$90 CTRL-P FOR PRINT? 2320 BEQ .2 YES->BRANCH 2330 RTS NO-> EXIT 2340 .2 LDA #$FF RESET FLAG TO SHOW 2350 STA FLAG HAVE COME THRU TRAP 2360 *-------------------------------- 2370 DUMP PHP SAVE INTERRUPT STATUS 2380 SEI DISABLE INTERRUPTS 2390 PHA SAVE A,X,Y 2400 TXA 2410 PHA 2420 TYA 2430 PHA 2440 LDA CH SAVE CH 2450 PHA 2460 LDA CSWL SAVE OUTPUT HOOKS 2470 PHA 2480 LDA CSWH 2490 PHA 2500 * 2510 LDA PRFLAG ROUTINE ENABLED? 2520 BEQ RESTOR IF NOT, GET OUT 2530 LDA #SLOT COLD START BOARD 2540 JSR OUTPRT IN SLOT 2550 .DO NOAIO GENERIC INTERFACE 2560 LDA #$89 KILL VIDEO ECHO 2570 JSR COUT VIA 2580 LDA #"N CTRL-I"N" 2590 JSR COUT 2600 .ELSE SSM AIO INTERFACE 2610 LDA #$80 2620 JSR COUT 2630 LDX SLOT 2640 STA NOVID,X 2650 .FIN 2660 * 2670 JSR CROUT START ON A NEW LINE 2680 LDA WINTOP 2690 STA LINCTR COUNT SCREEN LINES 2700 NXTLN JSR BASCAL GET ADDR OF LINE 2710 LDY #0 START W/ 1ST CHAR (0-TH) 2720 STY CH SET CH=0 TO START LEFT EDGE 2730 NXTCHR BIT COL80 80-COL ON? 2740 BPL MORCHR NO->BRANCH 2750 LDA IDBYTE //e OR c OR gs? 2760 CMP #6 2770 BNE MORCHR NO->BRANCH 2780 STA AUXRAM READ ODD COLUMN 2790 LDA (BASL),Y 2800 STA REGRAM READY FOR EVEN COLUMN 2810 LDX #$FF SHOW 80-COL ON 2820 BNE INVCHK SEND ODD COLUMN CHAR 2830 MORCHR LDA (BASL),Y 2840 LDX PRFN LORES OR TEXT? 2850 BNE INVCHK IF TEXT THEN BRANCH 2860 LDX LINCTR SO NOW LORES 2870 CPX #20 BUT DO AT MOST 20 LORES & 4 TEXT 2880 BPL INVCHK SO TEXT IF PAST LINE 20 2890 CMP #0 ZERO GRAPHICS? 2900 BEQ .1 YES, USE SPACE 2910 AND #$F0 HI-NIBBLE=LO-LORES BLOCK 2920 BEQ .2 0->USE ^, HI-LORES BLOCK ON 2930 EOR (BASL),Y LO-NIBBLE=HI-LORES BLOCK 2940 BEQ .3 0->USE _, LO-LORES BLOCK ON 2950 LDA #"X USE X, BOTH BLOCKS ON 2960 .HS 2C (BIT TO SKIP NEXT 2 BYTES) 2970 .1 LDA #$A0 SPACE FOR ZERO 2980 .HS 2C 2990 .2 LDA #"^ CARET FOR HI-LORES BLOCK ONLY 3000 .HS 2C 3010 .3 LDA #"_ UNDERLINE FOR LO-LORES BLOCK ONLY 3020 INVCHK CMP #$A0 INVERSE OR FLASHING? 3030 BCS REGCHR NO, SO REGULAR CHAR 3040 ADC #$40 YES, ALTER BITS 6 & 7 3050 BNE INVCHK AND KEEP CHECKING 3060 REGCHR AND #$7F MASK OFF HI BIT TO AVOID 3070 * EPSON BLOCK GRAPHICS 3080 JSR COUT PRINT IT 3090 INX 80-COL ON-> FROM $FF TO 0 3100 BEQ MORCHR IF SO, GET EVEN COLUMN CHAR 3110 INY 3120 CPY #40 WHOLE LINE DONE? 3130 BCC NXTCHR NO-GET NEXT CHAR 3140 JSR CROUT END OF LINE 3150 INC LINCTR GOTO NEXT LINE # 3160 LDA LINCTR 3170 CMP WINBOT AT BOTTOM SCREEN YET? 3180 BMI NXTLN NO-GOTO NEXT LINE 3190 BEQ NXTLN YES-GET LAST LINE 3200 3210 RESTOR PLA RESTORE OUTPUT HOOKS 3220 STA CSWH 3230 PLA 3240 STA CSWL 3250 PLA RESTORE CH 3260 STA CH 3270 JSR VTAB AND LINE 3280 PLA RESTORE Y,X,A 3290 TAY 3300 PLA 3310 TAX 3320 LDA FLAG DID WE COME THRU TRAP? 3330 BNE CLEAR YES->BRANCH 3340 PLA NO-> RESTORE (A) 3350 PLP RESTORE INTERRUPT STATUS 3360 RTS AND EXIT. 3370 CLEAR LDA #0 CLEAR FLAG 3380 STA FLAG 3390 PLA RESTORE (A) 3400 PLP RESTORE INTERRUPT STATUS 3410 JMP RDKEY GET NEXT KEY-JMPS TO (KSWL) 3420 *-------------------------------- |
If you are still using ProDOS 1.1.1, and you have some sort of clock card such as Thunderclock, TimeMaster, or any other "standard" ProDOS clock, you have a problem. Apple built this bug into ProDOS, and they came out with the new versions (they call it ProDOS-8 version 1.4 now) just in time.
In my article about the clock driver in the November 1983 issue of AAL (pages 25-28), I discussed the problem. It seemed a little more remote at the time. Apple based ProDOS on the Thunderclock, even though that device does not keep track of the year. The ProDOS clock driver reads the Month, Day, and Day of Week information and does some arithmetic to determine which of six years could produce that day of week on the corresponding month and day. ProDOS 1.1.1 and earlier versions could produce dates from 1982 through 1987. When 1988 rolled around a few weeks ago, hundreds of thousands of Applers around the world slipped back in time to 1982.
And it is not funny! Some programs will not let you operate if the dates are not correct!
Well, there are at least four ways around the problem. You can remove your clock card, and type the date in manually wherever it is really needed. Not very nice.
Or, you can get the up-to-date version of ProDOS, now called ProDOS-8 Version 1.4. You can get it, and then you can copy it to every floppy (both 3 1/2 and 5 1/4), to every RamFactor, to every hard disk in sight. This is tedious, but it is the best solution. If you have a friendly dealer, you can get it from the IIgs system disk. But don't copy the file named PRODOS from this disk (that is only a loader now). Instead, copy the file named P8 from the subdirectory SYSTEM. P8 is a longer file than version 1.1.1 of PRODOS was, so if you use BSAVE to put it on your disks be sure to specify the L parameter. Something like this should do the trick:
Boot any ProDOS disk, preferably one with version 1.4 so the correct dates will get into the file directories you are updating. Get into the S-C Macro Assembler or Applesoft. With the latest IIgs system disk in your drive, type:
BLOAD SYSTEM/P8,TSYS,A$2000
Now put the disk you want to update into a drive, and type the following. You may want to include slot and drive parameters, or set the prefix to the appropriate value for a ram disk or hard disk.
UNLOCK PRODOS BSAVE PRODOS,TSYS,A$2000,L$3C7D LOCK PRODOS
A third approach saves you a trip to the dealer. You can simply PATCH the copies of ProDOS version 1.1.1 to give you the correct year. When you BLOAD the file named PRODOS at $2000, the six-year table is at $4F76. If you look there now you will find the following bytes:
4F76: 54 54 53 52 57 56 55
These correspond to the years 1984, 1984, 1983, 1982, 1987, 1986, and 1985. Notice that 1984, being a leap year, takes up two of the values. Patch these seven bytes, using the monitor, as follows:
4F76:5A 59 58 58 57 56 5B
The table now includes the years from 1986 through 1991. If you want 1992 in there also, substitute 5C where I have 57 and 56 above. Both 1988 and 1992 are leap years, so they both take two table positions. When ProDOS 1.4 was released it was still 1987, so there was not room for 1992 in the table.
A fourth possible solution was suggested by reader Garth O'Donnell. You can replace the clock driver inside ProDOS with one that reads the year directly from your clock card! This is what happens when you boot Version 1.4 in a IIgs, because P8 senses that you are in a IIgs and plugs in a different driver. But if you are still using an older Apple, as most of us are, you can modify the PRODOS file to load an intelligent driver for your own clock card. Of course, if you are using a Thunderclock, the driver with the above patches is the best you can do. But if you have a TimeMaster, as Garth does, you can use a program like he wrote.
I decided to try my hand at modifying the standard clock driver so that it uses the year information in the TimeMaster. The following program is derived directly from the standard driver, with as few modifications as possible. It still resides in the ProDOS SYS file at $4F00, but it is a lot shorter. (Maybe you can think of something useful to do with the extra 45 bytes!) It still depends upon the standard ProDOS loader to plug in the actual slot number in lines 1260 and 1310. The major change I made was to call on the ":" instead of the "#" mode. The "#" mode is a ThunderClock mode, which does not return the year. The ":" mode is a TimeMaster mode, which does return the year.
If you have an Applied Engineering Serial Pro card, which includes a TimeMaster compatible clock, you can use the driver I wrote by making the single change as shown in the comments on line 1090. Or, maybe you could use those extra 45 bytes for a subroutine that would check which clock is in the slot and make the appropriate changes at run time.
1000 *SAVE S.CLOCK.1988 1010 *-------------------------------- 1020 * IF THE PRODOS BOOT RECOGNIZES A TIMEMASTER, 1030 * A "JMP $D742" IS INSTALLED AT $BF06 AND 1040 * THE SLOT ADDRESS IS PATCHED INTO THE FOLLOWING 1050 * CODE AT SLOT.A AND SLOT.B BELOW. 1060 *-------------------------------- 1070 * DEFINE CLOCK ENTRY POINT 1080 *-------------------------------- 1090 CLOCK .EQ $C108 <<<USE $C11D FOR AE SERIAL PRO>>> 1100 *-------------------------------- 1110 DATE .EQ $BF90 $BF91 = YYYYYYYM 1120 * $BF90 = MMMDDDDD 1130 TIME .EQ DATE+2 $BF93 = 000HHHHH 1140 * $BF92 = 00MMMMMM 1150 MODE .EQ $5F8-$C0 TIMEMASTER MODE IN SCREEN HOLE 1160 *-------------------------------- 1170 .OR $4F00 1180 .TF B.CLOCK.DRIVER 1190 .PH $D742 1200 *-------------------------------- 1210 PRODOS.TIMEMASTER.DRIVER 1220 LDX SLOT.B $CN 1230 LDA MODE,X SAVE CURRENT TIMEMASTER MODE 1240 PHA 1250 LDA #":" SEND ":" TO TIMEMASTER 1260 JSR CLOCK+3 SELECT TIMEMASTER MODE 1270 SLOT.A .EQ *-1 1280 *-------------------------------- 1290 * READ TIME & DATE INTO $200...$211 IN FORMAT: 1300 *-------------------------------- 1310 JSR CLOCK 1320 SLOT.B .EQ *-1 1330 *-------------------------------- 1340 * CONVERT ASCII VALUES TO BINARY 1350 * $3E -- MINUTE 1360 * $3D -- HOUR 1370 * $3C -- YEAR 1380 * $3B -- DAY OF MONTH 1390 * $3A -- MONTH 1400 *-------------------------------- 1410 CLC 1420 LDX #4 1430 LDY #12 POINT AT MINUTE 1440 .1 LDA $203,Y TEN'S DIGIT 1450 AND #$0F IGNORE TOP BIT 1460 STA $3A MULTIPLY DIGIT BY TEN 1470 ASL *2 1480 ASL *4 1490 ADC $3A *5 1500 ASL *10 1510 ADC $204,Y ADD UNIT'S DIGIT 1520 SEC 1530 SBC #$B0 SUBTRACT ASCII ZERO 1540 STA $3A,X STORE VALUE 1550 DEY BACK UP TO PREVIOUS FIELD 1560 DEY 1570 DEY 1580 DEX BACK UP TO PREVIOUS VALUE 1590 BPL .1 ...UNTIL ALL 5 FIELDS CONVERTED 1600 *-------------------------------- 1610 * PACK MONTH AND DAY OF MONTH, 1620 *-------------------------------- 1630 TAY MONTH (1...12) 1640 LSR 00000ABC--D 1650 ROR D00000AB--C 1660 ROR CD00000A--B 1670 ROR BCD00000--A 1680 ORA $3B MERGE DAY OF MONTH 1690 STA DATE SAVE PACKED DAY AND MONTH 1700 *-------------------------------- 1710 LDA $3C YEAR 1720 ROL MERGE TOP MONTH BIT 1730 STA DATE+1 YYYYYYYM 1740 *-------------------------------- 1750 LDA $3D GET HOUR 1760 STA TIME+1 1770 LDA $3E GET MINUTE 1780 STA TIME 1790 PLA RESTORE TIMEMASTER MODE 1800 LDX SLOT.B GET $CN FOR INDEX 1810 STA MODE,X 1820 RTS 1830 *-------------------------------- 1840 .EP 1850 *-------------------------------- |
BONUS PROGRAM (not printed in the original newsletter)
1000 .list off 1010 *SAVE border.disorder 1020 *-------------------------------- 1030 * This pgm messes with the border color 1040 *-------------------------------- 1050 .op 65816 1060 .or $800 1070 .in /hard2/asm/macros/gs.macros 1080 .in /hard2/asm/toolsets/tool.locator 1090 .in /hard2/asm/toolsets/miscellaneous 1100 .in /hard2/asm/toolsets/Integer.Math 1110 .in /hard2/asm/toolsets/Text 1120 *-------------------------------- 1130 Keyboard .eq $C000 1140 Keyboard.strobe .eq $C010 1150 Screen.Color.Register .eq $C022 Upper=foreground, Lower=background 1160 Border.Color.Register .eq $C034 Lower nibble only (16 colors) 1170 * Upper nibble effects real-time clock 1180 *-------------------------------- 1190 Start jmp Are.You.Ready This table is here 1200 jmp Cycle.Border.Color for the purpose of 1210 jmp Cycle.Background.Color making it easier to 1220 jmp Cycle.Foreground.Color mess around using the monitor 1230 jmp Fiddle.Border.Color.by.HB 1240 jmp Fiddle.Foreground.Color.by.HB 1250 jmp Fiddle.Background.Color.by.HB 1260 jmp Enable.VBL.IRQs 1270 jmp Disable.VBL.IRQs 1280 jmp Task1.on 1290 jmp Task2.on 1300 jmp Task1.off 1310 jmp Task2.off 1320 jmp Kill.All.Tasks 1330 jmp Whats.Going.On 1340 *-------------------------------- 1350 Cycle.Border.Color 1360 >emulate 1370 clc 1380 .0 lda Keyboard has a key been pressed? 1390 bmi .9 ... YES, so quit 1400 lda #$40 %0100.0000 set to read clock chip w/ border=0 1410 .1 sta Border.Color.Register 1420 adc #1 increment border color 1430 cmp #$50 have we gone through all 16 colors? 1440 bne .1 ...NO, so get the next color 1450 beq .0 ...YES, so keep looping, ain't it pruddy 1460 * 1470 * Watch for wider band at bottom of display area 1480 * which indicates that VBL interrupts are enabled 1490 * 1500 .9 sta Keyboard.Strobe 1510 rts 1520 *-------------------------------- 1530 Cycle.Background.Color 1540 >emulate 1550 clc 1560 .0 lda Keyboard has a key been pressed? 1570 bmi .9 ... YES, so quit 1580 lda Screen.Color.Register 1590 and #$F0 1600 pha 1610 .1 pla 1620 sta Screen.Color.Register 1630 clc 1640 adc #1 increment background color 1650 pha 1660 and #$0F 1670 cmp #$0F have we gone through all 16 colors? 1680 bne .1 ...NO, so get the next color 1690 pla 1700 bra .0 ...YES, so keep looping, ain't it pruddy 1710 * 1720 .9 sta Keyboard.Strobe 1730 rts 1740 *-------------------------------- 1750 Cycle.Foreground.Color 1760 >emulate 1770 clc 1780 .0 lda Keyboard has a key been pressed? 1790 bmi .9 ... YES, so quit 1800 lda Screen.Color.Register 1810 and #$0F 1820 pha 1830 .1 pla 1840 sta Screen.Color.Register 1850 clc 1860 adc #$10 increment Foreground color 1870 pha 1880 and #$F0 1890 cmp #$F0 have we gone through all 16 colors? 1900 bne .1 ...NO, so get the next color 1910 pla 1920 bra .0 ...YES, so keep looping, ain't it pruddy 1930 * 1940 .9 sta Keyboard.Strobe 1950 rts 1960 *-------------------------------- 1970 Foreground.Color.Task.Header 1980 >HB.task.header HC,1 1990 FFCT 2000 php HB tasks are invoked in native mode with 2010 pha 8 bit registers 2020 lda Screen.Color.Register 2030 clc 2040 adc #$10 2050 sta Screen.Color.Register 2060 lda #$03 Change Foreground color every 3 ticks 2070 sta HC 2080 pla 2090 plp 2100 rtl Must always RTL from an HB task 2110 *-------------------------------- 2120 Background.Color.Task.Header 2130 >HB.task.header BC,1 2140 BFCT 2150 php HB tasks are invoked in native mode with 2160 pha 8 bit registers 2170 lda Screen.Color.Register 2180 and #$0F 2190 cmp #$0F 2200 beq .1 2210 lda Screen.Color.Register 2220 clc 2230 adc #$01 2240 bra .2 2250 .1 lda Screen.Color.Register 2260 and #$F0 2270 .2 sta Screen.Color.Register 2280 lda #$20 Change Foreground color every 32 ticks 2290 sta BC 2300 pla 2310 plp 2320 rtl Must always RTL from an HB task 2330 *-------------------------------- 2340 Border.Disorder.Task.Header 2350 >hb.task.header TC,1 2360 Border.Disorder 2370 php This is not necessary, but lets be safe this year 2380 pha 2390 clc 2400 lda Border.Color.Register 2410 adc #1 Increment clock/border color register 2420 cmp #$50 don't exceed last color available 2430 bne .2 2440 lda #$40 Reset to first color 2450 .2 sta Border.Color.Register 2460 lda #$3C Setup task count to invoke task about once a second 2470 sta TC 2480 pla 2490 plp 2500 rtl 2510 *-------------------------------- 2520 Fiddle.Border.Color.by.HB 2530 >native 2540 >setmode16 2550 * 2560 >PushConstL Border.Disorder.Task.Header 2570 >TK SetHeartBeat 2580 >jsr.if.error Print.Error 2590 * 2600 >setmode16 2610 >emulate 2620 rts 2630 *-------------------------------- 2640 Fiddle.Foreground.Color.by.HB 2650 >native 2660 >setmode16 2670 * 2680 >PushConstL Foreground.Color.Task.Header 2690 >TK SetHeartBeat 2700 >jsr.if.error Print.Error 2710 * 2720 >setmode16 2730 >emulate 2740 rts 2750 *-------------------------------- 2760 Fiddle.Background.Color.by.HB 2770 >native 2780 >setmode16 2790 * 2800 >PushConstL Background.Color.Task.Header 2810 >TK SetHeartBeat 2820 >jsr.if.error Print.Error 2830 * 2840 >setmode16 2850 >emulate 2860 rts 2870 *-------------------------------- 2880 Enable.vbl.irqs 2890 >native 2900 >setmode16 2910 pea $0002 enable Vertical Blanking interrupts 2920 >TK IntSource 2930 >jsr.if.error Print.Error 2940 >emulate 2950 rts 2960 *-------------------------------- 2970 Disable.vbl.irqs 2980 >native 2990 >setmode16 3000 pea $0003 disable Vertical Blanking interrupts 3010 >TK IntSource 3020 >jsr.if.error Print.Error 3030 >emulate 3040 rts 3050 *-------------------------------- 3060 Whats.going.on 3070 >native 3080 >setmode16 3090 pea 0 save room for results 3100 >TK GetIRQenable 3110 >jsr.if.error Print.Error 3120 pla 3130 sta IRQ.status 3140 ldx ##$0007 3150 XBA Ignore upper eight bits (they're unused) 3160 .1 rol 3170 bcc .2 3180 jsr Print.an.IRQ.Name 3190 .2 dex 3200 cpx ##$FFFF 3210 bne .1 3220 >emulate 3230 rts 3240 * 3250 *-------------------------------- 3260 Print.an.IRQ.Name 3270 pha 3280 phx 3290 phy 3300 txa Get offset into message index table 3310 asl 3320 tay 3330 * 3340 lda msg.addr,y Get address of message 3350 pea 0 push upper 16-bits of long word pointer to string 3360 pha push lower 16-bits of long word pointer to string 3370 >TK WriteString 3380 >jsr.if.error Print.Error 3390 * 3400 pea 0 3410 pea IE 3420 >TK WriteCstring 3430 >jsr.if.error Print.Error 3440 * 3450 ply 3460 plx 3470 pla 3480 rts 3490 *-------------------------------- 3500 >word.boundary I don't know what 65816 does about 16-bit 3510 IRQ.status .bs 2,0 fetches from non-word alined addresses 3520 *-------------------------------- 3530 Msg.Addr 3540 .da M0 Index to interrupt name strings 3550 .da M1 3560 .da M2 3570 .da M3 3580 .da M4 3590 .da M5 3600 .da M6 3610 .da M7 3620 .da IE 3630 * 3640 M0 >str "External VGC" 3650 M1 >str "Scan Line" 3660 M2 >str "ADB Data" 3670 M3 >str "Reserved" 3680 M4 >str "One-Second" 3690 M5 >str "Quarter-Second" 3700 M6 >str "Vertical-Blanking" 3710 M7 >str "Keyboard" 3720 * 3730 IE .as " Interrupt Enabled" in C-String format 3740 .hs 0D.00 3750 *-------------------------------- 3760 Kill.All.Tasks 3770 >native 3780 >setmode16 3790 >TK clrheartbeat Removes all tasks from HeartBeat Queue 3800 >jsr.if.error Print.Error 3810 >emulate 3820 rts 3830 * 3840 Task1.on 3850 >native 3860 >setmode16 3870 >pushconstl Task1hdr 3880 >TK setheartbeat Adds a task to the HeartBeat Queue 3890 >jsr.if.error Print.Error 3900 >emulate 3910 rts 3920 * 3930 Task1.off 3940 >native 3950 >setmode16 3960 >pushconstl Task1hdr 3970 >TK delheartbeat Removes a task from the HeartBeat Queue 3980 >jsr.if.error Print.Error 3990 >emulate 4000 rts 4010 * 4020 Task2.on 4030 >native 4040 >setmode16 4050 >pushconstl Task2hdr 4060 >TK setheartbeat 4070 >jsr.if.error Print.Error 4080 >emulate 4090 rts 4100 * 4110 task2.off 4120 >native 4130 >setmode16 4140 >pushconstl Task2hdr 4150 >TK delheartbeat 4160 >jsr.if.error Print.Error 4170 >emulate 4180 rts 4190 *-------------------------------- 4200 * Task One: 4210 * This task increments a location in display screen memory 4220 * on every 10th VBL 4230 *-------------------------------- 4240 Task1Hdr 4250 >hb.task.header Task1Cnt,1 4260 *-------------------------------- 4270 Task1 4280 >setmode16 4290 phk 4300 plb 4310 lda ##$0001 4320 sta task1cnt 4330 inc $400 The first two bytes of the 40-column text display screen 4340 >setmode8 4350 rtl 4360 *-------------------------------- 4370 * Task Two: 4380 * This task increments a location in display screen memory 4390 * on every 30th VBL 4400 *-------------------------------- 4410 Task2Hdr 4420 >hb.task.header Task2Cnt,1 4430 *-------------------------------- 4440 Task2 4450 >setmode16 4460 phk 4470 plb 4480 lda ##$0001 4490 sta task2cnt 4500 inc $402 The next two bytes of the 40-column text display screen 4510 >setmode8 4520 rtl 4530 *-------------------------------- 4540 Print.Error 4550 pha Push word -- error code 4560 >pushconstl System.Error.Message Push FullWord -- pointer to message 4570 >TK SysFailMgr Call system failure -- don't come back 4580 rts 4590 *-------------------------------- 4600 System.Error.Message 4610 >str "Error return by ROM toolkit -- $" 4620 *-------------------------------- 4630 Are.You.Ready 4640 >native 4650 >setmode16 4660 * 4670 pea 0 Save room for return button number 4680 >pushconstl Line.1 4690 >pushconstl Line.2 4700 >pushconstl Button.1 4710 >pushconstl Button.2 4720 >TK TLTextMountVolume 4730 BCC .1 4740 jsr Print.Error 4750 pla Remove button number from stack 4760 bra .9 4770 * 4780 .1 pla Get the button number (1..2) 4790 sta Button.number and save it for later 4800 pha Put it back 4810 >pushconstl button.ascii 4820 pea 2 All we want is two ascii characters 4830 pea 0 4840 >TK Int2Dec 4850 * 4860 >pushconstl Button.msg 4870 >TK WriteCstring 4880 * 4890 lda button.number Button one was for start, two for abort 4900 lsr 4910 bcc .9 4920 * 4930 >emulate Turn everything on to impress the casual observer 4940 jsr kill.all.tasks 4950 jsr enable.vbl.irqs 4960 jsr fiddle.border.color.by.HB 4970 jsr fiddle.foreground.color.by.HB 4980 jsr fiddle.background.color.by.HB 4990 jsr task1.on 5000 jsr task2.on 5010 jsr Whats.going.on 5020 rts 5030 *-------------------------------- 5040 .9 >emulate Turn everything off 5050 jsr kill.all.tasks 5060 jsr disable.vbl.irqs 5070 rts 5080 *-------------------------------- 5090 Button.number .bs 2,0 5100 * 5110 Button.msg .hs 0D.0D "C-string format" 5120 .as "Button #" 5130 Button.ascii .bs 2,0 Save some space for ASCII characters 5140 .as " was pressed" 5150 .hs 0D.00 5160 *-------------------------------- 5170 Line.1 >str "Border.Disorder" 5180 Line.2 >str " By: D. VanHoozer" 5190 Button.1 >str "Start <rtn>" 5200 Button.2 >str "Abort <esc>" 5210 *-------------------------------- |
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 $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 $1.80 each for Volumes 1-7 (other countries inquire for postage).
A subscription to the newsletter and the Monthly Disk containing all source code is
$64 per year in the USA, Canada and Mexico, and $87 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.)