1 |
/* |
/* |
2 |
* Copyright (C) 2003-2006 Anders Gavare. All rights reserved. |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
* modification, are permitted provided that the following conditions are met: |
25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_ps2_stuff.c,v 1.28 2006/03/04 12:38:48 debug Exp $ |
* $Id: dev_ps2_stuff.c,v 1.31 2007/02/16 16:48:07 debug Exp $ |
29 |
* |
* |
30 |
* Playstation 2 misc. stuff: |
* Playstation 2 misc. stuff: |
31 |
* |
* |
32 |
* offset 0x0000 timer control |
* offset 0x0000 timer control |
33 |
* offset 0x8000 DMA controller |
* offset 0x8000 DMA controller |
34 |
* offset 0xf000 Interrupt register |
* offset 0xf000 Interrupt register |
35 |
|
* |
36 |
|
* The 16 normal PS2 interrupts interrupt at MIPS interrupt 2. |
37 |
|
* The 16 DMA interrupts are connected to MIPS interrupt 3. |
38 |
|
* |
39 |
|
* SBUS interrupts go via PS2 interrupt 1. |
40 |
*/ |
*/ |
41 |
|
|
42 |
#include <stdio.h> |
#include <stdio.h> |
44 |
#include <string.h> |
#include <string.h> |
45 |
|
|
46 |
#include "cpu.h" |
#include "cpu.h" |
47 |
#include "devices.h" |
#include "device.h" |
48 |
#include "machine.h" |
#include "machine.h" |
49 |
#include "memory.h" |
#include "memory.h" |
50 |
#include "misc.h" |
#include "misc.h" |
57 |
/* NOTE/TODO: This should be the same as in ps2_gs: */ |
/* NOTE/TODO: This should be the same as in ps2_gs: */ |
58 |
#define DEV_PS2_GIF_FAKE_BASE 0x50000000 |
#define DEV_PS2_GIF_FAKE_BASE 0x50000000 |
59 |
|
|
60 |
|
#define N_PS2_DMA_CHANNELS 10 |
61 |
|
#define N_PS2_TIMERS 4 |
62 |
|
|
63 |
/* |
struct ps2_data { |
64 |
* dev_ps2_stuff_tick(): |
uint32_t timer_count[N_PS2_TIMERS]; |
65 |
*/ |
uint32_t timer_comp[N_PS2_TIMERS]; |
66 |
void dev_ps2_stuff_tick(struct cpu *cpu, void *extra) |
uint32_t timer_mode[N_PS2_TIMERS]; |
67 |
|
uint32_t timer_hold[N_PS2_TIMERS]; |
68 |
|
/* NOTE: only 0 and 1 are valid */ |
69 |
|
struct interrupt timer_irq[N_PS2_TIMERS]; |
70 |
|
|
71 |
|
uint64_t dmac_reg[DMAC_REGSIZE / 0x10]; |
72 |
|
struct interrupt dmac_irq; /* MIPS irq 3 */ |
73 |
|
struct interrupt dma_channel2_irq; /* irq path of channel 2 */ |
74 |
|
|
75 |
|
uint64_t other_memory_base[N_PS2_DMA_CHANNELS]; |
76 |
|
|
77 |
|
uint32_t intr; |
78 |
|
uint32_t imask; |
79 |
|
uint32_t sbus_smflg; |
80 |
|
struct interrupt intr_irq; /* MIPS irq 2 */ |
81 |
|
struct interrupt sbus_irq; /* PS2 irq 1 */ |
82 |
|
}; |
83 |
|
|
84 |
|
#define DEV_PS2_LENGTH 0x10000 |
85 |
|
|
86 |
|
|
87 |
|
void ps2_intr_interrupt_assert(struct interrupt *interrupt) |
88 |
|
{ |
89 |
|
struct ps2_data *d = interrupt->extra; |
90 |
|
d->intr |= (1 << interrupt->line); |
91 |
|
if (d->intr & d->imask) |
92 |
|
INTERRUPT_ASSERT(d->intr_irq); |
93 |
|
} |
94 |
|
void ps2_intr_interrupt_deassert(struct interrupt *interrupt) |
95 |
|
{ |
96 |
|
struct ps2_data *d = interrupt->extra; |
97 |
|
d->intr &= ~(1 << interrupt->line); |
98 |
|
if (!(d->intr & d->imask)) |
99 |
|
INTERRUPT_DEASSERT(d->intr_irq); |
100 |
|
} |
101 |
|
void ps2_dmac_interrupt_assert(struct interrupt *interrupt) |
102 |
|
{ |
103 |
|
struct ps2_data *d = interrupt->extra; |
104 |
|
d->dmac_reg[0x601] |= (1 << interrupt->line); |
105 |
|
/* TODO: DMA interrupt mask? */ |
106 |
|
if (d->dmac_reg[0x601] & 0xffff) |
107 |
|
INTERRUPT_ASSERT(d->dmac_irq); |
108 |
|
} |
109 |
|
void ps2_dmac_interrupt_deassert(struct interrupt *interrupt) |
110 |
|
{ |
111 |
|
struct ps2_data *d = interrupt->extra; |
112 |
|
d->dmac_reg[0x601] &= ~(1 << interrupt->line); |
113 |
|
/* TODO: DMA interrupt mask? */ |
114 |
|
if (!(d->dmac_reg[0x601] & 0xffff)) |
115 |
|
INTERRUPT_DEASSERT(d->dmac_irq); |
116 |
|
} |
117 |
|
void ps2_sbus_interrupt_assert(struct interrupt *interrupt) |
118 |
|
{ |
119 |
|
/* Note: sbus irq 0 = mask 0x100, sbus irq 1 = mask 0x400 */ |
120 |
|
struct ps2_data *d = interrupt->extra; |
121 |
|
d->sbus_smflg |= (1 << (8 + interrupt->line * 2)); |
122 |
|
/* TODO: SBUS interrupt mask? */ |
123 |
|
if (d->sbus_smflg != 0) |
124 |
|
INTERRUPT_ASSERT(d->sbus_irq); |
125 |
|
} |
126 |
|
void ps2_sbus_interrupt_deassert(struct interrupt *interrupt) |
127 |
|
{ |
128 |
|
/* Note: sbus irq 0 = mask 0x100, sbus irq 1 = mask 0x400 */ |
129 |
|
struct ps2_data *d = interrupt->extra; |
130 |
|
d->sbus_smflg &= ~(1 << (8 + interrupt->line * 2)); |
131 |
|
/* TODO: SBUS interrupt mask? */ |
132 |
|
if (d->sbus_smflg == 0) |
133 |
|
INTERRUPT_DEASSERT(d->sbus_irq); |
134 |
|
} |
135 |
|
|
136 |
|
|
137 |
|
DEVICE_TICK(ps2) |
138 |
{ |
{ |
139 |
struct ps2_data *d = extra; |
struct ps2_data *d = extra; |
140 |
int i; |
int i; |
153 |
if (d->timer_mode[i] & T_MODE_ZRET) |
if (d->timer_mode[i] & T_MODE_ZRET) |
154 |
d->timer_count[i] = 0; |
d->timer_count[i] = 0; |
155 |
|
|
156 |
/* irq 9 is timer0, etc. */ |
INTERRUPT_ASSERT(d->timer_irq[i]); |
|
cpu_interrupt(cpu, 8 + 9 + i); |
|
157 |
|
|
158 |
/* timer 1..3 are "single-shot"? TODO */ |
/* timer 1..3 are "single-shot"? TODO */ |
159 |
if (i > 0) { |
if (i > 0) { |
165 |
} |
} |
166 |
|
|
167 |
|
|
168 |
/* |
DEVICE_ACCESS(ps2) |
|
* dev_ps2_stuff_access(): |
|
|
*/ |
|
|
DEVICE_ACCESS(ps2_stuff) |
|
169 |
{ |
{ |
170 |
uint64_t idata = 0, odata = 0; |
uint64_t idata = 0, odata = 0; |
171 |
int regnr = 0; |
int regnr = 0; |
201 |
/* :-) TODO: remove this? */ |
/* :-) TODO: remove this? */ |
202 |
d->timer_count[timer_nr] ++; |
d->timer_count[timer_nr] ++; |
203 |
} |
} |
204 |
debug("[ ps2_stuff: read timer %i count: 0x%llx ]\n", |
debug("[ ps2: read timer %i count: 0x%llx ]\n", |
205 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
206 |
} else { |
} else { |
207 |
d->timer_count[timer_nr] = idata; |
d->timer_count[timer_nr] = idata; |
208 |
debug("[ ps2_stuff: write timer %i count: 0x%llx ]\n", |
debug("[ ps2: write timer %i count: 0x%llx ]\n", |
209 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
210 |
} |
} |
211 |
break; |
break; |
212 |
case 0x0010: /* timer mode */ |
case 0x0010: /* timer mode */ |
213 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
214 |
odata = d->timer_mode[timer_nr]; |
odata = d->timer_mode[timer_nr]; |
215 |
debug("[ ps2_stuff: read timer %i mode: 0x%llx ]\n", |
debug("[ ps2: read timer %i mode: 0x%llx ]\n", |
216 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
217 |
} else { |
} else { |
218 |
d->timer_mode[timer_nr] = idata; |
d->timer_mode[timer_nr] = idata; |
219 |
debug("[ ps2_stuff: write timer %i mode: 0x%llx ]\n", |
debug("[ ps2: write timer %i mode: 0x%llx ]\n", |
220 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
221 |
} |
} |
222 |
break; |
break; |
223 |
case 0x0020: /* timer comp */ |
case 0x0020: /* timer comp */ |
224 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
225 |
odata = d->timer_comp[timer_nr]; |
odata = d->timer_comp[timer_nr]; |
226 |
debug("[ ps2_stuff: read timer %i comp: 0x%llx ]\n", |
debug("[ ps2: read timer %i comp: 0x%llx ]\n", |
227 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
228 |
} else { |
} else { |
229 |
d->timer_comp[timer_nr] = idata; |
d->timer_comp[timer_nr] = idata; |
230 |
debug("[ ps2_stuff: write timer %i comp: 0x%llx ]\n", |
debug("[ ps2: write timer %i comp: 0x%llx ]\n", |
231 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
232 |
} |
} |
233 |
break; |
break; |
234 |
case 0x0030: /* timer hold */ |
case 0x0030: /* timer hold */ |
235 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
236 |
odata = d->timer_hold[timer_nr]; |
odata = d->timer_hold[timer_nr]; |
237 |
debug("[ ps2_stuff: read timer %i hold: 0x%llx ]\n", |
debug("[ ps2: read timer %i hold: 0x%llx ]\n", |
238 |
timer_nr, (long long)odata); |
timer_nr, (long long)odata); |
239 |
if (timer_nr >= 2) |
if (timer_nr >= 2) |
240 |
fatal("[ WARNING: ps2_stuff: read from non-" |
fatal("[ WARNING: ps2: read from non-" |
241 |
"existant timer %i hold register ]\n"); |
"existant timer %i hold register ]\n"); |
242 |
} else { |
} else { |
243 |
d->timer_hold[timer_nr] = idata; |
d->timer_hold[timer_nr] = idata; |
244 |
debug("[ ps2_stuff: write timer %i hold: 0x%llx ]\n", |
debug("[ ps2: write timer %i hold: 0x%llx ]\n", |
245 |
timer_nr, (long long)idata); |
timer_nr, (long long)idata); |
246 |
if (timer_nr >= 2) |
if (timer_nr >= 2) |
247 |
fatal("[ WARNING: ps2_stuff: write to " |
fatal("[ WARNING: ps2: write to " |
248 |
"non-existant timer %i hold register ]\n", |
"non-existant timer %i hold register ]\n", |
249 |
timer_nr); |
timer_nr); |
250 |
} |
} |
253 |
case 0x8000 + D2_CHCR_REG: |
case 0x8000 + D2_CHCR_REG: |
254 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
255 |
odata = d->dmac_reg[regnr]; |
odata = d->dmac_reg[regnr]; |
256 |
/* debug("[ ps2_stuff: dmac read from D2_CHCR " |
/* debug("[ ps2: dmac read from D2_CHCR " |
257 |
"(0x%llx) ]\n", (long long)d->dmac_reg[regnr]); */ |
"(0x%llx) ]\n", (long long)d->dmac_reg[regnr]); */ |
258 |
} else { |
} else { |
259 |
/* debug("[ ps2_stuff: dmac write to D2_CHCR, " |
/* debug("[ ps2: dmac write to D2_CHCR, " |
260 |
"data 0x%016llx ]\n", (long long) idata); */ |
"data 0x%016llx ]\n", (long long) idata); */ |
261 |
if (idata & D_CHCR_STR) { |
if (idata & D_CHCR_STR) { |
262 |
int length = d->dmac_reg[D2_QWC_REG/0x10] * 16; |
int length = d->dmac_reg[D2_QWC_REG/0x10] * 16; |
266 |
D2_TADR_REG/0x10]; |
D2_TADR_REG/0x10]; |
267 |
unsigned char *copy_buf; |
unsigned char *copy_buf; |
268 |
|
|
269 |
debug("[ ps2_stuff: dmac [ch2] transfer addr=" |
debug("[ ps2: dmac [ch2] transfer addr=" |
270 |
"0x%016llx len=0x%lx ]\n", (long long) |
"0x%016llx len=0x%lx ]\n", (long long) |
271 |
d->dmac_reg[D2_MADR_REG/0x10], |
d->dmac_reg[D2_MADR_REG/0x10], |
272 |
(long)length); |
(long)length); |
274 |
copy_buf = malloc(length); |
copy_buf = malloc(length); |
275 |
if (copy_buf == NULL) { |
if (copy_buf == NULL) { |
276 |
fprintf(stderr, "out of memory in " |
fprintf(stderr, "out of memory in " |
277 |
"dev_ps2_stuff_access()\n"); |
"dev_ps2_access()\n"); |
278 |
exit(1); |
exit(1); |
279 |
} |
} |
280 |
cpu->memory_rw(cpu, cpu->mem, from_addr, |
cpu->memory_rw(cpu, cpu->mem, from_addr, |
291 |
idata &= ~D_CHCR_STR; |
idata &= ~D_CHCR_STR; |
292 |
|
|
293 |
/* interrupt DMA channel 2 */ |
/* interrupt DMA channel 2 */ |
294 |
cpu_interrupt(cpu, 8 + 16 + 2); |
INTERRUPT_ASSERT(d->dma_channel2_irq); |
295 |
} else |
} else |
296 |
debug("[ ps2_stuff: dmac [ch2] stopping " |
debug("[ ps2: dmac [ch2] stopping " |
297 |
"transfer ]\n"); |
"transfer ]\n"); |
298 |
d->dmac_reg[regnr] = idata; |
d->dmac_reg[regnr] = idata; |
299 |
return 1; |
return 1; |
316 |
d->dmac_reg[regnr] |= oldmask; |
d->dmac_reg[regnr] |= oldmask; |
317 |
if (((d->dmac_reg[regnr] & 0xffff) & |
if (((d->dmac_reg[regnr] & 0xffff) & |
318 |
((d->dmac_reg[regnr]>>16) & 0xffff)) == 0) { |
((d->dmac_reg[regnr]>>16) & 0xffff)) == 0) { |
319 |
/* irq 3 is the DMAC */ |
INTERRUPT_DEASSERT(d->dmac_irq); |
|
cpu_interrupt_ack(cpu, 3); |
|
320 |
} |
} |
321 |
} else { |
} else { |
322 |
/* Hm... make it seem like the mask bits are (at |
/* Hm... make it seem like the mask bits are (at |
329 |
case 0xf000: /* interrupt register */ |
case 0xf000: /* interrupt register */ |
330 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
331 |
odata = d->intr; |
odata = d->intr; |
332 |
debug("[ ps2_stuff: read from Interrupt Register:" |
debug("[ ps2: read from Interrupt Register:" |
333 |
" 0x%llx ]\n", (long long)odata); |
" 0x%llx ]\n", (long long)odata); |
334 |
|
|
335 |
/* TODO: This is _NOT_ correct behavior: */ |
/* TODO: This is _NOT_ correct behavior: */ |
336 |
d->intr = 0; |
// d->intr = 0; |
337 |
cpu_interrupt_ack(cpu, 2); |
// INTERRUPT_DEASSERT(d->intr_irq); |
338 |
} else { |
} else { |
339 |
debug("[ ps2_stuff: write to Interrupt Register: " |
debug("[ ps2: write to Interrupt Register: " |
340 |
"0x%llx ]\n", (long long)idata); |
"0x%llx ]\n", (long long)idata); |
341 |
/* Clear out bits that are set in idata: */ |
/* Clear out bits that are set in idata: */ |
342 |
d->intr &= ~idata; |
d->intr &= ~idata; |
343 |
|
|
344 |
if ((d->intr & d->imask) == 0) |
if ((d->intr & d->imask) == 0) |
345 |
cpu_interrupt_ack(cpu, 2); |
INTERRUPT_DEASSERT(d->intr_irq); |
346 |
} |
} |
347 |
break; |
break; |
348 |
|
|
349 |
case 0xf010: /* interrupt mask */ |
case 0xf010: /* interrupt mask */ |
350 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
351 |
odata = d->imask; |
odata = d->imask; |
352 |
/* debug("[ ps2_stuff: read from Interrupt Mask " |
/* debug("[ ps2: read from Interrupt Mask " |
353 |
"Register: 0x%llx ]\n", (long long)odata); */ |
"Register: 0x%llx ]\n", (long long)odata); */ |
354 |
} else { |
} else { |
355 |
/* debug("[ ps2_stuff: write to Interrupt Mask " |
/* debug("[ ps2: write to Interrupt Mask " |
356 |
"Register: 0x%llx ]\n", (long long)idata); */ |
"Register: 0x%llx ]\n", (long long)idata); */ |
357 |
d->imask = idata; |
/* Note: written value indicates which bits |
358 |
|
to _toggle_, not which bits to set! */ |
359 |
|
d->imask ^= idata; |
360 |
} |
} |
361 |
break; |
break; |
362 |
|
|
363 |
case 0xf230: /* sbus interrupt register? */ |
case 0xf230: /* sbus interrupt register? */ |
364 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) { |
365 |
odata = d->sbus_smflg; |
odata = d->sbus_smflg; |
366 |
debug("[ ps2_stuff: read from SBUS SMFLG:" |
debug("[ ps2: read from SBUS SMFLG:" |
367 |
" 0x%llx ]\n", (long long)odata); |
" 0x%llx ]\n", (long long)odata); |
368 |
} else { |
} else { |
369 |
/* Clear bits on write: */ |
/* Clear bits on write: */ |
370 |
debug("[ ps2_stuff: write to SBUS SMFLG:" |
debug("[ ps2: write to SBUS SMFLG:" |
371 |
" 0x%llx ]\n", (long long)idata); |
" 0x%llx ]\n", (long long)idata); |
372 |
d->sbus_smflg &= ~idata; |
d->sbus_smflg &= ~idata; |
373 |
/* irq 1 is SBUS */ |
/* irq 1 is SBUS */ |
374 |
if (d->sbus_smflg == 0) |
if (d->sbus_smflg == 0) |
375 |
cpu_interrupt_ack(cpu, 8 + 1); |
INTERRUPT_DEASSERT(d->sbus_irq); |
376 |
} |
} |
377 |
break; |
break; |
378 |
default: |
default: |
379 |
if (writeflag==MEM_READ) { |
if (writeflag==MEM_READ) { |
380 |
debug("[ ps2_stuff: read from addr 0x%x: 0x%llx ]\n", |
debug("[ ps2: read from addr 0x%x: 0x%llx ]\n", |
381 |
(int)relative_addr, (long long)odata); |
(int)relative_addr, (long long)odata); |
382 |
} else { |
} else { |
383 |
debug("[ ps2_stuff: write to addr 0x%x: 0x%llx ]\n", |
debug("[ ps2: write to addr 0x%x: 0x%llx ]\n", |
384 |
(int)relative_addr, (long long)idata); |
(int)relative_addr, (long long)idata); |
385 |
} |
} |
386 |
} |
} |
392 |
} |
} |
393 |
|
|
394 |
|
|
395 |
/* |
DEVINIT(ps2) |
|
* dev_ps2_stuff_init(): |
|
|
*/ |
|
|
struct ps2_data *dev_ps2_stuff_init(struct machine *machine, |
|
|
struct memory *mem, uint64_t baseaddr) |
|
396 |
{ |
{ |
397 |
struct ps2_data *d; |
struct ps2_data *d; |
398 |
|
int i; |
399 |
|
struct interrupt template; |
400 |
|
char n[300]; |
401 |
|
|
402 |
d = malloc(sizeof(struct ps2_data)); |
d = malloc(sizeof(struct ps2_data)); |
403 |
if (d == NULL) { |
if (d == NULL) { |
408 |
|
|
409 |
d->other_memory_base[DMA_CH_GIF] = DEV_PS2_GIF_FAKE_BASE; |
d->other_memory_base[DMA_CH_GIF] = DEV_PS2_GIF_FAKE_BASE; |
410 |
|
|
411 |
memory_device_register(mem, "ps2_stuff", baseaddr, |
/* Connect to MIPS irq 2 (interrupt controller) and 3 (dmac): */ |
412 |
DEV_PS2_STUFF_LENGTH, dev_ps2_stuff_access, d, DM_DEFAULT, NULL); |
snprintf(n, sizeof(n), "%s.2", devinit->interrupt_path); |
413 |
machine_add_tickfunction(machine, |
INTERRUPT_CONNECT(n, d->intr_irq); |
414 |
dev_ps2_stuff_tick, d, TICK_STEPS_SHIFT, 0.0); |
snprintf(n, sizeof(n), "%s.3", devinit->interrupt_path); |
415 |
|
INTERRUPT_CONNECT(n, d->dmac_irq); |
416 |
|
|
417 |
return d; |
/* |
418 |
|
* Register interrupts: |
419 |
|
* |
420 |
|
* 16 normal IRQs (emul[x].machine[x].cpu[x].ps2_intr.%i) |
421 |
|
* 16 DMA IRQs (emul[x].machine[x].cpu[x].ps2_dmac.%i) |
422 |
|
* 2 sbus IRQs (emul[x].machine[x].cpu[x].ps2_sbus.%i) |
423 |
|
*/ |
424 |
|
for (i=0; i<16; i++) { |
425 |
|
snprintf(n, sizeof(n), "%s.ps2_intr.%i", |
426 |
|
devinit->interrupt_path, i); |
427 |
|
memset(&template, 0, sizeof(template)); |
428 |
|
template.line = i; |
429 |
|
template.name = n; |
430 |
|
template.extra = d; |
431 |
|
template.interrupt_assert = ps2_intr_interrupt_assert; |
432 |
|
template.interrupt_deassert = ps2_intr_interrupt_deassert; |
433 |
|
interrupt_handler_register(&template); |
434 |
|
} |
435 |
|
for (i=0; i<16; i++) { |
436 |
|
snprintf(n, sizeof(n), "%s.ps2_dmac.%i", |
437 |
|
devinit->interrupt_path, i); |
438 |
|
memset(&template, 0, sizeof(template)); |
439 |
|
template.line = i; |
440 |
|
template.name = n; |
441 |
|
template.extra = d; |
442 |
|
template.interrupt_assert = ps2_dmac_interrupt_assert; |
443 |
|
template.interrupt_deassert = ps2_dmac_interrupt_deassert; |
444 |
|
interrupt_handler_register(&template); |
445 |
|
} |
446 |
|
for (i=0; i<2; i++) { |
447 |
|
snprintf(n, sizeof(n), "%s.ps2_sbus.%i", |
448 |
|
devinit->interrupt_path, i); |
449 |
|
memset(&template, 0, sizeof(template)); |
450 |
|
template.line = i; |
451 |
|
template.name = n; |
452 |
|
template.extra = d; |
453 |
|
template.interrupt_assert = ps2_sbus_interrupt_assert; |
454 |
|
template.interrupt_deassert = ps2_sbus_interrupt_deassert; |
455 |
|
interrupt_handler_register(&template); |
456 |
|
} |
457 |
|
|
458 |
|
/* Connect to DMA channel 2 irq: */ |
459 |
|
snprintf(n, sizeof(n), "%s.ps2_dmac.2", devinit->interrupt_path); |
460 |
|
INTERRUPT_CONNECT(n, d->dma_channel2_irq); |
461 |
|
|
462 |
|
/* Connect to SBUS interrupt, at ps2 interrupt 1: */ |
463 |
|
snprintf(n, sizeof(n), "%s.ps2_intr.1", devinit->interrupt_path); |
464 |
|
INTERRUPT_CONNECT(n, d->sbus_irq); |
465 |
|
|
466 |
|
/* Connect to the timers' interrupts: */ |
467 |
|
for (i=0; i<N_PS2_TIMERS; i++) { |
468 |
|
/* PS2 irq 9 is timer0, etc. */ |
469 |
|
snprintf(n, sizeof(n), "%s.ps2_intr.%i", |
470 |
|
devinit->interrupt_path, 9 + i); |
471 |
|
INTERRUPT_CONNECT(n, d->timer_irq[i]); |
472 |
|
} |
473 |
|
|
474 |
|
memory_device_register(devinit->machine->memory, "ps2", devinit->addr, |
475 |
|
DEV_PS2_LENGTH, dev_ps2_access, d, DM_DEFAULT, NULL); |
476 |
|
machine_add_tickfunction(devinit->machine, |
477 |
|
dev_ps2_tick, d, TICK_STEPS_SHIFT, 0.0); |
478 |
|
|
479 |
|
return 1; |
480 |
} |
} |
481 |
|
|