		/*************************************
		 *                                   *
		 *            Gadgets v2.0           *
		 *   by Torsten Jürgeleit in 05/91   *
		 *                                   *
		 *          Routines - Part 3        *
		 *                                   *
		 *************************************/

	/* Includes */

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/inputevent.h>
#include <intuition/intuition.h>
#include <functions.h>
#include <string.h>
#include "/render/render.h"
#include "/texts/texts.h"
#include "/borders/borders.h"
#include "gadgets.h"
#include "imports.h"

	/* Refresh gadget */

   VOID
refresh_gadget(struct ExtendedGadget  *egad)
{
   struct GadgetList      *gl = egad->eg_GadgetList;
   struct RenderInfo      *ri = gl->gl_RenderInfo;
   struct GadgetData      *gd = gl->gl_Data + egad->eg_DataEntry;
   struct ExtendedGadget  *first_egad = egad;
   struct Window          *win = gl->gl_Window;
   struct RastPort        *rp = win->RPort;
   struct Gadget          *gad;
   struct Image           *image;
   APTR  render;
   UBYTE flags;
   SHORT left_edge, top_edge, width, xoffset;

   /* render object text - rendering first because of ghosting by GADGDISABLED */
   switch (gd->gd_Type) {
      case GADGET_DATA_TYPE_CYCLE :
	 print_cycle_text(egad);
	 break;
      case GADGET_DATA_TYPE_COUNT :
	 print_count_text(egad);
	 break;
      case GADGET_DATA_TYPE_LISTVIEW :
	 print_list_view_text(egad);
	 break;
   }
   /* render all gadgets belonging to this object */
   do {
      gad   = &egad->eg_Gadget;
      flags = egad->eg_Flags;
      /* draw additional border or image if any */
      if (flags & (EXTENDED_GADGET_FLAG_RENDER_BORDER |
				       EXTENDED_GADGET_FLAG_RENDER_IMAGE)) {
	 left_edge = gad->LeftEdge;
	 top_edge  = gad->TopEdge;
	 if (flags & EXTENDED_GADGET_FLAG_RENDER_BORDER) {
	    DrawBorder(rp, (struct Border *)egad->eg_Render, (LONG)
						 left_edge, (LONG)top_edge);
	 } else {
	    image     = (struct Image *)egad->eg_Render;
	    xoffset   = (gd->gd_Flags & GADGET_DATA_FLAG_NO_BORDER ?
			       IMAGE_HORIZ_OFFSET : IMAGE_HORIZ_OFFSET + 2);
	    top_edge += (SHORT)(gad->Height - image->Height) / 2;
	    width     = gad->Width;
	    switch (egad->eg_Type) {
	       case EXTENDED_GADGET_TYPE_CYCLE :
		  left_edge += xoffset;
		  break;
	       case EXTENDED_GADGET_TYPE_COUNT :
		  DrawImage(rp, &ri->ri_Images[IMAGE_COUNT_RIGHT], (LONG)
			       (left_edge + width - image->Width - xoffset),
							    (LONG)top_edge);
		  left_edge += xoffset;
		  break;
	       default :
		  left_edge += (SHORT)(width - image->Width) / 2;
		  break;
	    }
	    DrawImage(rp, image, (LONG)left_edge, (LONG)top_edge);
	 }
      }
      /* special refresh for disabled selected toggle select gadgets to force correct ghosting */
      if ((gad->Activation & TOGGLESELECT) && (gad->Flags &
		  (GADGDISABLED | SELECTED)) == (GADGDISABLED | SELECTED)) {
	 render            = gad->GadgetRender;
	 gad->GadgetRender = gad->SelectRender;
	 gad->Flags       &= ~SELECTED;
	 RefreshGList(gad, win, (LONG)NULL, 1L);
	 gad->Flags       |= SELECTED;
	 gad->GadgetRender = render;
      } else {
	 RefreshGList(gad, win, (LONG)NULL, 1L);
      }
   } while (egad = egad->eg_NextGadget);
   print_gadget_text(first_egad);
}
	/* Change gadget */

   VOID
change_gadget(struct ExtendedGadget  *egad)
{
   struct GadgetList    *gl = egad->eg_GadgetList;
   struct Window        *win = gl->gl_Window;
   struct Gadget        *gad = &egad->eg_Gadget;
   struct PropInfo      *pinfo;
   struct Image         *image;
   struct MXData        *mx;
   struct SliderData    *sl;
   struct CycleData     *cy;
   struct ScrollerData  *sc;
   struct ListViewData  *lv;
   struct PaletteData   *pd;
   VOID   *data = (VOID *)(egad + 1);
   UBYTE  displayed = gl->gl_Flags & GADGET_LIST_FLAG_DISPLAYED;
   USHORT i, data_entry = egad->eg_DataEntry;
   ULONG  min, max, num, body, pot;

   switch ((gl->gl_Data + data_entry)->gd_Type) {
      case GADGET_DATA_TYPE_BUTTON :
      case GADGET_DATA_TYPE_CHECK :
      case GADGET_DATA_TYPE_STRING :
      case GADGET_DATA_TYPE_INTEGER :
	 if (displayed) {
	    RefreshGList(gad, win, (LONG)NULL, 1L);
	 }
	 break;
      case GADGET_DATA_TYPE_MX :
	 mx  = data;
	 max = mx->mx_TextEntries;
	 num = mx->mx_ActiveEntry;
	 for (i = 0; i < max; i++) {
	    gad = &egad->eg_Gadget;
	    if (i == num) {
	       gad->Flags |= SELECTED;
	       if (displayed) {
		  RefreshGList(gad, win, (LONG)NULL, 1L);
	       }
	    } else {
	       if (gad->Flags & SELECTED) {
		  gad->Flags &= ~SELECTED;
		  if (displayed) {
		     RefreshGList(gad, win, (LONG)NULL, 1L);
		  }
	       }
	       
	    }
	    egad = egad->eg_NextGadget;
	 }
	 break;
      case GADGET_DATA_TYPE_SLIDER :
	 pinfo = (struct PropInfo *)gad->SpecialInfo;
	 sl    = data;
	 min   = sl->sl_Min;
	 max   = (LONG)sl->sl_Max - (LONG)min;
	 num   = (LONG)sl->sl_Level - (LONG)min;
	 if (sl->sl_Flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	    num = max - num;   /* if vert slider then maximum is at top */
	 }
	 body = calc_prop_body(max + 1, 1L);
	 pot  = calc_prop_pot(max + 1, 1L, num);
	 if (sl->sl_Flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, 0L,
							 pot, 0L, body, 1L);
	    } else {
	       pinfo->VertBody = body;
	       pinfo->VertPot  = pot;
	    }
	 } else {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, pot,
							  0L, body, 0L, 1L);
	    } else {
	       pinfo->HorizBody = body;
	       pinfo->HorizPot  = pot;
	    }
	 }
	 break;
      case GADGET_DATA_TYPE_SCROLLER :
	 pinfo = (struct PropInfo *)gad->SpecialInfo;
	 sc    = data;
	 min   = sc->sc_Visible;
	 max   = sc->sc_Total;
	 num   = sc->sc_Top;
	 body  = calc_prop_body(max, min);
	 pot   = calc_prop_pot(max, min, num);
	 if (sc->sc_Flags & GADGET_DATA_FLAG_ORIENTATION_VERT) {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, 0L,
							 pot, 0L, body, 1L);
	    } else {
	       pinfo->VertBody = body;
	       pinfo->VertPot  = pot;
	    }
	 } else {
	    if (displayed) {
	       NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, pot,
							  0L, body, 0L, 1L);
	    } else {
	       pinfo->HorizBody = body;
	       pinfo->HorizPot  = pot;
	    }
	 }
	 break;
      case GADGET_DATA_TYPE_CYCLE :
	 cy                = data;
	 cy->cy_ActiveText = cy->cy_TextArray[cy->cy_ActiveEntry];
	 if (displayed) {
	    print_cycle_text(egad);
	 }
	 break;
      case GADGET_DATA_TYPE_COUNT :
	 if (displayed) {
	    print_count_text(egad);
	 }
	 break;
      case GADGET_DATA_TYPE_LISTVIEW :
	 pinfo = (struct PropInfo *)gad->SpecialInfo;
	 lv    = data;
	 min   = lv->lv_VisibleEntries;
	 max   = lv->lv_ListEntries;
	 num   = lv->lv_TopEntry;
	 body  = calc_prop_body(max, min);
	 pot   = calc_prop_pot(max, min, num);
	 if (displayed) {
	    NewModifyProp(gad, win, (LONG)NULL, (LONG)pinfo->Flags, 0L, pot,
							      0L, body, 1L);
	    print_list_view_text(egad);
	 } else {
	    pinfo->VertBody = body;
	    pinfo->VertPot  = pot;
	 }
	 break;
      case GADGET_DATA_TYPE_PALETTE :
	 pd = data;
	 if (!(pd->pd_Flags & GADGET_DATA_FLAG_PALETTE_NO_INDICATOR)) {
	    image             = (struct Image *)egad->eg_Render;
	    image->PlaneOnOff = pd->pd_ActiveColor;
	    if (displayed) {
	       DrawImage(win->RPort, image, (LONG)(gad->LeftEdge + (SHORT)
		     (gad->Width - image->Width) / 2), (LONG)(gad->TopEdge +
				 (SHORT)(gad->Height - image->Height) / 2));
	    }
	 }
	 break;
   }
}
	/* Change count gadget value */

   VOID
change_count_gadget(struct ExtendedGadget  *egad)
{
   struct GadgetList    *gl = egad->eg_GadgetList;
   struct Window        *win = gl->gl_Window;
   struct MsgPort       *up  = win->UserPort;
   struct IntuiMessage  *msg;
   struct Gadget        *gad = &egad->eg_Gadget;
   struct CountData     *co = (struct CountData *)(egad + 1);
   LONG   new_count, min = co->co_Min, max = co->co_Max, count = co->co_Value;
   ULONG  class, old_idcmp_flags = win->IDCMPFlags;
   USHORT distance, new_delay, max_delay = MAX_COUNT_DELAY,
	  delay = MAX_COUNT_DELAY;
   SHORT  count_change, middle = gad->LeftEdge + gad->Width / 2;
   BOOL   keepon = TRUE;

   ModifyIDCMP(win, (LONG)(GADGETUP | MOUSEBUTTONS | INTUITICKS));
   /* calc new delay and change value with new mouse pos */
   if (win->MouseX > middle) {
      count_change = +1;
      if (count < max) {
	 count++;
      }
   } else {
      count_change = -1;
      if (count > min) {
	 count--;
      }
   }
   co->co_Value = count;
   print_count_text(egad);
   do {
      /* calc new delay and change value with new mouse pos */
      if (win->MouseX > middle) {
	 distance     = win->MouseX - middle;
	 count_change = +1;
      } else {
	 if (win->MouseX < 0) {
	    distance = middle + win->MouseX;
	 } else {
	    distance = middle - win->MouseX;
	 }
	 count_change = -1;
      }
      if (distance > MAX_COUNT_DISTANCE) {
	 distance = MAX_COUNT_DISTANCE;
      }
      new_delay = MAX_COUNT_DELAY - distance / MAX_COUNT_STEP_SIZE;
      if (new_delay != max_delay) {
	 max_delay = delay = new_delay;
      }
      if (msg = (struct IntuiMessage *)GetMsg(up)) {
	 class = msg->Class;
	 ReplyMsg((struct Message *)msg);
	 if (class == INTUITICKS) {
	    if (max_delay) {
	       /* delayed change of count value */
	       if (! --delay) {
		  new_count = count + count_change;
		  if (new_count < min) {
		     new_count = min;
		  } else {
		     if (new_count > max) {
			new_count = max;
		     }
		  }
		  /* print new count only if changed */
		  if (new_count != count) {
		     co->co_Value = count = new_count;
		     print_count_text(egad);
		  }
		  delay = max_delay;
	       }
	    }
	 } else {
	    keepon = FALSE;
	 }
      }
      if (! max_delay) {
	 /* undelayed change of count value */
	 if ((count + count_change) >= min && (count + count_change) <=
								      max) {
	    co->co_Value = count += count_change;
	    print_count_text(egad);
	    timer_delay(0L, MIN_COUNT_DELAY_MICROS);
	 }
      }
   } while (keepon == TRUE);
   ModifyIDCMP(win, old_idcmp_flags);
   /* restore normal count gadget state (unselected) */
   if ((gad->Flags & GADGHIGHBITS) == GADGHCOMP) {
      RefreshGList(gad, win, (LONG)NULL, 1L);   /* complement gadget select box */
   }
   gad->Flags &= ~SELECTED;
   RefreshGList(gad, win, (LONG)NULL, 1L);
}
	/* Delay function - can't use Delay() from DOS (no process) */

   VOID
timer_delay(ULONG seconds, ULONG micros)
{
   struct MsgPort      *port;
   struct timerequest  *timer;

   if (port = CreatePort(NULL, 0L)) {
      if (timer = (struct timerequest *)CreateExtIO(port, (LONG)
					      sizeof(struct timerequest))) {
	 if (!OpenDevice(TIMERNAME, (LONG)UNIT_VBLANK, (struct IORequest *)
							       timer, 0L)) {
	    timer->tr_node.io_Command = TR_ADDREQUEST;
	    timer->tr_time.tv_secs    = seconds;
	    timer->tr_time.tv_micro   = micros;
	    DoIO((struct IORequest *)timer);
	    CloseDevice((struct IORequest *)timer);
	 }
	 DeleteExtIO((struct IORequest *)timer);
      }
      DeletePort(port);
   }
}
	/* Print gadget text */

   VOID
print_gadget_text(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct RenderInfo  *ri = gl->gl_RenderInfo;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *rp = win->RPort;
   struct GadgetData  *gd = gl->gl_Data + egad->eg_DataEntry;
   struct Gadget      *gad = &egad->eg_Gadget;
   struct TextAttr    *text_attr;
   BYTE   c, *text, *buffer;
   LONG   len;
   USHORT save_left_edge, left_edge, top_edge, pos, flags = (gd->gd_Flags &
		GADGET_DATA_FLAG_TEXT_COLOR2 ? TEXT_DATA_FLAG_ABSOLUTE_POS |
		       TEXT_DATA_FLAG_COLOR2 : TEXT_DATA_FLAG_ABSOLUTE_POS);

   if (text = egad->eg_Text) {
      left_edge = gad->LeftEdge + egad->eg_TextLeftEdge;
      top_edge  = gad->TopEdge + egad->eg_TextTopEdge;
      if (!(text_attr = gd->gd_TextAttr)) {
	 text_attr = &ri->ri_TextAttr;
      }
      if (egad->eg_Flags & EXTENDED_GADGET_FLAG_HOT_KEY) {
	 len = strlen(text) + 1;
	 pos = egad->eg_HotkeyPos;
	 if (buffer = AllocMem(len, (LONG)MEMF_PUBLIC)) {
	    strcpy(buffer, text);
	    *(buffer + pos - 1) = '\0';
	    left_edge          += print_text(ri, win, &buffer[0],
		left_edge, top_edge, TEXT_DATA_TYPE_TEXT, flags, text_attr);
	    c                   = *(buffer + pos + 1);
	    *(buffer + pos + 1) = '\0';
	    /* print and mark hot key in gadget text */
	    save_left_edge = left_edge;
	    left_edge     += print_text(ri, win, &buffer[pos], left_edge,
			   top_edge, TEXT_DATA_TYPE_TEXT, flags, text_attr);
	    SetDrMd(rp, (LONG)JAM1);
	    SetAPen(rp, (LONG)(gd->gd_Flags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
					ri->ri_TextPen2 : ri->ri_TextPen1));
	    RectFill(rp, (LONG)save_left_edge, (LONG)(top_edge +
			 text_attr->ta_YSize), (LONG)(left_edge - 1), (LONG)
					  (top_edge + text_attr->ta_YSize));
	    *(buffer + pos + 1) = c;
	    print_text(ri, win, &buffer[pos + 1], left_edge, top_edge,
				     TEXT_DATA_TYPE_TEXT, flags, text_attr);
	    FreeMem(buffer, len);
	 } else {
	    print_text(ri, win, text, left_edge, top_edge,
				     TEXT_DATA_TYPE_TEXT, flags, text_attr);
	 }
      } else {
	 print_text(ri, win, text, left_edge, top_edge, TEXT_DATA_TYPE_TEXT,
							  flags, text_attr);
      }
   }
}
	/* Print cycle text */

   VOID
print_cycle_text(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct RenderInfo  *ri = gl->gl_RenderInfo;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *rp = win->RPort;
   struct CycleData   *cy = (struct CycleData *)
				   (gl->gl_Gadgets[egad->eg_DataEntry] + 1);
   struct TextAttr    *text_attr = cy->cy_TextAttr;
   struct IntuiText   itext;
   BYTE   *text = cy->cy_ActiveText;
   USHORT flags = (cy->cy_Flags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
		       TEXT_DATA_FLAG_ABSOLUTE_POS | TEXT_DATA_FLAG_COLOR2 :
					       TEXT_DATA_FLAG_ABSOLUTE_POS),
	  left_edge = cy->cy_LeftEdge, top_edge = cy->cy_TopEdge,
	  width = cy->cy_Width, height = cy->cy_Height;

   itext.IText     = (UBYTE *)text;
   itext.ITextFont = text_attr;
   SetDrMd(rp, (LONG)JAM1);
   SetAPen(rp, (LONG)ri->ri_BackPen);
   RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge + width -
					 1), (LONG)(top_edge + height - 1));
   print_text(ri, win, text, left_edge + (SHORT)(width -
		IntuiTextLength(&itext)) / 2, top_edge, TEXT_DATA_TYPE_TEXT,
							  flags, text_attr);
}
	/* Print count text */

   VOID
print_count_text(struct ExtendedGadget  *egad)
{
   struct GadgetList  *gl = egad->eg_GadgetList;
   struct RenderInfo  *ri = gl->gl_RenderInfo;
   struct Window      *win = gl->gl_Window;
   struct RastPort    *rp = win->RPort;
   struct CountData   *co = (struct CountData *)
				   (gl->gl_Gadgets[egad->eg_DataEntry] + 1);
   struct TextAttr    *text_attr = co->co_TextAttr;
   USHORT type = (co->co_Flags & GADGET_DATA_FLAG_COUNT_SIGNED_DEC ?
					     TEXT_DATA_TYPE_NUM_SIGNED_DEC :
					   TEXT_DATA_TYPE_NUM_UNSIGNED_DEC),
	  flags = (co->co_Flags & GADGET_DATA_FLAG_TEXT_COLOR2 ?
		   TEXT_DATA_FLAG_ABSOLUTE_POS | TEXT_DATA_FLAG_PLACE_LEFT |
		       TEXT_DATA_FLAG_COLOR2 : TEXT_DATA_FLAG_ABSOLUTE_POS |
						 TEXT_DATA_FLAG_PLACE_LEFT),
	  left_edge = co->co_LeftEdge, top_edge = co->co_TopEdge,
	  width = co->co_Width, height = co->co_Height;

   if ((egad->eg_Gadget.Flags & SELECTED) && (egad->eg_Gadget.Flags &
					       GADGHIGHBITS) == GADGHCOMP) {
      flags |= TEXT_DATA_FLAG_COMPLEMENT;
      SetAPen(rp, (LONG)(ri->ri_BackPen ^ ((1 << ri->ri_ScreenDepth) - 1)));
   } else {
      SetAPen(rp, (LONG)ri->ri_BackPen);
   }
   SetDrMd(rp, (LONG)JAM1);
   RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge + width -
					 1), (LONG)(top_edge + height - 1));
   print_text(ri, win, (BYTE *)co->co_Value, left_edge + width,
					  top_edge, type, flags, text_attr);
}
	/* Print list view text */

   VOID
print_list_view_text(struct ExtendedGadget  *egad)
{
   struct GadgetList    *gl = egad->eg_GadgetList;
   struct RenderInfo    *ri = gl->gl_RenderInfo;
   struct Window        *win = gl->gl_Window;
   struct RastPort      *rp = win->RPort;
   struct Layer         *layer = rp->Layer;
   struct Region        *region, *old_region;
   struct Rectangle     rect;
   struct ListViewData  *lv = (struct ListViewData *)
				   (gl->gl_Gadgets[egad->eg_DataEntry] + 1);
   struct TextAttr      *text_attr = lv->lv_TextAttr;
   struct Node          *node = lv->lv_TopNode;
   USHORT i, text_width, flags = (lv->lv_Flags &
		      GADGET_DATA_FLAG_TEXT_COLOR2 ? TEXT_DATA_FLAG_COLOR2 |
		    TEXT_DATA_FLAG_ABSOLUTE_POS | TEXT_DATA_FLAG_BACK_FILL :
		    TEXT_DATA_FLAG_ABSOLUTE_POS | TEXT_DATA_FLAG_BACK_FILL),
          left_edge = lv->lv_LeftEdge, top_edge = lv->lv_TopEdge,
	  width = lv->lv_Width, height = lv->lv_Height,
	  entries = lv->lv_ListEntries, visible = lv->lv_VisibleEntries,
	  entry_height = lv->lv_EntryHeight, top = lv->lv_TopEntry;

   /* calc offsets for inner window */
   if (region = NewRegion()) {
      /* install clipping region around list view entry area */
      rect.MinX = left_edge;
      rect.MaxX = left_edge + width - 1;
      rect.MinY = top_edge;
      rect.MaxY = top_edge + height - 1;
      OrRectRegion(region, &rect);
      old_region = InstallClipRegion(layer, region);
      /* print list view entry text with clipping */
      SetDrMd(rp, (LONG)JAM1);
      SetAPen(rp, (LONG)ri->ri_BackPen);
      for (i = visible; i; i--, top++, top_edge += entry_height) {
	 if (top < entries) {
	    text_width = print_text(ri, win, node->ln_Name, left_edge,
			   top_edge, TEXT_DATA_TYPE_TEXT, flags, text_attr);
	    if (text_width < width) {
	       RectFill(rp, (LONG)(left_edge + text_width), (LONG)top_edge,
			    (LONG)(left_edge + width - 1), (LONG)(top_edge +
							 entry_height - 1));
	    }
	    node = node->ln_Succ;
	 } else {
	    break;
	 }
      }
      if (i) {
	 RectFill(rp, (LONG)left_edge, (LONG)top_edge, (LONG)(left_edge +
		       width - 1), (LONG)(top_edge + i * entry_height - 1));
      }
      /* remove clipping region */
      InstallClipRegion(layer, old_region);
      DisposeRegion(region);
   }
}
	/* Proportional gadget support routines (32 bit !!!) */

#asm
; USHORT = calc_prop_body(max_num, size)(d0.l,d1.l)
	XDEF	_calc_prop_body
_calc_prop_body:
	movem.l	d2-d6,-(sp)

	; --- check max_num
	move.l	d0,d6		; save max_num in d6
	beq.s	cpb_maximum	; max_num == 0 ?
	cmp.l	d0,d1		; size > max_num ?
	bhi.s	cpb_maximum

	; --- multiply size [32 bit] by MAXBODY ($ffff) [16 bit] with 48 bit arithmetic
	moveq	#0,d0		; clear shift out reg for size
	move.l	d1,d3		; init result by doing the first add
	moveq	#0,d2		; clear shift out reg of result
	moveq	#16-2,d4	; init shift count (MAXBODY has only 16 bits - one add already done)

cpb_mul_loop:
	; --- shift size
	add.l	d1,d1		; shift left of size
	addx.w	d0,d0		; save shift out

	; --- inc result
	add.l	d1,d3		; add shifted size to result
	addx.w	d0,d2		; add shift out and carry
	dbra	d4,cpb_mul_loop

	; --- divide result [48 bit] by max_num [32 bit] with 48 bit arithmetic
	moveq	#0,d0		; clear result
	moveq	#0,d1		; clear shift out reg for divident
	moveq	#0,d4		; clear sub divisor help reg
	moveq	#32-1,d5	; init shift count

cpb_div_loop:
	; --- shift result and divident
	add.l	d0,d0		; shift result
	add.l	d3,d3		; shift left of lower LONG of divident
	addx.l	d2,d2		; shift left of upper LONG of divident - add shift out
	addx.w	d1,d1		; save shift out

	; --- check if upper LONG of divident large enough to sub divisor from
	tst.w	d1		; any shift out ?
	bne.s	cpb_sub_divisor
	cmp.l	d2,d6		; upper LONG of divident > divisor ?
	bhi.s	cpb_next_div

cpb_sub_divisor:
	sub.l	d6,d2		; sub divisor from upper LONG of divident
	subx.w	d4,d1		; sub underflow (d4 = 0) from shift out of upper LONG of divident
	addq.l	#1,d0		; set bit 0 of result
	
cpb_next_div:
	dbra	d5,cpb_div_loop

	; --- don't return ZERO as prop_body
	tst.w	d0
	bne.s	cpb_exit
	moveq	#1,d0
	
cpb_exit:
	movem.l	(sp)+,d2-d6
	rts

cpb_maximum:
	move.l	#$ffff,d0	; prop_body := MAXBODY
	bra	cpb_exit


; USHORT = calc_prop_pot(max_num, size, num)(d0.l,d1.l,d2.l)
	XDEF	_calc_prop_pot
_calc_prop_pot:
	movem.l	d3-d6,-(sp)

	; --- prepare max_num
	move.l	d0,d6		; save max_num
	beq	cpp_minimum
	sub.l	d1,d6		; max_num -:= size
	bcs	cpp_minimum

	; --- multiply num by MAXPOT ($ffff) by shift num left by 16
	move.l	d2,d3
	swap	d3
	moveq	#0,d2
	move.w	d3,d2		; d2 := upper LONG of result
	clr.w	d3		; d3 := lower LONG of result

	; --- divide result [48 bit] by max_num [32 bit] with 48 bit arithmetic
	moveq	#0,d0		; clear result
	moveq	#0,d1		; clear shift out reg for divident
	moveq	#0,d4		; clear sub divisor help reg
	moveq	#32-1,d5	; init shift count

cpp_div_loop:
	; --- shift result and divident
	add.l	d0,d0		; shift result
	add.l	d3,d3		; shift left of lower LONG of divident
	addx.l	d2,d2		; shift left of upper LONG of divident - add shift out
	addx.w	d1,d1		; save shift out

	; --- check if upper LONG of divident large enough to sub divisor from
	tst.w	d1		; any shift out ?
	bne.s	cpp_sub_divisor
	cmp.l	d2,d6		; upper LONG of divident > divisor ?
	bhi.s	cpp_next_div

cpp_sub_divisor:
	sub.l	d6,d2		; sub divisor from upper LONG of divident
	subx.w	d4,d1		; sub underflow (d4 = 0) from shift out of upper LONG of divident
	addq.l	#1,d0		; set bit 0 of result
	
cpp_next_div:
	dbra	d5,cpp_div_loop

	; --- if pot_value > MAXPOT then return MAXPOT
	cmp.l	#$ffff,d0
	bls	cpp_exit
	move.l	#$ffff,d0	; prop_pot := MAXPOT

cpp_exit:
	movem.l	(sp)+,d3-d6
	rts

cpp_minimum:
	moveq	#0,d0		; prop_pot := 0
	bra	cpp_exit


; ULONG = get_prop_pos(max_num, size, pot_value)(d0.l,d1.l,d2.w)
	XDEF	_get_prop_pos
_get_prop_pos:
	movem.l	d3-d6,-(sp)

	; --- prepare max_num
	sub.l	d1,d0		; max_num -:= size
	bcs	gpp_minimum

	; --- if pot_value ]0..MAXPOT[ then inc pot_value
	move.w	d2,d6		; save pot value
	beq	gpp_minimum	; pot_value == 0 ?
	addq.w	#1,d6		; inc pot_value
	bcs	gpp_exit	; if pot_value == MAXPOT then return (max_num - size)

	; --- multiply max_num [32 bit] by pot_value [16 bit] with 48 bit arithmetic
	moveq	#0,d1		; clear shift out reg for max_num
	moveq	#0,d2		; clear result
	moveq	#0,d3		; clear shift out reg of result
	moveq	#0,d4		; reset bit count
	moveq	#16-1,d5	; init shift count (16 bits from pot_value)
	bra	gpp_check_bit

gpp_mul_loop:
	add.l	d0,d0		; shift left of max_num
	addx.w	d1,d1		; save shift out

gpp_check_bit:
	; --- check bit of pot_value
	btst	d4,d6
	beq	gpp_next_mul	; bit set ?

	; --- if bit set then inc result
	add.l	d0,d2		; add shifted max_num to result
	addx.w	d1,d3		; add shift out and carry

gpp_next_mul:
	addq.w	#1,d4		; inc bit count
	dbra	d5,gpp_mul_loop

	; --- divide result by MAXPOT ($ffff) by shift num right by 16
	move.w	d3,d2		; replace lower word (fractional part) of result by shifted out result
	swap	d2		; d2 := bits 16..47 (integer part) of result
	move.l	d2,d0		; d0 := prop_pos

gpp_exit:
	movem.l	(sp)+,d3-d6
	rts

gpp_minimum:
	moveq	#0,d0		; prop_pos := 0
	bra	gpp_exit

#endasm
