1 /* src/m68kpsp.c: Q68 emulator interface
2 Copyright 2009 Andrew Church
3
4 This file is part of Yabause.
5
6 Yabause is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Yabause is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Yabause; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "yabause.h"
22 #include "m68kcore.h"
23
24 #include "q68/q68.h"
25
26 /*************************************************************************/
27
28 /**
29 * NEED_TRAMPOLINE: Defined on platforms where we need trampoline
30 * functions to convert read/write calls from the native format to the
31 * FASTCALL format used by Yabause.
32 */
33 #ifdef CPU_X86
34 # define NEED_TRAMPOLINE
35 #endif
36
37 /**
38 * PROFILE_68K: Perform simple profiling of the 68000 emulation, reporting
39 * the average time per 68000 clock cycle. (Realtime execution would be
40 * around 88.5 nsec/cycle.)
41 *
42 * Note that this profiling has an overhead of two YabauseGetTicks() calls
43 * for each M68K->Exec() call; on PSP, this amounts to about 3.8 usec per
44 * Exec(), or 52.8 nsec/cycle at 72 cycles/call.
45 */
46 // #define PROFILE_68K
47
48 // #define COUNT_OPCODES // see COUNT_OPCODES in q68-core.c
49
50 /*************************************************************************/
51
52 /* Interface function declarations (must come before interface definition) */
53
54 static int m68kq68_init(void);
55 static void m68kq68_deinit(void);
56 static void m68kq68_reset(void);
57
58 static FASTCALL s32 m68kq68_exec(s32 cycles);
59 static void m68kq68_sync(void);
60
61 static u32 m68kq68_get_dreg(u32 num);
62 static u32 m68kq68_get_areg(u32 num);
63 static u32 m68kq68_get_pc(void);
64 static u32 m68kq68_get_sr(void);
65 static u32 m68kq68_get_usp(void);
66 static u32 m68kq68_get_ssp(void);
67
68 static void m68kq68_set_dreg(u32 num, u32 val);
69 static void m68kq68_set_areg(u32 num, u32 val);
70 static void m68kq68_set_pc(u32 val);
71 static void m68kq68_set_sr(u32 val);
72 static void m68kq68_set_usp(u32 val);
73 static void m68kq68_set_ssp(u32 val);
74
75 static FASTCALL void m68kq68_set_irq(s32 level);
76 static FASTCALL void m68kq68_write_notify(u32 address, u32 size);
77
78 static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr);
79 static void m68kq68_set_readb(M68K_READ *func);
80 static void m68kq68_set_readw(M68K_READ *func);
81 static void m68kq68_set_writeb(M68K_WRITE *func);
82 static void m68kq68_set_writew(M68K_WRITE *func);
83
84 static uint32_t dummy_read(uint32_t address);
85 static void dummy_write(uint32_t address, uint32_t data);
86
87 #ifdef NEED_TRAMPOLINE
88 static uint32_t readb_trampoline(uint32_t address);
89 static uint32_t readw_trampoline(uint32_t address);
90 static void writeb_trampoline(uint32_t address, uint32_t data);
91 static void writew_trampoline(uint32_t address, uint32_t data);
92 #endif
93
94 static void m68kq68_save_state(FILE * fp);
95 static void m68kq68_load_state(FILE * fp);
96
97 /*-----------------------------------------------------------------------*/
98
99 /* Module interface definition */
100
101 M68K_struct M68KQ68 = {
102 .id = M68KCORE_Q68,
103 .Name = "Q68 68k Emulator Interface",
104
105 .Init = m68kq68_init,
106 .DeInit = m68kq68_deinit,
107 .Reset = m68kq68_reset,
108
109 .Exec = m68kq68_exec,
110 .Sync = m68kq68_sync,
111
112 .GetDReg = m68kq68_get_dreg,
113 .GetAReg = m68kq68_get_areg,
114 .GetPC = m68kq68_get_pc,
115 .GetSR = m68kq68_get_sr,
116 .GetUSP = m68kq68_get_usp,
117 .GetMSP = m68kq68_get_ssp,
118
119 .SetDReg = m68kq68_set_dreg,
120 .SetAReg = m68kq68_set_areg,
121 .SetPC = m68kq68_set_pc,
122 .SetSR = m68kq68_set_sr,
123 .SetUSP = m68kq68_set_usp,
124 .SetMSP = m68kq68_set_ssp,
125
126 .SetIRQ = m68kq68_set_irq,
127 .WriteNotify = m68kq68_write_notify,
128
129 .SetFetch = m68kq68_set_fetch,
130 .SetReadB = m68kq68_set_readb,
131 .SetReadW = m68kq68_set_readw,
132 .SetWriteB = m68kq68_set_writeb,
133 .SetWriteW = m68kq68_set_writew,
134 .SaveState = m68kq68_save_state,
135 .LoadState = m68kq68_load_state,
136 };
137
138 /*-----------------------------------------------------------------------*/
139
140 /* Virtual processor state block */
141
142 static Q68State *state;
143
144
145 #ifdef NEED_TRAMPOLINE
146
147 /* Read/write functions passed to Set{Read,Write}[BW], called via the
148 * trampolines */
149 static M68K_READ *real_readb, *real_readw;
150 static M68K_WRITE *real_writeb, *real_writew;
151
152 #endif
153
154 /*************************************************************************/
155 /************************** Interface functions **************************/
156 /*************************************************************************/
157
158 /**
159 * m68kq68_init: Initialize the virtual processpr.
160 *
161 * [Parameters]
162 * None
163 * [Return value]
164 * Zero on success, negative on failure
165 */
m68kq68_init(void)166 static int m68kq68_init(void)
167 {
168 if (!(state = q68_create())) {
169 return -1;
170 }
171 q68_set_irq(state, 0);
172 q68_set_readb_func(state, dummy_read);
173 q68_set_readw_func(state, dummy_read);
174 q68_set_writeb_func(state, dummy_write);
175 q68_set_writew_func(state, dummy_write);
176
177 return 0;
178 }
179
180 /*-----------------------------------------------------------------------*/
181
182 /**
183 * m68kq68_deinit: Destroy the virtual processor.
184 *
185 * [Parameters]
186 * None
187 * [Return value]
188 * None
189 */
m68kq68_deinit(void)190 static void m68kq68_deinit(void)
191 {
192 q68_destroy(state);
193 state = NULL;
194 }
195
196 /*-----------------------------------------------------------------------*/
197
198 /**
199 * m68kq68_reset: Reset the virtual processor.
200 *
201 * [Parameters]
202 * None
203 * [Return value]
204 * None
205 */
m68kq68_reset(void)206 static void m68kq68_reset(void)
207 {
208 q68_reset(state);
209 }
210
211 /*************************************************************************/
212
213 /**
214 * m68kq68_exec: Execute instructions for the given number of clock cycles.
215 *
216 * [Parameters]
217 * cycles: Number of clock cycles to execute
218 * [Return value]
219 * Number of clock cycles actually executed
220 */
m68kq68_exec(s32 cycles)221 static FASTCALL s32 m68kq68_exec(s32 cycles)
222 {
223 #ifdef PROFILE_68K
224 static uint32_t tot_cycles = 0, tot_usec = 0, tot_ticks = 0;
225 static uint32_t last_report = 0;
226 uint32_t start, end;
227 start = (uint32_t) YabauseGetTicks();
228 int retval = q68_run(state, cycles);
229 end = (uint32_t) YabauseGetTicks();
230 tot_cycles += cycles;
231 tot_ticks += end - start;
232 if (tot_cycles/1000000 > last_report) {
233 tot_usec += (uint64_t)tot_ticks * 1000000 / yabsys.tickfreq;
234 tot_ticks = 0;
235 fprintf(stderr, "%ld cycles in %.3f sec = %.3f nsec/cycle\n",
236 (long)tot_cycles, (double)tot_usec/1000000,
237 ((double)tot_usec / (double)tot_cycles) * 1000);
238 last_report = tot_cycles/1000000;
239 # ifdef COUNT_OPCODES
240 if (last_report % 100 == 0) {
241 extern uint32_t q68_ops[128], q68_4xxx_ops[32];
242 int i;
243 fprintf(stderr, "Opcodes per 1M cycles:\n");
244 for (i = 0; i < 128; i++) {
245 fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "",
246 (long)((q68_ops[i] + last_report/2) / last_report),
247 i%8==7 ? "\n" : "");
248 }
249 fprintf(stderr, "$4xxx opcodes per 1M cycles:\n");
250 for (i = 0; i < 32; i++) {
251 fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "",
252 (long)((q68_4xxx_ops[i] + last_report/2) / last_report),
253 i%8==7 ? "\n" : "");
254 }
255 }
256 # endif // COUNT_OPCODES
257 }
258 return retval;
259 #else // !PROFILE_68K
260 return q68_run(state, cycles);
261 #endif
262 }
263
264 /*-----------------------------------------------------------------------*/
265
266 /**
267 * m68kq68_sync: Wait for background execution to finish.
268 *
269 * [Parameters]
270 * None
271 * [Return value]
272 * None
273 */
m68kq68_sync(void)274 static void m68kq68_sync(void)
275 {
276 /* Nothing to do */
277 }
278
279 /*************************************************************************/
280
281 /**
282 * m68kq68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of
283 * the specified register.
284 *
285 * [Parameters]
286 * num: Register number (m68kq68_get_dreg(), m68kq68_get_areg() only)
287 * [Return value]
288 * None
289 */
290
m68kq68_get_dreg(u32 num)291 static u32 m68kq68_get_dreg(u32 num)
292 {
293 return q68_get_dreg(state, num);
294 }
295
m68kq68_get_areg(u32 num)296 static u32 m68kq68_get_areg(u32 num)
297 {
298 return q68_get_areg(state, num);
299 }
300
m68kq68_get_pc(void)301 static u32 m68kq68_get_pc(void)
302 {
303 return q68_get_pc(state);
304 }
305
m68kq68_get_sr(void)306 static u32 m68kq68_get_sr(void)
307 {
308 return q68_get_sr(state);
309 }
310
m68kq68_get_usp(void)311 static u32 m68kq68_get_usp(void)
312 {
313 return q68_get_usp(state);
314 }
315
m68kq68_get_ssp(void)316 static u32 m68kq68_get_ssp(void)
317 {
318 return q68_get_ssp(state);
319 }
320
321 /*-----------------------------------------------------------------------*/
322
323 /**
324 * m68kq68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified
325 * register.
326 *
327 * [Parameters]
328 * num: Register number (m68kq68_set_dreg(), m68kq68_set_areg() only)
329 * val: Value to set
330 * [Return value]
331 * None
332 */
333
m68kq68_set_dreg(u32 num,u32 val)334 static void m68kq68_set_dreg(u32 num, u32 val)
335 {
336 q68_set_dreg(state, num, val);
337 }
338
m68kq68_set_areg(u32 num,u32 val)339 static void m68kq68_set_areg(u32 num, u32 val)
340 {
341 q68_set_areg(state, num, val);
342 }
343
m68kq68_set_pc(u32 val)344 static void m68kq68_set_pc(u32 val)
345 {
346 q68_set_pc(state, val);
347 }
348
m68kq68_set_sr(u32 val)349 static void m68kq68_set_sr(u32 val)
350 {
351 q68_set_sr(state, val);
352 }
353
m68kq68_set_usp(u32 val)354 static void m68kq68_set_usp(u32 val)
355 {
356 q68_set_usp(state, val);
357 }
358
m68kq68_set_ssp(u32 val)359 static void m68kq68_set_ssp(u32 val)
360 {
361 q68_set_ssp(state, val);
362 }
363
364 /*************************************************************************/
365
366 /**
367 * m68kq68_set_irq: Deliver an interrupt to the processor.
368 *
369 * [Parameters]
370 * level: Interrupt level (0-7)
371 * [Return value]
372 * None
373 */
m68kq68_set_irq(s32 level)374 static FASTCALL void m68kq68_set_irq(s32 level)
375 {
376 q68_set_irq(state, level);
377 }
378
379 /*-----------------------------------------------------------------------*/
380
381 /**
382 * m68kq68_write_notify: Inform the 68k emulator that the given address
383 * range has been modified.
384 *
385 * [Parameters]
386 * address: 68000 address of modified data
387 * size: Size of modified data in bytes
388 * [Return value]
389 * None
390 */
m68kq68_write_notify(u32 address,u32 size)391 static FASTCALL void m68kq68_write_notify(u32 address, u32 size)
392 {
393 q68_touch_memory(state, address, size);
394 }
395
396 /*************************************************************************/
397
398 /**
399 * m68kq68_set_fetch: Set the instruction fetch pointer for a region of
400 * memory. Not used by Q68.
401 *
402 * [Parameters]
403 * low_addr: Low address of memory region to set
404 * high_addr: High address of memory region to set
405 * fetch_addr: Pointer to corresponding memory region (NULL to disable)
406 * [Return value]
407 * None
408 */
m68kq68_set_fetch(u32 low_addr,u32 high_addr,pointer fetch_addr)409 static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr)
410 {
411 }
412
413 /*-----------------------------------------------------------------------*/
414
415 /**
416 * m68kq68_set_{readb,readw,writeb,writew}: Set functions for reading or
417 * writing bytes or words in memory.
418 *
419 * [Parameters]
420 * func: Function to set
421 * [Return value]
422 * None
423 */
424
m68kq68_set_readb(M68K_READ * func)425 static void m68kq68_set_readb(M68K_READ *func)
426 {
427 #ifdef NEED_TRAMPOLINE
428 real_readb = func;
429 q68_set_readb_func(state, readb_trampoline);
430 #else
431 q68_set_readb_func(state, (Q68ReadFunc *)func);
432 #endif
433 }
434
m68kq68_set_readw(M68K_READ * func)435 static void m68kq68_set_readw(M68K_READ *func)
436 {
437 #ifdef NEED_TRAMPOLINE
438 real_readw = func;
439 q68_set_readw_func(state, readw_trampoline);
440 #else
441 q68_set_readw_func(state, (Q68ReadFunc *)func);
442 #endif
443 }
444
m68kq68_set_writeb(M68K_WRITE * func)445 static void m68kq68_set_writeb(M68K_WRITE *func)
446 {
447 #ifdef NEED_TRAMPOLINE
448 real_writeb = func;
449 q68_set_writeb_func(state, writeb_trampoline);
450 #else
451 q68_set_writeb_func(state, (Q68WriteFunc *)func);
452 #endif
453 }
454
m68kq68_set_writew(M68K_WRITE * func)455 static void m68kq68_set_writew(M68K_WRITE *func)
456 {
457 #ifdef NEED_TRAMPOLINE
458 real_writew = func;
459 q68_set_writew_func(state, writew_trampoline);
460 #else
461 q68_set_writew_func(state, (Q68WriteFunc *)func);
462 #endif
463 }
464
465 /*************************************************************************/
466
467 /**
468 * dummy_read: Default read function, always returning 0 for any address.
469 *
470 * [Parameters]
471 * address: Address to read from
472 * [Return value]
473 * Value read (always zero)
474 */
dummy_read(uint32_t address)475 static uint32_t dummy_read(uint32_t address)
476 {
477 return 0;
478 }
479
480 /*-----------------------------------------------------------------------*/
481
482 /**
483 * dummy_write: Default write function, ignoring all writes.
484 *
485 * [Parameters]
486 * address: Address to write to
487 * data: Value to write
488 * [Return value]
489 * None
490 */
dummy_write(uint32_t address,uint32_t data)491 static void dummy_write(uint32_t address, uint32_t data)
492 {
493 }
494
495 /*-----------------------------------------------------------------------*/
496
497 #ifdef NEED_TRAMPOLINE
498
499 /**
500 * read[bw]_trampoline, write[bw]_trampoline: Adjust calling conventions
501 * between the M68k emulator and Yabause's M68k core.
502 *
503 * [Parameters]
504 * address: Address to read from
505 * data: Value to write (only for write_trampoline)
506 * [Return value]
507 * Value read (only for read_trampoline)
508 */
509
readb_trampoline(uint32_t address)510 static uint32_t readb_trampoline(uint32_t address) {
511 return (*real_readb)(address);
512 }
513
readw_trampoline(uint32_t address)514 static uint32_t readw_trampoline(uint32_t address) {
515 return (*real_readw)(address);
516 }
517
writeb_trampoline(uint32_t address,uint32_t data)518 static void writeb_trampoline(uint32_t address, uint32_t data) {
519 return (*real_writeb)(address, data);
520 }
521
writew_trampoline(uint32_t address,uint32_t data)522 static void writew_trampoline(uint32_t address, uint32_t data) {
523 return (*real_writew)(address, data);
524 }
525
526 #endif // NEED_TRAMPOLINE
527
m68kq68_save_state(FILE * fp)528 static void m68kq68_save_state(FILE * fp)
529 {
530 int i = 0;
531 u32 val = 0;
532 IOCheck_struct check = { 0, 0 };
533
534 for (i = 0; i < 8; i++)
535 {
536 val = q68_get_dreg(state, i);
537 ywrite(&check, (void *)&val, sizeof(u32), 1, fp);
538 }
539
540 for (i = 0; i < 8; i++)
541 {
542 val = q68_get_areg(state, i, val);
543 ywrite(&check, (void *)&val, sizeof(u32), 1, fp);
544 }
545
546 val = q68_get_pc(state, val);
547 ywrite(&check, (void *)&val, sizeof(u32), 1, fp);
548
549 val = q68_get_sr(state, val);
550 ywrite(&check, (void *)&val, sizeof(u32), 1, fp);
551
552 val = q68_get_usp(state, val);
553 ywrite(&check, (void *)&val, sizeof(u32), 1, fp);
554
555 val = q68_get_ssp(state, val);
556 ywrite(&check, (void *)&val, sizeof(u32), 1, fp);
557 }
558
m68kq68_load_state(FILE * fp)559 static void m68kq68_load_state(FILE * fp)
560 {
561 int i = 0;
562 u32 val = 0;
563 IOCheck_struct check = { 0, 0 };
564
565 for (i = 0; i < 8; i++)
566 {
567 yread(&check, (void *)&val, sizeof(u32), 1, fp);
568 q68_set_dreg(state, i, val);
569 }
570
571 for (i = 0; i < 8; i++)
572 {
573 yread(&check, (void *)&val, sizeof(u32), 1, fp);
574 q68_set_areg(state, i, val);
575 }
576
577 yread(&check, (void *)&val, sizeof(u32), 1, fp);
578 q68_set_pc(state, val);
579
580 yread(&check, (void *)&val, sizeof(u32), 1, fp);
581 q68_set_sr(state, val);
582
583 yread(&check, (void *)&val, sizeof(u32), 1, fp);
584 q68_set_usp(state, val);
585
586 yread(&check, (void *)&val, sizeof(u32), 1, fp);
587 q68_set_ssp(state, val);
588 }
589
590 /*************************************************************************/
591 /*************************************************************************/
592
593 /*
594 * Local variables:
595 * c-file-style: "stroustrup"
596 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
597 * indent-tabs-mode: nil
598 * End:
599 *
600 * vim: expandtab shiftwidth=4:
601 */
602