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"
|
#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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)))
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue