(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

前回の課題というか今までの課題として,第4回でちょろっと書いたのだけど,ブートローダーをモトローラSレコードフォーマットに対応させよう.理由は以下.
  • 現状で loadbuf[] というバッファにいったんロードしてから実メモリ上に展開しているので,RAM16KBの半分の8KBまでのサイズしか原理的にロードできない.
  • ELFフォーマットのプログラムヘッダを読みながら解析して実メモリ上に直接展開,ということはできなくはないが,なんかめんどい.
  • モトローラSレコードフォーマットだと,読み込みながら実メモリ上に直接展開が非常にやりやすい.
  • 現状でKOZOSのバイナリサイズとロード用のバッファとかがギリギリの感じで,これ以上機能拡張するのはちょっと難しい.ロード用バッファが不要な構成にしたい.
  • 他にもテキストのみのフォーマットなのでなにかと便利だし,安全安心.(バイナリコードだと7bit回線は通らないとか,そーいうことも気にしなくて済む)
  • いろんなところで使われていて,組み込み分野ではかなり普及度が高いフォーマットである.


で,以下の方針で実装してみた.
  • ELFとモトローラSフォーマットの両方に対応.
  • ELFの場合は loadelf, runelf コマンドでロードと起動.動作的には従来と同様で,loadelf でバッファにいったん読み込んで,runelf で実メモリに展開して実行する.
  • モトローラSフォーマットの場合は,load,run コマンドでロードと起動.
  • つまり従来の load, run コマンドは,ELFでなくモトローラSフォーマットを解釈するように仕様変更し,ELF用には loadelf, runelf を新設するということだ.
で,実装したのが以下.モトローラSフォーマットの解釈のために,ブートローダーに srec.c というファイルが追加されている.モトローラSフォーマットについてはネットで検索するといっぱい出てくるのでここではいちいち説明しないけど,まあ解析はたいしてたいへんではない.

修正内容について,以下に説明しよう.まずはブートローダーの修正箇所.

diff -ruN h8_08/kzload/ld.scr h8_10/kzload/ld.scr
--- h8_08/kzload/ld.scr Sun Sep 20 00:33:51 2009
+++ h8_10/kzload/ld.scr Sun Sep 20 13:39:14 2009
@@ -8,6 +8,7 @@
rom(rx) : o = 0x000100, l = 0x07ff00 /* 512kb */
ram(rwx) : o = 0xffbf20, l = 0x004000 /* 16kb */
buffer(rwx) : o = 0xffdf20, l = 0x002000 /* 8kb */
+ data(rwx) : o = 0xffdf20+7400, l = 0x002000-7400 /* -BUFSIZE */
stack(rw) : o = 0xffff00, l = 0x000010 /* end of RAM */
}

@@ -30,18 +31,22 @@
_erodata = . ;
} > rom

+ .buffer : {
+ _buffer_start = . ;
+ } > buffer
+
.data : AT(_erodata) {
_data_start = . ;
*(.data)
_edata = . ;
- } > buffer
+ } > data

.bss : {
_bss_start = . ;
*(.bss)
*(COMMON)
_ebss = . ;
- } > buffer
+ } > data

_end = . ;

まずリンカスクリプトなのだけど,従来はグローバル変数も loadbuf[] も両方ともバッファ領域に配置されていて,BSSの展開時にグローバル変数も破壊される恐れがあった(ていうか,破壊されていた).

対策として,ロード用のバッファ領域とデータ領域を別セクションとして,データ領域はバッファ領域の後ろのほうに配置してグローバル変数などのデータはそっちに持っていくように修正した.

同様に,main.c の修正が以下.

diff -ruN h8_08/kzload/main.c h8_10/kzload/main.c
--- h8_08/kzload/main.c Sun Sep 20 00:33:51 2009
+++ h8_10/kzload/main.c Sun Sep 20 13:39:14 2009
@@ -1,12 +1,10 @@
#include "lib.h"
#include "serial.h"
#include "xmodem.h"
+#include "srec.h"
#include "elf.h"
#include "interrupt.h"

-#define BUFSIZE 7400
-static unsigned char loadbuf[BUFSIZE];
-
static int init()
{
extern int erodata, data_start, edata, bss_start, ebss;
@@ -33,7 +31,8 @@
return 0;
}

-static int putval(unsigned int value, int column)
+#if 0
+static int putval(unsigned long value, int column)
{
char buf[12];
char *p;
@@ -54,8 +53,9 @@

return 0;
}
+#endif

-static int putxval(unsigned int value, int column)
+static int putxval(unsigned long value, int column)
{
char buf[9];
char *p;
@@ -118,6 +118,8 @@
int size = -1;
char *entry_point;
void (*f)();
+ unsigned char *loadbuf;
+ extern int buffer_start;

init();

@@ -127,7 +129,11 @@
puts("kzboot> ");
gets(buf);

- if (!strcmp(buf, "load")) {
+ if (!strcmp(buf, "load") || !strcmp(buf, "loadelf")) {
+ if (!strcmp(buf, "load"))
+ loadbuf = NULL;
+ else
+ loadbuf = (char *)(&buffer_start);
size = xmodem_recv(loadbuf);
if (size < 0) {
puts("XMODEM receive error!\n");
@@ -136,15 +142,20 @@
}
} else if (!strcmp(buf, "dump")) {
puts("size: ");
- putval(size, 0);
+ putxval(size, 0);
puts("\n");
dump(loadbuf, size);
- } else if (!strcmp(buf, "run")) {
- entry_point = elf_load(loadbuf);
+ } else if (!strcmp(buf, "run") || !strcmp(buf, "runelf")) {
+ if (!strcmp(buf, "run"))
+ entry_point = srec_startaddr();
+ else
+ entry_point = elf_load(loadbuf);
if (!entry_point) {
puts("run error!\n");
} else {
- puts("starting from entry point.\n");
+ puts("starting from entry point: ");
+ putxval((unsigned long)entry_point, 0);
+ puts("\n");
f = (void (*)())entry_point;
f();
}

従来はロード用のバッファ領域を loadbuf[] として静的に獲得していたが,リンカスクリプト内で定義されている &buffer_start を見て,バッファ領域を利用するように修正.

あと loadelf とか runelf とかのコマンド対応がされている.load が実行されたときにはバッファ領域は必要無いのでバッファ未指定でxmodem_recv()が呼ばれ,この場合にはモトローラSフォーマットを受信して直接展開する,という動作になる.

次に xmodem.c の修正.

diff -ruN h8_08/kzload/xmodem.c h8_10/kzload/xmodem.c
--- h8_08/kzload/xmodem.c Sun Sep 20 00:33:51 2009
+++ h8_10/kzload/xmodem.c Sun Sep 20 13:39:14 2009
@@ -1,5 +1,6 @@
#include "lib.h"
#include "serial.h"
+#include "srec.h"
#include "uudecode.h"
#include "xmodem.h"

@@ -46,11 +47,16 @@
check_sum = 0;
for (i = 0; i < XMODEM_BLOCK_SIZE; i++) {
c = serial_getb();
+ if (!buf) {
+ if (srec_decode(c) < 0)
+ return NULL;
+ } else {
#ifdef USE_UUENCODE
- buf = uu_decode(buf, c);
+ buf = uu_decode(buf, c);
#else
- *(buf++) = c;
+ *(buf++) = c;
#endif
+ }
check_sum += c;
}

@@ -67,6 +73,8 @@
unsigned char c;
unsigned char *p;

+ srec_init();
+
while (1) {
if (!receiving)
xmodem_wait();
@@ -79,10 +87,11 @@
} else if (c == XMODEM_SOH) {
receiving++;
p = xmodem_read_block(buf);
- if (!p) {
+ if (buf && !p) {
serial_putc(XMODEM_NAK);
} else {
- buf = p;
+ if (p)
+ buf = p;
size += XMODEM_BLOCK_SIZE;
serial_putc(XMODEM_ACK);
}

バッファ未指定の場合の対処が追加されている.

ブートローダーの修正のおおまかな内容は以上.次にOS側の修正点.

まず make image でモトローラSフォーマットのファイル「kozos.mot」を作成するように Makefile にターゲットを追加.

diff -ruN h8_08/os/Makefile h8_10/os/Makefile
--- h8_08/os/Makefile Sun Sep 20 00:34:04 2009
+++ h8_10/os/Makefile Sun Sep 20 13:39:30 2009
@@ -42,11 +42,14 @@
#$(LIB) : $(LIBOBJS)
# $(AR) ruc $(LIB) $(LIBOBJS)

-$(TARGET).uu : $(TARGET)
+$(TARGET).mot : $(TARGET)
+ $(OBJCOPY) -O srec $(TARGET) $(TARGET).mot
+
+$(TARGET).uu : $(TARGET)
uuencode -o $(TARGET).uu $(TARGET) $(TARGET)

-image : $(TARGET).uu
+image : $(TARGET).mot $(TARGET).uu

clean :
rm -f $(OBJS) $(LIBOBJS) $(LIB) \
- $(TARGET) $(TARGET).elf $(TARGET).uu
+ $(TARGET) $(TARGET).elf $(TARGET).mot $(TARGET).uu

次に,BSSの初期化処理を追加する.モトローラSフォーマットだとBSSの情報が来ないみたいなので,OS側でゼロクリアする必要がある.これをやらないとなんかうまく動かなかった.

diff -ruN h8_08/os/main.c h8_10/os/main.c
--- h8_08/os/main.c Sun Sep 20 00:34:04 2009
+++ h8_10/os/main.c Sun Sep 20 13:39:30 2009
@@ -9,6 +9,12 @@

static int init()
{
+ extern int bss_start;
+ extern int ebss;
+
+ /* clear BSS */
+ memset(&bss_start, 0, (uint32)&ebss - (uint32)bss_start);
+
serial_initialize(0, 0);
return 0;
}

OS側の修正はこれだけ.

では実際に試してみよう.ブートローダーをビルドしてフラッシュROMに書き込んで起動し,従来通りの動作として,loadelf; runelf でELFフォーマットをダウンロードさせてOS起動してみる.

kzboot> loadelf
~CLocal command? lsx kozos
Sending kozos, 56 blocks: Give your local XMODEM receive command now.
Bytes Sent: 7296 BPS:777

Transfer complete
eceive succeeded.
kzboot> runelf
starting from entry point: ffc120
kozos boot succeed!
command> echo aaa
aaa
OK
command> threads
extintr
idle
command1
OK
command>

とりあえず問題なさそう.

次に,リセットボタンを押してブートローダーを起動しなおして,load; run でモトローラSフォーマットでダウンロードしてOS起動してみる.

kzboot> load
~CLocal command? lsx kozos.mot
Sending kozos.mot, 153 blocks: Give your local XMODEM receive command now.
Bytes Sent: 19712 BPS:827

Transfer complete
eceive succeeded.
kzboot> run
starting from entry point: ffc120
kozos boot succeed!
command> echo aaaa
aaaa
OK
command> threads
extintr
idle
command1
OK
command>

おー問題無さそうだ.ちゃんと動いている.

ただ,当り前だけどモトローラSフォーマットはテキスト形式なので,ダウンロード時間がやたら長くなる.修正しては試してまた修正してをガンガン繰り返すような感じでやりたいときにはちょっとうっとうしいかも.まあシリアルの速度が9600bpsなので,115200bpsとかにすれば解決できるとは思うが.これはそのうち考えよう.