我們已經(jīng)到了需要為 4 位 HRRG 計(jì)算機(jī)定義匯編語(yǔ)言的地步,但首先我們需要考慮某些概念。
如今,我們習(xí)慣于使用 C/C++、Java、Python 等高級(jí)語(yǔ)言對(duì)我們的計(jì)算機(jī)和微控制器進(jìn)行編程,但生活并不總是那么容易。第一批計(jì)算機(jī)程序員用機(jī)器代碼(處理器本身使用的數(shù)值)捕獲他們的程序。由于這太痛苦了,他們很快將抽象階梯向上移動(dòng)到匯編語(yǔ)言。
另請(qǐng)參閱此索引,其中列出了構(gòu)成我們的 4 位 HRRG 計(jì)算機(jī)項(xiàng)目的所有文章,以及一些有趣的相關(guān)專(zhuān)欄。
我們必須做的一件事是為我們的 4 位 HRRG 計(jì)算機(jī)定義這樣一種匯編語(yǔ)言,但在我們滿(mǎn)懷熱情地投入戰(zhàn)斗之前,我們需要先介紹幾個(gè)概念。
Big-endian 與 little-endian
當(dāng)現(xiàn)實(shí)世界的計(jì)算機(jī)使用多個(gè)字節(jié)來(lái)表示數(shù)據(jù)值或內(nèi)存地址時(shí),將這些字節(jié)存儲(chǔ)在內(nèi)存中的主要技術(shù)有兩種:要么將最高有效字節(jié) (MSB) 存儲(chǔ)在具有最低地址的位置,在這種情況下,我們可以說(shuō)它是“大端優(yōu)先”存儲(chǔ)的,或者最低有效字節(jié) (LSB) 存儲(chǔ)在最低地址中,在這種情況下,我們可以說(shuō)它是“小端優(yōu)先”存儲(chǔ)的以終為先。”
讓我們?cè)?HRRG 計(jì)算機(jī)的上下文中考慮這兩種機(jī)制,它具有 4 位(1-nybble)數(shù)據(jù)總線和 12 位(3-nybble)地址總線,通過(guò)可視化我們?nèi)绾卧趦?nèi)存中存儲(chǔ) 3-nybble 值 $426 開(kāi)始在內(nèi)存位置 $100 如下圖所示:
HRRG 采用大端方法。當(dāng)然,您可能不會(huì)對(duì)此感到驚訝——由于各種超出這些討論范圍的技術(shù)原因——一些計(jì)算機(jī)設(shè)計(jì)師偏愛(ài)一種風(fēng)格,而另一些則采取相反的策略。直到人們開(kāi)始對(duì)創(chuàng)建異構(gòu)計(jì)算環(huán)境產(chǎn)生興趣,在該環(huán)境中將多臺(tái)不同的機(jī)器連接在一起以便文件可以在它們之間傳輸,這才真正重要,在這一點(diǎn)上,許多激烈的爭(zhēng)接踵而至。
1980 年,丹尼·科恩 (Danny Cohen) 撰寫(xiě)的一篇著名論文《論圣戰(zhàn)與和平懇求》使用術(shù)語(yǔ)big-endian和little-endian來(lái)指代存儲(chǔ)數(shù)據(jù)的兩種技術(shù)。這些至今仍在使用的術(shù)語(yǔ)源自盎格魯-愛(ài)爾蘭諷刺作家喬納森·斯威夫特所著的《格列佛游記》一書(shū)。little-endian 和 big-endian 的綽號(hào)來(lái)自這個(gè)故事的一部分,即兩個(gè)國(guó)家就應(yīng)該先吃煮雞蛋的哪一端——小端還是大端——而發(fā)生戰(zhàn)爭(zhēng)!
如果你想知道的偶然機(jī)會(huì),斯威夫特在 1726 年寫(xiě)下了他的偉大作品,那是在發(fā)明臺(tái)球桿之前的 9 年(在此之前,球員過(guò)去常常用小釘頭錘擊球)。
尋址模式
術(shù)語(yǔ)尋址模式是指指定指令操作數(shù)的方式。這些小流氓有無(wú)數(shù)不同的名稱(chēng)和口味,因此以下內(nèi)容應(yīng)視為僅代表一個(gè)概述。
出于這些討論的目的,我們將假設(shè) HRRG 型架構(gòu)具有 4 位(1-nybble)數(shù)據(jù)總線和 12 位(3-nybble)地址總線。但是,下面介紹的寄存器和指令助記符是虛構(gòu)的,僅用于這些審議。
隱含(又名隱含):在隱含(有時(shí)稱(chēng)為隱含)尋址模式的情況下,目標(biāo)是由指令本身隱含的。例如,假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 INCQ 的指令,其目的是增加(加 1 到)寄存器 Q 的內(nèi)容。在這種情況下,我們將只有一個(gè)沒(méi)有操作數(shù)的 INCQ 操作碼,如圖所示以下:
假設(shè)程序計(jì)數(shù)器 (PC) 從地址 $100 開(kāi)始,CPU 將讀取并執(zhí)行隱含的操作碼。我們最終將 PC 指向地址 $101,這是 CPU 期望在程序中找到下一個(gè)操作碼的地方。
立即:在立即尋址模式的情況下,數(shù)據(jù)立即出現(xiàn)在操作碼之后。例如,假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 LDQ 的指令,其目的是使用立即尋址模式將 nybble 數(shù)據(jù)加載到寄存器 Q 中,如下所示:
假設(shè)程序計(jì)數(shù)器(PC)從地址 $100 開(kāi)始,CPU 讀取操作碼,實(shí)現(xiàn)此操作碼使用立即模式,并將數(shù)據(jù) nybble(本例中為 $F)加載到 Q 寄存器中。我們最終將 PC 指向地址 $102,這是 CPU 期望在程序中找到下一個(gè)操作碼的地方。
相對(duì):在相對(duì)尋址模式的情況下,目標(biāo)地址被指定為相對(duì)于程序計(jì)數(shù)器 (PC) 中當(dāng)前值的偏移量。這樣的偏移量將被視為可以表示正值和負(fù)值的有符號(hào)二進(jìn)制數(shù)。
假設(shè)我們的偏移量表示為 2-nybble 值。由于 2-nybble 字段可以表示 -128 到 +127 范圍內(nèi)的有符號(hào)數(shù),這意味著偏移量可以指向當(dāng)前 PC 值之前的 128 個(gè)位置(即較低的內(nèi)存地址)和之后的127 個(gè)位置之間的某個(gè)內(nèi)存位置。當(dāng)前 PC 值(即更高的內(nèi)存地址)。
純粹出于與此處所示其他示例相關(guān)的示例的考慮,假設(shè)我們有一個(gè)名為Q的寄存器和一個(gè)稱(chēng)為L(zhǎng)DQ的指令,其目的是使用相對(duì)尋址模式將一個(gè)nyble數(shù)據(jù)加載到寄存器Q中(雖然我們使用的是相同的助記符,但該LDQ的操作碼與我們?cè)谏弦粋€(gè)示例中討論的LDQ指令不同)。此外,假設(shè)偏移值為$08(十進(jìn)制+8),如下所示:
假設(shè)程序計(jì)數(shù)器(PC)從地址$100開(kāi)始,CPU讀取操作碼,實(shí)現(xiàn)其使用相對(duì)模式,并將包含偏移值的以下兩個(gè)nyble復(fù)制到內(nèi)部(臨時(shí))寄存器中。
接下來(lái),它將偏移值添加到PC中的當(dāng)前值,并使用結(jié)果指向包含數(shù)據(jù)nyble的位置。最后,它將該數(shù)據(jù)值(本例中為F)加載到Q寄存器中。最后,PC指向地址$103,CPU期望在該地址找到程序中的下一個(gè)操作碼。
純粹出于完整性考慮,讓我們考慮第二個(gè)相對(duì)尋址示例,其中偏移值為$F8(十進(jìn)制中為-8),如下所示:
具有負(fù)偏移值的相對(duì)尋址模式。(來(lái)源:Max Maxfield)
需要注意的是,除了如上所示的數(shù)據(jù)操作指令外,還可以使用相對(duì)尋址來(lái)執(zhí)行跳轉(zhuǎn)或分支指令。
當(dāng)然,并非所有處理器都支持所有類(lèi)型指令的所有尋址模式。例如,正如我們?cè)谥暗膶?zhuān)欄中所討論的,6502 微處理器有一個(gè) 8 位數(shù)據(jù)總線和一個(gè) 16 位地址總線。在其 JMP(“無(wú)條件跳轉(zhuǎn)”)指令的情況下,6502 僅支持使用 16 位(2 字節(jié))地址的絕對(duì)和間接尋址(下面介紹絕對(duì)和間接模式)。但是,6502 還支持一套使用 8 位(1 字節(jié))相對(duì)地址的分支指令。正如我在該專(zhuān)欄中指出的那樣:
程序往往會(huì)進(jìn)行大量跳轉(zhuǎn)——例如循環(huán)循環(huán)——因此在時(shí)鐘有限的日子里,使用 1 字節(jié)的分支地址而不是 2 字節(jié)的跳轉(zhuǎn)地址可能會(huì)顯著節(jié)省空間和時(shí)間速度、處理器周期和內(nèi)存位置。
Zilog Z80 微處理器不支持相對(duì)尋址,您必須使用 Intel 8086 才能更好地查看使用相對(duì)尋址模式的“短跳轉(zhuǎn)”指令。
最后一點(diǎn),我們?cè)谏厦娴挠懻撝卸啻问褂枚陶Z(yǔ)“PC 中的當(dāng)前值”來(lái)說(shuō)明要添加偏移量的值。當(dāng)“迫在眉睫”時(shí),我們使用了 103 美元的值,這是下一個(gè)操作碼的地址。為什么我們使用這個(gè)值?使用 $100(原始操作碼的地址)或 $102(偏移量中第二個(gè) nybble 的地址)不是更有意義嗎?
好吧,假設(shè)我們正在執(zhí)行某種形式的分支指令,而不是執(zhí)行我們想象的 LDQ 指令。現(xiàn)在考慮如果偏移值為 0 美元會(huì)發(fā)生什么。如果偏移量是從 $100 處的分支指令操作碼的地址開(kāi)始的,那么如果執(zhí)行分支,$0 的偏移量將導(dǎo)致無(wú)限循環(huán)。或者,如果偏移量來(lái)自地址 $102 處操作數(shù)的第二個(gè) nybble,那么 $0 的偏移量會(huì)導(dǎo)致 CPU 將操作數(shù)的第二個(gè) nybble 誤解為操作碼。歸根結(jié)底,如果偏移量為 0 美元,那么在我們的原始指令之后立即分支到操作碼是有意義的;因此,我們使用下一個(gè)操作碼的地址作為“PC 中的當(dāng)前值”。
只是為了確認(rèn)這一切,因?yàn)樗谖夷芸吹降娜魏蔚胤蕉紱](méi)有得到很好的記錄,所以我請(qǐng)我的新朋友,基于 6502 的虛擬現(xiàn)實(shí)系統(tǒng)的創(chuàng)建者 Nick Bild提供經(jīng)驗(yàn)證明。為此,Nick 創(chuàng)建了如下所示的小型 6502 匯編程序:
6502 組裝程序(來(lái)源:Nick Bild)
請(qǐng)記住,6502 有一個(gè) 8 位數(shù)據(jù)總線和一個(gè) 16 位地址總線。觀察地址 $0004 處的 BNE(“如果不相等則分支”)指令。如果滿(mǎn)足分支條件,則該指令將分支到地址 $0008 處的 JUMPHERE 標(biāo)簽。現(xiàn)在觀察匯編程序生成并存儲(chǔ)在地址 $0005 中的偏移值是 $02。當(dāng)然,$0008 – $02 = $0006,也就是 LDY(“加載索引寄存器 Y”)指令的地址;也就是說(shuō),緊跟在 BNE 指令之后的操作碼。量子點(diǎn)
絕對(duì)(又名直接):在絕對(duì)(有時(shí)稱(chēng)為直接)尋址模式的情況下,目標(biāo)地址立即出現(xiàn)在操作碼之后。例如,假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 LDQ 的指令,其目的是使用絕對(duì)尋址模式將 nybble 數(shù)據(jù)加載到寄存器 Q 中,如下所示(再一次,雖然我們使用相同的助記符,但這個(gè) LDQ與我們?cè)谇懊娴睦又杏懻摰?LDQ 指令有不同的操作碼):
假設(shè)程序計(jì)數(shù)器(PC)從地址 $100 開(kāi)始,CPU 讀取操作碼,實(shí)現(xiàn)此操作碼使用絕對(duì)模式,并將以下三個(gè)半字節(jié)(本例中為 $426)加載到內(nèi)部寄存器中。然后 CPU 使用這個(gè)內(nèi)部寄存器的內(nèi)容來(lái)指向內(nèi)存中的數(shù)據(jù) nybble(在這個(gè)例子中是 $F),它加載到 Q 寄存器中。我們最終將 PC 指向地址 $104,這是 CPU 期望在程序中找到下一個(gè)操作碼的地方。
與我們想象的 LDQ 指令相反,假設(shè)地址 $100 處的操作碼指示 CPU 使用絕對(duì)尋址模式執(zhí)行無(wú)條件 JMP。在這種情況下,CPU 將跳轉(zhuǎn)(設(shè)置 PC)到地址 $426。
間接:這是事情開(kāi)始變得有趣的地方。假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 LDQ 的指令,其目的是使用如下所示的間接尋址模式將 nybble 數(shù)據(jù)加載到寄存器 Q 中:
至于絕對(duì)模式,操作碼后面的三個(gè)半字節(jié)包含一個(gè)地址,該地址被加載到內(nèi)部寄存器中。然而,在這種情況下,地址并不直接指向數(shù)據(jù),而是指向另一個(gè) 3 nybble 地址的第一個(gè) nybble,而正是這個(gè)第二個(gè)地址用于指向數(shù)據(jù)。
與我們想象的 LDQ 指令相反,假設(shè)地址 $100 處的操作碼指示 CPU 使用間接尋址模式執(zhí)行無(wú)條件 JMP。在這種情況下,CPU 最終會(huì)跳轉(zhuǎn)(設(shè)置 PC)到地址 $971。
索引(又名絕對(duì)索引):此模式與絕對(duì)模式非常相似,不同之處在于它還涉及索引 (X) 寄存器。假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 LDQ 的指令,其目的是使用索引尋址模式將 nybble 數(shù)據(jù)加載到寄存器 Q 中,如下所示:
假設(shè)程序計(jì)數(shù)器 (PC) 從地址 $100 開(kāi)始,CPU 讀取操作碼,實(shí)現(xiàn)此操作碼使用索引模式,并將以下三個(gè)半字節(jié)(本例中為 $426)加載到內(nèi)部寄存器中。然后 CPU 將此內(nèi)部寄存器的內(nèi)容添加到索引 (X) 寄存器的內(nèi)容中,并使用結(jié)果指向內(nèi)存中的數(shù)據(jù) nybble(本例中為 $F),然后將其加載到 Q 寄存器中。我們最終將 PC 指向地址 $104,這是 CPU 期望在程序中找到下一個(gè)操作碼的地方。
與我們想象的 LDQ 指令相反,假設(shè)地址 $100 處的操作碼指示 CPU 使用索引尋址模式執(zhí)行無(wú)條件 JMP。在這種情況下,CPU 最終會(huì)跳轉(zhuǎn)(設(shè)置 PC)到 549 美元的地址。
索引間接:此模式反映了索引和間接模式的一種可能組合。假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 LDQ 的指令,其目的是使用索引間接尋址模式將 nybble 數(shù)據(jù)加載到寄存器 Q 中,如下所示:
假設(shè)程序計(jì)數(shù)器(PC)從地址 $100 開(kāi)始,CPU 讀取操作碼,實(shí)現(xiàn)此操作碼使用索引間接模式,并將以下三個(gè)半字節(jié)(本例中為 $426)加載到內(nèi)部寄存器中。然后 CPU 將此內(nèi)部寄存器的內(nèi)容添加到索引 (X) 寄存器的內(nèi)容中以生成新地址。然而,在這種情況下,新地址并不直接指向數(shù)據(jù),而是指向另一個(gè) 3 nybble 地址的第一個(gè) nybble,而第二個(gè)地址用于指向?qū)⒁虞d到Q 寄存器。
與我們想象的 LDQ 指令相反,假設(shè)地址 $100 處的操作碼指示 CPU 使用索引間接尋址模式執(zhí)行無(wú)條件 JMP。在這種情況下,CPU 最終會(huì)跳轉(zhuǎn)(設(shè)置 PC)到 738 美元的地址。
間接索引:此模式反映了索引模式和間接模式的替代組合。假設(shè)我們有一個(gè)名為 Q 的寄存器和一個(gè)名為 LDQ 的指令,其目的是使用間接索引尋址模式將 nybble 數(shù)據(jù)加載到寄存器 Q 中,如下所示:
假設(shè)程序計(jì)數(shù)器(PC)從地址 $100 開(kāi)始,CPU 讀取操作碼,實(shí)現(xiàn)此操作碼使用間接索引模式,并將以下三個(gè)半字節(jié)(本例中為 $426)加載到內(nèi)部寄存器中。該地址指向另一個(gè) 3 nybble 地址的第一個(gè) nybble,該地址本身被復(fù)制到內(nèi)部寄存器中。然后 CPU 將第二個(gè)內(nèi)部寄存器的內(nèi)容添加到索引 (X) 寄存器的內(nèi)容中,以生成一個(gè)新地址,該地址指向?qū)⒓虞d到 Q 寄存器中的數(shù)據(jù)。
與我們想象的 LDQ 指令相反,假設(shè)地址 $100 處的操作碼指示 CPU 使用間接索引尋址模式執(zhí)行無(wú)條件 JMP。在這種情況下,CPU 最終會(huì)跳轉(zhuǎn)(設(shè)置 PC)到地址 $BD4。
Autoincrement 和 Autodecrement:除了上面討論的基本索引模式之外,一些 CPU 還支持 autoincrement 和 autodecrement 版本,其中索引寄存器在其內(nèi)容添加到臨時(shí)寄存器中的地址后遞增或遞減。
事實(shí)上,由于增量/減量發(fā)生在加法之后,這些模式應(yīng)該更恰當(dāng)?shù)胤Q(chēng)為“后自動(dòng)增量”和“后自動(dòng)減量”。這是因?yàn)橐恍┨幚砥鬟€支持“pre-autoincrement”和“pre-autodecrement”,其中索引寄存器在其內(nèi)容添加到臨時(shí)寄存器中的地址之前遞增或遞減。
另請(qǐng)注意,所有四種自動(dòng)增量和自動(dòng)減量都可以潛在地應(yīng)用于索引間接和間接索引模式。
我的天啊!真的嗎?
我知道當(dāng)我們考慮上面討論的所有可能的尋址模式時(shí),有很多事情需要考慮。不要恐慌!HRRG 僅支持這些模式的一個(gè)子集,以及最簡(jiǎn)單的模式——即隱含、立即、絕對(duì)和索引模式。
另一方面,我們構(gòu)建 HRRG 的方式意味著它有時(shí)會(huì)在同一指令中使用多種模式。我能說(shuō)什么?這是一個(gè)有趣的舊世界。
在我的下一篇專(zhuān)欄中,我們將開(kāi)始匯總我們?cè)诩拇嫫骱椭噶罴瘜?zhuān)欄、指令集權(quán)衡專(zhuān)欄以及本專(zhuān)欄中介紹的所有內(nèi)容,以描述 HRRG 的匯編語(yǔ)言和匯編程序?qū)嵱贸绦颉T诖酥埃乙蝗缂韧貧g迎您提出意見(jiàn)、問(wèn)題和建議。
審核編輯 黃昊宇
評(píng)論
查看更多