Volume 1 -- Issue 4 | January 1981

There are, as of Christmas Eve, 179 of you subscribing to the Apple Assembly Line! Last month I wondered if circulation could double, from 85, but we did even better! Also, several stores have decided to carry the AAL for sale like a magazine. We are growing a lot faster than I predicted, and I like it!

In This Issue...

- How to Move Memory
- Computed GOSUB for Applesoft
- Putting COPY into S-C Assembler II
- EDIT Command for S-C Assembler II

First "Disk of the Quarter"

Every three months I collect onto one disk all the source programs published in AAL for the quarter. QD#1 (for October, November, and December of 1980) is now available, for $15. You can save a lot of typing.

If you would like to help promote the newsletter, here is a nice offer: you sign up four new subscribers, and send me their mailing addresses and money, and I will send you a "Disk of the Quarter" FREE and POSTPAID!

Those Compatible Disassemblers

Bob Zant and Bob Kovacs both report that their new two-pass disassemblers are selling well. Well enough to warrant advertising again! Have you bought a copy yet?

TAB Locations in S-C Assembler II Version 4.0

For some reason, people are always asking me where the tab stops are kept, because they want to change them. The old version 3.2 manual gives the patch locations for the three tab stops, but they are different in version 4.0. You will find them at:

                   column  location
           1st tab   14    $140D:0B
           2nd tab   18    $1411:0F
           3rd tab   27    $1402:18

Note that the value stored in memory is three less than the column number.

How to Move Memory | Bob Sander-Cederlof

One of the most common problems in assembly language programming is the problem of moving data from one place in memory to another.

Moving Little Blocks: If you only need to move one or two bytes of data from one place to another in memory, it is easy. You might do it like this:

           LDA SOURCE
           STA DEST
           LDA SOURCE+1
           STA DEST+1

Or, if the A-register was busy but X and Y were not, you might write:

           LDX SOURCE
           LDY SOURCE+1
           STX DEST
           STY DEST+1

If you know ahead of time exactly how many bytes you want to move, and exactly where you want it copied from and to, you can write a very fast loop. For example, suppose I know that I want to copy 20 bytes from BUFFER1 into BUFFER2, and that there is no overlap. Then I can write:

           LDX #19
    LOOP   LDA BUFFER1,X
           STA BUFFER2,X
           DEX
           BPL LOOP
           ...

The loop moves the last byte first, then the next-to-last, and so on until the first byte in BUFFER1 is moved into BUFFER2. If it is important to move them in the opposite direction (first byte first, last byte last), you can change the loop this way:

           LDX #0
    LOOP   LDA BUFFER1,X
           STA BUFFER2,X
           INX
           CPX #20
           BCC LOOP
           ...

Terminating the loop can be done in various ways. The two examples above do it with a count in the X-register. Another way is to use a data sentinel. For example, the last byte to be moved, and only the last byte, might contain the value $00, or $FF, or anything you choose. Then after moving a byte, you can check to see if the sentinel byte was just moved. If it was, you are finished moving. Here is an example using a sentinel of $00:

           LDX #-1
    LOOP   INX
           LDA BUFFER1,X
           STA BUFFER2,X
           BNE LOOP
           ...

Pascal Language promoters often recommend the sentinel technique; however, in Assembly Language, you must be very careful if you plan to use it. The sentinel you choose today may become a valid data value tomorrow!

Moving Bigger Blocks: All of the examples so far will only work if the total number of bytes to be moved is less than 256. What if you need to move a larger block?

When I need to move a large block of data from one place to another, I frequently use the MOVE subroutine in the Apple Monitor ROM. It starts at $FE2C, and looks like this:

         FE2C- B1 3C    MOVE LDA (A1L),Y  MOVE (A1...A2)
         FE2E- 91 42         STA (A4L),Y    TO (A4)
         FE30- 20 B4 FC      JSR NSTA4
         FE33- 90 F7         BCC MOVE
         FE35- 60            RTS

The subroutine NXTA4 (at $FCB4) increments A4L,A4H ($42,43), which is the destination address. Then it compares A1L,A1H ($3C,3D) to A2L,A2H ($3E,3F); the result of the comparison is left in the Carry Status bit: Carry is set if A1 is greater than or equal to A2. Finally, the subroutine increments A2L,A2H ($3E,3F).

To use the MOVE subroutine, you have to set the starting address of the block to be copied into $3C,3D; the last address of the block to be copied into $3E,3F; and the starting address of the destination into $42,43. You also need to be sure that the Y-register contains zero before you start. Here is an example:

           LDY #0          CLEAR Y-REGISTER
           LDA #BUFFER1    START ADDRESS OF SOURCE
           STA $3C
           LDA /BUFFER1
           STA $3D
           LDA #BUFFER1.END  END ADDRESS OF SOURCE
           STA $3E
           LDA /BUFFER1.END
           STA $3F
           LDA #BUFFER2    START ADDRESS OF DESTINATION
           STA $42
           LDA /BUFFER2
           STA $43
           JSR $FE2C
           ...

Because it is there, the Monitor MOVE subroutine is handy. But it is not a general subroutine. If the source and destination blocks overlap, you may get funny results. For example, if I try to move the data between $1000 and $10FF up one byte in memory, so that it runs from $1001 to $1100, the MOVE subroutine will not work. Instead, it will copy the contents of $1000 into every location from $1001 through $1100.

The MOVE subroutine is also not very fast. Anyway, it is not as fast as it could be. Steve Wozniak evidently wrote with size in mind (to make it fit in ROM) rather than speed.

The Applesoft ROMs contain several subroutines for moving data around in memory. Here is one used during execution to move the array table up to make room for a new simple variable:

         1000  *---------------------------------
         1010  *      BLTU -- FROM THE APPLESOFT ROM
         1020  *      $D393 THROUGH $D3D5
         1030  *---------------------------------
         1040  *      ON ENTRY:
         1050  *          Y,A AND HIGHDS CONTAIN DESTINATION END + 1
         1060  *          LOWTR CONTAINS LOWEST ADDRESS OF SOURCE
         1070  *          HIGHTR CONTAINS HIGHEST SOURCE ADDRESS + 1
         1080  *---------------------------------
         1090  *      PAGE-ZERO VARIABLE NAMES FROM "THE APPLE ORCHARD"
         1100  *      VOL. 1, NO. 1, PAGES 12-18.
         1110  STREND .EQ $6D,6E   TOP OF ARRAY STORAGE
         1120  HIGHDS .EQ $94,95   BLTU'S DESTINATION POINTER
         1130  HIGHTR .EQ $96,97   BLTU'S SOURCE END POINTER
         1140  LOWTR  .EQ $9B,9C   BLTU'S SOURCE START POINTER
         1150  *---------------------------------
         1160  REASON .EQ $D3E3    CHECK IF ENOUGH MEMORY
         1170  *---------------------------------
         1180  BLTU   JSR REASON   BE SURE (Y,A) < FRETOP
         1190         STA STREND   NEW TOP OF ARRAY STORAGE
         1200         STY STREND+1
         1210         SEC          COMPUTE # OF BYTES TO BE MOVED
         1220         LDA HIGHTR
         1230         SBC LOWTR
         1240         STA $5E      SAVE PARTIAL PAGE AMOUNT
         1250         TAY          ALSO IN Y
         1260         LDA HIGHTR+1
         1270         SBC LOWTR+1
         1280         TAX          NUMBER OF WHOLE PAGES IN X
         1290         INX
         1300         TYA          # BYTES IN PARTIAL PAGE
         1310         BEQ .4       NO PARTIAL PAGE
         1320         LDA HIGHTR   BACK UP HIGHTR BY PARTIAL PAGE #
         1330         SEC
         1340         SBC $5E
         1350         STA HIGHTR
         1360         BCS .1
         1370         DEC HIGHTR+1
         1380         SEC
         1390  .1     LDA HIGHDS   BACK UP HIGHDS BY PARTIAL PAGE #
         1400         SBC $5E
         1410         STA HIGHDS
         1420         BCS .3
         1430         DEC HIGHDS+1
         1440         BCC .3       ...ALWAYS
         1450  .2     LDA (HIGHTR),Y
         1460         STA (HIGHDS),Y
         1470  .3     DEY
         1480         BNE .2       LOOP TO END OF THIS 256 BYTES
         1490         LDA (HIGHTR),Y  MOVE ONE MORE BYTE
         1500         STA (HIGHDS),Y
         1510  .4     DEC HIGHTR+1    DOWN TO NEXT BLOCK OF 256
         1520         DEC HIGHDS+1
         1530         DEX          PAGE COUNT
         1540         BNE .3
         1550         RTS

Since this code moves from the end of the block backwards, it will safely move a block up in memory. However, it would not be save to use with an overlapping range down in memory; it will do the same thing as the Monitor MOVE subroutine.

The Applesoft subroutine is faster than the Monitor subroutine, because the least significant half of the pointer is kept in the Y-register instead of in page-zero of memory. The INY instruction takes only two cycles, whereas an INC instruction takes five. The three cycles saved in moving each byte add up to nearly 25 milliseconds in moving 8K bytes. The extra overhead of setting up the pointers is more than paid for.

Additional time is saved in the termination test. Instead of testing after moving every byte with a LDA, CMP, LDA, SBC sequence, the number of full 256-byte blocks to be moved is put in the X-register; only a DEX instruction once out of every 256 bytes is needed. This saves over 100 millisecondes in moving an 8K block. By putting the incrementing and testing code in line, rather than in a subroutine like NXTA4, we save the JSR and RTS time. This amounts to another 100 milliseconds in moving an 8K block.

A General Move Subroutine: Can we write a subroutine which will move a block of data from one place to anothere regardless of overlap and direction? Of course! All we have to do is test at the beginning for direction, and choose which method to use accordingly.

Here is a fast subroutine which will move any block of memory anywhere you want. You call it by putting the starting address of the source block in A1L,A1H; the end address of the source in A2L,A2H; and the start address of the destination in A4L,A4H. (This is the same way you set up the MOnitor MOVE subroutine.) I wrote it to be used with the control-Y monitor command.

         1000  *---------------------------------
         1010  *      GENERAL MOVE SUBROUTINE
         1020  *---------------------------------
         1030  *      BRUN THE PROGRAM TO SET UP AS CONTROL-Y
         1040  *      MONITOR ROUTINE
         1050  *---------------------------------
         1060  *      USE LIKE MONITOR MOVE SUBROUTINE:
         1070  *      A1L,A1H -- SOURCE START ADDRESS
         1080  *      A2L,A2H -- SOURCE END ADDRESS
         1090  *      A4L,A4H -- DESTINATION START ADDRESS
         1100  *---------------------------------
         1110  BLOCK.SIZE .EQ $00,01
         1120  A1L    .EQ $3C
         1130  A1H    .EQ $3D
         1140  A2L    .EQ $3E
         1150  A2H    .EQ $3F
         1160  A4L    .EQ $42
         1170  A4H    .EQ $43
         1180  CONTROL.Y  .EQ $3F8
         1190  *---------------------------------
         1200  CONTROL.Y.SETUP
         1210         LDA #$4C     JMP OPCODE
         1220         STA CONTROL.Y
         1230         LDA #GENERAL.MOVE
         1240         STA CONTROL.Y+1
         1250         LDA /GENERAL.MOVE
         1260         STA CONTROL.Y+2
         1270         RTS
         1280  *---------------------------------
         1290  GENERAL.MOVE
         1300         PHA          SAVE REGISTERS
         1310         TYA
         1320         PHA
         1330         TXA
         1340         PHA
         1350         INC A2L      BUMP END ADDRESS ONCE
         1360         BNE .1
         1370         INC A2H
         1380  .1     SEC          COMPUTE SIZE OF BLOCK
         1390         LDA A2L
         1400         SBC A1L
         1410         STA BLOCK.SIZE
         1420         LDA A2H
         1430         SBC A1H
         1440         STA BLOCK.SIZE+1
         1450         TAX
         1460         INX          NUMBER OF BLOCKS TO MOVE
         1470         LDA A1L      DETERMINE DIRECTION
         1480         CMP A4L
         1490         LDA A1H
         1500         SBC A4H
         1510         BCC .2       A1 < A4
         1520         JSR MOVE.DOWN
         1530         JMP .3
         1540  .2     JSR MOVE.UP
         1550  .3     PLA          RESTORE REGS
         1560         TAX
         1570         PLA
         1580         TAY
         1590         PLA
         1600         RTS
         1610  *---------------------------------
         1620  MOVE.DOWN
         1630         LDY #0
         1640         DEX          ANY WHOLE BLOCKS LEFT?
         1650         BEQ .2       NO
         1660  .1     LDA (A1L),Y  MOVE 256 BYTES
         1670         STA (A4L),Y
         1680         INY
         1690         BNE .1
         1700         INC A1H      POINT AT NEXT BLOCK
         1710         INC A4H
         1720         DEX          ANY MORE WHOLE BLOCKS?
         1730         BNE .1       YES
         1740  .2     LDX BLOCK.SIZE   ANY EXTRA BYTES IN A SHORT BLOCK?
         1750         BEQ .4       NONE LEFT
         1760  .3     LDA (A1L),Y
         1770         STA (A4L),Y
         1780         INY
         1790         DEX
         1800         BNE .3
         1810  .4     RTS
         1820  *---------------------------------
         1830  MOVE.UP
         1840         CLC          COMPUTE DESTINATION END + 1
         1850         LDA A4L
         1860         ADC BLOCK.SIZE
         1870         STA A4L
         1880         LDA A4H
         1890         ADC BLOCK.SIZE+1
         1900         STA A4H
         1910         LDY #0
         1920         BEQ .3       ...ALWAYS
         1930  *---MOVE A WHOLE BLOCK------------
         1940  .1     LDA (A2L),Y  MOVE BYTES 255 THRU 1 IN BLOCK
         1950         STA (A4L),Y
         1960  .2     DEY
         1970         BNE .1
         1980         LDA (A2L),Y  MOVE LOWEST BYTE IN BLOCK
         1990         STA (A4L),Y
         2000  .3     DEC A2H
         2010         DEC A4H
         2020         DEX          ANY MORE BLOCKS?
         2030         BNE .2       YES
         2040  *---MOVE SHORT BLOCK IF ANY-------
         2050         LDX BLOCK.SIZE
         2060         BEQ .5       NONE LEFT
         2070  .4     DEY
         2080         LDA (A2L),Y
         2090         STA (A4L),Y
         2100         DEX
         2110         BNE .4
         2120  .5     RTS

A Computed GOSUB for Applesoft | Bob Sander-Cederlof

How many times I have wished for one! I guess I am spoiled from FORTRAN and Apple Integer BASIC. The Computed GOTO is also left out, but I saw that one written up in a recent newsletter. The author said he didn't know how to do the Computed GOSUB, so here it is!

         1000  *---------------------------------
         1010  *      &GOSUB <EXPRESSION>
         1020  *---------------------------------
         1030  TKN.GOSUB  .EQ $B0
         1040  *---------------------------------
         1050  AS.SYNCHR  .EQ $DEC0
         1060  AS.MEMCHK  .EQ $D3D6
         1070  AS.TXTPTR  .EQ $B8,B9
         1080  AS.LINNUM  .EQ $50,51
         1090  AS.FRMNUM  .EQ $DD67
         1100  AS.GOTO1   .EQ $D941
         1110  AS.NEWSTT  .EQ $D7D2
         1120  AS.GETADR  .EQ $E752
         1130  *---------------------------------
         1140         .OR $300
         1150  VARIABLE.GOSUB
         1160         LDA #TKN.GOSUB  CHECK IF &GOSUB
         1170         JSR AS.SYNCHR
         1180         LDA #3          CHECK IF ROOM ON STACK
         1190         JSR AS.MEMCHK
         1200         LDA AS.TXTPTR+1
         1210         PHA             STACK TXTPTR
         1220         LDA AS.TXTPTR
         1230         PHA
         1240         LDA AS.LINNUM+1
         1250         PHA             STACK CURRENT LINE NO.
         1260         LDA AS.LINNUM
         1270         PHA
         1280         LDA #TKN.GOSUB  MARK STACK
         1290         PHA
         1300         JSR AS.FRMNUM   EVALUATE FORMULA
         1310         JSR AS.GETADR   CONVERT TO INTEGER
         1320         JSR AS.GOTO1    USE GOTO CODE
         1330         JMP AS.NEWSTT

Lines 1160 and 1170 check the token after the "&" to see if it is "GOSUB"; if not, you will get a big SYNTAX ERROR. Lines 1180 and 1190 check the stack to see if there is room for another GOSUB entry; if not, you get an OUT OF MEMORY error. Lines 1200-1290 push the data on the stack that will be needed to RETURN. Lines 1300 and 1310 compute the value of whatever expression follows the &GOSUB, and turn it into an integer that looks just like a line number. Finally, lines 1320 and 1330 simulate a normal GOTO. That's all there is to it!

Here is a sample Appplesoft program using the new &GOSUB statement:

          10 POKE 1013,76: POKE 1014,0: POKE 1015,3
          20 INPUT X
          30 &GOSUB x*100
          40 GOTO 20

         100 PRINT 100:RETURN
         200 PRINT 200:RETURN
         300 PRINT 300:RETURN
         400 PRINT 400:RETURN

Putting COPY in S-C Assembler II | Lee Meador

I just looked at the first AAL Disk of the Quarter. The first item of business was to incorporate the changes into my copy of the assembler.

The lower-case mod and the .DA mod went just as described in AAL. However, when it came to the COPY stuff, I found that I wasn't really happy to load it at $800 and hope it didn't get clobbered. Here's what I did....

I changed the origin of the COPY program to $25A0 (since I already have a special printer driver at $2500.259F). The COPY program runs from $25A0 through $266F, so I changed the symbol table origin by typing "$1011:27". This sets the bottom of the symbol table at $2700. I put a ".TF B.SC COPY MODS" line in, to write the object on a binary file.

After assembling, I BLOADed the file B.SC COPY MODS into memory. Then I could have plugged in the USR vector like Bob suggested, but I wanted a real "COPY" command. Therefore I searched around in the assembler until I found the command table. I put the letters "COP" and the program address over the top of the tape SAVE command entry, by typing "1246:43 4F 50 9F 25". I felt the loss of the tape SAVE command was worth it, to get a real COPY command.

Now the command "COPY 1000,1050,2500" will copy lines 1000 through 1150 into the place right before line 2500. The USR command is still intact and I'm ready for some more changes!

EDIT Command for S-C Assembler II | Mike Laumer

At last! Owners of the S-C Assembler II Version 4.0 can now have the power of an EDIT command similar in function to the popular "Program Line Editor" (PLE) by Neil Knozen. (PLE only works with Integer BASIC and Applesoft, although some wizards have figured out how to interface it with the S-C Assembler.) The program presented here will patch itself into Version 4.0 to turn the "USR" command into an EDIT command.

Several weeks ago Bob Sander-Cederlof contacted me about some contract programming, to help out on various projects he had in mind. So I suggested lunch, and we met to discuss some of his projects. I was amazed at the list (as long as my arm!) of the the ideas for just one of his products, the S-C Assembler II. (If you like version 3.2, as I did; if you are thrilled with version 4.0, as I am; then version 5.0 will ....) So I picked out a couple that would be fairly straightforward and would let me pick up the internal structure of the assembler gradually.

After signing a non-disclosure agreement, I obtained the source files and made a listing of the assembler. Lucky for me I have a brand new Epson MX-80 printer! I think it is the greatest!

Thursday, I made the listing. Friday I looked at the listing. Friday night I began writing code for the EDIT command. Saturday from 9AM till 1AM I wrote more code, read it through, and rewrote it. Sunday morning I typed it into my Apple and eliminated the assembly errors (typos). And by 11AM, with the exception of two trivial bugs, I had it working! I nearly fell out of my chair! A 377-line program worked on the first run!

After you type in the program, assemble it, and BRUN it, the USR command will work as an edit command. If you type the command USR with no line number, it will do nothing. If you type USR and one line number, it will list the line on the bottom of the screen and set yo up to edit it. If you type USR and two line numbers, separated by a comma, all the lines in the range will be set up to edit, one at a time.

How to Use EDIT: Twelve editing functions are available, and you may see fit to add some more. Each function is selected by typing a control character. If you type a normal character, it will write over the top of the characters already in the line. The control characters and their associated functions are:

         control-B  Move to beginning of line.
         control-D  Delete character beneath cursor.
         control-E  Move to end of line.
         control-F  Find a character; the character searched for
                    is typed after the control-F; repeatedly
                    typing the same character will keep looking
                    successive occurrences.
         control-H  Backspace (left arrow).
         control-I  Insert characters before current cursor
                    position.
         control-M  (RETURN)  Stop editing the line,
                    and submit it to the line input routine
                    in the assembler.
         control-O  Same as control-I, except next character
                    may be any control character.
         control-Q  same as control-M, but line beyond cursor
                    is truncated.
         control-T  Skip to next tab stop.
         control-U  (Right Arrow)  Move cursor forward.
         control-X  Kill edit, does not submit line.

How EDIT Works: When you BRUN the file B.EDIT (after assembly has written the object code there!), the code in lines 1360-1530 is executed. This patches the USR command vector to jump to EDIT (line 1720), and makes some patches inside the assembler. The patches only work for version 4.0! Their purpose is to make the code which processes a source line into a subroutine.

Lines 1540-1620 are part of the patch code for the source line processing subroutine.

Lines 1720-2040 determine the number of line numbers typed, and search for them in the source program. Then E.LIST is called for each line to be edited.

Lines 2050-2360 list the source line on the screen and also stuff it into the line input buffer at $0200. All changes will be made in the buffer, not in the source program.

Lines 2370-2530 read a key from the keyboard and search the command table. If the key is found in the table, then DOIT is called to execute the command. If the key is not found, I assume it is a type-over character. The command table search is actually performed by a rather neat subroutine inside the assembler, called SEARCH.

Lines 2540-2690 process a type-over character, in which the key just typed replaces the character under the cursor. Then the modified line in the buffer is re-displayed on the screen.

Lines 2700-2750 position the cursor at the beginning of line 19 (on the screen), where the source line will be listed.

Lines 2760-2900 display the line from the buffer. Display always starts at line 19 on the screen. Control characters are shown in inverse video.

Lines 2910-4090 process the various commands. Each processor is written as a subroutine. The RTS returns to line 2520; at this point the Carry Status is used to flag whether or not to re-display the source line from the buffer.

Lines 4100-4260 read a character from the keyboard by calling on the monitor RDKEY subroutine. The internal line buffer index is also converted to cursor line and column position on the screen.

         1000  *---------------------------------
         1010  *  EDIT COMMAND FOR S-C ASSEMBLER II VERSION 4.0
         1020  *
         1030  *      WRITTEN BY MIKE LAUMER
         1040  *                 DECEMBER 6, 1980
         1050  *---------------------------------
         1060         .OR $0800
         1070         .TF B.EDIT2
         1080  *---------------------------------
         1090  *  SYSTEM EQUATES
         1100  *---------------------------------
         1110  MON.COUT   .EQ $FDED
         1120  MON.BELL   .EQ $FF3A
         1130  MON.RDKEY  .EQ $FD0C
         1140  MON.CLREOP .EQ $FC42
         1150  MON.VTAB   .EQ $FC22
         1160  CH     .EQ $24
         1170  CV     .EQ $25
         1180  DOS.REENTRY .EQ $03D0
         1190  *---------------------------------
         1200  *  ASSEMBLER EQUATES
         1210  *---------------------------------
         1220  GNL    .EQ $1026
         1230  NML    .EQ $1063
         1240  PLNO   .EQ $1779
         1250  GNB    .EQ $12C5
         1260  DOIT   .EQ $1874
         1270  SEARCH .EQ $164B
         1280  SERTXT .EQ $14F6
         1290  SERNXT .EQ $14FE
         1300  NTKN   .EQ $12AF
         1310  A0L    .EQ $3A,3B
         1320  A1L    .EQ $3C,3D
         1330  SRCP   .EQ $DD,DE
         1340  WBUF   .EQ $0200
         1350  CURRENT.LINE.NUMBER .EQ $D3,D4
         1360  *---------------------------------
         1370  *  ENTRY POINT FOR BRUN.  ACTIVATES
         1380  *  THE USR ASSEMBLER COMMAND.
         1390  *---------------------------------
         1400  ENTRY  LDA #EDIT
         1410         STA $1007    PATCH ASM USR COMMAND
         1420         LDA /EDIT
         1430         STA $1008
         1440         LDA #$60     PATCH NML TO MAKE IT
         1450         STA $1125        A SUBROUTINE
         1460         LDA #$4C
         1470         STA NML
         1480         STA $1078
         1490         LDA #NEW.NML
         1500         STA NML+1
         1510         LDA /NEW.NML
         1520         STA NML+2
         1530         JMP DOS.REENTRY
         1540  *---------------------------------
         1550  *  PATCH ROUTINES FOR ASSEMBLER
         1560  *---------------------------------
         1570  NEW.NML JSR MY.NML
         1580         JMP GNL
         1590  MY.NML LDY #0
         1600         JSR $128D
         1610         JSR $114A
         1620         JMP $1066
         1630  *---------------------------------
         1640  *  LOCAL VARIABLES FOR EDIT COMMAND
         1650  *---------------------------------
         1660  NEXT   .DA 0
         1670  END    .DA 0
         1680  CHAR   .DA #0
         1690  EDPTR  .DA #0
         1700  FKEY   .DA #0
         1710  *---------------------------------
         1720  EDIT   DEX
         1730         DEX
         1740         BMI .2       NO ARGUMENTS
         1750         BEQ .4       1 ARGUMENT
         1760         JSR .3       2 ARGUMENTS
         1770         LDX #A1L     FIND END PTR
         1780         JSR SERNXT
         1790         LDA $E6
         1800         STA END
         1810         LDA $E7
         1820         STA END+1
         1830  .1     LDA NEXT+1
         1840         STA SRCP+1
         1850         PHA
         1860         LDA NEXT
         1870         STA SRCP
         1880         CMP END
         1890         PLA
         1900         SBC END+1    PAST END LINE?
         1910         BCS .2       YES, EXIT
         1920         JSR E.LIST   NO, LIST AND EDIT
         1930         JMP .1       TRY FOR NEXT LINE
         1940  .3     LDX #A0L     FIND START PTR
         1950         JSR SERTXT
         1960         LDA $E4
         1970         STA SRCP
         1980         STA NEXT     SAVE NEXT LINE ADRS
         1990         LDA $E5
         2000         STA SRCP+1
         2010         STA NEXT+1
         2020  .2     RTS
         2030  .4     JSR .3       SEARCH FOR LINE
         2040         BCC .2       NOT FOUND EXIT
         2050  E.LIST JSR E.POSN   POSITION FOR EDIT
         2060         JSR MON.CLREOP  PREPARE DISPLAY
         2070         JSR GNB      GET LINE SIZE
         2080         CLC
         2090         ADC NEXT     COMPUTE NEXT LINE ADRS
         2100         STA NEXT
         2110         TYA
         2120         ADC NEXT+1
         2130         STA NEXT+1
         2140         JSR GNB      GET LINE NUMBER FOR DISPLAY
         2150         STA CURRENT.LINE.NUMBER
         2160         JSR GNB
         2170         STA CURRENT.LINE.NUMBER+1
         2180         SEC
         2190         ROR $F8      STUFF WBUF FLAG
         2200         JSR PLNO
         2210         LSR $F8      TURN OFF FLAG
         2220         LDA #$20     SPACE AFTER LINE #
         2230         LDX #0
         2240  .1     STX EDPTR
         2250         ORA #$80     FORCE VIDEO BIT
         2260         STA WBUF+4,X STORE INTO INPUT BUFFER
         2270         CMP #$A0     TEST FOR CONTROL CHAR
         2280         BCS .2       OK, IF NOT
         2290         AND #$7F     OUTPUT INVERSE ALPHA
         2300  .2     JSR MON.COUT PRINT CHAR
         2310         JSR NTKN     GET NEXT TOKEN
         2320         LDX EDPTR
         2330         INX
         2340         CMP #0       END TOKEN?
         2350         BNE .1       NO, PRINT IT
         2360         STA WBUF+4,X YES, PUT IT IN TOO
         2370  E.LINE LDX #0
         2380  E.0    STX EDPTR
         2390  E.1    JSR E.INPUT  GET INPUT CHAR
         2400  E.2    LDA #EDTB
         2410         STA $2
         2420         LDA /EDTB
         2430         STA $3
         2440         LDA #CHAR
         2450         STA $12
         2460         LDA /CHAR
         2470         STA $13
         2480         JSR SEARCH   SEARCH EDIT COMMAND TABLE
         2490         BNE .2       NOT IN TABLE
         2500         LDX EDPTR
         2510         JSR DOIT     EXECUTE COMMAND ROUTINE
         2520         BCC E.0      NO DISPLAY ON RETURN
         2530         BCS .5       DISPLAY ON RETURN
         2540  .2     LDX EDPTR    MUST BE TYPE OVER
         2550         LDA CHAR
         2560         CMP #$A0
         2570         BCS .4
         2580  .3     JSR MON.BELL ERR IF CONTROL KEY
         2590         JMP E.1
         2600  .4     LDA WBUF+5,X SEE IF END OF LINE
         2610         BNE .6       TYPE OVER IF NOT
         2620         STA WBUF+6,X SHIFT OVER END OF LINE
         2630  .6     LDA CHAR     STUFF CHAR INTO BUFFER
         2640         STA WBUF+5,X
         2650         CPX #256-5-2 TEST BUFFER SIZE
         2660         BEQ .5       TYPE OVER LAST CHAR IN BUFFER
         2670         INX          INSTEAD OF BUFFER END
         2680  .5     JSR E.DISP   DISPLAY LINE
         2690         JMP E.0      GET NEXT EDIT COMMAND
         2700  *---------------------------------
         2710  E.POSN LDA #19      POSITION TO LINE 19,
         2720         STA CV
         2730         LDA #0       COLUMN 0
         2740         STA CH
         2750         JMP MON.VTAB
         2760  *---------------------------------
         2770  E.DISP STX EDPTR
         2780         JSR E.POSN   POSITION DISPLAY
         2790         LDX #$FF
         2800  .1     INX
         2810         LDA WBUF,X   GET BUFFER CHAR
         2820         BEQ .3       END OF BUFFER
         2830         CMP #$A0     CONTROL CHAR?
         2840         BCS .2       NO
         2850         AND #$7F     PRINT INVERSE ALPHA
         2860  .2     JSR MON.COUT PRINT CHAR
         2870         JMP .1       NEXT CHAR
         2880  .3     JSR MON.CLREOP CLEAN ANY REMAINING SCREEN
         2890         LDX EDPTR
         2900         RTS
         2910  *---------------------------------
         2920  E.BEG  LDX #0   SET CURSOR TO BEGINNING OF LINE
         2930         CLC
         2940         RTS
         2950  *---------------------------------
         2960  E.DEL  LDA WBUF+5,X IS THIS THEN END OF
         2970         BEQ .2
         2980  .1     INX
         2990         LDA WBUF+5,X SHIFT TO LOWER MEMORY
         3000         STA WBUF+4,X TO DELETE CHAR
         3010         BNE .1
         3020         LDX EDPTR
         3030  .2     SEC          RETURN WITH DISPLAY
         3040         RTS
         3050  *---------------------------------
         3060  E.END  LDA WBUF+5,X END OF BUFFER?
         3070         BEQ .1       YES
         3080         INX          NO
         3090         BNE E.END    TRY END AGAIN
         3100  .1     CLC          RETURN NO DISPLAY
         3110         RTS
         3120  *---------------------------------
         3130  E.FIND LDA WBUF+5,X END OF BUFFER?
         3140         BNE .2       NO
         3150  .1     STA FKEY     YES SO ERR
         3160         JSR MON.BELL RING BELL
         3170         CLC          RETURN NO DISPLAY
         3180         RTS
         3190  .2     JSR E.INPUT  GET 1 CHAR
         3200         STA FKEY     SAVE KEY TO LOCATE
         3210  .3     INX
         3220         LDA WBUF+5,X TEST BUFFER
         3230         BEQ .1       END OF BUFFER
         3240         CMP FKEY     NO, SEE IF KEY
         3250         BNE .3       NO, GO FORWARD
         3260         JSR E.INPUT  TRY ANOTHER KEY
         3270         CMP FKEY     SAME CHAR?
         3280         BEQ .3       YES, SEARCH AGAIN
         3290         PLA
         3300         PLA
         3310         STX EDPTR    NO, EXIT POINTING HERE
         3320         JMP E.2
         3330  *---------------------------------
         3340  E.BKSP TXA          AT BEGINNING?
         3350         BEQ .1       YES, STAY THERE
         3360         DEX          BACKUP
         3370  .1     CLC          RETURN NO DISPLAY
         3380         RTS
         3390  *---------------------------------
         3400  E.OVR  JSR E.INPUT  READ CHAR
         3410         JMP E.INS1   SKIP CONTROL CHECK
         3420  *---------------------------------
         3430  E.INS  JSR E.INPUT  READ CHAR
         3440         CMP #$A0     CONTROL CHAR POPS USER OUT
         3450         BCC E.INS2   OF INSERT
         3460  E.INS1 CPX #256-5-2 END OF BLOCK
         3470         BEQ .1       YES STAY THERE
         3480         INX
         3490  .1     STX EDPTR
         3500  .2     PHA          CHAR TO INSERT
         3510         LDA WBUF+4,X SAVE CHAR TO MOVE
         3520         TAY
         3530         PLA          GET CHAR TO INSERT
         3540         STA WBUF+4,X PUT OVER SAVED CHAR
         3550         INX
         3560         TYA          INSERT SAVED CHAR
         3570         BNE .2       IF NOT BUFFER END
         3580         STA WBUF+4,X STUFF END CODE
         3590         STA WBUF+256-5-1  INSURE A  END CODE
         3600         LDX EDPTR
         3610         JSR E.DISP   DISPLAY LINE
         3620         JMP E.INS    GET NEXT INSERT CHAR
         3630  E.INS2 PLA          SEND CHAR TO
         3640         PLA          COMMAND SEARCH
         3650         LDX EDPTR
         3660  *---------------------------------
         3670         JMP E.2
         3680  E.RETQ LDA #0       CLEAR REST OF LINE
         3690         STA WBUF+5,X
         3700         JSR E.DISP   DISPLAY LINE
         3710  E.RET  LDX #$FF     SUBMIT LINE TO ASSEMBLER
         3720  .1     INX          COMPUTE LINE SIZE
         3730         LDA WBUF,X
         3740         BNE .1
         3750         DEX
         3760  .2     STX $E1      SAVE SIZE
         3770         PLA
         3780         PLA
         3790         JMP MY.NML   SUBMIT THE LINE
         3800  *---------------------------------
         3810  E.TAB  CPX #20      < COL 20?
         3820         BCS .1       NO
         3830         LDA WBUF+5,X END OF BUFFER?
         3840         BEQ .1       YES
         3850         INX          MOVE FORWARD
         3860         CPX #7       TAB MATCH?
         3870         BEQ .1
         3880         CPX #11      TAB MATCH?
         3890         BNE E.TAB
         3900  .1     CLC          RETURN WITHOUT DISPLAY
         3910         RTS
         3920  *---------------------------------
         3930  E.RIT  LDA WBUF+5,X END OF BUFFER
         3940         BNE .1       NO
         3950         STA WBUF+6,X
         3960         LDA #$A0     PUT A BLANK
         3970         STA WBUF+5,X TO EXTEND LINE
         3980         CPX #256-5-2
         3990         BEQ .2
         4000  .1     INX          MOVE AHEAD
         4010  .2     CLC          RETURN NO DISPLAY
         4020         RTS
         4030  *---------------------------------
         4040  E.ABORT LDA #$DC    OUTPUT BACKSLASH
         4050         STA WBUF+5
         4060         LDA #0
         4070         STA WBUF+6
         4080         JSR E.DISP   SHOW CANCEL
         4090         JMP GNL      GET NEXT COMMAND
         4100  *---------------------------------
         4110  E.INPUT LDA #19
         4120         STA CV
         4130         TXA          POSITION TO CURSOR
         4140         CLC
         4150         ADC #5
         4160  .1     CMP #40      THIS LINE?
         4170         BCC .2       YES
         4180         SEC
         4190         SBC #40
         4200         INC CV       ON NEXT LINE
         4210         BNE .1
         4220  .2     STA CH
         4230         JSR MON.VTAB SET BASL
         4240         JSR MON.RDKEY INPUT A CHAR
         4250         STA CHAR
         4260         RTS
         4270  *---------------------------------
         4280  *  COMMAND TABLE
         4290  *---------------------------------
         4300  EDTB   .DA #3,#1    ITEM SIZE, KEY SIZE
         4310   .DA #$82,E.BEG-1   ^B
         4320   .DA #$84,E.DEL-1   ^D
         4330   .DA #$85,E.END-1   ^E
         4340   .DA #$86,E.FIND-1  ^F
         4350   .DA #$88,E.BKSP-1  ^H
         4360   .DA #$89,E.INS-1   ^I
         4370   .DA #$8D,E.RET-1   ^M
         4380   .DA #$8F,E.OVR-1   ^O
         4390   .DA #$91,E.RETQ-1  ^Q
         4400   .DA #$94,E.TAB-1   ^T
         4410   .DA #$95,E.RIT-1   ^U
         4420   .DA #$98,E.ABORT-1 ^X
         4430   .DA #0

Lines 4270 through the end are the command table. The first line defines the entry size and key size for the SEARCH subroutine; 3 bytes per entry, with a one byte key at the fron of each entry. The remaining two bytes of each entry are the starting-address-minus-one of the command processor rotuine. A final $00 byte terminates the table.

WARNING! I have used the patch for Bob's assembler which allows a list of .DA items! Lines 4270-4420 require this patch to be installed. You can read about the patch in Apple Assembly Line for December, 1980, on page 9. If you have not installed the patch, then lines 4270-4420 need to be re-written with each .DA item on a separate source line.

Well, you better get typing on that Apple, I know this is one routine you can't wait to key in. I know I couldn't wait to create it! Or, if you CAN wait, you can get the source on the next Disk of the Quarter from Bob.

Apple Assembly Line is published monthly by S-C SOFTWARE, P. O. Box 5537, Richardson, TX 75080. Subscription rate is $12/year, in the U.S.A., Canada, and Mexico. Other countries add $6/year for extra 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.)