Hatena::ブログ(Diary)

Tosikの雑記 このページをアンテナに追加 RSSフィード

2006-10-23

作成中のPC-G850用Cライブラリ

SDCCでコンパイルできるポケコンPC-G850用Cライブラリを*1作成中です。必要な入出力関数と各種便利関数を予定しています。今のところ以下のような感じです。

はじめに断っておきますが、PC-G850系はG850Sしか持ってないので他の機種の動作確認は行ってません。G850S以外で変なことになる可能性も少なからずあるので何かあったら教えてください。

クイックスタート

z80.zip をダウンロードしてC:\直下に設置すると僕の例と同じ環境になります。C:\z80\main\make.bat を実行するとコンパイルされます。もちろんSDCCは事前に用意しないとできません。

あとは main.ihx を転送してください。

コード*2

ファイル名: lib.c

typedef unsigned char BYTE;
typedef unsigned int WORD;

typedef unsigned char COLOR;
#define		WHITE	0
#define		BLACK	1
#define		REVERSE	2


#define		NULL	0

enum Key
{
	SHIFT	= 0x80,
	DOWN	= 0x1f,
	UP		= 0x20,
	LEFT	= 0x21,
	RIGHT	= 0x22,
	SPACE	= 0x1e,
	CLS		= 0x50,
};


void printNum(BYTE x, BYTE y, WORD value);


/*********************************
  文字列関係
*********************************/

/*
 * strlen(BYTE *string)
 *  文字列の長さを測る
 */
BYTE strlen(BYTE *string)
{
	BYTE i;
	for(i=0; string[i]; ++i);
	return i;
}


/*********************************
  表示関係
*********************************/

/*
 * printChr(BYTE x, BYTE y, char c)
 *  指定した座標に文字列を表示
 */
void printChr(BYTE x, BYTE y, char c)
{
	_asm

	; de <- loc
	ld		e,4(ix)
	ld		d,5(ix)

	; a <- _c
	ld		a,6(ix)

	; 文字を表示するルーチン
	call	#0xbe62

	_endasm;
}

/*
 * printStr(BYTE x, BYTE y, char *string)
 *  指定した座標に文字列を表示
 */
void printStr_asm(BYTE len, WORD loc, char *str);
void printStr(BYTE x, BYTE y, char *string)
{
	printStr_asm(strlen(string),x+y*256 ,string);
}
void printStr_asm(BYTE len, WORD loc, char *str)
{
	_asm

	; b <- len
	ld		b,4(ix)

	; de <- loc
	ld		e,5(ix)
	ld		d,6(ix)

	; hl <- strのpointer
	ld		l,7(ix)
	ld		h,8(ix)

	; 文字を表示するルーチン
	call	#0xbff1

	_endasm;
}

/*
 * printGra(BYTE x, BYTE y, char *gpattern)
 *  指定した座標にグラフィックを表示
 */
void printGra_asm(BYTE len, WORD loc, char *gpattern);
void printGra(BYTE x, BYTE y, char *gpattern, int size)
{
	printGra_asm(size, x+y*256, gpattern);
}
void printGra_asm(BYTE len, WORD loc, char *gpattern)
{
	_asm

	; b <- len
	ld		b,4(ix)

	; de <- loc
	ld		e,5(ix)
	ld		d,6(ix)

	; hl <- gpatternのpointer
	ld		l,7(ix)
	ld		h,8(ix)

	; 文字を表示するルーチン
	call	#0xbfd0

	_endasm;
}

/*
 * printNum(BYTE x, BYTE y, WORD value)
 *  指定した箇所に数値を表示(0~65535の整数)
 */
void printNum(BYTE x, BYTE y, WORD value)
{
	char str[6];
	int i;

	for(i=4; i>=0; i--) {
		str[i] = '0' + value % 10;
		value /= 10;
	}
	str[5] = NULL;

	printStr(x,y,str);
}


/*
 * printHex(BYTE x, BYTE y, WORD value)
 *  指定した箇所に16進数を表示(0x0000~0xffffの整数)
 */
void printHex(BYTE x, BYTE y, WORD value)
{
	char str[5];
	int i;

	for(i=3; i>=0; i--) {
		str[i] = value % 16;
		str[i] += str[i] < 10 ? '0' : 'A' - 10;
		value /= 16;
	}
	str[4] = NULL;

	printStr(x,y,str);
}

/*
 * clearScreen()
 *  画面を消去する
 */
void clearScreen()
{
	_asm

	ld		a,#0x20
	ld		b,#144
	ld		de,#0
	call	#0xbfee

	_endasm;
}

/*
 * pointSet(WORD x, WORD y, COLOR c)
 *  ドットを表示する
 *  ref: http://orange.kakiko.com/cosmopatrol/more/motto7.html
 */
void pointSet_asm(BYTE x, BYTE y, BYTE c);
void pointSet(WORD x, WORD y, COLOR c)
{
	pointSet_asm((BYTE)x,(BYTE)y,c);
}
void pointSet_asm(BYTE x, BYTE y, BYTE c)
{
	_asm

	push	af

	; 機種判別
	ld		a,(#0x93cd)
	cp		#0xe5
	jr		nz,_s3
	ld		a,#0xcd
	jr		_s4
_s3:
	ld		a,#0xcb

_s4:
	ld		(_n2+1),a

	pop		af

	; hl <- 00 _x
	; de <- 00 _y
	ld		l,4(ix)
	ld		e,5(ix)
	ld		h,#0
	ld		d,h

	; a <- _c
	ld		a,6(ix)

	ld		(#0x7967),hl
	ld		(#0x7969),de
	ld		iy,#0xffff
	ld		(#0x777d),iy
	ld		(#0x777f),a

	call	_n2
	jr		_n3

	; 機種判別で書き換わる。
_n2:
	call	#0x93cd
	.db		#0x0d
	.dw		#0xc595

	ret

_n3:

	_endasm;
}


/*********************************
  入力関係
*********************************/

/*
 * inkeyWait()
 *  キー入力待ち
 */
enum Key inkeyWait()
{
	_asm

	call	#0x0bcfd

	; 戻り値 hl <- a
	ld		l,a
	ld		h,#0

	_endasm;
}

/*
 * inkey()
 *  リアルタイムキー入力
 */
enum Key inkey()
{
	_asm

	call	#0x0be53

	; 戻り値 hl <- a
	ld		l,a
	ld		h,#0

	_endasm;
}


/*********************************
  その他
*********************************/

/*
 * wait(int t)
 *  ウェイトする
 */
void wait(WORD t)
{
	while(t--);
}

使用例

全関数を使ってるわけじゃないですが、こんな感じに簡単にできます。上下左右の方向キーを押すと色を反転させながら線が延びます。SHIFTボタンを押すとゆっくり動きます。

ファイル名: main.c

#include "lib.c"

void main(void)
{

	BYTE x=0,y=0;
	enum Key k;

	clearScreen();
	for(;;) {

		k = inkey();

		switch(k % SHIFT)
		{
		case DOWN:
			y++;
			pointSet(x,y,REVERSE);
			break;
		case UP:
			y--;
			pointSet(x,y,REVERSE);
			break;
		case LEFT:
			x--;
			pointSet(x,y,REVERSE);
			break;
		case RIGHT:
			x++;
			pointSet(x,y,REVERSE);
			break;

		case CLS:
			clearScreen();
			break;

		}

		if(k - SHIFT > 0)
			wait(10000);

	}
}

基本的に関数名に "_asm" と書いてるものは使用しないでください。これは関数側から呼び出すための関数です。*3

コンパイルする前にすること

SDCCのリンクがうまく行ったを見て参考にしてもらえればいいですが、コンパイラのことをわかって無いと解り難いと思うのでこうすれば動く、という方法を書きます。

当然ですが、コンパイラSDCCを用意してください。最新版で結構だと思います。ここでつまづく場合はごめんなさい、なんともいえません・・・。

次にオブジェクトファイルを用意します。次のテキストを mycrt.o というファイル名で作成します。適当なディレクトリに置いてください。例えば C:\z80\ 。lib.c も適当な場所に置いていてください。例えば C:\z80\lib\ 。

ファイル名: mycrt.o

XL
H 1 areas 1 global symbols
S _main Ref0000
A _CODE size 4 flags 0
T 00 00 CD 00 00 C9
R 00 00 00 00 02 03 00 00

実験用コード main.c (上記)を適当なディレクトリに作成してください。例えば C:\z80\main\ 。#include "lib.c" はきちんと lib.c を置いた場所に書き換えてください。

また、次のテキストも同じ場所にファイル名 make.bat で作成してください。ここで注意ですが、2行目の最後、-Wlオプションの \z80\mycrt.o は mycrt.o を作成した場所を指定してください。ここでは C:\z80\ に作成したので \z80\mycrt.o を指定しています。-Wlはオブジェクトファイルをリンクするオプションです。

ファイル名: make.bat

as-z80  -o mycrt.o mycrt.asm
sdcc main.c -mz80 --code-loc 0x0100 --no-std-crt0 -Wl\z80\mycrt.o
pause

これで完了です。あとは make.bat を実行してコンパイルします。コンパイル時に警告が沢山出ますが、性質上仕方ないことなので問題ありません。

エラーが出ている場合は設置方法に問題があると思われます。main.c の #include文に注意。

どうしてもわからない・面倒くさい場合は先述した z80.zip をダウンロードしてください。ZIPファイルなので展開するとディレクトリ構造がそのまま再現されます。C:\直下に設置すると僕の例と同じ環境になります。C:\z80\main\make.bat を実行するとコンパイルされます。

転送

コンパイルに成功すると main.c と同じディレクトリに main.ihx というインテルヘキサ形式のテキストファイルが生成されます。これを PCG-LINK などを使用してポケコンに転送してください。転送時に自動的にポケコン側でメモリに展開されるので、完了するとポケコン側で実行が可能になります。ここで不具合が出た場合は転送方法に問題があると思われます。

参照: くりこうさんの簡易型レベルダウンコンバータ

実行

無事にポケコン側に転送が完了したら実行できます。実行するには BASIC の RUN MODE で CALL &H100 と入力するだけです。MACHINE MONITOR で G100 としても同じです。とにかく100h番地から実行するだけです。

変な動作の場合はオブジェクトファイル(mycrt.o)に問題があるかもしれません。コンパイルに失敗していたのかもしれません。そのあたりを確認してください。

ソースコードについて

ソースコードは自由に使ってください。何かあったらリンクやその他フィードバックがあるとうれしいです。

こちらでもソースコードは随時変更していくつもりです。ややこしいことにならないと思うのでバージョンとかは書いてませんが、一応 ver.0.1 です。というか作りかけなのでなんとも・・・。

終わりに

ターゲットはコアユーザだったんですが、SDCCが案外敷居の低いコンパイラだったのでライトユーザ向けに多少説明をさせてもらいました。

また、ライブラリを公開することで SDCC が少しでも流行らないかなと思った次第です。lib.c をもう少し作りこんで使えるものに仕上げよう!と意気込みもでてきました。意外と簡単に作れるので勝手に拡張して行ってください。

*1:数ヶ月前の話ですが

*2:タブ幅は4なので少し崩れてます

*3:Cの変数とレジスタとのやり取りを行うために使用しています。

2006-05-27

SDCCのリンクがうまく行った

--no-std-crt0オプションを使うとmain関数にジャンプする部分がないので、コンパイルするたびにエントリーポイントを変更しないといけない。main関数の位置はコンパイル時に決定するから。だから、main関数にジャンプするオブジェクトファイルを作って、crtとしてリンクさせることができないかを試した。

アセンブリ mycrt.asm

	.globl	_main

	call	_main
	ret

アセンブル時のコマンドライン

as-z80 -o mycrt.o mycrt.asm

出力されるオブジェクトファイル

XL
H 1 areas 1 global symbols
S _main Ref0000
A _CODE size 4 flags 0
T 00 00 CD 00 00 C9
R 00 00 00 00 02 03 00 00

コンパイル時のコマンドライン

sdcc lib.c -mz80 --code-loc 0x0100 --no-std-crt0 -Wlmycrt.o

-Wlでcrtとしてリンクされているのかどうかは怪しいが。

バッチファイル

これをmake.batとして保存したら無事終了。0x0100から実行が可能となった。これでいつも0x0100にmycrtが展開されているはず。

as-z80  -o mycrt.o mycrt.asm
sdcc lib.c -mz80 --code-loc 0x0100 --no-std-crt0 -Wlmycrt.o

最後に

マニュアル以外にほとんど情報が見つからなかった。SDCCは組み込み用に使う場合はほとんどそのままで問題は起こらないんじゃないかな。根拠はないけど。

これで、思う存分ポケコン用のライブラリが作れそう。

2006-05-26

SDCCのマニュアル読んでcrtのことを知った

前回、Z80でいろいろと実験を行ったが、今回もいろいろと実験を行う。

今回わかったこと

マニュアルにこんな文章を見つけた

3.11.3 Z80 Startup Code

On the Z80 the startup code is inserted by linking with crt0.o which is generated from sdcc/device/lib/z80/crt0.s. If you need a different startup code you can use the compiler option --no-std-crt0 and provide your own crt0.o.

前回、アセンブリソースコードの中に記述されていないコードが吐き出されることについていろいろと考えていたのだが、コンパイル時に--no-std-crt0というオプションを付け加えるだけで解決してしまった・・・。ざっと見ただけだが、crt0.oの中に書かれているのが前回余分にくっついたもののようだった。そして、--no-std-crt0オプションを追加すると、0x0000や0x0100に書かれていたものが一切なくなった。これで、自分で書いた物だけが実行される安心感が得られるよ!

やっぱり、マニュアルは読むものだと思った。

SDCCのインラインアセンブラ機能

SDCCにはインライナセンブラ機能がある。今回はそれをテストしてみる。

使ったコード

int func(int i)
{
	_asm

	ld		hl,_i
	ld		a,(hl)
	inc		a
	ld		(hl),a

	_endasm;

	return i;

}

void main(void)
{

	func(10);

}

引数をインクリメントして返す関数funcをアセンブリを使って書いた。

コマンドライン

sdcc -mz80 --no-std-crt0 test.c

出力されたアセンブリ

;--------------------------------------------------------
; File Created by SDCC : FreeWare ANSI-C Compiler
; Version 2.5.6 # (May 19 2006)
; This file generated Fri May 26 23:25:06 2006
;--------------------------------------------------------
	.module test
	.optsdcc -mz80
	
;--------------------------------------------------------
; Public variables in this module
;--------------------------------------------------------
	.globl _main
	.globl _func
;--------------------------------------------------------
; special function registers
;--------------------------------------------------------
;--------------------------------------------------------
;  ram data
;--------------------------------------------------------
	.area _DATA
;--------------------------------------------------------
; overlayable items in  ram 
;--------------------------------------------------------
	.area _OVERLAY
;--------------------------------------------------------
; external initialized ram data
;--------------------------------------------------------
;--------------------------------------------------------
; global & static initialisations
;--------------------------------------------------------
	.area _HOME
	.area _GSINIT
	.area _GSFINAL
	.area _GSINIT
;--------------------------------------------------------
; Home
;--------------------------------------------------------
	.area _HOME
	.area _CODE
;--------------------------------------------------------
; code
;--------------------------------------------------------
	.area _CODE
;test.c:2: int func(int i)
;	genLabel
;	genFunction
;	---------------------------------
; Function func
; ---------------------------------
_func_start::
_func:
	push	ix
	ld	ix,#0
	add	ix,sp
;test.c:11: _endasm;
;	genInline
	
	
		       ld hl,_i
		       ld a,(hl)
		       inc a
		       ld (hl),a
	
		       
;test.c:13: return i;
;	genRet
;	AOP_STK for 
; Dump of IC_LEFT: type AOP_STK size 2
;	 aop_stk 4
	ld	l,4(ix)
	ld	h,5(ix)
;	genLabel
00101$:
;	genEndFunction
	pop	ix
	ret
_func_end::
;test.c:17: void main(void)
;	genLabel
;	genFunction
;	---------------------------------
; Function main
; ---------------------------------
_main_start::
_main:
;test.c:20: func(10);
;	genIpush
; _saveRegsForCall: sendSetSize: 0 deInUse: 0 bcInUse: 0 deSending: 0
	ld	hl,#0x000A
	push	hl
;	genCall
	call	_func
	pop	af
;	genLabel
00101$:
;	genEndFunction
	ret
_main_end::
	.area _CODE

エラー内容

test.asm:59: Error: <a> machine specific addressing or addressing mode error

エラーの場所はここ

;	---------------------------------
; Function func
; ---------------------------------
_func_start::
_func:
	push	ix
	ld	ix,#0
	add	ix,sp
;test.c:11: _endasm;
;	genInline
	
	
		       ld hl,_i  ; ***ここ***
		       ld a,(hl)
		       inc a
		       ld (hl),a
	
		       
;test.c:13: return i;
;	genRet
;	AOP_STK for 
; Dump of IC_LEFT: type AOP_STK size 2
;	 aop_stk 4
	ld	l,4(ix)
	ld	h,5(ix)
;	genLabel
00101$:
;	genEndFunction
	pop	ix
	ret
_func_end::
;test.c:17: void main(void)
;	genLabel
;	genFunction

このエラーから考えて、_iが参照できないようだ。出力されたアセンブリにも_iは宣言されてない。他にもそれらしい宣言が見当たらない。引数はどこで見ればいいのだろう。

関数が呼び出されるとき、

	ld	hl,#0x000A
	push	hl
	call	_func

とhlに値を入れてプッシュしている。そして、関数側では、

	push	ix
	ld	ix,#0
	add	ix,sp

と、ixをプッシュで一時退避させてspを入れている。spはスタックポインタなので、引数はixでスタックを見ろということのようだ。つまり、iを参照したければixで2つ前のスタックを参照したらいいようだ。

ためしにアセンブリの箇所をCで書いてみた。

int func(int i)
{
	i++;
	return i;
}

すると以下のようなコードが出力された(i++の部分だけ抜粋)。

;test.c:14: i++;
;	genAssign
;	AOP_STK for 
	ld	c,4(ix)
	ld	b,5(ix)
;	genPlus
;	AOP_STK for 
;	genPlusIncr
;	Can't optimise plus by inc, falling back to the normal way
	ld	a,c
	add	a,#0x01
	ld	4(ix),a
	ld	a,b
	adc	a,#0x00
	ld	5(ix),a

ここで肝は

	ld	c,4(ix)
	ld	b,5(ix)

のようだ。また慣れない記述だ。使っていたポケコンのアセンブラではLD C,(IX+4)などと書いていたのであれっと思う。それはさておき、cに4つずらしたところを入れて、bに5ずらしたところを入れている。つまり、dcレジスタにスタックの前の前のデータを入れているということ。変数が2個以上の時も調べたが、第1引数から順に4 5, 6 7, 8 9・・・となっている。これでようやく引数が使えるようになった!

また、

;test.c:13: return i;
;	genRet
;	AOP_STK for 
; Dump of IC_LEFT: type AOP_STK size 2
;	 aop_stk 4
	ld	l,4(ix)
	ld	h,5(ix)

からわかるように、戻り値はhlに入れればいいみたいだ。まあ、これは全部int型の話で、他の型まで調べてませんが。必要になったらやればいいし。

じゃあ次は関数内で使われる変数を読み取る方法も考えないといけない。_varが使えないからだ。

_varが使えることを前提に書いたコード

void main(void)
{
	int i = 123;

	_asm

	inc	(_i)

	_endasm;

}

つまり、i++をしたいということだ。

実現方法の考察

iはint型(恐らく2バイト)の変数なので、hlやbcなどに入れるか、さっきみたいにpushしてixで読む方法が考えられる。しかし、Cでiの数値をレジスタに入れる方法がなければ実現不可能だ。

で、いろいろ調べたが結局見つからなかった。ただ、回避策を見つけた。これは簡単なことで、関数の引数は読めるんだから、アセンブリ部分はまるまる関数化したらいいということ。だから、この場合ははじめに作ったfunc関数を使えということ。面倒くさいがこれで納得。

あとがき

実は、このSDCCは3年前(高3)にも一度使ったことがあるのだが、あんまり踏み込んでいろいろとしなかったのでSDCCの存在をすっかり忘れていた。今回がきっかけでかなり遊べそうな予感がする。

それでは。

2006-05-22

Z80用Cコンパイラ(SDCC)のテスト

以下のソースをSDCCでコンパイルした時の出力など

環境

WindowsXP

SDCC 2.5.6

Cのソースコード

何もしないシンプルなコードでテスト。

void main(void)
{
}

コマンドライン

sdcc -mz80 test.c

出力されるアセンブリ

;--------------------------------------------------------
; File Created by SDCC : FreeWare ANSI-C Compiler
; Version 2.5.6 # (May 19 2006)
; This file generated Mon May 22 19:03:36 2006
;--------------------------------------------------------
	.module test
	.optsdcc -mz80
	
;--------------------------------------------------------
; Public variables in this module
;--------------------------------------------------------
	.globl _main
;--------------------------------------------------------
; special function registers
;--------------------------------------------------------
;--------------------------------------------------------
;  ram data
;--------------------------------------------------------
	.area _DATA
;--------------------------------------------------------
; overlayable items in  ram 
;--------------------------------------------------------
	.area _OVERLAY
;--------------------------------------------------------
; external initialized ram data
;--------------------------------------------------------
;--------------------------------------------------------
; global & static initialisations
;--------------------------------------------------------
	.area _HOME
	.area _GSINIT
	.area _GSFINAL
	.area _GSINIT
;--------------------------------------------------------
; Home
;--------------------------------------------------------
	.area _HOME
	.area _CODE
;--------------------------------------------------------
; code
;--------------------------------------------------------
	.area _CODE
;test.c:1: void main(void)
;	genLabel
;	genFunction
;	---------------------------------
; Function main
; ---------------------------------
_main_start::
_main:
;test.c:3: }
;	genLabel
00101$:
;	genEndFunction
	ret
_main_end::
	.area _CODE

出力されるインテルヘキサ

:03000000C3000139
:02000800ED4DBC
:02001000ED4DB4
:02001800ED4DAC
:02002000ED4DA4
:02002800ED4D9C
:02003000ED4D94
:02003800ED4D8C
:0C01000031FFFFCD0B02CD0A02C3040248
:0A0200003E02CFC93E00CF7618FD84
:01020B00C929
:01020A00C92A
:00000001FF

インテルヘキサを元に必要な箇所を手動逆アセンブラを行った結果

.ORG	#0x0000
JP	#0x0100

.ORG	#0x0008
RETI

.ORG	#0x0100
LD	SP,#0xFFFF
CALL	#0x020B
CALL	#0x020A
JP	#0x0204

; メイン --------

; 終了処理
.ORG	#0x0204
LD	A,0
RST	#0x08
RET

; メイン処理
.ORG	#0x020A
RET

; 不明
.ORG	#0x020B
RET

.ORG命令は配置する場所で、#0x????は16進数の表記(主にアドレスを指定)。

考察

ソースコードはシンプルなのだが、結構いろいろとやっているようだ。0x020AはCで書いた処理内容だと思われる。0x020Bは不明。最後に呼び出される0x0204はRST 0x08で、その先はRETI。割り込み許可をするためだと思うが、割り込んでいないにRETIが呼び出された場合の動作ってどうなるんだったかな。普通にRETと同じになるのかな?そしてHALTで終了している。あんまりうれしくない終わり方だ。

で、厄介なのは出力されたアセンブリソースにはそんなコードは一切見当たらないことである。アセンブリソースに無いものが出力されるのは大変気持ちが悪い!他のコンパイラをこうやってマシン語レベルで確認したためしはほとんどないので普通のことだったらorzです。

最終的にはSHARPのPC-G850S(ポケコン)で動作させることができるコードが書きたいのだが、そのポケコンは0x0100からの書き込みだけ許可されており(例外あり)上記のコードから出力されるものを0x0000から0x0100にずらす必要がある。これは0x0000から書き込みが行われるとポケコンとしてはまずいからだ。

sdcc -mz80 --code-loc 0x0100 test.c

とすることでずらすことができると思ったのだが、ずれたのは初期設定では0x0200であった位置が0x0100になったことだ。下のインテルヘキサは--code-loc 0x0100を付け加えたときの出力。

:03000000C3000139
:02000800ED4DBC
:02001000ED4DB4
:02001800ED4DAC
:02002000ED4DA4
:02002800ED4D9C
:02003000ED4D94
:02003800ED4D8C
:0C01000031FFFFCD0B01CD0A01C304014B
:0A0100003E02CFC93E00CF7618FD85 ;この行が0x0100への書き込みになった。
:01010B00C92A
:01010A00C92B
:00000001FF

0x0200に書き込みされていたものが0x0100になっている。他にもジャンプ先が0x0100になったりしている。ただし、上記の注釈の一つ上の行と書き込み開始箇所(0x0100)がかぶってしまうのであまりよろしくない(笑)

これでわかったが、逆アセンブラ結果で「メイン」と注釈を書いた場所が本来のコードのスタート地点であることがわかった。0x0000からコードが存在する理由は0x0000から実行しても動作するようにするためだろう。Z80は起動直後のプログラムカウンタは0x0000を指しているからだ。だが、これは先述したようにポケコン的にまずい。

ちなみに、はじめに出したインテルヘキサの0x0200からの4バイト(3E 02 CF C9)を逆アセンブリすると

LD	A,2
RST	#0x08
RET

となる。普通に0x0200から実行するとAレジスタに2が入って終了してしまう。なんじゃこりゃ。

もしポケコンで動作させたいときは--code-locオプションを指定せずにコンパイルし、出力されたインテルヘキサは上8行をざっくりと取り除いてポケコンへ転送後、0x0100から実行すれば問題はなさそうである。はたして・・・。

思いっきり忘れていたが、LD SP,#0xFFFFもよろしくない。0xFFFFは書き込み不可のROMだからスタックは使えないよー。これどうしましょう。

感想

今日、2chのポケコンスレで紹介されたSDCCというのを発見し、早速触ってみた次第。

アセンブリの文法がめちゃくちゃ見難いと感じた。命令に関してはザイログのニモニックは使えるものの、SDCCは他にもさまざまなCPU用にコンパイルできる汎用的なコンパイラなので、16進表記などの文法がインテルっぽいのかも。僕の知ってるものと違って戸惑った。今回はなれるためにSDCC用の文法で書いてみた。ちょっと間違ってるかもしれないが・・・。

それから、--code-locオプションを指定せずにコンパイルするとメインコードは0x0200から書き込まれるが、これはメモリの無駄遣いのようにも思える。SDCCのマニュアルをもう少し詳しく読んでいく必要がありそうである。

明日は大学の授業で朝から必出席の実験があるため、今日はこの辺で切り上げた方がよさそうだ。