/* * M A N D E L B R O T C O N S T R U C T I O N S E T * * (C) Copyright 1989 by Olaf Seibert. * Mandel may be freely distributed. See file 'doc/Notice' for details. * * The drawing task and interface code. * * This code MUST NOT be compiled with stack checking, since the drawing task * has its own stack. This is also true for anything that might get called * from here: DisableSizing(), EnableSizing(), StopFraming(), Sure(), * EnableSystemGadgets()... * * It must also be linked as one of the last files before the library, so it * is within 32K from the startup code containing geta4(). Otherwise, it * would need a4 in order to get a4. */ #include #ifndef EXEC_SEMAPHORES_H #include #endif #include #include "mandel.h" #ifdef DEBUG # include # undef STATIC # define STATIC /* EMPTY */ #endif extern double ReMouse, ImMouse; int DrawPri = 0; /* Set with SetDrawPri() */ struct Program Program[PROGRAMSIZE]; double PrgReg[2 * PROGRAMREGS]; /* Complex multiplication using pointers to reduce overhead. */ /* YOU must make sure there are no (dynamic) aliases around... */ STATIC void MulCplx(ReRes, ImRes, ReA, ImA, ReB, ImB) double *ReRes, *ImRes, *ReA, *ImA, *ReB, *ImB; { *ReRes = *ReA * *ReB - *ImA * *ImB; *ImRes = *ImA * *ReB + *ReA * *ImB; } /* * Private cache of the RastPort to speed up drawing */ STATIC struct RastPort *RastPort; /* Z^2-C: 4 multiplications per loop */ int ZQuadMinC(ReC, ImC) double ReC, ImC; { register double ReZ = 0.0, ImZ = 0.0; register double ReQuad, ImQuad; register int Depth = -1; while (ImQuad = ImZ * ImZ, ReQuad = ReZ * ReZ, Depth++, (ImQuad + ReQuad < 8) && (Depth <= MaxDepth)) { ImZ = 2 * ImZ * ReZ - ImC; ReZ = ReQuad - ImQuad - ReC; } PrgReg[RE(0)] = ReZ; PrgReg[IM(0)] = ImZ; return Depth; } /* Z^2-C: 4 multiplications per loop */ int I_ZQuadMinC(ReC, ImC) double ReC, ImC; { register double ReZ = 0.0, ImZ = 0.0; register double ReQuad, ImQuad; register int Depth = -1; while (ImQuad = ImZ * ImZ, ReQuad = ReZ * ReZ, Depth++, (ImQuad + ReQuad < 8) && (Depth <= MaxDepth)) { ImZ = 2 * ImZ * ReZ - ImC; ReZ = ReQuad - ImQuad - ReC; PrgReg[RE(0)] = ReZ; PrgReg[IM(0)] = ImZ; (*IPlotFunc)(); } PrgReg[RE(0)] = ReZ; PrgReg[IM(0)] = ImZ; return Depth; } /* Z*C*(1-Z): 10 multiplications per loop */ int ZC1MinZ(ReC, ImC) double ReC, ImC; { #define ReZ PrgReg[RE(0)] #define ImZ PrgReg[IM(0)] auto double NewReZ, NewImZ; auto double Re1MinZ, Im1MinZ; register int Depth = -1; ReZ = ReC; ImZ = ImC; while (Depth++, (ImZ * ImZ + ReZ * ReZ < 8) && (Depth <= MaxDepth)) { Re1MinZ = 1 - ReZ; Im1MinZ = -ImZ; MulCplx(&NewReZ, &NewImZ, &ReZ, &ImZ, &ReC, &ImC); MulCplx(&ReZ, &ImZ, &NewReZ, &NewImZ, &Re1MinZ, &Im1MinZ); } #undef ReZ #undef ImZ return Depth; } /* Z^3+Z*(C-1)-C: 12 multiplications per loop */ int Z3PlusZCMin1MinC(ReC, ImC) double ReC, ImC; { #define ReZ PrgReg[RE(0)] #define ImZ PrgReg[IM(0)] auto double ReCMin1; auto double ReZ2, ImZ2, ReZ3, ImZ3; register double ReQuad, ImQuad; register int Depth = -1; register void (*iplot)() = IPlotFunc; ReZ = ReC; ImZ = ImC; while (ImQuad = ImZ * ImZ, ReQuad = ReZ * ReZ, Depth++, (ImQuad + ReQuad < 8) && (Depth <= MaxDepth)) { /* Calculate z^2 */ ReZ2 = ReQuad - ImQuad; ImZ2 = 2 * ReZ * ImZ; /* Make z^3 */ MulCplx(&ReZ3, &ImZ3, &ReZ2, &ImZ2, &ReZ, &ImZ); /* Calculate z(c-1) while destroying z^2 */ ReCMin1 = ReC - 1; MulCplx(&ReZ2, &ImZ2, &ReZ, &ImZ, &ReCMin1, &ImC); /* Add everything */ ReZ = ReZ3 + ReZ2 - ReC; ImZ = ImZ3 + ImZ2 - ImC; } #undef ReZ #undef ImZ return Depth; } /* * User programmed function... */ int UserProgFunc(ReC, ImC) double ReC, ImC; { register struct Program *pc; register struct Program *MainPC; register int Depth = -1; register int pr_Dest; register void (*iplot)() = IPlotFunc; PrgReg[RE(2)] = ReC; PrgReg[IM(2)] = ImC; PrgReg[RE(3)] = ReMouse; PrgReg[IM(3)] = ImMouse; #define ReZ PrgReg[RE(0)] #define ImZ PrgReg[IM(0)] #define ReQuad PrgReg[RE(1)] #define ImQuad PrgReg[IM(1)] #define ReC PrgReg[RE(2)] #define ImC PrgReg[IM(2)] ReZ = 0.0; ImZ = 0.0; /* Do the prelude: */ pc = &Program[0]; goto InterpretLoop; /* I know this is ugly */ PreludeDone: MainPC = pc + 1; while (ImQuad = ImZ * ImZ, ReQuad = ReZ * ReZ, Depth++, (ImQuad + ReQuad < 8) && (Depth <= MaxDepth)) { pc = MainPC; InterpretLoop: for (;;) { pr_Dest = pc->pr_Dest; switch (pc->pr_OpCode) { case End: if (pr_Dest) /* == 1 */ goto EndOfProgram; else goto PreludeDone; case Cassign: PrgReg[IM(pr_Dest)] = PrgReg[IM(pc->pr_Op1)]; case Rassign: PrgReg[RE(pr_Dest)] = PrgReg[RE(pc->pr_Op1)]; break; case Ci: PrgReg[IM(pr_Dest)] = (double) pc->pr_Op2; case Ri: PrgReg[RE(pr_Dest)] = (double) pc->pr_Op1; break; case Cplus: PrgReg[IM(pr_Dest)] = PrgReg[IM(pc->pr_Op1)] + PrgReg[IM(pc->pr_Op2)]; case Rplus: PrgReg[RE(pr_Dest)] = PrgReg[RE(pc->pr_Op1)] + PrgReg[RE(pc->pr_Op2)]; break; case Cminus: PrgReg[IM(pr_Dest)] = PrgReg[IM(pc->pr_Op1)] - PrgReg[IM(pc->pr_Op2)]; case Rminus: PrgReg[RE(pr_Dest)] = PrgReg[RE(pc->pr_Op1)] - PrgReg[RE(pc->pr_Op2)]; break; case Ctimes: MulCplx( &PrgReg[RE(pr_Dest)], &PrgReg[IM(pr_Dest)], &PrgReg[RE(pc->pr_Op1)], &PrgReg[IM(pc->pr_Op1)], &PrgReg[RE(pc->pr_Op2)], &PrgReg[IM(pc->pr_Op2)] ); break; case Rtimes: PrgReg[RE(pr_Dest)] = PrgReg[RE(pc->pr_Op1)] * PrgReg[RE(pc->pr_Op2)]; break; } /* end of switch */ pc++; } /* end of for */ EndOfProgram: (*iplot)(); } /* end of while */ return Depth; #undef ReZ #undef ImZ #undef ReQuad #undef ImQuad #undef ReC #undef ImC } void PlotIterationCount(Count, x, y) register int Count; long x, y; { if (Count > MaxDepth) Count = PenTable[0]; else Count = PenTable[Count]; if (RastPort->FgPen != (BYTE) Count) SetAPen(RastPort, (long) Count); WritePixel(RastPort, x, y); } void PlotZ() { register int Color; register long x, y; x = (PrgReg[RE(0)] - LeftEdge) / CXStep; y = (TopEdge - PrgReg[IM(0)]) / CYStep; /* * Color will be -1 + 1 if we are within the window */ if (Color = ReadPixel(RastPort, x, y) + 1) { if (RastPort->FgPen != (BYTE) Color) SetAPen(RastPort, (long) Color); WritePixel(RastPort, x, y); } } void None() { /* empty */ } /* Some private variables */ struct Task *DrawTask = NULL; struct Task *MandelTask = NULL; struct SignalSemaphore DrawSemaphore; /* * This is what it is all about! */ STATIC bool MyFillIn; /* Parameter for new task */ STATIC void ActuallyDrawPicture() { double x, /* Running Real value */ y, /* Running Imaginary value */ Leftx; /* Reset Real value */ register long px, /* Running horizontal pixel coordinate */ py; /* Running vertical pixel coordinate */ long Leftpx, /* Reset horizontal pixel coordinate */ minpx; /* Minimal horizontal pixel coordinate */ register long maxpx; /* Maximal horizontal pixel coordinate */ long minpy, /* Minimal vertical pixel coordinate */ maxpy; /* Maximal vertical pixel coordinate */ double MyXstep, /* Real increment */ MyYstep; /* Imaginary increment */ int MyPixelStep,/* Private copy of PixelStep */ XOffset, /* Running from 0 to MyPixelStep */ YOffset; /* Running from 0 to MyPixelStep */ struct Library *MathBasBase;/* Pointer to mathxxx.library */ register int (*Function) (); register void (*Plot) (); geta4(); /* Manx small memory model */ ObtainSemaphore(&DrawSemaphore); #ifdef IEEEDP /* * We must open this library ourselves, just in case we have a math * chip that needs extra context saving to be done. But to really * access the library, we use the global library base that the main * task has set for us. (but they are the same, anyway) */ MathBasBase = OpenLibrary("mathieeedoubbas.library", LIBRARY_VERSION); #else MathBasBase = OpenLibrary("mathffp.library", LIBRARY_VERSION); #endif StillDrawing = TRUE; DisableSizing(); StopFraming(); OffMenu(MainWindow, (ULONG) SHIFTMENU(PRJMENU) | SHIFTITEM(PRJNEW) | SHIFTSUB(NOSUB)); OffMenu(MainWindow, (ULONG) SHIFTMENU(OPTMENU) | SHIFTITEM(OPTRES) | SHIFTSUB(ORFIL)); if (!MyFillIn && !Sure()) skipto exit; if (!MyFillIn) NameValid = FALSE; Saved = FALSE; MyPixelStep = PixelStep; XOffset = 0; YOffset = MyFillIn ? 1 : 0; minpx = 0; maxpx = MainWindow->GZZWidth - 1; minpy = 0; maxpy = MainWindow->GZZHeight - 1; RastPort = MainWindow->RPort; SetDrMd(RastPort, (long) JAM1); if (!MyFillIn) { /* Clear the window */ SetAPen(RastPort, (long) 2); /* was PenTable[0] */ RectFill(RastPort, minpx, minpy, maxpx, maxpy); } CalcCSteps(); /* Calculate CXStep and CYStep */ MyXstep = MyPixelStep * CXStep; MyYstep = MyPixelStep * CYStep; Function = DepthFunc; Plot = EPlotFunc; ReleaseSemaphore(&DrawSemaphore); again: Leftpx = minpx + XOffset; /* Start in the upper left-hand corner */ py = minpy + YOffset; /* of the window */ Leftx = LeftEdge + XOffset * CXStep; y = TopEdge - YOffset * CYStep; for (; py <= maxpy; y -= MyYstep, py += MyPixelStep) { ObtainSemaphore(&DrawSemaphore); for (px = Leftpx, x = Leftx; px <= maxpx; x += MyXstep, px += MyPixelStep) { (*Plot) ((*Function) (x, y), px, py); /* Check if we are asked to terminate. release s'phore. */ if (StillDrawing < 0) { ReleaseSemaphore(&DrawSemaphore); skipto exit; } } ReleaseSemaphore(&DrawSemaphore); } if (MyFillIn && (++YOffset < MyPixelStep)) { backto again; /* Draw pixels below current pixel */ } if (MyFillIn && (++XOffset < MyPixelStep)) { YOffset = 0; /* and next to them */ backto again; } exit: ObtainSemaphore(&DrawSemaphore); if (MathBasBase) CloseLibrary(MathBasBase); EnableSizing(); OnMenu(MainWindow, (ULONG) SHIFTMENU(PRJMENU) | SHIFTITEM(PRJNEW) | SHIFTSUB(NOSUB)); OnMenu(MainWindow, (ULONG) SHIFTMENU(OPTMENU) | SHIFTITEM(OPTRES) | SHIFTSUB(ORFIL)); DrawTask = NULL; StillDrawing = FALSE; /* We are finished, finally... */ ReleaseSemaphore(&DrawSemaphore); Signal(MandelTask, DrawSigMask); /* and shout it off the roof */ } bool DrawPicture(FillIn) bool FillIn; { register long Priority; if (StillDrawing == FALSE) { Priority = MandelTask->tc_Node.ln_Pri + DrawPri; MyFillIn = FillIn; SetSignal(0L, DrawSigMask); /* Clear the drawing signal */ DrawTask = CreateTask("Mandelbrot_Drawing.task", Priority, ActuallyDrawPicture, 2048L); } return DrawTask != NULL; } /* Interface because I wanted to CreateTask the drawing... */ void WaitForDrawing() { Forbid(); if (StillDrawing) Wait(DrawSigMask); Permit(); } void StopDrawing() { Forbid(); if (StillDrawing) { StillDrawing = -1; /* Indicate termination is wanted. */ WaitForDrawing(); /* Clears the draw/batch signal. */ } /* Bug or feature? Need Batch/Cont. */ Permit(); } void SetDrawPri(Priority) int Priority; { DrawPri = Priority; Forbid(); if (StillDrawing) { Priority = MandelTask->tc_Node.ln_Pri + DrawPri; SetTaskPri(DrawTask, (long) Priority); } Permit(); } /* * This is the external interface to the DrawSemaphore. Use it with care. * Always balance calls to SuspendDrawing() with ResumeDrawing() !!! */ void SuspendDrawing() { ObtainSemaphore(&DrawSemaphore); } void ResumeDrawing() { ReleaseSemaphore(&DrawSemaphore); } void CalcCSteps() { CXStep = (double) (RightEdge - LeftEdge) / (MainWindow->GZZWidth - 1); CYStep = (double) (TopEdge - BottomEdge) / (MainWindow->GZZHeight - 1); }