;   ***************************************************************
;   * Copyright (c) 2001, Embed Inc (http://www.embedinc.com)     *
;   *                                                             *
;   * Permission to copy this file is granted as long as this     *
;   * copyright notice is included in its entirety at the         *
;   * beginning of the file, whether the file is copied in whole  *
;   * or in part and regardless of whether other information is   *
;   * added to the copy.                                          *
;   *                                                             *
;   * The contents of this file may be used in any way,           *
;   * commercial or otherwise.  This file is provided "as is",    *
;   * and Embed Inc makes no claims of suitability for a          *
;   * particular purpose nor assumes any liability resulting from *
;   * its use.                                                    *
;   ***************************************************************
;
;   Module that manages the output sounds.
;
         include "hal.inc"

         extern  rand_max    ;set REG0 to random number in range 0 thru REG1
         extern  sound       ;start new sound at REG3,REG2 with REG1,REG0 samples
         extern  lookflas    ;prog memory word at REG3,REG2 into REG1,REG0, inc adr

         extern_flags        ;declare global flag bits EXTERN
;
;***********************************************************************
;
;   Configuration constants.
;
lbank    equ       0         ;register bank for the local state of this module
delmin   equ      50         ;min delay between sounds, 100mS units
delmax   equ     100         ;max delay between sounds, 100mS units
;
;   Derived constants.
;
lbankadr equ     bankadr(lbank) ;address within local state register bank
;
;***********************************************************************
;
;   Global state.  All this state is assumed to be in the GBANK register
;   bank by other modules.
;
.bank#v(gbank) udata

;
;***********************************************************************
;
;   Local state.
;
  if lbank != gbank
.bank#v(lbank) udata
    endif

cntdel   res     1           ;100mS ticks until start next sound

.soun    code
;
;***********************************************************************
;
;   Table SOUNDS
;
;   This table lists all the available sounds.  Each table entry contains
;   the 14 bit starting address of the sound.
;
;   A sound starts with the 14 bit number of samples in the sound, followed
;   by the samples.  Two sound samples are stored per 14 bit program memory
;   word, with the low 7 bits being the first sample and the high 7 bits
;   the second.  The number of samples may be odd, in which case the high
;   7 bits of the last program memory location of the sound will be ignored.
;
n_sounds set     0           ;init number of entries in SOUNDS table
;
;*****
;
;   Macro SOUND_ENT <start adr>
;
;   This macro creates one SOUNDS table entry.  The argument is the sound
;   start address.  The sound is assumed to be stored in a separate module,
;   so the start address symbol will be declared external, and a table
;   entry of the proper format will be created.  The assembly variable
;   N_SOUNDS will be incremented.  This will indicate the number of
;   table entries to code located after this table.
;
sound_ent macro  startadr
         extern  startadr    ;declare the sound start symbol external
         data    startadr    ;write the table entry
n_sounds set     n_sounds + 1 ;count one more SOUNDS table entry
         endm
;
;*****
;
sounds                       ;start of table listing the available sounds
         sound_ent owl
         sound_ent howl
         sound_ent pop
         sound_ent pop2
;
;***********************************************************************
;
;   Subroutine SOUN_INIT
;
;   Initialize the hardware and software state managed by this module.
;
         glbsub  soun_init, noregs

         dbankif lbankadr
         movlw   (delmin + delmax) / 2 ;init 100mS ticks until next sound start
         movwf   cntdel

         leaverest
;
;***********************************************************************
;
;   Subroutine SOUN_OFF
;
;   OFF mode was just entered.  Update the sound handling accordingly.
;
         glbsub  soun_off, noregs

         dbankif gbankadr
         bcf     flag_audon  ;make sure audio output is off

         leaverest
;
;***********************************************************************
;
;   Subroutine SOUN_100MS
;
;   This subroutine is called once every 100mS.
;
;   A new randomly chosen sound is started occasionally.  The local variable
;   CNTDEL is the number of 100mS ticks until it is time to start a new
;   sound.  It is reloaded with a random value from DELMIN to DELMAX for
;   each sound.  CNTDEL is not decremented while a sound is being played,
;   so DELMIN and DELMAX represent the min/max time of the silence between
;   sounds.
;
         glbsub  soun_100ms, regf0 | regf1 | regf2 | regf3
;
;   Check for OFF mode, in which case there is nothing to do.
;
         dbankif gbankadr
         btfsc   flag_off    ;not in OFF mode ?
         goto    done_newsound ;in OFF mode, sound output is disabled
;
;   Check for a sound is currently playing.  If so, there is nothing to do.
;
         dbankif gbankadr
         btfsc   flag_audon  ;the audio output is currently off (no sound playing) ?
         goto    done_newsound ;a sound is in progress, do nothing until it's done



; ;***** START DEBUG CODE *****
; ;
; ;   Don't start a sound if the external SNDON input is low.
; ;
;          dbankif sndon_reg
;          btfss   sndon_reg, sndon_bit ;sounds are enabled ?
;          goto    done_newsound ;sounds are disabled
; ;
; ;***** END DEBUG CODE *****



;
;   No sound is currently playing.
;
         dbankif lbankadr
         decfsz  cntdel      ;count one less 100mS tick until start new sound
         goto    done_newsound ;not time yet to start a new sound
;
;   The delay counter just expired.  This means it is time to start a new sound.
;
;   Reset the delay counter with a random value from DELMIN to DELMAX.  This
;   will be the length of the silence after this new sound.
;
         movlw   delmax - delmin ;make number of valid delays in the range
         movwf   reg1        ;pass random number max limit
         gcall   rand_max    ;get random number in REG0 that is at or below REG1
         movf    reg0, w     ;get the 0 - N random number into W
         addlw   delmin      ;make randomly chosen delay time within the range
         dbankif lbankadr
         movwf   cntdel      ;set length of quiet time after the new sound
;
;   Chose a random sound from the SOUNDS table.
;
         movlw   n_sounds - 1 ;pass max random number value
         movwf   reg1
         gcall   rand_max    ;pick a random 0-N SOUNDS table entry in REG0

         movlw   high sounds ;init high byte of table entry address
         movwf   reg3
         movlw   low sounds  ;get low byte of table start address
         addwf   reg0, w     ;make low byte of table entry address
         movwf   reg2        ;pass it
         skip_ncarr          ;no carry into address high byte ?
         incf    reg3        ;propagate the carry to the high address byte
         gcall   lookflas    ;lookup entry at REG3,REG2 into REG1,REG0, inc address
;
;   REG1,REG0 contains the starting address of the randomly chosen sound.
;   Now read the sound length word, and pass the starting data address and
;   data length to SOUND to start the sound playing.
;
         movf    reg0, w     ;pass program memory read address in REG3,REG2
         movwf   reg2
         movf    reg1, w
         movwf   reg3
         gcall   lookflas    ;read num samples into REG1,REG0, inc adr in REG3,REG2
         gcall   sound       ;start sound at REG3,REG2 with REG2,REG0 num of samples

done_newsound unbank         ;done dealing with starting new sound issues
         leaverest

         end