forum speccy.pl
ZX Spectrum => PROGRAMOWANIE => Wątek zaczęty przez: Tygrys w 2019.05.05, 15:44:42
-
Widząc zapotrzebowanie na kurs programowania w ASM Z80 w po polsku, ale też tak jak obiecywałem w innym wątku - wracam do tematu a zatem podejmę się napisania mini-kursu. Pierwsza część ukaże się oczywiście w Zin80. Pomyślałem że tematem pierwszego odcinka będzie obsługa Kempstona.
Aby tekst był bardziej przystępny dla zainteresowanego czytelnika, poszukuję 2-3 osoby, które chcą się nauczyć asm i chcą współtworzyć tekst. Ich zadaniem będzie przeczytanie tekstu oraz zaopiniowanie go - czy jest zrozumiały, czy czegoś brakuje, które części mogę pominąć a które rozwinąć. Ktoś jest chętny?
-
:) Tygrys, super :)
Możesz na mnie liczyć.
-
z chęcią bym się nauczył asemblera, nic a nic nie wiem na ten temat
-
Ja chętny.
-
ja też.
-
Jeśli nie jest wymagana specjalna wiedza, to ja też.
-
I ja :)
-
Ok, dzięki za zaangażowanie! Zatem do osób które się zgłosiły wyślę za jakiś czas program oraz artykuł go opisujący.
-
witam po majówce, dopisz mnie jeszcze do listy
-
@Tygrys a mogę też dostać? Jako grupa kontrolna? ;)
-
pięknie! samopomoc chłopska!
Chętnie przeczytam kurs
-
#metoo! :)
-
mat - na pewno przyda się wsparcie grupy kontrolnej ;-) Dzięki!
-
Może i ja się czegoś nauczę ;)
-
Oby nie wyszło pół kartki część I - rejestry procesora. Drugie pół kartki - współautorzy. ;D ;D ;D
-
Jeśli komuś angielski nie straszny to tu jest jakiś kurs asemblera w artykułach i filmikach.
http://www.chibiakumas.com/z80
https://www.youtube.com/watch?v=LpQCEwk2U9w&list=PLp_QNRIYljFq-9nFiAUiAkRzAXfcZTBR_
-
troche pozno ale rowniez jestem chetny do zadawania pytan itp.
-
Nie tylko angielski:
https://wiki.speccy.org/programacion/indice
a zwlaszcza:
https://wiki.speccy.org/cursos/ensamblador/indice
Na szczescie kod Z80 w kazdym jezyku brzmi tak samo :D
-
Witam. Ja też poproszę o materiały z kursu. Z góry dziękuję i pozdrawiam.
-
Program demonstacyjny jest już napisany, niestety zabrakło czasu na opis słowny i wytlumaczenie w jaki sposób to działa.
Jak tylko będę mieć w miarę napisany opis, podeślę kilku osobom do przejrzenia i podzielenia się uwagami.
Gotowiec ukaże się w najnowszym numerze Zin80.
-
Ciesze się że temat asemblera powrócił.
Mam taki problem, potrzebna jest procedura
czekania na na wcisniecie dowolnego klawisza ,
takie PAUSE 0
Przy okazji podzielę się ściągą którą kiedyś na swoje potrzeby wykonałem
mozna by do nie jeszcze wiele rzeczy dopisać.
Wpisałem tylko to co sam przetestowałem i sprawdziłem.
Musialem zmienic rozszerzenie na txt, bo rtf nie mozna przeslac, prosze zmienic u siebie na rtf.
Piszę w pasmo, środowisko można tak skonfigurować, że generacja kodu i sprawdzenie działa bardzo szybko.
Wystarczy zrobić sobie skrót do cmd i skojarzyc tap z emulatorem.
-
Ja to robię tak:
GETKEY LD HL, 23611
RES 5, (HL)
GK1 PUSH HL
CALL 703
POP HL
BIT 5, (HL)
JR Z, GK1
...
-
Dzięki działa
tali prosty programik prezentujący dostepne kolory
org 50000
call kas_ekr
ld hl,22528
ld a,0
ld b,0
loop1 ld (hl),b
inc b
inc b
inc b
inc b
inc hl ;zwieksz
inc a ;zwieksz
cp 32 ;porownuje akumulator z liczba 32
jp nz,loop1 ;jesli flaga zero skasowana wykonaj skok do loop1
call GETKEY
;=================================
kas_ekr ;kasowanie ekranu
ld hl,16383
ld bc,2048
sko1 dec bc
inc hl
ld (hl),0
ld a,b
or c
jp nz,sko1
ret
;=================================
GETKEY ; czekanie na dowony klawisz
LD HL, 23611
RES 5, (HL)
GK1 PUSH HL
CALL 703
POP HL
BIT 5, (HL)
JR Z, GK1
ret
end 50000
kasowanie ekranu może jakoś prościej by się dało ?
-
Ja to robię tak:
GETKEY LD HL, 23611
RES 5, (HL)
GK1 PUSH HL
CALL 703
POP HL
BIT 5, (HL)
JR Z, GK1
...
No fajnie, ale komentarze, tak jak Dalthon napisal w sasiednim watku, bylyby pomocne poczatkujacym takim jak ja. Zeskanowany opis malo mi mowi jak to zapisac w asm. Prosze, traktowac mnie jak idiote, ktoremu trzeba kawa na lawe.
-
kasowanie ekranu może jakoś prościej by się dało ?
kas_ekr ;kasowanie ekranu
ld hl,16384
ld de,16384+1
ld bc,2047
ld (hl),l
ldir
ret
-
Też działa
tylko pytanie co robi ld (hl),l ?
Drugi temat to synchronizacja z ramką, jak się za to zabrać ?
-
L=0.
Co do synchronizacji z ramką, to trzeba napisać swoją obsługę INT.
-
wyglada mi na to ze ta procedura kasuje 1/3 ekranu bez atrybutow.
co do INT są jakies przyklady ?
chyba wszystkie gry mają taką synchronizacje
-
wyglada mi na to ze ta procedura kasuje 1/3 ekranu bez atrybutow.
Wystarczy zwiększyć ilość "kasowanych" bajtów by mieć cały ekran: grafika - 6144, atrybuty 768
ld hl,16384 ; adres początku ekranu
ld de,16384+1 ; adres początku ekranu + 1
ld bc,6911 ; ilość kopiowanych bajtów
ld (hl),l ; wstaw zero do zawartości pamięci o adresie w HL (czemu zero? Bo 16384 to 64 w H i 0 w L)
ldir ; skopiuj zawartość pamięci z adresu HL do pamięci o adresie DE, zwiększ o 1 HL oraz DE, zmniejsz o 1 BC <- wykonuj aż BC=0
ret
co do INT są jakies przyklady ?
Są ale to temat dla zaawansowanych - w sumie to proste ale dla początkujących będzie wyglądało jak bełkot ew. czary-mary ;)
chyba wszystkie gry mają taką synchronizacje
Synchronizację co ramkę można też uzyskać przez:
halt ; czekaj na kolejne przerwanie
Pamiętać jednak trzeba by włączyć przerwanie - inaczej będzie czekać w nieskończoność :D
ei ; włącz przerwania
-
Co do synchronizacji z ramką, to trzeba napisać swoją obsługę INT.
Często wystarczy przed efektem dać HALT - poczeka do przerwania czyli początku wyświetlania ramki.
Da się to nawet zrobić w BASIC https://www.speccy.pl/forum/index.php?topic=1006.msg11966#msg11966 (https://www.speccy.pl/forum/index.php?topic=1006.msg11966#msg11966)
PAUSE n wykonuje właśnie n razy HALT :)
-
Synchronizację co ramkę można też uzyskać przez:
halt ; czekaj na kolejne przerwanie
Taaa...
Wciskasz jakiś klawisz i już po synchronizacji.
Do tego dziwne zakłócenia tej synchronizacji rytmicznie co kilka sekund.
Trzeba pamiętać, że przerwanie INT to standardowo skok do procedury w ROM, a czas jej wykonania zależy np. od wciśnięcia klawiszy, a co 256 cykli (ok. 4,3 s) nieco więcej taktów zawiera aktualizacja 24-bitowego licznika ramki.
Kod umieszczony po HALT będzie wznowiony dopiero po zakończeniu procedury przerwania w ROM-ie.
-
To znaczy synchronizacja przez HALT jest ale nie najlepsza.
To może uda się wytłumaczyć, jakoś prostymi słowami jak to zrobić.
W końcu nie trzeba wszystko rozumieć do końca aby używać.
Teraz to mamy taką epokę że mamy raczej małe pojęcie jak co działa, a używamy.
Od telefony do samochodu.
Skończyła się epoka ludzi omnibusów, którzy byli w stanie wiedzieć wszystko o wszystkim.
-
Trzeba pamiętać, że przerwanie INT to standardowo skok do procedury w ROM, a czas jej wykonania zależy np. od wciśnięcia klawiszy, a co 256 cykli (ok. 4,3 s) nieco więcej taktów zawiera aktualizacja 24-bitowego licznika ramki.
Kod umieszczony po HALT będzie wznowiony dopiero po zakończeniu procedury przerwania w ROM-ie.
Nie co 256, tylko co 65536 przerwań - czyli 21 i coś MINUT :P
A naciskanie klawisza czasem nie ma znaczenia, jeśli np. po naciśnięciu program i tak przechodzi dalej.
Mój programik na Wyzwanie #4 w drugiej części synchronizuje się przez HALT w trybie IM1 i jest malinowo :)
https://www.speccy.pl/forum/index.php?topic=3339.msg51420#msg51420 (https://www.speccy.pl/forum/index.php?topic=3339.msg51420#msg51420)
EDIT: Właśnie sprawdziłem co się dzieje przy naciskaniu klawisza jak nie powoduje to wyjścia z animacji (usunąłem sprawdzanie klawisza). Otóż nic się nie dzieje - dalej chodzi płynnie 8)
-
Moim zdaniem najlepiej zrobić własną obsługę przerwań INT.
Po pierwsze, potrzebna jest tablica wektorów dla trybu drugiego (IM 2). Potrzebujemy 2x256=512 bajtów.
O ile mnie pamięć nie myli, działa to tak, że starszy bajt tej tablicy wektorów ładujemy do rejestru I, a młodszy bajt jest odczytywany z szyny danych w momencie przyjęcia przerwania. Jako że Spectrum nie ma żadnych urządzeń wystawiających wektory w ten sposób, młodszy bajt może być z zakresu 0..255, stąd potrzeba takiej tablicy.
Adres pobrany z tablicy (dwa bajty) jest adresem skoku do procedury obsługi przerwań.
Uwaga, żeby procedura była synchroniczna z ramką, nasz kod obsługi przerwania powinien być w "górnej" pamięci (uncontended).
Załóżmy, że nasza procedura zaczyna się od adresu 32768, czyli #8000. Budujemy naszą tablicę wektorów przerwań, dajmy na to, od adresu 49152 (#C000). Poczynając od tego adresu, wypełniamy 512 bajtów wartościami #00 i #80 (naprzemiennie), czyli mamy 256 razy słowo 16-bitowe #8000.
Kod inicjujący obsługę przerwań powinien blokować przerwania (DI), ładować do rejestru I starszy bajt tablicy (#C0), włączać tryb IM 2 i odblokowywać przerwania (EI).
Nasz kod obsługi przerwań pod adresem #8000 powinien (podobno) blokować przerwania rozkazem DI (choć podobno przyjęcie przerwania robi to automatycznie). Dalej mamy kod synchroniczny z ramką, bez skoków/pętli warunkowych. Na koniec skaczemy do ROM pod adres 56 (#38), by odczytać klawiaturę, zaktualizować licznik ramek itp.
Jak ktoś lubi cwaniaczyć, to jako tablicę wektorów może wykorzystać obszar ROM wypełniony wartościami #FF (255) od adresu #386E do #3CFF. Potrzebujemy 512 bajtów, a w podanym zakresie jest ponad kilobajt. Początek tablicy będzie oczywiście pod adresem #nn00, czyli #3900, #3A00, #3B00 lub #3C00. Obsługa przerwania będzie wtedy pod adresem #FFFF, gdzie należy umieścić wartość 24 (#18), czyli kod operacji JR. Następny odczytany bajt, czy adres skoku względnego, będzie pobrany spod adresu #0000 (pamięć ROM), czyli będzie to #F3 (kod instrukcji DI). Instrukcja JR #F3 oznacza skok o -13, czyli od bieżącej wartości licznika PC (#0001) odejmujemy 13 i lądujemy pod adresem #FFF4 (65524). Pod tym adresem robimy skok do naszego kodu obsługi przerwań, np. JP #8000.
EDIT: To co pokreślone, jest nie do końca prawdą zgodnie z tym, co napisał potem Phonex :)
-
Nie co 256, tylko co 65536 przerwań - czyli 21 i coś MINUT :P
Zajrzałem do ROM-u, i faktycznie - inkrementacja jest na słowie 16-bitowym.
A naciskanie klawisza czasem nie ma znaczenia, jeśli np. po naciśnięciu program i tak przechodzi dalej.
Mój programik na Wyzwanie #4 w drugiej części synchronizuje się przez HALT w trybie IM1 i jest malinowo :)
"Obsuw" o 4 takty (czyli i tak minimalistycznie) oznacza przeskok o 8 pikseli.
Może akurat Twój efekt tak ma, że nie widać efektu.
Do prostych rzeczy faktycznie styknie, nie trzeba się babrać w IM 2.
-
Moim zdaniem najlepiej zrobić własną obsługę przerwań INT.
No pewnie że najlepiej, mówię tylko że nie zawsze trzeba.
A co do trybu IM2:
Skoro młodszy bajt może być z zakresu 0..255, to tablica ma mieć 256+1 bajtów. Dlaczego 257? Bo dla każdej wartości młodszego bajta adresu pobierane są dwa bajty jako adres procedury obsługi!
A ponieważ może przybrać każdą z tych wartości, to nie można jej wypełniać dwoma wartościami, tylko jedną. W efekcie oba bajty adresu muszą być takie same! Np. wstawiamy #80. Zostanie w każdej sytuacji odczytany adres #8080, a pod adresem #8080 wstawiamy skok gdzie nam się podoba, np. JP #8100.
-
A co do trybu IM2:
Skoro młodszy bajt może być z zakresu 0..255, to tablica ma mieć 256+1 bajtów. Dlaczego 257? Bo dla każdej wartości młodszego bajta adresu pobierane są dwa bajty jako adres procedury obsługi!
No, sporo jednak zapomniałem przez te >20 lat niedotykania asemblera :D
Coś mi chodziło po głowie, że ta tablica wektorów jest lekko nierówna, ale nic na ten temat nie znalazłem.
Niemniej rozwiązanie z tablicą #FF jest OK :D
-
Jest OK, sam tak kiedyś zrobiłem, ale ktoś pisał że nie zawsze...
W Spectrum 128 nie ma tam samych #FF-ów. A program działa mimo tego poprawnie (np. mój Start s12).
OK, zajrzałem do ROMU ZX 128, okazuje się że cwaniaki zostawili dwa #FF-y pod adresami #39FF, #3AFF i #3BFF!!!
(#3CFF odpada, bo nawet w ZX 48K jest tam tylko jeden #FF, dalej są już fonty).
Będzie działać jak odczyta z szyny #FF, czyli bez podłączonych interfejsów. Z interfejsem może się wywalić.
Kolejna z 11 tajemnic wyjaśniona :D
-
Szanse są niewielkie, bo żeby się wywaliło, trzeba by mieć interfejs z jakimś układem z rodziny Z80 (CTC, PIO, DMA, SIO, KIO, CIO). Rodzina intelowska (8xxx) raczej nie potrafi wystawiać po przerwaniu wektora na szynę danych. Choć trzeba by to sprawdzić :D
-
Szanse są niewielkie, bo żeby się wywaliło, trzeba by mieć interfejs z jakimś układem z rodziny Z80 (CTC, PIO, DMA, SIO, KIO, CIO). Rodzina intelowska (8xxx) raczej nie potrafi wystawiać po przerwaniu wektora na szynę danych. Choć trzeba by to sprawdzić :D
A floating bus nie może czasem wejść tu w paradę?
Muszę się chyba napić, bo na trzeźwo głupoty gadam...
-
A to co "akurat już jest" na szynie danych w momencie przerwania nie ma znaczenia?
Np. odczyt joysticka?
Czy ja też głupoty gadam? Chyba tak, bo naciskanie klawiszy nie wywala, a to też odczyt z portu...
-
@steev, zadałeś bardzo dobre pytanie, co zatem spowodowało, że je wykreśliłeś?
@Phonex, chyba znam odpowiedź, to się powymądrzam ;)
Otóż cykl potwierdzenia przyjęcia przerwania INT powoduje wystawienie przez CPU aktywnych linii /IORQ oraz /M1 (które w żadnej innej sytuacji nie są aktywne razem). W odpowiedzi na ten stan urządzenie zewnętrzne wystawia swój wektor.
I otóż: ani joystick, ani floating bus, ani żaden inny port I/O żadnego interfejsu nie powinien wystawiać niczego innego, ponieważ linia /RD jest w stanie wysokim.
-
@steev, zadałeś bardzo dobre pytanie, co zatem spowodowało, że je wykreśliłeś?
Bo IIRC INT jest generowany wewnątrz impulsu synchronizacji pionowej.
ULA wtedy nie generuje obrazu.
-
Ale INT może też przyjść z zewnątrz komputera...
-
Tak.
Ale nie kojarzę żadnego takiego interfejsu, poza slo-mo.
(choć nie znam na pamięć schematów wszystkich stacji dysków, może któraś z nich też... :) )
-
Zaglądnąłem do przerwań w Shock cz.2
Jest oczywiście IM2, I=#FE czyli tablica jest od #FE00, i jest wypełniona #FF. A pod #FFFF jest... RET
Czyli synchronizuje się przez HALT, po wyeliminowaniu wszystkich minusów tego rozwiązania!
-
I otóż: ani joystick, ani floating bus, ani żaden inny port I/O żadnego interfejsu nie powinien wystawiać niczego innego, ponieważ linia /RD jest w stanie wysokim.
Otóż prawdą jest, że nie powinien. Ale jak coś jest słabo zaprojektowane to różnie z tym może być i głównie dlatego od zawsze tablica ma 257 bajtów ;)
Co ciekawe - kiedyś dawno temu czytałem artykuł na temat tego, że z adresem wektora coś jest tak, że ma mieć najmłodszy bit wyzerowany. Artykuł sprawdzał czy "ma mieć" oznacza, że nawet jak nie ma to pobiera z parzystego adresu. Nie pamiętam niestety konkluzji ;)
Czyli synchronizuje się przez HALT, po wyeliminowaniu wszystkich minusów tego rozwiązania!
HALT i EI - bez EI po kolejnym HALT nie przyjmie przerwania, bo IRQ wyłącza przerwania aż do wywołania RETI albo EI. Obsługa synchronizacji w taki sposób daje bardzo ładny i równy punkt w którym kod po HALT może zacząć rysować - jakoś tak 32+10+4+4 takty od wygenerowania przerwania (32 na procedurę przyjęcia przerwania, 10 - RET, 4 - HALT i 4 EI) i możemy zacząć szaleć po borderze ;)
-
bo IRQ wyłącza przerwania aż do wywołania RETI albo EI.
Coś mi się wydaje, że RETI nie włącza przerwań i trzeba dać EI tak czy siak.
-
To miał być kurs dla początkujących a nie bredzenie starszych panów ;) Miejcie litość nad tymi którzy to czytają. Nie bez przyczyny napisałem że przerwania brzmią jak bełkot :D
Informacja dla chcących się czegoś nauczyć: Zapomnijcie o tym co przez blisko dwie strony było wypisywane i zapamiętajcie: EI by włączyć przerwania a potem HALT przed wyświetlaniem efektu dla synchronizacji ;)
-
Co ciekawe - kiedyś dawno temu czytałem artykuł na temat tego, że z adresem wektora coś jest tak, że ma mieć najmłodszy bit wyzerowany. Artykuł sprawdzał czy "ma mieć" oznacza, że nawet jak nie ma to pobiera z parzystego adresu. Nie pamiętam niestety konkluzji ;)
Na emulatorze na pewno odczytuje #FF. W innym przypadku Start s12 nie działałby na Spectrum 128, bo tam tablica jest skrócona do dwóch bajtów, a I=#3B
bo IRQ wyłącza przerwania aż do wywołania RETI albo EI.
Coś mi się wydaje, że RETI nie włącza przerwań i trzeba dać EI tak czy siak.
Sprawdzam... Tak, RETI nie włącza przerwań. EI musi być.
-
To miał być kurs dla początkujących a nie bredzenie starszych panów ;) Miejcie litość nad tymi którzy to czytają.
Za tą wypowiedź, masz u mnie piwo :D
-
To miał być kurs dla początkujących a nie bredzenie starszych panów ;) Miejcie litość nad tymi którzy to czytają.
Za tą wypowiedź, masz u mnie piwo :D
Strach pomyśleć co by się działo przy dyskusji o contended memory...
-
bo IRQ wyłącza przerwania aż do wywołania RETI albo EI.
Coś mi się wydaje, że RETI nie włącza przerwań i trzeba dać EI tak czy siak.
Faktycznie. Pamiętałem, że RETI czymś się różni od RETa - wyszło mi, że włączaniem przerwań. A różni się tym, że wystawia na szynę inny zestaw sygnałów sygnalizując, że jest powrotem z obsłui IRQ. To RETN włącza przerwania a dokładnie przywraca taki ich stan jaki był w momencie przyjęcia NMI.
Miejcie litość nad tymi którzy to czytają.
W asmie nie ma miejsca na litość ;)
Nie bez przyczyny napisałem że przerwania brzmią jak bełkot :D
Eeee tam. Obsługa przerwań jest bardzo prosta i całkowicie logiczna :P
Spectrum na szczęście nie pozwala wykorzystać tego, co tak na prawdę siedzi w Z80 i ewentualnie podłączonych do niego urządzeń - dopiero jakbyś zaczął tłumaczyć tryb IM 0 (zwłaszcza z wielobajtowymi rozkazami), chainowanie przerwań itp. zabawy to by się zrobiła jazda zmierzająca do bełkotu ;)
A tak masz dwa proste tryby z prostym obejściem ewentualnych problemów z urządzeniami zewnętrznymi i wszystko gra i buczy ;)
Edit : A tak na serio - zrobienie kursu assemblera dla początkujących jest możliwe tylko wtedy jeśli będzie to tekst pisany jako większa całość przez jedną osobę. Jak mamy dyskusję w której udział bierze kilku początkujących i stado "wiedzących" to efekty prędzej czy później stają się takie jak w tym wątku - zaczynamy się popisywać ;) A początkujący czują się zagubieni. Dlatego mam nadzieję, że Tygrysowi wyjdzie coś bardziej spójnego :) A i sam od dłuższego czasu noszę się z koncepcją, ale na razie czekam, żeby zobaczyć czy ma ona sens czy może to, co zaproponuje Tygrys będzie bardziej interesujące.
-
Podsumowujac, halt i ei i to na razie powiino wystarczyc.
A koledzy w tym czasie uzgodnia miedzy soba, jak to ma byc.
Chociaz wydaje sie maloprawdopodobne zeby poczatkujacy do tego poziomu doszli,
to jakis przepis jednoznaczny powinien byc.
-
Podsumowujac, halt i ei i to na razie powiino wystarczyc.
A dokładnie EI i HALT. W odwrotnej kolejności ryzykujesz, że przed HALT przerwania będą wyłączone i HALT się po prostu zawiesi.
-
A tak z ciekawosci, ile osob z poczatkujacych, ktore deklaruja tutaj chec nauki
przerobilo kurs asemblera w Tutorze ?
Program jest dostepny w archiwum i wiele rzeczy na poczatek może wyjasnic.
-
Przydało by się tu coś tytułem wstępu teoretycznego związanego z tematem zanim zacznie się mieszać w przerwaniach czy innych ciekawych mechanizmach związanych ze specyficznym hardware czy firmware jeśli ma to być bardziej edukacyjne ;)
Wiem że wszystko można doczytać... ale przynajmniej może jakiś celny link by nie szukać materiału przygotowawczego.
Też bym chętnie poznał co tam w ZXie piszczy ciekawszego na poziomie assemblera, choć tam już coś kiedyś robiłem w kodzie maszyny na różne procki i platformy, jednak tutaj bardziej spodziewałem się czegoś bardziej w kontekście ZX Speca, może jakieś sprytne procedury, fragmenty kodu z opisem co to robi i dlaczego tak jest optymalnie ...
Puki co to brzmiało mi jakoś na bełkot ludzi na pewnym poziomie znajomości tematu i niekoniecznie wnoszącego coś do edukacji mniej zaawansowanych w specyfice Speca ;)
Bez urazy .... bo to bardzo ciekawy temat .... :)
-
Dalthon zaczął bardzo fajnie, potem przyszli wyjadacze i podpuścili wodze fantazji. Jeśli tem „kurs” ma wyglądać tak, jak przez ostanie kilkanaście wypowiedzi, to ja pasuje. Jestem laikiem, idiotą pod względem programowania i raczej tak zostanie. :-X
-
... niekoniecznie wnoszącego coś do edukacji mniej zaawansowanych w specyfice Speca ;)
Dalthon zaczął bardzo fajnie, potem przyszli wyjadacze i podpuścili wodze fantazji. Jeśli tem „kurs” ma wyglądać tak, jak przez ostanie kilkanaście wypowiedzi, to ja pasuje. Jestem laikiem, idiotą pod względem programowania i raczej tak zostanie. :-X
To nie tak. Padło pytanie "Drugi temat to synchronizacja z ramką, jak się za to zabrać ?"
I kilka odpowiedzi.
To nie jest element tego tytułowego kursu, tylko na dobrą sprawę osobny temat, tak częsty na forum off-topic.
-
Jeszcze o HALT.
Wyobrażam to sobie tak że zaraz po HALT, nastąpią wywołania kolejnych podprogramów
realizujących jakieś zadania.
Na koncu będzie JP z powrotem do początkowego HALT, tak aby uzyskać wykonywanie podprogramów w pętli.
Ramka występuje 50 razy na sek, czyli wszystkie podprogramy w pętli muszą się wykonać w czasie krótszym niż 20ms.
MAIN
HALT
CALL skok1
CALL skok2
CALL skok n
JP MAIN
I teraz jak sprawdzić czy obsługa podprogramów wyrobiła się w tym czasie.
Czy jest jakiś program który może to zmierzyć i ocenić jaki czas zajmuje każdy podprogram ?
Ile taktów czy też może cykli, wykonuje procesor w jednej ramce ?
W książce Z80 Karczmarczuka dla każdego rozkazu procesora, podana jest ilość cykli maszynowych i liczba taktów.
Nie wyobrażam sobie ręcznego liczenia taktów, żeby sprawdzić czy mieszcze się w ramce czy nie.
-
Dobra... początkujących uprasza się o pominięcie 3 i 4 strony tego wątku :)
Sprowadzają się one do tego co było na 2 stronie: synchronizację 'co ramkę' uzyskuje się przez rozkaz HALT pamiętając by na początku programu był rozkaz EI włączający przerwanie. W przeciwnym wypadku z80 będzie czekał na w nieskończoność :)
Wracając do meritum...
Stawianie punktu na ekranie. Najprostszym (a tego oczekują początkujący <- informacja do bardziej zaawansowanych panów :D) sposobem będzie wykorzystanie procedury 'PLOT' (a właściwie 'PLOT_SUB' bo podamy sami współrzędne). Ogranicza się to do zapisania w rejestrze C współrzędnej 'x' a w B odpowiednio 'y':
ld c,100 ; zapisz współrzędną X do rejestru C
ld b,0 ; zapisz współrzędną Y do rejestru B
call 8933 ; skok do procedury 'PLOT_SUB' z ROM
ret ; koniec :)
Należy pamiętać że procedura będzie rysować w kolorach jakie są ustawione globalnie. Nie zapominajmy że to odpowiednik PLOT w Basicu i będzie wiązało się z kilkoma ograniczeniami:
- współrzędne X od 0 do 255, od lewej do prawej
- współrzędne Y od 0 do 175, od dołu do góry
- punkt 0,0 znajduje się w lewym dolnym rogu ale nie na samym dole ale 16 pixeli powyżej
- dolny pasek o wymiarach 256x16 pixeli jest dla tej procedury niedostępny
Na koniec mała procedura na rysowanie kilku punktów - przy okazji będzie można zobaczyć jak takiego typu rzeczy się pisze :)
ld hl,dane ; załaduj do pary rejestrów HL adres o etykiecie 'dane'
kolejny ld b,(hl) ; do rejestru B załaduj zawartość adresu HL czyli współrzędną Y
inc hl ; zwiększ adres HL o 1
ld c,(hl) ; do rejestru C załaduj zawartość adresu HL czyli współrzędną X
inc hl ; zwiększ adres HL o 1
push hl ; zapamiętaj na stosie adres HL
call 8933 ; skok do procedury 'PLOT_SUB' z ROM
pop hl ; ściągnij ze stosu zapamiętany adres HL
ld a,(hl) ; do rejestru A załaduj zawartość adresu HL
cp 255 ; porównaj zawartość A z liczbą 255
jr nz,kolejny ; skocz do etykiety kolejny jeśli porównanie dało negatywny wynik
ret ; koniec :)
dane db 0,100 ; współrzędne punktu: Y,X
db 45,250
db 90,170
db 170,45
db -1 ; to samo co '255' ale wizualnie lepiej widać że to znacznik końca ;)
Od razu rzuca się w oczy że zapisuję współrzędne w kolejności Y,X zamiast standardowego X,Y - dlaczego? By można było wykorzystać fakt że współrzędna Y zawierać może liczy w zakresie 0-175, więc można użyć np 255 jako znacznik końca pobierania z tabeli danych.
Przed skokiem wrzuciłem na stos rejestry HL bo procedura w ROM na 1000% ją zmieni a jest mi ona potrzebna by pamiętać w którym miejscu zakończyłem pobierać dane z tabeli.
Tyle by było jeśli chodzi o rysowanie punktów - następnie opiszę jak wyświetlać napisy (również korzystając z procedur zawartych w ROM).
-
Czy jest jakiś program który może to zmierzyć i ocenić jaki czas zajmuje każdy podprogram ?
Możemy od ręki taki stworzyć ;) Nie poda on nam co prawda z aptekarską dokładnością ile zajmuje dany podprogram ale wizualnie pokaże ile czasu 'ramki' wykorzystuje:
ld a,0 ; do rejestru A załaduj 0 (kolor czarny)
out (254),a ; wersja uproszczona: zmień kolor borderu na kolor z rejestru A
main
halt ; czekaj na przerwanie
ld a,2 ; do rejestru A załaduj 2 (kolor czerwony)
out (254),a ; wersja uproszczona: zmień kolor borderu na kolor z rejestru A
call skok1 ; skok do procedury
ld a,0 ; do rejestru A załaduj 0 (kolor czarny)
out (254),a ; wersja uproszczona: zmień kolor borderu na kolor z rejestru A
jp main ; skok do etykiety main
Zasada jest banalna:
1) zmieniamy kolor ramki na czarny
2) czekamy na przerwanie
3) zmieniamy kolor ramki na czerwony
4) wykonujemy podprogram
5) zmieniamy kolor na czarny ponownie
6) skaczemy do punktu 2)
Efektem jest czerwony pasek na czarnej ramce -> jego wysokość zależy od jego 'czasochłonności'.
-
I teraz jak sprawdzić czy obsługa podprogramów wyrobiła się w tym czasie.
Napisałem już ten sam kod, co Dalthon, ale on szybciej wysłał ;) Dodam może nieco dokładniej jak interpretować to, co się widzi. Jeżeli ramka ładnie podzieli się na 2 części (miejsce podziału może "pływać", jeżeli czasy wykonania pętli w każdej ramce nie są takie same), to mieścisz się w ramce i dodatkowo widzisz, jaką część ramki zajmuje wykonanie jednego przebiegu pętli. Natomiast jeżeli się nie mieścisz w ramce, to kolor ramki zaczyna brzydko migać, bo górna część ma na zmianę kolor czerwony i czarny.
-
ld hl,dane ; załaduj do pary rejestrów HL adres o etykiecie 'dane'
Czy to jest tak, że wszystkie dane są naraz ładowane? Czyli wszystkie cztery punkty(ich współrzędna) lądują w pamięci ?
-
Ile taktów czy też może cykli, wykonuje procesor w jednej ramce ?
Zależy który model Spectruma, 48K => 69888Ts, inne wartości tu https://www.speccy.pl/forum/index.php?topic=4487.0
(https://www.speccy.pl/forum/index.php?topic=4487.0) Podane są też czasy dla jednej linii.
W debuggerze ZX Spina też te liczby są dostępne (w Hardware Info).
W książce Z80 Karczmarczuka dla każdego rozkazu procesora, podana jest ilość cykli maszynowych i liczba taktów.
Nie wyobrażam sobie ręcznego liczenia taktów, żeby sprawdzić czy mieszcze się w ramce czy nie.
W debuggerze emulatora ZX Spin można wyeksportować tekst programu z pamięci do schowka, wraz z ilością taktów dla każdego rozkazu, także nie trzeba dopasowywać z tabeli. Ale zsumować to już trzeba "na piechotę".
Parę razy liczyłem takty, jak już mam opisaną czasowo procedurę w notatniku - nie trwa to długo.
-
ld hl,dane ; załaduj do pary rejestrów HL adres o etykiecie 'dane'
Czy to jest tak, że wszystkie dane są naraz ładowane? Czyli wszystkie cztery punkty(ich współrzędna) lądują w pamięci ?
Nie... Samo się nic nie dzieje ;) W asemblerze wszystko trzeba robić samemu :D
Do HL jest ładowany adres w pamięci 'dane'. Dopiero później pobierane są z tego miejsca (pamięci) poszczególne współrzędne instrukcjami:
ld b,(hl) ; do rejestru B załaduj zawartość adresu HL czyli współrzędną Y
inc hl ; zwiększ adres HL o 1
ld c,(hl) ; do rejestru C załaduj zawartość adresu HL czyli współrzędną X
inc hl ; zwiększ adres HL o 1
Nie sposób załadować do HL współrzędne 4 punktów (8 bajtów). HL to para rejestrów H i L - każdy z nich ma rozmiar 1 bajta. Do HL jak napisałem powyżej ładowany jest adres gdzie znajdują się dane współrzędnych i dopiero z tego miejsca są pobierane do rejestrów B (wsp. Y) i c (wsp. X).
Może łatwiej by Ci było to sobie wyobrazić jakbyś przyjął że w pamięci powiedzmy pod adresem 49152 masz zapisane współrzędne tych 4 punktów a załadowanie LD HL,dane by wyglądało LD HL,49152
-
Sprowadziłem program do minimum,
ale nie udało mi się uzyskać efektu podzielonego borderu
org 50000
ei
main
halt
ld a,2
out (254),a
call time
ld a,0
out (254),a
jp main
;==================================
time ld bc,1
petla dec bc
cp 2995
jp nz,petla
ret
end 50000
-
cp 2995?
Instrukcja CP ma ośmiobitowy argument. Porównuje ona zawartość akumulatora z tym argumentem.
Użycie BC też jest dość dziwne, najpierw wpisujesz wartość 1, a potem w pętli pomniejszasz o 1.
Jeśli to ma być pętla opóźniająco o liczbę cykli podanych w BC, to ja rozwiązałbym to tak:
LD BC, 2995
DELAYLOOP LD A, B
LD B, C
DELAYLOOP1 DJNZ DELAYLOOP1
LD B, A
DJNZ DELAYLOOP
RET
-
jeżeli zamiast tego
time ld bc,1
petla dec bc
cp 2995
jp nz,petla
ret
dasz to
time ld bc,2995
petla dec bc
ld a,b
or c
jp nz,petla
ret
to powinno zadziałać :)
Edit: O, tym razem trojacek był szybszy :)
-
O, tym razem trojacek był szybszy :)
Może i ja byłem szybszy, ale Twoje rozwiązanie jest znacznie lepsze :)
W moim są ukryte dwa haczyki, o których warto pamiętać, dobierając wartość w BC.
Po prostu ustalmy, że Twoje rozwiązanie jest wzorcowe :)
-
Dziekuje za podpowiedzi. Liczba 2995 to byla jedna z wielu prob od 5 do wlasnie tej.
Teraz jak patrze na ten kod to fez mi sie niepodoba.
Przeanalizuje to co napisaliscie i wyprobuje.
Ale to chyba dopiero jutro.
-
Nie wyobrażam sobie ręcznego liczenia taktów, żeby sprawdzić czy mieszcze się w ramce czy nie.
Więc wyobraź sobie kompilator ZASM. (https://k1.spdns.de/Develop/Projects/zasm/Documentation/z7.htm#A) :)
Wszystkiego sam nie zrobi, ale mocno ułatwi.
-
Przetrawiłem wszystko i udało się uruchomić.
Jest podział borderu w zależności od czasu trwania procedury TIME.
Przy zbyt małym opóźnieniu czerwony pas się nie pojawia w ogóle,
dlatego czterokrotnie powtórzyłem wywołanie.
Przy zbyt dużym, następuje desynchronizacja i border miga.
org 50000
ei
main
halt
ld a,2
out (254),a
call time
call time
call time
call time
ld a,0
out (254),a
jp main
;==================================
time ld c,0
petla inc c
ld a,c
cp 255
jp nz,petla
ret
end 50000
Dopracowałem swoją wersję opóźniacza,
pomyliłem DEC z INC, powinno byc oczywiscie INC (inkrementuj)
a porównanie liczb CP dotyczy liczb jednobajtowych max 255,
a porównanie wykonywane jest z zawartością akumulatora.
-
A dlaczego odszedłeś od liczenia pętli rejestrami BC? Dostałeś pięknego gotowca:
time ld bc,2995
petla dec bc
ld a,b
or c
jp nz,petla
ret
Jeśli nie potrzebujesz tak pojemnego licznika (16 bitów), a wystarcza Ci ośmiobitowy, to nie musisz używać rejestru C - wystarczy sam akumulator (wynik - 255 wykonań):
time xor a ; ld a,0
petla inc a
cp 255
jr nz,petla
ret
Możesz też skrócić nieco kod, porównując nie z 255, a z zerem (pętla wykona się o jeden raz więcej, czyli 256 wykonań):
time xor a ; ld a,0
petla inc a
or a ; cp 0
jr nz,petla
ret
Możesz też użyć specjalnej predefiniowanej pętli z użyciem rejestru B (uwaga - zero oznacza 256 wykonań, 255 - to 255, itp.).
time ld b, 0
petla djnz petla ; dec b + jr nz, petla
ret
Jednak ten kod wykona się szybciej od poprzednich, bo jest mniej rozkazów objętych pętlą! Można ją spowolnić, dodając np. rozkazy typu "nic nie rób" - NOP w środku pętli (dotyczy to każdego rodzaju pętli).
time ld b, 0
petla nop
nop
nop
...
djnz petla ; dec b + jr nz, petla
ret
Jeszcze uwaga: JP od JR różni się zasięgiem skoku. Argument JP jest 16-bitowy (więc kod zajmuje więcej pamięci), ale można skoczyć w dolone miejsce pamięci. JR ma argument 8-bitowy, zakodowany w formacie uzupełnień do 2 (U2), czyli w zakresie od -128 do +127. Asembler sam zadba o wyliczenie, jeśli używasz etykiet (a używasz). Jak skok jest za daleki - wyrzuci błąd.
Kolejna uwaga: XOR a - zeruje akumulator, bo funkcja XOR zeruje te bity, które są jednakowe, a "a XOR a"... chyba logiczne? :)
I kolejna: OR a - ustawia flagi, ale nie zmienia zawartości akumulatora (a OR a). Chdzi w głównej mierze o stwierdzenie, czy zawartość jest równa zero, czyli to samo, co "CP 0".
-
Użyłem dlatego, że chciałem się czegoś na błędach swoich nauczyć.
A gotowiec oczywiście też działa, dla pętli większych niż 255 jest wręcz konieczny (do 65535).
Faktycznie angażowanie do tego rejestru C jest zbędne.
Nie byłem pewien czy można zrobić inc A, a nie zajrzałem do książki.
Jak widać taki trywialny problem można rozwiązać na wiele sposobów,
wszystko zależy od konkretnych potrzeb i wiedzy programisty.
-
Nie byłem pewien czy można zrobić inc A, a nie zajrzałem do książki.
Muszę ASAP przeredagować/uzupełnić prezentacje z 2019.1 - tam właśnie są wszystkie (?) niezbędne informacje o podstawach.
Poza tym zawsze można użyć komendy i przy kompilacji się dowiedzieć czy jest ona prawidłowa czy też nie ;)
Jak widać taki trywialny problem można rozwiązać na wiele sposobów, wszystko zależy od konkretnych potrzeb i wiedzy programisty.
Praktycznie każdą rzecz (dłuższą niż 10 rozkazów) można napisać na wiele sposobów. Początkującym proponowałbym pisać jak najprościej - na optymalizację kodu przyjdzie czas (czyt. jak się opanuje wszystko inne). I przychodzi to samo z czasem (czyt. dupogodziny z asm ;) )
Inna sprawa że warto korzystać ze sprawdzonych wzorów - procedurki które znajdziesz w tym dziale (nie mówię o tym wątku) są z reguły szybkie, krótkie i na bank działają. Nie ma sensu wyważać otwartych już przez kogoś innego drzwi - lepiej się skupić na innych drzwiach: na swoich pomysłach :)
Kolejna uwaga: XOR a - zeruje akumulator, bo funkcja XOR zeruje te bity, które są jednakowe, a "a XOR a"... chyba logiczne? :)
Sam jak piszę tutaj LD a,0 to wydaje mi się to dziwne - od "zawsze" używałem XOR A (zajmuje to 1 bajt zamiast 2;) ) ale tutaj mamy początkujących, nie mieszajmy im w głowach :D :D :D
-
Jak widać taki trywialny problem można rozwiązać na wiele sposobów, wszystko zależy od konkretnych potrzeb i wiedzy programisty.
Praktycznie każdą rzecz (dłuższą niż 10 rozkazów) można napisać na wiele sposobów. Początkującym proponowałbym pisać jak najprościej - na optymalizację kodu przyjdzie czas (czyt. jak się opanuje wszystko inne). I przychodzi to samo z czasem (czyt. dupogodziny z asm ;) )
Piszący musi przede wszystkim rozumieć co pisze,
taki kod zwykle nie jest optymalny.
Wręcz przeciwnie bywa pokrętny i rozbudowany.
Do prostoty zwykle mu daleko.
Konieczne są też komentarze, bo na drugi dzień możemy już nie wiedzieć co mieliśmy na myśli.
-
Kolejna uwaga: XOR a - zeruje akumulator, bo funkcja XOR zeruje te bity, które są jednakowe, a "a XOR a"... chyba logiczne? :)
Sam jak piszę tutaj LD a,0 to wydaje mi się to dziwne - od "zawsze" używałem XOR A (zajmuje to 1 bajt zamiast 2;) ) ale tutaj mamy początkujących, nie mieszajmy im w głowach :D :D :D
Haha, XOR A podpowiedział mi kiedyś Bill Gilbert. Przedtem też pisałem LD A,0 jak ustawa przewiduje ;D
A potem kupiłem na giełdzie xero "The Complete Spectrum ROM Disassembly" i tam pełno jest takich skrótów...
-
Jeszcze tylko mała uwaga do tego:
Przy zbyt małym opóźnieniu czerwony pas się nie pojawia w ogóle
W rzeczywistości czerwony pas (raczej już pasek) się pojawia, ale poza obszarem widocznym na ekranie monitora. Cała generowana przez komputer ramka jest nieco większa niż to, co widać. Oczywiście tak samo na dole i na bokach.
-
Piszący musi przede wszystkim rozumieć co pisze, taki kod zwykle nie jest optymalny.
Wręcz przeciwnie bywa pokrętny i rozbudowany.
Jedno nie wyklucza drugiego - tutaj skupiam(y) się na nauce podstaw, kod nie musi być optymalny - ma działać i piszący musi (sic!) wiedzieć dlaczego. Jak nie wie, to jest właśnie miejsce by się o to zapytać.
Skracać kod to można jak on działa lub/i się jest bardziej zaawansowanym w programowaniu z80. Kod z80 niewiele pamięci zajmuje, więc jego rozbudowanie nie jest problemem (na początku). Lepiej skupić się na zamierzonym działaniu a potem udoskonalać kod. Poza tym każdy kod można (no dobra - prawie każdy) poprawić - sam patrze na swoje źródła z przed roku i zastanawiam się po co pisałem to "na około" jak można prościej :P
Do prostoty zwykle mu daleko.
On ma być prosty dla osoby piszącej - jak działa, nikogo nie obchodzi "jak to jest zrobione" :) Często, gęsto nie ma sensu poprawiać kodu - no chyba że pisze się 256b intro lub też brakuje pamięci. Ale jak już wspominałem - tutaj się uczymy. Pisanie 256b jest zajebistym zajęciem i wyzwaniem, ale poza pomysłem (który w sumie jak zawsze jest najważniejszy), potrzebne jest 'obycie' w kodzie....
Konieczne są też komentarze, bo na drugi dzień możemy już nie wiedzieć co mieliśmy na myśli.
Nigdy nie zaszkodzą - ba! nawet pomogą. Tylko że im bardziej skomplikowany kod tym bardziej opis powinien być szczegółowy a nikt tego nie robi :) Sam jak wracam do swojego kodu "z przed miesiąca", dłuższy czas zajmuje mi ogarnięcie "co i jak". I to jest normalne (dla starych ludzi? ;) ) i nie ma sensu z tym walczyć.
Przy zbyt małym opóźnieniu czerwony pas się nie pojawia w ogóle
I jak już SirD napisał - należy się cieszyć bo jest na tyle mały (czyli procedura szybka) że go nie widać. Sprawdzanie jaka jest szybkość działania ma praktycznie sens tylko wtedy gdy efekt nie wygląda na płynny albo zdarzają się 'przycięcia'. Jak jest ok, lepiej nie wiedzieć że nie mieści się w ramce :D
A propo's nie mieszczenia się w ramce:
Przy zbyt dużym, następuje desynchronizacja i border miga.
To nie jest desynchronizacja, to jak już też SirD wspominał: efekt nie mieści się w ramce i stąd to miganie.
-
Super, program z wyświetlaniem punktów bardzo cenny dla mnie początkującego. A w jaki sposób wyświetlić wynik np. dodawania dwóch liczb, stan liczbowy rejestru, itp?
-
Wydruk liczby (0-9999):
LD BC, 1234
CALL 6683
Liczby 0..255 można drukować tak samo, ładujemy liczbę do C, a do B zero.
Większe liczby oraz ułamki najwygodniej jest drukować przez stos kalkulatora. Ale to raczej nie jest temat dla początkujących.
Można też liczbę dzielić przez 10 i część całkowitą wyrzucać na ekran przez RST 16, a resztę dalej dzielić itp. Ale to też nie jest zbyt proste :)
-
Przez weekend wyprodukowałem kawałek kodu i stanąłem w jednym miejscu.
Może ktoś wpadnie co z tym zrobić.
Kodu jest sporo ale myślę że jest w miarę czytelny.
Tu jest podprogram który nie spełnia swojego zadania
kasuj
;kasowanie poprzedniej pozycji
ld hl,16384
ld bc,(50010)
add hl,bc
ld a,(hl)
ld hl,16384
add hl,bc
ld (hl),a
ret
calosc w zalaczniku
sterowanie klawiszami O, P
-
A co ten kod ma właściwie robić? Bo robi tak... ładuje do HL początek ekranu, do BC jak rozumiem jakieś przesunięcie, potem dodaje jedno do drugiego i pobiera stamtąd bajt. Następnie jeszcze raz dodaje to samo przesunięcie do tego samego początku ekranu i odkłada w to samo miejsce wcześniej pobrany bajt.
-
Ok, doszedłem jak to działa i dlaczego nie ;)
kasuj
ld hl,16384
ld a,(50011)
ld b,0
ld c,a
add hl,bc
ld (hl),0
ld hl,16384
ld a,(50001)
ld c,a
add hl,bc
ld a,(50000)
ld (hl),a
ret
-
To trzeba zobaczyć w całości, tak bedzie trudno wytłumaczyc.
Ten podprogram pobiera bajt z 50001 w zapisuje do pamieci ekranu.
Robi to, ale co 8 kroków jest błąd.
Pod 50000 jest bieząca pozycja, pod 50001 pozycja jeden krok wstecz.
-
Przyglądam się kursowi i fajnie, że coś się dzieje. Ja swoją przygodę z assemblerem zacząłem od CA80 będąc w wojsku (jakieś 30 lat wstecz). Były to czasy takie, że pisałem program na kartce, obok
assemblowałem na kod dla proca ręcznie i wpisywałem do ram-u cyferki i literki w HEX i programowałem eprom w dorobionym do CA80 programatorze. Największą skarbnicą wiedzy były książki Pana Gardynika twórcy CA80. Tytuły to: "MIK01-06". Początkującym serdecznie polecam. Pierwsze co zrobiłem to podłączyłem wyświetlacz VFD do CA80 od jakiegoś magnetowidu i wyświetlał zegar (czas, data i cyfra jaka mi została do cywila ;). Późnij się przesiadłem na systemy 8032 (rodzina `51) i to już była inna bajka.
-
To trzeba zobaczyć w całości, tak bedzie trudno wytłumaczyc.
No powyżej podałem poprawną wersję podprogramu.
Ale że to wątek poświęcony nauce, na przykładzie Twojego kodu opowiem o kilku ważnych rzeczach:
1) Tabulatory, tabulatory i jeszcze raz tabulatory! Pisanie wszystkiego od lewego brzegu jest nieczytelne. Nie widać co jest etykietą (i gdzie ona jest) a gdzie są rozkazy.
2) Podprogramy. Duży plus za używanie podprogramów, dzięki temu jest to czytelne dla innych (i dla piszącego po 24 godzinach :P). Tyle tylko że warto przy każdym CALL dać komentarz co to robi, szczególnie jak nazwa podprogramu jest lakoniczna ;) Komentarzy nigdy nie za dużo - szczególnie na początku drogi z asm.
3) Odstępy. Bardzo fajnie że poszczególne fragmenty kodu oddzielasz wolną linią - to ułatwia nawigację w kodzie. Także oddzielania poszczególnych części ;======================================== jest bardzo dobrym nawykiem.
4) Bezpośrednie adresowanie w pamięci o ile nie jest to ekran nie jest wskazane. Szczególnie jak trzymasz tam tylko dane. Lepiej zmienne umieszczać na końcu kodu:
bajt_old db 0
bajt_new db 0
kolumna_old db 0
kolumna_new db 0
a w samym kodzie zamiast:
ld a,(50000) ;przepisuje zawartosc
ld (50010),a
ld a,(50001) ;Ksb zapamietanie
ld (50011),a
używasz etykiet:
ld a,(bajt_old) ;przepisuje zawartosc
ld (bajt_new),a
ld a,(kolumna_old) ;Ksb zapamietanie
ld (kolumna_new),a
Jest to o wiele czytelniejsze - no i nie grozi pomyłka jak gdzieś wpiszesz zły adres ;) Weź znajdź w 1000 linijek kodu miejsce gdzie zamiast (50001) wpisałeś (50011) :D :D :D
5) Porządek. Pisząc w asm trzeba być dokładnym - jej brak szybko się zemści. Tak więc jak definiujesz początkowe dane, niech one nie będą przypadkowe.
Wartości startowe ustawiłeś na:
ld a,1 ;zawartosc bajtu (1 - kropka)
ld (50000),a ;K młodszy bajt
ld a,1 ;starszy bajt (0-31)
ld (50001),a ;K nr kolumny
co powoduje że punkt na starcie ustawia się 15 pixeli od lewego brzegu ekranu. Nie lepiej zacząć od lewego brzegu?
ld a,128 ;zawartosc bajtu (1 - kropka)
ld (bajt_old),a ;K młodszy bajt
ld a,0 ;starszy bajt (0-31)
ld (50001),a ;K nr kolumny
6) Szanujmy rejestry. Z80 nie ma ich za dużo więc nie ma sensu używać ich tam gdzie nie trzeba ;)
ld a,(50000)
ld b,a
ld (hl),b ;zapis na ekran
Użycie rejestru B nie ma tu zupełnie sensu, wydłuża czas działania i dodatkowo zaciera obraz całości:
ld a,(50000)
ld (hl),a ;zapis na ekran
7) Ładowanie danych do rejestru/rejestrów. Trzeba zwracać uwagę że czym innym jest LD a,(50000) a ld bc,(50000). I tak:
ld hl,16384
ld bc,(50010)
add hl,bc
powoduje że do rejestru B trafia zawartość pamięci o adresie 50010 a do C - 50011. A nie to chciałeś uzyskać. Prawidłowy kod wygląda następująco:
ld hl,16384
ld a,(50010)
ld b,0
ld c,a
add hl,bc
Ale najważniejsze że program działa! Nie ma znaczenia że robi to trochę "na około" (dlatego do tego się nie przyczepiłem) - najważniejsze że sobie coś wymyśliłeś i to Ci się udało! Nie poddawaj się i pisz dalej - już widać że złapałeś wiatr w żagle i szkoda tego nie pociągnąć dalej! Do Speccy Party 2020.1 jeszcze 10 miesięcy, więc jest szansa że do tego czasu stworzysz swoją produkcję :)
-
Wielkie dzięki za poprawienie kodu i cenne uwagi.
Teraz pora na ruch góra, dół.
a potem zobaczymy :)
-
Czegoś jednak niedopatrzyłeś
kasuj ;kasowanie poprzedniej pozycji
ld hl,16384
ld bc,(50011)
add hl,bc
ld (hl),0
ld hl,16384
;ld bc,(50001)
;add hl,bc
;ld a,(50000)
;ld (hl),a
ret
wyremowałem te linie i dalej działa.
zrobiłeś chyba zapis a ma być tylko kasowanie
-
Zwracam honor, zrobiłeś tak jak było w tym podprogramie o nazwie "kasuj".
A zapis w niej był niepotrzebny, bo zapis odbywa się w innym podprogramie "pokaz".
Tak więc wyświetlanie odbywało się dwukrotnie w "pokaz" i "kasuj".
-
Wkleiłeś starą wersję podprocedury - zobacz punkt 7 w mojej wyliczance - zmieniłem LD BC,(50011) Na LD A,(50011) LD C, A LD B,0
Nie analizowałem całego kodu - usprawniłem tylko by zadziałał ;) Walcz dalej - jak dodasz up/down, pochwal się tutaj :)
-
Wiem że to non profit i tylko w wolnym czasie (którego nikt nie ma w nadmiarze) ale czy coś się może w tym temacie ma szanse urodzić?
Zaczęły się już w sumie długie zimowe wieczory i liźnięcie assemblera dla ZX wydaje się dobrym pomysłem na ich spędzenie ;)
-
Artykuł jest w połowie napisany, dzięki za pingnięcie, postaram się go szybko dokończyć, bo to już czas na wypuszczenie kolejnego numeru Zin80.
-
Poszukuje
Z80 pełna listę komend
Mapę pamięci zx spectrum 128/48
Mapę pamięci nie tylko sektorowa, 8) 8) 8)
oficjalnie niby dostępna.
Przede wszystkim pełna z adresami rejestrów specjalnych do obsługi
hardware'u
Lista rejestrów w kolejności alfabetycznej
Lista rejestrów w kolejności adresowej
Dalej
Playfield
Spritey
Kolory
DMA
Przerwania
Interface joystick
Audio
Wszystko z uzytymi rejestracji nazwa/adres
ROM już mam zdisasemblowny na routiny :D
-
A-ha!
Stronicownie pamięci na bloki.
Lista rejestrów sterujacych ;D
-
https://www.worldofspectrum.org/faq/reference/reference.htm
Wszystko co szukasz i sporo więcej.
-
Tabela z listą rozkazów Z80 była w Bajtku 12-88 na str 23. Poniżej link oraz plik TXT.
http://atarionline.pl/biblioteka/czasopisma/Bajtek/Bajtek_1988_12.djvu
Kolejny plik to rozkazy Basica i Z80 z instrukcji ZX+3.
A obszerniej całe 30 stron o tych rozkazach Z80 było w specjalnym wydaniu IKS-a nr3 w 1987. :)
http://atarionline.pl/biblioteka/czasopisma/IKS/IKS_Zeszyty_Programow_Komputerowych_1987_03.djvu
Sam zdecyduj która postać wydaje ci się bardziej czytelna czy pomocna.
-
Artykuł został już zaopiniowany przez eksperta od asm - matofesi, i 'nie jest źle' -- to takie polskie ;-)
Odezwałem się też do kilku osób, które zgłosiły się jako 'beta testerzy' - liczę na ich opinię.
-
dla mnie jako kogoś kto w ogóle nigdy nie programował to poznanie tajniów tego języka będzie trudne
porównanie do basica jest bardzo dobre chociaż ja nie mam pojęcia jak sie programuje w basicu opócz tego poniżej bo więcej nic nie umiem
10 print "tekst"
20 goto 10
run
nie mam żadnego problemu z kompilacją jest bardzo prosta i na podstawie artykułu myslę że każdy da sobie radę
ciężkie dni przede mną ale dam radę :D
zaczałem od końca i skompilowałem od razu Tygrysa gotowy program
-
Niestety asm nie nadaje się na bycie "pierwszym językiem". Musisz mieć powiedzmy "średnie" pojęcie o programowaniu w ogóle, żeby móc się uczyć asm'a. Tekst Tygrysa daje dobry początek programującym w BASICu, ale niestety nie nieprogramującym.
I żeby nie było - "nie jest źle" - w tym wypadku oznacza, że "może ja bym to napisał inaczej, może byłoby moim zdaniem lepiej, ale jestem śmierdzącym leniem i nigdy się pewnie nie przekonamy" ;) Jak na materiał, który już jest jeśli czytelnik spełni założenia startowe to moim zdaniem będzie się w stanie nauczyć podstaw. A z kolejnych odcinków bardziej zaawansowanych rzeczy :)
-
Najsampierw, jak to się mówi, należy nauczyć się układać algorytm programu, czyli co i jak oraz dlaczego i kiedy.
Jeśli mamy gotowy algorytm, można przystąpić do jego realizacji programowej.
Wtedy, nie ważne jakiego języka programowania się użyje.
Najwyżej program będzie działał szybciej lub wolniej.
Każdy język programowania wysokiego poziomu, posiada składnię i słowa kluczowe mają za zadanie maksymalnie ułatwić rozumienie kodu programu przez człowieka.
Czyli takimi językami są - BASIC, Turbo Pascal itp.
Językiem niskiego poziomu - takim jakimi są asemblery - już nie tak łatwo się posługiwać.
Jest np. opisane tutaj https://pl.wikipedia.org/wiki/J%C4%99zyk_niskiego_poziomu
Niestety, jest tak, że jeśli ktoś nauczy się najpierw programować w języku wysokiego poziomu,
ciężko wyeliminować nawyki i myślenie takim językiem chcąc posłużyć się asemblerem,
gdzie program trzeba pisać "na piechotę" operując na rejestrach procesora, bajtach i komórkach pamięci.
Wiem to ze swojego doświadczenia :)
-
Niestety asm nie nadaje się na bycie "pierwszym językiem". Musisz mieć powiedzmy "średnie" pojęcie o programowaniu w ogóle, żeby móc się uczyć asm'a.
Powiem Ci ze, moglbym sie pospierac, bo z autopsji wiem, ze mozna nabrac pojecia o programowaniu uczac sie assemblera. Wystarczy odpowiednie wprowadzenie.
Np gdy przyjmiemy, ze Procesor to nic innego jak obrabiarka do bajtow to w sumie podstawa juz jest. Procesor po krotce wyciaga sobie wartosc z pamieci, jakos ja przerabia i wkada ja spowrotem do pamieci, lub przeklada gdzies indziej. Czasem dostaje wartosc do wlozenia w pamiec bez poprzedniej obrobki. W Przypadku Z80 trzeba mu tylko na poczatku programu powiedziec gdzie ma stos, czyli miejsce, gdzie moze sobie odlozyc cos na chwile i mamy baze.
Jesli bawimy sie na Zx Spectrum to lokacja stosu jest ustawiana przy starcie systemu, wiec nawet o to nie trzeba sie martwic.
Program 1: Wymysl sobie wartosc i wstaw ja do jakiejs lokacji w pamieci.
To mozna rozwiazac dwiema komendami tylko po co ?
I tu sie zaczyna przygoda poznawania komputera, przywitanie z mapa pamieci i ogolnymi zasadami jak to dziala. Wstawiajac wartosc w miejsce $5800 w prawwym gornym rogu zmieni sie kolor. Wybierajac odpowiednia wartosc przed wstawieniem w ta lokacje mamy kontrole nad tym jaki to bedzie kolor. Wpisujac wartosc w pozycji $5801 obok pojawi sie kolor, nad ktorym tez mamy kontrole. W ten sposob powtarzajac prosta czynnosc, jak wstaw wartosc w pozycje w pamieci mozemy wyrzucic np prosta mozaike dowolnej pozycji ekranu. Kombinujac dalej w pozycjach w pamieci mozna zmienic punkty na ekranie co dodatkowo daje duzo wiecej mozliwosci.
To mozna osiagnac dwiem akomendami i za pomoca tylko tych dwoch komend LD i ST mozna spedzic pol dnia bawiac sie i kombinujac co jeszcze mozna ulozyc z tych kolorow.
Pierwsze co zacznie irytowac w trakcie zabawy to duza ilosc powtarzajacych sie czynnosci i z czystej potrzeby poznamy kolejne dwie komendy potrzebne do petli. W ten sposob nie tylko nie probujemy narzucic kontekstu petli komus nowemu w temacie ale wrecz odpowiadamy na pytanie "jak to zapetlic", czyli powtorzyc okreslona ilosc razy, ktore samo wyniklo z zabawy pierwszymi dwiema komendami, takze koncepcji tlumaczyc juz nie trzeba, wystarczy przedstawic kolejne dwie komendy i wyjasnic kondycje, czyli za kazdym razem w petli sprawdzanie, czy w wartosc licznika ma juz zero. To mozna sprawdziec w wiecej niz jeden sposob ale.......
Mysle, ze gdy sie pozna koncepcje w/g kroych dziala komputer, to sam jezyk programowania stanie sie tylko narzedziem, a zamiast uczyc sie co jaka komenda robi, wystarczy podrzucic pomysl na rozwiazanie drobnego problemu a uczen sam sobie znajdzie i dobierze komendy, ktorych potrzebuje.
Reszta jak mowia to bajka w ktorej jedynym limitem jest wyobraznia.
:)
-
Np gdy przyjmiemy, ze Procesor to nic innego jak obrabiarka do bajtow
Chcialbym to troche rozwinac, bo czesto co ludzi odrzuca od prob podejscia do nauki programowania, to ogolne przekonanie, ze to jest niewiadomo jak bardzo skomplikowane i na pewno za trudne, lub jak to niektorzy mowia: "Nie na moje nerwy".
Tu chcialbym nalozyc lekki nacisk na zdanie powyzej, Procesor to nie jest niewiadomo jak skomplikowany kombajn, ktory ma nieskonczona ilosc rzeczy, ktore dla wiekszosci moga pozostac tylko abstrakcja. Jesli tak nie jest to poprosze o sprostowanie ale Procesor albo wstawia wartosc jaka mu sie poda do lokacji w pamieci, albo wyciaga sobie wartosc z lokacji w pamieci, obrabia ja np przesuwajac bity i albo wstawia spowrotem albo gdzies indziej. To jest wlasciwie wszystko oprocz kilku spraw oczywistych jak mozliwosc do skakania do dowolnej lokacji w pamieci, co wychodzi z potrzeby wynikajacej z powyzszego.
To jest tyle i w gruncie rzeczy tam nic wiecej nie ma. Reszta wynika z tego, ze procesor robi to w miare szybko, przez co mozna wygenerowac obraz i dzwiek wychodzacy z komputera monitorujac przy tym klawiature lub joystick i przy zmianie stanu zmienic obrazek lub muzyczke.
Kiedys, zeby podejsc do komputera, w sensie zeby nawet byc dopuszczonym w poblize, to trzeba bylo miec nalepiej ze dwa fakultety lub profesure z matematyki, ale to wynikalo z tego, ze gdy w programie byl blad to cos moglo stanac w ogniu.
Dzisiaj przecietny zegarek na rece ma wiecej mocy obliczeniowej, niz NASA miala wysylajac ludzi na ksiezyc, a mimo to ten zegarek dziala dokladnie tak samo, czyli ma procesor, ktory nie robi nic wiecej niz: wyciaga, manipuluje i wstawia spowrotem wartosci do pamieci.
Przy dosc sporym uproszczeniu sprawy dla programisty pozostaje tylko sie dowiedziec jaka wartosc wstawic do ktorej lokacji w pamieci, zeby pojawil sie obrazek, lub zeby zagrala muzyczka, a ktora lokacja w pamieci trzyma wartosc, ktora powie ktory klawisz, lub w ktora strone jest wysuniety Joystick.
Pozdr.
-
Pomijasz jedna wazna kwestie. Nie kazdy umie myslec abstrakcyjnie i przyczynowo-skutkowo. Gdyby bylo inaczej, kazdy bylby mistrzem w szachy.
Ze swojego doswiadczenia wiem, ze nie jestem w stanie napisac nic wiekszego niz kilka linii programu i to bez zlozonych petli, procedur, funcji i takich tam.
-
Pomijasz jedna wazna kwestie. Nie kazdy umie myslec abstrakcyjnie i przyczynowo-skutkowo.
Wiem, ale jesli masz czas to mozesz dojsc do wymaganego efektu metoda prob i bledow. Co do bardziej skomplikowanych spraw to Dalthon wspomnial na ostatnim party, ze w przypadku np Zx Spectrum, jesli natrafisz na problem, z ktorym nie wiesz jak sobie poradzic, to ten problem na pewno juz ktos rozwiazal i na pewno gdzis na necie znajdziesz kod, ktory zrobi co potrzebujesz, a jak nie znadziesz to pytaj tutaj.
Sam fakt, ze masz problem do rozwiazania to juz duzo, bo zazwyczaj potrzeba jest przyczyna wynalazkow tak w realu jak i w swiecie programowania.
Dla poczatkujacych posiadanie problemu w programowaniu czesto jest znakiem, ze nie do konca, albo juz nawet wcale sie nie jest poczatkujacym :)
Pozdr.
-
Każdy musi sobie wypracować własny styl i metodę jak przyswajać wiedzę.
Nie ma dwóch takich samych umysłów.
Np. ja zawsze staram się mieć plan (algorytm) i kilka pomysłów zapasowych.
Co nie oznacza, że trzymam się sztywno założeń, gdyż czasami należy być elastycznym i improwizować :)
-
Każdy musi sobie wypracować własny styl i metodę jak przyswajać wiedzę.
To na pewno, ale gdy na starcie dostaniesz dwa tomiszcza rzeczy, ktore na pewno sie przydadza, ale na poczatek nie so potrzebne zamiast jednego lub dwoch paragrafow, ktore wystarcza, zeby Ci zajac reszte dnia, to moze byc troche demotywujace.
Mysle, ze gdyby opracowc metode, gdzie na start dostarcza sie maksymalne minimum potrzebne do stworzenia czegos ciekawego to wtedy mozna by zachecic szersza publike do chocby podjecia proby.
Kiedy na cos spojzysz i stwierdzisz, ze to jest na tyle proste, ze mozesz sie tego podjac to sie podejmiesz, lecz gdy zamiast dostaniesz dwa tomy technicznego zargonu, to nawet pierwszego paragrafu sie odechciewa czytac i tak zazwyczaj jest.
Pierwszy problem to ustawienie srodowiska do programowania i testownia programu. Fanie by bylo miec krotki spis krok po kroku co skad zassac i zainstalowac, zeby mozna bylo napisac dwie linie kodu, uruchomic i zobaczyc ich efekt w emulatorze. Ludzie, ktorze chcieliby zrobic cos takiego na Commodore maja C64Studio i WinVice. Wystarczy zainstalowac te dwa programy i to pozwala na czystym komputerze, lub laptopie z windowsem i polaczeniem do internetu w ciagu pieciu do dziesieciu minut byc w pozycji, gdzie mozna napisac, skompilowac kod w assemblerze lub basicu, po czym go uruchomic w emulatorze i od razu zobaczyc jego efekty. C64 studio przychodzi z przykladami, ktore pokazuja jak szybko osiagnac proste rzeczy, jak np sprawdzenie, czy ktorys klawisz jest nacisniety.
Moze taki krotki opis bylby dobry na start. Nawazniejsze, zeby to bylo proste i w co najwyzej kilku krokach.
-
Kiedy można liczyć na ukazanie się nowego Zin80?
-
ósma strona kursu z którego nic nie wynika.
może jakieś przykłady z opisem np. ludzik w lewo, ludzik w prawo, kolizja itp.
bo 10 for goto itd. było w przedszkolu
-
Wstrzymaj konie ... wszystko po kolei ... ;D
Nie jestem jakimś tam guru od kodowania jak inni koledzy tutaj, ale trzeba wpierw wziąć pod uwagę architekturę samego sprzętu i jego ograniczenia.
Ludzik pobiegnie, ale w ZX Spectrum nie znajdziesz sprzętowego ruchu "ludzika" (sprite) i wykrywania kolizji, raczej powinieneś wiedzieć/liczyć co i gdzie jest oraz wszystko "malować" głównym procem ???
Optymalne czasowo procedury graficzne i triki programowe są często tajemnym patentem danego kodera. :P
-
A co Was boli w dostepnych tutorialach np. :
https://chuntey.wordpress.com/category/z80-assembly/
Dla poczatkujacych sa tez polskie publikacje np. ABC asemblera albo zeszyty Konkret.
Jak ktos chce pisac dema albo gry klasy R-Type to juz inna bajka, nawet biegla znajomosc Z80 nie wystarczy.
ZX-Basic ma duzo zalaczonych przykladow, a daje kod "prawie" jak asembler. Zeby zaczac brakuje przewaznie czasu i checi...
-
Właśnie chodzi o ten czas i chciałem trochę na skróty bo na karku już 42 lata ;)
-
Eee, to kolega młody jeszcze :)
Jest czas :)
-
Stary, to Ty jestes mlody :)
Na poczatek naprawde jest duzo materialow, a dzieki emulatorom latwo eksperymentowac.
Mysle, ze latwiej byloby zrobic jakis sensowny kurs, gdyby zainteresowani okreslili czego oczekuja.
Co ciekawe - na ZXa praktycznie nie bylo ksiazek o zaawansownym programowaniu w asemblerze, a na 128K to juz w ogole pustynia.
Jedyna znana mi anglo pozycja do 48K to Advanced Spectrum Machine Language:
https://spectrumcomputing.co.uk/index.php?cat=96&id=2000025
Inne ksiazki skupialy sie w zasadzie na programowaniu Z80, wiecej bylo za to w pismach typu Your Spectrum.
-
@Maryjan @Gryzor dzięki za słowa otuchy ;D
-
LePi - nowy numer zina już niebawem, może nawet w tym tygodniu.
-
Właśnie chodzi o ten czas i chciałem trochę na skróty bo na karku już 42 lata ;)
Ktoś musi powiedzieć drastyczną prawdę: nie da się na skróty! Niestety :/
Nie chciałbym nikogo zniechęcać ale samo nic nie przyjdzie, by coś osiągnąć trzeba pisać, pisać i jeszcze raz pisać. Z nieba samo nic nie spadnie ;)
Sam wracając do z80 po wielu latach potrzebowałem miesiąca (poważnie!) by napisać małe interko, które teraz trzasnąłbym w jeden wieczór.
Setki przykładów nie pomogą jak sami nie zaczniecie pisać - zgłaszajcie na forum problemy, na bank ktoś Wam je rozwiąże. Ale musicie zacząć, bez tego ani rusz :/
-
@Dalthon Jedyny problem jest wtedy jak ktoś bardzo by chciał, ale nigdy tego nie robił... Bez tego, co za chwilę będzie w Zin80 (albo funkcjonalnego odpowiednika) nie da się zacząć pisać jeśli nie wiesz jak ;)
-
Jedyny problem jest wtedy jak ktoś bardzo by chciał, ale nigdy tego nie robił...
Zgadzam się - tyle że samouczek na start nie zaszkodzi ale to tylko początek, ale nie ma drogi na skróty ;)
-
Tylko i aż ;)
-
Ano - dlatego też postaram się do Zin80 #3 napisać artykuł o bankowaniu w 128. Rzecz prosta jak się wie jak to zrobić - tyle że informacji w necie niewiele a te które są wprowadzają w błąd (o czym sam się przekonałem jak Stawicki vs. The Oryginal w dzień party działał na emu a na realnym sprzęcie już niekoniecznie;)
-
Wyglada, ze to bedzie kurs przyspieszny... ;D
-
@Gryzor to co Tygrys przygotował do nowego numeru Zin80 jest moim zdaniem dość łagodnym wprowadzeniem - o ile można w asma wprowadzić łagodnie ;)
-
@Gryzor to co Tygrys przygotował do nowego numeru Zin80 …
Nie pogrzewal atmosfety i tak juz goracej z drugim wydaniem Zina :D
-
W najnowszym numerze Zin80 (numer 2) jest mój artykuł dotyczący asemblera -- zgodnie z tematem tego watku.
Owocnej nauki!