IchigoJam でジャンプアクションゲーム

[IchigoJam](https://ichigojam.net/) でジャンプアクションゲームを作ってみた。

※IchigoJamはjig.jpの登録商標です。

# ルール

主人公を操作して、トゲに落ちないようにしつつ、うまく足場を渡り、ゴールにたどり着ければクリア。

![ゲーム画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/f6b00e8e-b461-a447-c80b-11cd152fc74f.png)

# 操作方法

* 左矢印キー：左に進む
* 右矢印キー：右に進む
* スペースキー：ジャンプ
* Xキー：最初からやり直す

[HetaPad](https://kitaharima.pcn.club/index.php?aid=posts&mid=hetapad) での操作も可能である。

# プログラム

[OneFiveCrowd で実行する](https://onefivecrowd.mikecat.work/#text=LRUN+100%0A&en=%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%B2%E3%83%BC%E3%83%A0&e0=CgAaJ7zerN3M37G4vK7dIMverrO23szf27je19EAFABGQT0jODAwOkxFVFswXSwjMkZCNywjRTAwMSwjNDUwNSwjODA4MiwjMjAwMCwjNDc3MDpJRlVTUigjODAwLDApR09UTzgwAAAeAMJEPSJJQURFT0BLRUNDSkNBSEhIQEJCQklJRU5AQEJCQEBPQEJPT0hDRUpGR0ZISE5AQ05PRkBAQ0lKR0tAQ09LQENPSkBDT0tOQUlDREpDRUhISElJSEhDTEFMQEhCRUBAQkJCQkZAQkRBTUZNQU5PS01BQEdCREBMREBDT0FJQEhCQkBHQkVARURAQ0RHSEJEQEJOTERAQ0xHREJEQEpDTEdAR0ZBTEdPQUxFQkFOT0VNQUBEREpAQE9AQElPSEJGIgAoAAhHT1NVQjEzMAAyAJZEPSJKQkFMSkNFQEhISUFISEFISEBFSUhAT0BLTU5ATkJOQU5DQElEQkFITURAREtEQkJKQ0xCQEhJS0FITEpASEVCQEFJS0FIQEdCQkFAREBBQURAQERLTEFKR0BBQkBKQEBEQkBATUBFSkdAQElEQkBGTUBCQENDQUJASkFKR0BAQERCQEFNQEFCQEpFSkdAR0BERyIAPAAIR09TVUIxMzAARgAIR09UTzEyMAAAUADCRD0iSUFERUlHQE9AQEBAT0lBT0dNR0FARkxAR05MQkBDTUVET0BNSENJRUZPQE1AQURGT05IRkhFQkhJQkRPSENNSEpPQE5JQ0hIQEhPQkhOQEhBQ0hIQE9PQExCSUhAQ01FSk9AT0hDTUVMT0BPREJIR0pBREdCQ0JAQEdAQEFBQEdPTUFHTkVPT0FDT0dHRUBAQ0pJSEpBREZJQ0dHR0VAQEBDTEZASEBAQkJARkNDRUZPRkBAQkNASExIQEBCQSIAWgAIR09TVUIxMzAAZADCRD0iSEJCQ0BATEhAQEhFQEhARUBIT01BRk9NT0JAQ01HSk9AT0hDTUdMT0BPQkNJSk5PQExCQ0lLT09ATENHTkZOQU5DQUNARkBGQk5PTkhGSEJEQERBRkFGQ0xJQEVAQkFDRUdDRUBAS0pJRkFDTUdDRUBAQUZAR0tKSUZBTUhJSU1ISUJDSEBMRkFAQkFIQkFJTEFKQ0hATEZBQEhBTElCQUhCQkNIQExGQUJAQUxFQkFIQkpDSEBMRkFCSEJIQCIAbgAIR09TVUIxMzAAeAAMTFJVTkZJTEUoKSsxAIIASEM9UEVFSyhEKTpJRkM9MzRSRVRVUk5FTFNFUE9LRUEsQyUxNioxNitQRUVLKEQrMSklMTY6QT1BKzE6RD1EKzI6R09UTzEzMA&e1=CgAOJyC83qzdzN-xuLyu3QAAFAC6Q0xTOlBPS0UjN0UwLDgsOCwyOCwyOCw2Miw2MiwxMjcsMTI3OkZPUlg9MFRPMzE6UE9LRSM5MDArWCw2OlBPS0UjQkUwK1gsNjpORVhUOkZPUlk9MVRPMjI6UE9LRSM5MDArWSozMiw2OlBPS0UjOTFGK1kqMzIsNjpORVhUOk09IkFWREFVRGZWQklWRklVRjBWRFVWSVVVSVVQQVBPQUpOQUFNREpIQE9HQFRIQFpGRFpHQFpIRCIAAB4Aklg9UEVFSyhNKTpZPVBFRUsoTSsxKSYzMTpMPVBFRUsoTSsyKSYzMTpJRlgtMzRQPSNGQy0jRjYqIShYJjMyKTpYPVgmMzE6Rk9SST0wVE9MOlBPS0UjOTAwK1kqMzIrWCtJLFA6TkVYVDpNPU0rMzpHT1RPMzBFTFNFTEMyNyw3Oj8iR09BTCI7OkU9MDpGPS04ACgAKFg9MTY6WT0xNjA6Vj0wOkQ9MDpOPTA6Rz0wOko9MDpUPVRJQ0soKQAAMgBGSz1CVE4oLTEpOklGSyYxNkFORCFKQU5EKFNDUihYLzgsWS84KzEpPTZPUlNDUigoWCs3KS84LFkvOCsxKT02KVY9LTE0AAA8AExKPUsmMTY6Vj1WKyhWPDE1KTpZPVkrVi8yOklGU0NSKFgvOCxZLzgpPTZPUlNDUigoWCs3KS84LFkvOCk9Nlk9KFkrNykmfjc6Vj0wAEYAZkw9U0NSKFgvOCwoWSs3KS84KTpSPVNDUigoWCs3KS84LChZKzcpLzgpOklGTD02T1JSPTZZPVkmfjc6Vj0wOklGWD4yMTVBTkRZPDQ5UExBWSJPNEc4TDE2QkFCQUIjNCI6Rz0xAABQACJJRkw9I0ZDT1JSPSNGQ1k9WSZ-NzpCRUVQODAsMzA6Tj0xAFoAQElGSyYzPTFYPVgtMjpEPTE6SUZTQ1IoWC84LFkvOCk9Nk9SU0NSKFgvOCwoWSs3KS84KT02WD0oWCs3KSZ-NwAAZABESUZLJjM9Mlg9WCsyOkQ9MDpJRlNDUigoWCs3KS84LFkvOCk9Nk9SU0NSKChYKzcpLzgsKFkrNykvOCk9Nlg9WCZ-NwAAbgBgUD0jRjQqTisoI0ZCK0QqMikqIU46SUZbMF09IzQ1OTFRPVVTUigjODAwLDApRUxTRUxDRS84LEYvODo_Q0hSJCgwKTs6TENYLzgsWS84Oj9DSFIkKFApOzpFPVg6Rj1ZAHgAJElGSU5LRVkoKT04OEdPVE80MEVMU0VJRk5PUkdHT1RPMTIwAACCACRVPVRJQ0soKTpJRlU9VEdPVE8xMzBFTFNFVD1VOkdPVE81MA&font=1_4)

## 本体

```
10 ' ｼﾞｬﾝﾌﾟｱｸｼｮﾝ
20 CLS:POKE#7E0,8,8,28,28,62,62,127,127:FORX=0TO31:POKE#900+X,6:POKE#BE0+X,6:NEXT:FORY=1TO22:POKE#900+Y*32,6:POKE#91F+Y*32,6:NEXT:M="AVDAUDfVBIVFIUF0VDUVIUUIUPAPOAJNAAMDJH@OG@TH@ZFDZG@ZHD"
30 X=PEEK(M):Y=PEEK(M+1)&31:L=PEEK(M+2)&31:IFX-34P=#FC-#F6*!(X&32):X=X&31:FORI=0TOL:POKE#900+Y*32+X+I,P:NEXT:M=M+3:GOTO30ELSELC27,7:?"GOAL";:E=0:F=-8
40 X=16:Y=160:V=0:D=0:N=0:G=0:J=0:T=TICK()
50 K=BTN(-1):IFK&16AND!JAND(SCR(X/8,Y/8+1)=6ORSCR((X+7)/8,Y/8+1)=6)V=-14
60 J=K&16:V=V+(V<15):Y=Y+V/2:IFSCR(X/8,Y/8)=6ORSCR((X+7)/8,Y/8)=6Y=(Y+7)&~7:V=0
70 L=SCR(X/8,(Y+7)/8):R=SCR((X+7)/8,(Y+7)/8):IFL=6ORR=6Y=Y&~7:V=0:IFX>215ANDY<49PLAY"O4G8L16BABAB#4":G=1
80 IFL=#FCORR=#FCY=Y&~7:BEEP80,30:N=1
90 IFK&3=1X=X-2:D=1:IFSCR(X/8,Y/8)=6ORSCR(X/8,(Y+7)/8)=6X=(X+7)&~7
100 IFK&3=2X=X+2:D=0:IFSCR((X+7)/8,Y/8)=6ORSCR((X+7)/8,(Y+7)/8)=6X=X&~7
110 P=#F4*N+(#FB+D*2)*!N:IF[0]=#4591Q=USR(#800,0)ELSELCE/8,F/8:?CHR$(0);:LCX/8,Y/8:?CHR$(P);:E=X:F=Y
120 IFINKEY()=88GOTO40ELSEIFNORGGOTO120
130 U=TICK():IFU=TGOTO130ELSET=U:GOTO50
```

## 描画プログラム

本体のみでも実行できるが、主人公の動きがカクカクしたものになる。
描画プログラムを使用することで、なめらかな動きを実現できる。
描画プログラムを使用する場合は、本体の**1個前のファイル番号**に保存して実行すること。

```
10 'ｼﾞｬﾝﾌﾟｱｸｼｮﾝ ﾋﾞｮｳｶﾞﾌﾟﾛｸﾞﾗﾑ
20 A=#800:LET[0],#2FB7,#E001,#4505,#8082,#2000,#4770:IFUSR(#800,0)GOTO80
30 D="IADEO@KECCJCAHHH@BBBIIEN@@BB@@O@BOOHCEJFGFHHN@CNOF@@CIJGK@COK@COJ@COKNAICDJCEHHHIIHHCLAL@HBE@@BBBBF@BDAMFMANOKMA@GBD@LD@COAI@HBB@GBE@ED@CDGHBD@BNLD@CLGDBD@JCLG@GFALGOALEBANOEMA@DDJ@@O@@IOHBF"
40 GOSUB130
50 D="JBALJCE@HHIAHHAHH@EIH@O@KMN@NBNANC@IDBAHMD@DKDBBJCLB@HIKAHLJ@HEB@AIKAH@GBBA@D@AAD@@DKLAJG@AB@J@@DB@@M@EJG@@IDB@FM@B@CCAB@JAJG@@@DB@AM@AB@JEJG@G@DG"
60 GOSUB130
70 GOTO120
80 D="IADEIG@O@@@@OIAOGMGA@FL@GNLB@CMEDO@MHCIEFO@M@ADFONHFHEBHIBDOHCMHJO@NICHH@HOBHN@HACHH@OO@LBIH@CMEJO@OHCMELO@ODBHGJADGBCB@@G@@AA@GOMAGNEOOACOGGE@@CJIHJADFICGGGE@@@CLF@H@@BB@FCCEFOF@@BC@HLH@@BA"
90 GOSUB130
100 D="HBBC@@LH@@HE@H@E@HOMAFOMOB@CMGJO@OHCMGLO@OBCIJNO@LBCIKOO@LCGNFNANCAC@F@FBNONHFHBD@DAFAFCLI@E@BACEGCE@@KJIFACMGCE@@AF@GKJIFAMHIIMHIBCH@LFA@BAHBAILAJCH@LFA@HALIBAHBBCH@LFAB@ALEBAHBJCH@LFABHBH@"
110 GOSUB130
120 LRUNFILE()+1
130 C=PEEK(D):IFC=34RETURNELSEPOKEA,C%16*16+PEEK(D+1)%16:A=A+1:D=D+2:GOTO130
```

## ライセンス

このプログラム (本体および描画プログラム) は、[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja) でライセンスする。
このプログラム (改造したものを含む) を公開の場で利用する際は、出典を示していただけると嬉しい。
これは、Qiitaの利用規約に基づくプログラムの利用を禁止するものではない。

# 実行結果例

IchigoJam Q でも実行はできるが、ゲームの速度がゆっくりとしたものになる。
IchigoJam R では、満足のいく速度で実行できる。

以下の動画は、IchigoJam R で実行した様子である。
最初は本体のみで実行し、クリア後描画プログラムを読み込んで実行する。

<iframe width="640" height="480" src="https://www.youtube.com/embed/FbNvddZrHu4?si=iZKmm7EMUG_eOs5M" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

# 解説

## 本体

### 10行目：タイトル

> ```
> 10 ' ｼﾞｬﾝﾌﾟｱｸｼｮﾝ
> ```

`FILES` 対応のタイトル行である。

### 20～30行目：マップ初期化

> ```
> 20 CLS:POKE#7E0,8,8,28,28,62,62,127,127
> ```

画面を初期化し、`#FC` にトゲのキャラクターパターンを設定する。


> ```
> FORX=0TO31:POKE#900+X,6:POKE#BE0+X,6:NEXT
> FORY=1TO22:POKE#900+Y*32,6:POKE#91F+Y*32,6:NEXT
> ```

画面の外周部分をレンガで埋める。

> ```
> M="AVDAUDfVBIVFIUF0VDUVIUUIUPAPOAJNAAMDJH@OG@TH@ZFDZG@ZHD"
> ```

画面内側のマップデータを用意する。
マップデータは3文字1組で、順に「左端のx座標と種類」「左端のy座標」「長さ」を表す。
座標と長さは、文字のキャラクターコードの下位5ビットで表す。
最初の文字の下から6ビット目 (1-origin) は、0でレンガを、1でトゲを表す。

> ```
> 30 X=PEEK(M):Y=PEEK(M+1)&31:L=PEEK(M+2)&31
> ```

マップデータの組をメモリから変数に読み込む。

> ```
> IFX-34P=#FC-#F6*!(X&32):X=X&31
> ```

マップデータの終わり `"` でなければ、描画する文字を設定し、x座標を取り出す。

> ```
> FORI=0TOL:POKE#900+Y*32+X+I,P:NEXT:M=M+3:GOTO30
> ```

指定された長さの分だけ設定した文字を画面に書き込む。
次の組の処理へ移る。

> ```
> ELSELC27,7:?"GOAL";:E=0:F=-8
> ```

マップデータの描画後、ゴールの文字を用意する。
主人公を描画した座標 (消去処理用) を初期化する。

### 40行目：状態の初期化

> ```
> 40 X=16:Y=160:V=0:D=0:N=0:G=0:J=0:T=TICK()
> ```

以下の変数を初期化する。

|変数|意味|
|:-:|---|
|X|主人公のx座標 (ピクセル)|
|Y|主人公のy座標 (ピクセル)|
|V|主人公の落下速度 (0.5ピクセル/フレーム)|
|D|主人公の向き (0：右、1：左)|
|N|ゲームオーバーフラグ|
|G|ゲームクリア (ゴール) フラグ|
|J|前フレームにジャンプボタンが押されていたか|
|T|現フレームの時刻|

### 50行目：ジャンプ処理

> ```
> 50 K=BTN(-1)
> ```

ボタンの状態を変数 `K` に読み込む。

> ```
> IFK&16AND!JAND(SCR(X/8,Y/8+1)=6ORSCR((X+7)/8,Y/8+1)=6)V=-14
> ```

以下の条件がすべて成り立つならば、主人公の落下速度を上向きの大きい値に設定する (ジャンプする)。

* 現フレームでジャンプボタンが押されている
* 前フレームでジャンプボタンが押されていない
* 主人公の左端もしくは右端の1ピクセル下がレンガである

### 60行目：落下・頭を打つ処理

> ```
> 60 J=K&16
> ```

現フレームのジャンプボタンの状態を保存する。

> ```
> V=V+(V<15)
> ```

主人公を下方向に加速させる。
ただし、ブロックを突き抜けないようにするため、速度が大きくなりすぎないようにする。

> ```
> Y=Y+V/2
> ```

現在の速度に応じて、主人公を落下 (もしくは上昇) させる。

> ```
> IFSCR(X/8,Y/8)=6ORSCR((X+7)/8,Y/8)=6Y=(Y+7)&~7:V=0
> ```

主人公の左端もしくは右端の上端がレンガにめりこんだ場合は、めりこまない位置に主人公を下げ、上昇を止める。

### 70～80行目：足元の処理

> ```
> 70 L=SCR(X/8,(Y+7)/8):R=SCR((X+7)/8,(Y+7)/8)
> ```

主人公の左端および右端の足元のキャラクターを取得し、それぞれ変数 `L` および `R` に格納する。

> ```
> IFL=6ORR=6Y=Y&~7:V=0
> ```

足元にレンガがあるならば、主人公をめりこまない位置まで上げ、落下を止める。

> ```
> IFX>215ANDY<49PLAY"O4G8L16BABAB#4":G=1
> ```

(足元にレンガがあり、かつ) 主人公がゴールエリアに居るならば、ゴール音を再生し、ゴールフラグを立てる。

> ```
> 80 IFL=#FCORR=#FCY=Y&~7:BEEP80,30:N=1
> ```

足元にトゲがあるならば、主人公をめりこまない位置まで上げ、ブザー音を再生し、ゲームオーバーフラグを立てる。

### 90～100行目：左右移動の処理

> ```
> 90 IFK&3=1X=X-2:D=1:IFSCR(X/8,Y/8)=6ORSCR(X/8,(Y+7)/8)=6X=(X+7)&~7
> ```

左移動キーが押されているならば、主人公を左に動かし、向きを左に設定する。
主人公の上端もしくは下端の左端がレンガにめり込んだ場合、めり込まない位置に移動する。

> ```
> 100 IFK&3=2X=X+2:D=0:IFSCR((X+7)/8,Y/8)=6ORSCR((X+7)/8,(Y+7)/8)=6X=X&~7
> ```

同様に、右方向の処理を行う。

### 110行目：主人公の描画

> ```
> 110 P=#F4*N+(#FB+D*2)*!N
> ```

状態に応じ、描画するキャラクターを設定する。
ゲームオーバーフラグ `N` が立っている場合、爆発 (`#F4`) を描画する。
そうでない場合、左向きフラグ `D` が立っているなら左向きの人 (`#FD`) を、立っていないなら右向きの人 (`#FB`) を描画する。

> ```
> IF[0]=#4591Q=USR(#800,0)
> ```

描画プログラムが読み込まれているようであれば、描画プログラムを実行する。

> ```
> ELSELCE/8,F/8:?CHR$(0);:LCX/8,Y/8:?CHR$(P);:E=X:F=Y
> ```

描画プログラムが読み込まれていないようであれば、以下の簡単な描画を実行する。

1. 以前主人公が描画されていた場所の描画を消去する
2. 現在の場所に主人公を描画する
3. 主人公を描画した位置 (`E`, `F`) を更新する

### 120行目：停止・再スタート処理

> ```
> 120 IFINKEY()=88GOTO40ELSEIFNORGGOTO120
> ```

`x` が入力された場合、初期化処理に移り、ゲームを再スタートする。
そうでなく、ゲームオーバーフラグ `N` またはゴールフラグ `G` が立っている場合は、処理をここで止める。

### 130行目：ウェイト処理

> ```
> 130 U=TICK():IFU=TGOTO130ELSET=U:GOTO50
> ```

時刻がフレーム開始時点と同じである間、待機する。
時刻が進んだら、次のフレームの処理に移る。

## 描画プログラム

環境 (M0 / RV32C) を判別し、それに応じて適切なバージョンの描画プログラム (マシン語) を配列領域に格納する。
マシン語のプログラムは、2文字で1バイトを表す形式で埋め込んでいる。
最初の文字の下位4ビットがデータの上位4ビット、次の文字の下位4ビットがデータの下位4ビットである。
格納後、`LRUN` により1個次のファイル番号に格納されているプログラムを実行する。

以下のプログラムで配列全体をエンコードし、最後の余計なゼロ (`@@`) をカットして埋め込んだ。

```
10 FOR I=0 TO 202
20 ?CHR$(#40 + PEEK(#800+I)/16,#40 + PEEK(#800+I)%16);
30 NEXT
```

以下が、埋め込んだマシン語のソースコードである。
これらについても、本体と同様に [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja) でライセンスする。

<details><summary>M0 用</summary>

```
ORG #800
UDATAW `0100010110010001 ' Rn - Rm (識別用)
PUSH {LR,R4,R5,R6,R7}
' 前回描いた主人公を画面から消去する
R3 = @VAR_E
R0 = [R3 + 0]W ' R0 = 消去対象X座標
R2 = 2
R1 = [R3 + R2]S ' R1 = 消去対象Y座標
R2 = 0
GOSUB @M0_DRAW

R6 = @VAR_O
R6 = [R6 + 1]W
R6 -= #E0
R6 = R6 << 3
R7 = @VRAM
R7 -= #B0
R7 -= #B0
R7 -= #A0 ' R7 = #700 に相当するアドレス
R6 = R7 + R6 ' R6 = 描画するキャラクターパターンのアドレス (RAM上)
R3 = @VAR_W
R0 = [R3 + 1]W ' R0 = 描画対象X座標
R1 = [R3 + 2]W ' R1 = 描画対象Y座標

' 既存のキャラクターパターンをゼロクリアする
R4 = R7 + 0
R5 = 8
R2 = 0
@CP_ERASE
[R4 + 0]L = R2
R4 = R4 + 4
R5 = R5 - 1
IF !0 GOTO @CP_ERASE

' X,Yのオフセットに応じてキャラクターパターンを書き込む
R4 = 7
R4 &= R1
R7 = R7 + R4
R2 = 8
R5 = 7
R5 &= R0
@CP_WRITE
R4 = [R6 + 0]
R4 = R4 << 8
R4 >>= R5
[R7 + 16] = R4 ' #E2 に右上を、#E3 に右下を書き込む
R4 = R4 >> 8
[R7 + 0] = R4 ' #E0 に左上を、#E1 に左下を書き込む
R6 = R6 + 1
R7 = R7 + 1
R2 = R2 - 1
IF !0 GOTO @CP_WRITE

' 現在の位置に主人公を描く (R0, R1 は上で座標を設定済み)
R2 = [@DRAW_PUTTERN]L
GOSUB @M0_DRAW

' 描画した位置を更新する
R2 = @VAR_W
R3 = @VAR_E
R0 = [R2 + 1]W
R1 = [R2 + 2]W
[R3 + 0]W = R0
[R3 + 1]W = R1

POP {PC,R4,R5,R6,R7}

ALIGN 4,0,0
@DRAW_PUTTERN
DATAL #E3E1E2E0

' (R0, R1) に R2 で定義されるパターンを描画する (# 右下 左下 右上 左上)
@M0_DRAW
R1 & R1
IF MI GOTO @M0_DRAW_END
PUSH {R2}
R3 = @VRAM
R2 = R0 >> 3
R3 = R3 + R2
R2 = R1 >> 3
R2 = R2 << 5
R3 = R3 + R2
R2 = 7
R0 &= R2
R1 &= R2
POP {R2}
[R3 + 0] = R2
R2 = R2 >> 8
R0 & R0
IF 0 GOTO @M0_DRAW_X1_Y0_END
[R3 + 1] = R2
@M0_DRAW_X1_Y0_END
R1 & R1
IF 0 GOTO @M0_DRAW_END
R3 += 32
R2 = R2 >> 8
[R3 + 0] = R2
R0 & R0
IF 0 GOTO @M0_DRAW_END
R2 = R2 >> 8
[R3 + 1] = R2
@M0_DRAW_END
RET

ORG #900 - 44
@VAR_E

ORG #900 - 24
@VAR_O

ORG #900 - 8
@VAR_W

ORG #900
@VRAM
```

</details>

<details><summary>RV32C 用</summary>

```
MODE RV32C
ORG #800
UDATAW `0100010110010001 ' R11 = 4 (識別用)
R31 = PC
R31 += -2
SP += -1
PUSH R1, 0
PUSH R31, 1
' 前回描いた主人公を画面から消去する
R10 = [R31 + #D4]W ' R10 = 消去対象X座標
R11 = [R31 + #D6]S ' R11 = 消去対象Y座標
R12 = 0
R13 = R31
GOSUB @RV32C_DRAW
POP R31, 1

R17 = [R31 + #EA]W ' キャラクターパターン (変数 P)
R17 = R17 + -#E0
R17 <<= 3
R16 = R31 + -#100 ' R16 = #700 に相当するアドレス
R17 += R16 ' R17 = 描画するキャラクターパターンのアドレス (RAM上)
R10 = [R31 + #FA]W ' R0 = 描画対象X座標
R11 = [R31 + #FC]W ' R1 = 描画対象Y座標

' 既存のキャラクターパターンをゼロクリアする
R14 = R16
R15 = 8
@CP_ERASE
[R14 + 0]L = R0
R14 += 4
R15 += -1
IF R15 GOTO @CP_ERASE

' X,Yのオフセットに応じてキャラクターパターンを書き込む
R14 = R11 & 7
R16 += R14
R13 = 8
R15 = R10 & 7
@CP_WRITE
R12 = [R17 + 0]
R12 <<= 8
R12 = R12 >> R15
[R16 + 16] = R12 ' #E2 に右上を、#E3 に右下を書き込む
R12 >>= 8
[R16 + 0] = R12 ' #E0 に左上を、#E1 に左下を書き込む
R17 += 1
R16 += 1
R13 += -1
IF R13 GOTO @CP_WRITE

' 描画した位置を更新する
R14 = [R31 + #FA]W
R15 = [R31 + #FC]W
[R31 + #D4]W = R14
[R31 + #D6]W = R15

' 現在の位置に主人公を描く (R10, R11 は上で座標を設定済み)
R12 = #E3E1E000
R12 = R12 + #2E0
R13 = R31
POP R1, 0
SP += 1
' @RV32C_DRAW へフォールスルーする

' 配列が R13 から始まるとき、(R10, R11) に R12 で定義されるパターンを描画する (# 右下 左下 右上 左上)
@RV32C_DRAW
IF R11 < R0 GOTO @RV32C_DRAW_END
R14 = R10 >> 3
R13 += R14
R14 = R11 >> 3
R14 <<= 5
R13 += R14
R10 &= 7
R11 &= 7
[R13 + #100] = R12
R12 >>= 8
IF !R10 GOTO @RV32C_DRAW_X1_Y0_END
[R13 + #101] = R12
@RV32C_DRAW_X1_Y0_END
IF !R11 GOTO @RV32C_DRAW_END
R12 >>= 8
[R13 + #120] = R12
IF !R10 GOTO @RV32C_DRAW_END
R12 >>= 8
[R13 + #121] = R12
@RV32C_DRAW_END
RET

ORG #900 - 44
@VAR_E

ORG #900 - 24
@VAR_O

ORG #900 - 8
@VAR_W

ORG #900
@VRAM
```

</details>

これらのプログラムでは、以下の処理を行う。

1. 前回描画した主人公を画面から消去する。
2. 主人公のx座標およびy座標を8で割った余りに応じて、描画するキャラクターパターンをずらしたキャラクターパターンを生成する。
3. 生成したキャラクターパターン (最大4枚) を用いて、新しい位置に主人公を描画する。
4. 主人公を描画した位置の情報を更新する。

この処理により、文字 (VRAM) を書き換えるだけでは8ピクセル単位でしか移動できない中、1ピクセル単位での主人公の描画を実現する。
ただし、この処理は今回のゲームの以下の性質を利用した手抜きである。

* 主人公以外に動くもの (敵キャラなど) が無い
* ブロックの位置と大きさは常に8の倍数 (中途半端な高さのブロックや坂などが無い)
* 主人公が居る位置の背景は常に真っ黒 (他のオブジェクトに重ならない)