/* : ai=0 bk=0 ts=8 */
#include "mm.h"

#define REDP 3L        /* workbench pen colors for Draw and RectFill */
#define BLKP 2L
#define WHTP 1L
#define BLUP 0L

#define SOK        0x001f     /* memory allocation control masks */
#define S1         0x0001
#define S2         0x0002
#define S4         0x0004
#define S8         0x0008
#define SA         0x0010
#define C1         0xfffe
#define C2         0xfffd
#define C4         0xfffb
#define C8         0xfff7
#define CA         0xffef

#define EXTENSION  16L       /* allocation request increment */

#define EVENMASK 0x7ffffffe           /* used to prevent odd address traps */
#define CLIHEIGHT 200L                             /* window scaling stuff */
#define SIZEGAD 9L
#define DRAGBAR 10L
#define BORDER 2L
#define WINW 16L                                   /* initial window width */
#define WINH (long)(CLIHEIGHT - SIZEGAD - DRAGBAR)   /* initial win height */
#define MINH 81L                              /* shortest allowable window */
#define MAXH 576L                              /* longest allowable window */
#define BART 1L                               /* title bar F text location */
#define BARH (long)(WINH - SIZEGAD - DRAGBAR)                /* bar height */
#define BARS (long)(BARH + 1)        /* number of memory items to allocate */
#define BARB (long)(BARH + DRAGBAR - 1)        /* bottom of mercury column */
#define BARE (long)(BARB + BORDER)              /* bar end E text position */
#define BARW (long)(((WINW - (2 * BORDER)) / 3) - 1)  /* 3 memfragmometers */
#define LEFT BORDER                   /* SLOW : since we're syncopated, we */
#define MIDDLE (LEFT + BARW + 1)      /* SLOW-FAST : do it going from bar  */
#define RIGHT (MIDDLE + BARW + 1)     /* FAST :to bar at an irregular pace */
#define RTEXT - 4L        /* offset for three char E/F text from left edge */
#define STARTVAL 64L    /* start by getting enough storage for a few frags */

/* typedef short BOOL */        /* this is in include/exec/types.h */

struct IntuitionBase *IntuitionBase ;
struct Window *window ;
struct IntuiMessage *message;
/* struct IntuiMessage *GetMsg(); */        /* in functions.h */

struct GfxBase *GfxBase ;

extern void InitProjItems();        /* this stuff is in the mminit section */
extern void InitSetupItems();
extern void InitChipItems();
extern void InitChipAItems();
extern void InitSFItems();
extern void InitSFAItems();
extern void InitFastItems();
extern void InitFastAItems();
extern void InitMenu();
extern void StartMenus();

BOOL p_mode = 0;              /* preset frags mode = 0, warps mode = 1 */
long p_rate = 2;              /* preset sample interval, secs see menu */
long p_chip = 512;            /* preset chip mem size, kbytes see menu */
long p_chipa = 0;             /* preset chip mem address, kb  see menu */
long p_sf = 512;              /* preset slowfast mem size, kb see menu */
long p_sfa = 0x3000;          /* preset slowfast mem addr, kb see menu */
long p_fast = 2;              /* preset fast mem size, mbytes see menu */
long p_fasta = 2;             /* preset fast mem addr, mbytes see menu */

static BOOL mode;                    /* uninitialized for mode  menu */
static long delayval;                /* uninitialized for delay menu */
static long cmemsize;                /* uninitialized for chip  menu */
static long cmembase;                /* uninitialized for chipa menu */
static long sfmemsize;               /* uninitialized for sf    menu */
static long sfmembase;               /* uninitialized for sfa   menu */
static long fmemsize;                /* uninitialized for fast  menu */
static long fmembase;                /* uninitialized for fasta menu */

static USHORT log ;
static BOOL things_are_cool = TRUE ; /* tells when to close the window  */
static BOOL intsig = FALSE ;         /* intuition message detector */
static long chip[3], fast[3] ;       /* place to keep mem header pointers */
static ULONG  *chunkv ;              /* demand allocated for frag chunks */
static ULONG  *sizev ;               /* demand allocated for frag sizes */
static ULONG  *csum ;                /* a col's height worth of checksums */
static ULONG  *oldcsum ;             /* same height worth of old checksums */
static USHORT *cell;                 /* 1 cell per pixel height of mercury */
static long x1, x2 ;                 /* used to Draw() memometer segments */
static long frags = STARTVAL ;       /* # of demand allocated frag items */
static long warps = BARS ;           /* # of demand allocated column items */
static long barh = BARH ;            /* EVEN #'d height of mercury column */
static long barb = BARB ;            /* bottom position of mercury column */

/* struct RastPort *rp ; */          /* wonder why we don't need this... ? */

static struct TextAttr myfont = {
   (STRPTR) "topaz.font",
   TOPAZ_EIGHTY,
   0,
   0
};
/* E newsize gadget refresh text */
static char ebuf[4] = " E " ;
static struct IntuiText e_text = {
   BLUP, WHTP,
   JAM2,
   0, BARE,
   &myfont,
   (UBYTE *)ebuf,
   NULL
};
/* F title refresh text */
static char fbuf[4] = " F " ;
static struct IntuiText f_text = {
   BLUP, WHTP,
   JAM2,
   0, BART,
   &myfont,
   (UBYTE *)fbuf,
   &e_text
};
static struct NewWindow newwindow = {
   0,                                        /* left edge */
   10,                                       /* top edge */
   WINW,                                     /* width */
   WINH,                                     /* height */
   0,                                        /* detail pen */
   1,                                        /* block pen */
   MENUPICK | NEWSIZE,                       /* messages */
/* WINDOWDEPTH | WINDOWCLOSE */              /* don't use these gads */
   WINDOWDRAG | WINDOWSIZING | SMART_REFRESH,   /* add a few gadgets */
   NULL,                                     /* no custom gadgets */
   NULL,                                     /* default checkmark */
   (UBYTE *)"F",                             /* title */
   NULL,                                     /* initialize this! */
   NULL,                                     /* use screen bitmap */
   WINW, MINH, WINW, MAXH,                   /* min and max sizes */
   WBENCHSCREEN } ;                          /* use workbench screen */

void initmenus()
{
   (void) InitProjItems();     	        /* Initialize the menu items */
   (void) InitSetupItems();
   (void) InitChipItems();
   (void) InitChipAItems();
   (void) InitSFItems();
   (void) InitSFAItems();
   (void) InitFastItems();
   (void) InitFastAItems();
   (void) InitMenu();
   (void) StartMenus();
   }

void handle_MENUPICK( class, code )
unsigned long class;
unsigned int code;
{
    unsigned int menunum, itemnum, subnum;

    if (code == MENUNULL) return;

    menunum = MENUNUM( code );
    itemnum = ITEMNUM( code );
    subnum  = SUBNUM( code );
    switch( menunum ) {
        case 0:
        switch( itemnum ) {
            case 0:
            WindowToFront(window);
            break;

            case 1:
            WindowToBack(window);
            break;

            case 2:
            things_are_cool = FALSE;
            break;
            } /* end of switch ( Project itemnum ) */
        break;

        case 1:
        switch( itemnum ) {
            case 0:
            switch( subnum ) {
                case 0:
                mode = FALSE ;
                break;

                case 1:
                mode = TRUE ;
                break;
                /* end of switch ( Mode subnum ) */
                }
            break;
            case 1:
            switch( subnum ) {
                case 0:
                delayval = 36;
                break;

                case 1:
                delayval = 72;
                break;

                case 2:
                delayval = 180;
                break;

                case 3:
                delayval = 360;
                break;
                /* end of switch ( Freq subnum ) */
                }
            break;
            /* end of switch ( Setup itemnum ) */
            }
        break;

        case 2:
        switch( itemnum ) {
            case 0:
            cmemsize = 0x000000;
            break;

            case 1:
            cmemsize = 0x040000;
            break;

            case 2:
            cmemsize = 0x080000;
            break;

            case 3:
            cmemsize = 0x100000;
            break;

            case 4:
            cmemsize = 0x200000;
            break;
            }
            /* end of switch ( Chip Size itemnum ) */
        break;

        case 3:
        switch( itemnum ) {
            case 0:
            cmembase = 0x000000;
            break;

            case 1:
            cmembase = 0x040000;
            break;

            case 2:
            cmembase = 0x080000;
            break;

            case 3:
            cmembase = 0x100000;
            break;
            }
            /* end of switch ( Chip Addr itemnum ) */
        break;

        case 4:
        switch( itemnum ) {
            case 0:
            sfmemsize = 0x000000;
            break;

            case 1:
            sfmemsize = 0x080000;
            break;

            case 2:
            sfmemsize = 0x100000;
            break;

            case 3:
            sfmemsize = 0x180000;
            break;
            }
            /* end of switch ( SF Size itemnum ) */
        break;

        case 5:
        switch( itemnum ) {
            case 0:
            sfmembase = 0xc00000;
            break;

            case 1:
            sfmembase = 0xc80000;
            break;

            case 2:
            sfmembase = 0xd00000;
            break;

            case 3:
            sfmembase = 0xd80000;
            break;
            }
            /* end of switch ( SF Addr itemnum ) */
        break;

        case 6:
        switch( itemnum ) {
            case 0:
            fmemsize = 0x000000;
            break;

            case 1:
            fmemsize = 0x080000;
            break;

            case 2:
            fmemsize = 0x100000;
            break;

            case 3:
            fmemsize = 0x200000;
            break;

            case 4:
            fmemsize = 0x400000;
            break;

            case 5:
            fmemsize = 0x600000;
            break;

            case 6:
            fmemsize = 0x800000;
            break;
            }
            /* end of switch ( Fast Size itemnum ) */
        break;

        case 7:
        switch( itemnum ) {
            case 0:
            fmembase = 0x200000;
            break;

            case 1:
            fmembase = 0x300000;
            break;

            case 2:
            fmembase = 0x400000;
            break;

            case 3:
            fmembase = 0x500000;
            break;

            case 4:
            fmembase = 0x600000;
            break;

            case 5:
            fmembase = 0x700000;
            break;

            case 6:
            fmembase = 0x800000;
            break;

            case 7:
            fmembase = 0x900000;
            break;
            }
            /* end of switch ( Fast Addr itemnum ) */
        break;
        }
        /* end of switch ( menunum ) */
}

void wrack_sploot() /* this subprogram cashes in the chips in bad times */
    {
    if (log != 0) {
        if (log & SA) {
            (void) FreeMem(oldcsum, (long)(sizeof(ULONG) * warps)) ;
            log = log & CA ;
            }
        if (log & S8) {
            (void) FreeMem(csum, (long)(sizeof(ULONG) * warps)) ;
            log = log & C8 ;
            }
        if (log & S4)  {
            (void) FreeMem(sizev, (long)(sizeof(long) * frags)) ;
            log = log & C4 ;
            }
        if (log & S2)  {
            (void) FreeMem(chunkv, (long)(sizeof(long) * frags)) ;
            log = log & C2 ;
            }
        if (log & S1) {
            (void) FreeMem(cell, (long)(sizeof(USHORT) * warps)) ;
            log = log & C1 ;
            }
        frags = 0;
        warps = 0;
        }
}

void handle_NEWSIZE()        /* oh rats, the user found the size gadget... */
    {
    if (barh != window->Height)  {  /* cash in the old memory allocations  */
        if (log & SA)  {
            (void) FreeMem(oldcsum, (long)(sizeof(ULONG) * warps)) ;
            log = log & CA ;
            }
        if (log & S8)  {
            (void) FreeMem(csum, (long)(sizeof(ULONG) * warps)) ;
            log = log & C8 ;
            }
        if (log & S1)  {
            (void) FreeMem(cell, (long)(sizeof(USHORT) * warps)) ;
            log = log & C1 ;
            }
        barh = window->Height - SIZEGAD - DRAGBAR ;  /* recalculate height */
        barb = window->Height - SIZEGAD - 1 ; /* get new bar base position */
        warps = barh + 1 ;       /* number of storage cells to be reserved */
        if ((cell = (USHORT *)AllocMem((long)sizeof(USHORT) * warps,
                                        MEMF_PUBLIC)) != 0L ) log = log | S1;
        if ((csum = (ULONG *)AllocMem((long)sizeof(ULONG) * warps,
                                        MEMF_PUBLIC)) != 0L ) log = log | S8;
        if ((oldcsum = (ULONG *)AllocMem((long)sizeof(ULONG) * warps,
                                        MEMF_PUBLIC)) != 0L ) log = log | SA;
        if (log != SOK)  {      /* bail out if we didn't get an allocation */
            wrack_sploot() ;
            things_are_cool = FALSE ;     /* tell _main that we're leaving */
            }
        e_text.TopEdge = barb + BORDER ;        /* set the E text position */
        }
}

void handle_MESSAGES()  {
    unsigned long class;
    unsigned int code, qual;

    /* Wait (1L << window->UserPort->mp_SigBit); this is rediculous! */
    while ((message=(struct IntuiMessage *)GetMsg(window->UserPort))
                                                                != 0L) {
        class = message->Class ;
        code = message->Code ;
        qual = message->Qualifier ;
        ReplyMsg ;
        switch( class ) {
            case NEWSIZE:
            intsig = TRUE ;			/* resize when done here */
            break ;

            case MENUPICK:
            handle_MENUPICK( class, code ) ;    /* do menus ASAP */
            break ;
            }
        }
}

void memometer()                /* this subprogram draws the bar graphs */
    {
    long i;
    long y;

    i = 0 ;
    while ((i < barh) && (intsig == FALSE)) {
        y = barb - i ;
        SetAPen(window->RPort, (long)cell[i]);
        Move(window->RPort, x1, y);
        Draw(window->RPort, x2, y);
        (void) handle_MESSAGES() ;  /* go see if the user wants anything */
        i++ ;
        }
}

void shake()    /* this subprogram slings down the mercury column */
    {
    int i;

    for (i = 0; i < barh; i++)  {
        cell[i] = REDP ;
        }
}

void lockout()  /* this subprogram renders a whole column in border colors */
    {
    (void) handle_MESSAGES() ;        /* go see if the user wants anything */
    SetAPen(window->RPort, WHTP) ;    /* set in the border color */
    RectFill(window->RPort, x1, (barb - barh + 1), x2, barb) ; /* fill in */
}

void updatewarps( wf, membase, memseg )  /* this subprogram finds changes */
short wf;
long membase, memseg ;
    {
    register long i ;
    register ULONG r ;
    register ULONG delta ;
    register ULONG *a ;

    if (memseg == 0) {
        (void) lockout() ;
        return ;
        }
    else (void) shake() ;

    delta = (ULONG)(memseg & EVENMASK) ;  /* try to avoid odd address trap */
    r = (ULONG)(membase & EVENMASK) ;     /* prefer wrong address to death */
    for (i = 0; i < barh; i++)  {
        csum[i] = 0 ;
        }
    for (i = 0; i < barh; i++)  {
        for (a = (ULONG *)r; a < (ULONG *)(r + delta); a++)  {
            csum[i] += *a ;
            }
        if (csum[i] == 0) cell[i] = cell[i] & BLUP ;
        if (csum[i] == oldcsum[i])  cell[i] = cell[i] & BLKP ;
        oldcsum[i] = csum[i] ;
        r += delta ;
        }
    (void) memometer();
}

void updatescreen( wf, membase, memseg )
short wf;
long membase, memseg;
    {
    register long size ;
    register struct MemHeader *hdr ;
    register struct MemChunk *chunk ;
    extern struct ExecBase *SysBase ;
    register long *which ;                /* active memlist chunk pointer */
    register long newlimit ;  /* number of chunks, incl 1 null chunk, + 1 */
    register long newfrags ;  /* number of chunks, incl null chunks, + 1  */
    long newsize ;  /* size of next request for chunkv/sizev memory, + 1  */
    int i, j, k, l ;
    int length ;       /* length is number of chunks to be processed, + 1 */
    BOOL cf, nf ;      /* cf is "chip flag" nf is "null chunk found" flag */

    if (memseg == 0) {
        (void) lockout() ;
        return;
        }
    else (void) shake() ;

    for (i = 0; i < 3; i++) {
        chip[i] = 0;
        fast[i] = 0;
        }
    for (i = 0; i < frags; i++) {
        chunkv[i] = 0;
        sizev[i] = 0;
        }
    newfrags = 0 ;
    newlimit = 0 ;
    nf = FALSE;
    Forbid() ;
    hdr = (struct MemHeader *) SysBase->MemList.lh_Head ;
    while (hdr->mh_Node.ln_Succ) {
        if (hdr->mh_Attributes & MEMF_CHIP) {
            which = chip ;
            cf = TRUE;
            }
        else {
            which = fast ;
            cf = FALSE;
            }
        if (((cf == TRUE) && (wf == 0)) || ((cf == FALSE) && (wf > 0)))  {
            for (chunk = hdr->mh_First; chunk; chunk = chunk->mc_Next) {
                if (which[1] < frags)  chunkv[which[1]++] = (unsigned long)chunk ;
                size = chunk->mc_Bytes ;
                if (which[2] < frags)  sizev[which[2]++] = (unsigned long)size;
                *which += size ;
                if (nf == FALSE)  {
                    newlimit++ ;
                    if (chunkv[newfrags] == NULL)  {
                        nf = TRUE;
                        }
                    }
                newfrags++ ;
            }
        }
        hdr = (struct MemHeader *)hdr->mh_Node.ln_Succ ;
        }
    Permit() ;
    length = frags ;
    if (newlimit < frags)  length = newlimit ;
    for (i = 0; i < length; i++)  {
        chunkv[i] -= membase;   /* chunkv is now array offset from base   */
        sizev[i] += chunkv[i];  /* and sizev is now address of array top  */
        chunkv[i] = chunkv[i] / memseg;  /* this is the number of pixels  */
        sizev[i] = sizev[i] / memseg;       /* and this is the top pixel  */
        if (chunkv[i] < 0) chunkv[i] = 0 ;  /* we do some bounds checking */
        if (sizev[i] < 0) sizev[i] = 0 ;
        if (chunkv[i] >= barh) chunkv[i] = barh - 1 ;
        if (sizev[i] >= barh) sizev[i] = barh - 1 ;
        if (sizev[i] - chunkv[i] < 0)  sizev[i] = chunkv[i] ;
        j = chunkv[i] ; /* from now on it will be less confusing if */
        k = sizev[i] ;  /* we assign some variables for this stuff  */
        if (sizev[i] - chunkv[i] == 0)  {
            cell[j] = cell[j] & BLUP ;
            }
        else  {
            for (l = j; l < k; l++)  {
                cell[l] = cell[l] & BLKP ;
                }
            }
        }
    (void) memometer();
    newsize = frags ;
    while (newsize < newlimit)  {
        newsize += EXTENSION ;
        }
    if (newlimit > frags)  {
        (void) FreeMem(chunkv, (long)(sizeof(long) * frags));
        if ((chunkv = (unsigned long *)AllocMem((long)sizeof(long) * newsize,
                                        MEMF_PUBLIC)) == 0L )  {
            log = log & C2 ;
            }
        (void) FreeMem(sizev, (long)(sizeof(long) * frags));
        if ((sizev = (unsigned long *)AllocMem((long)sizeof(long) * newsize,
                                        MEMF_PUBLIC)) == 0L ) {
            (void) FreeMem(chunkv, (long)(sizeof(long) * newsize));
            log = log & C4 ;
            }
        if (log != SOK)  {
            wrack_sploot() ;
            things_are_cool = FALSE ;
            }
        else frags = newsize ;
        }
}

main() {
    int i;
    short wf;
    long cmemseg;
    long sfmemseg;
    long fmemseg;
    long membase;
    long memseg;
    long memsize;


    log = 0 ;
    if ((cell = (USHORT *)AllocMem((long)sizeof(USHORT) * BARS,
                                        MEMF_PUBLIC)) != 0L ) log = log | S1;
    if ((chunkv = (ULONG *)AllocMem((long)sizeof(ULONG) * STARTVAL,
                                        MEMF_PUBLIC)) != 0L ) log = log | S2;
    if ((sizev = (ULONG *)AllocMem((long)sizeof(ULONG) * STARTVAL,
                                        MEMF_PUBLIC)) != 0L ) log = log | S4;
    if ((csum = (ULONG *)AllocMem((long)sizeof(ULONG) * BARS,
                                        MEMF_PUBLIC)) != 0L ) log = log | S8;
    if ((oldcsum = (ULONG *)AllocMem((long)sizeof(ULONG) * BARS,
                                        MEMF_PUBLIC)) != 0L ) log = log | SA;
    if (log != SOK)  {
        wrack_sploot() ;
        Exit() ;
        }
    if ((IntuitionBase = (struct IntuitionBase *)OpenLibrary(
            "intuition.library",33L)) != NULL &&
            (window=OpenWindow(&newwindow)) != NULL)  {
        if ((GfxBase = (struct GfxBase *)OpenLibrary(
                "graphics.library", 0L)) != NULL)  {
            (void) initmenus();
            /* rp = window->RPort ; */
            wf = 0;
            mode = p_mode ;
            delayval = p_rate * 36 ;
            cmemsize = p_chip << 10 ;
            cmembase = p_chipa << 10 ;
            sfmemsize = p_sf << 10 ;
            sfmembase = p_sfa << 10 ;
            if (p_fast >= 512)  fmemsize = p_fast << 10 ;
            else fmemsize = p_fast << 20 ;
            fmembase = p_fasta << 20 ;
            while (things_are_cool == TRUE) {
                cmemseg = cmemsize / barh ;
                sfmemseg = sfmemsize / barh ;
                fmemseg = fmemsize / barh ;
                switch ( wf ) {
                    case 0:
                    x1 = LEFT ;
                    membase = cmembase ;
                    memseg = cmemseg ;
                    memsize = cmemsize ;
                    break;

                    case 1:
                    x1 = MIDDLE ;
                    membase = sfmembase ;
                    memseg = sfmemseg ;
                    memsize = sfmemsize ;
                    break;

                    case 2:
                    x1 = RIGHT ;
                    membase = fmembase ;
                    memseg = fmemseg ;
                    memsize = fmemsize ;
                    break;
                    }
                x2 = x1 + BARW ;
                if (mode == TRUE)  {
                    (void) updatewarps( wf, membase, memseg ) ;
                    }
                if (mode == FALSE)  {
                    (void) updatescreen( wf, membase, memseg ) ;
                    }
                if (intsig == TRUE)  {
                    handle_NEWSIZE() ;
                    intsig = FALSE ;
                    }
                if (wf == 2)  {
                    PrintIText(window->RPort,&f_text,RTEXT,0L) ;
                    Delay(delayval) ;
                    }
                wf++ ;
                if (wf >= 3)  wf = 0 ;
                } /* end of while ( things_are_cool) */
            wrack_sploot() ;
            }
        if (GfxBase)    CloseLibrary(GfxBase) ;
        }
    if (window)         CloseWindow(window) ;
    if (IntuitionBase)  CloseLibrary(IntuitionBase) ;
}
_cli_parse() {}
_wb_parse() {}
