主にMSX関係のコンテンツが置いてあります。

1セクタゲーム作成に挑戦-4

しかしまぁ、大体予想はついていました。

DOSのプログラムなので実行時のページ0,1はRAM。コードを短くするにはBIOSを積極的に利用したいところですが、そうするとインタースロットコールでスロットを切り替えながらBIOSを呼び出す必要があり、オーバーヘッドが大きいです。

今回はファイルを実行時に自分自身を高位アドレスに転送して、ページ0,1をメインROMに切り替え実行するようにしてみます。

まずは、これだけでどれくらい速くなるのか。

ソース(クリックで展開)
; 1secgame

startadd: equ 9000h
org startadd

;ワークエリア-------------------------------------------------------------------
exptbl: equ 0fcc1h ;main romのスロット
forclr: equ 0f3d9h ;前景色
bakclr: equ 0f3eah ;背景色
bdrclr: equ 0f3ebh ;周辺色

;bios---------------------------------------------------------------------------
calslt: equ 001ch ;スロットの指定アドレスをコール iy,ix
chgmod: equ 005fh ;スクリーンモード変更
;a=モード
;forclr=前景色
;bakclr=背景色
;bdrclr=周辺色
filvrm: equ 0056h ;vramを埋める hl=アドレスbc=長さa=データ
setwrt: equ 0053h ;vramアドレスセット hl=アドレス
wrtvrm: equ 004dh ;vram書き込み hl=アドレス a=データ
rdslt: equ 000ch ;スロットの指定アドレスを読む
;hl=アドレス a=読み込み値
enaslt: equ 0024h ;スロット切り替え
;-------------------------------------------------------------------------------
offsets: ;高位アドレスへ転送して開始する
start: ld hl,codestart - startadd + 100h;
ld de,startadd + (offsete - offsets)
ld bc,codeend - codestart
ldir
jp startadd + (offsete - offsets)
offsete:
;プログラムのスタート
codestart: ;main rom スロットの切り換え
ld a,(exptbl)
ld hl,0
call enaslt
ld h,01000000b
call enaslt

ld a,15
ld (forclr),a
ld a,12
ld (bdrclr),a
ld a,1
ld (bakclr),a
call chgmod ;screen1:color15,1,12

;vramクリア
ld hl,0
ld bc,25 * 8
xor a
call filvrm ;vram0クリア

;vramパターンセット
;緩衝キャラクタ定義
ld hl,0010h
ld b,1
call setchrm

;障害物定義
ld hl,0088h
ld b,8
call setchrm

;ループ障害物定義
ld hl,0048h
ld a,8
l2: push af

ld de,bgpatn
ld c,8

lcnt: ld b,1
ld a,(de)
l0: djnz l1
jr l3
l1: rlca
jr l0
l3: call wrtvrm
inc hl
inc de
dec c
jr nz,lcnt
ld a,(lcnt+1)
inc a
ld (lcnt+1),a

pop af
dec a
jr nz,l2

;マップデータ作成
ld hl,0
ld de,map
maps2: ld a,(hl)
inc hl
cp 40
jr nc,mapsp ;空白設定
;障害物設定
ld a,(hl)
inc hl
and 00011111b ;a=ループキャラクタ数
ex de,hl ;hl=マップアドレス de=読み込みアドレス
ld (hl),1 ;緩衝キャラクタ
inc hl
maps1: or a
jr z,maps0 ;障害物
dec a
ld (hl),9 ;ループキャラクタ
inc hl
jr maps1
mapsp: ld a,(hl)
inc hl
and 00011111b
ex de,hl
mapsp0: ld (hl),0
inc hl
or a
jr z,maps3
dec a
jr mapsp0

maps0: ld (hl),11h ;障害物
inc hl
maps3: ex de,hl ;hl=読み込みアドレス, de=マップアドレス
push hl
ld hl,map + 1000h
or a
sbc hl,de
pop hl
jr nc,maps2

ld hl,map
ld (scpoint),hl
xor a
ld (dotpoint),a
;ここからサンプル動作-----------------------------------------------------------
ld b,255
ml1: push bc

ld de,(scpoint)
ld a,(dotpoint)
ld c,a
ld b,8
ml0: push bc
push de
call putsc
pop de
pop bc
inc c
djnz ml0
ld de,(scpoint)
inc de
ld (scpoint),de
pop bc
djnz ml1
ret
;終了---------------------------------------------------------------------------

putsc: ;画面表示
;de=スクロールアドレス
;c=ドットポイント
ld a,24
ld (putsc1+1),a

ld hl,1800h
putsc2: ld b,32
putsc0: ld a,(de)
or a
jr z,putsc3
add a,c
putsc3: call wrtvrm
inc hl
inc de
djnz putsc0

ex de,hl ;hl=スクロールアドレス
push de
ld de,map + 1000h
or a
sbc hl,de
jr c,putsc4

ld de,map
putsc4: add hl,de
pop de
ex de,hl

putsc1: ld a,0
dec a
ld (putsc1+1),a
jr nz,putsc2
ret

setchrm: ;キャrクタデータセットサブルーチン1
;hl=vram開始アドレス
;b=シフト数,c=転送バイト数
ld c,8
setchrm0: push bc
call setchr
pop bc
inc b
dec c
jr nz,setchrm0
ret

setchr: ;キャラクタデータセットサブルーチン2
;hl=vram開始アドレス
;b=左シフト回数(1以上)
ld de,bgpatn
ld c,8
setchrl1: push bc ;b=シフト数,c=転送バイト数
;データ作成開始
ld c,0
ld a,(de)
setchrl0:
or a
rla
rl c
djnz setchrl0

ld a,c
call wrtvrm
inc hl
inc de
pop bc ;シフト数転送バイト数復活
dec c
jr nz,setchrl1:
ret

;障害物グラフィックパターン
bgpatn: db 3ch,42h,8dh,85h,0c1h,0b1h,42h,3ch
scpoint: dw 0
dotpoint: db 0

map: equ 0a000h

codeend:

このコード、何が大変だったかと言えば転送によるアドレスのずれです(ソースコード記述上の)。

使っているアセンブラは単純にorgでファイル実行アドレスの100hと転送先の9000hを同居させると、間を00hで埋めた32KiB超のバイナリを作るので工夫が必要でした。

本筋以外で面倒だったこのコード、実行速度はどんなものでしょうか。

お、確かに速くなりました。

…が、まだ遅いです。まぁもう少し高速化しようと思えば、高速転送用BIOSを使うとか、そもそもBIOSを使わず自前でルーチンを組むとかありますが、これだけで311バイト。

ちょっと余裕がなさすぎます。こっち方面を頑張ってもダメそうですね。方針転換が必要かもしれません。

512バイト制限って、1画面プログラムよりキツイ気がしてきました。