組合語言作業四

 

姓名: 顏嘉志

學號: B83503019

專題報告:Using FPU

 

前言

 

在微處理器的計算之中,若使用FPU (floating point unit) 指令,相較於普通的指令,一般來說,速度可以快至10 ~ 100倍以上。FPU486DX以上的機器是和ALUControl Unit放在同一片Chip之上的;但在8088286386486SX都需要有所謂的x87協同處理器才能處理浮點運算。要詳盡的說明FPU至少需要一本書的內容,本文只對FPU指令做最基本的探討,若要更深入的了解FPU的話,可以參考本文最後的書目。

 

FPU stack registers

 

要處理浮點運算,當然需要特別的Register;下圖顯示了FPURegister set

 

 

FPU DATA REGISTERS

 

TAG

 

79

78

63

 

1

R1

sign

exponent

Significant

 

 

r2

 

 

 

 

 

r3

 

 

 

 

 

r4

 

 

 

 

 

r5

 

 

 

 

 

r6

 

 

 

 

 

r7

 

 

 

 

 

r8

 

 

 

 

 

 

15

Control Register

Status Register

Instruction

Pointer

Data

Pointer

 

其中,最重要的是floating register r1~r8FPU快速的原因之一是這些register擁有相當大的format (80 bits)。另外,2 bitsTAGflag的作用,若TAG = 00代表floating registersvalidTAG = 01代表floating registers contains zeroTAG = 10代表floating registers contains erroneous resultTAG = 11代表floating registersempty

 

Number Format

 

簡單的說,FPU可以處理相當於C語言中floatdoublelong、或short等資料型態。底下列出了FPU的各種Format

 

Data Type

BITS

Significant Digits

(decimal)

Approximate Range

(decimal)

word integer

16

4

-32768 ~ 32768

Short integer

32

9

-2*10^9~2*10^9

Long integer

64

18

-9*10^18 ~ 9*10^18

Short real

32

6-7

1.175*10^-38 ~ 3.403*10^38

Long real

64

15-16

2.225*10^308 ~ 1.798*10^308

Temp real

80

9

3.37*10^-4932 ~ 1.19*10^4932

Packed decimal

80

18

-99…99 ~ 99…99 (18 digits)

 

FPU基本運作

 

FPU基本上是以一個Stack和外界的memoryregister溝通。就如同stack的特性一樣,只有stack的入口可以和外界做loadstore的動作。而在stack內部 ( r0 - r7 )則可以任意的交換、運算,如同一般的register一樣。

先來看看一個簡單的例子:

 

若我想求 a*b/c ,則可以用以下的方法:

 

finit

 

; Initialize FPU & clear all registers

fld

a

; Push a onto FPU stack

fld

b

; Push b

fmulp

st(1), st

; st代表stacktopst(1) 代表stack

; 的第二個elementst(2) 代表stack

; 的第三個element,依此類推。Fmulp

; 代表把stst(1) 相乘之後放入

; st(1),然後再pop st,故,此時

; stack只剩a*b了。

fld

c

Push c

fdivp

st(1), st

St = a*b/c

fst

Memory

Store to the memory

 

在運算之中,FPU stack是用stst(1)、…、st(7) 來表示,st(i) st(j) 之間可以任意的運算或交換。在FPU的指令當中,開頭一定有個 ‘f’ 來和一般的指令區別。

下面列出了一些基本的指令:

 

(A)、加法:

 

fadd

st(i), st

 

fadd

st, st(i)

 

faddp

st(i), st

;add and pop

fadd

 

; faddp st(1), st

 

(B)、淢法:

 

fsub

st(i), st

;st(i) st

fsub

st, st(i)

;st st(i)

fsubp

st(i), st

;sub and pop

fsub

 

; fsubp st(1), st

 

(C)、乘法:

 

fmul

st(i), st

 

fmul

st, st(i)

 

fmulp

st(i), st

;mul and pop

fmul

 

; fmulp st(1), st

 

(D)、除法:

 

fdiv

st(i), st

;st(i) / st

fdiv

st, st(i)

;st / st(i)

fdivp

st(i), st

;div and pop

fdiv

 

; fdivp st(1), st

 

 

(E)、特殊運算:

 

fptan

;st = tan(st)

fsin

;st = sin(st)

fcos

;st = cos(st)

fpatan

;st = arctan(st)

f2xm1

;st = 2^st –1, |st| < 0.5

fyl2x

;st(1) = st(1)*log2(st) and pop

fyl2xp1

;st(1) = st(1)*log2(st+1) and pop

fsqrt

;st = sprt(st)

fscale

;st(1) = st*2^st(1)

fabs

;st = |st|

fchs

;st = -st

 

 

(F)Push 常數:

 

fldz

;push 0.0

fld1

;push 1.0

fldpi

;push pi

fldl2t

;push log2(10)

fldl2e

;push log2(e)

fldlg2

;push log10(2)

fldln2

;push log(2)

 

 

(G)、操作指令:

 

fld

st(i)

;load

fst

st(i)

;store

fxch

st(i)

;exchange st and st(i)

finit

 

;initialize

fwait

 

;CPU等候FPU運算

 

 

 

應用例子

 

問題:

在一RC Circuit中,電容充電的方程式為

Vc = E*(1-exp(-t/T))

其中 T = R*Ctime constantE為直流電壓。

E = 100Vt = 0.002sT = 0.001s,求Vc應該為多少?

 

答:

我們可以寫一個FPU的程式來解此問題。這個程式在I/O和迴圈部份用C來實作 (比較方便),在數學運算方面則用FPUInstruction set

 

 

#include <stdio.h>

#include <conio.h>

 

const double value = 0.5;

 

double t = -0.002;

double T = 0.001;

double E = 100.0;

double Vc;

 

void Calculate_exp_step1(void);

void Calculate_exp_step2(double temp);

void Calculate_Vc(void);

 

void main(void)

{

//----------------------------------------------------------------------------------------------------------//

// 第一步,先算出 Vc = (t/T)*log2(e)

//----------------------------------------------------------------------------------------------------------//

Calculate_exp_step1();

 

//----------------------------------------------------------------------------------------------------------//

// 第二步,讓 Vc = Vc / k,使得 Vc 在 0 ~ 0.5 之間 (為了要用 f2xm1 這

// 個指令)。而 k = 2^m,故迴圈要跑 m 次

//-----------------------------------------------------------------------------------------------------------//

Vc = -Vc;

int key = 0;

while( Vc > value )

{

Vc = Vc/2;

key++;

}

 

Vc = -Vc;

int number = 1;

//--------------------------------------------------------------------------------------------------------//

// number 就是前面所說的 k,key 就是前面所說的 m; number = 2^key

//--------------------------------------------------------------------------------------------------------//

number = number<<key;

double temp = Vc;

Vc = 1;

 

//------------------------------------------------------------------------------------------------------//

// 第三步,用 y^x = 2^(x*log2(y)) 使得 Vc = 2^( (1/k)*(t/T)*log2(e) )

// 即 Vc = ( exp((t/T)*(1/k)) ) ^ k = exp(t/T)

//------------------------------------------------------------------------------------------------------//

for(int i=0; i<number; i++)

Calculate_exp_step2(temp);

 

//-----------------------------------------------------------------------------------------------------//

// 第四步,求 Vc = E*( 1 - exp(t/T) )

//-----------------------------------------------------------------------------------------------------//

Calculate_Vc();

printf("\n Vc = %lf\n", Vc);

}

 

void Calculate_exp_step1(void)

{

//------------------------------------------------------------------------------------------//

// begin asm: return = (t/T)*log2(e)

//------------------------------------------------------------------------------------------//

asm{

finit;

fld t;

fld T;

fdivp st(1), st;

fldl2e;

fmulp st(1), st;

fst Vc;

}

//------------------------------------------------ End of asm -------------------------------------//

}

 

void Calculate_exp_step2(double temp)

{

//------------------------------------------------------------------------------------------//

// begin asm: return = e^(t/T)

//------------------------------------------------------------------------------------------//

double result;

asm{

finit;

fld temp;

fld1;

fxch;

f2xm1;

fxch;

faddp st(1), st;

fst result;

}

Vc *= result;

//------------------------------------------------ End of asm -------------------------------------//

}

 

void Calculate_Vc(void)

{

//------------------------------------------------------------------------------------------//

// begin asm: return = E*(1-e^(t/T))

//------------------------------------------------------------------------------------------//

asm{

finit;

fld Vc;

fld1;

fxch;

fsubp st(1), st;

fld E;

fmul;

fst Vc;

}

//------------------------------------------------ End of asm -------------------------------------//

}

 

執行結果如下:

A:\>testfpu

Vc = 86.466472

A:\>  

 

參考書目

 

 The Personal Computer from the inside out, Addison Wesley

 i486 Reference Manual