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