From fffc03c4e4aae7abd319d7508c800672d220803f Mon Sep 17 00:00:00 2001
From: Thomas Harte <thomas.harte@gmail.com>
Date: Fri, 12 Nov 2021 15:30:52 -0500
Subject: [PATCH] Propagates time to the audio subsystem.

---
 Machines/Amiga/Amiga.cpp   |  4 ++++
 Machines/Amiga/Audio.cpp   | 12 ++++++++----
 Machines/Amiga/Audio.hpp   | 15 +++++++++++++--
 Machines/Amiga/Chipset.cpp |  9 +++++++--
 Machines/Amiga/Chipset.hpp |  4 +++-
 5 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/Machines/Amiga/Amiga.cpp b/Machines/Amiga/Amiga.cpp
index ee638f454..dcaefae53 100644
--- a/Machines/Amiga/Amiga.cpp
+++ b/Machines/Amiga/Amiga.cpp
@@ -166,6 +166,10 @@ class ConcreteMachine:
 			return access_delay;
 		}
 
+		void flush() {
+			chipset_.flush();
+		}
+
 	private:
 		CPU::MC68000::Processor<ConcreteMachine, true> mc68000_;
 
diff --git a/Machines/Amiga/Audio.cpp b/Machines/Amiga/Audio.cpp
index 4a9ece054..d64f5b2fb 100644
--- a/Machines/Amiga/Audio.cpp
+++ b/Machines/Amiga/Audio.cpp
@@ -7,12 +7,16 @@
 //
 
 #include "Audio.hpp"
+
+#define LOG_PREFIX "[Audio] "
+#include "../../Outputs/Log.hpp"
+
 #include <cassert>
 
 using namespace Amiga;
 
 bool Audio::advance(int channel) {
-	if(channels_[channel].samples_remaining || !channels_[channel].length) {
+	if(channels_[channel].has_data || !channels_[channel].length) {
 		return false;
 	}
 
@@ -40,10 +44,10 @@ 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].samples_remaining) {
+	if(!channels_[channel].has_data) {
 		channels_[channel].period_counter = channels_[channel].period;
 	}
-	channels_[channel].samples_remaining = 2;
+	channels_[channel].has_data = true;
 	channels_[channel].data = data;
 }
 
@@ -57,7 +61,7 @@ void Audio::set_channel_enables(uint16_t enables) {
 void Audio::set_modulation_flags(uint16_t) {
 }
 
-void Audio::run_for(HalfCycles) {
+void Audio::run_for([[maybe_unused]] Cycles duration) {
 	// TODO:
 	//
 	// Check whether any channel's period counter is exhausted and, if
diff --git a/Machines/Amiga/Audio.hpp b/Machines/Amiga/Audio.hpp
index 84c5aafb7..e595cf2fb 100644
--- a/Machines/Amiga/Audio.hpp
+++ b/Machines/Amiga/Audio.hpp
@@ -28,7 +28,7 @@ class Audio: public DMADevice<4> {
 
 		/// Standard JustInTimeActor item; allows this class to track the
 		/// amount of time between other events.
-		void run_for(HalfCycles);
+		void run_for(Cycles);
 
 		/// Sets the total number of words to fetch for the given channel.
 		void set_length(int channel, uint16_t);
@@ -58,7 +58,7 @@ class Audio: public DMADevice<4> {
 			// The data latch plus a count of unused samples
 			// in the latch, which will always be 0, 1 or 2.
 			uint16_t data = 0x0000;
-			int samples_remaining = 0;
+			bool has_data = false;
 
 			// Number of words remaining in DMA data.
 			uint16_t length = 0;
@@ -73,6 +73,17 @@ class Audio: public DMADevice<4> {
 
 			// Indicates whether DMA is enabled for this channel.
 			bool dma_enabled = false;
+
+			// Replicates the Hardware Reference Manual state machine;
+			// comments indicate which of the documented states each
+			// label refers to.
+			enum class State {
+				Disabled,			// 000
+				WaitingForDummyDMA,	// 001
+				WaitingForDMA,		// 101
+				PlayingHigh,		// 010
+				PlayingLow,			// 011
+			} state_;
 		} channels_[4];
 };
 
diff --git a/Machines/Amiga/Chipset.cpp b/Machines/Amiga/Chipset.cpp
index 083aa0e49..3dbf31217 100644
--- a/Machines/Amiga/Chipset.cpp
+++ b/Machines/Amiga/Chipset.cpp
@@ -140,6 +140,9 @@ template <int cycle> void Chipset::output() {
 	constexpr int blank3 	= 7 + burst;
 	static_assert(blank3 == 43);
 
+	// Advance audio.
+	audio_ += Cycles(1);
+
 	// Trigger any sprite loads encountered.
 	constexpr auto dcycle = cycle << 1;
 	for(int c = 0; c < 8; c += 2) {
@@ -444,8 +447,6 @@ template <bool stop_on_cpu> int Chipset::advance_slots(int first_slot, int last_
 template <bool stop_on_cpu> Chipset::Changes Chipset::run(HalfCycles length) {
 	Changes changes;
 
-	// TODO: incorporate audio timing here or deeper.
-
 	// This code uses 'pixels' as a measure, which is equivalent to one pixel clock time,
 	// or half a cycle.
 	auto pixels_remaining = length.as<int>();
@@ -1286,3 +1287,7 @@ uint16_t Chipset::Mouse::get_position() {
 		declared_position_[0]
 	);
 }
+
+void Chipset::flush() {
+	audio_.flush();
+}
diff --git a/Machines/Amiga/Chipset.hpp b/Machines/Amiga/Chipset.hpp
index 67cd477f8..d9bc70493 100644
--- a/Machines/Amiga/Chipset.hpp
+++ b/Machines/Amiga/Chipset.hpp
@@ -102,6 +102,8 @@ class Chipset: private ClockingHint::Observer {
 			return keyboard_;
 		}
 
+		void flush();
+
 	private:
 		friend class DMADeviceBase;
 
@@ -309,7 +311,7 @@ class Chipset: private ClockingHint::Observer {
 
 		// MARK: - Audio.
 
-		JustInTimeActor<Audio> audio_;
+		JustInTimeActor<Audio, Cycles> audio_;
 
 		// MARK: - Serial port.