/* src/m68kpsp.c: Q68 emulator interface1Copyright 2009 Andrew Church23This file is part of Yabause.45Yabause is free software; you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation; either version 2 of the License, or8(at your option) any later version.910Yabause is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with Yabause; if not, write to the Free Software17Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA18*/1920#include "yabause.h"21#include "m68kcore.h"2223#include "q68/q68.h"2425/*************************************************************************/2627/**28* NEED_TRAMPOLINE: Defined on platforms where we need trampoline29* functions to convert read/write calls from the native format to the30* FASTCALL format used by Yabause.31*/32#ifdef CPU_X8633# define NEED_TRAMPOLINE34#endif3536/**37* PROFILE_68K: Perform simple profiling of the 68000 emulation, reporting38* the average time per 68000 clock cycle. (Realtime execution would be39* around 88.5 nsec/cycle.)40*41* Note that this profiling has an overhead of two YabauseGetTicks() calls42* for each M68K->Exec() call; on PSP, this amounts to about 3.8 usec per43* Exec(), or 52.8 nsec/cycle at 72 cycles/call.44*/45// #define PROFILE_68K4647// #define COUNT_OPCODES // see COUNT_OPCODES in q68-core.c4849/*************************************************************************/5051/* Interface function declarations (must come before interface definition) */5253static int m68kq68_init(void);54static void m68kq68_deinit(void);55static void m68kq68_reset(void);5657static FASTCALL s32 m68kq68_exec(s32 cycles);58static void m68kq68_sync(void);5960static u32 m68kq68_get_dreg(u32 num);61static u32 m68kq68_get_areg(u32 num);62static u32 m68kq68_get_pc(void);63static u32 m68kq68_get_sr(void);64static u32 m68kq68_get_usp(void);65static u32 m68kq68_get_ssp(void);6667static void m68kq68_set_dreg(u32 num, u32 val);68static void m68kq68_set_areg(u32 num, u32 val);69static void m68kq68_set_pc(u32 val);70static void m68kq68_set_sr(u32 val);71static void m68kq68_set_usp(u32 val);72static void m68kq68_set_ssp(u32 val);7374static FASTCALL void m68kq68_set_irq(s32 level);75static FASTCALL void m68kq68_write_notify(u32 address, u32 size);7677static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr);78static void m68kq68_set_readb(M68K_READ *func);79static void m68kq68_set_readw(M68K_READ *func);80static void m68kq68_set_writeb(M68K_WRITE *func);81static void m68kq68_set_writew(M68K_WRITE *func);8283static uint32_t dummy_read(uint32_t address);84static void dummy_write(uint32_t address, uint32_t data);8586#ifdef NEED_TRAMPOLINE87static uint32_t readb_trampoline(uint32_t address);88static uint32_t readw_trampoline(uint32_t address);89static void writeb_trampoline(uint32_t address, uint32_t data);90static void writew_trampoline(uint32_t address, uint32_t data);91#endif9293/*-----------------------------------------------------------------------*/9495/* Module interface definition */9697M68K_struct M68KQ68 = {98.id = M68KCORE_Q68,99.Name = "Q68 68k Emulator Interface",100101.Init = m68kq68_init,102.DeInit = m68kq68_deinit,103.Reset = m68kq68_reset,104105.Exec = m68kq68_exec,106.Sync = m68kq68_sync,107108.GetDReg = m68kq68_get_dreg,109.GetAReg = m68kq68_get_areg,110.GetPC = m68kq68_get_pc,111.GetSR = m68kq68_get_sr,112.GetUSP = m68kq68_get_usp,113.GetMSP = m68kq68_get_ssp,114115.SetDReg = m68kq68_set_dreg,116.SetAReg = m68kq68_set_areg,117.SetPC = m68kq68_set_pc,118.SetSR = m68kq68_set_sr,119.SetUSP = m68kq68_set_usp,120.SetMSP = m68kq68_set_ssp,121122.SetIRQ = m68kq68_set_irq,123.WriteNotify = m68kq68_write_notify,124125.SetFetch = m68kq68_set_fetch,126.SetReadB = m68kq68_set_readb,127.SetReadW = m68kq68_set_readw,128.SetWriteB = m68kq68_set_writeb,129.SetWriteW = m68kq68_set_writew,130};131132/*-----------------------------------------------------------------------*/133134/* Virtual processor state block */135136static Q68State *state;137138139#ifdef NEED_TRAMPOLINE140141/* Read/write functions passed to Set{Read,Write}[BW], called via the142* trampolines */143static M68K_READ *real_readb, *real_readw;144static M68K_WRITE *real_writeb, *real_writew;145146#endif147148/*************************************************************************/149/************************** Interface functions **************************/150/*************************************************************************/151152/**153* m68kq68_init: Initialize the virtual processpr.154*155* [Parameters]156* None157* [Return value]158* Zero on success, negative on failure159*/160static int m68kq68_init(void)161{162if (!(state = q68_create())) {163return -1;164}165q68_set_irq(state, 0);166q68_set_readb_func(state, dummy_read);167q68_set_readw_func(state, dummy_read);168q68_set_writeb_func(state, dummy_write);169q68_set_writew_func(state, dummy_write);170171return 0;172}173174/*-----------------------------------------------------------------------*/175176/**177* m68kq68_deinit: Destroy the virtual processor.178*179* [Parameters]180* None181* [Return value]182* None183*/184static void m68kq68_deinit(void)185{186q68_destroy(state);187state = NULL;188}189190/*-----------------------------------------------------------------------*/191192/**193* m68kq68_reset: Reset the virtual processor.194*195* [Parameters]196* None197* [Return value]198* None199*/200static void m68kq68_reset(void)201{202q68_reset(state);203}204205/*************************************************************************/206207/**208* m68kq68_exec: Execute instructions for the given number of clock cycles.209*210* [Parameters]211* cycles: Number of clock cycles to execute212* [Return value]213* Number of clock cycles actually executed214*/215static FASTCALL s32 m68kq68_exec(s32 cycles)216{217#ifdef PROFILE_68K218static uint32_t tot_cycles = 0, tot_usec = 0, tot_ticks = 0;219static uint32_t last_report = 0;220uint32_t start, end;221start = (uint32_t) YabauseGetTicks();222int retval = q68_run(state, cycles);223end = (uint32_t) YabauseGetTicks();224tot_cycles += cycles;225tot_ticks += end - start;226if (tot_cycles/1000000 > last_report) {227tot_usec += (uint64_t)tot_ticks * 1000000 / yabsys.tickfreq;228tot_ticks = 0;229fprintf(stderr, "%ld cycles in %.3f sec = %.3f nsec/cycle\n",230(long)tot_cycles, (double)tot_usec/1000000,231((double)tot_usec / (double)tot_cycles) * 1000);232last_report = tot_cycles/1000000;233# ifdef COUNT_OPCODES234if (last_report % 100 == 0) {235extern uint32_t q68_ops[128], q68_4xxx_ops[32];236int i;237fprintf(stderr, "Opcodes per 1M cycles:\n");238for (i = 0; i < 128; i++) {239fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "",240(long)((q68_ops[i] + last_report/2) / last_report),241i%8==7 ? "\n" : "");242}243fprintf(stderr, "$4xxx opcodes per 1M cycles:\n");244for (i = 0; i < 32; i++) {245fprintf(stderr, "%s%8ld%s", i%8==0 ? " " : "",246(long)((q68_4xxx_ops[i] + last_report/2) / last_report),247i%8==7 ? "\n" : "");248}249}250# endif // COUNT_OPCODES251}252return retval;253#else // !PROFILE_68K254return q68_run(state, cycles);255#endif256}257258/*-----------------------------------------------------------------------*/259260/**261* m68kq68_sync: Wait for background execution to finish.262*263* [Parameters]264* None265* [Return value]266* None267*/268static void m68kq68_sync(void)269{270/* Nothing to do */271}272273/*************************************************************************/274275/**276* m68kq68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of277* the specified register.278*279* [Parameters]280* num: Register number (m68kq68_get_dreg(), m68kq68_get_areg() only)281* [Return value]282* None283*/284285static u32 m68kq68_get_dreg(u32 num)286{287return q68_get_dreg(state, num);288}289290static u32 m68kq68_get_areg(u32 num)291{292return q68_get_areg(state, num);293}294295static u32 m68kq68_get_pc(void)296{297return q68_get_pc(state);298}299300static u32 m68kq68_get_sr(void)301{302return q68_get_sr(state);303}304305static u32 m68kq68_get_usp(void)306{307return q68_get_usp(state);308}309310static u32 m68kq68_get_ssp(void)311{312return q68_get_ssp(state);313}314315/*-----------------------------------------------------------------------*/316317/**318* m68kq68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified319* register.320*321* [Parameters]322* num: Register number (m68kq68_set_dreg(), m68kq68_set_areg() only)323* val: Value to set324* [Return value]325* None326*/327328static void m68kq68_set_dreg(u32 num, u32 val)329{330q68_set_dreg(state, num, val);331}332333static void m68kq68_set_areg(u32 num, u32 val)334{335q68_set_areg(state, num, val);336}337338static void m68kq68_set_pc(u32 val)339{340q68_set_pc(state, val);341}342343static void m68kq68_set_sr(u32 val)344{345q68_set_sr(state, val);346}347348static void m68kq68_set_usp(u32 val)349{350q68_set_usp(state, val);351}352353static void m68kq68_set_ssp(u32 val)354{355q68_set_ssp(state, val);356}357358/*************************************************************************/359360/**361* m68kq68_set_irq: Deliver an interrupt to the processor.362*363* [Parameters]364* level: Interrupt level (0-7)365* [Return value]366* None367*/368static FASTCALL void m68kq68_set_irq(s32 level)369{370q68_set_irq(state, level);371}372373/*-----------------------------------------------------------------------*/374375/**376* m68kq68_write_notify: Inform the 68k emulator that the given address377* range has been modified.378*379* [Parameters]380* address: 68000 address of modified data381* size: Size of modified data in bytes382* [Return value]383* None384*/385static FASTCALL void m68kq68_write_notify(u32 address, u32 size)386{387q68_touch_memory(state, address, size);388}389390/*************************************************************************/391392/**393* m68kq68_set_fetch: Set the instruction fetch pointer for a region of394* memory. Not used by Q68.395*396* [Parameters]397* low_addr: Low address of memory region to set398* high_addr: High address of memory region to set399* fetch_addr: Pointer to corresponding memory region (NULL to disable)400* [Return value]401* None402*/403static void m68kq68_set_fetch(u32 low_addr, u32 high_addr, pointer fetch_addr)404{405}406407/*-----------------------------------------------------------------------*/408409/**410* m68kq68_set_{readb,readw,writeb,writew}: Set functions for reading or411* writing bytes or words in memory.412*413* [Parameters]414* func: Function to set415* [Return value]416* None417*/418419static void m68kq68_set_readb(M68K_READ *func)420{421#ifdef NEED_TRAMPOLINE422real_readb = func;423q68_set_readb_func(state, readb_trampoline);424#else425q68_set_readb_func(state, (Q68ReadFunc *)func);426#endif427}428429static void m68kq68_set_readw(M68K_READ *func)430{431#ifdef NEED_TRAMPOLINE432real_readw = func;433q68_set_readw_func(state, readw_trampoline);434#else435q68_set_readw_func(state, (Q68ReadFunc *)func);436#endif437}438439static void m68kq68_set_writeb(M68K_WRITE *func)440{441#ifdef NEED_TRAMPOLINE442real_writeb = func;443q68_set_writeb_func(state, writeb_trampoline);444#else445q68_set_writeb_func(state, (Q68WriteFunc *)func);446#endif447}448449static void m68kq68_set_writew(M68K_WRITE *func)450{451#ifdef NEED_TRAMPOLINE452real_writew = func;453q68_set_writew_func(state, writew_trampoline);454#else455q68_set_writew_func(state, (Q68WriteFunc *)func);456#endif457}458459/*************************************************************************/460461/**462* dummy_read: Default read function, always returning 0 for any address.463*464* [Parameters]465* address: Address to read from466* [Return value]467* Value read (always zero)468*/469static uint32_t dummy_read(uint32_t address)470{471return 0;472}473474/*-----------------------------------------------------------------------*/475476/**477* dummy_write: Default write function, ignoring all writes.478*479* [Parameters]480* address: Address to write to481* data: Value to write482* [Return value]483* None484*/485static void dummy_write(uint32_t address, uint32_t data)486{487}488489/*-----------------------------------------------------------------------*/490491#ifdef NEED_TRAMPOLINE492493/**494* read[bw]_trampoline, write[bw]_trampoline: Adjust calling conventions495* between the M68k emulator and Yabause's M68k core.496*497* [Parameters]498* address: Address to read from499* data: Value to write (only for write_trampoline)500* [Return value]501* Value read (only for read_trampoline)502*/503504static uint32_t readb_trampoline(uint32_t address) {505return (*real_readb)(address);506}507508static uint32_t readw_trampoline(uint32_t address) {509return (*real_readw)(address);510}511512static void writeb_trampoline(uint32_t address, uint32_t data) {513return (*real_writeb)(address, data);514}515516static void writew_trampoline(uint32_t address, uint32_t data) {517return (*real_writew)(address, data);518}519520#endif // NEED_TRAMPOLINE521522/*************************************************************************/523/*************************************************************************/524525/*526* Local variables:527* c-file-style: "stroustrup"528* c-file-offsets: ((case-label . *) (statement-case-intro . *))529* indent-tabs-mode: nil530* End:531*532* vim: expandtab shiftwidth=4:533*/534535536