Autor Wątek: mój pierwszy programik  (Przeczytany 36461 razy)

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
mój pierwszy programik
« dnia: 2018.08.05, 20:11:35 »
właściwie to trzeci, ale pierwszy poważniejszy, działa to cholernie wolno, ale uczę się dopiero optymalizować na z80 :)
Załączam kod, byłbym wdzięczny za wszelkie uwagi :)

trojacek

  • *****
  • Wiadomości: 6831
  • Miejsce pobytu:
    Warszawa
Odp: mój pierwszy programik
« Odpowiedź #1 dnia: 2018.08.05, 20:15:46 »
jp loop
 ret
Po co ten ret???

cp 0Krócej, bo w jednym bajcie:
or a
xor $ffJeśli chcesz zanegować (logicznie) akumulator, to prościej - jednym bajtem kodu:
cpl
dec b
jr nz,loop_sin2
Zamiast tych dwóch rozkazów masz jeden:
djnz loop_sin2
ld a,(hl)
 ld (de),a
 dec e
 inc l
To jest bardzo niebezpieczne. Jesteś pewien, że rejestry e i l się nie "przekręcą"? Zamiast tego stosuj inkrementacje i dekerementacje 16-bitowe:
dec de
inc hl

add a,e
 ld e,a
 ld a,(de)
Uwaga j.w. Dodawanie (add a,e) może przepełnić akumulator i wtedy ld a,(de) pobierze wartość spod adresu, którego nie przewidziałeś. Chyba, że przewidziałeś, stosując jakąś niesamowitą sztuczkę :) (nadal nie rozumiem Twojego algorytmu).
« Ostatnia zmiana: 2018.08.05, 20:28:39 wysłana przez trojacek »

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #2 dnia: 2018.08.05, 20:40:31 »
Dziękówka za uwagi, przepełnienie rejestrów niczym nie grozi bo dane są pobierane z jednej strony pamięci ($7000-$70ff) i to tak ma działać

Dalthon

  • ****
  • Wiadomości: 428
  • Miejsce pobytu:
    TriCity
Odp: mój pierwszy programik
« Odpowiedź #3 dnia: 2018.08.06, 12:36:17 »
W domu rzucę na to okiem dokładniej (podoba mi się efekt!:) pomyśl aby zrobić to na atrybutach wtedy można na cały ekran to zrobić i będzie duuuuużo szybsze :)

     jr z, nxt_line
     jp loop_calc
nxt_line

można

     jp nz,loop_calc
nxt_line

Ogólna uwaga - aby procedury szybciej działały zamiast jr... używaj jp... 1 bajt więcej ale szybciej działa.
Zapis w pamięci jest dużo wolniejszy od operacji na rejestrach - używaj alternatywnych rejestrów (exx, ex af,af')
« Ostatnia zmiana: 2018.08.06, 12:45:32 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

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #4 dnia: 2018.08.06, 12:49:13 »
dziękuweczka za uwagi Dalthon, właśnie tak sobie pomyślałem, żeby użyć exx, nie wiedziałem,że operacje na pamięci są wolniejsze od rejestrów, zapamiętam

matofesi

  • *****
  • Wiadomości: 2048
  • Miejsce pobytu:
    Toruń/Poland
Odp: mój pierwszy programik
« Odpowiedź #5 dnia: 2018.08.06, 13:11:54 »
Ogólna uwaga - aby procedury szybciej działały zamiast jr... używaj jp... 1 bajt więcej ale szybciej działa.

A żeby uściślić - warunkowe JR ma różne czasy wykonania zależnie od tego, czy warunek jest spełniony (12 t-states) czy nie (7 t-states), warunkowe JP ma zawsze 10 t-states.

JP warto używać w kodzie, który ma się wykonywać "równo" (efekty na borderze, dokładna synchronizacja do rastra) albo w sytuacji, gdy np. przewidujemy "krótką" pętlę (czyli warunek może być spełniony bądź nie mniej więcej w równych proporcjach). Jeśli kod będzie się "kręcił" dość długo w jednym miejscu i wyskakiwał warunkowo raz na jakiś czas (czyli przez większość czasu "przelatywał" przez skok) to bardziej się opłaca JR. Oczywiście w takiej sytuacji dobrze jest przeliczyć takty zużywane na obsługę i np. główną pętlę robić na JR (o ile sięgnie) a warunki na JP itp.

Za każdym razem jeśli kod ma być szybki należy mu się przyjrzeć z tabelką rozkazów i policzyć co się bardziej opłaca. A już najlepiej - jeśli mamy miejsce w pamięci - rozwinąć wszystko co się da rozwinąć ;)


gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #6 dnia: 2018.08.06, 14:02:50 »
dzięki matofesi,
posiedziałem trochę nad kodem i tak jak radziliście zmieniłem operacje na pamięci na operacje na rejestrach, kod obliczający wartość poszczególnych pixeli wcześniej wyglądał tak:

 ld a,(s1)
 add a,2
 ld (s1),a
 
 add a,e
 ld e,a

 ld a,(de)
 ld e,0
 ld (byte1),a

 ld a,(s2)
 add a,3
 ld (s2),a
 
 add a,e
 ld e,a

 ld a,(de)
 ld e,0
 ld (byte2),a

 ld a,(s3)
 add a,6
 ld (s3),a

 add a,e
 ld e,a

 ld a,(de)
 ld e,0
 ld (byte3),a

 ld a,(s4)
 add a,8
 ld (s4),a
 
 add a,e
 ld e,a

 ld a,(de)
 ld e,0
 
 push hl

 ld hl,byte1

 add a,(hl)
 jr nc,forw1
 inc e
forw1
 inc l
 add a,(hl)
 jr nc,forw2
 inc e
forw2
 inc l
 add a,(hl)
 jr nc,forw3
 inc e
forw3

 pop hl

teraz wygląda tak:

 exx

 ld a,b
 add a,2
 ld b,a
 
 ld (for_sinus1+1),a

for_sinus1
 ld a,($7000)
 ld (for_add1+1),a

 ld a,c
 add a,3
 ld c,a
 
 ld (for_sinus2+1),a

for_sinus2
 ld a,($7000)
 ld (for_add2+1),a


 ld a,d
 add a,6
 ld d,a

 ld (for_sinus3+1),a

for_sinus3
 ld a,($7000)
 ld (for_add3+1),a


 ld a,e
 add a,8
 ld e,a

 ld (for_sinus4+1),a

for_sinus4
 ld a,($7000)
 
 exx

 ld e,0

for_add1
 add a,0
 jp nc,for_add2
 inc e
for_add2
 add a,0
 jp nc,for_add3
 inc e
for_add3
 add a,0
 jp nc,finished
 inc e
finished


I działa to znacznie szybciej :)

matofesi

  • *****
  • Wiadomości: 2048
  • Miejsce pobytu:
    Toruń/Poland
Odp: mój pierwszy programik
« Odpowiedź #7 dnia: 2018.08.06, 14:28:03 »
Nie analizując dogłębnie algorytmu zasugeruję jeszcze jedną rzecz - używasz "sla a" do mnożenia przez dwa. To jest wysoce nieekonomiczne - sla to 8 t-stateów a "add a,a" robiące w tym wypadku to samo tylko 4. Niby grosze, ale jednak ;)

Poz tym jeśli masz stosunkowo krótką pętlę DJNZ do przy każdym jej "obrocie" tracisz 13 t-stateów - jeśli nie ma ograniczeń co do rozmiaru kodu rozwiń pętlę np tak:
; ld b,$40
 
rept $40
;loop_sin2
 ld a,(hl)
 cpl
 ld (de),a 
 inc e
 inc l
endm
; djnz loop_sin2

Na każdej trzech pętli oszczędzisz ponad 800 t-stateów.

Pewnie jakby się temu przyjrzeć z głębszą analizą samego algorytmu dałoby się urwać jeszcze coś ;)

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #8 dnia: 2018.08.06, 14:30:24 »
wow dziękuweczka, cenne rady panie!

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #9 dnia: 2018.08.06, 14:42:04 »

Na każdej trzech pętli oszczędzisz ponad 800 t-stateów.


Akurat ta procka jest wywoływana tylko 1 raz, ale rozpętlanie kiedyś się na pewno przyda

matofesi

  • *****
  • Wiadomości: 2048
  • Miejsce pobytu:
    Toruń/Poland
Odp: mój pierwszy programik
« Odpowiedź #10 dnia: 2018.08.06, 14:47:56 »
Akurat ta procka jest wywoływana tylko 1 raz

No widzisz... tak to jest jak się człowiek skupia na szczegółach a nie na szerszym obrazie ;)

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #11 dnia: 2018.08.06, 14:49:20 »
spoczko, ale jestem wdzięczny za wszelkie uwagi, z80 to zupełnie inna filozofia niż 6502/6510

matofesi

  • *****
  • Wiadomości: 2048
  • Miejsce pobytu:
    Toruń/Poland
Odp: mój pierwszy programik
« Odpowiedź #12 dnia: 2018.08.06, 14:53:11 »
To może jeszcze takie coś:

set_vars
 ld a,(x1)
 add a,4
 ld (sb1),a
 sub 3
 ld (x1),a
 
 ld a,(x2)
 add a,7
 ld (sb2),a
 sub 5
 ld (x2),a
 
 ld a,(x3)
 add a,3
 ld (sb3),a
 sub 6
 ld (x3),a
 
 ld a,(x4)
 add a,1
 ld (sb4),a
 add a,4
 ld (x4),a
 ret

Reorganizacja kolejności oszczędza ci przy każdym wywołaniu cztery dostępy do pamięci. Też grosze, ale w niektórych sytuacjach może się opłacać ;)

gorgh

  • ***
  • Wiadomości: 128
  • Miejsce pobytu:
    Wyszków
Odp: mój pierwszy programik
« Odpowiedź #13 dnia: 2018.08.06, 15:01:33 »
faktycznie :)

matofesi

  • *****
  • Wiadomości: 2048
  • Miejsce pobytu:
    Toruń/Poland
Odp: mój pierwszy programik
« Odpowiedź #14 dnia: 2018.08.06, 15:42:01 »
I jeszcze jedna rzecz... Tym razem w procedurze rysującej. Najpierw sama procedura (od loop_draw):
loop_draw
 ld a,(hl)
 add a,a
 add a,a
 inc hl
 add a,(hl)
 add a,a
 inc hl

 push hl

 ld h,high pixels
 ld l,a

 push bc
 ld b,(hl)
 inc l
 ld c,(hl)

 ex de,hl
 
 ld (hl),b
 inc h
 ld (hl),c
 inc h
 ld (hl),c
 inc h
 ld (hl),b

 ex de,hl
 
 dec d
 dec d
 dec d

 pop bc
 
 pop hl

A do tego zmieniona tablica pikseli:
pixels
 db 0,0,0,6,9,6
 db 15,15,0,96,0,102
 db 9,102,15,111,144,96
 db 144,102,153,102,159,111
 db 240,240,240,246,249,246
 db 255,255

Modyfikacja najpierw zmienia liczenie przesunięcia w tablicy pikseli tak, żeby indeksować do tablicy dwu- a nie cztero- bajtowej (usunięcie jednego add a,a). Dalej zapamiętujemy na stosie BC a następnie dwa bajty piksela ładujemy do B i C. Zamieniamy HL z DE (bo nie da się wrzucić B lub C pod adres z DE) i ładujemy piksel z B i C zamiast czytać za każdym razem z pamięci. Dzięki temu dla każdego piksela czytasz dwa razy zamiast czterech i nie musisz dodatkowo inkrementować L przed kolejnymi odczytami. Czyli ścinamy jakieś dwadzieścia t-state'ów na piksel. Nadal cudów nie ma, ale jakaś poprawa chyba jest ;)