はじめに
とりあえず今の時点で上手く行った方法を書いてますが、適当に更新していくつもりです。
今回はgcc9.5.0をビルドしてMC68000でCのコードを走らせる方法を書いていきます。
gccのビルド
このレポジトリの内容に従うだけです。
最初はCygwinでやろうと思いましたが途中でコケたのでMSYS2を導入してビルド。
1 2 3 4 5 6 7 8 9
| admin@LUNAEDGE-X260 MSYS ~ $ m68k-elf-gcc -v Using built-in specs. COLLECT_GCC=m68k-elf-gcc COLLECT_LTO_WRAPPER=/opt/m68k-elf/libexec/gcc/m68k-elf/9.5.0/lto-wrapper Target: m68k-elf Configured with: /home/admin/m68k-elf-toolchain/projects/gcc/configure --prefix=/opt/m68k-elf --target=m68k-elf --enable-languages=c,c++,lto --enable-version-specific-runtime-libs --disable-libssp --disable-nls --disable-threads --disable-libmudflap --disable-libgomp --disable-libstdcxx-pch --with-gnu-as --with-gnu-ld --with-newlib --with-headers=/home/admin/m68k-elf-toolchain/projects/newlib-cygwin/newlib/libc/include/ --disable-shared --src=../../projects/gcc Thread model: single gcc version 9.5.0 (GCC)
|
こんな感じになります(PATHもちゃんと通しておく)。
スタートアップスクリプト
ここから先はKiさんに助言を乞いながら作成しました。
この場を借りて感謝申し上げます。
今回は極力アセンブラを使わずにスタートアップルーチンを記述します。
割り込みベクタも最小限しか書きません。
最低限動くコードがこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <stdint.h>
extern uint32_t __stack_top;
static void _init_board(void); static void _reset_handler(void);
const volatile uintptr_t vectors[] __attribute__((section(".isr_vector"))) = { (uintptr_t)(&__stack_top), (uintptr_t)(&_reset_handler) };
static void _init_board(void){ }
static void _memcpy(uint8_t* dst,uint8_t* src,size_t size){ while (size--) { *dst++ = *src++; } }
static void _memclr(uint8_t* dst,size_t size){ while (size--) { *dst++ = 0; } }
static void _reset_handler(void){ _init_board(); extern uint8_t __data_top,__data_bottom,__text_bottom; size_t size = (size_t)(&__data_bottom - &__data_top); _memcpy(&__data_top,&__text_bottom,size); extern uint8_t __bss_bottom,__bss_top; size = (size_t)(&__bss_bottom - &__bss_top); _memclr(&__bss_top,size); main(); while(1); }
|
スタートアップルーチンでは
- ボードの初期化
- 変数の初期値設定
- main(エントリポイント)へのジャンプ
を行います。
ここで、main関数は別ファイルでextern宣言されています。
MC68kのベクタは0番にSSP初期値、1番にPC初期値が入ります。
リンカスクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| /* Ricochet用Bootloader リンカスクリプト 2022.11.23 c0ntra1l */
/* メモリマップ(Bootloader実行時) 0x000000 - 0x7FFFFF Flash* 0x100000 - 0x13FFFF RAM(データ) 0x200000 - 0x23FFFF RAM(プログラム)
メモリマップ(ユーザープログラム実行時) 0x000000 - 0x13FFFF RAM(プログラム)* 0x100000 - 0x23FFFF RAM(データ)
*:実行するプログラム */
MEMORY { vec (r) : ORIGIN = 0x000000, LENGTH = 0x400 rom (xr) : ORIGIN = 0x000400, LENGTH = 512K - 0x400 ram (rw) : ORIGIN = 0x100000, LENGTH = 256K }
SECTIONS { /*ベクタ*/ .isr_vector : { __rom_top = .; __rom_vec = .; } > vec
/*.textセクション*/ .text : { __text_top = .; *(.text*) __text_bottom = .; *(.rodata*) } > rom
/*.dataセクション*/ .data : { __data_top = .; *(.data) __data_bottom = .; } > ram
/*.bssセクション*/ .bss : { __bss_top = .; *(.bss) *(COMMON) __bss_bottom = .; __stack_top = .; } > ram }
|
メモリ配置を定義します。hoge_topとhoge_bottomはスタートアップルーチンで転送(初期化)するメモリの領域計算に使用します。
.dataセクションが初期値あり変数、.bssセクションが初期値なし変数、.textがプログラム、.rodataが初期値あり変数の初期値が入る領域のようです。
Makefile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| prefix = m68k-elf- CC = $(prefix)gcc AS = $(prefix)as LD = $(prefix)ld OBJCOPY = $(prefix)objcopy OBJDUMP = $(prefix)objdump RM = rm
PROJECT = r68k_bootloader PROGRAM = $(PROJECT).elf TARGET_S = $(PROJECT).s68 TARGET_B = $(PROJECT).bin
CFLAGS = -g -O2 -m68000 -Wall -fomit-frame-pointer LDFLAGS = -Map $(PROJECT).map -T ricochet.lds -nostdlib
MAKEFILE = Makefile
OBJS = main.o startup.o
LIBS =
all: $(TARGET_B) $(TARGET_S)
$(TARGET_B): $(PROGRAM) $(OBJCOPY) -O binary --remove-section=.data $(PROGRAM) $(TARGET_B) @echo '-------------------------------------------------------------------' @echo " $(TARGET_B) build succeeded. [`date`]" @echo ''
$(TARGET_S): $(PROGRAM) $(OBJCOPY) -O srec --remove-section=.data $(PROGRAM) $(TARGET_S) $(OBJDUMP) -D $(PROGRAM) >$(PROGRAM).dasm $(OBJDUMP) -h $(PROGRAM) @echo '-------------------------------------------------------------------' @echo " $(TARGET_S) build succeeded. [`date`]" @echo ''
$(PROGRAM): $(OBJS) $(LIBS) $(LD) $(LDFLAGS) -o $(PROGRAM) $(OBJS)
%.o: %.c $(CC) $(CFLAGS) -c $<
clean: $(RM) -f $(PROGRAM) $(RM) -f $(TARGET_S) $(RM) -f $(TARGET_B) $(RM) -f *.o
tar: cd .. && tar czvf $(PROJECT)-`date '+%Y%m%d'`.tar.gz $(PROJECT)
|
今回はシミュレータでのテスト用にbinファイルだけでなくSレコードも出力しています。
また、gccのオプションに-fomit-frame-pointer
を指定し、フレームポインタを使用しないようにしています。
テストプログラム
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "startup.h" #include <stdint.h>
extern signed main(void);
uint8_t array[128];
signed main(void){ for(uint8_t i = 0;i < 128;i++){ array[i] = i; } return 0; }
|
コンパイルできることをチェックするために適当なプログラムをでっちあげました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| admin@LUNAEDGE-X260 MSYS /c/Users/Admin/Documents/Bootloader_68k $ make all m68k-elf-gcc -g -O2 -m68000 -Wall -fomit-frame-pointer -c main.c m68k-elf-gcc -g -O2 -m68000 -Wall -fomit-frame-pointer -c startup.c startup.c: In function '_reset_handler': startup.c:47:2: warning: implicit declaration of function 'main' [-Wimplicit-function-declaration] 47 | main(); | ^~~~ m68k-elf-ld -Map r68k_bootloader.map -T ricochet.lds -nostdlib -o r68k_bootloader.elf main.o startup.o m68k-elf-objcopy -O binary --remove-section=.data r68k_bootloader.elf r68k_bootloader.bin ------------------------------------------------------------------- r68k_bootloader.bin build succeeded. [Mon Nov 28 08:01:45 JST 2022]
m68k-elf-objcopy -O srec --remove-section=.data r68k_bootloader.elf r68k_bootloader.s68 m68k-elf-objdump -D r68k_bootloader.elf >r68k_bootloader.elf.dasm m68k-elf-objdump -h r68k_bootloader.elf
r68k_bootloader.elf: file format elf32-m68k
Sections: Idx Name Size VMA LMA File off Algn 0 .isr_vector 00000008 00000000 00000000 00002000 2**1 CONTENTS, ALLOC, LOAD, DATA 1 .text 00000064 00000400 00000400 00002400 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 2 .bss 00000080 00100000 00100000 00004000 2**2 ALLOC 3 .debug_info 0000149d 00000000 00000000 00002464 2**0 CONTENTS, READONLY, DEBUGGING 4 .debug_abbrev 000004b9 00000000 00000000 00003901 2**0 CONTENTS, READONLY, DEBUGGING 5 .debug_loc 00000116 00000000 00000000 00003dba 2**0 CONTENTS, READONLY, DEBUGGING 6 .debug_aranges 00000040 00000000 00000000 00003ed0 2**0 CONTENTS, READONLY, DEBUGGING 7 .debug_ranges 00000028 00000000 00000000 00003f10 2**0 CONTENTS, READONLY, DEBUGGING 8 .debug_line 00000336 00000000 00000000 00003f38 2**0 CONTENTS, READONLY, DEBUGGING 9 .debug_str 0000054d 00000000 00000000 0000426e 2**0 CONTENTS, READONLY, DEBUGGING 10 .comment 00000011 00000000 00000000 000047bb 2**0 CONTENTS, READONLY 11 .debug_frame 00000048 00000000 00000000 000047cc 2**2 CONTENTS, READONLY, DEBUGGING ------------------------------------------------------------------- r68k_bootloader.s68 build succeeded. [Mon Nov 28 08:01:45 JST 2022]
|
make all
を実行するとこのようになります。
Warningが出ていますが、コレはアセンブラを使わないようにするためにasm("jmp main")
のかわりにmain()
としたためです。
逆アセンブルを見てみると
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| r68k_bootloader.elf: file format elf32-m68k
Disassembly of section .isr_vector:
00000000 <vectors>: 0: 0010 0080 orib #-128,%a0@ 4: 0000 0418 orib #24,%d0
Disassembly of section .text:
00000400 <main>: 400: 41f9 0010 0000 lea 100000 <__data_bottom>,%a0 406: 4200 clrb %d0 408: 10c0 moveb %d0,%a0@+ 40a: 5200 addqb #1,%d0 40c: 0c00 ff80 cmpib #-128,%d0 410: 66f6 bnes 408 <main+0x8> 412: 7000 moveq #0,%d0 414: 4e75 rts ...
00000418 <_reset_handler>: 418: 203c 0010 0000 movel #1048576,%d0 41e: 0480 0010 0000 subil #1048576,%d0 424: 6718 beqs 43e <_reset_handler+0x26> 426: 43f9 0010 0000 lea 100000 <__data_bottom>,%a1 42c: 0680 0000 0464 addil #1124,%d0 432: 41f9 0000 0464 lea 464 <__text_bottom>,%a0 438: 12d8 moveb %a0@+,%a1@+ 43a: b1c0 cmpal %d0,%a0 43c: 66fa bnes 438 <_reset_handler+0x20> 43e: 203c 0010 0080 movel #1048704,%d0 444: 0480 0010 0000 subil #1048576,%d0 44a: 6710 beqs 45c <_reset_handler+0x44> 44c: 41f9 0010 0000 lea 100000 <__data_bottom>,%a0 452: 4218 clrb %a0@+ 454: b1fc 0010 0080 cmpal #1048704,%a0 45a: 66f6 bnes 452 <_reset_handler+0x3a> 45c: 4eb9 0000 0400 jsr 400 <main> 462: 60fe bras 462 <_reset_handler+0x4a>
Disassembly of section .bss:
00100000 <array>: ...
|
問題なくコンパイルできているようです。
いろいろ気になるところはありますが、とりあえず動いたのでよしとしましょう。
続きは実機で動かしてからですね。
シミュレータに突っ込む
S68をシミュレータに食わせてPCを0x000418にセット、走らせてメモリを覗いてみるとこんな感じになってます。
問題なさそうですね。
思った点
今回はMC68kのシミュレータにEASy68kを使いました。
なかなか使いやすくて良いのですが、いろいろ不満点もあります。
- ベクタを読まない(開始アドレスが0x400、SSPが0x100000固定っぽい?)
- S68だけでは逆アセンブルが表示されない(L68が必要らしい、どうすればいい?)
前者は直接PCを書き換えてリセットハンドラに飛べばいいのでまだなんとかなりますが、後者はかなり厳しいです。
今は横に逆アセンブルリストを出して、目視でデバッグをしていますが規模が大きくなるとそれすらも大変になりそうです。
なんとかならないものでしょうか…
おわり。