今の実装

  • 「4日目」終了したあたりと仮定。

今日の料理

とりあえず256色使えることになってるんだし、最大限使えるようにするか。
一々COL8_848484とか書くのもいいけど、この定数名もあまり覚えやすくないよなぁ・・・
よし、RGB関数みたいな奴も作っちゃえ!

問題は1678万色の内どれを使うか。

と言うことでRGB関数を作りたいわけですが、ここで一つ問題が発生するわけです。
8bitカラーな画面の最大同時発光色の数は256色。それに比べ24bitで表現できる色の数は16777216色。6.6万倍近い差があるわけです。
さてどうすっか。

やはり1678万色の内の殆どを捨てるしかないわけで、例えばVESAの16bitモードでは
赤が5bit、緑が6bit、青が5bit等となっているわけです。
こういう上手い仕様を自分で作る必要があります。
ここで3種類の方法が思い浮かびます。

  • 単純に、赤が3bit、緑が3bit、青が2bit等として割り振る(ただしこの場合、青の表現能力が極端に劣る。)
  • 赤の強さを6段階、緑の強さを7段階、青の強さを6段階等として、256色のパレットをなるべく有効利用する(ただし計算が重そう。)
  • 初期パレットには最低限だけ揃えておいて、必要に応じて後から追加する(明らかに実装が面倒そう)

さてどれを実装したものか。

パレットの初期化は1度しか呼ばないので、全体的な速度はあまり変わらないはずです。
問題は24bit→8bitの変換作業。様々なプログラムの様々な位置から呼ばれる事になる予定なので、
この変換作業はなるべく速く実行出来るようにしておきたいものです。
24bit→8bit用の変換テーブルを手作業で作るのも面倒ですし。

最初の方法だとビット演算とシフト演算で簡単に作れそうですが、二つ目の方法だと良くて色の強さの範囲を確認しながらの仕分け作業、
適当に実装すると割り算が顔を出します。
三つ目の方法は柔軟性はありますが、24bit→8bitの変換作業を考えると最悪で、つまり一々検索かけるので遅くなるかもしれません。
脱線:割り算ってどのくらい遅いの?

しかし実は赤用の変換テーブル、緑用の変換テーブル、青用の変換テーブルをそれぞれ作っておいて

char RGB(unsigned char r,unsigned char g,unsigned char b){
    return table_r[r]+table_g[g]+table_b[b];
}

とすればいいので、変換テーブルの大きさは16MiBではなく768Byteだったりします。
ただ今回は、変換テーブルを使うにも、ちょっと定数埋め込みで保存するには重いし書くの面倒だし
メモリ管理を始める前にメモリの好き勝手な位置をいじくるわけにも行かないので変換テーブルは使わない方向で進めたいと思います。

MiB
単位。1MiB=1024KiB=1048576Byte。私がこっちの単位が好きな理由はメモリ管理のKiBの説明を参照の事。

実装1:bit単位で割り当て

まず仕様だけ先に適当に決めてしまおうか

色は8bitで、赤:下位3bit、青:上位2bit、緑:その他3bit(最初に考えた方法。)
つまり2進数で行くとフィールドはbbgggrrrになる。

さて、init_palette関数内で全部定数書くのは面倒くさいので、8bit→24bitの変換関数を書いてしまおう。

// 何も考えずに適当に作ってみた奴
// 引数の形式:bbgggrrr、戻り値の形式:00000000bbbbbbbbggggggggrrrrrrrr
int RGB8ToRGB24(unsigned char color){
    int r,g,b;
    r=(color&0x07)<<5;
    g=(color&0x38)<<2;
    b=color&0xc0;
    return (b<<16)+(g<<8)+r;
}

これで上手くいくかちょっと考えてみよう。RGB8ToRGB24(0xff)は白になる?
答えは「ならない」なんだ。別に灰色って意味じゃないよ?少し黄色っぽい白になる。
なぜならRGB8ToRGB24(0xff)は0xc0e0e0になるから。
それじゃこんなのはどうだろう?

int RGB8ToRGB24(unsigned char color){
    int r,g,b;
    r=(color&0x07)<<5;
    g=(color&0x38)<<2;
    b=color&0xc0;
    return ((b<<16)+(g<<8)+r)|0x3f1f1f;
}

今度はどうだろう?RGB8ToRGB24(0xff)は確かに白(0xffffff)になるみたいだ。
でもRGB8ToRGB24(0x00)が黒にならない。そこで思いついたのがこれ。

int RGB8ToRGB24(unsigned char color){
    int r,g,b;
    r=(color&0x07)*73>>1; // 73=01001001(b)
    g=(color&0x38)*73>>4;
    b=(color&0xc0)*85>>6; // 85=01010101(b)
    return (b<<16)+(g<<8)+r;
}

これはどういう仕組みだろう?まず一番上の行から考えてみよう。
color=bbgggrrrだから、R=color&0x07とするとR=00000rrr
rばっかりじゃ分かりにくいから、仮にR=00000abcとしておくと

 00000abc
*01001001←73の2進数表記
---------
abcabcabc←レジスタは32bitだから9bit分は余裕で入る

となる。そして上位8bitが必要だから右に一つシフトして
(color&0x07)*73>>1=abcabcab
なるほど、この結果は良さそうだ。abc=000なら答えも00000000になるし、abc=111なら答えも11111111になる。
しかも掛け算を使ってるから大体同じ割合で数値が大きくなるわけだ。
すると3番目の式はどうだろう?
color=bbgggrrrだから、B=color&0xc0とするとB=bb000000
bばっかりじゃ分かりにくいから仮にB=xy000000とすると

      xy000000
*     01010101←85の2進数表記
--------------
xyxyxyxy000000

それで、上位8bitが必要だから右に6bitシフトして
(color&0xc0)*85>>6=xyxyxyxy
なるほど。

さて、init_paletteをRGB8ToRGB24を使うように書き換えよう。

void init_palette(void){
    unsigned char table_rgb[256 * 3]; // 初期値がないからstaticにする意味がない
    int i,c;
    for(i=0;i<256;i++){
        c=RGB8ToRGB24(i);
        table_rgb[i*3  ]= c     &0xff;
        table_rgb[i*3+1]=(c>>8 )&0xff;
        table_rgb[i*3+2]=(c>>16)&0xff;
    }
    set_palette(0, 255, table_rgb);
    return;
}

なんか前よりすっきりしたような・・・。

さて本題のRGB関数を作ろうか。

// 戻り値の形式:bbgggrrr
char RGB(unsigned char r,unsigned char g,unsigned char b){
    return (b&0xc0)+((g>>2)&0x38)+((r>>5)&0x07);
}

・・・予想以上に簡単に書けてしまった。
このくらいの内容と記述量ならわざわざ「関数として」存在させておかなくてもいいかな。

//24bitカラーを描画用カラーに変換するマクロ
#define RGB(r,g,b) (((b)&0xc0)+(((g)>>2)&0x38)+(((r)>>5)&0x07))


  • 実はシフト演算はビット演算より演算順位が高いので、下でも問題ない。私は読みにくいと感じるが。
    #define RGB(r,g,b) (((b)&0xc0)+((g)>>2&0x38)+((r)>>5&0x07))

実装2:bit単位ではない割り当て方法について

また仕様を先に決めてしまおう。

色は0〜251で、赤:色を6で割った余り、緑:色を6で割った奴を7で割った余り、青:色を42で割った奴を6で割った余り(2番目の方法。)

さて8bit→24bitの変換関数を書いてしまおう。

// 何も考えずに適当に作ってみた奴
// 戻り値の形式:00000000bbbbbbbbggggggggrrrrrrrr
int RGB8ToRGB24(unsigned char color){
    int r,g,b;
    r=color%6;
    g=color/6%7;
    b=color/42%6;
    return (b<<16)+(g<<8)+r;
}
演算記号「%」
剰余算。割った余りを求める。演算順位は*や/と同じ。

おっとこれじゃ駄目だ。例えばrは0〜255の値をとる必要があるのに、0〜5の値しかとってない。
255で掛けて5で割っておこう。いや、51を掛ければいいのか。
gはどうしよう。255で掛けて6で割ったら42.5倍だ。255で掛けて3で割って(つまり85を掛けて)、更に2で割ればいいのかな。

int RGB8ToRGB24(unsigned char color){
    int r,g,b;
    r=color%6*51;
    g=color/6%7*85>>1;
    b=color/42%6*51;
    return (b<<16)+(g<<8)+r;
}

あ、colorが252とかの時上手く動かないからおかしな値が来た時は弾かなきゃ。

int RGB8ToRGB24(unsigned char color){
    if(color>251) return -1; // 存在しない色番号
    int r,g,b;
    r=color%6*51;
    g=color/6%7*85>>1;
    b=color/42%6*51;
    return (b<<16)+(g<<8)+r;
}

うん、これで上手く動きそうだ。

さて、RGB関数を作るか。こんな感じかな?

char RGB(unsigned char r,unsigned char g,unsigned char b){
    return (r/43)+(g/37)*6+(b/43)*42; // 0<=x<=255→0<=x/43<6,0<=x/37<7
}

43と37は、それぞれ255/6と255/7を切り上げた数値です。なので255以下の正の整数をこいつらで割った時に0〜6弱、0〜7弱になったんです。

実装3:可変パレット

やっぱり、せっかく動的にパレットを弄れるんだから途中から色を追加できてもいいじゃないか。
これで好きな色を(最大同時発光色が256色に収まる範囲で)使えるように出来るに違いない。

色を追加する関数はこんな感じでいいかなぁ?

void set_palette1(int num,unsigned char r,unsigned char g,unsigned char b){
    int eflags;
    eflags = io_load_eflags();
    io_cli();
    io_out8(0x03c8,num);
    io_out8(0x03c9,r/4);
    io_out8(0x03c9,g/4);
    io_out8(0x03c9,b/4);
    io_store_eflags(eflags);
    return;
}

カラーパレットとそのパレットに入ってる色の数を記憶する変数はグローバル変数にしておこう。
あとで何度もアクセスしたいからね

int colorpalette_num;

// 標準カラーパレット
char table_rgb[256 * 3] = {
    0x00,0x00,0x00,    // 黒
    0xff,0xff,0xff,    // 白
};

void init_palette(void){
    colorpalette_num=2;
    set_palette(0,1,table_rgb);
    return;
}

さて8bit→24bitの変換作業は要らないし、RGB関数を作ってしまおう。
スケルトンはこんな感じかなぁ?

int RGB(unsigned char r,unsigned char g,unsigned char b){
    for(i=0;i<colorpalette_num;i++){// 似た色の検索
        /* 要求された色がどの位既存の色と似ているかを調べる */
        if(/* 同一の色なら */){
            return /* その色を返す */;
        }else if(/* 今までに調べたどの色より似ているなら */){
            /* その色の色番号と元の色との距離とを記録 */
        }
    }
    if(colorpalette_num==256){ // カラーテーブルにこれ以上追加できないとき
        return /* 記録しておいた、最も近い色を返す */;
    }else{ // カラーテーブルに追加できるとき
        /* set_palette1で要求された色を追加して */
        /* カラーパレットに、追加された色を記録して */
        colorpalette_num++; // 保存している色の総数が増えた事を記録して
        return colorpalette_num-1; // 新たに追加した色の色番号を返す
    }
}

う〜ん・・・、「2つの色がどの位似ているか」をどうやって表現しよう・・・。

今回は、色の値同士を引き算して、絶対値で括って、全部足して、その値が0からどの位離れてるかで表現するという方法で行きたいと思います。
要するにabs(table_rgb[i*3]-r)+abs(table_rgb[i*3+1]-g)+abs(table_rgb[i*3+2]-b)みたいな奴を求めて、
0なら同じ色、0に近ければ似てる色、という訳です。

絶対値
負の値を正の値に、正の値はそのままに返す関数の名前。abs(3)=3,abs(-5)=5

運のいい事に、abs関数は標準ライブラリに用意されているようです。

int abs(int n);

さて実装。こんな感じかな?

int RGB(unsigned char r,unsigned char g,unsigned char b){
    int i;
    int howfar,mostnear=256; // どの位既存の色と似ているかを記憶しておくための変数
    int nearcolor=-1; // 最も近い色の色番号
    // 下位2bitは描画するときに反映されないため、2bit分右シフトしておく。
    r>>=2;
    g>>=2;
    b>>=2;
    for(i=0;i<colorpalette_num;i++){// 似た色の検索
        howfar=abs((table_rgb[i*3  ]>>2)-r)+
               abs((table_rgb[i*3+1]>>2)-g)+
               abs((table_rgb[i*3+2]>>2)-b);
        if(howfar==0){// 同じ色が見つかったら
            return i; // その色を返す
        }else if(mostnear>howfar){// 今までに調べたどの色より似ているなら
            // その色の色番号と元の色との距離とを記録
            nearcolor=i;
            mostnear=howfar;
        }
    }
    if(colorpalette_num==256){ // カラーテーブルにこれ以上追加できないとき
        return nearcolor; // 記録しておいた、最も近い色を返す
    }else{ // カラーテーブルに追加できるとき
        set_palette1(colorpalette_num,r<<2,g<<2,b<<2); // set_palette1で要求された色を追加して
        /* カラーパレットに、追加された色を記録して */
        table_rgb[colorpalette_num*3  ]=r<<2;
        table_rgb[colorpalette_num*3+1]=g<<2;
        table_rgb[colorpalette_num*3+2]=b<<2;
        colorpalette_num++; // 保存している色の総数が増えた事を記録して
        return colorpalette_num-1; // 新たに追加した色の色番号を返す
    }
}

予想通りというかなんと言うか、物凄く長くなってしまった。

最後にやる事

確認も重要だね
(HariMain内部)

boxfill8(vram,xsize,RGB(0x00,0x84,0x84), 0      ,0       ,xsize- 1,ysize-29);
boxfill8(vram,xsize,RGB(0xc6,0xc6,0xc6), 0      ,ysize-28,xsize- 1,ysize-28);
boxfill8(vram,xsize,RGB(0xff,0xff,0xff), 0      ,ysize-27,xsize- 1,ysize-27);
boxfill8(vram,xsize,RGB(0xc6,0xc6,0xc6), 0      ,ysize-26,xsize- 1,ysize- 1);

boxfill8(vram,xsize,RGB(0xff,0xff,0xff), 3      ,ysize-24,59      ,ysize-24);
boxfill8(vram,xsize,RGB(0xff,0xff,0xff), 2      ,ysize-24, 2      ,ysize- 4);
boxfill8(vram,xsize,RGB(0x84,0x84,0x84), 3      ,ysize- 4,59      ,ysize- 4);
boxfill8(vram,xsize,RGB(0x84,0x84,0x84),59      ,ysize-23,59      ,ysize- 5);
boxfill8(vram,xsize,RGB(0x00,0x00,0x00), 2      ,ysize- 3,59      ,ysize- 3);
boxfill8(vram,xsize,RGB(0x00,0x00,0x00),60      ,ysize-24,60      ,ysize- 3);

boxfill8(vram,xsize,RGB(0x84,0x84,0x84),xsize-47,ysize-24,xsize- 4,ysize-24);
boxfill8(vram,xsize,RGB(0x84,0x84,0x84),xsize-47,ysize-23,xsize-47,ysize- 4);
boxfill8(vram,xsize,RGB(0xff,0xff,0xff),xsize-47,ysize- 3,xsize- 4,ysize- 3);
boxfill8(vram,xsize,RGB(0xff,0xff,0xff),xsize- 3,ysize-24,xsize- 3,ysize- 3);

忘れないうちにRGB関数とかの宣言も書いておこう。

色を混ぜて表現してみる

コメントでKさんが「色を混ぜる方法も説明して」と言ってるように見えてしまった。
適当に実装したら上手く動いてしまったのでそのソースをとりあえず公開。
(主に実装3に追加)

// c1、c2は混ぜる二つの色。patternは色をどの比率で混ぜるかに関する引数で、
// 0ならc1のみ、4ならc2のみ、その中間は例えば1ならc1が多めとなる。
void boxfill8_2color(unsigned char *vram, int xsize, unsigned char c1,unsigned char c2,int pattern, int x0, int y0, int x1, int y1)
{
    int x, y;
    unsigned char k[2],table[4]={3,1,0,2};
    if(pattern>4) pattern=4;
    if(pattern<0) pattern=0;
    if(c1==c2||pattern==0){ // 同じ色が来たら混ぜる必要が無いのでもっと単純な関数に処理を渡す。
                           // ここで渡す色がc1なので、ついでにパターンが0だった時についても判定。
        boxfill8(vram,xsize,c1,x0,y0,x1,y1);
        return;
    }else if(pattern==4){ // パターンが4の時。
        boxfill8(vram,xsize,c2,x0,y0,x1,y1);
        return;
    }
    k[0]=c1;
    k[1]=c2;
    for (y = y0; y <= y1; y++){
        for (x = x0; x <= x1; x++)
            // この少し複雑そうな計算式は、x-x0とy-y0の偶奇でtableのどの値を使うかを決めて、それに4よりは小さい値を足して、
            // 2bit右シフトしている。4より小さい値を足した時点で4以上になっていれば1、そうでなければ0がkのインデックスに使われる。
            vram[y * xsize + x] = k[(table[((x-x0)&1)+(((y-y0)&1)<<1)]+pattern)>>2];
    }
    return;
}

使う時はこんな感じ

boxfill8_2color(vram,xsize,RGB(0x00,0x88,0x88),RGB(0x00,0x80,0x80),2, 0      ,0       ,xsize- 1,ysize-29);
boxfill8_2color(vram,xsize,RGB(0xc8,0xc8,0xc8),RGB(0xc0,0xc0,0xc0),2, 0      ,ysize-28,xsize- 1,ysize-28);
boxfill8_2color(vram,xsize,RGB(0xff,0xff,0xff),RGB(0xff,0xff,0xff),0, 0      ,ysize-27,xsize- 1,ysize-27);
boxfill8_2color(vram,xsize,RGB(0xc8,0xc8,0xc8),RGB(0xc4,0xc4,0xc4),2, 0      ,ysize-26,xsize- 1,ysize- 1);

boxfill8_2color(vram,xsize,RGB(0xff,0xff,0xff),RGB(0xff,0xff,0xff),0, 3      ,ysize-24,59      ,ysize-24);
boxfill8_2color(vram,xsize,RGB(0xff,0xff,0xff),RGB(0xff,0xff,0xff),0, 2      ,ysize-24, 2      ,ysize- 4);
boxfill8_2color(vram,xsize,RGB(0x00,0x88,0x88),RGB(0x00,0x80,0x80),2, 3      ,ysize- 4,59      ,ysize- 4);
boxfill8_2color(vram,xsize,RGB(0x00,0x88,0x88),RGB(0x00,0x80,0x80),2,59      ,ysize-23,59      ,ysize- 5);
boxfill8_2color(vram,xsize,RGB(0x00,0x00,0x00),RGB(0x00,0x00,0x00),0, 2      ,ysize- 3,59      ,ysize- 3);
boxfill8_2color(vram,xsize,RGB(0x00,0x00,0x00),RGB(0x00,0x00,0x00),0,60      ,ysize-24,60      ,ysize- 3);

boxfill8_2color(vram,xsize,RGB(0x00,0x88,0x88),RGB(0x00,0x80,0x80),2,xsize-47,ysize-24,xsize- 4,ysize-24);
boxfill8_2color(vram,xsize,RGB(0x00,0x88,0x88),RGB(0x00,0x80,0x80),2,xsize-47,ysize-23,xsize-47,ysize- 4);
boxfill8_2color(vram,xsize,RGB(0xff,0xff,0xff),RGB(0xff,0xff,0xff),0,xsize-47,ysize- 3,xsize- 4,ysize- 3);
boxfill8_2color(vram,xsize,RGB(0xff,0xff,0xff),RGB(0xff,0xff,0xff),0,xsize- 3,ysize-24,xsize- 3,ysize- 3);

長いので別の関数を追加。

void boxfill8_color(unsigned char *vram, int xsize,unsigned char r,unsigned char g,unsigned char b, int x0, int y0, int x1, int y1)
{
    int cu=((r+3)&0xfc)+(((g+3)&0xfc)<<8)+(((b+3)&0xfc)<<16);
    int cd=(r&0xfc)+((g&0xfc)<<8)+((b&0xfc)<<16);
    if(cu==cd){ // 補正する必要が無い
        boxfill8(vram,xsize,RGB(r,g,b),x0,y0,x1,y1);
    }else{
        if(r>=0xfd) r=0xfc;
        if(g>=0xfd) g=0xfc;
        if(b>=0xfd) b=0xfc;
        boxfill8_2color(vram,xsize,RGB(r,g,b),RGB(r+((r&2)<<1),g+((g&2)<<1),b+((b&2)<<1)),2,x0,y0,x1,y1);
    }
    return;
}

これでいくらか見やすくなったかな。

boxfill8_color(vram,xsize,0x00,0x84,0x84, 0      ,0       ,xsize- 1,ysize-29);
boxfill8_color(vram,xsize,0xc6,0xc6,0xc6, 0      ,ysize-28,xsize- 1,ysize-28);
boxfill8_color(vram,xsize,0xff,0xff,0xff, 0      ,ysize-27,xsize- 1,ysize-27);
boxfill8_color(vram,xsize,0xc6,0xc6,0xc6, 0      ,ysize-26,xsize- 1,ysize- 1);

boxfill8_color(vram,xsize,0xff,0xff,0xff, 3      ,ysize-24,59      ,ysize-24);
boxfill8_color(vram,xsize,0xff,0xff,0xff, 2      ,ysize-24, 2      ,ysize- 4);
boxfill8_color(vram,xsize,0x84,0x84,0x84, 3      ,ysize- 4,59      ,ysize- 4);
boxfill8_color(vram,xsize,0x84,0x84,0x84,59      ,ysize-23,59      ,ysize- 5);
boxfill8_color(vram,xsize,0x00,0x00,0x00, 2      ,ysize- 3,59      ,ysize- 3);
boxfill8_color(vram,xsize,0x00,0x00,0x00,60      ,ysize-24,60      ,ysize- 3);

boxfill8_color(vram,xsize,0x84,0x84,0x84,xsize-47,ysize-24,xsize- 4,ysize-24);
boxfill8_color(vram,xsize,0x84,0x84,0x84,xsize-47,ysize-23,xsize-47,ysize- 4);
boxfill8_color(vram,xsize,0xff,0xff,0xff,xsize-47,ysize- 3,xsize- 4,ysize- 3);
boxfill8_color(vram,xsize,0xff,0xff,0xff,xsize- 3,ysize-24,xsize- 3,ysize- 3);

注意事項(懸案事項)

ー汰3で、「同じ色がなくてカラーパレットも満タンだよ」という時に何も考えずに最も近い色を返していますが、
例えばカラーパレットが漏れなく青や青緑、それに緑で満タンだ という時には赤を要求しても黒があれば黒、そうでなければ青か緑がおそらく返ってきます。
既存の色に良く似ている色がない時は、1色要らない色を探してそれを置換するのがいいのですが、
色の探し方には私が思いつく限りでは2種類あって、

  • 今現在最も使われていない色をなんとか言い当てる方法
  • 色の密度が濃い場所の色を探して、その色を言い当てる方法(要するにその色がなくなってもよく似た色でカバーできるような場所を探す。)

という方法なのですが、どちらにしてもちょっと長くなってしまったので、「皆さんへの宿題」ということでいいでしょうか?
また、最も似ている色とどのくらい似ていなかったら置換しに行くか、その閾値を設定するのも難しいと思うのですが・・・。

多分これは最良の方法ではありません。必ずロジックレベルでもっといいものがあるはずです。
私は今のところ思いつきませんが、皆さんもあれこれ考えてみてください。
できればその方法をコードに起こすなり他人に説明できる程度に詳しく外部仕様を紙に書き出すなりしてください。
私はその方法を見てみたいです。

0豈確認はしてるのですが、あまり頭のよくない私が書いたコードにバグがないとは考えられません。
コードをコピペする前に「何がしたくて、どうすれば実装できそうか」を理解して、一応コードを目で追ってみてください。

簡易投票(今回のカラーパレットの管理に関する記事について。)

どの程度役に立った?

選択肢 投票
全く。 1  
ほんのわずかに。あまり。 1  
やや、少し。 1  
まぁまぁ、割と。 2  
かなり、とても 1  
非常に、凄く 1  

説明は適切?

選択肢 投票
説明過剰 0  
やや過剰 0  
適切 1  
やや不適切 0  
分かりにくい 0  
全く分からない 0  

ソースコード中のコメントは?

選択肢 投票
多すぎる 0  
やや多い 0  
やや多いが適切 0  
適切で適量 0  
やや少ないが適切 0  
やや少ない 0  
少なすぎる 0  

脱線:割り算ってどのくらい遅いの?

ところで割り算は遅いと良く聞きますが、実際どれくらい遅いのかと言うと
http://kmkz.jp/mtm/mag/code/asm.html
あたりに古い資料が載ってるのですが、どうも8bit同士の符号なし整数除算でさえ17clockからかかるようです。
それに比べるとAND、OR、SHL、SHRあたりは1clockで実行できるようです。

clock
CPU内での時間の単位の一種。つまりこの時間が短ければ短いほど、速く実行できるというわけである。
1clockが何秒になるかはCPUの動作周波数(MHzとかGHzとかで表す奴)に依存する。
SHL、SHR
それぞれ左論理シフト、右論理シフト。符号なし整数型に対して<<や>>を実行する。
符合あり整数型だとSAR、SALになり、こいつらは算術シフトと言う。

Pentium4の場合はIntel本家から「IA-32 インテルR アーキテクチャー最適化リファレンス・マニュアル」「AP-485 インテルR プロセッサーの識別と CPUID 命令」を
落としてきて、前者の「付録C」表C-8と後者の「CPUID命令の出力」表4を参照すると、どのCPUでどの命令が遅いのか一部確認できます。慣れないと難しいですが。
資料番号はそれぞれ、248966-013JA、241618-029JA(後者はこの番号でググるとPDFへすぐ飛べます。前者もアドレスをftpからhttpに変えればいけました。)

ちなみに、定数除算の場合は割り算を掛け算に変換する方法を使えばそれだけで数十クロック浮いたりします。
(古い表の値を使うと、整数除算(46clock)が整数定数乗算(9clock)+α(2〜3clock)になったりとか)
この計算方法は少し説明しにくいのですが。「整数除算 高速化」あたりでググれば出てくるかもしれません。
書籍だと「ハッカーのたのしみ - 本物のプログラマはいかにして問題を解くか」とかが詳しいです。

このページの履歴

  • 2008年8月23日 - 出現
  • 2008年8月24日 - leaプロジェクトのサブページ(現在のページ)に移動
  • 2008年8月25日 - 2色を混ぜる方法について追記(bolfill8改造版を実装)

コメント

  • Sourceさんが25日目まで読んだかどうか分からないので念のために書きますが、色の扱いについては、25日目で一応一つ提案してあります。ここに挙がっているのはいずれも僕の案とは違うので面白いと思います。本書での方法と組み合わせられたらもっと面白いかもしれません。 -- K 2008-08-24 (日) 21:07:02
  • 実はその件なのですが、後半は流し読みしつつ頭に入れてなかったので私も昨日気が付いたんです(やけにグラフィカルなcolor1を見て)。実装2とは「既存のパレットを捨てるか保守するか」と使える色の数が主に違ってますね。 -- Source 2008-08-24 (日) 21:42:31
  • color1ではパレットへの工夫はほとんどなくて「色をまぜる」という方法で工夫しています。もしこれをSourceさんの方法と組み合わせて使うとすると、よく使う色に近い色を集めてそれらを混ぜることで、より表現力を高められそうです。本の方法ではそういう工夫をしないで等間隔(?)に色を用意しているので、色を混ぜる計算を大幅に簡単化・高速化できています。 -- K 2008-08-25 (月) 01:02:19
  • それはcolor2への工夫では・・・?2x2ピクセルを1単位とした場合に2色を混色する方法について少し書いてみます。 -- Source 2008-08-25 (月) 08:36:44
  • すみませんそうです。書き間違えました。color2のことです。 -- K 2008-08-25 (月) 14:25:11

コメントお名前NameLink

リロード   新規 編集 差分 添付   トップ 一覧 検索 最終更新 バックアップ   ヘルプ   最終更新のRSS
Last-modified: 2011-11-26 (土) 11:39:02 (3498d)