/* src/m68kpsp.c: Q68 emulator interface Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "yabause.h" #include "m68kcore.h" #include "q68/q68.h" /*************************************************************************/ /** * NEED_TRAMPOLINE: Defined on platforms where we need trampoline * functions to convert read/write calls from the native format to the * FASTCALL format used by Yabause. */ #ifdef CPU_X86 # define NEED_TRAMPOLINE #endif /** * PROFILE_68K: Perform simple profiling of the 68000 emulation, reporting * the average time per 68000 clock cycle. (Realtime execution would be * around 88.5 nsec/cycle.) * * Note that this profiling has an overhead of two YabauseGetTicks() calls * for each M68K->Exec() call; on PSP, this amounts to about 3.8 usec per * Exec(), or 52.8 nsec/cycle at 72 cycles/call. */ // #define PROFILE_68K // #define COUNT_OPCODES // see COUNT_OPCODES in q68-core.c /*************************************************************************/ /* Interface function declarations (must come before interface definition) */ static int m68kq68_init(void); static void m68kq68_deinit(void); static void m68kq68_reset(void); static FASTCALL s32 m68kq68_exec(s32 cycles); static void m68kq68_sync(void); static u32 m68kq68_get_dreg(u32 num); static u32 m68kq68_get_areg(u32 num); static u32 m68kq68_get_pc(void); static u32 m68kq68_get_sr(void); static u32 m68kq68_get_usp(void); static u32 m68kq68_get_ssp(void); static void m68kq68_set_dreg(u32 num, u32 val); static void m68kq68_set_areg(u32 num, u32 val); static void m68kq68_set_pc(u32 val); static void m68kq68_set_sr(u32 val); static void m68kq68_set_usp(u32 val); static void m68kq68_set_ssp(u32 val); static FASTCALL void m68kq68_set_irq(s32 level); static FASTCALL void m68kq68_write_notify(u32 address, u32 size); static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr); static void m68kq68_set_readb(M68K_READ *func); static void m68kq68_set_readw(M68K_READ *func); static void m68kq68_set_writeb(M68K_WRITE *func); static void m68kq68_set_writew(M68K_WRITE *func); static uint32_t dummy_read(uint32_t address); static void dummy_write(uint32_t address, uint32_t data); #ifdef NEED_TRAMPOLINE static uint32_t readb_trampoline(uint32_t address); static uint32_t readw_trampoline(uint32_t address); static void writeb_trampoline(uint32_t address, uint32_t data); static void writew_trampoline(uint32_t address, uint32_t data); #endif /*-----------------------------------------------------------------------*/ /* Module interface definition */ M68K_struct M68KQ68 = { .id = M68KCORE_Q68, .Name = "Q68 68k Emulator Interface", .Init = m68kq68_init, .DeInit = m68kq68_deinit, .Reset = m68kq68_reset, .Exec = m68kq68_exec, .Sync = m68kq68_sync, .GetDReg = m68kq68_get_dreg, .GetAReg = m68kq68_get_areg, .GetPC = m68kq68_get_pc, .GetSR = m68kq68_get_sr, .GetUSP = m68kq68_get_usp, .GetMSP = m68kq68_get_ssp, .SetDReg = m68kq68_set_dreg, .SetAReg = m68kq68_set_areg, .SetPC = m68kq68_set_pc, .SetSR = m68kq68_set_sr, .SetUSP = m68kq68_set_usp, .SetMSP = m68kq68_set_ssp, .SetIRQ = m68kq68_set_irq, .WriteNotify = m68kq68_write_notify, .SetFetch = m68kq68_set_fetch, .SetReadB = m68kq68_set_readb, .SetReadW = m68kq68_set_readw, .SetWriteB = m68kq68_set_writeb, .SetWriteW = m68kq68_set_writew, }; /*-----------------------------------------------------------------------*/ /* Virtual processor state block */ static Q68State *state; #ifdef NEED_TRAMPOLINE /* Read/write functions passed to Set{Read,Write}[BW], called via the * trampolines */ static M68K_READ *real_readb, *real_readw; static M68K_WRITE *real_writeb, *real_writew; #endif /*************************************************************************/ /************************** Interface functions **************************/ /*************************************************************************/ /** * m68kq68_init: Initialize the virtual processpr. * * [Parameters] * None * [Return value] * Zero on success, negative on failure */ static int m68kq68_init(void) { if (!(state = q68_create())) { return -1; } q68_set_irq(state, 0); q68_set_readb_func(state, dummy_read); q68_set_readw_func(state, dummy_read); q68_set_writeb_func(state, dummy_write); q68_set_writew_func(state, dummy_write); return 0; } /*-----------------------------------------------------------------------*/ /** * m68kq68_deinit: Destroy the virtual processor. * * [Parameters] * None * [Return value] * None */ static void m68kq68_deinit(void) { q68_destroy(state); state = NULL; } /*-----------------------------------------------------------------------*/ /** * m68kq68_reset: Reset the virtual processor. * * [Parameters] * None * [Return value] * None */ static void m68kq68_reset(void) { q68_reset(state); } /*************************************************************************/ /** * m68kq68_exec: Execute instructions for the given number of clock cycles. * * [Parameters] * cycles: Number of clock cycles to execute * [Return value] * Number of clock cycles actually executed */ static FASTCALL s32 m68kq68_exec(s32 cycles) { #ifdef PROFILE_68K static uint32_t tot_cycles = 0, tot_usec = 0, tot_ticks = 0; static uint32_t last_report = 0; uint32_t start, end; start = (uint32_t) YabauseGetTicks(); int retval = q68_run(state, cycles); end = (uint32_t) YabauseGetTicks(); tot_cycles += cycles; tot_ticks += end - start; if (tot_cycles/1000000 > last_report) { tot_usec += (uint64_t)tot_ticks * 1000000 / yabsys.tickfreq; tot_ticks = 0; fprintf(stderr, "%ld cycles in %.3f sec = %.3f nsec/cycle\n", (long)tot_cycles, (double)tot_usec/1000000, ((double)tot_usec / (double)tot_cycles) * 1000); last_report = tot_cycles/1000000; # ifdef COUNT_OPCODES if (last_report % 100 == 0) { extern uint32_t q68_ops[128], q68_4xxx_ops[32]; int i; fprintf(stderr, "Opcodes per 1M cycles:\n"); for (i = 0; i < 128; i++) { fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "", (long)((q68_ops[i] + last_report/2) / last_report), i%8==7 ? "\n" : ""); } fprintf(stderr, "$4xxx opcodes per 1M cycles:\n"); for (i = 0; i < 32; i++) { fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "", (long)((q68_4xxx_ops[i] + last_report/2) / last_report), i%8==7 ? "\n" : ""); } } # endif // COUNT_OPCODES } return retval; #else // !PROFILE_68K return q68_run(state, cycles); #endif } /*-----------------------------------------------------------------------*/ /** * m68kq68_sync: Wait for background execution to finish. * * [Parameters] * None * [Return value] * None */ static void m68kq68_sync(void) { /* Nothing to do */ } /*************************************************************************/ /** * m68kq68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of * the specified register. * * [Parameters] * num: Register number (m68kq68_get_dreg(), m68kq68_get_areg() only) * [Return value] * None */ static u32 m68kq68_get_dreg(u32 num) { return q68_get_dreg(state, num); } static u32 m68kq68_get_areg(u32 num) { return q68_get_areg(state, num); } static u32 m68kq68_get_pc(void) { return q68_get_pc(state); } static u32 m68kq68_get_sr(void) { return q68_get_sr(state); } static u32 m68kq68_get_usp(void) { return q68_get_usp(state); } static u32 m68kq68_get_ssp(void) { return q68_get_ssp(state); } /*-----------------------------------------------------------------------*/ /** * m68kq68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified * register. * * [Parameters] * num: Register number (m68kq68_set_dreg(), m68kq68_set_areg() only) * val: Value to set * [Return value] * None */ static void m68kq68_set_dreg(u32 num, u32 val) { q68_set_dreg(state, num, val); } static void m68kq68_set_areg(u32 num, u32 val) { q68_set_areg(state, num, val); } static void m68kq68_set_pc(u32 val) { q68_set_pc(state, val); } static void m68kq68_set_sr(u32 val) { q68_set_sr(state, val); } static void m68kq68_set_usp(u32 val) { q68_set_usp(state, val); } static void m68kq68_set_ssp(u32 val) { q68_set_ssp(state, val); } /*************************************************************************/ /** * m68kq68_set_irq: Deliver an interrupt to the processor. * * [Parameters] * level: Interrupt level (0-7) * [Return value] * None */ static FASTCALL void m68kq68_set_irq(s32 level) { q68_set_irq(state, level); } /*-----------------------------------------------------------------------*/ /** * m68kq68_write_notify: Inform the 68k emulator that the given address * range has been modified. * * [Parameters] * address: 68000 address of modified data * size: Size of modified data in bytes * [Return value] * None */ static FASTCALL void m68kq68_write_notify(u32 address, u32 size) { q68_touch_memory(state, address, size); } /*************************************************************************/ /** * m68kq68_set_fetch: Set the instruction fetch pointer for a region of * memory. Not used by Q68. * * [Parameters] * low_addr: Low address of memory region to set * high_addr: High address of memory region to set * fetch_addr: Pointer to corresponding memory region (NULL to disable) * [Return value] * None */ static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr) { } /*-----------------------------------------------------------------------*/ /** * m68kq68_set_{readb,readw,writeb,writew}: Set functions for reading or * writing bytes or words in memory. * * [Parameters] * func: Function to set * [Return value] * None */ static void m68kq68_set_readb(M68K_READ *func) { #ifdef NEED_TRAMPOLINE real_readb = func; q68_set_readb_func(state, readb_trampoline); #else q68_set_readb_func(state, (Q68ReadFunc *)func); #endif } static void m68kq68_set_readw(M68K_READ *func) { #ifdef NEED_TRAMPOLINE real_readw = func; q68_set_readw_func(state, readw_trampoline); #else q68_set_readw_func(state, (Q68ReadFunc *)func); #endif } static void m68kq68_set_writeb(M68K_WRITE *func) { #ifdef NEED_TRAMPOLINE real_writeb = func; q68_set_writeb_func(state, writeb_trampoline); #else q68_set_writeb_func(state, (Q68WriteFunc *)func); #endif } static void m68kq68_set_writew(M68K_WRITE *func) { #ifdef NEED_TRAMPOLINE real_writew = func; q68_set_writew_func(state, writew_trampoline); #else q68_set_writew_func(state, (Q68WriteFunc *)func); #endif } /*************************************************************************/ /** * dummy_read: Default read function, always returning 0 for any address. * * [Parameters] * address: Address to read from * [Return value] * Value read (always zero) */ static uint32_t dummy_read(uint32_t address) { return 0; } /*-----------------------------------------------------------------------*/ /** * dummy_write: Default write function, ignoring all writes. * * [Parameters] * address: Address to write to * data: Value to write * [Return value] * None */ static void dummy_write(uint32_t address, uint32_t data) { } /*-----------------------------------------------------------------------*/ #ifdef NEED_TRAMPOLINE /** * read[bw]_trampoline, write[bw]_trampoline: Adjust calling conventions * between the M68k emulator and Yabause's M68k core. * * [Parameters] * address: Address to read from * data: Value to write (only for write_trampoline) * [Return value] * Value read (only for read_trampoline) */ static uint32_t readb_trampoline(uint32_t address) { return (*real_readb)(address); } static uint32_t readw_trampoline(uint32_t address) { return (*real_readw)(address); } static void writeb_trampoline(uint32_t address, uint32_t data) { return (*real_writeb)(address, data); } static void writew_trampoline(uint32_t address, uint32_t data) { return (*real_writew)(address, data); } #endif // NEED_TRAMPOLINE /*************************************************************************/ /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */