1 |
/* |
2 |
* Copyright (C) 2006-2007 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 |
* $Id: dev_dreamcast_gdrom.c,v 1.5 2007/06/15 19:11:15 debug Exp $ |
29 |
* |
30 |
* COMMENT: Dreamcast GD-ROM |
31 |
* |
32 |
* TODO: This is just a dummy so far. |
33 |
*/ |
34 |
|
35 |
#include <stdio.h> |
36 |
#include <stdlib.h> |
37 |
#include <string.h> |
38 |
|
39 |
#include "cpu.h" |
40 |
#include "device.h" |
41 |
#include "diskimage.h" |
42 |
#include "machine.h" |
43 |
#include "memory.h" |
44 |
#include "misc.h" |
45 |
|
46 |
#include "dreamcast_sysasicvar.h" |
47 |
|
48 |
|
49 |
/* #define debug fatal */ |
50 |
|
51 |
struct dreamcast_gdrom_data { |
52 |
uint8_t busy; /* Busy status */ |
53 |
uint8_t stat; /* Status */ |
54 |
int cnt; /* Data length */ |
55 |
uint8_t cond; |
56 |
|
57 |
int cmd_count; |
58 |
uint8_t cmd[12]; |
59 |
|
60 |
uint8_t *data; |
61 |
int data_len; |
62 |
int cur_data_offset; |
63 |
int cur_cnt; |
64 |
}; |
65 |
|
66 |
/* Register offsets: */ |
67 |
#define GDROM_BUSY 0x18 |
68 |
#define GDROM_DATA 0x80 |
69 |
#define GDROM_REGX 0x84 |
70 |
#define GDROM_STAT 0x8c |
71 |
#define GDROM_CNTLO 0x90 |
72 |
#define GDROM_CNTHI 0x94 |
73 |
#define GDROM_COND 0x9c |
74 |
|
75 |
#define COND_DATA_AVAIL 0x08 |
76 |
|
77 |
|
78 |
static void alloc_data(struct dreamcast_gdrom_data *d) |
79 |
{ |
80 |
d->data_len = d->cnt; |
81 |
|
82 |
CHECK_ALLOCATION(d->data = malloc(d->data_len)); |
83 |
memset(d->data, 0, d->data_len); |
84 |
} |
85 |
|
86 |
|
87 |
static void handle_command(struct cpu *cpu, struct dreamcast_gdrom_data *d) |
88 |
{ |
89 |
int64_t sector_nr, sector_count; |
90 |
int i, res; |
91 |
|
92 |
debug("[ GDROM cmd: "); |
93 |
for (i=0; i<12; i++) |
94 |
debug("%02x ", d->cmd[i]); |
95 |
debug("(cnt=%i) ]\n", d->cnt); |
96 |
|
97 |
if (d->data != NULL) |
98 |
free(d->data); |
99 |
d->data = NULL; |
100 |
d->cur_data_offset = 0; |
101 |
d->cur_cnt = 0; |
102 |
|
103 |
switch (d->cmd[0]) { |
104 |
|
105 |
case 0x14: |
106 |
/* Read Table-Of-Contents: */ |
107 |
if (d->cnt != 408) { |
108 |
fatal("GDROM Read TOC not 408 bytes?\n"); |
109 |
exit(1); |
110 |
} |
111 |
alloc_data(d); |
112 |
|
113 |
/* TODO: Fill TOC in a better way */ |
114 |
d->data[99*4] = 1; /* First track */ |
115 |
d->data[100*4] = 2; /* Last track */ |
116 |
|
117 |
d->data[0*4] = 0x10; /* Track 1 */ |
118 |
d->data[1*4] = 0x10; /* Track 2 */ |
119 |
break; |
120 |
|
121 |
case 0x30: |
122 |
/* Read sectors: */ |
123 |
if (d->cmd[1] != 0x20) { |
124 |
fatal("GDROM unimplemented data format 0x%02x\n", |
125 |
d->cmd[1]); |
126 |
exit(1); |
127 |
} |
128 |
sector_nr = d->cmd[2] * 65536 + d->cmd[3] * 256 + d->cmd[4]; |
129 |
sector_count = d->cmd[8] * 65536 + d->cmd[9] * 256 + d->cmd[10]; |
130 |
if (d->cnt == 0) |
131 |
d->cnt = 65536; |
132 |
alloc_data(d); |
133 |
if (sector_count * 2048 != d->data_len) { |
134 |
fatal("Huh? GDROM data_len=0x%x, but sector_count" |
135 |
"=0x%x\n", (int)d->data_len, (int)sector_count); |
136 |
exit(1); |
137 |
} |
138 |
|
139 |
{ |
140 |
if (sector_nr >= 1376810) |
141 |
sector_nr -= 1376810; |
142 |
sector_nr -= 150; |
143 |
if (sector_nr > 1048576) |
144 |
sector_nr -= 1048576; |
145 |
/* printf("sector nr = %i\n", (int)sector_nr); */ |
146 |
|
147 |
if (sector_nr < 1000) |
148 |
sector_nr += (diskimage_get_baseoffset(cpu->machine, 0, DISKIMAGE_IDE) |
149 |
/ 2048); |
150 |
} |
151 |
|
152 |
res = diskimage_access(cpu->machine, 0, DISKIMAGE_IDE, |
153 |
0, sector_nr * 2048, d->data, d->data_len); |
154 |
if (!res) { |
155 |
fatal("GDROM: diskimage_access failed? TODO\n"); |
156 |
// exit(1); |
157 |
} |
158 |
break; |
159 |
|
160 |
case 0x70: |
161 |
/* Mount: (?) */ |
162 |
break; |
163 |
|
164 |
default:fatal("GDROM handle_command: unimplemented command 0x%02x" |
165 |
"\n", d->cmd[0]); |
166 |
exit(1); |
167 |
} |
168 |
|
169 |
if (d->data != NULL) |
170 |
d->cond |= COND_DATA_AVAIL; |
171 |
|
172 |
if (d->cnt == 65536) |
173 |
d->cnt = 32768; |
174 |
|
175 |
SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_GDROM); |
176 |
} |
177 |
|
178 |
|
179 |
DEVICE_ACCESS(dreamcast_gdrom) |
180 |
{ |
181 |
struct dreamcast_gdrom_data *d = (struct dreamcast_gdrom_data *) extra; |
182 |
uint64_t idata = 0, odata = 0; |
183 |
|
184 |
if (writeflag == MEM_WRITE) |
185 |
idata = memory_readmax64(cpu, data, len); |
186 |
|
187 |
switch (relative_addr) { |
188 |
|
189 |
case GDROM_BUSY: |
190 |
if (writeflag == MEM_READ) { |
191 |
odata = d->busy; |
192 |
} else { |
193 |
fatal("Write to GDROM_BUSY?\n"); |
194 |
exit(1); |
195 |
} |
196 |
break; |
197 |
|
198 |
case GDROM_DATA: |
199 |
if (len != sizeof(uint16_t)) { |
200 |
fatal("Non-16bit GDROM data access? TODO\n"); |
201 |
exit(1); |
202 |
} |
203 |
|
204 |
if (writeflag == MEM_READ) { |
205 |
if (!(d->cond & COND_DATA_AVAIL)) { |
206 |
fatal("Read from GDROM_DATA when no data" |
207 |
" is available? TODO\n"); |
208 |
exit(1); |
209 |
} |
210 |
if (d->cur_data_offset < d->data_len) { |
211 |
odata = d->data[d->cur_data_offset ++]; |
212 |
odata |= (d->data[d->cur_data_offset ++] << 8); |
213 |
d->cur_cnt += 2; |
214 |
if (d->cur_cnt >= d->cnt) { |
215 |
if (d->cur_data_offset >= d->data_len) { |
216 |
d->cond &= ~COND_DATA_AVAIL; |
217 |
} else { |
218 |
d->cnt = d->data_len - |
219 |
d->cur_data_offset; |
220 |
d->cur_cnt = 0; |
221 |
} |
222 |
SYSASIC_TRIGGER_EVENT( |
223 |
SYSASIC_EVENT_GDROM); |
224 |
} |
225 |
} else { |
226 |
fatal("Read too much from GDROM_DATA\n"); |
227 |
exit(1); |
228 |
} |
229 |
} else { |
230 |
if (d->busy & 0x08) { |
231 |
if (d->cmd_count >= 12) { |
232 |
fatal("Too much GDROM_DATA?\n"); |
233 |
exit(1); |
234 |
} |
235 |
/* Add data to cmd: */ |
236 |
d->cmd[d->cmd_count++] = idata; |
237 |
d->cmd[d->cmd_count++] = idata >> 8; |
238 |
if (d->cmd_count == 12) { |
239 |
d->busy &= ~0x08; |
240 |
handle_command(cpu, d); |
241 |
} |
242 |
} else { |
243 |
fatal("Write to GDROM_DATA, but not waiting" |
244 |
" for data?\n"); |
245 |
exit(1); |
246 |
} |
247 |
} |
248 |
break; |
249 |
|
250 |
case GDROM_REGX: |
251 |
if (writeflag == MEM_READ) { |
252 |
fatal("Read from GDROM_REGX?\n"); |
253 |
exit(1); |
254 |
} else { |
255 |
/* NetBSD/dreamcast writes 0 here. */ |
256 |
if (idata != 0) { |
257 |
fatal("Write to GDROM_REGX?\n"); |
258 |
exit(1); |
259 |
} |
260 |
} |
261 |
break; |
262 |
|
263 |
case GDROM_STAT: |
264 |
if (writeflag == MEM_READ) { |
265 |
odata = d->stat; |
266 |
} else { |
267 |
fatal("Write to GDROM_STAT?\n"); |
268 |
exit(1); |
269 |
} |
270 |
break; |
271 |
|
272 |
case GDROM_CNTLO: |
273 |
if (writeflag == MEM_READ) { |
274 |
odata = d->cnt & 0xff; |
275 |
} else { |
276 |
d->cnt = (d->cnt & 0xff00) | (idata & 0xff); |
277 |
} |
278 |
break; |
279 |
|
280 |
case GDROM_CNTHI: |
281 |
if (writeflag == MEM_READ) { |
282 |
odata = (d->cnt >> 8) & 0xff; |
283 |
} else { |
284 |
d->cnt = (d->cnt & 0x00ff) | ((idata & 0xff) << 8); |
285 |
} |
286 |
break; |
287 |
|
288 |
case GDROM_COND: |
289 |
if (writeflag == MEM_READ) { |
290 |
odata = d->cond; |
291 |
} else { |
292 |
d->cond = idata; |
293 |
|
294 |
/* |
295 |
* NetBSD/dreamcast writes 0xa0 to GDROM_COND to |
296 |
* start a command. It expects (BUSY & 0x88) to |
297 |
* be 0x08 after writing to GDROM_COND, and STAT |
298 |
* to be not equal to 6. NetBSD then sends 6 |
299 |
* 16-bit data words to GDROM_DATA. |
300 |
*/ |
301 |
if (idata == 0xa0) { |
302 |
d->stat = 0; /* TODO */ |
303 |
d->busy |= 0x08; |
304 |
d->cmd_count = 0; |
305 |
} else { |
306 |
fatal("dreamcast_gdrom: unimplemented " |
307 |
"GDROM_COND = 0x%02x\n", (int)idata); |
308 |
exit(1); |
309 |
} |
310 |
} |
311 |
break; |
312 |
|
313 |
default:if (writeflag == MEM_READ) { |
314 |
fatal("[ dreamcast_gdrom: read from addr 0x%x ]\n", |
315 |
(int)relative_addr); |
316 |
} else { |
317 |
fatal("[ dreamcast_gdrom: write to addr 0x%x: 0x%x ]\n", |
318 |
(int)relative_addr, (int)idata); |
319 |
} |
320 |
exit(1); |
321 |
} |
322 |
|
323 |
if (writeflag == MEM_READ) |
324 |
memory_writemax64(cpu, data, len, odata); |
325 |
|
326 |
return 1; |
327 |
} |
328 |
|
329 |
|
330 |
DEVINIT(dreamcast_gdrom) |
331 |
{ |
332 |
struct dreamcast_gdrom_data *d; |
333 |
|
334 |
CHECK_ALLOCATION(d = malloc(sizeof(struct dreamcast_gdrom_data))); |
335 |
memset(d, 0, sizeof(struct dreamcast_gdrom_data)); |
336 |
|
337 |
memory_device_register(devinit->machine->memory, devinit->name, |
338 |
0x005f7000, 0x100, dev_dreamcast_gdrom_access, d, |
339 |
DM_DEFAULT, NULL); |
340 |
|
341 |
return 1; |
342 |
} |
343 |
|