256バイトのアセンブラー制御勾配ヘリックス(k29)

この記事では、数百バイトのアセンブラーグラフィカルアプリケーションの作成について説明します。 420バイトの本格的な作業バージョンを作成した後、これらすべてを256バイトに詰め込むために汗をかかなければなりませんでした。 あなたがビデオで見ることができる結果。 この記事では、作成プロセスと機能の一般原則について説明します。



警告:てんかんの発作に苦しんでいる場合-見ないでください。





Win7およびVistaでは動作しません。 Windows XP / 2000/98が必要です。



実行可能ファイルのダウンロード: DropBoxのk29.com (256バイト)

ソースをダウンロード: DropBoxのk29.asmFASMでコンパイル)



コントロールキー:

1. R、G、B-色成分の有効化と無効化

2. <-、SPACE、->-回転の方向と速度を変更します

3.上、下-スパイラルのスケールを変更します

4. 0,1,2,3,4,5,6,7,8,9-スパイラルの分岐数を変更します

5. ESC-終了



エピローグ



かなり長い間、この記事は私のドラフトにありました。 まだBloggerの下書きの下書きにあります。 そして今日、私が今それを終えなければ、それは決して起こらないと決めました。 終わりました、最後に少し中断して終わりました)



1.はじめに



私は常にデモパーティー、特に数百バイト単位のカテゴリーに興味があります。 DirectX / OpenGLを使用して64キログラムでプログラムを作成することと、まったく別のこと-512/256/128バイトで、ビデオメモリを直接使用することなどです ここでは、すでに直感的なレベルでのアセンブラーの知識と理解が必要です。 ボリュームの観点からコードを最適化できることが必要です。したがって、機械プログラミング言語のすべての微妙な点を理解し、考慮する必要があります。 このようなことをしようとします。 以前アセンブラーで書いたことはありませんでしたが、既存のコードを理解することができました。 この記事は 、私のようなアセンブラーの初心者を対象としています。 したがって、私は問題の完全な解決策のふりをしません。



2.ターゲットの選択



次に、タスクを考え出し、それを実装する必要があります。 私はすぐに、パスカルでの学年で書かれた小さなプログラムを思いつきました。 このプログラムは不名誉に簡単でした(2つのコード画面-50行)が、同時に配信されました。 このプログラムは、グラフィックモード(320x200x256)フルスクリーンで回転するらせんを描きました。 スムーズな色の変更のために、256色すべてが含まれていました。 目に見える再描画なしで螺旋が動くのは驚くべきことでした。 これは、回転速度ではない場合、複数のビデオページを使用することで説明できます。 明らかに、らせんを描くには、実数による計算が必要であり、これも大幅な遅延をもたらします。 スパイラルは毎秒3〜5回転の速度で回転しました(図2.1を参照)。









[図 2.1。 3つの「手」を持つらせんのスナップショット]



そして、全体は、プログラムが開始されたときに、スパイラルが一度だけ描かれたことでした。 らせんを描いた後、プログラムはパレットの色を周期的にシフトしました。パレットはすぐに画面に表示されました。 メイン機能に加えて、プログラムはスパイラルの色の変更と回転方向の変更をサポートしました。 8色のみ:

0 #000000 黒(スパイラルは見えない)
1 #0000FF
2 #00FF00 ブライトグリーン
3 #00FFFF ターコイズ
4 #Ff0000
5 #FF00FF パープル
6 #FFFF00 黄色
7 #FFFFFF


RGBカラーモデルは画面表示に使用されるため、これらの8色は対応する色成分を組み合わせることで取得できます(3データビットは8つの異なる値をエンコードできます)。 プログラムは、キー「R」、「G」、および「B」を使用して、対応する色成分をオン/オフにしました。



プログラムはTurbo Pascal 7.0開発環境のPascalで作成されました。 プログラムの一部の機能は、アセンブラーの挿入として実装されました。 たとえば、パレットから特定の要素にRGB値を設定する機能。 学校の時間コード(人の精神を傷つけないようにフォーマットが変更されました):



program P_16_B_4; uses crt,graph,MUSE_OTH,MT_MAIN; const koef1=3;{   } koef2=3;{   } var gd,gm,gmx,gmy,i,flag,x0,y0,x,y:integer; r,alpha:extended; k,int:longint; key:char;rr,gg,bb:byte; ints:string; mas:array[0..255,1..3] of byte; BEGIN gd:=installuserdriver('SVGA256m.BGI',NIL);gm:=2; initgraph(gd,gm,''); gmx:=getmaxx; gmy:=getmaxy; flag:=1;k:=1; for i:=0 to 255 do begin setRGBpalette(i,k,k,k); mas[i,1]:=k;mas[i,2]:=k;mas[i,3]:=k; k:=k+flag; if k=63 then flag:=-1 else if k=0 then flag:=1; end; setcolor(63); settextstyle(1,horizdir,2); settextjustify(centertext,centertext); outtextxy(gmx div 2,gmy div 2-textheight('!<06@')*2 div 2, '!<06@ freeware'); outtextxy(gmx div 2,gmy div 2+textheight('!<06@') div 2, '! ! ! Press "R", "G", "B", " " or "Esc" ! ! !'); x0:=gmx div 2; y0:=gmy div 2; r:=400; repeat alpha:=0; repeat x:=round(x0+r*cos(alpha/180*Pi)); y:=round(y0-r*sin(alpha/180*Pi)); putpixel(x,y,round(r*koef2+alpha*256/360*koef1/2) mod 128); alpha:=alpha+20/(r+1); until alpha>=360; if keypressed then halt; r:=r-1; until r<=0; k:=1;flag:=-1;rr:=1;gg:=1;bb:=1;int:=0; repeat str(int SHR 2,ints); while byte(ints[0])<4 do insert('0',ints,1); if int and 3=0 then SAVE_MONITOR((gmx+1) div 2-75,(gmy+1) div 2-75,(gmx+1) div 2+74, (gmy+1) div 2+74,'c:\AVATAR\'+ints+'.bmp'); if keypressed then begin key:=readkey; if key=' ' then flag:=-flag else if upcase(key)='R' then rr:=not(rr) and 1 else if upcase(key)='G' then gg:=not(gg) and 1 else if upcase(key)='B' then bb:=not(bb) and 1 else if key=#27 then break; end; for i:=0 to 127 do setRGBpalette(i,mas[(i+k+512) mod 256,1]*rr, mas[(i+k+512) mod 256,2]*gg, mas[(i+k+512) mod 256,3]*bb); inc(k,flag);k:=k mod 256; inc(int); until false; closegraph; END.
      
      







3.アルゴリズムの開発



最初に、プログラムがどのように機能し、どの機能を記述する必要があるかを理解しましょう。 アルゴリズムの正式な説明:



1)次の値によるパレットの初期充填:(0,0,0)...(63,63,63)...(0,0,0)。 つまり、パレットの256を超える要素で、色が黒から白に滑らかに変化し、再び黒に戻ります。 このグラフィックモードでは、最大256色がサポートされ、各色は3つの色成分で構成されます。 各色成分は6ビット(0〜63の数字)で定義されます。 白い色は、それぞれ色成分のベクトル(63.63.63)と黒(0.0.0)に対応します。



2)スパイラルの描画には、画面のすべてのピクセルを通過してデータを入力することが含まれます。 らせん式は非常に単純です-画面の中心から特定のピクセルを指すベクトル(値のペア:距離と角度)のみに依存します。 言い換えれば、色はベクトルの長さと選択された係数を持つベクトルの角度のみに依存します。 さまざまな係数を並べ替えることで、らせんのさまざまな数の「分岐」とさまざまな程度のねじれの両方を取得できます。



3)1つの位置によるパレットの循環シフト。 これは、らせん回転の錯覚を与えます。 つまり、256色要素を変更すると、1回転の1 /(256 * k)のスパイラルシフトが得られます。 ここで、kはスパイラルの「分岐」の数です。 したがって、スパイラルを回転させるために画面のすべてのピクセルを再描画することは避けました。 実際、1つの位置だけシフトすることはありません。シフトの大きさと方向は特別な整数変数に格納されます。 これにより、スパイラルの回転の方向と速度を動的に変更できます。



4)キーストロークをテストします。 R、G、およびBキーを押すと、各キーに割り当てられた色成分がオンまたはオフになります。 右/左キーを押すと、変数の値が増加/減少します。これは、パレットを移動するときに明示的に使用されます。 ポイント3に進みます。



4.アルゴリズムの実装



さて、ここでアセンブラーで記述する必要がある関数を定義します。 明らかに、これらは次のようになります:パレットの初期充填機能、スパイラルのレンダリング機能、パレットのサイクリックシフト機能、およびプログラムのメイン機能。これらの機能の呼び出しに加えて、キーボードコントロールキーでのキーストロークの検証を提供します。 アルゴリズムのフローチャート:





[図 4.1。 フローチャート]



ウィキペディアで利用可能なアセンブラコンパイラと、Chris KasperskyとEva Roccoによる本「The Art of Disassembling」について読んだ後、FASMコンパイラを選択しました。 コードをメモ帳++で記述し、そこから構文を強調して記事にエクスポートします。



それでは、始めましょう。



4.1。 パレット初期機能



パレットに次の値を入力します:(0,0,0)、...(63,63,63)、...(0,0,0)。 それらの127のみが計算されるのは簡単です。すべての256要素を均一に埋めるために、2つの同一の要素を埋めます:(0,0,0)、(0,0,0)、...(63,63,63)、(63 、63.63)、...(0,0,0)、(0,0,0)。 アルゴリズムを高レベルの形式言語で記述します。



 for (int i = 0; i <= 127; i++) { setPalette(i, i/2, i/2, i/2); setPalette(255-i, i/2, i/2, i/2); }
      
      





次に、コメント付きのアセンブラコード:

A B C D
A.スタックに保存し、関数で使用されているレジスタをスタックから復元します。

B. CXレジスタは、0〜127のサイクルで実行されます。

C.パラメーター作成し、setPalette関数を呼び出します。 ALではカラーインデックスを読み込み、AHではインデックスの半分に等しい輝度値を読み込みます。

D.すべてのビットのインベントリ操作を使用してインデックスを(255-i)に変更し、setPalette関数を呼び出します。



4.2。 スパイラルレンダリング機能





勾配スパイラル記述関数について考えてみましょう。

半径のみに依存する関数は勾配円を与えます:



 pixel[x][y] = k1*sqrt(x*x + y*y);
      
      











[図 4.1。 グラデーションサークル]



角度のみに依存する関数は、中心からの勾配光線を与えます。



 pixel[x][y] = k1*arctan(x/y);
      
      











[図 4.2。 グラデーション光線]



半径と角度に線形に依存する関数は、目的の勾配スパイラルを与えます。

 pixel[x][y] = k1*sqrt(x*x + y*y) + k2*k3*arctan(x/y));
      
      











[図 4.3。 勾配スパイラル]



スパイラルを360度正しく描画するために必要な係数を選択するだけです。 係数k1は、スパイラルのねじれの程度にのみ影響します。 360度を正しく埋めるために、次のように係数k3を選択します。



 k3 = 128 / 3.1415927;
      
      











[図 4.4。 右360度勾配スパイラル]



係数k2は次の整数値を取ります:1、2、3、4 ...この係数を変更すると、対応する螺旋の分岐数が得られます。 k2が1、2、5に等しい勾配スパイラルの例を図に示します。 4.5、4.6、および4.7:









[図 4.5。 シングルブランチスパイラル]









[図 4.6。 2本の枝を持つスパイラル]









[図 4.7。 5分岐スパイラル]



擬似言語スパイラルレンダリングコード:

(ゼロ除算を含む、考えられるエラーを除く)



 for (int y = 0; y < 200; y++) for (int x = 0; x < 320; x++) { y -= 100; x -= 160; int color = k1*sqrt(x*x + y*y) + k2*128/3.1415927*arctan(x/y); y += 100; x += 160; pixel[x][y] = color; }
      
      







次に、アセンブラーアルゴリズムを実装します。



A B C D E
A.スタックに保存し、関数で使用されているレジスタをスタックから復元します。

B.レジスタAXは、0〜199のサイクルで実行されます。

C. BXレジスタは、0〜319のサイクルで実行されます。

D. AXとBXをスタックに保存します。 画面上のスパイラルの中心の座標を揃えます。320x200の解像度の場合、中心を中央にシフトします-(160、100)。 スタックからAXとBXを復元します。

E. AXを2乗し、レジスタAXとBXの値を交換し、再びAXを2乗します。 レジスタには、AXおよびBXの二乗座標があります。 レジスタの値を追加し、DXに結果をロードします。



ルートの計算は、アプリケーションコードのサイズを小さくするために犠牲にすることができます。 スタックを保存し、ベクトルの長さを計算するときにレジスタAXとBXを復元するとよいでしょう。



 push ax push bx ; dx = ax^2 + bx^2 mul ax, ax xchg ax, bx mul ax, ax add ax, bx mov dx, ax pop bx pop ax
      
      





次に、指定されたsinとcosの角度アクセントを計算するコード:



 ;    * k2 ; dx += arctan(alpha * k2) mov [cos], ax mov [sin], bx finit fild word [cos] fild word [sin] fpatan fimul word [k2] fimul word [glad1] fidiv word [glad2] fist word [tmp] add dx, [tmp]
      
      







4.3。 キーストロークをテストする





 ;     mov ah, 0Bh ; AX := 0B00h int 21h cmp al, 0ffh jmp_loop_pal_exit: jne loop_pal_out ;     mov ah, 08h int 21h label_push_space: cmp al, ' ' jne label_push_left mov ch, 0 label_push_left: cmp al, 75 jne label_push_right dec ch .......
      
      







コードはすでに全員を獲得したと思います))それは完全にある場所です: http : //codepad.org/mEDX1Z2X



余分なコントロールキーを削除することで、コードの量をさらに減らすことができます。 すべてのコントロールを捨てると、128バイトに簡単に収まると思います。 しかし、コントロールがなければ、それほど面白くありません。



All Articles