Apple Assembly   Line
Volume 2 -- Issue 3 December 1981

In This Issue...


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:

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