dreamcast.cc Source File

Back to the index.

dreamcast.cc
Go to the documentation of this file.
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 PROM emulation
29  *
30  * NOTE: This module probably only emulates enough system/BIOS calls to let
31  * NetBSD/dreamcast, Linux/dreamcast, and various KOS demos run. Don't expect
32  * it do be fully functional.
33  *
34  * Dreamcast memory layout during startup:
35  *
36  * 0xa0000000: 2 MB ROM/BIOS. This is copied into RAM (0x8c000000) during
37  * bootup by the real BIOS. In GXemul's fake PROM implementation,
38  * the only thing present at 0xa0000000 is an opcode which
39  * triggers a reboot/shutdown of the emulator.
40  *
41  * 0x8c000000 - 0x8c0000ff: Various variables and vectors.
42  * pointer (32-bit word) at 0x8c0000b0: SYSINFO
43  * pointer (32-bit word) at 0x8c0000b4: ROMFONT
44  * pointer (32-bit word) at 0x8c0000b8: FLASHROM
45  * pointer (32-bit word) at 0x8c0000bc: GDROM
46  * pointer (32-bit word) at 0x8c0000c0: (?) something
47  * pointer (32-bit word) at 0x8c0000e0: "main menu" call, or "continue
48  * booting" (?) or something.
49  *
50  * 0x8c000100: Not on a real Dreamcast, but in GXemul: This area is filled with
51  * a special invalid instruction. Each instruction slot corresponds
52  * to one of the pointers above (SYSINFO, ROMFONT, etc).
53  *
54  * 0x8c008000: This is where the first 32KB of the data track of a CDROM
55  * gets loaded, also known as the "IP.BIN" part.
56  *
57  * 0x8c010000: This is where the first binary executable gets loaded. The name
58  * of the executable is given in IP.BIN, and refers to the ISO9660
59  * filename on the CDROM. A common file name is "1ST_READ.BIN".
60  *
61  * See http://mc.pp.se/dc/syscalls.html for a description of what the
62  * PROM syscalls do. The symbolic names in this module are the same as on
63  * that page.
64  */
65 
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <sys/types.h>
70 
71 #include "cpu.h"
72 #include "machine.h"
73 #include "memory.h"
74 #include "misc.h"
76 
77 /* The ROM FONT seems to be located just after 1MB, in a real Dreamcast: */
78 #define DREAMCAST_ROMFONT_BASE 0x80100020
79 extern unsigned char font8x16[];
80 
81 /* Where the machine ID (64-bit) is stored. */
82 #define DREAMCAST_MACHINE_ID_ADDRESS 0x80000068
83 
84 
85 static int booting_from_cdrom = 0;
86 
87 
88 /*
89  * dreamcast_romfont_init()
90  *
91  * Initialize the ROM font.
92  */
93 static void dreamcast_romfont_init(struct machine *machine)
94 {
95  struct cpu *cpu = machine->cpus[0];
96  int i, y, v;
97  uint64_t d = DREAMCAST_ROMFONT_BASE;
98 
99  /*
100  * 288 narrow glyphs (12 x 24 pixels):
101  *
102  * Glyphs 1-94 are ASCII characters 33-126, according to
103  * http://mc.pp.se/dc/syscalls.html#vecB4
104  *
105  * syscalls.html says "(As there is no glyph for ASCII space, use
106  * glyph 96 = ISO-8859-1 unbreakable space instead.)", but this does
107  * not seem to work. Marcus Comstedt's own example (video.s) uses
108  * char 288 for space instead (but the comment says char 72).
109  *
110  * TODO: A better looking font. This simply reuses the standard 8x16
111  * font, which looks odd in Dreamcast programs.
112  */
113  for (i=0; i<288; i++) {
114  for (y=0; y<24; y+=2) {
115  if (y <= 1 || y >= 22)
116  v = 0;
117  else
118  v = random();
119  store_byte(cpu, d++, v & 0x3f);
120  store_byte(cpu, d++, v & 0xc3);
121  store_byte(cpu, d++, v & 0xfc);
122  }
123  }
124 
125  for (i=1; i<=94; i++) {
126  d = DREAMCAST_ROMFONT_BASE + i * (24 * 3 / 2);
127  int c = 32 + i;
128  int u;
129  for (y=0; y<24; y+=2) {
130  if (y < 4 || y >= 20)
131  u = v = 0x00;
132  else
133  u = font8x16[c*16 + (y-4)], v = font8x16[c*16 + (y-4+1)];
134 
135  // 00 00 u7 u6 u5 u4 u3 u2 u1 u0 00 00
136  // 00 00 v7 v6 v5 v4 v3 v2 v1 v0 00 00
137  // becomes:
138  // first byte: 00 00 u7 u6 u5 u4 u3 u2
139  // second byte: u1 u0 00 00 00 00 v7 v6
140  // third byte: v5 v4 v3 v2 v1 v0 00 00
141  store_byte(cpu, d++, u >> 2);
142  store_byte(cpu, d++, (u << 6) | (v >> 6));
143  store_byte(cpu, d++, v << 2);
144  }
145  }
146 
147  // "ISO-8859-1 characters 160-255" (at pos 96..191):
148  for (i=96; i<=191; i++) {
149  d = DREAMCAST_ROMFONT_BASE + i * (24 * 3 / 2);
150  int c = i - 96 + 160;
151  int u;
152  for (y=0; y<24; y+=2) {
153  if (y < 4 || y >= 20)
154  u = v = 0;
155  else
156  u = font8x16[c*16 + (y-4)], v = font8x16[c*16 + (y-4+1)];
157 
158  store_byte(cpu, d++, u >> 2);
159  store_byte(cpu, d++, (u << 6) | (v >> 6));
160  store_byte(cpu, d++, v << 2);
161  }
162  }
163 
164  d = DREAMCAST_ROMFONT_BASE + 289 * (24 * 3 / 2);
165 
166  /* 7078 wide glyphs (24 x 24 pixels): */
167  for (i=1; i<7078; i++) {
168  for (y=0; y<24; y++) {
169  if (y <= 1 || y >= 22)
170  v = 0;
171  else
172  v = 0xff;
173  store_byte(cpu, d++, v & 0x3f);
174  store_byte(cpu, d++, v);
175  store_byte(cpu, d++, v & 0xfc);
176  }
177  }
178 
179  /* 129 VME icons (32 x 32 pixels): */
180  for (i=0; i<129; i++) {
181  for (y=0; y<32; y++) {
182  if (y <= 1 || y >= 30)
183  v = 0;
184  else
185  v = random();
186  store_byte(cpu, d++, v & 0x3f);
187  store_byte(cpu, d++, v);
188  store_byte(cpu, d++, v);
189  store_byte(cpu, d++, v & 0xfc);
190  }
191  }
192 }
193 
194 
195 /*
196  * dreamcast_machine_setup():
197  *
198  * Initializes pointers to Dreamcast PROM syscalls.
199  */
201 {
202  int i;
203  struct cpu *cpu = machine->cpus[0];
204 
205  for (i=0; i<0x50; i+=sizeof(uint32_t)) {
206  /* Store pointer to PROM routine... */
207  store_32bit_word(cpu, 0x8c0000b0 + i, 0x8c000100 + i);
208 
209  /* ... which contains only 1 instruction, a special
210  opcode which triggers PROM emulation: */
211  store_16bit_word(cpu, 0x8c000100 + i, SH_INVALID_INSTR);
212  }
213 
214  /* PROM reboot, in case someone jumps to 0xa0000000: */
215  store_16bit_word(cpu, 0xa0000000, SH_INVALID_INSTR);
216 
217  /* Machine ID (64-bit): */
218  store_64bit_word(cpu, DREAMCAST_MACHINE_ID_ADDRESS, 0x0000000000000000ULL);
219 
220  dreamcast_romfont_init(machine);
221 
222  /* Return address, if the user program returns: exit. */
223  cpu->cd.sh.pr = 0x8c0000e0 + (0x100 - 0xb0);
224 
225  /* Stack starting at end of RAM. */
226  cpu->cd.sh.r[15] = 0x8c000000 + 16 * 1048576;
227 }
228 
229 
230 /*
231  * dreamcast_emul():
232  *
233  * We end up here if someone branched or called to 0x8c000100 + ofs,
234  * where ofs is a small number. These addresses correspond to the code reading
235  * a pointer from 0x8c0000b0 + ofs and calling it.
236  *
237  * See http://mc.pp.se/dc/syscalls.html for more details.
238  */
239 void dreamcast_emul(struct cpu *cpu)
240 {
241  // cpu->pc is the address where PROM emulation was triggered, but
242  // what we are after is the indirect vector that was used to fetch
243  // that address.
244  uint32_t vectorAddr = ((cpu->pc & 0x00ffffff) - 0x100 + 0xb0) | 0x8c000000;
245 
246  int r1 = cpu->cd.sh.r[1];
247  int r6 = cpu->cd.sh.r[6];
248  int r7 = cpu->cd.sh.r[7];
249 
250  /* Special case: Reboot */
251  if ((uint32_t)cpu->pc == 0x80000000 || (uint32_t)cpu->pc == 0xa0000000) {
252  fatal("[ dreamcast reboot ]\n");
253  cpu->running = 0;
254  return;
255  }
256 
257  switch (vectorAddr) {
258 
259  case 0x8c0000b0:
260  /* SYSINFO */
261  switch (r7) {
262  case 0: /* SYSINFO_INIT: Ignored for now. */
263  break;
264  case 3: /* SYSINFO_ID: */
265  cpu->cd.sh.r[0] = (uint32_t)DREAMCAST_MACHINE_ID_ADDRESS;
266  break;
267  default:fatal("[ SYSINFO: Unimplemented r7=%i ]\n", r7);
268  goto bad;
269  }
270  break;
271 
272  case 0x8c0000b4:
273  /* ROMFONT */
274  switch (r1) {
275  case 0: /* ROMFONT_ADDRESS */
276  cpu->cd.sh.r[0] = DREAMCAST_ROMFONT_BASE;
277  break;
278  default:fatal("[ ROMFONT: Unimplemented r1=%i ]\n", r1);
279  goto bad;
280  }
281  break;
282 
283  case 0x8c0000b8:
284  /* FLASHROM */
285  switch (r7) {
286  case 0: /* FLASHROM_INFO */
287  /* TODO */
288  cpu->cd.sh.r[0] = (uint32_t) -1;
289  break;
290  case 1: /* FLASHROM_READ */
291  /* TODO */
292  cpu->cd.sh.r[0] = (uint32_t) -1;
293  break;
294  default:fatal("[ FLASHROM: Unimplemented r7=%i ]\n", r7);
295  goto bad;
296  }
297  break;
298 
299  case 0x8c0000bc:
300  switch ((int32_t)r6) {
301  case 0: /* GD-ROM emulation */
302  switch (r7) {
303  case 0: /* GDROM_SEND_COMMAND */
304  /* TODO */
305  cpu->cd.sh.r[0] = (uint32_t) -1;
306  break;
307  case 1: /* GDROM_CHECK_COMMAND */
308  /* TODO */
309  cpu->cd.sh.r[0] = 0;
310  break;
311  case 2: /* GDROM_MAINLOOP */
312  /* TODO */
313  break;
314  case 3: /* GDROM_INIT */
315  /* TODO: Do something here? */
316  break;
317  case 4: /* GDROM_CHECK_DRIVE */
318  /* TODO: Return status words */
319  break;
320  default:fatal("[ GDROM: Unimplemented r7=%i ]\n", r7);
321  goto bad;
322  }
323  break;
324  default:fatal("[ 0xbc: Unimplemented r6=0x%x ]\n", r6);
325  goto bad;
326  }
327  break;
328 
329  case 0x8c0000e0:
330  /*
331  * This seems to have two uses:
332  *
333  * 1. KallistiOS calls this from arch_menu(), i.e. to return
334  * from a running program.
335  * 2. The "licence code" in the IP.BIN code when booting from
336  * a bootable CD image calls this once the license screen
337  * has been displayed, and it wants the ROM to jump to
338  * 0x8c00b800 ("Bootstrap 1").
339  *
340  * The easiest way to support both is probably to keep track
341  * of whether the IP.BIN code was started by the (software)
342  * ROM emulation code, or not.
343  */
344  if (booting_from_cdrom) {
345  debug("[ dreamcast: Switching to bootstrap 1 ]\n");
346 
347  booting_from_cdrom = 0;
348 
349  // Jump to bootstrap 1
350  cpu->pc = 0x8c00b800;
351  return;
352  } else {
353  fatal("[ dreamcast: Returning to main menu. ]\n");
354  cpu->running = 0;
355  }
356  break;
357 
358  case 0x8c0000f0:
359  /*
360  * GXemul hack:
361  *
362  * The vector (word) at 0x8c0000f0 contains the value 0x8c000140.
363  *
364  * By jumping to this address (0x8c000140), a "boot from
365  * CDROM" is simulated. Control is transfered to the license
366  * code in the loaded IP.BIN file.
367  */
368  debug("[ dreamcast boot from CDROM ]\n");
369  booting_from_cdrom = 1;
370  cpu->pc = 0x8c008300;
371  return;
372 
373  default:goto bad;
374  }
375 
376  /* Return from subroutine: */
377  cpu->pc = cpu->cd.sh.pr;
378 
379  return;
380 
381 bad:
382  cpu_register_dump(cpu->machine, cpu, 1, 0);
383  printf("\n");
384  fatal("[ dreamcast_emul(): unimplemented dreamcast PROM call, "
385  "pc=0x%08" PRIx32" (vectorAddr=0x%08" PRIx32") ]\n", (uint32_t)cpu->pc, vectorAddr);
386  cpu->running = 0;
387  return;
388 }
389 
void fatal(const char *fmt,...)
Definition: main.cc:152
void dreamcast_emul(struct cpu *cpu)
Definition: dreamcast.cc:239
uint32_t r[SH_N_GPRS]
Definition: cpu_sh.h:100
#define DREAMCAST_MACHINE_ID_ADDRESS
Definition: dreamcast.cc:82
int store_32bit_word(struct cpu *cpu, uint64_t addr, uint64_t data32)
Definition: memory.cc:783
union cpu::@1 cd
struct machine * machine
Definition: cpu.h:328
unsigned char font8x16[]
Definition: font8x16.cc:1
#define DREAMCAST_ROMFONT_BASE
Definition: dreamcast.cc:78
void dreamcast_machine_setup(struct machine *machine)
Definition: dreamcast.cc:200
struct cpu ** cpus
Definition: machine.h:140
int store_64bit_word(struct cpu *cpu, uint64_t addr, uint64_t data64)
Definition: memory.cc:752
uint64_t pc
Definition: cpu.h:383
uint32_t pr
Definition: cpu_sh.h:109
uint8_t running
Definition: cpu.h:353
void cpu_register_dump(struct machine *m, struct cpu *cpu, int gprs, int coprocs)
Definition: cpu.cc:203
#define debug
Definition: dev_adb.cc:57
Definition: cpu.h:326
void store_byte(struct cpu *cpu, uint64_t addr, uint8_t data)
Definition: memory.cc:679
int store_16bit_word(struct cpu *cpu, uint64_t addr, uint64_t data16)
Definition: memory.cc:807
#define SH_INVALID_INSTR
Definition: cpu_sh.h:93
struct sh_cpu sh
Definition: cpu.h:445

Generated on Fri Dec 7 2018 19:52:23 for GXemul by doxygen 1.8.13