どうも、昔PSPを触ってた人です。
サークルでmrubyを触る機会があって、PSPでも動くかなーと思って試したら普通に動いたので、やり方のメモと言う形で書きたいと思います。
PSPの環境構築の話はここではしないので、各自ググるなりしてください。
mrubyの準備
まずはGithubからmrubyを落としてきます。
落としてきたら、build_config.rb
にPSP用のmrubyライブラリを作るための設定を書いてあげます。
下のコードをbuild_config.rb
に追記してください。
MRuby::Build.new("psp") do |conf|
# load specific toolchain settings
# Gets set by the VS command prompts.
toolchain :gcc
enable_debug
# Use mrbgems
# conf.gem 'examples/mrbgems/ruby_extension_example'
# conf.gem 'examples/mrbgems/c_extension_example' do |g|
# g.cc.flags << '-g' # append cflags in this gem
# end
# conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
# conf.gem :github => 'masuidrive/mrbgems-example', :checksum_hash => '76518e8aecd131d047378448ac8055fa29d974a9'
# conf.gem :git => 'git@github.com:masuidrive/mrbgems-example.git', :branch => 'master', :options => '-v'
# include the default GEMs
conf.gembox 'non-bin'
# C compiler settings
BASE_PATH = "/usr/local/pspdev/bin"
PREFIX = "psp-"
conf.cc do |cc|
cc.command = ENV['CC'] || "#{BASE_PATH}/#{PREFIX}gcc"
cc.flags = [ENV['CFLAGS'] || %w(-O2 -G0 -Wall -g -fno-exceptions)]
# cc.include_paths = ["#{root}/include"]
# cc.defines = %w(DISABLE_GEMS)
# cc.option_include_path = '-I%s'
# cc.option_define = '-D%s'
cc.compile_options = "%{flags} -MMD -o %{outfile} -c %{infile}"
end
# mrbc settings
# conf.mrbc do |mrbc|
# mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers
# end
# Linker settings
# conf.linker do |linker|
# linker.command = ENV['LD'] || 'gcc'
# linker.flags = [ENV['LDFLAGS'] || []]
# linker.flags_before_libraries = []
# linker.libraries = %w()
# linker.flags_after_libraries = []
# linker.library_paths = []
# linker.option_library = '-l%s'
# linker.option_library_path = '-L%s'
# linker.link_options = "%{flags} -o %{outfile} %{objs} %{libs}"
# end
# Archiver settings
conf.archiver do |archiver|
archiver.command = ENV['AR'] || "#{BASE_PATH}/#{PREFIX}ar"
archiver.archive_options = 'rs %{outfile} %{objs}'
end
# Parser generator settings
# conf.yacc do |yacc|
# yacc.command = ENV['YACC'] || 'bison'
# yacc.compile_options = '-o %{outfile} %{infile}'
# end
# gperf settings
# conf.gperf do |gperf|
# gperf.command = 'gperf'
# gperf.compile_options = '-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" %{infile} > %{outfile}'
# end
# file extensions
# conf.exts do |exts|
# exts.object = '.o'
# exts.executable = '' # '.exe' if Windows
# exts.library = '.a'
# end
# file separetor
# conf.file_separator = '/'
# bintest
# conf.enable_bintest
end
これはホスト用の設定をコピーしてきて、一部を変えただけです。
ポイントは使用するGCCとArchiverを変更している点です。
ar
をpspのものにしておかないと、リンクできないライブラリが生成されてしまいます。
あとは、使用するgemboxを変えています。
conf.gembox 'non-bin'
PSP用の実行ファイルを生成できないため、それらを除いたgemboxを指定しています。
下のオプションを追加することで実行ファイルは生成しなくなるみたいな情報があったんですが、普通に生成しようとするので無理やりgemboxを書き換える方針にしました。
conf.bins = []
non-bin
というgemboxは存在しないため、自分で作る必要があります。
まずはデフォルトのgemboxをコピーします。
$ cp mrbgems/default.gembox mrbgems/non-bin.gembox
mrbgems/non-bin.gembox
を開いて、mruby-bin-
から始まるgemをコメントアウトします。
...
# Use toplevel object (main) methods extension
conf.gem :core => "mruby-toplevel-ext"
# Generate mirb command
# conf.gem :core => "mruby-bin-mirb"
# Generate mruby command
# conf.gem :core => "mruby-bin-mruby"
# Generate mruby-strip command
# conf.gem :core => "mruby-bin-strip"
# Use Kernel module extension
conf.gem :core => "mruby-kernel-ext"
...
mrubyのコンパイル
準備が終わったらコンパイルするだけです。
$ make
...
================================================
Config Name: psp
Output Directory: build/psp
Included Gems:
mruby-sprintf - standard Kernel#sprintf method
mruby-print - standard print/puts/p
mruby-math - standard Math module
mruby-time - standard Time class
mruby-struct - standard Struct class
mruby-enum-ext - Enumerable module extension
mruby-string-ext - String class extension
mruby-numeric-ext - Numeric class extension
mruby-array-ext - Array class extension
mruby-hash-ext - Hash class extension
mruby-range-ext - Range class extension
mruby-proc-ext - Proc class extension
mruby-symbol-ext - Symbol class extension
mruby-random - Random class
mruby-object-ext - Object class extension
mruby-objectspace - ObjectSpace class
mruby-fiber - Fiber class
mruby-enumerator - Enumerator class
mruby-enum-lazy - Enumerable::Lazy class
mruby-toplevel-ext - toplevel object (main) methods extension
mruby-kernel-ext - Kernel module extension
mruby-compiler - mruby compiler library
================================================
Config Name: psp
といった表示が出ていればコンパイル成功です。
生成されたライブラリはbuild/psp/lib/
の中にlibmruby.a
という名前で入っているはずです。
PSPアプリを作る
それではPSPでrubyのコードを動かしてみましょう。
とりあえずhello worldですかね。
mrubyのディレクトリと同じ階層にアプリを作る用のディレクトリを用意して、先ほど生成したlibmruby.a
とmrubyの関数群が入っているディレクトリもコピーします。
$ ls
mruby/
$ mkdir mruby-psp
$ ls
mruby/ mruby-psp/
$ cp mruby/build/psp/lib/libmruby.a mruby-psp/
$ cp -r mruby/include/ mruby-psp/
$ cd mruby-psp
$ ls
include/ libmruby.a
このディレクトリにコードやMakefileを設置しましょう
#include <pspkernel.h>
#include <pspmodulemgr.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <pspthreadman.h>
#include <pspctrl.h>
#include <pspsdk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mruby.h>
#include <mruby/proc.h>
#include <mruby/compile.h>
#include <mruby/string.h>
#define printf pspDebugScreenPrintf
PSP_MODULE_INFO("Mruby", 0, 0, 1);
PSP_MAIN_THREAD_ATTR(0);
PSP_HEAP_SIZE_KB(-512);
/* exit callback */
int exit_callback(int arg1, int arg2, void *common){
sceKernelExitGame();
return 0;
}
/* callback thread */
int CallbackThread(SceSize args, void *argp){
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return 0;
}
/* setup the callback thread */
int SetupCallbacks(void){
int thid = 0;
thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
if(thid >= 0)
sceKernelStartThread(thid, 0, 0);
return thid;
}
mrb_value myputs(mrb_state *mrb, mrb_value self){
mrb_value val;
mrb_get_args(mrb, "S", &val);
printf(RSTRING_PTR(val));
return mrb_nil_value();
}
int main(){
/* exit callback */
SetupCallbacks();
/* setup debug info handler */
pspDebugScreenInit();
mrb_state *mrb = mrb_open();
//Objectクラスにmyputsをメソッドとして定義
mrb_define_method(mrb, mrb->object_class,"myputs", myputs, ARGS_REQ(1));
mrb_load_string(mrb, "myputs 'hello world!'");
mrb_close(mrb);
while (1) {
sceKernelDelayThread(10*1000*1000);
}
sceKernelExitGame();
}
ポイントは、PSP_HEAP_SIZE_KB(-512);
でヒープ領域を多めに確保することです。
PSP_HEAP_SIZE_KB
にマイナス値を渡すと、メモリの上限から指定した値を引いただけヒープを確保します(確か)
TARGET = mruby
OBJS = main.o
OPTION_FLAGS =
INCDIR = ./include
CFLAGS = -O2 -G0 -Wall -g $(OPTION_FLAGS)
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
BUILD_PRX = 1
LIBDIR =
LDFLAGS =
LIBS = libmruby.a
LIBS += -lm -lc
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = mruby
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
INCDIR
に先ほどコピーしたinclude
ディレクトリを、LIBS
にはlibmruby.a
を指定します。
あとはmakeすればEBOOT.PBPが生成されると思います。
PSPにコピーして起動し、無事に「wello world!」が表示されれば成功です。
感想とか
たぶん需要はすごく少ないんじゃないかと思います。
ただ、PSPをrubyで動かすことができるのは面白そうかなと思います。
最初、mrubyからPSPのAPIを叩けるライブラリとか作ろうかなと思いましたが、他にもしたいことがあったので、やめました。
興味がある人は是非挑戦して公開して欲しいです。