졸업프로젝트

15차 전원회의 - p-code operator를! 공부해보자

img1244 2026. 5. 16. 16:23

p-code 오퍼레이터는 ghidra 공식 문서를 참조

https://ghidra.re/ghidra_docs/languages/html/pcodedescription.html#cpui_copy

 

P-Code Operation Reference

For each possible p-code operation, we give a brief description and provide a table that lists the inputs that must be present and their meaning. We also list the basic syntax for denoting the operation when describing semantics in a processor specificatio

ghidra.re

 

해당 문서를 ai에 넣고 제대로 내용 추출하는지 확인했는데,

없는 연산자들을 생성하고, 누락시키고, 이름도 맘대로 붙이고 etc

진짜 생 난리가 나서 그냥 하나하나 일일이 읽어보면서 정리했다. 

그래서 우선 json 파일로 제작하였다.

 

포멧은 아래와 같은 형식으로 만들어 봤다. 

[
 {
    "operator": "공식문서에서 연산자 명",
    "semantic_statement": [
      "어떤 형태로 사용되는지",
      "여러 형태로 표현가능하면 이렇게"
    ],
    "brief_description": "연산자에 대한 설명",
    "parameters": {
      "o": "파라미터에 대한 설명을 넣을 곳",
      "i0": "출력은 o, 입력은 i로 표현",
      "in": "입력이 여러개면, i_n 형태로 표현"
    }
  },
  ...
 ]

pcodeOP.json
0.01MB


Operator Semantic Statement Brief Description Parameters
COPY o = i0; 값을 복사 o: 결과(대상 변수), i0: 원본 변수
LOAD o = *i1;
o = *[i0]i1;
메모리에서 값 읽기 o: 결과, i0: 상수 ID, i1: 상수 오프셋
STORE *i1 = i2;
[i0]i1 = i2;
메모리에 값 저장 i0: 상수 ID, i1: 상수 오프셋, i2: 저장할 값
BRANCH goto i0; 무조건 분기 i0: 분기 목적지 주소
CBRANCH if (i0) goto i1; 조건 분기 i0: 조건, 0이 아니면 분기, i1: 분기 목적지 주소
BRANCHIND goto [i0]; 다음 instruction 주소로 분기(간접 분기) i0: 분기 주소
CALL call [i0]; 함수 호출 i0: 함수 주소,
i1: 1번째 인자, raw p-code에서는 표시 X,
in: n번째 인자, raw p-code에서는 표시 X
CALLIND call [i0]; 포인터 주소의 함수 호출(간접호출) i0: 함수 포인터, i1: 1번째 인자,
raw p-code에서는 표시 X, in: n번째 인자,
raw p-code에서는 표시 X
RETURN return [i0]; 함수 반환 i0: 반환 값
PIECE Cannot (currently) be explicitly coded 여러 변수를 결합(비트 연결) o: 결과, i0: 병합 시 높은 중요도의 데이터,
i1: 병합 시 낮은 중요도의 데이터
SUBPIECE o = i0(i1); 변수의 일부(부분) 추출 o: 결과, i0: 원본, i1: 오프셋(byte)
BOOL 연산
BOOL_XOR o = i0 ^^ i1; 불린 xor 연산 o: 결과, i0: 피연산자1, i1: 피연산자2
BOOL_AND o = i0 && i1; 불린 and 연산 o: 결과, i0: 피연산자1, i1: 피연산자2
BOOL_OR o = i0 || i1; 불린 or 연산 o: 결과, i0: 피연산자1, i1: 피연산자2
BOOL_NEGATE o = !i0; 불린 부정 o: 결과, i0: 피연산자
INT 연산
INT_EQUAL o = i0 == i1; 정수 동등 비교 o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_NOTEQUAL o = i0 != i1; 정수 다름 비교 o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_SLESS o = i0 s< i1; signed 비교 (작음) o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_SLESSEQUAL o = i0 s<= i1; signed 비교 (작거나 같음) o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_LESS o = i0 < i1; unsigned 비교 (작음) o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_LESSEQUAL o = i0 <= i1; unsigned 비교 (작거나 같음) o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_ZEXT o = zext(i0); 상위 비트를 0으로 채워 정수 확장 o: 결과, i0: 원본
INT_SEXT o = sext(i0); 정수를 부호(상위비트)로 확장 o: 결과, i0: 원본
INT_ADD o = i0 + i1; 정수 덧셈 o: 결과, i0: 피연산자1, i1: 피연산자2
INT_SUB o = i0 - i1; 정수 뺄셈 o: 결과, i0: 피연산자1, i1: 피연산자2
INT_CARRY o = carry(i0, i1); unsigned carry 발생 여부(오버플로우나 carry 플래그) o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_SCARRY o = scarry(i0, i1); signed carry 발생 여부 o: 결과(bool), i0: 피연산자1, i1: 피연산자2
INT_SBORROW o = sborrow(i0, i1); signed borrow 발생 여부 o: 결과(bool)unsigned, i0: 피연산자1, i1: 피연산자2
INT_2COMP o = -i0; 2의 보수(부호 반전) o: 결과, i0: 피연산자
INT_NEGATE o = ~i0; 비트 반전 o: 결과, i0: 피연산자
INT_XOR o = i0 ^ i1; 비트 xor 연산 o: 결과, i0: 피연산자1, i1: 피연산자2
INT_AND o = i0 & i1; 비트 and 연산 o: 결과, i0: 피연산자1, i1: 피연산자2
INT_OR o = i0 | i1; 비트 or 연산 o: 결과, i0: 피연산자1, i1: 피연산자2
INT_LEFT o = i0 << i1; 왼쪽 시프트 o: 결과, i0: 값, i1: 시프트 양
INT_RIGHT o = i0 >> i1; 오른쪽 시프트(무부호) o: 결과, i0: 값, i1: 시프트 양
INT_SRIGHT o = i0 s>> i1; 오른쪽 시프트(부호형) o: 결과, i0: 값, i1: 시프트 양
INT_MULT o = i0 * i1; 정수 곱셈 o: 결과, i0: 피연산자1, i1: 피연산자2
INT_DIV o = i0 / i1; 정수 나눗셈(무부호) o: 결과, i0: 피연산자1, i1: 피연산자2
INT_SDIV o = i0 s/ i1; 정수 나눗셈(부호형) o: 결과, i0: 피연산자1, i1: 피연산자2
INT_REM o = i0 % i1; 정수 나머지(무부호) o: 결과, i0: 피연산자1, i1: 피연산자2
INT_SREM o = i0 s% i1; 정수 나머지(부호형) o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT 연산
FLOAT_EQUAL o = i0 f== i1; float 동등 비교 o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_NOTEQUAL o = i0 f!= i1; float 다름 비교 o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_LESS o = i0 f< i1; float 비교 (작음) o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_LESSEQUAL o = i0 f<= i1; float 비교 (작거나 같음) o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_NAN o = isnan(i0); NaN(비수) 여부 판정 o: 결과(bool), i0: 피연산자
FLOAT_ADD o = i0 f+ i1; float 덧셈 o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_DIV o = i0 f/ i1; float 나눗셈 o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_MULT o = i0 f* i1; float 곱셈 o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_SUB o = i0 f- i1; float 뺄셈 o: 결과, i0: 피연산자1, i1: 피연산자2
FLOAT_NEG o = f- i0; float 부호 반전 o: 결과, i0: 피연산자
FLOAT_ABS o = abs(i0); float 절댓값 o: 결과, i0: 피연산자
FLOAT_SQRT o = sqrt(i0); float 제곱근 o: 결과, i0: 피연산자
INT2FLOAT o = int2float(i0); 정수→float 변환 o: 결과, i0: 정수
FLOAT2FLOAT o = float2float i0; float 포맷 변환 o: 결과, i0: 부동소수
TRUNC o = trunc(i0); 소수점 이하 절삭 o: 결과, i0: 피연산자
FLOAT_CEIL o = ceil(i0); 올림 함수 o: 결과, i0: 피연산자
FLOAT_FLOOR o = floor(i0); 내림 함수 o: 결과, i0: 피연산자
FLOAT_ROUND o = round(i0); 반올림 함수 o: 결과, i0: 피연산자
COUNT
POPCOUNT o = popcount(i0); 비트 1의 개수를 계산(팝카운트) o: 결과, i0: 피연산자
LZCOUNT o = lzcount(i0); 선행 0비트 개수 계산  o: 결과, i0: 피연산자

공부한 것에 비해서 뭔가 분량이 좀 아쉽다.

그래서 분명히 아무도 안쓸게 뻔하지만 전자사전을 함 만들어봤다.

  • 영문 대문자 → operator 기준
  • 영문 소문자 → semantic statement 기준
  • 한글 → brief description 기준

이렇게 언어 별 대/소문자 별 우선 검색 순위를 만들었다.

또, prompt_toolkit이라는 라이브러리를 통해서 tab키를 인식하게 하엿고, 이를 통해서 자동완성 기능도 넣어봤다.

tab을 누르면 저렇게 자동완성 된다.

결과는 위와 같이 나오게 된다.

 

부가적으로 exit이라는 명령어도 semantic statement도 없길래 exit으로 프로그램 종료

'''을 누르면 clear 또는 cls가 실행되도록 만들었다.

 

또, 원래는 json을 참조하게 만들었다가, 사전프로그램을 위해서 json까지 챙기는 건 번거롭다고 판단하여

python 파일 안에 내장시켰다.

pcode_op_dictionary.py
0.02MB

 

 

자동완성 구현

tab을 누르면 Completer(tab을 인식해서 자동완성을 해주는 prompt_toolkit의 class)를 상속 받은 PcodeCompleter의 get_completions()함수를 실행한다.

그리고 get_completions() 내부에서 입력을 분석 -> 검색내용 후보군을 추린 값을 yeild를 통해서 생성.

get_completions의 yield된 값을 prompt_toolkit의 기능으로 후보가 여러줄로 나타나고 tab을 누르면 전환되게한다.