Mandelbrot Set in MSIL






4.83/5 (5 votes)
Demonstrates the use of Windows Forms, GDI+ from CIL
Introduction
To really be able to program .NET, a good programmer will find himself/herself using ILDASM, the MSIL disassembler, on a regular basis. This necessitates an ability to, at a minimum, be competent at reading MSIL. Unfortunately, the best way to learn to read MSIL is to spend some time writing it! Thus, I set about thinking of something I could write, which would not be too large, but would cover enough of the concepts to give me an ability to read MSIL.
I remember a friend at college who, when he was learning a new language, would always generate Mandelbrot sets as his first piece of code (a mandelbrot set written in Postscript was amusing). This seemed do-able, so I chose to write a Mandelbrot set generator in MSIL.
The code uses Windows Forms to show a new window, then draws the pixels onto this graphics surface.
Compiling
Compile the code simply by ilasm /exe mandelbrot.il
. Remember to have ilasm
in your path - the batch file corvars.bat in the bin folder of the .NET Framework SDK will add it for you.
This should generate mandelbrot.exe in the same folder.
The Code
The code inherits from System.Windows.Form
and manages its own painting.
.class public auto ansi MandelBrotIL extends [System.Windows.Forms]System.Windows.Forms.Form {
This class overrides the OnPaint()
method of System.Windows.Forms.Form
(which it inherits from). OnPaint
simply sets up the graphics to draw onto, and then calls DrawMandelbrot()
to do the actual drawing:
.method family hidebysig virtual instance void OnPaint(class
[System.Windows.Forms]System.Windows.Forms.PaintEventArgs e) cil managed {
ldarg.0
ldarg e
call instance class [System.Drawing]System.Drawing.Graphics
[System.Windows.Forms]System.Windows.Forms.PaintEventArgs::get_Graphics()
ldc.r4 200 // Width in pixels
ldc.i4 250 // Iterations before we assume convergence to zero
call instance void ThunderMain.Demo.MandelBrotIL::DrawMandelbrot(class
[System.Drawing]System.Drawing.Graphics, float32, int32)
ret
}
DrawMandelbrot()
simply implements a basic Mandelbrot algorithm, details of which can be found in many places on the Internet.
.method private hidebysig instance
void DrawMandelbrot(class [System.Drawing]System.Drawing.Graphics g,
float32 fpMandelWidth, int32 ipIterations) cil managed {
.locals (
[0] float32 x, // Complex Real portion
[1] float32 y, // Complex Imaginary portion
[2] float32 fpGranularity, // Resolution of image on screen
[3] int32 ipPixelX, // X position on screen
[4] int32 ipPixelY, // Y position on screen
// Escape Velocity locals
[5] float32 fpX,
[6] float32 fpY,
[7] float32 fpXTemp,
[8] float32 fpYTemp,
[9] float32 fpX2, // X Squared
[10] float32 fpY2, // Y Squared
[11] int32 i // Loop variable used to calc escape velocity
)
// Calculate Granularity
ldc.r4 4
ldarg fpMandelWidth
div
stloc fpGranularity
// Initialise point on screen
ldc.i4.0
dup
stloc ipPixelX
stloc ipPixelY
// Start the real portion off
ldc.r4 -2.5
stloc x
NextReal:
// Start the imaginary portion off
ldc.r4 -1.5
stloc y
NextImaginary:
// Calculate Escape Velocity
// Initialise locals
ldloc x
stloc fpX
ldloc y
stloc fpY
ldloc x
dup
mul
stloc fpX2
ldloc y
dup
mul
stloc fpY2
// Initialise i
ldc.i4.0
stloc i
NextIteration:
ldloc x
ldloc fpX2
ldloc fpY2
sub
add
stloc fpXTemp
ldloc y
ldloc fpY
ldloc fpX
ldc.r4 2
mul
mul
add
stloc fpYTemp
// Calculate X squared
ldloc fpXTemp
dup
mul
stloc fpX2
// Calculate Y squared
ldloc fpYTemp
dup
mul
stloc fpY2
// If X Squared plus Y Squared is greater than 4, then we are guaranteed
// divergence to Infinity
ldloc fpX2
ldloc fpY2
add
ldc.r4 4
bge Divergence
// The previous values in the sequence become the current values in the sequence
ldloc fpXTemp
stloc fpX
ldloc fpYTemp
stloc fpY
// Incrememt i
ldc.i4.1
ldloc i
add
stloc i
ldloc i
ldarg ipIterations
blt NextIteration // Assume convergence to zero if we reach our iteration limit
Divergence:
// Draw Pixel. i is now the escape velocity
// Load the Graphics context
ldarg g
// Calculate the color based on the escape velocity
ldarg ipIterations
ldloc i
sub
stloc i
// Red
ldloc i
ldc.i4 12
mul
ldc.i4 256
rem
// Green
ldloc i
ldc.i4 16
mul
ldc.i4 256
rem
// Blue
ldloc i
ldc.i4 5
mul
ldc.i4 256
rem
call value class [System.Drawing]System.Drawing.Color
[System.Drawing]System.Drawing.Color::FromArgb(int32,
int32,
int32)
// Create a new Pen on the stack
ldc.r4 1
newobj instance void [System.Drawing]System.Drawing.Pen::.ctor(value class
[System.Drawing]System.Drawing.Color,
float32)
// Load the coords of the point to draw
ldloc ipPixelX
ldloc ipPixelY
// Width and Height
ldc.i4.2
dup
call instance void [System.Drawing]System.Drawing.Graphics::DrawRectangle(class
[System.Drawing]System.Drawing.Pen,
int32,
int32,
int32,
int32)
// Next PixelY
ldc.i4.1
ldloc ipPixelY
add
stloc ipPixelY
// Advance through the imaginary portion
ldloc y
ldloc fpGranularity
add
stloc y
ldloc y
ldc.r4 1.5
blt NextImaginary
// Advance through ipPixelX
ldc.i4.1
ldloc ipPixelX
add
stloc ipPixelX
// Start at the top of the screen for the next column
ldc.i4.0
stloc ipPixelY
// Advance through the real portion
ldloc x
ldloc fpGranularity
add
stloc x
ldloc x
ldc.r4 1.5
blt NextReal
ret
} // method
History
- 24th June, 2002: Initial version