1

我想在 commodore 64 中编写一个非常简单的基本程序,可以输入其他基本命令。

这是一个例子:
10 print"list"+chr$(13)

这会打印列表,但不会按 Enter。

我的期望是我得到 list 命令的结果。

我怎样才能做到这一点?

4

3 回答 3

4

简短的回答是,它不能完成。长的答案是它是可能的,但不是你这样做的方式,而且它可能非常困难。包括 Commodore 64 在内的大多数老式 BASIC 都没有 eval 函数,这基本上就是您所说的。(根据 David Lien的说法,BBC BASIC 有一个 EVAL 命令,而其中一个 Apple BASIC 有一个 EXEC 命令,可以从文件中读取文本,就好像它是从键盘输入的一样,这将允许对 EVAL 命令的模拟速度较慢.)

Commodore 64 和大多数老式 BASIC拥有的是对现有机器语言例程的调用。BASIC 命令在内存中的某个地方,如果您知道例程所在的内存地址,您可以将控制权转移到这些命令。在 Microsoft 变体中,这通常是 EXEC 命令。在 Commodore 64 上,它是 SYS 命令。

语法是SYS <ADDRESS>,其中 ADDRESS 是您要将控制权转移到的内存位置。只要该地址包含具有返回代码的例程,它就会完成其工作,然后将控制权转移回您的 BASIC 程序。

通常,您会将 SYS 调用与一些 POKE(为机器语言子例程提供数据)和/或一些 PEEK(以查看例程所做的事情)结合使用。

这是一个受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

该程序为 CO 创建一个从 0 到 39 的随机数,然后为 LN 创建一个从 0 到 24 的随机数。它将 C 的值插入内存位置 211,将 LN 的值插入内存位置 214,然后在内存位置 58640 调用机器语言例程。

该例程将存储位置 211 解释为列,将位置 214 解释为行,以将光标置于其中。所以这个程序所做的就是在屏幕的某处随机打印一个“x”;如果您按“r”,它会再次执行此操作,直到您按其他键为止。

该程序是小写的,因为我使用VICE 模拟器进行测试,并且 VICE(至少在 macOS 上)在粘贴时会自动将小写转换为大写,将大写转换为图形字符。

在您的示例中,这要困难得多。虽然 LIST 命令例程的入口点很容易发现(42652 或十六进制 $A69C),但您如何为该例程提供行号或范围却不太容易发现。从这个Commodore 64 拆解来看,可能需要以文本形式提供。(按照 LIST 例程到反汇编中的 LINGET 例程。)

然后你需要为你想要模拟的每个命令执行此操作。

也可以通过$AD9E 处的 BASIC 评估程序运行您的字符串以进行真正的评估,但这可能是一项更复杂的任务。

如果我被迫做这样的事情,我会考虑以下选项:

  1. 使用 PEEK 定位子程序中的虚拟行,然后 POKE 将该虚拟行重写为要评估的行;然后,转到该子程序。
  2. 在第一次将其写入为 BASIC 文件后,使用LOAD 命令加载要评估的行。
  3. 因为 C64 上的 BASIC 是交互式的,所以应该有一个可以调用的例程来评估各个行。找到它,并确定如何为该调用提供您要评估的文本。
  4. 搜索杂志和公告板,看看是否有人在当天为 C64 BASIC 编写了 EVAL 命令。

例如,这里是选项 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

如果你运行它,你会看到第 1010 行从一个长注释变为包含 EV$ 中的内容的注释。仍然需要做很多工作才能使其成为真正的评估,但是:

  1. 它没有验证 EV$ 是否足够短以适合第 1010 行;如果 EV$ 很长,它将继续超过第 1010 行的末尾并覆盖 1020。
  2. 它不会用代码实际运行所需的标记化替换 EV$ 的文本。您需要一种将单词“LIST”转换为 LIST 的标记化(很可能是数组或 SYS 调用)的方法,以便实际运行 LIST(或 EV$ 中的任何其他内容)。
  3. 它不会更新第 1010 行的长度,尽管这可能不是必需的。更新第 1010 行的长度也意味着必须在内存中移动第 1020 行,而单独保留第 1010 行的长度意味着不必移动第 1020 行。
于 2021-12-30T16:55:18.383 回答
1

执行从字符串构建的 BASIC 命令的一种方法是操作键盘缓冲区。考虑以下 BASIC 子例程,它执行您在 GOSUB 之前放入 CM$ 的任何 BASIC 命令:

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 清除屏幕,向下光标几次,然后将您的命令(以 CM$ 格式)打印到空白屏幕上。

110 Poke RETURN,然后是“C”“O”“N”“T”,然后是另一个 RETURN 到键盘缓冲区。

120 告诉系统缓冲区中有 6 个新的击键。

130 将光标移动到屏幕顶部并结束程序。

这就是魔法发生的地方。C64 将开始处理键盘缓冲区中的字符。

140 这是执行命令后 BASIC 程序将返回的位置。因为这是一个用于 GOSUBing 的例程,所以我只是在这里放了一个 RETURN 命令。

要测试子例程,请添加以下行:

10 CM$="LIST":GOSUB100
20 PRINT"MY PROGRAM CONTINUED RUNNING!": END

这是一个有趣的页面: Commodore 64 键盘缓冲区技巧:从 BASIC 删除和创建 BASIC 行

于 2022-01-21T01:44:01.423 回答
0

解决方案非常简单:

10 list

您基本上可以键入任何基本命令,它就可以工作:

10 print"Loading..."
20 load"*",8,1

这实际上是我需要修补一些小型自动化的全部内容。

于 2021-12-31T21:05:15.657 回答