text: /*****************************************************************************
   *
   *   tms7000.c
   *   Portable TMS7000 emulator (Texas Instruments 7000)
   *
   *   Copyright tim lindner, all rights reserved.
   *
   *   - 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.  If you're a nice person, you
   *     will clearly mark each change too.  :)
   *   - If you wish to use this for commercial purposes, please contact me at
   *     tlindner@macmess.org
   *   - This entire notice must remain in the source code.
   *
   *****************************************************************************
   *  Currently this source emulates a TMS70x0, not any of the other variants
   *  Unimplemented is the MC pin which (in conjunection with IOCNT0 bits 7 and 6
   *  control the memory mapping.
   *
   *  This source implements the MC pin at Vss and mode bits in single chip mode.
   *****************************************************************************/
  
  // SJE: Changed all references to ICount to icount (to match MAME requirements)
  // SJE: Changed RM/WM macros to reference newly created tms7000 read/write handlers & removed unused SRM(cpustate) macro
  // SJE: Fixed a mistake in tms70x0_pf_w where the wrong register was referenced
  // SJE: Implemented internal register file
  
  #include "cpuintrf.h"
  #include "cpuexec.h"
  #include "debugger.h"
  #include "tms7000.h"
  
  #define VERBOSE 0
  
  #define LOG(x)	do { if (VERBOSE) logerror x; } while (0)
  
  typedef struct _tms7000_state tms7000_state;
  
  /* Private prototypes */
  
  static void tms7000_set_irq_line(tms7000_state *cpustate, int irqline, int state);
  static void tms7000_check_IRQ_lines(tms7000_state *cpustate);
  static void tms7000_do_interrupt( tms7000_state *cpustate, UINT16 address, UINT8 line );
  static CPU_EXECUTE( tms7000 );
  static CPU_EXECUTE( tms7000_exl );
  static void tms7000_service_timer1( const device_config *device );
  static UINT16 bcd_add( UINT16 a, UINT16 b );
  static UINT16 bcd_tencomp( UINT16 a );
  static UINT16 bcd_sub( UINT16 a, UINT16 b);
  
  /* Static variables */
  
  #define RM(Addr) ((unsigned)memory_read_byte_8be(cpustate->program, Addr))
  #define WM(Addr,Value) (memory_write_byte_8be(cpustate->program, Addr, Value))
  
  #define IMMBYTE(b)	b = ((unsigned)memory_raw_read_byte(cpustate->program, pPC)); pPC++
  #define SIMMBYTE(b)	b = ((signed)memory_raw_read_byte(cpustate->program, pPC)); pPC++
  #define IMMWORD(w)	w.b.h = (unsigned)memory_raw_read_byte(cpustate->program, pPC++); w.b.l = (unsigned)memory_raw_read_byte(cpustate->program, pPC++)
  
  #define PUSHBYTE(b) pSP++; WM(pSP,b)
  #define PUSHWORD(w) pSP++; WM(pSP,w.b.h); pSP++; WM(pSP,w.b.l)
  #define PULLBYTE(b) b = RM(pSP); pSP--
  #define PULLWORD(w) w.b.l = RM(pSP); pSP--; w.b.h = RM(pSP); pSP--
  
  struct _tms7000_state
  {
  	PAIR		pc; 		/* Program counter */
  	UINT8		sp;		/* Stack Pointer */
  	UINT8		sr;		/* Status Register */
  	UINT8		irq_state[3];	/* State of the three IRQs */
  	UINT8		rf[0x80];	/* Register file (SJE) */
  	UINT8		pf[0x100];	/* Perpherial file */
  	cpu_irq_callback irq_callback;
  	const device_config *device;
  	const address_space *program;
  	const address_space *io;
  	int			icount;
  	int 		div_by_16_trigger;
  	int			cycles_per_INT2;
  	UINT8		t1_capture_latch; /* Timer 1 capture latch */
  	INT8		t1_prescaler;	/* Timer 1 prescaler (5 bits) */
  	INT16		t1_decrementer;	/* Timer 1 decrementer (8 bits) */
  	UINT8		idle_state;	/* Set after the execution of an idle instruction */
  };
  
  INLINE tms7000_state *get_safe_token(const device_config *device)
  {
  	assert(device != NULL);
  	assert(device->token != NULL);
  	assert(device->type == CPU);
  	assert(cpu_get_type(device) == CPU_TMS7000 ||
  		   cpu_get_type(device) == CPU_TMS7000_EXL);
  	return (tms7000_state *)device->token;
  }
  
  #define pPC		cpustate->pc.w.l
  #define PC		cpustate->pc
  #define pSP		cpustate->sp
  #define pSR		cpustate->sr
  
  #define RDA		RM(0x0000)
  #define RDB		RM(0x0001)
  
  #define WRA(Value) (WM(0x0000,Value))
  #define WRB(Value) (WM(0x0001,Value))
  
  #define SR_C	0x80		/* Carry */
  #define SR_N	0x40		/* Negative */
  #define SR_Z	0x20		/* Zero */
  #define SR_I	0x10		/* Interrupt */
  
  #define CLR_NZC 	pSR&=~(SR_N|SR_Z|SR_C)
  #define CLR_NZCI 	pSR&=~(SR_N|SR_Z|SR_C|SR_I)
  #define SET_C8(a)	pSR|=((a&0x0100)>>1)
  #define SET_N8(a)	pSR|=((a&0x0080)>>1)
  #define SET_Z(a)	if(!a)pSR|=SR_Z
  #define SET_Z8(a)	SET_Z((UINT8)a)
  #define SET_Z16(a)	SET_Z((UINT8)a>>8)
  #define GET_C		(pSR >> 7)
  
  /* Not working */
  #define SET_C16(a)	pSR|=((a&0x010000)>>9)
  
  #define SETC		pSR |= SR_C
  #define SETZ		pSR |= SR_Z
  #define SETN		pSR |= SR_N
  
  static READ8_HANDLER( tms7000_internal_r );
  static WRITE8_HANDLER( tms7000_internal_w );
  static READ8_HANDLER( tms70x0_pf_r );
  static WRITE8_HANDLER( tms70x0_pf_w );
  
  static ADDRESS_MAP_START(tms7000_mem, ADDRESS_SPACE_PROGRAM, 8)
  	AM_RANGE(0x0000, 0x007f)	AM_READWRITE(tms7000_internal_r, tms7000_internal_w)	/* tms7000 internal RAM */
  	AM_RANGE(0x0080, 0x00ff)	AM_READWRITE(SMH_NOP, SMH_NOP)						/* reserved */
  	AM_RANGE(0x0100, 0x01ff)	AM_READWRITE(tms70x0_pf_r, tms70x0_pf_w)				/* tms7000 internal I/O ports */
  ADDRESS_MAP_END
  
  
  INLINE UINT16 RM16( tms7000_state *cpustate, UINT32 mAddr )	/* Read memory (16-bit) */
  {
  	UINT32 result = RM(mAddr) << 8;
  	return result | RM((mAddr+1)&0xffff);
  }
  
  INLINE UINT16 RRF16( tms7000_state *cpustate, UINT32 mAddr )	/*Read register file (16 bit) */
  {
  	PAIR result;
  	result.b.h = RM((mAddr-1)&0xffff);
  	result.b.l = RM(mAddr);
  	return result.w.l;
  }
  
  INLINE void WRF16( tms7000_state *cpustate, UINT32 mAddr, PAIR p )	/*Write register file (16 bit) */
  {
  	WM( (mAddr-1)&0xffff, p.b.h );
  	WM( mAddr, p.b.l );
  }
  
  
  static CPU_INIT( tms7000 )
  {
  	tms7000_state *cpustate = get_safe_token(device);
  
  	cpustate->irq_callback = irqcallback;
  	cpustate->device = device;
  	cpustate->program = memory_find_address_space(device, ADDRESS_SPACE_PROGRAM);
  	cpustate->io = memory_find_address_space(device, ADDRESS_SPACE_IO);
  
  	memset(cpustate->pf, 0, 0x100);
  	memset(cpustate->rf, 0, 0x80);
  
  	/* Save register state */
  	state_save_register_device_item(device, 0, pPC);
  	state_save_register_device_item(device, 0, pSP);
  	state_save_register_device_item(device, 0, pSR);
  
  	/* Save Interrupt state */
  	state_save_register_device_item_array(device, 0, cpustate->irq_state);
  
  	/* Save register and perpherial file state */
  	state_save_register_device_item_array(device, 0, cpustate->rf);
  	state_save_register_device_item_array(device, 0, cpustate->pf);
  
  	/* Save timer state */
  	state_save_register_device_item(device, 0, cpustate->t1_prescaler);
  	state_save_register_device_item(device, 0, cpustate->t1_capture_latch);
  	state_save_register_device_item(device, 0, cpustate->t1_decrementer);
  
  	state_save_register_device_item(device, 0, cpustate->idle_state);
  }
  
  static CPU_RESET( tms7000 )
  {
  	tms7000_state *cpustate = get_safe_token(device);
  
  //  cpustate->architecture = (int)param;
  
  	cpustate->idle_state = 0;
  	cpustate->irq_state[ TMS7000_IRQ1_LINE ] = CLEAR_LINE;
  	cpustate->irq_state[ TMS7000_IRQ2_LINE ] = CLEAR_LINE;
  	cpustate->irq_state[ TMS7000_IRQ3_LINE ] = CLEAR_LINE;
  
  	WM( 0x100 + 9, 0 );		/* Data direction regs are cleared */
  	WM( 0x100 + 11, 0 );
  
  //  if( cpustate->architecture == TMS7000_NMOS )
  //  {
  		WM( 0x100 + 4, 0xff );		/* Output 0xff on port A */
  		WM( 0x100 + 8, 0xff );		/* Output 0xff on port C */
  		WM( 0x100 + 10, 0xff );		/* Output 0xff on port D */
  //  }
  //  else
  //  {
  //      WM( 0x100 + 4, 0xff );      /* Output 0xff on port A */
  //  }
  
  	pSP = 0x01;				/* Set stack pointer to r1 */
  	pSR = 0x00;				/* Clear status register (disabling interrupts */
  	WM( 0x100 + 0, 0 );		/* Write a zero to IOCNT0 */
  
  	/* On TMS70x2 and TMS70Cx2 IOCNT1 is zero */
  
  	WRA( cpustate->pc.b.h );	/* Write previous PC to A:B */
  	WRB( cpustate->pc.b.l );
  	pPC = RM16(cpustate, 0xfffe);		/* Load reset vector */
  
  	cpustate->div_by_16_trigger = -16;
  }
  
  
  
  /**************************************************************************
   * Generic set_info
   **************************************************************************/
  
  static CPU_SET_INFO( tms7000 )
  {
  	tms7000_state *cpustate = get_safe_token(device);
  
      switch (state)
      {
          /* --- the following bits of info are set as 64-bit signed integers --- */
          case CPUINFO_INT_INPUT_STATE + TMS7000_IRQ1_LINE:	tms7000_set_irq_line(cpustate, TMS7000_IRQ1_LINE, info->i);	break;
          case CPUINFO_INT_INPUT_STATE + TMS7000_IRQ2_LINE:	tms7000_set_irq_line(cpustate, TMS7000_IRQ2_LINE, info->i);	break;
          case CPUINFO_INT_INPUT_STATE + TMS7000_IRQ3_LINE:	tms7000_set_irq_line(cpustate, TMS7000_IRQ3_LINE, info->i);	break;
  
          case CPUINFO_INT_PC:
          case CPUINFO_INT_REGISTER + TMS7000_PC:	pPC = info->i; break;
          case CPUINFO_INT_SP:
          case CPUINFO_INT_REGISTER + TMS7000_SP:	pSP = info->i;	break;
          case CPUINFO_INT_REGISTER + TMS7000_ST:	pSR = info->i;	tms7000_check_IRQ_lines(cpustate);	break;
  		case CPUINFO_INT_REGISTER + TMS7000_IDLE: cpustate->idle_state = info->i;	break;
  		case CPUINFO_INT_REGISTER + TMS7000_T1_CL: cpustate->t1_capture_latch = info->i;	break;
  		case CPUINFO_INT_REGISTER + TMS7000_T1_PS: cpustate->t1_prescaler = info->i;	break;
  		case CPUINFO_INT_REGISTER + TMS7000_T1_DEC: cpustate->t1_decrementer = info->i;	break;
      }
  }
  
  /**************************************************************************
   * Generic get_info
   **************************************************************************/
  
  CPU_GET_INFO( tms7000 )
  {
  	tms7000_state *cpustate = (device != NULL && device->token != NULL) ? get_safe_token(device) : NULL;
  
      switch( state )
      {
          /* --- the following bits of info are returned as 64-bit signed integers --- */
          case CPUINFO_INT_CONTEXT_SIZE:	info->i = sizeof(tms7000_state);	break;
          case CPUINFO_INT_INPUT_LINES:	info->i = 3;	break;
          case CPUINFO_INT_DEFAULT_IRQ_VECTOR:	info->i = 0;	break;
          case DEVINFO_INT_ENDIANNESS:	info->i = ENDIANNESS_BIG;	break;
          case CPUINFO_INT_CLOCK_MULTIPLIER:	info->i = 1;	break;
          case CPUINFO_INT_CLOCK_DIVIDER:	info->i = 1;	break;
          case CPUINFO_INT_MIN_INSTRUCTION_BYTES:	info->i = 1;	break;
          case CPUINFO_INT_MAX_INSTRUCTION_BYTES:	info->i = 4;	break;
          case CPUINFO_INT_MIN_CYCLES:	info->i = 1;	break;
          case CPUINFO_INT_MAX_CYCLES:	info->i = 48;	break; /* 48 represents the multiply instruction, the next highest is 17 */
  
          case CPUINFO_INT_DATABUS_WIDTH_PROGRAM:	info->i = 8;	break;
          case CPUINFO_INT_ADDRBUS_WIDTH_PROGRAM:	info->i = 16;	break;
          case CPUINFO_INT_ADDRBUS_SHIFT_PROGRAM:	info->i = 0;	break;
          case CPUINFO_INT_DATABUS_WIDTH_DATA:	info->i = 0;	break;
          case CPUINFO_INT_ADDRBUS_WIDTH_DATA:	info->i = 0;	break;
          case CPUINFO_INT_ADDRBUS_SHIFT_DATA:	info->i = 0;	break;
          case CPUINFO_INT_DATABUS_WIDTH_IO:	info->i = 8;	break;
          case CPUINFO_INT_ADDRBUS_WIDTH_IO:	info->i = 8;	break;
          case CPUINFO_INT_ADDRBUS_SHIFT_IO:	info->i = 0;	break;
  
          case CPUINFO_INT_INPUT_STATE + TMS7000_IRQ1_LINE:	info->i = cpustate->irq_state[TMS7000_IRQ1_LINE]; break;
          case CPUINFO_INT_INPUT_STATE + TMS7000_IRQ2_LINE:	info->i = cpustate->irq_state[TMS7000_IRQ2_LINE]; break;
          case CPUINFO_INT_INPUT_STATE + TMS7000_IRQ3_LINE:	info->i = cpustate->irq_state[TMS7000_IRQ3_LINE]; break;
  
          case CPUINFO_INT_PREVIOUSPC:	info->i = 0; /* Not supported */	break;
  
          case CPUINFO_INT_PC:
          case CPUINFO_INT_REGISTER + TMS7000_PC:	info->i = pPC;	break;
          case CPUINFO_INT_SP:
          case CPUINFO_INT_REGISTER + TMS7000_SP:	info->i = pSP;	break;
          case CPUINFO_INT_REGISTER + TMS7000_ST:	info->i = pSR;	break;
  		case CPUINFO_INT_REGISTER + TMS7000_IDLE: info->i = cpustate->idle_state; break;
  		case CPUINFO_INT_REGISTER + TMS7000_T1_CL: info->i = cpustate->t1_capture_latch; break;
  		case CPUINFO_INT_REGISTER + TMS7000_T1_PS: info->i = cpustate->t1_prescaler; break;
  		case CPUINFO_INT_REGISTER + TMS7000_T1_DEC: info->i = cpustate->t1_decrementer; break;
  
          /* --- the following bits of info are returned as pointers to data or functions --- */
          case CPUINFO_FCT_SET_INFO:	info->setinfo = CPU_SET_INFO_NAME(tms7000);	break;
          case CPUINFO_FCT_INIT:	info->init = CPU_INIT_NAME(tms7000);	break;
          case CPUINFO_FCT_RESET:	info->reset = CPU_RESET_NAME(tms7000);	break;
          case CPUINFO_FCT_EXECUTE:	info->execute = CPU_EXECUTE_NAME(tms7000);	break;
          case CPUINFO_FCT_BURN:	info->burn = NULL;	/* Not supported */break;
          case CPUINFO_FCT_DISASSEMBLE:	info->disassemble = CPU_DISASSEMBLE_NAME(tms7000);	break;
          case CPUINFO_PTR_INSTRUCTION_COUNTER:	info->icount = &cpustate->icount;	break;
  		case CPUINFO_PTR_INTERNAL_MEMORY_MAP_PROGRAM:	info->internal_map8 = ADDRESS_MAP_NAME(tms7000_mem); break;
  
          /* --- the following bits of info are returned as NULL-terminated strings --- */
          case DEVINFO_STR_NAME:	strcpy(info->s, "TMS7000"); break;
          case DEVINFO_STR_FAMILY:	strcpy(info->s, "Texas Instriuments TMS7000"); break;
          case DEVINFO_STR_VERSION:	strcpy(info->s, "1.0"); break;
          case DEVINFO_STR_SOURCE_FILE:	strcpy(info->s, __FILE__); break;
          case DEVINFO_STR_CREDITS:	strcpy(info->s, "Copyright tim lindner"); break;
  
          case CPUINFO_STR_FLAGS:
                  sprintf(info->s,  "%c%c%c%c%c%c%c%c",
                          cpustate->sr & 0x80 ? 'C':'c',
                          cpustate->sr & 0x40 ? 'N':'n',
                          cpustate->sr & 0x20 ? 'Z':'z',
                          cpustate->sr & 0x10 ? 'I':'i',
                          cpustate->sr & 0x08 ? '?':'.',
                          cpustate->sr & 0x04 ? '?':'.',
                          cpustate->sr & 0x02 ? '?':'.',
                          cpustate->sr & 0x01 ? '?':'.' );
                  break;
  
          case CPUINFO_STR_REGISTER + TMS7000_PC:	sprintf(info->s, "PC:%04X", cpustate->pc.w.l); break;
          case CPUINFO_STR_REGISTER + TMS7000_SP:	sprintf(info->s, "S:%02X", cpustate->sp); break;
          case CPUINFO_STR_REGISTER + TMS7000_ST:	sprintf(info->s, "ST:%02X", cpustate->sr); break;
  		case CPUINFO_STR_REGISTER + TMS7000_IDLE: sprintf(info->s, "Idle:%02X", cpustate->idle_state); break;
  		case CPUINFO_STR_REGISTER + TMS7000_T1_CL: sprintf(info->s, "T1CL:%02X", cpustate->t1_capture_latch); break;
  		case CPUINFO_STR_REGISTER + TMS7000_T1_PS: sprintf(info->s, "T1PS:%02X", cpustate->t1_prescaler & 0x1f); break;
  		case CPUINFO_STR_REGISTER + TMS7000_T1_DEC: sprintf(info->s, "T1DEC:%02X", cpustate->t1_decrementer & 0xff); break;
  
      }
  }
  
  CPU_GET_INFO( tms7000_exl )
  {
      switch( state )
      {
  		case CPUINFO_FCT_EXECUTE:
  			info->execute = CPU_EXECUTE_NAME(tms7000_exl);
  			break;
  		default:
  			CPU_GET_INFO_CALL(tms7000);
  			break;
  	}
  }
  
  void tms7000_set_irq_line(tms7000_state *cpustate, int irqline, int state)
  {
  	if (cpustate->irq_state[irqline] != state)
  	{	/* check for transition */
  		cpustate->irq_state[irqline] = state;
  
  		LOG(("tms7000: (cpu '%s') set_irq_line (INT%d, state %d)\n", cpustate->device->tag, irqline+1, state));
  
  		if (state == CLEAR_LINE)
  		{
  			return;
  		}
  
  		cpustate->pf[0] |= (0x02 << (irqline * 2));	/* Set INTx iocntl0 flag */
  
  		if( irqline == TMS7000_IRQ3_LINE )
  		{
  			/* Latch the value in perpherial file register 3 */
  			cpustate->t1_capture_latch = cpustate->t1_decrementer & 0x00ff;
  		}
  
  		tms7000_check_IRQ_lines(cpustate);
  	}
  }
  
  static void tms7000_check_IRQ_lines(tms7000_state *cpustate)
  {
  	if( pSR & SR_I ) /* Check Global Interrupt bit: Status register, bit 4 */
  	{
  		if ((cpustate->irq_state[TMS7000_IRQ1_LINE] == ASSERT_LINE) || (cpustate->pf[0] & 0x02))
  		{
  			if( cpustate->pf[0] & 0x01 ) /* INT1 Enable bit */
  			{
  				tms7000_do_interrupt( cpustate, 0xfffc, TMS7000_IRQ1_LINE );
  				cpustate->pf[0] &= ~0x02; /* Data Manual, page: 9-41 */
  				return;
  			}
  		}
  
  		if( cpustate->irq_state[ TMS7000_IRQ2_LINE ] == ASSERT_LINE )
  		{
  			if( cpustate->pf[0] & 0x04 ) /* INT2 Enable bit */
  			{
  				tms7000_do_interrupt( cpustate, 0xfffa, TMS7000_IRQ2_LINE );
  				return;
  			}
  		}
  
  		if ((cpustate->irq_state[TMS7000_IRQ3_LINE] == ASSERT_LINE) || (cpustate->pf[0] & 0x20))
  		{
  			if( cpustate->pf[0] & 0x10 ) /* INT3 Enable bit */
  			{
  				tms7000_do_interrupt( cpustate, 0xfff8, TMS7000_IRQ3_LINE );
  				cpustate->pf[0] &= ~0x20; /* Data Manual, page: 9-41 */
  				return;
  			}
  		}
  	}
  }
  
  static void tms7000_do_interrupt( tms7000_state *cpustate, UINT16 address, UINT8 line )
  {
  	PUSHBYTE( pSR );		/* Push Status register */
  	PUSHWORD( PC );			/* Push Program Counter */
  	pSR = 0;				/* Clear Status register */
  	pPC = RM16(cpustate, address);	/* Load PC with interrupt vector */
  
  	if( cpustate->idle_state == 0 )
  		cpustate->icount -= 19;		/* 19 cycles used */
  	else
  	{
  		cpustate->icount -= 17;		/* 17 if idled */
  		cpustate->idle_state = 0;
  	}
  
  	(void)(*cpustate->irq_callback)(cpustate->device, line);
  }
  
  #include "tms70op.c"
  #include "tms70tb.c"
  
  static CPU_EXECUTE( tms7000 )
  {
  	tms7000_state *cpustate = get_safe_token(device);
  	int op;
  
  	cpustate->icount = cycles;
  	cpustate->div_by_16_trigger += cycles;
  
      tms7000_check_IRQ_lines(cpustate);
  
  	do
  	{
  		debugger_instruction_hook(device, pPC);
  
  		if( cpustate->idle_state == 0 )
  		{
  			op = memory_decrypted_read_byte(cpustate->program, pPC++);
  
  			opfn[op](cpustate);
  		}
  		else
  			cpustate->icount -= 16;
  
  		/* Internal timer system */
  
  		while( cpustate->icount < cpustate->div_by_16_trigger )
  		{
  			cpustate->div_by_16_trigger -= 16;
  
  			if( (cpustate->pf[0x03] & 0x80) == 0x80 ) /* Is timer system active? */
  			{
  				if( (cpustate->pf[0x03] & 0x40) != 0x40) /* Is system clock (divided by 16) the timer source? */
  					tms7000_service_timer1(device);
  			}
  		}
  
  	} while( cpustate->icount > 0 );
  
  	cpustate->div_by_16_trigger -= cpustate->icount;
  	return cycles - cpustate->icount;
  }
  
  static CPU_EXECUTE( tms7000_exl )
  {
  	tms7000_state *cpustate = get_safe_token(device);
  	int op;
  
  	cpustate->icount = cycles;
  	cpustate->div_by_16_trigger += cycles;
  
      tms7000_check_IRQ_lines(cpustate);
  
  	do
  	{
  		debugger_instruction_hook(device, pPC);
  
  		if( cpustate->idle_state == 0 )
  		{
  
  			op = memory_decrypted_read_byte(cpustate->program, pPC++);
  
  			opfn_exl[op](cpustate);
  		}
  		else
  			cpustate->icount -= 16;
  
  		/* Internal timer system */
  
  		while( cpustate->icount < cpustate->div_by_16_trigger )
  		{
  			cpustate->div_by_16_trigger -= 16;
  
  			if( (cpustate->pf[0x03] & 0x80) == 0x80 ) /* Is timer system active? */
  			{
  				if( (cpustate->pf[0x03] & 0x40) != 0x40) /* Is system clock (divided by 16) the timer source? */
  					tms7000_service_timer1(device);
  			}
  		}
  
  	} while( cpustate->icount > 0 );
  
  	cpustate->div_by_16_trigger -= cpustate->icount;
  	return cycles - cpustate->icount;
  }
  
  /****************************************************************************
   * Trigger the event counter
   ****************************************************************************/
  void tms7000_A6EC1( const device_config *device )
  {
  	tms7000_state *cpustate = get_safe_token(device);
      if( (cpustate->pf[0x03] & 0x80) == 0x80 ) /* Is timer system active? */
      {
          if( (cpustate->pf[0x03] & 0x40) == 0x40) /* Is event counter the timer source? */
              tms7000_service_timer1(device);
      }
  }
  
  static void tms7000_service_timer1( const device_config *device )
  {
  	tms7000_state *cpustate = get_safe_token(device);
      if( --cpustate->t1_prescaler < 0 ) /* Decrement prescaler and check for underflow */
      {
          cpustate->t1_prescaler = cpustate->pf[3] & 0x1f; /* Reload prescaler (5 bit) */
  
          if( --cpustate->t1_decrementer < 0 ) /* Decrement timer1 register and check for underflow */
          {
              cpustate->t1_decrementer = cpustate->pf[2]; /* Reload decrementer (8 bit) */
  			cpu_set_input_line(device, TMS7000_IRQ2_LINE, HOLD_LINE);
              //LOG( ("tms7000: trigger int2 (cycles: %d)\t%d\tdelta %d\n", cpu_get_total_cycles(device), cpu_get_total_cycles(device) - tick, cpustate->cycles_per_INT2-(cpu_get_total_cycles(device) - tick) );
  			//tick = cpu_get_total_cycles(device) );
              /* Also, cascade out to timer 2 - timer 2 unimplemented */
          }
      }
  //  LOG( ( "tms7000: service timer1. 0x%2.2x 0x%2.2x (cycles %d)\t%d\t\n", cpustate->t1_prescaler, cpustate->t1_decrementer, cpu_get_total_cycles(device), cpu_get_total_cycles(device) - tick2 ) );
  //  tick2 = cpu_get_total_cycles(device);
  }
  
  static WRITE8_HANDLER( tms70x0_pf_w )	/* Perpherial file write */
  {
  	tms7000_state *cpustate = get_safe_token(space->cpu);
  	UINT8	temp1, temp2, temp3;
  
  	switch( offset )
  	{
  		case 0x00:	/* IOCNT0, Input/Ouput control */
  			temp1 = data & 0x2a;							/* Record which bits to clear */
  			temp2 = cpustate->pf[0x00] & 0x2a;				/* Get copy of current bits */
  			temp3 = (~temp1) & temp2;						/* Clear the requested bits */
  			cpustate->pf[0x00] = temp3 | (data & (~0x2a) );	/* OR in the remaining data */
  			break;
  		case 0x02:
  			cpustate->t1_decrementer = cpustate->pf[0x02] = data;
  			cpustate->cycles_per_INT2 = 0x10*((cpustate->pf[3] & 0x1f)+1)*(cpustate->pf[0x02]+1);
  			LOG( ( "tms7000: Timer adjusted. Decrementer: 0x%2.2x (Cycles per interrupt: %d)\n", cpustate->t1_decrementer, cpustate->cycles_per_INT2 ) );
  			break;
  		case 0x03:	/* T1CTL, timer 1 control */
  			if( ((cpustate->pf[0x03] & 0x80) == 0) && ((data & 0x80) == 0x80 ) )   /* Start timer? */
  			{
  				cpustate->pf[0x03] = data;
  				cpustate->t1_prescaler = cpustate->pf[3] & 0x1f; /* Reload prescaler (5 bit) */
  				cpustate->cycles_per_INT2 = 0x10*((cpustate->pf[3] & 0x1f)+1)*(cpustate->pf[0x02]+1);
  				LOG( ( "tms7000: Timer started. Prescaler: 0x%2.2x (Cycles per interrupt: %d)\n", cpustate->pf[3] & 0x1f, cpustate->cycles_per_INT2 ) );
  			}
  			else if( ((data & 0x80) == 0x80 ) && ((cpustate->pf[0x03] & 0x80) == 0) )   /* Timer Stopped? */
  			{
  				cpustate->pf[0x03] = data;
  				cpustate->t1_prescaler = cpustate->pf[3] & 0x1f; /* Reload prescaler (5 bit) */
  				cpustate->cycles_per_INT2 = 0x10*((cpustate->pf[3] & 0x1f)+1)*(cpustate->pf[0x02]+1);
  				LOG( ( "tms7000: Timer stopped. Prescaler: 0x%2.2x (Cycles per interrupt: %d)\n", cpustate->pf[3] & 0x1f, cpustate->cycles_per_INT2 ) );
  			}
  			else /* Don't modify timer state, but still store data */
  			{
  				cpustate->pf[0x03] = data;
  				cpustate->cycles_per_INT2 = 0x10*((cpustate->pf[3] & 0x1f)+1)*(cpustate->pf[0x02]+1);
  				LOG( ( "tms7000: Timer adjusted. Prescaler: 0x%2.2x (Cycles per interrupt: %d)\n", cpustate->pf[3] & 0x1f, cpustate->cycles_per_INT2 ) );
  			}
  			break;
  
  		case 0x04: /* Port A write */
  			/* Port A is read only so this is a NOP */
  			break;
  
  		case 0x06: /* Port B write */
  			memory_write_byte_8be( cpustate->io, TMS7000_PORTB, data );
  			cpustate->pf[ 0x06 ] = data;
  			break;
  
  		case 0x08: /* Port C write */
  			temp1 = data & cpustate->pf[ 0x09 ];	/* Mask off input bits */
  			memory_write_byte_8be( cpustate->io, TMS7000_PORTC, temp1 );
  			cpustate->pf[ 0x08 ] = temp1;
  			break;
  
  		case 0x0a: /* Port D write */
  			temp1 = data & cpustate->pf[ 0x0b ];	/* Mask off input bits */
  			memory_write_byte_8be( cpustate->io, TMS7000_PORTD, temp1 );
  			cpustate->pf[ 0x0a ] = temp1;
  			break;
  
  		default:
  			/* Just stuff the other registers */
  			cpustate->pf[ offset ] = data;
  			break;
  	}
  }
  
  static READ8_HANDLER( tms70x0_pf_r )	/* Perpherial file read */
  {
  	tms7000_state *cpustate = get_safe_token(space->cpu);
  	UINT8 result;
  	UINT8	temp1, temp2, temp3;
  
  	switch( offset )
  	{
  		case 0x00:	/* IOCNT0, Input/Ouput control */
  			result = cpustate->pf[0x00];
  			if (cpustate->irq_state[TMS7000_IRQ1_LINE] == ASSERT_LINE)
  				result |= 0x02;
  			if (cpustate->irq_state[TMS7000_IRQ3_LINE] == ASSERT_LINE)
  				result |= 0x20;
  			break;
  
  		case 0x02:	/* T1DATA, timer 1 8-bit decrementer */
  			result = (cpustate->t1_decrementer & 0x00ff);
  			break;
  
  		case 0x03:	/* T1CTL, timer 1 capture (latched by INT3) */
  			result = cpustate->t1_capture_latch;
  			break;
  
  		case 0x04: /* Port A read */
  			result = memory_read_byte_8be( cpustate->io, TMS7000_PORTA );
  			break;
  
  
  		case 0x06: /* Port B read */
  			/* Port B is write only, return a previous written value */
  			result = cpustate->pf[ 0x06 ];
  			break;
  
  		case 0x08: /* Port C read */
  			temp1 = cpustate->pf[ 0x08 ] & cpustate->pf[ 0x09 ];	/* Get previous output bits */
  			temp2 = memory_read_byte_8be( cpustate->io, TMS7000_PORTC );			/* Read port */
  			temp3 = temp2 & (~cpustate->pf[ 0x09 ]);				/* Mask off output bits */
  			result = temp1 | temp3;								/* OR together */
  			break;
  
  		case 0x0a: /* Port D read */
  			temp1 = cpustate->pf[ 0x0a ] & cpustate->pf[ 0x0b ];	/* Get previous output bits */
  			temp2 = memory_read_byte_8be( cpustate->io, TMS7000_PORTD );			/* Read port */
  			temp3 = temp2 & (~cpustate->pf[ 0x0b ]);				/* Mask off output bits */
  			result = temp1 | temp3;								/* OR together */
  			break;
  
  		default:
  			/* Just unstuff the other registers */
  			result = cpustate->pf[ offset ];
  			break;
  	}
  
  	return result;
  }
  
  // BCD arthrimetic handling
  static UINT16 bcd_add( UINT16 a, UINT16 b )
  {
  	UINT16	t1,t2,t3,t4,t5,t6;
  
  	/* Sure it is a lot of code, but it works! */
  	t1 = a + 0x0666;
  	t2 = t1 + b;
  	t3 = t1 ^ b;
  	t4 = t2 ^ t3;
  	t5 = ~t4 & 0x1110;
  	t6 = (t5 >> 2) | (t5 >> 3);
  	return t2-t6;
  }
  
  static UINT16 bcd_tencomp( UINT16 a )
  {
  	UINT16	t1,t2,t3,t4,t5,t6;
  
  	t1 = 0xffff - a;
  	t2 = -a;
  	t3 = t1 ^ 0x0001;
  	t4 = t2 ^ t3;
  	t5 = ~t4 & 0x1110;
  	t6 = (t5 >> 2)|(t5>>3);
  	return t2-t6;
  }
  
  /*
      Compute difference a-b???
  */
  static UINT16 bcd_sub( UINT16 a, UINT16 b)
  {
  	//return bcd_tencomp(b) - bcd_tencomp(a);
  	return bcd_add(a, bcd_tencomp(b) & 0xff);
  }
  
  static WRITE8_HANDLER( tms7000_internal_w ) {
  	tms7000_state *cpustate = get_safe_token(space->cpu);
  	cpustate->rf[ offset ] = data;
  }
  
  static READ8_HANDLER( tms7000_internal_r ) {
  	tms7000_state *cpustate = get_safe_token(space->cpu);
  	return cpustate->rf[ offset ];
  }
  