[ASM]Using a macro to easily insert debug statements

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I want to track my app by sending strings out of the UART at appropriate times. So, for example, in the once-per-second interrupt handler I want to put (and can put, and it works perfectly)

one_second_interrupt_msg:
.db 10, 13, "one_second_interrupt" ,0
ldi r16, low (2*one_second_interrupt_msg)
ldi r17, high (2*one_second_interrupt_msg)
rcall lib_util_send_message

But then I want to put in another place

five_second_interrupt_msg:
.db 10, 13, "five_second_interrupt" ,0
ldi r16, low (2*five_second_interrupt_msg)
ldi r17, high (2*five_second_interrupt_msg)
rcall lib_util_send_message

etc etc

Now that looks like it could benefit from a macro being defined so that I could just do something like

_macro_track_app one_second_interrupt
...
macro_track_app one_second_interrupt_path_2a

etc where I want and it makes it easy to sprinkle around the code as much as I like

But my macro skills are defeating me. The closest I can get is

.macro _macro_track_app
@0_msg:
.db 10, 13, "@0" ,0

	ldi		r16, low (2*@0_msg)
	ldi		r17, high (2*@0_msg)
	rcall	lib_util_send_message

.endmacro

which all works (well, compiles) except, obviously, it prints out "@0" (no quotes) rather than the passed in value of @0.

So the bottom line is how do I get the string represented by @0 into a .db directive?...or is there some other way I can easily (minimal hand editing) do this "app tracking by serial output"?

TIA, Martin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Shirley you just use two parameters and call appropriately.

.macro _macro_track_app
@0_msg:
.db 10, 13, @1 ,0

   ldi      r16, low (2*@0_msg)
   ldi      r17, high (2*@0_msg)
   rcall   lib_util_send_message

.endmacro

...

     _macro_track_app david,"My name is David"

I would guess that you really do not want to be limited by the contents of your text message.

I would also guess you want to turn the macro on and off via a DEBUG macro.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cool - that works well - but I just have to make sure that all my "david"s are unique...I was after a one parameter macro for ultimate ease :D

...and yes, an easy overall turn-on and turn-offable facility is/was the next requirement to solve

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

...and yes, an easy overall turn-on and turn-offable facility is/was the next requirement to solve

Normally you'd use the pre-processor for this. At the top of the file just have:

#define DEBUG

then the debug messages and asserts are wrapped in:

#ifdef DEBUG
 ...
#endif

the "switch" is whether the #define line is commented or not.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

...or even easier...

#define DEBUG
...
.macro _macro_track_app
#ifdef DEBUG
@0_msg:
.db 10, 13, @1 ,0

   ldi      r16, low (2*@0_msg)
   ldi      r17, high (2*@0_msg)
   rcall   lib_util_send_message
#endif
.endmacro 

...

    _macro_track_app david,"My name is David" 

...and it seems to work perfectly.
EDIT:although it's a sledgehammer-stylee, all on or all off facility

(Still looking for a single parameter macro call method though!...)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Such macro can only work randomly, depending from the message.
Since the message was executed as code. :shock: :shock: :shock:

You must jump around the message. :!:

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No - the pre-processor in-line expands the calling line to the contents of the macro...and the #ifdef tells the pre-processor whether there is actually any macro content or not.

It all works really well in practice EDIT ...just needs two parameters in each call, the first of which must be unique across the whole application...which is why I'm trying to get a one parameter solution

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Danni's point was surely:

#define DEBUG 
... 
.macro _macro_track_app 
#ifdef DEBUG 
   rjmp  @0_skip_data
@0_msg: 
.db 10, 13, @1 ,0 

@0_skip_data:
   ldi      r16, low (2*@0_msg) 
   ldi      r17, high (2*@0_msg) 
   rcall   lib_util_send_message 
#endif 
.endmacro 

... 

    _macro_track_app david,"My name is David" 

Remember the .db is in .cseg and will be inline!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It also requires a push/pop of r16 and r17 at the top and bottom of the macro as it destroys them when it runs - that could mess up the main application somewhat ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK - I understand. The skip_data idea is nice. Ta

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another approach, use the return address as pointer to the string:

;-------------------------------------------------------------------------
;                       send zero terminated string after call
;-------------------------------------------------------------------------
;input: constant string after call
;use:   a0, b0, b1, yl, yh, zl, zh
;
puts:
        pop     zh                      ; get address after call
        pop     zl
        lsl     zl                      ; *2 to get byte pointer
        rol     zh
        rjmp    _pts2
_pts1:
        rcall   putchar
_pts2:
        lpm     a0, Z+
        cpi     a0, 0                   ; check zero byte
        brne    _pts1
        lsr     zh                      ; /2 to get word pointer
        ror     zl
        ijmp                            ; return after string
;-------------------------------------------------------------------------

and then:

        rcall   puts
        .db     ": START", 0

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

danni wrote:

        lpm     a0, Z+
        cpi     a0, 0                   ; check zero byte
        brne    _pts1
        lsr     zh                      ; /2 to get word pointer
        ror     zl
        ijmp                            ; return after string

Z pair must be rounded up to next even address, elsewhere it will be point to last word of message string before ijmp

        lpm     a0, Z+
        cpi     a0, 0                   ; check zero byte
        brne    _pts1
        adiw    zl, 1                   ; round up pointer for odd string size
        lsr     zh                      ; /2 to get word pointer
        ror     zl
        ijmp                            ; return after string

Last word "must be" hex 0000 (NOP) and ijmp to it "must be" safe...
But...

wbr, ReAl

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If the string contain even bytes, including zero on the end, then the ijmp point to the word after the string.

But if the string contain odd bytes, the assember append a zero byte to get full words of code.
Then the ijmp point to the zero byte on the end of the string.
This zero byte of the string plus the zero byte from the compiler was executed as NOP.

So on odd bytes the next instruction after the string was entered after an additional NOP.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Combining the best of both worlds, this seems to work very well

#define DEBUG

.macro _m_track_location
#ifdef DEBUG
	call   puts
	.db     @0, 0 
#endif
.endmacro

...

;-------------------------------------------------------------------------
puts:                       ;send zero terminated string after call
;-------------------------------------------------------------------------
;input: constant string after call
;use:   r16, zl, zh

        pop     zh                      ; get address after call
        pop     zl
        push    r16
        lsl     zl                      ; *2 to get byte pointer
        rol     zh
        rjmp    _pts2
_pts1:
        call   tx_uart
_pts2:
        lpm     r16, Z+
        cpi     r16, 0                   ; check zero byte
        brne    _pts1
        ;cr/lf
        ldi	r16, 13
        call tx_uart
        ldi	r16, 10
        call tx_uart

        pop r16
        lsr     zh                      ; /2 to get word pointer
        ror     zl
        ijmp                            ; return after string

;------------------------------------------------------------------------- 

...

_m_track_location "quarter_second_interrupt"

...

_m_track_location "one_second_interrupt"

... etc

Thanks to all the contributors...:)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not that elegant - a whole text is inefficiently repeared as many times as a macro is called. Look at this:

.macro  _m_track_location
#ifdef  debug
        ldi     zl,low(2*(PC+4))       ; Print dynamic head of a message
        ldi     zh,high(2*(PC+3))      ;
        rcall   debug_msg       ; Print a message
        rjmp    skipmsg         ; Jump over the message text
        .db     @0,0            ; ASCIIZ string made out of macro parameter
skipmsg:                        ; Local macro label, not visible outside the macro
#endif
.endm

        _m_track_location "Quarter"
        _m_track_location "Half"
        _m_track_location "One"
        _m_track_location "Five"
        _m_track_location "Many"
;....

debug_msg:
        rcall   puts       ; Print dynamic head of a message

        ldi     zl,low(2*msgtail)       ; Print static tail of a message
        ldi     zh,high(2*msgtail)      ;
        rcall   puts                    ; PUTS is just a generic ASCIIZ print routine
        ret

msgtail:                                ; Static tail of a message
        .db     "_second_interrupt",13,10,0 

Warning: Grumpy Old Chuff. Reading this post may severely damage your mental health.

Last Edited: Thu. Jan 21, 2010 - 01:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I like some of the ideas in there - thanks.

Unfortunately, my message texts are all different i.e. don't always end in "_second_interrupt" - that was just for illustration processes*

Martin

* - my timed interrupts work very well and don't actually need tracking - it's other parts of the code that need it ;)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MBedder wrote:

.macro  _m_track_location
#ifdef  debug
.set    msghead = PC+2          ; Quasy-label to be 
...
debug_msg:
        ldi     zl,low(2*msghead)       ; Print dynamic head of a message
        ldi     zh,high(2*msghead)      ; PUTS is just a generic ASCIIZ print routine

This don't work. :!:

It display always the same message, from the last macro insertion prior the debug_msg: function.

".set" works on the assemble time only but not at runtime.

Thus using the caller address was nice.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oops, you are right. See the corrected version above.

Warning: Grumpy Old Chuff. Reading this post may severely damage your mental health.