1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-09 06:29:33 +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
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
action:
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
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;
bool output();
template <State state> bool output();
template <State begin, State end> bool transit();
} channels_[4];
};