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:
parent
f43715775c
commit
ad8437e5ae
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue