[Assembly] 04 (분기문, 반복문, 배열과 주소)

dev.kelvin·2024년 7월 6일

Assembly

목록 보기
4/5
post-thumbnail

1. 분기문

분기문이란 특정 조건에 따라서 코드 흐름을 제어하는 것을 의미한다
ex) 스킬 버튼을 눌렀는가? -> 눌렀으면 스킬 사용
ex) 던전 입장 버튼을 눌렀는가? -> 눌렀으면 던전 입장

어셈블리에서는 분기문은 간단하고 명확한 문법으로 되어있지 않다

어셈블리에서 조건을 체크할 때는 CMP dst, src로 한다 (CMP는 compare)
나눗셈과 마찬가지로 비교한 결과는 특정 레지스터에 저장이 되어 있는데 그 레지스터가 바로 Flag Register이다

JMP [label] 시리즈들은 jump명령어이다, 지정한 label로 jump한다 (해당 코드로 이동한다는 의미)

JMP는 무조건 jump, JE는 JumpEquals로 같으면 jump, JNE 는 JumpNotEquals로 다르면 jump, JG는
JumpGreater로 크면 jump, JGE는 JumpGreaterEquals로 크거나 같으면 jump이다 이 이외에도 여러 조합으로 사용이 가능하다

그렇다면 두 숫자가 같으면 1, 아니면 0을 출력하는 코드를 제작해보자

    mov rax, 10
    mov rbx, 20
    
    cmp rax, rbx ;결과가 Flag Register에 저장    
    
    JE LABEL_EQUAL ;같을때 jump할 label생성
    
    mov rcx, 0
    jmp LABEL_EQUAL_END ;다르면 rcx에 0이 들어가고 Label_Equal이 순차적으로 실행되어 어차피 1이 들어가기 때문에 강제로 jump시킬 label
    
LABEL_EQUAL: 
    mov rcx, 1 ;숫자가 같으면 이 Label로 jump된다
    
LABEL_EQUAL_END: ;숫자가 다르면 이 Label로 강제 jump된다

    PRINT_HEX 1, rcx
    NEWLINE
    
    xor rax, rax
    ret

cmp rax, rbx로 비교한 결과는 flag register에 저장되고 JE, JMP로 비교결과를 보고 jump하여 코드를 분기한다, 이때 cmp에서 단순 숫자와도 비교가 가능하다 ex) cmp ah, 0

10,20은 다르기 때문에 결과값이 0이 나오게 된다

그렇다면 cmp에서 비교한 결과가 들어가는 flag register는 어떤 값이 들어갈까?

다음은 eflag 연산자를 설명하는 그림과 eflag register의 구조를 보여주는 사진이다

위의 코드를 값이 같게 만들고 출력한 결과에서 eflags register를 까보게 되면 다음과 같다

JE가 정상적으로 들어갔기 때문에 ZF=1이 잘 된 걸 확인할 수 있다

2. 반복문

반복문은 특정 조건을 만족하면 계속 반복해서 실행하는것을 의미한다
ex) Hello World를 10번 출력하려면 PRINT_STRING을 계속 쓰기엔 너무 비효율적이다 이를 편하게 하기 위해 반복문을 사용한다

다음은 Hello World를 10번 출력하는 반복 코드이다

    mov ecx, 10 ;반복횟수 카운팅을 위한 값, 보통 카운팅을 사용할 때는 ecx를 많이 사용한다
    
LABEL_LOOP:
    PRINT_STRING msg
    NEWLINE
    dec ecx ;1을 줄이는 명령 -> sub ecx, 1과 동일하다
    
    cmp ecx, 0 ;반복 카운팅이 0인지 확인
    jne LABEL_LOOP ;0이 아니면 LABEL_LOOP로 다시 돌아가기
                
    xor rax, rax
    ret
 
section .data
    msg db 'Hello World', 0x00

dec [레지스터]는 값을 1 줄이는 명령이다, sub [레지스터],[값]과 동일하다

결과값은 Hello World가 10번 출력된다

이러한 반복을 할 수 있게 해주는 명령어가 존재한다

loop [label]로 반복문 명령어를 사용할 수 있다

ecx에 반복횟수가 들어가고 loop할때마다 ecx값이 1 감소한다, ecx가 0이아니면 라벨로 jump한다
이때 ecx에는 반복횟수가 들어가기 때문에 중간에 ecx값을 수정한다면 infinite loop가 발생할 수 있다

C++에서도 for,while 반복문은 내부적으로 이런 처리가 들어가있다

loop를 사용하여 반복을 해보자

mov ecx, 100
    xor ebx, ebx
LABEL_LOOP_SUM:
    add ebx, ecx
    loop LABEL_LOOP_SUM

ecx카운딩 넘버를 셋팅해주고 loop를 이용하여 ecx가 0이 될 때 까지 LABEL로 jump하여 반복을 한다

3. 배열과 주소

배열이란 동일한 타입의 데이터 묶음을 의미한다

배열을 구성하는 각 값을 배열 요소 (element)라고 하며 배열의 위치를 가리키는 숫자는 인덱스 (index)라고 한다

[변수명] times [갯수][타입크기] [초기값]으로 배열 선언 및 초기화가 가능하다

    b times 5 dw 1 ;데이터는 5개고 타입의 크기는 word (2byte)이며 초기값은 1이다를 의미한다, 2byte가 5개 -> 10byte

그 외에 기존의 변수 선언, .bss변수 선언도 여러개를 넣어 배열표현이 가능하다

section .data
    msg db 'Hello World', 0x00
    
    a db 0x01, 0x02, 0x03, 0x04 ;이 표현도 배열의 표현이다, 1byte가 4개 -> 4byte

section .bss
    num resb 10 ;초기값이 없는 데이터 10개 -> 이 표현도 배열의 표현이다

위의 배열에 데이터가 어떻게 들어갔나 메모리를 보게 되면 다음과 같다

a는 1byte로 4개를 넣었기 때문에 0x1, 0x2, 0x3...해서 잘 들어갔고 b는 2byte크기로 넣었기 때문에 (0x1,0x0 == 0x0001)묶음으로 들어간 걸 확인할 수 있다 (Little Endian)

이때 mov rax, a를 하게 되면 a의 값이 아닌 a의 주소가 들어가고 실제 값을 얻기 위해서는 [a]로 사용해야 한다는 것을 이전에 다룬적이 있다

    mov rax, a ;a의 실제 값이 아니라 a의 주소값이 들어간다
    PRINT_HEX 1, [a]   

이 코드의 결과는 1이 나오게 된다 (0x01)

그렇다면 a배열의 0번째 값이 출력이 된 것인데 다른 값들은 어떻게 가져올 수 있을까?

단순하게 원하는 만큼 +값을 해주면 된다, [시작주소 + 원하는 index]

    PRINT_HEX 1, [a + 1]   
    PRINT_HEX 1, [a + 2]   

결과는 2, 3이 나오게 된다

배열의 이름은 배열의 시작 주소이기 때문에 [배열]은 첫번째 값이 나오게 되는 것이다

반복문을 이용해서 배열의 index를 순회해보자

    xor ecx, ecx
    
LABEL_LOOP:
    PRINT_HEX 1, [a + ecx]
    NEWLINE
    inc ecx
    cmp ecx, 4
    jne LABEL_LOOP

결과값은 1,2,3,4가 나오게 된다

그렇다면 b times 5 dw 1이 값도 같은 방식으로 반복문을 통해 순회가 가능할까?

LABEL_LOOP:
    PRINT_HEX 2, [b + ecx] ;dw기 때문에 2byte
    NEWLINE
    inc ecx
    cmp ecx, 4
    jne LABEL_LOOP

이 코드의 결과값은 다음과 같다

분명히 같은 방식이라면 1이 5개 나와야 하는데 왜 1,100,1,100이 나올까?

b의 메모리 구조를 보면 (0x1, 0x0)으로 배열 요소들이 들어있다 하지만 ecs를 더해주었기 때문에 1이 증가하게 되는데 이때 b는 2byte 배열요소들로 되어 있기때문에 1칸만 이동하여 다른 값이 나오게 되는 것이다 이러한 현상은 C++에서는 자동으로 타입의 크기를 맞춰 더해주기 때문에 발생하지 않는다

따라서 이를 방지하기 위해서 다음과 같은 코드를 작성해야 한다

LABEL_LOOP:
    PRINT_HEX 2, [b + ecx*2] ;dw기 때문에 2byte
    NEWLINE
    inc ecx
    cmp ecx, 4
    jne LABEL_LOOP

결과값이 1,1,1,1로 잘 나오는걸 확인할 수 있다

profile
GameDeveloper🎮 Dev C++, DataStructure, Algorithm, UE5, Assembly🛠, Git/Perforce🌏

0개의 댓글