我正在寻找在游戏中构建一个虚拟机,并且想知道是否有人知道任何非常简单的虚拟机(我认为 RISC/PIC 接近我想要的),这些虚拟机通常用于嵌入式项目,例如控制机器人、电机、我主要关心的是如果我自己开发一个编译器/汇编器,就必须编写一个编译器/汇编器。 我很乐意使用已经存在的工具,或者以最简单的形式使用可以为其进行编译的 C 编译器:-p。
我真的不想在这里重新发明轮子,但我还需要数千个在虚拟世界中运行的轮子,因此它们必须尽可能简单和快速。 正如一个人已经提到的那样,我也不关心现实世界的问题,例如时间安排、巴士以及所有有趣的事情。 我认为他们的虚拟时钟将仅限于速度相当慢的事情; 最终我可能不得不研究本机编译以使它们运行得更快,但现在我只是将原型放在一起以获得概念的一般证明。
作为输入,我计划在圆柱体周围安装距离、光线、材料和触摸传感器(16 个,也许 32 个),然后简单地使用 2 个电机进行定向输出,以控制两侧的某种轮子。 本质上,处理不会太费力,世界也足够简单,因此机器不必在简单的任务上投入大量的处理能力。
在内存方面,我希望它们能够存储足够的数据,以便在几天内无需干预来构建地图和收集统计数据。 我不喜欢 8 位会因为处理或内存而削减它,但 16 位肯定是一个竞争者。 32 和 64 位只会推动它,并且它们的内存不可能超过 1mb - 可能接近 256-512k。 (比尔一说 640k 就足够了,为什么我不能!!)
I'm looking to build a VM into a game and was wondering if anyone knew of any really simple VM's (I was thinking RISC/PIC was close to what I wanted) that are usually used for embedded projects such as controlling robots, motors, sensors, etc. My main concern is having to write a compiler/assembler if I roll my own. I'd be nice to use the tools that are already out there or in its simplest form just a C compiler that can compile for it :-p.
I really don't want to re-invent the wheel here but I also need thousands of these running around a virtual world so they have to be as simple and as fast as possible. As one person has already mentioned I also don't care about real world issues such a timing and buses and all that fun stuff. I think their virtual clocks will be limited to somthing quite slow; and eventually I'll probably have to look into native compiling to make them run even faster but for now I'm just putting together prototypes to get a general proof of concept.
As input, I'm planning on distance, light, material and touch sensors mounted around the cylindrical body (16, maybe 32 of them), then simply 2 motors for directional output to control a sort of wheel on each side. essentially the processing won't be too strenuous and the world will be simple enough so that the machine's don't have to throw lots of processing power at simple tasks.
In terms of memory, I'd like them to be able to store enough data to be left alone for a couple of days without intervention for building maps and gathering stats. I don't like 8bit would cut it for processing or memory but 16bit would definitely be a contender. 32 and 64bit would just be pushing it and there's no way they'll have any more than 1mb each of memory - probably closer to 256-512k. (Bill one said 640k would be enough so why can't I!!)
发布评论
评论(5)
我为一位朋友编写了 Wren,他想要在具有大约 16K RAM 的嵌入式控制器上运行 VM 语言。 (但在编写的代码中,它允许每个进程最多 64k。)它包括一个用于愚蠢的小型编程语言的编译器。 这一切,呃,非常基本,没有太多用处,但这正是您在第一段中所描述的。
I wrote Wren for a friend who wanted a VM language running on an embedded controller with around 16K of RAM. (But it allows up to 64k per process in the code as written.) It includes a compiler for a dumb little programming language. It's all, uh, pretty basic and hasn't seen much use, but it is just what you described in your first paragraph.
FORTH“虚拟机”非常简单。 16 位地址空间(通常)、16 位数据字、两个堆栈、内存。 Loeliger 的“Threaded Interpretive Languages”告诉您很多有关如何在 Z80 上构建 FORTH 解释器的信息。
The FORTH "virtual machine" is about as simple as they come. 16-bit address space (typically), 16-bit data words, two stacks, memory. Loeliger's "Threaded Interpretive Languages" tells you a lot about how to build a FORTH interpreter on a Z80.
如果您想要植根于现实世界的东西,最常用的嵌入式 RISC 微控制器之一是 PIC 系列。 谷歌提供了几个模拟器,但我认为大多数模拟器都无法使用源代码。
另一种可能性是 QEMU,它已经模拟了多个 ARM 品种。
当然,如果您对模拟现实世界的设备不感兴趣,那么更容易、更好的性能就是推出您自己的设备。 仅使用您需要的内容,而不会陷入混乱的状态标志、溢出位、有限的总线宽度、RAM 时序等。
if you want something rooted in the real world, one of the most-used embedded RISC microcontrollers is the PIC family. google gives several emulators, but i don't think the source is available for most.
another possibility is QEMU, which already emulates several ARM varieties.
and, of course, if you're not interested in emulating a real-world device, far easier and better performance would be to roll your own. with only what you need, and not getting into the mess of state flags, overflow bits, limited bus width, RAM timings, etc.
如果您想要简单,请考虑曼彻斯特 Mark I。请参阅此 PDF。 该机器有7条指令。 为其编写一个解释器大约需要一个小时。 不幸的是,这些说明非常有限(这就是为什么机器的完整规格几乎可以放在一页上)。
哈维尔自己推出的方法非常务实。 如果是的话,设计和制造一台微型机器需要两天的时间。 几年前,我为一个项目构建了一个小型虚拟机,我花了两天时间才用一个简单的可视化调试器编写了虚拟机。
另外——它必须是 RISC 吗? 例如,您可以选择 68K,因为它有开源模拟器,而且 68K 是一个很好理解的目标对于海湾合作委员会。
If you want simple, consider the Manchester Mark I. See page 15 of this PDF. The machine has 7 instructions. It takes about an hour to write an interpreter for it. Unfortunately, the instructions are pretty dang limited (which is why pretty much a full spec of the machine can fit on one page).
Javier's approach of rolling your own is very pragmatic. Designing and creating a tiny machine is a two day task, if that. I built a tiny VM for a project a few years ago and it took me two days to write the VM with a simple visual debugger.
Also - does it have to be RISC? You could choose, say, 68K for which there are open source emulators, and 68K was a well-understood target for gcc.
许多编写游戏程序和其他应用程序的人将一种语言嵌入到应用程序中,以允许用户编写小程序。
据我所知,最流行的嵌入式语言大致上是大多数-流行第一顺序(尽管“更流行”并不一定意味着“更好”)似乎是:
您可能想查看 Gamedev StackExchange,特别是诸如 “如何向游戏添加脚本语言?”。
您可能想查看 StackOverflow 上标记为 “嵌入式语言” 的一些问题;
例如
“选择嵌入式语言”,
“什么是我可以使用哪种良好的嵌入式语言在我的软件中编写脚本?”
“Lua 作为嵌入式语言的替代品?”
"哪种游戏脚本语言最好使用: Lua 还是 Python?",
等等。
这些语言的许多实现都使用某种
内部字节码。
通常,同一高级编程语言(例如 JavaScript)的两种不同实现在内部使用两种完全不同的字节码语言(a )。
通常,几种高级编程语言会编译为相同的底层字节码语言——例如,Python 的 Jython 实现、JavaScript 的 Rhino 实现、Tcl 的 Jacl 实现、JScheme 和其他几种 Scheme 实现,以及 Pascal 的几种实现; 全部编译为相同的 JVM 字节码。
详细信息
为什么使用脚本语言而不是解释某些硬件机器语言?
为什么“硬层和软层交替”?
为了获得简单性和更快的开发。
更快的开发
人们通常使用脚本语言而不是编译语言来更快地工作。
让初始原型工作通常要快得多——解释器在幕后处理一堆机器语言强制你显式写出的东西:将变量的初始值设置为零、子例程序言和子例程结束代码、malloc 和 realloc 以及 free 和相关的内存管理内容,在容器满时增加容器的大小等。
一旦有了初始原型,添加新功能就会更快:脚本语言具有快速的编辑-执行-调试周期,因为它们避免了编译语言的编辑-编译-执行-调试周期的“编译”阶段。
简单性
我们希望嵌入式语言在两个方面变得“简单”:
如果用户想要编写一些代码来完成一些概念上微不足道的任务,我们不想吓到它一个人需要花费 20 磅的书籍和数月的时间学习复杂的语言才能编写一个没有缓冲区溢出的“Hello, $USER”。
由于我们正在实现该语言,因此我们想要一些易于实现的东西。 也许一些简单的底层指令我们可以在一个周末敲出一个简单的解释器,也许我们可以通过最小的调整重用某种预先存在的编译器。
当人们构建 CPU 时,硬件限制最终总是会限制指令集。
许多概念上“简单”的操作——人们一直使用的东西——
最终需要大量机器语言指令来实现。
嵌入式语言没有这些硬件限制,允许我们实现
更复杂的“指令”做的事情(对人类来说)在概念上看起来很简单。
这通常会使系统在上面提到的两种方面变得更简单:
直接用该语言编写的人(或为该语言编写编译器的人)最终编写的代码要少得多,花在单步执行上的时间也更少 <
由于这些原因和其他原因,许多游戏程序员使用“脚本”语言作为他们的“嵌入式语言”。
(我现在看到哈维尔已经推荐“使用嵌入式脚本语言”,
因此,这已经变成了关于为什么这是解释硬件机器语言的一个很好的替代方案的长篇大论,并在一种特定的脚本语言似乎不合适时指出替代方案)。
Many people writing game programs and other applications embed a language into the application to allow users to write small programs.
As far as I can tell, the most popular embedded languages in very roughtly most-popular-first order (although "more popular" doesn't necessarily mean "better") seem to be:
You may want to check out the Gamedev StackExchange, in particular questions like "How do you add a scripting language to a game?".
You may want to check out some of the questions here on StackOverflow tagged "embedded language";
such as
"Selecting An Embedded Language",
"What is a good embeddable language I can use for scripting inside my software?"
"Alternatives to Lua as an embedded language?"
"Which game scripting language is better to use: Lua or Python?",
etc.
Many implementations of these languages use some sort of
bytecode internally.
Often two different implementations of the same high-level programming language such as JavaScript use two completely different bytecode languages internally ( a ).
Often several high-level programming languages compile to the same underlying bytecode language -- for example, the Jython implementation of Python, the Rhino implementation of JavaScript, the Jacl implementation of Tcl, JScheme and several other implementations of Scheme, and several implementations Pascal; all compile to the same JVM bytecode.
details
Why use a scripting language rather than interpreting some hardware machine language?
Why "Alternate Hard And Soft Layers"?
To gain simplicity, and faster development.
faster development
People generally get stuff working faster with scripting languages rather than compiled languages.
Getting the initial prototype working is generally much quicker -- the interpreter handles a bunch of stuff behind-the-scenes that machine language forces you to explicitly write out: setting the initial values of variables to zero, subroutine-prolog and subroutine-epilog code, malloc and realloc and free and related memory-management stuff, increasing the size of containers when they get full, etc.
Once you have an initial prototype, adding new features is faster: Scripting languages have rapid edit-execute-debug cycles, since they avoid the "compile" stage of edit-compile-execute-debug cycles of compiled languages.
simplicity
We want the embedded language language to be "simple" in two ways:
If a user wants to write a little code that does some conceptually trivial task, we don't want to scare this person off with a complex language that takes 20 pounds of books and months of study in order to write a "Hello, $USER" without buffer overflows.
Since we're implementing the language, we want something easy to implement. Perhaps a few simple underlying instructions we can knock out a simple interpreter for in a weekend, and perhaps some sort of pre-existing compiler we can reuse with minimal tweaking.
When people build CPUs, hardware restrictions always end up limiting the instruction set.
Many conceptually "simple" operations -- things people use all the time --
end up requiring lots of machine-language instructions to implement.
Embedded languages don't have these hardware restrictions, allowing us to implement
more complicated "instructions" that do things that (to a human) seem conceptually simple.
This often makes the system simpler in both ways mentioned above:
People writing directly in the language (or people writing compilers for the language) end up writing much less code, spending less time single-stepping through the code debugging it, etc.
For each such higher-level operation, we shift complexity from the compiler to an instruction's implementation inside the interpreter. Rather than (you writing code in) the compiler breaking some higher-level operation into a short loop in the intermediate language (and repeatedly stepping through that loop in your interpreter at runtime), the compiler emits one instructions in the intermediate language (and you write the same series of operations in your interpreter's implementation of that intermediate "instruction"). With all the CPU intensive stuff implemented in your compiled language ("inside" complex instructions), extremely simple interpreters are often more than fast enough. (I.e., you avoid spending a lot of time building a JIT or trying to speed things up in other ways).
For these reasons and others, many game programmers use a "scripting" language as their "embedded language".
(I see now that Javier already recommended "use an embedded scripting language",
so this has turned into a long rant on why that's a good alternative to interpreting a hardware machine language, and pointing out alternatives when one particular scripting language doesn't seem suitable).