361 lines
9.3 KiB
Plaintext
361 lines
9.3 KiB
Plaintext
|
|
{-----------------------------------------------------------------------+
|
|
| |
|
|
| 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<x<2
|
|
Hide the table in the top four lines of the screen }
|
|
\videoTop_v5 ptr= 8 ptr.
|
|
$800 SquareTable=
|
|
\SYS_LSRW1_48 \sysFn:
|
|
$1ff [do i= A= B= 1<< SquareTable+ ptr= SlowMulShift7! ptr: i 1- if>=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= <sign++]
|
|
0 B- [if>0 B= <sign++]
|
|
{Check range}
|
|
A B| \vACH, $fe&
|
|
[if<>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 |
|
|
+-----------------------------------------------------------------------}
|
|
|