{-----------------------------------------------------------------------+ | | | Mandelbrot fractal | | | +-----------------------------------------------------------------------} gcl0x { Plot the Mandelbrot set Originally by Marcel. This version is adapted to use SYS_MultiplyBytes_120 In order to make the multiplication as fast as possible we use 8 fraction bits instead of 7. } { multiply-shift ((A*B)>>8) } { Uses Karatsuba's algorithm, which only computes three partial products, the product of the two high-bytes, the product of the two low-bytes, and a product that turns out to be (in our case) high-of-A * low-of-B + low-of-A * high-of-B - high-of-A * high-of-B - low-of-A * low-of-B In other words, we can produce high-of-A * low-of-B + low-of-A * high-of-B (the sum of the two partial products we didn't calculate) by adding on the products we do. Nice. The setup to find the multiplicands needed is a bit fiddly, and would be slow in vCPU, so we use a SYS function to do it. This leaves sysFn pointing to SYS_MultiplyBytes_120, so the whole thing is easy. } [def {Extract sign and absolute values} 0 sign= {0}A- [if>0 A= 1 sign=] 0 B- [if>0 B= sign 1^ sign=] { Karatsuba multiplication } \SYS_KaratsubaPrepare_54 _sysFn= A _sysArgs0= B 54!! [if=0 120!! C= else 120!! C= 0 C- C=] { Find product of low bytes } A, \sysArgs0. >B, \sysArgs1. 98!! \sysArgs7. C+ _sysArgs6+ C= {Apply sign to return value } sign [if<>0 0 C- else C] ret ] MulShift8= {-----------------------------------------------------------------------+ |}\vLR>++ ret{ RAM page 3 | +-----------------------------------------------------------------------} $0300: [def {CalcSet} push $800 Pen= {Start of video} 120 Height= 160 Width= len= LastPixel= {Anything well-defined} 0 DY= Step DX= [do {Draw white pixel while busy here} 63 Pen. {Update clock} UpdateClock! \SYS_MultiplyBytes_120 \sysFn: LastPixel [if=0 {Check if we are inside one of the main bulbs for a quick bailout (Wikipedia) (x+1)^2 + y^2 < 1/16} Y0 A= B= MulShift8! YY= 256 X0+ A= B= MulShift8! YY+ 16- [if<0 0 else {q*(q + x - 1/4) < 1/4*y^2, where q = (x - 1/4)^2 + y^2} X0 64- A= B= MulShift8! YY+ {q} A= X0+ 64- B= MulShift8! tmp= tmp+ tmp= tmp+ tmp= {*4} YY- [if<0 0 else {Otherwise run the escape time algorithm} CalcPixel! ] ] else {No shortcut} CalcPixel! ] LastPixel= Pen. {Plot pixel} len 1- [if<=0 {Turn right} DY tmp= DX DY= 0 tmp- DX= {Length of next segment, either horizontal or vertical} DX [if<>0 Width 1- Width= else Height 1- Height=] ] {Break when reaching a zero-length segment} len= if>0 {Step in the fractal plane} X0 DX+ X0= Y0 DY+ Y0= {Matching step in video frame} DX [if<0 Pen 1- Pen=] DX [if>0 Pen 1+ Pen=] DY [if<0 -$100 Pen+ Pen=] DY [if>0 $100 Pen+ Pen=] loop] {60 \soundTimer. {For debugging}} GrayOut! pop ret ] CalcSet= {-----------------------------------------------------------------------+ |}\vLR>++ ret{ RAM page 4 | +-----------------------------------------------------------------------} $0400: [def {GrayOut} push $800 Pen= 120 Height= 160 Width= len= 1 DX= 0 DY= [do \SYS_LSRW1_48 \sysFn: 0 Level= 32 [do i= Pen, 63& i- [if>=0 Pen. i 21& [if<>0 1 else 2] Level+ Level=] i 48!! {ShiftRight} if>0loop] {Level in 0..9} Pen. {Paint it black} Level [if<>0 {Level in 1..9} {Ordered dithering} Pen>, 1& i= i+ i= {2 * bit 0 of y} Pen 1& i+ i= {+ bit 0 of x} [def 0# 2# {Bayer matrix (Wikipedia)} 3# 1#] i+ peek Level+ 1- Level= {Level in 0..11} {Map intensity level to actual gray color (multiple of 1+4+16)} [do Level 3- if>=0 Level= Pen, 21+ Pen. loop] ] {Advance to next pixel} len 1- [if<=0 {Turn right} DY tmp= DX DY= 0 tmp- DX= {Length of next segment, either horizontal or vertical} DX [if<>0 Width 1- Width= else Height 1- Height=] ] {Break when reaching a zero-length segment} len= if>0 {Matching step in video frame} DX [if<0 Pen 1- Pen=] DX [if>0 Pen 1+ Pen=] DY [if<0 -$100 Pen+ Pen=] DY [if>0 $100 Pen+ Pen=] {Update clock} UpdateClock! loop] pop ret ] GrayOut= $201a Separator= {High: separator character, Low: XOR when blinking} {-----------------------------------------------------------------------+ |}\vLR>++ ret{ RAM page 5 | +-----------------------------------------------------------------------} $0500: {Update 24-hour clock} [def \frameCount, LastFrame- 255& Elapsed= 60- [if<0 \buttonState, 128& [if<>0 ret] 15 {Yellow} else 63 {White}] Color= {1 second has elapsed, or button A was pressed} push Clock0 Elapsed+ Clock0= LastFrame Elapsed+ LastFrame= -3599 Clock0+ [if>=0 Clock0= 1 AddMinutes! ] {Change separator between on/off ($3a/$20)} Separator>, Separator^ Separator>. {Check if user is adjusting the time} \buttonState, 119^ {A+Up} [if=0 Clock0= +1 else 12^ {A+Down} [if=0 Clock0= -1 else 6^ {A+Left} [if=0 Clock0= -60 else 3^ {A+Right} [if=0 Clock0= +60 else 0]]]] [if<>0 AddMinutes! {Adjust minutes/hours clock} 0 Clock0= {Reset seconds/subseconds clock} $3a00 Separator= {Stop blinking once adjusted} ] \buttonState, 15| \buttonState. {Reset arrow presses, but not the others} {Convert to HH:MM:SS and draw on screen} $445c Pos= Clock1 Value= 600 NextDigit! {10-hour digit} 60 NextDigit! { 1-hour digit} Separator>, PrintChar! {Colon or space} 10 NextDigit! {10-minute digit} 1 NextDigit! { 1-minute digit} pop ret ] UpdateClock= [def {PrintChar} 32- i= \font32up fontData= {All of '0'-'9' and ':' are in the first page} i 2<< i+ {Multiply by 5} fontData+ fontData= {Add to page address to reach bitmap data} $800 Pos+ q= {Where to stop the inner drawing loop} {Draw 5 vertical slices: 5 using font data} 0 \sysArgs0. {Black} Color \sysArgs1. Pos \sysArgs4: 6+ Pos= \SYS_VDrawBits_134 \sysFn: $fb i= [do fontData 0? fontData<++ \sysArgs2. 134!! \sysArgs4<++ i<++ i if<>0loop] ret ] PrintChar= {-----------------------------------------------------------------------+ |}\vLR>++ ret{ RAM page 6 | +-----------------------------------------------------------------------} $0600: [def {NextDigit -- Value Radix} push Radix= $30 Digit= Value Radix- [if>=0 [do Value= Digit<++ Radix- if>=0loop] ] Digit PrintChar! pop ret ] NextDigit= [def {AddMinutes} Clock1+ Clock1= [if<0 1440 Clock1+ Clock1=] -1440 Clock1+ [if>=0 Clock1=] ret ] AddMinutes= { Calculate color for (X0,Y0) using the escape time algorithm } [def {CalcPixel} push 0 X= XX= Y= YY= i= [do i 1+ i= 64^ if<>0 {Break after completing 63 iterations} {Mandelbrot function: z' := z^2 + c} X A= Y Y+ B= MulShift8! Y0+ Y= {Y = 2*X*Y + Y0} XX YY- X0+ X= {X = X^2 - Y^2 + X0} {Calculate squares} {X}A= B= MulShift8! XX= Y A= B= MulShift8! YY= -$400 XX+ YY+ if<=0loop {else} i {Also break when X^2 + Y^2 > 4} ] pop ret ] CalcPixel= { Main } 0 Clock0= Clock1= {Frames and minutes} Digit= \frameCount, LastFrame= [do -360 Y0= -640 X0= 6 Step= CalcSet! {Global 1127.446 seconds} -296 Y0= -196 X0= 2 Step= CalcSet! {Zoom1 2125.648 seconds} -120 Y0= -512 X0= 2 Step= CalcSet! {Zoom2 2050.341 seconds} -640 Y0= -720 X0= 18 Step= CalcSet! {Wide 356.482 seconds} 0 Y0= -200 X0= 2 Step= CalcSet! {Zoom3 2337.633 seconds} -120 Y0= 0 X0= 2 Step= CalcSet! {Zoom4 1409.569 seconds} loop] { Benchmark settings: + $1c10 p= {Start of video} + 96 Height= 128 Width= len= + i 1+ i= 27^ if<>0 {Break after completing 26 iterations} + -144 Y0= -256 X0= 3 Step= CalcSet! {Global 736.586 seconds} } {-----------------------------------------------------------------------+ | End | +-----------------------------------------------------------------------}