/*

Name:
DRV_P14.C

Description:
Mikmod driver for output to 14-bit Paula channels.
No doubt about it..this sucked bad!

Portability:

This code is Amiga chipset specific.

This port was done on 30/01/97 by Tony Bybell.
bybell@cse.psu.edu

*/

#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <graphics/gfxbase.h>
#include <proto/all.h>
#include <stdlib.h>
#include <stdio.h>
#include <exec/interrupts.h>
#include <hardware/intbits.h>

#define  AMIGA_PAULADRV
#include "mikmod.h"

#define PAULA14BUFFERSIZE 32000

int CXBRK(void) /* This ensures our own break routine is used */
{
if((md_driver==&drv_p14)||(md_driver==&drv_pau)) return(0);
else
printf("** BREAK **\n");
exit(0);
}


char interruptname[]="paula14mod.device";
extern void irqhandler();

BYTE mainsignum=-1;
struct Interrupt *newirq=NULL;
struct Interrupt *oldirq=NULL;


struct  GfxBase *p14_LocalGfxBase;
UBYTE   whichchannell_HI[]={1};
UBYTE   whichchannelr_HI[]={2};

UBYTE   whichchannell_LO[]={8};
UBYTE   whichchannelr_LO[]={4};

int     p14_DBFlag=0;
short   p14_IsSixteenBit=0;
short   p14_IsStereo=0;

struct  IOAudio *AIOptrl_HI=NULL;
struct  MsgPort *portl_HI=NULL;
struct  Message *msgl_HI=NULL;
ULONG   devicel_HI;

struct  IOAudio *AIOptrr_HI=NULL;
struct  MsgPort *portr_HI=NULL;
struct  Message *msgr_HI=NULL;
ULONG   devicer_HI;



struct  IOAudio *AIOptrl_LO=NULL;
struct  MsgPort *portl_LO=NULL;
struct  Message *msgl_LO=NULL;
ULONG   devicel_LO;

struct  IOAudio *AIOptrr_LO=NULL;
struct  MsgPort *portr_LO=NULL;
struct  Message *msgr_LO=NULL;
ULONG   devicer_LO;


LONG    p14_clock=3579545;
LONG    p14_slen=0;
short   p14_slen2,p14_period;

char *PAULA14_DMABUF=NULL;
char *PAULA14_LEFT_HI[2], *PAULA14_RIGHT_HI[2];
char *PAULA14_LEFT_LO[2], *PAULA14_RIGHT_LO[2];


static void Paula14_Exit(void)
{
int i;

VC_Exit();

        if(newirq)
                {
                SetIntVector(INTB_AUD0,oldirq);
                FreeMem(newirq,sizeof(struct Interrupt));
                newirq=NULL;
                }        

        if(mainsignum)
                {
                FreeSignal(mainsignum);
                mainsignum=NULL;
                }


        if(portr_HI)
                {
                GetMsg(portr_HI);
                DeletePort(portr_HI);
                portr_HI=NULL;
                }

        if(AIOptrr_HI)
                {
                CloseDevice((struct IORequest *)AIOptrr_HI);
                FreeMem(AIOptrr_HI,sizeof(struct IOAudio));
                AIOptrr_HI=NULL;
                }
                
        if(portl_HI)
                {
                GetMsg(portl_HI);
                DeletePort(portl_HI);
                portl_HI=NULL;
                }

        if(AIOptrl_HI)
                {
                CloseDevice((struct IORequest *)AIOptrl_HI);
                FreeMem(AIOptrl_HI,sizeof(struct IOAudio));
                AIOptrl_HI=NULL;
                }


        if(portr_LO)
                {
                GetMsg(portr_LO);
                DeletePort(portr_LO);
                portr_LO=NULL;
                }

        if(AIOptrr_LO)
                {
                CloseDevice((struct IORequest *)AIOptrr_LO);
                FreeMem(AIOptrr_LO,sizeof(struct IOAudio));
                AIOptrr_LO=NULL;
                }
                
        if(portl_LO)
                {
                GetMsg(portl_LO);
                DeletePort(portl_LO);
                portl_LO=NULL;
                }

        if(AIOptrl_LO)
                {
                CloseDevice((struct IORequest *)AIOptrl_LO);
                FreeMem(AIOptrl_LO,sizeof(struct IOAudio));
                AIOptrl_LO=NULL;
                }


        for(i=0;i<2;i++)
                {
                if(PAULA14_LEFT_HI[i])FreeMem(PAULA14_LEFT_HI[i], p14_slen);
                if(PAULA14_RIGHT_HI[i])FreeMem(PAULA14_RIGHT_HI[i],p14_slen);
                PAULA14_LEFT_HI[i]=PAULA14_RIGHT_HI[i]=NULL;

                if(PAULA14_LEFT_LO[i])FreeMem(PAULA14_LEFT_LO[i], p14_slen);
                if(PAULA14_RIGHT_LO[i])FreeMem(PAULA14_RIGHT_LO[i],p14_slen);
                PAULA14_LEFT_LO[i]=PAULA14_RIGHT_LO[i]=NULL;
                }

        if(PAULA14_DMABUF)
                {
                FreeMem(PAULA14_DMABUF,  PAULA14BUFFERSIZE);
                PAULA14_DMABUF=NULL;
                }
}


static int Paula14_IsThere(void)
{
return 1;       /* Paula is always there */
}


struct Ourdata
        {
        long mainsig;
        struct Task *maintask;
        void *oldrutin;
        void *olddata;
        } ourdata;


short *DMAPTR=(short *)0xdff096;


static int Paula14_Init(void)
{
char *filter=(char *)0xbfe001;

*filter|=0x02;  /* Turn off the low pass filter */


if(-1==(mainsignum=AllocSignal(-1)))
        {
        printf("Could not allocate signal!\n");
        Paula14_Exit();
        return(0);
        }

if(!(newirq=(struct Interrupt *)AllocMem(sizeof(struct Interrupt),
        MEMF_PUBLIC|MEMF_CLEAR)))
        {
        printf("Could not allocate interrupt struct mem!\n");
        Paula14_Exit();
        return(0);
        }

if(p14_IsSixteenBit=(md_mode&DMODE_16BITS)) 
        p14_slen=PAULA14BUFFERSIZE/4; else
        p14_slen=PAULA14BUFFERSIZE/2;

if(!(p14_IsStereo=(md_mode&DMODE_STEREO))) p14_slen<<=1;

p14_slen2=p14_slen>>1;  /* for audxlen */

PAULA14_DMABUF=(char *)AllocMem(PAULA14BUFFERSIZE,MEMF_PUBLIC);
PAULA14_LEFT_HI[0]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);
PAULA14_LEFT_HI[1]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);
PAULA14_RIGHT_HI[0]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);
PAULA14_RIGHT_HI[1]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);

PAULA14_LEFT_LO[0]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);
PAULA14_LEFT_LO[1]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);
PAULA14_RIGHT_LO[0]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);
PAULA14_RIGHT_LO[1]=(char *)AllocMem(p14_slen,MEMF_CHIP|MEMF_CLEAR);

if((!PAULA14_DMABUF)||(!PAULA14_LEFT_HI[0])||(!PAULA14_LEFT_HI[1])
        ||(!PAULA14_RIGHT_HI[0])||(!PAULA14_RIGHT_HI[1]))
        {
        printf("Could not malloc buffer memory!\n");
        Paula14_Exit();
        return(0);
        }

if((!PAULA14_LEFT_LO[0])||(!PAULA14_LEFT_LO[1])
        ||(!PAULA14_RIGHT_LO[0])||(!PAULA14_RIGHT_LO[1]))
        {
        printf("Could not malloc buffer memory!\n");
        Paula14_Exit();
        return(0);
        }


if(!(p14_LocalGfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L)))
        {
        printf("Could not open graphics.library!\n");
        Paula14_Exit();
        return(0);
        }

if(p14_LocalGfxBase->DisplayFlags & PAL) p14_clock=3546895; else p14_clock=3579545;
CloseLibrary((struct Library *)p14_LocalGfxBase);

if(!(AIOptrl_HI=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR)))
        {
        printf("Could not alloc IOAudio structure mem!\n");
        Paula14_Exit();
        return(0);
        }

if(!(portl_HI=CreatePort(0,0)))
        {
        printf("Could not create reply port!\n");
        Paula14_Exit();
        return(0);
        }

AIOptrl_HI->ioa_Request.io_Message.mn_ReplyPort     =       portl_HI;
AIOptrl_HI->ioa_Request.io_Message.mn_Node.ln_Pri   =       0;
AIOptrl_HI->ioa_Request.io_Command                  =       ADCMD_ALLOCATE;
AIOptrl_HI->ioa_Request.io_Flags                    =       ADIOF_NOWAIT;
AIOptrl_HI->ioa_AllocKey                            =       0;
AIOptrl_HI->ioa_Data                                =       whichchannell_HI;
AIOptrl_HI->ioa_Length                              =       sizeof(whichchannell_HI);

if(devicel_HI=OpenDevice("audio.device",0L,(struct IORequest *)AIOptrl_HI,0L))
        {
        printf("Could not grab audio.device!\n");
        Paula14_Exit();
        return(0);
        }


if(!(AIOptrr_HI=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR)))
        {
        printf("Could not alloc IOAudio structure mem!\n");
        Paula14_Exit();
        return(0);
        }

if(!(portr_HI=CreatePort(0,0)))
        {
        printf("Could not create reply port!\n");
        Paula14_Exit();
        return(0);
        }

AIOptrr_HI->ioa_Request.io_Message.mn_ReplyPort     =       portr_HI;
AIOptrr_HI->ioa_Request.io_Message.mn_Node.ln_Pri   =       0;
AIOptrr_HI->ioa_Request.io_Command                  =       ADCMD_ALLOCATE;
AIOptrr_HI->ioa_Request.io_Flags                    =       ADIOF_NOWAIT;
AIOptrr_HI->ioa_AllocKey                            =       0;
AIOptrr_HI->ioa_Data                                =       whichchannelr_HI;
AIOptrr_HI->ioa_Length                              =       sizeof(whichchannelr_HI);

if(devicer_HI=OpenDevice("audio.device",0L,(struct IORequest *)AIOptrr_HI,0L))
        {
        printf("Could not grab audio.device!\n");
        Paula14_Exit();
        return(0);
        }

AIOptrl_HI->ioa_Request.io_Message.mn_ReplyPort     =       portl_HI;
AIOptrl_HI->ioa_Request.io_Command                  =       CMD_WRITE;
AIOptrl_HI->ioa_Request.io_Flags                    =       ADIOF_PERVOL;
AIOptrl_HI->ioa_Data                                =       (BYTE *)PAULA14_LEFT_HI[0];
AIOptrl_HI->ioa_Length                              =       p14_slen;
AIOptrl_HI->ioa_Period                              =       p14_period=p14_clock/md_mixfreq;
AIOptrl_HI->ioa_Volume                              =       64;
AIOptrl_HI->ioa_Cycles                              =       1;

AIOptrr_HI->ioa_Request.io_Message.mn_ReplyPort     =       portr_HI;
AIOptrr_HI->ioa_Request.io_Command                  =       CMD_WRITE;
AIOptrr_HI->ioa_Request.io_Flags                    =       ADIOF_PERVOL;
AIOptrr_HI->ioa_Data                                =       (BYTE *)PAULA14_RIGHT_HI[0];
AIOptrr_HI->ioa_Length                              =       p14_slen;
AIOptrr_HI->ioa_Period                              =       p14_period;
AIOptrr_HI->ioa_Volume                              =       64;
AIOptrr_HI->ioa_Cycles                              =       1;



if(!(AIOptrl_LO=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR)))
        {
        printf("Could not alloc IOAudio structure mem!\n");
        Paula14_Exit();
        return(0);
        }

if(!(portl_LO=CreatePort(0,0)))
        {
        printf("Could not create reply port!\n");
        Paula14_Exit();
        return(0);
        }

AIOptrl_LO->ioa_Request.io_Message.mn_ReplyPort     =       portl_LO;
AIOptrl_LO->ioa_Request.io_Message.mn_Node.ln_Pri   =       0;
AIOptrl_LO->ioa_Request.io_Command                  =       ADCMD_ALLOCATE;
AIOptrl_LO->ioa_Request.io_Flags                    =       ADIOF_NOWAIT;
AIOptrl_LO->ioa_AllocKey                            =       0;
AIOptrl_LO->ioa_Data                                =       whichchannell_LO;
AIOptrl_LO->ioa_Length                              =       sizeof(whichchannell_LO);

if(devicel_LO=OpenDevice("audio.device",0L,(struct IORequest *)AIOptrl_LO,0L))
        {
        printf("Could not grab audio.device!\n");
        Paula14_Exit();
        return(0);
        }


if(!(AIOptrr_LO=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR)))
        {
        printf("Could not alloc IOAudio structure mem!\n");
        Paula14_Exit();
        return(0);
        }

if(!(portr_LO=CreatePort(0,0)))
        {
        printf("Could not create reply port!\n");
        Paula14_Exit();
        return(0);
        }

AIOptrr_LO->ioa_Request.io_Message.mn_ReplyPort     =       portr_LO;
AIOptrr_LO->ioa_Request.io_Message.mn_Node.ln_Pri   =       0;
AIOptrr_LO->ioa_Request.io_Command                  =       ADCMD_ALLOCATE;
AIOptrr_LO->ioa_Request.io_Flags                    =       ADIOF_NOWAIT;
AIOptrr_LO->ioa_AllocKey                            =       0;
AIOptrr_LO->ioa_Data                                =       whichchannelr_LO;
AIOptrr_LO->ioa_Length                              =       sizeof(whichchannelr_LO);

if(devicer_LO=OpenDevice("audio.device",0L,(struct IORequest *)AIOptrr_LO,0L))
        {
        printf("Could not grab audio.device!\n");
        Paula14_Exit();
        return(0);
        }

AIOptrl_LO->ioa_Request.io_Message.mn_ReplyPort     =       portl_LO;
AIOptrl_LO->ioa_Request.io_Command                  =       CMD_WRITE;
AIOptrl_LO->ioa_Request.io_Flags                    =       ADIOF_PERVOL;
AIOptrl_LO->ioa_Data                                =       (BYTE *)PAULA14_LEFT_LO[0];
AIOptrl_LO->ioa_Length                              =       p14_slen;
AIOptrl_LO->ioa_Period                              =       p14_period;
AIOptrl_LO->ioa_Volume                              =       1;
AIOptrl_LO->ioa_Cycles                              =       1;

AIOptrr_LO->ioa_Request.io_Message.mn_ReplyPort     =       portr_LO;
AIOptrr_LO->ioa_Request.io_Command                  =       CMD_WRITE;
AIOptrr_LO->ioa_Request.io_Flags                    =       ADIOF_PERVOL;
AIOptrr_LO->ioa_Data                                =       (BYTE *)PAULA14_RIGHT_LO[0];
AIOptrr_LO->ioa_Length                              =       p14_slen;
AIOptrr_LO->ioa_Period                              =       p14_period;
AIOptrr_LO->ioa_Volume                              =       1;
AIOptrr_LO->ioa_Cycles                              =       1;


ourdata.mainsig=1L<<mainsignum;
ourdata.maintask=FindTask(NULL);        

newirq->is_Node.ln_Type=NT_INTERRUPT;
newirq->is_Node.ln_Name=interruptname;
newirq->is_Node.ln_Pri=0;
newirq->is_Data=&ourdata;
newirq->is_Code=irqhandler;

Disable();
oldirq=SetIntVector(INTB_AUD0,newirq);
ourdata.oldrutin=oldirq->is_Code;
ourdata.olddata =oldirq->is_Data;
*DMAPTR=(short)0x000f;
Enable();

printf("Replaced: audio.device with %s\n",interruptname);

if(!oldirq)exit(0);

if(!VC_Init()){ return 0; }
return 1;
}


static void Paula14_Update(void)
{
short *sptr;
long  *lptr;

short i;

int db;
unsigned char *buf, *left, *right, *leftlo, *rightlo;
ULONG signals;

Disable();

sptr=(short *)0xdff09a;
*sptr=(short)(INTF_SETCLR|INTF_AUD0);

lptr=(long *)0xdff0a0;
*lptr=(long)PAULA14_LEFT_HI[p14_DBFlag];
sptr=(short *)0xdff0a4;
*sptr++=p14_slen2;
*sptr++=p14_period;
*sptr++=64;

lptr=(long *)0xdff0d0;
*lptr=(long)PAULA14_LEFT_LO[p14_DBFlag];
sptr=(short *)0xdff0d4;
*sptr++=p14_slen2;
*sptr++=p14_period;
*sptr++=1;

if(p14_IsStereo)
{
lptr=(long *)0xdff0b0;
*lptr=(long)PAULA14_RIGHT_HI[p14_DBFlag];
sptr=(short *)0xdff0b4;
*sptr++=p14_slen2;
*sptr++=p14_period;
*sptr++=64;

lptr=(long *)0xdff0c0;
*lptr=(long)PAULA14_RIGHT_LO[p14_DBFlag];
sptr=(short *)0xdff0c4;
*sptr++=p14_slen2;
*sptr++=p14_period;
*sptr++=1;
}
else
{
lptr=(long *)0xdff0b0;
*lptr=(long)PAULA14_LEFT_HI[p14_DBFlag];
sptr=(short *)0xdff0b4;
*sptr++=p14_slen2;
*sptr++=p14_period;
*sptr++=64;

lptr=(long *)0xdff0c0;
*lptr=(long)PAULA14_LEFT_LO[p14_DBFlag];
sptr=(short *)0xdff0c4;
*sptr++=p14_slen2;
*sptr++=p14_period;
*sptr++=1;
}

*DMAPTR=(short)0x800f;
Enable();

signals=Wait(ourdata.mainsig|SIGBREAKF_CTRL_C);      

db=1-p14_DBFlag;
buf=PAULA14_DMABUF;
left=PAULA14_LEFT_HI[db];
right=PAULA14_RIGHT_HI[db];

leftlo=PAULA14_LEFT_LO[db];
rightlo=PAULA14_RIGHT_LO[db];

	VC_WriteBytes(PAULA14_DMABUF,PAULA14BUFFERSIZE);

if(p14_IsStereo)
{
if(p14_IsSixteenBit)
        for(i=0;i<p14_slen;i++)
                {
                *left++=*buf++;
                *leftlo++=(*buf++)>>2;
                *right++=*buf++;
                *rightlo++=(*buf++)>>2;
                }
        else
        {
        for(i=0;i<p14_slen;i++)
                {
                *left++=(*buf++)^0x80;  
                *leftlo++=0x00;
                *right++=(*buf++)^0x80; 
                *rightlo++=0x00;
                }
        }
}
else
{
if(p14_IsSixteenBit)
        for(i=0;i<p14_slen;i++)
                {
                *left++=*buf++;
                *leftlo++=(*buf++)>>2;
                }
        else
        {
        for(i=0;i<p14_slen;i++)
                {
                *left++=(*buf++)^0x80;  
                *leftlo++=0x00;
                }
        }
}
p14_DBFlag=db;


if(signals&SIGBREAKF_CTRL_C)
        {
        printf("** BREAK **\n");
        Paula14_Exit();
        exit(0);
        }

}



DRIVER drv_p14={
	NULL,
	"14-bit Paula sound output",
	"Paula 14-bit output driver v1.1a",
	Paula14_IsThere,
	VC_SampleLoad,
	VC_SampleUnload,
	Paula14_Init,
	Paula14_Exit,
	VC_PlayStart,
	VC_PlayStop,
	Paula14_Update,
	VC_VoiceSetVolume,
	VC_VoiceSetFrequency,
	VC_VoiceSetPanning,
	VC_VoicePlay
};
