prepare for NES platform
This commit is contained in:
parent
901ab6d436
commit
c543553b0f
|
@ -34,6 +34,7 @@ src/engine/platform/sound/sn76496.cpp
|
|||
src/engine/platform/sound/gb/apu.c
|
||||
src/engine/platform/sound/gb/timing.c
|
||||
src/engine/platform/sound/pce_psg.cpp
|
||||
src/engine/platform/sound/nes/apu.c
|
||||
|
||||
src/engine/blip_buf.c
|
||||
src/engine/safeReader.cpp
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// additional modifications by tildearrow for furnace
|
||||
|
||||
#include <string.h>
|
||||
#include "apu.h"
|
||||
|
||||
void apu_tick(BYTE *hwtick) {
|
||||
/* sottraggo il numero di cicli eseguiti */
|
||||
apu.cycles--;
|
||||
/*
|
||||
* questo flag sara' a TRUE solo nel ciclo
|
||||
* in cui viene eseguito il length counter.
|
||||
*/
|
||||
apu.length_clocked = FALSE;
|
||||
/*
|
||||
* se e' settato il delay del $4017, essendo
|
||||
* questo il ciclo successivo, valorizzo il
|
||||
* registro.
|
||||
*/
|
||||
#if defined (VECCHIA_GESTIONE_JITTER)
|
||||
if (r4017.jitter.delay) {
|
||||
r4017.jitter.delay = FALSE;
|
||||
r4017_jitter();
|
||||
}
|
||||
#else
|
||||
if (r4017.jitter.delay) {
|
||||
r4017.jitter.delay = FALSE;
|
||||
r4017_jitter(0)
|
||||
}
|
||||
r4017_reset_frame()
|
||||
#endif
|
||||
|
||||
/* quando apu.cycles e' a 0 devo eseguire uno step */
|
||||
if (!apu.cycles) {
|
||||
switch (apu.step) {
|
||||
case 0:
|
||||
/*
|
||||
* nel mode 1 devo eseguire il
|
||||
* length counter e lo sweep.
|
||||
*/
|
||||
if (apu.mode == APU_48HZ) {
|
||||
length_clock()
|
||||
sweep_clock()
|
||||
}
|
||||
envelope_clock()
|
||||
/* triangle's linear counter */
|
||||
linear_clock()
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(++apu.step);
|
||||
break;
|
||||
case 1:
|
||||
/* nel mode 0 devo eseguire il length counter */
|
||||
if (apu.mode == APU_60HZ) {
|
||||
length_clock()
|
||||
sweep_clock()
|
||||
}
|
||||
envelope_clock()
|
||||
/* triangle's linear counter */
|
||||
linear_clock()
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(++apu.step);
|
||||
break;
|
||||
case 2:
|
||||
/*
|
||||
* nel mode 1 devo eseguire il
|
||||
* length counter e lo sweep.
|
||||
*/
|
||||
if (apu.mode == APU_48HZ) {
|
||||
length_clock()
|
||||
sweep_clock()
|
||||
}
|
||||
envelope_clock()
|
||||
/* triangle's linear counter */
|
||||
linear_clock()
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(++apu.step);
|
||||
break;
|
||||
case 3:
|
||||
/*
|
||||
* gli step 3, 4 e 5 settano il bit 6 del $4015
|
||||
* ma solo nel 4 genero un IRQ.
|
||||
*/
|
||||
if (apu.mode == APU_60HZ) {
|
||||
/*
|
||||
* se e' a 0 il bit 6 del $4017 (interrupt
|
||||
* inhibit flag) allora devo generare un IRQ.
|
||||
*/
|
||||
if (!(r4017.value & 0x40)) {
|
||||
/* setto il bit 6 del $4015 */
|
||||
r4015.value |= 0x40;
|
||||
}
|
||||
} else {
|
||||
/* nel mode 1 devo eseguire l'envelope */
|
||||
envelope_clock()
|
||||
/* triangle's linear counter */
|
||||
linear_clock()
|
||||
}
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(++apu.step);
|
||||
break;
|
||||
case 4:
|
||||
/*
|
||||
* gli step 3, 4 e 5 settano il bit 6 del $4015
|
||||
* ma solo nel 4 genero un IRQ.
|
||||
*/
|
||||
if (apu.mode == APU_60HZ) {
|
||||
length_clock()
|
||||
sweep_clock()
|
||||
envelope_clock()
|
||||
/* triangle's linear counter */
|
||||
linear_clock()
|
||||
/*
|
||||
* se e' a 0 il bit 6 del $4017 (interrupt
|
||||
* inhibit flag) allora devo generare un IRQ.
|
||||
*/
|
||||
if (!(r4017.value & 0x40)) {
|
||||
/* setto il bit 6 del $4015 */
|
||||
r4015.value |= 0x40;
|
||||
}
|
||||
}
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(++apu.step);
|
||||
break;
|
||||
case 5:
|
||||
/*
|
||||
* gli step 3, 4 e 5 settano il bit 6 del $4015
|
||||
* ma solo nel 4 genero un IRQ.
|
||||
*/
|
||||
if (apu.mode == APU_60HZ) {
|
||||
/*
|
||||
* se e' a 0 il bit 6 del $4017 (interrupt
|
||||
* inhibit flag) allora devo generare un IRQ.
|
||||
*/
|
||||
if (!(r4017.value & 0x40)) {
|
||||
/* setto il bit 6 del $4015 */
|
||||
r4015.value |= 0x40;
|
||||
}
|
||||
apu.step++;
|
||||
} else {
|
||||
/* nel mode 1 devo ricominciare il ciclo */
|
||||
apu.step = 0;
|
||||
}
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(apu.step);
|
||||
break;
|
||||
case 6:
|
||||
/* da qui ci passo solo nel mode 0 */
|
||||
envelope_clock()
|
||||
/* triangle's linear counter */
|
||||
linear_clock()
|
||||
/* questo e' il passaggio finale del mode 0 */
|
||||
apu.step = 1;
|
||||
/* passo al prossimo step */
|
||||
apu_change_step(apu.step);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* eseguo un ticket per ogni canale
|
||||
* valorizzandone l'output.
|
||||
*/
|
||||
square_tick(S1, 0, apu)
|
||||
square_tick(S2, 0, apu)
|
||||
triangle_tick()
|
||||
noise_tick()
|
||||
dmc_tick()
|
||||
|
||||
// TODO
|
||||
/*if (snd_apu_tick) {
|
||||
snd_apu_tick();
|
||||
}*/
|
||||
|
||||
r4011.cycles++;
|
||||
}
|
||||
void apu_turn_on(void) {
|
||||
memset(&apu, 0x00, sizeof(apu));
|
||||
memset(&r4015, 0x00, sizeof(r4015));
|
||||
memset(&r4017, 0x00, sizeof(r4017));
|
||||
/* azzero tutte le variabili interne dei canali */
|
||||
memset(&S1, 0x00, sizeof(S1));
|
||||
memset(&S2, 0x00, sizeof(S2));
|
||||
memset(&TR, 0x00, sizeof(TR));
|
||||
memset(&NS, 0x00, sizeof(NS));
|
||||
memset(&DMC, 0x00, sizeof(DMC));
|
||||
/* al reset e' sempre settato a 60Hz */
|
||||
apu.mode = APU_60HZ;
|
||||
apu.type = 0;
|
||||
apu_change_step(apu.step);
|
||||
/* valori iniziali dei vari canali */
|
||||
S1.frequency = 1;
|
||||
S1.sweep.delay = 1;
|
||||
S1.sweep.divider = 1;
|
||||
S2.frequency = 1;
|
||||
S2.sweep.delay = 1;
|
||||
S2.sweep.divider = 1;
|
||||
TR.frequency = 1;
|
||||
TR.sequencer = 0;
|
||||
NS.frequency = 1;
|
||||
NS.shift = 1;
|
||||
DMC.frequency = 1;
|
||||
DMC.empty = TRUE;
|
||||
DMC.silence = TRUE;
|
||||
DMC.counter_out = 8;
|
||||
// sembra che l'address del DMC al power on dia valorizzato a 0xC000
|
||||
// e la lunghezza del sample sia settato a 1 byte.
|
||||
// http://forums.nesdev.com/viewtopic.php?f=3&t=18278
|
||||
DMC.length = 1;
|
||||
DMC.address_start = 0xC000;
|
||||
}
|
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// additional modifications by tildearrow for furnace
|
||||
|
||||
#ifndef APU_H_
|
||||
#define APU_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
enum dmc_types_of_dma { DMC_NORMAL, DMC_CPU_WRITE, DMC_R4014, DMC_NNL_DMA };
|
||||
enum apu_channels { APU_S1, APU_S2, APU_TR, APU_NS, APU_DMC, APU_EXTRA, APU_MASTER };
|
||||
enum apu_mode { APU_60HZ, APU_48HZ };
|
||||
|
||||
int cpu_cycles;
|
||||
int cpu_opcode_cycle;
|
||||
unsigned char* addrSpace;
|
||||
|
||||
#define apu_pre_amp 1.4f
|
||||
|
||||
/* length counter */
|
||||
#define length_run(channel)\
|
||||
/*\
|
||||
* se non e' settato il flag halt e il length\
|
||||
* counter non e' 0 allora devo decrementarlo.\
|
||||
*/\
|
||||
if (!channel.length.halt && channel.length.value) {\
|
||||
channel.length.value--;\
|
||||
}
|
||||
#define length_clock()\
|
||||
apu.length_clocked = TRUE;\
|
||||
length_run(S1)\
|
||||
length_run(S2)\
|
||||
length_run(TR)\
|
||||
length_run(NS)
|
||||
/* envelope */
|
||||
#define envelope_run(channel)\
|
||||
if (channel.envelope.enabled) {\
|
||||
channel.envelope.enabled = FALSE;\
|
||||
channel.envelope.counter = 15;\
|
||||
channel.envelope.delay = (channel.envelope.divider + 1);\
|
||||
} else if (!(--channel.envelope.delay)) {\
|
||||
channel.envelope.delay = (channel.envelope.divider + 1);\
|
||||
if (channel.envelope.counter | channel.length.halt) {\
|
||||
channel.envelope.counter = (channel.envelope.counter - 1) & 0x0F;\
|
||||
}\
|
||||
}
|
||||
#define envelope_volume(channel)\
|
||||
/* setto il volume */\
|
||||
if (!channel.length.value) {\
|
||||
channel.volume = 0;\
|
||||
} else if (channel.envelope.constant_volume) {\
|
||||
channel.volume = channel.envelope.divider;\
|
||||
} else {\
|
||||
channel.volume = channel.envelope.counter;\
|
||||
}
|
||||
#define envelope_clock()\
|
||||
envelope_run(S1)\
|
||||
envelope_run(S2)\
|
||||
envelope_run(NS)
|
||||
/* sweep */
|
||||
#define sweep_run(channel, negative_adjust)\
|
||||
if (!(--channel.sweep.delay)) {\
|
||||
channel.sweep.delay = (channel.sweep.divider + 1);\
|
||||
if (channel.sweep.enabled && channel.sweep.shift && (channel.timer >= 8)) {\
|
||||
SWORD offset = channel.timer >> channel.sweep.shift;\
|
||||
if (channel.sweep.negate) {\
|
||||
channel.timer += ((SWORD) negative_adjust - offset);\
|
||||
} else if ((channel.timer + offset) <= 0x800) {\
|
||||
channel.timer += offset;\
|
||||
}\
|
||||
}\
|
||||
sweep_silence(channel)\
|
||||
}\
|
||||
if (channel.sweep.reload) {\
|
||||
channel.sweep.reload = FALSE;\
|
||||
channel.sweep.delay = (channel.sweep.divider + 1);\
|
||||
}
|
||||
#define sweep_silence(channel)\
|
||||
{\
|
||||
WORD offset = channel.timer >> channel.sweep.shift;\
|
||||
channel.sweep.silence = FALSE;\
|
||||
if ((channel.timer <= 8) || (!channel.sweep.negate && ((channel.timer + offset) >= 0x800))) {\
|
||||
channel.sweep.silence = TRUE;\
|
||||
}\
|
||||
}
|
||||
#define sweep_clock()\
|
||||
sweep_run(S1, -1)\
|
||||
sweep_run(S2, 0)
|
||||
/* linear counter */
|
||||
#define linear_clock()\
|
||||
if (TR.linear.halt) {\
|
||||
TR.linear.value = TR.linear.reload;\
|
||||
} else if (TR.linear.value) {\
|
||||
TR.linear.value--;\
|
||||
}\
|
||||
if (!TR.length.halt) {\
|
||||
TR.linear.halt = FALSE;\
|
||||
}
|
||||
/* output */
|
||||
#define square_output(square, swap)\
|
||||
{\
|
||||
envelope_volume(square)\
|
||||
if (square.sweep.silence) {\
|
||||
square.output = 0;\
|
||||
} else {\
|
||||
square.output = square_duty[swap][square.duty][square.sequencer] * square.volume;\
|
||||
}\
|
||||
}
|
||||
#define triangle_output()\
|
||||
/*\
|
||||
* ai 2 cicli piu' bassi del timer, la frequenza\
|
||||
* risultante e' troppo alta (oltre i 20 kHz,\
|
||||
* quindi non udibile), percio' la taglio.\
|
||||
*/\
|
||||
TR.output = triangle_duty[TR.sequencer];\
|
||||
if (TR.timer < 2) {\
|
||||
TR.output = triangle_duty[8];\
|
||||
}
|
||||
#define noise_output()\
|
||||
envelope_volume(NS)\
|
||||
NS.output = 0;\
|
||||
if (NS.length.value && !(NS.shift & 0x0001)) {\
|
||||
NS.output = NS.volume;\
|
||||
}
|
||||
#define dmc_output()\
|
||||
DMC.output = DMC.counter & 0x7F
|
||||
/* tick */
|
||||
#define square_tick(square, swap, type)\
|
||||
if (!(--square.frequency)) {\
|
||||
square_output(square, swap)\
|
||||
square.frequency = (square.timer + 1) << 1;\
|
||||
square.sequencer = (square.sequencer + 1) & 0x07;\
|
||||
type.clocked = TRUE;\
|
||||
}
|
||||
#define triangle_tick()\
|
||||
if (!(--TR.frequency)) {\
|
||||
TR.frequency = TR.timer + 1;\
|
||||
if (TR.length.value && TR.linear.value) {\
|
||||
TR.sequencer = (TR.sequencer + 1) & 0x1F;\
|
||||
triangle_output()\
|
||||
apu.clocked = TRUE;\
|
||||
}\
|
||||
}
|
||||
#define noise_tick()\
|
||||
if (!(--NS.frequency)) {\
|
||||
if (NS.mode) {\
|
||||
NS.shift = (NS.shift >> 1) | (((NS.shift ^ (NS.shift >> 6)) & 0x0001) << 14);\
|
||||
} else {\
|
||||
NS.shift = (NS.shift >> 1) | (((NS.shift ^ (NS.shift >> 1)) & 0x0001) << 14);\
|
||||
}\
|
||||
NS.shift &= 0x7FFF;\
|
||||
noise_output()\
|
||||
NS.frequency = noise_timer[apu.type][NS.timer];\
|
||||
apu.clocked = TRUE;\
|
||||
}
|
||||
#define dmc_tick()\
|
||||
if (!(--DMC.frequency)) {\
|
||||
if (!DMC.silence) {\
|
||||
if (!(DMC.shift & 0x01)) {\
|
||||
if (DMC.counter > 1) {\
|
||||
DMC.counter -= 2;\
|
||||
}\
|
||||
} else {\
|
||||
if (DMC.counter < 126) {\
|
||||
DMC.counter += 2;\
|
||||
}\
|
||||
}\
|
||||
}\
|
||||
DMC.shift >>= 1;\
|
||||
dmc_output();\
|
||||
if (!(--DMC.counter_out)) {\
|
||||
DMC.counter_out = 8;\
|
||||
if (!DMC.empty) {\
|
||||
DMC.shift = DMC.buffer;\
|
||||
DMC.empty = TRUE;\
|
||||
DMC.silence = FALSE;\
|
||||
} else {\
|
||||
DMC.silence = TRUE;\
|
||||
}\
|
||||
}\
|
||||
DMC.frequency = dmc_rate[apu.type][DMC.rate_index];\
|
||||
apu.clocked = TRUE;\
|
||||
}\
|
||||
if (DMC.empty && DMC.remain) {\
|
||||
BYTE tick = 4;\
|
||||
switch (DMC.tick_type) {\
|
||||
case DMC_CPU_WRITE:\
|
||||
tick = 3;\
|
||||
break;\
|
||||
case DMC_R4014:\
|
||||
tick = 2;\
|
||||
break;\
|
||||
case DMC_NNL_DMA:\
|
||||
tick = 1;\
|
||||
break;\
|
||||
}\
|
||||
{\
|
||||
DMC.buffer = addrSpace[DMC.address];\
|
||||
}\
|
||||
/* incremento gli hwtick da compiere */\
|
||||
if (hwtick) { hwtick[0] += tick; }\
|
||||
/* e naturalmente incremento anche quelli eseguiti dall'opcode */\
|
||||
cpu_cycles += tick;\
|
||||
/* salvo a che ciclo dell'istruzione avviene il dma */\
|
||||
DMC.dma_cycle = cpu_opcode_cycle;\
|
||||
/* il DMC non e' vuoto */\
|
||||
DMC.empty = FALSE;\
|
||||
if (++DMC.address > 0xFFFF) {\
|
||||
DMC.address = 0x8000;\
|
||||
}\
|
||||
if (!(--DMC.remain)) {\
|
||||
if (DMC.loop) {\
|
||||
DMC.remain = DMC.length;\
|
||||
DMC.address = DMC.address_start;\
|
||||
} else if (DMC.irq_enabled) {\
|
||||
r4015.value |= 0x80;\
|
||||
}\
|
||||
}\
|
||||
}
|
||||
|
||||
#define apu_change_step(index)\
|
||||
apu.cycles += apuPeriod[apu.mode][apu.type][index]
|
||||
#if defined (VECCHIA_GESTIONE_JITTER)
|
||||
#define r4017_jitter()\
|
||||
r4017.value = (r4017.jitter.value & 0xC0);\
|
||||
/*\
|
||||
* se il bit 7 e' a zero, devo attivare la\
|
||||
* modalita' NTSC, se a uno quella PAL.\
|
||||
*/\
|
||||
if (r4017.value & 0x80) {\
|
||||
apu.mode = APU_48HZ;\
|
||||
} else {\
|
||||
apu.mode = APU_60HZ;\
|
||||
}\
|
||||
if (r4017.value & 0x40) {\
|
||||
/* azzero il bit 6 del $4015 */\
|
||||
r4015.value &= 0xBF;\
|
||||
/* questo non e' affatto necessario sul forno */\
|
||||
}\
|
||||
/* riavvio il frame audio */\
|
||||
apu.step = apu.cycles = 0;\
|
||||
apu_change_step(apu.step)
|
||||
#else
|
||||
#define r4017_jitter(apc)\
|
||||
r4017.value = (r4017.jitter.value & 0xC0);\
|
||||
r4017.reset_frame_delay = 1;\
|
||||
if (apu.cycles == apc) {\
|
||||
if (apu.mode == APU_48HZ) {\
|
||||
r4017.reset_frame_delay += 1;\
|
||||
} else {\
|
||||
r4017.reset_frame_delay += 2;\
|
||||
}\
|
||||
}\
|
||||
/*\
|
||||
* se il bit 7 e' a zero, devo attivare la\
|
||||
* modalita' NTSC, se a uno quella PAL.\
|
||||
*/\
|
||||
if (r4017.value & 0x80) {\
|
||||
apu.mode = APU_48HZ;\
|
||||
} else {\
|
||||
apu.mode = APU_60HZ;\
|
||||
}\
|
||||
if (r4017.value & 0x40) {\
|
||||
/* azzero il bit 6 del $4015 */\
|
||||
r4015.value &= 0xBF;\
|
||||
/* questo non e' affatto necessario sul forno */\
|
||||
}
|
||||
#define r4017_reset_frame()\
|
||||
if (r4017.reset_frame_delay && (--r4017.reset_frame_delay == 0)) {\
|
||||
/* riavvio il frame audio */\
|
||||
apu.step = apu.cycles = 0;\
|
||||
apu_change_step(apu.step);\
|
||||
}
|
||||
#endif
|
||||
#define square_reg0(square)\
|
||||
/* duty */\
|
||||
square.duty = value >> 6;\
|
||||
/* length counter */\
|
||||
square.length.halt = value & 0x20;\
|
||||
/* envelope */\
|
||||
square.envelope.constant_volume = value & 0x10;\
|
||||
square.envelope.divider = value & 0x0F
|
||||
#define square_reg1(square)\
|
||||
/* sweep */\
|
||||
square.sweep.reload = TRUE;\
|
||||
square.sweep.divider = (value >> 4) & 0x07;\
|
||||
square.sweep.shift = value & 0x07;\
|
||||
square.sweep.enabled = value & 0x80;\
|
||||
square.sweep.negate = value & 0x08
|
||||
#define square_reg2(square)\
|
||||
/* timer (low 8 bits) */\
|
||||
square.timer = (square.timer & 0x0700) | value
|
||||
#define square_reg3(square)\
|
||||
/* 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.\
|
||||
*/\
|
||||
if (square.length.enabled && !(apu.length_clocked && square.length.value)) {\
|
||||
square.length.value = length_table[value >> 3];\
|
||||
}\
|
||||
/* envelope */\
|
||||
square.envelope.enabled = TRUE;\
|
||||
/* timer (high 3 bits) */\
|
||||
square.timer = (square.timer & 0x00FF) | ((value & 0x07) << 8);\
|
||||
/*The correct behaviour is to reset the duty cycle sequencers but not the clock dividers*/\
|
||||
/*square.frequency = 1;*/\
|
||||
/* sequencer */\
|
||||
square.sequencer = 0
|
||||
#define init_nla_table(p, t)\
|
||||
{\
|
||||
WORD i;\
|
||||
for (i = 0; i < LENGTH(nla_table.pulse); i++) {\
|
||||
double vl = 95.52 / (8128.0 / (double) i + 100.0);\
|
||||
nla_table.pulse[i] = (vl * p);\
|
||||
}\
|
||||
for (i = 0; i < LENGTH(nla_table.tnd); i++) {\
|
||||
double vl = 163.67 / (24329.0 / (double) i + 100.0);\
|
||||
nla_table.tnd[i] = (vl * t);\
|
||||
}\
|
||||
}
|
||||
#define _apu_channel_volume_adjust(ch, index)\
|
||||
((ch * cfg->apu.channel[index]) * ch_gain_ptnd(index))
|
||||
#define s1_out\
|
||||
_apu_channel_volume_adjust(S1.output, APU_S1)
|
||||
#define s2_out\
|
||||
_apu_channel_volume_adjust(S2.output, APU_S2)
|
||||
#define tr_out\
|
||||
_apu_channel_volume_adjust(TR.output, APU_TR)
|
||||
#define ns_out\
|
||||
_apu_channel_volume_adjust(NS.output, APU_NS)
|
||||
#define dmc_out\
|
||||
_apu_channel_volume_adjust(DMC.output, APU_DMC)
|
||||
#define extra_out(ch)\
|
||||
(ch * cfg->apu.channel[APU_EXTRA])
|
||||
#define pulse_output()\
|
||||
nla_table.pulse[(int) (s1_out + s2_out)]
|
||||
#define tnd_output()\
|
||||
nla_table.tnd[(int) ((tr_out * 3) + (ns_out * 2) + dmc_out)]
|
||||
|
||||
typedef struct _config_apu {
|
||||
BYTE channel[APU_MASTER + 1];
|
||||
double volume[APU_MASTER + 1];
|
||||
} _config_apu;
|
||||
typedef struct _apu {
|
||||
BYTE mode;
|
||||
BYTE type;
|
||||
BYTE step;
|
||||
BYTE length_clocked;
|
||||
BYTE DMC;
|
||||
SWORD cycles;
|
||||
|
||||
/* ------------------------------------------------------- */
|
||||
/* questi valori non e' necessario salvarli nei savestates */
|
||||
/* ------------------------------------------------------- */
|
||||
/* */ BYTE clocked; /* */
|
||||
/* ------------------------------------------------------- */
|
||||
} _apu;
|
||||
typedef struct _r4011 {
|
||||
BYTE value;
|
||||
DBWORD frames;
|
||||
DBWORD cycles;
|
||||
SWORD output;
|
||||
} _r4011;
|
||||
typedef struct _r4015 {
|
||||
BYTE value;
|
||||
} _r4015;
|
||||
typedef struct _r4017 {
|
||||
BYTE value;
|
||||
struct _r4017_litter {
|
||||
BYTE value;
|
||||
BYTE delay;
|
||||
} jitter;
|
||||
BYTE reset_frame_delay;
|
||||
} _r4017;
|
||||
typedef struct _envelope {
|
||||
BYTE enabled;
|
||||
BYTE divider;
|
||||
BYTE counter;
|
||||
BYTE constant_volume;
|
||||
SBYTE delay;
|
||||
} _envelope;
|
||||
typedef struct _sweep {
|
||||
BYTE enabled;
|
||||
BYTE negate;
|
||||
BYTE divider;
|
||||
BYTE shift;
|
||||
BYTE reload;
|
||||
BYTE silence;
|
||||
SBYTE delay;
|
||||
} _sweep;
|
||||
typedef struct _length_counter {
|
||||
BYTE value;
|
||||
BYTE enabled;
|
||||
BYTE halt;
|
||||
} _length_counter;
|
||||
typedef struct _linear_counter {
|
||||
BYTE value;
|
||||
BYTE reload;
|
||||
BYTE halt;
|
||||
} _linear_counter;
|
||||
typedef struct _apuSquare {
|
||||
/* timer */
|
||||
DBWORD timer;
|
||||
/* ogni quanti cicli devo generare un output */
|
||||
WORD frequency;
|
||||
/* duty */
|
||||
BYTE duty;
|
||||
/* envelope */
|
||||
_envelope envelope;
|
||||
/* volume */
|
||||
BYTE volume;
|
||||
/* sequencer */
|
||||
BYTE sequencer;
|
||||
/* sweep */
|
||||
_sweep sweep;
|
||||
/* length counter */
|
||||
_length_counter length;
|
||||
/* output */
|
||||
SWORD output;
|
||||
} _apuSquare;
|
||||
typedef struct _apuTriangle {
|
||||
/* timer */
|
||||
DBWORD timer;
|
||||
/* ogni quanti cicli devo generare un output */
|
||||
WORD frequency;
|
||||
/* linear counter */
|
||||
_linear_counter linear;
|
||||
/* length counter */
|
||||
_length_counter length;
|
||||
/* sequencer */
|
||||
BYTE sequencer;
|
||||
/* output */
|
||||
SWORD output;
|
||||
} _apuTriangle;
|
||||
typedef struct _apuNoise {
|
||||
/* timer */
|
||||
DBWORD timer;
|
||||
/* ogni quanti cicli devo generare un output */
|
||||
WORD frequency;
|
||||
/* envelope */
|
||||
_envelope envelope;
|
||||
/* specifico del noise */
|
||||
BYTE mode;
|
||||
/* volume */
|
||||
BYTE volume;
|
||||
/* shift register */
|
||||
WORD shift;
|
||||
/* length counter */
|
||||
_length_counter length;
|
||||
/* sequencer */
|
||||
BYTE sequencer;
|
||||
/* output */
|
||||
SWORD output;
|
||||
} _apuNoise;
|
||||
typedef struct _apuDMC {
|
||||
/* ogni quanti cicli devo generare un output */
|
||||
WORD frequency;
|
||||
|
||||
WORD remain;
|
||||
BYTE irq_enabled;
|
||||
BYTE loop;
|
||||
BYTE rate_index;
|
||||
WORD address_start;
|
||||
DBWORD address;
|
||||
WORD length;
|
||||
BYTE counter;
|
||||
BYTE empty;
|
||||
BYTE buffer;
|
||||
|
||||
/* DMA */
|
||||
BYTE dma_cycle;
|
||||
|
||||
/* output unit */
|
||||
BYTE silence;
|
||||
BYTE shift;
|
||||
BYTE counter_out;
|
||||
|
||||
/* output */
|
||||
SWORD output;
|
||||
|
||||
/* misc */
|
||||
BYTE tick_type;
|
||||
} _apuDMC;
|
||||
|
||||
#if defined (__cplusplus)
|
||||
#define EXTERNC extern "C"
|
||||
#else
|
||||
#define EXTERNC
|
||||
#endif
|
||||
|
||||
EXTERNC struct _nla_table {
|
||||
SWORD pulse[32];
|
||||
SWORD tnd[203];
|
||||
} nla_table;
|
||||
|
||||
EXTERNC _apu apu;
|
||||
EXTERNC _r4011 r4011;
|
||||
EXTERNC _r4015 r4015;
|
||||
EXTERNC _r4017 r4017;
|
||||
EXTERNC _apuSquare S1, S2;
|
||||
EXTERNC _apuTriangle TR;
|
||||
EXTERNC _apuNoise NS;
|
||||
EXTERNC _apuDMC DMC;
|
||||
|
||||
/* apuPeriod[mode][type][cycles] */
|
||||
static const WORD apuPeriod[2][3][7] = {
|
||||
/*
|
||||
* Mode 0: 4-step sequence
|
||||
* Action Envelopes & Length Counter& Interrupt Delay to next
|
||||
* Linear Counter Sweep Units Flag NTSC PAL Dendy
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
* $4017=$00 - - - 7459 8315 7459
|
||||
* Step 1 Clock - - 7456 8314 7456
|
||||
* Step 2 Clock Clock - 7458 8312 7458
|
||||
* Step 3 Clock - - 7458 8314 7458
|
||||
* Step 4 Clock Clock Set if enabled 7458 8314 7458
|
||||
*/
|
||||
{
|
||||
{7459, 7456, 7458, 7457, 1, 1, 7457},
|
||||
{8315, 8314, 8312, 8313, 1, 1, 8313},
|
||||
{7459, 7456, 7458, 7457, 1, 1, 7457}
|
||||
},
|
||||
/*
|
||||
* Mode 1: 5-step sequence
|
||||
* Action Envelopes & Length Counter& Interrupt Delay to next
|
||||
* Linear Counter Sweep Units Flag NTSC PAL Dendy
|
||||
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
* $4017=$80 - - - 1 1 1
|
||||
* Step 1 Clock Clock - 7458 8314 7458
|
||||
* Step 2 Clock - - 7456 8314 7456
|
||||
* Step 3 Clock Clock - 7458 8312 7458
|
||||
* Step 4 Clock - - 7458 8314 7458
|
||||
* Step 5 - - - 7452 8312 7452
|
||||
*
|
||||
* Note:
|
||||
* il 7452 e il 8312 dello step 5 diventano 7451 e 8311
|
||||
* nella mia tabella perche' il ciclo mancante lo eseguo
|
||||
* all'inizio del ciclo successivo.
|
||||
*/
|
||||
{
|
||||
{1, 7458, 7456, 7458, 7458, 7451, 0},
|
||||
{1, 8314, 8314, 8312, 8314, 8311, 0},
|
||||
{1, 7458, 7456, 7458, 7458, 7451, 0}
|
||||
}
|
||||
};
|
||||
|
||||
/* la tabella con i valori da caricare nel length counter del canale */
|
||||
static const BYTE length_table[32] = {
|
||||
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
|
||||
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
|
||||
0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
|
||||
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
|
||||
};
|
||||
|
||||
static const BYTE square_duty[2][4][8] = {
|
||||
{
|
||||
{ 1, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
{ 1, 1, 1, 1, 0, 0, 0, 0},
|
||||
{ 0, 0, 1, 1, 1, 1, 1, 1}
|
||||
},
|
||||
{
|
||||
{ 1, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ 1, 1, 1, 1, 0, 0, 0, 0},
|
||||
{ 1, 1, 0, 0, 0, 0, 0, 0},
|
||||
{ 0, 0, 1, 1, 1, 1, 1, 1}
|
||||
},
|
||||
};
|
||||
|
||||
static const BYTE triangle_duty[32] = {
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
};
|
||||
|
||||
static const WORD noise_timer[3][16] = {
|
||||
{
|
||||
0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0,
|
||||
0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4
|
||||
},
|
||||
{
|
||||
0x0004, 0x0007, 0x000E, 0x001E, 0x003C, 0x0058, 0x0076, 0x0094,
|
||||
0x00BC, 0x00EC, 0x0162, 0x01D8, 0x02C4, 0x03B0, 0x0762, 0x0EC2
|
||||
},
|
||||
{
|
||||
0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0,
|
||||
0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4
|
||||
}
|
||||
};
|
||||
|
||||
static const WORD dmc_rate[3][16] = {
|
||||
{
|
||||
0x01AC, 0x017C, 0x0154, 0x0140, 0x011E, 0x00FE, 0x00E2, 0x00D6,
|
||||
0x00BE, 0x00A0, 0x008E, 0x0080, 0x006A, 0x0054, 0x0048, 0x0036
|
||||
},
|
||||
{
|
||||
0x018E, 0x0162, 0x013C, 0x012A, 0x0114, 0x00EC, 0x00D2, 0x00C6,
|
||||
0x00B0, 0x0094, 0x0084, 0x0076, 0x0062, 0x004E, 0x0042, 0x0032
|
||||
},
|
||||
{
|
||||
0x01AC, 0x017C, 0x0154, 0x0140, 0x011E, 0x00FE, 0x00E2, 0x00D6,
|
||||
0x00BE, 0x00A0, 0x008E, 0x0080, 0x006A, 0x0054, 0x0048, 0x0036
|
||||
}
|
||||
};
|
||||
|
||||
EXTERNC void apu_tick(BYTE *hwtick);
|
||||
EXTERNC void apu_turn_on(void);
|
||||
|
||||
#undef EXTERNC
|
||||
|
||||
#endif /* APU_H_ */
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// additional modifications by tildearrow for furnace
|
||||
|
||||
#ifndef COMMON_H_
|
||||
#define COMMON_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char BYTE;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned int DBWORD;
|
||||
typedef signed char SBYTE;
|
||||
typedef signed short SWORD;
|
||||
typedef signed int SDBWORD;
|
||||
|
||||
#if !defined (FALSE)
|
||||
enum false_value { FALSE, TRUE };
|
||||
#endif
|
||||
enum exit_type { EXIT_OK, EXIT_ERROR };
|
||||
enum lower_value { LOWER, UPPER };
|
||||
enum machine_mode { AUTO, NTSC, PAL, DENDY, DEFAULT = 255 };
|
||||
enum reset_type {
|
||||
RESET = 0x10,
|
||||
HARD = 0x20,
|
||||
CHANGE_ROM = 0x30,
|
||||
CHANGE_MODE = 0x40,
|
||||
POWER_UP = 0x50
|
||||
};
|
||||
/* le dimesioni dello screen da renderizzare */
|
||||
enum screen_dimension { SCR_LINES = 240, SCR_ROWS = 256 };
|
||||
enum type_of_system_info { HEADER, DATABASE };
|
||||
enum header_type { iNES_1_0, NES_2_0, UNIF_FORMAT, FDS_FORMAT, NSF_FORMAT, NSFE_FORMAT };
|
||||
enum length_file_name_type {
|
||||
LENGTH_FILE_NAME = 512,
|
||||
LENGTH_FILE_NAME_MID = 1024,
|
||||
LENGTH_FILE_NAME_LONG = 2048,
|
||||
LENGTH_FILE_NAME_MAX = 4096
|
||||
};
|
||||
enum forced_mirroring { UNK_HORIZONTAL, UNK_VERTICAL };
|
||||
enum max_chips_rom { MAX_CHIPS = 8 };
|
||||
enum languages { LNG_ENGLISH, LNG_ITALIAN, LNG_RUSSIAN };
|
||||
enum database_mode {
|
||||
NODIPSWITCH = 0xFF00,
|
||||
NOEXTRA = 0x0000,
|
||||
VSZAPPER = 0x0001,
|
||||
CHRRAM32K = 0x0002,
|
||||
CHRRAM256K = 0x0004
|
||||
};
|
||||
|
||||
#define LENGTH(x) (sizeof(x)/sizeof(*(x)))
|
||||
#define UNUSED(var) var __attribute__((unused))
|
||||
|
||||
#if defined (DEBUG)
|
||||
#define INLINE
|
||||
#else
|
||||
#define INLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
|
||||
#endif /* COMMON_H_ */
|
Loading…
Reference in New Issue