 /* 
  * UAE - The Un*x Amiga Emulator
  * 
  * OS specific functions
  * 
  * (c) 1995 Bernd Schmidt
  * (c) 1996 Marcus Sundberg
  */

#include "sysconfig.h"
#include "sysdeps.h"

#include "config.h"
#include "options.h"
#include "memory.h"
#include "custom.h"
#include "os.h"
#include "events.h"

#ifndef DONT_WANT_SOUND
static void sample16_handler(void);
static void sample8_handler(void);
#endif

int joystickpresent = 0;

#ifdef HAVE_LINUX_JOYSTICK_H

static int js0;

struct JS_DATA_TYPE jscal;

void read_joystick(UWORD *dir, int *button)
{
    static int minx = MAXINT, maxx = MININT,
               miny = MAXINT, maxy = MININT;
    int left = 0, right = 0, top = 0, bot = 0;
    struct JS_DATA_TYPE buffer;
    int len;
    
    *dir = 0;
    *button = 0;
    if (!joystickpresent)
    	return;
    
    len = read(js0, &buffer, sizeof(buffer));
    if (len != sizeof(buffer)) 
    	return;
    
    if (buffer.x < minx) minx = buffer.x;
    if (buffer.y < miny) miny = buffer.y;
    if (buffer.x > maxx) maxx = buffer.x;
    if (buffer.y > maxy) maxy = buffer.y;
    
    if (buffer.x < (minx + (maxx-minx)/3))
    	left = 1;
    else if (buffer.x > (minx + 2*(maxx-minx)/3))
    	right = 1;

    if (buffer.y < (miny + (maxy-miny)/3))
    	top = 1;
    else if (buffer.y > (miny + 2*(maxy-miny)/3))
    	bot = 1;
    	
    if (left) top = !top;
    if (right) bot = !bot;
    *dir = bot | (right << 1) | (top << 8) | (left << 9);
    *button = (buffer.buttons & 3) != 0;
}

void init_joystick(void)
{
    js0 = open("/dev/js0", O_RDONLY);
    if (js0 < 0)
    	return;
    joystickpresent = 1;
}

void close_joystick(void)
{
    if (joystickpresent)
	close(js0);
}

#elif defined(__DOS__)

void read_joystick(UWORD *dir, int *button)
{
    static int minx = MAXINT, maxx = MININT,
	       miny = MAXINT, maxy = MININT;
    int left = 0, right = 0, top = 0, bot = 0;
    char JoyPort;
    int laps, JoyX, JoyY;

    *dir = 0;
    *button = 0;
    if (!joystickpresent)
	return;

    JoyX = 0;
    JoyY = 0;
    laps = 0;
    __asm__ __volatile__("cli");
    outportb(0x201, 0xff);
    do {
	JoyPort = inportb(0x201);
	JoyX = JoyX + (JoyPort & 1);
	JoyY = JoyY + ((JoyPort & 2) >> 1);
	laps++;
    } while(((JoyPort & 3) != 0) && (laps != 65535));
    __asm__ __volatile__("sti");

    if (JoyX < minx) minx = JoyX;
    if (JoyY < miny) miny = JoyY;
    if (JoyX > maxx) maxx = JoyX;
    if (JoyY > maxy) maxy = JoyY;

    if (JoyX < (minx + (maxx-minx)/3))
	left = 1;
    else if (JoyX > (minx + 2*(maxx-minx)/3))
	right = 1;

    if (JoyY < (miny + (maxy-miny)/3))
	top = 1;
    else if (JoyY > (miny + 2*(maxy-miny)/3))
	bot = 1;

    if (left) top = !top;
    if (right) bot = !bot;
    *dir = bot | (right << 1) | (top << 8) | (left << 9);
    *button = ((~JoyPort) & 48) != 0;
}

void init_joystick(void)
{
    int laps = 0;
    char JoyPort;
    __asm__ __volatile__("cli");
    outportb(0x201, 0xff);
    do {
	JoyPort = inportb(0x201);
	laps++;
    } while(((JoyPort & 3) != 0) && (laps != 65535));
    __asm__ __volatile__("sti");
    if (laps != 65535)
	joystickpresent = 1;
}

void close_joystick(void)
{
}

#else
void read_joystick(UWORD *dir, int *button)
{
    *dir = 0;
    *button = 0;
}

void init_joystick(void)
{
}

void close_joystick(void)
{
}

#endif

int sound_available = 0;
static long count = 0;

struct audio_channel_data audio_channel[4];

/* The buffer is too large... */
static UWORD buffer[44100], *bufpt;

static ULONG data;

int sound_table[256][64];
static int smplcnt = 0;
static int dspbits = 16, freq_divisor = 1;
static int sndbufsize;

static void init_sound_table16(void)
{
    int i,j;
    
    for (i = 0; i < 256; i++)
	for (j = 0; j < 64; j++)
	    sound_table[i][j] = j * (BYTE)i;
}

static void init_sound_table8(void)
{
    int i,j;
    
    for (i = 0; i < 256; i++)
	for (j = 0; j < 64; j++)
	    sound_table[i][j] = (j * (BYTE)i) / 256;
}

static int exact_log2(int v)
{
    int l = 0;
    while ((v >>= 1) != 0)
	l++;
    return l;
}

#ifdef LINUX_SOUND

#include <sys/ioctl.h>
#include <sys/soundcard.h>

static int sfd;
static int have_sound;

int init_sound (void)
{
    int tmp;
    int rate;
    
    unsigned long formats;
    
    sfd = open ("/dev/dsp", O_WRONLY);
    have_sound = !(sfd < 0);
    if (!have_sound) {
	return 0;
    }
    
    ioctl (sfd, SNDCTL_DSP_GETFMTS, &formats);

    if (sound_desired_bsiz < 128 || sound_desired_bsiz > 16384) {
	fprintf(stderr, "Sound buffer size %d out of range.\n", sound_desired_bsiz);
	sound_desired_bsiz = 8192;
    }
    
    tmp = 0x00040000 + exact_log2(sound_desired_bsiz);
    ioctl (sfd, SNDCTL_DSP_SETFRAGMENT, &tmp);
    ioctl (sfd, SNDCTL_DSP_GETBLKSIZE, &sndbufsize);

    dspbits = sound_desired_bits;
    ioctl(sfd, SNDCTL_DSP_SAMPLESIZE, &dspbits);
    ioctl(sfd, SOUND_PCM_READ_BITS, &dspbits);
    if (dspbits != sound_desired_bits) {
	fprintf(stderr, "Can't use sound with %d bits\n", sound_desired_bits);
	return 0;
    }

    tmp = 0;
    ioctl(sfd, SNDCTL_DSP_STEREO, &tmp);
    
    rate = sound_desired_freq;
    ioctl(sfd, SNDCTL_DSP_SPEED, &rate);
    ioctl(sfd, SOUND_PCM_READ_RATE, &rate);
    /* Some soundcards have a bit of tolerance here. */
    if (rate < sound_desired_freq * 90 / 100 || rate > sound_desired_freq * 110 / 100) {
	fprintf(stderr, "Can't use sound with desired frequency %d\n", sound_desired_freq);
	return 0;
    }

    eventtab[ev_sample].evtime = (long)maxhpos * maxvpos * 50 / rate;

    if (dspbits == 16) {
	/* Will this break horribly on Linux/Alpha? Possible... */
	if (!(formats & AFMT_S16_LE))
	    return 0;
	init_sound_table16 ();
	eventtab[ev_sample].handler = sample16_handler;
    } else {
	if (!(formats & AFMT_U8))
	    return 0;
	init_sound_table8 ();
	eventtab[ev_sample].handler = sample8_handler;
    }
    sound_available = 1;
    printf ("Sound driver found and configured for %d bits at %d Hz, buffer is %d bytes\n",
	    dspbits, rate, sndbufsize);
    bufpt = buffer;
    smplcnt = 0;
    return 1;
}

static void flush_sound_buffer(void)
{
    write(sfd, buffer, sndbufsize);
    bufpt = buffer;
}

#elif defined(AF_SOUND)

#include <AF/AFlib.h>

static AFAudioConn  *aud;
static AC            ac;
static long          aftime;
static int           rate;

static int have_sound;

int init_sound (void)
{
    AFSetACAttributes   attributes;
    AFDeviceDescriptor *aDev;
    int                 device;
    
    aud = AFOpenAudioConn(NULL);
    have_sound = !(aud == NULL);
    if (!have_sound) {
	return 0;
    }
    
    for(device = 0; device < ANumberOfAudioDevices(aud); device++) {
	aDev = AAudioDeviceDescriptor(aud, device);
	rate = aDev->playSampleFreq;
	sndbufsize = (rate / 8) * 4;
	if(aDev->inputsFromPhone == 0
	   && aDev->outputsToPhone == 0
	   && aDev->playNchannels == 1)
	    break;
    }
    if (device == ANumberOfAudioDevices(aud)) {
	return 0;
    }
    
    dspbits = 16;
    attributes.type = LIN16;
    ac = AFCreateAC(aud, device, ACEncodingType, &attributes);
    aftime = AFGetTime(ac);

    init_sound_table16 ();
    eventtab[ev_sample].handler = sample16_handler;
    eventtab[ev_sample].evtime = (long)maxhpos * maxvpos * 50 / rate;
    
    bufpt = buffer;
    smplcnt = 0;
    sound_available = 1;
    printf ("Sound driver found and configured for %d bits at %d Hz, buffer is %d bytes\n", dspbits, rate, sndbufsize);
    return 1;
}

static void flush_sound_buffer(void)
{
    long size = (char *)bufpt - (char *)buffer;
    if (AFGetTime(ac) > aftime)
	aftime = AFGetTime(ac);
    AFPlaySamples(ac, aftime, size, (unsigned char*) buffer);
    aftime += size / 2;
    bufpt = buffer;
}

#elif defined(__mac__)

#include <Sound.h>

static SndChannelPtr newChannel;
static ExtSoundHeader theSndBuffer;
static SndCommand theCmd;

/* The buffer is too large... */
static UWORD buffer0[44100], buffer1[44100], *bufpt;

static int have_sound;
static int nextbuf=0;
static Boolean sFlag=true;

int init_sound (void)
{	
    if (SndNewChannel(&newChannel, sampledSynth, initMono, NULL)) 
	return 0;
    sndbufsize = 44100;
    init_sound_table8 ();
    smplcnt = 0;
    bufpt = buffer0;
    sound_available = 1;
    return 1;
}

static void flush_sound_buffer(void)
{
    bufpt = buffer0;
    
    theSndBuffer.samplePtr = (Ptr)buffer0;
    theSndBuffer.numChannels = 1;
    theSndBuffer.sampleRate = 0xac440000;
    theSndBuffer.encode = extSH;
    theSndBuffer.numFrames = sndbufsize;
    theSndBuffer.sampleSize = 8;
    theCmd.param1 = 0;
    theCmd.param2 = (long)&theSndBuffer;
    theCmd.cmd = bufferCmd;
    SndDoCommand(newChannel, &theCmd, false);    
}

#elif defined(__DOS__)

#include "dos-sb.h"

void (*SND_Write)(void *buf, unsigned long size);  // Pointer to function that plays data on card
int dos_dsprate = 1;  // 0 for 44100  -  1 for 22050
int dos_dspbits = 16;

int init_sound (void)
{
    int rate;

    dspbits = sound_desired_bits;
    rate = sound_desired_freq;
    if (rate == 22050)
	dos_dsprate = 1;
    else if (rate == 44100)
	dos_dsprate = 0;
    else {
	fprintf(stderr, "Can't use sample rate %d!\n", rate);
	return 0;
    }
	
    if (SB_DetectInitSound(&dspbits, &dos_dsprate, &sndbufsize));
    else if (0/*OTHER_CARD_DETECT_ROUTINE*/);
    else
	return 0;
    
    eventtab[ev_sample].evtime = (long)maxhpos * maxvpos * 50 / rate;

    if (dspbits == 16) {
	init_sound_table16 ();
	eventtab[ev_sample].handler = sample16_handler;
    } else {
	init_sound_table8 ();
	eventtab[ev_sample].handler = sample8_handler;
    }
    sound_available = 1;
    printf ("Sound driver found and configured for %d bits at %d Hz, buffer is %d bytes\n",
	    dspbits, rate, sndbufsize);
    bufpt = buffer;
    smplcnt = 0;
    return 1;
}

static void flush_sound_buffer(void)
{
    SND_Write(buffer, sndbufsize);
    bufpt = buffer;
}

#elif defined(SOLARIS_SOUND)

#include <sys/audioio.h>

static int sfd;
static int have_sound;

int init_sound (void)
{
    int rate;

    struct audio_info sfd_info;

    sfd = open("/dev/audio", O_WRONLY);
    have_sound = !(sfd <0);
    if (!have_sound) {
        return 0;
    }
    
    rate = sound_desired_freq;
    dspbits = sound_desired_bits;
    AUDIO_INITINFO(&sfd_info); /* 44100Hz, mono, 16Bit, linear */
    sfd_info.play.sample_rate = rate;
    sfd_info.play.channels = 1;
    sfd_info.play.precision = dspbits;
    sfd_info.play.encoding = AUDIO_ENCODING_LINEAR;
    if (ioctl(sfd, AUDIO_SETINFO, &sfd_info)) {
	fprintf(stderr, "Can't use sample rate %d with %d bits!\n", rate, bits);
	return 0;
    }
    eventtab[ev_sample].evtime = (long)maxhpos * maxvpos * 50 / rate;

    if (dspbits == 16) {
	init_sound_table16 ();
	eventtab[ev_sample].handler = sample16_handler;
    } else {
	init_sound_table8 ();
	eventtab[ev_sample].handler = sample8_handler;
    }

    bufpt = buffer;
    smplcnt = 0;
    sound_available = 1;
    sndbufsize = dspbits/8 * rate;
    printf ("Sound driver found and configured for %d bits at %d Hz, buffer is %d bytes\n", dspbits, rate, sndbufsize);
    return 1;
}

static void flush_sound_buffer(void)
{
    write(sfd, buffer, sndbufsize);
    bufpt = buffer;
}


#else

int init_sound (void)
{
    produce_sound = 0;
    return 1;
}

static void flush_sound_buffer(void)
{
}

#endif

void AUDxDAT(int nr, UWORD v) 
{
#ifndef DONT_WANT_SOUND
    struct audio_channel_data *cdp = audio_channel + nr;
    cdp->dat = v;
    if (cdp->state == 0 && !(INTREQR() & (0x80 << nr))) {
	cdp->state = 2;
	INTREQ(0x8000 | (0x80 << nr));
	/* data_written = 2 ???? */
	eventtab[ev_aud0 + nr].evtime = cycles + cdp->per;
	eventtab[ev_aud0 + nr].oldcycles = cycles;
	eventtab[ev_aud0 + nr].active = 1;
	events_schedule();
    }
#endif
}

#ifndef DONT_WANT_SOUND
static void sample16_handler(void)
{
    int nr;
    ULONG data = 0;

    eventtab[ev_sample].evtime += cycles - eventtab[ev_sample].oldcycles;
    eventtab[ev_sample].oldcycles = cycles;

    if (produce_sound < 2)
	return;

    for (nr = 0; nr < 4; nr++) {
	if (!(adkcon & (0x11 << nr)))
	    data += sound_table[audio_channel[nr].current_sample][audio_channel[nr].vol];
    }
    *bufpt++ = data;
    if ((char *)bufpt - (char *)buffer >= sndbufsize) {
	flush_sound_buffer();
    }
}

static void sample8_handler(void)
{
    int nr;
    ULONG data = 0;
    unsigned char *bp = (unsigned char *)bufpt;
    
    eventtab[ev_sample].evtime += cycles - eventtab[ev_sample].oldcycles;
    eventtab[ev_sample].oldcycles = cycles;
    
    if (produce_sound < 2)
	return;

    for (nr = 0; nr < 4; nr++) {
	if (!(adkcon & (0x11 << nr)))
	    data += sound_table[audio_channel[nr].current_sample][audio_channel[nr].vol];
    }
    *bp++ = data + 128;
    bufpt = (UWORD *)bp;

    if ((char *)bufpt - (char *)buffer >= sndbufsize) {
	flush_sound_buffer();
    }
}

static void audio_handler(int nr) 
{
    struct audio_channel_data *cdp = audio_channel + nr;

    switch (cdp->state) {
     case 0:
	fprintf(stderr, "Bug in sound code\n");
	break;

     case 1:
	/* We come here at the first hsync after DMA was turned on. */
	eventtab[ev_aud0 + nr].evtime += maxhpos;
	eventtab[ev_aud0 + nr].oldcycles += maxhpos;
	
	cdp->state = 5;
	INTREQ(0x8000 | (0x80 << nr));
	if (cdp->wlen != 1)
	    cdp->wlen--;
	cdp->nextdat = chipmem_bank.wget(cdp->pt);
	cdp->pt += 2;
	break;

     case 5:
	/* We come here at the second hsync after DMA was turned on. */
	if (produce_sound == 0)
	    cdp->per = 65535;

	eventtab[ev_aud0 + nr].evtime = cycles + cdp->per;
	eventtab[ev_aud0 + nr].oldcycles = cycles;
	cdp->dat = cdp->nextdat;
	cdp->current_sample = (UBYTE)(cdp->dat >> 8);
	cdp->state = 2;
	{
	    int audav = adkcon & (1 << nr);
	    int audap = adkcon & (16 << nr);
	    int napnav = (!audav && !audap) || audav;
	    if (napnav)
		cdp->data_written = 2;
	}
	break;
	
     case 2:
	/* We come here when a 2->3 transition occurs */
	if (produce_sound == 0)
	    cdp->per = 65535;

	cdp->current_sample = (UBYTE)(cdp->dat & 0xFF);
	eventtab[ev_aud0 + nr].evtime = cycles + cdp->per;
	eventtab[ev_aud0 + nr].oldcycles = cycles;

	cdp->state = 3;

	/* Period attachment? */
	if (adkcon & (0x10 << nr)) {
	    if (cdp->intreq2 && cdp->dmaen)
		INTREQ(0x8000 | (0x80 << nr));
	    cdp->intreq2 = 0;

	    cdp->dat = cdp->nextdat;
	    if (cdp->dmaen)
		cdp->data_written = 2;
	    if (nr < 3) {
		if (cdp->dat == 0)
		    (cdp+1)->per = 65535;

		else if (cdp->dat < maxhpos/2 && produce_sound < 3)
		    (cdp+1)->per = maxhpos/2;
		else
		    (cdp+1)->per = cdp->dat;
	    }
	}
	break;
	
     case 3:
	/* We come here when a 3->2 transition occurs */
	if (produce_sound == 0)
	    cdp->per = 65535;

	eventtab[ev_aud0 + nr].evtime = cycles + cdp->per;
	eventtab[ev_aud0 + nr].oldcycles = cycles;
	
	if ((INTREQR() & (0x80 << nr)) && !cdp->dmaen) {
	    cdp->state = 0;
	    cdp->current_sample = 0;
	    eventtab[ev_aud0 + nr].active = 0;
	    break;
	} else {
	    int audav = adkcon & (1 << nr);
	    int audap = adkcon & (16 << nr);
	    int napnav = (!audav && !audap) || audav;
	    cdp->state = 2;
	    
	    if ((cdp->intreq2 && cdp->dmaen && napnav)
		|| (napnav && !cdp->dmaen))
		INTREQ(0x8000 | (0x80 << nr));
	    cdp->intreq2 = 0;
	    
	    cdp->dat = cdp->nextdat;
	    cdp->current_sample = (UBYTE)(cdp->dat >> 8);

	    if (cdp->dmaen && napnav)
		cdp->data_written = 2;
	    
	    /* Volume attachment? */
	    if (audav) {
		if (nr < 3)
		    (cdp+1)->vol = cdp->dat;
	    }
	}
	break;
	    
     default:
	cdp->state = 0;
	eventtab[ev_aud0 + nr].active = 0;
	break;
    }
}

void aud0_handler(void)
{
    audio_handler(0);
}
void aud1_handler(void)
{
    audio_handler(1);
}
void aud2_handler(void)
{
    audio_handler(2);
}
void aud3_handler(void)
{
    audio_handler(3);
}
#endif
