1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-23 21:29:11 +00:00

Separates state transitions and tests.

This commit is contained in:
Thomas Harte 2021-11-15 05:29:28 -05:00
parent a9971917f5
commit 0c5bb9626b
2 changed files with 192 additions and 103 deletions

View File

@ -83,93 +83,8 @@ void Audio::output() {
} }
} }
bool Audio::Channel::output() {
// The following attempts to reproduce the audio state diagram provided in
// Commodore's Hardware Reference Manual.
//
// See big comment at the foot of this file.
switch(state) {
case State::Disabled:
// Test for top loop of Commodore's state diagram,
// which permits CPU-driven audio output.
if(has_data && !dma_enabled && !interrupt_pending) {
state = State::PlayingHigh;
data_latch = data; // i.e. pbufld1
has_data = false;
period_counter = period; // i.e. percntrld
// TODO: volcntrld (see above).
// Request an interrupt.
return true;
}
// Test for DMA-style transition.
if(dma_enabled) {
state = State::WaitingForDummyDMA;
period_counter = period; // i.e. percntrld
length_counter = length; // i.e. lenctrld
break;
}
break;
case State::WaitingForDummyDMA:
if(!dma_enabled) {
state = State::Disabled;
break;
}
if(dma_enabled && has_data) {
has_data = false;
state = State::WaitingForDMA;
if(length == 1) {
length_counter = length;
return true;
}
break;
}
break;
case State::WaitingForDMA:
if(!dma_enabled) {
state = State::Disabled;
break;
}
if(dma_enabled && has_data) {
data_latch = data;
has_data = false;
period_counter = period; // i.e. percntrld
state = State::PlayingHigh;
break;
}
break;
case State::PlayingHigh:
// TODO: penhi (i.e. output high byte).
-- period_counter;
// This is a reasonable guess as to the exit condition for this node;
// Commodore doesn't document.
if(period_counter == 1) {
state = State::PlayingLow;
// TODO: if attach period, reload output buffer.
}
break;
default: break;
}
return false;
}
/* /*
Big spiel on the state machine implemented above: Big spiel on the state machine:
Commodore's Hardware Rerefence Manual provides the audio subsystem's state Commodore's Hardware Rerefence Manual provides the audio subsystem's state
machine, so I've just tried to reimplement it verbatim. It's depicted machine, so I've just tried to reimplement it verbatim. It's depicted
@ -225,7 +140,7 @@ bool Audio::Channel::output() {
if: AUDxON, and AUDxDAT if: AUDxON, and AUDxDAT
action: action:
1. volcntrld, percentrld, pbufid1 1. volcntrld, percentrld, pbufid1
2. if napnav, AUDxDR 2. if napnav, then AUDxDR
@ -326,3 +241,175 @@ bool Audio::Channel::output() {
napnav /AUDxAV * /AUDxAP + AUDxAV -- no attach stuff or else attach napnav /AUDxAV * /AUDxAP + AUDxAV -- no attach stuff or else attach
volume. Condition for normal DMA and interrupt requests. volume. Condition for normal DMA and interrupt requests.
*/ */
//
// Non-action fallback transition.
//
template <
Audio::Channel::State begin,
Audio::Channel::State end> bool Audio::Channel::transit() {
state = end;
return false;
}
//
// Audio::Channel::State::Disabled
//
template <> bool Audio::Channel::transit<
Audio::Channel::State::Disabled,
Audio::Channel::State::WaitingForDummyDMA>() {
state = State::WaitingForDummyDMA;
period_counter = period; // i.e. percntrld
length_counter = length; // i.e. lenctrld
return false;
}
template <> bool Audio::Channel::transit<
Audio::Channel::State::Disabled,
Audio::Channel::State::PlayingHigh>() {
state = State::PlayingHigh;
data_latch = data; // i.e. pbufld1
has_data = false;
period_counter = period; // i.e. percntrld
// TODO: volcntrld (see above).
// Request an interrupt.
return true;
}
template <> bool Audio::Channel::output<Audio::Channel::State::Disabled>() {
if(has_data && !dma_enabled && !interrupt_pending) {
return transit<State::Disabled, State::PlayingHigh>();
}
// Test for DMA-style transition.
if(dma_enabled) {
return transit<State::Disabled, State::WaitingForDummyDMA>();
}
return false;
}
//
// Audio::Channel::State::WaitingForDummyDMA
//
template <> bool Audio::Channel::transit<
Audio::Channel::State::WaitingForDummyDMA,
Audio::Channel::State::WaitingForDMA>() {
state = State::WaitingForDMA;
has_data = false;
if(length == 1) {
length_counter = length;
return true;
}
return false;
}
template <> bool Audio::Channel::output<Audio::Channel::State::WaitingForDummyDMA>() {
if(!dma_enabled) {
return transit<State::WaitingForDummyDMA, State::Disabled>();
}
if(dma_enabled && has_data) {
return transit<State::WaitingForDummyDMA, State::WaitingForDMA>();
}
return false;
}
//
// Audio::Channel::State::WaitingForDMA
//
template <> bool Audio::Channel::transit<
Audio::Channel::State::WaitingForDMA,
Audio::Channel::State::PlayingHigh>() {
state = State::PlayingHigh;
data_latch = data;
has_data = false;
period_counter = period; // i.e. percntrld
return false;
}
template <> bool Audio::Channel::output<Audio::Channel::State::WaitingForDMA>() {
if(!dma_enabled) {
return transit<State::WaitingForDummyDMA, State::Disabled>();
}
if(dma_enabled && has_data) {
return transit<State::WaitingForDummyDMA, State::PlayingHigh>();
}
return false;
}
//
// Audio::Channel::State::PlayingHigh
//
template <> bool Audio::Channel::output<Audio::Channel::State::PlayingHigh>() {
-- period_counter;
// This is a reasonable guess as to the exit condition for this node;
// Commodore doesn't document.
if(!period_counter) {
return transit<State::PlayingHigh, State::PlayingLow>();
}
// TODO: penhi (i.e. output high byte).
return false;
}
//
// Audio::Channel::State::PlayingLow
//
template <> bool Audio::Channel::output<Audio::Channel::State::PlayingLow>() {
-- period_counter;
const bool dma_and_not_done = dma_enabled && !interrupt_pending;
if(!period_counter && !dma_and_not_done) {
return transit<State::PlayingLow, State::Disabled>();
}
if(!period_counter && dma_and_not_done) {
return transit<State::PlayingLow, State::PlayingHigh>();
}
// TODO: not penhi (i.e. output low byte).
return false;
}
//
// Dispatcher
//
bool Audio::Channel::output() {
switch(state) {
case State::Disabled: return output<State::Disabled>();
case State::WaitingForDummyDMA: return output<State::WaitingForDummyDMA>();
case State::WaitingForDMA: return output<State::WaitingForDMA>();
case State::PlayingHigh: return output<State::PlayingHigh>();
case State::PlayingLow: return output<State::PlayingLow>();
default:
assert(false);
break;
}
return false;
}

View File

@ -94,6 +94,8 @@ class Audio: public DMADevice<4> {
} state = State::Disabled; } state = State::Disabled;
bool output(); bool output();
template <State state> bool output();
template <State begin, State end> bool transit();
} channels_[4]; } channels_[4];
}; };