;* --------------------------------------------------------------------------------------
;*  Copyright (C) 2004 Dallas Semiconductor Corporation, All Rights Reserved.
;* 
;*  Permission is hereby granted, free of charge, to any person obtaining a
;*  copy of this software and associated documentation files (the "Software"),
;*  to deal in the Software without restriction, including without limitation
;*  the rights to use, copy, modify, merge, publish, distribute, sublicense,
;*  and/or sell copies of the Software, and to permit persons to whom the
;*  Software is furnished to do so, subject to the following conditions:
;* 
;*  The above copyright notice and this permission notice shall be included
;*  in all copies or substantial portions of the Software.
;* 
;*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
;*  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
;*  MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
;*  IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
;*  OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
;*  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
;*  OTHER DEALINGS IN THE SOFTWARE.
;* 
;*  Except as contained in this notice, the name of Dallas Semiconductor
;*  shall not be used except as stated in the Dallas Semiconductor
;*  Branding Policy.
;*
;* --------------------------------------------------------------------------------------
;* 
;*  This demonstration is meant to show some of the abilities of the GPIO
;*  on the MAXQ2000.  It shows how to configure and handle external interrupts, 
;*  how to read input ports and how to configure and write to output ports.
;*
;*  The demo application toggles three LEDs in the main loop.  The lowest LED
;*  (third from the right) toggles about once a second.  The top two LEDs
;*  (two LEDs on the left) alternate twice as fast.
;*
;*  4 of the remaining 5 LEDs are lit in the interrupt service routine for the 
;*  push buttons (the 5th LED is unused).  One LED is tied directly to each 
;*  push button (SW4 and SW5).  The other 2 LEDs will toggle if you press one
;*  push button while holding the other.
;*
;*  Turn ON the following switches for this demonstration:
;*
;*       SW6.2, SW6.5: Enable the push buttons
;*       SW6.8 : Enable the LED panel
;*
;* --------------------------------------------------------------------------------------
#include "..\api\maxQ2000.inc"

  .code
  .keep

    ;
    ; Some basic initialization.  Set Accumulator Controls to normal.
    ; Set interrupt vector.  Set data pointer controls.
    ;
    move  APC, #0                      ; no accumulator-modulo behavior
    move  AP, #0                       ; select accumulator 0
    move  DPC, #0                      ; select DP[0], byte mode
    move  IV, #w:interrupt_handler       ; set interrupt vector

    call  init_push_buttons            ; setup push button configuration
    move  IC, #1                       ; initialize global interrupts
    move  PD0, #0FFh                   ; set port 0 pins for all outputs
    move  PO0, #80h                    ; only light up the highest LED

;*****************************************************************************
;
; Main application loop.  Toggle the top two bits twice per loop.  Toggle the
; bottom bit once per loop.  Interrupt handler will take care of everything 
; else.
;
;*****************************************************************************
mainloop_top:
    ;
    ; Insert a simple delay loop here.
    ;
    move  LC[0], #8D80h                ; at 13.5 MHz, we want to delay ~6M times.
    move  LC[1], #5Bh                  ; That gives us 5B8D80h == 6,000,000
delay_loop:
    djnz  LC[0], delay_loop
    djnz  LC[1], delay_loop

    move  ACC, PO0                     ; get current value of port output pins
    xor   #0C0h                        ; toggle the top two bits
    move  PO0, ACC                     ; which will toggle the top 2 LEDs

    ;
    ; Another delay loop here.
    ;
    move  LC[0], #8D80h                ; at 13.5 MHz, we want to delay ~6M times.
    move  LC[1], #5Bh                  ; That gives us 5B8D80h == 6,000,000
delay_loop2:
    djnz  LC[0], delay_loop2
    djnz  LC[1], delay_loop2

    move  ACC, PO0                     ; get current value of port output pins
    xor   #0C1h                        ; toggle the top two bits, plus bottom bit
    move  PO0, ACC                     ; which will toggle the top 2 + bottom 1 LED

    jump  mainloop_top

;*****************************************************************************
;
; Initialize the push buttons.  We will be using push buttons 10 and 15 in 
; this application.  Set for falling edge triggered interrupts.  Enables 
; interrupts from module 1 in the system interrupt mask.
;
; Input:     None
; Output:    None
; Destroyed: ACC, PSW
;
;*****************************************************************************
init_push_buttons:
    move  EIE1, #84h                   ; enable interrupts for EI 10 and 15
    move  EIES1, #84h                  ; select falling edge for EI 10 and 15
    move  ACC, IMR                     ; get the interrupt mask enable
    or    #2                           ; include interrupts for module 1
    move  IMR, ACC                     ; store new interrupt mask enable value
    ret

;*****************************************************************************
;
; Interrupt handler for the application.  We are only expecting interrupts
; on external interrupt 10 and 15.  
;
; We assume that we can use any accumulator except for A[0], and that the 
; application never sets AP or APC to any value other than 0.  This code
; 
;*****************************************************************************
interrupt_handler:
    move  AP, #3                       ; select A[3] for our accumulator
    move  ACC, EIF1                    ; get the interrupt flag
    and   #4                           ; check for interrupt 10
    jump  z, interrupt_not_10
    ;
    ; External interrupt 10 fired.  Check to see state of other pushbutton.
    ;
    move  ACC, PI7                     ; get latch states for port 7
    and   #2                           ; isolate P7.1
    jump  nz, interrupt_10_only        ; ...to see if other button is down
    ;
    ; The other button is down.  Toggle LED #4
    ;
    move  ACC, PO0                     ; get output pin state
    xor   #10h                         ; toggle pin 4
    move  PO0, ACC                     ; write new LED state
    jump  interrupt_not_10             ; check if other interrupt fired too

interrupt_10_only:
    ;
    ; Other button is NOT down.  Toggle LED #1
    ;
    move  ACC, PO0                     ; get output pin state
    xor   #02h                         ; toggle pin 1
    move  PO0, ACC                     ; write new LED state
    jump  interrupt_not_10             ; check if other interrupt fired too

interrupt_not_10:
    move  ACC, EIF1                    ; get the interrupt flag
    and   #80h                         ; check for interrupt 15
    jump  z, interrupt_not_15
    ;
    ; External interrupt 15 fired.  Check to see state of other pushbutton.
    ;
    move  ACC, PI5                     ; get latch states for port 5
    and   #4                           ; isolate P5.2
    jump  nz, interrupt_15_only        ; ...to see if other button is down
    ;
    ; The other button is down.  Toggle LED #3
    ;
    move  ACC, PO0                     ; get output pin state
    xor   #08h                         ; toggle pin 3
    move  PO0, ACC                     ; write new LED state
    jump  interrupt_not_15             ; exit the interrupt routine

interrupt_15_only:
    ;
    ; Other button is NOT down.  Toggle LED #2
    ;
    move  ACC, PO0                     ; get output pin state
    xor   #04h                         ; toggle pin 2
    move  PO0, ACC                     ; write new LED state
    jump  interrupt_not_15             ; check if other interrupt fired too

interrupt_not_15:
    move  EIF1, #0                     ; clear the interrupt conditions
    move  AP, #0                       ; return accumulator to A[0]
    reti

end
