16KB ROMイメージの作成

ROMカートリッジ用ソフトをsdccでビルドする事は可能。
既に前任者によって書かれている内容もあるが、
完全に動作するものではないので、改めて説明する。

ここではsdccを使ってゲームアプリケーションを作る際のROMカートリッジ用のソフトを
作成する例を示す(基本的な情報は技術情報として広く知られている)。

ROMカートリッジは大きく分けて二種類あり、16/32KB容量のものと、
MEGAROMと呼ばれるタイプがある。
前者の小さな容量のROMカートリッジはスロットと呼ばれるメモリアドレス管理
のみ使うタイプなのでメモリ容量は小さい。
後者のメガROMはメモリアドレスの拡張が行なわれている。

今回は欲を出さず16KB容量のROMカートリッジ用のビルドを説明する。
この方法を使えば32KBも対応可能(テストはCBIOS Ver0.23で行なった)

ROMカートリッジは本体のスロット1/2に接続するが、ページ0と3は
システムで予約されているので使う事が出来ない。
ページ0はMAIN BIOS、ページ3はRAM(workarea)が使用している。
その結果ページ1,2に外部ROMが接続される形となり、最大で32KBの容量となる。
これが基本的なメモリマップとなる。
(メガROMの場合は32KB分のページを外部メモリと切り替えて使う事になるようだが詳細は判らない)

ROMから起動するために必要なことは、ROMに書き込むヘッダコード。
以下に簡単なアセンブラの初期化コードを示す。

start.asm
;
;	CARTRIDGE HEADER (* 16/32KB ROM, PAGEADDR 1-2, 0x4000-0xC000)
;
;	0,	0x0-0x3FFF,	MAINROM
;	1,	0x4000-0x7FFF,	EXT ROM *
;	2,	0x8000-0xBFFF,	EXT ROM *
;	3,	0xC000-0xFFFF,	RAM(WORK AREA)
;
	.module	start
	.globl	_main
;
	.area	_ROM_HDR (ABS)
	.org	0x4000
;
	.db	'A
	.db	'B
	.dw	_main
	.dw	0
	.dw	0
	.dw	0
	.dw	0
	.dw	0
	.dw	0
;
	.ascii	"END ROMHEADER"
;

アセンブル手順は以下

> sdasz80 -o start.rel start.asm

スタートアップコードは初期化コードを含まない。リセット後に直接
ROMイメージから起動し,C言語のmain関数にジャンプする。
このスタートアップルーチンは16/32KB兼用。
16KBのROMの場合は初期化を必要としないので簡単。


次にBIOSのchputを使うCのインターフェース関数を作る。
この関数は動作テスト用として使う。

biosutil.asm
;
;	BIOS UTILS FUNCTION
;
	.area	_CODE
;
;	function void chput(char)
;
_bios_chput::
	ld	hl,#2
	add	hl,sp
;
	ld	a,(hl)		; char -> [A]
;
	call	0x00a2		; bios chput
;
	ret

アセンブル手順は以下

> sdasz80 -o biosutil.rel biosutil.asm



つぎにC言語でmain関数を書く。
ここにプログラム本体を書く。

main.c
// ROM cartridge 16/32KB
// 32k,CODE=0x4100-0xBFFF,DATA=0xC000,STACK=
// 16k,CODE-0x4100-0x7FFF,DATA=0xC000,STACK=
 
//prototypes
extern void bios_chput(unsigned char);
int puts(char *s);

//program main
void main(void){

	bios_chput('\n');

	puts("hello world!!");

	while(1);
}

//bios puts 
int puts (char *s){
	int i = 0;
	while (*s){
		bios_chput(*s++);
		i++;
	}
	bios_chput(0xd);	//CR
	bios_chput(0xa);	//LF
	return i+1;
}

コンパイル手順は以下

> sdcc -mz80 -c main.c


最後に全てのオブジェクトコードをリンカで結合してバイナリを作る。

> sdld -b _CODE=0x4100 -b _DATA=0xC000 -i test.ihx main.rel biosutil.rel start.rel

ROMカートリッジはページ1から始まるのでコードアドレスを_CODE=0x4100とする。
データ領域は通常RAMに割り当てられるので、ワークエリアとして使われる
ページ3先頭の_DATA=0xC000とする。
必要であればC標準関数を使っていれば-l z80.libなどしてライブラリをリンクする。


以上で作成されたIntel HEX形式をバイナリ形式に変換するためにmakebinを使う。

> makebin -s 65536 test.ihx > test.bin


ここからROM領域の0x4000-0xBFFFまでのバイナリ領域を切り出す為にPerlスクリプトで編集する。

> perl romout.pl test.bin > test.rom

以上で作成されたtest.romというファイルがROMイメージ。
エミュレータを使ってROMイメージを起動することが可能で、実際に動作させることができる。
成功すれば画面に"hello world!!"と表示されるはずだ。


Perlスクリプトは基本的にMSXDOS用のバイナリ生成時に使ったものと同一だがROMイメージ用に若干修正している。

romout.pl
$fname=@ARGV[0];
open(IN,$fname);

binmode(IN);

$i=0;
while(eof(IN) == 0){
	read(IN,$buf,1);

	if($i<=0x3fff || $i>=0xc000){
	} else {
		print $buf;
	}
	$i++;
}

close(IN);

  • 32KB ROMの作成

32KB用のROMカートリッジはページ1,2が外部ROMとなるようにスロットページが切り替わる。
ブート直後は0x4000-0x7FFFのページ1のみしか切り替わらないので、初期化の際に
ページ2(0x8000-0xBFFF)を外部ROMを参照するように切り替える必要がある。

そのためのスロット切り替え関数を示す。
以下のC関数ライブラリはBIOSのスロット切り替え機能を利用している。

slotutil.asm
;
;	SLOT MEMORY MANAGE FUNCTION
;
	.area	_CODE
;
;	READ SLOT REGISTER
;	rtn=chkprislot()
_chkprislot::
	push	af		;backup registerfile
;
	call	0x0138		;call bios RSLREG(0x0138)
;
	ld	l,a		;return value
	ld	h,#0
;
	pop	af
;
	ret
;
;	ENABLE SLOT 
;	void chgpageaddr(pageaddress, primaryslotnumber)
_chgpageaddr::
	push	bc
;
	ld	hl,#4
	add	hl,sp
;
	ld	c,(hl)		;page_addr -> [BC] -> [HL]
	inc	hl
	ld	b,(hl)
	push	bc
;
	inc	hl		;pri_slot -> [A]
	ld	a,(hl)
;
	pop	hl
;
	push	af		;backup registerfile
	push	bc
	push	de
	push	hl
	push	ix
	push	iy
;
	call	0x0024		;call bios ENASLT(0x0024)
;
	pop	iy		;restore registers
	pop	ix
	pop	hl
	pop	de
	pop	bc
	pop	af
;
	pop	bc
;
	ei			;enable interrupt
	ret

次に32KB容量のROMを使う場合のC言語を示す。
公開されている資料では起動ROMヘッダ部分でアセンブラでスロット切り替えBIOSを呼び
スロットメモリアドレスを切替えているが、この例では初期化は
アセンブラを使わずC言語の関数で実装している。

main2.c
//prototypes
extern unsigned char chkprislot(void);
extern void chgpageaddr(unsigned int pageaddr, unsigned char prislot);
extern void bios_chput(unsigned char );
int puts(char *s);
void init_32krom(void);

//program main
void main(void){
	unsigned int i;
	unsigned int j;

	puts("ROM header ok.");
	puts("16k ROM boot ok.");
		
	init_32krom();

	puts("32k ROM init ok.");
	puts("start main::");

	while(1){
		for(i=0; i<20; i++){
			bios_chput('*');
			for(j=0; j<10000; j++);
		}
		bios_chput(0xd);
		for(i=0; i<20; i++){
			bios_chput(' ');
			for(j=0; j<10000; j++);
		}
		bios_chput(0xd);
	}
}

void init_32krom(void){
	unsigned char rtn;
	unsigned char slotnum;

	//check slot number
	rtn=chkprislot();
	slotnum=(rtn >> 2) & 3;

	//primary SLOT change of the PAGE address
	//(page2,0x8000-0xBFFF,PriSLOT 3 ==> PriSLOT 1 or 2 externalrom)
	chgpageaddr(0x8000,slotnum);
}

int puts (char *s){
	int i = 0;
	while (*s){
		bios_chput(*s++);
		i++;
	}
	bios_chput(0xd);	//CR
	bios_chput(0xa);	//LF
	return i+1;
}

BIOS機能を利用する関数ライブラリを作れば簡単なゲーム作成は可能だろう。
メガROMは各社仕様が異なり、コピー防止機能を含んでいるという側面もあり不明な点が多い。

実習として簡単なROM用ソフトを作るのであれば32KB容量でも問題はないだろう。
必要であれば外部記憶を用意してデータストレージとすればよい。

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2012年06月02日 12:06
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。