ORANGE pico のTFT液晶とフォントとVRAMと

[ORANGE pico](http://www.picosoft.co.jp/orange/) のTFT液晶モードの挙動をもう少し調べてみた。

# cpoke によるパターンの設定は効くか？

ORANGE pico では、`cpoke` という、キャラクターコードに対応するパターンを設定するコマンドがある。
これまで、`cpoke` について、ビデオ出力モードでは

* テキスト画面に表示するキャラクターのパターンを変更できる
* 設定は、既に画面に表示されているキャラクターにも遡って適用される
* `gprint` コマンドで使用するフォントはなぜか変更されない

ということがわかっている。
以下の画像は、「hello world」を出力させた後、`cpoke` コマンドにより空白 (`&H20`) のパターンを音符 (`9`) に変更し、再び「hello world」を出力された様子である。
テキスト画面への出力では空白が音符に置き換わっているが、`gprint` コマンドによるグラフィック画面への出力では空白が空白のまま出力されている。

![ビデオ出力モードで cpoke を試す](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d13a494a-4f1d-aff6-ea68-0f6e1d31e82e.png)

同様の実験をTFT液晶モード (`spitft 1`) で行うと、以下の画面になった。

![TFT液晶モードで cpoke を試す](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/10bda6ef-b6d5-5938-f323-92c82caa0abc.jpeg)

以下すべてにおいて、`cpoke` による設定は反映されていないことがわかる。

* `cpoke` の実行前に表示されていた文字
* `cpoke` の実行後にUART入力を反映して出力された文字
* `cpoke` の実行後に `print` コマンドにより出力された文字
* `cpoke` の実行後に `gprint` コマンドにより出力された文字

さて、ビデオ出力モードでは、`cpoke` によるキャラクターパターンの設定は `cpeek` により読み込まれるキャラクターパターンにも影響する。

![ビデオ出力モードで cpoke が cpeek に反映される](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/53c84938-673b-29ef-30a6-bde460d2e9a1.png)

TFT液晶モードでも試してみると、ここは同様に反映されるようである。

![TFT液晶モードでも cpoke が cpeek に反映される](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d48a71d7-f33e-7956-3e20-25aafb7427a3.jpeg)

# &H80台、&H90台のフォントは？

[以前の記事](https://qiita.com/mikecat_mixc/items/b6aedc84cd987981bd46#%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%BC%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%8C%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E7%94%BB%E9%9D%A2%E3%81%A8%E3%82%B0%E3%83%A9%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E7%94%BB%E9%9D%A2%E3%81%A7%E9%81%95%E3%81%86)で、テキスト画面とグラフィック画面で &H80 台などのフォントが違うことを紹介した。
そこで、これらのフォントがTFT液晶モードではどうなっているかを確認する。

たとえば、以下のプログラムを実行し、&H80 台および &H90 台の文字を出力してみる。

```
10 for i=&H80 to &H90 step &H10
20 for j=&H0 to &HF
30 print chr$(i+j);
40 next
50 print ""
60 next
```

結果は、ビデオ出力モード・TFT液晶モードともに罫線の文字が出力された。

![ビデオ出力モードでの出力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/69b82d37-bffd-85a7-1b73-e335bfbcbf86.png)
![TFT液晶モードでの出力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/20f2ed6c-6a70-1bb4-1f50-aa4b136c7332.jpeg)

さらに、以前の記事で紹介した以下のプログラムを実行し、`gprint` でどう出力されるかも見てみる。

```
10 cls:white=rgb(255,255,255)
20 for y=0 to 15
30 for x=0 to 15
40 c$=chr$(y*16+x)
50 locate x+2,y+4:print c$;
60 gprint 8*(x+22),8*(y+4),c$,white
70 next
80 next
90 if inkey()==0 then goto 90
```

結果は、ビデオ出力モードと同様に `gprint` では `print` とは異なる文字が出力されたり、8×8の領域からはみ出して描画されたりする現象を観測できた。

![gprint による一部の文字の出力が print と違うことを確認できる](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/bfc4dbc4-4572-823a-8e94-4b44ca369e82.jpeg)

# vmove による VRAM とのやり取りは？

ORANGE pico では、`vmove` コマンドにより、メモリー配列の内容をテキストVRAMにコピーしたり、テキストVRAMの内容をメモリー配列にコピーしたりできる。

[以前の記事](https://qiita.com/mikecat_mixc/items/b3815b95aa5136f12bad#%E6%96%87%E5%AD%97%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88)で紹介した以下のプログラムで、TFT液晶モードにおける `vmove` コマンドでのテキストVRAMへのコピーの挙動を見てみる。

```
10 cpoke
20 mset 0, 0, 1000
30 lx=12:ly=4
40 for i=0 to 15
50 c=asc(format$("%X", i))
60 mpoke 40*(ly-2)+lx+i, c
70 mpoke 40*(ly+i)+lx-2, c
80 next
90 for y=0 to 15
100 for x=0 to 15
110 mpoke 40*(ly+y)+lx+x,16*y+x
120 next
130 next
140 vmove 0, 1
150 locate 0, ly+16
160 if inkey()==0 then goto 160
```

以下が、プログラムの実行直前の様子である。

![VRAMに書き込むプログラム 実行直前](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/96ef9b3a-3cbc-5da1-336d-8bf28d8bd3eb.jpeg)

プログラムを実行し、さらにキーを押して終了させた。
130行目の位置に「OK」が出ている。
にもかかわらず、VRAMへの書き込みは反映されず、入力したプログラムがそのまま表示されている。

![VRAMに書き込むプログラム 実行後](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/c2bb46cb-7871-0840-a70d-2caba6615e1c.jpeg)

カーソルを画面の一番下まで移動させ、さらにCRを送信してスクロールさせると、VRAMに書き込んだ内容が反映された。

![スクロールによりVRAMに書き込んだ内容が反映された](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/7b5b7e26-02c5-2ac1-4e87-d7d8e1225d3d.jpeg)

TFT液晶の縦モード (`spitft 4`) でも試してみた。

![縦モードでプログラムを書き込んだ状態](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/98ce571c-e4c9-9ec7-200f-8495b17c7737.jpeg)

実行後、カーソルを動かしてみると「ネノハヒフヘホマ」が現れ、VRAMへの書き込みは行われている様子だった。

![縦モード 実行後の様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/f430ff0b-8de7-3907-3d67-916992433c21.jpeg)

しかし、TFT液晶の横モードとは異なり、スクロールさせても書き込んだはずの内容は反映されなかった。

![縦モード スクロール後の様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/b73e9612-a8c5-608f-d4d1-fa305e05d64e.jpeg)

ここで、公式のコマンド一覧の `vmove` の説明には、以下のように書かれている。

> テキストVRAMサイズは1000バイトありますので、アドレスの指定に注意してください

TFT液晶モードでは、画面の文字の枠は40×30=1200あり、1000を超える。
超えた分がどうなるか気になるので、以下のプログラムで実験をしてみた。

```
10 for i=0 to 1200-1
20 mpoke i,asc(hex$(i/100))
30 next
40 vmove 0,1
```

横画面ではスクロール、縦画面ではカーソルの移動により確認すると、それぞれ `a` や `b` すなわちアドレス `1000` 台や `1100` 台までVRAMに書き込まれていることがわかった。

![横画面でのVRAM書き込み結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/fd24706e-0109-a2eb-cff2-0bd024ab2e0e.jpeg)
![縦画面でのVRAM書き込み結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/4e5a3194-de42-3f9b-6c53-941e3d5f03e1.jpeg)

VRAMからの読み込みも試してみる。
「テキストVRAMサイズは1000バイトあります」と書かれているので、何も考えないでいるとテキストVRAMサイズは1000バイトしかないと思ってしまうだろう。
しかし、実際のテキストVRAMサイズは、特にTFT液晶モードにおいて1200バイトある可能性がある。
そこで、以下のプログラムで `vmove` によりメモリー配列に書き込まれるデータの量を調べてみた。

```
10 for i=0 to 2000
20 mpoke i,&HFF
30 next
40 vmove 500,0
50 for i=0 to 1999
60 if mpeek(i)==&HFF && mpeek(i+1)<>&HFF then print "start:";i+1
70 if mpeek(i)<>&HFF && mpeek(i+1)==&HFF then print "end:";i+1
80 next
```

これは、以下を行うプログラムである。

1. メモリー配列の先頭2001バイトに &HFF を書き込む
2. メモリー配列のアドレス500からVRAMを読み込む
3. メモリー配列のデータが &HFF でなくなった部分がどこかを調べる

結果は、ビデオ出力モード、TFT液晶横画面モード、TFT液晶縦画面モードにおいて、それぞれ以下のようになった。

![ビデオ出力モードでVRAMからの読み込みを試す](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/b42111c6-33d4-0931-c624-a3add79346a0.png)
![TFT液晶横画面モードでVRAMからの読み込みを試す](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/66796993-db4d-2b1b-cfcc-eeda1b1b92a7.jpeg)
![TFT液晶縦画面モードでVRAMからの読み込みを試す](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/8919948d-5732-98a1-6c15-459737121624.jpeg)

結果、**ビデオ出力モードを含む**試したすべてのモードにおいて

```
start:500
end:1700
```

と出力された。
これは、`vmove` で指定したアドレスより前にはデータが書き込まれておらず、指定したアドレス以降**1200バイト**に書き込みが行われていることを示す。
1000バイトしか書き込まれないと思い込んでプログラムを作ってしまうと、**予期せぬデータの破壊**に繋がる**罠**であるといえるだろう。

# まとめ

今回の調査で、以下のことがわかった。

* `cpoke` によるキャラクターパターンの設定は、TFT液晶モードでは `gprint` には効かないのに加え、ビデオ出力モードでは効く `print` や既に画面に表示さている文字に対しても効かない。
* `cpoke` によるキャラクターパターンの設定は、ビデオ出力モード・TFT液晶モードともに `cpeek` によるキャラクターパターンの取得には反映される。
* &H80台や&H90台のキャラクターは、TFT液晶モードでも、ビデオ出力モードと同様に `print` ではグラフィックや罫線となるが、`gprint` では謎の文字となり、さらに8×8の範囲外に描画される謎の現象も発生する。
* `vmove` コマンドは、TFT液晶の画面全体(1200バイト)のデータをVRAMに書き込める。ただし、
  * TFT液晶横画面モードでは、VRAMに書き込んだだけでは画面に反映されず、スクロールなどのキッカケがないと反映されない。
  * TFT液晶縦画面モードでは、横画面モードと違って、スクロールしてもVRAMに書き込んだ内容は画面に反映されない。カーソルを動かすと、カーソルが乗ったところはVRAMに書き込んだ文字が表示される。
* `vmove` コマンドによりVRAMからメモリー配列への読み込みを行うと、**1200バイト**のデータがメモリー配列に書き込まれる。これは**ビデオ出力モードでも同じ**である。
  * 公式のコマンド一覧の表現から、1000バイトしか書き込まれないと誤解しないように注意！