雑記

いち情報系大学生

OS学習メモ10(30日OS自作入門10日目)

通信制限のためか画像アップロードできないので後ほど修正加えます)
※修正しました(5/24 8:40)

さて10日目,ようやく3分の1ですね。やっていきしょう。
最初の方でちょっとメモリ管理の続きでmallocっぽいのを作ったり、切り上げ切り捨てのやり方の話とかの話もあるのですが、ちょっとその辺りはスキップしまして早速8日目の続きからいきたいのですが、8日目の記事書いたのだいぶ前なので、ちょっと振り返るところから。

8日目の所でマウスカーソルが動くようになったのですが、その処理を「カーソルの上に色を上塗りして見えなくする→再びカーソル描画」とやっていたために、マウスを動かす度に画面を破壊してしたところで終わりましたね。

f:id:unyamahiro:20180505121905p:plain

さて今回はこれを解決するために重ね合わせをやっていこうという所です。
イメージとしてはまさにお絵かきする時とかに使う”レイヤー”ですね。
ここでは”透明な下敷きにかいた絵”を重ねると表現されています。

というわけで、まずはその下敷きとやらを定義します。

struct SHEET{
	unsigned char *bar;
	int bxsize,bysize,vx0,vy0,col_inv,height,flags;
}

(そういえば、今回は構造体があたまの中でごちゃごちゃしたのでいつにも増して図が多いです。自分の備忘録のつもりですが、誰かの理解の助けになったりすれば嬉しいです)

構造体SHEETのイメージはこんな感じ。
f:id:unyamahiro:20180524083259p:plain
これで一枚分の情報が出来ましたね。
では次に、複数枚の下敷きを管理するための”下敷き管理用構造体”を作ります。

struct SHTCTL{
	unsigned char *vram;
	int xsize,ysize,top;
	struct SHEET *sheets[256];
	struct SHEET sheets0[256];
}

f:id:unyamahiro:20180524083127p:plain

sheets0で一枚一枚に下敷きの情報を保持しておきます。この時、どの下敷きが上にあってどれが下かとかは関係なくバラバラに格納されているので、それを下層から順に並べて番地だけを並べた構造体配列がsheetsです。イメージを見たほうが早い気がします。
f:id:unyamahiro:20180524083653p:plain


これで管理の準備が整いました。この下敷き管理構造体を記憶するためのメモリを確保してぱぱっと値を設定。flagはそのシートが未使用であるか否かを示していて、はじめは全て0にしておいて使うときには1にします。

struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
{
	struct SHTCTL *ctl;
	int i;
	ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));
	if (ctl == 0) {goto err;}
	ctl->vram = vram;
	ctl->xsize = xsize;
	ctl->ysize = ysize;
	ctl->top = -1; 
	for (i = 0; i < MAX_SHEETS; i++) {ctl->sheets0[i].flags = 0; }
err:
	return ctl;
}

次に未使用の下敷きを持ってくる関数。
気になるのは高さを-1にしている所だと思いますが、これは後から設定するからここでは気にしなくても大丈夫です。

struct SHEET *sheet_alloc(struct SHTCTL *ctl)
{
	struct SHEET *sht;
	int i;
	for (i = 0; i < MAX_SHEETS; i++) {
		if (ctl->sheets0[i].flags == 0) {
			sht = &ctl->sheets0[i];
			sht->flags = SHEET_USE; 
			sht->height = -1; 
			return sht;
		}
	}
	return 0;	
}

次に下敷きの高さを設定する関数です。

void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height){
	int h, old = sht->height; 

	if (height > ctl->top + 1) {height = ctl->top + 1;}
	if (height < -1) {height = -1;}
	sht->height = height; 

	if (old > height) {	
		if (height >= 0) {
			for (h = old; h > height; h--) {
				ctl->sheets[h] = ctl->sheets[h - 1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht;
		} else {	
			if (ctl->top > old) {
				for (h = old; h < ctl->top; h++) {
					ctl->sheets[h] = ctl->sheets[h + 1];
					ctl->sheets[h]->height = h;
				}
			}
			ctl->top--; 
		}
		sheet_refresh(ctl); 

	} else if (old < height) {
		if (old >= 0) {
			for (h = old; h < height; h++) {
				ctl->sheets[h] = ctl->sheets[h + 1];
				ctl->sheets[h]->height = h;
			}
			ctl->sheets[height] = sht;
		} else {	
			for (h = ctl->top; h >= height; h--) {
				ctl->sheets[h + 1] = ctl->sheets[h];
				ctl->sheets[h + 1]->height = h + 1;
			}
			ctl->sheets[height] = sht;
			ctl->top++; 
		}
		sheet_refresh(ctl); 
	}
	return;
}

プログラムはちょっと長いですが、こでまでのプログラムをなんとか読めるだけの力がついていればこれも読みこなせるはずです。最初の頃はつらいなぁとおもうわけですが、それでもめげずに頑張っていると、どんどん読む能力が上達してきます。

ということなので丁寧に読んでいきましょう。
このsheet_updown関数では、ある下敷きの高さを、引数で渡しているhightの位置に設定します。

int h, old = sht->height; 

もともとどこのレイヤにいたのかをoldに。

if (height > ctl->top + 1) {height = ctl->top + 1;}
if (height < -1) {height = -1;}
sht->height = height; 

一番上のレイヤがctl(管理用構造体)のtopなので、hightをtopよりも上に指定する必要はないし、同様に低すぎる値にする必要もありません。最下層を0ではなく−1にしていますが、これは下敷きを非表示にする時のためです(地下1階みたいなイメージ?)

if (old > height) {	
	if (height >= 0) {
		for (h = old; h > height; h--) {
			ctl->sheets[h] = ctl->sheets[h - 1];
			ctl->sheets[h]->height = h;
		}
		ctl->sheets[height] = sht;
	} else {	
		if (ctl->top > old) {
			for (h = old; h < ctl->top; h++) {
				ctl->sheets[h] = ctl->sheets[h + 1];
				ctl->sheets[h]->height = h;
			}
		}
		ctl->top--; 
	}
	sheet_refresh(ctl); 
} 

細かく分けてみていきます。

if (old > height) {	

old>hightはつまり、下敷きの”レイヤを下げる”場合です。

if (height >= 0) {
	for (h = old; h > height; h--) {
		ctl->sheets[h] = ctl->sheets[h - 1];
		ctl->sheets[h]->height = h;
	}	
	ctl->sheets[height] = sht;
}

ここは、コードと図を一緒に見てください。
f:id:unyamahiro:20180524083846p:plain
(ここでは青を一番下に持っていく例で考えます)
一回のループで↓をやります。
f:id:unyamahiro:20180524083926p:plain
最終的にはこうなります。
f:id:unyamahiro:20180524084010p:plain
下敷きを非表示にした場合はtopを1減らすのを忘れないようにしましょう。
並び順がちゃんと整理されたので、これに基づき再描画する処理が後に続きます。

ここまでわかれば残りの部分は”レイヤーを上げる処理”で、ほとんどやっていることは同じなのでスラスラと読めるはずです。

で、保留した再描画の処理がこちら

void sheet_refresh(struct SHTCTL *ctl){
	int h, bx, by, vx, vy;
	unsigned char *buf, c, *vram = ctl->vram;
	struct SHEET *sht;
	for (h = 0; h <= ctl->top; h++) {
		sht = ctl->sheets[h];
		buf = sht->buf;
		for (by = 0; by < sht->bysize; by++) {
			vy = sht->vy0 + by;
			for (bx = 0; bx < sht->bxsize; bx++) {
				vx = sht->vx0 + bx;
				c = buf[by * sht->bxsize + bx];
				if (c != sht->col_inv) {
					vram[vy * ctl->xsize + vx] = c;
				}
			}
		}
	}
	return;
}

下敷きを下層から描いていきます。
OS自作入門の初めの方で作ったboxfill関数とやってることは同じです。

あとは、下敷きをずらしたり,使い終わった下敷きを解放したり,といった関数をちょこっと追加してやると動きます。


平井堅「トドカナイカラ」いい曲です)

だいぶGUIっぽく、OSっぽくなってきましたね!
画像、くどかったかも...