type
status
date
slug
summary
tags
category
icon
password
大綱:
notion image
notion image
 
從大綱可以很明顯的感受到,這一章主要是在講「程式語言」,所以提到了硬體、數字的表示法、邏輯操作、 MIPS 等等的,第三章才會提到更底層的事情,另外 Signed bit 我們這邊不提,因為以前 Digital System 弄過了
 

MIPS 的小知識

原則一:簡單有利於規律性

MIPS 實際上的相加要怎麼達成
notion image
MIPS 的指令表一部分
notion image
 
可以發現到, MIPS 要將一坨數字相加,不會把全部寫在一起,因為這樣做的話就要動用到許多的設定,去符合所有的格式。只用單一一種格式,可以讓整個指令集更加簡潔
 
 
  • Design Principle 1: Simplicity favors regularity.

原則二:小就是快

在 MIPS 裡面, Register 最多只能用到 32 位元,不然會造成 clock cycle 變長
書也有提到,越小越快不是絕對的,是一個大方向的原則
另外,在 MIPS 裡面的 variable 都會使用 $ 來表示
notion image
另外,記憶體大概是長這個樣子
notion image
notion image

endianess(字節序)

指定 array 的 endianess 有分成兩種,假設今天我們要儲存一個16位數字 0x1234
  • 大端 (Big-endian)
    • 34
      12
  • 小端(Little-endian)
12
34
我們的 MIPS 是小端模式
 

加載常數

有時候我們會使用一些常見的常數(例如上面 Array 的例子,我們常常要使用 4 這個數字),那我們就會先加載到記憶體,有以下兩種方法
  1. 先放到 $t0
  1. 使用 addi
這個做主要是希望把 common case 做到能快就快
 

MIPS 真實的長相

notion image
notion image
  • 每一個格子代表 field
 
以上我們稱為 instruction format,而實際的數字指令我們稱為 Machine language,取名原因很好想像,沒有意外只有機器看得懂
 
更多的 MIPS Fields 的說明
反正等到我想要深入了解再來看
notion image
 
 

原則三:好的設計需要好的妥協

看到剛剛我們的指令設計,我們會發現,我們沒有辦法做超過 的加法,因此有了這個法則
所以我們沒有辦法使用這個指令,同時還是要保持指令的位元長度,因此解決方案是:
notion image
我們設計了另一種 formate,長得像上面這樣
notion image
更多 MIPS 的 Example
notion image
 

表達一些 bit operation

notion image
就邏輯
 

表達 true/false (流程控制)

課本有點囉嗦,我們看看 GPT 同學給我們的簡介

GPT同學的說明

在 MIPS 中實現 if/else 結構通常涉及以下幾個步驟:
  1. 條件評估:首先,需要計算條件表達式的值。這通常是通過比較指令來完成的,如 beq(branch if equal)、bne(branch if not equal)、slt(set on less than)等。
  1. 分支指令:一旦條件表達式被評估,接下來會使用分支指令來決定執行流程的方向。如果條件為真,則執行 if 塊中的代碼,否則跳轉到 else 塊。
  1. 代碼塊執行:根據條件評估的結果,處理器會執行 if 塊或 else 塊中的代碼。
  1. 流程合並:無論是 if 塊還是 else 塊執行完畢後,控制流需要合並到這兩塊之後的代碼。
以下是一個簡單的 if/else 結構在 MIPS 組合語言中的實現示例:
在這個例子中,bne 指令用於評估條件($t0 是否不等於 $t1)。如果條件為真(即,值不相等),執行跳轉到 else_block 標籤的代碼。否則,處理器將繼續執行 if_block 中的代碼。 j end_if_else 指令確保在執行完 if 塊之後跳過 else 塊,進而合並流程。

Supporting Procedures in Computer Hardware (活用記憶體, 2.8)

為了讓程式更快,在硬體的設計上就會加上一些小巧思,例如在記憶體上就有 default 的設定
notion image
 
另外,為了更加活用這兩個記憶體,我們有 jal(jump-and-link)還有 jr(jump register)這兩個指令,以下是 GPT 同學帶給我們的解釋:
  1. jal ProcedureAddress (Jump and Link):
      • 這條指令用於調用程序(如函數或過程)。
      • 當執行 jal 指令時,它會跳轉到指定的地址(在這裡是 ProcedureAddress)來執行該程序。
      • 同時,“link”部分的作用是將調用點後的下一條指令的地址保存到 $ra(返回地址寄存器,也就是寄存器31)中。這樣做的目的是為了讓被調用的程序知道在完成執行後需要返回到哪裡。
  1. 返回地址 (Return Address):
      • 存儲在 $ra 寄存器中的地址稱為“返回地址”。這對於程序控制流程來說非常重要,因為同一程序可能會從多個不同的地方被調用。
      • 當一個程序或函數需要返回到它被調用的地方時,就會使用這個返回地址。
  1. jr (Jump Register):
      • 這條指令用於從程序或函數中返回。
      • jr 執行一個無條件跳轉到某個寄存器(通常是 $ra)中指定的地址。
      • 這意味著當程序完成其任務後,它會使用 jr 指令跳轉回到 $ra 中存儲的地址,也就是調用它的地方。
 
那假設我們需要用到更多的記憶體,我們就會使用到 stack 的結構
notion image
以及 stack 在動作的時候想像起來是什麼樣子
notion image
另外,下表是 MIPS 的記憶體 table
notion image
notion image
 
關於 stack 要新增資料時的行為
notion image
關於 heap 要新增資料時的行為
notion image
 
 

人類文字 遇上 記憶體

我們都知道,在電腦裡面要使用 ASCII Code 之類的東西電腦才看得懂,那我們要怎麼去操作他們?
 
在 MIPS 中,當我們想要讀取或者寫入文字(人類的)時候,會用以下的指令
lb(load byte) 還有 sb(saved byte)
 

當遇到了需要 32-bit 大小的常數

平常的指令結構最大就是 16-bit 的指令 + 16-bit 的常數
但假如遇到了需要 32-bit 的情境,我們應該要做一些甚麼呢?
 
詳細的閱讀過程:
notion image
  1. 閱讀 upper 16-bit (使用 lui <要暫時記住的地方>, <前 16-bit 數字>)
  1. 閱讀後 16-bit 的數字 (ori <要暫時寄住的地方>, <前 16-bit 儲存的地方>, <後 16-bit>)
 

Jump

就跳到指定的記憶體位置:
notion image
 

Branches

指的是在類似 for 迴圈或者是 if/else 的流程控制
另外,書中寫到
Program counter = Register + Branch address
也很好想像,因為例如 for 迴圈的流程控制,就是把那個 i 記在記憶體,經過 if/else 判斷之後 jump 到特定的位置,而這種作法又稱為 PC-relative addressing (PC 是 program counter)
 

MIPS Addressing Mode Summary

 
notion image
我們這邊參考 GPT 同學給出來的內容:
當然,以下是對MIPS架構中這五種定址模式的解釋,以及每種模式的一個示例:
  1. 立即定址 (Immediate Addressing)
      • 解釋:操作數直接包含在指令中作為一個常數。這種模式常用於簡單的數學運算或賦值操作。
      • 範例addi $t0, $t1, 10,這裡 10 是立即數,將 $t1 寄存器的值與 10 相加,結果存儲在 $t0 寄存器中。
  1. 寄存器定址 (Register Addressing)
      • 解釋:操作數是處於寄存器中的值。這是最基本的定址方式,常用於寄存器間的數據傳輸和運算。
      • 範例add $t0, $t1, $t2,這裡 $t1$t2 是寄存器,將它們的值相加,結果存儲在 $t0 寄存器中。
  1. 基址或偏移定址 (Base or Displacement Addressing)
      • 解釋:操作數位於記憶體中,其地址是一個寄存器的值(基址)與指令中的一個常數(偏移量)相加得到的。
      • 範例lw $t0, 8($t1),這裡從 $t1 寄存器加上偏移量 8 計算出的地址讀取數據,然後將數據加載到 $t0 寄存器中。
  1. PC相對定址 (PC-relative Addressing)
      • 解釋:用於分支指令,其中分支的目標地址是程序計數器(PC)當前值加上指令中的一個常數(偏移量)。
      • 範例beq $t0, $t1, label,這裡如果 $t0$t1 的值相等,程序會跳轉到 label 指定的地址,該地址是當前 PC 值加上到 label 的偏移量。
  1. 偽直接定址 (Pseudodirect Addressing)
      • 解釋:用於跳轉指令,其中跳轉的目標地址是指令中的26位地址與程序計數器高位的組合。
      • 範例j 0x00400000,這裡跳轉到地址 0x00400000。該地址是指令中的26位與 PC 的高位結合而形成的。
每種定址模式都有其特定的應用場景,是MIPS指令集靈活處理各種操作和控制流程的基礎。
 

進階主題

Parallelism and Instructions: Synchronization

這個章節在討論「同步」這件事情,因為有時候十個工作,彼此之間是相關的,假如沒處理好就會打架,所以要討論實務上在記憶體要怎麼處理這件事情
 
當然,我可以為您介紹 MIPS 指令集中的 load linked(LL)和 store conditional(SC)指令,這兩種指令通常一起使用,用於實現在多處理器環境中的原子操作。
  1. Load Linked (LL)
      • 功能load linked 指令從指定的記憶體地址讀取一個值並將其加載到寄存器中。它的特殊之處在於它會標記所讀取的記憶體位置,以便於後續的 store conditional 指令使用。
      • 用途:這個指令主要用於在執行條件存儲之前檢查記憶體位置的狀態。如果在 load linked 和相應的 store conditional 執行之間,該記憶體位置被更改,則 store conditional 操作將失敗。
  1. Store Conditional (SC)
      • 功能store conditional 指令嘗試將一個值從寄存器存儲到之前由 load linked 指令讀取的記憶體地址。這個存儲操作只有在自上一次 load linked 執行以來,目標記憶體地址沒有被修改的情況下才會成功。
      • 用途store conditional 用於實現互斥和其他同步機制。它允許處理器確定自從最後一次讀取記憶體位置以來,該位置是否已被其他處理器更改。
LLSC 指令的組合使用允許在多核或多處理器系統中安全地進行記憶體操作,這對於防止競爭條件和數據競爭至關重要。例如,它們常用於實現鎖,其中一個處理器通過 LL 讀取鎖的狀態,並嘗試通過 SC 更改它。如果在這個過程中其他處理器已經修改了鎖的狀態,SC 將會失敗,這樣就保證了原子性操作。
 

具體要怎麼翻譯和執行 Program — 以 C 為例

notion image

Compiler

負責將 「高階語言」(high level language) 轉為 「組合語言」(assembly language)
💡
高階語言: 因為可以用短短幾行,把一大坨組合語言搞定,生產效率比較 high
💡
組合語言: 跟 binary machine code 有一對一關係的語言,例如 MIPS

Assembler (匯編器)

最主要的功能就是把組合語言換成機器碼,組合語言的 IDE 的感覺
不過這個 Assembler 是依照平台不同變的
 

Linker

假如有人更改了一行 C 語言,整個幾千行的 C 語言都要一起重新編譯,那非常的浪費效能
因此,我們會希望它一行一行獨立分開編譯,但也會因此出現彼此不知道指的是誰,因此,Linker 的角色就是把獨立的一行一行的程式在機械碼的情況下接起來。
 
 
到剛剛的階段,整個 Machine code 是準備可以跑的狀態

Loader

這是 Unix 的構造,它會執行以下的步驟,準備讀到記憶體,然後啟動它
  1. 讀取可執行檔案的 head,以確定文本和數據段的大小。
  1. 創建足夠大的空間來容納文本和數據。
  1. 將指令和數據從可執行檔案複製到記憶體中。
  1. 將參數(如果有的話)複製到主程序的 stack 上。
  1. 初始化機器寄存器,並將堆棧指針設置為第一個空閒位置。
  1. 跳轉到一個啟動例程,該例程將參數複製到參數寄存器中並調用程序的主例程。當主例程返回時,啟動例程通過退出系統調用結束程序。
 

Dynamically Linked Libraries

我的理解是,跟把 function 寫在程式碼裡面不一樣,我們是用外部的 function ,好處是可以省儲存空間,以及可以再利用,缺點是第一次 load 的時候會慢一點,而 Unix 和 Windows 系統都有大量用到這個東西。
 
我的理解是跟 Python 的 import 差不多
 
 
notion image
通過懶惰程序鏈接實現的動態鏈接庫。 (a) 首次呼叫DLL函數時的步驟。 (b) 在後續呼叫中,尋找函數、重新映射它和鏈接它的步驟被跳過。如我們將在第5章中看到,操作系統可以通過使用虛擬內存管理來重新映射,從而避免複製所需的函數。
 

Java 的語言方法

notion image
一個Java程序首先被編譯成Java字節碼的二進制版本,所有地址都由編譯器定義。
現在,這個Java程序已經準備好在解釋器上運行,該解釋器被稱為Java虛擬機(JVM)。
JVM在程序運行時鏈接到Java庫中所需的方法。
為了實現更高的性能,JVM可以調用即時編譯器(JIT),它選擇性地將方法編譯成運行它的機器上的本機機器語言。
 
總之跟 C 不一樣, Java 的程式,只要有 JVM 都可以到處執行,它有一些特別的機制:
  • Java 的程式碼會被 Compiler 轉換為 .class 文件
  • JVM 負責及時編譯這些 .class 文件
  • JIT 負責讓編譯速度變得更快
  • JVM 同時還會處理記憶體分配
 
 

Arrays versus Pointers

很好想像的事:假如要儲存一串東西,並且要擁有一個好的彈性,那 pointer 肯定比 array 還要方便,缺點是新手比較難以想像和入門,因此這個章節在用 MIPS 跟你解釋
 
白算盤 - ch1Notion API