1

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?

1
  • 1
    The PRINT function does only just that, it prints. You're looking to do some automation of a sort, and I doubt that the level of BASIC for the C-64 had anything capable of handling that, it would probably require assembler code. See if there is a KEY statement/function, it might be able to mimic the entry to keystrokes if it exists. Commented Dec 25, 2021 at 13:44

4 Answers 4

7

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

Sign up to request clarification or add additional context in comments.

1 Comment

Note that the value POKEd in 198 can be max 10 (the size of keyboard buffer)
6

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 an assembly return instruction, 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 CO 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:

  1. Use PEEK to locate a dummy line in a subroutine, and then POKE to rewrite that dummy line as the line to evaluate; then, GOSUB to that subroutine.
  2. Use the LOAD command to load the line to be evaluated after first writing it as a BASIC file.
  3. Because BASIC on the C64 is interactive, there should be a routine to call that evaluates individual lines. Find that, and determine how to provide that call the text you want evaluated.
  4. Scour the magazines and bulletin boards to see if anyone wrote an EVAL command for C64 BASIC back in the day.

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 to get this to be a real eval, however:

  1. It does not verify that EV$ is short enough to fit on line 1010; if EV$ is long, it will continue past the end of line 1010 and overwrite 1020 as well.
  2. It does not replace the text of EV$ with the tokenization necessary for the code to actually run. You would need a means of converting the word “LIST” to the tokenization of LIST (most likely either an array or a SYS call) for this to actually run the LIST (or whatever else is in EV$).
  3. It doesn’t update the length of line 1010, although this may not be necessary. Updating the length of line 1010 would also mean having to move line 1020 in memory, whereas leaving the length of line 1010 alone means not having to move line 1020.

1 Comment

Great read - thanks for making the effort to provide this well-researched and interesting answer!
1

Using this machine language routine by Giancarlo Mariani (look further for reference), you can interpret BASIC strings (lines 30-60):

10 x=49152
20 read a:if a<>-1 then poke x,a: x=x+1:goto 20

30 sys 49152, "print 2*3"
40 sys 49152, "print "+chr$(34)+"hello"+chr$(34)
50 sys 49152, "input x"
60 sys 49152, "print x"

999 end
1000 data 165,157,240,5,162,21,108,0,3,32,253,174,32,158
1010 data 173,32,130,183,165,122,133,251,165,123,133,252,132,2
1020 data 160,0,177,34,153,16,2,200,196,2,208,246,169,0
1030 data 153,16,2,169,128,133,157,169,16,133,122,169,2,133
1040 data 123,32,124,165,32,115,0,32,237,167,169,0,133,157
1050 data 165,251,133,122,165,252,133,123,96,-1

Reference: https://ready64.org/ccc/pagina.php?ccc=31&pag=081.jpg

The start address can be whatever. This routine is only for Commodore 64.

This is the disassembled routine:

.C:c000  A5 9D       LDA $9D
.C:c002  F0 05       BEQ $C009
.C:c004  A2 15       LDX #$15
.C:c006  6C 00 03    JMP ($0300)
.C:c009  20 FD AE    JSR $AEFD
.C:c00c  20 9E AD    JSR $AD9E
.C:c00f  20 82 B7    JSR $B782
.C:c012  A5 7A       LDA $7A
.C:c014  85 FB       STA $FB
.C:c016  A5 7B       LDA $7B
.C:c018  85 FC       STA $FC
.C:c01a  84 02       STY $02
.C:c01c  A0 00       LDY #$00
.C:c01e  B1 22       LDA ($22),Y
.C:c020  99 10 02    STA $0210,Y
.C:c023  C8          INY
.C:c024  C4 02       CPY $02
.C:c026  D0 F6       BNE $C01E
.C:c028  A9 00       LDA #$00
.C:c02a  99 10 02    STA $0210,Y
.C:c02d  A9 80       LDA #$80
.C:c02f  85 9D       STA $9D
.C:c031  A9 10       LDA #$10
.C:c033  85 7A       STA $7A
.C:c035  A9 02       LDA #$02
.C:c037  85 7B       STA $7B
.C:c039  20 7C A5    JSR $A57C
.C:c03c  20 73 00    JSR $0073
.C:c03f  20 ED A7    JSR $A7ED
.C:c042  A9 00       LDA #$00
.C:c044  85 9D       STA $9D
.C:c046  A5 FB       LDA $FB
.C:c048  85 7A       STA $7A
.C:c04a  A5 FC       LDA $FC
.C:c04c  85 7B       STA $7B
.C:c04e  60          RTS

Comments

0

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.

4 Comments

But it's not what you asked. And I'm sure Jerry Stratton spent a lot of time on his answer.
@WillHartung Even if I understand that Jerry put some time in writing his answer... It is not what I asked. I just asked, literally: "I want to write a very simple basic program in commodore 64 that enters other basic commands." My example is quite clear I'd say and I specified "very easy". His answer is clearly very far from easy. I will be happy to upvote his answer for the effort but it is not the answer to my question.
And, also: "My expectation is that I get the result of the list command."
So your actual need was "how do I create a program that executes the 'list' request" or more generally, "how do I create a program that executes a given BASIC command"? That is quite different than your title, "How to enter commands programmatically", or your initial sentence, "I want to write a very simple basic program in commodore 64 that enters other basic commands.". Manually typing "10 list" isn't entering the command programmatically, it's entering the command manually.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.