#include "ibm.h"
#include "io.h"
#include "pic.h"
#include "plat-midi.h"
#include "sound_mpu401_uart.h"

enum
{
        STATUS_OUTPUT_NOT_READY = 0x40,
        STATUS_INPUT_NOT_READY  = 0x80
};

static void mpu401_uart_raise_irq(void *p)
{
        mpu401_uart_t *mpu = (mpu401_uart_t *)p;

        if (mpu->irq != -1)
                picint(1 << mpu->irq);
}

static void mpu401_uart_write(uint16_t addr, uint8_t val, void *p)
{
        mpu401_uart_t *mpu = (mpu401_uart_t *)p;
        
        if (addr & 1) /*Command*/
        {
                switch (val)
                {
                        case 0xff: /*Reset*/
                        // From Roland: "An ACK will not be sent back upon sending a SYSTEM RESET to leave the UART MODE ($3F)."
                        // But actual behaviour is weird. For example, the MPU401 port test in the AZT1605 drivers for Windows NT
                        // want this to return an Ack but the IRQ test in the same driver wants this to raise no interrupts!
                        mpu->rx_data = 0xfe; /*Acknowledge*/
                        mpu->uart_mode = 0;
                        if (mpu->is_aztech)
                                mpu->status = STATUS_OUTPUT_NOT_READY;
                        else
                        {
                                mpu->status = 0;
                                mpu401_uart_raise_irq(p);
                        }
                        break;
                        
                        case 0x3f: /*Enter UART mode*/
                        mpu->rx_data = 0xfe; /*Acknowledge*/
                        mpu->uart_mode = 1;
                        if (mpu->is_aztech)
                        {
                                mpu->status = STATUS_OUTPUT_NOT_READY;
                                mpu401_uart_raise_irq(p);
                        }
                        else
                                mpu->status = 0;
                        break;
                }
                return;
        }
                        
        /*Data*/
        if (mpu->uart_mode)
                midi_write(val);
}

static uint8_t mpu401_uart_read(uint16_t addr, void *p)
{
        mpu401_uart_t *mpu = (mpu401_uart_t *)p;
        
        if (addr & 1) /*Status*/
                return mpu->status;
        
        /*Data*/
        mpu->status = STATUS_INPUT_NOT_READY;
        return mpu->rx_data;
}

void mpu401_uart_init(mpu401_uart_t *mpu, uint16_t addr, int irq, int is_aztech)
{
        mpu->status = STATUS_INPUT_NOT_READY;
        mpu->uart_mode = 0;
        mpu->addr = addr;
        mpu->irq = irq;
        mpu->is_aztech = is_aztech;
        
        io_sethandler(addr, 0x0002, mpu401_uart_read, NULL, NULL, mpu401_uart_write, NULL, NULL, mpu);
}

void mpu401_uart_update_addr(mpu401_uart_t *mpu, uint16_t addr)
{
        io_removehandler(mpu->addr, 0x0002, mpu401_uart_read, NULL, NULL, mpu401_uart_write, NULL, NULL, mpu);
        mpu->addr = addr;
        if (addr != 0)
                io_sethandler(addr, 0x0002, mpu401_uart_read, NULL, NULL, mpu401_uart_write, NULL, NULL, mpu);
}

void mpu401_uart_update_irq(mpu401_uart_t *mpu, int irq)
{
        mpu->irq = irq;
}

