summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/tms9901.h
blob: bd0700aaa71acc245492d8b8f9778d9f2a76081f (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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// license:BSD-3-Clause
// copyright-holders:Michael Zapf
/****************************************************************************

    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     // overridden 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
***************************************************************************/

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

	void set_single_int(int pin_number, int state);

	DECLARE_READ8_MEMBER( read );
	DECLARE_WRITE8_MEMBER( write );

	template<class _Object> static devcb_base &static_set_readblock_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_read_block.set_callback(object); }

	template<class _Object> static devcb_base &static_set_p0_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p0.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p1_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p1.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p2_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p2.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p3_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p3.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p4_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p4.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p5_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p5.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p6_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p6.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p7_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p7.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p8_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p8.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p9_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p9.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p10_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p10.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p11_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p11.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p12_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p12.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p13_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p13.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p14_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p14.set_callback(object); }
	template<class _Object> static devcb_base &static_set_p15_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_write_p15.set_callback(object); }

	template<class _Object> static devcb_base &static_set_intlevel_callback(device_t &device, _Object object)  { return downcast<tms9901_device &>(device).m_interrupt.set_callback(object); }

private:
	static const device_timer_id DECREMENTER = 0;

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

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

	// State of the INT1-INT15 lines (must be inverted when queried)
	// Note that the levels must also be delivered when reading the pins, which
	// may require to latch the int levels.
	int m_int_state;
	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;

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

	// Read callback.
	devcb_read8        m_read_block;

	// I/O lines, used for output. When used as inputs, the levels are delivered via the m_read_block
	devcb_write_line   m_write_p0;
	devcb_write_line   m_write_p1;
	devcb_write_line   m_write_p2;
	devcb_write_line   m_write_p3;
	devcb_write_line   m_write_p4;
	devcb_write_line   m_write_p5;
	devcb_write_line   m_write_p6;
	devcb_write_line   m_write_p7;
	devcb_write_line   m_write_p8;
	devcb_write_line   m_write_p9;
	devcb_write_line   m_write_p10;
	devcb_write_line   m_write_p11;
	devcb_write_line   m_write_p12;
	devcb_write_line   m_write_p13;
	devcb_write_line   m_write_p14;
	devcb_write_line   m_write_p15;

	// The invocation corresponds to the INTREQ signal (with the level passed as data)
	// and the address delivers the interrupt level (0-15)
	devcb_write8       m_interrupt;
};

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

#define MCFG_TMS9901_READBLOCK_HANDLER( _read ) \
	devcb = &tms9901_device::static_set_readblock_callback( *device, DEVCB_##_read );

#define MCFG_TMS9901_P0_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p0_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P1_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p1_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P2_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p2_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P3_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p3_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P4_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p4_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P5_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p5_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P6_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p6_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P7_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p7_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P8_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p8_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P9_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p9_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P10_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p10_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P11_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p11_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P12_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p12_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P13_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p13_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P14_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p14_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_P15_HANDLER( _write ) \
	devcb = &tms9901_device::static_set_p15_callback( *device, DEVCB_##_write );

#define MCFG_TMS9901_INTLEVEL_HANDLER( _intlevel ) \
	devcb = &tms9901_device::static_set_intlevel_callback( *device, DEVCB_##_intlevel );

#endif /* __TMS9901_H__ */