summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/ti99_peb/tn_ide.cpp
blob: f9e16d8eef74214cc5a03c165bb8719af18397d0 (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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/****************************************************************************

    Thierry Nouspikel's IDE card emulation

    This card is just a prototype.  It has been designed by Thierry Nouspikel,
    and its description was published in 2001.  The card has been revised in
    2004.

    The specs have been published in <http://www.nouspikel.com/ti99/ide.html>.

    The IDE interface is quite simple, since it only implements PIO transfer.
    The card includes a clock chip to timestamp files, and an SRAM for the DSR.
    It should be possible to use a battery backed DSR SRAM, but since the clock
    chip includes 4kb of battery-backed RAM, a bootstrap loader can be saved in
    the clock SRAM in order to load the DSR from the HD when the computer
    starts.

    Raphael Nabet, 2002-2004.

    Michael Zapf
    September 2010: Rewritten as device
    February 2012: Rewritten as class

    FIXME: Completely untested and likely to be broken

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

#include "emu.h"
#include "peribox.h"
#include "machine/ataintf.h"
#include "tn_ide.h"

#define CRU_BASE 0x1000

#define BUFFER_TAG "ram"

/* previously 0xff */
#define PAGE_MASK 0x3f

enum
{
	cru_reg_page_switching = 0x04,
	cru_reg_page_0 = 0x08,
	/*cru_reg_rambo = 0x10,*/   /* not emulated */
	cru_reg_wp = 0x20,
	cru_reg_int_en = 0x40,
	cru_reg_reset = 0x80
};

nouspikel_ide_interface_device::nouspikel_ide_interface_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: ti_expansion_card_device(mconfig, TI99_IDE, "Nouspikel IDE interface card", tag, owner, clock, "ti99_ide", __FILE__), m_ata_irq(false),
	m_cru_register(0), m_rtc(nullptr),
	m_ata(*this, "ata"), m_clk_irq(false), m_sram_enable(false), m_sram_enable_dip(false), m_cur_page(0), m_tms9995_mode(false),
	m_input_latch(0), m_output_latch(0), m_ram(nullptr)
{
}

/*
    CRU read
*/
READ8Z_MEMBER(nouspikel_ide_interface_device::crureadz)
{
	UINT8 reply = 0;
	if ((offset & 0xff00)==m_cru_base)
	{
		int bit = (offset >> 4) & 7;

		if (bit==0)
		{
			reply = m_cru_register & 0x30;
			reply |= 8; /* IDE bus IORDY always set */
			if (!m_clk_irq)
				reply |= 4;
			if (m_sram_enable_dip)
				reply |= 2;
			if (!m_ata_irq)
				reply |= 1;
		}
		*value = reply;
	}
}

/*
    CRU write
*/
WRITE8_MEMBER(nouspikel_ide_interface_device::cruwrite)
{
	if ((offset & 0xff00)==m_cru_base)
	{
		int bit = (offset >>1) & 7;
		switch (bit)
		{
		case 0:
			m_selected = (data!=0);

		case 1:         // enable SRAM or registers in 0x4000-0x40ff
			m_sram_enable = (data!=0);
			break;

		case 2:         // enable SRAM page switching
		case 3:         // force SRAM page 0
		case 4:         // enable SRAM in 0x6000-0x7000 ("RAMBO" mode)
		case 5:         // write-protect RAM
		case 6:         // irq and reset enable
		case 7:         // reset drive
			if (data!=0)
				m_cru_register |= 1 << bit;
			else
				m_cru_register &= ~(1 << bit);

			if (bit == 6)
				m_slot->set_inta((m_cru_register & cru_reg_int_en) && m_ata_irq);

			if ((bit == 6) || (bit == 7))
				if ((m_cru_register & cru_reg_int_en) && !(m_cru_register & cru_reg_reset))
					m_ata->reset();
			break;
		}
	}
}

/*
    Memory read
*/
READ8Z_MEMBER(nouspikel_ide_interface_device::readz)
{
	UINT8 reply = 0;
	if (space.debugger_access()) return;

	if (((offset & m_select_mask)==m_select_value) && m_selected)
	{
		int addr = offset & 0x1fff;

		if ((addr <= 0xff) && (m_sram_enable == m_sram_enable_dip))
		{   /* registers */
			switch ((addr >> 5) & 0x3)
			{
			case 0:     /* RTC RAM */
				if (addr & 0x80)
					/* RTC RAM page register */
					reply = m_rtc->xram_r(machine().driver_data()->generic_space(),(addr & 0x1f) | 0x20);
				else
					/* RTC RAM read */
					reply = m_rtc->xram_r(machine().driver_data()->generic_space(),addr);
				break;
			case 1:     /* RTC registers */
				if (addr & 0x10)
					/* register data */
					reply = m_rtc->rtc_r(machine().driver_data()->generic_space(),1);
				else
					/* register select */
					reply = m_rtc->rtc_r(machine().driver_data()->generic_space(),0);
				break;
			case 2:     /* IDE registers set 1 (CS1Fx) */
				if (m_tms9995_mode ? (!(addr & 1)) : (addr & 1))
				{   /* first read triggers 16-bit read cycle */
					m_input_latch = (! (addr & 0x10)) ? m_ata->read_cs0(space, (addr >> 1) & 0x7, 0xffff) : 0;
				}

				/* return latched input */
				/*reply = (addr & 1) ? input_latch : (input_latch >> 8);*/
				/* return latched input - bytes are swapped in 2004 IDE card */
				reply = ((addr & 1) ? (m_input_latch >> 8) : m_input_latch) & 0xff;
				break;
			case 3:     /* IDE registers set 2 (CS3Fx) */
				if (m_tms9995_mode ? (!(addr & 1)) : (addr & 1))
				{   /* first read triggers 16-bit read cycle */
					m_input_latch = (! (addr & 0x10)) ? m_ata->read_cs1(space, (addr >> 1) & 0x7, 0xffff) : 0;
				}

				/* return latched input */
				/*reply = (addr & 1) ? input_latch : (input_latch >> 8);*/
				/* return latched input - bytes are swapped in 2004 IDE card */
				reply = ((addr & 1) ? (m_input_latch >> 8) : m_input_latch) & 0xff;
				break;
			}
		}
		else
		{   /* sram */
			if ((m_cru_register & cru_reg_page_0) || (addr >= 0x1000))
				reply = m_ram[addr+0x2000 * m_cur_page];
			else
				reply = m_ram[addr];
		}
		*value = reply;
	}
}

/*
    Memory write. The controller is 16 bit, so we need to demultiplex again.
*/
WRITE8_MEMBER(nouspikel_ide_interface_device::write)
{
	if (space.debugger_access()) return;

	if (((offset & m_select_mask)==m_select_value) && m_selected)
	{
		if (m_cru_register & cru_reg_page_switching)
		{
			m_cur_page = (offset >> 1) & PAGE_MASK;
		}

		int addr = offset & 0x1fff;

		if ((addr <= 0xff) && (m_sram_enable == m_sram_enable_dip))
		{   /* registers */
			switch ((addr >> 5) & 0x3)
			{
			case 0:     /* RTC RAM */
				if (addr & 0x80)
					/* RTC RAM page register */
					m_rtc->xram_w(machine().driver_data()->generic_space(),(addr & 0x1f) | 0x20, data);
				else
					/* RTC RAM write */
					m_rtc->xram_w(machine().driver_data()->generic_space(),addr, data);
				break;
			case 1:     /* RTC registers */
				if (addr & 0x10)
					/* register data */
					m_rtc->rtc_w(machine().driver_data()->generic_space(),1, data);
				else
					/* register select */
					m_rtc->rtc_w(machine().driver_data()->generic_space(),0, data);
				break;
			case 2:     /* IDE registers set 1 (CS1Fx) */
/*
                if (addr & 1)
                    m_output_latch = (m_output_latch & 0xff00) | data;
                else
                    m_output_latch = (m_output_latch & 0x00ff) | (data << 8);
*/
				/* latch write - bytes are swapped in 2004 IDE card */
				if (addr & 1)
					m_output_latch = (m_output_latch & 0x00ff) | (data << 8);
				else
					m_output_latch = (m_output_latch & 0xff00) | data;

				if (m_tms9995_mode ? (addr & 1) : (!(addr & 1)))
				{   /* second write triggers 16-bit write cycle */
					m_ata->write_cs0(space, (addr >> 1) & 0x7, m_output_latch, 0xffff);
				}
				break;
			case 3:     /* IDE registers set 2 (CS3Fx) */
/*
                if (addr & 1)
                    m_output_latch = (m_output_latch & 0xff00) | data;
                else
                    m_output_latch = (m_output_latch & 0x00ff) | (data << 8);
*/
				/* latch write - bytes are swapped in 2004 IDE card */
				if (addr & 1)
					m_output_latch = (m_output_latch & 0x00ff) | (data << 8);
				else
					m_output_latch = (m_output_latch & 0xff00) | data;

				if (m_tms9995_mode ? (addr & 1) : (!(addr & 1)))
				{   /* second write triggers 16-bit write cycle */
					m_ata->write_cs1(space, (addr >> 1) & 0x7, m_output_latch, 0xffff);
				}
				break;
			}
		}
		else
		{   /* sram */
			if (! (m_cru_register & cru_reg_wp))
			{
				if ((m_cru_register & cru_reg_page_0) || (addr >= 0x1000))
					m_ram[addr+0x2000 * m_cur_page] = data;
				else
					m_ram[addr] = data;
			}
		}
	}
}

void nouspikel_ide_interface_device::do_inta(int state)
{
	m_slot->set_inta(state);
}

/*
    ti99_ide_interrupt()
    IDE interrupt callback
*/
WRITE_LINE_MEMBER(nouspikel_ide_interface_device::ide_interrupt_callback)
{
	m_ata_irq = state;
	if (m_cru_register & cru_reg_int_en)
		do_inta(state);
}

/*
    clk_interrupt_callback()
    clock interrupt callback
*/
WRITE_LINE_MEMBER(nouspikel_ide_interface_device::clock_interrupt_callback)
{
	m_clk_irq = (state!=0);
	m_slot->set_inta(state);
}

void nouspikel_ide_interface_device::device_start()
{
	m_rtc = subdevice<rtc65271_device>("ide_rtc");

	m_ram = memregion(BUFFER_TAG)->base();
	m_sram_enable_dip = false; // TODO: what is this?
}

void nouspikel_ide_interface_device::device_reset()
{
	m_cur_page = 0;
	m_sram_enable = false;
	m_cru_register = 0;

	if (m_genmod)
	{
		m_select_mask = 0x1fe000;
		m_select_value = 0x174000;
	}
	else
	{
		m_select_mask = 0x7e000;
		m_select_value = 0x74000;
	}
	m_selected = false;

	m_cru_base = ioport("CRUIDE")->read();
	m_clk_irq = false;

	m_tms9995_mode =  false; // (device->type()==TMS9995);
}

MACHINE_CONFIG_FRAGMENT( tn_ide )
	MCFG_DEVICE_ADD( "ide_rtc", RTC65271, 0 )
	MCFG_RTC65271_INTERRUPT_CB(WRITELINE(nouspikel_ide_interface_device, clock_interrupt_callback))
	MCFG_ATA_INTERFACE_ADD( "ata", ata_devices, "hdd", NULL, false)
	MCFG_ATA_INTERFACE_IRQ_HANDLER(WRITELINE(nouspikel_ide_interface_device, ide_interrupt_callback))
MACHINE_CONFIG_END

ROM_START( tn_ide )
	ROM_REGION(0x80000, BUFFER_TAG, 0)  /* RAM buffer 512 KiB */
	ROM_FILL(0x0000, 0x80000, 0x00)
ROM_END

INPUT_PORTS_START( tn_ide )
	PORT_START( "CRUIDE" )
	PORT_DIPNAME( 0x1f00, 0x1000, "IDE CRU base" )
		PORT_DIPSETTING( 0x1000, "1000" )
		PORT_DIPSETTING( 0x1100, "1100" )
		PORT_DIPSETTING( 0x1200, "1200" )
		PORT_DIPSETTING( 0x1300, "1300" )
		PORT_DIPSETTING( 0x1400, "1400" )
		PORT_DIPSETTING( 0x1500, "1500" )
		PORT_DIPSETTING( 0x1600, "1600" )
		PORT_DIPSETTING( 0x1700, "1700" )
		PORT_DIPSETTING( 0x1800, "1800" )
		PORT_DIPSETTING( 0x1900, "1900" )
		PORT_DIPSETTING( 0x1a00, "1A00" )
		PORT_DIPSETTING( 0x1b00, "1B00" )
		PORT_DIPSETTING( 0x1c00, "1C00" )
		PORT_DIPSETTING( 0x1d00, "1D00" )
		PORT_DIPSETTING( 0x1e00, "1E00" )
		PORT_DIPSETTING( 0x1f00, "1F00" )
INPUT_PORTS_END

machine_config_constructor nouspikel_ide_interface_device::device_mconfig_additions() const
{
	return MACHINE_CONFIG_NAME( tn_ide );
}

const rom_entry *nouspikel_ide_interface_device::device_rom_region() const
{
	return ROM_NAME( tn_ide );
}

ioport_constructor nouspikel_ide_interface_device::device_input_ports() const
{
	return INPUT_PORTS_NAME(tn_ide);
}

const device_type TI99_IDE = &device_creator<nouspikel_ide_interface_device>;