GB/CPU/LR35902 标准指令集

LR35902 拥有两套指令集, 一套是 8 位指令集, 另一套则是 16 位指令集, 其中 16 位指令集的操作码固定以 0xcb 作为前缀. 16 位指令集也常被称作扩展指令集, 因为原版 z80 CPU 不支持移位操作, 所以 LR35902 通过外接扩展电路的形式额外添加了移位指令.

关于其标准指令集与扩张指令集, 可以在网站 http://www.pastraiser.com/cpu/gameboy/gameboy_opcodes.html 上找到它的简要概览.

我们首先将聚焦在标准指令集上, 这套指令被同时应用在诸如 Intel 8080, z80 和 LR35902 等 CPU 上, 更是如今 x86 架构的前身. 请注意, 对 CPU 指令集的仿真是一个繁琐且不能出任何差错的工程, 初学者通常会花大量的时间在调式上. 更糟糕的是, 除非已经完成了整个项目, 不然几乎无法在开发阶段验证自己写的代码是否正确. 因此请做好准备, 并且在遇到困难时不必过于沮丧!

结构体定义

在开始之前, 定义如下的对 CPU 结构进行仿真的结构体, 后续所有的开发都将围绕该结构体进行.

pub struct Cpu {
    pub reg: Register,                // 寄存器
    pub mem: Rc<RefCell<dyn Memory>>, // 可访问的内存空间
    pub halted: bool,                 // 表明 CPU 是否处于工作状态
    pub ei: bool,                     // enable interrupt 的简写, 表明 CPU 是否接收硬件中断
}

同时为该结构体实现两个根据当前 PC 从内存中读取 u8 和 u16 的两个函数, 记得读取数据后需要准确地移动 PC: 如果读取的是 u8 数据则 PC 加 1, 如果是 u16 数据则 PC 加 2.

impl Cpu {
    fn imm(&mut self) -> u8 {
        let v = self.mem.borrow().get(self.reg.pc);
        self.reg.pc += 1;
        v
    }

    fn imm_word(&mut self) -> u16 {
        let v = self.mem.borrow().get_word(self.reg.pc);
        self.reg.pc += 2;
        v
    }
}

此处需要注意一个细节, u16 数据低位数据在内存前半部分, 高位数据在后半部分. 比如数字 0x0150, 其内存分布则是 0x50, 0x01. 这在计算机中被称为小端序(Little endian).

如何取指与执行

CPU 总是不断的根据当前 PC 进行取指过程, 并且在指令被取指后, 进入指令执行过程. 为 CPU 添加如下的指令执行函数:

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        ...
    }
}

其中 Opcode 为操作码, 不同的 Opcode 代表不同的指令, 在代码实现中需要使用 match 语句对 Opcode 进行匹配. 该函数返回一个 u32, 代表该指令所消耗的机器周期(CPU 执行该指令所花费的时间).

下面给出了标准指令的摘要与仿真实现. 如果该指令影响了标志位寄存器 Flag, 则会在指令描述中特别指明. 在此约定指令的书写方法如下:

Instruction (Parameter1) (Parameter2)
  • Instruction: 指令的英文缩写
  • Parameter1/Parameter2: 指令的参数

其中 Parameter 可以是如下几种情况之一:

  • 特定寄存器: 特定的目标寄存器, 可以是任意 8 位寄存器或 16 位组合寄存器之一.
  • r8: 8 位寄存器. 可以是寄存器 A, F, B, C, D, E, H 或 L.
  • d8: 8 位立即数. 立即数是指采用立即寻址方式指令中给出的数, 该数值紧跟在操作码之后.
  • r16: 16 位寄存器. 可以是寄存器 AF, BC, DE 或 HL.
  • d16: 16位立即数.
  • (r16): 以 16 位寄存器所存储数据视为内存地址取得的数
  • (d16): 以 16 位立即数所存储数据视为内存地址取得的数
  • (a8): 以 0xff00 与 8 位立即参数的或运算结果视为内存地址取得的数

8 位数据加载

LD r8, d8

1) 描述

将一个 8 位的立即参数写入到相应的 8 位寄存器中.

2) 标志位变化

3) 指令

下表分为 4 列, 第一列 Instruction 表示指令的缩写类型, 第二列 Parameters 表示详细的参数签名, 第三列 Opcode 为指令的操作码(16进制表示), 最后一列 Cycles 为指令所需消耗的时钟周期. 比如下表第一行, 就表示如果 CPU 从内存中读取到 0x06, 则将之视为 LD B, n 指令处理, 处理过程将消耗 8 个时钟周期. 后文同.

InstructionParametersOpcodeCycles
LDB, n068
LDC, n0e8
LDD, n168
LDE, n1e8
LDH, n268
LDL, n2e8
LD(HL), n368
LDA, n3e8

4) 代码实现

指令的仿真代码需填写入前文的 ex() 函数的 match 语句内. 数据加载的代码实现较为简单, 首先使用 self.imm() 读取一个 8 位立即数, 然后赋值到对应的寄存器即可. 稍微需要注意的是 Opcode 为 36 时, 立即数不是赋值给 HL 寄存器, 而是赋值给以 HL 寄存器存储数据为地址的内存中.

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x06 => self.reg.b = self.imm(),
        0x0e => self.reg.c = self.imm(),
        0x16 => self.reg.d = self.imm(),
        0x1e => self.reg.e = self.imm(),
        0x26 => self.reg.h = self.imm(),
        0x2e => self.reg.l = self.imm(),
        0x36 => {
            let a = self.reg.get_hl();
            let v = self.imm();
            self.mem.borrow_mut().set(a, v);
        }
        0x3e => self.reg.a = self.imm(),
        ...
    }
}

LD r8, r8

1) 描述

将一个 8 位寄存器的值写入到另一个 8 位寄存器.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDB, B404
LDB, C414
LDB, D424
LDB, E434
LDB, H444
LDB, L454
LDB, (HL)468
LDB, A474
LDC, B484
LDC, C494
LDC, D4A4
LDC, E4B4
LDC, H4C4
LDC, L4D4
LDC, (HL)4E8
LDC, A4F4
LDD, B504
LDD, C514
LDD, D524
LDD, E534
LDD, H544
LDD, L554
LDD, (HL)568
LDD, A574
LDE, B584
LDE, C594
LDE, D5A4
LDE, E5B4
LDE, H5C4
LDE, L5D4
LDE, (HL)5E8
LDE, A5F4
LDH, B604
LDH, C614
LDH, D624
LDH, E634
LDH, H644
LDH, L654
LDH, (HL)668
LDH, A674
LDL, B684
LDL, C694
LDL, D6A4
LDL, E6B4
LDL, H6C4
LDL, L6D4
LDL, (HL)6E8
LDL, A6F4
LD(HL), B708
LD(HL), C718
LD(HL), D728
LD(HL), E738
LD(HL), H748
LD(HL), L758
LD(HL), A778
LDA, B784
LDA, C794
LDA, D7A4
LDA, E7B4
LDA, H7C4
LDA, L7D4
LDA, (HL)7E8
LDA, A7F4
LD(BC), A028
LD(DE), A128
LDA, (BC)0a8
LDA, (DE)1a8

表中有一类指令可能会引起读者困惑, 以 0x40 Opcode 为例, 它可表示为 LD B, B, 其两个参数均是 B, 其含义是"读取寄存器 B 的值, 并写入寄存器 B". 从结果上来说, 它不会引起任何寄存器内容的变化, 但该操作仍然需要消耗 CPU 的时钟周期. 在后文会有专门章节对此进行介绍.

4) 代码实现

代码实现相对简单, 只需要读取对应的 8 位数据并写入目标寄存器即可.

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x40 => {}
        0x41 => self.reg.b = self.reg.c,
        0x42 => self.reg.b = self.reg.d,
        0x43 => self.reg.b = self.reg.e,
        0x44 => self.reg.b = self.reg.h,
        0x45 => self.reg.b = self.reg.l,
        0x46 => self.reg.b = self.mem.borrow().get(self.reg.get_hl()),
        0x47 => self.reg.b = self.reg.a,
        0x48 => self.reg.c = self.reg.b,
        0x49 => {}
        0x4a => self.reg.c = self.reg.d,
        0x4b => self.reg.c = self.reg.e,
        0x4c => self.reg.c = self.reg.h,
        0x4d => self.reg.c = self.reg.l,
        0x4e => self.reg.c = self.mem.borrow().get(self.reg.get_hl()),
        0x4f => self.reg.c = self.reg.a,
        0x50 => self.reg.d = self.reg.b,
        0x51 => self.reg.d = self.reg.c,
        0x52 => {}
        0x53 => self.reg.d = self.reg.e,
        0x54 => self.reg.d = self.reg.h,
        0x55 => self.reg.d = self.reg.l,
        0x56 => self.reg.d = self.mem.borrow().get(self.reg.get_hl()),
        0x57 => self.reg.d = self.reg.a,
        0x58 => self.reg.e = self.reg.b,
        0x59 => self.reg.e = self.reg.c,
        0x5a => self.reg.e = self.reg.d,
        0x5b => {}
        0x5c => self.reg.e = self.reg.h,
        0x5d => self.reg.e = self.reg.l,
        0x5e => self.reg.e = self.mem.borrow().get(self.reg.get_hl()),
        0x5f => self.reg.e = self.reg.a,
        0x60 => self.reg.h = self.reg.b,
        0x61 => self.reg.h = self.reg.c,
        0x62 => self.reg.h = self.reg.d,
        0x63 => self.reg.h = self.reg.e,
        0x64 => {}
        0x65 => self.reg.h = self.reg.l,
        0x66 => self.reg.h = self.mem.borrow().get(self.reg.get_hl()),
        0x67 => self.reg.h = self.reg.a,
        0x68 => self.reg.l = self.reg.b,
        0x69 => self.reg.l = self.reg.c,
        0x6a => self.reg.l = self.reg.d,
        0x6b => self.reg.l = self.reg.e,
        0x6c => self.reg.l = self.reg.h,
        0x6d => {}
        0x6e => self.reg.l = self.mem.borrow().get(self.reg.get_hl()),
        0x6f => self.reg.l = self.reg.a,
        0x70 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.b),
        0x71 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.c),
        0x72 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.d),
        0x73 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.e),
        0x74 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.h),
        0x75 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.l),
        0x77 => self.mem.borrow_mut().set(self.reg.get_hl(), self.reg.a),
        0x78 => self.reg.a = self.reg.b,
        0x79 => self.reg.a = self.reg.c,
        0x7a => self.reg.a = self.reg.d,
        0x7b => self.reg.a = self.reg.e,
        0x7c => self.reg.a = self.reg.h,
        0x7d => self.reg.a = self.reg.l,
        0x7e => self.reg.a = self.mem.borrow().get(self.reg.get_hl()),
        0x7f => {},
        0x02 => self.mem.borrow_mut().set(self.reg.get_bc(), self.reg.a),
        0x12 => self.mem.borrow_mut().set(self.reg.get_de(), self.reg.a),
        0x0a => self.reg.a = self.mem.borrow().get(self.reg.get_bc()),
        0x1a => self.reg.a = self.mem.borrow().get(self.reg.get_de()),
        ...
    }
}

LD (C), A

1) 描述

将寄存器 A 的值写入目标内存地址(由 0xff00 | 寄存器 C 指定).

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LD(C), Ae28

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xe2 => self.mem.borrow_mut().set(0xff00 | u16::from(self.reg.c), self.reg.a),
        ...
    }
}

LD A, (C)

1) 描述

将目标内存地址(由 0xff00 | 寄存器 C 指定)的值写入寄存器 A.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDA, (C)f28

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xf2 => self.reg.a = self.mem.borrow().get(0xff00 | u16::from(self.reg.c)),
        ...
    }
}

LD (HL+), A

1) 描述

将寄存器 A 写入目标内存地址(由 HL 寄存器指定), 同时 HL 自增.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LD(HL+), A228

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x22 => {
            let a = self.reg.get_hl();
            self.mem.borrow_mut().set(a, self.reg.a);
            self.reg.set_hl(a + 1);
        }
        ...
    }
}

LD (HL-), A

1) 描述

将寄存器 A 写入目标内存地址(由 HL 寄存器指定), 同时 HL 自减.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LD(HL-), A328

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x32 => {
            let a = self.reg.get_hl();
            self.mem.borrow_mut().set(a, self.reg.a);
            self.reg.set_hl(a - 1);
        }
        ...
    }
}

LD A, (HL+)

1) 描述

将目标内存地址(由 HL 寄存器指定)的值写入寄存器 A, 同时 HL 自增.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDA, (HL+)2a8

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x2a => {
            let v = self.reg.get_hl();
            self.reg.a = self.mem.borrow().get(v);
            self.reg.set_hl(v + 1);
        }
        ...
    }
}

LD A, (HL-)

1) 描述

将目标内存地址(由 HL 寄存器指定)的值写入寄存器 A, 同时 HL 自减.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDA, (HL-)3a8

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x3a => {
            let v = self.reg.get_hl();
            self.reg.a = self.mem.borrow().get(v);
            self.reg.set_hl(v - 1);
        }
        ...
    }
}

LD (d8), A

1) 描述

将寄存器 A 的值写入目标内存地址(由 0xff00 | 8 位立即参数指定).

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LD(d8), Ae012

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xe0 => {
            let a = 0xff00 | u16::from(self.imm());
            self.mem.borrow_mut().set(a, self.reg.a);
        }
        ...
    }
}

LD A, (a8)

1) 描述

将目标内存地址(由 0xff00 | 8 位立即参数指定)的值写入寄存器 A.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDA, (d8)f012

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xf0 => {
            let a = 0xff00 | u16::from(self.imm());
            self.reg.a = self.mem.borrow().get(a);
        }
        ...
    }
}

LD (d16), A

1) 描述

将寄存器 A 的值写入目标内存地址(由 16 位立即参数指定).

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LD(a16), Aea16

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xea => {
            let a = self.imm_word();
            self.mem.borrow_mut().set(a, self.reg.a);
        }
        ...
    }
}

LD A, (d16)

1) 描述

将目标内存地址(由 16 位立即参数指定)的值写入寄存器 A.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDA, (a16)fa16

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xfa => {
            let a = self.imm_word();
            self.reg.a = self.mem.borrow().get(a);
        }
        ...
    }
}

16 位数据加载

LD r16, d16

1) 描述

将 16 位立即参数写入相应 16 位寄存器中.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDBC, d160112
LDDE, d161112
LDHL, d162112
LDSP, d163112

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x01 | 0x11 | 0x21 | 0x31 => {
            let v = self.imm_word();
            match opcode {
                0x01 => self.reg.set_bc(v),
                0x11 => self.reg.set_de(v),
                0x21 => self.reg.set_hl(v),
                0x31 => self.reg.sp = v,
                _ => {}
            }
        }
        ...
    }
}

LD SP, HL

1) 描述

将寄存器 HL 写入到寄存器 SP.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LDSP, HLf98

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xf9 => self.reg.sp = self.reg.get_hl(),
        ...
    }
}

LD HL, SP + d8

1) 描述

将 SP 寄存器 + 有符号 8 位立即参数的结果写入寄存器 HL.

2) 标志位变化

  • Z - 置零
  • N - 置零
  • H - 第 3 位进位时, 则置位
  • C - 第 7 位进位时, 则置位

3) 指令

InstructionParametersOpcodeCycles
LDSP, d8f812

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xf8 => {
            let a = self.reg.sp;
            let b = i16::from(self.imm() as i8) as u16;
            self.reg.set_flag(C, (a & 0x00ff) + (b & 0x00ff) > 0x00ff);
            self.reg.set_flag(H, (a & 0x000f) + (b & 0x000f) > 0x000f);
            self.reg.set_flag(N, false);
            self.reg.set_flag(Z, false);
            self.reg.set_hl(a.wrapping_add(b));
        }
        ...
    }
}

LD (d16), SP

1) 描述

将 SP 寄存器的值写入目标内存地址(由 16 位立即参数指定).

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
LD(d16), SP0x0820

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x08 => {
            let a = self.imm_word();
            self.mem.borrow_mut().set_word(a, self.reg.sp);
        }
        ...
    }
}

PUSH

1) 描述

栈指令. 将相应 16 位寄存器的值入栈.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
PUSHBC0xc520
PUSHDE0xd520
PUSHHL0xe520
PUSHAF0xf520

4) 代码实现

impl Cpu {
    fn stack_push(&mut self, v: u16) {
        self.reg.sp -= 2;
        self.mem.borrow_mut().set_word(self.reg.sp, v);
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc5 => self.stack_add(self.reg.get_bc()),
        0xd5 => self.stack_add(self.reg.get_de()),
        0xe5 => self.stack_add(self.reg.get_hl()),
        0xf5 => self.stack_add(self.reg.get_af()),
        ...
    }
}

POP

1) 描述

栈指令. 出栈并将数据写入相应 16 位寄存器.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
POPBC0xc120
POPDE0xd120
POPHL0xe120
POPAF0xf120

4) 代码实现

impl Cpu {
    fn stack_pop(&mut self) -> u16 {
        let r = self.mem.borrow().get_word(self.reg.sp);
        self.reg.sp += 2;
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc1 | 0xf1 | 0xd1 | 0xe1 => {
            let v = self.stack_pop();
            match opcode {
                0xc1 => self.reg.set_bc(v),
                0xd1 => self.reg.set_de(v),
                0xe1 => self.reg.set_hl(v),
                0xf1 => self.reg.set_af(v),
                _ => {}
            }
        }
        ...
    }
}

8 位算数逻辑运算

ADD A, r8/d8

1) 描述

算术加法运算. 将指定的 8 位数与寄存器 A 相加, 并将结果回写入寄存器 A.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 第 3 位进位时, 则置位
  • C - 第 7 位进位时, 则置位

3) 指令

InstructionParametersOpcodeCycles
ADDB0x804
ADDC0x814
ADDD0x824
ADDE0x834
ADDH0x844
ADDL0x854
ADD(HL)0x868
ADDA0x874
ADD(d8)0xc68

4) 代码实现

impl Cpu {
    // Add n to A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Set if carry from bit 3.
    // C - Set if carry from bit 7.
    fn alu_add(&mut self, n: u8) {
        let a = self.reg.a;
        let r = a.wrapping_add(n);
        self.reg.set_flag(C, u16::from(a) + u16::from(n) > 0xff);
        self.reg.set_flag(H, (a & 0x0f) + (n & 0x0f) > 0x0f);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x80 => self.alu_add(self.reg.b),
        0x81 => self.alu_add(self.reg.c),
        0x82 => self.alu_add(self.reg.d),
        0x83 => self.alu_add(self.reg.e),
        0x84 => self.alu_add(self.reg.h),
        0x85 => self.alu_add(self.reg.l),
        0x86 => {
            let v = self.mem.borrow().get(self.reg.get_hl());
            self.alu_add(v);
        }
        0x87 => self.alu_add(self.reg.a),
        0xc6 => {
            let v = self.imm();
            self.alu_add(v);
        }
        ...
    }
}

ADC A, r8/d8

1) 描述

带进位的算术加法运算.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 第 3 位进位时, 则置位
  • C - 第 7 位进位时, 则置位

3) 指令

InstructionParametersOpcodeCycles
ADCB0x884
ADCC0x894
ADCD0x8a4
ADCE0x8b4
ADCH0x8c4
ADCL0x8d4
ADC(HL)0x8e8
ADCA0x8f4
ADC(d8)0xce8

4) 代码实现

impl Cpu {
    // Add n to A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Set if carry from bit 3.
    // C - Set if carry from bit 7.
    fn alu_adc(&mut self, n: u8) {
        let a = self.reg.a;
        let c = u8::from(self.reg.get_flag(C));
        let r = a.wrapping_add(n).wrapping_add(c);
        self.reg
            .set_flag(C, u16::from(a) + u16::from(n) + u16::from(c) > 0xff);
        self.reg
            .set_flag(H, (a & 0x0f) + (n & 0x0f) + (c & 0x0f) > 0x0f);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x88 => self.alu_adc(self.reg.b),
        0x89 => self.alu_adc(self.reg.c),
        0x8a => self.alu_adc(self.reg.d),
        0x8b => self.alu_adc(self.reg.e),
        0x8c => self.alu_adc(self.reg.h),
        0x8d => self.alu_adc(self.reg.l),
        0x8e => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_adc(a);
        }
        0x8f => self.alu_adc(self.reg.a),
        0xce => {
            let v = self.imm();
            self.alu_adc(v);
        }
        ...
    }
}

SUB A, r8/d8

1) 描述

算术减法运算.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置位
  • H - 第 4 位借位时, 则置位
  • C - 没有发生借位, 则置位

3) 指令

InstructionParametersOpcodeCycles
SUBB0x904
SUBC0x914
SUBD0x924
SUBE0x934
SUBH0x944
SUBL0x954
SUB(HL)0x968
SUBA0x974
SUB(d8)0xd68

4) 代码实现

impl Cpu {
    // Subtract n from A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Set.
    // H - Set if no borrow from bit 4.
    // C - Set if no borrow
    fn alu_sub(&mut self, n: u8) {
        let a = self.reg.a;
        let r = a.wrapping_sub(n);
        self.reg.set_flag(C, u16::from(a) < u16::from(n));
        self.reg.set_flag(H, (a & 0x0f) < (n & 0x0f));
        self.reg.set_flag(N, true);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x90 => self.alu_sub(self.reg.b),
        0x91 => self.alu_sub(self.reg.c),
        0x92 => self.alu_sub(self.reg.d),
        0x93 => self.alu_sub(self.reg.e),
        0x94 => self.alu_sub(self.reg.h),
        0x95 => self.alu_sub(self.reg.l),
        0x96 => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_sub(a);
        }
        0x97 => self.alu_sub(self.reg.a),
        0xd6 => {
            let v = self.imm();
            self.alu_sub(v);
        }
        ...
    }
}

SBC A, r8/d8

1) 描述

带借位的算术减法运算.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置位
  • H - 第 4 位借位时, 则置位
  • C - 没有发生借位, 则置位

3) 指令

InstructionParametersOpcodeCycles
SBCB0x984
SBCC0x994
SBCD0x9a4
SBCE0x9b4
SBCH0x9c4
SBCL0x9d4
SBC(HL)0x9e8
SBCA0x9f4
SBC(d8)0xde8

4) 代码实现

impl Cpu {
    // Subtract n + Carry flag from A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Set.
    // H - Set if no borrow from bit 4.
    // C - Set if no borrow.
    fn alu_sbc(&mut self, n: u8) {
        let a = self.reg.a;
        let c = u8::from(self.reg.get_flag(C));
        let r = a.wrapping_sub(n).wrapping_sub(c);
        self.reg
            .set_flag(C, u16::from(a) < u16::from(n) + u16::from(c));
        self.reg.set_flag(H, (a & 0x0f) < (n & 0x0f) + c);
        self.reg.set_flag(N, true);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x98 => self.alu_sbc(self.reg.b),
        0x99 => self.alu_sbc(self.reg.c),
        0x9a => self.alu_sbc(self.reg.d),
        0x9b => self.alu_sbc(self.reg.e),
        0x9c => self.alu_sbc(self.reg.h),
        0x9d => self.alu_sbc(self.reg.l),
        0x9e => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_sbc(a);
        }
        0x9f => self.alu_sbc(self.reg.a),
        0xde => {
            let v = self.imm();
            self.alu_sbc(v);
        }
        ...
    }
}

AND A, r8/d8

1) 描述

逻辑和运算.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置位
  • C - 置零

3) 指令

InstructionParametersOpcodeCycles
ANDB0xa04
ANDC0xa14
ANDD0xa24
ANDE0xa34
ANDH0xa44
ANDL0xa54
AND(HL)0xa68
ANDA0xa74
AND(d8)0xe68

4) 代码实现

impl Cpu {
    // Logically AND n with A, result in A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Set.
    // C - Reset
    fn alu_and(&mut self, n: u8) {
        let r = self.reg.a & n;
        self.reg.set_flag(C, false);
        self.reg.set_flag(H, true);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xa0 => self.alu_and(self.reg.b),
        0xa1 => self.alu_and(self.reg.c),
        0xa2 => self.alu_and(self.reg.d),
        0xa3 => self.alu_and(self.reg.e),
        0xa4 => self.alu_and(self.reg.h),
        0xa5 => self.alu_and(self.reg.l),
        0xa6 => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_and(a);
        }
        0xa7 => self.alu_and(self.reg.a),
        0xe6 => {
            let v = self.imm();
            self.alu_and(v);
        }
        ...
    }
}

OR A, r8/d8

1) 描述

逻辑或运算.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置零
  • C - 置零

3) 指令

InstructionParametersOpcodeCycles
ORB0xb04
ORC0xb14
ORD0xb24
ORE0xb34
ORH0xb44
ORL0xb54
OR(HL)0xb68
ORA0xb74
OR(d8)0xf68

4) 代码实现

impl Cpu {
    // Logical OR n with register A, result in A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Reset.
    // C - Reset.
    fn alu_or(&mut self, n: u8) {
        let r = self.reg.a | n;
        self.reg.set_flag(C, false);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xb0 => self.alu_or(self.reg.b),
        0xb1 => self.alu_or(self.reg.c),
        0xb2 => self.alu_or(self.reg.d),
        0xb3 => self.alu_or(self.reg.e),
        0xb4 => self.alu_or(self.reg.h),
        0xb5 => self.alu_or(self.reg.l),
        0xb6 => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_or(a);
        }
        0xb7 => self.alu_or(self.reg.a),
        0xf6 => {
            let v = self.imm();
            self.alu_or(v);
        }
        ...
    }
}

XOR A, r8/d8

1) 描述

逻辑异或运算.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置零
  • C - 置零

3) 指令

InstructionParametersOpcodeCycles
XORB0xa84
XORC0xa94
XORD0xaa4
XORE0xab4
XORH0xac4
XORL0xad4
XOR(HL)0xae8
XORA0xaf4
XOR(d8)0xee8

4) 代码实现

impl Cpu {
    // Logical exclusive OR n with register A, result in A.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Reset.
    // C - Reset.
    fn alu_xor(&mut self, n: u8) {
        let r = self.reg.a ^ n;
        self.reg.set_flag(C, false);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xa8 => self.alu_xor(self.reg.b),
        0xa9 => self.alu_xor(self.reg.c),
        0xaa => self.alu_xor(self.reg.d),
        0xab => self.alu_xor(self.reg.e),
        0xac => self.alu_xor(self.reg.h),
        0xad => self.alu_xor(self.reg.l),
        0xae => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_xor(a);
        }
        0xaf => self.alu_xor(self.reg.a),
        0xee => {
            let v = self.imm();
            self.alu_xor(v);
        }
        ...
    }
}

CP A, r8/d8

1) 描述

将寄存器 A 与相应 8 位数据进行比较. 该指令类似一个 A - n 减法指令, 但计算结果被丢弃了.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置位
  • H - 第 4 位借位时, 则置位
  • C - 没有发生借位, 则置位

3) 指令

InstructionParametersOpcodeCycles
CPB0xb84
CPC0xb94
CPD0xba4
CPE0xbb4
CPH0xbc4
CPL0xbd4
CP(HL)0xbe8
CPA0xbf4
CP(d8)0xfe8

4) 代码实现

impl Cpu {
    // Compare A with n. This is basically an A - n subtraction
    // instruction but the results are thrown away.
    // n = A,B,C,D,E,H,L,(HL),#
    //
    // Flags affected:
    // Z - Set if result is zero. (Set if A = n.)
    // N - Set.
    // H - Set if no borrow from bit 4.
    // C - Set for no borrow. (Set if A < n.)
    fn alu_cp(&mut self, n: u8) {
        let r = self.reg.a;
        self.alu_sub(n);
        self.reg.a = r;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xb8 => self.alu_cp(self.reg.b),
        0xb9 => self.alu_cp(self.reg.c),
        0xba => self.alu_cp(self.reg.d),
        0xbb => self.alu_cp(self.reg.e),
        0xbc => self.alu_cp(self.reg.h),
        0xbd => self.alu_cp(self.reg.l),
        0xbe => {
            let a = self.mem.borrow().get(self.reg.get_hl());
            self.alu_cp(a);
        }
        0xbf => self.alu_cp(self.reg.a),
        0xfe => {
            let v = self.imm();
            self.alu_cp(v);
        }
        ...
    }
}

INC r8

1) 描述

相应 8 位寄存器自增.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 第 3 位进位时, 则置位
  • C - 保持不变

3) 指令

InstructionParametersOpcodeCycles
INCB0x044
INCC0x0c4
INCD0x144
INCE0x1c4
INCH0x244
INCL0x2c4
INC(HL)0x3412
INCA0x3c4

4) 代码实现

impl Cpu {
    // Increment register n.
    // n = A,B,C,D,E,H,L,(HL)
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Set if carry from bit 3.
    // C - Not affected.
    fn alu_inc(&mut self, a: u8) -> u8 {
        let r = a.wrapping_add(1);
        self.reg.set_flag(H, (a & 0x0f) + 0x01 > 0x0f);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x04 => self.reg.b = self.alu_inc(self.reg.b),
        0x0c => self.reg.c = self.alu_inc(self.reg.c),
        0x14 => self.reg.d = self.alu_inc(self.reg.d),
        0x1c => self.reg.e = self.alu_inc(self.reg.e),
        0x24 => self.reg.h = self.alu_inc(self.reg.h),
        0x2c => self.reg.l = self.alu_inc(self.reg.l),
        0x34 => {
            let a = self.reg.get_hl();
            let v = self.mem.borrow().get(a);
            let h = self.alu_inc(v);
            self.mem.borrow_mut().set(a, h);
        }
        0x3c => self.reg.a = self.alu_inc(self.reg.a),
        ...
    }
}

DEC r8

1) 描述

相应 8 位寄存器自减.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置位
  • H - 第 4 位借位时, 则置位
  • C - 保持不变

3) 指令

InstructionParametersOpcodeCycles
DECB0x054
DECC0x0d4
DECD0x154
DECE0x1d4
DECH0x254
DECL0x2d4
DEC(HL)0x3512
DECA0x3d4

4) 代码实现

impl Cpu {
    // Decrement register n.
    // n = A,B,C,D,E,H,L,(HL)
    //
    // Flags affected:
    // Z - Set if reselt is zero.
    // N - Set.
    // H - Set if no borrow from bit 4.
    // C - Not affected
    fn alu_dec(&mut self, a: u8) -> u8 {
        let r = a.wrapping_sub(1);
        self.reg.set_flag(H, a.trailing_zeros() >= 4);
        self.reg.set_flag(N, true);
        self.reg.set_flag(Z, r == 0);
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x05 => self.reg.b = self.alu_dec(self.reg.b),
        0x0d => self.reg.c = self.alu_dec(self.reg.c),
        0x15 => self.reg.d = self.alu_dec(self.reg.d),
        0x1d => self.reg.e = self.alu_dec(self.reg.e),
        0x25 => self.reg.h = self.alu_dec(self.reg.h),
        0x2d => self.reg.l = self.alu_dec(self.reg.l),
        0x35 => {
            let a = self.reg.get_hl();
            let v = self.mem.borrow().get(a);
            let h = self.alu_dec(v);
            self.mem.borrow_mut().set(a, h);
        }
        0x3d => self.reg.a = self.alu_dec(self.reg.a),
        ...
    }
}

16 位算数逻辑运算

ADD HL, r16

1) 描述

算数加法运算. 将寄存器 HL 与相应 16 位数据相加, 并将结果写入 HL.

2) 标志位变化

  • Z - 保持不变
  • N - 置零
  • H - 第 11 位进位时, 则置位
  • C - 第 15 位进位时, 则置位

3) 指令

InstructionParametersOpcodeCycles
ADD(HL), BC0x098
ADD(HL), DE0x198
ADD(HL), HL0x298
ADD(HL), SP0x398

4) 代码实现

impl Cpu {
    // Add n to HL
    // n = BC,DE,HL,SP
    //
    // Flags affected:
    // Z - Not affected.
    // N - Reset.
    // H - Set if carry from bit 11.
    // C - Set if carry from bit 15.
    fn alu_add_hl(&mut self, n: u16) {
        let a = self.reg.get_hl();
        let r = a.wrapping_add(n);
        self.reg.set_flag(C, a > 0xffff - n);
        self.reg.set_flag(H, (a & 0x0fff) + (n & 0x0fff) > 0x0fff);
        self.reg.set_flag(N, false);
        self.reg.set_hl(r);
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x09 => self.alu_add_hl(self.reg.get_bc()),
        0x19 => self.alu_add_hl(self.reg.get_de()),
        0x29 => self.alu_add_hl(self.reg.get_hl()),
        0x39 => self.alu_add_hl(self.reg.sp),
        ...
    }
}

ADD SP, d8

1) 描述

算数加法运算. 将寄存器 SP 与 8 位立即参数相加, 并将结果写入 SP. 立即参数以有符号 8 整数表示.

2) 标志位变化

  • Z - 置零
  • N - 置零
  • H - 第 4 位进位时, 则置位
  • C - 第 7 位进位时, 则置位

3) 指令

InstructionParametersOpcodeCycles
ADDSP, (d8)0xe84

4) 代码实现

impl Cpu {
    // Add n to Stack Pointer (SP).
    // n = one byte signed immediate value (#).
    //
    // Flags affected:
    // Z - Reset.
    // N - Reset.
    // H - Set or reset according to operation.
    // C - Set or reset according to operation.
    fn alu_add_sp(&mut self) {
        let a = self.reg.sp;
        let b = i16::from(self.imm() as i8) as u16;
        self.reg.set_flag(C, (a & 0x00ff) + (b & 0x00ff) > 0x00ff);
        self.reg.set_flag(H, (a & 0x000f) + (b & 0x000f) > 0x000f);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, false);
        self.reg.sp = a.wrapping_add(b);
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xe8 => self.alu_add_sp(),
        ...
    }
}

INC r16

1) 描述

相应 16 位寄存器自增.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
INCBC0x038
INCDE0x138
INCHL0x238
INCSP0x338

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x03 => {
            let v = self.reg.get_bc().wrapping_add(1);
            self.reg.set_bc(v);
        }
        0x13 => {
            let v = self.reg.get_de().wrapping_add(1);
            self.reg.set_de(v);
        }
        0x23 => {
            let v = self.reg.get_hl().wrapping_add(1);
            self.reg.set_hl(v);
        }
        0x33 => {
            let v = self.reg.sp.wrapping_add(1);
            self.reg.sp = v;
        }
        ...
    }
}

DEC r16

1) 描述

相应 16 位寄存器自减.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
DECBC0x0b8
DECDE0x1b8
DECHL0x2b8
DECSP0x3b8

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x0b => {
            let v = self.reg.get_bc().wrapping_sub(1);
            self.reg.set_bc(v);
        }
        0x1b => {
            let v = self.reg.get_de().wrapping_sub(1);
            self.reg.set_de(v);
        }
        0x2b => {
            let v = self.reg.get_hl().wrapping_sub(1);
            self.reg.set_hl(v);
        }
        0x3b => {
            let v = self.reg.sp.wrapping_sub(1);
            self.reg.sp = v;
        }
        ...
    }
}

杂项

DAA

1) 描述

该指令调整寄存器 A, 以便获得二进制编码十进制(BCD)的正确表示.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 保持不变
  • H - 置零
  • C - 根据操作判断

3) 指令

InstructionParametersOpcodeCycles
DAA-0x274

4) 代码实现

impl Cpu {
    // Decimal adjust register A. This instruction adjusts register
    // A so that the correct representation of Binary
    // Coded Decimal (BCD) is obtained.
    //
    // Flags affected:
    // Z - Set if register A is zero.
    // N - Not affected.
    // H - Reset.
    // C - Set or reset according to operation
    fn alu_daa(&mut self) {
        let mut a = self.reg.a;
        let mut adjust = if self.reg.get_flag(C) { 0x60 } else { 0x00 };
        if self.reg.get_flag(H) {
            adjust |= 0x06;
        };
        if !self.reg.get_flag(N) {
            if a & 0x0f > 0x09 {
                adjust |= 0x06;
            };
            if a > 0x99 {
                adjust |= 0x60;
            };
            a = a.wrapping_add(adjust);
        } else {
            a = a.wrapping_sub(adjust);
        }
        self.reg.set_flag(C, adjust >= 0x60);
        self.reg.set_flag(H, false);
        self.reg.set_flag(Z, a == 0x00);
        self.reg.a = a;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x27 => self.alu_daa(),
        ...
    }
}

CPL

1) 描述

对寄存器 A 取反.

2) 标志位变化

  • Z - 保持不变
  • N - 置位
  • H - 置位
  • C - 保持不变

3) 指令

InstructionParametersOpcodeCycles
CPL-0x2f4

4) 代码实现

impl Cpu {
    // Complement A register. (Flip all bits.)
    //
    // Flags affected:
    // Z - Not affected.
    // N - Set.
    // H - Set.
    // C - Not affected.
    fn alu_cpl(&mut self) {
        self.reg.a = !self.reg.a;
        self.reg.set_flag(H, true);
        self.reg.set_flag(N, true);
    }
}


fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x2f => self.alu_cpl(),
        ...
    }
}

CCF

1) 描述

对进位标志位取反. 如果当前进位标志位为 1, 则置零;否则置位.

2) 标志位变化

  • Z - 保持不变
  • N - 置零
  • H - 置零
  • C - 取反

3) 指令

InstructionParametersOpcodeCycles
CCF-0x3f4

4) 代码实现

impl Cpu {
    // Complement carry flag. If C flag is set, then reset it.
    // If C flag is reset, then set it.
    // Flags affected:
    //
    // Z - Not affected.
    // N - Reset.
    // H - Reset.
    // C - Complemented.
    fn alu_ccf(&mut self) {
        let v = !self.reg.get_flag(C);
        self.reg.set_flag(C, v);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x3f => self.alu_ccf(),
        ...
    }
}

SCF

1) 描述

置位进位标志位.

2) 标志位变化

  • Z - 保持不变
  • N - 置零
  • H - 置零
  • C - 置位

3) 指令

InstructionParametersOpcodeCycles
SCF-0x374

4) 代码实现

impl Cpu {
    // Set Carry flag.
    //
    // Flags affected:
    // Z - Not affected.
    // N - Reset.
    // H - Reset.
    // C - Set.
    fn alu_scf(&mut self) {
        self.reg.set_flag(C, true);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x37 => self.alu_scf(),
        ...
    }
}

NOP

1) 描述

不做操作.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
NOP-0x0020

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x00 => {},
        ...
    }
}

HALT

1) 描述

关闭 CPU, 直到发生新的中断事件. 在程序开发中尽可能使用它可以降低能耗.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
HALT-0x764

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x76 => self.halted = true,
        ...
    }
}

STOP

1) 描述

在按下按钮之前暂停 CPU 和 LCD 显示. 仿真器实现时无需做特殊处理.

2) 标志位变化

无.

3) 指令

InstructionParametersOpcodeCycles
STOP-0x104

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x10 => {},
        ...
    }
}

DI/EI

1) 描述

DI(Disable Interrupt)指令禁用中断但不立即禁用. 执行 DI 后在下一个指令时禁用中断. EI(Enable Interrupt)指令启用中断, 该指令声明启用中断但不能立即执行. 执行 EI 后, 在下一个指令时启用中断.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
DI-0xf34
EI-0xfb4

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xf3 => self.ei = false,
        0xfb => self.ei = true,
        ...
    }
}

循环和移位操作

移位操作是位操作的一种. 移位运算可以在二进制的基础上对数字进行平移. 按照平移的方向和填充数字的规则分为多种: 左移, 带符号右移, 无符号右移, 循环左移, 循环右移等.

左移运算是将一个二进制位的操作数按指定移动的位数向左移动, 移出位被丢弃, 右边移出的空位一律补 0. 右移运算是将一个二进制位的操作数按指定移动的位数向右移动, 移出位被丢弃, 左边移出的空位一律补 0, 或者补符号位, 这由不同的机器而定. 在使用补码作为机器数的机器中, 正数的符号位为 0, 负数的符号位为 1.

RLCA

1) 描述

按位左移运算. 最高位移动至溢出标志位.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置零
  • C - 原始值的最高位.

3) 指令

InstructionParametersOpcodeCycles
RLCA-0x074

4) 代码实现

impl Cpu {
    // Rotate A left. Old bit 7 to Carry flag.
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Reset.
    // C - Contains old bit 7 data.
    fn alu_rlc(&mut self, a: u8) -> u8 {
        let c = (a & 0x80) >> 7 == 0x01;
        let r = (a << 1) | u8::from(c);
        self.reg.set_flag(C, c);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x07 => {
            self.reg.a = self.alu_rlc(self.reg.a);
            self.reg.set_flag(Z, false);
        }
        ...
    }
}

RLA

1) 描述

按位左移运算. 溢出标志位补充最低位, 同时最高位移动至溢出标志位.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置零
  • C - 原始值的最高位.

3) 指令

InstructionParametersOpcodeCycles
RLA-0x174

4) 代码实现

impl Cpu {
    // Rotate A left through Carry flag.
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Reset.
    // C - Contains old bit 7 data.
    fn alu_rl(&mut self, a: u8) -> u8 {
        let c = (a & 0x80) >> 7 == 0x01;
        let r = (a << 1) + u8::from(self.reg.get_flag(C));
        self.reg.set_flag(C, c);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x17 => {
            self.reg.a = self.alu_rl(self.reg.a);
            self.reg.set_flag(Z, false);
        }
        ...
    }
}

RRCA

1) 描述

按位右移运算. 最低位移动至溢出标志位.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置零
  • C - 原始值的最低位

3) 指令

InstructionParametersOpcodeCycles
RRCA-0x0f4

4) 代码实现

impl Cpu {
    // Rotate A right. Old bit 0 to Carry flag.
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Reset.
    // C - Contains old bit 0 data
    fn alu_rrc(&mut self, a: u8) -> u8 {
        let c = a & 0x01 == 0x01;
        let r = if c { 0x80 | (a >> 1) } else { (a >> 1) };
        self.reg.set_flag(C, c);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x0f => {
            self.reg.a = self.alu_rrc(self.reg.a);
            self.reg.set_flag(Z, false);
        }
        ...
    }
}

RRA

1) 描述

按位右移运算. 溢出标志位移动至最高位, 同时最低位移动至溢出标志位.

2) 标志位变化

  • Z - 计算结果为零, 则置位
  • N - 置零
  • H - 置零
  • C - 原始数据最低位

3) 指令

InstructionParametersOpcodeCycles
RRA-0x1f4

4) 代码实现

impl Cpu {
    // Rotate A right through Carry flag.
    //
    // Flags affected:
    // Z - Set if result is zero.
    // N - Reset.
    // H - Reset.
    // C - Contains old bit 0 data.
    fn alu_rr(&mut self, a: u8) -> u8 {
        let c = a & 0x01 == 0x01;
        let r = if self.reg.get_flag(C) {
            0x80 | (a >> 1)
        } else {
            a >> 1
        };
        self.reg.set_flag(C, c);
        self.reg.set_flag(H, false);
        self.reg.set_flag(N, false);
        self.reg.set_flag(Z, r == 0x00);
        r
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x1f => {
            self.reg.a = self.alu_rr(self.reg.a);
            self.reg.set_flag(Z, false);
        }
        ...
    }
}

分支跳转

在正式介绍 LR35902 的分支/跳转指令前, 先对该处理器的分支指令类型进行简单的介绍. 对于绝大部分处理器来说, 分支指令常常被分类为如下两类:

  • 无条件分支跳转. 该类型的指令是指无需判断条件一定会发生的跳转指令. 而按照跳转的目标地址计算方式, 还可以被分为以下两种情况: – 无条件直接地址跳转. 该类型的跳转目标地址是直接从指令编码的立即参数中获得的. – 无条件间接地址跳转. 该类型的跳转目标地址从寄存器中取得(或添加一定量的计算).
  • 有条件分支跳转. 该类型的指令是指在跳转前需要判断条件是否成立而决定是否发生跳转的指令. 同样按照跳转的目标地址计算方式, 还可以被分为以下两种情况: – 带条件直接地址跳转. 该类型的跳转目标地址是直接从指令编码的立即参数中获得的. – 带条件间接地址跳转. 该类型的跳转目标地址从寄存器中取得(或添加一定量的计算).

对于带条件分支/跳转指令而言, 是否发生跳转, 处理器将消耗不同的时钟周期. 理论上只有在执行阶段完成后, 才能够确切的解析出该指令消耗的时钟周期和目标跳转地址, 在仿真器的实现过程中需要特别注意. 对于现实世界的处理器而言, 为了提高性能, 在取指时会使用分支预测(Branch Prediction)技术. 该技术预测分支指令是否需要跳转以及跳转地址是什么, 也就是跳转的"方向"和"地址". 取指时对指令进行预测的方式被称为预测取指(Speculative Fetch), 对预取的指令进行执行也被称为预测执行(Speculative Exectution). 现代处理器已经可以非常高效的对分支进行预测, 预测正确率一般在 90% 到 99%.

JUMP

1)描述

跳转到指定的地址. 地址由 16 位立即参数指定.

2)标志位变化

3)指令

InstructionParametersOpcodeCycles
JUMP(d16)0xc312

4)代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc3 => self.reg.pc = self.imm_word(),
        0xe9 => self.reg.pc = self.reg.get_hl(),
        ...
    }
}

JUMP IF

1)描述

带条件的 JUMP 指令. 条件由下表指定.

2)标志位变化

3)指令

InstructionParametersOpcodeCycles
JUMP IF Z == 0(d16)0xc212
JUMP IF z != 0(d16)0xca12
JUMP IF c == 0(d16)0xd212
JUMP IF c != 0(d16)0xda12

4)代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc2 | 0xca | 0xd2 | 0xda => {
            let pc = self.imm_word();
            let cond = match opcode {
                0xc2 => !self.reg.get_flag(Z),
                0xca => self.reg.get_flag(Z),
                0xd2 => !self.reg.get_flag(C),
                0xda => self.reg.get_flag(C),
                _ => panic!(""),
            };
            if cond {
                self.reg.pc = pc;
            }
        }
        ...
    }
}

JR

1) 描述

读取一个 8 位有符号立即参数 n, 并跳转到 pc + n 位置.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
JR(d8)0x188

4)代码实现

impl Cpu {
    // Add n to current address and jump to it.
    // n = one byte signed immediate value
    fn alu_jr(&mut self, n: u8) {
        let n = n as i8;
        self.reg.pc = ((u32::from(self.reg.pc) as i32) + i32::from(n)) as u16;
    }
}

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x18 => {
            let n = self.imm();
            self.alu_jr(n);
        }
        ...
    }
}

JR IF

1) 描述

带条件的 JR 指令. 条件由下表指定.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
JR IF z == 0-0x208
JR IF z == 1-0x288
JR IF c == 0-0x308
JR IF c == 1-0x388

4)代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0x20 | 0x28 | 0x30 | 0x38 => {
            let cond = match opcode {
                0x20 => !self.reg.get_flag(Z),
                0x28 => self.reg.get_flag(Z),
                0x30 => !self.reg.get_flag(C),
                0x38 => self.reg.get_flag(C),
                _ => panic!(""),
            };
            let n = self.imm();
            if cond {
                self.alu_jr(n);
            }
        }
        ...
    }
}

调用

调用命令通常泛指 CALL 类型的指令. 它们用于处理主程序与子程序之间的调用关系. 经典的主程序/子程序切换流程如下图所示.

img

子程序是用于完成特定功能的一段程序. 当主程序(调用程序)需要执行这个功能时, 采用 CALL 调用指令转移到该子程序的起始处执行. 当运行完子程序功能后, 采用 RET 返回指令回到主程序继续执行.

CALL

1) 描述

将下一条指令的地址压入堆栈, 并跳转到相应地址(由 16 位立即参数指定). 类似高级语言中的函数调用.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
CALL-0xcd12

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xcd => {
            let nn = self.imm_word();
            self.stack_add(self.reg.pc);
            self.reg.pc = nn;
        }
        ...
    }
}

CALL IF

1) 描述

带条件的 CALL 指令. 条件由下表指定.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
CALL IF z == 0-0xc412
CALL IF z == 1-0xcc12
CALL IF c == 0-0xd412
CALL IF c == 1-0xdc12

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc4 | 0xcc | 0xd4 | 0xdc => {
            let cond = match opcode {
                0xc4 => !self.reg.get_flag(Z),
                0xcc => self.reg.get_flag(Z),
                0xd4 => !self.reg.get_flag(C),
                0xdc => self.reg.get_flag(C),
                _ => panic!(""),
            };
            let nn = self.imm_word();
            if cond {
                self.stack_add(self.reg.pc);
                self.reg.pc = nn;
            }
        }
        ...
    }
}

复位

RST

1) 描述

将当前地址推送到堆栈, 并跳转到某个固定地址. 地址见下表.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
RST0x000xc732
RST0x080xcf32
RST0x100xd732
RST0x180xdf32
RST0x200xe732
RST0x280xef32
RST0x300xf732
RST0x380xcf32

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc7 => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x00;
        }
        0xcf => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x08;
        }
        0xd7 => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x10;
        }
        0xdf => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x18;
        }
        0xe7 => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x20;
        }
        0xef => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x28;
        }
        0xf7 => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x30;
        }
        0xff => {
            self.stack_add(self.reg.pc);
            self.reg.pc = 0x38;
        }
        ...
    }
}

返回

RET

1) 描述

从堆栈中弹出一个 16 位地址, 并跳转到该地址. 它类似高级语言中的 Return 语句.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
RET-0xc98

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc9 => self.reg.pc = self.stack_pop(),
        ...
    }
}

RET IF

1) 描述

带条件的 RET 指令. 条件由下表指定.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
RET IF z == 0-0xc08
RET IF z == 1-0xc88
RET IF c == 0-0xd08
RET IF c == 1-0xd88

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xc0 | 0xc8 | 0xd0 | 0xd8 => {
            let cond = match opcode {
                0xc0 => !self.reg.get_flag(Z),
                0xc8 => self.reg.get_flag(Z),
                0xd0 => !self.reg.get_flag(C),
                0xd8 => self.reg.get_flag(C),
                _ => panic!(""),
            };
            if cond {
                self.reg.pc = self.stack_pop();
            }
        }
        ...
    }
}

RETI

1) 描述

执行 RET 指令并启用中断.

2) 标志位变化

3) 指令

InstructionParametersOpcodeCycles
RETI_0xd98

4) 代码实现

fn ex(&mut self) -> u32 {
    let opcode = self.imm();
    match opcode {
        0xd9 => {
            self.reg.pc = self.stack_pop();
            self.ei = true;
        }
        ...
    }
}