From a5fe1e4259eca96bdd136a53a641a9fab94a2524 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 5 Dec 2021 15:27:35 -0500 Subject: [PATCH] Largely debugs audio state machine. I think I'm still missing an address reload somewhere though, and attachment doesn't actually push. --- Machines/Amiga/Audio.cpp | 195 +++++++++++++++++++++++++++------------ Machines/Amiga/Audio.hpp | 4 + 2 files changed, 139 insertions(+), 60 deletions(-) diff --git a/Machines/Amiga/Audio.cpp b/Machines/Amiga/Audio.cpp index cf682b5fc..b4a19e8a4 100644 --- a/Machines/Amiga/Audio.cpp +++ b/Machines/Amiga/Audio.cpp @@ -175,11 +175,11 @@ void Audio::output() { -> State::PlayingHigh (010) if: AUDDAT, and not AUDxON, and not AUDxIP - action: volcntrld, percntrld, pbudld1, AUDxIR + action: percntrld, AUDxIR, volcntrld, pbudld1 -> State::WaitingForDummyDMA (001) if: AUDxON - action: lencntrld, AUDxDR, dmasen*, percntrld + action: percntrld, AUDxDR, lencntrld, dmasen* * NOTE: except for this case, dmasen is true only when @@ -218,7 +218,7 @@ void Audio::output() { -> State::PlayingHigh (010) if: AUDxON, and AUDxDAT action: - 1. volcntrld, percntrld, pbufid1 + 1. volcntrld, percntrld, pbufld1 2. if napnav, then AUDxDR @@ -232,7 +232,7 @@ void Audio::output() { -> State::PlayingLow (011) if: perfin action: - 1. if AUDxAP, then pubfid2 + 1. if AUDxAP, then pbufld2 2. if AUDxAP and AUDxON, then AUDxDR 3. percntrld 4. if intreq2 and AUDxON and AUDxAP, then AUDxIR @@ -260,8 +260,8 @@ void Audio::output() { action: 1. pbufld 2. percntrld - 3. if AUDxON and napnav, then AUDxDR - 4. if intreq2 and napnav and AUDxON, AUDxIR + 3. if napnav and AUDxON, then AUDxDR + 4. if napnav and AUDxON and intreq2, AUDxIR 5. if napnav and not AUDxON, AUDxIR 6. if lenfin and AUDxON and AUDxDAT, then lencntrld 7. if (not lenfin) and AUDxON and AUDxDAT, then lencount @@ -323,7 +323,6 @@ void Audio::output() { volume. Condition for normal DMA and interrupt requests. */ - // // Non-action fallback transition and setter, plus specialised begin_state declarations. // @@ -347,38 +346,51 @@ template < template <> bool Audio::Channel::transit< Audio::Channel::State::Disabled, - Audio::Channel::State::WaitingForDummyDMA>(Channel *moduland) { - begin_state(moduland); + Audio::Channel::State::PlayingHigh>(Channel *moduland) { + begin_state(moduland); - period_counter = period; // i.e. percntrld - length_counter = length; // i.e. lencntrld + // percntrld + period_counter = period; + + // [AUDxIR]: see return result. + + // TODO: volcntrld (?) + + // pbufld1 + data_latch = data; wants_data = true; - should_reload_address = true; // i.e. dmasen - return false; + // AUDxIR. + return true; } template <> bool Audio::Channel::transit< Audio::Channel::State::Disabled, - Audio::Channel::State::PlayingHigh>(Channel *moduland) { - begin_state(moduland); + Audio::Channel::State::WaitingForDummyDMA>(Channel *moduland) { + begin_state(moduland); - data_latch = data; // i.e. pbufld1 - period_counter = period; // i.e. percntrld + // percntrld + period_counter = period; + + // AUDxDR wants_data = true; - should_reload_address = true; // i.e. dmasen - // TODO: volcntrld (see above). - // Request an interrupt. - return true; + // lencntrld + length_counter = length; + + // dmasen / AUDxDSR + should_reload_address = true; + + return false; } template <> bool Audio::Channel::output(Channel *moduland) { + // if AUDDAT, and not AUDxON, and not AUDxIP. if(!wants_data && !dma_enabled && !interrupt_pending) { return transit(moduland); } - // Test for DMA-style transition. + // if AUDxON. if(dma_enabled) { return transit(moduland); } @@ -395,20 +407,25 @@ template <> bool Audio::Channel::transit< Audio::Channel::State::WaitingForDMA>(Channel *moduland) { begin_state(moduland); + // AUDxDR wants_data = true; - if(length == 1) { - length_counter = length; - return true; + + // if not lenfin, then lencount + if(length != 1) { + -- length_counter; } - return false; + // AUDxIR + return true; } template <> bool Audio::Channel::output(Channel *moduland) { + // if not AUDxON if(!dma_enabled) { return transit(moduland); } + // if AUDxON and AUDxDAT if(dma_enabled && !wants_data) { return transit(moduland); } @@ -425,18 +442,30 @@ template <> bool Audio::Channel::transit< Audio::Channel::State::PlayingHigh>(Channel *moduland) { begin_state(moduland); + // TODO: volcntrld (?) + + // percntrld + period_counter = period; + + // pbufld1 data_latch = data; - wants_data = true; - period_counter = period; // i.e. percntrld + + // if napnav + if(attach_volume || !(attach_volume || attach_period)) { + // AUDxDR + wants_data = true; + } return false; } template <> bool Audio::Channel::output(Channel *moduland) { + // if: not AUDxON if(!dma_enabled) { return transit(moduland); } + // if: AUDxON, and AUDxDAT if(dma_enabled && !wants_data) { return transit(moduland); } @@ -448,49 +477,75 @@ template <> bool Audio::Channel::output(Ch // Audio::Channel::State::PlayingHigh // +void Audio::Channel::decrement_length() { + // if lenfin and AUDxON and AUDxDAT, then lencntrld + // if (not lenfin) and AUDxON and AUDxDAT, then lencount + // if lenfin and AUDxON and AUDxDAT, then intreq2 + if(dma_enabled && !wants_data) { + if(length_counter == 1) { + length_counter = length; + will_request_interrupt = true; + } + + --length_counter; + } +} + template <> bool Audio::Channel::transit< Audio::Channel::State::PlayingHigh, Audio::Channel::State::PlayingLow>(Channel *moduland) { begin_state(moduland); - // TODO: if AUDxAP, then pubfid2 - // TODO: if AUDxAP and AUDxON, then AUDxDR + bool wants_interrupt = false; - period_counter = period; // i.e. percntrld + // if AUDxAP + if(attach_period) { + // pbufld2 + data_latch = data; - // TODO: if intreq2 and AUDxON and AUDxAP, then AUDxIR - // TODO: if AUDxAP and AUDxON, then AUDxIR + // [if AUDxAP] and AUDxON + if(dma_enabled) { + // AUDxDR + wants_data = true; - // if lenfin and AUDxON and AUDxDAT, then lencntrld - // if (not lenfin) and AUDxON and AUDxDAT, then lencount - // if lenfin and AUDxON and AUDxDAT, then intreq2 - if(dma_enabled && !wants_data) { - --length_counter; - if(!length_counter) { - length_counter = length; - will_request_interrupt = true; + // [if AUDxAP and AUDxON] and intreq2 + if(will_request_interrupt) { + will_request_interrupt = false; + + // AUDxIR + wants_interrupt = true; + } + } else { + // i.e. if AUDxAP and AUDxON, then AUDxIR + wants_interrupt = true; } } - return false; + // percntrld + period_counter = period; + + decrement_length(); + + return wants_interrupt; } template <> void Audio::Channel::begin_state(Channel *) { state = Audio::Channel::State::PlayingHigh; - // Output high byte. + // penhi. output_level = int8_t(data_latch >> 8); } template <> bool Audio::Channel::output(Channel *moduland) { - -- period_counter; - // This is a reasonable guess as to the exit condition for this node; // Commodore doesn't document. - if(!period_counter) { + if(period_counter == 1) { return transit(moduland); } + // percount. + -- period_counter; + return false; } @@ -498,32 +553,52 @@ template <> bool Audio::Channel::output(Chan // Audio::Channel::State::PlayingLow // +template <> bool Audio::Channel::transit< + Audio::Channel::State::PlayingLow, + Audio::Channel::State::Disabled>(Channel *moduland) { + begin_state(moduland); + + // Clear the slightly nebulous 'if intreq2 occurred' state. + will_request_interrupt = false; + + return false; +} + template <> bool Audio::Channel::transit< Audio::Channel::State::PlayingLow, Audio::Channel::State::PlayingHigh>(Channel *moduland) { begin_state(moduland); - // TODO: include napnav in tests + bool wants_interrupt = false; + // TODO: volcntrld? - period_counter = period; // i.e. percntrld + // percntrld + period_counter = period; - if(!dma_enabled) { - return true; - } else { - data_latch = data; // i.e. pbufld2 - wants_data = true; // AUDxDR + // pbufld1 + data_latch = data; - if(length_counter == 1) { - should_reload_address = true; // Not sure about this one; based on asterisked comment in ::Disabled documentation. + // if napnav + if(attach_volume || !(attach_volume || attach_period)) { + // [if napnav] and AUDxON + if(dma_enabled) { + // AUDxDR + wants_data = true; + + // [if napnav and AUDxON] and intreq2 + if(will_request_interrupt) { + will_request_interrupt = false; + wants_interrupt = true; + } + } else { + // AUDxIR + wants_interrupt = true; } } - if(dma_enabled && will_request_interrupt) { - will_request_interrupt = false; - return true; - } + decrement_length(); - return false; + return wants_interrupt; } template <> void Audio::Channel::begin_state(Channel *) { diff --git a/Machines/Amiga/Audio.hpp b/Machines/Amiga/Audio.hpp index aa63ff735..626f67e6e 100644 --- a/Machines/Amiga/Audio.hpp +++ b/Machines/Amiga/Audio.hpp @@ -131,6 +131,10 @@ class Audio: public DMADevice<4> { /// @param moduland The channel to modulate, if modulation is enabled. template void begin_state(Channel *moduland); + /// Provides the common length-decrementing logic used when transitioning + /// between PlayingHigh and PlayingLow in either direction. + void decrement_length(); + // Output state. int8_t output_level = 0; uint8_t output_phase = 0; // TODO: this should count down, not up.