Extending Xassembler with directives

published: 4 April 2021 / updated 8 April 2021

Lire cette page en français

 

Assembly directives

Let's go back to the assembly code 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

If we take the line of code ld t0, Y+, we see the operand t0 which is not defined in Xassembler. Ditto for t1

t0 and t1 are defined in assembler in the macros.inc file. Extract:

.def t0 = r16
.def t1 = r17

For our first meta-assembly test, we defined t0 and t1 with constant as follows in FORTH language:

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,               ]
;

Defining t0 t1 as constants is acceptable for a small example, but not valid for a large program.

Under gForth, if we define the same constant more than once, we will end up with uncontrollable values in the code to be meta-assembled.

The most elegant idea would be to create words .def .equ .set, having the same properties as their assembler equivalent, although the syntax is a little different. Example:

.def t0 = r16

becomes in FORTH:

r16 .def t0

The .def directive

The .def directive defines a synonym for a registry. Here is how is defined .def in Xassembler.txt:

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

Example of source code in assembler:

; 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

rewritten in 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

The .equ directive

The .equ directive defines a symbol by assigning a value to it. The value can be changed later. Here is how is defined .equ in 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 
    ; 

Example of source code in assembler:

;  ***** 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

rewritten in 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

Our word .equ tests whether the symbol already exists. If the symbol does not exist pas, it creates this symbol by assigning it the desired value. If the symbol already has been defined, it simply modifies its value.

The .set directive

This .set directive sets the value of a symbol. This value cannot not be changed. No example.