ch32funのmruby/cのhalと標準入出力


mrubyc_arduinoでは、hal_writeなどは、メインのスケッチの中で定義されている。 ch32funを使ったmruby/cにおいても、なんとなくその流儀を踏襲していたが、ファイルの構造を眺めていたら、これらはhal.cの中で定義すべきものであることに気が付いた。 そこで、ch32fun用のhal.cを作ってみたが、こんな感じになった。

#include "hal.h"
#include "ch32fun.h"

int hal_write(int fd, const void *buf, int nbytes) {
  _write(0, (char*)buf, nbytes);
  return nbytes;
}
int hal_flush(int fd) {
    return 0;
}
void hal_abort(const char *s) {}

するとこれらをメインのコードに書く必要が無くなる。

hal_writeはmruby/cの標準出力に対応するが、 2026/4/25のブログに書いたように、 私は標準入力も使いたいので、mrbc_get.cなどを作ってhal_readという関数を介してgetbyte,getc,getsを使えるようにしている。 以前はhal.hの中でhal_readの宣言を行っていたが、mruby/cのファイルをできるだけ変更するべきでは無いと考えて、mrbc_get.hに移動した。

#ifndef _MRBC_GET_H
#define _MRBC_GET_H

#include "mrubyc.h"
unsigned char hal_read(int fd);
void mrbc_init_class_get(void);

#endif

また、mrbc_get.cにおいては、getbyteはC言語で定義したが、getcとgetsはrubyで書いてmrbに変換して組み込んでいた。 しかし、コンパイルしたときのサイズを少しでも小さくするために、これらもC言語で書いてみた。 getsは動的なバッファを確保すると複雑になるので、一行は127文字以下とした。

#include "mrbc_get.h"

static void c_object_getbyte(struct VM *vm, mrbc_value v[], int argc)
{
  uint8_t ret=hal_read(0);
  SET_INT_RETURN( ret );
}
static void c_object_getc(struct VM *vm, mrbc_value v[], int argc)
{
  char buf[2] = {0,0};
  buf[0]=hal_read(0);
  mrbc_value value = mrbc_string_new_cstr(vm, buf);
  SET_RETURN(value);
}
static void c_object_gets(struct VM *vm, mrbc_value v[], int argc){
  int i;
  char buf[128];
  for(i=0;i<127;){ buf[i]=hal_read(0); if(buf[i++]=='\n') break; }
  buf[i]=0;
  mrbc_value value = mrbc_string_new_cstr(vm, buf);
  SET_RETURN(value);
}

void mrbc_init_class_get(void){
  mrbc_define_method(0, 0, "getbyte", c_object_getbyte);
  mrbc_define_method(0, 0, "getc", c_object_getc);
  mrbc_define_method(0, 0, "gets", c_object_gets);
}

確認してみると、160バイト程ではあるがコンパイルサイズは小さくなったし、おそらくメモリの使用量も減っただろう。 また、これも僅かではあるが、メインのコードもすっきりした。 hal_readもmrbc_get.cの中で定義した方が良いのかも知れないが、USB CDCを使っている関係で、main.cの中で定義するようにしている。