In This Issue...
For the last month and a half I have been working with a fantastic new device: the EXCEL-9 from Seikou Electronics in Japan. The EXCEL-9 contains a 6809E CPU, 8K bytes of ROM, and an interval timer. The 8K ROM contains a monitor with 35 commands (including mini-assembler and dis-assembler commands). The introductory price of $399.95 includes the FLEX Operating System from Technical Systems Consultants (TSC), with utilities, text editor, and macro assembler.
The board will soon be appearing in your local computer stores, courtesy of ESD Laboratories. I worked with them to translate the excellent reference manual into English. (That explains how I obtained one of the boards so early.)
EXCEL-9 has a lot of unique features that should make it a very popular board:
I intend to handle these boards. You can order them from me now, but please allow a while for delivery. The documentation is ready for the printer, but not yet printed.
One of the questions I hear the most is "How can I call the Hi-Res subroutines in the Applesoft ROMs?" The basic information about those subroutines has been published (in Apple Orchard, Vol. 1 No. 1), but with an error in the subroutine addresses.
First, some important locations in page zero:
$1A,1B Shape pointer used by DRAW and XDRAW $1C Last used color byte $26,27 Address of byte containing X,Y point $30 Bit mask for bit in that byte $E0,E1 X-coordinate (0-279) $E2 Y-coordinate (0-191) $E4 Color $E6 Page ($20 if HGR, $40 if HGR2) $E7 SCALE= value $E8,E9 Address of beginning of shape table $EA Collision counter $F9 ROT= value
The software uses some other page zero variables, but I am not too clear yet on their purpose.
Now here are the major entry points:
HGR2 $F3D8 Initialize and clear hi-res page 2. HGR $F3E2 Initialize and clear hi-res page 1. HCLR $F3F2 Clear the current hi-res screen to black. BKGND $F3F6 Clear the current hi-res screen to the last plotted color (from ($1C). HPOSN $F411 Positions the hi-res cursor without plotting a point. Enter with (A) = Y-coordinate, and (Y,X) = X-coordinate. HPLOT $F457 Calls HPOSN and tries to plot a dot at the cursor's position. If you are trying to plot a non-white color at a complementary color position, no dot will be plotted. HLIN $F53A Draws a line from the last plotted point or line destination to: (X,A) = X-coordinate, and (Y) = Y-coordinate. HFIND $F5CB Converts the hi-res cursor's position back to X- and Y-coordinates; stores X-coordinate at $E0,E1 and Y-coordinate at $E2. DRAW $F601 Draws a shape. Enter with (Y,X) = the address of the shape table, and (A) = the rotation factor. Uses the current color. XDRAW $F65D Draws a shape by inverting the existing color of the dots the shape draws over. Same entry parameters as DRAW. SETHCOL $F6EC Set the hi-res color to (X), where (X) must be between 0 and 7.
I wrote a sample demonstration program of the hi-res subroutines. First, here is an Applesoft version. Note that it first sets the whole screen to a particular color, and then draws a series of nested squares in a complementary color. Since it is nice and short, why don't you type it in and try it?
100 HGR2 110 FOR C = 0 TO 7: HCOLOR= C 120 HPLOT 0,0: CALL 62454: REM CLEAR TO CURRENT COLOR 130 HCOLOR= 7 - C 140 FOR S = 10 TO 190 STEP 10 150 X1 = 140 - S / 2: X2 = X1 + S 160 Y1 = 95 - S / 2: Y2 = Y1 + S 170 HPLOT X1,Y1 TO X2,Y1 TO X2,Y2 TO X1,Y2 TO X1,Y1 180 NEXT S 190 PRINT CHR$ (7): FOR I = 1 TO 500: NEXT 200 NEXT C 210 TEXT |
1000 *--------------------------------- 1010 * SAMPLE PLOTTING PROGRAM 1020 *--------------------------------- 1030 AS.LASTCLR .EQ $1C 1040 *--------------------------------- 1050 AS.HGR2 .EQ $F3D8 SET UP HI-RES PAGE 2 1060 AS.HCLR .EQ $F3F2 CLEAR HI-RES SCREEN 1070 AS.BKGND .EQ $F3F6 CLEAR HI-RES SCREEN TO LAST COLOR 1080 AS.HPOSN .EQ $F411 MOVE CURSOR TO (Y,X),(A) 1090 AS.HPLOT .EQ $F457 PLOT A DOT AT (Y,X),(A) 1100 AS.HLIN .EQ $F53A DRAW A LINE FROM LAST POINT TO (X,A),(Y) 1110 AS.SETHCOL .EQ $F6EC SET HI-RES COLOR 1120 MON.TEXT .EQ $FB2F 1130 *--------------------------------- 1140 HI.RES.DEMO 1150 JSR AS.HGR2 1160 LDX #0 FOR COLOR = 0 TO 7 1170 .1 STX COLOR 1180 JSR AS.SETHCOL 1190 STA AS.LASTCLR 1200 JSR AS.BKGND CLEAR SCREEN TO SOLID COLOR 1210 LDA COLOR 1220 EOR #7 COMPLEMENTARY COLOR 1230 TAX 1240 JSR AS.SETHCOL 1250 JSR DRAW.SQUARE 1260 LDX COLOR NEXT COLOR 1270 INX 1280 CPX #8 1290 BCC .1 1300 JSR MON.TEXT 1310 RTS 1320 *--------------------------------- 1330 DRAW.SQUARE 1340 LDA #10 FOR SIZE=10 TO 190 STEP 10 1350 .1 STA SIZE 1360 LSR SIZE/2 1370 STA SIZE2 1380 LDA #0 1390 STA XSTART+1 1400 STA XSTOP+1 1410 SEC XSTART=140-SIZE/2 1420 LDA #140 1430 SBC SIZE2 1440 STA XSTART 1450 CLC XSTOP=XSTART+SIZE 1460 ADC SIZE 1470 STA XSTOP 1480 SEC 1490 LDA #95 YSTART=95-SIZE/2 1500 SBC SIZE2 1510 STA YSTART 1520 CLC YSTOP=YSTART+SIZE 1530 ADC SIZE 1540 STA YSTOP 1550 LDY XSTART+1 HPLOT XSTART,YSTART 1560 LDX XSTART 1570 LDA YSTART 1580 JSR AS.HPLOT 1590 LDX XSTOP+1 TO XSTOP,YSTART 1600 LDA XSTOP 1610 LDY YSTART 1620 JSR AS.HLIN 1630 LDX XSTOP+1 TO XSTOP,YSTOP 1640 LDA XSTOP 1650 LDY YSTOP 1660 JSR AS.HLIN 1670 LDX XSTART+1 TO XSTART,YSTOP 1680 LDA XSTART 1690 LDY YSTOP 1700 JSR AS.HLIN 1710 LDX XSTART+1 TO XSTART,YSTART 1720 LDA XSTART 1730 LDY YSTART 1740 JSR AS.HLIN 1750 CLC 1760 LDA SIZE NEXT SIZE 1770 ADC #10 1780 CMP #191 1790 BCC .1 1800 DELAY.LOOP 1810 LDY #0 DELAY LOOP SO WE CAN SEE IT 1820 .1 LDX #0 1830 .2 DEX 1840 BNE .2 1850 LDA $C030 AND HEAR IT 1860 DEY 1870 BNE .1 1880 RTS 1890 *--------------------------------- 1900 COLOR .BS 1 1910 SIZE .BS 1 1920 SIZE2 .BS 1 1930 XSTART .BS 2 1940 YSTART .BS 1 1950 XSTOP .BS 2 1960 YSTOP .BS 1 |
[ The following advertisement is for historical interest only!!! The company closed its doors in 1988.]
A D V E R T I S E M E N T S-C ASSEMBLER II Version 4.0...................................$55.00 Includes Manual, Diskette with Assembler and sample source programs, and Quick Reference Card. Source code of Version 4.0 on disk.............................$95.00 Fully commented, easy to understand and modify to your own tastes. Cross Assembler Patches for 6809...............................$20.00 Requires possession of Version 4.0. Enables you to develop programs for the Motorola 6809 CPU. (The MILL from Stellation, EXCEL-9 from ESD Laboratories, or the Radio Shack Color Computer.) Cross Assembler for 6800.......................................$22.50 Requires possession of Version 4.0. Enables you to develop programs for the Motorola 6800, 6801, and 6802 CPUs. AAL Quarterly Disks.......................................each $15.00 Each disk contains all the source code from three issues of "Apple Assembly Line", to save you lots of typing and testing time. QD#1: Oct - Dec 1980 QD#4: Jul - Sep 1981 QD#2: Jan - Mar 1981 QD#5: Oct - Dec 1981 QD#3: Apr - Jun 1981 Double Precision Floating Point for Applesoft..................$50.00 Provides 21-digit precision for Applesoft programs. Includes subroutines for standard math functions. Some Simple Integer BASIC Games................................$10.00 Includes 4x4x4 tic-tac-toe, lo-res space war, lo-res jig-saw puzzle, and mastermind. Blank Diskettes..............................package of 20 for $50.00 Verbatim Datalife, with hub rings, no labels, in plain white jackets, in cellophane wrapper. Lower-Case Display Encoder ROM.................................$25.00 Works only Revision level 7 Apples. Replaces the encoder ROM. Comes with instructions. Diskette Mailing Protectors.....................10-99: 40 cents each 100 or more: 25 cents each Corrugated folder specially designed for mailing mini-floppy diskettes. Fits in standard 6x9-inch envelope. (Envelopes 5-cents each, if you need them.) Zip-Lock Bags (2-mil, 6"x9")............................100 for $8.50 (2-mil, 9"x12")..........................100 for $13.00 Books, Books, Books......................compare our discount prices! "Beneath Apple DOS", Worth & Lechner.............($19.95) $18.00 "What's Where in the Apple", William Leubert.....($14.95) $14.00 "6502 Assembly Language Programming", Leventhal..($16.99) $16.00 "Apple Assembly Language", Don & Kurt Inman......($12.95) $12.00 *** A D V E R T I S E M E N T |
Coding in BASIC has several frustrations for the assembly language programmer. One small but constant irritant for me has been the inability to directly specify hexadecimal values in Applesoft statements or in response to an INPUT command. I finally decided to do something about it when I read Bob Sander-Cederlof's article on the CHRGET routine in the September Apple Assembly Line. The result is the short program shown here.
My goal was to be able to enter a hex constant, defined as a "$" followed by one or more hex digits, anywhere Applesoft would allow an integer constant to appear. I nearly succeeded -- I'll discuss the exceptions a little later. I now can write statements like:
100 FOR I = $0 TO $FF 110 INPUT X,Y 120 Z(I) = $100*X + Y - $3DEF
The responses to the INPUT statement may also be hex constants. Values may range from -$FFFF (-65535) to $FFFF (65535); the left-most bit is not considered a sign bit.
My program is set up by BRUN-ning the object file XB.A/S HEX CONSTANTS (see line 1010). Initialization consists of modifying the Applesoft CHRGET subroutine to branch into new code starting at line 1400. As you may recall, CHRGET is used by the BASIC interpreter to fetch characters and tokens from the program text of keyboard when a program is executing. The new CHRGET code watches for a "$" character; when one is found, it scans forward until it hits a character which is not a hex digit, converting to a binary value (in VAL) on the fly.
Variable IDX serves two purposes. It is normally negative, signifying that characters are to be fetched without special action until a "$" is encountered. After a hex constant is found and converted to a binary value, IDX becomes a positive index into a power-of-ten table to facilitate converting VAL to a decimal value. Each subsequent call to CHRGET then returns a successive character of the decimal integer representation of VAL until IDX becomes -1, the entire value has been transformed from hex to decimal, and the normal mode is restored.
There are, of course, several complications. One is the BASIC "DEF" command, which happens to consist of a string of hex digits. Applesoft therefore parses a constant like "$3DEF" as the ASCII characters "$" and "3" followed by the DEF token (hex 88). Lines 1760 to 1840 take care of that.
A more serious complication is the existence of a frequently used alternate entry point to CHRGET called CHRGOT. CHRGOT is called to fetch the previous item from the text rather than the next one. It seems that numeric constants are parsed from several places within the Applesoft interpreter, with some using CHRGOT and others not. When I fixed things up so CHRGOT would work for inline constants and the INPUT command, it no longer worked for values in DATA statements (or for hex line numbers, for that matter!)
The trick that makes CHRGOT work (most of the time) is to back up TXTPTR and then return a leading zero to start off the converted decimal value. The zero causes no consternation for the parts of the interpreter that see it and is not missed by those that don't. If CHRGOT is not called, however, TXTPTR should not be backed up. You can't win!
I hope others will be able to make use of this routine -- better, that someone will overcome the problem with DATA statement values. It has been quite valuable to me as it is, as well as quite an education in understanding the inner workings of the Applesoft interpreter.
1000 .OR $300 1010 .TF XB.A/S HEX CONSTANTS 1020 *--------------------------------- 1030 * 1040 * APPLESOFT HEX CONSTANTS 1050 * 1060 * WRITTEN BY DAVID H. BARTLEY 1070 * AUSTIN, TEXAS -- AUGUST 1981 1080 * 1090 * TO INITIALIZE: 1100 * BRUN THIS PROGRAM (XB.A/S HEX CONSTANTS) 1110 * 1120 * TO USE: 1130 * PRECEDE HEX CONSTANTS 1140 * WITH A "$" CHARACTER 1150 * 1160 *--------------------------------- 1170 BASIC .EQ $E003 SOFT RE-ENTRY 1180 CHRGET .EQ $00B1 A/S CHRGET RTN 1190 CHRGOT .EQ $00B7 A/S CHRGOT RTN 1200 CHRCHK .EQ CHRGOT+3 1210 TXTPTR .EQ $B8 A/S TEXT PTR 1220 OVERR .EQ $E8D5 OVERFLOW ERROR 1230 TEMP .EQ $FC 16-BIT TEMPORARY 1240 VAL .EQ $FE 16-BIT VALUE 1250 *--------------------------------- 1260 INIT 1270 LDA #$4C MODIFY CHRGET 1280 STA CHRGET TO CALL HERE 1290 LDA #NEW.CHRGET 1300 STA CHRGET+1 1310 LDA /NEW.CHRGET 1320 STA CHRGET+2 1330 JMP BASIC RETURN TO A/S 1340 NEXTCH 1350 INC TXTPTR DUPLICATE THE 1360 BNE .10 OLD CHRGET 1370 INC TXTPTR+1 1380 .10 JMP CHRGOT 1390 *--------------------------------- 1400 NEW.CHRGET 1410 BIT IDX NORMAL MODE? 1420 BPL .60 -NO 1430 * 1440 * CHECK FOR "$" AS NEXT CHARACTER 1450 * 1460 JSR NEXTCH GET CHAR 1470 CMP #$24 "$"? 1480 BNE .50 -NO, RETURN IT 1490 .10 1500 * PARSE A HEX NUMBER AND CONVERT 1510 * IT TO A BINARY VALUE 1520 * 1530 LDA #0 1540 STA VAL VAL = 0 1550 STA VAL+1 1560 LDA #4 INDEX TO POWER 1570 STA IDX 1580 .20 1590 JSR NEXTCH GET HEX DIGIT 1600 BEQ .40 -EOL OR ":" 1610 SEC 1620 SBC #$30 CHECK FOR DIGIT 1630 BMI .35 -NOT A DIGIT 1640 CMP #10 1650 BCC .30 -OK (0-9) 1660 SBC #17 1670 BMI .40 -NOT A DIGIT 1680 CMP #6 1690 BCS .40 -NOT A DIGIT 1700 ADC #10 1710 .30 JSR ASL4 MULT VAL BY 16 1720 ORA VAL ADD NEW DIGIT 1730 STA VAL 1740 JMP .20 1750 .35 1760 CMP #$88 "DEF" TOKEN? 1770 BNE .40 -NO 1780 JSR ASL4 -YES 1790 LDA VAL 1800 ORA #$0D ASL BY 12 AND 1810 STA VAL+1 ADD $0DEF 1820 LDA #$EF 1830 STA VAL 1840 BNE .20 (ALWAYS) 1850 .40 1860 LDA TXTPTR BACK UP THE 1870 BNE .41 1880 DEC TXTPTR+1 1890 .41 DEC TXTPTR 1900 LDA TXTPTR SAVE TXTPTR 1910 STA TEMP IN CASE IS IS 1920 LDA TXTPTR+1 DECREMENTED 1930 STA TEMP+1 BY THE CALLER 1940 * 1950 LDA #$30 ASCII "0" 1960 .50 JMP CHRCHK -EXIT 1970 .60 1980 * CONVERT BINARY VALUE TO DECIMAL 1990 * AND RETURN THE NEXT ASCII DIGIT 2000 * 2010 LDA TEMP FIX ANY ATTEMPT 2020 STA TXTPTR TO DECREMENT 2030 LDA TEMP+1 TXTPTR 2040 STA TXTPTR+1 2050 STX SAVE.X 2060 LDX IDX POWER OF TEN 2070 DEC IDX 2080 LDA #$30 ASCII "0" 2090 .70 2100 PHA ASCII DIGIT 2110 LDA VAL 2120 CMP LO.TENS,X SET CARRY 2130 LDA VAL+1 2140 SBC HI.TENS,X 2150 BCC .80 -EXIT LOOP 2160 STA VAL+1 2170 LDA VAL 2180 SBC LO.TENS,X 2190 STA VAL 2200 PLA ASCII DIGIT 2210 CLC 2220 ADC #1 INCREMENT IT 2230 BNE .70 -LOOP 2240 .80 2250 PLA ASCII DIGIT 2260 LDX SAVE.X 2270 .90 2280 JMP CHRCHK PROCESS IT 2290 *--------------------------------- 2300 ASL4 JSR ASL2 ASL VAL BY 4 2310 ASL2 JSR ASL1 ASL VAL BY 2 2320 ASL1 ASL VAL ASL VAL BY 1 2330 ROL VAL+1 2340 BCS OVFLOW -OVERFLOW ERROR 2350 RTS -EXIT 2360 OVFLOW 2370 JMP OVERR REPORT OVERFLOW 2380 *--------------------------------- 2390 LO.TENS .DA #1 2400 .DA #10 2410 .DA #100 2420 .DA #1000 2430 .DA #10000 2440 HI.TENS .DA /1 2450 .DA /10 2460 .DA /100 2470 .DA /1000 2480 .DA /10000 2490 IDX .DA #$FF TABLE INDEX 2500 SAVE.X .DA #0 SAVE X-REG 2510 *--------------------------------- 2520 ZZZZZZ .EN |
[ Sandy is an M.D. in Port Chester, New York. You have probably seen his excellent articles and programs in NIBBLE. ]
The following program is a developmental tool for line-editing Applesoft programs. It places the line you specify at the top of the screen, ready to be cursor edited. The line is displayed without added blanks at the end of each screen line, which can mess up editing of PRINT statements. Obviously, adding Konzen-like PLE features would make it much nicer, but that's a story for another day.
The program loads at the ever-popular $300. If you BRUN it, or BLOAD and CALL768, it installs itself. To use it, type a slash and a line number. For example, to edit line 150, type "/150" and a carriage return. The screen will be cleared and line 150 displayed on the top. The cursor will be placed over the first character, and you will be ready to edit it with standard cursor-editing techniques. (If there is no line 150 in memory, the bell will ring instead.)
Several aspects of the code should be of interest to assembly language programmers:
Since I did not make any test to determine whether or not the program is RUNning at the time the slash is trapped in my filter, you have to be careful about using the slash character in REM statements. For example, "REM /150" will clear the screen and list line 150 at the top before proceeding. Other combinations of "/" in REM's may blow up. Also, typing "/" when Applesoft is executing an INPUT statement is now dangerous. Anyone know how to fix this?
1000 *--------------------------------- 1010 * LINE.EDIT 1020 * 1030 * BY SANDY MOSSBERG 1040 * 1050 * COMMERCIAL RIGHTS RESERVED 1060 * 1070 *--------------------------------- 1080 * 1.PACKS PROGRAM LINE FOR EASY EDITING. 1090 * 1100 * 2.USES CHRGET/CHRGOT FILTER ROUTINE NOTED IN AAL 9/81. 1110 * 1120 * 3.CHARACTER OUTPUT ROUTINE MODIFIED FROM APSOFT ROM 1130 * CODE (LIST, $D6A5-$D765). 1140 * 1150 * 4.INSTALLATION AND USE: 1160 * (A) BRUN LINE.EDIT. 1170 * (B) COMMAND "/LINENUMBER" PRODUCES PACKED LINE AT 1180 * TOP OF SCREEN. 1190 * (C) IF CHRGET/CHRGOT VECTOR DESTROYED BY APSOFT 1200 * COLDSTART (]FP, *E000G, *CTL-B), RESET LINE.EDIT 1210 * VECTOR BY CALL 768. 1220 *--------------------------------- 1230 .OR $300 1240 *--------------------------------- 1250 * APPLESOFT POINTERS 1260 *--------------------------------- 1270 AS.FORPNT .EQ $85 ;HOLD Y-REGISTER 1280 AS.LOWTR .EQ $9B,$9C ;LOCATION OF CHARACTER OR TOKEN IN PGM 1290 AS.DSCTMP .EQ $9D,$9E ;LOCATION IN KEYWORD TABLE 1300 *--------------------------------- 1310 * APPLESOFT CHRGET/CHRGOT 1320 *--------------------------------- 1330 AS.CHRGET .EQ $B1 ;GETS CHARACTER AT TEXT POINTER 1340 AS.TXTPTR .EQ $B8,$B9 ;TEXT POINTER 1350 AS.CHREXT .EQ $BA ;CHRGET/CHRGOT VECTOR TO LINE.EDIT 1360 AS.CHRENT .EQ $BE ;RE-ENTRY TO CHRGET/CHRGOT 1370 *--------------------------------- 1380 * APPLESOFT ROM 1390 *--------------------------------- 1400 AS.FNDLIN .EQ $D61A ;ADDR NMBR IN LINNUM ($50,$51) TO LOWTR 1410 AS.CRDO .EQ $DAFB ;LINEFEED 1420 AS.OUTSP .EQ $DB57 ;OUTPUT SPACE 1430 AS.OUTDO .EQ $DB5C ;OUTPUT CHARACTER 1440 AS.FRMEVL .EQ $DD7B ;FORMULA AT TEXT POINTER TO FAC ($9D-$A2) 1450 AS.GETADR .EQ $E752 ;FAC TO INTEGER IN LINNUM ($50,$51) 1460 AS.LINPRT .EQ $ED24 ;PRINT DECIMAL OF (A,X) 1470 *--------------------------------- 1480 * MONITOR ROM 1490 *--------------------------------- 1500 MON.TABV .EQ $FB5B ;VTAB TO VALUE IN (A) 1510 MON.HOME .EQ $FC58 ;HOME CURSOR, CLEAR SCREEN 1520 MON.BELL .EQ $FF3A ;BEEP! 1530 .PG 1540 *--------------------------------- 1550 * PUT LINE.EDIT VECTOR INTO CHRGET/CHRGOT 1560 *--------------------------------- 1570 START LDA #$4C ;JMP 'LINE.EDIT' 1580 STA AS.CHREXT 1590 LDA #EDIT 1600 STA AS.CHREXT+1 1610 LDA /EDIT 1620 STA AS.CHREXT+2 1630 RTS1 RTS 1640 *--------------------------------- 1650 * CHECK FOR VALID COMMAND 1660 *--------------------------------- 1670 EDIT CMP #$2F ;IS IT A SLASH (/)? 1680 BNE .1 ;NO. RETURN 1690 INC AS.TXTPTR ;YES. BUMP TEXT POINTER 1700 BNE .2 ;BRANCH ALWAYS 1710 *--------------------------------- 1720 * RETURN TO CHRGET/CHRGOT OR CALLER 1730 *--------------------------------- 1740 .1 CMP #$3A ;IF COLON (EOS), SET Z AND C 1750 BCS RTS1 ; FLAGS AND RETURN TO CALLER 1760 JMP AS.CHRENT ;IF NOT EOS, RE-ENTER CHRGET/CHRGOT 1770 *--------------------------------- 1780 * FIND LOCATION OF LINE NUMBER 1790 *--------------------------------- 1800 .2 JSR AS.FRMEVL ;PUT LINE NUMBER INTO FAC ($9D-$A2) 1810 JSR AS.GETADR ;PUT FAC INTO LINNUM ($50,$51) 1820 JSR AS.FNDLIN ;PUT ADDR OF LINE INTO LOWTR 1830 BCC .5 ;CARRY CLEAR IF LINE NMBR NOT FOUND 1840 *--------------------------------- 1850 * CLEAR SCREEN AND SET TO ROW 2, COLUMN 2 1860 *--------------------------------- 1870 JSR MON.HOME 1880 JSR AS.CRDO 1890 JSR AS.OUTSP 1900 *--------------------------------- 1910 * PRINT LINE NUMBER 1920 *--------------------------------- 1930 LDY #02 ;SET INDEX TO LINE NUMBER BYTES 1940 LDA (AS.LOWTR),Y ;PUT LINE NUMBER LO 1950 TAX ; INTO (X) 1960 INY 1970 LDA (AS.LOWTR),Y ;PUT LINE NUMBER HI INTO (A) 1980 STY AS.FORPNT ;HOLD (Y) 1990 JSR AS.LINPRT ;PRINT DECIMAL OF (A,X) 2000 *--------------------------------- 2010 * GET CHARACTER OR TOKEN 2020 *--------------------------------- 2030 LDA #$20 ;SPACE 2040 .3 LDY AS.FORPNT ;RESTORE (Y) 2050 .4 JSR AS.OUTDO ;PRINT CHARACTER IN (A) 2060 INY 2070 LDA (AS.LOWTR),Y ;GET CHARACTER OR TOKEN 2080 BNE .8 ;IF NOT EOS (0), GET MORE 2090 .PG 2100 *--------------------------------- 2110 * TWO ENDINGS -- ONE HAPPY, ONE SAD 2120 *--------------------------------- 2130 LDA #00 ;LINE WAS FOUND. END WITH 2140 JMP MON.TABV ; CURSOR AT ROW 2, COLUMN 2 2150 .5 JSR MON.BELL ;LINE WAS NOT FOUND. END WITH 2160 JMP AS.CRDO ; CURSOR BELOW COMMAND INPUT 2170 *--------------------------------- 2180 * GET CHARACTER IN KEYWORD TABLE 2190 *--------------------------------- 2200 .6 INY 2210 BNE .7 2220 INC AS.DSCTMP+1 2230 .7 LDA (AS.DSCTMP),Y 2240 RTS 2250 *--------------------------------- 2260 * PRINT CHARACTER OR KEYWORD 2270 *--------------------------------- 2280 .8 BPL .4 ;NON-TOKEN IS POS ASCII 2290 SEC ;TOKEN MINUS $7F EQUALS INDEX TO 2300 SBC #$7F ; LOCATION OF KEYWORD IN TABLE 2310 TAX ;PUT INDEX IN (X) 2320 STY AS.FORPNT ;HOLD (Y) 2330 LDY #$D0 ;KEYWORD TABLE STARTS AT $D0D0 2340 STY AS.DSCTMP 2350 LDY #$CF 2360 STY AS.DSCTMP+1 2370 LDY #$FF ;WHEN BUMPED, (Y) WILL BE ZERO 2380 .9 DEX ;DEC INDEX TO KEYWORD LOCATION 2390 BEQ .11 ;WHEN (X) IS ZERO, KEYWORD LOCATED 2400 .10 JSR .6 ;GET CHARACTER IN KEYWORD TABLE 2410 BPL .10 ;IF POS ASCII, GET ANOTHER 2420 BMI .9 ;IF NEG ASCII, DEC LOCATION INDEX 2430 .11 JSR AS.OUTSP ;PRINT SPACE 2440 .12 JSR .6 ;GET CHARACTER IN KEYWORD TABLE 2450 BMI .13 ;IT'S THE FINAL CHAR IN KEYWORD 2460 JSR AS.OUTDO ;PRINT NON-FINAL CHAR (POS ASCII) 2470 BNE .12 ;BRANCH ALWAYS 2480 .13 JSR AS.OUTDO ;PRINT FINAL CHAR (NEG ASCII) 2490 LDA #$20 ;SPACE 2500 BNE .3 ;BRANCH ALWAYS 2510 *--------------------------------- 2520 SIZE .EQ *-START 2530 .PG |
In the April 1981 issue of AAL I printed a subroutine to read a line from the keyboard or a text file into an Applesoft string. The original version had a minor flaw (or major, if you happened to run into it): it left the high-order bit on in each byte, so that Applesoft could not compare them properly with strings from other sources. I printed a correction in a later issue, which stripped off the leading bit from each byte before putting it in the string.
Now Sherm Ostrowsky (from Goleta, California) has pointed out a more elegant solution. He uses a subroutine inside Applesoft that reads a line, terminates it with hex 00, and strips off the leading bit from each byte. The subroutine starts at $D52C. The only thing it doesn't do that we need is give us the length of the input line. Here is a commented listing of it.
1000 *--------------------------------- 1010 * APPLESOFT LINE INPUT SUBROUTINE 1020 *--------------------------------- 1030 .OR $D52C 1040 .TA $82C 1050 *--------------------------------- 1060 MON.PROMPT .EQ $33 1070 MON.RDLINE .EQ $FD6A 1080 BUFFER .EQ $200 1090 *--------------------------------- 1100 AS.INLINE 1110 LDX #$80 NULL CHARACTER 1120 INLIN2 STX MON.PROMPT FOR THE PROMPT CHARACTER 1130 JSR MON.RDLINE READ A LINE INTO BUFFER 1140 CPX #239 TRUNCATE TO 239 CHARACTERS 1150 BCC .1 1160 LDX #239 1170 .1 LDA #0 MARK END OF LINE WITH $00 1180 STA BUFFER,X 1190 TXA # REAL CHARS IN LINE 1200 BEQ .3 EMPTY LINE 1210 .2 LDA BUFFER-1,X STRIP OFF ALL SIGN BITS 1220 AND #$7F 1230 STA BUFFER-1,X 1240 DEX 1250 BNE .2 1260 .3 LDA #0 1270 LDX #BUFFER-1 1280 LDY /BUFFER-1 1290 RTS |
Since $D52C stores $80 (null) in the prompt character, you might want to load the X-register with $87 (bell) and enter at $D52E instead.
Since the subroutine returns with $FF in the X-register, and we need the length of the input line instead, we can use the following code to get the line length in X:
JSR $D52C .1 INX LDA $200,X BNE .1
Here is a new version, then, of my fast string input subroutine:
1000 *--------------------------------- 1010 * FAST STRING INPUT ROUTINE 1020 * &GET <STRING VARIABLE> 1030 * ACCEPTS ANY CHARACTER, UNLIKE NORMAL INPUT 1040 *--------------------------------- 1050 .OR $300 1060 .TF B.FAST READ 1070 *--------------------------------- 1080 AS.CHRGET .EQ $00B1 1090 AS.SYNERR .EQ $DEC9 1100 AS.INLINE .EQ $D52C 1110 AS.PTRGET .EQ $DFE3 1120 AS.GETSPA .EQ $E452 1130 AS.MOVSTR .EQ $E5E2 1140 *--------------------------------- 1150 ADDR .EQ $71 AND 72 1160 PNTR .EQ $83 AND 84 1170 LENGTH .EQ $9D 1180 BUFFER .EQ $200 1190 *--------------------------------- 1200 GET CMP #$BE "GET" TOKEN 1210 BEQ .1 YES 1220 JMP AS.SYNERR SORRY... 1230 .1 JSR AS.CHRGET SET UP THE FOLLOWING CHARACTER 1240 JSR AS.PTRGET FIND THE STRING VARIABLE POINTER 1250 JSR AS.INLINE READ A LINE INTO BUFFER 1260 .2 INX COMPUTE THE LENGTH OF THE LINE 1270 LDA BUFFER,X 1280 BNE .2 NOT AT END OF LINE YET 1290 STX LENGTH SAVE LINE LENGTH 1300 TXA 1310 JSR AS.GETSPA GET SPACE IN STRING AREA 1320 LDY #0 SET UP STRING VARIABLE POINTER 1330 STA (PNTR),Y LENGTH 1340 INY 1350 LDA ADDR 1360 STA (PNTR),Y ADDRESS (LO-BYTE) 1370 INY 1380 LDA ADDR+1 1390 STA (PNTR),Y ADDRESS (HI-BYTE) 1400 LDY /BUFFER SET UP TO COPY STRING DATA 1410 LDX #BUFFER INTO STRING AREA 1420 LDA LENGTH 1430 JMP AS.MOVSTR COPY IT NOW, AND RETURN |
Here is how you might use it from an Applesoft program, to read a series of lines from a file:
100 D$ = CHR$ (4) 110 PRINT D$"BLOAD B.FAST READ" 120 POKE 1013,76 : POKE 1014,0 : POKE 1015,3 210 PRINT D$"OPEN MY.FILE" 220 PRINT D$"READ MY.FILE" 230 FOR I = 1 TO 10 240 & GET A$(I) 250 NEXT I
Note that the subroutine is fully relocatable. Since there are no internal JMP's or JSR's, and no internal variables, you can load the program anywhere it will fit and run it without any modifications. Just be sure to change line 120 above to POKE the correct address in 1014 and 1015.
Peter Bartlett (subscriber in Chicago, IL) sent me some source code for patches to the Apple Monitor ROM. Of course, patching a ROM may be a little too much hardware work, but if you have a 16K RAM card you can put the revised monitor up there. The space needed for the patch is stolen from the cassette I/O command, so if you install this patch you will lose cassette I/O.
Peter's patches add the ASCII dump to the Apple Monitor's hex dump. That is, when I type a command like "800.87F" in the monitor, it will not only print out the hex values, but also the ASCII values of each byte. I modified his patches a little, to shorten the code to the following:
1000 *--------------------------------- 1010 * PATCHES TO ADD ASCII DUMP 1020 * TO THE APPLE MONITOR 1030 *--------------------------------- 1040 A1L .EQ $3C 1050 COUT .EQ $FDED 1060 *--------------------------------- 1070 .OR $FDB8 1080 .TA $0DB8 1090 JSR PATCH CALL MY PATCH CODE 1100 *--------------------------------- 1110 .OR $FCC9 1120 .DA $0CC9 1130 PATCH 1140 JSR COUT PRINT A SPACE 1150 LDA (A1L),Y GET BYTE TO BE DISPLAYED 1160 PHA SAVE IT ON STACK 1170 LDA A1L LOW BYKTE OF DUMP ADDRESS 1180 AND #7 MASK LINE POSITION 1190 CLC 1200 ADC #41 COMMPUTE HORIZONTAL OFFSET 1210 TAY 1220 PLA GET BYTE FROM STACK 1230 STA ($28),Y STORE IT ON THE SCREEN 1240 LDY #0 RESTORE Y 1250 RTS REJOIN ORIGINAL CODE |
These patches will work with either the old monitor ROM, or the Autostart ROM. The JSR PATCH line goes right into the hex dump program, over the top of a JSR COUT that printed a space. That space is normally printed right before the next byte value is printed in hex. The address of the next byte is kept in A1L,A1H ($3C,3D). The Y-register has 0 in it.
The main patch subroutine is stored on top of part of the cassette tape I/O, at $FC99; it begins with the JSR COUT that was covered up at $FDB8. Lines 1150,1160 pick up the byte to be displayed and save it on the stack. Lines 1170-1210 compute the horizontal position for poking the byte on the screen. The low-order three bits of the memory address determine which column will be used, from column 31 through 38. Lines 1220,1230 retrieve the byte from the stack and store it into the screen buffer. Lines 1240,1250 restore Y=0 and return to the hex dump subroutine.
Note that this patch does not "print" the ASCII codes on the screen; it "pokes" them. Therefore if your printer is on, the printed copy will only contain the hex dump. The ASCII codes will only appear on the screen.
How do you patch the RAM card version of the monitor? Here's how I did it:
If you really do want to burn a new monitor ROM, follow the instructions with your ROM Burner.
Bob Potts called the other day with an interesting question. Suppose you want to jump to a particular line (by line number) of an Applesoft program, rather than simply returning from an assembly language program.
For example, I might call an assembly language subroutine at $300 with "CALL 768". After it does its job, the subroutine may decide either to return to the following Applesoft statement by an "RTS" instruction, or to GOTO a particular line number in the program. (Perhaps an error processing subroutine in the Applesoft code.) Can it be done?
Yes, and it is fairly simple. First we need to put the binary value of the line number into locations $50 and $51. Then we must jump to $D944 in the Applesoft ROMs to finish the GOTO operation. Here is the code to jump to line number 1350, for example:
GOTO1350 LDA #1350 LOW BYTE OF "1350" STA $50 LDA /1350 HIGH BYTE OF "1350" STA $51 JMP $D955 APPLESOFT GOTO PROCESSOR |
That's all there is to it!
I wrote a tiny little subroutine to demonstrate that this works. It expects to find the line number in $2FE and $2FF. You can POKE it there before CALLing 768. Here is my subroutine:
1000 *--------------------------------- 1010 * GO TO <LINE #> 1020 * POKE THE LINE # INTO 766,767 1030 * AND CALL768 TO GO TO IT 1040 *--------------------------------- 1050 .OR $300 1060 GOTO LDA $2FE 1070 STA $50 1080 LDA $2FF 1090 STA $51 1100 JMP $D944 |
Now here is a test program in Applesoft. Can you tell what it will do before you try it? The first two lines poke in the GOTO subroutine. The next five lines call the subroutine for successive values 1000, 2000, 3000 etc. up to 9000. The code in line 10000 jumps back to line 140 to continue the loop. Try it!
10 FOR I = 0 TO 12: READ A: POKE 768 + I,A: NEXT 20 DATA 173,254,2,133,80,173,255,2,133,81,76,68,217 100 FOR I = 1000 TO 9000 STEP 1000 110 IH = INT (I / 256): IL = I - IH * 256 120 POKE 766,IL: POKE 767,IH 130 CALL 768 140 NEXT I 150 END 1000 PRINT 1000: GOTO 10000 2000 PRINT 2000: GOTO 10000 3000 PRINT 3000: GOTO 10000 4000 PRINT 4000: GOTO 10000 5000 PRINT 5000: GOTO 10000 6000 PRINT 6000: GOTO 10000 7000 PRINT 7000: GOTO 10000 8000 PRINT 8000: GOTO 10000 9000 PRINT 9000 10000 POKE 766,140: POKE 767,0: CALL 768 |