 ;sound4 for Code Clinic page of Almanac
 ;Amiga Computing -April Issue
 ;M.Stanger Dec 91

                       ;adresses of routines used by C program
 XDEF  _clearsound
 XDEF  _initsound
 XDEF  _play
 XDEF  _play1
 XDEF  _play2
 XDEF  _play3
 XDEF  _play4
 XDEF  _play5
 XDEF  _startsound
 XDEF  _stopsound
 XDEF  _quietsound
 XDEF  _timerdevice
 XDEF  _closetimer
 XDEF  _settime
 XDEF  _waitbit


      INCLUDE "EXEC/TYPES.I"
      INCLUDE "GRAPHICS/GFXBASE.I"
      INCLUDE "DEVICES/INPUTEVENT.I"
      INCLUDE "DEVICES/AUDIO.I"
      INCLUDE "DEVICES/TIMER.I"


 XREF  _AbsExecBase
                        ;Amiga library routines
 XREF  _LVOOpenDevice
 XREF  _LVOCloseDevice
 XREF  _LVOAllocSignal
 XREF  _LVOFindTask
 XREF  _LVOFreeSignal
 XREF  _LVODoIO
 XREF  _LVOSendIO
 XREF  _LVOWaitIO
 XREF  _LVOWaitPort
 XREF  _LVOGetMsg
 XREF  _LVOAddPort
 XREF  _LVORemPort
 XREF  _LVOReplyMsg
 XREF  _LVOAllocMem
 XREF  _LVOFreeMem

     CSECT text,code       ;this directive is used by the Lattice Assembler
                           ;this routine is the main program for the
_soundtest:                ;assembler version
 jsr        _timerdevice   ;open the timer device
 jsr        _initsound     ;initialise the sound
 tst.l      errorflag      ;check for errors
 bne        nosound
 jsr         _play5        ;start playing tune 5
 jsr        _settime       ;set timer for duration of first note
hangabout:  
  jsr       _waitbit       ;start next note if necessary
  jsr       _playsig       ;read the joystick
  tst.l     fire           ;exit if fire button pressed
  beq       hangabout
cleanup:    
  jsr       _closetimer    ;close timer device
  jsr       _clearsound    ;tidy up and exit
  rts


_playsig: 
 clr.l      d0               ;routine for reading  joystick 0
 clr.l      d1
 move.l     #0,xmove         ;clear previous values    
 move.l     #0,ymove
 move.l     #0,fire
 move.w     $dff00a,d0       ;move JOY0DAT to d0 (game port 0) 
 btst       #1,d0            ;test bit 1 for joystick right
 beq        left
 move.l     #1,xmove
left:
 btst       #9,d0            ;test bit 9 for joystick left
 beq        down
 move.l     #2,xmove
down:
 move.w     d0,d1            ;copy d0 to d1
 lsr.w      #1,d1            ;move bit 1 to bit 0, and bit 9 to bit 8
 eor.w      d0,d1            ;exclusive OR d0 and d1
 btst       #0,d1            ;test bit 0 for joystick down
 beq        up
 move.l     #4,ymove
up:
 btst       #8,d1            ;test bit 8 for joystick up
 beq        jfire
 move.l     #8,ymove
jfire:
 move.b     $bfe001,d1       ;CIA-A parallel port  A
 btst       #6,d1            ;test bit 6 for game port 0 fire button
 bne        skip
 move.l     #16,fire
skip:
 move.l     xmove,d0
 add.l      ymove,d0
 add.l      fire,d0
 rts

_initsound:                  ;initialisation routine
 movem.l   d0-d7/a0-a6,-(sp)
 jsr       soundmemory       ;allocate memory for sound samples
 jsr       copydata
 jsr       sport             ;initialise audio device and sound ports
 jsr       _startsound       ;set volume of sound
 jsr       _note             ;start note playing
 movem.l   (sp)+,d0-d7/a0-a6
 rts

_clearsound:
 movem.l   d0-d7/a0-a6,-(sp)
 jsr       freememory        ;free allocated memory buffers
 jsr       cleansound        ;close down audio device
 movem.l   (sp)+,d0-d7/a0-a6
 rts

_startsound:                 ;set volume to 48 (loudish)
 movem.l   d0-d7/a0-a6,-(sp)
 move.l    #0,sflag
 move.l    #48,volume
 movem.l   (sp)+,d0-d7/a0-a6
 rts

_quietsound:                 ;set volume to 24 (quietish)
 movem.l   d0-d7/a0-a6,-(sp)
 move.l    #0,sflag
 move.l    #24,volume
 movem.l   (sp)+,d0-d7/a0-a6
 rts

_stopsound:                  ;set volume to zero
 movem.l   d0-d7/a0-a6,-(sp)
 move.l     #1,sflag
 move.l     #0,volume
 movem.l   (sp)+,d0-d7/a0-a6
 rts


cleansound:                  ;close the audio device
 lea       allocIOB,a1
 jsr       _LVOCloseDevice(a6)
 lea       allocIOB,a5       ;clear the IO block
 move.b    #$FF,LN_TYPE(a5)
 move.l    #-1,IO_DEVICE(a5)
 move.l    #-1,IO_UNIT(a5)
 lea       allocPort,a5      ;free the signal bit
 move.b    MP_SIGBIT(a5),d0
 jsr       _LVOFreeSignal(a6)
 move.l    a5,a1
 jsr       _LVORemPort(a6)   ;remove the allocation port
 move.b    #$FF,LN_TYPE(a5)
 move.l    #-1,MP_MSGLIST(a5)
 lea    soundIOB0,a5         ;free IO block
 move.b #$FF,LN_TYPE(a5)
 move.l #-1,IO_DEVICE(a5)
 move.l #-1,IO_UNIT(a5)
 lea     soundPort0,a1       ;remove sound port
 jsr     _LVORemPort(a6) 
 move.b #$FF,LN_TYPE(a5)
 move.l #-1,MP_MSGLIST(a5)
 lea    soundPort0,a5
 move.b MP_SIGBIT(a5),d0     ;free signal bit
 jsr    _LVOFreeSignal(a6)
 move.b #$FF,LN_TYPE(a5)
 move.l #-1,MP_MSGLIST(a5)
 rts


 ;open device and set up sound ports
 ;code adapted from CreatePort and CreateStdIO from ExecSupport

sport:                     
 move.l    _AbsExecBase,a6   
 move.l    #-1,d0
 jsr       _LVOAllocSignal(a6) ;allocate a signal bit
 cmp.l     #-1,d0
 beq       errorfound
 lea       allocPort,a5        ;set up allocation port
 move.l    #sound,LN_NAME(a5)
 move.b    #0,LN_PRI(a5)
 move.b    #0,MP_FLAGS(a5)     ;message port
 move.b    d0,MP_SIGBIT(a5)
 move.b    #4,LN_TYPE(a5)
 move.l    #0,a1               ;find current task
 jsr       _LVOFindTask(a6)
 movem.l   d0,MP_SIGTASK(a5)
 lea       allocPort,a1        ;add allocation port to list of ports
 jsr       _LVOAddPort(a6)
 lea       allocIOB,a5         ;set up IO structure
 move.b    #-40,LN_PRI(a5)
 lea       allocPort,a1
 move.l    a1,MN_REPLYPORT(a5)
 lea       allocationMap,a1
 move.l    a1,ioa_Data(a5)
 move.l    #1,ioa_Length(a5)
 lea       audioname,a0       ;open audio device
 lea       allocIOB,a1
 move.l    #0,d0
 move.l    #0,d1
 jsr       _LVOOpenDevice(a6)
 tst.l     d0
 bne       errorfound
 lea       allocIOB,a5
 move.l    IO_DEVICE(a5),a0
 movem.l   a0,device          ;save the device address
 move.l  #-1,d0
 jsr     _LVOAllocSignal(a6)  ;allocate signal bit
 cmp.l   #-1,d0
 beq      errorfound
 lea     soundPort0,a5        ;set up sound port
 move.l   #sound,LN_NAME(a5)
 move.b   #0,LN_PRI(a5)
 move.b  d0,MP_SIGBIT(a5)
 move.b  #0,MP_FLAGS(a5)
 move.b  #4,LN_TYPE(a5)       ;as message port
 move.l  #0,a1
 jsr     _LVOFindTask(a6)     ;find current task
 movem.l  d0,MP_SIGTASK(a5)   ;attach it to sound port
 lea      soundPort0,a1
 jsr      _LVOAddPort(a6)     ;add sound port to list of ports
 clr.l   errorflag
 rts

errorfound:                  ;set flag if necessary
 move.l   #1,errorflag
nosound:
 rts

setsound:                    ;put details in sound IO block
 move.l   _AbsExecBase,a6
 lea     soundIOB0,a5
 lea     soundPort0,a1
 lea      allocIOB,a4        ;find allocation key
 move.w   ioa_AllocKey(a4),d0
 move.l   a1,MN_REPLYPORT(a5) ;sound port
 move.l   device,a1          ;device
 move.l   a1,IO_DEVICE(a5)
 move.l   a0,IO_UNIT(a5)     ;unit
 move.w   #CMD_WRITE,IO_COMMAND(a5) ;set the command
 move.b   #(ADIOF_PERVOL),IO_FLAGS(a5) ;command flags
 move.w   d0,ioa_AllocKey(a5) ;allocation key
 move.l   waveadd,ioa_Data(a5)    ;sound sample address
 move.l   #16,ioa_Length(a5)  ;data length
 move.w   d5,ioa_Period(a5)  ;period
 move.w   d4,ioa_Volume(a5)  ;volume
 move.w   #0,ioa_Cycles(a5)  ;number of cycles
 move.l   a5,a1
 BEGINIO                     ;start the sound
 rts

soundmemory:             ;chipmemory for sound data
 move.l   _AbsExecBase,a6   ;exec library address
 move.l   #32,d0            ;number of bytes required
 move.l   #65539,d1         ;type of memory required (chip memory or clear)
 jsr      _LVOAllocMem(a6)  ;allocate the memory
 movem.l  d0,waveadd        ;store the pointer
 beq      errorfound     
 clr.l    d0
 rts

copydata:                   ;all the sound data is copied into chip memory  
 move.l   #31,d1
 lea      waveform,a1
 move.l   waveadd,a2
more1:
 move.b   (a1)+,(a2)+
 dbra     d1,more1
 rts

freememory:               ;free sound sample memory
  move.l  _AbsExecBase,a6
  move.l  #32,d0
  move.l  waveadd,a1
  jsr     _LVOFreeMem(a6)
  rts
 
updatenote:                          ;updates channel 1 period and volume
 move.l _AbsExecBase,a6
 lea     soundIOB0,a5
 lea     allocIOB,a4
 lea     soundPort0,a1
 move.l  a1,MN_REPLYPORT(a5)
 move.l  device,a1
 move.l  a1,IO_DEVICE(a5)
 move.l  unit0,a0
 move.l  a0,IO_UNIT(a5)
 move.w  #ADCMD_PERVOL,IO_COMMAND(a5)
 move.b  #(ADIOF_PERVOL+ADIOF_SYNCCYCLE),IO_FLAGS(a5)
 move.w  ioa_AllocKey(a4),d0
 move.w  d0,ioa_AllocKey(a5)
 move.l  waveadd,ioa_Data(a5)
 move.l  #16,ioa_Length(a5)
 move.w  d5,ioa_Period(a5)
 move.w  d4,ioa_Volume(a5)
 move.w  #0,ioa_Cycles(a5)
 move.l  a5,a1
 BEGINIO
 rts


_note:                ;musical note started with infinite cycles
 movem.l d0-d7/a0-a6,-(sp)
 move.l  waveadd,d7 ;updated when music is played
 move.l  #16,d6
 move.w  #0,d3
 move.l  #214,d5
 move.l  #0,d4
 lea     soundIOB0,a5
 move.l  unit0,a0
 bsr     setsound
 movem.l (sp)+,d0-d7/a0-a6
 rts

_play1:                       ;put tune1 into tunestart address
 lea        tune1,a0
 move.l     a0,tunestart
 clr.l      gotto
 bra        _play

_play2:                       ;put tune2 into tunestart address
 lea        tune2,a0
 move.l     a0,tunestart
 clr.l      gotto
 bra        _play

_play3:                       ;put tune3 into tunestart address
 lea        tune3,a0
 move.l     a0,tunestart
 clr.l      gotto
 bra        _play

_play4:                       ;put tune4 into tunestart address 
 lea        tune4,a0
 move.l     a0,tunestart
 clr.l      gotto
 bra        _play

_play5:                       ;put tune5 into tunestart address
 lea        tune5,a0
 move.l     a0,tunestart
 clr.l      gotto
                              ;play a note of the chosen tune
                              ;the tune uses less than 1 octave
_play:                        ;so this is a very simple music driver
 move.l     tunestart,a0
playtune:
 movem.l   d0-d7/a0-a6,-(sp)
 move.l     gotto,d7          ;find current note of the tune
 move.l     0(a0,d7.l),d0     ;find pitch code
 move.l     4(a0,d7.l),d2     ;find duration code
 tst.l      d0                
 beq        quiet             ;test for end of tune
                              ;find correct timer interval    
 lea        cycles,a0
 asl.l      #2,d2
 move.l     0(a0,d2.l),delay
 cmp.l      #128,d0
 beq        rest              ;test for rest
 subi.l      #24,d0
 asl.l      #1,d0
 move.l     volume,d4         ;find current volume
 tst.l      sflag             ;test for silence
 beq        per
 clr.l      d4
per:                         ;find correct period for pitch code
 lea        perval,a0
 move.w     0(a0,d0.l),d5
 jsr        updatenote       ;play the note
 addi.l      #8,gotto        ;update current note
 movem.l   (sp)+,d0-d7/a0-a6
 rts

quiet:                       ;play dummy note, reset to start of tune
 move.l     #0,d4
 move.l     #214,d5
 clr.l      gotto
 jsr        updatenote
 movem.l   (sp)+,d0-d7/a0-a6
 rts

rest:
 move.l     #0,d4
 move.l     #214,d5
 addi.l      #8,gotto        ;update current note
 jsr        updatenote       ;play note at zero volume
 movem.l   (sp)+,d0-d7/a0-a6
 rts



_timerdevice:                   ;set up a timer device
 move.l  _AbsExecBase,a6
 lea     devicename,a0           ;open the timer device
 move.l  #0,d0
 lea     timer_request,a1
 move.l  #0,d1
 jsr     _LVOOpenDevice(a6)
 tst.b   d0                     ;exit if unsuccessful
 beq     errorfound               
 lea     timer_request,a3       ;fill in the request block details
 move.b  #5,LN_TYPE(a3)         ;message type
 move.b  #0,LN_PRI(a3)          ;priority zero 
 move.l  #0,LN_NAME(a3)         ;no name
 move.l  #0,MN_REPLYPORT(a3)    ;no replyport needed
timerout:
 move.l  #0,errorflag
 rts

_closetimer:                    ;close the device
 move.l  _AbsExecBase,a6
 lea     timer_request,a1
 jsr     _LVOCloseDevice(a6)
 rts

_waitbit:                       ;get the system time
 move.l  _AbsExecBase,a6
 lea     timer_request,a1
 move.w  #TR_GETSYSTIME,IO_COMMAND(a1)
 jsr     _LVODoIO(a6)
 lea     timer_request,a1
 lea     IOTV_TIME(a1),a2
 move.l  TV_MICRO(a2),d2         ;are microseconds at zero?           
 bmi     nottimeyet             ;note still in progress
 jsr     _play                  ;play the next note
 jsr     _settime               ;set the timer
nottimeyet:
 rts

_settime:
 move.l  _AbsExecBase,a6         ;set the time interval 
 lea     timer_request,a1
 lea     IOTV_TIME(a1),a2
 move.l  #-1,TV_SECS(a2)        ;-1 seconds
                                ;negative interval value
 move.l  delay,d0
 move.l  d0,TV_MICRO(a2)
 lea     timer_request,a1       ;set the time
 move.w  #TR_SETSYSTIME,IO_COMMAND(a1)
 jsr     _LVODoIO(a6)
 rts

 CSECT   DATA,data
devicename:
 dc.b 'timer.device',0
 ds.w  0
timer_request:
 ds.b IOTV_SIZE
 ds.w 0
tunestart:
 dc.l 0
errorflag:     ;error flag for initialisation routines     
 dc.l    0        
volume:        ;sound level
 dc.l    64
de:            ;dosbase
 ds.l    1
DN:
 dc.b    'dos.library',0
 ds.w    0
s0flag:        ;sound in progress flag for channel 0
 dc.w    0
sflag:        ;sound on/off flag
 dc.l    0
unit0:         ;channels
 dc.l    1
unit1:
 dc.l    2
unit2:
 dc.l    4
unit3:
 dc.l    8
sound:        ;name of allocation port
 dc.b    'sound',0
 ds.w    0
allocationMap: 
 dc.b    15
 ds.w    0
allocPort:    ;allocation port structure
 ds.b    MP_SIZE
allocIOB:     ;aloocation input/output block
 ds.b   ioa_SIZEOF
device:       ;device address
 ds.l   1
soundPort0:   ;soundport structure
 ds.b   MP_SIZE
soundIOB0:    ;sound input/output structure  
 ds.b   ioa_SIZEOF
audioname:    ;device name
 dc.b   'audio.device',0
 ds.w   0
xmove:       ;variables used by playsig routine
 dc.l   0   
ymove:
 dc.l   0
fire:
 dc.l   0
waveadd:     ;chip memory address of wave data
 dc.l   0
cycles:
 dc.l  -$200000,-$100000,-$80000,-$40000,-$20000,-$10000
 dc.l  -$8000,-$4000,-$300000,-$1800000,-$c0000,-$60000
perval:              ;period values c-c'
 dc.w 428,404,381,360,339,320,302,285,269,254,240,226,214,0
waveform:            ;sinewave
 dc.b 0,-20,-38,-56,-71,-83,-92,-98,-100,-98,-92,-83,-71,-56,-38,-20
 dc.b 0,20,38,56,71,83,92,98,100,98,92,83,71,56,38,20
tune5:
 dc.l 31,2,28,3,29,2,28,3,31,2,28,3,24,2
 dc.l 28,3,26,2,28,3,29,2,33,3,31,2,28,3,24,2,0,0
tune1:
 dc.l 31,2,36,11,35,2,33,2,31,11,33,2
 dc.l 35,2,36,2,28,2,28,2,29,2,26,2,24,11,128,4,128,4,0,0
tune2:
 dc.l 36,3,33,3,33,3,34,3,31,3,31,2
 dc.l 29,3,31,3,33,3,34,3,36,3,36,3,36,2,128,2,128,2,0,0
tune3:
 dc.l 28,2,26,2,24,2,26,2,28,2,28,2,28,2,128,2
 dc.l 24,2,26,2,26,2,28,2,26,2,24,2,128,2,0,0
tune4:
 dc.l 24,3,26,3,28,3,24,3,24,3,26,3,28,3,24,3
 dc.l 28,3,29,3,31,2,28,3,29,3,31,2,128,2,0,0
gotto:             ;current note position in tune
 dc.l 0
delay:             ;duration of current note
 dc.l 0
 SECTION  mem,BSS

 END

