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

    p8000_16.c driver.

   Copyright Matt Knoth 2008,2009 p8000@knothusa.net

   - This source code is released as freeware for non-commercial purposes.
   - You are free to use and redistribute this code in modified or
     unmodified form, provided you list me in the credits.
   - If you modify this source code, you must add a notice to each modified
     source file that it has been changed.
   - The author of this copywritten work reserves the right to change the
     terms of its usage and license at any time, including retroactively
   - This entire notice must remain in the source code.

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

#define CTC0 0
#define CTC1 1
#define PIO0 0
#define PIO1 1
#define PIO2 2
#define SIO0 0
#define SIO1 1

#define MMU_CODE  0
#define MMU_DATA  1
#define MMU_STACK 2

// 1MB RAM space boundary - redefine more ram here
#define DRAM_BOUND 0xfffff

#include <time.h>
#include "driver.h"
#include "deprecat.h"
#include "ui.h"
#include "uimenu.h"
#include "sound/beep.h"
#include "cpu/z8001/z8001.h"
#include "cpu/z8001/z8010.h"
#include "cpu/z80/z80daisy.h"
#include "machine/z80ctc.h"
#include "machine/z80pio.h"
#include "machine/z80sio.h"
#include "debugger.h"

extern UINT8  p8000_16_present; // run 16bit board

UINT16* pDramLow0; // RAMBANK1 ram shadow ptr 1, ptr0 is EPROM
UINT16* pDramLow1; // RAMBANK2 ram shadow ptr 1, ptr0 is SRAM
UINT16* pDramHigh; // DRAM above 0x8000 ptr
UINT16* pFCW;      // CPU Flag Control Word mirror
UINT8   SCR;       // system configuration register
UINT8   SBR;       // system break register
UINT8   NBR;       // normal break register
UINT8   SNVR;      // segment violation register
UINT8   TRPL;      // trap low byte register
UINT8   IF1L;      // instruction fetch first word register
UINT8   DaisyNACK; // interrupt not acknowledged yet
UINT8   MMUID;     // MMU id after segtrap

static struct tm  clock_tm; // real time clock module

static emu_timer *pRTC; // fake the CTC1[2,3] real time clock counting
static UINT16 RTCstate;

z8010_regs* pCodeMMu;  // MMU1
z8010_regs* pDataMMu;  // MMU2
z8010_regs* pStackMMu; // MMU3

UINT32 handler_ingame_p8000_8(running_machine *machine, UINT32 state);
void put_adm31_vt100( UINT8 term, UINT8 code );    // send a character to the terminal
UINT8 osd_read_ntv_keyboard(void);

static TIMER_CALLBACK( rtc_callback );             // callback of 1/60Hz WEGA life clicks
static void p8000_16_interrupt(int state);         // request daisy chained vector interrupt
static int p8000_16_int_ack(int line);             // acknowledge pending interrupt
static void p8000_16_int_reti(void);               // return from daisy chained vector interrupt

void  p8000_16_abstr_put0A(UINT8 data);   // PIO0A 8bit abstraction - data output
void  p8000_16_abstr_put0B(UINT8 data);   // PIO0B 8bit abstraction - status i/o
void  p8000_16_abstr_init(void);          // initialize 8bit abstraction layer
void  p8000_16_abstr_get1A(UINT8 cnl);    // prepare for block read data (FD)

void  p8000_wdc_wr_ctl( UINT8 data );     // set control bits to WDC
void  p8000_wdc_wr_data( UINT8 data );    // write data to WDC
UINT8 p8000_wdc_rd_data( void );          // read data from WDC
void  p8000_wdc_init( void );             // initialize WDC
void  p8000_wdc_reset( void );            // reset WDC
void  p8000_wdc_disk_save( void );        // write WDC back to disk

extern VIDEO_START(  p8000_8 );
extern VIDEO_UPDATE( p8000_8 );
extern UINT8*  p8000_8_CharROM;

static const struct z80_irq_daisy_chain p8000_16_daisy_chain[8];

static ADDRESS_MAP_START( p8000_16_memmap, ADDRESS_SPACE_PROGRAM, 16 )
    AM_RANGE(0x000000, 0x003fff)   AM_RAMBANK(1) AM_BASE(&pDramLow0)
    AM_RANGE(0x004000, 0x007fff)   AM_RAMBANK(2) AM_BASE(&pDramLow1)
    AM_RANGE(0x008000, DRAM_BOUND) AM_RAM AM_BASE(&pDramHigh)
ADDRESS_MAP_END

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Z8010 Translation and Protection Functions - defined INLINE - therefore implemented in same module as used - coded in z8010.h
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Z8010_IMPLEMENT_TRAP(p8000_16);
Z8010_IMPLEMENT_TRANSLATE(p8000_16);

// mmu trap callback - hardware specific z8010 trap signal connections
void p8000_16_mmu_trap_cb(offs_t address, UINT8 segno, UINT8 mmuid)
{  cpuinfo info;

   z8001_get_info(CPUINFO_INT_PREVIOUSPC, &info);
//fprintf(stderr,"SEGTRAP: <%d>0x%04x mmu: %d PC: %08x\n",segno,address,mmuid,info.i);

   IF1L = info.i;
   TRPL = address;
   SNVR = segno;
   MMUID = mmuid;
   cpunum_set_input_line(Machine, 0, 2, ASSERT_LINE);
}

// Slow full step by step translation. Uses Z8001 default accessors.
// If that is too slow, consider overwriting the z8001_native_accessors table. The CPU knows what access to do, however, the
// CPU/driver interface forgets about that and the translate function must find out again.
int p8000_16_tranlate_slow(int space, offs_t *address)
{   int res;
    offs_t tmp;
    UINT8 segno;
    UINT16 block;

    tmp=*address;
    segno=(tmp >> 24) & 0x3f;
    block=tmp & 0xffff;
    *address = (segno<<16) | block; // fixup according to board wiring

    if(!(SCR & 0x02) || (!(SCR & 0x01) && !(tmp & 0x7fff8000))) {                                // MMUs off or EPROM/SRAM segment
         res=1;
    } else if(*pFCW & 0x4000) {                                                                  // system mode 
         res=z8010_translate_all_p8000_16( pCodeMMu, space, 0, address, segno, p8000_16_mmu_trap_cb);
    } else if(SCR&0x04)       {                                                                  // SEGUSR
         if(tmp & 0x40000000) {
            res=z8010_translate_all_p8000_16( pStackMMu, space, 0, address, segno, p8000_16_mmu_trap_cb);     // seg 64...127
       } else {
            res=z8010_translate_all_p8000_16( pDataMMu, space, 0, address, segno, p8000_16_mmu_trap_cb);      // seg  0...63
       }
    } else if(space==0)       {                                                                  // NON-SEGUSR code space
         res=z8010_translate_all_p8000_16( pCodeMMu, space, 0, address, segno, p8000_16_mmu_trap_cb);
    } else if(NBR <= (block>>8)) {                                                               // NON-SEGUSR stack space
         res=z8010_translate_all_p8000_16( pStackMMu,space, 0, address, segno, p8000_16_mmu_trap_cb);
    } else {                                                                                     // NON-SEGUSR data space
         res=z8010_translate_all_p8000_16( pDataMMu, space, 0, address, segno, p8000_16_mmu_trap_cb);
    }

    return !(*address & ~DRAM_BOUND) && res; // limit to DRAM size
}

READ16_HANDLER( z80sio_0xff81_r )   { return (offset & 4) ? (UINT16)z80sio_c_r(SIO0, (offset&2)>>1) : (UINT16)z80sio_d_r(SIO0, (offset&2)>>1); }
WRITE16_HANDLER( z80sio_0xff81_w )  { if (offset & 4) z80sio_c_w(SIO0, (offset&2)>>1, data); else z80sio_d_w(SIO0, (offset&2)>>1, data); }
READ16_HANDLER( z80sio_0xff89_r )   { return (offset & 4) ? (UINT16)z80sio_c_r(SIO1, (offset&2)>>1) : (UINT16)z80sio_d_r(SIO1, (offset&2)>>1); }
WRITE16_HANDLER( z80sio_0xff89_w )  { if (offset & 4) z80sio_c_w(SIO1, (offset&2)>>1, data); else z80sio_d_w(SIO1, (offset&2)>>1, data); }
READ16_HANDLER( z80pio_0xff91_r )   { UINT8 tmp=(offset & 4) ? z80pio_c_r(PIO0, (offset&2)>>1) : z80pio_d_r(PIO0, (offset&2)>>1); return (UINT16)tmp;  }
WRITE16_HANDLER( z80pio_0xff91_w )  { if (offset & 4) z80pio_c_w(PIO0, (offset&2)>>1, (UINT8)data); 
                                      else { z80pio_d_w(PIO0, (offset&2)>>1, (UINT8)data); 
                                             if(offset&2) p8000_16_abstr_put0B((UINT8)data); else p8000_16_abstr_put0A((UINT8)data); } }
READ16_HANDLER( z80pio_0xff99_r )   { UINT8 tmp; 
                                      if(offset & 4)   tmp=z80pio_c_r(PIO1, (offset&2)>>1);
                                      else           { p8000_16_abstr_get1A((offset&2)>>1);  // prepare if block data needed
                                                       tmp=z80pio_d_r(PIO1, (offset&2)>>1);
                                      }
                                      return (UINT16)tmp; }
WRITE16_HANDLER( z80pio_0xff99_w )  { if (offset & 4) z80pio_c_w(PIO1, (offset&2)>>1, (UINT8)data); else z80pio_d_w(PIO1, (offset&2)>>1, (UINT8)data); }
READ16_HANDLER( z80pio_0xffa1_r )   { UINT8 tmp;
                                      if(offset & 4) tmp=z80pio_c_r(PIO2, (offset&2)>>1);
                                      else {         tmp=z80pio_d_r(PIO2, (offset&2)>>1);
                                                     if(!(offset&2)) {
                                                          UINT8 tmp1=p8000_wdc_rd_data();     // pull next data from WDC
                                                                z80pio_p_w(PIO2, 0, tmp1);
                                      }}
                                      return (UINT16)tmp; }
WRITE16_HANDLER( z80pio_0xffa1_w )  { if (offset & 4) { z80pio_c_w(PIO2, (offset&2)>>1, (UINT8)data); }
                                      else {          z80pio_d_w(PIO2, (offset&2)>>1, (UINT8)data); 
                                                      if(offset&2) p8000_wdc_wr_ctl((UINT8)data);      // set WDC control bits
                                                      else         p8000_wdc_wr_data((UINT8)data);     // write data into WDC
                                      }}
READ16_HANDLER( z80ctc_0xffa9_r )   { UINT8 tmp=z80ctc_r(CTC0, (offset & 6)>>1); return (UINT16)tmp; }
WRITE16_HANDLER( z80ctc_0xffa9_w )  { z80ctc_w(CTC0, (offset & 6)>>1, data); }
READ16_HANDLER( z80ctc_0xffb1_r )   { UINT8 tmp=z80ctc_r(CTC1, (offset & 6)>>1); return (UINT16)tmp; }
WRITE16_HANDLER( z80ctc_0xffb1_w )  { z80ctc_w(CTC1, (offset & 6)>>1, data); 
                                      if((offset & 6)==6) {          // init channel  3
                                          if((data & 0x0ff)==0x03) { // Reset channel 3
                                              RTCstate=0;
                                              timer_adjust(pRTC, attotime_never, 0, attotime_never);
                                          } else {                   // program channel
                                              RTCstate=(RTCstate<<8) | (data & 0xff);
                                              if(RTCstate == 0xc750) {
                                                 timer_pulse(ATTOTIME_IN_MSEC(1000), NULL, 0x00, rtc_callback);
                                   ////          timer_set(ATTOTIME_IN_MSEC(1000), NULL, 0x00, rtc_callback);
                                                 RTCstate=1;
                                              }
                                          }
                                    } }
READ16_HANDLER(    scr_0xffc1_r )   { /*fprintf(stderr,"READ SCR %02x\n",SCR | 0xf0);*/ return (UINT16)(SCR | 0xf0); }
WRITE16_HANDLER(   scr_0xffc1_w )   { SCR=data | 0xf0; 
                                      memory_set_bank(STATIC_BANK1, data & 0x01); 
                                      memory_set_bank(STATIC_BANK2, data & 0x01); 
                                   // fprintf(stderr,"WROTE SCR: %02x - EPROM %s, MMU %s, SegUsr %s, Parity %s\n",
                                   //         (UINT8)data&0xf,(data&1)? "off":"on",(data&2)? "on":"off",(data&4)? "on":"off",(data&8)? "on":"off"); 
                                    }
READ16_HANDLER(    sbr_0xffc9_r )   { /*fprintf(stderr,"READ SBR %02x\n",SBR);*/ return (UINT16)SBR; }
WRITE16_HANDLER(   sbr_0xffc9_w )   { SBR=(UINT8)data;
                                      //fprintf(stderr,"WROTE SBR: %02x\n",data & 0xff);
                                    }
READ16_HANDLER(    nbr_0xffd1_r )   { /*fprintf(stderr,"READ NBR %02x\n",NBR);*/ return (UINT16)NBR; }
WRITE16_HANDLER(   nbr_0xffd1_w )   { NBR=(UINT8)data;
                                      // fprintf(stderr,"WROTE NBR: %02x\n",data & 0xff);
                                    }
READ16_HANDLER(   snvr_0xffd9_r )   { /*fprintf(stderr,"READ SNVR %02x\n",SNVR);*/ return (UINT16)SNVR; }

WRITE16_HANDLER( reti_flop_0xffe1_w ) { if((data & 0xff)==0x4d) p8000_16_int_reti(); }

READ16_HANDLER(   trpl_0xfff1_r )   { /*fprintf(stderr,"READ TRPL %02x\n",TRPL);*/ return (UINT16)TRPL; }
READ16_HANDLER(   if1l_0xfff9_r )   { /*fprintf(stderr,"READ IF1L %02x\n",IF1L);*/ return (UINT16)IF1L; }

WRITE8_HANDLER( p8000_16_sio_0xff81_transmit_cb ) { put_adm31_vt100( 0x04 | (offset & 0x01), (UINT8)data);  } // TTY 4,5
WRITE8_HANDLER( p8000_16_sio_0xff89_transmit_cb ) { put_adm31_vt100( 0x06 | (offset & 0x01), (UINT8)data);  } // TTY 6,7

static UINT8 p8000_16_clock_digit(UINT8 data, UINT8 digit)
{  UINT8 codes[]={ 0x00, 0x02, 0x05, 0x13, 0x1a, 0x19, 0x1d, 0x03, 0x1f, 0x1b};
   UINT8 res;
   if(data>=100) res=data%100;
   res=(digit==1)? (0x0f & (data % 10)):(0x0f & (data/10));
   if(res<10) res=codes[res];
   else       res=0;
   return res;
}

READ16_HANDLER( clock_0xfef1_r ) {  time_t t; UINT8 res=0;
                                    switch(offset) {
                                    case 0x01: t=time(NULL);
                                               memcpy(&clock_tm,localtime(&t),sizeof(struct tm));
                                               if(clock_tm.tm_sec==59) clock_tm.tm_sec=58;        // P8K has infinite loop until !=59
                                               clock_tm.tm_mon++;
                                               return(0xbf);                                      // module available
                                               break;
                                    case 0x05: if(clock_tm.tm_isdst) res=clock_tm.tm_year;        // year/sec first digit
                                               else                  res=clock_tm.tm_sec;
                                               res=p8000_16_clock_digit(res,1);
                                               break;
                                    case 0x07: if(clock_tm.tm_isdst) res=clock_tm.tm_year;        // year/sec second digit
                                               else                  res=clock_tm.tm_sec;
                                               res=p8000_16_clock_digit(res,2);
                                               break;
                                    case 0x09: if(clock_tm.tm_isdst) res=clock_tm.tm_mday;        // day/min first digit
                                               else                  res=clock_tm.tm_min;
                                               res=p8000_16_clock_digit(res,1);
                                               break;
                                    case 0x0b: if(clock_tm.tm_isdst) res=clock_tm.tm_mday;        // day/min second digit
                                               else                  res=clock_tm.tm_min;
                                               res=p8000_16_clock_digit(res,2);
                                               break;
                                    case 0x0d: if(clock_tm.tm_isdst) res=clock_tm.tm_mon;         // mon/hour first digit
                                               else                  res=clock_tm.tm_hour;
                                               res=p8000_16_clock_digit(res,1);
                                               break;
                                    case 0x0f: if(clock_tm.tm_isdst) res=clock_tm.tm_mon;         // mon/hour second digit
                                               else                  res=clock_tm.tm_hour;
                                               res=p8000_16_clock_digit(res,2);
                                               break;
                                    }
                                    return (UINT16)res;
                                 }
WRITE16_HANDLER( clock_0xfef1_w) { data &=0xff;
                                   switch(offset) {
                                   case 0x01: if(data==0x02) clock_tm.tm_isdst=1;  // use as date marker
                                              if(data==0x00) clock_tm.tm_isdst=0;  // use as time marker
                                   }
                                 } 
READ16_HANDLER( ior )   { /*fprintf(stderr," READ16:  0x%04x\n",offset);*/ return 0x0000; }
WRITE16_HANDLER( iow )  { /*fprintf(stderr," WRITE16: 0x%04x:=%04x mask: %04x\n",offset,data,mem_mask);*/ }
READ16_HANDLER( sior )  { if((~offset & 0x00f2)==0x0002) return(UINT16)z8010_r(MMU_CODE,  offset>>8);  
                          if((~offset & 0x00f4)==0x0004) return(UINT16)z8010_r(MMU_DATA,  offset>>8);  
                          if((~offset & 0x00f8)==0x0008) return(UINT16)z8010_r(MMU_STACK, offset>>8);  
                          return 0x0000;
                        }
WRITE16_HANDLER( siow ) { if((~offset & 0x00f2)==0x0002) z8010_w(MMU_CODE,  offset>>8,data & 0xff);  
                          if((~offset & 0x00f4)==0x0004) z8010_w(MMU_DATA,  offset>>8,data & 0xff);  
                          if((~offset & 0x00f8)==0x0008) z8010_w(MMU_STACK, offset>>8,data & 0xff);  
                        }

static ADDRESS_MAP_START( p8000_16_portmap, ADDRESS_SPACE_IO, 16 )
       AM_RANGE(0x00000, 0x0feef) AM_READWRITE(            ior, iow               )
       AM_RANGE(0x0fef0, 0x0feff) AM_READWRITE( clock_0xfef1_r, clock_0xfef1_w    )
       AM_RANGE(0x0ff00, 0x0ff7f) AM_READWRITE(            ior, iow               )
       AM_RANGE(0x0ff80, 0x0ff87) AM_READWRITE(z80sio_0xff81_r, z80sio_0xff81_w   )
       AM_RANGE(0x0ff88, 0x0ff8f) AM_READWRITE(z80sio_0xff89_r, z80sio_0xff89_w   )
       AM_RANGE(0x0ff90, 0x0ff97) AM_READWRITE(z80pio_0xff91_r, z80pio_0xff91_w   )
       AM_RANGE(0x0ff98, 0x0ff9f) AM_READWRITE(z80pio_0xff99_r, z80pio_0xff99_w   )
       AM_RANGE(0x0ffa0, 0x0ffa7) AM_READWRITE(z80pio_0xffa1_r, z80pio_0xffa1_w   )
       AM_RANGE(0x0ffa8, 0x0ffaf) AM_READWRITE(z80ctc_0xffa9_r, z80ctc_0xffa9_w   )
       AM_RANGE(0x0ffb0, 0x0ffb7) AM_READWRITE(z80ctc_0xffb1_r, z80ctc_0xffb1_w   )
       AM_RANGE(0x0ffc0, 0x0ffc1) AM_READWRITE(   scr_0xffc1_r, scr_0xffc1_w      )
       AM_RANGE(0x0ffc8, 0x0ffc9) AM_READWRITE(   sbr_0xffc9_r, sbr_0xffc9_w      )
       AM_RANGE(0x0ffd0, 0x0ffd1) AM_READWRITE(   nbr_0xffd1_r, nbr_0xffd1_w      )
       AM_RANGE(0x0ffd8, 0x0ffd9) AM_READ     (  snvr_0xffd9_r                    )
       AM_RANGE(0x0ffe0, 0x0ffe1) AM_WRITE    (                 reti_flop_0xffe1_w)
       AM_RANGE(0x0fff0, 0x0fff1) AM_READ     (  trpl_0xfff1_r                    )
       AM_RANGE(0x0fff8, 0x0fff9) AM_READ     (  if1l_0xfff9_r                    )
       AM_RANGE(0x10000, 0x1ffff) AM_READWRITE(           sior, siow              )  // special IO located above regular 64K IO space
ADDRESS_MAP_END

/*************************************
 *
 *  Machine "start"
 *
 *************************************/

static z80ctc_interface p8000_16_0xffa9_ctc_intf = {
        4000000,
        0,                 /* timer disables */
        p8000_16_interrupt,/* interrupt handler */
        0,                 /* ZC/TO0 callback */
        0,                 /* ZC/TO1 callback */
        0                  /* ZC/TO2 callback */
};

static z80ctc_interface p8000_16_0xffb1_ctc_intf = {
        4000000,
        0,                 /* timer disables */
        p8000_16_interrupt,/* interrupt handler */
        0,                 /* ZC/TO0 callback */
        0,                 /* ZC/TO1 callback */
        0                  /* ZC/TO2 callback */
};

static z80sio_interface p8000_16_0xff81_sio_intf =
{
        9600,                           /* 9.6kBaud default */
        p8000_16_interrupt,             /* interrupt handler */
        0,                              /* DTR changed handler */
        0,                              /* RTS changed handler */
        0,                              /* BREAK changed handler */
        p8000_16_sio_0xff81_transmit_cb /* transmit handler */
};

static z80sio_interface p8000_16_0xff89_sio_intf =
{
        9600,                           /* 9.6kBaud default */
        p8000_16_interrupt,             /* interrupt handler */
        0,                              /* DTR changed handler */
        0,                              /* RTS changed handler */
        0,                              /* BREAK changed handler */
        p8000_16_sio_0xff89_transmit_cb /* transmit handler */
};

static const z80pio_interface p8000_16_0xff91_pio_intf = {
        p8000_16_interrupt,    /* interrupt handler */
        0,                     /* portA ready active callback */
        0                      /* portB ready active callback */
};

static const z80pio_interface p8000_16_0xff99_pio_intf = {
        p8000_16_interrupt,    /* interrupt handler */
        0,                     /* portA ready active callback */
        0                      /* portB ready active callback */
};

static const z80pio_interface p8000_16_0xffa1_pio_intf = {
        p8000_16_interrupt,    /* interrupt handler */
        0,                     /* portA ready active callback */
        0,                     /* portB ready active callback */
};

static const z8001_interface p8000_16_cpu_inf = {
        p8000_16_tranlate_slow /* translate function */
};

static void p8000_16_save_exit_cb(UINT8 param)
{
    while(osd_read_ntv_keyboard());             // remove menu keys from buffer

    // F3 EXIT+SAVE dialog callback
    switch(param) {
    case 3: p8000_wdc_disk_save();        // Save YES and EXIT
    case 4: mame_schedule_exit(Machine);  // Save NO  and EXIT
            break;
    }
    // chicken'd out
}

/*****************************************************************************************************************************************
   Keyboard handling - replace handler_ingame through local handler for keyboard use - this is a dummy here to kill MAME keys
 ****************************************************************************************************************************************/
UINT32 handler_ingame_p8000_16(running_machine *machine, UINT32 state)
{
        return 0;
}

MACHINE_START( p8000_16 ) {  // cold start

      cpuinfo info;
      UINT16* pSRAM;

      // replace original ingame handler with own to use full keyboard */
      ui_set_handler_ingame( handler_ingame_p8000_16, 0 );
      uimenu_set_on_exit_callback(p8000_16_save_exit_cb);

      pSRAM = (UINT16*)auto_malloc(0x4000);

      memory_configure_bank(STATIC_BANK1, 0, 1, (void*)memory_region(REGION_CPU1), 0);
      memory_configure_bank(STATIC_BANK1, 1, 1, (void*)pDramLow0, 0);
      memory_set_bank(STATIC_BANK1, 0);

      memory_configure_bank(STATIC_BANK2, 0, 1, (void*)pSRAM, 0);
      memory_configure_bank(STATIC_BANK2, 1, 1, (void*)pDramLow1, 0);
      memory_set_bank(STATIC_BANK2, 0);

      z8001_get_info(CPUINFO_PTR_CPU_SPECIFIC + Z8001_FCW_CHANGE_PTR, &info);
      pFCW = info.p;

      cpunum_set_irq_callback(0, p8000_16_int_ack);

      z8010_get_info(0, CPUINFO_PTR_CPU_SPECIFIC + Z8010_RGS,  &info);
      pCodeMMu = info.p;

      z8010_get_info(1, CPUINFO_PTR_CPU_SPECIFIC + Z8010_RGS,  &info);
      pDataMMu = info.p;

      z8010_get_info(2, CPUINFO_PTR_CPU_SPECIFIC + Z8010_RGS,  &info);
      pStackMMu = info.p;

      p8000_8_CharROM = (UINT8 *)memory_region(REGION_PROMS);

      z8010_init(0, NULL);  // CODE  MMU
      z8010_init(1, NULL);  // DATA  MMU
      z8010_init(2, NULL);  // STACK MMU

      p8000_wdc_init();
      p8000_wdc_reset();

      pRTC = timer_alloc(rtc_callback, NULL);

      init_floppy_menu(0);
}

MACHINE_RESET( p8000_16 )
{  cpuinfo info;

   SCR = 0;     // system configuration register
   SBR = 0;     // system break register
   NBR = 0;     // normal break register
   SNVR = 0;    // segment violation register
   TRPL = 0;    // trap low byte register
   IF1L = 0;    // instruction fetch first word register
   DaisyNACK=0; // no unacknowledged interrupt
   RTCstate=0;  // disable UNIX clicks

   z8010_init(0, NULL);  // CODE  MMU
   z8010_init(1, NULL);  // DATA  MMU
   z8010_init(2, NULL);  // STACK MMU

   z80sio_init(SIO0, &p8000_16_0xff81_sio_intf);
   z80sio_set_dcd(SIO0, 0, 1);
   z80sio_set_cts(SIO0, 0, 1);
   z80sio_set_dcd(SIO0, 1, 1);
   z80sio_set_cts(SIO0, 1, 1);
   z80sio_init(SIO1, &p8000_16_0xff89_sio_intf);
   z80sio_set_dcd(SIO1, 0, 1);
   z80sio_set_cts(SIO1, 0, 1);
   z80sio_set_dcd(SIO1, 1, 1);
   z80sio_set_cts(SIO1, 1, 1);

   z80pio_init(PIO0, &p8000_16_0xff91_pio_intf);
   z80pio_reset(PIO0);
   z80pio_init(PIO1, &p8000_16_0xff99_pio_intf);
   z80pio_reset(PIO1);
   z80pio_init(PIO2, &p8000_16_0xffa1_pio_intf);
   z80pio_reset(PIO2);

   z80ctc_init(CTC0, &p8000_16_0xffa9_ctc_intf);
   z80ctc_init(CTC1, &p8000_16_0xffb1_ctc_intf);
   z80ctc_reset(CTC0);
   z80ctc_reset(CTC1);

   memory_set_bank(STATIC_BANK1, 0);
   memory_set_bank(STATIC_BANK2, 0);

   z8001_get_info(CPUINFO_PTR_RESET, &info);
   (*info.reset)();

   p8000_16_abstr_init();
}

void p8000_16_reset_button( void )                // Reset button pressed
{
     machine_reset_p8000_16(Machine);
}

/*************************************
 *
 *  Machine drivers
 *
 *************************************/

static MACHINE_DRIVER_START( p8000_16 )

      p8000_16_present=1; // run 16bit board

      MDRV_CPU_ADD(Z8001, 4000000 )
      MDRV_CPU_PROGRAM_MAP(p8000_16_memmap,0)
      MDRV_CPU_IO_MAP(p8000_16_portmap,0)
      MDRV_CPU_CONFIG(p8000_16_cpu_inf)

      MDRV_MACHINE_START( p8000_16 )
      MDRV_MACHINE_RESET( p8000_16 )

      MDRV_SOUND_ADD(BEEP, 1000);

      /* video hardware */
      MDRV_VIDEO_ATTRIBUTES(VIDEO_TYPE_RASTER)
      MDRV_SCREEN_ADD("main", 0)
      MDRV_SCREEN_FORMAT(BITMAP_FORMAT_RGB15)
      MDRV_SCREEN_SIZE(640,480)
      MDRV_SCREEN_VISIBLE_AREA(0,639, 0,479)
      MDRV_SCREEN_REFRESH_RATE(15)

      MDRV_VIDEO_START( p8000_8 )
      MDRV_VIDEO_UPDATE( p8000_8 )

MACHINE_DRIVER_END

/*************************************
 *
 *  Input ports
 *
 *************************************/

static INPUT_PORTS_START( p8000_16 )
INPUT_PORTS_END

/*************************************
 *
 *  External Exceptions and Daisy Chain
 *
 *************************************/

void p8000_16_nmi_button( void )                  // NMI button pressed
{
     cpunum_set_input_line_and_vector(Machine, 0, INPUT_LINE_NMI, ASSERT_LINE, 1); // manual NMI vector
}

// lets fake the 1 second CTC1[2,3] tick interrupt
// because too much performance burden counting 4MHz down in a CTC
static TIMER_CALLBACK( rtc_callback )
{
    if(RTCstate) {
       z80ctc_create_interrupt(CTC1,3);
    } 
}

static void p8000_16_spool_interrupts(void)
{
    // find higher priority interrupt, send if found and set un-acknowledged
    if(z80daisy_inquire_irq_state(p8000_16_daisy_chain)==Z80_DAISY_INT) {
       DaisyNACK=1;
//fprintf(stderr,"SPOOL Int\n");
       cpunum_set_input_line(Machine, 0, 1, 1);
    }
}

static void p8000_16_interrupt(int state)
{
    if(!state) return;   // the Z8001 does auto reset VI line upon intack

//z80daisy_debug_irq_state(p8000_16_daisy_chain);

    if(!DaisyNACK)                    // if no VI is un-acknowledged
       p8000_16_spool_interrupts();  
}

static int p8000_16_int_ack(int line)
{   int res;

    switch(line) {
    case 1:  DaisyNACK=0;                                            //  VI
             res=z80daisy_call_ack_device(p8000_16_daisy_chain);
//z80daisy_debug_irq_state(p8000_16_daisy_chain);
             p8000_16_spool_interrupts();
             break;
    case 2:  res=MMUID;                                              // SEGTRAP
             break;
    default: res=0x00;
    }

//fprintf(stderr,"INTACK %d res=%02x\n",line,res);

    return res;
}

static void p8000_16_int_reti(void)
{
//fprintf(stderr,"RETI called\n"); 

    z80daisy_call_reti_device(p8000_16_daisy_chain); 
//z80daisy_debug_irq_state(p8000_16_daisy_chain);
    p8000_16_spool_interrupts();
}

static const struct z80_irq_daisy_chain p8000_16_daisy_chain[] = {

        { z80ctc_reset, z80ctc_irq_state, z80ctc_irq_ack, z80ctc_irq_reti, 0 }, /* CTC0 addr 0xffa9 */
        { z80ctc_reset, z80ctc_irq_state, z80ctc_irq_ack, z80ctc_irq_reti, 1 }, /* CTC1 addr 0xffb1 */
        { z80sio_reset, z80sio_irq_state, z80sio_irq_ack, z80sio_irq_reti, 0 }, /* SIO0 addr 0xff81 */
        { z80sio_reset, z80sio_irq_state, z80sio_irq_ack, z80sio_irq_reti, 1 }, /* SIO1 addr 0xff89 */
        { z80pio_reset, z80pio_irq_state, z80pio_irq_ack, z80pio_irq_reti, 0 }, /* PIO0 addr 0xff91 */
        { z80pio_reset, z80pio_irq_state, z80pio_irq_ack, z80pio_irq_reti, 1 }, /* PIO1 addr 0xff99 */
        { z80pio_reset, z80pio_irq_state, z80pio_irq_ack, z80pio_irq_reti, 2 }, /* PIO2 addr 0xffa1 */
        { 0, 0, 0, 0, -1 }              /* end mark */
};

ROM_START( p8000_16 )
      ROM_REGION16_BE( 0x4000, REGION_CPU1, 0 )
      ROM_LOAD16_BYTE( "mon16_1h_3.1_udos",   0x0000, 0x1000, CRC(0c3c28da))
      ROM_LOAD16_BYTE( "mon16_1l_3.1_udos",   0x0001, 0x1000, CRC(e8857bdc))
      ROM_LOAD16_BYTE( "mon16_2h_3.1_udos",   0x2000, 0x1000, CRC(cddf58d5))
      ROM_LOAD16_BYTE( "mon16_2l_3.1_udos",   0x2001, 0x1000, CRC(395ee7aa))

      ROM_REGION( 0x1000, REGION_PROMS, 0 )
      ROM_LOAD( "p8t_zs",         0x0000, 0x0800, CRC(f9321251))
      ROM_LOAD( "p8tdzs.2",       0x0800, 0x0800, CRC(32736503))
ROM_END

GAME( 1989, p8000_16, 0, p8000_16, p8000_16, 0, ROT0, "EAW electronic Treptow", "P8000 16bit Board Emulation", 0 )

