Apple Assembly   Line
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...

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