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行目を書き換える必要がある。