📄 z80fmly.c
字号:
/***************************************************************************
Z80 FMLY.C Z80 FAMILY CHIP EMURATOR for MAME Ver.0.1 alpha
Support chip : Z80PIO , Z80CTC
Copyright(C) 1997 Tatsuyuki Satoh.
This version are tested starforce driver.
8/21/97 -- Heavily modified by Aaron Giles to be much more accurate for the MCR games
8/27/97 -- Rewritten a second time by Aaron Giles, with the datasheets in hand
pending:
Z80CTC , Counter mode & Timer with Trigrt start :not support Triger level
***************************************************************************/
#include <stdio.h>
#include "driver.h"
#include "z80fmly.h"
#include "timer.h"
typedef struct
{
int vector; /* interrupt vector */
int clock; /* system clock */
double invclock16; /* 16/system clock */
double invclock256; /* 256/system clock */
void (*intr)(int which); /* interrupt callback */
void (*zc[4])(int offset, int data); /* zero crossing callbacks */
int notimer; /* no timer masks */
int mask[4]; /* masked channel flags */
int mode[4]; /* current mode */
int tconst[4]; /* time constant */
int down[4]; /* down counter (clock mode only) */
int extclk[4]; /* current signal from the external clock */
void *timer[4]; /* array of active timers */
int int_state[4]; /* interrupt status (for daisy chain) */
} z80ctc;
static z80ctc ctcs[MAX_CTC];
/* these are the bits of the incoming commands to the CTC */
#define INTERRUPT 0x80
#define INTERRUPT_ON 0x80
#define INTERRUPT_OFF 0x00
#define MODE 0x40
#define MODE_TIMER 0x00
#define MODE_COUNTER 0x40
#define PRESCALER 0x20
#define PRESCALER_256 0x20
#define PRESCALER_16 0x00
#define EDGE 0x10
#define EDGE_FALLING 0x00
#define EDGE_RISING 0x10
#define TRIGGER 0x08
#define TRIGGER_AUTO 0x00
#define TRIGGER_CLOCK 0x08
#define CONSTANT 0x04
#define CONSTANT_LOAD 0x04
#define CONSTANT_NONE 0x00
#define RESET 0x02
#define RESET_CONTINUE 0x00
#define RESET_ACTIVE 0x02
#define CONTROL 0x01
#define CONTROL_VECTOR 0x00
#define CONTROL_WORD 0x01
/* these extra bits help us keep things accurate */
#define WAITING_FOR_TRIG 0x100
static void z80ctc_timercallback (int param);
void z80ctc_init (z80ctc_interface *intf)
{
int i;
memset (ctcs, 0, sizeof (ctcs));
for (i = 0; i < intf->num; i++)
{
ctcs[i].clock = intf->baseclock[i];
ctcs[i].invclock16 = 16.0 / (double)intf->baseclock[i];
ctcs[i].invclock256 = 256.0 / (double)intf->baseclock[i];
ctcs[i].notimer = intf->notimer[i];
ctcs[i].intr = intf->intr[i];
ctcs[i].zc[0] = intf->zc0[i];
ctcs[i].zc[1] = intf->zc1[i];
ctcs[i].zc[2] = intf->zc2[i];
ctcs[i].zc[3] = 0;
z80ctc_reset (i);
}
}
double z80ctc_getperiod (int which, int ch)
{
z80ctc *ctc = ctcs + which;
double clock;
int mode;
/* keep channel within range, and get the current mode */
ch &= 3;
mode = ctc->mode[ch];
/* if reset active */
if( (mode & RESET) == RESET_ACTIVE) return 0;
/* if counter mode */
if( (mode & MODE) == MODE_COUNTER)
{
return 0;
}
/* compute the period */
clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
return clock * (double)ctc->tconst[ch];
}
/* interrupt request callback with daisy-chain circuit */
static void z80ctc_interrupt_check( z80ctc *ctc )
{
int state = 0;
int ch;
for( ch = 3 ; ch >= 0 ; ch-- )
{
/* if IEO disable , same and lower IRQ is masking */
/* ASG: changed this line because this state could have an interrupt pending as well! */
/* if( ctc->int_state[ch] & Z80_INT_IEO ) state = Z80_INT_IEO;*/
if( ctc->int_state[ch] & Z80_INT_IEO ) state = ctc->int_state[ch];
else state |= ctc->int_state[ch];
}
/* change interrupt status */
if (ctc->intr) (*ctc->intr)(state);
}
void z80ctc_reset (int which)
{
z80ctc *ctc = ctcs + which;
int i;
/* set up defaults */
for (i = 0; i < 4; i++)
{
ctc->mode[i] = RESET_ACTIVE;
ctc->tconst[i] = 0x100;
ctc->timer[i] = NULL;
ctc->int_state[i] = 0;
}
z80ctc_interrupt_check( ctc );
}
void z80ctc_0_reset (void) { z80ctc_reset (0); }
void z80ctc_1_reset (void) { z80ctc_reset (1); }
void z80ctc_w (int which, int offset, int data)
{
z80ctc *ctc = ctcs + which;
int mode, ch;
/* keep channel within range, and get the current mode */
ch = offset & 3;
mode = ctc->mode[ch];
/* if we're waiting for a time constant, this is it */
if ((mode & CONSTANT) == CONSTANT_LOAD)
{
/* set the time constant (0 -> 0x100) */
ctc->tconst[ch] = data ? data : 0x100;
/* clear the internal mode -- we're no longer waiting */
ctc->mode[ch] &= ~CONSTANT;
/* also clear the reset, since the constant gets it going again */
ctc->mode[ch] &= ~RESET;
/* if we're in timer mode.... */
if ((mode & MODE) == MODE_TIMER)
{
/* if we're triggering on the time constant, reset the down counter now */
if ((mode & TRIGGER) == TRIGGER_AUTO)
{
double clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
if (ctc->timer[ch])
timer_remove (ctc->timer[ch]);
if (!(ctc->notimer & (1<<ch)))
ctc->timer[ch] = timer_pulse (clock * (double)ctc->tconst[ch], (which << 2) + ch, z80ctc_timercallback);
}
/* else set the bit indicating that we're waiting for the appropriate trigger */
else
ctc->mode[ch] |= WAITING_FOR_TRIG;
}
/* also set the down counter in case we're clocking externally */
ctc->down[ch] = ctc->tconst[ch];
/* all done here */
return;
}
/* if we're writing the interrupt vector, handle it specially */
#if 0 /* Tatsuyuki Satoh changes */
/* The 'Z80family handbook' wrote, */
/* interrupt vector is able to set for even channel (0 or 2) */
if ((data & CONTROL) == CONTROL_VECTOR && (ch&1) == 0)
#else
if ((data & CONTROL) == CONTROL_VECTOR && ch == 0)
#endif
{
ctc->vector = data & 0xf8;
return;
}
/* this must be a control word */
if ((data & CONTROL) == CONTROL_WORD)
{
/* set the new mode */
ctc->mode[ch] = data;
/* if we're being reset, clear out any pending timers for this channel */
if ((data & RESET) == RESET_ACTIVE)
{
if (ctc->timer[ch])
timer_remove (ctc->timer[ch]);
ctc->timer[ch] = NULL;
if( ctc->int_state[ch] != 0 )
{
/* clear interrupt service , request */
ctc->int_state[ch] = 0;
z80ctc_interrupt_check( ctc );
}
}
/* all done here */
return;
}
}
void z80ctc_0_w (int offset, int data) { z80ctc_w (0, offset, data); }
void z80ctc_1_w (int offset, int data) { z80ctc_w (1, offset, data); }
int z80ctc_r (int which, int ch)
{
z80ctc *ctc = ctcs + which;
int mode;
/* keep channel within range */
ch &= 3;
mode = ctc->mode[ch];
/* if we're in counter mode, just return the count */
if ((mode & MODE) == MODE_COUNTER)
return ctc->down[ch];
/* else compute the down counter value */
else
{
double clock = ((mode & PRESCALER) == PRESCALER_16) ? ctc->invclock16 : ctc->invclock256;
if (ctc->timer[ch])
return ((int)(timer_timeleft (ctc->timer[ch]) / clock) + 1) & 0xff;
else
return 0;
}
}
int z80ctc_0_r (int offset) { return z80ctc_r (0, offset); }
int z80ctc_1_r (int offset) { return z80ctc_r (1, offset); }
int z80ctc_interrupt( int which )
{
z80ctc *ctc = ctcs + which;
int ch;
for( ch = 0 ; ch < 4 ; ch++ )
{
if( ctc->int_state[ch] )
{
if( ctc->int_state[ch] == Z80_INT_REQ)
ctc->int_state[ch] = Z80_INT_IEO;
break;
}
}
if( ch > 3 )
{
ch = 0;
}
z80ctc_interrupt_check( ctc );
return ctc->vector + ch * 2;
}
/* when operate RETI , soud be call this function for request pending interrupt */
void z80ctc_reti( int which )
{
z80ctc *ctc = ctcs + which;
int ch;
for( ch = 0 ; ch < 4 ; ch++ )
{
if( ctc->int_state[ch] & Z80_INT_IEO )
{
/* highest served interrupt found */
/* clear interrupt status */
ctc->int_state[ch] &= ~Z80_INT_IEO;
/* search next interrupt */
break;
}
}
/* set next interrupt stattus */
z80ctc_interrupt_check( ctc );
}
static void z80ctc_timercallback (int param)
{
int which = param >> 2;
int ch = param & 3;
z80ctc *ctc = ctcs + which;
/* down counter has reached zero - see if we should interrupt */
if ((ctc->mode[ch] & INTERRUPT) == INTERRUPT_ON)
{
if( !(ctc->int_state[ch] & Z80_INT_REQ) )
{
ctc->int_state[ch] |= Z80_INT_REQ;
z80ctc_interrupt_check( ctc );
}
}
/* generate the clock pulse */
if (ctc->zc[ch])
{
(*ctc->zc[ch])(0,1);
(*ctc->zc[ch])(0,0);
}
/* reset the down counter */
ctc->down[ch] = ctc->tconst[ch];
}
void z80ctc_trg_w (int which, int trg, int offset, int data)
{
z80ctc *ctc = ctcs + which;
int ch = trg & 3;
int mode;
data = data ? 1 : 0;
mode = ctc->mode[ch];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -