diff --git a/mandelbrot-fixed.mu b/mandelbrot-fixed.mu index 6d961055..9cd8a793 100644 --- a/mandelbrot-fixed.mu +++ b/mandelbrot-fixed.mu @@ -17,8 +17,17 @@ fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) # Initially the viewport shows a section of the scene 4 units wide. var scene-width-f: int copy-to scene-width-f, 0x400/4 - var tmp-f/eax: int <- copy 0 - mandelbrot screen, scene-cx-f, scene-cy-f, scene-width-f + { + mandelbrot screen scene-cx-f, scene-cy-f, scene-width-f + # move at an angle slowly towards the edge + var adj-f/eax: int <- multiply-fixed scene-width-f, 0x12/0.07 + subtract-from scene-cx-f, adj-f + add-to scene-cy-f, adj-f + # slowly shrink the scene width to zoom in + var tmp-f/eax: int <- multiply-fixed scene-width-f, 0x80/0.5 + copy-to scene-width-f, tmp-f + loop + } } # Since they still look like int types, we'll append a '-f' suffix to variable @@ -127,7 +136,7 @@ fn mandelbrot screen: (addr screen), scene-cx-f: int, scene-cy-f: int, scene-wid { compare y, height break-if->= - var imaginary-f/ebx: int <- viewport-to-imaginary-f y, width, height, scene-cx-f, scene-width-f + var imaginary-f/ebx: int <- viewport-to-imaginary-f y, width, height, scene-cy-f, scene-width-f var x/eax: int <- copy 0 { compare x, width diff --git a/mandelbrot.mu b/mandelbrot.mu index fa1abaff..ac37901b 100644 --- a/mandelbrot.mu +++ b/mandelbrot.mu @@ -11,10 +11,34 @@ # $ qemu-system-i386 code.img fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { - mandelbrot screen + # Initially the viewport is centered at 0, 0 in the scene. + var zero: float + var scene-cx/xmm1: float <- copy zero + var scene-cy/xmm2: float <- copy zero + # Initially the viewport shows a section of the scene 4 units wide. + # scene-width-scale = 0.5 + var scene-width-scale: float + var dest/eax: (addr float) <- address scene-width-scale + fill-in-rational dest, 1, 2 + # scene-width = 4 + var four: float + var dest/eax: (addr float) <- address four + fill-in-rational dest, 4, 1 + var scene-width/xmm3: float <- copy four + { + mandelbrot screen scene-cx, scene-cy, scene-width + # move the center some % of the current screen-width + var adj/xmm0: float <- rational 2, 0x1c/28 + adj <- multiply scene-width + scene-cx <- subtract adj + scene-cy <- add adj + # slowly shrink the scene width to zoom in + scene-width <- multiply scene-width-scale + loop + } } -fn mandelbrot screen: (addr screen) { +fn mandelbrot screen: (addr screen), scene-cx: float, scene-cy: float, scene-width: float { var a/eax: int <- copy 0 var b/ecx: int <- copy 0 a, b <- screen-size screen @@ -26,12 +50,12 @@ fn mandelbrot screen: (addr screen) { { compare y, height break-if->= - var imaginary/xmm1: float <- viewport-to-imaginary y, width, height + var imaginary/xmm1: float <- viewport-to-imaginary y, width, height, scene-cy, scene-width var x/edx: int <- copy 0 { compare x, width break-if->= - var real/xmm0: float <- viewport-to-real x, width + var real/xmm0: float <- viewport-to-real x, width, scene-cx, scene-width var iterations/eax: int <- mandelbrot-iterations-for-point real, imaginary, 0x400/max compare iterations, 0x400/max { @@ -116,35 +140,45 @@ fn mandelbrot-y x: float, y: float, imaginary: float -> _/xmm3: float { # ranges from -2 to +2. Viewport height just follows the viewport's aspect # ratio. -fn viewport-to-real x: int, width: int -> _/xmm0: float { - # (x - width/2)*4/width +fn viewport-to-real x: int, width: int, scene-cx: float, scene-width: float -> _/xmm0: float { + # 0 in the viewport goes to scene-cx - scene-width/2 + # width in the viewport goes to scene-cx + scene-width/2 + # Therefore: + # x in the viewport goes to (scene-cx - scene-width/2) + x*scene-width/width + # At most two numbers being multiplied before a divide, so no risk of overflow. var result/xmm0: float <- convert x + result <- multiply scene-width var width-f/xmm1: float <- convert width + result <- divide width-f + result <- add scene-cx var two/eax: int <- copy 2 var two-f/xmm2: float <- convert two - var half-width-f/xmm2: float <- reciprocal two-f - half-width-f <- multiply width-f - result <- subtract half-width-f - var four/eax: int <- copy 4 - var four-f/xmm2: float <- convert four - result <- multiply four-f - result <- divide width-f + var half-scene-width/xmm1: float <- copy scene-width + half-scene-width <- divide two-f + result <- subtract half-scene-width return result } -fn viewport-to-imaginary y: int, width: int, height: int -> _/xmm1: float { - # (y - height/2)*4/width +fn viewport-to-imaginary y: int, width: int, height: int, scene-cy: float, scene-width: float -> _/xmm1: float { + # 0 in the viewport goes to scene-cy - scene-width/2*height/width + # height in the viewport goes to scene-cy + scene-width/2*height/width + # Therefore: + # y in the viewport goes to (scene-cy - scene-width/2*height/width) + y*scene-width/width + # scene-cy - scene-width/width * (height/2 + y) + # At most two numbers being multiplied before a divide, so no risk of overflow. var result/xmm0: float <- convert y - var height-f/xmm1: float <- convert height - var half-height-f/xmm1: float <- copy height-f - var two/eax: int <- copy 2 - var two-f/xmm2: float <- convert two - half-height-f <- divide two-f - result <- subtract half-height-f - var four/eax: int <- copy 4 - var four-f/xmm1: float <- convert four - result <- multiply four-f + result <- multiply scene-width var width-f/xmm1: float <- convert width result <- divide width-f + result <- add scene-cy + var two/eax: int <- copy 2 + var two-f/xmm2: float <- convert two + var second-term/xmm1: float <- copy scene-width + second-term <- divide two-f + var height-f/xmm2: float <- convert height + second-term <- multiply height-f + var width-f/xmm2: float <- convert width + second-term <- divide width-f + result <- subtract second-term return result }