1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +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
@ -277,11 +192,11 @@ bool Audio::Channel::output() {
Definitions:
AUDxON DMA on "x" indicates channel number (signal from DMACON).
AUDxON DMA on "x" indicates channel number (signal from DMACON).
AUDxIP Audio interrupt pending (input to channel from interrupt circuitry).
AUDxIP Audio interrupt pending (input to channel from interrupt circuitry).
AUDxIR Audio interrupt request (output from channel to interrupt circuitry)
AUDxIR Audio interrupt request (output from channel to interrupt circuitry)
intreq1 Interrupt request that combines with intreq2 to form AUDxIR.
@ -297,32 +212,204 @@ bool Audio::Channel::output() {
dmasen Restart request enable.
percntrld Reload period counter from back-up latch typically written
by processor with AUDxPER (can also be written by attach mode).
by processor with AUDxPER (can also be written by attach mode).
percount Count period counter down one latch.
percount Count period counter down one latch.
perfin Period counter finished (value = 1).
perfin Period counter finished (value = 1).
lencntrld Reload length counter from back-up latch.
lencntrld Reload length counter from back-up latch.
lencount Count length counter down one notch.
lencount Count length counter down one notch.
lenfin Length counter finished (value = 1).
lenfin Length counter finished (value = 1).
volcntrld Reload volume counter from back-up latch.
volcntrld Reload volume counter from back-up latch.
pbufld1 Load output buffer from holding latch written to by AUDxDAT.
pbufld1 Load output buffer from holding latch written to by AUDxDAT.
pbufld2 Like pbufld1, but only during 010->011 with attach period.
pbufld2 Like pbufld1, but only during 010->011 with attach period.
AUDxAV Attach volume. Send data to volume latch of next channel
AUDxAV Attach volume. Send data to volume latch of next channel
instead of to D->A converter.
AUDxAP Attach period. Send data to period latch of next channel
AUDxAP Attach period. Send data to period latch of next channel
instead of to the D->A converter.
penhi Enable the high 8 bits of data to go to the D->A converter.
penhi Enable the high 8 bits of data to go to the D->A converter.
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.
*/
//
// 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];
};