“C로 어셈블러 작성” 더 높은 수준의 언어로 낮은 수준의 언어에 대한 기계 코드 번역기를 작성하는 이유는 무엇입니까?


13

저의 마이크로 프로세서 수업 강사는 우리에게 과제를 주었고 다음과 같이 말했습니다.

"C로 어셈블러 작성" -내 사랑하는 교수

그래서 그것은 나에게 약간 비논리적 인 것처럼 보였다.

내가 틀렸다면 조립 언어는 기계어 코드에서 고급 언어로가는 첫 단계입니다. 나는 C가 Assembly보다 더 높은 수준의 언어라는 것을 의미한다. C로 어셈블러를 작성하는 요점은 무엇입니까? C 언어가없는 동안 과거에 무엇을하고 있었습니까? 기계어 코드로 어셈블러를 작성 했습니까?

고급 언어로 저수준 언어에 대한 기계어 코드 번역기를 작성하는 것은 의미가 없습니다.

해당 아키텍처를위한 C 컴파일러조차없는 새로운 마이크로 프로세서 아키텍처를 만들었다 고 가정 해 봅시다. C로 작성된 어셈블러가 새로운 아키텍처를 시뮬레이션 할 수 있습니까? 나는 그것이 쓸모 없 겠는가?

그런데 GNU 어셈블러와 Netwide 어셈블러가 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
고립 된 장치가 없습니다. 크로스 툴체인은 특히 작은 아키텍처에서 널리 사용됩니다.
Lars Viklund

3
"크로스"컴파일러 / 어셈블러는 대상과 다른 시스템에서 실행되며 대상 시스템에서 사용하기에 적합한 아티팩트를 생성합니다. 고대에는 반드시 시스템간에 데이터를 교환 할 필요는 없었지만 시스템 자체를 처음부터 다시 시작해야했습니다. 아키텍처에 대한 현대의 거의 모든 개발은 기존 시스템에서 이루어지며 모든 것을 크로스 컴파일합니다.
Lars Viklund

19
C 대신 머신 코드로 어셈블러를 작성 하시겠습니까? 교수님이 친절 하시네요
Winston Ewert

2
최고의 프로그래밍 환경 / 언어에서 모든 코드를 작성하려고 노력하지 않는 이유는 무엇입니까? 어셈블러도 예외는 아닙니다.
Erik Eidt

1
특정 방향으로 고정 된 "여행"은 없습니다.
whatsisname

답변:


18

사람들은 기계어 코드로 어셈블러를 작성했습니다. 또한 어셈블리 언어로 작성된 경우가 많습니다. 종종 자체 번역하는 언어의 하위 집합이므로 간단한 "부트 스트랩"버전의 어셈블러로 시작한 다음 어셈블러 자체에 필요한 기능을 추가합니다.

그러나이 중 어느 것도 특별히 필요한 것은 아닙니다. 결국 어셈블러는 (일반적으로 공정한) 간단한 번역 프로그램입니다. 파일은 한 형식 (텍스트) 형식으로 파일을 가져오고 다른 형식 (일반적으로 오브젝트 파일 형식)으로 파일을 작성합니다.

입력되는 텍스트가 텍스트 형식의 기계 명령어를 나타내고 결과가 이진 형식의 동일한 명령어를 나타낸다는 사실은 어셈블러를 구현하는 데 사용되는 언어 (실제로 C보다 더 높은 언어)와 크게 다르지 않습니다. SNOBOL과 Python은 꽤 훌륭하게 작동 할 수 있습니다. 저는 최근에 Python으로 작성된 어셈블러에서 일했으며, 그 일을 잘 수행했습니다.

초기에 부트 스트랩하는 방법 : 일반적으로 적절한 개발 도구가있는 다른 머신에서. 새 하드웨어를 개발하는 경우 일반적으로 어쨌든 새 컴퓨터에 대한 시뮬레이터 (또는 적어도 에뮬레이터)를 작성하는 것으로 시작하므로 처음에는 어떤 경우에도 일부 호스트 시스템에서 코드를 빌드하고 실행합니다.


3
"SNOBOL 및 Python과 같은 C보다 더 높은 언어조차도 아주 잘 작동 할 수 있습니다."-이것은 매우 좋은 지적입니다. NASM의 경우 C보다 더 높은 수준의 것을 고려하지는 않았지만 1995 년이 오늘날보다 성능이 훨씬 중요하고 고급 언어가 현재보다 훨씬 덜 고급화 된시기였습니다. 요즘에는 대안을 고려할 가치가 있습니다.
Jules

1
1980 년대 이래로 SNOBOL이라는 이름을 듣지 못했습니다.
pacmaninbw

나는 Haskell에서 컴파일러를 한 번 작성했습니다. 기능의 게으른 평가 및 연결로 인해 생성 된 기계 코드에 대한 들여다 보는 구멍 최적화 프로그램을 작성하는 것이 간단 해졌습니다.
Thorbjørn Ravn Andersen

11

존재하지 않는 연결이 표시됩니다.

"어셈블러 작성"은 다른 프로그래밍 작업과 마찬가지로 프로그래밍 작업입니다. 도구를 사용하여 해당 작업에 가장 적합한 작업을 처리합니다. 어셈블러를 작성하는 데 특별한 것은 없습니다. 고급 언어로 작성하지 않는 이유는 없습니다. C는 실제로 상당히 낮은 수준에 있으며 아마도 C ++ 또는 다른 고급 언어를 선호합니다.

어셈블리 언어는 실제로 이와 같은 작업에는 전혀 적합하지 않습니다. 어셈블리 언어를 합리적으로 사용하는 경우는 매우 드 rare니다. 고급 언어로 표현할 수없는 일을해야 할 때만.


1
다른 답변은 매우 좋지만, 나는이 답변이 가장 간단하다는 것을 알았습니다. 특히 처음 두 문장에서 그렇습니다. 나는 질문을 읽을 때 나에게 똑같은 것을 말하고 있었다.
MetalMikester

요즘에는 하드웨어 특정 해킹에만 어셈블리 언어를 작성해야합니다. 예를 들어, 일부 CPU에서 보호 모드를 설정하려면 특정 명령어 시퀀스가 ​​필요하고 논리적으로 동등한 시퀀스는 충분하지 않습니다. 거의 모든 일반 프로그램은 수행해야 할 작업에 특정 명령 순서가 필요하지 않으므로 결과적으로 특정 순서가 필요하지만 논리적으로 동등한 일부 명령 세트 만 필요합니다. 컴파일러 최적화는 실행 성능 (명령 수, 벽시계 시간, 코드 캐시 크기)을 향상시키기 위해 정확히 동일한 작업을 수행합니다.
Mikko Rantalainen

9

C 언어가없는 동안 과거에 무엇을하고 있었습니까? 기계어 코드로 어셈블러를 작성 했습니까?

어셈블리는 본질적으로 머신 코드의 니모닉입니다. 기계 언어의 각 opcode에는 어셈블리 니모닉이 제공됩니다. 즉 x86의 NOP는 0x90입니다. 따라서 어셈블러가 다소 단순 해집니다 (대부분의 어셈블러에는 두 개의 패스가 있습니다. 하나는 변환 할 경로이고 다른 하나는 주소 / 참조를 생성 / 해결하는 것입니다). 더 나은 버전은 손으로 '어셈블 된'어셈블러로 작성되고 어셈블되며 새로운 기능이 이런 방식으로 추가됩니다. 새로운 언어를위한 컴파일러는 이런 방식으로 구축 될 수 있습니다. 과거에는 컴파일러가 어셈블리를 출력하고 백엔드에 어셈블러를 사용하는 것이 일반적이었습니다!

고급 언어로 저수준 언어에 대한 기계어 코드 번역기를 작성하는 것은 의미가 없습니다. ... [기존 어셈블러]는 C로 작성되었습니다. 또한 C로 작성된 이유가 궁금합니다.

  • 보다 복잡한 언어의 소프트웨어를보다 높은 수준의 언어로 작성하는 것이 일반적으로 더 쉽습니다.
  • 일반적으로 상위 언어보다 하위 언어로 수행중인 작업을 추적하려면 더 많은 코드와 더 많은 노력이 필요합니다.
    • 한 줄의 C는 많은 명령어로 번역 될 수 있습니다. C ++ (또는 C)의 간단한 할당은 일반적으로 최소 3 개의 어셈블리 명령어 (로드, 수정, 저장)를 생성합니다. 한 줄로 더 높은 수준의 단일 행으로 수행 할 수있는 작업을 수행하려면 20 개 이상의 명령어 (수백 개)가 필요할 수 있습니다 언어 (c ++ 또는 c와 같은) 일반적으로 문제를 해결하는 데 시간을 투자하고 머신 코드에서 솔루션을 구현하는 방법을 알아내는 데 시간을 허비하지 않을 것입니다.

하지만 자기 호스팅은 프로그래밍 언어에 대한 공통의 이정표 / 것이 특징이다, 조립 대부분의 프로그래머가 높은 수준에서 작업하는 것을 선호 너무 낮은 수준이다. 즉, 어셈블리 (또는 실제로 다른 것)에서 어셈블러를 작성하려는 사람은 없습니다.

해당 아키텍처를위한 C 컴파일러조차없는 새로운 마이크로 프로세서 아키텍처를 만들었다 고 가정 해 봅시다.

부트 스트랩 은 새로운 아키텍처에서 툴체인을 얻는 과정입니다.

기본 프로세스는 다음과 같습니다.

  • 새 CPU (또는 MCU)의 코드를 생성하는 방법을 이해하는 새로운 백엔드 작성
  • 백엔드 컴파일 및 테스트
  • 새로운 백엔드를 사용하여 원하는 컴파일러 (및 OS 등)를 크로스 컴파일하십시오.
  • 이 바이너리를 새로운 시스템으로 전송

이 작업을하기 위해 한 번에 어셈블리 (새 또는 기존)로 작성하지 않아도되므로 어셈블러 / 백엔드 / 코드 생성기를 작성하는 데 가장 적합한 언어를 선택해야합니다.

C로 작성된 어셈블러가 새로운 아키텍처를 시뮬레이션 할 수 있습니까?

어셈블러는 시뮬레이션하지 않습니다!

새로운 (또는 기존) 기계 언어를 사용하여 새로운 CPU를 개발하는 경우 일반적으로 테스트를 위해 시뮬레이터가 필요합니다. 즉, 시뮬레이터를 통해 임의의 명령 및 데이터를 실행하고 프로토 타입 CPU의 동일한 명령 및 데이터와 출력을 비교하십시오. 그런 다음 버그를 찾고, 버그를 수정하고, 반복하십시오.


3

C (또는 다른 고급 언어)로 어셈블러를 작성하는 이유 중 하나는 다른 언어로 된 다른 프로그램을 작성하는 것을 정당화하는 데 사용할 수있는 모든 이유입니다. 이 경우 가장 중요한 것은 아마도 이식성과 사용 성일 것입니다.

이식성 : 모국어로 어셈블러를 작성하면 해당 플랫폼에 어셈블러가 있습니다. C로 작성하면 C 컴파일러가있는 모든 플랫폼에 어셈블러가 있습니다. 예를 들어, 워크 스테이션에서 임베디드 플랫폼 용 코드를 컴파일하고 대상 장치에서 직접 수행하지 않고 바이너리를 이동할 수 있습니다.

사용성 : 대부분의 사람들은 프로그램이 어셈블러 나 (나쁜) 원시 머신 코드보다 높은 수준의 언어 일 때 프로그램을 읽고, 추론하고, 수정하는 것이 훨씬 더 자연 스럽습니다. 따라서 고급 언어로 어셈블러를 개발하고 유지 관리하는 것이 더 쉽습니다. 하위 언어를 담당하는 축소에 대해 생각할 필요없이 상위 언어로 제공되는 추상화의 관점에서 생각할 수 있기 때문입니다.


3

질문 의이 부분만을 구체적으로 언급하십시오.

"GNU 어셈블러와 넷 와이드 어셈블러가 C로 작성되었다는 것을 알고있는 방법으로, C로 작성된 이유가 궁금합니다."

원래 Netwide Assembler를 작성한 팀의 일환으로 말하면 결정은 우리가 기본적으로 다른 옵션을 고려하지 않았지만 우리가 동일한 결론에 도달했을 때 우리에게 매우 명백해 보였습니다. 다음과 같은 이유 :

  • 더 낮은 수준의 언어로 작성하면 훨씬 어렵고 시간이 많이 걸렸을 것입니다.
  • 더 높은 수준의 언어로 작성하는 것이 더 빠를 수도 있지만 성능 고려 사항이있었습니다 (컴파일러의 백엔드로 사용되는 어셈블러, 특히 컴파일러 속도를 너무 느리게하는 것을 방지하기 위해 매우 빠름). 매우 많은 양의 코드를 처리하게되었고, 이것은 우리가 구체적으로 허용하고 싶었던 사용 사례였습니다.) 나는 주요 저자가 공통적으로 더 높은 수준의 언어를 가지고 있다고 생각하지 않습니다 (이것은 Java가 대중화되기 전에였습니다. 그런 언어는 그 당시에 오히려 조각화되었습니다.) 우리는 일부 메타 프로그래밍 작업 (코드 생성기 백엔드에 유용한 형식으로 명령어 테이블 생성)에 perl을 사용했지만 실제로 전체 프로그램에 적합하지는 않았습니다.
  • 우리는 운영 체제 이식성을 원했습니다
  • 우리는 (컴파일러를위한) 하드웨어 플랫폼 이식성을 원했습니다.

ANSI 결정 C (일명 C89)는 당시 그 모든 점에 실제로 영향을 준 유일한 언어였습니다. 이 당시 우리는 표준화 C ++ 있었다면 있도록 ++ 휴대용 C를 작성하는 것을 고려했다,하지만 서로 다른 시스템 사이의 C ++ 지원은 다음 오히려 누덕 누덕 기운 뒤였다 악몽의 조금이었다.


1

한 가지는 다른 것과 전혀 관련이 없습니다. 웹 브라우저는 HTML이나 PHP 또는 다른 웹 컨텐츠 언어를 사용하여 작성해야합니까? 아니요, 왜 그런가요? 자동차는 사람이 아닌 다른 자동차로만 운전할 수 있습니까?

한 비트의 비트 (일부 ASCII)를 다른 비트의 비트 (일부 기계 코드)로 변환하는 것은 프로그래밍 작업 일뿐입니다. 해당 작업에 사용하는 프로그래밍 언어는 원하는 것입니다. 여러 언어로 작성된 어셈블러가있을 수 있습니다.

아직 컴파일러 / 어셈블러가 없기 때문에 새로운 언어는 초기에 자국어로 쓸 수 없습니다. 새 언어에 대한 기존 컴파일러가없는 경우 다른 언어로 첫 번째 컴파일러를 작성해야하며 부트 스트랩에 적합한 경우 결국 부트 스트랩합니다. (html 및 웹 브라우저, 일부 비트를 가져오고 일부 비트를 뱉어내는 프로그램은 HTML로 작성되지 않습니다.)

새로운 언어 일 필요는 없으며 기존 언어 일 수 있습니다. 새로운 C 또는 C ++ 컴파일러는 자동적으로 게이트 밖으로 자동 컴파일되지 않습니다.

어셈블리 언어와 C의 경우 거의 모든 새로운 또는 수정 된 명령어 세트의 처음 두 언어입니다. 우리는 과거가 아니라 현재에 있습니다. 우리는 C 또는 java 또는 python 또는 아직 존재하지 않더라도 원하는 명령어 세트 및 어셈블리 언어에 대해 어셈블러를 쉽게 생성 할 수 있습니다. 마찬가지로 어셈블러가 아직 없더라도 원하는 어셈블리 언어에 대해 어셈블리 언어를 출력 할 수있는 대상 변경 가능한 C 컴파일러가 많이 있습니다.

이것이 바로 새로운 명령어 세트로하는 일입니다. 새 명령어 세트 나 어셈블러 용으로 컴파일되지 않은 C 컴파일러로 새 명령어 세트에서 실행되지 않는 일부 컴퓨터를 가져 와서 크로스 어셈블러와 크로스 컴파일러를 작성하십시오. 로직을 작성하고 시뮬레이션하면서이를 개발하고 사용하십시오. 이상적으로 모든 툴과 로직이 준비된 것으로 간주 될 때까지 버그를 찾아서 버그를 수정하고 다시 테스트하는 일반적인 개발주기를 거칩니다. 또한 대상에 따라 운영 체제를 실행할 수없는 마이크로 컨트롤러라고 말하면 툴체인이 기본 명령 세트를 사용하여 생성하고 실행하도록 부트 스트랩 할 이유가 없습니다. 당신은 항상 크로스 컴파일 할 것입니다. 웨이 백 머신 이외에는 어셈블러에서 어셈블러를 작성하는 것이 결코 의미가 없습니다.

예, 돌아가거나 돌아가는 척 할 수 있다면, 첫 번째 어셈블러는 연필과 종이를 가진 사람이었으며, 그에 맞는 것을 썼고 그 옆에 논리에 맞는 비트를 썼습니다. 그런 다음 스위치 또는 다른 방법을 사용하여 비트를 기계 (Google pdp8 또는 pdp11 또는 altair 8800)로 가져 와서 무언가를하도록하십시오. 처음에는 컴퓨터 시뮬레이터가 없었습니다. 로직을 충분히 길게 쳐다 보거나 칩의 여러 회전을 회전시켜 로직을 가져와야했습니다. 이 도구는 오늘날 충분히 큰 저항 이상이라는 점에서 A0 성공을 거두기에 충분합니다. 완전히 시뮬레이션 할 수 없었던 것들에 대해서는 여전히 스핀이 필요할 수 있지만 지금은 종종 부팅 할 수 있습니다. 세 번째 또는 네 번째 스핀을 기다리지 않고 첫 번째 spi,

웨이 백 머신에서 예상 한대로 수동으로 작성한 코드를 가져 와서 테이프 또는 카드에서 프로그램을로드하는 데 사용합니다. 또한 기계어 코드로 어셈블러를 직접 코딩하십시오. 완전히 불어 난 것이 아니라 프로그래밍을 조금 더 쉽게 할 수 있습니다. 그런 다음이 도구를 사용하여 고급 또는 복잡한 언어 (매크로 어셈블러)를 처리 할 수있는 도구를 만들고, 하나 더 복잡하게 만들면 FORTRAN, BASIC 또는 B 등을 만들 수 있습니다. 그런 다음 동일한 언어로 부트 스트래핑을 생각하고 크로스 컴파일러를 네이티브 컴파일러로 다시 작성합니다. 물론 환경이나 운영 체제가 필요합니다.

실리콘을 만들거나 테스트 할 때 1과 0 인 신호를 응시해야합니다. 이 도구는 기본적으로 바이너리 또는 16 진수를 표시하며 일부 도구를 사용하여 조회 할 수도있어 일부 니모닉 (어쩌면 조립품)을 표시하지만 엔지니어 (실리콘 / 하드웨어 및 소프트웨어)는 기계 코드 또는 분해 / 목록을 사용하여 지침을 "참조"하십시오.

수행중인 작업에 따라 테스트를 다시 작성하고 다시 컴파일하거나 다시 어셈블하지 않고 일부 머신 코드를 테스트 벡터에 삽입 할 수 있습니다. 예를 들어 파이프 라인이 있고 깊이가 프리 페치 인 경우 파이프가 정의되지 않은 명령을 수행하지 않도록 프로그램 끝을 지나서 몇 개의 nops 또는 다른 실제 명령을 작성해야 할 수도 있습니다. 컴파일러 나 어셈블러 또는 링커가이를 수행하도록하기보다는 가장 낮은 레벨의 목록 / 파일에서 머신 코드를 채우십시오.

물론 프로세서를 테스트하는 동안 undefineds를 처리하고 비트를 관리하지 않아야 할 수도 있습니다. 따라서 기계 코드로 이동하여 정상적으로 작동하는 프로그램의 명령어에서 하나 이상의 특정 비트를 수정해야합니다. 이 작업을 수행하거나 직접 수행하는 프로그램을 작성하는 것이 가치가 있습니까? 마찬가지로 ECC를 테스트 할 때 하나 이상의 비트를 뒤집어 비트가 수정되거나 갇히는 것을 확인하려고합니다. 그것은 할 프로그램을 작성하는 것이 훨씬 쉬워 지거나 손으로 직접 할 수 있습니다.

물론 프로세서, 초기 파스칼, 자바, 파이썬 등에서 실행되는 코드를 생성하지 않는 언어가 있습니다. 해당 언어를 사용하려면 다른 언어로 작성된 VM이 필요합니다. Java 컴파일러를 사용하여 Java VM을 만들 수 없으며 언어 디자인에 따라 의미가 없습니다.

(예, 이러한 언어의 순수한 구현 후에 누군가가 때로는 vm 명령어 세트가 아닌 실제 명령어 세트를 대상으로 할 수있는 불순한 백엔드를 빌드 한 다음 실제로 사용하면 언어를 사용하여 자체 또는 VM을 컴파일 할 수 있습니다 gcc에 대한 gnu Java 프론트 엔드).

시간이 지남에 따라 아마도 여전히 C로 C 컴파일러를 작성하지 않을 것입니다. 우리는 bison / flex와 같은 것들을 사용하여 우리가 직접 작성하고 싶지 않은 C를 생성하는 데 사용하는 다른 프로그래밍 언어를 사용합니다. 일부 백분율은 확실하지만 일부 백분율은 비트를 입력하고 다른 비트를 출력하는 다른 컴파일러를 사용하는 다른 언어입니다. 때때로이 접근 방식은 어셈블러 생성에도 사용됩니다. 구현 방법에 대한 컴파일러 / 어셈블러의 설계자 (비트를 입력 한 다음 다른 비트를 출력하는 작업이있는 프로그램)에 달려 있습니다. 프로그램 생성 파서는 수동으로 프로그래밍 될 수 있으며, 시간이 많이 걸리므로 사람들은 지름길을 찾습니다. 어셈블러에서 어셈블러를 작성할 수있는 것처럼 사람들은 바로 가기를 찾습니다.

웹 브라우저는 일부 비트를 가져오고 다른 비트를 뱉어내는 프로그램입니다. 어셈블러는 일부 비트를 가져오고 다른 비트를 뱉어내는 프로그램입니다. 컴파일러는 일부 비트를 취하고 다른 비트를 뱉어내는 프로그램입니다. 기타이 모든 것에는 각 프로그래밍 작업에 대한 입력 비트 및 출력 비트에 대한 문서화 된 규칙 세트가 있습니다. 이러한 작업과 비트는 사용 가능한 프로그래밍 언어를 사용할 수있을 정도로 일반적입니다 (비트 / 바이트 조작을 수행하고 입력 및 출력을 처리 할 수 ​​있음). 여기 열쇠가 있습니다. 스크래치 북 / 튜토리얼에서 리눅스를 사용해보십시오. 시뮬레이션 된 전면 패널이있는 pdp8 또는 pdp11 또는 altair 8800 또는 기타 시뮬레이터를 사용해보십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.