Some Similarities Between Applesoft And PET BASIC
Garry Kiziak
Burlington, Ontario
I wonder how often a PET owner will pick up a magazine article and seeing that it applies to the Apple say "Oh! This isn't of any use to me.". Similarly, how many Apple owners will pick up a PET article and say the same thing? In fact, there is much to be learned about the PET from Apple articles and vice-versa. In many cases translating a useful utility or idea from the one computer to the other simply involves chaining a few addresses or some simple modification.
For example, a very useful idea appeared in Volume 3 Issue #1 of The Transactor dealing with the idea of reading data from a particular DATA statement. The following short PET program demonstrates how it works.
10 DATA FIRST, SECOND, THIRD 20 DATA FOURTH 30 READ A$, B$ 40 POKE 62, PEEK(119) : POKE63, PEEK(120) 50 READ A, B 60 DATA 1, 2, 3, 4 70 PRINT A$, B$, A, B
In this program, line 40 causes the READ statement in line 50 to get its data from the next DATA statement (i.e. line 60) instead of from the next data item which would have been in line 10.
To get this program to work on an Apple, it is only necessary to change the addresses in line 40. The following will do the trick.
40 POKE 125, PEEK(184) : POKE 126, PEEK(185)
In COMPUTE!, May, 1981, #12, Craig Peterson offered an elegant routine for the Apple that would allow you to input anything into a string (including commas, colons, etc.) without getting the EXTRA IGNORED error message. That routine is reproduced here.
1000 CALL 54572 1010 FOR B = 512 TO 751 : IF PEEK(B) <> 0 THEN NEXT 1020 IN$ = "" : POKE PEEK(131) + 256 * PEEK(132) + 1, 0 : POKE PEEK(131) + 256 * PEEK(132 + 2, 2 : POKE PEEK(131) + 256 * PEEK(132), B - 512 : IN$ = MID$(IN$, 1) : RETURN
Once again, to get this to work on the PET, certain addresses will have to be changed. The necessary changes are as follows:
1000 SYS 48117 1010 FOR B = 512 TO 592 : IF PEEK(B) <> 0 THEN NEXT 1020 IN$ = "" : POKE PEEK(68) + 256 * PEEK(69) + 1, 0 : POKE PEEK(68) + 256 * PEEK(69) + 2, 2 1030 POKE PEEK(68) + 256 * PEEK(69), B - 512 : IN$ = MID$(IN$, 1) : RETURN
Notice that line 1020 had to be split into two lines. This is because the maximum length of a line in PET BASIC (including line numbers) is 80 characters. On the Apple, it is 239 characters.
The use of this subroutine instead of the traditional INPUT statement on the PET has an additional advantage – an empty response (i.e. simply pressing <RETURN>) does not break out of the program. Instead, the program continues and IN$ = "".
The above routine will prompt with a question mark and then a flashing cursor just like the regular INPUT statement. If you would rather that the question mark did not appear, then simply change line 1000 to:
1000 SYS 46306
Of course, not all programs will be as easy to change, but many will – even machine language programs. The trick is to find the correct change of addresses.
Recently, while writing a program for the Apple to draw the graph of practically any curve, I found it necessary to write a short machine language program which would change a line in the program to whatever you wanted. Specifically, it would be used to enter the equation of the curve to be graphed without going through the process of stopping the program, entering a new line, then typing in GOTO 550 or some similar process. This CHANGE routine would allow you to enter the equation just like in a regular INPUT statement. An assembly listing of the program is given below (Program 1). Here is an example of a machine language program that can be easily modified for the PET. The modified assembly listing is given in Program 2. Notice that the only changes required are in the addresses to the external ROM routines or zero page locations and to the IOSAVE and IOREST ore routines of the Apple which had to be simulated on the PET.
To illustrate how this routine works on the PET, get into the monitor (i.e. SYS 4) and type in .M 033A 0399. Then use the cursor to edit the displayed screen as indicated in the memory dump in Program 3. Get back inth BASIC and type in the short program in Program 4. RUN it, type in anything that you want, and watch line 100 change. RUN it again as often as you like and line 100 changes to whatever you dictate.
Now RUN the program again, but this time type in X = T : Y = SIN(T) in response to the prompt. Notice the momentary display of the message EXTRA IGNORED. In the listing you will see that line 100 has been changed correctly, but IN$ is only equal to X = T. If you happen to need the correct value of IN$ later on in the program, this would be totally unacceptable.
Now type in Program 5, RUN it, and type in your responses as before. This time, when you type in X = T : Y = SIN (T), you will notice that line 100 gets changed correctly as does variable IN$.
A good question would be: "How do you know what to change the various addresses to?". Personally, I have found two excellent sources. The first is the article "Applesoft Internal Entry Points" which appeared in the original Apple Orchard. The second source is the PET/CBM Personal Computer Guide (second edition). Page 476-493 have Hex Addresses and Label References for most of the zero page addresses and ROM routines in the new BASIC 4.0 (and BASIC 3.0) ROMs. Even most of the names of the routines from these two sources are the same. So, the next time you see an Apple article or a PET article, don't put it off as not applying to you, make it work for you and learn by the experiences of others.
Program 1.
SOURCE FILE : CHANGE - APPLE ----- NEXT OBJECT FILE NAME IS CHANGE - APPLE.OBJ0 0341: 1 ORG $341 009B: 2 LOWTR EQU $9B 00B8: 3 TXTPTR EQU $B8 DEBE: 4 CHKCOM EQU $DEBE DD7B: 5 FRMEVL EQU $DD7B E752: 6 GETADR EQU $DD7B FF4A: 7 IOSAVE EQU $FF4A FF3F: 8 IOREST EQU $FF3F D56C: 9 CRUNCH EQU $D56C D61A: 10 FNDLIN EQU $D61A D412: 11 ERROR EQU $D41A 0341: 12 ; 0341: 13 ; SAVE REGISTERS 0341: 14 ; 0341: 20 4A FF 15 JSR IOSAVE 0344: 16 ; 0344: 17 ; GET THE LINE NUMBER 0344: 20 BE DE 19 JSR CHKCOM 0347: 20 7B DD 20 JSR FRMEVL 034A: 20 52 E7 21 JSR GETADR 034D: 22 ; 034D; 23 ; SAVE TEXT POINTER TEMPORARILY 034D: 24 ; 034D:A5 B8 25 LDA TXTPTR 034F:8D 9A 03 26 STA TEMPTXT 0352:A5 B9 27 LDA TXTPTR + 1 0354:8D 9B 03 28 STA TEMPTXT + 1 0357: 29 ; 0357: 30 ; TOKENIZE THE INPUT BUFFER 0357: 31 ; 0357: A9 00 32 LDA #$00 0359: 85 B8 33 STA TXTPTR 035B: A9 02 34 LDA #$2 035D: 85 B9 35 STA TXTPTR + 1 035F: A2 FF 36 LDX #$FF 0361: A0 04 37 LDY #$4 0363: 20 6C D5 38 JSR CRUNCH 0366: 39 ; 0366: 40 ; FIND THE LINE IN THE BASIC PROGRAM 0366: 41 ; 0366: 20 1A D6 42 JSR FNDLIN 0366: 90 2A 43 BCC NOPE 036B: 44 ; 036B: 45 ; CHANGE IT TO THE NEW LINE STORED IN THE INPUT BUFFER 036B: 46 ; 036B: A0 04 47 LDY #$04 036D: B9 FC 01 48 BEGIN LDA $1FC,Y 0370: F0 09 49 BEQ DONE 0372: 91 9B 50 STA (LOWTR),Y 0374: C8 51 INY 0375: D0 F6 52 BNE BEGIN 0377: A2 B0 53 LDX #$B0 0379: D0 1C 54 BNE ERR 037B: 55 ; 037B: 56 ; FILL UP THE LINE WITH COLONS 037B: 57 ; 037B: A2 3A 58 DONE LDX #$3A 037D: B1 9B 59 START LDA (LOWRE), Y 037F: F0 06 60 BEQ LAST 0381: 8A 61 TXA 0382: 91 9B 62 STA (LOWTR),Y 0384: C8 63 INY 0385: D0 F6 64 BNE START 0387: 65 ; 0387: 66 ; RESTORE TEXT POINTER 0387: 67 ; 0387: AD 9A 03 68 LAST LDA TEMPTXT 038A: 85 B8 69 STA TXTPTR 038C: AD 9B 03 70 LDA TEMPTXT + 1 038F: 85 B9 71 STA TXTPTR + 1 0391: 72 ; 0391: 73 ; RESTORE REGISTERS 0391: 74 ; 0391:20 3F FF 75 JSR IOREST 0394:60 76 RTS 0395:A2 5A 77 NOPE LDX #$5A 0397:4C 12 D4 78 ERR JMP ERROR 039A: 79 TEMPTXT DS $2
Program 2.
SOURCE FILE: CHANGE - PET ---- NEXT OBJECT FILE NAME IS CHANGE - PET. OBJO 033A: 1 ORG $33A 005C: 2 LOWTR EQU $5C 0077: 3 TXTPTR EQU $77 BEF5: 4 CHKCOM EQU $BEF5 BD98: 5 FRMEVL EQU $BD98 C92D: 6 GETADR EQU $C92D B4FB: 7 CRUNCH EQU $B4FB B5A3: 8 FNDLIN EQU $B5A3 B3CF: 9 ERROR EQU $B3CF 033A: 10 ; 03AA: 11 ; SAVE REGISTERS 033A: 12 ; 033A: 48 13 PHA 033B: 08 14 PHB 033C: 8A 15 TXA 033D: 48 16 PHA 033E: 98 17 TYA 033F: 48 18 PHA 0340: 19 ; 0340: 20 ; GET THE LINE NUMBER 0340: 21 ; 0340: 20 F5 BE 22 JSR CHKCOM 0343: 20 98 BD 23 JSR FRMEVL 0346: 20 2D C9 24 JSR GETADR 0349: 25 ; 0349: 26 ; SAVE TEXT POINTER TEMPORARILY 0349: 27 ; 0349: A5 77 28 LDA TXTPTR 034B: 8D 99 03 29 STA TEMPTXT 034E: A5 78 30 LDA TXTPTR + 1 0350: 8D 9A 03 31 STA TEMPTXT + 1 0353: 32 ; 0353: 33 ; TOKENIZE THE INPUT BUFFER 0353: 34 ; 0353: A9 00 35 LDA #$00 0355: 85 77 36 STA TXTPTR 0357: A9 02 37 LDA #$2 0359: 85 78 38 STA TXTPTR + 1 035B: A2 FF 39 LDX #$FF 035D: A0 04 40 LDY #$4 035F: 20 FB B4 41 JSR CRUNCH 0362: 42 ; 0362: 43 ; FIND THE LINE IN THE BASIC PROGRAM 0362: 20 A3 BG 45 JSR FNDLIN 0365: 90 2D 46 BCC NOPE 0367: 47 ; 0367: 48 ; CHANGE IT TO THE NEW LINE STORED IN THE INPUT BUFFER 0367: 49 ; 0367: A0 04 50 LDY #$04 0369: B9 FC 01 51 BEGIN LDA $1FC, Y 036C: F0 09 52 BEQ DONE 036E: 91 5C 53 STA (LOWTR), Y 0370: C8 54 INY 0371: D0 F6 55 BNE BEGIN 0373: A2 B0 56 LDX #$B0 0375: D0 1F 57 BNE ERR 0377: 58 ; 0377: 59 ; FILL UP THE LINE WITH COLONS 0377: 60 ; 0377: A2 3A 61 DONE LDX #$3A 0379: B1 5C 62 START LDA (LOWTR), Y 037B: F0 06 63 BEQ LAST 037D: 8A 64 TXA 037E: 91 5C 65 STA (LOWTR), Y 0380: C8 66 INY 0381: D0 F6 67 BNE START 0383: 68 ; 0383: 69 ; RESTORE TEXT POINTER 0383: 70 ; 0383: AD 99 03 71 LAST LDA TEMPTXT 0386: 85 77 72 STA TXTPTR 0388: AD 9A 03 73 LDA TEMPTXT + 1 038B: 85 78 74 STA TXTPTR + 1 038D: 75 ; 038D: 76 ; RESTORE REGISTERS 038D: 77 ; 038D: 68 78 PLA 038E: A8 79 TAY 038F: 68 80 PLA 0390: AA 81 TAX 0391: 28 82 PLP 0392: 68 83 PLA 0393: 60 84 RTS 0394: A2 5A 85 NOPE LDX #$5A 0396: 4C CF B3 86 ERR JMP ERROR 0399: 87 TEMPTXT DS $2
Program 3.
" : 033A 48 08 8A 48 98 48 20 F5 " : 0342 BE 20 98 BD 20 2D C9 A5 " : 034A 77 8D 99 03 A5 78 8D 9A " : 0352 03 A9 00 85 77 A9 02 85 " : 035A 78 A2 FF A0 04 20 FB B4 " : 0362 20 A3 B5 90 2D A0 04 B9 " : 036A FC 01 F0 09 91 5C C8 D0 " : 0372 F6 A2 B0 D0 1F A2 3A B1 " : 037A 5C F0 06 8A 91 5C C8 D0 " : 0382 F6 AD 99 03 85 77 AD 9A " : 038A 03 85 78 68 A8 68 AA 28 " : 0392 68 60 A2 5A 4C CF B3 EF
Program 4.
10 INPUT" {CLEAR} CHANGE TO"; IN$ 20 CHANGE = 826: LINE = 100 30 SYSCHANGE, LINE 40 PRINT" {CLEAR} IN$ EQUALS :" IN$ : LIST 100 :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Program 5.
10 PRINT" {CLEAR} CHANGE TO "; : GOSUB 1000 20 CHANGE = 826 : LINE = 100 30 SYSCHANGE, LINE 40 PRINT" {CLEAR} IN$ EQUALS : "IN$: LIST 100 ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::::::::::::::::: 1000 SYS48117 1010 FOR B = 512 TO 592 : IF PEEK (b) <> 0 THEN NEXT 1020 IN$ = "" : POKE PEEK (68) + 256 * PEEK (69) + 1, 0 : [POKE PEEK (68) + 256 * PEEK (69) + 2, 2 1030 POKE PEEK (68) + 256* PEEK (69), B - 512 : IN$ = MID$ (IN$,1) : RETURN