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