535 lines
14 KiB
C
535 lines
14 KiB
C
/* 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:
|
|
*/
|