Reputation: 8579
I want to write a very simple basic program in commodore 64 that enters other basic commands.
Here's an example:
10 print"list"+chr$(13)
This prints list but doesn't hit enter.
My expectation is that I get the result of the list command.
How can I achieve this?
Upvotes: 1
Views: 1026
Reputation: 131
One way to execute BASIC commands built from a string is to manipulate the keyboard buffer. Consider the following BASIC subroutine that executes any BASIC command that you put into CM$ before GOSUBing to it:
100 PRINT CHR$(147)CHR$(17)CHR$(17)CHR$(17);CM$;
110 POKE631,13:POKE632,67:POKE633,79:POKE634,78:POKE635,84:POKE636,13
120 POKE198,6
130 PRINTCHR$(19):END
140 RETURN
100 Clear the screen, cursor down a few times, and then print your command, in CM$, onto the blank screen.
110 Poke RETURN, followed by 'C' 'O' 'N' 'T' followed by another RETURN into the keyboard buffer.
120 Tell the system that there are 6 new keystrokes in the buffer.
130 Move the cursor to the top of the screen and end the program.
This is where the magic happens. The C64 will begin processing the characters in the keyboard buffer.
140 Here is where the BASIC program will return to after your command is executed. Since this is a routine meant for GOSUBing, I just put a RETURN command here.
To test the subroutine, add the following lines:
10 CM$="LIST":GOSUB100
20 PRINT"MY PROGRAM CONTINUED RUNNING!": END
Here's an interesting page about it: Commodore 64 keyboard buffer tricks: deleting and creating BASIC lines from BASIC
Upvotes: 5
Reputation: 8579
The solution is incredibly easy:
10 list
You can basically type any basic command and it will just work:
10 print"Loading..."
20 load"*",8,1
This is actually all I needed to tinker with some small automations.
Upvotes: 0
Reputation: 3466
The short answer is, it can’t be done. The long answer is that it is possible, but not the way you’re doing it, and it’s likely to be very difficult. Most old-school BASICs, including the Commodore 64, don’t have an eval function, which is basically what you’re talking about. (According to David Lien, BBC BASIC had an EVAL command, and one of the Apple BASICs had an EXEC command that could read text from a file as if it had been typed from a keyboard, which would allow a slower emulation of an EVAL command.)
What the Commodore 64 and most old-school BASICs do have are calls to existing machine language routines. The BASIC commands are in memory somewhere, and you can transfer control to those commands if you know the memory address where the routine resides. In Microsoft variants, this is often the EXEC command. On the Commodore 64, it’s the SYS command.
The syntax is SYS <ADDRESS>
, where ADDRESS is the memory location you want to transfer control to. As long as that address contains a routine that has a return code, it will do its job and then transfer control back to your BASIC program.
Often, you’ll combine the SYS call with some POKEs (to provide data to the machine language subroutine) and/or some PEEKs (to look at what the routine has done).
Here’s an example inspired by the C64 Wiki:
9 rem clear screen
10 print chr$(147)
19 rem random cursor column and line
20 co = int(rnd(1)*40)
30 ln = int(rnd(1)*25)
39 rem position cursor
40 poke 211,co
50 poke 214,ln
60 sys 58640
70 print "x";
79 rem wait for keypress and quit
80 get i$
90 if i$="" then 80
91 if i$="r" then 20
99 end
This program creates a random number from 0 to 39 for CO, and then from 0 to 24 for LN. It pokes the value of C into memory location 211, the value of LN into memory location 214, and then calls the machine language routine at memory location 58640.
That routine interprets memory location 211 as the column, and location 214 as the line, to place the cursor at. So what this program does is randomly print an “x” somewhere on the screen; if you press “r”, it does it again, until you press some other key.
The program is in lower case because I used the VICE emulator for testing, and VICE (at least on macOS) automatically converts lowercase to uppercase, and uppercase to graphics characters, when pasting.
In your example, it’s much more difficult. While the entry point for the LIST command’s routine is easily discovered (42652, or hex $A69C), how you provide the line number or range to that routine is less easily discovered. Judging from this Commodore 64 disassembly, it may need to be provided as text. (Follow the LIST routine to the LINGET routine in the disassembly.)
And then you’d need to do this for every command you wanted to emulate.
It might also be possible to run your string through the BASIC evaluator routine at $AD9E for a true eval, but that would likely be an even more involved task.
If I were forced to do something like this, I would look into these options:
Here, for example, is a very rough example of option 1:
10 rem example of how to rewrite a line of code
20 ev$ = "list"
30 gosub 100
99 end
100 rem subroutine to create eval code
109 rem locate dummy line 1010;ls=line start;nl=next line
110 ls = 2049:rem start of basic in ram
120 nl = peek(ls)+peek(ls+1)*256
130 if peek(ls+2)+peek(ls+3)*256 <> 1010 then ls=nl:goto 120
139 rem found location, start writing
140 ls=ls+5
150 for i=1 to len(ev$)
160 poke ls+i,asc(mid$(ev$,i,1))
170 next i
180 poke ls+i,0
199 return
1000 rem subroutine to place eval code
1010 rem dummy line with lots of text to make it possible to put code here
1020 return
If you run this, you will see that line 1010 changes from a long remark to a remark that contains what is in EV$. There would still be a lot of work to do get this to be a real eval, however:
Upvotes: 4