Autor Wątek: Kurs programowania w asm (dla ZX na początek)...  (Przeczytany 48425 razy)

Dalthon

  • ****
  • Wiadomości: 428
  • Miejsce pobytu:
    TriCity
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #60 dnia: 2019.06.07, 14:51:07 »
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).
ZX Spectrum +2 Grey | Just Speccy 128 | ZX Spectrum Next | ZX-Uno 2MB |  Murmulator | Amstrad 6128 | MSX2 Philips VG-8235 | Commodore 64 | Commodore +4 | Atari 520 STF | Amiga 1200

Dalthon

  • ****
  • Wiadomości: 428
  • Miejsce pobytu:
    TriCity
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #61 dnia: 2019.06.07, 15:06:30 »
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'.
ZX Spectrum +2 Grey | Just Speccy 128 | ZX Spectrum Next | ZX-Uno 2MB |  Murmulator | Amstrad 6128 | MSX2 Philips VG-8235 | Commodore 64 | Commodore +4 | Atari 520 STF | Amiga 1200

Sir David

  • ****
  • Wiadomości: 391
  • Miejsce pobytu:
    Białystok
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #62 dnia: 2019.06.07, 15:20:17 »
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.

LaPi

  • *
  • Wiadomości: 24
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #63 dnia: 2019.06.07, 15:51:09 »
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 ?

Phonex

  • *****
  • Wiadomości: 1261
  • Miejsce pobytu:
    Warszawa
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #64 dnia: 2019.06.07, 15:53:40 »
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
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.
« Ostatnia zmiana: 2019.06.07, 16:03:10 wysłana przez Phonex »

Dalthon

  • ****
  • Wiadomości: 428
  • Miejsce pobytu:
    TriCity
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #65 dnia: 2019.06.07, 16:01:12 »
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
« Ostatnia zmiana: 2019.06.07, 16:28:34 wysłana przez Dalthon »
ZX Spectrum +2 Grey | Just Speccy 128 | ZX Spectrum Next | ZX-Uno 2MB |  Murmulator | Amstrad 6128 | MSX2 Philips VG-8235 | Commodore 64 | Commodore +4 | Atari 520 STF | Amiga 1200

tdu

  • *****
  • Wiadomości: 926
  • Miejsce pobytu:
    Gdansk
    • Nasze Wędrowanie
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #66 dnia: 2019.06.07, 19:18:59 »
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
ZX81/ZX 48k/Zx48k+/ZX +2/ZX +2A/+3/TC2048/FDD3000/FDD5000/3"/3,5'/5,25'/Beta 48k Apina/D+/GP50s/DIVIDE CF/Masterface/Polbasic SamCoupe QL CPC6128/N100 MSX-SVI738  MSX2-VG8235

trojacek

  • *****
  • Wiadomości: 6846
  • Miejsce pobytu:
    Warszawa
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #67 dnia: 2019.06.07, 19:22:04 »
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
« Ostatnia zmiana: 2019.06.07, 19:29:59 wysłana przez trojacek »

Sir David

  • ****
  • Wiadomości: 391
  • Miejsce pobytu:
    Białystok
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #68 dnia: 2019.06.07, 19:32:09 »
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 :)

trojacek

  • *****
  • Wiadomości: 6846
  • Miejsce pobytu:
    Warszawa
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #69 dnia: 2019.06.07, 20:36:41 »
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 :)
« Ostatnia zmiana: 2019.06.07, 20:45:40 wysłana przez trojacek »

tdu

  • *****
  • Wiadomości: 926
  • Miejsce pobytu:
    Gdansk
    • Nasze Wędrowanie
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #70 dnia: 2019.06.07, 21:03:05 »
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.

ZX81/ZX 48k/Zx48k+/ZX +2/ZX +2A/+3/TC2048/FDD3000/FDD5000/3"/3,5'/5,25'/Beta 48k Apina/D+/GP50s/DIVIDE CF/Masterface/Polbasic SamCoupe QL CPC6128/N100 MSX-SVI738  MSX2-VG8235

steev

  • *****
  • Wiadomości: 1366
  • Miejsce pobytu:
    inode 42
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #71 dnia: 2019.06.07, 22:22:36 »
Nie wyobrażam sobie ręcznego liczenia taktów, żeby sprawdzić czy mieszcze się w ramce czy nie.
Więc wyobraź sobie kompilator ZASM. :)
Wszystkiego sam nie zrobi, ale mocno ułatwi.
Machines should work. People should think.

tdu

  • *****
  • Wiadomości: 926
  • Miejsce pobytu:
    Gdansk
    • Nasze Wędrowanie
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #72 dnia: 2019.06.08, 12:16:21 »
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.

Cytuj

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.
ZX81/ZX 48k/Zx48k+/ZX +2/ZX +2A/+3/TC2048/FDD3000/FDD5000/3"/3,5'/5,25'/Beta 48k Apina/D+/GP50s/DIVIDE CF/Masterface/Polbasic SamCoupe QL CPC6128/N100 MSX-SVI738  MSX2-VG8235

trojacek

  • *****
  • Wiadomości: 6846
  • Miejsce pobytu:
    Warszawa
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #73 dnia: 2019.06.08, 12:29:29 »
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".



« Ostatnia zmiana: 2019.06.08, 12:46:11 wysłana przez trojacek »

tdu

  • *****
  • Wiadomości: 926
  • Miejsce pobytu:
    Gdansk
    • Nasze Wędrowanie
Odp: Kurs programowania w asm (dla ZX na początek)...
« Odpowiedź #74 dnia: 2019.06.08, 13:13:31 »
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.
ZX81/ZX 48k/Zx48k+/ZX +2/ZX +2A/+3/TC2048/FDD3000/FDD5000/3"/3,5'/5,25'/Beta 48k Apina/D+/GP50s/DIVIDE CF/Masterface/Polbasic SamCoupe QL CPC6128/N100 MSX-SVI738  MSX2-VG8235