CPU Collection
この間、見る気は無く、何でも鑑定団 なんてのを見てたら(再再放送ぐらいのやつですかね) 昔々のコンピュータキット、TLCS-12が出品されてた。この 番組を見てた人も懐かしさに駆られてかブログを 書いてましたよ。
骨董品値段で、100万円ですって! 世界初のマイクロコンピュータ、 i4004が出品されたら、幾らの値段を付けるんだろう? 日本でも、大事に大事に持っている人が、そこかしこに居そうだな。
実物を持っているか分かりませんが探してみると、 CPU worldなんてのが出てきました。一般に手に入るのは、 インテル系なんですかね。日本国内だと、 私の知っているCPUコレクターさんは、ちょっと幅広く 集めておられるようです。
実物が無くても、持ってる気分にさせてくれる魔法が有ります。火を入れればちゃんと動く所 が凄い、CPUワールドが有るんです。
魔法な所
MIPSの資料を求めてうろうろしてた時に見つけましたよ。魔法な所を。 フィーリングで読むアセンブラ入門がそれです。
アセンブラ出力だけで、39種。HelloWorldの出力だけで、18種を誇るコレクターぶり。 CPUのバリエーションも、インテル系だけなんて隔たっておらず、なんでもみさかいなく (これ、褒め言葉ですよ)収拾してるのがいいですね。
特に嬉しいのは、gdb付属の実行環境を使って、あれこれ出来る事でしょう。
# native architectures. TARGETS_NATIVE += i386-elf.d # major architectures. TARGETS_SIM += arm-elf.d h8300-elf.d mips-elf.d powerpc-elf.d sh-elf.d # other architectures. TARGETS_SIM += avr-elf.d cris-elf.d frv-elf.d m32r-elf.d m6811-elf.d TARGETS_SIM += mcore-elf.d mn10300-elf.d sh64-elf.d sparc-elf.d v850-elf.d # use compiler for main architecture. TARGETS_SIM += arm16-elf.d mips16-elf.d
折角なので、おいらも何か動かしてみよっと。いろいろ有りすぎて目移りしちゃうんで、 メジャーな所から取りあえず、一つだけ選んでみます。
フィーリングで選び(無視し)ます。shって、shellみたいな名前だから却下。powerpcって 3社連合(アプル、モト、IBM)の怨念を引きずっていそうなのでpass。mipsって前回、 作ってあるからpass。h8300って、H8の事だよね。はるか昔にやったからpass。sparcなんてのも 目につくな。これって、 TOPS 500の2番手、京コンピュータ に使われている石じゃないですか。64Bitで8coreを真似してくれたら、入れてもいいんだけど、 今回はpass。
そうなると残ったのは、ARMか。自分の携帯に入っていそうだな。今回は、こやつで やってみるか。
ARM環境
ArchLinuxの上に作ってみた。早速、試運転。
[sakae@archbang exec]$ make run ---------------- arm-elf ---------------- ---- create source file (sample.c => arm-elf.c) cp sample.c arm-elf.c ---- compile (arm-elf.c => arm-elf.s) /usr/local/cross/bin/arm-elf-gcc \ -fno-builtin -nostdinc -nostdlib -static -O -Wall -g -D___`uname -s | sed 's/[\_\-].*//'`___ \ -fverbose-asm -fomit-frame-pointer -DARCH=\"arm-elf\" -o arm-elf.s -S arm-elf.c ---- assemble (arm-elf.s => arm-elf.o) /usr/local/cross/bin/arm-elf-gcc \ -fno-builtin -nostdinc -nostdlib -static -O -Wall -g -D___`uname -s | sed 's/[\_\-].*//'`___ \ -o arm-elf.o -c arm-elf.s ---- link (arm-elf.o => arm-elf.x) /usr/local/cross/bin/arm-elf-gcc \ -fno-builtin -nostdinc -nostdlib -static -O -Wall -g -D___`uname -s | sed 's/[\_\-].*//'`___ \ -Wl,-Tld.scr lib-arm-elf.S -o arm-elf.x arm-elf.o ---- simulate (arm-elf.x => arm-elf.sot) /usr/local/cross/bin/arm-elf-run \ arm-elf.x > arm-elf.sot Hello World! abadface This architecture is arm-elf
長い時間をかけて、造ったかいがありましたよ。あれ? 良く見ると、逆アセンブラのコードが 出てきていないな。万能すぎるMakefileを改変しすぎたか。それにしても鬼のMakefileだなあ。
[sakae@archbang ~]$ du -sh /usr/local/cross/ 105M /usr/local/cross/
ターゲットが1CPUだと、こんなもので済むんですね。入れてはみたものの、どういうCPUか良く知らんな。 資料を探してみる。
玄箱PRO、Linux ZaurusでARMアセンブリプログラミング
こういう楽しいページが出てきた。 玄箱ってPowerPCも有るのね。知らなかったよ。 こちらも読んで、知ったかぶりしましょ。
移植
上でインストールしたARMはさておき、このLinuxにはmips版のクロスコンパイル環境(組み込み用 ライブラリー付き)が入っているので、こちらでもminiライブラリーを使って、Helloを やってみる。
まずは、cross/exec内にある必要なものを頂いてくる。
[sakae@archbang y]$ ls ld.scr lib-mips-elf.S Makefile sample.c syscall.h
続いて、要になるMakefileをちょっと改変(改悪?)。このあたりが移植なのかなあ。 そして実行
[sakae@archbang y]$ make run ---------------- mips-elf ---------------- ---- create source file (sample.c => mips-elf.c) cp sample.c mips-elf.c ---- compile (mips-elf.c => mips-elf.s) /usr/local/bin/mipsel-linux-elf-gcc \ -fno-builtin -nostdinc -nostdlib -static -O -Wall -g -D___`uname -s | sed 's/[\_\-].*//'`___ \ -fverbose-asm -fomit-frame-pointer -DARCH=\"mips-elf\" -o mips-elf.s -S mips-elf.c ---- assemble (mips-elf.s => mips-elf.o) /usr/local/bin/mipsel-linux-elf-gcc \ -fno-builtin -nostdinc -nostdlib -static -O -Wall -g -D___`uname -s | sed 's/[\_\-].*//'`___ \ -o mips-elf.o -c mips-elf.s ---- link (mips-elf.o => mips-elf.x) /usr/local/bin/mipsel-linux-elf-gcc \ -fno-builtin -nostdinc -nostdlib -static -O -Wall -g -D___`uname -s | sed 's/[\_\-].*//'`___ \ -Wl,-Tld.scr lib-mips-elf.S -Wl,-Ttext -Wl,0x80000000 -o mips-elf.x mips-elf.o ---- simulate (mips-elf.x => mips-elf.sot) /usr/local/bin/mipsel-linux-elf-run \ mips-elf.x > mips-elf.sot Hello World! abadface This architecture is mips-elf
無事に実行出来ましたよ。でも、引数の中に潜り込んでいる、unameのパイプは何やってる?
[sakae@archbang y]$ uname -s | sed 's/[\_\-].*//' Linux [sakae@archbang y]$ uname -s Linux [sakae@archbang y]$ uname -a Linux archbang 3.4.4-2-ARCH #1 SMP PREEMPT Sun Jun 24 17:28:37 UTC 2012 i686 GNU/Linux
リンカーとリンク
折角分かりやすい、上から下まで無色透明の例があるので、こってりと見て行く。 まずは、
[sakae@archbang y]$ readelf -e mips-elf.x ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: MIPS R3000 Version: 0x1 Entry point address: 0x80000000 Start of program headers: 52 (bytes into file) Start of section headers: 8108 (bytes into file) Flags: 0x1001, noreorder, o32, mips1 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 1 Size of section headers: 40 (bytes) Number of section headers: 20 Section header string table index: 17 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 80000000 001000 0001f0 00 AX 0 0 4 [ 2] .rodata PROGBITS 800001f0 0011f0 000044 01 AMS 0 0 4 [ 3] .data PROGBITS 80000400 001400 000004 00 WA 0 0 4 [ 4] .bss NOBITS 80000404 001404 000004 00 WA 0 0 4 [ 5] .stack NOBITS 80000410 001404 000400 00 WA 0 0 1 [ 6] .reginfo MIPS_REGINFO 00000000 001404 000018 01 0 0 4 [ 7] .pdr PROGBITS 00000000 00141c 0000c0 00 0 0 4 [ 8] .comment PROGBITS 00000000 0014dc 000011 01 MS 0 0 1 [ 9] .gnu.attributes LOOS+ffffff5 00000000 0014ed 000010 00 0 0 1 [10] .debug_line MIPS_DWARF 00000000 0014fd 0000eb 00 0 0 1 [11] .debug_info MIPS_DWARF 00000000 0015e8 000334 00 0 0 1 [12] .debug_abbrev MIPS_DWARF 00000000 00191c 000168 00 0 0 1 [13] .debug_aranges MIPS_DWARF 00000000 001a88 000040 00 0 0 8 [14] .debug_loc MIPS_DWARF 00000000 001ac8 0002c0 00 0 0 1 [15] .debug_str MIPS_DWARF 00000000 001d88 0000aa 01 MS 0 0 1 [16] .debug_frame MIPS_DWARF 00000000 001e34 0000bc 00 o 0 0 4 [17] .shstrtab STRTAB 00000000 001ef0 0000ba 00 0 0 1 [18] .symtab SYMTAB 00000000 0022cc 0002f0 10 19 18 4 [19] .strtab STRTAB 00000000 0025bc 0000cc 00 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x001000 0x80000000 0x80000000 0x00404 0x00810 RWE 0x1000 Section to Segment mapping: Segment Sections... 00 .text .rodata .data .bss .stack
readelf(i386用)とmipsel-linux-elf-readelfは同じ挙動をするの? これってひょっとして endianが同一だから?まあ、中身には触れないで、管理情報だけを扱うOSっぽい事をしてるだけ だから、兼用出来ちゃうんだな。それを証拠に、中身をもろに扱うアプリは、
[sakae@archbang y]$ objdump -d mips-elf.x mips-elf.x: file format elf32-little objdump: can't disassemble for architecture UNKNOWN!
そんなもの知るかいって、文句たれますな。
次は、readelfした時に出て来る、Entry addressの0x80000000。これリンクの時に、-Wl,0x80000000で 指定してるのね。疑い深いおいらは、-Wl,0x80000300にずらしてa.outを作ってみた。
[sakae@archbang y]$ mipsel-linux-elf-nm a.out 00000010 A _.d1 00000014 A _.d2 00000018 A _.d3 0000001c A _.d4 0000000c A _.frame 00000040 A _.tmp 00000004 A _.xy 00000008 A _.z 8000035c T __close 8000031c T __exit 8000034c T __open 8000032c T __read 8000033c T __write 80000808 B _ebss 80000804 D _edata 80000c10 A _end 80000534 R _erodata 80000c10 B _estack 800004f0 T _etext 80000800 A _gp 80000300 T _start ;; <======= 80000804 B bss_value 80000800 D data_value 8000036c T exit 800004b0 T main 800003a4 T putchar 800003d0 T puts 80000428 T putxval 8000037c T write1
うん、なる程。で、armの逆アセンブルリストを見てたら、1400番地からコードが詰まっていた。arm版は、特に 配置アドレスを指定していないけど、この1400と言う値は何処から出てくるの?
その答えは、ld.scrを嫁って事でした。
.text 0x1400 : { *(.text .text.*) _etext = .; }
ここに、デフォの値がセットしてあったのね。ついでに、このスクリプトと、nmした時に出て来る 値を比べてみると、なーる程ってなって、今までの疑問が氷解しましたよ。こういう事は、例の リンカー本に懇切丁寧に解説されているんだろうね。真面目に読んでみよっと。
面白いのは、lib-mips-elf.Sの冒頭付近に書いてある、ここ見ろっていう案内。
/* * Use reserved instruction. * See gdb/sim/mips/interp.c:signal_exception(),sim_monitor() * (ReservedInstruction) * Memory address is defined by K0BASE. (See gdb/sim/mips/interp.c) */ : /* * To avoid exit message. * (See gdb/sim/mips/mips.igen:break,HALT_INSTRUCTION) */
まずは簡単な所から
/* Note that the monitor code essentially assumes this layout of memory. If you change these, change the monitor code, too. */ /* FIXME Currently addresses are truncated to 32-bits, see mips/sim-main.c:address_translation(). If that changes, then these values will need to be extended, and tested for more carefully. */ #define K0BASE (0x80000000) #define K0SIZE (0x20000000) #define K1BASE (0xA0000000) #define K1SIZE (0x20000000)
0x80000000以下は、ユーザーのための領域。それ以上は、カーネル領域となり、3つの区画(K0-K2)に 分かれてると言うのがMIPSでのお約束。K0もK1もアドレス変換を行わないエリア。そして、K1は キャッシュしないエリアとなっている。(きっと、I/O関係のレジスタでも置くんだろうな) と言う事は、何でも出来るカーネルモードなのね。だから、節度ある使い方をしてねって事か。 このあたりの事は、例の本では、291ページから例外って事で説明してたよ。 ちらちら本を見てたら、SPIMでは、I/OレジスタをK2エリアに割り付けていたぞ。
次は、sim_monitorあたりかな。
case 6: /* int open(char *path,int flags) */ { char *path = fetch_str (sd, A0); V0 = sim_io_open (sd, path, (int)A1); free (path); break; } case 7: /* int read(int file,char *ptr,int len) */ : case 8: /* int write(int file,char *ptr,int len) */ : case 10: /* int close(int file) */ : case 17: /* void _exit() */ :
この case XXって、理由って名前を評価したものなんだけど、これって割り込みの理由って 事なのね。そう思って、lib-mips-elf.Sを覗くと、有りましたねぇ。俗に言うシステムコール 番号ってやつ。
#define SYS_exit 17 #define SYS_open 6 #define SYS_read 7 #define SYS_write 8 #define SYS_close 10
lib-arm-elf.Sの該当部分がどうなっているかと調べてみると、
#define SWI_Exit 0x11 #define SWI_Open 0x66 #define SWI_Close 0x68 #define SWI_Write 0x69 #define SWI_Read 0x6a
この違いは、gdb-simの作者様の違いによるものだろう。そんな各作者の意図をソースから 読み取って、lib-xxx-elf.Sを造っちゃう人って尊敬しちゃうぞ。これが一番の逸品だと 思いますね。
一方、割り込みの上位概念に相当するのが例外だ。(なんて書くと、厳格な人からは 注意されそう)signal_exceptionが担当する事になってる。 gdb-simの都合で、上記の割り込みは、予約命令として扱ってるのかな。
switch (exception) { case DebugBreakPoint: : case ReservedInstruction: : default: : switch ((CAUSE >> 2) & 0x1F) { case Interrupt: case NMIReset: case TLBModification: case TLBLoad: case TLBStore: case AddressLoad: case AddressStore: case InstructionFetch: case DataReference: case ReservedInstruction: case CoProcessorUnusable: case IntegerOverflow: case FPE: case BreakPoint: case SystemCall: case Trap: case Watch: :
2番目のswitch以下の分岐では、ほとんどの場合、sim-haltするように実装されてた。と言う事は gdb-simは、軽くユーザーアプリを動かす時に使ってねって事です。夢々、カーネルっぽいアプリを 動かしてはいけません。
最後は、HALTを見てからHALTします。
000000,20.CODE,001101:SPECIAL:32::BREAK "break %#lx<CODE>" *mipsI: *mipsII: : { /* Check for some break instruction which are reserved for use by the simulator. */ unsigned int break_code = instruction_0 & HALT_INSTRUCTION_MASK; if (break_code == (HALT_INSTRUCTION & HALT_INSTRUCTION_MASK) || break_code == (HALT_INSTRUCTION2 & HALT_INSTRUCTION_MASK)) { sim_engine_halt (SD, CPU, NULL, cia, sim_exited, (unsigned int)(A0 & 0xFFFFFFFF)); } :
HALTに出会ったら、終了って風に読めるんだけど、冒頭の方に余計なものが付いてて、Cのソース じゃない事は分かるな。ファイル名からして、mips.igenってなってるし。こりゃ、mipsの命令の 定義書だな。きっとこれを元に、Cのソースを自動生成するのだろうな。 行頭のやつは、難だ? ファイルの冒頭にあるのが、説明書かな。
// <insn> ::= // <insn-word> { "+" <insn-word> } // ":" <format-name> // ":" <filter-flags> // ":" <options> // ":" <name> // <nl> // { <insn-model> } // { <insn-mnemonic> } // <code-block>
もしかしてBFD(big fucking deal)に関係してる??
hexedit
フィーリング... のページでおまけとして紹介されたhexedit。 パッチを当ててより便利に使いましょうって案内。
早速、FreeBSDにもArchLinuxにも入れておきました。昔、おいらがMac信者だった頃、一番 使ってたのがResEditって言うツール。hexeditも必需品になりそうな予感。
ここでもMIPS
PIC32MX220 (200円のMIPS CPU)を使う とか、retroBSD on PIC32マイコンとか、 感覚の鋭いお方は、遊び方が素晴らしいですね。
上記の石は、日本の趣味の電子工作を支えてきた、秋月ちゃんが売ってるそうで、こちらも鋭い。 折角なので、この石に魅せられた方を探してみた。
PIC32MX220 Tutorials の回路図を見ると、石は28PinのDIPなのね。これだと、おいらにも作れそうだな。200円の石にも A/Dコンバーターが付いてるなんて、飽食な時代だねぇ。そんな事より、MIPSが200円ってのに 驚けよ!!
仕様書は、MICROCHIP のサイトに有るのか。落としてきて読んでみるかな。