1 //
2 // JAGUAR.CPP
3 //
4 // Originally by David Raingeard (Cal2)
5 // GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Carwin Jones (BeOS)
6 // Cleanups and endian wrongness amelioration by James Hammons
7 // Note: Endian wrongness probably stems from the MAME origins of this emu and
8 // the braindead way in which MAME handled memory when this was written. :-)
9 //
10 // JLH = James Hammons
11 //
12 // WHO WHEN WHAT
13 // --- ---------- -----------------------------------------------------------
14 // JLH 11/25/2009 Major rewrite of memory subsystem and handlers
15 //
16 #include <string.h>
17 #include <stdlib.h>
18
19 #include <time.h>
20
21 #include "jaguar.h"
22
23 #include "cdrom.h"
24 #include "dsp.h"
25 #include "eeprom.h"
26 #include "event.h"
27 #include "gpu.h"
28 #include "jerry.h"
29 #include "joystick.h"
30 #include "log.h"
31 #include "m68000/m68kinterface.h"
32 #include "memtrack.h"
33 #include "mmu.h"
34 #include "settings.h"
35 #include "tom.h"
36
37 bool frameDone;
38 uint32_t starCount;
39
40 #define ALPINE_FUNCTIONS
41
42 // Private function prototypes
43
jaguar_unknown_readbyte(unsigned address,uint32_t who)44 unsigned jaguar_unknown_readbyte(unsigned address, uint32_t who)
45 {
46 return 0xFF;
47 }
48
jaguar_unknown_readword(unsigned address,uint32_t who)49 unsigned jaguar_unknown_readword(unsigned address, uint32_t who)
50 {
51 return 0xFFFF;
52 }
53
54 // Unknown read/write byte/word routines
55
56 // It's hard to believe that developers would be sloppy with their memory writes, yet in
57 // some cases the developers screwed up royal. E.g., Club Drive has the following code:
58 //
59 // 807EC4: movea.l #$f1b000, A1
60 // 807ECA: movea.l #$8129e0, A0
61 // 807ED0: move.l A0, D0
62 // 807ED2: move.l #$f1bb94, D1
63 // 807ED8: sub.l D0, D1
64 // 807EDA: lsr.l #2, D1
65 // 807EDC: move.l (A0)+, (A1)+
66 // 807EDE: dbra D1, 807edc
67 //
68 // The problem is at $807ED0--instead of putting A0 into D0, they really meant to put A1
69 // in. This mistake causes it to try and overwrite approximately $700000 worth of address
70 // space! (That is, unless the 68K causes a bus error...)
71
jaguar_unknown_writebyte(unsigned address,unsigned data,uint32_t who)72 void jaguar_unknown_writebyte(unsigned address, unsigned data, uint32_t who)
73 {
74 }
75
jaguar_unknown_writeword(unsigned address,unsigned data,uint32_t who)76 void jaguar_unknown_writeword(unsigned address, unsigned data, uint32_t who)
77 {
78 }
79
JaguarGetHandler(uint32_t i)80 uint32_t JaguarGetHandler(uint32_t i)
81 {
82 return JaguarReadLong(i * 4, UNKNOWN);
83 }
84
85
JaguarInterruptHandlerIsValid(uint32_t i)86 bool JaguarInterruptHandlerIsValid(uint32_t i) // Debug use only...
87 {
88 uint32_t handler = JaguarGetHandler(i);
89 return (handler && (handler != 0xFFFFFFFF) ? true : false);
90 }
91
M68K_show_context(void)92 void M68K_show_context(void)
93 {
94 unsigned i;
95
96 WriteLog("68K PC=%06X\n", m68k_get_reg(NULL, M68K_REG_PC));
97
98 for(i=M68K_REG_D0; i<=M68K_REG_D7; i++)
99 {
100 WriteLog("D%i = %08X ", i-M68K_REG_D0, m68k_get_reg(NULL, (m68k_register_t)i));
101
102 if (i == M68K_REG_D3 || i == M68K_REG_D7)
103 WriteLog("\n");
104 }
105
106 for(i=M68K_REG_A0; i<=M68K_REG_A7; i++)
107 {
108 WriteLog("A%i = %08X ", i-M68K_REG_A0, m68k_get_reg(NULL, (m68k_register_t)i));
109
110 if (i == M68K_REG_A3 || i == M68K_REG_A7)
111 WriteLog("\n");
112 }
113
114 WriteLog("68K disasm\n");
115 JaguarDasm(m68k_get_reg(NULL, M68K_REG_PC) - 0x80, 0x200);
116
117 if (TOMIRQEnabled(IRQ_VIDEO))
118 {
119 WriteLog("video int: enabled\n");
120 JaguarDasm(JaguarGetHandler(64), 0x200);
121 }
122 else
123 WriteLog("video int: disabled\n");
124
125 WriteLog("..................\n");
126
127 for(i=0; i<256; i++)
128 {
129 uint32_t address;
130
131 WriteLog("handler %03i at ", i);
132 address = (uint32_t)JaguarGetHandler(i);
133
134 if (address == 0)
135 WriteLog(".........\n");
136 else
137 WriteLog("$%08X\n", address);
138 }
139 }
140
141 // External variables
142
143 // Really, need to include memory.h for this, but it might interfere with some stuff...
144 extern uint8_t jagMemSpace[];
145
146 // Internal variables
147
148 uint32_t jaguar_active_memory_dumps = 0;
149
150 uint32_t jaguarMainROMCRC32, jaguarROMSize, jaguarRunAddress;
151
152 bool jaguarCartInserted = false;
153 bool lowerField = false;
154
155
156 uint32_t pcQueue[0x400];
157 uint32_t a0Queue[0x400];
158 uint32_t a1Queue[0x400];
159 uint32_t a2Queue[0x400];
160 uint32_t a3Queue[0x400];
161 uint32_t a4Queue[0x400];
162 uint32_t a5Queue[0x400];
163 uint32_t a6Queue[0x400];
164 uint32_t a7Queue[0x400];
165 uint32_t d0Queue[0x400];
166 uint32_t d1Queue[0x400];
167 uint32_t d2Queue[0x400];
168 uint32_t d3Queue[0x400];
169 uint32_t d4Queue[0x400];
170 uint32_t d5Queue[0x400];
171 uint32_t d6Queue[0x400];
172 uint32_t d7Queue[0x400];
173 uint32_t pcQPtr = 0;
174 bool startM68KTracing = false;
175
176 // Breakpoint on memory access vars (exported)
177 bool bpmActive = false;
178 uint32_t bpmAddress1;
179
180
181 /* Callback function to detect illegal instructions */
182 static bool start = false;
183
M68KInstructionHook(void)184 void M68KInstructionHook(void)
185 {
186 unsigned i;
187 uint32_t m68kPC = m68k_get_reg(NULL, M68K_REG_PC);
188
189 // For tracebacks...
190 // Ideally, we'd save all the registers as well...
191 pcQueue[pcQPtr] = m68kPC;
192 a0Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A0);
193 a1Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A1);
194 a2Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A2);
195 a3Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A3);
196 a4Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A4);
197 a5Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A5);
198 a6Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A6);
199 a7Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_A7);
200 d0Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D0);
201 d1Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D1);
202 d2Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D2);
203 d3Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D3);
204 d4Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D4);
205 d5Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D5);
206 d6Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D6);
207 d7Queue[pcQPtr] = m68k_get_reg(NULL, M68K_REG_D7);
208 pcQPtr++;
209 pcQPtr &= 0x3FF;
210
211 if (m68kPC & 0x01) // Oops! We're fetching an odd address!
212 {
213 static char buffer[2048];
214 WriteLog("M68K: Attempted to execute from an odd address!\n\nBacktrace:\n\n");
215
216 for(i=0; i<0x400; i++)
217 {
218 // WriteLog("[A2=%08X, D0=%08X]\n", a2Queue[(pcQPtr + i) & 0x3FF], d0Queue[(pcQPtr + i) & 0x3FF]);
219 WriteLog("[A0=%08X, A1=%08X, A2=%08X, A3=%08X, A4=%08X, A5=%08X, A6=%08X, A7=%08X, D0=%08X, D1=%08X, D2=%08X, D3=%08X, D4=%08X, D5=%08X, D6=%08X, D7=%08X]\n", a0Queue[(pcQPtr + i) & 0x3FF], a1Queue[(pcQPtr + i) & 0x3FF], a2Queue[(pcQPtr + i) & 0x3FF], a3Queue[(pcQPtr + i) & 0x3FF], a4Queue[(pcQPtr + i) & 0x3FF], a5Queue[(pcQPtr + i) & 0x3FF], a6Queue[(pcQPtr + i) & 0x3FF], a7Queue[(pcQPtr + i) & 0x3FF], d0Queue[(pcQPtr + i) & 0x3FF], d1Queue[(pcQPtr + i) & 0x3FF], d2Queue[(pcQPtr + i) & 0x3FF], d3Queue[(pcQPtr + i) & 0x3FF], d4Queue[(pcQPtr + i) & 0x3FF], d5Queue[(pcQPtr + i) & 0x3FF], d6Queue[(pcQPtr + i) & 0x3FF], d7Queue[(pcQPtr + i) & 0x3FF]);
220 m68k_disassemble(buffer, pcQueue[(pcQPtr + i) & 0x3FF], 0);//M68K_CPU_TYPE_68000);
221 WriteLog("\t%08X: %s\n", pcQueue[(pcQPtr + i) & 0x3FF], buffer);
222 }
223 WriteLog("\n");
224
225 M68K_show_context();
226 LogDone();
227 exit(0);
228 }
229 }
230
ShowM68KContext(void)231 void ShowM68KContext(void)
232 {
233 unsigned i;
234 uint32_t currpc;
235 uint32_t disPC;
236 char buffer[128];
237
238 printf("\t68K PC=%06X\n", m68k_get_reg(NULL, M68K_REG_PC));
239
240 for(i=M68K_REG_D0; i<=M68K_REG_D7; i++)
241 {
242 printf("D%i = %08X ", i-M68K_REG_D0, m68k_get_reg(NULL, (m68k_register_t)i));
243
244 if (i == M68K_REG_D3 || i == M68K_REG_D7)
245 printf("\n");
246 }
247
248 for(i=M68K_REG_A0; i<=M68K_REG_A7; i++)
249 {
250 printf("A%i = %08X ", i-M68K_REG_A0, m68k_get_reg(NULL, (m68k_register_t)i));
251
252 if (i == M68K_REG_A3 || i == M68K_REG_A7)
253 printf("\n");
254 }
255
256 currpc = m68k_get_reg(NULL, M68K_REG_PC);
257 disPC = currpc - 30;
258
259 do
260 {
261 uint32_t oldpc = disPC;
262 disPC += m68k_disassemble(buffer, disPC, 0);
263 printf("%s%08X: %s\n", (oldpc == currpc ? ">" : " "), oldpc, buffer);
264 }
265 while (disPC < (currpc + 10));
266 }
267
268
269 /* Custom UAE 68000 read/write/IRQ functions */
270
irq_ack_handler(int level)271 int irq_ack_handler(int level)
272 {
273 // Tracing the IPL lines on the Jaguar schematic yields the following:
274 // IPL1 is connected to INTL on TOM (OUT to 68K)
275 // IPL0-2 are also tied to Vcc via 4.7K resistors!
276 // (DINT on TOM goes into DINT on JERRY (IN Tom from Jerry))
277 // There doesn't seem to be any other path to IPL0 or 2 on the schematic, which means
278 // that *all* IRQs to the 68K are routed thru TOM at level 2. Which means they're all maskable.
279
280 // The GPU/DSP/etc are probably *not* issuing an NMI, but it seems to work OK...
281 // They aren't, and this causes problems with a, err, specific ROM. :-D
282
283 if (level == 2)
284 {
285 m68k_set_irq(0); // Clear the IRQ (NOTE: Without this, the BIOS fails)...
286 return 64; // Set user interrupt #0
287 }
288
289 return M68K_INT_ACK_AUTOVECTOR;
290 }
291
292
293 //#define USE_NEW_MMU
294
m68k_read_memory_8(unsigned int address)295 unsigned int m68k_read_memory_8(unsigned int address)
296 {
297 #ifdef ALPINE_FUNCTIONS
298 // Check if breakpoint on memory is active, and deal with it
299 if (bpmActive && address == bpmAddress1)
300 M68KDebugHalt();
301 #endif
302
303 // Musashi does this automagically for you, UAE core does not :-P
304 address &= 0x00FFFFFF;
305 #ifndef USE_NEW_MMU
306 // Note that the Jaguar only has 2M of RAM, not 4!
307 if ((address >= 0x000000) && (address <= 0x1FFFFF))
308 return jaguarMainRAM[address];
309 else if ((address >= 0x800000) && (address <= 0xDFFEFF))
310 return jaguarMainROM[address - 0x800000];
311 else if ((address >= 0xE00000) && (address <= 0xE3FFFF))
312 return jagMemSpace[address];
313 else if ((address >= 0xDFFF00) && (address <= 0xDFFFFF))
314 return CDROMReadByte(address, UNKNOWN);
315 else if ((address >= 0xF00000) && (address <= 0xF0FFFF))
316 return TOMReadByte(address, M68K);
317 else if ((address >= 0xF10000) && (address <= 0xF1FFFF))
318 return JERRYReadByte(address, M68K);
319 else
320 return jaguar_unknown_readbyte(address, M68K);
321
322 return 0;
323 #else
324 return MMURead8(address, M68K);
325 #endif
326 }
327
328
329 void gpu_dump_disassembly(void);
330 void gpu_dump_registers(void);
331
m68k_read_memory_16(unsigned int address)332 unsigned int m68k_read_memory_16(unsigned int address)
333 {
334 #ifdef ALPINE_FUNCTIONS
335 // Check if breakpoint on memory is active, and deal with it
336 if (bpmActive && address == bpmAddress1)
337 M68KDebugHalt();
338 #endif
339
340 // Musashi does this automagically for you, UAE core does not :-P
341 address &= 0x00FFFFFF;
342
343 #ifndef USE_NEW_MMU
344 // Note that the Jaguar only has 2M of RAM, not 4!
345 if ((address >= 0x000000) && (address <= 0x1FFFFE))
346 return GET16(jaguarMainRAM, address);
347 else if ((address >= 0x800000) && (address <= 0xDFFEFE))
348 {
349 /* Memory Track reading... */
350 if (((TOMGetMEMCON1() & 0x0006) == (2 << 1)) && (jaguarMainROMCRC32 == 0xFDF37F47))
351 return MTReadWord(address);
352 else
353 return (jaguarMainROM[address - 0x800000] << 8)
354 | jaguarMainROM[address - 0x800000 + 1];
355 }
356 else if ((address >= 0xE00000) && (address <= 0xE3FFFE))
357 return (jagMemSpace[address] << 8) | jagMemSpace[address + 1];
358 else if ((address >= 0xDFFF00) && (address <= 0xDFFFFE))
359 return CDROMReadWord(address, M68K);
360 else if ((address >= 0xF00000) && (address <= 0xF0FFFE))
361 return TOMReadWord(address, M68K);
362 else if ((address >= 0xF10000) && (address <= 0xF1FFFE))
363 return JERRYReadWord(address, M68K);
364
365 return jaguar_unknown_readword(address, M68K);
366 #else
367 return MMURead16(address, M68K);
368 #endif
369 }
370
371
m68k_read_memory_32(unsigned int address)372 unsigned int m68k_read_memory_32(unsigned int address)
373 {
374 #ifdef ALPINE_FUNCTIONS
375 // Check if breakpoint on memory is active, and deal with it
376 if (bpmActive && address == bpmAddress1)
377 M68KDebugHalt();
378 #endif
379
380 // Musashi does this automagically for you, UAE core does not :-P
381 address &= 0x00FFFFFF;
382
383 #ifndef USE_NEW_MMU
384 if ((address >= 0x800000) && (address <= 0xDFFEFE))
385 {
386 // Memory Track reading...
387 if (((TOMGetMEMCON1() & 0x0006) == (2 << 1)) && (jaguarMainROMCRC32 == 0xFDF37F47))
388 return MTReadLong(address);
389
390 return GET32(jaguarMainROM, address - 0x800000);
391 }
392
393 return (m68k_read_memory_16(address) << 16) | m68k_read_memory_16(address + 2);
394 #else
395 return MMURead32(address, M68K);
396 #endif
397 }
398
399
m68k_write_memory_8(unsigned int address,unsigned int value)400 void m68k_write_memory_8(unsigned int address, unsigned int value)
401 {
402 #ifdef ALPINE_FUNCTIONS
403 // Check if breakpoint on memory is active, and deal with it
404 if (bpmActive && address == bpmAddress1)
405 M68KDebugHalt();
406 #endif
407
408 // Musashi does this automagically for you, UAE core does not :-P
409 address &= 0x00FFFFFF;
410
411 #ifndef USE_NEW_MMU
412 // Note that the Jaguar only has 2M of RAM, not 4!
413 if ((address >= 0x000000) && (address <= 0x1FFFFF))
414 jaguarMainRAM[address] = value;
415 else if ((address >= 0xDFFF00) && (address <= 0xDFFFFF))
416 CDROMWriteByte(address, value, M68K);
417 else if ((address >= 0xF00000) && (address <= 0xF0FFFF))
418 TOMWriteByte(address, value, M68K);
419 else if ((address >= 0xF10000) && (address <= 0xF1FFFF))
420 JERRYWriteByte(address, value, M68K);
421 else
422 jaguar_unknown_writebyte(address, value, M68K);
423 #else
424 MMUWrite8(address, value, M68K);
425 #endif
426 }
427
428
m68k_write_memory_16(unsigned int address,unsigned int value)429 void m68k_write_memory_16(unsigned int address, unsigned int value)
430 {
431 #ifdef ALPINE_FUNCTIONS
432 // Check if breakpoint on memory is active, and deal with it
433 if (bpmActive && address == bpmAddress1)
434 M68KDebugHalt();
435 #endif
436
437 // Musashi does this automagically for you, UAE core does not :-P
438 address &= 0x00FFFFFF;
439
440 #ifndef USE_NEW_MMU
441 // Note that the Jaguar only has 2M of RAM, not 4!
442 if ((address >= 0x000000) && (address <= 0x1FFFFE))
443 {
444 SET16(jaguarMainRAM, address, value);
445 }
446 /* Memory Track device writes.... */
447 else if ((address >= 0x800000) && (address <= 0x87FFFE))
448 {
449 if (((TOMGetMEMCON1() & 0x0006) == (2 << 1)) && (jaguarMainROMCRC32 == 0xFDF37F47))
450 MTWriteWord(address, value);
451 }
452 else if ((address >= 0xDFFF00) && (address <= 0xDFFFFE))
453 CDROMWriteWord(address, value, M68K);
454 else if ((address >= 0xF00000) && (address <= 0xF0FFFE))
455 TOMWriteWord(address, value, M68K);
456 else if ((address >= 0xF10000) && (address <= 0xF1FFFE))
457 JERRYWriteWord(address, value, M68K);
458 else
459 {
460 jaguar_unknown_writeword(address, value, M68K);
461 }
462 #else
463 MMUWrite16(address, value, M68K);
464 #endif
465 }
466
467
m68k_write_memory_32(unsigned int address,unsigned int value)468 void m68k_write_memory_32(unsigned int address, unsigned int value)
469 {
470 #ifdef ALPINE_FUNCTIONS
471 // Check if breakpoint on memory is active, and deal with it
472 if (bpmActive && address == bpmAddress1)
473 M68KDebugHalt();
474 #endif
475
476 // Musashi does this automagically for you, UAE core does not :-P
477 address &= 0x00FFFFFF;
478
479 #ifndef USE_NEW_MMU
480 m68k_write_memory_16(address, value >> 16);
481 m68k_write_memory_16(address + 2, value & 0xFFFF);
482 #else
483 MMUWrite32(address, value, M68K);
484 #endif
485 }
486
487 /* Disassemble M68K instructions at the given offset */
488
m68k_read_disassembler_8(unsigned int address)489 unsigned int m68k_read_disassembler_8(unsigned int address)
490 {
491 return m68k_read_memory_8(address);
492 }
493
494
m68k_read_disassembler_16(unsigned int address)495 unsigned int m68k_read_disassembler_16(unsigned int address)
496 {
497 return m68k_read_memory_16(address);
498 }
499
500
m68k_read_disassembler_32(unsigned int address)501 unsigned int m68k_read_disassembler_32(unsigned int address)
502 {
503 return m68k_read_memory_32(address);
504 }
505
JaguarDasm(uint32_t offset,uint32_t qt)506 void JaguarDasm(uint32_t offset, uint32_t qt)
507 {
508 }
509
JaguarReadByte(uint32_t offset,uint32_t who)510 uint8_t JaguarReadByte(uint32_t offset, uint32_t who)
511 {
512 offset &= 0xFFFFFF;
513
514 // First 2M is mirrored in the $0 - $7FFFFF range
515 if (offset < 0x800000)
516 return jaguarMainRAM[offset & 0x1FFFFF];
517 else if ((offset >= 0x800000) && (offset < 0xDFFF00))
518 return jaguarMainROM[offset - 0x800000];
519 else if ((offset >= 0xDFFF00) && (offset <= 0xDFFFFF))
520 return CDROMReadByte(offset, who);
521 else if ((offset >= 0xE00000) && (offset < 0xE40000))
522 return jagMemSpace[offset];
523 else if ((offset >= 0xF00000) && (offset < 0xF10000))
524 return TOMReadByte(offset, who);
525 else if ((offset >= 0xF10000) && (offset < 0xF20000))
526 return JERRYReadByte(offset, who);
527 else
528 return jaguar_unknown_readbyte(offset, who);
529
530 return 0x00;
531 }
532
JaguarReadWord(uint32_t offset,uint32_t who)533 uint16_t JaguarReadWord(uint32_t offset, uint32_t who)
534 {
535 offset &= 0xFFFFFF;
536
537 // First 2M is mirrored in the $0 - $7FFFFF range
538 if (offset < 0x800000)
539 return (jaguarMainRAM[(offset+0) & 0x1FFFFF] << 8) | jaguarMainRAM[(offset+1) & 0x1FFFFF];
540 else if ((offset >= 0x800000) && (offset < 0xDFFF00))
541 {
542 offset -= 0x800000;
543 return (jaguarMainROM[offset+0] << 8) | jaguarMainROM[offset+1];
544 }
545 // else if ((offset >= 0xDFFF00) && (offset < 0xDFFF00))
546 else if ((offset >= 0xDFFF00) && (offset <= 0xDFFFFE))
547 return CDROMReadWord(offset, who);
548 else if ((offset >= 0xE00000) && (offset <= 0xE3FFFE))
549 return (jagMemSpace[offset + 0] << 8) | jagMemSpace[offset + 1];
550 else if ((offset >= 0xF00000) && (offset <= 0xF0FFFE))
551 return TOMReadWord(offset, who);
552 else if ((offset >= 0xF10000) && (offset <= 0xF1FFFE))
553 return JERRYReadWord(offset, who);
554
555 return jaguar_unknown_readword(offset, who);
556 }
557
558
JaguarWriteByte(uint32_t offset,uint8_t data,uint32_t who)559 void JaguarWriteByte(uint32_t offset, uint8_t data, uint32_t who)
560 {
561 offset &= 0xFFFFFF;
562
563 // First 2M is mirrored in the $0 - $7FFFFF range
564 if (offset < 0x800000)
565 {
566 jaguarMainRAM[offset & 0x1FFFFF] = data;
567 return;
568 }
569 else if ((offset >= 0xDFFF00) && (offset <= 0xDFFFFF))
570 {
571 CDROMWriteByte(offset, data, who);
572 return;
573 }
574 else if ((offset >= 0xF00000) && (offset <= 0xF0FFFF))
575 {
576 TOMWriteByte(offset, data, who);
577 return;
578 }
579 else if ((offset >= 0xF10000) && (offset <= 0xF1FFFF))
580 {
581 JERRYWriteByte(offset, data, who);
582 return;
583 }
584
585 jaguar_unknown_writebyte(offset, data, who);
586 }
587
588
JaguarWriteWord(uint32_t offset,uint16_t data,uint32_t who)589 void JaguarWriteWord(uint32_t offset, uint16_t data, uint32_t who)
590 {
591 offset &= 0xFFFFFF;
592
593 // First 2M is mirrored in the $0 - $7FFFFF range
594 if (offset <= 0x7FFFFE)
595 {
596 jaguarMainRAM[(offset+0) & 0x1FFFFF] = data >> 8;
597 jaguarMainRAM[(offset+1) & 0x1FFFFF] = data & 0xFF;
598 return;
599 }
600 else if (offset >= 0xDFFF00 && offset <= 0xDFFFFE)
601 {
602 CDROMWriteWord(offset, data, who);
603 return;
604 }
605 else if (offset >= 0xF00000 && offset <= 0xF0FFFE)
606 {
607 TOMWriteWord(offset, data, who);
608 return;
609 }
610 else if (offset >= 0xF10000 && offset <= 0xF1FFFE)
611 {
612 JERRYWriteWord(offset, data, who);
613 return;
614 }
615 // Don't bomb on attempts to write to ROM
616 else if (offset >= 0x800000 && offset <= 0xEFFFFF)
617 return;
618
619 jaguar_unknown_writeword(offset, data, who);
620 }
621
622
623 // We really should re-do this so that it does *real* 32-bit access... !!! FIX !!!
JaguarReadLong(uint32_t offset,uint32_t who)624 uint32_t JaguarReadLong(uint32_t offset, uint32_t who)
625 {
626 return (JaguarReadWord(offset, who) << 16) | JaguarReadWord(offset+2, who);
627 }
628
629
630 // We really should re-do this so that it does *real* 32-bit access... !!! FIX !!!
JaguarWriteLong(uint32_t offset,uint32_t data,uint32_t who)631 void JaguarWriteLong(uint32_t offset, uint32_t data, uint32_t who)
632 {
633 JaguarWriteWord(offset, data >> 16, who);
634 JaguarWriteWord(offset+2, data & 0xFFFF, who);
635 }
636
637
JaguarSetScreenBuffer(uint32_t * buffer)638 void JaguarSetScreenBuffer(uint32_t * buffer)
639 {
640 // This is in TOM, but we set it here...
641 screenBuffer = buffer;
642 }
643
644
JaguarSetScreenPitch(uint32_t pitch)645 void JaguarSetScreenPitch(uint32_t pitch)
646 {
647 // This is in TOM, but we set it here...
648 screenPitch = pitch;
649 }
650
651 /* Jaguar console initialization */
JaguarInit(void)652 void JaguarInit(void)
653 {
654 unsigned i;
655 // For randomizing RAM
656 srand(time(NULL));
657
658 // Contents of local RAM are quasi-stable; we simulate this by randomizing RAM contents
659 for(i=0; i<0x200000; i+=4)
660 *((uint32_t *)(&jaguarMainRAM[i])) = rand();
661
662 lowerField = false; // Reset the lower field flag
663 memset(jaguarMainRAM + 0x804, 0xFF, 4);
664
665 m68k_pulse_reset(); // Need to do this so UAE disasm doesn't segfault on exit
666 GPUInit();
667 DSPInit();
668 TOMInit();
669 JERRYInit();
670 CDROMInit();
671
672 }
673
674 /* New timer based code stuffola... */
675
676 // The thing to keep in mind is that the VC is advanced every HALF line, regardless
677 // of whether the display is interlaced or not. The only difference with an
678 // interlaced display is that the high bit of VC will be set when the lower
679 // field is being rendered. (NB: The high bit of VC is ALWAYS set on the lower field,
680 // regardless of whether it's in interlace mode or not.
681 // NB2: Seems it doens't always, not sure what the constraint is...)
682 //
683 // Normally, TVs will render a full frame in 1/30s (NTSC) or 1/25s (PAL) by
684 // rendering two fields that are slighty vertically offset from each other.
685 // Each field is created in 1/60s (NTSC) or 1/50s (PAL), and every other line
686 // is rendered in this mode so that each field, when overlaid on each other,
687 // will yield the final picture at the full resolution for the full frame.
688 //
689 // We execute a half frame in each timeslice (1/60s NTSC, 1/50s PAL).
690 // Since the number of lines in a FULL frame is 525 for NTSC, 625 for PAL,
691 // it will be half this number for a half frame. BUT, since we're counting
692 // HALF lines, we double this number and we're back at 525 for NTSC, 625 for PAL.
693 //
694 // Scanline times are 63.5555... μs in NTSC and 64 μs in PAL
695 // Half line times are, naturally, half of this. :-P
HalflineCallback(void)696 void HalflineCallback(void)
697 {
698 uint16_t numHalfLines;
699 uint16_t vc = TOMReadWord(0xF00006, JAGUAR);
700 uint16_t vp = TOMReadWord(0xF0003E, JAGUAR) + 1;
701 uint16_t vi = TOMReadWord(0xF0004E, JAGUAR);
702 vc++;
703
704 // Each # of lines is for a full frame == 1/30s (NTSC), 1/25s (PAL).
705 // So we cut the number of half-lines in a frame in half. :-P
706 numHalfLines = ((vjs.hardwareTypeNTSC ? 525 : 625) * 2) / 2;
707
708 if ((vc & 0x7FF) >= numHalfLines)
709 {
710 lowerField = !lowerField;
711 // If we're rendering the lower field, set the high bit (#11, counting
712 // from 0) of VC
713 vc = (lowerField ? 0x0800 : 0x0000);
714 }
715
716 //WriteLog("HLC: Currently on line %u (VP=%u)...\n", vc, vp);
717 TOMWriteWord(0xF00006, vc, JAGUAR);
718
719 // Time for Vertical Interrupt?
720 if ((vc & 0x7FF) == vi && (vc & 0x7FF) > 0 && TOMIRQEnabled(IRQ_VIDEO))
721 {
722 // We don't have to worry about autovectors & whatnot because the Jaguar
723 // tells you through its HW registers who sent the interrupt...
724 TOMSetPendingVideoInt();
725 m68k_set_irq(2);
726 }
727
728 TOMExecHalfline(vc, true);
729
730 //Change this to VBB???
731 //Doesn't seem to matter (at least for Flip Out & I-War)
732 if ((vc & 0x7FF) == 0)
733 {
734 JoystickExec();
735 frameDone = true;
736 }
737
738 SetCallbackTime(HalflineCallback, (vjs.hardwareTypeNTSC ? 31.777777777 : 32.0), EVENT_MAIN);
739 }
740
JaguarReset(void)741 void JaguarReset(void)
742 {
743 unsigned i;
744
745 // Only problem with this approach: It wipes out RAM loaded files...!
746 // Contents of local RAM are quasi-stable; we simulate this by randomizing RAM contents
747 for(i=8; i<0x200000; i+=4)
748 *((uint32_t *)(&jaguarMainRAM[i])) = rand();
749
750 // New timer base code stuffola...
751 InitializeEventList();
752 //Need to change this so it uses the single RAM space and load the BIOS
753 //into it somewhere...
754 //Also, have to change this here and in JaguarReadXX() currently
755 // Only use the system BIOS if it's available...! (it's always available now!)
756 // AND only if a jaguar cartridge has been inserted.
757 if (vjs.useJaguarBIOS && jaguarCartInserted && !vjs.hardwareTypeAlpine)
758 memcpy(jaguarMainRAM, jagMemSpace + 0xE00000, 8);
759 else
760 SET32(jaguarMainRAM, 4, jaguarRunAddress);
761
762 // WriteLog("jaguar_reset():\n");
763 TOMReset();
764 JERRYReset();
765 GPUReset();
766 DSPReset();
767 CDROMReset();
768 m68k_pulse_reset(); // Reset the 68000
769 WriteLog("Jaguar: 68K reset. PC=%06X SP=%08X\n", m68k_get_reg(NULL, M68K_REG_PC), m68k_get_reg(NULL, M68K_REG_A7));
770
771 lowerField = false; // Reset the lower field flag
772 SetCallbackTime(HalflineCallback, (vjs.hardwareTypeNTSC ? 31.777777777 : 32.0), EVENT_MAIN);
773 }
774
775
JaguarDone(void)776 void JaguarDone(void)
777 {
778 WriteLog("Jaguar: Interrupt enable = $%02X\n", TOMReadByte(0xF000E1, JAGUAR) & 0x1F);
779 WriteLog("Jaguar: Video interrupt is %s (line=%u)\n", ((TOMIRQEnabled(IRQ_VIDEO))
780 && (JaguarInterruptHandlerIsValid(64))) ? "enabled" : "disabled", TOMReadWord(0xF0004E, JAGUAR));
781 M68K_show_context();
782
783 CDROMDone();
784 GPUDone();
785 DSPDone();
786 TOMDone();
787 JERRYDone();
788 }
789
GetRamPtr(void)790 uint8_t * GetRamPtr(void)
791 {
792 return jaguarMainRAM;
793 }
794
795
796 /* New Jaguar execution stack
797 * This executes 1 frame's worth of code. */
JaguarExecuteNew(void)798 void JaguarExecuteNew(void)
799 {
800 frameDone = false;
801
802 do
803 {
804 double timeToNextEvent = GetTimeToNextEvent(EVENT_MAIN);
805 m68k_execute(USEC_TO_M68K_CYCLES(timeToNextEvent));
806 GPUExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
807 HandleNextEvent(EVENT_MAIN);
808 } while(!frameDone);
809 }
810