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カートリッジはページ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容量でも問題はないだろう。
必要であれば外部記憶を用意してデータストレージとすればよい。
最終更新:2012年06月02日 12:06