Classic Computer Magazine Archive COMPUTE! ISSUE 56 / JANUARY 1985 / PAGE 131

Enhanced Applesoft INPUT

Dale W. Woolridge

Here's a way to make your APPLE II-family computer a little smarter and friendlier. The short routine is written in machine language, but you don't have to be an ML programmer to use it.

The loan-repayment program running on my Apple asked me a simple question:

HOW MANY MONTHLY PAYMENTS?

The loan was for 17 years, with 12 payments per year. So there I was, seated before a computer system that cost several thousand dollars, doing mental arithmetic! How nice if you could just enter the expression 17*12.

Apple users will guess that the Applesoft INPUT command was responsible for asking the question. It's one of the most useful commands in BASIC; it prints a prompt, waits for you to respond, and then stores your answer for future use.

Unfortunately, the INPUT command has some features that can be inconvenient—such as its inability to accept even simple mathematical expressions. So I wrote a program that adds a new command, &INPUT, to Applesoft. The syntax for &INPUT is almost the same as for INPUT, but its features are different.

A Few Improvements

If &INPUT is used with a numeric variable, you may enter any valid numeric expression. Numeric expression means anything that could legally appear to the right of the equals sign in a numeric assignment (LET) statement. &INPUT evaluates the expression and stores the result.

For example, if a program contains the lines:

100 PI = 3.1415926
110 & INPUT "GIVE ME A NUMERIC EXPRESSION"; A
120 PRINT "ITS VALUE IS "; A

you may enter something like:

SQR(PI) + PDL(O) + PEEK(127)

The PRINT statement in line 120 will show that the value of your expression is in A.

Unlike INPUT, &INPUT interprets a null expression (just pressing RETURN) as the value zero. &INPUT is smart enough to know where a numeric expression ends and a comment (or garbage) begins. If you enter something like 45 YEARS the &INPUT command knows that you really meant 45. INPUT would give you a REENTER message.

&INPUT may also be used with a string variable. Your input string may contain commas, quotes, or colons. The regular INPUT command is somewhat neurotic about these characters, in my opinion. Curiously, INPUT won't accept leading spaces in an input string, either. If you enter three spaces and a character, say, it interprets your input to be only one character long. But the improved &INPUT accepts the leading spaces as part of the string.

&INPUT treats most escape and control characters as INPUT does; however, it treats CTRL-C differently. If you enter CTRL-C as your input, &INPUT gives you a BREAK message, like INPUT. But then you can PRINT and change the values of any variables in your program and resume program execution with the CONT command. The variable in the &INPUT statement retains its previous value, unless you changed it in immediate mode.

One feature missing from &INPUT is the multiple variable function available with INPUT. A statement such as:

200 &INPUT "X,Y COORDINATES?"; X,Y

will not work, although the comparable INPUT statement would work.

How To Use &INPUT

The program is listed as a hex dump—a list of hexadecimal numbers which you can enter directly into the computer's memory with the Apple's built-in machine language monitor. You don't need to be a machine language programmer. Just enter the monitor by typing CALL—151 and pressing RETURN. An asterisk will appear on the screen. The * is the prompt for the monitor, similar to the bracket in BASIC.

Next, type 300.3AF after the asterisk and press RETURN. A hex dump appears on the screen. You have to replace those numbers with the new numbers in the program listing.

Starting with the first line, type 300: after the asterisk, then enter the first eight numbers. Press RETURN at the end of the line. Continue until the entire program is entered.

When you've checked that all your typing is correct, save the program to disk with this command:

BSAVE AMPER-INPUT, A$300, L$B0

Then exit the monitor by pressing the RESET button. To load, run, and initialize the program, simple type:

BRUN AMPER-INPUT

Program 1: Enhanced Applesoft INPUT—Hex Dump

0300- A0 02 B9 0C 03 99 F5 03
0308- 88 10 F7 60 4C 0f 03 C9
0310- 84 F0 05 A2 10 4C 12 D4
0318- 20 B1 00 C9 22 F0 06 20
0320- 5A DB 4C 30 03 20 81 DE
0328- A9 3B 20 C0 DE 20 3D DB
0330- 20 E3 DF 85 85 84 86 24
0338- 11 70 42 A5 B8 A4 B9 8D
0340- AE 03 8C AF 03 20 2C D5
0348- AD 00 02 D0 0C A9 30 8D
0350- 00 02 A9 00 8D 01 02 F0
0358- 0E C9 03 D0 03 4C 63 D8
0360- A9 00 85 B8 20 59 D5 A9
0368- 00 85 B8 A9 02 85 B9 20
0370- 52 DA AD AE 03 AC AF 03
0378- 85 B8 84 B9 60 20 2C D5
0380- AD 00 02 C9 03 D0 03 4C
0388- 63 D8 E8 BD 00 02 D0 FA
0390- 8E AD 03 8A 20 52 E4 AO
0398- 00 91 83 C8 A5 71 91 83
03A0- C8 A5 72 91 83 A2 00 AD
03A8- AD 03 4C E2 E5 00 00 00

Program 2: Enhanced Applesoft INPUT—Source Listing

331 000 *-------------------------------
1010 *       AMPER-INPUT
1020 *--------------------------------
1030        -OR $300	DECIMAL 768
1040 VALTYP –EQ $0011	$00 = NUMBER, $FF = STRING
1050 FRESPC –EQ $0071	PTR TO STRING (OBTAINED BY GETSPA)
1060 VARPNT –EQ $0083	PTR TO STRING DESCRIPTOR
1070 FORPNT –EQ $0085	PTR TO ADDR IN VAR TABLE
1080 CHRGET –EQ $00B1	GET NEXT CHAR, UPDATE TXTPTR
1090 TXTPTR –EQ $00B8	ADDR OF CHAR IN TEXT
1100 BUF    –EQ $0200	KEYBOARD BUFFER
1110 AMPERV –EQ $03F5	AMPERSAND VECTOR
1120 INLIN  –EQ $D412	APPLESOFT ERROR ROUTINE
1130 INLIN  –EQ $D52C	APPLESOFT LINE INPUT
1140 TOKEN  –EQ $D559	APPLESOFT TOKENIZER
1150 BREAK  –EQ $D863	BREAK IN LINE…
1160 LET1   –EQ $DA52	EVALUATE EXPRESSION
1170 STRPRT –EQ $DB3D	PRINT A STRING
1180 OUTQST –EQ $DB5A	PRINT A ?
1190 STRTXT –EQ $DE81	PREPARE STRING
1200 GETSPA –EQ $DEC0	SYNTAX CHARACTER CHECK
1210 PTRGET –EQ $DFE3	FIND ADDR OF VAR IN TABLE
1220 GETSPA –EQ $E452	GET SPACE FOR STRING
1230 MOVSTR –EQ $E5E2	MOVE A STRING
1240 *-------------------------------
1250 * INSTALL & VECTOR AT $3F5
1260 *-------------------------------
1270 BEGIN	LDY #$02	     MOVE 3-BYTE INSTRUCTION
1280 -1	LDA IMAGE, Y	     INTO AMPERSAND VECTOR
1290         STA AMPERV, Y     AT $3F5
1300		DEY
1310		BPL -1
1320		RTS
1330 IMAGE	JMP ENTRY	     IMAGE OF & VECTOR
1340 *-------------------------------------
1350 *      & NOW JUMPS HERE
1360 *-------------------------------------
1370 ENTRY	CMP #$84	     ‘INPUT’ TOKEN?
1380		BEQ INPUT	     YES
1390		LDX #$10	     ERROR CODE,
1400		JMP ERROR	     SYNTAX ERROR
1410 INPUT	JSR CHRGET	     GET CHAR AFTER ‘INPUT’
1420		CMP #$22	     ASCII QUOTE?
1430		BEQ -1		YES, PRINT PROMPT
1440		JSR OUTQST	USE ? FOR PROMPT
1450		JMP SEEVAR
1460 -1	JSR STRTXT	PREPARE STRING
1470		LDA #$3B	ASCII ‘;’ ?
1480		JSR SYNCHR	ERROR IF NOT
1490		JSR STRPRT	PRINT THE STRING
1500 SEEVAR  JSR PTRGET	GET POINTER INTO VAR TABLE
1510		STA FORPNT	AND SAVE IT
1520		STY FORPNT + 1
1530		BIT VALTYP	STRING VARIABLE?
1540		BVS STRING	YES
1550 *----------------------------
1560 * INPUT A NUMERIC EXPRESSION
1570 *----------------------------
1580		LDA TXTPTR	SAVE TXTPTR
1590		LDY TXTPTR + 1
1600		STA TMPPTR
1610		STY TMPPTR + 1
1620		JSR INLIN	GET A LINE FROM KEYBOARD
1630		LDA BUF	NULL INPUT?
1640		BNE -1		NO
1650		LDA #$30	ASCII ‘O’
1660		STA BUF	(SIMULATE INPUT OF 0)
1670		LDA #$00	END OF LINE
1680		STA BUF + 1
1690		BEQ -3		ALWAYS - NO NEED TO TOKENIZE
1700 -1	CMP #$03	CTRL – C?
1710		BNE -2		NO
1720		JMP BREAK
1730 -2	LDA #$00
1740		STA		TXTPTR TOKEN REQUIRES THIS
1750		JSR		TOKEN TOKENIZE INPUT LINE
1760 -3	LDA #BUF	POINT TXTPTR AT BUFFER		
1770		STA TXTPTR	SO LET1 KNOWS WHERE
1780		LDA /BUF	THE EXPRESSION IS
1790		STA TXTPTR + 1
1800		JSR LET 1	EVALUATE THE EXPRESSION
1810		LDA TMPPTR	RESTORE TXTPTR
1820		LDY TMPPTR + 1
1830		STA TXTPTR
1840		STY TXTPTR + 1
1850		RTS
1860 *---------------------------------------
1870 * INPUT A STRING FROM KEYBOARD
1880 *----------------------------------------
1890 STRING	JSR INLIN	GET A LINE FROM KEYBOARD
1900		LDA BUF	CHECK FOR CTRO-C
1910		CMP #$03
1920		BNE -1		NO
1930		JMP BREAK
1940 -1	INX		GET LENGTH OF STRING
1950		LDA BUF, X
1960		BNE -1
1970		STX LENGTH	AND SAVE IT
1980		TXA		TELL GETSPA THE LENGTH
1990		JSR GETSPA	GET SPACE FOR STRING
2000		LDY #$00	PUT DESCRIPTOR IN VAR TABLE
2010		STA (VARPNT), Y	LENGTH FIRST
2020		INY
2030		LDA FRESPC	ADDR OF STRING, LO BYTE
2040		STA (VARPNT), Y
2050		INY
2060		LDA FRESPC + 1 ADDR OF STRING, HI BYTE
2070		STA (VARPNT), Y
2080		LDX #BUF	COPY STRING INTO ITS
2090		LDA LENGTH	SPACE IN HIGH MEMORY
2100		JMP MOVSTR	(Y-REG HAS /BUF)
2110 *---------------------------------
2120 * SAVE AREA
2130 *---------------------------------
2140 LENGTH   –HS 00	LENGTH OF STRING
2150 TMPPTR   –HS 0000	TXTPTR

How It Works

Look at the machine language source listing (this is for reference purposes only; it's easier to enter the program from the hex dump). When Applesoft sees an ampersand, it JMPs to address $3F5. This address may contain another JMP instruction to the actual machine language program. Lines 1270–1330 set up a JMP at $3F5 to the start of the program, which is labeled ENTRY. These lines provide the code that is executed when AMPER-INPUT is initialized.

After the JMP to ENTRY, the Applesoft TXTPTR (at $B8 and $B9) points to the byte that follows the ampersand in memory, and the A register is loaded with the contents of that byte. Lines 1370–1400 check to make sure this byte contains the INPUT token.

Lines 1410–1490 print the string that follows &INPUT, or a question mark if there is no string. The STRTXT subroutine sets up the string so that STRPRT can print it; between the calls to these routines the program does a syntax check to make sure a semicolon follows the string.

Lines 1500–1540 look at the variable name in the &INPUT statement, find the variable's place in the BASIC program's variable table, and branch according to variable type. On exit from PTRGET the A and Y registers contain the address in the variable table, and VALTYP ($11) contains $FF to indicate a string variable, or $00 to indicate a numeric variable. It is important to save the address in FORPNT, because the LET1 subroutine looks for it there.

Numeric Variables

Lines 1580–1850 get the user's numeric expression, evaluate it, and store the value in the BASIC program's variable table. First, TXTPTR must be saved (lines 1580–1610) because it will be modified. The Applesoft 1NLIN routine is used to get the user's expression as a string. This routine puts the input into the keyboard buffer, resets the high-order bit of each byte to zero, puts a zero at the end of the string, and loads the registers—A with $00, Y with $01, and X with $FF.

Lines 1630–1690 check for null input. If null input, an ASCII zero is put into the buffer to simulate the input of a zero. Lines 1700–1720 check for CTRL-C, jumping to the BREAK routine if a CTRL-C was entered as the first character.

Lines 1730–1750 tokenize the contents of the buffer by replacing keywords with one-byte values. Lines 1760–1800 evaluate the expression. The evaluation is performed simply by pointing TXTFTR to the buffer and calling LET1. The LET1 routine not only evaluates expressions, but it stores the value in the BASIC program's variable table. It gets the address into the variable table from FORPNT (remember lines 1510–1520). LET1 can distinguish between floating point variables and integer variables because PTRGET puts $80 in address $12 to indicate an integer variable, and $00 otherwise (remember, $11 contains a $00 to indicate a numeric variable). Lines 1810–1850 restore TXTPTR and return to the BASIC program.

String Variables

Lines 1890–2100 get the user's string into the keyboard buffer, store it and its descriptor, and return to the BASIC program. Again, INLIN is used to get the string into the buffer. The program checks for CTRL-C and jumps to the BREAK routine if CTRL-C was entered as the first character of the string (lines 1900–1930). The program then finds the length of the string and puts it in the A register. The length is also stored locally.

The program calls GETSPA to find an address in high memory where the string can be stored; on entry to GETSPA the A register must contain the length, and on exit the address is in FRESPC. After the call to PTRGET (line 1500) the address of the string's descriptor (in the BASIC program's variable table) could be found in VARPNT. Lines 2000–2070 now use VARPNT to move the string descriptor into the variable table.

Finally, lines 2080–2100 call the MOVSTR routine to move the string itself into its spot in high memory. To call MOVSTR, the A register must contain the string's length, and the X and Y registers must contain its present address. The destination is the address in FRESPC. Note that the Y register was not explicitly loaded because it incidentally contains the proper byte, which is the high-order byte of the address of the keyboard buffer (see lines 2000, 2020, and 2050).