DIY DIYゲームコンソール

画像







この投稿は、ゼロから作られた私のプロジェクト「自家製」コンソールビデオコンソールの紹介です。 レトロなコンソールと最新のサンプルの両方に触発されましたが、独自のアーキテクチャを手に入れました。 私の友人は私に自分のプロジェクトについて話すべきであり、すべてを「自分自身のために」するわけではないことを絶えず言っていたので、ここでこの投稿を公開しています。







注意、これは翻訳です







それがすべて始まった方法



私の名前はセルジオ・ヴィエイラです。ポルトガルで80年代と90年代に育ちました。レトロゲーム、特に第3世代と第4世代のコンソールには懐かしさがあります。







数年前、私は電子機器をよりよく理解し、独自の接頭辞を作成しようとすることにしました。







職業として、私はプログラマーであり、destkopの独立したアップグレードを除き(考慮されるべきではありません)、電子技術者としての経験はありませんでした。







経験はありませんでしたが、「どうして?」と自分に言い聞かせ、数冊の本や電子キットを買って、勉強する価値があるという気持ちに基づいて勉強を始めました。







私は懐かしくなるような接頭辞を作りたかった、 NESスーパーファミコンの間、または多分セガマスターシステムメガドライブの間に何かが欲しかった。







これらのコンソールには、CPU、元のビデオチップ(当時はGPUと呼ばれていなかった)、およびオーディオチップがあり、内蔵されていることもあれば、外部にあることもあります。







ゲームはカートリッジで配布されました。これは一般に鉄の拡張であり、ROMチップだけである場合があり、追加のコンポーネントが含まれることもありました。







元の計画では、次の特性を持つプレフィックスを作成しました。









カートリッジではなくSDカードを使用する理由は、基本的にははるかに実用的であるため、コンピューターからコピーできます。 そして、カートリッジは、第一に、セットトップボックス内により多くの鉄を、そして第二に、各プログラムの鉄を生産することを意味します。







生産



ビデオ信号



私が最初にしたことは、ビデオ信号を生成することでした。







私がサンプルとして使用した期間のコンソールには、さまざまな独自のグラフィックチップがありました。つまり、それらはすべて異なる仕様を持っていました。

このため、既製のグラフィックチップを使用したくありませんでした。コンソールに独自のグラフィック仕様を持たせたかったのです。 自分のグラフィックチップを作成できず、その時点でFPGAを使用できなかったため、8ビット、20メガヘルツのマイクロコントローラーを使用したソフトウェア生成のグラフィック信号生成に制限することにしました。







これは多すぎず、私が興味を持っていたレベルのグラフィックスに十分な強力なソリューションです。







それで、TV用のPALビデオ信号を生成するために、20 MHzの純度でAtmega644マイクロコントローラを使用し始めました。 チップ自体はそれを行う方法を知らないため、PALプロトコルをビットビートする必要がありました。







imageVPUテスト1







imageVPUテスト2







マイクロコントローラーは8ビット色(RGB332、3ビット赤、3ビット緑、2青)を生成し、パッシブDACはすべてをRGBに変換します。 幸いポルトガルでは、ほぼすべてのテレビにSCARTコネクタが装備されており、RGB入力をサポートしています。







正しいグラフィックスサブシステム



マイクロコントローラーは非常に強力であり、ビデオ信号の生成専用に使用することにしたため(VPU-Video Processing Unitと呼びます)、ダブルバッファーを同時に編成することにしました。







2番目のマイクロコントローラー(PPU、Picture Processing Unit、Atmega1284チップも20 MHz)がRAMチップ番号1(私はVRAM1と呼びます)に画像を生成し、最初のマイクロコントローラーが2番目のチップ(VRAM2)の内容をテレビに同時に送信したことが判明しました。







PALシステムの1フレームと2フレームが1/25秒後、VPUはVRAMを切り替えて交換し、PPUはVRAM2に画像を生成し、VPUはVRAM1をTV出力にダンプします。







ビデオカードは非常に複雑であることが判明しました。これは、両方のマイクロコントローラーが両方のメモリモジュールを使用できるように外部ハードウェアを使用し、RAMへのアクセスを高速化する必要があり、ビットバンギングもあるため、カウンター、ラインセレクター、トランシーバーなどとして74シリーズチップを追加する必要があったためです。 。







VPUとPPUのファームウェアも、グラフィックスを最大限に活用するために多くのコードを書く必要があったため、面倒であることが判明しました。 最初はすべてがアセンブラーで記述され、次に一部がCで書き直されました。







画像ビデオボード1







画像ビデオボード2







その結果、PPUは224x192ピクセルの画像を生成し、VPUを介してTVに送信します。 解像度が低いことがありますが、実際には当時のコンソールが実際に持っていたのとほぼ同じで、通常は256x224です。 わずかに低い解像度ですが、システムが1フレームで計算するために管理する機能を追加することができました。







昔のように、PPUには独自の厳格なメカニズムがあり、これを使用する必要があります。 バッキング(バッキング)は、タイルとも呼ばれる8x8ピクセルの文字からレンダリングされます。 背景のサイズは28x24タイルです。







バッキングがピクセルごとにスムーズにスクロールできるように、4つの仮想画面があり、それぞれがメモリ内に順番に入れられ、互いにラップされている28x24のタイルがあります。







imageBackground







画像仮想背景







背景の上に、PPUは64のスプライトをレンダリングできます。 スプライトは、高さまたは幅が8または16ピクセル、つまり1、2または4タイルで、水平および/または垂直に反転することもできます。







背面の上部には、単一のバッファ28x6タイルのサイズでオーバーレイをレンダリングすることもできます。これは、メインスプライトと背面のスクロールを妨げないように、HUD、スコアをレンダリングするためのものでした。







「高度な」機能の1つは、背面全体をスクロールするのではなく、各行を個別にスクロールできることです。これにより、分割画面や近視差など、あらゆる種類の興味深い効果が得られます。







また、各タイルを0〜3の値に設定できる属性テーブルがあり、タイルのページを指定したり、1つの属性を持つすべてのタイルのシンボル値を増やしたりできます。 これは、バックアップの一部を定期的に変更する必要があり、CPUが各タイルを個別に計算する必要がない場合に便利です。「属性1のすべてのタイル、キャラクターの数値を2増やす」などと言うだけで十分です。たとえば、疑問符がアニメーション化されているマリオのブロックタイルや、すべてのタイルが絶えず変化して滝の効果を生み出している滝があるゲームで観察してください。







CPU



ビデオカードが機能し始めたとき、CPUをセットトップボックスのZilog 80として使用し始めました。







Z80が選ばれた理由の1つは、これがクールなレトロCPUであるという事実に加えて、メモリ用とI / Oポート用の2つの16ビットスペースに対処する能力です。 、アドレス指定できるのは16ビット空間のみであり、メモリとさまざまな外部デバイス、ビデオ、オーディオ、ジョイスティック、ハードウェア乱数ジェネレーターなどの両方をマップする必要があります。 メモリ内の64キロバイトのコードとデータ用に完全に放棄され、外部デバイスへのアクセス用に2つのアドレス空間を持つ方が便利です。







まず、CPUをテストプログラムのあるEEPROMに接続し、I / Oスペースを介してインストールしたマイクロコントローラーに接続して、 RS232を介してコンピューターと通信し、CPUやその他すべての動作を監視できるようにしました。 私はこの20 MHzで動作するAtmega324マイクロコントローラーをIO MCUと呼んでいます-入力/出力マイクロコントローラーユニット、ゲームコントローラー(ジョイスティック)、SDカード、リーダー、 PS / 2キーボード、コミュニケーターへのRS232経由のアクセスを制御します。







imageCPUボード1







CPUは128キロバイトのメモリチップに接続し、そのうち56キロバイトしか使用できません。これはもちろんナンセンスですが、128キロバイトまたは32キロバイトのチップしか入手できません。 メモリは8キロバイトのROMと56キロバイトのRAMで構成されていることが判明しました。







その後、 このライブラリを使用してIO MCUファームウェアを更新し、SDカードリーダーのサポートを得ました。







これで、CPUはディレクトリを調べて、ディレクトリにあるものを確認し、ファイルを開いて読み取ることができます。 これはすべて、I / O空間の特定のアドレスへの書き込みと読み取りによって行われます。







CPUをPPUに接続



次に、CPUとPPUを接続しました。 これを行うために、デュアルポートRAMを購入する「シンプルなソリューション」を適用しました。これは、2つの異なるバスに直接接続できるRAMチップです。 これにより、ラインセレクターのような追加のチップを取り除くことができ、さらに、両方のチップからほぼ同時にメモリにアクセスできます。 別のPPUは、 マスク不能割り込みをアクティブにすることにより、各フレームでCPUに直接アクセスできます。 CPUは各フレームで割り込みを受信することがわかります。これは、さまざまなタイミングタスクや、グラフィックの更新を行うタイミングを理解するのに役立ちます。







CPU、PPU、およびVPUの各対話フレームは、次のスキームに従って発生します。







  1. PPUは、PPUメモリから内部メモリに情報をコピーします。
  2. PPUは割り込み信号をCPUに送信します。
  3. 同時に:

    • CPUは割り込み機能にジャンプし、新しいグラフィックス状態でPPUメモリの更新を開始します。 プログラムは、割り込みから次のブロックまで戻る必要があります。
    • PPUは、VRAMの1つに以前にコピーされた情報に基づいて画像をレンダリングします。
    • VPUは、別のVRAMからTV出力に画像を送信します。


ほぼ同時期に、ゲームコントローラーのサポートを開始しました。最初は任天堂のコントローラーを使用したかったのですが、ソケットはプロプライエタリで一般に見つけるのが難しいので、Mega Drive / Genesisと互換性のある6ボタンコントローラーに決めました。どこにでもあります。







imageジョイントボード1







最初の実際のゲームを書く



この時点で、PPUの制御、ジョイスティックの操作、SDカードの読み取りが可能なCPUがすでにありました... 最初のゲーム書く時間でした、もちろんZ80アセンブラーで、空き時間から数日かかりました。









ダイナミックグラフィックを追加する



すべてがスーパーで、私は自分のゲームコンソールを持っていましたが、ゲームではPPUメモリに縫い付けられたグラフィックスを使用する必要があり、特定のゲームのタイルを描画することは不可能であり、ROMを再フラッシュするだけで変更することができたため、これでは十分ではありませんでした CPUがタイルの文字をロードできるようにメモリを追加する方法を考え始めました。その後、PPUがそこからすべてを読み取ることができ、プレフィックスがすでに複雑で大きいことが判明したため、それを簡単にする方法を考え始めました。







そして、私は次のことを思いつきました:PPUのみがこの新しいメモリにアクセスでき、CPUはそこにあるPPUを介してデータをロードします。このロードプロセスの進行中、このメモリは描画に使用できませんが、現時点ではROMからの描画が可能になります。







ロードの終了後、CPUは内部ROMメモリをこの新しいメモリに切り替えます。これをCharacter RAM(CHR-RAM)と呼びます。このモードでは、PPUはダイナミックグラフィックスの描画を開始しますが、これはおそらく最善の解決策ではありませんが、動作します。 その結果、128キロバイトの新しいメモリがインストールされ、背景用に8x8ピクセルの1024文字、スプライト用に同じ文字数を保存できます。







imageジョイントボード2







そして最後に音



手が最後に音に達しました。 最初は、 Uzeboxにあるようなサウンド、つまりマイクロコントローラーが4チャンネルのPWMサウンドを生成するサウンドが必要でした。







しかし、ビンテージチップを簡単に手に入れることができ、いくつかのFM合成チップYM3438を注文しました。これらのユーザーは、Mega Drive / Genesisで使用されていたYM2612と完全に互換性があります。 それらをインストールすることにより、マイクロコントローラによって生成された高品質の音楽Mega Driveとサウンドエフェクトを取得できます。







別のマイクロコントローラーをインストールし、SPU(Sound Processor Unit)と呼びました。これはYM3438を制御し、自分でサウンドを生成できます。 CPUはデュアルポートメモリを介して制御しますが、今回はわずか2キロバイトです。







グラフィックユニットと同様に、サウンドユニットにはPCMサンプルとサウンドパッチを格納するための128キロバイトのメモリがあり、CPUはSPUにアクセスしてこのメ​​モリにデータをロードします。 CPUは、このメモリからコマンドを実行するようにSPUに指示するか、フレームごとにSPUのコマンドを更新することが判明しました。







CPUは、SPUメモリ内の4つの循環バッファーを介して4つのPWMチャネルを制御します。 SPUはこれらのバッファを通過し、書き込まれたコマンドを実行します。 FM合成チップ用のこのようなバッファーも1つあります。







全体として、グラフのように、CPUとSPU間の相互作用はスキームに従って行われます。







  1. SPUは、SPUから内部メモリにデータをコピーします。
  2. SPUはPPUからの割り込みを待機しています(これは同期のためです)
  3. 同時に

    • CPUは、PWMチャネルバッファーとFMシンセサイザーバッファーを更新します。
    • SPUは、内部メモリのデータに従って、バッファ内のコマンドを実行します。
    • これに加えて、SPUは16キロヘルツの周波数でPWMサウンドを更新します。


画像サウンドボード1







最後に出てきたもの



すべてのブロックの準備が整った後、ブレッドボードに行く人もいました。

CPUブロックについては、カスタムPCBを開発して注文することができました。他のモジュールに価値があるかどうかはわかりません。PCBがすぐに機能したことは本当に幸運だったと思います。







現在のブレッドボードには(これまでのところ)音声のみが残っています。

今日の様子は次のとおりです。







imageConsole 1







建築



この図は、各ブロックのコンポーネントと、それらが相互作用する方法を示しています。 示されていない唯一のものは、割り込みの形式での各フレームのPPUからCPUへの信号と、SPUに送られる同じ信号です。







imageArchitecture









最終仕様



CPU:









IO:









ビデオ:









音:









コンソールの開発



コンソール用に、ブートローダーが作成されました。 ブートローダーはROM CPUに配置され、最大8キロバイトを使用できます。 RAMの最初の256バイトを使用します。 ローダーは、CPUが最初に実行するものです。 SDカードにあるプログラムを表示する必要があります。







これらのプログラムは、コンパイルされたコードを含むファイル内にあり、グラフィックスとサウンドも含む場合があります。







プログラムを選択すると、そのプログラムはCPUメモリ、CHRメモリ、およびSPUメモリにロードされます。 その後、プログラムコードが実行されます。 コンソールにロードされるコードの最大サイズは、最初の256バイトに加えて56キロバイトです。もちろん、スタックとデータのスペースを考慮する必要があります。

そして、このブートローダーとこのコンソール用に書かれた他のプログラムは、以下で説明するのと同じ方法で作成されました。







メモリ/ IOマッピング



このプレフィックスを開発する際に重要なのは、CPUがさまざまなブロックにアクセスする方法を考慮し、入力入力のアドレス空間とメモリアドレス空間を正しく割り当てることです。







CPUは、メモリのアドレス空間を介してブートローダーのランダムアクセスメモリにアクセスします。







メモリアドレス空間

imageMemoryマッピング







そして、I / Oアドレス空間を介してPPU-RAM、SPU-RAM、およびIO MCUに接続します。







I / Oアドレス空間

imageIOマッピング







表からわかるように、すべてのデバイス、IO MCU、PPU、およびSPUのアドレスは、I / Oアドレス空間内に割り当てられます。







PPU管理



表の情報から、PPU制御のために、I / Oアドレス空間のアドレス1000h-1FFFhで利用可能なPPUメモリに書き込む必要があることがわかります。







PPUアドレス空間の割り当て







imagePPUマッピング







PPUステータスは次の値を取ることができます。







  1. 組み込みグラフィックモード
  2. ダイナミックグラフィックモード(CHR-RAM)
  3. CHRメモリーの記録モード
  4. 記録が完了し、CPUからのモード確認を待機しています


ここで、たとえば、スプライトをどのように操作できるか:

プレフィックスは一度に64個のスプライトを描画できます。 それらのデータは、各スプライトの5バイトの情報(5 * 64 = 320)が落ちるごとに、アドレス1004h-1143h(320バイト)のI / Oアドレス空間を通じてCPUを介して利用できます。







  1. 異なるフラグのバイト。このバイトの各ビットはフラグです:Active、Flipped_X、Flipped_Y、PageBit0、PageBit1、AboveOverlay、Width16、Height16。
  2. シンボルバイト、テーブルのシンボル番号(上記のフラグで定義)。
  3. カラーキーバイト(つまり、透明な色)
  4. X座標バイト
  5. Y座標バイト


したがって、スプライトを表示するには、アクティブフラグを1に設定し、可視性内でXおよびY座標を設定する必要があります。32/ 32座標はスプライトを画面の左上隅に配置し、低い値はスプライトを非表示にするか、部分的に表示します。







次に、文字コードと透明色を設定できます。







たとえば、スプライト番号10を表示する必要がある場合、アドレスは4145(1004h +(5 x 9))になり、アクティブ化のために値1を書き込みます。たとえば、x = 100およびy = 120、アドレス4148に値100を書き込み、アドレス4149値120。







アセンブラーを使用する



コンソールのプログラミング方法の1つはアセンブラーです。







次に、1つのスプライトを表示し、アニメーション化して、画面の端から外に移動する方法の例を示します。







ORG 2100h PPU_SPRITES: EQU $1004 SPRITE_CHR: EQU 72 SPRITE_COLORKEY: EQU $1F SPRITE_INIT_POS_X: EQU 140 SPRITE_INIT_POS_Y: EQU 124 jp main DS $2166-$ nmi: ;    (NMI) ld bc, PPU_SPRITES + 3 ld a, (sprite_dir) and a, 1 jr z, subX in a, (c) ;  X inc a out (c), a cp 248 jr nz, updateY ld a, (sprite_dir) xor a, 1 ld (sprite_dir), a jp updateY subX: in a, (c) ;  X dec a out (c), a cp 32 jr nz, updateY ld a, (sprite_dir) xor a, 1 ld (sprite_dir), a updateY: inc bc ld a, (sprite_dir) and a, 2 jr z, subY in a, (c) ;  Y inc a out (c), a cp 216 jr nz, moveEnd ld a, (sprite_dir) xor a, 2 ld (sprite_dir), a jp moveEnd subY: in a, (c) ;  Y dec a out (c), a cp 32 jr nz, moveEnd ld a, (sprite_dir) xor a, 2 ld (sprite_dir), a moveEnd: ret main: ld bc, PPU_SPRITES ld a, 1 out (c), a ;   0 inc bc ld a, SPRITE_CHR out (c), a ;    0 inc bc ld a, SPRITE_COLORKEY out (c), a ;     0 inc bc ld a, SPRITE_INIT_POS_X out (c), a ;     0 inc bc ld a, SPRITE_INIT_POS_Y out (c), a ;   Y  0 mainLoop: jp mainLoop sprite_dir: DB 0
      
      





C言語を使用する







C言語を使用することもできます。そのためには、SDCCコンパイラといくつかの追加ユーティリティが必要です。







Cコードは遅くなる場合がありますが、それを書くのは速くて簡単です。







上記のアセンブラコードと同じコードの例を次に示します。PPUの呼び出しに役立つライブラリを使用しています。







 #include <console.h> #define SPRITE_CHR 72 #define SPRITE_COLORKEY 0x1F #define SPRITE_INIT_POS_X 140 #define SPRITE_INIT_POS_Y 124 struct s_sprite sprite = { 1, SPRITE_CHR, SPRITE_COLORKEY, SPRITE_INIT_POS_X, SPRITE_INIT_POS_Y }; uint8_t sprite_dir = 0; void nmi() { if (sprite_dir & 1) { sprite.x++; if (sprite.x == 248) { sprite_dir ^= 1; } } else { sprite.x--; if (sprite.x == 32) { sprite_dir ^= 1; } } if (sprite_dir & 2) { sprite.y++; if (sprite.y == 216) { sprite_dir ^= 2; } } else { sprite.y--; if (sprite.x == 32) { sprite_dir ^= 2; } } set_sprite(0, sprite); } void main() { while(1) { } }
      
      





ダイナミックグラフィックス



(元のカスタムグラフィックス。約あたり。)







プレフィックスROMでは、バッキング用のタイルの1ページと既製のスプライトの別のページが縫製されています)、デフォルトではこの固定グラフィックのみを使用できますが、ダイナミックに切り替えることができます。







私の目標は、バイナリ形式のすべての必要なグラフィックスがすぐにCHR RAMにロードされ、ROMからのブートローダーのコードがこれを実行できるようにすることでした。 これを行うために、さまざまな便利なシンボルを使用して正しいサイズの写真をいくつか作成しました。







imageSampleタイルコンポーネント







ダイナミックグラフィックメモリは、それぞれ8x8ピクセルの256文字の4ページとスプライトの同じ文字の4ページで構成されているため、写真をPNG形式に変換し、重複したものを削除しました。







画像サンプルキャラクターシート







そして、彼は自己記述ツールを使用して、すべてを8x8ブロックのバイナリRGB332形式に変換しました。







imageGraphicsコマンドライン







結果として、すべての文字が次々と連続して移動し、それぞれが64バイトを使用する文字を含むファイルがあります。









Wave RAWサンプルは、8ビットの8 KHz PCMサンプルに変換されます。







PWMと音楽の効果音のパッチは、特別な指示で書かれています。







Yamaha YM3438 FM合成チップについては、Genesis YM2612チップ用のPAL同期音楽を生成するDefleMaskというプログラムを見つけました。これはYM3438と互換性があります。







DefleMaskは音楽をVGM形式でエクスポートし、別の専用ユーティリティを使用して自分のバイナリ形式に変換します。







3種類すべてのサウンドのすべてのバイナリは1つのバイナリファイルに結合され、ブートローダーはこのファイルを読み取り、SDN RAMサウンドメモリに読み込むことができます。







imageSoundコマンドライン







最終リンクリンク



バイナリ実行可能コード、グラフィックス、およびサウンドは、1つのPRGファイルに結合されます。 PRGファイルには、オーディオデータとグラフィックデータの有無、占有量、データ自体がすべて記述されたヘッダーがあります。







このようなファイルはSDカードに書き込むことができ、コンソールプリローダーはそれを考慮して、すべてを適切な場所にダウンロードし、プログラム実行可能コードを起動します。







imagePRGコマンドライン







エミュレーター



wxWidgetsを使用してC ++でコンソールのエミュレーターを作成し、その開発を容易にしました。







CPUはlibz80ライブラリによってエミュレートされます。







デバッグのためにエミュレータに機能が追加されました。いつでも停止でき、アセンブラの段階的なデバッグを実行できます。この言語がゲームに使用された場合、Cのソースコードにマッピングされます。







グラフによれば、ビデオメモリ、シンボルテーブル、およびCHRメモリ自体を調べることができます。







デバッグツールが有効になっているエミュレータで実行されているプログラムの例を次に示します。







imageEmulatorデモ







プログラミングデモ



これらのビデオは、テレビのCRT画面を狙ったスマートフォンのカメラで撮影されたもので、画質が不完全であることをおIびします。







PS / 2キーボードからプログラム可能なBASICインタープリターは、最初のプログラムの後、スプライトをアクティブにして移動することにより、I / Oアドレス空間を介してPPUメモリに直接書き込む方法を示します。









このビデオでは、グラフィックのデモが、動的スクロールとスプライトの上下に移動するオーバーレイを背景に、プログラムで64個の16x16スプライトをダウンロードします。









サウンドデモでは、YM3438とPWMサウンドの機能を示します。このデモのサウンドデータとFM音楽とPWMサウンドは、128キロバイトのサウンドメモリのほぼすべてを占有します。









テトリスは、ほぼ独占的にバックグラウンド機能、YM3438の音楽、PWMパッチの効果音がグラフィックに使用されました。









おわりに



このプロジェクトは本当に夢がかなったものです。私は数年間、中断して、自由な時間を見て取り組んでいます。これまで自分のゲームのレトロなビデオコンソールを作成するとは思っていませんでした。 当然、完璧ではありません。私は確かにエレクトロニクスの専門家ではありません。セットトップボックスには明らかに要素が多すぎます。間違いなくそれを改善できる可能性があり、おそらく読者の一人がそれについて考えているだけです。







それでも、このプロジェクトに取り組む過程で、エレクトロニクス、ゲームコンソール、コンピューターデザイン、アセンブリ言語、その他の興味深いことについて多くのことを学び、最も重要なことは、自分が開発したハードウェアで書いたゲームをプレイすることで大きな満足を得ました収集しました。







コンソール/コンピューターなどを作成する計画があります。 実際、私はすでに新しいセットトップボックスを作成していますが、ほとんど準備ができており、FPGAボードといくつかの追加コンポーネント(このプロジェクトよりもはるかに少ない量です)に基づく単純化されたレトロなセットトップボックスです。







ここでこのプロジェクトについて多くのことを書きましたが、間違いなくもっと議論できますが、サウンドエンジンの動作、CPUとの対話方法についてはほとんど言及していません。言うことになります。







読者の反応を見て、更新、個々のプレフィックスブロックまたはその他のプロジェクトの詳細に焦点を当てた記事をもっと書くことができます。







私にインスピレーションを与え、技術的な知識を手伝ってくれたプロジェクト、サイト、Youtubeチャンネル:



これらのサイト/チャンネルは刺激を受けただけでなく、このプロジェクトの作業中に生じた複雑な問題の解決策を見つけるのにも役立ちました。









ここまで読んでくれてありがとう。 :)







質問やフィードバックがある場合は、 コメントに書いてください(Githubの英語の元の記事。約。)








All Articles