Volume 8 -- Issue 3 | December 1987

In This Issue...

- Peeking Inside AppleWorks 1.3:
- Screen Dump PLUS!
- It's 1988, and ProDOS Thinks It's 1982
- Border Disorder

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.

Peeking Inside AppleWorks 1.3 + Subroutine Call Parameter Passage + String Handling Subroutines | Bob Sander-Cederlof

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 *--------------------------------

Screen Dump PLUS! | Louis Pitz

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 *--------------------------------

It's 1988, and ProDOS Thinks it's 1982 | Bob Sander-Cederlof

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 *--------------------------------

Border Disorder | Dewayne Van Hoozer

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.)