RESUME-TP3.md 18 KB

RESUME — TP3-comp : "AU COEUR DE TURBO PASCAL" (John Colibri, Éditions Mnémodyne)

Analyse des 6 volumes OCRisés (Resources/coeur-tp-ocr/coeur-tp-*-d.txt) pour le projet TP3-comp (compilateur Turbo Pascal 3 → 8086 asm). OCR français (tesseract -l fra --psm 3), reconstruit à partir des colonnes opcode/adresse (OCR fréquemment faux : :=→\:=, *→\, instruction MOT). Ne vous fiez jamais au champ mnémonique seul — recalculez depuis les octets.


1. STRUCTURE DU LIVRE (plan confirmé)

  • Vol. 1 (coeur-tp-1-d.txt, 19 p.) : couverture / plan.
  • Vol. 2-3 (-02 26 p., -03 29 p.) : ch.4 IMPLÉMENTATION DES TYPES.
  • Vol. 4 (-04 46 p.) : ch.5 LE CODE COMPILE (5.1-5.5).
  • Vol. 5 (-05 42 p.) : ch.5 suite (5.5-5.7 + synthèse 5.A).
  • Vol. 6 (-06 59 p.) : ch.6 LES OPTIONS DE COMPILATION + début ch.7 LA LIBRAIRIE.

Total vérifié : 221 pages. Seuls les fichiers *_d.txt sont valides (les autres ont été supprimés).


2. CHAPITRE 4 — IMPLÉMENTATION DES TYPES

Entiers (5.3.x / ch.4)

  • 16 bits, complément à deux, bornes -32768..32767.
  • Petits-boutistes (octet faible d'abord), convention 8086/8088.
  • Débordement = wrap : 50000 → -15536.
  • READLN d'un entier > MAXINT → erreur runtime 10.
  • Un littéral > MAXINT est rejeté à la compilation.
  • CHAR : 1 octet, ASCII étendu (0..255).
  • BOOLEAN : 1 octet, FALSE = 0, TRUE = bit 0.
  • Énumérations : 1 octet si < 256 valeurs, sinon 2 octets petits-boutistes.
  • Sous-intervalles : 1 octet si les 2 bornes dans 0..255.

REAL (6 octets)

  • 1 octet d'exposant (complément-à-128 de l'exposant binaire, en premier), puis 1 bit de signe + mantisse 39 bits (octet le plus faible en premier en mémoire).

REAL BCD (10 octets)

  • Bit 79 : signe ; bits 78-72 : exposant en complément-à-63 (puissances de 10) ; 72 bits de chiffres.
  • Mémoire : octet signe/exposant puis 9 octets de mantisse.
  • Exemple : 48.9375 → $41 $00 $00 $00 $00 $75 $93 $48.

REAL 8087 (8 octets)

  • Bit 63 : signe ; bits 52-62 : exposant complément-à-$3FF ; mantisse 52 bits.

Tableaux

  • Mémoire contiguë, pas de compactage de bits.
  • 2 dimensions : parcours ligne-major, le dernier indice déclaré varie le plus vite.
  • Tableaux de CHAR compatibles avec les constantes chaîne pour WRITE et affectation, mais PAS pour READLN.

Chaines STRING

  • Longueur dans l'octet 0, puis les caractères ; taille mémoire = longueur_max + 1.
  • Maximum 255.
  • Option V- : désactive la vérification (débordement) des chaînes.

Enregistrements

  • Champs juxtaposés, non compactés.
  • Variants : l'enregistrement occupe la taille du variant le plus grand.
  • Variants libres (sans sélecteur) : pas d'octet de sélecteur.

Ensembles (SET)

  • 1 bit par élément. Plus grand ensemble 0..255 = 32 octets.
  • Ensembles hybrides (bornes non 0/255) : seuls les octets entiers couvrant le sous-intervalle sont utilisés (ex. 12..17 → octets 1-2).
  • En mémoire, l'octet de poids le plus fort vient en dernier.
  • Valeurs hors bornes : placées si codables, sinon ignorées/écrasées.

Pointeurs

  • 4 octets : décalage (offset) en premier, puis segment. NIL = 0.
  • Tas : entre la fin des globales et le sommet de la pile Pascal.
  • Le tas grandit par blocs de 8 octets (décalage $0000/$0008).
  • NEW réserve la place + bourrage à un multiple de 8.
  • Variable dynamique : doit rester < 64K.
  • Variant-pointeur NEW(n, selecteur) : non implanté.

Fichiers

  • Programmes .COM chargés à $0100.
  • Fichiers de données : enregistrements en continu les uns derrière les autres.
  • Fichiers FILE (blocs) : BLOCKREAD/BLOCKWRITE.
  • Fichiers texte : fins de ligne $0D $0A ; EOF = CTRL-Z $1A ; les fichiers TEXT n'ajoutent pas de $1A final.

3. CHAPITRE 5 — LE CODE COMPILÉ

3.1 Squelette de programme (5.2, 5.6, 5.7)

2D4D  E849DD     call   TU_PARAGRAPHS
2D50  0600       hex    0600     ; options
2D52  ....       hex    ....     ; cs
2D54  ....       hex    ....     ; ds
2D56  ....       hex    ....     ; code
2D58  ....       hex    ....     ; globales
2D5A  ....       hex    ....     ; min piles
2D5C  ....       hex    ....     ; max piles
2D5E  ....       hex    ....     ; fichiers
2D60  ....       hex    ....     ; input
2D62  ....       hex    ....     ; output
2D64  8BEC       mov    bp, sp
2D66  E814DF     call   TU_TAILLE_CODE
2D69  ....       hex    ....     ; taille du code
2D6B  ....       adr    SAUT_PRINCIPAL
2D6D  E9xxxx     SAUT_PRINCIPAL jmp PRINCIPAL
      ; ... ici : procédures + constantes typées ...
PRINCIPAL:
      ; ... code du programme ...
      jmp    FIN
      ; ... relais GOTO hors FOR : RELAI_GOTO_n: pop ax; jmp cible ...
FIN:
      xor    ax, ax
      call   TU_HALT
      hex    0000
  • l'adresse de retour PUSH du CALL : octet faible poussé d'abord, SP diminue de 2.
  • RET dépile dans le compteur de programme (PC).

3.2 Arithmétique entière (5.3.1.1)

Pascal 8086 généré
a * b mov ax,a ; mov cx,const ; imul cx
a DIV b ... ; cwd ; idiv cx
a MOD b ... ; idiv cx ; xchg dx,ax
SQR(x) mov ax,x ; imul ax
SUCC(x) inc ax
PRED(x) dec ax
  • Arbres d'expressions : les intermédiaires passent par la pile (push ax / pop cx). Exemple :
mov    ax, $11
mov    cx, $22
imul   cx
push   ax
mov    ax, $33
push   ax
mov    ax, $44
add    ax, $55
pop    cx
imul   cx
pop    cx
add    ax, cx

3.3 Comparaisons booléennes (5.3.1.2)

Résultat calculé dans AX (1 pour vrai, 0 pour faux) :

mov    ax, $0001
Jcc    saute      ; Jcc = je/jg/jnle/jnl/jl/jle/jnz selon l'opérateur
dec    ax
saute:
; avec R+ :
mov    cx, $0000
mov    dx, $0001
call   TU_RANGE_CHECK      ; bornes dans CX,DX
mov    [var], al           ; booléen = 1 octet

3.4 Affectations / accès

  • Valeur globale : mov ax,[$0262] (première globale = $0262).
  • Écriture : mov [addr], ax / mov [addr], al.
  • Dans une affectation, le calcul d'adresse peut être séparé des instructions de stockage ; les adresses calculées sont sauvegardées sur la pile.
  • Accès par sélecteurs (tableaux/enregistrements/pointeurs) :
    • calcul direct si le compilateur connaît les valeurs,
    • sinon DI porte le décalage + une constante relative ; ES sert de segment.
  • WITH : l'adresse de base est poussée sur la pile puis récupérée dans ES:DI à l'aide de BP (les di,[bp+$FC]).

3.5 IF (5.6.1)

mov    ax, var
cmp    ax, imm
je     THEN1
jmp    FIN_IF1
THEN1: ; ... corps ...
FIN_IF1:

IF/ELSE :

je     THEN2
jmp    ELSE2
THEN2: ; ... corps THEN ...
jmp    FIN_IF2
ELSE2: ; ... corps ELSE ...
FIN_IF2:
  • IF TRUE THEN ; → mov ax,$0001 ; or ax,ax ; jnz ....
  • Pas d'optimisation du test booléen : les deux opérandes sont toujours évaluées.

3.6 CASE (5.6.2)

  • Cascade : cmp ax,label ; je cas_n ; jmp test_suivant.
  • Intervalles : cmp ax,lo ; jl suivant ; cmp ax,hi ; jle cas_n.
  • Étiquette hors bornes énorme : inoffensif cmp ax,$AAAA ; je cas.
  • Chaque corps de cas se termine par jmp FIN_CASE.
  • Éti localités dupliquées : le premier l'emporte.
  • Plus de 20 étiquettes : insertion d'un relais JMP_S.

3.7 REPEAT (5.6.3)

DEBUT_REPEAT:
   ; ... corps ...
   ; ... évaluation condition ...
   jxx  FIN_REPEAT
   jmp  DEBUT_REPEAT       ; retour arrière toujours en JMP long
FIN_REPEAT:

3.8 WHILE (5.6.4)

teste_while:
   ; ... évaluation condition ...
   Jxx  debut_while
   JMP  fin_while
debut_while:
   ; ... instruction ...
   JMP  teste_while
fin_while:

3.9 FOR (5.6.5)

Calcul des bornes (la 1re borne est push ax pour ne pas être écrasée par l'expression de la 2e ; pop cx ; xchg cx,ax ; sub cx,ax) :

jnl    ENTRE_FOR
jmp    FIN_FOR
ENTRE_FOR:
inc    cx
mov    [compteur], valeur_initiale
boucle_for:
push   cx
       ; ... corps ...
pop    cx
dec    cx
je     FIN_FOR
inc    word [compteur]
jmp    boucle_for
FIN_FOR:
  • DOWNTO : miroir (décrément).
  • Si la boucle est entrée, le compteur est laissé à sa valeur finale.
  • Modifier le compteur dans le corps change le nombre d'itérations.
  • GOTO vers l'intérieur d'un corps de FOR : interdit (CX ne serait pas sauvegardé).
  • GOTO/EXIT hors d'un FOR : nécessite un relais spécial pour dépiler CX (RELAI_GOTO_n: pop ax ; jmp cible).

3.10 Procédures — prologue/épilogue (5.7)

Procédure au premier niveau :

X:       push   bp            ; BP de l'appelant
         mov    bp, sp
         push   bp            ; "valeur de BP actuelle"
         jmp    DEBUT_X
DEBUT_X: sub    sp, $03       ; zone variable(s) (3 octets ici)
...
FIN_X:   mov    sp, bp
         pop    bp
         ret                  ; (ret imm si paramètres, voir 3.11)

Procédure imbriquée (table des accès) :

T:       push   bp
         mov    ax, sp
         push   [bp+$FE]      ; recopie la table de l'englobante (BP)
         mov    bp, ax
         push   bp
         jmp    DEBUT_T

Imbrication à n niveaux : re-push des entrées [bp+$FE], [bp+$FC], ... (chaque niveau : sauter 2 octets de plus). [bp+$FE] pour la 1re visite ; +2 par niveau supplémentaire.

Accès aux variables locales/paramètres : via BP (mov [bp+$FC],ax, etc.). Accès aux variables de l'englobante (intermédiaires) via la table :

mov    bx, [bp+$FE]      ; BP de la procédure englobante
seg    ss
mov    ax, [bx+$FC]      ; variable de l'englobante

Pile d'une procédure (SP → adresses hautes) :

la dernière variable locale              <--- SP
la deuxième variable locale
la première variable locale
la valeur de BP actuelle
la valeur de BP de la procédure englobante
la valeur de BP de la première procédure
la valeur de BP de l'appelant            <--- BP
l'adresse de retour                      <--- BP+2
dernier paramètre
premier paramètre

3.11 Paramètres (5.7.6)

Appel (les params sont poussés dans l'ordre des déclarations) :

mov    cx, $0012          ; 6 environnement + taille des paramètres
call   TU_STACK_CHECK     ; TOUJOURS avant un appel avec zone
mov    ax, $AAAA
push   ax                 ; paramètre valeur
mov    di, $0264
push   ds
push   di                 ; paramètre variable : SEG puis OFFSET
call   DEUX

Accès dans le callee :

mov    ax, [bp+$0E]       ; 1er paramètre valeur (après 2 var + 1 valeur = $0E)
les    di, [bp+$08]       ; LES : ES:DI ← adresse segmentée du 1er paramètre variable
seg    es
mov    [di], ax
  • Le 1er octet des paramètres est à BP+4. Le dernier paramètre déclaré est le plus proche (BP+4), le premier déclaré le plus loin (ex. +$0E, +$0C, +$08, +$04).
  • Épilogue : mov sp,bp ; pop bp ; ret imm — ret imm (c.-à-d. "intra") dégage les paramètres (2 octets par paramètre valeur, 4 par paramètre variable).

3.12 Fonctions (5.7.7)

Fonction entière — résultat dans AX, zone résultat réservée par l'appelant :

; appelant :
mov    cx, $0008          ; 6 environnement + 2 résultat
call   TU_STACK_CHECK
dec    sp                 ; réserve le résultat (2 octets)
dec    sp
call   F_FONCTION
mov    [$0262], ax        ; entier := f_entier

; callee :
mov    ax, $0077
mov    [bp+$04], ax       ; affectation au nom de fonction → zone [BP+4]
mov    ax, [bp+$04]       ; AX ← résultat
mov    sp, bp
pop    bp
ret    0002               ; intra : dégage le résultat

Fonction chaîne — résultat laissé sur la pile, épilogue via la librairie :

DEBUT_F_CHAINE:
lea    di, [bp+$04]
push   ss
push   di
call   TU_POUSSE_K_ST     ; pousse la constante chaîne
       (03   ; longueur
       asc   'ABC')
mov    cl, $0B            ; taille max (STRING[11])
call   TU_POP_STRING
...
mov    dx, $0000
mov    cl, $0B
mov    sp, bp
pop    bp
jmp    TU_RESU_FCT_ST     ; le résultat reste sur la pile

; appelant :
mov    di, $0262
push   ds
push   di                 ; seg:off de chaine (dest)
mov    cx, $0012          ; 6 env + 12 résultat
call   TU_STACK_CHECK
sub    sp, $0C            ; réserve le résultat (string)
call   F_CHAINE
mov    cl, $0B
call   TU_POP_STRING

Résumé : résultat entier → AX ; résultat réel/chaîne → laissé sur la pile.

3.13 Entrées-sorties / runtime (5.5)

  • WRITE/WRITELN → call TU_DEB_RW_DEFAUT ; call TU_WRITELN ; call TU_IO_CHECK (idem : TU_READ_DEFAUT, TU_READ_ENTIER, TU_WRITE_ENTIER, TU_WRITE_FICHI, TU_READ_FICHI, TU_CLOSE).
  • Fin de programme → xor ax,ax ; call TU_HALT.
  • Réels : passés sur la pile, 6 octets ; les comparaisons retournent AL.
  • Les fichiers utilisent systématiquement la librairie, qui fait appel au DOS.

3.14 Constantes typées (5.8)

  • Syntaxe : CONST nom : type = valeur ;. Modifiables comme des variables.
  • Stockées au milieu du code, valeur unique, accès par CS.
  • Comportement de variables statiques (gardent la valeur entre appels ; un appel récursif retient la dernière modification).
  • Jeu du compilateur : préfixe seg cs sur les MOV, et pousse cs au lieu de ds pour les routines de la librairie qui attendent un segment sur la pile.
  • Le programme saute par-dessus la zone des constantes via jmp PRINCIPAL.
  • Exemple données : kt_entier $1711 → hex 1111 ; TRUE → 01 ; 'A' → 41 ; 1.75 → 6 octets.
  • Réels : push cs / push di ; mov di,offset ; push ds ; call TU_POUSSE_REEL ; call TU_POP_REEL_DI.
  • Constantes complexes (tableaux d'enregistrements) : calcul d'index normal puis seg cs sur le stockage ; WITH sur constante typée pousse CS.
  • Limitation notée par l'auteur : mélange de 3 propriétés (initialisation/statique/CS) qu'il aurait mieux valu séparer (initialisation sous VAR, attributs STATIQUE et CODE).

3.15 Variables absolues (5.9)

Quatre cas :

  • ABSOLUTE CSEG:$2D50 → traité comme constante typée.
  • ABSOLUTE DSEG:$0242 → traité comme globale (offset fourni).
  • ABSOLUTE autre_variable → traité selon le segment de l'autre (DS ou SS); globale → DS, locale/paramètre → SS.
  • ABSOLUTE $1234:$5678 → l'adresse (décalage puis segment, petits-boutistes) est stockée dans le code et l'accès se fait par LES :
2D70  7856      hex  7856      ; décalage $5678
2D72  3412      hex  3412      ; segment  $1234
...
les    di, [$2D70]             ; ES:DI ← $1234:$5678
seg    es
mov    ax, [di]

3.16 Synthèse (5.A)

Deux catégories de données :

  1. registre : arithmétique dans AX, intermédiaires sur la pile repris dans CX ; accès sélecteurs via DI + décalage constant, ES = segment ; WITH = base sur la pile reprise dans ES:DI via BP ; locales/params via BP ; intermédiaires via la table des BP englobants.
  2. volumineuses (réels, chaînes, tableaux/enregistrements manipulés globalement, ensembles) : la pile stocke les valeurs ET le segment (DS/CS/SS/ES) ; DI porte le déplacement.

Affectation : le calcul d'adresse peut être séparé du stockage ; adresses sauvegardées sur la pile. Fichiers : via librairie → DOS.


4. CHAPITRE 6 — LES OPTIONS DE COMPILATION

  • Syntaxe : (*$...*) ou { $... } (pas d'espace entre l'ouvrant et $).
  • Effet au point de lecture (le compilateur lisant le fichier de gauche à droite).
  • Principales options : I/R/K/V/U/C/B/D/F/G/P.
  • R- : retire les contrôles d'intervalles (TU_RANGE_CHECK/TU_INDEX_CHECK non émis).
  • I- : supprime la levée d'erreur d'entrée-sortie jusqu'à I+.
  • V- : désactive la vérification des chaînes.
  • (*$g512,p512,d-*) : exemple fixant nombre de fichiers = 512 ? et périphériques par défaut.
  • R-options liées à la pile (min piles, max piles — mots du squelette §3.1).

5. CHAPITRE 7 — LA LIBRAIRIE (début)

  • Adresses TU_* des routines runtime (TU_PARAGRAPHS, TU_TAILLE_CODE, TU_HALT, TU_STACK_CHECK, TU_RANGE_CHECK, TU_INDEX_CHECK, TU_POUSSE_REEL, TU_POP_REEL_DI, TU_POUSSE_K_ST, TU_POP_STRING, TU_RESU_FCT_ST, TU_DEB_RW_DEFAUT, TU_WRITELN, TU_IO_CHECK, TU_READ_DEFAUT, TU_READ_ENTIER, TU_WRITE_ENTIER, TU_WRITE_FICHI, TU_READ_FICHI, TU_CLOSE, ...). Détails complets dans coeur-tp-06-d.txt.

6. PROTOCÔLE DES PASTES (FilePaste) — pour les sections de référence

  • Les sections de référence sont livrées en structure compatible FilePaste (simulée en chat ; l'utilisateur les rattache sous File Paste: <pasteId>). Ne pas créer de fichiers pour la référence elle-même.
  • Utiliser des marqueurs de bloc ASM pour le code ; les blocs (*$...*) sur des lignes simples avec en-têtes de section style INI ; les options en ligne ((*$R-*)) en italique. Marqueur "OPTIONS INFO" pour les bascules d'options.
  • Pas de tl;dr : contenu complet ; pas d'explication de ce que fait l'agent dans une section de référence (sections 100 % référence).
  • Autant que possible garder l'anglais (ex. types) ; le français pour les termes propres au livre.
  • Édition par pastes corrigés (((o)) / ((c))) : ré-émettre la section complète corrigée ; ne pas prétendre avoir appliqué la correction si l'on ne sait pas qu'elle a été appliquée (c'est l'utilisateur qui l'applique). AM + END = délimiteur de zone.

7. ÉTAT DES TRAVAUX DE SESSION (repère pour reprendre)

  • Fini : lecture complète des 6 volumes (-1, -02..-06), extraction de ch.4 (types), ch.5 (code), ch.6 (options), ch.7 (début librairie).
  • Émis en pastes : routage IF (5.3.1.2), template FOR/INIT_FOR avec constantes, template UPTO/DOWNTO (contenant TU_SEG + TU_SUB_ALIGN), paramètres/fonctions (5.7.6-5.7.7), constantes typées (5.8), variables absolues (5.9), synthèse 5.A.
  • À faire : livrer les sections de référence restantes en pastes complets (selon protocole §6) ; recouper les micro-détails incertains contre les sources TP3 (Resources/turbopascal3source/TP3/, TPSRC1-10) et le désassembleur Resources/DESASS/ si besoin.

8. RESSOURCES DISPONIBLES DANS Resources/

  • coeur-tp-ocr/ — les 6 volumes OCRisés (source de cette référence).
  • turbopascal3source/TP3/ (TPSRC1-10, MAIN) — source du compilateur TP3 (vérité).
  • DESASS/ — désassembleur ; AS.COM / AS.PDF — assembleur.
  • TP3/ — distribution TP3.
  • The_Turbo_Pascal_Tutor_1985.pdf, Turbo_Pascal_Version_3.0_Reference_Manual_1985.pdf, TURBO-LESSONS (Cours.md), Welsh_Hay (Model Implementation of Standard Pascal), Disassembly of old Turbo Pascal (V3) code (Stack Overflow HTML), etc.