Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00053 005186 14053844 na godz. na dobę w sumie
Asembler dla procesorów ARM. Podręcznik programisty - ebook/pdf
Asembler dla procesorów ARM. Podręcznik programisty - ebook/pdf
Autor: Liczba stron: 352
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-9322-1 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> asembler - programowanie
Porównaj ceny (książka, ebook (-20%), audiobook).

Przewodnik programisty!

Jeszcze do niedawna mało kto zdawał sobie sprawę z istnienia takich rozwiązań, jak procesory ARM. Ten stan rzeczy zmieniła inwazja urządzeń mobilnych: tabletów, smartfonów oraz platform takich jak Raspberry Pi. Przed profesjonalnymi programistami pojawiło się nowe wyzwanie — poznanie asemblera platformy ARM.

Jeżeli należysz do tej grupy, trafiłeś na świetną książkę poświęconą temu tematowi. W trakcie lektury zaznajomisz się ze sposobami reprezentacji liczb i znaków oraz modelem programowania ARM7TDMI. Następnie stworzysz swój pierwszy program z wykorzystaniem asemblera oraz poznasz dyrektywy tego języka. Dalsze rozdziały to kolejne elementy programowania w języku asembler. Adresowanie, ładowanie danych, operacje logiczne i arytmetyczne, pętle i instrukcje warunkowe to tylko niektóre z poruszanych zagadnień. Dzięki tej książce zdobędziesz też cenną wiedzę o urządzeniach peryferyjnych oraz obsłudze wyjątków. Książka jest doskonałą lekturą dla wszystkich programistów tworzących oprogramowanie dla procesorów ARM.

Dzięki tej książce:

Odkryj tajniki języka asembler dla procesorów ARM!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: ARM Assembly Language: Fundamentals and Techniques Tłumaczenie: Paweł Gonera ISBN: 978-83-246-9319-1 © 2009 by ARM (UK). All rights reserved. Authorized translation from English language edition published by CRC Press, part of Taylor Francis Group LLC. Polish edition copyright © 2014 by Helion S.A. All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/asarpp Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treści Wprowadzenie ................................................................................................11 Podziękowania ................................................................................................15 Informacja o oprogramowaniu ........................................................................17 Autor ...............................................................................................................19 Rozdział 1. Przegląd systemów komputerowych ...............................................................21 1.1. Wstęp ......................................................................................................21 1.2. Historia architektury RISC .....................................................................23 1.2.1. Początki firmy ARM ...................................................................25 1.2.2. Powstanie ARM Ltd. ...................................................................26 1.2.3. ARM obecnie ..............................................................................28 1.3. Urządzenia liczące ..................................................................................29 1.4. Systemy liczbowe ...................................................................................31 1.5. Reprezentacja liczb i znaków .................................................................34 1.5.1. Reprezentacja liczb całkowitych .................................................34 1.5.2. Reprezentacja zmiennoprzecinkowa ...........................................37 1.5.3. Reprezentacja znakowa ...............................................................39 1.6. Tłumaczenie bitów na rozkazy ...............................................................39 1.7. Narzędzia ................................................................................................41 1.8. Ćwiczenia ...............................................................................................43 Rozdział 2. Model programowania ARM7TDMI ..............................................................47 2.1. Wstęp ......................................................................................................47 2.2. Typy danych ...........................................................................................47 2.3. Tryby procesora ......................................................................................48 2.4. Rejestry ...................................................................................................49 2.5. Rejestry stanu programu .........................................................................51 2.5.1. Bity kontrolne ..............................................................................51 2.5.2. Bity trybu ....................................................................................51 2.6. Tabela wektorów ....................................................................................52 2.7. Ćwiczenia ...............................................................................................53 Rozdział 3. Pierwsze programy ..........................................................................................55 3.1. Wstęp ......................................................................................................55 3.2. Program 1.: Przesuwanie danych ............................................................56 3.2.1. Uruchamianie kodu .....................................................................57 3.2.2. Sprawdzanie zawartości rejestrów i pamięci ...............................58 5 Kup książkęPoleć książkę 6 Spis treści 3.3. Program 2.: Obliczanie silni ...................................................................58 3.4. Program 3.: Zamiana zawartości rejestrów .............................................61 3.5. Wskazówki dla programistów ................................................................61 3.6. Ćwiczenia ...............................................................................................63 Rozdział 4. Dyrektywy i zasady korzystania z asemblera ..................................................65 4.1. Wstęp ......................................................................................................65 4.2. Struktura modułów języka asemblera .....................................................65 4.3. Predefiniowane nazwy rejestrów ............................................................68 4.4. Często używane dyrektywy ....................................................................68 4.4.1. AREA — definicja bloku danych lub kodu .................................69 4.4.2. RN — definicja nazwy rejestru ...................................................70 4.4.3. EQU — definicja symbolu dla stałej numerycznej .....................70 4.4.4. ENTRY — deklaracja punktu wejścia ........................................71 4.4.5. DCB, DCW i DCD — przydział pamięci i określenie zawartości ................................................................71 4.4.6. ALIGN — wyrównanie danych lub kodu do odpowiedniej granicy .............................................................72 4.4.7. SPACE — rezerwacja bloku pamięci ..........................................73 4.4.8. LTORG — przypisanie punktu startowego puli literałów ..........73 4.4.9. END — koniec pliku źródłowego ...............................................74 4.5. Makra ......................................................................................................74 4.6. Pozostałe funkcje asemblera ...................................................................76 4.6.1. Operacje asemblera .....................................................................76 4.6.2. Literały ........................................................................................77 4.7. Ćwiczenia ...............................................................................................77 Rozdział 5. Ładowanie, zapisywanie i adresowanie ..........................................................79 5.1. Wstęp ......................................................................................................79 5.2. Pamięć ....................................................................................................79 5.3. Ładowanie i zapisywanie — instrukcje ..................................................82 5.4. Operandy adresowania ...........................................................................85 5.4.1. Adresowanie preindeksowane .....................................................85 5.4.2. Adresowanie postindeksowane ...................................................86 5.5. Porządek bajtów .....................................................................................88 5.5.1. Zmiana porządku bajtów .............................................................89 5.5.2. Definiowanie obszarów pamięci .................................................90 5.6. Ćwiczenia ...............................................................................................91 Rozdział 6. Stałe i pule literałów .......................................................................................95 6.1. Wstęp ......................................................................................................95 6.2. Schemat rotacji ARM .............................................................................95 6.3. Ładowanie stałych do rejestrów .............................................................98 6.4. Ładowanie adresów do rejestrów .........................................................101 6.5. Ćwiczenia .............................................................................................105 Kup książkęPoleć książkę Spis treści 7 Rozdział 7. Operacje logiczne i arytmetyczne .................................................................107 7.1. Wstęp ....................................................................................................107 7.2. Znaczniki i ich wykorzystywanie .........................................................107 7.2.1. Znacznik N ................................................................................108 7.2.2. Znacznik V ................................................................................108 7.2.3. Znacznik Z ................................................................................109 7.2.4. Znacznik C ................................................................................109 7.3. Instrukcje porównania ..........................................................................109 7.4. Operacje przetwarzania danych ............................................................110 7.4.1. Operacje logiczne ......................................................................111 7.4.2. Przesunięcia i rotacje .................................................................112 7.4.3. Dodawanie i odejmowanie ........................................................117 7.4.4. Mnożenie ...................................................................................119 7.4.5. Mnożenie przez stałą .................................................................120 7.4.6. Dzielenie ...................................................................................121 7.5. Notacja ułamkowa ................................................................................122 7.6. Ćwiczenia .............................................................................................127 Rozdział 8. Pętle i skoki ...................................................................................................131 8.1. Wstęp ....................................................................................................131 8.2. Skoki .....................................................................................................132 8.3. Pętle ......................................................................................................135 8.3.1. Pętle while .................................................................................135 8.3.2. Pętle for .....................................................................................136 8.3.3. Pętle do ... while ........................................................................139 8.4. Więcej na temat znaczników ................................................................139 8.5. Wykonanie warunkowe ........................................................................140 8.6. Kodowanie w linii prostej .....................................................................142 8.7. Ćwiczenia .............................................................................................143 Rozdział 9. Tablice ..........................................................................................................145 9.1. Wstęp ....................................................................................................145 9.2. Tablice wyszukiwania ..........................................................................145 9.3. Tablice skoków .....................................................................................149 9.4. Wyszukiwanie binarne .........................................................................150 9.5. Ćwiczenia .............................................................................................153 Rozdział 10. Podprogramy i stosy ......................................................................................157 10.1.Wstęp ....................................................................................................157 10.2.Stos .......................................................................................................158 10.2.1.Instrukcje LDM i STM ..............................................................158 10.2.2.Stosy pełne, puste, rosnące i malejące .......................................160 10.3.Podprogramy ........................................................................................162 10.4.Przekazywanie parametrów do podprogramów ....................................163 10.4.1.Przekazywanie parametrów przez rejestry ................................163 10.4.2.Przekazywanie parametrów przez referencję ............................165 10.4.3.Przekazywanie parametrów na stosie ........................................167 Kup książkęPoleć książkę 8 Spis treści 10.5.Standard ARM APCS ...........................................................................168 10.6.Ćwiczenia .............................................................................................169 Rozdział 11. Obsługa wyjątków ........................................................................................173 11.1.Wstęp ....................................................................................................173 11.2.Przerwania ............................................................................................173 11.3.Błędy ....................................................................................................174 11.4.Sekwencja wyjątku w procesorze .........................................................175 11.5.Tablica wektorów .................................................................................176 11.6.Handlery wyjątków ..............................................................................179 11.7.Priorytety wyjątków .............................................................................180 11.8.Procedury obsługi wyjątków ................................................................181 11.8.1.Wyjątek Reset ...........................................................................181 11.8.2.Niezdefiniowana instrukcja .......................................................181 11.8.3.Przerwania .................................................................................185 11.8.4.Błędy przerwania .......................................................................194 11.8.5.SWI ...........................................................................................195 11.9.Ćwiczenia .............................................................................................196 Rozdział 12. Urządzenia peryferyjne z odwzorowaną pamięcią ........................................199 12.1.Wstęp ....................................................................................................199 12.2.LPC2104 ...............................................................................................200 12.2.1.Układ UART .............................................................................200 12.2.2.Mapa pamięci ............................................................................200 12.2.3.Konfigurowanie układu UART .................................................202 12.2.4.Zapis danych do UART .............................................................205 12.2.5.Kompletny kod ..........................................................................206 12.2.6.Uruchamianie aplikacji ..............................................................207 12.3.Układ LPC2132 ....................................................................................208 12.3.1.Konwerter C/A ..........................................................................208 12.3.2.Mapa pamięci ............................................................................210 12.3.3.Konfiguracja konwertera C/A ...................................................210 12.3.4.Generowanie fali sinusoidalnej .................................................210 12.3.5.Kompletny kod ..........................................................................212 12.3.6.Uruchamianie kodu ...................................................................214 12.4.Ćwiczenia .............................................................................................214 Rozdział 13. THUMB ........................................................................................................217 13.1.Wstęp ....................................................................................................217 13.2.Instrukcje THUMB ...............................................................................218 13.3.Różnice pomiędzy ARM i THUMB .....................................................220 13.4.Implementacja i użycie THUMB ..........................................................221 13.4.1.Modyfikacje procesora ..............................................................221 13.4.2.Przełączanie się pomiędzy stanem ARM i THUMB .................221 13.5.Kompilacja dla THUMB ......................................................................223 13.6.Ćwiczenia .............................................................................................225 Kup książkęPoleć książkę Spis treści 9 Rozdział 14. Łączenie C i asemblera .................................................................................227 14.1.Wstęp ....................................................................................................227 14.2.Wstawki asemblerowe ..........................................................................227 14.2.1.Składnia wstawek asemblerowych ............................................230 14.2.2.Ograniczenia działania wstawek asemblerowych .....................231 14.3.Asembler wbudowany ..........................................................................232 14.3.1.Składnia asemblera wbudowanego ............................................234 14.3.2.Ograniczenia działania asemblera wbudowanego .....................234 14.4.Wywołania pomiędzy C i asemblerem .................................................234 14.5.Ćwiczenia .............................................................................................236 Dodatek A Zbiór instrukcji ARM V4T ...........................................................................239 Dodatek B Korzystanie z narzędzi Keil ..........................................................................339 B.1. Wstęp ....................................................................................................339 B.2. Tworzenie projektu i wybór urządzenia ...............................................339 B.3. Tworzenie kodu aplikacji .....................................................................341 B.4. Budowanie projektu i uruchamianie kodu ............................................343 Dodatek C Kody znaków ASCII .....................................................................................345 Glosariusz .....................................................................................................347 Odnośniki ......................................................................................................349 Skorowidz .....................................................................................................350 Kup książkęPoleć książkę 10 Spis treści Kup książkęPoleć książkę Rozdział 7 Operacje logiczne i arytmetyczne 7 7.1. WSTĘP Jest to długi rozdział, ale są ku temu powody. Operacje arytmetyczne są prawdopodobnie jednymi z najczęściej używanych typów instrukcji, szczególnie jeżeli pisane oprogramowanie manipuluje dużymi porcjami danych, na przykład przychodzącym sygnałem audio. Algorytmy graficzne, algorytmy przetwarzania mowy, kontrolery cyfrowe oraz algorytmy przetwarzanie dźwięku wymagają dużej liczby obliczeń, więc ważne jest, aby poznać dostępne typy danych oraz sposoby wykonania operacji w możliwie najkrótszym czasie i (lub) z wykorzystaniem najmniejszej ilości pamięci. Zaczniemy od przedstawienia znaczników, zaprezentujemy pod- stawowe instrukcje arytmetyczne, a rozdział zakończymy przeglądem arytmetyki wykorzy- stującej ułamki. Gdy dobrze zrozumiesz koncepcje znajdujące zastosowanie w cyfrowej aryt- metyce, będziesz w stanie przejść do bardziej zaawansowanych funkcji, takich jak funkcje trygonometryczne, wykładnicze oraz pierwiastki. 7.2. ZNACZNIKI I ICH WYKORZYSTYWANIE Jak pamiętasz z rozdziału 2., rejestr Current Program Status Register zawiera znaczniki, dane o bieżącym stanie komputera oraz trybie pracy przedstawione jako wartości bitów pokazane na rysunku 7.1. W górnej części rejestru dostępne są cztery bity pozwalające określić, czy instruk- cja powinna być wykonana warunkowo, czy nie. Znaczniki są ustawiane i kasowane na pod- stawie dwóch operacji: albo instrukcji używanej specjalnie do ustawienia lub wyczyszczenia znaczników, albo instrukcji, która została „poinformowana”, przez dołączenie do jej kodu mne- monicznego litery „S”, o tym, że na podstawie wyniku jej wykonania mają być ustawione bity. Na przykład EORS oblicza różnicę symetryczną, a następnie ustawia znaczniki, ponieważ w tej instrukcji ustawiony jest bit S. Bitu tego można użyć we wszystkich instrukcjach ALU (jed- nostki arytmetyczno-logicznej), więc możemy sterować tym, czy znaczniki są ustawiane, czy nie. Przyjrzyjmy się każdemu znacznikowi z osobna — niektóre są bardzo proste, a niektóre wymagają przemyślenia. 107 Kup książkęPoleć książkę 108 Asembler dla procesorów ARM. Podręcznik programisty RYSUNEK 7.1. Rejestr Current Program Status Register 7.2.1. ZNACZNIK N Znacznik ten jest używany do sprawdzania wyniku ujemnego. Co to znaczy ujemnego? Defi- nicja wyniku ujemnego bazuje na systemie liczb z uzupełnieniem do dwójki, a jak widziałeś w ostatnich kilku rozdziałach, liczba z dopełnieniem do dwójki jest uważana za ujemną, jeżeli jest ustawiony jej najbardziej znaczący bit. Należy jednak zachować ostrożność, ponieważ możemy dodać do siebie dwie liczby dodatnie i otrzymamy wartość z ustawionym najbardziej znaczącym bitem. 7 Przykład 7.1. Dodanie –1 do –2 jest bardzo łatwe, a wynik ma ustawiony najbardziej znaczący bit, jak się tego spodziewaliśmy. W notacji z dopełnieniem do dwójki operacja ta jest reprezen- towana jako: FFFFFFFF + FFFFFFFE ---------- FFFFFFFD Przykład 7.2. Jeżeli dodamy poniższe wartości, to pomimo że składniki są dodatnie w zapisie z dopeł- nieniem do dwójki, suma jest ujemna: 7B000000 + 30000000 ---------- AB000000 co oznacza, że czasami można się pomylić. Na początek zwróć uwagę, że ponieważ naj- bardziej znaczący bit jest ustawiony, konieczne jest ustawienie bitu N, jeżeli nasza instrukcja ADD powinna ustawić znaczniki (jak pamiętamy, nie musi tego robić). Po drugie, jeśli nie korzystamy z liczb z dopełnieniem do dwójki, to prawdopodobnie nie musimy przejmować się wartością bitu N. Na koniec warto pamiętać, że w reprezentacji z dopełnieniem do dwójki suma dwóch liczb dodatnich daje większą liczbę dodatnią, ale powyższy wynik pokazuje, że ta suma liczb dodatnich nie może być reprezentowana za pomocą 32 bitów, więc w efek- cie powstaje przepełnienie ponad dostępną precyzję liczb. Z tego powodu do pracy z war- tościami ze znakiem potrzebujemy więcej znaczników. 7.2.2. ZNACZNIK V Jeżeli przy wykonywaniu takich operacji jak dodawanie lub odejmowanie określimy wartość znacznika V jako operację XOR bitu przeniesienia wchodzącego do najbardziej znaczącego bitu wyniku z bitem przeniesienia wychodzącym z najbardziej znaczącego bitu, to znacznik V Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 109 w precyzyjny sposób wskaże nam przepełnienie przy operacjach ze znakiem. Przepełnienie następuje, gdy wynik dodawania, odejmowania lub porównywania jest większy lub równy 231 lub mniejszy niż –231. Przykład 7.3. Dodajmy do siebie dwie wartości ze znakiem, reprezentowane w postaci dopełnienia do dwójki: A1234567 + B0000000 ---------- 151234567 Wynik nie mieści się w 32 bitach. Co ważniejsze, ponieważ liczby są zapisane w postaci dopełnienia do dwójki, a dodajemy do siebie dwie dosyć duże liczby ujemne, to przy prze- pełnieniu najbardziej znaczący bit 32-bitowego wyniku jest czyszczony (zwróć uwagę na cyfrę 5 w najbardziej znaczącym bajcie wyniku). Przeanalizujmy ponownie przykład 7.2. Gdy dodaliśmy 0x7B000000 do 0x30000000, wynik zmieścił się w 32 bitach. Jednak wynik może być interpretowany jako ujemny, kiedy dodamy dwie liczby dodatnie, więc czy jest to przypadek przepełnienia? Odpowiedź brzmi: tak. 7 7.2.3. ZNACZNIK Z Jest to jeden z najłatwiejszych do zrozumienia znaczników, ponieważ informuje nas, że wyni- kiem operacji było zero, czyli wszystkie 32 bity były ustawione na zero. 7.2.4. ZNACZNIK C Znacznik przeniesienia jest ustawiany, jeżeli wynik dodawania jest większy lub równy 232, jeśli wynik odejmowania jest dodatni lub w wyniku operacji na cyklicznym rejestrze przesuwnym w instrukcji kopiowania albo logicznej. Przeniesienie jest przydatnym znacznikiem, ponieważ pozwala budować operacje o większej dokładności, na przykład dodawania liczb 64-bito- wych, co pokażemy nieco dalej. 7.3. INSTRUKCJE PORÓWNANIA Oprócz użycia bitu S w instrukcji do ustawiania znaczników służą cztery instrukcje, które nie robią nic poza ustawianiem kodów warunku lub sprawdzaniem określonych bitów w reje- strze. Są to: • CMP — porównanie. CMP odejmuje wartość rejestru lub literał od wartości rejestru i aktu- alizuje kody warunku. Za pomocą CMP możemy szybko porównać zawartość rejestru z określoną wartością, na przykład na początku lub na końcu pętli. • CMN — porównanie ujemne. CMN dodaje wartość rejestru lub literał do wartości rejestru i aktualizuje kody warunku. CMN pozwala również szybko sprawdzić zawartość rejestru. Instrukcja ta jest odwrotnością CMP, a asembler będzie zastępował instrukcje CMP, jeżeli jest to właściwe. Na przykład gdy wpiszemy polecenie: Kup książkęPoleć książkę 7 110 Asembler dla procesorów ARM. Podręcznik programisty to asembler wygeneruje CMP r0, #-20 CMN r0, #0x14 • TST — test. TST wykonuje logiczną operację AND z wartością rejestru i aktualizuje kody warunku bez wpływania na znacznik V. Za pomocą TST możemy sprawdzić, czy okre- ślone bity rejestru są wyzerowane lub czy co najmniej jeden bit rejestru jest ustawiony. • TEQ — test odpowiedniości. TEQ wykonuje logiczną operację XOR z wartością rejestru i aktualizuje kody warunku bez wpływania na znacznik V. Za pomocą TEQ możemy sprawdzić, czy dwie wartości są takie same. Składnia tych funkcji jest następująca: instrukcja{ warunek } Rn , operand2 gdzie { warunek } jest jednym z opcjonalnych warunków przedstawionych w rozdziale 8., a operand2 może być rejestrem z opcjonalnym przesunięciem lub wartością. Typowe instruk- cje mogą wyglądać następująco: CMP r8, #0 ; r8==0? BEQ routine ; tak, więc przejdź do routine TEQ r9, r4, LSL #3 Jak pamiętasz, znaczniki kodów warunku są przechowywane w rejestrze Current Program Status Register, razem z informacją o trybie i stanie procesora. Możemy skorzystać z instrukcji MRS (przenieś PSR do rejestru ogólnego) do odczytania znaczników w CPSR i dowolnym SPSR, ponieważ instrukcja ta ładuje kopię całego rejestru stanu procesora do rejestru ogólnego prze- znaczenia. Na przykład poniższe dwie instrukcje MRS r0, CPSR MRS r1, SPSR ładują zawartość CPSR i SPSR do rejestrów odpowiednio r0 i r1. Dzięki temu możemy w dowolny sposób sprawdzać stan znaczników. Nie można jednak wykorzystać rejestru r15 jako docelowego ani nie można odwoływać się do SPSR w trybie User, ponieważ w tym przy- padku on nie istnieje. W dokumencie ARM Architectural Reference Manual (ARM 2007b) zdefiniowano wynik tej operacji jako UNPREDICTABLE. Trzeba pamiętać, że powinniśmy korzystać z kodów warunku razem z instrukcją skoku (B) lub innymi instrukcjami do tworze- nia pętli i warunkowych podprogramów asemblera, bez konieczności odczytywania znaczni- ków, np. tak jak w pokazanej powyżej instrukcji BEQ. Temat ten przedstawiamy dokładniej w rozdziale 8. 7.4. OPERACJE PRZETWARZANIA DANYCH Jak można oczekiwać, każdy mikroprocesor, nawet ten najprostszy, udostępnia podstawowe operacje, takie jak dodawanie, odejmowanie i przesunięcia, na podstawie których można budo- wać bardziej zaawansowane operacje, jak dzielenie, mnożenie i pierwiastkowanie. Mikropro- cesory ARM są zaprojektowane do wykorzystania w zastosowaniach wbudowanych, w których bardzo ważne jest zużycie energii i wielkość układu. W idealnej sytuacji procesor powinien oferować szeroką gamę instrukcji przetwarzania bez użycia zbyt wielu bramek logicznych Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 111 i bez zwiększania powierzchni układu. Dzięki połączeniu cyklicznego rejestru przesuwnego, 32-bitowej jednostki arytmetyczno-logicznej (ALU) oraz sprzętowego układu mnożenia pro- cesor ARM7TDMI oferuje bogaty zestaw instrukcji przy niewielkim zużyciu energii. Przykładowa operacja przetwarzania danych, ADD, wygląda następująco: ADDS{ warunek } r0, r1, operand2 gdzie { warunek } jest jednym z opcjonalnych warunków przedstawionych w rozdziale 8. Drugi operand, operand2 , może być bezpośrednią wartością, rejestrem lub rejestrem ze skojarzo- nym z nim przesunięciem albo rotacją. Ostatnia opcja okazuje się bardzo wygodna, co poka- żemy w kolejnych punktach. Warto też pamiętać, że niedawno składnia została zmodyfikowana na potrzeby UAL (Unified Assembly Language) — w starej wersji kodów mnemonicznych tę instrukcję zapisujemy jako ADD{ warunek }S1. 7.4.1. OPERACJE LOGICZNE ARM obsługuje operacje logiki Boolowskiej wykorzystujące dwa operandy zamieszczone w tabeli 7.1. Choć w poprzednich rozdziałach spotkałeś się już z instrukcją MOV, to warto wiedzieć, że instrukcja MOVN może być użyta do odwrócenia wszystkich bitów w rejestrze, ponieważ jako operandu oczekuje negacji z dopełnieniem do jedynki. Bardzo szybkim sposo- bem na załadowanie reprezentacji liczby –1 w dopełnieniu do dwójki jest logiczne odwrócenie zera, ponieważ 32-bitowa wartość 0xFFFFFFFF to właśnie –1 w notacji z dopełnieniem do dwójki, co można zapisać jako: 7 MOVN r5, #0 ; r5 = –1 w notacji z dopełnieniem do dwójki TABELA 7.1. Operacje logiczne Instrukcja Komentarz AND ORR EOR MOVN BIC Logiczna operacja AND na dwóch operandach. Logiczna operacja OR na dwóch operandach. Logiczna operacja XOR na dwóch operandach. Przeniesienie z odwróceniem — logiczna operacja NOT na wszystkich bitach. Wyczyszczenie bitów — wyczyszczenie wybranych bitów w rejestrze. Przykłady zastosowania tych operatorów są przedstawione poniżej: AND r1, r2, r3 ; r1 = r2 AND r3 ORR r1, r2, r3 ; r1 = r2 OR r3 EOR r1, r2, r3 ; r1 = r2 XOR r3 BIC r1, r2, r3 ; r1 = r2 AND NOT r3 Pierwsze trzy instrukcje są dosyć proste — AND, OR i XOR to podstawowe funkcje logiczne. Czwarta instrukcja jest operacją czyszczenia bitów, która może być używana do wyczyszczenia wybranych bitów w rejestrze. Dla każdego bitu w drugim operandzie wartość 1 czyści odpowiedni z obydwoma formatami instrukcji. 1 Ta książka została wydana w czasie przechodzenia na UAL; najprawdopodobniej spotkasz się Kup książkęPoleć książkę 112 Asembler dla procesorów ARM. Podręcznik programisty bit pierwszego operandu (rejestr), a 0 pozostawia go bez zmian. Zgodnie z formatem instruk- cji przetwarzania danych dla drugiego operandu możemy użyć bezpośrednio wartości. Na przykład: BIC r2, r3, #0xFF000000 powoduje wyczyszczenie górnego bajta rejestru r3 i przeniesienie wyniku do rejestru r2. 7.4.2. PRZESUNIĘCIA I ROTACJE Na rysunku 7.2 jest pokazana część wewnętrznej ścieżki danych procesora ARM7TDMI, w której dane dla instrukcji trafiają na dwie magistrale prowadzące do głównej jednostki aryt- metyczno-logicznej. Tylko jedna z tych magistral przechodzi przez cykliczny rejestr prze- suwny, który jest dedykowanym blokiem sprzętowym pozwalającym na wykonywanie rotacji lub przesunięć danych w prawo albo w lewo. Z powodu tej asymetrii możemy rotować lub przesuwać tylko jeden operand instrukcji, ale jest to zazwyczaj wystarczające. Przy użyciu kilku dodatkowych instrukcji można ominąć to ograniczenie projektowe. W rzeczywistości pomysł umieszczenia cyklicznego rejestru przesuwnego pomiędzy bankiem rejestrów (termin opisujący fizyczne rejestry od r0 do r15) a głównym ALU pozwala na wykorzystanie stałych 32-bitowych w instrukcjach ALU oraz MOV, pomimo że sama instrukcja ma wielkość tylko 32 bitów. Pokazaliśmy to w rozdziale 6. na przykładzie literałów i stałych. 7 RYSUNEK 7.2. Cykliczny rejestr przesuwny procesora ARM7TDMI Typy przesunięć i rotacji możliwych do wykonania przez procesory ARM są przedsta- wione na rysunku 7.3. Dostępne są dwa typy przesunięć logicznych, w których dane są trak- towane jako liczby bez znaku, przesunięcie arytmetyczne, w którym dane są traktowane jako liczby ze znakiem, oraz dwa typy rotacji. Brak rotacji w lewo można wytłumaczyć tym, że rotacja w lewo o m bitów jest tym samym co rotacja w prawo o (32–m) bitów (poza efektem ubocznym zmiany bitu przeniesienia), więc może być realizowana za pomocą tej samej instrukcji. Inną instrukcją, której pozornie brakuje, jest ASL, czyli arytmetyczne przesunięcie w lewo. Jednak szybko staje się jasne, że nie będziemy nigdy potrzebować tej instrukcji, ponie- waż przesunięcia arytmetyczne muszą zachowywać bit znaku, a przesunięcie danych ze znakiem w lewo pozwoli na to, o ile liczba nie ulegnie przepełnieniu. Na przykład liczba –1 w notacji z dopełnieniem do dwójki ma postać 0xFFFFFFFF, a po wykonaniu przesunięcia w lewo Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 113 otrzymujemy 0xFFFFFFFE, czyli –2, co jest prawidłowym wynikiem. Liczba 32-bitowa, na przykład 0x8000ABCD, przesunięta w lewo spowoduje przepełnienie, a w wyniku otrzymamy 0x0001579A, czyli liczbę dodatnią. 7 RYSUNEK 7.3. Przesunięcia i rotacje Jeżeli chcesz przenieść dane bez wykonywania dodatkowej operacji, na przykład dodawania, to należy skorzystać z MOV. Jak pamiętamy z rozdziału 6., instrukcja MOV pozwala przesyłać dane pomiędzy rejestrami, więc dodanie opcjonalnej operacji przesunięcia pozwala nieco ją rozbudować. Przykład 7.4. Poniższe instrukcje ilustrują, jak łatwo pisze się przesunięcia i rotacje. MOV r4, r6, LSL #4 ; r4 = r6 4 bity MOV r4, r6, LSL r3 ; r4 = r6 liczba określona w r3 MOV r4, r6, ROR #12 ; r4 = r6 rotowany w prawo o 12 bitów ; r4 = r6 rotowany w lewo o 20 bitów Wszystkie operacje przesunięcia są wykonywane w jednym cyklu. Wyjątkiem są prze- sunięcia definiowane w rejestrach, które wymagają dodatkowego cyklu, ponieważ w banku rejestrów dostępne są tylko dwa porty odczytu, a wymagany jest dodatkowy odczyt. Przy Kup książkęPoleć książkę 7 114 Asembler dla procesorów ARM. Podręcznik programisty wykonywaniu przesunięć licznik przesunięcia może być albo 5-bitową wartością bez znaku, czyli liczbą od 0 do 31, jak w pierwszym przykładzie, albo dolnym bajtem w rejestrze, jak w drugim przykładzie. Przykład 7.5. Operacje przesunięcia i logiczne mogą być również używane do przenoszenia danych z jed- nego bajta do innego. Załóżmy, że musimy przenieść górny bajt z rejestru r2 i umieścić go w dolnym bajcie rejestru r3. Na początek należy przesunąć w lewo zawartość rejestru r3 o 8 bitów. Do tego celu można użyć dwóch instrukcji: MOV r0, r2, LSR #24 ; pobranie górnego bajta z R2 do R0 ORR r3, r0, r3, LSL #8 ; przesunięcie w górę r3 i wstawienie do r0 Przykład 7.6. Kody Hamminga W latach 40. ubiegłego wieku matematyk Richard Hamming opracował i formalnie zdefi- niował sposoby nie tylko wykrywania błędów w strumieniu bitów, ale również ich kory- gowania. Na przykład jeżeli chcemy przesłać 8 bitów danych z komputera poprzez kanał (którym może być kawałek przewodu, łącze optyczne, a nawet interfejs bezprzewodowy) do odbiorcy, mamy nadzieję, że wartość wysłana zostanie odebrana w dokładnie takiej samej postaci. Jeżeli wystąpią błędy, to musimy o tym wiedzieć. Co bardziej interesujące, jeśli istnieje sposób na korektę błędu bitu, to ta część informacji nie musi być ponownie wysyłana. Od lat 40. zainteresowanie kodami korekcji błędów znacznie wzrosło, a nowo- czesne schematy, takie jak kod Reeda-Solomona, Binary Golay czy BCH, są opisane w publi- kacji Rotha (2006). Zagadnienia teoretyczne związane z tym problemem są dosyć skom- plikowane, ale prosty kod Hamminga jest dosyć łatwy do napisania, więc przedstawimy tu algorytmy pozwalające wykryć do dwóch błędnych bitów w wartości 8-bitowej. Algorytm ten pozwala również skorygować jeden błędny bit. Przeanalizujmy pomysł dodania do wartości bitu nazywanego sumą kontrolną, który wskazuje na parzystość bitów w tej wartości. Na przykład jeżeli mamy liczbę 7-bitową: i policzymy jedynki w wartości, w naszym przypadku 5, to musimy na początku wartości dodać 1, aby ich liczba była parzysta. Naszą nową wartością będzie: 1010111 11010111 Jeżeli dana zostanie przesłana w ten sposób, odbiorca może wykryć błąd, o ile jeden z bitów danych zostanie zmieniony, ponieważ nie będzie się zgadzała parzystość. Zwróć uwagę, że jeżeli dwa bity ulegną zmianie, to nie możemy wykryć błędu, ponieważ parzystość zosta- nie zachowana. Jeden z typów kodów Hamminga może być utworzony przez użycie czterech sum kon- trolnych umieszczonych w strategicznych lokalizacjach. Jeżeli wartość 12-bitowa jest utwo- rzona z 8 bitów danych i czterech sum kontrolnych w pokazany poniżej sposób, to możemy użyć bitów sumy kontrolnej do wykrycia do dwóch błędów w danych i skorygować jeden błędny bit. Oryginalna wartość 8-bitowa d7 d6 d5 d4 d3 d2 d1 d0 Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 115 Zmodyfikowana wartość 8-bitowa 11 d7 10 d6 9 d5 8 d4 7 c3 6 d3 5 d2 4 d1 3 c2 2 d0 1 c1 0 c0 Bity sumy kontrolnej, c3, c2, c1 i c0, są obliczane w następujący sposób: • Bit sumy kontrolnej c0 powinien tworzyć parzystość dla bitów 0, 2, 4, 6, 8 i 10. Inaczej mówiąc, sprawdzamy bit, opuszczamy bit, sprawdzamy bit itd. • Bit sumy kontrolnej c1 powinien tworzyć parzystość dla bitów 1, 2, 5, 6, 9 i 10. Inaczej mówiąc, sprawdzamy dwa bity, opuszczamy dwa bity, sprawdzamy dwa bity itd. • Bit sumy kontrolnej c2 powinien tworzyć parzystość dla bitów 3, 4, 5, 6 i 11. Teraz sprawdzamy cztery bity, pomijamy cztery bity itd. • Bit sumy kontrolnej c3 powinien tworzyć parzystość dla bitów 7, 8, 9, 10 i 11. Dla przykładu wygenerujmy sumy kontrolne dla wartości binarnej 10101100. Pierwszy bit sumy kontrolnej będzie miał wartość 1, ponieważ musi on zapewnić parzystość dla bitów 0, 0, 1, 0 i 0. Korzystając z tej samej metody, ustalamy, że pozostałe bity sumy kontrolnej będą miały wartości: c1 = 1 c2 = 1 c3 = 0 co daje w wyniku wartość 12-bitową 101001101011. Przedstawiony poniżej kod asemblera pozwalający zbudować 12-bitowy kod Hamminga wykorzystuje cykliczny rejestr przesuwny w czasie operacji logicznych. AREA HAMMING, CODE ENTRY ; Używane rejestry: ; R0 — tymczasowy ; R1 — używany do przechowywania adresu danych ; R2 — przechowuje wartość do wysłania ; R4 — tymczasowy main MOV r2, #0 ; czyszczenie rejestru transmisji ADR r1, arraya ; początek stałych LDRB r0, [r1] ; ; obliczenie c0 z użyciem bitów 76543210 ; * ** ** ; parzystość, więc wynik XOR jest wartością c0 ; MOV r4, r0 ; wykonanie kopii EOR r4, r4, r0, ROR #1 ; 1 XOR 0 EOR r4, r4, r0, ROR #3 ; 3 XOR 1 XOR 0 EOR r4, r4, r0, ROR #4 ; 4 XOR 3 XOR 1 XOR 0 EOR r4, r4, r0, ROR #6 ; 6 XOR 4 XOR 3 XOR 1 XOR 0 AND r2, r4, #1 ; tworzenie c0 - R2 ; ; obliczenie c1 za pomocą bitów 76543210 7 Kup książkęPoleć książkę 116 Asembler dla procesorów ARM. Podręcznik programisty 7 ; ** ** * MOV r4, r0 EOR r4, r4, r0, ROR #2 ; 2 XOR 0 EOR r4, r4, r0, ROR #3 ; 3 XOR 2 XOR 0 EOR r4, r4, r0, ROR #5 ; 5 XOR 3 XOR 2 XOR 0 EOR r4, r4, r0, ROR #6 ; 6 XOR 5 XOR 3 XOR 2 XOR 0 AND r4, r4, #1 ; izolowanie bitów ORR r2, r2, r4, LSL #1 ; 7 6 5 4 3 2 c1 c0 ; ; obliczenie c2 za pomocą bitów 76543210 ; * *** MOV r4, r0, ROR #1 ; pobranie bitu 1 EOR r4, r4, r0, ROR #2 ; 2 XOR 1 EOR r4, r4, r0, ROR #3 ; 3 XOR 2 XOR 1 EOR r4, r4, r0, ROR #7 ; 7 XOR 3 XOR 2 XOR 1 AND r4, r4, #1 ; izolowanie bitów ORR r2, r2, r4, ROR #29 ; 7 6 5 4 c2 2 c1 c0 ; ; obliczenie c3 za pomocą bitów 76543210 ; **** MOV r4, r0, ROR #4 ; pobranie bitu 4 EOR r4, r4, r0, ROR #5 ; 5 XOR 4 EOR r4, r4, r0, ROR #6 ; 6 XOR 5 XOR 4 EOR r4, r4, r0, ROR #7 ; 7 XOR 6 XOR 5 XOR 4 AND r4, r4, #1 ; ; budowanie końcowego 12-bitowego wyniku ; ORR r2, r2, r4, ROR #25 ; rotacja w lewo o 7 bitów AND r4, r0, #1 ; pobranie bitu 0 z oryginału ORR r2, r2, r4, LSL #2 ; dodanie bitu 0 do wyniku BIC r4, r0, #0xF1 ; pobranie bitów 3, 2, 1 ORR r2, r2, r4, LSL #3 ; dodanie bitów 3, 2, 1 do wyniku BIC r4, r0, #0x0F ; pobranie górnej czwórki ORR r2, r2, r4, LSL #4 ; r2 zawiera teraz 12 bitów ; z sumą kontrolną done B done ALIGN arraya DCB 0xB5 DCB 0xAA DCB 0x55 DCB 0xAA END Nasza początkowa wartość 8-bitowa znajduje się w pamięci w lokalizacji arraya, skąd jest ładowana do rejestru r0. Szybkim sposobem na wygenerowanie bitu parzystości jest użycie wyniku operacji XOR jako bitu sumy kontrolnej, np. gdy pobierzemy nieparzystą liczbę jedynek i wykonamy na nich operację XOR, wynikiem będzie 1, więc suma kontrolna powinna mieć wartość 1, aby utworzyć parzystą liczbę jedynek. Pierwsza suma kontrolna jest gene- rowana za pomocą kolejnych instrukcji EOR, na oryginalnych danych, wszystkie bity poza 0 są ignorowane. Zwróć uwagę, że pierwsza instrukcja logiczna: Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 117 EOR r4, r4, r0, ROR #1 pobiera oryginalne dane i wykonuje operację XOR bitu 0 z bitem 1 skopiowanych danych przy użyciu jednej instrukcji. Kolejne instrukcje EOR pobierają skopiowane dane i rotują je o odpowiednią liczbę bitów aż do bitu 0. W końcu jesteśmy zainteresowani tylko bitem 0, więc wykonujemy logiczną operację AND końcowego wyniku z 1, aby wyczyścić wszystkie bity poza najmniej znaczącym, ponieważ wykonanie operacji AND z wartością 0 daje 0, a AND z wartością 1 daje oryginalną wartość. Pozostałe sumy kontrolne są obliczane w taki sam sposób, odpowiednie bity są prze- suwane w kierunku bitu 0, a następnie jest wykonywana instrukcja EOR z tymczasowym wynikiem. Końcowa 12-bitowa wartość jest tworzona na podstawie oryginalnej 8-bitowej wartości i czterech sum kontrolnych za pomocą funkcji logicznych. Zwróć uwagę, że rotacja w lewo jest realizowana przy użyciu instrukcji ROR, ponieważ rotacja w lewo o n bitów jest tym samym co rotacja w prawo o (32–n) bitów, dlatego nie ma instrukcji ROL. Końcowa wartość jest umieszczona w rejestrze r2. Aby wykryć błąd w przesłanej wartości, należy sprawdzić cztery bity sumy kontrolnej, c3, c2, c1 i c0. Jeżeli okaże się, że jeden z bitów sumy kontrolnej jest nieprawidłowy (można to zweryfikować przez sprawdzenie bitów 12-bitowej danej), to sama suma kontrolna jest nieprawidłowa. Jeżeli dwa bity sumy kontrolnej są nieprawidłowe, na przykład cn i cm, to pozycja nieprawidłowego bitu, j, może być określona jako: j = (2n+2m)–1 Na przykład jeżeli bity sumy kontrolnej c3 i c2 są nieprawidłowe, to błąd znajduje się w bicie 11. Ponieważ jest to jedyny błąd, może być poprawiony. 7.4.3. DODAWANIE I ODEJMOWANIE Instrukcje arytmetyczne w zbiorze instrukcji ARM obejmują operacje dodawania, odejmowania oraz odwrotnego odejmowania, z których wszystkie są w wersjach z przeniesieniem i bez. ADD r1, r2, r3 ; r1 = r2+r3 ADC r1, r2, r3 ; r1 = r2+r3+C SUB r1, r2, r3 ; r1 = r2–r3 SUBC r1, r2, r3 ; r1 = r2–r3+C–1 RSB r1, r2, r3 ; r1 = r3–r2 RSC r1, r2, r3 ; r1 = r3–r2+C–1 Z fragmentu na temat znaczników pamiętamy, że znacznik przeniesienia może być uży- wany do wskazania, że operacja wygenerowała przeniesienie na najbardziej znaczącym bicie wyniku. Operacje ADC, SUBC oraz RSC wykorzystują ten znacznik przez dodanie znacznika prze- niesienia do operacji. Załóżmy, że chcemy wykonać dodawanie 64-bitowe. Ponieważ rejestry mają wielkość 32 bitów, musimy zapisać każdy ze składników w dwóch rejestrach, suma również będzie przechowywana w dwóch rejestrach. Przykład 7.7. Poniższe dwie instrukcje dodają 64-bitową liczbę całkowitą zapisaną w rejestrach r2 i r3 do innej 64-bitowej liczby całkowitej zapisanej w rejestrach r0 i r1, a wynik umieszczają w rejestrach r4 i r5: 7 Kup książkęPoleć książkę 118 Asembler dla procesorów ARM. Podręcznik programisty ADDS r4, r0, r2 ; dodawanie mniej znaczących słów ADC r5, r1, r3 ; dodawanie bardziej znaczących słów Jak możesz zobaczyć na rysunku 7.4, przeniesienie z dolnej 32-bitowej sumy jest doda- wane do górnej 32-bitowej sumy, co daje końcowy 64-bitowy wynik. 7 RYSUNEK 7.4. Dodawanie 64-bitowe Przykład 7.8. Operacja 64-bitowego odejmowania może być zbudowana podobnie do operacji pokaza- nego powyżej dodawania. Poniższy kod odejmuje dolne połówki wartości 64-bitowych, aktu- alizując znacznik przeniesienia, a następnie odejmuje górne połówki, uwzględniając prze- niesienie: sub64 SUBS r0, r0, r2 ; odejmowanie dolnych połówek, ; ustawienie znacznika przeniesienia SBC r1, r1, r3 ; odejmowanie górnych połówek i przeniesienia Użyliśmy tu dwóch rzadko wykorzystywanych, ale użytecznych instrukcji: RSB i RSC, reali- zujących odwrotne odejmowanie. Instrukcja odwrotnego odejmowania wynika z posiadania cyklicznego rejestru przesuwnego tylko na jednej magistrali pomiędzy bankiem rejestrów a głównym ALU, co jest pokazane na rysunku 7.2. Załóżmy, że chcemy wykonać następującą operację: SUB r0, r2, r3, LSL #2 ; r0 = r2–r3*4 Możemy to zrobić za pomocą tej jednej instrukcji. Co w przypadku, gdy chcemy zmody- fikować (przesunąć) r2, a nie r3 przed wykonaniem odejmowania? Ponieważ odejmowanie nie jest operacją przemienną, czyli: x–y ≠ y–x, gdzie x, y ≠ 0 to rejestr r2 musi być w jakiś sposób umieszczony na magistrali zawierającej rejestr prze- suwny. Zapewnia to operacja odwrotnego odejmowania, w której instrukcje mogą być zapi- sywane w następujący sposób: RSB r0, r3, r2, LSL #2 ; r0 = r2*4–r3 Ta sama instrukcja może być użyta dla uzyskania jeszcze większego efektu, ponieważ drugi operand również może być stałą, więc można odejmować wartość rejestru od stałej, a nie odwrotnie. Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 119 Przykład 7.9. Napiszmy kod asemblera ARM obliczający wartość bezwzględną. Rejestr r0 będzie zawie- rać wartość początkową, a r1 wartość bezwzględną. Pseudoinstrukcja będzie wyglądała tak jak poniżej: ABS r1, r0 Spróbuj użyć tylko dwóch instrukcji (nie licząc instrukcji do zakończenia programu ani dyrektyw). Rozwiązanie Jak wiemy, wartość bezwzględna jest funkcją zwracającą dodatnią wartość argumentu, więc f(x) = |x| zmienia znak argumentu, jeżeli wartość jest ujemna. Potrzebujemy jednego porównania i jednej instrukcji zmieniającej znak: AREA Prog7a, CODE, READONLY ENTRY CMP r1, #0 RSBLT r0, r1, #0 7 done B done END Program wykonuje porównanie z zerem w celu sprawdzenia, czy cokolwiek jest do zro- bienia. Jeżeli argument jest zerem, wynikiem jest zero. Jeżeli argument jest ujemny (LT ozna- cza mniejszy niż zero, co opiszemy dokładniej w rozdziale 8.), to za pomocą odwrotnego odejmowania odejmujemy r1 od zera, w efekcie zmieniając znak. Zwróć uwagę na warun- kowe wykonanie instrukcji RSB, ponieważ wartości dodatnie nie spełnią warunku. 7.4.4. MNOŻENIE Mnożenie binarne jest realizowane obecnie w niemal każdym procesorze, ale jest obarczone pewnym kosztem. Jako operacja jest dosyć częste. Jako blok sprzętowy jest kosztowne, ponie- waż taki blok zajmuje zwykle dosyć dużo miejsca i pobiera dużo prądu w stosunku do pozo- stałych części mikroprocesora. Starsze mikrokontrolery często korzystały z podprogramów sumujących i przesuwających w celu wykonania mnożenia, co pozwalało uniknąć budowania dużych macierzy mnożników, ale były to operacje dosyć powolne. Nowoczesne zwykle wyko- nują mnożenie w jednym lub dwóch cyklach, ale ponownie, z powodów związanych z zarzą- dzaniem energią, jeżeli istnieje sposób, by uniknąć macierzy, kompilator ARM próbuje wytwo- rzyć kod bez instrukcji mnożenia, co pokażemy nieco dalej. O wyborze mikroprocesorów i (lub) układów DSP często decydują możliwości szybkiego wykonywania mnożenia, szczegól- nie w przypadku przetwarzania mowy i sygnałów, analizy sygnału i sterowania adaptacyjnego. W tabeli 7.2 są zamieszczone wszystkie instrukcje mnożenia dostępne w zbiorze instrukcji Version 4T. Instrukcje MUL i MLA pozwalają na wykonanie operacji mnożenia oraz mnożenia i akumulacji, które dają wyniki 32-bitowe. Instrukcja MUL mnoży wartości w dwóch rejestrach, obcina wynik do 32 bitów i zapisuje iloczyn w trzecim rejestrze. Instrukcja MLA mnoży zawartość dwóch rejestrów, obcina wynik do 32 bitów, dodaje wartość trzeciego rejestru do iloczynu i zapisuje wynik w czwartym rejestrze, na przykład: Kup książkęPoleć książkę 7 120 Asembler dla procesorów ARM. Podręcznik programisty MUL r4, r2, r1 ; r4 = r2*r1 MULS r4, r2, r1 ; r4 = r2*r1, następnie ustawia znaczniki MLA r7, r8, r9, r3 ; r7 = r8*r9+r3 TABELA 7.2. Instrukcje mnożenia oraz mnożenia i akumulacji Instrukcja Komentarz MUL MLA SMULL UMULL SMLAL UMLAL Mnożenie 32 bitów przez 32 bity z 32-bitowym iloczynem. Mnożenie 32 bitów przez 32 bity, wynik dodawany do 32-bitowej wartości akumulowanej. Mnożenie ze znakiem 32 bitów przez 32 bity z 64-bitowym iloczynem. Mnożenie bez znaku 32 bitów przez 32 bity z 64-bitowym iloczynem. Mnożenie ze znakiem 32 bitów przez 32 bity, wynik dodawany do 64-bitowej wartości akumulowanej. Mnożenie bez znaku 32 bitów przez 32 bity, wynik dodawany do 64-bitowej wartości akumulowanej. Zarówno MUL, jak i MLA mogą opcjonalnie ustawiać znaczniki kodu warunku N i Z. Dla mnożenia dającego tylko 32-bitowy wynik nie istnieje rozróżnienie pomiędzy mnożeniem ze znakiem i bez znaku. Jedynie najmniej znaczące 32 bity wyniku są zapisywane w rejestrze docelowym, a znak operandów nie wpływa na wartość. Długie instrukcje mnożenia generują wynik 64-bitowy. Pozwalają one na pomnożenie dwóch rejestrów i zapisanie 64-bitowego wyniku w trzecim i czwartym rejestrze. SMLL oraz UMULL są długimi instrukcjami mnożenia ze znakiem i bez znaku: SMULL r4, r8, r2, r3 ; r4 = bity 31 – 0 z r2*r3 ; r8 = bity 63 – 32 z r2*r3 UMULL r6, r8, r0, r1 ; {r6, r8} = r0*r1 Poniższe instrukcje mnożą wartości dwóch rejestrów, dodają wartość 64-bitową z trzeciego i czwartego rejestru i zapisują 64-bitowy wynik w trzecim i czwartym rejestrze: SMLAL r4, r8, r2, r3 ; r4 = bity 31 – 0 z r2*r3+{r4, r8} ; r8 = bity 63 – 32 z r2*r3+{r4, r8} UMLAL r5, r8, r0, r1 ; {r5, r8} = r0*r1+{r5, r8} Wszystkie długie instrukcje mnożenia mogą opcjonalnie ustawiać znaczniki kodu warunku N i Z. Jeżeli jakikolwiek operand źródłowy jest ujemny, to wpływa to na najbardziej znaczące 32 bity wyniku. 7.4.5. MNOŻENIE PRZEZ STAŁĄ W naszym omówieniu przesunięć i rotacji pokazaliśmy, że wewnętrzny cykliczny rejestr prze- suwny w ścieżce danych procesora ARM7TDMI może być używany w połączeniu z innymi instrukcjami, takimi jak ADD lub SUB, co daje w efekcie darmowe mnożenie. Funkcja ta jest w pełni wykorzystywana w przypadkach, gdy określone mnożenia są realizowane za pomocą cyklicznego rejestru przesuwnego, a nie tablicy mnożników. Weźmy pod uwagę przypadek mnożenia liczby przez potęgę dwójki. Można to zrealizować za pomocą wyłącznie instrukcji MOV razem z przesunięciem, np.: MOV r1, r0, LSL #2 ; r1 = r0*4 Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 121 Co można zrobić, gdy chcemy pomnożyć dwie liczby, z których jedna nie jest potęgą dwójki, jak np. pięć? Przeanalizujmy poniższą instrukcję: ADD r0, r1, r1, LSL #2 ; r0 = r1+r1*4 Jest to operacja analogiczna do pobrania wartości, przesunięcia jej w lewo o dwa bity (co daje mnożenie przez cztery), a następnie dodania oryginalnej wartości do iloczynu. Inaczej mó- wiąc, mnożenia liczby przez pięć. Dlaczego wykonujemy to w ten sposób? Weźmy pod uwa- gę rozmiar macierzy mnożników zaznaczonej na rysunku 7.5 w mikroprocesorze ARM10200 i zużycie energii. W zastosowaniach o niskim poborze prądu często wymagane jest wykorzy- stanie każdej możliwej sztuczki, aby oszczędzić energię: wyłączenie nieużywanej logiki, wyłączenie pamięci podręcznej lub całego procesora, gdy nie jest potrzebny, ograniczenie napięć i częstotliwości itd. Dzięki użyciu wyłącznie 32-bitowego sumatora i cyklicznego reje- stru przesuwnego procesory ARM mogą generować mnożenia przez 2n, 2n–1 i 2n+1 w jednym cyklu bez konieczności korzystania z macierzy mnożników. Potencjalnie pozwala to również na skrócenie czasu wykonania. Na przykład: RSB r0, r2, r2, LSL #3 ; r0 = r2*7 będzie realizować mnożenie przez 7 przez pobranie rejestru r2, przesunięcie w lewo o 3 bity, co daje mnożenie przez 8, a następnie odjęcie rejestru r2 od iloczynu. Zwróć uwagę, że użyta tu została instrukcja odwrotnego odejmowania, ponieważ zwykłe odejmowanie da niewłaściwy wynik. Przez połączenie operacji mnożenia, na przykład przez 5, a następnie przez 7, można tworzyć większe stałe. Spójrz na poniższy przykład, pokazujący, że możliwe jest mnożenie argumentów niebędących potęgami dwójki: ADD r0, r1, r1, LSL #1 ; r0 = r1*3 SUB r0, r0, r1, LSL #4 ; r0 = (r1*3)–(r1*16) = r1*–13 ADD r0, r0, r1, LSL #7 ; r0 = (r1*–13)+(r1*128) = r1*115 7.4.6. DZIELENIE Dzielenie binarne jest tematem, który bardzo szybko staje się skomplikowany. Większość rdzeni ARM nie zawiera sprzętowego układu dzielenia całkowitego (Cortex-M3 taki oferuje), zwy- kle dlatego, że dzielenie jest rzadko używane (w związku z tym może być realizowane przez podprogramy). Ponadto układ dzielenia zajmuje zbyt dużo miejsca i wykorzystuje zbyt dużo energii, aby sensowne było umieszczanie go we wbudowanym procesorze, poza tym istnieją sposoby, aby całkowicie uniknąć dzielenia. Nie można powiedzieć, że brakuje dobrych pod- programów. Przy wybieraniu procedury dzielenia należy wziąć pod uwagę takie czynniki jak typ danych (dane ułamkowe czy całkowite), wymagana wydajność algorytmu oraz rozmiar kodu potrzebnego do realizacji algorytmu. Jeżeli chcesz zapoznać się dokładniej z tym tematem, polecamy pozycję Slossa, Symesa i Wrighta (2004). Biblioteki uruchomieniowe zawierają procedury dzielenia, więc jeżeli piszesz w C lub C++, kompilator zajmie się za Ciebie algo- rytmami dzielenia. My skupiamy się na asemblerze, więc przedstawimy tu przynajmniej naj- prostszy przypadek. Kod przedstawiony na rysunku 7.6 jest odmianą algorytmu przesuwająco-odejmującego i może być używany do dzielenia dwóch wartości 32-bitowych bez znaku; w rejestrze Ra znajduje się dzielna, w rejestrze Rb dzielnik. Po wykonaniu procedury w Rc znajduje się iloraz, a w Ra reszta z dzielenia. 7 Kup książkęPoleć książkę 122 Asembler dla procesorów ARM. Podręcznik programisty 7 RYSUNEK 7.5. Zdjęcie układu ARM10200 z zaznaczoną macierzą mnożników Przykład 7.10. Korzystając z procedury z rysunku 7.6, możemy podzielić 150 przez 120. Ponieważ warto- ści są małe, do ich załadowania do rejestrów Ra i Rb możemy użyć dwóch instrukcji MOV: MOV Ra, #0x96 ; ładowanie rejestru r1 MOV Rb, #0x78 ; ładowanie rejestru r2 Po uruchomieniu kodu rejestr r3 będzie zawierał wartość 1 (iloraz), a rejestr r1 wartość 0x1E (reszta z dzielenia). 7.5. NOTACJA UŁAMKOWA Jednym z pierwszych problemów, jakie powstają przy nauce asemblera, jest wykorzystanie w programie takich wartości jak e lub π. Najprawdopodobniej napotkasz też w kodzie takie wartości jak √2, a przecież procesor ARM operuje tylko na 32-bitowych wartościach całko- witych, o ile nie jest dostępna jednostka zmiennoprzecinkowa. A może nie? Jak przedstawiliśmy Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 123 7 RYSUNEK 7.6. Prosty algorytm dzielenia w rozdziale 1., procesor operuje na danych, surowych liczbach, wzorcach bitowych. Te wzorce bitowe są interpretowane przez programistę, a nie przez procesor (o ile nie powiemy mu jasno, że ma to zrobić). Załóżmy, że w rejestrze r3 mamy wartość 32-bitową 0xF320ABCD. Czy jest to liczba dodatnia, czy ujemna? W normalnych warunkach po prostu tego nie wiemy. Możesz ją zinterpretować jako dziesiętną wartość −215 962 675, jeżeli zastosujesz reprezen- tację z dopełnieniem do dwójki. Możesz ją też zinterpretować jako 4 079 004 621, jeśli potrak- tujesz ją jako liczbę bez znaku. Decyzja często zależy od algorytmu i typu operacji arytme- tycznych wykonywanych na tych liczbach. Normalnie program oczekuje danych w określonej postaci i odpowiednio do tego je interpretuje — jeżeli procedura filtra adaptacyjnego operuje na liczbach ze znakiem, to programista musi traktować wynik jako wartość ze znakiem. Kontynuując tę argumentację, możemy zadać pytanie, gdzie jest przecinek dziesiętny w 0xF320ABCD? Inaczej mówiąc, gdzie zaczyna się część całkowita tej liczby, a gdzie jest część ułamkowa? Czy ta liczba ma część ułamkową? Również w tym przypadku odpowiedź Kup książkęPoleć książkę 124 Asembler dla procesorów ARM. Podręcznik programisty zależy od programisty. Użyteczną techniką jest przedstawianie problemu na małych warto- ściach, a następnie uogólnianie do liczb 32-bitowych. Jeżeli liczba 1011 jest traktowana jako liczba bez znaku, to w postaci dziesiętnej ma war- tość 8+2+1 = 11. Jeśli jednak założymy, że przecinek dziesiętny jest umieszczony przed ostatnią cyfrą, jak jest to pokazane na rysunku 7.7, to wartość 101,1 będzie równa 4+1+0,5 = 5,5. W tej reprezentacji mamy tylko dwie możliwe wartości części ułamkowej — 0 lub 0,5, co nie jest szczególnie użyteczne. Jeżeli przesuniemy przecinek dziesiętny jeszcze o jedno miejsce w lewo, liczba będzie miała postać 10,11, czyli 2+0,5+0,25 = 2,75 przy podstawie 10. Zwróć uwagę, że po prawej stronie przecinka dziesiętnego mamy dwa bity, co daje nam cztery możliwe wartości ułamkowe: 0, 0,25, 0,5 i 0,75. Teraz rozdzielczością ułamka jest 0,25, inaczej mówiąc, różnica pomiędzy dowolnymi dwiema wartościami ułamkowymi będzie nie mniejsza niż 0,25. Jeżeli przesuniemy przecinek dziesiętny całkowicie w lewo, otrzymamy liczbę 0,1011, czyli 0,5+0,125+0,0625 = 0,6875 przy podstawie 10. Możliwość tworzenia wartości ułamkowych jest nadal ograniczona, ale rozdzielczość naszego ułamka wynosi teraz 0,0635, co jest znacz- nym usprawnieniem. 7 RYSUNEK 7.7. Reprezentacja binarna Jak pamiętasz z rozdziału 1., jeżeli liczba n jest reprezentowana jako m-bitowa w repre- zentacji z dopełnieniem do dwójki, gdzie b to wartości poszczególnych bitów, to wartość tej liczby przy podstawie 10 może być wyliczona jako: m  i b  m b i m 1   n i 2 2 1   2  0 więc liczba 8-bitowa 101100102 zapisana w postaci dopełnienia do dwójki ma wartość: –27+25+24+21 = –78 Jednak jak wspominaliśmy, pozycja przecinka dziesiętnego w dowolnej reprezentacji zależy od programisty. Załóżmy, że ta 8-bitowa liczba została podzielona przez 27. Wzorzec bitowy jest identyczny — interpretacja jest inna. Okazuje się, że przy podstawie 10 liczba n może być obliczona jako: n = –1+2–2+2–3+2–6 = –0,609375 W zasadzie możemy podzielić każdą liczbę m-bitową przez 2m–1, otrzymując tylko ułam- kową wartość n, taką jak: –1 ≤ n ≤ (1–2–(m–1)) Wracając do naszego pytania na temat e i π, jeżeli chcemy reprezentować liczby zawiera- jące tylko część ułamkową, możemy przeskalować taką liczbę przez mniej niż 2m–1. Załóżmy, Kup książkęPoleć książkę Operacje logiczne i arytmetyczne 125 że mamy 16 bitów i chcemy w obliczeniach użyć e. Wiemy, że do reprezentacji części całkowi- tej liczby potrzebujemy co najmniej dwóch bitów (ponieważ liczba ta wynosi 2,71828…), więc użyjemy co najwyżej 13 bitów po lewej stronie przecinka, zakładając, że najbardziej zna- czący bit jest bitem znaku. W literaturze jest to czasami nazywane notacją Q, w której nasza liczba będzie nazywana liczbą Q13. Na szczęście zasady notacji Q są proste. Liczby doda- wane i odejmowane w tej notacji powinny zawsze mieć wyrównane miejsca dziesiętne, więc Qn+Qn = Qn. Gdy dwie liczby są mnożone, Qn×Qm, to wynik będzie w formacie Q(n+m). Aby zapisać e w notacji Q13, bierzemy wartość e, mnożymy ją przez 213, a następnie konwertujemy wynik na postać szesnastkową (przyda się kalkulator). Mamy więc: e×213 = 22 268,1647 = 0x56FC Zwróć uwagę, że na postać szesnastkową konwertujemy tylko część całkowitą. Jeżeli liczba jest
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Asembler dla procesorów ARM. Podręcznik programisty
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: