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