357 lines
9.0 KiB
Plaintext
357 lines
9.0 KiB
Plaintext
|
|
{-----------------------------------------------------------------------+
|
|
| |
|
|
| 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.
|
|
120!! \sysArgs5: C+ C=
|
|
|
|
{ multiply high-byte of A by high-byte of B, left-shift by 8 and add to C }
|
|
>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 |
|
|
+-----------------------------------------------------------------------}
|
|
|