summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/tms9901.h
blob: 6f7be85bb1e22571f9e48b60a69cea1e8f5f2d7a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/****************************************************************************

    TMS9901 Programmable System Interface
    See tms9901.c for documentation

    Raphael Nabet
    Michael Zapf

    February 2012: Rewritten as class

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

#ifndef __TMS9901_H__
#define __TMS9901_H__

#include "emu.h"

extern const device_type TMS9901;

/***************************************************************************
    MACROS
***************************************************************************/

/* Masks for the interrupts levels available on TMS9901 */
#define TMS9901_INT1 0x0002
#define TMS9901_INT2 0x0004
#define TMS9901_INT3 0x0008		// overriden by the timer interrupt
#define TMS9901_INT4 0x0010
#define TMS9901_INT5 0x0020
#define TMS9901_INT6 0x0040
#define TMS9901_INT7 0x0080
#define TMS9901_INT8 0x0100
#define TMS9901_INT9 0x0200
#define TMS9901_INTA 0x0400
#define TMS9901_INTB 0x0800
#define TMS9901_INTC 0x1000
#define TMS9901_INTD 0x2000
#define TMS9901_INTE 0x4000
#define TMS9901_INTF 0x8000

enum
{
	TMS9901_CB_INT7 = 0,
	TMS9901_INT8_INT15 = 1,
	TMS9901_P0_P7 = 2,
	TMS9901_P8_P15 = 3
};

/***************************************************************************
    CLASS DEFINITION
***************************************************************************/

struct tms9901_interface 
{
	int 				interrupt_mask;			// a bit for each input pin whose state is always notified to the TMS9901 core
	devcb_read8 		read_handler;			// 4*8 bits, to be selected using the offset (0-3)
	devcb_write_line	write_handler[16];		// 16 Pn outputs
	devcb_write8		interrupt_callback; 	// called when interrupt bus state changes
};

class tms9901_device : public device_t
{
public:
	tms9901_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);

	void set_single_int(int pin_number, int state);

	DECLARE_READ8_MEMBER( read );
	DECLARE_WRITE8_MEMBER( write );

private:
	static const device_timer_id DECREMENTER = 0;

	void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
	void timer_reload(void);
	void field_interrupts(void);

	void device_start(void);
	void device_stop(void);
	void device_reset(void);

	/* interrupt registers */
	// mask:  bit #n is set if pin #n is supported as an interrupt pin,
	// i.e. the driver sends a notification whenever the pin state changes
	// setting these bits is not required, but it saves you the trouble of
	// saving the state of interrupt pins and feeding it to the port read
	// handlers again
	int m_supported_int_mask;
	int m_int_state;			// state of the int1-int15 lines (must be inverted when queried)
	int m_old_int_state;		// stores the previous value to avoid useless INT line assertions
	int m_enabled_ints;			// interrupt enable mask

	bool m_int_pending;			// status of the int* pin (connected to TMS9900)
	bool m_timer_int_pending;	// timer int pending (overrides int3 pin if timer enabled)

	// PIO registers
	int m_pio_direction;		// direction register for PIO

	// current PIO output (to be masked with pio_direction)
	int m_pio_output;

	// mirrors used for INT7*-INT15*
	int m_pio_direction_mirror;
	int m_pio_output_mirror;

	// =======================================================================

	// TMS9901 clock mode
	// false = so-called interrupt mode (read interrupt state, write interrupt enable mask)
	// true = clock mode (read/write clock interval)
	bool m_clock_mode;

	// MESS timer, used to emulate the decrementer register
	emu_timer *m_decrementer;

	// clock interval, loaded in decrementer when it reaches 0.
	// 0 means decrementer off
	int m_clock_register;

	// Current decrementer value
	int m_decrementer_value;

	// when we go into timer mode, the decrementer is copied there to allow to read it reliably
	int m_clock_read_register;

	// =======================================================================

	// Callbacks
	devcb_resolved_read8		m_read_block;
	devcb_resolved_write_line	m_write_line[16];
	devcb_resolved_write8		m_interrupt;  // also delivers the interrupt level
};

/***************************************************************************
    DEVICE CONFIGURATION MACROS
***************************************************************************/

#define MCFG_TMS9901_ADD(_tag, _intrf, _rate) \
	MCFG_DEVICE_ADD(_tag, TMS9901, _rate) \
	MCFG_DEVICE_CONFIG(_intrf)

#endif /* __TMS9901_H__ */