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