{-----------------------------------------------------------------------+ | | | Mandelbrot fractal | | | +-----------------------------------------------------------------------} gcl0x { Plot the Mandelbrot set - 160x120 pixels and 64 colors [updated 160x118 pixels!] - Faithful translation of mandelbrot.c pre-study - Use 16-bit vCPU math as 7-bit fixed point arithmetic (1.00 -> 128) - Implement multiplication in interpreter - Show settable clock, with blinking colon as long as unset Ideas for after ROMv1: XXX Random color offset for each new rendering XXX Lookup table for intensity mapping XXX Refactor pixel order from what to do with it XXX Double vertical resolution by exploiting vertical symmetry } { Slow unsigned multiply shift. Arguments are 3+7 fixed point numbers. Need to set sysFn to SYS_LSRW1_48 before calling this } [def B 2<< B= 0 C= 1 bit= [do bit A& [if<>0 B] C+ 48!! C= bit 1<< bit= \vACH, 2^ if<>0loop] >A, $fe& [if<>0 B] C+ C= ret ] SlowMulShift7= { Compute a table of squares for -2=0loop] { Multiply shift with fast mode Need to set sysFn to SYS_LSRW1_48 before calling this } [def {Extract sign and absolute values} 0 sign= A- [if>0 A= 0 B= 0 push SlowMulShift7! pop else A 1<< SquareTable+ deek C= {C now contains A^2} A B- [if<0 B A-] 1<< SquareTable+ deek A= {A now contains (A-B)^2} B 1<< SquareTable+ deek C+ A- 48!! C= {C now contains (A^2+B^2-(A-B)^2)/2} ] {Apply sign to return value} sign 1& [if=0 C ret] 0 C- ret ] MulShift7= { Square-shift with fast mode } [def 0 A- [if>0 A=] >A, $fe& [if<>0 push A B= SlowMulShift7! pop ret ] A 1<< SquareTable+ deek C= ret ] SqrShift7= {-----------------------------------------------------------------------+ |} $0300 \vLR: [ret]{ RAM page 3 | +-----------------------------------------------------------------------} $0300: [def {CalcSet} push $C00 Pen= {Start of video} 116 Height= 160 Width= len= LastPixel= {Anything well-defined} Step 1<< Y0+ Y0= 0 DY= Step DX= [do {Draw white pixel while busy here} 63 Pen. {Update clock} UpdateClock! {Reset sysFn to right shift} \SYS_LSRW1_48 \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= SqrShift7! YY= X0 128+ A= SqrShift7! YY+ 8- [if<0 0 else {q*(q + x - 1/4) < 1/4*y^2, where q = (x - 1/4)^2 + y^2} X0 32- A= SqrShift7! YY+ {q} A= X0+ 32- B= MulShift7! 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 $C00 Pen= 116 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} X Y- A= SqrShift7! Y= {Y=(X-Y)^2} XX YY+ Y- Y0+ Y= {Y=X2+Y2-(X-Y)^2+Y0} XX YY- X0+ X= {X =X2-Y2+X0} {Calculate squares} {X}A= SqrShift7! XX= Y A= SqrShift7! YY= -$200 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 -180 Y0= -320 X0= 3 Step= CalcSet! {Global 1127.446 seconds} {#$b4 #$80} -148 Y0= -98 X0= 1 Step= CalcSet! {Zoom1 2125.648 seconds} -60 Y0= -256 X0= 1 Step= CalcSet! {Zoom2 2050.341 seconds} -320 Y0= -360 X0= 9 Step= CalcSet! {Wide 356.482 seconds} 0 Y0= -100 X0= 1 Step= CalcSet! {Zoom3 2337.633 seconds} -60 Y0= 0 X0= 1 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 | +-----------------------------------------------------------------------}