LMC 동작 방식

LMC 란 Little Man Computer 의 약자로, 폰 노이만 구조를 가지며 프로그램 실행 원리를 파악할 수 있게 해줌으로써 교육용으로 많이 사용된다.

현재 실습중인 LMC는 웹ide에서 동작하고 있으며, 내부적으로 Loader나 Relocator를 갖고 있지 않다(Web IDE가 명령어를 메모리에 넣어준다는 뜻).


구성 요소

소스코드(기계어)를 작성하면 웹ide가 메모리에 명령어들을 배치해주고, LMC는 해당 명령어들을 자신의 작업 공간에서 기계적으로 수행한다.

LMC는 이러한 명령어들을 수행하기 위해서 아래와 같은 여러가지 도구들을 사용한다.

Mailboxes

메일박스는 메모리의 역할을 하는 공간이다.

LMC는 이 메일박스의 특정 위치에 들어있는 값을 토대로 자신이 해야 할 작업을 수행하는데, 메일박스에 들어있는 것이 명령어인지 데이터인지는 구분할 수 없다.

이를 구분하는 것은 사람만이 가능한 고급 태스크이고, 기계는 이를 구별하지 않고 설계된 회로대로 동작할 뿐이다.

메일박스에는 일단 총 100개의 공간(주소 00~99)이 있고, 각 공간마다 3자리의 숫자를 담을 수 있다.

따라서 데이터도 최대 3자리 숫자이고, 아래에서 볼 명령어들도 모두 3자리로 이루어져 있다.

Calculator

계산기는 LMC가 메일박스나 사용자의 입력(Basket)으로부터 값을 받아와서 저장해놓고, 다른 값과 연산할 수 있게 해준다.

더하기, 빼기와 같은 연산이 수행되고 나면 내부적으로 연산 결과에 대한 상태값을 갖고 있다.

즉, 해당 연산의 결과가 0인지에 대한(0/1), 자리올림(캐리)가 발생했는지에 대한 0/1, 양수인지 음수인지 등등 플래그를 유지한다.

단순히 메일박스에서 계산기로 값을 올리는 것은 연산이 일어난 것이 아니기 때문에 이러한 플래그를 변경시키지 않는다.

계산기에는 가장 최근에 올려진 값과 가장 최근에 연산한 결과의 상태값을 관리한다.

LMC가 취급하는 데이터들은 모두 최대 3자리 숫자들이기 때문에, 계산기도 마찬가지로 3자리 값을 취급한다.

Hand Counter

핸드카운터는 주소를 가지고 있어서, LMC가 매번 핸드카운터의 값을 확인하고 해당 주소의 명령어를 실행한다.

LMC가 만약 특정 주소로 이동하라는 명령어를 실행했다고 하면, 핸드카운터의 값을 변경시킴으로써, 다음 사이클 때 LMC가 핸드카운터의 값을 보고 해당 주소의 명령어를 실행하는 것이다.

핸드카운터는 주소값을 가리키는 값이기 때문에, 메일박스에 접근가능한 주소인 00~99 까지의 값만을 가질 수 있다.


Baskets

외부와 데이터를 주고받는 유일한 공간이며, 계산기에 가져올 데이터가 되기 때문에 3자리 값이다.

즉, 입력과 출력을 바구니라고 표현한다.


Instructions

LMC로 하여금 메일박스에 값을 저장하거나, 바구니로부터 값을 읽어오거나, 계산을 수행하게끔 하기 위한 명령어 규칙들이 있다.

아래의 명령어들은 현재 실습중인 LMC가 이해하는 명령어들일 뿐, 다른 기계(LMC)가 해당 명령어를 이해한다는 것은 아니다.

Load

Load 명령어는 1 로 사용되며, 메일박스의 특정 주소에 있는 값을 계산기로 가져온다.

즉 1XX 와 같이 사용되어 메일박스의 XX 주소에 담긴 값을 가져온다는 뜻이며, 예를 들어 LMC가 190 이라는 명령어를 수행할 경우에는 메일박스의 90번지에 위치한 값을 계산기로 Load 한다.

메일박스의 XX 번지에 있는 값은 변경되지 않는다.

Store

Store 의 명령어 코드는 2 이다.

로드와는 반대로 2XX 를 통해 현재 계산기에 올라와있는 값을 XX 주소의 메일박스에다가 넣는다는 뜻이다.

LMC가 291 이라는 명령어를 수행할 때는, 현재 계산기에 있는 값을 메일박스의 91 번지에 Store 하게 된다.

기존에 메일박스에 존재하던 값 위에 덮어써지며, 기존에 계산기에 있던 값은 변경되지 않는다.

Add / Subtract

Add 의 명령어 코드는 3, Subtract(빼기)의 명령어 코드는 4 이다.

명령어는 마찬가지로 3XX, 4XX 처럼 표현됨으로써 메일박스의 XX 주소에 위치한 값과 현재 계산기에 올라와있는 값을 연산한다.

연산된 결과는 계산기에 저장되며, 연산 결과에 대한 몇몇 상태값들이 플래그로 보관되어 다음 연산이 일어날 때 까지 유지된다.

Input / Output

명령어가 반드시 명령어코드 1자리 + 주소 2자리의 형태로 구성되는 것은 아니다.

이것은 사용자의 설계에 맞게 단순히 회로를 그대로 구현하면 되는 것인데, 다음 명령어들은 3자리 값 전체가 명령어가 된다.

501 은 바구니로부터 입력값을 받아온다.

바구니에 가서 3자리 숫자 하나를 가져오고, 해당 숫자를 계산기의 값에 넣는다. 이 때 연산이 일어난 것은 아니기 때문에 상태정보는 바뀌지 않는다.

502 는 현재 계산기에 있는 값은 출력 바구니에 올리며, 계산기의 값은 바뀌지 않는다.

Skip Condition

501 과 502 는 3자리 값 자체가 명령어로 쓰였지만, 앞의 2자리를 명령어로 하고 나머지 1자리를 다른 상태값으로 판단할 수 있다.

51 이라는 명령어 코드는 조건을 분기할 때 사용되며, 나머지 한 자리(0~9)로 특정 조건들을 매칭시킬 수 있지만 현재는 1,2,3 만 사용한다.

조건을 만족시킬 때는 바로 다음에 위치한 명령어를 건너뛴다.

1,2,3 의 상태는 각각 계산기의 상태값이 0인지, 양수인지, 음수인지에 대해 나타내는데, 511 명령어는 계산기의 플래그(상태값)를 확인하고 최근 연산 결과가 0 인 경우 다음 연산을 건너뛴다.

비슷하게 513 명령어는 계산기의 상태를 보고 음수인 경우 다음 연산을 skip 한다.

Jump

Jump는 명령어 코드 6을 사용하며, 뒤에 두 자리 주소값이 따라오는 6XX 의 형태를 갖는다.

LMC로 하여금 핸드카운터의 값을 주소 XX로 변경하게 하여서, Jump 명령어가 실행된 이후 다음 명령어를 실행할 때 LMC가 핸드카운터를 확인해서 해당 주소의 명령어를 실행하게끔 한다.

LMC가 690 명령어를 실행시키면 다음에는 메일박스의 90번지에 있는 값을 명령어로 인식하는 것이다.


예제

  • 501 : 바구니로부터 입력값을 받아오는 3자리 명령어 그 자체로, 입력값 하나를 계산기에 올린다.
  • 299 : 2는 메일박스에 Store하는 명령어로, 99번째 위치에 계산기에 있는 값을 저장한다. 즉 입력받은 값은 메일박스 99번째 공간에 저장된다.
  • 501 : 다시 한번 바구니로부터 값을 읽어 계산기에 새로 올린다.
  • 399 : 첫번째 입력받았던 값이 99번째 위치에 저장되어있기 때문에, 계산기에 올라와있는 두번째 입력값과 저장해뒀던 첫번째 입력값을 더해준다.
  • 502 : 계산기에는 연산의 결과가 새로 올라와있기 때문에, 해당 값을 출력한다.


아래는 고급 언어인 c++ 에서의 코드를 LMC가 이해할 수 있는 기계어로 변환한 예제이다.

while(value == 0)
{
    // Statements
}
// Exit
  • 주소 / 명령어
  • 45 / 190 : 주소 90에 있는 값(value)을 계산기에 올린다.
  • 46 / 491 : 해당 값과 주소 91에 있는 값(0)을 뺀다. 연산이 일어났으므로 해당 값에 대한 상태도 같이 갱신된다.
  • 47 / 511 : 최근 연산 결과의 상태값을 보고, 그 값이 0 과 같을 경우는 다음 명령어를 스킵한다.
  • 48 / 660 : 만약 상태값이 0 이 아니었다면, 핸드카운터의 값을 60으로 바꿔서 LMC가 다음에 주소 60의 명령어를 실행하게 한다.
  • 49 / XXX : 상태값이 0 이었다면 이 위치의 명령어들(Statements)을 실행하고, 핸드카운터는 1씩 계속 증가하며 주소 59까지 도달한다.
  • 59 / 645 : 핸드카운터를 45로 바꿔서 다시 맨 위의 명령어부터 반복한다. (while문)
  • 60 / XXX : 0 이었을 경우에는 위의 반복문에 해당하는 명령을 실행하지 않으므로, 반복문을 종료하고 현재 위치에 도달하여 나머지 명령어들을 수행한다(Exit)