Assembly-kieli ja x86-tason järjestelmäohjelmointi · Oppitunti

x87 FPU -ohjelmoinnin perusteet

Oppikaa käyttämään x87-liukuluku-yksikköä (FPU) tarkkuutta vaativiin liukulukulaskuihin Assembly-kielessä.

Oppitunti 1/411 vaihetta

x87 FPU -ohjelmoinnin perusteet on ilmainen Assembly-kieli ja x86-tason järjestelmäohjelmointi-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Assembly-kieli ja x86-tason järjestelmäohjelmointi-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Assembly-kieli ja x86-tason järjestelmäohjelmointi-kurssilla on yhteensä 4 oppituntia.

Tutustuminen x87 FPU:hun

x87 Floating-Point Unit (FPU) on CPU:n erityinen osa, joka on suunniteltu reaalilukujen eli liukulukujen käsittelyyn.

Toisin kuin kokonaislukuaritmetiikassa, liukulukulaskennassa tarvitaan erilaisia sisäisiä esitysmuotoja ja laskutoimituksia. FPU suoriutuu niistä erittäin tarkasti.

FPU:n rekisteripino

x87 FPU käyttää yleiskäyttöisten rekisterien sijaan erityistä rekisteripinoa. Pino koostuu kahdeksasta 80-bittisestä rekisteristä, jotka on merkitty ST(0)–ST(7).

  • ST(0) on aina pinon päällimmäinen alkio.
  • Operaatiot lisäävät uusia arvoja pinoon tai poistavat arvoja pinosta siirtäen samalla olemassa olevia arvoja.
  • Pino toimii periaatteella viimeisenä sisään, ensimmäisenä ulos (LIFO).

Datan lataaminen FLD:llä

Liukulukujen käsittelyä varten ne on ensin ladattava FPU:n pinoon. FLD-käsky työntää liukulukuarvon muistista FPU-pinon päälle, jolloin siitä tulee ST(0).

Assemblyssä liukulukuvakiot määritellään usein dataosiossa. Yleisiä tyyppejä ovat kerta­tarkkuus (32 bittiä, DD) ja kaksoistarkkuus (64 bittiä, DQ).

Kokeile ladata arvo pinoon:

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

FPU-tulosten tallentaminen

Laskutoimitusten jälkeen tulos kannattaa tallentaa FPU-pinosta takaisin muistiin. Tähän käytetään käskyjä FST ja FSTP.

  • FST: Kopioi arvon kohteesta ST(0) muistipaikkaan tai toiseen FPU-rekisteriin ja jättää kohteen ST(0) muuttumattomaksi.
  • FSTP: Kopioi arvon kohteesta ST(0) muistiin tai rekisteriin ja poistaa sen sitten pinosta pienentäen pinon osoitinta. Aiemmasta kohteesta ST(1) tulee ST(0).

Tallennetaan arvo:

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

FPU:n aritmeettiset operaatiot

FPU tarjoaa käskyjä yleisiin aritmeettisiin operaatioihin. Ne toimivat yleensä kohteella ST(0) ja toisella operandilla, joka voi olla toinen pinorekisteri tai muistio-operand.

  • FADD: Lisää (esimerkiksi FADD ST(1), ST(0) lisää ST(0):n arvon kohteeseen ST(1)).
  • FMUL: Kertoo
  • FSUB: Vähentää
  • FDIV: Jakaa

FADDP ST(1), ST(0) lisää ST(0):n kohteeseen ST(1), tallentaa tuloksen kohteeseen ST(1) ja poistaa ST(0):n pinosta. Tällöin summa jää pinon päälle.

Tässä on yhteenlaskuesimerkki:

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:n sisäiset vakiot

FPU voi ladata yleisesti käytettyjä vakioita suoraan pinoonsa, joten niitä ei tarvitse määritellä muistiin. Tämä parantaa tehokkuutta ja tarkkuutta.

  • FLD1: Työntää arvon 1.0 pinoon.
  • FLDZ: Työntää arvon 0.0 pinoon.
  • FLDPI: Työntää piin arvon (π) pinoon.

Ladataan pii:

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

Kokonaislukujen ja liukulukujen muuntaminen

Joskus kokonaisluku- ja liukulukutyyppien välillä on muunnettava arvoja. FPU tarjoaa tähän seuraavat käskyt:

  • FILD (Float Integer Load): Lataa muistista etumerkillisen kokonaisluvun, muuntaa sen liukulukumuotoon ja työntää sen FPU-pinoon.
  • FISTP (Float Integer Store and Pop): Tallentaa kohteen ST(0) kokonaislukuna muistiin ja poistaa sen sitten pinosta. Muunnon aikana arvo katkaistaan kohti nollaa.

Muunnetaan kokonaisluku liukuluvuksi, lisätään arvo ja muunnetaan tulos takaisin:

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

Liukulukujen vertaaminen

Liukulukujen vertaamiseen tarvitaan erityisiä FPU-käskyjä. Kokonaislukujen vertailukäskyjä, kuten CMP, ei voi käyttää suoraan.

  • FCOM: Vertaa kohdetta ST(0) operandiin, joka voi olla toinen FPU-rekisteri tai muistipaikka, ja asettaa FPU:n tilaliput.
  • FCOMP: Toimii kuten FCOM, mutta poistaa kohteen ST(0) vertailun jälkeen.

Jotta näitä lippuja voidaan käyttää ehdollisiin hyppäyksiin, kuten JE ja JB, ne on siirrettävä FPU:n tilasanasta CPU:n EFLAGS-rekisteriin:

  1. FSTSW AX: Tallentaa FPU:n tilasanan rekisteriin AX.
  2. SAHF: Siirtää rekisterin AH, joka sisältää nyt tarvittavat FPU:n liput, CPU:n EFLAGS-rekisteriin, erityisesti liput ZF, PF ja CF.

Kootaan kokonaisuus: (A + B) * C

Yhdistetään oppimamme ja suoritetaan yksinkertainen laskutoimitus: (A + B) * C. Ladataan kolme arvoa, lasketaan kaksi niistä yhteen, kerrotaan summa kolmannella ja tallennetaan lopullinen kokonaislukutulos.

Tämä esimerkki havainnollistaa pinon käsittelyä ja aritmeettisia operaatioita.

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-pinon haaste

Tarkastele seuraavaa x87 FPU:n assembly-koodikatkelmaa. Mikä on kohteen ST(0) arvo sen suorittamisen jälkeen?

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)

x87 FPU:n yhteenveto

Olet ottanut ensimmäiset askeleesi x87 FPU -ohjelmoinnissa! Käsittelimme seuraavat asiat:

  • FPU:n kahdeksan rekisterin pinon (ST(0)–ST(7)).
  • Arvojen lataamisen komennolla FLD ja tallentamisen komennoilla FST/FSTP.
  • Perusaritmetiikan: FADD, FSUB, FMUL ja FDIV.
  • Sisäisten vakioiden, kuten FLD1, FLDZ ja FLDPI, käytön.
  • Kokonaislukujen ja liukulukujen muuntamisen komennoilla FILD ja FISTP.
  • FPU-vertailujen tulosten valmistelemisen ehdollisia hyppäyksiä varten.

x87 FPU soveltuu tarkkoihin laskutoimituksiin, vaikka nykyaikaisissa järjestelmissä käytetään usein nopeampia SIMD-laajennoksia, kuten SSE:tä ja AVX:ää. Niihin tutustut seuraavaksi!

Aloita maksutta

Opi Assembly tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
12
Oppitunnit
48

Usein kysytyt kysymykset

Onko oppitunti ”x87 FPU -ohjelmoinnin perusteet” ilmainen?

Kyllä – oppitunnin ”x87 FPU -ohjelmoinnin perusteet” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Assembly-kieli ja x86-tason järjestelmäohjelmointi-kurssin, päivitä CoddyKit PROhon. Assembly-kieli ja x86-tason järjestelmäohjelmointi-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”x87 FPU -ohjelmoinnin perusteet”?

Oppikaa käyttämään x87-liukuluku-yksikköä (FPU) tarkkuutta vaativiin liukulukulaskuihin Assembly-kielessä. Harjoittelet Assembly-kieli ja x86-tason järjestelmäohjelmointi-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Assembly-kieli ja x86-tason järjestelmäohjelmointi-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Assembly-kieli ja x86-tason järjestelmäohjelmointi-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”x87 FPU -ohjelmoinnin perusteet”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Assembly-kieli ja x86-tason järjestelmäohjelmointi-oppitunnilla?

Kyllä. Jokainen Assembly-kieli ja x86-tason järjestelmäohjelmointi-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. x87 FPU -ohjelmoinnin perusteet
  2. SSE/AVX-käskyvalikoimien johdanto
  3. Koodin vektorointi SIMD:n avulla
  4. Liukulukujen tarkkuus, pyöristys ja poikkeukset
← Takaisin: Assembly-kieli ja x86-tason järjestelmäohjelmointi