- ShitBC 어셈블리 파일의 권장 확장자는
.sba
입니다.
; 주석은 코드에 설명을 덧붙이기 위해 사용합니다.
; 주석은 세미콜론(;)으로 시작되며, 세미콜론을 포함해 그 뒤에 있는 모든 문자는 무시됩니다.
nop ; 세미콜론의 위치는 상관이 없습니다.
ShitBC 어셈블리는 프로시저와 함수로 구성됩니다. 프로시저는 반환 값이 없는 명령어의 집합을, 함수는 반환 값이 있는 명령어의 집합을 나타냅니다.
proc/func 이름:
...
프로시저를 만들 때는 proc 키워드를, 함수를 만들 때는 func 키워드를 사용합니다. 이름은 중복될 수 없으며, 언더바(_
)를 제외한 특수문자를 사용할 수 없습니다. 숫자는 사용할 수 있으나 이름의 첫 번째 문자가 숫자일 수는 없습니다. 또, 예약어는 이름으로 사용할 수 없는데, 예약어 목록은 다음과 같습니다.
import
as
struct
func
proc
int
long
double
pointer
gcpointer
proc entrypoint:
...
이름이 entrypoint인 프로시저를 특별히 진입점이라고 하는데, ShitVM에 의해 프로그램이 실행되면 진입점이 가장 먼저 실행됩니다. 모든 ShitBC 어셈블리는 진입점을 가져야 합니다.
proc/func 이름(a, b, c, ...):
...
프로시저와 함수는 매개 변수를 가질 수도 있습니다. 이름 뒤에 소괄호 쌍을 붙인 뒤, 소괄호 안에서 컴마(,
)로 매개 변수를 구분합니다. 단, 진입점은 매개 변수를 가질 수 없습니다.
proc/func 이름:
...
foo:
...
bar:
...
프로시저와 함수 안에는 레이블을 만들 수 있습니다. 이름 뒤에 콜론(:
)을 붙이기만 하면 됩니다. 레이블의 이름은 중복될 수 없으나, 다른 프로시저/함수에 있는 레이블의 이름을 사용할 수는 있습니다. 레이블은 각각의 프로시저/함수마다 별도로 분리되어 만들어지므로 다른 프로시저/함수에 있는 레이블로 이동할 수는 없습니다.
<니모닉> [피연산자]
모든 명령어는 기본적으로 니모닉을 갖습니다. 니모닉은 해당 명령어가 어떤 동작을 수행해야 하는지 나타냅니다. 니모닉에 따라 피연산자가 필요한 경우도 있습니다. 이럴 때는 니모닉과 띄어쓰기 등으로 구분을 한 뒤 적습니다.
push 1024 ; Error!
proc entrypoint:
push 1024 ; OK
...
또, 모든 명령어는 반드시 어떤 프로시저나 함수에 소속되어 있어야 합니다. 프로시저나 함수에 소속되어 있지 않은 명령어가 있다면 ShitBC 어셈블러는 오류를 발생시키고, ShitVM 바이트 파일을 생성하지 않습니다.
ShitVM은 스택 기반 가상머신으로, 연산에 필요한 피연산자들을 스택에 저장한 뒤 연산을 하고, 그 결과물을 다시 스택에 저장하는 구조로 되어 있습니다.
push 1024
push 1024i
push 1024l
push 1024.0
push 123,456,789
push
니모닉을 사용하면 스택에 연산에 필요한 상수들을 저장할 수 있습니다. 이 니모닉은 피연산자가 필요한데, 스택에 저장할 상수가 피연산자가 됩니다. 이 명령어가 실행되면 피연산자가 스택의 가장 위에 저장됩니다.
ShitVM에서 다루는 값들은 자료형이 정해져 있는데, 자료형은 어떠한 값이 어떤 종류인지, 어떤 특성을 갖는지, 크기는 어느 정도 되는지 등을 나타냅니다. ShitVM에서 기본적으로 제공하는 자료형의 종류와 설명은 다음과 같습니다.
이름 | 크기 | 설명 | 표현 범위 |
---|---|---|---|
int |
4바이트 | 가장 기본적인 정수형입니다. | 0~4,294,967,295 -2,147,483,648~2,147,483,647 |
long |
8바이트 | 크기가 큰 정수형입니다. | 0~18,446,744,073,709,551,615 -9,223,372,036,854,775,808~9,223,372,036,854,775,807 |
double |
8바이트 | 가장 기본적인 실수형입니다. | 유효숫자 15자리 |
pointer |
OS에 따라 다름 | 메모리 주소를 나타내는 포인터입니다. | OS에 따라 다름 |
gcpointer |
OS에 따라 다름 | 메모리 주소를 나타내는 포인터입니다. | OS에 따라 다름 |
ShitVM에서 정수형은 기본적으로 부호 없이 처리됩니다. 따라서 부호의 여부에 영향을 받는 일부 동작들은 부호를 구분하는 니모닉과 구분하지 않는 니모닉으로 나뉘어 있습니다. 또, 정수형은 2의 보수법, 실수형은 IEEE754에 따라 구현됩니다.
기본적으로 push
니모닉을 사용하면 피연산자의 크기에 따라 자동으로 자료형이 결정됩니다. 소수점이 있으면 double
, int
로 표현할 수 있다면 int
, 그 외에 경우에는 long
으로 결정됩니다. 그러나 정수 상수의 자료형을 직접 지정하고 싶을 경우에는 i
또는 l
접미사를 사용합니다. i
접미사를 사용하면 해당 상수의 자료형은 int
가 되며, l
접미사를 사용하면 long
이 됩니다. 단, 포인터 상수는 push
니모닉을 사용해 만들 수 없습니다.
포인터는 운영체제에 따라 크기가 달라지는데, 일반적으로 32비트 운영체제에서는 4바이트, 64비트 운영체제에서는 8바이트이며, 표현 범위는 크기에 따라 int
, long
자료형의 표현 범위와 같습니다. pointer
와 gcpointer
는 둘 다 본질적으로는 포인터지만, gcpointer
는 약간의 제약이 가해지는 포인터입니다. 이에 대해서는 뒤에서 자세히 설명합니다.
pop
스택에 저장된 값을 삭제하고 싶다면 pop
니모닉을 사용하면 됩니다. 자료형과 관계없이 스택의 가장 위에 있는 값을 1개 삭제합니다. 이 니모닉은 피연산자가 필요하지 않습니다.
push 0
store i ; i = 0
push 1
store j ; j = 1
load j
store i ; i = 1
ShitVM에서는 지역 변수도 사용할 수 있습니다. 지역 변수는 스택에 저장되며, store
니모닉을 사용하면 지역 변수를 새롭게 만들 수 있습니다. 이 니모닉은 피연산자가 필요한데, 피연산자를 이름으로 하는 지역 변수를 만듭니다. 지역 변수의 값은 스택의 가장 위에 있는 값이 됩니다. 지역 변수의 값을 스택으로 가져오고자 한다면 load
니모닉을 사용하면 됩니다. 마찬가지로 피연산자는 지역 변수의 이름이 됩니다.
또, store
니모닉은 지역 변수를 만드는 역할 뿐만이 아니라, 이미 존재하는 지역 변수에 새로운 값을 저장하는 동작도 수행합니다. 즉, 피연산자가 존재하지 않는 지역 변수라면 새 지역 변수를 만들고, 존재하는 지역 변수라면 해당 지역 변수에 값을 새롭게 저장합니다.
지역 변수를 삭제하고 싶다면 pop
니모닉을 사용하면 됩니다. 스택의 가장 위에 있는 값이 지역 변수라면 해당 지역 변수를 삭제하는 동작도 함께 수행합니다.
lea i
lea j
push
니모닉은 포인터 상수를 만들 수 없는데, 따라서 포인터 상수를 만들기 위해서는 특별한 니모닉을 사용해야 합니다. lea
니모닉은 포인터를 만드는 니모닉 중 하나로, 피연산자가 나타내는 지역 변수의 메모리 주소를 스택에 저장합니다.
null
gcnull
null
, gcnull
니모닉을 사용하면 널포인터를 스택에 저장할 수 있습니다. null
니모닉은 pointer
자료형, gcnull
니모닉은 gcpointer
자료형의 널포인터를 저장합니다.
lea i
lea j
tload
tstore
포인터가 가리키는 곳에 있는 값에 접근하려면 tload
, tstore
니모닉을 사용합니다. tload
니모닉을 사용하면 포인터가 가리키고 있는 메모리 주소에 있는 지역 변수의 값을 스택에 푸시하고, 포인터는 스택에서 삭제합니다. tstore
니모닉을 사용하면 포인터가 가리키고 있는 메모리 주소에 있는 지역 변수에 스택에 저장된 값을 저장하고, 저장한 값과 포인터는 스택에서 삭제합니다. 이때, 스택의 가장 위에 있는 값이 저장할 값, 그 아래에 있는 값이 포인터가 되므로 순서에 유의해야 합니다.
swap
copy
위와 같이 스택에 있는 값들의 순서가 중요한 명령어를 위해 스택에 있는 값의 순서를 바꾸려면 swap
니모닉을 사용하면 됩니다. swap
니모닉은 스택의 가장 위에 있는 값과 그 바로 아래에 있는 값의 순서를 바꿉니다. 단, 두 값의 자료형은 반드시 같아야 합니다. copy
니모닉을 사용해 스택의 가장 위에 있는 값을 복사해 그 위에 덧붙일 수도 있습니다.
스택에 있는 값의 자료형을 변경할 수도 있는데, 이를 형 변환이라고 합니다. 형 변환 니모닉의 종류와 설명은 다음과 같습니다.
니모닉 | 시작 자료형 | 변환 자료형 |
---|---|---|
toi |
gcpointer 외의 아무 자료형 |
int |
tol |
gcpointer 외의 아무 자료형 |
long |
tod |
gcpointer 외의 아무 자료형 |
double |
top |
gcpointer 외의 아무 자료형 |
pointer |
형 변환 니모닉들은 피연산자가 필요하지 않으며, 스택의 가장 위에 있는 값의 자료형을 바꾸게 됩니다.
top
니모닉은 포인터를 만드는 니모닉 중 하나인데, 신중히 사용하는 것이 좋습니다. 정수나 실수를 그대로 메모리 주소로 바꾸어 포인터로 만들기 때문에 유효하지 않은 메모리 주소를 포인터로 형 변환할 경우 프로그램이 정상적으로 동작하지 않을 수도 있습니다.
스택에 있는 값끼리 산술 연산을 하려면 다음 니모닉을 사용합니다.
니모닉 | 설명 | 스택의 최소 크기 |
---|---|---|
add |
덧셈 | 2 |
sub |
뺄셈 | 2 |
mul |
부호 없는 곱셈 | 2 |
imul |
부호 있는 곱셈 | 2 |
div |
부호 없는 나눗셈 | 2 |
idiv |
부호 있는 나눗셈 | 2 |
mod |
부호 없는 나머지 연산 | 2 |
imod |
부호 있는 모듈로 연산 | 2 |
neg |
부호 변경 | 1 |
inc |
1 증가 | 1 |
dec |
1 감소 | 1 |
스택에 있는 값끼리 비트 연산을 하려면 다음 니모닉을 사용합니다.
니모닉 | 설명 | 스택의 최소 크기 |
---|---|---|
and |
논리곱 | 2 |
or |
논리합 | 2 |
xor |
배타적 논리합 | 2 |
not |
비트 반전 | 1 |
shl |
비트 왼쪽으로 시프트 | 2 |
sal |
비트 왼쪽으로 시프트 | 2 |
shr |
부호 없는 비트 오른쪽으로 시프트 | 2 |
sar |
부호 있는 비트 오른쪽으로 시프트 | 2 |
이 니모닉들은 스택에 저장된 값을 피연산자로 하므로 별도의 피연산자를 코드에 명시할 필요는 없습니다. 피연산자의 순서가 중요한 이진 연산은 스택의 가장 위에 있는 값을 오른쪽 피연산자, 그 값의 아래에 있는 값을 왼쪽 피연산자로 해 연산합니다.
연산의 결과는 피연산자가 스택에서 자동으로 삭제된 후 스택에 저장되며, 피연산자의 자료형은 반드시 같아야 합니다.
inc
, dec
니모닉은 피연산자의 자료형이 반드시 포인터여야 하는데, 포인터가 가리키는 곳에 있는 값에 대해 연산합니다. 다른 니모닉의 경우에는 피연산자가 포인터일 수 없습니다. 또, inc
, dec
이 가리키는 곳에 있는 값의 자료형 역시 포인터일 수는 없습니다.
cmp
icmp
스택에 있는 값끼리 비교를 하려면 cmp
또는 icmp
니모닉을 사용합니다. 유추할 수 있듯이 cmp
는 부호가 없는 비교, icmp
는 부호가 있는 비교입니다. 왼쪽 피연산자가 오른쪽 피연산자보다 더 크면 1, 더 작으면 -1, 두 값이 같으면 0이 결과가 되며, 산술/비트 연산과 같이 피연산자는 스택에서 자동으로 삭제된 후 결과가 스택에 저장됩니다. 피연산자의 자료형도 마찬가지로 같아야 합니다. 결과의 자료형은 반드시 int
입니다.
cmp
또는 icmp
니모닉으로 비교한 결과를 바탕으로 원하는 레이블로 이동하려면 조건 분기 니모닉을 사용합니다.
니모닉 | 조건 | 스택의 값 |
---|---|---|
je |
a == b |
0 |
jne |
a != b |
0이 아님 |
ja |
a > b |
1 |
jae |
a >= b |
-1이 아님 |
jb |
a < b |
-1 |
jbe |
a <= b |
1이 아님 |
조건 분기에 성공하면 비교 결과는 스택에서 삭제되며, 비교 결과의 자료형이 반드시 int
일 필요는 없습니다. 조건과 관계없이 분기하려면 jmp
니모닉을 사용하면 됩니다. 이 니모닉은 비교 결과를 필요로 하지 않습니다.
분기 명령어들은 반드시 피연산자가 필요한데, 이동할 레이블의 이름이 피연산자가 됩니다. 레이블은 반드시 해당 명령어가 소속된 프로시저/함수 내에 있는 레이블이어야 합니다.
call <프로시저/함수 이름>
프로시저나 함수를 호출할 때에는 분기 명령어들을 사용하지 않고 call
니모닉을 사용합니다. 분기 명령어와 마찬가지로 피연산자가 필요한데, 호출할 프로시저 또는 함수의 이름을 피연산자로 합니다. 단, 진입점은 호출할 수 없습니다.
push 1
push 0
call foo
호출할 프로시저 또는 함수가 매개 변수를 갖고 있다면 호출하기 전에 매개 변수의 개수에 맞게 인수를 스택에 저장하면 됩니다. 이때 인수들은 스택에 저장한 순서의 역순으로 전달됩니다.
proc/func 이름:
...
ret
프로시저나 함수는 반드시 종료할 때 ret
니모닉을 사용해야 합니다. 피연산자는 필요하지 않으며, 해당 명령어는 스택을 정리하고 호출한 곳으로 다시 돌아가는 동작을 수행합니다.
함수에서 ret
니모닉을 사용하면, 스택의 가장 위에 있는 값을 제외하고 스택을 정리합니다. 즉, 해당 값은 반환값의 역할을 하게됩니다.
진입점에서 ret
니모닉을 사용하면 프로그램이 즉시 종료되지만, ret
니모닉이 없어도 마지막 명령어를 실행한 후에는 프로그램이 자동으로 종료됩니다.
여러 개의 데이터를 하나로 묶어 다루고 싶을 경우 구조체를 사용할 수 있습니다.
struct 구조체이름:
타입이름1 필드이름1
타입이름2 필드이름2
...
구조체를 만들 때는 struct 키워드를 사용합니다. 구조체는 필드를 0개 이상 갖는데, 필드는 구조체 안에 묶인 데이터를 말합니다.
struct A:
B b
struct B:
A a
위와 같이 두 구조체가 서로를 순환 참조하고 있다면 어셈블은 정상적으로 되지만(차후에 수정할 예정입니다.), 생성된 ShitVM 바이트 파일을 실행하면 오류가 발생하며 인터프리팅이 되지 않으니 주의해야 합니다. 두 구조체가 서로를 순환 참조하는 것뿐만이 아니라 3개 이상의 구조체에서 순환이 발생하는 경우도 포함됩니다.
struct Grade:
int korean
int english
int math
proc entrypoint:
push Grade
store grade
lea grade
flea Grade.korean
push 10
tstore
구조체를 스택에 저장하기 위해 push
니모닉을 사용할 수 있습니다. 피연산자로 숫자 대신 구조체의 이름을 적으면 됩니다.
구조체의 필드에 접근하기 위해서는 포인터를 사용해야 하는데, flea
니모닉을 사용하면 구조체의 필드에 대한 포인터를 가져올 수 있습니다. 이렇게 얻어온 포인터를 tload
, tstore
등의 니모닉을 사용해 적절히 값을 불러오거나 저장함으로써 필드에 접근할 수 있습니다.
구조체에 대해서는 형 변환, 산술 연산, 비트 연산, 비교 연산을 할 수 없으니 주의해야 합니다.
스택의 크기는 상대적으로 작고, 함수가 종료되면 저장된 데이터가 사라질 수 있어 크기가 큰 데이터를 저장하거나 오랫동안 데이터를 다뤄야 한다면 힙이라는 또 다른 메모리 공간에 데이터를 저장할 수 있습니다. 데이터를 저장하기 위해 힙에 필요한 만큼의 메모리 영역을 요청하는 것을 동적 할당이라고 합니다.
new int
store intptr
load intptr
push 5
tstore
load intptr
delete
new
니모닉을 사용하면 힙에 관리되지 않는 메모리 영역을 할당받을 수 있습니다. 할당받은 메모리 영역의 주소는 스택에 pointer
자료형으로 저장됩니다. 할당 속도가 빠르지만, 메모리 영역이 필요가 없어지면 반드시 delete
니모닉을 사용해 할당을 해제해야 합니다. 그렇지 않으면 메모리 누수가 발생합니다.
gcnew int
store intptr
load intptr
push 5
tstore
gcnew
니모닉을 사용하면 힙에 관리되는 메모리 영역을 할당받을 수 있는데, 할당 속도가 느린 대신 GC에 의해 관리되기 때문에 메모리 관리를 직접 하지 않아도 됩니다. 할당받은 메모리 영역의 주소는 스택에 gcpointer
자료형으로 저장됩니다.
이렇게 gcpointer
는 GC에 의해 관리되므로 gcnew
니모닉을 사용하지 않고는 만들 수 없습니다. 또, GC에 따라 지역 변수나 구조체의 필드 등에 저장되지 않은 경우에는 사용되지 않는 메모리 영역이라고 판단되어 할당이 해제될 수 있으므로 반드시 gcnew
니모닉을 사용한 뒤 메모리 주소를 즉시 지역 변수 등에 저장하는 것이 좋습니다.
같은 자료형의 변수 여러 개가 필요한 상황이라면 배열을 사용할 수도 있습니다. 배열은 같은 자료형의 데이터를 일렬로 나열한 것입니다.
push 10
apush int[]
apush
니모닉을 사용해 스택에 배열을 추가할 수 있습니다. 배열의 각 데이터를 원소라고 하는데, 위 코드는 int
자료형의 원소 10개를 갖는 배열을 만듭니다.
push 15
anew int[]
delete
push 20
agcnew int[]
anew
, agcnew
니모닉을 사용하면 배열을 동적 할당할 수 있습니다. 할당되는 영역은 각각 new
, gcnew
니모닉의 경우와 같습니다. 할당되는 영역만 다를 뿐 사용법은 apush
니모닉과 같습니다.
push 10
apush int[]
store array
lea array
push 0
alea
push 1
tstore
alea
니모닉을 사용하면 배열의 특정 원소를 가리키는 포인터를 스택에 추가할 수 있습니다. 위 코드의 경우 배열의 가장 첫 번째 원소의 값을 1로 바꿉니다. 배열의 원소의 번호(인덱스)는 0부터 순서대로 할당됨에 유의해야 합니다.
struct Foo:
double[10] bar
구조체의 필드에서도 배열을 사용할 수 있습니다. apush
, anew
, agcnew
니모닉을 사용하면 배열의 원소의 개수를 실행 중 자유롭게 지정할 수 있지만, 구조체에서 사용할 경우에는 정적으로 지정해야 합니다.
구조체에 대해서는 형 변환, 산술 연산, 비트 연산, 비교 연산을 할 수 없으며, 배열의 배열은 만들 수 없으니 주의해야 합니다. 또, store
, tstore
등의 니모닉을 사용해 배열을 복사하는 경우 목적지 배열의 원소 개수와 복사할 배열의 원소 개수가 반드시 같아야 합니다. 배열을 복사하면 배열의 각 원소가 특정 위치로 복사됩니다.
하나의 프로그램은 보통 여러 개의 소스 파일로 구성되며, 다른 개발자가 만든 라이브러리를 가져와 활용하기도 합니다. 떄문에 ShitBC 어셈블리도 다수의 개발자들에게 친숙한 모듈 시스템을 가지고 있습니다.
하나의 ShitBC 어셈블리 파일은 하나의 독립적인 모듈로 인식되며, 모듈은 일반적으로 다른 모듈에 의해 영향을 받지 않습니다.
import "모듈 경로" as 네임스페이스
하지만, 위와 같이 import
구문을 사용하면 현재 어셈블 중인 모듈이 다른 모듈에 의해 영향을 받도록 설정할 수 있습니다. 예를 들어 A 모듈이 B 모듈을 import
한 경우, A가 B를 참조한다
또는 A가 B에 대한 의존성을 갖는다
와 같이 말합니다. import
는 명령어가 아니라 단지 ShitAsm에게 정보를 제공하는 구문임을 참고하세요.
모듈 경로는 가장 간결하게 나타내는 것이 권장되며, 디렉터리의 구분은 항상 슬래시(/
)를 사용합니다. 모듈 경로는 절대 경로이거나 상대 경로일 수 있습니다.
- 슬래시로 시작하는 경로를 절대 경로라고 합니다. 임포트 디렉터리를 기준으로 다른 모듈을 검색합니다. 외부 라이브러리를 참조하고자 할 때 사용합니다. 임포트 디렉터리는 명령줄 옵션
-I
를 이용해 추가할 수 있습니다. - 그 외의 경로를 상대 경로라고 합니다. 현재 모듈이 저장된 디렉터리를 기준으로 다른 모듈을 검색합니다. 같은 프로그램을 구성하는 다른 모듈을 참조하고자 할 때 사용합니다.
import
구문을 통해 다른 모듈을 참조할 경우, 해당 모듈에 있는 구조체와 함수, 프로시저(이하 '요소')를 현재 모듈에서도 사용할 수 있게 됩니다. 하지만, 현재 모듈에 있는 요소와의 충돌을 방지하기 위해, 다른 모듈의 요소들은 전역 네임스페이스가 아닌 import
구문에서 설정한 네임스페이스에 소속됩니다. 네임스페이스란, 요소가 소속된 모듈을 구분하기 위한 일종의 식별자를 말합니다.
; foo.sba
func foo:
push 0
ret
proc entrypoint:
; bar.sba
import "foo.sba" as foo
func foo:
push 1
ret
proc entrypoint:
call foo ; 현재 모듈의 foo 함수가 호출됩니다.
call foo.foo ; foo.sba 모듈의 foo 함수가 호출됩니다.
전역 네임스페이스에 소속되지 않은 요소에 접근하기 위해서는 위와 같이 네임스페이스.요소이름
의 꼴로 접근해야 합니다.
import "foo.sba" as foo
import "foo.sba" as bar ; Error!
import "bar.sba" as foo ; Error!
같은 모듈은 여러 번 참조될 수 없으며, 하나의 네임스페이스 안에 여러 모듈의 요소가 소속될 수 없습니다.
import "foo.sba" as foo
import "bar.sba" as foo.bar ; OK
import "baz.sba" as .foo.bar ; Error!
import "baz.sba" as foo.bar. ; Error!
import "baz.sba" as foo..bar ; Error!
import "baz.sba" as foo.bar.2 ; Error!
import "baz.sba" as foo.bar.int ; Error!
네임스페이스 이름은 여러 개의 식별자로 구성될 수도 있습니다. 단, 이때 식별자 사이는 점(.
)으로 연결되어 있어야 합니다.
foo.bar
네임스페이스의 경우, foo
네임스페이스 하위에 bar
라는 네임스페이스가 있는 것처럼 보일 수 있습니다. 하지만 ShitBC 어셈블리의 네임스페이스에는 계층 구조가 존재하지 않으므로, foo.bar
라는 이름을 가진 독립적인 네임스페이스로 존재하는 것임에 유의해야 합니다.
현재 모듈과 참조한 다른 모듈은 ShitVM에 의해 런타임때 동적으로 연결됩니다. 즉, 예를 들어 foo.sba
와 bar.sba
가 각각 foo.sbf
, bar.sbf
로 어셈블됐고, foo.sba
에서 bar.sba
를 참조한 경우, foo.sbf
를 배포할 때 bar.sbf
도 함께 배포해야 합니다. 또, 현재 ShitAsm는 foo.sba
를 어셈블한다고 해서 bar.sba
가 어셈블되는 것은 아니므로, 각각 어셈블해야 할 필요가 있습니다.
ShitVM에서는 IO, 문자열 등 개발에 도움이 되는 요소들을 제공하고 있습니다. 이를 ShitVM 표준 라이브러리라고 합니다. 이곳에서 표준 라이브러리에 대한 정보를 확인할 수 있습니다.
- 구조체를 활용한 퍼셉트론 학습
- 배열을 활용한 피보나치 수열 계산
- 모듈을 활용한 문자열 결합 연산
더 많은 예제는 이곳에서 확인할 수 있습니다.