summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mips/r4000.h
blob: 8c3a1760ba3d95e3da6ba1a75e7dc88283782280 (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
// license:BSD-3-Clause
// copyright-holders:Patrick Mackinlay

#ifndef MAME_CPU_MIPS_R4000_H
#define MAME_CPU_MIPS_R4000_H

#pragma once

DECLARE_DEVICE_TYPE(R4000, r4000_device)
DECLARE_DEVICE_TYPE(R4400, r4400_device)
DECLARE_DEVICE_TYPE(R4600, r4600_device)

class r4000_base_device : public cpu_device
{
public:
	enum config_mask : u32
	{
		CONFIG_K0 = 0x00000007, // kseg0 cache coherency
		CONFIG_CU = 0x00000080, // store conditional cache coherent
		CONFIG_DB = 0x00000010, // primary d-cache line 32 bytes
		CONFIG_IB = 0x00000020, // primary i-cache line 32 bytes
		CONFIG_DC = 0x000001c0, // primary d-cache size
		CONFIG_IC = 0x00000e00, // primary i-cache size
		CONFIG_EB = 0x00002000, // sub-block ordering
		CONFIG_EM = 0x00004000, // parity mode enable
		CONFIG_BE = 0x00008000, // big endian
		CONFIG_SM = 0x00010000, // dirty shared disable
		CONFIG_SC = 0x00020000, // secondary cache absent
		CONFIG_EW = 0x000c0000, // system port width
		CONFIG_SW = 0x00100000, // secondary cache port width
		CONFIG_SS = 0x00200000, // split secondary cache mode
		CONFIG_SB = 0x00c00000, // secondary cache line size
		CONFIG_EP = 0x0f000000, // transmit data pattern
		CONFIG_EC = 0x70000000, // system clock ratio
		CONFIG_CM = 0x80000000, // master/checker enable

		CONFIG_WM = 0x0000003f, // runtime-writable bits
	};
	void set_config(u32 data, u32 mem_mask = ~u32(0))
	{
		if (!configured())
			COMBINE_DATA(&m_cp0[CP0_Config]);
	}

protected:
	enum cache_size_t
	{
		CACHE_4K   = 0,
		CACHE_8K   = 1,
		CACHE_16K  = 2,
		CACHE_32K  = 3,
		CACHE_64K  = 4,
		CACHE_128K = 5,
		CACHE_256K = 6,
		CACHE_512K = 7,
	};
	r4000_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u32 prid, cache_size_t icache_size, cache_size_t dcache_size);

	enum cp0_reg : int
	{
		CP0_Index    = 0,
		CP0_Random   = 1,
		CP0_EntryLo0 = 2,
		CP0_EntryLo1 = 3,
		CP0_Context  = 4,
		CP0_PageMask = 5,
		CP0_Wired    = 6,
		CP0_BadVAddr = 8,
		CP0_Count    = 9,
		CP0_EntryHi  = 10,
		CP0_Compare  = 11,
		CP0_Status   = 12,
		CP0_Cause    = 13,
		CP0_EPC      = 14,
		CP0_PRId     = 15,
		CP0_Config   = 16,
		CP0_LLAddr   = 17,
		CP0_WatchLo  = 18,
		CP0_WatchHi  = 19,
		CP0_XContext = 20,
		CP0_ECC      = 26,
		CP0_CacheErr = 27,
		CP0_TagLo    = 28,
		CP0_TagHi    = 29,
		CP0_ErrorEPC = 30,
	};

	enum cp0_sr_mask : u32
	{
		SR_IE    = 0x00000001, // interrupt enable
		SR_EXL   = 0x00000002, // exception level
		SR_ERL   = 0x00000004, // error level
		SR_KSU   = 0x00000018, // kernel/supervisor/user mode
		SR_UX    = 0x00000020, // 64-bit addressing user mode
		SR_SX    = 0x00000040, // 64-bit addressing supervisor mode
		SR_KX    = 0x00000080, // 64-bit addressing kernel mode
		SR_IMSW0 = 0x00000100, // software interrupt 0 enable
		SR_IMSW1 = 0x00000200, // software interrupt 1 enable
		SR_IMEX0 = 0x00000400, // external interrupt 0 enable
		SR_IMEX1 = 0x00000800, // external interrupt 1 enable
		SR_IMEX2 = 0x00001000, // external interrupt 2 enable
		SR_IMEX3 = 0x00002000, // external interrupt 3 enable
		SR_IMEX4 = 0x00004000, // external interrupt 4 enable
		SR_IMEX5 = 0x00008000, // external interrupt 5 enable
		SR_DE    = 0x00010000, // disable cache parity/ecc exceptions
		SR_CE    = 0x00020000, // cache ecc check enable
		SR_CH    = 0x00040000, // cache hit
		SR_SR    = 0x00100000, // soft reset
		SR_TS    = 0x00200000, // tlb shutdown (read only)
		SR_BEV   = 0x00400000, // bootstrap exception vectors
		SR_RE    = 0x02000000, // reverse endian
		SR_FR    = 0x04000000, // enable additional floating-point registers
		SU_RP    = 0x08000000, // reduced power
		SR_CU0   = 0x10000000, // coprocessor usable 0
		SR_CU1   = 0x20000000, // coprocessor usable 1
		SR_CU2   = 0x40000000, // coprocessor usable 2
		SR_CU3   = 0x80000000, // coprocessor usable 3

		SR_IMSW  = 0x00000300,
		SR_IM    = 0x0000ff00, // interrupt mask
		SR_DS    = 0x01ff0000, // diagnostic status
	};

	enum cp0_sr_ksu_mode : u32
	{
		SR_KSU_K = 0x00000000, // kernel mode
		SR_KSU_S = 0x00000008, // supervisor mode
		SR_KSU_U = 0x00000010, // user mode
	};

	enum cp0_cause_mask : u32
	{
		CAUSE_EXCCODE = 0x0000007c, // exception code
		CAUSE_IPSW0   = 0x00000100, // software interrupt 0 pending
		CAUSE_IPSW1   = 0x00000200, // software interrupt 1 pending
		CAUSE_IPEX0   = 0x00000400, // external interrupt 0 pending
		CAUSE_IPEX1   = 0x00000800, // external interrupt 1 pending
		CAUSE_IPEX2   = 0x00001000, // external interrupt 2 pending
		CAUSE_IPEX3   = 0x00002000, // external interrupt 3 pending
		CAUSE_IPEX4   = 0x00004000, // external interrupt 4 pending
		CAUSE_IPEX5   = 0x00008000, // external interrupt 5 pending
		CAUSE_CE      = 0x30000000, // coprocessor unit
		CAUSE_BD      = 0x80000000, // branch delay slot

		CAUSE_IPSW    = 0x00000300,
		CAUSE_IPHW    = 0x0000fc00,
		CAUSE_IP      = 0x0000ff00,
	};

	enum cp0_watchlo_mask : u32
	{
		WATCHLO_W      = 0x00000001, // trap on store
		WATCHLO_R      = 0x00000002, // trap on load
		WATCHLO_PADDR0 = 0xfffffff8, // physical address bits 31:3
	};
	enum cp0_watchhi_mask : u32
	{
		WATCHHI_PADDR1 = 0x0000000f, // physical address bits 35:32
	};

	enum cp0_tlb_mask : u64
	{
		TLB_MASK = 0x0000'0000'01ff'e000,
	};
	enum cp0_tlb_eh : u64
	{
		EH_ASID    = 0x0000'0000'0000'00ff, // address space id
		EH_G       = 0x0000'0000'0000'1000, // global (tlb only)
		EH_VPN2_32 = 0x0000'0000'ffff'e000, // virtual page number (32-bit mode)
		EH_VPN2_64 = 0x0000'00ff'ffff'e000, // virtual page number (64-bit mode)
		EH_R       = 0xc000'0000'0000'0000, // region (64-bit mode)
	};
	enum cp0_tlb_el : u64
	{
		EL_G   = 0x0000'0000'0000'0001, // global (entrylo only)
		EL_V   = 0x0000'0000'0000'0002, // valid
		EL_D   = 0x0000'0000'0000'0004, // dirty
		EL_C   = 0x0000'0000'0000'0038, // coherency
		EL_PFN = 0x0000'0000'3fff'ffc0, // page frame number
	};
	enum cp0_tlb_el_c : u64
	{
		C_0 = 0x00, // reserved
		C_1 = 0x08, // reserved
		C_2 = 0x10, // uncached
		C_3 = 0x18, // cacheable noncoherent (noncoherent)
		C_4 = 0x20, // cacheable coherent exclusive (exclusive)
		C_5 = 0x28, // cacheable coherent exclusive on write (sharable)
		C_6 = 0x30, // cacheable coherent update on write (update)
		C_7 = 0x38, // reserved
	};

	enum cp0_context_mask : u64
	{
		CONTEXT_PTEBASE = 0xffff'ffff'ff80'0000,
		CONTEXT_BADVPN2 = 0x0000'0000'007f'fff0,
	};

	enum cp0_xcontext_mask : u64
	{
		XCONTEXT_PTEBASE = 0xffff'fffe'0000'0000, // page table entry base
		XCONTEXT_R       = 0x0000'0001'8000'0000, // region
		XCONTEXT_BADVPN2 = 0x0000'0000'7fff'fff0, // bad virtual page number / 2
	};

	enum cp0_pagemask_mask : u32
	{
		PAGEMASK = 0x01ff'e000,
	};

	enum exception_mask : u32
	{
		EXCEPTION_INT   = 0x00000000, // interrupt
		EXCEPTION_MOD   = 0x00000004, // tlb modification
		EXCEPTION_TLBL  = 0x00000008, // tlb load
		EXCEPTION_TLBS  = 0x0000000c, // tlb store
		EXCEPTION_ADEL  = 0x00000010, // address error load
		EXCEPTION_ADES  = 0x00000014, // address error store
		EXCEPTION_IBE   = 0x00000018, // bus error (instruction fetch)
		EXCEPTION_DBE   = 0x0000001c, // bus error (data reference: load or store)
		EXCEPTION_SYS   = 0x00000020, // syscall
		EXCEPTION_BP    = 0x00000024, // breakpoint
		EXCEPTION_RI    = 0x00000028, // reserved instruction
		EXCEPTION_CPU   = 0x0000002c, // coprocessor unusable
		EXCEPTION_OV    = 0x00000030, // arithmetic overflow
		EXCEPTION_TR    = 0x00000034, // trap
		EXCEPTION_VCEI  = 0x00000038, // virtual coherency exception instruction
		EXCEPTION_FPE   = 0x0000003c, // floating point
		EXCEPTION_WATCH = 0x0000005c, // reference to watchhi/watchlo address
		EXCEPTION_VCED  = 0x0000007c, // virtual coherency exception data

		EXCEPTION_CP0   = 0x0000002c, // coprocessor 0 unusable
		EXCEPTION_CP1   = 0x1000002c, // coprocessor 1 unusable
		EXCEPTION_CP2   = 0x2000002c, // coprocessor 2 unusable
		EXCEPTION_CP3   = 0x3000002c, // coprocessor 3 unusable
	};

	enum cp1_fcr31_mask : u32
	{
		FCR31_RM = 0x00000003, // rounding mode

		FCR31_FI = 0x00000004, // inexact operation flag
		FCR31_FU = 0x00000008, // underflow flag
		FCR31_FO = 0x00000010, // overflow flag
		FCR31_FZ = 0x00000020, // divide by zero flag
		FCR31_FV = 0x00000040, // invalid operation flag

		FCR31_EI = 0x00000080, // inexact operation enable
		FCR31_EU = 0x00000100, // underflow enable
		FCR31_EO = 0x00000200, // overflow enable
		FCR31_EZ = 0x00000400, // divide by zero enable
		FCR31_EV = 0x00000800, // invalid operation enable

		FCR31_CI = 0x00001000, // inexact operation cause
		FCR31_CU = 0x00002000, // underflow cause
		FCR31_CO = 0x00004000, // overflow cause
		FCR31_CZ = 0x00008000, // divide by zero cause
		FCR31_CV = 0x00010000, // invalid operation cause
		FCR31_CE = 0x00020000, // unimplemented operation cause

		FCR31_C  = 0x00800000, // condition
		FCR31_FS = 0x01000000, // flush denormalized results

		FCR31_FM = 0x0000007c, // flag mask
		FCR31_EM = 0x00000f80, // enable mask
		FCR31_CM = 0x0001f000, // cause mask (except unimplemented)
	};

	enum mips3_registers : unsigned
	{
		MIPS3_R0  = 0,
		MIPS3_CP0 = 32,
		MIPS3_F0  = 64,

		MIPS3_PC  = 96,
		MIPS3_HI,
		MIPS3_LO,
		MIPS3_FCR30,
		MIPS3_FCR31,
	};

	enum cp0_taglo_mask : u32
	{
		TAGLO_PTAGLO = 0xffffff00, // physical adddress bits 35:12
		TAGLO_PSTATE = 0x000000c0, // primary cache state
		TAGLO_P      = 0x00000001, // primary tag even parity
	};
	enum icache_mask : u32
	{
		ICACHE_PTAG = 0x00ffffff, // physical tag
		ICACHE_V    = 0x01000000, // valid
		ICACHE_P    = 0x02000000, // even parity
	};
	enum dcache_mask : u32
	{
		DCACHE_PTAG = 0x00ffffff, // physical tag
		DCACHE_CS   = 0x01000000, // primary cache state
		DCACHE_P    = 0x02000000, // even parity for ptag and cs
		DCACHE_W    = 0x02000000, // write-back
		DCACHE_WP   = 0x02000000, // even parity for write-back
	};

	// device_t overrides
	virtual void device_start() override;
	virtual void device_reset() override;
	virtual void device_stop() override;

	// device_memory_interface overrides
	virtual space_config_vector memory_space_config() const override;
	virtual bool memory_translate(int spacenum, int intention, offs_t &address) override;

	// device_disasm_interface overrides
	virtual std::unique_ptr<util::disasm_interface> create_disassembler() override;

	// device_execute_interface overrides
	virtual u32 execute_min_cycles() const override { return 1; }
	virtual u32 execute_max_cycles() const override { return 40; }
	virtual u32 execute_input_lines() const override { return 6; }
	virtual void execute_run() override;
	virtual void execute_set_input(int inputnum, int state) override;

	// cpu implementation
	void cpu_execute(u32 const op);
	void cpu_exception(u32 exception, u16 const vector = 0x180);
	void cpu_lwl(u32 const op);
	void cpu_lwr(u32 const op);
	void cpu_ldl(u32 const op);
	void cpu_ldr(u32 const op);
	void cpu_swl(u32 const op);
	void cpu_swr(u32 const op);
	void cpu_sdl(u32 const op);
	void cpu_sdr(u32 const op);

	// cp0 implementation
	void cp0_execute(u32 const op);
	u64 cp0_get(unsigned const reg);
	void cp0_set(unsigned const reg, u64 const data);
	void cp0_tlbr();
	void cp0_tlbwi(u8 const index);
	void cp0_tlbwr();
	void cp0_tlbp();

	// cp0 helpers
	TIMER_CALLBACK_MEMBER(cp0_timer_callback);
	void cp0_update_timer(bool start = false);
	bool cp0_64() const;

	// cp1 implementation
	void cp1_execute(u32 const op);
	void cp1_set(unsigned const reg, u64 const data);

	// cp2 implementation
	void cp2_execute(u32 const op);

	// address and memory handling
	enum translate_t { ERROR, MISS, UNCACHED, CACHED };
	translate_t translate(int intention, u64 &address);
	void address_error(int intention, u64 const address);

	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(T)>>::value, bool> load(u64 program_address, U &&apply);
	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, std::function<void(u64, T)>>::value, bool> load_linked(u64 program_address, U &&apply);
	template <typename T, typename U> std::enable_if_t<std::is_convertible<U, T>::value, bool> store(u64 program_address, U data, T mem_mask = ~T(0));
	bool fetch(u64 address, std::function<void(u32)> &&apply);

	// debugging helpers
	std::string debug_string(u64 string_pointer, unsigned limit = 0);
	std::string debug_string_array(u64 array_pointer);
	std::string debug_unicode_string(u64 unicode_string_pointer);

	// device configuration state
	address_space_config m_program_config_le;
	address_space_config m_program_config_be;

	// runtime state
	int m_icount;

	// cpu state
	u64 m_pc;
	u64 m_r[32];
	u64 m_hi;
	u64 m_lo;
	enum branch_state_t : unsigned
	{
		NONE      = 0,
		DELAY     = 1, // delay slot instruction active
		BRANCH    = 2, // branch instruction active
		EXCEPTION = 3, // exception triggered
		NULLIFY   = 4, // next instruction nullified
	}
	m_branch_state;
	u64 m_branch_target;

	// cp0 state
	u64 m_cp0[32];
	u64 m_cp0_timer_zero;
	emu_timer *m_cp0_timer;
	memory_passthrough_handler *m_ll_watch;
	struct tlb_entry_t
	{
		u64 mask;
		u64 vpn;
		u64 pfn[2];

		unsigned low_bit;
	}
	m_tlb[48];
	unsigned m_tlb_mru[3][48];

	// cp1 state
	u64 m_f[32]; // floating point registers
	u32 m_fcr0;  // implementation and revision register
	u32 m_fcr30; // unknown
	u32 m_fcr31; // control/status register

	// experimental icache state
	u32 m_icache_mask_hi;
	u32 m_icache_mask_lo;
	unsigned m_icache_line_size;
	unsigned m_icache_shift;
	std::unique_ptr<u32[]> m_icache_tag;
	std::unique_ptr<u32[]> m_icache_data;

	// statistics
	u64 m_tlb_scans;
	u64 m_tlb_loops;
	u64 m_icache_hits;
	u64 m_icache_misses;
};

class r4000_device : public r4000_base_device
{
public:
	// NOTE: R4000 chips prior to 3.0 have an xtlb bug
	r4000_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
		: r4000_base_device(mconfig, R4000, tag, owner, clock, 0x0430, CACHE_8K, CACHE_8K)
	{
		// no secondary cache
		m_cp0[CP0_Config] |= CONFIG_SC;
	}
};

class r4400_device : public r4000_base_device
{
public:
	r4400_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
		: r4000_base_device(mconfig, R4400, tag, owner, clock, 0x0440, CACHE_16K, CACHE_16K)
	{
		// no secondary cache
		m_cp0[CP0_Config] |= CONFIG_SC;
	}
};

class r4600_device : public r4000_base_device
{
public:
	r4600_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
		: r4000_base_device(mconfig, R4600, tag, owner, clock, 0x2000, CACHE_16K, CACHE_16K)
	{
		// no secondary cache
		m_cp0[CP0_Config] |= CONFIG_SC;
	}
};
#endif // MAME_CPU_MIPS_R4000_H