
我見過一些模擬器聲稱它們會執行,即使它們確實執行,它們的原始程式碼顯示它們不會直接解析每個 1 和 0 來確定指令。
我的問題是,如果模擬器必須模擬真實 CPU 所執行的確切操作碼,是否需要解析遊戲的正確二進位操作碼格式才能合法(或根本)模擬 CPU?
例如,在遊戲檔案中我儲存一條指令,一個位元組,標記如下:
0000 1111
我的程式必須驗證該指令確實意味著(例如「向 A 暫存器加一」),但它不需要檢查文字檔案中的每個零和一來確保這一點嗎?
然後模擬器將解析整個字節,但整個位元組又是八位,並且波動的模式會改變操作輸出。
例如,0000 1111 可能意味著 A 加 1,但 0000 1110 可能意味著 A 加 A。
答案1
闡述——試圖直接回答問題
如果您正在閱讀模擬器的原始程式碼,並且它沒有讀取二進位(可執行)檔案的某些位元並且仍然忠實地執行程式碼,則有三種可能的結果:
- 你是錯誤的認為模擬器不會讀取檔案的每一部分,並且它事實上確實如此,你完全錯了。
- 你是正確的,並且模擬器不會讀取每一個位,因為它能夠假設有關其正在模擬的程式的行為的某些事實,以便不需要讀取每一個位元來知道它需要做什麼(可能是因為它期望要運行的特定遊戲引擎,或特定類型的圖形API,或特定類型的聲音API等)。
- 你是正確的,並且模擬器不會讀取每一個位,因為可執行檔的某些位元對於正確執行程式來說並不是必需的。它們可能是遺留的“垃圾”,或元數據,或任何其他實際上並不包含程式功能的額外內容。
- 你是正確的,並且模擬器不會讀取每一位,因為模擬器會將程式碼中的某些操作轉換為更高級別的操作,並且完全繞過低級別的處理器/硬體特定指令。例如,如果您被要求準確模仿一個人在執行複雜操作的視頻錄製中正在做什麼,並且他們說“現在在盒子側面鑽一個孔”,您會很想停下來觀看視頻並利用您現有的如何在物體上鑽孔的經驗,而不是遵循影片中人的字面動作(假設您配備了合適的鑽頭並且通常在生活中經驗豐富)。類似地,如果模擬器可以推斷出程式要求在螢幕上給定的一組座標處繪製 32x32 影像,那麼它可以在了解影像是什麼、影像的格式後立即停止讀取程式碼,以及在哪裡繪製它— —它不需要查看模擬系統如何繪製它。
模擬器如何運作
為另一個平台和/或 CPU 執行程式碼的模擬器(例如,葡萄酒)在不同階段做事。有些階段是模擬器工作所必需的;其他階段是可選的,代表效能優化的可能性。
必需的:「解析」可執行程式碼(機器碼、MSIL、Java字節碼等)解析由組成:
- 讀取可執行程式碼的每一位。
- 充分了解本機程式碼的每個位元/位元組(或您關心使用的任何其他離散資訊測量單位)的佈局/格式(語法)和用途(語義),以便了解它在做什麼。
- 要了解什麼程式說,模擬器必須理解句法二進位格式,以及語意。語法由諸如「我們以最低符號位元格式表示 32 位元有符號整數」之類的內容組成;語意由諸如「當本機程式碼包含操作碼時52,這意味著進行函數調用。
- 助記符(幫助您記住為什麼這是必要的):如果我致力於遵循食譜,如果我完全忽略該食譜,甚至不做讀它,我不可能遵循那個食譜,除非我隨機嘗試一堆東西並且運氣採取與食譜要求相同的步驟。同樣,除非您有一個隨機蒙特卡羅模擬來執行隨機 CPU 指令,直到它幸運地執行與程式相同的功能,否則任何模擬器都必須理解什麼節目說。
必需的:將解析出的程式碼(通常是某種抽象資料模型、狀態機、抽象語法樹或類似的東西)「翻譯」成高水準命令(例如 C 或 Java 中的語句)或低級命令(例如 x86 處理器的 CPU 指令)。進階命令往往更加優化。例如,如果您分析一長串CPU 指令的程式碼流,並在較高層級上確定它所要求的是從磁碟播放某個MP3 文件,則您可以跳過整個指令級模擬,而只使用您的本機器平台的 MP3 解碼器(可能針對您的處理器進行了最佳化)來播放相同的 MP3 檔案。另一方面,如果您要盡可能按字面意思「追蹤」模擬程式的執行,這會更慢且不太理想,因為您將放棄透過本機執行指令而受益的大部分最佳化。
選修的:「最佳化」並分析大量模擬程式碼或整個程式的程式碼流,以確定完整的執行順序,並建立一個非常詳細且複雜的模型來說明模擬器將如何模擬此行為與本機平台的設施。 Wine 在某種程度上做到了這一點,但它所翻譯的程式碼是 x86 到 x86(這意味著在這兩種情況下 CPU 都是相同的指令集,因此您所要做的就是連接 Windows程式碼到基於UNIX 的外部環境並讓它「本地」運行)。
蛋糕類比
在考慮模擬器的性能時,請考慮一下,如果您在影片(帶音訊)中觀看某人烘烤蛋糕,在以下情況下,您需要多少張紙來為自己寫下說明:
如果您以前從未動手或鍛鍊過身體的任何肌肉;(提示:你需要數千張紙來記錄手部動作、手眼協調、角度、速度、位置、抓握、握住器具、揉捏等基本技巧的詳細步驟)
如果您有基本的運動控制能力(您可以行走和自己吃飯),但以前從未準備過任何食物;(提示:您需要數十張紙來記錄各個步驟,並且可能需要大量練習才能掌握諸如揉捏和握住不熟悉的器具之類的技巧,但您可以用更少的時間來記錄它比前一個案例花費的時間)
如果您以前從未烤過蛋糕,但之前已經準備過一些食物;(提示:你需要幾張紙,但不要超過 10 張;你已經熟悉了測量成分、攪拌等)
如果你以前烤過很多次蛋糕,並且非常熟悉這個過程,但你不知道如何烤這個特定品種/口味的蛋糕(提示:你可能需要半張紙記下基本成分和在烤箱中所需的時間,僅此而已)。
基本上,在“仿真器能力”水平不斷提高的情況下,仿真器可以“本地”執行更多更高級別的操作(使用它已經知道的例程和過程),並且必須執行更少的“跟踪」(使用它已知的例程和過程)。按照字面意思來自模擬程序)。
用電腦術語來比喻,你可以想像一個模擬器模擬實際硬體模擬程式將在該硬體上運行並忠實地「追蹤」該硬體的行為,甚至可能深入到硬體(電路)層級;這將是非常與模擬器相比,模擬器對程式的分析達到瞭如此複雜的程度,以至於它可以理解何時嘗試播放聲音文件,並且可以“本機”播放該聲音文件,而無需跟踪模擬程式的指令來執行此操作。
關於“跟踪”(又名死記硬背模仿)與“本機執行”
最後一件事:追蹤速度很慢,主要是因為您必須使用大量記憶體來「複製」您正在模擬的事物的非常詳細、複雜的元件,並且您不僅需要在主機 CPU 上執行指令,還必須執行執行指令的指令(看到間接等級了嗎?),這會導致效率低下。如果您全力以赴地模擬電腦系統的實體硬體和程序,那麼您將模擬 CPU、主機板、音效卡等,這些反過來又會「追蹤」程式的執行情況,就像您的程式一樣。 「CPU 的執行,並且透過如此多的追蹤級別,整個過程將變得極其緩慢且麻煩。
以下是一個詳細範例,說明模擬器不需要讀取輸入程式的每個位元/位元組來對其進行模擬。
假設我們知道一個用 C 或 C++ 編寫的 API(細節並不重要),用於模擬軟體環境,該 API 具有一個函數void playSound(string fileName)
。假設我們知道這個函數的語義是打開磁碟上的文件,讀取其內容,找出文件的編碼(MP3?WAV?其他什麼?),然後在揚聲器上播放它普通/預期採樣率和音高。如果我們從本機程式碼中讀取一組指令“進入 playSound 例程以開始播放聲音/home/hello/foo.mp3
”,我們可以立即停止讀取程式碼,並使用我們的自己的(優化!)用於本機開啟該聲音檔案並播放它的例程。我們是否需要在指令層級上遵循類比處理器?不,我們真的不知道,如果我們相信我們知道 API 的用途的話。
巨大的差異出現了! (高地的麻煩)
當然,透過閱讀一堆指令並「推斷」高級執行計劃(如上面的範例所示),您可能會面臨以下風險:恰恰模仿在原始硬體上運行的原始程式的行為。舉例來說,原始硬體可能有硬體限制,只能同時播放 8 個聲音檔。好吧,如果您的新型電腦可以同時播放 128 個聲音文件,並且您正在playSound
高水準地模擬例程,那麼什麼可以阻止您一次播放超過 8 個聲音檔案呢?這可能會導致...程式的模擬版本出現奇怪的行為(無論好壞)。這些情況可以透過仔細測試或透過很好地了解原始執行環境來解決。
例如,DOSBox有一個功能,可以讓你刻意限制模擬DOS程式的執行速度,因為有些DOS程式如果允許全速運行,就會運作不正確;它們實際上依賴 CPU 時脈頻率的計時來以預期的速度執行。這種有意限制執行環境的「功能」可用於在兩者之間提供良好的權衡忠誠執行(即,使模擬程式正常工作)和效率執行(即建立足夠高級的程式表示,以便可以透過最少的追蹤有效地模擬它)。
答案2
我的問題是,如果模擬器必須模擬真實 CPU 所執行的確切操作碼,是否需要解析遊戲的正確二進位操作碼格式才能合法(或根本)模擬 CPU?
「解析」的意思是「瀏覽文本並找出內容的含義」。文本和類似語言的語法很複雜,因為不僅單個「單字」表示事物,而且含義可以根據它們的位置和與其他單字的接近程度而變化。
對於 CPU 對指令流執行的操作(比解析簡單得多),更準確的術語可能是「解碼」——是的,模擬器必須以相同的方式「解碼」指令。不過,考慮到 x86 指令集的複雜性,我想「解析」並不是一個真正糟糕的術語。
我的程式必須驗證該指令確實意味著(例如「向 A 暫存器加一」),但它不需要檢查文字檔案中的每個零和一來確保這一點嗎?
CPU 並不真正驗證指令。任何 CPU 都有一個內部暫存器,指向 RAM 中的儲存位置。 CPU 讀取它,如果需要的話再讀取幾個位元組,然後嘗試執行它。如果指令是非法的,現代 CPU 將啟動「異常過程」。
舊的 CPU,例如舊的 8 位元 6502,甚至沒有這樣做 - 一些非法指令會鎖定 CPU,其他指令會做奇怪的、未記錄的事情。 (一些搜尋將揭示在 x86 萬神殿中的所有 CPU 中發現的許多未記錄的 x86 指令 - 例如,CPUID
在 Intel 正式記錄該指令之前,該指令已存在於某些 486 CPU 上。)
一個好的、準確的 CPU 模擬器只會像真正的 CPU 一樣盲目地咀嚼指令流 - 儘管它可能會在導致系統鎖定的情況下執行不同的操作,例如顯示一個對話框,告訴您虛擬 CPU 已死機鎖定你的模擬器。
此外,您說“它需要檢查文字檔案中的每個零和一”,但通常您不會向模擬器提供文字檔案。 CPU 模擬器需要模擬記憶體 - 由於許多平台使用記憶體映射 I/O,因此也需要模擬 I/O 裝置。模擬韌體之類的東西需要您擁有該韌體的二進位映像 - 真實韌體的合法副本或替代開源韌體。模擬整個平台(例如“PC 平台”,其中 x86 CPU 是其元件)需要的不僅僅是 CPU 模擬。