“ เขียนแอสเซมเบลอร์เป็น C” ทำไมต้องเขียนโปรแกรมแปลรหัสเครื่องสำหรับภาษาระดับต่ำในภาษาระดับสูงกว่า


13

ผู้สอนคลาสไมโครโปรเซสเซอร์ของฉันให้การบ้านและบอกว่า:

"เขียนแอสเซมเบลอร์เป็น C" - ศาสตราจารย์ที่รักของฉัน

ดังนั้นมันดูไร้เหตุผลสำหรับฉันเล็กน้อย

ถ้าฉันไม่ผิดภาษาแอสเซมบลีเป็นขั้นตอนแรกจากรหัสเครื่องไปยังการเดินทางของภาษาระดับสูงกว่า ฉันหมายถึง C เป็นภาษาระดับสูงกว่าการชุมนุม แล้วประเด็นของการเขียน Assembler ใน C คืออะไร? สิ่งที่พวกเขาทำในอดีตในขณะที่ไม่มีภาษา C? พวกเขากำลังเขียน Assembler ในรหัสเครื่องหรือไม่?

ฉันไม่ควรเขียนตัวแปลรหัสเครื่องสำหรับภาษาระดับต่ำในภาษาระดับสูงกว่านี้

สมมติว่าเราได้สร้างสถาปัตยกรรมไมโครโปรเซสเซอร์ใหม่ที่ไม่มีแม้แต่คอมไพเลอร์ C สำหรับสถาปัตยกรรมนั้น ผู้ประกอบของเราที่เขียนใน C จะสามารถจำลองสถาปัตยกรรมใหม่ได้หรือไม่? ฉันหมายความว่ามันจะไร้ประโยชน์หรือไม่?

ฉันรู้ได้อย่างไรว่า GNU Assembler และ Netwide Assembler เขียนด้วยภาษาซีฉันยังสงสัยว่าทำไมพวกเขาถึงเขียนเป็น 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
ไม่มีอุปกรณ์แยก Cross toolchains นั้นแพร่หลายมากโดยเฉพาะกับสถาปัตยกรรมขนาดเล็ก
Lars Viklund

3
คอมไพเลอร์ / แอสเซมเบลอร์ "cross" ทำงานบนระบบที่แตกต่างจากเป้าหมายและสร้างสิ่งประดิษฐ์ที่เหมาะสมสำหรับการใช้งานบนระบบเป้าหมาย ในสมัยโบราณคุณไม่จำเป็นต้องมีการแลกเปลี่ยนข้อมูลระหว่างระบบ แต่จำเป็นต้องบูตระบบตั้งแต่เริ่มต้นในแง่ของตัวเอง การพัฒนาสถาปัตยกรรมที่ทันสมัยเกือบทั้งหมดนั้นทำในระบบที่กำหนดไว้แล้ว
Lars Viklund

19
คุณต้องการเขียนแอสเซมเบลอร์ในรหัสเครื่องแทน C หรือไม่ อาจารย์ของคุณใจดีกับคุณมาก
Winston Ewert

2
ทำไมคุณไม่ลองเขียนโค้ดทั้งหมดในสภาพแวดล้อมการเขียนโปรแกรม / ภาษาที่ดีที่สุด? แอสเซมเบลอร์จะไม่มีข้อยกเว้น
Erik Eidt

1
ไม่มี "การเดินทาง" ที่แน่นอนในทิศทางใดโดยเฉพาะ
whatsisname

คำตอบ:


18

ผู้คนได้เขียนแอสเซมเบลอร์ในรหัสเครื่อง พวกเขายังเขียนด้วยภาษาแอสเซมบลี - บ่อยครั้งเป็นส่วนย่อยของภาษาที่พวกเขาแปลเองดังนั้นพวกเขาจึงเริ่มต้นด้วยแอสเซมเบลอร์เวอร์ชั่น "bootstrap" ที่เรียบง่ายจากนั้นเพิ่มคุณสมบัติลงไปตามที่ต้องการสำหรับแอสเซมเบลอร์

อย่างไรก็ตามสิ่งนี้ไม่จำเป็นโดยเฉพาะ ในที่สุดแอสเซมเบลอร์เป็นโปรแกรมแปลภาษาแบบง่าย ๆ มันใช้เวลาในไฟล์ในรูปแบบหนึ่ง (ข้อความ) และเขียนไฟล์ในอีกรูปแบบหนึ่ง (มักจะเป็นรูปแบบไฟล์วัตถุ)

ความจริงที่ว่าข้อความที่ป้อนเข้านั้นแสดงให้เห็นถึงคำแนะนำของเครื่องในรูปแบบข้อความและผลลัพธ์แสดงให้เห็นถึงคำแนะนำเดียวกันในรูปแบบไบนารีไม่ได้สร้างความแตกต่างอย่างมากกับภาษาที่ใช้ในการใช้งานแอสเซมเบลอร์ เนื่องจาก SNOBOL และ Python สามารถทำงานได้ค่อนข้างดี - ฉัน (ค่อนข้างดี) เพิ่งทำงานกับแอสเซมเบลอร์ที่เขียนด้วย Python และมันทำงานได้ค่อนข้างดีสำหรับงานนี้

เท่าที่คุณบู๊ตสิ่งแรกเริ่ม: โดยทั่วไปแล้วในเครื่องอื่นที่มีเครื่องมือในการพัฒนาที่เหมาะสมและเช่นนั้น หากคุณกำลังพัฒนาฮาร์ดแวร์ใหม่คุณมักจะเริ่มต้นด้วยการเขียนโปรแกรมจำลอง (หรืออย่างน้อย emulator) สำหรับเครื่องใหม่อย่างไรก็ตามในตอนแรกคุณกำลังสร้างและเรียกใช้รหัสบนระบบโฮสต์บางระบบในทุกกรณี


3
"ภาษาที่สูงกว่า C เช่น SNOBOL และ Python สามารถใช้งานได้ดี" - นี่เป็นจุดที่ดีมาก สำหรับ NASM เราไม่เคยพิจารณาสิ่งใดในระดับที่สูงกว่า C จริง ๆ แต่นั่นก็คือปี 1995 เมื่อการแสดงมีความสำคัญมากกว่าในทุกวันนี้และภาษาระดับสูงมีความก้าวหน้าน้อยกว่าในทุกวันนี้ วันนี้มันคุ้มค่าที่จะพิจารณาทางเลือก
จูลส์

1
ฉันไม่ได้ยินชื่อ SNOBOL มาตั้งแต่ปี 1980
pacmaninbw

ฉันเขียนคอมไพเลอร์ใน Haskell หนึ่งครั้ง การประเมินผลแบบไม่ต่อเนื่องและการผูกมัดของฟังก์ชันทำให้ง่ายขึ้นเล็กน้อยในการเขียนเครื่องมือเพิ่มประสิทธิภาพช่องมองสำหรับรหัสเครื่องที่สร้างขึ้น
Thorbjørn Ravn Andersen

11

คุณเห็นการเชื่อมต่อที่ไม่มีอยู่

"เขียนแอสเซมเบลอร์" เป็นงานการเขียนโปรแกรมเหมือนกับงานเขียนโปรแกรมอื่น ๆ คุณใช้เครื่องมือเพื่อจัดการงานที่ดีที่สุดสำหรับงานนั้น ไม่มีอะไรพิเศษเกี่ยวกับการเขียนแอสเซมเบลอร์; ไม่มีเหตุผลเลยที่จะไม่เขียนเป็นภาษาระดับสูง จริง ๆ แล้ว C อยู่ในระดับค่อนข้างต่ำและฉันอาจจะชอบ C ++ หรือภาษาระดับสูงกว่าอื่น ๆ

ภาษาแอสเซมบลีไม่เหมาะสมอย่างยิ่งสำหรับงานแบบนี้ กรณีที่คุณควรใช้ภาษาแอสเซมบลีอย่างสมเหตุสมผลมีน้อยมาก เมื่อคุณจำเป็นต้องทำสิ่งต่าง ๆ ที่ไม่สามารถแสดงออกเป็นภาษาระดับสูงได้


1
คำตอบอื่น ๆ นั้นดีมาก แต่ฉันพบว่าคำนี้ตรงไปตรงมาที่สุดโดยเฉพาะกับสองประโยคแรก ฉันกำลังบอกตัวเองในสิ่งเดียวกันเมื่ออ่านคำถาม
MetalMikester

ปัจจุบันการเขียนภาษาแอสเซมบลีด้วยตนเองเป็นสิ่งจำเป็นสำหรับแฮ็กเฉพาะฮาร์ดแวร์เท่านั้น ตัวอย่างเช่นการตั้งค่าโหมดที่ได้รับการป้องกันใน CPU บางตัวจำเป็นต้องมีลำดับการเรียนการสอนที่เฉพาะเจาะจงและลำดับที่เทียบเท่าในเชิงตรรกะนั้นไม่ดีพอ โปรแกรมปกติทุกโปรแกรมไม่จำเป็นต้องมีลำดับการสอนเฉพาะสำหรับงานที่พวกเขาต้องทำและด้วยเหตุนี้จึงไม่มีเหตุผลที่จะต้องมีลำดับเฉพาะใด ๆ แต่มีเพียงชุดคำสั่งที่มีเหตุผล การเพิ่มประสิทธิภาพคอมไพเลอร์ทำสิ่งเดียวกันเพื่อปรับปรุงประสิทธิภาพการดำเนินการ (นับจำนวนคำสั่งเวลานาฬิกาแขวนขนาดรหัสแคช)
Mikko Rantalainen

9

สิ่งที่พวกเขาทำในอดีตในขณะที่ไม่มีภาษา C? พวกเขากำลังเขียน Assembler ในรหัสเครื่องหรือไม่?

การประกอบเป็นหลักช่วยในการจำสำหรับรหัสเครื่อง; แต่ละ opcode ในภาษาเครื่องจะได้รับการช่วยในการประกอบเช่นใน x86 NOP คือ 0x90 สิ่งนี้ทำให้แอสเซมเบลอร์ของค่อนข้างง่าย (แอสเซมบลีส่วนใหญ่มีสองผ่านหนึ่งในการแปลและที่สองเพื่อสร้าง / แก้ไขที่อยู่ / การอ้างอิง) แอสเซมเบลอร์แรกถูกเขียนและแปลด้วยมือ รุ่นที่ดีกว่าจะถูกเขียนและประกอบกับแอสเซมเบลอร์ 'ประกอบ' มือคุณสมบัติใหม่จะถูกเพิ่มด้วยวิธีนี้ คอมไพเลอร์สำหรับภาษาใหม่สามารถสร้างได้ด้วยวิธีนี้ ในอดีตมันเป็นเรื่องธรรมดาสำหรับคอมไพเลอร์ในการประกอบเอาต์พุตและใช้แอสเซมเบลอร์สำหรับแบ็คเอนด์!

ฉันไม่ควรเขียนตัวแปลรหัสเครื่องสำหรับภาษาระดับต่ำในภาษาระดับสูงกว่านี้ ... [ผู้ประกอบการที่มีอยู่] ได้เขียนเป็นภาษาซีฉันยังสงสัยว่าทำไมพวกเขาถึงเขียนเป็นภาษาซี?

  • โดยทั่วไปแล้วจะง่ายกว่าในการเขียนซอฟต์แวร์ที่ซับซ้อนกว่าในภาษาระดับที่สูงขึ้น
  • โดยทั่วไปจะใช้รหัสมากขึ้นและมีความพยายามในการติดตามสิ่งที่คุณทำในภาษาระดับต่ำกว่าภาษาที่สูงกว่า
    • บรรทัดเดียวของ C สามารถแปลเป็นคำแนะนำมากมายเช่น การมอบหมายอย่างง่ายใน C ++ (หรือ C) มักจะสร้างคำสั่งการประกอบอย่างน้อย 3 คำสั่ง (โหลด, แก้ไข, จัดเก็บ;) อาจใช้คำสั่งยี่สิบคำสั่งหรือมากกว่า (อาจเป็นร้อย) เพื่อทำสิ่งที่ทำได้ด้วยบรรทัดเดียวในระดับที่สูงขึ้น ภาษา (เช่น c ++ หรือ c.) โดยทั่วไปเราต้องการใช้เวลาในการแก้ปัญหาและไม่ใช้เวลาในการหาวิธีใช้โซลูชันในรหัสเครื่อง

ในขณะที่การโฮสต์ด้วยตนเองเป็นคุณลักษณะสำคัญที่เป็นที่ต้องการสำหรับภาษาการเขียนโปรแกรมการประกอบอยู่ในระดับต่ำจนโปรแกรมเมอร์ส่วนใหญ่ต้องการทำงานในระดับที่สูงขึ้น คือไม่มีใครต้องการเขียนแอสเซมเบลอร์ในแอสเซมบลี (หรือสิ่งอื่นใดจริงๆ)

สมมติว่าเราได้สร้างสถาปัตยกรรมไมโครโปรเซสเซอร์ใหม่ที่ไม่มีแม้แต่คอมไพเลอร์ C สำหรับสถาปัตยกรรมนั้น

Bootstrappingเป็นกระบวนการของการรับโซ่เครื่องมือในสถาปัตยกรรมใหม่

กระบวนการพื้นฐานคือ:

  • เขียนส่วนหลังใหม่ที่เข้าใจวิธีการสร้างรหัสสำหรับ CPU ใหม่ของคุณ (หรือ MCU)
  • รวบรวมและทดสอบปลายด้านหลังของคุณ
  • รวบรวมคอมไพเลอร์ที่คุณต้องการ (และระบบปฏิบัติการ ฯลฯ ) โดยใช้แบ็คเอนด์ใหม่ของคุณ
  • ถ่ายโอนไบนารีเหล่านี้ไปยังระบบใหม่

คุณไม่จำเป็นต้องเขียนในแอสเซมบลี (เก่าหรือใหม่) เพื่อทำสิ่งนี้คุณควรเลือกภาษาที่ดีที่สุดในการเขียนแอสเซมเบลอร์ / แบ็คเอนด์ / โค้ด - ไดนาโม

ผู้ประกอบของเราที่เขียนใน C จะสามารถจำลองสถาปัตยกรรมใหม่ได้หรือไม่?

ผู้ประกอบไม่ได้จำลอง!

หากมีการพัฒนา CPU ใหม่ด้วยภาษาเครื่อง (หรือที่มีอยู่) ใหม่เครื่องจำลองมักจำเป็นสำหรับการทดสอบ นั่นคือรันคำสั่งแบบสุ่มและข้อมูลผ่านตัวจำลองและเปรียบเทียบผลลัพธ์ด้วยคำสั่งและข้อมูลเดียวกันบน CPU ต้นแบบของคุณ จากนั้นค้นหาข้อบกพร่องแก้ไขข้อผิดพลาดทำซ้ำ


3

ในบรรดาเหตุผลในการเขียนแอสเซมเบลอร์ใน C (หรือภาษาระดับสูงอื่น ๆ ) คือเหตุผลทั้งหมดที่คุณอาจใช้เพื่อปรับการเขียนโปรแกรมอื่น ๆ ในภาษาระดับสูงกว่านั้น หัวหน้าในบรรดากรณีนี้น่าจะเป็นแบบพกพาและใช้งานได้ดี

การพกพา: ถ้าคุณเขียนแอสเซมเบลอร์ของคุณเป็นภาษาท้องถิ่นคุณจะมีแอสเซมเบลอร์บนแพลตฟอร์มนั้น ถ้าคุณเขียนมันใน C คุณมีแอสเซมเบลอร์บนแพลตฟอร์มใด ๆ ที่มีคอมไพเลอร์ C สิ่งนี้ช่วยให้คุณสามารถคอมไพล์โค้ดสำหรับแพลตฟอร์มฝังตัวของคุณบนเวิร์กสเตชันของคุณและย้ายไบนารีแทนที่จะต้องทำทั้งหมดบนอุปกรณ์เป้าหมายโดยตรง

การใช้งาน: สำหรับคนส่วนใหญ่การอ่านการให้เหตุผลและการปรับเปลี่ยนโปรแกรมนั้นเป็นเรื่องที่เป็นธรรมชาติมากขึ้นเมื่อโปรแกรมอยู่ในระดับภาษาที่สูงกว่าเมื่ออยู่ในแอสเซมเบลอร์หรือ (แย่) รหัสเครื่องดิบ ดังนั้นจึงง่ายต่อการพัฒนาและบำรุงรักษาแอสเซมเบลอร์ในภาษาระดับที่สูงขึ้นเพราะคุณสามารถคิดในแง่ของ abstractions ที่จ่ายให้คุณโดยภาษาระดับที่สูงกว่าแทนที่จะต้องคิดถึง minutiae ที่คุณรับผิดชอบในภาษาที่ต่ำกว่า


3

การกล่าวถึงเฉพาะส่วนนี้ของคำถามเท่านั้น:

"โดยวิธีการที่ฉันรู้ว่า GNU Assembler และ Netwide Assembler ได้รับการเขียนใน C. ฉันยังสงสัยว่าทำไมพวกเขาเขียนใน C?"

การพูดในฐานะเป็นส่วนหนึ่งของทีมที่เขียน Netwide Assembler แต่เดิมการตัดสินใจดูเหมือนชัดเจนสำหรับเราในเวลาที่เราไม่ได้พิจารณาทางเลือกอื่น ๆ แต่ถ้าเราทำเราจะได้ข้อสรุปเดียวกัน เหตุผลดังต่อไปนี้:

  • การเขียนเป็นภาษาระดับต่ำกว่าจะยากขึ้นและใช้เวลานานขึ้น
  • การเขียนด้วยภาษาระดับสูงกว่าอาจเร็วกว่านี้ แต่มีข้อควรพิจารณาด้านประสิทธิภาพ (แอสเซมเบลอร์ที่ใช้เป็นแบ็คเอนด์ไปยังคอมไพเลอร์โดยเฉพาะอย่างยิ่งต้องมีความรวดเร็วมากเพื่อป้องกันไม่ให้คอมไพเลอร์ช้าลงมากเกินไป จบการจัดการโค้ดจำนวนมากและนี่เป็นกรณีการใช้งานที่เราต้องการอนุญาตโดยเฉพาะ) และฉันไม่เชื่อว่าผู้เขียนหลักมีภาษาระดับสูง ๆ เหมือนกัน (นี่คือก่อน Java เป็นที่นิยมดังนั้นโลกของ ภาษาดังกล่าวค่อนข้างกระจัดกระจายในตอนนั้น) เราใช้ perl สำหรับงาน metaprogramming (สร้างตารางคำสั่งในรูปแบบที่มีประโยชน์สำหรับตัวสร้างโค้ดแบ็คเอนด์) แต่มันไม่เหมาะสำหรับโปรแกรมทั้งหมด
  • เราต้องการความสะดวกในการพกพาระบบปฏิบัติการ
  • เราต้องการการพกพาแพลตฟอร์มฮาร์ดแวร์ (สำหรับการผลิตคอมไพเลอร์ข้าม)

สิ่งนี้ทำให้การตัดสินใจค่อนข้างง่าย: ตามมาตรฐาน ANSI C (หรือที่รู้จักใน C89 ทุกวันนี้) เป็นภาษาเดียวในเวลาที่กระทบกับประเด็นเหล่านั้นทั้งหมด หากมี C ++ ที่เป็นมาตรฐานย้อนกลับไปแล้วเราอาจจะคิดว่า แต่การสนับสนุน C ++ ระหว่างระบบที่แตกต่างกันนั้นค่อนข้างจะมีการปะทุย้อนกลับไปดังนั้นการเขียน C ++ แบบพกพาจึงเป็นฝันร้ายสักหน่อย


1

สิ่งหนึ่งไม่มีอะไรเกี่ยวข้องกับคนอื่น เว็บเบราว์เซอร์อย่างเคร่งครัดที่จะเขียนโดยใช้ html หรือ php หรือภาษาเนื้อหาเว็บอื่น ๆ หรือไม่? ไม่พวกเขาจะทำไม รถคันอื่นขับได้ แต่ไม่ใช่มนุษย์เท่านั้น?

การแปลงหนึ่งบิตของบิต (บาง ascii) ไปเป็นอีกบิตของบิต (บางรหัสเครื่อง) เป็นเพียงงานเขียนโปรแกรมภาษาการเขียนโปรแกรมที่คุณใช้สำหรับงานนั้นเป็นสิ่งที่คุณต้องการ คุณสามารถและมีแอสเซมบลีที่เขียนในภาษาต่าง ๆ มากมาย

ภาษาใหม่ไม่สามารถเขียนด้วยภาษาของตนเองได้ในตอนแรกเนื่องจากยังไม่มีคอมไพเลอร์ / แอสเซมเบลอร์สำหรับพวกเขา หากไม่มีคอมไพเลอร์ที่มีอยู่สำหรับภาษาใหม่คุณต้องเขียนภาษาแรกเป็นภาษาอื่นและจากนั้นคุณก็จะบู๊ตสแตรปได้ในที่สุดหากมันเหมาะสมกับ bootstrap (html และเว็บเบราว์เซอร์โปรแกรมที่ใช้บิตในและคายบางบิตจะไม่ถูกเขียนใน html ไม่สามารถ)

ไม่จำเป็นต้องเป็นภาษาใหม่สามารถเป็นภาษาที่มีอยู่ได้ คอมไพเลอร์ C หรือ C ++ ใหม่ไม่คอมไพล์ตัวเองโดยอัตโนมัติทันทีที่ออกจากประตู

สำหรับภาษาแอสเซมบลีและ C สองภาษาแรกสำหรับชุดคำสั่งใหม่หรือดัดแปลงเกือบทั้งหมด เราไม่ได้อยู่ในอดีตเราอยู่ในปัจจุบัน เราสามารถสร้างแอสเซมเบลอร์ใน C หรือจาวาหรือไพ ธ อนหรืออะไรก็ได้สำหรับชุดคำสั่งและภาษาแอสเซมบลีที่เราต้องการแม้ว่ามันจะยังไม่มีอยู่ก็ตาม ในทำนองเดียวกันมีคอมไพเลอร์ C ที่กำหนดเป้าหมายใหม่จำนวนมากซึ่งเราสามารถส่งออกภาษาแอสเซมบลีสำหรับภาษาแอสเซมบลีใด ๆ ที่เราต้องการแม้ว่าแอสเซมเบลอร์ยังไม่มีอยู่

นั่นคือสิ่งที่เราทำกับชุดคำสั่งใหม่ ใช้คอมพิวเตอร์บางเครื่องที่ไม่ได้ทำงานในชุดคำสั่งใหม่ของเราด้วยคอมไพเลอร์ C ที่ไม่ได้รวบรวมไว้สำหรับชุดคำสั่งใหม่ของเราหรือชุดประกอบของมันสร้าง cross assembler และ cross compiler พัฒนาและใช้สิ่งนั้นในขณะที่สร้างและจำลองตรรกะ ผ่านวงจรการพัฒนาตามปกติของการค้นหาข้อบกพร่องและแก้ไขข้อบกพร่องและทดสอบอีกครั้งจนกระทั่งนึกคิดเครื่องมือทั้งหมดและตรรกะจะถือว่าพร้อม และขึ้นอยู่กับเป้าหมายว่าเป็นไมโครคอนโทรลเลอร์ที่ไม่สามารถเรียกใช้ระบบปฏิบัติการคุณจะไม่มีเหตุผลที่จะบูตสแตรปที่ toolchain สร้างและรันโดยใช้ชุดคำสั่งดั้งเดิม คุณมักจะคอมไพล์ นอกเหนือจากเครื่องเวย์แบ็คมันไม่สมเหตุสมผลเลยที่จะเขียนแอสเซมเบลอร์ในแอสเซมเบลอร์

ใช่ถ้าคุณสามารถย้อนกลับไปหรือแกล้งที่จะกลับไปผู้ประกอบชิ้นแรกคือมนุษย์ด้วยดินสอและกระดาษที่เขียนสิ่งที่สมเหตุสมผลกับพวกเขาแล้วเขียนบิตถัดจากนั้นที่ทำให้รู้สึกถึงตรรกะ จากนั้นใช้สวิตช์หรือวิธีอื่น ๆ ในการรับบิตเข้าสู่เครื่อง (google pdp8 หรือ pdp11 หรือ altair 8800) และทำให้มันทำงานได้ ไม่มีการจำลองคอมพิวเตอร์ในตอนแรกคุณแค่ต้องใช้ตรรกะให้ถูกต้องโดยการจ้องมองที่มันนานพอหรือหมุนหลายรอบของชิป เครื่องมือนี้ดีพอในวันนี้ที่คุณสามารถได้รับความสำเร็จ A0 ในสิ่งที่เป็นมากกว่าตัวต้านทานขนาดใหญ่มันใช้งานได้หลายอย่างคุณยังคงต้องหมุนสำหรับสิ่งที่คุณไม่สามารถจำลองได้อย่างสมบูรณ์ แต่คุณสามารถบู๊ตได้ทันที spi แรกโดยไม่ต้องรอการหมุนรอบที่สามหรือสี่

ในเครื่องเวย์แบ็คของคุณอย่างที่คุณคาดไว้จากนั้นคุณก็นำโค้ดประกอบมือแล้วใช้มันเพื่อบอกว่าโหลดโปรแกรมจากเทปหรือการ์ด นอกจากนี้คุณยังส่งรหัสแอสเซมเบลอร์ในรหัสเครื่องอาจไม่ใช่แบบเต็มรูปแบบ แต่อย่างใดอย่างหนึ่งที่ทำให้การเขียนโปรแกรมง่ายขึ้นเล็กน้อย จากนั้นเครื่องมือดังกล่าวจะถูกใช้เพื่อสร้างเครื่องมือที่สามารถจัดการกับการบรรจุที่ซับซ้อนหรือซับซ้อนยิ่งขึ้น (แอสเซมเบลอร์มาโคร) และอีกอันหนึ่งเพื่อสร้างความซับซ้อนมากขึ้นและคุณจะจบลงด้วย FORTRAN หรือ BASIC หรือ B หรืออะไรก็ตาม จากนั้นคุณเริ่มคิดถึง bootstrapping ในภาษาเดียวกันเขียน cross compiler ใหม่ให้เป็นภาษาคอมไพเลอร์ แน่นอนว่าคุณต้องการสภาพแวดล้อมหรือระบบปฏิบัติการบางประเภท

เมื่อเรากำลังสร้างหรือทดสอบซิลิคอนเราสามารถ / ต้องจ้องที่สัญญาณซึ่งเป็นสัญญาณหนึ่งและศูนย์ เครื่องมือจะแสดงไบนารีหรือฐานสิบหกเป็นค่าเริ่มต้นและเป็นไปได้ด้วยเครื่องมือบางอย่างที่อาจมีการค้นหาดังนั้นเครื่องมือจะแสดงเครื่องมือช่วยจำบางอย่าง (อาจจะประกอบ) แต่บ่อยครั้งที่วิศวกร (ซิลิคอน / ฮาร์ดแวร์และซอฟต์แวร์) สามารถอ่านได้เพียงพอ รหัสเครื่องหรือใช้ส่วนประกอบ / รายการเพื่อ "ดู" คำแนะนำ

ขึ้นอยู่กับสิ่งที่คุณกำลังทำคุณอาจดันรหัสเครื่องลงในเวกเตอร์ทดสอบแทนที่จะเขียนใหม่และรวบรวมใหม่หรือประกอบการทดสอบอีกครั้ง ตัวอย่างเช่นถ้าคุณมีไพพ์ไลน์และการดึงข้อมูลล่วงหน้าที่ระดับความลึกคุณอาจต้องการหรือต้องการเติมผ่านจุดสิ้นสุดของโปรแกรมจำนวน nops หรือคำแนะนำจริงอื่น ๆ บางส่วนเพื่อให้ไพพ์ไม่ puke ตามคำแนะนำที่ไม่ได้กำหนดและคุณอาจเลือก กรอกรหัสเครื่องในรายการ / ไฟล์ระดับต่ำสุดแทนที่จะลองรับคอมไพเลอร์หรือแอสเซมเบลอร์หรือ linker เพื่อทำ

ในขณะที่การทดสอบโปรเซสเซอร์แน่นอนว่าคุณต้องจัดการกับ undefineds และอาจไม่สนใจบิต ฯลฯ ดังนั้นคุณจะต้องเข้าไปที่รหัสเครื่องและแก้ไขบิตที่ระบุในคำสั่งในโปรแกรมที่ทำงานตามปกติ มันคุ้มค่าที่จะเขียนโปรแกรมเพื่อทำสิ่งนี้หรือทำด้วยมือ เช่นเดียวกันเมื่อทดสอบ ECC คุณต้องการพลิกหนึ่งบิตขึ้นไปและดูว่าพวกเขาได้รับการแก้ไขหรือติดกับดัก ได้รับที่ง่ายกว่ามากในการเขียนโปรแกรมที่จะทำหรือคุณสามารถทำได้ด้วยมือ

แน่นอนว่ามีภาษาที่ไม่ได้ผลิตรหัสที่ทำงานบนโปรเซสเซอร์, ปาสคาลต้น, java, python เป็นต้นคุณต้องมี VM ที่เขียนในภาษาอื่นเพื่อใช้ภาษาเหล่านั้น คุณไม่สามารถใช้คอมไพเลอร์ภาษาจาวาของคุณเพื่อสร้างจาวา vm ไม่สมเหตุสมผลตามการออกแบบของภาษา

(ใช่แน่นอนหลังจากการใช้ภาษาเหล่านี้อย่างแท้จริงในที่สุดบางคนก็สร้างแบ็กเอนด์ที่ไม่บริสุทธิ์ซึ่งบางครั้งสามารถกำหนดเป้าหมายคำสั่งจริงไม่ใช่ชุดคำสั่ง vm และในกรณีนั้นคุณสามารถใช้ภาษาเพื่อรวบรวมมันเองหรือ vm ถ้าคุณรู้สึก ต้องการ. ส่วนหน้า java gnu กับ gcc เป็นต้น)

เมื่อเวลาผ่านไปและอาจเป็นไปได้ว่าเราไม่ได้เขียนคอมไพเลอร์ C ในซีเราใช้สิ่งต่างๆเช่นวัวกระทิง / ดิ้นภาษาการเขียนโปรแกรมอื่น ๆ ที่เราใช้ในการสร้าง C สำหรับเราที่เราไม่ต้องการเขียนเอง บางเปอร์เซ็นต์อยู่ใน C แน่ใจ แต่บางเปอร์เซ็นต์อยู่ในภาษาอื่นที่ใช้คอมไพเลอร์อื่น ๆ ที่อินพุตบิตและเอาท์พุทบิตอื่น ๆ บางครั้งวิธีนี้ยังใช้สำหรับการสร้างแอสเซมเบลอร์ ขึ้นอยู่กับผู้ออกแบบคอมไพเลอร์ / แอสเซมเบลอร์ (โปรแกรมที่มีภาระงานในการป้อนบิตแล้วส่งออกบิตอื่น ๆ ) ตามวิธีที่พวกเขาจะนำไปใช้งาน โปรแกรมแยกวิเคราะห์ที่สร้างขึ้นสามารถมั่นใจได้ว่าตั้งโปรแกรมด้วยมือใช้เวลานานดังนั้นผู้คนจึงมองหาทางลัด เช่นเดียวกับที่คุณสามารถเขียนแอสเซมเบลอร์ในแอสเซมเบลอร์ แต่ผู้คนมองหาทางลัด

เว็บเบราว์เซอร์เป็นเพียงโปรแกรมที่ใช้ในบิตและแยกบิตอื่น ๆ แอสเซมเบลอร์เป็นเพียงโปรแกรมที่ใช้ในบิตและแยกบิตอื่นออกมา คอมไพเลอร์เป็นเพียงโปรแกรมที่ใช้ในบิตและแยกบิตอื่น ๆ อื่น ๆ สำหรับสิ่งเหล่านี้มีชุดของกฎสำหรับการป้อนข้อมูลบิตและบิตการส่งออกสำหรับแต่ละงานการเขียนโปรแกรม งานและบิตเหล่านี้เป็นเรื่องทั่วไปมากพอที่จะใช้ภาษาการเขียนโปรแกรม AVAILABLE ใด ๆ ก็ได้ (นั่นคือความสามารถในการจัดการบิต / ไบต์และจัดการกับอินพุตและเอาต์พุต) กุญแจที่นี่สามารถใช้ได้ รับลองลินุกซ์จากหนังสือ / กวดวิชา ลองใช้ pdp8 หรือ pdp11 หรือ altair 8800 หรืออุปกรณ์จำลองอื่น ๆ ด้วยแผงด้านหน้าจำลอง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.