Implement DMA push method for sound.

This commit is contained in:
Maxim Poliakovski 2020-03-19 15:09:24 +01:00
parent 8e34c1657c
commit 945e63bdb2
7 changed files with 138 additions and 9 deletions

View File

@ -7,6 +7,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
add_subdirectory("${PROJECT_SOURCE_DIR}/cpu/ppc/") add_subdirectory("${PROJECT_SOURCE_DIR}/cpu/ppc/")
add_subdirectory("${PROJECT_SOURCE_DIR}/devices/") add_subdirectory("${PROJECT_SOURCE_DIR}/devices/")
add_subdirectory("${PROJECT_SOURCE_DIR}/machines/") add_subdirectory("${PROJECT_SOURCE_DIR}/machines/")
@ -15,7 +18,8 @@ add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/")
include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/devices" include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/devices"
"${PROJECT_SOURCE_DIR}/cpu/ppc" "${PROJECT_SOURCE_DIR}/cpu/ppc"
"${PROJECT_SOURCE_DIR}/debugger" "${PROJECT_SOURCE_DIR}/debugger"
"${PROJECT_SOURCE_DIR}/thirdparty") "${PROJECT_SOURCE_DIR}/thirdparty"
${SDL2_LIBRARIES})
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp" file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.cpp"
"${PROJECT_SOURCE_DIR}/debugger/*.cpp") "${PROJECT_SOURCE_DIR}/debugger/*.cpp")
@ -27,11 +31,15 @@ add_executable(dingusppc ${SOURCES} $<TARGET_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:machines> $<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>) $<TARGET_OBJECTS:loguru>)
target_link_libraries(dingusppc ${SDL2_LIBRARIES})
add_executable(testppc ${TEST_SOURCES} $<TARGET_OBJECTS:cpu_ppc> add_executable(testppc ${TEST_SOURCES} $<TARGET_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:devices> $<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:machines> $<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>) $<TARGET_OBJECTS:loguru>)
target_link_libraries(testppc ${SDL2_LIBRARIES})
add_custom_command( add_custom_command(
TARGET testppc POST_BUILD TARGET testppc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy COMMAND ${CMAKE_COMMAND} -E copy

View File

@ -25,8 +25,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <thirdparty/loguru.hpp> #include <thirdparty/loguru.hpp>
#include "endianswap.h"
#include "awacs.h" #include "awacs.h"
#include "machines/machinebase.h" #include "machines/machinebase.h"
#include "SDL.h"
static int awac_freqs[8] = {44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350};
AWACDevice::AWACDevice() AWACDevice::AWACDevice()
{ {
@ -40,6 +44,12 @@ AWACDevice::AWACDevice()
AWACDevice::~AWACDevice() AWACDevice::~AWACDevice()
{ {
delete this->audio_proc; delete this->audio_proc;
if (this->snd_buf)
delete[] this->snd_buf;
if (this->snd_out_dev)
SDL_CloseAudioDevice(snd_out_dev);
} }
uint32_t AWACDevice::snd_ctrl_read(uint32_t offset, int size) uint32_t AWACDevice::snd_ctrl_read(uint32_t offset, int size)
@ -67,7 +77,7 @@ void AWACDevice::snd_ctrl_write(uint32_t offset, uint32_t value, int size)
switch(offset) { switch(offset) {
case AWAC_SOUND_CTRL_REG: case AWAC_SOUND_CTRL_REG:
this->snd_ctrl_reg = value; this->snd_ctrl_reg = BYTESWAP_32(value);
LOG_F(INFO, "New sound control value = 0x%X", this->snd_ctrl_reg); LOG_F(INFO, "New sound control value = 0x%X", this->snd_ctrl_reg);
break; break;
case AWAC_CODEC_CTRL_REG: case AWAC_CODEC_CTRL_REG:
@ -82,3 +92,77 @@ void AWACDevice::snd_ctrl_write(uint32_t offset, uint32_t value, int size)
LOG_F(ERROR, "AWAC: unsupported register at offset 0x%X", offset); LOG_F(ERROR, "AWAC: unsupported register at offset 0x%X", offset);
} }
} }
uint32_t AWACDevice::convert_data(const uint8_t *data, int len)
{
int i;
uint16_t *p_in, *p_out;
if (len > this->buf_len) {
if (this->snd_buf)
delete this->snd_buf;
this->snd_buf = new uint8_t[len];
this->buf_len = len;
}
p_in = (uint16_t *)data;
p_out = (uint16_t *)this->snd_buf;
for (i = 0; i < len; i += 8) {
p_out[i] = p_in[i];
p_out[i+1] = p_in[i+2];
p_out[i+2] = p_in[i+1];
p_out[i+3] = p_in[i+3];
}
return i;
}
void AWACDevice::dma_start()
{
SDL_AudioSpec snd_spec, snd_settings;
SDL_zero(snd_spec);
snd_spec.freq = awac_freqs[(this->snd_ctrl_reg >> 8) & 7];
snd_spec.format = AUDIO_S16MSB; /* yes, AWAC accepts big-endian data */
snd_spec.channels = 2;
snd_spec.samples = 4096; /* buffer size, chosen empirically */
snd_spec.callback = NULL;
this->snd_out_dev = SDL_OpenAudioDevice(NULL, 0, &snd_spec, &snd_settings, 0);
if (!this->snd_out_dev) {
LOG_F(ERROR, "Could not open sound output device, error %s", SDL_GetError());
} else {
LOG_F(INFO, "Created audio output channel, sample rate = %d", snd_spec.freq);
this->wake_up = true;
}
}
void AWACDevice::dma_end()
{
if (this->snd_out_dev) {
SDL_CloseAudioDevice(this->snd_out_dev);
this->snd_out_dev = 0;
}
}
void AWACDevice::dma_push(uint8_t *buf, int size)
{
uint32_t dst_len;
dst_len = this->convert_data(buf, size);
if (dst_len) {
if (SDL_QueueAudio(this->snd_out_dev, this->snd_buf, dst_len)) {
LOG_F(ERROR, "SDL_QueueAudio error: %s", SDL_GetError());
}
}
if (this->wake_up) {
SDL_PauseAudioDevice(this->snd_out_dev, 0); /* start audio playing */
this->wake_up = false;
}
}
void AWACDevice::dma_pull(uint8_t *buf, int size)
{
}

View File

@ -30,6 +30,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cinttypes> #include <cinttypes>
#include "i2c.h" #include "i2c.h"
#include "dbdma.h"
#include "SDL.h"
/** AWAC registers offsets. */ /** AWAC registers offsets. */
enum { enum {
@ -98,7 +100,7 @@ private:
}; };
class AWACDevice { class AWACDevice : public DMACallback {
public: public:
AWACDevice(); AWACDevice();
~AWACDevice(); ~AWACDevice();
@ -106,11 +108,26 @@ public:
uint32_t snd_ctrl_read(uint32_t offset, int size); uint32_t snd_ctrl_read(uint32_t offset, int size);
void snd_ctrl_write(uint32_t offset, uint32_t value, int size); void snd_ctrl_write(uint32_t offset, uint32_t value, int size);
/* DMACallback methods */
void dma_start();
void dma_end();
void dma_push(uint8_t *buf, int size);
void dma_pull(uint8_t *buf, int size);
protected:
uint32_t convert_data(const uint8_t *data, int len);
private: private:
uint32_t snd_ctrl_reg = {0}; uint32_t snd_ctrl_reg = {0};
uint16_t control_regs[8] = {0}; /* control registers, each 12-bits wide */ uint16_t control_regs[8] = {0}; /* control registers, each 12-bits wide */
uint8_t is_busy = 0; uint8_t is_busy = 0;
AudioProcessor *audio_proc; AudioProcessor *audio_proc;
SDL_AudioDeviceID snd_out_dev = 0;
bool wake_up = false;
uint8_t* snd_buf = 0;
uint32_t buf_len = 0;
}; };

View File

@ -52,8 +52,9 @@ uint8_t DMAChannel::interpret_cmd()
LOG_F(ERROR, "non-zero i/b/w not implemented"); LOG_F(ERROR, "non-zero i/b/w not implemented");
break; break;
} }
LOG_F(INFO, "Transfer data, addr = 0x%X, length = 0x%X", this->dma_cb->dma_push(
cmd_struct.address, cmd_struct.req_count); mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count),
cmd_struct.req_count);
this->cmd_ptr += 16; this->cmd_ptr += 16;
break; break;
case 1: case 1:
@ -66,8 +67,9 @@ uint8_t DMAChannel::interpret_cmd()
LOG_F(ERROR, "non-zero i/b/w not implemented"); LOG_F(ERROR, "non-zero i/b/w not implemented");
break; break;
} }
LOG_F(INFO, "Transfer data, addr = 0x%X, length = 0x%X", this->dma_cb->dma_push(
cmd_struct.address, cmd_struct.req_count); mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count),
cmd_struct.req_count);
this->cmd_ptr += 16; this->cmd_ptr += 16;
break; break;
case 2: case 2:
@ -190,6 +192,8 @@ void DMAChannel::start()
LOG_F(INFO, "Starting DMA channel, stat = 0x%X", this->ch_stat); LOG_F(INFO, "Starting DMA channel, stat = 0x%X", this->ch_stat);
this->dma_cb->dma_start();
while (this->interpret_cmd() != 7) { while (this->interpret_cmd() != 7) {
} }
} }
@ -212,4 +216,5 @@ void DMAChannel::abort()
void DMAChannel::pause() void DMAChannel::pause()
{ {
LOG_F(INFO, "Pausing DMA channel"); LOG_F(INFO, "Pausing DMA channel");
this->dma_cb->dma_end();
} }

View File

@ -59,9 +59,17 @@ typedef struct DMACmd {
uint16_t xfer_stat; uint16_t xfer_stat;
} DMACmd; } DMACmd;
class DMACallback {
public:
virtual void dma_start(void) = 0;
virtual void dma_end(void) = 0;
virtual void dma_push(uint8_t *buf, int size) = 0;
virtual void dma_pull(uint8_t *buf, int size) = 0;
};
class DMAChannel { class DMAChannel {
public: public:
DMAChannel() = default; DMAChannel(DMACallback *cb) { this->dma_cb = cb; };
~DMAChannel() = default; ~DMAChannel() = default;
uint32_t reg_read(uint32_t offset, int size); uint32_t reg_read(uint32_t offset, int size);
@ -77,6 +85,7 @@ protected:
void pause(void); void pause(void);
private: private:
DMACallback *dma_cb = 0;
uint16_t ch_stat = 0; uint16_t ch_stat = 0;
uint32_t cmd_ptr = 0; uint32_t cmd_ptr = 0;
}; };

View File

@ -43,7 +43,7 @@ HeathrowIC::HeathrowIC() : PCIDevice("mac-io/heathrow")
gMachineObj->add_subdevice("ViaCuda", this->viacuda); gMachineObj->add_subdevice("ViaCuda", this->viacuda);
this->screamer = new AWACDevice(); this->screamer = new AWACDevice();
this->snd_out_dma = new DMAChannel(); this->snd_out_dma = new DMAChannel(this->screamer);
} }
HeathrowIC::~HeathrowIC() HeathrowIC::~HeathrowIC()

View File

@ -30,6 +30,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "ppcemu.h" #include "ppcemu.h"
#include "debugger/debugger.h" #include "debugger/debugger.h"
#include "machines/machinefactory.h" #include "machines/machinefactory.h"
#include "SDL.h"
using namespace std; using namespace std;
@ -71,6 +72,11 @@ int main(int argc, char **argv)
goto bail; goto bail;
} }
if (SDL_Init(SDL_INIT_AUDIO)){
LOG_F(ERROR, "SDL_Init error: %s", SDL_GetError());
goto bail;
}
if ((checker == "1") || (checker == "realtime") || \ if ((checker == "1") || (checker == "realtime") || \
(checker == "-realtime") || (checker == "/realtime")) { (checker == "-realtime") || (checker == "/realtime")) {
ppc_exec(); ppc_exec();