1 Ostatnio edytowany przez laoo/ng (2012-10-07 13:54:55)

Nie było to rozgłaszane, ale nie jest tajemnicą, że piszę diagnostykę i firmware dla pasiowej karty turbo. Przez te kilka tygodni zdążyłem się przekonać, że 65c816 w pełnej przestrzeni adresowej jest diabelnie trudnym procesorem do oprogramowania w asemblerze. Główną trudność stanowi jego kontekstowość, o której trzeba pamiętać pisząc każdy fragment kodu:

  • w zależności od stanu procesora rozkazy natychmiastowe mogą łykać 8 lub 16 bitów operandu,

  • w zależności od okoliczności ten sam adres logiczny może wskazywać na rózne adresy fizyczne, np. lda $00 to adresowanie zerostronicowe (direct) więc adres fizyczny to D + $00, lub adresowanie absolutne, które adresuje pamięć w banku danych (niekoniecznie tożsamym z bankiem zerowym) oraz adresowanie długie, które to dopiero zaadresuje komórkę o fizycznym adresie $000000.

  • "strona zerowa" jest ruchoma i indeksowanie 16-bitowe może trafić gdzie indziej niż to wynika z kodu, bo $00,X nie wskazuje na adres X, tylko D + X.

  • równocześnie można operować na trzech różnych bankach: zerowym, banku danych, banku programu, przez co takie sta *+4 nie zawsze trafia w operand następnego rozkazu.

O co zatem mi chodzi? O to, że składnia asemblera 6502 ma się nijak do programowania 65c816 i straciłem sumarycznie wiele godzin z powodu błędów wynikłych z nawyku programowania 6502. O wiele za dużo, szczególnie, że w części z nich z pewnocią mógłby pomóc asembler. Założyłem ten wątek, bo nie jestem 100% pewny jakie rozwiązanie byłoby najlepsze i może ktoś będzie miał jakieś fajne pomysły i spostrzeżenia.

Skrajnym rozwiązaniem byłoby zaproponowanie składni niekompatybilnej z dotychczasową, ale wadą byłaby konieczność nauki asemblera od nowa. Rozwiązaniem pośrednim i najprostszym mógłby być jakiś tryb "strict" istniejącej składni. Najważniejsze co byłoby niezbędne w tym trybie to wyłączenie optymalizacji długości operandu oraz obligatoryjne specyfikowanie długości. Byłoby więcej pisania, ale to zdecydowanie mniejsze zło od głupich pomyłek, bo przecież każdy z tych rozkazów robi coś innego:

0000 A5 00           lda.b 0
0002 AD 00 00        lda.w 0
0005 AF 00 00 00     lda.l 0

0009 A9 00           lda.b #0
000B A9 00 00        lda.w #0

i przy braku specyfikacji długości w zależności od tego jaki operand wpadnie to wygeneruje się inny rozkaz.


Następna rzecz, której brakuje mi w madsie, to możliwość nadawania adresu asemblacji poza bankiem zerowym. ORG przyjmuje tylko adres z przedziału $0000 do $ffff i napisanie kodu dedykowanego bankowi np $f0 nastręcza niemałych trudności (kilka godzin szukałem błędu, którym był skok jsl, który trafiał do banku zerowego).

To nie rozwiązuje wszystkich problemów, ale przynajmniej te najważniejsze.

Ogólnie spostrzeżenia mam takie, że karta turbo z 65c816 i 16 MB RAMu to już nie atari ;)
Jak widać programuje się to zupełnie inaczej i daje zupełnie inne możliwości. Pamięć atari w banku zerowym może być traktowana coś a la pamięć CHIP w amigach, i opłaca się go używać tylko do pamięci ekranu a kod i dane można trzymać wyżej, ale to wymaga szerokiej infrastruktury w postaci formatu plików wykonywalnych dedykowanych wysokim bankom i systemu operacyjnego, który to wszystko obsłuży.
No i dość dobrze pasowałby tu kompilator wysokiego poziomu, bo 816 ma tryby adresowania wspierające pracę z kompilatorami (np., adresowanie względem wskaźnika stosu) i jak ma się tak dużo pamięci, to nie trzeba optymalizować na rozmiar.

2

Nie jest też tajemnicą że WDC zaproponowało razem z procesorem składnię takiego asemblera, który w porządny sposób rozprawia się z możliwosciami tego procesora. To co my mamy to asemblery zorientowane na 6502 z kalekimi protezami dla 65816.
Nie wiem jak to wygląda w przypadku np ca65, gdzie 816 jest uwzględniony. Po prostu ktoś musi napisać nowy asembler i tyle :))))

"Was powinny uzbrojone służby wyciągać z domów do punktów szczepień, a potem zamykać do pi* za rozpowszechnianie zagrożenia epidemicznego" - Epi 2021
"Powinno się pałować tylko tych co tego nie rozumieją. No i nie szmatki i nie chirurgiczne tylko min FFP3, to by miało jakiś sens. U mnie we firmie, to jak przychodzi bezmaskowiec, to stoi w deszczu przed firmą" - Pin 2021

3

Jak pisze syscall, wiele rzeczy zostało już wymyślone, wystarczy poczytać dokumentację, zamiast kolejny raz przebijać się przez ścianę obok otwartych drzwi.

lda.b $00 = lda <$00

lda.w $00 = lda !$00

lda.l $00 = lda >$00

Z tego oczywiście pierwszy i ostatni przykład jest nie do zastosowania w madsie, jak mi się zdaje, bo tebe strzelił sobie w stopę przyjmując z rzyci wyciągniętą składnię QA za "standard".

Co do skoków JSL, wystarczy 24-bitowe adresowanie etykiet, które ma już MAE. Dużo gorszy jest brak zunifikowanego systemu ładowania czegokolwiek do wysokiej pamięci, kod i dane trzeba przepisywać i ewentualnie relokować "ręcznie", co jest uciążliwe. Natomiast format pliku sam w sobie istnieje, wzmankowany jest np. tutaj http://atariki.krap.pl/index.php/COM#AlfAssembler - nb. ten cały AlfAsm też może być jakimś punktem zaczepienia, jakkolwiek nie używałem go nigdy.

Że 65C816 z wiadrem fastu to "nie Atari", to się zgodzę - na pewno nie jest to Atari jakie znamy. Techniki programowania są zupełnie inne, i mnie się to akurat podoba, bo pokazuje to inną stronę procesorów 65xx, i czegoś trzeba się nauczyć, zamiast mielić w kółko te same zgrane od 30 lat schematy, ale też inne fajne rzeczy można robić (albo te same, tylko inaczej).

KMK
? HEX$(6670358)

4 Ostatnio edytowany przez laoo/ng (2012-10-07 14:49:13)

Operatory < ! > wydają się sensowne. Asembler powinien tylko wypisywać warning gdy żaden operator nie jest użyty (programista oczywiście może wiedzieć o co mu chodzi, ale tutaj wg mnie lepiej być dosłownym, bo to zmniejsza okazję do błędu).

Mads ma 24 bitowe adresowanie, nie potrafi tylko ustawić adresu asemblacji na adres spoza banku zerowego. Nie potrafi tego ani dyrektywa ORG, ani .SEGDEF. Odkryłem jednak, że potrafi to blok .PROC, któremu można ustawić dowolny 24-bitowy adres. Gdy już myślałem, że jest to rozwiązanie, okazało się, że z 24-bitowego adresu nie można (albo raczej nie potrafię) wyciągnąć 16 bitowego offsetu w segmencie i taki kod się nie kompiluje:

    opt h- c+

    org $0000

    .proc test, [$010000 + *]
label    lda.w #label
    .endp
label    lda.w #label
test.asm (6) ERROR: Value out of range

O nagłówku $FBFB dobrze wiedzieć. Szukałem czegoś takiego. Sprawdziłem i działa poprzez dodanie nagłówka z dwoma 24-bitowymi adresami do każdego bloku. Aczkolwiek wydaje mi się, że adres końca wystarczyłby 16 bitowy, ale to już szczegół.

Co do asemblerów, to w grę wchodzą tylko cross-assemblery (mam już środowisko do budowania itd). Co jest w madsie, a niema tego (nie znalazłem) nigdzie indziej, to idea procedur (bloki .PROC), która po podrasowaniu bardzo dobrze może wpisywać się w schemat programowania 65c816. Blok .PROC grupuje fragment kodu w logiczną całość z lokalnymi etykietami, a przekazywanie parametrów może być zrealizowane za pomocą stosu 65c816. Np. "engine" do deklarowania parametrów procedury mógłby służyć do śledzenia zawartości stosu. Np takie procedury mogłyby być tożsame:

    .proc sum1 ( .BYTE par1,par2 )
    lda pra1
    clc
    adc par2
    rts
    .endp


    .proc sum2
    lda 2,s
    clc
    adc 1,s
    rts
    .endp

Dodatkowo procedury można byłoby wzbogacić o chociażby specyfikację stanu procesora jaki w niej obowiązuje (długość rejestrów) i asembler mógłby śledzić czy stan jest spójny pomiędzy wywołaniami (niby nie daje to żadnych gwarancji, ale pomaga przy zachowaniu konwencji, no i tak, wiem, że mechanizmy śledzenia to nic nowego :)).

5

laoo/ng napisał/a:

O nagłówku $FBFB dobrze wiedzieć. Szukałem czegoś takiego. Sprawdziłem i działa poprzez dodanie nagłówka z dwoma 24-bitowymi adresami do każdego bloku. Aczkolwiek wydaje mi się, że adres końca wystarczyłby 16 bitowy, ale to już szczegół.

816 co prawda nie przejedzie kodem przez granicę 64k, ale zawsze przecież można mieć w jednym bloku 64k kodu i 64k (albo więcej) danych. Więc nie jest to tak kompletnie nieprzydatne.

Przydałby się też format niezależny od położenia w pamięci, żeby można było załadować coś nie na rympał, ale tam, gdzie akurat jest wolne miejsce. To jest jeszcze do wymyślenia.

Czy mamy ten format ($FBFB) zaimplementować w SDX?

KMK
? HEX$(6670358)

6 Ostatnio edytowany przez tebe (2012-10-07 15:19:42)

Możliwe jest użycie rozszerzenia mnemonika po znaku kropki '.' dla rozkazów typu LDA, LDX, LDY, STA, STX, STY:

   .b lub .z          BYTE
   .a lub .w lub .q   WORD
   .t lub .l          TRIPLE, LONG (24bit)

np.
   lda.w #$00   ; A9 00 00
   lda   #$80   ; A9 80

Wyjątki stanowią rozkazy n/w, którym nie można zmienić rozmiaru rejestru w adresowaniu absolutnym (niektóre assemblery nie wymagają dla tych rozkazów podania znaku '#', jednak MADS wymaga tego)
#$xx

   SEP   REP   COP

#$xxxx

   PEA

Innym wyjątkiem jest tryb adresowania pośredni długi, który reprezentowany jest przez nawiasy kwadratowe [ ]. Jak wiemy tego typu nawiasy wykorzystywane są też do obliczania wyrażeń, jednak jeśli asembler napotka pierwszy znak '[' uzna to za tryb adresowania pośredni długi i jeśli nie zasygnalizowaliśmy chęci używania 65816 wystąpi błąd z komunikatem Illegal adressing mode. Aby "oszukać" assembler wystarczy dać przed kwadratowym nawiasem otwierającym '[' znak '+'.

 lda [2+4]     ; lda [6]
 lda +[2+4]    ; lda 6

Unary operators:

+  Plus (does nothing)
-  Minus (changes sign)
~  Bitwise not (complements all bits)
!  Logical not (changes true to false and vice versa)
<  Low (extracts low byte)
>  High (extracts high byte)
^  High 24bit (extracts high byte)
=  Extracts memory bank
:  Extracts global variable value
*- TeBe/Madteam
3x Atari 130XE, SDX, CPU 65816, 2x VBXE, 2x IDE Plus rev. C

7

Draco: Format ładowania w dowolne miejsce mógłby być przydatny, gdyż kod 816-tki może być relokowalny bez żadnej ingerencji (aczkolwiek programowanie kodu relokowalnego jest trochę uciążliwe). Trzeba byłoby tylko wymyślić sposób na powiadamianie kodu, gdzie zostały załadowane jego dane (coś a'la bloki RUN i INIT?).

Trzeba utworzyć jakiś komitet standaryzacyjny i podyskutować nad takimi rzeczami przed konkretną implementacją w konkretnych systemach :)


TeBe: Te fragmenty w dokumentacji znam, ale nie tłumaczą one np. jak otrzymać 16 młodszych bitów z adresu 24-bitowego.

8

laoo: nie taki format miałem na myśli, kod, który jest sam z siebie position-independent, nie potrzebuje żadnego specjalnego formatu. Raczej chodziło mi o coś w rodzaju tego, co jest opisane w instrukcji od madsa - czyli niby jest, nie wiem tylko, na ile to praktyczne i przetestowane (z binariami SDX mads miewał jazdy).

KMK
? HEX$(6670358)

9

OK. O tym nie myślałem. Nie wiem czy stosunek trudność do zysków jest opłacalny, żeby się za to zabrać. A format COMa position-independent, którego trzeba po prostu załadować pod dowolny adres, potrzebuje przynajmniej nagłówka sygnalizującego niezależność od adresu oraz długość bloku. Czegoś takiego nie ma, bo na 6502 nie było to sensowne, a na 65c816 już tak.

10 Ostatnio edytowany przez tebe (2012-10-07 17:07:02)

laoo/ng napisał/a:

Te fragmenty w dokumentacji znam, ale nie tłumaczą one np. jak otrzymać 16 młodszych bitów z adresu 24-bitowego.

pewnie przesuwają bity >> lub << lub AND-ując

możesz też stworzyć jakieś makro które będzie wycinać konkretnie

p.s.
ORG powyżej $FFFF mads nie obsłuży, bo nie miałby jak zapisać tego do wyjścia. ATARI DOS czy SDX nie obsługują adresów > $FFFF

*- TeBe/Madteam
3x Atari 130XE, SDX, CPU 65816, 2x VBXE, 2x IDE Plus rev. C

11

Faktycznie. Na zrobienie  lda.w #( label & $ffff ) nie wpadłem. Zastosuję.

Na ORG powyżej $FFFF można mieć dwa sposoby:

  • Zezwalać na takiego orga tylko przy opt h-

  • Zastosować nagłówek AlfAssemblera zaproponowany przez Draco, czyli $FBFB

12 Ostatnio edytowany przez tebe (2012-10-08 07:43:43)

w mads ORG-a można zaprojektować wg potrzeb

 opt h-f+
 ORG [a(start), a(over-1)],$2000

start
         nop
over

adres asemblacji po nawiasie kwadratowym jest wymagany, zadziała to tylko gdy OPT H-

dla wspomnianego nagłówka $FBFB, ORG może wyglądać tak:

    opt h-c+
    ORG [a($fbfb),t(prg_start,prg_end-1)],$FFFF80

prg_start
    nop

prg_end

adresy są 24 bitowe

p.s.
w załączniku mads po poprawce, akceptuje adresy ORG 24 bit gdy OPT C+

Post's attachments

mads_07.10.2012.7z 141.01 kb, liczba pobrań: 3 (od 2012-10-07) 

Tylko zalogowani mogą pobierać załączniki.
*- TeBe/Madteam
3x Atari 130XE, SDX, CPU 65816, 2x VBXE, 2x IDE Plus rev. C

13

O! Super! Wiedziałem TeBe, że można na Ciebie liczyć :)

Jak zrobi się opt h- (w przykładzie jest przypadkowo h+), to generowany plik jest identyczny jak z AlfAssemblera.

14 Ostatnio edytowany przez tebe (2012-10-08 07:52:13)

w mads jest też możliwość śledzenia rozmiaru rejestrów OPT T+

    opt c+t+
    org $2000

    rep #$20

    lda #0

dla w/w przykładu wystąpi komunikat błędu
ERROR: Bad register size

w tym przypadku prawidłowo powinno być

 lda.w #0

OPT T+ sprawdza rozmiar tylko dla adresowania absolutnego #

*- TeBe/Madteam
3x Atari 130XE, SDX, CPU 65816, 2x VBXE, 2x IDE Plus rev. C

15 Ostatnio edytowany przez laoo/ng (2012-10-08 08:09:02)

Jeszcze tego nie próbowałem. Sprawdzę, jak to działa.

Nie wiem jak dokładnie masz zrealizowane te śledzenie, bo to nie jest trywialna sprawa, ale dość silnym mechanizmem mogłoby być śledzenie szerokości rejestrów na poziomie procedur (teraz trochę pofantazjuję):
- W samej deklaracji procedury, lub jako pierwszy pseudorozkaz (coś ala .a8x16, .a16x8 itd) mogłaby być deklaracja jaka szerokość rejestrów obowiązuje na wejściu.
- Wewnątrz procedury byłoby normalne śledzenie szerokości zaczynając od zadeklarowanego stanu początkowego.
- błędy byłyby generowane przy próbie użycia innej szerokości (tak jak już masz to zaimplementowane) ORAZ podczas próby wywołania procedury, która ma zadeklarowaną inną wstępną szerokość niż aktualna szerokość w miejscu wywołania.

Wyobrażam sobie, że jest to osiągalne, bo można byłoby wywołanie procedury traktować jako instrukcję wrażliwą na szerokość argumentu (taki lda #) z taką szerokością, jaka jest w danej procedurze deklaracja.

16

laoo: czyli proponujesz odpowiedniki http://www.cc65.org/doc/ca65-11.html#ss11.1 ?
cos a'a #pragma w c?

The UNIX Guru`s view of Sex:
unzip; strip; touch; finger; mount; fsck; more; yes; umount; sleep

17

Wiedziałem, że gdzieś widziałem już coś takiego, tylko nie wiedziałem gdzie :)

18

ustawianie odpowiedniego rozmiaru rejestrów realizowane jest poprzez makra, parametrem jest wartość 8 lub 16

*- TeBe/Madteam
3x Atari 130XE, SDX, CPU 65816, 2x VBXE, 2x IDE Plus rev. C

19

Używam tych makr. Tutaj akurat chodzi o hint dla asemblera (deklarację) jaka jest spodziewana szerokość rejestrów w danym miejscu, tak, aby asembler mógł wygenerować błąd, jeśli obliczona przez niego szerokość będzie różna, czyli np. (w pseudokodzie):

regA 16  ;rozkaz przełączający akumulator na 16 bitów
.a8 ; deklaracja, że w tym miejscu akumulator ma rozmiar 8 bitów -> ERROR

albo

.proc Prodecura
.a16 ;deklaracja 16-bitowego akumulatora.
...
.endp

...

.a8   ;deklaracja 8-bitowego akumulatora.
jsr Procedura  ;skok do procedury z kontekstu 8-bitowego akumulatora.
                Procedura deklaruje szerokość na 16-btów -> ERROR
...

20

akumulatora? a nie "trybu 16bit"? bo to sie tyczy ztcp i innych instrukcji...

The UNIX Guru`s view of Sex:
unzip; strip; touch; finger; mount; fsck; more; yes; umount; sleep

21

Osobno można ustawiać szerokość akumulatora oraz rejestrów indeksowych. To działa tylko w trybie natywnym 65c816.

22

Jezu... A ja myślałem kiedyś że tryb protected w 386 jest skomplikowany 8-)

The problem is not the problem; the problem is your attitude about the problem

23

To nie jest takie straszne, jak wygląda na pierwszy rzut oka, idzie się przyzwyczaić. Ale na początku są trudności :)

KMK
? HEX$(6670358)