Omówienie niektórych funkcji bibliotecznych
Poniżej przedstawiono opis tylko niektórych funkcji bibliotecznych występujących w kompilatorze SDCC, które w jakiś sposób różnią się od standardu ANSI C.
Funkcje getchar
i putchar
Chociaż w pliku nagłówkowym stdio.h
dostarczanym razem z kompilatorem SDCC znajdują się prototypy funkcji getchar
i putchar
to nigdzie nie zostały one zaimplementowane. Zatem przy ich wywołaniu pojawiają się błędy linkera postaci
?ASlink-Warning-Undefined Global '_putchar' referenced by module 'plik'
Podobne błędy wystepują, gdy wywoływana jest np. funkcja printf_tiny
, która wykorzystuje funkcję putchar
.
Brak implementacji tych funkcji jest świadomym posunięciem twórców kompilatora. W standardzie ANSI C funkcje putchar
i getchar
z założenie operują odpowiednio na standardowym wejściu i wyjściu. O ile z punktu widzenia komputera PC standardowym wejściem jest klawiatura, a wyjściem ekran monitora, to w systemach mikroprocesorowych nie jest to już takie jasne. Mianowicie standardowym wejściem jak i wyjściem może być port szeregowy. Ale również w systemach zaopatrzonych np. w wyświetlacz LCD standardowym wyjściem może być właśnie ten wyświetlacz. Zatem z punktu widzenia pisanego programu funkcja putchar
, będąca podstawową funkcją przesyłania pojedynczych znaków, może operować na różnych wyjściach. Dlatego jej implementację pozostawiono użytkownikom.
Jeśli założymy, że funkcja putchar
ma wysyłać znaki przez port szeregowy mikrokontrolera, to jej implementacja może wyglądać jak poniżej. Należy tylko pamiętać o zgodności jej implementacji z zamieszczonym w pliku nagłówkowym stdio.h
prototypem, tzn. funkcja ta nie powinna zwracać żadnych wartości, a jej jedyny parametr wejściowy powinien być typu char
.
void putchar(char znak) { while(!TI) // oczekiwanie na ustawienie bitu zakończenia.. ; // ..nadawania znaku informacji TI = 0; // zerowanie bitu TI SBUF = znak; // nadanie kolejnego znaku informacji }
Jeśli natomiast za standardowe wyjście w oprogramowywanym systemie mikoprocesorowym uznać wyświetlacz LCD, to implementacja funkcji putchar
może mieć postać jak poniżej. Impelentacja ta zakłada, że:
- istnieje implementacja funkcji
SpradzBitBF
, która w pętli sprawdza bit zajętości sterownika wyświewtlacza, a jej wykonanie kończy się dopiero, gdy sterownik może przyjąć kolejną informację, - sterownik wyświetlacza widziany jest jako komórki zewnętrznej pamięci danych począwszy od adresu 0xF800,
- sterowanie bitami RW oraz RS sterownika wyświetlacza odbywa się poprzez najmłodsze bity adresu.
// określenie adreu sterownika wyświetlacza // patrz "Adresowanie układów we/wy" volatile xdata at 0xF801 unsigned char LCD_ZNAK; ... void putchar(char znak) { // oczekiwanie na możliwość przesłania kolejnej informacji SprawdzBitBF(); // przesłanie znaku do sterownika wyświetlacza LCD LCD_ZNAK = znak; }
Poniżej zebrano nazwy funkcji, które wykorzystują w swojej implementacji funcje putchar
oraz getchar
. Funckja putchar
wykorzystywana jest w implementacjach następujących funkcji:
printf
,printf_small
,printf_fast
,printf_fast_f
,printf_tiny
,vprintf
,puts
,gets
.
Funckja getchar
wykorzystywana jest w następujących funkcjach:
gets
.
Funkcja printf
Funkcje formatujące, jak np. funkcja printf
, ze względu na możliwość umieszczania w ciągu formatującym wielu różnych sekwencji formatujących i ich modyfikatorów zajmują duży obszar pamięci kodu. Prawdopodobnie implementacja tej funkcji w w bibliotekach kompilatora SDCC dla mikrokontrolerów MCS-51 wykorzystuje również dość duży obszar pamięci danych, gdyż w dokumentacji kompilatora znajduje się uwaga o konieczności użycia modelu large
dla tworzonego programu, w którym jest wywoływana niniejsza funkcja.
Przypuszczalnie implementacja funkcji printf
umożliwia stosowanie wszystkich znaków typu określonych w standardzie ANSI C oraz ich modyfikatorów, jednak nie jest to podane w dokumentacji kompilatora. Jedyna informacja dotyczy braku obsługi liczb typu float
.
W praktyce część specyfikatorów typów używana jest częściej niż pozostałe, a niektóre ich modyfikatory prawie wcale, dlatego w kompilatorze SDCC istnieje kilka innych implementacji funkcji printf
.
printf_small
Implementacja printf_small
funkcji printf
pozwala na realizację operacji sformatowanego wyjścia dla sekwencji znaków w łańcuchu formatującym przedstawionych w poniższej tablicy. W polu 'Postać wyjściowa' podano, w jakim zapisie formatowana jest liczba odpowiadająca danej sekwencji formatującej.
Sekwencja formatująca |
Typ argumentu wejściowego |
Postać wyjściowa |
---|---|---|
%d | int | dziesiętna |
%ld | long int | dziesiętna |
%hd | char | dziesiętna |
%x | int | szesnastkowa |
%lx | long int | szesnastkowa |
%hx | char | szesnastkowa |
%o | int | ósemkowa |
%lo | long int | ósemkowa |
%ho | char | ósemkowa |
%c | char | znakowa |
%s | wskaźnikowy | znakowa |
Ze względu na założenie przenośności kodu na inne rodzaje mikrokontrolerów, niniejsza implementacja funkcji printf
została zrealizowana w języku C a następnie skompilowana dla różnych mikrokontrolerów obsługiwanych przez kompilator SDCC. W związku z tym nie jest to raczej optymalna jej implementacja i może zajmować znaczny obszar pamięci kodu.
printf_fast
Specjalną implementacją funkcji printf
dla mikrokontrolerów rodziny MCS-51 jest funkcja printf_fast
. Implementacji tej funkcji dokonano ręcznie w asemblerze, dlatego można spodziewać się, że jest to ona w miarę optymalna.
Analizując kod źródłowy implementacji tej funkcji można dopatrzeć się nastepujących kodów formatujących przedstawionych w poniższej tabeli. Pola oznaczone nawiasami kwadratowymi [] są opcjonalne. Pole [szerokość] odnosi się do modyfikatora określającego minimalną liczbę znaków występującą w postaci wyjściowej po przeprowadzeniu formatowania.
Sekwencja formatująca |
Typ argumentu wejściowego |
Postać wyjściowa |
---|---|---|
%[[0]szerokość]d | int | dziesiętna |
%[[0]szerokość]ld | long int | dziesiętna |
%[[0]szerokość]hd | char | dziesiętna |
%[[0]szerokość]u | unsigned int | dziesiętna |
%[[0]szerokość]lu | unsigned long int | dziesiętna |
%[[0]szerokość]hu | unsigned char | dziesiętna |
%x | int | szesnastkowa |
%lx | long int | szesnastkowa |
%hx | char | szesnastkowa |
%c | char | znakowa |
%[szerokość]s | wskaźnikowy | znakowa |
printf_fast_f
Implementacja funkcji printf
w postaci funkcji printf_fast_f
jest rozwinięciem funkcji printf_fast
o sekwencję formatującą dla liczb typu float
. W poniższej tabeli przedstawiono dostępne modyfikatory dla tej sekwencji.
Sekwencja formatująca |
Typ argumentu wejściowego |
Postać wyjściowa |
---|---|---|
%[szerokość][.precyzja]f | float | dziesiętna |
Pole [szerokość] pozwala na podanie minimalnej liczby znaków przy konwersji do postaci ciągu znakowego, natomiast pole [.precyzja] pozwala określić ilość cyfr po przecinku branych pod uwagę przy konwersji. Domyślnie, przy braku tego pola, przyjmuje się precyzję do 6 cyfr po przecinku.
printf_tiny
Funkcja printf_tiny
jest kolejną odmianą funkcji printf
dostępną w kompilatorze SDCC. Podobnie jak dwie poprzednie zrealizowana jest w asemblerze, co zapewnia małą objętość kodu. Jest ona właściwie okrojoną wersją funkcji printf_fast
, tj. nie umożliwia obsługi liczb typu long
oraz modyfikatora szerokości. Dostępne w tej funkcji sekwencje formatujące przedstawiono w poniższej tabeli.
Sekwencja formatująca |
Typ argumentu wejściowego |
Postać wyjściowa |
---|---|---|
%d | int | dziesiętna |
%u | unsigned int | dziesiętna |
%x | int | szesnastkowa |
%c | char | znakowa |
%s | wskaźnikowy | znakowa |