kopia lustrzana https://github.com/Jean-MarcHarvengt/MCUME
357 wiersze
8.5 KiB
C
357 wiersze
8.5 KiB
C
|
/*****************************************************************************
|
||
|
|
||
|
h6280.c - Portable HuC6280 emulator
|
||
|
|
||
|
Copyright (c) 1999, 2000 Bryan McPhail, mish@tendril.co.uk
|
||
|
|
||
|
This source code is based (with permission!) on the 6502 emulator by
|
||
|
Juergen Buchmueller. It is released as part of the Mame emulator project.
|
||
|
Let me know if you intend to use this code in any other project.
|
||
|
|
||
|
|
||
|
NOTICE:
|
||
|
|
||
|
This code is around 99% complete! Several things are unimplemented,
|
||
|
some due to lack of time, some due to lack of documentation, mainly
|
||
|
due to lack of programs using these features.
|
||
|
|
||
|
csh, csl opcodes are not supported.
|
||
|
set opcode and T flag behaviour are not supported.
|
||
|
|
||
|
I am unsure if instructions like SBC take an extra cycle when used in
|
||
|
decimal mode. I am unsure if flag B is set upon execution of rti.
|
||
|
|
||
|
Cycle counts should be quite accurate, illegal instructions are assumed
|
||
|
to take two cycles.
|
||
|
|
||
|
|
||
|
Changelog, version 1.02:
|
||
|
JMP + indirect X (0x7c) opcode fixed.
|
||
|
SMB + RMB opcodes fixed in disassembler.
|
||
|
change_pc function calls removed.
|
||
|
TSB & TRB now set flags properly.
|
||
|
BIT opcode altered.
|
||
|
|
||
|
Changelog, version 1.03:
|
||
|
Swapped IRQ mask for IRQ1 & IRQ2 (thanks Yasuhiro)
|
||
|
|
||
|
Changelog, version 1.04, 28/9/99-22/10/99:
|
||
|
Adjusted RTI (thanks Karl)
|
||
|
TST opcodes fixed in disassembler (missing break statements in a case!).
|
||
|
TST behaviour fixed.
|
||
|
SMB/RMB/BBS/BBR fixed in disassembler.
|
||
|
|
||
|
Changelog, version 1.05, 8/12/99-16/12/99:
|
||
|
Added CAB's timer implementation (note: irq ack & timer reload are changed).
|
||
|
Fixed STA IDX.
|
||
|
Fixed B flag setting on BRK.
|
||
|
Assumed CSH & CSL to take 2 cycles each.
|
||
|
|
||
|
Todo: Performance could be improved by precalculating timer fire position.
|
||
|
|
||
|
Changelog, version 1.06, 4/5/00 - last opcode bug found?
|
||
|
JMP indirect was doing a EAL++; instead of EAD++; - Obviously causing
|
||
|
a corrupt read when L = 0xff! This fixes Bloody Wolf and Trio The Punch!
|
||
|
|
||
|
Changelog, version 1.07, 3/9/00:
|
||
|
Changed timer to be single shot - fixes Crude Buster music in level 1.
|
||
|
|
||
|
Changelog, version 1.08, 1/18/01: (Charles MacDonald)
|
||
|
Added h6280_speed to reflect current CPU speed set by CSL/CSH.
|
||
|
Added implementation of SET opcode.
|
||
|
Changed INLINE in tblh6280.h to 'static __inline__' (Allegro conflict)
|
||
|
Added FAST_MEM routines for quicker ROM/RAM access.
|
||
|
|
||
|
******************************************************************************/
|
||
|
#include "shared.h"
|
||
|
|
||
|
#include "cpuintrf.h"
|
||
|
#include "h6280.h"
|
||
|
|
||
|
/* Default state of HuC6280 clock (1=7.16MHz, 0=3.58MHz) */
|
||
|
int h6280_speed = 1;
|
||
|
int h6280_ICount = 0;
|
||
|
static h6280_Regs h6280;
|
||
|
|
||
|
#include "h6280ops.h"
|
||
|
#include "tblh6280.h"
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
void h6280_reset(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* wipe out the h6280 structure */
|
||
|
memset(&h6280, 0, sizeof(h6280_Regs));
|
||
|
|
||
|
/* set I and Z flags */
|
||
|
P = _fI | _fZ;
|
||
|
|
||
|
/* stack starts at 0x01ff */
|
||
|
h6280.sp.d = 0x1ff;
|
||
|
|
||
|
/* read the reset vector into PC */
|
||
|
PCL = RDMEM(H6280_RESET_VEC);
|
||
|
PCH = RDMEM((H6280_RESET_VEC+1));
|
||
|
|
||
|
/* timer off by default */
|
||
|
h6280.timer_status=0;
|
||
|
h6280.timer_ack=1;
|
||
|
|
||
|
/* clear pending interrupts */
|
||
|
for (i = 0; i < 3; i++)
|
||
|
h6280.irq_state[i] = CLEAR_LINE;
|
||
|
|
||
|
h6280_speed = 1; /* default = 7.16MHz (?) */
|
||
|
}
|
||
|
|
||
|
void h6280_exit(void)
|
||
|
{
|
||
|
/* nothing */
|
||
|
}
|
||
|
|
||
|
int h6280_execute(int cycles)
|
||
|
{
|
||
|
int in,lastcycle,deltacycle;
|
||
|
h6280_ICount = cycles;
|
||
|
|
||
|
/* Subtract cycles used for taking an interrupt */
|
||
|
h6280_ICount -= h6280.extra_cycles;
|
||
|
h6280.extra_cycles = 0;
|
||
|
lastcycle = h6280_ICount;
|
||
|
|
||
|
// DEBUGLOG("h6280_execute 1\n");
|
||
|
|
||
|
/* Execute instructions */
|
||
|
do
|
||
|
{
|
||
|
h6280.ppc = h6280.pc;
|
||
|
// DEBUGLOG("h6280_execute 2 PC[%08X]\n",PCW );
|
||
|
|
||
|
/* Execute 1 instruction */
|
||
|
in=RDOP();
|
||
|
PCW++;
|
||
|
// DEBUGLOG("h6280_execute 2.5 in=[%d]\n",in);
|
||
|
insnh6280[in]();
|
||
|
|
||
|
// DEBUGLOG("h6280_execute 3\n");
|
||
|
|
||
|
/* Check internal timer */
|
||
|
if(h6280.timer_status)
|
||
|
{
|
||
|
deltacycle = lastcycle - h6280_ICount;
|
||
|
h6280.timer_value -= deltacycle;
|
||
|
if(h6280.timer_value<=0 && h6280.timer_ack==1)
|
||
|
{
|
||
|
h6280.timer_ack=h6280.timer_status=0;
|
||
|
h6280_set_irq_line(2,ASSERT_LINE);
|
||
|
}
|
||
|
}
|
||
|
lastcycle = h6280_ICount;
|
||
|
// DEBUGLOG("h6280_execute 4\n");
|
||
|
|
||
|
/* If PC has not changed we are stuck in a tight loop, may as well finish */
|
||
|
if( h6280.pc.d == h6280.ppc.d )
|
||
|
{
|
||
|
if (h6280_ICount > 0) h6280_ICount=0;
|
||
|
h6280.extra_cycles = 0;
|
||
|
return cycles;
|
||
|
}
|
||
|
// DEBUGLOG("h6280_execute 5\n");
|
||
|
|
||
|
} while (h6280_ICount > 0);
|
||
|
|
||
|
/* Subtract cycles used for taking an interrupt */
|
||
|
h6280_ICount -= h6280.extra_cycles;
|
||
|
h6280.extra_cycles = 0;
|
||
|
|
||
|
return cycles - h6280_ICount;
|
||
|
}
|
||
|
|
||
|
unsigned h6280_get_context (void *dst)
|
||
|
{
|
||
|
if( dst )
|
||
|
*(h6280_Regs*)dst = h6280;
|
||
|
return sizeof(h6280_Regs);
|
||
|
}
|
||
|
|
||
|
void h6280_set_context (void *src)
|
||
|
{
|
||
|
if( src )
|
||
|
h6280 = *(h6280_Regs*)src;
|
||
|
}
|
||
|
|
||
|
unsigned h6280_get_pc (void)
|
||
|
{
|
||
|
return PCD;
|
||
|
}
|
||
|
|
||
|
void h6280_set_pc (unsigned val)
|
||
|
{
|
||
|
PCW = val;
|
||
|
}
|
||
|
|
||
|
unsigned h6280_get_sp (void)
|
||
|
{
|
||
|
return S;
|
||
|
}
|
||
|
|
||
|
void h6280_set_sp (unsigned val)
|
||
|
{
|
||
|
S = val;
|
||
|
}
|
||
|
|
||
|
unsigned h6280_get_reg (int regnum)
|
||
|
{
|
||
|
switch( regnum )
|
||
|
{
|
||
|
case H6280_PC: return PCD;
|
||
|
case H6280_S: return S;
|
||
|
case H6280_P: return P;
|
||
|
case H6280_A: return A;
|
||
|
case H6280_X: return X;
|
||
|
case H6280_Y: return Y;
|
||
|
case H6280_IRQ_MASK: return h6280.irq_mask;
|
||
|
case H6280_TIMER_STATE: return h6280.timer_status;
|
||
|
case H6280_NMI_STATE: return h6280.nmi_state;
|
||
|
case H6280_IRQ1_STATE: return h6280.irq_state[0];
|
||
|
case H6280_IRQ2_STATE: return h6280.irq_state[1];
|
||
|
case H6280_IRQT_STATE: return h6280.irq_state[2];
|
||
|
case REG_PREVIOUSPC: return h6280.ppc.d;
|
||
|
default:
|
||
|
if( regnum <= REG_SP_CONTENTS )
|
||
|
{
|
||
|
unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
|
||
|
if( offset < 0x1ff )
|
||
|
return RDMEM( offset ) | ( RDMEM( offset+1 ) << 8 );
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void h6280_set_reg (int regnum, unsigned val)
|
||
|
{
|
||
|
switch( regnum )
|
||
|
{
|
||
|
case H6280_PC: PCW = val; break;
|
||
|
case H6280_S: S = val; break;
|
||
|
case H6280_P: P = val; break;
|
||
|
case H6280_A: A = val; break;
|
||
|
case H6280_X: X = val; break;
|
||
|
case H6280_Y: Y = val; break;
|
||
|
case H6280_IRQ_MASK: h6280.irq_mask = val; CHECK_IRQ_LINES; break;
|
||
|
case H6280_TIMER_STATE: h6280.timer_status = val; break;
|
||
|
case H6280_NMI_STATE: h6280_set_nmi_line( val ); break;
|
||
|
case H6280_IRQ1_STATE: h6280_set_irq_line( 0, val ); break;
|
||
|
case H6280_IRQ2_STATE: h6280_set_irq_line( 1, val ); break;
|
||
|
case H6280_IRQT_STATE: h6280_set_irq_line( 2, val ); break;
|
||
|
default:
|
||
|
if( regnum <= REG_SP_CONTENTS )
|
||
|
{
|
||
|
unsigned offset = S + 2 * (REG_SP_CONTENTS - regnum);
|
||
|
if( offset < 0x1ff )
|
||
|
{
|
||
|
WRMEM( offset, val & 0xff );
|
||
|
WRMEM( offset+1, (val >> 8) & 0xff );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|
||
|
|
||
|
void h6280_set_nmi_line(int state)
|
||
|
{
|
||
|
if (h6280.nmi_state == state) return;
|
||
|
h6280.nmi_state = state;
|
||
|
if (state != CLEAR_LINE)
|
||
|
{
|
||
|
DO_INTERRUPT(H6280_NMI_VEC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void h6280_set_irq_line(int irqline, int state)
|
||
|
{
|
||
|
h6280.irq_state[irqline] = state;
|
||
|
|
||
|
/* If line is cleared, just exit */
|
||
|
if (state == CLEAR_LINE) return;
|
||
|
|
||
|
/* Check if interrupts are enabled and the IRQ mask is clear */
|
||
|
CHECK_IRQ_LINES;
|
||
|
}
|
||
|
|
||
|
void h6280_set_irq_callback(int (*callback)(int irqline))
|
||
|
{
|
||
|
h6280.irq_callback = callback;
|
||
|
}
|
||
|
|
||
|
int H6280_irq_status_r (int offset)
|
||
|
{
|
||
|
int status;
|
||
|
|
||
|
switch (offset)
|
||
|
{
|
||
|
case 0: /* Read irq mask */
|
||
|
return h6280.irq_mask;
|
||
|
|
||
|
case 1: /* Read irq status */
|
||
|
status=0;
|
||
|
if(h6280.irq_state[1]!=CLEAR_LINE) status|=1; /* IRQ 2 */
|
||
|
if(h6280.irq_state[0]!=CLEAR_LINE) status|=2; /* IRQ 1 */
|
||
|
if(h6280.irq_state[2]!=CLEAR_LINE) status|=4; /* TIMER */
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void H6280_irq_status_w (int offset, int data)
|
||
|
{
|
||
|
switch (offset)
|
||
|
{
|
||
|
case 0: /* Write irq mask */
|
||
|
h6280.irq_mask=data&0x7;
|
||
|
CHECK_IRQ_LINES;
|
||
|
break;
|
||
|
|
||
|
case 1: /* Timer irq ack - timer is reloaded here */
|
||
|
h6280.timer_value = h6280.timer_load;
|
||
|
h6280.timer_ack=1; /* Timer can't refire until ack'd */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int H6280_timer_r (int offset)
|
||
|
{
|
||
|
switch (offset) {
|
||
|
case 0: /* Counter value */
|
||
|
return (h6280.timer_value/1024)&127;
|
||
|
|
||
|
case 1: /* Read counter status */
|
||
|
return h6280.timer_status;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void H6280_timer_w (int offset, int data)
|
||
|
{
|
||
|
switch (offset) {
|
||
|
case 0: /* Counter preload */
|
||
|
h6280.timer_load=h6280.timer_value=((data&127)+1)*1024;
|
||
|
return;
|
||
|
|
||
|
case 1: /* Counter enable */
|
||
|
if(data&1)
|
||
|
{ /* stop -> start causes reload */
|
||
|
if(h6280.timer_status==0) h6280.timer_value=h6280.timer_load;
|
||
|
}
|
||
|
h6280.timer_status=data&1;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************/
|