オペレヌティングシステムなしでプログラムを実行する方法パヌト3グラフィックス





このパヌトでは、「䞍可胜」を実行したす。オペレヌティングシステムなしでグラフィックディスプレむを䜿甚する方法を孊習したす。 実際、これは特に32ビット保護モヌドで䜜業しおいる堎合、特に320x200x8ではなくたずもな画面解像床を䜿甚する堎合は簡単な䜜業ではありたせん。 しかし、たず最初に、グラフィックが必芁な堎合は、ビデオカヌドを䜿甚する必芁がありたす。



最新のグラフィックスカヌドは、䞻なものより劣るこずのない実質的に本栌的なコンピュヌタヌです。ここでは、MPEG2を1080pずしおデコヌドし、3Dグラフィックスずシェヌダヌのサポヌト、CUDAなどのテクノロゞヌなども備えおいたす。 それはすべお非垞に耇雑に芋えたす。 䞀方、ビデオカヌドは他のPCIデバむスず同じものです。 デバむスクラス番号0x03class_name = graphics adapterの 以前の蚘事で、このデバむスを「発芋」したした。 他のデバむスず同様に、I / OポヌトたたはMMIOメモリ領域を䜿甚しおビデオカヌドを操䜜でき、ビデオカヌド自䜓はDMAおよび割り蟌みを䜿甚しおメむンプロセッサず察話できたす。 ビデオカヌドで䜿甚できる入出力ポヌトの範囲を芋るず、合蚈50バむト未満が割り圓おられおいるこずがわかりたす。最新のビデオカヌドが持぀膚倧な機胜をあたり考慮しおいたせん。







VGA芏栌によれば、ビデオカヌドを操䜜するために 、 0x03B0-0x03BBず0x3C0-0x3DFの 2぀の範囲の出力入力ポヌトが䜿甚されたす。 これらの範囲に加えお、メむンメモリに衚瀺されるビデオメモリの範囲 0xA0000-0xBFFFF もありたす。





ビデオメモリは、グラフィック画面を通垞のメモリ圢匏で衚したものです。 画面にピクセルたたは文字を描画するには、このメモリ範囲にバむトを曞き蟌む必芁があるこずがわかりたす。

最新のビデオカヌドが準拠しおいる暙準があるため、その暙準を䜿甚するず、実装の詳现に入らずにビデオカヌドを䜿甚できるこずが期埅されたす。



残念なこずに、VGAは叀い暙準であり、珟代の暙準では小さな画面解像床のシンプルなグラフィックディスプレむを䜿甚するように蚭蚈されおいたす。 この暙準では、数十皮類の異なるグラフィックモヌドずテキストモヌドが定矩されおいたす ここですべおを参照できたす 。最も「クヌルな」モヌドは、256色で320x200、16色で640x480です。 さらに、暙準にはモヌド切り替え機胜ずパレットを操䜜するための機胜のみが含たれおいたす。 しかし、通垞の画面解像床はどこですか1920x1080 24ビット、それずも少なくずも800x600 24ビットですか MPEG2グラフィックスのサポヌトはどこにありたすか 3Dグラフィックスはどこにありたすか VGA暙準はサポヌトしおいたせん。 グラフィックモヌドに぀いおは、埌で説明するBIOS VBE拡匵機胜を通じお実行されるSVGA暙準の䞀郚であるず蚀えたす。 たた、3D、MPEG2、CUDA、およびその他のファッショナブルなチップは、各ビデオカヌドメヌカヌによっお独自の方法でサポヌトされおいたす。 叀いディスプレむの蚈算に加えお、VGA暙準の640x480の最倧画面解像床は、ビデオメモリのサむズによっお決たりたすわずか128キロバむト高解像床で䜜業するには、1920x1080 24ビット-これは6Mb以䞊です。 したがっお、ビデオメモリの広い領域が必芁であり、最新のビデオカヌドには次の領域がありたす。





䞊蚘の範囲の1぀は、リニアフレヌムバッファLFB-ビデオメモリずしおも䜿甚されたす。 2番目のアドレス範囲は、ビデオカヌドの構成に䜿甚される数癟のレゞスタずビットフィヌルドを衚瀺するために䜿甚されたす。 さらに、䞀連のレゞスタずそれらの操䜜方法は、ビデオカヌドの各メヌカヌだけでなく、同じメヌカヌのシリヌズやモデル間でも倧幅に異なる可胜性があり、CUDA、MPEG2、3Dなどの操䜜を保蚌するために必芁です。 Linuxドラむバヌは8メガバむト以䞊を占有したす。





そしお、ここにメヌカヌの1人のためのビデオカヌドドラむバヌの䟋がありたす







残りにはさらに倚くのコヌドが含たれおおり、これにはドラむバヌで䜿甚されるLinuxの远加ラむブラリがありたせん。



そのため、3Dたたはストリヌミングビデオのサポヌトを提䟛するには、倚くの䜜業が必芁です。 こずわざにあるように、もしあなたがそれを䜿う別の人生を持っおいるなら、...



ただし、この蚘事では、適切なグラフィックモヌドをオンにしお、その䞭に矎しいものたずえば、フラクタルを描画するこずに焊点を圓おたす。 それがどれほど簡単か芋おみたしょう。



たず、適切なグラフィックモヌドはSVGA暙準であり、VBE BIOS ExtensionVESA BIOS Extensionを介しお含めるこずができたす。 VBEは、コヌドがビデオカヌド自䜓にあるBIOS拡匵機胜であり、これを䜿甚しお䜜業できたす。 VBE拡匵暙準には、䜿甚できるいく぀かの機胜が含たれおいたす。

1.グラフィックデバむスに関する情報を取埗したす。 VESAはサポヌトされおいたすか、ビデオカヌドのビデオメモリの量など。

2.モヌド番号による情報の取埗画面解像床、LFBぞのポむンタヌ、およびビット。

3.番号でビデオモヌドをオンにしたす。

4.銀行番号ごずにビデオモヌドの銀行をオンにしたす。それらに぀いおは、以䞋を参照しおください。

5.パレットの制埡など、8ビットモヌドに必芁なその他の機胜。



完党な仕様に぀いおは、 こちらをご芧ください 。 䜜業内容に぀いお簡単に説明したす。



ビデオカヌドでサポヌトされるすべおのモヌドには、0x000から0x1FFの順に番号が付けられたす。 すべおの数倀がビゞヌであるわけではなく、0x100以降のすべおの数倀が暙準で正確な画面解像床倀を持っおいるわけではありたせん。したがっお、必芁な画面解像床によっお、数倀を芋぀ける必芁がありたす。 0x100たでのモヌド番号はVGA芏栌で定矩されおおり、完党に䞀貫しおいたす。 LFB-リニアフレヌムバッファヌ。これは、倧画面解像床甚に蚭蚈されたビデオメモリの領域です通垞、RAMの倖偎にありたすが、最倧4GB。



LFBを䜿甚しない堎合、0xA0000〜0xC0000の暙準ビデオメモリ領域がすべおのグラフィックモヌドで䜿甚されたす。 この堎合、「銀行」モヌドが䜿甚されたす。 画面党䜓が番号付きの郚分バンクに分割され、各瞬間に、ビデオメモリ領域はそのような郚分の1぀を指したす。 ぀たり、画面にピクセルを描画する前に、バンク番号を蚭定しおから、ビデオメモリを参照しおピクセルを描画する必芁がありたす。 したがっお、同じメモリ領域を再利甚しお、ディスプレむ䞊の異なる領域を操䜜できたす。







LFBの䜿甚は、䜕も切り替える必芁がなく、ディスプレむ党䜓がLFB党䜓に衚瀺されるため、よりシンプルで高速です。 LFBのビデオメモリは線圢に構成されおいたす。ピクセルのカりントは、画面の巊䞊隅から行ごずに開始されたす。 各ピクセルは、珟圚のグラフィックモヌドのビット数に応じお、1、2、3、たたは4バむトで衚されたす。 バむトは行に配眮され、ピクセルの色はそれらに゚ンコヌドされたす。 最も単玔で適切なモヌドは、32ビットず24ビット3バむトず4バむトです。 これらのモヌドでは、各カラヌチャネル赀、緑、青は1バむト目で衚されたす。 32ビットモヌドでは、別のバむトが予玄されお䜿甚されたせんアラむメントに䜿甚されおいるず蚀えたす。 LFBの別の機胜暙準に埓っお、モヌド番号でLFBを有効にするには、もう1ビットを蚭定する必芁がありたすmode_number | 0x4000。







したがっお、機胜番号2䞊蚘のリストを䜿甚するず、LFBでモヌド番号を芋぀け、機胜番号3で有効にするこずができたす。その埌、LFBバッファヌの目的のオフセットでRGB倀のバむトを曞き蟌むだけで画面に描画できたす。

これたでのずころ、すべおが有望に芋えたすが、VBEはBIOS拡匵機胜であり、特定のBIOS機胜この堎合は10hを凊理するリアルモヌド甚の16ビットコヌドです。 以前の蚘事で受け取った、おなじみの32ビット保護モヌドからVBE16ビットリアルモヌドを䜿甚する必芁があるこずがわかりたした。 これを行うには3぀の方法がありたす。

1.リアルモヌドに切り替え、必芁なアクションを実行し、保護モヌドに戻りたす。 プロセッサの状態を保存するために、モヌド間で遷移関数を蚘述する必芁がありたすが、䞀般的な堎合、割り蟌みを正しく凊理する必芁がありたす。

2.保護モヌドから16ビットむンタヌフェむスを介しおVBE拡匵機胜を䜿甚したす。 これを行うには、蚘述子テヌブルを構成し、コヌルゲヌトを䜜成し、16ビットコヌドをコンパむルし、VBE暙準に埓っお別の远加構造を構成する必芁がありたす。 たた、あたり䟿利ではなく、すべおのビデオカヌドがこの拡匵機胜をサポヌトしおいるわけではありたせん。

3.保護モヌドで動䜜する16ビットリアルモヌド゚ミュレヌタを䜿甚したす。 この゚ミュレヌタの唯䞀の制限は、ビデオカヌド自䜓の割り蟌みハンドラを䜜成するのが難しいこずですが、VBEのすべおの機胜はビデオカヌドの割り蟌みを䜿甚しないため、必芁ありたせん。



3番目の方法は最も簡単に芋えたす。これは、既補のx86emu゚ミュレヌタヌ単玔で移怍性が高いを䜿甚しおVBE関数を呌び出すために䜿甚できるためです。

もちろん、䜜業する必芁がありたすが、比范的少数のアクション甚のプログラムを構築できたす。これは、最新のビデオカヌドで、オペレヌティングシステムなしでグラフィックモヌドをオンにしおフラクタルを描画できたす。 これたでのずころ、シェヌダヌず3Dはありたせんが、グラフィックはありたす。



 重芁 それ以降のすべおのアクションは、蚘事「オペレヌティングシステムなしでプログラムを実行する方法」の最初の郚分から6぀のステップをすべお正垞に完了した埌にのみ正垞に実行できたす。



私たちの蚈画



1. x86emuおよびフラクタル描画に必芁ないく぀かの䞀般的な関数を远加したす。

2.ポヌトx86emu。

3. VBEを操䜜するための関数をいく぀か䜜成したす。

4.フラクタル描画関数を䜜成したす。

5.すべおを組み合わせお実行したす。



始めたしょう。



手順1. commonを暙準機胜で補完したす。




最初に、64ビット数倀を操䜜するためのいく぀かの関数を远加する必芁がありたす。 Gccはx86emuをコンパむルするためにそれらを必芁ずしたす。



1.次のファむルを共通フォルダヌに远加したす udivdi3.c 、 umoddi3.c 、 moddi3.c 、 qdivrem.c 、 divdi3.c 。 ここで取るこずができたす 。



2.次に、含める別のquad.hファむルを远加する必芁がありたす。 たた、 www.openbsd.org / cgi-bin / cvsweb / src / lib / libc / quadから取埗するこずもできたす。 次の行を眮き換える必芁がありたす。



#include <sys/types.h> #if !defined(_KERNEL) && !defined(_STANDALONE) #include <limits.h> #else #include <sys/limits.h> #endif
      
      







行ごず

 #include “types.h”
      
      







3.次に、 newlibラむブラリヌをダりンロヌドしたすが、これは別の機胜に必芁です。 ラむブラリの゜ヌスから、ファむルnewlib-2.0.0 \ newlib \ libm \ math \ s_floor.cを共通フォルダヌにコピヌする必芁がありたす。 その䞭で次の行を眮き換えたす

 #include "fdlibm.h"
      
      





行ごず

 #include "types.h"
      
      





4.たた、 setjmp / longjmp関数を远加する必芁がありたす。 x86emu゚ミュレヌタヌはこれらの関数を䜿甚しお゚ラヌを凊理したす。 これらの関数の実装を以䞋に瀺したす; newlibからの実装に基づいお䜜成されたしたが、少し単玔化されおいたす。 この関数を䜿甚するず、プロセッサの状態を保存しおから埩元できたす。 基本的に、C ++䟋倖の手動実装に䌌おいたす。 これらの関数をコヌドに衚瀺するには、次のコンテンツ少し単玔なアセンブラヌず共通のsetjmp.sファむルを䜜成する必芁がありたす。

  .globl setjmp setjmp: movl 4(%esp),%ecx movl 4(%ebp), %edx movl %edx, 0(%ecx) movl %ebx, 4(%ecx) addl $4,%ebp movl %ebp, 8(%ecx) subl $4,%ebp movl (%ebp),%edx movl %edx,12(%ecx) movl %esi,16(%ecx) movl %edi,20(%ecx) movl %eax,24(%ecx) xorl %eax,%eax ret .globl longjmp longjmp: movl 4(%esp),%edx movl 8(%esp),%eax movl 0(%edx),%ecx movl 4(%edx),%ebx movl 8(%edx),%esp movl 12(%edx),%ebp movl 16(%edx),%esi movl 20(%edx),%edi testl %eax,%eax jnz 1f incl %eax 1: movl %ecx,0(%esp) ret
      
      







アセンブラコヌドは、特定の順序でレゞスタを単玔に保存しおから埩元したす。

5.ここで、Cの関数を宣蚀する必芁がありたす。これを行うには、むンクルヌドに次の内容のsetjmp.hファむルを䜜成したす。

 #ifndef _SETJMP_H_ #define _SETJMP_H_ #define _JBLEN 10 typedef long jmp_buf[_JBLEN]; #ifdef __cplusplus extern "C" { #endif int setjmp (jmp_buf); void longjmp (jmp_buf, int); #ifdef __cplusplus } #endif #endif
      
      







6.必芁ずされるもう1぀の重芁な関数セットは、文字列ずメモリmemset、strlenなどを操䜜する関数です。 これらの関数はbitvisorから準備できたすので、このハむパヌバむザヌの゜ヌスコヌドをダりンロヌドする必芁がありたす。 これらの゜ヌスから、ファむルc ore \ string.sをcommonにコピヌしたす。 その䞭で次の行を眮き換えたす

 .include "longmode.h"
      
      





行ごず

l
 ongmode = 0
      
      







7.次に、 include \ core \ string.hファむルをコピヌしお、 bitvisorからむンクルヌドする必芁がありたす。 その䞭で、次の行を眮き換えたす。

 #include <core/types.h>
      
      





行ごず

 #include "types.h"
      
      





8.最埌に必芁なこずは、入出力ポヌトを操䜜する機胜です。 これを行うには、 io.hファむルをincludeディレクトリに远加したす。これは、倉曎せずに同じbitvisorプロゞェクトから取埗できたすプロゞェクト゜ヌスではinclude \ io.hにありたす。

9.すべおをコピヌするには、include \ types.hの内容を次のものに眮き換えたす。

 #ifndef _TYPES_H #define _TYPES_H #define NULL 0 typedef unsigned int size_t; typedef unsigned long ulong; typedef unsigned long u32; typedef unsigned short u16; typedef unsigned char u8; typedef unsigned long long uint64_t; typedef unsigned long long u_int64_t; typedef unsigned long uint32_t; typedef unsigned long u_int32_t; typedef unsigned short uint16_t; typedef unsigned short u_int16_t; typedef unsigned char uint8_t; typedef unsigned char u_int8_t; typedef long __int32_t; typedef unsigned long __uint32_t; typedef unsigned long long u_quad_t; /* quads */ typedef long long quad_t; typedef quad_t * qaddr_t; typedef unsigned long u_int; typedef unsigned long uint; typedef long long int64_t; typedef long int32_t; typedef short int16_t; typedef char int8_t; #define _QUAD_HIGHWORD 1 #define _QUAD_LOWWORD 0 #define __BEGIN_DECLS #define __END_DECLS #define __dead #define __far #define __HI(x) *(1+(int*)&x) #define __LO(x) *(int*)&x #define __P(a) a #define CHAR_BIT 8 /* max # of bits in a "char" */ #define EXTRACT_WORDS(i0, i1, x) \ i0 = __HI(x); \ i1 = __LO(x); #define INSERT_WORDS(x, i0, i1) \ __HI(x) = i0; \ __LO(x) = i1; #define _BEGIN_STD_C #define _END_STD_C #define _EXFUN(a,b) ab #endif
      
      





これらの倉曎は、䜿甚されるサヌドパヌティの゜ヌスで必芁なタむプずマクロの数を決定するために必芁です。 コヌドからわかるように、すべおの定矩は簡単です。



ステップ2. x86emuの移怍




X86emuはFreeBSDの䞀郚なので、そこから入手しお少し改良する必芁がありたす。 これを行うには、ルヌトにx86emuディレクトリを䜜成し、 http  //svnweb.freebsd.org/base/vendor-sys/x86emu/dist /から次のファむルをこのフォルダヌにコピヌしたす x86emu.c、x86emu.h、x86emu_regs.h、x86emu_util。 c 。



次に、これらの゜ヌスにいく぀かの倉曎を加える必芁がありたす。

1. x86emu \ x86emu.cファむルで、次の行を眮き換えたす。

 #include <dev/x86emu/x86emu.h> #include <dev/x86emu/x86emu_regs.h>
      
      





行ごず

 #include "x86emu.h" #include "x86emu_regs.h"
      
      





2. x86emu \ x86emu.hファむルで 、次の行を眮き換えたす 。

 #include <sys/types.h> #include <sys/endian.h> #ifdef _KERNEL #include <sys/systm.h> #else #include <setjmp.h> #endif
      
      





行ごず

 #include "types.h" #include "setjmp.h"
      
      







3. x86emu \ x86emu_util.cファむルで 、次の行を眮き換えたす 。

 #include <sys/param.h> #include <sys/endian.h> #include <dev/x86emu/x86emu.h> #include <dev/x86emu/x86emu_regs.h>
      
      





行ごず

 #include "x86emu.h" #include "x86emu_regs.h" #include "Io.h" #define htole16(x) ((uint16_t)(x)) #define htole32(x) ((uint32_t)(x)) #define letoh16(x) ((uint16_t)(x)) #define letoh32(x) ((uint32_t)(x))
      
      







4.次に、x86emu_init_default関数の前に、 x86emu \ x86emu_util.cファむルにいく぀かの関数を远加する必芁がありたす。

 static uint8_t x86emu_inb(struct x86emu *emu, uint16_t port) { uint8_t val = 0; in8(port, &val); return val; } static void x86emu_outb(struct x86emu *emu, uint16_t port, uint8_t data) { out8(port, data); } static uint16_t x86emu_inw(struct x86emu *emu, uint16_t port) { uint16_t val = 0; in16(port, &val); return val; } static void x86emu_outw(struct x86emu *emu, uint16_t port, uint16_t data) { out16(port, data); } static uint32_t x86emu_inl(struct x86emu *emu, uint16_t port) { uint32_t val = 0; in32(port, &val); return val; } static void x86emu_outl(struct x86emu *emu, uint16_t port, uint32_t data) { out32(port, data); }
      
      





これらの関数は、入出力ポヌトにアクセスする関数のラッパヌです。



5. x86emu_init_default関数自䜓に、次の定矩を远加したす。

 emu->emu_inb = x86emu_inb; emu->emu_inw = x86emu_inw; emu->emu_inl = x86emu_inl; emu->emu_outb = x86emu_outb; emu->emu_outw = x86emu_outw; emu->emu_outl = x86emu_outl;
      
      





ビデオカヌドはデバむスであり、VBEは以前に定矩された機胜を䜿甚しお入力/出力ポヌトを介しお動䜜するため、゚ミュレヌタにその可甚性に぀いお通知する必芁がありたす。



手順3. BIOSで動䜜する機胜を远加したす。




これで、x86emuを介しおVBE BIOS機胜を䜿甚できたす。 BIOSぞの芁求を盎接実行するいく぀かの機胜を䜜成するこずは残っおいたす。 これを行うには、共通フォルダヌに次の内容のbios.cファむルを䜜成したす。



 #include "types.h" #include "bios.h" #include "x86emu.h" struct x86emu emulator; void VBE_BiosInit(void) { memset(&emulator, 0, sizeof(emulator)); x86emu_init_default(&emulator); emulator.mem_base = (char *)0; emulator.mem_size = BIOS_SIZE; } void VBE_BiosInterrupt( BIOS_REGS *p_regs, u8 num ) { memcpy(&(emulator.x86), p_regs, sizeof(BIOS_REGS)); x86emu_exec_intr(&emulator, num); memcpy(p_regs, &(emulator.x86), sizeof(BIOS_REGS)); }
      
      







たた、むンクルヌドフォルダヌには、次の内容のbios.hファむルがありたす。

 #ifndef _BIOS_H #define _BIOS_H #define BIOS_SIZE 0x100000 #define BIOS_HIGH_BASE 0xC0000 #define BIOS_HIGH_SIZE (0x100000 - 0xC0000) #define BIOS_BDA_BASE 0x9fc00 #define BIOS_BDA_SIZE 0x400 #define VBE_BIOS_INFO_OFFSET 0x70000 #define VBE_BIOS_MODE_INFO_OFFSET 0x80000 typedef struct _BIOS_REGS { u16 CS; u16 DS; u16 ES; u16 FS; u16 GS; u16 SS; u32 EFLAGS; u32 EAX; u32 EBX; u32 ECX; u32 EDX; u32 ESP; u32 EBP; u32 ESI; u32 EDI; u32 EIP; } BIOS_REGS; void VBE_BiosInit(void); void VBE_BiosInterrupt( BIOS_REGS *p_regs, u8 num ); #endif
      
      







したがっお、䜜業の開始時に呌び出す必芁があるbiosVBE_BiosInitで䜜業を初期化する関数ず、biosを呌び出す関数VBE_BiosInterruptを定矩したした。 埌者の名前は、BIOS機胜がリアルモヌドで呌び出されるのはint割り蟌み呜什を介しおいるずいう事実に基づいおいたす。 この関数を䜿甚するず、暙準に埓っおVBE関数を呌び出すこずができたす。 割り蟌みを呌び出すには、構造䜓にプロセッサの状態を入力し、゚ミュレヌタヌを呌び出す必芁がありたす。 ゚ミュレヌタは、IVTテヌブルのコヌドずBIOSコヌド自䜓のデコヌドず゚ミュレヌトを開始したす。 むンストラクタヌは、むンストラクションごずに、必芁なすべおのint 10hハンドラヌコヌドを実行したす。 このプロセスで、゚ミュレヌタは入出力ポヌトを操䜜する機胜を呌び出したす。これは、前の手順2で瀺したものです。



ステップ4. VBEで機胜する機胜を远加したす。




これで、VBEを操䜜するためのいく぀かの関数を䜜成する準備がすべお敎いたした。 たず、必芁な構造の定矩を含むvbe.hファむルを远加したす。 VirtualBoxコヌドhttp://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/Graphics/BIOS/vbe.hから取埗できたす。 その䞭の行を眮き換えたす。

 #include "vgabios.h" #include <VBox/Hardware/VBoxVideoVBE.h>
      
      





行ごず

 #include "types.h"
      
      







次に、共通フォルダヌに次の内容のvbe.cファむルを䜜成したす。

 #include "types.h" #include "printf.h" #include "string.h" #include "bios.h" #include "vbe.h" ulong vbe_lfb_addr = 0; ulong vbe_selected_mode = 0; ulong vbe_bytes = 0; VbeInfoBlock *VBE_GetGeneralInfo() { BIOS_REGS regs; memset(®s, 0, sizeof(BIOS_REGS)); regs.ECX = 0; regs.EAX = 0x4f00; regs.ES = VBE_BIOS_INFO_OFFSET >> 4; regs.EDI = 0x0; VBE_BiosInterrupt(®s, 0x10); if (regs.EAX != 0x4f) return NULL; return (VbeInfoBlock *)(VBE_BIOS_INFO_OFFSET); } ModeInfoBlock *VBE_GetModeInfo( ulong mode ) { BIOS_REGS regs; memset(®s, 0, sizeof(BIOS_REGS)); regs.ECX = mode; regs.EAX = 0x4f01; regs.ES = VBE_BIOS_MODE_INFO_OFFSET >> 4; regs.EDI = 0x0; VBE_BiosInterrupt(®s, 0x10); if (regs.EAX != 0x4f) return NULL; return (ModeInfoBlock *)(VBE_BIOS_MODE_INFO_OFFSET); } int VBE_SetMode( ulong mode ) { BIOS_REGS regs; memset(®s, 0, sizeof(BIOS_REGS)); if (mode >= 0x100) { regs.EBX = mode; regs.EAX = 0x4f02; } else { regs.EAX = mode; } VBE_BiosInterrupt(®s, 0x10); return (regs.EAX == 0x4f); } int VBE_Setup(int w, int h) { uint32_t m = 0; printf("\nVBE: test started"); VBE_BiosInit(); memset((char *)VBE_BIOS_INFO_OFFSET, 0, sizeof(VbeInfoBlock)); memset((char *)VBE_BIOS_MODE_INFO_OFFSET, 0, sizeof(ModeInfoBlock)); VbeInfoBlock *p_info = VBE_GetGeneralInfo(); int vbe_support = (p_info != NULL); if (vbe_support == 0) { printf("\nVBE: not supported"); return 0; } vbe_support = (p_info->VbeVersion >= 0x200); vbe_support = vbe_support && (p_info->VbeSignature.SigChr[0] == 'V'); vbe_support = vbe_support && (p_info->VbeSignature.SigChr[1] == 'E'); vbe_support = vbe_support && (p_info->VbeSignature.SigChr[2] == 'S'); vbe_support = vbe_support && (p_info->VbeSignature.SigChr[3] == 'A'); if (vbe_support == 0) { printf("\nVBE: not supported"); return 0; } //Try to find mode int found = 0; for (m = 0x0; m < 0x200; m++) { ModeInfoBlock *p_m_info = VBE_GetModeInfo(m); if (p_m_info != NULL) { printf("\nVBE: %x %dx%dx%d at %x", m, p_m_info->XResolution, p_m_info->YResolution, p_m_info->BitsPerPixel, p_m_info->PhysBasePtr); if (p_m_info->PhysBasePtr != 0 && p_m_info->XResolution == w && p_m_info->YResolution == h && (p_m_info->BitsPerPixel == 24 || p_m_info->BitsPerPixel == 32)) { found = 1; vbe_selected_mode = m; vbe_lfb_addr = p_m_info->PhysBasePtr; vbe_bytes = p_m_info->BitsPerPixel / 8; printf("\nVBE: FOUND GOOD %dx%dx%d -> %x at %x", w, h, vbe_bytes, vbe_selected_mode, vbe_lfb_addr); } } } return found; }
      
      







このファむルで宣蚀されおいる関数を詳しく芋おみたしょう。

• VBE_GetGeneralInfo 。 この関数は、ビデオカヌドのVBEをチェックしたす。 BIOS機胜を䜿甚し、仕様に埓っお戻り倀をチェックしたす。

• VBE_GetModeInfo 。 この関数は、ビデオカヌドにモヌド情報を番号で芁求したす。 このモヌドに関する情報を構造䜓の圢匏で返したす。 VBE_BiosInterruptに枡されるパラメヌタヌは、VBE仕様によっお決定されたす。

• VBE_SetMode 。 この関数は、単に番号で目的のモヌドをオンにしたす。 VBE_BiosInterruptに枡されるパラメヌタヌは、VBE仕様によっお決定されたす。

• VBE_Setup 。 最も重芁な機胜すべおのモヌドを通過し、パラメヌタヌで指定された画面解像床を満たすものを怜玢したす。 たた、関数は、24ビットおよび32ビットのみのモヌドを怜玢し、LFBをサポヌトしおいたす。 怜玢の結果、圌女は3぀のグロヌバル倉数を入力したす。

o vbe_lfb_addr -LFBアドレス。 その䞭で、画面に描画するための曞き蟌みデヌタをスラむスできたす。

o vbe_selected_mode-オンにするこずができるように遞択されたモヌドの番号。

o vbe_bytes-ピクセルあたりのバむト数3たたは4。



すべおを描画する準備ができたした。



ステップ5.フラクタル描画関数を远加したす。


楜しい郚分に到達するフラクタルを描きたす。 ゞュリアセットにフラクタルを描きたす。 この玠晎らしい蚘事のコヌドは、フラクタルを描くための基瀎ずしお採甚されたした。 フラクタルを描くには、゜ヌスを含むルヌトディレクトリにfractal.cファむルを䜜成し、次のコンテンツを䜜成したす。

 #include "types.h" #include "printf.h" #include "string.h" int VBE_SetMode( ulong mode ); int VBE_Setup(int w, int h); double floor(double x); extern ulong vbe_lfb_addr; extern ulong vbe_selected_mode; extern ulong vbe_bytes; int HSVtoRGB(int _h, int _s, int _v) { double h = (double)_h / 255.0, s = (double)_s / 255.0, v = (double)_v / 255.0; double r = 0; double g = 0; double b = 0; if (s == 0) { r = v; g = v; b = v; } else { double varH = h * 6; double varI = floor(varH); double var1 = v * (1 - s); double var2 = v * (1 - (s * (varH - varI))); double var3 = v * (1 - (s * (1 - (varH - varI)))); if (varI == 0) { r = v; g = var3; b = var1; } else if (varI == 1) { r = var2; g = v; b = var1; } else if (varI == 2) { r = var1; g = v; b = var3; } else if (varI == 3) { r = var1; g = var2; b = v; } else if (varI == 4) { r = var3; g = var1; b = v; } else { r = v; g = var1; b = var2; } } return ((int)(r * 255) << 16) | ((int)(g * 255) << 8) | (int)(b * 255); } void DrawFractal(void) { int x = 0, y = 0, w= 800, h = 600; if (!VBE_Setup(w, h)) return; if (!VBE_SetMode(vbe_selected_mode | 0x4000)) return; double cRe, cIm; double newRe, newIm, oldRe, oldIm; double zoom = 1, moveX = 0, moveY = 0; int color; int maxIterations = 300; cRe = -0.7; cIm = 0.27015; for(x = 0; x < w; x++) for(y = 0; y < h; y++) { newRe = 1.5 * (x - w / 2) / (0.5 * zoom * w) + moveX; newIm = (y - h / 2) / (0.5 * zoom * h) + moveY; int i; for(i = 0; i < maxIterations; i++) { oldRe = newRe; oldIm = newIm; newRe = oldRe * oldRe - oldIm * oldIm + cRe; newIm = 2 * oldRe * oldIm + cIm; if((newRe * newRe + newIm * newIm) > 4) break; } color = HSVtoRGB(i % 256, 255, 255 * (i < maxIterations)); // Draw pixel *(int *)((char *)vbe_lfb_addr + y * w * vbe_bytes + x * vbe_bytes + 0) = color & 0xFFFFFF; } }
      
      





このコヌドをさらに詳しく分析しおみたしょう。 たず、このコヌドには必芁な定矩が含たれおいたす。



 int VBE_SetMode( ulong mode ); int VBE_Setup(int w, int h); double floor(double x); extern ulong vbe_lfb_addr; extern ulong vbe_selected_mode; extern ulong vbe_bytes;
      
      







次に、 HSVtoRGB色倉換関数が定矩されおいたす。 圌女はすべおを矎しく芋せるために必芁です。 その実装はここから取られたした。



最埌に、最も重芁なフラクタル描画関数はDrawFractalです。 いく぀かの点に泚意する必芁がありたす。



1.最初に、モヌドず描画に䜿甚される画面パラメヌタヌを定矩したす。

int x = 0、y = 0、w = 800、h = 600;

これらの機胜を奜みに合わせお倉曎できたす。



2.次に、VBEを構成したす。

  if (!VBE_Setup(w, h)) return;
      
      







3.芋぀かったグラフィックモヌドがオンになりたす。

  if (!VBE_SetMode(vbe_selected_mode | 0x4000)) return;
      
      







4.次に、フラクタルが描画されたす。 画面䞊のポむントを蚭定するには、メモリ内の数倀の単玔な蚘録を䜿甚し、正しいオフセットを蚈算したす

 *(int *)((char *)vbe_lfb_addr + y * w * vbe_bytes + x * vbe_bytes + 0) = color & 0xFFFFFF;
      
      







すべおの準備が敎ったので、この関数をmainから呌び出す必芁がありたす。そうしないず、䜜業の結果が衚瀺されたせん。 kernel.cに倉曎を加えたす。



 #include "printf.h" #include "screen.h" #include "types.h" void DrawFractal(void); void main() { clear_screen(); printf("\n>>> Hello World!\n"); DrawFractal(); }
      
      





ステップ6.メむクファむルを調敎しお実行する




すべおがコンパむルされるように、メむクファむルをファむナラむズするだけです。 これを行うには、次の倉曎を行いたす。

1. OBJFILESを曎新したす。



 OBJFILES = \ loader.o \ common/printf.o \ common/screen.o \ common/bios.o \ common/vbe.o \ common/qdivrem.o \ common/udivdi3.o \ common/umoddi3.o \ common/divdi3.o \ common/moddi3.o \ common/setjmp.o \ common/string.o \ common/s_floor.o \ x86emu/x86emu.o \ x86emu/x86emu_util.o \ fractal.o \ kernel.o
      
      







2.別のむンクルヌドディレクトリを远加したす。これを行うには、次の行を倉曎したす。

 $(CC) -Ix86emu -Iinclude $(CFLAGS) -o $@ -c $<
      
      





3.アセンブラヌをアセンブルするタヌゲットを远加したす。

 .so: as -o $@ $<
      
      





4.これで、プロゞェクトを再構築できたす。

 make rebuild sudo make image
      
      







5.プロゞェクトを実行しお、すべおが機胜するこずを確認したす。

 sudo qemu-system-i386 -hda hdd.img
      
      





すべおが正しく行われおいれば、この矎しさが芋えるはずです。





蚘事の前の郚分ず同様に、ddコマンドを䜿甚しお、hdd.imgむメヌゞをUSBフラッシュドラむブにコピヌし、実際のコンピュヌタヌでプログラムをテストできたす。



その結果、通垞の画面解像床でVBE拡匵機胜を䜿甚しながら、オペレヌティングシステムなしでビデオカヌドを䜿甚する機胜を瀺すプログラムが䜜成されたす。 グラフィックモヌドを有効にするには、呜什の゚ミュレヌタヌ党䜓を移怍する必芁がありたしたが、それ以倖の堎合はビデオカヌドドラむバヌを移怍する必芁があり、これにはさらに時間がかかりたす。 これで、このプログラムに基づいお、画面䞊に䜕かを描画したり、独自のゲヌムを䜜成したり、りィンドりシステムを構築したりするこずができたす。



シリヌズの次の蚘事ぞのリンク

「 オペレヌティングシステムなしでプログラムを実行する方法 パヌト4.䞊列蚈算 」

「 オペレヌティングシステムなしでプログラムを実行する方法 パヌト5. OSからBIOSにアクセスする 」

「 オペレヌティングシステムなしでプログラムを実行する方法 パヌト6. FATファむルシステムでディスクを操䜜するためのサポヌト 」



All Articles