6502 彙編
本書是 6502 組合語言的指南。本書將教授 8 位 6502 處理器的不同記憶體定址模式和指令。
如果你想進行 Atari 2600/8 位系列/5200/7800 程式設計、Commodore PET/VIC/64/128 程式設計、Acorn 8 位程式設計、Apple I/II 程式設計、NES 程式設計 或 超級任天堂程式設計,你可能需要學習 6502 組合語言程式設計。
彙編器的語法會有所不同 - 本書將始終使用以下語法
| 語法 | 進位制 | 示例 |
|---|---|---|
%00001111 |
二進位制 | LDA #%0001
|
$FA |
十六進位制 | LDA #$0E
|
123 |
十進位制 | LDA #100
|
| 暫存器 | 大小(位) | 用途 |
|---|---|---|
| 累加器 (A) | 8 | 用於對資料進行計算。 大多數指令可以直接在累加器上操作,而不是花費 CPU 週期訪問記憶體。 |
| X 暫存器 (X) | 8 | 用作某些定址模式中的索引。 |
| Y 暫存器 (Y) | 8 | 用作某些定址模式中的索引。 |
| 程式計數器 (PC) | 16 | 指向要執行的下一條指令的地址。 |
| 堆疊指標 (S) | 8 | 儲存堆疊索引,下一個堆疊元素將寫入該索引。 該位置的地址為 $0100 + SP。SP 最初設定為 $FD。TSX 和 TXS 是唯一允許你直接修改 S 的指令。 |
| 狀態 (P) | 8 | 每一位表示一個狀態標誌。 標誌指示 CPU 的狀態,或有關先前指令結果的資訊。PHP 和 PLP 可以從堆疊中儲存/恢復 P。各種指令可以直接設定或清除 P 中的位:SEC、CLC、SEI、CLI、SED、CLD、CLV。 |
| 位 | 符號 | 名稱 | 描述 |
|---|---|---|---|
| 7 | N | 負數 |
比較: 如果暫存器的值小於輸入值,則設定。 |
| 6 | V | 溢位 |
算術運算: 如果在加法或減法期間發生有符號溢位,即結果的符號與輸入和累加器的符號不同,則設定。
其他: 原始 6502 具有一個名為 "SO" (Set Overflow) 的外部引腳,硬體可以使用它來設定 V 標誌。其目的是比 IRQ 更快地響應硬體事件。大多數常見的 6502 相容平臺沒有使用此功能,或者不使用它。 |
| 5 | - | (未用) | 始終設定 |
| 4 | B[1] | 斷點 | 如果 BRK 指令觸發中斷請求,則設定 |
| 3 | D | 十進位制 | 十進位制模式[2]: 算術指令將把輸入和輸出視為 "二進位制編碼十進位制" (BCD) 數字。
|
| 2 | I | 中斷停用 | 在設定時停用 IRQ 中斷。NMI 和 RESET 不受影響。 |
| 1 | Z | 零 |
比較: 如果暫存器的值等於輸入值,則設定
否則: 如果結果為零,則設定。 注意: 比較 (CMP、CPX、CPY) 指令透過減法來工作,但不保留結果。因此,如果值為 0,則 Z 將被設定,因此這就是為什麼 BEQ 測試 Z 標誌,以及為什麼在 BEQ 之前不必 CMP #0。 |
| 0 | C | 進位 | 進位/借位標誌用於數學和旋轉操作 算術運算: 如果在加法或減法期間發生無符號溢位,即結果小於初始值 (或等於初始值,如果進位標誌在進入時被設定),則設定。 比較: 如果暫存器的值大於或等於輸入值,則設定。 移位: 設定為輸入的消除位的的值,即左移時為第 7 位,右移時為第 0 位。 |
16 位的值以小端方式儲存在記憶體中,因此最低有效位元組儲存在最高有效位元組之前。例如,如果地址 $0000 包含 $FF 並且地址 $0001 包含 $00,則從 $0000 讀取兩個位元組的值將得到 $00FF。
有符號整數採用二進位制補碼,可以表示從 -128 (%10000000) 到 +127 (%01111111) 的值。如果整數為負數,則第 7 位被設定。
6502 的程式計數器是 16 位寬,因此可以定址高達 2^16 (65536) 位元組的記憶體。某些記憶體區域專用於特定用途
| 區域 | 內容 | 描述 |
|---|---|---|
$0000 - $00FF |
零頁 | 記憶體的第一頁,訪問速度比其他頁快。 指令可以用單個位元組而不是兩個位元組來指定零頁內的地址,因此使用零頁而不是其他任何頁的指令執行時只需要少一個 CPU 週期 |
$0100 - $01FF |
堆疊 | 後進先出資料結構。從 $01FF 到 $0100 向後增長。由一些傳輸、堆疊 和 子程式指令使用 |
$0200 - $FFFF |
通用 | 可以用於任何所需用途的記憶體。 使用 6502 處理器的裝置可以選擇保留子區域用於其他用途,例如 記憶體對映 I/O |
每條指令使用十三種記憶體定址模式中的一種,這決定了指令的運算元。每個模式都提供了一個示例。
累加器被隱式地用作運算元,因此不需要指定地址。
示例
使用沒有運算元的 ASL (算術左移) 指令時,累加器始終是左移的值。
ASL
運算元是隱式的,因此不需要指定它。
示例
這裡隱式的運算元是 X,即傳輸的源,和 A,即傳輸的目標。
TXA
運算元直接用於執行計算。
示例
將值 $22 載入到累加器中。
LDA #$22
指定完整的 16 位地址,並使用該地址處的位元組執行計算。
示例
將地址 $D010 處的數值 $24 載入到 X 暫存器中。
LDX $D010
單個位元組指定記憶體第一頁($00xx)中的地址,也稱為零頁,並使用該地址處的位元組執行計算。
示例
將地址 $0002 處的數值載入到 Y 暫存器中。
LDY $02
指定的偏移量將加到程式計數器 (PC) 中儲存的當前地址上。偏移量範圍為 -128 到 +127。
示例
將偏移量 $2D 加到程式計數器 (例如 $C100) 中的地址。分支 (如果執行) 的目標地址將為 $C12D。
BPL $2D
使用指定地址處儲存的低位元組序的 2 位元組數值執行計算。僅用於 JMP 指令。
示例
讀取地址 $A001 和 $A002,分別返回 $FF 和 $00。然後跳轉到地址 $00FF。
JMP ($A001)
將 X 中的數值加到指定的地址,得到一個和地址。使用和地址處的數值執行計算。
示例
將 X 中的數值 $02 加到 $C001,得到和 $C003。使用地址 $C003 處的數值 $5A 執行帶進位加 (ADC) 操作。
ADC $C001,X
將 Y 中的數值加到指定的地址,得到一個和地址。使用和地址處的數值執行計算。
示例
將 Y 中的數值 $03 加到 $F001,得到和 $F004。將地址 $F004 處的數值 $EF 加 1 (INC) 並將 $F0 寫回 $F004。
INC $F001,Y
將 X 中的數值加到指定的零頁地址,得到一個和地址。使用和地址處的數值執行計算。
示例
將 X 中的數值 $02 加到 $01,得到和 $03。將地址 $0003 處的數值 $A5 載入到累加器中。
LDA $01,X
將 Y 中的數值加到指定的零頁地址,得到一個和地址。使用和地址處的數值執行計算。
示例
將 Y 中的數值 $03 加到 $01,得到和 $04。將地址 $0004 處的數值 $E3 載入到累加器中。
LDA $01,Y
將 X 中的數值加到指定的零頁地址,得到一個和地址。載入和地址 (LSB) 和和地址加 1 (MSB) 處的 2 位元組對中儲存的低位元組序地址,並使用該地址處的數值執行計算。
示例
將 X 中的數值 $02 加到 $15,得到和 $17。地址 $0017 和 $0018 處的地址 $D010 將是累加器中數值 $0F 儲存的位置。
STA ($15,X)
將 Y 中的數值加到指定地址 (LSB) 和指定地址加 1 (MSB) 處的 2 位元組對中儲存的低位元組序地址處的地址。使用和地址處的數值執行計算。實際上,定址模式實際上重複了累加器暫存器的數字。
示例
將 Y 中的數值 $03 加到地址 $002A 和 $002B 處的地址 $C235,得到和 $C238。然後,將累加器與 $C238 處的數值 $2F 進行異或運算。
EOR ($2A),Y
這些是 6502 處理器的指令,包括 ASCII 視覺化、受影響標誌的列表以及可接受定址模式的操作碼錶。
將記憶體載入到累加器: LDA |
將記憶體載入到索引 X: LDX |
將記憶體載入到索引 Y: LDY | ||||||||||||||||||||||||||||||||||||||||||
|
M -> A 標誌:N,Z |
M -> X 標誌:N,Z |
M -> Y 標誌:N,Z | ||||||||||||||||||||||||||||||||||||||||||
|
|
| ||||||||||||||||||||||||||||||||||||||||||
將累加器儲存到記憶體: STA |
將索引 X 儲存到記憶體: STX |
將索引 Y 儲存到記憶體: STY | ||||||||||||||||||||||||||||||||||||||||||
|
A -> M 標誌:無 |
X -> M 標誌:無 |
Y -> M 標誌:無 | ||||||||||||||||||||||||||||||||||||||||||
|
|
|
將記憶體加到累加器並帶進位: ADC |
將記憶體從累加器中減去並帶借位: SBC | ||||||||||||||||||||||||||||||||||||
|
A + M + C -> A 標誌:N,V,Z,C |
A - M - ~C -> A 標誌:N,V,Z,C | ||||||||||||||||||||||||||||||||||||
|
|
將記憶體加 1: INC |
將索引 X 加 1: INX |
將索引 Y 加 1: INY | ||||||||||||||||||
|
M + 1 -> M 標誌:N,Z |
X + 1 -> X 標誌:N,Z |
Y + 1 -> Y 標誌:N,Z | ||||||||||||||||||
|
|
| ||||||||||||||||||
將記憶體減 1: DEC |
將索引 X 減 1: DEX |
將索引 Y 減 1: DEY | ||||||||||||||||||
|
M - 1 -> M 標誌:N,Z |
X - 1 -> X 標誌:N,Z |
Y - 1 -> Y 標誌:N,Z | ||||||||||||||||||
|
|
|
算術左移一位:ASL |
邏輯右移一位:LSR | ||||||||||||||||||||||||
|
C <- 7 6 5 4 3 2 1 0 <- 0 標誌:N,Z,C |
0 -> 7 6 5 4 3 2 1 0 -> C 標誌:N,Z,C | ||||||||||||||||||||||||
|
| ||||||||||||||||||||||||
迴圈左移一位:ROL |
迴圈右移一位:ROR | ||||||||||||||||||||||||
|
C <- 7 6 5 4 3 2 1 0 <- C 標誌:N,Z,C |
C -> 7 6 5 4 3 2 1 0 -> C 標誌:N,Z,C | ||||||||||||||||||||||||
|
|
累加器與記憶體執行與運算:AND |
累加器與記憶體執行或運算:ORA |
累加器與記憶體執行異或運算:EOR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
A & M -> A 標誌:N,Z |
A | M -> A 標誌:N,Z |
A ^ M -> A 標誌:N,Z | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
|
負號 (N)、零 (Z) 和進位 (C) 狀態標誌 用於條件 (分支) 指令。
所有比較指令都以相同的方式影響標誌。
| 條件 | N | Z | C |
|---|---|---|---|
| 暫存器 < 記憶體 | 1 | 0 | 0 |
| 暫存器 = 記憶體 | 0 | 1 | 1 |
| 暫存器 > 記憶體 | 0 | 0 | 1 |
比較記憶體和累加器:CMP |
比較記憶體和索引 X:CPX |
比較記憶體和索引 Y:CPY | ||||||||||||||||||||||||||||||||||
|
A - M 標誌:N,Z,C |
X - M 標誌:N,Z,C |
Y - M 標誌:N,Z,C | ||||||||||||||||||||||||||||||||||
|
|
|
測試記憶體中的位與累加器:BIT
A & M
標誌:N = M7,V = M6,Z
| 定址模式 | 操作碼 |
|---|---|
| a | 2C |
| # | 89 |
| zp | 24 |
當進位位為 0 時分支:BCC |
當進位位為 1 時分支:BCS | ||||||||
|
當 C = 0 時分支 標誌:無 |
當 C = 1 時分支 標誌:無 | ||||||||
|
| ||||||||
當結果不為 0 時分支:BNE |
當結果為 0 時分支:BEQ | ||||||||
|
當 Z = 0 時分支 標誌:無 |
當 Z = 1 時分支 標誌:無 | ||||||||
|
| ||||||||
當結果為正時分支:BPL |
當結果為負時分支:BMI | ||||||||
|
當 N = 0 時分支 標誌:無 |
當 N = 1 時分支 標誌:無 | ||||||||
|
| ||||||||
當溢位位為 0 時分支:BVC |
當溢位位為 1 時分支:BVS | ||||||||
|
當 V = 0 時分支 標誌:無 |
當 V = 1 時分支 標誌:無 | ||||||||
|
|
將累加器傳輸到索引 X:TAX |
將索引 X 傳輸到累加器:TXA | ||||||||
|
A -> X 標誌:N,Z |
X -> A 標誌:N,Z | ||||||||
|
| ||||||||
將累加器傳輸到索引 Y:TAY |
將索引 Y 傳輸到累加器:TYA | ||||||||
|
A -> Y 標誌:N,Z |
Y -> A 標誌:N,Z | ||||||||
|
| ||||||||
將堆疊指標傳輸到索引 X:TSX |
將索引 X 傳輸到堆疊指標:TXS | ||||||||
|
S -> X 標誌:N,Z |
X -> S 標誌:無 | ||||||||
|
|
將累加器壓入堆疊:PHA |
從堆疊中拉出累加器:PLA | ||||||||
|
A -> S 標誌:無 |
S -> A 標誌:N,Z | ||||||||
|
| ||||||||
將處理器狀態壓入堆疊:PHP |
從堆疊中拉出處理器狀態:PLP | ||||||||
|
P -> S 標誌:無 |
S -> P 標誌:全部 | ||||||||
|
|
處理器狀態儲存為一個位元組,從高到低位的標誌位如下:NV--DIZC。
跳轉到新位置:JMP
透過更改程式計數器的值跳轉到新位置。
警告: 當與 絕對間接 地址模式一起使用時,當指定地址為 $xxFF 時,硬體錯誤會導致意外行為。
例如,JMP ($11FF) 將從 $11FF 讀取低位元組,從 $1100 讀取高位元組,而不是從 $1200 讀取高位元組,正如人們預期的那樣。這是由於間接地址的低位元組溢位沒有進位到高位元組。
標誌:無
| 定址模式 | 操作碼 |
|---|---|
| a | 4C |
| (a) | 6C |
跳轉到新位置並儲存返回地址:JSR
跳轉到子程式。
下一個指令之前的地址 (PC - 1) 被壓入堆疊:首先是高位元組,然後是低位元組。由於堆疊向後增長,因此返回地址在記憶體中儲存為小端數。
PC 被設定為目標地址。
標誌:無
| 定址模式 | 操作碼 |
|---|---|
| a | 20 |
從子程式返回:RTS
從子程式返回到呼叫它的 JSR 的位置。
返回地址從堆疊中彈出 (先彈出低位元組,然後彈出高位元組)。
返回地址遞增並存儲在 PC 中。
標誌:無
| 定址模式 | 操作碼 |
|---|---|
| i | 60 |
從中斷返回:RTI
從中斷返回。
P 從堆疊中彈出。
PC 從堆疊中彈出。
標誌:全部
| 定址模式 | 操作碼 |
|---|---|
| i | 40 |
清除進位位:CLC |
設定進位位:SEC | ||||||||
|
0 -> C 標誌:C = 0 |
1 -> C 標誌:C = 1 | ||||||||
|
| ||||||||
清除十進位制模式:CLD |
設定十進位制模式:SED | ||||||||
|
0 -> D 標誌:D = 0 |
1 -> D 標誌:D = 1 | ||||||||
|
| ||||||||
清除中斷停用狀態:CLI |
設定中斷停用狀態:SEI | ||||||||
|
0 -> I 標誌:I = 0 |
1 -> I 標誌:I = 1 | ||||||||
|
| ||||||||
清除溢位位:CLV |
|||||||||
|
0 -> V 標誌:V = 0 |
|||||||||
|
中斷:BRK
強制中斷
標誌:B = 1,I = 1
| 定址模式 | 操作碼 |
|---|---|
| i | 00 |
無操作:NOP
無操作
標誌:無
| 定址模式 | 操作碼 |
|---|---|
| i | EA |
| 高半位元組 | 低半位元組 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F | |
| 00 | BRK i | ORA (zp,x) | ORA zp | ASL zp | PHP i | ORA # | ASL A | ORA a | ASL a | |||||||
| 10 | BPL r | ORA (zp),y | ORA zp,x | ASL zp,x | CLC i | ORA a,y | ORA a,x | ASL a,x | ||||||||
| 20 | JSR a | AND (zp,x) | BIT zp | AND zp | ROL zp | PLP i | AND # | ROL A | BIT a | AND a | ROL a | |||||
| 30 | BMI r | AND (zp),y | AND zp,x | ROL zp,x | SEC i | AND a,y | AND a,x | ROL a,x | ||||||||
| 40 | RTI i | EOR (zp,x) | EOR zp | LSR zp | PHA i | EOR # | LSR A | JMP a | EOR a | LSR a | ||||||
| 50 | BVC r | EOR (zp),y | EOR zp,x | LSR zp,x | CLI i | EOR a,y | EOR a,x | LSR a,x | ||||||||
| 60 | RTS i | ADC (zp,x) | ADC zp | ROR zp | PLA i | ADC # | ROR A | JMP (a) | ADC a | ROR a | ||||||
| 70 | BVS r | ADC (zp),y | ADC zp,x | ROR zp,x | SEI i | ADC a,y | ADC a,x | ROR a,x | ||||||||
| 80 | STA (zp,x) | STY zp | STA zp | STX zp | DEY i | BIT # | TXA i | STY a | STA a | STX a | ||||||
| 90 | BCC r | STA (zp),y | STY zp,x | STA zp,x | STX zp,y | TYA i | STA a,y | TXS i | STA a,x | |||||||
| A0 | LDY # | LDA (zp,x) | LDX # | LDY zp | LDA zp | LDX zp | TAY i | LDA # | TAX i | LDY a | LDA a | LDX a | ||||
| B0 | BCS r | LDA (zp),y | LDY zp,x | LDA zp,x | LDX zp,y | CLV i | LDA a,y | TSX i | LDY a,x | LDA a,x | LDX a,y | |||||
| C0 | CPY # | CMP (zp,x) | CPY zp | CMP zp | DEC zp | INY i | CMP # | DEX i | CPY a | CMP a | DEC a | |||||
| D0 | BNE r | CMP (zp),y | CMP zp,x | DEC zp,x | CLD i | CMP a,y | CMP a,x | DEC a,x | ||||||||
| E0 | CPX # | SBC (zp,x) | CPX zp | SBC zp | INC zp | INX i | SBC # | NOP i | CPX a | SBC a | INC a | |||||
| F0 | BEQ r | SBC (zp),y | SBC zp,x | INC zp,x | SED i | SBC a,y | SBC a,x | INC a,x | ||||||||
- NES程式設計:任天堂娛樂系統使用6502的一個版本
- 65c02彙編,6502的衍生產品,用於許多家用電腦、電子遊戲機和嵌入式系統。
- 超級任天堂程式設計:超級任天堂使用65c816,6502的衍生產品
- 蘋果公司歷史:早期蘋果電腦都使用6502的某些版本
- 計算機歷史/微型計算機的興起
- X86反彙編/反彙編器和反編譯器#反彙編8位CPU程式碼提到一些6502反彙編器
- 計算機程式設計/Hello world#累加器 + 索引暫存器機器:MOS技術6502,CBM核心,MOS彙編語法
- MOS 技術 6502,wikipedia.org
- Owad,Tom,“Apple I Replica Creation”,Syngress,2005。ISBN 193183640X
- 6502.org 6502微處理器資源,特別是教程和入門頁面。
- 6502指令集,masswerk.at
- 6502系列CPU參考由Michael Steil撰寫,pagetable.com
- NMOS 6502操作碼由John Pickens等人撰寫,6502.org
- 維基versity:學習 6502 彙編,wikiversity.org