1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Adds enough state machine to get into the near-incomprehensible stuff on the right.

This commit is contained in:
Thomas Harte 2021-11-14 10:48:50 -05:00
parent 47f36f08fb
commit 4c62611da3
2 changed files with 96 additions and 20 deletions

View File

@ -18,15 +18,16 @@
using namespace Amiga;
bool Audio::advance_dma(int channel) {
if(channels_[channel].has_data || !channels_[channel].length) {
switch(channels_[channel].state) {
case Channel::State::WaitingForDMA:
set_data(channel, ram_[pointer_[size_t(channel)]]);
return true;
case Channel::State::WaitingForDummyDMA:
channels_[channel].has_data = true;
return true;
default:
return false;
}
set_data(channel, ram_[pointer_[size_t(channel)]]);
++pointer_[size_t(channel)];
--channels_[channel].length;
return false;
}
void Audio::set_length(int channel, uint16_t length) {
@ -46,9 +47,6 @@ void Audio::set_volume(int channel, uint16_t volume) {
void Audio::set_data(int channel, uint16_t data) {
assert(channel >= 0 && channel < 4);
if(!channels_[channel].has_data) {
channels_[channel].period_counter = channels_[channel].period;
}
channels_[channel].has_data = true;
channels_[channel].data = data;
}
@ -77,17 +75,91 @@ void Audio::output() {
// so, attempt to consume another sample. If there are no more samples
// and length is 0, trigger an interrupt.
using State = Channel::State;
for(int c = 0; c < 4; c++) {
switch(channels_[c].state) {
case State::Disabled:
if(channels_[c].has_data && !channels_[c].dma_enabled && !channels_[c].interrupt_pending) {
channels_[c].state = Channel::State::PlayingHigh;
// TODO: [volcntrld, percntrld, pbufldl, AUDxID]
}
break;
constexpr InterruptFlag interrupts[] = {
InterruptFlag::AudioChannel0,
InterruptFlag::AudioChannel1,
InterruptFlag::AudioChannel2,
InterruptFlag::AudioChannel3,
};
default: break;
for(int c = 0; c < 4; c++) {
if(channels_[c].output()) {
posit_interrupt(interrupts[c]);
}
}
}
bool Audio::Channel::output() {
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;
if(!period_counter) {
}
break;
default: break;
}
return false;
}

View File

@ -62,9 +62,11 @@ class Audio: public DMADevice<4> {
// in the latch, which will always be 0, 1 or 2.
uint16_t data = 0x0000;
bool has_data = false;
uint16_t data_latch = 0x0000;
// Number of words remaining in DMA data.
uint16_t length = 0;
uint16_t length_counter = 0;
// Number of ticks between each sample, plus the
// current counter, which counts downward.
@ -90,6 +92,8 @@ class Audio: public DMADevice<4> {
PlayingHigh, // 010
PlayingLow, // 011
} state = State::Disabled;
bool output();
} channels_[4];
};