机器指令翻译成 JavaScript —— 终极目标
上一篇,我们顺利将 6502 指令翻译成 C 代码,并演示了一个案例。
现在,我们来完成最后的目标 —— 转换成 JavaScript。
中间码输出
我们之所以选择 C,就是为了使用 LLVM。现在来看看,生成的 LLVM 中间表示:
不难看出,顺序执行的逻辑都在一个 label 中,跳转则用 br 符号。
这种风格,和我们之前讨论的指令切割非常相似。一个 label 块,正好翻译成一个 block_xxx 的 JS 函数。
所以,理论上翻译成 JS 并不困难,写一个 LLVM backend 插件即可。
现有工具
不过,实际操作起来还是挺麻烦的。
LLVM 中间码和汇编差不多,一步一个操作。如果直接翻译,生成的 JS 会很累赘,类似这样:
$301 = MEM[16];
$302 = $301 + 10;
$303 = ($302 == 0);
if ($303) {
...
}
如果能将多步操作合并成一行 JS,则会简洁得多。另外,变量名分配和重用,也是比较麻烦的。
事实上 LLVM 输出成 JS,前辈们早就尝试过了。例如 emscripten,目前已非常成熟。所以,我们不如就用现成的工具吧。
emscripten 是如何处理流程及跳转的?其实和我们之前讨论的切割类似,也是用额外的变量模拟流程。只不过它是用数字变量,而不是函数变量。
例如这样的流程:
a: xx 1
goto c
b: xx 2
goto a
c: xx 3
goto b
生成的 JS 类似这样:
while (1) {
switch (label) {
case 1:
xx 1
label = 3; continue;
case 2:
xx 2
label = 1; continue;
case 3:
xx 3
label = 2; continue;
}
}
使用数字,就可以在同个 function 里控制流程,因此更合理一些。
但如果流程复杂,则会陷入众多的判断。哪个方案更好,还得看浏览器的实际优化能力。
线程模型
既然要在浏览器中运行,当然就不能用 sleep 了,取而代之的是 yield。
但 emscripten 生成的代码,显然是不会有 yield 的。因此,我们得手动实现上下文的切换。
我们在切出前,记住当前的流程位置,然后 return;下次调用时,根据上一次的流程位置,跳转到相应的地方继续执行(用上一篇提到的动态表)。
这样,就符合浏览器的线程模型了。
接口交互
之前为了演示,使用新线程 + getchar() 来接受输入,这显然不符合浏览器的模型。
我们得监听键盘事件,在回调中更新相应的内存数据。
不过,emscripten 内置了 SDL 框架(类似于 DirectX),它封装了各种事件处理、图形渲染、音频播放等,非常实用。
SDL 会把消息记录在自己的队列里。任何时候,我们可以通过 Poll 的方式去拉取。这样就避免了回调,也不会有阻塞。
因此最终的模型,就类似这样:
void render() {
cycle_remain = N;
input(); // 获取输入
update(); // 指令逻辑(执行到 cycle_remain <= 0)
output(); // 屏幕输出
}
// 通过浏览器的 rAF 接口实现
emscripten_set_main_loop(render);
具体可以参考:这个文件。
最终结果
我们将上一篇的「贪吃蛇」编译成 JavaScript,在浏览器中运行:
在线演示 (ASDW 控制方向)
由于 emscripten 打包了一些 C 运行时、辅助函数、SDL 框架等各种程序,所以生成的脚本很大,超过 200 KB。事实上 6502 指令对应的 JS 并不多,可以参考下面链接。
回顾下整个翻译过程:
机器码 --> (现有工具) --> 汇编码 --> (小脚本) --> C 代码 --> (emscripten) --> JS 代码
虽然这种方式不是最完美的,但实现起来很简单。
当然,我们的目标并非为了实现 6502 指令,只是借此学习一下「程序流程」相关的知识,以及探索一些开脑洞的想法。
机器指令翻译成 JavaScript —— 终极目标的更多相关文章
- 【探索】机器指令翻译成 JavaScript
前言 前些时候研究脚本混淆时,打算先学一些「程序流程」相关的概念.为了不因太枯燥而放弃,决定想一个有趣的案例,可以边探索边学. 于是想了一个话题:尝试将机器指令 1:1 翻译 成 JavaScript ...
- 机器指令翻译成 JavaScript —— No.5 指令变化
上一篇,我们通过内置解释器的方案,解决任意跳转的问题.同时,也提到另一个问题:如果指令发生变化,又该如何应对. 指令自改 如果指令加载到 RAM 中,那就和普通数据一样,也是可以随意修改的.然而,对应 ...
- 机器指令翻译成 JavaScript —— No.6 深度优化
第一篇 中我们曾提到,JavaScript 最终还得经过浏览器来解析.因此可以把一些优化工作,交给脚本引擎来完成. 现代浏览器的优化能力确实很强,但是,运行时的优化终归是有限的.如果能在事先实现,则可 ...
- 机器指令翻译成 JavaScript —— No.7 过渡语言
上一篇,我们决定使用 LLVM 来优化程序,并打算用 C 作为输入语言.现在我们来研究一下,将 6502 指令转换成 C 的可行性. 跳转支持 翻译成 C 语言,可比 JS 容易多了.因为 C 支持 ...
- 机器指令翻译成 JavaScript —— No.2 跳转处理
上一篇,我们发现大多数 6502 指令都可以直接 1:1 翻译成 JS 代码,但除了「跳转指令」. 跳转指令,分无条件跳转.条件跳转.从另一个角度,也可分: 静态跳转:目标地址已知 动态跳转:目标地址 ...
- 机器指令翻译成 JavaScript —— No.3 流程分割
上一篇 我们讨论了跳转指令,并实现「正跳转」的翻译,但最终困在「负跳转」上.而且,由于线程模型的差异,我们不能 1:1 的翻译,必须对流程进行一些改造. 当初之所以选择翻译,而不是模拟,就是出于性能考 ...
- 机器指令翻译成 JavaScript —— No.4 动态跳转
上一篇,我们用模拟流程的方式,解决了跳转问题. 不过静态跳转,好歹事先是知道来龙去脉的.而动态跳转,只有运行时才知道要去哪.既然流程都是未知的,翻译从何谈起? 动态跳转,平时出现的多吗?非常多!除了 ...
- 四十年前的 6502 CPU 指令翻译成 JS 代码会是怎样
去年折腾的一个东西,之前 blog 里也写过,不过那时边琢磨边写,所以比较杂乱,现在简单完整地讲解一下. 前言 当时看到一本虚拟机相关的书,正好又在想 JS 混淆相关的事,无意中冒出个问题:能不能把某 ...
- [书籍翻译] 《JavaScript并发编程》第五章 使用Web Workers
本文是我翻译<JavaScript Concurrency>书籍的第五章 使用Web Workers,该书主要以Promises.Generator.Web workers等技术来讲解Ja ...
随机推荐
- [ 高并发]Java高并发编程系列第二篇--线程同步
高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...
- 菜鸟Python学习笔记第一天:关于一些函数库的使用
2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...
- TODO:macOS上ThinkPHP5和Semantic-UI集成
TODO:macOS上ThinkPHP5和Semantic-UI集成 1. 全局安装 (on OSX via homebrew)Composer 是 homebrew-php 项目的一部分 2. 把X ...
- 关于微软HttpClient使用,避免踩坑
最近公司对于WebApi的场景使用也越来越加大了,随之而来就是Api的客户端工具我们使用哪个?我们最常用的估计就是HttpClient,在微软类库中命名空间地址:System.Net.Http,是一个 ...
- gitHub使用入门和github for windows的安装教程
在看这篇教程之前我想大家也在搜索怎样使用gitHub托管自己的项目,在使用gitHub之前我也遇到过各种问题,在网上我也搜索了很多,但总觉得网上搜索到的东西很多很杂,有的根本不知道是在表达什么.在这过 ...
- C++标准库实现WAV文件读写
在上一篇文章RIFF和WAVE音频文件格式中对WAV的文件格式做了介绍,本文将使用标准C++库实现对数据为PCM格式的WAV文件的读写操作,只使用标准C++库函数,不依赖于其他的库. WAV文件结构 ...
- C# 索引器,实现IEnumerable接口的GetEnumerator()方法
当自定义类需要实现索引时,可以在类中实现索引器. 用Table作为例子,Table由多个Row组成,Row由多个Cell组成, 我们需要实现自定义的table[0],row[0] 索引器定义格式为 [ ...
- nginx服务器安装及配置文件详解
nginx在工作中已经有好几个环境在使用了,每次都是重新去网上扒博客,各种编译配置,今天自己也整理一份安装文档和nginx.conf配置选项的说明,留作以后参考.像负载均衡配置(包括健康检查).缓存( ...
- ASP.NET MVC 5 系列 学习笔记 目录 (持续更新...)
前言: 记得当初培训的时候,学习的还是ASP.NET,现在回想一下,图片水印.统计人数.过滤器....HttpHandler是多么的经典! 不过后来接触到了MVC,便立马爱上了它.Model-View ...
- Prometheus 系统监控方案 一
最近一直在折腾时序类型的数据库,经过一段时间项目应用,觉得十分不错.而Prometheus又是刚刚推出不久的开源方案,中文资料较少,所以打算写一系列应用的实践过程分享一下. Prometheus 是什 ...