this is why I don't use tabs

in one editor it's 8 spaces, in another it's 2...
spaces on the other hand are the same regardless of editor
This commit is contained in:
tildearrow 2025-03-06 23:23:46 -05:00
parent f43715775c
commit ad8437e5ae
8 changed files with 1270 additions and 1270 deletions

View file

@ -22,303 +22,303 @@
#include "apu.h"
void apu_tick(struct NESAPU* a, BYTE *hwtick) {
/* sottraggo il numero di cicli eseguiti */
a->apu.cycles--;
/*
* questo flag sara' a TRUE solo nel ciclo
* in cui viene eseguito il length counter.
*/
a->apu.length_clocked = FALSE;
/*
* se e' settato il delay del $4017, essendo
* questo il ciclo successivo, valorizzo il
* registro.
*/
/* sottraggo il numero di cicli eseguiti */
a->apu.cycles--;
/*
* questo flag sara' a TRUE solo nel ciclo
* in cui viene eseguito il length counter.
*/
a->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();
}
if (r4017.jitter.delay) {
r4017.jitter.delay = FALSE;
r4017_jitter();
}
#else
if (a->r4017.jitter.delay) {
a->r4017.jitter.delay = FALSE;
r4017_jitter(0)
}
r4017_reset_frame()
if (a->r4017.jitter.delay) {
a->r4017.jitter.delay = FALSE;
r4017_jitter(0)
}
r4017_reset_frame()
#endif
/* quando apu.cycles e' a 0 devo eseguire uno step */
if (!a->apu.cycles) {
switch (a->apu.step) {
case 0:
/*
* nel mode 1 devo eseguire il
* length counter e lo sweep.
*/
if (a->apu.mode == APU_48HZ) {
length_clock()
sweep_clock()
}
envelope_clock()
/* triangle's linear counter */
linear_clock()
/* passo al prossimo step */
apu_change_step(++a->apu.step);
break;
case 1:
/* nel mode 0 devo eseguire il length counter */
if (a->apu.mode == APU_60HZ) {
length_clock()
sweep_clock()
}
envelope_clock()
/* triangle's linear counter */
linear_clock()
/* passo al prossimo step */
apu_change_step(++a->apu.step);
break;
case 2:
/*
* nel mode 1 devo eseguire il
* length counter e lo sweep.
*/
if (a->apu.mode == APU_48HZ) {
length_clock()
sweep_clock()
}
envelope_clock()
/* triangle's linear counter */
linear_clock()
/* passo al prossimo step */
apu_change_step(++a->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 (a->apu.mode == APU_60HZ) {
/*
* se e' a 0 il bit 6 del $4017 (interrupt
* inhibit flag) allora devo generare un IRQ.
*/
if (!(a->r4017.value & 0x40)) {
/* setto il bit 6 del $4015 */
a->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(++a->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 (a->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 (!(a->r4017.value & 0x40)) {
/* setto il bit 6 del $4015 */
a->r4015.value |= 0x40;
}
}
/* passo al prossimo step */
apu_change_step(++a->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 (a->apu.mode == APU_60HZ) {
/*
* se e' a 0 il bit 6 del $4017 (interrupt
* inhibit flag) allora devo generare un IRQ.
*/
if (!(a->r4017.value & 0x40)) {
/* setto il bit 6 del $4015 */
a->r4015.value |= 0x40;
}
a->apu.step++;
} else {
/* nel mode 1 devo ricominciare il ciclo */
a->apu.step = 0;
}
/* passo al prossimo step */
apu_change_step(a->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 */
a->apu.step = 1;
/* passo al prossimo step */
apu_change_step(a->apu.step);
break;
}
}
/* quando apu.cycles e' a 0 devo eseguire uno step */
if (!a->apu.cycles) {
switch (a->apu.step) {
case 0:
/*
* nel mode 1 devo eseguire il
* length counter e lo sweep.
*/
if (a->apu.mode == APU_48HZ) {
length_clock()
sweep_clock()
}
envelope_clock()
/* triangle's linear counter */
linear_clock()
/* passo al prossimo step */
apu_change_step(++a->apu.step);
break;
case 1:
/* nel mode 0 devo eseguire il length counter */
if (a->apu.mode == APU_60HZ) {
length_clock()
sweep_clock()
}
envelope_clock()
/* triangle's linear counter */
linear_clock()
/* passo al prossimo step */
apu_change_step(++a->apu.step);
break;
case 2:
/*
* nel mode 1 devo eseguire il
* length counter e lo sweep.
*/
if (a->apu.mode == APU_48HZ) {
length_clock()
sweep_clock()
}
envelope_clock()
/* triangle's linear counter */
linear_clock()
/* passo al prossimo step */
apu_change_step(++a->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 (a->apu.mode == APU_60HZ) {
/*
* se e' a 0 il bit 6 del $4017 (interrupt
* inhibit flag) allora devo generare un IRQ.
*/
if (!(a->r4017.value & 0x40)) {
/* setto il bit 6 del $4015 */
a->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(++a->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 (a->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 (!(a->r4017.value & 0x40)) {
/* setto il bit 6 del $4015 */
a->r4015.value |= 0x40;
}
}
/* passo al prossimo step */
apu_change_step(++a->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 (a->apu.mode == APU_60HZ) {
/*
* se e' a 0 il bit 6 del $4017 (interrupt
* inhibit flag) allora devo generare un IRQ.
*/
if (!(a->r4017.value & 0x40)) {
/* setto il bit 6 del $4015 */
a->r4015.value |= 0x40;
}
a->apu.step++;
} else {
/* nel mode 1 devo ricominciare il ciclo */
a->apu.step = 0;
}
/* passo al prossimo step */
apu_change_step(a->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 */
a->apu.step = 1;
/* passo al prossimo step */
apu_change_step(a->apu.step);
break;
}
}
/*
* eseguo un ticket per ogni canale
* valorizzandone l'output.
*/
// SQUARE 1 TICK
if (!(--a->S1.frequency)) {
square_output(a->S1, 0)
a->S1.frequency = (a->S1.timer + 1) << 1;
a->S1.sequencer = (a->S1.sequencer + 1) & 0x07;
a->apu.clocked = TRUE;
}
/*
* eseguo un ticket per ogni canale
* valorizzandone l'output.
*/
// SQUARE 1 TICK
if (!(--a->S1.frequency)) {
square_output(a->S1, 0)
a->S1.frequency = (a->S1.timer + 1) << 1;
a->S1.sequencer = (a->S1.sequencer + 1) & 0x07;
a->apu.clocked = TRUE;
}
// SQUARE 2 TICK
if (!(--a->S2.frequency)) {
square_output(a->S2, 0)
a->S2.frequency = (a->S2.timer + 1) << 1;
a->S2.sequencer = (a->S2.sequencer + 1) & 0x07;
a->apu.clocked = TRUE;
}
// SQUARE 2 TICK
if (!(--a->S2.frequency)) {
square_output(a->S2, 0)
a->S2.frequency = (a->S2.timer + 1) << 1;
a->S2.sequencer = (a->S2.sequencer + 1) & 0x07;
a->apu.clocked = TRUE;
}
// TRIANGLE TICK
if (!(--a->TR.frequency)) {
a->TR.frequency = a->TR.timer + 1;
if (a->TR.length.value && a->TR.linear.value) {
a->TR.sequencer = (a->TR.sequencer + 1) & 0x1F;
triangle_output()
a->apu.clocked = TRUE;
}
}
// TRIANGLE TICK
if (!(--a->TR.frequency)) {
a->TR.frequency = a->TR.timer + 1;
if (a->TR.length.value && a->TR.linear.value) {
a->TR.sequencer = (a->TR.sequencer + 1) & 0x1F;
triangle_output()
a->apu.clocked = TRUE;
}
}
// NOISE TICK
if (!(--a->NS.frequency)) {
if (a->NS.mode) {
a->NS.shift = (a->NS.shift >> 1) | (((a->NS.shift ^ (a->NS.shift >> 6)) & 0x0001) << 14);
} else {
a->NS.shift = (a->NS.shift >> 1) | (((a->NS.shift ^ (a->NS.shift >> 1)) & 0x0001) << 14);
}
a->NS.shift &= 0x7FFF;
noise_output()
a->NS.frequency = noise_timer[a->apu.type][a->NS.timer];
a->apu.clocked = TRUE;
}
// NOISE TICK
if (!(--a->NS.frequency)) {
if (a->NS.mode) {
a->NS.shift = (a->NS.shift >> 1) | (((a->NS.shift ^ (a->NS.shift >> 6)) & 0x0001) << 14);
} else {
a->NS.shift = (a->NS.shift >> 1) | (((a->NS.shift ^ (a->NS.shift >> 1)) & 0x0001) << 14);
}
a->NS.shift &= 0x7FFF;
noise_output()
a->NS.frequency = noise_timer[a->apu.type][a->NS.timer];
a->apu.clocked = TRUE;
}
// DMC TICK
if (!(--a->DMC.frequency)) {
if (!a->DMC.silence) {
if (!(a->DMC.shift & 0x01)) {
if (a->DMC.counter > 1) {
a->DMC.counter -= 2;
}
} else {
if (a->DMC.counter < 126) {
a->DMC.counter += 2;
}
}
}
a->DMC.shift >>= 1;
dmc_output();
if (!(--a->DMC.counter_out)) {
a->DMC.counter_out = 8;
if (!a->DMC.empty) {
a->DMC.shift = a->DMC.buffer;
a->DMC.empty = TRUE;
a->DMC.silence = FALSE;
} else {
a->DMC.silence = TRUE;
}
}
a->DMC.frequency = dmc_rate[a->apu.type][a->DMC.rate_index];
a->apu.clocked = TRUE;
}
if (a->DMC.empty && a->DMC.remain) {
BYTE tick = 4;
switch (a->DMC.tick_type) {
case DMC_CPU_WRITE:
tick = 3;
break;
case DMC_R4014:
tick = 2;
break;
case DMC_NNL_DMA:
tick = 1;
break;
}
{
a->DMC.buffer = a->readDMC(a->readDMCUser,a->DMC.address);
}
/* incremento gli hwtick da compiere */
if (hwtick) { hwtick[0] += tick; }
/* e naturalmente incremento anche quelli eseguiti dall'opcode */
a->apu.cpu_cycles += tick;
/* salvo a che ciclo dell'istruzione avviene il dma */
a->DMC.dma_cycle = a->apu.cpu_opcode_cycle;
/* il DMC non e' vuoto */
a->DMC.empty = FALSE;
if (++a->DMC.address > 0xFFFF) {
a->DMC.address = 0x8000;
}
if (!(--a->DMC.remain)) {
if (a->DMC.loop) {
a->DMC.remain = a->DMC.length;
a->DMC.address = a->DMC.address_start;
} else if (a->DMC.irq_enabled) {
a->r4015.value |= 0x80;
}
}
}
// DMC TICK
if (!(--a->DMC.frequency)) {
if (!a->DMC.silence) {
if (!(a->DMC.shift & 0x01)) {
if (a->DMC.counter > 1) {
a->DMC.counter -= 2;
}
} else {
if (a->DMC.counter < 126) {
a->DMC.counter += 2;
}
}
}
a->DMC.shift >>= 1;
dmc_output();
if (!(--a->DMC.counter_out)) {
a->DMC.counter_out = 8;
if (!a->DMC.empty) {
a->DMC.shift = a->DMC.buffer;
a->DMC.empty = TRUE;
a->DMC.silence = FALSE;
} else {
a->DMC.silence = TRUE;
}
}
a->DMC.frequency = dmc_rate[a->apu.type][a->DMC.rate_index];
a->apu.clocked = TRUE;
}
if (a->DMC.empty && a->DMC.remain) {
BYTE tick = 4;
switch (a->DMC.tick_type) {
case DMC_CPU_WRITE:
tick = 3;
break;
case DMC_R4014:
tick = 2;
break;
case DMC_NNL_DMA:
tick = 1;
break;
}
{
a->DMC.buffer = a->readDMC(a->readDMCUser,a->DMC.address);
}
/* incremento gli hwtick da compiere */
if (hwtick) { hwtick[0] += tick; }
/* e naturalmente incremento anche quelli eseguiti dall'opcode */
a->apu.cpu_cycles += tick;
/* salvo a che ciclo dell'istruzione avviene il dma */
a->DMC.dma_cycle = a->apu.cpu_opcode_cycle;
/* il DMC non e' vuoto */
a->DMC.empty = FALSE;
if (++a->DMC.address > 0xFFFF) {
a->DMC.address = 0x8000;
}
if (!(--a->DMC.remain)) {
if (a->DMC.loop) {
a->DMC.remain = a->DMC.length;
a->DMC.address = a->DMC.address_start;
} else if (a->DMC.irq_enabled) {
a->r4015.value |= 0x80;
}
}
}
a->r4011.cycles++;
a->r4011.cycles++;
}
void apu_turn_on(struct NESAPU* a, BYTE apu_type) {
memset(&a->apu, 0x00, sizeof(a->apu));
memset(&a->r4015, 0x00, sizeof(a->r4015));
memset(&a->r4017, 0x00, sizeof(a->r4017));
/* azzero tutte le variabili interne dei canali */
memset(&a->S1, 0x00, sizeof(a->S1));
memset(&a->S2, 0x00, sizeof(a->S2));
memset(&a->TR, 0x00, sizeof(a->TR));
memset(&a->NS, 0x00, sizeof(a->NS));
memset(&a->DMC, 0x00, sizeof(a->DMC));
/* al reset e' sempre settato a 60Hz */
a->apu.mode = APU_60HZ;
memset(&a->apu, 0x00, sizeof(a->apu));
memset(&a->r4015, 0x00, sizeof(a->r4015));
memset(&a->r4017, 0x00, sizeof(a->r4017));
/* azzero tutte le variabili interne dei canali */
memset(&a->S1, 0x00, sizeof(a->S1));
memset(&a->S2, 0x00, sizeof(a->S2));
memset(&a->TR, 0x00, sizeof(a->TR));
memset(&a->NS, 0x00, sizeof(a->NS));
memset(&a->DMC, 0x00, sizeof(a->DMC));
/* al reset e' sempre settato a 60Hz */
a->apu.mode = APU_60HZ;
/* per favore non fatemi questo... e' terribile */
a->apu.type = apu_type;
apu_change_step(a->apu.step);
/* valori iniziali dei vari canali */
a->S1.frequency = 1;
a->S1.sweep.delay = 1;
a->S1.sweep.divider = 1;
a->S2.frequency = 1;
a->S2.sweep.delay = 1;
a->S2.sweep.divider = 1;
a->TR.frequency = 1;
a->apu.type = apu_type;
apu_change_step(a->apu.step);
/* valori iniziali dei vari canali */
a->S1.frequency = 1;
a->S1.sweep.delay = 1;
a->S1.sweep.divider = 1;
a->S2.frequency = 1;
a->S2.sweep.delay = 1;
a->S2.sweep.divider = 1;
a->TR.frequency = 1;
/* questo era 0 ma produce click nell'audio */
a->TR.sequencer = 7;
a->NS.frequency = 1;
a->NS.shift = 1;
a->DMC.frequency = 1;
a->DMC.empty = TRUE;
a->DMC.silence = TRUE;
a->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
a->DMC.length = 1;
a->DMC.address_start = 0xC000;
a->TR.sequencer = 7;
a->NS.frequency = 1;
a->NS.shift = 1;
a->DMC.frequency = 1;
a->DMC.empty = TRUE;
a->DMC.silence = TRUE;
a->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
a->DMC.length = 1;
a->DMC.address_start = 0xC000;
a->apu.odd_cycle = 0;
// come non viene inizializzato? Vorrei qualche spiegazione...
a->r4011.frames = 0;

View file

@ -31,384 +31,384 @@ enum apu_mode { APU_60HZ, APU_48HZ };
/* 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--;\
}
/*\
* 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()\
a->apu.length_clocked = TRUE;\
length_run(a->S1)\
length_run(a->S2)\
length_run(a->TR)\
length_run(a->NS)
a->apu.length_clocked = TRUE;\
length_run(a->S1)\
length_run(a->S2)\
length_run(a->TR)\
length_run(a->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;\
}\
}
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;\
}
/* 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(a->S1)\
envelope_run(a->S2)\
envelope_run(a->NS)
envelope_run(a->S1)\
envelope_run(a->S2)\
envelope_run(a->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);\
}
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;\
}\
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(a->S1, -1)\
sweep_run(a->S2, 0)
sweep_run(a->S1, -1)\
sweep_run(a->S2, 0)
/* linear counter */
#define linear_clock()\
if (a->TR.linear.halt) {\
a->TR.linear.value = a->TR.linear.reload;\
} else if (a->TR.linear.value) {\
a->TR.linear.value--;\
}\
if (!a->TR.length.halt) {\
a->TR.linear.halt = FALSE;\
}
if (a->TR.linear.halt) {\
a->TR.linear.value = a->TR.linear.reload;\
} else if (a->TR.linear.value) {\
a->TR.linear.value--;\
}\
if (!a->TR.length.halt) {\
a->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;\
}\
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.\
*/\
a->TR.output = triangle_duty[a->TR.sequencer];\
if (a->TR.timer < 2) {\
a->TR.output = triangle_duty[8];\
}
/*\
* ai 2 cicli piu' bassi del timer, la frequenza\
* risultante e' troppo alta (oltre i 20 kHz,\
* quindi non udibile), percio' la taglio.\
*/\
a->TR.output = triangle_duty[a->TR.sequencer];\
if (a->TR.timer < 2) {\
a->TR.output = triangle_duty[8];\
}
#define noise_output()\
envelope_volume(a->NS)\
a->NS.output = 0;\
if (a->NS.length.value && !(a->NS.shift & 0x0001)) {\
a->NS.output = a->NS.volume;\
}
envelope_volume(a->NS)\
a->NS.output = 0;\
if (a->NS.length.value && !(a->NS.shift & 0x0001)) {\
a->NS.output = a->NS.volume;\
}
#define dmc_output()\
a->DMC.output = a->DMC.counter & 0x7F
a->DMC.output = a->DMC.counter & 0x7F
/* tick */
#define apu_change_step(index)\
a->apu.cycles += apuPeriod[a->apu.mode][a->apu.type][index]
a->apu.cycles += apuPeriod[a->apu.mode][a->apu.type][index]
#if defined (VECCHIA_GESTIONE_JITTER)
#define r4017_jitter()\
a->r4017.value = (a->r4017.jitter.value & 0xC0);\
/*\
* se il bit 7 e' a zero, devo attivare la\
* modalita' NTSC, se a uno quella PAL.\
*/\
if (a->r4017.value & 0x80) {\
a->apu.mode = APU_48HZ;\
} else {\
a->apu.mode = APU_60HZ;\
}\
if (a->r4017.value & 0x40) {\
/* azzero il bit 6 del $4015 */\
a->r4015.value &= 0xBF;\
/* questo non e' affatto necessario sul forno */\
}\
/* riavvio il frame audio */\
a->apu.step = a->apu.cycles = 0;\
apu_change_step(a->apu.step)
a->r4017.value = (a->r4017.jitter.value & 0xC0);\
/*\
* se il bit 7 e' a zero, devo attivare la\
* modalita' NTSC, se a uno quella PAL.\
*/\
if (a->r4017.value & 0x80) {\
a->apu.mode = APU_48HZ;\
} else {\
a->apu.mode = APU_60HZ;\
}\
if (a->r4017.value & 0x40) {\
/* azzero il bit 6 del $4015 */\
a->r4015.value &= 0xBF;\
/* questo non e' affatto necessario sul forno */\
}\
/* riavvio il frame audio */\
a->apu.step = a->apu.cycles = 0;\
apu_change_step(a->apu.step)
#else
#define r4017_jitter(apc)\
a->r4017.value = (a->r4017.jitter.value & 0xC0);\
a->r4017.reset_frame_delay = 1;\
if (a->apu.cycles == apc) {\
if (a->apu.mode == APU_48HZ) {\
a->r4017.reset_frame_delay += 1;\
} else {\
a->r4017.reset_frame_delay += 2;\
}\
}\
/*\
* se il bit 7 e' a zero, devo attivare la\
* modalita' NTSC, se a uno quella PAL.\
*/\
if (a->r4017.value & 0x80) {\
a->apu.mode = APU_48HZ;\
} else {\
a->apu.mode = APU_60HZ;\
}\
if (a->r4017.value & 0x40) {\
/* azzero il bit 6 del $4015 */\
a->r4015.value &= 0xBF;\
/* questo non e' affatto necessario sul forno */\
}
a->r4017.value = (a->r4017.jitter.value & 0xC0);\
a->r4017.reset_frame_delay = 1;\
if (a->apu.cycles == apc) {\
if (a->apu.mode == APU_48HZ) {\
a->r4017.reset_frame_delay += 1;\
} else {\
a->r4017.reset_frame_delay += 2;\
}\
}\
/*\
* se il bit 7 e' a zero, devo attivare la\
* modalita' NTSC, se a uno quella PAL.\
*/\
if (a->r4017.value & 0x80) {\
a->apu.mode = APU_48HZ;\
} else {\
a->apu.mode = APU_60HZ;\
}\
if (a->r4017.value & 0x40) {\
/* azzero il bit 6 del $4015 */\
a->r4015.value &= 0xBF;\
/* questo non e' affatto necessario sul forno */\
}
#define r4017_reset_frame()\
if (a->r4017.reset_frame_delay && (--a->r4017.reset_frame_delay == 0)) {\
/* riavvio il frame audio */\
a->apu.step = a->apu.cycles = 0;\
apu_change_step(a->apu.step);\
}
if (a->r4017.reset_frame_delay && (--a->r4017.reset_frame_delay == 0)) {\
/* riavvio il frame audio */\
a->apu.step = a->apu.cycles = 0;\
apu_change_step(a->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
/* 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
/* 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
/* timer (low 8 bits) */\
square.timer = (square.timer & 0x0700) | value
#define square_reg3(square,length_clocked)\
/* 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 && !(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
/* 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 && !(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);\
}\
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))
((ch))
#define s1_out(a)\
(a->muted[0] ? 0 : _apu_channel_volume_adjust(a->S1.output, APU_S1))
(a->muted[0] ? 0 : _apu_channel_volume_adjust(a->S1.output, APU_S1))
#define s2_out(a)\
(a->muted[1] ? 0 : _apu_channel_volume_adjust(a->S2.output, APU_S2))
(a->muted[1] ? 0 : _apu_channel_volume_adjust(a->S2.output, APU_S2))
#define tr_out(a)\
(a->muted[2] ? 0 : _apu_channel_volume_adjust(a->TR.output, APU_TR))
(a->muted[2] ? 0 : _apu_channel_volume_adjust(a->TR.output, APU_TR))
#define ns_out(a)\
(a->muted[3] ? 0 : _apu_channel_volume_adjust(a->NS.output, APU_NS))
(a->muted[3] ? 0 : _apu_channel_volume_adjust(a->NS.output, APU_NS))
#define dmc_out(a)\
(a->muted[4] ? 0 : _apu_channel_volume_adjust(a->DMC.output, APU_DMC))
(a->muted[4] ? 0 : _apu_channel_volume_adjust(a->DMC.output, APU_DMC))
#define extra_out(ch)\
(ch * cfg->apu.channel[APU_EXTRA])
(ch * cfg->apu.channel[APU_EXTRA])
#define pulse_output(a)\
nla_table.pulse[(int) (s1_out(a) + s2_out(a))]
nla_table.pulse[(int) (s1_out(a) + s2_out(a))]
#define tnd_output(a)\
nla_table.tnd[(int) ((tr_out(a) * 3) + (ns_out(a) * 2) + dmc_out(a))]
nla_table.tnd[(int) ((tr_out(a) * 3) + (ns_out(a) * 2) + dmc_out(a))]
typedef struct _config_apu {
BYTE channel[APU_MASTER + 1];
double volume[APU_MASTER + 1];
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;
BYTE mode;
BYTE type;
BYTE step;
BYTE length_clocked;
BYTE DMC;
SWORD cycles;
int cpu_cycles;
int cpu_opcode_cycle;
BYTE odd_cycle;
/* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */
/* */ BYTE clocked; /* */
/* ------------------------------------------------------- */
/* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */
/* */ BYTE clocked; /* */
/* ------------------------------------------------------- */
} _apu;
typedef struct _r4011 {
BYTE value;
DBWORD frames;
DBWORD cycles;
SWORD output;
BYTE value;
DBWORD frames;
DBWORD cycles;
SWORD output;
} _r4011;
typedef struct _r4015 {
BYTE value;
BYTE value;
} _r4015;
typedef struct _r4017 {
BYTE value;
struct _r4017_litter {
BYTE value;
BYTE delay;
} jitter;
BYTE reset_frame_delay;
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;
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;
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;
BYTE value;
BYTE enabled;
BYTE halt;
} _length_counter;
typedef struct _linear_counter {
BYTE value;
BYTE reload;
BYTE halt;
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;
/* 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;
/* 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;
/* 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;
/* 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;
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;
/* DMA */
BYTE dma_cycle;
/* output unit */
BYTE silence;
BYTE shift;
BYTE counter_out;
/* output unit */
BYTE silence;
BYTE shift;
BYTE counter_out;
/* output */
SWORD output;
/* output */
SWORD output;
/* misc */
BYTE tick_type;
/* misc */
BYTE tick_type;
} _apuDMC;
#if defined (__cplusplus)
@ -418,8 +418,8 @@ typedef struct _apuDMC {
#endif
EXTERNC struct _nla_table {
SWORD pulse[32];
SWORD tnd[203];
SWORD pulse[32];
SWORD tnd[203];
};
extern struct _nla_table nla_table;
@ -440,104 +440,104 @@ EXTERNC struct NESAPU {
/* 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}
}
/*
* 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
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}
},
{
{ 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,
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
}
{
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
}
{
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(struct NESAPU* a, BYTE *hwtick);

View file

@ -37,31 +37,31 @@ 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
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
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
NODIPSWITCH = 0xFF00,
NOEXTRA = 0x0000,
VSZAPPER = 0x0001,
CHRRAM32K = 0x0002,
CHRRAM256K = 0x0004
};
#define LENGTH(x) (sizeof(x)/sizeof(*(x)))

View file

@ -25,373 +25,373 @@
#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;\
}
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++;\
}
r2006_inc()\
if ((r2006.value & 0x1F) == 0x1F) {\
r2006.value ^= 0x41F;\
} else {\
r2006.value++;\
}
INLINE static void apu_wr_reg(struct NESAPU* a, WORD address, BYTE value) {
if (!(address & 0x0010)) {
/* -------------------- square 1 --------------------*/
if (address <= 0x4003) {
if (address == 0x4000) {
square_reg0(a->S1);
return;
}
if (address == 0x4001) {
square_reg1(a->S1);
sweep_silence(a->S1)
return;
}
if (address == 0x4002) {
square_reg2(a->S1);
sweep_silence(a->S1)
return;
}
if (address == 0x4003) {
square_reg3(a->S1,a->apu.length_clocked);
sweep_silence(a->S1)
return;
}
return;
}
/* -------------------- square 2 --------------------*/
if (address <= 0x4007) {
if (address == 0x4004) {
square_reg0(a->S2);
return;
}
if (address == 0x4005) {
square_reg1(a->S2);
sweep_silence(a->S2)
return;
}
if (address == 0x4006) {
square_reg2(a->S2);
sweep_silence(a->S2)
return;
}
if (address == 0x4007) {
square_reg3(a->S2,a->apu.length_clocked);
sweep_silence(a->S2)
return;
}
return;
}
/* -------------------- triangle --------------------*/
if (address <= 0x400B) {
if (address == 0x4008) {
/* length counter */
/*
* il triangle ha una posizione diversa per il
* flag LCHalt.
*/
a->TR.length.halt = value & 0x80;
/* linear counter */
a->TR.linear.reload = value & 0x7F;
return;
}
if (address == 0x400A) {
/* timer (low 8 bits) */
a->TR.timer = (a->TR.timer & 0x0700) | value;
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.
*/
if (a->TR.length.enabled && !(a->apu.length_clocked && a->TR.length.value)) {
a->TR.length.value = length_table[value >> 3];
}
/* timer (high 3 bits) */
a->TR.timer = (a->TR.timer & 0x00FF) | ((value & 0x07) << 8);
/*
* scrivendo in questo registro si setta
* automaticamente l'halt flag del triangle.
*/
a->TR.linear.halt = TRUE;
return;
}
return;
}
/* --------------------- noise ----------------------*/
if (address <= 0x400F) {
if (address == 0x400C) {
a->NS.length.halt = value & 0x20;
/* envelope */
a->NS.envelope.constant_volume = value & 0x10;
a->NS.envelope.divider = value & 0x0F;
return;
}
if (address == 0x400E) {
a->NS.mode = value & 0x80;
a->NS.timer = value & 0x0F;
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.
*/
if (a->NS.length.enabled && !(a->apu.length_clocked && a->NS.length.value)) {
a->NS.length.value = length_table[value >> 3];
}
/* envelope */
a->NS.envelope.enabled = TRUE;
return;
}
return;
}
return;
} else {
/* ---------------------- DMC -----------------------*/
if (address <= 0x4013) {
if (address == 0x4010) {
a->DMC.irq_enabled = value & 0x80;
/* se l'irq viene disabilitato allora... */
if (!a->DMC.irq_enabled) {
/* ...azzero l'interrupt flag del DMC */
a->r4015.value &= 0x7F;
}
a->DMC.loop = value & 0x40;
a->DMC.rate_index = value & 0x0F;
return;
}
if (address == 0x4011) {
BYTE save = a->DMC.counter;
if (!(address & 0x0010)) {
/* -------------------- square 1 --------------------*/
if (address <= 0x4003) {
if (address == 0x4000) {
square_reg0(a->S1);
return;
}
if (address == 0x4001) {
square_reg1(a->S1);
sweep_silence(a->S1)
return;
}
if (address == 0x4002) {
square_reg2(a->S1);
sweep_silence(a->S1)
return;
}
if (address == 0x4003) {
square_reg3(a->S1,a->apu.length_clocked);
sweep_silence(a->S1)
return;
}
return;
}
/* -------------------- square 2 --------------------*/
if (address <= 0x4007) {
if (address == 0x4004) {
square_reg0(a->S2);
return;
}
if (address == 0x4005) {
square_reg1(a->S2);
sweep_silence(a->S2)
return;
}
if (address == 0x4006) {
square_reg2(a->S2);
sweep_silence(a->S2)
return;
}
if (address == 0x4007) {
square_reg3(a->S2,a->apu.length_clocked);
sweep_silence(a->S2)
return;
}
return;
}
/* -------------------- triangle --------------------*/
if (address <= 0x400B) {
if (address == 0x4008) {
/* length counter */
/*
* il triangle ha una posizione diversa per il
* flag LCHalt.
*/
a->TR.length.halt = value & 0x80;
/* linear counter */
a->TR.linear.reload = value & 0x7F;
return;
}
if (address == 0x400A) {
/* timer (low 8 bits) */
a->TR.timer = (a->TR.timer & 0x0700) | value;
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.
*/
if (a->TR.length.enabled && !(a->apu.length_clocked && a->TR.length.value)) {
a->TR.length.value = length_table[value >> 3];
}
/* timer (high 3 bits) */
a->TR.timer = (a->TR.timer & 0x00FF) | ((value & 0x07) << 8);
/*
* scrivendo in questo registro si setta
* automaticamente l'halt flag del triangle.
*/
a->TR.linear.halt = TRUE;
return;
}
return;
}
/* --------------------- noise ----------------------*/
if (address <= 0x400F) {
if (address == 0x400C) {
a->NS.length.halt = value & 0x20;
/* envelope */
a->NS.envelope.constant_volume = value & 0x10;
a->NS.envelope.divider = value & 0x0F;
return;
}
if (address == 0x400E) {
a->NS.mode = value & 0x80;
a->NS.timer = value & 0x0F;
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.
*/
if (a->NS.length.enabled && !(a->apu.length_clocked && a->NS.length.value)) {
a->NS.length.value = length_table[value >> 3];
}
/* envelope */
a->NS.envelope.enabled = TRUE;
return;
}
return;
}
return;
} else {
/* ---------------------- DMC -----------------------*/
if (address <= 0x4013) {
if (address == 0x4010) {
a->DMC.irq_enabled = value & 0x80;
/* se l'irq viene disabilitato allora... */
if (!a->DMC.irq_enabled) {
/* ...azzero l'interrupt flag del DMC */
a->r4015.value &= 0x7F;
}
a->DMC.loop = value & 0x40;
a->DMC.rate_index = value & 0x0F;
return;
}
if (address == 0x4011) {
BYTE save = a->DMC.counter;
value &= 0x7F;
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)
*/
if (a->r4011.frames > 1) {
a->r4011.output = (value - save) >> 3;
a->DMC.counter = a->DMC.output = save + a->r4011.output;
} else {
a->DMC.counter = a->DMC.output = value;
}
a->apu.clocked = TRUE;
/*
* 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)
*/
if (a->r4011.frames > 1) {
a->r4011.output = (value - save) >> 3;
a->DMC.counter = a->DMC.output = save + a->r4011.output;
} else {
a->DMC.counter = a->DMC.output = value;
}
a->apu.clocked = TRUE;
a->r4011.cycles = a->r4011.frames = 0;
a->r4011.value = value;
return;
}
if (address == 0x4012) {
a->DMC.address_start = (value << 6) | 0xC000;
return;
}
if (address == 0x4013) {
/* sample length */
a->DMC.length = (value << 4) | 0x01;
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.
*/
a->r4015.value = (a->r4015.value & 0x60) | (value & 0x1F);
/*
* quando il flag di abilitazione del length
* counter di ogni canale e' a 0, il counter
* dello stesso canale e' immediatamente azzerato.
*/
if (!(a->S1.length.enabled = a->r4015.value & 0x01)) {
a->S1.length.value = 0;
}
if (!(a->S2.length.enabled = a->r4015.value & 0x02)) {
a->S2.length.value = 0;
}
if (!(a->TR.length.enabled = a->r4015.value & 0x04)) {
a->TR.length.value = 0;
}
if (!(a->NS.length.enabled = a->r4015.value & 0x08)) {
a->NS.length.value = 0;
}
/*
* 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.
*/
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;
}
return;
}
a->r4011.cycles = a->r4011.frames = 0;
a->r4011.value = value;
return;
}
if (address == 0x4012) {
a->DMC.address_start = (value << 6) | 0xC000;
return;
}
if (address == 0x4013) {
/* sample length */
a->DMC.length = (value << 4) | 0x01;
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.
*/
a->r4015.value = (a->r4015.value & 0x60) | (value & 0x1F);
/*
* quando il flag di abilitazione del length
* counter di ogni canale e' a 0, il counter
* dello stesso canale e' immediatamente azzerato.
*/
if (!(a->S1.length.enabled = a->r4015.value & 0x01)) {
a->S1.length.value = 0;
}
if (!(a->S2.length.enabled = a->r4015.value & 0x02)) {
a->S2.length.value = 0;
}
if (!(a->TR.length.enabled = a->r4015.value & 0x04)) {
a->TR.length.value = 0;
}
if (!(a->NS.length.enabled = a->r4015.value & 0x08)) {
a->NS.length.value = 0;
}
/*
* 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.
*/
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;
}
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;
}
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 */
a->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 (a->apu.odd_cycle) {
a->r4017.jitter.delay = TRUE;
} else {
a->r4017.jitter.delay = FALSE;
r4017_jitter(1)
r4017_reset_frame()
}
return;
}
if (address == 0x4017) {
/* APU frame counter */
a->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 (a->apu.odd_cycle) {
a->r4017.jitter.delay = TRUE;
} else {
a->r4017.jitter.delay = FALSE;
r4017_jitter(1)
r4017_reset_frame()
}
return;
}
#endif
}
}
#if defined (DEBUG)
//fprintf(stderr, "Alert: Attempt to write APU port %04X\n", address);
//fprintf(stderr, "Alert: Attempt to write APU port %04X\n", address);
#endif
return;
return;
}
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;
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;
// 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);
}
}
}
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);
return (FALSE);
}
#endif /* CPU_INLINE_H_ */

View file

@ -29,110 +29,110 @@ void fds_reset(struct _fds* fds) {
}
void extcl_apu_tick_FDS(struct _fds* fds) {
SWORD freq;
SWORD freq;
/* volume unit */
if (fds->snd.volume.mode) {
fds->snd.volume.gain = fds->snd.volume.speed;
} else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) {
if (fds->snd.volume.counter) {
fds->snd.volume.counter--;
} else {
fds->snd.volume.counter = (fds->snd.envelope.speed << 3) * (fds->snd.volume.speed + 1);
if (fds->snd.volume.increase) {
if (fds->snd.volume.gain < 32) {
fds->snd.volume.gain++;
}
} else if (fds->snd.volume.gain) {
fds->snd.volume.gain--;
}
}
}
/* volume unit */
if (fds->snd.volume.mode) {
fds->snd.volume.gain = fds->snd.volume.speed;
} else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) {
if (fds->snd.volume.counter) {
fds->snd.volume.counter--;
} else {
fds->snd.volume.counter = (fds->snd.envelope.speed << 3) * (fds->snd.volume.speed + 1);
if (fds->snd.volume.increase) {
if (fds->snd.volume.gain < 32) {
fds->snd.volume.gain++;
}
} else if (fds->snd.volume.gain) {
fds->snd.volume.gain--;
}
}
}
/* sweep unit */
if (fds->snd.sweep.mode) {
fds->snd.sweep.gain = fds->snd.sweep.speed;
} else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) {
if (fds->snd.sweep.counter) {
fds->snd.sweep.counter--;
} else {
fds->snd.sweep.counter = (fds->snd.envelope.speed << 3) * (fds->snd.sweep.speed + 1);
if (fds->snd.sweep.increase) {
if (fds->snd.sweep.gain < 32) {
fds->snd.sweep.gain++;
}
} else if (fds->snd.sweep.gain) {
fds->snd.sweep.gain--;
}
}
}
/* sweep unit */
if (fds->snd.sweep.mode) {
fds->snd.sweep.gain = fds->snd.sweep.speed;
} else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) {
if (fds->snd.sweep.counter) {
fds->snd.sweep.counter--;
} else {
fds->snd.sweep.counter = (fds->snd.envelope.speed << 3) * (fds->snd.sweep.speed + 1);
if (fds->snd.sweep.increase) {
if (fds->snd.sweep.gain < 32) {
fds->snd.sweep.gain++;
}
} else if (fds->snd.sweep.gain) {
fds->snd.sweep.gain--;
}
}
}
/* modulation unit */
freq = fds->snd.main.frequency;
/* modulation unit */
freq = fds->snd.main.frequency;
if (!fds->snd.modulation.disabled && fds->snd.modulation.frequency) {
if ((fds->snd.modulation.counter -= fds->snd.modulation.frequency) < 0) {
SWORD temp, temp2, a, d;
SBYTE adj = fds->snd.modulation.data[fds->snd.modulation.index];
if (!fds->snd.modulation.disabled && fds->snd.modulation.frequency) {
if ((fds->snd.modulation.counter -= fds->snd.modulation.frequency) < 0) {
SWORD temp, temp2, a, d;
SBYTE adj = fds->snd.modulation.data[fds->snd.modulation.index];
fds->snd.modulation.counter += 65536;
fds->snd.modulation.counter += 65536;
if (++fds->snd.modulation.index == 64) {
fds->snd.modulation.index = 0;
}
if (++fds->snd.modulation.index == 64) {
fds->snd.modulation.index = 0;
}
if (adj == -4) {
fds->snd.sweep.bias = 0;
} else {
fds->snd.sweep.bias += adj;
}
if (adj == -4) {
fds->snd.sweep.bias = 0;
} else {
fds->snd.sweep.bias += adj;
}
temp = fds->snd.sweep.bias * ((fds->snd.sweep.gain < 32) ? fds->snd.sweep.gain : 32);
temp = fds->snd.sweep.bias * ((fds->snd.sweep.gain < 32) ? fds->snd.sweep.gain : 32);
a = 64;
d = 0;
a = 64;
d = 0;
if (temp <= 0) {
d = 15;
} else if (temp < 3040) { //95 * 32
a = 66;
d = -31;
}
if (temp <= 0) {
d = 15;
} else if (temp < 3040) { //95 * 32
a = 66;
d = -31;
}
temp2 = a + (SBYTE) ((temp - d) / 16 - a);
temp2 = a + (SBYTE) ((temp - d) / 16 - a);
fds->snd.modulation.mod = freq * temp2 / 64;
}
fds->snd.modulation.mod = freq * temp2 / 64;
}
if (freq) {
freq += fds->snd.modulation.mod;
}
}
if (freq) {
freq += fds->snd.modulation.mod;
}
}
/* main unit */
if (fds->snd.main.silence) {
fds->snd.main.output = 0;
return;
}
/* main unit */
if (fds->snd.main.silence) {
fds->snd.main.output = 0;
return;
}
if (freq && !fds->snd.wave.writable) {
if ((fds->snd.wave.counter -= freq) < 0) {
WORD level;
if (freq && !fds->snd.wave.writable) {
if ((fds->snd.wave.counter -= freq) < 0) {
WORD level;
fds->snd.wave.counter += 65536;
fds->snd.wave.counter += 65536;
level = (fds->snd.volume.gain < 32 ? fds->snd.volume.gain : 32)
* volume_wave[fds->snd.wave.volume];
level = (fds->snd.volume.gain < 32 ? fds->snd.volume.gain : 32)
* volume_wave[fds->snd.wave.volume];
/* valore massimo dell'output (63 * (39 * 32)) = 78624 */
/*fds->snd.main.output = (fds->snd.wave.data[fds->snd.wave.index] * level) >> 4;*/
fds->snd.main.output = (fds->snd.wave.data[fds->snd.wave.index] * level) >> 3;
/* valore massimo dell'output (63 * (39 * 32)) = 78624 */
/*fds->snd.main.output = (fds->snd.wave.data[fds->snd.wave.index] * level) >> 4;*/
fds->snd.main.output = (fds->snd.wave.data[fds->snd.wave.index] * level) >> 3;
if (++fds->snd.wave.index == 64) {
fds->snd.wave.index = 0;
}
if (++fds->snd.wave.index == 64) {
fds->snd.wave.index = 0;
}
fds->snd.wave.clocked = TRUE;
}
}
fds->snd.wave.clocked = TRUE;
}
}
}

View file

@ -30,60 +30,60 @@ enum fds_operations { FDS_OP_NONE, FDS_OP_READ, FDS_OP_WRITE };
#endif
EXTERNC struct _fds {
// snd
// snd
BYTE enabled_snd_reg;
struct _fds_snd {
struct _fds_snd_wave {
BYTE data[64];
BYTE writable;
BYTE volume;
struct _fds_snd {
struct _fds_snd_wave {
BYTE data[64];
BYTE writable;
BYTE volume;
BYTE index;
int32_t counter;
BYTE index;
int32_t counter;
/* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */
/* */ BYTE clocked; /* */
/* ------------------------------------------------------- */
} wave;
struct _fds_snd_envelope {
BYTE speed;
BYTE disabled;
} envelope;
struct _fds_snd_main {
BYTE silence;
WORD frequency;
/* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */
/* */ BYTE clocked; /* */
/* ------------------------------------------------------- */
} wave;
struct _fds_snd_envelope {
BYTE speed;
BYTE disabled;
} envelope;
struct _fds_snd_main {
BYTE silence;
WORD frequency;
SWORD output;
} main;
struct _fds_snd_volume {
BYTE speed;
BYTE mode;
BYTE increase;
SWORD output;
} main;
struct _fds_snd_volume {
BYTE speed;
BYTE mode;
BYTE increase;
BYTE gain;
uint32_t counter;
} volume;
struct _fds_snd_sweep {
SBYTE bias;
BYTE mode;
BYTE increase;
BYTE speed;
BYTE gain;
uint32_t counter;
} volume;
struct _fds_snd_sweep {
SBYTE bias;
BYTE mode;
BYTE increase;
BYTE speed;
BYTE gain;
uint32_t counter;
} sweep;
struct _fds_snd_modulation {
SBYTE data[64];
WORD frequency;
BYTE disabled;
BYTE gain;
uint32_t counter;
} sweep;
struct _fds_snd_modulation {
SBYTE data[64];
WORD frequency;
BYTE disabled;
BYTE index;
int32_t counter;
SWORD mod;
} modulation;
} snd;
BYTE index;
int32_t counter;
SWORD mod;
} modulation;
} snd;
};
EXTERNC void extcl_apu_tick_FDS(struct _fds* fds);

View file

@ -28,93 +28,93 @@ const BYTE filler_attrib[4] = {0x00, 0x55, 0xAA, 0xFF};
BYTE prg_ram_mode;
void map_init_MMC5(struct _mmc5* mmc5) {
memset(mmc5,0,sizeof(struct _mmc5));
memset(mmc5,0,sizeof(struct _mmc5));
mmc5->S3.frequency = 1;
mmc5->S4.frequency = 1;
mmc5->S3.length.enabled = 0;
mmc5->S3.length.value = 0;
mmc5->S4.length.enabled = 0;
mmc5->S4.length.value = 0;
mmc5->S3.frequency = 1;
mmc5->S4.frequency = 1;
mmc5->S3.length.enabled = 0;
mmc5->S3.length.value = 0;
mmc5->S4.length.enabled = 0;
mmc5->S4.length.value = 0;
}
void extcl_cpu_wr_mem_MMC5(struct _mmc5* mmc5, WORD address, BYTE value) {
if (address < 0x5000) {
return;
}
if (address < 0x5000) {
return;
}
switch (address) {
case 0x5000:
square_reg0(mmc5->S3);
return;
case 0x5001:
/* lo sweep non e' utilizzato */
return;
case 0x5002:
square_reg2(mmc5->S3);
return;
case 0x5003:
square_reg3(mmc5->S3,0);
return;
case 0x5004:
square_reg0(mmc5->S4);
return;
case 0x5005:
/* lo sweep non e' utilizzato */
return;
case 0x5006:
square_reg2(mmc5->S4);
return;
case 0x5007:
square_reg3(mmc5->S4,0);
return;
case 0x5010:
mmc5->pcm.enabled = ~value & 0x01;
mmc5->pcm.output = 0;
if (mmc5->pcm.enabled) {
mmc5->pcm.output = mmc5->pcm.amp;
}
mmc5->clocked = TRUE;
return;
case 0x5011:
mmc5->pcm.amp = value;
mmc5->pcm.output = 0;
if (mmc5->pcm.enabled) {
mmc5->pcm.output = mmc5->pcm.amp;
}
mmc5->clocked = TRUE;
return;
case 0x5015:
if (!(mmc5->S3.length.enabled = value & 0x01)) {
mmc5->S3.length.value = 0;
}
if (!(mmc5->S4.length.enabled = value & 0x02)) {
mmc5->S4.length.value = 0;
}
return;
}
switch (address) {
case 0x5000:
square_reg0(mmc5->S3);
return;
case 0x5001:
/* lo sweep non e' utilizzato */
return;
case 0x5002:
square_reg2(mmc5->S3);
return;
case 0x5003:
square_reg3(mmc5->S3,0);
return;
case 0x5004:
square_reg0(mmc5->S4);
return;
case 0x5005:
/* lo sweep non e' utilizzato */
return;
case 0x5006:
square_reg2(mmc5->S4);
return;
case 0x5007:
square_reg3(mmc5->S4,0);
return;
case 0x5010:
mmc5->pcm.enabled = ~value & 0x01;
mmc5->pcm.output = 0;
if (mmc5->pcm.enabled) {
mmc5->pcm.output = mmc5->pcm.amp;
}
mmc5->clocked = TRUE;
return;
case 0x5011:
mmc5->pcm.amp = value;
mmc5->pcm.output = 0;
if (mmc5->pcm.enabled) {
mmc5->pcm.output = mmc5->pcm.amp;
}
mmc5->clocked = TRUE;
return;
case 0x5015:
if (!(mmc5->S3.length.enabled = value & 0x01)) {
mmc5->S3.length.value = 0;
}
if (!(mmc5->S4.length.enabled = value & 0x02)) {
mmc5->S4.length.value = 0;
}
return;
}
}
void extcl_length_clock_MMC5(struct _mmc5* mmc5) {
length_run(mmc5->S3)
length_run(mmc5->S4)
length_run(mmc5->S3)
length_run(mmc5->S4)
}
void extcl_envelope_clock_MMC5(struct _mmc5* mmc5) {
envelope_run(mmc5->S3)
envelope_run(mmc5->S4)
envelope_run(mmc5->S3)
envelope_run(mmc5->S4)
}
void extcl_apu_tick_MMC5(struct _mmc5* mmc5) {
// SQUARE 3 TICK
if (!(--mmc5->S3.frequency)) {
square_output(mmc5->S3, 0)
mmc5->S3.frequency = (mmc5->S3.timer + 1) << 1;
mmc5->S3.sequencer = (mmc5->S3.sequencer + 1) & 0x07;
mmc5->clocked = TRUE;
}
if (!(--mmc5->S3.frequency)) {
square_output(mmc5->S3, 0)
mmc5->S3.frequency = (mmc5->S3.timer + 1) << 1;
mmc5->S3.sequencer = (mmc5->S3.sequencer + 1) & 0x07;
mmc5->clocked = TRUE;
}
// SQUARE 4 TICK
if (!(--mmc5->S4.frequency)) {
square_output(mmc5->S4, 0)
mmc5->S4.frequency = (mmc5->S4.timer + 1) << 1;
mmc5->S4.sequencer = (mmc5->S4.sequencer + 1) & 0x07;
mmc5->clocked = TRUE;
}
if (!(--mmc5->S4.frequency)) {
square_output(mmc5->S4, 0)
mmc5->S4.frequency = (mmc5->S4.timer + 1) << 1;
mmc5->S4.sequencer = (mmc5->S4.sequencer + 1) & 0x07;
mmc5->clocked = TRUE;
}
}

View file

@ -28,45 +28,45 @@
#endif
EXTERNC struct _mmc5 {
BYTE prg_mode;
BYTE chr_mode;
BYTE ext_mode;
BYTE nmt_mode[4];
BYTE prg_ram_write[2];
BYTE prg_bank[4];
uint32_t prg_ram_bank[4][2];
BYTE chr_last;
WORD chr_high;
WORD chr_s[8];
WORD chr_b[4];
BYTE ext_ram[0x400];
BYTE fill_table[0x400];
BYTE fill_tile;
BYTE fill_attr;
BYTE split;
BYTE split_st_tile;
BYTE split_side;
BYTE split_scrl;
BYTE split_in_reg;
BYTE split_x;
BYTE split_y;
WORD split_tile;
uint32_t split_bank;
BYTE factor[2];
WORD product;
_apuSquare S3, S4;
struct _mmc5_pcm {
BYTE enabled;
BYTE output;
BYTE amp;
} pcm;
BYTE filler[50];
BYTE prg_mode;
BYTE chr_mode;
BYTE ext_mode;
BYTE nmt_mode[4];
BYTE prg_ram_write[2];
BYTE prg_bank[4];
uint32_t prg_ram_bank[4][2];
BYTE chr_last;
WORD chr_high;
WORD chr_s[8];
WORD chr_b[4];
BYTE ext_ram[0x400];
BYTE fill_table[0x400];
BYTE fill_tile;
BYTE fill_attr;
BYTE split;
BYTE split_st_tile;
BYTE split_side;
BYTE split_scrl;
BYTE split_in_reg;
BYTE split_x;
BYTE split_y;
WORD split_tile;
uint32_t split_bank;
BYTE factor[2];
WORD product;
_apuSquare S3, S4;
struct _mmc5_pcm {
BYTE enabled;
BYTE output;
BYTE amp;
} pcm;
BYTE filler[50];
/* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */
/* */ BYTE clocked; /* */
/* ------------------------------------------------------- */
/* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */
/* */ BYTE clocked; /* */
/* ------------------------------------------------------- */
};
EXTERNC void map_init_MMC5(struct _mmc5* mmc5);