全12件 (12件中 1-12件目)
1
本来は、船の慣性を考慮するべきなのですがキー入力により、上下左右に船を動かしてMAPとの連動を確認するための処理用として、まずは組み込みこんでみます。ーーーーーmap.cpp(変更点)void main(){ unsigned char key; unsigned char *pos; map_init(); ship_init(); map_draw(); ship_draw(); while (1) { key = key_pressed();// if (key == 0)// {// continue;// } if (key & KBIT_ENT) { break; } /* if (key & KBIT_UP) { map_scroll(-1); } else if (key & KBIT_DN) { map_scroll(1); }*/ ship_move(key); map_draw(); whirl_draw(render_pos); ship_draw(); if (key & KBIT_SPC) { in_Wait(100); } }}ーーーーーship.h(新規)/* * ZX81 Ship Handling Module header */ #ifndef _ZX81_SHIP_H#define _ZX81_SHIP_H // functionsvoid ship_init(void); void ship_draw(void);void ship_move(unsigned char key); #endif // _ZX81_SHIP_Hーーーーーship.c(新規)/* * ZX81 Ship Handling Module program */ #include <stdio.h>#include <string.h> #include "key.h"#include "map.h"#include "ship.h" unsigned char shipptn[2][5][25] ={ { // even-right45 {0x87,0x83,0x00,0x00,0x00, 0x85,0x00,0x03,0x04,0x00, 0x00,0x05,0x07,0x84,0x04, 0x00,0x02,0x82,0x80,0x00, 0x00,0x00,0x02,0x00,0x00}, // even-right23 {0x87,0x86,0x04,0x00,0x00, 0x85,0x00,0x02,0x04,0x00, 0x00,0x82,0x03,0x86,0x00, 0x00,0x02,0x81,0x07,0x00, 0x00,0x00,0x01,0x00,0x00}, // even-center {0x00,0x87,0x82,0x00,0x00, 0x00,0x06,0x02,0x04,0x00, 0x00,0x82,0x86,0x05,0x00, 0x00,0x82,0x83,0x05,0x00, 0x00,0x00,0x01,0x00,0x00}, // even-left23 {0x00,0x00,0x83,0x86,0x00, 0x00,0x06,0x00,0x85,0x00, 0x87,0x03,0x86,0x05,0x00, 0x02,0x80,0x06,0x00,0x00, 0x00,0x00,0x01,0x00,0x00}, // even-left45 {0x00,0x00,0x87,0x83,0x00, 0x00,0x06,0x01,0x85,0x00, 0x81,0x03,0x05,0x05,0x00, 0x85,0x82,0x07,0x00,0x00, 0x00,0x02,0x00,0x00,0x00} }, { // odd-right45 {0x00,0x83,0x04,0x00,0x00, 0x00,0x05,0x02,0x86,0x00, 0x00,0x85,0x85,0x03,0x82, 0x00,0x00,0x84,0x81,0x05, 0x00,0x00,0x00,0x01,0x00}, // odd-right23 {0x00,0x06,0x83,0x00,0x00, 0x00,0x05,0x00,0x86,0x00, 0x00,0x85,0x06,0x03,0x04, 0x00,0x00,0x86,0x80,0x01, 0x00,0x00,0x02,0x00,0x00}, // odd-center {0x00,0x00,0x81,0x04,0x00, 0x00,0x87,0x01,0x86,0x00, 0x00,0x85,0x06,0x81,0x00, 0x00,0x85,0x83,0x81,0x00, 0x00,0x00,0x02,0x00,0x00}, // odd-left23 {0x00,0x00,0x87,0x06,0x04, 0x00,0x87,0x01,0x00,0x05, 0x00,0x06,0x03,0x81,0x00, 0x00,0x84,0x82,0x01,0x00, 0x00,0x00,0x02,0x00,0x00}, // odd-left45 {0x00,0x00,0x00,0x83,0x04, 0x00,0x87,0x03,0x00,0x05, 0x87,0x07,0x84,0x85,0x00, 0x00,0x80,0x81,0x01,0x00, 0x00,0x00,0x01,0x00,0x00} }}; // screen address#define D_DX 33 // 32+1(CR)#define D_DY 24 int ship_x = 0;int ship_y = 0;//int ship_ // ----------------------------------------------------------// 初期化// ----------------------------------------------------------void ship_init(void){ ship_x = 0; ship_y = 12;} // ----------------------------------------------------------// VRAM へ描画// start_y を先頭として、論理 0..RENDER_DY-1 を順に出力// ----------------------------------------------------------void ship_draw(void){ unsigned char *chrp, c; unsigned char *pos, p; int y, x; pos = getscrnaddr(ship_y, 4+(ship_x>>1)); if (ship_x & 0x01) { chrp = &shipptn[1][2][0]; } else { chrp = &shipptn[0][2][0]; } for (y = 0; y < 5; y++) {// memcpy(pos, chrp, 5);// pos += D_DX;// chrp += 5; for (x = 0; x < 5; x++) {// *pos |= *chrp; p = *pos; if (p & 0x80) { p ^= 0x07; } c = *chrp; if (c & 0x80) { c ^= 0x07; } p |= c; if (p & 0x80) { p ^= 0x07; }/* bit 7, a ; bit7 をチェック jr z, skip ; 0 ならスキップ xor 0x07 ; bit0?2 を反転skip:*/ *pos = p; pos++; chrp++; } pos += D_DX-5; }} // ----------------------------------------------------------// 移動処理// key入力及び時間経過に伴う減速処理// ----------------------------------------------------------void ship_move(unsigned char key){ if (key & KBIT_UP) { map_scroll(-1); } else if (key & KBIT_DN) { map_scroll(1); } if (key & KBIT_LT) { if (ship_x > 0) { ship_x--; } } else if (key & KBIT_RT) { if (ship_x < 35) { ship_x++; } }}いつものように、下記でビルドします。 zcc +zx81 map.c key.c ship.c whirl.c main.c -createa-app今回は右下の0x80の処理を毎回行っていて処理が少し重いようですが船と背景の重ね合わせは上手くいっているようです。次は船の慣性を考えていきます。
2026.04.23
コメント(0)

では、実際の計算方法を検討します。まずは、重ね合わせるブロック文字の双方に対して0x80ビットをチェックします。ビットが立っていれば、0x07で反転します。その後orを取り、再び0x80ビットをチェックして0x07で再反転するものです。// C言語の場合unsigned char v; // 対象データ if (v & 0x80) { v ^= 0x07; // 下位3ビットを反転}// アセンブラの場合 ld a, (value) bit 7, a jr z, no_flip xor 0x07no_flip: ld (value), a今回は、岩のパターンをベタ書きし、その上に描画する渦、船、ワニなどを重ね合わせしていきます。速度が遅いようならば、重ね合わせ用のブロックは反転しないように持っておくのも手かもしれません。また、この重ね合わせの処理は船の衝突判定時にも関係していて岩などの障害物から船のブロックの差分を引いた時に元のブロックイメージと異なる場合は衝突にしたいと考えています。1ドットでも当たれば衝突となるので、シビアすぎるかもしれません。
2026.04.21
コメント(0)

ZX81では、以前紹介した通り2x2のブロック文字が定義されておりこの組み合わせでセミグラフィック表示(64x48)を実現しています。※文字セットはこちらを参照 Appendix A - The character set(文字セット) | ZX81の回想禄 - 楽天ブログ4×4ドットのブロック文字コードは下記となります。そう、左上が01、右上が02、左下が04そして、右下だけが何故か87となっています。全指定だと80になりますので、右下を指定すると01~04は反転する事となります。これから、描画した岩のブロック文字に船などを重ね書きするのですが単純にorすると右下のドット有り/無しでそれ以外が反転してしまう事となります。一工夫する必要がありそうです。
2026.04.17
コメント(0)

実際にスクロールしてみると、どれもそんなに悪くないイメージです。・偶数行・奇数行しかし、一番上のとぐろパターンは狙いすぎなのと、ちょっと飽きそうです。2番目の波立つ感じも悪くはないのですが外に広がるようなので回転のイメージではないかなと。3番目は、当初一番ないかと思ったのですがスクロールさせると回転しているイメージが一番あるのでもう少し調整して採用したいなと思いました。
2026.04.14
コメント(0)
結局3つから絞れなかったのでスクロールに合わせて渦巻を表示してみて決めたいと思います。ーーーーーmain.cpp#include <stdio.h>#include <string.h>#include <input.h> // in_Wait #include "key.h"#include "map.h"#include "whirl.h" void main(){ unsigned char key; unsigned char *pos; map_init(); map_draw(); while (1) { key = key_pressed(); if (key == 0) { continue; } if (key & KBIT_ENT) { break; } if (key & KBIT_UP) { map_scroll(-1); } else if (key & KBIT_DN) { map_scroll(1); } map_draw(); whirl_draw(render_pos); if (key & KBIT_SPC) { in_Wait(100); } }}ーーーーーwhirl.h/* * ZX81 Whirl Handling Module header */ #ifndef _ZX81_WHIRL_H#define _ZX81_WHIRL_H extern int render_pos; // Map functionsvoid whirl_init(void);void whirl_draw(int render_pos); #endif // _ZX81_WHIRL_Hーーーーーwhirl.cpp/* * ZX81 Whirl Handling Module program */ #include <stdio.h>#include <string.h> #include "map.h"#include "whirl.h" unsigned char whirl_chr[][50] = {{0x87,0x03,0x03,0x86,0x00, 0x05,0x06,0x03,0x04,0x05, 0x86,0x02,0x01,0x05,0x05, 0x00,0x03,0x03,0x87,0x01, 0x02,0x03,0x03,0x01,0x00, 0x00,0x83,0x83,0x83,0x00, 0x06,0x87,0x83,0x04,0x00, 0x05,0x05,0x83,0x02,0x04, 0x05,0x86,0x83,0x01,0x05, 0x02,0x83,0x83,0x06,0x00}, {0x87,0x01,0x00,0x86,0x00, 0x01,0x06,0x02,0x04,0x01, 0x00,0x04,0x00,0x04,0x00, 0x86,0x02,0x02,0x87,0x01, 0x00,0x01,0x00,0x01,0x00, 0x00,0x83,0x83,0x04,0x00, 0x02,0x87,0x83,0x02,0x00, 0x05,0x01,0x00,0x01,0x05, 0x01,0x86,0x83,0x01,0x01, 0x02,0x83,0x83,0x06,0x00}, {0x87,0x03,0x01,0x00,0x00, 0x05,0x00,0x03,0x04,0x04, 0x05,0x05,0x00,0x01,0x05, 0x00,0x02,0x01,0x87,0x01, 0x00,0x00,0x03,0x01,0x00, 0x00,0x00,0x83,0x04,0x00, 0x00,0x87,0x04,0x02,0x04, 0x04,0x05,0x00,0x04,0x05, 0x05,0x00,0x83,0x01,0x00, 0x02,0x83,0x00,0x00,0x00} /*{0x87,0x01,0x02,0x86,0x00},{0x01,0x06,0x01,0x04,0x05},{0x04,0x04,0x00,0x04,0x00},{0x86,0x00,0x03,0x87,0x01},{0x00,0x03,0x00,0x01,0x00}, {0x00,0x83,0x00,0x04,0x00},{0x06,0x00,0x83,0x02,0x04},{0x01,0x01,0x00,0x01,0x00},{0x04,0x86,0x04,0x01,0x05},{0x02,0x04,0x87,0x06,0x00},*/ }; #define WHIRL_MAX 3 struct { int y; int x;} whirl_tbl[WHIRL_MAX] ={ { 1,11}, { 4,13}, { 7,15}}; // screen address#define D_DX 33 // 32+1(CR)#define D_DY 24 // ----------------------------------------------------------// 初期化// ----------------------------------------------------------void whirl_init(void){} // ----------------------------------------------------------// VRAM へ描画// start_y を先頭として、論理 0..RENDER_DY-1 を順に出力// ----------------------------------------------------------void whirl_draw(int render_pos){ int i, wy, y, dy; unsigned char *chrp; unsigned char *pos; for (i = 0; i < WHIRL_MAX; i++) { wy = whirl_tbl[i].y * 2; dy = wy - ((render_pos+1) / 2); if ((dy >= -4) && (dy <= 23)) { if (dy < 0) { pos = getscrnaddr(0, whirl_tbl[i].x+4); } else { pos = getscrnaddr(dy, whirl_tbl[i].x+4); } if (render_pos & 1) { chrp = &whirl_chr[i][25]; } else { chrp = &whirl_chr[i][0]; } for (y = 0; y < 5; y++) { if ((dy <= 23) && (dy >= 0)) { memcpy(pos, chrp, 5); } if (dy >= 0) { pos += D_DX; } chrp += 5; dy++; } } } }いつものように、下記でビルドします。 zcc +zx81 map.c key.c whirl.c main.c -create-app表示結果については、次回確認していきます。
2026.04.13
コメント(0)

上下スクロールの検討が一通り終わったのでスクロールに合わせた障害物を検討していきます。船の大きさを縮小した時に気づいてはいたのですが船とのバランスを取るには、他の障害物も縮小が必須です。 リバーパトロール製作検討② | ZX81の回想禄 - 楽天ブログなので、まずは渦巻のイメージから再検討をします。6×6文字から5×5文字が適当なようです。表示仕様はPC-8001と同じように偶数行と奇数行の2つのパターンの切替を目指します。・とぐろ2.回転3.波が立つうーむ。イメージだけではどうしてもしっくりこないので実装して実際に表示を確認しながら決める事とします。
2026.04.10
コメント(0)
更にアセンブラ化を進めていきます。まずは、指定された1行分お岩を描画するrock_render_lineを試してみます。ーーーーーmap.cpp(変更点)void rock_render_line(int phys_y, int logical_row){/* int dx; unsigned char bit = 0x80; int rockx = 0; int rocky = (render_pos + logical_row * 2 + 1) / 4; int ptn = (render_pos + logical_row * 2) & 0x03; // 0 or 2 if (render_pos & 0x01) { memset(&render_buf_odd[phys_y][1], 0, RENDER_DX - 2); for (dx = 1; dx <= RENDER_DX - 2; dx++) { if (rock_tbl[rocky][rockx] & bit) memcpy(&render_buf_odd[phys_y][dx], &rock_chr[ptn][0], 2); bit >>= 1; if (!bit) { bit = 0x80; rockx++; } } } else { memset(&render_buf_even[phys_y][1], 0, RENDER_DX - 2); for (dx = 1; dx <= RENDER_DX - 2; dx++) { if (rock_tbl[rocky][rockx] & bit) memcpy(&render_buf_even[phys_y][dx], &rock_chr[ptn][0], 2); bit >>= 1; if (!bit) { bit = 0x80; rockx++; } } }*/#asm ld ix, 2 add ix, sp // render_pos + logical_row * 2 ld hl, (_render_pos) ld a, (ix+0) ; logical_row ld d, 0 ld e, a add hl, de ; *1 add hl, de ; *2 rock_render_line_setrockchr: // int ptn = (render_pos + logical_row * 2) & 0x03; // rock_render_line_rock_chr = &rock_chr[ptn][0]; push hl ld a, 0x03 and a, l ld hl, _rock_chr ld d, 0 ld e, a add hl, de ; *1 add hl, de ; *2 ld (_rock_render_line_rock_chr), hl pop hl rock_render_line_setroctbl: // int rocky = (render_pos + logical_row * 2 + 1) / 4; // rock_render_line_rock_tbl = &rock_tbl[rocky][0]; inc hl ; +1 srl h rr l ; ÷ 2 srl h rr l ; ÷ 4 ld de, _rock_tbl ex de, hl add hl, de add hl, de add hl, de ld (_rock_render_line_rock_tbl), hl // render_buf_odd[phys_y][dx] // render_buf_even[phys_y][dx] // 奇数判定のため最下位BITチェック ld a, (_render_pos) bit 0, a jr z, rock_render_line_setevenaddr rock_render_line_setoddaddr: ld bc, _render_buf_odd jr rock_render_line_setrenderbuf rock_render_line_setevenaddr: ld bc, _render_buf_even rock_render_line_setrenderbuf: ld a, (ix+2) ; phys_y ld h, 0 ld l, a push de ld d, h ld e, l add hl, hl ; *2 add hl, hl ; *4 add hl, de ; *5 add hl, hl ; *10 add hl, de ; *11 add hl, hl ; *22 pop de add hl, bc // memset(&render_buf_odd[phys_y][1], 0, RENDER_DX - 2) inc hl push hl ld b,RENDER_DX-2rock_render_line_rockclr: ld (hl), 0 inc hl djnz rock_render_line_rockclr pop hl rock_render_line_rockdraw: // for (dx = 1; dx <= RENDER_DX - 2; dx++) ld b, RENDER_DX-2 ld c, 0x80 // if (rock_tbl[rocky][rockx] & bit) ld de, (_rock_render_line_rock_tbl) rock_render_line_rockloop: ld a, (de) and a, c jp z, rock_render_line_nextrock push de // memcpy(&render_buf_odd[phys_y][dx], &rock_chr[ptn][0], 2); ld de, (_rock_render_line_rock_chr) ld a, (de) ld (hl), a inc de inc hl ld a, (de) ld (hl), a dec hl pop de rock_render_line_nextrock: // bit >>= 1; srl c // if (!bit) { bit = 0x80; rockx++; } jr nz, rock_render_line_chkloop rock_render_line_nexttbl: ld c, 0x80 inc de rock_render_line_chkloop: inc hl djnz rock_render_line_rockloop ret ._rock_render_line_rock_chr defw 0._rock_render_line_rock_tbl defw 0 #endasm}いつものように、下記でビルドします。 zcc +zx81 map.c key.c main.c -createa-app試してみると・・・28行スクロールにかかる時間が1秒になりました。うん、この速さならゲームとして使えそうです。上下スクロールの基礎がこれで固まった気がします。
2026.04.09
コメント(0)
アセンブラ化を進めていきます。まずは、全体を再描画するmap_drawを試してみます。ーーーーーmap.cpp(変更点)// ----------------------------------------------------------// VRAM へ描画// start_y を先頭として、論理 0..RENDER_DY-1 を順に出力// ----------------------------------------------------------void map_draw(void){/* int logical_row; int src_y; unsigned char *curr_bufp, *top_bufp; unsigned char *pos = getscrnaddr(0, 4); if (render_pos & 1) { src_y = start_y; curr_bufp = &render_buf_odd[src_y][0]; top_bufp = &render_buf_odd[0][0]; } else { src_y = start_y; curr_bufp = &render_buf_even[src_y][0]; top_bufp = &render_buf_even[0][0]; } for (logical_row = 0; logical_row < RENDER_DY; logical_row++) { memcpy(pos, curr_bufp, RENDER_DX); pos += D_DX; curr_bufp += RENDER_DX; src_y++; if (src_y >= RENDER_DY) { src_y = 0; curr_bufp = top_bufp; } }*/ // 描画開始位置をHLレジスタに取得 getscrnaddr(0, 4); #asm ld (_map_draw_scrnaddr),hl // 奇数判定のため最下位BITチェック ld a,(_render_pos) bit 0,a jr z,map_draw_setevenaddr map_draw_setoddaddr: ld hl,_render_buf_odd jr map_draw_settopbuf map_draw_setevenaddr: ld hl,_render_buf_even map_draw_settopbuf: ld (_map_draw_topbuf),hl map_draw_setsrcy: ld a,(_start_y) ld (_map_draw_srcy),a map_draw_setcurrbuf: ; HL = start_y * RENDER_DX ld d,0 ld e,a ld hl,0 add hl, de ; *1 0 0001 add hl, hl ; *2 0 0010 add hl, hl ; *4 0 0100 add hl, de ; *5 0 0101 add hl, hl ; *10 0 1010 add hl, de ; *11 0 1011 add hl, hl ; *22 1 0110 ld de,(_map_draw_topbuf) add hl,de; ld (_map_draw_currbuf),hl map_draw_buf2scrn: ld b,RENDER_DY ld de,(_map_draw_scrnaddr); ld hl,(_map_draw_currbuf) ld a,(_map_draw_srcy) map_draw_linecopy: push bc ld bc,RENDER_DX ldir pop bc inc a cp a,RENDER_DY jr nz,map_draw_nextline ld a,0 ld hl,(_map_draw_topbuf) map_draw_nextline: push hl ld hl,D_DX-RENDER_DX add hl,de ex de,hl pop hl djnz map_draw_linecopy ret ._map_draw_scrnaddr defw 0._map_draw_topbuf defw 0;._map_draw_currbuf defw 0._map_draw_srcy defb 0 #endasm}いつものように、下記でビルドします。 zcc +zx81 map.c key.c main.c -createa-app試してみると・・・28行スクロールにかかる時間が3秒→2秒になりました。あまり期待していなかっただけに、正直嬉しい!アセンブラ化は少々面倒ではありますが、目に見えて速度が向上すると、モチベーションが上がります!特にZX81では顕著に差が出るので、この作業は病みつきになります。さあ、皆さん!今こそ、この言葉を思い出すときです! Your Computer 誌「ZX81は喜びと失望の両方だ」 シンクレア ZX81 - Wikipedia 「評価」より
2026.04.07
コメント(0)

1行ずらしてコピーする事(行コピー方式)で高速化しましたがさらなる方法としてリングバッファでの描画の実装を試みます。1行上にスクロールする場合を図解してみました。 0~22行を1~23行にコピー後に0行目のみを再描画する事で、 全行描画よりも高速化が可能となりました。 しかし、23行分をコピーする事はZX81にとっては痛いタイムロスとなります。 次に、今回紹介する描画開始位置をずらしていく方式です。 描画開始位置であるstart_yを+1とし、元々start_yの行のみを再描画するものです。 メリットは行コピーを行わないため速度向上が見込まれること。 デメリットはソースが少し複雑化する事です。以下、ソースコードとなります。ーーーーーmap.cpp/* * ZX81 Map Handling Module program */ #include <stdio.h>#include <string.h> #include "map.h" unsigned char rock_tbl[19][3] = {// screen{0x80,0x00,0x00},{0x40,0x00,0x00},{0x20,0x00,0x00},{0x10,0x00,0x00},{0x08,0x00,0x00},{0x04,0x00,0x00},{0x02,0x00,0x00},{0x01,0x00,0x00},{0x00,0x80,0x00},{0x00,0x40,0x00},{0x00,0x20,0x00},{0x00,0x10,0x00},// scroll{0x00,0x08,0x00},{0x00,0x04,0x00},{0x00,0x02,0x00},{0x00,0x01,0x00},{0x00,0x00,0x80},{0x00,0x00,0x40},{0x00,0x00,0x20},}; unsigned char rock_chr[4][2] ={{0x81, 0x05},{0x80, 0x01},{0x03, 0x00},{0x87, 0x04}}; int start_y = 0; // Render buffers for smooth 2x2 tile scrolling (even/odd frames)#define RENDER_DX 22#define RENDER_DY 24unsigned char render_buf_even[RENDER_DY][RENDER_DX];unsigned char render_buf_odd[RENDER_DY][RENDER_DX]; int render_pos; // screen address #define D_FILE 0x400c#define D_DX 33 // 32+1(CR)#define D_DY 24 unsigned char* getscrnaddr(int posy, int posx){#asm ld ix,2 add ix,sp ; push de ld d, (ix+2) ; d=posy ld e, (ix+0) ; e=posx call getscrnaddr; pop de ret getscrnaddr:; IN: D=posy, E=posx; OUT: HL= screen address; push bc ; HL = posy * 33 ld h, 0 ld l, d add hl, hl ; *2 add hl, hl ; *4 add hl, hl ; *8 add hl, hl ; *16 add hl, hl ; *32 ld b, 0 ld c, d add hl, bc ; +1 -> *33 ; HL += posx ld b, 0 ld c, e ; c=posx add hl, bc ; HL += D_FILE ld bc,(D_FILE) inc bc add hl, bc ; pop bc ret #endasm} void rock_render_line(int phys_y, int logical_row){ int dx; unsigned char bit = 0x80; int rockx = 0; int rocky = (render_pos + logical_row * 2 + 1) / 4; int ptn = (render_pos + logical_row * 2) & 0x03; // 0 or 2 if (render_pos & 0x01) { memset(&render_buf_odd[phys_y][1], 0, RENDER_DX - 2); for (dx = 1; dx <= RENDER_DX - 2; dx++) { if (rock_tbl[rocky][rockx] & bit) memcpy(&render_buf_odd[phys_y][dx], &rock_chr[ptn][0], 2); bit >>= 1; if (!bit) { bit = 0x80; rockx++; } } } else { memset(&render_buf_even[phys_y][1], 0, RENDER_DX - 2); for (dx = 1; dx <= RENDER_DX - 2; dx++) { if (rock_tbl[rocky][rockx] & bit) memcpy(&render_buf_even[phys_y][dx], &rock_chr[ptn][0], 2); bit >>= 1; if (!bit) { bit = 0x80; rockx++; } } }} // ----------------------------------------------------------// 初期化// ----------------------------------------------------------void map_init(void){ int logical_row; int phys_y; render_pos = 0; start_y = 0; // ----------------------------- // even バッファの初期化(render_pos=0) // ----------------------------- for (logical_row = 0; logical_row < RENDER_DY; logical_row++) { render_buf_even[logical_row][0] = 0x82; render_buf_even[logical_row][RENDER_DX-1] = 0x81; phys_y = (start_y + logical_row) % RENDER_DY; rock_render_line(phys_y, logical_row); } // ----------------------------- // odd バッファの初期化(render_pos-1 にして描く) // ----------------------------- render_pos--; for (logical_row = 0; logical_row < RENDER_DY; logical_row++) { render_buf_odd[logical_row][0] = 0x07; render_buf_odd[logical_row][RENDER_DX-1] = 0x84; phys_y = (start_y + logical_row) % RENDER_DY; rock_render_line(phys_y, logical_row); } // 初期状態は render_pos++ に戻す render_pos++;} // ----------------------------------------------------------// スクロール// dy < 0 : 上へ 1dot// dy > 0 : 下へ 1dot//// ルール:// render_pos++ の結果が奇数なら start_y++// render_pos-- の結果が偶数なら start_y--// ----------------------------------------------------------void map_scroll(int dy){ int phys_y; int logical_row; if (dy < 0) { if (render_pos == 0) return; // 上へ 1dot render_pos--; // 結果が偶数なら start_y を -1 if ((render_pos & 1) == 0) start_y = (start_y - 1 + RENDER_DY) % RENDER_DY; logical_row = 0; // 画面の最上段が新規 phys_y = start_y; // 論理 0 行目の物理行 rock_render_line(phys_y, logical_row); } else if (dy > 0) { if (render_pos == 28) return; // 下へ 1dot render_pos++; // 結果が奇数なら start_y を +1 if (render_pos & 1) start_y = (start_y + 1) % RENDER_DY; logical_row = RENDER_DY - 1; // 画面の最下段が新規 phys_y = (start_y + logical_row) % RENDER_DY; rock_render_line(phys_y, logical_row); }} // ----------------------------------------------------------// VRAM へ描画// start_y を先頭として、論理 0..RENDER_DY-1 を順に出力// ----------------------------------------------------------void map_draw(void){ int logical_row; int src_y; unsigned char *curr_bufp, *top_bufp; unsigned char *pos = getscrnaddr(0, 4); if (render_pos & 1) { src_y = start_y; curr_bufp = &render_buf_odd[src_y][0]; top_bufp = &render_buf_odd[0][0]; } else { src_y = start_y; curr_bufp = &render_buf_even[src_y][0]; top_bufp = &render_buf_even[0][0]; } for (logical_row = 0; logical_row < RENDER_DY; logical_row++) { memcpy(pos, curr_bufp, RENDER_DX); pos += D_DX; curr_bufp += RENDER_DX; src_y++; if (src_y >= RENDER_DY) { src_y = 0; curr_bufp = top_bufp; } }}※他のソースの変更はありません。いつものように、下記でビルドします。 zcc +zx81 map.c key.c main.c -createa-appしかし、多少なりとも描画速度が速くなる事を期待したのですが。。。結果は行コピーとほぼ変わらない程度でした。非常に残念です。開始位置を計算する割り算や余りなどの計算が増えたためと予想します。これは、まあ仕方ないですね。あとはアセンブラ化して高速化してみます。
2026.04.06
コメント(0)
前回のソースでは描画があまりにも遅かったのでまずは、画面バッファを移動方向に合わせて-1なら1行下にコピーして最上段の1行のみを描画+1なら1行上にコピーして最下段の1行のみを描画としてみました。 ーーーーーmap.cpp/* * ZX81 Map Handling Module program */ #include <stdio.h>#include <string.h> #include "map.h" unsigned char rock_tbl[19][3] = {// screen{0x80,0x00,0x00},{0x40,0x00,0x00},{0x20,0x00,0x00},{0x10,0x00,0x00},{0x08,0x00,0x00},{0x04,0x00,0x00},{0x02,0x00,0x00},{0x01,0x00,0x00},{0x00,0x80,0x00},{0x00,0x40,0x00},{0x00,0x20,0x00},{0x00,0x10,0x00},// scroll{0x00,0x08,0x00},{0x00,0x04,0x00},{0x00,0x02,0x00},{0x00,0x01,0x00},{0x00,0x00,0x80},{0x00,0x00,0x40},{0x00,0x00,0x20},}; unsigned char rock_chr[4][2] ={{0x81, 0x05},{0x80, 0x01},{0x03, 0x00},{0x87, 0x04}}; // Render buffers for smooth 2x2 tile scrolling (even/odd frames)#define RENDER_DX 22#define RENDER_DY 24unsigned char render_buf_even[RENDER_DY][RENDER_DX];unsigned char render_buf_odd[RENDER_DY][RENDER_DX]; int render_pos; // screen address #define D_FILE 0x400c#define D_DX 33 // 32+1(CR)#define D_DY 24 unsigned char* getscrnaddr(int posy, int posx){#asm ld ix,2 add ix,sp ; push de ld d, (ix+2) ; d=posy ld e, (ix+0) ; e=posx call getscrnaddr; pop de ret getscrnaddr:; IN: D=posy, E=posx; OUT: HL= screen address; push bc ; HL = posy * 33 ld h, 0 ld l, d add hl, hl ; *2 add hl, hl ; *4 add hl, hl ; *8 add hl, hl ; *16 add hl, hl ; *32 ld b, 0 ld c, d add hl, bc ; +1 -> *33 ; HL += posx ld b, 0 ld c, e ; c=posx add hl, bc ; HL += D_FILE ld bc,(D_FILE) inc bc add hl, bc ; pop bc ret #endasm} void rock_render_line(int phys_y){ int dx; unsigned char bit = 0x80; int rockx = 0; int rocky = (render_pos + phys_y * 2 + 1) / 4; int ptn = (render_pos + phys_y * 2) & 0x03; // 0 or 2 if (render_pos & 0x01) { memset(&render_buf_odd[phys_y][1], 0, RENDER_DX - 2); for (dx = 1; dx <= RENDER_DX - 2; dx++) { if (rock_tbl[rocky][rockx] & bit) memcpy(&render_buf_odd[phys_y][dx], &rock_chr[ptn][0], 2); bit >>= 1; if (!bit) { bit = 0x80; rockx++; } } } else { memset(&render_buf_even[phys_y][1], 0, RENDER_DX - 2); for (dx = 1; dx <= RENDER_DX - 2; dx++) { if (rock_tbl[rocky][rockx] & bit) memcpy(&render_buf_even[phys_y][dx], &rock_chr[ptn][0], 2); bit >>= 1; if (!bit) { bit = 0x80; rockx++; } } }} // ----------------------------------------------------------// 初期化// ----------------------------------------------------------void map_init(void){ int phys_y; render_pos = 0; // ----------------------------- // even バッファの初期化(render_pos=0) // ----------------------------- for (phys_y = 0; phys_y < RENDER_DY; phys_y++) { render_buf_even[phys_y][0] = 0x82; render_buf_even[phys_y][RENDER_DX-1] = 0x81; rock_render_line(phys_y); } // ----------------------------- // odd バッファの初期化(render_pos-1 にして描く) // ----------------------------- render_pos--; for (phys_y = 0; phys_y < RENDER_DY; phys_y++) { render_buf_odd[phys_y][0] = 0x07; render_buf_odd[phys_y][RENDER_DX-1] = 0x84; rock_render_line(phys_y); } // 初期状態は render_pos++ に戻す render_pos++;} // ----------------------------------------------------------// スクロール// dy < 0 : 上へ 1dot// dy > 0 : 下へ 1dot// ----------------------------------------------------------void map_scroll(int dy){ int phys_y; if (dy < 0) { if (render_pos == 0) return; // 上へ 1dot render_pos--; // 結果が偶数ならバッファを1行上にコピー(memcpyはコピーが重なるためNG) if ((render_pos & 1) == 0) { memmove(&render_buf_even[1][0], &render_buf_even[0][0], RENDER_DX * (RENDER_DY - 1)); memmove(&render_buf_odd[1][0], &render_buf_odd[0][0], RENDER_DX * (RENDER_DY - 1)); } phys_y = 0; rock_render_line(phys_y); } else if (dy > 0) { if (render_pos == 28) return; // 下へ 1dot render_pos++; // 結果が奇数ならバッファ全体を1行下にコピー if (render_pos & 1) { memcpy(&render_buf_even[0][0], &render_buf_even[1][0], RENDER_DX * (RENDER_DY - 1)); memcpy(&render_buf_odd[0][0], &render_buf_odd[1][0], RENDER_DX * (RENDER_DY - 1)); } phys_y = RENDER_DY - 1; rock_render_line(phys_y); }} // ----------------------------------------------------------// VRAM へ描画// ----------------------------------------------------------void map_draw(void){ int phys_y; unsigned char *curr_bufp; unsigned char *pos = getscrnaddr(0, 4); if (render_pos & 1) { curr_bufp = &render_buf_odd[0][0]; } else { curr_bufp = &render_buf_even[0][0]; } for (phys_y = 0; phys_y < RENDER_DY; phys_y++) { memcpy(pos, curr_bufp, RENDER_DX); pos += D_DX; curr_bufp += RENDER_DX; }} 28ドット分スクロールした結果前回が14秒、今回が3秒程度かかってります。 約4倍高速化された事となります。
2026.04.03
コメント(0)

次に、岩のみですが1dot単位で上下描画するためのmap.hとmap.cを作成しました。岩のイメージは以前から変更していません。一面のマップも準備していますがとりあえずは左端から行をずらしながら右端まで描画するようにしています。ーーーーmap.h/* * ZX81 Map Handling Module header */ #ifndef _ZX81_MAP_H#define _ZX81_MAP_H // screen functionsunsigned char* getscrnaddr(int posy, int posx); // Map functionsvoid map_init(void);void map_scroll(int dy);void map_draw(void); #endif // _ZX81_MAP_Hーーーーmap.cpp/* * ZX81 Map Handling Module program */ #include <stdio.h>#include <string.h> #include "map.h" unsigned char rock_tbl[19][3] = {// screen{0x80,0x00,0x00},{0x40,0x00,0x00},{0x20,0x00,0x00},{0x10,0x00,0x00},{0x08,0x00,0x00},{0x04,0x00,0x00},{0x02,0x00,0x00},{0x01,0x00,0x00},{0x00,0x80,0x00},{0x00,0x40,0x00},{0x00,0x20,0x00},{0x00,0x10,0x00},// scroll{0x00,0x08,0x00},{0x00,0x04,0x00},{0x00,0x02,0x00},{0x00,0x01,0x00},{0x00,0x00,0x80},{0x00,0x00,0x40},{0x00,0x00,0x20},}; // Render buffers for smooth 2x2 tile scrolling (even/odd frames)#define RENDER_DX 22#define RENDER_DY 24unsigned char render_buf_even[RENDER_DY][RENDER_DX];unsigned char render_buf_odd[RENDER_DY][RENDER_DX]; int render_pos; // screen address #define D_FILE 0x400c#define D_DX (32 + 1)#define D_DY 24 unsigned char* getscrnaddr(int posy, int posx){#asm ld ix,2 add ix,sp ; push de ld d, (ix+2) ; d=posy ld e, (ix+0) ; e=posx call getscrnaddr; pop de ret getscrnaddr:; IN: D=posy, E=posx; OUT: HL= screen address; push bc ; HL = posy * 33 ld h, 0 ld l, d add hl, hl ; *2 add hl, hl ; *4 add hl, hl ; *8 add hl, hl ; *16 add hl, hl ; *32 ld b, 0 ld c, d add hl, bc ; +1 -> *33 ; HL += posx ld b, 0 ld c, e ; c=posx add hl, bc ; HL += D_FILE ld bc,(D_FILE) inc bc add hl, bc ; pop bc ret #endasm} void rock_render_at(int dy, int dx){ // 2×2 ブロックをbufp に書き込む char rock_chr[4][2] = { {0x81, 0x05},{0x80, 0x01}, {0x03, 0x00},{0x87, 0x04} }; int ptn; if (render_pos & 0x01) { ptn = (render_pos + dy*2) & 0x03; memcpy(&render_buf_odd[dy][dx], &rock_chr[ptn][0], 2); } else { ptn = (render_pos + dy*2) & 0x02; memcpy(&render_buf_even[dy][dx], &rock_chr[ptn][0], 2); }} void rock_render_all(void){ int dy, dx; unsigned char bit; int rockx, rocky; // 描画位置により、描画エリアを初期化 if (render_pos & 0x01) { memset(&render_buf_odd[0][0], 0x00, sizeof(render_buf_odd)); for (dy = 0; dy < RENDER_DY; dy++) { render_buf_odd[dy][0] = 0x07; render_buf_odd[dy][RENDER_DX-1] = 0x84; } } else { memset(&render_buf_even[0][0], 0x00, sizeof(render_buf_even)); for (dy = 0; dy < RENDER_DY; dy++) { render_buf_even[dy][0] = 0x82; render_buf_even[dy][RENDER_DX-1] = 0x81; } } // 岩を描画 for (dy = 0; dy < RENDER_DY; dy++) { bit = 0x80; rockx = 0; rocky = (render_pos + (dy*2)+1) / 4; for (dx = 1; dx <= RENDER_DY-2; dx++) { // bitが有効ならば岩を描画 if (rock_tbl[rocky][rockx] & bit) { rock_render_at(dy, dx); } // bitをShift。最後のbitまで参照したら次のテーブルへ移動 bit = bit >> 1; if (bit == 0) { bit = 0x80; rockx++; } } }} void map_init(void){ render_pos = 0; rock_render_all();} void map_scroll(int dy){ if (dy < 0) { if (render_pos == 0) { return; } render_pos--; } else { if (render_pos == 28) { return; } render_pos++; } rock_render_all();} void map_draw(void){ int dy; unsigned char *pos; pos = getscrnaddr(0, 4); if (render_pos & 0x01) { for (dy = 0; dy < D_DY; dy++) { memcpy(pos, &render_buf_odd[dy][0], RENDER_DX); pos += D_DX; } } else { for (dy = 0; dy < D_DY; dy++) { memcpy(pos, &render_buf_even[dy][0], RENDER_DX); pos += D_DX; } }}ーーーーmap.cpp#include <stdio.h>#include <string.h>#include "key.h"#include "map.h"void main(){ unsigned char key; unsigned char *pos; map_init(); map_draw(); while (1) { key = key_pressed(); if (key == 0) { continue; } if (key & KBIT_ENT) { break; } if (key & KBIT_UP) { map_scroll(-1); } else if (key & KBIT_DN) { map_scroll(1); } map_draw(); }}ここまでで、key.c、key.h、map.c、map.h、main.cが出そろいました。最後に下記コマンドでビルド(※)して、a.pファイルを作成します。 zcc +zx81 key.c map.c main.c -create-app※zccについては、以前の記事を参考にしてください。 z88dkセットアップ | ZX81の回想禄 - 楽天ブログ起動すると下記の表示になりますので、矢印の上下キーにて、1ドットスクロールする事を確認できます。ただ、書き換えに0.5秒近くかかっていて非常に遅いです。原因は全体のイメージを毎回更新しているのが原因のようなのでバッファーの表示開始位置をずらして、必要な箇所だけ更新する方法を検討したいと思います。
2026.04.02
コメント(0)
まずは、key.hとkey.cを作成しました。リアルタイムキー入力については、以前紹介したので詳細な説明は省きますがI/Oから直接キー入力を参照しています。 Mine Sweeper その3 | ZX81の回想禄 - 楽天ブログーーーーkey.h/* * ZX81 Keyboard Handling API header */ #ifndef _ZX81_KEY_H#define _ZX81_KEY_H // Bitmask flags for pressed keys (logical game input)#define KBIT_UP (1 << 0) // Up arrow (SHIFT + 7)#define KBIT_DN (1 << 1) // Down arrow (SHIFT + 6)#define KBIT_LT (1 << 2) // Left arrow (SHIFT + 5)#define KBIT_RT (1 << 3) // Right arrow (SHIFT + 8)#define KBIT_ENT (1 << 4) // ENTER#define KBIT_SPC (1 << 5) // SPACE // Key functionsunsigned char key_pressed(void); #endif // _ZX81_KEY_Hーーーーkey.c/* * ZX81 Keyboard Handling API program */ #include "key.h" /* * key_pressed: * Scan keyboard and return bitmask of pressed keys. */ unsigned char key_pressed(void){#asm key_pressed_read: ld a,0xf7 in a,(0xfe) ld d,a ld a,0xef in a,(0xfe) ld e,a ld a,0xbf in a,(0xfe) ld h,a ld a,0x7f in a,(0xfe) ld l,a key_pressed_bitset: ld a,0 key_pressed_up: bit 3,e jr nz,key_pressed_down or a,#KBIT_UP key_pressed_down: bit 4,e jr nz,key_pressed_left or a,#KBIT_DN key_pressed_left: bit 4,d jr nz,key_pressed_right or a,#KBIT_LT key_pressed_right: bit 2,e jr nz,key_pressed_enter or a,#KBIT_RT key_pressed_enter: bit 0,h jr nz,key_pressed_spc or a,#KBIT_ENT key_pressed_spc: bit 0,l jr nz,key_pressed_bitret or a,#KBIT_SPC key_pressed_bitret: ld h,0 ld l,a ret #endasm}いつもの矢印キーとSPCとEnterの6つのキーの押下げを判定可能です。
2026.04.01
コメント(0)
全12件 (12件中 1-12件目)
1


