728x90
Conditional Loop Instructions(조건부 루프 명령)
오늘은 조건부 loop에 대해 다뤄보겠다.
loop 복습해보면 : ecx레지스터를 카운터로 사용해서 하나씩 줄여가면서 반복을 진행했다.
1. LOOPZ and LOOPE Instructions
- LOOPZ : 제로플래그가 세팅 되어있을떄만 Loop 명령처럼 동작하는 명령어이다.
- LOOPE : LOOPZ와 완전히 동일한 옵코드를 공유한다. (JUMPZ와 JUMPE가 같은것과 같은 개념이다.)
두 명령의 특징
- 이 명령은 Loop명령 처럼 작동하지만 Zero flag를 확인해서 0일때만 레이블로 점프를 진행합니다.
- ECX가 0이 아니고, 제로플래그가 세팅되어있을 때 루프 실행한다.
- 만약 제로플레그가 0이 아니거나 ECX 레지스터가 0이 아닌 경우에는 점프가 발생하지 않고 다음 명령어로 제어권이 넘어간다.
- 두 명령은 동일한 opcode를 공유한다.
- 이 명령은 status flag를 바꾸지 않는다.
2. LOOPNZ and LOOPNE Instructions
- loop not zero의 의미로 LOOPZ와 대응(반대)되는 명령어다.
- LOOPZ와 LOOPE가 있듯 LOOPNZ와 LOOPNE가 있고 역시 동일한 옵코드를 공유한다.
- zero 플래그가 clear 되어있을때 루프가 동작한다. (당연하게 ECX레지스터는 0이 아니어야 한다.)
예시) 첫 번째로 등장 하는 양수값 찾기
.data
array SWORD -3,-6,-1,-10,10,30,40,4
sentinel SWORD 0
sentinel SWORD 0
.code
mov esi, OFFSET array
mov ecx, LENGTHOF array
L1 : test WORD PTR [esi], 8000h ;해당 주소에 접근해서 2바이트 단위로 검사
pushfd
add esi, TYPE array
popfd
loopnz L1
jnz quit
sub esi,TYPE array
quit:
- test연산(이전에 Boolean 게시물에서 다룬 명령이다.)
- and를 수행하지만 대상 피연산자를 수정하지 않는다.
- 즉 overflow 및 캐리 플래그를 지운다.
- AND명령과 동일한 방식으로, sign, zero, parity flag를 지운다.
- 2개의 피연산자간의 and연산의 결과로 설정이 된다는 것이다.(다만 변경은 안됨)
- test WORD PTR [esi], 8000h (msb가 0인지 1인지 판단)
- 들어간 숫자가 음수인지 양수인지 판단!!
- PTR연산자 (이전 6강에서 데이터 관련 연산자 게시물에서 다뤘다.)
- PTR연산자는 원래 사이즈를 overide해주는 역활이다.
- 즉 이 코드에서는 WORD라고 가정하고 esi의 레지스터에 접근하는 것이다.
[]
: indirect opeand(역시 이것도 6강 게시물에서 다뤘다.)- 이건 주소값으로 esi를 사용하고 있기 때문에 그곳을 역참조 하는 것이다.
- 즉 이 코드에서는 array의 첫번째 위치를 참조하는 것이라고 보면된다.
add esi, TYPE array
: esi를 늘려준다.(다음 값으로 이동)- TYPE는 sizeof연산자랑 비슷하다.(6강에 다뤘음)
pushfd
: 플레그레지스터에 있는 값을 스택에 넣어두겠다.- 즉 모든 플래그를 보존할 때 사용한다. (7강)
popfd
: 다시 이전의 결과 플래그를 복원한다.
jnz quit
: 전부 다 돌았는데도 음수를 못 찾았다면 빠져나가라.- jz : zero flag가 세팅되어 있는 경우(ZF = 1) 해당 레이블로 점프
- jnz는 zero flag가 세팅되어 있지 않은 경우(ZF = 0) 점프 명령이 수행된다.
sub esi,TYPE array
add랑 같은 느낌으로 배열하나 포인터를 하나 빼준다고 생각하기.
전체 흐름 요약
- 2바이트 단위로 esi를 검사하는데 esi에는 array의 시작점이 있다. MSB를 계속해서 검사하는데 AND연산을 하면 MSB가 1이어야만 zero flag가 세팅이 안된다. 즉 음수여야만 세팅이 안되는 것이다.
- 만약 제로플래그가 세팅이 안되었다면 esi만큼 계속해서 루프를 돌 것이고, 제로 플래그가 세팅이 되는 순간(양수가 있는 순간) quit으로 나갈 것이다.
- 하지만 루프가 그냥 종료가 됐다면 양수는 없는 것 이기 때문에 늘린 esi를 다시 하나 줄여줘서 이상한 곳을 참조하는게 아닌 마지막 value를 가리키게 만들었다.
Conditional structure
조건부 구조
- 서로 다른 논리 분기 사이에서 선택을 실행하는 하나 이상의 조건부 표현식
- 각 브랜치는 서로 다른 명령어 시퀀스를 실행한다.
- 즉 쉽게 생각해서 if else를 생각해봅시다.
1. Block-Structured IF Statements
high level 언어
- if문 이라고 생각하면 된다.
어셈블리 언어
- CPU 상태 플래그중 하나가 영향을 받는 방식으로 bool-expression을 평가한다.
- 관련 cpu상태 플래그의 값에 따라서 두개의 문 목록으로 제어권을 이전하는 일련의 점프를 구성한다.
예시 1
- high level 버전 코드
if (op1 == op2) { x = op1=op2; }
- CMP명령을 사용해서 if문 사용하는 어셈블리 코드
mov eax, op1 cmp eax, op2 jne L1 mov X,1 mov Y,2 L1 :
- cmp연산(8강 내용)
- 주어진 피연산자를 빼기 해보는 연산자 이지만 실제로 빼기를 하는건 아니고 비교만 하는 연산자 이다.
- op1과 op2를 비교해서 같지 않으면 L1으로 가서 명령을 실행하고 만약 0이라면 아래 명령을 실행한다.
- cmp연산(8강 내용)
- JE를 사용해서 == 연산자를 구현하는 방법 (코드가 덜 간결하다.)
mov eax, op1 cmp eax, op2 je L1 jmp L2 L1 : mov X,1 mov Y,2 L2 :
- 이건 다들 이해 할꺼라고 믿습니당. 만약 모르시면 제 게시물에서 모르는 명령을 검색해보면서 찾아가시죠.
- 둘 다 결과는 똑같다 다만 명령어의 갯수가 다르다.
- 퍼포먼스가 위에 것이 더 좋다.
예시 2
- high level 언어 버전
clusterSize = 8192; if terrabytes < 16 clusterSize = 4096;
- 어셈블리 언어 버전
mov clusterSize, 8192 cmp terrabytes, 16 jae next mov clusterSize,4096 next :
- jae (8-2강)
- left ≥ right 일 때 점프하는 명령어
- 즉 terrabyte가 16보다 같거나 큰 경우 jump를 하고 아닌경우 4096을 넣은 코드
- jae (8-2강)
Jump를 두번하는 것을 방지하기 위해서 컨디션을 반대로 뒤집어 준 것이다.(jB가 아니라 jae를 해줬기 때문)
어느 경우 컨디션의 조건을 뒤집어 주어야하는가?
- If 만 있고 else는 없는 경우!!!
예시 3
- high level 언어 버전
if (op1 > op2) call Routine1; else call Routine2; endif
- 어셈블리 언어 버전
mov eax,op1 cmp eax,op2 jg A1 call Routine2 jmp A2 A1 : call Routine1 A2 :
- 변수를 비교할 때 레지스터에 넣고 cmp하는 것 잊지 맙시다.
- 이 코드 보면 int 대충 4바이트라고 예측해볼 수 있다.
- signed로 취급하고 있다.(eax에 넣는 행동과 jg는 signed comparisons중 하나이기 때문)
- jg : left > right 인 경우 jump하는 명령
- 교수님의 한마디 : 이거 되게 간단한데..! 직접 짜라고 하면 어려울 수 있어요~ 연습 해보시는걸 추천합니다.(if를 써봤을때 패턴들을 연습하시길 추천합니다.)
White Box Testing (방법론)
- whiteBox testing에 대한 설명은 무시하셔도 됩니다.!!! (각 조건에 따라서 옳을 떄 옳지 않을때 등 truth table을 이용하는 것)
- 그냥 중첩 if문 코드를 볼 때 어떻게 구현되는지 보고가세요!
- high level 언어
- 어셈블리 버전
- 이 코드를 보면 시작 조건을 먼저 수행하고 내부 if else를 수행한다.
- 딱히 어렵지는 않은 예시이다.
2. Compound Expressions
Logical AND Operator (논리연산자 AND)
원래 high level에서는 Short-Circuit Evaluation(회로 단락 평가)를 고려해서 작동한다.
- Short-Circuit Evaluation 생각해야하는 이유
if ((A>B) AND (++c>D))
이렇게 되면 다 실행결과가 다르기 때문 생각해보자
- high level언어
- 어셈블리 코드
- ja를 썻기 때문에 unsigned라고 예상할 수 있다.
- 만약 signed였다면 jg를 했을 것이다.
- 개선 어셈블리 코드 (if 문에서는 뒤집기)
Logical OR Operator
- high level언어
- 어셈블리 코드 (이것도 두번째 표현에는 jbe사용)
3. WHILE Loops
- high level언어
- 어셈블리 코드
- while문은 loop가 쓰였다고 착각하기 쉬우나 조건을 사용했기 때문에 loop가 아니다.
- 조건이 참이 되면 루프 조건을 반전 시켜서 종료 조건으로 이동하는 것이 편리하다.
- 이 코드는 eax로 val1을 이동 시키고 eax와 val2를 비교해서 계속해서 값을 늘려가며 비교한다.
- 그리고 끝날 때 eax에 있는 값을 var1으로 넣어준다.
- 이렇게 하는 이유는 cmp연산자의 피연산자의 조합이 메모리 메모리는 안되고 레지스터가 필요하기 때문이다.
- jnl을 보면 이게 signed임을 알 수가 있다.
3 -1 while문 안에 if중첩 예시
50보다 큰 값들을 합산하는 예시코드
- high level언어
int array[] = {10,60,20,33,72,89,45,65,72,18}; int sample = 50; int ArraySize = sizeof array / sizeof sample; int index = 0; int sum = 0; while (index < ArraySize) { if (array[index] > sample) { sum += array[index]; } index++; }
- 어셈블리 코드
.data sum DWORD 0 sample DWORD 50 array DWORD 10,60,20,33,72,89,45,65,72,18 ArraySize = ($ - array) / TYPE array .code main PROC mov eax,0 ;sum mov edx,sample mov esi,0 ;index mov ecx,ArraySize L1: cmp esi, ecx ;if esi < ecx jl L2 jmp L5 L2: cmp array[esi*4], edx ; if array[esi] > edx jg L3 jmp L4 L3: add eax,array[esi*4] L4: inc esi jmp L1 L5: mov sum,eax
- ArraySize 는 변수? 아니다!! 메크로다(Symbolic Constants -기호상수)
- $ : 현재 위치 주소
- $ - Array : Array 의 크기 (이걸 type으로 나누니까 갯수가 된다.) (5강 기호상수에 있습니다 복습!!)
- cmp할 때 doubleWord PTR안 붙힌 이유 edx의 크기를 알고 있기 때문에.
4. Table-Driven Selection
- Look-up table을 만들어서 Data segment에 table을 만들어두고
- 이 코드에서 Process가 나오는데 그건 Processor이름(주소) 이다.
예시) 다음은 단일 문자 조회값과 프로시저의 주소가 포함된 태이블의 일부이다.
.data
CaseTable BYTE 'A'
DWORD Process_A
BYTE 'B'
DWORD Process_B
(etc...)
- Process_A, Process_B, Process_C, Process_D가 각각 주소 120h, 130h, 140h, 150h에 위치한다고 가정합니다
프로그램 설명
- 사용자가 키보드에서 문자를 입력한다.
- 루프를 사용해서 문자를 look-up table의 각 항목과 비교한다.
- 첫번째 일치 항목이 발견되면 조회값 바로 뒤에 저장된 프로시저 오프셋에 대한 호출이 발생한다.
- 각 프로시저는 루프 중에 표시되는 다른 문자열의 오프셋을 사용해서 EDX를 로드한다.
INCLUDE Irvine32.inc
.data
CaseTable BYTE 'A'
DWORD Process_A
EntrySize = ($ - CaseTable)
BYTE 'B'
DWORD Process_B
BYTE 'C'
DWORD Process_C
BYTE 'D'
DWORD Process_D
;EntrySize = 상수(Symbolic Constants) (숫자 5)
NumberOfEntries = ($ - CaseTable) / EntrySize
prompt BYTE "Press capital A,B,C,or D: " ,0
msgA BYTE "Process_A",0
msgB BYTE "Process_B",0
msgC BYTE "Process_C",0
msgD BYTE "Process_D",0
.code
main PROC
mov edx, OFFSET prompt ;ask your input
call WriteString
call ReadChar ;read character into AL
mov ebx,OFFSET CaseTable ;point EBX to the table
mov ecx,NumberOfEntries ;loop counter
L1:
cmp al,[ebx] ; match found?
jne L2 ; no : continue
call NEAR PTR [ebx + 1] ; yes : call the procedure
call WriteString ;display message
call Crlf ; "\n"
jmp L3 ;exit the search
L2:
add ebx,EntrySize
loop L1
L3:
exit
main ENDP
Process_A PROC
mov edx, OFFEST msgA
ret
Process_A ENDP
Process_B PROC
mov edx, OFFEST msgB
ret
Process_B ENDP
Process_C PROC
mov edx, OFFEST msgC
ret
Process_C ENDP
Process_D PROC
mov edx, OFFEST msgD
ret
Process_D ENDP
END main
- 프로시저의 주소를 초기화!
- call NEAR PTR [ebx +1] 이해가 안 간다.
- real address 모드가 사용되던 시절에는 메모리의 주소가 세그먼트와 오프셋으로 분리되어 표현되었습니다. 그 때, 호출하는 부분과 동일 세그먼트상의 프로시저를 Near, 다른 세그먼트상의 프로시저를 Far Label로 구분하여 호출을 해야 했었고 NEAR PTR의 near는 이를 의미합니다.
- 하지만, Protected Mode로 오면서 Flat 메모리 모델을 사용하게 되었고 이제는 Near와 Far의 구분이 의미없어졌습니다. Legacy로서 남아있는 것이라 보시면 되고, call 명령에서 indirect한 프로시저 호출을 위해 사용되고 있다는 것 정도만 보시고 넘어가시면 됩니다.
장점
- 이 방법의 장점 : if else 구조로 떡칠하지 않아도 된다.
- 런타임에 태이블 재구성 가능,
- 코드량 줄일 수 있다.
- 많은 수의 비교를 처리할 수 있고
- JUMP 및 CALL명령보다 더 쉽게 수정할 수 있다.
단점
- 초기에 약간 오버헤드가 발생한다.(프로시저가 있기 때문에)
- 다양한 오버헤드 발생. : 인스트럭션 포인터를 다루는 행동 + 프로시저에 들어갈 때 작업하고 있던 행동 레지스터 복사 등등
Application: Finite-State Machines
- 유한상태 머신 (FSM)
- 이 기계에 꽂혀서 저자가 만든겁니다..!(굳이 이것이 이해가 안가도 넘어가도 됨.
- 여기서 설명하는 FSM(유한 상태 머신)(디지털 논리에 나왔을 것)에서 보면 된다.
- 이동 못하면 (터미널 상태)
A finite-state machine (FSM)
- FSM을 나타내는 그래프
- 노드: 프로그램 상태
- 엣지: 한 상태에서 다른 상태로 전환되는 지점
- 하나의 노드가 초기 상태로 지정됩니다.
- 하나 이상의 상태가 터미널 상태로 지정됩니다.
- 종료 상태는 프로그램이 오류를 발생시키지 않고 중지될 수 있는 상태를 나타냅니다.
- FSM은 방향성 그래프라고 하는 보다 일반적인 유형의 구조의 특정 인스턴스입니다.
1. Validating an Input String
입력 유효성 검사
- 입력 스트림을 읽는 프로그램은 종종 입력의 유효성을 검사해야한다.
- 입력 유효성 검사를 할 수가 있다.
- 위 그림은 문자열이 x로 시작하고z 로 끝나야만 하는 상태이다.
- 첫번째 문자와 마지막 문자 사이에는 0개 이상의 문자가 있을 수 있다.
2. Validating a Signed Integer
- 입력은 선택 사항인 선행 기호와 그 뒤에 오는 일련의 숫자로 구성됩니다.
- 다이어그램이 암시하는 최대 자릿수는 없습니다.
- 우리가 살펴볼 것!! Signed Int를 input으로 받는 상황을 가정한다.
- 우리는 FSM자체를 보는게 아니라 어떻게 코드로 짰는지를 봅시다.
상태 A코드
유산상태 머신 전체 코드
INCLUDE Irvine32.inc
ENTER_KEY = 13
.data
InvalidInputMsg BYTE "Invalid input", 13,10,0
.code
main PROC
call Clrscr ; 콘솔창 지우는 함수
StateA:
call Getnext ;read next char into AL
cmp al,'+' ;leading + sign?
je StateB ; go to State B
cmp al,'-' ;leading - sign?
je StateB ;go to State B
call IsDigit ;Zf = 1 if AL contains a digit
jz StateC ;go to State C
call DisplayErrorMsg ;invalid input found
jmp Quit
StateB:
call Getnext ;read next char into AL
call IsDigit ;Zf = 1 if AL contains a digit
jz StateC
call DisplayErrorMsg ;invaild input found
jmp Quit
StateC:
;숫자인지 확인, 엔터인지 확인 그게 아니면 에러메세지 후 끝
call Getnext ;read next char into AL
call IsDigit ;Zf = 1 if AL contains a digit
jz StateC
cmp al,ENTER_KEY ; Enter key pressed?
je Quit
call DisplayErrorMsg ;no :invalid input found
jmp Quit
Quit:
call Crlf
exit
main ENDP
;Read a char from statdard input and return AL contain charactor
Getnext PROC
call ReadChar ;input from keyboard
call WriteChar ;echo on screen
Getnext ENDP
;Display an error msg
DisplayErrorMsg PROC
push edx
mov edx,OFFSET InvalidInputMsg
call WriteString
pop edx
ret
DisplayErrorMsg ENDP
END main
- 짧은 복습 je와 jz : 둘은 완전 동일 한 명령이지만 비교를 할 때는 주로 je 0인지 아닌 지 확인 할 때는 jz를 쓰는게 더 직관적이다.
- ReadChar : 저자가 만든 프로시저 (내가 입력한게 바로 출력이 되는게 아니라 읽는것)
- WriteChar 스크린에 띄워준다. 해도 AL에는 내가 받은 게 들어있다.
- InvaildInputMsg 쪽을 보면 13 10은 윈도우에서 개행을 의미한다.
저자가 만든 IsDigit코드
- ‘0’은 아스키코드 0이 아니라 문자 0임…!
- 마지막 빠져나올 때 제로 플래그 세팅을 해줬다!
Uploaded by N2T
728x90
'CSE > system programing' 카테고리의 다른 글
[시스템 프로그래밍 10강] Shift and Rotate 명령 및 활용 (0) | 2023.05.16 |
---|---|
9-2강 Conditional Control Flow Directives (조건부 제어 흐름 지시어) (0) | 2023.05.15 |
[시스템 프로그래밍 8-2]Conditional Jumps (조건부 점프 명령) (0) | 2023.04.28 |
[시스템 프로그래밍 8-1]Boolean and Comparison Instructions (어셈블리어에서 불린 연산자) (0) | 2023.04.28 |
[시스템 프로그래밍 7-3] Irvine32 Library 정리 (0) | 2023.04.28 |