[컴퓨터구조] C로 만든 instruction simulator


(황예은) #1
긴 코드
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

int pc = 12; // 최초 pc값 0으로 설정

void CaseBType(char *instruction, char opcode[], char rs2[], char rs1[], char funct3[], int reg[]);
void CaseIType(char *instruction, char opcode[], char rd[], char funct3[], char rs1[], int reg[]);
void CaseJType(char *instruction, char opcode[], char rd[], int reg[]);
void CaseRType(char *instruction, char opcode[], char rd[], char funct3[], char rs1[], char rs2[], char funct7, int reg[]);
void CaseSType(char *instruction, char opcode[], char funct3[], char rs1[], char rs2[], int reg[]);
void CaseUType(char *instruction, char opcode[], char rd[], int reg[]);

char* dectobin(int s);
char* hextobin(char s[]);
char* twosComplement(char s[]);
int bintodec(char s[]);

void BEQ();
void BNE();
void BLT();
void BGE();
void BLTU();
void BGEU();
// B-type instruction
void LB();
void LH();
void LW();
void LBU();
void LHU();
void LWU();
void LD();
void ADDI(char rd[], char rs1[], char imm[], int reg[]);
void SLTI();
void SLTIU();
void XORI();
void ORI();
void ANDI();
void ADDIW();
void JALR();
// I-type instruction
void JAL();
// J-type instruction
void SLLI(); 
void SRLI();
void SRAI();
void SLLI();
void SRLI();
void SRAI();
void SLLIW();
void ADD();
void SUB();
void SLL();
void SLT();
void SLTU();
void XOR();
void SRL();
void SRA(); 
void OR();
void AND();
void ADDW();
void SUBW();
void SLLW();
void SRLW();
void SRAW();
// R-type instruction
void SB();
void SH();
void SW();
void SD();
// S-type instruction
void LUI();
void AUIPC();
// U-type instruction

char* hextobin(char s[]) {
    int i, j, count=0;  // 임시변수들
    char *binary;       // 결과를 저장할 변수
   
    binary = (char*)malloc(33*sizeof(char)); // 변환후 2진수 결과값을 저장할 공간.
   
   // 16진수를 10진수로 바꾸는 과정이다.
   for(i=0; i < 32; i++) {
        int decimal;
        if(s[i] >= '0' && s[i] <= '9') 
         decimal = s[i] - '0';
        else if(s[i] >= 'a' && s[i] <= 'f') 
         decimal = s[i] - 'a' + 10;
        else if(s[i] >= 'A' && s[i] <= 'F') 
         decimal = s[i] - 'A' + 10;
        else continue;
      
      // 10진수를 2진수로 바꾼다.
        for(j=0; j < 4; j++) binary[count++] = (decimal & (1 << (3-j)))?'1':'0';
   }
    binary[32] = '\0';
   
   return binary;
   free(binary);
}

char* dectobin(int s){
   char *result = (char *)malloc(33*sizeof(char));
   if(s < 0){
      s = s * (-1);  // 입력받은 수가 음수이면 양수로 바꾼다.
      
      for(int i = 31; i >= 0; i--){
         if(s%2 == 1) 
            result[i] = '1';
         else 
            result[i] = '0';
         s = s/2;
      }
      result[32] = '\0';
      result = twosComplement(result);
   } // 입력받은 10진수가 음수일때 2's complement를 취한다.
   else{
      for(int j = 31; j >= 0; j--){
         if(s%2 == 1) 
            result[j] = '1';
         else 
            result[j] = '0';
         s = s/2;
      }
      result[32] = '\0';
   }   
   return result;
   free(result);
} // 10진수를 2진수로 바꾼다.

char* twosComplement(char s[]){
   char* result = (char *)malloc(33*sizeof(char));
   int temp=0;
   
   for(int i=0; i<32; i++)
   {
      if(s[i]=='0') result[i]='1';
      else if(s[i]=='1') result[i]='0';
   }
   result[32]='\0';
   
   char* buf = (char *)malloc(9*sizeof(char));
   result = itoa(bintodec(result)+1, buf, 2);
   
   return result;
   free(result);
   free(buf);
} // 2's complement 변환을 해준다.

int bintodec(char s[]) {
    int i;
    int length;
    int result=0;
   
   length = strlen(s);
   
   for(i=0; i < length ; i++) {
      result = result * 2 + s[i]-48;
   }
   
   return result;
} // 2진수를 10진수로 바꾼다.

int TypeVerify(char *instruction, char opcode[]){

   if(strcmp(opcode, "1100011") == 0){
      return 1; // B-Type
   }
   else if(strcmp(opcode, "1100111") || strcmp(opcode, "0000011") || 
      strcmp(opcode, "0010011" ) || strcmp(opcode, "0011011") == 0){
      return 2; // I-Type
      // ADDIW, SLLIW "0011011" R-Type과 공유
   }
   else if(strcmp(opcode, "1101111") == 0){
      return 3; // J-Type
   }
   else if(strcmp(opcode, "0010011") || strcmp(opcode, "0110011")
      || strcmp(opcode, "0011011") || strcmp(opcode, "0111011") == 0){
      return 4; // R-Type 
      // SRLI, SRAI "0010011" I-Type과 공유
      // SRLI, SRAI(7) "0011011" I-Type과 공유
   }
   else if(strcmp(opcode, "0100011") == 0){
      return 5; // S-Type
   }
   else if (strcmp(opcode, "0110111") || strcmp(opcode, "0010111") == 0){
      return 6; // U-Type
   }
   else{
      return 7;
   }
}// opcode로 Type별 분류

void CaseRType(char *instruction, char opcode[], char rd[], char funct3[], char rs1[], char rs2[], char funct7[], int reg[]){
   // func7, func3, opcode 필요
   int i = 0;      
   for(i = 0; i < 3; i++){
      funct3[2 - i] = instruction[19 - i];
   } // funct3 추출
   for(i = 0; i < 7; i++){
      funct7[6 - i] = instruction[6 - i];
   } // funct7 추출
   for(i = 0; i < 5; i++){
      rd[4 - i] = instruction[24 - i];
      rs1[4 - i]  = instruction[16 - i];
      rs2[4 - i] = instruction[11 - i];
   } // rd, rs1, rs2 추출
   
   if(strcmp(opcode, "0010011") == 0){
      if(strcmp(funct3, "001") == 0){

      } // SLLI 구현
      else if(strcmp(funct3, "101") == 0){
         if (strcmp(funct7, "0100000") == 0){

         } // SRAI
         else{

         } // SRLI
      }
   }
   else if(strcmp(opcode, "0011011") == 0){
      
   } //SLLIW
   else if(strcmp(opcode, "0110011") == 0){
      if (strcmp(funct3, "000") == 0){
         if(strcmp(funct7, "0000000") == 0){

         } // add
         else if(strcmp(funct7, "0100000") == 0){

         } // sub
      }
      else if(strcmp(funct3, "001") == 0){

      }// SLL
      else if(strcmp(funct3, "010") == 0){

      }// SLT
      else if(strcmp(funct3, "011") == 0){

      }// SLTU
      else if(strcmp(funct3, "100") == 0){

      }// XOR
      else if(strcmp(funct3, "101") == 0){
         if(strcmp(funct7, "0000000") == 0){
            
         }// SRL
         else if(strcmp(funct7, "0100000") == 0){
            
         }// SRA
      }
      else if(strcmp(funct3, "110") == 0){
         
      }// OR
      else if(strcmp(funct3, "111") == 0){
         
      }// AND
   }
}
void CaseIType(char *instruction, char opcode[], char rd[], char funct3[], char rs1[], int reg[]){
   // func3, opcode 필요
   int i = 0;   
   char imm[13] = {0, };
   for(i = 0; i < 3; i++){
      funct3[2 - i] = instruction[19 - i];
   }
   for(i = 0; i < 5; i++){
      rd[4 - i] = instruction[24 - i];
      rs1[4 - i]  = instruction[16 - i];
   } // rd, rs1 추출
   for(i = 0; i < 12; i++){
      imm[11 - i] = instruction[11 - i];
   }
   imm[12] = '\0';

   if(strcmp(opcode, "0000011") == 0){
      if(strcmp(funct3, "000") == 0){
         
      } // LB
      else if(strcmp(funct3, "101") == 0){

      } // LH
      else if(strcmp(funct3, "010") == 0){

      } // LW
      else if(strcmp(funct3, "100") == 0){

      } // LBU

      else if(strcmp(funct3, "101") == 0){

      } // LHU
      else if(strcmp(funct3, "110") == 0){

      } // LWU
      else if(strcmp(funct3, "011") == 0){

      } // LD
   }
   else if(strcmp(opcode, "0010011") == 0){
      if(strcmp(funct3, "000") == 0){
        //ADDI(rd, rs1, imm);
      } // ADDI
      else if(strcmp(funct3, "010") == 0){

      } // SLTI
      else if(strcmp(funct3, "011") == 0){

      } // SLTIU
      else if(strcmp(funct3, "100") == 0){
      
      } // XORI
      else if(strcmp(funct3, "110") == 0){

      } // ORI
      else if(strcmp(funct3, "111") == 0){

      } // ANDI
   }
   
   else if(strcmp(opcode, "0011011") == 0){

   } // ADDIW

   else if(strcmp(opcode, "1100111") == 0){
      
   } // JALR
}
void CaseBType(char *instruction, char opcode[], char rs2[], char rs1[], char funct3[], int reg[]){
   // func3, opcode 필요
   int i = 0;
   char imm_1[6] = {0, };
   char imm_2[8] = {0, };

   for(i = 0; i < 3; i++){
      funct3[2 - i] = instruction[19 - i];
   }
   for(i = 0; i < 5; i++){
      imm_1[4 - i] = instruction[24 - i];
      rs1[4 - i]  = instruction[16 - i];
      rs2[4 - i] = instruction[11 - i];
   } // rd, rs1, rs2 추출
   for(i = 0; i < 7; i++){
      imm_2[6 - i] = instruction[6 - i];
   }
   imm_2[5] = imm_1[7] = '\0';
   
      if(strcmp(funct3, "000") == 0){
         
      } // BEQ
      else if(strcmp(funct3, "001") == 0){

      } // BNE
      else if(strcmp(funct3, "100") == 0){
         
      } // BLT
      else if(strcmp(funct3, "101") == 0){
         
      } // BGE      
      else if(strcmp(funct3, "110") == 0){
         
      } // BLTU
      else if(strcmp(funct3, "111") == 0){
         
      } // BGEU
}

void CaseSType(char *instruction, char opcode[], char funct3[], char rs1[], char rs2[], int reg[]){
   // func3, opcode 필요
   for(int i = 0; i < 3; i++){
      funct3[2 - i] = instruction[19 - i];
   }

   if(strcmp(opcode, "0100011") == 0){
      if(strcmp(funct3, "000") == 0){
         
      } // SB
      else if(strcmp(funct3, "001") == 0){
         
      } // SH
      else if(strcmp(funct3, "010") == 0){
         
      } // SW
      else if(strcmp(funct3, "011") == 0){
         
      } // SD
   }
}
void CaseJType(char *instruction, char opcode[], char rd[], int reg[]){
   //opcode 필요
   if(strcmp(opcode, "1101111") == 0){
      
   } 
} // JAL
void CaseUType(char *instruction, char opcode[], char rd[], int reg[]){
   //opcode 필요
   if(strcmp(opcode, "0110111") == 0){
      
   } // LUI
   else if(strcmp(opcode, "0010111") == 0){
      
   } // AUIPC
}
void ADDI(char rd[], char rs1[], char imm[], int reg[]){
   
}

void main(){
   FILE *test_binary;
   test_binary = fopen("out.bin", "rb");
   int size;
   int i = 0;
   char *temp; // 23 ~ 31까지 받아올 임시변수
   char *instruction = (char *)malloc(33*sizeof(char)); // 32비트니까 끝문자 고려해서 +1해서 할당
   int type_code = 0;
   int reg[32]; // register 32개

   for(i = 0; i < 32; i++){
      reg[i] = 0;
   }

   if(test_binary == NULL){
      printf("\ntest.bin 파일이 존재하지 않습니다.\n");
   }

   fseek(test_binary, 0, SEEK_END); 
   size = ftell(test_binary);      //입력 bin파일의 크기를 구하기 위함

   char **memory = (char**)malloc(sizeof(char)*(size / 4)); 
   for(i = 0; i < size; i++){
      *(memory + i) = (char*)malloc(sizeof(char)*33);
   }
   // instruction 의 갯수에 맞추어 메모리 할당
   
   fseek(test_binary, 0, SEEK_SET); // 파일 포인터를 처음위치로 다시 초기화

   for(i = 0; i < size; i++){      
      temp = dectobin(fgetc(test_binary));

      for(int j = 0; j < 8; j++){
         if (i % 4 == 0){
            memory[i/4][31-j] = temp[31 - j]; 
         }
         else if(i % 4 == 1){
            memory[i/4][31 - j - 8] = temp[31 - j];
         }
         else if(i % 4 == 2){
            memory[i/4][31 - j - 16] = temp[31 - j];
         }
         else if(i % 4 == 3){
            memory[i/4][31 - j - 24] = temp[31 - j];
         }
      }
      memory[i/4][32] = '\0'; // 끝이라는걸 확인
   } //  memory에 instruction을 넣어 주는 과정

   for(int j = 0; j < 32; j++){
      instruction[j] = memory[pc / 4][j];
   }
   instruction[32] = '\0';
   
   char opcode[8] = {0, };
   for(i = 0; i < 7; i++){
      opcode[6 - i] = instruction[31 - i];
   } // 해당 instruction의 opcode 추출
   char rs1[6] = {0, };
   char rs2[6] = {0, };
   char rd[6] = {0, };
   char funct3[4] = {0, };
   char funct7[8] = {0, };
   opcode[7] = rs1[5] = rs2[5] = rd[5] = funct3[3] = funct7[7] = '\0';

   pc += 4; // 하나 꺼냈으니까 pc값 증가

   type_code = TypeVerify(instruction, opcode);
   
   /*for(j = 0; j < 32; j++){
      printf("%c ", instruction[j]);
   }*/
   switch(type_code){
      case 1 : printf("\nB타입");
            CaseBType(instruction, opcode, rs2, rs1, funct3, reg);
            break;
      case 2 : printf("\nI타입");
            CaseIType(instruction, opcode, rd, funct3, rs1, reg);
            break;
      case 3 : printf("\nJ타입");
            CaseJType(instruction, opcode, rd, reg);
            break;
      case 4 : printf("\nR타입");
            CaseRType(instruction, opcode, rd, funct3, rs1, rs2, funct7, reg);
            break;
      case 5 : printf("\nS타입");
            CaseSType(instruction, opcode, funct3, rs1, rs2, reg);
            break;
      case 6 : printf("\nU타입");
            CaseUType(instruction, opcode, rd, reg);
            break;
      case 7 : printf("\ntype 분류 실패");
            break;
   }

   fclose(test_binary);
}

MIPS와 유사한 32bit instruction을 생성중입니다. 이렇게한다음 어떻게해야할지 조원해주시면 감사하겠습니다ㅠㅠ


(P.노우렛지Δ) #2

코드가 길고 묶여있지 않아 정리해두었습니다.

``` 로 감싸시면 코드를 예쁘게 올리실 수 있습니다.

또한, 전체 코드를 올려놓고 두리뭉술하게 질문하는것은 답변을 받기 어렵습니다.

적절한 질문을 하는 방법은 다음 내용을 참조해 주세요.


(THP_Aya ☆☆) #3

긴 코드지만, 보다가 눈에 띄었네요.
이거 반환 후 할당 해제를 의도하셨다면, 아마 원하신 대러 안 될 겁니다. 호출할 때마다 새로 할당받을 걸요?
RET는 프로그램 흐름을 호출 함수로 되돌려 놓습니다. 그러니까 그 이후의 free는 호출이 안 되고 스택이 꺼지면서 할당받은 포인터는 잃어버리게 되는 거지요.

값을 읽어맬 메모리의 포인터 주소를 인자로 전달 후 함수 내에서 그 값에다가 복사해 넣는 방법을 쓰시면 되겠습니다.

그리고 OPCODE별로 되어있는 끝없는 if 조건문 대신에, 인스트럭션 별로 함수를 지정해서 각각 짜 주고, 한 배열에 함수의 포인터들을 opcode 위치대로 저장을 해 주고, 실행 단계에서 인스트럭션을 읽어와서 XOR과 시프트로 OPCODE만 따 준 다음에 배열에서 인덱스로 함수 주소를 읽어내서 그 함수를 호출하는 방법도 있을 것 같습니다.


(바보털) #4

이건 (decimal & (1 << (3 - j))) + '0'로 바꾸는게 어떨까요?

char* dectobin(int)에서는… 일단, 분명 다른 분기인데 비슷한 작업이 너무 많은 느낌이 있습니다. 또,

이건 result[ i ] = s % 2 + '0'처럼 쓰는게 더 깔끔해 보입니다.

이건 그냥 문자열 복사 아닌가요? 굳이 분기를… strcpy같은 내장 함수를 사용하면 좋습니다.

뒷쪽 함수들은 분기도 무척 복잡하고, strcmp를 무척 많이 호출하고 있는데, 이러면 읽기도 쉽지 않고 성능도 하락합니다. 명령어들을 해당하는 10진수 정수로 바꾸신 후(예: 110을 6으로) 함수 포인터 배열을 만드는 것이 바람직해 보입니다.