PA 1: simple computer

·

6 min read

1 Introduction

In lab 1, we will implement a simple debugger. There are three main tasks.

Task 1, Implement single-step execution, Print register status, Scan memory.

Task 2, Implement expression evaluation.

Task 3, Perfect expression evaluation and Implement expression evaluation.

2 Implementation

2.1 Correct exit

when using the command q to exit, the nemu will output an error message.

To fix it, you should debug through gdb and know must change the state.

static int cmd_q(char* args) {
    nemu_state.state = NEMU_QUIT;
    return 0;
};

2.2 Simple debugger

2.2.1 Single-step execution

add si cmd to cmd_table

static struct {
  const char *name;
  const char *description;
  int (*handler) (char *);
} cmd_table [] = {
  { "help", "Display information about all supported commands", cmd_help },
  { "c", "Continue the execution of the program", cmd_c },
  { "q", "Exit NEMU", cmd_q },

  /* TODO: Add more commands */
  { "si", "Execute N steps, default 1", cmd_si },
};

implement cmd_si function

static int cmd_si (char *args) {
    int n = 1;
    ...(analytic parameter to n)
    // execution N step
    cpu_exec(n);
    return 0;
};

2.2.2 Print register status

add info cmd to cmd_table

{ "info", "Display information about registers or watchpoints", cmd_info },

implement cmd_info function

the display register function is isa_reg_display, you need to modify it.

static int cmd_info(char *args) {
  // analytic parameter
  char *arg = strtok(NULL, " ");
  if (arg == NULL) {
    // error handler
  } else {
    if (strcmp(arg, "r") == 0) {
      // display register
      isa_reg_display()
    } else if (strcmp(arg, "w") == 0) {
      // display watchpoint (need to impl)
    } else {
      // error handler
    }
  }
  return 0;
}

2.2.3 Scan memory

add x cmd to cmd_table

{ "x", "Usage: x N EXPR. Scan the memory from EXPR", cmd_x },

implement cmd_x function

static int cmd_x(char *args) {
  // need to get parameter
  char *arg1, *arg2;
  if (arg1 == NULL || arg2 == NULL) {
    // error handler
    return 0;
  }
  // transfor type
  int n = strtol(arg1, NULL, 10);
  vaddr_t expr = strtol(arg2, NULL, 16);

  // print memory
  ... using vaddr_read(), format like gdb
  return 0;
}

2.3 Expression evaluation

To start this, you must read to experiment document expression evaluation and

watchpoint. Know how to analyze the expression and calculate it.

add p cmd to cmd_table

{ "p", "Usage: p EXPR. Calculate the experssion", cmd_p },

implement cmd_p function, and all details in the expr() function.

static int cmd_p(char *agrs) {
  bool success;
  word_t res = expr(agrs, &success);
  if (!success) {
    printf("invalid expression\n");
  } else {
    printf("%u\n", res);
  }
  return 0;
}

word_t expr(char *e, bool *success){
  if (!make_token(e)) {
    *success = false;
    return 0;
  }

  return eval(0, nr_token - 1, success);
}

finish make_token and eval function, all hints in the experiment document.

we should categorize all tokens.

enum {
  TK_NOTYPE = 256,

  /* TODO: Add more token types */
  TK_NUM, // number
  TK_REG,

  TK_POS, TK_NEG, TK_DEREF,
  TK_EQ, TK_NEQ, TK_GT, TK_LT, TK_GE, TK_LE,
  TK_AND, TK_OR,
};

static struct rule {
  const char *regex;
  int token_type;
} rules[] = {

  /* TODO: Add more rules.
   * Pay attention to the precedence level of different rules.
   */
  {" +", TK_NOTYPE},    // spaces
  {"\\+", '+'},         // plus
  {"==", TK_EQ},        // equal
  {"-", '-'},
  {"\\(", '('},
  {"\\)", ')'},
  {"\\*", '*'},
  {"/", '/'},
  {"<", TK_LT},
  {"<=", TK_LE},
  {">", TK_GT},
  {">=", TK_GE},
  {"!=", TK_NEQ},
  {"&&", TK_AND},
  {"\\|\\|", TK_OR},

  {"[0-9]+", TK_NUM},
  {"\\$\\w+", TK_REG},
};

static int bound_types[] = {')', TK_NUM, TK_REG};
static int nop_types[] = {'(', ')', TK_NUM, TK_REG};
static int op_types[] = {TK_NEG, TK_POS, TK_DEREF};

modify make_token

static bool make_token(char *e) {
  int position = 0;
  int i;
  regmatch_t pmatch;

  nr_token = 0;

  while (e[position] != '\0') {
    /* Try all rules one by one. */
    for (i = 0; i < NR_REGEX; i ++) {

      if (regexec(&re[i], e + position, 1, &pmatch, 0) == 0 && pmatch.rm_so == 0) {
        char *substr_start = e + position;
        int substr_len = pmatch.rm_eo;

        Log("match rules[%d] = \"%s\" at position %d with len %d: %.*s",
            i, rules[i].regex, position, substr_len, substr_len, substr_start);

        position += substr_len;

        /* TODO: Now a new token is recognized with rules[i]. Add codes
         * to record the token in the array `tokens'. For certain types
         * of tokens, some extra actions should be performed.
         */

        if (rules[i].token_type == TK_NOTYPE) break;
        // add to token
        tokens[nr_token].type = rules[i].token_type;

        switch (rules[i].token_type) {
          case TK_NUM:
          case TK_REG:
            strncpy(tokens[nr_token].str, substr_start, substr_len);
            tokens[nr_token].str[substr_len] = '\0';
            break;
          case '*':
          case '-':
          case '+':
            if (nr_token == 0 || !type_from(tokens[nr_token-1].type, bound_types, ARRLEN(bound_types))) {
              switch (rules[i].token_type) {
                case '-': tokens[nr_token].type = TK_NEG; break;
                case '+': tokens[nr_token].type = TK_POS; break;
                case '*': tokens[nr_token].type = TK_DEREF; break; 
              }
            }
            break;

          //default: TODO();
        }
        nr_token++;

        break;
      }
    }

    if (i == NR_REGEX) {
      printf("no match at position %d\n%s\n%*.s^\n", position, e, position, "");
      return false;
    }
  }

  return true;
}

word_t eval(int p, int q, bool *success) {
  *success = true;
  if (p > q) {
    *success = false;
    return 0;
  } else if (p == q) {
    // just one , analyze op_token
  } else if (check_parentheses(p, q)) {
    // in both (), remove ().
  } else {
    // get op posistion
    int op = get_position(p, q);
    if (op < 0) {
      *success = false;
      return 0;
    }
    // eval left and right
    bool ok1, ok2;
    word_t val1 = eval(p, op - 1, &ok1);
    word_t val2 = eval(op + 1, q, &ok2);


    if (!ok2) {
      *success = false;
      return 0;
    }
    word_t ret;
    // two op or one op
    if (ok1) {
      ret = calc2(tokens[op].type, val1, val2, success);
    } else {
      ret = calc1(tokens[op].type,val2, success);
    }
    return ret;
  }
}

2.4 Watchpoint

add w,d cmd to cmd_table

{ "w", "Usage: w EXPR. set watchpoint", cmd_w },
{ "d", "USage: d N. delete watchpoint", cmd_d },

implement cmd_w and cmd_d function

static int cmd_w(char *agrs) {
  if (agrs == NULL) {
    // error handler
    return 0;
  }
  bool success;
  // get expr
  word_t res = expr(agrs, &success);
  if (!success) {
    printf("invalid expression\n");
  } else {
    wp_add(agrs, res);
  }
  return 0;
}

static int cmd_d(char *args) {
  // analyze from args
  int num;
  wp_remove(num);

  return 0;
}

finish wp_remove(), wp_add() and wp_display() function

void wp_add(char *expr, word_t res) {
  WP* wp = new_wp();
  strcpy(wp->expr, expr);
  wp->ret = res;
  printf("watchpoint set. %d: %s\n", wp->NO, wp->expr);
}

void wp_remove(int num) {
  assert(num < NR_WP);
  WP* wp = &wp_pool[num];
  free_wp(wp);
  printf("delete watchpoint. %d: %s", wp->NO, wp->expr);
}

void wp_display() {
  WP *wp = head;
  // iterate wp 
}

Put the check of watchpoints in trace_and_difftest() and use a new macro CONFIG_WATCHPOINT

static void trace_and_difftest(Decode *_this, vaddr_t dnpc) {
#ifdef CONFIG_ITRACE_COND
  if (ITRACE_COND) { log_write("%s\n", _this->logbuf); }
#endif
  if (g_print_step) { IFDEF(CONFIG_ITRACE, puts(_this->logbuf)); }
  IFDEF(CONFIG_DIFFTEST, difftest_step(_this->pc, dnpc));
  IFDEF(CONFIG_WATCHPOINT, wp_difftest());
}

implement wp_difftest() in watchpoint.c.

void wp_difftest() {
  WP* wp = head;
  while (wp) {
    bool _;
    //calc expr 
    word_t new = expr(wp->expr, &_);
    if (wp->ret != new) {
      // get to watchpoint.
      wp->ret = new;
    }
    wp = wp->next;
  }
}