#include <stdlib.h>
#include "ibm.h"
#include "device.h"
#include "io.h"
#include "sound.h"
#include "mca.h"

#include "sound_adlib.h"
#include "sound_opl.h"

typedef struct adlib_t {
        opl_t opl;

        uint8_t pos_regs[8];
} adlib_t;

static void adlib_get_buffer(int32_t *buffer, int len, void *p) {
        adlib_t *adlib = (adlib_t *)p;
        int c;

        opl2_update2(&adlib->opl);

        for (c = 0; c < len * 2; c++)
                buffer[c] += (int32_t)adlib->opl.buffer[c];

        adlib->opl.pos = 0;
}

uint8_t adlib_mca_read(int port, void *p) {
        adlib_t *adlib = (adlib_t *)p;

        pclog("adlib_mca_read: port=%04x\n", port);

        return adlib->pos_regs[port & 7];
}

void adlib_mca_write(int port, uint8_t val, void *p) {
        adlib_t *adlib = (adlib_t *)p;

        if (port < 0x102)
                return;

        pclog("adlib_mca_write: port=%04x val=%02x\n", port, val);

        switch (port) {
        case 0x102:
                if ((adlib->pos_regs[2] & 1) && !(val & 1))
                        io_removehandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &adlib->opl);
                if (!(adlib->pos_regs[2] & 1) && (val & 1))
                        io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &adlib->opl);
                break;
        }
        adlib->pos_regs[port & 7] = val;
}

void *adlib_init() {
        adlib_t *adlib = malloc(sizeof(adlib_t));
        memset(adlib, 0, sizeof(adlib_t));

        pclog("adlib_init\n");
        opl2_init(&adlib->opl);
        io_sethandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &adlib->opl);
        sound_add_handler(adlib_get_buffer, adlib);

        return adlib;
}

void *adlib_mca_init() {
        adlib_t *adlib = adlib_init();

        io_removehandler(0x0388, 0x0002, opl2_read, NULL, NULL, opl2_write, NULL, NULL, &adlib->opl);
        mca_add(adlib_mca_read, adlib_mca_write, NULL, adlib);
        adlib->pos_regs[0] = 0xd7;
        adlib->pos_regs[1] = 0x70;

        return adlib;
}

void adlib_close(void *p) {
        adlib_t *adlib = (adlib_t *)p;

        free(adlib);
}

device_t adlib_device = {"AdLib", 0, adlib_init, adlib_close, NULL, NULL, NULL, NULL};

device_t adlib_mca_device = {"AdLib (MCA)", DEVICE_MCA, adlib_init, adlib_close, NULL, NULL, NULL, NULL};
