#include "types.h"

#include "z80_ctrl.h"

#include "tools.h"
#include "timer.h"

// Z80 driver
#include "z80_drv1.h"
#include "z80_drv2.h"
#include "z80_mvst.h"


static u16 currentDriver;


void Z80_init()
{
    // request bus
    Z80_requestBus(1);
    // clear Z80 RAM
    memset8((u8 *) Z80_RAM, 0, 0x2000);
    // request bus
    Z80_releaseBus();

    // set bank to 0
    Z80_setBank(0);

    currentDriver = Z80_DRIVER_NULL;
    // load default Z80 driver
    Z80_loadDriver(Z80_DRIVER_DEFAULT, 0);
}


u16 Z80_isBusTaken()
{
    volatile u16 *pw;

    pw = (u16 *) Z80_HALT_PORT;
    if (*pw & 0x0100) return 0;
    else return 1;
}

void Z80_requestBus(u16 wait)
{
    volatile u16 *pw_bus;
    volatile u16 *pw_reset;

    // request bus (need to end reset)
    pw_bus = (u16 *) Z80_HALT_PORT;
    pw_reset = (u16 *) Z80_RESET_PORT;
    *pw_bus = 0x0100;
    *pw_reset = 0x0100;

    if (wait)
    {
        // wait for bus taken
        while (*pw_bus & 0x0100);
    }
}

void Z80_releaseBus()
{
    volatile u16 *pw;

    pw = (u16 *) Z80_HALT_PORT;
    *pw = 0x0000;
}


void Z80_startReset()
{
    volatile u16 *pw;

    pw = (u16 *) Z80_RESET_PORT;
    *pw = 0x0000;
}

void Z80_endReset()
{
    volatile u16 *pw;

    pw = (u16 *) Z80_RESET_PORT;
    *pw = 0x0100;
}


void Z80_setBank(const u16 bank)
{
    volatile u8 *pb;
    u16 i, value;

    pb = (u8 *) Z80_BANK_REGISTER;

    i = 9;
    value = bank;
    while (i--)
    {
        *pb = value;
        value >>= 1;
    }
}


void Z80_uploadData(const u16 dest, const u8 *data, const u16 size, const u16 resetz80)
{
    Z80_requestBus(1);

    // copy data to Z80 RAM
    memcpy8((u8 *) (Z80_RAM + dest), data, size);

    if (resetz80) Z80_startReset();
    Z80_releaseBus();
    if (resetz80) Z80_endReset();
}


u16 Z80_getLoadedDriver()
{
    return currentDriver;
}

void Z80_loadDriver(const u16 driver, const u16 waitReady)
{
    const u8 *drv;
    u16 len;

    // already loaded
    if (currentDriver == driver) return;

    switch(driver)
    {
        case Z80_DRIVER_SAMPLEPLAY:
            drv = z80_drv1;
            len = sizeof(z80_drv1);
            break;

        case Z80_DRIVER_2SAMPLESPLAY:
            drv = z80_drv2;
            len = sizeof(z80_drv2);
            break;

        case Z80_DRIVER_MVSTRACKER:
            drv = z80_mvst;
            len = sizeof(z80_mvst);
            break;

        default:
            // no valid driver to load
            return;
    }

    // upload Z80 driver and reset Z80
    Z80_uploadData(0, drv, len, 1);

    // wait a bit for reset completion
    wait(1);

    // start the driver
    Z80_endReset();

    if (waitReady)
    {
        volatile u8 *pb;

        // point to Z80 status
        pb = (u8 *) 0xA0007C;

        // wait for driver ready
        do
        {
            Z80_releaseBus();
            wait(1);
            Z80_requestBus(1);
        } while(*pb != 0x80);
    }

    // new driver set
    currentDriver = driver;
}
