The Crystal Programming Language Forum

Best way to store an instruction dictionary

I’m working on a toy Gameboy emulator, and I need to store the specifications for the instructions, namely

  • a mnemonic, for debug purposes (String)
  • the instruction’s length, to know how many bytes the decoder needs to read to parse a full instruction (Int)
  • the number of cycles the CPU needs to take to execute the instruction (Int)
  • a description of the operands (byte, word, etc) (String in a convention that I parse)
  • a callable, the method that actually executes this class of instruction (lambda)

As an example, this is what I have now

module GBEmu
  class InstructionDictionary
    INSTRUCTION_MAP = {
      0x0 => {
        mnemonic: "NOP",
        length:   1,
        cycles:   4,
        operands: [] of String | Int32,
        callable: ->(cpu : CPU, instr : Instruction) { Opcodes.not_implemented(cpu, instr) },
      },
      0x01 => {
        mnemonic: "LD",
        length:   3,
        cycles:   12,
        operands: ["BC", "d16"],
        callable: ->(cpu : CPU, instr : Instruction) { Opcodes::LoadStoreMove16Bit.ld_n_nn(cpu, instr) },
      },
      0x02 => {
        mnemonic: "LD",
        length:   1,
        cycles:   8,
        operands: ["(BC)", "A"],
        callable: ->(cpu : CPU, instr : Instruction) { Opcodes::LoadStoreMove8Bit.ld_n_a(cpu, instr) },
      },
      0x03 => {
        mnemonic: "INC",
        length:   1,
        cycles:   8,
        operands: ["BC"],
        callable: ->(cpu : CPU, instr : Instruction) { Opcodes::ALU16Bit.inc_nn(cpu, instr) },
      },

What I do is get the first byte from the emulated Gameboy’s memory and use it as a key to fetch from this hash, then based on the definition, instantiate an Instruction object that is quite similar (and I should probably rethink this approach :smiley: ). But anyway, as it stands now, the hash’s values are NamedTuples, and I’m starting to run into

Invalid memory access (signal 11) at address 0x7f11d62b7280

at runtime after ~48k iterations of the emulated Gameboy’s CPU. It seems like @asterite doesn’t recommend using NamedTuples anyway (right?) so, disregarding my current shortcomings in terms of the InstructionDictionary/Instruction setup that definitely needs changing, what would be a better way to achieve this dictionary approach without having to do .as(sometype) when getting items from the Hash?

Hope this makes sense, thanks for any replies.

s t r u c t s

    INSTRUCTION_MAP = [
      # 0x00
      Instruction.new(
        mnemonic: "NOP",
        length:   1,
        cycles:   4,
        operands: [] of String | Int32,
        callable: ->(cpu : CPU, instr : Instruction) { Opcodes.not_implemented(cpu, instr) },
      ),
2 Likes

I have a distinction between InstructionDefinition and Instruction. The dictionary doesn’t hold every possible instruction, only info on a kind of them.

But I guess the example doesn’t make too much sense. I’ve changed it now to just

NON_PREFIXED = {

      # LD n, nn
       0x1 => ->(cpu : CPU) { Opcodes::LoadStoreMove16Bit.ld_n_nn(cpu) },
      0x11 => ->(cpu : CPU) { Opcodes::LoadStoreMove16Bit.ld_n_nn(cpu) },
      0x21 => ->(cpu : CPU) { Opcodes::LoadStoreMove16Bit.ld_n_nn(cpu) },
      0x31 => ->(cpu : CPU) { Opcodes::LoadStoreMove16Bit.ld_n_nn(cpu) },

and will do the operand decoding and so on in each specific handler method.

I think any approach is fine, but the runtime segfaulting is not. You can try reducing your code and submitting a bug. I don’t recommend named tuples, but not because they segfault (they should never segfault).