[AVR][ASM] Mandelbrot bez float

W tym miejscu zadajemy pytania na temat języka Assembler, dzielimy się swoją wiedzą, udzielamy wsparcia, rozwiązujemy problemy programistyczne.
PeterB314
Posty: 7
Rejestracja: środa 20 mar 2019, 08:08

[AVR][ASM] Mandelbrot bez float

Postautor: PeterB314 » poniedziałek 29 kwie 2019, 21:31

Dzień dobry, to mój pierwszy wątek tu na forum.

Kupiwszy na odść znanym portalu aukcyjnym, przecenione i jak widać nieco wybrakowane (co w niczym nie wadzi dla celów edukacyjno rozwojowych) moduły LED P3 RGB 64x64 pix,
fot1.JPG


zbudowałem taki oto ledowizor 192x128 pix, RGB bez skali, czyli 7 barw + czarny (jak zauważył kolega, nawet potrafię diodę na czarno zaświecić, na co ja się zapytałem czy woli z wstępnym rozbłyskiem czy bez...).

fot2.JPG


Dawno temu już robiłem monochromatyczne tabliczki led i napisałem do nich soft na AVR M32 oczywiście w asemblerze, jako że prócz ( a tfuuu..) Basica, był to jeden z pierwszych języków który począłem zgłębiać będąc podrostkiem, a więc jest mi on bliższym niż koszulka z napisem C cośtam ;).

fot3.JPG


W ledowizorze zastosowałem M1284; 1 nibel na pix=12kB ram, wiem, że gubię 1bit na pixel, ale odświeżanie treści zajmuje 40% czasu, a gdybym jeszcze robił jakieś rotacje bajtów to wogóle nie dam rady tego przesłać do wyświetlaczy, nie ma tu PMP, DMA i innych gadgetów, dopasoawłem bios i porty do złącza HUB75 i ledowizor zaczął świecić, a po kilkunastu dniach debugowania, nawet poprawnie wyświetlać treści. Mój ledowizor sterowany jest ze standardowej klawiatury ze złączem PS2, do której napisanie hendlera (12 lat wstecz, też ASM) zajęło mi zylion godzin (nie udało mi się wtedy wygooglać gotowca).

fot 2a.JPG
fot4.jpg
fot 4a.JPG


Mandelbrot kusił mnie już przy małej ledownicy, ale wtedy myślałem: mono 32x64 to mało, pozatym te floaty, si, za skomplikowane...

Później, gdy czyniłem pierwsze kontakty z Pic32 na Mp-labie, dzięki Lucio DI Jasio, procek narysował Mandelbrota, no ale tam, float-y są na dzień dobry.

Kod: Zaznacz cały

// Z lucio Di Jasio Pic32 Pseudocode:
x = x0;
y = y0;
k = 0;
// core iteration
do {   x2 = x*x;
   y2 = y*y;
   y = 2*x*y+y0;
   x = x2-y2+x0;
   k++;
   } while ( (x2 + y2 < 4) & & ( k < MAXIT));
// check if the point belongs to the Mandelbrot set
   if ( k == MAXIT) plot( j, i);

// Mandelbrot Set graphic demo

// configuration bit settings, Fcy=72 MHz, Fpb=36 MHz
#pragma config POSCMOD=XT, FNOSC=PRIPLL
#pragma config FPLLIDIV=DIV_2, FPLLMUL=MUL_18, FPLLODIV=DIV_1
#pragma config FPBDIV=DIV_2, FWDTEN=OFF, CP=OFF, BWP=OFF
#include < p32xxxx.h >
#include < plib.h >
#include < explore.h >
#include < graphic.h >
#define SIZE VRES
#define MAXIT 64
void mandelbrot( float xx0, float yy0, float w)
   {
   float x, y, d, x0, y0, x2, y2;
   int i, j, k;
   // calculate increments
   d = w/SIZE;
   // repeat on each screen pixel
   y0 = yy0;
   for (i=0; i < SIZE; i++)
      {
      x0 = xx0;
      for (j=0; j < SIZE; j++)
         {
         // initialization
         x = x0;
         y = y0;
         k = 0;
         // core iteration
         do {
            x2 = x*x;
            y2 = y*y;
            y = 2*x*y + y0;
            x = x2-y2 + x0;
            k++;
         } while ( (x2 + y2 < 4) & & ( k < MAXIT));
         // check if the point belongs to the Mandelbrot set
         if ( k == MAXIT) plot( j, i); //if ( k & 2) plot( j, i);
         // compute next point x0
         x0 += d;
      } // for j
      // compute next y0
      y0 += d;
   } // for i
} // mandelbrot


Aż tu nagle kilka tygodni temu, net mi pokazał taki obrazek:

https://rbteam.wordpress.com/2011/09/11 ... andelbrot/

Przypomniałem sobie też, że kiedys gdzieś widziałem Mandela w Z80asm i był on na integerach.

Chwila rozmyślań i wniosek: przecież jeśli nie wymagać dokładności niezbędnej do zoomowania robala, to da się to zrobić na integerach traktując ich wartości stałopozycyjnie!

Założenie było takie: jedności ma rozdzielczość 6 bitów, czyli mając 192 punkty ekranu argument obliczeń mógł by przyjmować wartość od -2 do 2. Idąc dalej tą drogą wymyśliłem, że w takim razie za każdą iteracją kwadrat będzie przesuwał 'kropkę' o 6 bitów(albo o 12?), a więc aby być na bierząco w operacji porównania czy moduł |Z|>2 muszę też przesuwać wzorzec do porównań. Wyszło mi że dla maksymalnie 8 iteracji potrzebuję 48 bitowe słowa... Hmm zaczyna się robić z tego niezły makaron, ale nie martwiąc się co będzie dalej, przystąpiłem do kodowania mej świetlistej koncepcji.

Po wielu godzinach kombinowania, szukania błędów oraz dopatrzeniu się zasadniczej ułomności w samej koncepcji światłej mej, przełamałem się i zastosowałem google. No i znalazłem:

https://codegolf.stackexchange.com/ques ... 1719#61719

Na pierwszym miejscu 64bajtowy Mandelbrot w x86, bez float! Wprawdzie miał pisać w ASCII ale golfiści jak widać porażeni niejasnością kodu oraz jego wynikiem, przystali na złoty medal. DOSBOX i żuchwa leży na podłodze... działa, no ale jak?

fot5.png


Kolejna chwila (czytaj 60 minut) z dokładna listą instrukcji x86:
http://dsearls.org/courses/C391OrgSys/I ... n_set.html
doprowadziły do ujawnienia sprytu tego rozwiązania.

Edit: staram się tabularyzować wszystkie spacja, a nadal kolumny nie trzymają pinou...

Kod: Zaznacz cały

;===========================================================================
;
; "Microbrot" by Sir_Lagsalot

org 100h
   mov al,13h
   int 10h      ;VGA 320x200 @ 256 colors, ah domyślnie 0

   les ax,[bx]   ;do rej ES:AX załadowana zawartość pam [DS:BX] pointer 4 bajty
            ;ES segment Vram, zkąd ładowany? DI=0         
FillLoop:      
   cwd         ;if high bit of AX = 1 then DX = 0xFFFF else 0,
               ;ale można by użyć xor dx,dx? byłoby jaśniej
   mov ax,di      ;DI licznik bajtów=adres w Vram 1bajt na pix dla 256 colorów
   mov cx,320   ;CX = Maxiter oraz Xmax obrazu
   div cx      ;AX = (DX,AX)/CX; DX = remainder, czyli DX=xx AX=yy
   sub ax,100   ;yy od -100    przesunięcie układu współrzędnych
   dec dh      ;xx od -256   

   xor bx,bx      ;BX=reZ = 0; Z0=(0+i0)
   xor si,si      ;SI=imZ = 0

MandelLoop:
   mov bp,si      ;BP = imZ
   imul si,bx      ;(DX,SI) = SI * BX   SI = reZ * imZ
   add si,si      ;SI = 2 * reZ * imZ

   imul bx,bx   ;(DX,BX) = BX^2      BX = ReZ^2
   jo MandelBreak

   imul bp,bp   ;(DX,BP) = BP^2    BP = imZ^2
   jo MandelBreak

   add bx,bp      ;bx = ReZ^2 + imZ^2
   jo MandelBreak

   sub bx,bp      ;bx = reZ^2  bp = imZ^2
   sub bx,bp      ;bx = ReZ^2 - imZ^2

   sar bx,6      ;BX/64 signed
   add bx,dx      ;ReZ = BX/64 + xx
   sar si,6      ;SI/64 signed
   add si,ax      ;ImZ = SI/64 + yy

   loop MandelLoop;CX=CX-1, loop until <>0

MandelBreak:
   xchg ax,cx   ;AX (320-ilosc iteracji) = kolor, obcinany z najwyzszego bitu
   stosb      ;[ES:DI] = AL if DF = 0 then DI = DI + 1; czyli adres Vram
   jmp FillLoop

;DI-adres Vram, CX-Maxiter i Xmax, AX-imC=yy, DX-reC=xx, BX-reZ, SI-imZ, BP=t_imZ
;
;=========================================================================


Jak widać koncepcja podziału jedności na 64 i tu występuje. Sir_Lagsalot chyba lepiej ode mnie rozumie istotę systemów liczbowych i umie ją stosować...

Nie było co się dalej zastanawiać tylko czym prędzej przekodować to do AVR. Oczywiście nie obyło się bez kilku błędów, przeoczeń i literówek (kolejne 'chwile' wyjęte z pięknego życia), z których każda dawała śmieszne efekty. Choć z początku wcale nie dawała efektów bo albo było czarno albo system odjeżdżał z powodu rozjechania się stosu.

Podczas wielokrotnego czytania programu z monitora nie byłem w stanie zobaczyć ostatniego błędu, powodującego, że kolorowe plamy jakby chciały, ale z jakiegoś powodu nie potrafiły ułożyć zbioru M.

fot 8.JPG
fot6.JPG
fot7.JPG


Przypomniałem sobie wówczas jak w zamierzchłej przeszłości bawiłem się debugiem pod dosem, tam polecenie r wyświetlało zawartość rejestrów i takiego czegoś właśnie potrzebowałem tutaj. Tak więc dostosowana do potrzeby procka Print_registers zagościła w kodzie. Możliwość pracy krokowej zapewniona jest przez odpowiednie zmienne sterowane z klawiatury. Rejestr B pokazuje krok pętli.

https://youtu.be/f8bzZb-RzoA

Prośba do czepialińskich: ja wiem, że styl poniższego kodu jest mało czytelny... (edit poprawiłem), niżej zamieściłem wersje elegancką. Aha: piszę w slangu angielsko-polskim, jako, że w takim systemie mnemonicznym szybciej układam myśli.

Wersja debugowa i efekt jej pracy:

Kod: Zaznacz cały

;=========================================================================
;   M A N D E L B R O T   AVR asm bez float, code by PeterB314 (C) 2019.
;   pierwsza zdebugowana działa
;   inspiracja: http://www.pouet.net/prod.php?which=53287
;
;instrukcje z x86: Imul, Sar są zastąpione przez Mul_1616s oraz odpowiednią ilość Asr R23 Ror R22
;Mul_1616s jest na koncu następnego listingu.

Mandelbrot_regen:   .equ max_Mandel_Iter=8
   Clr R30         ;Y loop
Next_YL_Mandel:      ;sword R29:R28 = (yy - 64)
   Ldi YH,0      Mov YL,R30   
   Ldi A,64      Sub R28,A   
   Ldi A,0      Sbc R29,A

   Clr R31          ;X loop
Next_XL_Mandel:      ;sword R27:R26 = (xx - 128)
   Ldi XH,0      Mov XL,R31   
   Ldi A,128      Sub R26,A   
   Ldi A,0      Sbc R27,A

Push R30 Push R31      ;R30 R31 Używany do Mul_1616s
   
   Ldi A,max_Mandel_Iter    
   Clr R22   Clr R23
   Clr R24   Clr R25   ;Z(0) = (0+i0)
                                    Ldi B,0 call Print_registers
LooP_Mandel_Iter:          
Push R26 Push R27      ;xx
Push R24 Push R25       ;imZ
Push R22 Push R23      ;reZ   
                                    Ldi B,1 call Print_registers
   MovW R30,R22       
   MovW R26,R24
   Rcall MUL_1616s      ;R2322 = reZ * imZ
    Lsl R22 Rol R23      
    Movw R18,R22         ;R1918 = 2 * reZ * imZ [si]
                                    Ldi B,2 call Print_registers

Pop R31 Pop R30         
   MovW R26,R30         ;reZ
                                    Ldi B,3 call Print_registers

   Rcall MUL_1616s      ;[R25:R24:R23:R22]=Z[R31:R30] * X[R27:R26]

   MovW R20,R22         ; R2120 = A = reZ^2; [BX]
                                    Ldi B,4 call Print_registers
Pop R31 Pop R30         
   MovW R26,R30         ;imZ
                                    Ldi B,5 call Print_registers

   Rcall MUL_1616s      ;R2322 = B = imZ^2; [BP]   

                                    Ldi B,6 call Print_registers

   Add R20,R22   Adc R21,R23    
   Brsh Skip_003    Rjmp Break_Mandel_Iter
Skip_003:
                                    Ldi B,7 call Print_registers

   Sub R20,R22   Sbc R21,R23    
   Sub R20,R22   Sbc R21,R23
   Movw R22,R20         ;reZ = ReZ^2 - imZ^2
                                    Ldi B,8 call Print_registers    
POP R27 POP R26         ;XH:XL
                                    Ldi B,9 call Print_registers    
   Asr R23   Ror R22
   Asr R23   Ror R22
   Asr R23   Ror R22       
   Asr R23   Ror R22
   Asr R23   Ror R22
   Asr R23   Ror R22
                                    Ldi B,10 call Print_registers

   Add R22,R26   
   Adc R23,R27         ;ReZ = (ReZ^2 - imZ^2) / 64 + xx
   Movw R24,R18   
                                    Ldi B,11 call Print_registers

   Asr R25   Ror R24
   Asr R25   Ror R24
   Asr R25   Ror R24       
   Asr R25   Ror R24
   Asr R25   Ror R24
   Asr R25   Ror R24

                                    Ldi B,12 call Print_registers

   Add R24,R28   Adc R25,R29   ;ImZ = (2 * reZ * imZ) / 64 + yy
;   Add R24,R28   Adc R24,R29   = tu był główny błąd, daje ciekawe artefakty :)

                                    Ldi B,13 call Print_registers
   Dec A Tst A
   Breq End_Mandel_Iter
      Rjmp LooP_Mandel_Iter

Break_Mandel_Iter:   POP R27   POP R26   
End_Mandel_Iter:   POP R31   POP R30
                                    Ldi B,14 call Print_registers    
   Sts Pixel_put_color,A
   Mov XL,R31
Push YL
   Mov YL,R30
   SET
   Call Set_Point_XY
POP YL            ; był brak ochrony YL !
                                    Ldi B,15 call Print_registers
   Lds A,Cur_edit_cmd
    Cpi A,0x40      Breq Exit_Mandel

   Lds A,Mt_X   Inc R31   
   Cp R31,A      Brsh Next_Mandel_line   
                  Rjmp Next_XL_Mandel
Next_Mandel_line:
   Lds A,Mt_Y   Inc R30   
   Cp R30,A      Brsh Exit_Mandel   
                  Rjmp Next_YL_Mandel
Exit_Mandel:   Store Pixel_put_color,4
RET   
;
;==========================================================================


fot 9.JPG


Poniżej wersja elegancka z wycofanym wyświetlaniem rejestrów oraz szeregiem zmian porządkowych; jest inna dyspozycja rejestrów dla wyników tymczasowych, gdyż w toku dalszej rozbudowy, potrzebuję R18,19,20,21 do sterowania powiększaniem i innych efektów.

Kod: Zaznacz cały

;=========================================================================
;
;   M A N D E L B R O T   AVR asm bez float, code by PeterB314 (C) 2019.
;   inspiracja: http://www.pouet.net/prod.php?which=53287
;
/*    dla skrócenia listingu, czasem kilka instrukcji w jednym wierszu jeśli są logicznie powiązane

Funkcje zewnętrzne: Set_Point_XY, zaświeca pixel (XL,YL) w kolorze [Pixel_put_color]

Zmienne zewnętrzne: [Cur_edit_cmd] - kod ostatnio wydanego polecenia z klawiatury
          [Mt_X],[Mt_Y] - rozmiar wyświetlacza   

   Z(n+1) = Z(n)^2 + C; Z(0)=(0+i0), dla C należcego do płaszczyzny Z (pole ekranu)
*/

   .equ Init_max_Iter=32

Mandelbrot_regen:
   Clr R30         ;licznik współrzęna ekranowa Y

Next_YL_Mandel:   
   Ldi YH,0
   Mov YL,R30       ;YHYL - zmienna urojona imC
   Ldi A,64        ;czyli yy=-1
   Sub R28,A
   Ldi A,0x00
   Sbc R29,A            //   sword R29:R28 = (yy - 64)

   Clr R31       ;licznik współrzęna ekranowa X

Next_XL_Mandel:   
   Ldi XH,0
   Mov XL,R31        ;XHXL - zmienna rzeczywista reC
   Ldi A,128       ;czyli xx=-2
   Sub R26,A
   Ldi A,0x00
   Sbc R27,A            //   sword R27:R26 = (xx - 128)

Push R30 Push R31   // wskażnik piksela XY, R30 R31 Używany do Mul_1616s
   
   Ldi A,Init_max_Iter    ;A alias dla R16
   movw R22,R26      ;Ponieważ pierwsza iteracja dla Z(0)=(0+i0) zakonczy się wynikiem (xx+iyy)
   movw R24,R28       ;to odrazu Z(0) = (xx+iyy), zmniejszy to ilość iteracji o 1
            ;a nadto pozwala to na łatwe przejście do Julia set
            ;wtedy dalej, zamiast dodawać xx i yy należy dodać J(re+im)    
LooP_Mandel_Iter:       
;Jeśli w tym miejscu wykonać reZ=|reZ| oraz imZ=|imZ| uzyskamy obraz fraktala "Burning Ship"
               // yy udało się zachować nienaruszany w pętli
Push R26 Push R27          // xx
Push R24 Push R25          // imZ
Push R22 Push R23         // reZ

   MovW R30,R22             // reZ * imZ
   MovW R26,R24   
   Rcall MUL_1616s            ;[R25:R24:R23:R22]=Z[R31:R30] * X[R27:R26]         
   
;o 1 mniej, gdyż tu reZ*imZ nie jest mnożone przez 2 skoro zaraz ma być dzielone przrz 64
   Asr R23 ror R22
   Asr R23 ror R22
   Asr R23 ror R22
   Asr R23 ror R22
   Asr R23 ror R22
   Movw R12,R22            // R1213 = (reZ * imZ)/64 [si]   

Pop R31 Pop R30               // reZ   

   MovW R26,R30
   Rcall MUL_1616s   
   
;tu powinno być sprawdzenie Overflow tak jak w wersji z x86 ale brak flagi w procku oraz brak czasu na
; napisanie odpowiedniego kodu wykrywającego, spowodował że nie ma tego testu w tym miejscu, jak i poniżej.

   MovW R14,R22            // R1415 = A = reZ^2; [BX]
   
Pop R31 Pop R30               // imZ      

   MovW R26,R30
   Rcall MUL_1616s            // R2322 = B = imZ^2; [BP]

   Add R14,R22 Adc R15,R23       // break If (A+B) > 0xFFFF
      Brlo Break_Mandel_Iter

   Sub R14,R22 Sbc R15,R23    
   Sub R14,R22 Sbc R15,R23       //reZ = reZ^2 - imZ^2

;jeden Asr więcej powiększa, mniej oddala, trzeba też dostosować maxiter i ilość Lsr A koloru
   Movw R22,R14       
   Asr R23 ror R22
   Asr R23 ror R22
   Asr R23 ror R22       
   Asr R23 ror R22
   Asr R23 ror R22
   Asr R23 ror R22

POP R27 POP R26           // xx

   Add R22,R26 Adc R23,R27         //ReZ = (ReZ^2 - imZ^2) / 64 + xx
                  ;dla Julia set zamiast xx dodać reJ
   Movw R24,R12
   Add R24,R28 Adc R25,R29         //ImZ = (2 * reZ * imZ) / 64 + yy
                  ;dla Julia set zamiast yy dodać imJ
   Dec A
   Tst A
      Breq End_Mandel_Iter
   Rjmp LooP_Mandel_Iter   ; w trakcie testowania czasem przekraczało 63 instrukcje

Break_Mandel_Iter:

   POP R27 POP R26      // xx był zrzucony na początku pętli

End_Mandel_Iter:

   POP R31 POP R30      // liczniki współrzędnych ekranu

   lsr A  Lsr A        ; dopasowanie koloru Max_color=7            
   Sts Pixel_put_color,A   ; no chyba samokomentująca się? ;)
   mov XL,R31
push yl          ;ochrona lokalnego yy
   mov YL,R30
   SET
   call Set_Point_XY    ;niszczy YL
pop yl
   Lds A,Cur_edit_cmd
   Cpi A,0x40       ;Shift+Esc = przerwij i wyjdź
      Breq Exit_Mandel

   Lds A,Mt_X   
   Inc R31   
   Cp R31,A   
      Brsh Next_Mandel_line   
         Rjmp Next_XL_Mandel

Next_Mandel_line:
   Lds A,Mt_Y   
   Inc R30   
   Cp R30,A   
      Brsh Exit_Mandel      
         Rjmp Next_YL_Mandel
Exit_Mandel:   
   Store Pixel_put_color,4      ;macro
RET   

;==========================================================================
;   USAGE:   [R25:R24:R23:R22]=Z[R31:R30] * X[R27:R26]
;   DECRIPTION: Signed multiply of two 16bits numbers with 32bits result.
;   STATISTICS:   Cycles :31 + ret   Words :   23 + ret
;   Based on avr201.asm with different register usage.
Mul_1616s:   Push A    Push B 
   Clr R2   
   muls    r31, r27   
   movw    r25:r24, r1:r0

   mul    r30, r26   
   movw    r23:r22, r1:r0   

   Mov    A,R31    
   Mov    B,R26
   mulsu    A,B      
   sbc   r25, r2   
   add   r23, r0   
   adc   r24, r1   
   adc   r25, r2

   Mov    A,R27       
   Mov    B,R30   
   mulsu    A,B      
   sbc   r25, r2   
   add   r23, r0   
   adc   r24, r1   
   adc   r25, r2
Pop B    Pop A   RET   
;
;   Integer Mandelbrot End
;
;===========================================================================


fot 10.JPG


Następnie zwiększyłem Max_iter do 64 i zmieniłem kolorowanie,

fot 11.JPG


oraz powiększanie zwiększając ilość rotacji w prawo.

fot 12.JPG


W porównaniu do wersji dosowej, zostaje czarna linia pozioma dla xx=0, jeszcze nie znalazłem przyczyny.

Ciekawy materiał źródłowy - coding chelange:

https://www.youtube.com/watch?v=6z7GQewK-Ks

Na zakonczenie totalne szaleństwo obliczeniowe:

https://www.youtube.com/watch?v=u1pwtSBTnPU

Dziękuję i pozdrawiam!
Edit: w listingu powyżej odpisałem uwagę o możliwości uzyskania obrazu fraktala "Burning Ship".
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
Ostatnio zmieniony piątek 10 maja 2019, 19:02 przez PeterB314, łącznie zmieniany 6 razy.
Głównie AVR8 ASM, Próbowałem PIC32 ale C mi jakoś nie leży...

Awatar użytkownika
wojtek
Geek
Geek
Posty: 1996
Rejestracja: piątek 04 wrz 2015, 09:03

Re: [AVR][ASM] Mandelbrot bez float

Postautor: wojtek » wtorek 30 kwie 2019, 05:52

No niezłe, mówię o efekcie :)
73 Wojtek

Awatar użytkownika
piotrek
Newb
Newb
Posty: 94
Rejestracja: niedziela 05 lis 2017, 02:46

Re: [AVR][ASM] Mandelbrot bez float

Postautor: piotrek » wtorek 30 kwie 2019, 10:07

Moim zdaniem ten wyświetlacz kompletnie się nie nadaje do pokazywanie zbioru Mandelbrota. Jego mała rozdzielczość nie pozwala na ukazanie "fraktalności", którą powinniśmy dostrzec w najmniejszych detalach. Znacznie bardziej nadaje się natomiast do ukazania pracujących automatów komórkowych, np game of life lub mrówki Langtona. Coś takiego jak na zdjęciu 4a.
Tak czy inaczej, ten projekt to ciekawe wyzwanie programistyczne.

Awatar użytkownika
mokrowski
User
User
Posty: 188
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: [AVR][ASM] Mandelbrot bez float

Postautor: mokrowski » wtorek 30 kwie 2019, 11:50

Punktem zaczepienia dla takich zabaw, może być program Fractint. W czasach DOS'a, wykonywał on obliczenia fraktali bez przecinka. W GNU/Linux jest także przeportowany od dawna. Zwie się xfractint.

[Dodame]
Witryna projektu: https://fractint.org/
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

PeterB314
Posty: 7
Rejestracja: środa 20 mar 2019, 08:08

Re: [AVR][ASM] Mandelbrot bez float

Postautor: PeterB314 » czwartek 02 maja 2019, 21:01

Wersja przyspieszona:
- przyporządkowanie rejestrów takie aby całkowicie wyeliminować operacje na stosie,
- rezygnacja z wywoływania procedury mnożenia,
- ponieważ do obliczeń brane jest tylko młodsze słowo wyniku mnożenia, liczymy tylko to co potrzebne
Czas wykonania spadł z 7,5s do 2,5s. :D

Kod: Zaznacz cały

Mandelbrot_Fast:
;Ze względu na działanie instrukcji Mulsu tylko w zakresie rejestrów 16-23
;zmienne ułożone są jak widać poniżej:

.def reL=R18 .def reH=R19 .def imL=R20 .def imH=R21 .def timL=R22 .def timH=R23

   .equ max_Mandel_Iter=32
   
   Clr R30         ;Y loop
Next_YL_Mandel_F:      ;sword R29:R28 = (yy - 64)
   Ldi YH,0   Mov YL,R30
   Ldi A,64   Sub R28,A
   Ldi A,0   Sbc R29,A
   
   Clr R31         ;X loop
Next_XL_Mandel_F:      ;sword R27:R26 = (xx - 128)
   Ldi XH,0   Mov XL,R31
   Ldi A,128   Sub R26,A
   Ldi A,0   Sbc R27,A
   
Ldi A,max_Mandel_Iter

Movw reL,R26    Movw imL,R28      ; Z(0)=Z(1)=(xx,yy)    

LooP_Mandel_Iter_F:

Movw timL,imL                     ;MOV BP SI      tim im


Mul   reL,imL   Movw R2,R0       ;MUL SI BX      im  re
Mulsu reH,imL   Add  R3,R0      
Mulsu imH,reL   Add  R3,R0      
Movw  imL,R2
                                 ;SAR SI 5      im  5
Asr imH Ror imL   Asr imH Ror imL   Asr imH Ror imL   
Asr imH Ror imL   Asr imH Ror imL   ;Asr imH Ror reH
                  
Add imL,R28    Adc imH,R29            ;ADD SI AX      im  yy
                     
Mul reL,reL    Movw  R2,R0          ;MUL BX BX      re  re
Mulsu reH,reL   Add R3,R0   
Add R3,R0      Movw reL,R2
                     

Mul timL,timL   Movw  R2,R0          ;MUL BP BP      tim tim
Mulsu timH,timL   Add R3,R0      
Add R3,R0   Movw timL,R2
                     
Add   reL,timL   Adc  reH,timH         ;ADD BX BP      re  tim
                  
Brlo Mandel_Break_F                  ;JO Break
                     
Sub   reL,timL   Sbc  reH,timH         ;SUB BX BP      re  tim
                     
Sub   reL,timL   Sbc  reH,timH         ;SUB BX BP      re  tim

                                       ;SAR BX 6      re   6
Asr reH Ror reL      Asr reH Ror reL      Asr reH Ror reL   
Asr reH Ror reL      Asr reH Ror reL      Asr reH Ror reL
   
Add reL,R26   Adc reH,R27         ;ADD BX DX      re  xx

   Dec A Tst A
      Breq Mandel_Break_F
         Rjmp LooP_Mandel_Iter_F
Mandel_Break_F:
   Lsr A Lsr A
   Sts Pixel_put_color,A   
   mov XL,R31
Push YL
   mov YL,R30
   SET                   ;zaświeć punkt, CLT 'gasi'
   Call Set_Point_XY
Pop YL
                                    
   Lds A,Mt_X   Inc R31   
      Cp R31,A   Brsh Next_Mandel_line_F
         Rjmp Next_XL_Mandel_F
         
Next_Mandel_line_F:
   Lds A,Mt_Y   Inc R30   
      Cp R30,A   Brsh Exit_Mandel_F      
         Rjmp Next_YL_Mandel_F
         
Exit_Mandel_F:   Store Pixel_put_color,7

RET   ;   Mandel_Fast   

Edytowany: drobne poprawki tabulatorskie.
Głównie AVR8 ASM, Próbowałem PIC32 ale C mi jakoś nie leży...

PeterB314
Posty: 7
Rejestracja: środa 20 mar 2019, 08:08

Re: [AVR][ASM] Mandelbrot bez float

Postautor: PeterB314 » niedziela 26 maja 2019, 11:01

Dopisałem trochę kodu i teraz mam wytwornice serwetek ...

https://youtu.be/gbHt2Z4YkqE
Wyświetlacz po lewej : 192 x 128 pix, 6 modułów 64x64/32Scan line, 2xHUB75E, odświeżanie 87Hz, Atmega 1284@20MHz
Wyświetlacz po prawej: 96 x 96 pix, 9 modułów 32x32/ 8 Scan line , 3 x HUB75C, odświeżanie 112Hz, Atmega 1284@ 8MHz
Intensywność świecenia około 20-25%. Pobór prądu przy tym wypełnieniu, około 0,8A na 1 moduł przy wszystkich punktach na biało.

https://youtu.be/JSB0lNVCKb8
Tu lewy pionowo

Powoli szykuję się do zwiększenia formatu i zacznę się uczyć STM32, chyba, że polecicie inną rodzinę ..?
Rpi ma za mało pinów, można wprawdzie multipleksować, ale wolę więcej gpio.

Jeszcze eksperyment z rekurencją i stos jedzie w dół:
https://youtu.be/hbGnyKvSC8w
Głównie AVR8 ASM, Próbowałem PIC32 ale C mi jakoś nie leży...

PeterB314
Posty: 7
Rejestracja: środa 20 mar 2019, 08:08

Re: [AVR][ASM] Mandelbrot bez float

Postautor: PeterB314 » sobota 08 cze 2019, 10:12

Nihil novi sub sole, tak mógłbym zamknąć ten wątek po zapoznaniu się z przywołanym przez kol. Mokrowski tematem fractint, którego wcześniej nie poznałem. Problematyka liczenia na maszynie bez liczb rzeczywistych została obszernie tam wyjaśniona.
Niby wyważanie otwartych drzwi a jednak jakieś wyzwanie do zakodowania. Zgadzając się z opinią kol. Piotrek, dopowiem, że podstawowym zamiarem było tylko wyświetlenie całości, podobnie jak w przywołanym przykładzie tekstowym.

Obrazek

A wyszło jak zwykle co innego i z tego się cieszę.

Mrówka Langtona, zwłaszcza z możliwością zmieniania reguł to dobre wyzwanie :)
Głównie AVR8 ASM, Próbowałem PIC32 ale C mi jakoś nie leży...


Wróć do „Pisanie programów w Assembler”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość