diff --git a/devices/ioctrl/amic.cpp b/devices/ioctrl/amic.cpp index ccd2aa7..d7e534f 100644 --- a/devices/ioctrl/amic.cpp +++ b/devices/ioctrl/amic.cpp @@ -74,6 +74,7 @@ AMIC::AMIC() : MMIODevice() // initialize sound HW this->snd_out_dma = std::unique_ptr (new AmicSndOutDma()); + this->snd_out_dma->init_interrupts(this, 2 << 8); this->awacs = std::unique_ptr (new AwacDevicePdm()); this->awacs->set_dma_out(this->snd_out_dma.get()); @@ -176,6 +177,10 @@ uint32_t AMIC::read(uint32_t rgn_start, uint32_t offset, int size) return this->mon_id; case AMICReg::Int_Ctrl: return (this->int_ctrl & 0xC0) | (this->dev_irq_lines & 0x3F); + case AMICReg::DMA_IFR_0: + return this->dma_ifr0; + case AMICReg::DMA_IFR_1: + return this->dma_ifr1; case AMICReg::Diag_Reg: return 0xFE | this->emmo_pin; case AMICReg::DMA_Base_Addr_0: @@ -245,14 +250,14 @@ void AMIC::write(uint32_t rgn_start, uint32_t offset, uint32_t value, int size) LOG_F(9, "AMIC Sound Out Ctrl updated, val=%x", value); if ((value & 1) != (this->snd_out_ctrl & 1)) { if (value & 1) { - LOG_F(9, "AMIC Sound Out DMA enabled!"); + LOG_F(INFO, "AMIC Sound Out DMA enabled!"); this->snd_out_dma->init(this->dma_base & ~0x3FFFF, this->snd_buf_size); this->snd_out_dma->enable(); this->awacs->set_sample_rate((this->snd_out_ctrl >> 1) & 3); this->awacs->dma_out_start(); } else { - LOG_F(9, "AMIC Sound Out DMA disabled!"); + LOG_F(INFO, "AMIC Sound Out DMA disabled!"); this->snd_out_dma->disable(); } } @@ -505,7 +510,24 @@ void AMIC::ack_cpu_int(uint32_t irq_id, uint8_t irq_line_state) { } void AMIC::ack_dma_int(uint32_t irq_id, uint8_t irq_line_state) { - ABORT_F("AMIC: ack_dma_int() not implemented"); + if (irq_id >= 0x100) { // DMA Interrupt Flags 1 + irq_id = (irq_id >> 8) & 0xFFU; + if (irq_line_state) + this->dma_ifr1 |= irq_id; + else + this->dma_ifr1 &= ~irq_id; + } else { // DMA Interrupt Flags 0 + irq_id &= 0xFFU; + if (irq_line_state) + this->dma_ifr0 |= irq_id; + else + this->dma_ifr0 &= ~irq_id; + } + uint8_t new_irq = (this->dma_ifr0 || this->dma_ifr1) ? 1 : 0; + if (new_irq != this->dma_irq) { + this->dma_irq = new_irq; + this->ack_cpu_int(CPU_INT_ALL_DMA, new_irq); + } } // ============================ Sound DMA stuff ================================ @@ -524,6 +546,7 @@ void AmicSndOutDma::init(uint32_t buf_base, uint32_t buf_samples) this->snd_buf_num = 0; this->cur_buf_pos = 0; + this->irq_level = 0; } uint8_t AmicSndOutDma::read_stat() @@ -531,12 +554,25 @@ uint8_t AmicSndOutDma::read_stat() return this->dma_out_ctrl; } +void AmicSndOutDma::update_irq() { + uint8_t new_level = !!((this->dma_out_ctrl >> 4) & this->dma_out_ctrl); + if (new_level != this->irq_level) { + this->irq_level = new_level; + TimerManager::get_instance()->add_immediate_timer([this] { + this->int_ctrl->ack_dma_int(this->irq_id, this->irq_level); + }); + } +} + void AmicSndOutDma::write_dma_out_ctrl(uint8_t value) { // clear interrupt flags - value &= ~PDM_DMA_INTS_MASK; - this->dma_out_ctrl = value; - LOG_F(9, "AMIC: Sound out DMA control set to 0x%X", value); + if (value & PDM_DMA_INTS_MASK) { + this->dma_out_ctrl &= ~(value & PDM_DMA_INTS_MASK); + this->update_irq(); + } + // enable sound output DMA interrupts + this->dma_out_ctrl |= value & 0x0FU; } DmaPullResult AmicSndOutDma::pull_data(uint32_t req_len, uint32_t *avail_len, @@ -549,13 +585,15 @@ DmaPullResult AmicSndOutDma::pull_data(uint32_t req_len, uint32_t *avail_len, if (!this->snd_buf_num) { // signal buffer 0 drained this->dma_out_ctrl |= PDM_DMA_IF0; - // TODO: generate IE0 interrupt if enabled } else { // signal buffer 1 drained this->dma_out_ctrl |= PDM_DMA_IF1; - // TODO: generate IE1 interrupt if enabled } + // generate sound out DMA interrupt if (IF0 & IE0) + // or (IF1 & IE1) or (ERR_IF & ERR_IE) + this->update_irq(); + // check DMA enable flag after buffer 1 was processed // if it's false stop delivering sound data // this will effectively stop audio playback diff --git a/devices/ioctrl/amic.h b/devices/ioctrl/amic.h index df91a0c..d518266 100644 --- a/devices/ioctrl/amic.h +++ b/devices/ioctrl/amic.h @@ -96,20 +96,30 @@ public: void enable() { this->enabled = true; }; void disable() { this->enabled = false; }; uint8_t read_stat(); + void update_irq(); void write_dma_out_ctrl(uint8_t value); uint32_t get_cur_buf_pos() { return this->cur_buf_pos; }; DmaPullResult pull_data(uint32_t req_len, uint32_t *avail_len, uint8_t **p_data); -private: - bool enabled; - uint8_t dma_out_ctrl; + void init_interrupts(InterruptCtrl *int_ctrl, uint32_t irq_id) { + this->int_ctrl = int_ctrl; + this->irq_id = irq_id; + }; - uint32_t out_buf0; - uint32_t out_buf1; - uint32_t out_buf_len; - uint32_t snd_buf_num; - uint32_t cur_buf_pos; +private: + bool enabled = false; + uint8_t dma_out_ctrl; + + uint32_t out_buf0; + uint32_t out_buf1; + uint32_t out_buf_len; + uint32_t snd_buf_num; + uint32_t cur_buf_pos; + + InterruptCtrl *int_ctrl = nullptr; + uint32_t irq_id = 0; + uint8_t irq_level = 0; }; /** AMIC-specific floppy DMA implementation. */ @@ -203,9 +213,9 @@ enum AMICReg : uint32_t { // Interrupt registers Int_Ctrl = 0x2A000, - DMA_Int_0 = 0x2A008, + DMA_IFR_0 = 0x2A008, Bus_Err_Int_0 = 0x2A009, - DMA_Int_1 = 0x2A00A, + DMA_IFR_1 = 0x2A00A, Bus_Err_Int_1 = 0x2A00B, // Undocumented diagnostics register @@ -293,6 +303,11 @@ private: uint8_t int_ctrl = 0; uint8_t dev_irq_lines = 0; // state of the IRQ lines + // DMA IRQ flag registers + uint8_t dma_ifr0 = 0; + uint8_t dma_ifr1 = 0; + uint8_t dma_irq = 0; + // pseudo VIA2 state uint8_t via2_ier = 0; uint8_t via2_ifr = 0;