summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/cammu.h
blob: 1a600efa6cb04c576f69d8de33b075ff786e5972 (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
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

#ifndef MAME_MACHINE_CAMMU_H
#define MAME_MACHINE_CAMMU_H

#pragma once

#include "cpu/clipper/common.h"

class cammu_device : public device_t
{
public:
	auto exception_callback() { return m_exception_func.bind(); }

	static const u32 CAMMU_PAGE_SIZE = 0x1000;
	static const u32 CAMMU_PAGE_MASK = (CAMMU_PAGE_SIZE - 1);

	enum pdo_mask : u32
	{
		PDO_MASK = 0xfffff000
	};

	enum ptde_mask : u32
	{
		PTDE_F   = 0x00000001, // page fault
		PTDE_PTO = 0xfffff000  // page table origin
	};

	enum pte_mask : u32
	{
		PTE_F     = 0x00000001, // page fault
		PTE_R     = 0x00000002, // referenced flag
		PTE_D     = 0x00000004, // dirty flag
		PTE_PL    = 0x00000078, // protection level
		PTE_S     = 0x00000180, // system reserved
		PTE_ST    = 0x00000e00, // system tag
		PTE_RA    = 0xfffff000, // real address

		PTE_CW    = 0x00000040, // copy on write (c400)
		PTE_NDREF = 0x00000080, // secondary reference (software) / copy on write (fault)?
		PTE_LOCK  = 0x00000100  // page lock (software)
	};

	static constexpr int PL_SHIFT = 3;
	static constexpr int ST_SHIFT = 9;

	enum va_mask : u32
	{
		VA_POFS = 0x00000fff, // page offset
		VA_PTI  = 0x003ff000, // page table index
		VA_PTDI = 0xffc00000  // page table directory index
	};

	enum system_tag_t : u8
	{
		ST0 = 0, // private, write-through, main memory space
		ST1 = 1, // shared, write-through, main memory space
		ST2 = 2, // private, copy-back, main memory space
		ST3 = 3, // noncacheable, main memory space
		ST4 = 4, // noncacheable, i/o space
		ST5 = 5, // noncacheable, boot space
		ST6 = 6, // cache purge
		ST7 = 7  // slave i/o
	};

	void set_spaces(address_space &main_space, address_space &io_space, address_space &boot_space);

	// translation lookaside buffer and register access
	virtual u32 cammu_r(const u32 address) = 0;
	virtual void cammu_w(const u32 address, const u32 data) = 0;

	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(const u32 ssw, const u32 address, U &&apply)
	{
		// check for cammu access
		if ((ssw & (SSW_UU | SSW_U)) || ((address & ~0x7ff) != 0x00004800))
		{
			translated_t t = translate_address(ssw, address, access_size(sizeof(T)), READ);

			if (!t.cache)
				return false;

			switch (sizeof(T))
			{
			case 1: apply(T(t.cache->read_byte(t.address))); break;
			case 2: apply(T(t.cache->read_word(t.address))); break;
			case 4: apply(T(t.cache->read_dword(t.address))); break;
			case 8: apply(T(t.cache->read_qword(t.address))); break;
			default:
				fatalerror("unhandled load 0x%08x size %d (%s)",
					address, access_size(sizeof(T)), machine().describe_context().c_str());
			}
		}
		else if (sizeof(T) == 4)
			apply(cammu_r(address));
		else
			fatalerror("unhandled cammu load 0x%08x size %d (%s)",
				address, access_size(sizeof(T)), machine().describe_context().c_str());

		return true;
	}

	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(const u32 ssw, const u32 address, U data)
	{
		// check for cammu access
		if ((ssw & (SSW_UU | SSW_U)) || ((address & ~0x7ff) != 0x00004800))
		{
			translated_t t = translate_address(ssw, address, access_size(sizeof(T)), WRITE);

			if (!t.cache)
				return false;

			switch (sizeof(T))
			{
			case 1: t.cache->write_byte(t.address, T(data)); break;
			case 2: t.cache->write_word(t.address, T(data)); break;
			case 4: t.cache->write_dword(t.address, T(data)); break;
			case 8: t.cache->write_qword(t.address, T(data)); break;
			default:
				fatalerror("unhandled store 0x%08x size %d (%s)",
					address, access_size(sizeof(T)), machine().describe_context().c_str());
			}
		}
		else if (sizeof(T) == 4)
			cammu_w(address, data);
		else
			fatalerror("unhandled cammu store 0x%08x size %d (%s)",
				address, access_size(sizeof(T)), machine().describe_context().c_str());

		return true;
	}

	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<T(T)>>::value, bool> modify(const u32 ssw, const u32 address, U &&apply)
	{
		translated_t t = translate_address(ssw, address, access_size(sizeof(T)), access_type(READ | WRITE));

		if (!t.cache)
			return false;

		switch (sizeof(T))
		{
		case 4: t.cache->write_dword(t.address, apply(T(t.cache->read_dword(t.address)))); break;
		default:
			fatalerror("unhandled modify 0x%08x size %d (%s)",
				address, access_size(sizeof(T)), machine().describe_context().c_str());
		}

		return true;
	}

	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> fetch(const u32 ssw, const u32 address, U &&apply)
	{
		translated_t t = translate_address(ssw, address, access_size(sizeof(T)), EXECUTE);

		if (!t.cache)
			return false;

		switch (sizeof(T))
		{
		case 2: apply(T(t.cache->read_word(t.address))); break;
		case 4:
			{
				// check for unaligned access
				if (address & 0x2)
				{
					// check for page span
					if ((address & CAMMU_PAGE_MASK) == (CAMMU_PAGE_SIZE - 2))
					{
						translated_t u = translate_address(ssw, address + 2, access_size(sizeof(u16)), EXECUTE);
						if (u.cache)
						{
							const u16 lsw = t.cache->read_word(t.address);
							const u16 msw = t.cache->read_word(u.address);

							apply((T(msw) << 16) | lsw);
						}
						else
							return false;
					}
					else
						apply(T(t.cache->read_dword_unaligned(t.address)));
				}
				else
					apply(T(t.cache->read_dword(t.address)));
			}
		break;
		default:
			fatalerror("unhandled fetch 0x%08x size %d (%s)\n",
				address, access_size(sizeof(T)), machine().describe_context().c_str());
		}

		return true;
	}

	// address translation for debugger
	bool memory_translate(const u32 ssw, const int spacenum, const int intention, offs_t &address);

protected:
	cammu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);

	// device-level overrides
	virtual void device_start() override;
	virtual void device_reset() override;

	enum access_size : u8
	{
		BYTE  = 1,
		WORD  = 2,
		DWORD = 4,
		QWORD = 8
	};

	enum access_type : u8
	{
		READ    = 1,
		WRITE   = 2,
		EXECUTE = 4,

		// matrix abbreviations and combinations
		N       = 0,
		R       = READ,
		W       = WRITE,
		RW      = READ | WRITE,
		RE      = READ | EXECUTE,
		RWE     = READ | WRITE | EXECUTE,
	};

	struct translated_t
	{
		memory_access_cache<2, 0, ENDIANNESS_LITTLE> *const cache;
		const u32 address;
	};

	struct pte_t
	{
		u32 entry;
		u32 address;
	};

	struct memory_t
	{
		address_space *space;
		memory_access_cache<2, 0, ENDIANNESS_LITTLE> *cache;
	};

	// address translation
	virtual translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode);
	pte_t get_pte(const u32 va, const bool user);

	// helpers
	virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const = 0;
	virtual bool get_alignment() const = 0;
	virtual u32 get_pdo(const bool user) const = 0;
	virtual system_tag_t get_ust_space() const = 0;
	virtual void set_fault(const u32 address, const exception_vector type) = 0;

	// device state
	devcb_write16 m_exception_func;
	memory_t m_memory[8];
};

class cammu_c4_device : public cammu_device
{
public:
	// TODO: translation lookaside buffer and register access
	virtual void map(address_map &map) = 0;
	virtual u32 cammu_r(const u32 address) override { return 0; }
	virtual void cammu_w(const u32 address, const u32 data) override {}

	void set_cammu_id(const u32 cammu_id) { m_control = cammu_id; }

	DECLARE_READ32_MEMBER(s_pdo_r) { return m_s_pdo; }
	DECLARE_WRITE32_MEMBER(s_pdo_w) { m_s_pdo = ((m_s_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }
	DECLARE_READ32_MEMBER(u_pdo_r) { return m_u_pdo; }
	DECLARE_WRITE32_MEMBER(u_pdo_w) { m_u_pdo = ((m_u_pdo & ~mem_mask) | (data & mem_mask)) & PDO_MASK; }

	virtual DECLARE_READ32_MEMBER(control_r) = 0;
	virtual DECLARE_WRITE32_MEMBER(control_w) = 0;

	DECLARE_READ32_MEMBER(i_fault_r) { return m_i_fault; }
	DECLARE_WRITE32_MEMBER(i_fault_w) { m_i_fault = data; }
	DECLARE_READ32_MEMBER(fault_address_1_r) { return m_fault_address_1; }
	DECLARE_WRITE32_MEMBER(fault_address_1_w) { m_fault_address_1 = data; }
	DECLARE_READ32_MEMBER(fault_address_2_r) { return m_fault_address_2; }
	DECLARE_WRITE32_MEMBER(fault_address_2_w) { m_fault_address_2 = data; }
	DECLARE_READ32_MEMBER(fault_data_1_lo_r) { return m_fault_data_1_lo; }
	DECLARE_WRITE32_MEMBER(fault_data_1_lo_w) { m_fault_data_1_lo = data; }
	DECLARE_READ32_MEMBER(fault_data_1_hi_r) { return m_fault_data_1_hi; }
	DECLARE_WRITE32_MEMBER(fault_data_1_hi_w) { m_fault_data_1_hi = data; }
	DECLARE_READ32_MEMBER(fault_data_2_lo_r) { return m_fault_data_2_lo; }
	DECLARE_WRITE32_MEMBER(fault_data_2_lo_w) { m_fault_data_2_lo = data; }
	DECLARE_READ32_MEMBER(fault_data_2_hi_r) { return m_fault_data_2_hi; }
	DECLARE_WRITE32_MEMBER(fault_data_2_hi_w) { m_fault_data_2_hi = data; }

protected:
	cammu_c4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);

	virtual void device_start() override;

	virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override;
	virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }

	virtual void set_fault(const u32 address, const exception_vector type) override { m_fault_address_1 = address; m_exception_func(type); }

	u32 m_s_pdo;
	u32 m_u_pdo;
	u32 m_control;

	u32 m_i_fault;
	u32 m_fault_address_1;
	u32 m_fault_address_2;
	u32 m_fault_data_1_lo;
	u32 m_fault_data_1_hi;
	u32 m_fault_data_2_lo;
	u32 m_fault_data_2_hi;
};

class cammu_c4t_device : public cammu_c4_device
{
public:
	cammu_c4t_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	virtual void map(address_map &map) override;

	DECLARE_READ32_MEMBER(ram_line_r) { return m_ram_line; }
	DECLARE_WRITE32_MEMBER(ram_line_w) { m_ram_line = data; }

	DECLARE_READ32_MEMBER(htlb_offset_r) { return m_htlb_offset; }
	DECLARE_WRITE32_MEMBER(htlb_offset_w) { m_htlb_offset = data; }

	DECLARE_READ32_MEMBER(c4_bus_poll_r) { return m_c4_bus_poll; }
	DECLARE_WRITE32_MEMBER(c4_bus_poll_w) { m_c4_bus_poll = data; }

	enum control_mask : u32
	{
		CNTL_RUV   = 0x00000001, // reset user valid
		CNTL_RSV   = 0x00000002, // reset supervisor valid
		CNTL_DBWR  = 0x00000004, // disable bus watch read
		CNTL_ATD   = 0x00000008, // alignment trap disable
		CNTL_UST   = 0x00000030, // unmapped system tag
		CNTL_IOTS  = 0x00000040, // i/o tag select
		CNTL_UVS   = 0x00000080, // user valid status
		CNTL_PB    = 0x00000100, // purge busy
		CNTL_CICT  = 0x00000200, // clear i-side cache tags
		CNTL_CFR   = 0x00000400, // clear trap registers
		CNTL_HTLBD = 0x00000800, // htlb disable
		CNTL_CDCT  = 0x00001000, // clear d-side cache tags
		CNTL_CID   = 0xff000000  // cammu id
	};

	enum control_ust_mask : u32
	{
		UST_NCA = 0x00, // unmapped system tag, noncacheable
		UST_PWT = 0x10, // unmapped system tag, write through
		UST_PCB = 0x20, // unmapped system tag, copy back
		UST_PGE = 0x30  // unmapped system tag, purge mode
	};

	enum control_cid_mask : u32
	{
		CID_C4T = 0x00000000 // unknown
	};

	virtual DECLARE_READ32_MEMBER(control_r) override { return m_control; }
	virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }
	DECLARE_READ32_MEMBER(bio_control_r) { return m_bio_control; }
	DECLARE_WRITE32_MEMBER(bio_control_w) { m_bio_control = data; }
	DECLARE_READ32_MEMBER(bio_address_tag_r) { return m_bio_address_tag; }
	DECLARE_WRITE32_MEMBER(bio_address_tag_w) { m_bio_address_tag = data; }

	DECLARE_READ32_MEMBER(cache_data_lo_r) { return m_cache_data_lo; }
	DECLARE_WRITE32_MEMBER(cache_data_lo_w) { m_cache_data_lo = data; }
	DECLARE_READ32_MEMBER(cache_data_hi_r) { return m_cache_data_hi; }
	DECLARE_WRITE32_MEMBER(cache_data_hi_w) { m_cache_data_hi = data; }
	DECLARE_READ32_MEMBER(cache_cpu_tag_r) { return m_cache_cpu_tag; }
	DECLARE_WRITE32_MEMBER(cache_cpu_tag_w) { m_cache_cpu_tag = data; }
	DECLARE_READ32_MEMBER(cache_system_tag_valid_r) { return m_cache_system_tag_valid; }
	DECLARE_WRITE32_MEMBER(cache_system_tag_valid_w) { m_cache_system_tag_valid = data; }
	DECLARE_READ32_MEMBER(cache_system_tag_r) { return m_cache_system_tag; }
	DECLARE_WRITE32_MEMBER(cache_system_tag_w) { m_cache_system_tag = data; }
	DECLARE_READ32_MEMBER(tlb_va_line_r) { return m_tlb_va_line; }
	DECLARE_WRITE32_MEMBER(tlb_va_line_w) { m_tlb_va_line = data; }
	DECLARE_READ32_MEMBER(tlb_ra_line_r) { return m_tlb_ra_line; }
	DECLARE_WRITE32_MEMBER(tlb_ra_line_w) { m_tlb_ra_line = data; }

protected:
	virtual void device_start() override;

	virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
	virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & (CNTL_IOTS | CNTL_UST)) >> 4); }

private:
	u32 m_ram_line;
	u32 m_htlb_offset;
	u32 m_c4_bus_poll;
	u32 m_bio_control;
	u32 m_bio_address_tag;

	u32 m_cache_data_lo;
	u32 m_cache_data_hi;
	u32 m_cache_cpu_tag;
	u32 m_cache_system_tag_valid;
	u32 m_cache_system_tag;
	u32 m_tlb_va_line;
	u32 m_tlb_ra_line;
};

class cammu_c4i_device : public cammu_c4_device
{
public:
	cammu_c4i_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	virtual void map(address_map &map) override;

	enum control_mask : u32
	{
		CNTL_LRAS = 0x00000001, // tlb line replacement
		CNTL_BWWD = 0x00000002, // buswatch write disable
		CNTL_BWRD = 0x00000004, // buswatch read disable
		CNTL_FSR  = 0x00000010, // fake system response
		CNTL_ATD  = 0x00000100, // alignment trap disable
		CNTL_UMM  = 0x00003000, // unmapped mode address space select
		CNTL_POLL = 0x00030000, // poll bus signals
		CNTL_BM   = 0x00040000, // burst mode address space select
		CNTL_PZBS = 0x00080000, // page 0 boot select
		CNTL_CRR  = 0x00700000, // cache memory refresh rate
		CNTL_CID  = 0xff000000  // cammu identification
	};

	enum control_umm_mask : u32
	{
		UMM_MM    = 0x00000000, // mm space, noncacheable
		UMM_MMRIO = 0x00001000, // mm or i/o space, noncacheable
		UMM_IO    = 0x00002000  // i/o space noncacheable
	};

	enum control_crr_mask : u32
	{
		CRR_GT131  = 0x00000000, // clock rate over 131 MHz
		CRR_GT66   = 0x00100000, // clock rate over 66 MHz
		CRR_GT33   = 0x00200000, // clock rate over 33 MHz
		CRR_GT8    = 0x00300000, // clock rate over 8 MHz
		CRR_GT2    = 0x00400000, // clock rate over 2 MHz
		CRR_GT1    = 0x00500000, // clock rate over 1 MHz
		CRR_GTHALF = 0x00600000, // clock rate over 0.5 MHz
		CRR_OFF    = 0x00700000, // refresh off
	};

	// c4i cammu identification (rev 2 and rev 3 known to have existed)
	enum control_cid_mask : u32
	{
		CID_C4IR0 = 0x00000000,
		CID_C4IR2 = 0x02000000
	};

	virtual DECLARE_READ32_MEMBER(control_r) override { return m_control; }
	virtual DECLARE_WRITE32_MEMBER(control_w) override { m_control = ((m_control & (~mem_mask | CNTL_CID)) | (data & (mem_mask & ~CNTL_CID))); }

	enum reset_mask : u32
	{
		RESET_CDCT  = 0x00000001, // clear data cache tags
		RESET_RDUV  = 0x00000100, // reset all d-side uv flags
		RESET_RDSV  = 0x00001000, // reset all d-side sv flags
		RESET_CICT  = 0x00010000, // clear ins. cache tags
		RESET_RIUV  = 0x01000000, // reset all i-side uv flags
		RESET_RISV  = 0x10000000, // reset all i-side sv flags
		RESET_FLUSH = 0x40000000, // flush out burst io buffer
		RESET_CFR   = 0x80000000  // clear fault registers
	};
	DECLARE_READ32_MEMBER(reset_r) { return m_reset; }
	DECLARE_WRITE32_MEMBER(reset_w) { m_reset = data; }

	DECLARE_READ32_MEMBER(clr_s_data_tlb_r) { return m_clr_s_data_tlb; }
	DECLARE_WRITE32_MEMBER(clr_s_data_tlb_w) { m_clr_s_data_tlb = data; }
	DECLARE_READ32_MEMBER(clr_u_data_tlb_r) { return m_clr_u_data_tlb; }
	DECLARE_WRITE32_MEMBER(clr_u_data_tlb_w) { m_clr_u_data_tlb = data; }
	DECLARE_READ32_MEMBER(clr_s_insn_tlb_r) { return m_clr_s_insn_tlb; }
	DECLARE_WRITE32_MEMBER(clr_s_insn_tlb_w) { m_clr_s_insn_tlb = data; }
	DECLARE_READ32_MEMBER(clr_u_insn_tlb_r) { return m_clr_u_insn_tlb; }
	DECLARE_WRITE32_MEMBER(clr_u_insn_tlb_w) { m_clr_u_insn_tlb = data; }

	DECLARE_READ32_MEMBER(test_data_r) { return m_test_data; }
	DECLARE_WRITE32_MEMBER(test_data_w) { m_test_data = data; }

	DECLARE_READ32_MEMBER(test_address_r) { return m_test_address; }
	DECLARE_WRITE32_MEMBER(test_address_w) { m_test_address = data; }

protected:
	virtual void device_start() override;

	virtual bool get_alignment() const override { return (m_control & CNTL_ATD) == 0; }
	// FIXME: don't really know how unmapped mode works on c4i
	virtual system_tag_t get_ust_space() const override { return (m_control & UMM_IO) ? ST4 : ST3; }

private:
	u32 m_reset;
	u32 m_clr_s_data_tlb;
	u32 m_clr_u_data_tlb;
	u32 m_clr_s_insn_tlb;
	u32 m_clr_u_insn_tlb;
	u32 m_test_data;
	u32 m_test_address;
};

class cammu_c3_device : public cammu_device
{
public:
	cammu_c3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);

	void add_linked(cammu_c3_device *child) { m_linked.push_back(child); }

protected:
	// device-level overrides
	virtual void device_reset() override;
	virtual void device_start() override;

	// translation lookaside buffer and register access
	virtual u32 cammu_r(const u32 address) override;
	virtual void cammu_w(const u32 address, const u32 data) override;

	// address translation
	virtual translated_t translate_address(const u32 ssw, const u32 virtual_address, const access_size size, const access_type mode) override;

private:
	enum cammu_address_mask : u32
	{
		CAMMU_TLB_VA  = 0x00000001, // tlb va/ra select
		CAMMU_TLB_X   = 0x00000002, // tlb x/w line select
		CAMMU_TLB_SET = 0x000000fc, // tlb set select
		CAMMU_REG     = 0x000000ff, // register select
		CAMMU_SELECT  = 0x00000700, // cammu select
	};
	enum tlb_ra_mask : u32
	{
		TLB_RA_U  = 0x00000001, // used flag
		TLB_RA_R  = 0x00000002, // referenced flag
		TLB_RA_D  = 0x00000004, // dirty flag
		TLB_RA_PL = 0x00000078, // protection level
		TLB_RA_ST = 0x00000e00, // system tag
		TLB_RA_RA = 0xfffff000, // real address
	};
	enum tlb_va_mask : u32
	{
		TLB_VA_UV = 0x00000002, // user valid flag
		TLB_VA_SV = 0x00000004, // supervisor valid flag
		TLB_VA_VA = 0xfffc0000, // virtual address tag
	};

	/*
	* The C1/C3 CAMMU has 64-entry, two-way set associative TLB, with lines
	* grouped into W and X compartments. The associated U flag is set to
	* indicate that the W line of the set was most recently accessed, and
	* cleared when the X line was most recently accessed. On TLB miss, the
	* least recently used line as indicated by this flag is replaced.
	*
	* Each line consists of a real address field and a virtual address field.
	* The real address field format is practically identical to the page table
	* entry format.
	*/
	struct tlb_line_t
	{
		u32 ra; // real address field
		u32 va; // virtual address field

		memory_access_cache<2, 0, ENDIANNESS_LITTLE> *cache;
	};
	struct tlb_set_t
	{
		tlb_line_t w;
		tlb_line_t x;
		bool u;
	};

	enum cammu_select_mask : u32
	{
		CAMMU_D_TLB = 0x000, // d-cammu tlb
		CAMMU_D_REG = 0x100, // d-cammu register
		CAMMU_I_TLB = 0x200, // i-cammu tlb
		CAMMU_I_REG = 0x300, // i-cammu register
		CAMMU_G_TLB = 0x400, // global tlb
		CAMMU_G_REG = 0x500, // global register
	};
	enum cammu_register_mask : u8
	{
		CAMMU_REG_SPDO    = 0x04, // supervisor pdo register
		CAMMU_REG_UPDO    = 0x08, // user pdo register
		CAMMU_REG_FAULT   = 0x10, // fault register
		CAMMU_REG_CONTROL = 0x40, // control register
		CAMMU_REG_RESET   = 0x80, // reset register
	};

	enum control_mask : u32
	{
		CNTL_EP   = 0x00000001, // enable prefetch
		CNTL_EWCW = 0x00000002, // enable watch cpu writes
		CNTL_EWIW = 0x00000004, // enable watch i/o writes
		CNTL_EWIR = 0x00000008, // enable watch i/o reads
		CNTL_UST  = 0x00000030, // unmapped system tag
		CNTL_CV   = 0x00000100, // clear valid
		CNTL_ATE  = 0x00000200, // alignment trap enable
		CNTL_CID  = 0xff000000  // cammu id
	};
	enum control_ust_mask : u32
	{
		UST_0 = 0x00000000, // private, write-through, main memory space
		UST_1 = 0x00000010, // shared, write-through, main memory space
		UST_2 = 0x00000020, // private, copy-back, main memory space
		UST_3 = 0x00000030  // noncacheable, main memory space
	};
	enum control_cid_mask : u32
	{
		CID_C3 = 0x00000000 // unknown
	};

	enum reset_mask : u32
	{
		RESET_RLVW = 0x00000001, // reset all W line LV flags in cache
		RESET_RLVX = 0x00000002, // reset all X line LV flags in cache
		RESET_RSV  = 0x00000004, // reset all SV flags in tlb
		RESET_RUV  = 0x00000008, // reset all UV flags in tlb
		RESET_RD   = 0x00000010, // reset all D flags in tlb
		RESET_RR   = 0x00000020, // reset all R flags in tlb
		RESET_RU   = 0x00000040, // reset all U flags in cache
	};

	u32 tlb_r(const u8 address) const;
	void tlb_w(const u8 address, const u32 data);
	tlb_line_t &tlb_lookup(const bool user, const u32 virtual_address, const access_type mode);

	u32 s_pdo_r() const { return m_s_pdo; }
	void s_pdo_w(const u32 data) { m_s_pdo = data & PDO_MASK; }
	u32 u_pdo_r() const { return m_u_pdo; }
	void u_pdo_w(const u32 data) { m_u_pdo = data & PDO_MASK; }
	u32 fault_r() const { return m_fault; }
	void fault_w(const u32 data) { m_fault = data; }
	u32 control_r() const { return m_control; }
	void control_w(const u32 data) { m_control = (m_control & CNTL_CID) | (data & ~CNTL_CID); }
	void reset_w(const u32 data);

	virtual bool get_alignment() const override { return m_control & CNTL_ATE; }
	virtual system_tag_t get_ust_space() const override { return system_tag_t((m_control & CNTL_UST) >> 4); }
	virtual bool get_access(const access_type mode, const u32 pte, const u32 ssw) const override;
	virtual u32 get_pdo(const bool user) const override { return user ? m_u_pdo : m_s_pdo; }

	virtual void set_fault(const u32 address, const exception_vector type) override { m_fault = address; m_exception_func(type); }

	static const u8 protection_matrix[4][16];

	// device state
	std::vector<cammu_c3_device *> m_linked;

	u32 m_s_pdo;
	u32 m_u_pdo;
	u32 m_fault;
	u32 m_control;

	tlb_set_t m_tlb[64];
};

// device type definitions
DECLARE_DEVICE_TYPE(CAMMU_C4T, cammu_c4t_device)
DECLARE_DEVICE_TYPE(CAMMU_C4I, cammu_c4i_device)
DECLARE_DEVICE_TYPE(CAMMU_C3,  cammu_c3_device)

#endif // MAME_MACHINE_CAMMU_H