最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

I need to write a series of C++ switch branches that do the same thing but to different variables. How can I do that programmati

programmeradmin1浏览0评论

Background

I am in the process of writing a GameBoy emulator in C++. In my code, the processor is implemented with a switch statement like the following:

class Processor {

  // ...

  void executeOpcode(Opcode opcode) {
    switch (opcode) {
      // ...
      case LD_A_B:
        A = B;
        break;
      case LD_A_C:
        A = C;
        break;
      case LD_A_D:
        A = D;
        break;
      // ...
    }
  }

  // ...

}

Here, Opcode is just a typedef enum where the name of each instruction corresponds to its 1-byte opcode. In this example, there is a series of instructions that perform the same operation, but using different registers. This pattern happens for a lot of different instructions, some performing more complex operations than just a simple copy. What I am asking is: is there a way to programmatically write switch clauses for all the registers of the processor?

What I've tried so far

I know I could implement a series of functions like this:

void Processor::LD_x_y(Register& x, Register y) { x = y };

so that the switch becomes like:

switch (opcode) {
  // ...
  case LD_A_B:
    LD_x_y(A, B);
    break;
  case LD_A_C:
    LD_x_y(A, C):
    break;
  //...
}

But repeating this pattern for a lot of instructions would expose me to mistakes like:

switch (opcode) {
  // ...
  case LD_A_B:
    LD_x_y(A, B);
    break;
  case LD_A_C:
    LD_x_y(A, B): // <-- forgot to change the register in the function call!
    break;
  //...
}

I am not very familiar with using preprocessor directives, but I know that in principle I should be able to write something along the lines of:

#define EXPAND_LD_x_y(X, Y) case LD_X_Y: X=Y; break; 

// ...

switch (opcode) {
  // ...
  EXPAND_LD_x_y(A, B)
  EXPAND_LD_x_y(A, C)
  //...
}

Bottom line

I believe this last approach is good enough but I would really like to have something that automatically expands the switch cases for each combination of registers. Is there a way to do that with preprocessor directives? Is there any better way to achieve what I'm trying to do?

Background

I am in the process of writing a GameBoy emulator in C++. In my code, the processor is implemented with a switch statement like the following:

class Processor {

  // ...

  void executeOpcode(Opcode opcode) {
    switch (opcode) {
      // ...
      case LD_A_B:
        A = B;
        break;
      case LD_A_C:
        A = C;
        break;
      case LD_A_D:
        A = D;
        break;
      // ...
    }
  }

  // ...

}

Here, Opcode is just a typedef enum where the name of each instruction corresponds to its 1-byte opcode. In this example, there is a series of instructions that perform the same operation, but using different registers. This pattern happens for a lot of different instructions, some performing more complex operations than just a simple copy. What I am asking is: is there a way to programmatically write switch clauses for all the registers of the processor?

What I've tried so far

I know I could implement a series of functions like this:

void Processor::LD_x_y(Register& x, Register y) { x = y };

so that the switch becomes like:

switch (opcode) {
  // ...
  case LD_A_B:
    LD_x_y(A, B);
    break;
  case LD_A_C:
    LD_x_y(A, C):
    break;
  //...
}

But repeating this pattern for a lot of instructions would expose me to mistakes like:

switch (opcode) {
  // ...
  case LD_A_B:
    LD_x_y(A, B);
    break;
  case LD_A_C:
    LD_x_y(A, B): // <-- forgot to change the register in the function call!
    break;
  //...
}

I am not very familiar with using preprocessor directives, but I know that in principle I should be able to write something along the lines of:

#define EXPAND_LD_x_y(X, Y) case LD_X_Y: X=Y; break; 

// ...

switch (opcode) {
  // ...
  EXPAND_LD_x_y(A, B)
  EXPAND_LD_x_y(A, C)
  //...
}

Bottom line

I believe this last approach is good enough but I would really like to have something that automatically expands the switch cases for each combination of registers. Is there a way to do that with preprocessor directives? Is there any better way to achieve what I'm trying to do?

Share Improve this question asked Feb 5 at 12:49 Matteo BonaciniMatteo Bonacini 553 bronze badges 12
  • 2 Put the switch into a separate function, which accepts arguments named opcode, a and b. For your first example, pass (from the caller) opcode, A, and B to that function. For your second example, pass (from the caller) opcode, A, and C. For other "registers" pass arguments corresponding to each register. Wash your mouth out with soap for considering macros - they are unnecessary in your problem, as described, and introduce a significant range of other problems. – Peter Commented Feb 5 at 13:06
  • Do you have control over Opcode value? can you retrieve register from opcode? – Jarod42 Commented Feb 5 at 13:07
  • @Jarod42 In principle yes: some bits from the opcode tell me that the operation is "LD" and some other bits tell me which register I should be using. However, opcodes for different operations get parsed differently and thus I could not parse it this way with just a switch statement. – Matteo Bonacini Commented Feb 5 at 13:10
  • 1 Extracting that information might be an alternative then; and if A/B/C/.. are array, instead of named variables, it might even been simpler. – Jarod42 Commented Feb 5 at 13:13
  • 1 “doing so is essentially the same as writing the switch statement...” No way, not even close. Here is why: you cannot populate switch, but you can easily populate a map instance. Yes, programmatically. What's the problem? – Sergey A Kryukov Commented Feb 5 at 13:49
 |  Show 7 more comments

4 Answers 4

Reset to default 5

I don't know at all GameBoy specific stuff, but if they have been smart when designing opcodes and registers, you should be able to use arrays and bitwise operations, as they are the fastest possible ones.

Let's say we have:

enum Opcode {
  LD_A_B = 0b0000_00_01,
  LD_A_C = 0b0000_00_10,
  LD_A_D = 0b0000_00_11,
  LD_B_A = 0b0000_01_00,
  LD_B_C = 0b0000_01_10,
  ...
};

Then it means that we have exactly 4 available registers and that we can extract the destination register in bits 6-7, and source in bits 4-5.

int registers[4];
...
switch(opcode) {
  case LD_A_B:  case LD_A_C: case LD_A_D: case LD_B_A: case LD_B_C:
    registers[opcode&0b11] = registers[(opcode>>2)&0b11]
  ...
}

I'm pretty sure they were especially smart at that time, when every tiny single operations could make everything slow. For sure they thought about such tricks.

Instead of switch

std::map<
    Opcode,
    std::function<void()>
> instruction = {
    {LD_A_B, [&A,&B](){ A = B; }},
    {LD_A_C, [&A,&C](){ A = C; }}
    //...
};

and then afterwards

Opcode opcode = //...
instruction[opcode]();

The idea here is, since each instruction basically knows what to do, we let the instructions decide where to get their resources from instead of passing them as arguments.

I think you are looking for preprocessor token pasting:

switch (opcode) {
  // ...
#define EXPAND_LD_x_y(X, Y) 
  case LD_ ## X ## _ ## Y: \
        X = Y; \
        break; 
  EXPAND_LD_x_y(A, B)
  EXPAND_LD_x_y(A, C)
#undef EXPAND_LD_x_y
  //...
}

To answer the question from the comments (a matrix of LD_ codes), you might write like this:

EXPAND_LD_X(X) \
  EXPAND_LD_x_y(X,A) \
  EXPAND_LD_x_y(X,B) \
  EXPAND_LD_x_y(X,C)
EXPAND_LD_X(A)
EXPAND_LD_X(B)
EXPAND_LD_X(C)

You can achieve this with a map<Opcode, Register> and then define your void Processor::LD_x_y(Register& x, Register y) { x = y }; method which you can use as:

LD_x_y(A, yourmap[opcode])

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论