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

View file

@ -31,384 +31,384 @@ enum apu_mode { APU_60HZ, APU_48HZ };
/* length counter */ /* length counter */
#define length_run(channel)\ #define length_run(channel)\
/*\ /*\
* se non e' settato il flag halt e il length\ * se non e' settato il flag halt e il length\
* counter non e' 0 allora devo decrementarlo.\ * counter non e' 0 allora devo decrementarlo.\
*/\ */\
if (!channel.length.halt && channel.length.value) {\ if (!channel.length.halt && channel.length.value) {\
channel.length.value--;\ channel.length.value--;\
} }
#define length_clock()\ #define length_clock()\
a->apu.length_clocked = TRUE;\ a->apu.length_clocked = TRUE;\
length_run(a->S1)\ length_run(a->S1)\
length_run(a->S2)\ length_run(a->S2)\
length_run(a->TR)\ length_run(a->TR)\
length_run(a->NS) length_run(a->NS)
/* envelope */ /* envelope */
#define envelope_run(channel)\ #define envelope_run(channel)\
if (channel.envelope.enabled) {\ if (channel.envelope.enabled) {\
channel.envelope.enabled = FALSE;\ channel.envelope.enabled = FALSE;\
channel.envelope.counter = 15;\ channel.envelope.counter = 15;\
channel.envelope.delay = (channel.envelope.divider + 1);\ channel.envelope.delay = (channel.envelope.divider + 1);\
} else if (!(--channel.envelope.delay)) {\ } else if (!(--channel.envelope.delay)) {\
channel.envelope.delay = (channel.envelope.divider + 1);\ channel.envelope.delay = (channel.envelope.divider + 1);\
if (channel.envelope.counter | channel.length.halt) {\ if (channel.envelope.counter | channel.length.halt) {\
channel.envelope.counter = (channel.envelope.counter - 1) & 0x0F;\ channel.envelope.counter = (channel.envelope.counter - 1) & 0x0F;\
}\ }\
} }
#define envelope_volume(channel)\ #define envelope_volume(channel)\
/* setto il volume */\ /* setto il volume */\
if (!channel.length.value) {\ if (!channel.length.value) {\
channel.volume = 0;\ channel.volume = 0;\
} else if (channel.envelope.constant_volume) {\ } else if (channel.envelope.constant_volume) {\
channel.volume = channel.envelope.divider;\ channel.volume = channel.envelope.divider;\
} else {\ } else {\
channel.volume = channel.envelope.counter;\ channel.volume = channel.envelope.counter;\
} }
#define envelope_clock()\ #define envelope_clock()\
envelope_run(a->S1)\ envelope_run(a->S1)\
envelope_run(a->S2)\ envelope_run(a->S2)\
envelope_run(a->NS) envelope_run(a->NS)
/* sweep */ /* sweep */
#define sweep_run(channel, negative_adjust)\ #define sweep_run(channel, negative_adjust)\
if (!(--channel.sweep.delay)) {\ if (!(--channel.sweep.delay)) {\
channel.sweep.delay = (channel.sweep.divider + 1);\ channel.sweep.delay = (channel.sweep.divider + 1);\
if (channel.sweep.enabled && channel.sweep.shift && (channel.timer >= 8)) {\ if (channel.sweep.enabled && channel.sweep.shift && (channel.timer >= 8)) {\
SWORD offset = channel.timer >> channel.sweep.shift;\ SWORD offset = channel.timer >> channel.sweep.shift;\
if (channel.sweep.negate) {\ if (channel.sweep.negate) {\
channel.timer += ((SWORD) negative_adjust - offset);\ channel.timer += ((SWORD) negative_adjust - offset);\
} else if ((channel.timer + offset) <= 0x800) {\ } else if ((channel.timer + offset) <= 0x800) {\
channel.timer += offset;\ channel.timer += offset;\
}\ }\
}\ }\
sweep_silence(channel)\ sweep_silence(channel)\
}\ }\
if (channel.sweep.reload) {\ if (channel.sweep.reload) {\
channel.sweep.reload = FALSE;\ channel.sweep.reload = FALSE;\
channel.sweep.delay = (channel.sweep.divider + 1);\ channel.sweep.delay = (channel.sweep.divider + 1);\
} }
#define sweep_silence(channel)\ #define sweep_silence(channel)\
{\ {\
WORD offset = channel.timer >> channel.sweep.shift;\ WORD offset = channel.timer >> channel.sweep.shift;\
channel.sweep.silence = FALSE;\ channel.sweep.silence = FALSE;\
if ((channel.timer <= 8) || (!channel.sweep.negate && ((channel.timer + offset) >= 0x800))) {\ if ((channel.timer <= 8) || (!channel.sweep.negate && ((channel.timer + offset) >= 0x800))) {\
channel.sweep.silence = TRUE;\ channel.sweep.silence = TRUE;\
}\ }\
} }
#define sweep_clock()\ #define sweep_clock()\
sweep_run(a->S1, -1)\ sweep_run(a->S1, -1)\
sweep_run(a->S2, 0) sweep_run(a->S2, 0)
/* linear counter */ /* linear counter */
#define linear_clock()\ #define linear_clock()\
if (a->TR.linear.halt) {\ if (a->TR.linear.halt) {\
a->TR.linear.value = a->TR.linear.reload;\ a->TR.linear.value = a->TR.linear.reload;\
} else if (a->TR.linear.value) {\ } else if (a->TR.linear.value) {\
a->TR.linear.value--;\ a->TR.linear.value--;\
}\ }\
if (!a->TR.length.halt) {\ if (!a->TR.length.halt) {\
a->TR.linear.halt = FALSE;\ a->TR.linear.halt = FALSE;\
} }
/* output */ /* output */
#define square_output(square, swap)\ #define square_output(square, swap)\
{\ {\
envelope_volume(square)\ envelope_volume(square)\
if (square.sweep.silence) {\ if (square.sweep.silence) {\
square.output = 0;\ square.output = 0;\
} else {\ } else {\
square.output = square_duty[swap][square.duty][square.sequencer] * square.volume;\ square.output = square_duty[swap][square.duty][square.sequencer] * square.volume;\
}\ }\
} }
#define triangle_output()\ #define triangle_output()\
/*\ /*\
* ai 2 cicli piu' bassi del timer, la frequenza\ * ai 2 cicli piu' bassi del timer, la frequenza\
* risultante e' troppo alta (oltre i 20 kHz,\ * risultante e' troppo alta (oltre i 20 kHz,\
* quindi non udibile), percio' la taglio.\ * quindi non udibile), percio' la taglio.\
*/\ */\
a->TR.output = triangle_duty[a->TR.sequencer];\ a->TR.output = triangle_duty[a->TR.sequencer];\
if (a->TR.timer < 2) {\ if (a->TR.timer < 2) {\
a->TR.output = triangle_duty[8];\ a->TR.output = triangle_duty[8];\
} }
#define noise_output()\ #define noise_output()\
envelope_volume(a->NS)\ envelope_volume(a->NS)\
a->NS.output = 0;\ a->NS.output = 0;\
if (a->NS.length.value && !(a->NS.shift & 0x0001)) {\ if (a->NS.length.value && !(a->NS.shift & 0x0001)) {\
a->NS.output = a->NS.volume;\ a->NS.output = a->NS.volume;\
} }
#define dmc_output()\ #define dmc_output()\
a->DMC.output = a->DMC.counter & 0x7F a->DMC.output = a->DMC.counter & 0x7F
/* tick */ /* tick */
#define apu_change_step(index)\ #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) #if defined (VECCHIA_GESTIONE_JITTER)
#define r4017_jitter()\ #define r4017_jitter()\
a->r4017.value = (a->r4017.jitter.value & 0xC0);\ a->r4017.value = (a->r4017.jitter.value & 0xC0);\
/*\ /*\
* se il bit 7 e' a zero, devo attivare la\ * se il bit 7 e' a zero, devo attivare la\
* modalita' NTSC, se a uno quella PAL.\ * modalita' NTSC, se a uno quella PAL.\
*/\ */\
if (a->r4017.value & 0x80) {\ if (a->r4017.value & 0x80) {\
a->apu.mode = APU_48HZ;\ a->apu.mode = APU_48HZ;\
} else {\ } else {\
a->apu.mode = APU_60HZ;\ a->apu.mode = APU_60HZ;\
}\ }\
if (a->r4017.value & 0x40) {\ if (a->r4017.value & 0x40) {\
/* azzero il bit 6 del $4015 */\ /* azzero il bit 6 del $4015 */\
a->r4015.value &= 0xBF;\ a->r4015.value &= 0xBF;\
/* questo non e' affatto necessario sul forno */\ /* questo non e' affatto necessario sul forno */\
}\ }\
/* riavvio il frame audio */\ /* riavvio il frame audio */\
a->apu.step = a->apu.cycles = 0;\ a->apu.step = a->apu.cycles = 0;\
apu_change_step(a->apu.step) apu_change_step(a->apu.step)
#else #else
#define r4017_jitter(apc)\ #define r4017_jitter(apc)\
a->r4017.value = (a->r4017.jitter.value & 0xC0);\ a->r4017.value = (a->r4017.jitter.value & 0xC0);\
a->r4017.reset_frame_delay = 1;\ a->r4017.reset_frame_delay = 1;\
if (a->apu.cycles == apc) {\ if (a->apu.cycles == apc) {\
if (a->apu.mode == APU_48HZ) {\ if (a->apu.mode == APU_48HZ) {\
a->r4017.reset_frame_delay += 1;\ a->r4017.reset_frame_delay += 1;\
} else {\ } else {\
a->r4017.reset_frame_delay += 2;\ a->r4017.reset_frame_delay += 2;\
}\ }\
}\ }\
/*\ /*\
* se il bit 7 e' a zero, devo attivare la\ * se il bit 7 e' a zero, devo attivare la\
* modalita' NTSC, se a uno quella PAL.\ * modalita' NTSC, se a uno quella PAL.\
*/\ */\
if (a->r4017.value & 0x80) {\ if (a->r4017.value & 0x80) {\
a->apu.mode = APU_48HZ;\ a->apu.mode = APU_48HZ;\
} else {\ } else {\
a->apu.mode = APU_60HZ;\ a->apu.mode = APU_60HZ;\
}\ }\
if (a->r4017.value & 0x40) {\ if (a->r4017.value & 0x40) {\
/* azzero il bit 6 del $4015 */\ /* azzero il bit 6 del $4015 */\
a->r4015.value &= 0xBF;\ a->r4015.value &= 0xBF;\
/* questo non e' affatto necessario sul forno */\ /* questo non e' affatto necessario sul forno */\
} }
#define r4017_reset_frame()\ #define r4017_reset_frame()\
if (a->r4017.reset_frame_delay && (--a->r4017.reset_frame_delay == 0)) {\ if (a->r4017.reset_frame_delay && (--a->r4017.reset_frame_delay == 0)) {\
/* riavvio il frame audio */\ /* riavvio il frame audio */\
a->apu.step = a->apu.cycles = 0;\ a->apu.step = a->apu.cycles = 0;\
apu_change_step(a->apu.step);\ apu_change_step(a->apu.step);\
} }
#endif #endif
#define square_reg0(square)\ #define square_reg0(square)\
/* duty */\ /* duty */\
square.duty = value >> 6;\ square.duty = value >> 6;\
/* length counter */\ /* length counter */\
square.length.halt = value & 0x20;\ square.length.halt = value & 0x20;\
/* envelope */\ /* envelope */\
square.envelope.constant_volume = value & 0x10;\ square.envelope.constant_volume = value & 0x10;\
square.envelope.divider = value & 0x0F square.envelope.divider = value & 0x0F
#define square_reg1(square)\ #define square_reg1(square)\
/* sweep */\ /* sweep */\
square.sweep.reload = TRUE;\ square.sweep.reload = TRUE;\
square.sweep.divider = (value >> 4) & 0x07;\ square.sweep.divider = (value >> 4) & 0x07;\
square.sweep.shift = value & 0x07;\ square.sweep.shift = value & 0x07;\
square.sweep.enabled = value & 0x80;\ square.sweep.enabled = value & 0x80;\
square.sweep.negate = value & 0x08 square.sweep.negate = value & 0x08
#define square_reg2(square)\ #define square_reg2(square)\
/* timer (low 8 bits) */\ /* timer (low 8 bits) */\
square.timer = (square.timer & 0x0700) | value square.timer = (square.timer & 0x0700) | value
#define square_reg3(square,length_clocked)\ #define square_reg3(square,length_clocked)\
/* length counter */\ /* length counter */\
/*\ /*\
* se non disabilitato, una scrittura in\ * se non disabilitato, una scrittura in\
* questo registro, carica immediatamente il\ * questo registro, carica immediatamente il\
* length counter del canale, tranne nel caso\ * length counter del canale, tranne nel caso\
* in cui la scrittura avvenga nello stesso\ * in cui la scrittura avvenga nello stesso\
* momento del clock di un length counter e\ * momento del clock di un length counter e\
* con il length diverso da zero.\ * con il length diverso da zero.\
*/\ */\
if (square.length.enabled && !(length_clocked && square.length.value)) {\ if (square.length.enabled && !(length_clocked && square.length.value)) {\
square.length.value = length_table[value >> 3];\ square.length.value = length_table[value >> 3];\
}\ }\
/* envelope */\ /* envelope */\
square.envelope.enabled = TRUE;\ square.envelope.enabled = TRUE;\
/* timer (high 3 bits) */\ /* timer (high 3 bits) */\
square.timer = (square.timer & 0x00FF) | ((value & 0x07) << 8);\ square.timer = (square.timer & 0x00FF) | ((value & 0x07) << 8);\
/*The correct behaviour is to reset the duty cycle sequencers but not the clock dividers*/\ /*The correct behaviour is to reset the duty cycle sequencers but not the clock dividers*/\
/*square.frequency = 1;*/\ /*square.frequency = 1;*/\
/* sequencer */\ /* sequencer */\
square.sequencer = 0 square.sequencer = 0
#define init_nla_table(p, t)\ #define init_nla_table(p, t)\
{\ {\
WORD i;\ WORD i;\
for (i = 0; i < LENGTH(nla_table.pulse); i++) {\ for (i = 0; i < LENGTH(nla_table.pulse); i++) {\
double vl = 95.52 / (8128.0 / (double) i + 100.0);\ double vl = 95.52 / (8128.0 / (double) i + 100.0);\
nla_table.pulse[i] = (vl * p);\ nla_table.pulse[i] = (vl * p);\
}\ }\
for (i = 0; i < LENGTH(nla_table.tnd); i++) {\ for (i = 0; i < LENGTH(nla_table.tnd); i++) {\
double vl = 163.67 / (24329.0 / (double) i + 100.0);\ double vl = 163.67 / (24329.0 / (double) i + 100.0);\
nla_table.tnd[i] = (vl * t);\ nla_table.tnd[i] = (vl * t);\
}\ }\
} }
#define _apu_channel_volume_adjust(ch, index)\ #define _apu_channel_volume_adjust(ch, index)\
((ch)) ((ch))
#define s1_out(a)\ #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)\ #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)\ #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)\ #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)\ #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)\ #define extra_out(ch)\
(ch * cfg->apu.channel[APU_EXTRA]) (ch * cfg->apu.channel[APU_EXTRA])
#define pulse_output(a)\ #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)\ #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 { typedef struct _config_apu {
BYTE channel[APU_MASTER + 1]; BYTE channel[APU_MASTER + 1];
double volume[APU_MASTER + 1]; double volume[APU_MASTER + 1];
} _config_apu; } _config_apu;
typedef struct _apu { typedef struct _apu {
BYTE mode; BYTE mode;
BYTE type; BYTE type;
BYTE step; BYTE step;
BYTE length_clocked; BYTE length_clocked;
BYTE DMC; BYTE DMC;
SWORD cycles; SWORD cycles;
int cpu_cycles; int cpu_cycles;
int cpu_opcode_cycle; int cpu_opcode_cycle;
BYTE odd_cycle; BYTE odd_cycle;
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */ /* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
/* */ BYTE clocked; /* */ /* */ BYTE clocked; /* */
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
} _apu; } _apu;
typedef struct _r4011 { typedef struct _r4011 {
BYTE value; BYTE value;
DBWORD frames; DBWORD frames;
DBWORD cycles; DBWORD cycles;
SWORD output; SWORD output;
} _r4011; } _r4011;
typedef struct _r4015 { typedef struct _r4015 {
BYTE value; BYTE value;
} _r4015; } _r4015;
typedef struct _r4017 { typedef struct _r4017 {
BYTE value; BYTE value;
struct _r4017_litter { struct _r4017_litter {
BYTE value; BYTE value;
BYTE delay; BYTE delay;
} jitter; } jitter;
BYTE reset_frame_delay; BYTE reset_frame_delay;
} _r4017; } _r4017;
typedef struct _envelope { typedef struct _envelope {
BYTE enabled; BYTE enabled;
BYTE divider; BYTE divider;
BYTE counter; BYTE counter;
BYTE constant_volume; BYTE constant_volume;
SBYTE delay; SBYTE delay;
} _envelope; } _envelope;
typedef struct _sweep { typedef struct _sweep {
BYTE enabled; BYTE enabled;
BYTE negate; BYTE negate;
BYTE divider; BYTE divider;
BYTE shift; BYTE shift;
BYTE reload; BYTE reload;
BYTE silence; BYTE silence;
SBYTE delay; SBYTE delay;
} _sweep; } _sweep;
typedef struct _length_counter { typedef struct _length_counter {
BYTE value; BYTE value;
BYTE enabled; BYTE enabled;
BYTE halt; BYTE halt;
} _length_counter; } _length_counter;
typedef struct _linear_counter { typedef struct _linear_counter {
BYTE value; BYTE value;
BYTE reload; BYTE reload;
BYTE halt; BYTE halt;
} _linear_counter; } _linear_counter;
typedef struct _apuSquare { typedef struct _apuSquare {
/* timer */ /* timer */
DBWORD timer; DBWORD timer;
/* ogni quanti cicli devo generare un output */ /* ogni quanti cicli devo generare un output */
WORD frequency; WORD frequency;
/* duty */ /* duty */
BYTE duty; BYTE duty;
/* envelope */ /* envelope */
_envelope envelope; _envelope envelope;
/* volume */ /* volume */
BYTE volume; BYTE volume;
/* sequencer */ /* sequencer */
BYTE sequencer; BYTE sequencer;
/* sweep */ /* sweep */
_sweep sweep; _sweep sweep;
/* length counter */ /* length counter */
_length_counter length; _length_counter length;
/* output */ /* output */
SWORD output; SWORD output;
} _apuSquare; } _apuSquare;
typedef struct _apuTriangle { typedef struct _apuTriangle {
/* timer */ /* timer */
DBWORD timer; DBWORD timer;
/* ogni quanti cicli devo generare un output */ /* ogni quanti cicli devo generare un output */
WORD frequency; WORD frequency;
/* linear counter */ /* linear counter */
_linear_counter linear; _linear_counter linear;
/* length counter */ /* length counter */
_length_counter length; _length_counter length;
/* sequencer */ /* sequencer */
BYTE sequencer; BYTE sequencer;
/* output */ /* output */
SWORD output; SWORD output;
} _apuTriangle; } _apuTriangle;
typedef struct _apuNoise { typedef struct _apuNoise {
/* timer */ /* timer */
DBWORD timer; DBWORD timer;
/* ogni quanti cicli devo generare un output */ /* ogni quanti cicli devo generare un output */
WORD frequency; WORD frequency;
/* envelope */ /* envelope */
_envelope envelope; _envelope envelope;
/* specifico del noise */ /* specifico del noise */
BYTE mode; BYTE mode;
/* volume */ /* volume */
BYTE volume; BYTE volume;
/* shift register */ /* shift register */
WORD shift; WORD shift;
/* length counter */ /* length counter */
_length_counter length; _length_counter length;
/* sequencer */ /* sequencer */
BYTE sequencer; BYTE sequencer;
/* output */ /* output */
SWORD output; SWORD output;
} _apuNoise; } _apuNoise;
typedef struct _apuDMC { typedef struct _apuDMC {
/* ogni quanti cicli devo generare un output */ /* ogni quanti cicli devo generare un output */
WORD frequency; WORD frequency;
WORD remain; WORD remain;
BYTE irq_enabled; BYTE irq_enabled;
BYTE loop; BYTE loop;
BYTE rate_index; BYTE rate_index;
WORD address_start; WORD address_start;
DBWORD address; DBWORD address;
WORD length; WORD length;
BYTE counter; BYTE counter;
BYTE empty; BYTE empty;
BYTE buffer; BYTE buffer;
/* DMA */ /* DMA */
BYTE dma_cycle; BYTE dma_cycle;
/* output unit */ /* output unit */
BYTE silence; BYTE silence;
BYTE shift; BYTE shift;
BYTE counter_out; BYTE counter_out;
/* output */ /* output */
SWORD output; SWORD output;
/* misc */ /* misc */
BYTE tick_type; BYTE tick_type;
} _apuDMC; } _apuDMC;
#if defined (__cplusplus) #if defined (__cplusplus)
@ -418,8 +418,8 @@ typedef struct _apuDMC {
#endif #endif
EXTERNC struct _nla_table { EXTERNC struct _nla_table {
SWORD pulse[32]; SWORD pulse[32];
SWORD tnd[203]; SWORD tnd[203];
}; };
extern struct _nla_table nla_table; extern struct _nla_table nla_table;
@ -440,104 +440,104 @@ EXTERNC struct NESAPU {
/* apuPeriod[mode][type][cycles] */ /* apuPeriod[mode][type][cycles] */
static const WORD apuPeriod[2][3][7] = { static const WORD apuPeriod[2][3][7] = {
/* /*
* Mode 0: 4-step sequence * Mode 0: 4-step sequence
* Action Envelopes & Length Counter& Interrupt Delay to next * Action Envelopes & Length Counter& Interrupt Delay to next
* Linear Counter Sweep Units Flag NTSC PAL Dendy * Linear Counter Sweep Units Flag NTSC PAL Dendy
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* $4017=$00 - - - 7459 8315 7459 * $4017=$00 - - - 7459 8315 7459
* Step 1 Clock - - 7456 8314 7456 * Step 1 Clock - - 7456 8314 7456
* Step 2 Clock Clock - 7458 8312 7458 * Step 2 Clock Clock - 7458 8312 7458
* Step 3 Clock - - 7458 8314 7458 * Step 3 Clock - - 7458 8314 7458
* Step 4 Clock Clock Set if enabled 7458 8314 7458 * Step 4 Clock Clock Set if enabled 7458 8314 7458
*/ */
{ {
{7459, 7456, 7458, 7457, 1, 1, 7457}, {7459, 7456, 7458, 7457, 1, 1, 7457},
{8315, 8314, 8312, 8313, 1, 1, 8313}, {8315, 8314, 8312, 8313, 1, 1, 8313},
{7459, 7456, 7458, 7457, 1, 1, 7457} {7459, 7456, 7458, 7457, 1, 1, 7457}
}, },
/* /*
* Mode 1: 5-step sequence * Mode 1: 5-step sequence
* Action Envelopes & Length Counter& Interrupt Delay to next * Action Envelopes & Length Counter& Interrupt Delay to next
* Linear Counter Sweep Units Flag NTSC PAL Dendy * Linear Counter Sweep Units Flag NTSC PAL Dendy
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* $4017=$80 - - - 1 1 1 * $4017=$80 - - - 1 1 1
* Step 1 Clock Clock - 7458 8314 7458 * Step 1 Clock Clock - 7458 8314 7458
* Step 2 Clock - - 7456 8314 7456 * Step 2 Clock - - 7456 8314 7456
* Step 3 Clock Clock - 7458 8312 7458 * Step 3 Clock Clock - 7458 8312 7458
* Step 4 Clock - - 7458 8314 7458 * Step 4 Clock - - 7458 8314 7458
* Step 5 - - - 7452 8312 7452 * Step 5 - - - 7452 8312 7452
* *
* Note: * Note:
* il 7452 e il 8312 dello step 5 diventano 7451 e 8311 * il 7452 e il 8312 dello step 5 diventano 7451 e 8311
* nella mia tabella perche' il ciclo mancante lo eseguo * nella mia tabella perche' il ciclo mancante lo eseguo
* all'inizio del ciclo successivo. * all'inizio del ciclo successivo.
*/ */
{ {
{1, 7458, 7456, 7458, 7458, 7451, 0}, {1, 7458, 7456, 7458, 7458, 7451, 0},
{1, 8314, 8314, 8312, 8314, 8311, 0}, {1, 8314, 8314, 8312, 8314, 8311, 0},
{1, 7458, 7456, 7458, 7458, 7451, 0} {1, 7458, 7456, 7458, 7458, 7451, 0}
} }
}; };
/* la tabella con i valori da caricare nel length counter del canale */ /* la tabella con i valori da caricare nel length counter del canale */
static const BYTE length_table[32] = { static const BYTE length_table[32] = {
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
}; };
static const BYTE square_duty[2][4][8] = { static const BYTE square_duty[2][4][8] = {
{ {
{ 1, 0, 0, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0, 0, 0},
{ 1, 1, 0, 0, 0, 0, 0, 0}, { 1, 1, 0, 0, 0, 0, 0, 0},
{ 1, 1, 1, 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0, 0, 0, 0},
{ 0, 0, 1, 1, 1, 1, 1, 1} { 0, 0, 1, 1, 1, 1, 1, 1}
}, },
{ {
{ 1, 0, 0, 0, 0, 0, 0, 0}, { 1, 0, 0, 0, 0, 0, 0, 0},
{ 1, 1, 1, 1, 0, 0, 0, 0}, { 1, 1, 1, 1, 0, 0, 0, 0},
{ 1, 1, 0, 0, 0, 0, 0, 0}, { 1, 1, 0, 0, 0, 0, 0, 0},
{ 0, 0, 1, 1, 1, 1, 1, 1} { 0, 0, 1, 1, 1, 1, 1, 1}
}, },
}; };
static const BYTE triangle_duty[32] = { static const BYTE triangle_duty[32] = {
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
}; };
static const WORD noise_timer[3][16] = { static const WORD noise_timer[3][16] = {
{ {
0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0,
0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4 0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4
}, },
{ {
0x0004, 0x0007, 0x000E, 0x001E, 0x003C, 0x0058, 0x0076, 0x0094, 0x0004, 0x0007, 0x000E, 0x001E, 0x003C, 0x0058, 0x0076, 0x0094,
0x00BC, 0x00EC, 0x0162, 0x01D8, 0x02C4, 0x03B0, 0x0762, 0x0EC2 0x00BC, 0x00EC, 0x0162, 0x01D8, 0x02C4, 0x03B0, 0x0762, 0x0EC2
}, },
{ {
0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0060, 0x0080, 0x00A0,
0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4 0x00CA, 0x00FE, 0x017C, 0x01FC, 0x02FA, 0x03F8, 0x07F2, 0x0FE4
} }
}; };
static const WORD dmc_rate[3][16] = { static const WORD dmc_rate[3][16] = {
{ {
0x01AC, 0x017C, 0x0154, 0x0140, 0x011E, 0x00FE, 0x00E2, 0x00D6, 0x01AC, 0x017C, 0x0154, 0x0140, 0x011E, 0x00FE, 0x00E2, 0x00D6,
0x00BE, 0x00A0, 0x008E, 0x0080, 0x006A, 0x0054, 0x0048, 0x0036 0x00BE, 0x00A0, 0x008E, 0x0080, 0x006A, 0x0054, 0x0048, 0x0036
}, },
{ {
0x018E, 0x0162, 0x013C, 0x012A, 0x0114, 0x00EC, 0x00D2, 0x00C6, 0x018E, 0x0162, 0x013C, 0x012A, 0x0114, 0x00EC, 0x00D2, 0x00C6,
0x00B0, 0x0094, 0x0084, 0x0076, 0x0062, 0x004E, 0x0042, 0x0032 0x00B0, 0x0094, 0x0084, 0x0076, 0x0062, 0x004E, 0x0042, 0x0032
}, },
{ {
0x01AC, 0x017C, 0x0154, 0x0140, 0x011E, 0x00FE, 0x00E2, 0x00D6, 0x01AC, 0x017C, 0x0154, 0x0140, 0x011E, 0x00FE, 0x00E2, 0x00D6,
0x00BE, 0x00A0, 0x008E, 0x0080, 0x006A, 0x0054, 0x0048, 0x0036 0x00BE, 0x00A0, 0x008E, 0x0080, 0x006A, 0x0054, 0x0048, 0x0036
} }
}; };
EXTERNC void apu_tick(struct NESAPU* a, BYTE *hwtick); 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 lower_value { LOWER, UPPER };
enum machine_mode { AUTO, NTSC, PAL, DENDY, DEFAULT = 255 }; enum machine_mode { AUTO, NTSC, PAL, DENDY, DEFAULT = 255 };
enum reset_type { enum reset_type {
RESET = 0x10, RESET = 0x10,
HARD = 0x20, HARD = 0x20,
CHANGE_ROM = 0x30, CHANGE_ROM = 0x30,
CHANGE_MODE = 0x40, CHANGE_MODE = 0x40,
POWER_UP = 0x50 POWER_UP = 0x50
}; };
/* le dimesioni dello screen da renderizzare */ /* le dimesioni dello screen da renderizzare */
enum screen_dimension { SCR_LINES = 240, SCR_ROWS = 256 }; enum screen_dimension { SCR_LINES = 240, SCR_ROWS = 256 };
enum type_of_system_info { HEADER, DATABASE }; 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 header_type { iNES_1_0, NES_2_0, UNIF_FORMAT, FDS_FORMAT, NSF_FORMAT, NSFE_FORMAT };
enum length_file_name_type { enum length_file_name_type {
LENGTH_FILE_NAME = 512, LENGTH_FILE_NAME = 512,
LENGTH_FILE_NAME_MID = 1024, LENGTH_FILE_NAME_MID = 1024,
LENGTH_FILE_NAME_LONG = 2048, LENGTH_FILE_NAME_LONG = 2048,
LENGTH_FILE_NAME_MAX = 4096 LENGTH_FILE_NAME_MAX = 4096
}; };
enum forced_mirroring { UNK_HORIZONTAL, UNK_VERTICAL }; enum forced_mirroring { UNK_HORIZONTAL, UNK_VERTICAL };
enum max_chips_rom { MAX_CHIPS = 8 }; enum max_chips_rom { MAX_CHIPS = 8 };
enum languages { LNG_ENGLISH, LNG_ITALIAN, LNG_RUSSIAN }; enum languages { LNG_ENGLISH, LNG_ITALIAN, LNG_RUSSIAN };
enum database_mode { enum database_mode {
NODIPSWITCH = 0xFF00, NODIPSWITCH = 0xFF00,
NOEXTRA = 0x0000, NOEXTRA = 0x0000,
VSZAPPER = 0x0001, VSZAPPER = 0x0001,
CHRRAM32K = 0x0002, CHRRAM32K = 0x0002,
CHRRAM256K = 0x0004 CHRRAM256K = 0x0004
}; };
#define LENGTH(x) (sizeof(x)/sizeof(*(x))) #define LENGTH(x) (sizeof(x)/sizeof(*(x)))

View file

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

View file

@ -29,110 +29,110 @@ void fds_reset(struct _fds* fds) {
} }
void extcl_apu_tick_FDS(struct _fds* fds) { void extcl_apu_tick_FDS(struct _fds* fds) {
SWORD freq; SWORD freq;
/* volume unit */ /* volume unit */
if (fds->snd.volume.mode) { if (fds->snd.volume.mode) {
fds->snd.volume.gain = fds->snd.volume.speed; fds->snd.volume.gain = fds->snd.volume.speed;
} else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) { } else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) {
if (fds->snd.volume.counter) { if (fds->snd.volume.counter) {
fds->snd.volume.counter--; fds->snd.volume.counter--;
} else { } else {
fds->snd.volume.counter = (fds->snd.envelope.speed << 3) * (fds->snd.volume.speed + 1); fds->snd.volume.counter = (fds->snd.envelope.speed << 3) * (fds->snd.volume.speed + 1);
if (fds->snd.volume.increase) { if (fds->snd.volume.increase) {
if (fds->snd.volume.gain < 32) { if (fds->snd.volume.gain < 32) {
fds->snd.volume.gain++; fds->snd.volume.gain++;
} }
} else if (fds->snd.volume.gain) { } else if (fds->snd.volume.gain) {
fds->snd.volume.gain--; fds->snd.volume.gain--;
} }
} }
} }
/* sweep unit */ /* sweep unit */
if (fds->snd.sweep.mode) { if (fds->snd.sweep.mode) {
fds->snd.sweep.gain = fds->snd.sweep.speed; fds->snd.sweep.gain = fds->snd.sweep.speed;
} else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) { } else if (!fds->snd.envelope.disabled && fds->snd.envelope.speed) {
if (fds->snd.sweep.counter) { if (fds->snd.sweep.counter) {
fds->snd.sweep.counter--; fds->snd.sweep.counter--;
} else { } else {
fds->snd.sweep.counter = (fds->snd.envelope.speed << 3) * (fds->snd.sweep.speed + 1); fds->snd.sweep.counter = (fds->snd.envelope.speed << 3) * (fds->snd.sweep.speed + 1);
if (fds->snd.sweep.increase) { if (fds->snd.sweep.increase) {
if (fds->snd.sweep.gain < 32) { if (fds->snd.sweep.gain < 32) {
fds->snd.sweep.gain++; fds->snd.sweep.gain++;
} }
} else if (fds->snd.sweep.gain) { } else if (fds->snd.sweep.gain) {
fds->snd.sweep.gain--; fds->snd.sweep.gain--;
} }
} }
} }
/* modulation unit */ /* modulation unit */
freq = fds->snd.main.frequency; freq = fds->snd.main.frequency;
if (!fds->snd.modulation.disabled && fds->snd.modulation.frequency) { if (!fds->snd.modulation.disabled && fds->snd.modulation.frequency) {
if ((fds->snd.modulation.counter -= fds->snd.modulation.frequency) < 0) { if ((fds->snd.modulation.counter -= fds->snd.modulation.frequency) < 0) {
SWORD temp, temp2, a, d; SWORD temp, temp2, a, d;
SBYTE adj = fds->snd.modulation.data[fds->snd.modulation.index]; 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) { if (++fds->snd.modulation.index == 64) {
fds->snd.modulation.index = 0; fds->snd.modulation.index = 0;
} }
if (adj == -4) { if (adj == -4) {
fds->snd.sweep.bias = 0; fds->snd.sweep.bias = 0;
} else { } else {
fds->snd.sweep.bias += adj; 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; a = 64;
d = 0; d = 0;
if (temp <= 0) { if (temp <= 0) {
d = 15; d = 15;
} else if (temp < 3040) { //95 * 32 } else if (temp < 3040) { //95 * 32
a = 66; a = 66;
d = -31; 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) { if (freq) {
freq += fds->snd.modulation.mod; freq += fds->snd.modulation.mod;
} }
} }
/* main unit */ /* main unit */
if (fds->snd.main.silence) { if (fds->snd.main.silence) {
fds->snd.main.output = 0; fds->snd.main.output = 0;
return; return;
} }
if (freq && !fds->snd.wave.writable) { if (freq && !fds->snd.wave.writable) {
if ((fds->snd.wave.counter -= freq) < 0) { if ((fds->snd.wave.counter -= freq) < 0) {
WORD level; WORD level;
fds->snd.wave.counter += 65536; fds->snd.wave.counter += 65536;
level = (fds->snd.volume.gain < 32 ? fds->snd.volume.gain : 32) level = (fds->snd.volume.gain < 32 ? fds->snd.volume.gain : 32)
* volume_wave[fds->snd.wave.volume]; * volume_wave[fds->snd.wave.volume];
/* valore massimo dell'output (63 * (39 * 32)) = 78624 */ /* 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) >> 4;*/
fds->snd.main.output = (fds->snd.wave.data[fds->snd.wave.index] * level) >> 3; fds->snd.main.output = (fds->snd.wave.data[fds->snd.wave.index] * level) >> 3;
if (++fds->snd.wave.index == 64) { if (++fds->snd.wave.index == 64) {
fds->snd.wave.index = 0; 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 #endif
EXTERNC struct _fds { EXTERNC struct _fds {
// snd // snd
BYTE enabled_snd_reg; BYTE enabled_snd_reg;
struct _fds_snd { struct _fds_snd {
struct _fds_snd_wave { struct _fds_snd_wave {
BYTE data[64]; BYTE data[64];
BYTE writable; BYTE writable;
BYTE volume; BYTE volume;
BYTE index; BYTE index;
int32_t counter; int32_t counter;
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
/* questi valori non e' necessario salvarli nei savestates */ /* questi valori non e' necessario salvarli nei savestates */
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
/* */ BYTE clocked; /* */ /* */ BYTE clocked; /* */
/* ------------------------------------------------------- */ /* ------------------------------------------------------- */
} wave; } wave;
struct _fds_snd_envelope { struct _fds_snd_envelope {
BYTE speed; BYTE speed;
BYTE disabled; BYTE disabled;
} envelope; } envelope;
struct _fds_snd_main { struct _fds_snd_main {
BYTE silence; BYTE silence;
WORD frequency; WORD frequency;
SWORD output; SWORD output;
} main; } main;
struct _fds_snd_volume { struct _fds_snd_volume {
BYTE speed; BYTE speed;
BYTE mode; BYTE mode;
BYTE increase; BYTE increase;
BYTE gain; BYTE gain;
uint32_t counter; uint32_t counter;
} volume; } volume;
struct _fds_snd_sweep { struct _fds_snd_sweep {
SBYTE bias; SBYTE bias;
BYTE mode; BYTE mode;
BYTE increase; BYTE increase;
BYTE speed; BYTE speed;
BYTE gain; BYTE gain;
uint32_t counter; uint32_t counter;
} sweep; } sweep;
struct _fds_snd_modulation { struct _fds_snd_modulation {
SBYTE data[64]; SBYTE data[64];
WORD frequency; WORD frequency;
BYTE disabled; BYTE disabled;
BYTE index; BYTE index;
int32_t counter; int32_t counter;
SWORD mod; SWORD mod;
} modulation; } modulation;
} snd; } snd;
}; };
EXTERNC void extcl_apu_tick_FDS(struct _fds* fds); 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; BYTE prg_ram_mode;
void map_init_MMC5(struct _mmc5* mmc5) { void map_init_MMC5(struct _mmc5* mmc5) {
memset(mmc5,0,sizeof(struct _mmc5)); memset(mmc5,0,sizeof(struct _mmc5));
mmc5->S3.frequency = 1; mmc5->S3.frequency = 1;
mmc5->S4.frequency = 1; mmc5->S4.frequency = 1;
mmc5->S3.length.enabled = 0; mmc5->S3.length.enabled = 0;
mmc5->S3.length.value = 0; mmc5->S3.length.value = 0;
mmc5->S4.length.enabled = 0; mmc5->S4.length.enabled = 0;
mmc5->S4.length.value = 0; mmc5->S4.length.value = 0;
} }
void extcl_cpu_wr_mem_MMC5(struct _mmc5* mmc5, WORD address, BYTE value) { void extcl_cpu_wr_mem_MMC5(struct _mmc5* mmc5, WORD address, BYTE value) {
if (address < 0x5000) { if (address < 0x5000) {
return; return;
} }
switch (address) { switch (address) {
case 0x5000: case 0x5000:
square_reg0(mmc5->S3); square_reg0(mmc5->S3);
return; return;
case 0x5001: case 0x5001:
/* lo sweep non e' utilizzato */ /* lo sweep non e' utilizzato */
return; return;
case 0x5002: case 0x5002:
square_reg2(mmc5->S3); square_reg2(mmc5->S3);
return; return;
case 0x5003: case 0x5003:
square_reg3(mmc5->S3,0); square_reg3(mmc5->S3,0);
return; return;
case 0x5004: case 0x5004:
square_reg0(mmc5->S4); square_reg0(mmc5->S4);
return; return;
case 0x5005: case 0x5005:
/* lo sweep non e' utilizzato */ /* lo sweep non e' utilizzato */
return; return;
case 0x5006: case 0x5006:
square_reg2(mmc5->S4); square_reg2(mmc5->S4);
return; return;
case 0x5007: case 0x5007:
square_reg3(mmc5->S4,0); square_reg3(mmc5->S4,0);
return; return;
case 0x5010: case 0x5010:
mmc5->pcm.enabled = ~value & 0x01; mmc5->pcm.enabled = ~value & 0x01;
mmc5->pcm.output = 0; mmc5->pcm.output = 0;
if (mmc5->pcm.enabled) { if (mmc5->pcm.enabled) {
mmc5->pcm.output = mmc5->pcm.amp; mmc5->pcm.output = mmc5->pcm.amp;
} }
mmc5->clocked = TRUE; mmc5->clocked = TRUE;
return; return;
case 0x5011: case 0x5011:
mmc5->pcm.amp = value; mmc5->pcm.amp = value;
mmc5->pcm.output = 0; mmc5->pcm.output = 0;
if (mmc5->pcm.enabled) { if (mmc5->pcm.enabled) {
mmc5->pcm.output = mmc5->pcm.amp; mmc5->pcm.output = mmc5->pcm.amp;
} }
mmc5->clocked = TRUE; mmc5->clocked = TRUE;
return; return;
case 0x5015: case 0x5015:
if (!(mmc5->S3.length.enabled = value & 0x01)) { if (!(mmc5->S3.length.enabled = value & 0x01)) {
mmc5->S3.length.value = 0; mmc5->S3.length.value = 0;
} }
if (!(mmc5->S4.length.enabled = value & 0x02)) { if (!(mmc5->S4.length.enabled = value & 0x02)) {
mmc5->S4.length.value = 0; mmc5->S4.length.value = 0;
} }
return; return;
} }
} }
void extcl_length_clock_MMC5(struct _mmc5* mmc5) { void extcl_length_clock_MMC5(struct _mmc5* mmc5) {
length_run(mmc5->S3) length_run(mmc5->S3)
length_run(mmc5->S4) length_run(mmc5->S4)
} }
void extcl_envelope_clock_MMC5(struct _mmc5* mmc5) { void extcl_envelope_clock_MMC5(struct _mmc5* mmc5) {
envelope_run(mmc5->S3) envelope_run(mmc5->S3)
envelope_run(mmc5->S4) envelope_run(mmc5->S4)
} }
void extcl_apu_tick_MMC5(struct _mmc5* mmc5) { void extcl_apu_tick_MMC5(struct _mmc5* mmc5) {
// SQUARE 3 TICK // SQUARE 3 TICK
if (!(--mmc5->S3.frequency)) { if (!(--mmc5->S3.frequency)) {
square_output(mmc5->S3, 0) square_output(mmc5->S3, 0)
mmc5->S3.frequency = (mmc5->S3.timer + 1) << 1; mmc5->S3.frequency = (mmc5->S3.timer + 1) << 1;
mmc5->S3.sequencer = (mmc5->S3.sequencer + 1) & 0x07; mmc5->S3.sequencer = (mmc5->S3.sequencer + 1) & 0x07;
mmc5->clocked = TRUE; mmc5->clocked = TRUE;
} }
// SQUARE 4 TICK // SQUARE 4 TICK
if (!(--mmc5->S4.frequency)) { if (!(--mmc5->S4.frequency)) {
square_output(mmc5->S4, 0) square_output(mmc5->S4, 0)
mmc5->S4.frequency = (mmc5->S4.timer + 1) << 1; mmc5->S4.frequency = (mmc5->S4.timer + 1) << 1;
mmc5->S4.sequencer = (mmc5->S4.sequencer + 1) & 0x07; mmc5->S4.sequencer = (mmc5->S4.sequencer + 1) & 0x07;
mmc5->clocked = TRUE; mmc5->clocked = TRUE;
} }
} }

View file

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