無意味のような生き方

組込みエンジニアが怒りと無念をさえずるブログ。

独習Cの1問目がむずい

C言語の能力が足りないことを痛感する日々。OSを作っていても、C言語の基本的なことがわかっていないから、無駄な作業に膨大な時間を費やしてしまう。「HelloWorld」が画面に現れなくて、OS本を何度も読み返し、処理のシーケンスを1から見直しても原因がわからず、完全に諦めてから数日後、「if(xx == 0)」と書かれていたのを見つけた。修正してみたら、あっさり上手く行った。がっくし。こんな初歩的なミスに気づかないほど、C言語に慣れていない自分に落ち込む。C言語にも自信がない状態でスキルアップしたいとかふざけてんじゃねえと、採用担当者だったら思うだろうな。

 

C言語の問題集を買う。独習C。著者はartonさんという謎の人物。まだ読んでいないので内容の感想は書けないが、ハンドルネームで大著を出すのはすごい。僕だったら自己顕示欲が我慢できないと思う。


早速、1問目からやってみることにした。

 

【ch02-01.c】

#include <stdio.h>
#include <stdlib.h>
int main( int argc, char *argv[] )
{

  if( argc == 1 )
  {

   puts( "hello world!" );
  }
  else
  {

   int sum = 0;
   for( int i = 1; i < argc; i++ )
  {
   sum += atoi( argv[i] );
  }
   printf( "sum = %d ", sum );
  }
   return 0;
}

 

書いて・・実行。

 

f:id:ikenohotorino:20190115230806p:plain

エラー

コンパイルエラーや!

 

何度見直しても、テキスト通りに書かれている。環境がまずいのか?でも、他のテキストを見ながらプログラムは間違いなくコンパイルできている。もうだめだ。ここから3ヶ月経った。

 

 

f:id:ikenohotorino:20190115230844p:plain

コンパイル成功

通った!


会社のコンパイラコンパイルしてみたことがきっかけだった。仕事中、やることをほったらかしにして、試しにやってみたところ、ワーニングすら出ず、あっさり上手く行った。会社のコンパイラEclipse。何が違うんだろうと調べてみた結果、以下の文に問題があることが判った。

 

for( int i = 1; i < argc; i++ )

 

for文の中で変数宣言しているのが原因らしい。

 

調べるうちに判ってきたのは、borland c++ というコンパイラは相当古いということだ。C言語の規格が「K&R」→「C89」→「C99」→「C11」とアップグレードされる中、どうやらコンパイラはC89くらいで止まっているらしい*1。for文内の変数宣言が可能になったのがC99以降らしいので、それ以前に対応しているコンパイラだと、エラーになってしまうらしい。いや、それにしても、89って・・・。生まれる前にできたものを使ってるエンジニアに未来は。。

 

最近『プログラマが知るべき97のこと』という本を買った。その界隈ではおそらく有名だと思われるエンジニアの人たちが、2ページずつ各々の教えを説いているコラム集である。その中に「浮動小数点数は実数ではない」というタイトルのコラムを見つけて、自己顕示欲のあまりの低さに驚嘆する。書籍化されると決まっている何でもありのコラムで、自分のことを語らず、技術の話題だけで割り切れるってすごい。そしてやっぱり、技術ブログは難しい。

 

独習C 新版

独習C 新版

 

 

 

プログラマが知るべき97のこと

プログラマが知るべき97のこと

 

 

 

*1:確証はない。いつか確かめたいけど

一番つまらないことを言ってしまう

 「面白いことがあったんだけど」という枕言葉が禁止されて久しい。僕が最初に耳にしたのは、ホンマでっかTVのさんまだった気がする。あれから10年。いつも間にか使っている私。全然言いたくないし、そもそも面白い話だと思っていないのに、気がつくと頭に付けていることがある。この話し出しをすると、何かしらのオチがないと終わらなくなってしまうこともわかっているのに。

 

頭の中で浮かんだ選択肢のうち、一番つまらないことを言ってしまうという最悪の癖をもっている。特に、気を遣わなければならない上司や女子の前で、発動する傾向にある。普段リラックスしている時は、思いついた発言の中から、「面白さ」「伝えたさ」を基準に選んで会話している。しかし、上司や女子の前だと、基準が「波風立てなさ」「無難さ」になってしまい、ロクな事が言えなくなる。しかも「無難さ」ならまだ良いのだが、さらに質が悪いのが「手垢のつき具合」で決めてしまうことだ。無難さですらなく、「いかに口&耳馴染みがよいか」で発言を決めてしまっていることがある。要はパクリだ。人と同じでなければ何とでもなる時代に、人がやったことをついしてしまうと悲しくなる。しかも無難とは違い、大きくスベることもある。だけど、スベっても、一番ベタなスベり方だから何も生まれない。

 

自分の中の無難を塞ぐため、今年から青色の服を身につけることを禁じた。どうにかなってくれることを願う。

 

 

青い服の女 新・御宿かわせみ

青い服の女 新・御宿かわせみ

 

 

人生の出発は、つねにあまい。

2018年末。ファミリーマートの鶏めし&みぞれチキン弁当を食べながら年を越した。わんこそばの代わりにカップ味噌汁と一番搾りを飲み、紅白とガキ使の代わりにyoutubeで日本中のフリースタイルバトルを見ていた。バトルの負けてる方を嫌いな会社の人間に重ねながら、何度も見たバトルなので頭の中で覚えている言葉を映像に合わせて復唱し、熱くなっていた。年をまたぐ瞬間は、abemaTVのニュース番組で小川アナを見ながら迎えた。2019年最初に見た女性が小川アナって、めちゃくちゃ贅沢だ。

 

フリースタイルバトルを見まくった後は、さぞ日常会話も上手くなっているだろう思うのだが、実際は驚くほど何も変わっていない。むしろ家にいる時間が増えている分、より下手になっていると思われる。この間、最近読んだ『ホモ・デウス』の話を、意気揚々と地元の友だちにしたのだが、反応はイマイチだった。まず、舌が回らない。話しだそうとして、最初の言葉が出てこずに5~6秒沈黙してしまう。極めつけは、「最近すごく面白かったのが~~」という、バラエティっ子とは思えない始め方をしてしまうことだ。

 

自己啓発本は「挑戦」させたがる。お決まりの言い草はこうだ。「とにかく挑戦しろ。挑戦して成功体験を積むことで自信がつき、次また挑戦したくなる」

だけど、この理屈はおかしい。挑戦したら成功するとは限らないはずだ。ギャンブルで成功するコツを聞かれて、「とにかく賭けろ。賭けてペイバックされれば、それを元手にまた勝負したくなる」と言っているのと変わらない。でも、ギャンブルの必勝法は「とにかく賭ける」ことではない。賭けるタイミングを見極めることが大切だと思う。

 

失敗するのが怖くて挑戦できない人は、失敗体験を積んでいるのだと思う。何かに挑戦して、失敗体験を積んでしまったので、自信が失われ、新しいことに行動するための億劫さが増した結果、新しいことに挑戦しなくなったのだと思う。でも、挑戦しないことで、長時間かけて人生が大きく失敗してしまうことを考えると、失敗するとわかっていても挑戦せざるを得ないのかもしれない。

 

正月を過ぎて実家に返った後、生まれて初めて地元のお土産を会社の人たちに配った。チームの会議が終わったタイミングを見計らい、意を決して「お土産を・・」と言って、何人かが僕に気づいた瞬間、声が大きい人が「飲み会やりましょーー!」と言って場の支配権を奪った。しかも、一通り話し終えた後に「では解散!」と言ったせいで、チーム内の人たちはぞろぞろと帰り出してしまった。今思い出しても苦しいが、すでに何人かに気づかれた手前、僕はもう一度「お土産があるので受け取ってください」と蚊の鳴くような声で発言し、会議室の出口にお土産をおいた。その後は気が動転してあまり覚えていないが、周りの人にめちゃくちゃに話しかけまくった気がする。ただ、最後に会議室を出た時に見たお土産は、チームのメンバーが20人いる中で、4個しか取られていなかった。4人、本当にありがとう。

 

お土産は貰ってくれた方が嬉しいんだ。僕がお土産をもらえそうな時は、必ず受け取ってその場で食べるようにしよう。思い出して心がむしられる失敗体験ではなく、お土産をもらう側のマナーを学んだという成功体験として記憶に残すため、「よかった」「よかった」と呪文のように唱えるのである。

 

【組込みOS開拓史 PART4】今日までの進捗状況

久々の更新。これまでの組み込みOSの進捗状況を書き記す。

 

■本来のスケジュール
11/25-:機能設計(起動・メモリ)
12/2-:機能設計(ブートローダ・通信) 
12/9-:機能設計(割込み)
12/16-:機能設計(タスク)
12/23-:詳細設計
12/30-:コーディング①
1/6-:コーディング②
1/13-:試験項目作成
1/20-1/27:テスト実施・リファクタリング作業

 

本来だと、「機能設計」工程が終わって、現在は詳細設計の段階に入っていたらしい。案の定、まったくできていない。本当は、毎週のペースでブログを更新して、モチベーションを保つ予定が、まったくできなくなってた。仕事のせいもあるけれど、ほとんど怠惰とアサシンクリードのせい。まだ諦めたわけではないので、新しくスケジュールを引き直した。

 

■スケジュール

11/25-:機能設計(起動・メモリ)
12/16-:機能設計(ブートローダ)
12/31-:機能設計(割込み)、ライブラリ関数作成
1/2-:機能設計(タスク・OS関連)
1/8-:手直し作業
1/12:お披露目
1/20:これまでの成果物を全て公開

 

「思い切って」でもないけど、詳細設計とコーディングを丸々削除した。機能設計時点で実装してしまっているので、詳細設計もコーディングもはや意味がないことに今更気づいた。ただ、ライブラリ関数だけは、関数の作りを覚えておきたいので、設計書を残すことにする。

あと、お披露目日を無理やり1/12に設定した。人に見せないとやる気が出ない凡庸さにがっかりしながら最後までやりたい。

 

【組込みOS開拓史 PART3】機能設計_メモリ ①事前知識

組込み最重要課題のメモリ設計。

 

リンカスクリプト解説】

<コマンド系>

OUTPUT_FORMAT("ファイル形式"):実行ファイル形式

OUTPUT_ARCH():h8300h

ENTRY():

MEMORY{}:物理的メモリ構成を決める

SECTION{}:メモリ領域を役割ごとに分割する

 

<MEMORY内記法>

xxxxx(rx)             : o = 0x000000, l = 0x080000

:xxxxx→領域名。※romall ramallは?

  (rx)→読み出し・書き込みの性質を定義。

 o → 領域の開始(オリジン)アドレス

  l → 領域の長さ(レングス)。

 

<SECTION内記法>

.:ロケーションカウンタ。セクション領域の先頭に「. = 0x00 」と記述することで、後続を0x00以降にマッピングで     

  きる。アドレス配置はMEMORYコマンドで管理できるため、上記の記述は不要。

  ロケーションカウンタを取得する場合は、「_text_start = . ;」と記述する。セクションの開始/終了アドレスを

  知るために、この記述は必要。(MEMORYで定義されているstackのようなセクションでもなぜ必要なのか?)

.xxx:{ aaa.o(.bbb)} > ccc

  :xxx→セクション生成。.vector={}で、vectorというセクションが作られる。

  aaa.o(.bbb)→aaa.oというオブジェクトファイルの.bbbセクションのデータをc領域に配置。

   (初期値なし変数は.bssセクションと、どこで決まっている?)

  例:.text:{ *(.text) }> rom   *は全オブジェクトファイルファイル

 

PA≠VA対応】

初期値つき変数の場合、書き込み時のアドレス(ROM)と、実行中にアクセスするアドレス(RAM)を分ける。

プログラムの初期化時に、RAM→RAMへのコピーを行う。

このことをPA≠VA対応と言い、これによって、初期値つき変数の動的な書き換えが可能になる。

PA物理アドレス(ロード・アドレス)。変数の初期値が配置されるアドレス。

VA:仮想アドレス(リンク・アドレス)。プログラムがアクセスするアドレス。

 

【メモリ配置を考える】

※マニュアル「3.6 各動作モードのメモリマップ モード5」および『OS本』 p.92参照。

※データ長の算出

:開始アドレス「0x00」 終了アドレス「0xFF」の場合、データ長は「0x100」。

  開始アドレス0x00の領域+0x01~0xFFの領域となるため。

 

【用語一覧】

・リンクとは

コンパイルして生成された複数のオブジェクトファイルを結合すること。

 

リンカスクリプトファイルとは

:メモリ配置を定義しているファイル

 

・プログラムがRAM/ROM上で動く、とはどういうことか?

機械語コード(.text領域)がRAMにあるか、ROMにあるか。

【組込みOS開拓史 PART2】機能設計_起動 ①スタックポインタの初期化

まずは、起動に関する設計を行う。設計の対象は、マイコンの電源をONしてから、main関数がコールされるまでの処理だ。普段、仕事でやっている組込みよりも、さらに「下」の領域だ。「レジスタの操作をアセンブラで行う」という痛快なことをやる。

 とりあえずスタックポインタの初期化からはじめる。

 

【起動シーケンス】
①電源ON
②リセット割込み
 (リセット割込みベクタに登録済の関数start()がコールされる。)
 ※割込みベクタについては、別途、割込みの設計をする時に考える。
③割込みベクタ内でスタックポインタの初期化
④main関数へジャンプ
→ 以降のシーケンスは「機能設計_ブートローダ」に記載する。

 

【スタックポインタの初期化】
スタックポインタ:スタック領域の最上段*1のアドレスを格納するレジスタ。ここからスタック領域として使います!という目印としての役割を果たす。

 

・スタックポインタをどうやって設定するか?
マイコンマニュアルによれば、「sp」というレジスタが用意されているらしい。関数をコールする際、何もしなくても、spが勝手にスタックポインタとしての役割を果たしてくれる。よって、僕がやるべきことは、spに対し、スタックポインタとしたいアドレスを代入することだけだ。

 

<スタックポインタの設定方法>
レジスタ「sp」について

f:id:ikenohotorino:20181125230515p:plain

ER0~ER7:汎用レジスタ(32bit)

 

ER7という32bitのレジスタがスタックポインタのレジスタとして使えるらしい。上の表だと(SP)というのがどの領域かわかりにくい。*2

 

レジスタへのアクセスについて
マニュアル「2.7 アドレッシングモードと実効アドレスの計算方法」を読むと以下のように書いてある。

 

レジスタ直接  Rn
レジスタ間接  @ERn
イミディエイト  #xxx(レジスタ、値)

 

他にもアクセス方法はあるが、今の所は直接アクセスの方法がわかればいい。ER7とかけば、ER7のレジスタにアクセスできるようだ。また、ER7にかんしては、spと言い換えができるらしい。*3

 

レジスタへの命令は以下のように書く。(『組込みOS入門』(以下『OS本』)より

 

mov.l #_stack,sp

 

- mov A B:A→Bへのデータ転送。(.lはlongワード型を表す。アドレスなので4バイト。)
- _stackは、stackをリンカスクリプトで定義している。_stackと記述することで、アドレスを表す。(別途記載する)
- #は、即値命令を表す。*4

 

・実際にどのアドレスから何バイトをスタック領域として使うべきか?
具体的なコードでいうと、stackをリンカスクリプトファイルのどこに書けばよいかという問題だ。スタック領域がどれだけのサイズになるかは、実行してみないと分からない。よって、『OS本』では、RAMの末端付近に配置している。

 

stack(rw) : o = 0xffff00, l = 0x000000 /* end of RAM */

 

o:開始アドアドレス
l:サイズ

 

・なんでアドレス「0xffff00」が末端付近だとわかるのか?
マニュアル「3.6 各動作モードのメモリマップ」および『OS本』 p.92に記されている。

 

【用語一覧】
・スタートアップ
:起動時に始めに実行される処理。リセットのベクタ内に書かれているorベクタからジャンプした先に書かれている。

 

*1:言い方

*2:ER7全体を使えるのか?

*3:どこに書いてある?

*4:ここがよくわからない。#を外してコンパイルしたところ、「invalid operands」というエラーになった。