summaryrefslogtreecommitdiffstats
path: root/src/devices/cpu/mips/mips3com.cpp
blob: 294dd6b20e5e6ceb2f196f54d34e11e5f9a3f06b (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
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************

    mips3com.c

    Common MIPS III/IV definitions and functions

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

#include "emu.h"
#include "mips3com.h"
#include "ps2vu.h"


/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static void tlb_entry_log_half(mips3_tlb_entry *entry, int tlbindex, int which);



/***************************************************************************
    INLINE FUNCTIONS
***************************************************************************/

/*-------------------------------------------------
    tlb_entry_matches_asid - true if the given
    TLB entry matches the provided ASID
-------------------------------------------------*/

static inline bool tlb_entry_matches_asid(const mips3_tlb_entry *entry, uint8_t asid)
{
	return (entry->entry_hi & 0xff) == asid;
}


/*-------------------------------------------------
    tlb_entry_is_global - true if the given
    TLB entry is global
-------------------------------------------------*/

static inline bool tlb_entry_is_global(const mips3_tlb_entry *entry)
{
	return (entry->entry_lo[0] & entry->entry_lo[1] & TLB_GLOBAL);
}


void mips3_device::execute_set_input(int inputnum, int state)
{
	if (inputnum >= MIPS3_IRQ0 && inputnum <= MIPS3_IRQ5)
	{
		if (state != CLEAR_LINE)
			m_core->cpr[0][COP0_Cause] |= 0x400 << inputnum;
		else
			m_core->cpr[0][COP0_Cause] &= ~(0x400 << inputnum);
	}
}


/***************************************************************************
    INITIALIZATION AND SHUTDOWN
***************************************************************************/

/*-------------------------------------------------
    mips3com_update_cycle_counting - update cycle
    counts and the timers
-------------------------------------------------*/

void mips3_device::mips3com_update_cycle_counting()
{
	/* modify the timer to go off */
	if (m_core->compare_armed)
	{
		uint32_t count = (total_cycles() - m_core->count_zero_time) / 2;
		uint32_t compare = m_core->cpr[0][COP0_Compare];
		uint32_t delta = compare - count;
		m_core->compare_armed = 0;
		attotime newtime = cycles_to_attotime((uint64_t)delta * 2);
		m_compare_int_timer->adjust(newtime);
		return;
	}
}

/***************************************************************************
    TLB HANDLING
***************************************************************************/

/*-------------------------------------------------
    mips3com_asid_changed - remap all non-global
    TLB entries
-------------------------------------------------*/

void mips3_device::mips3com_asid_changed()
{
	int tlbindex;

	/* iterate over all non-global TLB entries and remap them */
	for (tlbindex = 0; tlbindex < m_tlbentries; tlbindex++)
		if (!tlb_entry_is_global(&m_tlb[tlbindex]))
			tlb_map_entry(tlbindex);
}


/*-------------------------------------------------
    mips3com_tlbr - execute the tlbr instruction
-------------------------------------------------*/

void mips3_device::mips3com_tlbr()
{
	uint32_t tlbindex = m_core->cpr[0][COP0_Index] & 0x3f;

	/* only handle entries within the TLB */
	if (tlbindex < m_tlbentries)
	{
		mips3_tlb_entry *entry = &m_tlb[tlbindex];

		/* copy data from the TLB entry into the COP0 registers */
		m_core->cpr[0][COP0_PageMask] = entry->page_mask;
		m_core->cpr[0][COP0_EntryHi] = entry->entry_hi;
		m_core->cpr[0][COP0_EntryLo0] = entry->entry_lo[0];
		m_core->cpr[0][COP0_EntryLo1] = entry->entry_lo[1];
	}
}


/*-------------------------------------------------
    mips3com_tlbwi - execute the tlbwi instruction
-------------------------------------------------*/

void mips3_device::mips3com_tlbwi()
{
	/* use the common handler and write based off the COP0 Index register */
	tlb_write_common(m_core->cpr[0][COP0_Index] & 0x3f);
}


/*-------------------------------------------------
    mips3com_tlbwr - execute the tlbwr instruction
-------------------------------------------------*/

void mips3_device::mips3com_tlbwr()
{
	uint32_t wired = m_core->cpr[0][COP0_Wired] & 0x3f;
	uint32_t unwired = m_tlbentries - wired;
	uint32_t tlbindex = m_tlbentries - 1;

	/* "random" is based off of the current cycle counting through the non-wired pages */
	if (unwired > 0)
		tlbindex = ((total_cycles() - m_core->count_zero_time) % unwired + wired) & 0x3f;

	/* use the common handler to write to this tlbindex */
	tlb_write_common(tlbindex);
}


/*-------------------------------------------------
    mips3com_tlbp - execute the tlbp instruction
-------------------------------------------------*/

void mips3_device::mips3com_tlbp()
{
	uint32_t tlbindex;

	/* iterate over TLB entries */
	for (tlbindex = 0; tlbindex < m_tlbentries; tlbindex++)
	{
		mips3_tlb_entry *entry = &m_tlb[tlbindex];
		uint64_t mask = ~((entry->page_mask >> 13) & 0xfff) << 13;

		/* if the relevant bits of EntryHi match the relevant bits of the TLB */
		if ((entry->entry_hi & mask) == (m_core->cpr[0][COP0_EntryHi] & mask))

			/* and if we are either global or matching the current ASID, then stop */
			if ((entry->entry_hi & 0xff) == (m_core->cpr[0][COP0_EntryHi] & 0xff) || ((entry->entry_lo[0] & entry->entry_lo[1]) & TLB_GLOBAL))
				break;
	}

	/* validate that our tlb_table was in sync */
//  vpn = ((m_cores->cpr[0][COP0_EntryHi] >> 13) & 0x07ffffff) << 1;
	if (tlbindex != m_tlbentries)
		m_core->cpr[0][COP0_Index] = tlbindex;
	else
		m_core->cpr[0][COP0_Index] = 0x80000000;
}



/***************************************************************************
    INTERNAL HELPERS
***************************************************************************/

/*-------------------------------------------------
    compare_int_callback - callback that fires
    whenever a compare interrupt is generated
-------------------------------------------------*/

TIMER_CALLBACK_MEMBER( mips3_device::compare_int_callback )
{
	m_compare_int_timer->adjust(attotime::never);
	set_input_line(MIPS3_IRQ5, ASSERT_LINE);
}


/*-------------------------------------------------
    compute_config_register - compute the value
    of the config register
-------------------------------------------------*/

uint32_t mips3_device::compute_config_register()
{
	/* set the cache line size to 32 bytes */
	uint32_t configreg = 0x00026030;
	int divisor;

	// NEC VR series does not use a 100% compatible COP0/TLB implementation
	if (m_flavor == MIPS3_TYPE_VR4300)
	{
		/*
		    For VR43xx, Config is as follows:
		    bit 31 = always 0
		    bits 28-30 = EC
		    bits 24-27 = EP
		    bits 16-23 = always b0000010
		    bit 15 = endian indicator as standard MIPS III
		    bits 4-14 = always b11001000110
		    bit 3 = CU
		    bits 0-2 = K0 ("Coherency algorithm of kseg0")
		*/

		configreg = 0x6460;
	}
	else if (m_flavor == MIPS3_TYPE_VR5500)
	{
		/*
		    For VR55xx, Config is as follows:
		    bit 31 = always 0
		    bits 28-30 = EC
		    bits 24-27 = EP
		    bits 23-22 = EM
		    bits 21-20 = always b11
		    bits 19-18 = EW
		    bits 17-16 = always b10
		    bit 15 = endian indicator as standard MIPS III
		    bits 3-14 = always b110011011110
		    bits 0-2 = K0 ("Coherency algorithm of kseg0")
		*/

		configreg = 0x6460;
	}
	else
	{
		/* set the data cache size */
				if (c_icache_size <= 0x01000) configreg |= 0 << 6;
		else if (c_icache_size <= 0x02000) configreg |= 1 << 6;
		else if (c_icache_size <= 0x04000) configreg |= 2 << 6;
		else if (c_icache_size <= 0x08000) configreg |= 3 << 6;
		else if (c_icache_size <= 0x10000) configreg |= 4 << 6;
		else if (c_icache_size <= 0x20000) configreg |= 5 << 6;
		else if (c_icache_size <= 0x40000) configreg |= 6 << 6;
		else                                   configreg |= 7 << 6;

		/* set the instruction cache size */
				if (c_icache_size <= 0x01000) configreg |= 0 << 9;
		else if (c_icache_size <= 0x02000) configreg |= 1 << 9;
		else if (c_icache_size <= 0x04000) configreg |= 2 << 9;
		else if (c_icache_size <= 0x08000) configreg |= 3 << 9;
		else if (c_icache_size <= 0x10000) configreg |= 4 << 9;
		else if (c_icache_size <= 0x20000) configreg |= 5 << 9;
		else if (c_icache_size <= 0x40000) configreg |= 6 << 9;
		else                                   configreg |= 7 << 9;


		/* set the system clock divider */
		divisor = 2;
		if (c_system_clock != 0)
		{
			divisor = m_cpu_clock / c_system_clock;
			if (c_system_clock * divisor != m_cpu_clock)
			{
				configreg |= 0x80000000;
				divisor = m_cpu_clock * 2 / c_system_clock;
			}
		}
		configreg |= (((divisor < 2) ? 2 : (divisor > 8) ? 8 : divisor) - 2) << 28;
	}

	/* set the endianness bit */
	if (m_bigendian)
		configreg |= 0x00008000;

	return configreg;
}


/*-------------------------------------------------
    compute_prid_register - compute the value
    of the PRId register
-------------------------------------------------*/

uint32_t mips3_device::compute_prid_register()
{
	switch (m_flavor)
	{
		case MIPS3_TYPE_VR4300:
			return 0x0b00;

		case MIPS3_TYPE_VR5500:
			return 0x5500;

		case MIPS3_TYPE_R4600:
		case MIPS3_TYPE_R4650:
			return 0x2000;

		case MIPS3_TYPE_R4700:
			return 0x2100;

		case MIPS3_TYPE_TX4925:
			return 0x2d23;

		case MIPS3_TYPE_R5000:
		case MIPS3_TYPE_QED5271:
			return 0x2300;

		case MIPS3_TYPE_RM7000:
			return 0x2700;

		case MIPS3_TYPE_R5900:
			return 0x2e14;

		default:
			fatalerror("Unknown MIPS flavor specified\n");
	}
	// never executed
	//return 0x2000;
}


/*-------------------------------------------------
    tlb_map_entry - map a single TLB
    entry
-------------------------------------------------*/

void mips3_device::tlb_map_entry(int tlbindex)
{
	int current_asid = m_core->cpr[0][COP0_EntryHi] & 0xff;
	mips3_tlb_entry *entry = &m_tlb[tlbindex];
	uint32_t count, vpn;
	int which;

	/* the ASID doesn't match the current ASID, and if the page isn't global, unmap it from the TLB */
	if (!tlb_entry_matches_asid(entry, current_asid) && !tlb_entry_is_global(entry))
	{
		vtlb_load(2 * tlbindex + 0, 0, 0, 0);
		vtlb_load(2 * tlbindex + 1, 0, 0, 0);
		return;
	}

	/* extract the VPN index; ignore if the virtual address is beyond 32 bits */
	vpn = ((entry->entry_hi >> 13) & 0x07ffffff) << 1;
	if (vpn >= (1 << (MIPS3_MAX_PADDR_SHIFT - MIPS3_MIN_PAGE_SHIFT)))
	{
		vtlb_load(2 * tlbindex + 0, 0, 0, 0);
		vtlb_load(2 * tlbindex + 1, 0, 0, 0);
		return;
	}

	/* get the number of pages from the page mask */
    /* R5900: if the S bit is set in EntryLo, it is the scratchpad, and is always 4 pages. */
	if ((entry->entry_lo[0] & 0x80000000) && m_flavor == MIPS3_TYPE_R5900)
		count = 4;
	else
		count = ((entry->page_mask >> 13) & 0x00fff) + 1;

	/* loop over both the even and odd pages */
	for (which = 0; which < 2; which++)
	{
		uint32_t effvpn = vpn + count * which;
		uint64_t lo = entry->entry_lo[which];
		uint32_t pfn;
		uint32_t flags = 0;

		/* compute physical page index */
		pfn = (lo >> 6) & m_pfnmask;

		/* valid? */
		if ((lo & 2) != 0)
		{
			flags |= VTLB_FLAG_VALID | VTLB_READ_ALLOWED | VTLB_FETCH_ALLOWED;

			/* writable? */
			if ((lo & 4) != 0)
				flags |= VTLB_WRITE_ALLOWED;

			/* mirror the flags for user mode if the VPN is in user space */
			if (effvpn < (0x80000000 >> MIPS3_MIN_PAGE_SHIFT))
				flags |= (flags << 4) & (VTLB_USER_READ_ALLOWED | VTLB_USER_WRITE_ALLOWED | VTLB_USER_FETCH_ALLOWED);
		}

		/* load the virtual TLB with the corresponding entries */
		if ((effvpn + count) <= (0x80000000 >> MIPS3_MIN_PAGE_SHIFT) || effvpn >= (0xc0000000 >> MIPS3_MIN_PAGE_SHIFT))
			vtlb_load(2 * tlbindex + which, count, effvpn << MIPS3_MIN_PAGE_SHIFT, (pfn << MIPS3_MIN_PAGE_SHIFT) | flags);
		else
			vtlb_load(2 * tlbindex + which, 0, 0, 0);
	}
}


/*-------------------------------------------------
    tlb_write_common - common routine for writing
    a TLB entry
-------------------------------------------------*/

void mips3_device::tlb_write_common(int tlbindex)
{
	/* only handle entries within the TLB */
	if (tlbindex < m_tlbentries)
	{
		mips3_tlb_entry *entry = &m_tlb[tlbindex];

		/* fill in the new TLB entry from the COP0 registers */
		entry->page_mask = m_core->cpr[0][COP0_PageMask];
		entry->entry_hi = m_core->cpr[0][COP0_EntryHi] & ~(entry->page_mask & u64(0x0000000001ffe000U));
		entry->entry_lo[0] = m_core->cpr[0][COP0_EntryLo0];
		entry->entry_lo[1] = m_core->cpr[0][COP0_EntryLo1];

		/* remap this TLB entry */
		tlb_map_entry(tlbindex);

		/* log the two halves once they are in */
		tlb_entry_log_half(entry, tlbindex, 0);
		tlb_entry_log_half(entry, tlbindex, 1);
	}
}


/*-------------------------------------------------
    tlb_entry_log_half - log half of a single TLB
    entry
-------------------------------------------------*/

static void tlb_entry_log_half(mips3_tlb_entry *entry, int tlbindex, int which)
{
if (PRINTF_TLB)
{
	uint64_t hi = entry->entry_hi;
	uint64_t lo = entry->entry_lo[which];
	uint32_t vpn = (((hi >> 13) & 0x07ffffff) << 1);
	uint32_t asid = hi & 0xff;
	uint32_t r = (hi >> 62) & 3;
	uint32_t pfn = (lo >> 6) & 0x00ffffff;
	uint32_t c = (lo >> 3) & 7;
	uint32_t pagesize = (((entry->page_mask >> 13) & 0xfff) + 1) << MIPS3_MIN_PAGE_SHIFT;
	uint64_t vaddr = (uint64_t)vpn * MIPS3_MIN_PAGE_SIZE;
	uint64_t paddr = (uint64_t)pfn * MIPS3_MIN_PAGE_SIZE;

	vaddr += pagesize * which;

	printf("index=%08X  pagesize=%08X  vaddr=%08X%08X  paddr=%08X%08X  asid=%02X  r=%X  c=%X  dvg=%c%c%c\n",
			tlbindex, pagesize, (uint32_t)(vaddr >> 32), (uint32_t)vaddr, (uint32_t)(paddr >> 32), (uint32_t)paddr,
			asid, r, c, (lo & 4) ? 'd' : '.', (lo & 2) ? 'v' : '.', (lo & 1) ? 'g' : '.');
}
}