의 퍼즐 게임 시리즈 | ||
{{{#!folding [ 펼치기 / 접기 ] | SpaceChem | Infinifactory |
TIS-100 | SHENZHEN I/O | |
Opus Magnum | EXAPUNKS | |
MOLEK-SYNTEZ | Last Call BBS | }}} |
<colbgcolor=#1b232d><colcolor=#fff> SHENZHEN I/O | |
개발 | Zachtronics |
유통 | Zachtronics |
플랫폼 | Microsoft Windows | macOS | Linux |
ESD | Steam | GOG.com | itch.io |
출시 | 2016년 11월 18일 |
장르 | 프로그래밍 퍼즐 게임 |
한국어 지원 | 미지원 |
웹사이트 | 공식 홈페이지 |
[clearfix]
1. 개요
2016년 Zachtronics에서 개발한 퍼즐 게임. 간소화된 문법의 어셈블리어를 사용하는 MCU와 칩들을 이용해 요구사항에 맞춰 작동하는 전자회로기판을 만들어내는 프로그래밍 퍼즐 게임이다. 같은 제작사에서 개발한 비슷한 구성의 게임 TIS-100의 정신적인 후속작이다.
Xbox 버전 출시 당일 Xbox Game Pass에 게임이 등록되었다. 발매 첫날부터 PC에서 플레이가 가능하다.
2. 상세
2.1. 스토리
주인공[1]은 물건을 만드는 것을 좋아했기에 공학자가 되었지만 곧 현실에 부딪힌다. 자신의 방식은 더 이상 업계에서는 사용되지 않는 구식 방법이라는 것. 그렇게 자신의 꿈을 이룰 수 있는 어딘가로 가야 한다는 생각에 중국으로 이민을 떠난다. 주인공이 도착한 곳은 중국 남부의 선전, 그곳에서 선전용등과기유한회사(深圳龙腾科技有限公司 / Shenzhen Longteng Electronics Co., Ltd.)에 취업해 임베디드 시스템 엔지니어로써 전자 회로를 설계하게 된다.3. 매뉴얼
이전작이었던 TIS-100은 한번 매뉴얼을 숙지하고 나면 특별히 다시는 볼 일이 없었지만, SHENZHEN I/O는 플레이어가 사용하려는 부품의 제한이 없는 덕에 해당 설계에서 사용되는 부품의 데이터시트, 해당 설계의 요구사항 충족을 위한 추가자료(Supplemental Data) 등을 직접 확인하며 게임을 진행하여야 하므로 매뉴얼의 활용 빈도가 매우 높아졌다. 덕분에 매뉴얼 내용도 TIS-100에 3배 많은 47페이지다.개발자는 매뉴얼을 직접 프린트하여 목차와 함께 바인더 노트에 꽂아 플레이하는 것을 권장한다. 스팀 커뮤니티 페이지를 보면 실제로 서류철을 해 모니터 앞에 두고 플레이하는 이들의 사진들을 볼 수 있다.[2][3]
매뉴얼 파일은 스팀 폴더 안의 SHENZHEN I/O 폴더 안의 Content 폴더에서 확인 가능하다.
3.1. 명령어
편의상 명령어의 피연산자는 매뉴얼과 유사하게 다음 기호로 표기합니다.- R: 모든 레지스터
- I: 정수
- P: 핀 레지스터 (입출력)
- L: 라벨
레지스터
- pin: 부품의 단자에 해당하는 x0 또는 p0 등을 말하며, 정의상 이것 또한 레지스터이다. 이것을 통해 부품의 단자로 입출력을 한다. 아래 서술 중 명령어 인자에 'R'이 있다면. 이것을 포함한 모든 레지스터를, 'P'가 대신 있다면 이 레지스터만을 인자로 사용할 수 있다.
- acc: 누산기(accumulator). 범용 레지스터로 다용도로 사용할 수 있다. 산술 명령어는 결과값이 이 레지스터에 자동으로 저장된다.
- dat: MC6000 부품에 존재하는 범용 레지스터중 하나로 역시 다용도로 사용할 수 있다.
- null: 휴지통 또는 UNIX 시스템의 /dev/null 에 해당한다. 이 레지스터로 보내진 값은 모두 사라지며, 이 레지스터에서 값을 읽으면 전부 0으로 읽혀진다.
기본 명령어
- nop: 아무것도 하지 않는다.
- mov ''R/I R'': 특정 레지스터에 저장된 정수 또는 프로그램상의 상수 값(반드시 -999 ~ 999 사이)을 다른 레지스터로 이동한다. 값을 받아들인 핀이 아닌 다른 핀 레지스터나 MCU 내부 레지스터(ACC, DAT)가 대상이 될 수 있다. 가령, 'mov p0 p1'은 p0으로 들어온 어떠한 값을 p1로 이동시킨다. 'mov 100 acc'는 값 100을 내부 레지스터인 acc로 이동한다.
- jmp ''L'': 해당 라벨로 흐름을 건너띈다. 이때, jmp 명령어를 쓴 해당 MCU 안에는 반드시 라벨이 정의되어 있어야 한다.
- (원하는 라벨 이름): : 라벨을 정의한다. 원하는 라벨 이름을 적고 바로 콜론을 붙인다. 라벨 뒤에 명령어를 입력할 수 있으며, 때문에 라벨 이름이 길 수록 명령어를 적을 수 있는 공간은 좁아진다.
- slp ''R/I'': 해당 시간동안 작동을 정지한다.
- slx ''P'': 해당 핀에서 입력이 들어올 때 까지 해당 MCU를 정지시킨다.
- gen ''P X Y'': mov 100 P, slp X, mov 0 P, slp Y 의 축약 명령어이다. 이 명령어를 사용하여 라인 수는 줄일 수 있지만 전력 소모는 동일하게 유지된다.
산술 명령어
산술 명령어의 연산 결과값은 항상 acc 레지스터에 저장되며 acc가 아닌 다른 레지스터로는 결과값을 직접 저장할 수 없다.
* add ''R/I'': R/I에 해당하는 값을 acc에 저장된 값과 덧셈한다.
* sub ''R/I'': R/I에 해당하는 값을 acc에 저장된 값과 뺄셈한다.
* mul ''R/I'': R/I에 해당하는 값을 acc에 저장된 값과 곱셈한다.
* not: acc 레지스터의 값이 0이면 100으로, 0이 아니면 0으로 대체한다.
* dgt ''R/I: acc 레지스터에 저장된 값의 특정 자릿수(10^^n^^, n'' = 0, 1, 2)의 값을 acc에 저장한다. 가령 acc에 234가 저장되어있다면 'dgt 1' 실행 후의 acc 값은 3이다.
* dst ''R/I R/I: acc 레지스터에 저장된 값의 특정 자릿수(10^^n^^, n'' = 0, 1, 2)의 값을 입력한 값과 교체한다. 가령 acc가 678이면 'dst 2 4' 실행 후 acc 값은 478이 된다.
* add ''R/I'': R/I에 해당하는 값을 acc에 저장된 값과 덧셈한다.
* sub ''R/I'': R/I에 해당하는 값을 acc에 저장된 값과 뺄셈한다.
* mul ''R/I'': R/I에 해당하는 값을 acc에 저장된 값과 곱셈한다.
* not: acc 레지스터의 값이 0이면 100으로, 0이 아니면 0으로 대체한다.
* dgt ''R/I: acc 레지스터에 저장된 값의 특정 자릿수(10^^n^^, n'' = 0, 1, 2)의 값을 acc에 저장한다. 가령 acc에 234가 저장되어있다면 'dgt 1' 실행 후의 acc 값은 3이다.
* dst ''R/I R/I: acc 레지스터에 저장된 값의 특정 자릿수(10^^n^^, n'' = 0, 1, 2)의 값을 입력한 값과 교체한다. 가령 acc가 678이면 'dst 2 4' 실행 후 acc 값은 478이 된다.
테스트 명령어
분기 명령어로 두 값을 비교하여 그 비교 결과가 참이면 + 분기 명령어들을, 아니라면 - 분기 명령어들을 실행한다. 이때 가장 마지막으로 실행한 테스트 명령어의 분기 결과는 유지된다. 이를테면, 참인 결과의 테스트 명령어를 가장 위에서 실행하였다면 그 뒤로 + 분기 명령어를 연달아 쓰거나 분기 심볼이 붙지 않은 명령어들과 섞어 사용해도 정상 작동된다.
* + 또는 -: 분기 명령어를 지정하는 심볼. '+ add 1' 과 같이 명령어 앞에 + 혹은 - 를 붙이는 것으로 분기 명령어를 지정할 수 있다.
* teq ''R/I R/I'': 두 값이 같은지 비교한다. 같다면 +, 아니면 - 분기 명령어를 실행한다.
* tgt ''R/I R/I'': 첫 번째 값이 더 큰지(초과 >) 비교한다. 첫 번째 R/I 값을 A, 두 번째 R/I값을 B라 할때 A > B 이면 +, A <= B 이면 - 분기 명령어를 실행한다.
* tlt ''R/I R/I'': 첫 번째 값이 더 작은지(미만 <) 비교한다. A < B 이면 +, A >= B 이면 - 분기 명령어를 실행한다.
* tcp ''R/I R/I'': 두 값의 대소를 비교한다. A > B 이면 +, A < B 이면 - 분기 명령어를 실행하며, 같다면 그 어느 분기 명령어도 실행하지 않는다.
* + 또는 -: 분기 명령어를 지정하는 심볼. '+ add 1' 과 같이 명령어 앞에 + 혹은 - 를 붙이는 것으로 분기 명령어를 지정할 수 있다.
* teq ''R/I R/I'': 두 값이 같은지 비교한다. 같다면 +, 아니면 - 분기 명령어를 실행한다.
* tgt ''R/I R/I'': 첫 번째 값이 더 큰지(초과 >) 비교한다. 첫 번째 R/I 값을 A, 두 번째 R/I값을 B라 할때 A > B 이면 +, A <= B 이면 - 분기 명령어를 실행한다.
* tlt ''R/I R/I'': 첫 번째 값이 더 작은지(미만 <) 비교한다. A < B 이면 +, A >= B 이면 - 분기 명령어를 실행한다.
* tcp ''R/I R/I'': 두 값의 대소를 비교한다. A > B 이면 +, A < B 이면 - 분기 명령어를 실행하며, 같다면 그 어느 분기 명령어도 실행하지 않는다.
기타
- #: 주석을 달 때 사용한다.
- @: 명령어 앞에 붙이면 한 번만 실행되고 이후에는 무시된다.
- 콜론( : ): 점프 명령어의 라벨을 선언할 때 사용한다. 라벨 선언의 자세한 용법은 기본 명령어를 참조.
이 외에 매뉴얼에 기재되지 않은 명령어와 심볼이 존재하며, 진행 도중 이메일을 통해 알 수 있다.
3.2. 부품
부품들에서 사용하는 단자 인터페이스로 Simple I/O와 XBus가 있다. Simple I/O 인터페이스는 언제든지 읽고 쓰기가 가능하면서 한 번 출력을 지정하였다면 직접 출력값을 변경하지 않는 한 시간에 관계없이 출력이 유지된다. 이에 반해 XBus 인터페이스는 동기식 프로토콜로 지정한 출력이 유지되지 않으며, 읽거나 쓰려면 반드시 쓰거나 읽으려는 다른 단자가 있어야 된다. 만약 입출력 타이밍이 맞지 않는다면 먼저 쓰거나 읽으려던 MCU가 대기상태에 들어가게 된다. 때문에 서로 쓰거나 읽으려하면 데드락 상태에 빠지는 것은 당연지사. 물론 이 두 인터페이스의 단자는 서로 호환되지 않으므로 같은 배선으로 연결하면 경고가 뜬다.쉽게 정리하면 Simple I/O 인터페이스는 아날로그 신호 배선, XBus 인터페이스는 데이터 버스 배선으로 볼 수 있다.
MC4000
|| 분류 || 마이크로컨트롤러 ||
가격 | 3¥ |
크기 | 3 × 2 |
특징 | 9줄의 프로그램 메모리 1개의 범용 레지스터(acc) 2개의 XBus 핀 2개의 Simple I/O 핀 |
||<-3> 핀 배치 ||
[clearfix]x0 | p1 | |
p0 | x1 |
변종으로 4개의 모든 단자가 XBus 인터페이스인 MC4000X가 존재한다. 이외의 모든 사양은 MC4000와 동일.
MC6000
|| 분류 || 마이크로컨트롤러 ||
가격 | 5¥ |
크기 | 3 × 3 |
특징 | 14줄의 프로그램 메모리 2개의 범용 레지스터(acc, dat) 4개의 XBus 핀 2개의 Simple I/O 핀 |
||<-3> 핀 배치 ||
[clearfix]x0 | p1 | |
x1 | x3 | |
p0 | x2 |
DX300
|| 분류 || 디지털 입출력 확장기 ||
가격 | 1¥ |
크기 | 2 × 3 |
||<-3> 핀 배치 ||
[clearfix](XBus) | p2 | |
(XBus) | p1 | |
(XBus) | p0 |
각각 3개의 Simple I/O와 XBus 단자를 가진 부품으로, 입력하는 정수값의 자릿수를 이용해 1개의 XBus 단자로 3개의 Simple I/O 입출력을 동시에 제어할 수 있다. 자릿수는 Simple I/O 단자의 핀 번호 pn 의 n에 따라 10n 으로 대응되는 자리가 정해지며 DX300의 XBus 단자로 읽거나 쓰는 것으로 각각 읽기 모드와 쓰기 모드로 동작한다.
- 읽기 모드: Simple I/O 단자로 입력이 있다면 각각의 핀에 대응되는 자릿수가 1이 되어 XBus로 값이 읽힌다. 단, 읽기시 Simple I/O 단자의 입력이 50 이상이어야 1로 읽히며, 읽는 시점에 이전에 쓰기모드 작동으로 출력중인 단자가 있었다면 모두 0인 상태로 초기화된다.
- 쓰기 모드: XBus 단자로 키고자 하는 Simple I/O 핀의 자릿수를 0이 아닌 수로 만들어 입력해주면 해당되는 Simple I/O 핀이 켜지게 된다. Simple I/O 인터페이스이므로 일단 출력이 켜지면 그 출력이 유지되나, 0 또는 100의 출력만을 출력할 수 있다.
100P-14
|| 분류 || Random-Access Memory (RAM) ||
가격 | 2¥ |
크기 | 3 × 2 |
특징 | 14개의 저장공간 자동으로 증가하는 2개의 독립된 메모리 포인터 |
||<-3> 핀 배치 ||
[clearfix]a0 | d1 | |
d0 | a1 |
14개의 저장공간이 존재하는 RAM으로 부품의 좌우에 있는 어드레스(a) 단자와 데이터(d) 단자를 통해 접근할 수 있다. 데이터 단자를 통해 값을 읽거나 쓰며, 이때 자동으로 메모리 포인터가 증가하여 다음으로 넘어간다. 메모리 포인터는 어드레스 단자를 통해 제어하며, 읽거나 쓰는 것으로 현재 가리키고있는 메모리의 주소값을 얻어오거나 변경할 수 있다. 좌측의 메모리 포인터는 0번 어드레스와 데이터 단자로, 우측은 1번으로 제어한다. 포인터는 2개이지만 저장공간은 서로 공유한다.
변종으로 33개의 저장공간을 가진 100P-33이 존재한다.
200P-14
|| 분류 || Read-Only Memory (ROM) ||
가격 | 2¥ |
크기 | 3 × 2 |
특징 | 14개의 저장공간 자동으로 증가하는 2개의 독립된 메모리 포인터 |
||<-3> 핀 배치 ||
[clearfix]a0 | d1 | |
d0 | a1 |
14개의 저장공간이 존재하는 ROM. 초기에 값을 직접 입력하여 초기화시켜야하며, 읽기만 가능하다는 점 외에는 100P-14(RAM)와 동일하다.
변종으로 33개의 저장공간을 가진 200P-33이 존재한다.
LC70G04, LC70G08, LC70G32, LC70G86
|| 분류 || Logic Gate ||
가격 | 1¥ |
크기 | 2 × 1 또는 2 × 2 |
||<-3> 핀 배치 ||
A | A' |
||<-3> 핀 배치 ||
[clearfix]A | Y | |
B | Y' |
논리 게이트. NOT, AND, OR, XOR로 총 4개가 있으며, 입력된 신호를 논리연산하고 이를 출력한다. 모든 단자의 인터페이스는 Simple I/O 이며, 입력 신호의 세기가 50 이상이어야 1로 인식한다. 출력은 0 또는 100이다. 특이하게 AND, OR, XOR 게이트의 경우 반전 출력도 기본으로 제공된다.
진리표는 다음과 같다.
A | B | LC70G04 (NOT) | LC70G08 (AND) | LC70G32 (OR) | LC70G86 (XOR) |
0 | 0 | 1 | 0 | 0 | 0 |
0 | 1 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 0 | 1 | 1 |
1 | 1 | 0 | 1 | 1 | 0 |