SDCC用の環境構築
2012年5月22日
Z80用のコンパイラとして、何か使えるかなと思い、Webを色々検索してみると、SDCC (Small Device C Compiler)というものを見つけた。ライセンスはGPLだから、普通に使える。
使い方に関する情報が多くはない。それでも先人の方々がいらっしゃるので、初めての人間には非常に助かる。以下のページを参考にして、KM-Z80用(MZ-80K用)のソフト開発環境を構築してみた。
・Z80のC言語クロスコンパイル(SDCC)(1) - Resilient Mind
・SDCCでクロス開発環境をインストールと使い方(Z80用) - Tosikの雑記
コンパイルは、
のように指令すればよいようだ。インテル方式のHEXファイル(*.ihx)が作成される。ただ、このデフォルトのコンパイルでは色々と問題があって、そのままでは使えないらしい。スタックの開始位置が0xFFFFになっていたり、スタートアドレスが0x0100になっていたりするので、これをKM-Z80用に変更する必要がある。上記2つのリンク先両方で説明されているが、専用のCRT (C Run Time)を作成して、それを利用すればよいとのこと。MZ-80K用のソフトでは、コールドスタートとホットスタートの2つが用意されているのが常だから、0x1200がコールドスタート、0x1203がホットスタートとするようなランタイムとし、アセンブラコード(ファイル名:crt.asm)を次のようにした。
C のソースコードを書くときに、main()関数とinit()関数の2つを準備することになる。このアセンブラコードからオブジェクトを、次のようにして作成した。
この"crt.o"ファイルを取り込むようにリンクすれば、KM-Z80用に利用できる。次のようにコンパイル(+リンク)すればよい。
"sdcc -mz80"というコマンドで、コンパイルとリンクが続けて行われる。ただ、この方法だと、一つのCファイルしか使えない。中規模以上のプログラムになると、複数のCファイルが必要になる。これを行うには、コンパイルのみをまず行い、最後に複数のオブジェクトをリンクすればよい。次のように行えばよいらしい。
これを自動で行うバッチファイル(Windows用)を次のように作成した。
これで、このバッチファイルを実行するだけで、"release.ihx"というHEXファイルが出来上がる。あとは、このHEXファイルをMZTファイルもしくはMZFファイルに変換し(多分、VBScriptで行ける)、mzt2wav.exe もしくは mzf2wav.exeを用いてWAVファイルに変換すれば、KM-Z80上で実行できるようになるはずだ。
120523 追記
ihxファイルをmztファイルに変換するスクリプト(VBScript)は、以下の通り。VBScriptでバイナリデータを扱うためのクラスを以下のリンク先を参考に使わせていただいた。
ADODB.Streamに書き込めるバイナリデータを生成するクラス
使用の際は、4-8行目を書き換える必要がある。
使い方に関する情報が多くはない。それでも先人の方々がいらっしゃるので、初めての人間には非常に助かる。以下のページを参考にして、KM-Z80用(MZ-80K用)のソフト開発環境を構築してみた。
・Z80のC言語クロスコンパイル(SDCC)(1) - Resilient Mind
・SDCCでクロス開発環境をインストールと使い方(Z80用) - Tosikの雑記
コンパイルは、
sdcc main.c -mz80
のように指令すればよいようだ。インテル方式のHEXファイル(*.ihx)が作成される。ただ、このデフォルトのコンパイルでは色々と問題があって、そのままでは使えないらしい。スタックの開始位置が0xFFFFになっていたり、スタートアドレスが0x0100になっていたりするので、これをKM-Z80用に変更する必要がある。上記2つのリンク先両方で説明されているが、専用のCRT (C Run Time)を作成して、それを利用すればよいとのこと。MZ-80K用のソフトでは、コールドスタートとホットスタートの2つが用意されているのが常だから、0x1200がコールドスタート、0x1203がホットスタートとするようなランタイムとし、アセンブラコード(ファイル名:crt.asm)を次のようにした。
.globl _init .globl _main call _init jp _main
C のソースコードを書くときに、main()関数とinit()関数の2つを準備することになる。このアセンブラコードからオブジェクトを、次のようにして作成した。
sdasz80 -o crt.o crt.asm
この"crt.o"ファイルを取り込むようにリンクすれば、KM-Z80用に利用できる。次のようにコンパイル(+リンク)すればよい。
sdcc main.c -mz80 --code-loc 0x1200 --no-std-crt0 -Wlcrt.o
"sdcc -mz80"というコマンドで、コンパイルとリンクが続けて行われる。ただ、この方法だと、一つのCファイルしか使えない。中規模以上のプログラムになると、複数のCファイルが必要になる。これを行うには、コンパイルのみをまず行い、最後に複数のオブジェクトをリンクすればよい。次のように行えばよいらしい。
sdcc main.c -mz80 -c sdcc sub.c -mz80 -c sdcc main.rel sub.rel -mz80 --code-loc 0x1200 --no-std-crt0 -Wlcrt.o
これを自動で行うバッチファイル(Windows用)を次のように作成した。
@if exist *.ihx del *.ihx @if exist *.asm del *.asm @for %%i in (*.c) do sdcc %%i -mz80 -c sdcc *.rel -mz80 --code-loc 0x1200 --no-std-crt0 -Wlcrt.o @ren *.ihx release.ihx @if exist *.lk del *.lk @if exist *.lst del *.lst @if exist *.map del *.map @if exist *.noi del *.noi @if exist *.rel del *.rel @if exist *.rst del *.rst @if exist *.sym del *.sym @if exist *.cdb del *.cdb @if exist *.mem del *.mem @if exist *.omf del *.omf
これで、このバッチファイルを実行するだけで、"release.ihx"というHEXファイルが出来上がる。あとは、このHEXファイルをMZTファイルもしくはMZFファイルに変換し(多分、VBScriptで行ける)、mzt2wav.exe もしくは mzf2wav.exeを用いてWAVファイルに変換すれば、KM-Z80上で実行できるようになるはずだ。
120523 追記
ihxファイルをmztファイルに変換するスクリプト(VBScript)は、以下の通り。VBScriptでバイナリデータを扱うためのクラスを以下のリンク先を参考に使わせていただいた。
ADODB.Streamに書き込めるバイナリデータを生成するクラス
option explicit dim fileName, loadAddress, startAddress, ihxFileName, mztFileName fileName="KM-BASIC" loadAddress=&h1200 startAddress=&h1200 ihxFileName="release.ihx" mztFileName="release.mzt" makeMzt fileName,loadAddress,startAddress,hexData(ihxFileName,loadAddress),mztFileName msgbox mztFileName & " is created." function hexData(fname,loadAddr) Dim fobj, line, addr, b1, b2, b3 dim hdata hdata=string("0",fileLength(fname,loadAddr)*2) set fobj=CreateObject("Scripting.FileSystemObject").OpenTextFile(fname,1) while not fobj.AtEndOfStream line=fobj.ReadLine() if left(line,1)=":" then b1=cByte("&H" & mid(line,2,2)) 'length b2=cByte("&H" & mid(line,4,2)) 'address MSB b3=cByte("&H" & mid(line,6,2)) 'address LSB addr=b2*256+b3-loadAddr if 0<b2 then hdata=left(hdata,addr*2) & mid(line,10,b1*2) & mid(hdata,addr*2+b1*2+1) end if end if wend hexData=hdata end function function fileLength(fname,loadAddr) Dim fobj, line, flen, b1, b2, b3 set fobj=CreateObject("Scripting.FileSystemObject").OpenTextFile(fname,1) flen=loadAddr while not fobj.AtEndOfStream line=fobj.ReadLine() if left(line,1)=":" then b1=cByte("&H" & mid(line,2,2)) 'length b2=cByte("&H" & mid(line,4,2)) 'address MSB b3=cByte("&H" & mid(line,6,2)) 'address LSB if flen<b2*256+b3+b1 then flen=b2*256+b3+b1 end if wend fileLength=flen-loadAddr end function sub makeMzt(fname,load,start,hData,saveFile) dim stream, bstream, i, fsize fsize=len(hData)/2 fname=left(fname,15) for i=1 to 15 fname=fname & chr(&h0D) next fname=left(fname,16) set bstream=new ByteStream set stream=CreateObject("ADODB.Stream") stream.open stream.type=1 stream.write bstream.getByte(&h01) '0x01: machine code for i=1 to 16 stream.write bstream.getByte(asc(mid(fname,i,1))) next stream.write bstream.getByte(&h00) stream.write bstream.getByte(fsize mod 256) 'byte size LSB stream.write bstream.getByte(fsize \ 256) 'byte size MSB stream.write bstream.getByte(load mod 256) 'load address LSB stream.write bstream.getByte(load \ 256) 'load address MSB stream.write bstream.getByte(start mod 256) 'start address LSB stream.write bstream.getByte(start \ 256) 'start address MSB for i=1 to 104 stream.write bstream.getByte(&h00) 'Comment next for i=1 to fsize stream.write bstream.getByte(cByte("&h"&mid(hData,i*2-1,2))) next stream.SaveToFile saveFile,2 stream.close end sub ' Following class was fetched from: ' http://sei.qee.jp/docs/program/hta/sample/bstream.html Const ENCODE_UNICODE = "unicode" ' StreamTypeEnum Const adTypeBinary = 1 Const adTypeText = 2 '********************************************************************* ' ByteStreamクラス ' version 1.1 '********************************************************************* Class ByteStream Private innerArray(255) '================================================================= ' クラスの初期化処理 '================================================================= Private Sub Class_Initialize() Dim wkStream Set wkStream = WScript.CreateObject("ADODB.Stream") wkStream.Type = adTypeText wkStream.Charset = ENCODE_UNICODE wkStream.Open Dim i For i=0 To &hff wkStream.WriteText ChrW(i) Next wkStream.Position = 0 wkStream.Type = adTypeBinary If ("fe" = LCase(Hex(AscB(wkStream.Read(1))))) Then wkStream.Position = 2 End If For i=0 To &hff wkStream.Position = wkStream.Position + 1 innerArray(i) = wkStream.Read(1) Next wkStream.Close Set wkStream = Nothing End Sub '================================================================= ' 指定した数値のByte()を返す '================================================================= Public Function getByte(num) If (num < 0) Or (UBound(innerArray) < num) Then getByte = innerArray(0) '0x00を返す Else getByte = innerArray(num) End If End Function End Class
使用の際は、4-8行目を書き換える必要がある。