/*****************************************************************************
 *
 *   z8010.h
 *   Portable Z8001 MMU emulator
 *   Z8010 MAME interface
 *
 *   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 "cpuintrf.h"

#ifndef Z8010_H
#define Z8010_H

enum {
       Z8010_SAR=1, Z8010_DSC, Z8010_MOD, Z8010_VSN, Z8010_VSO, Z8010_BCS,
       Z8010_ISN, Z8010_ISO, Z8010_VIT, 
       Z8010_RGS=0, Z8010_SDR=1 };

enum {
        Z8010_MMU_VTR_FATL  = 0x80,
        Z8010_MMU_VTR_SWW   = 0x40,
        Z8010_MMU_VTR_PWW   = 0x20,
        Z8010_MMU_VTR_EXCV  = 0x10,
        Z8010_MMU_VTR_CPUIV = 0x08,
        Z8010_MMU_VTR_SLV   = 0x04,
        Z8010_MMU_VTR_SYSV  = 0x02,
        Z8010_MMU_VTR_RDV   = 0x01
};

typedef void z8010_interface;

typedef struct {
     UINT16             seg_ba;        /* Segment Base Addrss */
     UINT8              seg_limit;     /* Segment Addr Limit  */
     UINT8              seg_attr;      /* Segment Attribute   */
} seginfo_t;

typedef struct _z8010_regs z8010_regs;
struct _z8010_regs
{
     seginfo_t          seg_descr[64]; /* segment descriptor  */
     UINT8              mode;          /* mode register       */
     UINT8              sar;           /* segment address     */
     UINT8              dscr;          /* selector counter    */
     UINT8              vio_type;      /* violation register  */
     UINT8              vio_segno;     /* violation segment   */
     UINT8              vio_offset;    /* violation offset    */
     UINT8              bus_status;    /* vio bus status      */
     UINT8              instr_segno;   /* instruction segno   */
     UINT8              instr_offset;  /* instruction offset  */
};

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

void z8010_init(int which, z8010_interface *intf);
void z8010_reset(int which);

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

void z8010_w(int which, UINT8 opcode, UINT8 data);

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

UINT8 z8010_r(int which, UINT8 opcode);

/***************************************************************************
    INFO HANDLERS
***************************************************************************/

void z8010_get_info(int which, UINT32 state, cpuinfo *info);

/***************************************************************************
    TRANSLATE HANDLER
***************************************************************************/
typedef void (*segtrap_callback)(offs_t address, UINT8 segno, UINT8 id);

#ifdef ENABLE_DEBUGGER
  #define DEBUG_ACTIVE_RETURN  if(mame_debug_is_active()) return
#else
  #define DEBUG_ACTIVE_RETURN
#endif

// Z8010 Trap Requests ////////////////////////////////////////////////////////////////////////////////
#define Z8010_IMPLEMENT_TRAP(driver)  INLINE void z8010_segment_trap_request_##driver( z8010_regs* pMMu, segtrap_callback trap_cb, int frdwr, int usermod, offs_t addr, UINT8 segno, UINT8 flags) \
{           UINT8 vio; \
            cpuinfo info; \
 \
            DEBUG_ACTIVE_RETURN; \
 \
            vio=pMMu->vio_type; \
            if(!vio) { \
               z8001_get_info(CPUINFO_INT_PREVIOUSPC, &info); \
               pMMu->vio_segno = segno; \
               pMMu->vio_offset= addr >>  8; \
               pMMu->bus_status = ((usermod)? 0x20:0x00) | ((frdwr>1)? 0x10:0x00) | ((frdwr==0)? 0x0c:0x08); /* grossly simplified ;-( */ \
               pMMu->instr_segno = info.i >> 24; \
               pMMu->instr_offset= info.i >>  8; \
               (*trap_cb)(addr, segno, pMMu->mode & 0x07); /* send trap to cpu */ \
            } else { \
               vio |= Z8010_MMU_VTR_FATL; \
            } \
 \
            pMMu->vio_type |= vio | flags; \
} 

// All purpose Z8010 address translation and protection - slowest translator
// return 0 for WR suppress, otherwise 1
#define Z8010_IMPLEMENT_TRANSLATE(driver) INLINE int z8010_translate_all_##driver( z8010_regs* pMMu, int frdwr, int usermod, offs_t *address, UINT8 segno, segtrap_callback trap_cb) \
{      \
       switch(pMMu->mode & 0xd0) { \
       case 0xd0: /* NMS flag dependency */ \
                  if(!(usermod) != !(pMMu->mode & 0x08))  {                                                      /* multiple tables selection */ \
                     return 1; \
                  } \
       case 0xc0: \
                  if(usermod && (pMMu->seg_descr[segno].seg_attr & 0x02)) {                                        /* System Violation Check */ \
                     z8010_segment_trap_request_##driver( pMMu, trap_cb, frdwr, usermod, *address, segno, Z8010_MMU_VTR_SYSV); \
                     return (frdwr > 1)? 0:1; \
                  } \
                  if(pMMu->seg_descr[segno].seg_attr & 0x20) {                                                     /* DIRW - Stack type access */ \
                     if((frdwr > 1) && !(*address & 0xff00)) {                                                   /* Primary Stack Range Warning */ \
                         if(!usermod && (pMMu->vio_type & 0x3f)) pMMu->vio_type |= Z8010_MMU_VTR_SWW;            /* Secondary Write Warning */ \
                         pMMu->vio_type |= Z8010_MMU_VTR_PWW;                                                    /* Primary Write Warning */ \
                     } \
                     if(((*address>>8) & 0xff) <= pMMu->seg_descr[segno].seg_limit) {                                               /* Segment Length violation */ \
                         z8010_segment_trap_request_##driver( pMMu, trap_cb, frdwr, usermod, *address, segno, Z8010_MMU_VTR_SLV); \
                         if(((*address>>8) & 0xff) == pMMu->seg_descr[segno].seg_limit) return 1; \
                         else                                        return (frdwr > 1)? 0:1; \
                     } \
                  } else { /* Data or Fetch type access*/ \
                     if(((*address>>8) & 0xff) > pMMu->seg_descr[segno].seg_limit) {                                                /* Segment Length violation */ \
                         z8010_segment_trap_request_##driver( pMMu, trap_cb, frdwr, usermod, *address, segno, Z8010_MMU_VTR_SLV); \
                         return (frdwr > 1)? 0:1; \
                     } \
                  } \
                  if((frdwr > 1) && (pMMu->seg_descr[segno].seg_attr & 0x01)) {                                    /* read only check */ \
                     z8010_segment_trap_request_##driver( pMMu, trap_cb, frdwr, usermod, *address, segno, Z8010_MMU_VTR_RDV); \
                     return 0; \
                  } \
                  if((frdwr > 0) && (pMMu->seg_descr[segno].seg_attr & 0x08)) {                                    /* execute only check */ \
                     z8010_segment_trap_request_##driver( pMMu, trap_cb, frdwr, usermod, *address, segno, Z8010_MMU_VTR_EXCV); \
                     return (frdwr > 1)? 0:1; \
                  } \
                  *address = (pMMu->seg_descr[segno].seg_ba <<8) + (0xffff & *address);                            /* translate address */ \
                  break; \
       case 0x00: \
       case 0x40: break; \
       } \
    \
       return 1; \
} 

#endif

