From 9425edc406832fd98f0f1262a5ab1ea5de16ab39 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Fri, 29 Aug 2014 16:19:58 -0400 Subject: [PATCH 01/14] Added HID event code, based on the GSP event code. Added event id check in gspWaitForEvent(). --- libctru/include/3ds/services/hid.h | 17 ++++++++++++++++- libctru/source/services/gsp.c | 2 ++ libctru/source/services/hid.c | 23 +++++++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/libctru/include/3ds/services/hid.h b/libctru/include/3ds/services/hid.h index 17e13ab..b39898f 100644 --- a/libctru/include/3ds/services/hid.h +++ b/libctru/include/3ds/services/hid.h @@ -1,5 +1,7 @@ #pragma once +//See also: http://3dbrew.org/wiki/HID_Services http://3dbrew.org/wiki/HID_Shared_Memory + #define HID_SHAREDMEM_DEFAULT (0x10000000) typedef enum @@ -39,6 +41,17 @@ typedef struct s16 dx, dy; } circlePosition; +typedef enum +{ + HIDEVENT_PAD0 = 0, //"Event signaled by HID-module, when the sharedmem+0(PAD/circle-pad)/+0xA8(touch-screen) region was updated." + HIDEVENT_PAD1, //"Event signaled by HID-module, when the sharedmem+0(PAD/circle-pad)/+0xA8(touch-screen) region was updated." + HIDEVENT_Accel, //"Event signaled by HID-module, when the sharedmem accelerometer state was updated." + HIDEVENT_Gyro, //"Event signaled by HID-module, when the sharedmem gyroscope state was updated." + HIDEVENT_DebugPad, //"Event signaled by HID-module, when the sharedmem DebugPad state was updated." + + HIDEVENT_MAX, // used to know how many events there are +} HID_Event; + extern Handle hidMemHandle; extern vu32* hidSharedMem; @@ -52,6 +65,8 @@ u32 hidKeysUp(); void hidTouchRead(touchPosition* pos); void hidCircleRead(circlePosition* pos); +void hidWaitForEvent(HID_Event id, bool nextEvent); + // libnds compatibility defines #define scanKeys hidScanInput #define keysHeld hidKeysHeld @@ -60,7 +75,7 @@ void hidCircleRead(circlePosition* pos); #define touchRead hidTouchRead #define circleRead hidCircleRead -Result HIDUSER_GetSharedMem(Handle* outMemHandle); +Result HIDUSER_GetHandles(Handle* outMemHandle, Handle *eventpad0, Handle *eventpad1, Handle *eventaccel, Handle *eventgyro, Handle *eventdebugpad); Result HIDUSER_EnableAccelerometer(); Result HIDUSER_DisableAccelerometer(); Result HIDUSER_EnableGyroscope(); diff --git a/libctru/source/services/gsp.c b/libctru/source/services/gsp.c index bd76035..7c64019 100644 --- a/libctru/source/services/gsp.c +++ b/libctru/source/services/gsp.c @@ -68,6 +68,8 @@ void gspExitEventHandler() void gspWaitForEvent(GSP_Event id, bool nextEvent) { + if(id>=GSPEVENT_MAX)return; + if (nextEvent) svcClearEvent(gspEvents[id]); svcWaitSynchronization(gspEvents[id], U64_MAX); diff --git a/libctru/source/services/hid.c b/libctru/source/services/hid.c index 1265ec9..aaff8e1 100644 --- a/libctru/source/services/hid.c +++ b/libctru/source/services/hid.c @@ -8,6 +8,8 @@ Handle hidHandle; Handle hidMemHandle; +Handle hidEvents[5]; + vu32* hidSharedMem; static u32 kOld, kHeld, kDown, kUp; @@ -24,7 +26,7 @@ Result hidInit(u32* sharedMem) if((ret=srvGetServiceHandle(&hidHandle, "hid:USER")))return ret; // Get sharedmem handle. - if((ret=HIDUSER_GetSharedMem(&hidMemHandle))) goto cleanup1; + if((ret=HIDUSER_GetHandles(&hidMemHandle, &hidEvents[HIDEVENT_PAD0], &hidEvents[HIDEVENT_PAD1], &hidEvents[HIDEVENT_Accel], &hidEvents[HIDEVENT_Gyro], &hidEvents[HIDEVENT_DebugPad]))) goto cleanup1; // Map HID shared memory at addr "sharedMem". hidSharedMem=sharedMem; @@ -49,6 +51,17 @@ void hidExit() svcCloseHandle(hidHandle); } +void hidWaitForEvent(HID_Event id, bool nextEvent) +{ + if(id>=HIDEVENT_MAX)return; + + if (nextEvent) + svcClearEvent(hidEvents[id]); + svcWaitSynchronization(hidEvents[id], U64_MAX); + if (!nextEvent) + svcClearEvent(hidEvents[id]); +} + void hidScanInput() { kOld = kHeld; @@ -93,7 +106,7 @@ void hidCircleRead(circlePosition* pos) if (pos) *pos = cPos; } -Result HIDUSER_GetSharedMem(Handle* outMemHandle) +Result HIDUSER_GetHandles(Handle* outMemHandle, Handle *eventpad0, Handle *eventpad1, Handle *eventaccel, Handle *eventgyro, Handle *eventdebugpad) { u32* cmdbuf=getThreadCommandBuffer(); cmdbuf[0]=0xa0000; //request header code @@ -103,6 +116,12 @@ Result HIDUSER_GetSharedMem(Handle* outMemHandle) if(outMemHandle)*outMemHandle=cmdbuf[3]; + if(eventpad0)*eventpad0=cmdbuf[4]; + if(eventpad1)*eventpad1=cmdbuf[5]; + if(eventaccel)*eventaccel=cmdbuf[6]; + if(eventgyro)*eventgyro=cmdbuf[7]; + if(eventdebugpad)*eventdebugpad=cmdbuf[8]; + return cmdbuf[1]; } From 62f26e87604f7f351645586d582830860241a140 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sat, 30 Aug 2014 20:41:09 -0400 Subject: [PATCH 02/14] Improved HID code, and added Accelerometer/Gyroscope support. --- libctru/include/3ds/services/hid.h | 17 +++++++ libctru/source/services/hid.c | 74 ++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/libctru/include/3ds/services/hid.h b/libctru/include/3ds/services/hid.h index b39898f..3333abb 100644 --- a/libctru/include/3ds/services/hid.h +++ b/libctru/include/3ds/services/hid.h @@ -41,6 +41,20 @@ typedef struct s16 dx, dy; } circlePosition; +typedef struct +{ + s16 x; + s16 y; + s16 z; +} accelVector; + +typedef struct +{ + s16 x;//roll + s16 z;//yaw + s16 y;//pitch +} angularRate; + typedef enum { HIDEVENT_PAD0 = 0, //"Event signaled by HID-module, when the sharedmem+0(PAD/circle-pad)/+0xA8(touch-screen) region was updated." @@ -64,6 +78,8 @@ u32 hidKeysDown(); u32 hidKeysUp(); void hidTouchRead(touchPosition* pos); void hidCircleRead(circlePosition* pos); +void hidAccelRead(accelVector* vector); +void hidGyroRead(angularRate* rate); void hidWaitForEvent(HID_Event id, bool nextEvent); @@ -80,3 +96,4 @@ Result HIDUSER_EnableAccelerometer(); Result HIDUSER_DisableAccelerometer(); Result HIDUSER_EnableGyroscope(); Result HIDUSER_DisableGyroscope(); + diff --git a/libctru/source/services/hid.c b/libctru/source/services/hid.c index aaff8e1..9e32566 100644 --- a/libctru/source/services/hid.c +++ b/libctru/source/services/hid.c @@ -15,6 +15,8 @@ vu32* hidSharedMem; static u32 kOld, kHeld, kDown, kUp; static touchPosition tPos; static circlePosition cPos; +static accelVector aVec; +static angularRate gRate; Result hidInit(u32* sharedMem) @@ -62,23 +64,66 @@ void hidWaitForEvent(HID_Event id, bool nextEvent) svcClearEvent(hidEvents[id]); } +u32 hidCheckSectionUpdateTime(vu32 *sharedmem_section, u32 id) +{ + s64 tick0=0, tick1=0; + + if(id==0) + { + tick0 = *((u64*)&sharedmem_section[0]); + tick1 = *((u64*)&sharedmem_section[2]); + + if(tick0==tick1 || tick0<0 || tick1<0)return 1; + } + + return 0; +} + void hidScanInput() { + u32 Id=0; + kOld = kHeld; - u32 padId = hidSharedMem[4]; - if(padId>7)padId=7; - kHeld = hidSharedMem[10 + padId*4]; - cPos = *(circlePosition*)&hidSharedMem[10 + padId*4 + 3]; + kHeld = 0; + memset(&cPos, 0, sizeof(circlePosition)); + memset(&tPos, 0, sizeof(touchPosition)); + memset(&aVec, 0, sizeof(accelVector)); + memset(&gRate, 0, sizeof(angularRate)); - u32 touchId = hidSharedMem[42 + 4]; - if(touchId>7)touchId=7; - tPos = *(touchPosition*)&hidSharedMem[42 + 8 + touchId*2]; - if (hidSharedMem[42 + 8 + touchId*2 + 1]) - kHeld |= KEY_TOUCH; + Id = hidSharedMem[4];//PAD / circle-pad + if(Id>7)Id=7; + if(hidCheckSectionUpdateTime(hidSharedMem, Id)==0) + { + kHeld = hidSharedMem[10 + Id*4]; + cPos = *(circlePosition*)&hidSharedMem[10 + Id*4 + 3]; + } + + Id = hidSharedMem[42 + 4];//Touch-screen + if(Id>7)Id=7; + if(hidCheckSectionUpdateTime(&hidSharedMem[42], Id)==0) + { + tPos = *(touchPosition*)&hidSharedMem[42 + 8 + Id*2]; + if (hidSharedMem[42 + 8 + Id*2 + 1]) + kHeld |= KEY_TOUCH; + } kDown = (~kOld) & kHeld; kUp = kOld & (~kHeld); + + Id = hidSharedMem[66 + 4];//Accelerometer + if(Id>7)Id=7; + if(hidCheckSectionUpdateTime(&hidSharedMem[66], Id)==0) + { + aVec = *(accelVector*)&hidSharedMem[66 + 8 + Id*2]; + } + + Id = hidSharedMem[86 + 4];//Gyroscope + if(Id>31)Id=31; + if(hidCheckSectionUpdateTime(&hidSharedMem[86], Id)==0) + { + gRate = *(angularRate*)&hidSharedMem[86 + 8 + Id*2]; + } } u32 hidKeysHeld() @@ -106,6 +151,16 @@ void hidCircleRead(circlePosition* pos) if (pos) *pos = cPos; } +void hidAccelRead(accelVector* vector) +{ + if (vector) *vector = aVec; +} + +void hidGyroRead(angularRate* rate) +{ + if (rate) *rate = gRate; +} + Result HIDUSER_GetHandles(Handle* outMemHandle, Handle *eventpad0, Handle *eventpad1, Handle *eventaccel, Handle *eventgyro, Handle *eventdebugpad) { u32* cmdbuf=getThreadCommandBuffer(); @@ -168,3 +223,4 @@ Result HIDUSER_DisableGyroscope() return cmdbuf[1]; } + From 3d34e123eea9bf6d8636ad3a719e92bd43691d00 Mon Sep 17 00:00:00 2001 From: profi200 Date: Mon, 1 Sep 2014 15:49:32 +0200 Subject: [PATCH 03/14] Added functions for sysCore usage --- libctru/include/3ds/services/apt.h | 2 ++ libctru/source/services/apt.c | 32 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/libctru/include/3ds/services/apt.h b/libctru/include/3ds/services/apt.h index 1a1e644..9377c0b 100644 --- a/libctru/include/3ds/services/apt.h +++ b/libctru/include/3ds/services/apt.h @@ -67,3 +67,5 @@ Result APT_ReplySleepQuery(Handle* handle, NS_APPID appID, u32 a); Result APT_ReplySleepNotificationComplete(Handle* handle, NS_APPID appID); Result APT_PrepareToCloseApplication(Handle* handle, u8 a); Result APT_CloseApplication(Handle* handle, u32 a, u32 b, u32 c); +Result APT_SetAppCpuTimeLimit(Handle* handle, u32 percent); +Result APT_GetAppCpuTimeLimit(Handle* handle, u32 *percent); diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index bda13b6..d2f65d9 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -743,3 +743,35 @@ Result APT_CloseApplication(Handle* handle, u32 a, u32 b, u32 c) return cmdbuf[1]; } + +//See http://3dbrew.org/APT:SetApplicationCpuTimeLimit +Result APT_SetAppCpuTimeLimit(Handle* handle, u32 percent) +{ + if(!handle)handle=&aptuHandle; + + u32* cmdbuf=getThreadCommandBuffer(); + cmdbuf[0]=0x4F0080; + cmdbuf[1]=1; + cmdbuf[2]=percent; + + Result ret=0; + if((ret=svcSendSyncRequest(*handle)))return ret; + + return cmdbuf[1]; +} + +Result APT_GetAppCpuTimeLimit(Handle* handle, u32 *percent) +{ + if(!handle)handle=&aptuHandle; + + u32* cmdbuf=getThreadCommandBuffer(); + cmdbuf[0]=0x500040; + cmdbuf[1]=1; + + Result ret=0; + if((ret=svcSendSyncRequest(*handle)))return ret; + + if(percent)*percent=cmdbuf[2]; + + return cmdbuf[1]; +} From a2f32f91c1ad914ffb50f988288309210c895e19 Mon Sep 17 00:00:00 2001 From: profi200 Date: Mon, 1 Sep 2014 15:51:53 +0200 Subject: [PATCH 04/14] Now the same as in master --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 01e0c2a..eabf74c 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,8 @@ library for writing user mode arm11 code for the 3DS (CTR) the goal with this is to create a very straightforward interface with the 3DS's OS. it is not meant to provide higher level functions; to put things in perspective, the purpose of ctrulib would be to sit between the OS and a possible port of SDL rather than replace it. + +license +======= + +you may do whatever the hell you want with ctrulib as long as you give proper credit to its authors. that includes making commercial games/apps that use ctrulib. i'd personally rather you didn't sell ctrulib itself, because that would not be nice of you, but i'm not a lawyer and it's not like i'm going to sue you. From aa77f9b1e2b42ad79bfbb9f555a17b144680da71 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Wed, 3 Sep 2014 14:36:05 -0400 Subject: [PATCH 05/14] Added code for using the microphone and an example app for it. --- examples/mic/Makefile | 139 +++++++++++++++ examples/mic/README.md | 5 + examples/mic/source/main.c | 95 ++++++++++ libctru/include/3ds.h | 1 + libctru/include/3ds/services/mic.h | 20 +++ libctru/source/services/mic.c | 270 +++++++++++++++++++++++++++++ 6 files changed, 530 insertions(+) create mode 100644 examples/mic/Makefile create mode 100644 examples/mic/README.md create mode 100644 examples/mic/source/main.c create mode 100644 libctru/include/3ds/services/mic.h create mode 100644 libctru/source/services/mic.c diff --git a/examples/mic/Makefile b/examples/mic/Makefile new file mode 100644 index 0000000..9a74360 --- /dev/null +++ b/examples/mic/Makefile @@ -0,0 +1,139 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +ifeq ($(strip $(CTRULIB)),) +# THIS IS TEMPORARY - in the future it should be at $(DEVKITPRO)/libctru +$(error "Please set CTRULIB in your environment. export CTRULIB=libctru") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# SPECS is the directory containing the important build and link files +#--------------------------------------------------------------------------------- +export TARGET := $(shell basename $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include + + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore + +CFLAGS := -g -Wall -O2 -mword-relocations -save-temps \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) \ + -Wl,-Map,$(TARGET).map + +LIBS := -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).3dsx : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/examples/mic/README.md b/examples/mic/README.md new file mode 100644 index 0000000..37a7328 --- /dev/null +++ b/examples/mic/README.md @@ -0,0 +1,5 @@ +arm11u +======= + +Example for using the microphone with ctrulib. Hold down the A button to record, the app will then play the recorded audio once the A button is released. Roughly 32 seconds of audio can be recorded with the default audiobuf size in this app. + diff --git a/examples/mic/source/main.c b/examples/mic/source/main.c new file mode 100644 index 0000000..022902a --- /dev/null +++ b/examples/mic/source/main.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include <3ds.h> + +int main() +{ + u8 *framebuf; + u32 *sharedmem = NULL, sharedmem_size = 0x30000; + u8 *audiobuf; + u32 audiobuf_size = 0x100000, audiobuf_pos = 0; + u8 control=0x40; + + srvInit(); + aptInit(); + gfxInit(); + hidInit(NULL); + aptSetupEventHandler(); + + CSND_initialize(NULL); + + sharedmem = (u32*)memalign(0x1000, sharedmem_size); + audiobuf = linearAlloc(audiobuf_size); + + MIC_Initialize(sharedmem, sharedmem_size, control, 0, 3, 1, 1);//See mic.h. + + APP_STATUS status; + while((status=aptGetStatus())!=APP_EXITING) + { + if(status==APP_RUNNING) + { + framebuf = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + hidScanInput(); + + if(hidKeysDown() & KEY_A) + { + audiobuf_pos = 0; + + CSND_setchannel_playbackstate(0x8, 0);//Stop audio playback. + CSND_sharedmemtype0_cmdupdatestate(0); + + MIC_SetRecording(1); + + memset(framebuf, 0x20, 0x46500); + } + + if((hidKeysHeld() & KEY_A) && audiobuf_pos < audiobuf_size) + { + audiobuf_pos+= MIC_ReadAudioData(&audiobuf[audiobuf_pos], audiobuf_size-audiobuf_pos, 1); + if(audiobuf_pos > audiobuf_size)audiobuf_pos = audiobuf_size; + + memset(framebuf, 0x60, 0x46500); + } + + if(hidKeysUp() & KEY_A) + { + MIC_SetRecording(0); + GSPGPU_FlushDataCache(NULL, audiobuf, audiobuf_pos); + CSND_playsound(0x8, CSND_LOOP_DISABLE, CSND_ENCODING_PCM16, 16000, (u32*)audiobuf, NULL, audiobuf_pos, 2, 0); + + memset(framebuf, 0xe0, 0x46500); + + gfxFlushBuffers(); + gfxSwapBuffers(); + + framebuf = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + memset(framebuf, 0xe0, 0x46500); + } + + gfxFlushBuffers(); + gfxSwapBuffers(); + } + else if(status == APP_SUSPENDING) + { + aptReturnToMenu(); + } + else if(status == APP_SLEEPMODE) + { + aptWaitStatusEvent(); + } + gspWaitForVBlank(); + } + + MIC_Shutdown(); + + CSND_shutdown(); + + hidExit(); + gfxExit(); + aptExit(); + srvExit(); + return 0; +} + diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index f2a9367..1f17a92 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -23,6 +23,7 @@ extern "C" { #include <3ds/services/ir.h> #include <3ds/services/ptm.h> #include <3ds/services/soc.h> +#include <3ds/services/mic.h> #include <3ds/gpu/gx.h> #include <3ds/gpu/gpu.h> diff --git a/libctru/include/3ds/services/mic.h b/libctru/include/3ds/services/mic.h new file mode 100644 index 0000000..e0c0cae --- /dev/null +++ b/libctru/include/3ds/services/mic.h @@ -0,0 +1,20 @@ +#pragma once + +//See also: http://3dbrew.org/wiki/MIC_Services + +Result MIC_Initialize(u32 *sharedmem, u32 sharedmem_size, u8 control, u8 recording, u8 unk0, u8 unk1, u8 unk2);//sharedmem_size = audiodata size + 4, aligned to 0x1000-bytes. The sharedmem ptr must be 0x1000-bytes aligned. The offical 3ds-sound app uses the following values for unk0-unk2: 3, 1, and 1. +Result MIC_Shutdown(); +u32 MIC_GetSharedMemOffsetValue(); +u32 MIC_ReadAudioData(u8 *outbuf, u32 readsize, u32 waitforevent);//Reads MIC audio data. When waitforevent is non-zero, this clears the event, then waits for MIC-module to signal it again when audio data is written to shared-mem. The return value is the actual byte-size of the read data. + +Result MIC_MapSharedMem(Handle handle, u32 size); +Result MIC_UnmapSharedMem(); +Result MIC_cmd3_Initialize(u8 unk0, u8 unk1, u32 sharedmem_baseoffset, u32 sharedmem_endoffset, u8 unk2); +Result MIC_cmd5(); +Result MIC_GetCNTBit15(u8 *out); +Result MIC_GetEventHandle(Handle *handle); +Result MIC_SetControl(u8 value);//See here: http://3dbrew.org/wiki/MIC_Services +Result MIC_GetControl(u8 *value); +Result MIC_SetRecording(u8 value); +Result MIC_IsRecoding(u8 *value); + diff --git a/libctru/source/services/mic.c b/libctru/source/services/mic.c new file mode 100644 index 0000000..395792b --- /dev/null +++ b/libctru/source/services/mic.c @@ -0,0 +1,270 @@ +#include +#include +#include <3ds.h> + +//See also: http://3dbrew.org/wiki/MIC_Services + +Handle MIC_handle; + +static u8 *MIC_sharedmem; +static u32 MIC_sharedmem_size; +static u32 *MIC_sharedmem_offsetfield, MIC_sharedmem_offsetfield_location; +static Handle MIC_sharedmem_handle; +static Handle MIC_event; + +static u32 MIC_prev_endpos, MIC_cur_endpos; + +Result MIC_Initialize(u32 *sharedmem, u32 sharedmem_size, u8 control, u8 recording, u8 unk0, u8 unk1, u8 unk2) +{ + Result ret=0; + + MIC_sharedmem = (u8*)sharedmem; + MIC_sharedmem_size = sharedmem_size; + MIC_sharedmem_offsetfield_location = MIC_sharedmem_size - 4; + MIC_sharedmem_offsetfield = (u32*)&MIC_sharedmem[MIC_sharedmem_offsetfield_location]; + MIC_event = 0; + MIC_prev_endpos = 0; + MIC_cur_endpos = 0; + + ret = srvGetServiceHandle(&MIC_handle, "mic:u"); + if(ret!=0)return ret; + + ret = svcCreateMemoryBlock(&MIC_sharedmem_handle, (u32)MIC_sharedmem, MIC_sharedmem_size, 3, 3); + if(ret!=0)return ret; + + ret = MIC_SetControl(control); + if(ret!=0)return ret; + + ret = MIC_MapSharedMem(MIC_sharedmem_handle, sharedmem_size); + if(ret!=0)return ret; + + ret = MIC_SetRecording(recording); + if(ret!=0)return ret; + + ret = MIC_cmd3_Initialize(unk0, unk1, 0, MIC_sharedmem_size-4, unk2); + if(ret!=0)return ret; + + ret = MIC_GetEventHandle(&MIC_event); + if(ret!=0)return ret; + svcClearEvent(MIC_event); + + return 0; +} + +Result MIC_Shutdown() +{ + Result ret=0; + + MIC_cmd5(); + MIC_SetRecording(0); + + ret = MIC_UnmapSharedMem(); + if(ret!=0)return ret; + + MIC_cmd5(); + + ret = svcUnmapMemoryBlock(MIC_sharedmem_handle, (u32)MIC_sharedmem); + if(ret!=0)return ret; + + ret = svcCloseHandle(MIC_sharedmem_handle); + if(ret!=0)return ret; + + ret = svcCloseHandle(MIC_event); + if(ret!=0)return ret; + + ret = svcCloseHandle(MIC_handle); + if(ret!=0)return ret; + + MIC_sharedmem_offsetfield = NULL; + MIC_sharedmem = NULL; + MIC_sharedmem_size = 0; + MIC_handle = 0; + MIC_event = 0; + + return 0; +} + +u32 MIC_GetSharedMemOffsetValue() +{ + u32 pos = 0; + + if(MIC_sharedmem_offsetfield==NULL)return pos; + pos = *MIC_sharedmem_offsetfield; + if(pos > MIC_sharedmem_offsetfield_location)pos = MIC_sharedmem_offsetfield_location; + + return pos; +} + +u32 MIC_ReadAudioData(u8 *outbuf, u32 readsize, u32 waitforevent) +{ + u32 pos = 0, bufpos = 0; + + if(waitforevent) + { + svcClearEvent(MIC_event); + svcWaitSynchronization(MIC_event, U64_MAX); + } + + MIC_prev_endpos = MIC_cur_endpos; + MIC_cur_endpos = MIC_GetSharedMemOffsetValue(); + pos = MIC_prev_endpos; + + while(pos != MIC_cur_endpos) + { + if(pos >= MIC_sharedmem_offsetfield_location)pos = 0; + if(bufpos>=readsize)break; + + outbuf[bufpos] = MIC_sharedmem[pos]; + bufpos++; + pos++; + } + + return bufpos; +} + +Result MIC_MapSharedMem(Handle handle, u32 size) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00010042; + cmdbuf[1] = size; + cmdbuf[2] = 0; + cmdbuf[3] = handle; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_UnmapSharedMem() +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00020000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_cmd3_Initialize(u8 unk0, u8 unk1, u32 sharedmem_baseoffset, u32 sharedmem_endoffset, u8 unk2) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00030140; + cmdbuf[1] = unk0; + cmdbuf[2] = unk1; + cmdbuf[3] = sharedmem_baseoffset; + cmdbuf[4] = sharedmem_endoffset; + cmdbuf[5] = unk2; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_cmd5() +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00050000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_GetCNTBit15(u8 *out) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00060000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(out)*out = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result MIC_GetEventHandle(Handle *handle) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + if(MIC_event) + { + *handle = MIC_event; + return 0; + } + + cmdbuf[0] = 0x00070000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(handle)*handle = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result MIC_SetControl(u8 value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00080040; + cmdbuf[1] = value; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_GetControl(u8 *value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00090000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(value)*value = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result MIC_SetRecording(u8 value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x000A0040; + cmdbuf[1] = value; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(value==1)MIC_cur_endpos = MIC_GetSharedMemOffsetValue(); + + return (Result)cmdbuf[1]; +} + +Result MIC_IsRecoding(u8 *value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x000B0000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(value)*value = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + From 63c3e5af0d9d800937a77308b0479b01b7947d78 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Wed, 3 Sep 2014 14:38:47 -0400 Subject: [PATCH 06/14] Fixed mic example README. --- examples/mic/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mic/README.md b/examples/mic/README.md index 37a7328..f3e6d72 100644 --- a/examples/mic/README.md +++ b/examples/mic/README.md @@ -1,4 +1,4 @@ -arm11u +mic ======= Example for using the microphone with ctrulib. Hold down the A button to record, the app will then play the recorded audio once the A button is released. Roughly 32 seconds of audio can be recorded with the default audiobuf size in this app. From 449ead1141466c59bb48c242520d58eb8567b318 Mon Sep 17 00:00:00 2001 From: plutoo Date: Sat, 6 Sep 2014 21:07:26 +0200 Subject: [PATCH 07/14] os: Added osGetTime(). --- libctru/include/3ds/os.h | 1 + libctru/source/os.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/libctru/include/3ds/os.h b/libctru/include/3ds/os.h index c267590..92e6483 100644 --- a/libctru/include/3ds/os.h +++ b/libctru/include/3ds/os.h @@ -7,3 +7,4 @@ u32 osConvertVirtToPhys(u32 vaddr); const char* osStrError(u32 error); u32 osGetFirmVersion(); u32 osGetKernelVersion(); +u64 osGetTime(); diff --git a/libctru/source/os.c b/libctru/source/os.c index f09bf24..84431bf 100644 --- a/libctru/source/os.c +++ b/libctru/source/os.c @@ -1,5 +1,21 @@ #include <3ds.h> +#define TICKS_PER_MSEC 268123.480 + +typedef struct { + u64 date_time; + u64 update_tick; + //... +} datetime_t; + +static volatile u32* __datetime_selector = + (u32*) 0x1FF81000; +static volatile datetime_t* __datetime1 = + (datetime_t*) 0x1FF81020; +static volatile datetime_t* __datetime2 = + (datetime_t*) 0x1FF81040; + + u32 osConvertVirtToPhys(u32 vaddr) { if(vaddr >= 0x14000000 && vaddr < 0x1c000000) @@ -13,6 +29,21 @@ u32 osConvertVirtToPhys(u32 vaddr) return 0; } +// Returns number of milliseconds since 1st Jan 1900 00:00. +u64 osGetTime() { + volatile datetime_t* dt; + + switch(*__datetime_selector & 1) { + case 0: + dt = __datetime1; + case 1: + dt = __datetime2; + } + + u64 offset = (svcGetSystemTick() - dt->update_tick) / TICKS_PER_MSEC; + return dt->date_time + offset; +} + u32 osGetFirmVersion() { return (*(u32*)0x1FF80000) & ~0xFF; } From a9f4b3402beea2cf69728bda40572a387b640dda Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sat, 6 Sep 2014 17:49:48 -0400 Subject: [PATCH 08/14] Fixed broken CSND_playsound() code for looping(use vaddr1 for that instead of vaddr0, and adjust the size). --- libctru/source/services/csnd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libctru/source/services/csnd.c b/libctru/source/services/csnd.c index 9eb11bb..77b1686 100644 --- a/libctru/source/services/csnd.c +++ b/libctru/source/services/csnd.c @@ -285,7 +285,11 @@ Result CSND_playsound(u32 channel, u32 looping, u32 encoding, u32 samplerate, u3 CSND_sharedmemtype0_cmde(channel, looping, encoding, samplerate, unk0, unk1, physaddr0, physaddr1, totalbytesize); CSND_sharedmemtype0_cmd8(channel, samplerate); - if(looping)CSND_sharedmemtype0_cmd3(channel, physaddr0, totalbytesize); + if(looping) + { + if(physaddr1>physaddr0)totalbytesize-= (u32)physaddr1 - (u32)physaddr0; + CSND_sharedmemtype0_cmd3(channel, physaddr1, totalbytesize); + } CSND_sharedmemtype0_cmd8(channel, samplerate); CSND_sharedmemtype0_cmd9(channel, 0xffff); CSND_setchannel_playbackstate(channel, 1); From ad08977e3777de77fe12c7124e2148f1a78c101a Mon Sep 17 00:00:00 2001 From: StapleButter Date: Thu, 11 Sep 2014 21:02:56 +0200 Subject: [PATCH 09/14] Fix FS init. --- libctru/source/services/fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libctru/source/services/fs.c b/libctru/source/services/fs.c index be5bbd0..00a184b 100644 --- a/libctru/source/services/fs.c +++ b/libctru/source/services/fs.c @@ -68,7 +68,7 @@ FSUSER_Initialize(Handle* handle) { // don't run command if we got handle from the list handle = &fsuHandle; - if(fsuHandle != 0 && __get_handle_from_list("fs:USER")==0) + if(fsuHandle != 0 && __get_handle_from_list("fs:USER")!=0) return 0; } From 8b27dbe623c2d38b447bdfa9c2b16c0ba5183887 Mon Sep 17 00:00:00 2001 From: StapleButter Date: Sat, 13 Sep 2014 17:01:20 +0200 Subject: [PATCH 10/14] * Make the GSP event handler signal events in the right order (essential for proper PICA200 sync) * Add some comments about the GSP events (based on my observations, may not be right) --- libctru/include/3ds/services/gsp.h | 6 +++--- libctru/source/services/gsp.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libctru/include/3ds/services/gsp.h b/libctru/include/3ds/services/gsp.h index f7ddfc2..d324b9a 100644 --- a/libctru/include/3ds/services/gsp.h +++ b/libctru/include/3ds/services/gsp.h @@ -37,12 +37,12 @@ typedef struct typedef enum { - GSPEVENT_PSC0 = 0, + GSPEVENT_PSC0 = 0, // memory fill completed GSPEVENT_PSC1, GSPEVENT_VBlank0, GSPEVENT_VBlank1, - GSPEVENT_PPF, - GSPEVENT_P3D, + GSPEVENT_PPF, // display transfer finished + GSPEVENT_P3D, // command list processing finished GSPEVENT_DMA, GSPEVENT_MAX, // used to know how many events there are diff --git a/libctru/source/services/gsp.c b/libctru/source/services/gsp.c index 7c64019..24fff1d 100644 --- a/libctru/source/services/gsp.c +++ b/libctru/source/services/gsp.c @@ -85,15 +85,15 @@ void gspEventThreadMain(u32 arg) svcClearEvent(gspEvent); int count = gspEventData[1]; - int last = gspEventData[0] + count; + int cur = gspEventData[0]; + int last = cur + count; while (last >= 0x34) last -= 0x34; - int cur = last; int i; for (i = 0; i < count; i ++) { int curEvt = gspEventData[0xC + cur]; - cur --; - if (cur < 0) cur += 0x34; + cur ++; + if (cur >= 0x34) cur -= 0x34; if (curEvt >= GSPEVENT_MAX) continue; svcSignalEvent(gspEvents[curEvt]); } From 2c589cd2ef9a2b663e7c792e0451af1f1d544223 Mon Sep 17 00:00:00 2001 From: fincs Date: Tue, 16 Sep 2014 22:24:26 +0200 Subject: [PATCH 11/14] linearAlloc: use 16-byte alignment --- libctru/source/linear.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libctru/source/linear.c b/libctru/source/linear.c index ba78f40..66eb43c 100644 --- a/libctru/source/linear.c +++ b/libctru/source/linear.c @@ -5,20 +5,20 @@ extern u32 __linear_heap, __linear_heap_size; // TODO: this allocator sucks! It is not thread-safe and you cannot 'free' this memory. void* linearAlloc(size_t size) { - static size_t currentOffset = 0; - size_t free = __linear_heap_size - currentOffset; + static size_t currentOffset = 0; + size_t free = __linear_heap_size - currentOffset; - // Enforce 8-byte alignment - size = (size + 7) &~ 7; + // Enforce 16-byte alignment + size = (size + 15) &~ 15; void* mem = NULL; - if (free >= size) - { + if (free >= size) + { mem = (void*)(__linear_heap + currentOffset); - currentOffset += size; - } + currentOffset += size; + } - return mem; + return mem; } void* linearRealloc(void* mem, size_t size) From 27be99a79306526f9da16ebe6bf31fd7f3c5e1ec Mon Sep 17 00:00:00 2001 From: fincs Date: Wed, 17 Sep 2014 00:22:30 +0200 Subject: [PATCH 12/14] Add proper linear heap allocator --- libctru/Makefile | 2 +- libctru/source/allocator/linear.cpp | 50 +++++++++++ libctru/source/allocator/mem_pool.cpp | 125 ++++++++++++++++++++++++++ libctru/source/allocator/mem_pool.h | 88 ++++++++++++++++++ libctru/source/linear.c | 32 ------- 5 files changed, 264 insertions(+), 33 deletions(-) create mode 100644 libctru/source/allocator/linear.cpp create mode 100644 libctru/source/allocator/mem_pool.cpp create mode 100644 libctru/source/allocator/mem_pool.h delete mode 100644 libctru/source/linear.c diff --git a/libctru/Makefile b/libctru/Makefile index 83aa6d0..c42536b 100644 --- a/libctru/Makefile +++ b/libctru/Makefile @@ -17,7 +17,7 @@ include $(DEVKITARM)/base_rules #--------------------------------------------------------------------------------- TARGET := ctru BUILD := build -SOURCES := source source/services source/gpu +SOURCES := source source/services source/gpu source/allocator DATA := data INCLUDES := include diff --git a/libctru/source/allocator/linear.cpp b/libctru/source/allocator/linear.cpp new file mode 100644 index 0000000..a7581e8 --- /dev/null +++ b/libctru/source/allocator/linear.cpp @@ -0,0 +1,50 @@ +#include <3ds.h> +#include "mem_pool.h" + +extern u32 __linear_heap, __linear_heap_size; + +static MemPool sLinearPool; + +static bool linearInit() +{ + auto blk = MemBlock::Create((u8*)__linear_heap, __linear_heap_size); + if (blk) + { + sLinearPool.AddBlock(blk); + return true; + } + return false; +} + +void* linearAlloc(size_t size) +{ + // Initialize the pool if it is not ready + if (!sLinearPool.Ready() && !linearInit()) + return nullptr; + + // Reserve memory for MemChunk structure + size += 16; + + // Allocate the chunk + MemChunk chunk; + if (!sLinearPool.Allocate(chunk, size, 4)) // 16-byte alignment + return nullptr; + + // Copy the MemChunk structure and return memory + auto addr = chunk.addr; + *(MemChunk*)addr = chunk; + return addr + 16; +} + +void* linearRealloc(void* mem, size_t size) +{ + // TODO + return NULL; +} + +void linearFree(void* mem) +{ + // Find MemChunk structure and free the chunk + auto pChunk = (MemChunk*)((u8*)mem - 16); + sLinearPool.Deallocate(*pChunk); +} diff --git a/libctru/source/allocator/mem_pool.cpp b/libctru/source/allocator/mem_pool.cpp new file mode 100644 index 0000000..93eeb4f --- /dev/null +++ b/libctru/source/allocator/mem_pool.cpp @@ -0,0 +1,125 @@ +#include "mem_pool.h" + +/* +// This method is currently unused +void MemPool::CoalesceLeft(MemBlock* b) +{ + auto curPtr = b->base; + for (auto p = b->prev; p; p = p->prev) + { + if ((p->base + p->size) != curPtr) break; + curPtr = p->base; + p->size += b->size; + DelBlock(b); + b = p; + } +} +*/ + +void MemPool::CoalesceRight(MemBlock* b) +{ + auto curPtr = b->base + b->size; + auto next = b->next; + for (auto n = next; n; n = next) + { + next = n->next; + if (n->base != curPtr) break; + b->size += n->size; + curPtr += n->size; + DelBlock(n); + } +} + +bool MemPool::Allocate(MemChunk& chunk, u32 size, int align) +{ + int alignM = (1 << align) - 1; + size = (size + alignM) &~ alignM; // Round the size + // Find the first suitable block + for (auto b = first; b; b = b->next) + { + auto addr = b->base; + u32 begWaste = (u32)addr & alignM; + addr += begWaste; + u32 bSize = b->size - begWaste; + if (bSize < size) continue; + + // Found space! + chunk.addr = addr; + chunk.size = size; + + // Resize the block + if (!begWaste) + { + b->base += size; + b->size -= size; + if (!b->size) + DelBlock(b); + } else + { + auto nAddr = addr + size; + auto nSize = bSize - size; + b->size = begWaste; + if (nSize) + { + // We need to add the tail chunk that wasn't used to the list + auto n = MemBlock::Create(nAddr, nSize); + if (n) InsertAfter(b, n); + else chunk.size += nSize; // we have no choice but to waste the space. + } + } + return true; + } + + return false; +} + +void MemPool::Deallocate(const MemChunk& chunk) +{ + u8* cAddr = chunk.addr; + auto cSize = chunk.size; + bool done = false; + + // Try to merge the chunk somewhere into the list + for (auto b = first; !done && b; b = b->next) + { + auto addr = b->base; + if (addr > cAddr) + { + if ((cAddr + cSize) == addr) + { + // Merge the chunk to the left of the block + b->base = cAddr; + b->size += cSize; + } else + { + // We need to insert a new block + auto c = MemBlock::Create(cAddr, cSize); + if (c) InsertBefore(b, c); + } + done = true; + } else if ((b->base + b->size) == cAddr) + { + // Coalesce to the right + b->size += cSize; + CoalesceRight(b); + done = true; + } + } + + if (!done) + { + // Either the list is empty or the chunk address is past the end + // address of the last block -- let's add a new block at the end + auto b = MemBlock::Create(cAddr, cSize); + if (b) AddBlock(b); + } +} + +/* +void MemPool::Dump(const char* title) +{ + printf("<%s> VRAM Pool Dump\n", title); + for (auto b = first; b; b = b->next) + printf(" - %p (%u bytes)\n", b->base, b->size); +} +*/ diff --git a/libctru/source/allocator/mem_pool.h b/libctru/source/allocator/mem_pool.h new file mode 100644 index 0000000..7237f49 --- /dev/null +++ b/libctru/source/allocator/mem_pool.h @@ -0,0 +1,88 @@ +#pragma once +#include <3ds.h> + +struct MemChunk +{ + u8* addr; + u32 size; +}; + +struct MemBlock +{ + MemBlock *prev, *next; + u8* base; + u32 size; + + static MemBlock* Create(u8* base, u32 size) + { + auto b = (MemBlock*)malloc(sizeof(MemBlock)); + if (!b) return nullptr; + b->prev = nullptr; + b->next = nullptr; + b->base = base; + b->size = size; + return b; + } +}; + +struct MemPool +{ + MemBlock *first, *last; + + bool Ready() { return first != nullptr; } + + void AddBlock(MemBlock* blk) + { + blk->prev = last; + if (last) last->next = blk; + if (!first) first = blk; + last = blk; + } + + void DelBlock(MemBlock* b) + { + auto prev = b->prev, &pNext = prev ? prev->next : first; + auto next = b->next, &nPrev = next ? next->prev : last; + pNext = next; + nPrev = prev; + free(b); + } + + void InsertBefore(MemBlock* b, MemBlock* p) + { + auto prev = b->prev, &pNext = prev ? prev->next : first; + b->prev = p; + p->next = b; + p->prev = prev; + pNext = p; + } + + void InsertAfter(MemBlock* b, MemBlock* n) + { + auto next = b->next, &nPrev = next ? next->prev : last; + b->next = n; + n->prev = b; + n->next = next; + nPrev = n; + } + + //void CoalesceLeft(MemBlock* b); + void CoalesceRight(MemBlock* b); + + bool Allocate(MemChunk& chunk, u32 size, int align); + void Deallocate(const MemChunk& chunk); + + void Destroy() + { + MemBlock* next = nullptr; + for (auto b = first; b; b = next) + { + next = b->next; + free(b); + } + first = nullptr; + last = nullptr; + } + + //void Dump(const char* title); +}; diff --git a/libctru/source/linear.c b/libctru/source/linear.c deleted file mode 100644 index 66eb43c..0000000 --- a/libctru/source/linear.c +++ /dev/null @@ -1,32 +0,0 @@ -#include <3ds.h> - -extern u32 __linear_heap, __linear_heap_size; - -// TODO: this allocator sucks! It is not thread-safe and you cannot 'free' this memory. -void* linearAlloc(size_t size) -{ - static size_t currentOffset = 0; - size_t free = __linear_heap_size - currentOffset; - - // Enforce 16-byte alignment - size = (size + 15) &~ 15; - - void* mem = NULL; - if (free >= size) - { - mem = (void*)(__linear_heap + currentOffset); - currentOffset += size; - } - - return mem; -} - -void* linearRealloc(void* mem, size_t size) -{ - return NULL; // TODO -} - -void linearFree(void* mem) -{ - // TODO -} From 1f413a7d4485b8e1d1a69d32c83aa703f2c17898 Mon Sep 17 00:00:00 2001 From: StapleButter Date: Thu, 18 Sep 2014 22:09:15 +0200 Subject: [PATCH 13/14] Add synchronization mechanism for entering sleep mode. When the APT status is APP_PREPARE_SLEEPMODE, the application main thread should call aptSignalReadyForSleep() to signal that it is ready to enter sleep mode, and then call aptWaitStatusEvent() as usual. Example code: APP_STATUS status; while ((status = aptGetStatus()) != APP_EXITING) { if(status==APP_RUNNING) { // application logic here } else if(status == APP_SUSPENDING) { aptReturnToMenu(); } else if(status == APP_PREPARE_SLEEPMODE) { aptSignalReadyForSleep(); aptWaitStatusEvent(); } } This maybe isn't the proper/recommended way to do sleep mode, but I tested it multiple times and it always worked reliably. (note: maybe the sample code above will not work if GPU drawing is done in a separate thread, haven't tested that) --- libctru/include/3ds/services/apt.h | 1 + libctru/source/services/apt.c | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/libctru/include/3ds/services/apt.h b/libctru/include/3ds/services/apt.h index 9377c0b..b8c05d9 100644 --- a/libctru/include/3ds/services/apt.h +++ b/libctru/include/3ds/services/apt.h @@ -48,6 +48,7 @@ u32 aptGetStatusPower();//This can be used when the status is APP_SUSPEND* to ch void aptSetStatusPower(u32 status); void aptReturnToMenu();//This should be called by the user application when aptGetStatus() returns APP_SUSPENDING, not calling this will result in return-to-menu being disabled with the status left at APP_SUSPENDING. This function will not return until the system returns to the application, or when the status was changed to APP_EXITING. void aptWaitStatusEvent(); +void aptSignalReadyForSleep(); NS_APPID aptGetMenuAppID(); Result APT_GetLockHandle(Handle* handle, u16 flags, Handle* lockHandle); diff --git a/libctru/source/services/apt.c b/libctru/source/services/apt.c index d2f65d9..3c8de84 100644 --- a/libctru/source/services/apt.c +++ b/libctru/source/services/apt.c @@ -26,6 +26,7 @@ Handle aptStatusEvent = 0; APP_STATUS aptStatus = APP_NOTINITIALIZED; APP_STATUS aptStatusBeforeSleep = APP_NOTINITIALIZED; u32 aptStatusPower = 0; +Handle aptSleepSync = 0; u32 aptParameters[0x1000/4]; //TEMP @@ -204,21 +205,24 @@ static void __handle_notification() { case APTSIGNAL_PREPARESLEEP: // Reply to sleep-request. aptStatusBeforeSleep = aptGetStatus(); + aptSetStatus(APP_PREPARE_SLEEPMODE); + svcWaitSynchronization(aptSleepSync, U64_MAX); + svcClearEvent(aptSleepSync); + aptOpenSession(); APT_ReplySleepQuery(NULL, currentAppId, 0x1); aptCloseSession(); - - aptSetStatus(APP_PREPARE_SLEEPMODE); break; case APTSIGNAL_ENTERSLEEP: if(aptGetStatus() == APP_PREPARE_SLEEPMODE) { // Report into sleep-mode. + aptSetStatus(APP_SLEEPMODE); + aptOpenSession(); APT_ReplySleepNotificationComplete(NULL, currentAppId); aptCloseSession(); - aptSetStatus(APP_SLEEPMODE); } break; @@ -319,6 +323,7 @@ Result aptInit(void) } svcCreateEvent(&aptStatusEvent, 0); + svcCreateEvent(&aptSleepSync, 0); return 0; } @@ -344,6 +349,8 @@ void aptExit() APT_CloseApplication(NULL, 0x0, 0x0, 0x0); aptCloseSession(); } + + svcCloseHandle(aptSleepSync); svcCloseHandle(aptStatusMutex); //svcCloseHandle(aptLockHandle); @@ -421,7 +428,7 @@ void aptSetStatus(APP_STATUS status) svcWaitSynchronization(aptStatusMutex, U64_MAX); - prevstatus = status; + prevstatus = aptStatus; aptStatus = status; if(prevstatus != APP_NOTINITIALIZED) @@ -461,6 +468,11 @@ void aptCloseSession() svcReleaseMutex(aptLockHandle); } +void aptSignalReadyForSleep() +{ + svcSignalEvent(aptSleepSync); +} + Result APT_GetLockHandle(Handle* handle, u16 flags, Handle* lockHandle) { if(!handle)handle=&aptuHandle; From af31a7c861e472ff9ef9e0def347a66aeb51a61e Mon Sep 17 00:00:00 2001 From: StapleButter Date: Thu, 23 Oct 2014 17:56:56 +0200 Subject: [PATCH 14/14] * add code for alphablending, color logic op, alpha test and multitexturing. * add GPU_FinishDrawing() to be called after a batch of GPU_DrawArray() calls if you're done drawing or if you intend to change the GPU configuration before drawing more. Also fix GPU_Finalize(). With those changes, the GPU no longer freezes if you call GPU_DrawArray() an even number of times. * fix GPU_SetViewport() to allow color buffer reading, so blending and logicop work as expected. --- libctru/include/3ds/gpu/gpu.h | 71 +++++++++++++++++++++++++++++++++- libctru/source/gpu/gpu.c | 72 +++++++++++++++++++++++++++++++---- 2 files changed, 135 insertions(+), 8 deletions(-) diff --git a/libctru/include/3ds/gpu/gpu.h b/libctru/include/3ds/gpu/gpu.h index 0b2ee2a..aaceb0a 100644 --- a/libctru/include/3ds/gpu/gpu.h +++ b/libctru/include/3ds/gpu/gpu.h @@ -9,6 +9,13 @@ void GPUCMD_Add(u32 cmd, u32* param, u32 paramlength); void GPUCMD_AddSingleParam(u32 cmd, u32 param); void GPUCMD_Finalize(); +typedef enum +{ + GPU_TEXUNIT0 = 0x1, + GPU_TEXUNIT1 = 0x2, + GPU_TEXUNIT2 = 0x4 +} GPU_TEXUNIT; + typedef enum{ GPU_RGBA8=0x0, GPU_RGB8=0x1, @@ -37,6 +44,54 @@ typedef enum GPU_GEQUAL = 7 }GPU_TESTFUNC; +typedef enum +{ + GPU_BLEND_ADD = 0, + GPU_BLEND_SUBTRACT = 1, + GPU_BLEND_REVERSE_SUBTRACT = 2, + GPU_BLEND_MIN = 3, + GPU_BLEND_MAX = 4 +} GPU_BLENDEQUATION; + +typedef enum +{ + GPU_ZERO = 0, + GPU_ONE = 1, + GPU_SRC_COLOR = 2, + GPU_ONE_MINUS_SRC_COLOR = 3, + GPU_DST_COLOR = 4, + GPU_ONE_MINUS_DST_COLOR = 5, + GPU_SRC_ALPHA = 6, + GPU_ONE_MINUS_SRC_ALPHA = 7, + GPU_DST_ALPHA = 8, + GPU_ONE_MINUS_DST_ALPHA = 9, + GPU_CONSTANT_COLOR = 10, + GPU_ONE_MINUS_CONSTANT_COLOR = 11, + GPU_CONSTANT_ALPHA = 12, + GPU_ONE_MINUS_CONSTANT_ALPHA = 13, + GPU_SRC_ALPHA_SATURATE = 14 +} GPU_BLENDFACTOR; + +typedef enum +{ + GPU_LOGICOP_CLEAR = 0, + GPU_LOGICOP_AND = 1, + GPU_LOGICOP_AND_REVERSE = 2, + GPU_LOGICOP_COPY = 3, + GPU_LOGICOP_SET = 4, + GPU_LOGICOP_COPY_INVERTED = 5, + GPU_LOGICOP_NOOP = 6, + GPU_LOGICOP_INVERT = 7, + GPU_LOGICOP_NAND = 8, + GPU_LOGICOP_OR = 9, + GPU_LOGICOP_NOR = 10, + GPU_LOGICOP_XOR = 11, + GPU_LOGICOP_EQUIV = 12, + GPU_LOGICOP_AND_INVERTED = 13, + GPU_LOGICOP_OR_REVERSE = 14, + GPU_LOGICOP_OR_INVERTED = 15 +} GPU_LOGICOP; + typedef enum{ GPU_BYTE = 0, GPU_UNSIGNED_BYTE = 1, @@ -84,14 +139,28 @@ typedef enum{ }GPU_Primitive_t; void GPU_SetUniform(u32 startreg, u32* data, u32 numreg); + void GPU_SetViewport(u32* depthBuffer, u32* colorBuffer, u32 x, u32 y, u32 w, u32 h); + void GPU_DepthRange(float nearVal, float farVal); +void GPU_SetAlphaTest(bool enable, GPU_TESTFUNC function, u8 ref); void GPU_SetDepthTest(bool enable, GPU_TESTFUNC function, u8 ref); void GPU_SetStencilTest(bool enable, GPU_TESTFUNC function, u8 ref); void GPU_SetFaceCulling(GPU_CULLMODE mode); + +// these two can't be used together +void GPU_SetAlphaBlending(GPU_BLENDEQUATION colorEquation, GPU_BLENDEQUATION alphaEquation, + GPU_BLENDFACTOR colorSrc, GPU_BLENDFACTOR colorDst, + GPU_BLENDFACTOR alphaSrc, GPU_BLENDFACTOR alphaDst); +void GPU_SetColorLogicOp(GPU_LOGICOP op); + void GPU_SetAttributeBuffers(u8 totalAttributes, u32* baseAddress, u64 attributeFormats, u16 attributeMask, u64 attributePermutation, u8 numBuffers, u32 bufferOffsets[], u64 bufferPermutations[], u8 bufferNumAttributes[]); -void GPU_SetTexture(u32* data, u16 width, u16 height, u32 param, GPU_TEXCOLOR colorType); + +void GPU_SetTextureEnable(GPU_TEXUNIT units); // GPU_TEXUNITx values can be ORed together to enable multiple texture units +void GPU_SetTexture(GPU_TEXUNIT unit, u32* data, u16 width, u16 height, u32 param, GPU_TEXCOLOR colorType); void GPU_SetTexEnv(u8 id, u16 rgbSources, u16 alphaSources, u16 rgbOperands, u16 alphaOperands, GPU_COMBINEFUNC rgbCombine, GPU_COMBINEFUNC alphaCombine, u32 constantColor); void GPU_DrawArray(GPU_Primitive_t primitive, u32 n); void GPU_DrawElements(GPU_Primitive_t primitive, u32* indexArray, u32 n); + +void GPU_FinishDrawing(); diff --git a/libctru/source/gpu/gpu.c b/libctru/source/gpu/gpu.c index d6685e3..835f1ff 100644 --- a/libctru/source/gpu/gpu.c +++ b/libctru/source/gpu/gpu.c @@ -65,6 +65,9 @@ void GPUCMD_AddSingleParam(u32 cmd, u32 param) void GPUCMD_Finalize() { + GPUCMD_AddSingleParam(0x0008025E, 0x00000000); + GPUCMD_AddSingleParam(0x000F0111, 0x00000001); + GPUCMD_AddSingleParam(0x000F0110, 0x00000001); GPUCMD_AddSingleParam(0x000F0010, 0x12345678); } @@ -272,7 +275,7 @@ void GPU_SetViewport(u32* depthBuffer, u32* colorBuffer, u32 x, u32 y, u32 w, u3 GPUCMD_Add(0x800F0065, param, 0x00000003); //enable depth buffer - param[0x0]=0x00000000; + param[0x0]=0x0000000F; param[0x1]=0x0000000F; param[0x2]=0x00000002; param[0x3]=0x00000002; @@ -286,6 +289,11 @@ void GPU_DepthRange(float nearVal, float farVal) GPUCMD_AddSingleParam(0x000F004E, f32tof24(farVal)); } +void GPU_SetAlphaTest(bool enable, GPU_TESTFUNC function, u8 ref) +{ + GPUCMD_AddSingleParam(0x000F0104, (enable&1)|((function&7)<<4)|(ref<<8)); +} + void GPU_SetStencilTest(bool enable, GPU_TESTFUNC function, u8 ref) { GPUCMD_AddSingleParam(0x000F0105, (enable&1)|((function&7)<<4)|(ref<<8)); @@ -296,12 +304,54 @@ void GPU_SetDepthTest(bool enable, GPU_TESTFUNC function, u8 ref) GPUCMD_AddSingleParam(0x000F0107, (enable&1)|((function&7)<<4)|(ref<<8)); } -void GPU_SetTexture(u32* data, u16 width, u16 height, u32 param, GPU_TEXCOLOR colorType) +void GPU_SetAlphaBlending(GPU_BLENDEQUATION colorEquation, GPU_BLENDEQUATION alphaEquation, + GPU_BLENDFACTOR colorSrc, GPU_BLENDFACTOR colorDst, + GPU_BLENDFACTOR alphaSrc, GPU_BLENDFACTOR alphaDst) { - GPUCMD_AddSingleParam(0x000F008E, colorType); - GPUCMD_AddSingleParam(0x000F0085, ((u32)data)>>3); - GPUCMD_AddSingleParam(0x000F0082, (width)|(height<<16)); - GPUCMD_AddSingleParam(0x000F0083, param); + // TODO: fixed color + // it is controlled by command 0103 but I haven't found were to place said command without freezing the GPU + + GPUCMD_AddSingleParam(0x000F0101, colorEquation | (alphaEquation<<8) | (colorSrc<<16) | (colorDst<<20) | (alphaSrc<<24) | (alphaDst<<28)); + GPUCMD_AddSingleParam(0x00020100, 0x00000100); +} + +void GPU_SetColorLogicOp(GPU_LOGICOP op) +{ + GPUCMD_AddSingleParam(0x000F0102, op); + GPUCMD_AddSingleParam(0x00020100, 0x00000000); +} + +void GPU_SetTextureEnable(GPU_TEXUNIT units) +{ + GPUCMD_AddSingleParam(0x0002006F, units<<8); // enables texcoord outputs + GPUCMD_AddSingleParam(0x000F0080, 0x00011000|units); // enables texture units +} + +void GPU_SetTexture(GPU_TEXUNIT unit, u32* data, u16 width, u16 height, u32 param, GPU_TEXCOLOR colorType) +{ + switch (unit) + { + case GPU_TEXUNIT0: + GPUCMD_AddSingleParam(0x000F008E, colorType); + GPUCMD_AddSingleParam(0x000F0085, ((u32)data)>>3); + GPUCMD_AddSingleParam(0x000F0082, (width)|(height<<16)); + GPUCMD_AddSingleParam(0x000F0083, param); + break; + + case GPU_TEXUNIT1: + GPUCMD_AddSingleParam(0x000F0096, colorType); + GPUCMD_AddSingleParam(0x000F0095, ((u32)data)>>3); + GPUCMD_AddSingleParam(0x000F0092, (width)|(height<<16)); + GPUCMD_AddSingleParam(0x000F0093, param); + break; + + case GPU_TEXUNIT2: + GPUCMD_AddSingleParam(0x000F009E, colorType); + GPUCMD_AddSingleParam(0x000F009D, ((u32)data)>>3); + GPUCMD_AddSingleParam(0x000F009A, (width)|(height<<16)); + GPUCMD_AddSingleParam(0x000F009B, param); + break; + } } const u8 GPU_FORMATSIZE[4]={1,1,2,4}; @@ -385,7 +435,6 @@ void GPU_DrawArray(GPU_Primitive_t primitive, u32 n) GPUCMD_AddSingleParam(0x000F0231, 0x00000001); GPUCMD_AddSingleParam(0x000F0111, 0x00000001); - GPUCMD_AddSingleParam(0x000F0110, 0x00000001); } void GPU_DrawElements(GPU_Primitive_t primitive, u32* indexArray, u32 n) @@ -405,4 +454,13 @@ void GPU_DrawElements(GPU_Primitive_t primitive, u32* indexArray, u32 n) GPUCMD_AddSingleParam(0x000F022F, 0x00000001); GPUCMD_AddSingleParam(0x00010245, 0x00000001); GPUCMD_AddSingleParam(0x000F0231, 0x00000001); + + // CHECKME: does this one also require command 0x0111 at the end? +} + +void GPU_FinishDrawing() +{ + GPUCMD_AddSingleParam(0x000F0111, 0x00000001); + GPUCMD_AddSingleParam(0x000F0110, 0x00000001); + GPUCMD_AddSingleParam(0x000F0063, 0x00000001); }