Re: 从零开始的红白机模拟 - [06]基础指令 - Dust Loong的文章 - 知乎
https://zhuanlan.zhihu.com/p/44100786
Dust Loong
兴趣:人机交互 https://github.com/dustpg
2 人赞了该文章
本文(知乎不支持表格, 可以到这里查看)github备份地址
这节就详细谈谈基础指令, 所谓'基础指令'只是自己随便命名的, 避免一节过长, 请勿对号入座.
不同指令需要消耗不同的周期, 这很好理解. 不过就算相同的指令环境不同也会消耗不同周期:
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|----------|-----------|----|---------|----------|
| 立即 | LDA #Oper | A9 | 2 | 2 |
| 零页 | LDA Oper | A5 | 2 | 3 |
| 零页,X | LDA Oper,X | B5 | 2 | 4 |
| 绝对 | LDA Oper | AD | 3 | 4 |
| 绝对,X | LDA Oper,X | BD | 3 | 4* |
| 绝对,Y | LDA Oper,Y | B9 | 3 | 4* |
| (间接,X) | LDA (Oper,X) | A1 | 2 | 6 |
| (间接),Y | LDA (Oper),Y | B1 | 2 | 5* |
* 在页面边界交叉时 +1s
由存储器取数送入累加器A, 影响FLAG: Z(ero),S(ign), 伪C代码:
A = READ(address);
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|--------------|----|---------|----------|
| 立即 | LDX #Oper | A2 | 2 | 2 |
| 零页 | LDX Oper | A6 | 2 | 3 |
| 零页, Y | LDX Oper,Y | B6 | 2 | 4 |
| 绝对 | LDX Oper | AE | 3 | 4 |
| 绝对, Y | LDX Oper,Y | BE | 3 | 4* |
* 在页面边界交叉时 +1s
由存储器取数送入变址寄存器X, 影响FLAG: Z(ero),S(ign), 伪C代码:
X = READ(address);
CHECK_ZSFLAG(X);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|--------------|----|---------|----------|
| 立即 | LDY #Oper | A0 | 2 | 2 |
| 零页 | LDY Oper | A4 | 2 | 3 |
| 零页,X | LDY Oper,X | B4 | 2 | 4 |
| 绝对 | LDY Oper | AC | 3 | 4 |
| 绝对,X | LDY Oper,X | BC | 3 | 4* |
* 在页面边界交叉时 +1s
由存储器取数送入变址寄存器Y, 影响FLAG: Z(ero),S(ign), 伪C代码:
Y = READ(address);
CHECK_ZSFLAG(Y);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 零页 | STA Oper | 85 | 2 | 3 |
| 零页,X | STA Oper,X | 95 | 2 | 4 |
| 绝对 | STA Oper | 80 | 3 | 4 |
| 绝对,X | STA Oper,X | 90 | 3 | 5 |
| 绝对,Y | STA Oper, Y | 99 | 3 | 5 |
| (间接,X)| STA (Oper,X) | 81 | 2 | 6 |
| (间接),Y| STA (Oper),Y | 91 | 2 | 6 |
将累加器A的数送入存储器, 影响FLAG:(无), 伪C代码:
WRTIE(address, A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 零页 | STX Oper | 86 | 2 | 3 |
| 零页,Y| STX Oper,Y | 96 | 2 | 4 |
| 绝对 | STX Oper | 8E | 3 | 4 |
将变址寄存器X的数送入存储器, 影响FLAG:(无), 伪C代码:
WRTIE(address, X);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 零页 | STY Oper | 84 | 2 | 3 |
| 零页,X | STY Oper,X | 94 | 2 | 4 |
| 绝对 | STY Oper | 8C | 3 | 4 |
将变址寄存器Y的数送入存储器, 影响FLAG:(无), 伪C代码:
WRTIE(address, Y);
助记符号 A = A + M +C
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | ADC #Oper | 69 | 2 | 2 |
| 零页 | ADC Oper | 65 | 2 | 3 |
| 零页,X | ADC Oper,X | 75 | 2 | 4 |
| 绝对 | ADC Oper | 60 | 3 | 4 |
| 绝对,X | ADC Oper,X | 70 | 3 | 4* |
| 绝对,Y | ADC Oper,Y | 79 | 3 | 4* |
| (间接,X) | ADC (Oper,X) | 61 | 2 | 6 |
| (间接),Y | ADC (Oper),Y | 71 | 2 | 5* |
* 在页面边界交叉时 +1s
累加器,存储器,进位标志C相加,结果送累加器A. 影响FLAG: S(ign), Z(ero), C(arry), (o)V(erflow), 伪C代码:
src = READ(address);
uint16_t result16 = A + src + (CF ? 1 : 0);
CHECK_CFLAG(result16>>8);
uint8_t result8 = result16;
CHECK_VFLAG(!((A ^ src) & 0x80) && ((A ^ result8) & 0x80));
A = result8;
CHECK_ZSFLAG(A);
助记符号 A = A - M - (1-C)
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | SBC #Oper | E9 | 2 | 2 |
| 零页 | SBC Oper | E5 | 2 | 3 |
| 零页,X | SBC Oper,X | F5 | 2 | 4 |
| 绝对 | SBC Oper | ED | 3 | 4 |
| 绝对,X | SBC Oper,X | FD | 3 | 4* |
| 绝对,Y | SBC Oper,Y | F9 | 3 | 4* |
| (间接,X)| SBC (Oper,X) | E1 | 2 | 6 |
| (间接),Y| SBC (Oper),Y | F1 | 2 | 5 |
从累加器减去存储器和进位标志C,结果送累加器A. 影响FLAG: S(ign), Z(ero), C(arry), (o)V(erflow), 伪C代码:
src = READ(address);
uint16_t result16 = A - src - (CF ? 0 : 1);
CHECK_CFLAG(!(result16>>8));
uint8_t result8 = result16;
CHECK_VFLAG(((A ^ result8) & 0x80) && ((A ^ src) & 0x80));
A = result8;
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 零页 | INC Oper | E6 | 2 | 5 |
| 零页,X | INC Oper,X | F6 | 2 | 6 |
| 绝对 | INC Oper | EE | 3 | 6 |
| 绝对,X | INC Oper,X | FE | 3 | 7 |
存储器单元内容+1, 影响FLAG:Z(ero),S(ign), 伪C代码:
tmp = READ(address);
++tmp;
WRITE(address, tmp);
CHECK_ZSFLAG(tmp);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 零页 | DEC Oper | C6 | 2 | 5 |
| 零页,X | DEC Oper,X | D6 | 2 | 6 |
| 绝对 | DEC Oper | CE | 3 | 6 |
| 绝对,X | DEC Oper,X | DE | 3 | 7 |
存储器单元内容-1, 影响FLAG:Z(ero),S(ign), 伪C代码:
tmp = READ(address);
--tmp;
WRITE(address, tmp);
CHECK_ZSFLAG(tmp);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | AND #Oper | 29 | 2 | 2 |
| 零页 | AND Oper | 25 | 2 | 3 |
| 零页,X | AND Oper,X | 35 | 2 | 4 |
| 绝对 | AND Oper | 2D | 3 | 4 |
| 绝对,X | AND Oper,X | 3D | 3 | 4* |
| 绝对,Y | AND Oper,Y | 39 | 3 | 4* |
| (间接,X)| AND (Oper,X) | 21 | 2 | 6 |
| (间接),Y| AND (Oper),Y | 31 | 2 | 5 |
* 在页面边界交叉时 +1s
存储器单元与累加器做与运算, 影响FLAG:Z(ero),S(ign), 伪C代码:
A &= READ(address);
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | ORA #Oper | 09 | 2 | 2 |
| 零页 | ORA Oper | 05 | 2 | 3 |
| 零页,X | ORA Oper,X | 15 | 2 | 4 |
| 绝对 | ORA Oper | 0D | 3 | 4 |
| 绝对,X | ORA Oper,X | 10 | 3 | 4* |
| 绝对,Y | ORA Oper,Y | 19 | 3 | 4* |
| (间接,X)| ORA (Oper,X)| 01 | 2 | 6 |
| (间接),Y| ORA (Oper),Y| 11 | 2 | 5 |
* 在页面边界交叉时 +1s
存储器单元与累加器做或运算, 影响FLAG:Z(ero),S(ign), 伪C代码:
A |= READ(address);
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | EOR #Oper | 49 | 2 | 2 |
| 零页 | EOR Oper | 45 | 2 | 3 |
| 零页,X | EOR Oper,X | 55 | 2 | 4 |
| 绝对 | EOR Oper | 40 | 3 | 4 |
| 绝对,X | EOR Oper,X | 50 | 3 | 4* |
| 绝对,Y | EOR Oper,Y | 59 | 3 | 4* |
| (间接,X)| EOR (Oper,X)| 41 | 2 | 6 |
| (间接),Y| EOR (Oper),Y| 51 | 2 | 5* |
* 在页面边界交叉时 +1s
存储器单元与累加器做或运算, 影响FLAG:Z(ero),S(ign), 伪C代码:
A ^= READ(address);
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | INX | E8 | 1 | 2 |
变址寄存器X内容+1, 影响FLAG:Z(ero),S(ign), 伪C代码:
++X;
CHECK_ZSFLAG(X);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | DEX | CA | 1 | 2 |
变址寄存器X内容-1, 影响FLAG:Z(ero),S(ign), 伪C代码:
--X;
CHECK_ZSFLAG(X);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | INY | C8 | 1 | 2 |
变址寄存器Y内容+1, 影响FLAG:Z(ero),S(ign), 伪C代码:
++Y;
CHECK_ZSFLAG(Y);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | DEY | 88 | 1 | 2 |
变址寄存器Y内容-1, 影响FLAG:Z(ero),S(ign), 伪C代码:
--Y;
CHECK_ZSFLAG(Y);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | TAX | AA | 1 | 2 |
将累加器A的内容送入变址寄存器X, 影响FLAG:Z(ero),S(ign), 伪C代码:
X = A;
CHECK_ZSFLAG(X);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | TXA | 8A | 1 | 2 |
将累加器A的内容送入变址寄存器X, 影响FLAG:Z(ero),S(ign), 伪C代码:
A = X;
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | TAY | A8 | 1 | 2 |
将累加器A的内容送入变址寄存器Y, 影响FLAG:Z(ero),S(ign), 伪C代码:
Y = A;
CHECK_ZSFLAG(Y);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | TYA | 98 | 1 | 2 |
将变址寄存器Y的内容送入累加器A, 影响FLAG:Z(ero),S(ign), 伪C代码:
A = Y;
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | TSX | BA | 1 | 2 |
将栈指针SP内容送入变址寄存器X, 影响FLAG:Z(ero),S(ign), 伪C代码:
X = SP;
CHECK_ZSFLAG(X);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | TXS | 9A | 1 | 2 |
将变址寄存器X内容送入栈指针SP, 影响FLAG:无, 伪C代码:
SP = X;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | CLC | 18 | 1 | 2 |
清除进位标志C, 影响FLAG: C(arry), 伪C代码:
CF = 0;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | SEC | 38 | 1 | 2 |
设置进位标志C, 影响FLAG: C(arry), 伪C代码:
CF = 1;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | CLD | D8 | 1 | 2 |
清除十进制模式标志D, 影响FLAG: D(Decimal), 伪C代码:
DF = 0;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | SED | F8 | 1 | 2 |
设置十进制模式标志D, 影响FLAG: D(Decimal), 伪C代码:
DF = 1
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | CLV | B8 | 1 | 2 |
清除溢出标志V, 影响FLAG: (o)V(erflow), 伪C代码:
VF = 0;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | CLI | 58 | 1 | 2 |
清除中断禁止标志I, 影响FLAG: I(nterrupt-disable), 伪C代码:
IF = 0;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | SEI | 78 | 1 | 2 |
设置中断禁止标志I, 影响FLAG: I(nterrupt-disable), 伪C代码:
IF = 1;
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | CMP #Oper | C9 | 2 | 2 |
| 零页 | CMP Oper | C5 | 2 | 3 |
| 零页,X | CMP Oper,X | D5 | 2 | 4 |
| 绝对 | CMP Oper | CD | 3 | 4 |
| 绝对,X | CMP Oper,X | DD | 3 | 4* |
| 绝对,Y | CMP Oper,Y | D9 | 3 | 4* |
| (间接,X) | CMP (Oper,X) | C1 | 2 | 6 |
| (间接),Y | CMP (Oper),Y | D1 | 2 | 5* |
比较储存器值与累加器A. 影响FLAG: C(arry), S(ign), Z(ero). 伪C代码:
uint16_t result16 = (uint16_t)A - (uint16_t)READ(address);
CF = result16 < 0x100;
CHECK_ZSFLAG((uint8_t)result16);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | CPX #Oper | E0 | 2 | 2 |
| 零页 | CPX Oper | E4 | 2 | 3 |
| 绝对 | CPX Oper | EC | 3 | 4 |
比较储存器值与变址寄存器X. 影响FLAG: C(arry), S(ign), Z(ero). 伪C代码:
uint16_t result16 = (uint16_t)X - (uint16_t)READ(address);
CF = result16 < 0x100;
CHECK_ZSFLAG((uint8_t)result16);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 立即 | CPY #Oper | C0 | 2 | 2 |
| 零页 | CPY Oper | C4 | 2 | 3 |
| 绝对 | CPY Oper | CC | 3 | 4 |
比较储存器值与变址寄存器Y. 影响FLAG: C(arry), S(ign), Z(ero). 伪C代码:
uint16_t result16 = (uint16_t)Y - (uint16_t)READ(address);
CF = result16 < 0x100;
CHECK_ZSFLAG((uint8_t)result16);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 零页 | BIT Oper | 24 | 2 | 3 |
| 绝对 | BIT Oper | 2C | 3 | 4 |
位测试 - 若 A&M 结果 =0, 那么Z=1 - 若 A&M 结果!=0, 那么Z=0 - S = M的第7位 - V = M的第6位
影响FLAG: (o)V(erflow), S(ign), Z(ero). 伪C代码:
tmp = READ(address);
VF = (tmp >> 6) & 1;
SF = (tmp >> 7) & 1;
ZF = A & tmp ? 0 : 1;
助记符号: C <- |7|6|5|4|3|2|1|0| <- 0
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 累加器A | ASL A | 0A | 1 | 2 |
| 零页 | ASL Oper | 06 | 2 | 5 |
| 零页,X | ASL Oper,X | 16 | 2 | 6 |
| 绝对 | ASL Oper | 0E | 3 | 6 |
| 绝对, X | ASL Oper,X | 1E | 3 | 7 |
累加器A, 或者存储器单元算术按位左移一位. 最高位移动到C, 最低位0. 影响FLAG: S(ign) Z(ero) C(arry), 伪C代码:
// ASL A:
CHECK_CFLAG(A>>7);
A <<= 1;
CHECK_ZSFLAG(A);
// 其他情况
tmp = READ(address);
CHECK_CFLAG(tmp>>7);
tmp <<= 1;
WRITE(address, tmp);
CHECK_ZSFLAG(tmp);
助记符号: 0 -> |7|6|5|4|3|2|1|0| -> C
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 累加器A | LSR A | 4A | 1 | 2 |
| 零页 | LSR Oper | 46 | 2 | 5 |
| 零页,X | LSR Oper,X | 56 | 2 | 6 |
| 绝对 | LSR Oper | 4E | 3 | 6 |
| 绝对,X | LSR Oper,X | 5E | 3 | 7 |
累加器A, 或者存储器单元逻辑按位右移一位. 最低位回移进C, 最高位变0, 影响FLAG: S(ign) Z(ero) C(arry), 伪C代码:
// LSR A:
CHECK_CFLAG(A & 1);
A >>= 1;
CHECK_ZSFLAG(A);
// 其他情况
tmp = READ(address);
CHECK_CFLAG(tmp & 1);
tmp >>= 1;
WRITE(address, tmp);
CHECK_ZSFLAG(tmp);
助记符号: ...|0| <- C <- |7|6|5|4|3|2|1|0| <- C <- |7|...
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 累加器A | ROL A | 2A | 1 | 2 |
| 零页 | ROL Oper | 26 | 2 | 5 |
| 零页,X | ROL Oper,X | 36 | 2 | 6 |
| 绝对 | ROL Oper | 2E | 3 | 6 |
| 绝对,X | ROL Oper,X | 3E | 3 | 7 |
累加器A, 或者储存器内容 连同C位 按位循环左移一位, 影响FLAG: S(ign) Z(ero) C(arry), 伪C代码:
// A_M 意思到了就行
uint16_t src = A_M;
src <<= 1;
if (CF) src |= 0x1;
CHECK_CFLAG(src > 0xff);
A_M = src;
CHECK_ZSFLAG(A_M);
助记符号: ...|0| -> C -> |7|6|5|4|3|2|1|0| -> C -> |7|...
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 累加器A | ROR A | 6A | 1 | 2 |
| 零页 | ROR Oper | 66 | 2 | 5 |
| 零页,X | ROR Oper,X | 76 | 2 | 6 |
| 绝对 | ROR Oper | 6E | 3 | 6 |
| 绝对,X | ROR Oper,X | 7E | 3 | 7 |
累加器A, 或者储存器内容 连同C位 按位循环左移一位, 影响FLAG: S(ign) Z(ero) C(arry), 伪C代码:
// A_M 意思到了就行
uint16_t src = A_M;
if (CF) src |= 0x100;
CF = src & 1;
src >> 1;
A_M = src;
CHECK_ZSFLAG(A_M);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | PHA | 48 | 1 | 3 |
累加器A压入栈顶(栈指针SP-1). 影响FLAG:(无), 伪C代码:
PUSH(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | PLA | 68 | 1 | 4 |
push的反操作, 在6502汇编称为pull, 不过自己还是习惯称为pop. 6502_cn这篇文档提到没有FLAG修改, 可能是笔误, 其他文档(比如这个)提到会影响S(ign) Z(ero) , 伪C代码:
A = POP();
CHECK_ZSFLAG(A);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | PHP | 08 | 1 | 3 |
将状态FLAG压入栈顶, 影响FLAG:(无), 伪C代码:
PUSH(P | FLAG_B | FLAG_R);
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
| 隐含 | PLP | 28 | 1 | 4 |
PHP逆操作, 影响FLAG: (是的), 伪C代码:
p = POP();
// 无视BIT4 BIT5
RF = 1;
BF = 0;