"Напишіть асемблер в C." Навіщо писати машинний перекладач для мови низького рівня мовою вищого рівня?


13

Мій інструктор з класу мікропроцесорів дав нам завдання і сказав:

"Напишіть асемблер в 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
Пристрій не існує ізольовано. Перехресні ланцюги інструментів дуже поширені, особливо для крихітних архітектур.
Ларс Віклунд

3
"Перехресний" компілятор / асемблер працює в іншій системі, ніж цільова, і створює артефакти, придатні для використання в цільовій системі. У стародавні часи вам не обов'язково було обмінюватися даними між системами, але довелося завантажувати систему з нуля з точки зору себе. В основному вся сучасна розробка архітектур робиться на усталених системах, перетинаючи все.
Ларс Віклунд

19
Ви хочете написати асемблер в машинному коді замість C? Ваш професор приємно ставиться до вас.
Вінстон Еверт

2
Чому б ти не прагнув написати весь код у найкращому можливому середовищі / мові програмування? Ассамблер не є винятком.
Ерік Ейдт

1
Не існує фіксованої "подорожі" в якомусь конкретному напрямку.
whatsisname

Відповіді:


18

Люди написали асемблери в машинному коді. Вони також писали тоді мовою асемблера - часто це підмножина мови, яку вони перекладають, тому вони починають з простої версії асемблера "завантажувальна" програма, а потім додають їй функції, як вони потрібні для самого асемблера.

Однак нічого з цього не є особливо необхідним. Зрештою, асемблер - це (як правило, досить) проста програма перекладу. Він бере файл у одному (текстовому) форматі і записує файл в іншому (зазвичай це об'єктний формат файлу).

Те, що введений текст представляє машинні інструкції у текстовому форматі, а результат представляє ті самі інструкції у двійковому форматі, не має великої різниці в мові, яка використовується для реалізації асемблера - насправді навіть вищі мови, ніж C такі як SNOBOL і Python можуть працювати досить непогано - я (досить) нещодавно працював над асемблером, написаним на Python, і це досить добре працювало для роботи.

Що стосується того, як ви спочатку завантажуєте речі: як правило, на іншій машині, яка має гідні інструменти для розробки тощо. Якщо ви розробляєте нове обладнання, ви, як правило, починаєте з написання тренажера (або принаймні емулятора) для нової машини, так що спочатку ви будуєте та запускаєте код на якійсь хост-системі в будь-якому випадку.


3
"навіть більш високі мови, ніж C, такі як SNOBOL та Python, можуть працювати досить непогано" - це дуже хороший момент. Для NASM ми ніколи насправді не розглядали нічого вищого рівня, ніж C, але це був 1995 рік, коли продуктивність була набагато важливішою, ніж сьогодні, а мови високого рівня були набагато менш розвиненими, ніж вони є сьогодні. У ці дні, безумовно, варто розглянути альтернативи.
Jules

1
Я не чув прізвища SNOBOL з 1980-х.
pacmaninbw

Я писав компілятор в Haskell один раз. Ледача оцінка та ланцюжок функцій зробила тривіально простим написання оптимізатора маточок для створеного машинного коду.
Thorbjørn Ravn Andersen

11

Ви бачите зв’язки, які не існують.

"Написати асемблер" - це завдання програмування, як і будь-яке інше завдання програмування. Ви використовуєте інструменти для вирішення тієї задачі, яка найкраще підходить для цього завдання. У написанні асемблера немає нічого особливого; взагалі немає причин не писати це мовою високого рівня. C насправді знаходиться на досить низькому рівні, і я, мабуть, вважаю за краще C ++ або якусь іншу мову вищого рівня.

Мова складання насправді абсолютно непридатна для такого завдання. Випадки, коли ви розумно користуєтесь мовою складання, дуже і дуже рідкісні. Тільки тоді, коли потрібно робити речі, які неможливо виразити мовою вищого рівня.


1
Інші відповіді дуже хороші, але я вважаю, що ця є найпростішою, особливо з першими двома реченнями. Я говорив собі те саме, читаючи запитання.
MetalMikester

Ручне написання асемблерної мови на сьогоднішній день потрібне лише для апаратних злому. Наприклад, налаштування захищеного режиму на деяких процесорах вимагає певної послідовності інструкцій, а логічно еквівалентна послідовність недостатньо хороша. Практично всі звичайні програми не потребують певної послідовності інструкцій для завдання, яке їм потрібно виконати, і, як наслідок, немає підстав вимагати певної послідовності, а лише якийсь логічно еквівалентний набір інструкцій. Оптимізація компіляторів робить саме те, що покращує продуктивність виконання (кількість інструкцій, час настінного годинника, розмір кешу коду).
Мікко Ранталайнен

9

Що вони робили в минулому під час відсутності мови С? Чи писали Асемблер у машинному коді?

Збірка по суті мнемонічна для машинного коду; кожному опкоду на машинній мові надається мнемонічна збірка, тобто в x86 NOP - 0x90. Це робить асемблер досить простим (більшість асемблерів nb мають два проходи, один для перекладу, а другий для створення / вирішення адрес / посилань.) Перший асемблер був написаний і перекладений вручну (ймовірно, на папері) в машинний код. Краща версія написана та зібрана за допомогою ручного «зібраного» асемблера, нові функції додаються таким чином. Компілятори для нових мов можна побудувати таким чином; в минулому для компіляторів було звичайно виводити збірку і використовувати асемблер для їх заднього кінця!

Немає сенсу мені писати машинний перекладач для мови низького рівня мовою вищого рівня. ... [існуючі асемблери] написані в C. Мені також цікаво, чому вони написані на С?

  • Зазвичай простіше писати складнішу програму мовою вищого рівня.
  • Зазвичай потрібно більше коду та більше розумових зусиль для відстеження того, що ви робите мовою нижчого рівня, ніж вищою.
    • Один рядок C може перекласти багато інструкцій, напр. просте призначення в C ++ (або C) зазвичай генерує щонайменше 3 інструкції по збірці (завантажуйте, змінюйте, зберігайте;) На це може знадобитися двадцять інструкцій або більше (можливо, сотні), щоб зробити те, що можна зробити з одним рядком на більш високому рівні мова (як, наприклад, c ++ або c.), як правило, хочеться витратити свій час на вирішення проблеми, а не витрачати час на з'ясування способів реалізації рішення в машинному коді.

Хоча самостійний хостинг є загальною віхою / бажаною особливістю для мови програмування, збірка настільки низька, що більшість програмістів вважають за краще працювати на більш високому рівні. Тобто ніхто не хоче писати асемблера в зборі (або що-небудь ще)

Скажімо, ми створили абсолютно нову мікропроцесорну архітектуру, що для цієї архітектури не існує навіть компілятора С.

Запуск завантаження - це процес створення ланцюжка інструментів нової архітектури.

основний процес:

  • написати новий зворотній кінець, який розуміє, як генерувати код для нового CPU (або MCU)
  • складіть і протестуйте свій задній кінець
  • крос-компіляція потрібного компілятора (і ОС тощо), використовуючи новий бек-енд
  • перенести ці двійкові файли в нову систему

Не раз для цього вам потрібно писати в збірку (нову чи стару), для цього слід вибрати кращу мову, щоб написати асемблер / бек-енд / генератор коду.

Чи зможе наш ассемблер, написаний на мові C, імітувати нову архітектуру?

Асемблери не імітують!

Якщо хтось розробляв новий процесор з новою (або існуючою) машинною мовою, для тестування зазвичай необхідний тренажер; тобто запустіть випадкові інструкції та дані через тренажер та порівняйте вихід з тими ж інструкціями та даними на вашому процесорі прототипу. Потім знайдіть помилок, виправте помилки, повторіть.


3

Серед причин написання асемблера на C (або будь-якій іншій мові вищого рівня) є всі причини, які ви можете використовувати для виправдання написання будь-якої іншої програми на цій мові вищого рівня. Найважливішими серед них у цьому випадку є портативність та зручність використання.

Переносність: Якщо ви пишете асемблер рідною мовою, у вас є асемблер на цій платформі. Якщо ви пишете це на C, у вас є асемблер на будь-якій платформі з компілятором C. Це дозволяє, наприклад, компілювати код для вбудованої платформи на робочій станції та переміщувати двійковий файл, а не потрібно робити це все безпосередньо на цільовому пристрої.

Практичність: Для більшості людей читання, міркування та зміна програм набагато природніше, коли програма є мовою вищого рівня, ніж тоді, коли вона знаходиться в асемблері або (гірше) сировинному кодовому коді. Тому простіше розробити і підтримувати асемблер мовою вищого рівня, оскільки ти можеш думати з точки зору абстракцій, які надаються тобою мовами вищого рівня, а не думати про деталі, за які ти несеш відповідальність у нижчих.


3

Спеціально звертаючись лише до цієї частини питання:

"До речі, мені відомо, що GNU Assembler і Netwide Assembler написані в 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 в тому, що річ - це не просто великий резистор, багато чого працює, можливо, вам все ще знадобиться віджимання для речей, які ви не змогли повністю імітувати, але ви можете часто завантажуватися зараз на перший спій, не чекаючи третього чи четвертого віджиму,

У вашому зворотному автоматі, як можна було очікувати, ви берете в руки зібраний код і використовуєте його, щоб сказати, що завантажуєте програму зі стрічки або карт. Ви також передаєте код асемблера в машинному коді, можливо, він не є повноцінним, а таким, що спрощує програмування. Потім цей інструмент використовується для створення такого, який може працювати з більш досконалим або складним мовою (макроскладачем), а той, щоб створити ще один складний, і в кінцевому підсумку ви отримаєте FORTRAN або BASIC або B або що завгодно. І тоді ви починаєте думати про завантажувальну програму на тій же мові, переписуючи компілятор кросу, щоб він був нативним компілятором. звичайно, для цього вам в ідеалі потрібна середовище чи операційна система.

Коли ми створюємо або випробовуємо кремній, ми можемо / потрібно дивитися на сигнали, які є одними і нулями. За замовчуванням інструменти показують нам двійкові або шістнадцяткові, і за допомогою деяких інструментів можна навіть шукати вікна, щоб інструменти відображали мнемоніку (можливо, складання), але часто інженери (кремній / обладнання та програмне забезпечення) можуть читати досить машинного коду або використовуйте розбірку / перелік, щоб "побачити" інструкції.

Залежно від того, чим ви займаєтесь, ви можете просто занести якийсь машинний код у тестові вектори, а не переписувати та перекомпонувати чи перекомпонувати тест. Наприклад, якщо у вас є конвеєр і попередній вибір на певній глибині, вам може знадобитися або хочете заповнити минулу кінець програми деяку кількість куполів або інші реальні інструкції, щоб труба не мовчала за невизначеними інструкціями, і ви можете просто вибрати просто заповніть машинний код у списку / файлі найнижчого рівня, а не намагайтеся домогтися компілятора чи асемблера чи лінкера.

Під час тестування процесора, звичайно, вам потрібно мати справу з невизначеними та, можливо, не доглядаючи бітами і т. Д. Тому вам потрібно буде зайти в машинний код і змінити один або кілька конкретних бітів в інструкції в звичайно працюючій програмі. Чи варто написати програму для цього чи просто зробити це вручну. Так само під час тестування ECC ви хочете перевернути один або кілька бітів і побачити, що вони виправляються або потрапляють у пастку. Зрозуміло, що написати програму зробити набагато простіше, або ви можете просто це зробити вручну.

Тоді, звичайно, є мови, які не виробляють код, який працює на процесорі, ранній паскаль, java, python тощо. Для використання цих мов вам потрібен VM, написаний якоюсь іншою мовою. Ви не можете використовувати компілятор java, щоб зробити java vm, не має сенсу на основі дизайну мови.

(так впевнений, що після чистої реалізації цих мов, врешті-решт, хтось створює нечистий бекенд, який іноді може орієнтуватися на реальні набори інструкцій, а не набір інструкцій vm. Передній кінець gnu java для gcc, наприклад).

З часом і, можливо, все ще ми не пишемо компілятори C в C. Ми використовуємо такі речі, як зубр / флекс деяку іншу мову програмування, яку ми використовуємо для створення С для нас, що ми не хотіли писати самі. Деякий відсоток є на C, але деякий відсоток - в іншій мові, яка використовує інший компілятор, який вводить біти та видає інші біти. Іноді такий підхід використовується і для генерації асемблера. До дизайнера компілятора / асемблера (програми, які мають завдання вводити біти, а потім виводити інші біти) щодо того, як вони збираються його реалізувати. Розроблені програмою парсери можуть бути запрограмовані вручну, просто забираючи багато часу, тому люди шукають ярлик. Так само, як ви могли написати асемблера в асемблері, але люди шукають ярлик.

Веб-браузер - це лише програма, яка бере деякі біти і випилює деякі інші біти. Асемблер - це лише програма, яка бере деякі біти і випилює деякі інші біти. Компілятор - це лише програма, яка займає деякі біти і випилює деякі інші біти. І т. Д. Для всього цього існує документований набір правил для вхідних і вихідних бітів для кожного завдання програмування. Ці завдання та біти є загальними для того, щоб можна було використовувати будь-яку НАЛИЧНУ мову програмування (яка здатна робити маніпуляції бітами / байтами та працювати з входами та виходами). Тут доступний ключ. Отримайте і спробуйте Linux з нуля книги / підручника. Спробуйте pdp8 або pdp11 або altair 8800 або інший симулятор з імітованою передньою панеллю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.