블로그 이미지
송시혁

calendar

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

Notice

Tag

Recent Post

Recent Comment

Recent Trackback

Archive



_start:
  xor  eax,  eax  ;2cycle;xor는 같으면, 0이므로 이 코드에서 0이 된다.
  mov  ecx,  20  ;2cycle 
whilez:  cmp  ecx,  0   ;2cycle          
  je  whileEND    ;3cycle
  
  dec  ecx    ;2cycle
  inc  eax    ;2cycle
  jmp  whilez    ;7+clycle

;20cycle



  xor  eax,  eax  ;2cycle
  mov  ecx,  20  ;2cyle
whilez:
  inc  eax    ;2cycle
  loop  whilez    ;7c
loop문은 0이 될때까지 계속해서 1찍 증감시키는 명령어 
순서는 아래 영어를 참고.



대충 해석하면 다음과 같다.
1. ECX의 값은 감소된다.
2. 감소된(새로운)ECX의 값이 0이라면, 반복문을 돌지 않고 아래 코드를 계속해서 실행.
3. 0이 아니라면, 다시 올라가서 반복하게 된다.

결국, 0이 될때까지 감소시키는 명령어이다. operland는 존재하지 않으며, 라벨을 써서 지정한다.


자, 그렇다면, 어는 쪽이 최적화된 코드일까?? C에서는 보통의 경우 1줄이라도 짧은소스가 최적화된 코드였으나,
어세블리는 각각의 명령어의 주기를 따져야 한다. 다음그림에서는 386cycle기준으로 더한 결과 이다.










xor eax,  eax     =2cycle
mov ecx, 20      =2cycle

cmp ecx, 0        =2
je                     =3
dec ecx            =2
inc eax             =2
jmp                  =7+

cycle=    18+


xor eax, eax      =2
mov ecx, 20      =2
loop                  7

cycle=11


단순히 반복적으로 어떤 수를 빼야한다면, 일일이 명령어로 구성하는것보다 loop문 명령어 한 개를 

써서 구성하는것이 소스의 길이로나, cycle적으로나 적으므로 컴파일을 빠르게 할 수 있다.



















posted by 송시혁


conditional jump는 상태적이 점프 ? 

ㅋㅋㅋ 

상황에 따라 점프를 할 수도 있고 안 할 수 도 있다는 얘기이다. 명령어는 jns, jnz....등이 있다.

이에 비해 unconditional jump는 무조건 특정한 곳으로 점프하는 명령어이다. 명령어 jmp가 된다.


아래 그림들과 같이 C언어 소스와 disassembly를 이용하여 비교하여 생각해보자.





아래 부분은 if문이다.

jns명령어를 주목한다. jns는 jump not set의 줄임말.

flag bit sf는 sign flags. 이 비트가 0이 아니라면, 이라는 뜻으로 해석한다.

즉, 1이 아니면, 음수가 아니라 양수라는 뜻이다. 그렇다면, if문의 조건인 0보다 작다는

성립하지 않는다. 따라서 jns가 실행이 되어 if문이 아래 코드가 실행되지 않고 else if문으로

점프한다. 


만약에 음수라면 아래 코드가 실행이 되고 jmp명령어가 실행되어 else문 다음코드로 실행이 된다.





else if,else

자, else if문에 코드를 좀 변경해 보겠다.


위의 그림에는 

cmp    dword ptr[a], 0

jne    main   +43h 라고 코드가 되어있다. 이것은 컴파일러가 최적화 해서 나타낸 코드

지금 단계에서는 어려움이 있으므로 이 부분을 고치면, 다음과 같다.


jnz    else의 주소(else의 주소를 알 수 없으므로...)



if문 끝에 jmp와 else if문 다음에 있는 jmp를 자세히 보면 숫자가 같다. 즉, 같은 곳으로 jump를 한다는 의미.

아마도 else문 다음에 나와있는 것은 컴파일러가 disassembly를 하면서, 화면상 이상하게 나온거. 

실제 코드상의 위치는 각각, if문과 else if문 끝에 jmp명령어가 위치한다.

if~else if~else문까지 있는데 끝을 나타낸다. 그래서 if문이 실행된 경우 else if, else문이 실행되지 않는다.



아래는 교재에서 설명하는 부분.




자, 교재에는 elseIfZero: jnz elsepos라고 되어있다. 즉, else if문의 조건이 거짓이면, elsepos로 점프한다는

뜻이다.




빨간색 네모의 영문과 not ture.까지 해석을 한다면, 대충 이런말이다.

sing flag(SF)가 1이 아니라면, elseIfZero로 점프한다. if문의 조건(balanced <0)은 거짓이 된다.










































































posted by 송시혁

2*((length*width)+(length*height)+(width*height))를 어셈블리로 구하기




아래소스는 메모리 변수를 3개나 써서 구현한거.


.386
.MODEL FLAT

ExitProcess PROTO NEAR32 stdcall, dxExitCode:DWORD

include io.h

cr  EQU  0dh
LF  EQU  0ah


.STACK 4096

.DATA  ;;;;;;;;;;전역변수
prompt1  BYTE  "This program will find the area of a rectangle", cr,Lf, Lf
  BYTE  "Width of rectangle? ",  0  
prompt2  BYTE  "Length of rectangle? ",  0
prompt3  BYTE  "height  of rectangle: ",  0
value  BYTE  16  DUP  (?)
answer  BYTE  cr, Lf, "The area of the rectangle is "
area  BYTE  11  DUP  (?)
  BYTE  cr, Lf, 0
length1  DWORD  ?
width1  DWORD  ?
height1  DWORD  ?
  
.CODE
_start:
Prompt:
  output  prompt1
  input  value,  16
  atod  value
  mov  length1,  eax                    ;length
  
 // 처음 키보드로부터 입력받은 eax(length)를 변수 length1에 백업.
  
  output  prompt2
  input  value,  16
  atod  value
  mov  width1,  eax                     ;width, 두번째 키보드로 부터 입력받은 eax를 width1로 백업
  mul  length1                             ;eax=length*width, 백업해 두었던 length1과 eax를 곱한다.
  mov  ebx,  eax                        ;eax를 ebx에 저장.
  
//ebx=length*widtd의 값을 가지고 있다.

  output  prompt3
  input  value,  16  
  atod  value  
  mov  height1,  eax                    ;height를 백업
  mul  length1                             ;eax=length*height    
  mov  ecx,  eax                        ;eax를 ecx에 대입     
  add  ebx,  ecx                         ;ebx=ebx+ecx
  
  mov  eax,  width1                     ;
  mul  height1
  add  ebx,  eax
  add  ebx,  ebx


  
  dtoa  area,  ebx
  output  answer
  
  
         
  INVOKE  ExitProcess,     0
        
PUBLIC  _start

END




레지스터만와 한 개의 메로리로 구현한 소스

.386
.MODEL FLAT

ExitProcess PROTO NEAR32 stdcall, dxExitCode:DWORD

include io.h

cr  EQU  0dh
LF  EQU  0ah


.STACK 4096

.DATA  ;;;;;;;;;;전역변수
prompt1  BYTE  "This program will find the area of a rectangle", cr,Lf, Lf
  BYTE  "Width of rectangle? ",  0  
prompt2  BYTE  "Length of rectangle? ",  0
prompt3  BYTE  "height  of rectangle: ",  0
value  BYTE  16  DUP  (?)
answer  BYTE  cr, Lf, "The area of the rectangle is "
area  BYTE  11  DUP  (?)
  BYTE  cr, Lf, 0

  
.CODE
_start:
Prompt:
  output  prompt1
  input  value,  16
  atod  value
  
  mov  edx,  eax ;length를 먼저 백업한다.
  
  
  output  prompt2
  input  value,  16
  atod  value
  mov  ecx,  eax  ;width를 ecx에 백업
  mov  ebx,  edx  ;ebx=width ,ebx에 width를 백업
  imul  edx,  eax  ;edx=length*width 
  
  
  output  prompt3
  input  value,  16  
  atod  value  
  imul  ecx,  eax  ;ecx=length*height, imul명령어를 사용하여 곱한다.
  add  edx,  ecx  ;edx=edx+eax , edx=(length*width)+(length*height)
  imul  ebx,  eax  ;eax=height*width, 백업해두었던 ebx와 현재 eax(height값)를 곱한다.
  add  edx,  ebx  ;최종적으로 edx=(length*width)+(length*height)와 eax=height*width를 더한다.
  add  edx,  edx ;그리고 2개를 더한다. 곱하기 2의 효과이다. 
  
  dtoa  area, edx  ;정수를 아스키코드로 변환하여 화면에 출력한다.
  output  answer
  
  
         
  INVOKE  ExitProcess,     0
        
PUBLIC  _start

END

메모리 여기서는 변수를 사용하면, cycle이 느려서 컴파일이 느리다. 그래서 같은 결과의 소스라도 어셈브리는 최적화가

관건이다. 그래서 어셈블리가 어렵다. 필자가 작성한 소스는 완전한 최적하가 아니다. 배우는 단계라서 조금 더 공부하고

올릴예정이다.


posted by 송시혁


inc = +1증가 명령어

dec=-1증감 명령어


해당 operland는 한 개. 레지스터16,32를 (ex:eax, ax,...)을 쓸수 있고 변수를 선언하여 메모리로 operland로 사용가능.


inc    register16/ register32

//     memory byte/word/dword


dec도 inc와 동일하다.


neg 명령어

위의 표와 같이 operland는 한 개. 레지스터와 메모리를 operland로 쓸 수 있으며, neg명령어를 사용하면, 해당문자에

부호(-)를 붙인다. 예를 들어서 eax의 값이 100이라면, neg eax를 수행하면, -100이 된다.




다음그림은 예제 소스, 각각, add와 sub를 이용하여 사칙연산하는 어셈브리어이다.

C언어는 참 간단했었는데 어셈블리는 정말 어려운거라고 실감했다.


386
.MODEL FLAT

ExitProcess PROTO NEAR32 stdcall, dxExitCode:DWORD

include io.h

cr  equ  0dh
Lf  equ  0ah


.STACK 4096

.DATA  ;;;;;;;;;;전역변수
prompt1  BYTE  "This program will evaluate the expression"   
  BYTE  "  - (x + y -2z +1)", cr, Lf, Lf
  BYTE  "for your choice of integer values.",cr, Lf, Lf 
  BYTE  "Enter value for x: "0

prompt2  BYTE  "Enter value for y: "0
prompt3  BYTE  "Enter value for z: "0
Value  BYTE  16 DUP (?)   
Answer  BYTE  cr, Lf, "The result is "
Result  BYTE  6 DUP (?)
  BYTE  cr, Lf, 0
.CODE
_start:
  output  prompt1;prompt1의 내용을 화면에 출력한다.
  input  Value,  16;키보드로 부터 입력받는다. 16개까지 가능.
  atoi  Value ;아스키 코드를 정수로 바꾼다. atoi함수에서 'i'는 레지스터가 2byte인 경우 쓴다. 
  mov  dx,   ax ;ax를 dx에 집어넣는다. 최초의 입력값을 백업의 의미.

  output  prompt2
  input  Value,  16
  atoi  Value
  add  dx,   ax;2번째 숫자를 입력받은 뒤에 dx에 백업해두었던 첫번째 숫자와 지금 입력받은 
두 번째 숫자인 ax를 더한다. 즉, dx=dx+ax

  output  prompt3
  input  Value,   16
  atoi  Value
  add   ax,   ax ;ax와 ax를 더한다. *2의 효과
  sub  dx,   ax;dx와 2를 곱한 ax를 뺀다. 그것을 dx에 넣는다.
  
         inc  dx dx를 1증가.
         neg  dx dx를 1가미소

         itoa  Result,  dx itoa는 atoi와 마찬가지로 2byte일 때만 사용가능하며, 정수를 아스키로 바꾼다.


         output  Answer
         
  INVOKE  ExitProcess,     0
        
PUBLIC  _start

END




.386
.MODEL FLAT

ExitProcess PROTO NEAR32 stdcall, dxExitCode:DWORD

include io.h

cr  equ  0dh
Lf  equ  0ah


.STACK 4096

.DATA  ;;;;;;;;;;전역변수
prompt1  BYTE  "This program will evaluate the expression"   
  BYTE  "  2(-x + y- 1)+z", cr, Lf, Lf
  BYTE  "for your choice of integer values.",cr, Lf, Lf 
  BYTE  "Enter value for x: "0

prompt2  BYTE  "Enter value for y: "0
prompt3  BYTE  "Enter value for z: "0
Value  BYTE  32 DUP (?)   
Answer  BYTE  cr, Lf, "The result is "
Result  BYTE  6 DUP (?)
  BYTE  cr, Lf, 0
.CODE
_start:
  output  prompt1
  input  Value,  32
  atod  Value
  mov  edx,   eax
  neg  edx
  
  output  prompt2
  input  Value,  32
  atod  Value
  add  edx,   eax

  dec  edx

  add  edx,  edx
  
  output  prompt3
  input  Value,   32
  atod  Value
  add  edx,  eax
  
  ;add   ax,   ax
  ;sub  dx,   ax
  
         ;inc  dx
         ;neg  dx

         dtoa  Result,  edx

         output  Answer
         
  INVOKE  ExitProcess,     0
        
PUBLIC  _start

END



posted by 송시혁

어큐물레이터




prefix



아래 그림은 xchg명령어 그림.



아래 그림은 잘못된 예


2개의 메모리가 사용되었기 때문에 잘못된 예이다. 메모리에서 메모리로 값을 집어넣을 수 없다.

어셈블리는 반드시 메모리->cpu->메모리를 거쳐야 된다. 아래 2두줄이 그 예가 되겠다.



아래 그림은 xchg 두 수를 스왑하는 명령어이다. xchg를 사용하지 않으면 밑에 mov명령어처럼 3줄을

써야한다. 이 중에서 어떤것이 최적화되고 사용하기 좋은가(?)라는 질문을 한다면, 컴파일러의 속도가 빠른것을

사용하는 것이 좋다.



일단, xchg명령어를 보면, timing386(386기준으로 속도, 주기)가 3주기가 된다.

그런데 아래 빨간색 mov명령어를 보면 timing386주기가 2주기. 명령어를 3줄썼으므로 6주기가 된다.

주기가 작을 수록 속도가 빠르기에 두 수를 스왑할 일이 생긴다면, xchg를 사용하는 것이 좋다.




아래 그림을 참조해서 본다면, memory가 reg보다 주기가 2배나 느리다는 것을 알 수 있다.






아래 그림은 엑셀 파일로 EFLAGS를 구성해본 그림. 아래 PDF에서는 OF, SF, ZF, CF를 모니터링 하고 있다.


SF=1이라면, 음수라고 생각하면 된다. 0이면 음수가 아니다.

ZF=계산 결과가 0이라면, 1이 된다.

CF=carry가 발생하면 1, 아니면, 0이다.

OF=1이라면, overflow


77ac와 4b35를 어셈블리어로 빼서 디버깅하였다. 그랬더니 efl이 16진수로 a96이 나왔다. 이것을 2진수로 반환하여

아래 그림과 같이 EFLAGS구조를 만들어 비교해보았다. 그랬더니, OF=1, SF=1, ZF=0, CF=0이 나왔다.

overflow와 음수인것을 의미한다. 



아래그림은 pdf의 그림. 위 그림과 비교.


아래는 디버깅한 부분. Dbl을 예로 들어봤다. EFL레지스터가 헥사값으로 206이다. 이것을 2진수로 변환하여 위의 두그림처럼, 비교하여 CF,SF,ZF,OF가 pdf와 일치하는지 보았다.

































































posted by 송시혁




디버그 모드로 실행.


mov ebx, 1000


bb e8 03 00 00


pdf의 appendix를 참고하여 opcode를 찾으면 bb가 나온다. 




아래 그림은 레지스터 값이다. 현재 eax는 헥사값 3e8(1000)이다. 지금 레지스터는 big endian형태로

되어 있다. 그러나 위의 메모리 상에는 little endian으로 되어 있다는 것을 유의하자.


































posted by 송시혁

어셈블리는 숫자뒤에 h, b,o 또는 q를 붙이면, 각각 16진수, 10진수, 8진수를 나타낸다.

아래 그림을 참고한다. 


(10진수는 아무것도 안적으면 된다. 마이크로소프트에서 어셈블리는 대소문자 구별안함. 다른 운영체제는 구별함.)



보충설명. 따옴표 ' '안에 있는 m은 m의 아스키코드를 나타낸다. 반대로 6d는 헥사값으로 m의 아스키 코드값이다.

결론은 char1과 char2는 똑같은 문장이다.

그리고 밑에 string1, string2를 유심히 보면, "joe"는 3바이트 "joe's"는 5바이트이다. 옆에 주석문 참고.

 "joe's"는 안에 '까지 포함한다.




아래 그림에 stars는 1바이트형 50바이트까지 확보, 그리고 DUP '*'를 50개를 넣는다. 복사하다.(duplication)

starswAndSpaces는 공백까지 포함하여 *가 들어간다. 즉, * * * * ......이렇게 12개 그리고 마지막 널 자리에 *가 

들어간다. 공간은 총 25바이트



아래 3개의 변수는 모두 144를 나타낸다. (참고로 word는 2바이트형)

어셈블리는 기본적으로 연산을 하고 실행되기 때문에 144를 적든, 12*12를 적은 똑같다.


QWORD는 8바이트.

TBYTE는 10바이트.





opcode= 기계어

number of bytes =바이트 수

timing 386 = 386에서의 주기

tuming 486=486에서의 주기

timing pentium=펜티엄에서의 주기


예를 들어 2개의 코드를 예를 들어 보았다.


1. mov al, '/'


mnemonic이 mov,

operrand가 al, imm8은 8비트크기의 상수.

flags affected는 모름(죄송)

opcode는 B0,

바이트수 2바이트 (B0, '/'를 아스키코드로 변환하면, 2F가 된다. 그래서 B0 2F의 크기의 2바이트)

386에서는 어셈블리 실행속도가 2주기, 486과 펜티엄은 1주기가 된다. 속도가 훨씬 빠르다.


2. add eax, number1




operland의 eax는 레지스터 32, number1은 변수로서 memory location 32bit 4바이트가 된다. 

(원래는 eax, mem32를 찾으려 했으나, 없다. --, 없는 경우에 reg32, mem32를 찾아서 분석하자!!)

이 코드에서 잘보면, opcode까지는 별다른 이상이 없다. 그런데 바이트 수가 2+이다. 이것은 최소 2바이트 이상의

크기를 갖는다는 의미가 된다. 





먼저,


 []안에 ebx는 C언어로 따지면 포인터라고 

생각하면 된다. 

그러나 이 코드는 에러메시지를 출력한다.

그 이유는 mov [ebx]는 크기가 정해져 있지 않기 때문이다. 
포인터는 포인터인데 대체 얼마만큼의 크기의 주소를 가르키는지
모르기 때문이다. 따라서 다음과 같은 코드로 대체된다.

mov byte ptr [ebx], 0

이 코드는 1바이트크기의 주소를 가르켜 0의 값을 대입하라는 뜻이다.
char *ebx;
a=0;
ebx=&a;

*ebx=0;


단, 생략이 가능한 경우가 있다. 


add edx, [eax]의 코드가 정상적으로 실행되었다고 가정한다면, 그것은

operland 중의 edx의 크기가 이미 알고있거나 정해진코드이기 때문에 굳이

자료형과 ptr을 쓰지 않고 []만 쳐도 상관없다.























'어셈블리(assembly) > 어셈블리 이론' 카테고리의 다른 글

8월 19일 어셈블리 기초이론1.  (0) 2013.08.19
posted by 송시혁







posted by 송시혁




eax=(edx+ecx)-(ebx-eax);

를 어셈블리로 짠 소스.


add edx, ecx

sub ebx, eax

sub edx, ebx

mov eax, edx









posted by 송시혁

C코드





아래는 디스어셈블리 코드



mov 대입 '='의미. 

dword ptr [a]

dword= 4바이트.

[a]= 변수 'a'

ptr은 포인터와 비슷한 의미. 여기서는 a의 주소에 헥사값 64를 a에 대입한다.

64h =100의 헥사값. (100의 16진수)


add eax, 1

add는 '더한다'라는 명령어. 여기서는 eax에 1을 더한다는 의미. 즉, c코드로 생각하면, a=a+1

eax=eax+1이 되겠다.


++a와 a++은 어셈블리코드로 보면, 똑같다. 실제 컴파일 하는 속도는 ++a와 a++는 차이가 없다는 것을

알 수 있다.


a=b+a;

1. mov eax, dword ptr [b]= eax에 b를 대입하겠다는 의미. a와 b는 메모리 상에서 존재한다. 연산은 cpu만이

할 수 있으므로 먼저 cpu에서 메모리로부터 가져와서 임시저장공간 eax에 넣는다.


2. add eax, dword ptr[a]= eax와 a를 더한다. 당연히 cpu가 연산한다. (연산은 ALU 레지스터가 담당)

3. mov dword ptr[a], eax= eax는 지금 b+a를 연산한 결과값을 cpu가 지니고 있다. 이 결과값을 다시 메모리

    에 있는 'a'라는 변수가 있는 메모리 주소에 보낸다. 그래서 이 3번째 코드가 없다면, 연산은 하지만, 연산한 결과

값은 나타나지 않는다.

b=a;


mov eax, dword ptr[a]= 'a'라는 변수를 eax에 대입한다. 

mov dword ptr[b], eax= eax(a가 가지고 있는 값)를 메모리상에 존재하는 'b'에 대입한다.




posted by 송시혁
prev 1 2 next