
/*
 *  ASYNCOP.C
 */

typedef unsigned char	ubyte;
typedef unsigned short	uword;
typedef unsigned long	ulong;
typedef struct Message	MSG;
typedef struct MsgPort	PORT;
typedef struct Task	TASK;
typedef struct Node	NODE;

#define ASYMSG	struct _ASYMSG
#define ASYHAN	struct _ASYHAN

ASYMSG {
    MSG msg;
    void (*func)();
    long arg1;
    long arg2;
    long arg3;
};

ASYHAN {
    PORT *rport;    /*	Reply Port	*/
    PORT *port;     /*	Send Port	*/
    long acount;    /*	Messages Sent	*/
    long ccount;    /*	Messages Replied*/
    long a4,a5;     /*	A4 and A5	*/
};

extern TASK *CreateTask();
extern TASK *FindTask();
extern PORT *CreatePort();
extern void *AllocMem();
extern void *GetMsg();

extern void asyhandler();
extern void nop();


ASYHAN *
NewAsyncOp()
{
    register TASK *task;
    register TASK *mytask = FindTask(NULL);
    register ASYHAN *as = AllocMem(sizeof(ASYHAN), MEMF_CLEAR|MEMF_PUBLIC);
    ASYMSG StartupMsg;

    as->rport = CreatePort(NULL, 0);
    PutA4A5(&as->a4);
    task = CreateTask("async.task", mytask->tc_Node.ln_Pri + 1, asyhandler, 4096);
    task->tc_UserData = (APTR)as;

    Signal(task, SIGBREAKF_CTRL_F);
    Wait(1 << as->rport->mp_SigBit);

    return(as);
}


StartAsyncOp(as, func, arg1, arg2, arg3)
register ASYHAN *as;
void (*func)();
{
    register ASYMSG *am = GetMsg(as->rport);    /*  Free Msg List   */

    if (!am) {
	am = AllocMem(sizeof(ASYMSG), MEMF_PUBLIC|MEMF_CLEAR);
	am->msg.mn_ReplyPort = as->rport;
    }
    am->func = func;
    am->arg1 = arg1;
    am->arg2 = arg2;
    am->arg3 = arg3;
    ++as->acount;
    PutMsg(as->port, am);
}

CheckAsyncOp(as, n)
register ASYHAN *as;
unsigned long n;
{
    if (n > as->acount)
	n = as->acount;
    return(n <= as->ccount);
}

/*
 *  acount = #messages sent
 *  ccount = #messages replied
 */

WaitAsyncOp(as, n)
register ASYHAN *as;
unsigned long n;
{
    if (n > as->acount)
	n = as->acount;
    while (n > as->ccount)
	Wait(1 << as->rport->mp_SigBit);
    Forbid();
    as->ccount -= n;
    Permit();
    as->acount -= n;
}

CloseAsyncOp(as)
register ASYHAN *as;
{
    ASYMSG EndMsg;
    ASYMSG *am;

    WaitAsyncOp(as, -1);                /*  Wait for all operations to complete */
    while (am = GetMsg(as->rport))      /*  Free any messages   */
	FreeMem(am, sizeof(ASYMSG));
    EndMsg.func = NULL;
    EndMsg.msg.mn_ReplyPort = as->rport;
    PutMsg(as->port, &EndMsg);
    WaitPort(as->rport);
    GetMsg(as->rport);
    DeletePort(as->rport);
    FreeMem(as, sizeof(*as));
}

static
void
asyhandler()
{
    register ASYHAN *as;
    register ASYMSG *am;

    Wait(SIGBREAKF_CTRL_F);
    as = (ASYHAN *)FindTask(NULL)->tc_UserData;
    as->port = CreatePort(NULL, 0);
    Signal(as->rport->mp_SigTask, 1 << as->rport->mp_SigBit);
    for (;;) {
	WaitPort(as->port);
	am = GetMsg(as->port);
	if (!am->func)
	    break;
	CallAMFunc(&as->a4, &am->func);
	++as->ccount;
	ReplyMsg(am);
    }
    DeletePort(as->port);
    as->port = NULL;
    Forbid();
    ReplyMsg(am);
}

#asm
	    ;
	    ;	load the lw array ptr with a4 & a5

_PutA4A5:   move.l  4(sp),A0
	    move.l  A4,(A0)+
	    move.l  A5,(A0)+
	    rts

_CallAMFunc:
	    move.l  4(sp),A0    ; &a4,a5
	    move.l  8(sp),A1    ; &func,arg1,arg2,arg3
	    movem.l D2/D3/A4/A5/A6,-(sp)
	    move.l  (A0)+,A4
	    move.l  (A0)+,A5
	    move.l  12(A1),-(sp)
	    move.l  8(A1),-(sp)
	    move.l  4(A1),-(sp)
	    move.l  (A1),A1
	    jsr     (A1)
	    add.w   #12,sp
	    movem.l (sp)+,D2/D3/A4/A5/A6
	    rts

#endasm

