Volume 3 -- Issue 5 | February 1983

In This Issue...

- Really Adding ASCII Strings
- More on the Macro-Videx Connection
- On CATALOG ARRANGER and RAM Card DOS
- Quickie No. 6 -- Endless Alarm
- Patch to Fix .TI Problem
- Apple //e Notes
- Macro Assembler Patch
- TRAPPER: An Applesoft INPUT Tuner
- Star-tling Stunts
- Promising New Book
- A Sometimes Useful Patch
- Source Code for a Word Processor

S-C Macro Assembler ///

The Apple /// version of the S-C Macro Assembler is coming right along! I am now selling a preliminary "as is" version for $100. That buys you the assembler, a few pages of documentation about the differences from the Apple ][ version, and free updates until the finished product appears. This is a working assembler for producing free-running programs; it assembles itself just fine. The biggest gap is the ability to produce relocatable modules for Pascal or BASIC. That will be added next. Call or write if you are interested in being among the first to have this new enhancement to the Apple ///.

Zero-Insertion-Force Game Socket Extender

One of the first things I did to my Apple back in 1977 was to plug a ZIF socket into the game connector. Not too easy, because it first has to be soldered to a header, but I did it.

Now I have discovered a source for a ready-made device that does the same thing, plus brings the socket outside the Apple (if you so desire). There's a picture of the device on page 14. For only $20 I'll send you one!

Really Adding ASCII Strings | Bob Sander-Cederlof

Last month I promised a "reasonably useful" program to add two numbers together from ASCII strings. I promised:

- Callable from Applesoft, using &.
- Automatic passing of string parameters.
- Allow operands of unequal length.
- Automatic alignment of decimal points.
- Allow negative numbers.
- Handle sums longer than operands.
- Allow leading blanks on operands.
- Allow operands and results up to 253 bytes long!

Okay! It took me three days, but I did it! Of course, the program has grown from 12 lines and 26 bytes of code to over 290 lines and over 450 bytes, too.

The program is now assembled to load at $9000, but you can choose other positions by changing line 1130. I set HIMEM:36864 before doing anything else in the Applesoft program, and then BRUN B.STRING ADDER.

When B.STRING ADDER is BRUN, only the setup code in lines 1160-1220 is executed. What this does is link in the ampersand (&) to the body of my program. Once the "&" is linked, my program responds to a call like "& +$,A$,B$,C$" by adding the numeric values represented in ASCII in A$ and B$ and storing the sum as a string in C$.

When an &-line occurs, Applesoft branches to my line 1520. Lines 1520-1600 check for the characters "+$," after the ampersand. If you don't like those characters, change them to something else. Anyway, if the characters do not match, you get SYNTAX ERROR. If they do match, it is time to collect the three strings variables.

Lines 1620-1690 collect the three string variables. The first two are the operands, the third is the result string. I save the address and length of the actual data of the operand strings. All I save at this point for the result string is the address of the variable descriptor. I call the subroutine PARSE.STRING.NAME to check for a leading comma, search for the variable name, and store the length and address of the referenced string data.

Lines 1730-1860 scan each operand string in turn to find the decimal point position. The routine SCAN divides a string at the decimal point (or where the decimal point would be if there was one), and returns in Y the number of characters to the left of the decimal point. SCAN returns in X the count of the number of characters on the right end, including the decimal point. I save the "digits.after" parts of both strings, and also the maxima of the two parts. The maxima describe the result string (almost).

Lines 1900-2000 finish the description of the result string, by lengthening the integral (left) side by two characters. These two characters allow for extension of the result by carry, and for representation of the sign of the result using ten's complement notation. At this point I also clear the necessary bytes of the result to zero, so the buffer can be used as an accumulator.

Now comes the EASY part. Lines 2040-2100 add each operand in turn to the buffer contents. EASY. Just call the subroutine ADD.TO.BUFFER, and it's done! Don't worry, I'll amplify later.

In ten's complement notation, if the first digit is 0-4 the number is positive; if the first digit is 5-9, the number is negative. For example, 1234 looks like 001234; -1234 becomes 998766. Ten's complement means in decimal the same thing two's complement means in binary. I can form the ten's complement by subtracting the number from a power of ten equal to the number of digits in the result. In that example, 1000000-1234=998766. Note that the ten's complement is equal to the nine's complement plus one. (Since 10=9+1.)

Lines 2140-2410 convert the buffer contents from the ten's complement numeric notation back to ASCII. Lines 2140-2180 set or clear the CARRY and TENS.FLAG sign bits according to the first digit in the buffer. A negative number, with a first digit of 5-9, causes both of these variables to get a value of the form 1xxxxxxx.

Lines 2190-2360 scan through the number from right to left, making the ten's complement if the number was negative, and converting each digit to ASCII. Lines 2370-2400 store a minus sign in the first digit position if the result is negative.

Line 2410 calls a subroutine to chop off leading zeros, and move the minus sign if there is one. You may justifiably ask, "Why did you call a subroutine rather than use in-line code?" Because when I wrote it in-line, the local labels stretched out too far from the major label STRADD and caused an assembly error. Also, sometimes I use subroutines for clarity, even when the subroutine is only called once.

The final step is to pack the resulting string up and ship it to the result string variable. Lines 2450-2590 do just that. AS.GETSPA makes room at the bottom of string pool space, and AS.MOVSTR copies the string data. C'est finis!

Lines 2640-3100 do the actual addition. On entry, X is either 0 or 4, selecting either the first or second operand. SETUP.OPERAND copies the string address into VARPNT, and retrieves the length of the string. Lines 2690-2760 set or clear the TENS.FLAG and CARRY variables according to the sign of the operand.

Lines 2780-2810 compute the position in the buffer at which the operand will be aligned properly. We saved the size of the integral (left) side of the buffer in MAX.DIGITS.BEFORE. That plus the lenght of the fractional side of the operand tells us where this operand aligns. Since we are using ten's complement for negative numbers, rather than nine's complement, we don't have to worry about extending the fractional parts to the same length. We can just start adding at the end of the current operand. (In ten's complement form fractional extensions are zeros; in nine's complement form, the extension digits would all be nines.)

Lines 2830-3100 do the addition. X points into the buffer, and Y points into the operand string. To start with, both X and Y point just past the end; therefore the loop BEGINS with a test-and-decrement sequence. I first t-a-d the buffer pointer; if it is zero, all is finished. If not, on to t-a-d the string pointer. If it is zero, there are still digits left in the buffer, so I use an assumed leading zero digit for the operand. We still may have carries to propagate across the rest of the sum.

Assuming neither pointer is zero, line 2900 gets the next digit from the operand string. If it is a decimal point, I just store the decimal point ASCII value into the buffer. If you want to be able to ignore leading blanks, insert the following two lines between line 2920 and 2930:

         2924       CMP #'          BLANK?
         2925       BEQ .3          YES, USE ZERO.

I left them out in my version, because I forgot I promised it to you.

If the character is not a decimal point (or blank), it may be a minus sign or digit. I did not put any error checking in my program for other extraneous characters; if you try them, you will get extraneous results! I treat a sign as a leading zero in the arithmetic loop.

If the character is a digit, or an assumed leading zero, we can add it to the buffer's value. Lines 2960-3010 will complement the digit if the operand had a minus sign. Lines 3020-3070 add the current operand digit (or its complement) to the current buffer digit, plus any carry hung over from the preceding digit, and save the resulting carry in CARRY.

That's it! Now here is a short little Applesoft program to test the code.

         100  REM TEST&+$,A$,B$
         110  HIMEM: 36864: PRINT  CHR$ (4)"BLOAD B.STRING ADDER":
              CALL 36864
         120  INPUT A$: INPUT B$
         130  &  + $,A$,B$,C$
         140  PRINT C$: GOTO 120

     1000  *SAVE S.SUPER STRING ADDER
     1010  *--------------------------------
     1020  *      STRING ADDITION:  & +$,A$,B$,C$
     1030  *--------------------------------
     1040  BUFFER              .EQ $200 - $2FF
     1050  AMPERSAND.VECTOR    .EQ $3F5 - $3F7
     1060  AS.CHRGET           .EQ $00B1
     1070  AS.SYNERR           .EQ $DEC9
     1080  AS.PTRGET           .EQ $DFE3
     1090  AS.CHKCOM           .EQ $DEBE
     1100  AS.GETSPA           .EQ $E452
     1110  AS.MOVSTR           .EQ $E5E2
     1120  *--------------------------------
     1130         .OR $9000
     1140         .TF B.STRING ADDER
     1150  *--------------------------------
     1160  SETUP  LDA #$4C     JMP OPCODE
     1170         STA AMPERSAND.VECTOR
     1180         LDA #STRADD
     1190         STA AMPERSAND.VECTOR+1
     1200         LDA /STRADD
     1210         STA AMPERSAND.VECTOR+2
     1220         RTS
     1230  *--------------------------------
     1240  FRESPC              .EQ $71,72
     1250  VARPNT              .EQ $83,84
     1260  *--------------------------------
     1270  *      TWO SIMILAR BLOCKS, FOR A$ AND B$
     1280  *      REFERENCED WITH X=0 OR X=4
     1290  *--------------------------------
     1300  A.LENGTH            .BS 1
     1310  A.ADDR              .BS 2
     1320  A.DIGITS.AFTER      .BS 1
     1330  *
     1340  B.LENGTH            .BS 1
     1350  B.ADDR              .BS 2
     1360  B.DIGITS.AFTER      .BS 1
     1370  *--------------------------------
     1380  *      A THIRD BLOCK, NEARLY THE SAME AS ABOVE,
     1390  *      FOR C$:  REFERENCED WITH X=8
     1400  *--------------------------------
     1410  C.LENGTH            .BS 1
     1420  C.STRING            .BS 2
     1430  *--------------------------------
     1440  CARRY               .BS 1
     1450  TENS.FLAG           .BS 1
     1460  C.ADDR              .BS 2
     1470  MAX.DIGITS.BEFORE   .BS 1
     1480  MAX.DIGITS.AFTER    .BS 1
     1490  *--------------------------------
     1500  *      & BRANCHES HERE
     1510  *--------------------------------
     1520  STRADD CMP #$C8     CHECK FOR "+$,"
     1530         BNE .1
     1540         JSR AS.CHRGET
     1550         CMP #'$
     1560         BNE .1
     1570         JSR AS.CHRGET
     1580         CMP #',
     1590         BEQ .2
     1600  .1     JMP AS.SYNERR
     1610  *--------------------------------
     1620  .2     LDX #0       POINT AT A$ DATA
     1630         JSR PARSE.STRING.NAME   FIRST OPERAND
     1640         LDX #4       POINT AT B$ DATA
     1650         JSR PARSE.STRING.NAME   SECOND OPERAND
     1660         JSR AS.CHKCOM           RESULT STRING
     1670         JSR AS.PTRGET
     1680         STY C.STRING+1          ADDRESS OF VARIABLE
     1690         STA C.STRING
     1700  *--------------------------------
     1710  *      SCAN BOTH STRINGS TO DETERMINE BUFFER PARAMETERS
     1720  *--------------------------------
     1730         LDX #0       POINT AT A$ DATA
     1740         JSR SCAN     GET Y=LEFT LENGTH, X=RIGHT LENGTH
     1750         STX A.DIGITS.AFTER
     1760         STX MAX.DIGITS.AFTER
     1770         STY MAX.DIGITS.BEFORE
     1780         LDX #4       POINT AT B$ DATA
     1790         JSR SCAN     GET Y=LEFT LENGTH, X=RIGHT LENGTH
     1800         STX B.DIGITS.AFTER
     1810         CPX MAX.DIGITS.AFTER
     1820         BCC .3
     1830         STX MAX.DIGITS.AFTER
     1840  .3     CPY MAX.DIGITS.BEFORE
     1850         BCC .4
     1860         STY MAX.DIGITS.BEFORE
     1870  *--------------------------------
     1880  *      CLEAR THAT MUCH OF THE BUFFER
     1890  *--------------------------------
     1900  .4     INC MAX.DIGITS.BEFORE  TWO MORE CHARS FOR
     1910         INC MAX.DIGITS.BEFORE    SIGN AND CARRY
     1920         CLC
     1930         LDA MAX.DIGITS.BEFORE  TOTAL LENGTH OF RESULT
     1940         ADC MAX.DIGITS.AFTER
     1950         STA C.LENGTH
     1960         TAY
     1970         LDA #0       ZERO THE BUFFER FOR USE AS AN
     1980  .5     STA BUFFER-1,Y    ACCUMULATOR
     1990         DEY
     2000         BNE .5
     2010  *--------------------------------
     2020  *      ADD A$ TO BUFFER
     2030  *--------------------------------
     2040         LDX #0       POINT AT A$ DATA
     2050         JSR ADD.TO.BUFFER
     2060  *--------------------------------
     2070  *      ADD B$ TO BUFFER
     2080  *--------------------------------
     2090         LDX #4       POINT AT B$ DATA
     2100         JSR ADD.TO.BUFFER
     2110  *--------------------------------
     2120  *      CONVERT BUFFER TO ASCII AGAIN
     2130  *--------------------------------
     2140         LDA BUFFER   SEE IF NUMBER IS NEGATIVE
     2150         CMP #5       SET CARRY IF NEGATIVE, ELSE CLEAR
     2160         ROR          MAKE A=0XXXXXXX OR 1XXXXXXX
     2170         STA CARRY        TO SET OR CLEAR THESE FLAGS
     2180         STA TENS.FLAG    APPROPRIATELY
     2190         LDX C.LENGTH
     2200         BEQ .10      FINISHED
     2210  .6     LDA BUFFER-1,X
     2220         CMP #'.
     2230         BEQ .9
     2240         BIT TENS.FLAG
     2250         BPL .8
     2260         ASL CARRY
     2270         LDA #10
     2280         SBC BUFFER-1,X
     2290         CMP #10
     2300         BCC .7
     2310         SBC #10
     2320  .7     ROR CARRY
     2330  .8     ORA #'0
     2340  .9     STA BUFFER-1,X
     2350         DEX
     2360         BNE .6
     2370  .10    BIT TENS.FLAG   SEE ABOUT FINAL SIGN
     2380         BPL .11         VALUE IS POSITIVE
     2390         LDA #'-         NEGATIVE, SO STUFF "-"
     2400         STA BUFFER        IN FRONT OF BUFFER
     2410  .11    JSR CHOP.OFF.LEADING.ZEROES
     2420  *--------------------------------
     2430  *      PUT (BUFFER) IN OUTPUT STRING
     2440  *--------------------------------
     2450         LDX #8       POINT AT C$ DATA
     2460         JSR SETUP.OPERAND
     2470         JSR AS.GETSPA
     2480         LDY #0
     2490         STA (VARPNT),Y
     2500         INY
     2510         LDA FRESPC
     2520         STA (VARPNT),Y
     2530         INY
     2540         LDA FRESPC+1
     2550         STA (VARPNT),Y
     2560         LDY C.ADDR+1
     2570         LDX C.ADDR
     2580         LDA C.LENGTH
     2590         JMP AS.MOVSTR
     2600  *--------------------------------
     2610  *      ADD STRING TO BUFFER
     2620  *      ENTER WITH X=0 FOR A$, X=4 FOR B$
     2630  *--------------------------------
     2640  ADD.TO.BUFFER
     2650         JSR SETUP.OPERAND
     2660         TAY          STRING LENGTH
     2670         LDA A.DIGITS.AFTER,X
     2680         PHA
     2690         LDX #0
     2700         LDA (VARPNT,X)  CHECK FOR MINUS SIGN
     2710         CMP #'-
     2720         BEQ .1       YES, CARRY SET
     2730         CLC          ELSE CLEAR CARRY
     2740  .1     ROR          MAKE A=0XXXXXXX OR 1XXXXXXX
     2750         STA TENS.FLAG  MAKE FLAGS<0 IF MINUS
     2760         STA CARRY
     2770  *--------------------------------
     2780         CLC          POINT INTO BUFFER WHERE OPERAND
     2790         PLA               ALIGNS
     2800         ADC MAX.DIGITS.BEFORE
     2810         TAX
     2820  *--------------------------------
     2830  .2     TXA          TEST X FOR BEGINNING OF BUFFER
     2840         BEQ .8       YES, FINISHED!
     2850         DEX          NO, BACK ANOTHER ONE
     2860         TYA          CHECK OPERAND POINTER
     2870         BEQ .3       END OF OPERAND, BUT WE
     2880  *                          STILL NEED TO FINISH CARRIES
     2890         DEY          BACK UP IN OPERAND
     2900         LDA (VARPNT),Y  NEXT CHAR FROM OPERAND
     2910         CMP #'.      DECIMAL POINT?
     2920         BEQ .7       YES, SKIP OVER IT
     2930         CMP #'-      MINUS SIGN?
     2940         BNE .4       NO, MUST BE DIGIT
     2950  .3     LDA #'0      ASCII ZERO THEN
     2960  .4     AND #$0F     CONVERT ASCII TO BINARY
     2970         BIT TENS.FLAG
     2980         BPL .5       NOT 9'S COMPLEMENTING
     2990         EOR #$FF
     3000         CLC
     3010         ADC #10      FORM 9'S COMPLEMENT
     3020  .5     ASL CARRY    GET PREVIOUS CARRY INTO C-BIT
     3030         ADC BUFFER,X
     3040         CMP #10      SEE IF CARRY
     3050         BCC .6       NO
     3060         SBC #10      YES, BACK THIS DIGIT DOWN
     3070  .6     ROR CARRY    SAVE CARRY FOR NEXT LOOP
     3080  .7     STA BUFFER,X
     3090         JMP .2
     3100  .8     RTS
     3110  *--------------------------------
     3120  *      SCAN STRING
     3130  *      ENTER WITH X=0 FOR A$, X=4 FOR B$
     3140  *      RETURN WITH X = # DIGITS AFTER DECIMAL POINT
     3150  *                        (COUNTING THE DECIMAL POINT)
     3160  *                  Y = # DIGITS BEFORE DECIMAL POINT
     3170  *                        (COUNTING SIGN IF ANY)
     3180  *--------------------------------
     3190  SCAN
     3200         JSR SETUP.OPERAND
     3210         LDY #0
     3220         TAX
     3230         BEQ .2       NULL STRING
     3240  .1     LDA (VARPNT),Y
     3250         CMP #'.      LOOKING FOR DECIMAL POINT
     3260         BEQ .2
     3270         INY
     3280         DEX
     3290         BNE .1
     3300  .2     RTS
     3310  *--------------------------------
     3320  *      CHOP OFF LEADING ZEROES
     3330  *--------------------------------
     3340  CHOP.OFF.LEADING.ZEROES
     3350         LDY #1       FIND FIRST NON-ZERO POSITION
     3360  .1     LDA BUFFER,Y
     3370         CMP #'0
     3380         BNE .2
     3390         INY
     3400         CPY MAX.DIGITS.BEFORE
     3410         BCC .1
     3420         DEY
     3430  .2     LDA BUFFER   SIGN, MAYBE
     3440         CMP #'-
     3450         BNE .3
     3460         DEY
     3470         STA BUFFER,Y
     3480  .3     CLC
     3490         TYA
     3500         ADC #BUFFER
     3510         STA C.ADDR
     3520         LDA #0
     3530         ADC /BUFFER
     3540         STA C.ADDR+1
     3550         SEC
     3560         TYA
     3570         EOR #$FF
     3580         ADC C.LENGTH
     3590         STA C.LENGTH
     3600         RTS
     3610  *--------------------------------
     3620  *      PARSE STRING NAME, SET UP POINTER
     3630  *--------------------------------
     3640  PARSE.STRING.NAME
     3650         TXA
     3660         PHA
     3670         JSR AS.CHKCOM
     3680         JSR AS.PTRGET     GET SECOND STRING PNTR
     3690         PLA
     3700         TAX
     3710         LDY #0
     3720         LDA (VARPNT),Y    GET LENGTH
     3730         STA A.LENGTH,X
     3740         INY
     3750         LDA (VARPNT),Y    GET ADDRESS OF DATA
     3760         STA A.ADDR,X
     3770         INY
     3780         LDA (VARPNT),Y
     3790         STA A.ADDR+1,X
     3800         RTS
     3810  *--------------------------------
     3820  *      LOAD ADDRESS INTO VARPNT
     3830  *      X=0 FOR A$, X=4 FOR B$
     3840  *--------------------------------
     3850  SETUP.OPERAND
     3860         LDA A.ADDR,X
     3870         STA VARPNT
     3880         LDA A.ADDR+1,X
     3890         STA VARPNT+1
     3900         LDA A.LENGTH,X
     3910         RTS
     3920  *--------------------------------

More on the Macro-Videx Connection | Bill Linn

Don Taylor's original article in the August (1982) issue of AAL and Mike Laumer's follow-up the next month gave us the patches for running the S-C Macro Assembler in conjunction with the Videx 80-column board. I recently purchased a Videx card in order to implement the 80-column version of ES-CAPE, so I installed the patches.

I have really enjoyed using the Macro assembler in 80-column mode. Naturally, though, I couldn't resist adding a few enhancements to Don's and Mike's work.

Mike added the right arrow code, which copies characters off the Videx screen, but he stopped short of implementing the Escape-L LOAD sequence. To install the following code, you will need to change line 3080 in Don's article to point to my routine. Change it to "3080 .DA MY.ESC.L-1". Also, the STX instruction at line 4235 in Mike's article must be labelled GETCH.

         *--------------------------------------
         SCM.INSTALL .EQ SCM.BASE+$52A
         *
         MY.ESC.L
                CPX #0          CURSOR AT BEGINNING?
                BEQ .1          YES, CONTINUE
                JMP SCM.ESC.L   NO, LET S-C HANDLE IT
         .1     LDA #0          CONNECT DOS
                STA $AA52       BY SETTING INTERCEPT STATE = 0
                LDA #$84        SEND A CTRL-D
                JSR MON.COUT
         .2     LDA LOADCMD,X
                JSR SCM.INSTALL
                JSR FAKE.COUT
                CPX #6
                BCC .2
         .3     STX $406        SAVE CHAR POS'N
                JSR GETCH       GET SCREEN CHAR
                LDX $406        RESTORE POS'N
                JSR SCM.INSTALL
                JSR FAKE.COUT
                CPX #40         40 CHARS SENT YET?
                BNE .3          NO, LOOP BACK
                JMP CLREOP      CLEAR TO END OF PAGE
         *                      AND EXIT
         *
         LOADCMD    .AS -/LOAD  /
         *--------------------------------------

Secondly, I wanted a longer "*---" line on my screen, so I changed it to 68 characters instead of 38. This uses more of the 80-column screen, without wrapping around during assembly. To make this modification insert the following two lines after the label "INSTALL.PATCHES" in Don's original listing:

         LDA #68
         STA SCM.BASE+$494

Finally, I changed the dimensions of the Videx cursor so that it looks like a blinking underline instead of a blinking block. (Users of my ES-CAPE are already familiar with my love for the blinking underline!) Insert the following lines immediately after the "INSTALL.VECTORS" label:

         LDA #$0A       VIDEX REGISTER 10
         STA V.DEV0
         LDA #$68
         STA V.DEV0+1
         LDA #$0B       VIDEX REGISTER 11
         STA V.DEV0
         LDA #$08
         STA V.DEV0+1

Speaking of ES-CAPE, I am making progress on Version 2 and have included suggestions from many of you. If you have others, please drop me a line soon at 3199 Hammock Creek, Lithonia, GA 30058, or call evenings at (404) 483-7637.

On CATALOG ARRANGER and RAM Card DOS | Chuck Welman

Chuck Welman just called to report some errors in the January piece on using CATALOG ARRANGER with a relocated DOS. He says that the sentence about where to put the BIT MONREAD statements had problems. Here's his corrected version:

"Then add BIT MONREAD at these positions: Lines 1675, 3775, 3895, 3955, 4015 (".5" moved to this line), 4205 (".3" moved to this line, 4315, 4425, 4455 (".7" moved to this line), and 4895."

Chuck also passed along instructions for using FILENAME EDITOR with a RAM Card DOS. Here are his additions:

         2635 .3     BIT MONREAD
         2640        JSR MON.BELL
         2642        BIT DOSREAD
         2644        BIT DOSREAD
         2646        RTS

Thanks to all of you for showing your appreciation for these programs.

Quickie No. 6 | Bob Sander-Cederlof

Here is a little run-anywhere program sure to wake up the neighborhood dogs. Put it in your program as a last resort to get attention, because the only escape is by RESET or power-off.

         1000 ALARM  INY       INCREMENT DELAY TIME
         1010        TYA
         1020        TAX       DELAY COUNT TO X
         1030        LDA $C030 TOGGLE SPEAKER
         1040 .1     DEX       DELAY LOOP
         1050        BNE .1
         1060        BEQ ALARM ....FOREVER....

That's it, only eleven bytes! For a slightly different effect, change the "DEX" in line 1030 to "INX".

Patch to Fix .TI Problem | Mike Laumer

You may have noticed the annoying problem with the .TI directive, in which there is sometimes a blank line after the title line and sometimes not. The blank line is there when the page break is forced with a .PG directive, but not when it is caused by merely filling a page.

The following little patch will fix it. I haven't put a definite address on the patch, because I don't know what other patches you may already have appended to the assembler. Just find an empty place and plop it in!

Motherboard version: :$21F0:4C xx yy (was 20 CF 2C) :$yyxx:20 CF 2C 4C E3 21

RAM Card version: :$E33C:4C xx yy (was 20 1B EE) :$yyxx:20 1B EE 4C 2F E3

Another .TI problem of which I am aware is that the line count is messed up on the first page of the symbol table listing. This is caused by the fact that the extra carriage returns in the "SYMBOL TABLE" message are not counted. You can clean up the appearance by making the last line of your source program be ".PG"; this forces the symbol table to start on a fresh page.

Apple //e Notes | Bob Sander-Cederlof

We don't have one yet, but we did play with one for about an hour last week. All our software works fine, as long as you stay in the 40-column caps-lock mode. We will be making new versions available in the near future which take full advantage of the extended memory, lower-case, and 80-column display.

The best write-up I have seen yet on the //e is in the February 1983 Apple Orchard (published by the International Apple Core, 908 George St., Santa Clara, CA 95050).

Here are some of the things that caught my attention:

* Real shift key, and a caps-lock key.

* Open-Apple and Closed-Apple keys, which duplicate the first two paddle buttons.

* Recessed RESET key. CTRL-RESET required (no longer a switchable option). CTRL-Closed-Apple-RESET starts a memory test program.

* Two 8K ROMs, instead of six 2K ROMs. The extra 2K of ROM space is used by the modified Monitor program. Fancy soft-switches map the extra 2K into the $C000-C7FF space. These sockets are supposedly compatible with 2764 EPROMs.

* Apparently the Monitor now uses (clobbers) zero-page locations $08 and $1F.

* Up- and down-arrows on the keyboard. Down is CTRL-J, or linefeed. Up is CTRL-K.

* The keyboard includes all the ASCII set, even $7F (DELETE, or RUBOUT).

* 64K RAM on the motherboard. This simulates an Apple II Plus with a 16K RAM card in slot 0.

* New slot instead of slot 0, with 60-pin connector (other slots still have 50-pin connectors). Apple's 80-column card plugs in here. The extra pins carry other signals not normally available at the slots. Look for some amazing new combined function cards from the peripheral-card makers for this slot! I wouldn't be surprised to find ads real soon for 256K RAM cards including 80-column support, clock- calendar, serial/parallel interfaces, and all on one card.

* 80-column card with or without extra 64K RAM. But this 64K RAM is soft-switched in a totally different manner. It maps over the same space as the motherboard 64K, with switches to map portions such as page-zero, text screen, hi-res screen, and so on.

* Now you can READ the state of most of the soft-switches. Bit 7 (high bit) tells the state, as follows:

           $C013 -- RAMREAD
           $C014 -- RAMWRT
           $C015 -- SLOTCXROM/CX00ROM
           $C016 -- ALTZP/MAIN
           $C017 -- SLOTC3ROM/SLOTROM
           $C018 -- 80 COL STORE
           $C019 -- VERTICAL BLANKING
           $C01A -- TEXT
           $C01B -- MIXED MODE
           $C01C -- PAGE2
           $C01D -- HIRES
           $C01E -- ALTCHAR
           $C01F -- 80 COL DISP

* Yes, you saw right...the vertical blanking signal is now readable! So lovers of Lancaster's Enhancements can continue to tinker!

* Inverse lower-case display is selectable, at the expense of the flashing mode.

* The cursor display is different. A small checkerboard alternates with the character under the cursor in 40-column mode. In 80-column mode an inverse blank is the normal cursor, and an inverse "+" is used when in escape-mode.

Whether we view the changes as improvements or not, the //e will very soon be the standard we all have to deal with. The same situation arose when Apple switched from II to II Plus. A year from now, when 300,000 have been sold, we will wonder how we ever lived without it!

Macro Assembler Patch | Bob Sander-Cederlof

Peter Bartlett, of Chicago, has reported an unpublished limit on the number of Target Files that can be generated by one assembly. Right now there can only be 31; above that number the load address and length bytes go astray. If you need more than 31 files from one assembly, you can make the following patches:

    Regular version

              :$29EA:3F

    Language Card version

              :$C083 C083 EB36:3F N C080

These patches will allow you to have up to 63 target files. That should be plenty!

TRAPPER: An Applesoft Input Tuner | Allen Marsalis

How would you like a radio which played every available station at one time? Well that's how I sometimes feel about using Applesoft's INPUT statement. I want to be able to "tune in" on the character(s) of the input stream, in much the same way as a radio tunes into a station. Applesoft's INPUT statement, however, accepts all characters typed into the keyboard and allows up to 255 of them. This means that I have to do a lot of checking and monitoring of string lengths and characters to avoid input errors.

For example, when answering a Y or N question, what happens when the user inputs "WXYZ"? Provisions are needed within the program to guard against such errors. This can be very inconvenient and space-consuming, yet it is essential for good programming.

A better example occurs when you are creating a disk file. Field lengths and data types are often restricted, such as in a name, address, or social security number. A SSN, for instance, has a fixed length and must be constructed of numbers only. Checking a field such as this can be very time consuming and lengthy. In fact, it seems that a quarter of the contents of my Applesoft programs does nothing but check on field lengths, option boundaries, and other input checks.

So, I set out to create an input routine which would allow Applesoft to "tune" into the characters specified and also monitor the field length. I've seen several input routines such as this on larger systems, but all had one disadvantage: Only a fixed number of options were available, such as alpha only, numeric only, and (Y or N) input. More options available meant more parameters were necessary, making the systems more cumbersome to work with. After much thought I decided on a totally new approach which would allow almost limitless control of input. I christened this routine TRAPPER for "Tuning and Regulating APPlesoft Entries by Restriction."

TRAPPER employs a coded restriction string (not unlike Applesoft's IF expression) to tune out the characters I don't want to accept. TRAPPER is then, in essence, a tiny interactive interpreter that provides a short, convenient method of filtering out any unwanted characters in the input. Here's how it works.

TRAPPER uses three parameters as follows:

Syntax: & INPUT (A, B$, C$)

         A:  Input field length (real expression)
        B$:  Coded restriction string (string expression)
             includes:  > < = ' AND OR NOT <sp> <single char>
        C$:  Input string (string variable)
             variable to receive input

As I have said, the restriction string is a simple relational expression as is used by Applesoft's IF statement. It is constructed of the following special characters and rules:

1. < > = are its relational operators
2. AND OR NOT are its logical operators
3. Blanks are allowed anywhere within the expression, but lengthy expressions increase the delay between keystrokes.
4. One and only one character is allowed within single quotes.
5. <cr> and <-- have special functions and cannot be trapped.
6. Parentheses are not yet implemented.

EXAMPLES:

         YN$ = " ='Y' OR ='N' "            :REM (Y OR N) ONLY
         NOSP$ = " NOT =' ' "              :REM NO SPACES ALLOWED
         MENU$ = " NOT <'1' AND NOT >'4' " :REM ALLOWS 1 THRU 4
         WAITCR$ = ""                      :REM WAIT FOR A <CR>

After using Trapper awhile, I noticed a significant reduction in the size of my Applesoft programs, with even better error trapping than ever before possible. And it doesn't print that leading question mark which I never did like (not all input prompts are questions.)

For a 48K Apple, DOS sets HIMEM at $9600. Trapper resides just below this at $9300 and moves HIMEM down to that point.

     1000  *SAVE S.TRAPPER
     1010  *--------------------------------
     1020  *      TRAPPER, BY ALLEN MARSALIS
     1030  *--------------------------------
     1040         .OR $9300
     1050         .TF B.TRAPPER
     1060  *--------------------------------
     1070  RLEN   .EQ $1A      RESTRICTION STRING
     1080  RSTR   .EQ $1B      DESCRIPTOR
     1090  TEMPPT .EQ $52
     1100  LASTPT .EQ $53
     1110  FRESPC .EQ $71,72
     1120  HIMEM  .EQ $73,74
     1130  VARPNT .EQ $83,84
     1140  FACMO  .EQ $A0
     1150  *--------------------------------
     1160  BUF    .EQ $200     INPUT BUFFER
     1170  AMPVEC .EQ $3F5     AMPERSAND VECTOR
     1180  STROBE .EQ $C010    KEYBOARD STROBE
     1190  *--------------------------------
     1200  AS.FRMNUM  .EQ $DD67     EVALUATE NUMERIC FORMULA
     1210  AS.CHKSTR  .EQ $DD6C     REQUIRE STRING
     1220  AS.FRMEVL  .EQ $DD7B     EVALUATE GENERAL FORMULA
     1230  AS.CHKCLS  .EQ $DEB8     REQUIRE ")"
     1240  AS.CHKCOM  .EQ $DEBE     REQUIRE ","
     1250  AS.CHKOPN  .EQ $DEBB     REQUIRE "("
     1260  AS.SYNCHR  .EQ $DEC0     REQUIRE (A-REG)
     1270  AS.SYNERR  .EQ $DEC9     SYNTAX ERROR
     1280  AS.PTRGET  .EQ $DFE3     GET VARIABLE PNTR
     1290  AS.GETSPA  .EQ $E452     GET SPACE IN STRING AREA
     1300  AS.MOVSTR  .EQ $E5E2     COPY STRING DATA
     1310  AS.FRETMP  .EQ $E604     FREE TEMPORARY STRING
     1320  AS.CONINT  .EQ $E6FB     CONVERT FAC TO 8-BITS
     1330  *--------------------------------
     1340  MON.CLREOL .EQ $FC9C     CLEAR TO END-OF-LINE
     1350  MON.RDKEY  .EQ $FD0C     READ A KEY
     1360  MON.COUT   .EQ $FDED     DISPLAY A CHARACTER
     1370  *--------------------------------
     1380  SETUP  LDA #$4C     "JMP" OPCODE
     1390         STA AMPVEC
     1400         LDA #TRAPPER
     1410         STA AMPVEC+1
     1420         LDA /TRAPPER
     1430         STA AMPVEC+2
     1440         LDA #SETUP   SET HIMEM UNDER TRAPPER
     1450         STA HIMEM
     1460         LDA /SETUP
     1470         STA HIMEM+1
     1480         RTS
     1490  *--------------------------------
     1500  *      AMPERSAND COMES HERE
     1510  *--------------------------------
     1520  TRAPPER
     1530         LDA #$84          "INPUT" TOKEN
     1540         JSR AS.SYNCHR
     1550         JSR AS.CHKOPN     "& INPUT ("
     1560         JSR AS.FRMNUM     READ FIELD LENGTH PARAMETER
     1570         JSR AS.CONINT     CONVERT TO 8-BIT VALUE
     1580         STX FL            SAVE FIELD LENGTH
     1590         JSR AS.CHKCOM     ","
     1600         JSR AS.FRMEVL     GET RESTRICTION STRING
     1610         JSR AS.CHKSTR
     1620         JSR AS.CHKCOM     ANOTHER ","
     1630         LDY #2            SAVE DESCRIPTOR
     1640  .1     LDA (FACMO),Y
     1650         STA RLEN,Y
     1660         DEY
     1670         BPL .1
     1680         LDA TEMPPT        DID FRMEVL MAKE A TEMP STRING?
     1690         CMP #$56
     1700         BCC .2            NO
     1710         LDA LASTPT        YES, SO FREE THE TEMP
     1720         LDY #0
     1730         JSR AS.FRETMP
     1740  .2     LDA #0            INIT BUFFER INDEX
     1750         STA BINDEX
     1760  *---UNDERSCORE INPUT FIELD-------
     1770         LDA #$DF          UNDERLINE CHAR
     1780         JSR PRINT.FIELD
     1790         LDA #$88          BACKSPACE  TO BEGINNING AGAIN
     1800         JSR PRINT.FIELD
     1810  *---READ A KEY-------------------
     1820         BIT STROBE        DON'T ALLOW TYPE AHEAD
     1830  .3     JSR MON.RDKEY     READ NEXT KEY
     1840         AND #$7F          INTERNAL FORM
     1850         STA KEY           SAVE IT
     1860  *---BACKSPACE--------------------
     1870         CMP #$08          BACKSPACE?
     1880         BNE .22           NO
     1890         LDA BINDEX        IGNORE AT BEGINNING OF LINE
     1900         BEQ .21
     1910         LDA #$88          YES, ECHO IT
     1920         JSR MON.COUT
     1930         LDA #$DF          REPLACE UNDERLINE
     1940         JSR MON.COUT
     1950         LDA #$88          BACKSPACE AGAIN
     1960         JSR MON.COUT
     1970         DEC BINDEX        BACK UP BUFFER TOO
     1980  .21    JMP .3
     1990  *---CARRIAGE RETURN--------------
     2000  .22    CMP #$0D          RETURN?
     2010         BNE .23           NO
     2020         JSR MON.CLREOL
     2030         JSR AS.PTRGET     GET DESTINATION STRING
     2040         JSR AS.CHKCLS     MUST HAVE ")" AT END
     2050         LDA BINDEX        LENGTH OF INPUT LINE
     2060         JSR AS.GETSPA     FIND ROOM FOR IT
     2070         LDY #0            MOVE IN DESCRIPTOR
     2080         STA (VARPNT),Y
     2090         INY
     2100         LDA FRESPC
     2110         STA (VARPNT),Y
     2120         INY
     2130         LDA FRESPC+1
     2140         STA (VARPNT),Y
     2150         LDY /BUF          COPY DATA INTO STRING
     2160         LDX #BUF
     2170         LDA BINDEX
     2180         JMP AS.MOVSTR     ...AND RETURN
     2190  *---CHECK IF VALID KEY-----------
     2200  .23    JSR CHECK.RESTRICTIONS
     2210  *---CHECK VALIDITY AND ECHO------
     2220         LDA KEY           GET KEY AGAIN
     2230         LDA BINDEX
     2240         CMP FL
     2250         BCS .27           TOO FAR, ABORT KEY
     2260         LDA NEW           IF NEW = FAIL, ABORT KEY
     2270         BEQ .27           YES, ABORT KEY
     2280         LDA KEY
     2290         LDY BINDEX
     2300         STA BUF,Y         PUT KEY INTO BUFFER
     2310         INC BINDEX
     2320         CMP #$20          IF KEY WAS CONTROL-KEY,
     2330         BCS .26              THEN PRINT SPACE
     2340         LDA #$20
     2350  .26    ORA #$80
     2360         JSR MON.COUT      ECHO
     2370         JMP .3            NEXT KEY
     2380  .27    LDA #$07          RING BELL
     2390         BNE .26
     2400  *--------------------------------
     2410  CHECK.RESTRICTIONS
     2420         LDA #0
     2430         STA RINDEX        RINDEX = 0
     2440         STA NEW           NEW = FAIL
     2450         STA ANDOR         ANDOR = OR
     2460         STA NOT           NOT = FALSE
     2470  *---FETCH OPERATOR---------------
     2480  .4     LDY RINDEX        IF RINDEX >= RLEN,
     2490         CPY RLEN              THEN QUIT SCAN
     2500         BCC .5            NOT YET
     2510         RTS
     2520  .5     LDA (RSTR),Y      FETCH OPERATOR
     2530         INC RINDEX
     2540  *---DETERMINE OPERATION----------
     2550         CMP #'            IGNORE BLANKS
     2560         BEQ .4
     2570         CMP #'<           < = >, THEN FETCH OPERAND
     2580         BEQ .10
     2590         CMP #'>
     2600         BEQ .10
     2610         CMP #'=
     2620         BEQ .10
     2630         CMP #'A           "AND"
     2640         BEQ .7
     2650         CMP #'O
     2660         BEQ .8
     2670         CMP #'N           "NOT"
     2680         BEQ .9
     2690         JMP AS.SYNERR
     2700  *---AND OPERATOR-----------------
     2710  .7     LDA #'N
     2720         JSR SYNSTR
     2730         LDA #'D
     2740         JSR SYNSTR
     2750         LDA #1            SET AND OPERATOR
     2760         STA ANDOR
     2770         BNE .4            ...ALWAYS
     2780  *---OR OPERATOR------------------
     2790  .8     LDA #'R
     2800         JSR SYNSTR
     2810         LDA #0            SET OR OPERATOR
     2820         STA ANDOR
     2830         BEQ .4            ...ALWAYS
     2840  *---NOT OPERATOR-----------------
     2850  .9     LDA #'O
     2860         JSR SYNSTR
     2870         LDA #'T
     2880         JSR SYNSTR
     2890         LDA #1            SET NOT OPERATOR "TRUE"
     2900         STA NOT
     2910         BNE .4            ...ALWAYS
     2920  *---FETCH OPERAND----------------
     2930  .10    STA ROPR
     2940         LDA #$27          CHECK FOR APOSTROPHE
     2950         JSR SYNSTR
     2960         LDY RINDEX
     2970         LDA (RSTR),Y      GET OPERAND
     2980         STA ROPD
     2990         INC RINDEX
     3000         LDA #$27          ANOTHER APOSTROPHE
     3010         JSR SYNSTR
     3020  *---EVALUATE RELATIONAL OPERATION
     3030         LDA NEW
     3040         STA LAST          LAST = NEW
     3050         LDA #0            NEW = FAIL
     3060         STA NEW
     3070         LDY ROPR          OPERATOR
     3080         LDA KEY           LATEST KEY
     3090         CMP ROPD          COMPARE TO OPERAND
     3100         BEQ .11           THEY ARE EQUAL
     3110         BCC .12           KEY < OPERAND
     3120         CPY #'>           KEY > OPERAND
     3130         BEQ .13           SUCCESS!
     3140         BNE .14           FAIL.
     3150  .11    CPY #'=
     3160         BEQ .13           SUCCESS
     3170         BNE .14           FAIL
     3180  .12    CPY #'<
     3190         BNE .14           FAIL
     3200  .13    LDA #1            FLAG SUCCESS
     3210         STA NEW
     3220  *---PERFORM NOT OPERATION--------
     3230  .14    LDA NOT           IF NOT, TOGGLE NEW
     3240         BEQ .17           NOT NOT
     3250         LDA NEW
     3260         EOR #1
     3270         STA NEW
     3280         LDA #0            CLEAR NOT
     3290         STA NOT
     3300  *---PERFORM AND/OR OPERATION-----
     3310  .17    LDA LAST
     3320         LDY ANDOR
     3330         BEQ .18           OR
     3340         AND NEW           AND
     3350         STA NEW
     3360         JMP .4
     3370  .18    ORA NEW
     3380         STA NEW
     3390         JMP .4
     3400  *--------------------------------
     3410  SYNSTR STA HOLD     SAVE CHAR
     3420  .1     LDY RINDEX
     3430         LDA (RSTR),Y
     3440         INC RINDEX
     3450         CMP #'            IGNORE BLANKS
     3460         BEQ .1
     3470         CMP HOLD
     3480         BEQ .2
     3490         JMP AS.SYNERR
     3500  .2     RTS
     3510  *--------------------------------
     3520  PRINT.FIELD
     3530         LDY FL
     3540  .1     JSR MON.COUT
     3550         DEY
     3560         BNE .1
     3570         RTS
     3580  *--------------------------------
     3590  HOLD   .BS 1
     3600  NOT    .BS 1
     3610  ANDOR  .BS 1
     3620  FL     .BS 1
     3630  NEW    .BS 1
     3640  LAST   .BS 1
     3650  KEY    .BS 1
     3660  BINDEX .BS 1
     3670  RINDEX .BS 1
     3680  ROPR   .BS 1
     3690  ROPD   .BS 1
     3700  *--------------------------------

Star-tling Stunts | Bill Morgan & Mike Laumer

In most assemblers, including the S-C Macro Assembler, you can use the character "*" in the operand of an instruction to mean the current value of the location counter. (The location counter is a variable used by the assembler to keep track of where the next byte of object code goes.) Here are a couple of simple examples of using the *, from page 6-2 of the Macro Assembler manual:

              0800- 03       1000 QT     .DA #QTSZ
              0801- 41 42 43 1010        .AS /ABC/
              0003-          1020 QTSZ   .EQ *-QT-1
                             1030
              0804- 00 00    1040 VAR    .DA *-*
                             1050
              0806-          1060 FILLER .BS $900-*
              0900-          1070 END    .EQ *

The QT, QTSZ example uses the * to help calculate the length of a string of characters. The VAR line uses "*-*" to define a variable as having a value of zero.

The expression labelled FILLER causes the assembler to skip ahead to $900. This has much the same effect as .OR $900, but it won't cause the assembler to close a target file, the way .OR would.

One thing Bill wanted was an expression to have the assembly skip up to the beginning of the next page, no matter what that page might be. Here's what we came up with:

              0800- 34 12    1000 START  .DA $1234
              0802-          1010 FILL   .BS *+255/256*256-*
              0900- 45 23    1020 END    .DA $2345

If you change the origin to $C00, END will move to $D00. With this coding, END will always be $100 above START. Note that there is no precedence when the assembler is evaluating an expression. Terms are taken strictly left-to-right. But notice how smart the expression cracker in the assembler is! It knows that a "*" between numbers or labels means "multiply", and a "*" between arithmetic operators means "location counter".

In the American Heart Association CPR project Mike uses lots of overlays, and has to make sure that modules don't grow above a certain address. He does it by putting lines like these at the end of a module:

              1000        .DO *>LIMIT
              1010   !!! PROGRAM TOO BIG !!!
              1020        .FIN

Here's an example, to keep a program below the Hi-res pages:

              1000        .OR $1FFE
              1010        .DA $4321
              1020        .DO *>$2000
              1030   !!! PROGRAM TOO BIG !!!
              1040        .FIN

That will assemble just fine:

                             1000        .OR $1FFE
              1FFE- 21 43    1010        .DA $4321
                             1020        .DO *>$2000
                             1040        .FIN

              0000 ERRORS IN ASSEMBLY

But, try inserting another line:

              1015        .DA $1234

Here's what happens:

              *** BAD OPCODE ERROR
               1030   !!! PROGRAM TOO BIG !!!

              0001 ERRORS IN ASSEMBLY

The key to this technique is putting a couple of blanks at the beginning of line 1030. That way, the assembler tries to parse "!!!" as an opcode, and reports an error during pass one, before any code has been generated.

You should be very careful about using "*", and experiment on a test disk when trying something new. For example, take another look at line 1060 in the first listing. If you put "*-$900" for the operand, that would be negative. The result would be $FF07, which would try to write 65,287 zero bytes onto your target file. The next thing you see is probably DISK FULL!

That's about all the tricky things we have room for right now. We hope these hints will help you to navigate "by the stars" in your programming. Just remember to experiment carefully with the * operand before using it in vital programs. There are also many pitfalls on this road!

Promising New Book | Bob Sander-Cederlof

I just received an advance copy of a forthcoming book by Jules Gilder (a long-time AAL subscriber), titled "Now That You Know Apple Assembly Language, What Can You Do With It?" As the title implies, this will be an intermediate level look at really using assembly language in your Apple. It looks good. As soon as I have details about price and publication date, I'll let you know.

A Sometimes Useful Patch | Bob Sander-Cederlof

Sometimes you would like to see all the hex bytes a macro produces, but not the expanded lines of source code. The >LIST MOFF directive turns off both, but with the following three byte patch you can see the hex bytes for each macro call.

    Motherboard version:   :$218B:0   (was 03)
                           :$21B3:0   (was 05)
                           :$21E2:0   (was 10)

    RAM Card version:      :$C083 C083   (enable writing)
                           :$E2D7:0   (was 03)
                           :$E2FF:0   (was 05)
                           :$E32E:0   (was 10)

Don't make these into permanent patches, because there will be times when you want to use the .LIST directives normally. If you feel like making the changes often, you might make two separate versions of the assembler, or make some EXEC files to do the patching on demand.

Source Code for a Word Processor | Bob Sander-Cederlof

I finally have had to face it. I am never going to have time to finish the S-C word processor. It is certainly usable, because we have been using it here for months now. And we use it a lot, writing the newsletter, manuals, letters, etc. My father-in-law uses it, and so does my best friend, Fred. Fred's 11-year-old daughter is also using it, and loves it. She is currently typing a research paper using it.

I know it is easy to use, because I didn't even give Fred a list of commands, let alone a reference manual. Of course, I did sit down with them for a few hours at the first, because they had never even seen a word processor before.

In power, it is somewhere between Applewriter 1.1 and Applewriter II. It is similar in operation to Applewriter 1.1, and works in 40-column mode only. It requires a lower-case display and shift-key mod.

It can read Applewriter 1.1 files, and instantly convert them to standard ASCII form. Normally it uses standard Apple text files (type T in the catalog). Of course, with Bobby Deen's help, I built in FAST read and write of those text files. Faster than binary files, actually. Something like 100 sectors in 7 seconds, if I remember correctly.

I want to make a deal with you. I'll send you the complete commented source code on disk, together with a few sample text files. The text files will describe the command repertoire. If you are already familiar with Applewriter 1.1, you won't have any trouble at all. The assembled word processor will also be there, in case you don't have the S-C Macro Assembler.

But if you do have my assembler, you can proceed to modify, improve, augment, enhance, and so on, to your heart's content.

I'll send you the disk, if you'll send me $50. Or your charge card numbers, of course. I also want your commitment to keep this in the family. You know, don't go out and write a manual and wrap it in a fancy cover and call it YOUR product!

If you do enhance it, send in your additions and we'll make this a joint effort. With all of us working on it, we may soon have the world's best word machine!

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 $15 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $13 postage for other countries. Back issues are available for $1.50 each (other countries add $1 per back issue for postage).

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