COLORREF Set Pixel(hdc, nXPos, nYPos, clrref)
지정한 좌표에 clrref색 하나의 점을 찍는다.

DWORD MoveToEX(hdc, x, y, lpPoint);
BOOL LineTo(hdc,xEnd,yEnd);

LineTo는 CP부터 지정한 좌표까지 선을 긋는다. 그리고 CP를 지정한 좌표로 업데이트한다. 따라서 LineTo를 계속 호출하면 연속된 선을 그을수 있다.
MoveToEx는 CP를 지정한 좌표로 이동시키고 필요한경우 이전 CP의 좌표를 lpPoint에 담는다. 필요하지 않을경우(현재수준에서는 계속해서) NULL값을 주면 된다.

Rectangle(hdc, nLeftRect,nTopRect,nRightRect,nBotomRect);
Ellipse(hdc, nLeftRect,nTopRect,nRightRect,nBottomRect);

Rectangle은 네모를
Ellipse는 동그라미를 그린다.
둘다 왼쪽 위x,y, 오른쪽 아래,x,y좌표 순서대로 입력하면 된다.

SetWindowText(HWND hWnd,LPCWSTR lpString);
윈도우 타이틀바에 표시할때 사용. 뭔가 문자열 테스트할때도 많이 사용한다.
이것또한 wsprintf문을 사용할수 있다.

'Programming > Visual C++' 카테고리의 다른 글

입력1. WM_CHAR  (0) 2011.06.29
출력5. MessageBox & MessageBeep  (0) 2011.06.28
출력3. DrawText  (0) 2011.06.28
출력2. WM_Paint  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
TextOut에서 발전하면서도 디테일한 것을 만질수 있는 함수다.
int DrawText(HDC hDC, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat);

뭐 DC는 알테고 그 다음은 출력할 글자,
그 다음에 있는 nCount는 문자열 안에 있는 만자의 갯수를 나태내는 int형 변수다. 만약 이 것이 -1이면 DrawText는 마지막에 null 값을 가지는 포인터가 있는 것으로 가정하고 문자의 갯수는 자동으로 카운트 한다.
그다음은 Rect의 주소값. 즉, 글자가 들어갈 사각형을 표시해준다.
uFormat은 출력 형식인데,
DT_LEFT 수평 왼쪽정렬
DT_Right 수평 오른쪽 정렬
DT_CENTER 수평 중앙정렬
DT_BOTTOM 사각영역의 바닥에 문자열 출력
DT_VCENTER 사각영역의 수직 중앙에 문자열 출력
DT_WORDBREAK 사각영역의 오른쪽 끝에서 자동 개행
DT_SINGLELINE 한줄로 출력
DT_NOCLIP 사각영역의 경계를 벗어나도 문자열을 자르지않고 그대로 출력
등이 있다.

'Programming > Visual C++' 카테고리의 다른 글

출력5. MessageBox & MessageBeep  (0) 2011.06.28
출력4. 기타  (0) 2011.06.28
출력2. WM_Paint  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
How to Programming by Unicode in windows programming - 3  (1) 2011.06.25

자, 이번엔 Paint에 대해서 알아보도록 하겠다.
dc의 경우엔 사실 Invalidate()가 자동적으로 이루어지지 않았다.(2010, windows7에서는 자동으로 되는듯 싶지만) WM_Paint를 사용하면 Uncover가 자동적으로 이루어진다. 음... 정확하진 않다.
일단 사용방법은.

 PAINTSTRUCT 구조체를 사용하는 방법이다.

자, 자세한 예제를 보면,
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;

switch(iMessage)
{
case WM_DESTROY:PostQuitMessage(0);return 0;
case WM_PAINT :
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 100,100,TEXT("Beautiful Korea"),15);
EndPaint(hWnd,&ps);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam);
}

자, 여기서 새로나온걸 보자면 PAINTSTRUCT 구조체와 BeginPaint, EndPaint다.
PAINTSTRUCT의 안을 들여다보면
typedef struct tagPAINTSTRUCT
{
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL flncUpdate;
BYTE rgbReserved[16];
} PAINTSTRUCT;

MSDN에서의 설명을 그냥 번역해보자면
hdc

A handle to the display DC to be used for painting.
그리기를 위해 사용될 DC를 표시할 핸들.

fErase

Indicates whether the background must be erased. This value is nonzero if the application should erase the background. The application is responsible for erasing the background if a window class is created without a background brush. For more information, see the description of the hbrBackground member of the WNDCLASS structure.
지시한다 배경이 지워져야만 할것인지 아닌지. 만약 이 어플이 배경을 지운다면 이 값은 0이 아니다. 만약 윈도우 클래스가 배경색 프러쉬없이 만들어진다면, 이 어플은 배경을 지울 책임이 있다. 뭔가 더 궁금하면 WNDCLASS구조체의 hbrBackground멤버 설명을 좀더 보면 이해가 될수도 있을껄?

rcPaint

A RECT structure that specifies the upper left and lower right corners of the rectangle in which the painting is requested, in device units relative to the upper-left corner of the client area.
그리기가 요청된 위 왼쪽, 아래, 오른쪽 코너들을 특정짓는 사각형(RECT) 구조. 디바이스 유닛에서는 클라이언트영역의 왼쪽위로 연결된다.

fRestore

Reserved; used internally by the system.
시스템 내부적으로 예약되어 있음. 건들지 마셈.

fIncUpdate

Reserved; used internally by the system.
시스템 내부적으로 예약되어 있음. 건들지 마셈.

rgbReserved

Reserved; used internally by the system.
시스템 내부적으로 예약되어 있음. 건들지 마셈.

 
자, 한마디로 말하자면, 알필요 없는 구조체네. 그저 BeginPaint를 사용하기 위한 전초전이라 생각해도 편할듯 싶다.
 
BeginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint);
자, 그럼 이 코드를 보자면, BeginPaint다. 음.. GetDC, ReleaseDC와 마찬가지로
BeginPaint를 해주면, 반드시 EndPaint를 해줘야 한다.
자, 그리고 넘겨주는것에는 HWND와 LPPAINTSTRUCT. LP는 포인터를 의미한다. 따라서 저기 선언해 놓은 PAINTSTRUCT ps;의 주소값을 넘겨주면 된다.
 





















'Programming > Visual C++' 카테고리의 다른 글

출력4. 기타  (0) 2011.06.28
출력3. DrawText  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
How to Programming by Unicode in windows programming - 3  (1) 2011.06.25
윈도우즈 프로그래밍에서 유니코드로 코딩법 2  (1) 2011.06.25
자, 이제 공부를 더 하기 위해서 음.. 뭔가에 대해서 깊숙히 들어가기 보단, 기록과 기억의 측면에서 포스팅을 해보기로 하자.
 이번에 공부할것은 출력. 몇일 전에 했던 LPCTSTR의 문자열 출력이 아닌 이상에야 뭐 그리 어려울건 없다. 따라서 윈도우즈 툴에서 출력하는 방법과 출력하는 함수들의 원형 및 사용방법을 익히면 될것이다.

시작해보자.

우선, 출력이라는 것에 있어서의 필요한것에 대해 알아보자, 일단 출력이란, 우리가 가장 간단하게 상상할수 있는 console style의 문자열이 있겠다. console style만 보더라도, 흰색글자에 검은색 바탕화면, 맨 왼쪽 위부터 출력이 차례대로 진행된다. 하지만 이거슨 도스시절의 이야기. 윈도우와 도스의 가장큰 차이점중 하나를 꼽으라면 뭐가 있을까? 일단 멀티테스킹이 있겠다. 그러면서 윈도우창이 바뀌고, 윈도우창이 움직일수도 있다.
 자, 그러면 두개의 프로그램을 실행중에 있는데 한나의 프로그램에서 출력하고 있던것은 다른 프로그램이 활성화가 된다면 비활성화가 된 프로그램과 같이 보이지 않아야하고 다시 그 프로그램을 활성화 시킨다면 다시 보여져야 한다.
 그리고 윈도우 창을 움직인다면 그 출력이 따라서 움직여야 할것이다.
 그리고 도스시절부터 있을수 있었던 이야기인, 함수의 원형에 무엇이 들어가야 하는가?에 대한 문제이다. 일단, 글자의 시작위치가 들어갈수 있겠다. 도스에서는 그냥저냥 첫줄부터 했지만, 윈도우에서는 어디서부터라도 글자나 그림의 위치가 실행이 가능하다. 글자의 크기, 폰트, 색깔, 등등등 여러가지가 있다. 음... 하지만 정작 생각을 해보면, 시작위치, 프로그램의 핸들만 알려주면 그냥 디폴트 정해주면 안되? 라고 해서 만든것이 DC(Device Context)다. 이 DC에 기타 여러가지의 기능들을 디폴트로 해놓든가 따로 수정을 해주면 알아서 해줄수 있도록 편하게 만들어 놓았다.

자, 그럼 간단한 코드를 보도록 하겠다.

LRESULT CALLBACK WndProc(HWND, hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;

switch(iMessage)
{
case WM_DESTROY: PostQuitMessage(0);return 0;
case WM_LBUTTONDOWN:
hdc=GetDC(hWnd);
TextOut(hdc,100,100,Text("Beautiful Korea"),15);
ReleaseDC(hWnd,hdc);
return 0;  
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}

자, 여기서 중요한건, HDC와 GetDC, TextOut, ReleaseDC
뭐, 다중요하네 ㅋㅋ

일단 HDC는 DC의 식별번호(Handle)를 담을수 있는 거고, GetDC는 hWnd를 GetDC 함수에 넘겨줘서 리턴값을 hdc로 대입한다. 핸들은 카테고리처럼 프로그램마다 지정되는거니까 :)
자, 그리고 TextOut을 보기전에 오선 ReleaseDC를 보자하면, 이거슨 생성자와 소멸자, 아니, new와 delete의 관계에 가깝다. 반드시 해줘야한다는 것이지.
 자, 그러면 Text Out을 보도록 하자, TextOut의 경우는
 TextOut(HDC hdc, int x, int y, LPCWSTR lpString, int c);
요롷게 된다. 맨처음은, dc를 사용할 핸들값이겠고, x,y는 출력할 xy의 좌표이다. 이 xy좌표의 원점. 즉, 0,0은 메뉴 부분(non-client area)를 제외한 가장 왼쪽 위부터 시작된다.따라서 오른쪽으로 1만큼 아래로 1만큼 움직이려면, 1, -1이 되는것이다.
 그리고 나오는 LPCWSTR은 물론 LPCTSTR도 사용할수 있으며 출력할 값을 나타낸다.
 그리고 그 뒤에오는 int c는 lpString의 최대길이를 나타낸다.
 만약 마지막의 이것을 쓰기 귀찮은 경우에는

void MyTextOut(HDC hdc, int x, int y, LPCTSTR Text)
{TextOut(hdc,x,y,Text,lstrlen(Text));}

이것과 같이 마지막을 lstrlen(Text)를 해줘서 없애고 MyTextOut이란 함수를 만들면 된다.


그리고 아래는 좌표에 따른 정렬방법.

SetTextAlign(HDC hdc, UINT align);

align::
TA_TOP 지정한 좌표가 상단 좌표
TA_BOTTOM  " 하단 좌표
TA_CENTER   " 중앙 좌표
TA_LEFT " 왼쪽 자좌표
TA_RIGHT " 오른쪽 좌표.
TAUPDATECP  지정한 좌표 대신CP(Current Position)를 사용하여 문자열 출력후에 CP를 저장하고 다음에 출력할 경우 그 CP 다음에 출력한다.
TA_NOUPDATECP CP를 사용하지 않고 지정한 좌표를 사용하며 CP를 변경하지 않는다.

보통 CP는 TA_LEFT|TA_TOP 에 Default로 있다가 어느 한 Text아웃이 발생하면 그 Text의 끝에 붙는다.
따라서 만약 이 CP를 옮기고 싶지 않을때는 TA_NOUPDATECP를 해주어야 한다.

그리고 중간에 변수를 넣을 경우에는 wsprintf나 sprintf를 사용해주면 된다.
wsprintf(LPWSTR, LPWSTR,...);
이것인데, 처음에는 LPWSTR은 버퍼.
그다음 LPWSTR은 버퍼에 넣을 서식이고, ... 는 인수값들이다.


How to Programming by Unicode in windows programming - 3

자, 이제 실전이다. 뭐, 다들 책이나 기본적인 윈도우즈 프레임 툴은 어떻게 짜는지 알것이다.

내가 배운것은

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCSTR lpszClass="First";

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;

 WndClass.cbClsExtra = 0;
 WndClass.cbWndExtra = 0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style = CS_HREDRAW|CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);

 while(GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 switch(iMessage)
 {
 case WM_DESTROY : PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}

요코드다. 자, 그렇다면 유니코드를 해놓고 비주얼 스튜디오 2010에서 돌리면 어디가 에러가 나는가?
바로 hWnd=CreateWindow 요 줄에 있는 두번째 배개변수 lpszClass가 문제다. 왜냐하면 윈도우즈 함수에서는 분명히 유니코드를 이용하겠다고 했는데 이 lpszClass는 아직 유니코드가 아닌갑다. 그래서 나는 이 lpszClass의 유니코드명이 뭔지 한참이나 찾았으나 인터넷에는 도저히 나와있지 않다. 왜냐하면!! lpszClass는 내가 코드안에서 생성해준 변수명이잖아-_ -;;;;

자, 5번줄을 보면
LPCSTR lpszClass="First";
요게 있었어..
복습을 해보자.
LPCSTR은 Const하고 long pointer하고 str인것이다. 유니코드의 흔적조차 없다.
그러면 어떻게 해야할까?

그거야 바로 LPC와 STR사이에
W나 T를 넣어주면 된다. 다른 코드들과의 이식성을 위해 일부러 연습을 위해서라도 LPCTSTR을 사용해주기로 한다.
자, 이제 해결?!

5번줄만 바꿔서
LPCTSTR lpszClass="First";
자, 디버깅 해보자.

으악! 에러다. 에러다. 에러다. ㅠㅜㅠ 아 도대체 왜!!!
자, 읽어보자면
error C2440: '초기화 중' : 'const char [6]'에서 'LPCTSTR'(으)로 변환할 수 없습니다.
라고 뜬다.
즉슨, 그럼 "First" 가 5자리+NULL의 문자열 상수일테고 왼쪽은 LPCTSTR이다.
음.. LPCTSTR은 틀릴리가 없고, 그럼 const char [6]이 틀렸다는 건데.
일단 내가 짠것은 유니코드니 wchar_t const * != const char[6] 라는 소리겠지.
틀린말은 아니다, "First"는 분명한 문자열 상수고 그것을 다른 자료형에 넣을순 없는거니까.
자, 그러면 형변환이다!

자, 그럼 유니코드 안에서 LPCTSTR == wchar_t const * 이니까
LPCTSTR lpszClass == (wchar_t const *)"First";

라고 고 치고 해보자.... 와우!!! 디버깅 에러 없고, 컴파일 제대로 되..... 었지만 출력물이 이상하다. 무슨 이상한 한자하고 잡스러운게 많이 나온다. ㅠㅠㅠㅠㅠㅠ 앙데 ㅠㅠ

음... 이것의 이유는 찾아본 결과 일단, 강제 형변환에 있다. 일반적인 char 형과 유니코드의 공간 할당 크기가 다르고 이것을 강제적으로 형변환을 할때 그 할당공간 크기 차이의 빈자리를 쓰레기값이 들어가 쓰레기 글자가 나오게 되는 것이다.

 그래서 자연스레 형변환을 해주는 접두어가 있었으니 그거슨 바로 L이다.
 라이토는 아니고, 음... 정확히 찾아보진 못했지만. L이다.

 사용방법은. 그냥 일반 접두어처럼 사용하면 된다.
 
LPCTSTR lpszClass=L"First";

요롷게 하면 디버깅도 되고 컴파일도 되고 실행도 된다. 제대로 출력도 된다.
하지만 이렇게 하면 사실 LPCTSTR을 쓴 의미가 없어진다. 왜냐하면 이 L은 유니코드형으로의 형변환이다. 선택적 형변환을 하기 위해선 TEXT()함수를 사용해야한다. 이 TEXT한수를 사용할 경우엔 이것도 T와 마찬가지로 알아서 유니코드면 유니코드, 일반 ANSI면 일반 char로 알아서 변환한다.

다시 말해

LPCTSTR lpszClass=TEXT("First");

가 최종 답안이라는거지 :)
아.. 여기까지 달려오시느라 참 수고 많으셨고(다 읽었을리는 없겠지만)
최종 코드를 공개하겠다.(거창하진 않지만)

아래 코드로 컴파일 하여 실행한다면 유니코드든 멀티바이트든 에러없이 실행 된다:)

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass=TEXT("First");

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;

 WndClass.cbClsExtra = 0;
 WndClass.cbWndExtra = 0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
 WndClass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance = hInstance;
 WndClass.lpfnWndProc=WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style = CS_HREDRAW|CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd = CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);
 
 //MessageBox(hWnd,L"하이",TEXT("Test"),NULL);

 while(GetMessage(&Message,NULL,0,0))
 {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }

 return (int)Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
 switch(iMessage)
 {
 case WM_DESTROY : PostQuitMessage(0);
  return 0;
 }
 return (DefWindowProc(hWnd,iMessage,wParam,lParam));
}


How to Programming by Unicode in windows programming - 2

자, 저번시간에 멀티바이트와 유니코드의 차이를 알았아보았다. 사실 엄격히 말하면 유니코드 조차 멀티바이트긴 하지만 일반적으로 ASCII CODE를 멀티바이트, 유니코드를 유니코드라 한다.

그렇다면 아까의 그 비주얼 스튜디오 2010에서 유니코드로 윈도우즈 프로그래밍을 할수 있는 두번째 방법을 소개 하겠다. 개념은 그리 어렵지 않다.

 그저 멀티바이트 즉, ASCII CODE가 아닌  UNICODE를 사용하면된다.

하지만 방법은 막 어렵지는 않지만 간단하지만은 않다. 왜냐하면, 이 C++이 만들어 질땐 ASCII CODE를 전제로 만들어져 기본적인 명령어는 대부분 ASCII CODE를 기반으로 만들어 졌기때문에 그것을 요리해서 UNICODE도 사용할 수 있도록 변경해야 한다. 문자열이 들어가는 곳이라면 대부분의 코드를 손을 보아야한다. 따라서 ASCII CODE, 즉 일반적인 C에 적응되어 있는 분들은 문자열이 나온다면 긴장을 해야 할것이다.
물론, C++에서 막 문법을 떄고 오신분들이라면 우선 여기에 먼저 적응해지면 되긴 하겠지만 그래도 윈도우즈 프로그래밍에서는 char 가 직접적으로는 절대 안쓰인다고 생각해야한다.

 자, 그럼 어떤걸 쓰느냐?
char는 TCHAR로 사용한다. TCHAR의 정의를 보면
#ifdef UNICODE
typedef wchar_t TCHAR;
#else
typedef char TCHAR;
#endif

만약, UNICODE를 사용한다면, wchar_t형의 TCHAR를 사용하고 유니코드가 아니면 그냥 char를 사용할께용. 이라는 뜻이다. 그렇다면 wchar_t는 무엇인가? 음... int는 정수형을 담기위한 자료 저장 범위를 지정하기 위한 "자료형"이다. 그렇다면 UNICODE를 위한 저장 크기를 제한한 자료형정도가 되겠다. 정확히 말하면 저거슨 unsigned short로 정의 되어있다.(for UTF-16)
 사실 일반 멀티바이트라면 char를 사용하고 유니코드라면 wchar_t의 자료형을 사용하면 된다. 하지만 xp이후 유니코드가 대세가 되어가고 있고 마소또한 유니코드를 권장하고 있다.

또 몇개의 자료형을 더 찾아보자면

TCHAR : char
LPSTR : char *
LPCSTR : const char*
LPWSTR : wchar_t*
LPCWSTR : const wchar_t*
LPCTSTR : const wchar_t * || const char*

이렇게 있겠는데 음.. 헝가리언 명명법이라고는 알고 있을란지... 아무튼 이름 붙이는 방법이다. 그 이름 붙이는 방법중 약어를 쓰는경우가 있는데 STR은 딱봐도 string이라는거 알겠고 다른 약어들을 대충 찾아본다면

W : Wide char (2바이트를 의미. 대충 유니코드따라잡기용 ASCII Code라 생각하면 됨)
T : unicode || ASCII
C : constant
LP : long pointer
STR : string자료형

그리고 이 유니코드를 지원하는 문자열함수도 따로 있으니 아래의 함수들을 이용하면 된다.
strlen lstrlen
strcpy lstrcpy
strcat lstrcat
strcmp lstrcmp
sprintf wsprintf


음.. 이제 코드만 짜보면 되겠구나 :)
자, 그렇다면 코드는... 다음 이시간에 계속!! 으흐흐흐흐
How to coding by UNICODE in Visual Studio 2010. 

이번학기에 비주얼 프로그래밍 수업을 들으면서..(사실은 뭐 그의 MFC수업이었지만)
기초적인 Windows API에 대해 '맛'을 봤었다.
사실, 이번학기 시작전에 WINDOWS API책을 살짝 훑어보고 시작한지라 공부를 하지 않고도 그럭저럭 학점이 잘 나오리라 생각한다.

하지만, 수업을 듣던중 가장 난감했던 것은. 바로 유니코드.
난 수업시간에 내 노트북을 갖고 다니면서 프로그래밍을 했다.
하지만 계속해서 유니코드때문에 막혔다. 첫번째 시간은 코드를 그대로 쳤으나 코드가 돌아가지 않았다.
아예 해결방법을 찾지못해 하루정도 내내 해결방안을 찾았던것 같다.

일단 해결방법 1
Alt + F7을 누르거나 메뉴에서 (프로젝트)- > (속성) 으로 들어가
왼쪽 프레임에서 (구성 속성) -> (일반)을 클릭하고
오른쪽 프레임에서 아래 문자집합 에 있는 (유니코드 문자 집합 사용)을 (멀티바이트 문자 집합 사용)으로 해준다면 왠만큼 프로그래밈이 이 전 비주얼 스튜디오 에서 코딩하는것 처럼 비슷하게 돌아간다.

하지만, 이건 약간 룰 위반이다.
우선 멀티바이트와 유니코드를 이해해야 한다.
멀티바이트를 이해하기 위해선 아스키 코드에 대해서도 알아야한다.
ASCII CODE란 만약 이걸 보는 이들은 아마도 알고 있겠지만,
컴퓨터는 문자를 인식하지 못한다. 1과 0으로 밖에 인식을 하지 못하기 때문에 인간은 그 문자를 일단 10진수 숫자와 16진수 숫자와 매치시켜 놓는다. 예를들어, 숫자가 73이면 16진수로는 0x4C고 문자는 L 이 된다. 
 이 모든걸 결정한 곳은 어느 말을 쓰는 나라 일까? 바로 미쿡. 따라서 모든 ASCII CODE는 영어로만 이루어져 있다. 사실 ASCII 라는 말은 American Standard Code for Information Interchange의 약자다. 미국 표준이란 소리지. 걔네들이 한국어나 따른 나라 언어 알게 뭐가 있었겠어?
 아무튼 그렇게 10진수와 16진수로 옮겨놓으면 2진수로 만드는건 뭐 식은죽을 그냥 앉아서 천천히 TV보면서 먹는 그런 느낌이 되어버렸지. 그리고
 부호를 포함한 1 Byte즉 8 bit의 2진수로 나타낼수 있는 수는 0000000 부터 1111111 까지. 즉 0부터 127까지다.
알파벳은 총 몇개? 어이어이 세지 말고 ㅋㅋㅋ 26개다. A~Z까지 26개고 음... 그래 대소문자 다 합쳐봐야 52개 에다가... 음.. 그래 숫자도 넣어두면 편하겠찌 그래서 숫자도 넣고. 음.. 또 뭐 없을까? 그래 따옴표하고 구두점하고 수학기호하고 뭐 그런것들좀 집어넣어서 채우다 채우다 좀 남아서 비트들을 활용하여 (그래픽 문자)도 모자라 (제어문자)까지 꽉꽉 채워 넣었다 .제어문자란 음.. DEL키라던가 Space라던가 ESC라던가 그런거 있잖아. 아 그런 공간 좀 남으면 한글이나 넣어줄것이지! 라고 말하고 싶지만 사실 인접 국가. 미국에서 자주사용되는 스폐인어 고유 글자조차 부호없는 1byte(ASCII CODE Extended)에 겨우 포함이 될 정도다.

 자, 이제 아스키코드란걸 조금은 이해를 하겠나? 음... ASCII CODE가 궁금하다면 http://ko.wikipedia.org/wiki/Ascii
위키피디아를 찾아보아도 좋음 :)


자, 이제 ASCII CODE란 걸 왠만큼 알았으니 멀티바이트로 넘어가 보자. 그럼 멀티 바이트란 무엇인가?
바로 이 ASCII CODE는 잘 해봐야 1Byte 인데 왜 싱글 바이트가 아닌 멀티 바이트인가?
 즉슨, 위 에서 언급했던대로 Signed 1byte안에는 only english다. 뭐, 숫자나 뭐그런거 말고 다른 나라의 언어는 없다는 이야기이다. 그리고 unsigned는 그외 프랑스, 스페인, 따깔, 더치어등 잉글리시와 비슷한 언어들만 확장형으로 넣어두고 나머지는 표그리기나 그림그리용으로 채워 넣었다.
 그럼 이제 비주얼스튜디오에서 멀티바이트로 하고 hello world 코딩을 해보자. 아, 영어말고 printf("헬로 월드"); 요롷게 당당하게 한국어로 해보자. 출력은 어떻게 나오는가? 한국어로 나오지 않는가?! 와우. 분명 ASCII CODE에는 영어 밖에 없다. 미국 표준이므로. 하지만 어떻게 한글이 나오지? 물론 개중에는 한글이 지원하지 않아 이상한 상형문자들이 나오는 경우가 있다. 그 이유는 나중에 점차점차 알아가도록 하고. 어찌되었든 한국어가 나온다. 그 이유가 멀티바이트에 있다. 이 문자를 인식할때 싱글바이트, 즉 1Byte로만 인식을 하면 영어만 인식을 한다. 왜냐? 1Byte안에 영어, 한국어, 중국어, 일본어 같은 것들을 모두 넣다보면 그 옛날 꼬물 컴퓨터를 쓸때 만들어진 표준으로써는 그당시 현실에 너무 안맞기 때문이다. 그렇게 몇년을 쓰다가 그걸 또 2Byte로 넘기기엔 표준이 송두리채 바뀌기가 쉽지 않았다. 따라서 하나의 대안을 제시한다.
1Byte를 두개 해서 문자를 표현하자고!!!
영어를 할땐 1Byte만 하면 되고 만약 영어이외의 한국어나 일어, 한자를 표현할때는 1Byte 여러개를 붙여 문자를 표현하기로 한다. 그 것이 ISO-2200(ISO-2200-KR, ISO-2200-JP, ISO-2200-CN) 에 정의 되어 있는 멀티바이트 문자열이다. 뭐 조금 자세히 들여다 본다면 그 1byte의 21-7E를 2개 이어붙여 94*94=8836개의 글자를 표현할수 있다. 좀더 자세히들어가 유닉스쪽 얘기를 한다면 유닉스에서는 그 표준이 약간다른데(EUC:Extended Unix Code로 EUC-KR, EUC-JP, EUC-CN으로 표기한다.) 두개의 바이트를 이어붙이는게 A1-FE 까지라는것을 제외하면 표현할수 있는 글자수도 같아 뭐 비슷비슷하게 사용할수 있다.

자, 이제 멀티바이트에 대해 약간이나마 감이 좀 잡히나? 간단히 말해 ASCII CODE 1Byte를 두개이상 붙여만들었기떄문에 멀티바이트라고 한다.


그렇다면 유니코드란 무엇인가?
ASCII는 영어일경우는 1Byte, 다른나라 언어는 +a를 하게된다. 그렇다면 어떤 일이 벌어지는가?
다른 나라 언어에 +a를 한다고 해도, 만약 한국어로 된 문장을 일본어로 해석하게 되면 어떻게 되겠는가? 이런 연유로 가끔씩 페이지를 돌아다닐때 홈페이지 전체가 상형문자로 덮혀있는 페이지를 발견하게 되는것이다. 즉, 글자가 깨진다고 하는것이다.

 자, 이것을 위해 유니코드가 태어났다. 이 American놈들의 지저분한 Standard를 꺠고 Universial한 Code를 만들어 냈다. 그것이 바로 유니코드다. 이 유니코드는 두개를 이어 붙이는게 아니라 처음부터 2Byte를 할당하여 파일 하나에 컴퓨터로 표현하는 세상 모든 문자를 담았다. 하지만 아직은 인터넷계의 주류를 담고 있는 영어에 뭔가 불리하지 않겠는가? 쓰지도 않을 일본어때문에 굳이 용량을 키우면 뭔가 좀 거시기 하지 않겠는가?라는 생각이 들어 이 유니코드도 3가지로 나눠져 있다. 그거슨 UTF-8, UTF-16, UTF-32 로 나눈다.
UTF-8이 그 ASCII CODE와 같이 8비트만을 이용한 글자. UTF-16은 16비트를 사용하고 UTF-32는 32비트를 사용하여 글자를 나타낸다. 하지만 이것은 말했듯이 ASCII CODE처럼 두개의 1 Byte를 이어붙이는 것이 아니라 순수하게 1byte면 1byte, 2byte면 2 byte를 사용한다. 그래서 인터넷보면 UTF-8 인터넷 주소만 이용 뭐 이런게 있는데 즉, 한글주소나 뭐그런거 다 빼고 영어만 쓰겠음. 뭐 그런거다.

자, 이렇게 ASCII CODE와 UNICODE는 그 근본적으로 차이가 있다. 따라서 개발자는 죽어나간다. 표준이 여러개라...

블로깅이 너무 길어 졌으므로 다음 내용은 다음 포스팅에 계~~ 속. (아마도 실질적인 첫, Windows Programming에서 기본적인 Windows Frame의 UNICODE 의 Code가 되지 않을까 싶다. 즉 방법2는 .... 다음 회에 계속 이라는 거지 ㅋㅋ)

'Programming > Visual C++' 카테고리의 다른 글

출력3. DrawText  (0) 2011.06.28
출력2. WM_Paint  (0) 2011.06.28
출력1. TextOut  (0) 2011.06.28
How to Programming by Unicode in windows programming - 3  (1) 2011.06.25
윈도우즈 프로그래밍에서 유니코드로 코딩법 2  (1) 2011.06.25
 안드로이드공부를 하기 위해서 도서관에 있는 책들을 속독 음.... 정확히 말하면 발췌독으로 10권이상 봤다. 그러니까 내가 필요한 부분은 정독을 하고 그 외의 부분은 대충 무언가를 찾기 위해 주루룩 읽었다는 소리지.
 그러면서 느낀건. 아... 외국책을 찾아봐야 하나? 라는 생각이 들었다. 요세 아이패드로 C++ the Complete Reference 4th Ed 라는 책을 원서로 보고 있는데 우리나라의 윤성우씨 만큼이나 잘 만들었다. 끝까지 읽어봐야 하겠지만, 윤성우씨만큼 친근감이 있는 서체는 아니지만 그래도 필요한것과 알아야할것들의 순서를 잘 배열해서 책을 쓰셨다.
 공부를 잘하고 못하는것. 그것은 무언가를 배울때 무언가를 할때 포인트를 잡느냐 못잡느냐의 차이다. 한정된 시간속에서 뭐가 중요하고 뭐가 중요하지 않은지를 정확히 이해하기 빠르게 알려주는 선생이 좋은 선생이 된다. 그것에 실패해서 살아생전 아인슈타인은 특수상대성 이론으로는 노벨상을 못타지 않았는가?!
 아무튼, 아직 우리나라 책중에 읽을만한 안드로이드 책은 없다. 제대로 구성된 책도 없다. 커리큘럼을 어디서부터 어떻게 해야되는지 모르는 책들이 너무 많다. 읽어 본 책들중 70%가 비슷한 구성으로 되어있다. 그중 특이하다 싶은건 어도비 플레쉬로 만드는 안드로이드 프로그래밍이랑 인사이드 안드로이드. 그리고 아이폰이랑 크로스 플랫폼을 만들기위한 책.
 다른 책들은 너무 피상적인것들이 많다. 물론, 그렇게 코드를 보고 따라하는것이 시작이지만, 시작만 써놓은 책들이 대부분이다. 만약 원하는 것 이 있다면, 시작은 그렇게 하되 그 원리를, 주요 안드로이드 소스를 분석해서 그것을 응용할수 있게 해줘야 되는게 아닐까? 그걸 조금이나마 한 책이 아이폰 안드로이드 크로스 플랫폼에 대한 책이었다. 그림그리기 프로그램 프로젝트 하나만 잇어서 뭔가 좀 아쉽긴 했지만, 그런식으로 안드로이드 전체를 소개한다면 반드시 사고 싶은 책중 하나.
 
 만약 지금 시중에 나와 있는 책으로 안드로이드를 공부하자 하면, 리눅스를 알아야 하고, O/S를 이해해야하며, 커널과 기본적인 O/S기반 프로그램 및 자바의 클래스 구조, 패키지의 개념, 그리고 xml까지 알아야 한다. 어떤 책에서는 xml을 사용하지 않고 자바코드만으로 구현이 가능하다며 "소개"하면서 코드를 비교하는 것까지 해놓은걸 봤지만 그 다음 부턴 xml과 연동해서 설명한다. 물론, 그게 빠른 길이긴 하지만 그렇게 되면 원리의 시점에서 보면 조금 안타까운 면이 없지 않다. 사실 안드로이드 프로그래밍에서 xml을 주요 프로그래밍 소스로 선택한다면 음.. 뭐랄까 코드 해석 할때도 그렇고 짤때도 그렇고 여기저기 옮겨다니면서 해야되기때문에 더 복잡해 진다고나 할까? 아직 원리를 배우는 입장에서는 말이지... 만약 그걸 다 할수 있게 된 후에 xml을 사용한 프로그래밍을 추가한다면, 더욱 효과적이게 되지 않을까?
 
그리고 내가 정작 필요한 image 아답터를 만드는 방법이나 이런걸 소개한 책은 없었다. 게다가 4권정도는 그림 파일을 불러올 때 안드로이드에서 제공한 갤러리에서 string배열을 이용한 아답타 소개만을 코드로 보여주고 소개했을뿐이었다. SD card제어부분도 상당히 약한 게 많았고 말이지. 이래서 안드로이드를 공부하려면 인터넷을 이용하고 스터디로 모여서 하라! 고 하는게 아닐까? 음.. 아무튼 그럼 기말끝나고는 원서들좀 뒤적거려봐야겠다.

'Android' 카테고리의 다른 글

안드로이드 DatePickerDialog 년도 제한  (0) 2015.05.27
전치암호는 단순히 평문의 문자순서를 정해진 순서에 따라 재 배열한것. 
만약 모자라는 부분은 임의이 문자로 대치한다.

정해진 순서가 있기때문에 키값을 정해 순열을 정할수 있다.
예를들어 1 2 3 4 5 6 7 이란 순서를
3 4 5 7 1 6 4 2 이렇게 뒤집는다는 소리다.
만약 총 글자의 수가 7로 나누어 떨어지지 않는다면 빈 자리가 있을수 있기때문에 그 자리를 임의의 문자나 무언가로 대치한다.

평문 : My name is Peace

암호문 :  aeMmnysPa e ixxxcxxe 

복호문 : My name is Peace

C++ 코드

#define BLOCK_SIZE 7

#include <iostream>


using namespace std;


int main()

{

    int i,j,size, block_num;

    int key[64]={3,5,7,1,6,4,2};

    char p_text[64], c_text[64],d_text[64];

    

    cout<<"평문 입력 : ";

    gets(p_text);

    

    size=strlen(p_text);

    

    if(size%BLOCK_SIZE>0)

    {

        block_num = strlen(p_text) / BLOCK_SIZE +1;

        

        for(i=strlen(p_text);i<block_num*BLOCK_SIZE;i++)

            p_text[i]='x';

    }

    else

        block_num = strlen(p_text) /BLOCK_SIZE;

    

    for(i = 0;i<block_num;i++)

        for(j=0;j<BLOCK_SIZE;j++)

            c_text[i*BLOCK_SIZE+j] = p_text[(key[j]-1)+i*BLOCK_SIZE];

    

    cout<<"암호문 : ";

    for(i=0;i<block_num*BLOCK_SIZE;i++)

        cout<<c_text[i];

    cout<<endl;

    

    for(i=0;i<block_num;i++)

        for(j=0;j<BLOCK_SIZE;j++)

            d_text[(key[j]-1)+i*BLOCK_SIZE] = c_text[i*BLOCK_SIZE+j];

    

    cout<<"복호문 : ";

    for(i=0;i<size;i++)

        cout<<d_text[i];

    cout<<endl;


}

 

'정보 보안' 카테고리의 다른 글

Vigenere 암호(Vigenere cipher)  (0) 2011.05.21
단일 대치 암호(mono-alphabetic substitution cipher)  (0) 2011.05.21
시저 암호(Caesar cipher)  (0) 2011.05.21
다중 문자 대치 암호로써 하나의 펴문 문자가 여러 개의 암호로 대치 될 수 있다.
대치 될때 그 순서에 따라 즉, 비밀키 열(stream)에 의해 달라진다.
프랑스의 암호학자 B. Vigenere가 고안하여 이런 이름이 붙여 졌다.

이 암호 방식은 대치표의 개수를 d라 하면 다중 대치 암호방식은 주기 d를 갖는다. 다중 대치 암호에서 비밀키는 k1, k2 부터 kd까지 있고 평문 m1, m2부터 md, md+1, md+2 이렇게 있으면 암호 키열에 따라 d의 주기로 이 키열의 암호문이 변경된다.

평문 :: My name is Peace
키 값 :: diffcode 
암호문 :: 

Vg scah la Agofi
복문 :: My name is Peace

  

C++ 코드


#include <iostream>

#include <cstring>


using namespace std;


int main()

{

    int i=0,j=0,key_size=0, str_size=0, select=0;

    char str[50] ={0,};

    char key[16]={0,};

    

    cout<<"평문 또는 암호문을 입력하시오 "<<endl;

    gets(str);

    

    

    cout<<"암호문은 1 복호는 2 : ";

    cin>>select;

    

    fflush(stdin);

    

    cout<<" 값입력 : ";

    cin>>key;

    

    

    str_size= strlen(str);

    key_size= strlen(key);

    

        

    for(i=0;i<str_size;i++)

    {

        j=i % key_size;

        

        if(select==1){

        

        if((str[i]>='a')&&(str[i]<='z'))

        {

            str[i]-='a';

            key[j]-='a';

            

            if((str[i]+key[j]) <0)

                str[i]+=26;

            

            str[i] =(str[i]+key[j])%26;

            str[i]+='a';

            key[j]+='a';

        }

        

        if((str[i] >='A')&&(str[i]<='Z'))

        {

            str[i]-='A';

            key[j]-='A';

            

            if((str[i]+key[j])<0)

                str[i]+=26;

            

            str[i]=(str[i]+key[j])%26;

            str[i]+='A';

            key[j]+='A';

        }

        }

           

        if(select == 2)

        {

            

            if((str[i]>='a')&&(str[i]<='z'))

            {

                str[i]-='a';

                key[j]-='a';

                

                if((str[i]-key[j]) <0)

                    str[i]+=26;

                

                str[i] =(str[i]-key[j])%26;

                str[i]+='a';

                key[j]+='a';

            }

            

            if((str[i] >='A')&&(str[i]<='Z'))

            {

                str[i]-='A';

                key[j]-='A';

                

                if((str[i]-key[j])<0)

                    str[i]+=26;

                

                str[i]=(str[i]-key[j])%26;

                str[i]+='A';

                key[j]+='A';

            }


            

        }

    }

    

    cout<<endl<<"암호화 또는 복호화된 결과 출력 : "<<str<<endl;

    

    return 0;

    

} 
말 그대로 글자 하나하나를 다른 알파벳으로 대치해서 암호화하는 것.
예전에 한글을 만들어서 사용해 봤으나 1시간만에 친구에게 해독되었다.......

전수 조사 공격에는 비교적 강인한 특성을 가지지만 이렇게 출현 빈도수를 따라 통계학 적인 방법으로 분석하면 비밀 키를 찾아내는 것이 어렵지만은 않다. 

'정보 보안' 카테고리의 다른 글

전치암호(transposition chipher)  (0) 2011.05.21
Vigenere 암호(Vigenere cipher)  (0) 2011.05.21
시저 암호(Caesar cipher)  (0) 2011.05.21
 가장 오래된 암호중 하나.
 줄리어스 시저(Julius Caesar)가 사용하여 그 이름이 유래.
 
 가장 대표적인 대치 암호(substitution cipher)

평문 문자를 다르 문자로 일대일 대응시켜 암호문을 만들어 내는 방식.

 
 a  0  n  13
 b  1  o  14
 c  2  p  15
 d  3 q  16
 e  4  r  17
 f  5  s  18
 g  6  t  19
 h  7  u  20
 i  8  v  21
 j  9  w 22 
 k  10  x  23
 l  11  y  24
 m 12   z  25

 영문 문자에 번호를 0부터 25까지 부여하여 이 문자를 비밀키와 조합하여 법연산하는 것이 특징.

만약 암호용 비밀키가 k라고 가정하고 평문은 p 암호문이 c라고 하면

c = p+k mod 26
m = c - k mod 26

이 되는 것이다.

여기서 시저 암호의 키는 평문의 시프트 간격인 k 가 되고 mod 26이란 모듈러 연산으로 합의 값을 다시 26으로 나눈 나머지 값을 의미한다.

ex

평문 :: My name is Peace
비밀 키 :: 6
암호문 ::  Se tgsk oy Vkgik
복호 키 ::-6
복호 문 :: My name is Peace

C++로 만들어진 코드


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
#include <cstring>
 
using namespace std;
 
int main()
{
    int i=0, key=0, str_size=0;
    char str[50={0,};
 
    cout<<"평문 또는 암호문을 입력하시오 "<<endl;
    gets(str); // Enter가 들어올때까지 입력을 받기위해 gets를 사용했다.    
 
    cout<<"키 값을 입력하세요 : ";
    cin>>key;    
 
    str_size=strlen(str); // 평문, 암호문의 길이를 구한다.    
 
    for(i=0;i<str_size;i++)
    {
        if((str[i]>='a')&&(str[i]<='z'))
        {
            str[i]-='a';
 
            if((str[i]+key) <0)
                str[i]+=26;            
 
            str[i] =(str[i]+key)%26;
            str[i]+='a';
        }        
 
        if((str[i] >='A')&&(str[i]<='Z'))
        {
            str[i]-='A';
 
            if((str[i]+key)<0)
                str[i]+=26;            
 
            str[i]=(str[i]+key)%26;
            str[i]+='A';
        }
    }    
 
    cout<<endl<<"암호화 또는 복호화된 결과 출력 : "<<str<<endl;
 
    return 0;
 
 
 
cs





C++에서 복사생성자를 사용할때 주의 사항
 * 묵시적 변환
 만약,
class example{
private:
public:
  example(){}
};
void main()
{
  example a;
  example b=a;
}

라는 코드가 있을 때
example b=a;
라는 코드는
묵시적으로 example b(a);
라는 코드로 묵시적으로 변환.

(참고 이를 막기 위해 explicit라는 키워드가 존재. 묵시적 변환을 막으려면, 복사 생성자 함수 앞에 explicit를 넣어주면, 묵시적으로 변환하지 않음.)

=> 형식

class example{
private:
public:
  example(){}
  example(const example &a){}
};

요롷게 선언을 해주면 됨.
함수 오버로딩에 따라 클래스를 복사할경우, 클래스 하나를 다시 생성해줌.

'Programming > C/C++' 카테고리의 다른 글

무식하게 소수 판별 하는 방법  (0) 2013.07.29
 ARM의 데이터 이동명령어는 크게 3가지로 나눌 수 있습니다.
  • 한 레지스터를 이동시키는 명령어
  • 여러개의 레지스터를 이동시키는 명령어
  • Swap 명령어

 첫번재의 move single register instruction이 가장 일반적입니다. 그리고 그렇게 크게 어려운 점은 없다고 봐도 될 것입니다. 그리고 세번째로 나와 있는 스왑(SWAP)명령어는 사용자로서는 잘 사용하지 않고 다중프로세서 시스템에서 공유 데이터 관리를 위해 주로 사용됩니다.
 하지만 이 챕터. 아니 명령어를 이해하는데 가장 걸림 돌이 되는 것은 무슨 프로그래밍이든 구조든지 메모리 access가 아닐까 생각됩니다. 특히 두번째에 언급했던 mutiple register road and store는 많은 데이터를 블럭단위(연속된 메모리주소)로 이동하는데 적합하고 ARM을 더욱 효율적으로 만들었습니다.
 이 Multiple Register Road and Store를 조금 더 부연설명을 하자면 최대 16개(레지스터의 가용갯수)까지 가능합니다.

 ARM에서의 이동명령어는 모두 레지스터를 이용한 간접 주소지정모드를 사용하게 된다. 레지스터 간접주소지정 방법은 한 레지스터에 메모리 주소값을 넣어두고 그 값을 주소로 이용하여 메모리에서 데이터를 읽어서 오퍼랜드를 가져오거나 메모리에 데이터를 써서 오퍼랜드를 저장합니다. 이때 메모리 주소값이 들어가 있던 레지스터를 베이스 레지스터(Base Register)라고 합니다.

 일단 ARM에서 유일안 메모리 접근 명령어인 LDR와 STR 명령어의 예를 살펴보도록 하겠습니다.

 LDR r1,[r2] ; r1 <- memory[r2]

 이 위의 예제는 r1에 다가 저장을 하는데 r2에 있는 값이 아니라 현재 r2에는 메모리의 주소값이 저장되어 있을 겁니다. 그 주소값이 가르키는 값을 r1에다가 저장하라는 것입니다. C 언어로 이야기 하자면 포인터 정도가 될 것 같습니다. 아, 그리고 STR도 한번 살펴보고 가도록 하겠습니다.

STR r1,[r2] ; memory[r2] <- r1

 이 위의 예제는 메모리에 값을 저장하는 예 입니다. LDR의 예제와 마찬가지로 r2가 가르키는 곳에 r1값을 저장하고 있습니다.  여기서 중요한 것은 데이터의 이동방향입니다. 이것을 헤깔리지 않도록 하는것이 중요합니다.

 
 이번에는 그렇다면 위의 예제에서 어떻게 r2에 memory 주소를 넣을 수 있을지 생각해보도록 하겠습니다. 첫번째로는 프로그램의 시작위치에서 계산하는 방법이있습니다. 이것이 가능한 이유는 r15가 pc, 즉 프로그램이 시작되는 부분을 가르키기 때문에 여기서 뺄셈을 해서 사용하면 됩니다.
 하지만 이 방법은 번거롭기도 하고 사용자가 하기에는 자칫잘못했다가는 엉키는 작업입니다. 그래서 생긴 명령어가 ADR 명령어 입니다.
 원래 명령어라는 것은 CPU가 처리하는 것 입니다. 하지만 이 ADR 이라는 명령어는 CPU에 명령하는 것이 아닙니다. 바로 컴파일러에게 알려주는 것입니다. 그래서 이 ADR명령어를 슈도(pseudo)명령어, 즉 가상명령어라고 합니다. 이 ADR 명령어 사용방법을 알아보기 위해 예제를 하나 제시하겠습니다.
 
ADR r1, TABLE1 ;

 r1에 TABLE1의 주소값을 대입합니다. 사용자는 TABLE1의 주소값을 모릅니다. 하지만 ARM은 알고 있습니다. 사실 사용자는 알 필요가 없습니다. 굳이 알고 싶다면 r1을 출력하면 되지만 컴퓨터가 알아서 해주는데 굳이 TABLE1의 주소를 알 필요는 없습니다. 이렇게 사용자가 사용가능한 메모리의 주소값을 얻었습니다.

 
그럼 이제는 이 r1의 다음 주소를 알아내는 방법입니다. 이것을 base plus offset 주소지정이라고 합니다.
 다음 중 r2에 r1의 다음주소에 있는 값을  저장 하는 코드는 무엇일까요? 

  1. LDR r2,[r1,#1]
  2. LDR r2,[r1,#4]
  3. LDR r2,[r1,#16]
  4. LDR r2,[r1,#32]

일단 생각을 해보도록하자. r1의 주소가 만약 0x1000 이라고 한다면 r1,#1은 0x1001이 될것입니다. 오, 이거 그럴사 합니다. 하지만 ARM에서 사용하는 값들은 32bit의 길이로 저장됩니다. 오! 그렇다면 정답은 4번 r1,#32 가 되어 0x1032? 땡! 아닙니다. 왜냐하면 일반적으로 주소값을 사용할때는 bit단위가 아닌 byte단위를 사용하게 때문에 답은 2번 r1,#4 즉 0x1000 번지의 다음번지는 0x1004가 됩니다.

 이렇게 base plus offset주소를 지정하는데 3가지 방법이 있습니. 지금 방금 사용한 방법이 pre-indexing이라고 합니다.

 pre-indexing이란 명령어를 실행하기 전에 base 에다가 Offset을 더해 주소에 있는 값을 레지스터에 더하는 것입니다.
 그다음은 Post-indexing 입니다. post-indexing이란 pre-indexing의 반대 개념으로 base 값만으로 명령어 실행후 base 값에 offset을 더해 base에 저장합니다. 그리고는 다음 실행시 반영합니다. 즉 저장후 base 값을 update 시키는 것 입니다. 예를 들어 보겠습니다.

 LDR r0,[r1],#4 ;
 
 이렇게 하면 r0에 r1의 값을 저장 한 후 r1의 값을 그 다음주소값으로 update 시켜 줍니다. 그래서 다음 실행시 반영이 됩니다. 

 그리고 마지막으로 auto-indexing이 있습니다. auto-indexing은 두개를 합쳐놓은 것인데 일단 pre-indexing으로 계산을 한 후에 base register를 update 합니다. 다시 정리 해 보자면

pre-indexing은 update하여 주소에 있는 값을 register에 저장하고
post-indexing은 주소에 있는 값은 register에 저장 한 후 base값을 update 합니다.
auto-indexing은 update하여 주소에 있는 값을 register에 저장하고 또 base 값을 update하게 됩니다.

이 auto-indexing을 사용하는 방법은 pre-indexing의 명령어 뒤에 '!'를 넣어주면 됩니다.

 LDR r2,[r1,#4]! ;



 


  


'ARM(Advanced RISC Machine)' 카테고리의 다른 글

2.1 ARM 데이터 처리 명령어  (0) 2009.10.23
2 - ARM과 Assembly  (0) 2009.10.23
1.4 - ARM의 Instructions  (0) 2009.10.23
1.3 - ARM의 Register  (0) 2009.10.23
1.2 - ARM과 Bekeley RISC  (0) 2009.10.23
 이번 절에서는 ARM 명령어중 데이터 처리 명령어를 알아보겠습니다.
 일단 ARM 데이터 처리 명령어에는 크게 1. 레지스터 오퍼랜드 명령어와 2.시프트(shift)된 레지스터 operand가 있습니다.
 또 레즈스터 오퍼랜드 명령어에는 
  • 산술 명령어
  • 비트논리연산 명령어
  • 레지스터이동 명령어
  • 비교명령어

가 있습니다.
 
 일단 산술명령어는 2진 덧셈, 뺄셈을 뜻합니다. 이때 오퍼랜드는 32bit를 사용하고 부호를 사용하지 않을수도 있고 부호를 사용한다면 2의 보수체계를 사용합니다.
 ADD : 덧셈, ADC : carry와 함께 덧셈
 SUB : 뺄셈 SBC : 케리와 함께 뺄샘
 RSB : 역순으로 뺄셈 RSC : 역순으로 케리와 함께 뺄셈

 비트논리 연산명령어는 두개의 오퍼랜드의 비트를 연산하는 명령어입니다. 예를 들어 11 곱하기 23을 일반적으로 하면 230+23이 되어 253입니다. 하지만 비트연산을 하면 11을 2진수로 표현하면
 01101(2), 23은 10111(2) 입니다. 이 둘을 AND 비트연산을 하면 00101(2)이 되어 5가 됩니다.

레지스터 이동 명령어는 두번재 오퍼랜드첫번재 오퍼랜드로 이동시키는 명령어 입니다. 예를 들어 
 MOV R0,R2 ; r0 := r2 
가 됩니다. 
 하지만 이 과정을 조금 더 정확히 보자고 한다면 이것은 연산명령어, 즉 ALU를 거쳐가야 하므로 사실은 R2에 0을 더하는 연산명령어 입니다.다시 말하면 MOV R0,R2 는 ADD R0,R2,#0 을 바꿔서 쓴 것이라고 생각해도 무방합니다.

 비교명령어는 연산을 하지만 결과값을 레지스터에 저장하는 것이 아니라 CPSR의 Condition flag만이 update된다.

 여기서 ARM CPU만의 특징이 하나 더 나오는데 그것은 바로 Default flag update가 안된다는 점이다. 보통 cpu들은 이런 연산을 할 경우 명령어에 특별한 명령을 더 하지 않아도 flag가 update된다 하지만 ARM CPU에서는 ADD를 하더라도 ADDEQ 와 같은 뒤에 명령어를 더 붙여줘야만 flag가 업데이트 된다.
 
 
이번에는 시프트된 오퍼랜드에 대해 살펴 보겠다.

이 ARM 명령어 세트 형식을 보면 두번재 소스의 코드 길이가 12bit나 된다. 왜 이렇게 많은 bit를 할당해놨을까? 그 이유는 ALU의 shifter에 있다.
 ARM의 ALU의 두번째 source operand쪽 회로에는 Shifter가 달려있다. 즉, 모든 2nd source가 ALU연산을 할 때 shifter를 거친다는 말이다. 이런 이유로 RSB같은 명령어가 존재한다.(뒤에 있는 오퍼랜드를 shift 연산하여 뺄 경우)
 사용 예는 이렇다.
 ADD r0,r1,r2,LSL #3 ; r0 = r1 + (r2 * 3)
이 shift는 한 사이클에 모두 해결되며 31bit까지 할 수 있다. 물론 이 shift의 방법에도 여러가지가 있다.
 

  • LSL : 왼쪽으로 쉬프트 후 LSB(1의 자리)의 빈자리를 0으로 채움
  • LSR : 오른쪽으로 쉬프트 후 MSB(부호비트)의 빈자리를 0으로 채움
  • ASL : LSL과 같음
  • ASR : 오른쪽으로 쉬프트 한 후 MSB(부호비트)의 빈자리가 양수인 경우에는 0 음수인 경우에는 1로 채운다.
  • ROR : 오른쪽으로 쉬프트 한 후 LSB에서 밖으로 나온 비트는 다시 MSB로 들어간다
  • RRX  : MSB가 비워지면 CCR의 C flag가 입력된다. 나머지는 ROR과 같음


또한 알아두어야 할 것은 Condition Code의 설정이다. 일반적으로 TST 또는 CMP와 같은 비교명령들을 제외한 일반 산술 및 논리 연산은 ARM CPU의 특성에 따라 Condition flag를 update시키지 못한다. 따라서 condition flag를 update시키려면 명령어 끝에 'S'를 붙여 주어야만한다.

'ARM(Advanced RISC Machine)' 카테고리의 다른 글

2.2 - ARM 데이터 이동 명령어 -1  (0) 2009.10.23
2 - ARM과 Assembly  (0) 2009.10.23
1.4 - ARM의 Instructions  (0) 2009.10.23
1.3 - ARM의 Register  (0) 2009.10.23
1.2 - ARM과 Bekeley RISC  (0) 2009.10.23
 ARM의 기본적인 소개가 끝나고 이제 프로그래밍에대한 기반지식을 배울 차례입니다. 이번 챕터에서는 ARM에서 사용하는 명령어 형식과 메모리 접근방법등을 공부할 예정입니다.

 일단 ARM의 명령어 형식은 일반적으로 32-bit 워드로 이루어져 있습니다. 만약 메모리상에서 16-bit를 사용하는 것은 하프워드(Half-word) 그리고 8-bit는 바이트(byte)단위라고 부릅니다. 따라서 연산 결과들도 32비트이며 레지스터에 저장됩니다.
 또한 ARM의 명령어는 Operand와 Opcode로 이루어져 있습니다. 여기서 Opcode란 명령어가 어떤 명령어인지 즉 operation code란 말이고 operand는 피연산자 즉 opcode가 필요로하는 데이터를 가르키는 말입니다. 예를 들어
 
     ADD r1,r2,r3     ;   r1= r2 + r3

이 소스에서 Opcode는 ADD 이고 operand는 r1,r2,r3가 됩니다. 이 operand사이에서도 source operand와 destination operand가 있는데 위 소스에서 source operand는 r2와 r3이 되겠고 destination operand는 r1이 되는걸 볼수 있습니다.


 위 그림은 ARM 명령어 세트의 형식을 나타내 준다. 맨 앞 4 Bit는 Condition Bit로 챕터 1의 마지막 장에서 설명했던 ARM 명령어들의 특징에서 모든 명령어가 Condition을 포함한다는 것을 나타내 줍니다.
 그리고 그 다음 8bit가 opcode를 나타내 주는 bit로 어찌보면 위 그림에서 가장 복잡하게 보이는 부분입니다. 하지만 위의 비트들은 일일이 알필요가 없으니 그냥 넘어가도 좋습니다.
 그다음에 나오는 20bit가 모두다 operand를 표시하는 bit입니다. 이 bit들은 조금더 뜯어 보면 앞의 4bit가 sourse bit 1입니다. 즉, 첫번재 나오는 sourse bit 입니다. 그리고 그 다음 4bit가 destination bit입니다. 그리고 그 뒤에 나오는 12bit가 sourse bit 2 즉, 두번재 나오는 소스 비트 입니다.
 차례 대로 나열하자면
 Condition(4) OPcode(8)  Source1(4)  Destination(4)  Source2 
정도가 됩니다.

'ARM(Advanced RISC Machine)' 카테고리의 다른 글

2.2 - ARM 데이터 이동 명령어 -1  (0) 2009.10.23
2.1 ARM 데이터 처리 명령어  (0) 2009.10.23
1.4 - ARM의 Instructions  (0) 2009.10.23
1.3 - ARM의 Register  (0) 2009.10.23
1.2 - ARM과 Bekeley RISC  (0) 2009.10.23

 이제 ARM의 소개가 거의 끝나 갑니다. ARM의 특징들을 조금 더 살펴보게 될텐데요 일단 명령어들의 특징을 보겠습니다.

  • The Load-Store Architecture
  • 3-address data processing instructions
  • 모든 명령어가 Condition을 포함
  • Multi-access가 가능한 Load-Store 명령어
  • ALU연산에 포함된 Shift 연산(->single cycle 처리가 가능)
  • 보조 프로세서 명령어 세트(새로운 명령어 생성 가능)
  • 16-bit의 Thumb 명령어

물론 이 모든 특징들을 지금 알 필요는 없습니다. 하지만 이런 특징들이 있다는 것은 알아둘 필요가 있습니다. 특히 ARM의 Load-Store 명령어가 특징이 되는 이유는 ARM에서는 CPU에서 명령어를 처리 할때 명령어를 처리할 때 마다 Memory에 접근을 한다면 Memory에 갔다오기까지 pipeline가 멈춰 있어야 하므로 Memory의 접근 명령어를 무조건 Load 명령어와 Store명령어만으로 구성해 놓았습니다.

 또한 ARM 명령들은 다음의 세가지 범주로 나눌 수 있습니다. 

  1. 데이터 처리 (ALU)
  2. 데이터 전송 (Load-Store)
  3. 제어 명령 (branch, branch & link, trap)

  이렇게 나누어진 명령어들이 CISC보다는 적지만 ARM은 RISC의 구조적인 성능 저하를 막기 위해 명령어를 복잡하게 설계해 놓았습니다.

'ARM(Advanced RISC Machine)' 카테고리의 다른 글

2.1 ARM 데이터 처리 명령어  (0) 2009.10.23
2 - ARM과 Assembly  (0) 2009.10.23
1.3 - ARM의 Register  (0) 2009.10.23
1.2 - ARM과 Bekeley RISC  (0) 2009.10.23
1.1 ARM과 RISC (RISC란 무엇인가?)  (0) 2009.10.23

 ARM의 레지스터는 r0부터 r15까지 총 16개의 user mode의 Register가 존재합니다. 거기에 system mode에서 사용하는 15개의 register와 각모드에 존재하는 CPSR, SPSR을 합쳐 총 37개의 레지스터가 존재 합니다.

 
<출처 : ARM Sysyem-On-chip Architecture>
 위 그림은 ARM Register를 그림으로 나타낸 것입니다. 일단 가장 왼쪽에 User Mode를 보면 하얀색으로 r0~r15(pc)와 CPSR이 있습니다. 일단 r0~r12까지는 일반적으로 cpu에서 load-store 명령어를 가지고 사용할 수 있는 Register들 입니다.
 그리고 r13부터가 조금 특이한 성격을 갖고 있는데 일단 r13은 SP(Stack Pointer)라고 불립니다. 이 스텍포인터는 다른 컴퓨터 구조와 마찬가지로 사용하는 프로그램의 메모리중 가장 위쪽(Top)을 가리킵니다.
 R14는 링크 레지스터로 예외처리(Excetpion Mode)를 하고 난 뒤 혹은 서브루틴 함수에서 다시 메인으로 돌아갈 주소를 저장하는 링크 주소를 저장하는 레지스터라고 생각하시면 도비니다.
 R15는 PC라고 불리는 Program Counter 입니다. 이 PC가 하는 역할은 실행할 프로그램을 잃어올 메모리의 위치를 가리킵니다. 따라서 PC는 user mode뿐만 아니라 system mode에서도 공유하여 사용합니다.

 위에 그림을 보면 의문점이 하나 생길 수 있습니다. 그것은 "위에 그림을 보니 FIQ라든가 SVC라든가 다른 system mode를 보면 register들이 usermode에 비해 현격히 적습니다. 특히 svc, abort, irq, undefined 같은 경우는 r13, 14. 두개의 register밖에 사용을 못하는겁니까?" 라고 물을 수 있습니다.
  결론을 먼저 말한다면 다행히도 아닙니다. 지금 위에 있는 것들은 유저모드의 레지스터와 '예외처리 및 에러처림의 전용레지스터'만 나타낸 것입니다. 따라서 어떤 모드 이던지 모든 모드에서 레지스터는 r0~r15+CPSR을 사용합니다. 그리고 전용레지스터가 아닌 레지스터는 공용(General)으로 사용합니다.
 다시말해서 일반 interrupt가 발생시 irq mode로 전환됩니다. 이때 사용되는 r0~r12까지는 user mode에서 사용하던 General register(범용레지스터)를 사용하게 됩니다. 물론 이 때 있던 값들은 메모리에 저장이 되어서  다시 되돌아갈 주소를 r14가 저장을 해주게 됩니다.

 그럼 이제 ARM register의 mode 들을 살펴보겠습니다.
 가장 일반적인 user mode는 일반적으로 응용프로그램들을 실행할때 사용됩니다. 이때 다른모드로 넘어가기 위해 svc(supervisor call)를 사용하여 넘어가게 됩니다.
 FiQ는 Fast IRQ로서 빠른 Interrupt 처리를 위해 만들어진 레지스터로써 ARM의 중요점인 interrupt처리를 위해 다른 예외처리보다 더 register를 설정해 두었습니다.
 IRQ는 일반적으로 사용되는 외부 Interrupt들을 처리하기 위해 만들어진 Register로서 대부분 하드웨어적인 입출력 장치들의 완료보고등을 처리한다고 보시면 됩니다.
 SVC는 Siperviser Call로서 일반 유저 모드가 아닌 다른 레지스터들. O/S에서의 커널이나 장치 드라이버등을 처리할때 작동됩니다. 이 Mode는 Reset신호가 입력이 되거나 SWI(software interrupt : 프로그래머의 임의적인 요청)가 발생하면 SVC모드로 넘어 갈 수 있습니다.
 Abort는 interrupt가 아닌 error처리모드로 데이터를 읽거나 쓸 때 오류가 발생할 경우 abort모드로 전환되어 처리됩니다.
 undefined는 처리하고자 하는 명령어가 ARM명령어가 아닌경우(디코더에 정의 되어있지 않은경우)에 발생하는 모드입니다.


그리고는 또 중요한 것이 각 모드마다 있는 CPSR(SPSR)이라는 Register입니다. 이 CPSR은 Current Program Statue Register의 약자로 즉, 현재의 프로그램 상태를 나타내 주는 레지스터 입니다.
 이 CPSR은 Condition Flag와 현재의 Mode를 나타내주는 값들이 들어 있습니다. 우선 중요한것은 Condition Flag인데 상위에 4bit(순서대로 N,Z,C,V)가 이것을 나타내 줍니다.

 N : Negative로 ALU operation의 결과가 음수인 경우 최상위 비트가 1로 Setting이 됩니다. 즉, CPSR의 최상위 비트가 1이 됩니다.
 Z : Zero로 ALU operation의 결가가 0인경우 set이 됩니다. 보통 비교연산중 같은지를 비교할 때 많이 사용됩니다.
(왜냐하면 컴퓨터에서 비교연산을 하는 방법은 a와 b가 같은지 볼 때 a에서 b를 빼서 0이 나오면 같다는 의미이므로 z flag를 사용합니다.)
 C : Carry로 ALU operation의 결과 혹은, shift operation의 결과가 carry출력이 발생하면 set이 됩니다.
 V : oVerflow로 산술연산시 sign bit를 넘어서면 V가 Set이 됩니다.

SPSR을 알아보자면 SPSR은 Saved Program Status Register로 지금 실행되고 있는 Mode를 실행하기 바로전의 CPSR을 저장하는 것으로 하는 역할은 같습니다.

이번 절은 다른분들의 블로그에서도 많이 참조하였습니다. 다른것들은 몰라도 이 내용은 여러군대서 소개되어 있더라구요. 

'ARM(Advanced RISC Machine)' 카테고리의 다른 글

2 - ARM과 Assembly  (0) 2009.10.23
1.4 - ARM의 Instructions  (0) 2009.10.23
1.2 - ARM과 Bekeley RISC  (0) 2009.10.23
1.1 ARM과 RISC (RISC란 무엇인가?)  (0) 2009.10.23
1 - ARM 이란 무엇인가?  (0) 2009.10.23

 ARM이 Bekeley대학의 Bekeley RISC를 기반으로 만들었다고는 했지만 Bekeley RISC를 그대로 복사해서 놓은 것은 아닙니다. 물론 Bekeley RISC에서 많은 개념들을 빌려왔지요. ARM 프로세서가 Bekeley에서 빌려온 중요한 개념들을 보자면


  • Load-Store 구조
  • Fixed-length 32bit instruction
  • 3-address instruction 형식

 

 정도 입니다. 가만히 보자 하면 이 세가지는 ARM과 Bekeley RISC뿐만 아니라 다른 RISC구조에서도 찾아볼 수 있는 일반적인 RISC 구조라고 할 수 있습니다. 중요한 것은 채택하지 않은 기능들인데 ARM이 아래 기술할 것들을 채택하지 않은 가장 큰 이유는 사실 Bekeley RISC라는 프로세서는 Computer의 CPU를 고려해서 설계한 Chip입니다. 따라서 '비용'의 문제점이 나타나게 되고 비용을 줄이자면 또한 '속도'의 문제점이 나타나게 됩니다.
 ARM은 Embedded에 적용하기 위한 CPU입니다. 따라서 첫번째로 비용이 크게 들면 안됩니다. 두번째로는 현대의 컴퓨터처럼 그렇게 많이 빠를 필요는 없습니다. 적당한 속도만 내주어도 괜찮다는 이야기 입니다. 세번째로는 적은 비용을 위해 구조가 간단해야 합니다.

 이런 이유로 ARM이 Bekeley에서 적용하지 않은 개념들을 보자면

 

  • Register Windows(레지스터 윈도우)
  • Delayed Branch(지연 분기)
  • Single-cycle Execution(싱글사이클 실행)

 

이 있습니다.

 우선 Register Windows란 일반적인 컴퓨터는 복잡한 멀티미디어와 프로그램들을 실행하기 위해 중첩 프로시져(Procedure : ①특정 작업을 수행하도록 이름 붙여진 컴퓨터 프로그램의 일부, 정보통신용어사전제6판,한국정보통신기술협회)(역저 : 하나의 함수라고 생각하면 편함)를 사용하게 되는데 이때 바람직하지 않은 데이터 트래픽이 발생하게 됩니다.
 그래서 Register Windows에서는 이 트래픽을 감소하기 위해 CPU에 많은 수의 레지스터를 확보한 후에 레지스터를 한번에 이동시키면 더 빠르게 처리 할 수 있습니다. 하지만 이럴 경우 설계시 레지스터를 많이 사용하게 되고 그러면 비용이 많이들어가 임베디드의 지향점인 '저렴한 비용'에서 멀어지게 됩니다. 따라서 ARM에서는 Register Windows가 아닌 Shadow Register라는 것을 사용하여 예외상황(Exception)이 발생시 한 Register에 이 전의 상태값을 저장하여 Procedure가 아닌 Exception 처리에 비중을 두게 됩니다.

 두번째로 Delayed Branch(지연분기)는 말 그대로 Branch, 분기 명령어에 대한 처리 방법입니다. CPU의 설계 기술중 Pipe LIne 이라는 기술이 도입되면서부터 CPU의 처리 속도가 비약적으로 높아 졌습니다. 따라서 ARM도 Pipeline을 도입하였습니다. 하지만 이 Pipeline에는 고질적인 문제점이 있는데 그것을 Hazard라고 합니다. 그중 이 Branch Hazard는 이 Pipeline 구조에서 해결 할 수 없는 문제점중 하나입니다.
 예를 들어 Pipeline이 돌아가고 있는 중에 분기문이 발생했습니다. Pipeline의 첫번재인 IF(instruction fetch, 명령를 읽는것)를 수행하고 ID(instruction decode)를 하게되면 다음 명령어가 들어와야 되는데 다음 명령어가 분기 명령어에 영향을 받을 경우 cpu는 처리하지 못하게 되는 현상이 발생합니다. 이것이 Branch hazard입니다. 
 Bekeley RISC에서는 이를 해결하기 위해 지연분기를 사용하였는데 이 지연분기란 분기 명령어와 이 분기명령어가 사용될 혹은 사용이 취소될 명령어 사이에 지금 실행되고 있는 분기명령어의 영향을 받지 않은 다른 명령어를 집어넣어 실행하게 됩니다. 그리고 나서 분기의 영향을 받은 명령어가 실행이 되도록 하였습니다.
 하지만 이 방법은 파이프 라인이 하나일때는 문제가 없지만 슈퍼스칼라(super-scalar)구조 에서는 분기 예측회로를 복잡하게 만드는 단점이 있고 또한 HardWare적으로 부담이 되기도 했습니다. 그리고 Embedded에서 필요로 했던 Exception처리를 복잡하게 만들기 때문에 지연분기를 사용하지 않았습니다.
 따라서 ARM에서는 BTB(Branch Target Buffer:분기예측)란 방법을 사용하는데 분기 명령어 발생시 BTB에서 True와 False를 예측하여 True로 예측되었으면 True를 Pipeline에서 실행 한 후 분기 명령어 다음 명령을 실행을 하고 만약 틀리면 True명령어를 취소하고 바로 False명령어를 실행하게 됩니다.

 마지막으로 Single-cycle execution 입니다. 이 Single-cycle execution은 말그대로 모든 명령어를 한 clock cycle동안에 수행하는 구조입니다. 이것이 가능한 이유는 pipeline을 적용하기 때문이겠지요. 따라서 첫번재부터 총 stage-1만큼의 명령어는 제외입니다.
 거의 대부분의 ARM 명령어는 한 cycle에서 처리가 가능하지만 load나 store같은 명령어는 데이터를 갖고 올때와 데이터를 읽고 쓸때 이렇게 두번의 Cycle이 필요로 하게 됩니다. 따라서 이 Single-cycle execution을 구현하려면 명령어 메모리와 데이터 메모리가 따로 있어야 하는데 이것또한 ARM 프로세서의 초반에서는 가격의 문제로 이것을 사용하지 않았습니다. 그래서 이 Single-cycle execution대신 메모리 access를 최소화하도록 auto-indexing addressing mode에서처럼 cpu내부의 유휴 자원들을 활용함으로써 전체 프로그램의 처리 시간을 줄여보려고 최선을 다했습니다.


 위에서의 세가지 적용하지 않은 구조들을 보면 대충 ARM에서 지향하는 바를 알수 있습니다. 그것은 바로 Simple입니다. ARM에서도 역시 가장 중요하게 생각했던 것은 Simple design이였습니다. 이렇게 설계해 놓음으로 인해서 ARM은 싸고 가볍고 저전력의 Chip을 개발할 수 있었습니다.

요약

 Bekeley에서 적용한 것 적용하지 않은 것  대체 수단 
Load-Store 구조  Register Windows  Shadow register 
 Fixed-length 32bit instruction Delayed Branch   Branch Target Buffer
 3-address instruction 형식  Single-cycle Execution  Multi-Cycle Execution

 Bekeley RISC에서 ARM 프로세서에 적용하지 않은 공통적인 이유.
   비싼가격. Exception처리에 중점을 두기 위해.

'ARM(Advanced RISC Machine)' 카테고리의 다른 글

1.4 - ARM의 Instructions  (0) 2009.10.23
1.3 - ARM의 Register  (0) 2009.10.23
1.1 ARM과 RISC (RISC란 무엇인가?)  (0) 2009.10.23
1 - ARM 이란 무엇인가?  (0) 2009.10.23
0 - 들어가는 말.  (0) 2009.10.23

 앞에서 ARM의 간단한 발생 배경을 알아보았습니다. 그렇다면 앞에서 말했던 RISC는 무엇일까요? 일단 RISC란 Reduced Instruction Set Computer의 약자 입니다. 말을 그대로 풀어보자면 줄어든 '명령어 세트' 컴퓨터 라는 것이지요. 즉, CPU 명령어의 개수를 줄여 하드웨어 CISC보다 구조를 조금 더 간단하게 만드는 방식이지요.

 RISC는 CISC와는 반대되는 개념입니다. 이 Arcon사가 BBC Microcomputer를 만드려는 1983년에는 16-bit의 CISC(Complex Instruction Set Computer)가 주류를 이루고 있었습니다. 하지만 이 CISC라는 것은 1. 명령어의 숫자가 많았고, 2. 명령어를 처리하는데 많은시간(clock cycle)이 들었고 따라서 3. Interrupt Latency(대기시간)도 길어지게 됩니다.
 하지만 ARM이라는 것은 멀티미디어와 같은 복잡한 성능을 요구하는 것이 아니라 기계와 기계를 컨트롤 할 수 있는, 즉 인터럽트를 잘 해결할수 있는 구조가 필요로 했습니다.

 따라서 Arcon사는 CISC로 CPU를 설계하는 것을 망설이던 때 Berkely 대학에서 내놓은 Berkely RISC I 의 논문이 발표되었습니다. 이 RISC I 은 몇명의 대학원생이 일년정도의 연구기간 동안 만들어 낸 것이였지만 그 당시의 어떠한 사용 CPU에서도 뒤지지 않았습니다.

 이런 배경에서 Arcon사에서는 이 Berkley RISC I을 변형하여  ARM CPU를 만들어 내었고 이것이 성공하게 되었습니다. 그리고 후에 Arcon Computers와 Apple computer가 합작을하여 ARM 유한회사라는 이름을 갖게 되었고 이때 ARM이 Arcon RISC Machin이 아닌 Advanced RISC Machins라는 이름을 사용하게 되었습니다.

 따라서 우리가 지금 사용하고 있는 ARM이라는 것은 Chip의 이름도 되겠지만 회사의 이름도 됩니다. 그리고 이때부터 ARM사는 CPU의 종류를 늘려가고 있습니다.


'ARM(Advanced RISC Machine)' 카테고리의 다른 글

1.4 - ARM의 Instructions  (0) 2009.10.23
1.3 - ARM의 Register  (0) 2009.10.23
1.2 - ARM과 Bekeley RISC  (0) 2009.10.23
1 - ARM 이란 무엇인가?  (0) 2009.10.23
0 - 들어가는 말.  (0) 2009.10.23

+ Recent posts