1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! Instructions related to interfacing with the Falcon data segment.

use faucon_asm::{Instruction, Operand, Register, RegisterKind};

use super::{branch::ret, utils, Cpu, SP};

/// Loads a value from data segment to a register.
pub fn ld(cpu: &mut Cpu, insn: &Instruction) -> usize {
    let operands = insn.operands();

    // Extract the instruction operands (register and memory access descriptor).
    let destination = operands[0];
    let source = operands[1];

    // Read the value from DMem and store it in the destination register.
    utils::write_reg(cpu, insn.operand_size(), destination, source);

    // Signal regular PC increment to the CPU.
    cpu.increment_pc = true;

    1
}

/// Stores a value from a register to data segment.
pub fn st(cpu: &mut Cpu, insn: &Instruction) -> usize {
    let operands = insn.operands();

    // Extract the instruction operands (memory access descriptor and register).
    let destination = operands[0];
    let source = operands[1];

    // Write the value in the source register to DMem.
    utils::write_mem(cpu, insn.operand_size(), source, destination);

    // Signal regular PC increment to the CPU.
    cpu.increment_pc = true;

    1
}

/// Pushes a given register onto the stack.
pub fn push(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Extract the instruction operand (a single register).
    let source = insn.operands()[0];

    // Push the word in the supplied register onto the stack.
    cpu.stack_push(cpu.registers[source]);

    // Signal regular PC increment to the CPU.
    cpu.increment_pc = true;

    1
}

/// Pops a value off the stack and stores the result in a register.
pub fn pop(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Extract the instruction operand (a single register).
    let destination = insn.operands()[0];

    // Pop a value off the stack and store it in the destination register.
    cpu.registers[destination] = cpu.stack_pop();

    // Signal regular PC increment to the CPU.
    cpu.increment_pc = true;

    1
}

/// Pushes a specified range of registers onto the stack.
pub fn mpush(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Extract the instruction operand (a single register).
    if let Operand::Register(reg) = insn.operands()[0] {
        // Push the described range of registers onto the stack.
        for i in 0..reg.1 {
            let fake_reg = Register(RegisterKind::Gpr, i);
            cpu.stack_push(cpu.registers[fake_reg]);
        }
    } else {
        unreachable!();
    }

    // Signal regular PC increment to the CPU.
    cpu.increment_pc = true;

    1 // TODO: Is this really one cycle?
}

/// Pops a specified range of registers off the stack.
pub fn mpop(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Extract the instruction operand (a single register).
    if let Operand::Register(reg) = insn.operands()[0] {
        // Pop the described range of registers off the stack.
        for i in 0..reg.1 {
            let fake_reg = Register(RegisterKind::Gpr, i);
            cpu.registers[fake_reg] = cpu.stack_pop();
        }
    } else {
        unreachable!();
    }

    // Signal regular PC increment to the CPU.
    cpu.increment_pc = true;

    1 // TODO: Is this really one cycle?
}

/// Pops a specified range of registers off the stack and adds an immediate to $sp.
pub fn mpopadd(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Execute the mpop instruction using the first operand (a register).
    mpop(cpu, insn);

    // Add the second operand (an immediate) to the $sp register.
    if let Operand::Immediate(imm) = insn.operands()[1] {
        cpu.registers[SP] = cpu.registers[SP].wrapping_add(imm as u32);
    } else {
        unreachable!();
    }

    2
}

/// Pops a specified range of registers off the stack and returns.
pub fn mpopret(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Execute the mpop instruction using the first operand (a register).
    mpop(cpu, insn);

    // Execute the ret instruction.
    ret(cpu, insn);

    2
}

/// Pops a specified range of registers off the stack, adds an immediate to $sp and returns.
pub fn mpopaddret(cpu: &mut Cpu, insn: &Instruction) -> usize {
    // Execute the mpopadd instruction using the instruction operands.
    mpopadd(cpu, insn);

    // Execute the ret instruction.
    ret(cpu, insn);

    2
}