In This Issue...
Cross Assemblers continue to appear. We now have ready a version for the Intel 8048, and one for the yet unreleased Rockwell 65C02. More on the latter inside.
Don Lancaster, famous author of many books published by Howard Sams, says the Apple II is probably going to have a greater impact on history than the automobile or television! Perhaps verging on Apple-olatry, but you will surely enjoy his new book. See Bill's reviews inside.
If I am trying to learn how to program in assembly language, or to increase my skill at it, what (besides AAL) should I read? I strongly recommend Softalk, Nibble, Micro, and Call APPLE. Every month they publish excellent examples of assembly language programs which you can study, modify, and use. As for books, Roger Wagner's, Lance Leventhal's, and Don Lancaster's are my favorites at this time.
A machine language routine is said to be relocatable if it can function properly regardless of its absolute location in memory. If a routine contains a JMP or a JSR to an INTERNAL address then it is not relocatable; if it is run in another part of memory then the internal JSR or JMP will still reference the former region of memory. JMPs and JSRs to subroutines at absolute locations (e.g. in the Monitor) do not impair the relocatability of a routine.
I will explain here a technique whereby you can, in effect, perform internal JSRs and JMPs without impairing the relocatability of your routine. There is a small price to pay for this: namely, an increase in the length of your routine. Your routine must be preceded by a 2-part Header Routine which is 43 bytes long. In addition, each internal JSR requires 8 bytes of code, and each internal JMP requires 11 bytes of code.
No tables or other data storage are required, except that three bytes must be reserved for a JMP instruction. These three bytes can be anywhere in memory, but must be at an absolute location. There are three bytes that normally are used only by Applesoft, namely, the ampersand JMP vector at $3F5 to $3F7. Since we are here concerned only with machine language routines in their own right, we can use the locations $3F5 to $3F7 for our own purposes. However, other locations would do just as well.
The technique is fully illustrated in the accompanying assembly language program. This routine consists of three parts:
(1) Header Part 1 (SETUP), which sets up a JMP instruction at VECTOR (at $3F5-$3F7, but could be different, as explained above) to point to Header Part 2.
(2) Header Part 2 (HANDLER), which is a 15-byte section of code whose task is to handle requests to perform internal JSRs and JMPs (more on this below).
(3) The main part of the routine, in which internal JSRs and JMPs (in effect) are performed using macro instructions.
When your routine (including the Header) is executed, the first thing that happens is that Header Part 1 locates itself (using the well-known JSR $FF58 technique), then places a JMP HANDLER at VECTOR. Thereafter a JMP VECTOR is equivalent to JMP HANDLER, and a JSR VECTOR is equivalent to a JSR HANDLER. The HANDLER routine handles requests from your routine for internal JSRs and JMPs. To perform a JSR to an internal subroutine labelled SUBR simply include the following code:
HERE LDA #SUBR-HERE-7 low byte of offset LDY /SUBR-HERE-7 high byte of offset TSX JSR VECTOR |
As explained above, the JSR VECTOR is in effect a JSR HANDLER. The Header Part 2 code takes the values in the A and Y registers and adds them to an address which it obtains from the stack to obtain the address of SUBR. It then places this address in INDEX ($5E,5F) and executes "JMP (INDEX)".
An internal JMP, from one part of your routine to another, is performed in a similar manner. Suppose you wish to JMP from HERE to THERE. It is done as follows:
HERE LDA #THERE-HERE-7 low byte of offset LDY /THERE-HERE-7 high byte of offset TSX JSR $FF58 JMP VECTOR |
Since we are (in effect) performing a JMP, rather than a JSR, we do a JMP VECTOR rather than a JSR VECTOR. The other difference is that we have a JSR $FF58 following the TSX.
Clearly the sequence of instructions which allows you to perform a JMP or a JSR could be coded as a macro. The macros to use are shown in the accompanying program listing. By using macros an internal JMP or JSR can be performed with a single macro instruction bearing a very close resemblance to a real JSR or JMP instruction.
The following program, which consists of the Header Routine plus a demonstration routine, can be assembled to disk using the .TF directive. It can then be BRUN at any address and it will function properly. Thus it is relocatable, despite the fact that there are (in effect) an internal JMP and two internal JSRs performed.
When performing an internal JSR or JMP using my techniques, it is not possible to pass values in the registers, since these are required to pass information to the HANDLER routine. Nor is it advisable to try to pass parameters on the stack, even though the HANDLER routine does not change the value of the stack pointer. Better is to deposit values in memory and retrieve them after the transition has been made.
The HANDLER routine passes control to the requested part of your routine using a JMP indirect. (INDEX at $5E,5F, has been used in the accompanying program, but any other address would do as well, provided that it does not cross a page boundary.) This means that the section of your routine to which control is passed (whether or not it is a subroutine) may find its own location by inspecting the contents of the location used for the JMP indirect. This feature of this technique is also illustrated in the accompanying program, in the PRINT.MESSAGE subroutine.
The use of internal data blocks is something not normally possible in a relocatable routine, but it can be done if the techniques shown here are used.
This method of performing internal JSRs and JMPs in a relocatable routine may be simplified if the routine is intended to function as a subroutine appended to an Applesoft program. If the subroutine is appended using my utility the Routine Machine (available from Southwestern Data Systems), then it is not necessary to include the 47-byte Header Routine. Internal JMPs and JSRs can still be performed exactly as described above, except that the address of VECTOR must be $3F5-$3F7. This technique is not described in the documentation to the Routine Machine. A full explanation may be found in the Appendix to the documentation accompanying Ampersoft Program Library, Volume 4 (also available from Southwestern Data Systems).
1010 .TF B.MEYER.1 1020 *SAVE S.MEYER.1 1030 *-------------------------------- 1040 * SETUP AND HANDLER ROUTINES 1050 * TO ALLOW INTERNAL JSRS AND 1060 * JMPS IN A RELOCATABLE MACHINE 1070 * LANGUAGE ROUTINE 1080 1090 * BY PETER MEYER, 11/3/82 1100 *-------------------------------- 1110 * LOCATIONS 1120 1130 INDEX .EQ $5E,5F 1140 STACK .EQ $100 - $1FF 1150 VECTOR .EQ $3F5 - $3F7 1160 *-------------------------------- 1170 * MACRO DEFINITIONS 1180 1190 .MA JSR 1200 :1 LDA #]1-:1-7 1210 LDY /]1-:1-7 1220 TSX 1230 JSR VECTOR 1240 .EM 1250 1260 .MA JMP 1270 :1 LDA #]1-:1-7 1280 LDY /]1-:1-7 1290 TSX 1300 JSR $FF58 1310 JMP VECTOR 1320 .EM 1330 *-------------------------------- 1340 * HEADER PART 1 1350 1360 SETUP JSR $FF58 FIND OURSELVES 1370 TSX 1380 CLC 1390 LDA #HANDLER-SETUP-2 1400 .DA #$7D,STACK-1 FORCE ABS,X MODE 1410 STA VECTOR+1 1420 1430 LDA /HANDLER-SETUP-2 1440 ADC STACK,X 1450 STA VECTOR+2 1460 1470 LDA #$4C "JMP" 1480 STA VECTOR 1490 BNE MAIN.ROUTINE ALWAYS 1500 *-------------------------------- 1510 * HEADER PART 2 1520 1530 HANDLER 1540 1550 * ON ENTRY A,Y HOLD OFFSET 1560 * FOR JMP OR JSR FROM ROUTINE 1570 * X IS STACK POINTER FROM BEFORE LAST JSR 1580 1590 CLC 1600 .DA #$7D,STACK-1 FORCE ABS,X MODE 1610 STA INDEX 1620 TYA 1630 ADC STACK,X 1640 STA INDEX+1 1650 JMP (INDEX) 1660 *-------------------------------- 1670 * MAIN ROUTINE, FOR EXAMPLE 1680 *-------------------------------- 1690 MSG .EQ $06 AND $07 1700 CH .EQ $24 1710 CV .EQ $25 1720 INVFLG .EQ $32 1730 COUNT .EQ $3C 1740 SETTXT .EQ $FB39 1750 VTABZ .EQ $FC24 1760 HOME .EQ $FC58 1770 COUT .EQ $FDED 1780 *-------------------------------- 1790 MAIN.ROUTINE 1800 JSR SETTXT 1810 JSR HOME 1820 MAIN.LOOP 1830 LDA #190 1840 STA COUNT 1850 .1 LDA #AALQT-PRINT.MESSAGE 1860 STA MSG 1870 LDA /AALQT-PRINT.MESSAGE 1880 STA MSG+1 1890 >JSR PRINT.MESSAGE 1900 DEC COUNT 1910 BNE .1 1920 LDA #LONGQT-PRINT.MESSAGE 1930 STA MSG 1940 LDA /LONGQT-PRINT.MESSAGE 1950 STA MSG+1 1960 >JSR PRINT.MESSAGE 1970 >JMP FORWRD 1980 1990 *-------------------------------- 2000 PRINT.MESSAGE 2010 CLC 2020 LDA MSG CHANGE RELATIVE ADDRESS TO 2030 ADC INDEX AN ABSOLUTE ADDRESS, BY 2040 STA MSG ADDING THE OFFSET 2050 LDA MSG+1 2060 ADC INDEX+1 2070 STA MSG+1 2080 LDY #0 POINT AT FIRST CHAR OF MSG 2090 .1 LDA (MSG),Y GET NEXT CHAR OF MSG 2100 BMI .2 IT IS LAST CHAR 2110 ORA #$80 MAKE APPLE VIDEO FORM 2120 JSR COUT PRINT IT 2130 INY ADVANCE POINTER 2140 BNE .1 ...ALWAYS 2150 .2 JMP COUT PRINT AND RETURN 2160 *-------------------------------- 2170 * 256 BYTES TO JUMP OVER, JUST FOR ILLUSTRATION 2180 2190 .BS $100 2200 *-------------------------------- 2210 * TOGGLE INVERSE FLAG, AND HOME CURSOR 2220 2230 FORWRD LDA INVFLG 2240 EOR #$C0 2250 STA INVFLG 2260 LDA #0 2270 STA CH 2280 STA CV 2290 JSR VTABZ 2300 >JMP MAIN.LOOP 2310 *-------------------------------- 2320 AALQT .AT /AAL / 2330 LONGQT .HS 0D0D 2340 .AS / A P P L E A S S E M B L Y L I N E / 2350 .HS 0D02 2360 .AT / S - C S O F T W A R E C O R P . / |
The other day someone sent me a disk with an article for AAL on it as a binary file. The codes in the file were all ASCII characters, but they were nevertheless not compatible with any word processors I had around.
All blanks were coded as $A0 (hi-bit on), and all other characters were coded in the range $00-$7F (hi-bit off). Otherwise, everything was compatible with my favorite word processor (the one I am still in the process of finishing).
I need to have all the hi-bits set to one in the file, or in the memory image after BLOADing the file. That's the motivation for the following neat enhancement to the Apple monitor.
The enhancement hooks in through the control-Y user command vector. By merely typing:
*80FF<2000.3FFF^Y (^Y means control-Y)
I set all the hi-bits between $2000 and $3FFF.
The "80FF" in the command line above is the magic part of this enhancement. The last two digits are ANDed with every byte in the specified range, clearing every bit which is zero in those two digits. By using $FF, no bits are cleared. Other values for these two digits will clear whatever bits you wish.
The first two digits are ORed into every byte in the specified range, setting every bit which is one in the two digits. $80 in my example sets the top bit in every byte.
The code is designed to be BRUN from a binary file, and it is completely relocatable. You can BRUN it anywhere in memory that you have room for 36 bytes. That is why the SETUP code is longer than the actual control-Y code!
The SETUP routine first discovers where in memory it is running, and then sets up the control-Y vector in page 3 to point at the BITS code. The discovery is done in the usual way, by JSRing to a guaranteed RTS in the monitor ROM, at $FF58. This leaves a return address just below the stack pointer. I pick up that address and add the difference between that and BITS to get the appropriate control-Y vector pointer.
Line 1200 needs a little explanation. My assembler automatically switches to page zero addressing mode if the address is less than $100. STACK-1 is less than $100, so "ADC STACK-1,X" would assemble as though I wrote "ADC $FF,X". Indexed addressing in page zero mode stays in page zero, wrapping around. If X=3, "ADC $FF,X" would reference location $02 in page zero rather than $102. Therefore I had to use the ".DA #$7D" to force the assembler to use a full 16-bit address mode. (Some assemblers have a special way of forcing 16-bit addressing in cases like this; others require special marks to get zero-page modes.)
BITS itself is very straightforward code. The monitor leaves the starting address of the specified range in A1 ($3C,3D), the ending address in A2 ($3E,3F), and the mask in A4 ($42,43). The subroutine at $FCBA increments A1 and compares it to A2, leaving carry clear if the range is not yet complete.
1000 *-------------------------------- 1010 * MONITOR CTRL-Y COMMAND 1020 * 1030 * TO SET AND CLEAR ANY COMBINATION 1040 * OF BITS IN A RANGE OF MEMORY 1050 * 1060 * *MASK<ADR1.ADR2Y (WHERE Y MEANS CTRL-Y) 1070 * 1080 * MASK = XXYY BITS = 0 IN YY WILL BE CLEARED 1090 * BITS = 1 IN XX WILL BE SET 1100 *-------------------------------- 1110 A1 .EQ $3C 1120 A4L .EQ $42 1130 A4H .EQ $43 1140 STACK .EQ $100 1150 *-------------------------------- 1160 SETUP JSR $FF58 FIND SELF FIRST 1170 TSX 1180 CLC 1190 LDA #BITS-SETUP-2 1200 .DA #$7D,STACK-1 FORCE ABS,X MODE 1210 STA $3F9 MONITOR CTRL-Y VECTOR 1220 LDA /BITS-SETUP-2 1230 ADC STACK,X 1240 STA $3FA 1250 RTS 1260 *-------------------------------- 1270 BITS LDA (A1),Y GET NEXT BYTE IN SPECIFIED RANGE 1280 AND A4L CLEAR BITS USING LO-BYTE OF MASK 1290 ORA A4H SET BITS FROM HI-BYTE OF MASK 1300 STA (A1),Y STORE MODIFIED BYTE 1310 JSR $FCBA ADVANCE POINTER 1320 BCC BITS MORE IN RANGE 1330 RTS FINISHED 1340 *-------------------------------- |
Today Jules Gilder called, asking for patches to allow sending the assembly listing to a TEXT file. He is in the process of writing a book, and wanted the listings on TEXT files so they could be automatically typeset.
My first response was a familiar one: "It is a very difficult problem, because of the interaction of .IN and .TF files."
"But I don't need .TF or .IN files!", he swiftly parried.
Suddenly in a flash of insight I saw that it could be EASILY done. All that is needed is to patch the .TF directive so that it opens a TEXT file, but does not set the TF-flag. Then all listing output will go to the text file, and the object code will go to the current origin or target address.
Here are the patches for the motherboard version:
:$2998:A5 60 D0 03 20 6A 2A 4C 04 1F
And the language card version:
:$C083 C083 EAE4:A5 60 D0 03 20 B6 EB 4C 50 E0
Note that the two "C083's" above write-enable the language card so the patches will be effective.
After the patches are installed, a .TF directive will direct the listing to your text file. Here is an example:
.TF T.LISTING SAMPLE LDA #3 STA $06 RTS
Just remember that the normal use of .TF is not available when this patch is in place; also that .IN should not be used. Using .IN would turn off the listing output, directing it back to the screen.
To merge selected bits from one byte with the rest of the bits of another byte:
Code Example LDA MASK 00011111 EOR #$FF 11100000 ORA BYTE1 111xxxxx STA TEMP LDA BYTE2 yyyzzzzz ORA MASK yyy11111 AND TEMP yyyxxxxx
Since most of the owners of ES-CAPE also subscribe to the Apple Assembly Line, I thought I would pass on some neat patches here.
The automatic line numbering feature in ES-CAPE can be enhanced by the following patches, which make the numbers track whatever you type. With these patches, each time an Applesoft line is changed via hitting return or via a CHANGE command, that line number plus the current increment becomes the next automatic line number to be generated when the space bar is pressed. (Now ES-CAPE works more like the S-C Macro Assembler.)
Here are the patches for the mother-board version:
LOAD ES-CAPE CALL -151 E44:69 00 EA 102E:4C 51 9B 1C51:A5 51 8D 34 9B 18 A5 50 6D 35 9B 8D 33 9B :90 03 EE 34 9B A5 25 F0 02 C6 25 4C 34 8F 3D0G SAVE ES-CAPE REVISED
Here are the patches for the RAM-card version:
LOAD ES-CAPE LC CALL -151 E41:69 00 EA 102B:4C 60 E1 1B60:A5 51 8D 51 E1 18 A5 50 6D 52 E1 8D 50 E1 :90 03 EE 51 E1 A5 25 F0 02 C6 25 4C 31 D6 3D0G SAVE ES-CAPE LC REVISED
I believe these patches make ES-CAPE even easier to use. If any of you still have not upgraded your AED II copies to ES-CAPE, send me $10 and your old disk and I'll return a new version and the great new manual.
I am continuing to work on ES-CAPE, hoping for a new version around the middle of next year. What new features would you like? The main ones we have in mind are 80-column support, renumber, and merge. If you have some good ideas, drop me a line at 3199 Hammock Creek, Lithonia, GA 30058.
To test a byte in memory without disturbing any registers:
INC BYTE DEC BYTE Restore value, and test against zero BEQ ....
For several years I have been working on this. I even bought an assembler from another company just to get the promised source code of Applesoft that came in the package, but I was very disappointed. (No complaints, it was intended as a "freebie" with their assembler.) I wanted LOTS of comments, MEANINGFUL labels, and I wanted it in the format of the S-C Macro Assembler.
Now I have it. And you can have a copy, on two diskettes, for only $50. It comes in an encrypted form, with a driving program which creates the source code files on your machine with the aid of the Applesoft image in ROM or RAM. The encryption is meant to protect the interests of Apple and Microsoft.
You need two disk drives to assemble Applesoft, a printer to get a permanent listing, and of course you need the S-C Macro Assembler. The source code is split into more than a dozen source files, assembled together using a control file full of .IN directives. After assembling and printing, you will have well over 100 pages of the best documentation of Applesoft internals available anywhere.
In the process of writing the comments, I discovered some very interesting bugs. Some have been published before, and others I have never heard of. Just for fun, try this:
]A%=-32768 (you get an error message, correctly) ]A%=-32768.00049 (No error message!) ]PRINT A% (You get 32767!)
Or open your disk drive doors, just in case, and type:
]LIST 440311
That last one can be disastrous! Any use of a six-digit line number (illegal, but not caught by Applesoft) between 437760 and 440319 will cause a branch to a totally incorrect place. For example, GOTO 440311 branches to $22D9!
The causes of these and other interesting phenomena, as well as some suggested improvements resulting in smaller/faster code, are documented in my comments.
To shift a two-byte value right one bit with sign extension:
LDA HI.BYTE ASL SIGN BIT INTO CARRY ROR HI.BYTE HI BYTE RIGHT ONE, CARRY (SIGN) INTO BIT 7 ROR LO.BYTE
Nigel Nathan from Micro-Mixedware in Stow, MA, sent me some copies of reference material for the new 65C02 chip. This is the enhanced CMOS version, soon to be available from GTE and Rockwell.
Nigel's interest was that I might produced an enhanced S-C Macro Assembler to accommodate the new opcodes and addressing modes. I not only might...I did it right away! It is ready now, although you may have some difficulty getting the chips for a few more months.
Rockwell is sampling the 65C02 now, and scheduled for production in February. Rockwell is also readying an entire family of CMOS products to go with the 65C02, including 2Kx8 CMOS static RAM and multi-byte parallel interfaces.
The 65C02 is expected to be plug-compatible with the 6502 in your Apple II. In fact, Cliff Whitaker of Rockwell told me that the first chips they made were tested by plugging them into Apples. Hopefully you will be able to simply plug them in and start using the new opcodes. If true, then I will probably become a source for these chips.
What enhancements did they make? According to the GTE document, some "bugs" in the 6502 design were corrected:
The Rockwell literature makes reference to the following new opcodes, or new addressing modes for old opcodes:
80 BRA rel Branch Always 12 ORA (zp) ) 32 AND (zp) ) 52 EOR (zp) ) new addressing mode: 72 ADC (zp) ) 92 STA (zp) ) zero-page indirect B2 LDA (zp) ) D2 CMP (zp) ) without indexing F2 SBC (zp) ) 04 TSB zp Test and set bits 14 TRB zp Test and reset bits 34 BIT zp,X new addressing mode for BIT 64 STZ zp Store Zero 74 STZ zp,X " " 07-77 RMB b,zp Reset bit b in zp 87-F7 SMB b,zp Set bit b in zp 89 BIT imm new addressing mode for BIT 1A INC Increment A-register 3A DEC Decrement A-register 5A PHY Push Y 7A PLY Pull Y DA PHX Push X FA PLX Pull X 0C TSB abs Test and set bits 1C TRB abs Test and reset bits 3C BIT abs,X new addressing mode for BIT 7C JMP (abs),X new addressing mode for JMP 9C STZ abs Store zero 9E STZ abs,X Store zero 0F-7F BBR b,zp,rel Branch if bit b in zp is zero 8F-FF BBS b,zp,rel Branch if bit b in zp is one
Let your imagination run wild with all the great ways to use these new opcodes! If you feel the need for the ability to assemble them now, the Cross Assembler upgrade for the 65C02 is available for $20 to subscribers of the Apple Assembly Line who already own the S-C Macro Assembler.
[ Psychology Dept., Univ. of So. Dakota, P. O. Box 7187, Grand Forks, ND 58202 ]
I have made the necessary modifications to the assembler (and to my Apple) that allow me to enter lower case characters in my source programs, but have found that I like to have all upper case in certain sections (the labels and opcodes) and mixed (mostly lower) case only in the comment field. In order to do accommodate this most effectively, I wanted to be able to toggle back and forth from upper to lower case while I was entering my source code.
The simplest solution for me was to patch the assembler to accept one of the escape key sequences as an upper/lower case toggle. From back issues of AAL I was able to determine that a table of address vectors for the escape keys A-L is maintained from $1467 thru $1482 ($D467 thru $D482 in the language card version). Each two-byte entry is the address-1 (low byte first) of the routine that will handle that particular escape sequence.
Certain of the sequences are already taken (e.g. ESC L loads a disk file; ESC I,J,K, and M move the cursor, etc.) Since I don't use the ESC A,B,C,D cursor moves, I selected the ESC A sequence as the code for the case toggle. I also used ESC C for "CATALOG", as suggested by Bill Morgan some months back in these pages.
Implementation of the toggle is accomplished with the following patches to the HELLO program (for the RAM version of the assembler.) First, line 50 should be changed to:
50 PRINT A: IF A=1 THEN PRINT CHR$(4)"BLOAD S-C.ASM.MACRO" :GOSUB 200: CALL 4096 |
The subroutine at 200 is as follows:
197 REM 198 REM ESC A TOGGLES UP/LOW CASE 199 REM 200 POKE 5229,109:POKE 5230,165 210 FOR I=1 TO 9:READ J:POKE 48350+I,J:NEXT 220 DATA 173,22,16,73,255,141,22,16,96 230 RETURN 240 REM 250 REM ROUTINE RESIDES AT $BCDF 260 REM 270 REM CODE IS AS FOLLOWS: 280 REM 290 REM LDA $1016 300 REM EOR #FF 310 REM STA $1016 320 REM RTS 330 REM |
Note that I put the patch code at $BCDF, which is in an unused area inside DOS 3.3. If you have already used this area for other purposes, you can tack the patch on to the end of the assembler image instead.
The actual code is very simple. The upper/lower case flag is stored at $1016 and is either a $00 or a $FF (in binary all zeros or all ones.) Toggling the flag involves loading the current flag and EORing it with #$FF. This will cause all set bits to be cleared and all clear bits to be set, so the zeros become ones and the ones become zeros. Thus, an #$FF byte becomes a #$00 or a #$00 becomes an #$FF. The new flag value is then stored back in $1016.
For the language card version the program is basically the same, but slightly longer due to the need to first write enable the language card. The code looks like this:
PATCH LDA $C083 Two of these in succession LDA $C083 write-enable the card LDA $D016 Get the flag EOR #$FF Complement it STA $D016 Save the new flag LDA $C080 Write protect the card RTS |
I put the code in the same place as in the RAM version ($BCDF) and put it into memory from the LOAD LCASM exec file which loads the assembler onto the card. Two lines need to be added to the file. Between lines 1070 and 1080 (assuming your version has not been modified) you need to place these two lines:
1072 D469:DE BC 1074 BCDF:AD 83 C0 AD 83 C0 AD 16 D0 49 FF 8D 16 D0 AD 80 C0 60
The first line places the address of the case toggle handler in the escape vector table and the second line contains the assembled source code listed above. If you are not sure how to modify the LOAD LCASM file see the step by step description given in the May 1982 AAL (page 3).
After you have made the patch, experiment with the toggle. One particularly valuable feature is that you can toggle the case within a single line as you enter the line. This means that you can enter the label and opcode in upper case, tab over to the comment field, toggle the case flag, and then enter your comment in lower case.
I have found using the case toggle to be easy while improving the appearance and readability of my source listings. The only problem I have encountered so far is that the flag can not be toggled from within the edit mode (either case can be used in the edit mode, but you can't change the case in the middle of editing.) If any of you find a way to add this to the assembler be sure to let me know.
[ P.S. If you haven't put in the automatic catalog yet, here is an easy way. Add the following line to your LOAD LCASM file:
1076 D46D:6D A5
and then ESC C will do a catalog as long as you don't mind having to hit return at the end of the catalog. For the motherboard version, add:
205 POKE 5225,222: POKE 5226,188
in the subroutine I've added to the HELLO program. ]
{Ouch! Why didn't I think of that? One caution: With this method ESC C will do a CATALOG even if you are in the middle of typing a line. . . . Bill Morgan}
As we all know, Applesoft programs which use a significant number of strings can appear to die for long periods of time while "garbage collection" is performed. Many techniques have been published to reduce the frequency of garbage collection, or reduce the amount of garbage to be collected, or to speed up the collector.
Randy Wiggington published a much faster garbage collector in "Call-A.P.P.L.E.", January, 1981, pages 40-45. The source code is available in S-C format on the Dallas Apple Corps disk of the month for March, 1981. (Copies available for $10 from me.) Randy's speed improvement is gained by searching for the highest 16 strings in memory at once, rather than just the highest one string. It is much faster, but not the fastest. The time for collection still varies quadratically with the number of strings in use.
Cornelius Bongers wrote the fastest collector I have seen. It was published in "MICRO -- The 6502/6809 Journal", August, 1982, pages 90-97. Corny's method is very straightforward, and has the advantage that execution time varies linearly with the number of strings in use. His method also has the disadvantage that it does not work if any strings contain any characters with the high bit on. (Applesoft normally does not generate any strings with high-bit-set-characters, but you can do it with oddball code.) I typed in the program from MICRO, made a few changes here and there, and found it to be lightning fast.
I installed the linear method in a client's program, and his garbage collection time went from 100 seconds after printing every four lines, to only 1/4 second. Other changes, such as the one described below, cut the interval of collection to once every 12 lines.
It so happens that strings which are empty do not increase the garbage collection time. Many times in Applesoft programs a string array is filled with data from a disk file, processed, and then filled with fresh data, and so on. By emptying the array before each pass at reading the disk file, the number of active strings can be greatly reduced.
One of my programs was like this: the one that prints your mailing label so that the AAL gets to you every month. Before reading each file (the list is divided into about 12 different files, based on zip code and other factors), I wrote a loop that set each string in the array to a null string, and then forced garbage collection:
FOR I = 1 TO NH : A$(I)="" : NEXT : F = FRE(0)
The only problem with this was that the loop takes so long! About ten seconds, anyway, enought to try my patience.
All the above motivated me to write the following little subroutine, which nulls out (or empties) a string array. Bongers included an array eraser in his article, which completely released an array; however, that requires re-DIMming the array each time. My program is faster in my case, and it was fun to write.
The program is listed below with the origin set at $300, so "CALL 768,arrayname" will activate it. It happens to be fully relocatable, so you can load it anywhere in memory you wish. You could easily add it to your Applesoft program with Amper-Magic or Amperware or The Routine Machine.
I used three subroutines inside the Applesoft ROMs. CHKCOM gives SYNTAX ERROR unless the next character is a comma. I use it to check for the comma that separates the call address from the array name. CHKSTR checks to make sure that the last-parsed variable is a string variable, and gives TYPE MISMATCH if not. GETARYPT scans an array name and returns the address of the start of the array in variable space.
If you look at page 137 of your Applesoft reference manual, you will see in the third column a picture of a string array. (Notice first the error: the signs of the first and second bytes of the string name are just the reverse of what the book says.) My program looks at the number of dimensions to determine the size of the preamble (the number of bytes before you get to the actual string pointers).
I use the preamble size to compute the starting address of the string pointers, and the number of bytes of string pointers that there are. Then a tight little loop stores zeros on top of all the descriptors.
The Applesoft program below illustrates the CLEAR subroutine in action.
100 A$(25),B$(5) 105 FOR I = 0 TO 5: B$(I) = "ABCD": NEXT 110 FOR I = 0 TO 25: N = INT(RND(1)*5)+1 120 FOR J = 1 TO N: A$(I) = A$(I) + CHR$(RND(1)*26+65): NEXT 130 NEXT: PRINT: PRINT 140 CALL 768,A$ 150 FOR I = 0 TO 25: PRINT LEN(A$(I))":"A$(I),: NEXT 155 PRINT: PRINT 160 FOR I = 0 TO 5: PRINT B$(I),: NEXT 170 GOTO 110 1000 *SAVE S.STRING ARRAY CLEAR 1010 *-------------------------------- 1020 * CLEAR STRING ARRAY 1030 *-------------------------------- 1040 GETARYPT .EQ $F7D9 1050 CHKSTR .EQ $DD6C 1060 CHKCOM .EQ $DEBE 1070 *-------------------------------- 1080 FAC .EQ $9D 1090 LOWTR .EQ $9B,9C 1100 .OR $300 1110 *-------------------------------- 1120 CLEAR 1130 JSR CHKCOM 1140 JSR GETARYPT 1150 JSR CHKSTR 1160 LDY #4 COMPUTE SIZE OF PREAMBLE 1170 LDA (LOWTR),Y # OF DIMS 1180 ASL *2, CLEAR CARRY 1190 ADC #5 +5 1200 STA FAC SAVE PREAMBLE SIZE 1210 LDY #2 POINT AT OFFSET 1220 SEC COMPUTE ARRAY LENGTH 1230 LDA (LOWTR),Y 1240 SBC FAC 1250 PHA # BYTES IN PARTIAL PAGE 1260 INY 1270 LDA (LOWTR),Y 1280 SBC #0 1290 TAX # WHOLE PAGES 1300 CLC POINT AT BEGINNING OF DATA 1310 LDA LOWTR 1320 ADC FAC 1330 STA LOWTR 1340 BNE .2 1350 INC LOWTR+1 1360 .2 LDY #0 1370 TXA # WHOLE PAGES 1380 BEQ .4 1390 TYA SET A=0 1400 .3 STA (LOWTR),Y 1410 DEY 1420 BNE .3 1430 INC LOWTR+1 1440 DEX COUNT WHOLE PAGES 1450 BNE .3 1460 .4 PLA 1470 BEQ .6 FINISHED 1480 TAY 1490 LDA #0 1500 .5 DEY 1510 STA (LOWTR),Y 1520 BNE .5 1530 .6 RTS |
Printing text on the screen, or even on a printer, is not as easy at it ought to be. The problem is splitting words at the right margin. Word processors handle it nicely, but what do you do in an Applesoft program?
You might write a subroutine, in Applesoft, which looks for the first space between words before a specified column position. The subroutine could split a string at the space into two substrings: one containing the next display line, the other the remainder of the original string.
You might. But believe me, it builds up a lot of garbage strings and takes a long time to execute. If you like the general approach, you might try coding the subroutine in assembly language. You can avoid garbage build-up and save lots of running time, so it is probably worth the effort. Especially since I already wrote the program for you!
The program is written to be called from an ampersand parser like the one in last month's article on REPEAT/UNTIL. Or, you can use it with Amper-Magic, Amperware, The Routine Machine, etc. It is fully relocatable, having no internal data or JMP/JSR addresses. I set the origin to $300, but it can be loaded and used anywhere without re-assembly.
Here is an Applesoft program to show how to call SPLIT:
100 POKE 1014,0: POKE 1015,3 120 FOR N = 5 TO 40 STEP 3: GOSUB 1000: NEXT : END 1000 A$ = "NOW IS THE TIME FOR ALL GOOD MEN TO COME TO THE AID OF THEIR PARTY." 1005 & ,A$,B$,N 1010 PRINT B$ 1020 IF A$ < > "" THEN 1005 1025 PRINT 1030 RETURN
Call SPLIT with three parameters. The first (A$ above) is the source string, which will be split. After SPLITting, the remainder string will be left in A$.
The second parameter, B$ above, will receive the left part, including the last complete word, up to N (the 3rd parameter) characters. If there is no space in the left N characters, exactly N characters will be split.
Here are some of the printouts from the test program:
N=5 N=11 N=20 ----- ----------- -------------------- NOW NOW IS THE NOW IS THE TIME FOR IS TIME FOR ALL GOOD MEN TO COME THE ALL GOOD TO THE AID OF THEIR TIME MEN TO COME PARTY. ...etc. ...etc.
1000 *SAVE S.SPLIT 1010 *-------------------------------- 1020 * & SPLIT,A$,B$,W 1030 * 1040 * A$ -- SOURCE STRING 1050 * W -- MAXIMUM WIDTH OF SPLIT 1060 * 1070 * B$ -- LEFT W CHARS OF A$ 1080 * A$ -- REST OF A$ 1090 * 1100 *-------------------------------- 1110 .OR $300 1120 .TF B.SPLIT 1130 *-------------------------------- 1140 LINNUM .EQ $50,51 1150 DPTRA .EQ $06,07 1160 DPTRB .EQ $08,09 1170 SPTRA .EQ $FE,FF 1180 *-------------------------------- 1190 AS.CHKCOM .EQ $DEBE 1200 AS.PTRGET .EQ $DFE3 1210 AS.GETADR .EQ $E752 1220 AS.FRMNUM .EQ $DD67 1230 *-------------------------------- 1240 SPLIT JSR AS.CHKCOM GET COMMA 1250 JSR AS.PTRGET GET SOURCE STRING 1260 STA DPTRA 1270 STY DPTRA+1 1280 JSR AS.CHKCOM ANOTHER COMMA 1290 JSR AS.PTRGET GET TARGET STRING 1300 STA DPTRB 1310 STY DPTRB+1 1320 JSR AS.CHKCOM ANOTHER COMMA 1330 JSR AS.FRMNUM 1340 JSR AS.GETADR GET MAXIMUM WIDTH 1350 LDY #2 1360 LDA (DPTRA),Y 1370 STA SPTRA+1 1380 STA (DPTRB),Y 1390 DEY 1400 LDA (DPTRA),Y 1410 STA SPTRA 1420 STA (DPTRB),Y 1430 DEY 1440 LDA LINNUM 1450 CMP (DPTRA),Y 1460 BCC .1 1470 LDA (DPTRA),Y A$ SHORTER THAN OR SAME AS W 1480 STA (DPTRB),Y 1490 LDA #0 1500 STA (DPTRA),Y 1510 RTS 1520 *-------------------------------- 1530 .1 TAY 1540 .2 LDA (SPTRA),Y LOOK AT SPLIT BOUNDARY 1550 CMP #$20 FOR A BLANK 1560 BEQ .3 FOUND ONE 1570 DEY 1580 BNE .2 BACK UP THE SPLIT 1590 *---NO BLANK IN W CHARS---------- 1600 LDA LINNUM 1610 BNE .4 ...ALWAYS 1620 *-------------------------------- 1630 .3 TYA 1640 INY SKIP OVER BLANK 1650 STY LINNUM 1660 .4 LDY #0 LENGTH OF B$ 1670 STA (DPTRB),Y 1680 SEC 1690 LDA (DPTRA),Y LENGTH OF A$ 1700 SBC LINNUM 1710 STA (DPTRA),Y 1720 INY 1730 CLC 1740 LDA (DPTRA),Y 1750 ADC LINNUM 1760 STA (DPTRA),Y 1770 INY 1780 LDA (DPTRA),Y 1790 ADC #0 1800 STA (DPTRA),Y 1810 RTS 1820 *-------------------------------- |
Don Lancaster, the well-known author of electronics books for the hobbyist (and a subscriber to AAL), has now entered the Apple arena in a big way. His latest book, "Enhancing Your Apple II, Vol. 1", promises to be the start of a long series of easy-to-use guides to the important internal workings of the Apple.
The main enhancements he offers in this volume are simple modifications to the Apple's video circuitry, to allow EXACT software access to the video timing. This permits your program to play all sorts of tricks with the display modes. There is also a wealth of information on the Apple's techniques of video storage and output.
The basic hardware modification is a single wire from an IC in the video circuitry to either the cassette or the game input. With this wire and a little bit of code, it is easy to switch display modes between screen scans, avoiding a lot of messy glitches on the screen. With more code, and careful timing, you can lock the processor to the display timing and switch between text and graphic modes (hi-res or lo-res) in mid-line.
There is also a very good 60-page chapter on disassembling and understanding other people's programs. Don presents a novel technique of color-coding a printout of a monitor disassembly, to bring out the structure of a program and the function of each routine. The example program is Apple's High-Res Character Generator, from the DOS Toolkit. He later uses the information discovered about the character generator and the Hi-Res display to develop a slower and smoother scrolling routine for Hi-Res text.
He shows us other enhancements, as well. There are two different ways to attach a modulator's output line to your TV set, avoiding that clumsy little switch box that the manufacture gives us. How about a programmable color-killer circuit? With this one you can have software control of color vs. black-and-white display. There are sections about generating extra colors, in both Hi-Res and Lo-Res graphics.
In the back of the book are postcards for sending feedback and ordering other materials. All the code in the book (26 programs) can be ordered on diskette, for $14.95. He uses the DOS Toolkit Assembler, but we plan to talk to him about providing the programs in S-C format. You can also order a kit of the parts for all of the hardware modifications he describes. That kit costs only $11.95 + shipping, from a dealer in Oklahoma. Future plans include more volumes of enhancements and a possible bulletin board system for updates to the books.
All in all, "Enhancing Your Apple II" looks to be an important and useful book. Like all of Lancaster's books, it is published by Howard W. Sams. It is 232 pages long, size 8 1/2 X 11 inches, and sells for $15.95. We have ordered a stock here at S-C, and will sell them for $15.00 + postage.
For Volume 2 of the "Enhancing" series, he has promised us more video techniques, a keyboard enhancer, something called an "Adventure Emergency Toolkit", graphics software for daisy-wheel printers, a two-dollar interface for the BSR controller, and much more. I'm looking forward to it!
This is a good time to mention another of Don's books, which has received too little attention. I am speaking of "The Incredible Secret Money Machine". Despite the title, it is not a get-rich-quick pamphlet, but rather a very, very useful guide to starting and operating a free-lance technical or craft business. "Money Machine" is 160 pages of tightly packed information on strategy and tactics, getting started, and dealing with customers, suppliers, and the government.
There is enough practical advice on communication, both verbal and graphic, to make up several courses in advertising and technical writing. Bob and I refer to this book regularly, and have long felt that it is one of the best books around for the budding entrepeneur. We have also ordered "Money Machine", and will sell it for $7.50 + postage.
Last minute addition: We just received a review copy of another new book from Don Lancaster, Micro Cookbook Vol. 1 - Fundamentals. This one is a very basic introduction to microcomputer principles. He talks about how to learn and what to learn, and introduces some hardware fundamentals. He also promises Vol. 2, about Machine-Language Programming. It looks very good; I'll have more details next month. We especially like this sentence at the end of the Preface: This book is dedicated to the 6502.
To print a two byte value in hexadecimal:
LDA HI.BYTE LDX LO.BYTE JSR $F941
Last month Bob S-C told how to use an EXEC command file without ENDing an Applesoft program. His example may have obfuscated the process of loading a file into a RAM card, because it really is not necessary to use an EXEC file for this purpose.
You can BLOAD into the RAM card without leaving Applesoft, contrary to Bob's information, by merely write-enabling it. The soft-switches $C081 and $C089 write-enable the RAM card (with bank 2 or bank 1 at $D000, respectively), leaving the motherboard ROMs read-enabled. This means everything you write goes into the RAM card, and everything you read comes from the motherboard ROMs. Thus you can simply BLOAD into the RAM card, and BLOAD will write to those addresses.
Here is a short program that loads the whole 16K RAM card, all from within a running Applesoft program, without EXEC files.
100 D$ = CHR$ (4) 110 B2 = 49281 : REM $C081 -- SELECT BANK TWO 120 B1 = 49289 : REM $C089 -- SELECT BANK ONE 130 P = PEEK(B2) + PEEK(B2) : REM WRITE ENABLE BANK TWO 140 PRINT D$"BLOAD LC.BANK 2" 150 P = PEEK(B1) + PEEK(B1) : REM WRITE ENABLE BANK ONE 160 PRINT D$"BLOAD LC.BANK 1"
[ Note from Bob S-C: My face is red! Paul will note that I modified his program above in lines 130 and 150. He wrote "130 POKE B2, PEEK(B2)" and similarly for line 150. However, some RAM cards, such as my Andromeda board, will disable write-enable if the soft-switches are addressed during a write-cycle. The POKE does just that; therefore, I changed 130 and 150 to do two PEEKs in a row. Further, I recall when working with a Corvus drive last year that BLOADing from a Corvus into the RAM card did not work unless a monitor had already been copied into the space from $F800-$FFFF. ]
Apple Assembly Line is published monthly by S-C SOFTWARE, P. O. Box
280300, Dallas, TX 75228. Phone (214) 324-2050 Subscription rate is $15 per year,
in the USA, sent Second Class Mail; $18 per year sent First Class Mail in USA,
Canada, and Mexico; $28 per year sent Air Mail to other countries. Back issues
are available for $1.50 each (other countries add $1 per back issue for postage).
All material herein is copyrighted by S-C SOFTWARE, all rights reserved.
Unless otherwise indicated, all material herein is authored by Bob Sander-Cederlof.
(Apple is a registered trademark of Apple Computer, Inc.)