用復古電腦程序 Toy CPU 學習低級編程
我寫了一個名為 「Toy CPU」 的教育性復古計算機程序,以便我的學生能夠學習機器語言。
我兼職教授大學課程,包括一個對所有專業開放的一般計算機主題的課程。這是一門入門課程,向學生講授技術是如何運作的,以消除圍繞計算的神秘感。
雖然不是計算機科學課程,但這門課的一個部分涉及計算機編程。我通常用非常抽象的術語談論編程,所以不會讓聽眾聽不懂。但是今年,我想讓我的學生以 「老派」 的方式做一些需要 「動手」 的編程。同時,我又想保持簡單,以便讓每個人都能跟上。
我喜歡將我的課程結構化,以顯示你是如何從 「那裡」 到 「這裡」 的。理想情況下,我會讓我的學生學習如何編寫一個簡單的程序。然後,我將從這裡開始,展示現代編程是如何讓開發人員創建更複雜的程序的。我決定嘗試一種非常規的方法 —— 教學生學習終極的低級別編程語言:機器語言。
機器語言編程
早期的個人電腦如 Apple II(1977 年)、TRS-80(1977 年)和 IBM PC(1981 年)讓用戶用鍵盤輸入程序,並在屏幕上顯示結果。但計算機並不總是帶有屏幕和鍵盤。
Altair 8800 和 IMSAI 8080(均為 1975 年製造)要求用戶使用面板上的 「開關和燈」 輸入程序。你可以用機器語言輸入指令,使用一組開關,機器會點亮 LED 燈以代表每個二進位指令的 1 和 0。
對這些早期機器進行編程,需要了解被稱為 「 操作碼 」 (操作代碼的簡稱)的機器語言指令,以執行基本操作,如將兩個數字相加或將一個值存儲到計算機的存儲器中。我想向我的學生展示程序員是如何通過開關和燈,手工輸入一系列指令和內存地址的。
然而,在這門課上,使用實際的 Altair 8800 就有點太複雜了。我需要一些簡單的、任何初級水平的學生都能掌握的東西。理想情況下,我希望能找到一個簡單的 「業餘」 復古計算機,其工作原理與 Altair 8800 相似,但我無法找到一個價格低於 100 美元的合適的 「類似 Altair」 的設備。我找到了幾個 「Altair」 軟體模擬器,但它們忠實地再現了 Altair 8800 的操作碼,這對我的需求來說太過沉重。
我決定編寫我自己的 「教育」 復古計算機。我稱它為 「Toy CPU」。你可以在我的 GitHub 代碼庫 上找到它,包括幾個可以運行的版本。第一版是一個實驗性的原型,運行在 FreeDOS 上。第二版是一個更新的原型,在 Linux 上用 ncurses 運行。版本 3 是一個 FreeDOS 程序,在圖形模式下運行。
Toy CPU 的編程
Toy CPU 是一個非常簡單的復古計算機。它只有 256 位元組的內存和一個最小化的指令集,其目的是在複製 「開關和燈」 編程模式的同時保持簡單化。它的界面模仿 Altair 8800,有一系列 8 個 LED 燈,分別代表計數器(程序的 「行號」)、指令、累積器(用於臨時數據的內部存儲器)和狀態。
當你啟動 Toy CPU 時,它通過清除內存來模擬 「啟動」。當它啟動時,它也會在屏幕右下方的狀態燈中顯示 「INI」(初始化)。「PWR」(電源)燈亮表示 Toy CPU 已被打開。
當 Toy CPU 準備好讓你進入一個程序時,它通過狀態燈指示 「INP」(「輸入」模式),並讓你從程序的計數器 0 開始。Toy CPU 的程序總是從計數器 0 開始。
在 「輸入」 模式下,用上下方向鍵顯示不同的程序計數器,按回車鍵編輯當前計數器上的指令。當你進入 「編輯」 模式時,Toy CPU 的狀態燈上會顯示 「EDT」(「編輯」 模式)。
Toy CPU 有一張速查表,被 「貼」 在顯示屏的前面。它列出了 Toy CPU 可以處理的不同操作碼。
00000000
(STOP
):停止程序執行。00000001
(RIGHT
):將累加器中的位向右移動一個位置。值00000010
變成00000001
,00000001
變成00000000
。00000010
(LEFT
):將累加器中的位向左移動一個位置。值01000000
變成10000000
,10000000
變成00000000
。00001111
(NOT
):對累加器進行二進位非操作。例如,值10001000
變成01110111
。00010001
(AND
):對累加器用存儲在某一地址的值進行二進位與操作。該地址被存儲在下一個計數器中。00010010
(OR
):對累積器用存儲在某一地址的值進行二進位或運算。00010011
(XOR
):對累加器用存儲在某一地址的值進行二進位異或運算。00010100
(LOAD
):將一個地址的值載入(複製)到累加器中。00010101
(STORE
): 存儲(複製)累加器中的值到一個地址。00010110
(ADD
):將存儲在某一地址的數值加入到累加器中。00010111
(SUB
):從累積器中減去儲存在某一地址的數值。00011000
(GOTO
):轉到(跳到)一個計數器地址。00011001
(IFZERO
):如果累加器為零,轉到(跳到)一個計數器地址。10000000
(NOP
):空操作,可以安全地忽略。
當處於 「編輯」 模式時,使用左右方向鍵選擇操作碼中的一個位,然後按空格鍵在關閉(0)和開啟(1)之間翻轉數值。當你完成編輯後,按回車鍵回到 「輸入」 模式。
一個示常式序
我想通過輸入一個簡短的程序來探索 Toy CPU,將兩個數值相加,並將結果存儲在 Toy CPU 的內存中。實際上,這執行的是算術運算 A+B=C
。要創建這個程序,你只需要幾個操作碼:
00010100
(LOAD
)00010110
(ADD
)00010101
(STORE
)00000000
(STOP
)
LOAD
、ADD
和 STORE
指令需要一個內存地址,這個地址總是在下一個計數器的位置。例如,程序的前兩條指令是:
計數器 0:00010100
計數器 1:某個內存地址,第一個值 A 存放在那裡
計數器 0 中的指令是 LOAD
操作,計數器 1 中的值是你存儲某個值的內存地址。這兩條指令一起將內存中的數值複製到 Toy CPU 的累加器中,在那裡你可以對該數值進行操作。
將一個數字 A
裝入累加器後,你需要將數值 B
加到它上面。你可以用這兩條指令來做:
計數器 2:00010110
計數器 3:存儲第二個值 B 的內存地址
假設你把值 1
(A
)裝入累加器,然後把值 3
(B
)加到它上面。現在累加器的值是 4
。現在你需要用這兩條指令把數值 4
複製到另一個內存地址(C
):
計數器 4:00010101
計數器 5:一個內存地址(C),我們可以在那裡保存新的值
把這兩個值加在一起後,現在可以用這條指令結束程序:
計數器 6: 00000000
計數器 6 之後的任何指令都可以供程序作為存儲內存使用。這意味著你可以用計數器 7 的內存來儲存值 A
,計數器 8 的內存來儲存值 B
,計數器 9 的內存來儲存值 C
。你需要將這些分別輸入到 Toy CPU 中:
計數器 7:00000001(1)
計數器 8:00000011(3)
計數器 9:00000000(0,以後會被覆蓋)
在弄清了所有指令和 A
、B
和 C
的內存位置後,現在可以將完整的程序輸入到 Toy CPU 中。這個程序將數值 1 和 3 相加,得到 4:
計數器 0:00010100
計數器 1:00000111(7)
計數器 2:00010110
計數器 3:00001000(8)
計數器 4:00010101
計數器 5:00001001(9)
計數器 6:00000000
計數器 7:00000001(1)
計數器 8:00000011(3)
計數器 9:00000000(0,以後會被覆蓋)
要運行程序,在 「輸入」 模式下按下 R
鍵。Toy CPU 將在狀態燈中顯示 「RUN」(「運行」 模式),並從計數器 0 開始執行你的程序。
Toy CPU 有一個明顯的延遲,所以你可以看到它執行程序中的每一步。隨著程序的進行,你應該看到計數器從 00000000
(0)移動到 00000110
(6)。在計數器 1 之後,程序從內存位置 7 載入數值 1
,累積器更新為 00000001
(1)。在計數器 3 之後,程序將加數值 3
,並更新累加器顯示 00000100
(4)。累加器將保持這種狀態,直到程序在計數器 5 之後將數值存入內存位置 9,然後在計數器 6 結束。
探索機器語言編程
你可以使用 Toy CPU 來創建其他程序,並進一步探索機器語言編程。通過用機器語言編寫這些程序來測試你的創造力。
一個在累積器上閃燈的程序
你能點亮累加器上的右四位,然後是左四位,然後是所有的位嗎?你可以用兩種方法之一來寫這個程序。
一種直接的方法是,從不同的內存地址載入三個數值,像這樣:
計數器 0:LOAD
計數器 1:「右邊」
計數器 2:LOAD
計數器 3:「左邊」
計數器 4:LOAD
計數器 5:「所有」
計數器 6:STOP
計數器 7:00001111(「右邊」)
計數器 8:11110000(「左邊」)
計數器 9:11111111(「全部」)
寫這個程序的另一種方法是嘗試使用 NOT
和 OR
二進位操作。這樣可以得到一個更小的程序:
計數器 0:LOAD
計數器 1:「右邊」
計數器 2:NOT
計數器 3:OR
計數器 4:「右邊」
計數器 5:STOP
計數器 6:00001111(「右邊」)
從一個數字開始倒數
你可以把 Toy CPU 作為一個倒數計時器。這個程序行使 IFZERO
測試,只有當累加器為零時,程序才會跳轉到一個新的計數器:
計數器 0:LOAD
計數器 1:「初始值」
計數器 2:IFZERO(這也是倒計時的「開始」)
計數器 3:「結束」
計數器 4:SUB
計數器 5:「1」
計數器 6:GOTO
計數器 7:「開始」
計數器 8:STOP
計數器 9:00000111(「初始值」)
計數器 10:00000001(「1」)
Toy CPU 是學習機器語言的一個好方法。我在入門課程中使用了 Toy CPU,學生們說他們發現寫第一個程序很困難,但寫下一個程序就容易多了。學生們還表示,用這種方式編寫程序其實很有趣,他們學到了很多關於計算機實際工作的知識。Toy CPU 既具有教育性,也很有趣味性!
via: https://opensource.com/article/23/1/learn-machine-language-retro-computer
作者:Jim Hall 選題:lkxed 譯者:wxy 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive