/*****************************************************************************
 *
 *   z8010.c
 *   Portable Z8010 MMU
 *   Z8001 MAME MMU
 *
 *   Copyright Matt Knoth, all rights reserved.
 *   You can contact me at p8000@knothusa.net
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *****************************************************************************/

#include "driver.h"
#include "deprecat.h"
#include "z8010.h"

#define MAX_Z8010 4

static z8010_regs       z8010[MAX_Z8010];  /* n-MMU instances */

/***************************************************************************
    INIT and Reset HANDLERS
***************************************************************************/

void z8010_init(int which, z8010_interface *intf)
{
        z8010_regs *mmu = &z8010[which];

        assert(which < MAX_Z8010);
        memset(mmu, 0, sizeof(z8010_regs));
}

void z8010_reset(int which)
{
        z8010_regs *mmu = &z8010[which];

        assert(which < MAX_Z8010);
        memset(mmu, 0, sizeof(z8010_regs));
}

/***************************************************************************
    WRITE HANDLERS
***************************************************************************/

void z8010_w(int which, UINT8 opcode, UINT8 data)
{
        int i;
        z8010_regs *mmu = &z8010[which];

        switch(opcode) {
        case 0x00: /* Mode Register */
                   mmu->mode=data;
                   break;
        case 0x01: /* Segment Address Register */
                   mmu->sar=data & 0x3f;
                   break;
        case 0x08: /* Descriptor Base Field Register */
                   if(mmu->dscr & 0x01) 
                      mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff00) | data;
                   else
                      mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (data<<8) | (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0x00ff);
                   mmu->dscr=(mmu->dscr & 0x01)? 0:1;
                   break;
        case 0x09: /* Descriptor Limit Field Register */
                   mmu->seg_descr[mmu->sar & 0x3f].seg_limit=data;
                   mmu->dscr=0;
                   break;
        case 0x0a: /* Descriptor Attribute Field Register */
                   mmu->seg_descr[mmu->sar & 0x3f].seg_attr=data;
                   mmu->dscr=0;
                   break;
        case 0x0b: /* Descriptor consecutive access */
                        if(mmu->dscr==0) mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (data<<8) | (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0x00ff);
                   else if(mmu->dscr==1) mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff00) | data;
                   else if(mmu->dscr==2) mmu->seg_descr[mmu->sar & 0x3f].seg_limit = data;
                   else                  mmu->seg_descr[mmu->sar & 0x3f].seg_attr = data;
                   mmu->dscr=(mmu->dscr>=3)? 0 : (mmu->dscr+1);
                   break;
        case 0x0c: /* Descriptor Base Field consecutive access */
                   if(mmu->dscr & 0x01) {
                      mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff00) | data;
                      mmu->sar = (mmu->sar + 1) & 0x3f;
                      mmu->dscr= 0;
                   } else {
                      mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (data<<8) | (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0x00ff);
                      mmu->dscr= 1;
                   }
                   break;
        case 0x0d: /* Descriptor Limit Field consecutive access */
                   mmu->seg_descr[mmu->sar & 0x3f].seg_limit = data;
                   mmu->sar = (mmu->sar + 1) & 0x3f;
                   mmu->dscr=0;
                   break;
        case 0x0e: /* Descriptor Attribute Field consecutive access */
                   mmu->seg_descr[mmu->sar & 0x3f].seg_attr = data;
                   mmu->sar = (mmu->sar + 1) & 0x3f;
                   mmu->dscr=0;
                   break;
        case 0x0f: /* All Descriptor consecutive access */
                        if(mmu->dscr==0) mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (data<<8) | (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0x00ff);
                   else if(mmu->dscr==1) mmu->seg_descr[mmu->sar & 0x3f].seg_ba = (mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff00) | data;
                   else if(mmu->dscr==2) mmu->seg_descr[mmu->sar & 0x3f].seg_limit = data;
                   else                  mmu->seg_descr[mmu->sar & 0x3f].seg_attr = data;
                   if(mmu->dscr>=3) {
                      mmu->sar = (mmu->sar + 1) & 0x3f;
                      mmu->dscr= 0;
                   } else
                      mmu->dscr=mmu->dscr+1;
                   break;
        case 0x11: /* Reset Violation Type Register */
                   mmu->vio_type = 0;
//fprintf(stderr,"VIO-MMU%d: Reset Violation Type Register\n",which);
                   break;
        case 0x13: /* Reset SWW Flag in Violation Type Register */
                   mmu->vio_type &= ~0x40;
//fprintf(stderr,"VIO-MMU%d: Reset SWW Flag\n",which);
                   break;
        case 0x14: /* Reset FATL Flag in Violation Type Register */
                   mmu->vio_type &= ~0x80;
//fprintf(stderr,"VIO-MMU%d: Reset FATL Flag\n",which);
                   break;
        case 0x15: /* Set All CPU-INHIBIT Flags */
                   for(i=0; i<64; mmu->seg_descr[i++].seg_attr |= 0x04);
                   break;
        case 0x16: /* Set All DMA-INHIBIT Flags */
                   for(i=0; i<64; mmu->seg_descr[i++].seg_attr |= 0x10);
                   break;
        case 0x20: /* Write DSCR */
                   mmu->dscr=data & 0x03;
                   break;
        }
}

/***************************************************************************
    READ HANDLERS
***************************************************************************/

UINT8 z8010_r(int which, UINT8 opcode)
{
        UINT8 res;
        z8010_regs *mmu = &z8010[which];

        res=0;
        switch(opcode) {
        case 0x00: /* Mode Register */
                   res=mmu->mode;
                   break;
        case 0x01: /* Segment Address Register */
                   res=mmu->sar & 0x3f;
                   break;
        case 0x02: /* Violation Type Register */
                   res=mmu->vio_type;
//fprintf(stderr,"VIO-MMU%d: Read Violation Type Register\n",which);
                   break;
        case 0x03: /* Violation Segment Register */
                   res=mmu->vio_segno;
                   break;
        case 0x04: /* Violation Offset Register */
                   res=mmu->vio_offset;
                   break;
        case 0x05: /* Bus Cycle Status Register */
                   res=mmu->bus_status;
                   break;
        case 0x06: /* Instruction Segment Number Register */
                   res=mmu->instr_segno;
                   break;
        case 0x07: /* Instruction Offset Register */
                   res=mmu->instr_offset;
                   break;
        case 0x08: /* Descriptor Base Field Register */
                   res=(mmu->dscr & 0x01)? (mmu->seg_descr[mmu->sar & 0x3f].seg_ba) & 0xff : (mmu->seg_descr[mmu->sar & 0x3f].seg_ba) >>8;
                   mmu->dscr=(mmu->dscr & 0x01)? 0:1;
                   break;
        case 0x09: /* Descriptor Limit Field Register */
                   res=mmu->seg_descr[mmu->sar & 0x3f].seg_limit;
                   mmu->dscr=0;
                   break;
        case 0x0a: /* Descriptor Attribute Field Register */
                   res=mmu->seg_descr[mmu->sar & 0x3f].seg_attr;
                   mmu->dscr=0;
                   break;
        case 0x0b: /* Descriptor consecutive access */
                   res=(mmu->dscr==0)? mmu->seg_descr[mmu->sar & 0x3f].seg_ba >>8    : 
                       (mmu->dscr==1)? mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff :
                       (mmu->dscr==2)? mmu->seg_descr[mmu->sar & 0x3f].seg_limit     :
                                       mmu->seg_descr[mmu->sar & 0x3f].seg_attr;
                   mmu->dscr=(mmu->dscr>=3)? 0 : (mmu->dscr+1);
                   break;
        case 0x0c: /* Descriptor Base Field consecutive access */
                   if(mmu->dscr & 0x01) {
                      res=mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff;
                      mmu->sar = (mmu->sar + 1) & 0x3f;
                      mmu->dscr= 0;
                   } else {
                      res=mmu->seg_descr[mmu->sar & 0x3f].seg_ba >>8;
                      mmu->dscr= 1;
                   }
                   break;
        case 0x0d: /* Descriptor Limit Field consecutive access */
                   res=mmu->seg_descr[mmu->sar & 0x3f].seg_limit;
                   mmu->sar = (mmu->sar + 1) & 0x3f;
                   mmu->dscr=0;
                   break;
        case 0x0e: /* Descriptor Attribute Field consecutive access */
                   res=mmu->seg_descr[mmu->sar & 0x3f].seg_attr;
                   mmu->sar = (mmu->sar + 1) & 0x3f;
                   mmu->dscr=0;
                   break;
        case 0x0f: /* All Descriptor consecutive access */
                   res=(mmu->dscr==0)? mmu->seg_descr[mmu->sar & 0x3f].seg_ba >>8    :
                       (mmu->dscr==1)? mmu->seg_descr[mmu->sar & 0x3f].seg_ba & 0xff :
                       (mmu->dscr==2)? mmu->seg_descr[mmu->sar & 0x3f].seg_limit     :
                                       mmu->seg_descr[mmu->sar & 0x3f].seg_attr;
                   if(mmu->dscr>=3) {
                      mmu->sar = (mmu->sar + 1) & 0x3f;
                      mmu->dscr= 0;
                   } else
                      mmu->dscr=mmu->dscr+1;
                   break;
        case 0x20: /* Read DSCR */
                   res=mmu->dscr & 0x03;
                   break;
        }
        return res;
}

void z8010_get_info(int which, UINT32 state, cpuinfo *info)
{
        if(which>=MAX_Z8010) return;

        switch (state)
        {
                case CPUINFO_INT_REGISTER + Z8010_SAR:      info->i = z8010[which].sar;           break;  // Segment Address Register
                case CPUINFO_INT_REGISTER + Z8010_DSC:      info->i = z8010[which].dscr;          break;  // Descriptor Selection Counter Register
                case CPUINFO_INT_REGISTER + Z8010_MOD:      info->i = z8010[which].mode;          break;  // Mode Register
                case CPUINFO_INT_REGISTER + Z8010_VSN:      info->i = z8010[which].vio_segno;     break;  // Violation Segment Number
                case CPUINFO_INT_REGISTER + Z8010_VSO:      info->i = z8010[which].vio_offset;    break;  // Violation Segment Offset
                case CPUINFO_INT_REGISTER + Z8010_BCS:      info->i = z8010[which].bus_status;    break;  // Bus Cycle Status
                case CPUINFO_INT_REGISTER + Z8010_ISN:      info->i = z8010[which].instr_segno;   break;  // Instruction Segment Number
                case CPUINFO_INT_REGISTER + Z8010_ISO:      info->i = z8010[which].instr_offset;  break;  // Instruction Offset
                case CPUINFO_INT_REGISTER + Z8010_VIT:      info->i = z8010[which].instr_offset;  break;  // Violation Type

                case CPUINFO_PTR_CPU_SPECIFIC + Z8010_RGS:  info->p = &z8010[which];              break;  // z8010_regs structure
                case CPUINFO_PTR_CPU_SPECIFIC + Z8010_SDR:  info->p = &z8010[which].seg_descr[0]; break;  // z8010 seg_descr structure
        }
}

