Etendre Xassembler avec des directives

publication: 4 avril 2021 / mis à jour 8 avril 2021

Read this page in english

 

Les directives d'assemblage

Reprenons le code assembleur PLUS.ASM:

.include "m328pdef.inc"
.include "macros.inc"
 
	.cseg
	.org	0x00
 
PLUS:
        ld      t0, Y+
        ld      t1, Y+
        add     tosl, t0
        adc     tosh, t1
        ret

Si on prend la ligne de code ld t0, Y+, on voit l'opérande t0 qui n'est pas défini dans Xassembler. Idem pour t1

t0 et t1 sont définis en assembleur dans le fichier macros.inc. Extrait:

.def t0 = r16
.def t1 = r17

Pour notre premier essai de méta assemblage, nous avons défini t0 et t1 avec constant comme suit en langage FORTH:

r16 constant t0
r17 constant t1
r24 constant tosl
r25 constant tosh
: plus
    [    t0 Y+ ld,          ]
    [    t1 Y+ ld,          ]
    [    tosl t0 add,       ]
    [    tosh t1 adc,       ]
    [    ret,               ]
;

Définir t0 t1 sous forme de constantes est acceptable pour un petit exemple, mais non valide pour un gros programme.

Sous gForth, si on définit la même constante plus d'une fois, on va se retrouver avec des valeurs incontrôlables dans le code à méta assembler.

L'idée la plus élégante serait de créer des mots .def .equ .set, ayant les mêmes propriétés que leur équivalent assembleur, même si la sytaxe est un peu différente. Exemple:

.def t0 = r16

devient en FORTH:

r16 .def t0

La directive .def

La directive .def définit un synonyme pour un registre. Voici comment est défini .def dans Xassembler.txt:

\ Defines a synonym for a register, syntax: 
\ R16 .def MyReg 
: .def  ( comp: n --  | exec: -- n) 
    value 
    ; 

Exemple de code source en assembleur:

; Register definitions
  .def upl = r2         ; not in interrupt
  .def uph = r3         ; not in interrupt
  .def r_zero = r5      ; read only zero
  .def r_one = r6       ; read only one
  .def r_two = r7       ; read only two
  .def t8 = r8          ; Not in interrupt
  .def wflags  = r9     ; not in interrupt

réécrit en FORTH:

\ Register definitions
r2  .def upl        \ not in interrupt
r3  .def uph        \ not in interrupt
r5  .def r_zero     \ read only zero
r6  .def r_one      \ read only one
r7  .def r_two      \ read only two
r8  .def t8         \ Not in interrupt
r9  .def wflags     \ not in interrupt

La directive .equ

La directive .equ définit un symbole en lui affectant une valeur. La valeur peut être modifiée ultérieurement. Voici comment est défini .equ dans Xassembler.txt:

\ Defines a symbol and sets its value  
\ later changes of this value remain possible, syntax:  
\ 1234 .equ test 
: .equ  ( comp: n --  | exec: -- n) 
    >in @ >r 
    bl word 
    count 2dup 
    find-name       \ leave nfa or 0 
    ?dup             
    if              \ word defined 
        r> drop 
        name>int    \ nfa > cfa 
        16 + >r     \ cfa > pfa 
        2drop r> !  \ store value          
    else            \ word undefined 
        drop 
        r> >in ! 
        drop value 
    then 
    ; 

Exemple de code source en assembleur:

;  ***** DATA MEMORY DECLARATIONS *****************************************
.equ	FLASHEND    = 0x3fff	;  Note: Word address
.equ	IOEND       = 0x00ff
.equ	SRAM_START  = 0x0100
.equ	SRAM_SIZE   = 2048
.equ	RAMEND      = 0x08ff
.equ	XRAMEND     = 0x0000
.equ	E2END       = 0x03ff
.equ	EEPROMEND   = 0x03ff
.equ	EEADRBITS   = 10

réécrit en FORTH:

\  ***** DATA MEMORY DECLARATIONS *****************************************
$3fff   .equ	FLASHEND		\  Note: Word address
$00ff   .equ	IOEND
$0100   .equ	SRAM_START
2048    .equ	SRAM_SIZE
$08ff   .equ	RAMEND
$0000   .equ	XRAMEND
$03ff   .equ	E2END
$03ff   .equ	EEPROMEND
10      .equ	EEADRBITS

Notre mot .equ teste si le symbole existe déjà. Si le symbole n'existe pas, il crée ce symbole en lui affectant la valeur souhaitée. Si le symbole a déjà été défini, il en modifie simplement sa valeur.

La directive .set

Cette directive .set fixe la valeur d'un symbole. Cette valeur ne peut pas être modifiée. Pas d'exemple.