개요
- 우리가 지금껏 쓰던 프로시저(PROC)는 그냥 코드의 위치를 변경하는 것 뿐이었다.
- 사실 high level 언어인 C나 CPP나 모두 함수를 할 때 processor를 쓰는것이다.
- 하지만 고급 언어에서는 함수로 인자를 넘겨주는 것을 볼 수가 있다. 오늘은 어떻게 인자를 함수에 넘겨주는가에 대해서 배울 것이다.
- 물론 그 방법을 이해하기 앞서 필요한 개념들을 먼저 설명하도록 하겠다.
Stack Frames
1. Stack Parameters
stack Frame이란 간단히 말해 다음의 것들을 저장하기 위해서 스택의 영역이다.
- 함수에게 전달된 인수(인자)가 스택에 저장된다.
- 서브루틴의 반환주소(리턴 주소)
- 로컬 변수(함수 안에서 사용하는 로컬 변수로 스택에서 지역변수에 대한 메모리 공간이 할당 되는 것이다.)
- 저장된 레지스터 (이건 옵션적인 것인데, 프로시저 안에서 레지스터를 사용하는 경우 프로시저를 호출하기 전의 레지스터 상태를 푸쉬해준다.
- 이런 정보들을 저장해주는 것을 Stack Frame이라고 부른다.
스택 프레임 생성 과정 (단계별)
함수를 우리가 구현해보는 것이라고 생각하면된다.
- 중요 개념!! stack에 뭔가를 push하면 주소값이 감소한다! 스택은 주소를 높은곳에서 낮은 곳으로 올라가는 형식을 취하기 때문이다.
- 함수(프로지서)에 전달되는 인자들이 있으면 스택에 집어 넣어준다.
- 해당 프로시저가 불린다. → 당연하게도 서브루틴의 리턴 주소가 스택에 들어간다. (프로지서가 실행 후에 다시 원래 코드의 진행 방향으로 돌아오기 위해서 있는 것으로 당연하다고 생각하면 된다.)
- 서브루틴(프로시저)가 호출이 되서 실행되면, 일단 EBP를 스택에 집어 넣어준다.
- EBP는 Base pointer이고
- ESP는 스택포인터로 TOP을 가리기고 있다.
- 스택에 뭔가가 푸쉬가 되면 ESP가 감소가 된다.(사실 푸쉬를 하는것인데 감소 시켜주는게 푸쉬한 것이다.) (그림상으로 ESP가 올라간다는 의미)
- 다음으로 EBP가 ESP와 동일하게 설정된다.
- 이것이 무슨의미인가? 함수안에 들어왔기 때문에 스택의 바닥을 다시 설정해주는 것이다.
- 만약 함수 안에서 지역 변수를 만들게 되면, 다시 ESP가 그만큼 감소하게 된다.(ESP가 그림상으로 위로 올라간다)
- 함수 안에서 특정 레지스터의 값을 바꾸는 경우 레지스터를 스택에 푸쉬해준다.
- 스택 프레임의 구조는 메모리 모델과 인수 전달 규칙에 영향을 받는다.
- 우리는 메모리 Model까지 신경 쓸 필요는 없다.
- 왜냐하면 우리는 공부하는게 32비트 x86을 기준으로 하는데 32비트 메모리의 경우 protected모드를 사용하고, 무조건 flat모델만을 쓰기 때문에 무시해도 된다.
- 다만 인수전달 규칙은 조금 다를 수 있다. (이는 조금 뒤에서 다루겠다.)
- 그동안 우리는 stdcall 컨벤션을 사용했지만, 그것이 조금 다른 종류를 사용할 수가 있다.
- 우리가 만약 윈도우 32비트 API에서 함수를 사용하려면 스택에 인수를 전달 해줘야 한다.
- 이걸 언제 써봤는가? ExitProcess 를 사용해봤다.
invoke ExitProcess,0
- ExitProcess가 윈도우 API가 제공하는 함수이다.
2. Disadvantages of Register Parameters
레지스터 매개변수의 단점!!!
- 우리는 그동안 인자를 전달할 때 레지스터에 그 값을 집어 넣어주고 프로시저를 호출 했었다. (Irvine32에서 그랬듯)
- 우리는 폰노이만 컴퓨터에서 bottleneck이 Bus인 것을 알고 있다.
- 그렇기 때문에 당연하게도 레지스터에 넘겨서 주는 방식은 효율적이다.
- 이렇게 파라미터를 레지스터에 넣어서 전달하겠다는 것을 우리는 Fastcall 방식이라고 부른다.
- 다만 여기서 문제점이 있다
- 레지스터의 갯수는 한정적이기 때문에 프로시저 내부에서 레지스터를 사용하려면 stack에 이전값을 push해주고 사용을 해야만 했다.
- 우리가 레지스터에 전달해주기 위해서 다른 레지스터의 값을 stack에 푸쉬하고 pop을 해야한다는 것이 모순적이다.
- 이는 우리가 빠르게 하려고 레지스터에 인자를 집어 넣은 행동을 상쇄시켜버린다.
- 또한 우리가 push와 pop을 할 때 순서가 일치하도록 매우 주의해야 한다는 단점도 생겼다..
- 이 단점을 극복하기 위해서 레지스터에 넣는게 아니라 매개변수를 스택에 넣는 방식으로 변화했다.
매개변수를 레지스터에 push하는 방법과 stack에 푸쉬하는 방법 사이 비교
- 스택에 어떤 순서로 꺼내면 어떤 변수가 나올지에 대해서 미리 합의를 했다.(마지막 매개변수를 가장 먼저 집어넣음..!)
- 따라서 프로그래머가 실수 할 여지가 줄어들고 깔끔해졌다.
- 이렇게 서브루틴 호출 중에 스택에 푸쉬되는 인자의 유형은 2가지 이다.
- Value인자(변수 및 상수의 값)
- referance 인자(변수의 주소)
스택 매개변수를 전달하는 방법 2가지
- 주의할 점!! :: 여기 아래 stack 그림은 위에서 그린 stack을 거꾸로 그려 놓은것 이라고 보면된다.
1. Value를 전달하는 것
- Passing by Value 방법
- 이 방법은 값의 복사본을 스택에 푸쉬해주는 방법이다.
- 두번째 인자가 먼저 들어간다. (C와 C++에서는 그렇게 약속이 되어있다.)
- 이런 약속을 기반으로 C언어의 코드를 어셈블리어로 변경해서 해석하는 것이다.
- 위 코드는 C++에서는 :
int sum = AddTwo(val1, val2)
2. Reference를 전달하는 것
- Passing by Reference
- 이건 전달 되는 인수가 객체의 주소(OFFSET)이다.
- 여기서 레퍼런스는 CPP에서 레퍼런스와 살짝 다르다.
- 여기서는 포인터에 더 가까운 의미이다.
- 보면 주소값을 push 해주는 것을 볼 수가 있다.
- 이 코드는 C++에서는
Swap(&val,&val2);
이다.
- Passing Arrays
- 우리는 배열을 전달할 때 배열의 주소를 전달해준다.(상식)
.data array DWORD 50 DUP(?) .code push OFFSET array call ArrayFill
- 만약 여기서 주소를 전달하지 않고 통째로 집어 넣으면..! 배열의 원소를 하나하나 다 스택에 집어 넣어야 한다. (공간, 시간적 낭비)
3. Accessing Stack Parameters
지금까지는 스택에 집어넣는 방법이었다면 이제 어떻게 프로시저(함수)가 전달받은 인자에 접근하는지 살펴보자.
- 프로시저가 실행되면(Fuction 안에서) EBP레지스터가 PUSH된다.(복귀 주소를 알리는 행동이다.)
- 이제 새로운 바닥설정. EBP를 ESP로 올린다.
- 함수 호출이 끝나면 EBP레지스터를 복구한다. (스택에 넣어준 EBP를 다시 EBP에 올려준다.)
- 다시 ESP레지스터도 resturn address로 이동한다.
- 이걸 왜 해주냐?? 프로시저 호출 되기 전에도 stack을 사용하고 있었기 때문에!
CPP함수와 PROC(프로시저)비교
- 위와 같은 스택의 구조를 볼 수가 있다.
- 스택은 다시 말하지만 처음의 그림에 거꾸로 뒤집혀 있다고 보면 된다 .(스택은 push하면 주소값이 작아진다.)
위의 코드 간단한 흐름으로 설명
- Y가 들어간다
- X가 들어간다.
- return address들어간다.
- ESP들어간다.
- EBP를 ESP로 올린다.
- 이제 EBP+8의 의미 : 첫번째 인자.
- 두번째 인자 : EBP + 12 : 두번째 인자.
- return 되면 EIP로 이동한다.(intruction pointer)
- 그러면 함수 호출 전으로 돌아간다.
결국 인자 접근은 stack을 이용해서 간접적인 방식으로 참조해서 접근 하는 것이다.
- 아래 코드는 인자를 간접적 주소를 표현하는 것을 매크로로 변경해서 더 보기 쉽게 만든 코드이다.
y_param EQU [ebp + 12]
x_param EQU [ebp + 8]
AddTwo PROC
push ebp
mov ebp, esp
mov eax, y_param
add eax, x_param
pop ebp
ret
AddTwo ENDP
4. 32-Bit Calling Conventions
- 윈도우 환경에서 32비트 프로그램할 때 가장 일반적으로 사용하는 2가지 calling convention이 존재한다.
1. The C Calling Convention
Example1 PROC
push 6
push 5
call AddTwo
add esp,8 ; remove argument from the stack
ret
Example1 ENDP
- 이제 우리는 인자를 4바이트를 넣어준 것이고, 그냥 ESP를 8만큼 밀어주게 되면 이제 비로소, 완전히 인자를 넘겨주기 전에 상태로 돌아오는 것이다.
- 저기서 8의 의미는 스택에 넣어준 녀석이 크기를 8이라고 생각하는 것이다.
- 이게 C언어에서 사용하는 Calling convention이다.
- C calling Convention 의 경우는 우리가 직접 넣어준 매개변수를 직접 pop해야하는 것을 의미한다.(ESP레지스터의 주소를 매개변수의 크기만큼 원래대로 이동)
2. STDCALL Calling Convention
AddTwo PROC
push ebp
mov ebp, esp
mov eax,[ebp + 12]
add eax,[ebp + 8]
pop ebp
ret 8
AddTwo ENDP
- C스타일은 ESP를 함수 밖에서 증가 시켜주는 것이고, STDCALL 스타일은 함수 내부에서 ret을 해주는 것이다.
- 이건 알아서 처리를 해준다.(내부적으로는 같은 일을 하고 있다.)
- 쉽게 말해서 ret 8을 해주면 아까 전에 add esp,8 을 직접 하지 않아도 알아서 해준다는 의미이다.
- 즉 여기서 ret은 ESP + ret리턴값으로 스택에서 사용하는 매개변수의 크기만큼 즉 함수의 인자의 크기만큼 넣어주면 된다.
- 물론 C와 마찬가지로 STDCALL은 인자를 역순으로 스택에 밀어 넣는다.
- RET명령에 매개변수를 포함함으로써 STDCALL은 하위루틴 호출을 위해서 생성되는 코드의 양을 줄이고 호출 프로그램이 stack을 정리하는 것을 잊지 않게 할 수 있다.
Saving and Restoring Registers
이 파트는 우리가 특정 레지스터의 값을 변경했을 때 즉 선택적으로 발생하는 상황이다.
MySub PROC
push ebp ;save base pointer
mov ebp, esp ;base of stack frame (스택 프레임의 base를 위로 올림)
push ecx
push edx ;save EDX
mov eax,[ebp + 8] ;get the stack parameter 스택에
.
.
.
pop edx ;restore saved resigsters
pop ecx ;
pop ebp ;restore base pointer
ret ;clean up the stack
MySub ENDP
- 당연하게 EBP, ESP를 먼저 push하고 쓰는것이다.
5. Local Variables
이제 함수내부 즉 프로시저 내부에서 사용되는 지역변수에 대해서 알아보자.
- 지역변수 접근도 EBP-4, EBP-8이렇게 접근도 가능하다.
MySub PROC push ebp mov ebp,esp sub esp,8 ;create locals mov DWORD PTR [ebp-4],10 ;X mov DWORD PTR [ebp-8],20 ;Y mov esp,ebp ;remove locals from stack pop ebp ret MySub ENDP
- 코드를 보면
esp,ebp
부분이 있는데 이는 pop해주는 것과 같다. (왜냐면 stack의 top을 현재 bottom으로 주소값을 변경한 것이기 때문에
- 지역변수들은 스택에 들어가기 때문에 어셈블타임에 default값들이 지정될 수 었다.
- 따라서 지역변수는 runtime에 초기화가 가능하고 전역변수는 어셈블 타임에 초기화가 가능하다.
6. Reference Parameters
참조 매개변수
배열을 채우는 예제
.data
count = 100
array WORD count DUP(?)
.code
push OFFSET array
push count
call ArrayFill
ArrayFill PROC
push ebp
mov ebp, esp
pushad ;save registers
mov esi,[ebp+12];offset of array
mov ecx,[ebp+8] ;array length
cmp ecx,0. ;ECX == 0?
je L2 ;yes : skip over loop
L1 :
mov eax, 10000h ;get random 0 ~ FFFFh
call RandomRange;form the link libray
mov [esi],ax ;insert value in array
add esi,TYPE WORD;move to next element
loop L1
L2 : popad ;restore register
pop ebp
ret 8 ;clean up the stack
ArrayFill ENDP
- 포인터를 전달해준 것 과 같다.
- 이 예제는 배열에 값을 채워주는 예제이다.
- 2바이트가 100개 있고,
- 배열의 주소를 push해주고,
- 매개변수 푸쉬 해주고
- 함수 호출하면 자동으로 ret addr들어가는 거 잊지 말자.
- 프로시저에서 레지스터를 쭉 저장해주면 스택에 쭉 푸쉬가 또 되는 것이다.
- ecx에 100을 넣어주는 걸 보면 100번 돌 것을 예상 가능
- esi를 보면, 배열의 위치가 있으님까 랜덤한 값이 들어가는 것이다.
- 이제 빠져나온다.
- 원래대로 esp가 복구되서
- 이건 standcall 방식인 것을 알 수가 있다.(ret 8이기 때문)
7. LEA (Load Effective Address) Instruction
- LEA명령은 간접 피연산자의 주소를 리턴해준다.
- 이전에 OFFSET을 썻었는데 OFFSET은 런타임에서 달라지는 주소를 리턴해주는 게 아니다.
- OFFSET은 어셈블 타임에 바뀌는 녀석의 주소를 리턴해주는 것이다.
- 지금까지 우리가 변수를 저장했던 Data segment에 있는 녀석들은 전역변수라고 생각해 볼 수 있다.
- 여러분이 작성한 프로그램을 뜯어보면 code와 data로 나뉘어서 있는 것을 볼 수가 있다.
- 전역변수들은 프로그램에 일부로 들어가게 되는 것이다. (전역변수가 크기가 크면 프로그램의 크기가 커진다
- 프로그램을 실행하면 운영체제가 세그먼트를 할당해준다.
- 그 세그먼트는 data code stack의 세그먼트로 쪼개진다.
- OFFSET은 주소라기보다는 얼마나 떨어져 있는가 즉 상대주소의 개념이다.
- 따라서 어셈블타임에 결정이 된다.
- 지역변수들은 스택에 들어가기 때문에 어셈블타임에 default값들이 지정될 수 없다.
- 그래서 우리가 런타임중에 알아야하는 주소를 offset명령어를 쓰면 오류가 발생한다.
mov esi,OFFSET [ebp-30]
은 에러가 발생한다.
- 그럴 때 쓰는게 LEA 명령이다.
cpp코드
void makeArray()
{
char myString[30];
for (int i=0; i < 30; i++)
myString[i] = '*';
}
어셈블리 코드
makeArray PROC
push ebp
mov ebp,esp
sub esp,32 ;myString is at EBP -30
lea esi,[ebp-30] ;load address of myString
mov ecx,30 ;loop counter
L1: mov BYTE PTR [esi], '*' ;fill noe position
inc esi ;move to next
loop L1 ;continue untill ECX =0
add esp,32 ;remove the array (restore ESP)
pop ebp
ret
makeArray ENDP
- ESP를 32빼줘서 공간 만들고
- esidp ebp-30 즉 첫번째 원소의 주소를 넣어주는 것이다.
- 여기서 왜 32만큼 공간을 할당했는지 의문이 생길 수 있는데, 단어의 경계가 4바이트 단위로 접근해야 하려고 그것을 맞춰주는 것이다.(X86프로세서의 특징 4바이트씩 읽음) 메모리는 조금 낭비되지만 속도에서 큰 이득이 있기 때문에 이런 방식을 사용한다.
8. ENTER and LEAVE Instructions
ENTER
- 스택에서 항상 하는 일을 더 간단하게 해주는 명령어이다.
- 로컬 변수를 위한 스택 공간을 확보하고 스택에 EBP를 Save해주는 것을 자동화 해주는 명령어이다.
- 즉 프로시저를 들어갈때마다 해야하는 행동을 간단하게 해주기 위해서 등장한 명령이다.
- 첫번째 매개변수는 예약할 스택의 공간의 바이트수를 지정하는 상수이다.(즉 지역변수의 크기) 항상 4의 배수로 반올림해서 ESP를 double Word 경계에 유지 시켜준다.
- 두번째 매개변수는 함수안에 몇번째 함수인가 (어휘중첩 수준이라고 하는데 함수안에 함수를 정의하는 언어에서는 중요하다고 한다.) (사실 우리가 쓰는 거의 대부분의 프로그램에서는 다 0으로 준다고 생각해도 된다.)
LEAVE
- 함수를 빠져나올 때 하는 일을 간단하게 만들어주는 명령어
- 시험문제는 enter의 사용법이 아니라 과연 이게 실제로 하는 게 무엇일까에 대한 걸 시험에 낼 수 있다.
- 이런 명령은 어셈블리어를 업으로 삼는 사람에 대해서 편의 기능을 제공하는 것이다.
- 이것도 enter할 때 ebp를 push 했듯 leave할 때는 원래대로 돌려준다. 즉 코드에서 보듯 esp를 ebp로 바꿔주고, ebp를 pop해주는 것을 볼 수가 있다.
- ebp에 스택의 최상위 값을 pop해주는 것이기 때문에, 처음 들어올때 push됐던 ebp의 위치로 ebp를 보내주는 것이다.
예제 Recursion
1. Recursively Calculating a Sum
- 아까 한 행동이 재귀를 사용해도 아무런 문제가 없는지를 보여준 예제이다.
- 예제 1부터 n까지의 합을 재귀로 구현한다.
- 우리가 레지스터에 값을 전달한다고 stack을 안 쓰는게 아니라 return주소가 stack에 들어갑니다.
예시코드
INCLUDE Irvine32.inc
.code
main PROC
mov ecx, 5 ;count = 5
mov eax, 0 ;holds the sum
call CalcSum ;calulate sum
L1: call WriteDec ;display EAX
call Crlf
exit
main ENDP
CalcSum PROC
cmp ecx,0 ;check counter value
jz L2. ;quit if zero
add eax,ecx ;otherwise, add to sum
dec ecx ;decrement counter
call CalcSum ;recursive call
L2 :ret
CalcSum ENDP
end Main
2. Calculating a Factorial
INCLUDE Irvine32.inc
.code
main PROC
push 5 ;calc 5! (5팩토리알)
call Factorial;calc factorial eax
call WriteDec ;display
call Crlf
exit
main ENDP
Factorial PROC
push ebp
mov ebp,esp
mov eax,[ebp+8] ;get n
cmp eax,0 ;n > 0?
ja L1 ;yes continue
mov eax,1 ;no : return 1 as the value of 0!
jmp L2 ;and return to the caller
L1 : dec eax
push eax ;Factorial(n-1)
call Factorial
ReturnFact:
mov ebx,[ebp+8] ;get n
mul ebx ;EDX:EAX = EAX *EBX
L2 : pop ebp
ret 4
Factorial ENDP
END main
- 이 코드는 stack을 사용해서 한 예제이다.
- 5팩토리얼을 구하는 예제
- 그림은 3팩토리얼이다. (5팩토리얼은 그림이 너무 길어지기 때문에)
- 이건 스택이 뒤집혀 있음을 알면 됩니다.
INVOKE, ADDR, PROC, and PROTO
1. INVOKE Directive
- 문법 :
INVOKE procedureName [, argumentList]
push TYPE array
push LENGTHOF array
push OFFSET array
call DumpArray
위 코드를 우리는 아래와 같이 바꿀 수 있다.
INVOKE DumpArray, OFFSET array, LENGTHOF array, TYPE array
- 우린 이미 exitprocess 할 때 이미 사용해봤다.
- 뒤에 오는 인자들은 생략이 가능하고 여러개가 들어갈 수도 있다.
- 뒤에 들어오는 ,0은 stack에 0을 푸쉬해주고 함수를 실행한다는 의미이다.
- 아 이건 거꾸로 푸쉬하는게 아니라 정순으로 인자를 넣어줍니다.(이전에는 거꾸로 인자들을 push했지만 이건 순서대로 인자를 넣어줘도 된다.)
- 한가지 특이사항!! : 스택에 집어넣기 위해서 32비트에 맞춰서 집어넣는 특성이 있다.
- 알아서 4바이트로 확장해서 집어넣어준다. (4바이트 단위로 읽기 때문)
- 따라서 이 녀석은 레지스터를 사용하고 그 과정에서 EAX와 EDX를 사용하기 때문에 프로시저 호출 전에 저장해두는 것을 추천합니다.
2. ADDR Operator
- 이 녀석은 OFFESET과 똑같이 작동하는데 INVOKE 명령을 위해서 사용하는 녀석이라고 보면된다.
에러 예시
INVOKE FillArray, ADDR myArray
INVOKE mySub, ADDR [ebp + 12] ;error
mov esi, ADDR myArray ;error
ADDR 사용예시
.data
Array DWORD 20 DUP(?)
.code
...
INVOKE Swap,
ADDR Array,
ADDR [Array+4]
- 사실 이것과 같다
push OFFSET Array +4 push OFFSET Array call Swap
3. PROC Directive
- 그동안 그냥 PROC를 사용해왔지만 사실 이건 더 많은 기능이 있다.
- PROC 구문 :
label PROC [attributes] [USER reglist], parameter_list
parameter_list
- PROC 지시문을 사용하면 쉼표로 구분된 명명된 매개변수 목록으로 프로시저를 선언할 수 있다.
- 코드에서 매개변수를 [ebp +8] 과 같이 계산된 스택 오프셋이 아닌 이름으로 참조할 수 있다.
과거 버전
AddTwo PROC,
push ebp
mov ebp, esp
mov eax, dword ptr [ebp + 8]
add eax, dword ptr [ebp+0Ch]
leave
ret 8
AddTwo ENDP
지시문을 사용해서 더 간단하게 표현하는 방법
AddTwo PROC,
val1:DWORD,
val2:DWORD,
mov eax, val1
add eax, val2
ret
AddTwo ENDP
- 인자 2개를 넣어주는 것이다. 지역변수 아니다.
- 즉 val1과 val2를 이렇게 쓰겠다는 의미다. (형식 지정)
- 그동안은 접근방식이 ebp에서 몇만큼 더해주서 push된 지역변수를 사용했지만 그것보다 훨씬 더 심플하게 사용할 수 있다.
- 교수님께서 Directive의 사용법 보다는 Directive를 쓰는게 아니라 직접 이 코드를 해석하는 걸 시험에 낼 수 있는 것이다.
- leave도 코드로 직접 만들어보는 것도 좋아요!
프로시저에서 전달 프로토콜을 직접 설정 할 수도 있다.
Example1 PROC c,
parm1:DWORD, parm2:DWORD
Example1 PROC STDCALL,
parm1:DWORD, parm2:DWROD
- CALLING 컨벤션을 바꿔줄 수도 있다. (중요하지는 않습니다~~)
4. PROTO Directive
- 내가 작성한 코드에 존재하지 않는 프로시저를 실행하라고 명령을 내리면, 어셈블러가 알아들을 수가 없다.
- INVOKE를 사용하려면 PROTO가 필요하다.
- 위에서 선언이 되어있다면 굳이 필요없다. (C언어의 함수 전방 선언과 같다.)
- 4바이트 단위로 들어가기 때문에 저렇게 되는 것이고 2 빈칸 만들고 푸쉬 뒤쪽은 al만 정상 뒤는 의미없는 값 이렇게 들어간다.
MySub PROTO
.
INVOKE MySub
.
MySub PROC
.
.
MySub ENDP
- PROTO 지시어는 기존 프로시저의 프로토타입을 생성한다.
- 프로토타입은 프로시저의 이름과 매개변수 목록을 선언합니다.
Sub1 PROTO, p1:
Uploaded by N2T
728x90
'CSE > system programing' 카테고리의 다른 글
[시스템 프로그래밍 12-2강] Memory Management 페이징 기법 (0) | 2023.05.28 |
---|---|
[시스템 프로그래밍 11강] Multiplication and Division Instructions & Extended Addition and Subtraction (0) | 2023.05.18 |
[시스템 프로그래밍 10강] Shift and Rotate 명령 및 활용 (0) | 2023.05.16 |
9-2강 Conditional Control Flow Directives (조건부 제어 흐름 지시어) (0) | 2023.05.15 |
9 -1 강 Conditional Loop Instructions (LOOPZ,LOOPE) Conditional structure(조건부 구조 예시코드!) (0) | 2023.05.15 |