Obligement - L'Amiga au maximum

Vendredi 06 juin 2025 - 12:21  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z,
ALL


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


Programmation : Assembleur - les fichiers exécutables
(Article écrit par Frédéric Delacroix et extrait d'Amiga News - avril 1996)


Aujourd'hui nous nous plongeons dans certains méandres internes à AmigaDOS : le format des fichiers exécutables. Cet article va tenter d'expliquer, pour les programmeurs d'éditeurs de liens et les autres, comment AmigaDOS stocke les programmes sur disque.

Introduction

C'est une question qui m'est fréquemment posée et qui déroute souvent le débutant en programmation sur Amiga : comment un programme utilisant des adresses absolues peut-il être chargé et exécuté à une adresse quelconque ? Par exemple, dans l'instruction «"MOVE d0,_DOSBase», _DOSBase ici est une adresse absolue au moment de l'exécution du programme. Si on voulait que le programme puisse être chargé à une adresse quelconque sans traitement particulier, il faudrait une séquence d'instructions du type :

    lea     _DOSBase(pc),a0
    move.l  d0,(a0)

Ce qui, vous en conviendrez, est beaucoup plus lourd. Pour contourner ce problème, AmigaDOS stocke sur disque, dans le fichier programme, plus que les données du programme lui-même. On y trouve également des informations de relogement, ainsi que d'autres choses comme des sections de données.

Toutes ces informations sont décodées lors du chargement par la fonction LoadSeg() de la dos.library : de la mémoire est allouée pour toutes les différentes sections du programme (définies par la directive SECTION de Devpac) et se charge de tous les travaux nécessaires comme le relogement par le biais des tables stockées dans le fichier.

C'est pourquoi, pour charger un programme, il ne faut pas utiliser Open(), Read() et consorts mais obligatoirement LoadSeg() ou NewLoadSeg(). Dans le cas contraire, on accède aux données "brutes" du fichier programme. C'est aussi pourquoi le code d'un secteur de démarrage (bootblock) ne doit jamais contenir d'adresses absolues : le DOS n'étant pas initialisé au moment de sa lecture, aucun relogement ne peut être effectué. Il faut donc utiliser la forme "lourde" ci-dessus étant donné qu'Exec ne charge pas (ne peut pas charger !) ce code à une adresse absolue.

C'est également là le noeud de la distinction que Devpac (par exemple) fait entre les expressions absolues et les expressions relatives : une expression relative fera l'objet d'un relogement en fonction de l'adresse de chargement du programme et donc de l'entrée d'une donnée correspondante dans les tables de relogement, tandis qu'une expression absolue est la même quelle que soit l'adresse de chargement du programme.

Les "hunks" (segments)

Écartons dans cet article la structure des fichiers objets et bibliothèques liables, que nous étudierons éventuellement dans un prochain numéro, si l'abondance du courrier me convainc de me plonger dans cet épineux problème. Nous ne nous intéresserons ici qu'aux fichiers exécutables, mot d'ailleurs impropre puisque LoadSeg() charge aussi des fichiers qui ne sont pas des exécutables au sens du Shell : polices, keymaps... Excluons aussi - pour l'instant - le format des overlays, c'est suffisamment compliqué comme ça.

La division du source (assembleur) en différentes SECTIONS correspond, dans le fichier programme produit, à différents segments. Un segment est ainsi la division primaire du programme. Chaque segment, lors du chargement, est placé à une adresse quelconque en mémoire et le relogement est effectué si nécessaire pour assurer la cohérence des données. Une fois le fichier chargé en mémoire, on parle plutôt de "segments".

Mais voyons d'abord l'en-tête indispensable d'un fichier exécutable. Il s'agit du bloc HUNK_HEADER, qui contient notamment le nombre et la taille des différents segments. Précisons qu'ici les décalages sont toujours donnés en mots longs, étant donné que tout ce format est orienté mot long, sans doute en raison de son origine BCPLienne.

     décalage   contenu
        0       constante HUNK_HEADER ($000003F3)
        1       constante 0 ($00000000 :-)
        2       nombre de segments (n)
        3       numéro du premier segment (normalement 0)
        4       numéro du dernier segment
        5       n mots longs représentant la taille (en mots longs) des 
                différents segments à allouer, ainsi que le type de la 
                mémoire dans les bits 30 et 31.

Ainsi, à la lecture de ce bloc, la fonction LoadSeg() est en mesure d'allouer n blocs de mémoire dont les tailles sont indiquées. L'avantage de mesurer la taille de ces blocs en mots longs est que cela permet de stocker dans les bits 30 et 31 le type de mémoire à allouer. 00 indique de la mémoire publique quelconque, 01 de la mémoire Chip et 10 de la mémoire Fast. On retrouve là un autre paramètre de la directive SECTION de Devpac.

La combinaison 11 est reconnue depuis le Kickstart 2.0 et indique que le type réel (le paramètre passé à AllocVec()) de la mémoire se trouve dans le mot long suivant (qui n'est bien sûr pas comptabilisé dans "n"). Cette possibilité, qui permet de réclamer de la mémoire MEMF_KICK, par exemple, n'est pas (encore) utilisée par nos assembleurs, compilateurs ou éditeurs de liens.

Sous Kickstart 1.3 et antérieurs, on peut également trouver des spécifications de bibliothèques résidentes, mais celles-ci sont obsolètes depuis (LoadSeg() échoue s'il en trouve !), c'est pourquoi je n'en parlerai pas ici.

Précisons également que les tailles de segments données dans le bloc HUNK_HEADER peuvent ne pas correspondre à celles des segments : elles ne spécifient que la taille du bloc à allouer, pas des données à y mettre. On peut utiliser une taille dans HUNK_HEADER plus grande que celle du code ou des données correspondantes pour "simuler" des zones de type BSS dans la même section que du code ou des données (ce que fait le SAS/C d'ailleurs). Ensuite, on trouve, juxtaposées, les données des différents segments. Un segment est lui-même formaté en différents blocs, chacun d'un type et d'une fonction particuliers.

Les types de blocs

Un bloc commence toujours par son identificateur sous forme de mot long. Voyons les types permis dans les segments des exécutables non overlays.

Les blocs HUNK_NAME, HUNK_SYMBOL et HUNK_DEBUG sont purement et simplement ignorés par LoadSeg(). Les débogueurs comme MonAm tirent cependant parti des deux derniers pour afficher respectivement des noms de symboles à la place de noms et des numéros de ligne de source. HUNK_NAME et HUNK_DEBUG ont le même format :

       décalage contenu
            0   HUNK_NAME ($3E8) ou HUNK_DEBUG ($3F1)
            1   n
            2   n mots longs indiquant le nom du segment (autre paramètre
                de la directive SECTION) pour HUNK_NAME, ou des 
                données spécifiques pour un débogueur pour HUNK_DEBUG.

Le format de HUNK_SYMBOL est calqué sur celui de HUNK_EXT qui n'apparaît que dans les fichiers objet et liste les XDEF et XREF :

        décalage contenu
            0    HUNK_SYMBOL ($3F0)
           {1    nombre 24 bits "n" (nombre non nul) 
                 (0 dans les bits 24 à 31)
            2    n mots longs contenant le nom du symbole
            2+n} valeur du symbole
            ?    0

La partie entre accolades peut être répétée un nombre quelconque de fois, listant ainsi tous les symboles de la section. La valeur mentionnée dans le dernier champ représente en réalité un décalage par rapport à l'adresse de chargement des données. La véritable valeur du symbole est donc obtenue en ajoutant au contenu de ce champ l'adresse de chargement du segment.

Les blocs HUNK_CODE et HUNK_DATA contiennent les données effectives du programme, et ont le format très simple suivant :

      décalage  contenu
            0   HUNK_CODE ($3E9) ou HUNK_DATA ($3EA)
            1   n
            2   n mots longs de code (pour HUNK_CODE) 
                ou de données (HUNK_DATA)

On y trouve les données et le programme générés par l'assembleur ou le compilateur, mais les adresses absolues (comme le _DOSBase du début de l'article dans l'opérande de l'instruction MOVE elle-même) sont remplacées par un simple décalage par rapport au début du segment auquel cette adresse fait référence. Concrètement, si votre code est du type...

        section   programme,CODE
        ...
        move.l    d0,_DOSBase
        ...
        section   données,DATA
      _ExecBase   dc.l   0
      _DOSBase    dc.l   0

...alors, au moment de coder l'opérande destination de l'instruction MOVE, c'est-à-dire l'adresse absolue _DOSBase, l'assembleur y met 4, c'est-à-dire le décalage de _DOSBase par rapport au début de sa section. Il génère de plus une entrée dans la table de relogement du premier segment indiquant qu'il faut ajouter au contenu de cette adresse, l'adresse de chargement du segment numéro 1. Ainsi, après LoadSeg(), l'adresse effective du MOVE est correcte, elle a été calculée au moment du chargement. Magique... Le bloc HUNK_BSS indique, lui, un bloc de données non initialisées :

      décalage  contenu
            0   HUNK_BSS ($3EB) avec attributs de mémoire
            1   nombre de mots longs à allouer

Ici, aucune donnée ne suit (on passe directement au bloc suivant). À la rencontre d'un tel bloc, LoadSeg() remplit la mémoire (qui avait été allouée lors de la lecture du HUNK_HEADER) par des zéros. C'est donc un moyen commode de réserver des tampons et autres variables non initialisés dans un programme. En assembleur, on réclame une section BSS, puis on la remplit par des directives ds.b, ds.w ou ds.l, comme on en a l'habitude. BSS signifie "Block Storage Segment", ds signifie "Define Storage".

Contrairement aux HUNK_BSS toutefois, les BSS simulés par des HUNK_DATA comme mentionné ci-dessus, ne sont pas remplis de zéros, sauf utilisation (non prévue par Devpac) d'un type de mémoire MEMF_CLEAR dans le HUNK_HEADER, bien entendu.

Le format des tables

Pour ce qui concerne la division d'un programme en segments et la division de ceux-ci en blocs, je vous renvoie à l'article du mois dernier. Dans chaque segment qui contient des références absolues, on trouve une table de relogement, indiquant les adresses à corriger. Elle se situe dans un bloc de type HUNK_RELOC32. Il existe aussi les identificateurs HUNK_RELOC16 et HUNK_RELOC8 mais ceux-ci ne sont utilisés que dans les modules objets, les relogements effectifs en mémoire ne pouvant se faire que sur 32 bits.

décalage contenu

0        HUNK_RELOC32 ($3EC)
{1       n (non nul)
2        numéro de segment concerné
3        n mots longs donnat les décalages à corriger
3+n}
?        0

Ici, tous les décalages sont en mots longs, sauf depuis le Kickstart 2.0 qui a introduit l'identificateur HUNK_RELOC32SHORT ($3FC), qui utilise des mots courts, d'où un gain de place. Le but du programme est de proposer de convertir ces tables au format court.

Le programme

Nommé, ShortenReloc, il se propose de convertir les tables de relogements de fichiers exécutables au format court exposé ci-dessus. Attention toutefois, après la conversion, le programme en question ne pourra plus être chargé sous Kickstart 1.3 ou antérieur. De plus, ce procédé ne fonctionne que pour les fichiers suffisamment petits pour que les relogements n'excèdent pas 65535 octets ! De plus, les versions du Kickstart antérieures à la 3.0 comportent un bogue qui utilise $3F7 à la place de $3FC pour HUNK_RELOC32SHORT. L'utilisation de cette dernière valeur peut être forcée dans ShortenReloc par l'option V39, mais le programme ainsi créé ne pourra être chargé que sous Kickstart 3.0 ou supérieur !

    include exec/exec.i
    include exec/exec_lib.i
    include dos/dos.i
    include dos/doshunks.i
    include dos/dos_lib.i

    move.l  4.w,a6
    move.l  a6,Exec.Base
    lea     DOS.Name(pc),a1
    moveq   #37,d0      ; Kickstart 2.0 mini
    jsr     _LVOOpenLibrary(a6)
    move.l  d0,DOS.Base
    beq     Exit
    move.l  #Args.Template,d1
    move.l  #Args.Array,d2
    moveq   #0,d3
    move.l  DOS.Base(pc),a6
    jsr     _LVOReadArgs(a6)
    move.l  d0,Args.RDArgs
    beq.s   CloseDOS

    move.l  From.Arg(pc),d1
    move.l  #MODE_OLDFILE,d2
    jsr     _LVOOpen(a6)    ; ouverture fichier source
    move.l  d0,Source.Handle
    beq.s   FreeArgs
    move.l  To.Arg(pc),d1
    move.l  #MODE_NEWFILE,d2
    jsr     _LVOOpen(a6)    ; ouverture fichier destination
    move.l  d0,Dest.Handle
    beq.s   CloseSource

    bsr     ReadHeader  ; transfère (et teste sa présence) le bloc
    beq.s   CloseDest   ; HunkHeader.

    moveq   #0,d2   ; numéro du segment courant (commence à 0!)
    move.l  d0,d3   ; nombre de segments
HunkLoop
    bsr     ReadHunk
    beq.s   CloseDest
    addq.l  #1,d2   ; segment suivant
    cmp.l   d2,d3
    bne.s   HunkLoop

CloseDest
    move.l  Dest.Handle(pc),d1
    jsr     _LVOClose(a6)
CloseSource
    move.l  Source.Handle(pc),d1
    jsr     _LVOClose(a6)
FreeArgs
    move.l  Args.RDArgs(pc),d1
    jsr     _LVOFreeArgs(a6)
CloseDOS
    move.l  a6,a1
    move.l  Exec.Base(pc),a6
    jsr     _LVOCloseLibrary(a6)
Exit    moveq   #0,d0
    rts

PrintError  ; PrintError()
    movem.l d0-d2/a0-a1/a6,-(sp)
    move.l  DOS.Base(pc),a6
    jsr     _LVOIoErr(a6)
    bsr.s   PrintFault
    movem.l (sp)+,d0-d2/a0-a1/a6
    rts

PrintFault  ; PrintFault(Code)(D0)
    movem.l d0-d2/a0-a1/a6,-(sp)
    move.l  DOS.Base(pc),a6
    move.l  d0,d1
    move.l  #Fault.Header,d2
    jsr     _LVOPrintFault(a6)
    movem.l (sp)+,d0-d2/a0-a1/a6
    rts
ReadVal         ; (Z=1)Error,(D0)Value=ReadVal(Size-1)(D0)
        movem.l d1-d3/a0-a1/a6,-(sp)
        move.l  DOS.Base(pc),a6
        moveq   #0,d3
        move.l  d0,d2
.CLoop  move.l  Source.Handle(pc),d1
        jsr _LVOFGetC(a6)
        cmp.l   #-1,d0
        beq.s   .Fail
        lsl.l   #8,d3
        add.b   d0,d3
        dbra    d2,.CLoop
        move.l  d3,d0
        moveq   #-1,d1
.Ret    movem.l (sp)+,d1-d3/a0-a1/a6
        rts
.Fail   bsr     PrintError
        moveq   #0,d0
        bra.s   .Ret

; lit un mot long
ReadLong        ; (Z=1)Error,(D0)Value=ReadLong()
        moveq   #3,d0   ; 4 octets
        bra     ReadVal

; lit un mot
ReadWord        ; (Z=1)Error,(D0)Value=ReadWord()
        moveq   #1,d0   ; 2 octets
        bra     ReadVal

; les <Size> octets les plus à GAUCHE de <Value> sont écrits dans le fichier
WriteVal        ; (Z=1)Error=WriteVal(Value,Size-1)(D0,D1)
        movem.l d0-d4/a0-a1/a6,-(sp)
        move.l  DOS.Base(pc),a6
        move.l  d0,d3   ; valeur (mot long)
        move.l  d1,d4   ; compteur
        moveq   #0,d2
.CLoop  rol.l   #8,d3   ; octet suivant
        move.b  d3,d2
        move.l  Dest.Handle(pc),d1
        jsr     _LVOFPutC(a6)
        cmp.l   #-1,d0  ; erreur?
        beq.s   .Fail
        dbra    d4,.CLoop
        moveq   #1,d0   ; met Z à 0
.Ret    movem.l (sp)+,d0-d4/a0-a1/a6
        rts
.Fail   bsr     PrintError
        moveq   #0,d0   ; met Z à 1
        bra.s   .Ret

; écrit un mot long
WriteLong       ; (Z=1)Error=WriteLong(Value)(D0)
        move.l  d1,-(sp)
        moveq   #3,d1
        bsr     WriteVal
        movem.l (sp)+,d1    ; movem pour préserver le bit Z
        rts

; écrit un mot
WriteWord       ; (Z=1)Error=WriteWord(Value)(D0)
        movem.l d0-d1,-(sp)
        cmp.l   #$10000,d0  ; ça tient dans un mot?
        bhs.s   .TooBig
        swap    d0  ; WriteVal() demande une justification à gauche
        moveq   #1,d1
        bsr     WriteVal
.Ret    movem.l (sp)+,d0-d1
        rts
.TooBig move.l  #ERROR_OBJECT_TOO_LARGE,d0
        bsr PrintFault
        moveq   #0,d0
        bra.s   .Ret

ReadRecords     ; (Z=0)Success,(D0)Buffer=ReadRecords(Number,Size)(D0,D1)
        movem.l d1-d4/a0-a1/a6,-(sp)
        move.l  d0,d4
        move.l  d1,d3
        mulu    d3,d0   ; nombre d'octets
        move.l  #MEMF_PUBLIC,d1
        move.l  Exec.Base(pc),a6
        jsr     _LVOAllocVec(a6)
        move.l  d0,d2
        bne.s   .MemOK
        bsr     PrintError  ; en cas d'échec, Exec met IoErr() à 103
        bra.s   .Fail       ; pour les processus...
.MemOK  move.l  DOS.Base(pc),a6
        moveq   #0,d0   ; corrige un léger bug de FRead()
        jsr     _LVOSetIoErr(a6)
        move.l  Source.Handle(pc),d1
        jsr     _LVOFRead(a6)
        cmp.l   d0,d4   ; lecture réussie?
        beq.s   .OK
        bsr PrintError
        move.l  d2,a0
        bsr.s   FreeBuf
.Fail   moveq   #0,d2
.OK     move.l  d2,d0
        movem.l (sp)+,d1-d4/a0-a1/a6
        rts

; libère de la mémoire allouée par AllocVec(), en préservant les
; codes de conditions
FreeBuf         ; FreeBuf(Buffer)(A0)   ; DOIT préserver le bit Z
        movem.l d0-d2/a0-a1/a6,-(sp)
        movea.l Exec.Base(pc),a6
        jsr _LVOGetCC(a6)
        move.l  d0,d2
        move.l  a0,a1
        jsr _LVOFreeVec(a6)
        move.w  d2,CCR
        movem.l (sp)+,d0-d2/a0-a1/a6
        rts

ReadLongs       ; (Z=0)Success,(D0)Buffer=ReadLongs(Number)(D0)
        move.l  d1,-(sp)
        moveq   #4,d1   ; 4 octets par enregistrement
        bsr     ReadRecords
        movem.l (sp)+,d1    ; préserve Z
        rts

ReadWords       ; (Z=0)Success,(D0)Buffer=ReadWords(Number)(D0)
        move.l  d1,-(sp)
        moveq   #2,d1
        bsr     ReadRecords
        movem.l (sp)+,d1
        rts

WriteRecords    ; (Z=0)Success=WriteRecords(Buffer,Number,Size)(A0,D0,D1)
        movem.l d0-d4/a0-a1/a6,-(sp)
        move.l  d0,d4   ; nombre
        move.l  a0,d2   ; adresse
        move.l  d1,d3   ; taille des blocs
        moveq   #0,d0   ; corrige un petit bug de FWrite()
        move.l  DOS.Base(pc),a6
        jsr     _LVOSetIoErr(a6)
        move.l  Dest.Handle(pc),d1
        jsr     _LVOFWrite(a6)
        cmp.l   d0,d4
        eori.b  #1<<2,ccr   ; inverse l'état du bit Z
        movem.l (sp)+,d0-d4/a0-a1/a6
        rts

WriteLongs      ; (Z=0)Success=WriteLongs(Buffer,Number)(A0,D0)
        move.l  d1,-(sp)
        moveq   #4,d1
        bsr     WriteRecords
        movem.l (sp)+,d1
        rts

WriteWords      ; (Z=0)Success=ReadWords(Buffer,Number)(A0,D0)
        move.l  d1,-(sp)
        moveq   #2,d1
        bsr     WriteRecords
        movem.l (sp)+,d1
        rts

; la présence d'un HUNK_HEADER en début de fichier indique soit un fichier
; exécutable, soit un fichier objet. Un fichier objet sera automatiquement
; rejeté par notre programme car il contient aussi des HUNK_UNIT.
ReadHeader      ; (Z=0)Success,(D0)Hunks=ReadHeader()
        movem.l d1-d2/a0-a1/a6,-(sp)
        bsr     ReadLong
        beq.s   .Fail
        cmp.l   #HUNK_HEADER,d0
        beq.s   .HeaderFound
        move.l  #ERROR_FILE_NOT_OBJECT,d0
        bsr     PrintFault
        bra.s   .Fail
.HeaderFound
        bsr     ReadLong
        beq.s   .Fail
        tst.l   d0
        beq.s   .NoResLib
        move.l  #ERROR_BAD_HUNK,d0  ; les bibliothèques
        bsr     PrintFault  ; résidentes ne sont plus autorisées
        bra.s   .Fail
.NoResLib
        bsr     ReadLong
        beq.s   .Fail
        move.l  d0,d2   ; nombre de segments
        addq.l  #2,d0   ; il y a deux mots longs supplémentaires
        bsr     ReadLongs
        beq.s   .Fail
        move.l  d0,a0   ; A0= partie du bloc HunkHeader
        move.l  Exec.Base(pc),a6
        move.l  #HUNK_HEADER,d0
        bsr     WriteLong   ; identificateur de bloc
        beq.s   .FreeBuf
        moveq   #0,d0
        bsr     WriteLong   ; pas de bibliothèque résidente
        beq.s   .FreeBuf
        move.l  d2,d0
        bsr     WriteLong   ; nombre de segments
        beq.s   .FreeBuf
        addq.l  #2,d0
        bsr     WriteLongs  ; le reste du bloc
        bne.s   .HeaderWritten
.FreeBuf
        bsr     FreeBuf
.Fail   moveq   #0,d2
        bra.s   .Ret
.HeaderWritten
        bsr     FreeBuf
.Ret    move.l  d2,d0
        movem.l (sp)+,d1-d2/a0-a1/a6
        rts

; cette fonction est appelée pour chaque segment. Elle transfère tous
; les blocs qu'il contient en convertissant les RELOC32.
ReadHunk        ; (Z=0)Success=ReadHunk()
        movem.l d0-d2/a0-a1/a6,-(sp)
.BlockLoop
        bsr     ReadLong
        beq.s   .Fail
        move.l  d0,d2
        bsr     Tell
        cmp.l   #HUNK_END,d0
        beq.s   .End
        lea     Blocks.Table(pc),a0
.Table  cmp.w   (a0)+,d0
        beq.s   .Found
        tst.w   (a0)+
        bne.s   .Table
        btst    #HUNKB_ADVISORY,d0
        beq.s   .Unknown
        lea     Hunk_DEBUG(pc),a0
        bra.s   .Ignore
.Unknown
        move.l  #ERROR_BAD_HUNK,d0
        bsr     PrintError
.Fail   moveq   #0,d0
.Ret    movem.l (sp)+,d0-d2/a0-a1/a6
        rts
.End    bsr     WriteLong
        beq.s   .Ret
        moveq   #1,d0
        bra.s   .Ret
.Found  movea.w (a0),a0
        add.l   #ReadHunk,a0    ; évite trop de Reloc32 dans le prog :-)
.Ignore jsr (a0)
        beq.s   .Fail
        bra.s   .BlockLoop

; les routines suivantes sont appelées pour traiter les blocs dont
; l'identificateur se trouve en D2. En retour, Z=0 indique l'absence
; d'erreur

READLONG    MACRO
        bsr     ReadLong
        beq.s   .Ret
        ENDM

READWORD    MACRO
        bsr     ReadWord
        beq.s   .Ret
        ENDM

WRITELONG   MACRO
        bsr     WriteLong
        beq.s   .Ret
        ENDM

WRITEWORD   MACRO
        bsr     WriteWord
        beq.s   .Ret
        ENDM

; les suivants se contentent de recopier le bloc intégralement.
Hunk_NAME
Hunk_CODE
Hunk_DATA
Hunk_DEBUG
Hunk_Generic
        move.l  d2,d0
        WRITELONG   ; identificateur
        READLONG    ; nombre de mots longs
        WRITELONG
        move.l  d0,d1
        bsr     ReadLongs
        beq.s   .Ret
        move.l  d0,a0
        move.l  d1,d0
        jsr     WriteLongs
        bsr     FreeBuf
.Ret    rts

; ici, il n'y a que deux mots longs
Hunk_BSS
        move.l  d2,d0
        WRITELONG
        READLONG    ; longueur (mots longs)
        bsr     WriteLong
.Ret    rts

; un peu plus compliqué pour connaître la longueur de celui-ci (il y
; a d'autres données)
Hunk_SYMBOL
        bsr     Hunk_Generic    ; longueur et nom
        beq.s   .Ret
        READLONG    ; valeur du symbole
        WRITELONG
        READLONG    ; zéro final
        WRITELONG
.Ret    rts

; à convertir en BOGUSSHORT ou RELOC32SHORT (pour option V39)
Hunk_RELOC32
        move.l  #HUNK_RELOC32SHORT,d0
        tst.l   V39.Arg
        bne.s   .NewShort
        move.l  #HUNK_BOGUSSHORT,d0
.NewShort
        WRITELONG   ; change l'identificateur de bloc
        moveq   #0,d2   ; compteur pour l'alignement
.Loop   READLONG    ; nombre de décalages
        WRITEWORD
        move.l  d0,d1
        beq.s   .End
        READLONG    ; numéro de segment
        WRITEWORD
        move.l  d1,d0
        bsr     ReadLongs
        beq.s   .Ret
        move.l  d0,a0
        bsr     ConvertOffsets
        beq.s   .Ret
        move.l  d1,d0
        add.l   d1,d2
        bsr     WriteWords
        bsr     FreeBuf
        bne.s   .Loop
.Ret    rts
.End    and.l   #1,d2   ; déjà aligné sur un mot long (nombre de mots impair)?
        bne.s   .LW ; (il faut ajouter le 0 final!) sinon rajouter un
        moveq   #0,d0   ; mot nul
        WRITEWORD
.LW     moveq   #1,d0   ; met Z à 0
        bra.s   .Ret

; à recopier
Hunk_RELOC32SHORT
Hunk_BOGUSSHORT
        move.l  d2,d0
        WRITELONG
.Loop   READWORD    ; longueur du nom
        WRITEWORD
        moveq   #0,d1
        move.w  d0,d1
        beq.s   .End
        READWORD    ; numéro de segment
        WRITEWORD
        move.l  d1,d0
        bsr     ReadWords
        beq.s   .Ret
        move.l  d0,a0
        move.l  d1,d0
        add.l   d1,d2
        bsr     WriteWords
        bsr     FreeBuf
        bne.s   .Loop
.Ret    rts
.End    and.l   #1,d2   ; y a-t-il encore un mot nul?
        beq.s   .LW
        READWORD
        WRITEWORD
.LW     moveq   #1,d0
        bra.s   .Ret

Tell                    ; Tell(Block)(D0)
        movem.l d0-d2/a0-a1/a6,-(sp)
        tst.l   Verbose.Arg
        beq.s   .None
        move.l  DOS.Base(pc),a6
        move.l  #Verbose.Fmt,d1
        move.l  d0,-(sp)
        move.l  sp,d2
        jsr     _LVOVPrintf(a6)
        addq.l  #4,sp
.None   movem.l (sp)+,d0-d2/a0-a1/a6
        rts

; cette routine convertit (dans le même tampon) les décalages mots longs
; en mots courts, tout en testant si tous les décalage sont suffisamment
; courts.
ConvertOffsets          ; (Z=0)Success=ConvertOffsets(RelocOffsets,Number)(A0,D1)
        movem.l d0-d1/a0-a1,-(sp)
        subq.l  #1,d1
        move.l  a0,a1
.Loop   move.l  (a0)+,d0
        cmp.l   #$10000,d0
        bhs.s   .TooBig
        move.w  d0,(a1)+
        dbra    d1,.Loop
        moveq   #1,d0   ; met Z à 0
.Ret    movem.l (sp)+,d0-d1/a0-a1
        rts
.TooBig move.l  #ERROR_OBJECT_TOO_LARGE,d0
        bsr     PrintFault
        moveq   #0,d0
        bra.s   .Ret

Exec.Base       dc.l    0
DOS.Base        dc.l    0
Args.RDArgs     dc.l    0
Source.Handle   dc.l    0
Dest.Handle     dc.l    0

Args.Array
From.Arg        dc.l    0
To.Arg          dc.l    0
Verbose.Arg     dc.l    0
V39.Arg         dc.l    0

BLOCK   MACRO   ; nom du type de bloc
        dc.w    HUNK_1,Hunk_1-ReadHunk
        ENDM

Blocks.Table
        BLOCK   NAME    ; types de blocs reconnus par notre
        BLOCK   CODE    ; programme. Les autres types génèrent
        BLOCK   DATA    ; une erreur. Les autres types n'arrivent
        BLOCK   BSS ; que dans les modules objets, à l'exception
        BLOCK   RELOC32 ; des overlays.
        BLOCK   RELOC32SHORT

HUNK_BOGUSSHORT EQU HUNK_DREL32 ; les mecs de Commodore se sont
        BLOCK   BOGUSSHORT      ; trompés !

        BLOCK   SYMBOL
        BLOCK   DEBUG
        dc.l    0,0 ; fin de la table

DOS.Name        dc.b    "dos.library",0
Args.Template   dc.b    "FROM/A,TO/A,VERBOSE/S,V39/S",0
Fault.Header    dc.b    "ShortenReloc",0
Verbose.Fmt     dc.b    "Bloc: $%08.lx",10,0


[Retour en haut] / [Retour aux articles] [Article précédent] / [Article suivant]