본문 바로가기
프로그래밍/SYSTEM HACKING

[SYSTEM HACKING] 어셈블리 사칙연산 명령어( add, sub, mul, imul, div, idiv ) / 계산기 만들기 실습

by B T Y 2017. 10. 20.
반응형

어셈블리 사칙연산 / 계산기 만들기 실습에 대해서 정리한다.




1. 사칙연산: +,  -, *, /

   * 어셈블리에서 나머지를 구하는 연산은 없다.


  1). 덧셈: ADD





( 어셈블리에서 + 이용해서 연산을 하는 경우는 Effective Address의 주소 연산을 할 경우에 [ ] 안에서만 사용하고

더하기 연산 기능은 add 명령어를 통해서 하게 된다.. )






  2). 뺄셈: SUB



  3). 곱셈


        - MUL( unsigned )

           * 피연산자는 하나이고 곱셈을 해서 eax와 edx 레지스터에 저장한다.


        - IMUL( signed )

           * 피연산자가 1부터 3개까지 올 수 있고 형식은 MUL과 똑같다.




( unsigned의 곱하기 연산을 할 경우에는 mul을 사용한다 )



( signed의 경우에는 IMUL 명령어를 이용하고 IMUL은 피연산자를 1~3개 가지게 된다 )



( 어셈블리에서 곱하기 연산 기능을 담당하는 mul 연산이고 mul은 부호가 없을 경우에 사용된다..

그리고 해당 명령어의 피연산자가 한 개이고 곱하기의 결과는 bit가 확장된다.. )



( 알수없는 값이 출력되는 이유는 위에서 먼저 add 명령어를 사용했기 때문에

연산을 담당하는 레지스터인 eax의 값이 변경되었기 때문이다...

그러므로 연산 명령어를 사용했다면 eax나 edx 레지스터를 초기화 해서 사용해줘야 한다.. )


* 레지스터를 이용할 때는 해당 값을 예측 할 수 없는 경우가 생길 수 있으므로 사용전에 먼저 초기화 해주는 것이 좋다.



( 여기서는 mov 명령어를 이용해서 사용전에 eax의 값을 0으로 초기화 해준다... )




( 곱셈의 경우에는 형 확장이 일어나는 경우가 많다...

위 코드를 확인해봐도 형 확장이 일어나는 경우인데 ax와 dx에 할당되는 값인 10000은

16bit로 표현이 가능하지만 mul을 이용해서 곱하게 되면 결과 값이 16bit로 표현 가능한 범위를 넘어가버리게 된다...

이럴 경우 16bit에서는 상위 16bit는 dx, 하위 16bit는 ax에 나눠서 저장이 된다.. )



( 현재 상위 16bit, 하위 16bit로 나눠져서 dx, ax에 값이 들어있기 때문에 위와 같은 결과가 나오게 되는데 

값을 정상적으로 가져오려면 상위 16bit의 값인 1525를 왼쪽으로 16번 시프트 연산 해주고

하위 16bit를 더해주게 되면 정상적인 값을 얻어낼 수 있다.. )


  4). 나눗셈


        - DIV( unsigned )

        - IDIV( signed )




( 곱하기 명령어와 마찬가지로 unsigned 범위의 나누기 연산을 할 때는 DIV를 사용한다 )



( signed 범위의 연산을 할때는 IDIV를 사용하고 여기서는 곱하기 명령어의 signed 형태인 

IMUL과 다르게 IDIV는 피연산자 하나만을 가지게 된다 )

     

* 보통 곱셈은 작은단위에서 큰단위로 이루어지고 나눗셈은 큰단위에서 작은단위로 

  이루어진다는 점을 주의해야한다!!!




( 피제수인 ax의 값을 제수 dl의 값으로 나누게 되면 결과는 몫은 al, 나머지는 ah로 들어가게 된다.. )



( 32bit인 eax로 출력 했기 때문에 514라는 값이 출력 되었고 공학용 계산기를 이용해서

상위 8bit, 하위 8bit씩 잘라서 보게 되면 상위 8bit에는 몫인 2의 값이 있고

하위 8bit에는 나머지 2의 값이 있는걸 확인할 수 있다 )



( 나누기의 경우 피제수가 해당 메모리의 표현 범위를 넘어갈 경우에는 값을 레지스터에 나눠서 저장한다..

16bit의 메모리 표현 범위가 넘어가는 해당 값이 32bit인 경우에는 상위 16bit를 dx에 넣고

하위 16bit를 ax에 넣고 해당 제수와 나누기 연산을 하게 된다... )



( 나누기 연산의 결과는 몫은 ax로 들어가고 나머지는 dx로 들어가게 되는걸 위에서 확인 가능하다... )



( 어셈블리에서 피제수 1000000을 표현할때 해당 값이 16bit 표현 범위를 넘어가기 때문에

상위 16bit dx와 하위 16bit ax에 15, 16960으로 값을 나눠서 저장했다...

위 계산기 결과에서 32bit를 상위와 하위 16bit로 나눈다음 상위 16bit의 값을 10진수로 변환해보면 15가 나오고

하위 16bit를 10진수로 변환해보면 16960이 나오는걸 알 수 있다.. )



( 상위 16bit를 모두 0으로 채워넣은 후에 값을 보게 되면 ax에 넣었던 16960이라는 값이 나오는걸 볼 수 있다.. )



2. 비교연산


3. 논리연산


4. 비트연산


5. 할당 / 타입변환




[실습] 

  - 두 정수를 입력받아서 덧셈, 뺄셈, 곱셈, 나눗셈(몫, 나머지)의 결과를 출력하는 

    어셈블리 프로그램을 작성


      ! 입력값의 크기는 8비트로 제한


      ! DIV 명령어 사용할 때


           - Floating Point Error 발생


           - DIV 명령어 전에 다음의 명령어 중에 하나를 사용


               - CBW: Convert Byte To Word ( ax <- al )

               - CWD: Convert Word To Double Word ( dx:ax <- ax )

               - CDQ:  Convert Double Word To Quad Word ( edx:eax <- eax )




extern printf

extern scanf


section .data

prompt_output:        db        "%d", 10, 00

prompt_input:          db        "%d %d", 00


section .bss

num1:        resd        1

num2:        resd        1

result:        resd        1


section .text

global main


main:

; scanf("%d %d", &a, &b);

push    num2

push    num1

push    prompt_input

call     scanf


; a + b;

mov    al,    byte [num1]

add     al,    byte [num2]


; printf("%d", a+b);

push    eax

push    prompt_output

call      printf


; a - b;

mov    al,    0

mov    al,    byte [num1]

sub     al,    byte [num2]


push    eax

push    prompt_output

call      printf


; a * b;

mov    al,    0

mov    al,    byte [num1]

mul     byte [num2]


push    eax

push    prompt_output

call      printf


; a / b

mov    eax,    0

mov    edx,    0

mov    ax,      word [num1]

mov    bx,      word [num2]

div      bx


push    eax

push    prompt_output

call      printf


push    edx

push    prompt_output

call      printf


반응형

댓글