/******************************************************************************


    CD-i MCD212 video emulation
    -------------------

    MESS implementation by Harmony


*******************************************************************************

STATUS:

- Just enough for the Mono-I CD-i board to work somewhat properly.

TODO:

- Unknown yet.

*******************************************************************************/

#include "emu.h"
#include "cpu/m68000/m68000.h"
#include "video/mcd212.h"
#include "includes/cdi.h"

#if ENABLE_VERBOSE_LOG
INLINE void verboselog(running_machine &machine, int n_level, const char *s_fmt, ...)
{
    if( VERBOSE_LEVEL >= n_level )
    {
        va_list v;
        char buf[ 32768 ];
        va_start( v, s_fmt );
        vsprintf( buf, s_fmt, v );
        va_end( v );
        logerror( "%08x: %s", cpu_get_pc(machine.device("maincpu")), buf );
    }
}
#else
#define verboselog(x,y,z,...)
#endif

static void cdi220_draw_lcd(running_machine &machine, int y);
static void mcd212_update_region_arrays(mcd212_regs_t *mcd212);
static void mcd212_set_display_parameters(mcd212_regs_t *mcd212, int channel, UINT8 value);
static void mcd212_update_visible_area(running_machine &machine);
static void mcd212_set_vsr(mcd212_regs_t *mcd212, int channel, UINT32 value);
static void mcd212_set_dcp(mcd212_regs_t *mcd212, int channel, UINT32 value);
static UINT32 mcd212_get_vsr(mcd212_regs_t *mcd212, int channel);
static UINT32 mcd212_get_dcp(mcd212_regs_t *mcd212, int channel);
static UINT32 mcd212_get_screen_width(mcd212_regs_t *mcd212);
static void mcd212_set_register(running_machine &machine, int channel, UINT8 reg, UINT32 value);
static void mcd212_process_ica(mcd212_regs_t *mcd212, int channel);
static void mcd212_process_dca(mcd212_regs_t *mcd212, int channel);
static void mcd212_process_vsr(mcd212_regs_t *mcd212, int channel, UINT8 *pixels_r, UINT8 *pixels_g, UINT8 *pixels_b);
static void mcd212_mix_lines(mcd212_regs_t *mcd212, UINT8 *plane_a_r, UINT8 *plane_a_g, UINT8 *plane_a_b, UINT8 *plane_b_r, UINT8 *plane_b_g, UINT8 *plane_b_b, UINT32 *out);
static void mcd212_draw_cursor(mcd212_regs_t *mcd212, UINT32 *scanline, int y);
static void mcd212_draw_scanline(mcd212_regs_t *mcd212, int y);

static const UINT16 cdi220_lcd_char[20*22] =
{
    0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x8000, 0x8000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0002, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x8000, 0x8000, 0x8000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x8000, 0x8000, 0x8000, 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0002, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x0000, 0x8000, 0x8000, 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x0000, 0x0000, 0x8000, 0x8000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0200, 0x0200, 0x0200, 0x0200,
    0x2000, 0x2000, 0x2000, 0x2000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0200, 0x0200, 0x0200, 0x0200,
    0x1000, 0x1000, 0x1000, 0x1000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x4000, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0000, 0x0010, 0x0010, 0x0001, 0x0001, 0x0001, 0x0001, 0x0008, 0x0008, 0x0000, 0x0000, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0000, 0x0010, 0x0010, 0x0010, 0x0001, 0x0001, 0x0001, 0x0001, 0x0008, 0x0008, 0x0008, 0x0000, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0010, 0x0010, 0x0010, 0x0010, 0x0001, 0x0001, 0x0001, 0x0001, 0x0008, 0x0008, 0x0008, 0x0008, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0010, 0x0010, 0x0010, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0008, 0x0008, 0x0008, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0010, 0x0010, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0008, 0x0008, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400,
    0x1000, 0x1000, 0x1000, 0x1000, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0400, 0x0400, 0x0400, 0x0400
};

static void cdi220_draw_lcd(running_machine &machine, int y)
{
    cdi_state *state = machine.driver_data<cdi_state>();
    bitmap_t *bitmap = state->m_lcdbitmap;
    UINT32 *scanline = BITMAP_ADDR32(bitmap, y, 0);
    int x = 0;
    int lcd = 0;

    for(lcd = 0; lcd < 8; lcd++)
    {
        cdislave_device *slave = downcast<cdislave_device *>(machine.device("slave"));
        UINT16 data = (slave->get_lcd_state()[lcd*2] << 8) |
                       slave->get_lcd_state()[lcd*2 + 1];
        for(x = 0; x < 20; x++)
        {
            if(data & cdi220_lcd_char[y*20 + x])
            {
                scanline[(7 - lcd)*24 + x] = 0x00ffffff;
            }
            else
            {
                scanline[(7 - lcd)*24 + x] = 0;
            }
        }
    }
}

static void mcd212_update_region_arrays(mcd212_regs_t *mcd212)
{
    int x = 0;
    int latched_rf0 = 0;
    int latched_rf1 = 0;
    int latched_wfa = mcd212->channel[0].weight_factor_a[0];
    int latched_wfb = mcd212->channel[1].weight_factor_b[0];
    int reg = 0;

    for(x = 0; x < 768; x++)
    {
        if(mcd212->channel[0].image_coding_method & MCD212_ICM_NR)
        {
            int reg_ = 0;
            int flag = 0;

            for(flag = 0; flag < 2; flag++)
            {
                for(reg_ = 0; reg_ < 4; reg_++)
                {
                    if(mcd212->channel[0].region_control[reg_] == 0)
                    {
                        break;
                    }
                    if(x == (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_X))
                    {
                        switch((mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_OP) >> MCD212_RC_OP_SHIFT)
                        {
                            case 0: // End of region control for line
                                break;
                            case 1:
                            case 2:
                            case 3: // Not used
                                break;
                            case 4: // Change weight of plane A
                                latched_wfa = (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                                break;
                            case 5: // Not used
                                break;
                            case 6: // Change weight of plane B
                                latched_wfb = (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                                break;
                            case 7: // Not used
                                break;
                            case 8: // Reset region flag
                                if(flag)
                                {
                                    latched_rf1 = 0;
                                }
                                else
                                {
                                    latched_rf0 = 0;
                                }
                                break;
                            case 9: // Set region flag
                                if(flag)
                                {
                                    latched_rf1 = 1;
                                }
                                else
                                {
                                    latched_rf0 = 1;
                                }
                                break;
                            case 10:    // Not used
                            case 11:    // Not used
                                break;
                            case 12: // Reset region flag and change weight of plane A
                                latched_wfa = (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                                if(flag)
                                {
                                    latched_rf1 = 0;
                                }
                                else
                                {
                                    latched_rf0 = 0;
                                }
                                break;
                            case 13: // Set region flag and change weight of plane A
                                latched_wfa = (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                                if(flag)
                                {
                                    latched_rf1 = 1;
                                }
                                else
                                {
                                    latched_rf0 = 1;
                                }
                                break;
                            case 14: // Reset region flag and change weight of plane B
                                latched_wfb = (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                                if(flag)
                                {
                                    latched_rf1 = 0;
                                }
                                else
                                {
                                    latched_rf0 = 0;
                                }
                                break;
                            case 15: // Set region flag and change weight of plane B
                                latched_wfb = (mcd212->channel[0].region_control[flag*4 + reg_] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                                if(flag)
                                {
                                    latched_rf1 = 1;
                                }
                                else
                                {
                                    latched_rf0 = 1;
                                }
                                break;
                        }
                    }
                }
            }
        }
        else
        {
            if(reg < 8)
            {
                int flag = (mcd212->channel[0].region_control[reg] & MCD212_RC_RF) >> MCD212_RC_RF_SHIFT;
                if(!(mcd212->channel[0].region_control[reg] & MCD212_RC_OP))
                {
                    for(; x < 768; x++)
                    {
                        mcd212->channel[0].weight_factor_a[x] = latched_wfa;
                        mcd212->channel[1].weight_factor_b[x] = latched_wfb;
                        mcd212->region_flag_0[x] = latched_rf0;
                        mcd212->region_flag_1[x] = latched_rf1;
                    }
                    break;
                }
                if(x == (mcd212->channel[0].region_control[reg] & MCD212_RC_X))
                {
                    switch((mcd212->channel[0].region_control[reg] & MCD212_RC_OP) >> MCD212_RC_OP_SHIFT)
                    {
                        case 0: // End of region control for line
                            break;
                        case 1:
                        case 2:
                        case 3: // Not used
                            break;
                        case 4: // Change weight of plane A
                            latched_wfa = (mcd212->channel[0].region_control[reg] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                            break;
                        case 5: // Not used
                            break;
                        case 6: // Change weight of plane B
                            latched_wfb = (mcd212->channel[0].region_control[reg] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                            break;
                        case 7: // Not used
                            break;
                        case 8: // Reset region flag
                            if(flag)
                            {
                                latched_rf1 = 0;
                            }
                            else
                            {
                                latched_rf0 = 0;
                            }
                            break;
                        case 9: // Set region flag
                            if(flag)
                            {
                                latched_rf1 = 1;
                            }
                            else
                            {
                                latched_rf0 = 1;
                            }
                            break;
                        case 10:    // Not used
                        case 11:    // Not used
                            break;
                        case 12: // Reset region flag and change weight of plane A
                            latched_wfa = (mcd212->channel[0].region_control[reg] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                            if(flag)
                            {
                                latched_rf1 = 0;
                            }
                            else
                            {
                                latched_rf0 = 0;
                            }
                            break;
                        case 13: // Set region flag and change weight of plane A
                            latched_wfa = (mcd212->channel[0].region_control[reg] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                            if(flag)
                            {
                                latched_rf1 = 1;
                            }
                            else
                            {
                                latched_rf0 = 1;
                            }
                            break;
                        case 14: // Reset region flag and change weight of plane B
                            latched_wfb = (mcd212->channel[0].region_control[reg] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                            if(flag)
                            {
                                latched_rf1 = 0;
                            }
                            else
                            {
                                latched_rf0 = 0;
                            }
                            break;
                        case 15: // Set region flag and change weight of plane B
                            latched_wfb = (mcd212->channel[0].region_control[reg] & MCD212_RC_WF) >> MCD212_RC_WF_SHIFT;
                            if(flag)
                            {
                                latched_rf1 = 1;
                            }
                            else
                            {
                                latched_rf0 = 1;
                            }
                            break;
                    }
                    reg++;
                }
            }
        }
        mcd212->channel[0].weight_factor_a[x] = latched_wfa;
        mcd212->channel[1].weight_factor_b[x] = latched_wfb;
        mcd212->region_flag_0[x] = latched_rf0;
        mcd212->region_flag_1[x] = latched_rf1;
    }
}

static void mcd212_set_vsr(mcd212_regs_t *mcd212, int channel, UINT32 value)
{
    mcd212->channel[channel].vsr = value & 0x0000ffff;
    mcd212->channel[channel].dcr &= 0xffc0;
    mcd212->channel[channel].dcr |= (value >> 16) & 0x003f;
}

static void mcd212_set_register(running_machine &machine, int channel, UINT8 reg, UINT32 value)
{
    cdi_state *state = machine.driver_data<cdi_state>();
    mcd212_regs_t *mcd212 = &state->m_mcd212_regs;

    switch(reg)
    {
        case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: // CLUT 0 - 63
        case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f:
        case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
        case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
        case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7:
        case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf:
        case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7:
        case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf:
            verboselog(machine, 11, "          %04xxxxx: %d: CLUT[%d] = %08x\n", channel * 0x20, channel, mcd212->channel[channel].clut_bank * 0x40 + (reg - 0x80), value );
            mcd212->channel[0].clut_r[mcd212->channel[channel].clut_bank * 0x40 + (reg - 0x80)] = (UINT8)(value >> 16) & 0xfc;
            mcd212->channel[0].clut_g[mcd212->channel[channel].clut_bank * 0x40 + (reg - 0x80)] = (UINT8)(value >>  8) & 0xfc;
            mcd212->channel[0].clut_b[mcd212->channel[channel].clut_bank * 0x40 + (reg - 0x80)] = (UINT8)(value >>  0) & 0xfc;
            break;
        case 0xc0: // Image Coding Method
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Image Coding Method = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].image_coding_method = value;
            }
            break;
        case 0xc1: // Transparency Control
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Transparency Control = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].transparency_control = value;
            }
            break;
        case 0xc2: // Plane Order
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Plane Order = %08x\n", channel * 0x20, channel, value & 7);
                mcd212->channel[channel].plane_order = value & 0x00000007;
            }
            break;
        case 0xc3: // CLUT Bank Register
            verboselog(machine, 6, "          %04xxxxx: %d: CLUT Bank Register = %08x\n", channel * 0x20, channel, value & 3);
            mcd212->channel[channel].clut_bank = channel ? (2 | (value & 0x00000001)) : (value & 0x00000003);
            break;
        case 0xc4: // Transparent Color A
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Transparent Color A = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].transparent_color_a = value & 0xfcfcfc;
            }
            break;
        case 0xc6: // Transparent Color B
            if(channel == 1)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Transparent Color B = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].transparent_color_b = value & 0xfcfcfc;
            }
            break;
        case 0xc7: // Mask Color A
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Mask Color A = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].mask_color_a = value & 0xfcfcfc;
            }
            break;
        case 0xc9: // Mask Color B
            if(channel == 1)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Mask Color B = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].mask_color_b = value & 0xfcfcfc;
            }
            break;
        case 0xca: // Delta YUV Absolute Start Value A
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Delta YUV Absolute Start Value A = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].dyuv_abs_start_a = value;
            }
            break;
        case 0xcb: // Delta YUV Absolute Start Value B
            if(channel == 1)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Delta YUV Absolute Start Value B = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].dyuv_abs_start_b = value;
            }
            break;
        case 0xcd: // Cursor Position
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Cursor Position = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].cursor_position = value;
            }
            break;
        case 0xce: // Cursor Control
            if(channel == 0)
            {
                verboselog(machine, 11, "          %04xxxxx: %d: Cursor Control = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].cursor_control = value;
            }
            break;
        case 0xcf: // Cursor Pattern
            if(channel == 0)
            {
                verboselog(machine, 11, "          %04xxxxx: %d: Cursor Pattern[%d] = %04x\n", channel * 0x20, channel, (value >> 16) & 0x000f, value & 0x0000ffff);
                mcd212->channel[channel].cursor_pattern[(value >> 16) & 0x000f] = value & 0x0000ffff;
            }
            break;
        case 0xd0: // Region Control 0-7
        case 0xd1:
        case 0xd2:
        case 0xd3:
        case 0xd4:
        case 0xd5:
        case 0xd6:
        case 0xd7:
            verboselog(machine, 6, "          %04xxxxx: %d: Region Control %d = %08x\n", channel * 0x20, channel, reg & 7, value );
            mcd212->channel[0].region_control[reg & 7] = value;
            mcd212_update_region_arrays(mcd212);
            break;
        case 0xd8: // Backdrop Color
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Backdrop Color = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].backdrop_color = value;
            }
            break;
        case 0xd9: // Mosaic Pixel Hold Factor A
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Mosaic Pixel Hold Factor A = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].mosaic_hold_a = value;
            }
            break;
        case 0xda: // Mosaic Pixel Hold Factor B
            if(channel == 1)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Mosaic Pixel Hold Factor B = %08x\n", channel * 0x20, channel, value );
                mcd212->channel[channel].mosaic_hold_b = value;
            }
            break;
        case 0xdb: // Weight Factor A
            if(channel == 0)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Weight Factor A = %08x\n", channel * 0x20, channel, value );
                memset(mcd212->channel[channel].weight_factor_a, value & 0x000000ff, 768);
                mcd212_update_region_arrays(mcd212);
            }
            break;
        case 0xdc: // Weight Factor B
            if(channel == 1)
            {
                verboselog(machine, 6, "          %04xxxxx: %d: Weight Factor B = %08x\n", channel * 0x20, channel, value );
                memset(mcd212->channel[channel].weight_factor_b, value & 0x000000ff, 768);
                mcd212_update_region_arrays(mcd212);
            }
            break;
    }
}

static UINT32 mcd212_get_vsr(mcd212_regs_t *mcd212, int channel)
{
    return ((mcd212->channel[channel].dcr & 0x3f) << 16) | mcd212->channel[channel].vsr;
}

static void mcd212_set_dcp(mcd212_regs_t *mcd212, int channel, UINT32 value)
{
    mcd212->channel[channel].dcp = value & 0x0000ffff;
    mcd212->channel[channel].ddr &= 0xffc0;
    mcd212->channel[channel].ddr |= (value >> 16) & 0x003f;
}

static UINT32 mcd212_get_dcp(mcd212_regs_t *mcd212, int channel)
{
    return ((mcd212->channel[channel].ddr & 0x3f) << 16) | mcd212->channel[channel].dcp;
}

static void mcd212_set_display_parameters(mcd212_regs_t *mcd212, int channel, UINT8 value)
{
    mcd212->channel[channel].ddr &= 0xf0ff;
    mcd212->channel[channel].ddr |= (value & 0x0f) << 8;
    mcd212->channel[channel].dcr &= 0xf7ff;
    mcd212->channel[channel].dcr |= (value & 0x10) << 7;
}

static void mcd212_update_visible_area(running_machine &machine)
{
    cdi_state *state = machine.driver_data<cdi_state>();
    mcd212_regs_t *mcd212 = &state->m_mcd212_regs;
    const rectangle &visarea = machine.primary_screen->visible_area();
    rectangle visarea1;
    attoseconds_t period = machine.primary_screen->frame_period().attoseconds;
    int width = 0;

    if((mcd212->channel[0].dcr & (MCD212_DCR_CF | MCD212_DCR_FD)) && (mcd212->channel[0].csrw & MCD212_CSR1W_ST))
    {
        width = 360;
    }
    else
    {
        width = 384;
    }

    visarea1.max_x = width-1;
    visarea1.min_x = visarea.min_x;
    visarea1.min_y = visarea.min_y;
    visarea1.max_y = visarea.max_y;

    machine.primary_screen->configure(width, 302, visarea1, period);
}

static UINT32 mcd212_get_screen_width(mcd212_regs_t *mcd212)
{
    if((mcd212->channel[0].dcr & (MCD212_DCR_CF | MCD212_DCR_FD)) && (mcd212->channel[0].csrw & MCD212_CSR1W_ST))
    {
        return 720;
    }
    return 768;
}

static void mcd212_process_ica(mcd212_regs_t *mcd212, int channel)
{
    running_machine &machine = mcd212->machine();
    cdi_state *state = machine.driver_data<cdi_state>();
    UINT16 *ica = channel ? state->m_planeb : state->m_planea;
    UINT32 addr = 0x000400/2;
    UINT32 cmd = 0;
    while(1)
    {
        UINT8 stop = 0;
        cmd = ica[addr++] << 16;
        cmd |= ica[addr++];
        switch((cmd & 0xff000000) >> 24)
        {
            case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: // STOP
            case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
                verboselog(machine, 11, "%08x: %08x: ICA %d: STOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                stop = 1;
                break;
            case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: // NOP
            case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
                verboselog(machine, 12, "%08x: %08x: ICA %d: NOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                break;
            case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: // RELOAD DCP
            case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
                verboselog(machine, 11, "%08x: %08x: ICA %d: RELOAD DCP\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_dcp(mcd212, channel, cmd & 0x001fffff);
                break;
            case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: // RELOAD DCP and STOP
            case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
                verboselog(machine, 11, "%08x: %08x: ICA %d: RELOAD DCP and STOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_dcp(mcd212, channel, cmd & 0x001fffff);
                stop = 1;
                break;
            case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: // RELOAD ICA
            case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
                verboselog(machine, 11, "%08x: %08x: ICA %d: RELOAD ICA\n", addr * 2 + channel * 0x200000, cmd, channel );
                addr = (cmd & 0x001fffff) / 2;
                break;
            case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: // RELOAD VSR and STOP
            case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
                verboselog(machine, 11, "%08x: %08x: ICA %d: RELOAD VSR and STOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_vsr(mcd212, channel, cmd & 0x001fffff);
                stop = 1;
                break;
            case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: // INTERRUPT
            case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
                verboselog(machine, 11, "%08x: %08x: ICA %d: INTERRUPT\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212->channel[1].csrr |= 1 << (2 - channel);
                if(mcd212->channel[1].csrr & (MCD212_CSR2R_IT1 | MCD212_CSR2R_IT2))
                {
                    UINT8 interrupt = (state->m_scc68070_regs.lir >> 4) & 7;
                    if(interrupt)
                    {
                        device_set_input_line_vector(machine.device("maincpu"), M68K_IRQ_1 + (interrupt - 1), 56 + interrupt);
                        cputag_set_input_line(machine, "maincpu", M68K_IRQ_1 + (interrupt - 1), ASSERT_LINE);
                    }
                }
#if 0
                if(mcd212->channel[1].csrr & MCD212_CSR2R_IT2)
                {
                    UINT8 interrupt = state->m_scc68070_regs.lir & 7;
                    if(interrupt)
                    {
                        device_set_input_line_vector(machine.device("maincpu"), M68K_IRQ_1 + (interrupt - 1), 24 + interrupt);
                        cputag_set_input_line(machine, "maincpu", M68K_IRQ_1 + (interrupt - 1), ASSERT_LINE);
                    }
                }
#endif
                break;
            case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: // RELOAD DISPLAY PARAMETERS
                verboselog(machine, 6, "%08x: %08x: ICA %d: RELOAD DISPLAY PARAMETERS\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_display_parameters(mcd212, channel, cmd & 0x1f);
                break;
            default:
                mcd212_set_register(machine, channel, cmd >> 24, cmd & 0x00ffffff);
                break;
        }
        if(stop)
        {
            break;
        }
    }
}

static void mcd212_process_dca(mcd212_regs_t *mcd212, int channel)
{
    running_machine &machine = mcd212->machine();
    cdi_state *state = machine.driver_data<cdi_state>();
    UINT16 *dca = channel ? state->m_planeb : state->m_planea;
    UINT32 addr = (mcd212->channel[channel].dca & 0x0007ffff) / 2; //(mcd212_get_dcp(mcd212, channel) & 0x0007ffff) / 2; // mcd212->channel[channel].dca / 2;
    UINT32 cmd = 0;
    UINT32 count = 0;
    UINT32 max = 64;
    UINT8 addr_changed = 0;
    //printf( "max = %d\n", max );
    while(1)
    {
        UINT8 stop = 0;
        cmd = dca[addr++] << 16;
        cmd |= dca[addr++];
        count += 4;
        switch((cmd & 0xff000000) >> 24)
        {
            case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: // STOP
            case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
                verboselog(machine, 11, "%08x: %08x: DCA %d: STOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                stop = 1;
                break;
            case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: // NOP
            case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
                verboselog(machine, 12, "%08x: %08x: DCA %d: NOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                break;
            case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: // RELOAD DCP
            case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
                verboselog(machine, 11, "%08x: %08x: DCA %d: RELOAD DCP (NOP)\n", addr * 2 + channel * 0x200000, cmd, channel );
                break;
            case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: // RELOAD DCP and STOP
            case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
                verboselog(machine, 11, "%08x: %08x: DCA %d: RELOAD DCP and STOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_dcp(&state->m_mcd212_regs, channel, cmd & 0x001fffff);
                addr = (cmd & 0x0007ffff) / 2;
                addr_changed = 1;
                stop = 1;
                break;
            case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: // RELOAD VSR
            case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
                verboselog(machine, 11, "%08x: %08x: DCA %d: RELOAD VSR\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_vsr(&state->m_mcd212_regs, channel, cmd & 0x001fffff);
                break;
            case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: // RELOAD VSR and STOP
            case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
                verboselog(machine, 11, "%08x: %08x: DCA %d: RELOAD VSR and STOP\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_vsr(&state->m_mcd212_regs, channel, cmd & 0x001fffff);
                stop = 1;
                break;
            case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: // INTERRUPT
            case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
                verboselog(machine, 11, "%08x: %08x: DCA %d: INTERRUPT\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212->channel[1].csrr |= 1 << (2 - channel);
                if(mcd212->channel[1].csrr & (MCD212_CSR2R_IT1 | MCD212_CSR2R_IT2))
                {
                    UINT8 interrupt = (state->m_scc68070_regs.lir >> 4) & 7;
                    if(interrupt)
                    {
                        device_set_input_line_vector(machine.device("maincpu"), M68K_IRQ_1 + (interrupt - 1), 56 + interrupt);
                        cputag_set_input_line(machine, "maincpu", M68K_IRQ_1 + (interrupt - 1), ASSERT_LINE);
                    }
                }
#if 0
                if(mcd212->channel[1].csrr & MCD212_CSR2R_IT2)
                {
                    UINT8 interrupt = state->m_scc68070_regs.lir & 7;
                    if(interrupt)
                    {
                        device_set_input_line_vector(machine.device("maincpu"), M68K_IRQ_1 + (interrupt - 1), 24 + interrupt);
                        cputag_set_input_line(machine, "maincpu", M68K_IRQ_1 + (interrupt - 1), ASSERT_LINE);
                    }
                }
#endif
                break;
            case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: // RELOAD DISPLAY PARAMETERS
                verboselog(machine, 6, "%08x: %08x: DCA %d: RELOAD DISPLAY PARAMETERS\n", addr * 2 + channel * 0x200000, cmd, channel );
                mcd212_set_display_parameters(&state->m_mcd212_regs, channel, cmd & 0x1f);
                break;
            default:
                mcd212_set_register(machine, channel, cmd >> 24, cmd & 0x00ffffff);
                break;
        }
        if(stop != 0 || count == max)
        {
            break;
        }
    }
    if(!addr_changed)
    {
        if(count < max)
        {
            addr += (max - count) >> 1;
        }
    }
    mcd212->channel[channel].dca = addr * 2;
}

INLINE UINT8 MCD212_LIM(INT32 in)
{
    if(in < 0)
    {
        return 0;
    }
    else if(in > 255)
    {
        return 255;
    }
    return (UINT8)in;
}

INLINE UINT8 BYTE_TO_CLUT(int channel, int icm, UINT8 byte)
{
    switch(icm)
    {
        case 1:
            return byte;
        case 3:
            if(channel)
            {
                return 0x80 + (byte & 0x7f);
            }
            else
            {
                return byte & 0x7f;
            }
        case 4:
            if(!channel)
            {
                return byte & 0x7f;
            }
            break;
        case 11:
            if(channel)
            {
                return 0x80 + (byte & 0x0f);
            }
            else
            {
                return byte & 0x0f;
            }
        default:
            break;
    }
    return 0;
}

static void mcd212_process_vsr(mcd212_regs_t *mcd212, int channel, UINT8 *pixels_r, UINT8 *pixels_g, UINT8 *pixels_b)
{
    running_machine &machine = mcd212->machine();
    cdi_state *state = machine.driver_data<cdi_state>();
    UINT8 *data = channel ? (UINT8*)state->m_planeb : (UINT8*)state->m_planea;
    UINT32 vsr = mcd212_get_vsr(mcd212, channel) & 0x0007ffff;
    UINT8 done = 0;
    UINT32 x = 0;
    UINT32 icm_mask = channel ? MCD212_ICM_MODE2 : MCD212_ICM_MODE1;
    UINT32 icm_shift = channel ? MCD212_ICM_MODE2_SHIFT : MCD212_ICM_MODE1_SHIFT;
    UINT8 icm = (mcd212->channel[0].image_coding_method & icm_mask) >> icm_shift;
    UINT8 *clut_r = mcd212->channel[0].clut_r;
    UINT8 *clut_g = mcd212->channel[0].clut_g;
    UINT8 *clut_b = mcd212->channel[0].clut_b;
    UINT8 mosaic_enable = ((mcd212->channel[channel].ddr & MCD212_DDR_FT) == MCD212_DDR_FT_MOSAIC);
    UINT8 mosaic_factor = 1 << (((mcd212->channel[channel].ddr & MCD212_DDR_MT) >> MCD212_DDR_MT_SHIFT) + 1);
    int mosaic_index = 0;
    UINT32 width = mcd212_get_screen_width(mcd212);

    //printf( "vsr before: %08x: ", vsr );
    //fflush(stdout);

    if(!icm || !vsr)
    {
        memset(pixels_r, 0x10, width);
        memset(pixels_g, 0x10, width);
        memset(pixels_b, 0x10, width);
        return;
    }

    while(!done)
    {
        UINT8 byte = data[(vsr & 0x0007ffff) ^ 1];
        vsr++;
        switch(mcd212->channel[channel].ddr & MCD212_DDR_FT)
        {
            case MCD212_DDR_FT_BMP:
            case MCD212_DDR_FT_BMP2:
            case MCD212_DDR_FT_MOSAIC:
                if(mcd212->channel[channel].dcr & MCD212_DCR_CM)
                {
                    // 4-bit Bitmap
                    verboselog(machine, 0, "Unsupported display mode: 4-bit Bitmap\n" );
                }
                else
                {
                    // 8-bit Bitmap
                    if(icm == 5)
                    {
                        BYTE68K bY;
                        BYTE68K bU;
                        BYTE68K bV;
                        switch(channel)
                        {
                            case 0:
                                bY = (mcd212->channel[0].dyuv_abs_start_a >> 16) & 0x000000ff;
                                bU = (mcd212->channel[0].dyuv_abs_start_a >>  8) & 0x000000ff;
                                bV = (mcd212->channel[0].dyuv_abs_start_a >>  0) & 0x000000ff;
                                break;
                            case 1:
                                bY = (mcd212->channel[1].dyuv_abs_start_b >> 16) & 0x000000ff;
                                bU = (mcd212->channel[1].dyuv_abs_start_b >>  8) & 0x000000ff;
                                bV = (mcd212->channel[1].dyuv_abs_start_b >>  0) & 0x000000ff;
                                break;
                            default:
                                bY = bU = bV = 0x80;
                                break;
                        }
                        for(; x < width; x += 2)
                        {
                            BYTE68K b0 = byte;
                            BYTE68K bU1 = bU + state->m_mcd212_ab.deltaUV[b0];
                            BYTE68K bY0 = bY + state->m_mcd212_ab.deltaY[b0];

                            BYTE68K b1 = data[(vsr & 0x0007ffff) ^ 1];
                            BYTE68K bV1 = bV + state->m_mcd212_ab.deltaUV[b1];
                            BYTE68K bY1 = bY0 + state->m_mcd212_ab.deltaY[b1];

                            BYTE68K bU0 = (bU + bU1) >> 1;
                            BYTE68K bV0 = (bV + bV1) >> 1;

                            BYTE68K *pbLimit;

                            vsr++;

                            bY = bY0;
                            bU = bU0;
                            bV = bV0;

                            pbLimit = state->m_mcd212_ab.limit + bY + BYTE68K_MAX;

                            pixels_r[x + 0] = pixels_r[x + 1] = pbLimit[state->m_mcd212_ab.matrixVR[bV]];
                            pixels_g[x + 0] = pixels_g[x + 1] = pbLimit[state->m_mcd212_ab.matrixUG[bU] + state->m_mcd212_ab.matrixVG[bV]];
                            pixels_b[x + 0] = pixels_b[x + 1] = pbLimit[state->m_mcd212_ab.matrixUB[bU]];

                            if(mosaic_enable)
                            {
                                for(mosaic_index = 0; mosaic_index < mosaic_factor; mosaic_index++)
                                {
                                    pixels_r[x + 0 + mosaic_index*2] = pixels_r[x + 0];
                                    pixels_g[x + 0 + mosaic_index*2] = pixels_g[x + 0];
                                    pixels_b[x + 0 + mosaic_index*2] = pixels_b[x + 0];
                                    pixels_r[x + 1 + mosaic_index*2] = pixels_r[x + 1];
                                    pixels_g[x + 1 + mosaic_index*2] = pixels_g[x + 1];
                                    pixels_b[x + 1 + mosaic_index*2] = pixels_b[x + 1];
                                }
                                x += mosaic_factor * 2;
                            }
                            else
                            {
                                x += 2;
                            }

                            bY = bY1;
                            bU = bU1;
                            bV = bV1;

                            pbLimit = state->m_mcd212_ab.limit + bY + BYTE68K_MAX;

                            pixels_r[x + 0] = pixels_r[x + 1] = pbLimit[state->m_mcd212_ab.matrixVR[bV]];
                            pixels_g[x + 0] = pixels_g[x + 1] = pbLimit[state->m_mcd212_ab.matrixUG[bU] + state->m_mcd212_ab.matrixVG[bV]];
                            pixels_b[x + 0] = pixels_b[x + 1] = pbLimit[state->m_mcd212_ab.matrixUB[bU]];

                            if(mosaic_enable)
                            {
                                for(mosaic_index = 0; mosaic_index < mosaic_factor; mosaic_index++)
                                {
                                    pixels_r[x + 0 + mosaic_index*2] = pixels_r[x + 0];
                                    pixels_g[x + 0 + mosaic_index*2] = pixels_g[x + 0];
                                    pixels_b[x + 0 + mosaic_index*2] = pixels_b[x + 0];
                                    pixels_r[x + 1 + mosaic_index*2] = pixels_r[x + 1];
                                    pixels_g[x + 1 + mosaic_index*2] = pixels_g[x + 1];
                                    pixels_b[x + 1 + mosaic_index*2] = pixels_b[x + 1];
                                }
                                x += (mosaic_factor * 2) - 2;
                            }

                            byte = data[(vsr & 0x0007ffff) ^ 1];

                            vsr++;
                        }
                        mcd212_set_vsr(&state->m_mcd212_regs, channel, (vsr - 1) & 0x0007ffff);
                    }
                    else if(icm == 1 || icm == 3 || icm == 4)
                    {
                        for(; x < width; x += 2)
                        {
                            UINT8 clut_entry = BYTE_TO_CLUT(channel, icm, byte);
                            pixels_r[x + 0] = clut_r[clut_entry];
                            pixels_g[x + 0] = clut_g[clut_entry];
                            pixels_b[x + 0] = clut_b[clut_entry];
                            pixels_r[x + 1] = clut_r[clut_entry];
                            pixels_g[x + 1] = clut_g[clut_entry];
                            pixels_b[x + 1] = clut_b[clut_entry];
                            if(mosaic_enable)
                            {
                                for(mosaic_index = 0; mosaic_index < mosaic_factor; mosaic_index++)
                                {
                                    pixels_r[x + 0 + mosaic_index*2] = pixels_r[x + 0];
                                    pixels_g[x + 0 + mosaic_index*2] = pixels_g[x + 0];
                                    pixels_b[x + 0 + mosaic_index*2] = pixels_b[x + 0];
                                    pixels_r[x + 1 + mosaic_index*2] = pixels_r[x + 1];
                                    pixels_g[x + 1 + mosaic_index*2] = pixels_g[x + 1];
                                    pixels_b[x + 1 + mosaic_index*2] = pixels_b[x + 1];
                                }
                                x += (mosaic_factor * 2) - 2;
                            }
                            byte = data[(vsr & 0x0007ffff) ^ 1];
                            vsr++;
                        }
                        mcd212_set_vsr(&state->m_mcd212_regs, channel, (vsr - 1) & 0x0007ffff);
                    }
                    else if(icm == 11)
                    {
                        for(; x < width; x += 2)
                        {
                            UINT8 even_entry = BYTE_TO_CLUT(channel, icm, byte >> 4);
                            UINT8 odd_entry = BYTE_TO_CLUT(channel, icm, byte);
                            if(mosaic_enable)
                            {
                                for(mosaic_index = 0; mosaic_index < mosaic_factor; mosaic_index++)
                                {
                                    pixels_r[x + mosaic_index] = clut_r[even_entry];
                                    pixels_g[x + mosaic_index] = clut_g[even_entry];
                                    pixels_b[x + mosaic_index] = clut_b[even_entry];
                                }
                                for(mosaic_index = 0; mosaic_index < mosaic_factor; mosaic_index++)
                                {
                                    pixels_r[x + mosaic_factor + mosaic_index] = clut_r[odd_entry];
                                    pixels_g[x + mosaic_factor + mosaic_index] = clut_g[odd_entry];
                                    pixels_b[x + mosaic_factor + mosaic_index] = clut_b[odd_entry];
                                }
                                x += (mosaic_factor * 2) - 2;
                            }
                            else
                            {
                                pixels_r[x + 0] = clut_r[even_entry];
                                pixels_g[x + 0] = clut_g[even_entry];
                                pixels_b[x + 0] = clut_b[even_entry];
                                pixels_r[x + 1] = clut_r[odd_entry];
                                pixels_g[x + 1] = clut_g[odd_entry];
                                pixels_b[x + 1] = clut_b[odd_entry];
                            }
                            byte = data[(vsr & 0x0007ffff) ^ 1];
                            vsr++;
                        }
                        mcd212_set_vsr(&state->m_mcd212_regs, channel, (vsr - 1) & 0x0007ffff);
                    }
                    else
                    {
                        for(; x < width; x++)
                        {
                            pixels_r[x] = 0x10;
                            pixels_g[x] = 0x10;
                            pixels_b[x] = 0x10;
                        }
                    }
                }
                done = 1;
                break;
            case MCD212_DDR_FT_RLE:
                if(mcd212->channel[channel].dcr & MCD212_DCR_CM)
                {
                    verboselog(machine, 0, "Unsupported display mode: 4-bit RLE\n" );
                    done = 1;
                }
                else
                {
                    if(byte & 0x80)
                    {
                        // Run length
                        UINT8 length = data[((vsr++) & 0x0007ffff) ^ 1];
                        if(!length)
                        {
                            UINT8 clut_entry = BYTE_TO_CLUT(channel, icm, byte);
                            UINT8 r = clut_r[clut_entry];
                            UINT8 g = clut_g[clut_entry];
                            UINT8 b = clut_b[clut_entry];
                            // Go to the end of the line
                            for(; x < width; x++)
                            {
                                pixels_r[x] = r;
                                pixels_g[x] = g;
                                pixels_b[x] = b;
                                x++;
                                pixels_r[x] = r;
                                pixels_g[x] = g;
                                pixels_b[x] = b;
                            }
                            done = 1;
                            mcd212_set_vsr(&state->m_mcd212_regs, channel, vsr);
                        }
                        else
                        {
                            int end = x + (length * 2);
                            UINT8 clut_entry = BYTE_TO_CLUT(channel, icm, byte);
                            UINT8 r = clut_r[clut_entry];
                            UINT8 g = clut_g[clut_entry];
                            UINT8 b = clut_b[clut_entry];
                            for(; x < end && x < width; x++)
                            {
                                pixels_r[x] = r;
                                pixels_g[x] = g;
                                pixels_b[x] = b;
                                x++;
                                pixels_r[x] = r;
                                pixels_g[x] = g;
                                pixels_b[x] = b;
                            }
                            if(x >= width)
                            {
                                done = 1;
                                mcd212_set_vsr(&state->m_mcd212_regs, channel, vsr);
                            }
                        }
                    }
                    else
                    {
                        // Single pixel
                        UINT8 clut_entry = BYTE_TO_CLUT(channel, icm, byte);
                        pixels_r[x] = clut_r[clut_entry];
                        pixels_g[x] = clut_g[clut_entry];
                        pixels_b[x] = clut_b[clut_entry];
                        x++;
                        pixels_r[x] = clut_r[clut_entry];
                        pixels_g[x] = clut_g[clut_entry];
                        pixels_b[x] = clut_b[clut_entry];
                        x++;
                        if(x >= width)
                        {
                            done = 1;
                            mcd212_set_vsr(&state->m_mcd212_regs, channel, vsr);
                        }
                    }
                }
                break;
        }
    }

    //printf( ": vsr after: %08x\n", vsr);
    //mcd212_set_vsr(&state->m_mcd212_regs, channel, vsr);
}

static const UINT32 mcd212_4bpp_color[16] =
{
    0x00101010, 0x0010107a, 0x00107a10, 0x00107a7a, 0x007a1010, 0x007a107a, 0x007a7a10, 0x007a7a7a,
    0x00101010, 0x001010e6, 0x0010e610, 0x0010e6e6, 0x00e61010, 0x00e610e6, 0x00e6e610, 0x00e6e6e6
};

static void mcd212_mix_lines(mcd212_regs_t *mcd212, UINT8 *plane_a_r, UINT8 *plane_a_g, UINT8 *plane_a_b, UINT8 *plane_b_r, UINT8 *plane_b_g, UINT8 *plane_b_b, UINT32 *out)
{
    running_machine &machine = mcd212->machine();
    int x = 0;
    UINT8 debug_mode = input_port_read(machine, "DEBUG");
    UINT8 global_plane_a_disable = debug_mode & 1;
    UINT8 global_plane_b_disable = debug_mode & 2;
    UINT8 debug_backdrop_enable = debug_mode & 4;
    UINT8 debug_backdrop_index = debug_mode >> 4;
    UINT32 backdrop = debug_backdrop_enable ? mcd212_4bpp_color[debug_backdrop_index] : mcd212_4bpp_color[mcd212->channel[0].backdrop_color];
    UINT8 transparency_mode_a = (mcd212->channel[0].transparency_control >> 0) & 0x0f;
    UINT8 transparency_mode_b = (mcd212->channel[0].transparency_control >> 8) & 0x0f;
    UINT8 transparent_color_a_r = (UINT8)(mcd212->channel[0].transparent_color_a >> 16);
    UINT8 transparent_color_a_g = (UINT8)(mcd212->channel[0].transparent_color_a >>  8);
    UINT8 transparent_color_a_b = (UINT8)(mcd212->channel[0].transparent_color_a >>  0);
    UINT8 transparent_color_b_r = (UINT8)(mcd212->channel[1].transparent_color_b >> 16);
    UINT8 transparent_color_b_g = (UINT8)(mcd212->channel[1].transparent_color_b >>  8);
    UINT8 transparent_color_b_b = (UINT8)(mcd212->channel[1].transparent_color_b >>  0);
    UINT8 image_coding_method_a = mcd212->channel[0].image_coding_method & 0x0000000f;
    UINT8 image_coding_method_b = (mcd212->channel[0].image_coding_method >> 8) & 0x0000000f;
    UINT8 dyuv_enable_a = (image_coding_method_a == 5);
    UINT8 dyuv_enable_b = (image_coding_method_b == 5);
    UINT8 mosaic_enable_a = (mcd212->channel[0].mosaic_hold_a & 0x800000) >> 23;
    UINT8 mosaic_enable_b = (mcd212->channel[1].mosaic_hold_b & 0x800000) >> 23;
    UINT8 mosaic_count_a = (mcd212->channel[0].mosaic_hold_a & 0x0000ff) << 1;
    UINT8 mosaic_count_b = (mcd212->channel[1].mosaic_hold_b & 0x0000ff) << 1;
    for(x = 0; x < 768; x++)
    {
        out[x] = backdrop;
        if(!(mcd212->channel[0].transparency_control & MCD212_TCR_DISABLE_MX))
        {
            UINT8 abr = MCD212_LIM(((MCD212_LIM((INT32)plane_a_r[x] - 16) * mcd212->channel[0].weight_factor_a[x]) >> 6) + ((MCD212_LIM((INT32)plane_b_r[x] - 16) * mcd212->channel[1].weight_factor_b[x]) >> 6) + 16);
            UINT8 abg = MCD212_LIM(((MCD212_LIM((INT32)plane_a_g[x] - 16) * mcd212->channel[0].weight_factor_a[x]) >> 6) + ((MCD212_LIM((INT32)plane_b_g[x] - 16) * mcd212->channel[1].weight_factor_b[x]) >> 6) + 16);
            UINT8 abb = MCD212_LIM(((MCD212_LIM((INT32)plane_a_b[x] - 16) * mcd212->channel[0].weight_factor_a[x]) >> 6) + ((MCD212_LIM((INT32)plane_b_b[x] - 16) * mcd212->channel[1].weight_factor_b[x]) >> 6) + 16);
            out[x] = (abr << 16) | (abg << 8) | abb;
        }
        else
        {
            UINT8 plane_enable_a = 0;
            UINT8 plane_enable_b = 0;
            UINT8 plane_a_r_cur = mosaic_enable_a ? plane_a_r[x - (x % mosaic_count_a)] : plane_a_r[x];
            UINT8 plane_a_g_cur = mosaic_enable_a ? plane_a_g[x - (x % mosaic_count_a)] : plane_a_g[x];
            UINT8 plane_a_b_cur = mosaic_enable_a ? plane_a_b[x - (x % mosaic_count_a)] : plane_a_b[x];
            UINT8 plane_b_r_cur = mosaic_enable_b ? plane_b_r[x - (x % mosaic_count_b)] : plane_b_r[x];
            UINT8 plane_b_g_cur = mosaic_enable_b ? plane_b_g[x - (x % mosaic_count_b)] : plane_b_g[x];
            UINT8 plane_b_b_cur = mosaic_enable_b ? plane_b_b[x - (x % mosaic_count_b)] : plane_b_b[x];
            switch(transparency_mode_a)
            {
                case 0:
                    plane_enable_a = 0;
                    break;
                case 1:
                    plane_enable_a = (plane_a_r_cur != transparent_color_a_r || plane_a_g_cur != transparent_color_a_g || plane_a_b_cur != transparent_color_a_b);
                    break;
                case 3:
                    plane_enable_a = !mcd212->region_flag_0[x];
                    break;
                case 4:
                    plane_enable_a = !mcd212->region_flag_1[x];
                    break;
                case 5:
                    plane_enable_a = (plane_a_r_cur != transparent_color_a_r || plane_a_g_cur != transparent_color_a_g || plane_a_b_cur != transparent_color_a_b) && (dyuv_enable_a || mcd212->region_flag_0[x] == 0);
                    break;
                case 6:
                    plane_enable_a = (plane_a_r_cur != transparent_color_a_r || plane_a_g_cur != transparent_color_a_g || plane_a_b_cur != transparent_color_a_b) && (dyuv_enable_a || mcd212->region_flag_1[x] == 0);
                    break;
                case 8:
                    plane_enable_a = 1;
                    break;
                case 9:
                    plane_enable_a = (plane_a_r_cur == transparent_color_a_r && plane_a_g_cur == transparent_color_a_g && plane_a_b_cur == transparent_color_a_b);
                    break;
                case 11:
                    plane_enable_a = mcd212->region_flag_0[x];
                    break;
                case 12:
                    plane_enable_a = mcd212->region_flag_1[x];
                    break;
                case 13:
                    plane_enable_a = (plane_a_r_cur == transparent_color_a_r && plane_a_g_cur == transparent_color_a_g && plane_a_b_cur == transparent_color_a_b) || dyuv_enable_a || mcd212->region_flag_0[x] == 1;
                    break;
                case 14:
                    plane_enable_a = (plane_a_r_cur == transparent_color_a_r && plane_a_g_cur == transparent_color_a_g && plane_a_b_cur == transparent_color_a_b) || dyuv_enable_a || mcd212->region_flag_1[x] == 1;
                    break;
                default:
                    verboselog(machine, 0, "Unhandled transparency mode for plane A: %d\n", transparency_mode_a);
                    plane_enable_a = 1;
                    break;
            }
            switch(transparency_mode_b)
            {
                case 0:
                    plane_enable_b = 0;
                    break;
                case 1:
                    plane_enable_b = (plane_b_r_cur != transparent_color_b_r || plane_b_g_cur != transparent_color_b_g || plane_b_b_cur != transparent_color_b_b);
                    break;
                case 3:
                    plane_enable_b = !mcd212->region_flag_0[x];
                    break;
                case 4:
                    plane_enable_b = !mcd212->region_flag_1[x];
                    break;
                case 5:
                    plane_enable_b = (plane_b_r_cur != transparent_color_b_r || plane_b_g_cur != transparent_color_b_g || plane_b_b_cur != transparent_color_b_b) && (dyuv_enable_b || mcd212->region_flag_0[x] == 0);
                    break;
                case 6:
                    plane_enable_b = (plane_b_r_cur != transparent_color_b_r || plane_b_g_cur != transparent_color_b_g || plane_b_b_cur != transparent_color_b_b) && (dyuv_enable_b || mcd212->region_flag_1[x] == 0);
                    break;
                case 8:
                    plane_enable_b = 1;
                    break;
                case 9:
                    plane_enable_b = (plane_b_r_cur == transparent_color_b_r && plane_b_g_cur == transparent_color_b_g && plane_b_b_cur == transparent_color_b_b);
                    break;
                case 11:
                    plane_enable_b = mcd212->region_flag_0[x];
                    break;
                case 12:
                    plane_enable_b = mcd212->region_flag_1[x];
                    break;
                case 13:
                    plane_enable_b = (plane_b_r_cur == transparent_color_b_r && plane_b_g_cur == transparent_color_b_g && plane_b_b_cur == transparent_color_b_b) || dyuv_enable_b || mcd212->region_flag_0[x] == 1;
                    break;
                case 14:
                    plane_enable_b = (plane_b_r_cur == transparent_color_b_r && plane_b_g_cur == transparent_color_b_g && plane_b_b_cur == transparent_color_b_b) || dyuv_enable_b || mcd212->region_flag_1[x] == 1;
                    break;
                default:
                    verboselog(machine, 0, "Unhandled transparency mode for plane B: %d\n", transparency_mode_b);
                    plane_enable_b = 1;
                    break;
            }
            if(global_plane_a_disable)
            {
                plane_enable_a = 0;
            }
            if(global_plane_b_disable)
            {
                plane_enable_b = 0;
            }
            plane_a_r_cur = MCD212_LIM(((MCD212_LIM((INT32)plane_a_r_cur - 16) * mcd212->channel[0].weight_factor_a[x]) >> 6) + 16);
            plane_a_g_cur = MCD212_LIM(((MCD212_LIM((INT32)plane_a_g_cur - 16) * mcd212->channel[0].weight_factor_a[x]) >> 6) + 16);
            plane_a_b_cur = MCD212_LIM(((MCD212_LIM((INT32)plane_a_b_cur - 16) * mcd212->channel[0].weight_factor_a[x]) >> 6) + 16);
            plane_b_r_cur = MCD212_LIM(((MCD212_LIM((INT32)plane_b_r_cur - 16) * mcd212->channel[1].weight_factor_b[x]) >> 6) + 16);
            plane_b_g_cur = MCD212_LIM(((MCD212_LIM((INT32)plane_b_g_cur - 16) * mcd212->channel[1].weight_factor_b[x]) >> 6) + 16);
            plane_b_b_cur = MCD212_LIM(((MCD212_LIM((INT32)plane_b_b_cur - 16) * mcd212->channel[1].weight_factor_b[x]) >> 6) + 16);
            switch(mcd212->channel[0].plane_order)
            {
                case MCD212_POR_AB:
                    if(plane_enable_a)
                    {
                        out[x] = (plane_a_r_cur << 16) | (plane_a_g_cur << 8) | plane_a_b_cur;
                    }
                    else if(plane_enable_b)
                    {
                        out[x] = (plane_b_r_cur << 16) | (plane_b_g_cur << 8) | plane_b_b_cur;
                    }
                    break;
                case MCD212_POR_BA:
                    if(plane_enable_b)
                    {
                        out[x] = (plane_b_r_cur << 16) | (plane_b_g_cur << 8) | plane_b_b_cur;
                    }
                    else if(plane_enable_a)
                    {
                        out[x] = (plane_a_r_cur << 16) | (plane_a_g_cur << 8) | plane_a_b_cur;
                    }
                    break;
            }
        }
    }
}

static void mcd212_draw_cursor(mcd212_regs_t *mcd212, UINT32 *scanline, int y)
{
    if(mcd212->channel[0].cursor_control & MCD212_CURCNT_EN)
    {
        UINT16 curx =  mcd212->channel[0].cursor_position        & 0x3ff;
        UINT16 cury = ((mcd212->channel[0].cursor_position >> 12) & 0x3ff) + 22;
        UINT32 x = 0;
        if(y >= cury && y < (cury + 16))
        {
            UINT32 color = mcd212_4bpp_color[mcd212->channel[0].cursor_control & MCD212_CURCNT_COLOR];
            y -= cury;
            if(mcd212->channel[0].cursor_control & MCD212_CURCNT_CUW)
            {
                for(x = curx; x < curx + 64 && x < 768; x++)
                {
                    if(mcd212->channel[0].cursor_pattern[y] & (1 << (15 - ((x - curx) >> 2))))
                    {
                        scanline[(x++)/2] = color;
                        scanline[(x++)/2] = color;
                        scanline[(x++)/2] = color;
                        scanline[(x/2)] = color;
                    }
                    else
                    {
                    }
                }
            }
            else
            {
                for(x = curx; x < curx + 32 && x < 768; x++)
                {
                    if(mcd212->channel[0].cursor_pattern[y] & (1 << (15 - ((x - curx) >> 1))))
                    {
                        scanline[(x++)/2] = color;
                        scanline[x/2] = color;
                    }
                    else
                    {
                    }
                }
            }
        }
    }
}

static void mcd212_draw_scanline(mcd212_regs_t *mcd212, int y)
{
    running_machine &machine = mcd212->machine();
    bitmap_t *bitmap = machine.generic.tmpbitmap;
    UINT8 plane_a_r[768], plane_a_g[768], plane_a_b[768];
    UINT8 plane_b_r[768], plane_b_g[768], plane_b_b[768];
    UINT32 out[768];
    UINT32 *scanline = BITMAP_ADDR32(bitmap, y, 0);
    int x;

    mcd212_process_vsr(mcd212, 0, plane_a_r, plane_a_g, plane_a_b);
    mcd212_process_vsr(mcd212, 1, plane_b_r, plane_b_g, plane_b_b);

    mcd212_mix_lines(mcd212, plane_a_r, plane_a_g, plane_a_b, plane_b_r, plane_b_g, plane_b_b, out);

    for(x = 0; x < 384; x++)
    {
        scanline[x] = out[x*2];
    }

    mcd212_draw_cursor(mcd212, scanline, y);
}

READ16_HANDLER( mcd212_r )
{
    cdi_state *state = space->machine().driver_data<cdi_state>();
    mcd212_regs_t *mcd212 = &state->m_mcd212_regs;
    UINT8 channel = 1 - (offset / 8);

    switch(offset)
    {
        case 0x00/2:
        case 0x10/2:
            if(ACCESSING_BITS_0_7)
            {
                verboselog(space->machine(), 12, "mcd212_r: Status Register %d: %02x & %04x\n", channel + 1, mcd212->channel[1 - (offset / 8)].csrr, mem_mask);
                if(channel == 0)
                {
                    return mcd212->channel[0].csrr;
                }
                else
                {
                    UINT8 old_csr = mcd212->channel[1].csrr;
                    UINT8 interrupt1 = (state->m_scc68070_regs.lir >> 4) & 7;
                    //UINT8 interrupt2 = state->m_scc68070_regs.lir & 7;
                    mcd212->channel[1].csrr &= ~(MCD212_CSR2R_IT1 | MCD212_CSR2R_IT2);
                    if(interrupt1)
                    {
                        cputag_set_input_line(space->machine(), "maincpu", M68K_IRQ_1 + (interrupt1 - 1), CLEAR_LINE);
                    }
                    //if(interrupt2)
                    //{
                    //  cputag_set_input_line(space->machine(), "maincpu", M68K_IRQ_1 + (interrupt2 - 1), CLEAR_LINE);
                    //}
                    return old_csr;
                }
            }
            else
            {
                verboselog(space->machine(), 2, "mcd212_r: Unknown Register %d: %04x\n", channel + 1, mem_mask);
            }
            break;
        case 0x02/2:
        case 0x12/2:
            verboselog(space->machine(), 2, "mcd212_r: Display Command Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, mcd212->channel[1 - (offset / 8)].dcr, mem_mask);
            return mcd212->channel[1 - (offset / 8)].dcr;
        case 0x04/2:
        case 0x14/2:
            verboselog(space->machine(), 2, "mcd212_r: Video Start Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, mcd212->channel[1 - (offset / 8)].vsr, mem_mask);
            return mcd212->channel[1 - (offset / 8)].vsr;
        case 0x08/2:
        case 0x18/2:
            verboselog(space->machine(), 2, "mcd212_r: Display Decoder Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, mcd212->channel[1 - (offset / 8)].ddr, mem_mask);
            return mcd212->channel[1 - (offset / 8)].ddr;
        case 0x0a/2:
        case 0x1a/2:
            verboselog(space->machine(), 2, "mcd212_r: DCA Pointer Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, mcd212->channel[1 - (offset / 8)].dcp, mem_mask);
            return mcd212->channel[1 - (offset / 8)].dcp;
        default:
            verboselog(space->machine(), 2, "mcd212_r: Unknown Register %d & %04x\n", (1 - (offset / 8)) + 1, mem_mask);
            break;
    }

    return 0;
}

WRITE16_HANDLER( mcd212_w )
{
    cdi_state *state = space->machine().driver_data<cdi_state>();
    mcd212_regs_t *mcd212 = &state->m_mcd212_regs;

    switch(offset)
    {
        case 0x00/2:
        case 0x10/2:
            verboselog(space->machine(), 2, "mcd212_w: Status Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, data, mem_mask);
            COMBINE_DATA(&mcd212->channel[1 - (offset / 8)].csrw);
            mcd212_update_visible_area(space->machine());
            break;
        case 0x02/2:
        case 0x12/2:
            verboselog(space->machine(), 2, "mcd212_w: Display Command Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, data, mem_mask);
            COMBINE_DATA(&mcd212->channel[1 - (offset / 8)].dcr);
            mcd212_update_visible_area(space->machine());
            break;
        case 0x04/2:
        case 0x14/2:
            verboselog(space->machine(), 2, "mcd212_w: Video Start Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, data, mem_mask);
            COMBINE_DATA(&mcd212->channel[1 - (offset / 8)].vsr);
            break;
        case 0x08/2:
        case 0x18/2:
            verboselog(space->machine(), 2, "mcd212_w: Display Decoder Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, data, mem_mask);
            COMBINE_DATA(&mcd212->channel[1 - (offset / 8)].ddr);
            break;
        case 0x0a/2:
        case 0x1a/2:
            verboselog(space->machine(), 2, "mcd212_w: DCA Pointer Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, data, mem_mask);
            COMBINE_DATA(&mcd212->channel[1 - (offset / 8)].dcp);
            break;
        default:
            verboselog(space->machine(), 2, "mcd212_w: Unknown Register %d: %04x & %04x\n", (1 - (offset / 8)) + 1, data, mem_mask);
            break;
    }
}

TIMER_CALLBACK( mcd212_perform_scan )
{
    cdi_state *state = machine.driver_data<cdi_state>();
    mcd212_regs_t *mcd212 = &state->m_mcd212_regs;
    int scanline = machine.primary_screen->vpos();

    if(/*mcd212->channel[0].dcr & MCD212_DCR_DE*/1)
    {
        if(scanline == 0)
        {
            // Process ICA
            int index = 0;
            verboselog(machine, 6, "Frame Start\n" );
            mcd212->channel[0].csrr &= 0x7f;
            for(index = 0; index < 2; index++)
            {
                if(mcd212->channel[index].dcr & MCD212_DCR_ICA)
                {
                    mcd212_process_ica(mcd212, index);
                }
            }
            cdi220_draw_lcd(machine, scanline);
        }
        else if(scanline < 22)
        {
            cdi220_draw_lcd(machine, scanline);
        }
        else if(scanline >= 22)
        {
            int index = 0;
            mcd212->channel[0].csrr |= 0x80;
            // Process VSR
            mcd212_draw_scanline(mcd212, scanline);
            // Process DCA
            for(index = 0; index < 2; index++)
            {
                if(mcd212->channel[index].dcr & MCD212_DCR_DCA)
                {
                    if(scanline == 22)
                    {
                        mcd212->channel[index].dca = mcd212_get_dcp(mcd212, index);
                    }
                    mcd212_process_dca(mcd212, index);
                }
            }
            if(scanline == 301)
            {
                mcd212->channel[0].csrr ^= 0x20;
            }
        }
    }
    mcd212->scan_timer->adjust(machine.primary_screen->time_until_pos(( scanline + 1 ) % 302, 0));
}

void mcd212_init(running_machine &machine, mcd212_regs_t *mcd212)
{
    mcd212->m_machine = &machine;

    int index = 0;
    for(index = 0; index < 2; index++)
    {
        mcd212->channel[index].csrr = 0;
        mcd212->channel[index].csrw = 0;
        mcd212->channel[index].dcr = 0;
        mcd212->channel[index].vsr = 0;
        mcd212->channel[index].ddr = 0;
        mcd212->channel[index].dcp = 0;
        mcd212->channel[index].dca = 0;
        memset(mcd212->channel[index].clut_r, 0, 768);
        memset(mcd212->channel[index].clut_g, 0, 768);
        memset(mcd212->channel[index].clut_b, 0, 768);
        mcd212->channel[index].image_coding_method = 0;
        mcd212->channel[index].transparency_control = 0;
        mcd212->channel[index].plane_order = 0;
        mcd212->channel[index].clut_bank = 0;
        mcd212->channel[index].transparent_color_a = 0;
        mcd212->channel[index].transparent_color_b = 0;
        mcd212->channel[index].mask_color_a = 0;
        mcd212->channel[index].mask_color_b = 0;
        mcd212->channel[index].dyuv_abs_start_a = 0;
        mcd212->channel[index].dyuv_abs_start_b = 0;
        mcd212->channel[index].cursor_position = 0;
        mcd212->channel[index].cursor_control = 0;
        memset((UINT8*)&mcd212->channel[index].cursor_pattern, 0, 16 * sizeof(UINT32));
        memset((UINT8*)&mcd212->channel[index].region_control, 0, 8 * sizeof(UINT32));
        mcd212->channel[index].backdrop_color = 0;
        mcd212->channel[index].mosaic_hold_a = 0;
        mcd212->channel[index].mosaic_hold_b = 0;
        memset(mcd212->channel[index].weight_factor_a, 0, 768);
        memset(mcd212->channel[index].weight_factor_b, 0, 768);
    }
    memset(mcd212->region_flag_0, 0, 768);
    memset(mcd212->region_flag_1, 0, 768);

    state_save_register_global_array(machine, mcd212->region_flag_0);
    state_save_register_global_array(machine, mcd212->region_flag_1);
    state_save_register_global(machine, mcd212->channel[0].csrr);
    state_save_register_global(machine, mcd212->channel[0].csrw);
    state_save_register_global(machine, mcd212->channel[0].dcr);
    state_save_register_global(machine, mcd212->channel[0].vsr);
    state_save_register_global(machine, mcd212->channel[0].ddr);
    state_save_register_global(machine, mcd212->channel[0].dcp);
    state_save_register_global(machine, mcd212->channel[0].dca);
    state_save_register_global_array(machine, mcd212->channel[0].clut_r);
    state_save_register_global_array(machine, mcd212->channel[0].clut_g);
    state_save_register_global_array(machine, mcd212->channel[0].clut_b);
    state_save_register_global(machine, mcd212->channel[0].image_coding_method);
    state_save_register_global(machine, mcd212->channel[0].transparency_control);
    state_save_register_global(machine, mcd212->channel[0].plane_order);
    state_save_register_global(machine, mcd212->channel[0].clut_bank);
    state_save_register_global(machine, mcd212->channel[0].transparent_color_a);
    state_save_register_global(machine, mcd212->channel[0].transparent_color_b);
    state_save_register_global(machine, mcd212->channel[0].mask_color_a);
    state_save_register_global(machine, mcd212->channel[0].mask_color_b);
    state_save_register_global(machine, mcd212->channel[0].dyuv_abs_start_a);
    state_save_register_global(machine, mcd212->channel[0].dyuv_abs_start_b);
    state_save_register_global(machine, mcd212->channel[0].cursor_position);
    state_save_register_global(machine, mcd212->channel[0].cursor_control);
    state_save_register_global_array(machine, mcd212->channel[0].cursor_pattern);
    state_save_register_global_array(machine, mcd212->channel[0].region_control);
    state_save_register_global(machine, mcd212->channel[0].backdrop_color);
    state_save_register_global(machine, mcd212->channel[0].mosaic_hold_a);
    state_save_register_global(machine, mcd212->channel[0].mosaic_hold_b);
    state_save_register_global_array(machine, mcd212->channel[0].weight_factor_a);
    state_save_register_global_array(machine, mcd212->channel[0].weight_factor_b);
    state_save_register_global(machine, mcd212->channel[1].csrr);
    state_save_register_global(machine, mcd212->channel[1].csrw);
    state_save_register_global(machine, mcd212->channel[1].dcr);
    state_save_register_global(machine, mcd212->channel[1].vsr);
    state_save_register_global(machine, mcd212->channel[1].ddr);
    state_save_register_global(machine, mcd212->channel[1].dcp);
    state_save_register_global(machine, mcd212->channel[1].dca);
    state_save_register_global_array(machine, mcd212->channel[1].clut_r);
    state_save_register_global_array(machine, mcd212->channel[1].clut_g);
    state_save_register_global_array(machine, mcd212->channel[1].clut_b);
    state_save_register_global(machine, mcd212->channel[1].image_coding_method);
    state_save_register_global(machine, mcd212->channel[1].transparency_control);
    state_save_register_global(machine, mcd212->channel[1].plane_order);
    state_save_register_global(machine, mcd212->channel[1].clut_bank);
    state_save_register_global(machine, mcd212->channel[1].transparent_color_a);
    state_save_register_global(machine, mcd212->channel[1].transparent_color_b);
    state_save_register_global(machine, mcd212->channel[1].mask_color_a);
    state_save_register_global(machine, mcd212->channel[1].mask_color_b);
    state_save_register_global(machine, mcd212->channel[1].dyuv_abs_start_a);
    state_save_register_global(machine, mcd212->channel[1].dyuv_abs_start_b);
    state_save_register_global(machine, mcd212->channel[1].cursor_position);
    state_save_register_global(machine, mcd212->channel[1].cursor_control);
    state_save_register_global_array(machine, mcd212->channel[1].cursor_pattern);
    state_save_register_global_array(machine, mcd212->channel[1].region_control);
    state_save_register_global(machine, mcd212->channel[1].backdrop_color);
    state_save_register_global(machine, mcd212->channel[1].mosaic_hold_a);
    state_save_register_global(machine, mcd212->channel[1].mosaic_hold_b);
    state_save_register_global_array(machine, mcd212->channel[1].weight_factor_a);
    state_save_register_global_array(machine, mcd212->channel[1].weight_factor_b);
}

void mcd212_ab_init(mcd212_ab_t *mcd212_ab)
{
    WORD68K w = 0;
    SWORD68K sw = 0;
    WORD68K d = 0;

    //* Delta decoding array.
    static const BYTE68K mcd212_abDelta[16] = { 0, 1, 4, 9, 16, 27, 44, 79, 128, 177, 212, 229, 240, 247, 252, 255 };

    // Initialize delta decoding arrays for each unsigned byte value b.
    for (d = 0; d < BYTE68K_MAX + 1; d++)
    {
        mcd212_ab->deltaY[d] = mcd212_abDelta[d & 15];
    }

    // Initialize delta decoding arrays for each unsigned byte value b.
    for (d = 0; d < (BYTE68K_MAX + 1); d++)
    {
        mcd212_ab->deltaUV[d] = mcd212_abDelta[d >> 4];
    }

    // Initialize color limit and clamp arrays.
    for (w = 0; w < 3 * BYTE68K_MAX; w++)
    {
        mcd212_ab->limit[w] = (w < BYTE68K_MAX + 16) ?  0 : w <= 16 + 2 * BYTE68K_MAX ? w - BYTE68K_MAX - 16 : BYTE68K_MAX;
        mcd212_ab->clamp[w] = (w < BYTE68K_MAX + 32) ? 16 : w <= 16 + 2 * BYTE68K_MAX ? w - BYTE68K_MAX - 16 : BYTE68K_MAX;
    }

    for (sw = 0; sw < 0x100; sw++)
    {
        mcd212_ab->matrixUB[sw] = (444 * (sw - 128)) / 256;
        mcd212_ab->matrixUG[sw] = - (86 * (sw - 128)) / 256;
        mcd212_ab->matrixVG[sw] = - (179 * (sw - 128)) / 256;
        mcd212_ab->matrixVR[sw] = (351 * (sw - 128)) / 256;
    }
}

VIDEO_START( cdimono1 )
{
    cdi_state *state = machine.driver_data<cdi_state>();

    VIDEO_START_CALL(generic_bitmapped);
    mcd212_ab_init(&state->m_mcd212_ab);
    mcd212_init(machine, &state->m_mcd212_regs);
    state->m_mcd212_regs.scan_timer = machine.scheduler().timer_alloc(FUNC(mcd212_perform_scan));
    state->m_mcd212_regs.scan_timer->adjust(machine.primary_screen->time_until_pos(0, 0));

    state->m_lcdbitmap = downcast<screen_device *>(machine.device("lcd"))->alloc_compatible_bitmap();
}

SCREEN_UPDATE( cdimono1 )
{
    copybitmap(bitmap, screen->machine().generic.tmpbitmap, 0, 0, 0, 0, cliprect);
    return 0;
}

SCREEN_UPDATE( cdimono1_lcd )
{
    cdi_state *state = screen->machine().driver_data<cdi_state>();
    copybitmap(bitmap, state->m_lcdbitmap, 0, 0, 0, 0, cliprect);
    return 0;
}
