atarionline.pl Pętla 6502 na RTS - Forum Atarum

Jeśli chcesz wziąć udział w dyskusjach na forum - zaloguj się. Jeżeli nie masz loginu - poproś o członkostwo.

  • :
  • :

Vanilla 1.1.4 jest produktem Lussumo. Więcej informacji: Dokumentacja, Forum.

    • 1: CommentAuthor0xF
    • CommentTime2 dni temu
     
    Dyskusja z nowinki ->link<-

    0xF @2024-09-23 11:10:20
    Najfajniejszą rzeczą, której się nauczyłem, jest pętla na RTS, chociaż na 6502 to już nie taki dopał (6 cykli na RTS vs 8 na DEC ZP + BNE). A gotowy kod do rysowania krótkich linii można spokojnie przenieść nie tylko na 6502, ale na dowolną architekturę.

    tebe @2024-09-23 11:54:47
    :) pętla RTS ? w sensie wrzucasz na stos n-adresów początku takiej pętli, uruchamiasz i po każdym RTS skacze na wskazany początek

    taka pętla może wykonywać skoki pod różne adresy, niekoniecznie na początek takiej pętli

    0xF @2024-09-23 12:26:46
    Dokładnie.

    0xF @2024-09-23 13:24:20
    Z minusów trzeba uważać, żeby przerwanie nie zamazało stosu do następnego użycia. Z plusów zysk będzie większy, jeśli pętla jest długa i branch nie sięga.

    mono @2024-09-23 15:40:59
    Przecież przerwanie niczego nie zamazuje, bo adresy są za wskaźnikiem stosu.

    0xF @2024-09-23 15:45:56
    A jak wykonasz tę pętlę drugi raz, co? Przecież zapis na stos przed każdym użyciem się nie opłaca. Robisz LDX count2sp,Y TXS

    Cyprian @2024-09-23 16:59:02
    68k ma dwa stosy - użytkownika USP i superwizora (np. przerwania) SSP. Druga sprawa, jeśli program działa w trybie superwizora i korzysta z SSP to tu też przerwanie nie powinno nic namieszać.
    Przerwanie zrzuca stan/rejestry na stos na wejściu przerwania i przywraca je na wyjściu, czyli z punktu widzenia programu, stos nie został naruszony.

    0xF @2024-09-23 17:47:49
    Zrzuca na stos nie naruszając stosu?

    Cyprian @2024-09-23 18:53:10
    na 68k z punktu widzenia aktualnie wykonywanego programu przerwanie nie narusza stosu, gdyż wyjście z przetrwania przywraca jego pierwotny adres.

    Konop @2024-09-23 18:58:04
    Przerwania musiałyby nie manipulować stosem w ogóle (co może być uciążliwe) lub dysponować swoją ramką w obrębie stosu (osobnym wskaźnikiem stosu). W tym drugim przypadku należałoby zapamiętać gdzieś na boku w pamięci te 3 bajty odkładane przy wywołaniu przerwania, przywrócić zawartość stosu pod tymi lokalizacjami podczas wyjścia oraz wychodzić przez jmp odpowiednio modyfikując rejestr flagowy. To ostatnie stanowi pewne wyzwanie.

    To tylko teoria, bo w praktyce na A8 to niepraktyczne w szczególności, gdy pętla mogłaby kończyć się przez dex/dey bne.

    Trzeba uważać na to, aby przerwanie nie nadpisało stosu również podczas pierwszego wykonania pętli.

    pirx @2024-09-23 19:03:17
    co do kodu do rysowania linii - oczywiście da się zrobić na malucha, nie będzie taki ładny, bo kilka rozkazów na punkt, dodatkowo jednak trochę mało ramu na dane i któtki stos. ale końcepcja zadziała.

    0xF @2024-09-23 21:11:16
    Konop, ciekawa analiza. Odtworzenie flag trzebaby zrobić kilkoma instrukcjami już po przywróceniu wskaźnika stosu TXS i rejestru X.
    Założenie jest oczywiście takie, że w pętli potrzebne nam X i Y do celów innych, niż zliczanie przebiegów pętli.

    O co chodzi w ostatnim zdaniu? Dlaczego pierwsze wykonanie pętli jest szczególne?

    0xF @2024-09-23 21:31:14
    Ciekawe wyzwanie :)
    V
    0 CLV
    1 BIT CONST64
    NZ
    00 CMP #A-1
    01 CMP #A
    10 CMP #A+1
    11 BIT CONST128 ; w pętli zrobiliśmy BIT, bo przecież nie PLP, trzeba obsłużyć razem z V
    C
    0 CLC
    1 SEC
    I - tylko jeśli mamy IRQ
    0 CLI
    D - teoretycznie, bo raczej nie zmieniamy na przerwaniu
    0 CLD
    1 SED



    Konop @2024-09-23 21:35:04
    Rozumiem, że można przyjąć założenie o tym, że to się w praktyce nie zdarzy w przypadku efektów synchronizujących się do VBL, ale w ogólności musimy być pewni, że przerwanie nie zniszczy nam zawartości stosu w trakcie wykonywania pętli na głównym wątku, bez względu na to, czy jest to pierwsze, czy kolejne wywołanie. No i oczywiście to samo dotyczy sytuacji, w której inicjujemy "stos". Tutaj również przerwanie występujące w trakcie tej operacji nie jest mile widziane. Oczywiście należy to synchronizować.

    Konop @2024-09-23 21:46:56
    Pierwotnie sądziłem, że to może być skomplikowane, ale zapamiętanie rejestru flagowego w osobnej komórce pamięci gdzieś na początku przerwania, a później odtworzenie go tuż przed powrotnym skokiem w postaci lda storedFlags pha plp jmp (returnAddressPtr) powinno być wystarczające.

    0xF @2024-09-23 21:55:29
    Albo synchronizujemy się do VBL, albo robimy takie VBL, które sprząta po sobie na stosie (patrz wyżej).
    Z DLI co 8 linii to już za duży narzut.
    Dla inicjalizacji stosu jest jeszcze trzecia opcja: bez przestawiania wskaźnika stosu.

    Ale PHA zamaże stos, a mieliśmy zostawić czysty?

    0xF @2024-09-23 22:00:41
    Prościej będzie zapamiętać na VBL, które trzy bajty stosu popsuliśmy (TSX STX vbl_sp) i przywracać je przed następną pętlą.

    0xF @2024-09-23 22:43:42
    NZ
    00 LSR $D20C
    01 INC $D20C
    10 DEC $D20C


    Konop @2024-09-24 08:21:57
    0xF: PHA zapisze na tym nowym ("przerwaniowym"), dedykowanym stosie, jeszcze przed odtworzeniem wskaźnika stosu, więc powinno być OK. Tak, z DLI to raczej odpada. Tak, inicjalizacja stosu mogłaby odbywać się również przez regularne zapisy do pamięci bez użycia wskaźnika stosu, ale trzeba uważać na bieżącą zawartość wskaźnika stosu no chyba, że chcemy mieć jeszcze trzeci stos. Skończylibyśmy wtedy z a) regularnym stosem na głównym wątku wykorzystywanym poza pętlą rysującą , "stosem" wykorzystywanym do powrotu na początek pętli rysującej, stosem "przerwaniowym". Zastanawiałem się szerzej, gdzie można byłoby realnie skorzystać z faktu, że odczyty ze stosu są szybsze (3 cykle) w stosunku do regularnej pamięci, albo gdzie wygenerowalibyśmy krótszy kod (1 bajt na pla), ale to byłyby zawsze naciągane przykłady.

    Konop @2024-09-24 08:36:11
    Faktycznie, trzeba zadbać dodatkowo o N i Z. lda storedFlags / pha / plp / txs / ldx oldX / ldy oldY / lda oldA / FixNandZCode jmp (returnAddressPtr).

    Konop @2024-09-24 08:39:59
    Ten fragment FixNandZCode musiałby być dynamicznie nadpisany w zależności od tego jak ustawione były flagi N i Z w storedFlags. To jest do zrobienia.
    • 2: CommentAuthor0xF
    • CommentTime2 dni temu zmieniony
     
    Wpisujemy odpowiednią instrukcję lub skaczemy do niej.
    NZ
    00 INC $D01E ; bez ruszania C
    01 INC $D406 ; pod $D20C może być SIO FIFO
    10 DEC $D406
    11 BIT CONST128 lub BIT CONST192 w zależności od V

    Ostatnia kombinacja może się pojawić w wyniku BIT (PLP nie, bo w pętli nie można używać stosu) i zawartość akumulatora i pamięci spowodowała ustawienie Z.

    Edit:
    Jest jeszcze szczególny przypadek, gdy przerwanie obsługujemy, a nie jesteśmy w pętli RTS, np.
    BIT foo
    ; N=Z=1
    PHP
    ...
    LDA #$ff
    PLP

    i nie wiem, jak go ugryźć inaczej, niż wykryć na podstawie S, że jesteśmy poza pętlą i nie bawić się w to całe sprzątanie stosu.

    konop:

    odczyty ze stosu są szybsze (3 cykle)

    PLA i PLP mają 4 cykle. To PHA i PHP mają po 3 cykle.
    • 3: CommentAuthorKonop
    • CommentTime2 dni temu
     
    Wygląda na to, że odtwarzanie N oraz Z przy wyjściu z przerwania nie zadziała dla przypadku, kiedy BIT na głównym wątku odwali nam numer z jednoczesnym ustawieniem tych bitów. Problemem jest to, że musielibyśmy odtworzyć zawartość X już po przywróceniu SP, a to oznacza potencjalną modyfikację N oraz Z. Nie da się tego zrobić bez załadowania A dla późniejszego BIT'u. To oznacza kolejny powód dla zmiany flag. I tak bez końca. Niestety 6502 nie dysponuje instrukcją ładowania rejestru X bez wpływu na rejestr flag. Pomijam tutaj TSX, bo on się do tego zadania nie nadaje, ze względu na konieczność załadowania wcześniej X. Zastanawiałem się, czy może jakiś nieudokumentowany opcode mógłby tutaj pomóc, ale wygląda na to, że nie.

    Chyba jedyną opcją jest posiadanie dwóch, rozdzielonych stosów na głównym wątku. Pierwszy - regularny, oraz drugi - pętlowy. Zakresy wartości wskaźników dla tych dwóch stosów nie mogą się na siebie nachodzić. Przełączanie między stosami musi być w pełni skoordynowane na głównym wątku. Detektor SP na przerwaniu wtedy może dokonać dokładnej identyfikacji porcji wykonywanego kodu (pętla stosowa vs regularny kod). Oczywiście TXS jest instrukcją atomową - jak każda inna, więc wystąpienie przerwania podczas przełączania stosów nie stanowi problemu.

    Trzeba pamiętać, aby na stosie pętlowym najpierw wrzucić adres powrotny po zakończeniu iteracji. Detektor na przerwaniu musi wykryć wartość SP odpowiedzialną za ten adres, aby nie było niespodzianki. Zawartość fragmentu stosu odpowiedzialnego za przechowywanie adresu kontynuacji kodu po pętli musi być w takiej sytuacji prawidłowo przywrócona.

    Zakładając, że jedynym praktycznym wystąpieniem przerwania jest VBI, odtworzenie zawartości stosu dla wariantu pętlowego nie powinno być problematyczne z pkt. widzenia wydajności. W praktyce to oznacza, że gra jest warta świeczki wyłącznie w przypadku efektów wymagających renderowania w czasie przekraczającym 1 VBLANK. W obrębie 1 VBLANK żadnych dodatkowych zabiegów odtwarzających nie ma potrzeby realizować.

    Faktycznie PHA jest szybsze. OFFTOPIC: To urealnia scenariusze zastosowań. Przykładowo, dla kodu wrzuconego na stronę zerową, gdzie brakuje nam już miejsca na jedną linię rastra (32-48 bajtów), pakujemy ją na stos, wyświetlamy non stop ANTIC'iem, rysując jednocześnie w synchronizacji z rastrem poprzez PHA. Klasa efektów, które potencjalnie mogą zyskać to Kefren Bars, a szerzej te ostatnio popularne animacje na rastrze w oparciu o skompresowane dane.
    • 4: CommentAuthor0xF
    • CommentTime2 dni temu zmieniony
     
    Ustawienie N i Z przez BIT nie jest problemem. Wystarczy, jak pisałem wyżej, w ramach przywrócenia flag na koniec przerwania zrobić BIT z wartością 128 lub 192. Flaga Z też zostanie ustawiona, bo zawartość akumulatora jest już taka sama, jak w głównym wątku.

    lda oldFlags
    pha
    and #$c2 ; NVZ
    sta restoreNZJump+1
    plp
    ldx oldS
    txs
    lda oldA
    ldx oldX
    ldy oldY
    restoreNZJump
    jmp (restoreNZJumpTable)

    ert <restoreNZJumpTable
    org restoreNZJumpTable
    dta a(restoreN0Z0,restoreN0Z1)
    restoreN0Z0
    inc $d01e
    jmp (oldPC)
    restoreN0Z1
    inc $d406
    jmp (oldPC)
    restoreN1Z0
    dec $d406
    jmp (oldPC)
    restoreN1V0Z1
    bit const128
    jmp (oldPC)
    const128 dta 128
    restoreN1V1Z1
    bit const192
    jmp (oldPC)
    const192 dta 192

    org restoreNZJumpTable+$40
    dta a(restoreN0Z0,restoreN0Z1)
    org restoreNZJumpTable+$80
    dta a(restoreN1Z0,restoreN1V0Z1)
    org restoreNZJumpTable+$c0
    dta a(restoreN1Z0,restoreN1V1Z1)


    Professional Copy 2.0 z lat '90 wyświetla nazwy plików z pamięci ekranu na stosie aktualizowanej na DLI przy pomocy PHA.
    • 5: CommentAuthorKonop
    • CommentTime1 dzień temu
     
    To zadziała pod warunkiem, że w pętli RTS nie zdecydujemy się na odrobinkę perwersji w postaci odkładania flag na stos i późniejszego ich zdejmowania z niego. Pętla RTS może zapamiętywać zawartość stosu pod zapisywaną lokacją przed PHP i później go odtwarzać po wykonaniu PLP. Mało realne, podważające sens wykonywania wydajnego kodu w pętli RTS, ale teoretycznie możliwe.

    Bardziej realna jest sytuacja, kiedy operacje z odkładaniem/zdejmowaniem flag wykonamy na głównym wątku poza pętlą RTS.
    • 6: CommentAuthorświęty
    • CommentTime1 dzień temu
     
    o ile temat jest dość ciekawy to na razie nie widzę jakichś specjalnych zastosowań tego typu budowy kodu - ale może się mylę , w kązdym razie rts zajmuje tyle samo cykli co 2x jmp, tyle że ładowanie na stos jest szybsze niż zmiana wektorów skoków dla jmp $xxxx

    Konop:
    co do stosowanie stosu jako bufora, to w rewindzie 2 stosuję stos jako bufor - np kasowany przez n x pha bo jest szybsze niż sta $xxxx, po drugie używam indexowania tablic znacznikiem stosu , gdzie mechanizm:

    pla
    ...
    tsx
    lda tablica1,x

    jest całkiem sensowny - znacznik stosu sam się inkrementuje ;) i przy okazji jest kolejnym licznikiem

    oraz wykorzystywanie znacznika stosu jako dodatkowego rejestru
    • 7: CommentAuthor0xF
    • CommentTime1 dzień temu
     
    Nie rozumiem porównania RTS do 2x JMP. Tutaj chodzi o to, że RTSami możesz wykonać coś dokładnie N razy, a adresy trzymasz na stosie cały czas, a nie wrzucasz za każdym razem.

    Jak już wymieniamy doświadczenia: na stosie tworzyłem DL dla wektorów w Drunk Chessboard (ze względu na ruch szachownicy w pionie). W tymże Drunk Chessboard przejście między częściami jest RTSem - na początku wszystkie adresy są wrzucone na stos. W firmwarze Karin Maxi procedura formatowania może zostać wykonana z różnym przeplotem sektorów. Domyślny przeplot "co 2" jest zapisywany na stosie.
    • 8: CommentAuthorświęty
    • CommentTime1 dzień temu
     
    jeśli chodziłoby o zwarty kod to może i jest w tym jakiś sens, w przeciwnym wypadku lepiej rozpętlić kod pętli ... tutaj bardziej wydawało mi się że chodzi o dynamicznie budowany kod podskoków , który miałby coś robić w zależności od zawartości stosu , ale i tak uważam to jako ciekawostkę. Może w intrze 256b to miałoby sens albo mój mózg nie dojrzał do tego jeszcze
    • 9: CommentAuthor0xF
    • CommentTime1 dzień temu
     
    Tak, to jest ciekawostka - sporo problemów, a pożytek znikomy, jeśli w ogóle. A nie każdy kod opłaca się rozpętlać.