Git Product home page Git Product logo

opennask's People

Contributors

hangingman avatar ohiroyukinagata avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

opennask's Issues

INSTRSETの実装

ちょっと面倒くさい

  また、INSTRSETなどの追加命令があるので、NASMとの互換ソースを書く際に問題にな
るかもしれません。そのために、「;%NASK」という記述をすることができます。これはN
ASMでは当然注釈になりますが、NASKではスペース扱いになります。NASK専用ソースとい
うことでしたら、この記述はいりません。

  INSTRSETはCPU名を指定します。今のところ以下のCPU名が指定できます。

  "8086", "80186", "80286", "80286p", "i386", "i386p", "i486", "i486p"

  8086モードでは、FSやEBXなどが予約語になりません。デフォルトは8086です。末尾
にpが付くのはプロテクトモード用の命令群を使えるようにする意味です。なお8086を
選択しているからといって、8086に実行できないコードを出力する可能性がないわけで
はありません。単に予約語をラベル定義用に開放しているだけです。ですから、Jccな
どでNEARにされてしまうこともありえます。不安な場合は、SHORTを明示しましょう。

spdlogのloggerレベル(trace)を条件コンパイルする、開発時デバッグ用にtraceレベルを導入する

ref https://tadaoyamaoka.hatenablog.com/entry/2018/02/10/175237

  • traceログの関数はリリース時不要なため
    • debugレベルは入れてもいいかな
  • traceログはコマンドラインからは -vv --very-verbose とやったときだけ出す

(1) debug/traceを条件コンパイル

デバッグ版でデバッグ用ログ出力の条件コンパイルを有効するには、
SPDLOG_TRACE_ONとSPDLOG_DEBUG_ONマクロを定義する。

#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON

(2) 開発時デバッグ用にtraceレベルを導入する

  • 今までdebugレベルしか使っていなかったが、traceレベルも導入してdebugと差をつける
  • debugはユーザーにも見せてOKなものとする

備考

  • cmake連携時は CMAKE_BUILD_TYPE==Debug の際にSPDLOG_TRACE_ONにしておけばいいと思う
  • CIでのビルド時やパッケージビルド時は明示的に CMAKE_BUILD_TYPE==Release にする
  • cmakeの中で毎回 message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") とかして今の状態を出したい

bison+flexを使う

bisonはC++のコードを出力できるらしい
Re: What are the benefits to migrate from Bison++ to Bison

リファクタリング

  • #33 editorconfig でコードのフォーマットを固定
  • #34 ログが多すぎるので少なくする

開発環境準備

  • #35 bison/flexをCIで動かせるようにする対応

実装

  • #36 lexer/parserを仮に設定し字句解析と構文解析
  • #37 BNFCで生成したソースを使う
  • #38 アドレス指定の際の「exp:exp」のようなテキストを読めるようにする

マイグレーション

残り、flexをc++インターフェースで使うかどうかというところとopennaskの実装をbnfcの生成コードで置き換えていくという作業がある

  • flexをc++インターフェースで使うかどうか検討して実装

    • bnfcの生成するflexはc interfaceであり、かなり改修は困難だとわかったのでc++インターフェースを使うのは諦める
    • bnfcの方の改修をするかも
    • 結局滅茶苦茶がんばってbnfcを修正し、C++14相当のコードを生成できるようにした
  • opennaskの実装をbnfcの生成コードで置き換え

メモリーリークも解決した( #51 )ので、2022年は暇を見つけて上記の続きをやる

メモリーアドレッシング・モード

実装にけっこう悩んでいたが、評価時に単純に左から順に連結していけばいいかも

# 擬似コード
for (int i = 0; i < オペランドのサイズ; i++) {
    オペランドと( 即値 or  ラベル)を取り出して機械語の結果を連結していく
}

ディスプレイスメントでどういうパターンがあるのかがわかりずらい

たぶん以下のパターンしかない?

BASE + (INDEX*SCALE) + DISPLACEMENT

* BASE: ベースレジスタ
* INDEX: インデックスレジスタ、配列の添え字のようなもの
* SCALE: 2,4,8 のいずれか。配列要素のサイズを指定する
* DISPLACEMENT: アドレス、もしくはベースレジスタからのオフセットを示す。定数。

osaskプロジェクトだと REG[REG+X] ぐらいしかパターンはなさそう
下記のようなパターンがあるっぽい
https://web.archive.org/web/20230616085837/https://faculty.kfupm.edu.sa/COE/aimane/assembly/pagegen-64.aspx.htm

➔ 上記部分は最終的にEBNFを書き換えて攻略した #73 で継続して実施

harib00iの実装

  • 実装するものがかなり多い
  • tinyexpr の組み込み => これで MOV AX,1*8 のような構文に対応できた
  • CALL: ほぼJMP命令と同じなのでコピペ
  • OUT: もう実装されてた
  • LGDT: メモリー指定時のニーモニックが面倒くさい
  • MOV: 制御レジスタが入る、CRnへのデータのコピー
  • AND: 9704f10
  • OR: 何らかの理由で0x83 /1 ibが優先されているが実装
  • IMUL: 7a07af6 で実装、1パターンしか対応してない
  • SUB: c0a1e4e で実装、一部動きが変
  • SHR: cd8eff1 で実装
  • JZ: 0x74 cb ゼロの場合ショートジャンプします、 29c8bc1 で実装
  • IN: 494a998 で実装
  • JNZ: 0x75 cb ゼロでない場合ショートジャンプします
  • RET: 0xc3 ジャンプからの復帰
  • ALIGNB: アラインメントを埋めるための疑似命令、サンプル少ないので難しい 6650aeb で実装。
  • アセンブラへのショートカット
    3日目のアセンブラをダンプ(後編)#harib00i

PASS-1, PASS-2 Assemblerについて調査/実装

#56 (comment)

この問題は"forward reference"として知られる

ChatGPT先生曰く

一般的にはパス1アセンブリの方が速度は遅いものの、実装が簡単であるとされています

らしいので、その方向でいくかも

調査

パス1アセンブラの解説

解説のためにLTORG命令が出ているが、たぶんnaskでは使わないやつ
http://www.arteceed.net/755.html

概要

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

設計メモ

ソフトウェアコンポーネントについて

  • 登場人物

    • ソースコード: 元のアセンブラ文
    • LC(Location Counter;ロケーションカウンタ): 現在のプログラムカウント(現在のアドレス)を指す。アセンブラがプログラムを解析している途中で、各命令のサイズを確定し、各命令に対応する絶対アドレスを計算するために使用される。LCは、プログラムの解析が進むにつれて逐次増加します。そのため、LCの値は常に現在のアドレスを示す。bnfcの機能で取得できないか。
    • IC(Intermediate Code;中間言語): プログラムのコンパイル処理の途中結果。bnfcを使っているので、構文解析後のASTを利用できそうである。
  • 補助的要素

    • ST (Symbol Table;シンボルテーブル): アセンブラ言語において、シンボル名とそのシンボルに対応する値を格納するためのテーブル
    • LT (Literal Table;リテラルテーブル): アセンブラ言語において、リテラル(プログラム中に直接定義された定数値)に対応するアドレスを格納するためのテーブル
  • Intermediate Code;中間言語、の一般的な表し方

    • AD (Address)
    • DL (Data Length)
    • IS は命令セット(=Instruction)

これらの用語の意味は、特定のアセンブラにおいての文脈に応じて決定されるため、具体的な解釈については使用されているアセンブラのドキュメンテーションを参照することが望ましい。

フローチャート

https://gist.github.com/hangingman/b1fa7598976e753d59e24599040903dc

他の実装を参考にして機械語生成部分(code selector / instruction selection)を簡略化

検討

課題感

  • 字句解析、構文解析、ラベルの解決はできたが肝心の機械語生成部分(code selector)の実装が厳しい

結果

  • 色々と調査した結果、こういう部分は昔から難しいと言われていて下記のような解決策があるようだった
    • (1) コードジェネレータを作成する
      • BURSという方法で中間表現からアセンブラを生成する
      • これをやると割と宣言的(declarative)に機械語を作るところまでいけそう(特にif elseのような制御構文をもったプログラミング言語に対して有効)
      • ただしこれは中間表現をつくる必要が出てくる、そしてアセンブラなのにアセンブラを生成してどうする(...なぜか既存手法はアセンブラまでしか生成していないのだ)
      • この記事がとても参考になった Code-generator generators: generating the instruction selector
    • (2) 頑張って自分ですべて実装する
      • もとのnaskとかLuaJitのDynASMはこんな感じ
      • ただ、これは保守できないし辛い
    • (3) Jitの実装を借りてくる

Jit、どれを使うか

  • LLVM, v8は規模がでかくメジャーすぎるので除外
  • libgccjitはgccに依存してしまいアーキが選べない

まだ候補的にはlibjit, lightning, nanojitを残した状態ではあるがasmjitを採用することにした。
たぶんこれでだいぶ楽できるはず…

残作業

  • ADD #74
  • DB #75
  • DD #75
  • DW #75
  • RESB ... RESBにあたる命令はasmjitになかった
    • db命令を内部で使い、asmjitのアーキテクチャに乗せた #78
  • CMP #75
  • CLI #76
  • HLT #76
  • NOP #76

自力実装の場合の参考

https://github.com/nayuki/x86-Assembler

makeMachineCode 部分の実装に注目

https://github.com/nayuki/x86-Assembler/blob/master/src/org/p79068/assembler/generator/CodeGenerator.java#L77

public InstructionPattern match 部分の実装に注目

https://github.com/nayuki/x86-Assembler/blob/ae0062266fbdc2c3f71d0b60bcb1e6e9e97f09c2/src/org/p79068/assembler/generator/InstructionPatternTable.java#L570

  • #match(String ニーモニック, List<Operand> オペランド) の形の引数を持たせている
  • #matchInstructionPattern クラスを返し、それは toBytes で機械語を返す

改名

opennaskはダサいのでなんかほかの名前にしてみたい

出力の遅延評価の試み

C++でLazyExpressionというものがある
https://github.com/tirimatangi/LazyExpression

先に機械語を確定してからoffsetの計算をするような仕組みを導入したい
参考: ラベルの判定など

メモ

    using LazyEvalVariant = std::variant<std::vector<uint8_t>, Label>;  // 機械語かラベルかどちらか
    using LazyEvalStatement = std::variant<LabelStmt, DeclareStmt, ConfigStmt, MnemonicStmt, OpcodeStmt>; // 一応もとのstatement(文)を参照できるようにする

    std::vector<LazyEvalVariant> machine_codes = {  [ 0x88, 0x88, 0x88 ],  {Label, name, offset...}  }; // イメージ
    std::vector<LazyEvalStatement> statements = {  (DeclareStmt "LEDS" [(ImmExp [(HexFactor "0x0ff1")]), ...  }; // イメージ

    // Calculates offset etc.
    auto f = [&](LazyEvalVariant v, LazyEvalStatement stmt) { 計算; };
    auto expr = Expression{f, machine_codes, statements};

    auto evaluated_expr = expr();
    // このあと rubyで言うところの evaluated_expr.flatten() 的な処理が必要

qemu-system-i386: -localtime: invalid optionが出て起動できない

ref https://www.qemu.org/docs/master/about/removed-features.html#localtime-removed-in-3-1

qemu 3.1でオプションが無くなった

-localtime (removed in 3.1)

Replaced by -rtc base=localtime.

https://www.qemu.org/docs/master/system/qemu-manpage.html

Specify base as utc or localtime to let the RTC start at the current UTC or local time, 
respectively. localtime is required for correct date in MS-DOS or Windows. 
To start at a specific point in time, provide datetime in the format 2006-06-17T16:01:21 or 2006-06-17. The default base is UTC.

MS-DOSで必要な機能だったようだ

bnfcにメモリリーク問題がある

cpputestでユニットテストをするとmemory leakを指摘される

../test/exp_suite.cpp:123: error: Failure in TEST(exp_suite, testCalc)
	Memory leak(s) found.
Alloc num (52) Leak size: 16 Allocated at: <unknown> and line: 0. Type: "new"
	Memory: <0x55cf60fa8b90> Content:
    0000: c8 ca ef 5f cf 55 00 00  40 84 fa 60 cf 55 00 00 |..._.U..@..`.U..|
Alloc num (51) Leak size: 8 Allocated at: <unknown> and line: 0. Type: "new"
	Memory: <0x55cf60fa8440> Content:
    0000: a8 bd ef 5f cf 55 00 00                          |..._.U..|
Total number of leaks:  2

ワークアラウンド

  • ed44b16
    再帰的にdeleteを実行している

原因と思われるところ(これは修正済みだったが参考のため残す)

  • BNFC/bnfc#347
    • flexの初期化でファイルポインタ(FILE*)ではなく文字列を指定したときのyyrestartの呼び方がおかしい模様
    • BNFC/bnfc#347 (comment) に記載のようにfmemopenを使うことでとりあえず回避できる

workaround

    nask_statements = R"( blah blah blah )";
    std::unique_ptr<Driver> d(new Driver(false, false));
    FILE* stream = fmemopen(&nask_statements, strlen(nask_statements), "r");
    d->Parse<Program>(stream, "test.img");
    fclose(stream);

メモリーアドレッシングモードの実装修正

ref #32

結論から書くと、このような処理も字句解析時にある程度解析してもいいのかもしれない

仕様

ref https://web.archive.org/web/20230616085837/https://faculty.kfupm.edu.sa/COE/aimane/assembly/pagegen-64.aspx.htm

16ビットメモリアドレッシングモード

Operand Type Pattern
direct [displacement]
register indirect [BX]
[SI]
[DI]
[BP]
based [BP + displacement]
[BX + displacement]
indexed [SI + displacement]
[DI + displacement]
based-indexed [BX + SI]
[BX + DI]
[BP + SI]
[BP + DI]
based-indexed with displacement [BX + SI + displacement]
[BX + DI + displacement]
[BP + SI + displacement]
[BP + DI + displacement]

32ビットメモリアドレッシングモード

Operand Type Pattern
direct [displacement]
register indirect [base]
based [base + displacement]
indexed [Index*scale + displacement]
based-indexed [base + Index + displacement]
based-indexed with displacement [base + index*scale + displacement]
  • 注意

    • ESP をインデックスレジスタとして使用することはできない
  • メモ

    • SIB バイトの計算が必要

(1) MOV ECX, [ESP]
を考える

(1)をアセンブルすると
67 66 8B 0C 24 となる
67 66 8B 0C までは自明なのであるが、0x24 がよくわからない

下記の表をもとに計算する

  • ss (Scale)= 00 <-- 掛け算してないから
  • iii (Index)= 100 <-- 当てはまるものがないので
  • bbb (Base)= 100 <-- 表の上部にある通り
  • 結合した2進数 "00 100 100"
  • 16進数では0x24

image

Intelの本, p.37
IA-32 インテル ® アーキテクチャ
ソフトウェア・デベロッパーズ・
マニュアル 中巻 A:命令セット・リファレンス A-M

webでアセンブラの結果確認できるサイト
https://defuse.ca/online-x86-assembler.htm

x86の命令セットをある程度自動で作成する

Limitations

This project is for demonstration purposes and only supports a super limited subset of x86 assembly. 
Many instructions are currently not available and many assembly language features are missing or broken.

Many instructions are currently not available とは書かれているが https://github.com/mattbierner/Template-Assembly/blob/master/include/tasm/isa/x86.h をgrepすると223個はサポートされていそうに見える。手直ししたら使えそうな気はする。

$ cat x86.h | grep auto | sed -e 's/(.*$//g' -e 's/constexpr auto //g' | sed -e 's/^[[:blank:]]*//' | uniq | wc -l
223

bnfcで構文解析後のデータをある程度自動でTemplate-Assemblyに渡せば、コードを書く量を減らせそうな気がしている。
(そもそもosaskをビルドするのにフル実装のアセンブリ実装は不要な気もするけど)


2023/02/14

x86-64のJSON
https://github.com/astocko/json-x86-64

をobj化し
https://www.linuxjournal.com/content/embedding-file-executable-aka-hello-world-version-5967
https://github.com/graphitemaster/incbin

この形式でconstexpr化, struct化できる
https://github.com/jorgen/json_struct/blob/4f6d60c1c90aa55110c46342487d64615790ce8a/tests/json-struct-nested.cpp#L27-L44

PE(Portable Executable)ヘッダの出力

EBNF版のnask_parseで実装を置き換える

残り必要なテストケース

差分があるものを列挙している

ブートローダー

  • 3日目
    • Day03Suite Harib00i

naskfunc.nas

21日目からはテストケースは不要で、とりあえず完全一致してればいいかな

  • 3日目
    • Day03Suite Harib00j
  • 4日目
    • Day04Suite Harib01a
    • Day04Suite Harib01f
  • 5日目
    • Day05Suite Harib02i
  • 6日目
    • Day06Suite Harib03e
  • 9日目
    • Day09Suite Harib06b (新出命令ナシ)
    • Day09Suite Harib06c
  • 12日目
    • Day12Suite Harib09a
  • 15日目
    • Day15Suite Harib12a
    • Day15Suite Harib12b (新出命令ナシ)
    • Day15Suite Harib12c (新出命令ナシ)
  • 20日目
    • Day20Suite Harib17b (新出命令ナシ)
    • Day20Suite Harib17c (新出命令ナシ)
    • Day20Suite Harib17d (新出命令ナシ)
    • Day20Suite Harib17e
    • Day20Suite Harib17g
    • Day20Suite Harib17h (新出命令ナシ)
  • 21日目
    • Day21Suite Harib18d (新出命令ナシ)
    • Day21Suite Harib18e (新出命令ナシ)
    • Day21Suite Harib18g (新出命令ナシ)
  • 22日目
    • Day22Suite Harib19b (新出命令ナシ)
    • Day22Suite Harib19c (新出命令ナシ)
  • 25日目
    • Day25Suite Harib22f (新出命令ナシ)

実装必要な話

昔作成したテストはもう少し作り直すかも
5日目までできてしまえば、あとは昔のコードを削除してバージョンを2.0.0にしたい

<program> ::= <block> | <compound statement>
<block> ::= <unlabelled block> | <label>: <block>

<unlabelled block> ::= <block head> ; <compound tail>

<block head> ::= begin <declaration> | <block head> ; <declaration>
<compound statement> ::= <unlabelled compound> | <label>: <compound statement>
<unlabelled compound> ::= begin <compound tail>
<compound tail> ::= <statement> end | <statement> ; <compound tail>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.