「Cでアセンブラーを記述します。」なぜ低レベル言語の機械語翻訳機を高レベル言語で書くのですか?


13

私のマイクロプロセッサクラスのインストラクターが私たちに課題を与えて言った:

「Cでアセンブラーを作成します。」-私の最愛の教授

だから、私には少し非論理的に思えた。

私が間違っていなければ、アセンブリ言語は、機械語コードから高レベル言語の旅への最初のステップです。つまり、Cはアセンブリよりも高レベルの言語です。では、Cでアセンブラーを書く意味は何ですか?C言語がないのに、彼らは過去何をしていたのですか?彼らはマシンコードでアセンブラーを書いていましたか?

低レベル言語用の機械語翻訳を高レベル言語で書くのは意味がありません。

たとえば、そのアーキテクチャ用のCコンパイラさえない、まったく新しいマイクロプロセッサアーキテクチャを作成したとします。Cで記述されたアセンブラは、新しいアーキテクチャをシミュレートできますか?私はそれが役に立たないかどうかを意味しますか?

ところで、GNU AssemblerとNetwide AssemblerはCで書かれていることを知っています。また、なぜそれらがCで書かれているのだろうかと思っています。

最後に、これは私たちの教授が私たちに与えた単純なアセンブラのソースコードの例です。

// to compile, gcc assembler.c -o assembler
// No error check is provided.
// Variable names cannot start with 0-9.
// hexadecimals are twos complement.
// first address of the code section is zero, data section follows the code section.
//fout tables are formed: jump table, ldi table, label table and variable table.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//Converts a hexadecimal string to integer.
int hex2int( char* hex)  
{
    int result=0;

    while ((*hex)!='\0')
    {
        if (('0'<=(*hex))&&((*hex)<='9'))
            result = result*16 + (*hex) -'0';
        else if (('a'<=(*hex))&&((*hex)<='f'))
            result = result*16 + (*hex) -'a'+10;
        else if (('A'<=(*hex))&&((*hex)<='F'))
            result = result*16 + (*hex) -'A'+10; 
        hex++;
    }
    return(result);
}


main()
{   
    FILE *fp;
        char line[100];
        char *token = NULL;
    char *op1, *op2, *op3, *label;
    char ch;
    int  chch;

    int program[1000];
    int counter=0;  //holds the address of the machine code instruction




// A label is a symbol which mark a location in a program. In the example 
// program above, the string "lpp", "loop" and "lp1" are labels.
    struct label  
    {
        int location;
        char *label;
    };
    struct label labeltable[50]; //there can be 50 labels at most in our programs
    int nooflabels = 0; //number of labels encountered during assembly.




// Jump instructions cannot be assembled readily because we may not know the value of 
// the label when we encountered a jump instruction. This happens if the label used by
// that jump instruction appear below that jump instruction. This is the situation 
// with the label "loop" in the example program above. Hence, the location of jump 
// instructions must be stored.
    struct jumpinstruction   
    {
        int location;
        char *label;
    };
    struct jumpinstruction jumptable[100]; //There can be at most 100 jumps
    int noofjumps=0;  //number of jumps encountered during assembly.    




// The list of variables in .data section and their locations.
    struct variable
    {
        int location;
        char *name;
    };
    struct variable variabletable[50]; //There can be 50 varables at most.
    int noofvariables = 0;




//Variables and labels are used by ldi instructions.
//The memory for the variables are traditionally allocated at the end of the code section.
//Hence their addresses are not known when we assemble a ldi instruction. Also, the value of 
//a label may not be known when we encounter a ldi instruction which uses that label.
//Hence, the location of the ldi instructions must be kept, and these instructions must be 
//modified when we discover the address of the label or variable that it uses.
    struct ldiinstruction   
    {
        int location;
        char *name;
    };
    struct ldiinstruction lditable[100];
    int noofldis=0;




    fp = fopen("name_of_program","r");

    if (fp != NULL)
    {
        while(fgets(line,sizeof line,fp)!= NULL)  //skip till .code section
        {
            token=strtok(line,"\n\t\r ");
            if (strcmp(token,".code")==0 )
                break;
        } 
        while(fgets(line,sizeof line,fp)!= NULL)
        {
            token=strtok(line,"\n\t\r ");  //get the instruction mnemonic or label

//========================================   FIRST PASS  ======================================================
            while (token)
            {
                if (strcmp(token,"ldi")==0)        //---------------LDI INSTRUCTION--------------------
                {
                    op1 = strtok(NULL,"\n\t\r ");                                //get the 1st operand of ldi, which is the register that ldi loads
                    op2 = strtok(NULL,"\n\t\r ");                                //get the 2nd operand of ldi, which is the data that is to be loaded
                    program[counter]=0x1000+hex2int(op1);                        //generate the first 16-bit of the ldi instruction
                    counter++;                                                   //move to the second 16-bit of the ldi instruction
                    if ((op2[0]=='0')&&(op2[1]=='x'))                            //if the 2nd operand is twos complement hexadecimal
                        program[counter]=hex2int(op2+2)&0xffff;              //convert it to integer and form the second 16-bit 
                    else if ((  (op2[0])=='-') || ((op2[0]>='0')&&(op2[0]<='9')))       //if the 2nd operand is decimal 
                        program[counter]=atoi(op2)&0xffff;                         //convert it to integer and form the second 16-bit 
                    else                                                           //if the second operand is not decimal or hexadecimal, it is a laber or a variable.
                    {                                                               //in this case, the 2nd 16-bits of the ldi instruction cannot be generated.
                        lditable[noofldis].location = counter;                 //record the location of this 2nd 16-bit  
                        op1=(char*)malloc(sizeof(op2));                         //and the name of the label/variable that it must contain
                        strcpy(op1,op2);                                        //in the lditable array.
                        lditable[noofldis].name = op1;
                        noofldis++;                                             
                    }       
                    counter++;                                                     //skip to the next memory location 
                }                                       

                else if (strcmp(token,"ld")==0)      //------------LD INSTRUCTION---------------------         
                {
                    op1 = strtok(NULL,"\n\t\r ");                //get the 1st operand of ld, which is the destination register
                    op2 = strtok(NULL,"\n\t\r ");                //get the 2nd operand of ld, which is the source register
                    ch = (op1[0]-48)| ((op2[0]-48) << 3);        //form bits 11-0 of machine code. 48 is ASCII value of '0'
                    program[counter]=0x2000+((ch)&0x00ff);       //form the instruction and write it to memory
                    counter++;                                   //skip to the next empty location in memory
                }
                else if (strcmp(token,"st")==0) //-------------ST INSTRUCTION--------------------
                {
                    //to be added
                }
                else if (strcmp(token,"jz")==0) //------------- CONDITIONAL JUMP ------------------
                {
                    //to be added
                }
                else if (strcmp(token,"jmp")==0)  //-------------- JUMP -----------------------------
                {
                    op1 = strtok(NULL,"\n\t\r ");           //read the label
                    jumptable[noofjumps].location = counter;    //write the jz instruction's location into the jumptable 
                    op2=(char*)malloc(sizeof(op1));         //allocate space for the label                  
                    strcpy(op2,op1);                //copy the label into the allocated space
                    jumptable[noofjumps].label=op2;         //point to the label from the jumptable
                    noofjumps++;                    //skip to the next empty location in jumptable
                    program[counter]=0x5000;            //write the incomplete instruction (just opcode) to memory
                    counter++;                  //skip to the next empty location in memory.
                }               
                else if (strcmp(token,"add")==0) //----------------- ADD -------------------------------
                {
                    op1 = strtok(NULL,"\n\t\r ");    
                    op2 = strtok(NULL,"\n\t\r ");
                    op3 = strtok(NULL,"\n\t\r ");
                    chch = (op1[0]-48)| ((op2[0]-48)<<3)|((op3[0]-48)<<6);  
                    program[counter]=0x7000+((chch)&0x00ff); 
                    counter++; 
                }
                else if (strcmp(token,"sub")==0)
                {
                    //to be added
                }
                else if (strcmp(token,"and")==0)
                {
                    //to be added
                }
                else if (strcmp(token,"or")==0)
                {
                    //to be added
                }
                else if (strcmp(token,"xor")==0)
                {
                    //to be added
                }                       
                else if (strcmp(token,"not")==0)
                {
                    op1 = strtok(NULL,"\n\t\r ");
                    op2 = strtok(NULL,"\n\t\r ");
                    ch = (op1[0]-48)| ((op2[0]-48)<<3);
                    program[counter]=0x7500+((ch)&0x00ff);  
                    counter++;
                }
                else if (strcmp(token,"mov")==0)
                {
                    //to be added
                }
                else if (strcmp(token,"inc")==0)
                {
                    op1 = strtok(NULL,"\n\t\r ");
                    ch = (op1[0]-48)| ((op1[0]-48)<<3);
                    program[counter]=0x7700+((ch)&0x00ff);  
                    counter++;
                }
                else if (strcmp(token,"dec")==0)
                {
                                    //to be added
                }
                else //------WHAT IS ENCOUNTERED IS NOT AN INSTRUCTION BUT A LABEL. UPDATE THE LABEL TABLE--------
                {
                    labeltable[nooflabels].location = counter;  //buraya bir counter koy. error check
                    op1=(char*)malloc(sizeof(token));
                    strcpy(op1,token);
                    labeltable[nooflabels].label=op1;
                    nooflabels++;
                } 
                token = strtok(NULL,",\n\t\r ");  
            }
        }


//================================= SECOND PASS ==============================

                //supply the address fields of the jump and jz instructions from the 
        int i,j;         
        for (i=0; i<noofjumps;i++)                                                                   //for all jump/jz instructions
        {
            j=0;
            while ( strcmp(jumptable[i].label , labeltable[j].label) != 0 )             //if the label for this jump/jz does not match with the 
                j++;                                                                // jth label in the labeltable, check the next label..
            program[jumptable[i].location] +=(labeltable[j].location-jumptable[i].location-1)&0x0fff;       //copy the jump address into memory.
        }                                                     




                // search for the start of the .data segment
        rewind(fp);  
        while(fgets(line,sizeof line,fp)!= NULL)  //skip till .data, if no .data, also ok.
        {
            token=strtok(line,"\n\t\r ");
            if (strcmp(token,".data")==0 )
                break;

        }


                // process the .data segment and generate the variabletable[] array.
        int dataarea=0;
        while(fgets(line,sizeof line,fp)!= NULL)
        {
            token=strtok(line,"\n\t\r ");
            if (strcmp(token,".code")==0 )  //go till the .code segment
                break;
            else if (token[strlen(token)-1]==':')
            {               
                token[strlen(token)-1]='\0';  //will not cause memory leak, as we do not do malloc
                variabletable[noofvariables].location=counter+dataarea;
                op1=(char*)malloc(sizeof(token));
                strcpy(op1,token);
                variabletable[noofvariables].name=op1;
                token = strtok(NULL,",\n\t\r ");
                if (token==NULL)
                    program[counter+dataarea]=0;
                else if (strcmp(token, ".space")==0)
                {
                    token=strtok(NULL,"\n\t\r ");
                    dataarea+=atoi(token);
                }
                else if((token[0]=='0')&&(token[1]=='x')) 
                    program[counter+dataarea]=hex2int(token+2)&0xffff; 
                else if ((  (token[0])=='-') || ('0'<=(token[0])&&(token[0]<='9'))  )
                    program[counter+dataarea]=atoi(token)&0xffff;  
                noofvariables++;
                dataarea++;
            }
        }






// supply the address fields for the ldi instructions from the variable table
        for( i=0; i<noofldis;i++)
        {
            j=0;
            while ((j<noofvariables)&&( strcmp( lditable[i].name , variabletable[j].name)!=0 ))
                j++;
            if (j<noofvariables)
                program[lditable[i].location] = variabletable[j].location;              
        } 

// supply the address fields for the ldi instructions from the label table
        for( i=0; i<noofldis;i++)
        {
            j=0;
            while ((j<nooflabels)&&( strcmp( lditable[i].name , labeltable[j].label)!=0 ))
                j++;
            if (j<nooflabels){
                program[lditable[i].location] = (labeltable[j].location)&0x0fff;
                printf("%d %d %d\n", i, j, (labeltable[j].location));   
            }           
        } 

//display the resulting tables
        printf("LABEL TABLE\n");
        for (i=0;i<nooflabels;i++)
            printf("%d %s\n", labeltable[i].location, labeltable[i].label); 
        printf("\n");
        printf("JUMP TABLE\n");
        for (i=0;i<noofjumps;i++)
            printf("%d %s\n", jumptable[i].location, jumptable[i].label);   
        printf("\n");
        printf("VARIABLE TABLE\n");
        for (i=0;i<noofvariables;i++)
            printf("%d %s\n", variabletable[i].location, variabletable[i].name);    
        printf("\n");
        printf("LDI INSTRUCTIONS\n");
        for (i=0;i<noofldis;i++)
            printf("%d %s\n", lditable[i].location, lditable[i].name);  
        printf("\n");
        fclose(fp);
        fp = fopen("RAM","w");
        fprintf(fp,"v2.0 raw\n");
        for (i=0;i<counter+dataarea;i++)
            fprintf(fp,"%04x\n",program[i]);
    }   
}

2
分離されたデバイスは存在しません。クロスツールチェーンは、特に小さなアーキテクチャで非常に普及しています。
ラースヴィクランド16

3
「クロス」コンパイラ/アセンブラは、ターゲットとは異なるシステムで実行され、ターゲットシステムでの使用に適したアーティファクトを生成します。古代では、システム間のデータ交換は必ずしも必要ではありませんでしたが、システム自体をゼロからブートストラップする必要がありました。アーキテクチャのほとんどすべての最新の開発は、確立されたシステムで行われ、すべてをクロスコンパイルします。
ラースヴィクランド16

19
Cではなくマシンコードでアセンブラを記述しますか?あなたの教授はあなたに親切です。
ウィンストンユワート

2
可能な限り最高のプログラミング環境/言語ですべてのコードを記述しようとしないのはなぜですか?アセンブラも例外ではありません。
エリックエイド16

1
特定の方向に決まった「旅」はありません。
-whatsisname

回答:


17

人々はマシンコードでアセンブラを書いています。また、アセンブリ言語(多くの場合、自分で翻訳する言語のサブセット)で記述しているため、単純な「ブートストラップ」バージョンのアセンブラーから開始し、アセンブラー自体に必要な機能を追加します。

ただし、これは特に必要なものではありません。最終的に、アセンブラーは(通常はかなり)単純な翻訳プログラムです。1つの(テキスト)形式のファイルを取り込み、別の(通常はオブジェクトファイル形式)のファイルを書き出します。

入力されるテキストがテキスト形式の機械命令を表し、結果がバイナリ形式の同じ命令を表すという事実は、実際にはアセンブラーの実装に使用される言語とそれほど違いはありません。 SNOBOLとPythonは非常にうまく機能するので、私は最近(かなり)Pythonで書かれたアセンブラーで作業しました。

最初に物事をブートストラップする方法に関しては、通常、まともな開発ツールなどを備えた別のマシンで。新しいハードウェアを開発している場合、通常は新しいマシン用のシミュレーター(または少なくともエミュレーター)を作成することから始めるので、最初は何らかの場合に何らかのホストシステムでコードをビルドして実行します。


3
「SNOBOLやPythonなど、Cよりも高い言語でも非常にうまく機能します」-これは非常に良い点です。NASMについては、Cよりも高レベルなものを実際に検討したことはありませんでしたが、それは1995年であり、現在よりもパフォーマンスがはるかに重要であり、高レベル言語は現在よりもはるかに高度ではありませんでした。最近では、代替案を検討する価値があります。
ジュール

1
1980年代からSNOBOLという名前は聞いていません。
pacmaninbw

Haskellでコンパイラを1回書いた。怠evaluationな評価と関数の連鎖により、生成されたマシンコードのピープホールオプティマイザーを簡単に記述できました。
するThorbjörnRavnアンデルセン

10

存在しない接続が表示されています。

「アセンブラーの作成」は、他のプログラミングタスクと同様のプログラミングタスクです。そのタスクに最適なツールを使用して、そのタスクを処理します。アセンブラーの作成について特別なことはありません。高級言語で書かない理由はまったくありません。Cは実際にはかなり低レベルであり、おそらくC ++または他の高レベル言語を好むでしょう。

アセンブリ言語は、実際にはこのようなタスクにはまったく不適切です。アセンブリ言語を合理的に使用するケースは非常にまれです。より高いレベルの言語で表現できないことを行う必要がある場合のみ。


1
他の答えは非常に良いですが、特に最初の2つの文では、これが最もわかりやすいと思います。質問を読んだとき、私は自分自身に同じことを言っていました。
MetalMikester

アセンブリ言語を手動で記述することは、現在ではハードウェア固有のハッキングにのみ必要です。たとえば、一部のCPUで保護モードを設定するには特定の命令シーケンスが必要であり、論理的に同等のシーケンスでは不十分です。ほとんどすべての通常プログラムは、実行する必要のあるタスクに特定の命令シーケンスを必要としません。その結果、特定のシーケンスを必要とする理由はなく、論理的に同等の命令セットのみが必要です。最適化コンパイラは、実行パフォーマンス(命令数、ウォールクロック時間、コードキャッシュサイズ)を改善するためにまったく同じことを行います。
ミッコランタライネン

9

C言語がないのに、彼らは過去何をしていたのですか?彼らはマシンコードでアセンブラーを書いていましたか?

アセンブリは基本的にマシンコードのニーモニックです。機械語の各オペコードにはアセンブリニーモニックが割り当てられます。つまり、x86ではNOPは0x90です。これにより、アセンブラーはかなり単純になります(ほとんどのアセンブラーは2つのパスを持ちます。1つは翻訳用、もう1つはアドレス/参照を生成/解決します)。より良いバージョンが作成され、手作業の「アセンブル」アセンブラーでアセンブルされます。この方法で新しい機能が追加されます。この方法で新しい言語のコンパイラを構築できます。過去には、コンパイラがアセンブリを出力し、バックエンドにアセンブラを使用するのが一般的でした!

低レベル言語用の機械語翻訳を高レベル言語で書くのは意味がありません。... [既存のアセンブラ]はCで記述されています。また、なぜCで記述されているのでしょうか。

  • 一般に、より複雑なソフトウェアをより高いレベルの言語で記述する方が簡単です。
  • 一般に、高レベルの言語よりも低レベルの言語で実行していることを追跡するには、より多くのコードとより多くの精神的な努力が必要です。
    • Cの1行は、多くの命令に変換できます。C ++(またはC)での単純な割り当てでは、通常、少なくとも3つのアセンブリ命令(ロード、変更、ストア)が生成されます。言語(c ++やcなど)通常、問題を解決するために時間を費やし、マシンコードでソリューションを実装する方法を理解するために時間を費やす必要はありません。

一方でセルフホスティングは、プログラミング言語のための共通のマイルストーン/望ましい特徴である、アセンブリほとんどのプログラマは、より高いレベルで作業することを好むように低レベルです。すなわち、アセンブリでアセンブラーを記述したい人はいません

たとえば、そのアーキテクチャ用のCコンパイラさえない、まったく新しいマイクロプロセッサアーキテクチャを作成したとします。

ブートストラップは、新しいアーキテクチャでツールチェーンを取得するプロセスです。

基本的なプロセスは次のとおりです。

  • 新しいCPU(またはMCU)のコードを生成する方法を理解する新しいバックエンドを記述します
  • バックエンドのコンパイルとテスト
  • 新しいバックエンドを使用して、目的のコンパイラ(およびOSなど)をクロスコンパイルします。
  • これらのバイナリを新しいシステムに転送します

これを行うためにアセンブリ(新しいまたは古い)を記述する必要はありません。アセンブラー/バックエンド/コードジェネレーターを記述するのに最適な言語を選択する必要があります。

Cで記述されたアセンブラは、新しいアーキテクチャをシミュレートできますか?

アセンブラーはシミュレートしません!

新しい(または既存の)機械語で新しいCPUを開発している場合、通常、テストにはシミュレータが必要です。すなわち、シミュレータを介してランダムな命令とデータを実行し、プロトタイプCPU上の同じ命令とデータと出力を比較します。次に、バグを見つけ、バグを修正し、繰り返します。


3

アセンブラーをC(またはその他の高水準言語)で記述する理由には、その高水準言語で他のプログラムを記述することを正当化するために使用できるすべての理由があります。この場合の主なものは、おそらく移植性と使いやすさです。

移植性:ネイティブ言語でアセンブラーを記述する場合、そのプラットフォーム上にアセンブラーがあります。Cで記述する場合、Cコンパイラを備えた任意のプラットフォームにアセンブラがあります。これにより、たとえば、ワークステーションで組み込みプラットフォームのコードをコンパイルし、ターゲットデバイスで直接すべて実行する必要なく、バイナリを移動できます。

使いやすさ:ほとんどの人にとって、プログラムがアセンブラーや(より悪い)生のマシンコードよりも高レベルの言語である場合、プログラムを読んだり、推論したり、修正したりするのははるかに自然です。したがって、下位言語で担当している特徴点について考える必要はなく、上位言語で提供される抽象化の観点から考えることができるため、上位言語でアセンブラーを開発および保守するのは簡単です。


3

質問のこの部分のみに具体的に取り組む:

「ちなみに、GNU AssemblerとNetwide AssemblerはCで書かれていることを知っています。なぜそれらがCで書かれているのだろうと思っていますか?」

Netwide Assemblerを最初に書いたチームの一部として話すと、その決定は当時非常に明白だったので、基本的に他のオプションを考慮していませんでしたが、そうすれば同じ結論に達しました次の理由があります。

  • 低レベルの言語でそれを書くことはより困難で、はるかに時間がかかります。
  • より高いレベルの言語でそれを書くことはより速いかもしれませんが、パフォーマンスの考慮事項がありました(特に、コンパイラーのバックエンドとして使用されるアセンブラーは、できる限り、コンパイラーのスローダウンを防ぐために非常に高速である必要があります最終的には非常に大量のコードを処理することになりますが、これは私たちが特に許可したかったユースケースでした)、主要な作者が共通の高レベル言語を持っているとは思いませんそのような言語は当時かなり断片化されていました)。いくつかのメタプログラミングタスク(コードジェネレーターのバックエンドに便利な形式で命令のテーブルを生成する)にperlを使用しましたが、プログラム全体には実際には適していませんでした。
  • オペレーティングシステムの移植性が欲しかった
  • ハードウェアプラットフォームの移植性が必要でした(クロスコンパイラを作成するため)

これにより、決定が非常に簡単になりました。ANSI準拠のC(最近ではC89とも呼ばれています)が、これらのすべてのポイントに本当に当たった唯一の言語でした。当時標準化されたC ++ があったと考えていたかもしれませんが、当時は異なるシステム間のC ++サポートはかなりパッチが多かったため、移植性のあるC ++の作成は少し悪夢でした。


1

1つのことは、他のこととはまったく関係ありません。Webブラウザーは、HTML、PHP、またはその他のWebコンテンツ言語を使用して厳密に記述されていますか?いいえ、なぜですか?車は人間ではなく他の車でのみ運転できますか?

あるビットのブロブ(一部のASCII)を別のビットのブロブ(一部のマシンコード)に変換することは単なるプログラミングタスクであり、そのタスクに使用するプログラミング言語は何でもかまいません。多くの異なる言語で書かれたアセンブラがあります。

新しい言語は、コンパイラ/アセンブラがまだないため、最初は独自の言語で作成できません。新しい言語用の既存のコンパイラがない場合、他の言語で最初のコンパイラを作成する必要があり、ブートストラップに意味がある場合は最終的にブートストラップします。(htmlとWebブラウザー、いくつかのビットを取り込み、いくつかのビットを吐き出すプログラムは、決してhtmlで書かれることはありません)。

新しい言語である必要はなく、既存の言語でもかまいません。新しいCまたはC ++コンパイラは、すぐに自動的にコンパイルされません。

アセンブリ言語とCでは、ほとんどすべての新規または変更された命令セットの最初の2つの言語。私たちは過去ではなく、現在にいます。アセンブラは、Cやjava、pythonなど、必要な命令セットやアセンブリ言語に合わせて簡単に生成できます(まだ存在していなくても)。同様に、アセンブラがまだ存在していなくても、必要なアセンブリ言語のアセンブリ言語を出力できる多くのリターゲット可能なCコンパイラがあります。

それがまさに新しい命令セットで行うことです。新しい命令セットやアセンブラ用にコンパイルされていないCコンパイラを使用して、新しい命令セットで実行されていないコンピュータを用意し、クロスアセンブラとクロスコンパイラを作成します。ロジックを作成およびシミュレートしながら、それを開発して使用します。理想的にはすべてのツールとロジックの準備が整ったとみなされるまで、バグを見つけてバグを修正してテストするという通常の開発サイクルを繰り返します。また、ターゲットに応じて、たとえばオペレーティングシステムを実行できないマイクロコントローラーである場合、ネイティブの命令セットを使用してツールチェーンが生成および実行するようにブートストラップする理由はありません。常にクロスコンパイルします。ウェイバックマシン以外では、アセンブラーをアセンブラーで作成しても意味がありません。

はい、戻ったりふりをしたりできるなら、最初のアセンブラーは鉛筆と紙を持っている人間で、彼らにとって意味のある何かを書いてから、論理にとって意味のあるビットを隣に書きます。次に、スイッチまたは他の方法を使用してビットをマシンに取り込み(google pdp8またはpdp11またはaltair 8800)、何かをさせる。最初はコンピューターシミュレーターはありませんでした。ロジックを十分長く見つめたり、チップのいくつかの回転を回転させたりして、ロジックを正しく取得する必要がありました。今日のツールは十分に優れているので、A0の成功を得ることができます。これは、大きな抵抗以上のものであり、多くの場合、完全にシミュレートできなかったものに対してスピンが必要な場合がありますが、 3番目または4番目のスピンを待たずに最初のspi

予想されるウェイバックマシンでは、手作業で組み立てられたコードを取得し、それを使用してテープまたはカードからプログラムをロードします。また、マシンコードでアセンブラを手作業でコーディングします。完全なものではなく、プログラミングを少し簡単にするものです。次に、そのツールを使用して、より高度または複雑な言語(マクロアセンブラー)を処理できるツールを作成し、そのツールを使用してさらに複雑な言語を作成すると、FORTRANまたはBASICまたはBなどが作成されます。そして、同じ言語でのブートストラップについて考え始め、クロスコンパイラをネイティブコンパイラに書き換えます。もちろん、そのためには何らかの環境またはオペレーティングシステムが理想的に必要です。

シリコンを作成またはテストするとき、1と0の信号をじっと見つめる必要があります。ツールはデフォルトでバイナリまたは16進数を表示し、一部のツールではルックアップすることもできます。これにより、ツールはニーモニック(おそらくアセンブリ)を表示しますが、多くの場合、エンジニア(シリコン/ハードウェアおよびソフトウェア)は機械コード、または分解/リストを使用して指示を「参照」します。

何をしているのかに応じて、テストを書き直して再コンパイルまたは再組み立てするのではなく、マシンコードをテストベクトルに押し込むだけです。たとえば、パイプラインがあり、ある程度の深さでプリフェッチする場合、パイプが未定義の命令を吐かないように、プログラムの終わりを越えていくつかのnopsまたは他の実際の命令を埋める必要がある場合があります。コンパイラー、アセンブラー、またはリンカーに取得を試みるのではなく、最下位レベルのリスト/ファイルでマシンコードを入力します。

もちろん、プロセッサのテスト中は、未定義のビットやおそらく気にしないビットなどに対処する必要があります。そのため、通常動作するプログラムの命令の1つ以上の特定のビットをマシンコードに変更する必要があります。これを行うプログラムを書く価値があるのか​​、それとも手作業で行う価値があるのか​​。同様に、ECCをテストする場合、1つ以上のビットを反転させて、それらが修正またはトラップされることを確認します。確かに、実行するプログラムを作成する方がはるかに簡単であるか、手動で実行できます。

それからもちろん、プロセッサ、初期のパスカル、java、pythonなどで実行されるコードを生成しない言語があります。これらの言語を使用するには、他の言語で書かれたVMが必要です。Javaコンパイラを使用してjava vmを作成することはできません。言語の設計に基づいては意味がありません。

(はい、これらの言語の純粋な実装の後、最終的に誰かがvm命令セットではなく実際の命令セットをターゲットとする不純なバックエンドを構築した後、その場合、言語を使用して自分自身またはそのvmを実際にコンパイルできますたとえば、gccのgnu javaフロントエンド)。

時間が経つにつれて、おそらくCでCコンパイラを書くことはおそらくないでしょう。私たちは、自分で書きたくないCを生成するために使用する他のプログラミング言語bison / flexなどを使用します。一部のパーセンテージは確かですが、一部のパーセンテージは、ビットを入力し、他のビットを出力する他のコンパイラーを使用する他の言語です。このアプローチは、アセンブラの生成にも使用される場合があります。コンパイラ/アセンブラ(ビットを入力してから他のビットを出力するタスクを持つプログラム)の設計者が、それをどのように実装するかについて。プログラムで生成されたパーサーは確実に手動でプログラムできますが、時間がかかるため、ユーザーはショートカットを探します。アセンブラーでアセンブラーを作成できるのと同じように、人々はショートカットを探します。

Webブラウザは、いくつかのビットを取り込み、他のビットを吐き出す単なるプログラムです。アセンブラは、いくつかのビットを取り込んで、他のビットを吐き出す単なるプログラムです。コンパイラは、いくつかのビットを取り込み、他のビットを吐き出す単なるプログラムです。これらすべてについて、各プログラミングタスクの入力ビットと出力ビットに関する文書化された一連のルールがあります。これらのタスクとビットは、任意のAVAILABLEプログラミング言語を使用できるほど十分に汎用的です(ビット/バイト操作を行い、入力と出力を処理できます)。ここのキーは利用可能です。Linuxをゼロから入手して試してみてください。シミュレートされたフロントパネルを備えたpdp8またはpdp11またはaltair 8800またはその他のシミュレーターを試してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.