mirror of https://github.com/mgba-emu/mgba.git
Build up DMA channel audio infrastructure from GBA.js
This commit is contained in:
parent
9bf6b571b1
commit
09b4a4a29a
|
@ -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})
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue