Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Assembler 80x86 - konwersja liczb na system szesnastkowy U2

haku221 30 Oct 2018 13:58 2877 28
  • #1
    haku221
    Level 9  
    Witam. Dostałem za zadanie napisać program w assemblerze, który zamienia podane przez użytkownika liczby w linii poleceń na 16-bitowy system szesnastkowy (U2). Miałem dopiero 3 zajęcia z assemblera i tak średnio to ogarniam... Oczywiście dostałem jakieś tam przykładowy program, który zamienia podaną już z góry liczbę w systemie dziesiętnym na 8-bitowy system szesnastkowy.
    I właśnie chciałem od tego przykładu sobie powoli zacząć projekt, który muszę zrobić. Jednak już na samym początku pojawił się problem, z którym nie potrafię sobie poradzić...
    Otóż na samym początku chciałem ten przykładowy program przerobić tak, aby zamiast przeliczać podaną już wcześniej w zmiennej stałą liczbę, program pobierał ją od użytkownika, a następnie zamieniał.
    Tak też zrobiłem, pobrałem znak, który zapisał się w rejestrze al, potem przerzuciłem tą liczbę to rejestru bl, ale niestety wyniki nie są poprawne...
    Poniżej znajduje się cały kod z programu. Póki co jest możliwość wprowadzenia tylko liczby jednocyfrowej i nie ma sprawdzenia czy mieści się w przedziale itd..
    Jeśli zamiast mov bl, al pojawi się tam mov bl, [zmienna] to oczywiście wszystko działa jak należy. Jeśli ktoś potrafi mi pomóc to bardzo bym prosił i odpowiedź.

    Code: x86asm
    Log in, to see the code
  • #2
    Dżyszla
    Level 42  
    Nie bardzo rozumiem, czemu by przeniesienie AL do BL miało nie działać? Co pokazuje debbuger w rejestrach po tej operacji? Może wcale w AL nie masz tego, czego oczekujesz? (kod jest niekompletny, więc trudno stwierdzić)
  • #3
    haku221
    Level 9  
    W kodzie nie mam nic więcej, to jest cały kod. W rejestrze al znajduje się liczba, którą wprowadzam. Też najpierw podobnie pomyślałem i dlatego to sprawdziłem wyświetlając potem co tam się znajduje. Wydaje mi się, że problem może być w tym, że ta liczba może być traktowana jako tekst i dlatego te błędne obliczenia. Tylko nie wiem jak zamienić zmienną pobraną z klawiatury, zapisaną w rejestrze al na liczbę :/ W ogóle przerasta mnie to zadanie, od 3 dni próbuję coś wykombinować i nic, w internecie też nic nie mogę znaleźć... Koszmarny jest ten assembler.
  • #4
    Dżyszla
    Level 42  
    To w jaki sposób pobierane jest coś z klawiatury?

    Bez tego to właśnie zgadywanie, co tam w ogóle jest.

    Co do zmiany - przecież robisz to samo w drugą stronę też. Wystarczy odwrócić działanie. Przyjrzyj się tabeli kodów ASCII i wartościom, jakie przyjmują poszczególne znaki.
  • #5
    haku221
    Level 9  
    Przepraszam, znak pobieram poprzez makro, dlatego nie widać tego w kodzie. Liczbę z klawiatury pobieram tak:
    Code: x86asm
    Log in, to see the code
  • #6
    kinggustav
    Level 26  
    Cała trudność polega na wczytaniu liczby ujemnej i zapisaniu jej binarnie. Pokazanie tego w Hex proponuję załatwić 16-znakową tablicą cyfr, indeksowaną połówkami bajta (4 bity). Przy większej liczbie bajtów (potem słów 16 bitowych, itd) pamiętaj o odwrotnej kolejności: młodszy - starszy.
  • #7
    haku221
    Level 9  
    Tylko, że póki co nie potrafię wczytać nawet dodatniej liczby... Chociaż nie, wczytać potrafię, ale operacje na niej źle się wykonują. Nie mam zielonego pojęcia co zrobić aby to zadziałało. W rejestrze bl, na pewno znajduje się cyfra, którą podałem, bo sprawdziłem to, wyświetlając zawartość rejestru. I tak po wprowadzeniu liczby 7 albo 5, wylicza mi, że po zamianie tych liczb na szesnastkowy wynik wyszedł 33... A gdy wprowadzę liczbę 6, to też wyświetla się wynik 33, tyle, że potem jeszcze prosi mnie o wprowadzenie liczby kilka razy. Ewidentnie coś nie działa w tym kodzie jak powinno. A ja niestety nie potrafię tego naprawić, bo nie wiem co jest nie tak...
  • #9
    haku221
    Level 9  
    A mógłbyś mi to jakoś jaśniej wytłumaczyć? Albo jakiś przykład może podać? Bo nie wiem jak mam odjąć wartość od kodu ASCII żeby wyszła cyfra, którą użytkownik wprowadzi :/
  • #10
    User removed account
    Level 1  
  • Helpful post
    #11
    Dżyszla
    Level 42  
    Jeśli kod ASCII znaku '0' to 30h, to ile od 30h trzeba odjąć (SUB), żeby dostać 0? A jeśli '1' to 31h, to odejmując tą samą... takie czary :) Przecież masz dokładnie to samo tylko w drugą stronę cyfra -> znak w ety1.

    Masz tu kilka prostych kodów, w tym także wprowadzanie liczb:
    https://www.dzyszla.pl/download-34.html
  • #12
    haku221
    Level 9  
    Hmm, chyba powoli coś już ogarniam. Jak pobieram liczbę z klawiatury, to program odczytuje to jako znak. Czyli jak wpisuję 7, to dla programu jest to 37h. I żeby teraz zamienić to na liczbę w systemie szesnastkowym, czyli 07, muszę odjąć od tego 37h 30h. Bo zostanie wtedy 07h. I dla każdej liczby jest tak samo, czyli po pobraniu znaku, muszę odjąć od rejestru al 30h, w taki sposób:
    Code: x86asm
    Log in, to see the code


    Tylko, że nadal coś nie działa :/ Po wpisaniu jakiejkolwiek liczby, program pokazuje na ekranie po przeliczeniu 00

    Czy nadal coś źle robię?
  • #13
    Dżyszla
    Level 42  
    Pokaż cały kod obecnie.
    Generalnie jak na razie to masz tylko pobieranie pojedynczego znaku. Jak chcesz pobrać liczbę, to musisz pobrać kilka kolejnych znaków, a wartość sobie wyliczyć. Jak już wartość będzie znana, to wtedy możesz się brać za konwersję.
  • #14
    haku221
    Level 9  
    No tak, tylko najpierw chciałem żeby to działało chociaż dla jednocyfrowej liczby. Wydaje mi się, że łatwiej mi będzie to potem zrozumieć i ogarnąć. Kod zbytnio się nie zmienił:

    Code: x86asm
    Log in, to see the code
  • #16
    haku221
    Level 9  
    Niestety nie mam pojęcia jak odczytywać ten kod w debuggerze. Nikt mi na zajęciach nie pokazywał jak z niego korzystać i o co w nim chodzi. Zrobiłem screeny, ale wydaje mi się, że za dużo i tak nie można się z tego dowiedzieć.

    Assembler 80x86 - konwersja liczb na system szesnastkowy U2 Assembler 80x86 - konwersja liczb na system szesnastkowy U2 Assembler 80x86 - konwersja liczb na system szesnastkowy U2
  • #17
    Dżyszla
    Level 42  
    Wystarczy naciskać F8 idąc po swoim kodzie w głównym oknie i patrzeć na wartości rejestrów procesora po prawej.
    Pokaż co masz bezpośrednio po funkcji 7 przerwania 21h bo to coś dziwnego aż, że wszystkie rejestry są ciągle zerowe.
  • #18
    haku221
    Level 9  
    Po tej funkcji, przekierowało mnie do programu abym wpisał liczbę, to wpisałem 7. Oto screeny, poszedłem też trochę dalej.
    Tylko czemu po wprowadzeniu 7 do rejestru al, w ax znajduje się 737?

    Assembler 80x86 - konwersja liczb na system szesnastkowy U2 Assembler 80x86 - konwersja liczb na system szesnastkowy U2 Assembler 80x86 - konwersja liczb na system szesnastkowy U2

    Dodano po 10 [godziny] 58 [minuty]:

    Przejrzałem jeszcze raz programy, które mi wysłałeś i zmodyfikowałem jeszcze trochę mój kod. Zadeklarowałem zmienną, do której po wprowadzeniu znaku przez użytkownika, zamienią ją na liczbę oraz z rejestru al trafia właśnie do tej zmiennej. I na tej zmiennej potem dalej pracuję. I co się okazało. Wyniki są dobre. Tylko znowu pojawił się kolejny problem :/ Kiedy wpiszę 7, na ekranie pojawia się wynik 07 i wszystko jest w jak najlepszy porządku. Jednak nie la wszystkich liczb program działa. Na niektórych się wysypuje i nie wiem czemu. Wysypał się dla 2 i 3, jednak ku mojemu zdziwieniu za jakiś czas również dla tych liczb program pokazywał normalnie wynik, a w kodzie nic nie zmieniałem.
    Kiedy wpiszę liczbę np. 1 albo 8 to pojawia się takie coś:

    Assembler 80x86 - konwersja liczb na system szesnastkowy U2 Assembler 80x86 - konwersja liczb na system szesnastkowy U2

    A tak teraz wygląda kod programu:
    Code: x86asm
    Log in, to see the code


    Dodano po 3 [minuty]:

    Coś musi być nie tak chyba z tym fragmentem:
    Code: x86asm
    Log in, to see the code


    Zauważyłem, że kiedy zmieniam go np na:
    Code: x86asm
    Log in, to see the code


    Albo:
    Code: x86asm
    Log in, to see the code


    Program za każdym razem zachowuje się inaczej.
  • Helpful post
    #19
    Dżyszla
    Level 42  
    Prawidłowo - po wpisaniu '7' w AL (czyli dwóch ostatnich rejestru AX) znajdzie się 37h. Do BL przenosisz wiec po odjęciu 7. Tyle, że cały kod jest przygotowany do pracy z większymi wartościami, niż jedna cyfra, więc przesuniecie BL w prawo sprawi, że będzie tam 0. To pierwsza część. Dopiero kawałek dalej jeszcze raz przenosisz do BL z AL i tym razem obcinasz górę (AND).
    Jeśli tam nie masz poprawnego wyniku ze swojego kodu, to któraś operacja musiała wyzerować AL. łatwo to w debugerze wychwycisz właśnie.

    Natomiast co do zmiennej - jeśli piszesz ją w nawiasie to tak, jakbyś używał adresu spod niej, a nie traktował jej samej jako wartość - źle w tym przypadku. Poza tym nie potrzebujesz tablicy, a jedynie jednobajtową wartość na ten moment, po prostu DB.
  • #20
    haku221
    Level 9  
    Hmm, bardzo dziwną rzecz zauważyłem. Czy jest na to może jakieś sensowne wyjaśnienie? Bo nie wiem czy jest coś źle w kodzie i dlatego tak się dzieje, czy może po prostu tak już jest i inaczej nie będzie.
    Otóż program przelicza prawidłowo wszystkie liczby jednocyfrowe, które podaję. Jednak za każdym razem musi on zostać skompilowany. Jeśli np. skompiluję program i go uruchomię, to po wpisaniu liczby zamieni ją na system szesnastkowy poprawnie. I kiedy chciałem sprawdzić inną liczbę, to już nie kompilowałem ponownie tego programu, tylko po prostu odpalałem go od razu. I właśnie wtedy program się wysypywał. Natomiast jeśli po przeliczeniu prawidłowo jakiejś liczby, ponownie program skompiluję (tasm i tlink) to się nie wysypie i normalnie wszystko policzy. Jest to dla mnie dosyć dziwne, czy to normalne?

    Dodano po 7 [minuty]:

    Jezu, nie wierzę, że rozwiązanie było tak proste.... Wystarczyło dopisać instrukcję aby program po obliczeniu liczby się zakończył. Teraz wszystko działa jak należy i mogę w końcu przejść dalej :D Macie może jakieś pomysły jak można przejść na konwersję 16 bitową? Żeby po przeliczeniu na ten system szesnastkowy zamiast np. 07 było 0007. Trzeba po prostu dorobić więcej instrukcji podobnych do tych w ety0 i ety2, czy w jakiś inny sposób to się robi?
  • Helpful post
    #21
    Dżyszla
    Level 42  
    Po pierwsze, to musiałbyś zrobić wczytywanie liczb, a nie cyfr. Czyli pętla odczytująca cyfry i wynik zapisująca do rejestru/zmiennej.
    Potem wyświetlanie też najlepiej oprzeć na pętli, bo powtarzanie kodu jest po prostu bez sensu. Wygodnym tutaj może być użycie funkcji ROL w celu przerzucenia 4 najstarszych bitów na najmłodsze pozycje i wymnożenie przez 4 ostatnie bity aby dostać liczbę z zakresu 0-15. Wtedy można dokonać jej konwersji, wyświetlić i znów powtórzyć całą operację. I tak np. 4 razy.
  • #22
    haku221
    Level 9  
    Ok, dziękuję za pomoc :)
    Mam już pętlę odczytującą cyfry. Mógłbyś mi powiedzieć, czy dobrze zapisuję wpisane cyfry do zmiennej liczby2? Bo program zamienia mi tylko pierwszą wprowadzoną cyfrę :/ Tak jakby do tej zmiennej trafiała tylko pierwsza wpisana cyfra, a reszta gdzieś znikała. Przykładowo kiedy wpiszę 12, to wyświetla się 01, kiedy podam 23, program wyświetli 02. Gdy wpiszę 48, program wypisze 04 itd...


    Code: x86asm
    Log in, to see the code
  • #23
    Dżyszla
    Level 42  
    Nie do końca. To wczytywanie umieszcza w tablicy kolejne cyfry. Natomiast wpisanie 10 to nie to samo co 1 i 0, które trzeba przeliczyć.

    Na początek zdefiniuj sobie zmienną np. Word lub DoubleWord (DW). Teraz wczytywanie musi przebiegać tak:
    1. Pobierz znak
    2. Jeśli to Enter - opuść pętlę (to masz i jest ok)
    3. Zmień na cyfrę (to też ok).
    4. Obecną liczbę pomnóż przez 10
    5. Dodaj odczytaną cyfrę.
    6. Wróć do 1.

    Oczywiście jeszcze można by zabezpieczać przed maksymalną długością. Ale idea jest taka - jak wprowadzasz cyfrę, to najpierw jest ona jednością, jak za nią umieszczasz kolejną, to tą obecną trzeba przemnożyć przez 10 i dodać kolejną w miejscu jedności. I tak w kółko - w ten sposób budujesz liczbę, którą potem można łatwo zmieniać na hex biorąc po 4 bity wg zasady opisanej przeze mnie wcześniej (czyli ROR - rotate right o 4 bity, AND na te 4 ostatnie bity i pod operacje jak obecnie - jeśli mniej niż 10 to dodanie 30h, jeśli więcej to dodanie 55(dec) - kod 'A' - 10).
  • #24
    haku221
    Level 9  
    To mnożenie liczby przez 10 ma tak wyglądać?

    Code: x86asm
    Log in, to see the code
  • #25
    Dżyszla
    Level 42  
    Argumentem mogą być tylko rejestry 8, 16 lub 32 bitowy. Argument mnożony jest przez odpowiedni AL, AE, EAX, a wynik umieszczany odpowiednio w AX, DX:AX, EDX:EAX.
    Przykładowo po:
    Code: asm
    Log in, to see the code

    W AX znajdzie się 50 (AX = AH * AL).
  • #26
    haku221
    Level 9  
    Nie za bardzo ogarniam przesunięcia tych cyfr chyba :/
    Zrobiłem coś takiego, tylko teraz zamienia mi tylko cyfrę, która została podana jako ostatnia xd Czyżbym był coraz bliżej rozwiązania?
    Code: x86asm
    Log in, to see the code

    Samo wczytywanie teraz tak wygląda:
    Code: x86asm
    Log in, to see the code
  • #27
    Dżyszla
    Level 42  
    To na przykładzie, chcesz wprowadzić np. liczbę 902:

    0.
    liczba DW 0

    1. Naciskasz 9
    AL = '9'
    AL = '9' - '0' = 9
    liczba = liczba * 10 + AL = 9 (0000 0000 0000 1001 b)

    2. Naciskasz 0
    AL = '0'
    AL = '0' - '0' = 0
    liczba = liczba * 10 + AL = 90 (0000 0000 0101 1010 b)

    3. Naciskasz 2
    AL = '2'
    AL = '2' - '0' = 2
    liczba = liczba * 10 + AL = 902 (0000 0011 1000 0110 b)

    4. Naciskasz Enter - konec

    Teraz wyświetlanie:
    1. ROL liczba, 4
    liczba = 0011 1000 0110 0000 b

    2. BX = liczba
    BL = 0110 0000 b (BH = 0000 0011 b - ale ta część rejestru nie interesuje)
    3. tylko 4 ostatnie bity:
    AND BL, 0fh -> BL = 0000 0000 b

    4. Konwersja na hex i wyświetlenie
    DL = BL
    DL = DL + '0' (pomijam tu dla uproszczenia osobne warunku dla cyfr > 10)
    INT
    Na ekranie: 0

    5. Powtarzamy od punktu 1:
    ROL... liczba = 1000 0110 0000 0011 b
    BL = 0000 0011 b
    Wyświetlasz: 3

    6. Znowu to samo:
    ROL... liczba = 0110 0000 0011 1000 b
    BL = 0000 1000b
    Wyświetlasz: 8

    7. I jeszcze raz
    ROL... liczba = 0000 0011 1000 0110 b
    BL = 0000 0110 b
    Wyświetlasz: 6


    Cały wyświetlony tekst: 0386
    Czyli 902dec = 386hex

    Pamiętaj tylko, że np, operacje mnożenia muszą być na rejestrach, więc trzeba przenosić zmienną do rejestru i zapisywać po wyliczeniu.
  • #28
    haku221
    Level 9  
    Tylko, że jest tutaj niezgodność typów teraz przez to DW

    Code: x86asm
    Log in, to see the code
  • #29
    Dżyszla
    Level 42  
    Przenosisz do rejestrów x. Mnożenie w tym przypadku również musisz wykonać na rejestrze x.
    Choć wynik oczywiście w takim przypadku jest w dx:ax, to jednak możesz śmiało starszą część olać, jeśli wcześniej wprowadzi się tylko zabezpieczenie przed przekroczeniem zakresu. Dodawanie tak samo - na rejestrze x (wcześniej dla pewności możesz wyzerować część h, przez XOR AH, AH).
    Pamiętaj, że rX = rH:rL zawsze, i jak pracujesz na którejś ze stron, to zmienia się wszystko. Tak samo, jak ErX w młodszej części ma zawarty rX (nie ma dostępu do starszej w tryvie 16-bitowym).