dustpg / BlogFM

Blog for Me
MIT License
155 stars 23 forks source link

Re: 从零开始的红白机模拟 - [08]扩展指令 #12

Open dustpg opened 6 years ago

dustpg commented 6 years ago

STEP3: CPU 指令实现 - 扩展指令

这节是指令介绍的最后一节.

扩展指令, 或者说非法/未被记录的指令: 一般是组合指令. 由于非官方记录, 所以指令名称可能不一致.

本节很多指令并没有在STEP3中实现, 原因是因为使用的测试ROM并没有测试这些指令, 所以'忘记'实现了, 这些指令直到测试了'blOperg's CPU test rom v5'才实现.

NOP - No OP

除了基本的NOP, 还有高级的NOP. 一般来讲6502指令是根据寻址方式排序的(偏移), 所以在其他寻址方式对应NOP的地方还有

详细的请到引用链接了解

ALR[ASR] - 'And' then Logical Shift Right - AND+LSR

助记符号: A &= M; C = A & 1; A >>= 1;

寻址模式 汇编格式 OP代码 指令字节 指令周期
立即 ASR #Oper 4B 2 2

只有两个指令周期(NOP你看看你), 应该是都消耗在了读取数据上

AND+LSR 的组合指令 - 逻辑与运算再右移一位: 影响FLAG: C(arry), S(ign), Z(ero), 伪C代码:

A &= READ(address);
CHEKC_CFLAG(A & 1);
A >>= 1;
CHECK_ZSFLAG(A);

例子: ALR #\$FE 基本等价于 LSR A + CLC. 前者2字节2周期, 后者2字节4周期

ANC[AAC] - 'And' then copy N(S) to C

助记符号: A &= M; C = N(S);

寻址模式 汇编格式 OP代码 指令字节 指令周期
立即 ANC #Oper 0B 2 2

N(egative)或者说S(ign)位的值复制到C(arry)位, 影响FLAG: C(arry), S(ign), Z(ero), 伪C代码:

A &= READ(address);
CHECK_ZSFLAG(A);
CF = SF; 

例子: ANC #\$FF 有符号扩展; ANC #\$00 基本等价于 LDA #$00 + CLC.

ARR - 'AND' then Rotate Right - AND+ROR [类似]

助记符号: A &= M; A = (A>>1)|(C<<7); C = (A>>6)&1; V = ((A>>5)^(A>>6)&1);

寻址模式 汇编格式 OP代码 指令字节 指令周期
立即 ARR #Oper 6B 2 2

基本等价于 AND+ROR, 除了FALG设置.

C is bit 6 and, V is bit 6 xor bit 5.

影响FLAG: C(arry), S(ign), Z(ero), (o)V(erflow), 伪C代码:

A &= READ(address);
A = (A >> 1) | (CF << 7);
CHECK_ZSFLAG(A);
CHECK_CFLAG(A>>6);
CHECK_VFLAG(((A>>5)^(A>>6))&1);

AXS[SBX] - A 'And' X, then Subtract memory, to X

助记符号: X = (A&X) - M;

寻址模式 汇编格式 OP代码 指令字节 指令周期
立即 AXS #Oper CB 2 2

影响FLAG: C(arry), S(ign), Z(ero), 伪C代码:

uint16_t tmp = (SFC_A & SFC_X) - SFC_READ_PC(address);
X = (uint8_t)tmp;
CHECK_ZSFLAG(X);
SET_CF((tmp & 0x8000) == 0);

LAX - Load 'A' then Transfer X - LDA + TAX

助记符号: X = A = M;

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 LAX Oper $A7 2 3
零页 ,Y LAX Oper,Y $B7 2 4
绝对 LAX Oper $AF 3 4
绝对,Y LAX Oper,Y $BF 3 4 *
(间接,X) LAX (Oper,X) $A3 2 6
(间接),Y LAX (Oper),Y $B3 2 5 *

* 在页面边界交叉时 +1s

将储存器数据载入A, 然后传给X. 影响FLAG: S(ign), Z(ero), 伪C代码:

X = A = READ(address);
CHECK_ZSFLAG(X);

SAX - Store A 'And' X

助记符号: M = A & X;

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 AAX Oper $87 2 3
零页 ,Y AAX Oper,Y $97 2 4
(间接,X) AAX (Oper,X) $83 2 6
绝对 AAX Oper $8F 3 4

将累加器A和变址寄存器X '与' 的结果保留在储存器上. 影响FLAG: (无), 伪C代码:

WRITE(address, A & X);

DCP - Decrement memory then Compare with A - DEC + CMP

助记符号: M -= 1; A - M ? 0

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 DCP Oper $C7 2 5
零页 ,X DCP Oper,X $D7 2 6
绝对 DCP Oper $CF 3 6
绝对,X DCP Oper,X $DF 3 7
绝对,Y DCP Oper,Y $DB 3 7
(间接,X) DCP (Oper,X) $C3 2 8
(间接),Y DCP (Oper),Y $D3 2 8

读改写(RMW)指令, 存储器值-1再与累加器比较. 影响FLAG: C(arry), Z(ero), S(ign). 伪C代码:

tmp = READ(address);
--tmp;
WRITE(address, tmp);

uint16_t result16 = (uint16_t)A - (uint16_t)tmp;
CF = result16 < 0x100;
CHECK_ZSFLAG((uint8_t)result16);

ISC(ISB) - Increment memory then Subtract with Carry - INC + SBC

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 ISC Oper $E7 2 5
零页 ,X ISC Oper,X $F7 2 6
绝对 ISC Oper $EF 3 6
绝对,X ISC Oper,X $FF 3 7
绝对,Y ISC Oper,Y $FB 3 7
(间接,X) ISC (Oper,X) $E3 2 8
(间接),Y ISC (Oper),Y $F3 2 8

读改写(RMW)指令, 存储器值+1再用累加器减. 影响FLAG: C(arry), (o)V(erflow), Z(ero), S(ign). 伪C代码:

// INC
tmp = READ(address);
++tmp;
WRITE(address, tmp);

// SBC
uint16_t result16 = A - tmp - (CF ? 0 : 1);
CHECK_CFLAG(!(result16>>8));
uint8_t result8 = result16;
CHECK_VFLAG(((A ^ result8) & 0x80) && ((A ^ tmp) & 0x80));
A = result8;
CHECK_ZSFLAG(A);

RLA - Rotate Left then 'And' - ROL + AND

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 RLA Oper $27 2 5
零页 ,X RLA Oper,X $37 2 6
绝对 RLA Oper $2F 3 6
绝对,X RLA Oper,X $3F 3 7
绝对,Y RLA Oper,Y $3B 3 7
(间接,X) RLA (Oper,X) $23 2 8
(间接),Y RLA (Oper),Y $33 2 8

读改写(RMW)指令, 储存器数据循环左移再与累加器A做'与'运算. 影响FLAG: C(arry), Z(ero), S(ign). 伪C代码:

// ROL
uint16_t src = READ(address);
src <<= 1;
if (CF) src |= 0x1;
CHECK_CFLAG(src > 0xff);
uint8_t result8 = src;
WRITE(address, result8);
// AND
A &= result8;
CHECK_ZSFLAG(A);

RRA - Rotate Right then Add with Carry - ROR + ADC

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 RRA Oper $67 2 5
零页 ,X RRA Oper,X $77 2 6
绝对 RRA Oper $6F 3 6
绝对,X RRA Oper,X $7F 3 7
绝对,Y RRA Oper,Y $7B 3 7
(间接,X) RRA (Oper,X) $63 2 8
(间接),Y RRA (Oper),Y $73 2 8

读改写(RMW)指令, 储存器数据循环右移再加上累加器A和进位标记. 拿来用来计算A+V/2, 其中V是9位(支持到512)数据. 影响FLAG: C(arry), S(ign), Z(ero), (o)V(erflow), 伪C代码:

// ROR
uint16_t src = READ(address);
if (CF) src |= 0x100;
CF = src & 1;
src >> 1;
WRITE((uint8_t)src);
// ADC
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);

值得注意的是ADC的第一行的 (CF? 1: 0) 就是继承于ROR的第三行CF = 操作. 所以可以实现为:

// ROR
// ...
tmp_CF = src & 1;
// ...
// ADC
uint16_t result16 = A + src + tmp_CF;
// ...

SLO - Shift Left then 'Or' - ASL + ORA

助记符号: A |= (M <<= 1)

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 SLO Oper $07 2 5
零页 ,X SLO Oper,X $17 2 6
绝对 SLO Oper $0F 3 6
绝对,X SLO Oper,X $1F 3 7
绝对,Y SLO Oper,Y $1B 3 7
(间接,X) SLO (Oper,X) $03 2 8
(间接),Y SLO (Oper),Y $13 2 8

读改写(RMW)指令, 储存器数据算术左移一位然后和累加器A做'或'运算, 由于要和累加器计算, 所以没有单字节指令SLO A, 即寻址方式为'累加器A'的. 影响FLAG: C(arry), Z(ero), S(ign). 伪C代码:

// ASL
tmp = READ(adress);
CHECK_CFLAG(tmp>>7);
tmp <<= 1;
WRITE(address, tmpdt);
// ORA
A |= tmp;
CHECK_ZSFLAG(A);

SRE - Shift Right then "Exclusive-Or" - LSR + EOR

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 SRE Oper $47 2 5
零页 ,X SRE Oper,X $57 2 6
绝对 SRE Oper $4F 3 6
绝对,X SRE Oper,X $5F 3 7
绝对,Y SRE Oper,Y $5B 3 7
(间接,X) SRE (Oper,X) $43 2 8
(间接),Y SRE (Oper),Y $53 2 8

读改写(RMW)指令, 储存器数据逻辑右移一位然后和累加器A做'异或'运算, 由于要和累加器计算, 所以没有单字节指令SRE A, 即寻址方式为'累加器A'的. 影响FLAG: C(arry), Z(ero), S(ign). 伪C代码:

// LSR
tmp = READ(address);
CHECK_CFLAG(tmp & 1);
tmp >>= 1;
WRITE(address, tmpdt);
// EOR
A ^= tmp;
CHECK_ZSFLAG(A);

SHX[SXA] - 行为可能不一致, 不做实现

SHX & SHY, 不过"blOperg's CPU test rom v5"中测试了该指令

SHY[SYA] - 行为可能不一致, 不做实现

SHX & SHY, 不过"blOperg's CPU test rom v5"中测试了该指令

blOperg's CPU test rom v5

测试ROM: "blOperg's CPU test rom v5"中, 仅仅不通过以上两个指令.

LAS - 行为可能不一致, 不做实现

XAA - 行为可能不一致, 不做实现

AHX[SHA] - 行为可能不一致, 不做实现

TAS - 行为可能不一致, 不做实现

KIL(STP) Kill Stop - 指令会导致CPU停下来, 不做实现

REF