【メモ】Native Interops in TinyCLR その2

これの続きです。

matsujirushi.hatenablog.jp

Native stubを生成

前回最後にやった「Native stubを生成」で、

class MyNativeClass
{
    [MethodImpl(MethodImplOptions.InternalCall)]
    public extern int MyNativeFunc(int param1, int param2);
}

から生成されたファイルは3つでした。

  • TinyCLRApplication1.h
  • TinyCLRApplication1.cpp
  • TinyCLRApplication1_TinyCLRApplication1_MyNativeClass.cpp

そのうち、実コードを記入するのはTinyCLRApplication1_TinyCLRApplication1_MyNativeClass.cppにある、

TinyCLR_Result Interop_TinyCLRApplication1_TinyCLRApplication1_MyNativeClass::MyNativeFunc___I4__I4__I4(const TinyCLR_Interop_MethodData md)

です。

TinyCLR_ResultはTinyCLR.hに定義されていて、enum型。まぁ、これは分かりやすい。

TinyCLR_Interop_MethodDataApiProviderStackが含まれた構造体で、ApiProviderはAcquire,Release,Add,Removeなどのvtableっぽい。StackはApiProviderにあるAPIに渡す引数で使うのかな。

これらメンバーの使い方はココにありますが、ほとんど書かれておらず参考になりませんねw

ApiProviderのメンバーをじっくり見ると、下記のとおり分類できるようだ。

ApiProvider 引数と戻り値

GetArgument(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_StackFrame& stack, size_t index, TinyCLR_Interop_ManagedValue& value)
GetReturn(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_StackFrame& stack, TinyCLR_Interop_ManagedValue& value)

ApiProvider 参照カウンタ操作?

Acquire(const TinyCLR_Interop_Provider* self)
Release(const TinyCLR_Interop_Provider* self)

ApiProvider Interop追加/削除

Add(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_Assembly* interop)
Remove(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_Assembly* interop)

ApiProvider 型情報の検索

FindType(const TinyCLR_Interop_Provider* self, const char* assemblyName, const char* namespaceName, const char* typeName, TinyCLR_Interop_ManagedObjectType& type)

ApiProvider マネージオブジェクトの生成/入れ替え?/自オブジェクトの取得

CreateObject(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_StackFrame& stack, TinyCLR_Interop_ManagedValue& value)
ReplaceObject(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_ManagedValue& source, TinyCLR_Interop_ManagedValue& destination)
GetThisObject(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_StackFrame& stack, TinyCLR_Interop_ManagedValue& value)

ApiProvider フィールドとイベント

GetField(const TinyCLR_Interop_Provider* self, TinyCLR_Interop_ManagedValue managedObject, size_t index, TinyCLR_Interop_ManagedValue& value)
GetStaticField(const TinyCLR_Interop_Provider* self, const TinyCLR_Interop_Assembly& interop, size_t index, TinyCLR_Interop_ManagedValue& value)
RaiseEvent(const TinyCLR_Interop_Provider* self, const char* eventDispatcherName, const char* apiName, size_t implementationIndex, uint64_t data0, uint64_t data1, intptr_t data2)

Interopコードを実装

細かいところは置いておいて、サンプルコードを参考に、呼び出された回数を返すようTinyCLRApplication1_TinyCLRApplication1_MyNativeClass.cppを実装してみました。

#include "TinyCLRApplication1.h"

TinyCLR_Result Interop_TinyCLRApplication1_TinyCLRApplication1_MyNativeClass::MyNativeFunc___I4__I4__I4(const TinyCLR_Interop_MethodData md)
{
    static int counter = 0;

    TinyCLR_Interop_ManagedValue ret;
    ret.Type = TinyCLR_Interop_ManagedValueType::I4;
    ret.Data.Numeric->I4 = counter++;

    return TinyCLR_Result::Success;
}

Interopのビルド

makefileの作成

サンプルのmakefileをコピペして、OUTPUT_NAMEをMyInterop、INC_DIRSにTinyCLR.hがあるフォルダを追加しました。

OUTPUT_NAME = MyInterop
LINKERSCRIPT_NAME = scatterfile

MCU_FLAGS = -mcpu=cortex-m4 -mthumb
INC_DIRS = -I. -IC:\TinyCLR\TinyCLR-Ports\Core

CC = arm-none-eabi-g++.exe
LD = arm-none-eabi-g++.exe
OC = arm-none-eabi-objcopy.exe

CC_FLAGS = $(INC_DIRS) $(MCU_FLAGS) -Os -std=c++11 -xc++ -Wall -Wabi -w -mabi=aapcs -fPIC -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics
LD_FLAGS = $(MCU_FLAGS) -nostartfiles -lc -lgcc -T $(LINKERSCRIPT_NAME) -Wl,-Map,$(OUTPUT_NAME).map -Wl,--oformat -Wl,elf32-littlearm
OC_FLAGS = -S -O binary

SRC_FILES = $(wildcard *.cpp)
OBJ_FILES = $(patsubst %.cpp, %.obj, $(SRC_FILES))
 
rebuild: clean build

clean:
    del $(OBJ_FILES) $(OUTPUT_NAME).bin $(OUTPUT_NAME).elf $(OUTPUT_NAME).map

build: $(OBJ_FILES)
    $(LD) $(LD_FLAGS) -o $(OUTPUT_NAME).elf $^
    $(OC) $(OC_FLAGS) $(OUTPUT_NAME).elf $(OUTPUT_NAME).bin

%.obj: %.cpp
    $(CC) -c $(CC_FLAGS) -o $@ $^

scatterfileの作成

サンプルのscatterfileをコピペして、RLI_BASEとRLI_LENGTHを、デバイスのScatterfile.gcc.ldfに書かれている(ER_RLP_BEGIN値)と(ER_RLP_END値-ER_RLP_BEGIN値)に書き換えます。

MEMORY {
    SDRAM (wx) : ORIGIN = 0x2001BC00, LENGTH = (0x2001C000 - 0x08) - 0x2001BC00
}

SECTIONS {
    . = ALIGN(4);

    .text : {
        *(.text)
    }

    .rodata ALIGN(4): {
        *(.rodata )
    }

    .data ALIGN(4): {
        *(.data)
    }

    .bss ALIGN(4): {
        *(.bss)
    }
}

make build

準備できた状態がこちら。

f:id:matsujirushix:20171202184749p:plain

make buildコマンドを実行して、ビルド(コンパイル&リンク)してみます。

f:id:matsujirushix:20171202184929p:plain

f:id:matsujirushix:20171202185117p:plain

おおお!なんかできてるっぽい。

f:id:matsujirushix:20171202185607p:plain

(つづく)