Apple Assembly Line
Volume 6 -- Issue 5 February 1986

In This Issue...

Bag of Tricks 2

You've been asking when Bag of Tricks, that very popular and useful disk utility package, will be updated for ProDOS. Well, you can relax now: it's here.

The new ZAP program in "Bag of Tricks 2" adds the ability to access ProDOS blocks, directories, and files; the 80-column display can show most of a block at one view. The new version of FIXCAT can reconstruct a blown ProDOS directory, as far as is possible. You do still need to follow up with ZAP to correct things like file size and load address, which completely disappear when a directory is damaged.

This new, non-copy-protected edition of an old friend costs $49.95, or $45 + shipping from S-C. Owners of the older Bag of Tricks can get an upgrade directly from Quality Software for only $20 by returning your original disk.

Correction to Day of Week Programs

On page 20 of the December 1985 AAL, change lines 130 and 140 to the following: 130 FOR I=0 TO 11 : READ MD(I) : NEXT 140 FOR I=0 TO 6 : READ D$(I) : NEXT

On page 24, same issue, change line 120 to: 120 FOR I=0 TO 6 : READ D$(I) : NEXT

That's what we get for typing a program into the Word Processor rather than printing a LISTing!

WildCAT for DOS 3.3 Erv Edge

WildCAT is a series of patches to DOS 3.3 which modify the CATALOG command. The new features include:

Lee Reynold's FILEDUMP command has been re-packaged and re-presented as TYPE (see Call-A.P.P.L.E. 6/82 p47). More on this later. WildCAT, along with TYPE, is an attempt to teach new tricks to an old dog, as it were.

The normal DOS CATALOG command allows slot, drive, and volume parameters. I have added a filename parameter, but process it a little differently than the way file names are usually processed. To display the catalog entries for all files whose names contain a particular string, type any of the following:

   CATALOG ^string [,Dn] [,Sn] [,Vn]
   DIR ^string [,Dn] [,Sn] [,Vn]
   CAT ^string [,Dn] [,Sn] [,Vn]

where "^string" begins with the "^" or caret symbol (shifted N on the ][+ or shifted 6 on the //e or //c); the string should contain no blanks, although it may "end" with them; the string would normally end with a carriage return or with a comma if a drive or slot number is specified. Only those files that contain the "string" somewhere in the filename will be listed. (Of course you already know that the D, S, and V parameters are shown in brackets above because they are optional; you do not type the brackets.)

For example, "CATALOG ^TEST" would list each file with 'TEST' as part of the filename; while "DIR ^PAY." would list those with 'PAY.' somewhere in the filename; and "CAT^.OBJ,D2" would list filenames on drive 2 that contain the partial string '.OBJ'. "CAT" and "DIR" are simply synonyms for "CATALOG".

I have also arranged things so you can list the catalog entries of a specified file-type. You simply type the file type code in the CATALOG command:

   CATALOG t [,Dn] [,Sn] [,Vn]
   DIR t [,Dn] [,Sn] [,Vn]
   CAT t [,Dn] [,Sn] [,Vn]

where "t" is any of the unadorned, single-letter filetypes: A B I R S T. Only that type of file (if present) will be listed.

For example, "CATALOG T" would list all the text files; "DIR A,D2" would list all of the Applesoft files on drive 2; "CAT B,S5,D1" would list all the binary files on slot 5, drive 1. Yes, "DIRT" works just fine.

I added the TYPE command, which allows you to display the contents of text files. Both CATALOG and TYPE will optionally:

  1. Print "hidden" control characters as inverse:
    POKE 234,0 to print as inverse (default)
    POKE 234,255 to function as-is
  2. Lower case letters may be shifted to upper case:
    POKE -18700,255 no shift (default)
    POKE -18700,223 to shift lower to upper case.

You can slow down TYPE's output via SPEED=xx or POKE 241,xx; or pause by pressing any key; then Ctrl-Q to abort. Also, TYPE pauses and waits for a keypress when it encounters a hex 00 imbedded in the file or at end of file; press Ctrl-Q to quit. You may TYPE random text files by holding down REPT-SPACE to get past the hex 00's at the end of each logical record.

The listing that follows is intended for information only: it is not BRUNable. My intention is that you prepare the EXEC shown below to actually install the patches. Any word processor that produces a straight, sequential text file may be used to prepare the EXEC. Of course you can also use the S-C Macro Assembler for this purpose. Then, type EXEC WILDCAT to apply the patches to DOS 3.3 in memory. After checking it out and running any other tests you like, put in a new diskette, enter a HELLO program, and type INIT HELLO to "permanently" install WildCAT in the DOS on tracks 0, 1, and 2.

When I wrote WildCAT, I had two main goals in mind: it should be a (mostly in-place) code replacement, and it should be compatible with the known means of using (abusing?) the existing CATALOG code at $AD98-AE69.

One major design consideration was a mechanism for entering the ^string/type parameter. This required merely changing the "keyword parameter table" so CATALOG could have a "filename".

Next, a distinction had to be made between a "wildcard" and a "filetype" parameter. It made sense to 'delimit' the wildcard string; then the single-character filetype would be just that: a single character, entered without a delimiter. But this "phony" name mechanism has it's own problems:

First, "What's in a Name?" (DOS Manual p. 16): a filename has to start with a letter...which automatically eliminates most special characters (eg, equal, pound, slash, colon, etc) as the delimiter. The command parsing routine doesn't really know what it's working on at the time. All it knows is: if a name may be present, it must be valid. The validity test is only that the first character be equal to or greater that $C0 or an @-sign. The @-sign could have been used, but it's a problem on some 80-column boards; the ^ or caret works nicely (and besides, it looks good).

Second, now that we have a name (however, phony) and since the CATALOG command lives in the File Manager (FM) portion of DOS, there will be a buffer allocated for it. Unfortunately, the Command Interpreter (CI) DOCAT routine, which calls the FM, already "knows" that there will not really be a name, so it does not include housekeeping code to deallocate a buffer. So merrily allocating files without closing them...after the third time: NO BUFFERS AVAILABLE. And naively adding CLOSE (even if there were room for it), would have one very undesirable side effect if a "regular" catalog were requested: CATALOG-CLOSE without FNAME will close all open files. WildCAT instead plays a little shell game with DOS: The new DOCAT routine saves the first character of FNAME and substitutes a zero. Thereafter, neither the File Manager nor the rest of DOS ever knows that a name has been entered, so FM never actually allocates a buffer.

Third, what really should happen if a phony name is not entered? A regular catalog, of course, but how would this be indicated to WildCAT? Well, the shell game has a sting. Early on when the CI PARSE routine discovers that a filename is a valid parameter, it first clears FNAME to all blanks, expecting to fill it in with whatever comes in next. If a comma or carriage return comes in next, then FNAME still contains the blank; and that's what WildCAT saves off (under the shell) before it substitutes the zero.

Thus, the "sting" is that the CI "tricks" itself into telling WildCAT what to do in the absence of a ^string/type specifier: WildCAT takes a blank to indicate "do a regular" catalog; just as positively as a "^" indicates "do a wildcard" catalog, and a single character indicates "do a filetype" catalog.

The blank indicator also helps satisfy the second goal above and solves the problem of compatibilty with the "known means" of using/abusing the existing CATALOG code. WildCAT simply has to put a blank under the shell at each of the points where the code could most reasonably be entered without going thru the Command Interpreter's new DOCAT routine. That's exactly what all the JSR's to the routine AllowENTry are doing.

Satisfying that second goal takes up a lot of space, however; and has somewhat undermined the first constraint: WildCAT certainly isn't "in-place" in one place! And I apologize for this rather bizarre, serpentine code; I do hope that now you understand why some things were done the way they were.

Although considerable effort was spent to maintain compatibilty with the existing DOS commands, there were some compromises:

1. While the DOS manual (page 22) states: "To specify drive 1, you use the notation D1 separated from the file name by a comma", you can in fact leave out the comma between CATALOG and D1. With WildCAT that comma is now required; otherwise, it would take the "D" as a filetype and try to find it ... which of course it wouldn't and there would be no files reported. This would also be a problem for Applesoft programs that do something like: PRINT D$"CATALOG D1" without the comma. Therefore, WildCAT issues a (late) "SYNTAX ERROR" message if it encounters an undelimited string of length 2 or more.

2. CATALOG is a favorite routine to execute directly, bypassing the DOS Command Interpreter. FID, for example, provides its CATALOG via the "external" entry to the File Manager, which means that the main entry at CATHNDLR must provide for a "regular" catalog. It is also possible from machine language, however, to bypass both the CI and the FM. This usually involves changing the exit JMP address at DONEXT2 (to return to the user's code) and then jumping directly into almost anywhere in the CATALOG code (see the Listing 1 labels that begin "at"). I believe most of these cases are covered, but you may find some programs, which provide an "internal" CATALOG, that just won't work with WildCAT.

3. In order to both gain some patch space and provide the DIR/CAT short-form command name, the DOS command POSITION was eliminated. You may have to look it up just to find out that it is, much less what it is. Its relative rarity may be due to its implementation: it, like APPEND, finds its way through the file one byte at a time...all day long. Any program that uses it will now get a syntax error. If POSITION is really needed, it can be readily simulated by programming a read-loop to discard N-1 fields before processing the desired Nth field.

The following is a brief commentary on the assembly listing. The paragraph numbers correspond to numbers in comment lines.

The page zero locations I used ($EB thru $EF) are free, i.e. not used by DOS, the Monitor, or the Basics.

(1) In CMDTBL, replace Integer CHAIN address with TYPE and DOCAT address with NewDOCAT.

(2) Rearrange some code (and change both references to it) to add a "print blank" capability. The Command Interpreter uses its own vector to a "COUT" routine via CSW at $36; however, the File Manager (previously) used the Monitor COUT and CROUT routines for printing the catalog. With WildCAT all of DOS now consistently uses the vector at $9FCA for output; plus it has a new BlankOUT routine, all within the original code space.

(3) Recode a very cumbersome form of the "indexed indirect jump" to use register Y and leave X (which is zero by a previous operation) so it can be used in NewDOCAT.

(4) Replace old DOCAT's 12 bytes of code with a JMP to NewDOCAT and use the remainder to space over to column 7 after the file length has been displayed.

(5) NewDOCAT saves the first character of FNAME and substitutes a zero to prevent buffer allocation. It then loads 13, the new Catalog Function Code, and proceeds to CMDHNDLR2. Function 13 enters the catalog code past the "allow for irregular, direct entry".

(6) In the keyword parameter table, change parms to allow a filename with CATALOG and a filename, drive, and slot with DIR. Set new Function 13 address (previously a "no-op" to NOERROR) to WildCAT and change the range check to 14 to allow for it.

(7) Replace the Integer CHAIN code; PrtLOCK displays an asterisk or blank if the file is locked or not.

(8) Shorten the "NO BUFFERS AVAILABLE" message to "NO BUFFER" and re-use the space to decide which Basic is active, then JMP to the appropriate decimal print routine; used to print the free sector value and catalog filesizes. The value to be printed has been previously loaded into A and X.

(9) First, eliminate the need for "NOT DIRECT COMMAND" error message and then re-use the space to check for a "regular" catalog (no filename) or for a catalog by filetype (undelimited, single character). If more than a single, non-blank character is detected (ie, 2nd byte of FNAME is not blank), then "SYNTAX ERROR" message is issued.

(10) At beginning of catalog code allow for most normal points where the code could be directly entered. The new "official" Function 13, WildCAT initializes the FM workarea (per normal) and branches to Read VTOC to "find" the first catalog sector.

(11) Freespace "prolog"; clear carry and branch around another likely "irregular" entry point. Read first/next catalog sector, then lookup and save the filetype. Setup Y with 30 for name length and branch to CkFNAME.

(12) AllowVTOC fakes a "regular" catalog and falls into a JSR to read the VTOC. The BCC to initialize linecount is always taken; only if there had been an I/O error would the carry be set, in which case, control would have passed to the error-message-print exit anyway.

(13) PrtCat displays a catalog line. Note that loc $24, CH, is "POKEd" with 7 for uniform spacing over to the filename. If your printer interface board or 80-column card do not support this convention, then the display will not be properly spaced. The DONEXT routine is unchanged. SKIPLN has been re-arranged to allow init linecount, put out a carriage return, and check for a keypress (Ctrl-Q to quit) after 22 lines. Note: This leaves the cursor in column 37; see below.

(14) CkFNAME "looks under the shell" to figure out what to do. A caret indicates to check for a wildcard string. After JSR to CkCAT, if the equal status is set, then branch to print the catalog line. DoWild checks for the occurence of the wildcard string within the filename. $B4C9,X indexes the name in the Catalog Sector; $AA75,Y indexes the wildcard string; CatNmLen counts from 30 to 0, to scan the whole name.

(15) FreeSpce counts the free sectors, as indicated by the VTOC, loads X and A with the count, and JMPs ToPrtDec.

(16) WaitCk79 provides the "wait" for TYPE; also checks and puts out a carriage return after 79 characters to avoid overprinting long lines on certain printers, such as the MX-80.

(17) TYPE displays the contents of a sequential or random text file. A keypress will pause the display, and Ctrl-Q quits.

(18) InvCOUT is used by both CATALOG and TYPE. It converts hi-bit off characters to proper inverse. It will optionally show control characters as inverse or allow them to "function" as-is; and it will optionally "shift" lower case letters to upper case, if you do not have a lower case adapter; see "...Options" above. Loc $EA, decimal 234, is the Applesoft Hi-Res collision counter; it should always be zero, unless you POKE it.

(19) WaitCQ waits for a keypress and sets the equal status, if Ctrl-Q was pressed.

(20) Replace the inverted phrase DISK VOLUME with FREE SPACE=.

(21) The DOSCMDS list is moved down 6 bytes. AllowENT re-uses these 6 bytes to force a blank in FName1 "under the shell" to ease "irregular" entries into the catalog code; and clears the carry in case the entry was 'atADC9' which also cleared the carry. In the command list, TYPE replaces CHAIN and DIR replaces POSITION; change $A8BF:43 41 D4 to replace with CAT.

(22) Change the two references to DOSCMDS to the new location. These two changes must be done last as the EXEC is changing the very code that is executing.

I would like to thank Lee Reynolds and Art Schumer for their helpful comments and suggestions.

  1010 *--------------------------------
  1020 CatLnCnt   .EQ $EB Catalog Linecount
  1030 FType      .EQ $EC Hold looked-up filetype
  1040 FName1     .EQ $EE Hold FNAME shell 1st char
  1050 CatNmLen   .EQ $ED CatName check-length=30
  1060 CatPtLen   .EQ $EF CatName print-length=30
  1070 *-----(1)----
  1080        .PH $9D26 In CMDTBL, command addresses,
  1090        .DA TYPE-1 change Integer CHAIN to TYPE
  1100        .PH $9D3E In CMDTBL, change address to
  1110        .DA NewDOCAT-1 new DOCAT in POSITION code
  1120 *-----(2)----
  1130        .PH $9FA8 In ECHO, change old COUT ref
  1140        .DA #$CA was JSR $9FC5 now JSR $9FCA
  1150        .PH $9FC5 Cleanup CDI COUT and CROUT
  1160 BlankOUT LDA #" " and add BLANK out routine
  1170        .DA #$2C fake BIT-NOP on fall-thru
  1180 CROUT  LDA #$8D  DOS vectored CROUT; same loc
  1190 COUT   JMP ($36) DOS vectored COUT; new loc
  1200        .PH $A710 In PRTERROR, change old COUT
  1210        .DA #$CA was JSR $9FC5 now JSR $9FCA
  1220 *-----(3)----
  1230        .PH $A186 Cleanup DOCMD; X=0 in NewDOCAT
  1240        LDY $AA5F CMDINDX
  1250        LDA $9D1F,Y CMDTBL+1; use Y instead of X
  1260        PHA
  1270        LDA $9D1E,Y CMDTBL
  1280        PHA
  1290        RTS
  1300        NOP
  1310 *-----(4)----
  1320        .PH $A56E Replace old DOCAT code:
  1330 OldDOCAT JMP NewDOCAT To allow for direct entry
  1340 PrtTYPE LDA FType Print looked-up filetype
  1350        JSR COUT and
  1360        JMP BlankOUT a blank
  1370        NOP
  1380 *-----(5)----
  1390        .PH $A5DD Replace old POSITION code:
  1400 NewDOCAT LDA $AA75 FNAME set by CATALOG command
  1410        STA FName1 save first byte, then zero
  1420        STX $AA75 to avoid buffer allocation
  1430        LDA #13 FM WildCAT Function Code
  1440        JMP $A2AA CMDHNDL2 routine, per usual
  1450        NOP
  1460 *-----(6)----
  1470        .PH $A921 DIR [string] [,Dn] [,Sn]
  1480        .DA #$60,$70 ->First comma: is NOT optional
  1490        .PH $A929 CATALOG [string] <[,Dn] [,Sn]>
  1500        .DA #$60 ->Must be CATALOG,D1 or DIR,D2
  1510        .PH $AAE3 In FM function table, "borrow"
  1520        .DA WildCAT-1 otherwise useless address
  1530        .PH $AB10 Change range check from 13 for
  1540        CMP #14 above now USEFULL address
  1550 *-----(7)----
  1560        .PH $A4F0 Replace Integer CHAIN code
  1570 PrtLOCK LDA #" " blank=unlocked
  1580        LDX $B4C8,Y Catalog Filetype entry
  1590        BPL ToPrint
  1600        LDA #"*" *=locked
  1610 ToPrint JMP COUT Print " " or "*" indicator
  1620 *-----(8)----
  1630        .PH $A9FD Shorten NO BUFFER[S AVAILABLE]
  1640        .AS -"R" to free 11 bytes for ToPrtDec:
  1650 ToPrtDec BIT $E006 Check which Basic...
  1660        BMI ToInt Integer or
  1670        JMP $ED24 Applesoft; use appropriate
  1680 ToInt JMP $E51B print decimal routine
  1690 *-----(9)----
  1700        .PH $A021 Replace JSR ISBASRUN to allow
  1710        NOP  ALL commands entered direct
  1720        NOP  then error msg is redundant so
  1730        NOP  ok to re-use msg space below
  1740        .PH $AA2C Replace NOT DIRECT COMMAND msg
  1750 CkCAT CMP #" " If blank, do regular catalog
  1760        BEQ ToRTS
  1770        LDY #" " Must be single-char filetype
  1780        CPY $AA76 FNAME+1, ie blank afterwards
  1790        BEQ CkType if catalog by filetype; else
  1800        JMP $A6C4 CSYNTAX error
  1810 CkType CMP FType Does filetype match?
  1820 ToRTS RTS
  1830        NOP
  1840        NOP
  1850 *----(10)----
  1860        .PH $AD98
  1870 CATHNDLR JSR AllowENT Allow for non-CDI entry
  1880 WildCAT JSR $ABDC Init File Manager Workarea
  1890        BNE ToRWVTOC
  1900 atADA0 JMP AlowVTOC Allow for non-CDI entry
  1910 atADA3 NOP  Allow for non-CDI entry and
  1920        NOP  alignment
  1930 atADA5 JSR AllowENT Allow for non-CDI entry
  1940 atADA8 JSR AllowENT Allow for non-CDI entry
  1950 atADAB JSR InitCR Init Linecount; output C/R
  1960 *----(11)----
  1970        JSR SKIPLN
  1980        LDX #12
  1990 PrtFreSP LDA FreeMsg-1,X
  2000        JSR COUT Print " FREE SPACE="
  2010        DEX
  2020        BNE PrtFreSP X=0 for PrtFreSP
  2030        JSR FreeSpce Count & print free sectors
  2040        JSR SKIPLN
  2050        JSR SKIPLN
  2060        CLC  Setup for RDNXTDIR to read
  2070        BCC RDNXTDIR first sector; always branch
  2080        NOP  alignment
  2090 atADC9 JSR AllowENT Allow non-CDI, non-FM entry
  2110        BCS DONEXT2
  2120        LDX #0
  2140        LDA $B4C6,X Track part of T/S list
  2150        BEQ DONEXT2 If End of Catalog, then exit
  2160        BMI DONEXT If Deleted File, then skip it
  2170        LDA $B4C8,X Catalog Filetype
  2180        ASL  ;skip hi-bit LOCK/UNLOCK flag
  2190        LDY #7
  2200 FindTYPE ASL
  2210        BCS GotTYPE
  2220        DEY
  2230        BNE FindTYPE
  2240 GotTYPE LDA $B3A7,Y From filetype table,
  2250        STA FType save looked-up filetype
  2260        LDY #30 Check CatName length and
  2270        STY CatPtLen Print CatName length
  2280        BNE CkFNAME always BNE
  2290 *----(12)----
  2300 AlowVTOC JSR AllowENT Allow for non-CDI entry
  2320        BCC atADAB always; carry set=I/O ERROR
  2330 *----(13)----
  2340        NOP  ;alignment
  2350        NOP
  2360 PrtCAT LDY $B39C Restore Y from DIRINDX
  2370        JSR PrtLOCK Print Lock indicator
  2380        JSR PrtTYPE Print filetype and BlankOUT
  2390        LDX $B4E7,Y Filesize
  2400        LDA $B4E8,Y Filesize+1
  2410        JSR ToPrtDec Print "true" filesize
  2420        LDY #7 "Poke" CH with 7 to "tab"
  2430        STY $24 over for filename spacing
  2440        LDX $B39C Restore X from DIRINDX
  2450 PrtFN LDA $B4C9,X Print Catalog Filename
  2460        JSR InvCOUT with optional conversions
  2470        INX
  2480        DEC CatPtLen CatName print length
  2490        BNE PrtFN
  2500        JSR SKIPLN
  2510 DONEXT JSR $B230 NXTDIREN...atAE25
  2520        BCC GTRKNUM
  2530        BCS RDNXTDIR
  2540 DONEXT2 JMP $B37F NOERROR....atAE2C
  2550 SKIPLN DEC CatLnCnt Linecount..atAE2F
  2560        BNE ToCR If not zero, C/R & return
  2570        JSR WaitCQ else wait for keypress
  2580        BEQ DONEXT2 If Ctrl-Q, exit to NOERROR
  2590 InitCR LDA #22-1 else setup for next 22 lines
  2600        STA CatLnCnt in line count
  2610 ToCR JMP CROUT DOS vectored C/R out
  2620 *----(14)----
  2630 CkFNAME LDA FName1 Holds FNAME first character
  2640        CMP #"^" Wildcard string?
  2650        BEQ DoWild yes...maybe
  2660        JSR CkCAT Regular or by filetype?
  2670        BEQ PrtCAT yes...else
  2680        BNE DONEXT none of the above
  2690 DoWild STY CatNmLen CatName length=30, for NotEQ
  2700        LDY #1 Decr'd to 0; indexes FNAME
  2710 NotEQ DEC CatNmLen Checked all 30 chars?
  2720        BMI DONEXT Yes; no match, do next CatName
  2730 BackDown DEX  Backdown to string match start
  2740        DEY  Backdown to 0, ie. FNAME start
  2750        BNE BackDown
  2760 YesEQ INY  First Y=1, then on past "^"
  2770        INX
  2780        LDA $AA75,Y FNAME
  2790        CMP #" " If blank then wildcard EOS and
  2800        BEQ PrtCAT still =, so we have a match!
  2810        CMP $B4C9,X FNAME = CatName?
  2820        BEQ YesEQ
  2830        INX  No, setup X to backdown 1 past
  2840        BNE NotEQ string match start; always BNE
  2850        NOP
  2860 *----(15)----
  2870        .PH $BA69 Catalog Free Space Patch
  2880 FreeSpce STX $44 X=0
  2890        STX $45 Init Free Sec Count var
  2900        LDY #50*4 VTOC entries * entry length
  2910 NxBitMap LDA $B3F2,Y BITMAP-1 in VTOC buffer
  2920 CkFree ASL  ;shift hi-order bit into CARRY
  2930        BCC CkMore In use, so check if any more
  2940        INC $44 Incr free sector count
  2950        BNE CkFree Zero means > 255, so
  2960        INC $45 incr "page" part of word
  2970 CkMore BNE CkFree More bits in same byte?
  2980        DEY  decr index to next VTOC byte
  2990        BNE NxBitMap All done?
  3000        LDX $44 Yes, so setup count in X & A
  3010        LDA $45 for decimal print via
  3020        JMP ToPrtDec one of the BASICs
  3030 *----(16)----
  3040 WaitCk79 JSR $FCA8 Monitor WAIT routine
  3050        DEC $55 Decr char cnt
  3060        BNE $BA10 Fortuitous RTS; else fall thru
  3070 InitLine LDA #79 TYPE prolog/setup
  3080        STA $55 Init printer 80-col char cnt
  3090        JMP CROUT
  3100        NOP
  3110 *----(17)----
  3120        .PH $B6B3
  3130 TYPE JSR $A2A3 DOS Open file
  3140 DoInitLn JSR InitLine Init char cnt & CROUT
  3150 ToRead JSR $A68C DOS Read char
  3160        BEQ ToWaitCQ EOF maybe...Ctrl-Q quit?
  3170        CMP #$8D Carriage return?
  3180        BEQ DoInitLn Yes, handle immediately
  3190        JSR InvCOUT Optional Ctrls & Hibit=0 INV
  3200        LDA $F1 Applesoft SPEED=nn byte
  3210        JSR WaitCk79 Wait SPEED; 79 chars yet?
  3220        LDA $C000 Has a key been pressed?
  3230        BPL ToRead No, read on
  3240        STA $C010 Reset keyboard strobe
  3250 ToWaitCQ JSR WaitCQ Wait keypress, check Ctrl-Q?
  3260        BNE ToRead If not Ctrl-Q, read on
  3270        JMP $A2FC DOS Close, Deallocate, Exit
  3280 *----(18)----
  3290 InvCOUT TAY  If < $80, then hibit off
  3300        BPL SetINV so set inverse flag & convert
  3310        CMP #$A0 Ctrl-char?
  3320        BCS CkLoCase No
  3330        BIT $EA Usually, loc 234 contains 0:
  3340        BMI CkLoCase POKE 234,255 skips conversion
  3350 SetINV LSR $32 Set Inverse by shifting 0 into
  3360        LSR $32 INVFLG first 2 bits; set carry
  3370        AND #$3F Turn off 1st 2 bits maps down
  3380        ADC #$1F maps up into hibit-on part of
  3390        EOR #$E0 upper-case screen-char range
  3400 CkLoCase CMP #$E0 Lower-case?
  3410        BCC ToCOUT No; but POKE -18700,223 or
  3420        AND #$FF B6F4:DF shifts l.c. to U.C.
  3430 ToCOUT JSR COUT DOS vectored COUT
  3440        LDA #$FF
  3450        STA $32 Set normal video; always
  3460        RTS
  3470 *----(19)----
  3480        .PH $B78D Wait keypress; check Ctrl-Q
  3490 WaitCQ JSR $FD0C Monitor RDKEY
  3500        CMP #$91 Was it Ctrl-Q?
  3510        RTS
  3520 *----(20)----
  3530        .PH $B3AF Replace: DISK VOLUME inverted
  3540 FreeMsg .AS -"=ECAPS EERF " with FREE SPACE=
  3550 *----(21)----
  3560        .PH $A884 Setup FName1 for "irregular"
  3570 AllowENT LDA #" " entry into CATALOG code
  3580        STA FName1 Force blank at CkFNAME above
  3590        CLC  For possible RDNXTDIR entry
  3600        RTS
  3610 DOSCMDS .AT 'INIT' Move down DOSCMDS table and
  3620        .AT 'LOAD' re-use the freed space above
  3630        .AT 'SAVE'
  3640        .AT 'RUN'
  3650        .AT 'TYPE' was CHAIN
  3660        .AT 'DELETE'
  3670        .AT 'LOCK'
  3680        .AT 'UNLOCK'
  3690        .AT 'CLOSE'
  3700        .AT 'READ'
  3710        .AT 'EXEC'
  3720        .AT 'WRITE'
  3730 atA8BF .AT 'DIR' was POSITION; for CAT:43 41 D4
  3740 *----(22)----
  3750        .PH $9FFB In Command Interpreter PARSE
  3760        LDA DOSCMDS,Y DOSCMDS table ref. was $A884
  3770        .PH $9FED Only 2 references to DOSCMDS
  3790 *--------------------------------
     MON I
     CALL -151
     9D26:B2 B6
     9D3E:DC A5
     9FC5:A9 A0 2C A9 8D 6C 36 00
     A186:AC 5F AA B9 1F 9D 48 B9
         :1E 9D 48 60 EA
     A56E:4C DD A5 A5 EC 20 CA 9F
         :4C C5 9F EA
     A5DD:AD 75 AA 85 EE 8E 75 AA
         :A9 0D 4C AA A2 EA
     A921:60 70
     AAE3:9A AD
     AB10:C9 0E
     A4F0:A9 A0 BE C8 B4 10 02 A9
         :AA 4C CA 9F
     A9FD:D2 2C 06 E0 30 03 4C 24
         :ED 4C 1B E5
     A021:EA EA EA
     AA2C:C9 A0 F0 0C A0 A0 CC 76
         :AA F0 03 4C C4 A6 C5 EC
         :60 EA EA
     AD98:20 84 A8 20 DC AB D0 57
         :4C F4 AD EA EA 20 84 A8
         :20 84 A8 20 38 AE
     ADAE:20 2F AE A2 0C BD AE B3
         :20 CA 9F CA D0 F7 20 69
         :BA 20 2F AE 20 2F AE
     ADC5:18 90 04 EA 20 84 A8 20
         :11 B0 B0 5B A2 00 8E 9C
         :B3 BD C6 B4 F0 51 30 48
     ADDD:BD C8 B4 0A A0 07 0A B0
         :03 88 D0 FA
     ADE9:B9 A7 B3 85 EC A0 1E 84
         :EF D0 4B
     ADF4:20 84 A8 20 F7 AF 90 AF
     ADFC:EA EA AC 9C B3 20 F0 A4
         :20 71 A5
     AE07:BE E7 B4 B9 E8 B4 20 FE
         :A9 A0 07 84 24 AE 9C B3
     AE17:BD C9 B4 20 DA B6 E8 C6
         :EF D0 F5 20 2F AE
     AE25:20 30 B2 90 A9 B0 A0 4C
         :7F B3
     AE2F:C6 EB D0 09 20 8D B7 F0
         :F4 A9 15 85 EB 4C C8 9F
     AE3F:A5 EE C9 DE F0 07 20 2C
         :AA F0 B4 D0 D9
     AE4C:84 ED A0 01 C6 ED 30 D1
         :CA 88 D0 FC C8 E8 B9 75
         :AA C9 A0 F0 9D DD C9 B4
         :F0 F2 E8 D0 E7 EA
     BA69:86 44 86 45 A0 C8 B9 F2
         :B3 0A 90 06 E6 44 D0 F9
         :E6 45 D0 F5 88 D0 EF A6
         :44 A5 45 4C FE A9
     BA87:20 A8 FC C6 55 D0 82 A9
         :4F 85 55 4C C8 9F EA
     B6B3:20 A3 A2 20 8E BA 20 8C
         :A6 F0 14 C9 8D F0 F4 20
         :DA B6 A5 F1 20 87 BA AD
         :00 C0 10 EA 8D 10 C0 20
         :8D B7 D0 E2 4C FC A2
     B6DA:A8 10 08 C9 A0 B0 0E 24
         :EA 30 0A 46 32 46 32 29
         :3F 69 1F 49 E0
     B6EF:C9 E0 90 02 29 FF 20 CA
         :9F A9 FF 85 32 60
     B78D:20 0C FD C9 91 60
     B3AF:BD C5 C3 C1 D0 D3 A0 C5
         :C5 D2 C6 A0
     A884:A9 A0 85 EE 18 60
     A88A:49 4E 49 D4 4C 4F 41 C4
         :53 41 56 C5 52 55 CE 54
         :59 50 C5
     A89D:44 45 4C 45 54 C5 4C 4F
         :43 CB 55 4E 4C 4F 43 CB
     A8AD:43 4C 4F 53 C5 52 45 41
         :C4 45 58 45 C3 57 52 49
         :54 C5
     A8BF:44 49 D2
     9FFB:B9 8A A8
     9FED:59 8A A8
     48:04 N 3D0G
     NOMON I

Mitsubishi 50740 Series Bob Sander-Cederlof

I received information from several sources this week about an interesting new branch of the 6502 tree. Mitsubishi has published specs for eight varieties, all part of the "740 series". The chip is based on the 6502, adds some new addressing modes for some of the standard 6502 opcodes, and adds 13 new opcodes. (Unfortunately, the opcode enhancements are not compatible with any of the other enhanced 6502s.)

The chips in the 740 series are intended for use as microcontrollers. As such, most of them have on-chip RAM and ROM. They all have built-in I/O ports, timers, and other goodies. The most interesting (to Don Lancaster, Nigel Nathan, and me) is the M50734. This chip, said to cost only $12, has four A/D converters, UART, six timers, a serial I/O port, four 8-bit I/O ports, a pair of stepper-motor drivers, and more. It all lives in a 64-pin shrink-DIP package. The M50734 is the only one in the 740 series which has no internal ROM and RAM. It is CMOS. The clock runs at 8 MHz, which in effect runs the opcodes at 2 MHz (that is, two cycle instructions take one microsecond).

To control all these functions, the bytes in page zero from $DA through $FF are used as I/O, control, and status registers.

One of the trickiest enhancements allows direct access (without bank switching or bank registers) to a second 64K memory, for data only. Apparently one of the address modes changes the state of one of the output signals during data memory references; if you use that signal to enable another bank of memory. ALMOST like having direct-addressability of 128K.

The data bus is multiplexed with half of the address bus, so it's a little harder to interface. Naturally, to get all the functions I mentioned above with only 64 pins, there have to be shared pins. Depending on which functions you are using, some of the timers and some of the I/O pins have dedicated uses.

The 6502 has one unused status bit. The 740 series calls this the T-flag, and gives it a use. If T=1, a special address mode is enabled which allows memory-to-memory operations without using the A-register. As I understand it, when T=1, address modes which use X as an index register take on a new meaning: rather than moving data between the indexed address and the A-register, data is moved between the absolute address and the zero page location whose address is in the X-register. If I am correct, ADC $400,X (assume X contains $34) would add the contents of $400 to the contents of $34, and store the result in $34. If T=0, indexing works in the old-fashioned 6502 way.

Another powerful enhancement allows you call subroutines with a two-byte version of the JSR opcode. One variation uses vectors stored in page zero, and the other uses vectors stored $FF00 through $FFF3. JMP can also uses vectors stored in page zero, so you have a two-byte JMP indirect.

Four new opcodes give you the ability to set, clear, or test any bit in the A-register or in page zero. This uses up 64 opcodes, because the bit number and bit state are coded into the opcode byte. Rockwell's version of the 65C02 includes page-zero bit-addressing, but the opcodes are not the same.

There are other new instructions, including several about which I do not have accurate complete information.

       RRF zp     (I think it swaps nybbles in the byte)
       COM zp     (Probably forms 2's complement at zp)
       LDM zp     (Probably loads ABS(zp) into A-register)
       CLT        clear T-bit in status
       SET        set T-bit in status
       STP        stop the clock until reset or interrupt
       WIT        low power mode   "     "   "      "
       SLW        (slow?)
       FST        (fast?)
       INC        increment A-reg
       DEC        decrement A-reg
       BRA rel    branch always.

Of all the extensions, only ONE (BRA) is compatible with the standard 65C02 and 65816 extensions from Western Design Center (the OFFICIAL source for 6502 designs). The others, even if they do the same thing, use a different opcode value. Why?

If you have worked up an appetite for more information on the 740 series, contact Mitsubishi. I don't have all their numbers, but you can get close by calling 1-800-421-1132.

When we get all the data, we will be writing a Cross Assembler so you can use your Apple to develop software for this chip.

Faster Cyclic Redundancy Checking Bob Sander-Cederlof

In the April 1984 issue of AAL I showed how to compute a cyclic redundancy check code (CRC) for a buffer full of data. I also tried to explain a little of the theory, as much as I understood. In the June 1984 issue Bruce Love explained how to work backward from the computed CRC of a received buffer to correct a single bit error. Both of these programs were written in plain 6502 code.

In the February 1986 "Dr. Dobb's Journal" Terry Ritter writes about "The Great CRC Mystery". He also presents some Pascal programs and 8088 machine code programs for calculating the CRC in various ways. Terry describes very briefly a table driven method (the very fastest way) and a byte-oriented method (almost as fast as table-driven).

I translated Terry's machine-coded byte-oriented method from 8088 to 65802 code, but even after twiddling and tweaking for half a day I could not make it give the correct answers. I don't know if his method is correct or not, but of course it MUST be, since it is printed in Dr. Dobb's and since he claims it works and since he even tells how many milliseconds it takes.

Anyway, I decided to derive my own byte-oriented method. The CRC algorithm is basically a "long division" of the entire bit stream in the buffer as though it were one long binary word. The divisor is $11021 in the CCITT scheme. The check code we use is the remainder of the division. The normal algorithm does "long division" on a bit-by-bit basis. The byte-oriented algorithm does "long division" on a byte-by-byte basis.

I put long division in quotation marks above because it is not EXACTLY long division. The difference is that the subtraction steps are replaced with exclusive-or operations. The exclusive-or is performed whenever the leading bit of the new dividend is a 1-bit. Here is a fully worked out example, for a CRC-so-far = $E1F0, and the next byte = $CC:

       "divide"   $E1F0CC by $11021, "quotient bits" down
       the left edge.  Next CRC is the "remainder"

       1110 0001 1111 0000 1100 1100   (E1F0CC in binary)
1  eor 1000 1000 0001 0000 1           (11021 in binary)
        110 1001 1110 0000 01
1   eor 100 0100 0000 1000 01
         10 1101 1110 1000 000
1    eor 10 0010 0000 0100 001
0         0 1111 1110 1100 0010
            1111 1110 1100 0010 1
1     eor   1000 1000 0001 0000 1
             111 0110 1101 0010 01
1      eor   100 0100 0000 1000 01
              11 0010 1101 1010 000
1       eor   10 0010 0000 0100 001
               1 0000 1101 1110 0010
1        eor   1 0001 0000 0010 0001
                 0001 1101 1100 0011 = $1DC3

Note that the "quotient" is $EF. This "quotient" can always be exactly computed by using just the first byte of the dividend (the high byte of the old CRC code): quotient = crchi .eor. crchi/16. If you carefully study the worked out example above, you should be able to see why this is true. Now, if we use the exclusive-or rather than addition to perform a multiplication of the quotient times $11021, it will look like this:

                      uuuu.vvvv  (symbolic quotient in binary)
                       x $11021  (multiplier in hexadecimal)

There are several significant things to notice about the multiplication above. First, we only need to save the rightmost 16 bits of the "product". If we exclusive-or those bits with the rightmost 16 bits of the original dividend (which means the low byte of the old CRC followed by the new byte), we will get the next CRC. (This trick relies on the fact that exclusive-or is a reversible operation, so that "adding" and "subtracting" give the same result!)

Furthermore, we can organize those "partial products" in a more efficient way for computation. Now, let's write the original CRC symbolically as "aaaa.bbbb.cccc.dddd", and the next data byte as "eeee.ffff". The "quotient" after "dividing" by $11021 will be "aaaa.bbbb exclusive-or 0000.aaaa"; let's write that symbolically as "aaaa.gggg". Then we can compute the next CRC code by the following very simple steps:

  eor  gggg.0000.aaaa.gggg
  eor  000a.aaag.ggg0.0000

Believe it or not!

The program that follows implements this algorithm, in lines 1550-1760. I used 65802 code, but it really could be done quite nicely in plain 6502 as well. I leave it as "an exercise for the reader" (as college textbooks are wont to say), should you wish to try the algorithm in a plain-vanilla 6502.

The SEND and RECV programs simulate sending and receiving a buffer-full of data. I chose to put my buffer at $4000, for 258 bytes. This is the same as in the April 1984 article.

The FIND.BAD.BIT program is simply a translation of Bruce Love's 1984 program into 65802 code. Thanks to 16-bit registers, it is significantly faster and shorter.

Speaking of speed, the code for computing the next CRC code for one new byte takes (if I counted correctly) 57 clock cycles. In a normal Apple that means about 56 microseconds. The time for 8088 machine code in Terry Ritter's article was 17 microseconds for the equivalent steps. He was running with a 7.16 MHz clock. If you ran the 65802 code in an Applied Engineering Transwarp card or a Titan Accelerator card with a 4-MHz 65802 (running at 3.58 MHz), the time would be only 15.9 microseconds in an Apple.

  1010 *--------------------------------
  1020 BUFFER .EQ $4000
  1030 LIMIT  .EQ $4102
  1040 *--------------------------------
  1050 CRC    .EQ $00,01
  1060 PNTR   .EQ $02,03
  1070 TEMP   .EQ $0A,0B
  1080 *--------------------------------
  1090 PRNTAX .EQ $F941
  1100 CROUT  .EQ $FD8E
  1110 *--------------------------------
  1130 *--------------------------------
  1140 SEND
  1150        LDA #0       CLEAR CRC BYTES IN BUFFER
  1160        STA LIMIT-1
  1170        STA LIMIT-2
  1190        LDX CRC      STORE CRC INTO LAST 2 BYTES
  1200        LDA CRC+1
  1210        STX LIMIT-1
  1220        STA LIMIT-2
  1240        JMP CROUT    <RETURN> AND RETURN
  1250 *--------------------------------
  1270 *--------------------------------
  1280 RECV
  1300        LDX CRC      DISPLAY CRC IN HEX
  1310        LDA CRC+1
  1320        JSR PRNTAX
  1330        JMP CROUT
  1340 *--------------------------------
  1350        .OP 65802
  1360 *--------------------------------
  1370 *      CRCH      CRCL      DATA
  1380 *   aaaa.bbbb.cccc.dddd.eeee.ffff
  1390 *  +0000.aaaa
  1400 *   ---------
  1410 *   aaaa.gggg
  1420 *            +gggg.0000.aaaa.gggg
  1430 *            +000a.aaag.ggg0.0000
  1440 *             -------------------
  1450 *             (crchi)   (crclo)
  1460 *--------------------------------
  1480        CLC
  1490        XCE
  1500        REP #$30     M&X BOTH 16-BITS
  1510        LDA ##$FFFF
  1540 *--------------------------------
  1550 .1     SEP #$20     CRC=aaaabbbbccccdddd, DATA=eeeeffff
  1560        LDA CRC+1    aaaabbbb .eor. 0000aaaa = aaaagggg
  1570        LSR
  1580        LSR
  1590        LSR
  1600        LSR          0000aaaa
  1610        EOR CRC+1    aaaabbbb
  1620        XBA          AGXX
  1630        LDA #0       AG00
  1640        REP #$20
  1650        LSR
  1660        LSR
  1670        LSR          000a.aaag.ggg0.0000
  1680        STA TEMP
  1690        LSR          0000.aaaa.gggg.0000
  1700        EOR CRC      aaaa.bbbb.cccc.dddd = aaaa.gggg.kkkk.dddd
  1710        XBA          kkkk.dddd.aaaa.gggg
  1720        EOR TEMP     000a.aaag.ggg0.0000
  1730        SEP #$20
  1740        EOR 0,X        crchi.crclo
  1750        REP #$20
  1760        STA CRC
  1770 *--------------------------------
  1780        INX
  1790        CPX ##LIMIT
  1800        BCC .1
  1810        XCE
  1820        RTS
  1830 *--------------------------------
  1850 *--------------------------------
  1860 DUMMY.CRC  .EQ $10,11
  1870 *--------------------------------
  1900 *--------------------------------
  1910        CLC
  1920        XCE          ENTER NATIVE MODE
  1930        REP #$30     X,M 16 BITS
  1940        LDX ##$80F   X=BIT NUMBER
  1950        LDA ##1      START DUMMY CRC IN A-REG
  1960 .1     CMP CRC
  1970        BEQ .2       ...FOUND BAD BIT!
  1980        DEX          DECREMENT BIT NUMBER
  1990        BMI .2       ...WENT TOO FAR, COULDN'T FIND BAD BIT
  2000        ASL          SHIFT DUMY CRC
  2010        BCC .1
  2020        EOR ##$1021
  2030        BCS .1       ...ALWAYS
  2040 *--------------------------------
  2050 .2     TXA          BIT NUMBER
  2060        SEC
  2070        XCE
  2080        XBA
  2090        JMP PRNTAX
  2100 *--------------------------------

Correction to Fast Garbage Collector Bob Sander-Cederlof

In the March 1984 AAL, Paul Shetler gave us a very fast garbage collector for Applesoft. Last week Keith Satterley called from Australia, and mentioned he thought there was a bug in the handling of strings over 128 characters long. I looked into it, and he is right.

The bug is in the loop in lines 3240-3320, on page 9 of that issue. The loop moves a string from one place in memory to another. The way we printed the code, a string longer than 128 characters would only have one byte moved! Here is the old code and the correct code, side-by-side:

-----old code------------        -----correct code--------

3250    DEY                      3250 .3 DEY
3260 .3 LDA (FRESPC),Y           3260    LDA (FRESPC),Y
3270    STA (LOWTR),Y            3270    STA (LOWTR),Y
3280    DEY                      3280    TYA
3290    BPL .3                   3290    BNE .3
3300    BMI .1                   3300    BEQ .1

Can you see why the new code works and the old doesn't?

DOS Patch: Prevent Direct Commands Richard Gendron

I operate a AE/CATFUR line using my Apple and a modem in Montreal, Quebec. I have found that protecting your DOS from illegal entry can be a tough job to say the least.

In searching for ways to protect my system, I came across an interesting address in DOS: at $A026 there is some code which is executed whenever you try to type in a DOS command. The code checks to see if the command you typed is allowed as a direct command, and if not gives you the NOT DIRECT COMMAND message (or ERROR 15 if you are using DiversiDOS).

I have written a little patch that will catch you when you type a DOS command, and re-RUN the Applesoft program. If a sneaky caller finds a way to get out of the executing Applesoft program, at least he/she will be prevented from doing DOS commands.

Now every lock should have a key. You do want to be able to use your own DOS in direct mode, so I have included a way to turn off the protection. If you type "PRINT USR (0)" the system will respond with "PW:". Then enter a two-character password and the protection patch will be removed. Then you can CATALOG, DELETE, or whatever you want to do.

Since I use Diversi-DOS, and in both the 48K and 64K configurations, I set up my patching program so that it will work with both. The code which checks which version is loaded is in lines 1220-1260 and lines 1390-1410. If the output hook at $36,37 points up to $BDxx or higher, the 64K version must be running. Normal 48K DOS points to $9EBD.

These patches worked on my system, but yours may be a little different depending on which version of DOS you use. Examine carefully all the addresses I use inside DOS to see if yours is the same as mine before you try to use these patches.

  1010 *--------------------------------
  1040 *  (514) 738-1247 (AE/CAT-FUR)
  1050 *--------------------------------
  1060        .OR $300
  1070 *--------------------------------
  1080 INSTALL
  1090        LDA #$4C     BUILD "USR" VECTOR
  1100        STA $0A      "JMP" OPCODE
  1110        LDA #USR
  1120        STA $0B
  1130        LDA /USR
  1140        STA $0C
  1150 *---MOVE DATA INTO DOS-----------
  1170 ***    JMP PATCH.DOS
  1180 *--------------------------------
  1190 PATCH.DOS
  1200        LDA #$A026
  1210        STA $00
  1220        LDA $37      48K OR 64K DOS?
  1230        CMP #$BD     CARRY CLEAR IF 48K
  1240        LDA /$A026   ...48K
  1250        BCC .1       ...48K
  1260        LDA /$E026   ...64K
  1270 .1     STA $01
  1280        LDY #4       MOVE 5 BYTES
  1290 .2     LDA PATCHES,X
  1300        STA ($00),Y
  1310        DEX
  1320        DEY
  1330        BPL .2
  1340        RTS
  1350 *--------------------------------
  1360 REBOOT
  1370        SEI                  TURN OFF ANNOYING INTERRUPTS
  1380        JSR $03EA            RESET THE I/O HOOKS
  1390        LDA $37              LETS SEE WHICH DOS WE ARE USING
  1400        CMP #$BD             IS IT 64K DOS ?
  1410        BMI .1               SNIFF, NO IT IS NOT
  1420        BIT $C081            YES IT IS, SO TURN OFF THE LANGUAGE CARD
  1430        BIT $C081            TWICE, EVERYONE KNOWS WHY ?.
  1440        JSR $E316            DOS "CLOSE" ALL FILES
  1450        JMP $D566            NOW LET'S JUMP TO THE APPLESOFT "RUN"
  1460 .1     JSR $A316            DOS "CLOSE" ALL FILES (NO TROUBLE PLS)
  1470        JMP $A4FC            48K INTERNAL "DOS RUN"
  1480 *--------------------------------
  1490 USR
  1500        LDY #0               START OF LOOP COUNTER
  1520        BEQ .2               ...END OF STRING
  1530        JSR PRINT            NO, SO PRINT IT
  1540        INY                  INCREMENT THE LOOP
  1550        BNE .1               FOREVER LOOP (NEVER CAN EXIT)
  1560 *--------------------------------
  1570 .2     JSR INPUT            ALL TEXT PRINTED SO LET'S GET A KEY
  1580        CMP #"*              WAS IT A "*" ? (OR WHATEVER YOU WANT)
  1590        BNE .4               NO IT WAS NOT , SO BYE BYE
  1600        JSR INPUT            YES IT WAS, SO GET ANOTHER KEY
  1610        CMP #".              WAS IT A "." (OR WHATEVER YOU WANT)
  1620        BNE .4               NO IT WAS NOT, SO BYE BYE
  1630 *--------------------------------
  1640        LDX #P2-PATCHES
  1650        JSR PATCH.DOS
  1660 .4     RTS                  WE HAVE FINISHED
  1670 *--------------------------------
  1700 *        IN APPLESOFT
  1710 *--------------------------------
  1730        .AS -"PW:"
  1740        .HS 00
  1750 *--------------------------------
  1770 *--------------------------------
  1780 PRINT  JMP ($36)
  1790 INPUT  JMP ($38)
  1800 *--------------------------------
  1810 PATCHES
  1830        NOP          NEEDED FOR DIVERSI-DOS
  1840 P1     NOP
  1850 *--------------------------------
  1860        .HS A9023909    ORIGINAL CODE
  1870 P2     .HS A9
  1880 *--------------------------------
  9999        .LIF

Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $18 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $14 postage for other countries. Back issues are available for $1.80 each (other countries add $1 per back issue for postage).

All material herein is copyrighted by S-C SOFTWARE CORPORATION, all rights reserved. (Apple is a registered trademark of Apple Computer, Inc.)