Regiszter tartalmának kiírása assemblyben

Regiszter tartalmának kiírása assemblyben
2008-09-02T17:45:09+02:00
2008-10-20T09:01:43+02:00
2022-11-11T07:42:02+01:00
Kirilla
Sziasztok,

Egy elég 1xű kérdésem lenne és gondolom a válasz is az, de ezidáig nem sikerült megoldanom. Assembly-ben szeretném egy regiszter tartalmát kiíratni. Ameddig eljutottam:


# print.s # # This programs prints the number in %ecx # # PARAMETERS: # %ecx - holds the number to print out .include "../linux.s" .section .data num: .long 1 .section .text .globl _start _start: movl $num, %ecx # %ecx = 1 movl $4, %edx # %edx = 4 (4 byte hosszú az %ecx-ben lévő '1') addl $48, %ecx # A számjegyek a 48-as kódtól kezdődnek( ASCII 48 = '0') az ASCII táblában, ezért 48-at hozzáadunk %ecx-hez movl $STDOUT, %ebx # STDOUT = 1 - standard output movl $SYS_WRITE, %eax # SYS_WRITE = 4 int $LINUX_SYSCALL movl $0, %ebx # visszatérési érték movl $SYS_EXIT, %eax # klipési kód int $LINUX_SYSCALL

A "linux.s" a konstansokat tartalmazza, úgy mint pl.

.equ $LINUX_SYSCALL, 0x80 .equ $SYS_WRITE, 4

A probléma alapvetően az, hogy a sys_write-nál az %ecx egy const char* -t vár nekem pedig a karakter ascii kódja van benne. Elvileg letárolhatnám az %ecx értékét egy bufferbe és annak a címét tölteném vissza %ecx-be, de érdekelne, hogy van e ennél frappánsabb megoldás.

Köszi,
Kiri
Mutasd a teljes hozzászólást!
; bemenet eax-ban mov edi,offset str_puff+12 ; ide dolgozunk .if eax&80000000h mov [sign],TRUE neg eax .else mov [sign],FALSE .endif ; lebontas 10-es osztassal mov ebx,10 mov cl,"0" xor edx,edx .while eax!=0 div ebx or dl,cl mov [edi],dl xor edx,edx dec edi .endw
Mutasd a teljes hozzászólást!

  • Ez igy nagyon nem jo.
    A regizterben lebvo numerikus erteket string-e kell alakitanod.

    ciklikus 10-es osztassal maradekhoz "0"=at adva eltarolva pufferba (hatulrol elore tarolva).




    Mutasd a teljes hozzászólást!
  • ; bemenet eax-ban mov edi,offset str_puff+12 ; ide dolgozunk .if eax&80000000h mov [sign],TRUE neg eax .else mov [sign],FALSE .endif ; lebontas 10-es osztassal mov ebx,10 mov cl,"0" xor edx,edx .while eax!=0 div ebx or dl,cl mov [edi],dl xor edx,edx dec edi .endw
    Mutasd a teljes hozzászólást!
  • Szia,

    Köszi a választ. A kulcs akkor ezek szerint az, hogy mindenféleképpen stringé kell alakítanom a sys_write előtt. Én úgy gondoltam, hogy a számjegyeket a stack-be tárolom, mert úgy eleve fordított sorrendben tudom visszaolvasni. Persze így meg kell jegyezni, hol kezdődik és egy \0-t kell a végére írni(gondolom).

    Megpróbálom én is összedobni.

    Jól látom, hogy te HLA-t használsz?

    üdv,
    Tibor

    Mutasd a teljes hozzászólást!
  • Jól látom, hogy te HLA-t használsz?


    Nem.
    MASM32

    hogy mindenféleképpen stringé kell alakítanom


    Igen.
    Tipusos nyelv az assembly. (igen)
    Mutasd a teljes hozzászólást!
  • Bocs, így soha nem használtam ciklust, de jól látom, hogy eax=0 esetén nem ír ki adatot?
    Mutasd a teljes hozzászólást!
  • Így írnám meg:

    ; bemenet eax-ban mov edi,offset str_puff+12 ; ide dolgozunk .if eax&80000000h mov ch,"-" neg eax .else mov ch,"+" ; vagy szóköz .endif ; lebontas 10-es osztassal mov ebx,10 mov cl,"0" cikl: xor edx,edx div ebx or dl,cl mov [edi],dl dec edi or eax,eax jnz cikl mov [edi],ch ; előjel
    Mutasd a teljes hozzászólást!
  • Jol latod.
    Nalam (ahonnan kivagtam ezt a kodot) a "0" ott kerul bele ahol a stringet inicializalom.
    Feltoltom definialhato karakterrel, altalaban " " -al de neha kellenek a vezeto nullak akkor vegig "0"-al. Majd a vegere egy "0" es egy 0 (string vege) kerul.
    Mutasd a teljes hozzászólást!
  • Egy működő, de még messze nem kioptimalizált változatot nekem is sikerült összedobnom. Ennek most örülök.

    .include "../linux.s" .section .data num: .long 131145377 strnum: .ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0" .section .text .globl _start _start: movl num, %eax movl $10, %edi xor %ecx, %ecx counter_loop: xor %edx, %edx divl %edi addl $'0', %edx pushl %edx incl %ecx cmpl $0, %eax je end_counter jmp counter_loop end_counter: movl %ecx, %edi xor %ecx, %ecx read_digits: cmpl %edi, %ecx je end_read_digits popl %edx movb %dl, strnum(,%ecx,1) incl %ecx jmp read_digits end_read_digits: movl $strnum, %ecx movl %edi, %edx movl $STDOUT, %ebx movl $SYS_WRITE, %eax int $LINUX_SYSCALL movl num, %ebx movl $SYS_EXIT, %eax int $LINUX_SYSCALL
    Mutasd a teljes hozzászólást!
  • ez ránézésre tényleg jónak tűnik de még sokat lehet optimalizálni rajta. Arra is gondolj, hogy ebben a formában csak egyszer használható a rutin, mert szemetet hagy a strnum területen; érdemes az end_read_digits: címkénél egy 0 bájtot a végére írni.
    Másik erőteljes optimalizálási lehetőség, hogy ne ragaszkodj a verembe pakoláshoz, a két ciklusodat könnyedén összevonhatod, és egyből a strnum területre írhatod az eredményt.
    Mutasd a teljes hozzászólást!
  • Köszi a biztatást és a tanácsokat.

    Másik erőteljes optimalizálási lehetőség, hogy ne ragaszkodj a verembe pakoláshoz, a két ciklusodat könnyedén összevonhatod, és egyből a strnum területre írhatod az eredményt.


    Ezen én is gondolkodtam, de a stack-et nem csak tárolásra használom, hanem hogy visszafelé tudjam kiszedni a számjegyeket. Ha közvetlen az strnum-ra írnék, tudnom kellene egyből hány digites a szám.

    Alapvetően úgy akartam megoldani, hogy a verembe nem 4byte-ot, hanem 1 byteot az ascii kódot teszem és a verem címét pakolom az %ecx-be, mert így nem kellett volna külön címre másolni, de ezt nem lehetett megcsinálni.(a részleteket nem tudom, van pushb utasítás, de ezen az architektúrán nem fut/támogatott)

    Köszi,
    Kiri
    Mutasd a teljes hozzászólást!
  • Elvileg akkor így nézett volna ki a verem

    esp+3 ...
    esp+2 \0
    esp+1 digit1
    esp digit2

    itt %ecx = %esp és mivel a stack címe csökenő, ezért pont jó is lett volna, mert a mem-ben ez lenne:

    digit2 digit1 \0 .....

    de hát......
    Mutasd a teljes hozzászólást!
  • Visszafelé kell a puffer végétől tenni, ahogy KisJ példája is mutatja.

    szerk. pushb pedig a legtöbb 8-bitesnél szélesebb processzoron nem támogatott, mert nem tudja hatékonyan kezelni a páratlan veremcímet.
    Mutasd a teljes hozzászólást!
  • Sikerült a tanácsok alapján egy cseppet optimalizálnom. Még nem az igazi, de már alakul.


    # int2str.s # Ez a funkcio egy szamot string-ge konvertal # # SIGNATURE: void int2str(int, const char*) __attribute__((cdecl)); # # PARAMETERS: # PARAMETER1 - szam (int) # PARAMETER2 - buffer (const char*) .include "../linux.s" .section .data .equ ST_VALUE, 8 # a szam pozicioja a stackben .equ ST_BUFFER, 12 # a buffer cimenek pozicioja a stackben num: .long 131276 strnum: .ascii "\0\0\0\0\0\0\0\0\0\0\0\0" .section .text .globl _start .type int2str, @function _start: movl $strnum, %eax # %eax = buffer cime pushl %eax # %eax -> stack movl num, %eax # %eax = szam pushl %eax # %eax -> stack call int2str # konvertalo rutin hivasa popl %ecx # %ecx = szam (nincs ra szukseg) popl %ecx # %ecx = modositott buffer cim movl $12, %edx # max 12 bajtra(digitre) szamitunk movl $STDOUT, %ebx # %ebx = 1 (standard output) movl $SYS_WRITE, %eax # %eax = 4 (sys_write) int $LINUX_SYSCALL # interrupt - az %ecx-ben levo cimtol kiirja # a karaktereket amig '0' karaktert nem talal # vagy max 12 digitet movl $0, %ebx # %ebx = 0 program visszateresi ertek movl $SYS_EXIT, %eax # %eax = 1 (sys_exit) int $LINUX_SYSCALL # interrupt - exit int2str: pushl %ebp # elmentjuk az %ebp-t movl %esp, %ebp # a base pointerunk = a stack pointerrel movl ST_VALUE(%ebp), %eax # a szamot betoltjuk %eax-ba movl $10, %edi # %edi = 10, mert 10-zel osztunk movl ST_BUFFER(%ebp), %esi # a buffer cimet betoltjuk %esi-be addl $11, %esi # a buffer veger allitjuk %esi-t movb $0, (%esi) # a buffer vegere egy '0' karaktert irunk conversion_loop: xor %edx, %edx # %edx = 0, ebben lesz a maradek divl %edi # %edx:%eax = %eax / %edi (%eax = egesz, %edx=maradek) addl $'0', %edx # %edx + 48, mert a '0' ascii kodja 48 decl %esi # %esi = %esi - 1 movb %dl,(%esi) # %edx also byte-jat beirjuk %esi cimre cmpl $0, %eax # %eax = 0 ? je end_loop # ha igen, akkor nem kell tovabb osztani jmp conversion_loop # ujabb osztas end_loop: movl %esi, ST_BUFFER(%ebp) # a buffer kezdo cimet modositjuk movl %ebp, %esp # a stack pointer visszaallitjuk popl %ebp # a hivo base pointeret visszaallitjuk ret # visszateres a funkciobol

    Ami még nem annyira tetszik, hogy a paraméterként átadott buffer címét megváltoztatom és egy módosított címet adok vissza.

    új_cím = buffer + buffer_hossz - digitek_szama

    C-ből még nem néztem, hogy megy e, elvileg igen, bár az int2str nem menti le a regisztereket és ez gond lehet.
    Mutasd a teljes hozzászólást!
  • je end_loop jmp conversion_loop

    =

    jne conversion_loop # ujabb osztas
    Mutasd a teljes hozzászólást!
  • Ennek most örülök.


    Lesz még ebböl operácios rendszer...
    Mutasd a teljes hozzászólást!
  • Nekem is tetszik!

    A puffer-eltolás ellen két ötlet:

    1. a végén odébbrakod a puffert (de akkor majdnem jobb a vermes változatod)
    2. fordított sorrendben osztasz (ehhez kell egy táblázat az osztókkal 1 000 000 000, 100 000 000, ..., 1)

    +bónusz:
    haladj a pufferben %esi-vel balról jobbra, ekkor fordított sorrendben képződnek a számjegyek. Utána fordítsd meg a sorrendet:

    #most %esi az utolsó jegy utánra mutat movl ST_BUFFER(%ebp),%edi #ezt persze már az elején megőrizheted %esi-ből reverse_loop: decl %esi movl (%esi),%al xchg (%edi),%al xhcg (%esi),%al inc %edi cmp %edi,%esi jb reverse_loop
    Mutasd a teljes hozzászólást!
  • Még 1x köszönöm a biztató szavakat!!!
    Sajnos, C-ből még nem müxik. Kiszedtem a _start-ot és az int2str-t globállá tettem. Tehát csak a tényleges konvertáló rutin maradt az int2str.o-ban. Csináltam egy teszt c progit, ami ezt a natív funkciót hívja.

    include "stdio.h" void int2str(int, const char*) __attribute__((cdecl)); int main(void) { int num1; printf("Enter number:"); scanf("%d", &num1); char strint[12] = "111"; int2str(num1, strint); printf("%d, %s\n", num1, strint); return 0; }

    Még nem gondoltam át eléggé. Most, ahogy nézem, tök hülyeségnek tünik, hogy a módosított buffer cím visszaíródjon strint-be. Akkor még reszelünk.
    Mutasd a teljes hozzászólást!
  • bónusz2: ez már nagyon furmányos!

    Három regiszter kell, az pont 12 bájt. Az EBX, ECX szabad, sajnos harmadiknak az ESI van, azt a művelethez cserélgetni kell egy olyannal, ami bájtosan is hozzáférhető. Lényeg, hogy ebben a három regiszterben elférnek a számjegyek, minden osztás után 8 bittel léptetni kell, és hozzáírni az új számjegyet. És ha jól építetted fel, akkor a végén egyszerűen a pufferbe írhatod őket.
    Mutasd a teljes hozzászólást!
  • Izé... Kis diszkrepancia van: ha C-ből hívod, akkor C-be kellene visszatérni, az asm rutinod a végén kilép a rendszerbe ($SYS_EXIT)! Másrészt az asm rutin direktben kiiratja az $STDOUT-ra a számot, nem pedig visszaadja a C-be!
    Mutasd a teljes hozzászólást!
  • Szia,
    Mint írtam a "_start" részt kiszedtem, csak a konvertáló rutin van. Ezután újrafordítottam as-sal majd a test.c-t és az int2str.o-t a gcc-vel.
    Mutasd a teljes hozzászólást!
  • Köszi. A bónusz 5let jónak tünik. Nekem is eszembejutott 2 újabb ötlet, de még nem sikerült a végére jutnom, mert jobban át kellene gondolnom.(levezetésként viszont megírtam a cp-t legalább is egy lebutított változatát)

    1. A 3 regiszteres ötleted továbbfejlesztése, hogy a regiszterekben pakolt BCD-ként tárolom le a számot. Egyenlőre ott akadtam el, hogy nem találtam esetlegesen már meglévő binary -> BCD konverziós utasítást.

    2. A koprocit felhasználva a számot lebegőpontos számmá alakítve és normál alakra hozva meglenne a számjegyek száma, ami egyenlő a 10 x-edik hatványával. Így, előre tudnám, hányszor kell osztanom 10-zel és hány karakteres lesz a szám.

    üdv,
    Kiri
    Mutasd a teljes hozzászólást!
  • Közvetlen konverziós utasítás nincs, viszont a koprocesszor ki tud írni BCD számot. Nyilván ez a legegyszerűbb: FILD, aztán FBSTP.
    Mutasd a teljes hozzászólást!
  • Üdv,

    Végre volt egy kis időm.


    # int2str.s .include "linux.s" .section .data num: .int 123456 .section .bss .lcomm bcdval, 10 .lcomm strnum, 20 .section .text .globl _start _start: movl %esp, %ebp fildl num fbstp bcdval movl $9, %ecx movl $0, %edx movl $-1, %esi findnum: # első nem 0-a megkeresése movb bcdval(%esi,%ecx,1), %al cmp $0, %al jne putnum loop findnum putnum: # bcd kibontása és karakterek kiírása movb bcdval(%esi,%ecx,1), %al movb %al, %bl andl $0x000000f0, %eax andl $0x0000000f, %ebx shrl $4, %eax addl $48, %eax addl $48, %ebx movb %al, strnum(,%edx,1) incl %edx movb %bl, strnum(,%edx,1) incl %edx loop putnum endloop: # sortörés és 0 karakter a végére incl %edx movb $'\n',strnum(,%edx,1) incl %edx movb $0, strnum(,%edx,1) movl $strnum, %ecx movl $STDOUT, %ebx movl $SYS_WRITE, %eax int $INT movl %ebp, %esp movl $0, %ebx movl $SYS_EXIT, %eax int $INT

    Ez a megoldás már kezd tecceni. A ciklusokon még biztos lenne mit javítani, talán később.
    Mutasd a teljes hozzászólást!
  • ja. a "putnum" elé még be kell tenni:


    incl %ecx

    mert ha '0' a szám, akkor túlfut a 'bcdval'-on.
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd