openMSXで、V9990に画像を出力してみる
V9990のエミュレーションが組み込まれたMSXエミュレーター
オープンソースのMSXエミュレーター、openMSXには、各種拡張カートリッジのエミュレーターが予め用意されている。その中の、V9990搭載グラフィックカード、GFX9000のエクステンションを有効にすることで、V9990対応ソフトを動かすことができる。
msx-sdcc@Wikiに簡単な解説とサンプルコードがあったので、ありがたくパク…参考にさせてもらうことにする。
www28.atwiki.jpwww28.atwiki.jp
512x424ドット、32768色モードを使いたい
ただ、これそのままやっても面白くないので、画面モードを512x424のインターレースモードで高解像度の画像表示に挑戦してみた。なにしろ256x212ではいまどきかなり凸凹して見えてしまうので…
で、実際画面モードを希望のものにするにはどうしたらいいかとなると、上記サイトの情報だけでは全く足りないので。
MSX Banzai! - The MSX Worshipping Shrine - V9990 Programmers Manual
をチェック。うーん、MSX2 テクニカルハンドブックを思い出す詳しい説明だ。ただ問題は英語だということなのだけど…
どうやら、V9990の場合は、インターレースモードで2プレーンに分けて描画する必要はなく、普通の縦424ピクセルの画像としてVRAMに上から書き込んでいけばうまいことインターレース表示してくれるらしいので、そのようにする。
以前の記事で書いた、z88dkを使用し、以下のようなコードを書く。
juangotoh.hatenablog.com
#include <stdio.h> unsigned char inp(unsigned char addr); void outp(unsigned char addr, unsigned char data); void V9990Reg(unsigned char reg, unsigned char data); void V9990Memadr(unsigned long addr,unsigned char rw); unsigned char inp(unsigned char addr) { #asm ld hl,2 add hl,sp ;skip return address ld c,(hl) ; c=addr (LSB) in a,(c) ld h,0 ld l,a ; hl = return parameter #endasm } void outp(unsigned char addr, unsigned char data) { #asm ld hl,2 add hl,sp ld a,(hl) ;a=data inc hl inc hl ld c,(hl) ;c=addr out (c),a #endasm } void V9990Reg(unsigned char reg, unsigned char data){ outp(0x64,reg); outp(0x63,data); } void V9990Memadr(unsigned long addr,unsigned char rw){ unsigned char hi,mi,lo; lo=addr & 0xff; mi=(addr >> 8) & 0x3f; hi=(addr >> 14) & 0x7; if (rw) { /* R#3 VRAM READ ADDRESS REGISTER */ outp(0x64,0x3); }else{ /* R#0 VRAM WRITE ADDRESS REGISTER */ outp(0x64,0x0); } /* SET ADDRESS */ outp(0x63,lo); outp(0x63,mi); outp(0x63,hi); } void main(void){ long i; unsigned char col[3]; FILE *fp; int c; int cIndex=0; // R#6 SCREEN MODE 512x424 16bit/pixel V9990Reg(0x6,0x97); // R#7 ENABLE INTERLACE V9990Reg(0x7,0x85); // R#8 ENABLE SCREEN V9990Reg(0x8,0x82); // SET VRAM ADDRESS(r=0,w=1) V9990Memadr(0x0,1); if ((fp = fopen("A004.RAW", "rb")) == NULL ) { //puts("file not found\n"); return 1; } fseek(fp,0,SEEK_SET); while((c=fgetc(fp)) !=EOF){ unsigned char d1,d2,tmp; if (feof(fp)) break; col[cIndex]=c; cIndex++; if (cIndex >2){ // col[0]=R col[1]=G col[2]=B cIndex =0; d2 = (col[1] >>1) & 0x7c; tmp=col[0] >>6; d2 |= tmp; d1 = ((col[0] <<2) & 0xE0) | (col[2] >>3); outp(0x60,d1); outp(0x60,d2); } } fclose(fp); }
これをMSX-DOS用にコンパイルし、できたコマンドと同じディレクトリに"A004.RAW"という画像ファイルを置く。
これはあらかじめPhotoshopで512x424ピクセルにした、RGB各8bit、つまり24bitカラーのRAW画像だ。RAW画像というのは、ひたすらピクセルデータが左上から右下まで隙間なく並んだ、全く圧縮されておらず、一切メタデータを含まない画像で、普通こんなものを使う用途はあまりない。なにしろ画像ファイルに縦横のドット数とかRGBの並び順とかの、表示に必要な情報が一切含まれていない。ただ、予め必要な情報を自分が持っているなら、プログラムから読み出すのに一番簡単なフォーマットである。
V9990の16bitカラーモードは、1ピクセルに2バイト使用し、RGB各5bitとなっており、その並びは
0GGGGGRR RRRBBBBB
となっている。リトルエンディアンで格納されるので、RRRBBBBBの方を先に書き込む必要がある。もとのRAW画像は、
RRRRRRRR GGGGGGGG BBBBBBBB
の順で並んでいるので、3バイト読み込んでは、下位3ビットをクリアし、左右にずらした上で合成し、2バイトにまとめなないといけないのでちょっとだけめんどい。
結果
実行してみると、まあ呆れるほど遅い。そりゃ1バイトずつ愚直に読み込んでは表示してるんだから遅いわなあ。結果が以下の画像。
あー、縦方向の解像度全く出てない。もちろん1/60秒ごとにチラチラ切り替わる画面をスクリーンショットでうまく取り込めないという問題はあるのだけど、これ実際見てるとわかるのだけど、openMSXのインターレースモードって、エミュレーターの画面上で全く同じ位置に奇数ラインと偶数ラインが描かれてて、それを切り換え表示してる感じなんだよね。本来テレビのインターレースモードは、走査線の奇数偶数で上下にずれて隙間を埋めてるから、それで解像度上がるのだけど、同じ位置で混ざって表示されるんじゃインターレース使う意味がない。実機とエミュレーターの違いが出てしまった感じかあ。
まあとにかく、32,768色、横512ドットモードの画面のサンプルということで。
余談
V9990、640x400とか、640x480とかの画面モードがあるのだけど、これらのモードだと最大16色しか出ないんだよね。時代的にMS-DOSで動くPC-9801シリーズのアプリが移植できればOK的なモードだったのかなあ。