1. 程式人生 > >【探索】機器指令翻譯成 JavaScript

【探索】機器指令翻譯成 JavaScript

前言

前些時候研究指令碼混淆時,打算先學一些「程式流程」相關的概念。為了不因太枯燥而放棄,決定想一個有趣的案例,可以邊探索邊學。

於是想了一個話題:嘗試將機器指令 1:1 翻譯 成 JavaScript,這樣就能在瀏覽器中,直接執行等價的邏輯。

為了簡單起見,這裡選擇古董級 CPU —— MOS 6502

本系列陸續更新了 8 篇,前面幾篇只是理論分析:

原本只打算遐想一下,分析下可行性而已。不過,後來發現實現也不難,於是又補了兩篇:

6502

MOS 6502 是一款經典的 CPU,在上世紀 80 年代十分流行。

例如 Atari、Apple II,還有國內的文曲星,都配置了這個系列的 CPU。小時候常玩的 FC 紅白機,也是相同的指令集。

網上相關的文章也非常多,這裡收集了一些:

甚至還有線上模擬器:

事實上,模擬器的原理是很簡單的:讀取一條指令,做相應的操作;然後再讀取下一條指令。。。參照文件實現即可。

do {
    opcode = memory[pc++]

    switch (opcode) {
        case 0xA9:        // LDA
            ...
        case 0x85:        // STA
            ...
        case 0xE6:        // INC
            ...
        ....
    }
} while (...)

模擬雖然簡單,但有個很大的缺點:效率低。模擬一個指令,需要很多額外操作 —— 那些原本是硬體的工作,現在要用軟體來完成,顯然會慢得多。

不過,我們的目標並非模擬,而是翻譯 —— 在程式執行前,把「虛擬指令」翻譯成相應的本地「原生指令」,這樣就能直接執行,無需模擬,效率自然大幅提升。

在瀏覽器層面,JavaScript 就是原生指令。那麼,能否將 6502 翻譯成 JavaScript 呢?下面開始探索。。。

硬體實現

6502 CPU 有三個 8 位暫存器 A、X、Y,我們用 JS 變數來表示:

var A = 0, X = 0, Y = 0;

至於「狀態暫存器」SR,為了直觀起見,分別用單獨的 bool 變量表示每一位:

// SR: NV-BDIZC
// bit 76543210

var SR_N = false,
    SR_V = false,
    ...
    SR_C = false;

其他諸如「棧暫存器」、「指令計數器」,這裡暫時先省略。

6502 的地址匯流排有 16 位,最多能訪問 64K 的空間。資料匯流排 8 位,因此用一個 Uint8Array 就能實現記憶體:

var MEM = new Uint8Array(65536);

這裡假設把整個地址空間都用做 RAM,事實上螢幕、鍵盤等 IO 互動,還會佔用一些地址空間。

嘗試翻譯

現在,嘗試翻譯第一條指令:

STA 100

STA 即 “Store A”,將 A 寫入儲存 —— 寫到第 100 號位置。對應的 JS 即:

MEM[100] = A;

很簡單吧。下面翻譯第二條指令:

LDA #123

LDA 即 “Load A”,給 A 賦值,# 表示立即數。因此,生成的 JS 的就是:

A = 123;
SR_Z = (123 == 0);
SR_N = (123 > 127);

稍瞭解彙編的都知道,修改暫存器的同時,還得更新狀態標誌。SR_Z 表示結果是否為零;SR_N 表示最高位(符號位)是否為 1。

這時「翻譯」的優勢就體現出來了。因為 123 == 0 和 123 > 127 都是常量計算,所以預先就能得出結果:

A = 123;
SR_N = false;
SR_Z = false;

相比模擬,翻譯能減少執行時的計算量。如果有多個指令,效果則更明顯,例如:

LDX 10
INX

翻譯成如下 JS 程式碼:

X = MEM[10];              // LDX 10
SR_Z = (X == 0);
SR_N = (X > 127);

X = (X + 1) & 0xff;       // INX (X 自增)
SR_Z = (X == 0);
SR_N = (X > 127);

這裡雖然沒有預先計算,但不要忘了,JavaScript 最終還得交給瀏覽器解析。

如今的瀏覽器,本身就有很強的優化能力,指令碼引擎發現 SR_Z 和 SR_N 重複賦值,並且中間沒有使用,於是就將之前的計算優化掉了。因此,最終效率會非常高。

真正困難

通過這幾個例子,感覺翻譯並不困難。事實上大多數 6502 指令,都可以生成對應的 JS 邏輯。有的很簡短,只有一兩行;有的較複雜,例如算術加減法。但不管怎樣,都是沒有障礙的。

但是,有一類指令很難翻譯,那就是「跳轉指令」。因為不同的層面,流程控制的能力是不一樣的。

在 JavaScript 中,流程控制只能以「語塊」為單位:

if (...) {
    block 1
} else {
    block 2
}

for (...) {
    break;
    continue;
}

我們最多隻能退出語塊(break),或者重新進入語塊(continue),無法指定從某一行開始執行。

而在 C 語言中,流程控制可以細緻到行:

a:  ...
    goto c;
b:  ...
    goto a;
c:  ...
    goto b;

機器指令更底層,因此更靈活。流程控制是以「位元組」為單位的,可以跳到任意位置。甚至跳到一個指令的中間:

Address  Hexdump   Dissassembly
-------------------------------
$0600    a9 00     LDA #$00
$0602    4c 01 06  JMP $0601

於是將 LDA 的引數 0x00 當成另一個指令(BRK 指令)執行。

更有甚者,還可以跳到棧記憶體上,將動態資料當成指令執行。如此靈活的特性,又該如何實現?

下一篇,我們探討如何處理跳轉指令。

相關推薦

探索機器指令翻譯 JavaScript

前言 前些時候研究指令碼混淆時,打算先學一些「程式流程」相關的概念。為了不因太枯燥而放棄,決定想一個有趣的案例,可以邊探索邊學。 於是想了一個話題:嘗試將機器指令 1:1 翻譯 成 JavaScript,這樣就能在瀏覽器中,直接執行等價的邏輯。 為了簡單起見,這裡選擇古董級 CPU —— MOS 6502。

PL/SQL查詢查詢結果翻譯其他值_decode

else column .... 解釋 == hid 其他 hive cnblogs decode()函數簡介: 主要作用:將查詢結果翻譯成其他值(即以其他形式表現出來,以下舉例說明); 使用方法: Select decode(columnname,值1,翻譯值1,值2,翻

探索JavaScript 中使用 C 程式

JavaScript 是個靈活的指令碼語言,能方便的處理業務邏輯。當需要傳輸通訊時,我們大多選擇 JSON 或 XML 格式。 但在資料長度非常苛刻的情況下,文字協議的效率就非常低了,這時不得不使用二進位制格式。 去年的今天,在折騰一個 前後端結合的 WAF 時,就遇到了這個麻煩。 因為前端指令碼需要採集不少

Spring boot 打jar包問題總結

alt date function get .post stack 問題 chan property http://www.cnblogs.com/xingzc/p/5972488.html 1、Unable to find a single main class

JSPJSP指令

內容 其它 cti 也不會 頁面 版權信息 技術 cor 不能 JSP指令 說明: 1、JSP指令用於告知JSP 引擎轉譯JSP時的轉化參數,他們不產生輸出信息,只是在JSP的轉譯和編譯期間起作用。 2、JSP指令必須寫在規定的格式中: <%@ [goes he

iOSJenkins持續集iOS包

edi 節點 path num nco visio os包 接下來 運行   今天項目經理說讓APP端做一下用Jenkins做持續集成,因為之前沒有做過,所以就開始了解,終於在鍵哥的幫助下,完成了持續集成的功能,感動的不行,在這裏感謝鍵哥。   因為公司的Jenkins是搭

方法論機器學習算法概覽

bootstra c4.5 enc letter 當下 重要 dial 最大 local http://blog.itpub.net/31542119/viewspace-2168911/ 1、 監督式學習 工作機制:這個算法由一個目標變量或結果變量(或因變量)組成。這

理解WebKit和Chromium: JavaScript引擎簡介

正則表達 根據 tco 抽象 由於 介紹 後來 rom 都在 轉載請註明原文地址:http://blog.csdn.net/milado_nju1. 什麽是JavaScript引擎什麽是JavaScript引擎?簡單來講,就是能夠提供執行JavaScript代碼的運行環境。

將HTML5封裝android應用APK 檔案若干方法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

ML2機器學習之線性迴歸

【知識儲備】 線性迴歸: 1: 函式模型(Model): 假設有訓練資料   那麼為了方便我們寫成矩陣的形式   2: 損失函式(cost):  現在我們需要根據給定的X求解W的值,這裡採用最小二乘法。   

ML1機器學習之EM演算法(含演算法詳細推導過程)

        寫在前面的話:對於EM演算法(Expectation Maximization Algorithm, 最大期望演算法), 大家如果僅僅是為了使用,則熟悉演算法流程即可。此處的演算法推導過程,僅提供給大家進階 之用。對於其應用,

MavenMaven 專案打包 war 包部署到 Tomcat

【Maven】Maven 專案打包成 war 包部署到 Tomcat   實踐環境 作業系統: Windows IDE: Eclipse 打包部署過程  1 專案打包   1.1 右鍵點選所需要打包的專案,點選如圖所示&

A機器學習 過擬合與正則化

過擬合問題 預測房價的模型: 第一張圖對該資料做線性迴歸,可以獲得擬合數據的這樣一條直線,實際上這並不是一個很好的模型。很明顯,隨著房子面積增大,住房價格的變化趨於穩定或者說越往右越平緩。因此線性迴歸並沒有很好擬合訓練資料。我們把此類情況稱為欠擬合(un

原始碼機器學習中的投影牛頓型方法

機器學習中的投影牛頓型方法 我們考慮用於解決機器學習及相關領域中出現的大規模優化問題的投影牛頓型方法。 We consider projected Newton-type methodsfor solving large-scale optimization

你會用哪些JavaScript迴圈遍歷

總結JavaScript中的迴圈遍歷定義一個數組和物件 const arr = ['a', 'b', 'c', 'd', 'e', 'f']; const obj = { a: 1, b: 2, c: 3, d: 4 } for() 經常用來遍歷陣列元素 遍歷值為陣列元素

轉載機器學習頂級會議

以下是不完整的列表,但基本覆蓋。機器學習頂級會議:NIPS, ICML, UAI, AISTATS; (期刊:JMLR, ML, Trends in ML, IEEE T-NN)計算機視覺和影象識別:ICCV, CVPR, ECCV; (期刊:IEEE T-

轉載機器學習入門好文,強烈推薦

轉載自:https://www.cnblogs.com/subconscious/ 強烈推薦--入門必讀 如果你做好準備了,那一定耐心的讀完,你一定會有所收穫。(大概需要10分鐘)   在進入正題前,我想讀者心中可能會有一個疑惑:機器學習有什麼重要性,以至於要閱讀完這篇非常長的文章呢?

DP機器分配

題目 總公司擁有高效生產裝置M臺,準備分給下屬的N個公司。各分公司若獲得這些裝置,可以為國家提供一定的盈利。問:如何分配這M臺裝置才能使國家得到的盈利最大?求出最大盈利值。其中M《=15,N〈=10。分配原則:每個公司有權獲得任意數目的裝置,但總檯數不得超過總裝置數M。 輸入 第

演算法把字串轉換整數,樹中兩個結點的最低公共祖先

本書最後的兩道題,作者拿了兩個面試案例來呈現,主要是要弄清面試官的意圖、考慮周全,有些演算法雖然容易,不要輕易下手。在此之上最好寫出具有魯棒性的和好的擴充套件性的程式碼,遵循編碼規範。 面試題67:把字串轉換成整數 請你寫一個函式StrToInt,實現把字串轉

開發Git指令操作和使用—聽說指令看起來比客戶端騷

前言: Git作為分散式版本控制系統,是我們工作和開原始碼平臺專案管理最火的工具之一,基本上是每個入職的同學都要熟知和學習的。由於我以前的公司都是用的SVN,有時也會用的github客戶端,最近抽空來學習下Git的指令使用,聽說這樣的操作比較騷哦 一、Git下