/*
			MAND1.C - Graphics routines
		Mandelbrot Self-Squared Dragon Generator
		       For the Commodore Amiga
			    Version 2.05
	   	Copyright (C) 1985, Robert S. French
	       Vastly Enhanced by =RJ Mical=  1985/86
	       Copyright (C) 1986,  =Robert J. Mical=
       	 Further Feached and even considerably optimized by
			      >>SDB<<
	       Copyright (C) 1986 by Scott D. Ballantyne.
		      Placed in the Public Domain

This program may be distributed free of charge as long as the above
notice is retained.
*/
#include "mand.h"

extern struct Menu	MainMenu[MENU_COUNT];
extern struct MenuItem	OptionsItems[OPTIONS_COUNT];

extern SHORT		Color0, Color1, Color2;
extern SHORT		UserPalette[29];
/*----------------------*/
/* Graphics definitions */
extern struct	GfxBase		*GfxBase;
extern struct	IntuitionBase	*IntuitionBase;
 
extern struct	RastPort	*rp;
extern struct	ViewPort	*vp;
 
extern struct	Window		*w, *ColorWindow;
extern struct	Screen		*screen;
extern struct	IntuiMessage	*message;
 
extern SHORT	last_color;
extern BOOL	SettingCenter, SettingBoxSize, SetBoxProportional;
/*----------------------------------*/
/* Miscellaneous Global Definitions */
extern FLOAT	start_r, end_r, start_i, end_i;	/* Block bounds for set */
extern int	max_x, max_y;			/* Graphics window size */
extern int	max_count, color_inc, color_offset;
extern int	color_set, color_mode, color_div;
extern int	color_inset, func_num;
extern UWORD	*color_table;
extern FILE	*console, *redir_fp;
extern BOOL	Resume;
 
extern SHORT	ZoomCenterX, ZoomCenterY, ZoomBoxSizeX, ZoomBoxSizeY;
extern SHORT	ZoomBoxStartX, ZoomBoxStartY;

/*----------------*/
/* Color routines */
init_colors()
{
	switch (color_set)
	{
		case 0:
			init_cincr();
			break;
		case 1:
			init_c7rot();
			break;
		case 2:
			copy_userpalette();
			break;
	}
}

init_cincr()
/* this routine initializes the color table with sequential values from
 * 0 to 4095
 */
{
	REGISTER UWORD	i;

	for (i = 0; i < 4096; i++)
		*(color_table + i) = i;
}

init_c7rot()
/* this routine initializes the color table with this pattern:
 * 0 = 0
 * 1-15 = unit steps of blue
 * 16-30 = unit steps of green
 * 31-45 = unit steps of red
 * 46-60 = unit steps of sky sky blue (blue and green)
 * 61-75 = unit steps of purple (blue and red)
 * 76-90 = unit steps of yellow (green and red)
 * 91-115 = unit steps of white (all colors)
 */
{
	REGISTER UWORD i , j, *base;

	base = color_table;
	*(base++) = 0;
	for (j = 0; j < 39; j++)
	{
		for (i = 1; i < 16; i++)
			*(base++) = i; /* gimme the blues */
		for (i = 1; i < 16; i++)
			*(base++) = i << 4; /* these are the greens */
		for (i = 1; i < 16; i++)
			*(base++) = i << 8; /* the reds */
		for (i = 1; i < 16; i++)
			*(base++) = i | (i << 4); /* blue and green */
		for (i = 1; i < 16; i++)
			*(base++) = i | (i << 8); /* blue and red */
		for (i = 1; i < 16; i++)
			*(base++) = (i << 4) | (i << 8); /* green and red */
		for (i = 1; i < 16; i++)
			*(base++) = i | (i << 4) | (i << 8); /* all three */
	}
}

copy_userpalette()
{
	REGISTER SHORT	i;

	for (i = 0; i <= 28; i++)
		*(color_table + color_offset + (i + 1) * color_inc) =
			UserPalette[i];
}

do_broken_menu()	/* Handle menu during gen_mand() */
{
	REGISTER ULONG	class;
	REGISTER USHORT	code;

	while(message)
	{
		class = message->Class;
		code = message->Code;
		ReplyMsg(message);
		if (class == MENUPICK)
		{
		/* We only have two choices while drawing. */
		switch MENUNUM( code )
		{
			case 0:
				switch ITEMNUM( code )
				{
					case 0:
						return(1);
					case 1:
						return(2);
				}
				break;
		}
		}
		message = GetMsg(w->UserPort);
	}
	return(0);
}

/* Following variables are used by assembler routines and C. */
/* Global only to some of the routines in this file. */

FLOAT	x_gap, y_gap, u_r, u_i;

/* Following is used by assembler routines and C. */
/* Also used by SavePicture() ReadPicture() and MenuOptions() */
/* To restart after Stop/save/load. */

SHORT	y_coord;

gen_mand(starty)
SHORT starty;
{
	int returncode = 1;

	if (starty == 0)	/* starting a new graph */
	{
		CloseDisplay();
		CurrentSettings();
		if (open_winds())
			goto OPEN_W_RETURN;
	}
	else			/* continuing an old one */
	{
		MainMenu[MENU_OPTIONS].NextMenu = NULL;
		MainMenu[MENU_OPTIONS].FirstItem = &OptionsItems[OPTIONS_STOP];
		SetMenuStrip(w, &MainMenu[MENU_OPTIONS]);
	}
	SettingCenter = SettingBoxSize = FALSE;
	ZoomCenterX = (max_x >> 1);
	ZoomCenterY = (max_y >> 1);

	ModifyIDCMP(w, MENUPICK);	/* Only want menu events for now. */

	x_gap = (end_r - start_r) / max_x;
	y_gap = (end_i - start_i) / max_y;

	for (y_coord = starty; y_coord < max_y; y_coord++)
	{
		last_color = 0xfff;
		if (func_num == 0)
			returncode = Do_Function0();
		else
			returncode = Do_Function1();
		if (returncode)
		{
			returncode--;
			goto OPEN_W_RETURN;
		}
	}
	DisplayBeep(NULL);

OPEN_W_RETURN:
	if (returncode == 0)
	{
		MainMenu[MENU_OPTIONS].NextMenu = &MainMenu[MENU_ZOOM];
		MainMenu[MENU_OPTIONS].FirstItem = &OptionsItems[0];
		SetMenuStrip(w, &MainMenu[0]);
		ModifyIDCMP(w, MOUSEBUTTONS | MENUPICK | INTUITICKS);
	}
	else
		CloseDisplay();

	return(returncode);
}

DrawZoomCenter()
{
	DrawCross(ZoomCenterX, ZoomCenterY);
}

DrawDisplayCenter()
{
	DrawCross(max_x >> 1, max_y >> 1);
}

DrawCross(x, y)
SHORT x, y;
{
	SetDrMd(w->RPort, COMPLEMENT);
	Move(w->RPort, 0L, (LONG)y);
	Draw(w->RPort, (LONG)max_x, (LONG)y);
	Move(w->RPort, (LONG)x, 0L);
	Draw(w->RPort, (LONG)x, (LONG)max_y);
}

RecalcZoomBox()
{
	ZoomBoxSizeY = ((ABS( w->MouseY - ZoomCenterY )) << 1) + 1;
	if (w->MouseY < ZoomCenterY)
		ZoomBoxStartY = w->MouseY;
	else
		ZoomBoxStartY = ZoomCenterY - ((ZoomBoxSizeY - 1) >> 1);
	/* if SetBoxProportional, size X proportional to Y */
	if (SetBoxProportional)
	{
		ZoomBoxSizeX = (ZoomBoxSizeY * 8) / 5;
		ZoomBoxStartX = ZoomCenterX - ((ZoomBoxSizeX - 1) >> 1);
	}
	else
	{
		ZoomBoxSizeX = ((ABS( w->MouseX - ZoomCenterX )) << 1) + 1;
		if (w->MouseX < ZoomCenterX)
			ZoomBoxStartX = w->MouseX;
		else
			ZoomBoxStartX = ZoomCenterX - ((ZoomBoxSizeX-1) >> 1);
	}
}

DrawZoomBox()
{
	SetDrMd(w->RPort, COMPLEMENT);
	DrawBox(w->RPort, (LONG)ZoomBoxStartX, (LONG)ZoomBoxStartY, 
		((LONG) (ZoomBoxStartX + ZoomBoxSizeX - 1)),
		((LONG) (ZoomBoxStartY + ZoomBoxSizeY - 1)));
}

CloseDisplay()
{
	if (w) CloseWindow(w);
	if (ColorWindow) CloseWindow(ColorWindow);
	if (screen) CloseScreen(screen);
	w = NULL;
	screen = NULL;
}

DrawBox(rp, x1, y1, x2, y2)
struct RastPort *rp;
LONG x1, y1, x2, y2;
/* draws a box without overlapping the edges (in case of complement mode) */
{
	Move(rp, x1, y1 + SIGN(y2 - y1));
	Draw(rp, x1, y2);  
	Move(rp, x1 + SIGN(x2 - x1), y2);
	Draw(rp, x2, y2);  
	Move(rp, x2, y2 - SIGN(y2 - y1));
	Draw(rp, x2, y1); 
	Move(rp, x2 - SIGN(x2 - x1), y1);
	Draw(rp, x1, y1);
}
/* The two Dragon functions, and the pixel write routines, in 68k assembler */

#asm
	public	_Do_Function0
	public	_MathBase, _SysBase
	public	_LVOSPAdd, _LVOSPSub, _LVOSPMul
	public	_LVOSPCmp, _LVOSPFlt
	public	_LVOGetMsg
	public	_max_count, _color_mode, _color_div, _color_inset, _rp
	public	_last_color

float4	equ	$80000043	; 4.0 in FFP format
float2	equ	$80000042	; 2.0 in FFP format
USRPORT	equ	86		; UserPort offset in Window struct
				; register usage:
				;	d0, d1	FFP calls (d0 = return)
				;	a0	z_i^2
				;	d2	z_r^2
				;	d4	x_coord loop counter
				;	d3	count loop counter
				;	d6	z_i
				;	d5	z_r
				;	d7	temp
				;	a5	MathBase library ptr


_Do_Function0:
	movem.l	d4-d7/a2-a5,-(sp)
	move.l	_MathBase,a5
	moveq.l	#0,d4			; x_coord = 0
xloop:	move.l	_w,a6			; check for waiting messages
	move.l	USRPORT(a6),a0
	move.l	_SysBase,a6
	jsr	_LVOGetMsg(a6)
	tst.l	d0
	beq.s	no_messages
	move.l	d0,_message
	jsr	_do_broken_menu
	tst.w	d0
	bne.s	exit			; if returns != 0, stop already
no_messages:
	move.l	d4,d0
	jsr	_LVOSPFlt(a5)
	move.l	_x_gap,d1
	jsr	_LVOSPMul(a5)
	move.l	_start_r,d1
	jsr	_LVOSPAdd(a5)
	move.l	d0,_u_r			; done calculating u_r
	move.w	_max_y,d0
	sub.w	_y_coord,d0
	subq.w	#1,d0
	ext.l	d0
	jsr	_LVOSPFlt(a5)
	move.l	_y_gap,d1
	jsr	_LVOSPMul(a5)
	move.l	_start_i,d1
	jsr	_LVOSPAdd(a5)
	move.l	d0,_u_i			; done calculatin u_i
	moveq.l	#0,d6			; d6 is z_i
	move.l	d6,d5			; d5 will be z_r
	move.l	d6,d3			; count = 0
countloop:
	move.l	d5,d0			; z_r_squared = z_r * z_r
	move.l	d0,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,d2
	move.l	d6,d0			; z_i_squared = z_i * z_i
	move.l	d0,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,a0
	move.l	d2,d1			; if....
	jsr	_LVOSPAdd(a5)		; z_i^2 + z_r^2
	move.l	#float4,d1		; is >= 4.0
	jsr	_LVOSPCmp(a5)
	cmp.l	#1,d0			; break....
	blt.s	countexit
	move.l	d2,d0			; temp =
	move.l	a0,d1			; z_r^2 - z_i^2
	jsr	_LVOSPSub(a5)
	move.l	_u_r,d1			; - u_r
	jsr	_LVOSPSub(a5)
	move.l	d0,d7			; save in d7 for a while
	move.l	#float2,d0		; z_i = 2.0 * z_r
	move.l	d5,d1
	jsr	_LVOSPMul(a5)
	move.l	d6,d1			; * z_i
	jsr	_LVOSPMul(a5)
	move.l	_u_i,d1			; - u_i
	jsr	_LVOSPSub(a5)
	move.l	d0,d6
	move.l	d7,d5			; z_r = temp
	addq.w	#1,d3
	cmp.w	_max_count,d3
	blt.s	countloop
countexit:
	cmp.w	_max_count,d3
	blt.s	nomandel
	move.l	#0,d3
nomandel:
	bsr	writeout
	addq.w	#1,d4
	cmp.w	_max_x,d4
	blt.s	xloop
	move.w	#0,d0				; return == 0
exit:	movem.l	(sp)+,d4-d7/a2-a5
	rts

		; write_out - at entry, d4 = x_coord
		;			d3 = count
		;			_y_coord = y_coord
		; must preserve all registers except:
		; d0-d3, a1-a4
		; got it?
	public	_GfxBase
	public	_LVOWritePixel, _LVOSetAPen

		; NOTE: The equate below refer to the bitnumbers, and not
		;	the actual vales, of the similar names in MAND.H
		;	We use btst - which is takes longer than AND, but
		;	saves us from reloading _color_mode each time.

NO_HOLDANDMODIFY	equ	0
HIRES			equ	2
HALF_BRITE		equ	3

writeout:
	move.w	_color_mode,d0		; check for HAM mode
	btst	#NO_HOLDANDMODIFY,d0	; must be same as in MAND.H
	beq.s	hammode
					; else fall through to noham
noham:	move.l	#61,d1			; assume half_brite modulus
	btst	#HALF_BRITE,d0
	bne.s	getcolor
	move.l	#13,d1			; hires modulus
	btst	#HIRES,d0
	bne.s	getcolor
	move.l	#29,d1			; lores modulus

getcolor:
	move.l	#2,d0			; MandelColor
	tst.w	d3			; count
	beq.s	writepix		; color = color_inset, else
	move.w	d3,d0			; compute the color
	subq.w	#1,d0
	divu	_color_div,d0
	divu	d1,d0			; modulo math
	swap	d0
	addq.w	#3,d0
writepix:
	move.l	_rp,a1
	move.l	_GfxBase,a6
	ext.l	d0
	jsr	_LVOSetAPen(a6)
	move.l	d4,d0			; x_coord
	move.w	_y_coord,d1
	ext.l	d1
	jsr	_LVOWritePixel(a6)
	rts

		; hammode - uses last_color, and assumes it is SHORT!!
		; the original version of this program called it a long,
		; for no reason I can see...
hammode:
	move.w	d3,d1			; count == 0?
	bne.s	ham_color
	move.w	_color_inset,d0		; mandel color
	bra.s	doham
ham_color:
	mulu	_color_inc,d1
	add.w	_color_offset,d1
	ext.l	d1
	asl.l	#1,d1
	move.l	_color_table,a6
	move.w	0(a6,d1.l),d0		; got color from color table in d0
doham:	move.w	d0,d1
	move.w	_last_color,d2
	and.w	#$f00,d1
	and.w	#$f00,d2
	eor.w	d1,d2
	beq.s	green
	move.w	_last_color,d2		; update last_color
	and.w	#$ff,d2
	or.w	d1,d2
	move.w	d2,_last_color
	asr.w	#8,d1
	add.w	#$20,d1
	move.w	d1,d0
	bra.s	writepix
green:	move.w	d0,d1
	move.w	_last_color,d2
	and.w	#$f0,d1
	and.w	#$f0,d2
	eor.w	d1,d2
	beq.s	blue
	move.w	_last_color,d2
	and.w	#$f0f,d2
	or.w	d1,d2
	move.w	d2,_last_color
	asr.w	#4,d1
	add.w	#$30,d1
	move.w	d1,d0
	bra.s	writepix
blue:	and.w	#$f,d0
	move.w	_last_color,d1
	and.w	#$ff0,d1
	or.w	d0,d1
	move.w	d1,_last_color
	add.w	#$10,d0
	bra.s	writepix


	public	_Do_Function1
	public	_MathBase, _SysBase
	public	_LVOSPAdd, _LVOSPSub, _LVOSPMul
	public	_LVOSPCmp, _LVOSPFlt, _LVOSPNeg
	public	_LVOGetMsg

floathf	equ	$80000040	; 0.5 in FFP format
float1	equ	$80000041	; 1.0 in FFP format
				; register usage:
				;	d0, d1	FFP calls (d0 = return)
				;	a0	z_i^2
				;	d2	z_r^2
				;	d4	x_coord loop counter
				;	d3	count loop counter
				;	d6	z_i
				;	d5	z_r
				;	d7	temp
				;	a5	MathBase library ptr
				; register selections optimized so no
				; saves must be done for subroutine calls.
_Do_Function1:
	movem.l	d4-d7/a2-a5,-(sp)
	move.l	_MathBase,a5		; Math library base pointer.
	moveq.l	#0,d4			; x_coord = 0
xloop1:
	move.l	_w,a6			; check for menu events, etc.
	move.l	USRPORT(a6),a0
	move.l	_SysBase,a6
	jsr	_LVOGetMsg(a6)
	tst.l	d0
	beq.s	no_message1
	move.l	d0,_message
	jsr	_do_broken_menu
	tst.w	d0
	bne.s	exit1
no_message1:
	move.l	d4,d0
	jsr	_LVOSPFlt(a5)
	move.l	_x_gap,d1
	jsr	_LVOSPMul(a5)		; (x_coord * x_gap)
	move.l	_start_r,d1		; (x_coord * x_gap) + start_r
	jsr	_LVOSPAdd(a5)
	move.l	d0,_u_r			; is u_r
	move.w	_max_y,d0
	sub.w	_y_coord,d0		; max_y - y_coord 
	subq.w	#1,d0			; -1
	ext.l	d0
	jsr	_LVOSPFlt(a5)
	move.l	_y_gap,d1
	jsr	_LVOSPMul(a5)		; * y_gap
	move.l	_start_i,d1
	jsr	_LVOSPAdd(a5)		; + start_i
	move.l	d0,_u_i			; is u_i
	move.l	#floathf,d5		; z_r = 0.5
	moveq.l	#0,d3			; count = z_i = 0
	move.l	d3,d6
countloop1:
	move.l	d5,d0
	move.l	d0,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,d2			; z_r^2 = z_r * z_r
	move.l	d6,d0
	move.l	d0,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,a0			; z_i^2 = z_i * z_i
	move.l	d2,d1
	jsr	_LVOSPAdd(a5)
	move.l	#float4,d1
	jsr	_LVOSPCmp(a5)		; if (z_r^2 + z_r^2) >= 4.0
	cmp.l	#1,d0
	blt.s	countexit1		; 	break;
	move.l	#float1,d0
	move.l	d5,d1
	jsr	_LVOSPSub(a5)
	move.l	d0,a1			; temp2 = 1.0 - z_r
	move.l	d6,d0
	jsr	_LVOSPNeg(a5)
	move.l	d0,a2			; temp3 = -z_i;
	move.l	d6,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,d7			; temp = temp3 * z_i
	move.l	a1,d0
	move.l	d5,d1
	jsr	_LVOSPMul(a5)		; temp2 * z_r
	move.l	d7,d1
	jsr	_LVOSPSub(a5)
	move.l	d0,d7			; temp = (temp2 * z_r)-(temp3*z_i)
	move.l	a2,d0
	move.l	d5,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,a2			; temp3 = temp3 * z_r
	move.l	a1,d0
	move.l	d6,d1
	jsr	_LVOSPMul(a5)		; temp2 * z_i
	move.l	a2,d1
	jsr	_LVOSPAdd(a5)
	move.l	d0,d6			; z_i = (temp2*z_i)+(temp3*z_r)
	move.l	d7,d5			; z_r = temp
	move.l	_u_i,d1			; z_i still in d0
	jsr	_LVOSPMul(a5)
	move.l	d0,a2			; temp3 = z_i * u_i
	move.l	d5,d0			; z_r
	move.l	_u_r,d1
	jsr	_LVOSPMul(a5)		; z_r * u_r
	move.l	a2,d1
	jsr	_LVOSPSub(a5)		; - z_i * u_i
	move.l	d0,d7			; is temp
	move.l	d6,d0
	move.l	_u_r,d1
	jsr	_LVOSPMul(a5)
	move.l	d0,a1			; temp2 = z_i * u_r
	move.l	d5,d0
	move.l	_u_i,d1
	jsr	_LVOSPMul(a5)		; z_r * u_i
	move.l	a1,d1
	jsr	_LVOSPAdd(a5)		; + z_i * u_r
	move.l	d0,d6			; is z_i
	move.l	d7,d5			; z_r = temp
	addq.w	#1,d3
	cmp.w	_max_count,d3
	blt.s	countloop1
countexit1:
	cmp.w	_max_count,d3
	blt.s	no_mandel1
	moveq.l	#0,d3
no_mandel1:
	bsr	writeout
	addq.w	#1,d4
	cmp.w	_max_x,d4
	blt.s	xloop1
	moveq.l	#0,d0
exit1:	movem.l	(sp)+,d4-d7/a2-a5
	rts
#endasm
