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                                                             |
 | 
						|
+-----------------------------------------------------------------------}
 | 
						|
 |