/* * pokey.c - POKEY sound chip emulation * * Copyright (C) 1995-1998 David Firth * Copyright (C) 1998-2008 Atari800 development team (see DOC/CREDITS) * * This file is part of the Atari800 emulator project which emulates * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. * * Atari800 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. * * Atari800 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 Atari800; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cpu.h" #include "pokey.h" #include "gtia.h" #ifdef SOUND #include "pokeysnd.h" #endif #include "antic.h" #ifdef VOICEBOX #include "voicebox.h" #include "votraxsnd.h" #endif #ifdef POKEY_UPDATE void pokey_update(void); #endif UBYTE POKEY_KBCODE; UBYTE POKEY_SERIN; UBYTE POKEY_IRQST; UBYTE POKEY_IRQEN; UBYTE POKEY_SKSTAT; UBYTE POKEY_SKCTL; int POKEY_DELAYED_SERIN_IRQ; int POKEY_DELAYED_SEROUT_IRQ; int POKEY_DELAYED_XMTDONE_IRQ; /* structures to hold the 9 pokey control bytes */ UBYTE POKEY_AUDF[4 * POKEY_MAXPOKEYS]; /* AUDFx (D200, D202, D204, D206) */ UBYTE POKEY_AUDC[4 * POKEY_MAXPOKEYS]; /* AUDCx (D201, D203, D205, D207) */ UBYTE POKEY_AUDCTL[POKEY_MAXPOKEYS]; /* AUDCTL (D208) */ int POKEY_DivNIRQ[4], POKEY_DivNMax[4]; int POKEY_Base_mult[POKEY_MAXPOKEYS]; /* selects either 64Khz or 15Khz clock mult */ UBYTE POKEY_POT_input[8] = {228, 228, 228, 228, 228, 228, 228, 228}; static int pot_scanline; #include "noise.h" //UBYTE POKEY_poly9_lookup[POKEY_POLY9_SIZE]; //UBYTE POKEY_poly17_lookup[POKEY_POLY17_SIZE]; static ULONG random_scanline_counter; ULONG POKEY_GetRandomCounter(void) { return random_scanline_counter; } void POKEY_SetRandomCounter(ULONG value) { random_scanline_counter = value; } UBYTE POKEY_GetByte(UWORD addr, int no_side_effects) { UBYTE byte = 0xff; #ifdef STEREO_SOUND if (addr & 0x0010 && POKEYSND_stereo_enabled) return 0; #endif addr &= 0x0f; if (addr < 8) { return (INPUT_handle_trigger(addr)); //byte = POKEY_POT_input[addr]; //if (byte <= pot_scanline) // return byte; //return pot_scanline; } switch (addr) { case POKEY_OFFSET_ALLPOT: { int i; for (i = 0; i < 8; i++) if (POKEY_POT_input[i] <= pot_scanline) byte &= ~(1 << i); /* reset bit if pot value known */ } break; case POKEY_OFFSET_KBCODE: byte = POKEY_KBCODE; break; case POKEY_OFFSET_RANDOM: if ((POKEY_SKCTL & 0x03) != 0) { int i = random_scanline_counter + ANTIC_XPOS; if (POKEY_AUDCTL[0] & POKEY_POLY9) byte = POKEY_poly9_lookup[i % POKEY_POLY9_SIZE]; else { const UBYTE *ptr; i %= POKEY_POLY17_SIZE; ptr = POKEY_poly17_lookup + (i >> 3); i &= 7; byte = (UBYTE) ((ptr[0] >> i) + (ptr[1] << (8 - i))); } } break; case POKEY_OFFSET_SERIN: byte = POKEY_SERIN; #ifdef DEBUG3 printf("SERIO: SERIN read, bytevalue %02x\n", POKEY_SERIN); #endif #ifdef SERIO_SOUND POKEYSND_UpdateSerio(0,byte); #endif break; case POKEY_OFFSET_IRQST: byte = POKEY_IRQST; break; case POKEY_OFFSET_SKSTAT: #if SKIP byte = POKEY_SKSTAT + (CASSETTE_IOLineStatus() << 4); #else byte = INPUT_handle_skstat(addr); // byte = POKEY_SKSTAT; #endif #ifdef VOICEBOX if (VOICEBOX_enabled) { byte = POKEY_SKSTAT + (VOTRAXSND_busy << 4); } #endif break; } return byte; } static void Update_Counter(int chan_mask); static int POKEY_siocheck(void) { return (((POKEY_AUDF[POKEY_CHAN3] == 0x28 || POKEY_AUDF[POKEY_CHAN3] == 0x10 || POKEY_AUDF[POKEY_CHAN3] == 0x08 || POKEY_AUDF[POKEY_CHAN3] == 0x0a) && POKEY_AUDF[POKEY_CHAN4] == 0x00) /* intelligent peripherals speeds */ || (POKEY_SKCTL & 0x78) == 0x28) /* cassette save mode */ && (POKEY_AUDCTL[0] & 0x28) == 0x28; } #ifndef SOUND_GAIN /* sound gain can be pre-defined in the configure/Makefile */ #define SOUND_GAIN 4 #endif #ifndef SOUND #define POKEYSND_Update(addr, val, chip, gain) #else //#define POKEYSND_Update(addr, val, chip, gain) if (chip == 0) emu_sndPlaySound((chip*4)+addr, gain*16, val*16) #endif void POKEY_PutByte(UWORD addr, UBYTE byte) { #ifdef STEREO_SOUND addr &= POKEYSND_stereo_enabled ? 0x1f : 0x0f; #else addr &= 0x0f; #endif switch (addr) { case POKEY_OFFSET_AUDC1: POKEY_AUDC[POKEY_CHAN1] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC1, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDC2: POKEY_AUDC[POKEY_CHAN2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC2, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDC3: POKEY_AUDC[POKEY_CHAN3] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC3, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDC4: POKEY_AUDC[POKEY_CHAN4] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC4, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDCTL: POKEY_AUDCTL[0] = byte; /* determine the base multiplier for the 'div by n' calculations */ if (byte & POKEY_CLOCK_15) POKEY_Base_mult[0] = POKEY_DIV_15; else POKEY_Base_mult[0] = POKEY_DIV_64; Update_Counter((1 << POKEY_CHAN1) | (1 << POKEY_CHAN2) | (1 << POKEY_CHAN3) | (1 << POKEY_CHAN4)); POKEYSND_Update(POKEY_OFFSET_AUDCTL, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDF1: POKEY_AUDF[POKEY_CHAN1] = byte; Update_Counter((POKEY_AUDCTL[0] & POKEY_CH1_CH2) ? ((1 << POKEY_CHAN2) | (1 << POKEY_CHAN1)) : (1 << POKEY_CHAN1)); POKEYSND_Update(POKEY_OFFSET_AUDF1, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDF2: POKEY_AUDF[POKEY_CHAN2] = byte; Update_Counter(1 << POKEY_CHAN2); POKEYSND_Update(POKEY_OFFSET_AUDF2, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDF3: POKEY_AUDF[POKEY_CHAN3] = byte; Update_Counter((POKEY_AUDCTL[0] & POKEY_CH3_CH4) ? ((1 << POKEY_CHAN4) | (1 << POKEY_CHAN3)) : (1 << POKEY_CHAN3)); POKEYSND_Update(POKEY_OFFSET_AUDF3, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_AUDF4: POKEY_AUDF[POKEY_CHAN4] = byte; Update_Counter(1 << POKEY_CHAN4); POKEYSND_Update(POKEY_OFFSET_AUDF4, byte, 0, SOUND_GAIN); break; case POKEY_OFFSET_IRQEN: POKEY_IRQEN = byte; #ifdef DEBUG1 printf("WR: IRQEN = %x, PC = %x\n", POKEY_IRQEN, PC); #endif POKEY_IRQST |= ~byte & 0xf7; /* Reset disabled IRQs except XMTDONE */ #if SKIP if ((~POKEY_IRQST & POKEY_IRQEN) == 0 && PBI_IRQ == 0 && PIA_IRQ == 0) #else if ((~POKEY_IRQST & POKEY_IRQEN) == 0) #endif { CPU_IRQ = 0; } else { CPU_GenerateIRQ(); } break; case POKEY_OFFSET_SKRES: POKEY_SKSTAT |= 0xe0; break; case POKEY_OFFSET_POTGO: if (!(POKEY_SKCTL & 4)) pot_scanline = 0; /* slow pot mode */ break; case POKEY_OFFSET_SEROUT: #ifdef VOICEBOX VOICEBOX_SEROUTPutByte(byte); #endif #if SKIP if ((POKEY_SKCTL & 0x70) == 0x20 && POKEY_siocheck()) SIO_PutByte(byte); #endif /* check if cassette 2-tone mode has been enabled */ if ((POKEY_SKCTL & 0x08) == 0x00) { /* intelligent device */ #if SKIP POKEY_DELAYED_SEROUT_IRQ = SIO_SEROUT_INTERVAL; #endif POKEY_IRQST |= 0x08; #if SKIP POKEY_DELAYED_XMTDONE_IRQ = SIO_XMTDONE_INTERVAL; #endif } else { /* cassette */ /* some savers patch the cassette baud rate, so we evaluate it here */ /* scanlines per second*10 bit*audiofrequency/(1.79 MHz/2) */ POKEY_DELAYED_SEROUT_IRQ = 312*50*10*(POKEY_AUDF[POKEY_CHAN3] + POKEY_AUDF[POKEY_CHAN4]*0x100)/895000; /* safety check */ if (POKEY_DELAYED_SEROUT_IRQ >= 3) { POKEY_IRQST |= 0x08; POKEY_DELAYED_XMTDONE_IRQ = 2*POKEY_DELAYED_SEROUT_IRQ - 2; } else { POKEY_DELAYED_SEROUT_IRQ = 0; POKEY_DELAYED_XMTDONE_IRQ = 0; } }; #ifdef SERIO_SOUND POKEYSND_UpdateSerio(1, byte); #endif break; case POKEY_OFFSET_STIMER: POKEY_DivNIRQ[POKEY_CHAN1] = POKEY_DivNMax[POKEY_CHAN1]; POKEY_DivNIRQ[POKEY_CHAN2] = POKEY_DivNMax[POKEY_CHAN2]; POKEY_DivNIRQ[POKEY_CHAN4] = POKEY_DivNMax[POKEY_CHAN4]; POKEYSND_Update(POKEY_OFFSET_STIMER, byte, 0, SOUND_GAIN); #ifdef DEBUG1 printf("WR: STIMER = %x\n", byte); #endif break; case POKEY_OFFSET_SKCTL: #ifdef VOICEBOX VOICEBOX_SKCTLPutByte(byte); #endif POKEY_SKCTL = byte; POKEYSND_Update(POKEY_OFFSET_SKCTL, byte, 0, SOUND_GAIN); if (byte & 4) pot_scanline = 228; /* fast pot mode - return results immediately */ if ((byte & 0x03) == 0) { /* POKEY reset. */ /* Stop serial IO. */ POKEY_DELAYED_SERIN_IRQ = 0; POKEY_DELAYED_SEROUT_IRQ = 0; POKEY_DELAYED_XMTDONE_IRQ = 0; #if SKIP CASSETTE_ResetPOKEY(); #endif /* TODO other registers should also be reset. */ } break; #ifdef STEREO_SOUND case POKEY_OFFSET_AUDC1 + POKEY_OFFSET_POKEY2: POKEY_AUDC[POKEY_CHAN1 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC1, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDC2 + POKEY_OFFSET_POKEY2: POKEY_AUDC[POKEY_CHAN2 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC2, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDC3 + POKEY_OFFSET_POKEY2: POKEY_AUDC[POKEY_CHAN3 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC3, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDC4 + POKEY_OFFSET_POKEY2: POKEY_AUDC[POKEY_CHAN4 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDC4, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDCTL + POKEY_OFFSET_POKEY2: POKEY_AUDCTL[1] = byte; /* determine the base multiplier for the 'div by n' calculations */ if (byte & POKEY_CLOCK_15) POKEY_Base_mult[1] = POKEY_DIV_15; else POKEY_Base_mult[1] = POKEY_DIV_64; POKEYSND_Update(POKEY_OFFSET_AUDCTL, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDF1 + POKEY_OFFSET_POKEY2: POKEY_AUDF[POKEY_CHAN1 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDF1, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDF2 + POKEY_OFFSET_POKEY2: POKEY_AUDF[POKEY_CHAN2 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDF2, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDF3 + POKEY_OFFSET_POKEY2: POKEY_AUDF[POKEY_CHAN3 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDF3, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_AUDF4 + POKEY_OFFSET_POKEY2: POKEY_AUDF[POKEY_CHAN4 + POKEY_CHIP2] = byte; POKEYSND_Update(POKEY_OFFSET_AUDF4, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_STIMER + POKEY_OFFSET_POKEY2: POKEYSND_Update(POKEY_OFFSET_STIMER, byte, 1, SOUND_GAIN); break; case POKEY_OFFSET_SKCTL + POKEY_OFFSET_POKEY2: POKEYSND_Update(POKEY_OFFSET_SKCTL, byte, 1, SOUND_GAIN); break; #endif } } int POKEY_Initialise(void) { int i; ULONG reg; /* Initialise Serial Port Interrupts */ POKEY_DELAYED_SERIN_IRQ = 0; POKEY_DELAYED_SEROUT_IRQ = 0; POKEY_DELAYED_XMTDONE_IRQ = 0; POKEY_KBCODE = 0xff; POKEY_SERIN = 0x00; /* or 0xff ? */ POKEY_IRQST = 0xff; POKEY_IRQEN = 0x00; POKEY_SKSTAT = 0xef; POKEY_SKCTL = 0x00; for (i = 0; i < (POKEY_MAXPOKEYS * 4); i++) { POKEY_AUDC[i] = 0; POKEY_AUDF[i] = 0; } for (i = 0; i < POKEY_MAXPOKEYS; i++) { POKEY_AUDCTL[i] = 0; POKEY_Base_mult[i] = POKEY_DIV_64; } for (i = 0; i < 4; i++) POKEY_DivNIRQ[i] = POKEY_DivNMax[i] = 0; pot_scanline = 0; #if SKIP /* initialise poly9_lookup */ reg = 0x1ff; for (i = 0; i < POKEY_POLY9_SIZE; i++) { reg = ((((reg >> 5) ^ reg) & 1) << 8) + (reg >> 1); POKEY_poly9_lookup[i] = (UBYTE) reg; } /* initialise poly17_lookup */ reg = 0x1ffff; for (i = 0; i < POKEY_POLY17_SIZE; i++) { reg = ((((reg >> 5) ^ reg) & 0xff) << 9) + (reg >> 8); POKEY_poly17_lookup[i] = (UBYTE) (reg >> 1); } #endif #ifndef BASIC #if SKIP if (INPUT_Playingback()) { random_scanline_counter = INPUT_PlaybackInt(); } else #endif #endif { random_scanline_counter = #ifdef HAVE_WINDOWS_H GetTickCount() % POKEY_POLY17_SIZE; #elif defined(HAVE_TIME) time(NULL) % POKEY_POLY17_SIZE; #else 0; #endif } #ifndef BASIC #if SKIP if (INPUT_Recording()) { INPUT_RecordInt(random_scanline_counter); } #endif #endif return TRUE; } void POKEY_Frame(void) { random_scanline_counter %= (POKEY_AUDCTL[0] & POKEY_POLY9) ? POKEY_POLY9_SIZE : POKEY_POLY17_SIZE; } /*************************************************************************** ** Generate POKEY Timer IRQs if required ** ** called on a per-scanline basis, not very precise, but good enough ** ** for most applications ** ***************************************************************************/ void POKEY_Scanline(void) { #ifdef POKEY_UPDATE pokey_update(); #endif #ifdef VOL_ONLY_SOUND POKEYSND_UpdateVolOnly(); #endif #ifndef BASIC INPUT_Scanline(); /* Handle Amiga and ST mice. */ /* It's not a part of POKEY emulation, */ /* but it looks to be the best place to put it. */ #endif /* on nonpatched i/o-operation, enable the cassette timing */ #if SKIP if (!ESC_enable_sio_patch) { if (CASSETTE_AddScanLine()) POKEY_DELAYED_SERIN_IRQ = 1; } #endif if ((POKEY_SKCTL & 0x03) == 0) /* Don't process timers when POKEY is in reset mode. */ return; if (pot_scanline < 228) pot_scanline++; random_scanline_counter += ANTIC_LINE_C; if (POKEY_DELAYED_SERIN_IRQ > 0) { if (--POKEY_DELAYED_SERIN_IRQ == 0) { /* Load a byte to SERIN - even when the IRQ is disabled. */ #if SKIP POKEY_SERIN = SIO_GetByte(); #else // POKEY_SERIN = 0; #endif if (POKEY_IRQEN & 0x20) { if (POKEY_IRQST & 0x20) { POKEY_IRQST &= 0xdf; #ifdef DEBUG2 printf("SERIO: SERIN Interrupt triggered, bytevalue %02x\n", POKEY_SERIN); #endif } else { POKEY_SKSTAT &= 0xdf; #ifdef DEBUG2 printf("SERIO: SERIN Interrupt triggered, bytevalue %02x\n", POKEY_SERIN); #endif } CPU_GenerateIRQ(); } #ifdef DEBUG2 else { printf("SERIO: SERIN Interrupt missed, bytevalue %02x\n", POKEY_SERIN); } #endif } } if (POKEY_DELAYED_SEROUT_IRQ > 0) { if (--POKEY_DELAYED_SEROUT_IRQ == 0) { if (POKEY_IRQEN & 0x10) { #ifdef DEBUG2 printf("SERIO: SEROUT Interrupt triggered\n"); #endif POKEY_IRQST &= 0xef; CPU_GenerateIRQ(); } #ifdef DEBUG2 else { printf("SERIO: SEROUT Interrupt missed\n"); } #endif } } if (POKEY_DELAYED_XMTDONE_IRQ > 0) if (--POKEY_DELAYED_XMTDONE_IRQ == 0) { POKEY_IRQST &= 0xf7; if (POKEY_IRQEN & 0x08) { #ifdef DEBUG2 printf("SERIO: XMTDONE Interrupt triggered\n"); #endif CPU_GenerateIRQ(); } #ifdef DEBUG2 else printf("SERIO: XMTDONE Interrupt missed\n"); #endif } if ((POKEY_DivNIRQ[POKEY_CHAN1] -= ANTIC_LINE_C) < 0 ) { POKEY_DivNIRQ[POKEY_CHAN1] += POKEY_DivNMax[POKEY_CHAN1]; if (POKEY_IRQEN & 0x01) { POKEY_IRQST &= 0xfe; CPU_GenerateIRQ(); } } if ((POKEY_DivNIRQ[POKEY_CHAN2] -= ANTIC_LINE_C) < 0 ) { POKEY_DivNIRQ[POKEY_CHAN2] += POKEY_DivNMax[POKEY_CHAN2]; if (POKEY_IRQEN & 0x02) { POKEY_IRQST &= 0xfd; CPU_GenerateIRQ(); } } if ((POKEY_DivNIRQ[POKEY_CHAN4] -= ANTIC_LINE_C) < 0 ) { POKEY_DivNIRQ[POKEY_CHAN4] += POKEY_DivNMax[POKEY_CHAN4]; if (POKEY_IRQEN & 0x04) { POKEY_IRQST &= 0xfb; CPU_GenerateIRQ(); } } } /*****************************************************************************/ /* Module: Update_Counter() */ /* Purpose: To process the latest control values stored in the AUDF, AUDC, */ /* and AUDCTL registers. It pre-calculates as much information as */ /* possible for better performance. This routine has been added */ /* here again as I need the precise frequency for the pokey timers */ /* again. The pokey emulation is therefore somewhat sub-optimal */ /* since the actual pokey emulation should grab the frequency values */ /* directly from here instead of calculating them again. */ /* */ /* Author: Ron Fries,Thomas Richter */ /* Date: March 27, 1998 */ /* */ /* Inputs: chan_mask: Channel mask, one bit per channel. */ /* The channels that need to be updated */ /* */ /* Outputs: Adjusts local globals - no return value */ /* */ /*****************************************************************************/ static void Update_Counter(int chan_mask) { /************************************************************/ /* As defined in the manual, the exact Div_n_cnt values are */ /* different depending on the frequency and resolution: */ /* 64 kHz or 15 kHz - AUDF + 1 */ /* 1 MHz, 8-bit - AUDF + 4 */ /* 1 MHz, 16-bit - AUDF[CHAN1]+256*AUDF[CHAN2] + 7 */ /************************************************************/ /* only reset the channels that have changed */ if (chan_mask & (1 << POKEY_CHAN1)) { /* process channel 1 frequency */ if (POKEY_AUDCTL[0] & POKEY_CH1_179) POKEY_DivNMax[POKEY_CHAN1] = POKEY_AUDF[POKEY_CHAN1] + 4; else POKEY_DivNMax[POKEY_CHAN1] = (POKEY_AUDF[POKEY_CHAN1] + 1) * POKEY_Base_mult[0]; if (POKEY_DivNMax[POKEY_CHAN1] < ANTIC_LINE_C) POKEY_DivNMax[POKEY_CHAN1] = ANTIC_LINE_C; } if (chan_mask & (1 << POKEY_CHAN2)) { /* process channel 2 frequency */ if (POKEY_AUDCTL[0] & POKEY_CH1_CH2) { if (POKEY_AUDCTL[0] & POKEY_CH1_179) POKEY_DivNMax[POKEY_CHAN2] = POKEY_AUDF[POKEY_CHAN2] * 256 + POKEY_AUDF[POKEY_CHAN1] + 7; else POKEY_DivNMax[POKEY_CHAN2] = (POKEY_AUDF[POKEY_CHAN2] * 256 + POKEY_AUDF[POKEY_CHAN1] + 1) * POKEY_Base_mult[0]; } else POKEY_DivNMax[POKEY_CHAN2] = (POKEY_AUDF[POKEY_CHAN2] + 1) * POKEY_Base_mult[0]; if (POKEY_DivNMax[POKEY_CHAN2] < ANTIC_LINE_C) POKEY_DivNMax[POKEY_CHAN2] = ANTIC_LINE_C; } if (chan_mask & (1 << POKEY_CHAN4)) { /* process channel 4 frequency */ if (POKEY_AUDCTL[0] & POKEY_CH3_CH4) { if (POKEY_AUDCTL[0] & POKEY_CH3_179) POKEY_DivNMax[POKEY_CHAN4] = POKEY_AUDF[POKEY_CHAN4] * 256 + POKEY_AUDF[POKEY_CHAN3] + 7; else POKEY_DivNMax[POKEY_CHAN4] = (POKEY_AUDF[POKEY_CHAN4] * 256 + POKEY_AUDF[POKEY_CHAN3] + 1) * POKEY_Base_mult[0]; } else POKEY_DivNMax[POKEY_CHAN4] = (POKEY_AUDF[POKEY_CHAN4] + 1) * POKEY_Base_mult[0]; if (POKEY_DivNMax[POKEY_CHAN4] < ANTIC_LINE_C) POKEY_DivNMax[POKEY_CHAN4] = ANTIC_LINE_C; } } #ifndef BASIC void POKEY_StateSave(void) { int shift_key = 0; int keypressed = 0; StateSav_SaveUBYTE(&POKEY_KBCODE, 1); StateSav_SaveUBYTE(&POKEY_IRQST, 1); StateSav_SaveUBYTE(&POKEY_IRQEN, 1); StateSav_SaveUBYTE(&POKEY_SKCTL, 1); StateSav_SaveINT(&shift_key, 1); StateSav_SaveINT(&keypressed, 1); StateSav_SaveINT(&POKEY_DELAYED_SERIN_IRQ, 1); StateSav_SaveINT(&POKEY_DELAYED_SEROUT_IRQ, 1); StateSav_SaveINT(&POKEY_DELAYED_XMTDONE_IRQ, 1); StateSav_SaveUBYTE(&POKEY_AUDF[0], 4); StateSav_SaveUBYTE(&POKEY_AUDC[0], 4); StateSav_SaveUBYTE(&POKEY_AUDCTL[0], 1); StateSav_SaveINT(&POKEY_DivNIRQ[0], 4); StateSav_SaveINT(&POKEY_DivNMax[0], 4); StateSav_SaveINT(&POKEY_Base_mult[0], 1); } void POKEY_StateRead(void) { int i; int shift_key; int keypressed; StateSav_ReadUBYTE(&POKEY_KBCODE, 1); StateSav_ReadUBYTE(&POKEY_IRQST, 1); StateSav_ReadUBYTE(&POKEY_IRQEN, 1); StateSav_ReadUBYTE(&POKEY_SKCTL, 1); StateSav_ReadINT(&shift_key, 1); StateSav_ReadINT(&keypressed, 1); StateSav_ReadINT(&POKEY_DELAYED_SERIN_IRQ, 1); StateSav_ReadINT(&POKEY_DELAYED_SEROUT_IRQ, 1); StateSav_ReadINT(&POKEY_DELAYED_XMTDONE_IRQ, 1); StateSav_ReadUBYTE(&POKEY_AUDF[0], 4); StateSav_ReadUBYTE(&POKEY_AUDC[0], 4); StateSav_ReadUBYTE(&POKEY_AUDCTL[0], 1); for (i = 0; i < 4; i++) { POKEY_PutByte((UWORD) (POKEY_OFFSET_AUDF1 + i * 2), POKEY_AUDF[i]); POKEY_PutByte((UWORD) (POKEY_OFFSET_AUDC1 + i * 2), POKEY_AUDC[i]); } POKEY_PutByte(POKEY_OFFSET_AUDCTL, POKEY_AUDCTL[0]); StateSav_ReadINT(&POKEY_DivNIRQ[0], 4); StateSav_ReadINT(&POKEY_DivNMax[0], 4); StateSav_ReadINT(&POKEY_Base_mult[0], 1); } #endif