### STEP3: CPU 指令实现 - 流程指令
本文github[备份地址](https://github.com/dustpg/BlogFM/issues/11)

同样, '流程指令'是指为了和上节分开而自己随便取的名字.

### JMP - Jump
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  绝对      |   JMP Oper            |    4C   |    3    |    3     |
|  间接      |   JMP (Oper)          |    6C   |    3    |    5     |

无条件跳转, 影响FLAG: (无), 伪C代码:
```c
PC = address;
```

### BEQ - Branch if Equal
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BEQ Oper            |    F0   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

当然, 如果没有实行跳转则花费2周期, 下同.

如果标志位Z(ero) = 1[即相同]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (ZFLAG) PC = address;
```

### BNE - Branch if Not Equal
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BNE Oper            |    D0   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位Z(ero) = 0[即不相同]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (!ZFLAG) PC = address;
```

### BCS - Branch if Carry Set
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BCS Oper            |    B0   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位C(arry) = 1[即进位了]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (CFLAG) PC = address;
```

### BCC - Branch if Carry Clear
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BCC Oper            |    90   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位C(arry) = 0[即没进位]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (!CFLAG) PC = address;
```

### BMI - Branch if Minus
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BMI Oper            |    30   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位S(ign) = 1[即负数]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (SFLAG) PC = address;
```

### BPL - Branch if Plus
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BPL Oper            |    10   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位S(ign) = 1[即正数]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (!SFLAG) PC = address;
```

### BVS - Branch if Overflow Set
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BVS Oper            |    70   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位(o)V(erflow) = 1[即溢出]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (VFLAG) PC = address;
```

### BVC - Branch if Overflow Clear
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  相对      |   BVC Oper            |    50   |    2    |    2*    |

\* +1s 跳转同一页面
\* +2s 跳转不同页面

如果标志位(o)V(erflow) = 0[即没有溢出]则跳转，否则继续, 影响FLAG: (无). 伪C代码:
```c
if (!VFLAG) PC = address;
```

### JSR - Jump to Subroutine
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  绝对      |   JSR Oper            |    20   |    3    |    6     |

跳转至子程序, 记录该条指令最后的地址(即当前PC-1, 或者说JSR代码\$20所在地址+2), 影响FLAG: (无), 伪C代码:
```c
--PC;
PUSH(PC >> 8);
PUSH(PC & 0xFF);
PC = address;
```
### RTS - Return from Subroutine
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  隐含       |   RTS                 |    60   |    1    |    6     |

JSR逆操作, 从子程序返回. 返回之前记录的位置+1(话说为什么不直接存+1的地址), 影响FLAG: (无), 伪C代码:
```c
PC = POP();
PC |= POP() << 8;
++PC;
```

### NOP - No Operation
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
  |  隐含       |   NOP                 |    EA   |    1    |    2     |

啥都不干, 居然两个周期, 太丢NOP的脸了, 褪裙吧. 

### BRK - Force Break(Interrupt)
助记符号: PUSH (PC+1）; PUSH (P); I = 1; PC = IRQ;

| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  隐含       |   BRK                 |    00   |    1    |    7     |
详细指令周期:
  1. 读取OP代码, PC+1
  2. 读取下一字节指令, 无视, **PC+1**
  3. 压入 PC-H, SP-1
  4. 压入 PC-L, SP-1
  5. 压入 P, SP-1
  6. 读取IRQ+0至PC-L
  7. 读取IRQ+1至PC-H

强制中断, 记录当前PC+1作为返回地址, 以及PS. 跳转到IRQ地址

由于大部分游戏都没有使用该指令, 所以有些模拟器的实现可能有些问题.

BRK虽然是单字节指令, 但是会让PC + 2, 所以**干脆认为是双字节指令**也不错.

影响FLAG: I(nterrupt), 伪C代码:
```c
++PC;
PUSH(PC>>8);
PUSH(PC & 0xFF);
PUSH(P | FLAG_R | FLAG_B);
IF = 1;
PC = READ(IRQ);
PC |= READ(IRQ + 1) << 8;
```

### RTI - Return from Interrupt
| 寻址模式| 汇编格式| OP代码 |指令字节|指令周期|
|--------|-----------|----|---------|----------|
|  隐含       |   RTI                 |    4D   |    1    |    6     |

从中断返回, 影响FLAG: 是的, 伪C代码:
```c
P = POP();
// 无视BIT4 BIT5
RF = 1;
BF = 0;

PC = POP();
PC |= POP() << 8;
```

### REF
 - [nesdev-archive](http://nesdev.com/archive.html)
 - [NES 文档2.00](http://nesdev.com/nestech_cn.txt)
 - [6502 微处理器](http://nesdev.com/6502_cn.txt)
 - [6502_cpu](http://nesdev.com/6502_cpu.txt)