Assembler och lågnivåprogrammering för x86-system · Lektion

Grunderna i x87 FPU-programmering

Lär dig använda x87 Floating-Point Unit (FPU) för flyttalsaritmetik med hög precision i Assembly.

Lektion 1 av 411 steg

Grunderna i x87 FPU-programmering är en gratis lektion i Assembler och lågnivåprogrammering för x86-system på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Assembler och lågnivåprogrammering för x86-system, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Assembler och lågnivåprogrammering för x86-system innehåller totalt 4 lektioner.

Lär känna x87 FPU

x87 Floating-Point Unit (FPU) är en specialiserad del av processorn som är utformad för att hantera matematiska operationer på reella tal, även kallade flyttal.

Till skillnad från heltalsaritmetik kräver flyttalsberäkningar andra interna representationer och beräkningar, något som FPU:n hanterar med hög precision.

FPU:ns registerstack

x87 FPU använder en unik registerstack, inte allmänna register. Stacken består av åtta 80-bitarsregister, betecknade ST(0) till och med ST(7).

  • ST(0) är alltid överst i stacken.
  • Operationer lägger nya värden på stacken eller tar bort värden från den, vilket flyttar befintliga värden.
  • Den fungerar som en LIFO-stack (Last-In, First-Out).

Läsa in data med FLD

För att arbeta med flyttal måste du först läsa in dem på FPU-stacken. Instruktionen FLD lägger ett flyttalsvärde från minnet överst på FPU-stacken, vilket gör värdet till ST(0).

I assembler definierar vi ofta flyttalskonstanter i datasegmentet. Vanliga typer är enkel precision (32 bitar, DD) och dubbel precision (64 bitar, DQ).

Prova att läsa in ett värde på stacken:

section .data
  float_val dq 3.1415926535

section .text
  global _start

_start:
  finit                 ; Initialize FPU
  fld qword [float_val] ; Load float_val onto FPU stack (ST(0))

  ; At this point, ST(0) contains 3.1415926535
  ; We just exit as printing floats is complex in basic assembly.

  mov rax, 60           ; syscall number for exit
  xor rdi, rdi          ; exit code 0
  syscall

Lagra FPU-resultat

Efter beräkningarna vill du lagra resultatet från FPU-stacken tillbaka i minnet. Instruktionerna FST och FSTP används för detta.

  • FST: Kopierar värdet från ST(0) till en minnesadress eller ett annat FPU-register utan att ändra ST(0).
  • FSTP: Kopierar värdet från ST(0) till minnet eller ett register och tar sedan bort det från stacken, vilket minskar stackpekaren. Det tidigare ST(1) blir ST(0).

Nu lagrar vi ett värde:

section .data
  float_val dq 123.45
  result_val dq 0.0     ; Will store result here

section .text
  global _start

_start:
  finit                 ; Initialize FPU
  fld qword [float_val] ; ST(0) = 123.45

  fstp qword [result_val] ; Store ST(0) to result_val, then pop.
                          ; FPU stack is now empty.

  ; result_val now holds 123.45 in memory.

  mov rax, 60           ; syscall number for exit
  xor rdi, rdi          ; exit code 0
  syscall

Aritmetiska FPU-operationer

FPU:n tillhandahåller instruktioner för vanliga aritmetiska operationer. Dessa arbetar vanligtvis med ST(0) och en annan operand (antingen ett annat stackregister eller en minnesoperand).

  • FADD: Addera (till exempel adderar FADD ST(1), ST(0) ST(0) till ST(1)).
  • FMUL: Multiplicera
  • FSUB: Subtrahera
  • FDIV: Dividera

Med FADDP ST(1), ST(0) adderas ST(0) till ST(1), resultatet lagras i ST(1) och ST(0) tas bort från stacken. Då ligger summan överst i stacken.

Här är ett exempel på addition:

section .data
  val1 dq 10.5
  val2 dq 2.0
  sum_result dq 0.0

section .text
  global _start

_start:
  finit                 ; Initialize FPU
  fld qword [val1]      ; ST(0) = 10.5
  fld qword [val2]      ; ST(0) = 2.0, ST(1) = 10.5

  faddp st(1), st(0)    ; ST(1) = ST(1) + ST(0) (10.5 + 2.0 = 12.5).
                        ; Pop ST(0). Now ST(0) = 12.5.

  fstp qword [sum_result] ; Store 12.5 to sum_result and pop.

  mov rax, 60           ; syscall number for exit
  xor rdi, rdi          ; exit code 0
  syscall

FPU:ns inbyggda konstanter

FPU:n kan läsa in vanligt förekommande konstanter direkt på stacken, så att du slipper definiera dem i minnet. Detta förbättrar effektiviteten och precisionen.

  • FLD1: Lägger 1.0 på stacken.
  • FLDZ: Lägger 0.0 på stacken.
  • FLDPI: Lägger värdet av Pi (π) på stacken.

Nu läser vi in Pi:

section .data
  pi_val dq 0.0 ; To store PI

section .text
  global _start

_start:
  finit           ; Initialize FPU
  fldpi           ; ST(0) = PI (approx 3.14159...)

  fstp qword [pi_val] ; Store PI to pi_val and pop.

  mov rax, 60     ; syscall number for exit
  xor rdi, rdi    ; exit code 0
  syscall

Konvertera heltal och flyttal

Ibland behöver du konvertera mellan heltalstyper och flyttalstyper. FPU:n tillhandahåller instruktioner för detta:

  • FILD (Float Integer Load): Läser in ett teckenförsett heltal från minnet, konverterar det till flyttalsformat och lägger det på FPU-stacken.
  • FISTP (Float Integer Store and Pop): Lagrar ST(0) som ett heltal i minnet och tar sedan bort värdet från stacken. Vid konverteringen trunkeras värdet mot noll.

Nu konverterar vi ett heltal till ett flyttal, adderar och konverterar sedan tillbaka:

section .data
  int_val dd 5
  float_add dq 2.5
  int_result dd 0

section .text
  global _start

_start:
  finit               ; Initialize FPU
  fild dword [int_val] ; ST(0) = 5.0 (from 5)
  fld qword [float_add] ; ST(0) = 2.5, ST(1) = 5.0

  faddp st(1), st(0)  ; ST(1) = 5.0 + 2.5 = 7.5. Pop ST(0).
                      ; Now ST(0) = 7.5.

  fistp dword [int_result] ; Store 7.5 as integer (7) to int_result and pop.

  mov rax, 60         ; syscall number for exit
  xor rdi, rdi        ; exit code 0
  syscall

Jämföra flyttalsvärden

För att jämföra flyttal krävs särskilda FPU-instruktioner. Du kan inte använda heltalsinstruktioner för jämförelse, som CMP, direkt.

  • FCOM: Jämför ST(0) med en operand (ett annat FPU-register eller minnet) och ställer in FPU:ns statusflaggor.
  • FCOMP: Fungerar som FCOM, men tar bort ST(0) efter jämförelsen.

För att använda dessa flaggor vid villkorliga hopp (som JE och JB) måste du överföra dem från FPU:ns statusord till processorns EFLAGS-register:

  1. FSTSW AX: Lagrar FPU:ns statusord i registret AX.
  2. SAHF: Överför registret AH (som nu innehåller de relevanta FPU-flaggorna) till processorns EFLAGS-register, närmare bestämt flaggorna ZF, PF och CF.

Sätta ihop det: (A + B) * C

Nu kombinerar vi det vi har lärt oss för att utföra en enkel beräkning: (A + B) * C. Vi läser in tre värden, adderar två av dem, multiplicerar med det tredje och lagrar det slutliga heltalsresultatet.

Exemplet visar hur stacken hanteras och hur aritmetiska operationer utförs.

section .data
  val_A dq 3.0
  val_B dq 1.5
  val_C dq 2.0
  final_int_result dd 0

section .text
  global _start

_start:
  finit           ; Initialize FPU

  fld qword [val_A] ; ST(0) = 3.0
  fld qword [val_B] ; ST(0) = 1.5, ST(1) = 3.0

  faddp st(1), st(0) ; Add ST(0) (1.5) to ST(1) (3.0), store in ST(1).
                     ; Pop ST(0). Now ST(0) = 4.5 (sum of A+B)

  fld qword [val_C] ; ST(0) = 2.0, ST(1) = 4.5 (A+B)

  fmulp st(1), st(0) ; Multiply ST(0) (2.0) by ST(1) (4.5), store in ST(1).
                     ; Pop ST(0). Now ST(0) = 9.0 ((A+B)*C)

  fistp dword [final_int_result] ; Store 9.0 as integer (9) to final_int_result and pop.

  mov rax, 60     ; syscall number for exit
  xor rdi, rdi    ; exit code 0
  syscall

FPU-stackutmaning

Betrakta följande kodavsnitt med x87 FPU-assembler. Vilket värde har ST(0) efter körningen?

section .data
  val_X dq 10.0
  val_Y dq 3.0

section .text
  finit
  fld qword [val_X] ; ST(0) = 10.0
  fld qword [val_Y] ; ST(0) = 3.0, ST(1) = 10.0
  faddp st(1), st(0) ; ST(0) = 13.0
  fld1              ; ST(0) = 1.0, ST(1) = 13.0
  fsub              ; ST(0) = ST(0) - ST(1) (1.0 - 13.0 = -12.0)

Sammanfattning av x87 FPU

Du har tagit dina första steg inom x87 FPU-programmering! Vi gick igenom:

  • FPU:ns stack med åtta register (ST(0) till ST(7)).
  • Att läsa in värden med FLD och lagra dem med FST/FSTP.
  • Grundläggande aritmetik: FADD, FSUB, FMUL och FDIV.
  • Att använda inbyggda konstanter som FLD1, FLDZ och FLDPI.
  • Att konvertera mellan heltal och flyttal med FILD och FISTP.
  • Hur du förbereder resultat från FPU-jämförelser för villkorliga hopp.

x87 FPU är kraftfull för precisa beräkningar, även om moderna system ofta använder SIMD-utökningar som SSE/AVX för högre hastighet, vilket du utforskar härnäst!

Gratis att börja

Lär dig Assembly med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
12
Lektioner
48

Vanliga frågor

Är lektionen ”Grunderna i x87 FPU-programmering” gratis?

Ja – hela texten till ”Grunderna i x87 FPU-programmering” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Assembler och lågnivåprogrammering för x86-system, kan Ni uppgradera till CoddyKit PRO. Kursen i Assembler och lågnivåprogrammering för x86-system innehåller totalt 4 lektioner.

Vad lär jag mig i ”Grunderna i x87 FPU-programmering”?

Lär dig använda x87 Floating-Point Unit (FPU) för flyttalsaritmetik med hög precision i Assembly. Ni övar på Assembler och lågnivåprogrammering för x86-system med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig Assembler och lågnivåprogrammering för x86-system?

Du behöver inga förkunskaper. Utbildningen i Assembler och lågnivåprogrammering för x86-system på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Grunderna i x87 FPU-programmering”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här Assembler och lågnivåprogrammering för x86-system-lektionen?

Ja. Varje Assembler och lågnivåprogrammering för x86-system-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Grunderna i x87 FPU-programmering
  2. Introduktion till SSE/AVX-instruktionsuppsättningar
  3. Vektorisera kod med SIMD
  4. Flyttalsprecision, avrundning och undantag
← Tillbaka till Assembler och lågnivåprogrammering för x86-system