2

Je suis intéressé à savoir comment compiler/créer un langage très simple (c'est-à-dire brainfuck) sans l'aide de fonctions de haut niveau comme les appels système Unix. Je voudrais écrire un compilateur pour le langage dans certains assemblages de bas niveau dépendant de la CPU, de sorte que je puisse fournir le code source dans le langage simple et finir avec un binaire. Pas sûr que ce soit clair ou pas, mais la question fondamentale est de savoir comment convertir la source en binaire sans l'aide de tout ce qui n'est pas déjà présent dans le matériel.Rédaction d'un langage de bas niveau à partir du matériel

Edit: Une problématique plus concise ...

DONNÉ:

-Hardware (carte mère/CPU, etc.)

PAS DONNÉ:

- UNIX/DOS

-C/FORTRAN/autres langues

Comment pourrais-je mettre en œuvre un langage simple comme brainfuck? Je suis conscient qu'il y a beaucoup plus de façons pratiques de compiler, mais cela m'intéresse à des fins éducatives.

Désolé si cette question est redondante ou évidente - je ne suis pas un informaticien alors peut-être que je ne connais pas le vocabulaire approprié pour trouver une solution au problème en ligne. Si quelqu'un peut fournir un lien ou un texte sur le sujet, il serait apprécié.

+1

Un peu plus pratique serait d'implémenter Forth from scratch sur un métal nu. Et puis vous serez capable de l'utiliser pour démarrer un système plus complexe en plus. –

+0

Ok ... ça ressemble à ce qui m'intéresse. Je suppose que par "métal nu" vous voulez dire uniquement du matériel? Y a-t-il un bon tutoriel pour le faire? Tout va bien ... comme je l'ai dit je ne suis intéressé qu'à des fins éducatives, donc si je comprends comment le faire avec Forth, je serai heureux. –

+0

Oui, il y a beaucoup de tutoriels, comme celui-ci: http://lambda-the-ultimate.org/node/2452 ou ceci: http://hbrobotics.org/wiki/index.php?title=Forth_for_Robotics_and_Programming_at_the_Bare_Metal –

Répondre

0

Le livre d'apprentissage de compilateur canonique est le livre du dragon, http://dragonbook.stanford.edu/.

Cependant, il est vraiment pointé vers plus ... Langues SOPHISTIQUÉES. Vous pourriez ne pas vouloir parler de l'analyse syntaxique hors-contexte et similaires (bien que je ne recommander ce livre, il est double, plus dur, mais IMPRESSIONNANT.)

Avec cela à l'esprit, vous voudrez peut-être commencer par trouver un interprète ou un compilateur pour un langage vraiment simple - peut-être Brainfuck lui-même, peut-être quelque chose d'un peu plus utilisable comme une implémentation de Scheme. Lisez, analysez, apprenez ce qu'il fait. Implémentez toutes les fonctions de bibliothèque de niveau inférieur que le compilateur utilise, ajustez son générateur de code pour générer la marque de code machine que vous souhaitez cibler, et vous avez terminé.

1

En regardant la description sur wikipedia, ce n'est pas une tâche difficile. Je commencerais probablement dans une langue que vous connaissez et peut-être même, peut-être pas. C est un bon choix. Fichier I/O est un petit ou grand projet en fonction de la plate-forme, etc. Inquiétude à ce sujet plus tard, compiler dans la "source" pour la langue. pour chaque caractère dans cette source, exécutez la tâche

> ++ptr; 
< --ptr; 
+ ++*ptr; 
etc 

puis de traduire cela en assemblage. vous n'avez besoin que d'un registre pour contenir ptr, quelques lignes de asm pour initialiser le tableau/ram et mettre le registre/ptr au début. Un autre registre pour parcourir le code source. vous recherchez seulement 8 caractères, vous pouvez si-alors-autrement votre chemin à travers ceux-ci à moins qu'il y ait une chose de modèle qui les rend plus faciles à traiter. Si vous voulez, vous pouvez créer une table de recherche de 256 octets et l'utiliser soit pour l'adresse du gestionnaire de cette instruction, soit pour la convertir en entier de 0 à 7, et l'utiliser dans une table de saut, peu importe.

Eh bien c'est un analyseur, pas nécessairement un compilateur.J'écrirais le compilateur en C ou un langage de haut niveau, il prend un tableau d'octets qui est le programme, pour chaque instruction que vous fournissez le code source asm qui implémente cette instruction, vous obtenez un octet inférieur à l'entrée, sortie (en utilisant ARM asm)

add r0,#1 

un signe moins

ldr r1,[r0] 
sub r1,#1 
str r1,[r0] 

r0 étant le registre ptr et r1 juste aider. Si vous êtes vraiment opposé à l'utilisation d'appels comme printf, alors faites de la sortie de ce code un tableau d'octets qui sont les ascii pour la source asm mettez chaque caractère a, d, d, espace, r, 0, virgule, #, 1, cr, lf et ainsi de suite. Assez facile à mettre en œuvre dans ASM ainsi que certains langage de haut niveau. Si vous voulez aller directement au binaire, il vous suffit de sortir le code machine, encore plus facilement.

Obtenir la chaîne source dans ce compilateur et la sortie dans un fichier qui peut être ensuite exécuté plus tard va probablement prendre des appels système. Vous pouvez éviter que la sortie soit un fichier si vous exécutez sur la même plate-forme et pouvez faire du code auto-modifiable dans le sens où vous construisez le code machine à une certaine adresse, puis lorsque vous avez fini d'analyser cette adresse.

Il a fallu plusieurs fois plus de temps pour écrire cette réponse que cela aurait été le cas pour implémenter la solution en C ou asm. Quelle est la difficulté exacte que vous rencontrez?

+0

ahh, en fait, l'entrée/sortie que le programme génère, point (.) et la virgule (,) sont problématiques car vous devez entrer dans ce qui pourrait être une quantité importante de code. Avoir votre sortie être un tableau dans RAM quelque part, ou si embarqué utiliser un uart et cracher les octets sur l'uart, qui est généralement des quantités minimales de code, quelques lignes pour la fonction uart putchar. entrée sur un uart est également minime. –

+0

Merci pour l'entrée ... il semble que mon problème a plus à voir avec le fichier I/O. Je sais comment écrire un compilateur dans C/assembly, mais je suis curieux de savoir comment on pourrait implémenter un langage en l'absence de telles commodités. Fondamentalement, comment pouvez-vous mettre en œuvre un langage si tout ce que vous avez reçu est le matériel (y compris probablement le BIOS)? J'espère que mon edit a clarifié la question. –

+0

@AJMedford: Eh bien, vous écrivez aussi vos pilotes d'E/S et de système de fichiers, si c'est ce qui vous manque. –

1

Vous pouvez compiler du code source intelligent dans des applications DOS .COM assez facilement (vous aurez également besoin de NASM ou de code supplémentaire pour émettre des codes d'instruction et calculer des sauts). Ci-dessous un interprète bf légèrement modifié, transformé en un compilateur de toutes sortes:

// file: bfcompil.c 

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

#define MAX_CODE_SIZE 30000 

char code[MAX_CODE_SIZE]; 
char* pc = &code[0]; 
char* pcEnd = &code[0]; 

#define MAX_DATA_SIZE 30000 

char data[MAX_DATA_SIZE] = { 0 }; 
char* pd = &data[0]; 

// Structures for quick bracket matching 
unsigned brStack[MAX_CODE_SIZE]; 
unsigned brSptr = 0; 
unsigned brMatch[MAX_CODE_SIZE]; 

int main(int argc, char** argv) 
{ 
    FILE* f = NULL; 
    int ch; 

    if (argc != 2) 
    { 
    fprintf(stderr, "usage:\n bfcompil <brainfuck-source-code-file>\n" 
        "bfcompil will output NASM-compilable source code for" 
        "a DOS program\n"); 
    return EXIT_FAILURE; 
    } 

    if ((f = fopen(argv[1], "rb")) == NULL) 
    { 
    fprintf(stderr, "can't open file \"%s\" for reading\n", argv[1]); 
    return EXIT_FAILURE; 
    } 

    while ((ch = getc(f)) != EOF) 
    { 
    if (strchr(" \t\r\n", ch) != NULL) // skip white space 
    { 
     continue; 
    } 
    else if (strchr("><+-.,[]", ch) != NULL) // store valid commands 
    { 
     if (pcEnd >= &code[sizeof(code)]) 
     { 
     fprintf(stderr, "too many commands in file \"%s\", expected at most " 
         "%u commands\n", argv[1], (unsigned)sizeof(code)); 
     fclose(f); 
     return EXIT_FAILURE; 
     } 

     if (ch == '[') 
     { 
     brStack[brSptr++] = (unsigned)(pcEnd - &code[0]); 
     } 
     else if (ch == ']') 
     { 
     if (brSptr == 0) 
     { 
      fprintf(stderr, "unmatched ']' in file \"%s\"\n", argv[1]); 
      fclose(f); 
      return EXIT_FAILURE; 
     } 

     brSptr--; 
     brMatch[brStack[brSptr]] = (unsigned)(pcEnd - &code[0]); 
     brMatch[pcEnd - &code[0]] = brStack[brSptr]; 
     } 

     *pcEnd++ = ch; 
    } 
    else // fail on invalid commands 
    { 
     fprintf(stderr, "unexpected character '%c' in file \"%s\", valid command " 
         "set is: \"><+-.,[]\"\n", ch, argv[1]); 
     fclose(f); 
     return EXIT_FAILURE; 
    } 
    } 

    fclose(f); 

    if (brSptr != 0) 
    { 
    fprintf(stderr, "unmatched '[' in file \"%s\"\n", argv[1]); 
    return EXIT_FAILURE; 
    } 

    if (pcEnd == &code[0]) 
    { 
    fprintf(stderr, "no commands found in file \"%s\"\n", argv[1]); 
    return EXIT_FAILURE; 
    } 

    printf("; how to compile: nasm -f bin <input file with this code.asm> -o " 
     "<output executable.com>\n\n" 
     "org 0x100\n" 
     "bits 16\n\n" 
     " mov  bx, data\n" 
     " mov  di, bx\n" 
     " mov  cx, 30000\n" 
     " xor  al, al\n" 
     " cld\n" 
     " rep  stosb\n\n" 
     " jmp  code\n\n" 
     "print:\n" 
     " mov  ah, 2\n" 
     " cmp  byte [bx], 10\n" 
     " jne  lprint1\n" 
     " mov  dl, 13\n" 
     " int  0x21\n" 
     "lprint1:\n" 
     " mov  dl, [bx]\n" 
     " int  0x21\n" 
     " ret\n\n" 
#if 01 
     // buffered input 
     "input:\n" 
     " cmp  byte [kbdbuf+1], 0\n" 
     " jne  linput1\n" 
     " mov  ah, 0xa\n" 
     " mov  dx, kbdbuf\n" 
     " int  0x21\n" 
     " inc  byte [kbdbuf+1]\n" 
     "linput1:\n" 
     " mov  al, [kbdbuf+2]\n" 
     " cmp  al, 13\n" 
     " jne  linput4\n" 
     " mov  al, 10\n" 
     "linput4:\n" 
     " mov  [bx], al\n" 
     " mov  si, kbdbuf+3\n" 
     " mov  di, kbdbuf+2\n" 
     " xor  cx, cx\n" 
     " dec  byte [kbdbuf+1]\n" 
     " mov  cl, [kbdbuf+1]\n" 
     " jz  linput3\n" 
     "linput2:\n" 
     " lodsb\n" 
     " stosb\n" 
     " loop linput2\n" 
     "linput3:\n" 
     " ret\n\n" 
#else 
     // unbuffered input 
     "input:\n" 
     " mov  ah, 1\n" 
     " int  0x21\n" 
     " cmp  al, 13\n" 
     " jne  linput\n" 
     " mov  al, 10\n" 
     "linput:\n" 
     " mov  [bx], al\n" 
     " ret\n\n" 
#endif 
     "code:\n\n"); 

    for (pc = &code[0]; pc < pcEnd; pc++) 
    { 
    switch (*pc) 
    { 
    case '>': 
     printf(" inc  bx\n"); 
     break; 
    case '<': 
     printf(" dec  bx\n"); 
     break; 
    case '+': 
     printf(" inc  byte [bx]\n"); 
     break; 
    case '-': 
     printf(" dec  byte [bx]\n"); 
     break; 
    case '.': 
     printf(" call print\n"); 
     break; 
    case ',': 
     printf(" call input\n"); 
     break; 
    case '[': 
     printf("label%u:\n", (unsigned)(pc - &code[0])); 
     printf(" cmp  byte [bx], 0\n"); 
     printf(" je  label%u\n", (unsigned)brMatch[pc - &code[0]]); 
     break; 
    case ']': 
     printf(" jmp  label%u\n", brMatch[pc - &code[0]]); 
     printf("label%u:\n", (unsigned)(pc - &code[0])); 
     break; 
    } 
    } 

    printf("\n ret\n\n"); 
    printf("kbdbuf:\n" 
     " db  254\n" 
     " db  0\n" 
     " times 256 db 0\n\n"); 
    printf("data:\n"); 

    return EXIT_SUCCESS; 
} 

Si vous alimentez le programme Bonjour tout le monde:

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. 

Il va produire un code d'assemblage compilable:

; how to compile: nasm -f bin <input file with this code.asm> -o <output executable.com> 

org 0x100 
bits 16 

    mov  bx, data 
    mov  di, bx 
    mov  cx, 30000 
    xor  al, al 
    cld 
    rep  stosb 

    jmp  code 

print: 
    mov  ah, 2 
    cmp  byte [bx], 10 
    jne  lprint1 
    mov  dl, 13 
    int  0x21 
lprint1: 
    mov  dl, [bx] 
    int  0x21 
    ret 

input: 
    cmp  byte [kbdbuf+1], 0 
    jne  linput1 
    mov  ah, 0xa 
    mov  dx, kbdbuf 
    int  0x21 
    inc  byte [kbdbuf+1] 
linput1: 
    mov  al, [kbdbuf+2] 
    cmp  al, 13 
    jne  linput4 
    mov  al, 10 
linput4: 
    mov  [bx], al 
    mov  si, kbdbuf+3 
    mov  di, kbdbuf+2 
    xor  cx, cx 
    dec  byte [kbdbuf+1] 
    mov  cl, [kbdbuf+1] 
    jz  linput3 
linput2: 
    lodsb 
    stosb 
    loop linput2 
linput3: 
    ret 

code: 

    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
label10: 
    cmp  byte [bx], 0 
    je  label41 
    inc  bx 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  bx 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  bx 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  bx 
    inc  byte [bx] 
    dec  bx 
    dec  bx 
    dec  bx 
    dec  bx 
    dec  byte [bx] 
    jmp  label10 
label41: 
    inc  bx 
    inc  byte [bx] 
    inc  byte [bx] 
    call print 
    inc  bx 
    inc  byte [bx] 
    call print 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    call print 
    call print 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    call print 
    inc  bx 
    inc  byte [bx] 
    inc  byte [bx] 
    call print 
    dec  bx 
    dec  bx 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    call print 
    inc  bx 
    call print 
    inc  byte [bx] 
    inc  byte [bx] 
    inc  byte [bx] 
    call print 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    call print 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    dec  byte [bx] 
    call print 
    inc  bx 
    inc  byte [bx] 
    call print 
    inc  bx 
    call print 

    ret 

kbdbuf: 
    db  254 
    db  0 
    times 256 db 0 

data: 

Si vous le compilez, vous pourrez l'exécuter sous DOS, Windows 9x/XP (probablement Vista-7 32 bits) et dans DosBox.

La sortie, sans surprise, est la suivante:

Hello World! 

UPDATE: L'entrée DOS et des routines de sortie dans le code ci-dessus peuvent être remplacés par des accès directs à la mémoire tampon d'écran et les ports de clavier. Le code du clavier devra également gérer les interruptions du clavier. Ce n'est pas très difficile à faire sur le PC x86. Vous pouvez vraiment implémenter un compilateur pour qu'un langage s'exécute sur du matériel nu sans OS.

Vous devriez également jeter un oeil à Forth car c'est exactement le genre de langage pour l'environnement donné. Et c'est facile à mettre en œuvre. Beaucoup plus facile que C. Harder que brainfuck, un peu comparable à l'assemblage.

MISE À JOUR 2: Voici un petit (~ 1 Ko en taille) interprète brainfuck qui est de ne pas utiliser toutes les fonctionnalités DOS ou BIOS:

; file: bfint.asm 
; compile: nasm.exe -f bin bfint.asm -o bfint.com 
; run in: DOS, DosBox or equivalent 

bits 16 
org 0x100 

section .text 

SCREEN_WIDTH equ 80 
SCREEN_HEIGHT equ 25 
SCAN_BUF_SIZE equ 256 

MAX_CODE_SIZE equ 20000 
MAX_DATA_SIZE equ 30000 

    cld 

    ; set new keyboard (IRQ1) ISR 
    push byte 0 
    pop  es 
    cli       ; update ISR address w/ ints disabled 
    mov  word [es:9*4], Irq1Isr 
    mov  [es:9*4+2], cs 
    sti 

    push cs 
    pop  es 

Restart: 

    call ClearScreen 
    mov  si, MsgHello 
    call PrintStr 

    mov  word [CodeSize], 0 
    mov  byte [EnterCount], 0 

WaitForKey: 
    call GetKey 

    ; Escape erases code 
    cmp  ah, 1  ; Escape 
    je  Restart 

    ; Non-characters are ignored 
    cmp  al, 0  ; non-character key 
    je  WaitForKey 

    ; Enter is "printed" but not stored, use for formatting 
    cmp  al, 10  ; Enter 
    je  KeyEnter 
    mov  byte [EnterCount], 0 

    ; Backspace deletes last character 
    cmp  al, 8  ; Backspace 
    je  KeyBackspace 

    ; Space is printed but not stored, use for formatting 
    cmp  al, " " ; Space 
    je  PrintOnly 

    ; 0 runs a test program 
    cmp  al, "0" 
    je  TestProgram 

    ; Other chracters are stored as code 
    mov  bx, [CodeSize] 
    cmp  bx, MAX_CODE_SIZE 
    jae  ErrCodeTooBig 
    mov  [Code + bx], al 
    inc  word [CodeSize] 
PrintOnly: 
    call PrintChar 
    jmp  WaitForKey 

ErrCodeTooBig: 
    mov  si, MsgCodeTooBig 
    call PrintStr 
    mov  word [CodeSize], 0 
    jmp  WaitForKey 

KeyEnter: 
    call PrintChar 
    inc  byte [EnterCount] 
    cmp  byte [EnterCount], 1 
    je  WaitForKey 
    mov  byte [EnterCount], 0 
    call Execute 
    jmp  WaitForKey 

KeyBackspace: 
    call PrintChar 
    cmp  word [CodeSize], 0 
    je  WaitForKey 
    dec  word [CodeSize] 
    jmp  WaitForKey 

TestProgram: 
    mov  si, TestCode 
    mov  di, Code 
    mov  cx, TestCodeEnd - TestCode 
    mov  [CodeSize], cx 
    rep  movsb 
    call Execute 
    jmp  WaitForKey 

Execute: 
    mov  si, Code ; code start 
    xor  bp, bp ; instruction index 

    mov  di, Data ; data start 
    mov  cx, MAX_DATA_SIZE 
    xor  al, al 
    rep  stosb 
    sub  di, MAX_DATA_SIZE 
    xor  bx, bx ; data index 

ExecuteLoop: 
    cmp  bp, [CodeSize] 
    jae  ExecuteDone 

    mov  al, [bp+si] 
    cmp  al, ">" 
    je  IncPtr 
    cmp  al, "<" 
    je  DecPtr 
    cmp  al, "+" 
    je  IncData 
    cmp  al, "-" 
    je  DecData 
    cmp  al, "." 
    je  PrintData 
    cmp  al, "," 
    je  InputData 
    cmp  al, "[" 
    je  While 
    cmp  al, "]" 
    je  EndWhile 

    mov  si, MsgInvalidChar 
    call PrintStr 
    call PrintChar 
    mov  al, 10 
    call PrintChar 
    jmp  ExecuteDone 

IncPtr: 
    inc  bx 
    jmp  ExecuteContinue 

DecPtr: 
    dec  bx 
    jmp  ExecuteContinue 

IncData: 
    inc  byte [bx+di] 
    jmp  ExecuteContinue 

DecData: 
    dec  byte [bx+di] 
    jmp  ExecuteContinue 

PrintData: 
    mov  al, [bx+di] 
    call PrintChar 
    jmp  ExecuteContinue 

InputData: 
    call GetKey 
    or  al, al 
    jz  InputData 
    mov  [bx+di], al 
    jmp  ExecuteContinue 

While: 
    cmp  byte [bx+di], 0 
    jne  ExecuteContinue 
    mov  ax, 1 
    mov  dx, "[]" 
    call FindMatchingBracket 
ExecuteContinue: 
    inc  bp 
    jmp  ExecuteLoop 

EndWhile: 
    mov  ax, -1 
    mov  dx, "][" 
    call FindMatchingBracket 
    jmp  ExecuteLoop 

ExecuteDone: 
    mov  word [CodeSize], 0 
    mov  si, MsgCompleted 
    jmp  PrintStr 

FindMatchingBracket: 
    xor  cx, cx 
FindMatchingBracket1: 
    cmp  byte [bp+si], dl 
    jne  FindMatchingBracket2 
    inc  cx 
    jmp  FindMatchingBracket3 
FindMatchingBracket2: 
    cmp  byte [bp+si], dh 
    jne  FindMatchingBracket3 
    dec  cx 
    jnz  FindMatchingBracket3 
    ret 
FindMatchingBracket3: 
    add  bp, ax 
    jmp  FindMatchingBracket1 

; Inputs: 
; AL = ASCII character code 
PrintChar: 
    ; assuming it's a color text mode (not monochrome or graphics) 
    pusha 
    push es 

    push word 0xb800 
    pop  es 
    mov  bx, [CursorPos] 

    cmp  al, 8 
    je  PrintCharBackSpace 

    cmp  al, 10 
    je  PrintCharBackLF 

    cmp  al, 13 
    je  PrintCharBackCR 

    mov  [es:bx], al 
    call AdvanceCursorPosition 

    jmp  PrintCharDone 

PrintCharBackSpace: 
    ; move the cursor back and erase the last character 
    or  bx, bx 
    jz  PrintCharDone 
    dec  bx 
    dec  bx 
    mov  word [es:bx], 0x0720 
    jmp  PrintCharSetCursorPos 

PrintCharBackLF: 
    ; move the cursor to the beginning of the next line - '\n' behavior 
    add  bx, SCREEN_WIDTH * 2 
    cmp  bx, SCREEN_WIDTH * SCREEN_HEIGHT * 2 
    jc  PrintCharBackCR 
    sub  bx, SCREEN_WIDTH * 2 
    call ScrollUp 

PrintCharBackCR: 
    ; move the cursor to the beginning of the current line - '\r' behavior 
    mov  ax, SCREEN_WIDTH * 2 
    xchg ax, bx 
    xor  dx, dx 
    div  bx 
    mul  bx 
    mov  bx, ax 

PrintCharSetCursorPos: 
    mov  [CursorPos], bx 
    shr  bx, 1 
    call SetCursorPosition 

PrintCharDone: 
PopEsAllRet: 
    pop  es 
    popa 
    ret 

ClearScreen: 
    ; assuming it's a color text mode (not monochrome or graphics) 
    pusha 
    push es 

    push word 0xb800 
    pop  es 
    xor  di, di 
    mov  cx, SCREEN_WIDTH * SCREEN_HEIGHT 
    mov  ax, 0x0720 ; character = space, color = lightgray on black 
    rep  stosw 

    xor  bx, bx 
    mov  [CursorPos], bx 
    call SetCursorPosition 

    jmp  PopEsAllRet 

ScrollUp: 
    ; assuming it's a color text mode (not monochrome or graphics) 
    pusha 
    push es 
    push ds 

    push word 0xb800 
    pop  es 
    push es 
    pop  ds 
    mov  si, SCREEN_WIDTH * 2 
    xor  di, di 
    mov  cx, SCREEN_WIDTH * (SCREEN_HEIGHT - 1) 
    rep  movsw 

    mov  cx, SCREEN_WIDTH 
    mov  ax, 0x0720 ; character = space, color = lightgray on black 
    rep  stosw 

    pop  ds 
    jmp  PopEsAllRet 

; Inputs: 
; DS:SI = address of NUL-terminated ASCII string 
PrintStr: 
    pusha 
PrintStr1: 
    lodsb 
    or  al, al 
    jz  PrintStrDone 
    call PrintChar 
    jmp  PrintStr1 
PrintStrDone: 
    popa 
    ret 

; Inputs: 
; BX = Y * SCREEN_WIDTH + X 
SetCursorPosition: 
    ; assuming it's a color text mode (not monochrome or graphics) 
    pusha 

%if 0 
    mov  dx, 0x3d4 
    mov  al, 0x0f 
    out  dx, al 
    inc  dx 
    mov  al, bl 
    out  dx, al 

    dec  dx 
    mov  al, 0x0e 
    out  dx, al 
    inc  dx 
    mov  al, bh 
    out  dx, al 
%else 
    mov  dx, 0x3d4 
    mov  al, 0x0f 
    mov  ah, bl 
    out  dx, ax 

    dec  al 
    mov  ah, bh 
    out  dx, ax 
%endif 

    popa 
    ret 

AdvanceCursorPosition: 
    ; assuming it's a color text mode (not monochrome or graphics) 
    pusha 

    mov  ax, [CursorPos] 
    inc  ax 
    inc  ax 
    cmp  ax, SCREEN_WIDTH * SCREEN_HEIGHT * 2 
    jc  AdvanceCursorPosition1 

    sub  ax, SCREEN_WIDTH * 2 
    call ScrollUp 

AdvanceCursorPosition1: 
    mov  [CursorPos], ax 
    shr  ax, 1 
    xchg ax, bx 
    call SetCursorPosition 

    popa 
    ret 

; Outputs: 
; AH = scan code 
; AL = character 
GetKey: 
    push bx 
    push si 

GetKeyRepeat: 
    mov  ax, [ScanWriteIdx] 
    mov  si, [ScanReadIdx] 
    sub  ax, si 
    jz  GetKeyRepeat 
    mov  bx, si 
    mov  ax, [ScanBuf + bx + si] 
    inc  si 
    and  si, SCAN_BUF_SIZE - 1 
    mov  [ScanReadIdx], si 

    pop  si 
    pop  bx 
    ret 

Irq1Isr: 
    pusha 
    push ds 

    push cs 
    pop  ds 

    ; read keyboard scan code 
    in  al, 0x60 

    cmp  al, 0x2a ; Left Shift down 
    jne  Irq1Isr1 
    or  byte [Shift], 1 
Irq1Isr1: 
    cmp  al, 0x36 ; Right Shift down 
    jne  Irq1Isr2 
    or  byte [Shift], 2 
Irq1Isr2: 
    cmp  al, 0xaa ; Left Shift up 
    jne  Irq1Isr3 
    and  byte [Shift], ~1 
Irq1Isr3: 
    cmp  al, 0xb6 ; Right Shift up 
    jne  Irq1Isr4 
    and  byte [Shift], ~2 
Irq1Isr4: 

    test al, 0x80 
    jnz  Irq1IsrEois ; key released 

    mov  ah, al 
    cmp  al, 58 
    jc  Irq1Isr5 
    xor  al, al ; don't translate non-character keys 
    jmp  Irq1Isr7 
Irq1Isr5: 
    mov  bx, ScanToChar 
    cmp  byte [Shift], 0 
    je  Irq1Isr6 
    add  bx, ScanToCharShift - ScanToChar 
Irq1Isr6: 
    xlatb 

Irq1Isr7: 
    mov  bx, [ScanWriteIdx] 
    mov  di, bx 
    mov  [ScanBuf + bx + di], ax 
    inc  bx 
    and  bx, SCAN_BUF_SIZE - 1 
    mov  [ScanWriteIdx], bx 

Irq1IsrEois: 
%if 0 
    ; send EOI to XT keyboard 
    in  al, 0x61 
    mov  ah, al 
    or  al, 0x80 
    out  0x61, al 
    mov  al, ah 
    out  0x61, al 
%endif 

    ; send EOI to master PIC 
    mov  al, 0x20 
    out  0x20, al 

    pop  ds 
    popa 
    iret 

ScanToChar: 
    db  0 ; unused 
    db  0 ; Escape 
    db  "1234567890-=" 
    db  8 ; Backspace 
    db  9 ; Tab 
    db  "qwertyuiop[]" 
    db  10 ; Enter 
    db  0 ; Ctrl 
    db  "asdfghjkl;'`" 
    db  0 ; Left Shift 
    db  "\zxcvbnm,./" 
    db  0 ; Right Shift 
    db  0 ; Print Screen 
    db  0 ; Alt 
    db  " " ; Space 
ScanToCharShift: 
    db  0 ; unused 
    db  0 ; Escape 
    db  "[email protected]#$%^&*()_+" 
    db  8 ; Backspace 
    db  9 ; Tab 
    db  "QWERTYUIOP{}" 
    db  10 ; Enter 
    db  0 ; Ctrl 
    db  'ASDFGHJKL:"~' 
    db  0 ; Left Shift 
    db  "|ZXCVBNM<>?" 
    db  0 ; Right Shift 
    db  0 ; Print Screen 
    db  0 ; Alt 
    db  " " ; Space 

MsgHello: 
    db  "Brainfuck Interpreter", 10, 10 
    db  "Press 0 to run test code OR", 10 
    db  "Type your code.", 10 
    db  "Use Esc to erase it all or Backspace to delete last character.", 10 
    db  "Press Enter twice to run it.", 10, 10, 0 

MsgCodeTooBig: 
    db  10, "Code's too big", 10, 0 

MsgCompleted: 
    db  10, "Code's completed", 10, 0 

MsgInvalidChar: 
    db  10, "Invalid character: ", 0 

Shift   db  0 

CursorPos  dw  0 

ScanReadIdx  dw  0 
ScanWriteIdx dw  0 

EnterCount  db  0 

CodeSize  dw  0 

TestCode: 
    ; Hello World! 
    db "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." 
    ; Squares of 0 through 100 
; db "++++[>+++++<-]>[<+++++>-]+<+[>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-]" 
    ; ROT13 
; db "+[,+[-[>+>+<<-]>[<+>-]+>>++++++++[<-------->-]<-[<[-]>>>+[<+<+>>-]<[>+<-]<[<++>>>+[<+<->>-]<[>+<-]]>[<]<]>>[-]<<<[[-]<[>>+>+<<<-]>>[<<+>>-]>>++++++++[<-------->-]<->>++++[<++++++++>-]<-<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<<<<+>>>>++++[<++++++++>-]>-]<<-<-]>[<<<<[-]>>>>[<<<<->>>>-]]<<++++[<<++++++++>>-]<<-[>>+>+<<<-]>>[<<+>>-]+>>+++++[<----->-]<-[<[-]>>>+[<+<->>-]<[>+<-]<[<++>>>+[<+<+>>-]<[>+<-]]>[<]<]>>[-]<<<[[-]<<[>>+>+<<<-]>>[<<+>>-]+>------------[<[-]>>>+[<+<->>-]<[>+<-]<[<++>>>+[<+<+>>-]<[>+<-]]>[<]<]>>[-]<<<<<------------->>[[-]+++++[<<+++++>>-]<<+>>]<[>++++[<<++++++++>>-]<-]>]<[-]++++++++[<++++++++>-]<+>]<.[-]+>>+<]>[[-]<]<]" 
TestCodeEnd: 

section .bss 

ScanBuf: 
    resw SCAN_BUF_SIZE 

Code: 
    resb MAX_CODE_SIZE 

Data: 
    resb MAX_DATA_SIZE 

Si vous voulez prendre DOS (comme l'environnement d'hébergement) et MSNA Hors de l'image, vous êtes invités à encoder le code d'assemblage ci-dessus à la main, en faire une disquette de démarrage et le démarrer.

+0

vous effectuez des appels système, faites-le sans appels système. Dans certains cas, la vidéo et le clavier sont assez basiques (des centaines à des milliers de lignes de code de haut niveau), mais les systèmes de fichiers et le contrôle du disque dur sans appel système (l'instruction int) ne sont pas si triviaux. Même après le montage de l'affiche doit clarifier la question, peut-être avec quelques exemples de ce qui est acceptable et ce qui ne l'est pas. –

+0

il a clairement indiqué dans certains assemblage de niveau bas dépendant du CPU. Ce n'est pas un compilateur qui se compile dans le même langage. Je veux la définition des appels système de haut niveau, je considère que c'est vague et facile de débattre de la définition dans ce contexte. Si un appel int21 est correct alors il en est de même pour putchar et il suffit de lier asm ou C ou quoi avec une librairie.la définition détermine s'il s'agit d'une tâche de programmation de 5 minutes ou du reste de votre tâche de vie ou quelque part au milieu. –

+0

@dwelch: J'espère que l'interprète ajouté répond aux exigences. –

0

En fait, j'ai aussi un projet similaire en tête. Ce que vous voulez, c'est écrire un compilateur qui fonctionne sur du matériel nu (sans système d'exploitation). Un compilateur est juste un programme comme tous les autres programmes, sauf qu'il fonctionnera sur du matériel nu comme dans votre cas.

Vous pouvez envisager d'utiliser un chargeur de démarrage pour votre compilateur (un chargeur de démarrage n'est pas un système d'exploitation). Le bootloader charge normalement un système d'exploitation à partir du disque dans la mémoire pour l'exécution seulement cette fois il charge votre compilateur plutôt qu'un système d'exploitation.

Il existe de nombreux chargeurs de démarrage gratuits et open-source (google pour eux). Choisissez celui qui convient à vos besoins ou si vous êtes aussi curieux/curieux que moi, vous pourriez même écrire le vôtre.

Choisir un bootloader (ou en écrire un) nécessite que vous sachiez comment votre système cible démarre - soit via le BIOS (ancien) ou UEFI (nouveau, il remplace le BIOS). À mesure que votre langage de programmation évolue, vous pouvez écrire votre propre chargeur de démarrage dans votre nouveau langage de programmation.

Espérons que cela aide

Questions connexes