eXec.plMAGAZYN UŻYTKOWNIKÓW KOMPUTERÓW AMIGA
MAGAZYN UŻYTKOWNIKÓW KOMPUTERÓW AMIGA

poniedziałek, 20. sierpnia, 2018, 05:38

Dodano: 2013-09-15, Autor: Spot (tłumaczenie - Konrad Rojek), Kategoria: Oprogramowanie, Liczba wyświetleń: 5152

A A A

Spota kurs programowania dla początkujących, cz. 2

Część 1

  1. Po co mam to czytać? Przecież nie potrafię programować!
  2. Co portować?
  3. Dostęp do aktualnego SDK
  4. Podstawy
  5. Konfigurowanie środowiska
  6. Konfigurowanie cross-kompilatora
  7. Błędy konfigurowania środowiska
  8. Wyjaśnienie komunikatów o błędach

Część 2

  1. Naprawianie błędów
  2. Błędy linkowania?
  3. Konwersja ścieżek unixowych do amigowych
  4. Problemy 'LValue'
  5. Obsługa właściwej kolejności bajtów (ENDIAN)
  6. Definicje
  7. Sztuczki i kruczki

Część 3

  1. Konwersja windowsowego makefile na amigowy
  2. Naprawianie źródeł
  3. Sprawdzanie obecności bibliotek
  4. Tworzenie GUI w oparciu o Emperor (ALPHA!)
  5. Kompilowanie aplikacji SDL/GL
  6. Dodawanie muzyki do aplikacji SDL
  7. POMÓŻ MI!
  8. Kontakt
  9. Podziękowania
  10. SPOTa Wyzwanie Portowania
  11. WIP

Naprawianie błędów

Jeśli nie ma tu stosownej odpowiedzi, warto skorzystać z Google. W wielu przypadkach, na przykład komunikatu pokroju:

warning: implicit declaration of function 'strlen'

można tak znaleźć właściwe rozwiązanie. Precyzując ten przykładowy problem i komunikatów typu:

 warning: implicit declaration of function 'strlen'
 warning: incompatible implicit declaration of built-in function 'strlen'

Ostrzeżenie te oznaczają brak odpowiedniej deklaracji 'include' w kodzie źródłowym. Deklarację 'include' (tutaj: dla 'strlen') można odnaleźć w następujący sposób:
- przejść w powłoce (Shell) do SDK: (cd SDK:)
- wpisać polecenie 'grep -R strlen'

Polecenie to wypisze bardzo dużą ilość informacji, warto z tego powodu zainstalować KingCon. Pliki nagłówkowe znajdują się jedynie w kilku lokalizacjach, wystarczy przeszukać następujące:

  • SDK:Local/Newlib/Include
  • SDK:Local/Common/Include
  • SDK:Local/Clib2/Include
  • SDK:newlib/Include
  • SDK:Clib2/Include

Dla tych bardziej 'leniwych' czytelników poniżej zamieszczone zostały wyjaśnienia najbardziej powszechnych komunikatów:

- nazwa 'close', 'write' itp. nie zostały zadeklarowane w danym kontekście

 error: 'close'  was not declared in this scope
 error: 'write'  was not declared in this scope
 error: 'read'   was not declared in this scope
 error: 'chdir'  was not declared in this scope
 error: 'lseek'  was not declared in this scope
 error: 'usleep' was not declared in this scope

rozwiązanie:

#include <unistd.h>

- nazwy 'cout' i 'endl' nie zostały zdefiniowane w danym kontekście

 error: 'cout' was not declared in this scope
 error: 'endl' was not declared in this scope

rozwiązanie:

#include <stdlib.h> #include <iostream.h>

- nazwa 'nanosleep' nie została zadeklarowana w danym kontekście

error: 'nanosleep' was not declared in this scope

rozwiązanie:

#ifdef __amigaos4__ #include <pthread.h> #define nanosleep(s, n) usleep(((s)->tv_sec)*1000000 + ((n)->tv_nsec)/1000) #endif

- niekompatybilna, niejawna deklaracja funkcji 'exit' oraz 'abort', brak pliku 'malloc.h'

 warning: incompatible implicit declaration of built-in function 'exit'
 warning: incompatible implicit declaration of built-in function 'abort'
error: malloc.h: No such file or directory
 

rozwiązanie:

#include <stdlib.h>

usunięcie linii

#include <malloc.h>

- niekompatybilna, niejawna deklaracja funkcji 'memset', 'strcpy' oraz 'strrchr'

 warning: incompatible implicit declaration of built-in function 'memset'
 warning: incompatible implicit declaration of built-in function 'strcpy'
 warning: incompatible implicit declaration of built-in function 'strrchr'

rozwiązanie:

#include <string.h>

- niekompatybilna, niejawna deklaracja funkcji 'strlen' oraz 'memcpy'.

- nazwa 'strcmp' nie została zadeklarowana, brak pliku 'memory.h''.

warning: implicit declaration of function 'strlen'
warning: incompatible implicit declaration of built-in function 'strlen'
warning: implicit declaration of function 'memcpy'
warning: incompatible implicit declaration of built-in function 'memcpy'
error: 'strcmp' was not declared in this scope
error: memory.h: No such file or directory

rozwiązanie:

#include <string.h>

oraz usunąć linię:

#include <memory.h>

- niezdefiniowane odniesienie do obiektu 'ntohl'

error: undefined reference to `ntohl'

rozwiązanie:

#include <netinet/in.h>

- nazwa 'SHUT_RDWR' nie została zadeklarowana w danym kontekście.

error: 'SHUT_RDWR' was not declared in this scope

rozwiązanie:

#define SHUT_RDWR 2

- nazwa 'execl' nie została zadeklarowana w danym kontekście

error: 'execl' was not declared in this scope

rozwiązanie:

#include <process.h>

- niekompatybilna, niejawna deklaracja funkcji 'printf'

 warning: incompatible implicit declaration of built-in function 'printf'

rozwiązanie:

#include <stdio.h>

- niezadeklarowana nazwa 'int64_t' (pierwsze użycie)

 error: 'int64_t' undeclared (first use in this function)

rozwiązanie:

#include <stdint.h>

- funkcja '::main' musi zwracać 'int'

 error: '::main' must return 'int'

rozwiązanie, zmiana:

void main()

na

int main()

- nazwa '_SSTRING" nie została zadeklarowana w danym kontekście

error: '__STRING' was not declared in this scope

rozwiązanie:

#define __STRING(x) #x

- niejawna deklaracja funkcji 'srandom' oraz 'random'

warning: implicit declaration of function 'srandom'
warning: implicit declaration of function 'random'

rozwiązanie:
W bibliotece newlib nie ma funkcji 'random()' oraz 'srandom()'. Można teoretycznie użyć w zamian 'rand()' i 'srand()'. Nie są one w pełni zgodne, ale powinny działać. Zamianę można dokonać ręcznie, zmieniając nazwy funkcji w źródłach, albo poprzez dopisanie definicji:

#define srandom srand

- niejawna deklaracja funkcji 'execvp'

 warning: implicit declaration of function 'execvp'

rozwiązanie:
Pomaga linkowanie z opcją -lunix i clib2.

- niejawna deklaracja funkcji "MyRemove'

 warning: implicit declaration of function 'MyRemove'

rozwiązanie:

#define MyRemove Remove

- nazwy 'u_int8_t', 'u_int16_t' oraz 'u_int32_t' nie zostały zadeklarowane

error: 'u_int8_t' has not been declared
error: 'u_int16_t' has not been declared
error: 'u_int32_t' has not been declared

rozwiązanie:

#define u_int8_t uint32_t #define u_int16_t uint32_t #define u_int32_t uint32_t #include <machine/types.h> #include <stdint.h>

- nazwa 'sqrtl' nie została zadeklarowana w danym kontekście

 error: 'sqrtl' was not declared in this scope

rozwiązanie:

#define sqrtl sqrt

Powyższe rozwiązanie zmniejsza precyzję wyniku. Jest to szczególnie ważne, jeśli faktycznie oczekiwane jest zwrócenie wartości o wysokiej precyzji, tj. typu 'long double' (zamiast 'double').
By poprawnie obsłużyć ten komunikat, należy do pliku go generującego dodać następującą funkcję:

static long double sqrtl(long double x) { long double delta, y; int exponent; if (isnanl (x)) return x; if (x < 0.0L) return (long double) sqrt(-1); if (x + x == x) return x;
   frexpl (x, &exponent);
   y = ldexpl (x, -exponent / 2);
do { delta = y; y = (y + x / y) * 0.5L; delta -= y; } while (delta != 0.0L); return y; }

- niezadeklarowana nazwa 'MAXPATHLEN' (pierwsze użycie)

 error: 'MAXPATHLEN' undeclared (first use in this function)

rozwiązanie:

#include <sys/unistd.h>

- stała MAX_PATH nie została zdefiniowana (I)

error: MAX_PATH not defined.

rozwiązanie:

#include <sys/limits.h>

- stała MAX_PATH nie została zdefiniowana (II)

 error: MAX_PATH not defined.

rozwiązanie:

#define MAX_PATH 256

można też połączyć powyższe w następujący ciąg:

 #ifndef PATH_MAX
 #  ifdef MAX_PATH
 #    define PATH_MAX MAX_PATH
 #  else
 #    define PATH_MAX 256
 #  endif
 #endif

 #ifndef MAXPATHLEN
 #define MAXPATHLEN PATH_MAX
 #endif

- konieczność konwersji 'float' na typ nie sklararny 'fix'

 error: conversion from 'float' to non-scalar type 'fix' requested

rozwiązanie:

#include <math.h>

- tryb pełnoekranowy nie działa w portowanej grze SDL?

rozwiązanie:
spróbuj zamienić SDL_HWSURFACE with SDL_SWSURFACE.

- nazwa 'TankFire::' została już zakwalifikowana jako przestrzeń nazw.

error: extra qualification 'TankFire::' on member 'TankFire'

rozwiązanie:
dodanie opcji -fpermissive do CFLAGS w makefile.

- redefinicja struktur 'timespec' oraz 'option'

error: In file included from SDK:newlib/include/pthread.h:2,
                   from tools.h:52,
                   from bonus.c:30:
SDK:local/common/include/pthread.h:39: error: redefinition of 'struct timespec'
In file included from bonus.c:42:
options_panel.h:64: error: redefinition of 'struct option'

Definicja struktury musi być spójna we wszystkich plikach kodu źródłowego oraz w plikach nagłówkowych. W przeciwnym przypadku wymagana jest zmiana nazwy deklaracji oraz wszystkich odniesień.
Przykład:

Zdefiniowane w SDK sys/time.h:
   struct timeval {
   	unsigned long tv_secs;
 	unsigned long tv_micro;
   };


Zdefiniowane w kodzie źródłowym WRONG.H:
   struct timeval {
 	unsigned char something;
 	signed short something_else;
   };


W kodzie źródłowym CLEAR.C:
timeval hereitis;

Powyższy kod wygeneruje błąd 'redefinicji'.

Rozwiązanie:
- zmiana nazwy struktury w pliku wrong.h na inną, może być: timeval2. - zmiana referencji w clear.c na zgodną ze zmienioną nazwą (w tym przypadku: timeval2)

Poprawny przykład przyjmie następującą postać:

WRONG.H;

struct timeval2 {
	unsigned char something;
 	signed short something_else;
};

CLEAR.C;
	timeval2 hereitis;

- redefinicja struktury 'Menu'

 error: redefinition of 'struct Menu'

Rozwiązanie:
zmiana nazwy na 'struct GLMenu'
Zmiana wygeneruje następujące błędy:

 menu/menusystem.h:47: error: 'Menu' was not declared in this scope
 menu/menusystem.h:47: error: template argument 1 is invalid
 menu/menusystem.h:47: error: template argument 2 is invalid
 menu/menusystem.h:48: error: 'Menu' was not declared in this scope
 menu/menusystem.h:48: error: template argument 1 is invalid
 menu/menusystem.h:48: error: template argument 2 is invalid

Kolejny krok rozwiązania:
zmiana 'Menu' na 'GLMenu' w wymienionych w komunikatach liniach kodu.

- brak wsparcia dla CDROM w SDL

Błąd:
Port programu nie chce się uruchomić. Komunikat mówi, że SDL nie został zbudowane ze wsparciem dla CDROM.

Rozwiązanie:
należy w kodzie źródłowym odnaleźć SDL_INIT. Może wyglądać podobnie do:

   if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )

   Zmienić na:

   if ( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_AUDIO|SDL_INIT_JOYSTICK ) == -1 )

Powyższa linia zainicjuje wszystkie mechanizmy dostępne w SDL, oprócz CD.

- 'return' bez wartości w funkcji zwracającej 'int'

 error: return-statement with no value, in function returning 'int'

rozwiązanie:
w kodzie powinno być
'return 0;'
zamiast:
'return;'

- nazwa 'sleep' nie została zadeklarowana w danym kontekście

 error: 'sleep' was not declared in this scope

rozwiązanie:

#include <unistd.h>

- nazwy 'cos' oraz 'sin' nie zostały zadeklarowane w danym kontekście

 error: 'cos' was not declared in this scope
 error: 'sin' was not declared in this scope

rozwiązanie:

#include <cmath>

- nazwy 'false' i 'true' nie zostały zadeklarowane w danym kontekście

error: 'false' undeclared (first use in this function)
error: 'true' undeclared (first use in this function)

rozwiązanie, dodanie do źródeł:

#define false 0
#define true 1

- UINT32 nie nazwą typu zmiennej

 error: 'UINT32' does not name a type

rozwiązanie:

#include <types.h>

- konflikt typów dla 'uint32' - wielokrotna deklaracja

../include/xmpi.h:55: error: conflicting types for 'uint32'
 SDK:include/include_h/exec/types.h:94: error: previous declaration of 'uint32' was here

W przypadku komunikatu podobego do powyższego: trzeba wyszukać wspomnianą linię w pliku xmpi.h. Wygląda ona następująco:

typedef unsigned int uint32;

Koliduje ona z typedef z SDK. By rozwiązać ten problem, warto zorientować się w jakim kontekście (sąsiadujących linii kodu) ta deklaracja występuje w xmpi.h:

 typedef signed char int8;
 typedef signed short int int16;
 typedef signed int int32;
 typedef unsigned char uint8;
 typedef unsigned short int uint16;
 typedef unsigned int uint32;

Definicje te nie są potrzebne, bo występują już w naszym SDK w 'exec/types.h'. Należy zmienić kod tak, by AmigaOS 4 je ignorował i wykorzystał te znajdujące się w 'exec/types.h'. Oto przykład:

#ifdef __amigaos4__ #include <exec/types.h> #include <stdlib.h> #include <stdint.h> #else typedef signed char int8; typedef signed short int int16; typedef signed int int32; typedef unsigned char uint8; typedef unsigned short int uint16; typedef unsigned int uint32; #endif

- Utworzenie tabeli jest zakane po specyfikacji typu w nawiasach

 error: array bound forbidden after parenthesized type-id

rozwiązanie:
'podejrzana' linia kodu będzie wyglądać mniej więcej tak:

 	frames = new(BITMAP*)[number_of_frames];

konieczne w takim przypadku jest usunięcie nawiasów okrągłych:

 	frames = new BITMAP*[number_of_frames];

- przypadkowy znak o kodzie \26

 error: stray '\26' in program

rozwiązanie:

W ostatniej linii kodu występuje pojedynczy znak spacji, trzeba go usunąć

- oczekiwane wyrażenie przed znakeim '='

 error: expected primary-expression before '=' token

rozwiązanie:
konieczne jest usunięcie niepotrzebnego znaku spacji. W poniższym przykładzie występuje ona w symbolu porównania:

Pierwotna wersja kodu:
		if ( i > = 320 )

po zmianie:
		if ( i >= 320 )

- niewłaściwe przypisanie po lewej stronie wyrażenia

 error: invalid lvalue in assignment

Postać linii powodującej ten błąd będzie zbliżona do:

 (unsigned char *)screen_buffer = calloc(1, VWIDTH * (VHEIGHT +2));

Nie można (w obecnym standarzie) rzutować docelowego typu zmiennej na inny po lewej stronie wyrażenia. Poprawna postać to:

screen_buffer = calloc(1, VWIDTH * (VHEIGHT + 2));

port aplikacji SDL zawiesza się przy zamykaniu?

rozwiązanie:
umieszczenie 'SDL_Quit();' przed *każdym* 'exit();'
Oto nieco inny przykład:

if(SDL_Init(SDL_INIT_VIDEO)==-1) { std::cerr << "SDL_Init error SDL_INIT_VIDEO" << std::endl; return EXIT_FAILURE; } atexit(SDL_Quit); if (!setmode(w,h,fullscreen)) { std::cerr << "SDL_SetVideoMode error" << std::endl; return EXIT_FAILURE; }

- nazwy 'cout', 'cerr', 'endl' nie zostały zadeklarowane w danym kontekście:

error: 'cout' was not declared in this scope
error: 'cerr' was not declared in this scope
error: 'endl' was not declared in this scope

rozwiązanie:
dyrektywa "using namespace std" musi zostać umieszczona na początku pliku z kodem źródłowym.

- rwiący się dźwięk w aplikacji?

Za problem może odpowiadać linia kodu o postaci podobnej do:

if (Mix_OpenAudio(44100, AUDIO_S16, 2, 512))

Ostatnia wartość określa rozmiar bufora audio. Zmiana do poziomu 2048 powinna dać pozytywny efekt. Zmieniona linia będzie wyglądać tak:

if (Mix_OpenAudio(44100, AUDIO_S16, 2, 2048))

- niezalecana konwersja 'stałej tekstowej' na 'char*'

warning: deprecated conversion from string constant to 'char*'

Słowo kluczowe 'const' oznacza, że nie spodziewamy się by dane przypisane do zmiennej miały się zmienić. Kompilator umieszcza je w sekcji: 'tylko do odczytu' zapewniając pewną formę ochrony.
Oto pierwotna postać kodu.

 static char * videoMode_to_str[8] =

rozwiązanie, dodanie słowa kluczowego 'const':

 static const char * videoMode_to_str[8] =

inny przykład, pierwotny kod:

 characters[text[i]]=new C3DObject(filename,"textures/");

rozwiązanie, dodanie rzutowania na (char *):

 characters[text[i]]=new C3DObject(filename,(char *)"textures/");

Ostrzeżenie to pojawi się też w przypadku funkcji, w których do argumentu zdefiniowanego jako 'char *' zostanie przekazany typ 'const char *'.
Przykład, funkcja:

- int blabla(char *cokolwiek) - wywołana z argumentem typu ' const char *'.
Ostrzeżenie pojawi się dla każdej linii, w której wystąpi taka sytuacja. Rozwiązać ten problem można na dwa sposoby:
1) Zmienić definicję argumentu funkcji (char *) na 'const char *'.
2) Zadeklarować rzutowanie typu na 'char *' w wywołaniu funkcji:

i = blabla((char*)jakaś_zmienna_która_była_pierwotnie_const_char)

Wyboru metody najlepiej dokonać na podstawie analizy kodu źródłowego. Metoda #1 jest preferowana jeśli dane argumentów podanych jako 'char*' nie ulegają zmieniane. W przypadku zmiany danych, lepiej wybrać #2.

- niewłaściwa konwersja 'Uint16*' na 'Sint16*'

error: invalid conversion from 'Uint16*' to 'Sint16*'

Rozwiązanie - zamiana:

 Sint16 *vx, *vy;
 vx = vy = (Uint16 *) malloc(2 * sizeof(Uint16) * numpoints);

 na

 Sint16 *vx, *vy;
 vx = vy = (Sint16 *) malloc(2 * sizeof(Uint16) * numpoints);

Zadeklarowane powyżej typy zmiennych to liczba ze znakiem, ale pierwotne rzutowanie (konwersja) wykonywane jest na wersję 'bez'. Nie rozumiem (Spot - przyp. red.) źródła tego błędu, ponieważ dla funkcji malloc nie ma to znaczenia.
Ogólnie: w przypadku nieprawidłowej konwersji z typu 'blabla' na cokolwiek innego, należy jawnie określić właściwe rzutowanie.

- nie można zadeklarować funkcji bez typu zwracanej wartości

 error: ISO C++ forbids declaration of 'setDrawLineShadow' with no type

Rozwiązanie - zamiana:

 setDrawLineShadow( PAGE color, PAGE bw, int shadow_offset );

na:

void setDrawLineShadow( PAGE color, PAGE bw, int shadow_offset );

- nie można zadeklarować funkcji bez typu

error: ISO C++ forbids declaration of 'tmp' with no type

Rozwiązanie - zamiana:

 register tmp = offset-x1;

 na:

register int tmp = offset-x1; 

- problem z deklarowaniem nowej zmiennej w definicji pętli 'for'

error: 'for' loop initial declaration used outside C99 mode

rozwiązanie: dodaj flagę -std=c99 do makefile , by przełączyć kompilację w tryb C99

Błędy linkowania

Błędy postaci zbliżonej do następującej:

 src/gui.o(.text+0x11aa):src/gui.cpp:73: undefined reference to `__iob'
 src/gui.o(.text+0x11ae):src/gui.cpp:73: more undefined references to `__iob' follow

Prawdopodobnie, przypadkowo zostały pomieszane ze sobą odwołania do newlib i clib2. Należy sprawdzić czy:
-mcrt=newlib
jest wykorzystywane przy kompilowaniu wszystkich plików oraz podczas linkowania. Nowsze wersje GCC mają wpisane to ustawienie jako domyślne, więc problem powinien występować rzadziej.

Konwersja ścieżek unixowych do amigowych

Jak przekonwertować ścieżki UNIX na zgodne z AmigaDOS?
a) Zamiana getenv("HOME"); na "/PROGDIR/"

Przykład 1

#ifdef __amigaos4__
	strcpy(fname, "Progdir:blockrage-scr.pcx");
#else
  strncpy(fname,getenv("HOME"),40);
  cat_with_slash(fname,"blockrage-scr.pcx");
#endif

Przykład 2

#ifdef __amigaos4__
 strcpy(path, "PROGDIR:");
#else
 strcpy(path, getenv("HOME"));
#endif

Przykład 3

#ifdef __amigaos4__
 strcpy(home,"PROGDIR:");
#else
 strcpy(home,getenv("HOME"));
#endif

Przykład 4

#ifdef __amigaos4__
 sprintf(rc_dir, "%sgngeo/romrc.d", "PROGDIR:");
#else
 sprintf(rc_dir, "%s/.gngeo/romrc.d", getenv("HOME"));
#endif

Uwaga! w ostatnim przykładzie pominięte zostało także: "/.".

Ścieżki do katalogów z danymi często są definiowane podczas konfigurowania za pomocą zmiennej:
'-DATADIR='. W takim przypadku zmiennej tej powinno się przypisać: 'DDATADIR=/PROGDIR/'. Polecenie './configure help' pomoże określić jakie zmienne można wykorzystać.
Można również napotkać 'datadir' zdefinioway w 'make file'. Parametru 'DATADIR=' powinien przyjąć postać:
DATADIR=/PROGDIR/

Problemy 'LValue'

Cała kwestia 'lvalue' rozbija się o gcc 4.x, które nie życzy sobie by umieszczać rzutowanie typów danych po lewej stronie znaku '='. Na przykład, tak jak tu:

(unsigned char *)blabla = (unsigned char *)whatever;

Powyższa linia będzie problemem dla gcc 4. Jeśli 'blabla' już jest zdefiniowane jako 'unsigned char *', to rzutowanie na ten typ i tak jest bezcelowe. Linia może przyjąć postać:

blabla = (unsigned char *)whatever;

inny przykład:

 short jakas_zmienna;
 (signed char) jakas_zmienna = (signed char)cokolwiek;

Tym razem zmienna jest rzutowana jest na inny typ. Można się domyślać, że intencją zapisu jest umieszczenie wartości 'cokolwiek' w pierwszym bajcie 'jakiejś_zmiennej'. Obejściem tego zapisu będzie wykorzystanie dodatkowej, tymczasowej zmiennej, o właściwym typie i zamującej ten sam obszar w pamięci co oryginalna:

 short jakas_zmienna;
 signed char *tymczasowa= &jakas_zmienna;  
 /* uzyskujemy tak wskaźnik typu signed char, na ten sam obszar pamięci jaki zajmuje 'jakas_zmienna' */

 tymczasowa[0]=(signed char)cokolwiek;

Jest to inny sposób na przepisanie wartości prawej zmiennej. Ręczna konwersja typów zmiennych spowodowała, że dodatkowa konwersja po lewej stronie znaku '=' nie jest potrzebna.

Uwaga! Metoda nie jest odporna na problemy zachowania kolejności bajtów (Endian)!

Obsługa właściwej kolejności bajtów (ENDIAN)

Pytanie: Kiedy muszę w swoim porcie zadbać o właściwą kolejność bajtów?
Odpowiedź: Na przykład, kiedy dźwięk brzmi jak biały szum.

Procesory X86 CPU's wykorzystują system 'little endian', a PowerPC and 68k oparte są o 'big endian'.
Przykładowa zmienna o typie 'short' ma długość 16bit, musi być więc zapisana za pomocą dwóch bajtów. Umieszczenie liczby $1234 w takiej zmiennej na maszynie 'big endian' będzie skutkować zapisem danych w takiej kolejności:
Pierwszy bajt: $12
Drugi bajt: $34

Na maszynie 'little endian' dane przyjmą taką postać:
Pierwszy bajt: $34
Drugi bajt: $12

'Little endian' umieszcza najbardziej znaczące bajty na końcu, a nie na początku. Wynika to ze sposobu w jaki procesor pobiera wielobajtowe dane z pamięci. Dla zmiennej 'short':
$1234 -> pamięć: $12 $34 (big)
$1234 -> pamięć: $34 $12 (little)

rzypadku typu 'long' sytuacja jest analogiczna:
$12345678 -> memory: $12 $34 $56 $78 (big endian)
$12345678 -> memory: $78 $56 $34 $12 (little endian)

nica ta nie ma znaczenia jak długo 'long' pozostanie 'longiem', a 'short' - 'shortem'. Procesor pobierając dane przywraca 'właściwą' kolejność i obliczenia są zgodnie z oczekiwaniami.

Problem pojawi się przy próbie zapisu wartości do pliku, który zawsze odbywa się bajt po bajcie. Zapis na maszynie 'Little endian' zmieni kolejność bajtów i trafią one do pliku jako:
- $78 $56 $34 $12
Odczyt na maszynie tego samego typu nie będzie oczywiście problemem. 'Big endian' nie będzie jednak świadomy konieczności zmiany kolejności i odczyta wartość tak jak została ona zapisana, tzn. jako:
- $78 $56 $34 $12.

Przykład:
Pewien format zapisu dźwięku korzysta z nagłówka, w którym długość piosenki zapisana jest za pomocą typu long, ale w kolejności 'little endian'. Informacja ta, odczytana na maszynie 'big endian' bez korekcji, będzie błędna. Korekcja jest jednak prosta:

unsigned int swaplong(unsigned int x) {
    unsigned char *s = (unsigned char *)&x;
return (unsigned int)(s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]); }

Powyższa funkcja po prostu zamienia kolejność bajtów: 4->1, 3->2, 2->3, 1->4.

W skrócie:
Naprawa problemu kolejności bajtów (np. przy odczycie danych z pliku) wymaga znajomości struktury oraz typów pobieranych danych. Jeśli wiadomo, czy dane zostały zapisane dla maszyn 'little endian' (oraz ich typ, np. long), jedyne co trzeba zrobić to zmienić kolejność bajtów przed pierwszym użyciem.

/ Blacky_Stardust

Dodatkowe informacje od Faba

W przypadku wywołania typu:

fread(&ble, sizeof(long), 1, f); 

należy dodać: 'ble = swaplong(ble)'. Tak jak poniżej:

fread(&ble, sizeof(long), 1, f);
ble = swaplong(ble);

Taką 'konstrukcję' stosuje się nie tylko do odczytu/zapisu, ale i do wszystkich innych danych przechowywanych w niewłaściwej kolejności.

/Fab

Definicje

Definicje są jedną z dyrektyw preprocesora języka C i C++, które pozwalają deklarować stałe oraz makroinstrukcje. W kodzie źródłowym elementy te umieszczane są często w pliku config.h. Niewłaściwą konfigurację można łatwo zmodyfikować wykorzystując słowa kluczowe: #DEFINE and #UNDEF.

Przykładowa definicja parametrów dla środowisk AmigaOS będzie wyglądać następująco:

   #ifdef __AMIGA__
	   Bla bla bla
   #else 
           Bla bla bla
   #endif

Przykład definicji wyłącznie dla AmigaOS 4.x:

   #ifdef __amigaos4__
 	  Bla bla bla
   #else 
           Bla bla bla
   #endif

Przykład definicji przetwarzanych tylko w przypadku zadeklarowania etykiet:'MORPHOS' albo 'amigaos4':

   #if defined(MORPHOS) || defined(__amigaos4__)

Przykład definicji wykonywanych tylko gdy etykiety 'MORPHOS', albo 'amigaos4' nie są zdefiniowane:

#if !defined(MORPHOS) && !defined(__amigaos4__)

Przykład bardziej złożonych deklaracji warunkowych #ifdef:

   #ifdef GP2X
 	char *gngeo_dir="save/";
   #elif defined __amiga__
 	char *gngeo_dir="/PROGDIR/save/";
   #else
	char *gngeo_dir=get_gngeo_dir();
   #endif

Sztuczki i kruczki

Wpisz 'Dummy' w powłoce albo abc-shell by przywołać te porady
Wpisz 'Dummyhelp' w powłoce albo abc-shell by przywołać szybką pomoc.

Jak wyszukiwać tekst za pomocą GREP?

Z poziomu ABC-Shell:
grep -R "tego właśnie szukam" *

Jak wygenerować plik DIFF z moimi zmianami?

Z poziomu ABC-Shell:
diff oryginalny_plik.cpp plik_po_zmianach.cpp >./oryginalnyplik.patch

uwaga: zunifikowany 'diff' łatwiej się czyta I przegląda. Generacja wygląda następująco:
diff -urN originalfile.cpp mychangedfile.cpp >./originalfile.patch

Jak zaaplikować plik DIFF?

patch -p0 -i mój_patch.diff

Uwaga: jeśli program 'patch' narzeka, że nie może znaleźć docelowego pliku można wykorzystać parametr -p1.
patch -p1 -i mój_patch.diff

Mój plik wykonywalny się zawiesza, jak mam go debugować?

Program Snoopy pomaga stwierdzić co aplikacja stara się zrobić. Bardzo często okazuje się, że program nie może znaleźć wymaganego pliku. Sekcja 'naprawa ścieżek UNIXowych' porusza ten problem.

W oknie 'Grim Reaper' należy kliknąć 'generate stack trace' z zakładki 'Stack Trace'. Pozwoli to uzyskać dostęp do wielu liczb i rzeczy. Za pomocą 'addr2line' można stwierdzić, które miejsce w źródłach spowodowało błąd. Przykład:
addr2line -e xmoto.exe 0x39c48

Uwaga: wykorzystanie parametru 'strip' przy kompilacji pliku wykonywalnego usunie wszystkie symbole (pomocnicze, nazwy zmiennych, funkcji itp.), które 'addr2line' potrzebuje.

Jak wykorzystać Python?

Należy zadeklarować przypisanie python: wskazujące lokalizację python'a, a potem dodać do PATH za pomocą:
abc-shell: PATH=$PATH:/python

Jak przekierować ostrzeżenia I komunikaty o błędach do pliku tekstowego?

gmake 2>/ram/warnings.txt

Jeszcze lepszym rozwiązaniem jest wykorzystanie polecenia 'tee'. Polecenie udostępnia podgląd na zapisywane w pliku informacje:

gmake 2 > &1 | tee /ram/warnings.txt

Jak przygotować archiwum tar.gz ze źródeł?

tar czvf moje_archiwum.tar.gz moje_źródła/

Jak wydobyc źródła, gdy jedyną informacją jest URL o postaci takiej jak: svn://svn.72dpiarmy.com/smw?

Potrzebny jest program obsługi kontroli wersji (Subversion, SVN). Z wiersza poleceń należy wpisać:
svn checkout svn://svn.72dpiarmy.com/smw

Jak dodać numer wersji AmigaOS do plików wykonywalnych?

Następujący kod należy umieścić w pliku main.cpp:

 #ifdef __amigaos4__
 const char *version_tag = "$VER: xxxxxx 1.0 (01.01.2007)";
 #endif

Jak wygenerować możliwie najmniejszy plik wykonywalny?

Kompilacja z flagami: -Os -msdata=sysv I wywołanie 'stripreloc' przed 'strip'.
Można dodatkowo wykorzystać LZMA do zmniejszenia pliku wykonywalnego:
lzma e mybig.exe mysmall.exe
Join lzmaloader mysmall.exe AS myfinal.exe

lzma and lzmaloader znajdują się:
http://strohmayer.org/sfs/files/lzma.lha
http://strohmayer.org/sfs/files/lzmaLoader2.lha

Ustwienie flagi 'wykonywalny' na wygenerowanym pliku:
Protect myfinal.exe +e

Jak zastosować 'strip' na dynamicznie dołączanymi pliku wykonywalnym?

Ze względu na błąd w AmigaOS 4.0 SDK, dynamicznie dołączany plik wykonywalny musi być przetworzony w specjalny sposób:
strip `nm -u moja_app.exe | sed "s/U /-K/"` moja_app.exe -R.comment

Jak zdefiniować 'minimum stack cookie' dla aplikacji (pewna forma zabezpieczenia przed przepełnieniem stosu - przyp.)?

Umieścić w pliku main.c projektu:
'static const char * __attribute__((used)) stackcookie = "$STACK: 123456";'

Spot (tłumaczenie - Konrad Rojek)
zobacz inne nasze artykuły »



AmigaOS.pl

Polecamy
Najpopularniejsze
eXec blog

Świat poza Amigą: