atarionline.pl Rzeczy które chciałbyś w MADSie ale boisz się poprosić :) - 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:
       
      CommentAuthorxeen
    • CommentTime14 Dec 2015
     
    Przeglądam sobie manuala do KICK assemblera i twierdzę, że feature opisany w punkcie 10.7 byłby bardzo wygodny przy współpracy grafik - koder jeżeli grafik preferuje np. grafx2

    ->link<-

    Zakładam wątek - może ktoś ma inne przemyślenia co by jeszcze można dodać.
    • 2: CommentAuthorbob_er
    • CommentTime14 Dec 2015
     
    Hmm... Nie widzę tam punktu 10.7.
    • 3:
       
      CommentAuthorxeen
    • CommentTime14 Dec 2015
     
    10.7Converting Graphics - na stronie 49
    • 4: CommentAuthorbob_er
    • CommentTime14 Dec 2015
     
    U mnie w spisie treści jest '9.7 Converting Graphics', ale ok :).
    • 5: CommentAuthortebe
    • CommentTime14 Dec 2015
     
    mads używa tylko biblioteki 'system' czyli jedynie podstawowej

    wczytywanie bmp bez kompresji jest możliwe, napisanie makra robiącego z taką bmpą coś więcej też

    w przykładach są makra dokonujące relokacji RMT, MPT, CMC bez potrzeby zapisywania modułów przez dedykowane trackery
    • 6: CommentAuthorilmenit
    • CommentTime13 Jul 2021
     
    MADS posiada świetny system definiowania segmentów i można np. zdefiniować sekcje jak w kompilatorach: CODE, DATA, BSS czy ZEROPAGE, BANK, gdzie asembler pilnuje przekraczania sekcji i nadpisywania innych danych.

    Co byłoby przydatne, to:
    - możliwość definiowania segmentów mających kilka obszarów np. jeden segment CODE $2000 do $4000, ten sam segment $8000 do $D000, ten sam segment od $D800 w górę.
    - automatyczne przydzielanie pamięci do takich kilku segmentów minimalizujące liczbę dziur (uwzględniające .align) gdy się użyje .segment CODE

    W ten sposób można by się pozbyć ręcznego zarządzania pamięcią (aktualnie trzeba samemu definiować co do którego segmentu ma iść, gdy się nie mieści) a może by też zoptymalizowało generowane pliki dla Mad Pascala?
    Zakładam, że można by do tego wykorzystać jakieś gotowe rozwiązanie dla problemu plecakowego ->link<- (PL) lub ->link<- (ENG)
    • 7: CommentAuthorlaoo
    • CommentTime13 Jul 2021 zmieniony
     
    Też bym chciał to mieć bo także mi to zaczęło doskwierać. I to na tyle mocno, że pracuję na boku nad narzędziem rozwiązującym ten problem (a nawet powinno rozwiązać kilka problemów więcej jak przydzielenie różnym zmiennym automatycznym z innych funkcji tej samej lokacji na zp). W ogólniejszej formule to nie jest do końca problem plecakowy, ale myślę że da się to ogarnąć systemem więzów (pisałem z więzów magisterkę, niech się na coś to przyda ;)).
    • 8: CommentAuthorilmenit
    • CommentTime13 Jul 2021 zmieniony
     
    też w aktualnym projekcie używam zmiennych lokalnych typu:
    .proc zrob_cos
    ...kod...
    rts
    zmienna_lokalna_byte .byte 0
    zmienna_lokalna_word .ds 2
    .endp

    i rzeczywiście data reuse by się przydał (choć to może ze 100 bajtów oszczędzone na cały projekt), ale do tego asembler by musiał generować drzewo wywołań i cały kod by miał mieć gwarantowane, że nie będzie dziwnych skoków (które akurat u mnie dla optymalizacji są).
    No i takie zmienne lokalne, które aktualnie nie mają reuse bardzo pomagają debugować kod - mogę zobaczyć co było w jakieś procedurze długo po jej wykonaniu, w momencie wykonywania innego kodu.
    Ogólnie fajnie, jakby takie zmienne niezaincjowane .ds można przenieść automatycznie do jakiejś sekcji BSS, albo wybrać, czy mają być lokalnie w kodzie przy procedurach, czy nie (gdy np. chcemy taki kod przenieść do ROMu).

    O systemie więzów muszę przeczytać, ja zakładałem, że to ->link<-
    • 9: CommentAuthorlaoo
    • CommentTime13 Jul 2021 zmieniony
     
    "Data reuse" ma sens tylko w przypadku strony zerowej i to zawsze powinna być opcja - jeśli jakiś wskaźnik czy licznik jest wybitnie tymczasowy to można jego adres współdzielić z innymi tego typu zmiennymi.
    Na wykrywanie co można współdzielić mam chyba dobry patent - wystarczy odkryć wszystkie zależności pomiędzy segmentami i jeżeli dwa segmenty kodu nie mają żadnej relacji z wyjątkiem takiej że ktoś inny je woła, to segmenty danych tymczasowych do jakich się odwołują mogą zajmować te same adresy. Jasne, że zawsze można robić jakieś krzywe skoki, ale wtedy można systemowi pomóc dodając zależności ręcznie.
    Przykład, który podałeś to problem plecakowy, ale z tego co widzę, to jest rozwiązywany ogólnym solverem do więzów, czyli takim jakiego ja używam. Nawet używam tej samej biblioteki - googlowe OR-Tools to state of the art w dziedzinie (przynajmniej wśród darmowych pakietów). Poczytaj sobie o "Programowaniu z więzami".
    Ogólnie zdecydowałem się na taką armatę, bo ja uogólniłem problem także w kwestii czasu - u mnie różne segmenty mogą być w pamięci w różnych momentach działania programu i pozwalam na tranzycje pomiędzy tymi konfiguracjami (np podział na menu/levele/hi-score/ending w grze czy różne fragmenty dema). Zresztą nie ma co zaśmiecać tego tematu bo to jest o madsie, a nie o moich fanaberiach, których mogę nigdy nie skończyć :)
    • 10: CommentAuthorilmenit
    • CommentTime14 Jul 2021
     
    takie rozważania są ciekawe, bo mogą przynieść też pomysły do MADSa :-)
    Odnośne reuse zmiennych lokalnych (np. na ZP), to gdy są naprawdę lokalne, to zmienną typu zp_local1, zp_local_ptr1 można zdefiniować raz i potem używać wszędzie bez obawy, że coś przestanie działać. Problem jest z zapewnieniem czytelności kodu, ponieważ taki zp_local_ptr1 to "ogólna nazwa", gdy chcielibyśmy lokalnie używać nazwy src_ptr. Wciąż da radę to zrobić w MADS stosując aliasy nazw:

    .struct @ZP
    local1 .byte
    local_ptr1 .word
    local_ptr2 .word
    .ends
    ;;; Define segment
    ZP_START = $80
    .segdef seg_zp ZP_START $100-ZP_START

    CODE_START = $2000
    BANK_ADDRESS = $4000
    .segdef seg_first CODE_START BANK_ADDRESS-CODE_START
    ;;; Put data into segment
    .segment seg_zp
    .var zp @ZP
    .endseg

    .segment seg_first
    DATA_SIZE = 64
    data .ds DATA_SIZE

    .proc copy_data (.word src_ptr .word dst_ptr .byte size) .var
    ; set local variables
    size = zp.local1
    src_ptr = zp.local_ptr1
    dst_ptr = zp.local_ptr2

    ldy #0
    copy_loop:
    lda (src_ptr),y
    sta (dst_ptr),y
    iny
    cpy size
    bne copy_loop
    rts
    .endp

    main:
    copy_data #$C000 #data #DATA_SIZE
    jmp *
    .endseg
    RUN main


    Choć "ładniej" by było zadefiniować:
    .proc copy_data (.word src_ptr .word dst_ptr .byte size) .var
    ; local variables on zp
    size .zp_ds 1
    src_ptr .zp_ds 2
    dst_ptr .zp_ds 2

    i nie przejmować się gdzie na ZP to będzie.

    Dlatego o ile reuse nie jest dla mnie aż takim problemem, większym jest zarządzanie pamięcią z punktu widzenia rozbicia jednego segmentu na kilka obszarów. Niestety pamięć w Atari ma dwie główne dziury - jedną dla banku (od $4000 lub $8000), a drugą dla sprzętu (od $D000), przez co do wykorzystania są 3 niezależne segmenty na pamięć podstawową gdzie trzeba "ręcznie" wrzucać kod i dane i przesuwać między tymi segmentami, gdy się któryś przepełnia.
    • 11: CommentAuthorlaoo
    • CommentTime14 Jul 2021
     
    Generyczne miejsca na stronie zerowej i aliasy do nich to oczywiście rzeczy, które się stosuje i to nie jest coś złego. Z doświadczenia jednak wiem, że przy naprawdę dużym projekcie, który ledwo mieści się w pamięci (albo nie mieści i trzeba go optymalizować pamięciowo, żeby dodać coś nowego), jeżeli nie pisze się w pojedynkę, to ogarnięcie co gdzie jest i gdzie mamy jakieś wolne bajty, jest dość dużym wysiłkiem. Mieliśmy już kilka takich projektów. W TP literalnie zostało kilka wolnych bajtów, o których wiem. Wydaje mi się jednak, że gdyby tą pamięcią zarządzał automat, to zrobiłby to o wiele lepiej, a człowiekowi byłoby o wiele lżej. Decydowanie, w który z trzech obszarów pamięci Atari (które opisałeś) wrzucić kod, czy dane, też jest pewnym wysiłkiem. A co dopiero decydowanie o bankach i pamiętanie co w którym jest. Mnie tak jak w Twojej dewizie z Adam is Me - [...] exploring capabilities of augmenting retro-programming with power of the modern computers - dziwi po prostu, że przez tyle lat nikt nie napisał takiego automatu zarządzającego pamięcią i wiem, że bez niego wyżej już trudniej mi się wspiąć, bo za dużo energii poświęcam na houskeeping.
    • 12: CommentAuthorilmenit
    • CommentTime14 Jul 2021
     
    Też tak uważam. MADS czy KickAss mają wiele bajerów bardzo ułatwiających pisanie "wysokopoziomowo", ale wciąż trzeba samemu zajmować się rolą linkera.
    Z drugiej strony pytanie ile osób coś takiego potrzebuje i lubi. Ja lubię mieć w kodzie porządek i lubię czytelny kod, ale nawet w naszej grupie są tacy, dla których to zbędne wymysły, bo to asembler i nawet używają konkretnych adresów typu $E0 na ZP czy $D301 zamiast tekstowych odpowiedników ;)
    Piszesz swojego asemblera czy jakiś dodatkowy preprocesor? Własny asembler to maaasa pracy, aby dobić do poziomu MADSa.
    • 13: CommentAuthorlaoo
    • CommentTime14 Jul 2021 zmieniony
     
    Pisałem już preprocesor dla madsa - asemblowałem fragment kodu raz pod jakiś adres, określałem jego rozmiar, przydzielałem adres i asemblowałem drugi raz już pod docelowy adres i jakoś to działało. Tak teraz powstają nasze kody pod Lynxa. Ale szybko doszedłem do wniosku że to zbyt zagmatwane i mało elastyczne, więc zacząłem pisać coś innego, co nie jest stricte asemblerem, a jeśli już, to w innym znaczeniu słowe "assemble", czyli "składać", bo jest to narzędzie, które operuje segmentami generowanymi na różne sposoby (np przez kod lua), a jednym z generatorów może być prosty asembler 6502, więc nie mam zamiaru dobijać do poziomu madsa, bo mads jest tylko jeden i nie potrzeba drugiego :) a z drugiej strony nie potrzebuję - wszelkie dane mogę generować zewnętrznie, a asemblerem tylko prosty kod. Każdy segment musi eksportować jakieś symbole i może importować jakieś inne z innych segmentów. Ostatecznie dostaję graf zależności, odrzucam segmenty do których nie można dojść z segmentu startowego, nakładam więzy na adresy tych segmentów, tu dzieje się magia, po czym constraint solver powinien mi wypluć adres każdego z nich. Potem już tylko złożyć to w plik wynikowy. Tyle teorii.
    Z ciekawostek jednym z generatorów segmentów jest u mnie importer ELFa, więc czekam na mos-llvm :)
    • 14: CommentAuthorpirx
    • CommentTime15 Jul 2021
     
    ja myślałem o czymś prostszym, może do dość łatwego zrobienia - przy okazji walki z kilkuset stronami z zajętymi 160 bajtami myszlałem o jakiejś metodzie zaznaczenia w madsie bloków, które ma omijać. w kodzie niekrytycznym czasowo wystarczałoby, żeby kompilator na końcu takiej dziury do wykorzystania skakał do następnej dziury zwykłym jmp.
    Z takim rozwiązaniem nie bolałoby nawet zrobienie ekranu graficznego z 40 bajtami na stronę.
    • 15: CommentAuthorilmenit
    • CommentTime15 Jul 2021 zmieniony
     
    @pirx - to, o czym piszesz, to trochę inny problem, choć zakładam, że po zaimplementowaniu "problem wielu plecaków" do rozmieszczania kodu, by sie i to automatycznie rozwiązało, gdyby asembler automatycznie wrzucał kod/dane w takie dziury na końcu stron. Ale przy pełnej automatyzcji organizacji pamięci przez asembler nie byłoby gwarantowane, że linie ekranu by były przydzielane na kolejnych stronach, więc trzeba by robić tablice wskaźników do linii w stylu:

    SCREEN_WIDTH = 40
    SCREEN_LINES = 24
    .rept SCREEN_LINES, #
    .align $100
    screen_line:1 .ds SCREEN_WIDTH
    .endr

    screen_ptr_lo
    .rept SCREEN_LINES, #
    .byte #<(screen_line:1)
    .endr
    screen_ptr_hi
    .rept SCREEN_LINES, #
    .byte #>(screen_line:1)
    .endr

    Co się nie różni wiele od ciągłego obszaru danych i wskaźników do poszczególnych linii dla (ptr),y
    • 16: CommentAuthorlaoo
    • CommentTime15 Jul 2021
     
    @ilmenit wyobrażam sobie, że można byłoby mieć taki język "życzeń" co do allokacji pamięci, w którym dałoby się wyrazić, żeby pamięć ekranu zajmowała poczatki 20+ kolejnych stron. Ogólnie super pomysł to co podrzucił Pirx, że segment wcale nie musi być ciągły, a może mieć wręcz dziury i nawet automat mógłby rozlewać kod po takich dziurach kontynując go automatycznie wstawionymi jumpami. Dzięki! :)
    • 17: CommentAuthortebe
    • CommentTime15 Jul 2021
     
    w Mads jest dyrektywa .zpvar alokująca zmienne na stronie zerowej, można ją zapisywać i odczytywać

    .zpvar = $80
    .zpvar .word tmp
    .print .zpvar ; = $82


    przykład procedur i alokacji zmiennych na stronie zerowej
    .proc pr1

    .zpvar = $80

    .zpvar .word ala

    zpmax = .zpvar ; dodatkowa etykieta z informacją o
    ; maksymalnym adresie na stronie zerowej
    .endp


    .proc pr2

    .zpvar = $80

    .zpvar .long tmp

    zpmax = .zpvar
    .endp
    .print pr1.zpmax ; = $82
    .print pr2.zpmax ; = $82


    można też uzależnić adres początkowy .zpvar od adresu z którego korzysta inna procedura, brakuje tylko możliwości wybrania MAX w przypadku kilkunastu procedur korzystających ze strony zerowej w ten sposób

    .procedure ...
    .zpvar = proc_name.zpmax
    • 18: CommentAuthorilmenit
    • CommentTime15 Jul 2021 zmieniony
     
    O, dzięki Tebe, zerknę jak można to wykorzystać. Można to też wrzucić jakoś do segmentu lub inaczej kontrolować zakres, aby ustalić jakieś maksymalny rozmiar takiej alokacji np. od $80 do $B0?

    A co myślisz o takim zarządzaniu pamięcią, poprzez szukanie optymalnego rozmieszczenia obszarów kodu/danych (nawet bez wspomnianego dzielenia kodu i JMP) i/lub wieloobszarowych segmentach?

    @Laoo - a jak jest z szybkością tego OR-Tools? Jest na tyle szybkie, że można by wykorzystać w procesie kompilacji bez denerwującego spowolnienia?
    • 19: CommentAuthorlaoo
    • CommentTime16 Jul 2021 zmieniony
     
    @ilmenit Mamy teraz takie czasy, w których nasze komputery są zmuszone do robienia takich bzdur, że zapominamy jakie one są cholernie szybkie. OR-Tools jest zaprojektowany do rozwiązywania prawdziwych "inżynieryjnych" problemów na miliony więzów, podczas gdy nie wiem, czy w największym projekcie atari będzie więcej niż kilkaset segmentów, więc zakładam, że czas rozwiązywania tego powiedzmy tysiąca więzów byłby niezauważalny. Problem pojawi się dopiero, gdy w pamięci zrobi się bardzo ciasno - wtedy backtracking da o sobie znać, więc trzeba byłoby się zabezpieczyć, żeby algorytm niepotrzebnie nie przeglądał wszystkich możliwych kombinacji. Ale ogólnie jestem dobrej myśli - powinno się dać to zaprogramować tak, żeby wynik był "od ręki".
    • 20:
       
      CommentAuthorYosh
    • CommentTime17 Jul 2021 zmieniony
     
    Masoni...

    mi tam by wystarczyło działające zagnieżdżone .rept
    mads 2.1.3
    Source: f.asm
    1 opt h-
    2 org 0
    3 .rept 2
    4 .REPT 3
    5 DTA #
    6 .ENDR
    7 .REPT 1000
    8 DTA $10+#
    9 .ENDR
    10 .ENDR
    10 .endr
    Source: REPT
    10 .endr
    Source: REPT
    5 0000 00 DTA #
    5 0001 01 DTA #
    5 0002 02 DTA #
    Source: f.asm
    5 .endr
    Source: REPT
    5 0003 00 DTA #
    5 0004 01 DTA #
    5 0005 02 DTA #
    Source: f.asm
    5 .endr
    Source: REPT
    5 0006 00 DTA #
    5 0007 01 DTA #
    5 0008 02 DTA #
    Source: f.asm
    5 .endr
    Source: REPT
    5 0009 00 DTA #
    5 000A 01 DTA #
    5 000B 02 DTA #
    Source: f.asm
    Source: f.asm
    11
    • 21: CommentAuthorilmenit
    • CommentTime17 Jul 2021 zmieniony
     
    @Yosh - ale tam już byliśmy ->link<- , teraz mame inne problemy, na które natrafisz ;)
    • 22:
       
      CommentAuthorYosh
    • CommentTime18 Jul 2021 zmieniony
     
    Do licznika potrafię się dobrać, chodzi o to że drugie zagnieżdżone .rept "przejmuje" cały kod pierwszego.

    No ale to bug a nie masońskie życzenie, więc podesłałem na aa.

    Co do alokacji to kibicuje laoo, ale boję się że więzy powiedzą "nie da się" i będę szukał w 2342521 propozycjach która była "najbliżej"

    ja sobie radze makrami

    at - czy uwazam ze jestem tam gdzie jestem
    atmost - nie przelecialem


    i plikiem atdbg którym symuluje atrybuty stron z dużych platform

    bx "write>=main_f and write<main_t and write!=m_dliy and write!=m_dlia and write!=m_dlix and write!=m_markerrrr and write!=m_marker_s_x and write!=wiper.m_exit_column and (write<wiper.fill_s or write>=wiper.fill_e)"

    wygenerowana grep-em sekcja "read only" dla kodu (poza miejscami gdzie kod zmienia się dynamicznie)

    albo - na razie definiowana bardziej ręcznie

    bx "pc>=charFiller_f and pc<charFiller_t and (write>=$100 and write<chardata_f)"


    To powoduje, że elementy które ja wkładam do plecaka są bardzo wybredne i nie dają się tak łatwo zrzucić sobie na stopę.

    Naszczacie 'dziurek' na kod zostaje mi na tyle mało, że jeszcze przeżyje :) Ale segmenty 'execute only(z wyraźnym zaznaczeniem ze etykiety m_ są zapisywalne) / read only / write only definiowane w mads + rozszerzenie Altirry które to ładnie zgłasza to bym przytulił