<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          成本不到 40 元!DIY 大神用樹莓派,重現(xiàn) 40 年前、售價(jià) 1.8 萬(wàn)元的 Mac

          共 10863字,需瀏覽 22分鐘

           ·

          2024-07-28 18:00

          架構(gòu)師大咖
          架構(gòu)師大咖,打造有價(jià)值的架構(gòu)師交流平臺(tái)。分享架構(gòu)師干貨、教程、課程、資訊。架構(gòu)師大咖,每日推送。
          公眾號(hào)

          這件事的起因,源于對(duì) RP2040 MCU(Raspberry Pi 的首款微控制器)的一次討論。

          當(dāng)時(shí)大家正在探討如何為 RP2040 MCU 構(gòu)建一個(gè)簡(jiǎn)單的桌面/圖形用戶界面,我便隨口說了句“要么,干脆運(yùn)行一些舊操作系統(tǒng)算了”。說完之后,我突然聯(lián)想到了最初的 Macintosh。

          Macintosh 最早發(fā)布于 40 年前,是一款硬件非常簡(jiǎn)單卻相當(dāng)酷的設(shè)備。不過內(nèi)存方面有些緊張:最初的 128KB 版本內(nèi)存不足,僅售賣了幾個(gè)月便被 512K 的 Macintosh 取代,可以看出 512K 的內(nèi)存似乎更為適當(dāng)。

          盡管如此,128KB 版本仍能運(yùn)行一些真正的應(yīng)用程序。雖然當(dāng)時(shí)它還沒有 MultiFinder/實(shí)際多任務(wù)處理功能,我依然覺得它很有魅力。

          在 1984 年,Mac 的價(jià)格大約是 VW Golf(大眾高爾夫,一款小型家庭轎車)的三分之一。但如今,我想用這塊售價(jià) 3.80 英鎊(合人民幣約 34.8 元)的 RPi Pico 微控制器板來試試:RP2040 有 264KB 內(nèi)存,扣除 Mac 的 128KB 之后還剩下很多空間可以用——如果能快速搞定,然后在這上面玩 Mac,那該有多酷?

          一段時(shí)間過去后,我真的做到了:

          說出來你可能不信,這個(gè)高質(zhì)量項(xiàng)目我沒花多長(zhǎng)時(shí)間就完成了。軟件顯然是最關(guān)鍵的部分,我分成了 3 個(gè)不同的項(xiàng)目來進(jìn)行。

          那么接下來,你將看到一個(gè)關(guān)于我這場(chǎng)“開發(fā)之旅”的故事。


          什么是 pico-mac?

          它是一個(gè)基于 Raspberry Pi RP2040 微控制器(安裝在 Pico 板上)的系統(tǒng),能夠驅(qū)動(dòng)單色 VGA 視頻并接受 USB 鍵盤/鼠標(biāo)輸入,仿真 Macintosh 128K 計(jì)算機(jī)及其磁盤存儲(chǔ)。RP2040 的 RAM 容量足以容納 Mac 的內(nèi)存和仿真器的內(nèi)存。通過一些小技巧,它的速度能達(dá)到真實(shí) Macintosh 的性能,還具備 USB 主機(jī)功能,并且 PIO 模塊使得驅(qū)動(dòng) VGA 視頻也相對(duì)簡(jiǎn)單。基本版 Pico 板的 2MB 閃存足以容納操作系統(tǒng)和軟件的磁盤映像。

          以下是 Pico MicroMac 的實(shí)際運(yùn)行情況,為未來的“無紙化辦公”做好了準(zhǔn)備:

          (未來的 Pico MicroMac RISC CISC 工作站)

          我以前沒怎么用過 Mac 128K,只在博物館的機(jī)器上點(diǎn)擊過幾下。但我知道它們可以運(yùn)行 MacDraw、MacWrite 和 MacPaint——對(duì)于 128K 設(shè)備來說,這三個(gè)應(yīng)用程序非常棒:一個(gè)基本所見即所得的文字處理器,帶有多種字體,還有一個(gè)矢量繪圖軟件。

          如果想體驗(yàn)早期 Macintosh 系統(tǒng)軟件和這些出色的應(yīng)用軟件,有一個(gè)方法是訪問 https://infinitemac.org,這個(gè)網(wǎng)站通過 emscript 把 Mini vMac 仿真器封裝在瀏覽器中運(yùn)行(強(qiáng)烈推薦,有很多有趣內(nèi)容可以玩)

          提前劇透一下,我開發(fā)的 MicroMac 確實(shí)可以運(yùn)行 MacDraw,在“仿真硬件”上用它非常有趣:

          如果你也想做一個(gè)自己的 Pico-Mac,可以參考 GitHub 鏈接(https://github.com/evansm7/pico-mac),里面有具體的制作說明。


          我的開發(fā)之旅,開始!

          細(xì)想了一下,其實(shí)我一開始并沒有打算做一個(gè) Pico 項(xiàng)目,只是隱約對(duì)它是否可行有點(diǎn)感興趣,于是開始在我的普通電腦上搗鼓制作了一個(gè) Mac 128K仿真器。

          三條規(guī)則

          對(duì)于這個(gè)項(xiàng)目,我最初定了幾條簡(jiǎn)單的規(guī)則:

          • 必須要做有趣的事。為了讓它能正常運(yùn)行,黑進(jìn)去做一些改動(dòng)也是可以的。

          • 我喜歡寫仿真程序,但我不想深入學(xué)習(xí)和了解 68K 匯編語(yǔ)言。我知道有很多人喜歡 68K,它也確實(shí)很好,但我不喜歡把它作為 CPU。所以,一開始我本想直接用別人做好的現(xiàn)成 68K 解釋器。

          • 同樣,我還想深入了解很多操作系統(tǒng)的內(nèi)部結(jié)構(gòu),但早期的 Mac 系統(tǒng)軟件并不在我的考慮之列。我只需要進(jìn)入系統(tǒng)、模擬硬件、把操作系統(tǒng)作為黑盒啟動(dòng),就可以了。

          但在整個(gè)項(xiàng)目過程中,我經(jīng)常打破上述規(guī)則,有時(shí)是兩條,有時(shí)是全部。


          Mac 128K

          這類機(jī)器一般都非常簡(jiǎn)單,也符合當(dāng)時(shí)的時(shí)代特征。我從原理圖和《Inside Macintosh》開始學(xué)習(xí),這些 PDF 文件涵蓋了原始 Mac 硬件、內(nèi)存映射、鼠標(biāo)/鍵盤等各種細(xì)節(jié)。

          Macintosh 的硬件配置:

          • 運(yùn)行頻率大約 8MHz 的 Motorola 68000 CPU;

          • 扁平內(nèi)存結(jié)構(gòu),將內(nèi)存解碼為不同區(qū)域,用于內(nèi)存映射 IO,連接到 6522 VIA、8530 SCC 和 IWM 軟盤控制器(某些地址解碼有些復(fù)雜)

          • 鍵盤和鼠標(biāo)通過 VIA/SCC 芯片連接。

          • 沒有外部中斷控制器:68K 有 3 條 IRQ 線,對(duì)應(yīng) 3 個(gè) IRQ 來源(VIA、SCC、編程器開關(guān)/NMI)

          • 沒有插槽或擴(kuò)展卡。

          • 沒有 DMA 控制器:一個(gè)簡(jiǎn)單的自主 PAL 狀態(tài)機(jī)從 DRAM 中掃描視頻和音頻樣本。視頻分辨率固定為 512x342 1BPP。

          • 唯一的存儲(chǔ)設(shè)備是內(nèi)部軟盤驅(qū)動(dòng)器(外加一個(gè)外部驅(qū)動(dòng)器),由 IWM 芯片驅(qū)動(dòng)。

          前三款 Mac 型號(hào)非常相似:

          • Mac 128K 和 Mac 512K 是同一款設(shè)備,只是內(nèi)存不同。

          • Mac Plus 在內(nèi)存映射中添加了 SCSI 接口和一個(gè) 800K 軟盤驅(qū)動(dòng)器,該驅(qū)動(dòng)器是雙面的,而原來的軟驅(qū)是單面 400K。

          • Mac Plus 的 ROM 也支持 128K/512K,是 Macintosh 512Ke 的升級(jí)版。其中 ‘e’ 表示額外的 ROM 功能。

          Mac Plus 的 ROM 支持 HD20 外部硬盤和 HFS 文件系統(tǒng),Steve Chamberlin 對(duì)其拆解進(jìn)行了注釋。這就是我要使用的 ROM:我正在制作一臺(tái) Macintosh 128Ke。


          Mac 仿真器:umac

          經(jīng)過大約 8 分鐘的研究,我選擇了 Musashi 68K 解釋器。它是用 C 語(yǔ)言編寫的,接口簡(jiǎn)單,并且提供了一個(gè)簡(jiǎn)單的、開箱即用的 68K 系統(tǒng)示例,包含 RAM、ROM 和一些 IO。Musashi 適合嵌入到更大的項(xiàng)目中:連接內(nèi)存讀/寫回調(diào)、一個(gè)引發(fā) IRQ 的函數(shù),并在循環(huán)中調(diào)用執(zhí)行,完成。

          我開始圍繞它構(gòu)建一個(gè)仿真器,最終這個(gè)項(xiàng)目成為了 umac。前半部分進(jìn)行得相當(dāng)順利:

          1、構(gòu)建一個(gè)簡(jiǎn)單的命令行應(yīng)用程序,加載 ROM 鏡像,分配 RAM、提供調(diào)試消息、斷言和日志記錄,并配置 Musashi。

          2、添加地址解碼:將 CPU 的讀/寫操作引導(dǎo)到 RAM 或 ROM。“overlay”寄存器使得 ROM 可以在 0x00000000 地址啟動(dòng),然后在設(shè)置 CPU 異常向量后跳轉(zhuǎn)到一個(gè)高地址的 ROM 鏡像——這會(huì)影響地址解碼。這是通過操作 VIA 寄存器完成的,所以現(xiàn)在只解碼了該寄存器的一部分。

          3、此時(shí),ROM 開始運(yùn)行并訪問更多不存在的 VIA 和 SCC 寄存器。于是添加更多的地址解碼和一個(gè)模擬這些設(shè)備的框架——讓 MMIO 讀/寫操作只是被簡(jiǎn)單地標(biāo)記出來。

          4、有一些 ROM 訪問的特殊地址會(huì)“錯(cuò)過”記錄在案的設(shè)備:有一個(gè)制造測(cè)試選項(xiàng),它會(huì)探測(cè)是否有插件,然后我們就會(huì)看到 RAM 大小的探測(cè)結(jié)果。Mac Plus ROM 正在尋找最多 4MB 的 RAM。在分配給 RAM 的大區(qū)域中,實(shí)際 RAM 的較小容量被反復(fù)鏡像,因此探針會(huì)在高地址和開始環(huán)繞的點(diǎn)寫入一個(gè)特殊值,

          5、然后初始化 RAM 并填充已知模式。這是一個(gè)令人興奮的時(shí)刻,因?yàn)槲铱梢赞D(zhuǎn)儲(chǔ) RAM,將用于視頻幀緩沖區(qū)的區(qū)域轉(zhuǎn)換為圖像,并看到用于 RAM 測(cè)試的“對(duì)角條紋”圖案!

          6、并非所有設(shè)備代碼都喜歡讀取全零值,所以有時(shí)需要參考反匯編并返回 0xffffffff 以推動(dòng)它進(jìn)一步運(yùn)行。我們的目標(biāo)是讓它能夠訪問 IWM 芯片,即嘗試加載操作系統(tǒng)。

          7、在看到一些 IWM 訪問并返回隨機(jī)無意義的值后,第一個(gè)美妙的時(shí)刻是出現(xiàn)了帶問號(hào)的“未知磁盤”圖標(biāo)——真正的圖形!ROM 真的在做一些事!

          8、此時(shí)我還沒有實(shí)現(xiàn)任何 IRQ,并發(fā)現(xiàn) ROM 進(jìn)入了一個(gè)無限循環(huán):它在計(jì)算幾個(gè) Vsync 以延遲閃爍的問號(hào)。于是我轉(zhuǎn)向了更好的 VIA,它能為 GPIO 寄存器讀/寫和 IRQ 處理提供回調(diào)。這還需要連接到 Musashi 的 IRQ 函數(shù)。

          以上過程很大程度上激勵(lì)了我繼續(xù)做下去——記住規(guī)則一:盡管這是通過手動(dòng)內(nèi)存轉(zhuǎn)儲(chǔ)和 ImageMagick 轉(zhuǎn)換才看到的“圖形”,但這依然很棒。


          IWM、68K 和磁盤驅(qū)動(dòng)程序

          其實(shí)在上個(gè)步驟中,我就知道 IWM 是一款很“有趣”的芯片,但對(duì)具體細(xì)節(jié)不太了解,因此打算在需要時(shí)再弄清楚——幸虧我把研究 IWM 的事拖到了現(xiàn)在。如果我在項(xiàng)目開始時(shí)就讀了它的“數(shù)據(jù)手冊(cè)”(一份含糊不清的寄存器文檔),我肯定會(huì)原地放棄。

          IWM 確實(shí)很不錯(cuò),但它非常底層。其他同時(shí)代機(jī)器的磁盤控制器,例如 WD1770,會(huì)抽象出磁盤的物理操作,所以在某種程度上,你只需撥動(dòng)寄存器,讓控制器步進(jìn)到第 17 條軌道,然后抓取第 3 扇區(qū)。但 IWM 不是這樣的:首先,磁盤是恒線速度的,這意味著角速度需要根據(jù)當(dāng)前軌道進(jìn)行調(diào)整;其次,IWM 只會(huì)給 CPU 提供從磁盤頭讀取的大量原始數(shù)據(jù)(幾乎沒有解碼)

          我花了很長(zhǎng)時(shí)間閱讀 ROM 中 IWM 驅(qū)動(dòng)程序的反匯編代碼(違反了規(guī)則 1 和規(guī)則 2):驅(qū)動(dòng)程序包含某種伺服控制環(huán)路,通過調(diào)節(jié)發(fā)送到 DAC 的 PWM 值來控制磁盤馬達(dá),并與 VIA 定時(shí)器的參考值進(jìn)行比較,以實(shí)現(xiàn)動(dòng)態(tài)速率匹配,從磁盤扇區(qū)獲取正確的比特率。我認(rèn)為,一旦找到軌道起點(diǎn),驅(qū)動(dòng)程序就會(huì)將軌道數(shù)據(jù)流入內(nèi)存,解碼符號(hào)(更復(fù)雜的編碼)并選擇感興趣的扇區(qū)。

          說實(shí)話,我有點(diǎn)喪。我原以為像 Basilisk II 和 Mini vMac 這樣的仿真器已經(jīng)通過某種巧妙的方式解決了這個(gè)問題,因?yàn)樗鼈兡苣M軟盤——但實(shí)際上它們并沒有,而是直接避開了這個(gè)問題。

          至于其他仿真器,對(duì) ROM 進(jìn)行了很多補(bǔ)丁處理:ROM 并不是未經(jīng)修改就運(yùn)行的。可能有人會(huì)說,雖然這樣修改 ROM 它就不再是完美的硬件仿真了,但那又如何?嗯,我懷疑他們也遵循了規(guī)則 1,因?yàn)槲乙泊蛩氵@樣做。

          我研究了一些 Mac 驅(qū)動(dòng)程序接口的工作原理(唉,還是違反了規(guī)則 3),并理解了其他仿真器是如何進(jìn)行補(bǔ)丁的。它們使用自定義的半虛擬化 68K 驅(qū)動(dòng)程序,覆蓋 ROM 中的 IWM 驅(qū)動(dòng)程序,為來自塊層的 .Sony 請(qǐng)求提供服務(wù),并將其路由到更方便的主機(jī)端代碼來管理這些請(qǐng)求。Basilisk II 使用了一些自定義的 68K 操作碼和一個(gè)簡(jiǎn)單的驅(qū)動(dòng)程序,而 Mini vMac 則使用了一個(gè)復(fù)雜的驅(qū)動(dòng)程序,對(duì)自定義的內(nèi)存區(qū)域進(jìn)行“陷阱”訪問。我重新使用了 Basilisk II 驅(qū)動(dòng)程序,但將其轉(zhuǎn)換為訪問一個(gè)自定義區(qū)域(這樣更容易路由:只需模擬另一個(gè)設(shè)備)。驅(qū)動(dòng)程序的回調(diào)主機(jī) / C 端執(zhí)行,一些簡(jiǎn)化的 Basilisk II 代碼解釋請(qǐng)求,并將數(shù)據(jù)復(fù)制到操作系統(tǒng)提供的緩沖區(qū)或從中復(fù)制數(shù)據(jù)。這樣一來,我只需要從一個(gè)磁盤讀取塊:不需要不同的格式(甚至不需要寫入支持),也不需要多個(gè)驅(qū)動(dòng)器,更不需要彈出/更換鏡像。

          從磁盤加載第一個(gè)數(shù)據(jù)塊比第一部分花的總時(shí)間還長(zhǎng)。我本來想著要不再學(xué)點(diǎn) 68K 匯編(又違反了規(guī)則 3……),但在這千鈞一發(fā)之際,我看到了一個(gè) Happy Mac 圖標(biāo),表示系統(tǒng)軟件開始加載。

          這時(shí),我的仿真器仍然是一個(gè)簡(jiǎn)單的 Linux 命令行應(yīng)用程序,沒有任何用戶界面,沒有鍵盤或鼠標(biāo),也沒有視頻輸出。于是,我覺得是時(shí)候?qū)⑺庋b在一個(gè) SDL2 前端中了,這樣能實(shí)時(shí)看到屏幕重繪效果。我把 1Hz 的計(jì)時(shí)器中斷添加到 VIA 中,它就成功啟動(dòng)了!

          (第一次啟動(dòng))

          順便一提,我試著為所有嵌入式項(xiàng)目都創(chuàng)建一個(gè)雙目標(biāo)構(gòu)建,即一個(gè)用于快速原型設(shè)計(jì)/調(diào)試的本地主機(jī)構(gòu)建,用 libSDL 代替 LCD,這意味著我不需要在 MCU 上編碼。

          接下來是鼠標(biāo)支持。Macintosh 內(nèi)部和原理圖展示了它是如何與 VIA 和 SCC 連接的。SCC 是我在這臺(tái)機(jī)器中第二個(gè)不喜歡的芯片:很復(fù)雜,數(shù)據(jù)手冊(cè)似乎故意隱藏信息、惹惱讀者、報(bào)復(fù)世界。但它能執(zhí)行各種上世紀(jì) 80 年代的線路編碼方案,減輕 CPU 的工作負(fù)擔(dān),對(duì)于支持 AppleTalk 等功能至關(guān)重要

          到這一步,雛形幾乎就完整了:有一個(gè)能工作的鼠標(biāo),我可以用 Mini vMac 構(gòu)建一個(gè)新的磁盤鏡像,其中還包含 Missile Command 這款游戲——不到 10KB,非常好玩。

          總體來說:

          • 視頻正常

          • 能從磁盤啟動(dòng)

          • 鼠標(biāo)正常,Missile Command 也能運(yùn)行

          雖然還沒有鍵盤,但大部分功能已經(jīng)實(shí)現(xiàn)。是時(shí)候開始第二個(gè)子項(xiàng)目了。


          硬件和 RP2040

          與 umac 無關(guān),我設(shè)計(jì)了一個(gè)電路和固件,目的有兩個(gè):

          (1)用最少的組件將 512x342x1 的視頻顯示到 VGA 上。

          (2)讓 TinyUSB HID 示例正常工作并集成。

          準(zhǔn)確來說,這個(gè)項(xiàng)目只是為了將測(cè)試圖像復(fù)制到幀緩沖區(qū),并通過 printf() 輸出鍵盤/鼠標(biāo),作為一個(gè)概念驗(yàn)證。視頻部分的工作很有趣:雖然我之前做過一些 I2S 音頻 PIO 的項(xiàng)目,但這次我想輸出視頻信號(hào)并隨意控制 Vsync 和 Hsync。

          為了測(cè)試,我需要一個(gè)電路。VGA 接口要求視頻 R、G、B 信號(hào)最大電壓為 0.7V,以及同步信號(hào)的某些電壓(具體數(shù)值略)。R、G、B 信號(hào)對(duì)地電阻為 75Ω:經(jīng)過計(jì)算,3.3V GPIO 通過 100Ω 電阻驅(qū)動(dòng)這三個(gè)信號(hào)大致可行。

          開始焊接的那天,我需要一個(gè) VGA 接口。我手頭雖說有一個(gè) DB15 接頭,但想把它用在另一個(gè)項(xiàng)目上,剪斷 VGA 電纜也不太合適。午餐后散步時(shí),我無意在街邊發(fā)現(xiàn)了一些電纜,其中就有一根 VGA 電纜——雖然生銹了,但看起來有一種隨性的美感。

          (免費(fèi)的 VGA 電纜)

          VGA PIO 這部分非常有趣。最終,PIO 動(dòng)態(tài)讀取配置信息來控制 Hsync 寬度、顯示位置等,然后用一些 DMA 技巧掃描出配置信息與幀緩沖區(qū)數(shù)據(jù)。通過正確的位移方向并使用 RP2040 DMA 上的字節(jié)交換選項(xiàng),無需在 CPU 端進(jìn)行拷貝或格式轉(zhuǎn)換,就能直接輸出大端 Mac 幀緩沖區(qū)。

          不過,我總共重寫了三次視頻部分:

          (1)第一個(gè)版本有兩個(gè) DMA 通道寫入 PIO TX FIFO。第一個(gè)傳輸配置信息,然后觸發(fā)第二個(gè)傳輸視頻數(shù)據(jù),接著引發(fā) IRQ。然后,IRQ 處理程序會(huì)在短時(shí)間內(nèi)選擇要讀取的新幀緩沖區(qū)地址,并重新對(duì) DMA 進(jìn)行編程。這種方法能正常工作,但對(duì)系統(tǒng)中的其他活動(dòng)非常敏感。有個(gè)很明顯的解決方法是,任何對(duì)延遲敏感的 IRQ 處理程序都必須具有 __not_in_flash_func() 屬性,避免 RAM 耗盡。但即便如此,該設(shè)計(jì)也沒有給重新配置 DMA 留出太多時(shí)間:快速移動(dòng)鼠標(biāo)時(shí),會(huì)出現(xiàn)隨機(jī)閃爍和空白。

          (3)第二個(gè)版本采用了雙緩沖區(qū),目的是讓 IRQ 處理程序的工作變得簡(jiǎn)單:快速插入預(yù)先準(zhǔn)備好的 DMA 配置,然后在關(guān)鍵時(shí)刻計(jì)算出下次使用的緩沖區(qū)。這種方法的效果好了很多,但在高負(fù)載下仍會(huì)有一些故障。更奇怪的是,它有時(shí)會(huì)完全空白,需要重置,這讓我困惑了好一陣子。最終我打印出了 PIO FIFO 的 FDEBUG 寄存器,試圖在運(yùn)行中發(fā)現(xiàn)錯(cuò)誤。我看到 TXOVER 溢出標(biāo)志被設(shè)置了,但這應(yīng)該是不可能的:FIFO 根據(jù)需求從 DMA 拉取數(shù)據(jù),帶有 DMA 請(qǐng)求和基于信用的流量控制……哦,等等,如果信用發(fā)生混亂或重復(fù),就會(huì)發(fā)生過多傳輸,導(dǎo)致接收端溢出。

          我漏看了 RP2040 DMA 文檔中的一個(gè)細(xì)節(jié)規(guī)則:“多個(gè)通道不應(yīng)連接到相同的 DREQ。”

          (3)因此第三個(gè)版本……并沒有違反這個(gè)規(guī)則,但也變得更加復(fù)雜:

          • 一個(gè) DMA 通道傳輸數(shù)據(jù)到 PIO TX FIFO

          • 另一個(gè)通道負(fù)責(zé)設(shè)置第一個(gè)通道,從配置數(shù)據(jù)緩沖區(qū)發(fā)送數(shù)據(jù)

          • 第三個(gè)通道負(fù)責(zé)設(shè)置第一個(gè)通道,從視頻數(shù)據(jù)緩沖區(qū)發(fā)送數(shù)據(jù)

          • 第一個(gè)通道的設(shè)置觸發(fā)相應(yīng)的“next reprogram me”通道

          除了不會(huì)出現(xiàn)鎖定或視頻損壞之外,還有一個(gè)好處就是在視頻行掃描期間會(huì)觸發(fā) Hsync IRQ,從而大大縮短了重新配置 DMA 的時(shí)間限制。我還想進(jìn)一步改進(jìn)這一點(diǎn)(再增加一個(gè) DMA 通道),讓每行傳輸都不需要 IRQ,因?yàn)槟壳?IRQ 的開銷約占 CPU 時(shí)間的 1%。

          所以,現(xiàn)在我們有了一個(gè)可以嵌入 umac 的平臺(tái)和固件框架,支持 HID 輸入和視頻輸出。至此,硬件部分已完成,接下來交給軟件團(tuán)隊(duì)。


          回到仿真器的開發(fā)工作上

          看了一眼本地 umac 二進(jìn)制文件,我發(fā)現(xiàn)要在 Pico 上運(yùn)行還需要解決一些問題:

          • Musashi 運(yùn)行時(shí)在 RAM 中構(gòu)建了一個(gè)巨大的操作碼解碼跳轉(zhuǎn)表。這個(gè)表永遠(yuǎn)不會(huì)改變,也不會(huì)在運(yùn)行時(shí)更改。我添加了一個(gè) Musashi 構(gòu)建時(shí)生成器,這樣該表就可以設(shè)為 const(可存儲(chǔ)在 flash 中)

          • 反匯編器占用了很大空間,而且在 Pico 上也沒用,所以可以一個(gè)構(gòu)建不包含反匯編器的版本。

          • Musashi 為了準(zhǔn)確計(jì)算每條指令的執(zhí)行周期,用了很多的大型查找表。雖然這對(duì)一些游戲主機(jī)來說很有用,但對(duì) Mac 來說并不重要,因此我移除了這些查找表。

          pico-mac 開始成形,ROM 和磁盤鏡像存儲(chǔ)在 flash 中,現(xiàn)在可以在 Pico 上構(gòu)建并運(yùn)行了!只要注意不要把東西塞進(jìn) RAM,RAM 的使用情況還是不錯(cuò)的。仿真器和 HID 代碼總共使用了大約 35-40KB 的 Mac 128KB RAM 區(qū)域,還剩 95KB 以上的可用 RAM。

          這正是為 umac 添加鍵盤支持的好時(shí)機(jī)。Mac 鍵盤通過 VIA 的“移位寄存器”串行接口連接,這是一個(gè)基本的同步串行接口。雖然邏輯上很簡(jiǎn)單,但在早期嘗試響應(yīng) ROM 的“初始化”命令時(shí)總是被忽略。ROM 的反匯編又派上了大用場(chǎng):在閱讀鍵盤啟動(dòng)代碼時(shí),如果響應(yīng)字節(jié)在請(qǐng)求發(fā)送后過早出現(xiàn),就會(huì)導(dǎo)致中斷確認(rèn)的競(jìng)爭(zhēng)條件。因此我在請(qǐng)求發(fā)送后插入了一個(gè)延遲,將響應(yīng)延遲到稍后輪詢,然后就只需要映射按鍵代碼了。

          有了鍵盤支持,就達(dá)到了 MacWrite 的最終關(guān)卡:

          但有一個(gè)問題:它的表現(xiàn)完全不行,速度超級(jí)慢。我添加了一個(gè) 1Hz 的指令計(jì)數(shù)轉(zhuǎn)儲(chǔ),發(fā)現(xiàn)它每秒只執(zhí)行約 300 KIPS(千條指令)

          68000 CPU 在 IPC 方面并不出色。雖然有些指令可以在 4 個(gè)周期內(nèi)執(zhí)行,但如果你想用那些復(fù)雜的尋址模式,訪問內(nèi)存會(huì)花費(fèi)很多周期。當(dāng)然我不是專家,但我覺得為大約 8MHz 的 68000 設(shè)定大約 1 MIPS(百萬(wàn)條指令)的目標(biāo)并不過分,只需要提高 3 倍。


          性能

          我可沒說我不會(huì)作弊:讓我們把 Pico 的運(yùn)行頻率從 125MHz 提高到 250MHz。好是好了點(diǎn),但也沒好到翻倍,我記得好像只提高了大約 30%。

          Musashi 有很多可配置選項(xiàng)。我的第一個(gè)目標(biāo)是讓主循環(huán)(從反匯編/編譯后端來看)變小:Mac 不會(huì)報(bào)告總線錯(cuò)誤,所以寄存器不需要副本展開。操作碼總是從 16 位邊界獲取,因此不需要對(duì)齊檢查,可以用半字加載(而不是將兩個(gè)字節(jié)加載合并為一個(gè)半字)。對(duì)于 Cortex-M0+/armv6m ISA ,通過重新排列 CPU 上下文結(jié)構(gòu)字段,可實(shí)現(xiàn)即時(shí)偏移訪問和更好的代碼。令人費(fèi)解的是,CPU 類型是動(dòng)態(tài)可變的,這導(dǎo)致了大量運(yùn)行時(shí)的間接操作。

          看起來好多了,也許有 2 倍的改進(jìn),但還不夠。Missile Command 仍然很卡,鼠標(biāo)也依舊不流暢!接下來,是一些較為激進(jìn)的優(yōu)化:刪除地址對(duì)齊檢查,因?yàn)樵谶@種受限環(huán)境中不會(huì)發(fā)生未對(duì)齊的訪問。

          不過,真正的優(yōu)化來自下面提及的另一個(gè)小技巧。


          RP2040 內(nèi)存訪問

          RP2040 擁有快速 RAM,該 RAM 采用多行設(shè)計(jì),允許多個(gè)用戶(如兩個(gè) CPU 和 DMA)進(jìn)行單周期訪問。默認(rèn)情況下,大多數(shù)代碼通過外部 QSPI 閃存的 XIP 運(yùn)行,而 QSPI 通常以內(nèi)核時(shí)鐘(默認(rèn) 125MHz)的速度運(yùn)行,但隨機(jī)讀取一個(gè)字的延遲約為 20 個(gè)周期。為縮短這種延遲,RP2040 配備了一個(gè) 16KB 的簡(jiǎn)單緩存,但如果代碼量大,就更容易在調(diào)用函數(shù)時(shí)觸發(fā) QSPI 讀取。當(dāng)超頻到 250MHz 時(shí),QSPI 無法達(dá)到那么快的頻率,所以會(huì)保持在 125MHz。這樣一來,當(dāng)緩存未命中時(shí), QSPI 的 20 個(gè)周期延遲會(huì)變成 40 個(gè) CPU 周期。

          這里的問題在于,Musashi 在構(gòu)建時(shí)會(huì)生成大量代碼,每 1968 個(gè)操作碼都有一個(gè)函數(shù),再加上一個(gè) 256KB 的操作碼跳轉(zhuǎn)表。即使內(nèi)部執(zhí)行循環(huán)非常高效,操作碼分發(fā)和函數(shù)調(diào)用也可能在閃存緩存中未命中。如果我們想在 200 MIPS 的基礎(chǔ)上實(shí)現(xiàn) 1 MIPS ,那這些延遲就會(huì)累加。

          面對(duì)這種情況,可以用 __not_in_flash_func() 屬性將指定函數(shù)復(fù)制到 RAM 中,以保證快速執(zhí)行。最起碼,主循環(huán)和內(nèi)存訪問函數(shù)需要這個(gè)屬性,因?yàn)槊織l指令都需要訪問一個(gè)操作碼,并且很可能會(huì)讀寫 RAM 。

          這樣的優(yōu)化,又提升了幾個(gè)百分點(diǎn)的性能。

          接下來,我嘗試對(duì)整類操作碼進(jìn)行優(yōu)化:移動(dòng)很頻繁,分支也很頻繁,所以把它們放在 RAM 中。這確實(shí)能提高性能,但 RAM 很快就用完了,距離目標(biāo)的 1 MIPS 也還有差距。

          還記得我說 RISC 架構(gòu)會(huì)改變一切嗎?

          我們想要加速的 1968 個(gè) 68K 操作碼中,有哪些是最常用的?在 umac 中加入一個(gè) 64K 的計(jì)數(shù)器表,啟動(dòng) Mac 并運(yùn)行一些關(guān)鍵應(yīng)用程序(實(shí)際上是玩了一會(huì)兒 Missile Command ),就能得到一份動(dòng)態(tài)指令使用情況的統(tǒng)計(jì)概況。結(jié)果顯示,最常用的 100 個(gè)操作碼(占總數(shù)的 5%)占了 89% 的執(zhí)行次數(shù),而最常用的 200 個(gè)操作碼則占了 98% 的執(zhí)行次數(shù)。

          根據(jù)這個(gè)統(tǒng)計(jì)結(jié)果,umac 在構(gòu)建后對(duì) Musashi 自動(dòng)生成的代碼進(jìn)行處理,并將最常用的 200 個(gè)函數(shù)附上 __not_in_flash_func() 屬性。這樣只增加了 17KB 的 RAM 使用(剩余 95KB),而性能提升到了約 1.4 MIPS!

          終于,我們可以流暢地享受 Missile Command 的黑暗主題了:


          MacPaint 如何?

          人人都愛 MacPaint,但你會(huì)發(fā)現(xiàn)我一直對(duì)它避而不談,因?yàn)椋?/span>

          它無法在 Mac 128Ke 上運(yùn)行,因?yàn)?Mac Plus ROM 使用的 RAM 比原來的多:我在 68kMLA 上看到過一個(gè)關(guān)于“Mac 256K”的討論,很可能 Mac 128K 在實(shí)驗(yàn)室里實(shí)際上是 Mac 256K(甚至可能本來打算是 256K,但在發(fā)布前削減了成本),因?yàn)椴僮飨到y(tǒng)在 256KB RAM 下運(yùn)行良好。

          當(dāng)時(shí)我在想,Mac ROM/OS 是否一定要是 2 的 n 次方才行?如果不是,那我還有 95K 空閑內(nèi)存,能否制作一個(gè)“Mac 200K”,然后運(yùn)行去 MacPaint?于是,我試了一個(gè)本地黑客程序,可以根據(jù)給定的內(nèi)存大小修改 ROM,更新其全局 memTop 變量。結(jié)果不錯(cuò),我還用 256K 、 208K 和 192K 進(jìn)行了啟動(dòng)測(cè)試。不過,也有一些問題需要解決:如果內(nèi)存大小不是 2 的 n 次方,ROM memtest 就會(huì)出錯(cuò),而跳過這個(gè)測(cè)試又會(huì)導(dǎo)致其他問題。這些問題都可以解決,但有些啟動(dòng)過程會(huì)訪問超出 RAM 末尾的區(qū)域。另外,2 的 n 次方以通過簡(jiǎn)單的地址掩碼將 RAM 訪問限制在有效緩沖區(qū)內(nèi),而 192K 無法做到這一點(diǎn)。

          不幸的是,當(dāng)我測(cè)試 MacPaint 時(shí),它仍然無法運(yùn)行,因?yàn)樗枰獙⑴R時(shí)文件寫入一個(gè)只讀引導(dǎo)卷。這完全違反了規(guī)則 1,所以我們現(xiàn)在暫時(shí)還是保持 128KB。另外,256K MicroMac 是完全可行的,只需要一個(gè)內(nèi)存容量為 300KB 的微控制器就大功告成了。

          Python入門到精通
          Python入門到精通:人生苦短,我用Python!Python每日推送、Python教程、Python資料、Python視頻、Python項(xiàng)目、Python學(xué)習(xí)等。
          公眾號(hào)
          瀏覽 90
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  直接能看的黄色网址 | 9999re| 超碰在线伊人 | 操逼电影影音先锋 | 久久小视频 |