1499 lines
49 KiB
C
1499 lines
49 KiB
C
/*****************************************************************************
|
|
*
|
|
* POKEY chip emulator 4.51
|
|
* Copyright Nicola Salmoria and the MAME Team
|
|
*
|
|
* Based on original info found in Ron Fries' Pokey emulator,
|
|
* with additions by Brad Oliver, Eric Smith and Juergen Buchmueller,
|
|
* paddle (a/d conversion) details from the Atari 400/800 Hardware Manual.
|
|
* Polynome algorithms according to info supplied by Perry McFarlane.
|
|
*
|
|
* This code is subject to the MAME license, which besides other
|
|
* things means it is distributed as is, no warranties whatsoever.
|
|
* For more details read mame.txt that comes with MAME.
|
|
*
|
|
* 4.51:
|
|
* - changed to use the attotime datatype
|
|
* 4.5:
|
|
* - changed the 9/17 bit polynomial formulas such that the values
|
|
* required for the Tempest Pokey protection will be found.
|
|
* Tempest expects the upper 4 bits of the RNG to appear in the
|
|
* lower 4 bits after four cycles, so there has to be a shift
|
|
* of 1 per cycle (which was not the case before). Bits #6-#13 of the
|
|
* new RNG give this expected result now, bits #0-7 of the 9 bit poly.
|
|
* - reading the RNG returns the shift register contents ^ 0xff.
|
|
* That way resetting the Pokey with SKCTL (which resets the
|
|
* polynome shifters to 0) returns the expected 0xff value.
|
|
* 4.4:
|
|
* - reversed sample values to make OFF channels produce a zero signal.
|
|
* actually de-reversed them; don't remember that I reversed them ;-/
|
|
* 4.3:
|
|
* - for POT inputs returning zero, immediately assert the ALLPOT
|
|
* bit after POTGO is written, otherwise start trigger timer
|
|
* depending on SK_PADDLE mode, either 1-228 scanlines or 1-2
|
|
* scanlines, depending on the SK_PADDLE bit of SKCTL.
|
|
* 4.2:
|
|
* - half volume for channels which are inaudible (this should be
|
|
* close to the real thing).
|
|
* 4.1:
|
|
* - default gain increased to closely match the old code.
|
|
* - random numbers repeat rate depends on POLY9 flag too!
|
|
* - verified sound output with many, many Atari 800 games,
|
|
* including the SUPPRESS_INAUDIBLE optimizations.
|
|
* 4.0:
|
|
* - rewritten from scratch.
|
|
* - 16bit stream interface.
|
|
* - serout ready/complete delayed interrupts.
|
|
* - reworked pot analog/digital conversion timing.
|
|
* - optional non-indexing pokey update functions.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "mamedef.h"
|
|
//#include "emu.h"
|
|
#include <stdlib.h>
|
|
#ifdef _DEBUG
|
|
#include <stdio.h>
|
|
#endif
|
|
#include "pokey.h"
|
|
|
|
/*
|
|
* Defining this produces much more (about twice as much)
|
|
* but also more efficient code. Ideally this should be set
|
|
* for processors with big code cache and for healthy compilers :)
|
|
*/
|
|
#ifndef BIG_SWITCH
|
|
#ifndef HEAVY_MACRO_USAGE
|
|
#define HEAVY_MACRO_USAGE 1
|
|
#endif
|
|
#else
|
|
#define HEAVY_MACRO_USAGE BIG_SWITCH
|
|
#endif
|
|
|
|
#define SUPPRESS_INAUDIBLE 1
|
|
|
|
/* Four channels with a range of 0..32767 and volume 0..15 */
|
|
//#define POKEY_DEFAULT_GAIN (32767/15/4)
|
|
|
|
/*
|
|
* But we raise the gain and risk clipping, the old Pokey did
|
|
* this too. It defined POKEY_DEFAULT_GAIN 6 and this was
|
|
* 6 * 15 * 4 = 360, 360/256 = 1.40625
|
|
* I use 15/11 = 1.3636, so this is a little lower.
|
|
*/
|
|
#define POKEY_DEFAULT_GAIN (32767/11/4)
|
|
|
|
#define VERBOSE 0
|
|
#define VERBOSE_SOUND 0
|
|
#define VERBOSE_TIMER 0
|
|
#define VERBOSE_POLY 0
|
|
#define VERBOSE_RAND 0
|
|
|
|
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
|
|
|
#define LOG_SOUND(x) do { if (VERBOSE_SOUND) logerror x; } while (0)
|
|
|
|
#define LOG_TIMER(x) do { if (VERBOSE_TIMER) logerror x; } while (0)
|
|
|
|
#define LOG_POLY(x) do { if (VERBOSE_POLY) logerror x; } while (0)
|
|
|
|
#define LOG_RAND(x) do { if (VERBOSE_RAND) logerror x; } while (0)
|
|
|
|
#define CHAN1 0
|
|
#define CHAN2 1
|
|
#define CHAN3 2
|
|
#define CHAN4 3
|
|
|
|
#define TIMER1 0
|
|
#define TIMER2 1
|
|
#define TIMER4 2
|
|
|
|
/* values to add to the divisors for the different modes */
|
|
#define DIVADD_LOCLK 1
|
|
#define DIVADD_HICLK 4
|
|
#define DIVADD_HICLK_JOINED 7
|
|
|
|
/* AUDCx */
|
|
#define NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */
|
|
#define POLY4 0x40 /* selects POLY4 or POLY17 */
|
|
#define PURE 0x20 /* selects POLY4/17 or PURE tone */
|
|
#define VOLUME_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */
|
|
#define VOLUME_MASK 0x0f /* volume mask */
|
|
|
|
/* AUDCTL */
|
|
#define POLY9 0x80 /* selects POLY9 or POLY17 */
|
|
#define CH1_HICLK 0x40 /* selects 1.78979 MHz for Ch 1 */
|
|
#define CH3_HICLK 0x20 /* selects 1.78979 MHz for Ch 3 */
|
|
#define CH12_JOINED 0x10 /* clocks channel 1 w/channel 2 */
|
|
#define CH34_JOINED 0x08 /* clocks channel 3 w/channel 4 */
|
|
#define CH1_FILTER 0x04 /* selects channel 1 high pass filter */
|
|
#define CH2_FILTER 0x02 /* selects channel 2 high pass filter */
|
|
#define CLK_15KHZ 0x01 /* selects 15.6999 kHz or 63.9211 kHz */
|
|
|
|
/* IRQEN (D20E) */
|
|
#define IRQ_BREAK 0x80 /* BREAK key pressed interrupt */
|
|
#define IRQ_KEYBD 0x40 /* keyboard data ready interrupt */
|
|
#define IRQ_SERIN 0x20 /* serial input data ready interrupt */
|
|
#define IRQ_SEROR 0x10 /* serial output register ready interrupt */
|
|
#define IRQ_SEROC 0x08 /* serial output complete interrupt */
|
|
#define IRQ_TIMR4 0x04 /* timer channel #4 interrupt */
|
|
#define IRQ_TIMR2 0x02 /* timer channel #2 interrupt */
|
|
#define IRQ_TIMR1 0x01 /* timer channel #1 interrupt */
|
|
|
|
/* SKSTAT (R/D20F) */
|
|
#define SK_FRAME 0x80 /* serial framing error */
|
|
#define SK_OVERRUN 0x40 /* serial overrun error */
|
|
#define SK_KBERR 0x20 /* keyboard overrun error */
|
|
#define SK_SERIN 0x10 /* serial input high */
|
|
#define SK_SHIFT 0x08 /* shift key pressed */
|
|
#define SK_KEYBD 0x04 /* keyboard key pressed */
|
|
#define SK_SEROUT 0x02 /* serial output active */
|
|
|
|
/* SKCTL (W/D20F) */
|
|
#define SK_BREAK 0x80 /* serial out break signal */
|
|
#define SK_BPS 0x70 /* bits per second */
|
|
#define SK_FM 0x08 /* FM mode */
|
|
#define SK_PADDLE 0x04 /* fast paddle a/d conversion */
|
|
#define SK_RESET 0x03 /* reset serial/keyboard interface */
|
|
|
|
#define DIV_64 28 /* divisor for 1.78979 MHz clock to 63.9211 kHz */
|
|
#define DIV_15 114 /* divisor for 1.78979 MHz clock to 15.6999 kHz */
|
|
|
|
typedef struct _pokey_state pokey_state;
|
|
struct _pokey_state
|
|
{
|
|
INT32 counter[4]; /* channel counter */
|
|
INT32 divisor[4]; /* channel divisor (modulo value) */
|
|
UINT32 volume[4]; /* channel volume - derived */
|
|
UINT8 output[4]; /* channel output signal (1 active, 0 inactive) */
|
|
UINT8 audible[4]; /* channel plays an audible tone/effect */
|
|
UINT8 Muted[4];
|
|
UINT32 samplerate_24_8; /* sample rate in 24.8 format */
|
|
UINT32 samplepos_fract; /* sample position fractional part */
|
|
UINT32 samplepos_whole; /* sample position whole part */
|
|
UINT32 polyadjust; /* polynome adjustment */
|
|
UINT32 p4; /* poly4 index */
|
|
UINT32 p5; /* poly5 index */
|
|
UINT32 p9; /* poly9 index */
|
|
UINT32 p17; /* poly17 index */
|
|
UINT32 r9; /* rand9 index */
|
|
UINT32 r17; /* rand17 index */
|
|
UINT32 clockmult; /* clock multiplier */
|
|
//device_t *device;
|
|
//sound_stream * channel; /* streams channel */
|
|
//emu_timer *timer[3]; /* timers for channel 1,2 and 4 events */
|
|
//attotime timer_period[3]; /* computed periods for these timers */
|
|
//int timer_param[3]; /* computed parameters for these timers */
|
|
//emu_timer *rtimer; /* timer for calculating the random offset */
|
|
//emu_timer *ptimer[8]; /* pot timers */
|
|
//devcb_resolved_read8 pot_r[8];
|
|
//devcb_resolved_read8 allpot_r;
|
|
//devcb_resolved_read8 serin_r;
|
|
//devcb_resolved_write8 serout_w;
|
|
//void (*interrupt_cb)(device_t *device, int mask);
|
|
UINT8 AUDF[4]; /* AUDFx (D200, D202, D204, D206) */
|
|
UINT8 AUDC[4]; /* AUDCx (D201, D203, D205, D207) */
|
|
UINT8 POTx[8]; /* POTx (R/D200-D207) */
|
|
UINT8 AUDCTL; /* AUDCTL (W/D208) */
|
|
UINT8 ALLPOT; /* ALLPOT (R/D208) */
|
|
UINT8 KBCODE; /* KBCODE (R/D209) */
|
|
UINT8 RANDOM; /* RANDOM (R/D20A) */
|
|
UINT8 SERIN; /* SERIN (R/D20D) */
|
|
UINT8 SEROUT; /* SEROUT (W/D20D) */
|
|
UINT8 IRQST; /* IRQST (R/D20E) */
|
|
UINT8 IRQEN; /* IRQEN (W/D20E) */
|
|
UINT8 SKSTAT; /* SKSTAT (R/D20F) */
|
|
UINT8 SKCTL; /* SKCTL (W/D20F) */
|
|
//pokey_interface intf;
|
|
//attotime clock_period;
|
|
double clock_period;
|
|
//attotime ad_time_fast;
|
|
//attotime ad_time_slow;
|
|
|
|
UINT8 poly4[0x0f];
|
|
UINT8 poly5[0x1f];
|
|
UINT8 poly9[0x1ff];
|
|
UINT8 poly17[0x1ffff];
|
|
|
|
UINT8 rand9[0x1ff];
|
|
UINT8 rand17[0x1ffff];
|
|
};
|
|
|
|
|
|
#define P4(chip) chip->poly4[chip->p4]
|
|
#define P5(chip) chip->poly5[chip->p5]
|
|
#define P9(chip) chip->poly9[chip->p9]
|
|
#define P17(chip) chip->poly17[chip->p17]
|
|
|
|
//static TIMER_CALLBACK( pokey_timer_expire );
|
|
//static TIMER_CALLBACK( pokey_pot_trigger );
|
|
|
|
|
|
#define SAMPLE -1
|
|
|
|
#define ADJUST_EVENT(chip) \
|
|
chip->counter[CHAN1] -= event; \
|
|
chip->counter[CHAN2] -= event; \
|
|
chip->counter[CHAN3] -= event; \
|
|
chip->counter[CHAN4] -= event; \
|
|
chip->samplepos_whole -= event; \
|
|
chip->polyadjust += event
|
|
|
|
#if SUPPRESS_INAUDIBLE
|
|
|
|
#define PROCESS_CHANNEL(chip,ch) \
|
|
int toggle = 0; \
|
|
ADJUST_EVENT(chip); \
|
|
/* reset the channel counter */ \
|
|
if( chip->audible[ch] ) \
|
|
chip->counter[ch] = chip->divisor[ch]; \
|
|
else \
|
|
chip->counter[ch] = 0x7fffffff; \
|
|
chip->p4 = (chip->p4+chip->polyadjust)%0x0000f; \
|
|
chip->p5 = (chip->p5+chip->polyadjust)%0x0001f; \
|
|
chip->p9 = (chip->p9+chip->polyadjust)%0x001ff; \
|
|
chip->p17 = (chip->p17+chip->polyadjust)%0x1ffff; \
|
|
chip->polyadjust = 0; \
|
|
if( (chip->AUDC[ch] & NOTPOLY5) || P5(chip) ) \
|
|
{ \
|
|
if( chip->AUDC[ch] & PURE ) \
|
|
toggle = 1; \
|
|
else \
|
|
if( chip->AUDC[ch] & POLY4 ) \
|
|
toggle = chip->output[ch] == !P4(chip); \
|
|
else \
|
|
if( chip->AUDCTL & POLY9 ) \
|
|
toggle = chip->output[ch] == !P9(chip); \
|
|
else \
|
|
toggle = chip->output[ch] == !P17(chip); \
|
|
} \
|
|
if( toggle ) \
|
|
{ \
|
|
if( chip->audible[ch] && ! chip->Muted[ch] ) \
|
|
{ \
|
|
if( chip->output[ch] ) \
|
|
sum -= chip->volume[ch]; \
|
|
else \
|
|
sum += chip->volume[ch]; \
|
|
} \
|
|
chip->output[ch] ^= 1; \
|
|
} \
|
|
/* is this a filtering channel (3/4) and is the filter active? */ \
|
|
if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) \
|
|
{ \
|
|
if( chip->output[ch-2] ) \
|
|
{ \
|
|
chip->output[ch-2] = 0; \
|
|
if( chip->audible[ch] && ! chip->Muted[ch] ) \
|
|
sum -= chip->volume[ch-2]; \
|
|
} \
|
|
} \
|
|
|
|
#else
|
|
|
|
#define PROCESS_CHANNEL(chip,ch) \
|
|
int toggle = 0; \
|
|
ADJUST_EVENT(chip); \
|
|
/* reset the channel counter */ \
|
|
chip->counter[ch] = p[chip].divisor[ch]; \
|
|
chip->p4 = (chip->p4+chip->polyadjust)%0x0000f; \
|
|
chip->p5 = (chip->p5+chip->polyadjust)%0x0001f; \
|
|
chip->p9 = (chip->p9+chip->polyadjust)%0x001ff; \
|
|
chip->p17 = (chip->p17+chip->polyadjust)%0x1ffff; \
|
|
chip->polyadjust = 0; \
|
|
if( (chip->AUDC[ch] & NOTPOLY5) || P5(chip) ) \
|
|
{ \
|
|
if( chip->AUDC[ch] & PURE ) \
|
|
toggle = 1; \
|
|
else \
|
|
if( chip->AUDC[ch] & POLY4 ) \
|
|
toggle = chip->output[ch] == !P4(chip); \
|
|
else \
|
|
if( chip->AUDCTL & POLY9 ) \
|
|
toggle = chip->output[ch] == !P9(chip); \
|
|
else \
|
|
toggle = chip->output[ch] == !P17(chip); \
|
|
} \
|
|
if( toggle && ! chip->Muted[ch] ) \
|
|
{ \
|
|
if( chip->output[ch] ) \
|
|
sum -= chip->volume[ch]; \
|
|
else \
|
|
sum += chip->volume[ch]; \
|
|
chip->output[ch] ^= 1; \
|
|
} \
|
|
/* is this a filtering channel (3/4) and is the filter active? */ \
|
|
if( chip->AUDCTL & ((CH1_FILTER|CH2_FILTER) & (0x10 >> ch)) ) \
|
|
{ \
|
|
if( chip->output[ch-2] && ! chip->Muted[ch] ) \
|
|
{ \
|
|
chip->output[ch-2] = 0; \
|
|
sum -= chip->volume[ch-2]; \
|
|
} \
|
|
} \
|
|
|
|
#endif
|
|
|
|
#define PROCESS_SAMPLE(chip) \
|
|
ADJUST_EVENT(chip); \
|
|
/* adjust the sample position */ \
|
|
chip->samplepos_whole++; \
|
|
/* store sum of output signals into the buffer */ \
|
|
/* *buffer++ = (sum > 0x7fff) ? 0x7fff : sum; */ \
|
|
*bufL++ = *bufR++ = sum; \
|
|
samples--
|
|
|
|
#if HEAVY_MACRO_USAGE
|
|
|
|
/*
|
|
* This version of PROCESS_POKEY repeats the search for the minimum
|
|
* event value without using an index to the channel. That way the
|
|
* PROCESS_CHANNEL macros can be called with fixed values and expand
|
|
* to much more efficient code
|
|
*/
|
|
|
|
#define PROCESS_POKEY(chip) \
|
|
UINT32 sum = 0; \
|
|
if( chip->output[CHAN1] && ! chip->Muted[CHAN1] ) \
|
|
sum += chip->volume[CHAN1]; \
|
|
if( chip->output[CHAN2] && ! chip->Muted[CHAN2] ) \
|
|
sum += chip->volume[CHAN2]; \
|
|
if( chip->output[CHAN3] && ! chip->Muted[CHAN3] ) \
|
|
sum += chip->volume[CHAN3]; \
|
|
if( chip->output[CHAN4] && ! chip->Muted[CHAN4] ) \
|
|
sum += chip->volume[CHAN4]; \
|
|
while( samples > 0 ) \
|
|
{ \
|
|
if( chip->counter[CHAN1] < chip->samplepos_whole ) \
|
|
{ \
|
|
if( chip->counter[CHAN2] < chip->counter[CHAN1] ) \
|
|
{ \
|
|
if( chip->counter[CHAN3] < chip->counter[CHAN2] ) \
|
|
{ \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN3]; \
|
|
PROCESS_CHANNEL(chip,CHAN3); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN2] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN2]; \
|
|
PROCESS_CHANNEL(chip,CHAN2); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN3] < chip->counter[CHAN1] ) \
|
|
{ \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN3]; \
|
|
PROCESS_CHANNEL(chip,CHAN3); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN1] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN1]; \
|
|
PROCESS_CHANNEL(chip,CHAN1); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN2] < chip->samplepos_whole ) \
|
|
{ \
|
|
if( chip->counter[CHAN3] < chip->counter[CHAN2] ) \
|
|
{ \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN3]; \
|
|
PROCESS_CHANNEL(chip,CHAN3); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN2] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN2]; \
|
|
PROCESS_CHANNEL(chip,CHAN2); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN3] < chip->samplepos_whole ) \
|
|
{ \
|
|
if( chip->counter[CHAN4] < chip->counter[CHAN3] ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN3]; \
|
|
PROCESS_CHANNEL(chip,CHAN3); \
|
|
} \
|
|
} \
|
|
else \
|
|
if( chip->counter[CHAN4] < chip->samplepos_whole ) \
|
|
{ \
|
|
UINT32 event = chip->counter[CHAN4]; \
|
|
PROCESS_CHANNEL(chip,CHAN4); \
|
|
} \
|
|
else \
|
|
{ \
|
|
UINT32 event = chip->samplepos_whole; \
|
|
PROCESS_SAMPLE(chip); \
|
|
} \
|
|
}/* \
|
|
chip->rtimer->adjust(attotime::never)*/
|
|
|
|
#else /* no HEAVY_MACRO_USAGE */
|
|
/*
|
|
* And this version of PROCESS_POKEY uses event and channel variables
|
|
* so that the PROCESS_CHANNEL macro needs to index memory at runtime.
|
|
*/
|
|
|
|
#define PROCESS_POKEY(chip) \
|
|
UINT32 sum = 0; \
|
|
if( chip->output[CHAN1] && ! chip->Muted[CHAN1] ) \
|
|
sum += chip->volume[CHAN1]; \
|
|
if( chip->output[CHAN2] && ! chip->Muted[CHAN2] ) \
|
|
sum += chip->volume[CHAN2]; \
|
|
if( chip->output[CHAN3] && ! chip->Muted[CHAN3] ) \
|
|
sum += chip->volume[CHAN3]; \
|
|
if( chip->output[CHAN4] && ! chip->Muted[CHAN4] ) \
|
|
sum += chip->volume[CHAN4]; \
|
|
while( samples > 0 ) \
|
|
{ \
|
|
UINT32 event = chip->samplepos_whole; \
|
|
UINT32 channel = SAMPLE; \
|
|
if( chip->counter[CHAN1] < event ) \
|
|
{ \
|
|
event = chip->counter[CHAN1]; \
|
|
channel = CHAN1; \
|
|
} \
|
|
if( chip->counter[CHAN2] < event ) \
|
|
{ \
|
|
event = chip->counter[CHAN2]; \
|
|
channel = CHAN2; \
|
|
} \
|
|
if( chip->counter[CHAN3] < event ) \
|
|
{ \
|
|
event = chip->counter[CHAN3]; \
|
|
channel = CHAN3; \
|
|
} \
|
|
if( chip->counter[CHAN4] < event ) \
|
|
{ \
|
|
event = chip->counter[CHAN4]; \
|
|
channel = CHAN4; \
|
|
} \
|
|
if( channel == SAMPLE ) \
|
|
{ \
|
|
PROCESS_SAMPLE(chip); \
|
|
} \
|
|
else \
|
|
{ \
|
|
PROCESS_CHANNEL(chip,channel); \
|
|
} \
|
|
}/* \
|
|
chip->rtimer->adjust(attotime::never)*/
|
|
|
|
#endif
|
|
|
|
|
|
/*INLINE pokey_state *get_safe_token(device_t *device)
|
|
{
|
|
assert(device != NULL);
|
|
assert(device->type() == POKEY);
|
|
return (pokey_state *)downcast<legacy_device_base *>(device)->token();
|
|
}*/
|
|
|
|
|
|
//static STREAM_UPDATE( pokey_update )
|
|
void pokey_update(void *param, stream_sample_t **outputs, int samples)
|
|
{
|
|
pokey_state *chip = (pokey_state *)param;
|
|
//stream_sample_t *buffer = outputs[0];
|
|
stream_sample_t *bufL = outputs[0];
|
|
stream_sample_t *bufR = outputs[1];
|
|
PROCESS_POKEY(chip);
|
|
}
|
|
|
|
|
|
static void poly_init(UINT8 *poly, int size, int left, int right, int add)
|
|
{
|
|
int mask = (1 << size) - 1;
|
|
int i, x = 0;
|
|
|
|
#ifdef _DEBUG
|
|
LOG_POLY(("poly %d\n", size));
|
|
#endif
|
|
for( i = 0; i < mask; i++ )
|
|
{
|
|
*poly++ = x & 1;
|
|
#ifdef _DEBUG
|
|
LOG_POLY(("%05x: %d\n", x, x&1));
|
|
#endif
|
|
/* calculate next bit */
|
|
x = ((x << left) + (x >> right) + add) & mask;
|
|
}
|
|
}
|
|
|
|
static void rand_init(UINT8 *rng, int size, int left, int right, int add)
|
|
{
|
|
int mask = (1 << size) - 1;
|
|
int i, x = 0;
|
|
|
|
#ifdef _DEBUG
|
|
LOG_RAND(("rand %d\n", size));
|
|
#endif
|
|
for( i = 0; i < mask; i++ )
|
|
{
|
|
if (size == 17)
|
|
*rng = x >> 6; /* use bits 6..13 */
|
|
else
|
|
*rng = x; /* use bits 0..7 */
|
|
#ifdef _DEBUG
|
|
LOG_RAND(("%05x: %02x\n", x, *rng));
|
|
#endif
|
|
rng++;
|
|
/* calculate next bit */
|
|
x = ((x << left) + (x >> right) + add) & mask;
|
|
}
|
|
}
|
|
|
|
|
|
/*static void register_for_save(pokey_state *chip, device_t *device)
|
|
{
|
|
device->save_item(NAME(chip->counter));
|
|
device->save_item(NAME(chip->divisor));
|
|
device->save_item(NAME(chip->volume));
|
|
device->save_item(NAME(chip->output));
|
|
device->save_item(NAME(chip->audible));
|
|
device->save_item(NAME(chip->samplepos_fract));
|
|
device->save_item(NAME(chip->samplepos_whole));
|
|
device->save_item(NAME(chip->polyadjust));
|
|
device->save_item(NAME(chip->p4));
|
|
device->save_item(NAME(chip->p5));
|
|
device->save_item(NAME(chip->p9));
|
|
device->save_item(NAME(chip->p17));
|
|
device->save_item(NAME(chip->r9));
|
|
device->save_item(NAME(chip->r17));
|
|
device->save_item(NAME(chip->clockmult));
|
|
device->save_item(NAME(chip->timer_period[0]));
|
|
device->save_item(NAME(chip->timer_period[1]));
|
|
device->save_item(NAME(chip->timer_period[2]));
|
|
device->save_item(NAME(chip->timer_param));
|
|
device->save_item(NAME(chip->AUDF));
|
|
device->save_item(NAME(chip->AUDC));
|
|
device->save_item(NAME(chip->POTx));
|
|
device->save_item(NAME(chip->AUDCTL));
|
|
device->save_item(NAME(chip->ALLPOT));
|
|
device->save_item(NAME(chip->KBCODE));
|
|
device->save_item(NAME(chip->RANDOM));
|
|
device->save_item(NAME(chip->SERIN));
|
|
device->save_item(NAME(chip->SEROUT));
|
|
device->save_item(NAME(chip->IRQST));
|
|
device->save_item(NAME(chip->IRQEN));
|
|
device->save_item(NAME(chip->SKSTAT));
|
|
device->save_item(NAME(chip->SKCTL));
|
|
}*/
|
|
|
|
|
|
//static DEVICE_START( pokey )
|
|
int device_start_pokey(void **_info, int clock)
|
|
{
|
|
//pokey_state *chip = get_safe_token(device);
|
|
pokey_state *chip;
|
|
//int sample_rate = device->clock();
|
|
int sample_rate = clock;
|
|
//int i;
|
|
|
|
chip = (pokey_state *) calloc(1, sizeof(pokey_state));
|
|
*_info = (void *) chip;
|
|
|
|
//if (device->static_config())
|
|
// memcpy(&chip->intf, device->static_config(), sizeof(pokey_interface));
|
|
//chip->device = device;
|
|
//chip->clock_period = attotime::from_hz(device->clock());
|
|
chip->clock_period = 1.0 / clock;
|
|
|
|
/* calculate the A/D times
|
|
* In normal, slow mode (SKCTL bit SK_PADDLE is clear) the conversion
|
|
* takes N scanlines, where N is the paddle value. A single scanline
|
|
* takes approximately 64us to finish (1.78979MHz clock).
|
|
* In quick mode (SK_PADDLE set) the conversion is done very fast
|
|
* (takes two scanlines) but the result is not as accurate.
|
|
*/
|
|
//chip->ad_time_fast = (attotime::from_nsec(64000*2/228) * FREQ_17_EXACT) / device->clock();
|
|
//chip->ad_time_slow = (attotime::from_nsec(64000 ) * FREQ_17_EXACT) / device->clock();
|
|
|
|
/* initialize the poly counters */
|
|
poly_init(chip->poly4, 4, 3, 1, 0x00004);
|
|
poly_init(chip->poly5, 5, 3, 2, 0x00008);
|
|
poly_init(chip->poly9, 9, 8, 1, 0x00180);
|
|
poly_init(chip->poly17, 17,16, 1, 0x1c000);
|
|
|
|
/* initialize the random arrays */
|
|
rand_init(chip->rand9, 9, 8, 1, 0x00180);
|
|
rand_init(chip->rand17, 17,16, 1, 0x1c000);
|
|
|
|
//chip->samplerate_24_8 = (device->clock() << 8) / sample_rate;
|
|
chip->samplerate_24_8 = (clock << 8) / sample_rate;
|
|
chip->divisor[CHAN1] = 4;
|
|
chip->divisor[CHAN2] = 4;
|
|
chip->divisor[CHAN3] = 4;
|
|
chip->divisor[CHAN4] = 4;
|
|
chip->clockmult = DIV_64;
|
|
chip->KBCODE = 0x09; /* Atari 800 'no key' */
|
|
chip->SKCTL = SK_RESET; /* let the RNG run after reset */
|
|
//chip->rtimer = device->machine().scheduler().timer_alloc(FUNC_NULL);
|
|
|
|
//chip->timer[0] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip);
|
|
//chip->timer[1] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip);
|
|
//chip->timer[2] = device->machine().scheduler().timer_alloc(FUNC(pokey_timer_expire), chip);
|
|
|
|
/*for (i=0; i<8; i++)
|
|
{
|
|
chip->ptimer[i] = device->machine().scheduler().timer_alloc(FUNC(pokey_pot_trigger), chip);
|
|
chip->pot_r[i].resolve(chip->intf.pot_r[i], *device);
|
|
}
|
|
chip->allpot_r.resolve(chip->intf.allpot_r, *device);
|
|
chip->serin_r.resolve(chip->intf.serin_r, *device);
|
|
chip->serout_w.resolve(chip->intf.serout_w, *device);
|
|
chip->interrupt_cb = chip->intf.interrupt_cb;*/
|
|
|
|
//chip->channel = device->machine().sound().stream_alloc(*device, 0, 1, sample_rate, chip, pokey_update);
|
|
|
|
//register_for_save(chip, device);
|
|
|
|
return sample_rate;
|
|
}
|
|
|
|
void device_stop_pokey(void *chip)
|
|
{
|
|
free(chip);
|
|
|
|
return;
|
|
}
|
|
|
|
void device_reset_pokey(void *_info)
|
|
{
|
|
pokey_state *chip = (pokey_state *)_info;
|
|
UINT8 CurChn;
|
|
|
|
for (CurChn = 0; CurChn < 4; CurChn ++)
|
|
{
|
|
chip->counter[CurChn] = 0;
|
|
chip->divisor[CurChn] = 4;
|
|
chip->volume[CurChn] = 0;
|
|
chip->output[CurChn] = 0;
|
|
chip->audible[CurChn] = 0;
|
|
}
|
|
chip->samplepos_fract = 0;
|
|
chip->samplepos_whole = 0;
|
|
chip->polyadjust = 0;
|
|
chip->p4 = 0;
|
|
chip->p5 = 0;
|
|
chip->p9 = 0;
|
|
chip->p17 = 0;
|
|
chip->r9 = 0;
|
|
chip->r17 = 0;
|
|
chip->clockmult = DIV_64;
|
|
|
|
return;
|
|
}
|
|
|
|
/*static TIMER_CALLBACK( pokey_timer_expire )
|
|
{
|
|
pokey_state *p = (pokey_state *)ptr;
|
|
int timers = param;
|
|
|
|
LOG_TIMER(("POKEY #%p timer %d with IRQEN $%02x\n", p, timers, p->IRQEN));
|
|
|
|
// check if some of the requested timer interrupts are enabled //
|
|
timers &= p->IRQEN;
|
|
|
|
if( timers )
|
|
{
|
|
// set the enabled timer irq status bits //
|
|
p->IRQST |= timers;
|
|
// call back an application supplied function to handle the interrupt //
|
|
if( p->interrupt_cb )
|
|
(*p->interrupt_cb)(p->device, timers);
|
|
}
|
|
}*/
|
|
|
|
/*static char *audc2str(int val)
|
|
{
|
|
static char buff[80];
|
|
if( val & NOTPOLY5 )
|
|
{
|
|
if( val & PURE )
|
|
strcpy(buff,"pure");
|
|
else
|
|
if( val & POLY4 )
|
|
strcpy(buff,"poly4");
|
|
else
|
|
strcpy(buff,"poly9/17");
|
|
}
|
|
else
|
|
{
|
|
if( val & PURE )
|
|
strcpy(buff,"poly5");
|
|
else
|
|
if( val & POLY4 )
|
|
strcpy(buff,"poly4+poly5");
|
|
else
|
|
strcpy(buff,"poly9/17+poly5");
|
|
}
|
|
return buff;
|
|
}
|
|
|
|
static char *audctl2str(int val)
|
|
{
|
|
static char buff[80];
|
|
if( val & POLY9 )
|
|
strcpy(buff,"poly9");
|
|
else
|
|
strcpy(buff,"poly17");
|
|
if( val & CH1_HICLK )
|
|
strcat(buff,"+ch1hi");
|
|
if( val & CH3_HICLK )
|
|
strcat(buff,"+ch3hi");
|
|
if( val & CH12_JOINED )
|
|
strcat(buff,"+ch1/2");
|
|
if( val & CH34_JOINED )
|
|
strcat(buff,"+ch3/4");
|
|
if( val & CH1_FILTER )
|
|
strcat(buff,"+ch1filter");
|
|
if( val & CH2_FILTER )
|
|
strcat(buff,"+ch2filter");
|
|
if( val & CLK_15KHZ )
|
|
strcat(buff,"+clk15");
|
|
return buff;
|
|
}*/
|
|
|
|
/*static TIMER_CALLBACK( pokey_serin_ready_cb )
|
|
{
|
|
pokey_state *p = (pokey_state *)ptr;
|
|
if( p->IRQEN & IRQ_SERIN )
|
|
{
|
|
// set the enabled timer irq status bits //
|
|
p->IRQST |= IRQ_SERIN;
|
|
// call back an application supplied function to handle the interrupt //
|
|
if( p->interrupt_cb )
|
|
(*p->interrupt_cb)(p->device, IRQ_SERIN);
|
|
}
|
|
}
|
|
|
|
static TIMER_CALLBACK( pokey_serout_ready_cb )
|
|
{
|
|
pokey_state *p = (pokey_state *)ptr;
|
|
if( p->IRQEN & IRQ_SEROR )
|
|
{
|
|
p->IRQST |= IRQ_SEROR;
|
|
if( p->interrupt_cb )
|
|
(*p->interrupt_cb)(p->device, IRQ_SEROR);
|
|
}
|
|
}
|
|
|
|
static TIMER_CALLBACK( pokey_serout_complete )
|
|
{
|
|
pokey_state *p = (pokey_state *)ptr;
|
|
if( p->IRQEN & IRQ_SEROC )
|
|
{
|
|
p->IRQST |= IRQ_SEROC;
|
|
if( p->interrupt_cb )
|
|
(*p->interrupt_cb)(p->device, IRQ_SEROC);
|
|
}
|
|
}
|
|
|
|
static TIMER_CALLBACK( pokey_pot_trigger )
|
|
{
|
|
pokey_state *p = (pokey_state *)ptr;
|
|
int pot = param;
|
|
LOG(("POKEY #%p POT%d triggers after %dus\n", p, pot, (int)(1000000 * p->ptimer[pot]->elapsed().as_double())));
|
|
p->ALLPOT &= ~(1 << pot); // set the enabled timer irq status bits //
|
|
}*/
|
|
|
|
/*#define AD_TIME ((p->SKCTL & SK_PADDLE) ? p->ad_time_fast : p->ad_time_slow)
|
|
|
|
static void pokey_potgo(pokey_state *p)
|
|
{
|
|
int pot;
|
|
|
|
LOG(("POKEY #%p pokey_potgo\n", p));
|
|
|
|
p->ALLPOT = 0xff;
|
|
|
|
for( pot = 0; pot < 8; pot++ )
|
|
{
|
|
p->POTx[pot] = 0xff;
|
|
if( !p->pot_r[pot].isnull() )
|
|
{
|
|
int r = p->pot_r[pot](pot);
|
|
|
|
LOG(("POKEY %s pot_r(%d) returned $%02x\n", p->device->tag(), pot, r));
|
|
if( r != -1 )
|
|
{
|
|
if (r > 228)
|
|
r = 228;
|
|
|
|
// final value //
|
|
p->POTx[pot] = r;
|
|
p->ptimer[pot]->adjust(AD_TIME * r, pot);
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
|
|
//READ8_DEVICE_HANDLER( pokey_r )
|
|
UINT8 pokey_r(void *_info, offs_t offset)
|
|
{
|
|
//pokey_state *p = get_safe_token(device);
|
|
pokey_state *p = (pokey_state *)_info;
|
|
int data = 0, pot;
|
|
UINT32 adjust = 0;
|
|
|
|
switch (offset & 15)
|
|
{
|
|
case POT0_C: case POT1_C: case POT2_C: case POT3_C:
|
|
case POT4_C: case POT5_C: case POT6_C: case POT7_C:
|
|
pot = offset & 7;
|
|
/*if( !p->pot_r[pot].isnull() )
|
|
{
|
|
//
|
|
* If the conversion is not yet finished (ptimer running),
|
|
* get the current value by the linear interpolation of
|
|
* the final value using the elapsed time.
|
|
//
|
|
if( p->ALLPOT & (1 << pot) )
|
|
{
|
|
//data = p->ptimer[pot]->elapsed().attoseconds / AD_TIME.attoseconds;
|
|
data = p->POTx[pot];
|
|
LOG(("POKEY '%s' read POT%d (interpolated) $%02x\n", p->device->tag(), pot, data));
|
|
}
|
|
else
|
|
{
|
|
data = p->POTx[pot];
|
|
LOG(("POKEY '%s' read POT%d (final value) $%02x\n", p->device->tag(), pot, data));
|
|
}
|
|
}
|
|
else
|
|
logerror("%s: warning - read '%s' POT%d\n", p->device->machine().describe_context(), p->device->tag(), pot);*/
|
|
break;
|
|
|
|
case ALLPOT_C:
|
|
/****************************************************************
|
|
* If the 2 least significant bits of SKCTL are 0, the ALLPOTs
|
|
* are disabled (SKRESET). Thanks to MikeJ for pointing this out.
|
|
****************************************************************/
|
|
/*if( (p->SKCTL & SK_RESET) == 0)
|
|
{
|
|
data = 0;
|
|
LOG(("POKEY '%s' ALLPOT internal $%02x (reset)\n", p->device->tag(), data));
|
|
}
|
|
else if( !p->allpot_r.isnull() )
|
|
{
|
|
data = p->allpot_r(offset);
|
|
LOG(("POKEY '%s' ALLPOT callback $%02x\n", p->device->tag(), data));
|
|
}
|
|
else
|
|
{
|
|
data = p->ALLPOT;
|
|
LOG(("POKEY '%s' ALLPOT internal $%02x\n", p->device->tag(), data));
|
|
}*/
|
|
break;
|
|
|
|
case KBCODE_C:
|
|
data = p->KBCODE;
|
|
break;
|
|
|
|
case RANDOM_C:
|
|
/****************************************************************
|
|
* If the 2 least significant bits of SKCTL are 0, the random
|
|
* number generator is disabled (SKRESET). Thanks to Eric Smith
|
|
* for pointing out this critical bit of info! If the random
|
|
* number generator is enabled, get a new random number. Take
|
|
* the time gone since the last read into account and read the
|
|
* new value from an appropriate offset in the rand17 table.
|
|
****************************************************************/
|
|
if( p->SKCTL & SK_RESET )
|
|
{
|
|
//adjust = p->rtimer->elapsed().as_double() / p->clock_period.as_double();
|
|
adjust = 0;
|
|
p->r9 = (p->r9 + adjust) % 0x001ff;
|
|
p->r17 = (p->r17 + adjust) % 0x1ffff;
|
|
}
|
|
else
|
|
{
|
|
adjust = 1;
|
|
p->r9 = 0;
|
|
p->r17 = 0;
|
|
//LOG_RAND(("POKEY '%s' rand17 frozen (SKCTL): $%02x\n", p->device->tag(), p->RANDOM));
|
|
}
|
|
if( p->AUDCTL & POLY9 )
|
|
{
|
|
p->RANDOM = p->rand9[p->r9];
|
|
//LOG_RAND(("POKEY '%s' adjust %u rand9[$%05x]: $%02x\n", p->device->tag(), adjust, p->r9, p->RANDOM));
|
|
}
|
|
else
|
|
{
|
|
p->RANDOM = p->rand17[p->r17];
|
|
//LOG_RAND(("POKEY '%s' adjust %u rand17[$%05x]: $%02x\n", p->device->tag(), adjust, p->r17, p->RANDOM));
|
|
}
|
|
//if (adjust > 0)
|
|
// p->rtimer->adjust(attotime::never);
|
|
data = p->RANDOM ^ 0xff;
|
|
break;
|
|
|
|
case SERIN_C:
|
|
//if( !p->serin_r.isnull() )
|
|
// p->SERIN = p->serin_r(offset);
|
|
data = p->SERIN;
|
|
//LOG(("POKEY '%s' SERIN $%02x\n", p->device->tag(), data));
|
|
break;
|
|
|
|
case IRQST_C:
|
|
/* IRQST is an active low input port; we keep it active high */
|
|
/* internally to ease the (un-)masking of bits */
|
|
data = p->IRQST ^ 0xff;
|
|
//LOG(("POKEY '%s' IRQST $%02x\n", p->device->tag(), data));
|
|
break;
|
|
|
|
case SKSTAT_C:
|
|
/* SKSTAT is also an active low input port */
|
|
data = p->SKSTAT ^ 0xff;
|
|
//LOG(("POKEY '%s' SKSTAT $%02x\n", p->device->tag(), data));
|
|
break;
|
|
|
|
default:
|
|
//LOG(("POKEY '%s' register $%02x\n", p->device->tag(), offset));
|
|
break;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/*READ8_HANDLER( quad_pokey_r )
|
|
{
|
|
static const char *const devname[4] = { "pokey1", "pokey2", "pokey3", "pokey4" };
|
|
int pokey_num = (offset >> 3) & ~0x04;
|
|
int control = (offset & 0x20) >> 2;
|
|
int pokey_reg = (offset % 8) | control;
|
|
|
|
return pokey_r(space->machine().device(devname[pokey_num]), pokey_reg);
|
|
}*/
|
|
|
|
|
|
//WRITE8_DEVICE_HANDLER( pokey_w )
|
|
void pokey_w(void *_info, offs_t offset, UINT8 data)
|
|
{
|
|
//pokey_state *p = get_safe_token(device);
|
|
pokey_state *p = (pokey_state *)_info;
|
|
int ch_mask = 0, new_val;
|
|
|
|
//p->channel->update();
|
|
|
|
/* determine which address was changed */
|
|
switch (offset & 15)
|
|
{
|
|
case AUDF1_C:
|
|
if( data == p->AUDF[CHAN1] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDF1 $%02x\n", p->device->tag(), data));
|
|
p->AUDF[CHAN1] = data;
|
|
ch_mask = 1 << CHAN1;
|
|
if( p->AUDCTL & CH12_JOINED ) /* if ch 1&2 tied together */
|
|
ch_mask |= 1 << CHAN2; /* then also change on ch2 */
|
|
break;
|
|
|
|
case AUDC1_C:
|
|
if( data == p->AUDC[CHAN1] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDC1 $%02x (%s)\n", p->device->tag(), data, audc2str(data)));
|
|
p->AUDC[CHAN1] = data;
|
|
ch_mask = 1 << CHAN1;
|
|
break;
|
|
|
|
case AUDF2_C:
|
|
if( data == p->AUDF[CHAN2] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDF2 $%02x\n", p->device->tag(), data));
|
|
p->AUDF[CHAN2] = data;
|
|
ch_mask = 1 << CHAN2;
|
|
break;
|
|
|
|
case AUDC2_C:
|
|
if( data == p->AUDC[CHAN2] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDC2 $%02x (%s)\n", p->device->tag(), data, audc2str(data)));
|
|
p->AUDC[CHAN2] = data;
|
|
ch_mask = 1 << CHAN2;
|
|
break;
|
|
|
|
case AUDF3_C:
|
|
if( data == p->AUDF[CHAN3] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDF3 $%02x\n", p->device->tag(), data));
|
|
p->AUDF[CHAN3] = data;
|
|
ch_mask = 1 << CHAN3;
|
|
|
|
if( p->AUDCTL & CH34_JOINED ) /* if ch 3&4 tied together */
|
|
ch_mask |= 1 << CHAN4; /* then also change on ch4 */
|
|
break;
|
|
|
|
case AUDC3_C:
|
|
if( data == p->AUDC[CHAN3] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDC3 $%02x (%s)\n", p->device->tag(), data, audc2str(data)));
|
|
p->AUDC[CHAN3] = data;
|
|
ch_mask = 1 << CHAN3;
|
|
break;
|
|
|
|
case AUDF4_C:
|
|
if( data == p->AUDF[CHAN4] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDF4 $%02x\n", p->device->tag(), data));
|
|
p->AUDF[CHAN4] = data;
|
|
ch_mask = 1 << CHAN4;
|
|
break;
|
|
|
|
case AUDC4_C:
|
|
if( data == p->AUDC[CHAN4] )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDC4 $%02x (%s)\n", p->device->tag(), data, audc2str(data)));
|
|
p->AUDC[CHAN4] = data;
|
|
ch_mask = 1 << CHAN4;
|
|
break;
|
|
|
|
case AUDCTL_C:
|
|
if( data == p->AUDCTL )
|
|
return;
|
|
//LOG_SOUND(("POKEY '%s' AUDCTL $%02x (%s)\n", p->device->tag(), data, audctl2str(data)));
|
|
p->AUDCTL = data;
|
|
ch_mask = 15; /* all channels */
|
|
/* determine the base multiplier for the 'div by n' calculations */
|
|
p->clockmult = (p->AUDCTL & CLK_15KHZ) ? DIV_15 : DIV_64;
|
|
break;
|
|
|
|
case STIMER_C:
|
|
/*// first remove any existing timers //
|
|
LOG_TIMER(("POKEY '%s' STIMER $%02x\n", p->device->tag(), data));
|
|
|
|
p->timer[TIMER1]->adjust(attotime::never, p->timer_param[TIMER1]);
|
|
p->timer[TIMER2]->adjust(attotime::never, p->timer_param[TIMER2]);
|
|
p->timer[TIMER4]->adjust(attotime::never, p->timer_param[TIMER4]);
|
|
|
|
// reset all counters to zero (side effect) //
|
|
p->polyadjust = 0;
|
|
p->counter[CHAN1] = 0;
|
|
p->counter[CHAN2] = 0;
|
|
p->counter[CHAN3] = 0;
|
|
p->counter[CHAN4] = 0;
|
|
|
|
// joined chan#1 and chan#2 ? //
|
|
if( p->AUDCTL & CH12_JOINED )
|
|
{
|
|
if( p->divisor[CHAN2] > 4 )
|
|
{
|
|
LOG_TIMER(("POKEY '%s' timer1+2 after %d clocks\n", p->device->tag(), p->divisor[CHAN2]));
|
|
// set timer #1 _and_ #2 event after timer_div clocks of joined CHAN1+CHAN2 //
|
|
p->timer_period[TIMER2] = p->clock_period * p->divisor[CHAN2];
|
|
p->timer_param[TIMER2] = IRQ_TIMR2|IRQ_TIMR1;
|
|
p->timer[TIMER2]->adjust(p->timer_period[TIMER2], p->timer_param[TIMER2], p->timer_period[TIMER2]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( p->divisor[CHAN1] > 4 )
|
|
{
|
|
LOG_TIMER(("POKEY '%s' timer1 after %d clocks\n", p->device->tag(), p->divisor[CHAN1]));
|
|
// set timer #1 event after timer_div clocks of CHAN1 //
|
|
p->timer_period[TIMER1] = p->clock_period * p->divisor[CHAN1];
|
|
p->timer_param[TIMER1] = IRQ_TIMR1;
|
|
p->timer[TIMER1]->adjust(p->timer_period[TIMER1], p->timer_param[TIMER1], p->timer_period[TIMER1]);
|
|
}
|
|
|
|
if( p->divisor[CHAN2] > 4 )
|
|
{
|
|
LOG_TIMER(("POKEY '%s' timer2 after %d clocks\n", p->device->tag(), p->divisor[CHAN2]));
|
|
// set timer #2 event after timer_div clocks of CHAN2 //
|
|
p->timer_period[TIMER2] = p->clock_period * p->divisor[CHAN2];
|
|
p->timer_param[TIMER2] = IRQ_TIMR2;
|
|
p->timer[TIMER2]->adjust(p->timer_period[TIMER2], p->timer_param[TIMER2], p->timer_period[TIMER2]);
|
|
}
|
|
}
|
|
|
|
// Note: p[chip] does not have a timer #3 //
|
|
|
|
if( p->AUDCTL & CH34_JOINED )
|
|
{
|
|
// not sure about this: if audc4 == 0000xxxx don't start timer 4 ? //
|
|
if( p->AUDC[CHAN4] & 0xf0 )
|
|
{
|
|
if( p->divisor[CHAN4] > 4 )
|
|
{
|
|
LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", p->device->tag(), p->divisor[CHAN4]));
|
|
// set timer #4 event after timer_div clocks of CHAN4 //
|
|
p->timer_period[TIMER4] = p->clock_period * p->divisor[CHAN4];
|
|
p->timer_param[TIMER4] = IRQ_TIMR4;
|
|
p->timer[TIMER4]->adjust(p->timer_period[TIMER4], p->timer_param[TIMER4], p->timer_period[TIMER4]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( p->divisor[CHAN4] > 4 )
|
|
{
|
|
LOG_TIMER(("POKEY '%s' timer4 after %d clocks\n", p->device->tag(), p->divisor[CHAN4]));
|
|
// set timer #4 event after timer_div clocks of CHAN4 //
|
|
p->timer_period[TIMER4] = p->clock_period * p->divisor[CHAN4];
|
|
p->timer_param[TIMER4] = IRQ_TIMR4;
|
|
p->timer[TIMER4]->adjust(p->timer_period[TIMER4], p->timer_param[TIMER4], p->timer_period[TIMER4]);
|
|
}
|
|
}
|
|
|
|
p->timer[TIMER1]->enable(p->IRQEN & IRQ_TIMR1);
|
|
p->timer[TIMER2]->enable(p->IRQEN & IRQ_TIMR2);
|
|
p->timer[TIMER4]->enable(p->IRQEN & IRQ_TIMR4);*/
|
|
break;
|
|
|
|
case SKREST_C:
|
|
/* reset SKSTAT */
|
|
//LOG(("POKEY '%s' SKREST $%02x\n", p->device->tag(), data));
|
|
p->SKSTAT &= ~(SK_FRAME|SK_OVERRUN|SK_KBERR);
|
|
break;
|
|
|
|
case POTGO_C:
|
|
//LOG(("POKEY '%s' POTGO $%02x\n", p->device->tag(), data));
|
|
//pokey_potgo(p);
|
|
break;
|
|
|
|
case SEROUT_C:
|
|
//LOG(("POKEY '%s' SEROUT $%02x\n", p->device->tag(), data));
|
|
//p->serout_w(offset, data);
|
|
//p->SKSTAT |= SK_SEROUT;
|
|
/*
|
|
* These are arbitrary values, tested with some custom boot
|
|
* loaders from Ballblazer and Escape from Fractalus
|
|
* The real times are unknown
|
|
*/
|
|
//device->machine().scheduler().timer_set(attotime::from_usec(200), FUNC(pokey_serout_ready_cb), 0, p);
|
|
/* 10 bits (assumption 1 start, 8 data and 1 stop bit) take how long? */
|
|
//device->machine().scheduler().timer_set(attotime::from_usec(2000), FUNC(pokey_serout_complete), 0, p);
|
|
break;
|
|
|
|
case IRQEN_C:
|
|
//LOG(("POKEY '%s' IRQEN $%02x\n", p->device->tag(), data));
|
|
|
|
/* acknowledge one or more IRQST bits ? */
|
|
if( p->IRQST & ~data )
|
|
{
|
|
/* reset IRQST bits that are masked now */
|
|
p->IRQST &= data;
|
|
}
|
|
else
|
|
{
|
|
/* enable/disable timers now to avoid unneeded
|
|
breaking of the CPU cores for masked timers */
|
|
/*if( p->timer[TIMER1] && ((p->IRQEN^data) & IRQ_TIMR1) )
|
|
p->timer[TIMER1]->enable(data & IRQ_TIMR1);
|
|
if( p->timer[TIMER2] && ((p->IRQEN^data) & IRQ_TIMR2) )
|
|
p->timer[TIMER2]->enable(data & IRQ_TIMR2);
|
|
if( p->timer[TIMER4] && ((p->IRQEN^data) & IRQ_TIMR4) )
|
|
p->timer[TIMER4]->enable(data & IRQ_TIMR4);*/
|
|
}
|
|
/* store irq enable */
|
|
p->IRQEN = data;
|
|
break;
|
|
|
|
case SKCTL_C:
|
|
if( data == p->SKCTL )
|
|
return;
|
|
//LOG(("POKEY '%s' SKCTL $%02x\n", p->device->tag(), data));
|
|
p->SKCTL = data;
|
|
if( !(data & SK_RESET) )
|
|
{
|
|
pokey_w(p, IRQEN_C, 0);
|
|
pokey_w(p, SKREST_C, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/************************************************************
|
|
* As defined in the manual, the exact counter values are
|
|
* different depending on the frequency and resolution:
|
|
* 64 kHz or 15 kHz - AUDF + 1
|
|
* 1.79 MHz, 8-bit - AUDF + 4
|
|
* 1.79 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7
|
|
************************************************************/
|
|
|
|
/* only reset the channels that have changed */
|
|
|
|
if( ch_mask & (1 << CHAN1) )
|
|
{
|
|
/* process channel 1 frequency */
|
|
if( p->AUDCTL & CH1_HICLK )
|
|
new_val = p->AUDF[CHAN1] + DIVADD_HICLK;
|
|
else
|
|
new_val = (p->AUDF[CHAN1] + DIVADD_LOCLK) * p->clockmult;
|
|
|
|
//LOG_SOUND(("POKEY '%s' chan1 %d\n", p->device->tag(), new_val));
|
|
|
|
p->volume[CHAN1] = (p->AUDC[CHAN1] & VOLUME_MASK) * POKEY_DEFAULT_GAIN;
|
|
p->divisor[CHAN1] = new_val;
|
|
if( new_val < p->counter[CHAN1] )
|
|
p->counter[CHAN1] = new_val;
|
|
//if( p->interrupt_cb && p->timer[TIMER1] )
|
|
// p->timer[TIMER1]->adjust(p->clock_period * new_val, p->timer_param[TIMER1], p->timer_period[TIMER1]);
|
|
p->audible[CHAN1] = !(
|
|
(p->AUDC[CHAN1] & VOLUME_ONLY) ||
|
|
(p->AUDC[CHAN1] & VOLUME_MASK) == 0 ||
|
|
((p->AUDC[CHAN1] & PURE) && new_val < (p->samplerate_24_8 >> 8)));
|
|
if( !p->audible[CHAN1] )
|
|
{
|
|
p->output[CHAN1] = 1;
|
|
p->counter[CHAN1] = 0x7fffffff;
|
|
/* 50% duty cycle should result in half volume */
|
|
p->volume[CHAN1] >>= 1;
|
|
}
|
|
}
|
|
|
|
if( ch_mask & (1 << CHAN2) )
|
|
{
|
|
/* process channel 2 frequency */
|
|
if( p->AUDCTL & CH12_JOINED )
|
|
{
|
|
if( p->AUDCTL & CH1_HICLK )
|
|
new_val = p->AUDF[CHAN2] * 256 + p->AUDF[CHAN1] + DIVADD_HICLK_JOINED;
|
|
else
|
|
new_val = (p->AUDF[CHAN2] * 256 + p->AUDF[CHAN1] + DIVADD_LOCLK) * p->clockmult;
|
|
//LOG_SOUND(("POKEY '%s' chan1+2 %d\n", p->device->tag(), new_val));
|
|
}
|
|
else
|
|
{
|
|
new_val = (p->AUDF[CHAN2] + DIVADD_LOCLK) * p->clockmult;
|
|
//LOG_SOUND(("POKEY '%s' chan2 %d\n", p->device->tag(), new_val));
|
|
}
|
|
|
|
p->volume[CHAN2] = (p->AUDC[CHAN2] & VOLUME_MASK) * POKEY_DEFAULT_GAIN;
|
|
p->divisor[CHAN2] = new_val;
|
|
if( new_val < p->counter[CHAN2] )
|
|
p->counter[CHAN2] = new_val;
|
|
//if( p->interrupt_cb && p->timer[TIMER2] )
|
|
// p->timer[TIMER2]->adjust(p->clock_period * new_val, p->timer_param[TIMER2], p->timer_period[TIMER2]);
|
|
p->audible[CHAN2] = !(
|
|
(p->AUDC[CHAN2] & VOLUME_ONLY) ||
|
|
(p->AUDC[CHAN2] & VOLUME_MASK) == 0 ||
|
|
((p->AUDC[CHAN2] & PURE) && new_val < (p->samplerate_24_8 >> 8)));
|
|
if( !p->audible[CHAN2] )
|
|
{
|
|
p->output[CHAN2] = 1;
|
|
p->counter[CHAN2] = 0x7fffffff;
|
|
/* 50% duty cycle should result in half volume */
|
|
p->volume[CHAN2] >>= 1;
|
|
}
|
|
}
|
|
|
|
if( ch_mask & (1 << CHAN3) )
|
|
{
|
|
/* process channel 3 frequency */
|
|
if( p->AUDCTL & CH3_HICLK )
|
|
new_val = p->AUDF[CHAN3] + DIVADD_HICLK;
|
|
else
|
|
new_val = (p->AUDF[CHAN3] + DIVADD_LOCLK) * p->clockmult;
|
|
|
|
//LOG_SOUND(("POKEY '%s' chan3 %d\n", p->device->tag(), new_val));
|
|
|
|
p->volume[CHAN3] = (p->AUDC[CHAN3] & VOLUME_MASK) * POKEY_DEFAULT_GAIN;
|
|
p->divisor[CHAN3] = new_val;
|
|
if( new_val < p->counter[CHAN3] )
|
|
p->counter[CHAN3] = new_val;
|
|
/* channel 3 does not have a timer associated */
|
|
p->audible[CHAN3] = !(
|
|
(p->AUDC[CHAN3] & VOLUME_ONLY) ||
|
|
(p->AUDC[CHAN3] & VOLUME_MASK) == 0 ||
|
|
((p->AUDC[CHAN3] & PURE) && new_val < (p->samplerate_24_8 >> 8))) ||
|
|
(p->AUDCTL & CH1_FILTER);
|
|
if( !p->audible[CHAN3] )
|
|
{
|
|
p->output[CHAN3] = 1;
|
|
p->counter[CHAN3] = 0x7fffffff;
|
|
/* 50% duty cycle should result in half volume */
|
|
p->volume[CHAN3] >>= 1;
|
|
}
|
|
}
|
|
|
|
if( ch_mask & (1 << CHAN4) )
|
|
{
|
|
/* process channel 4 frequency */
|
|
if( p->AUDCTL & CH34_JOINED )
|
|
{
|
|
if( p->AUDCTL & CH3_HICLK )
|
|
new_val = p->AUDF[CHAN4] * 256 + p->AUDF[CHAN3] + DIVADD_HICLK_JOINED;
|
|
else
|
|
new_val = (p->AUDF[CHAN4] * 256 + p->AUDF[CHAN3] + DIVADD_LOCLK) * p->clockmult;
|
|
//LOG_SOUND(("POKEY '%s' chan3+4 %d\n", p->device->tag(), new_val));
|
|
}
|
|
else
|
|
{
|
|
new_val = (p->AUDF[CHAN4] + DIVADD_LOCLK) * p->clockmult;
|
|
//LOG_SOUND(("POKEY '%s' chan4 %d\n", p->device->tag(), new_val));
|
|
}
|
|
|
|
p->volume[CHAN4] = (p->AUDC[CHAN4] & VOLUME_MASK) * POKEY_DEFAULT_GAIN;
|
|
p->divisor[CHAN4] = new_val;
|
|
if( new_val < p->counter[CHAN4] )
|
|
p->counter[CHAN4] = new_val;
|
|
//if( p->interrupt_cb && p->timer[TIMER4] )
|
|
// p->timer[TIMER4]->adjust(p->clock_period * new_val, p->timer_param[TIMER4], p->timer_period[TIMER4]);
|
|
p->audible[CHAN4] = !(
|
|
(p->AUDC[CHAN4] & VOLUME_ONLY) ||
|
|
(p->AUDC[CHAN4] & VOLUME_MASK) == 0 ||
|
|
((p->AUDC[CHAN4] & PURE) && new_val < (p->samplerate_24_8 >> 8))) ||
|
|
(p->AUDCTL & CH2_FILTER);
|
|
if( !p->audible[CHAN4] )
|
|
{
|
|
p->output[CHAN4] = 1;
|
|
p->counter[CHAN4] = 0x7fffffff;
|
|
/* 50% duty cycle should result in half volume */
|
|
p->volume[CHAN4] >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*WRITE8_HANDLER( quad_pokey_w )
|
|
{
|
|
static const char *const devname[4] = { "pokey1", "pokey2", "pokey3", "pokey4" };
|
|
int pokey_num = (offset >> 3) & ~0x04;
|
|
int control = (offset & 0x20) >> 2;
|
|
int pokey_reg = (offset % 8) | control;
|
|
|
|
pokey_w(space->machine().device(devname[pokey_num]), pokey_reg, data);
|
|
}
|
|
|
|
void pokey_serin_ready(device_t *device, int after)
|
|
{
|
|
pokey_state *p = get_safe_token(device);
|
|
device->machine().scheduler().timer_set(p->clock_period * after, FUNC(pokey_serin_ready_cb), 0, p);
|
|
}
|
|
|
|
void pokey_break_w(device_t *device, int shift)
|
|
{
|
|
//pokey_state *p = get_safe_token(device);
|
|
if( shift ) // shift code ? //
|
|
p->SKSTAT |= SK_SHIFT;
|
|
else
|
|
p->SKSTAT &= ~SK_SHIFT;
|
|
// check if the break IRQ is enabled //
|
|
if( p->IRQEN & IRQ_BREAK )
|
|
{
|
|
// set break IRQ status and call back the interrupt handler //
|
|
p->IRQST |= IRQ_BREAK;
|
|
if( p->interrupt_cb )
|
|
(*p->interrupt_cb)(device, IRQ_BREAK);
|
|
}
|
|
}
|
|
|
|
void pokey_kbcode_w(device_t *device, int kbcode, int make)
|
|
{
|
|
pokey_state *p = get_safe_token(device);
|
|
// make code ? //
|
|
if( make )
|
|
{
|
|
p->KBCODE = kbcode;
|
|
p->SKSTAT |= SK_KEYBD;
|
|
if( kbcode & 0x40 ) // shift code ? //
|
|
p->SKSTAT |= SK_SHIFT;
|
|
else
|
|
p->SKSTAT &= ~SK_SHIFT;
|
|
|
|
if( p->IRQEN & IRQ_KEYBD )
|
|
{
|
|
// last interrupt not acknowledged ? //
|
|
if( p->IRQST & IRQ_KEYBD )
|
|
p->SKSTAT |= SK_KBERR;
|
|
p->IRQST |= IRQ_KEYBD;
|
|
if( p->interrupt_cb )
|
|
(*p->interrupt_cb)(device, IRQ_KEYBD);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p->KBCODE = kbcode;
|
|
p->SKSTAT &= ~SK_KEYBD;
|
|
}
|
|
}*/
|
|
|
|
|
|
void pokey_set_mute_mask(void *_info, UINT32 MuteMask)
|
|
{
|
|
pokey_state *chip = (pokey_state *)_info;
|
|
UINT8 CurChn;
|
|
|
|
for (CurChn = 0; CurChn < 4; CurChn ++)
|
|
chip->Muted[CurChn] = (MuteMask >> CurChn) & 0x01;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**************************************************************************
|
|
* Generic get_info
|
|
**************************************************************************/
|
|
|
|
/*DEVICE_GET_INFO( pokey )
|
|
{
|
|
switch (state)
|
|
{
|
|
// --- the following bits of info are returned as 64-bit signed integers --- //
|
|
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pokey_state); break;
|
|
|
|
// --- the following bits of info are returned as pointers to data or functions --- //
|
|
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( pokey ); break;
|
|
case DEVINFO_FCT_STOP: // Nothing // break;
|
|
case DEVINFO_FCT_RESET: // Nothing // break;
|
|
|
|
// --- the following bits of info are returned as NULL-terminated strings --- //
|
|
case DEVINFO_STR_NAME: strcpy(info->s, "POKEY"); break;
|
|
case DEVINFO_STR_FAMILY: strcpy(info->s, "Atari custom"); break;
|
|
case DEVINFO_STR_VERSION: strcpy(info->s, "4.51"); break;
|
|
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
|
|
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
|
|
}
|
|
}
|
|
|
|
|
|
DEFINE_LEGACY_SOUND_DEVICE(POKEY, pokey);*/
|