Volume 2 -- Issue 3 | December 1981

In This Issue...

- EXCEL-9: A 6809 Card with FLEX
- Applesoft Hi-Res Subroutines
- Hex Constants in Applesoft
- Applesoft Line Editing Aid
- Improved Applesoft Fast String Input
- Adding ASCII to Apple Monitor Dump
- Applesoft GOTO from Assembly Language

EXCEL-9: A 6809 Card with FLEX | Bob Sander-Cederlof

For the last month and a half I have been working with a fantastic new device: the EXCEL-9 from Seikou Electronics in Japan. The EXCEL-9 contains a 6809E CPU, 8K bytes of ROM, and an interval timer. The 8K ROM contains a monitor with 35 commands (including mini-assembler and dis-assembler commands). The introductory price of $399.95 includes the FLEX Operating System from Technical Systems Consultants (TSC), with utilities, text editor, and macro assembler.

The board will soon be appearing in your local computer stores, courtesy of ESD Laboratories. I worked with them to translate the excellent reference manual into English. (That explains how I obtained one of the boards so early.)

EXCEL-9 has a lot of unique features that should make it a very popular board:

- An on-board interval timer (with 24 intervals from 2 microseconds to 16 seconds) can be used from both the 6809 and 6502.
- Built-in linkage routines for calling 6809 subroutines from Applesoft, Integer BASIC, or 6502 machine language. You can also call 6502 routines and even DOS 3.3 commands from 6809 programs.
- Option of using standard Apple intelligent interfaces with 6502 firmware, or of using new cards with 6809 firmware.
- Memory Mapping that supports the FLEX operating system. Future option to add external memory to EXCEL-9, allowing full-speed multiprocessing.

I intend to handle these boards. You can order them from me now, but please allow a while for delivery. The documentation is ready for the printer, but not yet printed.

Applesoft Hi-Res Subroutines | Bob Sander-Cederlof

One of the questions I hear the most is "How can I call the Hi-Res subroutines in the Applesoft ROMs?" The basic information about those subroutines has been published (in Apple Orchard, Vol. 1 No. 1), but with an error in the subroutine addresses.

First, some important locations in page zero:

         $1A,1B  Shape pointer used by DRAW and XDRAW
         $1C     Last used color byte
         $26,27  Address of byte containing X,Y point
         $30     Bit mask for bit in that byte
         $E0,E1  X-coordinate (0-279)
         $E2     Y-coordinate (0-191)
         $E4     Color
         $E6     Page ($20 if HGR, $40 if HGR2)
         $E7     SCALE= value
         $E8,E9  Address of beginning of shape table
         $EA     Collision counter
         $F9     ROT= value

The software uses some other page zero variables, but I am not too clear yet on their purpose.

Now here are the major entry points:

         HGR2     $F3D8   Initialize and clear hi-res page 2.

         HGR      $F3E2   Initialize and clear hi-res page 1.

         HCLR     $F3F2   Clear the current hi-res screen to black.

         BKGND    $F3F6   Clear the current hi-res screen to the
                          last plotted color (from ($1C).

         HPOSN    $F411   Positions the hi-res cursor without
                          plotting a point.
                          Enter with (A) = Y-coordinate, and
                          (Y,X) = X-coordinate.

         HPLOT    $F457   Calls HPOSN and tries to plot a dot at
                          the cursor's position.  If you are
                          trying to plot a non-white color at
                          a complementary color position, no
                          dot will be plotted.

         HLIN     $F53A   Draws a line from the last plotted
                          point or line destination to:
                          (X,A) = X-coordinate, and
                          (Y) = Y-coordinate.

         HFIND    $F5CB   Converts the hi-res cursor's position
                          back to X- and Y-coordinates; stores
                          X-coordinate at $E0,E1 and Y-coordinate
                          at $E2.

         DRAW     $F601   Draws a shape.  Enter with (Y,X) = the
                          address of the shape table, and (A) =
                          the rotation factor.  Uses the current
                          color.

         XDRAW    $F65D   Draws a shape by inverting the existing
                          color of the dots the shape draws over.
                          Same entry parameters as DRAW.

         SETHCOL  $F6EC   Set the hi-res color to (X), where (X)
                          must be between 0 and 7.

I wrote a sample demonstration program of the hi-res subroutines. First, here is an Applesoft version. Note that it first sets the whole screen to a particular color, and then draws a series of nested squares in a complementary color. Since it is nice and short, why don't you type it in and try it?

      100  HGR2
      110  FOR C = 0 TO 7: HCOLOR= C
      120  HPLOT 0,0: CALL 62454: REM CLEAR TO CURRENT COLOR
      130  HCOLOR= 7 - C
      140  FOR S = 10 TO 190 STEP 10
      150  X1 = 140 - S / 2: X2 = X1 + S
      160  Y1 = 95 - S / 2: Y2 = Y1 + S
      170  HPLOT X1,Y1 TO X2,Y1 TO X2,Y2 TO X1,Y2 TO X1,Y1
      180  NEXT S
      190  PRINT  CHR$ (7): FOR I = 1 TO 500: NEXT
      200  NEXT C
      210  TEXT

Now here is the assembly language program for the same task. It seemed to run about twice as fast as the Applesoft version, but I didn't use the stopwatch on it.

     1000  *---------------------------------
     1010  *      SAMPLE PLOTTING PROGRAM
     1020  *---------------------------------
     1030  AS.LASTCLR .EQ $1C
     1040  *---------------------------------
     1050  AS.HGR2    .EQ $F3D8 SET UP HI-RES PAGE 2
     1060  AS.HCLR    .EQ $F3F2 CLEAR HI-RES SCREEN
     1070  AS.BKGND   .EQ $F3F6 CLEAR HI-RES SCREEN TO LAST COLOR
     1080  AS.HPOSN   .EQ $F411 MOVE CURSOR TO (Y,X),(A)
     1090  AS.HPLOT   .EQ $F457 PLOT A DOT AT (Y,X),(A)
     1100  AS.HLIN    .EQ $F53A DRAW A LINE FROM LAST POINT TO (X,A),(Y)
     1110  AS.SETHCOL .EQ $F6EC SET HI-RES COLOR
     1120  MON.TEXT   .EQ $FB2F
     1130  *---------------------------------
     1140  HI.RES.DEMO
     1150         JSR AS.HGR2
     1160         LDX #0       FOR COLOR = 0 TO 7
     1170  .1     STX COLOR
     1180         JSR AS.SETHCOL
     1190         STA AS.LASTCLR
     1200         JSR AS.BKGND   CLEAR SCREEN TO SOLID COLOR
     1210         LDA COLOR
     1220         EOR #7       COMPLEMENTARY COLOR
     1230         TAX
     1240         JSR AS.SETHCOL
     1250         JSR DRAW.SQUARE
     1260         LDX COLOR      NEXT COLOR
     1270         INX
     1280         CPX #8
     1290         BCC .1
     1300         JSR MON.TEXT
     1310         RTS
     1320  *---------------------------------
     1330  DRAW.SQUARE
     1340         LDA #10      FOR SIZE=10 TO 190 STEP 10
     1350  .1     STA SIZE
     1360         LSR          SIZE/2
     1370         STA SIZE2
     1380         LDA #0
     1390         STA XSTART+1
     1400         STA XSTOP+1
     1410         SEC          XSTART=140-SIZE/2
     1420         LDA #140
     1430         SBC SIZE2
     1440         STA XSTART
     1450         CLC          XSTOP=XSTART+SIZE
     1460         ADC SIZE
     1470         STA XSTOP
     1480         SEC
     1490         LDA #95      YSTART=95-SIZE/2
     1500         SBC SIZE2
     1510         STA YSTART
     1520         CLC          YSTOP=YSTART+SIZE
     1530         ADC SIZE
     1540         STA YSTOP
     1550         LDY XSTART+1 HPLOT XSTART,YSTART
     1560         LDX XSTART
     1570         LDA YSTART
     1580         JSR AS.HPLOT
     1590         LDX XSTOP+1    TO XSTOP,YSTART
     1600         LDA XSTOP
     1610         LDY YSTART
     1620         JSR AS.HLIN
     1630         LDX XSTOP+1    TO XSTOP,YSTOP
     1640         LDA XSTOP
     1650         LDY YSTOP
     1660         JSR AS.HLIN
     1670         LDX XSTART+1   TO XSTART,YSTOP
     1680         LDA XSTART
     1690         LDY YSTOP
     1700         JSR AS.HLIN
     1710         LDX XSTART+1   TO XSTART,YSTART
     1720         LDA XSTART
     1730         LDY YSTART
     1740         JSR AS.HLIN
     1750         CLC
     1760         LDA SIZE     NEXT SIZE
     1770         ADC #10
     1780         CMP #191
     1790         BCC .1
     1800  DELAY.LOOP
     1810         LDY #0       DELAY LOOP SO WE CAN SEE IT
     1820  .1     LDX #0
     1830  .2     DEX
     1840         BNE .2
     1850         LDA $C030    AND HEAR IT
     1860         DEY
     1870         BNE .1
     1880         RTS
     1890  *---------------------------------
     1900  COLOR  .BS 1
     1910  SIZE   .BS 1
     1920  SIZE2  .BS 1
     1930  XSTART .BS 2
     1940  YSTART .BS 1
     1950  XSTOP  .BS 2
     1960  YSTOP  .BS 1

[ The following advertisement is for historical interest only!!! The company closed its doors in 1988.]

A D V E R T I S E M E N T
    S-C ASSEMBLER II Version 4.0...................................$55.00
        Includes Manual, Diskette with Assembler and sample
        source programs, and Quick Reference Card.

    Source code of Version 4.0 on disk.............................$95.00
        Fully commented, easy to understand and modify to
        your own tastes.

    Cross Assembler Patches for 6809...............................$20.00
        Requires possession of Version 4.0.  Enables you to
        develop programs for the Motorola 6809 CPU.  (The
        MILL from Stellation, EXCEL-9 from ESD Laboratories,
        or the Radio Shack Color Computer.)

    Cross Assembler for 6800.......................................$22.50
        Requires possession of Version 4.0.  Enables you to
        develop programs for the Motorola 6800, 6801, and
        6802 CPUs.

    AAL Quarterly Disks.......................................each $15.00
        Each disk contains all the source code from three
        issues of "Apple Assembly Line", to save you lots
        of typing and testing time.
        QD#1:  Oct - Dec 1980    QD#4:  Jul - Sep 1981
        QD#2:  Jan - Mar 1981    QD#5:  Oct - Dec 1981
        QD#3:  Apr - Jun 1981

    Double Precision Floating Point for Applesoft..................$50.00
        Provides 21-digit precision for Applesoft programs.
        Includes subroutines for standard math functions.

    Some Simple Integer BASIC Games................................$10.00
        Includes 4x4x4 tic-tac-toe, lo-res space war, lo-res
        jig-saw puzzle, and mastermind.

    Blank Diskettes..............................package of 20 for $50.00
        Verbatim Datalife, with hub rings, no labels, in plain
        white jackets, in cellophane wrapper.

    Lower-Case Display Encoder ROM.................................$25.00
        Works only Revision level 7 Apples.  Replaces the
        encoder ROM.  Comes with instructions.

    Diskette Mailing Protectors.....................10-99:  40 cents each
                                              100 or more:  25 cents each
        Corrugated folder specially designed for mailing
        mini-floppy diskettes.  Fits in standard 6x9-inch
        envelope.  (Envelopes 5-cents each, if you need them.)

    Zip-Lock Bags (2-mil, 6"x9")............................100 for $8.50
                  (2-mil, 9"x12")..........................100 for $13.00

    Books, Books, Books......................compare our discount prices!
        "Beneath Apple DOS", Worth & Lechner.............($19.95)  $18.00
        "What's Where in the Apple", William Leubert.....($14.95)  $14.00
        "6502 Assembly Language Programming", Leventhal..($16.99)  $16.00
        "Apple Assembly Language", Don & Kurt Inman......($12.95)  $12.00

          *** S-C SOFTWARE, P. O. BOX 280300, Dallas, TX 75228 ***
          *** (214) 324-2050    We take Master Charge and VISA ***
A D V E R T I S E M E N T

Hex Constants in Applesoft | David Bartley

Coding in BASIC has several frustrations for the assembly language programmer. One small but constant irritant for me has been the inability to directly specify hexadecimal values in Applesoft statements or in response to an INPUT command. I finally decided to do something about it when I read Bob Sander-Cederlof's article on the CHRGET routine in the September Apple Assembly Line. The result is the short program shown here.

My goal was to be able to enter a hex constant, defined as a "$" followed by one or more hex digits, anywhere Applesoft would allow an integer constant to appear. I nearly succeeded -- I'll discuss the exceptions a little later. I now can write statements like:

           100 FOR I = $0 TO $FF
           110 INPUT X,Y
           120 Z(I) = $100*X + Y - $3DEF

The responses to the INPUT statement may also be hex constants. Values may range from -$FFFF (-65535) to $FFFF (65535); the left-most bit is not considered a sign bit.

My program is set up by BRUN-ning the object file XB.A/S HEX CONSTANTS (see line 1010). Initialization consists of modifying the Applesoft CHRGET subroutine to branch into new code starting at line 1400. As you may recall, CHRGET is used by the BASIC interpreter to fetch characters and tokens from the program text of keyboard when a program is executing. The new CHRGET code watches for a "$" character; when one is found, it scans forward until it hits a character which is not a hex digit, converting to a binary value (in VAL) on the fly.

Variable IDX serves two purposes. It is normally negative, signifying that characters are to be fetched without special action until a "$" is encountered. After a hex constant is found and converted to a binary value, IDX becomes a positive index into a power-of-ten table to facilitate converting VAL to a decimal value. Each subsequent call to CHRGET then returns a successive character of the decimal integer representation of VAL until IDX becomes -1, the entire value has been transformed from hex to decimal, and the normal mode is restored.

There are, of course, several complications. One is the BASIC "DEF" command, which happens to consist of a string of hex digits. Applesoft therefore parses a constant like "$3DEF" as the ASCII characters "$" and "3" followed by the DEF token (hex 88). Lines 1760 to 1840 take care of that.

A more serious complication is the existence of a frequently used alternate entry point to CHRGET called CHRGOT. CHRGOT is called to fetch the previous item from the text rather than the next one. It seems that numeric constants are parsed from several places within the Applesoft interpreter, with some using CHRGOT and others not. When I fixed things up so CHRGOT would work for inline constants and the INPUT command, it no longer worked for values in DATA statements (or for hex line numbers, for that matter!)

The trick that makes CHRGOT work (most of the time) is to back up TXTPTR and then return a leading zero to start off the converted decimal value. The zero causes no consternation for the parts of the interpreter that see it and is not missed by those that don't. If CHRGOT is not called, however, TXTPTR should not be backed up. You can't win!

I hope others will be able to make use of this routine -- better, that someone will overcome the problem with DATA statement values. It has been quite valuable to me as it is, as well as quite an education in understanding the inner workings of the Applesoft interpreter.

     1000         .OR $300
     1010         .TF XB.A/S HEX CONSTANTS
     1020  *---------------------------------
     1030  *
     1040  *      APPLESOFT HEX CONSTANTS
     1050  *
     1060  *      WRITTEN BY DAVID H. BARTLEY
     1070  *      AUSTIN, TEXAS -- AUGUST 1981
     1080  *
     1090  *      TO INITIALIZE:
     1100  *          BRUN THIS PROGRAM (XB.A/S HEX CONSTANTS)
     1110  *
     1120  *      TO USE:
     1130  *          PRECEDE HEX CONSTANTS
     1140  *          WITH A "$" CHARACTER
     1150  *
     1160  *---------------------------------
     1170  BASIC  .EQ $E003    SOFT RE-ENTRY
     1180  CHRGET .EQ $00B1    A/S CHRGET RTN
     1190  CHRGOT .EQ $00B7    A/S CHRGOT RTN
     1200  CHRCHK .EQ CHRGOT+3
     1210  TXTPTR .EQ $B8      A/S TEXT PTR
     1220  OVERR  .EQ $E8D5    OVERFLOW ERROR
     1230  TEMP   .EQ $FC      16-BIT TEMPORARY
     1240  VAL    .EQ $FE      16-BIT VALUE
     1250  *---------------------------------
     1260  INIT
     1270         LDA #$4C     MODIFY CHRGET
     1280         STA CHRGET   TO CALL HERE
     1290         LDA #NEW.CHRGET
     1300         STA CHRGET+1
     1310         LDA /NEW.CHRGET
     1320         STA CHRGET+2
     1330         JMP BASIC    RETURN TO A/S
     1340  NEXTCH
     1350         INC TXTPTR   DUPLICATE THE
     1360         BNE .10      OLD CHRGET
     1370         INC TXTPTR+1
     1380  .10    JMP CHRGOT
     1390  *---------------------------------
     1400  NEW.CHRGET
     1410         BIT IDX      NORMAL MODE?
     1420         BPL .60      -NO
     1430  *
     1440  * CHECK FOR "$" AS NEXT CHARACTER
     1450  *
     1460         JSR NEXTCH   GET CHAR
     1470         CMP #$24    "$"?
     1480         BNE .50      -NO, RETURN IT
     1490  .10
     1500  * PARSE A HEX NUMBER AND CONVERT
     1510  * IT TO A BINARY VALUE
     1520  *
     1530         LDA #0
     1540         STA VAL      VAL = 0
     1550         STA VAL+1
     1560         LDA #4       INDEX TO POWER
     1570         STA IDX
     1580  .20
     1590         JSR NEXTCH   GET HEX DIGIT
     1600         BEQ .40      -EOL OR ":"
     1610         SEC
     1620         SBC #$30     CHECK FOR DIGIT
     1630         BMI .35      -NOT A DIGIT
     1640         CMP #10
     1650         BCC .30      -OK (0-9)
     1660         SBC #17
     1670         BMI .40      -NOT A DIGIT
     1680         CMP #6
     1690         BCS .40      -NOT A DIGIT
     1700         ADC #10
     1710  .30    JSR ASL4     MULT VAL BY 16
     1720         ORA VAL      ADD NEW DIGIT
     1730         STA VAL
     1740         JMP .20
     1750  .35
     1760         CMP #$88     "DEF" TOKEN?
     1770         BNE .40      -NO
     1780         JSR ASL4     -YES
     1790         LDA VAL
     1800         ORA #$0D     ASL BY 12 AND
     1810         STA VAL+1    ADD $0DEF
     1820         LDA #$EF
     1830         STA VAL
     1840         BNE .20      (ALWAYS)
     1850  .40
     1860         LDA TXTPTR   BACK UP THE
     1870         BNE .41
     1880         DEC TXTPTR+1
     1890  .41    DEC TXTPTR
     1900         LDA TXTPTR   SAVE TXTPTR
     1910         STA TEMP     IN CASE IS IS
     1920         LDA TXTPTR+1 DECREMENTED
     1930         STA TEMP+1   BY THE CALLER
     1940  *
     1950         LDA #$30     ASCII "0"
     1960  .50    JMP CHRCHK   -EXIT
     1970  .60
     1980  * CONVERT BINARY VALUE TO DECIMAL
     1990  * AND RETURN THE NEXT ASCII DIGIT
     2000  *
     2010         LDA TEMP     FIX ANY ATTEMPT
     2020         STA TXTPTR   TO DECREMENT
     2030         LDA TEMP+1   TXTPTR
     2040         STA TXTPTR+1
     2050         STX SAVE.X
     2060         LDX IDX      POWER OF TEN
     2070         DEC IDX
     2080         LDA #$30     ASCII "0"
     2090  .70
     2100         PHA          ASCII DIGIT
     2110         LDA VAL
     2120         CMP LO.TENS,X  SET CARRY
     2130         LDA VAL+1
     2140         SBC HI.TENS,X
     2150         BCC .80      -EXIT LOOP
     2160         STA VAL+1
     2170         LDA VAL
     2180         SBC LO.TENS,X
     2190         STA VAL
     2200         PLA          ASCII DIGIT
     2210         CLC
     2220         ADC #1       INCREMENT IT
     2230         BNE .70      -LOOP
     2240  .80
     2250         PLA          ASCII DIGIT
     2260         LDX SAVE.X
     2270  .90
     2280         JMP CHRCHK   PROCESS IT
     2290  *---------------------------------
     2300  ASL4   JSR ASL2     ASL VAL BY 4
     2310  ASL2   JSR ASL1     ASL VAL BY 2
     2320  ASL1   ASL VAL      ASL VAL BY 1
     2330         ROL VAL+1
     2340         BCS OVFLOW   -OVERFLOW ERROR
     2350         RTS          -EXIT
     2360  OVFLOW
     2370         JMP OVERR    REPORT OVERFLOW
     2380  *---------------------------------
     2390  LO.TENS .DA #1
     2400         .DA #10
     2410         .DA #100
     2420         .DA #1000
     2430         .DA #10000
     2440  HI.TENS .DA /1
     2450         .DA /10
     2460         .DA /100
     2470         .DA /1000
     2480         .DA /10000
     2490  IDX    .DA #$FF     TABLE INDEX
     2500  SAVE.X .DA #0       SAVE X-REG
     2510  *---------------------------------
     2520  ZZZZZZ .EN

Applesoft Line Editing Aid | Sandy Mossberg

[ Sandy is an M.D. in Port Chester, New York. You have probably seen his excellent articles and programs in NIBBLE. ]

The following program is a developmental tool for line-editing Applesoft programs. It places the line you specify at the top of the screen, ready to be cursor edited. The line is displayed without added blanks at the end of each screen line, which can mess up editing of PRINT statements. Obviously, adding Konzen-like PLE features would make it much nicer, but that's a story for another day.

The program loads at the ever-popular $300. If you BRUN it, or BLOAD and CALL768, it installs itself. To use it, type a slash and a line number. For example, to edit line 150, type "/150" and a carriage return. The screen will be cleared and line 150 displayed on the top. The cursor will be placed over the first character, and you will be ready to edit it with standard cursor-editing techniques. (If there is no line 150 in memory, the bell will ring instead.)

Several aspects of the code should be of interest to assembly language programmers:

1. As noted in AAL of 9/81, the CHRGET/CHRGOT routine screens for the command character (a slash). This technique permits concurrent use of an amper-utility. The KSW hook could be employed as yet another filter, making a trio of vectors operative.
2. To allow "illegal" line numbers (64000-65535) to be accessed, the LINGET routine is replaced by calls to FRMEVL and GETADR (see Lines 1800-1810).
3. The de-parsing section (see Lines 2030-2500) is an offspring of Applesoft's LIST routine, modified to print a single program line rather than an entire listing. I also eliminated the code which adds those extra blanks in the middle of quoted strings which take more than one screen line to LIST. To me it seems pretty neat!

Since I did not make any test to determine whether or not the program is RUNning at the time the slash is trapped in my filter, you have to be careful about using the slash character in REM statements. For example, "REM /150" will clear the screen and list line 150 at the top before proceeding. Other combinations of "/" in REM's may blow up. Also, typing "/" when Applesoft is executing an INPUT statement is now dangerous. Anyone know how to fix this?

     1000  *---------------------------------
     1010  *           LINE.EDIT
     1020  *
     1030  *       BY SANDY MOSSBERG
     1040  *
     1050  *   COMMERCIAL RIGHTS RESERVED
     1060  *
     1070  *---------------------------------
     1080  * 1.PACKS PROGRAM LINE FOR EASY EDITING.
     1090  *
     1100  * 2.USES CHRGET/CHRGOT FILTER ROUTINE NOTED IN AAL 9/81.
     1110  *
     1120  * 3.CHARACTER OUTPUT ROUTINE MODIFIED FROM APSOFT ROM
     1130  *   CODE (LIST, $D6A5-$D765).
     1140  *
     1150  * 4.INSTALLATION AND USE:
     1160  *    (A) BRUN LINE.EDIT.
     1170  *    (B) COMMAND "/LINENUMBER" PRODUCES PACKED LINE AT
     1180  *        TOP OF SCREEN.
     1190  *    (C) IF CHRGET/CHRGOT VECTOR DESTROYED BY APSOFT
     1200  *        COLDSTART (]FP, *E000G, *CTL-B), RESET LINE.EDIT
     1210  *        VECTOR BY CALL 768.
     1220  *---------------------------------
     1230             .OR $300
     1240  *---------------------------------
     1250  *          APPLESOFT POINTERS
     1260  *---------------------------------
     1270  AS.FORPNT  .EQ  $85     ;HOLD Y-REGISTER
     1280  AS.LOWTR   .EQ  $9B,$9C ;LOCATION OF CHARACTER OR TOKEN IN PGM
     1290  AS.DSCTMP  .EQ  $9D,$9E ;LOCATION IN KEYWORD TABLE
     1300  *---------------------------------
     1310  *          APPLESOFT CHRGET/CHRGOT
     1320  *---------------------------------
     1330  AS.CHRGET  .EQ  $B1     ;GETS CHARACTER AT TEXT POINTER
     1340  AS.TXTPTR  .EQ  $B8,$B9 ;TEXT POINTER
     1350  AS.CHREXT  .EQ  $BA     ;CHRGET/CHRGOT VECTOR TO LINE.EDIT
     1360  AS.CHRENT  .EQ  $BE     ;RE-ENTRY TO CHRGET/CHRGOT
     1370  *---------------------------------
     1380  *          APPLESOFT ROM
     1390  *---------------------------------
     1400  AS.FNDLIN  .EQ  $D61A   ;ADDR NMBR IN LINNUM ($50,$51) TO LOWTR
     1410  AS.CRDO    .EQ  $DAFB   ;LINEFEED
     1420  AS.OUTSP   .EQ  $DB57   ;OUTPUT SPACE
     1430  AS.OUTDO   .EQ  $DB5C   ;OUTPUT CHARACTER
     1440  AS.FRMEVL  .EQ  $DD7B   ;FORMULA AT TEXT POINTER TO FAC ($9D-$A2)
     1450  AS.GETADR  .EQ  $E752   ;FAC TO INTEGER IN LINNUM ($50,$51)
     1460  AS.LINPRT  .EQ  $ED24   ;PRINT DECIMAL OF (A,X)
     1470  *---------------------------------
     1480  *          MONITOR ROM
     1490  *---------------------------------
     1500  MON.TABV   .EQ  $FB5B   ;VTAB TO VALUE IN (A)
     1510  MON.HOME   .EQ  $FC58   ;HOME CURSOR, CLEAR SCREEN
     1520  MON.BELL   .EQ  $FF3A   ;BEEP!
     1530         .PG
     1540  *---------------------------------
     1550  *  PUT LINE.EDIT VECTOR INTO CHRGET/CHRGOT
     1560  *---------------------------------
     1570  START  LDA #$4C         ;JMP 'LINE.EDIT'
     1580         STA AS.CHREXT
     1590         LDA #EDIT
     1600         STA AS.CHREXT+1
     1610         LDA /EDIT
     1620         STA AS.CHREXT+2
     1630  RTS1   RTS
     1640  *---------------------------------
     1650  *  CHECK FOR VALID COMMAND
     1660  *---------------------------------
     1670  EDIT   CMP #$2F         ;IS IT A SLASH (/)?
     1680         BNE .1           ;NO. RETURN
     1690         INC AS.TXTPTR    ;YES. BUMP TEXT POINTER
     1700         BNE .2           ;BRANCH ALWAYS
     1710  *---------------------------------
     1720  *  RETURN TO CHRGET/CHRGOT OR CALLER
     1730  *---------------------------------
     1740  .1     CMP #$3A         ;IF COLON (EOS), SET Z AND C
     1750         BCS RTS1         ; FLAGS AND RETURN TO CALLER
     1760         JMP AS.CHRENT    ;IF NOT EOS, RE-ENTER CHRGET/CHRGOT
     1770  *---------------------------------
     1780  *  FIND LOCATION OF LINE NUMBER
     1790  *---------------------------------
     1800  .2     JSR AS.FRMEVL    ;PUT LINE NUMBER INTO FAC ($9D-$A2)
     1810         JSR AS.GETADR    ;PUT FAC INTO LINNUM ($50,$51)
     1820         JSR AS.FNDLIN    ;PUT ADDR OF LINE INTO LOWTR
     1830         BCC .5           ;CARRY CLEAR IF LINE NMBR NOT FOUND
     1840  *---------------------------------
     1850  *  CLEAR SCREEN AND SET TO ROW 2, COLUMN 2
     1860  *---------------------------------
     1870         JSR MON.HOME
     1880         JSR AS.CRDO
     1890         JSR AS.OUTSP
     1900  *---------------------------------
     1910  *  PRINT LINE NUMBER
     1920  *---------------------------------
     1930         LDY #02          ;SET INDEX TO LINE NUMBER BYTES
     1940         LDA (AS.LOWTR),Y ;PUT LINE NUMBER LO
     1950         TAX              ; INTO (X)
     1960         INY
     1970         LDA (AS.LOWTR),Y ;PUT LINE NUMBER HI INTO (A)
     1980         STY AS.FORPNT    ;HOLD (Y)
     1990         JSR AS.LINPRT    ;PRINT DECIMAL OF (A,X)
     2000  *---------------------------------
     2010  *  GET CHARACTER OR TOKEN
     2020  *---------------------------------
     2030         LDA #$20         ;SPACE
     2040  .3     LDY AS.FORPNT    ;RESTORE (Y)
     2050  .4     JSR AS.OUTDO     ;PRINT CHARACTER IN (A)
     2060         INY
     2070         LDA (AS.LOWTR),Y ;GET CHARACTER OR TOKEN
     2080         BNE .8           ;IF NOT EOS (0), GET MORE
     2090         .PG
     2100  *---------------------------------
     2110  *  TWO ENDINGS -- ONE HAPPY, ONE SAD
     2120  *---------------------------------
     2130         LDA #00          ;LINE WAS FOUND. END WITH
     2140         JMP MON.TABV     ; CURSOR AT ROW 2, COLUMN 2
     2150  .5     JSR MON.BELL     ;LINE WAS NOT FOUND. END WITH
     2160         JMP AS.CRDO      ; CURSOR BELOW COMMAND INPUT
     2170  *---------------------------------
     2180  *  GET CHARACTER IN KEYWORD TABLE
     2190  *---------------------------------
     2200  .6     INY
     2210         BNE .7
     2220         INC AS.DSCTMP+1
     2230  .7     LDA (AS.DSCTMP),Y
     2240         RTS
     2250  *---------------------------------
     2260  *  PRINT CHARACTER OR KEYWORD
     2270  *---------------------------------
     2280  .8     BPL .4           ;NON-TOKEN IS POS ASCII
     2290         SEC              ;TOKEN MINUS $7F EQUALS INDEX TO
     2300         SBC #$7F         ; LOCATION OF KEYWORD IN TABLE
     2310         TAX              ;PUT INDEX IN (X)
     2320         STY AS.FORPNT    ;HOLD (Y)
     2330         LDY #$D0         ;KEYWORD TABLE STARTS AT $D0D0
     2340         STY AS.DSCTMP
     2350         LDY #$CF
     2360         STY AS.DSCTMP+1
     2370         LDY #$FF         ;WHEN BUMPED, (Y) WILL BE ZERO
     2380  .9     DEX              ;DEC INDEX TO KEYWORD LOCATION
     2390         BEQ .11          ;WHEN (X) IS ZERO, KEYWORD LOCATED
     2400  .10    JSR .6           ;GET CHARACTER IN KEYWORD TABLE
     2410         BPL .10          ;IF POS ASCII, GET ANOTHER
     2420         BMI .9           ;IF NEG ASCII, DEC LOCATION INDEX
     2430  .11    JSR AS.OUTSP     ;PRINT SPACE
     2440  .12    JSR .6           ;GET CHARACTER IN KEYWORD TABLE
     2450         BMI .13          ;IT'S THE FINAL CHAR IN KEYWORD
     2460         JSR AS.OUTDO     ;PRINT NON-FINAL CHAR (POS ASCII)
     2470         BNE .12          ;BRANCH ALWAYS
     2480  .13    JSR AS.OUTDO     ;PRINT FINAL CHAR (NEG ASCII)
     2490         LDA #$20         ;SPACE
     2500         BNE .3           ;BRANCH ALWAYS
     2510  *---------------------------------
     2520  SIZE   .EQ *-START
     2530         .PG

Improved Applesoft Fast String Input | Bob Sander-Cederlof

In the April 1981 issue of AAL I printed a subroutine to read a line from the keyboard or a text file into an Applesoft string. The original version had a minor flaw (or major, if you happened to run into it): it left the high-order bit on in each byte, so that Applesoft could not compare them properly with strings from other sources. I printed a correction in a later issue, which stripped off the leading bit from each byte before putting it in the string.

Now Sherm Ostrowsky (from Goleta, California) has pointed out a more elegant solution. He uses a subroutine inside Applesoft that reads a line, terminates it with hex 00, and strips off the leading bit from each byte. The subroutine starts at $D52C. The only thing it doesn't do that we need is give us the length of the input line. Here is a commented listing of it.

     1000  *---------------------------------
     1010  *      APPLESOFT LINE INPUT SUBROUTINE
     1020  *---------------------------------
     1030         .OR $D52C
     1040         .TA $82C
     1050  *---------------------------------
     1060  MON.PROMPT .EQ $33
     1070  MON.RDLINE .EQ $FD6A
     1080  BUFFER     .EQ $200
     1090  *---------------------------------
     1100  AS.INLINE
     1110         LDX #$80     NULL CHARACTER
     1120  INLIN2 STX MON.PROMPT  FOR THE PROMPT CHARACTER
     1130         JSR MON.RDLINE  READ A LINE INTO BUFFER
     1140         CPX #239     TRUNCATE TO 239 CHARACTERS
     1150         BCC .1
     1160         LDX #239
     1170  .1     LDA #0       MARK END OF LINE WITH $00
     1180         STA BUFFER,X
     1190         TXA          # REAL CHARS IN LINE
     1200         BEQ .3       EMPTY LINE
     1210  .2     LDA BUFFER-1,X  STRIP OFF ALL SIGN BITS
     1220         AND #$7F
     1230         STA BUFFER-1,X
     1240         DEX
     1250         BNE .2
     1260  .3     LDA #0
     1270         LDX #BUFFER-1
     1280         LDY /BUFFER-1
     1290         RTS

Since $D52C stores $80 (null) in the prompt character, you might want to load the X-register with $87 (bell) and enter at $D52E instead.

Since the subroutine returns with $FF in the X-register, and we need the length of the input line instead, we can use the following code to get the line length in X:

                JSR $D52C
         .1     INX
                LDA $200,X
                BNE .1

Here is a new version, then, of my fast string input subroutine:

     1000  *---------------------------------
     1010  *      FAST STRING INPUT ROUTINE
     1020  *      &GET <STRING VARIABLE>
     1030  *      ACCEPTS ANY CHARACTER, UNLIKE NORMAL INPUT
     1040  *---------------------------------
     1050         .OR $300
     1060         .TF B.FAST READ
     1070  *---------------------------------
     1080  AS.CHRGET  .EQ $00B1
     1090  AS.SYNERR  .EQ $DEC9
     1100  AS.INLINE  .EQ $D52C
     1110  AS.PTRGET  .EQ $DFE3
     1120  AS.GETSPA  .EQ $E452
     1130  AS.MOVSTR  .EQ $E5E2
     1140  *---------------------------------
     1150  ADDR       .EQ $71 AND 72
     1160  PNTR       .EQ $83 AND 84
     1170  LENGTH     .EQ $9D
     1180  BUFFER     .EQ $200
     1190  *---------------------------------
     1200  GET    CMP #$BE     "GET" TOKEN
     1210         BEQ .1       YES
     1220         JMP AS.SYNERR  SORRY...
     1230  .1     JSR AS.CHRGET SET UP THE FOLLOWING CHARACTER
     1240         JSR AS.PTRGET FIND THE STRING VARIABLE POINTER
     1250         JSR AS.INLINE READ A LINE INTO BUFFER
     1260  .2     INX          COMPUTE THE LENGTH OF THE LINE
     1270         LDA BUFFER,X
     1280         BNE .2       NOT AT END OF LINE YET
     1290         STX LENGTH   SAVE LINE LENGTH
     1300         TXA
     1310         JSR AS.GETSPA  GET SPACE IN STRING AREA
     1320         LDY #0       SET UP STRING VARIABLE POINTER
     1330         STA (PNTR),Y   LENGTH
     1340         INY
     1350         LDA ADDR
     1360         STA (PNTR),Y   ADDRESS (LO-BYTE)
     1370         INY
     1380         LDA ADDR+1
     1390         STA (PNTR),Y   ADDRESS (HI-BYTE)
     1400         LDY /BUFFER    SET UP TO COPY STRING DATA
     1410         LDX #BUFFER    INTO STRING AREA
     1420         LDA LENGTH
     1430         JMP AS.MOVSTR  COPY IT NOW, AND RETURN

Here is how you might use it from an Applesoft program, to read a series of lines from a file:

         100 D$ = CHR$ (4)
         110 PRINT D$"BLOAD B.FAST READ"
         120 POKE 1013,76 : POKE 1014,0 : POKE 1015,3
         210 PRINT D$"OPEN MY.FILE"
         220 PRINT D$"READ MY.FILE"
         230 FOR I = 1 TO 10
         240 & GET A$(I)
         250 NEXT I

Note that the subroutine is fully relocatable. Since there are no internal JMP's or JSR's, and no internal variables, you can load the program anywhere it will fit and run it without any modifications. Just be sure to change line 120 above to POKE the correct address in 1014 and 1015.

Adding ASCII to Apple Monitor Dump | Bob Sander-Cederlof

Peter Bartlett (subscriber in Chicago, IL) sent me some source code for patches to the Apple Monitor ROM. Of course, patching a ROM may be a little too much hardware work, but if you have a 16K RAM card you can put the revised monitor up there. The space needed for the patch is stolen from the cassette I/O command, so if you install this patch you will lose cassette I/O.

Peter's patches add the ASCII dump to the Apple Monitor's hex dump. That is, when I type a command like "800.87F" in the monitor, it will not only print out the hex values, but also the ASCII values of each byte. I modified his patches a little, to shorten the code to the following:

     1000 *---------------------------------
     1010 *    PATCHES TO ADD ASCII DUMP
     1020 *    TO THE APPLE MONITOR
     1030 *---------------------------------
     1040 A1L    .EQ $3C
     1050 COUT   .EQ $FDED
     1060 *---------------------------------
     1070        .OR $FDB8
     1080        .TA $0DB8
     1090        JSR PATCH     CALL MY PATCH CODE
     1100 *---------------------------------
     1110        .OR $FCC9
     1120        .DA $0CC9
     1130 PATCH
     1140        JSR COUT      PRINT A SPACE
     1150        LDA (A1L),Y   GET BYTE TO BE DISPLAYED
     1160        PHA           SAVE IT ON STACK
     1170        LDA A1L       LOW BYKTE OF DUMP ADDRESS
     1180        AND #7        MASK LINE POSITION
     1190        CLC
     1200        ADC #41       COMMPUTE HORIZONTAL OFFSET
     1210        TAY
     1220        PLA           GET BYTE FROM STACK
     1230        STA ($28),Y   STORE IT ON THE SCREEN
     1240        LDY #0        RESTORE Y
     1250        RTS           REJOIN ORIGINAL CODE

These patches will work with either the old monitor ROM, or the Autostart ROM. The JSR PATCH line goes right into the hex dump program, over the top of a JSR COUT that printed a space. That space is normally printed right before the next byte value is printed in hex. The address of the next byte is kept in A1L,A1H ($3C,3D). The Y-register has 0 in it.

The main patch subroutine is stored on top of part of the cassette tape I/O, at $FC99; it begins with the JSR COUT that was covered up at $FDB8. Lines 1150,1160 pick up the byte to be displayed and save it on the stack. Lines 1170-1210 compute the horizontal position for poking the byte on the screen. The low-order three bits of the memory address determine which column will be used, from column 31 through 38. Lines 1220,1230 retrieve the byte from the stack and store it into the screen buffer. Lines 1240,1250 restore Y=0 and return to the hex dump subroutine.

Note that this patch does not "print" the ASCII codes on the screen; it "pokes" them. Therefore if your printer is on, the printed copy will only contain the hex dump. The ASCII codes will only appear on the screen.

How do you patch the RAM card version of the monitor? Here's how I did it:

1. Load the language card using your DOS 3.3 Master Disk, or whatever technique you like to use.
2. Turn on the language that is in the card (using FP or INT).
3. BSAVE MONITOR,A$F800,L$800.
4. BRUN ASMDISK 4.0
5. BLOAD MONITOR,A$800
6. Enter the source code for the patches and assemble them with the ASM command. This will patch the monitor copy which you loaded at A$800 in step 5.
7. Type "$C081 C081" to write enable the language card.
8. Type "$F800<800.FFFM" to move the patched monitor into the real monitor space.
9. Type "BSAVE <your file name>,A$D000,L$3000" to save the combined language and monitor for later loading into the language card.

If you really do want to burn a new monitor ROM, follow the instructions with your ROM Burner.

Applesoft GOTO from Assembly Language | Bob Sander-Cederlof

Bob Potts called the other day with an interesting question. Suppose you want to jump to a particular line (by line number) of an Applesoft program, rather than simply returning from an assembly language program.

For example, I might call an assembly language subroutine at $300 with "CALL 768". After it does its job, the subroutine may decide either to return to the following Applesoft statement by an "RTS" instruction, or to GOTO a particular line number in the program. (Perhaps an error processing subroutine in the Applesoft code.) Can it be done?

Yes, and it is fairly simple. First we need to put the binary value of the line number into locations $50 and $51. Then we must jump to $D944 in the Applesoft ROMs to finish the GOTO operation. Here is the code to jump to line number 1350, for example:

         GOTO1350 LDA #1350   LOW BYTE OF "1350"
                  STA $50
                  LDA /1350   HIGH BYTE OF "1350"
                  STA $51
                  JMP $D955   APPLESOFT GOTO PROCESSOR

That's all there is to it!

I wrote a tiny little subroutine to demonstrate that this works. It expects to find the line number in $2FE and $2FF. You can POKE it there before CALLing 768. Here is my subroutine:

     1000  *---------------------------------
     1010  *      GO TO <LINE #>
     1020  *      POKE THE LINE # INTO 766,767
     1030  *      AND CALL768 TO GO TO IT
     1040  *---------------------------------
     1050         .OR $300
     1060  GOTO   LDA $2FE
     1070         STA $50
     1080         LDA $2FF
     1090         STA $51
     1100         JMP $D944

Now here is a test program in Applesoft. Can you tell what it will do before you try it? The first two lines poke in the GOTO subroutine. The next five lines call the subroutine for successive values 1000, 2000, 3000 etc. up to 9000. The code in line 10000 jumps back to line 140 to continue the loop. Try it!

       10  FOR I = 0 TO 12: READ A: POKE 768 + I,A: NEXT
       20  DATA 173,254,2,133,80,173,255,2,133,81,76,68,217
      100  FOR I = 1000 TO 9000 STEP 1000
      110  IH =  INT (I / 256): IL = I - IH * 256
      120  POKE 766,IL: POKE 767,IH
      130  CALL 768
      140  NEXT I
      150  END
     1000  PRINT 1000: GOTO 10000
     2000  PRINT 2000: GOTO 10000
     3000  PRINT 3000: GOTO 10000
     4000  PRINT 4000: GOTO 10000
     5000  PRINT 5000: GOTO 10000
     6000  PRINT 6000: GOTO 10000
     7000  PRINT 7000: GOTO 10000
     8000  PRINT 8000: GOTO 10000
     9000  PRINT 9000
    10000  POKE 766,140: POKE 767,0: CALL 768

Apple Assembly Line is published monthly by S-C SOFTWARE, P. O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $12 per year in the U.S.A., Canada, and Mexico. Other countries add $12/year for extra postage. Back issues are available for $1.20 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.)