Kirjoittaja: Päärynämies
Kirjoitettu: 14.11.2007 – 19.04.2013
Tagit: matematiikka, ohjelmointitavat, koodi näytille, vinkki, Linux
kikkareen koodiesimerkin (https://www.ohjelmointiputka.net/koodivinkit/25070-assembly-fibonaccin-lukujono) innoittamana Fibonaccin lukujonon laskua Linuxilla. Esittelee komentoriviargumenttien ja Linuxin systeemikutsujen (system calls) käyttöä. AT&T syntaksilla kirjoitettu. Koodia yritetty mahdollisimman hyödyllisesti kommentoida, käskyjen toiminta kuitenkin jätetty suurimmaksi osin lukijan tietämyksen varaan.
Funktio print_number tulostaa luvun numero numerolta. Tulostettava luku jaetaan kymmenellä ja jakojäännös laitetaan pinoon. Kun luku menee alle kymmeneen, niin aletaan tulostaa jakojäännöksiä pinosta. Näin saadaan tulostettua useamman numeron sisältävä luku oikein. Funktio on rekursiivinen, mutta se olisi voitu toteuttaa myös pitämällä kirjaa pinossa olevien jäännöstä määrästä ja käyttää hyppykäskyjä silmukassa.
Koodi ei tarkista oliko käyttäjän haluama määrä termejä oikeasti luku vai sisälsikö se muitakin merkkejä. Ohjelma todennäköisesti kaatuu, jos se ei ole luku. Tarkistus olisi helppo toteuttaa, mutta jätin sen tekemättä, koska se ei mielestäni ollut oleellista koodiesimerkin kannalta. Lukija voi harjoitustyönä lisätä tarkistuksen halutessaan.
Ei tarvitse kirjastoja kääntämiseen.
Kääntäminen:
as fibs.s -o fibs.o
ld fibs.o -o fibs -s
Käyttö:
./fibs x
jossa x haluttujen termien määrä
fibs.s
#Laskee käyttäjän haluaman määrän Fibonaccin lukujonon termejä alkaen #ensimmäisestä. Kun termi on suurempi kuin 2^32, niin ohjelma ei enää #toimi oikein, johtuen siitä, että ohjelma käyttää 32-bittisiä #rekistereitä lukujen laskemiseen. #Kääntäminen: #as fibs.s -o fibs.o #ld fibs.o -o fibs -s #Käyttö: #./fibs x #jossa x on haluttujen termien määrä. #-s ei pakollinen ld:lle, mutta poistaa turhaa tietoa suoritettavasta #tiedostosta ja pienentää sen kokoa huomattavasti. Ohjelman koko vain #456 tavua #Määritellään joitakin vakioita selkeyttämään. .equ LINUX_SYSCALL, 0x80 .equ SYSCALL_EXIT, 1 .equ SYSCALL_WRITE, 4 .equ STDOUT, 1 .section .bss .lcomm char, 1 #varataan tulostettavalle numerolle muistia .section .text .globl _start #Tulostaa %eax:ssä saamansa luvun. #Funktio on rekursiivinen. .type print_number, @function print_number: xorl %edx, %edx movl $10, %ebx divl %ebx #jaetaan %eax %ebx:llä. %eax sisältää osamäärän pushl %edx #%edx sisältää jakojäännöksen cmpl $0, %eax jz no_call call print_number no_call: popl %eax addl $48, %eax #48 on ascii taulukossa 0, 49 on 1 jne. call print ret #Tulostettavan merkin ascii arvo välitetään %eax:ssä .type print,@function print: leal char, %ecx movb %al, (char) movl $SYSCALL_WRITE, %eax movl $STDOUT, %ebx movl $1, %edx int $LINUX_SYSCALL ret #Funktio, joka muuttaa ascii-merkkijonon luvuksi. #Merkkijonon osoite välitetään rekisterissä %eax. #Funktio palauttaa luvun %eax:ssä. .type atoi, @function atoi: pushl %ebx pushl %ecx pushl %esi movl %eax, %esi xorl %eax, %eax movl $10, %ebx cld #%esi kasvaa yhdellä jokasen lodsb käskyn jälkeen loop1: lodsb #Ladataan %esi:n osoittamasta muistipaikasta tavu %eax:ään cmpl $0, %eax je end_loop1 xchg %eax, %ecx mul %ebx subl $48, %ecx addl %ecx, %eax xchg %eax, %ecx jmp loop1 end_loop1: movl %ecx, %eax popl %esi popl %ecx popl %ebx ret _start: movl (%esp), %eax #argc %eax:ään cmpl $2, %eax #Syöttikö käyttäjä tarpeeksi argumenttejä? jb exit movl 8(%esp), %eax #argv[1] %eax:ään call atoi movl %eax, %ecx #haluttujen lukujen määrä on nyt %ecx:ssä xorl %eax, %eax #sarjan ensimmäinen luku on 0 movl $1, %ebx #sarjan toinen luku on 1 fib_loop: pusha #tallennetaan kaikki rekisterit call print_number movl $32, %eax #32 on välilyönti call print popa #palautetaan kaikki rekisterit pushl %eax addl %ebx, %eax popl %ebx loop fib_loop exit: movl $0, %ebx #Ohjelman palauttama arvo movl $SYSCALL_EXIT, %eax int $LINUX_SYSCALL
Kylpä nyt on assenplyvinkit suosiossa ^_^
Ja millä seuraavaksi? x86-vinkkejä fibonaccin jonon tulostukseen on jo nyt tässä kaksi kappaletta peräkkäin, joten välillä joku voisi laittaa jotain muuta.
Ihan hyvä vinkki kyllä kymmenjärjestelmän tulostuksesta ja lukemisesta (sekä vinkeissä vähemmän käytetystä at&t-syntaksista).
./fibs 0
toimii kivasti :)
EDIT: Niin no sanotaanhan selostuksessa toki, että argumenttia ei tarkisteta millään lailla.
miten saa käännettyä fasmilla?
Vinkki ei taida suoraan kääntyä fasmilla, koska joka assembleri tuntuu käyttävän vähän omaa syntaksiaan erilaisten vakioiden ja muiden määrittelyssä. Suurin osa suosituimmista assemblereista myös käyttää Intelin syntaksia, eikä AT&T syntaksia. Koodin muuntaminen fasmin käyttämään syntaksiin ei pitäisi olla hankalaa, jos vain osaa AT&T syntaksia ja osaa tarvittaessa as:n manuaalia kurkata. Monet pitävät AT&T syntaksia sekavampana tai vaikeampana, mutta itse pidän siitä enemmän.
Mukava myös tietää, että joku on kokeillut ihan kääntää ja ajaa koodivinkkiäni.