Build up DMA channel audio infrastructure from GBA.js

This commit is contained in:
Jeffrey Pfau 2013-10-01 23:56:10 -07:00
parent 9bf6b571b1
commit 09b4a4a29a
8 changed files with 256 additions and 9 deletions

View File

@ -5,12 +5,14 @@ set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -Wno-error=type-limits --std=gnu99")
set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra --std=gnu99")
file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c)
file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c)
file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.c)
file(GLOB RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gba/renderers/video-software.c)
file(GLOB DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/*.c)
file(GLOB THIRD_PARTY ${CMAKE_SOURCE_DIR}/third-party/linenoise/linenoise.c)
include_directories(${CMAKE_SOURCE_DIR}/src/arm)
include_directories(${CMAKE_SOURCE_DIR}/src/gba)
include_directories(${CMAKE_SOURCE_DIR}/src/debugger)
include_directories(${CMAKE_SOURCE_DIR}/src/util)
include_directories(${CMAKE_SOURCE_DIR}/third-party/linenoise)
find_package(SDL 1.2 REQUIRED)
@ -31,5 +33,5 @@ else()
endif()
include_directories(${SDL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR})
add_executable(${BINARY_NAME} ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${THIRD_PARTY} ${SDL_SRC} ${MAIN_SRC})
add_executable(${BINARY_NAME} ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${THIRD_PARTY} ${SDL_SRC} ${MAIN_SRC})
target_link_libraries(${BINARY_NAME} m pthread ${SDL_LIBRARY} ${OPENGL_LIBRARY})

View File

@ -1,13 +1,61 @@
#include "gba-audio.h"
#include "gba.h"
#include "gba-io.h"
const unsigned GBA_AUDIO_SAMPLES = 4096 * sizeof(int32_t);
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
static void _sample(struct GBAAudio* audio);
void GBAAudioInit(struct GBAAudio* audio) {
(void)(audio);
audio->nextEvent = 0;
audio->eventDiff = 0;
audio->nextSample = 0;
audio->sampleRate = 0x8000;
audio->sampleInterval = GBA_ARM7TDMI_FREQUENCY / audio->sampleRate;
CircleBufferInit(&audio->left, GBA_AUDIO_SAMPLES);
CircleBufferInit(&audio->right, GBA_AUDIO_SAMPLES);
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE);
CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE);
}
void GBAAudioDeinit(struct GBAAudio* audio) {
(void)(audio);
CircleBufferDeinit(&audio->left);
CircleBufferDeinit(&audio->right);
CircleBufferDeinit(&audio->chA.fifo);
CircleBufferDeinit(&audio->chB.fifo);
}
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles) {
audio->nextEvent -= cycles;
if (audio->nextEvent <= 0) {
audio->nextSample -= audio->eventDiff;
if (audio->nextSample <= 0) {
_sample(audio);
audio->nextSample += audio->sampleInterval;
}
audio->nextEvent = audio->nextSample;
audio->eventDiff = audio->nextEvent;
}
return audio->nextEvent;
}
void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info) {
switch (info->dest) {
case BASE_IO | REG_FIFO_A_LO:
audio->chA.dmaSource = number;
break;
case BASE_IO | REG_FIFO_B_LO:
audio->chB.dmaSource = number;
break;
default:
GBALog(audio->p, GBA_LOG_GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest);
return;
}
info->dstControl = DMA_FIXED;
}
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value) {
@ -51,15 +99,15 @@ void GBAAudioWriteSOUND4CNT_HI(struct GBAAudio* audio, uint16_t value) {
}
void GBAAudioWriteSOUNDCNT_LO(struct GBAAudio* audio, uint16_t value) {
GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented");
audio->soundcntLo = value;
}
void GBAAudioWriteSOUNDCNT_HI(struct GBAAudio* audio, uint16_t value) {
GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented");
audio->soundcntHi = value;
}
void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value) {
GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented");
audio->soundcntX = (value & 0xF0) | (audio->soundcntX & 0x0F);
}
void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) {
@ -67,6 +115,46 @@ void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value) {
}
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value) {
GBALog(audio->p, GBA_LOG_STUB, "Audio unimplemented");
struct CircleBuffer* fifo;
switch (address) {
case REG_FIFO_A_LO:
fifo = &audio->chA.fifo;
break;
case REG_FIFO_B_LO:
fifo = &audio->chB.fifo;
break;
default:
GBALog(audio->p, GBA_LOG_ERROR, "Bad FIFO write to address 0x%03x", address);
return;
}
while (!CircleBufferWrite32(fifo, value)) {
int32_t dummy;
CircleBufferRead32(fifo, &dummy);
}
}
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId) {
struct GBAAudioFIFO* channel;
if (fifoId == 0) {
channel = &audio->chA;
} else if (fifoId == 1) {
channel = &audio->chB;
} else {
GBALog(audio->p, GBA_LOG_ERROR, "Bad FIFO write to address 0x%03x", fifoId);
return;
}
if (CircleBufferSize(&channel->fifo) < 4 * sizeof(int32_t)) {
struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
dma->nextCount = 4;
GBAMemoryServiceDMA(&audio->p->memory, channel->dmaSource, dma);
}
CircleBufferRead32(&channel->fifo, &channel->sample);
}
static void _sample(struct GBAAudio* audio) {
int32_t sampleLeft = 0;
int32_t sampleRight = 0;
CircleBufferWrite32(&audio->left, sampleLeft);
CircleBufferWrite32(&audio->right, sampleRight);
}

View File

@ -1,8 +1,12 @@
#ifndef GBA_AUDIO_H
#define GBA_AUDIO_H
#include "circle-buffer.h"
#include <stdint.h>
struct GBADMA;
union GBAAudioWave {
struct {
unsigned length : 6;
@ -94,7 +98,9 @@ struct GBAAudioChannel4 {
};
struct GBAAudioFIFO {
struct CircleBuffer fifo;
int dmaSource;
int32_t sample;
};
struct GBAAudio {
@ -107,11 +113,74 @@ struct GBAAudio {
struct GBAAudioFIFO chA;
struct GBAAudioFIFO chB;
struct CircleBuffer left;
struct CircleBuffer right;
union {
struct {
unsigned volumeRight : 3;
unsigned : 1;
unsigned volumeLeft : 3;
unsigned : 1;
unsigned ch1Right : 1;
unsigned ch2Right : 1;
unsigned ch3Right : 1;
unsigned ch4Right : 1;
unsigned ch1Left : 1;
unsigned ch2Left : 1;
unsigned ch3Left : 1;
unsigned ch4Left : 1;
};
uint16_t soundcntLo;
};
union {
struct {
unsigned volume : 2;
unsigned volumeDmaA : 1;
unsigned volumeDmaB : 1;
unsigned : 4;
unsigned dmaARight : 1;
unsigned dmaALeft : 1;
unsigned dmaATimer : 1;
unsigned dmaAReset : 1;
unsigned dmaBRight : 1;
unsigned dmaBLeft : 1;
unsigned dmaBTimer : 1;
unsigned dmaBReset : 1;
};
uint16_t soundcntHi;
};
union {
struct {
unsigned playingCh1 : 1;
unsigned playingCh2 : 1;
unsigned playingCh3 : 1;
unsigned playingCh4 : 1;
unsigned : 3;
unsigned enable : 1;
unsigned : 8;
};
uint16_t soundcntX;
};
unsigned sampleRate;
int32_t nextEvent;
int32_t eventDiff;
int32_t nextSample;
int32_t sampleInterval;
};
void GBAAudioInit(struct GBAAudio* audio);
void GBAAudioDeinit(struct GBAAudio* audio);
int32_t GBAAudioProcessEvents(struct GBAAudio* audio, int32_t cycles);
void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info);
void GBAAudioWriteSOUND1CNT_LO(struct GBAAudio* audio, uint16_t value);
void GBAAudioWriteSOUND1CNT_HI(struct GBAAudio* audio, uint16_t value);
void GBAAudioWriteSOUND1CNT_X(struct GBAAudio* audio, uint16_t value);
@ -128,5 +197,6 @@ void GBAAudioWriteSOUNDCNT_X(struct GBAAudio* audio, uint16_t value);
void GBAAudioWriteWaveRAM(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioWriteFIFO(struct GBAAudio* audio, int address, uint32_t value);
void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId);
#endif

View File

@ -634,7 +634,7 @@ void GBAMemoryScheduleDMA(struct GBAMemory* memory, int number, struct GBADMA* i
break;
case 1:
case 2:
//this.cpu.irq.audio.scheduleFIFODma(number, info);
GBAAudioScheduleFifoDma(&memory->p->audio, number, info);
break;
case 3:
//this.cpu.irq.video.scheduleVCaptureDma(dma, info);

View File

@ -14,6 +14,8 @@
#include <sys/mman.h>
#include <sys/stat.h>
const uint32_t GBA_ARM7TDMI_FREQUENCY = 0x1000000;
enum {
SP_BASE_SYSTEM = 0x03FFFF00,
SP_BASE_IRQ = 0x03FFFFA0,
@ -95,6 +97,11 @@ static void GBAProcessEvents(struct ARMBoard* board) {
nextEvent = testEvent;
}
testEvent = GBAAudioProcessEvents(&gbaBoard->p->audio, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
testEvent = GBAMemoryProcessEvents(&gbaBoard->p->memory, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;

View File

@ -7,6 +7,8 @@
#include "gba-video.h"
#include "gba-audio.h"
const uint32_t GBA_ARM7TDMI_FREQUENCY;
enum GBAIRQ {
IRQ_VBLANK = 0x0,
IRQ_HBLANK = 0x1,

59
src/util/circle-buffer.c Normal file
View File

@ -0,0 +1,59 @@
#include "circle-buffer.h"
#include <stddef.h>
#include <stdlib.h>
void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity) {
buffer->data = malloc(capacity);
buffer->capacity = capacity;
buffer->readPtr = buffer->data;
buffer->writePtr = (int8_t*) buffer->data + capacity;
}
void CircleBufferDeinit(struct CircleBuffer* buffer) {
free(buffer->data);
buffer->data = 0;
}
unsigned CircleBufferSize(const struct CircleBuffer* buffer) {
ptrdiff_t size = (int8_t*) buffer->readPtr - (int8_t*) buffer->writePtr;
if (size < 0) {
return buffer->capacity - size;
}
return size;
}
int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value) {
uint32_t* data = buffer->writePtr;
if ((int8_t*) buffer->writePtr + 1 == buffer->readPtr) {
return 0;
}
if ((int8_t*) buffer->writePtr == (int8_t*) buffer->data + buffer->capacity - 1 && buffer->readPtr == buffer->data) {
return 0;
}
*data = value;
++data;
ptrdiff_t size = (int8_t*) data - (int8_t*) buffer->data;
if (size < buffer->capacity) {
buffer->writePtr = data;
} else {
buffer->writePtr = buffer->data;
}
return 1;
}
int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value) {
uint32_t* data = buffer->readPtr;
if (buffer->readPtr == buffer->writePtr) {
return 0;
}
*value = *data;
++data;
ptrdiff_t size = (int8_t*) data - (int8_t*) buffer->data;
if (size < buffer->capacity) {
buffer->readPtr = data;
} else {
buffer->readPtr = buffer->data;
}
return 1;
}

19
src/util/circle-buffer.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef CIRCLE_BUFFER_H
#define CIRCLE_BUFFER_H
#include <stdint.h>
struct CircleBuffer {
void* data;
unsigned capacity;
void* readPtr;
void* writePtr;
};
void CircleBufferInit(struct CircleBuffer* buffer, unsigned capacity);
void CircleBufferDeinit(struct CircleBuffer* buffer);
unsigned CircleBufferSize(const struct CircleBuffer* buffer);
int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value);
int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value);
#endif