이 아티클은 귀도 자이벨스(Guido Gybels)의 Using Assembler in Delphi를 번역한 것으로, 총 4개의 장으로 되어 있는 시리즈 아티클들 중 네번째, 마지막 장입니다. 번역 및 전재를 하도록 허락해주신 귀도씨에게 감사드립니다.
원문 : http://www.guidogybels.eu/asmch4.html
4장. 결과 값 리턴하기
여러분의 어셈블리 루틴으로부터 호출자로 결과 값을 돌려줘야 하는 경우가 많습니다. 이전의 장들에서 함수로 파라미터를 전달하는 방법과 어셈블리 코드에서 로컬 변수를 사용하는 방법에 대해 알아봤습니다. 이 장에서는 호출자 쪽으로 결과를 리턴하는 방법에 대해 알아봅니다.
4.1. 정수 값의 리턴
델파이는 서로 다른 여러 정수 타입들을 제공하는데, 8비트, 16비트, 32비트 정수 타입들이 있습니다. 이 타입들 중에는 부호가 있는 것도 있고 없는 것들도 있습니다. 부호 없는(unsigned) 정수는 항상 양의 정수를 나타냅니다. 부호 있는(signed) 정수는 부호 비트를 가지고 있는데, 이 비트가 설정되면 음의 값을 나타내며 지워지면 양의 값을 나타냅니다. 음의 값은 절대값에 대한 2의 보수로 표현됩니다. 델파이의 정수 타입에는 Shortint (8비트, 부호 있음), Smallint (16비트, 부호 있음), Longint (32비트, 부호 있음), Int64 (64비트, 부호 있음), Byte (8비트, 부호 없음), Word (16비트, 부호 없음), Longword (32비트, 부호 없음)가 있습니다. 그에 더해 델파이에는 일반(generic) 타입으로서 Integer 및 Cardinal 타입이 있는데, 이들은 32비트 플랫폼에서 각각 부호 있는 32비트 값과 부호 없는 32비트 값에 해당합니다. 따라서 32비트 플랫폼에서 Integer는 Longint와 동일하고 Cardinal은 Longword와 동일합니다.
델파이에는 위의 정수 타입들 중 하나와 매핑되는 다른 데이터 타입들도 있습니다. 이들 추가 타입들중 다수는 C 언어의 선언과 동일한 이름의 타입을 제공하기 위한 것이며, 윈도우 API에 맞추기 위한 목적인 것이 많습니다. 예를 들면 DWORD와 UINT는 Longword와 동일하며, SHORT는 Smallint와 동일합니다.
일반적으로, 정수를 리턴하는 것은 단순합니다. 호출자(caller)로 리턴하기 전에 값을 eax 레지스터에 저장하면 됩니다. 리턴 타입이 eax보다 작은 경우에는 eax 레지스터의 al(8비트) 혹은 eax(16비트) 부분만이 유효하고 레지스터의 나머지 부분은 무시됩니다. 자세한 내용은 표 4에서 살펴볼 수 있습니다. 이 규칙의 유일한 예외는 64비트 정수입니다. 32비트 플랫폼에서 64비트 정수는 edx:eax로 리턴되며, edx가 상위 부분을 가집니다.
64비트 정수를 나타내는 Comp 타입은 다른 정수 타입들처럼 동작하지 않는다는 점을 기억해둡시다. Comp 타입은 프로세서의 부동소수점 유닛을 이용하며 따라서 실수 타입의 방식을 따릅니다. 이 타입은 하위 호환성을 위해 제공되는 것이며 가급적 사용하지 않느 것이 좋습니다. 대신 Int64 타입을 사용하는 것을 권합니다. Int64는 FPU 대신 CPU 레지스터를 사용하기 때문에 일부 프로세서들에서는 정수 연산에서 더 나은 성능을 보여줍니다.
다음의 코드는 어셈블리 코드에서 정수를 리턴하는 방법을 보여줍니다. 이 함수는 AValue 파라미터에서 세팅된 비트들의 수를 부호 없는 8비트 값으로 리턴합니다(리턴되는 값이 0~32 범위 안이므로 16비트나 32비트 정수가 필요하지 않지요).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function CountBits(const AValue: Longword): Byte; asm mov ecx, eax xor al, al test ecx, ecx jz @@ending @@counting: shr ecx, 1 adc al, 0 test ecx, ecx jnz @@counting @@ending: end; |
4.2. 부울 값의 리턴
부울(boolean) 값을 리턴하는 것도 마찬가지로 상당히 쉽습니다. 결과는 정수의 리턴과 비슷하게 eax 레지스터 혹은 eax 레지스터의 일부에 들어갑니다. 델파이에는 몇가지 부울 타입들이 있습니다. Boolean은 “적절한” 부울 타입입니다. 이 타입에는 오직 true 및 false의 두 가지의 값만 가능합니다. 델파이에서는 항상 이 타입을 사용해야 합니다. 다른 부울 타입들인 ByteBool, WordBool, LongBool은 다른 언어들과의 호환성과 윈도우 API 호출을 위해서 제공됩니다.
Boolean 타입은 실제로는 열거형 타입입니다. 또한 메모리에서 한 바이트를 차지합니다. 이 타입은 숫자 타입이 아니기 때문에 대입에서는 이미 정의된 상수인 True 및 False만 사용할 수 있습니다. Boolean에는 숫자 값을 대입해서는 안되며 그것은 구현 특성에 의존하기 때문입니다. 델파이의 향후 버전들에서 이것이 변경될 것으로 생각되지는 않지만 역시 좋지 않은 일이고 구조적으로도 보기 나쁘며 부울 상수 True와 False를 사용하는 것보다 불명확하기도 합니다. Boolean 타입은 8비트를 필요로 하므로 al 레지스터로 리턴합니다:
1 2 3 4 5 6 |
function DoSomething(...): Boolean; asm ... mov al, True ... end; |
반대로, ByteBool, WordBool, LongBool은 다른 언어와의 호환성 및 윈도우 API 호출을 위해 제공되며, 본질적으로는 숫자 타입입니다. 이들 타입들은 각각 8비트, 16비트, 32비트를 차지하며 따라서 각각 al, ax, eax 레지스터로 리턴됩니다. 이 타입들은 그 내용의 숫자 값이 0이 아니면 참, 0이면 거짓으로 간주됩니다. 컴파일러는 필요한 경우 이 타입들과 Boolean 사이의 변환을 수행합니다.
표 4에서 자세한 내용을 다룹니다.
4.3. 실수의 리턴
델파이의 실수(real number)는 부동소수점 타입으로 구현됩니다. 불행히도, 요즘의 프로그래머들 다수가 부동소수점 표현을 제대로 이해하고 있지 못합니다. 부동소수점에 대해 간단히 몇가지를 설명해보겠습니다.
여러분의 코드에서 실수를 리턴하는 기본적인 메커니즘은 결과 값을 FPU의 ST(0) 레지스터에 넣는 것입니다. 이것은 FPU 스택의 최상위에 해당합니다. 델파이는 single(7~8 유효숫자, 4바이트 점유)나 double(15~16 유효숫자, 8바이트 점유) 같은 몇가지 부동소수점 포맷들을 지원하지만, 내부적으로 FPU는 부동소수점을 항상 80비트 값으로 저장하고 다룹니다. 델파이의 Extended 타입(19~20 유효 숫자, 10바이트 점유)이 이 포맷에 맞습니다. 이런 모든 실수 타입들은 ST(0)의 값으로 호출자측에 리턴됩니다. 실제로 4, 8, 10바이트 인코딩으로 변환되는 것은 메모리에 저장되거나 프로그램의 다른 부분으로 전달될 때 뿐입니다. 부동소수점 값에 대한 제 다른 아티클에서 이 포맷들에 대해 자세히 다룹니다.
하지만 실수를 다루는 데 있어 염두에 두어야 할 다른 고려점들도 있습니다. 인텔 FPU는 정밀도와 반올림을 제어하는 컨트롤 레지스터와 부동소수점 예외 핸들링을 조정하기 위한 exception mask를 가지고 있습니다. 여러 정밀도와 반올림 방식들로 인해 프로그래머는 FPU의 동작을 더 잘 제어할 수 있으며, 다른 시스템이나 법률 관련 혹은 다른 표준들과의 호환성에도 중요합니다. 따라서 결과를 특정 데이터 타입(single이나 double 등)으로 선언하는 것만으로는 충분하지 않을 수 있다는 것을 이해하는 것이 중요합니다. 대신 여러분은 컨트롤 레지스터를 명시적으로 설정해야 할 필요가 있을 수 있습니다.
FPU 컨트롤 레지스터 내의 정밀도 컨트롤 비트는 이런 측면과 관련성이 큽니다. 일반적인 상황에서는 이 두 비트 바이너리 값은 항상 11로 설정되어 64비트 가수(mantissa) 정밀도를 나타냅니다. 만약 정밀도 컨트롤 비트들이 더 낮은 정밀도로 설정되면 FPU는 계산 중에 정밀도를 낮추므로 그 결과는 예상했던 것보다 더 낮은 정밀도를 가지게 됩니다. 부동소수점 연산의 이론과 인텔 FPU에 대한 자세한 내용은 이 글의 범위를 벗어나지만, 부동소수점 코드에 노력을 들이려고 하기 전에 이 주제에 대해 익숙해질 필요가 있습니다. 델파이에서는 Get8087CW, Set8087CW, SetPrecisionMode 등의 몇가지 지원 함수들과 변수들이 제공합니다. 더 자세한 정보를 위해서는 온라인 헬프를 참고하십시오. 많은 라이브러리와 심지어는 OS 함수 호출까지도 FPU 컨트롤 워드의 값을 변경시킬 수 있다는 점을 기억해두십시오. 여러분의 코드 내에서 컨트롤 워드를 변경하게 되면 작업이 끝났을 때 원래대로 되돌리는 것이 좋은 습관입니다. 이것은 시간에 민감한 부동소수점 코드의 바깥에서 수행해야 합니다.
single, double, extended 외에 Real48, Comp, Currency 타입도 ST(0)에서 리턴됩니다. Comp는 64비트 정수를 나타내기는 하지만 CPU 레지스터가 아닌 FPU를 사용하는 타입이므로 FPU 명령을 이용하여 다루어집니다. Currency는 고정소수점 타입으로서 주로 통화의 계산을 위해 설계된 타입인데, Comp와 마찬가지로 FPU 기반의 타입입니다. Currency는 104배로 저장된다는 점도 알아둡시다. 따라서 Currency 타입에서 5.4321이라는 값은 ST(0)에 54321로 저장됩니다.
통화 관련 애플리케이션에서 부동소수점 연산을 이용하려는 경우, 반드시 부동소수점 연산의 속성을 제대로 이해해야 합니다. 부동소수점 값에 대한 필자의 다른 글에서는 이 주제에 대해 간략한 소개를 제공하고 읽어야 할 자료들도 소개합니다. 그런 애플리케이션에서는 Currency와 같은 배수 정수를 사용하는 것이 좋은 방법이 될 수도 있습니다. 또한 인텔 CPU들은 BCD 인코딩과 연산을 지원하는데, 이런 목적에 유용합니다. 불행히도, 델파이 언어는 BCD 타입이 지원되지 않으므로 BCD 데이터를 직접 인코딩 및 디코딩해야 합니다.
다른 애플리케이션이나 다른 환경과의 호환성 때문에 필요한 경우가 아니라면, 네이티브가 아닌 타입인 Real48은 사용하지 말아야 합니다. 이 타입은 하드웨어에서 지원되지 않으므로 관련 조작은 모두 코드로 이루어지고, 따라서 매우 느립니다. Real48 타입은 받은 즉시 네이티브 실수로 변환하십시오. System 유닛의 _Real2Ext 루틴을 이용하면 됩니다. _Real2Ext를 호출할 때는 eax에는 Real48 값에 대한 포인터가 들어갑니다. 리턴할 때는 ST(0)에 결과 값이 들어갑니다. 이후로는 필요한 연산의 수행에 FPU를 활용할 수 있게 됩니다. 결과 값을 다시 Real48 타입으로 전해줘야 할 경우에는 역시 System 유닛에 있는 _Ext2Real 루틴을 호출하면 됩니다. 이 루틴은 ST(0)에 있는 값을 Real48 값으로 변환합니다. eax는 변환된 값이 저장될 6바이트 메모리 위치에 대한 포인터를 가져야 합니다. 델파이 4보다 전의 버전들에서는 네이티브가 아닌 6바이트 타입은 Real48 대신 Real이라고 불렸습니다. 델파이 4 이후로는 Real은 실수를 위한 일반 타입으로 동작하며, 현재는 Double로 구현됩니다.
표 4에서 실수 타입을 포함하여 결과 값의 리턴에 대한 규칙을 요약해서 보여줍니다.
실수의 리턴에 대한 이 섹션의 내용을 정리하고자 아래의 완전히 동작하는 예제를 제시해봅니다. 아래의 CalcRelativeMass 함수는 델파이 환경 내에서 어셈블리로 작성된 부동소수점 연산의 예를 보여줍니다. 이 함수는 질량과 속도 2개의 파라미터를 받아 상대성 이론에 따라 상대 질량을 계산합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function CalcRelativeMass(m,v: Double): Double; register; const LightVelocity: Integer = 299792500; asm {다음의 공식에 따라 상대 질량을 계산합니다: Result = m / Sqrt(1-v²/c²), 여기서 c = 빛의 속도, m은 질량, v는 객체의 속도} fild LightVelocity fild LightVelocity fmulp {c²를 계산} fld v fld v fmulp {v²를 계산} fxch fdivp {v²/c²} fld1 fxch fsubp {ST(0)=1-(v²/c²)} fsqrt {Root of ST(0)} fld m fxch fdivp {divide mass by root result} end; |
4.4. 문자의 리턴
델파이는 두 가지 기본 문자 타입들을 제공합니다. AnsiChar는 8비트 문자이며, WideChar는 16비트 유니코드 문자입니다. 일반 타입인 Char도 존재하는데, 델파이 2009 이후의 버전들에서는 WideChar에, 그 이전의 델파이 버전들에서는 AnsiChar에 매핑됩니다. 정수 타입들에서처럼 어셈블리 코드에서는 기본 타입들을 사용하는 것이 좋습니다.
AnsiChar는 8비트 타입이기 때문에 al 레지스터로 리턴됩니다. WideChar는 16비트 유니코드 문자이며 ax로 리턴됩니다. 표 4에서 결과 값의 리턴에 대한 규칙을 요약해서 보여줍니다.
4.5. 긴 문자열의 리턴
델파이 애플리케이션에서 아주 흔하게 사용되는 타입들 중 하나는 긴 문자열, 즉 AnsiString입니다. 이 타입은 본질적으로는 AnsiChar 문자들의 배열이지만 약간 복잡합니다. AnsiString 타입은 어셈블리 프로그래머의 입장에서 두 가지 중요한 특성이 있습니다. 첫번째로, AnsiString은 참조 카운트(reference count)가 되는 타입입니다. 참조 카운트 메커니즘은 몇가지 장점들을 가지고 있습니다. 여러 곳에서 동일한 문자열이 사용되는 경우, 각각의 문자열에 대해 메모리를 할당하는 대신 동일한 인스턴스가 공유될 수 있습니다. copy-on-write로 동작하는 참조 카운트를 사용함으로써, 메모리 문자열의 복사는 꼭 필요한 경우에만 일어나게 되므로 오버헤드를 줄이고 성능을 개선시켜줍니다. 참조 카운트는 메모리 관리를 자동화할 수 있게도 해줍니다. 참조 카운트가 0에 이르면 해당 문자열이 어디에서도 사용되지 않는다는 것을 의미하며 자동으로 해제됩니다. 하지만 어셈블리 코드 내에서는 컴파일러가 이런 기능을 지원하지 않으므로, 메모리 관리와 참조 카운트를 수작업으로 처리해야 할 경우가 종종 있습니다.
긴 문자열에서 중요한 특성으로서 두번째는, 힙 메모리에 저장된다는 점입니다. 문자열 변수는 힙에 있는 문자열에 대한 포인터이며, 파스칼 코드에서와는 달리 긴 문자열을 위한 메모리 관리 문제들을 명시적으로 처리해야만 합니다.
어셈블리 코드에서 긴 문자열을 만들고 조작할 때 그 문자열이 asm 블럭의 바깥인 나머지 델파이 코드에서 여전히 제대로 동작하는지 확인할 필요가 있습니다. 그러려면 델파이 함수들을 통해 문자열에 대한 메모리를 제대로 할당해야 하며 긴 문자열을 만들고 처리하고 리턴할 때 항상 참조 카운트를 고려해야 합니다.
서수 타입들과 달리, 긴 문자열은 레지스터로 리턴되지 않으며, 모든 다른 파라미터들 뒤에 추가로 var 파라미터가 선언된 것처럼 동작합니다. 다시 말해, 여러분의 함수로 파라미터 하나가 추가로 참조로 전달됩니다. 2장에서 파라미터 전달에 대해 자세히 다뤘으며, 참조로 변수를 전달하는 이슈와 다양한 콜링 컨벤션들의 차이점들에 대해서도 살펴봤습니다. 추가로 파라미터를 사용하는 방식에 대해 쓸데없이 복잡하게 만들었다고 생각할 수도 있겠지만, 사실은 상당히 영리한 방식입니다. 추가로 var 파라미터를 넘김으로써 참조 카운트를 감소시키는 책임은 호출자에게 넘어갑니다. 이것은 어셈블리 코드에서 긴 문자열을 리턴하는 일을 상당히 쉽게 해주며, 동시에 호출자가 작업을 마친 후 참조 카운트가 적절히 조절되도록 보장해줍니다.
어셈블리 코드에서 긴 문자열의 메모리를 할당하는 과정을 위해서는 System.pas에서 제공되는 다양한 메모리 관리 루틴들을 공부해야 합니다. 예를 들어, Result 문자열에 내용을 채우기 전에 길이를 설정하기 위해 LStrSetLength를 호출할 수 있습니다. 이 방식의 주된 단점은 델파이가 버전업되는 과정에서 동작이 달라질 수도 있다는 것입니다. System.pas는 컴파일 타임에 특별한 취급을 받으며 델파이가 버전업되면서 이런 내부 루틴들이 바뀔 수 있습니다. 이것을 피하는 한가지 방법은, 문자열을 파스칼로 된 코드 다른 어딘가에서 생성한 후 이미 할당된 긴 문자열을 여러분의 어셈블리 코드로 전달하는 것입니다. 포팅이 용이한 코드를 작성하는 일은 중요한 고려 사항입니다. 어셈블리 코드에서 최고의 선택을 하자면 당연히 특정 플랫폼과 컴파일러에 밀접하게 연관된다는 것을 의미하지만, 그런 제한 안에서는 프로그래머는 가능한 한 읽기 쉽고 미래에 대비가 된 코드를 작성해야 합니다.
다음의 예에서, FillWithPlusMinus 프로시저는 전달받은 문자열을 특정 패턴으로 채웁니다. 이 경우에는 프로시저를 호출하기 전에 문자열이 이미 할당되어 System.pas 루틴 호출을 피하고 있습니다.
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 |
procedure FillWithPlusMinus(var AString: AnsiString); register; asm push esi mov esi, [eax] {esi가 우리 문자열을 가리키게 됨} test esi,esi {ni이면 빠져나감} jz @@ending mov edx, [esi-4] {edx = 문자열의 길이} mov eax,'+-+-' {사용할 패턴} mov ecx, edx {카운터 레지스터에 길이를 넣음} shr ecx,2 {나눔, 한번에 4바이트씩 처리} test ecx,ecx jz @@remain @@loop: mov [esi],eax add esi,4 dec ecx jnz @@loop @@remain: {나머지 바이트들을 채움} mov ecx, edx and ecx, 3 jz @@ending @@loop2: mov BYTE PTR [esi],al shr eax,8 inc esi dec ecx jnz @@loop2 @@ending: pop esi end; |
위 예에서는 System.pas의 루틴들을 호출할 필요가 없지만, 긴 문자열의 길이를 읽어오는 부분 등 아직 일부 구현 방식에 종속되는 부분이 있습니다. 긴 문자열은 앞에 두 개의 추가 dword를 가지는데, 32비트 크의 길이 표시가 -4 오프셋 위치에 있고, 32비트 크기의 참조 카운트가 -8 오프셋 위치에 있습니다. 이런 구조가 영원히 유지될 거라는 보장은 없습니다. 위의 함수를 사용하려면 문자열을 할당한 후 루틴을 호출하면 됩니다:
1 2 3 4 5 6 7 8 9 |
procedure DoSomething; var ALine: AnsiString; begin ... SetLength(ALine, {필요한 길이}); FillWithPlusMinus(ALine); ... end; |
다른 방법으로, 아래의 PlusMinusLine 함수에서처럼 긴 문자열의 길이를 설정하기 위해 System.pas의 루틴(LStrSetLength)을 호출하는 쪽을 선택할 수도 있습니다. 2장에서 살펴봤던 것처럼, register 콜링 컨벤션에서 첫번째 파라미터인 LineLength는 eax에 들어가게 됩니다. 결과 문자열은 위에서 설명한 것처럼 추가적인 var 파라미터로 전달됩니다. 이 경우 Result 파라미터는 두번째 파라미터이므로 register 콜링 컨벤션에서는 edx에 들어갑니다. Result의 참조 카운트가 1이 되도록 하기 위해 UniqueStringA을 호출한 것도 주목하십시오. 우리가 문자열의 내용을 조작한 사실을 컴파일러가 알 수 없기 때문에 필요한 것입니다.
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 31 32 33 34 35 36 37 |
function PlusMinusLine(LineLength: Integer): AnsiString; register; asm push esi push ebx mov ebx, eax {ebx=LineLength} mov esi, edx {esi=Result에 대한 포인터} xchg edx, eax {eax=Result에 대한 포인터, edx=길이} call System.@LStrSetLength mov ecx, [esi] jecxz @@ending {nil이면 빠져나감} mov eax, esi call System.@UniqueStringA mov esi, [esi] {esi = 문자열의 첫번째 문자} mov eax, '+-+-' {사용할 패턴} mov ecx, ebx {카운터 레지스터에 길이를 넣음} shr ecx,2 {나눔, 한번에 4바이트씩 처리} test ecx,ecx jz @@remain @@loop: mov [esi],eax add esi,4 dec ecx jnz @@loop @@remain: {나머지 바이트들을 채움} mov ecx, ebx and ecx, 3 jz @@ending @@loop2: mov BYTE PTR [esi],al shr eax,8 inc esi dec ecx jnz @@loop2 @@ending: pop ebx pop esi end; |
이번 예에서는 데이터를 리턴하기 위해 일반적인 Result 방식을 사용합니다. 단순히 필요한 길이만 파라미터로 넘기면서 호출하면 됩니다:
1 2 3 4 5 6 7 8 |
procedure DoSomething; var ALine: AnsiString; begin ... ALine:=PlusMinusLine({Required Length}); ... end; |
위의 예는 델파이 7으로 작성되었습니다. 다른 델파이 버전들 대부분에서도 아주 비슷하게 동작할 것입니다. 내부 함수의 이름은 델파이 버전에 따라 약간 다를 수도 있겠습니다만. System.pas를 살펴보면 적절한 함수를 찾는 데 도움이 될 것입니다. UniqueString 같은 파스칼 함수의 이름에서 컨트롤+마우스 왼쪽 버튼을 눌러 System.pas의 구현을 찾아가보면 더 편리할 것입니다.
표 4: 결과값의 리턴
이 표에서는 델파이 32비트 애플리케이션의 어셈블리 루틴에서 결과를 리턴하는 방법을 정리합니다. 첫번째 컬럼은 여러 데이터 타입들을 나열합니다. 두번째 컬럼은 각각의 타입들이 리턴되는 위치입니다. 세번째 컬럼은 리턴되는 결과의 정체입니다.
리턴되는 위치 | 리턴 값 | |
ShortInt | al | 8비트 부호 있는 값 |
SmallInt(1) | ax | 16비트 부호 있는 값 |
LongInt(2) | eax | 32비트 부호 있는 값 |
Byte | al | 8비트 부호 없는 값 |
Word | ax | 16비트 부호 없는 값 |
LongWord(3) | eax | 32비트 부호 없는 값 |
Int64 | edx:eax | 64비트 부호 있는 값 |
Boolean | al | True 혹은 False |
ByteBool | al | 8비트 값, 0이 아니면 true |
WordBool | ax | 16비트 값, 0이 아니면 true |
LongBool | eax | 32비트 값, 0이 아니면 true |
Single | ST(0)(4) | 80비트 부동소수점 값 |
Double | ST(0)(4) | 80비트 부동소수점 값 |
Extended | ST(0)(4) | 80비트 부동소수점 값 |
Real48 | ST(0)(4) | 80비트 부동소수점 값 |
Comp | ST(0)(4) (5) | 80비트 부동소수점 값 |
Currency | ST(0)(4) | 80비트 부동소수점 값(6) |
AnsiChar | al | 8비트 문자 |
WideChar | ax | 16비트 유니코드 문자 |
Byte | al | 8비트 부호 없는 값 |
AnsiString | 추가 var 파라미터(7) | AnsiString에 대한 포인터에 대한 포인터 |
(1) SHORT 타입은 SmallInt와 동일.
(2) 일반 타입 Integer는 현재 LongInt에 매핑됨.
(3) DWORD 및 UINT 타입은 Longword와 동일. 또한 일반 타입 Cardinal은 32비트 플랫폼에서 Longword에 매핑됨.
(4) FPU 레지스터들은 80비트임. 모든 결과는 ST(0)로 리턴됨. 결과를 single, double 등으로 규정하는 것은 저장 및 조작의 차원.
(5) Comp는 64비트 정수를 나타내지만 FPU에서 조작되는 타입. CPU에서 조작되는 다른 정수 타입들과 다름. 따라서 Comp는 조작 및 전달 등에서 실수의 규칙을 따름.
(6) 10,000배로 저장됨.
(7) 자세한 내용은 “4.5. 긴 문자열의 리턴” 참조.
이 아티클은 귀도 자이벨스(Guido Gybels)의 Using Assembler in Delphi를 번역한 것으로, 총 4개의 장으로 되어 있는 시리즈 아티클들 중 네번째, 마지막 장입니다. 번역 및 전재를 하도록 허락해주신 귀도씨에게 감사드립니다.
원문 : http://www.guidogybels.eu/asmch4.