본문 바로가기

Project

[Project] Tico & Tico Simulator

728x90

1. 개요

이 포스팅에서는 나의 학교 Open-Source Software Lab (OSSL) 수업의 당시 괴물 같았던 과제들 중 하나인 TICO에 대한 회고 및 정리를 하고자 한다. 그 당시의 나는 과제에 대한 설명을 들었을 때 상당히 흥미롭다는 느낌과 더불어 과연 내가 이것을 해낼 수 있을지 걱정이 앞섰다. 더불어 이 수업은 이와 같은 과제가 계속해서 제시되었기 때문에 다른 수업들 듣는 나를 비롯한 모든 학생들 입장에서는 큰 부담으로 다가왔었다. 하지만 교수님의 과제는 항상 나에게 있어 가히 흥미로웠기에 완벽을 기하기 위해 노력했던 것으로 기억한다. 상당히 공들였던 과제이니만큼 나의 여럿 프로젝트의 일환으로서 해당 과제의 설명과 해결과정을 상세히 기록한다.

2. 과제 설명

과제 원문

  1. TICO is a simple computer with 15 instructions and 256 memory addresses.
  2. Each team is asked to construct a C program that executes a given TICO assembly code.
  3. Also, you are asked to write TICO assembly programs to solve given problems, and show the results using your TICO simulators.

한국어 해석

  1. TICO 는 15개의 명령어과 256개의 메모리 주소를 갖는 간단한 컴퓨터이다.
  2. 주어진 TICO 어셈블리어로 작성된 코드를 구동할 수 있는 프로그램 (TICO 시뮬레이터) 을 C언어로 개발하라.
  3. 또한 주어진 문제를 해결하는 코드를 TICO 어셈블리어로 작성하고, 작성한 코드를 개발한 TICO 시뮬레이터로 컴파일하여 결과를 보여라.

TICO 는 교수님께서 직접 창작하신 개념으로 12개의 명령어256개의 메모리 주소를 갖는 개념적 컴퓨터이다. 이 컴퓨터에는 해당 명령어와 메모리를 최대한 활용하는 TICO 어셈블리어가 존재한다. TICO 어셈블리어는 교수님께서 여타 어셈블리어를 수월하게 이해할 수 있도록 특별제작한 언어이다. 하지만 개념만 존재하며 실체가 없기 때문에 해당 언어를 구동시킬 시뮬레이터를 C언어로 제작하는 것이 1차 목표이고, 해당 컴파일러가 제대로 제작되었는지 확인하기 위한 프로그래밍 문제 4개를 TICO 어셈블리어로 해결하는 것이 2차 목표이다.

2.1. TICO (TIny COmputer)

다음은 TICO 에 대한 설명이다.

  • 메모리(Memory)는 주소(Address)를 값(Value)으로 매핑한다.
  • 메모리 주소(Memory Address)0 부터 255 까지의 음이 아닌 정수로 이루어진다.
  • 값(Value)은 1Byte 의 정수 또는 명령어(Instruction)에 해당한다.
    • 정수는 -128 부터 127 까지만 표현 가능하다.
    • 명령어는 15개를 갖는다.
    • 초기화되지 않은 메모리는 0의 값을 갖는다.
    • 실제 컴퓨터와 달리 TICO 는 1Byte 값을 표현할 수 없다.
  • JUMP, JUMPIF 명령어를 제외하면 순차실행을 기본으로 한다.
  • 첫 명령어는 0번 메모리 주소에 위치해야 한다.
  • 잘못된 명령에 대해서는 즉시 오류 메시지를 출력하고 종료한다.

2.1.1. 명령 표

Instruction Description
READ [m] 사용자로부터 숫자를 받아 [m] 에 저장한다.
WRITE [m] [m] 에 저장된 숫자를 출력한다.
ASSIGN [m] "c" [m] 에 숫자 c 를 저장한다.
MOVE [md] [ms] [ms] 에 저장된 숫자를 [md] 에 저장한다.
LOAD [md] [ms] [ms] 에 저장된 메모리 주소의 값을 [md] 에 저장한다.
STORE [md] [ms] [md] 에 저장된 값을 [ms] 에 저장된 메모리 주소에 저장한다.
ADD [md] [mx] [my] [mx], [my] 에 각각 저장된 값을 더하여 [md] 에 저장한다.
MINUS [md] [mx] [my] [mx] 에 저장된 값에서 [my] 에 저장된 값을 빼 [md] 에 저장한다.
MULT [md] [mx] [my] [mx], [my] 에 각각 저장된 값을 곱하여 [md] 에 저장한다.
MOD [md] [mx] [my] [mx] 에 저장된 값에서 [my] 에 저장된 값을 나눈 나머지를 [md] 에 저장한다.
EQ [md] [mx] [my] [mx] 와 [my] 에 저장된 값이 같으면 [md] 에 1을, 그렇지 않으면 0을 저장한다.
LESS [md] [mx] [my] [mx] 에 저장된 값이 [my] 에 저장된 값보다 작으면 [md] 에 1을, 그렇지 않으면 0을 저장한다.
JUMP [m] [m] 번으로 이동하여 명령어를 실행한다.
JUMPIF [m] [c] [c] 에 저장된 값이 0이 아니면 [m] 번으로 이동하여 저장된 명령어를 실행한다.
TERM 프로그램을 종료한다.

2.2. TICO 어셈블리어

다음은 TICO 어셈블리어에 대한 설명이다.

  • TICO 어셈블리 프로그램은 어셈블리 명령 목록이 저장된 하나의 텍스트 기반 파일이다.
  • 코드의 각 줄은 특정 주소에 명령 또는 값이 저장되어 있음을 나타낸다.
    • <address>: <value>

[예제1] 입력한 숫자가 홀수면 0, 짝수면 1을 출력하는 프로그램

0: READ 21
1: EQ 23 21 22
2: JUMPIF 10 23
3: JUMPIF 7 24
4: MOVE 24 20
5: MINUS 21 21 20
6: JUMP 1
7: MOVE 24 22
8: MINUS 21 21 20
9: JUMPIF 1 20
10: WRITE 24
11: TERM
20: "1"
21:
22: "0"
23:
24: "1"

[예제2] 입력한 10개의 숫자 중 최솟값을 출력하는 프로그램

0: READ 23
1: LESS 25 24 23
2: JUMPIF 4 25
3: MOVE 24 23
4: MINUS 20 20 21
5: LESS 23 22 20
6: JUMPIF 0 23
7: WRITE 24
8: TERM
20: "10"
21: "1"
22: "0"
23:
24: "127"

2.2.1. 구현할 문제

TICO 어셈블리어로 구현해야할 문제는 다음과 같다.

  1. 두 개의 자연수를 입력받아 최대 공약수를 출력하는 프로그램
  2. 8자리의 이진수를 입력받아 십진수로 변환하여 출력하는 프로그램
  3. 20 미만의 자연수 5개를 입력받아 평균을 구한 후 올림한 값을 출력하는 프로그램
  4. 10개의 입력된 정수 중 최빈값을 출력하는 프로그램

2.3. TICO 시뮬레이터

다음은 TICO 시뮬레이터에 대한 설명이다.

  1. 작동시킬 TICO 어셈블리 프로그램의 파일 이름을 입력할 수 있도록 한다.
  2. READ 명령에 대하여 표준입력 (stdin) 으로부터 정수를 입력받는다.
  3. WRITE 명령에 대하여 표준출력 (stdout) 으로부터 정수를 출력한다.
  4. 잘못된 명령에 대해서는 오류 메시지를 출력하고 프로그램을 종료한다.
    • 잘못된 메모리 주소 할당
    • 산술 오류 (overflow, underflow, divide-by-zero 등)

2.3.1. 워크플로우

  1. 입력 파일을 읽는다.
    1.1. 각 줄을 값으로 변환하고, 알맞은 메모리 주소에 저장한다.
    1.2. 입력값이 잘못되었으면 프로그램을 종료한다.
  2. 프로그램을 실행한다.
    2.1. 실행 위치(Cursor)를 0으로 설정한다.
    2.2. 해당 실행 위치의 명령(Instruction)을 실행한다.
    2.2.1. 주어진 명령에 대하여 메모리를 알맞게 사용한다.
    2.2.2. JUMPJUMPIF 명령에 대해서는 각각 알맞은 위치로 실행 위치(Cursor)를 최신화한다.
    2.2.3. TERM 명령에 대하여 프로그램을 종료한다.
    2.2.4. 명령이 잘못되었으면 오류 메시지를 출력 후 프로그램을 종료한다.
    2.2.5. 위 경우 모두 해당사항 없을 경우 실행 위치(Cursor)의 값을 1 증가시킨다.

2.4. 과제 요구사항

과제는 다음의 요구사항이 수반된다.

  • 값(Value)을 표현하는데 struct, enum, union 를 사용해야 한다.
  • atoi, strtok, strcmp 의 문자열 라이브러리 함수를 반드시 그리고 적절히 사용하여야 한다.
  • 프로그램을 Makefile 를 사용하여 동작시킬 수 있어야 한다.

3. TICO 시뮬레이터 구현

3.1. #define 상수

#define MAX_SCHR 127    // maximum of signed char
#define MIN_SCHR -128   // minimum of signed char
#define MAX_UCHR 255    // maximum of unsigned char
#define MIN_UCHR 0      // minimum of unsigned char
#define MEM_SIZ 256     // size of memory
#define MEM_VIS 80      // memory range to visualize
Expression Description Value
MAX_SCHR char 최댓값 127
MIN_SCHR char 최솟값 -128
MAX_UCHR unsigned char 최댓값 255
MIN_UCHR unsigned char 최솟값 0
MEM_SIZ TICO 메모리 크기 256
MEM_VIS visMem() 를 통해 표시할 메모리의 크기 80

3.2. 열거형 enum

3.2.1. CompileErrorType

typedef enum { 
  NO_OPR, OPD_NUM, 
  WF_COLON, WF_DQ,
  NUM_IPT, NUM_ADR, NUM_OPD, NUM_VAL, NUM_ASN
} CompileErrorType;
Value Expression Description
0 NO_OPR Operator undefined
1 OPD_NUM Unmatched number of operand
2 WF_COLON Wrong format of colon(:)
3 WF_DQ Wrong format of double quotes(")
4 NUM_IPT Problem with the input number
5 NUM_ADR Problem with the number of address
6 NUM_OPD Problem with the number of operand
7 NUM_VAL Problem with the number of Value
8 NUM_ASN Problem with the number of Constant

3.2.2. NumErrorType

typedef enum {
  OVFL, NOT_NUM, DIV_ZERO
} NumErrorType;
Value Expression Description
0 OVFL Overflow
1 NOT_NUM Not a number
2 DIV_ZERO Divided by zero

3.2.3. Type

typedef enum { VALUE,  
  READ,   WRITE,  ASSIGN, MOVE, 
  LOAD,   STORE,  ADD,    MINUS, 
  MULT,   MOD,    EQ,     LESS, 
  JUMP,   JUMPIF, TERM,   TYPE_LEN
} Type;

2.1.1. 참고

3.3. 구조체 struct

3.3.1. Instruction

typedef struct {
  unsigned char operands[3];
  Type operator;
} Instruction;

모든 명령어는 1개의 연산자(Operator)0개 이상의 피연산자(Operand)를 갖는다. 이때 피연산자는 최대 3개를 가질 수 있다.


이를 그림으로 표현하면 다음과 같다.



3.4. 공용체 union

3.4.1. MemoryCell

typedef union {
  char value;
  Instruction inst;
} MemoryCell;

모든 메모리는 명령어 또는 을 저장한다. 즉, 명령어와 값을 동시에 가질 수 없으므로 메모리 공간을 공유하는 공용체를 사용하여 구현하였다.


이를 그림으로 표현하면 다음과 같다.



3.5. 전역변수

3.5.1. fp

입력 파일의 포인터이다.

  • 자료형: FILE *

3.5.2. memory

TICO 의 메모리를 나타내는 배열이다.

  • 자료형: MemoryCell *

3.5.3. iptCount

input count


입력을 요하는 TICO 어셈블리 프로그램의 경우에 현재까지 입력된 개수를 세기위한 변수이다.

  • 자료형: int

3.5.4. optCount

output count


TICO 어셈블리 프로그램의 현재까지 출력된 개수를 세기위한 변수이다.

  • 자료형: int

3.5.5. filename

읽고자 하는 파일의 이름이다.

  • 자료형: chat *

3.5.6. curType

current type


현재 실행 위치(Cursor) 의 Type 을 저장하는 변수이다.

  • 자료형: Type

3.5.7. r, c

row, column


오류 메시지를 표시할 때 어디에서 오류가 발생했는지 표시할 때 사용할 변수이다.

  • 자료형: int

3.5.8. instStrs

instruction strings(string array)


명령어 문자열을 포함하는 정적 배열이다.

const char *instStrs[TYPE_LEN] = {
  0x0, "READ", "WRITE", "ASSIGN", "MOVE", "LOAD", 
  "STORE", "ADD", "MINUS", "MULT", "MOD", 
  "EQ", "LESS", "JUMP", "JUMPIF", "TERM",
};
  • 자료형: char **

3.5.9. params

parameters


각 명령어의 매개변수 개수를 담은 정적 배열이다.

const int params[TYPE_LEN] = {
-1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 1, 2, 0,
};
  • 자료형: int *

3.6. 함수

3.6.1. 문자열-정수 확장 함수

3.6.1.1. intlen()

integer length


주어진 정수에서 숫자(Digit) 의 개수를 계산한다.


  • 프로토타입
int intlen(int i);
  • 구현부
/// @brief Calculates the number of digits in a given integer 
///        when it is converted to a string
/// @param i The integer to calculate the number of digits for
/// @return The number of digits in the integer as an integer value
int intlen(int i) { return 1 + (i ? (int) log10(i) : 1); }
  • 매개변수
Type Expression Description
int i 숫자 개수를 계산하고자 하는 정수
  • 반환값
Type Description
int 정수 i 의 숫자(Digit) 개수
  • 예시
intlen(3);     // 1
intlen(50);    // 2
intlen(1234);  // 4

3.6.1.2. extractNumber()

주어진 문자열에서 첫번째로 등장하는 정수를 추출한다.


  • 프로토타입
int extractNumber(char *str);
  • 구현부
/// @brief Extracts the first integer found in a given character string and returns its integer value
/// @param str The character string to search for the first integer
/// @return The integer value of the first integer found in the character string
int extractNumber(char *str) {
  char numStr[10];
  int count = 1, first;

  for (int i = 0; i < strlen(str); i++) {
    numStr[0] = '+';
    if (!isdigit(str[i])) continue;
    if (count == 1) first = i;
    numStr[count++] = str[i];
  }
  numStr[count] = '\0';
  if (str[first - 1] == '-') numStr[0] = '-';

  return atoi(numStr);
}
  • 매개변수
Type Expression Description
char * str 첫 정수를 추출하고자 하는 문자열
  • 반환값
Type Description
int 문자열 str 에서 추출된 첫 정수
  • 예시
extractNumber("123");      // 123
extractNumber("a123");     // 123
extractNumber("123a");     // 123
extractNumber("b-123");    // -123
extractNumber("a123b45");  // 123

3.6.1.3. ctoi()

constant to integer


쌍따옴표(")로 둘러싸인 정수를 포함하는 문자열에서 정수만을 추출하고, 해당 작업 중 오류 발생 시 오류 번호를 반환한다.


  • 프로토타입
int ctoi(char *str, int *num, int min, int max);
  • 구현부
/// @brief Converts a string representation of an integer within double quotes to an integer value, and checks if it falls within a specified range
/// @param str The string containing the integer within double quotes
/// @param num A pointer to an integer where the parsed integer value will be store
/// @param min The minimum allowed value for the integer
/// @param max The maximum allowed value for the integer
/// @return An integer value indicating success (0) or an error code (1-4)
int ctoi(char *str, int *num, int min, int max) {
  *num = extractNumber(str);

  char *dqPtr = strchr(str, '\"');
  if (!dqPtr || str[0] != '\"') return 1;

  char *end = strchr(dqPtr + 1, '\"');
  if (!end) return 2;
  *end = '\0';

  int val = atoi(str + 1);
  if (!val && strcmp(str + 1, "0")) return 3;
  if (val < min || val > max) return 4;
  return 0;
}
  • 매개변수
Type Expression Description
char * str 큰따옴표(")로 둘러싸인 정수를 포함하는 문자열
int * num 추출한 정수를 저장할 포인터 변수
int min 정수의 최소 허용 범위
int max 정수의 최대 허용 범위
  • 반환값
Type Value Description
int 0 오류없음
int 1 큰따옴표(")가 존재하지 않음
int 2 큰따옴표(")가 닫히지 않음
int 3 숫자가 포함되지 않음
int 4 오버플로우
  • 예시
ctoi("\"12\"",  &num, MIN_SCHR, MAX_SCHR);  // num:  12, return: 0
ctoi("\"-10\"", &num, MIN_SCHR, MAX_SCHR);  // num: -10, return: 0
ctoi("12",      &num, MIN_SCHR, MAX_SCHR);  // num:  12, return: 1
ctoi("\"12",    &num, MIN_SCHR, MAX_SCHR);  // num:  12, return: 2 
ctoi("\"ab\"",  &num, MIN_SCHR, MAX_SCHR);  // num:   0, return: 3
ctoi("\"999\"", &num, MIN_SCHR, MAX_SCHR);  // num: 999, return: 4

3.6.2. TICO 의 주요 함수

3.6.2.1. readLine()

전역변수 fp 가 가리키는 파일의 전체 문자열을 반환한다.


  • 프로토타입
char *readLine();
  • 구현부
/// @brief Reads a line from a file pointed to by the 
///        global variable fp, dynamically allocating memory 
///         as needed to store the line
/// @return A pointer to a dynamically allocated string 
///         containing the read line, or a null pointer 
///         if the end of the file is reached
char *readLine() {
  static char buf[BUFSIZ];
  static int buf_n = 0;
  static int curr = 0;

  if (feof(fp) && curr == buf_n - 1) return 0x0;

  char *s = 0x0;
  size_t s_len = 0;
  do {
    int end = curr;
    while (!(end >= buf_n || !iscntrl(buf[end]))) end++;
    if (curr < end && s != 0x0) { curr = end; break; }
    curr = end;
    while (!(end >= buf_n || iscntrl(buf[end]))) end++;
    if (curr < end) {
      if (s == 0x0) {
        s = strndup(buf + curr, end - curr);
        s_len = end - curr;
      }
      else {
        s = realloc(s, s_len + end - curr + 1);
        s = strncat(s, buf + curr, end - curr);
        s_len = s_len + end - curr;
      }
    }
    if (end < buf_n) { curr = end + 1; break; }

    buf_n = fread(buf, 1, sizeof(buf), fp);
    curr = 0;
  } while (buf_n > 0);

  return s;
}
  • 반환값
Type Description
char * fp 가 가리키는 파일의 전체 문자열
  • 예시
readLine(); // Lorem ipsum dolor sit amet...

3.6.2.2. initMem()

initialize memory


메모리 (memory) 를 초기화한다.



  • 프로토타입
void initMem();
  • 구현부
/// @brief Initializes the global memory array by setting all values to zero and setting each instruction to a value operator with no operands
void initMem() {
  for (int addr = 0; addr < MEM_SIZ; addr++) {
    memory[addr].inst.operator = VALUE;
    for (int i = 0; i < 3; i++)
      memory[addr].inst.operands[i] = 0;
    memory[addr].value = 0;
  }
}

3.6.2.3. visMem()

현재 메모리 상태를 사용자 친화적 표 형태로 출력한다.


  • 프로토타입
void visMem();
  • 구현부
/// @brief Visualize a formatted table of the contents of the memory
void visMem() {
  printf("\n┏");
  for (int i = 0; i < 63; i++) printf("━"); printf("┓");
  printf("\n┃ MEMORY (0~%d) %47s", MEM_VIS - 1, "");
  if (MEM_VIS - 1 < 100) printf(" ");
  if (MEM_VIS - 1 < 10) printf(" ");
  printf("┃\n┠");
  for (int i = 0; i < 31; i++) printf("─"); printf("┬");
  for (int i = 0; i < 31; i++) printf("─"); printf("┨");
  for (int addr = 0; addr < MEM_VIS / 2 + MEM_VIS % 2; addr++) {
    printf("\n");
    for (int i = 0; i < 2; i++) {
      printf(i ? "│ " : "┃ ");
      int idx = (MEM_VIS / 2 + MEM_VIS % 2) * i + addr;
      if (idx == MEM_VIS) { printf("%30s", ""); break; }
      printf("%3d | ", idx);
      Instruction inst = memory[idx].inst;
      if (inst.operator > 0 && inst.operator < TYPE_LEN) {
        printf("%6s | ", instStrs[inst.operator]);
        for (int j = 0; j < 3; j++) {
          if (j >= params[inst.operator]) printf("     ");
          else if (inst.operator == ASSIGN && j == 1) 
            printf("%4d ", (char) inst.operands[j]);
          else printf("%4d ", inst.operands[j]);
        }
        continue;
      }
      printf("%6s | %4d %9s ", "VALUE", memory[idx].value, "");
    }
    printf("┃");
  }
  printf("\n┗");
  for (int i = 0; i < 31; i++) printf("━"); printf("┷");
  for (int i = 0; i < 31; i++) printf("━"); printf("┛\n\n");
}
  • 출력예시
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ MEMORY (0~79)                                                 ┃
┠───────────────────────────────┬───────────────────────────────┨
┃   0 |   READ |   60           │  40 |  VALUE |    0           ┃
┃   1 |   READ |   61           │  41 |  VALUE |    0           ┃
┃   2 |  MINUS |   62   62   51 │  42 |  VALUE |    0           ┃
┃   3 | JUMPIF |    5   62      │  43 |  VALUE |    0           ┃
┃   4 |   JUMP |   14           │  44 |  VALUE |    0           ┃
┃   5 |     EQ |   61   61   50 │  45 |  VALUE |    0           ┃
┃   6 | JUMPIF |    1   61      │  46 |  VALUE |    0           ┃
┃   7 |   MOVE |   63   62      │  47 |  VALUE |    0           ┃
┃   8 |   MOVE |   64   51      │  48 |  VALUE |    0           ┃
┃   9 |   MULT |   64   64   52 │  49 |  VALUE |    0           ┃
┃  10 |  MINUS |   63   63   51 │  50 |  VALUE |    0           ┃
┃  11 | JUMPIF |    9   63      │  51 |  VALUE |    1           ┃
┃  12 |    ADD |   65   65   64 │  52 |  VALUE |    2           ┃
┃  13 |   JUMP |    1           │  53 |  VALUE |    8           ┃
┃  14 |     EQ |   61   61   50 │  54 |  VALUE |   10           ┃
┃  15 | JUMPIF |   17   61      │  55 |  VALUE |   12           ┃
┃  16 |    ADD |   65   65   51 │  56 |  VALUE |    0           ┃
┃  17 | JUMPIF |   20   60      │  57 |  VALUE |    0           ┃
┃  18 |  WRITE |   65           │  58 |  VALUE |    0           ┃
┃  19 |   TERM |                │  59 |  VALUE |    0           ┃
┃  20 |    MOD |   66   65   54 │  60 |  VALUE |    0           ┃
┃  21 |  MINUS |   67   65   66 │  61 |  VALUE |    0           ┃
┃  22 | JUMPIF |   24   67      │  62 |  VALUE |    7           ┃
┃  23 |   JUMP |   27           │  63 |  VALUE |    0           ┃
┃  24 |  MINUS |   67   67   54 │  64 |  VALUE |    0           ┃
┃  25 |    ADD |   62   62   51 │  65 |  VALUE |    0           ┃
┃  26 | JUMPIF |   24   67      │  66 |  VALUE |    0           ┃
┃  27 |    ADD |   66   66   53 │  67 |  VALUE |    0           ┃
┃  28 |   LESS |   68   66   54 │  68 |  VALUE |    0           ┃
┃  29 | JUMPIF |   32   68      │  69 |  VALUE |    0           ┃
┃  30 |  MINUS |   66   66   54 │  70 |  VALUE |    0           ┃
┃  31 |    ADD |   62   62   51 │  71 |  VALUE |    0           ┃
┃  32 |    ADD |   62   62   55 │  72 |  VALUE |    0           ┃
┃  33 |  WRITE |   62           │  73 |  VALUE |    0           ┃
┃  34 |  WRITE |   66           │  74 |  VALUE |    0           ┃
┃  35 |   TERM |                │  75 |  VALUE |    0           ┃
┃  36 |  VALUE |    0           │  76 |  VALUE |    0           ┃
┃  37 |  VALUE |    0           │  77 |  VALUE |    0           ┃
┃  38 |  VALUE |    0           │  78 |  VALUE |    0           ┃
┃  39 |  VALUE |    0           │  79 |  VALUE |    0           ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

3.6.2.4. ignoreComment()

주어진 문자열 line// 문자열 뒤의 문자열을 모두 제거한다.


  • 프로토타입
void ignoreComment(char *line);
  • 구현부
/// @brief Ignores inline comments from a given string
/// @param line The input string to be processed
void ignoreComment(char *line) { 
  char *ptr = strstr(line, "//"); 
  if (ptr) *ptr = '\0'; 
}
  • 매개변수
Type Expression Description
char * line 주석을 제거할 문자열
  • 예시
ignoreComment("ab//cde");               // line: "ab"
ignoreComment("abc//de");               // line: "abc"
ignoreComment("3: TERM // terminates"); // line: "3: TERM "

3.6.2.5. splitAdrInst()

split address instruction


콜론(:) 을 기준으로 메모리 주소 부분과 명령어 부분을 분리한다.


  • 프로토타입
void splitAdrInst(char *str, int *addr, char **inst);
  • 구현부
/// @brief Splits a string containing an address and an instruction separated by a colon into its address and instruction components
/// @param str  the string to be split
/// @param addr a pointer to an integer that will hold the parsed address
/// @param inst a pointer to a char pointer that will point to the parsed instruction
void splitAdrInst(char *str, int *addr, char **inst) {
  char *line; 
  char *colonPtr, *addrPtr, *instPtr, *endPtr;
  strcpy(line, str);

  colonPtr = strchr(str, ':');
  if (!colonPtr) { c++; compileError(WF_COLON, line); }
  *colonPtr = '\0';

  addrPtr = str;
  instPtr = colonPtr + 1;

  while (*instPtr == ' ') instPtr++;
  endPtr = instPtr + strlen(instPtr) - 1;

  while (endPtr > instPtr && *endPtr == ' ') {
    *endPtr = '\0'; endPtr--;
  }

  *addr = atoi(addrPtr);

  bool nonNumeric = !*addr && strcmp(addrPtr, "0");
  bool overflow = *addr < MIN_UCHR || *addr > MAX_UCHR;

  if (overflow) numError(NUM_ADR, NOT_NUM, line);
  if (nonNumeric) numError(NUM_ADR, OVFL, line);

  *inst = instPtr;
}
  • 매개변수
Type Expression Description
char * str 메모리 주소 부분과 명령어 부분을 분리하고자 하는 문자열
int * addr 메모리 주소를 저장할 변수
char * inst 명령어 문자열을 저장할 변수
  • 예시
splitAdrInst("1: MOVE 10 20");  // addr: 1, inst: "MOVE 10 20"
splitAdrInst("2: ADD 3 4 5");   // addr: 2, inst: "ADD 3 4 5"
splitAdrInst("3: TERM");        // addr: 3, inst: "TERM"

3.6.2.6. saveToMem()

save to memory


TICO 어셈블리 코드를 읽어 TICO 메모리에 해당 내용을 적절히 저장한다.


  • 프로토타입
void saveToMem(int addr, char *str, char *line);
  • 구현부
/// @brief A function that saves str into the memory at the addr position after processing it appropriately
/// @param addr specific memory address to save
/// @param str  assembly code information to process
/// @param line pure line from the file
void saveToMem(int addr, char *str, char *line) {
  bool hasSpace = strchr(str, ' ');

  char *typeStr;
  typeStr = strtok(str, " ");
  curType = toType(typeStr);
  c += 1 + intlen(addr);

  bool noOperator = true;
  noOperator &= strcmp(typeStr, "VALUE");
  noOperator &= !curType;
  noOperator &= !strcmp(typeStr, "0") || !atoi(typeStr);

  if (noOperator) { c++; compileError(NO_OPR, line); }

  bool isTerm = !strcmp(str, "TERM");
  bool isAssign = !strcmp(str, "ASSIGN");

  if (hasSpace) {
    c += 1 + strlen(typeStr);

    if (isTerm) compileError(OPD_NUM, line);
    char *token = strtok(NULL, " ");

    int i = 0, operand = 0;
    for (; i < 3; i++) {
      if (!token) break;

      if (isAssign && i == 1) {
        int err = ctoi(token, &operand, MIN_SCHR, MAX_SCHR);

        switch (err) {
          case 4: c += 2; numError(NUM_ASN, OVFL, line);
          case 3: c += 2; numError(NUM_ASN, NOT_NUM, line);
          case 2: c += 2 + intlen(operand);
          case 1: compileError(WF_DQ, line);
          default: break;
        }
      }
      else {
        c++;
        operand = atoi(token);

        bool nonNumeric = !operand && strcmp(token, "0");
        bool overflow = operand < MIN_UCHR || operand > MAX_UCHR;

        if (overflow) numError(NUM_OPD, OVFL, line);
        if (nonNumeric) numError(NUM_OPD, NOT_NUM, line);

        c += intlen(operand);
      }

      memory[addr].inst.operands[i] = operand;

      token = strtok(NULL, " ");

      if (token && i + 1 >= params[curType]) compileError(OPD_NUM, line);
    }

    if (i < params[curType]) compileError(OPD_NUM, line);

    memory[addr].inst.operator = curType;
  }
  else {
    if (isTerm) { memory[addr].inst.operator = TERM; return; }

    int n, err;
    err = ctoi(str, &n, MIN_UCHR, MAX_UCHR);
    switch (err) {
      case 4: numError(NUM_VAL, OVFL, line); break;
      case 3: c += 2; numError(NUM_VAL, NOT_NUM, line); break;
      case 2: c += 2 + intlen(n);
      case 1: compileError(WF_DQ, line); break;
      case 0: memory[addr].value = n; break;
      default: break;
    }

    return;
  }
  if (!toType(str)) compileError(NO_OPR, line);

}
  • 매개변수
Type Expression Description
int addr 저장할 메모리 주소
char * str 실행할 TICO 어셈블리 코드
char * line "<addr>: <str>"

3.6.2.7. toType()

문자열을 Type (사실 상 정수) 으로 변환한다.


  • 프로토타입
Type toType(char *str);
  • 구현부
/// @brief Converts a string to a Type enumeration value
/// @param str The string to convert
/// @return The Type value corresponding to the input string
Type toType(char *str) {
  for (int i = 1; i < TYPE_LEN; i++)
    if (!strcmp(instStrs[i], str)) return i;
  return VALUE;
}
  • 매개변수
Type Expression Description
char * str Type 으로 바꾸고자 하는 문자열
  • 예시
toType("24")     // 0 (VALUE)
toType("READ")   // 1 (READ)
toType("STORE")  // 6 (STORE)

3.6.2.8. execute()

주어진 메모리 주소 번호의 명령을 실행한다.

  • 프로토타입
int execute(int addr);
  • 구현부
/// @brief Executes the instruction at the given memory address
/// @param addr The memory address of the instruction to execute
/// @return The address of the next instruction to execute, or a negative value if an error occurred.
int execute(int addr) {
  Instruction inst = memory[addr].inst;
  if (!inst.operator) return addr + 1;

  int reg0 = inst.operands[0];
  int reg1 = inst.operands[1];
  int reg2 = inst.operands[2];

  switch (memory[addr].inst.operator) {
    case READ   : return read  (addr, reg0);
    case WRITE  : return write (addr, reg0);
    case ASSIGN : return assign(addr, reg0, reg1);
    case MOVE   : return move  (addr, reg0, reg1);
    case LOAD   : return load  (addr, reg0, reg1);
    case STORE  : return store (addr, reg0, reg1);
    case ADD    : return add   (addr, reg0, reg1, reg2);
    case MINUS  : return minus (addr, reg0, reg1, reg2);
    case MULT   : return mult  (addr, reg0, reg1, reg2);
    case MOD    : return mod   (addr, reg0, reg1, reg2);
    case EQ     : return eq    (addr, reg0, reg1, reg2);
    case LESS   : return less  (addr, reg0, reg1, reg2);
    case JUMP   : return jump  (addr, reg0);
    case JUMPIF : return jumpIf(addr, reg0, reg1);
    case TERM   : return -1;
    default: break;
  }

  return -2;
}
  • 매개변수
Type Expression Description
int addr 명령을 실행할 메모리 주소
  • 반환값
Type Value Description
int n 다음으로 실행할 메모리 주소 번호
int -1 종료 조건

3.6.2.9. ticoInput()

사용자로부터 입력을 받아 정수로 변환하여 반환한다.


  • 프로토타입
int ticoInput();
  • 구현부
/// @brief Reads user input and validates it, returning a valid input as an integer.
/// @return A valid input as an integer.
int ticoInput() {
  int n; 
  bool overflow = false;
  bool nonNumeric = false;

  if (!iptCount++) printf("  << INPUT >>\n");
  do {
    if (overflow) inputError(OVFL);
    if (nonNumeric) inputError(NOT_NUM);

    overflow = false;
    nonNumeric = false;

    printf("  %d > ", iptCount); 
    nonNumeric |= !scanf("%d", &n);
    while (getchar() != '\n');
    overflow |= n < MIN_SCHR || n > MAX_SCHR;
  } while (nonNumeric || overflow);

  return n;
}
  • 반환값
Type Description
int 입력받은 정수

3.6.2.10. ticoOutput()

주어진 정수를 출력한다.

  • 프로토타입
void ticoOutput(int n);
  • 구현부
/// @brief Outputs an integer value to the console
/// @param n The integer value to output
void ticoOutput(int n) {
  if (!optCount) printf("\n  << OUTPUT >>\n  ");
  printf("%d", n); optCount++;
}
  • 매개변수
Type Expression Description
int n 출력하고자 하는 정수

3.6.3. 오류 처리 관련 함수

3.6.3.1. fileIOError()

파일 입출력 오류 발생 시 호출되어 오류 메시지를 출력하고 프로그램을 종료한다.


  • 프로토타입
void fileIOError();
  • 구현부
/// @brief This function is called in case of file I/O errors
void fileIOError() {
  char *msg = malloc(100);
  char *desc = malloc(100);

  if (strcmp(filename, "")) {
    strcpy(msg, "File not found");
    strcpy(desc, "`\033[1;31m");
    strcat(desc, filename);
    strcat(desc, "\033[0;31m`");
    sprintf(desc, "%s file does not exist!", desc);
  }
  else {
    strcpy(msg, "Filename not entered");
    strcpy(desc, "Enter the filename");
  }

  visError(msg, desc);

  free(msg);
  free(desc);
  exit(-1);
}

3.6.3.2. inputError()

잘못된 입력에 대하여 오류 메시지를 출력한다.


  • 프로토타입
void inputError(NumErrorType type);
  • 구현부
/// @brief Displays an error message for invalid input
/// @param type The type of error encountered
void inputError(NumErrorType type) {
  char *msg = malloc(100);
  char *desc = malloc(100);

  switch (type) {
  case OVFL: strcpy(desc, "Value overflow"); break;
  case NOT_NUM: strcpy(desc, "Non-numeric Value"); break;
  default: break;
  }

  strcpy(msg, "Input error");
  strcat(desc, "\n\n\033[0;32m");
  sprintf(desc, "%s Enter value %d ~ %d integer", desc, MIN_SCHR, MAX_SCHR);

  visError(msg, desc);

  free(msg);
  free(desc);
}
  • 매개변수
Type Expression Description
NumErrorType type 산술오류 종류

3.6.3.3. compileError()

컴파일 오류를 발생시키고 메시지를 출력한 뒤 프로그램을 종료한다.


  • 프로토타입
void compileError(CompileErrorType type, char *line);
  • 구현부
/// @brief Generates a compile error message and displays it
/// @param type The type of compile error that occurred
/// @param line The line of code that caused the error
void compileError(CompileErrorType type, char *line) {
  char *msg = malloc(100);
  char *desc = malloc(100);

  switch (type) {
    case NO_OPR: 
      strcpy(msg, "Operator undefined"); 
      strcpy(desc, "Existing operator expected"); 
      break;
    case OPD_NUM: 
      strcpy(msg, "Unmatched number of operand");
      sprintf(
        desc, "%s operator must have %d operand%s", 
        instStrs[curType], 
        params[curType], 
        params[curType] > 1 ? "s" : ""
      );
      break;
    case WF_COLON: 
      strcpy(msg, "Wrong format"); 
      strcpy(desc, ":"); 
      break;
    case WF_DQ: 
      strcpy(msg, "Wrong format"); 
      strcpy(desc, "\""); 
      break;
    default: break;
  }

  visCompileError(msg, desc, line);

  free(msg);
  free(desc);
  exit(-1);
}
  • 매개변수
Type Expression Description
CompileErrorType type 컴파일 오류 종류
char * line 오류가 발생한 TICO 어셈블리 코드

3.6.3.4. numError()

number error


산술오류를 발생시키고 메시지를 출력한 뒤 프로그램을 종료한다.


  • 프로토타입
void numError(CompileErrorType cType, NumErrorType nType, char *line);
  • 구현부
/// @brief Report a numeric error and terminate the program
/// @param cType The type of numeric object that caused the error
/// @param nType The type of numeric error that occurred
/// @param line  The line of source code where the error occurred
void numError(CompileErrorType cType, NumErrorType nType, char *line) {
  int min, max;
  char *obj = malloc(100);
  char *msg = malloc(100);
  char *desc = malloc(100);

  switch (cType) {
    case NUM_ASN: 
      min = MIN_SCHR; max = MAX_SCHR; 
      strcpy(obj, "Constant"); break;
    case NUM_VAL: 
      min = MIN_SCHR; max = MAX_SCHR; 
      strcpy(obj, "Value"); break;
    case NUM_ADR: 
      min = MIN_UCHR; max = MAX_UCHR; 
      strcpy(obj, "Address"); break;
    case NUM_OPD: 
      min = MIN_UCHR; max = MAX_UCHR; 
      strcpy(obj, "Operand"); break;
    default: break;
  }

  switch (nType) {
    case OVFL:
      sprintf(msg, "%s overflow", obj); 
      sprintf(desc, "Set %s %d ~ %d integer", obj, min, max);
      break;
    case NOT_NUM: 
      sprintf(msg, "Non-numeric %s", obj); 
      sprintf(desc, "Set %s %d ~ %d integer", obj, min, max);
      break;
    case DIV_ZERO: 
      strcpy(msg, "Divide by zero"); 
      strcpy(desc, "Cannot be divided by 0");
      break;
    default: break;
  }

  visCompileError(msg, desc, line);

  free(obj);
  free(msg);
  free(desc);
  exit(-1);
}
  • 매개변수
Type Expression Description
CompileErrorType type 컴파일 오류 종류
NumErrorType nType 산술오류 종류
char * line 오류가 발생한 TICO 어셈블리 코드

3.6.3.5. visError()

오류 메시지를 빨간색으로 출력한다.


  • 프로토타입
void visError(char *msg, char *desc);
  • 구현부
/// @brief Displays an error message and its description on the console in red color
/// @param msg The error message to be displayed
/// @param desc The description of the error to be displayed
void visError(char *msg, char *desc) {
  fprintf(stderr, "\033[0;31m");
  fprintf(stderr, "\n  [ERROR] %s", msg);
  fprintf(stderr, "\n  %s\n\n", desc);
  fprintf(stderr, "\033[0m");
}
  • 매개변수
Type Expression Description
char * msg 오류 메시지
char * desc 오류 메시지 설명

3.6.3.6. visCompileError()

컴파일 오류 메시지와 오류가 나타난 위치를 출력한다.


  • 프로토타입
void visCompileError(char *msg, char *desc, char *line);
  • 구현부
/// @brief Prints a compile error message with additional 
///        information about the error location and code 
///        line where it occurred
/// @param msg The error message to be displayed
/// @param desc The description of the error to be displayed
/// @param line The code line where the error occurred
void visCompileError(char *msg, char *desc, char *line) {
  fprintf(stderr, "\033[0;31m");
  fprintf(stderr, "\n  [ERROR] Compile error");
  fprintf(stderr, "\n  %s", msg);
  fprintf(stderr, "\033[0m\n\n");

  if (line) {
    fprintf(stderr, "\033[1;37m");
    fprintf(stderr, "  %s:%d:%d", filename, r, c);
    fprintf(stderr, "\033[0m");
    fprintf(stderr, "\n  %s\n  ", line);

    fprintf(stderr, "\033[0;32m");
    for (int e = 0; e < c - 1; e++) fprintf(stderr, "~"); fprintf(stderr, "^\n");
    for (int e = 0; e < c - 1; e++) fprintf(stderr, " ");
    fprintf(stderr, "  %s\n\n", desc);
    fprintf(stderr, "\033[0m");
  }
}
  • 매개변수
Type Expression Description
char * msg 오류 메시지
char * desc 오류 메시지 설명
char * line 오류가 발생한 TICO 어셈블리 코드

3.6.4. 명령 함수

각 명령어에 대하여 실행되는 함수이다.


  • 프로토타입
void save(int m, int v);

int read   (int cur, int m);
int write  (int cur, int m);

int assign (int cur, int m, int c);
int move   (int cur, int md, int ms);
int load   (int cur, int md, int ms);
int store  (int cur, int md, int ms);

int add    (int cur, int md, int mx, int my);
int minus  (int cur, int md, int mx, int my);
int mult   (int cur, int md, int mx, int my);
int mod    (int cur, int md, int mx, int my);

int eq     (int cur, int md, int mx, int my);
int less   (int cur, int md, int mx, int my);

int jump   (int cur, int m);
int jumpIf (int cur, int m, int c);
  • 구현부
void save(int m, int v) {
  memory[m].value = v;
  addToHistory(m, v);
  // visHistory();
}
int read(int cur, int m) {
  int n = ticoInput();
  save(m, n);
  return cur + 1;
}
int write(int cur, int m) { 
  ticoOutput(memory[m].value);
  return cur + 1; 
}
int assign(int cur, int m, int c) { 
  save(m, c);
  return cur + 1;
}
int move(int cur, int md, int ms) { 
  save(md, memory[ms].value);
  return cur + 1;
}
int load(int cur, int md, int ms) { 
  if (memory[ms].value < MIN_UCHR) numError(NUM_VAL, OVFL, 0x0);
  save(md, memory[memory[ms].value].value);
  return cur + 1;
}
int store(int cur, int md, int ms) { 
  if (memory[md].value < MIN_UCHR) numError(NUM_VAL, OVFL, 0x0);
  save(memory[md].value, memory[ms].value);
  return cur + 1;
}
int add(int cur, int md, int mx, int my) { 
  save(md, memory[mx].value + memory[my].value);
  return cur + 1;
}
int minus(int cur, int md, int mx, int my) { 
  save(md, memory[mx].value - memory[my].value);
  return cur + 1;
}
int mult(int cur, int md, int mx, int my) {  
  save(md, memory[mx].value * memory[my].value);
  return cur + 1;
}
int mod(int cur, int md, int mx, int my) { 
  if (!memory[my].value) numError(NUM_VAL, DIV_ZERO, 0x0);
  save(md, memory[mx].value % memory[my].value);
  return cur + 1;
}
int eq(int cur, int md, int mx, int my) { 
  save(md, memory[mx].value == memory[my].value); 
  return cur + 1;
}
int less(int cur, int md, int mx, int my) { 
  save(md, memory[mx].value < memory[my].value);
  return cur + 1;
}
int jump(int cur, int m) { return m; }
int jumpIf(int cur, int m, int c) { return memory[c].value ? m : cur + 1; }
  • 매개변수
Type Expression Description
int cur 오류 메시지
int m 1개의 메모리 번호
int c 상수
int ms, md 2개의 선후관계가 있는 메모리 번호
int mx, my 2개의 대등한 메모리 번호
  • 반환값
Type Value Description
int n 다음으로 실행할 메모리 주소 번호

3.6.4. 메모리 히스토리 관리 함수

3.6.4.1. initHistory()

메모리 히스토리를 초기화한다.

  • 프로토타입
void initHistory();
  • 구현부
/// @brief Initializes the global memory history array by setting
void initHistory() {
  for (int i = 0; i < MEM_SIZ; i++) {
    history[i] = (HistoryNode *) malloc(sizeof(HistoryNode));
    history[i]->data = memory[i].value;
    history[i]->next = NULL;
  }
}

3.6.4.2. addToHistory()

메모리에 히스토리에 값을 추가한다.


  • 프로토타입
void addToHistory(int index, int value);
  • 구현부
/// @brief Adds a value to the memory history array at the specified index
/// @param index The index where the value should be added
/// @param value The value to be added
void addToHistory(int index, int value) {
  HistoryNode* current = history[index];
  while (current->next != NULL) {
    current = current->next;
  }
  HistoryNode* newNode = (HistoryNode *) malloc(sizeof(HistoryNode));
  newNode->data = value;
  newNode->next = NULL;
  current->next = newNode;
}
  • 매개변수
Type Expression Description
int index 값을 최신화 할 메모리의 주소
int value 최신화 할 값

3.6.4.3. visHistory()

메모리 히스토리를 사용자 친화적으로 출력한다.


  • 프로토타입
void visHistory();
  • 구현부
/// @brief Visualizes the history of memory values that have been changed
void visHistory() {
  printf("\033[0;34m");
  printf("\n    Memory History\n");
  printf("\033[0m");
  for (int i = 0; i < MEM_SIZ; i++) {
    HistoryNode* current = history[i];

    bool isValue = memory[i].inst.operator == VALUE;
    bool isZero = !memory[i].value;
    bool hasNext = current->next;

    if (!isValue) continue;
    if (isZero && !hasNext) continue;

    printf("\033[0;34m");
    printf("    %2d: ", i);
    printf("\033[0m");
    printf("[");
    while (current != NULL) {
      printf("%d", current->data);
      if (current->next) printf(", ");
      current = current->next;
    }
    printf("]\n");
  }
  printf("\n");
}
  • 출력예시
Memory History
51: [1]
52: [2]
53: [8]
54: [10]
55: [12]
60: [0, 0]
61: [0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0]
62: [7, 6, 5, 4, 3, 2, 1, 0]
63: [0, 6, 5, 4, 3, 2, 1, 0, 5, 4, 3, 2, 1, 0, 1, 0]
64: [0, 1, 2, 4, 8, 16, 32, 64, 1, 2, 4, 8, 16, 32, 1, 2]
65: [0, 64, 96, 98, 99]

3.6.4.4. freeHistory()

메모리 히스토리의 메모리 할당을 해제한다.


  • 프로토타입
void freeHistory();
  • 구현부
/// @brief Frees the memory allocated for the memory history array
void freeHistory() {
  for (int i = 0; i < MEM_SIZ; i++) {
    HistoryNode* current = history[i];
    while (current != NULL) {
      HistoryNode* temp = current;
      current = current->next;
      free(temp);
    }
  }
}

4. TICO 어셈블리 프로그램 작성

해당 과제의 풀이는 최적의 정답이 아니다. 단순히 내가 냈던 과제 답안이며 당시의 사고 과정을 복기하여 설명하고자 함이다.

설명상의 편의를 위해 다음의 기호를 사용한다.

  • [m]: m 번 메모리
  • <m>: [m] 에 저장된 값

4.1. [과제1] 두 개의 자연수를 입력받아 최대 공약수를 출력하는 프로그램

4.1.1. 예시

  • 입력
  << INPUT >>
  1 > 12
  2 > 15
  • 출력
  << OUTPUT >>
  3

4.1.2. 풀이

4.1.2.1. 유클리드 호제법

임의의 두 정수의 최대공약수를 구하는 알고리즘이다.

정의

  • $g(a, b)$: 임의의 두 정수 $a$, $b$ 의 최대공약수
  • $r(a, b)$: 정수 $a$ 를 정수 $b$ 로 나눈 나머지

  1. $g(a, b) = g(b, r(a, b))$ 의 식은 항상 성립한다.
  2. 임의의 두 정수를 각각 $m_0$, $n_0$ 이라고 한다.
  3. 음이 아닌 정수 $k$ 에 대하여 다음과 같이 정의한다.
    $$ \begin{cases} m_{k+1} = n_k \\ n_{k+1} = r(m_k, n_k) \end{cases} $$
  4. $n_i = 0$ 을 만족시키는 음이 아닌 정수 $i$ 에 대하여 $g(m_0, n_0) = m_i$ 이다.

4.1.2.2. 전체코드

0: READ 10
1: READ 11
2: MOVE 12 11
3: MOD 11 10 11
4: MOVE 10 12
5: JUMPIF 2 11
6: WRITE 10
7: TERM

4.1.2.3. 메모리 변화 예시

사용자 입력을 12, 15 로 가정하고 메모리 변화를 단계별로 표현하면 다음과 같다.

Step Instruction [10] [11] [12] Output
1 0: READ 10 12 0 0
2 1: READ 11 12 15 0
2 2: MOVE 12 11 12 15 15
3 3: MOD 11 10 11 12 12 15
4 4: MOVE 10 12 15 12 15
5 5: JUMPIF 2 11 15 12 15
6 2: MOVE 12 11 15 12 12
7 3: MOD 11 10 11 15 3 12
8 4: MOVE 10 12 12 3 12
9 5: JUMPIF 2 11 12 3 12
10 2: MOVE 12 11 12 3 3
11 3: MOD 11 10 11 12 0 3
12 4: MOVE 10 12 3 0 3
13 5: JUMPIF 2 11 3 0 3
14 6: WRITE 10 3 0 3 3
15 7: TERM 3 0 3 3

4.2. [과제2] 8자리의 이진수를 입력받아 십진수로 변환하여 출력하는 프로그램

4.2.1. 예시

  • 입력
  << INPUT >>
  1 > 1
  2 > 0
  3 > 1
  4 > 0
  5 > 0
  6 > 0
  7 > 1
  8 > 0
  • 출력
  << INPUT >>
  162

4.2.2. 풀이

설명 상의 편의를 위해 먼저 십진수와 이진수 표기를 정의한다.

  • 일의자리 D, 십의자리 C, 백의자리 B, 천의자리 A 값을 갖는 십진수를 ABC(10) 와 같이 표기한다.
  • $2^0$ 의자리 H, $2^1$ 의자리 G, $2^2$ 의자리 F, $2^3$ 의자리 E, $2^4$ 의자리 D, $2^5$ 의자리 C, $2^6$ 의자리 B, $2^7$ 의자리 A 값을 갖는 이진수를 ABCDEFGH(2) 와 같이 표기한다.

4.2.2.1. 설명

이 문제가 가장 어려웠는데, 해당 과제에는 두 가지 큰 난관이 존재한다.



[난관1] 오버플로우


TICO 컴퓨터의 각 메모리 레지스터는 1Byte 의 값만을 담을 수 있다. 즉 정수 -128(10) ~ 127(10) 사이의 값을 가질 수 있는 것이다. 하지만 8자리의 이진수 즉 00000000(2) ~ 11111111(2) 의 범위는 십진수로 나타내면 0(10) ~ 255(10) 이므로 문제가 생긴다.

  << INPUT >>
  1 > 1
  2 > 1
  3 > 1
  4 > 1
  5 > 1
  6 > 1
  7 > 1
  8 > 1

위와 같은 입력이 들어오면 다음과 같이 출력해야 하는데,

  << OUTPUT >>
  255

이는 TICO 의 메모리 레지스터 하나가 가질 수 있는 값의 범위를 벗어난다.


즉, 레지스터에 값을 누적하려는 경우에

이진수 십진수
00000001(2) 1(10)
00000010(2) 2(10)
00000100(2) 4(10)
00001000(2) 8(10)
00010000(2) 16(10)
00100000(2) 32(10)
01000000(2) 64(10)
10000000(2) -128(10) (Overflow)

위와 같은 오버플로우가 발생할 수 있고

WRITE [m]

의 명령으로도 255(10) (128(10) 이상) 를 출력할 수 없게 되는 것이다.


우리는 한 가지를 생각할 수 있다.


255(10) 를 하나의 숫자가 아닌 25(10)5(10) 로 나누되 공백없이 출력한다면 원하는 목표에 달성할 수 있을 것이다.


다만 2(10)55(10) 로 나누는 방법은 좋지 않다. 뒤에 오는 수가 $[0, 10)$ 범위에 해당할 경우 원하는 결과를 얻지 못할 것이다. 가령, 209(10) 로 출력되어야 하는 값은 29(10) 와 같이 표시될 것이므로 이에 관한 조건 처리를 추가적으로 해주어야 한다는 문제가 생기기 때문이다.


따라서 과정은 크게 다음의 두 가지로 구분된다.

  1. 127(10) 이하의 수 ABC(10) 의 경우 별도의 작업 없이 출력한다.
  2. 128(10) 이상의 수 ABC(10) 의 경우 AB(10)C(10) 으로 나누어 각 정수를 공백 없이 연달아 출력한다.

위 두 가지 경우를 나누는 것은 첫 입력값 즉, $128(2^7)$ 의 자리이다.


그렇기 때문에 가장 첫 입력값은 따로 취급해야하고, 그 값에 따라서 다른 동작을 수행해야한다. 따라서 다음과 같이 조건문을 사용하여 동작 범위를 구분할 수 있다.

??: READ X       // 사용자 입력을 받아 [X] 에 저장
??: JUMPIF ?? X  // <X> 에 따라 JUMP 여부 결정

동작 범위를 구분한 것까지는 완료했지만 그래서 $128$ 이상의 수에 대하여 과연 어떻게 처리하도록 구현할 것인지는 다른 문제이다.


XXXXXXX(2) 를 공유하는 1XXXXXXX(2)0XXXXXXX(2) 의 두 이진수는 정확히 $128$ 만큼의 차이를 갖는다. 이 말은 즉, 1XXXXXXX(2) 를 십진수로 변환할 때 XXXXXXX(2) 의 부분은 기존과 같이 계산 후 $128$ 을 더하면 된다는 생각으로 연결될 수 있다.


다만, 128(10) 역시 12(10)8(10) 으로 나누어 각각 더해주어야 한다는 점에 유의한다.

즉, 입력값 1XXXXXXX(2) 에 대하여 XXXXXXX(2) 이 십진수로 ABC(10) 의 값을 갖는다고 할 때,

DE(10) = AB(10) + 12(10)
F(10) = C(10) + 8(10)

정수 DE(10) 를 먼저 출력하고 공백없이 이어서 정수 F(10) 를 출력하면 된다.


여기서 한 가지 문제가 더 발생하는데, F(10)캐리(Carry) 값을 가질 수 있다는 점이다. 즉, F(10) 가 아닌 FG(10) 의 값을 가질 경우에 해당한다.


해당 캐리(Carry) 값DE(10) 에 더하고, F(10) 에서 빼어 올바른 출력에 도달할 수 있다.


가령, 입력값 1110110(2) 의 경우를 살펴보자.


첫 자리 1(2) 을 제외한 110110(2)54(10) 의 값을 갖기 때문에

AB(10) = 5(10)
C(10) = 4(10)

이고 DE(10)F(10) 를 구하면 다음과 같다.

DE(10) = 5(10) + 12(10) = 17(10) 
F(10) = 4(10) + 8(10) = 12(10) 

F(10) 는 반드시 한 자리수여야 하는데 12(10) 라는 값을 갖게되었고, 이 경우 출력값은 1712(10) 으로 올바른 값이 아니다.


따라서 실질적인 DE(10)F(10) 의 값은 다음과 같이 구해야 한다.

DE(10) = 17(10) + 1(10) = 18(10)
F(10) = 12(10) - 10(10) = 2(10)

이 경우 출력값은 182(10) 으로 정확히 1110110(2) 와 같은 값을 나타낸다.


정리하면 다음과 같다.

입력값 PXXXXXXX(2) 에 대하여 십진수를 출력해야 하는 경우

XXXXXXX(2)ABC(10) 의 값을 갖는다고 할 때,

  1. P(2) = 0(2) 일 경우, ABC(10) 을 출력한다.
  2. P(2) = 1(2) 일 경우,
    DE(10) = AB(10) + 12(10)
    FG(10) = C(10) + 8(10)
    HI(10) = DE(10) + F(10)
    위의 계산을 완료하고 HIG(10) 을 출력한다.



[난관2] 나눗셈(몫) 연산의 부재


교수님께서는 의도적인지 실수인지 알 수 없지만 TICO 어셈블리 언어에 나눗셈 연산을 포함시키지 않으셨다. 이에 뺄셈을 반복함으로써 나눗셈(몫) 연산을 구현하여야 했다.


예를 들어, 사용자가 입력한 두 정수를 $x$, $y$ 라고 할 때, $\left \lfloor \frac{x}{y} \right \rfloor$ 를 구하기 위한다고 가정하자.


만약 DIV 명령이 존재한다면,

0: READ 10
1: READ 11
2: DIV 12 10 11
3: WRITE 12
4: TERM

으로 끝날 수 있지만 TICO 어셈블리어는 그렇지 않다.

MINUS조건문과 반복문을 적절히 혼합하여 사용해야 한다.

0: READ 10
1: READ 11
2: LESS 13 10 12
3: JUMPIF 7 13
4: MINUS 10 10 11
5: ADD 14 14 9
6: JUMP 2
7: WRITE 14
8: TERM
9: "1"
14: "-1"

위 TICO 어셈블리 코드가 두 정수를 입력받아 한 수를 다른 수로 나눈 몫을 출력하는 프로그램이다.

4.2.2.2. 전체코드

0: READ 60
1: READ 61
2: MINUS 62 62 51
3: JUMPIF 5 62 
4: JUMP 14 
5: EQ 61 61 50
6: JUMPIF 1 61
7: MOVE 63 62 
8: MOVE 64 51 
9: MULT 64 64 52
10: MINUS 63 63 51
11: JUMPIF 9 63 
12: ADD 65 65 64 
13: JUMP 1
14: EQ 61 61 50
15: JUMPIF 17 61
16: ADD 65 65 51
17: JUMPIF 20 60
18: WRITE 65
19: TERM
20: MOD 66 65 54
21: MINUS 67 65 66
22: JUMPIF 24 67
23: JUMP 27
24: MINUS 67 67 54
25: ADD 62 62 51
26: JUMPIF 24 67
27: ADD 66 66 53
28: LESS 68 66 54
29: JUMPIF 32 68
30: MINUS 66 66 54
31: ADD 62 62 51
32: ADD 62 62 55
33: WRITE 62
34: WRITE 66
35: TERM
51: "1" 
52: "2"
53: "8"
54: "10"
55: "12"
62: "7"

4.2.2.3. 메모리 변화 예시

다음으로 몇 가지 입력값에 따른 메모리 변화를 visHistory() 함수를 통해 관찰한다.


  • 예시1

입력

  << INPUT >>
  1 > 0
  2 > 1
  3 > 0
  4 > 0
  5 > 0
  6 > 0
  7 > 0
  8 > 0

메모리 변화

Memory History
51: [1]
52: [2]
53: [8]
54: [10]
55: [12]
60: [0, 0]
61: [0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
62: [7, 6, 5, 4, 3, 2, 1, 0]
63: [0, 6, 5, 4, 3, 2, 1, 0]
64: [0, 1, 2, 4, 8, 16, 32, 64]
65: [0, 64]

출력

  << OUTPUT >>
  64
  • 예시2

입력

  << INPUT >>
  1 > 1
  2 > 0
  3 > 0
  4 > 0
  5 > 0
  6 > 0
  7 > 0
  8 > 0

메모리 변화

Memory History
51: [1]
52: [2]
53: [8]
54: [10]
55: [12]
60: [0, 1]
61: [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
62: [7, 6, 5, 4, 3, 2, 1, 0, 12]
66: [0, 0, 8]
67: [0, 0]
68: [0, 1]

출력

  << OUTPUT >>
  128
  • 예시3

입력

  << INPUT >>
  1 > 1
  2 > 1
  3 > 0
  4 > 0
  5 > 0
  6 > 0
  7 > 0
  8 > 0

메모리 변화

Memory History
51: [1]
52: [2]
53: [8]
54: [10]
55: [12]
60: [0, 1]
61: [0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
62: [7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 19]
63: [0, 6, 5, 4, 3, 2, 1, 0]
64: [0, 1, 2, 4, 8, 16, 32, 64]
65: [0, 64]
66: [0, 4, 12, 2]
67: [0, 60, 50, 40, 30, 20, 10, 0]
68: [0, 0]

출력

  << OUTPUT >>
  192

4.3. [과제3] 20 미만의 자연수 5개를 입력받아 평균을 구한 후 올림한 값을 출력하는 프로그램

4.3.1. 예시

  • 입력
  << INPUT >>
  1 > 10
  2 > 12
  3 > 5
  4 > 11
  5 > 8
  • 출력
  << OUTPUT >>
  10

4.3.2. 풀이

4.3.2.1. 설명

해당 문제는 자비롭게도 20 미만의 자연수만 입력받는다는 제약이 걸려있다. 이는 5개의 자연수를 모두 합하는 과정에서 발생하는 오버플로우를 완전히 배제하려는 의도로 추측되며 이에 따라 TICO 어셈블리 코드를 작성하는데 [과제2] 에 비해 쉬운 난이도로 느껴졌다.


해당 과제 문제는 다음의 단계에 따라 해결할 수 있다.

  1. 반복문을 통해 5번 사용자 입력을 받아 특정 메모리에 값을 누적시킨다.
  2. 반복문이 끝나면 누적된 값을 5로 나눈 몫 $Q$ 과 나머지 $R$ 를 구한다.
  3. $R = 0$ 이면 $Q$ 를, $R \ne 0$ 이면 $Q + 1$ 를 출력한다.

이 역시 나눗셈(몫) 연산을 필요로 하기 때문에 [과제2] 와 비슷하게 구현한다. 다만 한 가지 주의해야 할 점은 올림값을 구해야 한다는 것이다. 몫 연산은 내림 연산이므로 나머지를 따로 구하여 나머지가 0이 아닐 경우에 한해 몫에 1을 더해주어야 올바른 값을 구할 수 있다.

4.3.2.2. 전체코드

0: READ 60
1: ADD 61 61 60
2: MINUS 62 62 51
3: JUMPIF 0 62
4: MOD 63 61 52
5: MINUS 64 61 63
6: MINUS 64 64 52
7: ADD 62 62 51
8: JUMPIF 6 64
9: EQ 63 63 50
10: EQ 63 63 50
11: ADD 62 62 63
12: WRITE 62
13: TERM
50: "0"
51: "1"
52: "5"
62: "5"
78: "9"

4.3.2.3. 메모리 변화 예시

다음으로 몇 가지 입력값에 따른 메모리 변화를 visHistory() 함수를 통해 관찰한다.

  • 예시1

입력

  << INPUT >>
  1 > 10
  2 > 12
  3 > 5
  4 > 11
  5 > 8

메모리 변화

Memory History
51: [1]
52: [5]
60: [0, 10, 12, 5, 11, 8]
61: [0, 10, 22, 27, 38, 46]
62: [5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
63: [0, 1, 0, 1]
64: [0, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0]
78: [9]

출력

  << OUTPUT >>
  10
  • 예시2

입력

  << INPUT >>
  1 > 1
  2 > 2
  3 > 3
  4 > 4
  5 > 5

메모리 변화

Memory History
51: [1]
52: [5]
60: [0, 1, 2, 3, 4, 5]
61: [0, 1, 3, 6, 10, 15]
62: [5, 4, 3, 2, 1, 0, 1, 2, 3, 3]
63: [0, 0, 1, 0]
64: [0, 15, 10, 5, 0]
78: [9]

출력

  << OUTPUT >>
  3

4.4. [과제4] 10개의 입력된 정수 중 최빈값을 출력하는 프로그램

4.4.1. 예시

  • 입력
  << INPUT >>
  1 > 10
  2 > 20
  3 > 30
  4 > 10
  5 > 20
  6 > 40
  7 > 50
  8 > 30
  9 > 10
  10 > 10
  • 출력
  << OUTPUT >>
  10

4.4.2. 풀이

4.4.2.1. 설명

이 문제는 입력된 값들의 빈도를 파악해야 한다. 여러 가지 알고리즘을 택할 수 있지만 나는 고정적으로 50번 부터 69번 까지의 주소를 가지는 총 20개의 메모리를 배열처럼 사용하는 방식을 채택하였다.

메모리를 총 20개만 사용한다는 것은 아니다. 물론 계산이나 값 저장을 위한 메모리를 추가적으로 사용한다.


[5X] 에는 사용자 입력값을 열 개의 메모리에 순서대로 저장한다.

주소 50 51 52 53 54 55 56 57 58 59
10 20 30 10 20 40 50 30 10 10

그 후에 [6X] 에는 다음의 순서에 따라 값을 업데이트 한다.

for i=0, i<10, i++
  for j=i, j<10, j++
    <6j> += <5i> == <5j>
주소 60 61 62 63 64 65 66 67 68 69
1 1 1 2 2 1 1 2 3 4

위와 같은 값을 갖게되며 가장 큰 값을 갖는 메모리 주소 [6X] 에 대하여 <5X> 값이 최빈값이 된다.

4.4.2.2. 전체코드

0: MOVE 70 43
1: READ 71
2: STORE 70 71
3: ADD 70 70 41
4: LESS 72 70 44
5: JUMPIF 1 72
6: MOVE 70 43
7: LOAD 73 70
8: LOAD 74 70
9: EQ 76 73 74
10: ADD 75 70 42 
11: LOAD 77 75
12: ADD 77 77 76
13: STORE 75 77
14: ADD 70 70 41
15: LESS 72 70 44
16: JUMPIF 8 72
17: MINUS 70 70 78
18: MOVE 79 78
19: MINUS 78 78 41
20: JUMPIF 7 79
21: MINUS 70 70 41
22: ADD 75 70 42
23: LOAD 73 75
24: LESS 72 73 79
25: JUMPIF 28 72
26: MOVE 79 73
27: LOAD 80 70
28: LESS 72 43 70
29: JUMPIF 21 72
30: WRITE 80
31: TERM
41: "1"
42: "10"
43: "50"
44: "60"
78: "9"

4.4.2.3. 메모리 변화 예시

  • 예시

입력

  << INPUT >>
  1 > 10
  2 > 20
  3 > 30
  4 > 10
  5 > 20
  6 > 40
  7 > 50
  8 > 30
  9 > 10
  10 > 10

메모리 변화

Memory History
41: [1]
42: [10]
43: [50]
44: [60]
50: [0, 10]
51: [0, 20]
52: [0, 30]
53: [0, 10]
54: [0, 20]
55: [0, 40]
56: [0, 50]
57: [0, 30]
58: [0, 10]
59: [0, 10]
60: [0, 1]
61: [0, 0, 1]
62: [0, 0, 0, 1]
63: [0, 1, 1, 1, 2]
64: [0, 0, 1, 1, 1, 2]
65: [0, 0, 0, 0, 0, 0, 1]
66: [0, 0, 0, 0, 0, 0, 0, 1]
67: [0, 0, 0, 1, 1, 1, 1, 1, 2]
68: [0, 1, 1, 1, 2, 2, 2, 2, 2, 3]
69: [0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 4]
70: [0, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 52, 53, 54, 55, 56, 57, 58, 59, 60, 53, 54, 55, 56, 57, 58, 59, 60, 54, 55, 56, 57, 58, 59, 60, 55, 56, 57, 58, 59, 60, 56, 57, 58, 59, 60, 57, 58, 59, 60, 58, 59, 60, 59, 60, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50]
71: [0, 10, 20, 30, 10, 20, 40, 50, 30, 10, 10]
72: [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
73: [0, 10, 20, 30, 10, 20, 40, 50, 30, 10, 10, 4, 3, 2, 1, 1, 2, 2, 1, 1, 1]
74: [0, 10, 20, 30, 10, 20, 40, 50, 30, 10, 10, 20, 30, 10, 20, 40, 50, 30, 10, 10, 30, 10, 20, 40, 50, 30, 10, 10, 10, 20, 40, 50, 30, 10, 10, 20, 40, 50, 30, 10, 10, 40, 50, 30, 10, 10, 50, 30, 10, 10, 30, 10, 10, 10, 10, 10]
75: [0, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 61, 62, 63, 64, 65, 66, 67, 68, 69, 62, 63, 64, 65, 66, 67, 68, 69, 63, 64, 65, 66, 67, 68, 69, 64, 65, 66, 67, 68, 69, 65, 66, 67, 68, 69, 66, 67, 68, 69, 67, 68, 69, 68, 69, 69, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60]
76: [0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1]
77: [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 1, 2, 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 0, 1, 0, 0, 1, 1, 2, 2, 2, 2, 0, 1, 1, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 3, 2, 3, 3, 4]
78: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1]
79: [0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 4]
80: [0, 10]

출력

  << OUTPUT >>
  10

5. Makefile 과 컴파일

해당 과제에는 앞서 언급했듯이 Makefile 를 사용하여 동작시킬 수 있어야 하는 요구사항이 존재한다. 컴파일 할 때 입력 파일이 존재하므로 해당 사실에 유의하여 Makefile 을 구성하면 다음과 같다.

all: tico run

tico: tico.c
    gcc tico.c -o tico -lm

run: tico $(file)
    ./tico $(file)

clean:
    rm -f tico

Makefile 을 바탕으로 다음의 명령어를 사용하여 컴파일할 수 있다.

입력 파일명은 filename.txt 이다.

make file=filename.txt

6. 인터페이스

컴파일이 완료되면 다음의 인터페이스를 확인할 수 있다.

6.1. 메인

Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  
  filename.txt file successfully loaded
  

6.2. 입력

6.2.1 첫 번째 입력

  << INPUT >>
  1 > 

6.2.2 두 번째 입력

  << INPUT >>
  1 > [첫 번째 입력값]
  2 > 

6.3. 출력

  << OUTPUT >>
  [출력값]

Program terminated!
GOOD BYE!

7. 오류 처리

해당 과제에는 다양한 오류 처리 또한 요구 사항으로써 존재한다.

다음의 오류 상황이 존재한다.

  • 컴파일 오류
Expression Description
NO_OPR Operator undefined
OPD_NUM Unmatched number of operand
WF_COLON Wrong format of colon(:)
WF_DQ Wrong format of double quotes(")
NUM_IPT Problem with the input number
NUM_ADR Problem with the number of address
NUM_OPD Problem with the number of operand
NUM_VAL Problem with the number of Value
NUM_ASN Problem with the number of Constant
  • 산술오류
Expression Description
OVFL Overflow
NOT_NUM Not a number
DIV_ZERO Divided by zero

7.1. 예시

7.1.1. Non-numeric Address

  • 입력 파일 내용
a: ADD 1 2 3       // error: Non-numeric address
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Non-numeric Address
  
  test/err.txt:1:1
  a: ADD 1 2 3       
  ^
  Set Address 0 ~ 255 integer

7.1.2. Address overflow

  • 입력 파일 내용
-1: ADD 1 2 3      // error: Address overflow
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Address overflow
  
  test/err.txt:1:1
  -1: ADD 1 2 3       
  ^
  Set Address 0 ~ 255 integer

7.1.3. Wrong format

  • 입력 파일 내용
0 ADD 1 2 3        // error: Wrong format
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Wrong format
  
  test/err.txt:1:2
  0 ADD 1 2 3     
  ~^
   :



  • 입력 파일 내용
0: ASSIGN 1 2      // error: Wrong format
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Wrong format
  
  test/err.txt:1:12
  0: ASSIGN 1 2
  ~~~~~~~~~~~^
             "



  • 입력 파일 내용
0: ASSIGN 1 "2     // error: Wrong format
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Wrong format
  
  test/err.txt:1:15
  0: ASSIGN 1 "2
  ~~~~~~~~~~~~~~^
                "



  • 입력 파일 내용
0: 3               // error: Wrong format
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Wrong format
  
  test/err.txt:1:3
  0: 3
  ~~^
    "



  • 입력 파일 내용
0: "3              // error: Wrong format
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Wrong format
  
  test/err.txt:1:6
  0: "3
  ~~~~~^
       "

7.1.4. Operator undefined

  • 입력 파일 내용
0: AD 1 2 3        // error: Operator undefined
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operator undefined
  
  test/err.txt:1:4
  0: AD 1 2 3      
  ~~~^
     Existing operator expected



  • 입력 파일 내용
0: AD              // error: Operator undefined
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operator undefined
  
  test/err.txt:1:4
  0: AD     
  ~~~^
     Existing operator expected

7.1.5. Unmatched number of operand

  • 입력 파일 내용
0: ADD 1 2         // error: Unmatched number of operand
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Unmatched number of operand
  
  test/err.txt:1:11
  0: ADD 1 2   
  ~~~~~~~~~~^
            ADD operator must have 3 operands



  • 입력 파일 내용
0: ADD 1 2 3 3     // error: Unmatched number of operand
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Unmatched number of operand
  
  test/err.txt:1:13
  0: ADD 1 2 3 3   
  ~~~~~~~~~~~~^
              ADD operator must have 3 operands



  • 입력 파일 내용
0: TERM 1          // error: Unmatched number of operand
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Unmatched number of operand
  
  test/err.txt:1:8
  0: TERM 1    
  ~~~~~~~^
         TERM operator must have 0 operand

7.1.6. Operand overflow

  • 입력 파일 내용
0: LOAD 256 2      // error: Operand overflow
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operand overflow
  
  test/err.txt:1:9
  0: LOAD 256 2
  ~~~~~~~~^
          Set Operand 0 ~ 255 integer



  • 입력 파일 내용
0: LOAD 1 -1       // error: Operand overflow
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operand overflow
  
  test/err.txt:1:11
  0: LOAD 1 -1
  ~~~~~~~~~~^
            Set Operand 0 ~ 255 integer

7.1.7. Constant overflow

  • 입력 파일 내용
0: ASSIGN 1 "128"  // error: Constant overflow
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operand overflow
  
  test/err.txt:1:14
  0: ASSIGN 1 "128"
  ~~~~~~~~~~~~~^
               Set Constant -128 ~ 127 integer

7.1.8. Non-numeric constant

  • 입력 파일 내용
0: ASSIGN 1 "a"    // error: Non-numeric constant
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operand overflow
  
  test/err.txt:1:14
  0: ASSIGN 1 "a"
  ~~~~~~~~~~~~~^
               Set Constant -128 ~ 127 integer

7.1.9. Non-numeric value

  • 입력 파일 내용
0: "a"             // error: Non-numeric value
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Operand overflow
  
  test/err.txt:1:5
  0: "a"
  ~~~~^
      Set Value -128 ~ 127 integer

7.1.10. Divide by zero

  • 입력 파일 내용
0: MOD 1 2 3       // error: Divided by zero
  • 출력
Program starts ...
  
  *************************
  *  TICO: TIny COmputer  *
  *************************
  
  [ERROR] Compile error
  Divide by zero

8. 전체코드

전체코드는 아래 링크에서 확인할 수 있다.



728x90

'Project' 카테고리의 다른 글

[Project] 3D Renderer  (0) 2024.05.30