본문 바로가기

무언가 만들기 위한 지식/SPARC Assembler

SPARC Assembler Floating Point Control


Floating Point를 이용하여 소수점을 제어하는 부분을 알아볼 것이다.
기타 언어에서는 알아서 처리해주기 때문에 단지 자료형을 float double로 사용하고 출력할때 형식만 정해주면 별로 신경쓸 일이 없지만, SPARC Assembler에서는 신경쓸일이 좀 있다.

일단 예제를 통해 알아보기전에 기본적으로 알아야할 Floating Point에 대해 알아보자.
(이게 은근히 복잡하고 알아두어야 할 내용이 많다. Floating Point는 아예 따로 포스팅 예정이고 일단 간단하게 알아본후 코딩 방식을 알아볼 것이다.)

실수를 나타내는 방식은 고정소수점, 부동소수점 2가지 방식이 있는데, 실제로 모든 곳에서 사용되는 방식은 부동소수점이다. (부동소수점, 고정소수점은 컴퓨터의 실수 표현 방식이다.) [부동(浮動)]
Floating Point가 부동소수점이다.
Floating Point의 경우 표현 크기에 따라 Single, Double, Quadruple으로 나뉜다.
Single은 총 32bit로 구성,
Double은 총 64bit로 구성,
Quadruple은 총 128bit로 구성 된다.

Single format에 대해 보면 다음과 같다.


처음 1bit는 부호(Sign)을 나타내고                                 = Sign(s)
30~23bit 소수점의 위치(floating Point)를 나타내고,          = biased exponent(e)
22~0사이에는 유효숫자를 나타낸다.                              = fractional part of significand (f)

s와 e와 f를 이용한 공식으로 표현하면 N=((-1)의 s승)*(2의(e-127)승)*1.f

exponent의 최대값은 0xfe, 최소값은 0x1이다.
0xff의 경우 squr(-1), ∞으로 사용되고 0=0x00000000로 사용된다.

이식으로 계산하다보면 Single(32bit)의 경우 유효숫자를 8~9개 정도밖에 표현 못하는 상황이 생긴다.
이에 유효숫자를 늘리기 위한 방법이 바로 Double이다.

Floating Point의 경우 고정소수점에 비해 표현가능한 량은 많지만 연산속도에서 느리기 때문에 따로 Coprocessor를 둔다.
특히 이 Floating Point를 담당하는 unit을 FPU라고 한다. 실제로 대부분의 CPU에서 FPU가 따로 존재하여 Floating Point연산을 보조한다고 한다.

어셈블리언어에서는 명령어 자체도 ldf, stf, fadds, fmuls등 특화된 floating point 전용 명령어 및 분기문이 존재한다. cc코드의 경우도 fcc, fcmp등 다 따로 존재한다.
레지스터의 경우에도 %f0~f31이 따로 존재하여 연산에는 F Register를 사용하게 된다.
(%f Register를 0으로 초기화하는 방법은 단순히 %g0를 사용하면 된다.)

single의 경우 명령어들을 살펴보면
ldf, stf, fadds, fsubs, fmuls, fdivs, fsqrts, fmovs, fnegs, fabss, fitos, fatoi 등이 존재하고
double의 경우 명령어들을 살펴보면
lddf, stdf, faddd, fsubd, fmuld, fsmuld, fdivd, sqrtd, fitod, fdtoi, fdtos, fstod, fcmpd 등이 있다.
두개의 차이점을 보면 s or d가 추가되는 점을 볼 수 있다.

※ fabss 및 fmovs는 유효숫자만 바꾸기 떄문에 두경우 모두 사용된다.

사실 %f Register는 마치 %g Register처럼 Global하게 사용된다. 어디서나 접근이 가능하다.

%f를 이용한 Function Call에서 하나 알아두어야할 중요한 점은 반환값은 %o Register가 아닌 %f Register에 리턴이 된다는 것이다.
하나 충분히 고려해야 할 것은 64bit가 사용되는 double의 경우 항상 두개의 Register가 같이 사용된다. 이 때 2의 배수를 충분히 고려하여 Register를 사용하지 않는 다면 컴파일 에러가 나고 말 것이다.

그럼 실제로 사용 예를 보자.
[작성환경 : solaris sparc machine  작성툴 : Vi editor   컴파일러 : gcc]


9~15 Line : scanf를 통해서 수를 입력받고 있다. 보면 위에  format에서 %lf의 형태로 된 것을 볼 수 있다. %lf는 double형을 나타내는 형식이다. 즉 double형을 받아 사용하는 루틴이다. double형을 받아 메모리 공간에 저장하는데 메모리 크기를 -8의 크기로 잡은 것을 볼 수 있다. 즉 64bit를 할당해 주는 것이다. 해당 라인의 형식으로 하면 메모리영역의 연속된 8byte에 float point의 정보가 저장되게 된다.
17, 18 Line : f Register로 Load하는 부분이다. ldf를 써주지 않고 해당 Register를 %f로 하면 자동으로 값을 Float형태로 가져온다.
19, 20 Line : faddd를 이용해 Double 타입의 두 Register 값을 더하고 해당 Register값을 메모리에 저장한다.
저장되는 메모리상에는 단지 형식대로 저장된다.(형식이란 bit 패턴을 말함)
22~32 Line : printf를 이용하여 출력한다. 이부분이 이외로 헷갈리고 까다롭다. 사용법은 간단한데 그냥 포맷을 지정해주고 메모리 주소의 값을 꺼내 %o1로 넣어주면 된다. 어떻게 생각하면 %f1 로 넣어줘야하지 않나 생각도 되는데 그렇게 하면 잘못된 값이 나온다. 그렇다고 %f 로 로드한후 %o1로 값도 전환이 안된다. 이러한경우 그냥 메모리상의 bit 패턴을 그대로 %o1로 넣어주면 printf내의 format형식 (%lf)에 따라 알아서 출력해주게 된다.

필자가 여러 테스트를 해본 결과 Double(64bit)형에 대한 테스트는 잘되나 Single(32bit)의 경우 잘못된 출력과 저장이 계속 되었다. 원인을 잘 모르겠으나 Double의 경우 잘되서 이를 이용하였다.


결과 값은 위에서 같이 두개의 입력을 받은후 더한수를 출력한다.