summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/tms9901.h
blob: a962ceb28f7a96031cbb79af91451ddb7efe323b (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
// 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 MAME_MACHINE_TMS9901_H
#define MAME_MACHINE_TMS9901_H


DECLARE_DEVICE_TYPE(TMS9901, tms9901_device)

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

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

class tms9901_device : public device_t
{
public:
	// I/O pins
	enum
	{
		INT1=1,
		INT2,
		INT3,
		INT4,
		INT5,
		INT6,
		INT7_P15,
		INT8_P14,
		INT9_P13,
		INT10_P12,
		INT11_P11,
		INT12_P10,
		INT13_P9,
		INT14_P8,
		INT15_P7,
		P0,
		P1,
		P2,
		P3,
		P4,
		P5,
		P6
	} pins;

	tms9901_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	void set_int_line(int pin_number, int state);

	DECLARE_WRITE_LINE_MEMBER( rst1_line );

	// Synchronous clock input
	DECLARE_WRITE_LINE_MEMBER( phi_line );

	DECLARE_READ8_MEMBER( read );
	DECLARE_WRITE8_MEMBER( write );

	auto p_out_cb(int n) { return m_write_p[n].bind(); }
	auto read_cb() { return m_read_port.bind(); }
	auto intreq_cb() { return m_interrupt.bind(); }

	// Pins IC3...IC0
	// When no interrupt is active, the IC lines are all set to 1
	// The difference to /INT15 is that INTREQ is cleared.
	int get_int_level() { return m_int_pending? m_int_level : 15; }

	// Return PIO all outputs; ports configured as inputs return 1
	// Used by si5500
	uint16_t pio_outputs() const { return m_pio_output | ~m_pio_direction; }

	void set_poll_int_lines(bool poll) { m_poll_lines = poll; }

private:
	static constexpr device_timer_id DECREMENTER = 0;

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

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

	// Common method for device_reset and rst1_line
	void do_reset();

	// Common clock handling
	void timer_clock_in(line_state clk);

	// Direction and value of P pins
	bool is_output(int p);
	bool output_value(int p);
	void set_and_latch_output(int p, bool val);
	void set_bit(uint16_t& bitfield, int pos, bool val);

	int m_int_level;
	int m_last_level;

	// 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 on the caller's side.
	uint16_t m_int_line;
	uint16_t m_int_mask;
	bool m_int_pending;
	bool m_poll_lines;

	// Find the interrupt with the lowest level (most important)
	void prioritize_interrupts();

	// Outgoing INTREQ* line
	void signal_int();

	// Sample the interrupt inputs.
	void sample_interrupt_inputs();

	// P15..P0; 1=output H, 0=output L
	uint16_t m_pio_output;

	// For P15..P0; 1=output, 0=input
	// Once set to 1, a reset is necessary to return the pin to input
	uint16_t m_pio_direction;

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

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

	// Clock divider
	int  m_clockdiv;

	// Clock has reached 0
	bool m_timer_int_pending;

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

	// Clock interval, loaded in decrementer when it reaches 0.
	uint16_t m_clock_register;

	// Current decrementer value
	uint16_t m_decrementer_value;

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

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

	// Read callback.
	devcb_read8        m_read_port;

	// I/O lines, used for output. When used as inputs, the levels are delivered via the m_read_block
	devcb_write_line   m_write_p[16];

	// INTREQ pin
	devcb_write_line   m_interrupt;
};

#endif // MAME_MACHINE_TMS9901_H