mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-06 15:00:05 +00:00
Adds enough state machine to get into the near-incomprehensible stuff on the right.
This commit is contained in:
parent
47f36f08fb
commit
4c62611da3
@ -18,15 +18,16 @@
|
|||||||
using namespace Amiga;
|
using namespace Amiga;
|
||||||
|
|
||||||
bool Audio::advance_dma(int channel) {
|
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;
|
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) {
|
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) {
|
void Audio::set_data(int channel, uint16_t data) {
|
||||||
assert(channel >= 0 && channel < 4);
|
assert(channel >= 0 && channel < 4);
|
||||||
if(!channels_[channel].has_data) {
|
|
||||||
channels_[channel].period_counter = channels_[channel].period;
|
|
||||||
}
|
|
||||||
channels_[channel].has_data = true;
|
channels_[channel].has_data = true;
|
||||||
channels_[channel].data = data;
|
channels_[channel].data = data;
|
||||||
}
|
}
|
||||||
@ -77,17 +75,91 @@ void Audio::output() {
|
|||||||
// so, attempt to consume another sample. If there are no more samples
|
// so, attempt to consume another sample. If there are no more samples
|
||||||
// and length is 0, trigger an interrupt.
|
// and length is 0, trigger an interrupt.
|
||||||
|
|
||||||
using State = Channel::State;
|
constexpr InterruptFlag interrupts[] = {
|
||||||
for(int c = 0; c < 4; c++) {
|
InterruptFlag::AudioChannel0,
|
||||||
switch(channels_[c].state) {
|
InterruptFlag::AudioChannel1,
|
||||||
case State::Disabled:
|
InterruptFlag::AudioChannel2,
|
||||||
if(channels_[c].has_data && !channels_[c].dma_enabled && !channels_[c].interrupt_pending) {
|
InterruptFlag::AudioChannel3,
|
||||||
channels_[c].state = Channel::State::PlayingHigh;
|
};
|
||||||
// TODO: [volcntrld, percntrld, pbufldl, AUDxID]
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
@ -62,9 +62,11 @@ class Audio: public DMADevice<4> {
|
|||||||
// in the latch, which will always be 0, 1 or 2.
|
// in the latch, which will always be 0, 1 or 2.
|
||||||
uint16_t data = 0x0000;
|
uint16_t data = 0x0000;
|
||||||
bool has_data = false;
|
bool has_data = false;
|
||||||
|
uint16_t data_latch = 0x0000;
|
||||||
|
|
||||||
// Number of words remaining in DMA data.
|
// Number of words remaining in DMA data.
|
||||||
uint16_t length = 0;
|
uint16_t length = 0;
|
||||||
|
uint16_t length_counter = 0;
|
||||||
|
|
||||||
// Number of ticks between each sample, plus the
|
// Number of ticks between each sample, plus the
|
||||||
// current counter, which counts downward.
|
// current counter, which counts downward.
|
||||||
@ -90,6 +92,8 @@ class Audio: public DMADevice<4> {
|
|||||||
PlayingHigh, // 010
|
PlayingHigh, // 010
|
||||||
PlayingLow, // 011
|
PlayingLow, // 011
|
||||||
} state = State::Disabled;
|
} state = State::Disabled;
|
||||||
|
|
||||||
|
bool output();
|
||||||
} channels_[4];
|
} channels_[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user