2021-12-04 01:19:54 -05:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010-2019 Fabio Cavallo (aka FHorse)
|
|
|
|
*
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
* This program 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 this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef CPU_INLINE_H_
|
|
|
|
#define CPU_INLINE_H_
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "apu.h"
|
2022-04-03 23:37:16 -04:00
|
|
|
#include "fds.h"
|
2021-12-04 01:19:54 -05:00
|
|
|
|
|
|
|
#define mod_cycles_op(op, vl) cpu.cycles op vl
|
|
|
|
#define r2006_during_rendering()\
|
|
|
|
if (!ppu.vblank && r2001.visible && (ppu.frame_y > ppu_sclines.vint) &&\
|
|
|
|
(ppu.screen_y < SCR_LINES)) {\
|
|
|
|
_r2006_during_rendering()\
|
|
|
|
} else {\
|
|
|
|
r2006.value += r2000.r2006_inc;\
|
|
|
|
}
|
|
|
|
#define _r2006_during_rendering()\
|
|
|
|
r2006_inc()\
|
|
|
|
if ((r2006.value & 0x1F) == 0x1F) {\
|
|
|
|
r2006.value ^= 0x41F;\
|
|
|
|
} else {\
|
|
|
|
r2006.value++;\
|
|
|
|
}
|
|
|
|
|
2022-01-08 17:44:17 -05:00
|
|
|
INLINE static void apu_wr_reg(struct NESAPU* a, WORD address, BYTE value) {
|
2021-12-04 01:19:54 -05:00
|
|
|
if (!(address & 0x0010)) {
|
|
|
|
/* -------------------- square 1 --------------------*/
|
|
|
|
if (address <= 0x4003) {
|
|
|
|
if (address == 0x4000) {
|
2022-01-08 17:44:17 -05:00
|
|
|
square_reg0(a->S1);
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4001) {
|
2022-01-08 17:44:17 -05:00
|
|
|
square_reg1(a->S1);
|
|
|
|
sweep_silence(a->S1)
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4002) {
|
2022-01-08 17:44:17 -05:00
|
|
|
square_reg2(a->S1);
|
|
|
|
sweep_silence(a->S1)
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4003) {
|
2022-04-06 01:34:12 -04:00
|
|
|
square_reg3(a->S1,a->apu.length_clocked);
|
2022-01-08 17:44:17 -05:00
|
|
|
sweep_silence(a->S1)
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* -------------------- square 2 --------------------*/
|
|
|
|
if (address <= 0x4007) {
|
|
|
|
if (address == 0x4004) {
|
2022-01-08 17:44:17 -05:00
|
|
|
square_reg0(a->S2);
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4005) {
|
2022-01-08 17:44:17 -05:00
|
|
|
square_reg1(a->S2);
|
|
|
|
sweep_silence(a->S2)
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4006) {
|
2022-01-08 17:44:17 -05:00
|
|
|
square_reg2(a->S2);
|
|
|
|
sweep_silence(a->S2)
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4007) {
|
2022-04-06 01:34:12 -04:00
|
|
|
square_reg3(a->S2,a->apu.length_clocked);
|
2022-01-08 17:44:17 -05:00
|
|
|
sweep_silence(a->S2)
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* -------------------- triangle --------------------*/
|
|
|
|
if (address <= 0x400B) {
|
|
|
|
if (address == 0x4008) {
|
|
|
|
/* length counter */
|
|
|
|
/*
|
|
|
|
* il triangle ha una posizione diversa per il
|
|
|
|
* flag LCHalt.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
a->TR.length.halt = value & 0x80;
|
2021-12-04 01:19:54 -05:00
|
|
|
/* linear counter */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->TR.linear.reload = value & 0x7F;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x400A) {
|
|
|
|
/* timer (low 8 bits) */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->TR.timer = (a->TR.timer & 0x0700) | value;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x400B) {
|
|
|
|
/* length counter */
|
|
|
|
/*
|
|
|
|
* se non disabilitato, una scrittura in
|
|
|
|
* questo registro, carica immediatamente il
|
|
|
|
* length counter del canale, tranne nel caso
|
|
|
|
* in cui la scrittura avvenga nello stesso
|
|
|
|
* momento del clock di un length counter e
|
|
|
|
* con il length diverso da zero.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
if (a->TR.length.enabled && !(a->apu.length_clocked && a->TR.length.value)) {
|
|
|
|
a->TR.length.value = length_table[value >> 3];
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
|
|
|
/* timer (high 3 bits) */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->TR.timer = (a->TR.timer & 0x00FF) | ((value & 0x07) << 8);
|
2021-12-04 01:19:54 -05:00
|
|
|
/*
|
|
|
|
* scrivendo in questo registro si setta
|
|
|
|
* automaticamente l'halt flag del triangle.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
a->TR.linear.halt = TRUE;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* --------------------- noise ----------------------*/
|
|
|
|
if (address <= 0x400F) {
|
|
|
|
if (address == 0x400C) {
|
2022-01-08 17:44:17 -05:00
|
|
|
a->NS.length.halt = value & 0x20;
|
2021-12-04 01:19:54 -05:00
|
|
|
/* envelope */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->NS.envelope.constant_volume = value & 0x10;
|
|
|
|
a->NS.envelope.divider = value & 0x0F;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x400E) {
|
2022-01-08 17:44:17 -05:00
|
|
|
a->NS.mode = value & 0x80;
|
|
|
|
a->NS.timer = value & 0x0F;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x400F) {
|
|
|
|
/*
|
|
|
|
* se non disabilitato, una scrittura in
|
|
|
|
* questo registro, carica immediatamente il
|
|
|
|
* length counter del canale, tranne nel caso
|
|
|
|
* in cui la scrittura avvenga nello stesso
|
|
|
|
* momento del clock di un length counter e
|
|
|
|
* con il length diverso da zero.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
if (a->NS.length.enabled && !(a->apu.length_clocked && a->NS.length.value)) {
|
|
|
|
a->NS.length.value = length_table[value >> 3];
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
|
|
|
/* envelope */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->NS.envelope.enabled = TRUE;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
/* ---------------------- DMC -----------------------*/
|
|
|
|
if (address <= 0x4013) {
|
|
|
|
if (address == 0x4010) {
|
2022-01-08 17:44:17 -05:00
|
|
|
a->DMC.irq_enabled = value & 0x80;
|
2021-12-04 01:19:54 -05:00
|
|
|
/* se l'irq viene disabilitato allora... */
|
2022-01-08 17:44:17 -05:00
|
|
|
if (!a->DMC.irq_enabled) {
|
2021-12-04 01:19:54 -05:00
|
|
|
/* ...azzero l'interrupt flag del DMC */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->r4015.value &= 0x7F;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
2022-01-08 17:44:17 -05:00
|
|
|
a->DMC.loop = value & 0x40;
|
|
|
|
a->DMC.rate_index = value & 0x0F;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4011) {
|
2022-01-08 17:44:17 -05:00
|
|
|
BYTE save = a->DMC.counter;
|
2021-12-04 01:19:54 -05:00
|
|
|
|
|
|
|
value &= 0x7F;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* questa lo faccio perche' in alcuni giochi come Batman,
|
|
|
|
* Ninja Gaiden 3, Castlevania II ed altri, producono
|
|
|
|
* un popping del suono fastidioso;
|
|
|
|
* from Fceu doc:
|
|
|
|
* Why do some games make a popping sound (Batman, Ninja Gaiden 3,
|
|
|
|
* Castlevania II etc.)? These games do a very crude drum imitation
|
|
|
|
* by causing a large jump in the output level for a short period of
|
|
|
|
* time via the register at $4011. The analog filters on a real
|
|
|
|
* Famicom make it sound decent(better). I have not completely
|
|
|
|
* emulated these filters.
|
|
|
|
* (Xodnizel)
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
if (a->r4011.frames > 1) {
|
|
|
|
a->r4011.output = (value - save) >> 3;
|
|
|
|
a->DMC.counter = a->DMC.output = save + a->r4011.output;
|
2021-12-04 01:19:54 -05:00
|
|
|
} else {
|
2022-01-08 17:44:17 -05:00
|
|
|
a->DMC.counter = a->DMC.output = value;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
2022-01-08 17:44:17 -05:00
|
|
|
a->apu.clocked = TRUE;
|
2021-12-04 01:19:54 -05:00
|
|
|
|
2022-01-08 17:44:17 -05:00
|
|
|
a->r4011.cycles = a->r4011.frames = 0;
|
|
|
|
a->r4011.value = value;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4012) {
|
2022-01-08 17:44:17 -05:00
|
|
|
a->DMC.address_start = (value << 6) | 0xC000;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (address == 0x4013) {
|
|
|
|
/* sample length */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->DMC.length = (value << 4) | 0x01;
|
2021-12-04 01:19:54 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* --------------------------------------------------*/
|
|
|
|
if (address == 0x4015) {
|
|
|
|
/*
|
|
|
|
* 76543210
|
|
|
|
* || |||||
|
|
|
|
* || ||||+- Pulse channel 1's length counter enabled flag
|
|
|
|
* || |||+-- Pulse channel 2's length counter enabled flag
|
|
|
|
* || ||+--- Triangle channel's length counter enabled flag
|
|
|
|
* || |+---- Noise channel's length counter enabled flag
|
|
|
|
* || +----- If clear, the DMC's bytes remaining is set to 0,
|
|
|
|
* || otherwise the DMC sample is restarted only if the
|
|
|
|
* || DMC's bytes remaining is 0
|
|
|
|
* |+------- Frame interrupt flag
|
|
|
|
* +-------- DMC interrupt flag
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* dopo la write il bit 7 (dmc flag) deve
|
|
|
|
* essere azzerato mentre lascio inalterati
|
|
|
|
* i bit 5 e 6.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
a->r4015.value = (a->r4015.value & 0x60) | (value & 0x1F);
|
2021-12-04 01:19:54 -05:00
|
|
|
/*
|
|
|
|
* quando il flag di abilitazione del length
|
|
|
|
* counter di ogni canale e' a 0, il counter
|
|
|
|
* dello stesso canale e' immediatamente azzerato.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
if (!(a->S1.length.enabled = a->r4015.value & 0x01)) {
|
|
|
|
a->S1.length.value = 0;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
2022-01-08 17:44:17 -05:00
|
|
|
if (!(a->S2.length.enabled = a->r4015.value & 0x02)) {
|
|
|
|
a->S2.length.value = 0;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
2022-01-08 17:44:17 -05:00
|
|
|
if (!(a->TR.length.enabled = a->r4015.value & 0x04)) {
|
|
|
|
a->TR.length.value = 0;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
2022-01-08 17:44:17 -05:00
|
|
|
if (!(a->NS.length.enabled = a->r4015.value & 0x08)) {
|
|
|
|
a->NS.length.value = 0;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* se il bit 4 e' 0 allora devo azzerare i bytes
|
|
|
|
* rimanenti del DMC, alrimenti devo riavviare
|
|
|
|
* la lettura dei sample DMC solo nel caso che
|
|
|
|
* in cui i bytes rimanenti siano a 0.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
if (!(a->r4015.value & 0x10)) {
|
|
|
|
a->DMC.remain = 0;
|
|
|
|
a->DMC.empty = TRUE;
|
|
|
|
} else if (!a->DMC.remain) {
|
|
|
|
a->DMC.remain = a->DMC.length;
|
|
|
|
a->DMC.address = a->DMC.address_start;
|
2021-12-04 01:19:54 -05:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined (VECCHIA_GESTIONE_JITTER)
|
|
|
|
if (address == 0x4017) {
|
|
|
|
/* APU frame counter */
|
|
|
|
r4017.jitter.value = value;
|
|
|
|
/*
|
|
|
|
* nell'2A03 se la scrittura del $4017 avviene
|
|
|
|
* in un ciclo pari, allora l'effettiva modifica
|
|
|
|
* avverra' nel ciclo successivo.
|
|
|
|
*/
|
|
|
|
if (cpu.odd_cycle) {
|
|
|
|
r4017.jitter.delay = TRUE;
|
|
|
|
} else {
|
|
|
|
r4017.jitter.delay = FALSE;
|
|
|
|
r4017_jitter();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (address == 0x4017) {
|
|
|
|
/* APU frame counter */
|
2022-01-08 17:44:17 -05:00
|
|
|
a->r4017.jitter.value = value;
|
2021-12-04 01:19:54 -05:00
|
|
|
/*
|
|
|
|
* nell'2A03 se la scrittura del $4017 avviene
|
|
|
|
* in un ciclo pari, allora l'effettiva modifica
|
|
|
|
* avverra' nel ciclo successivo.
|
|
|
|
*/
|
2022-01-08 17:44:17 -05:00
|
|
|
if (a->apu.odd_cycle) {
|
|
|
|
a->r4017.jitter.delay = TRUE;
|
2021-12-04 01:19:54 -05:00
|
|
|
} else {
|
2022-01-08 17:44:17 -05:00
|
|
|
a->r4017.jitter.delay = FALSE;
|
2021-12-04 01:19:54 -05:00
|
|
|
r4017_jitter(1)
|
|
|
|
r4017_reset_frame()
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined (DEBUG)
|
|
|
|
//fprintf(stderr, "Alert: Attempt to write APU port %04X\n", address);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
2022-04-03 23:37:16 -04:00
|
|
|
|
|
|
|
INLINE static BYTE fds_wr_mem(struct _fds* fds, WORD address, BYTE value) {
|
|
|
|
if (address == 0x4023) {
|
|
|
|
fds->enabled_snd_reg=value&0x02;
|
|
|
|
}
|
|
|
|
if ((address >= 0x4040) && (address <= 0x408A)) {
|
|
|
|
if (fds->enabled_snd_reg) {
|
|
|
|
if ((address >= 0x4040) && (address <= 0x407F)) {
|
|
|
|
fds->snd.wave.data[address & 0x003F] = value & 0x3F;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4080) {
|
|
|
|
fds->snd.volume.speed = value & 0x3F;
|
|
|
|
fds->snd.volume.increase = value & 0x40;
|
|
|
|
fds->snd.volume.mode = value & 0x80;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4082) {
|
|
|
|
fds->snd.main.frequency = (fds->snd.main.frequency & 0xFF00) | value;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4083) {
|
|
|
|
fds->snd.main.frequency = ((value & 0x0F) << 8) | (fds->snd.main.frequency & 0x00FF);
|
|
|
|
fds->snd.envelope.disabled = value & 0x40;
|
|
|
|
fds->snd.main.silence = value & 0x80;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4084) {
|
|
|
|
fds->snd.sweep.speed = value & 0x3F;
|
|
|
|
fds->snd.sweep.increase = value & 0x40;
|
|
|
|
fds->snd.sweep.mode = value & 0x80;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4085) {
|
|
|
|
fds->snd.sweep.bias = ((SBYTE) (value << 1)) / 2;
|
|
|
|
fds->snd.modulation.index = 0;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4086) {
|
|
|
|
fds->snd.modulation.frequency = (fds->snd.modulation.frequency & 0xFF00) | value;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4087) {
|
|
|
|
fds->snd.modulation.frequency = ((value & 0x0F) << 8)
|
|
|
|
| (fds->snd.modulation.frequency & 0x00FF);
|
|
|
|
fds->snd.modulation.disabled = value & 0x80;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4088) {
|
|
|
|
BYTE i;
|
|
|
|
|
|
|
|
// 0,2,4,6,-8,-6,-4,-2
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
BYTE a = i << 1;
|
|
|
|
|
|
|
|
if (i < 31) {
|
|
|
|
fds->snd.modulation.data[a] = fds->snd.modulation.data[a + 2];
|
|
|
|
} else {
|
|
|
|
BYTE tmp = ((value & 0x03) | (0x3F * (value & 0x04)));
|
|
|
|
fds->snd.modulation.data[a] = (SBYTE) tmp;
|
|
|
|
}
|
|
|
|
fds->snd.modulation.data[a + 1] = fds->snd.modulation.data[a];
|
|
|
|
}
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x4089) {
|
|
|
|
fds->snd.wave.writable = value & 0x80;
|
|
|
|
fds->snd.wave.volume = value & 0x03;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
if (address == 0x408A) {
|
|
|
|
fds->snd.envelope.speed = value;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (FALSE);
|
|
|
|
}
|
2021-12-04 01:19:54 -05:00
|
|
|
#endif /* CPU_INLINE_H_ */
|