#include <stdlib.h>
#include "ibm.h"

#include "device.h"
#include "io.h"
#include "ide.h"
#include "mem.h"
#include "rom.h"
#include "xtide.h"

typedef struct xtide_t {
	uint8_t data_high;
	rom_t bios_rom;
} xtide_t;

static void xtide_write(uint16_t port, uint8_t val, void *p) {
	xtide_t *xtide = (xtide_t *)p;

	switch (port & 0xf) {
	case 0x0:writeidew(0, val | (xtide->data_high << 8));
		return;

	case 0x1:
	case 0x2:
	case 0x3:
	case 0x4:
	case 0x5:
	case 0x6:
	case 0x7:writeide(0, (port & 0xf) | 0x1f0, val);
		return;

	case 0x8:xtide->data_high = val;
		return;

	case 0xe:writeide(0, 0x3f6, val);
		return;
	}
}

static uint8_t xtide_read(uint16_t port, void *p) {
	xtide_t *xtide = (xtide_t *)p;
	uint16_t tempw;

	switch (port & 0xf) {
	case 0x0:tempw = readidew(0);
		xtide->data_high = tempw >> 8;
		return tempw & 0xff;

	case 0x1:
	case 0x2:
	case 0x3:
	case 0x4:
	case 0x5:
	case 0x6:
	case 0x7:return readide(0, (port & 0xf) | 0x1f0);

	case 0x8:return xtide->data_high;

	case 0xe:return readide(0, 0x3f6);
	}

	return 0xff;
}

static void *xtide_init() {
	xtide_t *xtide = malloc(sizeof(xtide_t));
	memset(xtide, 0, sizeof(xtide_t));

	rom_init(&xtide->bios_rom, "ide_xt.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
	device_add(&ide_device);
	ide_pri_disable();
	ide_sec_disable();
	io_sethandler(0x0300, 0x0010, xtide_read, NULL, NULL, xtide_write, NULL, NULL, xtide);

	return xtide;
}

static void *xtide_at_init() {
	xtide_t *xtide = malloc(sizeof(xtide_t));
	memset(xtide, 0, sizeof(xtide_t));

	rom_init(&xtide->bios_rom, "ide_at.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
	device_add(&ide_device);

	return xtide;
}

static void *xtide_ps1_init() {
	xtide_t *xtide = malloc(sizeof(xtide_t));
	memset(xtide, 0, sizeof(xtide_t));

	rom_init(&xtide->bios_rom, "ide_at_1_1_5.bin", 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL);
	device_add(&ide_device);

	return xtide;
}

static void xtide_close(void *p) {
	xtide_t *xtide = (xtide_t *)p;

	free(xtide);
}

static int xtide_available() {
	return rom_present("ide_xt.bin");
}

static int xtide_at_available() {
	return rom_present("ide_at.bin");
}

static int xtide_ps1_available() {
	return rom_present("ide_at_1_1_5.bin");
}

device_t xtide_device =
	{
		"XTIDE",
		0,
		xtide_init,
		xtide_close,
		xtide_available,
		NULL,
		NULL,
		NULL,
		NULL
	};
device_t xtide_at_device =
	{
		"XTIDE (AT)",
		DEVICE_AT,
		xtide_at_init,
		xtide_close,
		xtide_at_available,
		NULL,
		NULL,
		NULL,
		NULL
	};
device_t xtide_ps1_device =
	{
		"XTIDE (PS/1)",
		DEVICE_PS1,
		xtide_ps1_init,
		xtide_close,
		xtide_ps1_available,
		NULL,
		NULL,
		NULL,
		NULL
	};
