PC-1600KでBad Apple!!

このところ体調が良くなかったりして集中力がゼロ…。

よーしやるぞ!…がなかなか出来ない状況の中でも何かやりたい(^^;

こういう時にはチマチマしたデータを加工するプログラミングが捗りますw

そんなコトない?? 

 

まずはこれは見て欲しい。

これはPC-1600Kで「Bad Apple!!」の動画(元から影絵)を表示させてみたものだ。

今回はこれの解説をしてみたい。

あんまり面白い内容じゃないかも(^^;;

 

PC-1600Kのメモリを使い切ってみたい!

やっぱレトロやってると空きスロットは埋めたくなりますよね!

私は使う使わないは別にしても、埋めるチャンスがあるのならば埋めますw

f:id:PocketGriffon:20210221201233j:plain

我が家のPC-1600Kさんには増設メモリ(CE-1600M:32KB×2)を取り付けてある。最初は辞書ROM(CE-1650M)が付いてたんだけど、プログラム開発には何も使わない事に気が付いて、それ以降は増設メモリだけを取り付けるようになった。

でも……せっかく取り付けても使う機会が無かったんですよね…。まさかBASICでそんなでっかいプログラムを組む事も無いし…。

 

開発環境も整った事だから、何かメモリを埋め尽くすような「なにか」をしてみたい!

そんな事を漠然と考えながらアイディアを探していた。

そもそも、PC-1600Kの画面(156×32ドット白黒)で何かをやるにしても、世の中で参考に出来るものが少ない。他のポケコンシューティングゲームとか凄いなぁ…とか思うけれども、あれを移植する気には到底なれない(^^;;

 

何気に見ていたyoutubeで出てきたBad Apple!!というPV。実は何度となく名前は見たことがあったけれども、動画を見るのは初めて!なるほど……そうね、これをPC-1600Kで動かしたら…と頭によぎってしまうのには時間が掛からなかったw

拡張メモリの64KBにどのくらい入るのか分からないけれども、頑張ってみるには楽しい題材だと思った!

 

検討!

そもそも…こんな大きくて長い動画、どうなってるんだろう…と思って探してみたところ、mp4ファイルが存在する事が分かった。調べてみると、動画の画像サイズは512x384ドットのグレイスケールだった。

PC-1600Kで表示出来るのは156x32ドット、常識的に考えてもそのまま綺麗に変換という感じにはならないだろう。

まずはこのオリジナルの画面サイズを小さくしてみて、見るに堪えられるかどうかを検討してみる。

ffmpegというコマンドを使えば、アスペクト比を固定したまま画像サイズが変更出来る。

 

さっそくやってみたが……縦サイズを32ドットにしようとしたところ、横方向が割り切れない(アスペクトを維持したまま変換出来ない)というエラーが出てしまった。そうか、縦も横も綺麗に割り切れる数で変換してやらないといけないのか…そりゃそうだ。

 

そこで、縦サイズを64ドットにして変換してみた。この場合、縦サイズは48ドットとなりPC-1600Kではそのまま表示出来ない。でもまぁまずは変換してみる事が大切だ。

f:id:PocketGriffon:20210221204815p:plain

うーん…見られなくはないけど、相当厳しいかも?でもこのくらいが現実的だろう。

で、表示仕切れない部分については、上下8ドットずつ削る事にした!単純明快w

 

表示するサイズは横64ドット、縦32ドットと決めた!

PC-1600Kの画像は縦8ドットで1バイトの構成。という事は64x4バイトで1つの画像に256バイトが必要となる。64KBのメモリで256枚の画像。これを高速に切り替えてやればアニメーションに見える…という算段だ。

だがまてよ……オリジナルのデータは秒間30フレームで作られているっぽい。という事は256枚という最初の8秒だけ表示されるって事なのか…。うーんさすがに少ない??圧縮してもう少し入るように頑張ってみよう!

 

データ抽出の旅!

mp4フォーマットのままでは加工がしづらいので、扱いなれたjpegに変換する。

  ffmpeg -i movie -f badapple.mp4 data/%04d.jpg

これでフレームごとにデータを出力する事が出来る。

そしたら…なんと6566ファイル出力された!!

これを…うまいこと加工して64KBに納めないといけないが、そもそもこんな沢山のファイル全部納めるのはまず無理だw この時点で全データ格納は諦めた。

 

元のデータは白黒と言ってもグレイスケールのデータだ。しかしPC-1600K用のデータに変換する際には白黒の2値に落としてやらないといけない。真っ黒が0、真っ白が1とした場合、0.5までの色は白にする事にした。この辺りは良い感じの値が必要だが、まずは決めちゃうことが大事。気になったらあとで調整すればいーのよ(^-^)

 

圧縮アルゴリズムは迷わずでRLE(Run Length Encoding)とした。理由はいくつかあったが、PC-1600K上でリアルタイムに展開して表示しなければならない。潤沢なCPUパワーがあるマシンでは無いので、展開処理が簡単な方が良い。そう考えると選択肢がどどーんと限られてくる。今回のように色が2値限定の場合、ランレングス圧縮との相性は抜群だ!

 

ここではランレングス圧縮の細かい説明をするつもりはないので、ネットで検索してもらいたい。

 

絵によって縦方向を基準で圧縮した方が良いか、横方向を基準として圧縮する方が良いのかを見極める必要がある。

例えばこんな感じの絵↓

f:id:PocketGriffon:20210221212830p:plain

これは縦を基準として圧縮した方が良さそうだ。

逆にこういう絵↓は横を基準とした方が圧縮率があがる。

f:id:PocketGriffon:20210221213034p:plain

まさかデータを1つずつ見ていくわけにもいかないので、ツールの中で両方の圧縮を試してみて、1バイトでも縮まった方を採用する事にした。同じサイズだった場合は縦を採用した。これはPC-1600KのVRAMの都合で、縦方向の圧縮が都合が良いからだ。

 

拡張メモリで使える詳しいサイズを知りたい!

拡張メモリを積んでも「MEM」しかしたことが無い人は多いと思うw 数字が増える満足感は計り知れないw かくいう私もそのひとりだ!

 

しかし今回はCE-1600Mの32KBメモリ全てを使い切る事を前提としているため、使い方について正しく理解しなければならない。

なぜこんな風に強調しているのかと言えば、今回エラくハマったからだww

 

目的としては拡張メモリの64KB分をデータ領域として使いたい。出来ればファイルとして置くのではなく、普通にアクセス出来るメモリとして扱いたい。

マシン語領域として宣言する必要があるんだろうな…と思ったので、何も考えずに「NEW "S1:",&8000」としてみたがERROR 24になってしまう。

ERROR 24というのはパスワードが宣言されている場合に禁止されているコマンドを実行しようとすると出るエラーだ。

パスワードなんて設定していないよ…

知らぬ間に何かをしてしまったのかも…と思い、本体を完全リセットしてみるが全く変わらず。これだけで丸2日くらいハマってしまった(やる気なしモードなので実際には数分x2日分)。

 

散々調べてみて分かったのだが、どうやらINIT命令でRAMモジュールのモードを切り替えてやらねばならなかったらしい!これって基本中の基本???

  INIT "S1:", "P"

普通に増設メモリを取り付けるとINIT "S1:", "M"としたのと同等のようで、メモリモジュールは拡張メモリに設定される。MEMするとメモリが増えて見えるのはそのおかげだ。

"P"を指定すると、通常のプログラムから保護される場所になるらしく、マシン語データなどはここにいれるっぽい。プログラムからは保護されるのでMEMしても容量は増えて見えない。

そもそも「プログラムから保護」って言い方が分かりづらい(T-T) マシン語のプログラムは、プログラムではなくデータとして扱われるって事なのかな…。

 

ここで疑問がひとつ。標準メモリの16KBはモード的には"M"みたいなんだけど、INITでマシン語エリアも設定出来る。なんで???これがいらぬ混乱を引き起こす(T_T) アタマの中にメモリマップやモジュールの事が入っていないと理解する事は不可能だと思った…orz

 

  INIT "S1:","P"
  NEW "S1:",&7F3B
  INIT "S2:","P"
  NEW "S2:",&7F3B

よし!これでようやく拡張メモリにデータを置けるぞ!(^-^)

データが置けるのはBANK0では&80C5〜&BFFF、BANK1では&8000〜&BFFF…という感じにものすごーく分かりにくくなってる…。

今回のプログラムでは以下のように決めた。

  BANK0 &8100 - &BFFF
  BANK1 &8000 - &BFFF
  BANK2 &8100 - &BFFF
  BANK3 &8000 - &BFFF

アドレスが連続しないという問題は解決しないけれども、脳内での変換はだいぶ楽になるはずだ!拡張メモリ2つで64KBのうち、利用出来るのは65024バイト(63.5KB)となった。

 

ちなみにプログラムはBANK0の&C100から置いてある。ここは本体標準メモリ。ワークなどもここに置いてある。なんとなくこういうメモリ構成で作るのが作りやすいかもな…と思った。

 

実際の画像データ自体は、ランレングス圧縮して出来たファイルをcatで繋げて大きなサイズのファイル(65024バイト以内)にし、それを各バンクごとのファイルに分割した。その際、BLOADで読めるヘッダ(16バイト)を付けて終わり。あとはPC-1600K本体に読み込ませるだけだ。

f:id:PocketGriffon:20210221221636j:plain

結局、このサイズの中に1086枚の画像が入った!

 

実機でプログラミングする前に…

この手のプログラムを、PC-1600K実機で作っていくとデバッグが結構大変だ。出来る限りホストマシンで作り、動作的に問題ないものを実機に送り込んだ方が良い。そのためのC言語開発でもある!

このブログを読んでる界隈の方々に伝える事でもないが、C言語であればMacとPC-1600Kの両方で動くプログラムを作るのは簡単だ。

f:id:PocketGriffon:20210221222629j:plain

こんな感じでテキストで構わないので、確実にMac上で動くモノを作ってから実機に送り込む。おかげで実機での動作確認は数回程度で済む。

 

速度が出ない!

まずは動かす事が大事!速度は後回し!…といつも言ってます、はい。

そして今回もそういう作り方をした。まずは確実に動くようにしてからでないと工夫のしようがないからね。あ、メモリに関しては最初から工夫しとかないとダメ!!後からなんとかするのは大変だよw

 

今回は出来る限りC言語(LSIC 80)で書いた。アセンブラ化したところは

・バンク切り替えして拡張メモリから1バイト取得

・出来上がったデータをVRAMへ転送

の2つくらいだ。他はインラインアセンブラも使わずに作った。

 

実際にPC-1600K実機で動かしてみたのだが……こりゃあかん(^^;; 秒間1コマ程度しか動かない!うーん…予想では秒間3コマくらいは出そうな気持ちだったんだけど(T-T)

この状態で、取り切れるだけのバグは取ることにした!実際に表示がちゃんとしないデータなどが残っていたのだが、これらを全て片付けてからの高速化だ。

 

一通りバグが取り終えたところで、改めて高速化について考えてみる事に。

基本中の基本として、C言語で書いたモノがどんなアセンブラコードに変換されているのかをチェックする。すると仮想VRAMアドレスの計算時のy/8などが除算処理されていた。これは数の暴力、テーブル化で単純化して解決した。

 

次は圧縮されたデータの展開処理。展開処理は2つあり、縦方向を基準とした展開と、それを横にした展開ルーチンだ。この2つが非常に重たいのは明白。ちなみにデータは縦基準で圧縮されてるものが圧倒的に多いので、まずは縦展開処理の高速化を考える。

 

パッと考えられるのは、縦1ラインが全て黒または白の場合だ。これは判定も簡単だ。

次は1バイト全てが黒または白の処理も簡単に対応が出来た。

この2つの処理を入れてみたところ、かなり高速化された。やっぱりビット演算は得意では無いZ80。特に高級言語で書く際には注意が必要って事だ。

 

横展開処理に関しては、横方向にアドレスが連続しているので仮想VRAMの計算そのものを外した。さらにビット演算もテーブル参照をやめて計算するようにしたところ、想像以上に速くなった!

 

とりあえず完成ということで…

なんかやる気が出ないモードに突入しているにも関わらず、ちまちま作業は出来るんだなと実感。気合い入ってる時は30分で出来る事が1週間掛かってる感じだけど!

 

この後やるとしたら、ついにアセンブラ化かなぁ…でもとりあえず動いてるので、これでいいかなーって気持ちにもなるw きっと今よりも3倍くらい速くなる気がする…。

 

あと、アニメーションが最後まで流せないのも残念なので、オンメモリにこだわらずRS-232C経由でデータを読みつつ表示…なんてのも楽しそうだ。これは画像処理が速くなればなるほど厳しくなってきそう。目一杯高速化したのちに可能かどうか検討するのが良さそうだ。

 

当初の目標だった「メモリを目一杯使って何かをする」は達成出来た気がする!ちゃんとバンク切り替えてデータ読めた!グラフィックも表示出来たので一定の達成感はある。

よーしよし、PC-1600K面白いぞ!(^-^)

 

ではまた次回!(^-^)ノ