diff --git a/devices/dbdma.cpp b/devices/dbdma.cpp new file mode 100644 index 0000000..439c779 --- /dev/null +++ b/devices/dbdma.cpp @@ -0,0 +1,139 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-20 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Descriptor-based direct memory access emulation. */ + +#include +#include +#include "dbdma.h" +#include "endianswap.h" + +uint32_t DMAChannel::reg_read(uint32_t offset, int size) +{ + uint32_t res = 0; + + if (size != 4) { + LOG_F(WARNING, "Unsupported non-DWORD read from DMA channel"); + return 0; + } + + switch(offset) { + case DMAReg::CH_CTRL: + res = 0; /* ChannelControl reads as 0 (DBDMA spec 5.5.1, table 74) */ + break; + case DMAReg::CH_STAT: + res = BYTESWAP_32(this->ch_stat); + break; + default: + LOG_F(WARNING, "Unsupported DMA channel register 0x%X", offset); + } + + return res; +} + +void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) +{ + uint16_t mask, old_stat, new_stat; + + if (size != 4) { + LOG_F(WARNING, "Unsupported non-DWORD write to DMA channel"); + return; + } + + value = BYTESWAP_32(value); + old_stat = this->ch_stat; + + switch(offset) { + case DMAReg::CH_CTRL: + mask = value >> 16; + new_stat = (value & mask & 0xF0FFU) | (old_stat & ~mask); + LOG_F(INFO, "New ChannelStatus value = 0x%X", new_stat); + + if ((new_stat & CH_STAT_RUN) != (old_stat & CH_STAT_RUN)) { + if (new_stat & CH_STAT_RUN) { + new_stat |= CH_STAT_ACTIVE; + this->ch_stat = new_stat; + this->start(); + } else { + new_stat &= ~CH_STAT_ACTIVE; + new_stat &= ~CH_STAT_DEAD; + this->ch_stat = new_stat; + this->abort(); + } + } else if ((new_stat & CH_STAT_WAKE) != (old_stat & CH_STAT_WAKE)) { + new_stat |= CH_STAT_ACTIVE; + this->ch_stat = new_stat; + this->resume(); + } else if ((new_stat & CH_STAT_PAUSE) != (old_stat & CH_STAT_PAUSE)) { + if (new_stat & CH_STAT_PAUSE) { + new_stat &= ~CH_STAT_ACTIVE; + this->ch_stat = new_stat; + this->pause(); + } + } + if (new_stat & CH_STAT_FLUSH) { + LOG_F(WARNING, "DMA flush not implemented!"); + new_stat &= ~CH_STAT_FLUSH; + this->ch_stat = new_stat; + } + break; + case DMAReg::CH_STAT: + break; /* ingore writes to ChannelStatus */ + case DMAReg::CMD_PTR_LO: + if (!(this->ch_stat & CH_STAT_RUN) && !(this->ch_stat & CH_STAT_ACTIVE)) { + this->cmd_ptr = BYTESWAP_32(value); + LOG_F(INFO, "CommandPtrLo set to 0x%X", this->cmd_ptr); + } + break; + default: + LOG_F(WARNING, "Unsupported DMA channel register 0x%X", offset); + } +} + +void DMAChannel::start() +{ + if (this->ch_stat & CH_STAT_PAUSE) { + LOG_F(WARNING, "Cannot start DMA channel, PAUSE bit is set"); + return; + } + + LOG_F(INFO, "Starting DMA channel, stat = 0x%X", this->ch_stat); +} + +void DMAChannel::resume() +{ + if (this->ch_stat & CH_STAT_PAUSE) { + LOG_F(WARNING, "Cannot resume DMA channel, PAUSE bit is set"); + return; + } + + LOG_F(INFO, "Resuming DMA channel"); +} + +void DMAChannel::abort() +{ + LOG_F(INFO, "Aborting DMA channel"); +} + +void DMAChannel::pause() +{ + LOG_F(INFO, "Pausing DMA channel"); +} diff --git a/devices/dbdma.h b/devices/dbdma.h new file mode 100644 index 0000000..1016559 --- /dev/null +++ b/devices/dbdma.h @@ -0,0 +1,70 @@ +/* +DingusPPC - The Experimental PowerPC Macintosh emulator +Copyright (C) 2018-20 divingkatae and maximum + (theweirdo) spatium + +(Contact divingkatae#1017 or powermax#2286 on Discord for more info) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/** @file Descriptor-based direct memory access emulation. + + Official documentation can be found in the fifth chapter of the book + "Macintosh Technology in the Common Hardware Reference Platform" + by Apple Computer, Inc. + */ + +#ifndef DB_DMA_H +#define DB_DMA_H + +#include + +/** DBDMA Channel registers offsets */ +enum DMAReg : uint32_t { + CH_CTRL = 0, + CH_STAT = 4, + CMD_PTR_LO = 12, +}; + +/** Channel Status bits (DBDMA spec, 5.5.3) */ +enum { + CH_STAT_ACTIVE = 0x400, + CH_STAT_DEAD = 0x800, + CH_STAT_WAKE = 0x1000, + CH_STAT_FLUSH = 0x2000, + CH_STAT_PAUSE = 0x4000, + CH_STAT_RUN = 0x8000 +}; + +class DMAChannel { +public: + DMAChannel() = default; + ~DMAChannel() = default; + + uint32_t reg_read(uint32_t offset, int size); + void reg_write(uint32_t offset, uint32_t value, int size); + +protected: + void start(void); + void resume(void); + void abort(void); + void pause(void); + +private: + uint16_t ch_stat = 0; + uint32_t cmd_ptr = 0; +}; + +#endif /* DB_DMA_H */ diff --git a/devices/heathrow.cpp b/devices/heathrow.cpp index 796a274..4c1a14a 100644 --- a/devices/heathrow.cpp +++ b/devices/heathrow.cpp @@ -25,6 +25,7 @@ along with this program. If not, see . #include "macio.h" #include "viacuda.h" #include "awacs.h" +#include "dbdma.h" #include "machines/machinebase.h" /** Heathrow Mac I/O device emulation. @@ -42,6 +43,7 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow") gMachineObj->add_subdevice("ViaCuda", this->viacuda); this->screamer = new AWACDevice(); + this->snd_out_dma = new DMAChannel(); } HeathrowIC::~HeathrowIC() @@ -83,6 +85,33 @@ void HeathrowIC::pci_cfg_write(uint32_t reg_offs, uint32_t value, uint32_t size) } } +uint32_t HeathrowIC::dma_read(uint32_t offset, int size) +{ + uint32_t res = 0; + + switch(offset >> 8) { + case 8: + res = this->snd_out_dma->reg_read(offset & 0xFF, size); + break; + default: + LOG_F(WARNING, "Unsupported DMA channel read, offset=0x%X", offset); + } + + return res; +} + +void HeathrowIC::dma_write(uint32_t offset, uint32_t value, int size) +{ + switch(offset >> 8) { + case 8: + this->snd_out_dma->reg_write(offset & 0xFF, value, size); + break; + default: + LOG_F(WARNING, "Unsupported DMA channel write, offset=0x%X, val=0x%X", offset, value); + } +} + + uint32_t HeathrowIC::read(uint32_t offset, int size) { uint32_t res = 0; @@ -96,7 +125,7 @@ uint32_t HeathrowIC::read(uint32_t offset, int size) res = mio_ctrl_read(offset, size); break; case 8: - LOG_F(WARNING, "Attempting to read DMA channel register space \n"); + res = dma_read(offset - 0x8000, size); break; case 0x14: res = this->screamer->snd_ctrl_read(offset - 0x14000, size); @@ -128,7 +157,7 @@ void HeathrowIC::write(uint32_t offset, uint32_t value, int size) mio_ctrl_write(offset, value, size); break; case 8: - LOG_F(WARNING, "Attempting to write to DMA channel register space \n"); + dma_write(offset - 0x8000, value, size); break; case 0x14: this->screamer->snd_ctrl_write(offset - 0x14000, value, size); diff --git a/devices/macio.h b/devices/macio.h index 603d3a5..97c37a3 100644 --- a/devices/macio.h +++ b/devices/macio.h @@ -60,6 +60,7 @@ along with this program. If not, see . #include "viacuda.h" #include "nvram.h" #include "awacs.h" +#include "dbdma.h" /** Heathrow ASIC emulation @@ -104,6 +105,9 @@ public: void write(uint32_t offset, uint32_t value, int size); protected: + uint32_t dma_read(uint32_t offset, int size); + void dma_write(uint32_t offset, uint32_t value, int size); + uint32_t mio_ctrl_read(uint32_t offset, int size); void mio_ctrl_write(uint32_t offset, uint32_t value, int size); @@ -130,6 +134,8 @@ private: ViaCuda *viacuda; /* VIA cell with Cuda MCU attached to it */ NVram *nvram; /* NVRAM cell */ AWACDevice *screamer; /* Screamer audio codec instance */ + + DMAChannel *snd_out_dma; }; #endif /* MACIO_H */