/*

	Hitachi H8S/2xxx MCU peripherals

	(c) 2001-2007 Tim Schuerewegen

	H8S/2241
	H8S/2246
	H8S/2323

*/

#include "emu.h"
#include "debugger.h"
#include "h8.h"
#include "h8priv.h"

#define VERBOSE_LEVEL ( 0 )

INLINE void ATTR_PRINTF(3,4) 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( "%s: %s", machine.describe_context( ), buf);
	}
}

const UINT8 H8S_RESET_H8S_IO_224x[0x1C0] = // values from "e602100_h8s2245.pdf"
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE40
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE50
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE60
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE70
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE80
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE90
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FEA0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, // FEB0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FEC0
	0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FED0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FEE0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FEF0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF00
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF10
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF20
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, // FF30
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF40
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF50
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF60
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x84, 0x00, 0xF2, 0x00, // FF70
	0x00, 0xFF, 0x00, 0xFF, 0x84, 0x00, 0xF2, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x84, 0x00, 0xF2, 0x00, // FF80
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF90
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FFA0
	0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FFB0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FFC0
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FFD0
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // FFE0
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // FFF0
};

const UINT8 H8S_RESET_H8S_IO_2323[0x1C0] = // values from "rej09b0220_h8s2329.pdf"
{
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE40
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE50
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE60
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FE70
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FE80
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // FE90
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // FEA0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, // FEB0
	0x00, 0x00, 0x00, 0x00, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, // FEC0
	0xFF, 0xFF, 0xFF, 0xFF, 0xD0, 0x3C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FED0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FEE0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FEF0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF00
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF10
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF20
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x3F, 0xFF, 0x00, 0x00, // FF30
	0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF40
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF50
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF60
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x84, 0x00, 0xF2, 0x00, // FF70
	0x00, 0xFF, 0x00, 0xFF, 0x84, 0x00, 0xF2, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x84, 0x00, 0xF2, 0x00, // FF80
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FF90
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, // FFA0
	0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FFB0
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // FFC0
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // FFD0
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // FFE0
	0x00, 0xC0, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // FFF0
};

#define SCR_TIE		0x80
#define SCR_RIE		0x40
#define SCR_TE		0x20
#define SCR_RE		0x10

#define SSR_TDRE  0x80
#define SSR_RDRF  0x40
#define SSR_ORER  0x20
#define SSR_FER   0x10
#define SSR_PER   0x08
#define SSR_TEND  0x04

#define SMR_CM    0x80
#define SMR_CHR   0x40
#define SMR_PE    0x20
#define SMR_PM    0x10
#define SMR_STOP  0x08
#define SMR_MP    0x04
#define SMR_CKS1  0x02
#define SMR_CKS0  0x01

#define TCSR_CMFB  0x80
#define TCSR_CMFA  0x40
#define TCSR_OVF   0x20

#define TCR_CMIEB  0x80
#define TCR_CMIEA  0x40
#define TCR_OVIE   0x20

#define TSR_TCFD  0x80
#define TSR_TCFU  0x20
#define TSR_TCFV  0x10
#define TSR_TGFD  0x08
#define TSR_TGFC  0x04
#define TSR_TGFB  0x02
#define TSR_TGFA  0x01

#define TIER_TCIEU  0x20
#define TIER_TCIEV  0x10
#define TIER_TGIED  0x08
#define TIER_TGIEC  0x04
#define TIER_TGIEB  0x02
#define TIER_TGIEA  0x01

#define DTC_MRA_MD_NORMAL  0
#define DTC_MRA_MD_REPEAT  1
#define DTC_MRA_MD_BLOCK   2

#define DTC_MRA_SM1  0x80
#define DTC_MRA_SM0  0x40
#define DTC_MRA_DM1  0x20
#define DTC_MRA_DM0  0x10
#define DTC_MRA_DTS  0x02

#define DTC_MRB_CHNE   0x80
#define DTC_MRB_DISEL  0x40
#define DTC_MRB_CHNS   0x20

#define DMABCRL_DTME1   0x80
#define DMABCRL_DTE1    0x40
#define DMABCRL_DTME0   0x20
#define DMABCRL_DTE0    0x10
#define DMABCRL_DTIE1B  0x08
#define DMABCRL_DTIE1A  0x04
#define DMABCRL_DTIE0B  0x02
#define DMABCRL_DTIE0A  0x01

#define ADCSR_ADF   0x80
#define ADCSR_ADIE  0x40
#define ADCSR_ADST  0x20
#define ADCSR_SCAN  0x10

enum
{
	PIN_P10, PIN_P11, PIN_P12, PIN_P13, PIN_P14, PIN_P15, PIN_P16, PIN_P17,
	PIN_P20, PIN_P21, PIN_P22, PIN_P23, PIN_P24, PIN_P25, PIN_P26, PIN_P27,
	PIN_P30, PIN_P31, PIN_P32, PIN_P33, PIN_P34, PIN_P35,
	PIN_P40, PIN_P41, PIN_P42, PIN_P43, PIN_P44, PIN_P45, PIN_P46, PIN_P47,
	PIN_P50, PIN_P51, PIN_P52, PIN_P53,
	PIN_P60, PIN_P61, PIN_P62, PIN_P63, PIN_P64, PIN_P65, PIN_P66, PIN_P67,
	PIN_PA0, PIN_PA1, PIN_PA2, PIN_PA3, PIN_PA4, PIN_PA5, PIN_PA6, PIN_PA7,
	PIN_PB0, PIN_PB1, PIN_PB2, PIN_PB3, PIN_PB4, PIN_PB5, PIN_PB6, PIN_PB7,
	PIN_PC0, PIN_PC1, PIN_PC2, PIN_PC3, PIN_PC4, PIN_PC5, PIN_PC6, PIN_PC7,
	PIN_PD0, PIN_PD1, PIN_PD2, PIN_PD3, PIN_PD4, PIN_PD5, PIN_PD6, PIN_PD7,
	PIN_PE0, PIN_PE1, PIN_PE2, PIN_PE3, PIN_PE4, PIN_PE5, PIN_PE6, PIN_PE7,
	PIN_PF0, PIN_PF1, PIN_PF2, PIN_PF3, PIN_PF4, PIN_PF5, PIN_PF6, PIN_PF7,
	PIN_PG0, PIN_PG1, PIN_PG2, PIN_PG3, PIN_PG4
};

#define PIN_TIOCA0	PIN_P10
#define PIN_TIOCB0	PIN_P11
#define PIN_TIOCC0	PIN_P12
#define PIN_TIOCD0	PIN_P13
#define PIN_TIOCA1	PIN_P14
#define PIN_TIOCB1	PIN_P15
#define PIN_TIOCA2	PIN_P16
#define PIN_TIOCB2	PIN_P17

#define PIN_TCLKA	PIN_P12
#define PIN_TCLKB	PIN_P13
#define PIN_TCLKC	PIN_P15
#define PIN_TCLKD	PIN_P17

#define PIN_TXD0	PIN_P30
#define PIN_TXD1	PIN_P31
#define PIN_RXD0	PIN_P32
#define PIN_RXD1	PIN_P33
#define PIN_SCK0	PIN_P34
#define PIN_SCK1	PIN_P35

#define PIN_TIOCA3	PIN_P20
#define PIN_TIOCB3	PIN_P21
#define PIN_TIOCC3	PIN_P22
#define PIN_TIOCD3	PIN_P23
#define PIN_TIOCA4	PIN_P24
#define PIN_TIOCB4	PIN_P25
#define PIN_TIOCA5	PIN_P26
#define PIN_TIOCB5	PIN_P27

#define PIN_TMRI0	PIN_P22
#define PIN_TMCI0	PIN_P23
#define PIN_TMRI1	PIN_P24
#define PIN_TMCI1	PIN_P25
#define PIN_TMO0	PIN_P26
#define PIN_TMO1	PIN_P27

const int TPU_0_DIV[] = { 1, 4, 16, 64, 0, 0, 0,0 };
const int TPU_1_DIV[] = { 1, 4, 16, 64, 0, 0, 256, 0 };
const int TPU_2_DIV[] = { 1, 4, 16, 64, 0, 0, 0, 1024 };
const int TPU_3_DIV[] = { 1, 4, 16, 64, 0, 1024, 256, 4096 };
const int TPU_4_DIV[] = { 1, 4, 16, 64, 0, 0, 1024, 0 };
const int TPU_5_DIV[] = { 1, 4, 16, 64, 0, 0, 256, 0 };

const int H8S_IO_SMR[] = { H8S_IO_SMR0, H8S_IO_SMR1, H8S_IO_SMR2 };
const int H8S_IO_BRR[] = { H8S_IO_BRR0, H8S_IO_BRR1, H8S_IO_BRR2 };
const int H8S_IO_SCR[] = { H8S_IO_SCR0, H8S_IO_SCR1, H8S_IO_SCR2 };
const int H8S_IO_TDR[] = { H8S_IO_TDR0, H8S_IO_TDR1, H8S_IO_TDR2 };
const int H8S_IO_SSR[] = { H8S_IO_SSR0, H8S_IO_SSR1, H8S_IO_SSR2 };
const int H8S_IO_RDR[] = { H8S_IO_RDR0, H8S_IO_RDR1, H8S_IO_RDR2 };

const int H8S_INT_TXI[] = { H8S_INT_TXI0, H8S_INT_TXI1, H8S_INT_TXI2 };
const int H8S_INT_RXI[] = { H8S_INT_RXI0, H8S_INT_RXI1, H8S_INT_RXI2 };

const int H8S_IO_TCORA[] = { H8S_IO_TCORA0, H8S_IO_TCORA1 };
const int H8S_IO_TCORB[] = { H8S_IO_TCORB0, H8S_IO_TCORB1 };
const int H8S_IO_TCR[]   = { H8S_IO_TCR0,   H8S_IO_TCR1   };
const int H8S_IO_TCNT[]  = { H8S_IO_TCNT0,  H8S_IO_TCNT1  };
const int H8S_IO_TCSR[]  = { H8S_IO_TCSR0,  H8S_IO_TCSR1  };

const int H8S_INT_OVI[]  = { H8S_INT_OVI0,  H8S_INT_OVI1  };
const int H8S_INT_CMIA[] = { H8S_INT_CMIA0, H8S_INT_CMIA1 };
const int H8S_INT_CMIB[] = { H8S_INT_CMIB0, H8S_INT_CMIB1 };

const int H8S_IO_TGRA_H[] = { H8S_IO_TGR0A_H, H8S_IO_TGR1A_H, H8S_IO_TGR2A_H, H8S_IO_TGR3A_H, H8S_IO_TGR4A_H, H8S_IO_TGR5A_H };
const int H8S_IO_TGRA_L[] = { H8S_IO_TGR0A_L, H8S_IO_TGR1A_L, H8S_IO_TGR2A_L, H8S_IO_TGR3A_L, H8S_IO_TGR4A_L, H8S_IO_TGR5A_L };
const int H8S_IO_TGRB_H[] = { H8S_IO_TGR0B_H, H8S_IO_TGR1B_H, H8S_IO_TGR2B_H, H8S_IO_TGR3B_H, H8S_IO_TGR4B_H, H8S_IO_TGR5B_H };
const int H8S_IO_TGRB_L[] = { H8S_IO_TGR0B_L, H8S_IO_TGR1B_L, H8S_IO_TGR2B_L, H8S_IO_TGR3B_L, H8S_IO_TGR4B_L, H8S_IO_TGR5B_L };
const int H8S_IO_TGRC_H[] = { H8S_IO_TGR0C_H, 0, 0, H8S_IO_TGR3C_H, 0, 0 };
const int H8S_IO_TGRC_L[] = { H8S_IO_TGR0C_L, 0, 0, H8S_IO_TGR3C_L, 0, 0 };
const int H8S_IO_TGRD_H[] = { H8S_IO_TGR0D_H, 0, 0, H8S_IO_TGR3D_H, 0, 0 };
const int H8S_IO_TGRD_L[] = { H8S_IO_TGR0D_L, 0, 0, H8S_IO_TGR3D_L, 0, 0 };
const int H8S_IO_TCNT_H[] = { H8S_IO_TCNT0_H, H8S_IO_TCNT1_H, H8S_IO_TCNT2_H, H8S_IO_TCNT3_H, H8S_IO_TCNT4_H, H8S_IO_TCNT5_H };
const int H8S_IO_TCNT_L[] = { H8S_IO_TCNT0_L, H8S_IO_TCNT1_L, H8S_IO_TCNT2_L, H8S_IO_TCNT3_L, H8S_IO_TCNT4_L, H8S_IO_TCNT5_L };
const int H8S_IO_TIOR_H[] = { H8S_IO_TIOR0_H, H8S_IO_TIOR1, H8S_IO_TIOR2, H8S_IO_TIOR3_H, H8S_IO_TIOR4, H8S_IO_TIOR5 };
const int H8S_IO_TIOR_L[] = { H8S_IO_TIOR0_L, 0, 0, H8S_IO_TIOR3_L, 0, 0 };
const int H8S_IO_TTCR[]   = { H8S_IO_TTCR0, H8S_IO_TTCR1, H8S_IO_TTCR2, H8S_IO_TTCR3, H8S_IO_TTCR4, H8S_IO_TTCR5 };
const int H8S_IO_TIER[]   = { H8S_IO_TIER0, H8S_IO_TIER1, H8S_IO_TIER2, H8S_IO_TIER3, H8S_IO_TIER4, H8S_IO_TIER5 };
const int H8S_IO_TSR[]    = { H8S_IO_TSR0, H8S_IO_TSR1, H8S_IO_TSR2, H8S_IO_TSR3, H8S_IO_TSR4, H8S_IO_TSR5 };

const int H8S_INT_TCIV[] = { H8S_INT_TCI0V, H8S_INT_TCI1V, H8S_INT_TCI2V, H8S_INT_TCI3V, H8S_INT_TCI4V, H8S_INT_TCI5V };
const int H8S_INT_TCIU[] = { 0, H8S_INT_TCI1U, H8S_INT_TCI2U, 0, H8S_INT_TCI4U, H8S_INT_TCI5U };
const int H8S_INT_TGIA[] = { H8S_INT_TGI0A, H8S_INT_TGI1A, H8S_INT_TGI2A, H8S_INT_TGI3A, H8S_INT_TGI4A, H8S_INT_TGI5A };
const int H8S_INT_TGIB[] = { H8S_INT_TGI0B, H8S_INT_TGI1B, H8S_INT_TGI2B, H8S_INT_TGI3B, H8S_INT_TGI4B, H8S_INT_TGI5B };
const int H8S_INT_TGIC[] = { H8S_INT_TGI0C, 0, 0, H8S_INT_TGI3C, 0, 0 };
const int H8S_INT_TGID[] = { H8S_INT_TGI0D, 0, 0, H8S_INT_TGI3D, 0, 0 };

const int PIN_TIOCA[] = { PIN_TIOCA0, PIN_TIOCA1, PIN_TIOCA2, PIN_TIOCA3, PIN_TIOCA4, PIN_TIOCA5 };
const int PIN_TIOCB[] = { PIN_TIOCB0, PIN_TIOCB1, PIN_TIOCB2, PIN_TIOCB3, PIN_TIOCB4, PIN_TIOCB5 };
const int PIN_TIOCC[] = { PIN_TIOCC0, 0, 0, PIN_TIOCC3, 0, 0 };
const int PIN_TIOCD[] = { PIN_TIOCD0, 0, 0, PIN_TIOCD3, 0, 0 };

const int TCR_CCLR_AND[] = { 7, 3, 3, 7, 3, 3 };

// prototypes

void h8s_tmr_x_reset( h83xx_state *h8, int x);
void h8s_tpu_x_reset( h83xx_state *h8, int x);
void h8s_tpu_x_tick( h83xx_state *h8, int x);

////////////
// DTVECR //
////////////

#define DTVECR_ADDR(x) (0x400 + (x << 1))

///////////////
// MEMCONV.H //
///////////////

static TIMER_CALLBACK( h8s_tmr_callback );
static TIMER_CALLBACK( h8s_tpu_callback );
static TIMER_CALLBACK( h8s_sci_callback );

/////////////
// TIMER.C //
/////////////

UINT16 ptr_read_16( void *ptr)
{
	return (*(((UINT8*)ptr) + 0) << 8) | (*(((UINT8*)ptr) + 1) << 0);
}

void ptr_write_16( void *ptr, UINT16 data)
{
	*(((UINT8*)ptr) + 0) = (data >> 8) & 0xFF;
	*(((UINT8*)ptr) + 1) = (data >> 0) & 0xFF;
}

int device_icount_min_timer_icount1( h83xx_state *h8, emu_timer *timer)
{
	return (timer->elapsed() * h8->device->unscaled_clock()).as_double();
}

void h8s2xxx_interrupt_request(h83xx_state *h8, UINT32 vecnum)
{
	UINT8 idx, bit;
	idx = vecnum >> 5;
	bit = vecnum & 0x1F;
	h8->irq_req[idx] |= (1 << bit);
}

void h8s_dtce_execute(h83xx_state *h8, UINT32 addr_dtce, UINT8 mask_dtce, UINT32 addr_dtvecr)
{
	UINT32 data[3], dtc_vect, dtc_sar, dtc_dar, cnt, i;
	UINT8 dtc_mra, dtc_mrb, sz;
	UINT16 dtc_cra, dtc_crb;
	// get dtc info
	dtc_vect  = 0xFF0000 | h8->program->read_word(addr_dtvecr);
	data[0]   = h8->program->read_dword( dtc_vect + 0);
	data[1]   = h8->program->read_dword( dtc_vect + 4);
	data[2]   = h8->program->read_dword( dtc_vect + 8);
	dtc_mra   = (data[0] >> 24) & 0xFF;
	dtc_sar   = (data[0] >>  0) & 0xFFFFFF;
	dtc_mrb   = (data[1] >> 24) & 0xFF;
	dtc_dar   = (data[1] >>  0) & 0xFFFFFF;
	dtc_cra   = (data[2] >> 16) & 0xFFFF;
	dtc_crb   = (data[2] >>  0) & 0xFFFF;
	verboselog( h8->device->machine(), 3, "dtc : vect %08X mra %02X sar %08X mrb %02X dar %08X cra %04X crb %04X\n", dtc_vect, dtc_mra, dtc_sar, dtc_mrb, dtc_dar, dtc_cra, dtc_crb);
	// execute
	if ((dtc_mra & 0x0E) != 0x00) fatalerror("H8S: dtc unsupported MRA %x\n", dtc_mra&0x0e);
	sz = 1 << (dtc_mra & 0x01);
	cnt = dtc_cra;
	for (i=0;i<cnt;i++)
	{
		if (dtc_sar == H8S_IO_ADDR( H8S_IO_RDR1)) h8->program->write_byte( H8S_IO_ADDR( H8S_IO_SSR1), h8->program->read_byte( H8S_IO_ADDR( H8S_IO_SSR1)) & (~H8S_SSR_TDRE));
		if (dtc_mra & 0x01) h8->program->write_word( dtc_dar, h8->program->read_word( dtc_sar)); else h8->program->write_byte( dtc_dar, h8->program->read_byte( dtc_sar));
		if (dtc_dar == H8S_IO_ADDR( H8S_IO_TDR0)) h8->program->write_byte( H8S_IO_ADDR( H8S_IO_SSR0), h8->program->read_byte( H8S_IO_ADDR( H8S_IO_SSR0)) & (~H8S_SSR_TDRE));
		if (dtc_mra & 0x80) { if (dtc_mra & 0x40) dtc_sar -= sz; else dtc_sar += sz; }
		if (dtc_mra & 0x20) { if (dtc_mra & 0x10) dtc_dar -= sz; else dtc_dar += sz; }
	}
	h8->program->write_byte( addr_dtce, h8->program->read_byte( addr_dtce) & (~mask_dtce));
}

void h8s_dtce_check(h83xx_state *h8,  int vecnum)
{
	UINT32 dtce = 0;
	int bit = 0;
	// get dtce info
	switch (vecnum)
	{
		// DTCEA
		case H8S_INT_IRQ0  : dtce = H8S_IO_DTCEA; bit = 7; break;
		case H8S_INT_IRQ1  : dtce = H8S_IO_DTCEA; bit = 6; break;
		case H8S_INT_IRQ2  : dtce = H8S_IO_DTCEA; bit = 5; break;
		case H8S_INT_IRQ3  : dtce = H8S_IO_DTCEA; bit = 4; break;
		case H8S_INT_IRQ4  : dtce = H8S_IO_DTCEA; bit = 3; break;
		case H8S_INT_IRQ5  : dtce = H8S_IO_DTCEA; bit = 2; break;
		case H8S_INT_IRQ6  : dtce = H8S_IO_DTCEA; bit = 1; break;
		case H8S_INT_IRQ7  : dtce = H8S_IO_DTCEA; bit = 0; break;
		// DTCEB
		case H8S_INT_ADI   : dtce = H8S_IO_DTCEB; bit = 6; break;
		case H8S_INT_TGI0A : dtce = H8S_IO_DTCEB; bit = 5; break;
		case H8S_INT_TGI0B : dtce = H8S_IO_DTCEB; bit = 4; break;
		case H8S_INT_TGI0C : dtce = H8S_IO_DTCEB; bit = 3; break;
		case H8S_INT_TGI0D : dtce = H8S_IO_DTCEB; bit = 2; break;
		case H8S_INT_TGI1A : dtce = H8S_IO_DTCEB; bit = 1; break;
		case H8S_INT_TGI1B : dtce = H8S_IO_DTCEB; bit = 0; break;
		// DTCEC
		case H8S_INT_TGI2A : dtce = H8S_IO_DTCEC; bit = 7; break;
		case H8S_INT_TGI2B : dtce = H8S_IO_DTCEC; bit = 6; break;
		// DTCED
		case H8S_INT_CMIA0 : dtce = H8S_IO_DTCED; bit = 3; break;
		case H8S_INT_CMIB0 : dtce = H8S_IO_DTCED; bit = 2; break;
		case H8S_INT_CMIA1 : dtce = H8S_IO_DTCED; bit = 1; break;
		case H8S_INT_CMIB1 : dtce = H8S_IO_DTCED; bit = 0; break;
		// DTCEE
		case H8S_INT_RXI0  : dtce = H8S_IO_DTCEE; bit = 3; break;
		case H8S_INT_TXI0  : dtce = H8S_IO_DTCEE; bit = 2; break;
		case H8S_INT_RXI1  : dtce = H8S_IO_DTCEE; bit = 1; break;
		case H8S_INT_TXI1  : dtce = H8S_IO_DTCEE; bit = 0; break;
		// DTCEF
		case H8S_INT_RXI2  : dtce = H8S_IO_DTCEF; bit = 7; break;
		case H8S_INT_TXI2  : dtce = H8S_IO_DTCEF; bit = 6; break;
	}
	// execute
	if ((dtce != 0) && (h8->per_regs[dtce] & (1 << bit))) h8s_dtce_execute(h8, H8S_IO_ADDR( dtce), (1 << bit), DTVECR_ADDR( vecnum));
}

void h8s_periph_reset(h83xx_state *h8)
{
	const int tpu_max = (h8->device->type() == H8S2323) ? 6 : 3;
	if ((h8->device->type() == H8S2241) || (h8->device->type() == H8S2246))
	{
		memcpy( h8->per_regs, H8S_RESET_H8S_IO_224x, sizeof( h8->per_regs));
	}
	else if (h8->device->type() == H8S2323)
	{
		memcpy( h8->per_regs, H8S_RESET_H8S_IO_2323, sizeof( h8->per_regs));
	}
	for (int i = 0; i < 2; i++) h8s_tmr_x_reset( h8, i);
	for (int i = 0; i < tpu_max; i++) h8s_tpu_x_reset( h8, i);
}

/////////////////
// TIMER 8-BIT //
/////////////////

void h8s_tmr_init(h83xx_state *h8)
{
	for (int i = 0; i < 2; i++)
	{
		h8->tmr[i].timer = h8->device->machine().scheduler().timer_alloc(FUNC(h8s_tmr_callback), h8);
		h8->tmr[i].timer->adjust(attotime::never, i);
	}
}

void h8s_tmr_x_reset( h83xx_state *h8, int x)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	tmr->cycles_per_tick = 0;
	tmr->timer->adjust(attotime::never, x);
}

UINT64 h8s_tmr_x_calculate_ticks( h83xx_state *h8, int x, UINT8 tcnt)
{
	UINT64 cycles1, cycles2;
	UINT8 tcor;
	// overflow
	if (tcnt == 0) cycles1 = 0xFF + 1; else cycles1 = (UINT8)(0xFF - tcnt + 1);
	// tcora
	tcor = h8->per_regs[H8S_IO_TCORA[x]];
	if (tcnt == tcor) cycles2 = 0xFF + 1; else cycles2 = (UINT8)(tcor - tcnt);
	if ((cycles2 > 0) && (cycles2 < cycles1)) cycles1 = cycles2;
	// tcorb
	tcor = h8->per_regs[H8S_IO_TCORB[x]];
	if (tcnt == tcor) cycles2 = 0xFF + 1; else cycles2 = (UINT8)(tcor - tcnt);
	if ((cycles2 > 0) && (cycles2 < cycles1)) cycles1 = cycles2;
	// done
	return cycles1;
}

void h8s_tmr_x_update( h83xx_state *h8, int x)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	UINT64 cycles;
	int div = 0;
	UINT8 tcr, tcnt;
	tcr = h8->per_regs[H8S_IO_TCR[x]];
	switch ((tcr >> 0) & 7)
	{
		case 1  : div = 8; break;
		case 2  : div = 64; break;
		case 3  : div = 8192; break;
//		default : OS_MessageBox( NULL, "tmr_x_update", "error", 0); break;
	}
	tmr->cycles_per_tick = div;
	tcnt = h8->per_regs[H8S_IO_TCNT[x]];
	verboselog( h8->device->machine(), 5, "TMR %d - update (div %d tcnt %02X)\n", x, div, tcnt);
	// update timer
	cycles = h8s_tmr_x_calculate_ticks( h8, x, tcnt) * tmr->cycles_per_tick;
	tmr->timer_cycles = cycles;
	tmr->timer->adjust(attotime::from_hz(h8->device->unscaled_clock() / cycles), x);
}

UINT8 h8s_tmr_x_calculate_tcnt( h83xx_state *h8, int x)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	UINT8 tcnt;
	tcnt = h8->per_regs[H8S_IO_TCNT[x]];
	if (tmr->timer->enabled())
	{
		tcnt = (UINT8)(tcnt + ((device_icount_min_timer_icount1( h8, tmr->timer)) / tmr->cycles_per_tick));
	}
	return tcnt;
}

void h8s_tmr_x_start( h83xx_state *h8, int x)
{
	verboselog( h8->device->machine(), 5, "TMR %d - start\n", x);
	// ...
	h8s_tmr_x_update( h8, x);
}

void h8s_tmr_x_stop( h83xx_state *h8, int x)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	UINT8 tcnt;
	// ...
	verboselog( h8->device->machine(), 5, "TMR %d - stop\n", x);
	// calculate tcnt
	tcnt = h8s_tmr_x_calculate_tcnt( h8, x);
	// update tcnt
	h8->per_regs[H8S_IO_TCNT[x]] = tcnt;
	// disable timer
	tmr->timer->reset();
}

UINT8 h8s_tmr_x_tcnt_change( h83xx_state *h8, int x, UINT8 tcnt)
{
	UINT8 tcr, tcor;
	int clear = 0;
	// ...
	verboselog( h8->device->machine(), 9, "TMR %d - tmr_x_tcnt_change (%02X)\n", x, tcnt);
	// ...
	tcr = h8->per_regs[H8S_IO_TCR[x]];
	// overflow
	if (tcnt == 0)
	{
		verboselog( h8->device->machine(), 9, "TMR %d - overflow match\n", x);
		h8->per_regs[H8S_IO_TCSR[x]] |= TCSR_OVF;
		if ((tcr & TCR_OVIE) != 0)
		{
			h8s2xxx_interrupt_request( h8, H8S_INT_OVI[x]);
		}
	}
	// tcora
	tcor = h8->per_regs[H8S_IO_TCORA[x]];
	if (tcnt == tcor)
	{
		verboselog( h8->device->machine(), 9, "TMR %d - tcora match\n", x);
		h8->per_regs[H8S_IO_TCSR[x]] |= TCSR_CMFA;
		if ((tcr & TCR_CMIEA) != 0)
		{
			h8s2xxx_interrupt_request( h8, H8S_INT_CMIA[x]);
		}
		if (((tcr >> 3) & 3) == 1) // CCLR 1 -> "clear by compare match a"
		{
			clear = 1;
		}
	}
	// tcorb
	tcor = h8->per_regs[H8S_IO_TCORB[x]];
	if (tcnt == tcor)
	{
		verboselog( h8->device->machine(), 9, "TMR %d - tcorb match\n", x);
		h8->per_regs[H8S_IO_TCSR[x]] |= TCSR_CMFB;
		if ((tcr & TCR_CMIEB) != 0)
		{
			h8s2xxx_interrupt_request( h8, H8S_INT_CMIB[x]);
		}
		if (((tcr >> 3) & 3) == 2) // CCLR 2 -> "clear by compare match b"
		{
			clear = 1;
		}
	}
	// ...
	if (clear)
	{
		verboselog( h8->device->machine(), 5, "TMR %d - tcnt clear\n", x);
		tcnt = 0;
	}
	// ...
	return tcnt;
}

void h8s_tmr_x_callback( h83xx_state *h8, int x)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	UINT64 cycles;
	UINT8 tcnt;
	// calculate tcnt
	tcnt = h8->per_regs[H8S_IO_TCNT[x]];
	tcnt = (UINT8)(tcnt + tmr->timer_cycles / tmr->cycles_per_tick);
	// ...
	tcnt = h8s_tmr_x_tcnt_change( h8, x, tcnt);
	// update tcnt
	h8->per_regs[H8S_IO_TCNT[x]] = tcnt;
	// update timer
	cycles = h8s_tmr_x_calculate_ticks( h8, x, tcnt) * tmr->cycles_per_tick;
	tmr->timer_cycles = cycles;
	tmr->timer->adjust(attotime::from_hz(h8->device->unscaled_clock() / cycles), x);
}

void h8s_tmr_x_write_tcr( h83xx_state *h8, int x, UINT8 data)
{
	UINT8 old_data = h8->per_regs[H8S_IO_TCR[x]];
	h8->per_regs[H8S_IO_TCR[x]] = data;
	if ((data & 0x07) != (old_data & 0x07))
	{
		if ((data & 0x07) != 0)
		{
			h8s_tmr_x_start( h8, x);
		}
		else
		{
			h8s_tmr_x_stop( h8, x);
		}
	}
}

void h8s_tmr_x_write_tcnt( h83xx_state *h8, int x, UINT8 data)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	h8->per_regs[H8S_IO_TCNT[x]] = data;
	if (tmr->timer->enabled())
	{
		h8s_tmr_x_update( h8, x);
	}
}

void h8s_tmr_x_write_tcor( h83xx_state *h8, int x)
{
	H8S2XXX_TMR *tmr = &h8->tmr[x];
	if ((tmr->timer->enabled()) && (tmr->cycles_per_tick != 0))
	{
		UINT8 tcnt1, tcnt2;
		UINT64 cycles;
		tcnt1 = h8->per_regs[H8S_IO_TCNT[x]];
		tcnt2 = (UINT8)(tcnt1 + ((device_icount_min_timer_icount1( h8, tmr->timer)) / tmr->cycles_per_tick));
		cycles = h8s_tmr_x_calculate_ticks( h8, x, tcnt2) * tmr->cycles_per_tick;
		cycles = cycles + (tcnt2 - tcnt1) * tmr->cycles_per_tick;
		tmr->timer_cycles = cycles;
		tmr->timer->adjust(attotime::from_hz(h8->device->unscaled_clock() / cycles), x);
	}
}

UINT8 h8s_tmr_x_read_tcnt( h83xx_state *h8, int x)
{
	return h8s_tmr_x_calculate_tcnt( h8, x);
}

void h8s_tmr_x_write_tcora( h83xx_state *h8, int x, UINT8 data)
{
	h8->per_regs[H8S_IO_TCORA[x]] = data;
	h8s_tmr_x_write_tcor( h8, x);
}

void h8s_tmr_x_write_tcorb( h83xx_state *h8, int x, UINT8 data)
{
	h8->per_regs[H8S_IO_TCORB[x]] = data;
	h8s_tmr_x_write_tcor( h8, x);
}

static TIMER_CALLBACK( h8s_tmr_callback)
{
	h83xx_state *h8 = (h83xx_state *)ptr;
	h8s_tmr_x_callback( h8, param);
}

//////////////////
// TIMER 16-BIT //
//////////////////

void h8s_tpu_init(h83xx_state *h8)
{
	const int tpu_max = (h8->device->type() == H8S2323) ? 6 : 3;
	for (int i = 0; i < tpu_max; i++)
	{
		h8->tpu[i].timer = h8->device->machine().scheduler().timer_alloc(FUNC(h8s_tpu_callback), h8);
		h8->tpu[i].timer->adjust(attotime::never, i);
	}
}

void h8s_tpu_x_reset( h83xx_state *h8, int x)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	tpu->cycles_per_tick = 0;
	tpu->timer->reset();
}

UINT16 h8s_tpu_x_calculate_tcnt( h83xx_state *h8, int x)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	UINT16 tcnt;
	tcnt = ptr_read_16( h8->per_regs + H8S_IO_TCNT_H[x]);
	if ((tpu->timer->enabled()) && (tpu->cycles_per_tick > 0))
	{
		tcnt = (UINT16)(tcnt + ((device_icount_min_timer_icount1( h8, tpu->timer)) / tpu->cycles_per_tick));
	}
	return tcnt;
}

UINT64 h8s_tpu_x_calculate_ticks( h83xx_state *h8, int x, UINT16 tcnt)
{
	UINT64 cycles1, cycles2;
	UINT16 tgr;
	// overflow
	if (tcnt == 0) cycles1 = 0xFFFF + 1; else cycles1 = (UINT16)(0xFFFF - tcnt + 1);
	// tgra
	tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRA_H[x]);
	if (tcnt == tgr) cycles2 = 0xFFFF + 1; else cycles2 = (UINT16)(tgr - tcnt);
	if ((cycles2 > 0) && (cycles2 < cycles1)) cycles1 = cycles2;
	// tgrb
	tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRB_H[x]);
	if (tcnt == tgr) cycles2 = 0xFFFF + 1; else cycles2 = (UINT16)(tgr - tcnt);
	if ((cycles2 > 0) && (cycles2 < cycles1)) cycles1 = cycles2;
	// tgrc
	if ((x == 0) || (x == 3))
	{
		tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRC_H[x]);
		if (tcnt == tgr) cycles2 = 0xFFFF + 1; else cycles2 = (UINT16)(tgr - tcnt);
		if ((cycles2 > 0) && (cycles2 < cycles1)) cycles1 = cycles2;
	}
	// tgrd
	if ((x == 0) || (x == 3))
	{
		tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRD_H[x]);
		if (tcnt == tgr) cycles2 = 0xFFFF + 1; else cycles2 = (UINT16)(tgr - tcnt);
		if ((cycles2 > 0) && (cycles2 < cycles1)) cycles1 = cycles2;
	}
	// done
	return cycles1;
}

void h8s_pin_write( h83xx_state *h8, int pin, int data)
{
	switch (pin)
	{
		case PIN_TIOCB1 :
		{
			UINT8 p1dr;
			p1dr = h8->per_regs[H8S_IO_P1DR];
			if (data) p1dr |= H8S_P1_TIOCB1; else p1dr &= ~H8S_P1_TIOCB1;
			h8->io->write_byte(H8S_IO_ADDR(H8S_IO_P1DR), p1dr);
			h8->per_regs[H8S_IO_P1DR] = p1dr;
		}
		break;
	}
}

int h8s_pin_read( h83xx_state *h8, int pin)
{
	switch (pin)
	{
		case PIN_TIOCB1 : return (h8->per_regs[H8S_IO_P1DR] & H8S_P1_TIOCB1) ? 1 : 0;
	}
	return 0;
}

void h8s_tioc_pin_update( h83xx_state *h8, int io, int pin)
{
	switch (io)
	{
		case 1 : h8s_pin_write( h8, pin, 0); break;
		case 2 : h8s_pin_write( h8, pin, 1); break;
		case 3 : h8s_pin_write( h8, pin, h8s_pin_read( h8, pin) ? 0 : 1); break;
		case 5 : h8s_pin_write( h8, pin, 0); break;
		case 6 : h8s_pin_write( h8, pin, 1); break;
		case 7 : h8s_pin_write( h8, pin, h8s_pin_read( h8, pin) ? 0 : 1); break;
	}
}

void h8s_tioc_pin_reset( h83xx_state *h8, int io, int pin)
{
	switch (io)
	{
		case 1 : h8s_pin_write( h8, pin, 0); break;
		case 2 : h8s_pin_write( h8, pin, 0); break;
		case 3 : h8s_pin_write( h8, pin, 0); break;
		case 5 : h8s_pin_write( h8, pin, 1); break;
		case 6 : h8s_pin_write( h8, pin, 1); break;
		case 7 : h8s_pin_write( h8, pin, 1); break;
	}
}

void h8s_tpu_x_tioc_reset( h83xx_state *h8, int x)
{
	UINT8 tior;
	int cclr;
	// ...
	cclr = (h8->per_regs[H8S_IO_TTCR[x]] >> 5) & TCR_CCLR_AND[x];
	// tioca & tiocb
	tior = h8->per_regs[H8S_IO_TIOR_H[x]];
	if (cclr != 1) h8s_tioc_pin_reset( h8, (tior >> 0) & 0x0F, PIN_TIOCA[x]);
	if (cclr != 2) h8s_tioc_pin_reset( h8, (tior >> 4) & 0x0F, PIN_TIOCB[x]);
	// tiocc & tiocd
	if ((x == 0) || (x == 3))
	{
		tior = h8->per_regs[H8S_IO_TIOR_L[x]];
		if (cclr != 5) h8s_tioc_pin_reset( h8, (tior >> 0) & 0x0F, PIN_TIOCC[x]);
		if (cclr != 6) h8s_tioc_pin_reset( h8, (tior >> 4) & 0x0F, PIN_TIOCD[x]);
	}
}

void h8s_tpu_x_overflow( h83xx_state *h8, int x)
{
	if (x == 2)
	{
		UINT8 tcr = h8->per_regs[H8S_IO_TTCR[1]];
		if (((tcr >> 0) & 7) == 7)
		{
			h8s_tpu_x_tick( h8, 1);
		}
	}
	if (x == 5)
	{
		UINT8 tcr = h8->per_regs[H8S_IO_TTCR[4]];
		if (((tcr >> 0) & 7) == 7)
		{
			h8s_tpu_x_tick( h8, 4);
		}
	}
}

UINT16 h8s_tpu_x_tcnt_change( h83xx_state *h8, int x, UINT16 tcnt)
{
	UINT16 tgr;
	UINT8 tcr, tier, tior;
	int clear = 0;
	// ...
	verboselog( h8->device->machine(), 9, "TPU %d - tpu_x_tcnt_change (%04X)\n", x, tcnt);
	// ...
	tcr = h8->per_regs[H8S_IO_TTCR[x]];
	tier = h8->per_regs[H8S_IO_TIER[x]];
	// overflow
	if (tcnt == 0)
	{
		verboselog( h8->device->machine(), 9, "TPU %d - overflow match\n", x);
		h8->per_regs[H8S_IO_TSR[x]] |= TSR_TCFV;
		if (tier & TIER_TCIEV)
		{
			h8s2xxx_interrupt_request( h8, H8S_INT_TCIV[x]);
		}
		h8s_tpu_x_overflow( h8, x);
	}
	// tgra
	tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRA_H[x]);
	if (tcnt == tgr)
	{
		verboselog( h8->device->machine(), 9, "TPU %d - tgra match\n", x);
		h8->per_regs[H8S_IO_TSR[x]] |= TSR_TGFA;
		if (tier & TIER_TGIEA)
		{
			h8s2xxx_interrupt_request( h8, H8S_INT_TGIA[x]);
		}
		if (((tcr >> 5) & TCR_CCLR_AND[x]) == 1) // CCLR 1 -> "TCNT cleared by TGRA compare match/input capture"
		{
			clear = 1;
		}
		else
		{
			tior = h8->per_regs[H8S_IO_TIOR_H[x]];
			h8s_tioc_pin_update( h8, (tior >> 0) & 0x0F, PIN_TIOCA[x]);
		}
	}
	// tgrb
	tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRB_H[x]);
	if (tcnt == tgr)
	{
		verboselog( h8->device->machine(), 9, "TPU %d - tgrb match\n", x);
		h8->per_regs[H8S_IO_TSR[x]] |= TSR_TGFB;
		if (tier & TIER_TGIEB)
		{
			h8s2xxx_interrupt_request( h8, H8S_INT_TGIB[x]);
		}
		if (((tcr >> 5) & TCR_CCLR_AND[x]) == 2) // CCLR 2 -> "TCNT cleared by TGRB compare match/input capture"
		{
			clear = 1;
		}
		else
		{
			tior = h8->per_regs[H8S_IO_TIOR_H[x]];
			h8s_tioc_pin_update( h8, (tior >> 4) & 0x0F, PIN_TIOCB[x]);
		}
	}
	// tgrc
	if ((x == 0) || (x == 3))
	{
		tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRC_H[x]);
		if (tcnt == tgr)
		{
			verboselog( h8->device->machine(), 9, "TPU %d - tgrc match\n", x);
			h8->per_regs[H8S_IO_TSR[x]] |= TSR_TGFC;
			if (tier & TIER_TGIEC)
			{
				h8s2xxx_interrupt_request( h8, H8S_INT_TGIC[x]);
			}
			if (((tcr >> 5) & TCR_CCLR_AND[x]) == 5) // CCLR 5 -> "TCNT cleared by TGRC compare match/input capture"
			{
				clear = 1;
			}
			else
			{
				tior = h8->per_regs[H8S_IO_TIOR_L[x]];
				h8s_tioc_pin_update( h8, (tior >> 0) & 0x0F, PIN_TIOCC[x]);
			}
		}
	}
	// tgrd
	if ((x == 0) || (x == 3))
	{
		tgr = ptr_read_16( h8->per_regs + H8S_IO_TGRD_H[x]);
		if (tcnt == tgr)
		{
			verboselog( h8->device->machine(), 9, "TPU %d - tgrd match\n", x);
			h8->per_regs[H8S_IO_TSR[x]] |= TSR_TGFD;
			if (tier & TIER_TGIED)
			{
				h8s2xxx_interrupt_request( h8, H8S_INT_TGID[x]);
			}
			if (((tcr >> 5) & TCR_CCLR_AND[x]) == 6) // CCLR 6 -> "TCNT cleared by TGRD compare match/input capture"
			{
				clear = 1;
			}
			else
			{
				tior = h8->per_regs[H8S_IO_TIOR_L[x]];
				h8s_tioc_pin_update( h8, (tior >> 4) & 0x0F, PIN_TIOCD[x]);
			}
		}
	}
	// ...
	if (clear)
	{
		verboselog( h8->device->machine(), 9, "TPU %d - tcnt clear\n", x);
		h8s_tpu_x_tioc_reset( h8, x);
		tcnt = 0;
	}
	// ...
	return tcnt;
}

void h8s_tpu_x_callback( h83xx_state *h8, int x)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	UINT64 cycles;
	UINT16 tcnt;
	// calculate tcnt
	tcnt = ptr_read_16( h8->per_regs + H8S_IO_TCNT_H[x]);
	tcnt = (UINT16)(tcnt + tpu->timer_cycles / tpu->cycles_per_tick);
	// ...
	tcnt = h8s_tpu_x_tcnt_change( h8, x, tcnt);
	// update tcnt
	ptr_write_16( h8->per_regs + H8S_IO_TCNT_H[x], tcnt);
	// update timer
	cycles = h8s_tpu_x_calculate_ticks( h8, x, tcnt) * tpu->cycles_per_tick;
	tpu->timer_cycles = cycles;
	tpu->timer->adjust(attotime::from_hz(h8->device->unscaled_clock() / cycles), x);
}

void h8s_tpu_x_tick( h83xx_state *h8, int x)
{
	UINT16 tcnt;
	// calculate tcnt
	tcnt = ptr_read_16( h8->per_regs + H8S_IO_TCNT_H[x]);
	tcnt = (UINT16)(tcnt + 1);
	// ...
	tcnt = h8s_tpu_x_tcnt_change( h8, x, tcnt);
	// update tcnt
	ptr_write_16( h8->per_regs + H8S_IO_TCNT_H[x], tcnt);
}

int h8s_tpu_x_get_div( h83xx_state *h8, int x)
{
	UINT8 tcr;
	int tpsc;
	const int *TPU_X_DIV[] = { TPU_0_DIV, TPU_1_DIV, TPU_2_DIV, TPU_3_DIV, TPU_4_DIV, TPU_5_DIV };
	tcr = h8->per_regs[H8S_IO_TTCR[x]];
	tpsc = (tcr >> 0) & 7;
	return TPU_X_DIV[x][tpsc];
}

void h8s_tpu_x_update( h83xx_state *h8, int x)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	// ...
	tpu->cycles_per_tick = h8s_tpu_x_get_div( h8, x);
	// update timer
	if (tpu->cycles_per_tick != 0)
	{
		UINT16 tcnt;
		UINT64 cycles;
		tcnt = ptr_read_16( h8->per_regs + H8S_IO_TCNT_H[x]);
		cycles = h8s_tpu_x_calculate_ticks( h8, x, tcnt) * tpu->cycles_per_tick;
		tpu->timer_cycles = cycles;
		tpu->timer->adjust(attotime::from_hz(h8->device->unscaled_clock() / cycles), x);
	}
	else
	{
		tpu->timer->reset();
	}
}

void h8s_tpu_x_start( h83xx_state *h8, int x)
{
	verboselog( h8->device->machine(), 5, "TPU %d - start\n", x);
	// ...
	h8s_tpu_x_update( h8, x);
}

void h8s_tpu_x_stop( h83xx_state *h8, int x)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	UINT16 tcnt;
	// ...
	verboselog( h8->device->machine(), 5, "TPU %d - stop\n", x);
	// calculate tcnt
	tcnt = h8s_tpu_x_calculate_tcnt( h8, x);
	// update tcnt
	ptr_write_16( h8->per_regs + H8S_IO_TCNT_H[x], tcnt);
	// disable timer
	tpu->timer->reset();
}

UINT16 h8s_tpu_x_read_tcnt( h83xx_state *h8, int x)
{
	return h8s_tpu_x_calculate_tcnt( h8, x);
}

void h8s_tpu_x_write_tcnt( h83xx_state *h8, int x, UINT16 data)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	verboselog( h8->device->machine(), 9, "TPU %d - write tcnt (%04X)\n", x, data);
	ptr_write_16( h8->per_regs + H8S_IO_TCNT_H[x], data);
	if (tpu->timer->enabled())
	{
		h8s_tpu_x_update( h8, x);
	}
}

void h8s_tpu_x_write_tgr( h83xx_state *h8, int x)
{
	H8S2XXX_TPU *tpu = &h8->tpu[x];
	if ((tpu->timer->enabled()) && (tpu->cycles_per_tick != 0))
	{
		UINT16 tcnt1, tcnt2;
		UINT64 cycles;
		tcnt1 = ptr_read_16( h8->per_regs + H8S_IO_TCNT_H[x]);
		tcnt2 = (UINT16)(tcnt1 + ((device_icount_min_timer_icount1( h8, tpu->timer)) / tpu->cycles_per_tick));
		cycles = h8s_tpu_x_calculate_ticks( h8, x, tcnt2) * tpu->cycles_per_tick;
		cycles = cycles + (tcnt2 - tcnt1) * tpu->cycles_per_tick;
		tpu->timer_cycles = cycles;
		tpu->timer->adjust(attotime::from_hz(h8->device->unscaled_clock() / cycles), x);
	}
}

void h8s_tpu_x_write_tgra( h83xx_state *h8, int x, UINT16 data)
{
	verboselog( h8->device->machine(), 9, "TPU %d - write tgra (%04X)\n", x, data);
	ptr_write_16( h8->per_regs + H8S_IO_TGRA_H[x], data);
	h8s_tpu_x_write_tgr( h8, x);
}

void h8s_tpu_x_write_tgrb( h83xx_state *h8, int x, UINT16 data)
{
	verboselog( h8->device->machine(), 9, "TPU %d - write tgrb (%04X)\n", x, data);
	ptr_write_16( h8->per_regs + H8S_IO_TGRB_H[x], data);
	h8s_tpu_x_write_tgr( h8, x);
}

void h8s_tpu_x_write_tgrc( h83xx_state *h8, int x, UINT16 data)
{
	verboselog( h8->device->machine(), 9, "TPU %d - write tgrc (%04X)\n", x, data);
	ptr_write_16( h8->per_regs + H8S_IO_TGRC_H[x], data);
	h8s_tpu_x_write_tgr( h8, x);
}

void h8s_tpu_x_write_tgrd( h83xx_state *h8, int x, UINT16 data)
{
	verboselog( h8->device->machine(), 9, "TPU %d - write tgrd (%04X)\n", x, data);
	ptr_write_16( h8->per_regs + H8S_IO_TGRD_H[x], data);
	h8s_tpu_x_write_tgr( h8, x);
}

void h8s_tpu_x_write_tior( h83xx_state *h8, int x)
{
	UINT8 tstr;
	tstr = h8->per_regs[H8S_IO_TSTR];
	if ((tstr & (1 << x)) == 0)
	{
		h8s_tpu_x_tioc_reset( h8, x);
	}
}

void h8s_tpu_0_write_tior_h( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR0_H] = data;
	h8s_tpu_x_write_tior( h8, 0);
}

void h8s_tpu_0_write_tior_l( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR0_L] = data;
	h8s_tpu_x_write_tior( h8, 0);
}

void h8s_tpu_1_write_tior( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR1] = data;
	h8s_tpu_x_write_tior( h8, 1);
}

void h8s_tpu_2_write_tior( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR2] = data;
	h8s_tpu_x_write_tior( h8, 2);
}

void h8s_tpu_3_write_tior_h( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR3_H] = data;
	h8s_tpu_x_write_tior( h8, 3);
}

void h8s_tpu_3_write_tior_l( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR3_L] = data;
	h8s_tpu_x_write_tior( h8, 3);
}

void h8s_tpu_4_write_tior( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR4] = data;
	h8s_tpu_x_write_tior( h8, 4);
}

void h8s_tpu_5_write_tior( h83xx_state *h8, UINT8 data)
{
	h8->per_regs[H8S_IO_TIOR5] = data;
	h8s_tpu_x_write_tior( h8, 5);
}

void h8s_tpu_write_tstr( h83xx_state *h8, UINT8 data)
{
	const int tpu_max = (h8->device->type() == H8S2323) ? 6 : 3;
	UINT8 old_data = h8->per_regs[H8S_IO_TSTR];
	int i;
	h8->per_regs[H8S_IO_TSTR] = data;
	for (i = 0; i < tpu_max; i++)
	{
		if ((data & (1 << i)) != (old_data & (1 << i)))
		{
			if ((data & (1 << i)) != 0)
			{
				h8s_tpu_x_start( h8, i);
			}
			else
			{
				h8s_tpu_x_stop( h8, i);
			}
		}
	}
}

static TIMER_CALLBACK( h8s_tpu_callback)
{
	h83xx_state *h8 = (h83xx_state *)ptr;
	h8s_tpu_x_callback( h8, param);
}

/////////////////////////////////
// SERIAL CONTROLLER INTERFACE //
/////////////////////////////////

typedef struct
{
	UINT32 reg_smr, reg_brr, reg_scr, reg_tdr, reg_ssr, reg_rdr;
	UINT32 reg_pdr, reg_port;
	UINT8 port_mask_sck, port_mask_txd, port_mask_rxd;
	UINT8 int_tx, int_rx;
} H8S_SCI_ENTRY;

const H8S_SCI_ENTRY H8S_SCI_TABLE[] =
{
	// SCI 0
	{
		H8S_IO_SMR0, H8S_IO_BRR0, H8S_IO_SCR0, H8S_IO_TDR0, H8S_IO_SSR0, H8S_IO_RDR0,
		H8S_IO_P3DR, H8S_IO_PORT3,
		H8S_P3_SCK0, H8S_P3_TXD0, H8S_P3_RXD0,
		H8S_INT_TXI0, H8S_INT_RXI0
	},
	// SCI 1
	{
		H8S_IO_SMR1, H8S_IO_BRR1, H8S_IO_SCR1, H8S_IO_TDR1, H8S_IO_SSR1, H8S_IO_RDR1,
		H8S_IO_P3DR, H8S_IO_PORT3,
		H8S_P3_SCK1, H8S_P3_TXD1, H8S_P3_RXD1,
		H8S_INT_TXI1, H8S_INT_RXI1
	},
	// SCI 2
	{
		H8S_IO_SMR2, H8S_IO_BRR2, H8S_IO_SCR2, H8S_IO_TDR2, H8S_IO_SSR2, H8S_IO_RDR2,
		H8S_IO_P5DR, H8S_IO_PORT5,
		H8S_P5_SCK2, H8S_P5_TXD2, H8S_P5_RXD2,
		H8S_INT_TXI2, H8S_INT_RXI2
	}
};

const H8S_SCI_ENTRY *h8s_sci_entry( int num)
{
	return &H8S_SCI_TABLE[num];
}

void h8s_sci_init(h83xx_state *h8)
{
	int i;
	for (i=0;i<3;i++)
	{
		h8->sci[i].timer = h8->device->machine().scheduler().timer_alloc(FUNC(h8s_sci_callback), h8);
		h8->sci[i].timer->adjust(attotime::never, i);
	}
}

static TIMER_CALLBACK(h8s_sci_callback)
{
	h83xx_state *h8 = (h83xx_state *)ptr;
	verboselog( h8->device->machine(), 7, "h8s_sci_callback (%d)\n", param);
}

void h8s_sci_start(h83xx_state *h8, int num)
{
	#if 0
	h8->sci[num].timer->adjust(h8->sci[num].bitrate, num);
	#endif
}

void h8s_sci_stop(h83xx_state *h8, int num)
{
	h8->sci[num].timer->adjust(attotime::never, num);
}

void h8s_sci_execute(h83xx_state *h8, int num)
{
	UINT8 scr, tdr, ssr, rdr, tsr, rsr, pdr, port;
	int i;
	const H8S_SCI_ENTRY *info = h8s_sci_entry( num);
	verboselog( h8->device->machine(), 5, "h8s_sci_execute(%d)\n", num);
	// load regs
	scr = h8->per_regs[info->reg_scr];
	tdr = h8->per_regs[info->reg_tdr];
	ssr = h8->per_regs[info->reg_ssr];
	rdr = h8->per_regs[info->reg_rdr];
	tsr = 0;
	rsr = 0;
	pdr = h8->per_regs[info->reg_pdr] & (~info->port_mask_sck);
	// move byte from TDR to TSR
	if (scr & H8S_SCR_TE)
	{
		tsr = tdr;
		ssr |= H8S_SSR_TDRE;
	}
	// generate transmit data empty interrupt
	if ((scr & H8S_SCR_TIE) && (ssr & H8S_SSR_TDRE)) h8s2xxx_interrupt_request(h8, info->int_tx);
	// transmit/receive bits
	for (i=0;i<8;i++)
	{
		// write bit
		if (scr & H8S_SCR_TE)
		{
			if (tsr & (1 << i)) pdr = pdr | info->port_mask_txd; else pdr = pdr & (~info->port_mask_txd);
			h8->io->write_byte( H8S_IO_ADDR( info->reg_pdr), pdr);
		}
		// clock high to low
		h8->io->write_byte( H8S_IO_ADDR( info->reg_pdr), pdr | info->port_mask_sck);
		h8->io->write_byte( H8S_IO_ADDR( info->reg_pdr), pdr);
		// read bit
		if (scr & H8S_SCR_RE)
		{
			port = h8->io->read_byte(H8S_IO_ADDR( info->reg_port));
			if (port & info->port_mask_rxd) rsr = rsr | (1 << i);
		}
	}
	// move byte from RSR to RDR
	if (scr & H8S_SCR_RE)
	{
		rdr = rsr;
		//ssr |= H8S_SSR_RDRF;
	}
	// generate receive data full interrupt
	if ((scr & H8S_SCR_RIE) && (ssr & H8S_SSR_RDRF)) h8s2xxx_interrupt_request(h8, info->int_rx);
	// save regs
	h8->per_regs[info->reg_scr] = scr;
	h8->per_regs[info->reg_tdr] = tdr;
	h8->per_regs[info->reg_ssr] = ssr;
	h8->per_regs[info->reg_rdr] = rdr;
}

const double SCR_CKE[] = { 0.5, 2, 8, 32 }; // = 2 ^ ((2 * cke) - 1)
const int SMR_MODE[] = { 64, 8 };

void h8s_sci_rate(h83xx_state *h8, int num)
{
	UINT8 brr, scr, smr, cke, mode;
	UINT32 bitrate;
	const H8S_SCI_ENTRY *info = h8s_sci_entry( num);
	// read regs
	brr = h8->per_regs[info->reg_brr];
	scr = h8->per_regs[info->reg_scr];
	smr = h8->per_regs[info->reg_smr];
	verboselog( h8->device->machine(), 2, "BRR %02X SCR %02X SMR %02X\n", brr, scr, smr);
	// calculate bitrate
	cke = (scr >> 0) & 3;
	mode = (smr >> 7) & 1;
	bitrate = (UINT32)((h8->device->unscaled_clock() / (brr + 1)) / (SMR_MODE[mode] * SCR_CKE[cke]));
	verboselog( h8->device->machine(), 2, "SCI%d bitrate %d\n", num, bitrate);
	// store bitrate
	h8->sci[num].bitrate = bitrate;
}

////////////////////////////
// INTERNAL I/O REGISTERS //
////////////////////////////

void h8s_onchip_reg_write_8(h83xx_state *h8, int offset, UINT8 data)
{
	verboselog( h8->device->machine(), 9, "%08X | %08X <- %02X\n", h8->ppc, H8S_IO_ADDR(offset), data);
	switch (offset)
	{
		// SCI 0
		case H8S_IO_SSR0  : h8->per_regs[offset] = data; if ((data & H8S_SSR_TDRE) == 0) h8s_sci_execute(h8, 0); break;
		case H8S_IO_SCR0  : h8->per_regs[offset] = data; if (data & H8S_SCR_TIE) h8s2xxx_interrupt_request(h8, h8s_sci_entry(0)->int_tx); break;
		case H8S_IO_BRR0  : h8->per_regs[offset] = data; h8s_sci_rate(h8, 0); h8s_sci_start(h8, 0); break;
		// SCI 1
		case H8S_IO_SSR1  : h8->per_regs[offset] = data; if ((data & H8S_SSR_TDRE) == 0) h8s_sci_execute(h8, 1); break;
		case H8S_IO_SCR1  :	h8->per_regs[offset] = data; if (data & H8S_SCR_RIE) h8s2xxx_interrupt_request(h8,  h8s_sci_entry(1)->int_rx); break;
		case H8S_IO_BRR1  : h8->per_regs[offset] = data; h8s_sci_rate(h8, 1); h8s_sci_start(h8, 1); break;
		// SCI 2
		case H8S_IO_SSR2  : h8->per_regs[offset] = data; if ((data & H8S_SSR_TDRE) == 0) h8s_sci_execute(h8, 2); break;
		case H8S_IO_SCR2  : h8->per_regs[offset] = data; if (data & H8S_SCR_TIE) h8s2xxx_interrupt_request(h8,  h8s_sci_entry(2)->int_tx); break;
		case H8S_IO_BRR2  : h8->per_regs[offset] = data; h8s_sci_rate(h8, 2); h8s_sci_start(h8, 2); break;
		// TMR 0
		case H8S_IO_TCR0   : h8s_tmr_x_write_tcr( h8, 0, data); break;
		case H8S_IO_TCNT0  : h8s_tmr_x_write_tcnt( h8, 0, data); break;
		case H8S_IO_TCORA0 : h8s_tmr_x_write_tcora( h8, 0, data); break;
		case H8S_IO_TCORB0 : h8s_tmr_x_write_tcorb( h8, 0, data); break;
		// TMR 1
		case H8S_IO_TCR1   : h8s_tmr_x_write_tcr( h8, 1, data); break;
		case H8S_IO_TCNT1  : h8s_tmr_x_write_tcnt( h8, 1, data); break;
		case H8S_IO_TCORA1 : h8s_tmr_x_write_tcora( h8, 1, data); break;
		case H8S_IO_TCORB1 : h8s_tmr_x_write_tcorb( h8, 1, data); break;
		// ports
		case H8S_IO_P1DR : case H8S_IO_P2DR : case H8S_IO_P3DR : case H8S_IO_P4DR : case H8S_IO_P5DR :
		case H8S_IO_PADR : case H8S_IO_PBDR : case H8S_IO_PCDR : case H8S_IO_PDDR : case H8S_IO_PEDR :
		case H8S_IO_PFDR : h8->per_regs[offset] = data; h8->io->write_byte( H8S_IO_ADDR( offset), data); break;
		// ...
		case H8S_IO_PFDDR : h8->per_regs[offset] = data; h8->io->write_byte( H8S_IO_ADDR( offset), data); break;
		// TPU
		case H8S_IO_TSTR  : h8s_tpu_write_tstr( h8, data); break;
		// DMA
		case H8S_IO_DMABCRL :
		{
			h8->per_regs[offset] = data;
			if (h8->device->type() == H8S2323)
			{
				if ((data & 0x40) && (data & 0x80))
				{
					UINT32 i, dma_src, dma_dst;
					UINT16 dma_cnt, dma_con;
					int sz;
					dma_src = h8->program->read_dword( H8S_IO_ADDR(H8S_IO_MAR1AH));
					dma_dst = h8->program->read_dword( H8S_IO_ADDR(H8S_IO_MAR1BH));
					dma_cnt = h8->program->read_word( H8S_IO_ADDR(H8S_IO_ETCR1A));
					dma_con = h8->program->read_word( H8S_IO_ADDR(H8S_IO_DMACR1A));
					sz = (dma_con & 0x8000) ? 2 : 1;
					for (i=0;i<dma_cnt;i++)
					{
						if (dma_con & 0x8000) h8->program->write_word( dma_dst, h8->program->read_word( dma_src)); else h8->program->write_byte( dma_dst, h8->program->read_byte( dma_src));
						if (dma_con & 0x2000) { if (dma_con & 0x4000) dma_src -= sz; else dma_src += sz; }
						if (dma_con & 0x0020) { if (dma_con & 0x0040) dma_dst -= sz; else dma_dst += sz; }
					}
					h8->per_regs[H8S_IO_DMABCRL] &= ~0x40;
				}
			}
		}
		break;
		// ...
		default :
		{
			h8->per_regs[offset] = data;
		}
		break;
	}
}

void h8s_onchip_reg_write_16(h83xx_state *h8, int offset, UINT16 data)
{
	verboselog( h8->device->machine(), 9, "%08X | %08X <- %04X\n", h8->ppc, H8S_IO_ADDR(offset), data);
	switch (offset)
	{
		// TPU 0
		case H8S_IO_TCNT0_H : h8s_tpu_x_write_tcnt( h8, 0, data); break;
		case H8S_IO_TGR0A_H : h8s_tpu_x_write_tgra( h8, 0, data); break;
		case H8S_IO_TGR0B_H : h8s_tpu_x_write_tgrb( h8, 0, data); break;
		case H8S_IO_TGR0C_H : h8s_tpu_x_write_tgrc( h8, 0, data); break;
		case H8S_IO_TGR0D_H : h8s_tpu_x_write_tgrd( h8, 0, data); break;
		// TPU 1
		case H8S_IO_TCNT1_H : h8s_tpu_x_write_tcnt( h8, 1, data); break;
		case H8S_IO_TGR1A_H : h8s_tpu_x_write_tgra( h8, 1, data); break;
		case H8S_IO_TGR1B_H : h8s_tpu_x_write_tgrb( h8, 1, data); break;
		// TPU 2
		case H8S_IO_TCNT2_H : h8s_tpu_x_write_tcnt( h8, 2, data); break;
		case H8S_IO_TGR2A_H : h8s_tpu_x_write_tgra( h8, 2, data); break;
		case H8S_IO_TGR2B_H : h8s_tpu_x_write_tgrb( h8, 2, data); break;
		// TPU 3
		case H8S_IO_TCNT3_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tcnt( h8, 3, data); break;
		case H8S_IO_TGR3A_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgra( h8, 3, data); break;
		case H8S_IO_TGR3B_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgrb( h8, 3, data); break;
		case H8S_IO_TGR3C_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgrc( h8, 3, data); break;
		case H8S_IO_TGR3D_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgrd( h8, 3, data); break;
		// TPU 4
		case H8S_IO_TCNT4_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tcnt( h8, 4, data); break;
		case H8S_IO_TGR4A_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgra( h8, 4, data); break;
		case H8S_IO_TGR4B_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgrb( h8, 4, data); break;
		// TPU 5
		case H8S_IO_TCNT5_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tcnt( h8, 5, data); break;
		case H8S_IO_TGR5A_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgra( h8, 5, data); break;
		case H8S_IO_TGR5B_H : if (h8->device->type() == H8S2323) h8s_tpu_x_write_tgrb( h8, 5, data); break;
		// ...
		default :
		{
			h8s_onchip_reg_write_8( h8, offset + 0, (data >> 8) & 0xFF);
			h8s_onchip_reg_write_8( h8, offset + 1, (data >> 0) & 0xFF);
		}
		break;
	}
}

UINT8 h8s_onchip_reg_read_8(h83xx_state *h8, int offset)
{
	UINT8 data;
	switch (offset)
	{
		// SCI 0
		case H8S_IO_SSR0  : data = H8S_SSR_TDRE | H8S_SSR_TEND; break;
		case H8S_IO_RDR0  : data = h8->io->read_byte( H8S_IO_ADDR( offset)); break;
		// SCI 1
		case H8S_IO_SSR1  : data = H8S_SSR_TDRE | H8S_SSR_TEND; break;
		// SCI 2
		case H8S_IO_SSR2 :
		{
			data = h8->per_regs[offset];
			if (!(h8->per_regs[H8S_IO_SCR2] & H8S_SCR_TE)) data |= H8S_SSR_TDRE;
		}
		break;
		// ports
		case H8S_IO_PORT1 : case H8S_IO_PORT2 : case H8S_IO_PORT3 : case H8S_IO_PORT4 : case H8S_IO_PORT5 :
		case H8S_IO_PORTA : case H8S_IO_PORTB : case H8S_IO_PORTC : case H8S_IO_PORTD : case H8S_IO_PORTE :
		case H8S_IO_PORTF : data = h8->io->read_byte( H8S_IO_ADDR( offset)); break;
		// ...
		case H8S_IO_P3DR  : data = 0; break; // todo: without this cybiko hangs
		// TMR 0
		case H8S_IO_TCNT0 : data = h8s_tmr_x_read_tcnt( h8, 0); break;
		// TMR 1
		case H8S_IO_TCNT1 : data = h8s_tmr_x_read_tcnt( h8, 1); break;
		// ...
		// default
		default : data = h8->per_regs[offset]; break;
	}
	verboselog( h8->device->machine(), 9, "%08X | %08X -> %02X\n", h8->ppc, H8S_IO_ADDR(offset), data);
	return data;
}

UINT16 h8s_onchip_reg_read_16(h83xx_state *h8, int offset)
{
	UINT16 data = 0;
	switch (offset)
	{
		case H8S_IO_TCNT0_H : data = h8s_tpu_x_read_tcnt( h8, 0); break;
		case H8S_IO_TCNT1_H : data = h8s_tpu_x_read_tcnt( h8, 1); break;
		case H8S_IO_TCNT2_H : data = h8s_tpu_x_read_tcnt( h8, 2); break;
		case H8S_IO_TCNT3_H : if (h8->device->type() == H8S2323) data = h8s_tpu_x_read_tcnt( h8, 3); break;
		case H8S_IO_TCNT4_H : if (h8->device->type() == H8S2323) data = h8s_tpu_x_read_tcnt( h8, 4); break;
		case H8S_IO_TCNT5_H : if (h8->device->type() == H8S2323) data = h8s_tpu_x_read_tcnt( h8, 5); break;
		default :
		{
			UINT8 b[2];
			b[0] = h8s_onchip_reg_read_8( h8, offset + 0);
			b[1] = h8s_onchip_reg_read_8( h8, offset + 1);
			data = (b[0] << 8) | (b[1] << 0);
		}
		break;
	}
	verboselog( h8->device->machine(), 9, "%08X | %08X -> %04X\n", h8->ppc, H8S_IO_ADDR(offset), data);
	return data;
}

void h8s2241_per_regs_write_8(h83xx_state *h8, int offset, UINT8 data)
{
	h8s_onchip_reg_write_8(h8, offset, data);
}

UINT8 h8s2241_per_regs_read_8(h83xx_state *h8, int offset)
{
	return h8s_onchip_reg_read_8(h8, offset);
}

void h8s2241_per_regs_write_16(h83xx_state *h8, int offset, UINT16 data)
{
	h8s_onchip_reg_write_16(h8, offset, data);
}

UINT16 h8s2241_per_regs_read_16(h83xx_state *h8, int offset)
{
	return h8s_onchip_reg_read_16(h8, offset);
}

void h8s2246_per_regs_write_8(h83xx_state *h8, int offset, UINT8 data)
{
	h8s_onchip_reg_write_8(h8, offset, data);
}

UINT8 h8s2246_per_regs_read_8(h83xx_state *h8, int offset)
{
	return h8s_onchip_reg_read_8(h8, offset);
}

void h8s2246_per_regs_write_16(h83xx_state *h8, int offset, UINT16 data)
{
	h8s_onchip_reg_write_16(h8, offset, data);
}

UINT16 h8s2246_per_regs_read_16(h83xx_state *h8, int offset)
{
	return h8s_onchip_reg_read_16(h8, offset);
}

void h8s2323_per_regs_write_8(h83xx_state *h8, int offset, UINT8 data)
{
	h8s_onchip_reg_write_8(h8, offset, data);
}

UINT8 h8s2323_per_regs_read_8(h83xx_state *h8, int offset)
{
	return h8s_onchip_reg_read_8(h8, offset);
}

void h8s2323_per_regs_write_16(h83xx_state *h8, int offset, UINT16 data)
{
	h8s_onchip_reg_write_16(h8, offset, data);
}

UINT16 h8s2323_per_regs_read_16(h83xx_state *h8, int offset)
{
	return h8s_onchip_reg_read_16(h8, offset);
}
