180 lines
5.6 KiB
Forth
180 lines
5.6 KiB
Forth
# Mandelbrot set
|
|
#
|
|
# Install:
|
|
# $ git clone https://github.com/akkartik/mu
|
|
# $ cd mu
|
|
# Build on Linux:
|
|
# $ ./translate mandelbrot.mu
|
|
# Build on other platforms (slow):
|
|
# $ ./translate_emulated mandelbrot.mu
|
|
# Run:
|
|
# $ qemu-system-i386 code.img
|
|
|
|
fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
|
|
# 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), 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
|
|
var width/esi: int <- copy a
|
|
width <- shift-left 3/log2-font-width
|
|
var height/edi: int <- copy b
|
|
height <- shift-left 4/log2-font-height
|
|
var y/ecx: int <- copy 0
|
|
{
|
|
compare y, height
|
|
break-if->=
|
|
var imaginary/xmm1: float <- viewport-to-imaginary y, width, height, scene-cy, scene-width
|
|
var x/ebx: int <- copy 0
|
|
{
|
|
compare x, width
|
|
break-if->=
|
|
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
|
|
iterations <- shift-right 3
|
|
var color/edx: int <- copy 0
|
|
iterations, color <- integer-divide iterations, 0x18/24/size-of-cycle-0
|
|
color <- add 0x20/cycle-0
|
|
pixel screen, x, y, color
|
|
x <- increment
|
|
loop
|
|
}
|
|
y <- increment
|
|
loop
|
|
}
|
|
}
|
|
|
|
fn mandelbrot-iterations-for-point real: float, imaginary: float, max: int -> _/eax: int {
|
|
var zero: float
|
|
var x/xmm0: float <- copy zero
|
|
var y/xmm1: float <- copy zero
|
|
var iterations/ecx: int <- copy 0
|
|
{
|
|
var done?/eax: boolean <- mandelbrot-done? x, y
|
|
compare done?, 0/false
|
|
break-if-!=
|
|
compare iterations, max
|
|
break-if->=
|
|
var newx/xmm2: float <- mandelbrot-x x, y, real
|
|
var newy/xmm3: float <- mandelbrot-y x, y, imaginary
|
|
x <- copy newx
|
|
y <- copy newy
|
|
iterations <- increment
|
|
loop
|
|
}
|
|
return iterations
|
|
}
|
|
|
|
fn mandelbrot-done? x: float, y: float -> _/eax: boolean {
|
|
# x*x + y*y > 4
|
|
var x2/xmm0: float <- copy x
|
|
x2 <- multiply x
|
|
var y2/xmm1: float <- copy y
|
|
y2 <- multiply y
|
|
var sum/xmm0: float <- copy x2
|
|
sum <- add y2
|
|
var four/eax: int <- copy 4
|
|
var four-f/xmm1: float <- convert four
|
|
compare sum, four-f
|
|
{
|
|
break-if-float>
|
|
return 0/false
|
|
}
|
|
return 1/true
|
|
}
|
|
|
|
fn mandelbrot-x x: float, y: float, real: float -> _/xmm2: float {
|
|
# x*x - y*y + real
|
|
var x2/xmm0: float <- copy x
|
|
x2 <- multiply x
|
|
var y2/xmm1: float <- copy y
|
|
y2 <- multiply y
|
|
var result/xmm0: float <- copy x2
|
|
result <- subtract y2
|
|
result <- add real
|
|
return result
|
|
}
|
|
|
|
fn mandelbrot-y x: float, y: float, imaginary: float -> _/xmm3: float {
|
|
# 2*x*y + imaginary
|
|
var two/eax: int <- copy 2
|
|
var result/xmm0: float <- convert two
|
|
result <- multiply x
|
|
result <- multiply y
|
|
result <- add imaginary
|
|
return result
|
|
}
|
|
|
|
# Scale (x, y) pixel coordinates to a complex plane where the viewport width
|
|
# ranges from -2 to +2. Viewport height just follows the viewport's aspect
|
|
# ratio.
|
|
|
|
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-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, 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
|
|
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
|
|
}
|