1 /*
2 * Copyright (C) 2006-2014 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 GD-ROM
29 *
30 * TODO: This is mostly just a dummy so far. It is enough for NetBSD/dreamcast
31 * to read the GD-ROM (or actually, just a particular Live CD). It shouldn't
32 * be assumed to work for anything else.
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 "thirdparty/dreamcast_sysasicvar.h"
47
48
49 // #define debug fatal
50
51 #define NREGS_GDROM_DMA (0x100/sizeof(uint32_t))
52
53 struct dreamcast_gdrom_data {
54 // 0x005f7000: GDROM control registers
55 uint8_t busy; /* Busy status */
56 uint8_t stat; /* Status */
57 int cnt; /* Data length in bytes */
58 uint8_t cond;
59
60 int cmd_count;
61 uint8_t cmd[12];
62
63 uint8_t *data;
64 int data_len;
65 int cur_data_offset;
66 int cur_cnt;
67
68 // 0x005f7400: GDROM DMA
69 uint32_t dma_reg[NREGS_GDROM_DMA];
70 };
71
72 /* Register offsets, from NetBSD: */
73 #define GDROM_BUSY 0x18
74 #define GDROM_DATA 0x80
75 #define GDROM_REGX 0x84
76 #define GDROM_UNKNOWN_0x88 0x88
77 #define GDROM_STAT 0x8c
78 #define GDROM_CNTLO 0x90
79 #define GDROM_CNTHI 0x94
80 #define GDROM_COND 0x9c
81
82 // See the interrupt routine (gdrom_intr()) in NetBSD's dreamcast/dev/gdrom.c:
83 #define COND_DATA_AVAIL 0x08
84
85
alloc_data(struct dreamcast_gdrom_data * d)86 static void alloc_data(struct dreamcast_gdrom_data *d)
87 {
88 d->data_len = d->cnt;
89
90 CHECK_ALLOCATION(d->data = (uint8_t *) malloc(d->data_len));
91 memset(d->data, 0, d->data_len);
92 }
93
94
handle_command(struct cpu * cpu,struct dreamcast_gdrom_data * d)95 static void handle_command(struct cpu *cpu, struct dreamcast_gdrom_data *d)
96 {
97 int64_t sector_nr, sector_count;
98 int i, res;
99
100 debug("[ GDROM cmd: ");
101 for (i=0; i<12; i++)
102 debug("%02x ", d->cmd[i]);
103 debug("(cnt=%i) ]\n", d->cnt);
104
105 if (d->data != NULL)
106 free(d->data);
107 d->data = NULL;
108 d->cur_data_offset = 0;
109 d->cur_cnt = 0;
110
111 switch (d->cmd[0]) {
112
113 case 0x14:
114 /*
115 * Read Table-Of-Contents:
116 *
117 * See NetBSD's sys/arch/dreamcast/dev/gdrom.c: gdrom_read_toc().
118 */
119 if (d->cnt != 408) {
120 fatal("GDROM Read TOC not 408 bytes?\n");
121 exit(1);
122 }
123 alloc_data(d);
124
125 /*
126 * From David Brownlee:
127 * "TOC from test booted real CD image on NetBSD/dreamcast
128 * 01000096,41002e4c,ffffffff * 97,01010000,41020000,6100e641,"
129 *
130 * TODO: Perhaps generalize this in the future, to support
131 * disk images with multiple TOCs and/or tracks.
132 */
133 memset(d->data, 0xff, d->cnt); /* Default data to 0xff */
134
135 d->data[0*4] = 0x10; /* Track 1 */
136 d->data[0*4+1] = 0;
137 d->data[0*4+2] = 0;
138 d->data[0*4+3] = 0x96;
139
140 d->data[1*4] = 0x41; /* Track 2 */
141 d->data[1*4+1] = 0;
142 d->data[1*4+2] = 0x2e;
143 d->data[1*4+3] = 0x4c;
144
145 d->data[99*4] = 0x01; /* First track */
146 d->data[99*4+1] = 0x01;
147 d->data[99*4+2] = 0;
148 d->data[99*4+3] = 0;
149
150 d->data[100*4] = 0x41; /* Last track */
151 d->data[100*4+1] = 0x02;
152 d->data[100*4+2] = 0;
153 d->data[100*4+3] = 0;
154
155 d->data[101*4] = 0x61; /* Leadout */
156 d->data[101*4+1] = 0;
157 d->data[101*4+2] = 0xe6;
158 d->data[101*4+3] = 0x41;
159
160 break;
161
162 case 0x30:
163 /*
164 * Read sectors:
165 *
166 * See NetBSD's sys/arch/dreamcast/dev/gdrom.c: gdrom_read_sectors().
167 */
168 if (d->cmd[1] == 0x24) {
169 fatal("GDROM unimplemented data format 0x%02x. Continuing anway.\n", d->cmd[1]);
170 } else if (d->cmd[1] != 0x20) {
171 fatal("GDROM unimplemented data format 0x%02x\n",
172 d->cmd[1]);
173 exit(1);
174 }
175 sector_nr = d->cmd[2] * 65536 + d->cmd[3] * 256 + d->cmd[4];
176 sector_count = d->cmd[8] * 65536 + d->cmd[9] * 256 + d->cmd[10];
177
178 // NetBSD/dreamcast uses correct length, but the Dreamcast PROM
179 // uses len = 0 (!), but sector count = 7,
180 // which means len = 0x3800. It also sets up DMA to
181 // transfer 0x3800 bytes, so I'm assuming that the
182 // sector count is to be trusted more than the length.
183 if (d->cnt == 0)
184 d->cnt = 2048 * sector_count;
185
186 if (sector_count * 2048 != d->cnt) {
187 fatal("Huh? GDROM data_len=0x%x, but sector_count"
188 "=0x%x\n", (int)d->cnt, (int)sector_count);
189 exit(1);
190 }
191
192 alloc_data(d);
193
194 // Hm. This is an ugly hack to make a NetBSD/dreamcast
195 // live-cd work. It should be fixed (i.e. removed).
196 // When running with -Q (i.e. no PROM software emulation),
197 // experiments with the Dreamcast PROM can be made instead.
198 if (cpu->machine->prom_emulation) {
199 // printf("sector nr step 1 = %i\n", (int)sector_nr);
200
201 // Hack to get NetBSD/dreamcast to work:
202 // See the variable "openpart_start" in NetBSD's
203 // sys/arch/dreamcast/dev/gdrom.c:
204 sector_nr -= 150;
205
206 // printf("sector nr step 2 = %i\n", (int)sector_nr);
207 } else {
208 // printf("sector nr step 1 = %i\n", (int)sector_nr);
209 sector_nr -= 45150;
210 // printf("sector nr step 2 = %i\n", (int)sector_nr);
211 sector_nr += (diskimage_get_baseoffset(cpu->machine, 0, DISKIMAGE_IDE) / 2048);
212 // printf("sector nr step 3 = %i\n", (int)sector_nr);
213 }
214
215 res = diskimage_access(cpu->machine, 0, DISKIMAGE_IDE,
216 0, sector_nr * 2048, d->data, d->data_len);
217 if (!res) {
218 fatal("GDROM: diskimage_access failed? TODO\n");
219 free(d->data);
220 d->data = NULL;
221 }
222
223 /* {
224 printf("(Dump of GDROM sector %i: \"", (int)sector_nr);
225 for (int k = 0; k < 256; ++k)
226 printf("%c", d->data[k] >= 32 ? d->data[k] : '.');
227 printf("\")\n");
228 } */
229
230 break;
231
232 case 0x70:
233 /*
234 * Mount:
235 *
236 * See NetBSD's sys/arch/dreamcast/dev/gdrom.c: gdrom_mount_disk().
237 */
238
239 // Note/TODO: This is ignored for now.
240 break;
241
242 default:fatal("GDROM handle_command: unimplemented command 0x%02x"
243 "\n", d->cmd[0]);
244 exit(1);
245 }
246
247 // Any resulting data? Then set COND_DATA_AVAIL. Otherwise, clear
248 // that bit, and set count to zero.
249 if (d->data != NULL) {
250 d->cond |= COND_DATA_AVAIL;
251 } else {
252 d->cnt = 0;
253 d->cond &= ~COND_DATA_AVAIL;
254 }
255
256 // NetBSD seems to sometimes request 32 sectors (2048 bytes each), i.e.
257 // 65536 bytes. That is represented as count = 0x0000 (when NetBSD reads
258 // from the CNTHI and CNTLO registers). That does not work very well.
259 // This is a hack/workaround for that.
260 if (d->cnt == 65536)
261 d->cnt = 32768;
262
263 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_GDROM);
264 }
265
dreamcast_gdrom_update_stat(struct cpu * cpu,struct dreamcast_gdrom_data * d)266 void dreamcast_gdrom_update_stat(struct cpu *cpu, struct dreamcast_gdrom_data *d)
267 {
268 // See NetBSD's gdrom.c.
269 d->stat = 6;
270
271 if (diskimage_exist(cpu->machine, 0, DISKIMAGE_IDE)) {
272 d->stat = 0;
273 }
274 }
275
276
DEVICE_ACCESS(dreamcast_gdrom)277 DEVICE_ACCESS(dreamcast_gdrom)
278 {
279 struct dreamcast_gdrom_data *d = (struct dreamcast_gdrom_data *) extra;
280 uint64_t idata = 0, odata = 0;
281
282 if (writeflag == MEM_WRITE)
283 idata = memory_readmax64(cpu, data, len);
284
285 switch (relative_addr) {
286
287 case GDROM_BUSY:
288 if (writeflag == MEM_READ) {
289 odata = d->busy;
290 } else {
291 // fatal("[ Write to GDROM_BUSY: 0x%08x ]\n", (int)idata);
292 // I'm assuming that writing bits to BUSY clears them.
293 d->busy &= ~idata;
294 }
295 break;
296
297 case GDROM_DATA:
298 if (len != sizeof(uint16_t)) {
299 fatal("Non-16bit GDROM data access? TODO\n");
300 exit(1);
301 }
302
303 if (writeflag == MEM_READ) {
304 if (!(d->cond & COND_DATA_AVAIL)) {
305 fatal("Read from GDROM_DATA when no data"
306 " is available? TODO\n");
307 exit(1);
308 }
309
310 if (d->cur_data_offset < d->data_len) {
311 odata = d->data[d->cur_data_offset ++];
312 odata |= (d->data[d->cur_data_offset ++] << 8);
313 d->cur_cnt += sizeof(uint16_t);
314 if (d->cur_cnt >= d->cnt) {
315 if (d->cur_data_offset >= d->data_len) {
316 d->cond &= ~COND_DATA_AVAIL;
317 } else {
318 d->cnt = d->data_len - d->cur_data_offset;
319 d->cur_cnt = 0;
320 }
321
322 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_GDROM);
323 }
324 } else {
325 fatal("Read too much from GDROM_DATA\n");
326 exit(1);
327 }
328 } else {
329 if (d->busy & 0x08) {
330 if (d->cmd_count >= 12) {
331 fatal("Too much GDROM_DATA?\n");
332 exit(1);
333 }
334 /* Add data to cmd: */
335 d->cmd[d->cmd_count++] = idata;
336 d->cmd[d->cmd_count++] = idata >> 8;
337 if (d->cmd_count == 12) {
338 d->busy &= ~0x08;
339 handle_command(cpu, d);
340 }
341 } else {
342 fatal("Write to GDROM_DATA, but not waiting"
343 " for data?\n");
344 exit(1);
345 }
346 }
347 break;
348
349 case GDROM_REGX:
350 if (writeflag == MEM_READ) {
351 debug("[ Read to GDROM_REGX? ]\n");
352 } else {
353 /* NetBSD/dreamcast writes 0 here. */
354 if (idata != 0)
355 debug("[ Write 0x%x to GDROM_REGX? ]\n", (int)idata);
356 }
357 break;
358
359 case GDROM_UNKNOWN_0x88:
360 if (writeflag == MEM_READ) {
361 fatal("Read from GDROM_UNKNOWN_0x88?\n");
362 // exit(1);
363 } else {
364 if (idata != 0xb) {
365 fatal("[ Write to GDROM_UNKNOWN_0x88: TODO ]\n");
366 exit(1);
367 }
368 }
369 break;
370
371 case GDROM_STAT:
372 if (writeflag == MEM_READ) {
373 dreamcast_gdrom_update_stat(cpu, d);
374 odata = d->stat;
375 } else {
376 fatal("[ Write to GDROM_STAT? ]\n");
377 /* exit(1); */
378 }
379 break;
380
381 case GDROM_CNTLO:
382 if (writeflag == MEM_READ) {
383 odata = d->cnt & 0xff;
384 } else {
385 d->cnt = (d->cnt & 0xff00) | (idata & 0xff);
386 }
387 break;
388
389 case GDROM_CNTHI:
390 if (writeflag == MEM_READ) {
391 odata = (d->cnt >> 8) & 0xff;
392 } else {
393 d->cnt = (d->cnt & 0x00ff) | ((idata & 0xff) << 8);
394 }
395 break;
396
397 case GDROM_COND:
398 if (writeflag == MEM_READ) {
399 odata = d->cond;
400 } else {
401 d->cond = idata;
402
403 /*
404 * NetBSD/dreamcast writes 0xa0 to GDROM_COND to
405 * start a command. See gdrom_do_command() in NetBSD's
406 * sys/arch/dreamcast/dev/gdrom.c.
407 *
408 * Before sending anything, NetBSD expects:
409 * o) the lowest 4 bits of STAT to not be 6.
410 *
411 * After writing 0xa0 to GDROM_COND, NetBSD expects:
412 * o) (BUSY & 0x88) to be 0x08.
413 *
414 * NetBSD then sends 6 16-bit data words to GDROM_DATA.
415 */
416 if (idata == 0xa0) {
417 d->stat = 0; /* TODO */
418 d->busy |= 0x08;
419 d->cmd_count = 0;
420 } else if (idata == 0xef) {
421 debug("dreamcast_gdrom: ROM: TODO\n");
422 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_GDROM);
423 } else {
424 fatal("dreamcast_gdrom: unimplemented "
425 "GDROM_COND = 0x%02x\n", (int)idata);
426 exit(1);
427 }
428 }
429 break;
430
431 default:if (writeflag == MEM_READ) {
432 fatal("[ dreamcast_gdrom: read from addr 0x%x ]\n",
433 (int)relative_addr);
434 } else {
435 fatal("[ dreamcast_gdrom: write to addr 0x%x: 0x%x ]\n",
436 (int)relative_addr, (int)idata);
437 }
438
439 exit(1);
440 }
441
442 if (writeflag == MEM_READ)
443 memory_writemax64(cpu, data, len, odata);
444
445 return 1;
446 }
447
448
DEVICE_ACCESS(dreamcast_gdrom_dma)449 DEVICE_ACCESS(dreamcast_gdrom_dma)
450 {
451 struct dreamcast_gdrom_data *d = (struct dreamcast_gdrom_data *) extra;
452 uint64_t idata = 0, odata = 0;
453
454 if (writeflag == MEM_WRITE)
455 idata = memory_readmax64(cpu, data, len);
456
457 /* Default read: */
458 if (writeflag == MEM_READ)
459 odata = d->dma_reg[relative_addr / sizeof(uint32_t)];
460
461 switch (relative_addr) {
462
463 case 0x04: // destination address? e.g. 0x8c008000
464 case 0x08: // DMA length in bytes? e.g. 0x3800
465 case 0x0c: // count? e.g. 1
466 case 0x14: // "enable"?
467 break;
468
469 case 0x18:
470 // GDROM DMA start?
471 if (idata != 0) {
472 if (d->dma_reg[0x0c / sizeof(uint32_t)] == 1 &&
473 d->dma_reg[0x14 / sizeof(uint32_t)] == 1) {
474 // GDROM DMA transfer.
475 uint32_t dst = d->dma_reg[0x04 / sizeof(uint32_t)];
476 int length = d->dma_reg[0x08 / sizeof(uint32_t)];
477 fatal("[ dreamcast_gdrom_dma: Transfering %i bytes to 0x%08" PRIx32" ]\n", length, dst);
478
479 if (d->data == NULL) {
480 fatal("dreamcast_gdrom_dma: DMA transfer but d->data is NULL. TODO\n");
481 exit(1);
482 }
483
484 dst &= 0x0fffffff; // 0x8c008000 => 0x0c008000
485 cpu->memory_rw(cpu, cpu->mem, dst,
486 d->data, d->data_len, MEM_WRITE, PHYSICAL);
487
488 SYSASIC_TRIGGER_EVENT(SYSASIC_EVENT_GDROM_DMA);
489
490 idata = 0;
491 } else {
492 fatal("Unimplemented GDROM DMA start? TODO\n");
493 fatal(" %08x\n", (int) d->dma_reg[4 / sizeof(uint32_t)]);
494 fatal(" %08x\n", (int) d->dma_reg[8 / sizeof(uint32_t)]);
495 fatal(" %08x\n", (int) d->dma_reg[0xc / sizeof(uint32_t)]);
496 fatal(" %08x\n", (int) d->dma_reg[0x14 / sizeof(uint32_t)]);
497 exit(1);
498 }
499 }
500 break;
501
502 default:if (writeflag == MEM_READ) {
503 fatal("[ dreamcast_gdrom_dma: read from addr 0x%x ]\n",
504 (int)relative_addr);
505 } else {
506 fatal("[ dreamcast_gdrom_dma: write to addr 0x%x: "
507 "0x%x ]\n", (int)relative_addr, (int)idata);
508 }
509
510 exit(1);
511 }
512
513 /* Default write: */
514 if (writeflag == MEM_WRITE)
515 d->dma_reg[relative_addr / sizeof(uint32_t)] = idata;
516
517 if (writeflag == MEM_READ)
518 memory_writemax64(cpu, data, len, odata);
519
520 return 1;
521 }
522
523
DEVINIT(dreamcast_gdrom)524 DEVINIT(dreamcast_gdrom)
525 {
526 struct dreamcast_gdrom_data *d;
527
528 CHECK_ALLOCATION(d = (struct dreamcast_gdrom_data *) malloc(sizeof(struct dreamcast_gdrom_data)));
529 memset(d, 0, sizeof(struct dreamcast_gdrom_data));
530
531 memory_device_register(devinit->machine->memory, devinit->name,
532 0x005f7000, 0x100, dev_dreamcast_gdrom_access, d,
533 DM_DEFAULT, NULL);
534
535 memory_device_register(devinit->machine->memory, "gdrom_dma", 0x005f7400,
536 0x80, dev_dreamcast_gdrom_dma_access, d, DM_DEFAULT, NULL);
537
538 return 1;
539 }
540
541