1 /*
2  *  Copyright (C) 2006-2011  Anders Gavare.  All rights reserved.
3  *
4  *  Redistribution and use in source and binary forms, with or without
5  *  modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. The name of the author may not be used to endorse or promote products
13  *     derived from this software without specific prior written permission.
14  *
15  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  *  SUCH DAMAGE.
26  *
27  *
28  *  COMMENT: Dreamcast-specific ASIC
29  *
30  *  A simple device which forwards various Dreamcast device events as
31  *  interrupts 13, 11, or 9, to the CPU.
32  */
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/time.h>
38 
39 #include "cpu.h"
40 #include "device.h"
41 #include "machine.h"
42 #include "memory.h"
43 #include "misc.h"
44 
45 #include "thirdparty/dreamcast_sysasicvar.h"
46 #include "thirdparty/sh4_exception.h"
47 
48 
49 // #define debug fatal
50 
51 #define	DREAMCAST_ASIC_TICK_SHIFT	15
52 
53 struct dreamcast_asic_data {
54 	uint32_t	pending_irq[3];
55 	uint32_t	mask_13[3];
56 	uint32_t	mask_11[3];
57 	uint32_t	mask_9[3];
58 
59 	int		asserted_13;
60 	int		asserted_11;
61 	int		asserted_9;
62 
63 	struct interrupt irq_13;
64 	struct interrupt irq_11;
65 	struct interrupt irq_9;
66 };
67 
68 
DEVICE_TICK(dreamcast_asic)69 DEVICE_TICK(dreamcast_asic)
70 {
71 	struct dreamcast_asic_data *d = (struct dreamcast_asic_data *) extra;
72 	int i, old_asserted_13 = d->asserted_13, old_asserted_11 =
73 	    d->asserted_11, old_asserted_9 = d->asserted_9;
74 
75 	d->asserted_13 = d->asserted_11 = d->asserted_9 = 0;
76 
77 	for (i=0; i<3; i++) {
78 		if (d->pending_irq[i] & d->mask_13[i])
79 			d->asserted_13 = 1;
80 
81 		if (d->pending_irq[i] & d->mask_11[i])
82 			d->asserted_11 = 1;
83 
84 		if (d->pending_irq[i] & d->mask_9[i])
85 			d->asserted_9 = 1;
86 	}
87 
88 	if (d->asserted_13 != old_asserted_13) {
89 		if (d->asserted_13)
90 			INTERRUPT_ASSERT(d->irq_13);
91 		else
92 			INTERRUPT_DEASSERT(d->irq_13);
93 	}
94 	if (d->asserted_11 != old_asserted_11) {
95 		if (d->asserted_11)
96 			INTERRUPT_ASSERT(d->irq_11);
97 		else
98 			INTERRUPT_DEASSERT(d->irq_11);
99 	}
100 	if (d->asserted_9 != old_asserted_9) {
101 		if (d->asserted_9)
102 			INTERRUPT_ASSERT(d->irq_9);
103 		else
104 			INTERRUPT_DEASSERT(d->irq_9);
105 	}
106 }
107 
108 
DEVICE_ACCESS(dreamcast_asic)109 DEVICE_ACCESS(dreamcast_asic)
110 {
111 	struct dreamcast_asic_data *d = (struct dreamcast_asic_data *) extra;
112 	uint64_t idata = 0, odata = 0;
113 	int r;
114 
115 	if (writeflag == MEM_WRITE)
116 		idata = memory_readmax64(cpu, data, len);
117 
118 	r = (relative_addr / 4) & 3;
119 	if (r == 3) {
120 		fatal("[ dreamcast_asic: Bad address ]\n");
121 		r = 0;
122 		exit(1);	// TODO?
123 	}
124 
125 	switch (relative_addr) {
126 
127 	case 0:
128 	case 4:
129 	case 8:	if (writeflag == MEM_READ) {
130 			odata = d->pending_irq[r];
131 		} else {
132 			/*  Should only be used interally by GXemul:  */
133 			if (idata & 0x100000000ULL) {
134 				/*  Set specific bits:  */
135 				d->pending_irq[r] |= idata;
136 			} else {
137 				/*  Clear interrupt assertions:  */
138 				d->pending_irq[r] &= ~idata;
139 			}
140 			dev_dreamcast_asic_tick(cpu, d);
141 		}
142 		break;
143 
144 	case 0x10:
145 	case 0x14:
146 	case 0x18:
147 		if (writeflag == MEM_WRITE) {
148 			d->mask_13[r] = idata;
149 			dev_dreamcast_asic_tick(cpu, d);
150 		} else {
151 			odata = d->mask_13[r];
152 		}
153 		break;
154 
155 	case 0x20:
156 	case 0x24:
157 	case 0x28:
158 		if (writeflag == MEM_WRITE) {
159 			d->mask_11[r] = idata;
160 			dev_dreamcast_asic_tick(cpu, d);
161 		} else {
162 			odata = d->mask_11[r];
163 		}
164 		break;
165 
166 	case 0x30:
167 	case 0x34:
168 	case 0x38:
169 		if (writeflag == MEM_WRITE) {
170 			d->mask_9[r] = idata;
171 			dev_dreamcast_asic_tick(cpu, d);
172 		} else {
173 			odata = d->mask_9[r];
174 		}
175 		break;
176 
177 	default:if (writeflag == MEM_READ) {
178 			debug("[ dreamcast_asic: read from addr 0x%x ]\n",
179 			    (int)relative_addr);
180 		} else {
181 			debug("[ dreamcast_asic: write to addr 0x%x: 0x%x ]\n",
182 			    (int)relative_addr, (int)idata);
183 		}
184 	}
185 
186 	if (writeflag == MEM_READ)
187 		memory_writemax64(cpu, data, len, odata);
188 
189 	return 1;
190 }
191 
192 
DEVINIT(dreamcast_asic)193 DEVINIT(dreamcast_asic)
194 {
195 	char tmpstr[300];
196 	struct machine *machine = devinit->machine;
197 	struct dreamcast_asic_data *d;
198 
199 	CHECK_ALLOCATION(d = (struct dreamcast_asic_data *) malloc(sizeof(struct dreamcast_asic_data)));
200 	memset(d, 0, sizeof(struct dreamcast_asic_data));
201 
202 	/*  Connect to SH4 interrupt levels 13, 11, and 9:  */
203 	snprintf(tmpstr, sizeof(tmpstr), "%s.irq[0x%x]",
204 	    devinit->interrupt_path, SH_INTEVT_IRL13);
205 	INTERRUPT_CONNECT(tmpstr, d->irq_13);
206 	snprintf(tmpstr, sizeof(tmpstr), "%s.irq[0x%x]",
207 	    devinit->interrupt_path, SH_INTEVT_IRL11);
208 	INTERRUPT_CONNECT(tmpstr, d->irq_11);
209 	snprintf(tmpstr, sizeof(tmpstr), "%s.irq[0x%x]",
210 	    devinit->interrupt_path, SH_INTEVT_IRL9);
211 	INTERRUPT_CONNECT(tmpstr, d->irq_9);
212 
213 	memory_device_register(machine->memory, devinit->name, SYSASIC_BASE,
214 	    SYSASIC_SIZE, dev_dreamcast_asic_access, d, DM_DEFAULT, NULL);
215 
216 	machine_add_tickfunction(devinit->machine, dev_dreamcast_asic_tick, d,
217 	    DREAMCAST_ASIC_TICK_SHIFT);
218 
219 	return 1;
220 }
221 
222