"Tulis Assembler di C." Mengapa menulis penerjemah kode mesin untuk bahasa tingkat rendah dalam bahasa tingkat yang lebih tinggi?


13

Instruktur kelas Microprocessor saya memberi kami tugas dan berkata:

"Tulis Assembler di C." - Profesor terkasihku

Jadi rasanya agak tidak masuk akal bagi saya.

Jika saya tidak salah, Majelis Bahasa adalah langkah pertama dari Kode Mesin ke perjalanan bahasa tingkat yang lebih tinggi. Maksud saya C adalah bahasa tingkat yang lebih tinggi daripada Assembly. Jadi apa gunanya menulis Assembler di C? Apa yang mereka lakukan di masa lalu sementara tidak ada bahasa C? Apakah mereka menulis Assembler dalam Kode Mesin?

Tidak masuk akal bagi saya menulis penerjemah kode mesin untuk bahasa tingkat rendah dalam bahasa tingkat yang lebih tinggi.

Katakanlah kita telah membuat arsitektur mikroprosesor baru yang bahkan tidak ada kompiler C untuk arsitektur itu. Apakah Assembler kami yang ditulis dalam C dapat mensimulasikan arsitektur baru? Maksud saya apakah itu tidak berguna atau tidak?

Ngomong-ngomong, saya sadar bahwa GNU Assembler dan Netwide Assembler telah ditulis dalam C. Saya juga bertanya-tanya mengapa mereka ditulis dalam C?

Terakhir ini adalah contoh kode sumber untuk assembler sederhana yang diberikan oleh Profesor kami kepada kami:

// 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
Tidak ada perangkat yang terisolasi. Cross toolchains sangat lazim, terutama untuk arsitektur kecil.
Lars Viklund

3
Compiler / assembler "lintas" berjalan pada sistem yang berbeda dari target, dan menghasilkan artefak yang cocok untuk digunakan pada sistem target. Pada zaman kuno, Anda tidak perlu memiliki pertukaran data antar sistem, tetapi harus melakukan bootstrap sistem dari awal dengan sendirinya. Hampir semua pengembangan modern untuk arsitektur dilakukan pada sistem yang mapan, mengkompilasi semuanya.
Lars Viklund

19
Apakah Anda ingin menulis assembler dalam kode mesin, bukan C? Profesor Anda bersikap baik kepada Anda.
Winston Ewert

2
Mengapa Anda tidak berusaha untuk menulis semua kode Anda dalam lingkungan / bahasa pemrograman terbaik? Assembler tidak terkecuali.
Erik Eidt

1
Tidak ada "perjalanan" yang pasti ke arah tertentu.
whatsisname

Jawaban:


17

Orang-orang memiliki assembler tertulis dalam kode mesin. Mereka juga telah menulis kemudian dalam bahasa assembly - sering kali merupakan subset dari bahasa yang mereka terjemahkan sendiri, jadi mereka mulai dengan versi sederhana "bootstrap" dari assembler, kemudian menambahkan fitur padanya ketika mereka membutuhkannya untuk assembler itu sendiri.

Namun, tidak satu pun dari ini yang merupakan keharusan. Pada akhirnya, assembler adalah program terjemahan sederhana (biasanya cukup). Ini mengambil file dalam satu format (teks), dan menulis file dalam format lain (biasanya format file objek).

Fakta bahwa teks yang diinput mewakili instruksi mesin dalam format tekstual dan hasilnya mewakili instruksi yang sama dalam format biner tidak membuat banyak perbedaan pada bahasa yang digunakan untuk mengimplementasikan assembler - bahkan, bahasa yang bahkan lebih tinggi daripada C seperti karena SNOBOL dan Python dapat bekerja dengan cukup baik - Saya (cukup) baru-baru ini bekerja pada assembler yang ditulis dengan Python, dan itu bekerja cukup baik untuk pekerjaan itu.

Sejauh bagaimana Anda melakukan bootstrap pada awalnya: biasanya pada komputer lain yang memiliki alat pengembangan yang layak dan semacamnya. Jika Anda mengembangkan perangkat keras baru, Anda biasanya memulai dengan menulis simulator (atau setidaknya emulator) untuk mesin baru, jadi pada awalnya Anda membangun dan menjalankan kode pada beberapa sistem host.


3
"bahasa yang bahkan lebih tinggi daripada C seperti SNOBOL dan Python dapat bekerja dengan cukup baik" - ini adalah poin yang sangat bagus. Untuk NASM, kami tidak pernah benar-benar menganggap sesuatu yang lebih tinggi daripada C, tetapi itu adalah 1995 ketika kinerja jauh lebih penting daripada sekarang dan bahasa tingkat tinggi jauh lebih maju daripada sekarang. Saat ini, ada baiknya mempertimbangkan alternatif.
Jules

1
Saya belum pernah mendengar nama SNOBOL sejak 1980-an.
pacmaninbw

Saya pernah menulis kompiler di Haskell. Evaluasi malas dan rangkaian fungsi membuatnya mudah untuk menulis pengoptimal lubang intip untuk kode mesin yang dihasilkan.
Thorbjørn Ravn Andersen

10

Anda melihat koneksi yang tidak ada.

"Menulis assembler" adalah tugas pemrograman sama seperti tugas pemrograman lainnya. Anda menggunakan alat untuk menangani tugas itu yang terbaik untuk tugas itu. Tidak ada yang istimewa tentang menulis assembler; tidak ada alasan sama sekali untuk tidak menulisnya dalam bahasa tingkat tinggi. C sebenarnya pada level yang cukup rendah, dan saya mungkin lebih suka C ++ atau bahasa tingkat tinggi lainnya.

Bahasa assembly sebenarnya sama sekali tidak cocok untuk tugas seperti ini. Kasus-kasus di mana Anda cukup menggunakan bahasa assembly sangat, sangat jarang. Hanya ketika Anda perlu melakukan hal-hal yang tidak dapat diungkapkan dalam bahasa tingkat yang lebih tinggi.


1
Jawaban lainnya sangat baik, tetapi saya menemukan ini adalah yang paling mudah, terutama dengan dua kalimat pertama. Saya mengatakan pada diri saya hal yang sama ketika membaca pertanyaan.
MetalMikester

Menulis bahasa rakitan secara manual saat ini hanya diperlukan untuk peretasan perangkat keras tertentu. Misalnya, pengaturan mode terproteksi pada beberapa CPU memerlukan urutan instruksi khusus dan urutan yang setara secara logis tidak cukup baik. Hampir semua program normal tidak memerlukan urutan instruksi khusus untuk tugas yang harus mereka lakukan dan sebagai akibatnya tidak ada alasan untuk meminta urutan tertentu tetapi hanya beberapa set instruksi yang setara secara logis. Mengoptimalkan kompiler melakukan hal yang persis sama untuk meningkatkan kinerja eksekusi (jumlah instruksi, waktu jam dinding, ukuran cache kode).
Mikko Rantalainen

9

Apa yang mereka lakukan di masa lalu sementara tidak ada bahasa C? Apakah mereka menulis Assembler dalam Kode Mesin?

Assembly pada dasarnya adalah mnemonic untuk kode mesin; setiap opcode dalam bahasa mesin diberikan mnemonic rakitan yaitu dalam x86 NOP adalah 0x90. Ini membuat assembler agak sederhana (nb kebanyakan assembler memiliki dua lintasan, satu untuk menerjemahkan, dan yang kedua untuk menghasilkan / menyelesaikan alamat / referensi.) Assembler pertama ditulis dan diterjemahkan dengan tangan (kemungkinan di atas kertas) ke dalam kode mesin. Versi yang lebih baik ditulis dan dirakit dengan assembler 'assembled' tangan, fitur baru ditambahkan dengan cara ini. Kompiler untuk bahasa baru dapat dibangun dengan cara ini; di masa lalu itu umum untuk kompiler untuk keluaran perakitan, dan menggunakan assembler untuk ujung belakang mereka!

Tidak masuk akal bagi saya menulis penerjemah kode mesin untuk bahasa tingkat rendah dalam bahasa tingkat yang lebih tinggi. ... [perakit yang ada] telah ditulis dalam C. Saya juga bertanya-tanya mengapa mereka ditulis dalam C?

  • Pada umumnya lebih mudah untuk menulis perangkat lunak yang lebih rumit dalam bahasa tingkat yang lebih tinggi.
  • Biasanya diperlukan lebih banyak kode, dan lebih banyak upaya mental untuk melacak apa yang Anda lakukan dalam bahasa tingkat yang lebih rendah daripada yang lebih tinggi.
    • Satu baris C dapat diterjemahkan ke dalam banyak instruksi, mis. tugas sederhana dalam C ++ (atau C) biasanya menghasilkan setidaknya 3 instruksi perakitan (memuat, memodifikasi, menyimpan;) dapat mengambil dua puluh instruksi atau lebih (mungkin ratusan,) untuk melakukan apa yang dapat dilakukan dengan satu baris di level yang lebih tinggi bahasa (seperti c ++ atau c.) Orang umumnya ingin menghabiskan waktu mereka untuk memecahkan masalah, dan tidak menghabiskan waktu mencari tahu bagaimana menerapkan solusi dalam kode mesin.

Sementara hosting sendiri adalah tonggak sejarah / fitur yang diinginkan untuk bahasa pemrograman, perakitan adalah level yang sangat rendah sehingga kebanyakan programmer lebih suka bekerja di level yang lebih tinggi. Yaitu tidak ada yang ingin menulis assembler di majelis (atau apa pun yang lain benar-benar)

Katakanlah kita telah membuat arsitektur mikroprosesor baru yang bahkan tidak ada kompiler C untuk arsitektur itu.

Bootstrapping adalah proses mendapatkan rantai alat pada arsitektur baru.

proses dasarnya adalah:

  • tulis ujung belakang baru yang mengerti cara menghasilkan kode untuk CPU baru Anda (atau MCU)
  • kompilasi dan uji back end Anda
  • kompilasi silang kompiler yang Anda inginkan (dan os, dll.) menggunakan back-end baru Anda
  • mentransfer binari ini ke sistem baru

Tidak sekali pun Anda perlu menulis dalam perakitan (baru atau lama) untuk melakukan ini, orang harus memilih bahasa terbaik untuk menulis assembler / back-end / generator kode Anda.

Apakah Assembler kami yang ditulis dalam C dapat mensimulasikan arsitektur baru?

Perakit tidak mensimulasikan!

Jika seseorang mengembangkan CPU baru dengan bahasa mesin baru (atau yang sudah ada), simulator biasanya diperlukan untuk pengujian; yaitu menjalankan instruksi dan data acak melalui simulator, dan membandingkan output dengan instruksi dan data yang sama pada prototipe CPU Anda. Kemudian temukan bug, perbaiki bug, ulangi.


3

Di antara alasan untuk menulis assembler dalam C (atau bahasa tingkat tinggi lainnya) adalah semua alasan yang dapat Anda gunakan untuk membenarkan penulisan program lain dalam bahasa tingkat yang lebih tinggi itu. Kepala di antara mereka dalam hal ini mungkin portabilitas dan kegunaan.

Portabilitas: Jika Anda menulis assembler Anda dalam bahasa asli Anda memiliki assembler pada platform itu. Jika Anda menulisnya di C, Anda memiliki assembler di platform apa pun dengan kompiler C. Ini memungkinkan Anda, misalnya, mengkompilasi kode untuk platform tertanam Anda di workstation Anda dan memindahkan biner daripada perlu melakukan semuanya langsung pada perangkat target.

Kegunaan: Bagi kebanyakan orang, membaca, menalar tentang, dan memodifikasi program jauh lebih alami ketika program itu dalam bahasa tingkat yang lebih tinggi daripada ketika itu di assembler atau (lebih buruk) kode mesin mentah. Oleh karena itu, lebih mudah untuk mengembangkan dan memelihara assembler dalam bahasa tingkat yang lebih tinggi karena Anda dapat berpikir dalam hal abstraksi yang diberikan kepada Anda oleh bahasa tingkat yang lebih tinggi daripada harus memikirkan hal-hal kecil yang menjadi tanggung jawab Anda dalam hal-hal yang lebih rendah.


3

Mengatasi secara khusus bagian dari pertanyaan ini saja:

"Ngomong-ngomong aku sadar bahwa GNU Assembler dan Netwide Assembler telah ditulis dalam C. Aku juga bertanya-tanya mengapa mereka ditulis dalam C?"

Berbicara sebagai bagian dari tim yang awalnya menulis Netwide Assembler, keputusan itu tampak begitu jelas bagi kami pada saat itu sehingga kami pada dasarnya tidak mempertimbangkan opsi lain, tetapi seandainya kami melakukannya, kami akan sampai pada kesimpulan yang sama, berdasarkan pada alasan berikut:

  • Menulisnya dalam bahasa tingkat yang lebih rendah akan lebih sulit dan lebih memakan waktu.
  • Menulisnya dalam bahasa tingkat yang lebih tinggi mungkin lebih cepat, tetapi ada pertimbangan kinerja (assembler yang digunakan sebagai ujung belakang ke kompiler, terutama, harus sangat cepat untuk mencegah memperlambat kompiler turun terlalu banyak, karena dapat akhirnya menangani jumlah kode yang sangat besar, dan ini adalah kasus penggunaan yang secara khusus ingin kami izinkan) dan saya tidak percaya para penulis utama memiliki kesamaan bahasa tingkat yang lebih tinggi (ini sebelum Jawa menjadi populer, sehingga dunia bahasa seperti itu agak terfragmentasi saat itu). Kami memang menggunakan perl untuk beberapa tugas pemrograman metap (menghasilkan tabel instruksi dalam format yang berguna untuk pembuat kode bagian belakang), tetapi itu tidak benar-benar cocok untuk keseluruhan program.
  • Kami menginginkan portabilitas sistem operasi
  • Kami menginginkan portabilitas platform perangkat keras (untuk menghasilkan cross-compiler)

Ini membuat keputusan cukup mudah: C-compliant ANSI (alias C89 hari ini) adalah satu-satunya bahasa pada saat itu yang benar-benar menyentuh semua poin tersebut. Seandainya ada C ++ terstandarisasi saat itu kita mungkin telah mempertimbangkan itu, tetapi dukungan C ++ antara sistem yang berbeda agak tambal saat itu, jadi menulis portable C ++ sedikit mimpi buruk.


1

Satu hal sama sekali tidak ada hubungannya dengan yang lain. Apakah browser web hanya ditulis menggunakan html atau php atau bahasa konten web lainnya? Tidak, mengapa mereka melakukannya? Bisakah mobil hanya dikendarai oleh mobil lain dan bukan oleh manusia?

Mengubah satu gumpalan bit (beberapa ascii) ke gumpalan bit lainnya (beberapa kode mesin) hanyalah tugas pemrograman, bahasa pemrograman yang Anda gunakan untuk tugas itu adalah apa pun yang Anda inginkan. Anda dapat dan telah ada assembler yang ditulis dalam berbagai bahasa.

Bahasa baru tidak dapat ditulis dalam bahasa mereka sendiri pada awalnya karena belum ada kompiler / assembler untuk mereka. Jika tidak ada kompiler yang ada untuk bahasa baru Anda harus menulis yang pertama dalam beberapa bahasa lain dan kemudian Anda akhirnya bootstrap jika itu masuk akal untuk bootstrap. (html dan browser web, sebuah program yang mengambil beberapa bit dan meludahkan beberapa bit tidak akan pernah ditulis dalam html, tidak bisa).

Tidak harus bahasa baru, bisa bahasa yang sudah ada. Kompiler C atau C ++ baru tidak secara otomatis mengkompilasi sendiri keluar dari gerbang.

Untuk bahasa assembly dan C, dua bahasa pertama untuk hampir semua set instruksi baru atau yang dimodifikasi. Kita tidak berada di masa lalu, kita ada di masa sekarang. Kita dapat dengan mudah menghasilkan assembler dalam C atau java atau python atau apa pun untuk set instruksi dan bahasa assembly yang kita inginkan bahkan jika itu belum ada. Demikian juga ada banyak kompiler C yang dapat di-retargable sehingga kita bisa menampilkan bahasa assembly untuk bahasa assembly apa pun yang kita inginkan walaupun assembler belum ada.

Itulah tepatnya yang kami lakukan dengan set instruksi baru. Ambil beberapa komputer yang tidak berjalan pada set instruksi baru kami dengan kompiler C-nya yang dikompilasi bukan untuk set instruksi baru kami atau assembler-nya, buat cross assembler dan cross compiler. Kembangkan dan gunakan itu sambil membuat dan mensimulasikan logika. Ikuti siklus pengembangan normal untuk menemukan bug dan perbaiki bug dan uji lagi, sampai idealnya semua alat dan logika dianggap siap. Dan tergantung pada target, katakan itu adalah mikrokontroler yang tidak mampu menjalankan sistem operasi, Anda tidak akan pernah memiliki alasan untuk bootstrap sehingga toolchain menghasilkan dan berjalan menggunakan set instruksi asli. Anda akan selalu melakukan kompilasi silang. Selain di mesin wayback tidak pernah masuk akal untuk menulis assembler di assembler.

Ya jika Anda bisa kembali atau berpura-pura kembali, assembler pertama adalah manusia dengan pensil dan kertas, yang menulis sesuatu yang masuk akal bagi mereka dan kemudian menulis bit di sebelahnya yang masuk akal dengan logika. Kemudian gunakan switch atau cara lain untuk memasukkan bit ke dalam mesin (google pdp8 atau pdp11 atau altair 8800) dan membuatnya melakukan sesuatu. Tidak ada simulator komputer pada awalnya Anda hanya perlu mendapatkan logika dengan menatapnya cukup lama atau juga memutar beberapa putaran chip. Alat-alatnya cukup bagus hari ini sehingga Anda bisa mendapatkan kesuksesan A0 karena masalahnya lebih dari sekadar resistor besar, banyak yang berfungsi Anda mungkin masih memerlukan putaran untuk hal-hal yang tidak dapat Anda simulasikan sepenuhnya, tetapi Anda sering dapat mem-boot sekarang di spi pertama tanpa harus menunggu putaran ketiga atau keempat,

Di mesin wayback Anda seperti yang diharapkan, Anda kemudian mengambil kode rakitan tangan Anda dan Anda menggunakannya untuk mengatakan memuat program dari tape atau kartu. Anda juga memberi kode assembler dalam kode mesin, mungkin bukan kode yang lengkap tetapi yang membuat pemrograman sedikit lebih mudah. Kemudian alat itu digunakan untuk membuat yang dapat menangani langauge yang lebih maju atau rumit (assembler makro), dan yang itu untuk membuatnya lebih rumit dan Anda berakhir dengan FORTRAN atau BASIC atau B atau apa pun. Dan kemudian Anda mulai berpikir tentang bootstrap dalam bahasa yang sama, menulis ulang kompiler silang menjadi kompiler asli. tentu saja Anda idealnya memerlukan lingkungan atau sistem operasi semacam itu.

Ketika kita membuat atau menguji silikon kita bisa / memang perlu menatap sinyal yang satu dan nol. Alat-alat akan menunjukkan kepada kita biner atau hex secara default dan dimungkinkan dengan beberapa alat untuk melihat bahkan sehingga alat-alat akan menampilkan beberapa mnemonik (perakitan mungkin) tetapi seringkali para insinyur (silikon / perangkat keras dan perangkat lunak) dapat membaca dengan cukup baik. kode mesin, atau gunakan pembongkaran / daftar untuk "melihat" instruksi.

Bergantung pada apa yang Anda lakukan, Anda mungkin hanya memasukkan beberapa kode mesin ke dalam vektor uji alih-alih menulis ulang dan menyusun ulang atau merakit ulang pengujian. Sebagai contoh jika Anda memiliki pipa dan membuat prefetch pada kedalaman Anda mungkin perlu atau ingin mengisi melewati akhir program beberapa jumlah nops atau instruksi nyata lainnya sehingga pipa tidak muntah pada instruksi yang tidak ditentukan, dan Anda mungkin hanya memilih untuk hanya isi kode mesin di daftar / file level terendah daripada mencoba membuat kompiler atau assembler atau linker melakukannya.

Saat menguji prosesor tentu saja Anda harus berurusan dengan yang tidak terdefinisi dan mungkin tidak peduli bit, dll. Jadi Anda perlu masuk ke kode mesin dan memodifikasi satu atau lebih bit spesifik dalam instruksi dalam program yang biasanya berfungsi. Apakah layak menulis program untuk melakukan ini atau hanya melakukannya dengan tangan. Demikian juga saat menguji ECC Anda ingin membalik satu atau lebih bit dan melihat bahwa mereka terkoreksi atau terjebak. Memang itu jauh lebih mudah untuk menulis sebuah program untuk dilakukan atau Anda bisa melakukannya dengan tangan.

Kemudian tentu saja ada bahasa yang tidak menghasilkan kode yang berjalan pada prosesor, pascal awal, java, python, dll. Anda memerlukan VM yang ditulis dalam beberapa bahasa lain hanya untuk menggunakan bahasa-bahasa tersebut. Anda tidak dapat menggunakan kompiler java Anda untuk membuat java vm, tidak masuk akal berdasarkan desain bahasa.

(ya tentu setelah implementasi murni dari bahasa-bahasa ini pada akhirnya seseorang membangun backend tidak murni yang kadang-kadang dapat menargetkan set instruksi nyata bukan set instruksi vm dan kemudian dalam hal itu Anda dapat menggunakan bahasa untuk mengkompilasi sendiri atau vm jika Anda benar-benar merasakan butuhkan. Sebuah gnu java front end ke gcc misalnya).

Seiring waktu dan mungkin masih kita tidak menulis kompiler C di C. Kami menggunakan hal-hal seperti bison / melenturkan beberapa bahasa pemrograman lain yang kami gunakan untuk menghasilkan C untuk kami yang kami tidak ingin menulis sendiri. Beberapa persentase dalam C pasti, tetapi beberapa persentase dalam beberapa bahasa lain yang menggunakan beberapa kompiler lain yang menginput bit dan output bit lainnya. Terkadang pendekatan ini juga digunakan untuk menghasilkan assembler. Hingga perancang kompiler / assembler (program yang memiliki tugas untuk memasukkan bit dan kemudian output bit lainnya) tentang bagaimana mereka akan mengimplementasikannya. Program yang dihasilkan parser dapat diprogram dengan tangan, hanya memakan waktu lama sehingga orang mencari jalan pintas. Sama seperti Anda bisa menulis assembler di assembler tetapi orang mencari jalan pintas.

Browser web hanyalah sebuah program yang mengambil beberapa bit dan meludahkan beberapa bit lainnya. Assembler hanyalah sebuah program yang mengambil beberapa bit dan mengeluarkan beberapa bit lainnya. Compiler hanyalah sebuah program yang mengambil beberapa bit dan mengeluarkan beberapa bit lainnya. Dll. Untuk semua ini ada seperangkat aturan yang terdokumentasi untuk bit input dan bit output untuk setiap tugas pemrograman. Tugas dan bit ini cukup umum sehingga setiap bahasa pemrograman TERSEDIA dapat digunakan (yang mampu melakukan manipulasi bit / byte dan berurusan dengan input dan output). Kuncinya di sini tersedia. Dapatkan dan coba linux dari buku awal / tutorial. Coba pdp8 atau pdp11 atau altair 8800 atau simulator lain dengan simulasi panel depan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.