계산기코어 2.1 뜯어보기 1강. 전체구조와 호출방식

※주의 1
이 계산기 코어는 트리거로 작성하기에 적합하도록 짜여 있으며, 트리거의 여러 제약을
극복하기 위해 실제 프로그래밍에서는 권장하지 않는 구현기법이 일부 사용되었음을
알립니다.(예를들면 한개의 변수를 여러가지 용도로 사용한 경우)

※주의 2
이 글에선 C언어의 문법들(연산자나 함수, 포인터 등등)이 종종 등장합니다.




※예제맵 다운로드
1. noname50
사실 이 예제맵은 그냥 계산기코어 2.1

※첨부파일
1. 사용된 데스값, 스위치.txt




안녕하세요? noname01입니다.
이번에는 제가 제작한 계산 트리거인 "Calculator Core 2.1"의 내부를 살펴보도록 합시다.
저 계산 트리거를 만들고 나서 팁을 쓸 계획이 있었으나 미루고 미루다 이제서야 쓰게
되었네요.... 먼저 혹시나 기다리셨던 분들께 죄송하다는 말씀을 드립니다.

먼저 계산 트리거인 "Calculator Core 2.1"가 무엇이고 기능과 사용법은 대충
알고 넘어가야겠죠?? 아래 링크는 "Calculator Core 2.1"와 관련된 글로 이어집니다.

계산기 2.1

자 그럼 바로 본론으로 넘어가서 "Calculator Core 2.1"의 전체 구조를 먼저 살펴봅시다.



"Calculator Core 2.1"의 제일 위쪽에는 최소의 작업단위를 구성하는 트리거들로 이루어져
있습니다. 예를들면 '유닛1의 데스값을 유닛2의 데스값으로 옮긴다' 와 같은 트리거나
'유닛1의 데스값을 2배로 만들어 유닛2의 데스값으로 옮긴다' 와 같은 트리거들로 구성되어
있죠. 이 최소 구성단위들은 각종 계산을 하면서 재활용될 수 있는 일종의 '함수' 와 같은
역할을 수행합니다.

그 바로밑에는 오류가 발생할 경우 어떤 오류가 발생했는지를 디스플레이 텍스트 메시지로
출력해주는 트리거들이 모여 있습니다.

그리고 그 바로밑에는 수행하고자 하는 연산의 종류를 입력받아 그 연산을 수행하기위해
필요한 준비작업을 하는 트리거가 있으며,

마지막으로 가장 아래쪽에는, 입력받은 수와 연산종류를 바탕으로, 제일 위쪽에 위치하는
'함수'의 역할을 하는 트리거를 적절히 조합하여 호출하여, 실제 계산을 수행하는 트리거들로
이루어져 있습니다.



이때 아래쪽의 트리거들이 제일 위의 트리거들을 호출할때는 다음 팁과 유사한 방식을
사용합니다.

리스트식 디스플레이 텍스트 메시지 트리거

위의 링크로 이어진 팁에서는 디스플레이 텍스트 트리거를 호출했지만 계산기에서는
디스플레이 텍스트 트리거 대신 계산을 위한 '함수'의 역할을 하는 트리거들을 호출했다고
생각하시면 됩니다.

위 링크의 팁과 계산기의 '함수'트리거 호출방식의 또다른 차이점은 링크의 팁에서는 2진법을
이용했지만 계산기의 '함수'트리거 호출방식에서는 8진법을 이용했다는 점입니다. 처음에는
8진법이 필요할것 같아서 8진법을 채택했는데 이제 와서야 그냥 2진법을 사용해도 문제가
없다는 점을 깨달았습니다. ㅇㅅㅇ...

2진법의 경우 데스값 1개를 이용해서 32가지 종류의 트리거를 호출할 수 있는 반면,
8진법의 경우 동시에(트리거 동작 1주기 이내에)호출할 수 없는 트리거가 생기는 대신에
약 73가지 종류의 트리거를 호출할 수 있게 됩니다. 그런데 실제 완성시키고 보니 호출
해야할 트리거는 25가지밖에 되지 않더군요...

대신에 빈 공간이 많음으로써 차후에 '함수'의 역할을 하는 트리거들을 더 많이 추가할 수
있고 그것들을 조합해서 더 많은 연산을 수행할 수 있도록 만들 수도 있겠죠. 꼭 제가 하지
않더라도 이 구조를 이해하신 분들에 의해 다른 기능이 더 추가될 수도 있다고 할 수
있습니다.(이게 합리화로 보이는것은 기분탓일거에요. ^ㅅ^ )
이런거 다필요없고 2진수가 더편함

그럼 이 '함수' 역할을 하는 트리거들을 호출하기 위해 각 트리거에 붙여진 번호(주소)들을
살펴봅시다. 참고로 이 주소들을 저장할 변수는 c(소문자)로 표기하도록 하겠습니다.



================================================
8^10 * 1 : R=t;  t=0;
8^10 * 2 : r=t;  t=0;
8^10 * 3 : r=R;  R*=2;
================================================
8^9 * 1 : a=A;
8^9 * 2 : a=b=R;
8^9 * 3 : a=b=X/2; X=0;
8^9 * 4 : a=b=r; r=0;
8^9 * 5 : a=X;
8^9 * 6 :
8^9 * 7 : A=X;  X=0;
================================================
8^8 * 1 : b=B;
8^8 * 2 : b=X;
8^8 * 3 : b=r;
8^8 * 4 :
8^8 * 5 :
8^8 * 6 :
8^8 * 7 : B=X;  X=0;
================================================
8^7 * 7 :
8^7 * 6 :

8^7 * 5 : -
8^7 * 4 : -
8^7 * 3 : x=compare(&a, &b); ←이 함수는 나중에 설명할게요.
8^7 * 2 : x=a+b; a=b=0;
8^7 * 1 : X=a+b; a=b=0;
================================================
8^6 * 7 : if(x<=2) { R=2*X; X=0; ++A; }
8^6 * 6 :

8^6 * 5 : -
8^6 * 4 : X=t=0;

8^6 * 3 : -
8^6 * 2 : a=t;  t=0;
8^6 * 1 : X=a*b; t=a; a=b=0;
================================================
8^5 * 7 : if(A>=2^n) { ++R; A-=2^n; }
8^5 * 6 :
8^5 * 5 :
8^5 * 4 :

8^5 * 3 : -
8^5 * 2 : x=X;  X=0;
8^5 * 1 : X=r;  r=0;
================================================
8^4 * 1 : r=rand()%(2^n);
8^4 * 2 : A*=2;
8^4 * 3 :
8^4 * 4 :
     .
     . (이 중간은 다 비어있습니다.)
     .
8^0 * 5 :
8^0 * 6 :
8^0 * 7 :
================================================



잘 보시면 각 자릿수의 번호가 오름차순이 아닌 내림차순으로 뒤집어진 구간들이 존재하는데,
이 구간들은 내부에서 또다시 중복호출이 가능한 구조로 되어있습니다. 자세하게 살펴봅시다.

원래 8진수의 특정 자릿수 내부(줄로 구별된 각 칸 내부)에 있는 호출들끼리는 트리거 한
주기 내에 호출할 수 없습니다. 예를들면 8진수의 10의 자리에 속해있는 다음 두개의 호출은

8^9 * 2 : a=b=R;
8^9 * 3 : a=b=X/2; X=0;

트리거 한 주기 내에 동시호출할 수 없습니다. 왜냐구요?? 저걸 동시호출하게되면

8^9 * 2 + 8^9 * 3 = 8^9 * 5가 되어 위의 두개가 순서대로 호출되는게 아니라

8^9 * 5 : a=X;

가 호출되기 때문입니다.
그런데 만들다보니 한칸 내에서도 동시호출이 할 필요성이 생겼죠. 그래서 내부에서도
동시호출이 가능하도록 만든것입니다. 예를 들어 들어보면

================================================
8^6 * 7 : if(x<=2) { R=2*X; X=0; ++A; } ←얘는 독립
8^6 * 6 :                ←비어있지만 굳이 따지자면 독립

8^6 * 5 : - ←종속①
8^6 * 4 : X=t=0;

8^6 * 3 : - ←종속②
8^6 * 2 : a=t;  t=0;
8^6 * 1 : X=a*b; t=a; a=b=0;
================================================

이 경우 종속①을 호출하게 되면 그 자체는 비어있어 아무런 동작도 못하지만 그 대신에

 8^6 * 4 : X=t=0;
+ 8^6 * 1 : X=a*b; t=a; a=b=0;
---------------------------------------
= 8^6 * 5 : - ←종속①

이렇게 두개가 호출되게 됩니다.

마찬가지로 종속②를 호출하게 되면

 8^6 * 2 : a=t;  t=0;
+ 8^6 * 1 : X=a*b; t=a; a=b=0;
---------------------------------------
= 8^6 * 3 : - ←종속②

이렇게 두개가 호출되는 구조입니다.
애초에 2진수로 했으면 이렇게 복잡하게 할필요가 없었잖아!!! ㅇㅅㅇ

나머지 서로 다른칸에 있는 호출들끼리는 동시호출이 가능하도록 되어 있는데 이것은 위의
목록에서 한두개만 직접 계산해보면 왜 그런지 알 수 있을겁니다.



자 아까 위에서 compare라는 함수가 등장했는데 이것만 살펴보고 마치도록 하겠습니다.
우선 compare라는 함수의 정의는 다음과 같습니다.(※C언어 주의)



int compare(int* a, int* b)
{
  if(*a > *b) {
    *a -= *b;
    *b = 0;
    return 1;
  } else if(*a == *b) {
    *a = *b = 0;
    return 2;
  } else {
    *b -= *a;
    *a = 0;
    return 3;
  }
}



네 아주 간단한(?) 함수죠?물론 C언어를 공부해본 사람들에게만
C언어를 잘 모르시는 분들을 위해 저 함수의 의미를 설명해 보겠습니다.

먼저 아까 목록에서

8^7 * 3 : x=compare(&a, &b);

와 같이 되어 있었는데 이게 무슨뜻인가 한번 봅시다.


a와 b가 있다고 가정합니다.

①a > b 일때
a의 값에서 b의 값을 뺀 값을 a에 대입하고
b에는 0을, x에는 1을 대입한다.

②a = b 일때
a와 b에 각각 0을 대입하고
x에는 2를 대입한다.

③a < b 일때
b의 값에서 a의 값을 뺀 값을 b에 대입하고
a에는 0을, x에는 3을 대입한다.

이런 뜻입니다. 원래 이 함수는 'a - b' 를 계산하기 위한 함수였습니다. 그런데 a와 b의
크기를 비교하는 기능도 구현할 필요가 있었고, 트리거의 숫자를 줄이기 위해 두 기능을
하나로 묶어 버린 것입니다. 거기다가 음수처리가 안되는 문제가 있어서 더욱 복잡해진
것이죠. 그런데 C언어로 표현하면 복잡하지만 오히려 트리거로 표현하면 훨씬 간단하답니다.
애초에 트리거에 최적화될 수 있도록 만들었기 때문에 당연한 결과라고 할 수 있습니다.
(사실 저 위에서 이런게 꽤 있습니다.)

자 이번에는 여기까지 설명하도록 하고 나머지는 다음 강의에서 이어가도록 하겠습니다.




[50]계산기코어 2.1 뜯어보기 1강. 전체구조와 호출방식
[50]계산기코어 2.1 뜯어보기 2강. 오류와 입력처리
[50]계산기코어 2.1 뜯어보기 3강. 덧셈, 뺄셈, 곱셈
[50]계산기코어 2.1 뜯어보기 4강. 나눗셈
[50]계산기코어 2.1 뜯어보기 5강. 제곱근
[50]계산기코어 2.1 뜯어보기 6강. 거듭제곱, 팩토리얼, 순열
[50]계산기코어 2.1 뜯어보기 7강. 조합
[50]계산기코어 2.1 뜯어보기 8강. 난수 생성




다른 맵 제작 팁을 보려면 이 이미지를 클릭해 주세요.