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