Paweł "Yosh" Różański atakuje nas drugim fragmentem rozważań
o języku "Action!" (pierwszy był
tu). Jest nadzieja, że przy większym zainteresowaniu ze strony
czytelników zamieni się to w długi cykl, który pozwoli nam
dokładnie poznać wady i zalety tego języka, a jednocześnie nauczy
nas praktycznej umiejętności pisania w nim gier... Dalej już będzie
peorował Yosh:
Siła ekspresji
Kolejnym aspektem języka, po dostępności kompilatora generującego
zwięzły i szybki kod, jest jego zwięzłość - czyli możliwość
wyrażania się w nim w sposób przejrzysty. W języku BASIC
propagowany był kiedyś styl "samokomentujący", jakby to dziś
powiedziano. Polegał na nazwaniu stałych po imieniu, co podnosiło
czytelność listingu. Nie jakieś tam PEEK(20) czy GOTO
10, ale PEEK (RTCLOCK) i GOTO POCZATEK.
Oczywiście zmiennym RTCLOCK i POCZATEK należało
wcześniej przypisać odpowiednie wartości. Dobry kompilator mógłby
zoptymalizować kod podstawiając wartości zmiennych na swoje
miejsca. Tak skompilowany program nie traciłby cykli na wyczytanie
wartości ze zmiennej. Jednakże problem statycznej analizy
zawartości zmiennych (czyli określenie podczas kompilacji
zawartości zmiennych w danym miejscu wykonywanego kodu) jest
złożony... Nic dziwnego, że nie robi tego ani kompilator "Action!",
ani kompilator "Turbo Basic'a XL".
"Action!" próbuje obejść ten problem tak jak robi to język C -
udostępniając makra. Dzięki nim możemy dowolny kawałek kodu nazwać
inaczej, np.
DEFINE PLUS = "+"
DEFINE I = "+"
DEFINE TRZY = "3"
DEFINE DZIESCI = "* 10"
DEFINE PISZ = "PRINTF(""%U%E"", "
DEFINE NA = ")"
DEFINE EKRAN = ""
PROC MAIN()
PISZ TRZY DZIESCI I TRZY PLUS TRZY NA EKRAN
PISZ TRZY PLUS TRZY I TRZY NA EKRAN
RETURN
"Action!" posłusznie "wkleja" kolejne napisy podczas kompilacji -
ważne jest, aby na koniec uzyskał kod zgodny z gramatyką "Action!".
W tym szaleństwie jest metoda - kompilator jest zwolniony z
jakichkolwiek weryfikacji przed podstawieniem tekstów (łatwe dla
twórców kompilatora). Programista natomiast posiada możliwość
nazywania części kodu bez troski o późniejszy przekład na kod
maszynowy.
Do celu
Po tym powyższym przykładzie wyrwanym z kontekstu, kolejne będą
nieuchronnie zbliżały nas do wykonania prostej gry.
Przed wykorzystaniem zmiennej należy ją wcześniej zadeklarować.
Jeżeli zostanie zadeklarowana przed procedurami i funkcjami, jest
zmienną globalną dostępną w dowolnym miejscu programu (podobnie jak
w BASIC-u). "Action!" udostępnia trzy typy liczbowe: bajt -
BYTE, liczbę 16-bitową ze znakiem - INT i liczbę
16-bitową bez znaku - CARD.
Deklaracja w programie wygląda następująco:
CARD SCORE
Nie interesuje nas pod jakim adresem "Action!" będzie trzymał tą
wartość dla nas nazywa się ona SCORE. Jeżeli umiejscowienie
zmiennej w pamięci jest istotne, możemy wymusić adres, na
przykład:
BYTE RND = $D20A
Tym oto sposobem dostaliśmy generator liczb losowych (sponsorem
jest układ POKEY), bez wykorzystywania biblioteki "Action!".
Wywoływanie procedur i funkcji w "Action!" jest kosztowne w
porównaniu do czystego kodu maszynowego. Więc nie STICK(0)
ale BYTE STICK0 = $0278 i tak dalej. Jest to przy okazji
spójne, bo prędzej czy później zamarzy nam się rejestr, który nie
jest obsługiwany przez bibliotekę.
Napiszemy prostą grę silnie wykorzystującą pamięć ekranu. W BASIC-u
wykorzystywana jest do tego procedura LOCATE, zwracająca kod
ATASCII z podanej pozycji. W bibliotece standardowej "Action!"
zawarta jest ona jako funkcja, co umożliwia proste użycie jej w
wyrażeniu warunkowym. Rozpatrzmy prostą labiryntówkę, napisaną w
stylu BASIC (tak aby ukazać, że nie tak ciężko uczy się
"Action!"):
BYTE X,Y
WHILE (1) ;z programu nie ma wyjscia - RESET
przerywa
DO
POSITION(X,Y) PRINTE("@")
WHILE (STICK(0)=15) DO OD
POSITION(X,Y) PRINTE(" ")
IF STICK(0)=7 AND LOCATE(X+1,Y)=32 THEN
X=X+1 FI
IF STICK(0)=11 AND LOCATE(X-1,Y)=32 THEN
X=X-1 FI
IF STICK(0)=13 AND LOCATE(X,Y+1)=32 THEN
Y=Y+1 FI
IF STICK(0)=14 AND LOCATE(X,Y-1)=32 THEN
Y=Y-1 FI
OD
RETURN
W zmiennych X i Y zawarta jest pozycja gracza. Główna pętla rysuje
gracza, czeka na wychył joysticka i przesuwa gracza, jeżeli w danym
kierunku jest znak o kodzie ATASCII 32 (czyli spacja). Taki program
działa wystarczająco szybko nawet w BASIC-u. Jest jednak na tyle
krótki, że pozwoli na zobrazowanie technik przyśpieszenia.
Wszystko sprowadza się do zrezygnowania z "ciężkich" poleceń
POSITION i PRINTE i LOCATE. Polecenia
POSITION i LOCATE pieczołowicie przeliczają pozycje
X, Y na adres w pamięci ekranu. LOCATE i PRINTE
dodatkowo operują na znakach kodowanych w ATASCII, co jest nam
całkowicie zbędne.
Ekran trybu Graphics 0, jest rysowany od ciągłego obszaru pamięci
którego adres zawarty jest w:
CARD SAVMSC = $58
Aby zadeklarować taki obszar konieczne jest wprowadzenie tablic w
języku "Action!". Tablica to spójny obszar pamięci o określonym
początku, deklarowanym poprzez:
BYTE ARRAY SCREEN
Teraz każde odwołanie postaci SCREEN(x)=y tłumaczone jest na
"zapiszę na adres, który jest sumą zawartości pola SCREEN i
x". Technicznie taka tablica to dwubajtowa zmienna zawierająca
adres:
SCREEN = SAVMSC
spowoduje przestawienie początku tablicy na adres ekranu! Umożliwia
to już rozsądnie wygodny dostęp do pamięci ekranu, na przykład:
SCREEN(4+40*10) = 128 ; narysuj murek w pozycji x:4 y:10
A=SCREEN(X+40*Y); to prawie A=LOCATE(X,Y)
Oczywiście nikt nie każe nam pamiętać, że spacja w negatywie to
128, a nasz ekran ma 40 bajtów szerokości, wystarczy to
zdefiniować:
DEFINE WALL= "128"
DEFINE XY = "+40*"
I uzyskujemy w miarę przejrzyste konstrukcje:
SCREEN(4 XY 10) = WALL
A=SCREEN(X XY Y)
Oczywiście operacja mnożenia też kosztuje, zapiszmy więc pozycje
naszego gracza poprzez offset w pamięci ekranu:
CARD PLAYERPOS
PLAYERPOS = 3 XY 1
Co pozwoli na wpisywanie go w plansze poprzez:
SCREEN(PLAYERPOS) = PLAYER
Znając rozkład pamięci ekranu wiemy, że pozycje wokół gracza
znajdującego się na PLAYERPOS można wyrazić następująco:
PLAYERPOS - 41
PLAYERPOS - 40
PLAYERPOS - 39
PLAYERPOS - 1
PLAYERPOS
PLAYERPOS + 1
PLAYERPOS + 39
PLAYERPOS + 40
PLAYERPOS + 41
Czyli wykrycie co mamy po prawej możemy zrealizować poprzez
SCREEN(PLAYERPOS + 1). Podobnie jego ruch w górę:
PLAYERPOS = PLAYERPOS - 40.
W tym momencie powinna już jak zmora pojawić się "pani od
matematyki", na szczęście ze szkoły podstawowej :). Skoro:
wynika więc, że SCREEN + PLAYERPOS można wyciągnąć przed
nawias. W połączeniu z wiedzą o tablicach, nie pozostaje nic innego
jak wyrażać pozycje gracza... adresem tablicy. Niech:
BYTE ARRAY PLAYERPOS
PLAYERPOS = SCREEN + 3 XY 1
Zakładając, że adres tablicy zawarty w zmiennej PLAYERPOS to
adres komórki ekranu, w której jest gracz, mamy:
PLAYERPOS(0) - zapis/odczyt w pozycje gracza PLAYERPOS(-40) - pozycja nad graczem PLAYERPOS=PLAYERPOS-1 - przestaw pozycje gracza w lewo
zmieniając adres tablicy, itd.
Przerobiona gierka wyglądać będzie teraz tak:
DEFINE LEFT = "-1"
DEFINE RIGHT = "1"
DEFINE UP = "-40"
DEFINE DOWN = "40"
DEFINE XY = "+40*"
DEFINE NOTHING = "0"
DEFINE PLAYER = "32"
WHILE (1) ;z programu nie ma wyjscia - RESET
przerywa
DO
PLAYERPOS(0) = PLAYER
WHILE (STICK0=15) DO OD
PLAYERPOS(0) = NOTHING
IF STICK0=7 AND
PLAYERPOS(RIGHT)=NOTHING
THEN PLAYERPOS ==+ RIGHT FI
IF STICK0=11 AND
PLAYERPOS(LEFT)=NOTHING
THEN PLAYERPOS ==+ LEFT FI
IF STICK0=13 AND
PLAYERPOS(DOWN)=NOTHING
THEN PLAYERPOS ==+ DOWN FI
IF STICK0=14 AND PLAYERPOS(UP)=NOTHING
THEN PLAYERPOS ==+ UP FI
OD
RETURN
gdzie PLAYERPOS ==+ RIGHT to skrót składniowy dla
PLAYERPOS = PLAYERPOS + RIGHT.
Mając dość szybki sposób na określanie co się wokół dzieje, możemy
porwać się na zdefiniowanie kamieni z "BoulderDash-a" i bomb z
"Robbo", co da nam grę "Robbo Dash" ;). Poniższy program przegląda
każde pole planszy i wykonuje operacje na znajdujących się tam
obiektach:
DEFINE RIGHT = "1" DEFINE LEFT = "-1" DEFINE UP = "-40" DEFINE DOWN = "40" DEFINE XY="+40*"
BYTE B
PROC MAIN() GRAPHICS (0) SCREEN = SAVMSC OBJTO = SCREEN + 41 ; murek jest przy poczatku OBJFROM = SCREEN + 919 ; i przy koncu
FOR B = 0 TO 23 DO SCREEN(0 XY B) = WALL SCREEN(39 XY B) = WALL OD FOR B = 1 TO 38 DO SCREEN(B XY 0) = WALL SCREEN(B XY 23) = WALL OD
OBJ = OBJFROM WHILE (1) DO IF OBJ=OBJTO THEN ; jak caly ekran obsluzony OBJ=OBJFROM ; to od nowa IF (RND AND 7) THEN ; dostaw kamien/bombe SCREEN(45 + (RND AND 31)) = STONE ELSE SCREEN(45 + (RND AND 31)) = BOMB FI ELSE OBJ = OBJ - 1 ; iteruj po obszarze gry FI
B = OBJ(0) IF NULL = B THEN ;nic nie rob dla pustego ;ale sprawdz go pierwszego ELSEIF STONE = B THEN IF OBJ(DOWN) = NULL THEN OBJ(DOWN) = STONE OBJ(0) = NULL ELSEIF OBJ(DOWN+RIGHT) = NULL AND OBJ(RIGHT) = NULL THEN OBJ(RIGHT) = STONE OBJ(0) = NULL ELSEIF OBJ(DOWN+LEFT) = NULL AND OBJ(LEFT) = NULL THEN OBJ(LEFT) = STONE OBJ(0) = NULL ;jak kamyk polecial w lewo, ;zapobiegnij ponownemu rozpatrzeniu OBJ = OBJ - 1 FI ELSEIF BOMB = B THEN IF OBJ(DOWN) = NULL THEN OBJ(DOWN) = BOMB OBJ(0) = NULL ELSE OBJ(0) = NULL IF OBJ(DOWN+LEFT) <> WALL THEN OBJ(DOWN+LEFT) = NULL FI IF OBJ(DOWN) <> WALL THEN OBJ(DOWN) = NULL FI IF OBJ(DOWN+RIGHT) <> WALL THEN OBJ(DOWN+RIGHT) = NULL FI IF OBJ(LEFT) <> WALL THEN OBJ(LEFT) = NULL FI IF OBJ(RIGHT) <> WALL THEN OBJ(RIGHT) = NULL FI IF OBJ(UP+RIGHT) <> WALL THEN OBJ(UP+RIGHT) = NULL FI IF OBJ(UP) <> WALL THEN OBJ(UP) = NULL FI IF OBJ(UP+LEFT) <> WALL THEN OBJ(UP+LEFT) = NULL FI FI FI OD RETURN
Efektem programu jest deszcz kamieni, podobny do tego z "Boulder
Dash". Mam nadzieję, że przykłady są czytelne. Pozdrawiam i czekam
na uwagi.
Jedyną procedurą, która pozostała jest GRAPHICS. Pozbędziemy
się jej w kolejnym artykule, o Display List i generatorze znaków. W
nim (jeżeli ktoś to będzie czytał) przybliżę kolejne niuanse
tablic, na przykład dlaczego w powyższych przykładach brak jest
rozmiaru tablicy.
sikor 2009-03-24 07:03:37
I o to chodzi. Dobrym przykładem byłby cykl artykułów o zrobieniu gierki od A do Z w Action, dodakjąc w każdej części kolejny "klocek" (procedurę) z szeroko opisanym kodem. Na początek z funkcjami bibliotecznymi, a później można pokazać metody optymalizacji (tak jak to jest tutaj zrobione). Pozdrawiam i liczę na więcej ;) pps 2009-03-24 08:58:04
Genialnie proste :) Wielkie dzięki za artykulik i również proszę o więcej! Pozdrawiam. xxl 2009-03-24 09:13:50
pysznie, moze powiazac cykl artkow o pisaniu gier w action! z tym: http://atarionline.pl/forum/comments.php?DiscussionID=105 immolator 2009-03-24 09:58:26
Super! Dzięki za trud włożony w arta. George 2009-03-24 10:56:23
Właśnie zaczynam zabawę z Action!, więc ten artykuł jest Just In Time. Dzięki wielkie. anonymus 2009-03-24 11:46:42
Nie wiem gdzie napisać, więc proszę tu o pomoc, jaki emulator wybrać, żeby mi saturday demo ładnie grało, a nie jak w a800win czy w alttirze? Kaz 2009-03-24 12:26:17
W sprawie gdzie pisac - proponuje forum - zostales juz zarejestrowany :). zilq 2009-03-24 15:24:14
Bardzo fajna sprawa dla ludzików, chcący poznać "tajniki" pisania gier (nie tylko w ACTION!) Oczekuję dalszych artykułów. Sam zapewne skorzystam, gdyż ACTION! to język który najbardziej jest zbliżony do stosowanego (i znanego) przeze mnie Pascala (tudzież PHP) Więc przesiadka mało boli :] Oby tak dalej... irwin 2009-03-24 16:13:50
Brawo Yosh! - bije pokłony, i czekamy na dalsze odcinki. Tak trzymaj! @Kaz - może by tak z boku strony zrobić jakiś link typu kurs Action!, lub programowanie kursy - aby wszystkie odcinki były w jednym miejscu, bo za jakiś czas jak ktoś ich będzie potrzebował to się sporo naszuka a i tak nie będzie pewny czy wszystkie odnalazł w gąszczu nowinek, które niejaki Kaz wypuszcza z prędkością nadświetlną ;-) stjack 2009-03-24 17:50:50
Super! Dzieki wielkie! Yosh 2009-03-24 18:46:39
@sikor: ku temu to zmierza, aczkolwiek cały kod raczej będzie do ściągnięcia - już teraz ciężko się 'cytuje' @xll: raczej nie ma takiej flaszowej gry... starałem się wybrać prosty - a za razem 'procesorożerny' przykład.
Chciałbym także podziękować Kazowi za miejsce na stronie i za redakcje - to On dostawił obrazki jak i przeredagował kilka zdań aby mniej kluły po oczach.
Dzięki wszystkim za dobre słowa - powiedzmy, że już jest tego tyle że dalej to przyjemność :P:P (w pisaniu kodu, bo tekściarzem to nigdy nie byłem...) George 2009-03-25 08:32:17
Ja sobie dodaję do ulubionych i już nie muszę szukać :) Ilmenit 2009-03-25 15:42:32
Pomysł - Cross-kompilator kodu Action!, ale kompilujący oryginalnym kompilatorem. Dałoby radę zrobić na różne sposoby np. generując a8s, interpretując oryginalny kod kompilatora... Yosh 2009-03-25 23:11:33
@Ilmenit: dość łatwo w Atariwin plus podmontować katalog dla Action! działa jak złoto - może napisze o tym ciut...
Z Action! jest problem, że jego przekład jest 'wprost' bez najmniejszego namysłu, c65 jeszcze dokładnie nie testowałem - ale widzę, że tam będzie ciut lepiej. (Po skończeniu z Action! planuje przekład kodu na c65 - aby pokazać, że praktycznie jest to to samo)
Generalnie są programy które w Action! 'się wyrobią' i te które trzeba ostro w assemblerze (wiem brzmi to jak truizm - tak jest z każdym językiem programowania). Czasami ... bardziej mnie boli, że kod mógłby być krótszy ;) Kaz 2009-03-26 12:59:48
Irwin - linki nie powinny zaginac, bo w kazdym nastepnym artykule z cyklu beda odnosniki do poprzednich czesci. A na koniec cos sie z tym zrobi - umiesci w dziale "Poradniki". Tdc 2009-03-28 03:24:07
Bardzo fajny tekst - Yosh ma nieco odmienne podejście do tematu ode mnie, nad niektórymi rzeczami się nigdy nie zastanawiałem !:) To dobrze bo takie różne punkty widzenia są bardzo cenne dla czytelników !
Sikor: Dobrym przykładem byłby cykl artykułów o zrobieniu gierki od A do Z w Action, dodakjąc w każdej części kolejny "klocek" (procedurę) z szeroko opisanym kodem.
Mówisz - masz ;) Z tego co zgrywaliśmy u Mikera jest jedno źródło bardzo dobre do nauki np. dla wielbicieli Basica (choć chyba wtedy nie widzieliście tego programu). Muszę je teraz tylko opisać ;):) Może nie dziś ale macie je jak w banku.
Myślę że fajnie by było aby Yosh również zastanowił się nad swoim cyklem (o ile w ogóle chce) - wtedy oba nasze przykłady gier byłby pewną całością. Zapraszam na priva;)
Ilmenit: ja kiedyś (wczesne lata 90-te) na pececie napisałem coś podobnego co powodowało możliwość kompilowania kodu z Action! pod (np. pecetowym) językiem C. Nie działało to 100% bo pewne problemy były nierozwiązywalne lub trudne (czego mi się już robić nie chciało). Ale ciekawostka wyszła fajna;) Kaz 2009-03-28 07:18:13
No i fajnie! To czekam na opisanie i oczywiscie udostepniam łamy AO. Yosh 2009-03-28 13:15:39
Znalazłem przykład (prosty!) w którym Action! rozkłada cc65 (albo ja nie umiem go używać i ktoś mnie wyprostuje :P)
Szykuje całkiem nowe smakowitości
@Tdc: fajnie, że na priv-a - tylko jak? :) (zapobiegawczo wklepałem swój email w tym komentarzu - daj znać jakie masz pomysły)
Aktualnie mam plan, żeby okrasić RD lepszą grafiką (przy okazji pokrótce o display liście) czyli generalnie go 'skończyć' w kilku kolejnych 'odcinkach' - starając się żeby nie urósł. Ilmenit 2009-03-29 22:11:42
@Yosh: daj ten przykład w Action! i CC65. Kaz 2009-03-29 23:53:46
Yosh - zaraz Ci wysle maila do TDC. Tdc 2009-03-30 22:21:38
Sorry z tym e-mailem, ale nie miałem za bardzo czasu w ostatnich dniach aby pisać, a jak napisałem posta to zdałem sobie sprawę, że nie wspisałem swojego emaila... W każdym razie wiele osób ma do mnie e-mail i zbytniego problemu z tym nie ma (można też na forum AA słać listy do mnie - choć szkoda że nie ma takiej możliwści na Atarum - czyli adres jest niewidoczny ale list wysłać można). No i dzięki Kaz za przesłanie emaila do Yoshowi.
Jestem bardzo zainteresowany tym przykładem Action! vs cc65.
I wszyscy czekamy na Twoje smakowitości ;)
Co do opisu DL to jestem jaknajbardziej za. Szczególnie, że w Action! można wiele fajnych rzeczy zrobić. Yosh 2009-03-31 18:47:37
Jako, że mam sprawę niecierpiącą zwłoki i chwilowo odpoczywam (no powiedzmy :)) od Atari. to będę się streszczał (Ilmenit widzę, że niecierpliwy)
BYTE ARRAY a(100) = 40040
PROC MAIN() while(1) do a(1) = 2 od RETURN
i jego przekład (tak Action! be, nie domyślił się że to martwa pętla i niepotrzebnie robi LDA #1 BNE) (liczba cykli po ; )
Ładnie załatwił martwą pętlę (jest tylko jmp).. ale dalej.. to jakas masakra.. już nie mówię - nie domyślił się, że to adres jest stały, i zrobił sobie tymczasowy pointer sreg... no ale jak go zrobil to mógł się chociaż domyśleć, że
ldx #$9C lda #$68 sta sreg stx sreg+1 lda #$02 ldy #$01 L0003: sta (sreg),y jmp L0003
:) czyli ponawiać tylko sta
Jak wywołać cc65 ew, jak zmienić kod aby był ciut ładniejszy niż ten co jest teraz... ?
Ten przykład jest może naiwny - ale obrazuje, że nie potrafię zmusić cc65 do elementarnych optymalizacji - typu "stały adres kodu" Ilmenit 2009-03-31 19:14:23
Btw, dokładnie taki kod wynikowy daje napisanie tego "w stylu CC65":
#include
#define a 0x9C68
void main(void) { while(1) POKE(a+1,1); } Yosh 2009-03-31 19:47:42
Dzięki, faktycznie ten konkretny przykład da się tak obejść - cc65 wraca do rozważenia ...
natomiast jest to drobne oszustwo ponieważ adres jest obliczany już na etapie sklejania stałych. Należy więc stworzyć przykład który nie pozwoli na ten haczyk :)
dla pętli wewnętrznej i=1 while(i) do a(i) = 2 i=i+1 od
0E32 LDY #$01 0E34 STY $0E27 0E37 LDA $0E27 0E3A BNE $0E3F 0E3C JMP $0E4D 0E3F LDA #$02 0E41 LDX $0E27 0E44 STA $9C68,X 0E47 INC $0E27 0E4A JMP $0E37 0E4D RTS i porownywalny kod w C
Wiesz, Action! został napisany do tego, aby był szybki i tworzył ładny kod na 6502, przez co język jest ubogi w stosunku do C. Co najważniejesze nie ma rekurencji, a bez tego możesz robić znacznie wiecej optymalizacji.
Najprościej ten kod zapisać w C jako:
#include #define a 0x9C68 void main(void) { register unsigned char i; for (i=1;i;++i) POKE(i + a,1); }
ale według specyfikacji języka C tworzymy w ten sposób zmienną na stosie (dla rekurencji). 1. Jeżeli nie używamy rekurencji, to można opcją kompilatora takie zmienne traktować jako statyczne i kod będzie szybszy (choć już niezgodny z C). 2. Można też przenieść "unsigned char i" do zasięgu globalnego, ale odpada wtedy słowo register, które umieszcza zmienne na stronie zerowej.
W celu optymalizacji najprościej potraktować zmienną jako stały adres na stronie zerowej np. 0xFB, ale wtedy traci się wygodę automatycznego przydziału adresów...
Jeden z dawnych branchy CC65 miał słowo zstatic do definiowania zmiennych zero-page. Niestety to nie zostało użyte i w CC65 trzeba takie zmienne definiować w ASMie i użyć #pragma zpsym(), jak poniżej:
język C ma zapewniać rekurencje - ale nie zasłaniać się nią - skoro on wie, że to nie rekurencyjne to niech agresywnie optymalizuje
Język to co innego niż jego kompilator np niektóre języki rozwijają rekurencje, najbardziej znana to ogonowa - wspomniana przy LOGO (przy poprzednim arcie)
implementujesz rekurencyjną pętelkę - a jako wynik masz iteracyjną pętelkę.
robi to oczywiście gcc...
int suma(int b, int a) { if (a>0) return suma(b+a, a-1); return b; }
W Action! wręcz się cieszę że nie ma rekurencji - jak zauważyłeś da się prostszy kod generować, a w grach rekurencja i tak mało potrzebna.....
Co nie zmienia faktu, że dobry kompilator C wie kiedy co powycinać - bo Język a jego kompilator to dwie różne sprawy. Językiem się wyrażamy, a kompilator ma to zrobić dobrze(tm).
Żeby nie było, że jestem stronniczy... w Action! zapomnieli że jest strona zerowa :) Ilmenit 2009-03-31 22:18:59
Dobry kompilator wie. CC65 nie jest dobry :-) Nie ma co się dziwić, skoro rozwija go właściwie tylko jedna osoba. A i tak jest lepszy niż "konkurencyjny" z88dk, który nawet nie wspiera podstawowych cech języka C, nie mówiąc już o nowszych standardach. Z rekurencją jest inny problem, mianowicie rekurencyjnie możesz wołać funkcje A->B->A->B, też w przypadku, gdy znajdują się w innych jednostkach kompilacji. Bez globalnego optymalizatora nic nie poradzisz. Rekurencja ogonowa to szczególny przypadek rekurencji, podobnie jak szczególnym przypadkiem są funkcje, które nie wołają innych funkcji.
Fajnie spojrzeć jak różny kod generuje Action i CC65. Chyba podeślę Ulrichowi powyższe kody, bo nie widziałem, żeby CC65 wygenerował STA Absolute,X, co w praktyce jest bardzo przydatne. Yosh 2009-03-31 22:27:08
Żeby nie było - ja cc65 lubię - bo .. jest :) tzn nie jest tak źle a napewno kilka osób scena 8bit przez niego zyskała... Action! mimo że jest hackiem ('a co to strona zerowa' 'a rekurencja? nie wiem nie widzialem') zdobył moje serce rokiem wydania
gcc ma też ultra duper przełącznik: "GCC 4.1 provides the -fwhole-program and --combine options to do this, but you have to pass all translation units you want optimized together to GCC at once. This is very unfriendly to existing makefiles and counter to existing practice."
Podaje się mu wszystko w jednej lini - wszystkie pliki .c... a on to mieli jak może - nic nie widać. ale kod jest krótszy nawet o 25% względem kompilowania każdego pliku osobno -Os i linkowania ich.
Jak trochę o tym czytałem to on to robi baaaaaardzo siłowo - wkleja wszystko razem, każdej zmiennej globalnej daje static - a co! przecież nie będzie widziana z innego modułu skoro wszystko w jednym na raz kompiluje :)
Jak widać, wszyscy orzą jak mogą (fwhole-program fajne do embedded) Ilmenit 2009-03-31 22:41:06
CC65 przyciągnął na przykład mnie :-) Jak tylko się o nim dowiedziałem, to postanowiłem napisać jakąś grę, bo sentyment odżył, a nie wyobrażam sobie rzeźbienia w czystym ASMie. A tak robię jedną z najbardziej złożonych gier, jakie powstały na małe Atari, mając wygodne IDE Visuala :-) Yosh 2009-03-31 22:50:24
Jak nostalgia to nostalgia :) mnie też cc65 przyciągnął - też rzeźba w asmie gry do której aż takiej wydajności nie trzeba. Action! użyłem dlatego, że gdy powstał miałem lat 5 :) gdybym miał go od początku atari....... ale w Action! mam wgrany tylko kod include "h9:gra.act" - co powoduje ciągniecie źródeł z katalogu w którym edytuje w notepad++
1) jeden include - mało tekstu - więcej ramu na kod skompilowany 2) często pisze tak, że wszystko się wysypuje - szczerze mówiąc gdybym z Action! zaczynał na real atari to może bym się zniechecił, a może pisał mniejsze przekmniy...
Ciekawe jak w tym cc65 jest drzewo trzymane (i czy wogole) a może to kompilator sterowany składnią ? wiesz coś może (ja wiem, mogę źródła - ale i tak za dużo czasu dla Atari w tym tygodniu :P) może mógłbym dopisać trochę optymalizatora :P Yosh 2009-03-31 22:55:39
o kurka ale jestem zmęczony "mnie też cc65 przyciągnął - też rzeźba w asmie gry do której aż takiej wydajności nie trzeba." - miało być że nie chciało mi się rzeźbić w asm czegoś co można w cc65/action no i dwa razy "Żeby nie było," .. dobranoc :) Kaz 2009-04-01 02:32:40
Niezle sie to czyta takie rozwazania za i przeciw. To przypomina mi "Sonde" i Kurka i Kaminskiego.
Tdc - dopisuje postulat mozliwosci wyslania maila na forum do listy rzeczy do zrobienia. Tdc 2009-04-02 03:47:07
Kaz: dzięki, bo jak widać to może się sprawdzić w praktyce ;) Ilmenit 2009-04-14 12:45:23
Im więcej bawię się CC65 tym mniej jestem nim zachwycony... Jest oparty na Small C, przez co generuje paskudnie rozwlekły kod, którego nie da się optymalizować bez kompletnej przebudowy kompilatora. Jednocześnie jest - niestety - najbardziej rozbudowanym kompilatorem C dla 6502... :/ Ilmenit 2009-04-14 17:06:29
@Yosh: Jak widzę CC65 nie potrafi optymalizować kodu, gdy wskaźnik zdefiniujesz na stałe miejsce w pamięci. Spójrz jednak na kod wygenerowany w poniższym przykładzie (cl65 -Osi -Cl -l test.c). Kod nawet ładniejszy niż z Actiona :-) Jak się jeszcze pobawię to napiszę chyba artka o tym jak pisać w CC65, żeby było szybko.