dustpg / BlogFM

Blog for Me
MIT License
155 stars 23 forks source link

Re: 从零开始的红白机模拟 - [07]流程指令 #11

Open dustpg opened 6 years ago

dustpg commented 6 years ago

STEP3: CPU 指令实现 - 流程指令

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

JMP - Jump

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

无条件跳转, 影响FLAG: (无), 伪C代码:

PC = address;

BEQ - Branch if Equal

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BEQ Oper F0 2 2*

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

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

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

if (ZFLAG) PC = address;

BNE - Branch if Not Equal

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BNE Oper D0 2 2*

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

如果标志位Z(ero) = 0[即不相同]则跳转,否则继续, 影响FLAG: (无). 伪C代码:

if (!ZFLAG) PC = address;

BCS - Branch if Carry Set

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BCS Oper B0 2 2*

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

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

if (CFLAG) PC = address;

BCC - Branch if Carry Clear

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BCC Oper 90 2 2*

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

如果标志位C(arry) = 0[即没进位]则跳转,否则继续, 影响FLAG: (无). 伪C代码:

if (!CFLAG) PC = address;

BMI - Branch if Minus

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BMI Oper 30 2 2*

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

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

if (SFLAG) PC = address;

BPL - Branch if Plus

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BPL Oper 10 2 2*

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

如果标志位S(ign) = 1[即正数]则跳转,否则继续, 影响FLAG: (无). 伪C代码:

if (!SFLAG) PC = address;

BVS - Branch if Overflow Set

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BVS Oper 70 2 2*

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

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

if (VFLAG) PC = address;

BVC - Branch if Overflow Clear

寻址模式 汇编格式 OP代码 指令字节 指令周期
相对 BVC Oper 50 2 2*

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

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

if (!VFLAG) PC = address;

JSR - Jump to Subroutine

寻址模式 汇编格式 OP代码 指令字节 指令周期
绝对 JSR Oper 20 3 6

跳转至子程序, 记录该条指令最后的地址(即当前PC-1, 或者说JSR代码\$20所在地址+2), 影响FLAG: (无), 伪C代码:

--PC;
PUSH(PC >> 8);
PUSH(PC & 0xFF);
PC = address;

RTS - Return from Subroutine

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 RTS 60 1 6

JSR逆操作, 从子程序返回. 返回之前记录的位置+1(话说为什么不直接存+1的地址), 影响FLAG: (无), 伪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代码:

++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代码:

P = POP();
// 无视BIT4 BIT5
RF = 1;
BF = 0;

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

REF

fushang318 commented 3 years ago

image