티스토리 뷰

[전광성의 어셈블리어 이해하기:4회] 프로그래밍에 필요한 명령어와 디렉티브 (1)
저자: 전광성 |  날짜: 2005년 02월 02일  

/
0 여러가지 명령어(MOV, 다이렉트-오프셋 오퍼랜드, ADD, SUB)
2 .0 플래그와 연산자(OFFSET, PTR, TYPE, 인다이렉트 오퍼랜드) 및 배열
3 .0 JMP, LOOP 명령과 예제
/
  • 시작하기에 앞서...

    시작한지 얼마 되지 않은 것 같은데 벌써 4회이다. 이번 회에서 배우게 될 내용은 다음과 같다.

      - 자료전송명령(C에서의 대입연산자(=)의 역할을 한다)의 사용
      - 덧셈과 뺄셈과 이에 관련된 부수적인 내용의 이해
      - 포인터와 배열의 사용
      - jmp명령(C에서의 goto)과 루프사용

    이제는 본격적으로 프로그래밍에 필요한 명령어와 디렉티브들을 배우게 될 것이다. 어렵게 생각하지 않았으면 하는 바램이다. 고급언어에서 나오던 개념을 조금더 깊게 생각해 보아야 할 것이다. 계속 언급하지만, 고급언와의 연계성을 생각해 가며 읽어주길 바란다.

    또 한가지 언급하고 싶은 것이 있는데, 본 강좌는 개념 파악에 주력할 것이며, 세세한 명령의 사용법이나 구체적인 부분에 대한 설명은 지양할 예정이다. 이는 본 강의를 부담없이 봐달라는 뜻과도 일맥상통한다. 따라서 코드 자체의 구체적인 부분까지 고민하지 않았으면 하는 바램이다.

  • 짚어두고 가야 할 개념

    우리는 지난 회에서 변수 이름은 단지 레이블이라는 것을 배웠다. 다시 설명하자면, 변수의 이름은 프로그래머가 알아보기 쉽도록 영문자로 되어있을 뿐이지 사실은 그 변수가 위치하는 메모리 공간의 주소와 대응된다. 그래서 '데이터 레이블'이라고 부르는 것이다. 다음과 같이 변수선언을 했다고 하자.

    var1 BYTE 10h

    우리가 다음 과 같은 명령을 내리려 한다고 치자.

    mov al, var1

    al레지스터에 var1변수의 값을 복사해서 넣으라는 명령이다. var1이 위치하는 메모리 주소가 10400h라고 하자. 이 코드가 어셈블된 다음에는 다음과 같은 명령으로 바뀌어 있을 것이다.

    mov al, [00010400]

    각괄호[]는 그 안에 있는 값을 주소로 인식하여, 그 주소가 가리키는 곳을 찾아가라는 의미이다. 허나, 실제로 어셈블리어 상에서 위와같은 구문으로 임의의 주소로 역참조 할 수는 없음에 유의하도록 하자. 만약 데이터 레이블이 없다면 우리는 데이터를 기록하고 읽어올 때 직접 그 메모리 주소로 접근해야 할 것이다. 얼마나 불편하겠는가? 또, 실수로 메모리주소를 잘못 적어 프로그램이 이상하게 돌아갈 경우에 디버깅 할 것을 생각하면 소름이 돋는다.

    여기서 한가지 짚고 넘어갈 것이 있다. 지금 필자는 계속해서 메모리 주소라는 단어를 사용해 왔다. 그런데 좀 더 구체적으로 이야기하자면 메모리 주소라고 하기 힘들다. 이것은 메모리 내의 데이터 세그먼트(메모리에 데이터를 쓰고 읽기 위해 잡아놓은 공간)의 시작 주소로부터 어떤 변수가 위치한 주소까지의 거리(Offset)라고 하는 것이 정확하다. 물론 지금 당장 이해하는 데에는 그 거리(Offset)를 그저 주소라고 생각해도 큰 문제 없지만, 정확히 알아두는 것이 좋을 것 같아서 지금 언급하는 것이다.

  • MOV 명령

    가장 기본이 되는 MOV명령이다. 어떤 어셈블리어 언어코드를 보더라도 가장 근간이 되는 명령이다. MOV명령은 피연산자를 두 개 갖는다. 첫번째가 데이터를 복사할 목적지이고 두번째가 데이터를 읽어올 곳이다. 예를 들어보이겠다.

    mov al, 0FFh

    위의 코드는 al레지스터에 16진수 FF값을 넣는다는 뜻이다. FF앞에 굳이 0을 써 준 이유는 그렇게 하지 않고 FFh라고 쓰면 FFh라는 데이터 레이블을 뜻하는 것으로 어셈블러가 착각하기 때문이다. MOV는 변수, 레지스터, 상수값 등을 피연산자로 취할 수 있는데, 조심해야 할 것은 메모리에 적재되는 변수끼리의 복사는 허용되지 않는다는 것이다. 따라서 레지스터 변수를 하나 이용하여 그것을 거쳐 복사해야 한다. 마지막으로, 두 피연산자의 사이즈가 서로 같아야 하므로 이에 유의하기 바란다.

  • 다이렉트-오프셋 오퍼랜드(Direct-Offset Operands)

    다음과 같이 변수를 선언했다고 하자.

    array BYTE 10h, 20h, 30h, 40h, 50h

    보다시피 array라는 레이블로 시작하는 연속된 메모리 공간에 10h, 20h, 30h, 40h, 50h가 순서대로 들어가 있다. 우리는 여기서 array라는 데이터 레이블을 사용하면 10h를 접근할 수 있다. 하지만, 그 뒤의 것은 어떻게 접근할까? 20h는 다음과 같이 접근할 수 있다.

    mov al, [array + 1]

    []는 역참조한다는 것을 명시적으로 표기해 주는 것으로서, 그 안에 있는 값이 가리키는 곳을 찾아가라는 의미이다. array는 데이터 레이블이라서 배열의 첫 번째 원소가 위치한 곳의 메모리 주소라고 하였다. 또, 위와같이 콤마로 데이터 선언을 한꺼번에 할 경우 연속적인 공간에 배치된다. 따라서 [array + 1]이라고 쓰면, array라는 데이터 레이블이 갖는 주소값에 1을 더한 곳을 역참조하므로, 20h가 된다. 같은 방식으로 [array + 2]라고 하면 30h가 된다.

    이와 같은 것을 다이렉트-오프셋 오퍼랜드(Direct-Offset Operands)라고 한다. array + 1을 하는 것이 array의 값을 1 증가시키는 것이 아님에 조심하여라. 변수의 값을 이용한 계산을 하려면 해당 계산의 인스트럭션을 이용해야 한다.

  • ADD 명령

    문자 그대로 덧셈을 수행하는 명령이다. 이 명령은 두개의 피연산자를 받아 덧셈을 하는데, 덧셈의 결과를 첫 번째 피연산자에 저장된다. 피연산자가 될 수 있는 조건은 MOV명령과 같다. 다음의 예를 보면 좀 더 쉬울 것이다.
      .data
      var1 DWORD 30000h
      var2 DWORD 10000h
      .code
      mov eax, var1
      add eax, var2 ; eax는 40000h이 된다.

    지난번에도 설명했지만, .data에는 전역 변수를 기록하게 되고, .code에는 실제 수행 코드가 들어가게 된다. 본래 main프로시져가 있어야 하지만 편의상 생략하였으니 헷갈리지 않도록 주의하라. 처음에 var1의 값을 eax로 복사하였다. 그 다음에 eax와 var2를 더한 값을 eax에 넣은 것이다.

  • SUB 명령

    SUB명령은 subtract의 약자로 뺄셈을 하는 명령이다. 이 역시 ADD와 마찬가지로, 피연산자로 올 수 있는 변수나 상수의 요건이 MOV와 같다. 첫번째 피연산자에서 두번째 피연산자를 뺀 후 그 결과를 첫번째 피연산자에 저장한다. 단순히 뺄셈 명령이므로 더 이상 설명할 것이 없다. 간단히 예를 들자면, 위의 ADD명령어 예제에서 add대신 sub을 사용하였다면 최종적으로 eax에 들어간 값은 20000h가 될 것이다.

    이것 하나는 알아두고 넘어갔으면 한다. 컴퓨터는 덧셈밖에 할 줄 모른다는 이야기를 들어 보았는가? 그렇다면 컴퓨터가 어떻게 뺄셈을 수행할까? 이것은 첫 번째 장에서 2의 보수 표현에 대해 설명할 때 언급하였다. 빼고싶은 값에 2의 보수를 취하여 음수로 만든 후 더해주면 된다. 예를 들어, 4 - 1을 수행하고자 할 때는 4 + (-1)을 계산하여 뺄셈을 한다.
  • 댓글
    안내
    궁금한 점을 댓글로 남겨주시면 답변해 드립니다.