チューリング爆弾

1936 年、科学者のアラン チューリングは、著書『計算可能な数について、現実の問題への応用』の中で、数学における可解性の問題に終止符を打つ可能性のある万能計算機の使用について説明しました。その結果、彼は、そのようなマシンの作業結果が反転され、それ自体でループバックされる場合、そのようなマシンは何も正しく解決できないだろうという結論に達しました。 *理想的な* アンチウイルス、*理想的な* タイル セッター、クラッシュに対して理想的なフレーズを提案するプログラムなどを作成するのは不可能であることが判明しました。パラドックス!

しかし、この汎用コンピューティング マシンはあらゆるアルゴリズムの実装に使用でき、英国諜報機関はそれを利用してチューリングを雇い、第二次世界大戦中にドイツのメッセージを解読するための「ボンベ」マシンの作成を許可しました。

以下は、元のドキュメントに基づいた、Dart 言語による単一テープ コンピューターの OOP モデリングです。

チューリング マシンはセクションに分割されたフィルムで構成されており、各セクションにはシンボルが含まれており、シンボルは読み書きできます。映画クラスの例:

final _map = Map<int, String>(); 

  String read({required int at}) { 
    return _map[at] ?? ""; 
  } 

  void write({required String symbol, required int at}) { 
    _map[at] = symbol; 
  } 
}

「スキャン スクエア」もあり、フィルム上を移動して情報を読み書きすることができます (現代語で言えば –)。磁気ヘッド。磁気ヘッド クラスの例:

  int _index = 0; 
  InfiniteTape _infiniteTape; 
  TapeHead(this._infiniteTape) {} 

  String next() { 
    _index += 1; 
    move(to: _index); 
    final output = read(); 
    return output; 
  } 

  String previous() { 
    _index -= 1; 
    move(to: _index); 
    final output = read(); 
    return output; 
  } 

  void move({required int to}) { 
    this._index = to; 
  } 

  String read() { 
    return _infiniteTape.read(at: this._index); 
  } 

  void write(String symbol) { 
    _infiniteTape.write(symbol: symbol, at: this._index); 
  } 

  int index() { 
    return _index; 
  } 
} 

マシンには、次に何を行うかを決定できる「m 構成」が含まれています。現代語で–状態と状態ハンドラー。状態ハンドラーの例:

  FiniteStateControlDelegate? delegate = null; 

  void handle({required String symbol}) { 
    if (symbol == OPCODE_PRINT) { 
      final argument = delegate?.nextSymbol(); 
      print(argument);
    } 
    else if (symbol == OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER) { 
      final to = int.tryParse(delegate!.nextSymbol())!; 
      final value = new Random().nextInt(to); 
      delegate!.nextSymbol(); 
      delegate!.write(value.toString()); 
    } 
    else if (symbol == OPCODE_INPUT_TO_NEXT) { 
      final input = stdin.readLineSync()!; 
      delegate?.nextSymbol(); 
      delegate?.write(input); 
    } 
    else if (symbol == OPCODE_COPY_FROM_TO) { 
      final currentIndex = delegate!.index(); 

и т.д. 

この後、「構成」を作成する必要があります。現代の言葉で言うと、これらはオペレーション コード (オペコード) とそのハンドラーです。オペコードの例:

const OPCODE_PRINT = "print"; 
const OPCODE_INCREMENT_NEXT = "increment next"; 
const OPCODE_DECREMENT_NEXT = "decrement next"; 
const OPCODE_IF_PREVIOUS_NOT_EQUAL = "if previous not equal"; 
const OPCODE_MOVE_TO_INDEX = "move to index"; 
const OPCODE_COPY_FROM_TO = "copy from index to index"; 
const OPCODE_INPUT_TO_NEXT = "input to next"; 
const OPCODE_GENERATE_RANDOM_NUMBER_FROM_ZERO_TO_AND_WRITE_AFTER = "generate random number from zero to next and write after"; 

オペコードと停止ハンドラーを作成することを忘れないでください。そうしないと、解決の問題を証明できないか、証明できないことになります。

次に、「メディエーター」パターンを使用して、チューリング マシン クラス内のすべてのクラスを接続し、クラスのインスタンスを作成し、テープ レコーダーでプログラムを録音し、テープをロードすると、使用できるようになります。

私個人にとって、何が主要であるかという問題は依然として興味深いものでした –万能計算機の作成、または「Entscheidungsproblem」の証明、その結果副産物として計算機が登場しました。

カセット

楽しみのために、自分のバージョンのマシン用にいくつかのカセット プログラムを録音しました。

ハローワールド

hello world 
stop

Считаем до 16-ти

0
if previous not equal
16
copy from index to index
1
8
print
?
move to index
0
else
copy from index to index
1
16
print
?
print
Finished!
stop

Самой интересной задачей было написание Quine программы, которая печатает свой исходный код, для одноленточной машины. Первые 8 часов мне казалось что эта задача не решаема с таким малым количеством опкодов, однако всего через 16 часов оказалось что я был не прав.

Реализация и примеры кассет, источники ниже.

Ссылки

https://gitlab.com/demensdeum/turing-machine

Источники

https://www.astro.puc.cl/~rparra/tools/PAPERS/turing_1936.pdf
https://kpolyakov.spb.ru/prog/turing.htm
https://www.youtube.com/watch?v=dNRDvLACg5Q
https://www.youtube.com/watch?v=jP3ceURvIYc
https://www.youtube.com/watch?v=9QCJj5QzETI
https://www.youtube.com/watch?v=HeQX2HjkcNo&t=0s

Sega Genesis #5 のアセンブリでの書き込み

このメモでは、ジョイスティックの読み取り、スプライトの位置の変更、水平方向の反転、Sega Genesis エミュレータ、および場合によってはコンソール自体のプロセスについて説明します。

将棋ジョイスティックのクリックの読み取りと「イベント」の処理は、次のスキームに従って行われます。

<オル>

  • 押されたボタンのビットの組み合わせをリクエストする
  • 押されたボタンのビットを読み取る
  • ゲームロジックレベルでの処理
  • スケルトン スプライトを移動するには、現在の位置の変数を保存する必要があります。

    RAM

    ゲーム ロジック変数は RAM に保存されます。これより優れたものはまだ見つかっていません。変数アドレスを宣言して、レンダリング コードを変更しましょう。

    skeletonYpos = $FF0002 
    frameCounter = $FF0004 
    skeletonHorizontalFlip = $FF0006
    
        move.w #$0100,skeletonXpos 
        move.w #$0100,skeletonYpos 
        move.w #$0001,skeletonHorizontalFlip 
    
    FillSpriteTable: 
        move.l #$70000003,vdp_control_port 
        move.w skeletonYpos,vdp_data_port  
        move.w #$0F00,vdp_data_port 
        move.w skeletonHorizontalFlip,vdp_data_port 
        move.w skeletonXpos,vdp_data_port 
    

    ご覧のとおり、作業に利用できるアドレスは 0xFF0000 で始まり 0xFFFFFF で終わり、合計 64 KB のメモリが利用可能です。スケルトンの位置は、skeletonXpos、skeletonYpos で宣言され、水平方向の反転は、skeletonhorizo​​ntalflip で宣言されます。

    ジョイパッド

    VDP と同様に、ジョイパッドの操作は 2 つのポートを介して個別に行われます。制御ポートとデータ ポート。最初のポートは 0xA10009 と 0xA10003 の共通番号です。ジョイパッドを使用する場合、興味深い機能が 1 つあります。まず、ポーリング用のボタンの組み合わせをリクエストする必要があります。次に、バス上の更新を待った後、必要なボタンの押し方を読み取ります。 C/B および D パッド ボタンの場合、これは 0x40 です。以下の例:

      move.b #$40,joypad_one_control_port; C/B/Dpad 
      nop ; bus sync 
      nop ; bus sync 
      move.b joypad_one_data_port,d2 
      rts 
    

    レジスタ d2 では、ボタンが押されたか押されなかったかの状態が残ります。一般に、日付ポートを通じて要求された内容が残ります。その後、お気に入りのエミュレータの Motorola 68000 レジスタ ビューアに移動し、キーストロークに応じて d2 レジスタが何に等しいかを確認します。賢明な方法では、マニュアルでそれを知ることができますが、私たちは彼らの言葉を鵜呑みにしません。 d2

    レジスタで押されたボタンのさらなる処理

        cmp #$FFFFFF7B,d2; handle left 
        beq MoveLeft  
        cmp #$FFFFFF77,d2; handle right  
        beq MoveRight  
        cmp #$FFFFFF7E,d2; handle up  
        beq MoveUp  
        cmp #$FFFFFF7D,d2; handle down  
        beq MoveDown  
        rts

    Проверять нужно конечно отдельные биты, а не целыми словами, но пока и так сойдет. Теперь осталось самое простое – написать обработчики всех событий перемещения по 4-м направлениям. Для этого меняем переменные в RAM, и запускаем процедуру перерисовки.

    Пример для перемещения влево + изменение горизонтального флипа:

        move.w skeletonXpos,d0 
        sub.w #1,d0 
        move.w d0,skeletonXpos 
        move.w #$0801,skeletonHorizontalFlip 
        jmp FillSpriteTable

    После добавления всех обработчиков и сборки, вы увидите как скелет перемещается и поворачивается по экрану, но слишком быстро, быстрее самого ежа Соника.

    Не так быстро!

    Чтобы замедлить скорость игрового цикла, существуют несколько техник, я выбрал самую простую и не затрагивающую работу с внешними портами – подсчет цифры через регистр пока она не станет равна нулю.

    Пример замедляющего цикла и игрового цикла:

      move.w #512,frameCounter 
    WaitFrame: 
      move.w frameCounter,d0 
      sub.w #1,d0 
      move.w d0,frameCounter 
      dbra d0,WaitFrame 
    GameLoop: 
      jsr ReadJoypad 
      jsr HandleJoypad 
      jmp GameLoop 
    

    この後、スケルトンの実行速度が遅くなりますが、これは必要なことです。私が知っているように、「速度を落とす」ための最も一般的なオプションは、垂直同期フラグをカウントすることです。これにより、画面が描画された回数をカウントできるため、特定の fps に関連付けられます。

    リンク

    https://gitlab .com/demensdeum/segagenesisamples/-/blob/main/8Joypad/vasm/main.asm

    ソース

    https://www.chibiakumas.com/68000/platform2.php
    https://huguesjohnson.com/programming/genesis/tiles-sprites/

    Sega Genesis #4 のアセンブリでの作成

    この投稿では、Sega Genesis コンソールの VDP エミュレータを使用してスプライトを描画する方法を説明します。
    スプライトのレンダリングのプロセスは、タイルのレンダリングと非常に似ています。

    <オル>

  • カラーを CRAM にロードする
  • スプライト 8×8 の一部を VRAM にアップロードする
  • VRAM にスプライト テーブルを埋める
  • たとえば、32×32 ピクセルの剣を持ったスケルトンのスプライトを考えてみましょう。

    Skeleton Guy [Animated] by Disthorn

    クラム

    ImaGenesis を使って、アセンブラ用の CRAM カラーと VRAM パターンに変換してみましょう。この後、asm形式のファイルを2つ取得し、色をワードサイズに書き換え、タイルを正しい順序で配置して描画する必要があります
    興味深い情報: 0xF レジスタを介して VDP 自動インクリメントをワード サイズに切り替えることができます。これにより、CRAM カラー フィル コードからアドレス インクリメントが削除されます。

    VRAM

    将棋マニュアルには、大きなスプライトに対する正しい牌の順序が記載されていますが、私たちはより賢いので、ブログからインデックスを取得します。ちび悪魔、インデックス 0 から数え始めましょう:

    0 4 8 12

    1 5 9 13

    2 6 10 14

    3 7 11 15

    なぜすべてが逆さまなのですか?どうしたものか、コンソールは日本語です!右から左でも構いません
    よ!asm スプライト ファイル内の順序を手動で変更してみましょう:

    	dc.l	$11111111	; Tile #0 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111	; Tile #4 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111
    	dc.l	$11111111	; Tile #8 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111122 
    	dc.l	$11111122 
    	dc.l	$11111166 
    	dc.l	$11111166	; Tile #12 
    	dc.l	$11111166 
    	dc.l	$11111166 
    	и т.д. 
    

    通常のタイル/パターンと同様にスプライトを読み込みます:

      lea Sprite,a0 
      move.l #$40200000,vdp_control_port; write to VRAM command 
      move.w #128,d0 ; (16*8 rows of sprite) counter 
    SpriteVRAMLoop: 
      move.l (a0)+,vdp_data_port; 
      dbra d0,SpriteVRAMLoop 
    

    スプライトを描画するには、スプライト テーブルに記入するだけです。

    スプライト テーブル

    スプライト テーブルは VRAM に入力され、その位置のアドレスが VDP レジスタ 0x05 に入力されます。アドレスはこれも複雑です。アドレス F000 の例はマニュアルで確認できます。

    Ок, теперь запишем наш спрайт в таблицу. Для этого нужно заполнить “структуру” данных состоящую из четырех word. Бинарное описание структуры вы можете найти в мануале. Лично я сделал проще, таблицу спрайтов можно редактировать вручную в эмуляторе Exodus.
    構造パラメータは名前から明らかです (例: XPos、YPos)。座標、タイル –描画開始タイルの番号、HSize、VSize –パーツ 8×8、HFlip、VFlip – を追加することでスプライト サイズを変更します。ハードウェアによるスプライトの水平方向と垂直方向の回転

    スプライトは画面外にある可能性があることを覚えておくことが非常に重要です。これは正しい動作です。なぜなら…オフスクリーン スプライトをメモリからアンロード –非常にリソースを大量に消費するアクティビティ
    エミュレータにデータを入力した後、VRAM からアドレス 0xF000 にデータをコピーする必要があります。Exodus はこの機能もサポートしています。
    タイルの描画と同様に、まず VDP 制御ポートにアクセスしてアドレス 0xF000 への書き込みを開始し、次に構造をデータ ポートに書き込みます。
    VRAM アドレス指定の説明はマニュアルまたはブログ 名前のないアルゴリズム

    簡単に言うと、VDP アドレス指定は次のように機能します。
    [..DC BA98 7654 3210 …。 …。 …。 ..FE]
    ここで、hex は目的のアドレスのビット位置です。最初の 2 ビットは、要求されているコマンドのタイプです (例: 01 –)。 VRAMに書き込みます。次に、アドレス 0XF000 については次のようになります。
    0111 0000 0000 0000 0000 0000 0000 0011 (70000003)

    その結果、次のコードが得られます。

      move.l #$70000003,vdp_control_port 
      move.w #$0100,vdp_data_port 
      move.w #$0F00,vdp_data_port 
      move.w #$0001,vdp_data_port 
      move.w #$0100,vdp_data_port 
    

    この後、スケルトン スプライトは座標 256、256 に表示されます。いいですね?

    リンク

    https://gitlab.com/demensdeum /segagenesissamples/-/tree/main/7Sprite/vasm
    https://opengameart.org/content/skeleton-guy-animated

    ソース

    https://namelessalgorithm.com/genesis/blog/vdp/https://www.chibiakumas.com/68000/platform3.php#LessonP27
    https://plutiedev.com/sprites

    Sega Genesis #3 のアセンブリでの書き込み

    この記事では、アセンブラを使用して Sega Genesis エミュレータでタイルから画像を表示する方法を説明します。
    Exodus エミュレータのDemens Deum スプラッシュ画像は次のようになります:

    タイルを使用して PNG 画像を出力するプロセスは、段階的に実行されます。

    <オル>

  • 画像を将棋画面サイズに縮小する
  • PNG を色とタイルに分けたアセンブリ データ コードに変換します
  • カラー パレットを CRAM にロードする
  • タイル/パターンを VRAM にロードする
  • VRAM のプレーン A/B アドレスにタイル インデックスをロードする
  • Blender などのお気に入りのグラフィック エディタを使用して、画像を将棋画面のサイズに縮小できます。
  • PNG 変換

    画像を変換するには、ImaGenesis ツールを使用できます。wine で作業するには、Visual Basic 6 ライブラリが必要です。これらのライブラリは、 winetricks (winetricks vb6run) を使用してインストールするか、インターネットから RICHTX32.OCX をダウンロードして配置できます。正しく動作するためにアプリケーション フォルダーを参照してください。

    ImaGenesis では、4 ビット カラーを選択し、カラーとタイルを 2 つのアセンブラ形式ファイルにエクスポートする必要があります。次に、色を含むファイルで、各色を単語 (2 バイト) に入れる必要があります。これには、dc.w オペコードを使用します。

    CRAM スプラッシュ画面の例:

      dc.w $0000 
      dc.w $0000 
      dc.w $0222 
      dc.w $000A 
      dc.w $0226 
      dc.w $000C 
      dc.w $0220 
      dc.w $08AA 
      dc.w $0446 
      dc.w $0EEE 
      dc.w $0244 
      dc.w $0668 
      dc.w $0688 
      dc.w $08AC 
      dc.w $0200 
      dc.w $0000 
    

    タイル ファイルはそのままにしておきます。このファイルには、読み込むための正しい形式がすでに含まれています。タイル ファイルの一部の例:

    	dc.l	$11111111	; Tile #0 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111	; Tile #1 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    	dc.l	$11111111 
    

    上の例からわかるように、タイルは CRAM カラー パレット インデックスで構成される 8×8 グリッドです。

    CRAM の色

    CRAM へのロードは、制御ポート (vdp 制御) への特定の CRAM アドレスへのカラー ロード コマンドを設定することによって行われます。コマンドの形式は Sega Genesis ソフトウェア マニュアル (1989 年) に説明されています。次の色に移動するには、アドレスに 0x20000 を追加するだけであることを付け加えておきます。

    次に、カラーをデータ ポート (vdp データ) にロードする必要があります。読み込みを理解する最も簡単な方法は、以下の例を使用することです。

        lea Colors,a0 ; pointer to Colors label 
        move.l #15,d7; colors counter 
    VDPCRAMFillLoopStep: 
        move.l  d0,vdp_control_port ;  
        move.w  (a0)+,d1; 
        move.w  d1,(vdp_data_port); 
        add.l #$20000,d0 ; increment CRAM address 
        dbra d7,VDPCRAMFillLoopStep 
    

    VRAM 内のタイル

    次は、タイル/パターンを VRAM ビデオ メモリにロードします。これを行うには、VRAM 内のアドレス (たとえば、0x00000000) を選択します。 CRAM と同様に、VRAM に書き込むコマンドと開始アドレスを使用して VDP 制御ポートに接続します。

    この後、ロングワードを VRAM にアップロードできます。VRAM 自動インクリメント モードがあるため、CRAM とは異なり、各ロングワードにアドレスを指定する必要がありません。 VDP レジスタ フラグ 0x0F (dc.b $02) を使用して有効にできます。

      lea Tiles,a0 
      move.l #$40200000,vdp_control_port; write to VRAM command 
      move.w #6136,d0 ; (767 tiles * 8 rows) counter 
    TilesVRAMLoop: 
      move.l (a0)+,vdp_data_port; 
      dbra d0,TilesVRAMLoop 
    

    プレーン A/B のタイル インデックス

    次に、インデックスに従って画面をタイルで埋める必要があります。これを行うには、VDP レジスタ (0x02、0x04) に入力されるプレーン A/B アドレスで VRAM が埋められます。難しいアドレス指定の詳細については、Sega のマニュアルを参照してください。この例では、VRAM アドレスは 0xC000 です。そこにインデックスをアップロードしましょう。

    いずれにせよ、画像はオフスクリーン VRAM スペースを埋めるため、スクリーンスペースを描画した後、レンダラーは描画を停止し、カーソルが新しい行に移動したときに再び続行する必要があります。これを実装する方法には多くのオプションがあります。私は、画像幅カウンタとカーソル位置カウンタの 2 つのレジスタをカウントする最も単純なバージョンを使用しました。

    コード例:

      move.w #0,d0     ; column index 
      move.w #1,d1     ; tile index 
      move.l #$40000003,(vdp_control_port) ; initial drawing location 
      move.l #2500,d7     ; how many tiles to draw (entire screen ~2500) 
    
    imageWidth = 31 
    screenWidth = 64 
    
    FillBackgroundStep: 
      cmp.w	#imageWidth,d0 
      ble.w	FillBackgroundStepFill 
    FillBackgroundStep2: 
      cmp.w	#imageWidth,d0 
      bgt.w	FillBackgroundStepSkip 
    FillBackgroundStep3: 
      add #1,d0 
      cmp.w	#screenWidth,d0 
      bge.w	FillBackgroundStepNewRow 
    FillBackgroundStep4: 
      dbra d7,FillBackgroundStep    ; loop to next tile 
    
    Stuck: 
      nop 
      jmp Stuck 
    
    FillBackgroundStepNewRow: 
      move.w #0,d0 
      jmp FillBackgroundStep4 
    FillBackgroundStepFill: 
      move.w d1,(vdp_data_port)    ; copy the pattern to VPD 
      add #1,d1 
      jmp FillBackgroundStep2 
    FillBackgroundStepSkip: 
      move.w #0,(vdp_data_port)    ; copy the pattern to VPD 
      jmp FillBackgroundStep3 
    

    この後は、vasm を使用して rom を組み立て、シミュレーターを起動して画像を確認するだけです。

    デバッグ

    すべてがすぐにうまくいくわけではないため、次の Exodus エミュレータ ツールをお勧めします。

    <オル>

  • M68k プロセッサ デバッガ
  • m68k プロセッサ サイクル数の変更 (デバッガのスローモーション モードの場合)
  • ビューア CRAM、VRAM、プレーン A/B
  • m68k のドキュメントと使用されているオペコードを注意深く読んでください (すべてが一見したほど明白であるわけではありません)
  • Github でゲーム コード/逆アセンブリのサンプルを表示する
  • プロセッサ例外のサブルーチンを実装して処理する
  • プロセッサ例外サブルーチンへのポインタは、ROM ヘッダーに配置されます。GitHub には、genesis-debugger と呼ばれる、Sega 用の対話型ランタイム デバッガーを備えたプロジェクトもあります。

    利用可能なツールをすべて使用し、昔ながらの優れたコーディングを行い、Blast Processing を使用することもできます。

    リンク

    https://gitlab.com/demensdeum /segagenesisamples/-/tree/main/6Image/vasm
    http://devster.monkeeh.com/sega/imagenesis/
    https://github.com/flamewing/genesis-debugger

    ソース

    https://www.chibiakumas.com/68000/helloworld .php#レッスンH5
    https://huguesjohnson.com/programming/genesis/tiles-sprites/

     

    Sega Genesis #2 のアセンブリでの書き込み

    この投稿では、アセンブリ言語で将棋パレットに色を読み込む方法について説明します。
    Exodus エミュレータの最終結果は次のようになります。

    プロセスを簡単にするために、Genesis Software Manual (1989) という名前の PDF をインターネット上で見つけてください。このマニュアルにはプロセス全体が詳細に説明されています。実際、このメモはオリジナルのマニュアルの解説です。< /p>

    Sega エミュレータの VDP チップに色を書き込むには、次のことを行う必要があります。

    • TMSS 保護を無効にする
    • 正しいパラメータを VDP レジスタに書き込む
    • 希望の色を CRAM に書き込みます

    アセンブリには、vasmm68k_mot とお気に入りのテキスト エディタ (echo など) を使用します。アセンブリは次のコマンドで実行されます。

    Порты VDP

    VDP чип общается с M68K через два порта в оперативной памяти – порт контроля и порт данных.
    По сути:

    1. Через порт контроля можно выставлять значения регистрам VDP.
    2. Также порт контроля является указателем на ту часть VDP (VRAM, CRAM, VSRAM etc.) через которую передаются данные через порт данных

    Интересная информация: Сега сохранила совместимость с играми Master System, на что указывает MODE 4 из мануала разработчика, в нем VDP переключается в режим Master System.

    Объявим порты контроля и данных:

    vdp_data_port        = $C00000

    Отключить систему защиты TMSS

    Защита от нелицензионных игр TMSS имеет несколько вариантов разблокировки, например требуется чтобы до обращения к VDP в адресном регистре A1 лежала строка “SEGA”.

    MOVE.B A1,D0; Получаем версию хардвары цифрой из A1 в регистр D0 
    ANDI.B 0x0F,D0; По маске берем последние биты, чтобы ничего не сломать 
    BEQ.B SkipTmss; Если версия равна 0, скорее всего это японка или эмулятор без включенного TMSS, тогда идем в сабрутину SkipTmss 
    MOVE.L "SEGA",A1; Или записываем строку SEGA в A1 
    

    正しいパラメータを VDP レジスタに書き込みます

    そもそも、なぜ VDP レジスタに正しいパラメータを設定する必要があるのでしょうか? VDP は多くの機能を備えているため、レンダリングする前に必要な機能を使用して VDP を初期化する必要があるという考えです。そうしないと、VDP が VDP に何を求めているかを理解できなくなります。

    各レジスタは、特定の設定/動作モードを担当します。 Segov マニュアルには、24 の各レジスタのすべてのビット/フラグと、レジスタ自体の説明が記載されています。

    bigevilcorporation ブログからのコメントを含む既製のパラメーターを使用してみましょう。

    
    VDPReg0:   dc.b $14 ;  0: H interrupt on, palettes on 
    VDPReg1:   dc.b $74 ;  1: V interrupt on, display on, DMA on, Genesis mode on 
    VDPReg2:   dc.b $30 ;  2: Pattern table for Scroll Plane A at VRAM $C000 
                        ;     (bits 3-5 = bits 13-15) 
    VDPReg3:   dc.b $00 ;  3: Pattern table for Window Plane at VRAM $0000 
                        ;     (disabled) (bits 1-5 = bits 11-15) 
    VDPReg4:   dc.b $07 ;  4: Pattern table for Scroll Plane B at VRAM $E000 
                        ;     (bits 0-2 = bits 11-15) 
    VDPReg5:   dc.b $78 ;  5: Sprite table at VRAM $F000 (bits 0-6 = bits 9-15) 
    VDPReg6:   dc.b $00 ;  6: Unused 
    VDPReg7:   dc.b $00 ;  7: Background colour - bits 0-3 = colour, 
                        ;     bits 4-5 = palette 
    VDPReg8:   dc.b $00 ;  8: Unused 
    VDPReg9:   dc.b $00 ;  9: Unused 
    VDPRegA:   dc.b $FF ; 10: Frequency of Horiz. interrupt in Rasters 
                        ;     (number of lines travelled by the beam) 
    VDPRegB:   dc.b $00 ; 11: External interrupts off, V scroll fullscreen, 
                        ;     H scroll fullscreen 
    VDPRegC:   dc.b $81 ; 12: Shadows and highlights off, interlace off, 
                        ;     H40 mode (320 x 224 screen res) 
    VDPRegD:   dc.b $3F ; 13: Horiz. scroll table at VRAM $FC00 (bits 0-5) 
    VDPRegE:   dc.b $00 ; 14: Unused 
    VDPRegF:   dc.b $02 ; 15: Autoincrement 2 bytes 
    VDPReg10:  dc.b $01 ; 16: Vert. scroll 32, Horiz. scroll 64 
    VDPReg11:  dc.b $00 ; 17: Window Plane X pos 0 left 
                        ;     (pos in bits 0-4, left/right in bit 7) 
    VDPReg12:  dc.b $00 ; 18: Window Plane Y pos 0 up 
                        ;     (pos in bits 0-4, up/down in bit 7) 
    VDPReg13:  dc.b $FF ; 19: DMA length lo byte 
    VDPReg14:  dc.b $FF ; 20: DMA length hi byte 
    VDPReg15:  dc.b $00 ; 21: DMA source address lo byte 
    VDPReg16:  dc.b $00 ; 22: DMA source address mid byte 
    VDPReg17:  dc.b $80 ; 23: DMA source address hi byte, 
                        ;     memory-to-VRAM mode (bits 6-7)  
    

    OK、制御ポートに移動して、すべてのフラグを VDP レジスタに書き込みましょう。

        move.l  #VDPRegisters,a0 ; Пишем адрес таблицы параметров в A1 
        move.l  #$18,d0          ; Счетчик цикла - 24 = 18 (HEX) в D0 
        move.l  #$00008000,d1    ; Готовим команду на запись в регистр VDP по индексу 0, по мануалу - 1000 0000 0000 0000 (BIN) = 8000 (HEX) 
    
    FillInitialStateForVDPRegistersLoop: 
        move.b  (a0)+,d1         ; Записываем в D1 итоговое значение регистра VDP из таблицы параметров, на отправку в порт контроля VDP  
        move.w  d1,vdp_control_port     ; Отправляем итоговую команду + значение из D1 в порт контроля VDP 
        add.w   #$0100,d1        ; Поднимаем индекс регистра VDP на 1 (бинарное сложение +1 к индексу по мануалу Сеги) 
        dbra    d0,FillInitialStateForVDPRegistersLoop ; Уменьшаем счетчик регистров, продолжаем цикл если необходимо

    Самое сложное это прочитать мануал и понять в каком формате подаются данные на порт контроля, опытные разработчики разберутся сразу, а вот неопытные… Немного подумают и поймут, что синтаксис для записи регистров такой:

    0B100(5 бит – индекс регистра)(8 бит/байт – значение)

    0B1000001001000101 – записать в регистр VDP 2 (00010), значение флажков 01000101.

    Записать нужные цвета в CRAM

    Далее идем писать два цвета в память цветов CRAM (Color RAM). Для этого пишем в порт контроля команду на доступ к цвету по индексу 0 в CRAM и отправляем по дата порту цвет. Все!

    Пример:

        move.l  #$C0000000,vdp_control_port ; Доступ к цвету по индексу 0 в CRAM через порт контроля  
        move.w  #228,d0; Цвет в D0 
        move.w  d0,vdp_data_port; Отправляем цвет в порт данных 
    

    Exodus でエミュレータを構築して実行すると、画面がカラー 228 で塗りつぶされるはずです。

    最後のバイト 127 に基づいて、2 番目の色で塗りつぶしてみましょう。

    <コード>

      move.l  #$C07f0000,vdp_control_port ; Доступ к цвету по байту 127 в CRAM через порт контроля 
      move.w  #69,d0; Цвет в D0 
      move.w  d0,vdp_data_port; Отправляем цвет в порт данных 
    

    リンク

    https://gitlab.com/demensdeum/segagenesissamples
    https://www.exodusemulator.com/
    http://sun.hasenbraten.de/vasm/
    https://tomeko.net/online_tools/bin_to_32bit_hex.php?lang=en

    ソース

    https://namelessalgorithm.com/genesis/blog/genesis/https://plutiedev.com/vdp-commands
    https://huguesjohnson.com/programming/genesis/palettes/
    https://www.chibiakumas.com/68000/helloworld.php#LessonH5
    https://blog.bigevilcorporation.co.uk/2012/03/09/sega-megadrive-3-awaking-the-beast/

    Sega Genesis のアセンブリでの書き込み #1

    Motorola 68000 Assembly での古典的な Sega Genesis コンソール用のゲームの作成に特化した最初の記事。

    Sega 用の最も単純な無限ループを書いてみましょう。このためには、アセンブラ、逆アセンブラを備えたエミュレータ、お気に入りのテキスト エディタ、Sega rum の構造の基本的な理解が必要です。

    開発には、独自のアセンブラ/逆アセンブラ Gen68KryBaby を使用します。

    https://gitlab.com/demensdeum/gen68krybaby/

    このツールは Python 3 で開発されており、アセンブリの場合、拡張子 .asm または .gen68KryBabyDisasm を持つファイルが入力として提供され、出力は拡張子 .gen68KryBabyAsm.bin を持つファイルとなり、エミュレータまたは上で実行できます。本物のコンソール (コンソールが爆発する可能性があるので、注意して離れてください!)

    ROM の逆アセンブルもサポートされており、そのためには、.asm または .gen68KryBabyDisasm 拡張子を付けずに、ROM ファイルを入力として送信する必要があります。オペコードのサポートは、トピックに対する私の関心と貢献者の参加に応じて増減します。

    構造

    Sega rom ヘッダーは最初の 512 バイトを占めます。これには、ゲーム、名前、サポートされている周辺機器、チェックサム、およびその他のシステム フラグに関する情報が含まれています。タイトルがなければ、コンソールはラム酒を確認することさえせず、それが間違っていると判断し、「ここで何をくれるのですか?」と言うと思います。

    ヘッダーの後にサブルーチン/リセット サブルーチンが続き、ここで m68K プロセッサが作業を開始します。さて、それは小さな問題です –オペコード (オペレーション コード) を見つけます。つまり、何もせず (!)、メモリ内のアドレスのサブルーチンに切り替えます。グーグルで検索すると、何もしない NOP オペコードと、引数アドレスへの無条件ジャンプを実行する JSR オペコードを見つけることができます。つまり、何の気まぐれもせずに、要求した場所にキャリッジを移動するだけです。

    すべてをまとめる

    ROM のヘッダードナーはベータ版のゲームの 1 つであり、現在は 16 進データとして記録されています。

    
     00 ff 2b 52 00 00 02 00 00 00 49 90 00 00 49 90 00 00 49 90 00...и т.д. 

    Код программы со-но представляет из себя объявление сабрутины Reset/EntryPoint в 512 (0x200) байте, NOP, возврат каретки к 0x00000200, таким образом мы получим бесконечный цикл.

    Ассемблерный код сабрутины Reset/EntryPoint:

        NOP
        NOP
        NOP 
        NOP
        NOP
        JSR 0x00000200  
    

    ROM ヘッダーを含む完全な例:

    https://gitlab.com /demensdeum/segagenesisamples/-/blob/main/1InfiniteLoop/1infiniteloop.asm

    次に収集します:

    Запускаем ром 1infiniteloop.asm.gen68KryBabyAsm.bin в режиме дебаггера эмулятора Exodus/Gens, смотрим что m68K корректно считывает NOP, и бесконечно прыгает к EntryPoint в 0x200 на JSR

    Здесь должен быть Соник показывающий V, но он уехал на Вакен.

    Ссылки

    https://gitlab.com/demensdeum/gen68krybaby/

    https://gitlab.com/demensdeum/segagenesissamples

    https://www.exodusemulator.com/downloads/release-archive

    Источники

    ROM Hacking Demo – Genesis and SNES games in 480i

    http://68k.hax.com/

    https://www.chibiakumas.com/68000/genesis.php

    https://plutiedev.com/rom-header

    https://blog.bigevilcorporation.co.uk/2012/02/28/sega-megadrive-1-getting-started/

    https://opensource.apple.com/source/cctools/cctools-836/as/m68k-opcode.h.auto.html

    ポールに立っていた男や驚くべき創意工夫についての話をどれほど見逃していたか

    このノートでは、アプリケーションの開発、サポート、およびチーム開発環境におけるアーキテクチャ上の決定の重要性について書きます。

    セルフ手術用ナプキンのルシファー・ゴルゴンゾーラ教授。ルーブ・ゴールドバーグ

    若い頃、私はタクシー注文アプリケーションの開発に取り組んでいました。プログラムでは、乗車場所と降車場所を選択し、旅行費用と料金の種類を計算し、実際にタクシーを注文することができます。リリース前の最終段階でアプリケーションを受け取り、いくつかの修正を加えた後、アプリケーションは AppStore でリリースされました。すでにその段階で、チーム全体は、実装が非常に不十分で、デザインパターンが使用されておらず、システムのすべてのコンポーネントが緊密に接続されており、一般に、それを1つの大きな連続クラス(神オブジェクト)に書き込むことが可能であることを理解していました。何も変わらなかっただろうから、階級が責任の境界をどのように混同し、その総量においてデッドカップリングで互いに重なり合ったかということだ。その後、経営陣は正しいアーキテクチャを使用してアプリケーションを最初から作成することを決定し、それが実行され、最終製品は数十の B2B クライアントに実装されました。

    しかし、私は過去の建築に関する奇妙な出来事について説明します。その出来事から、私は時々夜中に冷や汗をかきながら目が覚めたり、昼間に突然思い出してヒステリックに笑い始めたりすることがあります。問題は、最初にポールにいる男を攻撃できなかったということで、これによりアプリケーションの大部分がダウンしましたが、まず最初に。

    その日は通常の営業日で、顧客の 1 人がアプリケーションのデザインを少し改良するというタスクを受けました –受け取り住所の選択画面の中央にあるアイコンを数ピクセル上に移動するのは簡単です。さて、専門的にタスクを 10 分で見積もったので、何も疑うことなくアイコンを 20 ピクセル上に上げ、タクシーの注文を確認することにしました。

    何?アプリに注文ボタンが表示されなくなりました?どうしてこんなことになったのでしょうか?

    自分の目を信じられませんでした。アイコンを 20 ピクセル上げた後、アプリケーションに注文を続けるボタンが表示されなくなりました。変更をロールバックすると、ボタンが再び表示されました。ここで何かが間違っていました。デバッガで 20 分を費やした後、重複するクラスへの呼び出しのスパゲッティを解くのに少しうんざりしましたが、*画像を移動するとアプリケーションのロジックが実際に変わる*ことがわかりました。

    すべては中央のアイコンに関するものでした –ポールの上にいる男性が、カードを動かすときに飛び上がってカメラの動きをアニメーション化しました。このアニメーションの後に、下部のボタンが消えました。どうやらプログラムは、20 ピクセル移動した男性がジャンプ中であると判断したため、内部ロジックに従って確認ボタンを非表示にしました。

    どうしてこのようなことが起こるのでしょうか?画面の *状態* は本当にステート マシンのパターンではなく、ポール上の男の位置の *表現* に依存するのでしょうか?

    その結果、マップが描画されるたびにアプリケーション *画面の中央を視覚的に突いて*、そこに何があるかを確認します。ポールの上に人がいる場合は、マップ シフト アニメーションが終了したことを意味し、表示する必要があります。ボタン。男性がそこにいない場合は、マップが移動し、ボタンが非表示になる必要があります。

    上記の例では、すべて問題ありません。第一に、これはゴールドバーグ マシン (難解なマシン) の例であり、第二に、開発者がチーム内の他の開発者と何らかの形で対話することに消極的であることの例です (何も考えずに理解してみてください)私)、第三に、SOLID、パターン(コード臭)、MVC 違反などに基づいてすべての問題をリストできます。

    このようなことをしないようにし、あらゆる方向に発展し、同僚の仕事を助けてください。皆さん明けましておめでとうございます)

    リンク

    https://ru.wikipedia.org/wiki/Goldberg_Machine

    https://ru.wikipedia.org/wiki/SOLID

    https://refactoring.guru/ru/refactoring/smells

    https://ru.wikipedia.org/wiki/Model -View-Controller

    https://refactoring.guru/ru/design-patterns/state

    グループを推測してください

    この投稿では、fasttext テキスト分類子の操作について説明します。

    ファーストテキスト –テキスト分類のための機械学習ライブラリ。彼女に曲のタイトルでメタルバンドを見分けるように教えてみましょう。これを行うために、データセットを使用した教師あり学習を使用します。

    グループ名を付けた曲のデータセットを作成しましょう:

    __label__metallica fuel
    __label__metallica escape
    __label__black_sabbath gypsy
    __label__black_sabbath snowblind
    __label__black_sabbath am i going insane
    __label__anthrax anthrax
    __label__anthrax i'm alive
    __label__anthrax antisocial
    [и т.д.]

    Формат обучающей выборки:

    Обучим fasttext и сохраним модель:

    model.save_model("model.bin")
    

    トレーニングされたモデルをロードし、曲の名前でグループを識別するように要求します。

    predictResult = model.predict("Bleed")
    print(predictResult)

    В результате мы получим список классов на которые похож данный пример, с указанием уровня похожести цифрой, в нашем случае похожесть названия песни Bleed на одну из групп датасета.
    Для того чтобы модель fasttext умела работать с датасетом выходящим за границы обучающей выборки, используют режим autotune с использованием файла валидации (файл тест). Во время автотюна fasttext подбирает оптимальные гиперпараметры модели, проводя валидацию результата на выборке из тест файла. Время автотюна ограничивается пользователем в самостоятельно, с помощью передачи аргумента autotuneDuration.
    Пример создания модели с использованием файла тест:

    x86_64 アセンブラー + C = One Love

    このノートでは、アセンブラから C 関数を呼び出すプロセスについて説明します。
    printf(“Hello World!\n”); を呼び出してみましょう。そして exit(0);

        message: db "Hello, world!", 10, 0
    
    section .text
        extern printf
        extern exit
        global main
    
    main:
        xor	rax, rax
        mov	rdi, message    
        call printf
        xor rdi, rdi
        call exit
    

    すべては見かけよりもはるかに単純です。.rodata セクションでは静的データを記述します。この場合、行「Hello, world!」、10 は改行文字であり、null にすることも忘れません。

    コード セクションでは、外部関数 printf、stdio の出口、stdlib ライブラリを宣言し、入力関数 main も宣言します。

        extern printf
        extern exit
        global main
    

    rax 関数から戻りレジスタに 0 を渡します。mov rax, 0; を使用できます。しかし、高速化するために、xor rax、rax が使用されます。次に、文字列へのポインタを最初の引数に渡します。

    Далее вызываем внешнюю функцию Си printf:

        xor	rax, rax
        mov	rdi, message    
        call printf
        xor rdi, rdi
        call exit
    

    同様に、最初の引数に 0 を渡して exit を呼び出します。

        call exit
    

    アメリカ人はこう言います。
    誰の言うことも聞かない
    人は。そのピラフは @ Alexander Pelevin

    を食べています。

    ソース

    https://www.devdungeon. com/content/how-mix-c-and-assembly
    https://nekosecurity.com/x86-64-assembly/part-3-nasm-anatomy-syscall-passing-argument
    https://www.cs.uaf.edu/2017/fall/cs301/reference/x86_64.html

    ソースコード

    https://gitlab.com/demensdeum/assembly-playground

    Hello World x86_64 アセンブラ

    この投稿では、IDE をセットアップし、Ubuntu Linux オペレーティング システム用の x86_64 アセンブラで最初の Hello World を作成するプロセスについて説明します。
    まず、SASM IDE、nasm アセンブラをインストールしましょう。

    Далее запустим SASM и напишем Hello World:

    
    section .text
    
    main:
        mov rbp, rsp      ; for correct debugging
        mov rax, 1        ; write(
        mov rdi, 1        ;   STDOUT_FILENO,
        mov rsi, msg      ;   "Hello, world!\n",
        mov rdx, msglen   ;   sizeof("Hello, world!\n")
        syscall           ; );
    
        mov rax, 60       ; exit(
        mov rdi, 0        ;   EXIT_SUCCESS
        syscall           ; );
    
    section .rodata
        msg: db "Hello, world!"
        msglen: equ $-msg
    

    ブログから取得した Hello World コード James Fisher は、SASM でのアセンブリとデバッグに適合しています。 SASM のドキュメントには、エントリ ポイントは main という名前の関数である必要があると記載されています。そうしないと、コードのデバッグとコンパイルが正しく行われません。
    このコードでは何をしたのでしょうか?システムコール – を呼び出しました。レジスタ内の正しい引数、データ セクション内の文字列へのポインタを使用して、Linux オペレーティング システム カーネルにアクセスします。

    虫眼鏡の下

    コードをさらに詳しく見てみましょう:

    global – директива ассемблера позволяющая задавать глобальные символы со строковыми именами. Хорошая аналогия – интерфейсы заголовочных файлов языков C/C++. В данном случае мы задаем символ main для функции входа.

    section – директива ассемблера позволяющая задавать секции (сегменты) кода. Директивы section или segment равнозначны. В секции .text помещается код программы.

    Обьявляем начало функции main. В ассемблере функции называются подпрограммами (subroutine)

    Первая машинная команда mov – помещает значение из аргумента 1 в аргумент 2. В данном случае мы переносим значение регистра rbp в rsp. Из комментария можно понять что эту строку добавил SASM для упрощения отладки. Видимо это личные дела между SASM и дебаггером gdb.

    Далее посмотрим на код до сегмента данных .rodata, два вызова syscall, первый выводит строку Hello World, второй обеспечивает выход из приложения с корректным кодом 0.

    Представим себе что регистры это переменные с именами rax, rdi, rsi, rdx, r10, r8, r9. По аналогии с высокоуровневыми языками, перевернем вертикальное представление ассемблера в горизонтальное, тогда вызов syscall будет выглядеть так:

    Тогда вызов печати текста:

    Вызов exit с корректным кодом 0:

    Рассмотрим аргументы подробнее, в заголовочном файле asm/unistd_64.h находим номер функции __NR_write – 1, далее в документации смотрим аргументы для write:
    ssize_t write(int fd, const void *buf, size_t count);

    Первый аргумент – файловый дескриптор, второй – буфер с данными, третий – счетчик байт для записи в дескриптор. Ищем номер файлового дескриптора для стандартного вывода, в мануале по stdout находим код 1. Далее дело за малым, передать указатель на буфер строки Hello World из секции данных .rodata – msg, счетчик байт – msglen, передать в регистры rax, rdi, rsi, rdx корректные аргументы и вызвать syscall.

    Обозначение константных строк и длины описывается в мануале nasm:

    ハッシュテーブル

    ハッシュ テーブルを使用すると、挿入、削除、検索操作の平均パフォーマンス O(1) の連想配列 (ディクショナリ) データ構造を実装できます。

    以下は、nodeJS でのハッシュ マップの最も単純な実装の例です。

    どのように機能するのでしょうか?手に注意してください:

    • ハッシュ マップ内には配列があります
    • 配列要素内には、リンクされたリストの最初のノードへのポインタがあります
    • メモリはポインタの配列(たとえば、65535 要素)に割り当てられます
    • ハッシュ関数を実装しており、辞書キーが入力であり、出力では何でもできますが、最終的には配列要素のインデックスを返します

    録音の仕組み:

    • 入り口にはキーペアがあります –値
    • ハッシュ関数はキーごとにインデックスを返します
    • インデックスを使用して配列からリンク リスト ノードを取得する
    • キーと一致するかどうかを確認します
    • 一致する場合は、値を置き換えます
    • 一致しない場合は、必要なキーを持つノードが見つかるまで、次のノードに進みます。
    • それでもノードが見つからない場合は、リンク リストの最後にノードを作成します

    キーによる検索の仕組み:

    • 入り口にはキーペアがあります –値
    • ハッシュ関数はキーごとにインデックスを返します
    • インデックスを使用して配列からリンク リスト ノードを取得する
    • キーと一致するかどうかを確認します
    • 一致する場合は値を返します
    • 一致しない場合は、必要なキーを持つノードが見つかるまで、次のノードに進みます。

    なぜ配列内にリンクされたリストが必要なのでしょうか?ハッシュ関数の計算時に衝突が発生する可能性があるため。この場合、いくつかの異なるキーと値のペアが配列内の同じインデックスに配置されます。この場合、必要なキーを見つけるためにリンク リストが走査されます。

    ソース

    https://ru.wikipedia.org/wiki/ハッシュ テーブル
    https://www.youtube.com/watch?v=wg8hZxMRwcw

    ソースコード

    https://gitlab.com/demensdeum/data Structures

    Android C++ でのリソースの操作

    ndk を介して Android のリソースを操作するには – C++ にはいくつかのオプションがあります:

    <オル>

  • AssetManager を使用して APK ファイルからリソースにアクセスする
  • インターネットからリソースをダウンロードし、アプリケーション ディレクトリに解凍し、標準の C++ メソッドを使用して使用します
  • 複合メソッド – AssetManager 経由で APK 内のリソースを含むアーカイブにアクセスし、それらをアプリケーション ディレクトリに解凍して、標準の C++ メソッドを使用して使用します
  • 次に、Flame Steel Engine ゲーム エンジンで使用される結合アクセス方法について説明します。
    SDL を使用すると、apk からリソースへのアクセスを簡素化できます。ライブラリは AssetManager への呼び出しをラップし、stdio (fopen、fread、fclose など) と同様のインターフェイスを提供します。

    SDL_RWops *io = SDL_RWFromFile("files.fschest", "r");
    
    

    APK からバッファにアーカイブをダウンロードした後、現在の作業ディレクトリをアプリケーション ディレクトリに変更する必要があります。追加のアクセス許可を取得せずにアプリケーションで使用できるようになります。これを行うには、SDL ラッパーを使用します。

    chdir(SDL_AndroidGetInternalStoragePath());
    
    

    次に、fopen、fwrite、fclose を使用して、バッファから現在の作業ディレクトリにアーカイブを書き込みます。アーカイブが C++ にアクセスできるディレクトリに配置されたら、解凍します。 ZIP アーカイブは、2 つのライブラリを組み合わせて使用​​して解凍できます。 minizip と zlib、最初のものはアーカイブの構造を操作でき、2 つ目はデータを解凍します。
    より制御しやすく移植を容易にするために、FSChest (Flame Steel Chest) と呼ばれる独自のゼロ圧縮アーカイブ形式を実装しました。この形式は、ファイルを含むディレクトリのアーカイブと解凍をサポートします。フォルダー階層はサポートされていないため、ファイルのみを操作できます。
    FSChest ライブラリのヘッダーを接続し、アーカイブを解凍します。

    #include "fschest.h" 
    FSCHEST_extractChestToDirectory(archivePath, SDL_AndroidGetInternalStoragePath()); 
    
    

    解凍後、C/C++ インターフェイスはアーカイブのファイルにアクセスできるようになります。したがって、エンジン内のファイルに関するすべての作業を書き直す必要はなく、起動段階でファイルの解凍を追加するだけで済みました。

    ソース

    https://developer.android.com/ndk/参照/グループ/アセット

    ソースコード

    https://gitlab.com/demensdeum/space-ジャガー アクション RPG
    https://gitlab.com/demensdeum/fschest

    スタックマシンとRPN

    単純なバイトコード インタープリターを実装する必要があるとします。このタスクを実装するにはどのようなアプローチを選択すればよいでしょうか?

    データ構造 スタックは、単純なバイトコード マシンを実装する機能を提供します。スタック マシンの機能と実装については、欧米および国内のインターネット上の多くの記事で説明されています。Java 仮想マシンはスタック マシンの一例であることだけを述べておきます。

    マシンの動作原理は単純で、データと操作コード (オペコード) を含むプログラムが入力に供給され、必要な操作はスタックによる操作を使用して実装されます。私のスタック マシンのバイトコード プログラムの例を見てみましょう。

    пMVkcatS olleHП
     
    

    出力では、文字列「Hello StackVM」を受け取ります。スタック マシンはプログラムを左から右に読み取り、オペコードがシンボル – に現れるとデータを 1 文字ずつスタックにロードします。スタックを使用してコマンドを実装します。

    nodejs でのスタック マシンの実装例:

    逆ポーランド記法 (RPN)

    スタック マシンは、逆ポーランド記法 (後置記法) を使用するため、計算機の実装にも簡単に使用できます。
    通常の中置記法の例:
    2*2+3*4

    RPN に変換します:
    22*34*+

    接尾辞レコードをカウントするには、スタック マシンを使用します。
    2–スタックの先頭(スタック: 2)へ
    2–スタックの先頭(スタック: 2,2)
    *–スタックの先頭を2回取得し、その結果を乗算してスタックの先頭(スタック: 4)に送信します
    3–スタックの先頭(スタック: 4、3)へ
    4–スタックの一番上(スタック: 4、3、4)へ
    *–スタックの先頭を 2 回取得し、その結果を乗算してスタックの先頭 (スタック: 4、12) に送信します。
    +–スタックの先頭を 2 回取得し、結果を追加して、スタックの先頭 (スタック: 16) に送信します。

    ご覧のとおり –操作 16 の結果はスタックに残ります。たとえば、スタック印刷オペコードを実装することで印刷できます。
    p22*34*+P

    P–スタック印刷開始オペコード、p –スタックの印刷を終了し、レンダリングのために最終行を送信するためのオペコード。
    算術演算を中置演算から後置演算に変換するには、「Sorting Yard」と呼ばれるエドガー ダイクストラのアルゴリズムが使用されます。実装の例は上記、または以下の Nodejs マシン スタック プロジェクトのリポジトリで見ることができます。

    ソース

    https:/ /tech.badoo.com/ru/article/579/interpretatory-bajt-kodov-svoimi-rukami/
    https://ru.wikipedia.org/wiki/Обратная_польская_запись

    ソースコード

    https://gitlab.com/demensdeum/stackvm/< /p>

    スケルタルアニメーション (パート 2 – ノード階層、補間)

    引き続き、Flame Steel Engine に実装されているスケルトン アニメーション アルゴリズムについて説明します。

    このアルゴリズムは私が実装したアルゴリズムの中で最も複雑であるため、開発プロセスに関するメモにエラーが表示される場合があります。このアルゴリズムに関する前回の記事では、ボーンの配列がモデル全体ではなく、メッシュごとに個別にシェーダーに転送されるという間違いを犯しました。

    ノード階層

    アルゴリズムが正しく動作するには、モデルにボーンとボーン間の接続 (グラフ) が含まれている必要があります。 2 つのアニメーションが同時に再生される状況を想像してみましょう。ジャンプして右手を上げる。ジャンプ アニメーションはモデルを Y 軸に沿って持ち上げる必要がありますが、腕を上げるアニメーションはこれを考慮して、モデルがジャンプするときにモデルと一緒に上昇する必要があります。そうしないと、腕は自動的に所定の位置に留まります。

    この場合のノードの接続について説明します –体には手が含まれています。アルゴリズムを作成するとき、骨グラフが読み取られ、すべてのアニメーションが正しい接続で考慮されます。モデルのメモリ内では、グラフはモデルのボーンの接続性を反映するためだけに、すべてのアニメーションとは別に保存されます。

    CPU での補間

    前回の記事では、スケルトン アニメーションのレンダリングの原理について説明しました。 「変換マトリックスは、レンダリング フレームごとに CPU からシェーダーに転送されます。」

    各レンダリング フレームは CPU で処理され、メッシュ ボーンごとに、エンジンは位置補間、回転、ズームを使用して最終的な変換行列を受け取ります。最終的なボーン マトリックスの補間中、すべてのアクティブなノード アニメーションに対してノード ツリーを通じてパスが作成され、最終的なマトリックスが親のマトリックスと乗算されて、レンダリングのために頂点シェーダーに送信されます。

    ベクトルは位置補間と拡大に使用され、クォータニオンは回転に使用されます。オイラー角とは異なり、補間 (SLERP) が非常に簡単で、変換行列として表すことも非常に簡単です。

    実装を簡素化する方法

    頂点シェーダーのデバッグを容易にするために、FSGLOGLNEWAGERENDERER_CPU_BASED_VERTEX_MODS_ENABLED マクロを使用して、CPU 上に頂点シェーダーのシミュレーションを追加しました。ビデオ カード メーカーである NVIDIA は、シェーダー コードをデバッグするためのユーティリティ Nsight を提供しています。これにより、複雑な頂点/ピクセル シェーダー アルゴリズムの開発も簡素化できる可能性がありますが、CPU でのシミュレーションだけでは十分な機能をテストできませんでした。

    次の記事では、複数のアニメーションの混合について説明し、残りのギャップを埋める予定です。

    ソース

    https://www.youtube.com/watch?v= f3Cr8Yx3GGA

    C++ での JavaScript スクリプトのサポートの追加

    この投稿では、Tiny-JS ライブラリを使用して C++ アプリケーションに JavaScript スクリプトのサポートを追加する方法について説明します。

    Tiny-JS は、C++ に埋め込むためのライブラリであり、バインディング (スクリプトから C++ コードを呼び出す機能) のサポートとともに JavaScript コードの実行を提供します。

    最初は、人気のあるライブラリである ChaiScript、Duktape、または connect Lua を使用したいと考えていましたが、依存関係や、さまざまなプラットフォームへの移植が困難になる可能性があるため、シンプルで最小限だが強力な MIT JS ライブラリを見つけることにしました。 JS はこれらの基準を満たしています。このライブラリの唯一の欠点は、作成者によるサポート/開発がないことですが、コードは非常にシンプルなので、必要に応じてサポートを引き継ぐことができます。

    リポジトリから Tiny-JS をダウンロードします。
    https://github.com/gfwilliams/tiny-js

    次に、スクリプトを担当するコードに Tiny-JS ヘッダーを追加します。

    #include "tiny-js/TinyJS.h"
    #include "tiny-js/TinyJS_Functions.h"
    
    

    TinyJS .cpp ファイルをビルド ステージに追加すると、スクリプトの読み込みと実行の作成を開始できます。

    ライブラリの使用例はリポジトリで入手できます。
    https://github.com/gfwilliams/tiny-js/blob/master/Script.cpp
    https://github.com/gfwilliams/tiny-js/blob/wiki/CodeExamples.md

    ハンドラー クラスの実装例は、SpaceJaguar プロジェクトにあります。
    https://gitlab.com/demensdeum/space-jaguar-action-rpg/-/blob/master/project/src/Controllers/SpaceJaguarScriptController/SpaceJaguarScriptController.h
    https://gitlab.com/demensdeum/space-jaguar-action-rpg/-/blob/master/project/src/Controllers/SpaceJaguarScriptController/SpaceJaguarScriptController.cpp

    アプリケーションに追加されたゲーム スクリプトの例:
    https://gitlab.com/demensdeum/space-jaguar-action-rpg/-/blob/master/project/resources/com.demensdeum.spacejaguaractionrpg.scripts.sceneController.js

    ソース

    https://github.com/gfwilliams/tiny-js
    https://github.com/dbohdan/embedded-scripting-languages
    https://github.com/AlexKotik/embeddable-scripting-languages

    Linux 上で iOS 用の C++ SDL アプリケーションを構築する

    この投稿では、Linux 上で iOS 用の C++ SDL アプリケーションを構築し、有料の Apple Developer サブスクリプションなしで ipa アーカイブに署名し、ジェイルブレイクなしで macOS を使用してクリーンなデバイス (iPad) にアプリケーションをインストールする手順について説明します。< /p>

    まず、Linux 用のビルド ツールチェーンをインストールしましょう。
    https://github.com/tpoechtrager/cctools-port

    ツールチェーンをリポジトリからダウンロードし、Godot Engine Web サイトの指示に従ってインストールを完了する必要があります。
    https://docs.godotengine.org/ru/latest/development/compiling/cross-compiling_for_ios_on_linux.html

    現時点では、Xcode dmg をダウンロードし、そこから SDK をコピーして cctools-port をビルドする必要があります。この段階は macOS で簡単に完了でき、インストールされた Xcode から必要な SDK ファイルをコピーするだけです。アセンブリが成功すると、ターミナルにはクロスコンパイラー ツールチェーンへのパスが含まれます。

    次に、iOS 用の SDL アプリケーションの構築を開始できます。 cmake を開いて、C++ コードをビルドするために必要な変更を追加しましょう。

    SET(CMAKE_SYSTEM_NAME Darwin)
    SET(CMAKE_C_COMPILER arm-apple-darwin11-clang)
    SET(CMAKE_CXX_COMPILER arm-apple-darwin11-clang++)
    SET(CMAKE_LINKER arm-apple-darwin11-ld)
    
    

    これで、cmake と make を使用してコンパイルできるようになりますが、$PATH をクロスコンパイラー ツールチェーンに追加することを忘れないでください。

    
    PATH=$PATH:~/Sources/cctools-port/usage_examples/ios_toolchain/target/bin
    
    

    フレームワークや SDL と正しくリンクするために、それらを cmake に記述します。たとえば、ゲーム Space Jaguar の依存関係です。

    
    target_link_libraries(
    ${FSEGT_PROJECT_NAME}
    ${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libclang_rt.ios.a
    ${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libSDL2.a
    ${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libSDL2_mixer.a
    ${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/libSDL2_image.a
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreServices.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/ImageIO.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/Metal.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/AVFoundation.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/GameController.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreMotion.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreGraphics.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/AudioToolbox.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/CoreAudio.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/QuartzCore.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/OpenGLES.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/UIKit.framework"
    "${FLAME_STEEL_PROJECT_ROOT_DIRECTORY}/scripts/buildScripts/ios/resources/libs/Foundation.framework"
    )
    
    

    私の場合、SDL、SDL_Image、SDL_mixer ライブラリは、静的リンクのために事前に macOS 上の Xcode でコンパイルされています。 Xcode からコピーされたフレームワーク。 libclang_rt.ios.a ライブラリも追加されました。これには、iOS 固有のランタイム呼び出し (isOSVersionAtLeast など) が含まれます。 Android と同様に、OpenGL ES を操作して、モバイル バージョンでサポートされていない機能を無効にするためのマクロが含まれています。

    ビルドの問題をすべて解決したら、arm 用にアセンブルされたバイナリを取得する必要があります。次に、ジェイルブレイクなしでデバイス上でアセンブルされたバイナリを実行することを検討してみましょう。

    macOS では、開発者プログラムの料金を支払わずに、Xcode をインストールし、Apple ポータルに登録します。 Xcode でアカウントを追加します ->設定 ->アカウント、空のアプリケーションを作成し、実際のデバイス上に構築します。組み立て中に、デバイスは無料の開発者アカウントに追加されます。アセンブリと起動後、アーカイブを構築する必要があります。これを行うには、[汎用 iOS デバイスと製品] を選択します。アーカイブ。アーカイブが構築されたら、そこからembedded.mobileprovisionファイルとPkgInfoファイルを抽出します。デバイスへのビルド ログから、正しい署名キーを含むコードデザイン行、拡張子 app.xcent を持つ資格ファイルへのパスを見つけてコピーします。

    アーカイブから .app フォルダーをコピーし、アーカイブ内のバイナリを Linux のクロスコンパイラーでコンパイルされたバイナリ (SpaceJaguar.app/SpaceJaguar など) に置き換えてから、必要なリソースを .app に追加し、アーカイブからの .app 内の PkgInfo およびembedded.mobileprovision ファイルの整合性を確認するには、必要に応じて再度コピーします。 codesign コマンド – を使用して .app に再署名します。 codedesign には、署名用の入力キー、資格ファイルへのパス (.plist 拡張子で名前変更可能) が必要です。

    再署名した後、Payload フォルダーを作成し、.app 拡張子の付いたフォルダーをそこに移動し、ルートに Payload を含む zip アーカイブを作成し、.ipa 拡張子を付けてアーカイブの名前を変更します。その後、Xcode でデバイスのリストを開き、新しい ipa をデバイスのアプリケーションのリストにドラッグ アンド ドロップします。この方法では、Apple Configurator 2 によるインストールは機能しません。再署名が正しく行われると、新しいバイナリを含むアプリケーションが 7 日間の証明書とともに iOS デバイス (iPad など) にインストールされます。テスト期間にはこれで十分です。

    ソース

    https://github.com/tpoechtrager/cctools-port
    https://docs.godotengine.org/ru/latest/development/compiling/cross-compiling_for_ios_on_linux.html
    https://jonnyzzz.com/blog/2018/06/13/link-error-3/
    https://stackoverflow.com/questions/6896029/re-sign-ipa-iphone
    https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html

    Windows 10で遅いHDDを修正する

    このメモは、諦めていないすべてのハード ドライブ ユーザーに捧げます。


    Original (Mae Mu)

    デュアル HDD (Windows 10) と SSD (Ubuntu) を搭載した HP Pavilion ラップトップを 1 年半使用した後、アプリケーションのロード時間が非常に長いこと、インターフェイスが全般的に応答しなくなっていること、最も単純な操作でフリーズすることに気づき始めました。 Windows 10 では、問題は最小限に抑えられ、ラップトップを再び使用できるようになりました。次に、問題を解決するために私が行った手順について説明します。

    診断

    調査を開始するには、あらゆる種類のデマを排除する必要があります。まず、ハードドライブ障害の主な原因を特定しましょう。ハードドライブを使用するときに問題が発生する可能性がありますか?問題は、電子機器の物理レベルおよび論理的なソフトウェア データ レベルで発生する可能性があります。
    電子機器の問題には、コンピュータやラップトップの電源が機能しない、ラップトップのバッテリの問題などが含まれます。ハードドライブのコンポーネントの磨耗、ドライブの内部コンポーネントの回路やチップの問題、ファームウェアのエラー、ドライブの衝撃や落下による影響、または動作に影響を与える他のデバイスの同様の問題。
    ハードドライブの重大な摩耗は、ドライブのそれ以上の操作が不可能になるほどの数の不良セクタ (不良ブロック) が発生した瞬間であると考えられます。これらのブロックはハード ドライブのファームウェアによってブロックされ、データは自動的に他のセクターに転送され、特定の重大な瞬間までディスクの動作に影響を与えることはありません。
    プログラム ロジックの問題には、アプリケーションの誤った操作によるファイル システムのエラー、ユーザーのアクション (熱いときにデバイスの電源を切る、アプリケーションを正しく停止せずに録画プロセスを完了する)、ドライバー、オペレーティング システム サービスのエラーが含まれます。
    特殊な電子機器診断ツールがなければ、ソフトウェア レベルの正確性を確認することしかできません。その過程で電子機器の問題が発見される可能性がありますが、通常はブロック修復方法 (コンポーネント/チップの交換) によって解決されます。次に、診断ユーティリティを使用したソフトウェアの診断方法を検討します。すべてのユーティリティはシステム上で最優先で起動する必要があることに注意してください。他のアプリケーションがパフォーマンス測定を妨害し、ディスクの読み取り/書き込みをブロックする可能性があり、その結果、不正確な診断結果が得られる可能性があります。

    スマート

    S.M.A.R.T. ストレージ デバイス ステータス監視システム – HDD、SDD、eMMC など。デバイスの摩耗を評価し、不良ブロックの数を表示し、データに基づいてさらなるアクションを実行できます。 SMART は、ディスクを操作するためのさまざまなアプリケーションで表示できます。私は、メーカーが提供するユーティリティを使用することを好みます。 Seagate ハード ドライブの場合は、SeaTools ユーティリティを使用しました。ステータスは GOOD と表示されました。つまり、ディスク ファームウェアはすべてが正常であると判断しています。

    メーカーのユーティリティ

    ディスク製造元のユーティリティは、その動作を検証するためのテストを提供しています。 SeaTools にはいくつかの種類のテストがあり、それらをすべて使用して問題の場所を特定できます。簡単なテストでは問題が見つからない可能性があるため、長いテストを推奨します。私の場合、Long Test のみでエラーが見つかりました。

    スローライド

    読み取りの正確性を確認し、遅いブロックやデッドブロックを見つけるために、アプリケーション を作成しました。スローライドは、非常に単純な原理で動作します。指定されたユーザー設定を使用してブロック デバイス記述子を開き、デバイス全体のデータを読み取り、時間測定、低速ブロックの出力を行います。この場合、プログラムは最初のエラーで停止します。単純な方法ではディスク データを読み取ることができないため、より深刻なデータ削除ユーティリティに進む必要があります。
    私の場合、ディスク全体の読み取りは正しく実行されましたが、速度はわずかに低下しました。ディスクの一部の領域では、1 秒間に 90MB/秒 (5400rpm)。このことから、ソフトウェアの問題に対処していると結論付けることができます。

    音響分析

    この方法はソフトウェア診断方法には適用されませんが、問題を解決することは非常に重要です。たとえば、電源が部分的に動作している場合、ハードドライブがフリーズ/フリーズし、カチッという音が大きくなることがあります
    。私の場合、Windows 10 でディスクを操作しているときに、HDD 所有者なら誰でもよく知っている、 オペレーティング システムで何かを実行しようとすると、ディスク ヘッドが往復する大きなパチパチという音が聞こえますが、その音はほぼ一定であり、断片化が多すぎるのではないかと思いました。ディスク、バックグラウンド サービスによるディスクの過負荷。

    修正中

    ソフトウェア診断中に電子機器の問題は検出されず、ディスク全体のブロックごとの読み取りは正常に完了しましたが、SeaTools はロング テスト中にエラーを示しました。

    メーカーのユーティリティ

    診断に加えて、ディスク製造元のソフトウェアはエラー修正手順を提供します。 SeaTools では、[すべて修復] ボタンがこれを担当します。データ損失の可能性への同意を確認した後、修正プロセスが開始されます。この修正は私の場合に役に立ちましたか?いいえ、ディスクは引き続き大音量でゆっくりと動作しましたが、ロング テストではエラーが表示されなくなりました。

    CHKDSK

    CHKSDK は、Windows ファイル システムのソフトウェア エラーをトラブルシューティングするための Microsoft ユーティリティです。時間の経過とともに、このようなエラーはディスク上に蓄積され、データの読み取り/書き込みがまったくできなくなるなど、作業に大きな支障をきたす可能性があります。このユーティリティの使用手順は Microsoft Web サイトで見つけることができますが、エラーを修正するには可能な限りすべてのフラグを使用することをお勧めします (この記事の執筆時点では、これは /r /b /f です)。 Windows ターミナル (cmd) を介して管理者権限でスキャンを実行する必要があります。システム パーティションの場合、スキャンはシステム起動時に実行され、非常に長い時間がかかる場合があります。私の場合は 12 時間かかりました。
    この修正は私の場合に役に立ちましたか?いいえ。

    ディスクのデフラグ

    ディスク上のデータはブロック単位で処理され、大きなファイルは通常、複数のブロック/フラグメントに書き込まれます。時間が経つにつれて、削除されたファイルの多くは、近くにない空のブロックを作成します。そのため、ファイルを書き込むときに、これらの空いたブロックが埋められ、ディスク ヘッドが物理的に長距離を移動する必要があります。この問題は断片化と呼ばれ、ハード ドライブ ユーザーのみが経験します。数回の修正時点で、ハードドライブの断片化は 41% でした。視覚的には次のように見えました。

    つまり、すべてが悪いということです。デフラッガー ユーティリティまたは組み込みのデフラグ ツールを使用して、断片化を確認し、デフラグすることができます。 「ドライブの最適化」サービスを有効にすることもできます。 Windows 10 では、コントロール パネルでデフラグのスケジュールを設定します。 デフラグが必要なのは HDD ドライブのみです。SSD ドライブに対してデフラグを有効にすることはお勧めできません。これにより、ディスクの摩耗が加速します。このため、バックグラウンドでのデフラグはデフォルトで無効になっているようです。

    代替のデフラグ オプションも知られています –データを別のディスクに転送し、ディスクをフォーマットし、データをコピーして戻します。この場合、システム動作に適した正しい論理構造を維持しながら、データは完全に空のセクタに書き込まれます。このオプションには、通常のコピー中に移動できない可能性がある重要なメタデータをリセットする際に問題が伴います。

    サービスを無効にする

    Mark Russinovich のユーティリティの使用 プロセス モニター IO 書き込み/読み取り列を有効にするだけで、ハード ドライブにロードするプロセスをその作業とともに追跡できます。このコラムを調査した後、コントロール パネルのサービス パネルから、新しい名前 SysMain の Superfetch プログラム用のよく知られたバックグラウンド アクセラレーション サービスである Xbox Game Bar サービスを無効にしました。 Superfetch は、ユーザーが使用するアプリケーションを常に分析し、RAM にキャッシュすることでアプリケーションの起動を高速化する必要があります。これにより、ディスク全体がバックグラウンドで読み込まれ、動作できなくなりました。

    ディスクのクリーニング

    また、古いアプリケーションや不要なファイルも削除しました。これにより、適切な断片化のためにセクターが解放され、オペレーティング システムの操作が簡素化され、無駄で重いサービスやプログラムの数が減りました。

    合計

    何が最も役に立ちましたか?ディスクをデフラグするとパフォーマンスに顕著な違いが見られ、Xbox サービスと Superfetch サービスを無効にすることで自然発生的なフリーズが解消されました。 SSD を使用していれば、これらの問題は発生しなかったのでしょうか?断片化による動作の遅さの問題はまったくありません。サービスの問題はいずれにしても修正する必要があり、ソフトウェア エラーはドライブの種類に依存しません。近い将来、SSD への完全な移行を計画していますが、今のところは「パンケーキ万歳、パンケーキ永遠に!」

    リンク

    http://www.outsidethebox.ms/why-windows-8-defragments-your-ssd-and-how-you-can-avoid-this/
    https://channel9.msdn.com/Shows/The-Defrag-Show
    https://www.seagate.com/ru/ru/support/downloads/seatools/
    https://www.ccleaner.com/defraggler/download
    https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/chkdsk
    https://gitlab.com/demensdeum/slowride/

    C++ FCGI でのバックエンド サーバーの作成

    3D エディタ Cube Art Project のサーバー部分をどのように書いたかについての簡単なメモです。サーバーは Web バージョン ユーザーの作業を保存して表示し、保存ボタンを使用して短い URL を提供する必要があります。当初はバックエンドにSwift/PHP/Ruby/JSなどの最新言語を使いたかったのですが、VPSの特性を見てサーバーをC/C++で書くことにしました。
    まず、サーバーに libfcgi をインストールし、Web サーバー用の fcgi サポート モジュール (Ubuntu と Apache など) をインストールする必要があります。

    sudo apt install libfcgi libapache2-mod-fcgid
    
    

    次に、config でモジュールを構成します。

    FcgidMaxProcessesPerClass –クラスあたりの最大プロセス数ですが、大きな負荷が予想されないため1プロセスに設定しました。
    AddHandler fcgid-script .fcgi – fcgi モジュールを開始するファイル拡張子。
    CGI アプリケーションの起動元となるフォルダーを構成に追加します。

    次に、fcgi サポートを備えた C/C++ でアプリケーションを作成し、アセンブルして、/var/www/html/cgi-bin フォルダーにコピーします。
    コードとビルド スクリプトの例:
    https://gitlab.com/demensdeum/cube-art-project-server/-/blob/master/src/cubeArtProjectServer.cpp
    https://gitlab.com/demensdeum/cube-art-project-server/-/blob/master/src/build.sh
    この後、Web サーバーを再起動する必要があります。

    systemctl restart apache2
    
    

    次に、chmod 経由で cgi-bin フォルダーを実行するために必要な権限を入力します。
    この後、CGI プログラムは、Cube Art Project サーバーの例のリンクを使用してブラウザ経由で動作するはずです。
    http://192.243.103.70/cgi-bin/cubeArtProject/cubeArtProjectServer.fcgi
    何かが動作しない場合は、Web サーバーのログを確認するか、デバッガーを使用して実行中のプロセスに接続します。デバッグ プロセスは、通常のクライアント アプリケーションのデバッグ プロセスと異なっていてはなりません。

    ソース

    https://habr.com/ru/post/154187/http://chriswu.me/blog/writing-hello-world-in-fcgi-with-c-plus-plus/

    ソースコード

    https://gitlab.com/demensdeum/cube-art -プロジェクトサーバー

    C++ SDL アプリケーションの Android への移植

    この投稿では、3D エディタのプロトタイプを移植した私の経験について説明します キューブ アート プロジェクト
    まず、赤い 3D キューブ カーソルを持つエディタがエミュレータで実行されている結果を見てみましょう。

    組み立てを成功させるには、次のことを行う必要がありました:

    <オル>

  • 最新の Android SDK と NDK をインストールします(NDK バージョンは新しいほど良いです)
  • SDL2 ソース コードをダウンロードし、そこからテンプレートを取得して Android アプリケーションを構築します。
  • SDL イメージ、SDL ミキサーをアセンブリに追加します。
  • ゲーム エンジンとツールキットのライブラリ、それらの依存関係 (GLM、モダン C++ 用の JSON) を追加します
  • アセンブリ ファイルを Gradle に適合させる。
  • Android との互換性のために C++ コードを調整し、影響を受けるプラットフォーム依存コンポーネント(OpenGL ES、グラフィックス コンテキストの初期化)を変更する
  • エミュレータ上でプロジェクトをビルドしてテストします。
  • プロジェクト テンプレート

    ソース SDL、SDL イメージ、SDL ミキサーの読み込み:
    https://www.libsdl.org/download-2.0.php
    docs フォルダーには、Android プロジェクト テンプレートを使用するための詳細な手順が含まれています。 android-project ディレクトリを別のフォルダーにコピーするか、シンボリックリンクを作成するか、SDL フォルダーを android-project/app/jni にコピーします。
    avd フラグを正しい識別子に置き換えて、Sdk:

    ディレクトリから Android エミュレータを起動します。

    cd ~/Android/Sdk/emulator
    ./emulator -avd Pixel_2_API_24
    
    

    スクリプトでパスを指定し、プロジェクトをアセンブルします。

    rm -rf app/build || true
    export ANDROID_HOME=/home/demensdeum/Android/Sdk/
    export ANDROID_NDK_HOME=/home/demensdeum/Android/android-ndk-r21-beta2/
    ./gradlew clean build
    ./gradlew installDebug
    
    

    ファイルからの C コードを含む SDL プロジェクト テンプレートをアセンブルする必要があります

    android-sdl-test-app/cube-art-project-android/app/jni/src/YourSourceHere.c
    
    

    依存関係

    SDL_image、SDL_mixer のソース コードをアーカイブからダウンロードします。
    https://www.libsdl.org/projects/SDL_image/
    https://www.libsdl.org/projects/SDL_mixer/

    プロジェクトの依存関係(共有ライブラリなど)を読み込みます。
    https://gitlab.com/demensdeum/FlameSteelCore/
    https://gitlab.com/demensdeum/FlameSteelCommonTraits
    https://gitlab.com/demensdeum/FlameSteelBattleHorn
    https://gitlab.com/demensdeum/FlameSteelEngineGameToolkit/
    https://gitlab.com/demensdeum/FlameSteelEngineGameToolkitFSGL
    https://gitlab.com/demensdeum/FSGL
    https://gitlab.com/demensdeum/cube-art-project

    これらすべてを app/jni にアップロードし、各「モジュール」を別のフォルダー (例: app/jni/FSGL) にアップロードします。次に、Application.mk および Android.mk ファイルの動作するジェネレーターを見つけるオプションがあります。私はそれらを見つけられませんでしたが、おそらく CMake に基づく簡単な解決策があるでしょう。リンクをクリックして、Android NDK のアセンブリ ファイル形式について学び始めてください。
    https://developer.android.com/ndk/guides/application_mk
    https://developer.android.com/ndk/guides/android_mk

    NDK のさまざまな APP_STL 実装についてもお読みください。
    https://developer.android.com/ndk/guides/cpp-support.html

    慣れた後、「モジュール」ごとに Android.mk ファイルを作成し、続いて共有ライブラリ Cube-Art-Project のサンプル アセンブリ ファイルを作成します。

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    APP_STL := c++_static
    APP_CPPFLAGS := -fexceptions
    LOCAL_MODULE := CubeArtProject
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/src $(LOCAL_PATH)/../include $(LOCAL_PATH)/../include/FlameSteelCommonTraits/src/FlameSteelCommonTraits
    LOCAL_EXPORT_C_INCLUDES = $(LOCAL_PATH)/src/
    
    define walk
    $(wildcard $(1)) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e)))
    endef
    
    ALLFILES = $(call walk, $(LOCAL_PATH)/src)
    FILE_LIST := $(filter %.cpp, $(ALLFILES))
    $(info CubeArtProject source code files list)
    $(info $(FILE_LIST))
    LOCAL_SRC_FILES := $(FILE_LIST:$(LOCAL_PATH)/%=%)
    
    LOCAL_SHARED_LIBRARIES += FlameSteelCore
    LOCAL_SHARED_LIBRARIES += FlameSteelBattleHorn
    LOCAL_SHARED_LIBRARIES += FlameSteelCommonTraits
    LOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkit
    LOCAL_SHARED_LIBRARIES += FlameSteelEngineGameToolkitFSGL
    LOCAL_SHARED_LIBRARIES += FSGL
    LOCAL_SHARED_LIBRARIES += SDL2
    LOCAL_SHARED_LIBRARIES += SDL2_image
    
    LOCAL_LDFLAGS := -static-libstdc++
    include $(BUILD_SHARED_LIBRARY)
    
    

    経験豊富な CMake ユーザーなら最初の行からこの構成を理解できるでしょう。形式は非常に似ており、Android.mk には GLOB_RECURSIVE がないため、walk 関数を使用してソース ファイルを再帰的に検索する必要があります。

    C コードではなく C++ をビルドするために Application.mk、Android.mk を変更します。

    APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
    APP_PLATFORM=android-16
    APP_STL := c++_static
    APP_CPPFLAGS := -fexceptions
    
    

    YourSourceHere.c -> YourSourceHere.cpp の名前を変更し、エントリを grep し、アセンブリ内のパスを変更します。

    app/jni/src/Android.mk:LOCAL_SRC_FILES := YourSourceHere.cpp
    
    

    次に、プロジェクトをビルドしてみます。ヘッダーがないことに関するコンパイラーからのエラーが表示された場合は、Android.mk 内のパスが正しいことを確認してください。 「未定義の参照」などのリンカーからのエラーがある場合は、アセンブリ内のソース コード ファイルが正しく指定されていることを確認してください。リストは、Android.mk ファイルで $(info $(FILE_LIST)) を指定することで追跡できます。 LOCAL_SHARED_LIBRARIES キーのモジュールを使用し、FSGL などの LD を介して正しいリンクを使用する、二重リンク メカニズムを忘れないでください。

    LOCAL_LDLIBS := -lEGL -lGLESv2
    
    

    適応と発売

    いくつかの点を変更する必要がありました。たとえば、iOS および Android のビルドから GLEW を削除し、一部の OpenGL 呼び出しの名前を変更し、EOS ポストフィックス (glGenVertexArrays -> glGenVertexArraysOES) を追加し、欠落している最新のデバッグ関数用のマクロを追加しました。おまけに、マクロを示す GLES2 ヘッダーが暗黙的に組み込まれていることです。 GL_GLEXT_PROTOTYPES 1:

    #define GL_GLEXT_PROTOTYPES 1
    #include "SDL_opengles2.h"
    
    

    また、最初の起動時に黒い画面が表示され、「E/libEGL: validate_display:255 error 3008 (EGL_BAD_DISPLAY)」のようなエラーが表示され、SDL ウィンドウの初期化、OpenGL プロファイルが変更され、すべてが機能しました。

    SDL_DisplayMode mode;
    SDL_GetDisplayMode(0,0,&mode);
    int width = mode.w;
    int height = mode.h;
    
    window = SDL_CreateWindow(
                title,
                0,
                0,
                width,
                height,
                SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN | SDL_WINDOW_RESIZABLE
            );
    
    SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES );
    

    エミュレータでは、アプリケーションはデフォルトで SDL アイコンと「Game」という名前でインストールされます。

    CMake に基づいてアセンブリ ファイルを自動的に生成するか、すべてのプラットフォームのアセンブリを Gradle に移行する可能性を検討する必要があります。ただし、CMake は現在進行中の C++ 開発における事実上の選択肢であり続けています。

    ソースコード

    https://gitlab.com/demensdeum/android- sdl-テスト-アプリ
    https://gitlab.com/demensdeum/android-sdl-test-app/tree/master/cube-art-project-android

    ソース

    https://developer.android.com/ ndk/guides/cpp-support.html
    https://developer.android.com/ndk/guides/application_mk
    https://developer.android.com/ndk/guides/android_mk
    https://lazyfoo.net/tutorials/SDL/52_hello_mobile/android_windows/index.php
    https://medium.com/androiddevelopers/getting-started-with-c-and-android-native-activities-2213b402ffff

    逆さまの世界

    新しいプロジェクトを開発するために、Cube Art Project はテスト駆動開発手法を採用しました。このアプローチでは、アプリケーションの特定の機能のテストが最初に実装され、次に特定の機能が実装されます。このアプローチの大きな利点は、機能の開発が開始される前に、実装の詳細にできるだけ関与しない最終インターフェイスの実装であると考えています。このアプローチでは、インターフェイスが特定の実装のコントラクトである場合、テストによってさらなる実装が決定され、コントラクト プログラミングの利点がすべて追加されます。
    キューブアートプロジェクト –ユーザーが立方体からフィギュアを構築する 3D エディターは、少し前までは非常に人気がありました。これはグラフィカルなアプリケーションなので、スクリーンショット検証を含むテストを追加することにしました。
    スクリーンショットを検証するには、OpenGL コンテキストからスクリーンショットを取得する必要があります。これは glReadPixels 関数を使用して行われます。関数の引数の説明は簡単です –開始位置、幅、高さ、形式 (RGB/RGBA/など)、出力バッファへのポインタ。SDL を使用したことがある人、または C でデータ バッファを使用した経験がある人は、必要な引数を単純に置き換えることができます。ただし、glReadPixels 出力バッファの興味深い機能について説明する必要があると思います。SDL_Surface ではすべての基本操作が上から下に行われるのに対し、ピクセルは下から上に保存されます。
    つまり、png ファイルから参照スクリーンショットをロードしたのですが、2 つのバッファーの 1 つが上下逆だったため、2 つのバッファーを直接比較できませんでした。
    OpenGL から出力バッファーを反転するには、Y 座標のスクリーンショットの高さを減算して出力バッファーを埋める必要がありますが、埋めるときに 1 を減算しないとバッファーの制限を超える可能性があることを考慮する価値があります。メモリ破損の原因となります。
    私は常に C のようなポインタによる直接メモリ アクセスではなく、「インターフェイスによるプログラミング」という OOP パラダイムを使用しようとしているため、バッファの外にデータを書き込もうとすると、メソッドの境界検証のおかげでオブジェクトがこれを通知してくれました。 。
    トップダウン スタイルでスクリーンショットを取得するメソッドの最終コード:

        auto width = params->width;
        auto height = params->height;
    
        auto colorComponentsCount = 3;
        GLubyte *bytes = (GLubyte *)malloc(colorComponentsCount * width * height);
        glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, bytes);
    
        auto screenshot = make_shared(width, height);
    
        for (auto y = 0; y < height; y++) {
            for (auto x = 0; x < width; x++) {
                auto byteX = x * colorComponentsCount;
                auto byteIndex = byteX + (y * (width * colorComponentsCount));
                auto redColorByte = bytes[byteIndex];
                auto greenColorByte = bytes[byteIndex + 1];
                auto blueColorByte = bytes[byteIndex + 2];
                auto color = make_shared(redColorByte, greenColorByte, blueColorByte, 255);
                screenshot->setColorAtXY(color, x, height - y - 1);
            }
        }
    
        free(bytes);
    
    

    ソース

    https://community.khronos.org/ t/glreadpixels-fliped-image/26561
    https://stackoverflow.com/questions/8346115/why-are-bmps-stored-upside-down

    ソースコード

    https://gitlab.com/demensdeum/cube-アートプロジェクトブートストラップ

    最長の共通部分文字列

    この投稿では、最大共通部分文字列問題を解決するためのアルゴリズムについて説明します。暗号化されたバイナリ データを復号化しようとしているとします。まず、最大の部分文字列を検索して共通のパターンを見つけてみましょう。
    入力文字列の例:
    adasDATAHEADER??jpjjwerthhkjbcvkDATAHEADER??kkasdf
    2 回繰り返される文字列を探しています。
    データヘッダー??

    プレフィックス

    まず、2 つの文字列の接頭辞を比較するメソッドを作成しましょう。左側の接頭辞の文字が右側の接頭辞の文字と等しい結果の文字列を返します。
    たとえば、行の場合は次のようになります。

            val lhs = "asdfWUKI"
            val rhs = "asdfIKUW"
    
    

    結果文字列 –空自
    Kotlin の例:

    fun longestPrefix(lhs: String, rhs: String): String {
            val maximalLength = min(lhs.length-1, rhs.length -1)
            for (i in 0..maximalLength) {
                val xChar = lhs.take(i)
                val yChar = rhs.take(i)
                    if (xChar != yChar) {
                        return lhs.substring(0, i-1)
                    }
            }
            return lhs.substring(0,maximalLength)
    }
    
    

    ブルートフォース

    物事がうまくいかないときは、強引な手段に頼るべきです。 longestPrefix メソッドを使用して、文字列を 2 つのループで処理します。最初のループでは i から最後までの文字列を取得し、2 番目のループでは i + 1 から最後までの文字列を渡して、最大のプレフィックスを検索します。このアルゴリズムの時間計算量は約 O(n^2) ~ O(n*^3) です。
    Kotlin の例:

    fun searchLongestRepeatedSubstring(searchString: String): String {
            var longestRepeatedSubstring = ""
            for (x in 0..searchString.length-1) {
                val lhs = searchString.substring(x)
                for (y in x+1..searchString.length-1) {
                    val rhs = searchString.substring(y)
                    val longestPrefix = longestPrefix(lhs, rhs)
                    if (longestRepeatedSubstring.length < longestPrefix.length) {
                        longestRepeatedSubstring = longestPrefix
                    }
                }
            }
            return longestRepeatedSubstring
    }
    
    

    サフィックス配列

    より洗練されたソリューションには、「Suffix Array」と呼ばれるデータ構造というツールが必要です。このデータ構造は、ループ内に埋められた部分文字列の配列であり、各部分文字列は行の次の文字から始まり、最後まで続きます。
    たとえば、次の行の場合:

    adasDATAHEADER??
    
    

    サフィックス配列は次のようになります:

    adasDATAHEADER??
    dasDATAHEADER??
    asDATAHEADER??
    sDATAHEADER??
    DATAHEADER??
    ATAHEADER??
    TAHEADER??
    AHEADER??
    HEADER??
    EADER??
    ADER??
    DER??
    ER??
    R??
    ??
    ?
    
    

    並べ替えることで解決します

    サフィックス配列をソートしてから、現在の要素が左側 (lhs) にあり、次の要素が右側 (rhs) にあるループ内のすべての要素を調べて、longestPrefix を使用して最長のプレフィックスを計算します。方法
    について。Kotlin の例:

    fun searchLongestRepeatedSubstring(searchString: String): String {
        val suffixTree = suffixArray(searchString)
        val sortedSuffixTree = suffixTree.sorted()
    
        var longestRepeatedSubstring = ""
        for (i in 0..sortedSuffixTree.count() - 2) {
            val lhs = sortedSuffixTree[i]
            val rhs = sortedSuffixTree[i+1]
            val longestPrefix = longestPrefix(lhs, rhs)
            if (longestRepeatedSubstring.length < longestPrefix.length) {
                longestRepeatedSubstring = longestPrefix
            }
        }
        return longestRepeatedSubstring
    }
    
    

    アルゴリズムの時間計算量は O(N log N) であり、単純な解決策よりもはるかに優れています。

    ソース

    https://en.wikipedia.org/wiki/Longest_common_substring_problem

    ソースコード

    https://gitlab.com/demensdeum/algorithms

    挿入ソート、マージソート

    挿入並べ替え

    挿入並べ替え –各要素はリスト内の前の要素と比較され、 大きい方の要素があればその要素と交換されます。それ以外の場合は内部比較ループが実行されます。止まります。要素は最初から最後までソートされるため、各要素はすでにソートされたリストと比較され、*おそらく* 全体の実行時間が短縮されます。アルゴリズムの時間計算量は O(n^2)、つまりバブルの種類と同じです。

    並べ替えを結合

    並べ替えを結合–リストは 1 つの要素のグループに分割され、その後、それらのグループがペアで「マージ」され、同時に比較されます。私の実装では、ペアをマージするときに、左側の要素が右側の要素と比較され、左側の要素がなくなった場合は、右側のすべての要素が結果のリストに追加されます。 list (グループ内のすべての要素が繰り返しソートされるため、追加の比較は不要です)< br />このアルゴリズムの作業は並列化するのが非常に簡単です。ペアを結合する段階は、ディスパッチャーでの反復の終了を待ってスレッドで実行できます。
    シングルスレッド実行のアルゴリズムの出力:

    ["John", "Alice", "Mike", "#1", "Артем", "20", "60", "60", "DoubleTrouble"]
    [["John"], ["Alice"], ["Mike"], ["#1"], ["Артем"], ["20"], ["60"], ["60"], ["DoubleTrouble"]]
    [["Alice", "John"], ["#1", "Mike"], ["20", "Артем"], ["60", "60"], ["DoubleTrouble"]]
    [["#1", "Alice", "John", "Mike"], ["20", "60", "60", "Артем"], ["DoubleTrouble"]]
    [["#1", "20", "60", "60", "Alice", "John", "Mike", "Артем"], ["DoubleTrouble"]]
    ["#1", "20", "60", "60", "Alice", "DoubleTrouble", "John", "Mike", "Артем"]
    
    

    マルチスレッド実行のアルゴリズムの出力:

    ["John", "Alice", "Mike", "#1", "Артем", "20", "60", "60", "DoubleTrouble"]
    [["John"], ["Alice"], ["Mike"], ["#1"], ["Артем"], ["20"], ["60"], ["60"], ["DoubleTrouble"]]
    [["20", "Артем"], ["Alice", "John"], ["60", "60"], ["#1", "Mike"], ["DoubleTrouble"]]
    [["#1", "60", "60", "Mike"], ["20", "Alice", "John", "Артем"], ["DoubleTrouble"]]
    [["DoubleTrouble"], ["#1", "20", "60", "60", "Alice", "John", "Mike", "Артем"]]
    ["#1", "20", "60", "60", "Alice", "DoubleTrouble", "John", "Mike", "Артем"]
    
    

    アルゴリズムの時間計算量は O(n*log(n)) で、O(n^2) よりわずかに優れています

    ソース

    https://en.wikipedia.org/wiki/Insertion_sort
    https://en.wikipedia.org/wiki/Merge_sort

    ソースコード

    https://gitlab.com/demensdeum /algorithms/-/tree/master/sortAlgorithms/insertionSort
    https://gitlab.com/demensdeum/algorithms/-/tree/master/sortAlgorithms/mergeSort

    Erlang のバブルソート

    バブル ソートは非常に退屈ですが、通信用の関数型言語で実装してみるとさらに面白くなります。アーラン。

    数値のリストがあるので、それを並べ替える必要があります。バブル ソート アルゴリズムは、リスト全体を調べて、数値をペアごとに反復して比較します。チェック中に次の処理が行われます。出力リストに小さい番号が追加されるか、現在のリスト内の番号が交換されます。右側の番号が小さい場合は、反復内の次の番号で検索が続行されます。この走査は、リストに置換がなくなるまで繰り返されます。

    実際には、アルゴリズムの時間の複雑さのため、使用する価値はありません – O(n^2);私は Erlang で命令型スタイルで実装しましたが、興味がある場合は、より良いオプションを探すことができます。

    -module(bubbleSort).
    -export([main/1]).
    
    startBubbleSort([CurrentHead|Tail]) ->
        compareHeads(CurrentHead, Tail, [], [CurrentHead|Tail]).
    
    compareHeads(CurrentHead, [NextHead|Tail], [], OriginalList) ->   
        if
            CurrentHead < NextHead ->
                compareHeads(NextHead, Tail, [CurrentHead], OriginalList);
            true ->
                compareHeads(CurrentHead, Tail, [NextHead], OriginalList)
        end;
        
    compareHeads(CurrentHead, [NextHead|Tail], OriginalOutputList, OriginalList) ->
        if
            CurrentHead < NextHead ->
                OutputList = OriginalOutputList ++ [CurrentHead],
                compareHeads(NextHead, Tail, OutputList, OriginalList);
            true ->
                OutputList = OriginalOutputList ++ [NextHead],
                compareHeads(CurrentHead, Tail, OutputList, OriginalList)
        end;
      
    compareHeads(CurrentHead, [], OriginalOutputList, OriginalList) ->
        OutputList = OriginalOutputList ++ [CurrentHead],
        if
            OriginalList == OutputList ->
                io:format("OutputList: ~w~n", [OutputList]);
            true ->
                startBubbleSort(OutputList)
        end.
      
    main(_) ->
        UnsortedList = [69,7,4,44,2,9,10,6,26,1],
        startBubbleSort(UnsortedList).
    
    

    インストールと起動

    Ubuntu では、Erlang のインストールは非常に簡単です。ターミナルに sudo apt install erlang と入力するだけです。この言語では、各ファイルは外部で使用できる関数のリストを含むモジュールである必要があります。輸出。この言語の興味深い特徴には、変数がなく定数のみであること、OOP の標準構文がないこと (OOP テクニックの使用を妨げるものではありません)、そしてもちろんアクター モデルに基づくロックなしの並列計算が含まれます。

    モジュールは、対話型 erl コンソールを使用してコマンドを次々に実行するか、あるいはもっと簡単に escript bubbleSort.erl を使用して実行できます。ケースが異なると、ファイルの外観も異なります。たとえば、escript の場合は、開始元となる main 関数を作成する必要があります。

    ソース

    https://www.erlang.org/
    https://habr.com/ru/post/197364/

    ソースコード

    https://gitlab.com/ demensdeum/algorithms/blob/master/bubbleSort/bubbleSort.erl

    辞書編集的比較アルゴリズム

    辞書編集文字列比較アルゴリズムは非常に簡単に動作します。文字コードはループ内で比較され、文字が等しくない場合は結果が返されます。

    C 言語の例は次の場所にあります。
    https://github.com/gcc-mirror/gcc/blob/master/libiberty/memcmp.c

    単一の静的エンコーディングで文字を比較する必要があることを考慮する必要があります。たとえば、Swift では UTF-32 で文字ごとの比較を使用しました。 memcmp を使用した配列ソート オプションは、シングルバイト文字に対しては正確に機能しますが、その他の場合 (可変長エンコーディング)、順序が正しくない可能性があります。可変長エンコーディングに基づいた実装の可能性は排除しませんが、おそらく桁違いに複雑になるでしょう。

    アルゴリズムの時間計算量は、最良の場合は O(1)、平均および最悪の場合は O(n) です

    ソース

    https://ru.wikipedia.org/wiki/Lexicographic_order

    ソース

    https://gitlab.com/demensdeum /algorithms/blob/master/lexiCompare/lexiCompare.swift

    二分探索

    電子メール アドレス「demensdeum@gmail.com」が、手紙の受信が許可されている電子メール アドレスのリストに含まれているかどうかを確認する必要があるとします。 .

    最初の要素から最後の要素までリスト全体を調べて、その要素が指定されたアドレスと等しいかどうかを確認してみましょう –線形探索アルゴリズムを実装してみましょう。しかし、これには長い時間がかかりますか?

    この質問に答えるには、「アルゴリズムの時間計算量」の「O」表記を使用します。最悪の場合の線形探索の動作時間は配列要素の n 番目の数に等しいので、これを「O」表記で書きましょう –の上)。次に、既知のアルゴリズムには 3 つのパフォーマンス指標があることを説明する必要があります。最良の場合、最悪の場合、および平均的な場合の実行時間。たとえば、メール アドレス「demensdeum@gmail.com」は配列の最初のインデックスにあり、次の最初のステップで見つかります。このアルゴリズムを使用すると、実行時間はせいぜい – ということになります。 O(1);そして、リストの最後にある場合、これは最悪のケースです。 O(n)

    しかし、ソフトウェアの実装やハードウェアのパフォーマンスの詳細についてはどうなのでしょうか?それらは Big O に影響を与えるはずです。ここで一呼吸置いて、時間計算量の計算が、このアルゴリズムのみが存在し、他には何も存在しない、抽象的な理想的なマシンに対して計算されると想像してください。

    アルゴリズム

    OK、線形探索はかなり遅いことがわかりました。二分探索を使ってみましょう。まず、バイナリ データを処理しないことを明確にしておきます。この名前は、その動作の特殊性のためにこのメソッドに付けられました。最初に配列を 辞書編集順の場合、アルゴリズムは配列全体の範囲を取得し、範囲の中央の要素を取得し、それを比較します辞書順に基づいて、比較の結果に応じて、さらに検索するためにどの範囲を使用するかを決定します。現在の上半分または下半分。つまり、各検索ステップで、可能な 2 つの候補から決定が行われます。バイナリロジック。このステップは、単語が見つかるか見つからない (範囲の下位インデックスと上位インデックスの交差が発生する) まで繰り返されます。

    このアルゴリズムのパフォーマンス –最良のケースは要素が配列 O(1) の中央ですぐに見つかった場合であり、列挙の最悪のケースは O(log n) です。

    落とし穴

    二分探索を実装する際、 プログラミング言語ライブラリにおける辞書編集の比較の標準化が欠如という興味深い問題に遭遇しただけでなく、 実装のための統一標準が存在しないことさえ発見しました。 localeJavaScript 内で比較します。 ECMAScript 標準では、この関数のさまざまな実装が許可されています。そのため、localeCompare を使用して並べ替える場合、異なる JavaScript エンジンではまったく異なる結果が観察される可能性があります。

    したがって、アルゴリズムが正しく機能するには、同じ辞書編集比較アルゴリズムのみを並べ替えて使用する必要があり、そうでないと何も機能しません。しかし、たとえば、ある実装の独自のソート/ソートを実装せずに、Scala で配列をソートし、nodejs を使用して検索しようとすると、人間性への失望以外に何も待つことはありません。

    ソース

    辞書編集的比較とは何ですか?また、それは何を表しますか?
    Почему для вычисления сложности алгоритмов используется log N вместо lb N?
    Двоичный поиск
    Знай сложности алгоритмов
    https://stackoverflow.com/questions/52941016/sorting-in-localecompare-in-javascript

    ソースコード

    https://gitlab.com/demensdeum/algorithms

    パターンファサード


    ファサードとは、構造設計パターンを指します。複雑なシステムの操作を可能にする単一のインターフェイスを提供し、クライアントがこれらのシステムに関する実装の詳細を持たないようにできるため、コードが簡素化され、クライアントと下位レベルのシステム間の疎結合が実装されます。 GoF にはファサードの良い例があります。さまざまな目標を追求するさまざまなクライアントに、単一のコンパイラ ファサード インターフェイスを通じてコードをアセンブルする機能を提供するプログラミング言語コンパイラ。

    ソース

    https://refactoring.guru/ru/design-patterns/facade
    https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

    抽象的な工場パターン

    抽象ファクトリー–特定のクラスを指定せずに、関連オブジェクトを作成するためのインターフェイスを提供します。

    このパターンの別名がとても気に入っています – キット (キット)

    これはファクトリ メソッドに非常に似ていますが、抽象ファクトリは作成されるオブジェクト間の関係を記述する必要があります。そうでない場合は、単なる神オブジェクトになります。 すべてを生み出すアンチパターンは行き当たりばったりです。

    メガネ用の AR フレームワークを開発するところを想像してください。屋内ナビゲーションの矢印、店舗のアイコン、興味深い場所、ウィンドウ、ユーザーが現在いる場所に関する情報を含むボタンを画面上に表示します。

    同時に、AR 環境コントロールの外観と動作をカスタマイズする機能も必要です。この場合にこそ、Set パターンを使用する必要があります。

    Abstract FactoryAbstract Products のインターフェースを書いてみましょう –親プロトコル、AR 環境要素:

    protocol ARFactory {
        func arrow() -> ARArrow
        func icon() -> ARIcon
        func button() -> ARButton
        func window() -> ARWindow
    }
    
    protocol ARArrow {
        var image: { get }
        func handleSelection()
    }
    
    protocol ARIcon {
        var image: { get }
        var title: String
    }
    
    protocol ARButton {
        var title: String
        func handleSelection()
    }
    
    protocol ARWindow {
        var title: String
        var draw(canvas: Canvas)
    }
    

    キット開発者は、Abstract Factory インターフェイスに基づいて Concrete Factory を実装する必要があります。また、アプリケーションの残りの部分は、コードを変更せずにファクトリで動作できるように、すべての要素をまとめて実装する必要があります。< /p>

    ソース

    https://refactoring.guru/ru/design-patterns /abstract-factory
    https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

    ファクトリーメソッド

    ファクトリー メソッド パターンは、ジェネレーティブ デザイン パターンを指します。
    このパターンは、特定のクラスのオブジェクトを作成するためのインターフェイスの作成を記述します。簡単そうに思えますよね?

    理論上

    AR メガネを操作するためのフレームワークを開発しているとします。頭を横に傾けると、利用可能なアプリケーションのメニューがユーザーの目の前に表示されるはずです。アプリケーションは、当社のフレームワークのクライアントであるサードパーティ企業によって開発されます。当然のことながら、どのアプリケーション、アイコン、名前が表示されるべきかわからないため、アイコンとアプリケーションに関する関連情報を実装するためのインターフェイスを提供する必要があります。これを製品:

    と呼びましょう。

    protocol Product {
     var name: String { get }
     var image: Image { get }
     var executablePath: String { get }
    }
    

    次に、クライアントが特定の製品用の一連のアプリケーションの発行を実装できるように、インターフェイスを提供する必要があります。名前が付いたアプリケーション アイコンの配列。これはフレームワーク内ですでに描画されています。

    このインターフェースを書いてみましょう – プロダクトの配列を返すファクトリ メソッドを含むクリエイターインターフェース。

    protocol Creator {
     func factoryMethod() -> [Product]
    }
    

    実際に

    AR フレームワークの最初のクライアントは 7B – 社でした。 ホンジュラスのコーヒーメーカー用ソフトウェアの大手サプライヤー。彼らは、屋内マップ モードを使用して、コーヒーを淹れたり、水や豆が入っているかどうかを確認したり、最寄りのコーヒー メーカーへの道を示す機能を備えた拡張現実グラスを販売したいと考えています。

    彼らはソフトウェアの開発を引き受けます。私たちは、アプリケーションのリストとその詳細を正しく表示するために、クリエイター インターフェイスと 製品 インターフェイスに関するドキュメントを提供することのみを求められます。

    ドキュメントを転送した後、7B 社は Creator インターフェースを使用して Specific Creator を実装します。アプリケーションアイコンの配列を返すクラス。アイコン アプリケーション自体は、製品 インターフェースを実装する特定の製品 クラスです。

    特定の製品のコード例:

    class CoffeeMachineLocator: implements Product {
     let name = “7B Coffee Machine Locator v.3000”
     let image = Image.atPath(“images/locator.tga”)
     let executablePath = “CoffeeMachineLocator.wasm”
    }
    
    class iPuchinno: implements Product {
     let name = “iPuchinno 1.0.3”
     let image = Image.atPath(“images/puchino.pvrtc”)
     let executablePath = “neutron/ipuchBugFixFinalNoFreezeFixAlpha4.js”
    }
    

    クラス Concrete Creator は、2 つのアプリケーションの配列を提供します。

    class 7BAppsCreator: implements Creator {
     func factoryMethod() -> [Product] {
      return [CoffeeMachineLocator(), iPuchinno()]
     }
    }
    

    この後、7B 社はコンクリート製品コンクリート クリエーターのライブラリを編集し、それを当社のフレームワークと組み合わせて、コーヒー メーカー用の AR グラスの販売を開始します。 私たちの側での追加は必要ありません

    ソース

    https://refactoring.guru/ru/design-patterns/command
    https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

    パターンコマンド

    コマンド パターンは動作設計パターンを指します。

    これは私が長い間こだわってきたパターンです。単純すぎて非常に複雑です。しかし、個人的には、独学の素晴らしさは、特定のトピックをあらゆる角度から研究するための時間をいつでも手に入れられることだと思います。

    したがって、GoF では、適用可能性が非常に簡潔かつ明確に説明されています。
    リクエストをオブジェクトとしてカプセル化し、さまざまなリクエストでクライアントをパラメータ化し、キューを使用し、リクエストをログに記録し、キャンセル操作を実行できるようにします。

    次に、説明にあるコマンドの単純なバージョンを実装してみましょう。

    string fakeTrumpsRequest = “SELECT * from Users where name beginsWith DonaldTrump”
    

    リクエストは文字列クラス オブジェクトにカプセル化されており、クライアントの設定、キューへのコマンドの追加、ログ記録、キャンセル(「スナップショット」パターンを使用)に使用できます。

    SQL クエリなどを実行するにはこれで十分だと思いますが、実装の詳細、さまざまなアプリケーション オプション、パターンのコード ベース、クライアント ロール、補助クラスも大きく異なります。

    マテリアルパーツ

    コマンド パターンは、単一の execute() メソッドを含む コマンド プロトコル で始まります。次に特定のコマンドとレシーバーが続きます。CC はレシーバー上で操作を実装し、レシーバーとアクションの間の接続を記述します。何か不明な点はありますか?私もそうですが、先に進みましょう。 クライアント特定のコマンドのインスタンスを作成し、 それをレシーバーに関連付けます。 依頼者コマンドを起動するプロセスを実行するオブジェクト。

    例を使用してそれを理解してみましょう。myPhone で myOS を更新したいとします。これを行うには、myOS_Update アプリケーションを起動します。その中で [Update Now] ボタンを押すと、10 秒後にシステムが更新されます。アップデートが成功したことを報告します。

    上記の例のクライアント は myOS_Update! アプリケーション、呼び出し元 は「今すぐ更新!」ボタンで、特定のコマンド を起動します。 b>レシーバーにアクセスするexecute() メソッドを使用してシステムを更新します–オペレーティング システムのアップデート デーモン。

    使用例

    myOS_Update アプリケーションの UI を受け入れましょう。非常に優れていたため、他のオペレーティング システムを更新するためのインターフェイスを提供するために、別の製品として販売することにしました。この場合、ライブラリを介して拡張機能をサポートするアプリケーションを実装します。ライブラリには特定のコマンド、 レシーバーの実装があり、静的/不変の呼び出し元は残します。 、クライアント、プロトコルコマンド

    したがって、コードは変更されないままであるため、変更可能なコードをサポートする必要はありません。特定のコマンド のコード内のエラーにより、クライアント側で実装された場合にのみ問題が発生する可能性があります。 受信機。また、この実装では、メイン アプリケーションのソース コードを転送する必要はありません。つまり、コマンド パターンを使用してコマンドと UI インタラクションをカプセル化しました。

    ソース

    https://refactoring.guru/ru/design-patterns/command
    https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

    Ubuntu OSXCros CMake 用の macOS アプリケーションの構築

    この投稿では、CMake と osxcross を使用して、Ubuntu ビルド マシン上で macOS 用のクロスプラットフォーム C++ アプリケーションを構築する方法について説明します。
    まず、osxcross ツールチェーンをインストールします。
    https://github.com/tpoechtrager/osxcross
    インストールは 3 段階で行われ、依存関係をダウンロードします。

    cd tools
    ./get_dependencies.sh
    

    Apple 公式 Web サイトから XCode.xip をダウンロードし、XCode から SDK をダウンロードします。

    ./gen_sdk_package_pbzx.sh /media/demensdeum/2CE62A79E62A4404/LinuxSupportStorage/xcode111.xip
    

    最後のステップで XCode 使用許諾契約を読んでいただければ幸いです。次に、必要なプレフィックスを使用してツールチェーンを構築します。

    INSTALLPREFIX=/home/demensdeum/Apps/osxcross ./build.sh 
    

    これで、前の手順のプレフィックス ディレクトリから osxcross を使用できるようになります。 CMake の新しいビルド マクロを追加して、必要なものをすべて記述してみましょう。

    if (OSXCROSS)
    SET(CMAKE_SYSTEM_NAME Darwin)
    SET(CMAKE_C_COMPILER o64-clang)
    SET(CMAKE_CXX_COMPILER o64-clang++)
    SET(CMAKE_C_COMPILER_AR x86_64-apple-darwin19-ar)
    SET(CMAKE_CXX_COMPILER_AR x86_64-apple-darwin19-ar)
    SET(CMAKE_LINKER x86_64-apple-darwin19-ld)
    SET(ENV{OSXCROSS_MP_INC} 1)
    endif()
    

    動的リンクが成功しなかったため、ライブラリを静的にエクスポートします。

    if (OSXCROSS)
    add_library(FlameSteelCore STATIC ${SOURCE_FILES})
    else()
    

    次に、osxcross に必要なライブラリがないという事実に直面するかもしれません。私は SDL2 を使用しているときにこれに遭遇しました。 osxcross は既製のライブラリ パッケージをサポートしています –マックポート。たとえば、SDL2-mixer をインストールする場合は次のようになります。

    osxcross-macports -v install libsdl2_mixer
    

    この後、cmake-make リンクで通常どおりライブラリ/アプリケーションの構築を開始できます。必要に応じてライブラリの静的リンクを指定することを忘れないでください。

    ライブラリの手動アセンブリ

    現在、最終的なアプリケーションを構築するときに、静的リンク中にライブラリが正しくアーカイブされないという問題が発生し、次のエラーが発生します。

    file was built for archive which is not the architecture being linked (x86_64)
    

    このチケットと非常によく似た、回避策を実行すると、アセンブリが正しく完了します。静的ライブラリを解凍し、osxcross アーカイバを使用して新たにビルドしましょう。

    ar x ../libFlameSteelCore.a
    rm ../libFlameSteelCore.a
    x86_64-apple-darwin19-ar rcs ../libFlameSteelCore.a *.o
    

    私は個人的に、macOS アプリケーションを Ubuntu 上で直接実行する機能 (少なくとも一部の機能) が欠如していることも問題の 1 つであると考えています。もちろん、プロジェクトがあります ダーリンですが、サポートにはまだ改善の余地がたくさんあります。

    ソース

    https://github.com/tpoechtrager/osxcross