Chapter 6. Tutorial

Table of Contents
Writing the firmware
A sample debugging session
Project settings

This chapter outlines the simple procedures involved in debugging an application with NiceMon.

Writing the firmware

Here is an example with tips on writing a program that can be debugged with NiceMon.

See Accommodating NiceMon for the list of requirements.

staying configured for NiceMon

Extra care is needed when modifying registers used by NiceMon. Make sure when using sbit or rbit instructions on registers that NiceMon uses that they don't conflict with the settings required by NiceMon.

Also make sure that registers initialized with X A,## don't conflict with NiceMon settings either. This can be a little tricky. The constant used to initialize the register may need to be different depending on whether NiceMon is used or not.

A convenient way to deal with this problem is to use macros that are conditionally defined for use with or without NiceMon. Example macros are given in debug.inc

; These macros can be used instead of
; loading a constant into a register used
; by NiceMon.
;
; These macros ensure that the configuration will won't
; disturb NiceMon.

    .macro NICEMON_CNTRL value
        .mloc NICEMON_CNTRL_MASK 
        .mloc NICEMON_CNTRL_VALUE 
        NICEMON_CNTRL_MASK  = B'11110111
        NICEMON_CNTRL_VALUE = B'00001000
        .ifdef DEBUG_NICEMON
            ld CNTRL,#((value AND NICEMON_CNTRL_MASK) OR NICEMON_CNTRL_VALUE)
        .else
            ld CNTRL,#value
        .endif
    .endm

    .macro NICEMON_ICNTRL value
        .mloc NICEMON_ICNTRL_MASK 
        .mloc NICEMON_ICNTRL_VALUE 
        NICEMON_ICNTRL_MASK  = B'11110011
        NICEMON_ICNTRL_VALUE = B'00000100
        .ifdef DEBUG_NICEMON
            ld ICNTRL,#((value AND NICEMON_ICNTRL_MASK) OR NICEMON_ICNTRL_VALUE)
        .else
            ld ICNTRL,#value
        .endif
    .endm

    .macro NICEMON_PSW value
        .mloc NICEMON_PSW_MASK 
        .mloc NICEMON_PSW_VALUE 
        NICEMON_PSW_MASK  = B'11111010
        NICEMON_PSW_VALUE = B'00000101
        .ifdef DEBUG_NICEMON
            ld PSW,#((value AND NICEMON_PSW_MASK) OR NICEMON_PSW_VALUE)
        .else
            ld PSW,#value
        .endif
    .endm

    .macro NICEMON_PORTGC value
        .mloc NICEMON_PORTGC_MASK 
        .mloc NICEMON_PORTGC_VALUE 
        NICEMON_PORTGC_MASK  = B'10001111
        NICEMON_PORTGC_VALUE = B'00010000
        .ifdef DEBUG_NICEMON
            ld PORTGC,#((value AND NICEMON_PORTGC_MASK) OR NICEMON_PORTGC_VALUE)
        .else
            ld PORTGC,#value
        .endif
    .endm

    .macro NICEMON_PORTGD value
        .mloc NICEMON_PORTGD_MASK 
        .mloc NICEMON_PORTGD_VALUE 
        NICEMON_PORTGD_MASK  = B'11011111
        NICEMON_PORTGD_VALUE = B'00100000
        .ifdef DEBUG_NICEMON
            ld PORTGC,#((value AND NICEMON_PORTGD_MASK) OR NICEMON_PORTGD_VALUE)
        .else
            ld PORTGD,#value
        .endif
    .endm

If the debug macros are used, your program only needs to specify the constants required by your program. The macro will make sure the NiceMon configuration is not disturbed.

Adding the statement DEBUG_NICEMON = 1 will direct the macros to alter the configuration.

    DEBUG_NICEMON = 1
    .incld debug.inc

    NICEMON_CNTRL B'10100000

    ld TMR1LO,#L(10000)
    ld TMR1HI,#H(10000)
    ld T1RALO,#L(5000)
    ld T1RAHI,#H(5000)
    ld T1RBLO,#L(10000)
    ld T1RBHI,#H(10000)

    NICEMON_PSW B'00010001

    sbit T1C0,CNTRL ; start timer 1a

configuring to debug with interrupts

NiceMon requires interrupts to operate properly. So your interrupt sub routines must coexist with NiceMon. This fact puts constraints on your program.

NiceMon will enable interrupts when it returns control to the user program. This causes a problem when trying to single step through an interrupt sub routine. When NiceMon returns from a single step it automatically re-enables interrupts, even if interrupts were disabled before NiceMon gained control. The effect of this is that subsequent interrupts will re-enter and restart the interrupt handler.

The interrupt can be debugged if the interrupt source is disabled. The debug.inc include file has a macro that can be used to debug the timer 1a interrupt handler.

; This macro can be used to enable debugging of
; the timer 1a interrupt handler.
;
; If the timer interrupt is disabled, break points
; can be set in the interrupt handler.
;
; NiceMon will enable global interrupts so disabling
; the timer interrupt ensures that subsequent interrupts
; don't mess with the debugging.
;
; The symbol DEBUG_TIMER_1A_ISR must be defined for this
; macro to have an effect.
; 
; DEBUG_TIMER_1A_ISR = 1
    .macro NICEMON_DEBUG_TIMER_1A_ISR_ENTER
        .ifdef DEBUG_NICEMON
        .ifdef DEBUG_TIMER_1A_ISR
            rbit T1ENA,PSW ; disable timer 1a interrupt
        .endif
        .endif
    .endm


; This macro is used to re-enable
; the timer 1a interrupt
    .macro NICEMON_DEBUG_TIMER_1A_ISR_EXIT
        .ifdef DEBUG_NICEMON
        .ifdef DEBUG_TIMER_1A_ISR
            sbit T1ENA,PSW ; enable timer 1a interrupt
        .endif
        .endif
    .endm

The tutorial.asm example shows how this macro can be used.

timer_1a_isr:   
    NICEMON_DEBUG_TIMER_1A_ISR_ENTER ; this is required to
                                     ; debug the interrupt handler
    push A     ; save A
    ld a,B     ; save B
    push A
    ld a,PSW   ; save C and HC
    push A

    ld a,count3
    inc a
    x a,count3
    rbit T1PNDA,PSW ; reset timer 1a pending flag
    ld A,PORTD
    xor A,#001 ; toggle port D pin 0
    x A,PORTD

    pop A      ; restore C and HC
    rc
    ld B,#PSW
    ifbit 7,A
    sbit 7,[B]
    ifbit 6,A
    sbit 6,[B]
    pop A      ; restore B
    x A,B
    pop A      ; restore A
    NICEMON_DEBUG_TIMER_1A_ISR_EXIT ; this is required to
                                    ; debug the interrupt handler
    reti