In This Issue...
S-C Macro Assembler ///
The Apple /// version of the S-C Macro Assembler is coming right along! I am now selling a preliminary "as is" version for $100. That buys you the assembler, a few pages of documentation about the differences from the Apple ][ version, and free updates until the finished product appears. This is a working assembler for producing free-running programs; it assembles itself just fine. The biggest gap is the ability to produce relocatable modules for Pascal or BASIC. That will be added next. Call or write if you are interested in being among the first to have this new enhancement to the Apple ///.
Zero-Insertion-Force Game Socket Extender
One of the first things I did to my Apple back in 1977 was to plug a ZIF socket into the game connector. Not too easy, because it first has to be soldered to a header, but I did it.
Now I have discovered a source for a ready-made device that does the same thing, plus brings the socket outside the Apple (if you so desire). There's a picture of the device on page 14. For only $20 I'll send you one!
Last month I promised a "reasonably useful" program to add two numbers together from ASCII strings. I promised:
Okay! It took me three days, but I did it! Of course, the program has grown from 12 lines and 26 bytes of code to over 290 lines and over 450 bytes, too.
The program is now assembled to load at $9000, but you can choose other positions by changing line 1130. I set HIMEM:36864 before doing anything else in the Applesoft program, and then BRUN B.STRING ADDER.
When B.STRING ADDER is BRUN, only the setup code in lines 1160-1220 is executed. What this does is link in the ampersand (&) to the body of my program. Once the "&" is linked, my program responds to a call like "& +$,A$,B$,C$" by adding the numeric values represented in ASCII in A$ and B$ and storing the sum as a string in C$.
When an &-line occurs, Applesoft branches to my line 1520. Lines 1520-1600 check for the characters "+$," after the ampersand. If you don't like those characters, change them to something else. Anyway, if the characters do not match, you get SYNTAX ERROR. If they do match, it is time to collect the three strings variables.
Lines 1620-1690 collect the three string variables. The first two are the operands, the third is the result string. I save the address and length of the actual data of the operand strings. All I save at this point for the result string is the address of the variable descriptor. I call the subroutine PARSE.STRING.NAME to check for a leading comma, search for the variable name, and store the length and address of the referenced string data.
Lines 1730-1860 scan each operand string in turn to find the decimal point position. The routine SCAN divides a string at the decimal point (or where the decimal point would be if there was one), and returns in Y the number of characters to the left of the decimal point. SCAN returns in X the count of the number of characters on the right end, including the decimal point. I save the "digits.after" parts of both strings, and also the maxima of the two parts. The maxima describe the result string (almost).
Lines 1900-2000 finish the description of the result string, by lengthening the integral (left) side by two characters. These two characters allow for extension of the result by carry, and for representation of the sign of the result using ten's complement notation. At this point I also clear the necessary bytes of the result to zero, so the buffer can be used as an accumulator.
Now comes the EASY part. Lines 2040-2100 add each operand in turn to the buffer contents. EASY. Just call the subroutine ADD.TO.BUFFER, and it's done! Don't worry, I'll amplify later.
In ten's complement notation, if the first digit is 0-4 the number is positive; if the first digit is 5-9, the number is negative. For example, 1234 looks like 001234; -1234 becomes 998766. Ten's complement means in decimal the same thing two's complement means in binary. I can form the ten's complement by subtracting the number from a power of ten equal to the number of digits in the result. In that example, 1000000-1234=998766. Note that the ten's complement is equal to the nine's complement plus one. (Since 10=9+1.)
Lines 2140-2410 convert the buffer contents from the ten's complement numeric notation back to ASCII. Lines 2140-2180 set or clear the CARRY and TENS.FLAG sign bits according to the first digit in the buffer. A negative number, with a first digit of 5-9, causes both of these variables to get a value of the form 1xxxxxxx.
Lines 2190-2360 scan through the number from right to left, making the ten's complement if the number was negative, and converting each digit to ASCII. Lines 2370-2400 store a minus sign in the first digit position if the result is negative.
Line 2410 calls a subroutine to chop off leading zeros, and move the minus sign if there is one. You may justifiably ask, "Why did you call a subroutine rather than use in-line code?" Because when I wrote it in-line, the local labels stretched out too far from the major label STRADD and caused an assembly error. Also, sometimes I use subroutines for clarity, even when the subroutine is only called once.
The final step is to pack the resulting string up and ship it to the result string variable. Lines 2450-2590 do just that. AS.GETSPA makes room at the bottom of string pool space, and AS.MOVSTR copies the string data. C'est finis!
Lines 2640-3100 do the actual addition. On entry, X is either 0 or 4, selecting either the first or second operand. SETUP.OPERAND copies the string address into VARPNT, and retrieves the length of the string. Lines 2690-2760 set or clear the TENS.FLAG and CARRY variables according to the sign of the operand.
Lines 2780-2810 compute the position in the buffer at which the operand will be aligned properly. We saved the size of the integral (left) side of the buffer in MAX.DIGITS.BEFORE. That plus the lenght of the fractional side of the operand tells us where this operand aligns. Since we are using ten's complement for negative numbers, rather than nine's complement, we don't have to worry about extending the fractional parts to the same length. We can just start adding at the end of the current operand. (In ten's complement form fractional extensions are zeros; in nine's complement form, the extension digits would all be nines.)
Lines 2830-3100 do the addition. X points into the buffer, and Y points into the operand string. To start with, both X and Y point just past the end; therefore the loop BEGINS with a test-and-decrement sequence. I first t-a-d the buffer pointer; if it is zero, all is finished. If not, on to t-a-d the string pointer. If it is zero, there are still digits left in the buffer, so I use an assumed leading zero digit for the operand. We still may have carries to propagate across the rest of the sum.
Assuming neither pointer is zero, line 2900 gets the next digit from the operand string. If it is a decimal point, I just store the decimal point ASCII value into the buffer. If you want to be able to ignore leading blanks, insert the following two lines between line 2920 and 2930:
2924 CMP #' BLANK? 2925 BEQ .3 YES, USE ZERO.
I left them out in my version, because I forgot I promised it to you.
If the character is not a decimal point (or blank), it may be a minus sign or digit. I did not put any error checking in my program for other extraneous characters; if you try them, you will get extraneous results! I treat a sign as a leading zero in the arithmetic loop.
If the character is a digit, or an assumed leading zero, we can add it to the buffer's value. Lines 2960-3010 will complement the digit if the operand had a minus sign. Lines 3020-3070 add the current operand digit (or its complement) to the current buffer digit, plus any carry hung over from the preceding digit, and save the resulting carry in CARRY.
That's it! Now here is a short little Applesoft program to test the code.
100 REM TEST&+$,A$,B$ 110 HIMEM: 36864: PRINT CHR$ (4)"BLOAD B.STRING ADDER": CALL 36864 120 INPUT A$: INPUT B$ 130 & + $,A$,B$,C$ 140 PRINT C$: GOTO 120
1000 *SAVE S.SUPER STRING ADDER 1010 *-------------------------------- 1020 * STRING ADDITION: & +$,A$,B$,C$ 1030 *-------------------------------- 1040 BUFFER .EQ $200 - $2FF 1050 AMPERSAND.VECTOR .EQ $3F5 - $3F7 1060 AS.CHRGET .EQ $00B1 1070 AS.SYNERR .EQ $DEC9 1080 AS.PTRGET .EQ $DFE3 1090 AS.CHKCOM .EQ $DEBE 1100 AS.GETSPA .EQ $E452 1110 AS.MOVSTR .EQ $E5E2 1120 *-------------------------------- 1130 .OR $9000 1140 .TF B.STRING ADDER 1150 *-------------------------------- 1160 SETUP LDA #$4C JMP OPCODE 1170 STA AMPERSAND.VECTOR 1180 LDA #STRADD 1190 STA AMPERSAND.VECTOR+1 1200 LDA /STRADD 1210 STA AMPERSAND.VECTOR+2 1220 RTS 1230 *-------------------------------- 1240 FRESPC .EQ $71,72 1250 VARPNT .EQ $83,84 1260 *-------------------------------- 1270 * TWO SIMILAR BLOCKS, FOR A$ AND B$ 1280 * REFERENCED WITH X=0 OR X=4 1290 *-------------------------------- 1300 A.LENGTH .BS 1 1310 A.ADDR .BS 2 1320 A.DIGITS.AFTER .BS 1 1330 * 1340 B.LENGTH .BS 1 1350 B.ADDR .BS 2 1360 B.DIGITS.AFTER .BS 1 1370 *-------------------------------- 1380 * A THIRD BLOCK, NEARLY THE SAME AS ABOVE, 1390 * FOR C$: REFERENCED WITH X=8 1400 *-------------------------------- 1410 C.LENGTH .BS 1 1420 C.STRING .BS 2 1430 *-------------------------------- 1440 CARRY .BS 1 1450 TENS.FLAG .BS 1 1460 C.ADDR .BS 2 1470 MAX.DIGITS.BEFORE .BS 1 1480 MAX.DIGITS.AFTER .BS 1 1490 *-------------------------------- 1500 * & BRANCHES HERE 1510 *-------------------------------- 1520 STRADD CMP #$C8 CHECK FOR "+$," 1530 BNE .1 1540 JSR AS.CHRGET 1550 CMP #'$ 1560 BNE .1 1570 JSR AS.CHRGET 1580 CMP #', 1590 BEQ .2 1600 .1 JMP AS.SYNERR 1610 *-------------------------------- 1620 .2 LDX #0 POINT AT A$ DATA 1630 JSR PARSE.STRING.NAME FIRST OPERAND 1640 LDX #4 POINT AT B$ DATA 1650 JSR PARSE.STRING.NAME SECOND OPERAND 1660 JSR AS.CHKCOM RESULT STRING 1670 JSR AS.PTRGET 1680 STY C.STRING+1 ADDRESS OF VARIABLE 1690 STA C.STRING 1700 *-------------------------------- 1710 * SCAN BOTH STRINGS TO DETERMINE BUFFER PARAMETERS 1720 *-------------------------------- 1730 LDX #0 POINT AT A$ DATA 1740 JSR SCAN GET Y=LEFT LENGTH, X=RIGHT LENGTH 1750 STX A.DIGITS.AFTER 1760 STX MAX.DIGITS.AFTER 1770 STY MAX.DIGITS.BEFORE 1780 LDX #4 POINT AT B$ DATA 1790 JSR SCAN GET Y=LEFT LENGTH, X=RIGHT LENGTH 1800 STX B.DIGITS.AFTER 1810 CPX MAX.DIGITS.AFTER 1820 BCC .3 1830 STX MAX.DIGITS.AFTER 1840 .3 CPY MAX.DIGITS.BEFORE 1850 BCC .4 1860 STY MAX.DIGITS.BEFORE 1870 *-------------------------------- 1880 * CLEAR THAT MUCH OF THE BUFFER 1890 *-------------------------------- 1900 .4 INC MAX.DIGITS.BEFORE TWO MORE CHARS FOR 1910 INC MAX.DIGITS.BEFORE SIGN AND CARRY 1920 CLC 1930 LDA MAX.DIGITS.BEFORE TOTAL LENGTH OF RESULT 1940 ADC MAX.DIGITS.AFTER 1950 STA C.LENGTH 1960 TAY 1970 LDA #0 ZERO THE BUFFER FOR USE AS AN 1980 .5 STA BUFFER-1,Y ACCUMULATOR 1990 DEY 2000 BNE .5 2010 *-------------------------------- 2020 * ADD A$ TO BUFFER 2030 *-------------------------------- 2040 LDX #0 POINT AT A$ DATA 2050 JSR ADD.TO.BUFFER 2060 *-------------------------------- 2070 * ADD B$ TO BUFFER 2080 *-------------------------------- 2090 LDX #4 POINT AT B$ DATA 2100 JSR ADD.TO.BUFFER 2110 *-------------------------------- 2120 * CONVERT BUFFER TO ASCII AGAIN 2130 *-------------------------------- 2140 LDA BUFFER SEE IF NUMBER IS NEGATIVE 2150 CMP #5 SET CARRY IF NEGATIVE, ELSE CLEAR 2160 ROR MAKE A=0XXXXXXX OR 1XXXXXXX 2170 STA CARRY TO SET OR CLEAR THESE FLAGS 2180 STA TENS.FLAG APPROPRIATELY 2190 LDX C.LENGTH 2200 BEQ .10 FINISHED 2210 .6 LDA BUFFER-1,X 2220 CMP #'. 2230 BEQ .9 2240 BIT TENS.FLAG 2250 BPL .8 2260 ASL CARRY 2270 LDA #10 2280 SBC BUFFER-1,X 2290 CMP #10 2300 BCC .7 2310 SBC #10 2320 .7 ROR CARRY 2330 .8 ORA #'0 2340 .9 STA BUFFER-1,X 2350 DEX 2360 BNE .6 2370 .10 BIT TENS.FLAG SEE ABOUT FINAL SIGN 2380 BPL .11 VALUE IS POSITIVE 2390 LDA #'- NEGATIVE, SO STUFF "-" 2400 STA BUFFER IN FRONT OF BUFFER 2410 .11 JSR CHOP.OFF.LEADING.ZEROES 2420 *-------------------------------- 2430 * PUT (BUFFER) IN OUTPUT STRING 2440 *-------------------------------- 2450 LDX #8 POINT AT C$ DATA 2460 JSR SETUP.OPERAND 2470 JSR AS.GETSPA 2480 LDY #0 2490 STA (VARPNT),Y 2500 INY 2510 LDA FRESPC 2520 STA (VARPNT),Y 2530 INY 2540 LDA FRESPC+1 2550 STA (VARPNT),Y 2560 LDY C.ADDR+1 2570 LDX C.ADDR 2580 LDA C.LENGTH 2590 JMP AS.MOVSTR 2600 *-------------------------------- 2610 * ADD STRING TO BUFFER 2620 * ENTER WITH X=0 FOR A$, X=4 FOR B$ 2630 *-------------------------------- 2640 ADD.TO.BUFFER 2650 JSR SETUP.OPERAND 2660 TAY STRING LENGTH 2670 LDA A.DIGITS.AFTER,X 2680 PHA 2690 LDX #0 2700 LDA (VARPNT,X) CHECK FOR MINUS SIGN 2710 CMP #'- 2720 BEQ .1 YES, CARRY SET 2730 CLC ELSE CLEAR CARRY 2740 .1 ROR MAKE A=0XXXXXXX OR 1XXXXXXX 2750 STA TENS.FLAG MAKE FLAGS<0 IF MINUS 2760 STA CARRY 2770 *-------------------------------- 2780 CLC POINT INTO BUFFER WHERE OPERAND 2790 PLA ALIGNS 2800 ADC MAX.DIGITS.BEFORE 2810 TAX 2820 *-------------------------------- 2830 .2 TXA TEST X FOR BEGINNING OF BUFFER 2840 BEQ .8 YES, FINISHED! 2850 DEX NO, BACK ANOTHER ONE 2860 TYA CHECK OPERAND POINTER 2870 BEQ .3 END OF OPERAND, BUT WE 2880 * STILL NEED TO FINISH CARRIES 2890 DEY BACK UP IN OPERAND 2900 LDA (VARPNT),Y NEXT CHAR FROM OPERAND 2910 CMP #'. DECIMAL POINT? 2920 BEQ .7 YES, SKIP OVER IT 2930 CMP #'- MINUS SIGN? 2940 BNE .4 NO, MUST BE DIGIT 2950 .3 LDA #'0 ASCII ZERO THEN 2960 .4 AND #$0F CONVERT ASCII TO BINARY 2970 BIT TENS.FLAG 2980 BPL .5 NOT 9'S COMPLEMENTING 2990 EOR #$FF 3000 CLC 3010 ADC #10 FORM 9'S COMPLEMENT 3020 .5 ASL CARRY GET PREVIOUS CARRY INTO C-BIT 3030 ADC BUFFER,X 3040 CMP #10 SEE IF CARRY 3050 BCC .6 NO 3060 SBC #10 YES, BACK THIS DIGIT DOWN 3070 .6 ROR CARRY SAVE CARRY FOR NEXT LOOP 3080 .7 STA BUFFER,X 3090 JMP .2 3100 .8 RTS 3110 *-------------------------------- 3120 * SCAN STRING 3130 * ENTER WITH X=0 FOR A$, X=4 FOR B$ 3140 * RETURN WITH X = # DIGITS AFTER DECIMAL POINT 3150 * (COUNTING THE DECIMAL POINT) 3160 * Y = # DIGITS BEFORE DECIMAL POINT 3170 * (COUNTING SIGN IF ANY) 3180 *-------------------------------- 3190 SCAN 3200 JSR SETUP.OPERAND 3210 LDY #0 3220 TAX 3230 BEQ .2 NULL STRING 3240 .1 LDA (VARPNT),Y 3250 CMP #'. LOOKING FOR DECIMAL POINT 3260 BEQ .2 3270 INY 3280 DEX 3290 BNE .1 3300 .2 RTS 3310 *-------------------------------- 3320 * CHOP OFF LEADING ZEROES 3330 *-------------------------------- 3340 CHOP.OFF.LEADING.ZEROES 3350 LDY #1 FIND FIRST NON-ZERO POSITION 3360 .1 LDA BUFFER,Y 3370 CMP #'0 3380 BNE .2 3390 INY 3400 CPY MAX.DIGITS.BEFORE 3410 BCC .1 3420 DEY 3430 .2 LDA BUFFER SIGN, MAYBE 3440 CMP #'- 3450 BNE .3 3460 DEY 3470 STA BUFFER,Y 3480 .3 CLC 3490 TYA 3500 ADC #BUFFER 3510 STA C.ADDR 3520 LDA #0 3530 ADC /BUFFER 3540 STA C.ADDR+1 3550 SEC 3560 TYA 3570 EOR #$FF 3580 ADC C.LENGTH 3590 STA C.LENGTH 3600 RTS 3610 *-------------------------------- 3620 * PARSE STRING NAME, SET UP POINTER 3630 *-------------------------------- 3640 PARSE.STRING.NAME 3650 TXA 3660 PHA 3670 JSR AS.CHKCOM 3680 JSR AS.PTRGET GET SECOND STRING PNTR 3690 PLA 3700 TAX 3710 LDY #0 3720 LDA (VARPNT),Y GET LENGTH 3730 STA A.LENGTH,X 3740 INY 3750 LDA (VARPNT),Y GET ADDRESS OF DATA 3760 STA A.ADDR,X 3770 INY 3780 LDA (VARPNT),Y 3790 STA A.ADDR+1,X 3800 RTS 3810 *-------------------------------- 3820 * LOAD ADDRESS INTO VARPNT 3830 * X=0 FOR A$, X=4 FOR B$ 3840 *-------------------------------- 3850 SETUP.OPERAND 3860 LDA A.ADDR,X 3870 STA VARPNT 3880 LDA A.ADDR+1,X 3890 STA VARPNT+1 3900 LDA A.LENGTH,X 3910 RTS 3920 *-------------------------------- |
Don Taylor's original article in the August (1982) issue of AAL and Mike Laumer's follow-up the next month gave us the patches for running the S-C Macro Assembler in conjunction with the Videx 80-column board. I recently purchased a Videx card in order to implement the 80-column version of ES-CAPE, so I installed the patches.
I have really enjoyed using the Macro assembler in 80-column mode. Naturally, though, I couldn't resist adding a few enhancements to Don's and Mike's work.
Mike added the right arrow code, which copies characters off the Videx screen, but he stopped short of implementing the Escape-L LOAD sequence. To install the following code, you will need to change line 3080 in Don's article to point to my routine. Change it to "3080 .DA MY.ESC.L-1". Also, the STX instruction at line 4235 in Mike's article must be labelled GETCH.
*-------------------------------------- SCM.INSTALL .EQ SCM.BASE+$52A * MY.ESC.L CPX #0 CURSOR AT BEGINNING? BEQ .1 YES, CONTINUE JMP SCM.ESC.L NO, LET S-C HANDLE IT .1 LDA #0 CONNECT DOS STA $AA52 BY SETTING INTERCEPT STATE = 0 LDA #$84 SEND A CTRL-D JSR MON.COUT .2 LDA LOADCMD,X JSR SCM.INSTALL JSR FAKE.COUT CPX #6 BCC .2 .3 STX $406 SAVE CHAR POS'N JSR GETCH GET SCREEN CHAR LDX $406 RESTORE POS'N JSR SCM.INSTALL JSR FAKE.COUT CPX #40 40 CHARS SENT YET? BNE .3 NO, LOOP BACK JMP CLREOP CLEAR TO END OF PAGE * AND EXIT * LOADCMD .AS -/LOAD / *-------------------------------------- |
Secondly, I wanted a longer "*---" line on my screen, so I changed it to 68 characters instead of 38. This uses more of the 80-column screen, without wrapping around during assembly. To make this modification insert the following two lines after the label "INSTALL.PATCHES" in Don's original listing:
LDA #68 STA SCM.BASE+$494 |
Finally, I changed the dimensions of the Videx cursor so that it looks like a blinking underline instead of a blinking block. (Users of my ES-CAPE are already familiar with my love for the blinking underline!) Insert the following lines immediately after the "INSTALL.VECTORS" label:
LDA #$0A VIDEX REGISTER 10 STA V.DEV0 LDA #$68 STA V.DEV0+1 LDA #$0B VIDEX REGISTER 11 STA V.DEV0 LDA #$08 STA V.DEV0+1 |
Speaking of ES-CAPE, I am making progress on Version 2 and have included suggestions from many of you. If you have others, please drop me a line soon at 3199 Hammock Creek, Lithonia, GA 30058, or call evenings at (404) 483-7637.
Chuck Welman just called to report some errors in the January piece on using CATALOG ARRANGER with a relocated DOS. He says that the sentence about where to put the BIT MONREAD statements had problems. Here's his corrected version:
"Then add BIT MONREAD at these positions: Lines 1675, 3775, 3895, 3955, 4015 (".5" moved to this line), 4205 (".3" moved to this line, 4315, 4425, 4455 (".7" moved to this line), and 4895."
Chuck also passed along instructions for using FILENAME EDITOR with a RAM Card DOS. Here are his additions:
2635 .3 BIT MONREAD 2640 JSR MON.BELL 2642 BIT DOSREAD 2644 BIT DOSREAD 2646 RTS
Thanks to all of you for showing your appreciation for these programs.
Here is a little run-anywhere program sure to wake up the neighborhood dogs. Put it in your program as a last resort to get attention, because the only escape is by RESET or power-off.
1000 ALARM INY INCREMENT DELAY TIME 1010 TYA 1020 TAX DELAY COUNT TO X 1030 LDA $C030 TOGGLE SPEAKER 1040 .1 DEX DELAY LOOP 1050 BNE .1 1060 BEQ ALARM ....FOREVER.... |
That's it, only eleven bytes! For a slightly different effect, change the "DEX" in line 1030 to "INX".
You may have noticed the annoying problem with the .TI directive, in which there is sometimes a blank line after the title line and sometimes not. The blank line is there when the page break is forced with a .PG directive, but not when it is caused by merely filling a page.
The following little patch will fix it. I haven't put a definite address on the patch, because I don't know what other patches you may already have appended to the assembler. Just find an empty place and plop it in!
Motherboard version: :$21F0:4C xx yy (was 20 CF 2C) :$yyxx:20 CF 2C 4C E3 21
RAM Card version: :$E33C:4C xx yy (was 20 1B EE) :$yyxx:20 1B EE 4C 2F E3
Another .TI problem of which I am aware is that the line count is messed up on the first page of the symbol table listing. This is caused by the fact that the extra carriage returns in the "SYMBOL TABLE" message are not counted. You can clean up the appearance by making the last line of your source program be ".PG"; this forces the symbol table to start on a fresh page.
We don't have one yet, but we did play with one for about an hour last week. All our software works fine, as long as you stay in the 40-column caps-lock mode. We will be making new versions available in the near future which take full advantage of the extended memory, lower-case, and 80-column display.
The best write-up I have seen yet on the //e is in the February 1983 Apple Orchard (published by the International Apple Core, 908 George St., Santa Clara, CA 95050).
Here are some of the things that caught my attention:
* Real shift key, and a caps-lock key.
* Open-Apple and Closed-Apple keys, which duplicate the first two paddle buttons.
* Recessed RESET key. CTRL-RESET required (no longer a switchable option). CTRL-Closed-Apple-RESET starts a memory test program.
* Two 8K ROMs, instead of six 2K ROMs. The extra 2K of ROM space is used by the modified Monitor program. Fancy soft-switches map the extra 2K into the $C000-C7FF space. These sockets are supposedly compatible with 2764 EPROMs.
* Apparently the Monitor now uses (clobbers) zero-page locations $08 and $1F.
* Up- and down-arrows on the keyboard. Down is CTRL-J, or linefeed. Up is CTRL-K.
* The keyboard includes all the ASCII set, even $7F (DELETE, or RUBOUT).
* 64K RAM on the motherboard. This simulates an Apple II Plus with a 16K RAM card in slot 0.
* New slot instead of slot 0, with 60-pin connector (other slots still have 50-pin connectors). Apple's 80-column card plugs in here. The extra pins carry other signals not normally available at the slots. Look for some amazing new combined function cards from the peripheral-card makers for this slot! I wouldn't be surprised to find ads real soon for 256K RAM cards including 80-column support, clock- calendar, serial/parallel interfaces, and all on one card.
* 80-column card with or without extra 64K RAM. But this 64K RAM is soft-switched in a totally different manner. It maps over the same space as the motherboard 64K, with switches to map portions such as page-zero, text screen, hi-res screen, and so on.
* Now you can READ the state of most of the soft-switches. Bit 7 (high bit) tells the state, as follows:
$C013 -- RAMREAD $C014 -- RAMWRT $C015 -- SLOTCXROM/CX00ROM $C016 -- ALTZP/MAIN $C017 -- SLOTC3ROM/SLOTROM $C018 -- 80 COL STORE $C019 -- VERTICAL BLANKING $C01A -- TEXT $C01B -- MIXED MODE $C01C -- PAGE2 $C01D -- HIRES $C01E -- ALTCHAR $C01F -- 80 COL DISP
* Yes, you saw right...the vertical blanking signal is now readable! So lovers of Lancaster's Enhancements can continue to tinker!
* Inverse lower-case display is selectable, at the expense of the flashing mode.
* The cursor display is different. A small checkerboard alternates with the character under the cursor in 40-column mode. In 80-column mode an inverse blank is the normal cursor, and an inverse "+" is used when in escape-mode.
Whether we view the changes as improvements or not, the //e will very soon be the standard we all have to deal with. The same situation arose when Apple switched from II to II Plus. A year from now, when 300,000 have been sold, we will wonder how we ever lived without it!
Peter Bartlett, of Chicago, has reported an unpublished limit on the number of Target Files that can be generated by one assembly. Right now there can only be 31; above that number the load address and length bytes go astray. If you need more than 31 files from one assembly, you can make the following patches:
Regular version :$29EA:3F Language Card version :$C083 C083 EB36:3F N C080
These patches will allow you to have up to 63 target files. That should be plenty!
How would you like a radio which played every available station at one time? Well that's how I sometimes feel about using Applesoft's INPUT statement. I want to be able to "tune in" on the character(s) of the input stream, in much the same way as a radio tunes into a station. Applesoft's INPUT statement, however, accepts all characters typed into the keyboard and allows up to 255 of them. This means that I have to do a lot of checking and monitoring of string lengths and characters to avoid input errors.
For example, when answering a Y or N question, what happens when the user inputs "WXYZ"? Provisions are needed within the program to guard against such errors. This can be very inconvenient and space-consuming, yet it is essential for good programming.
A better example occurs when you are creating a disk file. Field lengths and data types are often restricted, such as in a name, address, or social security number. A SSN, for instance, has a fixed length and must be constructed of numbers only. Checking a field such as this can be very time consuming and lengthy. In fact, it seems that a quarter of the contents of my Applesoft programs does nothing but check on field lengths, option boundaries, and other input checks.
So, I set out to create an input routine which would allow Applesoft to "tune" into the characters specified and also monitor the field length. I've seen several input routines such as this on larger systems, but all had one disadvantage: Only a fixed number of options were available, such as alpha only, numeric only, and (Y or N) input. More options available meant more parameters were necessary, making the systems more cumbersome to work with. After much thought I decided on a totally new approach which would allow almost limitless control of input. I christened this routine TRAPPER for "Tuning and Regulating APPlesoft Entries by Restriction."
TRAPPER employs a coded restriction string (not unlike Applesoft's IF expression) to tune out the characters I don't want to accept. TRAPPER is then, in essence, a tiny interactive interpreter that provides a short, convenient method of filtering out any unwanted characters in the input. Here's how it works.
TRAPPER uses three parameters as follows:
Syntax: & INPUT (A, B$, C$)
A: Input field length (real expression) B$: Coded restriction string (string expression) includes: > < = ' AND OR NOT <sp> <single char> C$: Input string (string variable) variable to receive input
As I have said, the restriction string is a simple relational expression as is used by Applesoft's IF statement. It is constructed of the following special characters and rules:
EXAMPLES:
YN$ = " ='Y' OR ='N' " :REM (Y OR N) ONLY NOSP$ = " NOT =' ' " :REM NO SPACES ALLOWED MENU$ = " NOT <'1' AND NOT >'4' " :REM ALLOWS 1 THRU 4 WAITCR$ = "" :REM WAIT FOR A <CR>
After using Trapper awhile, I noticed a significant reduction in the size of my Applesoft programs, with even better error trapping than ever before possible. And it doesn't print that leading question mark which I never did like (not all input prompts are questions.)
For a 48K Apple, DOS sets HIMEM at $9600. Trapper resides just below this at $9300 and moves HIMEM down to that point.
1000 *SAVE S.TRAPPER 1010 *-------------------------------- 1020 * TRAPPER, BY ALLEN MARSALIS 1030 *-------------------------------- 1040 .OR $9300 1050 .TF B.TRAPPER 1060 *-------------------------------- 1070 RLEN .EQ $1A RESTRICTION STRING 1080 RSTR .EQ $1B DESCRIPTOR 1090 TEMPPT .EQ $52 1100 LASTPT .EQ $53 1110 FRESPC .EQ $71,72 1120 HIMEM .EQ $73,74 1130 VARPNT .EQ $83,84 1140 FACMO .EQ $A0 1150 *-------------------------------- 1160 BUF .EQ $200 INPUT BUFFER 1170 AMPVEC .EQ $3F5 AMPERSAND VECTOR 1180 STROBE .EQ $C010 KEYBOARD STROBE 1190 *-------------------------------- 1200 AS.FRMNUM .EQ $DD67 EVALUATE NUMERIC FORMULA 1210 AS.CHKSTR .EQ $DD6C REQUIRE STRING 1220 AS.FRMEVL .EQ $DD7B EVALUATE GENERAL FORMULA 1230 AS.CHKCLS .EQ $DEB8 REQUIRE ")" 1240 AS.CHKCOM .EQ $DEBE REQUIRE "," 1250 AS.CHKOPN .EQ $DEBB REQUIRE "(" 1260 AS.SYNCHR .EQ $DEC0 REQUIRE (A-REG) 1270 AS.SYNERR .EQ $DEC9 SYNTAX ERROR 1280 AS.PTRGET .EQ $DFE3 GET VARIABLE PNTR 1290 AS.GETSPA .EQ $E452 GET SPACE IN STRING AREA 1300 AS.MOVSTR .EQ $E5E2 COPY STRING DATA 1310 AS.FRETMP .EQ $E604 FREE TEMPORARY STRING 1320 AS.CONINT .EQ $E6FB CONVERT FAC TO 8-BITS 1330 *-------------------------------- 1340 MON.CLREOL .EQ $FC9C CLEAR TO END-OF-LINE 1350 MON.RDKEY .EQ $FD0C READ A KEY 1360 MON.COUT .EQ $FDED DISPLAY A CHARACTER 1370 *-------------------------------- 1380 SETUP LDA #$4C "JMP" OPCODE 1390 STA AMPVEC 1400 LDA #TRAPPER 1410 STA AMPVEC+1 1420 LDA /TRAPPER 1430 STA AMPVEC+2 1440 LDA #SETUP SET HIMEM UNDER TRAPPER 1450 STA HIMEM 1460 LDA /SETUP 1470 STA HIMEM+1 1480 RTS 1490 *-------------------------------- 1500 * AMPERSAND COMES HERE 1510 *-------------------------------- 1520 TRAPPER 1530 LDA #$84 "INPUT" TOKEN 1540 JSR AS.SYNCHR 1550 JSR AS.CHKOPN "& INPUT (" 1560 JSR AS.FRMNUM READ FIELD LENGTH PARAMETER 1570 JSR AS.CONINT CONVERT TO 8-BIT VALUE 1580 STX FL SAVE FIELD LENGTH 1590 JSR AS.CHKCOM "," 1600 JSR AS.FRMEVL GET RESTRICTION STRING 1610 JSR AS.CHKSTR 1620 JSR AS.CHKCOM ANOTHER "," 1630 LDY #2 SAVE DESCRIPTOR 1640 .1 LDA (FACMO),Y 1650 STA RLEN,Y 1660 DEY 1670 BPL .1 1680 LDA TEMPPT DID FRMEVL MAKE A TEMP STRING? 1690 CMP #$56 1700 BCC .2 NO 1710 LDA LASTPT YES, SO FREE THE TEMP 1720 LDY #0 1730 JSR AS.FRETMP 1740 .2 LDA #0 INIT BUFFER INDEX 1750 STA BINDEX 1760 *---UNDERSCORE INPUT FIELD------- 1770 LDA #$DF UNDERLINE CHAR 1780 JSR PRINT.FIELD 1790 LDA #$88 BACKSPACE TO BEGINNING AGAIN 1800 JSR PRINT.FIELD 1810 *---READ A KEY------------------- 1820 BIT STROBE DON'T ALLOW TYPE AHEAD 1830 .3 JSR MON.RDKEY READ NEXT KEY 1840 AND #$7F INTERNAL FORM 1850 STA KEY SAVE IT 1860 *---BACKSPACE-------------------- 1870 CMP #$08 BACKSPACE? 1880 BNE .22 NO 1890 LDA BINDEX IGNORE AT BEGINNING OF LINE 1900 BEQ .21 1910 LDA #$88 YES, ECHO IT 1920 JSR MON.COUT 1930 LDA #$DF REPLACE UNDERLINE 1940 JSR MON.COUT 1950 LDA #$88 BACKSPACE AGAIN 1960 JSR MON.COUT 1970 DEC BINDEX BACK UP BUFFER TOO 1980 .21 JMP .3 1990 *---CARRIAGE RETURN-------------- 2000 .22 CMP #$0D RETURN? 2010 BNE .23 NO 2020 JSR MON.CLREOL 2030 JSR AS.PTRGET GET DESTINATION STRING 2040 JSR AS.CHKCLS MUST HAVE ")" AT END 2050 LDA BINDEX LENGTH OF INPUT LINE 2060 JSR AS.GETSPA FIND ROOM FOR IT 2070 LDY #0 MOVE IN DESCRIPTOR 2080 STA (VARPNT),Y 2090 INY 2100 LDA FRESPC 2110 STA (VARPNT),Y 2120 INY 2130 LDA FRESPC+1 2140 STA (VARPNT),Y 2150 LDY /BUF COPY DATA INTO STRING 2160 LDX #BUF 2170 LDA BINDEX 2180 JMP AS.MOVSTR ...AND RETURN 2190 *---CHECK IF VALID KEY----------- 2200 .23 JSR CHECK.RESTRICTIONS 2210 *---CHECK VALIDITY AND ECHO------ 2220 LDA KEY GET KEY AGAIN 2230 LDA BINDEX 2240 CMP FL 2250 BCS .27 TOO FAR, ABORT KEY 2260 LDA NEW IF NEW = FAIL, ABORT KEY 2270 BEQ .27 YES, ABORT KEY 2280 LDA KEY 2290 LDY BINDEX 2300 STA BUF,Y PUT KEY INTO BUFFER 2310 INC BINDEX 2320 CMP #$20 IF KEY WAS CONTROL-KEY, 2330 BCS .26 THEN PRINT SPACE 2340 LDA #$20 2350 .26 ORA #$80 2360 JSR MON.COUT ECHO 2370 JMP .3 NEXT KEY 2380 .27 LDA #$07 RING BELL 2390 BNE .26 2400 *-------------------------------- 2410 CHECK.RESTRICTIONS 2420 LDA #0 2430 STA RINDEX RINDEX = 0 2440 STA NEW NEW = FAIL 2450 STA ANDOR ANDOR = OR 2460 STA NOT NOT = FALSE 2470 *---FETCH OPERATOR--------------- 2480 .4 LDY RINDEX IF RINDEX >= RLEN, 2490 CPY RLEN THEN QUIT SCAN 2500 BCC .5 NOT YET 2510 RTS 2520 .5 LDA (RSTR),Y FETCH OPERATOR 2530 INC RINDEX 2540 *---DETERMINE OPERATION---------- 2550 CMP #' IGNORE BLANKS 2560 BEQ .4 2570 CMP #'< < = >, THEN FETCH OPERAND 2580 BEQ .10 2590 CMP #'> 2600 BEQ .10 2610 CMP #'= 2620 BEQ .10 2630 CMP #'A "AND" 2640 BEQ .7 2650 CMP #'O 2660 BEQ .8 2670 CMP #'N "NOT" 2680 BEQ .9 2690 JMP AS.SYNERR 2700 *---AND OPERATOR----------------- 2710 .7 LDA #'N 2720 JSR SYNSTR 2730 LDA #'D 2740 JSR SYNSTR 2750 LDA #1 SET AND OPERATOR 2760 STA ANDOR 2770 BNE .4 ...ALWAYS 2780 *---OR OPERATOR------------------ 2790 .8 LDA #'R 2800 JSR SYNSTR 2810 LDA #0 SET OR OPERATOR 2820 STA ANDOR 2830 BEQ .4 ...ALWAYS 2840 *---NOT OPERATOR----------------- 2850 .9 LDA #'O 2860 JSR SYNSTR 2870 LDA #'T 2880 JSR SYNSTR 2890 LDA #1 SET NOT OPERATOR "TRUE" 2900 STA NOT 2910 BNE .4 ...ALWAYS 2920 *---FETCH OPERAND---------------- 2930 .10 STA ROPR 2940 LDA #$27 CHECK FOR APOSTROPHE 2950 JSR SYNSTR 2960 LDY RINDEX 2970 LDA (RSTR),Y GET OPERAND 2980 STA ROPD 2990 INC RINDEX 3000 LDA #$27 ANOTHER APOSTROPHE 3010 JSR SYNSTR 3020 *---EVALUATE RELATIONAL OPERATION 3030 LDA NEW 3040 STA LAST LAST = NEW 3050 LDA #0 NEW = FAIL 3060 STA NEW 3070 LDY ROPR OPERATOR 3080 LDA KEY LATEST KEY 3090 CMP ROPD COMPARE TO OPERAND 3100 BEQ .11 THEY ARE EQUAL 3110 BCC .12 KEY < OPERAND 3120 CPY #'> KEY > OPERAND 3130 BEQ .13 SUCCESS! 3140 BNE .14 FAIL. 3150 .11 CPY #'= 3160 BEQ .13 SUCCESS 3170 BNE .14 FAIL 3180 .12 CPY #'< 3190 BNE .14 FAIL 3200 .13 LDA #1 FLAG SUCCESS 3210 STA NEW 3220 *---PERFORM NOT OPERATION-------- 3230 .14 LDA NOT IF NOT, TOGGLE NEW 3240 BEQ .17 NOT NOT 3250 LDA NEW 3260 EOR #1 3270 STA NEW 3280 LDA #0 CLEAR NOT 3290 STA NOT 3300 *---PERFORM AND/OR OPERATION----- 3310 .17 LDA LAST 3320 LDY ANDOR 3330 BEQ .18 OR 3340 AND NEW AND 3350 STA NEW 3360 JMP .4 3370 .18 ORA NEW 3380 STA NEW 3390 JMP .4 3400 *-------------------------------- 3410 SYNSTR STA HOLD SAVE CHAR 3420 .1 LDY RINDEX 3430 LDA (RSTR),Y 3440 INC RINDEX 3450 CMP #' IGNORE BLANKS 3460 BEQ .1 3470 CMP HOLD 3480 BEQ .2 3490 JMP AS.SYNERR 3500 .2 RTS 3510 *-------------------------------- 3520 PRINT.FIELD 3530 LDY FL 3540 .1 JSR MON.COUT 3550 DEY 3560 BNE .1 3570 RTS 3580 *-------------------------------- 3590 HOLD .BS 1 3600 NOT .BS 1 3610 ANDOR .BS 1 3620 FL .BS 1 3630 NEW .BS 1 3640 LAST .BS 1 3650 KEY .BS 1 3660 BINDEX .BS 1 3670 RINDEX .BS 1 3680 ROPR .BS 1 3690 ROPD .BS 1 3700 *-------------------------------- |
In most assemblers, including the S-C Macro Assembler, you can use the character "*" in the operand of an instruction to mean the current value of the location counter. (The location counter is a variable used by the assembler to keep track of where the next byte of object code goes.) Here are a couple of simple examples of using the *, from page 6-2 of the Macro Assembler manual:
0800- 03 1000 QT .DA #QTSZ 0801- 41 42 43 1010 .AS /ABC/ 0003- 1020 QTSZ .EQ *-QT-1 1030 0804- 00 00 1040 VAR .DA *-* 1050 0806- 1060 FILLER .BS $900-* 0900- 1070 END .EQ *
The QT, QTSZ example uses the * to help calculate the length of a string of characters. The VAR line uses "*-*" to define a variable as having a value of zero.
The expression labelled FILLER causes the assembler to skip ahead to $900. This has much the same effect as .OR $900, but it won't cause the assembler to close a target file, the way .OR would.
One thing Bill wanted was an expression to have the assembly skip up to the beginning of the next page, no matter what that page might be. Here's what we came up with:
0800- 34 12 1000 START .DA $1234 0802- 1010 FILL .BS *+255/256*256-* 0900- 45 23 1020 END .DA $2345
If you change the origin to $C00, END will move to $D00. With this coding, END will always be $100 above START. Note that there is no precedence when the assembler is evaluating an expression. Terms are taken strictly left-to-right. But notice how smart the expression cracker in the assembler is! It knows that a "*" between numbers or labels means "multiply", and a "*" between arithmetic operators means "location counter".
In the American Heart Association CPR project Mike uses lots of overlays, and has to make sure that modules don't grow above a certain address. He does it by putting lines like these at the end of a module:
1000 .DO *>LIMIT 1010 !!! PROGRAM TOO BIG !!! 1020 .FIN
Here's an example, to keep a program below the Hi-res pages:
1000 .OR $1FFE 1010 .DA $4321 1020 .DO *>$2000 1030 !!! PROGRAM TOO BIG !!! 1040 .FIN
That will assemble just fine:
1000 .OR $1FFE 1FFE- 21 43 1010 .DA $4321 1020 .DO *>$2000 1040 .FIN 0000 ERRORS IN ASSEMBLY
But, try inserting another line:
1015 .DA $1234
Here's what happens:
*** BAD OPCODE ERROR 1030 !!! PROGRAM TOO BIG !!! 0001 ERRORS IN ASSEMBLY
The key to this technique is putting a couple of blanks at the beginning of line 1030. That way, the assembler tries to parse "!!!" as an opcode, and reports an error during pass one, before any code has been generated.
You should be very careful about using "*", and experiment on a test disk when trying something new. For example, take another look at line 1060 in the first listing. If you put "*-$900" for the operand, that would be negative. The result would be $FF07, which would try to write 65,287 zero bytes onto your target file. The next thing you see is probably DISK FULL!
That's about all the tricky things we have room for right now. We hope these hints will help you to navigate "by the stars" in your programming. Just remember to experiment carefully with the * operand before using it in vital programs. There are also many pitfalls on this road!
I just received an advance copy of a forthcoming book by Jules Gilder (a long-time AAL subscriber), titled "Now That You Know Apple Assembly Language, What Can You Do With It?" As the title implies, this will be an intermediate level look at really using assembly language in your Apple. It looks good. As soon as I have details about price and publication date, I'll let you know.
Sometimes you would like to see all the hex bytes a macro produces, but not the expanded lines of source code. The >LIST MOFF directive turns off both, but with the following three byte patch you can see the hex bytes for each macro call.
Motherboard version: :$218B:0 (was 03) :$21B3:0 (was 05) :$21E2:0 (was 10) RAM Card version: :$C083 C083 (enable writing) :$E2D7:0 (was 03) :$E2FF:0 (was 05) :$E32E:0 (was 10)
Don't make these into permanent patches, because there will be times when you want to use the .LIST directives normally. If you feel like making the changes often, you might make two separate versions of the assembler, or make some EXEC files to do the patching on demand.
I finally have had to face it. I am never going to have time to finish the S-C word processor. It is certainly usable, because we have been using it here for months now. And we use it a lot, writing the newsletter, manuals, letters, etc. My father-in-law uses it, and so does my best friend, Fred. Fred's 11-year-old daughter is also using it, and loves it. She is currently typing a research paper using it.
I know it is easy to use, because I didn't even give Fred a list of commands, let alone a reference manual. Of course, I did sit down with them for a few hours at the first, because they had never even seen a word processor before.
In power, it is somewhere between Applewriter 1.1 and Applewriter II. It is similar in operation to Applewriter 1.1, and works in 40-column mode only. It requires a lower-case display and shift-key mod.
It can read Applewriter 1.1 files, and instantly convert them to standard ASCII form. Normally it uses standard Apple text files (type T in the catalog). Of course, with Bobby Deen's help, I built in FAST read and write of those text files. Faster than binary files, actually. Something like 100 sectors in 7 seconds, if I remember correctly.
I want to make a deal with you. I'll send you the complete commented source code on disk, together with a few sample text files. The text files will describe the command repertoire. If you are already familiar with Applewriter 1.1, you won't have any trouble at all. The assembled word processor will also be there, in case you don't have the S-C Macro Assembler.
But if you do have my assembler, you can proceed to modify, improve, augment, enhance, and so on, to your heart's content.
I'll send you the disk, if you'll send me $50. Or your charge card numbers, of course. I also want your commitment to keep this in the family. You know, don't go out and write a manual and wrap it in a fancy cover and call it YOUR product!
If you do enhance it, send in your additions and we'll make this a joint effort. With all of us working on it, we may soon have the world's best word machine!
Apple Assembly Line is published monthly by S-C SOFTWARE CORPORATION, P.O. Box 280300, Dallas, Texas 75228. Phone (214) 324-2050. Subscription rate is $15 per year in the USA, sent Bulk Mail; add $3 for First Class postage in USA, Canada, and Mexico; add $13 postage for 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.)