Autor Wątek: Proste i przyjemne procedurki asemblerowe dla początkujących  (Przeczytany 36125 razy)

pear

  • *****
  • Wiadomości: 5511
  • Miejsce pobytu:
    Będzin
  • Z80 only
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #15 dnia: 2014.03.03, 19:36:03 »
A to powyżej dopiero można rozpętlić wykorzystując 'inc l' zamiast inc hl , troszcząc się o odpowiednią obsługę rejestru H.
I tu od razu powinieneś dla początkujących napisać, dlaczego osobno INC L i osobno obsługa rejestru H.
INC HL zajmuje 2 bajty/10 taktów, a INC L tylko 1 bajt/4 takty.
Dodatkowo INC HL nie zmienia stanu znaczników, a INC L wpływa m.in. na stan znacznika Z co można wykorzystać nie tylko do obsługi rejestru H, ale też do kontroli pętli.
ZX/Enterprise/CPC/Robotron/C128D

Tygrys

  • Administrator
  • *****
  • Wiadomości: 4540
  • Miejsce pobytu:
    Warszawa
  • mistrz ceremonii
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #16 dnia: 2014.03.03, 19:55:06 »
@Rafał: sjasmplus to na prawdę niezły kompilator, ma zaimplementowane struktury, ale ma, jak wspomniałeś, wielką tolerancję na dowolny zapis rozkazów, które mogą doprowadzić do błędów. Przez wiele godzin przepisywałem replayer PT3 na pasmo, porównując kod binarny wygenerowany przez sjasm i pasmo. sjasmplus miał totalną dowolność do znaku używanego w (IX+nn).

@pear: po raz kolejny muszę Cię poprawić.
INC L  i INC HL zajmują 1 bajt. INC L trwa 4 takty, INC HL - 6 taktów.
2 bajty zajmują INC IX,IY.

pear

  • *****
  • Wiadomości: 5511
  • Miejsce pobytu:
    Będzin
  • Z80 only
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #17 dnia: 2014.03.03, 20:16:46 »
Tak, masz rację ... znowu  :D
Mam przesuniętą tabelkę w moim wydaniu "Przewodnika po ZX Spectrum"

A przy okazji sprawdziłem czyszczenie z użyciem stosu.

         org   60000
         di
         ld (buf_SP),SP
         ld SP,$5800
         ld HL,$C00
         ld DE,0         
ins_loop push DE        ;(11T*256)*12
         dec L          ; (4T*256)*12
         jr NZ,ins_loop ;(12T*255+7T)*12
         dec H          ;         4T*12
         ld A,H         ;         4T*12
         or L           ;         4T*12
         jr NZ,ins_loop ;        12T*12    =  83172
                        ; LDIR 21T*6143+16 = 129019
end_loop ld SP,(buf_SP)
         ei
         ret
buf_SP   dw 0
Jeśli tym razem się nigdzie nie walnąłem, to pętla czyszczenia ekranu z użyciem stosu zajmie 83172 takty, a przez LDIR 129019 taktów.
Względem LDIR użycie stosu skraca czas do 64,5%.
« Ostatnia zmiana: 2014.03.03, 20:36:38 wysłana przez pear »
ZX/Enterprise/CPC/Robotron/C128D

Tygrys

  • Administrator
  • *****
  • Wiadomości: 4540
  • Miejsce pobytu:
    Warszawa
  • mistrz ceremonii
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #18 dnia: 2014.03.03, 20:29:25 »
Zrezygnowałbym z jr i zastąpił go jp - szybszy o 2 takty, za to dłuższy o bajt.
Do tego push de powtórzyłbym 2^n ilość razy i na tym budował pętlę - kolejne takty zaoszczędzone.
Teoretycznie można 3072 razy użyć PUSH, ale to już chyba nie o to chodzi.

pear

  • *****
  • Wiadomości: 5511
  • Miejsce pobytu:
    Będzin
  • Z80 only
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #19 dnia: 2014.03.03, 20:32:43 »
Z JP zamiast JR wychodzi już 77064 takty, czyli 59,7% zwykłego LDIR.
Do tego PUSH DE można powtórzyć 12 razy i zlikwidować drugą pętlę - wtedy wyjdzie 37376 taktów, a to już 29% :)

Ale to już jakby nie na temat :)
« Ostatnia zmiana: 2014.03.03, 20:43:11 wysłana przez pear »
ZX/Enterprise/CPC/Robotron/C128D

Phonex

  • *****
  • Wiadomości: 1261
  • Miejsce pobytu:
    Warszawa
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #20 dnia: 2014.03.03, 21:13:08 »
Hej, a po co w ostatnim kodzie jest
LD A, H
OR L   ?
Niepotrzebne!

pear

  • *****
  • Wiadomości: 5511
  • Miejsce pobytu:
    Będzin
  • Z80 only
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #21 dnia: 2014.03.03, 21:15:52 »
W sumie racja :)
L już jest równe 0, a DEC H ustawi znacznik Z.
ZX/Enterprise/CPC/Robotron/C128D

ZbyniuR

  • *****
  • Wiadomości: 3333
  • Miejsce pobytu:
    Carlisle w UK
  • CPC AGA PSX
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #22 dnia: 2014.03.03, 23:39:03 »
sect0r - czy ty coś łapiesz z tego co oni tu gadają?
- Jeśli masz w domu światło i wodę, tzn. że masz światłowód. ;)

sect0r

  • *****
  • Wiadomości: 698
  • Miejsce pobytu:
    Oltedal/NO
  • speccyholic
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #23 dnia: 2014.03.04, 06:13:41 »
Jak na razie to łapie te procedurki co wrzuciłem na początku. Tego ze stosem jeszcze nie analizowałem, jak czegoś nie rozumiesz to po prostu pisz, właśnie po to założyłem ten temat.
Szarak # DivIDE+ # MasakratorFM DeluXe by Zaxon

pear

  • *****
  • Wiadomości: 5511
  • Miejsce pobytu:
    Będzin
  • Z80 only
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #24 dnia: 2014.03.04, 06:46:02 »
No to powoli i jeszcze raz.
         org   60000    ; ustawiamy adres, pod którym ma się znaleźć w pamięci kod programu
         di             ; wyłączamy przerwania, bo one również korzystają ze stosu
         ld (buf_SP),SP ; zapamiętujemy aktualne położenie szczytu stosu
         ld SP,$5800    ; szczyt stosu ustawiamy na koniec obszaru pamięci ekranu
         ld L,0         ; ustawiamy licznik pętli na 0, czyli na 256,
                        ; ponieważ przed pierwszym sprawdzeniem zawartości rejestru L
                        ; zostanie on pomniejszony o 1, więc będzie miał wtedy
                        ; wartość -1 w kodzie U2 co bitowo bez znaku daje dokładnie 255
                        ; i pętla nie zatrzyma się nam zaraz po pierwszym wykonaniu
         ld DE,0        ; zerujemy parę rejestrów DE, której zawartość będzie kopiowana do pamięci         
ins_loop push DE        ; i w pętli wrzucamy po 2 bajty na stos
         push DE        ; każdy PUSH automatycznie zmniejsza adres licznika
         push DE        ; stosu o 2, rozmiar pamięci obrazu to 6144 bajty
         push DE        ; podzielone przez 2, bo wpisujemy po 2 bajty na raz
         push DE        ; daje 3072, dalej podzielone przez 256, bo tyle
         push DE        ; można zmieścić w 8-bitowej pętli, daje 12 powtórzeń
         push DE        ;
         push DE        ;
         push DE        ;
         push DE        ;
         push DE        ;
         push DE        ;
         dec L          ; zmniejszamy licznik pętli, w zależności od zawartości
                        ; rejestru L po tej operacji zostanie ustawiony znacznik Z
                        ; tzn. Z=1, jeśli L=0 lub Z=0, jeśli L<>0
         jp NZ,ins_loop ; jeśli Z=0, to kontynuujemy pętlę
end_loop ld SP,(buf_SP) ; odtwarzamy zawartość rejestru SP
         ei             ; i przywracamy przerwania                                                 
         ret            ; koniec
buf_SP   dw 0           ; definicja zmiennej, w której przechowujemy SP
ZX/Enterprise/CPC/Robotron/C128D

ZbyniuR

  • *****
  • Wiadomości: 3333
  • Miejsce pobytu:
    Carlisle w UK
  • CPC AGA PSX
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #25 dnia: 2014.03.04, 12:29:58 »
Parę razy słyszałem że pętla z komend LDI jest szybsza niż LDIR. Czy mógłby ktoś pokazać taki zamiennik w którym na początku podaje się te same 3 dwubajtowe "zmienne" czyli skąd, dokąd i ile przenieść.
Ciekawi mnie o ile jest to bardziej skomplikowane, o ile dłuższe, i o ile szybsze od tych 11 bajtów z LDIR.
- Jeśli masz w domu światło i wodę, tzn. że masz światłowód. ;)

matofesi

  • *****
  • Wiadomości: 2049
  • Miejsce pobytu:
    Toruń/Poland
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #26 dnia: 2014.03.04, 13:12:39 »
Sprawa z LDI/LDIR jest prosta - pojedyncze LDI przerzucające jeden bajt to 16 taktów. LDIR ma 21/16 taktów czyli dla każdego bajtu poza ostatnim to 21 taktów dla ostatniego 16. Dodatkowe 5 taktów na bajt to wewnętrzna pętla. Jeśli więc uda nam się zrobić sensowną pętlę, która wykona się w krótszym czasie to ciąg LDI będzie szybszy niż LDIR.

Wersja LDIR:
ld hl,skad  ; 10
ld de,dokad ; 10
ld bc,ile   ; 10

ldir        ; ((ile-1)*21)+16

Zakładając, że przerzucamy 2048 bajtów czas wykonania to 10+10+10+(2047*21)+16=43033 takty

Wersja z LDI to na przykład:
ld hl,skad  ; 10
ld de,dokad ; 10
ld a,ile/32 ; 7

petla:
rept 32
  ldi       ; 16
endm
dec a       ; 4
jp nz,petla ; 10

Czas wykonania przy 2048 bajtach to 10+10+7+64*32*16+64*(10+4)=33691 takty. Jak widać nie da się tego tak po prostu zamienić - użycie LDI wymaga przerzucania "zaokrąglonych" bloków danych - w tym wypadku do 32 bajtów. Dzięki temu jednak przy najprostszej wersji kodu zaoszczędziliśmy prawie 9500 taktów.

Konstrukcja rept...endm to skrót używany przez assembler pasmo, żeby nie pisać 32 razyLDI ;)

edit : Poprawiłem bezmyślne użycie DJNZ na pętlę z użyciem akumulatora - oczywiście nie jest to optymalne itp. ale jest czytelne ;) I kosztuje extra 1 takt na każdym przebiegu pętli.

RafalM

  • *****
  • Wiadomości: 1133
  • Miejsce pobytu:
    Sulejówek
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #27 dnia: 2014.03.04, 13:56:59 »
ld hl,skad  ; 10
ld de,dokad ; 10
ld b,ile/32 ; 7

petla:
rept 32
  ldi       ; 16
endm
djnz petla  ; 13/8

To jest dosyć ryzykowny kod bo LDI zmienia (zmniejsza) również parę rejestrów BC. Jak C przejdzie przez 0 to zmniejszy się B i pętla wykona się złą ilość razy. Nigdzie nie ustawiasz C więc to tak naprawdę będzie losowa sprawa.

Chyba mam lepsze rozwiązanie. Zupełnie niedawno poznałem ciekawą i chyba dość nieznaną instrukcję:

JP PE,nn - chyba 10 taktów

Można napisać
ld bc,7
petla:
rept 32
  ldi     
endm
JP PE, petla 

i wykona się 7 razy, tyle ile w BC

Phonex

  • *****
  • Wiadomości: 1261
  • Miejsce pobytu:
    Warszawa
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #28 dnia: 2014.03.04, 14:13:08 »
A no jasne!!! :D
Dzięki Rafał za eleganckie rozwiązanie!
Do tej pory robiłem to klasycznie:
ld hl,skąd
ld de,dokąd
ld bc,ilość

petla:
rept 32
  ldi
endm
ld a, b
or c
jp nz, petla

edit: poprawiłem błędne ld bc,ilość/32 na ld bc, ilość
« Ostatnia zmiana: 2014.03.04, 15:17:00 wysłana przez Phonex »

Tygrys

  • Administrator
  • *****
  • Wiadomości: 4540
  • Miejsce pobytu:
    Warszawa
  • mistrz ceremonii
Odp: Proste i przyjemne procedurki asemblerowe dla początkujących
« Odpowiedź #29 dnia: 2014.03.04, 14:38:28 »
Nie ma to jak złe rozwiązanie zastąpić jeszcze gorszym ;)
PE tu jedynie zaciemnia obraz, no i przede wszystkim nie spełnia swojego zadania.

Poprawne rozwiązanie powinno wyglądać tak:

ld hl,zrodlo
ld de,cel
ld a,N
_petla:
rept 32
 ldi
endm
dec a
jp nz,_petla

o ile długość bloku da się podzielić przez 32 i nie jest większe niż 32*256