BlogFM icon indicating copy to clipboard operation
BlogFM copied to clipboard

Re: 从零开始的红白机模拟 - [06]基础指令

Open dustpg opened this issue 7 years ago • 6 comments

STEP3: CPU 指令实现 - 基础指令

这节就详细谈谈基础指令, 所谓'基础指令'只是自己随便命名的, 避免一节过长, 请勿对号入座.

指令周期

不同指令需要消耗不同的周期, 这很好理解. 不过就算相同的指令环境不同也会消耗不同周期:

  • 页面边界交叉(Page Boundary Crossed)
    • 页面边界交叉是指6502将内存划分为256个页面(8位机但是拥有16位地址空间).
    • 当访问不同页面时, 需要额外的指令周期去读取.
  • 跳转到不同页面(Branch Occurs Different Page)
    • 意思和上面的差不多
  • 这两个会在这一步的最后一节详细谈谈

LDA - Load "A"

寻址模式 汇编格式 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);

LDX - Load 'X'

寻址模式 汇编格式 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);

LDY - Load 'Y'

寻址模式 汇编格式 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);

STA - Store 'A'

寻址模式 汇编格式 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);

STX - Store 'X'

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 STX Oper 86 2 3
零页,Y STX Oper,Y 96 2 4
绝对 STX Oper 8E 3 4

将变址寄存器X的数送入存储器, 影响FLAG:(无), 伪C代码:

WRTIE(address, X);

STY - Store 'Y'

寻址模式 汇编格式 OP代码 指令字节 指令周期
零页 STY Oper 84 2 3
零页,X STY Oper,X 94 2 4
绝对 STY Oper 8C 3 4

将变址寄存器Y的数送入存储器, 影响FLAG:(无), 伪C代码:

WRTIE(address, Y);

ADC - Add with Carry

助记符号 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);

SBC - Subtract with Carry

助记符号 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);

INC - Increment memory

寻址模式 汇编格式 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);

DEC - Decrement memory

寻址模式 汇编格式 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);

AND - 'And' memory with A

寻址模式 汇编格式 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);

ORA - 'Or' memory with 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);

EOR - "Exclusive-Or" memory with 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);

INX - Increment X

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 INX E8 1 2

变址寄存器X内容+1, 影响FLAG:Z(ero),S(ign), 伪C代码:

++X;
CHECK_ZSFLAG(X);

DEX - Decrement X

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 DEX CA 1 2

变址寄存器X内容-1, 影响FLAG:Z(ero),S(ign), 伪C代码:

--X;
CHECK_ZSFLAG(X);

INY - Increment Y

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 INY C8 1 2

变址寄存器Y内容+1, 影响FLAG:Z(ero),S(ign), 伪C代码:

++Y;
CHECK_ZSFLAG(Y);

DEY - Decrement Y

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 DEY 88 1 2

变址寄存器Y内容-1, 影响FLAG:Z(ero),S(ign), 伪C代码:

--Y;
CHECK_ZSFLAG(Y);

TAX - Transfer A to X

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 TAX AA 1 2

将累加器A的内容送入变址寄存器X, 影响FLAG:Z(ero),S(ign), 伪C代码:

X = A;
CHECK_ZSFLAG(X);

TXA - Transfer X to A

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 TXA 8A 1 2

将变址寄存器X的内容送入累加器A, 影响FLAG:Z(ero),S(ign), 伪C代码:

A = X;
CHECK_ZSFLAG(A);

TAY - Transfer A to Y

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 TAY A8 1 2

将累加器A的内容送入变址寄存器Y, 影响FLAG:Z(ero),S(ign), 伪C代码:

Y = A;
CHECK_ZSFLAG(Y);

TYA - Transfer Y to A

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 TYA 98 1 2

将变址寄存器Y的内容送入累加器A, 影响FLAG:Z(ero),S(ign), 伪C代码:

A = Y;
CHECK_ZSFLAG(A);

TSX - Transfer SP to X

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 TSX BA 1 2

将栈指针SP内容送入变址寄存器X, 影响FLAG:Z(ero),S(ign), 伪C代码:

X = SP;
CHECK_ZSFLAG(X);

TXS - Transfer X to SP

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 TXS 9A 1 2

将变址寄存器X内容送入栈指针SP, 影响FLAG:, 伪C代码:

SP = X;

CLC - Clear Carry

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 CLC 18 1 2

清除进位标志C, 影响FLAG: C(arry), 伪C代码:

CF = 0;

SEC - Set Carry

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 SEC 38 1 2

设置进位标志C, 影响FLAG: C(arry), 伪C代码:

CF = 1;

CLD - Clear Decimal

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 CLD D8 1 2

清除十进制模式标志D, 影响FLAG: D(Decimal), 伪C代码:

DF = 0;

SED - Clear Decimal

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 SED F8 1 2

设置十进制模式标志D, 影响FLAG: D(Decimal), 伪C代码:

DF = 1

CLV - Clear Overflow

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 CLV B8 1 2

清除溢出标志V, 影响FLAG: (o)V(erflow), 伪C代码:

VF = 0;

CLI - Clear Interrupt-disable

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 CLI 58 1 2

清除中断禁止标志I, 影响FLAG: I(nterrupt-disable), 伪C代码:

IF = 0;

SEI - Set Interrupt-disable

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 SEI 78 1 2

设置中断禁止标志I, 影响FLAG: I(nterrupt-disable), 伪C代码:

IF = 1;

CMP - Compare memory with A

寻址模式 汇编格式 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);

CPX - Compare memory with X

寻址模式 汇编格式 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);

CPY - Compare memory with Y

寻址模式 汇编格式 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);

BIT - Bit test memory with A

寻址模式 汇编格式 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;

ASL - Arithmetic Shift Left

助记符号: 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);

LSR - Logical Shift Right

助记符号: 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);

ROL - Rotate Left

助记符号: ...|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);

ROR - Rotate Right

助记符号: ...|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);

PHA - Push A

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 PHA 48 1 3

累加器A压入栈顶(栈指针SP-1). 影响FLAG:(无), 伪C代码:

PUSH(A);

PLA - Pull(Pop) A

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 PLA 68 1 4

push的反操作, 在6502汇编称为pull, 不过自己还是习惯称为pop. 6502_cn这篇文档提到没有FLAG修改, 可能是笔误, 其他文档(比如这个)提到会影响S(ign) Z(ero) , 伪C代码:

A = POP();
CHECK_ZSFLAG(A);

PHP - Push Processor-status

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 PHP 08 1 3

将状态FLAG压入栈顶, 影响FLAG:(无), 伪C代码:

PUSH(P | FLAG_B | FLAG_R);

PLP - Pull Processor-status

寻址模式 汇编格式 OP代码 指令字节 指令周期
隐含 PLP 28 1 4

PHP逆操作, 影响FLAG: (是的), 伪C代码:

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

REF

dustpg avatar Sep 09 '18 05:09 dustpg

LDY指令的表格错了

fixecl avatar Oct 19 '18 08:10 fixecl

LDY指令的表格错了

感谢指出

dustpg avatar Oct 19 '18 08:10 dustpg

ERA - "Exclusive-Or" memory with A 存储器单元与累加器做或运算, 影响FLAG:Z(ero),S(ign), 伪C代码

发现这里漏了一个‘异’233

Slackre avatar Dec 05 '19 11:12 Slackre

image

fushang318 avatar May 12 '21 13:05 fushang318

image

fushang318 avatar May 15 '21 08:05 fushang318

and ora sbc 的 (间接),Y 指令周期数 都漏掉了 *

fushang318 avatar May 22 '21 01:05 fushang318