MC68000

**摩托羅拉 68000 型中央處理器**，或稱 **MC68000**，**680x0**，**m68000**，**m68k**，**68k；**是由美國摩托羅拉公司（Motorola）的半導體部門（現已獨立成為飛思卡爾公司（Freescale））出品的一款 16/32 位 CISC（複雜指令集）微處理器。作為 M68K 處理器系列的第一個成員，MC68000 於 1979 年投放市場。由於內部使用 32 位匯流排和暫存器，它在軟體層（指令集）上基本與隨後的純 32 位產品保持兼容。

## 歷史

摩托羅拉公司於 1976 年起動 MACSS 計畫（Motorola Advanced Computer System on Silicon，摩托羅拉矽晶高級電腦系統），打算開發一款與以前產品完全不相容的全新微處理器。根據計劃，新 CPU 應該是對當時摩托羅拉主流 8 位 CPU6800 的一個高階互補產品，而不會考慮兩者間的兼容性。不過，當 68000 設計出來後，它還是被保留了一個可相容 6800 外部的匯流排協定模式，並且專門有 8 位匯流排的產品被生產出來。當然，設計人員還是更在意於其向後兼容性，這為 68000 確立在 32 位 CPU 領域的領先優勢奠定了基礎。比如，68000 使用 32 位暫存器和內部匯流排，僅管其本身的結構很少直接操作長字。小型電腦諸如 PDP-11 和 VAX— 二者採用了類似的微編碼 — 對 68000 的設計有深遠的影響。

在 20 世紀 70 年代中期，8 位微處理器製造商紛紛競爭導入下一代 16 位 CPU。國家半導體在 1973 年到 1975 年間首先開發出了 IMP-16 和 PACE，但其速度並不理想。英特爾公司於 1977 年推出 8086，迅速受到歡迎。此時，為確保競爭上的領先，摩托羅拉意識到其 MACSS 計劃必需跳過 16 位系統，而直接推出 16/32 位混合型 CPU。到 1979 年，摩托羅拉 68000，即 MC68000，才告姍姍來遲。由於比 8086 晚兩年，其電晶體數目更多，並因其易用性得到了好評。

最初的 MC68000 使用 3.5 微米 HMOS 技術（即高性能 N 通道金屬氧化物半導體，CMOS 的前身）製造。1979 年發布了工程樣品，次年產品型面世，速度有 4、6、8、10 兆赫茲多種。最快的 16.67MHz 版本到 80 年代末才面市。

MC68000 在早期得到了很多高階產品的青睞。在昇陽公司的 Sun workstation 等多種 Unix 工作站中，MC68000 一度占領導地位。市場領先的其他一些電腦，包括 Amiga（阿米加）、Atari ST（雅達利 ST）、Apple Lisa （蘋果 Lisa）和 Macintosh（麥金塔），以及第一代雷射印表機，如蘋果公司的 LaserWriter，都使用 MC68000。1982 年，摩托羅拉進一步更新了 MC68000 的指令集以支持虛擬記憶體，並使其能夠符合由 Popek 和 Goldberg 於 1974 年提出的虛擬化標准。

為支援低成本系統和使用較少記憶體的應用，摩托羅拉於 1982 年還推出了面向 8 位外部匯流排的 MC68008。1982 年以後，摩托羅拉開始把更多的注意力投向 68020 和 88000。

### 其他製造商

由日立公司設計，1985 年兩公司聯合推出了使用 CMOS 技術的 68HC000。68HC000 的速度有 8 到 20MHz 多個版本。儘管除了使用 CMOS 電路，68HC000 與基於 HMOS 的 MC68000 完全一致，但正是因此其能耗得到大幅下降。MC68000 在 25 攝氏度環境下能耗大約為 1.35 瓦，而 8MHz 的 68HC000 則為 0.13 瓦，較高頻率的版本則能耗也相對提高（HMOS 技術則不同：其在 CPU 空閒時仍會耗電，所以功耗與頻率基本無關）。後於 1990 年摩托羅拉又推出了 MC68008 的 CMOS 版本，並將其改進為可相容 8/16 位兩種匯流排模式。

其他 HMOS 版 68000 的製造商包括：Mostek、Rockwell（洛克維爾）、Signetics、Thomson（湯姆遜）和東芝。東芝也生產 CMOS 版 68000（TMP68HC000）。

## 應用

68000 首先被應用於許多高端產品中，如多用戶微型計算機 WICAT 150 及阿爾法微系統的一些早期計算機等。單用戶工作站如惠普公司的 HP 9000/200、昇陽電腦的 Sun-1；圖形終端如 DEC 的 VAXstation 100 和 Silicon Graphics 的 IRIS 1000 均使用 68000。許多 Unix 系統也開始使用 68K 系列 CPU。

在 80 年代中期，68000 又成為首先應用於個人 / 家庭計算機的 CPU。先後有蘋果的 Apple Lisa 與麥金塔（Macintosh 128K）、Amiga 的海軍准將（Commodore）、雅達利 ST 及夏普的 Sharp X68000 採用 68000。

68000 最主要的成功在於控制器領域。早在 1981 年 Imagen 公司就把 68000 用作其雷射印表機 Imprint-10 外部控制器的 CPU。惠普於 1984 年發布的第一款 LaserJet 印表機亦使用一片 8MHz 的 68000 作為內置控制器。類似的基於 68000 的整合控制器也被廣泛用在其他多款印表機中。到 90 年代，68000 仍在許多低端印表機中被使用。

除了傳統商業和家用計算應用，68000 在工業控制系統中也取得了巨大成功。Allen-Bradley、德州儀器及西門子公司生產的可程式邏輯控制器（PLC）即使用 68000。一般來說，這類工業系統的用戶更重視產品的可用性，而不是向家庭用戶一樣過於在意其是否過時。所以，仍有許多使用 68000 的系統，在被安裝二十多年後，在生產一線提供著持續而可靠的服務。

隨著技術的進步，68000 在計算機單機市場漸被淘汰，但其應用仍活躍於消費和嵌入式領域。遊戲機製造商使用 68000 作為許多街機和家用遊戲機的處理器。雅達利在 1983 年推出的 Food Fight 便是使用 68000 的代表街機遊戲。世嘉的 System 16、卡普空的 CPS-1 和 CPS-2 以及 SNK 的 Neo Geo 也都使用 68000。

在 80 年代末到 90 年代初，一些遊戲機廠商使用 68000 作為家用遊戲平台的中央處理器。這包括世嘉的 Mega Drive（MD）和 Neo Geo 家用版和 Super A'Can。到了 90 年代，儘管街機遊戲開始使用更加強大的 CPU，68000 還在世嘉的 32 位 CPU 遊戲機 Sega Saturn 中用作聲音控制器。

基於 68000 的 683XX 系列微控制器則廣泛應用於許多應用領域中，包括網絡和電話設備、電視機機頂盒、實驗室與醫療設備等。思科、3Com 等公司曾在他們生產的通訊設備中使用 MC68302 及其衍生產品。Palm 公司曾使用 DragonBall 系列 CPU（68K 系列的後續）作為其 PDA 的處理器，直到基於 RISC 的 ARM 處理器開始統治 PDA 市場。此外，AlphaSmart 公司在其可攜式文字處理器中使用 DragonBall。

德州儀器在一些高端圖形計算器，如 TI-89 和 TI-92 中，使用 68000。這些設備早期使用以 68EC000 為內核的專門化微控制器，後來則改用封裝好的 MC68SEC000。

### 作為微控制器內核

在被純 32 位 CPU 取代後，68000 開始被用作許多微控制器的內核。1989 年，摩托羅拉發布了 MC68302 通訊處理器。兩年後又推出獨立處理器晶片 MC68EC000。1996 年的 MC68SEC000 便使用了這款晶片。

1996 年摩托羅拉停產 HMOS MC68000 和 MC68008，但其分離出來的半導體部門，飛思卡爾公司，仍在生產 MC68HC000 等 68K 家族系列產品。68000 架構的後代，680x0、CPU32 以及 Coldfire 系列，也仍在生產。

## 架構

### 地址匯流排

68000 地址匯流排為 24 位，故支持 16MB 最大物理內存。在使用 32 位長字對地址進行存儲和計算時，高位的一個字節會被自動忽略。這種設計使得其具備相當的向前兼容性，可以直接運行為後續的純 32 位 CPU 編寫的軟體。也因此，根據現今的定義，68000 應稱得上是一款 32 位 CPU。摩托羅拉使用 32 位內部匯流排的目的在於希望能夠在 68000 上編寫可以被將來的後續產品直接使用的軟體，而相關指令不必作位數上的調整。

然而，程式工程師還是有可能編寫出無法與後續產品兼容的軟體。倘若這種 24 位軟體丟棄高位字節，或將該字節用作尋址以外的目的，它就有可能在 32 位 68K 系列 CPU 上運行失敗。這就是說，對於希望支持向前兼容的軟體，必須始終使用 32 位長字尋址，並且將最高位字節置零。

### 內部暫存器

68000 包含 8 個 32 位通用暫存器（D0-D7），及 8 個 32 位地址暫存器（A0-A7）。最後一個地址暫存器，即 A7，也作為標準棧指針使用，在編程中可以使用 SP 作為同義詞。這組暫存器在規模上恰到好處：既可以對中斷快速反應（只有十多個暫存器要保存），也有足夠的暫存器來進行快速計算。

儘管兩種暫存器並存有時會比較麻煩，但在實踐中並非難於掌握。據稱，這還使得 CPU 的設計者們可以通過對地址暫存器組使用輔助計算單元，從而實現較高程度的並行機制。

存儲內容高位字節在前（Big Endian 模式），與 x86 相反。

### 狀態暫存器

68000 比較、算術和邏輯操作會在狀態暫存器 SR 的低端字節（又稱 CCR）中設置一些標誌位，以供之後的條件跳轉使用。這些標誌位是：得零（Z）、進位（C）、溢出（V）、擴展（X）、負數（N）。儘管許多時候值是相同的，X 與 C 依然是兩個不同的標誌位。這就允許算術、邏輯和移位操作的多餘位與邏輯控制 / 連接造成的進位區別開。

### 指令集

68000 的指令集基本上是正交的。大部分指令被劃分成操作和地址模式兩部分，並且大部分地址模式都對幾乎全部指令可用。這種近似正交性在編程人員當中毀譽參半。

編程者會清楚地發現，他 / 她所書寫的指令可能被彙編成幾種不同的二進位操作碼。這實際上是一種不錯的妥協：一方面，在便利性上與純粹的正交指令系統相仿；一方面，CPU 設計者可以有更多的自由來設計操作碼錶。

對於一台 16 位時代的機器而言，由 56 條指令構成的最小指令集仍顯巨大。此外，許多指令和尋址模式會在指令後邊加入地址 / 尋址模式碼。

許多設計者確信 MC68000 體系結構應基於成本考量使用較精簡的指令碼，特別是使用編譯器自動生成時。這種認識為對其設計上的成功加分不少，並且使之成為一種經久不衰的體系結構。這一信條持續地保證了整個系列指令集的設計優勢，直到 ARM 體系結構引入同樣精簡的 Thumb 指令集。

### 特權級

68K 系列 CPU 包含兩個特權級。超級用戶（supervisor）模式和用戶（user）模式。後者相比於前者只是禁用了中斷級控制。中斷總會使 CPU 進入超級用戶態。超級用戶標誌位存儲於狀態暫存器 SR 中，並對用戶可見。

超級用戶態下會有一個分離的棧指針用於中斷處理。

### 中斷

68000 可以識別 7 級中斷，從級別 1 到級別 7。7 級中斷嚴格按優先級排列，一個高級中斷總是能嵌套於一個低級中斷。可以使用專門的特權指令在 SR 內設置最小中斷級別，從而屏蔽所有小於此級別的中斷。但如果設置為 0，表示不接受中斷。級別 7 不可被屏蔽，即 NMI。級別 1 總是可以被高級中斷打斷。

硬體中斷源將中斷信號以編碼方式通過三條輸入線傳送給 CPU。一般會使用專門的中斷控制器來匯總各外部設備，並將中斷信號按級編碼與 CPU 硬連。中斷控制器可以使用簡單如 74LS148 優先級編碼器，複雜如 MC68901 多功能外設（支持可程式中斷控制、通用異步收發裝置、定時器及並行輸入輸出等）等各種電路模塊。

在內存低 1K 位置存儲中斷向量表，共支持 256 條中斷向量。部分中斷向量有特殊用途：向量 1 為初始棧地址；向量 2 為初始代碼地址；向量 3 到 15 用於錯誤報告，包括匯流排錯誤、尋址錯誤、非法指令、除零異常、優先權違反等。從向量 24 起處理真正的中斷，包括偽中斷、針對級別 1 到級別 7 的默認處理向量，多達 15 個自陷向量，以及用戶定義向量。

由於必須在重啟時保證向量 1 和 2 的內容有效，所以 68000 系統通常包含在地址底部使用非易失性存儲器（如 ROM）來存儲一些例程向量和啟動代碼。但是，一個通用計算機的作業系統會期望在運行時改變向量內容。解決辦法是將 ROM 內的向量指向 RAM 的分支表，或使用早期在 8 位 CPU 中廣泛使用的換頁技術（Bank switching）。

由於包含一條非特權指令 MOVE from SR，允許一般用戶只讀地訪問某些特權狀態，68000 並未完全滿足波佩克與戈德堡虛擬化需求。該需求指出了為構建某一 CPU 之等價虛擬機而對 CPU 提出的若干要求。

MC68000 對虛擬內存缺乏方便的支持。一款支持虛擬內存的 CPU 應能在內存訪問失敗後自陷並恢復。不過，68000 確實提供了一個匯流排錯誤異常來使 CPU 自陷，儘管還不能保存足夠的狀態信息以便於異常處理之後的恢復。為此，一些 Unix 工作站通過使用兩塊 68000 來解決虛擬內存問題。兩塊 CPU 的運行時鐘存在相位差。當第一塊遇到尋址異常後，特殊的硬體會設法向第二塊發出中斷，以防止其也訪問錯誤地址。中斷例程在第二塊 CPU 上處理完內存換頁後，會按之前的狀態重啟第一塊 CPU，從而再次使兩 CPU 同步。

不過，以上這些問題在 MC68010 被徹底解決。在 MC68010 中，匯流排異常和地址錯誤均會使大量狀態信息壓入系統棧，以便於之後的恢復。MOVE from SR 也被修正為特權指令。原本用於訪問 SR 低端字節的代碼可由新指令 MOVE from CCR 取代。

## 指令集細節

### 標準尋址模式

68000 提供多種尋址模式，並統稱為有效尋址（EA）。在 CPU 參考手冊中，經常會有諸如 MOVE <ea>,<ea> 這樣的表記方式。這表示在目的操作數和源操作數上可分別使用一種（但通常不是全部）有效尋址。

* 暫存器直接尋址
  * 數據暫存器直接尋址，如 D0。
  * 地址暫存器直接尋址，如 A0。通常不使用 A7。
* 暫存器間接尋址
  * 簡單間址，如 (A0)。先取得 A0 所存儲的地址，再在內存中該地址處取出數據。
  * 自增間址，如 (A0)+。只能用於源操作數域。先取得 A0 所存儲的地址，再在內存中該地址處取出數據，然後 A0 的內容（地址）自增一定長度（因指令後綴而定）。這個操作其實相當於 x86 中的出棧指令 POP（注意棧的方向和內存方向相反）。在這裡，A0 被用作一個用戶自定義的棧指針，與系統使用的 A7/A7' 不同。
  * 自減間址，如 -(A0)。可用於源或目的操作數域。先將 A0 的內容（地址）自減一定長度（因指令後綴而定）；然後取得 A0 所存儲的地址，再在內存中該地址處存入數據，然後這個操作其實相當於 x86 中的入棧指令 PUSH。
  * 偏移間址，如 2AFF (A0)。前邊的偏移量為 16 位。實際取得的地址為 (A0)+2AFF。
  * 加索引的偏移間址，如 E3 (A0, D0)。前邊的偏移量為 8 位。實際取得的地址為 (A0)+(D0)+E3。
* 程序計數器位移
  * 長位移，如 2AFF (PC)。前邊的偏移量為 16 位。PC 變為 (PC)+2AFF。
  * 短位移，如 E3 (PC, D0)。前邊的偏移量為 8 位。PC 變為 (PC)+(D0)+E3。
* 絕對內存尋址
  * 直接使用內存地址值，如 $4000，表示目標地址在內存地址為 4000 處。注意 $ 表示後邊所跟數字為 16 進位，% 表示 2 進位。
  * 在實際編程中，可以使用代碼中的標號來充當內存地址值。彙編器會自動翻譯為數字地址。
  * 注意不應與立即數混淆。使用立即數時應在值前再加一個 #。$4000 表示 16 進位地址 4000；#$4000 表示 16 進位立即數 4000；4000 表示 10 進位地址 4000；#4000 表示 10 進位立即數 4000。

### 指令後綴

大部分指令都有表示操作長度的後綴：.B、.W 或.L，分別表示這一操作在字節（8 比特）上、字（16 比特）上或長字（32 比特）上進行。運算的過程和結果都會受到後綴的影響。在運算過程中，只有屬於操作長度範圍的部分才會參與運算。比方說，執行 MOVE.B D2,D1 會把 D1 的最低一字節複製到 D2 的最低一字節處，而兩者的其餘字節均不受影響。對於 CCR，各標誌位的值會由操作長度的最高有效位決定。如果某次 ADD.B 使得結果的第 7Bit 位為 1，則 CCR 的 N 位會置 1；如果某次 ADD.L 使得結果的第 7Bit 位為 1，並且第 15Bit 不為 1，則 CCR 的 N 位不會置 1。在後一種情況下，CCR 的 N 位只受第 15Bit，即一個字的最高有效位影響。另一個例子是在自增 / 自減尋址中，自增 / 自減的長度因操作後綴而異。如果操作為 MOVE.B，則自增 / 自減 1；W 為 2；L 為 4。

### 常用指令

大部分 68000 的指令都是二元的。目的操作數在前，源操作數在後。

* 移動指令
  * MOVE：標準移動指令。另有一些其它移動指令供選擇：MOVEA（移動到地址暫存器 An，不會影響 CCR，但後綴不可為.B）、MOVEQ（移動一個 8 位數到目標暫存器，因為可將數值直接寫入指令故稱快速移動）、MOVEM（移動暫存器組到指定堆疊，多用於中斷 / 子程序處理的第一步）等。
  * LEA：移動一個地址值到目標暫存器。`LEA $2000,A0` 表示將 A0 的值設為 16 進位的 2000。應注意此時不加立即數標誌 #。
  * LINK/ULNK：建立 / 取消堆疊幀。這個指令用於翻譯高級語言的函數調用。`LINK An, #-x` 可將 An 作為函數棧指針，並 SP 所指向的用戶棧內取得 x 大小的空間存儲局部變量，之後 SP 仍將指向棧頂，而 An 指向棧頂 + x 的位置。x 的值在編譯時由編譯器自動算出。
* 算數指令：ADD（加）、SUB（減）、MULU /MULS（無 / 有符號乘）、DIVU/DIVS（無 / 有符號除）、NEG （取補）、及 CMP（類似於減但只會影響標誌位而不改變操作數）。算數指令也多成組提供。對於 ADD，還有 ADDA（加地址，不會影響標誌位）、ADDI（加立即數）、ADDQ（快速加，加數不大於 8 以便於直接放在操作碼中，比 x86 的 INC 指令書寫麻煩但功能更強）、ADDX（影響進位符，用於大數運算）等。
* BCD 碼算數指令：ABCD（加）和 SBCD（減）。
* 移位指令
  * 邏輯移位（移位後用 0 補充）：LSL（左移）、LSR（右移）
  * 算數移位（移位後用原來最高 / 最低有效位補充）：ASL（左移）、ASR（右移）
  * 循環移位（移位後用所移動位補充）：ROL（左移）、ROR（右移）
  * 循環擴展移位（移位至 CCR 的 X 位，同時用之前的 X 位補充）：ROXL（左移）、ROXR（右移）
* 位操作指令：BSET（置 1）、BCLR （清 0）和 BTST （如果測試位在改變前為 0 則將 CCR 的 Z 設為 1）。
* 多任務處理：TAS（測試並置位）。這個指令用於實現信號燈等同步機制。
* 流程控制：JMP（無責任跳轉）、 JSR （跳轉至子例程）、BSR （按相對地址跳轉至子例程，多用於跳轉至用戶定義例程）、RTS （從子例程返回）、RTE （從異常 / 中斷中返回）、 TRAP（自陷，即軟中斷）、CHK（檢查地址是否越界，如是則觸發異常）。
  * JMP 指令只是純粹跳轉，不會將下一條指令地址壓棧；JSR 和 BSR 會將下一條指令壓棧。
  * RTS 將棧指針指向內容彈出給 PC。
* 條件測試並跳轉：Bcc 系列指令。cc 定義了所測試的條件位。常用的如：
  * BNE：不等於時跳轉。
  * BEQ：等於時跳轉。
  * BRA：無條件跳轉。

## 參考文獻