컴퓨터를 만듭시다. 어때요~ 참 쉽죠? (19)

나빌레라의 이미지

#19. 다시 코드..

멀리 떨어져 사는 두 친구가 서로 통신을 위해 알파벳을 이진수로 표현하기 위한 약속을 정하고 서로 이진수로 해석 가능한 디지털 신호를 주고 받으며 대화를 한다. 이진수로 해석 가능한 디지털 신호의 예로 나는 스위치와 전등을 이야기했었다. 그리고 특정 숫자와 문자를 대응해 놓은 체계를 코드라고 이야기했고, 대표적으로 아스키 코드를 예로 들었다. 디지털을 이용해 사람과 사람이 대화하기 위해서 코드는 꼭 필요하다.

그렇다면 디지털 회로 자체와 사람이 대화하기 위해서는 무엇이 필요할까? 역시 코드가 필요하다. 오늘은 컴퓨터라는 디지털 회로 덩어리로 된 기계와 사람이 대화하기 위한 코드에 대한 이야기를 할까한다.

우리가 만들고 있는 회로에 컨트롤 유닛을 만들어 붙였다. 덕분에 컨트롤 유닛에 특정 이진수 데이터를 보내 회로를 제어할 수 있게 되었다. 여기서 눈여겨 볼 부분은 회로를 제어하는 이진수 데이터는 특정 동작 별로 유일하게 존재한다는 점이다. 현 시점에서 우리가 만들고 있는 기계가 할 수 있는 동작과 그 동작을 실행시키기 위한 이진수 데이터의 대응은 아래와 같다.

더하기 : 0x00
빼기 : 0x01
곱하기 : 0x02
나누기 : 0x03
레지스터 A로 읽어오기 : 0x0C
레지스터 B로 읽어오기 : 0x14
레지스터 A의 값을 램에 저장 : 0x40
레지스터 B의 값을 램에 저장 : 0x80

동작을 실행시키기 위한 이진수 데이터를 기계어라고 부른다고 했다. 기계어 하나 당 동작은 하나고, 같은 동작에 대해 오직 한 개의 기계어만 존재한다. 이 전제 조건을 만족하기에 기계어와 동작을 표시하는 문자에 대해 코드를 만들 수 있다.

더하기 – ADD : 0x00
빼기 – SUB : 0x01
곱하기 – MUL : 0x02
나누기 – DIV : 0x03
레지스터 A로 읽어오기 – LDA : 0x0C
레지스터 B로 읽어오기 – LDB : 0x14
레지스터 A의 값을 램에 저장 – STA : 0x40
레지스터 B의 값을 램에 저장 – STB : 0x80

더하기, 빼기, 곱하기, 나누기는 영어 명칭을 줄여서 만들었다. 램에서 데이터를 읽어서 레지스터에 넣는 작업을 보통 로드(Load)라고 부른다. 그래서 레지스터 A로 값을 읽어오는 작업은 Load to A라고 쓸 수 있으며 이를 줄여서 LDA로 명명했고, 레지스터 B로 데이터를 읽어오는 작업 역시 마찬가지 규칙을 따르게 했다. 그리고 레지스터에서 램으로 데이터를 저장하는 작업을 스토어(Store)라고 부른다. 레지스터 A의 데이터를 램에 넣는 작업은 Store from A라고 쓸 수 있고 이를 줄여서 STA라고 명명했다. STB도 마찬가지 규칙으로 이름을 정했다.

아스키 코드처럼 기계어를 간단한 문자 몇개로 표현 가능한 코드가 만들어졌다. 레지스터 A와 B에 데이터를 가져와서 이를 더한 다음 다시 램에 저장하는 동작을 표현하기 위해

00001100
00010100
00000000
01000000

이렇게 쓰거나

0x0C
0x14
0x00
0x40

이렇게 쓰는 것 보다

LDA
LDB
ADD
STA

이렇게 쓰면 인간이 읽거나 쓰기 훨씬 편하다.

LDA, LDB 같은 코드는 그에 대응되는 기계어가 있다. 이 기계어는 CPU의 컨트롤 유닛에 입력되어 기계를 동작시킨다. 기계를 동작시키는 코드라는 뜻으로 Operation Code라고 부른다. 줄여서 오피코드, opcode라고 한다. 나도 opcode라고 쓰겠다.

이 이야기에서 내가 만들고 있는 cpu의 원시적인 명령어와 데이터 버스 구분 덕에 우리는 opcode에 인자를 같이 줄 수 있다.

16비트의 데이터가 SRAM에서 나와 8비트씩 각각 컨트롤 유닛과 ALU로 가기 때문에 명령어와 동시에 데이터를 줄 수 있다. 그래서 “레지스터 A에 0x50을 넣는다.”라는 동작을 아래 처럼 표현할 수 있다.

LDA 0x50

SRAM에서는 16비트로 0x0C50이 출력되었고, 0x0C는 컨트롤 유닛으로 0x50은 DEMUX로 전달되어 레지스터 A에 저장되었다.

LDA 0x50이라는 표현을 ‘레지스터 A에 0x50을 넣는다’가 아닌 ‘메모리 주소 0x50번지의 데이터를 레지스터 A에 넣는다’라는 표현으로 인식하게 할 수도 있을 것이다. 그리고 앞서 이야기에서 로드(Load)동작은 램에서 레지스터로 데이터를 이동시키는 동작이라고 정의했으므로 메모리 주소 0x50으로 동작하게 하는 것이 더 이치에 맞을 것같다. 그것이 가능하려면 약간의 회로 수정이 필요하다.

메모리의 Address 핀에는 원래 PC 레지스터의 값이 항상 들어가고 있었는데, 새로 opcode의 인자를 주소로 받아야할 필요가 생겼다. 그래서 메모리의 Address 핀 앞에 PC 레지스터의 값과 DEMUX에서 넘어온 값을 구분하는 MUX를 하나 더 만들었다.

이 새로 생긴 MUX의 SEL 핀은 SRAM 밑의 4 출력 DEMUX의 SEL0과 SEL1이 각각 0, 0일때만 1이 입력된다. 나머지 경우에는 전부 0이어야 한다. 무언가 생각나지 않는가? XOR 게이트의 진리표와 입출력이 같다. 간단히 컨트롤 유닛에서 SRAM 밑의 DEMUX를 제어하는 핀 출력에 XOR 게이트를 달아서 SRAM의 Address 입력 핀 옆에 있는 MUX의 SEL로 연결해 주면 된다.

회로를 이렇게 고쳐서 8비트로 표현 가능한 주소인 0x00부터 0xFF 번지 안에 있는 16비트 값을 언제든지 원하는 때에 골라서 가져올 수 있게 되었다. 마찬가지로 0x00에서 0xFF 번지 사이의 메모리 아무 곳에나 값을 저장 할 수도 있게되었다. 항상 PC가 가리키고 있는 위치에서만 값을 읽어오고 저장할 수 있는 한계에서 벗어났다. 지금까지 램을 순차 접근했다면 드디어 이제서야 램에 랜덤 접근이 가능하게 된 것이다.

이미 대단한 발전을 했지만 아직 부족하다. 내가 달아 놓은 SRAM은 64KB 크기의 램이다. 주소 영역이 0x0000에서 0xFFFF까지 무려 16바이트다. 방금전에 회로를 수정해서 램에 랜덤 접근이 가능하게 되었지만 그 랜덤 접근 가능한 영역이란 것이 8비트 한계가 있어 램의 전체 크기에 반 밖에 사용하지 못한다. 뭔가 아쉽다. 좀더 넓은 영역에 접근하고 싶다.

그 전에 한 가지 이야기하고 싶은 것이 있다. 캐시 이론 같은것 설명할 때 자주 나오는 이야기 중에 데이터 접근의 지역성이라는 말이 있다. 쉽게 말해서 프로그램이 동작할 때 주로 접근하는 데이터는 현재 실행중인 메모리 위치의 주변에 위치한다는 말이다. 즉 현재 PC가 가리키고 있는 주소 근처에 접근해서 데이터를 읽거나 쓰는 경우가 아주 많다는 의미가 된다.

그렇다면 아까 회로 처럼 랜덤 접근 가능한 램의 주소 영역을 0x00에서 0xFF 번지 사이로 제한을 둔다면 데이터 접근의 지역성을 이용해서 무언가를 할 수 있는 여지가 사라진다. 읽고 쓰고하는 데이터는 항상 램의 상위 256바이트 안에만 두어야 한다라는 어이없는 제약이 생겨버리고 만다. 나는 이 제약을 없애고 싶다. 하지만 opcode에서 직접 지정 가능한 주소 영역의 크기는 8비트를 넘길 수 없다. 어떻게 해야 할까?

PC 레지스터 근처의 회로를 잘 보자. 반복해서 2씩 PC에 더하기만 하는 가산기가 있다. 더하는 수 2는 가산기의 CLK로부터 받는다. 더하는 수를 opcode의 인자로부터 받게 한다면 어떤 효과가 생길까? 현재 PC를 기준으로 앞으로 256바이트 영역까지의 주소를 지정할 수 있을 것이다. 문제는 가산기라 더하기 밖에 할 줄 모르기 때문에 PC의 뒤쪽 주소로는 접근이 안된다. 간단하게 가산기를 ALU로 바꿔버리자. 그렇다면 현재 PC를 기준으로 앞 뒤로 127바이트씩 접근이 가능하다.

PC 뒤쪽의 주소로 접근하려면 PC에서 빼기 연산을 해야 하기 때문에 2의 보수 표기법 기준으로 8비트로 표기 가능한 숫자는 -128에서 127까지다. 접근 가능한 주소 영역은 그대로 256바이트지만 PC 기준으로 앞 뒤로 접근이 가능하기 때문에 주소 번지는 제약이 없다.

즉, PC가 0x3F20번지를 가리키고 있다면 위 그림처럼 회로를 수정했을 경우 0x3F12, 0x3F14, 0x3F24, 0x3F28 등 앞 뒤로 127바이트씩 접근 가능하다. 기존에는 0x3F12라는 주소 번지 자체를 입력으로 줄 수가 없었다.

여기까지 했지만 그래도 아직 부족하다. PC를 이용해서 메모리에 접근하기 때문에 접근하고자 하는 데이터의 메모리 주소가 PC 근방에 있다면 위에서 이야기한 방법으로 접근이 가능하지만 만약에 접근하고자 하는 주소가 PC에서 앞 뒤로 127바이트보다 멀리 떨어진 곳에 위치한다면 어떻게 해야 하는가? 어쩔 수 없이 16바이트의 메모리 주소를 직접 지정해 줘야한다. 하지만 현재 회로 구조상 16바이트의 메모리 주소는 직접 지정할 수 없다. SRAM의 출력으로 나오는 16비트중 상위 8비트는 opcode로 인식하기 때문이다.

램에서 출력되는 16비트를 온전히 주소 지정에 모두 사용할 수 있어야 한다. 램에서 데이터가 출력될 때는 데이터건 명령어건 구분없이 언제나 Address 핀에 들어간 주소에서 2바이트, 즉 16비트가 출력될 뿐이다. 출력핀 아래에서 이 데이터를 사용하는 회로가 적절히 잘 구성되면 16비트를 그대로 데이터로 쓸 수도 있고, 명령어로 쓸 수도 있을 것이다. 어떤 경우에는 16비트를 8비트/8비트로 나눠서 opcode 부분과 인자 부분으로 나눠서 사용하고, 어떤 경우에는 16비트 전체를 데이터로 사용해야 한다. 16비트를 데이터로 사용해야 할 경우는 바로 직전에 opcode에서 인자로 주소가 지정되는 단계를 거쳐야 한다. 그러므로 직전에 opcode로 사용되었다는 것을 기억할 필요가 있다.

기억이라는 문제를 해결하는데있어 나는 언제나 D 플립플랍을 사용해왔다. 이번에도 마찬가지로 1비트 D 플립플랍을 컨트롤 유닛 안에 넣어두고 직전 opcode에서 메모리의 Address 핀의 입력을 결정하는 MUX의 SEL선의 값을 기억시켰다. 그림에는 표현되지 않았지만 컨트롤 유닛 안에는 1비트 D 플립플랍이 있어서 바로 위의 DEMUX를 제어한다고 생각하자.

회로가 복잡해지면서 화살표 굵기와 버스 크기에 일관성이 없어졌다. 화살표에 써 넣은 숫자가 버스의 폭이므로 그림 자체에 얽매이지 말자. 나도 맘에 안든다. 컨트롤 유닛 위에 있는 DEMUX의 선택에 따라 opcode 쪽으로 오는 8비트는 컨트롤 유닛으로 들어갈 수도 있고 다시 레지스터 쪽으로 가서 하위 8비트와 합쳐져 16비트를 만들 수도 있다.

이렇게 만들어진 16비트는 레지스터의 입력으로 갈 수도 있고 직접 SRAM의 Address 핀으로 입력되어 주소 지정에 쓰일 수도 있다. 이렇게 되면 16비트를 주소 지정에 사용할 수 있으므로 주소 영역의 크기에 제한 받지 않고 전체 64KB의 전체 SRAM 영역에 모두 접근할 수 있다. 다만 주소 영역을 16비트로 사용하기 위해서는 메모리에서 데이터를 한 번에 읽어오는 것이 아니라 데이터를 읽어와 opcode를 해석하고 opcode의 인자를 다시 메모리에 넣어서 값을 가져 온다음 그 값을 또 메모리의 주소로 넣어서 값을 읽어와야 한다. 즉 속도가 느려진다.

새로운 소자가 cpu에 포함되어 제어해야 할 비트가 하나 더 늘었으므로 당연히 LDA, LDB, STA, STB의 opcode에 대응되는 기계어는 바뀐다. 그런데 LDA와 LDB는 레지스터만 다를 뿐 동일한 동작이다. STA와 STB도 레지스터만 다를 뿐 동일한 동작이다. 기계어에 대응되는 코드를 표기하는 방법은 정해진 것이 아니라 만드는 사람이 하고 싶은 대로 정의하는 것이다. 그래서 나는 앞으로 이 opcode를 동작에 따라 묶어서 표현하려 한다. 그래서

LDA 0x77
LDB 0x88
STA 0x99
STB 0xAA

이 네 개의 명령은

LDR A, 0x77
LDR B, 0x88
STR A, 0x99
STR B, 0xAA

이렇게 바꿔서 표기하겠다. 물론 LDA 0x77과 LDR A, 0x77에 대응되는 기계어는 같다. 단지 사람이 좀더 체계적으로 읽기 위해서 코드를 바꾼것 뿐이다.

그렇다면 맨 처음에 의도했던 동작. 즉, 인자로 넘어오는 값 자체를 그대로 레지스터에 넣는 명령도 만들 수 있지 않을까. 인자를 메모리 Address로 주지 않고 그대로 레지스터로 주면 될것이다. 물론 이렇게 만들면 레지스터에 넣을 수 있는 값은 8비트로 한정되지만 8비트 이하의 작은 수만 다룬다면 충분히 필요할 것이다. 이런 동작은 보통 Move라고 부른다. 그래서 MOV라는 명령을 만들었다. LDR이나 STR과 마찬가지로

MOV 레지스터이름, 값

의 형식을 가진다. 0x77을 A레지스터에 넣고 싶다면 MOV A, 0x77이 된다. 앞에서도 말했지만 MOV 명령은 내가 현재 이 이야기에서 만들고 있는 cpu에서 8비트까지만 허용되므로 인자로 전달 할 수 있는 최대 값이 0xFF다.

레지스터로 데이터를 직접 집어 넣을 수도 있고, 64KB의 메모리 영역 전체 중 아무곳이나 접근해서 16비트의 데이터를 가지고 올 수도 있게 되었다. 그렇다면 같은 원리로 64KB의 메모리 영역 전체 중 아무곳이나 접근해서 opcode도 가지고 올 수 있지 않을까? 당연히 가능하다. 또 약간의 회로만 수정해 주면 된다.

데이터를 가지고 오는 메모리 주소는 직접 지정하면 되지만 opcode를 가지고 오는 메모리 주소는 반드시 PC 레지스터를 통해 전달되어야 한다. 그러므로 PC 레지스터의 값을 임의로 변경할 수만 있다면 메모리 아무곳에나 있는 opcode를 가지고 올 수 있을 것이다.

메모리 아무곳이나 접근해서 opcode를 가지고 오려면 데이터를 가지고 올 때와 마찬가지로 16비트 값을 전달할 방법을 찾아야 한다. 데이터를 가지고 올 때 사용하는 연결선을 연장해서 사용하는 방법이 있고, 레지스터 A 혹은 레지스터 B의 출력을 PC 레지스터의 입력으로 연결하는 방법도 있을 것이다. 어떤 방법을 사용해도 상관없다. 나는 회로의 복잡도를 최소화하기 위해 레지스터 A의 출력값에서 연결하는 방법을 그렸다. 하지만 내가 방금 이야기한 모든 방법이 다 가능하게 회로가 구성되어 있다고 치자. (그리기 귀찮아서 그런다.)

이렇게 PC를 옮겨서 opcode를 가지고 오고 나면 그 다음에 PC는 어떻게 될까? 또 다시 PC 값을 수정해 주지 않는한 PC는 여전히 묵묵히 새로 바뀐 값을 시작으로 해서 또 2바이트 씩 증가한다. 언제나 그 자리에서부터 전진할 뿐이다. PC는 우직하다.

이 처럼 늘 한 발작씩 전진만 하는 PC를 뒤로 혹은 앞으로 멀리 뛰게하는 동작을 브렌치 동작이라고 한다. 점프라고 표현하기도 한다. 보통 B 명령을 많이 사용한다.

B 점프할 주소
B 레지스터 이름

크게 두 가지 종류로 명령을 줄 수 있을 것이다. 실제로는 주소를 표현하기 위한 방법에 따라 좀더 세분화 된다. 주소 표기 방법에 대해서는 다음에 이야기해 주도록 하겠다.

그 외에도 ALU에 회로를 추가해서 계산에 관련된 opcode를 몇개 더 추가할 수 있을 것이다. 잘 알려진 회로를 이 이야기에서 보여주고 일일이 설명하는 것은 이제 큰 의미가 없다. 처음 가산기를 만들 때의 원리와 크게 다르지 않기 때문이다. 다만 어떤 opcode가 가능한지만 이야기 하겠다.

먼저 비교 연산이 가능하다. 레지스터 A와 레지스터 B를 통해 들어온 데이터를 비교해서 어떤 것이 큰지를 알려준다. 사실 방법 자체는 아주 간단하다. A에서 B를 뺀다. 그 결과가 양수면 A가 큰것이고 0이면 같은 수, 음수면 B가 큰 것이다. 비교 동작이기 때문에 Compare의 약자로 CMP라는 명령으로 많이 사용한다.

CMP 레지스터 이름, 레지스터 이름

이렇게 많이 사용한다. 다른 식으로라면

CMP 레지스터 이름, 값

이렇게 쓸 수도 있을 것이다. 내가 만들고 있는 머신에서 opcode는 전체 16비트고 명령어가 8비트다. 레지스터 이름을 판별하는 것까지 명령어에 포함되므로 값으로 들어갈 수 있는 숫자의 범위는 8비트로 한정된다는 것을 잊지 말기 바란다. 물론 opcode가 16비트를 넘고 명령어의 크기를 어떻게 설계하느냐에 따라 값에 들어갈 수 있는 숫자의 범위는 달라질 것이다.

비트 시프트 연산도 가능하다. 레지스터 A로 들어온 값을 레지스터 B로 들어온 숫자 만큼 왼쪽이나 오른쪽으로 비트를 이동시키는 것이다. 왼쪽으로 비트를 이동시키는 명령을 SHL, 오른쪽으로 비트를 이동시키는 명령을 SHR이라고 이름 붙이겠다. Shift Left, Shift Right를 적당히 줄인 이름이다.

가장 기본적인 연산인 논리 연산도 가능하다. 사실 논리 회로에서 사칙 연산보다 더 기본적인 연산이 논리 연산이다. 사칙 연산은 논리 게이트를 이리저리 엮어서 결과 값이 나오도록 만들어야 하지만 논리 연산은 그냥 논리 게이트만 연결해서 나오는 값 자체가 그 결과기 때문이다. 회로 구성 자체도 간단하다. 레지스터 A와 B의 출력 값을 각각 논리 게이트에 연결해서 그 값을 보면 된다. 논리 게이트는 AND, NAND, OR, NOR를 따로 두고 있지만 이 논리 연산들을 모두 별개의 opcode로 만드는 것은 오히려 낭비기 때문에 AND, OR, XOR, NOT 이렇게 네 개의 opcode만 만들도록 하자.

지금까지 나온 opcode를 모두 정리해 보자. 적절히 opcode의 목적에 따라 분류해봤다.

산술 명령어
ADD, SUB, MUL, DIV, MOD

이동 명령어
MOV

비교 명령어
CMP

분기 명령어
B

메모리 명령어
LDR, STR

논리 명령어
AND, OR, XOR, NOT

비트 시프트 명령어
SHL, SHR

무려 16개나 된다. 이렇게 코드로 표현하는 명령어를 조합해서 컴퓨터에게 원하는 동작을 하도록 일을 시킬 수 있으며 그 조합된 코드를 어셈블리어라고 부른다. 그리고 어셈블리어를 이진수로 바꿔주는 녀석을 어셈블러라고 부른다.

후아~ 오늘 이야기는 아주 길었다. 사실 분량 조절을 못한 내 잘못이 크다. 하지만 어떻게 쪼개기도 좀 애매한 내용이라 그냥 길게 한 번에 이야기를 풀었다. 오늘 이야기의 핵심은 컨트롤 유닛에 입력으로 들어가는 이진수에 인간이 알아 보기 쉬운 어떤 문자를 대응시켜 코드를 만들 수 있다는 것이다. 그리고 어떤 코드를 만들 수 있는지를 계속 회로를 수정하면서 보여줬다. 이 코드가 바로 어셈블리어라는 사실을 나중에 밝혔다. 이 이야기를 읽는 분들중 어떤 분들은 어셈블리어가 뭔지 아는 분도 계실것이고 그 중에는 어셈블리 프로그래밍을 할 줄 아는 분도 계실것이다. 우리가 책에서 배워서 그냥 프로그래밍으로 공부했던 어셈블리어가 실제로는 저렇게 회로의 구성과 아주 밀접하게 관련이 있다는 사실을 이 이야기에서 처음 알게된 분도 계실 것이다. 어셈블리어는 기계어와 일대일로 대응되어 있다고 그냥 맹목적으로 외웠을 것이다. 실제로 회로와 어셈블리어를 대응하면서 이야기를 읽어보니 실감이 가지 않는가!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.